Skip to content

Commit

Permalink
Merge pull request #145 from mulkieran/rhel7-cherry-picks
Browse files Browse the repository at this point in the history
rhel7 cherry picks
  • Loading branch information
mulkieran committed Jun 3, 2015
2 parents 1a944d2 + e633c16 commit dd56b7d
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 33 deletions.
20 changes: 10 additions & 10 deletions blivet/devicelibs/btrfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

from . import raid
from .. import util
from ..errors import BTRFSError
from ..errors import BTRFSError, BTRFSValueError

import logging
log = logging.getLogger("blivet")
Expand Down Expand Up @@ -64,9 +64,9 @@ def create_volume(devices, label=None, data=None, metadata=None):
recognizes the string.
"""
if not devices:
raise ValueError("no devices specified")
raise BTRFSValueError("no devices specified")
elif any([not os.path.exists(d) for d in devices]):
raise ValueError("one or more specified devices not present")
raise BTRFSValueError("one or more specified devices not present")

args = []
if data:
Expand All @@ -90,28 +90,28 @@ def create_volume(devices, label=None, data=None, metadata=None):

def add(mountpoint, device):
if not os.path.ismount(mountpoint):
raise ValueError("volume not mounted")
raise BTRFSValueError("volume not mounted")

return btrfs(["device", "add", device, mountpoint])

def remove(mountpoint, device):
if not os.path.ismount(mountpoint):
raise ValueError("volume not mounted")
raise BTRFSValueError("volume not mounted")

return btrfs(["device", "delete", device, mountpoint])

def create_subvolume(mountpoint, name):
"""Create a subvolume named name below mountpoint mountpoint."""
if not os.path.ismount(mountpoint):
raise ValueError("volume not mounted")
raise BTRFSValueError("volume not mounted")

path = os.path.normpath("%s/%s" % (mountpoint, name))
args = ["subvol", "create", path]
return btrfs(args)

def delete_subvolume(mountpoint, name):
if not os.path.ismount(mountpoint):
raise ValueError("volume not mounted")
raise BTRFSValueError("volume not mounted")

path = os.path.normpath("%s/%s" % (mountpoint, name))
args = ["subvol", "delete", path]
Expand All @@ -123,7 +123,7 @@ def delete_subvolume(mountpoint, name):
# get a list of subvolumes from a mounted btrfs filesystem
def list_subvolumes(mountpoint, snapshots_only=False):
if not os.path.ismount(mountpoint):
raise ValueError("volume not mounted")
raise BTRFSValueError("volume not mounted")

args = ["subvol", "list", "-p", mountpoint]
if snapshots_only:
Expand All @@ -139,7 +139,7 @@ def list_subvolumes(mountpoint, snapshots_only=False):

def get_default_subvolume(mountpoint):
if not os.path.ismount(mountpoint):
raise ValueError("volume not mounted")
raise BTRFSValueError("volume not mounted")

args = ["subvol", "get-default", mountpoint]
buf = btrfs(args, capture=True)
Expand Down Expand Up @@ -170,7 +170,7 @@ def create_snapshot(source, dest, ro=False):
:keyword bool ro: whether to create a read-only snapshot
"""
if not os.path.ismount(source):
raise ValueError("source is not a mounted subvolume")
raise BTRFSValueError("source is not a mounted subvolume")

args = ["subvol", "snapshot"]
if ro:
Expand Down
14 changes: 14 additions & 0 deletions blivet/devicelibs/mdraid.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,20 @@ def mddeactivate(device):
except MDRaidError as msg:
raise MDRaidError("mddeactivate failed for %s: %s" % (device, msg))

def mdrun(device):
"""Start a possibly degraded array.
:param str device: the device to be started
:raises :class:`~.errors.MDRaidError`: on failure
"""
args = ["--run", device]

try:
mdadm(args)
except MDRaidError as msg:
raise MDRaidError("mdrun failed for %s: %s" % (device, msg))

