Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Missing parameters #59

Merged
merged 10 commits into from
Feb 10, 2021
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ As of now, the role supports managing file systems and mount entries on
Role Variables
--------------

__NOTE__: Beginning with version 1.3.0, unspecified parameters are interpreted
differently for existing and non-existing pools/volumes. For new/non-existent
pools and volumes, any omitted omitted parameters will use the default value as
described in `defaults/main.yml`. For existing pools and volumes, omitted
parameters will inherit whatever setting the pool or volume already has.
This means that to change/override role defaults in an existing pool or volume,
you must explicitly specify the new values/settings in the role variables.

#### `storage_pools`
The `storage_pools` variable is a list of pools to manage. Each pool contains a
nested list of `volume` dicts as described below, as well as the following
Expand Down
2 changes: 2 additions & 0 deletions defaults/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ storage_safe_mode: true # fail instead of implicitly/automatically removing dev
storage_pool_defaults:
state: "present"
type: lvm
disks: []
volumes: []

encryption: false
Expand All @@ -27,6 +28,7 @@ storage_volume_defaults:
state: "present"
type: lvm
size: 0
disks: []

fs_type: "xfs"
fs_label: ""
Expand Down
165 changes: 157 additions & 8 deletions library/blivet.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@
use_partitions = None # create partitions on pool backing device disks?
disklabel_type = None # user-specified disklabel type
safe_mode = None # do not remove any existing devices or formatting
pool_defaults = dict()
volume_defaults = dict()


def find_duplicate_names(dicts):
Expand Down Expand Up @@ -291,16 +293,16 @@ def required_packages(self):
if self.__class__.blivet_device_class is not None:
packages.extend(self.__class__.blivet_device_class._packages)

fmt = get_format(self._volume['fs_type'])
fmt = get_format(self._volume.get('fs_type'))
packages.extend(fmt.packages)
if self._volume['encryption']:
if self._volume.get('encryption'):
packages.extend(get_format('luks').packages)
return packages

