Deleting a parted.Device causes parted to close its rw fd for the device, which triggers a change uevent on that device, which could in turn trigger any number of actions via udev rules. One example is when we reset a Blivet instance: All devices are deleted, including their parted.Device instances, which triggers a change uevent for every device. In response to these events, mdadm's udev rules activate all arrays on those devices. Activating devices -- even indirectly -- without cause, is not acceptable behavior for a storage library.
This also adds a trailing comma to 1-tuples in variable_copy arguments. In my testing, ('foo') is the string 'foo' -- not a tuple with lone element 'foo'.
(cherry picked from commit 73d9c995c8d46f1aa248d1ade3bbb1838653b6b0)
Resolves: rhbz#1069597 --- blivet/deviceaction.py | 9 ---- blivet/devices/btrfs.py | 3 ++ blivet/devices/container.py | 3 ++ blivet/devices/device.py | 2 +- blivet/devices/disk.py | 14 ++---- blivet/devices/dm.py | 3 +- blivet/devices/file.py | 9 ++++ blivet/devices/lib.py | 3 ++ blivet/devices/luks.py | 6 +-- blivet/devices/md.py | 19 ++++---- blivet/devices/nfs.py | 3 ++ blivet/devices/nodev.py | 2 + blivet/devices/partition.py | 41 +++++++++-------- blivet/devices/storage.py | 107 +++++++++++++++++++++----------------------- blivet/devicetree.py | 15 +++---- blivet/platform.py | 10 +++-- tests/storagetestcase.py | 5 +-- 17 files changed, 124 insertions(+), 130 deletions(-)
diff --git a/blivet/deviceaction.py b/blivet/deviceaction.py index a8cd489..bc7c1a0 100644 --- a/blivet/deviceaction.py +++ b/blivet/deviceaction.py @@ -28,7 +28,6 @@ from .util import get_current_entropy from .devices import StorageDevice from .devices import PartitionDevice, LVMLogicalVolumeDevice from .formats import getFormat, luks -from .storage_log import log_exception_info from parted import partitionFlag, PARTITION_LBA from .i18n import _, N_ from .callbacks import CreateFormatPreData, CreateFormatPostData @@ -344,14 +343,6 @@ class ActionDestroyDevice(DeviceAction): super(ActionDestroyDevice, self).execute(callbacks=None) self.device.destroy()
- # Make sure libparted does not keep cached info for this device - # and returns it when we create a new device with the same name - if self.device.partedDevice: - try: - self.device.partedDevice.removeFromCache() - except Exception: # pylint: disable=broad-except - log_exception_info(fmt_str="failed to remove info for device %s from libparted cache", fmt_args=[self.device]) - def requires(self, action): """ Return True if self requires action.
diff --git a/blivet/devices/btrfs.py b/blivet/devices/btrfs.py index 8e47708..eeb203f 100644 --- a/blivet/devices/btrfs.py +++ b/blivet/devices/btrfs.py @@ -61,6 +61,9 @@ class BTRFSDevice(StorageDevice): self.sysfsPath = self.parents[0].sysfsPath log.debug("%s sysfsPath set to %s", self.name, self.sysfsPath)
+ def updateSize(self): + pass + def _postCreate(self): super(BTRFSDevice, self)._postCreate() self.format.exists = True diff --git a/blivet/devices/container.py b/blivet/devices/container.py index d8a1379..6e5683e 100644 --- a/blivet/devices/container.py +++ b/blivet/devices/container.py @@ -200,3 +200,6 @@ class ContainerDevice(StorageDevice):
if member in self.parents: self.parents.remove(member) + + def updateSize(self): + pass diff --git a/blivet/devices/device.py b/blivet/devices/device.py index 498fef4..1824f8c 100644 --- a/blivet/devices/device.py +++ b/blivet/devices/device.py @@ -99,7 +99,7 @@ class Device(util.ObjectID): """ return util.variable_copy(self, memo, omit=('_raidSet', 'node'), - shallow=('_partedDevice', '_partedPartition')) + shallow=('_partedPartition',))
def __repr__(self): s = ("%(type)s instance (%(id)s) --\n" diff --git a/blivet/devices/disk.py b/blivet/devices/disk.py index 6a9a91a..d17e975 100644 --- a/blivet/devices/disk.py +++ b/blivet/devices/disk.py @@ -27,6 +27,7 @@ import block from .. import errors from .. import util from ..flags import flags +from ..size import Size from ..storage_log import log_method_call from .. import udev
@@ -89,8 +90,7 @@ class DiskDevice(StorageDevice):
def __repr__(self): s = StorageDevice.__repr__(self) - s += (" removable = %(removable)s partedDevice = %(partedDevice)r" % - {"removable": self.removable, "partedDevice": self.partedDevice}) + s += (" removable = %(removable)s" % {"removable": self.removable}) return s
@property @@ -98,23 +98,15 @@ class DiskDevice(StorageDevice): if flags.testing: return True
- if not self.partedDevice: - return False - # Some drivers (cpqarray <blegh>) make block device nodes for # controllers with no disks attached and then report a 0 size, # treat this as no media present - return self.partedDevice.getLength(unit="B") != 0 + return self.exists and self.currentSize > Size(0)
@property def description(self): return self.model
- @property - def size(self): - """ The disk's size """ - return super(DiskDevice, self).size - def _preDestroy(self): """ Destroy the device. """ log_method_call(self, self.name, status=self.status) diff --git a/blivet/devices/dm.py b/blivet/devices/dm.py index 57314bd..9326786 100644 --- a/blivet/devices/dm.py +++ b/blivet/devices/dm.py @@ -34,6 +34,7 @@ import logging log = logging.getLogger("blivet")
from .storage import StorageDevice +from .lib import LINUX_SECTOR_SIZE
class DMDevice(StorageDevice): """ A device-mapper device """ @@ -181,7 +182,7 @@ class DMLinearDevice(DMDevice): """ Open, or set up, a device. """ log_method_call(self, self.name, orig=orig, status=self.status, controllable=self.controllable) - slave_length = self.slave.partedDevice.length + slave_length = self.slave.currentSize / LINUX_SECTOR_SIZE dm.dm_create_linear(self.name, self.slave.path, slave_length, self.dmUuid)
diff --git a/blivet/devices/file.py b/blivet/devices/file.py index da231c5..7aaf011 100644 --- a/blivet/devices/file.py +++ b/blivet/devices/file.py @@ -21,6 +21,7 @@ #
import os +import stat
from .. import util from ..storage_log import log_method_call @@ -80,6 +81,14 @@ class FileDevice(StorageDevice):
return os.path.normpath("%s%s" % (root, self.name))
+ def _getSize(self): + size = self._size + if self.exists and os.path.exists(self.path): + st = os.stat(self.path) + size = Size(st[stat.ST_SIZE]) + + return size + def _preSetup(self, orig=False): if self.format and self.format.exists and not self.format.status: self.format.device = self.path diff --git a/blivet/devices/lib.py b/blivet/devices/lib.py index c4996db..979dd6e 100644 --- a/blivet/devices/lib.py +++ b/blivet/devices/lib.py @@ -20,6 +20,9 @@ # from .. import errors from .. import udev +from ..size import Size + +LINUX_SECTOR_SIZE = Size(512)
def get_device_majors(): majors = {} diff --git a/blivet/devices/luks.py b/blivet/devices/luks.py index f02cd28..197926f 100644 --- a/blivet/devices/luks.py +++ b/blivet/devices/luks.py @@ -22,8 +22,6 @@ # device backend modules from ..devicelibs import crypto
-from ..size import Size - import logging log = logging.getLogger("blivet")
@@ -63,10 +61,10 @@ class LUKSDevice(DMCryptDevice):
@property def size(self): - if not self.exists or not self.partedDevice: + if not self.exists: size = self.slave.size - crypto.LUKS_METADATA_SIZE else: - size = Size(self.partedDevice.getLength(unit="B")) + size = self.currentSize return size
def _postCreate(self): diff --git a/blivet/devices/md.py b/blivet/devices/md.py index 8ce7854..33d91b5 100644 --- a/blivet/devices/md.py +++ b/blivet/devices/md.py @@ -28,7 +28,6 @@ from .. import util from ..flags import flags from ..storage_log import log_method_call from .. import udev -from ..size import Size from ..i18n import P_
import logging @@ -81,15 +80,15 @@ class MDRaidArrayDevice(ContainerDevice): self._memberDevices = 0 # the number of active (non-spare) members self._totalDevices = 0 # the total number of members
+ if level == "container": + self._type = "mdcontainer" + self.level = level + super(MDRaidArrayDevice, self).__init__(name, fmt=fmt, uuid=uuid, exists=exists, size=size, parents=parents, sysfsPath=sysfsPath)
- if level == "container": - self._type = "mdcontainer" - self.level = level - # For new arrays check if we have enough members if (not exists and parents and len(parents) < self.level.min_members): for dev in self.parents: @@ -196,7 +195,7 @@ class MDRaidArrayDevice(ContainerDevice): if self.type == "mdbiosraidarray": return self._size
- if not self.exists or not self.partedDevice: + if not self.exists or not self.mediaPresent: try: size = self.level.get_size([d.size for d in self.devices], self.memberDevices, @@ -207,11 +206,15 @@ class MDRaidArrayDevice(ContainerDevice): size = 0 log.debug("non-existent RAID %s size == %s", self.level, size) else: - size = Size(self.partedDevice.getLength(unit="B")) + size = self.currentSize log.debug("existing RAID %s size == %s", self.level, size)
return size
+ def updateSize(self): + # pylint: disable=bad-super-call + super(ContainerDevice, self).updateSize() + @property def description(self): if self.type == "mdcontainer": @@ -561,7 +564,7 @@ class MDRaidArrayDevice(ContainerDevice): elif flags.testing: return True else: - return self.partedDevice is not None + return super(MDRaidArrayDevice, self).mediaPresent
@property def model(self): diff --git a/blivet/devices/nfs.py b/blivet/devices/nfs.py index 513a11c..da00114 100644 --- a/blivet/devices/nfs.py +++ b/blivet/devices/nfs.py @@ -69,6 +69,9 @@ class NFSDevice(StorageDevice, NetworkStorageDevice): """ Destroy the device. """ log_method_call(self, self.name, status=self.status)
+ def updateSize(self): + pass + @classmethod def isNameValid(cls, name): # Override StorageDevice.isNameValid to allow / diff --git a/blivet/devices/nodev.py b/blivet/devices/nodev.py index a66636c..1aee5d3 100644 --- a/blivet/devices/nodev.py +++ b/blivet/devices/nodev.py @@ -69,6 +69,8 @@ class NoDevice(StorageDevice): log_method_call(self, self.name, status=self.status) self._preDestroy()
+ def udpateSize(self): + pass
class TmpFSDevice(NoDevice): """ A nodev device for a tmpfs filesystem. """ diff --git a/blivet/devices/partition.py b/blivet/devices/partition.py index 2701572..18add66 100644 --- a/blivet/devices/partition.py +++ b/blivet/devices/partition.py @@ -125,6 +125,12 @@ class PartitionDevice(StorageDevice):
self._bootable = False
+ # FIXME: Validate partType, but only if this is a new partition + # Otherwise, overwrite it with the partition's type. + self._partType = None + self._partedPartition = None + self._origPath = None + StorageDevice.__init__(self, name, fmt=fmt, size=size, major=major, minor=minor, exists=exists, sysfsPath=sysfsPath, parents=parents) @@ -134,13 +140,6 @@ class PartitionDevice(StorageDevice): self.req_disks = list(self.parents) self.parents = []
- # FIXME: Validate partType, but only if this is a new partition - # Otherwise, overwrite it with the partition's type. - self._partType = None - self._partedPartition = None - self._origPath = None - self._currentSize = 0 - # FIXME: Validate size, but only if this is a new partition. # For existing partitions we will get the size from # parted. @@ -529,7 +528,6 @@ class PartitionDevice(StorageDevice): return
self._size = Size(self.partedPartition.getLength(unit="B")) - self._currentSize = self._size self.targetSize = self._size
self._partType = self.partedPartition.type @@ -592,7 +590,6 @@ class PartitionDevice(StorageDevice): DeviceFormat(device=self.path, exists=True).destroy()
StorageDevice._postCreate(self) - self._currentSize = Size(self.partedPartition.getLength(unit="B"))
def create(self): """ Create the device. """ @@ -661,7 +658,7 @@ class PartitionDevice(StorageDevice): end=geometry.end)
self.disk.format.commit() - self._currentSize = Size(partition.getLength(unit="B")) + self.updateSize()
def _preDestroy(self): StorageDevice._preDestroy(self) @@ -728,12 +725,16 @@ class PartitionDevice(StorageDevice): return size
def _setSize(self, newsize): - """ Set the device's size (for resize, not creation). + """ Set the device's size. + + Most devices have two scenarios for setting a size:
- Arguments: + 1) set actual/current size + 2) set target for resize
- newsize -- the new size + Partitions have a third scenario:
+ 3) update size of an allocated-but-non-existent partition """ log_method_call(self, self.name, status=self.status, size=self._size, newsize=newsize) @@ -746,6 +747,12 @@ class PartitionDevice(StorageDevice): self.req_size = newsize self.req_base_size = newsize
+ if self.exists: + super(PartitionDevice, self)._setSize(newsize) + return + + # the rest is for changing the size of an allocated-but-not-existing + # partition, which I'm not sure is advisable if newsize > self.disk.size: raise ValueError("partition size would exceed disk size")
@@ -829,14 +836,6 @@ class PartitionDevice(StorageDevice): unalignedMax = min(maxFormatSize, maxPartSize) if maxFormatSize else maxPartSize return self.alignTargetSize(unalignedMax)
- @property - def currentSize(self): - if self.exists: - return self._currentSize - else: - return 0 - - @property def resizable(self): return super(PartitionDevice, self).resizable and \ self.disk.type != 'dasd' diff --git a/blivet/devices/storage.py b/blivet/devices/storage.py index 73ba645..6128886 100644 --- a/blivet/devices/storage.py +++ b/blivet/devices/storage.py @@ -22,8 +22,6 @@
import os import copy -import parted -import _ped import pyudev
from .. import errors @@ -38,6 +36,7 @@ import logging log = logging.getLogger("blivet")
from .device import Device +from .lib import LINUX_SECTOR_SIZE
class StorageDevice(Device): """ A generic storage device. @@ -105,7 +104,11 @@ class StorageDevice(Device): Device.__init__(self, name, parents=parents)
self._format = None + + # The size will be overridden by a call to updateSize at the end of this + # method for existing and active devices. self._size = Size(util.numeric_type(size)) + self._currentSize = self._size if self.exists else Size(0) self.major = util.numeric_type(major) self.minor = util.numeric_type(minor) self._serial = serial @@ -123,16 +126,8 @@ class StorageDevice(Device):
self.deviceLinks = []
- if self.exists and flags.testing and not self._size: - def read_int_from_sys(path): - return int(open(path).readline().strip()) - - device_root = "/sys/class/block/%s" % self.name - if os.path.exists("%s/queue" % device_root): - sector_size = read_int_from_sys("%s/queue/logical_block_size" - % device_root) - size = read_int_from_sys("%s/size" % device_root) - self._size = Size(size * sector_size) + if self.exists and self.status: + self.updateSize()
self._orig_size = self._size
@@ -199,26 +194,6 @@ class StorageDevice(Device): """ True if this device, or any it requires, is encrypted. """ return self._encrypted or any(p.encrypted for p in self.parents)
- def _getPartedDevicePath(self): - return self.path - - @property - def partedDevice(self): - devicePath = self._getPartedDevicePath() - if self.exists and self.status and not self._partedDevice: - log.debug("looking up parted Device: %s", devicePath) - - # We aren't guaranteed to be able to get a device. In - # particular, built-in USB flash readers show up as devices but - # do not always have any media present, so parted won't be able - # to find a device. - try: - self._partedDevice = parted.Device(path=devicePath) - except (_ped.IOException, _ped.DeviceException): - pass - - return self._partedDevice - @property def raw_device(self): """ The device itself, or when encrypted, the backing device. """ @@ -281,12 +256,12 @@ class StorageDevice(Device): " format = %(format)s\n" " major = %(major)s minor = %(minor)s exists = %(exists)s" " protected = %(protected)s\n" - " sysfs path = %(sysfs)s partedDevice = %(partedDevice)s\n" + " sysfs path = %(sysfs)s\n" " target size = %(targetSize)s path = %(path)s\n" " format args = %(formatArgs)s originalFormat = %(origFmt)s" % {"uuid": self.uuid, "format": self.format, "size": self.size, "major": self.major, "minor": self.minor, "exists": self.exists, - "sysfs": self.sysfsPath, "partedDevice": self.partedDevice, + "sysfs": self.sysfsPath, "targetSize": self.targetSize, "path": self.path, "protected": self.protected, "formatArgs": self.formatArgs, "origFmt": self.originalFormat.type}) @@ -410,9 +385,10 @@ class StorageDevice(Device): def _postSetup(self): """ Perform post-setup operations. """ udev.settle() - # we always probe since the device may not be set up when we want - # information about it - self._size = self.currentSize + self.updateSysfsPath() + # the device may not be set up when we want information about it + if self._size == Size(0): + self.updateSize()
# # teardown @@ -487,9 +463,7 @@ class StorageDevice(Device): udev.settle()
# make sure that targetSize is updated to reflect the actual size - if self.resizable: - self._partedDevice = None - self._targetSize = self.currentSize + self.updateSize()
# # destroy @@ -535,12 +509,6 @@ class StorageDevice(Device):
def _getSize(self): """ Get the device's size, accounting for pending changes. """ - if self.exists and not self.mediaPresent: - return 0 - - if self.exists and self.partedDevice: - self._size = self.currentSize - size = self._size if self.exists and self.resizable: size = self.targetSize @@ -548,19 +516,41 @@ class StorageDevice(Device): return size
def _setSize(self, newsize): - """ Set the device's size to a new value. """ + """ Set the device's size to a new value. + + This is not adequate to set up a resize as it does not set a new + target size for the device. + """ if not isinstance(newsize, Size): raise ValueError("new size must of type Size")
- if self.maxSize and newsize > self.maxSize: + # only calculate these once + max_size = self.maxSize + min_size = self.minSize + if max_size and newsize > max_size: raise errors.DeviceError("device cannot be larger than %s" % - (self.maxSize,), self.name) + max_size, self.name) + elif min_size and newsize < min_size: + raise errors.DeviceError("device cannot be smaller than %s" % + min_size, self.name) + self._size = newsize
size = property(lambda x: x._getSize(), lambda x, y: x._setSize(y), doc="The device's size, accounting for pending changes")
+ def readCurrentSize(self): + log_method_call(self, exists=self.exists, path=self.path, + sysfsPath=self.sysfsPath) + size = Size(0) + if self.exists and os.path.exists(self.path) and \ + os.path.isdir(self.sysfsPath): + blocks = int(util.get_sysfs_attr(self.sysfsPath, "size")) + size = Size(blocks * LINUX_SECTOR_SIZE) + + return size + @property def currentSize(self): """ The device's actual size, generally the size discovered by using @@ -569,12 +559,17 @@ class StorageDevice(Device):
If the device does not exist, then the actual size is 0. """ - size = 0 - if self.exists and self.partedDevice: - size = Size(self.partedDevice.getLength(unit="B")) - elif self.exists: - size = self._size - return size + if self._currentSize == Size(0): + self._currentSize = self.readCurrentSize() + return self._currentSize + + def updateSize(self): + """ Update size, currentSize, and targetSize to actual size. """ + self._currentSize = Size(0) + new_size = self.currentSize + self._size = new_size + self._targetSize = new_size # bypass setter checks + log.debug("updated %s size to %s (%s)", self.name, self.size, new_size)
@property def minSize(self): @@ -658,8 +653,6 @@ class StorageDevice(Device):
@property def model(self): - if not self._model: - self._model = getattr(self.partedDevice, "model", "") return self._model
@property diff --git a/blivet/devicetree.py b/blivet/devicetree.py index 70b95f1..a70d7b9 100644 --- a/blivet/devicetree.py +++ b/blivet/devicetree.py @@ -1085,6 +1085,7 @@ class DeviceTree(object): device = diskType(name, major=udev.device_get_major(info), minor=udev.device_get_minor(info), + model=udev.device_get_model(info), sysfsPath=sysfs_path, **kwargs)
if diskType == DASDDevice: @@ -1203,9 +1204,7 @@ class DeviceTree(object): # The first step is to either look up or create the device # if device: - # we successfully looked up the device. skip to format handling. - # first, grab the parted.Device while it's active - _unused = device.partedDevice + pass elif udev.device_is_loop(info): log.info("%s is a loop device", name) device = self.addUdevLoopDevice(info) @@ -1529,6 +1528,7 @@ class DeviceTree(object):
if lv_device.status: lv_device.updateSysfsPath() + lv_device.updateSize() lv_info = udev.get_device(lv_device.sysfsPath) if not lv_info: log.error("failed to get udev data for lv %s", lv_device.name) @@ -1871,15 +1871,10 @@ class DeviceTree(object): kwargs["name"] = "luks-%s" % uuid elif format_type in formats.mdraid.MDRaidMember._udevTypes: # mdraid - try: - kwargs["mdUuid"] = udev.device_get_md_uuid(info) - except KeyError: - log.warning("mdraid member %s has no md uuid", name) - # reset the uuid to the member-specific value # this will be None for members of v0 metadata arrays - kwargs["uuid"] = udev.device_get_md_device_uuid(info) - + kwargs["uuid"] = info.get("ID_FS_UUID_SUB") + kwargs["mdUuid"] = uuid kwargs["biosraid"] = udev.device_is_biosraid_member(info) elif format_type == "LVM2_member": # lvm diff --git a/blivet/platform.py b/blivet/platform.py index 1376ffd..ad048b9 100644 --- a/blivet/platform.py +++ b/blivet/platform.py @@ -125,10 +125,12 @@ class Platform(object): if flags.testing: return self.defaultDiskLabelType
+ parted_device = parted.Device(path=device.path) + # if there's a required type for this device type, use that - labelType = self.requiredDiskLabelType(device.partedDevice.type) + labelType = self.requiredDiskLabelType(parted_device.type) log.debug("required disklabel type for %s (%s) is %s", - device.name, device.partedDevice.type, labelType) + device.name, parted_device.type, labelType) if not labelType: # otherwise, use the first supported type for this platform # that is large enough to address the whole device @@ -136,8 +138,8 @@ class Platform(object): log.debug("default disklabel type for %s is %s", device.name, labelType) for lt in self.diskLabelTypes: - l = parted.freshDisk(device=device.partedDevice, ty=lt) - if l.maxPartitionStartSector > device.partedDevice.length: + l = parted.freshDisk(device=parted_device, ty=lt) + if l.maxPartitionStartSector > parted_device.length: labelType = lt log.debug("selecting %s disklabel for %s based on size", labelType, device.name) diff --git a/tests/storagetestcase.py b/tests/storagetestcase.py index 615e727..8293818 100644 --- a/tests/storagetestcase.py +++ b/tests/storagetestcase.py @@ -66,10 +66,7 @@ class StorageTestCase(unittest.TestCase): device = device_class(*args, **kwargs)
if exists: - # set up mock parted.Device w/ correct size - device._partedDevice = Mock() - device._partedDevice.getLength = Mock(return_value=int(device.size.convertTo(spec="B"))) - device._partedDevice.sectorSize = 512 + device._currentSize = kwargs.get("size")
if isinstance(device, blivet.devices.PartitionDevice): #if exists:
mediaPresent indicates whether a device node is backed by readable/writeable media. It is generally only meaningful for root devices (devices with no parents). There are only a few instances in which this is relevant: cdrom/dvd, card readers, md containers, and some goofy raid hardware that creates a zero-size block device node. If you need a general- purpose property to tell you if a device is available for general use, what you want is the device's status attribute.
(cherry picked from commit 0d7c5749f4a48700ccfb2294fcaa7feab9e7dd49)
Related: rhbz#1069597 --- blivet/devices/device.py | 5 ----- blivet/devices/md.py | 5 ----- blivet/devices/storage.py | 5 +++++ 3 files changed, 5 insertions(+), 10 deletions(-)
diff --git a/blivet/devices/device.py b/blivet/devices/device.py index 1824f8c..98db834 100644 --- a/blivet/devices/device.py +++ b/blivet/devices/device.py @@ -298,11 +298,6 @@ class Device(util.ObjectID):
return services
- @property - def mediaPresent(self): - """ True if this device contains usable media. """ - return True - @classmethod def isNameValid(cls, name): # pylint: disable=unused-argument """Is the device name valid for the device type?""" diff --git a/blivet/devices/md.py b/blivet/devices/md.py index 33d91b5..096975a 100644 --- a/blivet/devices/md.py +++ b/blivet/devices/md.py @@ -558,11 +558,6 @@ class MDRaidArrayDevice(ContainerDevice): # (the device node does not allow read / write calls) if self.type == "mdcontainer": return False - # BIOS RAID sets should show as present even when teared down - elif self.type == "mdbiosraidarray": - return True - elif flags.testing: - return True else: return super(MDRaidArrayDevice, self).mediaPresent
diff --git a/blivet/devices/storage.py b/blivet/devices/storage.py index 6128886..b6ee4dc 100644 --- a/blivet/devices/storage.py +++ b/blivet/devices/storage.py @@ -582,6 +582,11 @@ class StorageDevice(Device): return self.alignTargetSize(self.format.maxSize) if self.resizable else self.currentSize
@property + def mediaPresent(self): + """ True if this device contains usable media. """ + return True + + @property def status(self): """ This device's status.
Also make sure _model and _vendor attrs are never set to None in the StorageDevice constructor.
(cherry picked from commit 067183942c97f432e739c979c4b1d7a9816eef06)
Related: rhbz#1069597 --- blivet/devices/disk.py | 2 +- blivet/devices/storage.py | 4 ++-- blivet/devicetree.py | 9 +++------ 3 files changed, 6 insertions(+), 9 deletions(-)
diff --git a/blivet/devices/disk.py b/blivet/devices/disk.py index d17e975..3638f50 100644 --- a/blivet/devices/disk.py +++ b/blivet/devices/disk.py @@ -105,7 +105,7 @@ class DiskDevice(StorageDevice):
@property def description(self): - return self.model + return " ".join(s for s in (self.vendor, self.model) if s)
def _preDestroy(self): """ Destroy the device. """ diff --git a/blivet/devices/storage.py b/blivet/devices/storage.py index b6ee4dc..348eb65 100644 --- a/blivet/devices/storage.py +++ b/blivet/devices/storage.py @@ -112,8 +112,8 @@ class StorageDevice(Device): self.major = util.numeric_type(major) self.minor = util.numeric_type(minor) self._serial = serial - self._vendor = vendor - self._model = model + self._vendor = vendor or "" + self._model = model or "" self.bus = bus
self.protected = False diff --git a/blivet/devicetree.py b/blivet/devicetree.py index a70d7b9..915b94e 100644 --- a/blivet/devicetree.py +++ b/blivet/devicetree.py @@ -1000,12 +1000,10 @@ class DeviceTree(object): serial = udev.device_get_serial(info) bus = udev.device_get_bus(info)
- # udev doesn't always provide a vendor. - vendor = udev.device_get_vendor(info) - if not vendor: - vendor = "" + vendor = util.get_sysfs_attr(sysfs_path, "device/vendor") + model = util.get_sysfs_attr(sysfs_path, "device/model")
- kwargs = { "serial": serial, "vendor": vendor, "bus": bus } + kwargs = { "serial": serial, "vendor": vendor, "model": model, "bus": bus } if udev.device_is_iscsi(info): diskType = iScsiDiskDevice initiator = udev.device_get_iscsi_initiator(info) @@ -1085,7 +1083,6 @@ class DeviceTree(object): device = diskType(name, major=udev.device_get_major(info), minor=udev.device_get_minor(info), - model=udev.device_get_model(info), sysfsPath=sysfs_path, **kwargs)
if diskType == DASDDevice:
On Fri, 2015-06-26 at 16:54 -0500, David Lehman wrote:
Deleting a parted.Device causes parted to close its rw fd for the device, which triggers a change uevent on that device, which could in turn trigger any number of actions via udev rules. One example is when we reset a Blivet instance: All devices are deleted, including their parted.Device instances, which triggers a change uevent for every device. In response to these events, mdadm's udev rules activate all arrays on those devices. Activating devices -- even indirectly -- without cause, is not acceptable behavior for a storage library.
This also adds a trailing comma to 1-tuples in variable_copy arguments. In my testing, ('foo') is the string 'foo' -- not a tuple with lone element 'foo'.
(cherry picked from commit 73d9c995c8d46f1aa248d1ade3bbb1838653b6b0)
Resolves: rhbz#1069597
blivet/deviceaction.py | 9 ---- blivet/devices/btrfs.py | 3 ++ blivet/devices/container.py | 3 ++ blivet/devices/device.py | 2 +- blivet/devices/disk.py | 14 ++---- blivet/devices/dm.py | 3 +- blivet/devices/file.py | 9 ++++ blivet/devices/lib.py | 3 ++ blivet/devices/luks.py | 6 +-- blivet/devices/md.py | 19 ++++---- blivet/devices/nfs.py | 3 ++ blivet/devices/nodev.py | 2 + blivet/devices/partition.py | 41 +++++++++-------- blivet/devices/storage.py | 107 +++++++++++++++++++++----------------------- blivet/devicetree.py | 15 +++---- blivet/platform.py | 10 +++-- tests/storagetestcase.py | 5 +-- 17 files changed, 124 insertions(+), 130 deletions(-)
diff --git a/blivet/deviceaction.py b/blivet/deviceaction.py index a8cd489..bc7c1a0 100644 --- a/blivet/deviceaction.py +++ b/blivet/deviceaction.py @@ -28,7 +28,6 @@ from .util import get_current_entropy from .devices import StorageDevice from .devices import PartitionDevice, LVMLogicalVolumeDevice from .formats import getFormat, luks -from .storage_log import log_exception_info from parted import partitionFlag, PARTITION_LBA from .i18n import _, N_ from .callbacks import CreateFormatPreData, CreateFormatPostData @@ -344,14 +343,6 @@ class ActionDestroyDevice(DeviceAction): super(ActionDestroyDevice, self).execute(callbacks=None) self.device.destroy()
# Make sure libparted does not keep cached info for this device# and returns it when we create a new device with the same nameif self.device.partedDevice:try:self.device.partedDevice.removeFromCache()except Exception: # pylint: disable=broad-exceptlog_exception_info(fmt_str="failed to remove info for device %s from libparted cache", fmt_args=[self.device])- def requires(self, action): """ Return True if self requires action.
diff --git a/blivet/devices/btrfs.py b/blivet/devices/btrfs.py index 8e47708..eeb203f 100644 --- a/blivet/devices/btrfs.py +++ b/blivet/devices/btrfs.py @@ -61,6 +61,9 @@ class BTRFSDevice(StorageDevice): self.sysfsPath = self.parents[0].sysfsPath log.debug("%s sysfsPath set to %s", self.name, self.sysfsPath)
- def updateSize(self):
pass- def _postCreate(self): super(BTRFSDevice, self)._postCreate() self.format.exists = True
diff --git a/blivet/devices/container.py b/blivet/devices/container.py index d8a1379..6e5683e 100644 --- a/blivet/devices/container.py +++ b/blivet/devices/container.py @@ -200,3 +200,6 @@ class ContainerDevice(StorageDevice):
if member in self.parents: self.parents.remove(member)
- def updateSize(self):
passdiff --git a/blivet/devices/device.py b/blivet/devices/device.py index 498fef4..1824f8c 100644 --- a/blivet/devices/device.py +++ b/blivet/devices/device.py @@ -99,7 +99,7 @@ class Device(util.ObjectID): """ return util.variable_copy(self, memo, omit=('_raidSet', 'node'),
shallow=('_partedDevice', '_partedPartition'))
shallow=('_partedPartition',))def __repr__(self): s = ("%(type)s instance (%(id)s) --\n"
diff --git a/blivet/devices/disk.py b/blivet/devices/disk.py index 6a9a91a..d17e975 100644 --- a/blivet/devices/disk.py +++ b/blivet/devices/disk.py @@ -27,6 +27,7 @@ import block from .. import errors from .. import util from ..flags import flags +from ..size import Size from ..storage_log import log_method_call from .. import udev
@@ -89,8 +90,7 @@ class DiskDevice(StorageDevice):
def __repr__(self): s = StorageDevice.__repr__(self)
s += (" removable = %(removable)s partedDevice = %(partedDevice)r" %{"removable": self.removable, "partedDevice": self.partedDevice})
s += (" removable = %(removable)s" % {"removable": self.removable}) return s@property
@@ -98,23 +98,15 @@ class DiskDevice(StorageDevice): if flags.testing: return True
if not self.partedDevice:return False# Some drivers (cpqarray <blegh>) make block device nodes for # controllers with no disks attached and then report a 0 size, # treat this as no media presentreturn self.partedDevice.getLength(unit="B") != 0
return self.exists and self.currentSize > Size(0)@property def description(self): return self.model
- @property
- def size(self):
""" The disk's size """return super(DiskDevice, self).size- def _preDestroy(self): """ Destroy the device. """ log_method_call(self, self.name, status=self.status)
diff --git a/blivet/devices/dm.py b/blivet/devices/dm.py index 57314bd..9326786 100644 --- a/blivet/devices/dm.py +++ b/blivet/devices/dm.py @@ -34,6 +34,7 @@ import logging log = logging.getLogger("blivet")
from .storage import StorageDevice +from .lib import LINUX_SECTOR_SIZE
class DMDevice(StorageDevice): """ A device-mapper device """ @@ -181,7 +182,7 @@ class DMLinearDevice(DMDevice): """ Open, or set up, a device. """ log_method_call(self, self.name, orig=orig, status=self.status, controllable=self.controllable)
slave_length = self.slave.partedDevice.length
slave_length = self.slave.currentSize / LINUX_SECTOR_SIZE dm.dm_create_linear(self.name, self.slave.path, slave_length, self.dmUuid)diff --git a/blivet/devices/file.py b/blivet/devices/file.py index da231c5..7aaf011 100644 --- a/blivet/devices/file.py +++ b/blivet/devices/file.py @@ -21,6 +21,7 @@ #
import os +import stat
from .. import util from ..storage_log import log_method_call @@ -80,6 +81,14 @@ class FileDevice(StorageDevice):
return os.path.normpath("%s%s" % (root, self.name))
- def _getSize(self):
size = self._sizeif self.exists and os.path.exists(self.path):st = os.stat(self.path)size = Size(st[stat.ST_SIZE])return size- def _preSetup(self, orig=False): if self.format and self.format.exists and not self.format.status: self.format.device = self.path
diff --git a/blivet/devices/lib.py b/blivet/devices/lib.py index c4996db..979dd6e 100644 --- a/blivet/devices/lib.py +++ b/blivet/devices/lib.py @@ -20,6 +20,9 @@ # from .. import errors from .. import udev +from ..size import Size
+LINUX_SECTOR_SIZE = Size(512)
def get_device_majors(): majors = {} diff --git a/blivet/devices/luks.py b/blivet/devices/luks.py index f02cd28..197926f 100644 --- a/blivet/devices/luks.py +++ b/blivet/devices/luks.py @@ -22,8 +22,6 @@ # device backend modules from ..devicelibs import crypto
-from ..size import Size
import logging log = logging.getLogger("blivet")
@@ -63,10 +61,10 @@ class LUKSDevice(DMCryptDevice):
@property def size(self):
if not self.exists or not self.partedDevice:
if not self.exists: size = self.slave.size - crypto.LUKS_METADATA_SIZE else:
size = Size(self.partedDevice.getLength(unit="B"))
size = self.currentSize return sizedef _postCreate(self):
diff --git a/blivet/devices/md.py b/blivet/devices/md.py index 8ce7854..33d91b5 100644 --- a/blivet/devices/md.py +++ b/blivet/devices/md.py @@ -28,7 +28,6 @@ from .. import util from ..flags import flags from ..storage_log import log_method_call from .. import udev -from ..size import Size from ..i18n import P_
import logging @@ -81,15 +80,15 @@ class MDRaidArrayDevice(ContainerDevice): self._memberDevices = 0 # the number of active (non-spare) members self._totalDevices = 0 # the total number of members
if level == "container":self._type = "mdcontainer"self.level = levelsuper(MDRaidArrayDevice, self).__init__(name, fmt=fmt, uuid=uuid, exists=exists, size=size, parents=parents, sysfsPath=sysfsPath)
if level == "container":self._type = "mdcontainer"self.level = level
This chunk comes from a different commit and is unrelated to the rest of this commit, I think.
# For new arrays check if we have enough members if (not exists and parents and len(parents) < self.level.min_members): for dev in self.parents:@@ -196,7 +195,7 @@ class MDRaidArrayDevice(ContainerDevice): if self.type == "mdbiosraidarray": return self._size
if not self.exists or not self.partedDevice:
if not self.exists or not self.mediaPresent: try: size = self.level.get_size([d.size for d in self.devices], self.memberDevices,@@ -207,11 +206,15 @@ class MDRaidArrayDevice(ContainerDevice): size = 0 log.debug("non-existent RAID %s size == %s", self.level, size) else:
size = Size(self.partedDevice.getLength(unit="B"))
size = self.currentSize log.debug("existing RAID %s size == %s", self.level, size) return sizedef updateSize(self):
# pylint: disable=bad-super-callsuper(ContainerDevice, self).updateSize()@property def description(self): if self.type == "mdcontainer":
@@ -561,7 +564,7 @@ class MDRaidArrayDevice(ContainerDevice): elif flags.testing: return True else:
return self.partedDevice is not None
return super(MDRaidArrayDevice, self).mediaPresent@property def model(self):
diff --git a/blivet/devices/nfs.py b/blivet/devices/nfs.py index 513a11c..da00114 100644 --- a/blivet/devices/nfs.py +++ b/blivet/devices/nfs.py @@ -69,6 +69,9 @@ class NFSDevice(StorageDevice, NetworkStorageDevice): """ Destroy the device. """ log_method_call(self, self.name, status=self.status)
- def updateSize(self):
pass- @classmethod def isNameValid(cls, name): # Override StorageDevice.isNameValid to allow /
diff --git a/blivet/devices/nodev.py b/blivet/devices/nodev.py index a66636c..1aee5d3 100644 --- a/blivet/devices/nodev.py +++ b/blivet/devices/nodev.py @@ -69,6 +69,8 @@ class NoDevice(StorageDevice): log_method_call(self, self.name, status=self.status) self._preDestroy()
- def udpateSize(self):
passclass TmpFSDevice(NoDevice): """ A nodev device for a tmpfs filesystem. """ diff --git a/blivet/devices/partition.py b/blivet/devices/partition.py index 2701572..18add66 100644 --- a/blivet/devices/partition.py +++ b/blivet/devices/partition.py @@ -125,6 +125,12 @@ class PartitionDevice(StorageDevice):
self._bootable = False
# FIXME: Validate partType, but only if this is a new partition# Otherwise, overwrite it with the partition's type.self._partType = Noneself._partedPartition = Noneself._origPath = NoneStorageDevice.__init__(self, name, fmt=fmt, size=size, major=major, minor=minor, exists=exists, sysfsPath=sysfsPath, parents=parents)@@ -134,13 +140,6 @@ class PartitionDevice(StorageDevice): self.req_disks = list(self.parents) self.parents = []
# FIXME: Validate partType, but only if this is a new partition# Otherwise, overwrite it with the partition's type.self._partType = Noneself._partedPartition = Noneself._origPath = Noneself._currentSize = 0# FIXME: Validate size, but only if this is a new partition. # For existing partitions we will get the size from # parted.@@ -529,7 +528,6 @@ class PartitionDevice(StorageDevice): return
self._size = Size(self.partedPartition.getLength(unit="B"))
self._currentSize = self._size self.targetSize = self._size self._partType = self.partedPartition.type@@ -592,7 +590,6 @@ class PartitionDevice(StorageDevice): DeviceFormat(device=self.path, exists=True).destroy()
StorageDevice._postCreate(self)
self._currentSize = Size(self.partedPartition.getLength(unit="B"))def create(self): """ Create the device. """
@@ -661,7 +658,7 @@ class PartitionDevice(StorageDevice): end=geometry.end)
self.disk.format.commit()
self._currentSize = Size(partition.getLength(unit="B"))
self.updateSize()def _preDestroy(self): StorageDevice._preDestroy(self)
@@ -728,12 +725,16 @@ class PartitionDevice(StorageDevice): return size
def _setSize(self, newsize):
""" Set the device's size (for resize, not creation).
""" Set the device's size.Most devices have two scenarios for setting a size:
Arguments:
1) set actual/current size2) set target for resize
newsize -- the new size
Partitions have a third scenario:3) update size of an allocated-but-non-existent partition """ log_method_call(self, self.name, status=self.status, size=self._size, newsize=newsize)@@ -746,6 +747,12 @@ class PartitionDevice(StorageDevice): self.req_size = newsize self.req_base_size = newsize
if self.exists:super(PartitionDevice, self)._setSize(newsize)return# the rest is for changing the size of an allocated-but-not-existing# partition, which I'm not sure is advisable if newsize > self.disk.size: raise ValueError("partition size would exceed disk size")@@ -829,14 +836,6 @@ class PartitionDevice(StorageDevice): unalignedMax = min(maxFormatSize, maxPartSize) if maxFormatSize else maxPartSize return self.alignTargetSize(unalignedMax)
- @property
- def currentSize(self):
if self.exists:return self._currentSizeelse:return 0- @property def resizable(self): return super(PartitionDevice, self).resizable and \ self.disk.type != 'dasd'
diff --git a/blivet/devices/storage.py b/blivet/devices/storage.py index 73ba645..6128886 100644 --- a/blivet/devices/storage.py +++ b/blivet/devices/storage.py @@ -22,8 +22,6 @@
import os import copy -import parted -import _ped import pyudev
from .. import errors @@ -38,6 +36,7 @@ import logging log = logging.getLogger("blivet")
from .device import Device +from .lib import LINUX_SECTOR_SIZE
class StorageDevice(Device): """ A generic storage device. @@ -105,7 +104,11 @@ class StorageDevice(Device): Device.__init__(self, name, parents=parents)
self._format = None
# The size will be overridden by a call to updateSize at the end of this# method for existing and active devices. self._size = Size(util.numeric_type(size))self._currentSize = self._size if self.exists else Size(0) self.major = util.numeric_type(major) self.minor = util.numeric_type(minor) self._serial = serial@@ -123,16 +126,8 @@ class StorageDevice(Device):
self.deviceLinks = []
if self.exists and flags.testing and not self._size:def read_int_from_sys(path):return int(open(path).readline().strip())device_root = "/sys/class/block/%s" % self.nameif os.path.exists("%s/queue" % device_root):sector_size = read_int_from_sys("%s/queue/logical_block_size"% device_root)size = read_int_from_sys("%s/size" % device_root)self._size = Size(size * sector_size)
if self.exists and self.status:self.updateSize() self._orig_size = self._size@@ -199,26 +194,6 @@ class StorageDevice(Device): """ True if this device, or any it requires, is encrypted. """ return self._encrypted or any(p.encrypted for p in self.parents)
- def _getPartedDevicePath(self):
return self.path- @property
- def partedDevice(self):
devicePath = self._getPartedDevicePath()if self.exists and self.status and not self._partedDevice:log.debug("looking up parted Device: %s", devicePath)# We aren't guaranteed to be able to get a device. In# particular, built-in USB flash readers show up as devices but# do not always have any media present, so parted won't be able# to find a device.try:self._partedDevice = parted.Device(path=devicePath)except (_ped.IOException, _ped.DeviceException):passreturn self._partedDevice- @property def raw_device(self): """ The device itself, or when encrypted, the backing device. """
@@ -281,12 +256,12 @@ class StorageDevice(Device): " format = %(format)s\n" " major = %(major)s minor = %(minor)s exists = %(exists)s" " protected = %(protected)s\n"
" sysfs path = %(sysfs)s partedDevice = %(partedDevice)s\n"
" sysfs path = %(sysfs)s\n" " target size = %(targetSize)s path = %(path)s\n" " format args = %(formatArgs)s originalFormat = %(origFmt)s" % {"uuid": self.uuid, "format": self.format, "size": self.size, "major": self.major, "minor": self.minor, "exists": self.exists,
"sysfs": self.sysfsPath, "partedDevice": self.partedDevice,
"sysfs": self.sysfsPath, "targetSize": self.targetSize, "path": self.path, "protected": self.protected, "formatArgs": self.formatArgs, "origFmt": self.originalFormat.type})@@ -410,9 +385,10 @@ class StorageDevice(Device): def _postSetup(self): """ Perform post-setup operations. """ udev.settle()
# we always probe since the device may not be set up when we want# information about itself._size = self.currentSize
self.updateSysfsPath()# the device may not be set up when we want information about itif self._size == Size(0):self.updateSize()# # teardown
@@ -487,9 +463,7 @@ class StorageDevice(Device): udev.settle()
# make sure that targetSize is updated to reflect the actual size
if self.resizable:self._partedDevice = Noneself._targetSize = self.currentSize
self.updateSize()# # destroy
@@ -535,12 +509,6 @@ class StorageDevice(Device):
def _getSize(self): """ Get the device's size, accounting for pending changes. """
if self.exists and not self.mediaPresent:return 0if self.exists and self.partedDevice:self._size = self.currentSizesize = self._size if self.exists and self.resizable: size = self.targetSize@@ -548,19 +516,41 @@ class StorageDevice(Device): return size
def _setSize(self, newsize):
""" Set the device's size to a new value. """
""" Set the device's size to a new value.This is not adequate to set up a resize as it does not set a newtarget size for the device.""" if not isinstance(newsize, Size): raise ValueError("new size must of type Size")
if self.maxSize and newsize > self.maxSize:
# only calculate these oncemax_size = self.maxSizemin_size = self.minSizeif max_size and newsize > max_size: raise errors.DeviceError("device cannot be larger than %s" %
(self.maxSize,), self.name)
max_size, self.name)elif min_size and newsize < min_size:raise errors.DeviceError("device cannot be smaller than %s" %min_size, self.name)self._size = newsizesize = property(lambda x: x._getSize(), lambda x, y: x._setSize(y), doc="The device's size, accounting for pending changes")
def readCurrentSize(self):
log_method_call(self, exists=self.exists, path=self.path,sysfsPath=self.sysfsPath)size = Size(0)if self.exists and os.path.exists(self.path) and \os.path.isdir(self.sysfsPath):blocks = int(util.get_sysfs_attr(self.sysfsPath, "size"))size = Size(blocks * LINUX_SECTOR_SIZE)return size@property def currentSize(self): """ The device's actual size, generally the size discovered by using
@@ -569,12 +559,17 @@ class StorageDevice(Device):
If the device does not exist, then the actual size is 0. """
size = 0if self.exists and self.partedDevice:size = Size(self.partedDevice.getLength(unit="B"))elif self.exists:size = self._sizereturn size
if self._currentSize == Size(0):self._currentSize = self.readCurrentSize()return self._currentSizedef updateSize(self):
""" Update size, currentSize, and targetSize to actual size. """self._currentSize = Size(0)new_size = self.currentSizeself._size = new_sizeself._targetSize = new_size # bypass setter checkslog.debug("updated %s size to %s (%s)", self.name, self.size, new_size)@property def minSize(self):
@@ -658,8 +653,6 @@ class StorageDevice(Device):
@property def model(self):
if not self._model:self._model = getattr(self.partedDevice, "model", "") return self._model@property
diff --git a/blivet/devicetree.py b/blivet/devicetree.py index 70b95f1..a70d7b9 100644 --- a/blivet/devicetree.py +++ b/blivet/devicetree.py @@ -1085,6 +1085,7 @@ class DeviceTree(object): device = diskType(name, major=udev.device_get_major(info), minor=udev.device_get_minor(info),
model=udev.device_get_model(info), sysfsPath=sysfs_path, **kwargs) if diskType == DASDDevice:@@ -1203,9 +1204,7 @@ class DeviceTree(object): # The first step is to either look up or create the device # if device:
# we successfully looked up the device. skip to format handling.# first, grab the parted.Device while it's active_unused = device.partedDevice
pass elif udev.device_is_loop(info): log.info("%s is a loop device", name) device = self.addUdevLoopDevice(info)@@ -1529,6 +1528,7 @@ class DeviceTree(object):
if lv_device.status: lv_device.updateSysfsPath()
lv_device.updateSize() lv_info = udev.get_device(lv_device.sysfsPath) if not lv_info: log.error("failed to get udev data for lv %s", lv_device.name)@@ -1871,15 +1871,10 @@ class DeviceTree(object): kwargs["name"] = "luks-%s" % uuid elif format_type in formats.mdraid.MDRaidMember._udevTypes: # mdraid
try:kwargs["mdUuid"] = udev.device_get_md_uuid(info)except KeyError:log.warning("mdraid member %s has no md uuid", name)# reset the uuid to the member-specific value # this will be None for members of v0 metadata arrayskwargs["uuid"] = udev.device_get_md_device_uuid(info)
kwargs["uuid"] = info.get("ID_FS_UUID_SUB")kwargs["mdUuid"] = uuid
This chunk also looks unrelated to the rest of the commit.
On 06/29/2015 08:39 AM, Vratislav Podzimek wrote:
On Fri, 2015-06-26 at 16:54 -0500, David Lehman wrote:
Deleting a parted.Device causes parted to close its rw fd for the device, which triggers a change uevent on that device, which could in turn trigger any number of actions via udev rules. One example is when we reset a Blivet instance: All devices are deleted, including their parted.Device instances, which triggers a change uevent for every device. In response to these events, mdadm's udev rules activate all arrays on those devices. Activating devices -- even indirectly -- without cause, is not acceptable behavior for a storage library.
This also adds a trailing comma to 1-tuples in variable_copy arguments. In my testing, ('foo') is the string 'foo' -- not a tuple with lone element 'foo'.
(cherry picked from commit 73d9c995c8d46f1aa248d1ade3bbb1838653b6b0)
Resolves: rhbz#1069597
blivet/deviceaction.py | 9 ---- blivet/devices/btrfs.py | 3 ++ blivet/devices/container.py | 3 ++ blivet/devices/device.py | 2 +- blivet/devices/disk.py | 14 ++---- blivet/devices/dm.py | 3 +- blivet/devices/file.py | 9 ++++ blivet/devices/lib.py | 3 ++ blivet/devices/luks.py | 6 +-- blivet/devices/md.py | 19 ++++---- blivet/devices/nfs.py | 3 ++ blivet/devices/nodev.py | 2 + blivet/devices/partition.py | 41 +++++++++-------- blivet/devices/storage.py | 107 +++++++++++++++++++++----------------------- blivet/devicetree.py | 15 +++---- blivet/platform.py | 10 +++-- tests/storagetestcase.py | 5 +-- 17 files changed, 124 insertions(+), 130 deletions(-)
diff --git a/blivet/deviceaction.py b/blivet/deviceaction.py index a8cd489..bc7c1a0 100644 --- a/blivet/deviceaction.py +++ b/blivet/deviceaction.py @@ -28,7 +28,6 @@ from .util import get_current_entropy from .devices import StorageDevice from .devices import PartitionDevice, LVMLogicalVolumeDevice from .formats import getFormat, luks -from .storage_log import log_exception_info from parted import partitionFlag, PARTITION_LBA from .i18n import _, N_ from .callbacks import CreateFormatPreData, CreateFormatPostData @@ -344,14 +343,6 @@ class ActionDestroyDevice(DeviceAction): super(ActionDestroyDevice, self).execute(callbacks=None) self.device.destroy()
# Make sure libparted does not keep cached info for this device# and returns it when we create a new device with the same nameif self.device.partedDevice:try:self.device.partedDevice.removeFromCache()except Exception: # pylint: disable=broad-exceptlog_exception_info(fmt_str="failed to remove info for device %s from libparted cache", fmt_args=[self.device])def requires(self, action): """ Return True if self requires action.diff --git a/blivet/devices/btrfs.py b/blivet/devices/btrfs.py index 8e47708..eeb203f 100644 --- a/blivet/devices/btrfs.py +++ b/blivet/devices/btrfs.py @@ -61,6 +61,9 @@ class BTRFSDevice(StorageDevice): self.sysfsPath = self.parents[0].sysfsPath log.debug("%s sysfsPath set to %s", self.name, self.sysfsPath)
- def updateSize(self):
passdef _postCreate(self): super(BTRFSDevice, self)._postCreate() self.format.exists = Truediff --git a/blivet/devices/container.py b/blivet/devices/container.py index d8a1379..6e5683e 100644 --- a/blivet/devices/container.py +++ b/blivet/devices/container.py @@ -200,3 +200,6 @@ class ContainerDevice(StorageDevice):
if member in self.parents: self.parents.remove(member)
- def updateSize(self):
passdiff --git a/blivet/devices/device.py b/blivet/devices/device.py index 498fef4..1824f8c 100644 --- a/blivet/devices/device.py +++ b/blivet/devices/device.py @@ -99,7 +99,7 @@ class Device(util.ObjectID): """ return util.variable_copy(self, memo, omit=('_raidSet', 'node'),
shallow=('_partedDevice', '_partedPartition'))
shallow=('_partedPartition',)) def __repr__(self): s = ("%(type)s instance (%(id)s) --\n"diff --git a/blivet/devices/disk.py b/blivet/devices/disk.py index 6a9a91a..d17e975 100644 --- a/blivet/devices/disk.py +++ b/blivet/devices/disk.py @@ -27,6 +27,7 @@ import block from .. import errors from .. import util from ..flags import flags +from ..size import Size from ..storage_log import log_method_call from .. import udev
@@ -89,8 +90,7 @@ class DiskDevice(StorageDevice):
def __repr__(self): s = StorageDevice.__repr__(self)
s += (" removable = %(removable)s partedDevice = %(partedDevice)r" %{"removable": self.removable, "partedDevice": self.partedDevice})
s += (" removable = %(removable)s" % {"removable": self.removable}) return s @property@@ -98,23 +98,15 @@ class DiskDevice(StorageDevice): if flags.testing: return True
if not self.partedDevice:return False# Some drivers (cpqarray <blegh>) make block device nodes for # controllers with no disks attached and then report a 0 size, # treat this as no media presentreturn self.partedDevice.getLength(unit="B") != 0
return self.exists and self.currentSize > Size(0) @property def description(self): return self.model
- @property
- def size(self):
""" The disk's size """return super(DiskDevice, self).sizedef _preDestroy(self): """ Destroy the device. """ log_method_call(self, self.name, status=self.status)diff --git a/blivet/devices/dm.py b/blivet/devices/dm.py index 57314bd..9326786 100644 --- a/blivet/devices/dm.py +++ b/blivet/devices/dm.py @@ -34,6 +34,7 @@ import logging log = logging.getLogger("blivet")
from .storage import StorageDevice +from .lib import LINUX_SECTOR_SIZE
class DMDevice(StorageDevice): """ A device-mapper device """ @@ -181,7 +182,7 @@ class DMLinearDevice(DMDevice): """ Open, or set up, a device. """ log_method_call(self, self.name, orig=orig, status=self.status, controllable=self.controllable)
slave_length = self.slave.partedDevice.length
slave_length = self.slave.currentSize / LINUX_SECTOR_SIZE dm.dm_create_linear(self.name, self.slave.path, slave_length, self.dmUuid)diff --git a/blivet/devices/file.py b/blivet/devices/file.py index da231c5..7aaf011 100644 --- a/blivet/devices/file.py +++ b/blivet/devices/file.py @@ -21,6 +21,7 @@ #
import os +import stat
from .. import util from ..storage_log import log_method_call @@ -80,6 +81,14 @@ class FileDevice(StorageDevice):
return os.path.normpath("%s%s" % (root, self.name))
- def _getSize(self):
size = self._sizeif self.exists and os.path.exists(self.path):st = os.stat(self.path)size = Size(st[stat.ST_SIZE])return sizedef _preSetup(self, orig=False): if self.format and self.format.exists and not self.format.status: self.format.device = self.pathdiff --git a/blivet/devices/lib.py b/blivet/devices/lib.py index c4996db..979dd6e 100644 --- a/blivet/devices/lib.py +++ b/blivet/devices/lib.py @@ -20,6 +20,9 @@ # from .. import errors from .. import udev +from ..size import Size
+LINUX_SECTOR_SIZE = Size(512)
def get_device_majors(): majors = {} diff --git a/blivet/devices/luks.py b/blivet/devices/luks.py index f02cd28..197926f 100644 --- a/blivet/devices/luks.py +++ b/blivet/devices/luks.py @@ -22,8 +22,6 @@ # device backend modules from ..devicelibs import crypto
-from ..size import Size
- import logging log = logging.getLogger("blivet")
@@ -63,10 +61,10 @@ class LUKSDevice(DMCryptDevice):
@property def size(self):
if not self.exists or not self.partedDevice:
if not self.exists: size = self.slave.size - crypto.LUKS_METADATA_SIZE else:
size = Size(self.partedDevice.getLength(unit="B"))
size = self.currentSize return size def _postCreate(self):diff --git a/blivet/devices/md.py b/blivet/devices/md.py index 8ce7854..33d91b5 100644 --- a/blivet/devices/md.py +++ b/blivet/devices/md.py @@ -28,7 +28,6 @@ from .. import util from ..flags import flags from ..storage_log import log_method_call from .. import udev -from ..size import Size from ..i18n import P_
import logging @@ -81,15 +80,15 @@ class MDRaidArrayDevice(ContainerDevice): self._memberDevices = 0 # the number of active (non-spare) members self._totalDevices = 0 # the total number of members
if level == "container":self._type = "mdcontainer"self.level = levelsuper(MDRaidArrayDevice, self).__init__(name, fmt=fmt, uuid=uuid, exists=exists, size=size, parents=parents, sysfsPath=sysfsPath)
if level == "container":self._type = "mdcontainer"self.level = levelThis chunk comes from a different commit and is unrelated to the rest of this commit, I think.
It's a followup to the original commit. The StorageDevice constructor now calls the size setter, which needs to know which type of md array it is.
# For new arrays check if we have enough members if (not exists and parents and len(parents) < self.level.min_members): for dev in self.parents:@@ -196,7 +195,7 @@ class MDRaidArrayDevice(ContainerDevice): if self.type == "mdbiosraidarray": return self._size
if not self.exists or not self.partedDevice:
if not self.exists or not self.mediaPresent: try: size = self.level.get_size([d.size for d in self.devices], self.memberDevices,@@ -207,11 +206,15 @@ class MDRaidArrayDevice(ContainerDevice): size = 0 log.debug("non-existent RAID %s size == %s", self.level, size) else:
size = Size(self.partedDevice.getLength(unit="B"))
size = self.currentSize log.debug("existing RAID %s size == %s", self.level, size) return sizedef updateSize(self):
# pylint: disable=bad-super-callsuper(ContainerDevice, self).updateSize()@property def description(self): if self.type == "mdcontainer":@@ -561,7 +564,7 @@ class MDRaidArrayDevice(ContainerDevice): elif flags.testing: return True else:
return self.partedDevice is not None
return super(MDRaidArrayDevice, self).mediaPresent @property def model(self):diff --git a/blivet/devices/nfs.py b/blivet/devices/nfs.py index 513a11c..da00114 100644 --- a/blivet/devices/nfs.py +++ b/blivet/devices/nfs.py @@ -69,6 +69,9 @@ class NFSDevice(StorageDevice, NetworkStorageDevice): """ Destroy the device. """ log_method_call(self, self.name, status=self.status)
- def updateSize(self):
pass@classmethod def isNameValid(cls, name): # Override StorageDevice.isNameValid to allow /diff --git a/blivet/devices/nodev.py b/blivet/devices/nodev.py index a66636c..1aee5d3 100644 --- a/blivet/devices/nodev.py +++ b/blivet/devices/nodev.py @@ -69,6 +69,8 @@ class NoDevice(StorageDevice): log_method_call(self, self.name, status=self.status) self._preDestroy()
def udpateSize(self):
passclass TmpFSDevice(NoDevice): """ A nodev device for a tmpfs filesystem. """
diff --git a/blivet/devices/partition.py b/blivet/devices/partition.py index 2701572..18add66 100644 --- a/blivet/devices/partition.py +++ b/blivet/devices/partition.py @@ -125,6 +125,12 @@ class PartitionDevice(StorageDevice):
self._bootable = False
# FIXME: Validate partType, but only if this is a new partition# Otherwise, overwrite it with the partition's type.self._partType = Noneself._partedPartition = Noneself._origPath = NoneStorageDevice.__init__(self, name, fmt=fmt, size=size, major=major, minor=minor, exists=exists, sysfsPath=sysfsPath, parents=parents)@@ -134,13 +140,6 @@ class PartitionDevice(StorageDevice): self.req_disks = list(self.parents) self.parents = []
# FIXME: Validate partType, but only if this is a new partition# Otherwise, overwrite it with the partition's type.self._partType = Noneself._partedPartition = Noneself._origPath = Noneself._currentSize = 0# FIXME: Validate size, but only if this is a new partition. # For existing partitions we will get the size from # parted.@@ -529,7 +528,6 @@ class PartitionDevice(StorageDevice): return
self._size = Size(self.partedPartition.getLength(unit="B"))
self._currentSize = self._size self.targetSize = self._size self._partType = self.partedPartition.type@@ -592,7 +590,6 @@ class PartitionDevice(StorageDevice): DeviceFormat(device=self.path, exists=True).destroy()
StorageDevice._postCreate(self)
self._currentSize = Size(self.partedPartition.getLength(unit="B")) def create(self): """ Create the device. """@@ -661,7 +658,7 @@ class PartitionDevice(StorageDevice): end=geometry.end)
self.disk.format.commit()
self._currentSize = Size(partition.getLength(unit="B"))
self.updateSize() def _preDestroy(self): StorageDevice._preDestroy(self)@@ -728,12 +725,16 @@ class PartitionDevice(StorageDevice): return size
def _setSize(self, newsize):
""" Set the device's size (for resize, not creation).
""" Set the device's size.Most devices have two scenarios for setting a size:
Arguments:
1) set actual/current size2) set target for resize
newsize -- the new size
Partitions have a third scenario:3) update size of an allocated-but-non-existent partition """ log_method_call(self, self.name, status=self.status, size=self._size, newsize=newsize)@@ -746,6 +747,12 @@ class PartitionDevice(StorageDevice): self.req_size = newsize self.req_base_size = newsize
if self.exists:super(PartitionDevice, self)._setSize(newsize)return# the rest is for changing the size of an allocated-but-not-existing# partition, which I'm not sure is advisable if newsize > self.disk.size: raise ValueError("partition size would exceed disk size")@@ -829,14 +836,6 @@ class PartitionDevice(StorageDevice): unalignedMax = min(maxFormatSize, maxPartSize) if maxFormatSize else maxPartSize return self.alignTargetSize(unalignedMax)
- @property
- def currentSize(self):
if self.exists:return self._currentSizeelse:return 0- @property def resizable(self): return super(PartitionDevice, self).resizable and \ self.disk.type != 'dasd'
diff --git a/blivet/devices/storage.py b/blivet/devices/storage.py index 73ba645..6128886 100644 --- a/blivet/devices/storage.py +++ b/blivet/devices/storage.py @@ -22,8 +22,6 @@
import os import copy -import parted -import _ped import pyudev
from .. import errors @@ -38,6 +36,7 @@ import logging log = logging.getLogger("blivet")
from .device import Device +from .lib import LINUX_SECTOR_SIZE
class StorageDevice(Device): """ A generic storage device. @@ -105,7 +104,11 @@ class StorageDevice(Device): Device.__init__(self, name, parents=parents)
self._format = None
# The size will be overridden by a call to updateSize at the end of this# method for existing and active devices. self._size = Size(util.numeric_type(size))self._currentSize = self._size if self.exists else Size(0) self.major = util.numeric_type(major) self.minor = util.numeric_type(minor) self._serial = serial@@ -123,16 +126,8 @@ class StorageDevice(Device):
self.deviceLinks = []
if self.exists and flags.testing and not self._size:def read_int_from_sys(path):return int(open(path).readline().strip())device_root = "/sys/class/block/%s" % self.nameif os.path.exists("%s/queue" % device_root):sector_size = read_int_from_sys("%s/queue/logical_block_size"% device_root)size = read_int_from_sys("%s/size" % device_root)self._size = Size(size * sector_size)
if self.exists and self.status:self.updateSize() self._orig_size = self._size@@ -199,26 +194,6 @@ class StorageDevice(Device): """ True if this device, or any it requires, is encrypted. """ return self._encrypted or any(p.encrypted for p in self.parents)
- def _getPartedDevicePath(self):
return self.path- @property
- def partedDevice(self):
devicePath = self._getPartedDevicePath()if self.exists and self.status and not self._partedDevice:log.debug("looking up parted Device: %s", devicePath)# We aren't guaranteed to be able to get a device. In# particular, built-in USB flash readers show up as devices but# do not always have any media present, so parted won't be able# to find a device.try:self._partedDevice = parted.Device(path=devicePath)except (_ped.IOException, _ped.DeviceException):passreturn self._partedDevice@property def raw_device(self): """ The device itself, or when encrypted, the backing device. """@@ -281,12 +256,12 @@ class StorageDevice(Device): " format = %(format)s\n" " major = %(major)s minor = %(minor)s exists = %(exists)s" " protected = %(protected)s\n"
" sysfs path = %(sysfs)s partedDevice = %(partedDevice)s\n"
" sysfs path = %(sysfs)s\n" " target size = %(targetSize)s path = %(path)s\n" " format args = %(formatArgs)s originalFormat = %(origFmt)s" % {"uuid": self.uuid, "format": self.format, "size": self.size, "major": self.major, "minor": self.minor, "exists": self.exists,
"sysfs": self.sysfsPath, "partedDevice": self.partedDevice,
"sysfs": self.sysfsPath, "targetSize": self.targetSize, "path": self.path, "protected": self.protected, "formatArgs": self.formatArgs, "origFmt": self.originalFormat.type})@@ -410,9 +385,10 @@ class StorageDevice(Device): def _postSetup(self): """ Perform post-setup operations. """ udev.settle()
# we always probe since the device may not be set up when we want# information about itself._size = self.currentSize
self.updateSysfsPath()# the device may not be set up when we want information about itif self._size == Size(0):self.updateSize() # # teardown@@ -487,9 +463,7 @@ class StorageDevice(Device): udev.settle()
# make sure that targetSize is updated to reflect the actual size
if self.resizable:self._partedDevice = Noneself._targetSize = self.currentSize
self.updateSize() # # destroy@@ -535,12 +509,6 @@ class StorageDevice(Device):
def _getSize(self): """ Get the device's size, accounting for pending changes. """
if self.exists and not self.mediaPresent:return 0if self.exists and self.partedDevice:self._size = self.currentSizesize = self._size if self.exists and self.resizable: size = self.targetSize@@ -548,19 +516,41 @@ class StorageDevice(Device): return size
def _setSize(self, newsize):
""" Set the device's size to a new value. """
""" Set the device's size to a new value.This is not adequate to set up a resize as it does not set a newtarget size for the device.""" if not isinstance(newsize, Size): raise ValueError("new size must of type Size")
if self.maxSize and newsize > self.maxSize:
# only calculate these oncemax_size = self.maxSizemin_size = self.minSizeif max_size and newsize > max_size: raise errors.DeviceError("device cannot be larger than %s" %
(self.maxSize,), self.name)
max_size, self.name)elif min_size and newsize < min_size:raise errors.DeviceError("device cannot be smaller than %s" %min_size, self.name)self._size = newsize size = property(lambda x: x._getSize(), lambda x, y: x._setSize(y), doc="The device's size, accounting for pending changes")def readCurrentSize(self):
log_method_call(self, exists=self.exists, path=self.path,sysfsPath=self.sysfsPath)size = Size(0)if self.exists and os.path.exists(self.path) and \os.path.isdir(self.sysfsPath):blocks = int(util.get_sysfs_attr(self.sysfsPath, "size"))size = Size(blocks * LINUX_SECTOR_SIZE)return size@property def currentSize(self): """ The device's actual size, generally the size discovered by using@@ -569,12 +559,17 @@ class StorageDevice(Device):
If the device does not exist, then the actual size is 0. """
size = 0if self.exists and self.partedDevice:size = Size(self.partedDevice.getLength(unit="B"))elif self.exists:size = self._sizereturn size
if self._currentSize == Size(0):self._currentSize = self.readCurrentSize()return self._currentSizedef updateSize(self):
""" Update size, currentSize, and targetSize to actual size. """self._currentSize = Size(0)new_size = self.currentSizeself._size = new_sizeself._targetSize = new_size # bypass setter checkslog.debug("updated %s size to %s (%s)", self.name, self.size, new_size) @property def minSize(self):@@ -658,8 +653,6 @@ class StorageDevice(Device):
@property def model(self):
if not self._model:self._model = getattr(self.partedDevice, "model", "") return self._model @propertydiff --git a/blivet/devicetree.py b/blivet/devicetree.py index 70b95f1..a70d7b9 100644 --- a/blivet/devicetree.py +++ b/blivet/devicetree.py @@ -1085,6 +1085,7 @@ class DeviceTree(object): device = diskType(name, major=udev.device_get_major(info), minor=udev.device_get_minor(info),
model=udev.device_get_model(info), sysfsPath=sysfs_path, **kwargs) if diskType == DASDDevice:@@ -1203,9 +1204,7 @@ class DeviceTree(object): # The first step is to either look up or create the device # if device:
# we successfully looked up the device. skip to format handling.# first, grab the parted.Device while it's active_unused = device.partedDevice
pass elif udev.device_is_loop(info): log.info("%s is a loop device", name) device = self.addUdevLoopDevice(info)@@ -1529,6 +1528,7 @@ class DeviceTree(object):
if lv_device.status: lv_device.updateSysfsPath()
lv_device.updateSize() lv_info = udev.get_device(lv_device.sysfsPath) if not lv_info: log.error("failed to get udev data for lv %s", lv_device.name)@@ -1871,15 +1871,10 @@ class DeviceTree(object): kwargs["name"] = "luks-%s" % uuid elif format_type in formats.mdraid.MDRaidMember._udevTypes: # mdraid
try:kwargs["mdUuid"] = udev.device_get_md_uuid(info)except KeyError:log.warning("mdraid member %s has no md uuid", name)# reset the uuid to the member-specific value # this will be None for members of v0 metadata arrayskwargs["uuid"] = udev.device_get_md_device_uuid(info)
kwargs["uuid"] = info.get("ID_FS_UUID_SUB")kwargs["mdUuid"] = uuidThis chunk also looks unrelated to the rest of the commit.
Hmm, yeah. I guess this is from a followup to something else. I'll split it out into a separate commit that uses the rebase as "Related" since we won't be able to detect any existing md raid without this.
David
Works for me, thanks for the clarifications.
anaconda-patches@lists.fedorahosted.org