def process_UUIDS(info, UUID_keys):
""" Extract and convert expected UUIDs to canonical form.
Reassign canonicalized UUIDs to corresponding keys.
Expand Down
74 changes: 59 additions & 15 deletions blivet/devices/btrfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def __init__(self, *args, **kwargs):
args = ("btrfs.%d" % self.id,)

if kwargs.get("parents") is None:
raise ValueError("BTRFSDevice must have at least one parent")
raise errors.BTRFSValueError("BTRFSDevice must have at least one parent")

self.req_size = kwargs.pop("size", None)
super(BTRFSDevice, self).__init__(*args, **kwargs)
Expand Down Expand Up @@ -161,6 +161,9 @@ def __init__(self, *args, **kwargs):

super(BTRFSVolumeDevice, self).__init__(*args, **kwargs)

# avoid attribute-defined-outside-init pylint warning
self._dataLevel = self._metaDataLevel = None

# assign after constructor to avoid AttributeErrors in setter functions
self.dataLevel = dataLevel
self.metaDataLevel = metaDataLevel
Expand All @@ -180,6 +183,49 @@ def __init__(self, *args, **kwargs):

self._defaultSubVolumeID = None

def _validateRaidLevel(self, level):
""" Returns an error message if the RAID level is invalid for this
device, otherwise None.
:param level: a RAID level
:type level: :class:`~.devicelibs.raid.RAIDLevel`
:returns: an error message if the RAID level is invalid, else None
:rtype: str or NoneType
"""
if not self.exists and len(self.parents) < level.min_members:
return "RAID level %s requires that device have at least %d members, but device has only %d members." % (level, level.min_members, len(self.parents))
return None

def _setLevel(self, value, data):
""" Sets a valid level for this device and level type.
:param object value: value for this RAID level
:param bool data: True if for data, False if for metadata
:returns: a valid level for value, if any, else None
:rtype: :class:`~.devicelibs.raid.RAIDLevel` or NoneType
:raises: :class:`~.errors.BTRFSValueError` if value represents
an invalid level.
"""
level = None
if value:
try:
levels = btrfs.RAID_levels if data else btrfs.metadata_levels
level = levels.raidLevel(value)
except errors.RaidError:
data_type_str = "data" if data else "metadata"
raise errors.BTRFSValueError("%s is an invalid value for %s RAID level." % (value, data_type_str))

error_msg = self._validateRaidLevel(level)
if error_msg:
raise errors.BTRFSValueError(error_msg)

if data:
self._dataLevel = level
else:
self._metaDataLevel = level

@property
def dataLevel(self):
""" Return the RAID level for data.
Expand All @@ -193,12 +239,11 @@ def dataLevel(self):
def dataLevel(self, value):
""" Set the RAID level for data.
:param value: new raid level
:param type: a valid raid level descriptor
:param object value: new raid level
:returns: None
:raises: :class:`~.errors.BTRFSValueError`
"""
# pylint: disable=attribute-defined-outside-init
self._dataLevel = btrfs.RAID_levels.raidLevel(value) if value else None
self._setLevel(value, True)

@property
def metaDataLevel(self):
Expand All @@ -213,12 +258,11 @@ def metaDataLevel(self):
def metaDataLevel(self, value):
""" Set the RAID level for metadata.
:param value: new raid level
:param type: a valid raid level descriptor
:param object value: new raid level
:returns: None
:raises: :class:`~.errors.BTRFSValueError`
"""
# pylint: disable=attribute-defined-outside-init
self._metaDataLevel = btrfs.metadata_levels.raidLevel(value) if value else None
self._setLevel(value, False)

@property
def formatImmutable(self):
Expand Down Expand Up @@ -263,13 +307,13 @@ def _removeParent(self, member):

def _addSubVolume(self, vol):
if vol.name in [v.name for v in self.subvolumes]:
raise ValueError("subvolume %s already exists" % vol.name)
raise errors.BTRFSValueError("subvolume %s already exists" % vol.name)

self.subvolumes.append(vol)

def _removeSubVolume(self, name):
if name not in [v.name for v in self.subvolumes]:
raise ValueError("cannot remove non-existent subvolume %s" % name)
raise errors.BTRFSValueError("cannot remove non-existent subvolume %s" % name)

names = [v.name for v in self.subvolumes]
self.subvolumes.pop(names.index(name))
Expand Down Expand Up @@ -566,13 +610,13 @@ def __init__(self, *args, **kwargs):
source = kwargs.pop("source", None)
if not kwargs.get("exists") and not source:
# it is possible to remove a source subvol and keep snapshots of it
raise ValueError("non-existent btrfs snapshots must have a source")
raise errors.BTRFSValueError("non-existent btrfs snapshots must have a source")

if source and not isinstance(source, BTRFSDevice):
raise ValueError("btrfs snapshot source must be a btrfs subvolume")
raise errors.BTRFSValueError("btrfs snapshot source must be a btrfs subvolume")