@property
def ultimately_present(self):
""" Should this volume be present when we are finished? """
return (self._volume['state'] == 'present' and
return (self._volume.get('state', 'present') == 'present' and
(self._blivet_pool is None or self._blivet_pool.ultimately_present))

def _type_check(self): # pylint: disable=no-self-use
Expand Down Expand Up @@ -345,6 +347,55 @@ def _look_up_device(self):
self._device = None
return # TODO: see if we can create this device w/ the specified name

def _update_from_device(self, param_name):
""" Return True if param_name's value was retrieved from a looked-up device. """
log.debug("Updating volume settings from device: %r", self._device)
encrypted = "luks" in self._device.type or self._device.format.type == "luks"
if encrypted and "luks" in self._device.type:
luks_fmt = self._device.parents[0].format
elif encrypted:
luks_fmt = self._device.format

if param_name == 'size':
self._volume['size'] = int(self._device.size.convert_to())
elif param_name == 'fs_type' and (self._device.format.type or self._device.format.name != get_format(None).name):
self._volume['fs_type'] = self._device.format.type
elif param_name == 'fs_label':
self._volume['fs_label'] = getattr(self._device.format, 'label', "") or ""
elif param_name == 'mount_point':
self._volume['mount_point'] = getattr(self._device.format, 'mountpoint', None)
elif param_name == 'disks':
self._volume['disks'] = [d.name for d in self._device.disks]
elif param_name == 'encryption':
self._volume['encryption'] = encrypted
elif param_name == 'encryption_key_size' and encrypted:
self._volume['encryption_key_size'] = luks_fmt.key_size
elif param_name == 'encryption_key_file' and encrypted:
self._volume['encryption_key_file'] = luks_fmt.key_file
elif param_name == 'encryption_cipher' and encrypted:
self._volume['encryption_cipher'] = luks_fmt.cipher
elif param_name == 'encryption_luks_version' and encrypted:
self._volume['encryption_luks_version'] = luks_fmt.luks_version
else:
return False

return True

def _apply_defaults(self):
global volume_defaults
for name, default in volume_defaults.items():
if name in self._volume:
continue

default = None if default in ('none', 'None', 'null') else default

if self._device:
# Apply values from the device if it already exists.
if not self._update_from_device(name):
self._volume[name] = default
else:
self._volume.setdefault(name, default)

def _get_format(self):
""" Return a blivet.formats.DeviceFormat instance for this volume. """
fmt = get_format(self._volume['fs_type'],
Expand Down Expand Up @@ -427,6 +478,8 @@ def manage(self):
# look up the device
self._look_up_device()

self._apply_defaults()

# schedule destroy if appropriate
if not self.ultimately_present:
self._destroy()
Expand Down Expand Up @@ -617,6 +670,23 @@ def _create_raid_members(self, member_names):

return members

def _update_from_device(self, param_name):
""" Return True if param_name's value was retrieved from a looked-up device. """
if param_name == 'raid_level':
self._volume['raid_level'] = self._device.level.name
elif param_name == 'raid_chunk_size':
self._volume['raid_chunk_size'] = str(self._device.chunk_size)
elif param_name == 'raid_device_count':
self._volume['raid_device_count'] = self._device.member_devices
elif param_name == 'raid_spare_count':
self._volume['raid_spare_count'] = self._device.spares
elif param_name == 'raid_metadata_version':
self._volume['raid_metadata_version'] = self._device.metadata_version
else:
return super(BlivetMDRaidVolume, self)._update_from_device(param_name)

return True

def _create(self):
global safe_mode

Expand Down Expand Up @@ -675,7 +745,8 @@ def _destroy(self):

def _get_blivet_volume(blivet_obj, volume, bpool=None):
""" Return a BlivetVolume instance appropriate for the volume dict. """
volume_type = volume.get('type', bpool._pool['type'] if bpool else None)
global volume_defaults
volume_type = volume.get('type', bpool._pool['type'] if bpool else volume_defaults['type'])
if volume_type not in _BLIVET_VOLUME_TYPES:
raise BlivetAnsibleError("Volume '%s' has unknown type '%s'" % (volume['name'], volume_type))

Expand All @@ -700,15 +771,15 @@ def required_packages(self):
if self.ultimately_present and self.__class__.blivet_device_class is not None:
packages.extend(self.__class__.blivet_device_class._packages)

if self._pool['encryption']:
if self._pool.get('encryption'):
packages.extend(get_format('luks').packages)

return packages

@property
def ultimately_present(self):
""" Should this pool be present when we are finished? """
return self._pool['state'] == 'present'
return self._pool.get('state', 'present') == 'present'

@property
def _is_raid(self):
Expand Down Expand Up @@ -779,6 +850,69 @@ def _look_up_device(self):
self._device = None
return # TODO: see if we can create this device w/ the specified name

# Apply encryption keys as appropriate
if any(d.encrypted for d in self._device.parents):
passphrase = self._pool.get("encryption_passphrase")
key_file = self._pool.get("encryption_key_file")
for member in self._device.parents:
if member.parents[0].format.type == "luks":
if passphrase:
member.parents[0].format.passphrase = passphrase
member.parents[0].original_format.passphrase = passphrase
if key_file:
member.parents[0].format.key_file = key_file
member.parents[0].original_format.key_file = key_file

def _update_from_device(self, param_name):
""" Return True if param_name's value was retrieved from a looked-up device. """
# We wouldn't have the pool device if the member devices weren't unlocked, so we do not
# have to consider the case where the devices are unlocked like we do for volumes.
encrypted = bool(self._device.parents) and all("luks" in d.type for d in self._device.parents)
raid = len(self._device.parents) == 1 and hasattr(self._device.parents[0].raw_device, 'level')
log.debug("BlivetPool._update_from_device: %s", self._device)

if param_name == 'disks':
self._pool['disks'] = [d.name for d in self._device.disks]
elif param_name == 'encryption':
self._pool['encryption'] = encrypted
elif param_name == 'encryption_key_size' and encrypted:
self._pool['encryption_key_size'] = self._device.parents[0].parents[0].format.key_size
elif param_name == 'encryption_key_file' and encrypted:
self._pool['encryption_key_file'] = self._device.parents[0].parents[0].format.key_file
elif param_name == 'encryption_cipher' and encrypted:
self._pool['encryption_cipher'] = self._device.parents[0].parents[0].format.cipher
elif param_name == 'encryption_luks_version' and encrypted:
self._pool['encryption_luks_version'] = self._device.parents[0].parents[0].format.luks_version
elif param_name == 'raid_level' and raid:
self._pool['raid_level'] = self._device.parents[0].raw_device.level.name
elif param_name == 'raid_chunk_size' and raid:
self._pool['raid_chunk_size'] = str(self._device.parents[0].raw_device.chunk_size)
elif param_name == 'raid_device_count' and raid:
self._pool['raid_device_count'] = self._device.parents[0].raw_device.member_devices
elif param_name == 'raid_spare_count' and raid:
self._pool['raid_spare_count'] = self._device.parents[0].raw_device.spares
elif param_name == 'raid_metadata_version' and raid:
self._pool['raid_metadata_version'] = self._device.parents[0].raw_device.metadata_version
else:
return False

return True


def _apply_defaults(self):
global pool_defaults
for name, default in pool_defaults.items():
if name in self._pool:
continue

default = None if default in ('none', 'None', 'null') else default

if self._device:
if not self._update_from_device(name):
self._pool[name] = default
else:
self._pool.setdefault(name, default)

def _create_members(self):
""" Schedule actions as needed to ensure pool member devices exist. """
members = list()
Expand Down Expand Up @@ -826,7 +960,7 @@ def _create_members(self):

def _get_volumes(self):
""" Set up BlivetVolume instances for this pool's volumes. """
for volume in self._pool['volumes']:
for volume in self._pool.get('volumes', []):
bvolume = _get_blivet_volume(self._blivet, volume, self)
self._blivet_volumes.append(bvolume)

Expand All @@ -842,6 +976,7 @@ def manage(self):
# look up the device
self._look_up_disks()
self._look_up_device()
self._apply_defaults()

# schedule destroy if appropriate, including member type change
if not self.ultimately_present:
Expand Down Expand Up @@ -932,6 +1067,10 @@ def _create(self):

def _get_blivet_pool(blivet_obj, pool):
""" Return an appropriate BlivetPool instance for the pool dict. """
if 'type' not in pool:
global pool_defaults
pool['type'] = pool_defaults['type']

if pool['type'] not in _BLIVET_POOL_TYPES:
raise BlivetAnsibleError("Pool '%s' has unknown type '%s'" % (pool['name'], pool['type']))

Expand Down Expand Up @@ -1147,6 +1286,8 @@ def run_module():
packages_only=dict(type='bool', required=False, default=False),
disklabel_type=dict(type='str', required=False, default=None),
safe_mode=dict(type='bool', required=False, default=True),
pool_defaults=dict(type='dict', required=False),
volume_defaults=dict(type='dict', required=False),
use_partitions=dict(type='bool', required=False, default=True),
diskvolume_mkfs_option_map=dict(type='dict', required=False, default={}))

Expand Down Expand Up @@ -1187,6 +1328,14 @@ def run_module():
global diskvolume_mkfs_option_map
diskvolume_mkfs_option_map = module.params['diskvolume_mkfs_option_map']

global pool_defaults
if 'pool_defaults' in module.params:
pool_defaults = module.params['pool_defaults']

global volume_defaults
if 'volume_defaults' in module.params:
volume_defaults = module.params['volume_defaults']

b = Blivet()
b.reset()
fstab = FSTab(b)
Expand Down Expand Up @@ -1215,7 +1364,7 @@ def action_dict(action):
module.fail_json(msg="multiple pools with the same name: {0}".format(",".join(duplicates)),
**result)
for pool in module.params['pools']:
duplicates = find_duplicate_names(pool['volumes'])
duplicates = find_duplicate_names(pool.get('volumes', list()))
if duplicates:
module.fail_json(msg="multiple volumes in pool '{0}' with the "
"same name: {1}".format(pool['name'], ",".join(duplicates)),
Expand Down
2 changes: 1 addition & 1 deletion library/find_unused_disk.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ def get_partitions(disk_path):
sys_name = get_sys_name(disk_path)
partitions = list()
for filename in os.listdir(SYS_CLASS_BLOCK + sys_name):
if re.match(sys_name + 'p?\d+$', filename):
if re.match(sys_name + r'p?\d+$', filename):
partitions.append(filename)

return partitions
Expand Down
Loading