if source and not source.exists:
raise ValueError("btrfs snapshot source must already exist")
raise errors.BTRFSValueError("btrfs snapshot source must already exist")

self.source = source
""" the snapshot's source subvolume """
Expand All @@ -584,7 +628,7 @@ def __init__(self, *args, **kwargs):
if source and getattr(source, "volume", source) != self.volume:
self.volume._removeSubVolume(self.name)
self.parents = []
raise ValueError("btrfs snapshot and source must be in the same volume")
raise errors.BTRFSValueError("btrfs snapshot and source must be in the same volume")

def _create(self):
log_method_call(self, self.name, status=self.status)
Expand Down
32 changes: 26 additions & 6 deletions blivet/devicetree.py
Original file line number Diff line number Diff line change
Expand Up @@ -873,15 +873,15 @@ def addUdevMDDevice(self, info):
return

# try to get the device again now that we've got all the slaves
device = self.getDeviceByName(name, incomplete=flags.allow_degraded_mdraid)
device = self.getDeviceByName(name, incomplete=flags.allow_imperfect_devices)

if device is None:
try:
uuid = udev.device_get_md_uuid(info)
except KeyError:
log.warning("failed to obtain uuid for mdraid device")
else:
device = self.getDeviceByUuid(uuid, incomplete=flags.allow_degraded_mdraid)
device = self.getDeviceByUuid(uuid, incomplete=flags.allow_imperfect_devices)

# if we get here, we found all of the slave devices and
# something must be wrong -- if all of the slaves are in
Expand Down Expand Up @@ -1148,11 +1148,31 @@ def addUdevDevice(self, info):
log.info("scanning %s (%s)...", name, sysfs_path)
device = self.getDeviceByName(name)
if device is None and udev.device_is_md(info):
device = self.getDeviceByName(
udev.device_get_md_name(info),
incomplete=flags.allow_degraded_mdraid)

# If the md name is None, then some udev info is missing. Likely,
# this is because the array is degraded, and mdadm has deactivated
# it. Try to activate it and re-get the udev info.
if flags.allow_imperfect_devices and udev.device_get_md_name(info) is None:
devname = udev.device_get_devname(info)
if devname:
try:
mdraid.mdrun(devname)
except MDRaidError as e:
log.warning("Failed to start possibly degraded md array: %s", e)
else:
udev.settle()
info = udev.get_device(sysfs_path)
else:
log.warning("Failed to get devname for possibly degraded md array.")

md_name = udev.device_get_md_name(info)
if md_name is None:
log.warning("No name for possibly degraded md array.")
else:
device = self.getDeviceByName(md_name, incomplete=flags.allow_imperfect_devices)

if device and not isinstance(device, MDRaidArrayDevice):
# make sure any device we found is an md device
log.warning("Found device %s, but it turns out not be an md array device after all.", device.name)
device = None

if device and device.isDisk and \
Expand Down
3 changes: 3 additions & 0 deletions blivet/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,9 @@ class LoopError(StorageError):
class BTRFSError(StorageError):
pass

class BTRFSValueError(BTRFSError, ValueError):
pass

# DeviceTree
class DeviceTreeError(StorageError):
pass
Expand Down
4 changes: 2 additions & 2 deletions blivet/flags.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def __init__(self):
self.boot_cmdline = {}

self.update_from_boot_cmdline()
self.allow_degraded_mdraid = True
self.allow_imperfect_devices = True

def get_boot_cmdline(self):
buf = open("/proc/cmdline").read().strip()
Expand Down Expand Up @@ -101,7 +101,7 @@ def update_from_anaconda_flags(self, anaconda_flags):
self.gpt = anaconda_flags.gpt

self.multipath_friendly_names = anaconda_flags.mpathFriendlyNames
self.allow_degraded_mdraid = anaconda_flags.rescue_mode
self.allow_imperfect_devices = anaconda_flags.rescue_mode

self.ibft = anaconda_flags.ibft
self.dmraid = anaconda_flags.dmraid
Expand Down
3 changes: 3 additions & 0 deletions blivet/udev.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,9 @@ def device_get_major(info):
def device_get_minor(info):
return int(info["MINOR"])

def device_get_devname(info):
return info.get('DEVNAME')

def device_get_md_level(info):
""" Returns the RAID level of the array of which this device is a member.
Expand Down

0 comments on commit dd56b7d

Please sign in to comment.