From 64594db962e4e4280a15f97a78b7f90db1fbc6ff Mon Sep 17 00:00:00 2001 From: Guillaume Demonet Date: Thu, 19 Nov 2020 11:04:25 +0100 Subject: [PATCH] WIP: sparse loop dev provisioning with systemd --- buildchain/buildchain/salt_tree.py | 3 +- salt/_modules/metalk8s_volumes.py | 68 +------- salt/_states/metalk8s_volumes.py | 37 ----- .../minion/files/minion-99-metalk8s.conf.j2 | 5 - .../files/metalk8s-sparse-volume-cleanup | 128 +++++++++++++++ .../files/metalk8s-sparse-volume@.service | 14 ++ salt/metalk8s/volumes/prepared/init.sls | 54 +++++-- salt/metalk8s/volumes/provisioned/init.sls | 8 - salt/metalk8s/volumes/unprepared/init.sls | 24 ++- .../modules/files/test_metalk8s_volumes.yaml | 153 +----------------- .../unit/modules/test_metalk8s_volumes.py | 117 +------------- 11 files changed, 205 insertions(+), 406 deletions(-) create mode 100755 salt/metalk8s/volumes/prepared/files/metalk8s-sparse-volume-cleanup create mode 100644 salt/metalk8s/volumes/prepared/files/metalk8s-sparse-volume@.service delete mode 100644 salt/metalk8s/volumes/provisioned/init.sls diff --git a/buildchain/buildchain/salt_tree.py b/buildchain/buildchain/salt_tree.py index 59fa574101..3749fc2058 100644 --- a/buildchain/buildchain/salt_tree.py +++ b/buildchain/buildchain/salt_tree.py @@ -651,7 +651,8 @@ def _get_parts(self) -> Iterator[str]: Path('salt/metalk8s/volumes/init.sls'), Path('salt/metalk8s/volumes/prepared/init.sls'), Path('salt/metalk8s/volumes/prepared/installed.sls'), - Path('salt/metalk8s/volumes/provisioned/init.sls'), + Path('salt/metalk8s/volumes/prepared/files/metalk8s-sparse-volume@.service'), + Path('salt/metalk8s/volumes/prepared/files/metalk8s-volume-clean-up'), Path('salt/metalk8s/volumes/unprepared/init.sls'), Path('salt/_auth/kubernetes_rbac.py'), diff --git a/salt/_modules/metalk8s_volumes.py b/salt/_modules/metalk8s_volumes.py index cf2c85eee1..34e3ed32cb 100644 --- a/salt/_modules/metalk8s_volumes.py +++ b/salt/_modules/metalk8s_volumes.py @@ -62,39 +62,6 @@ def create(name): _get_volume(name).create() -def is_provisioned(name): - """Check if the backing storage device is provisioned for the given volume. - - Args: - name (str): volume name - - Returns: - bool: True if the backing storage device is provisioned, otherwise False - - CLI Example: - - .. code-block:: bash - - salt '' metalk8s_volumes.is_provisioned example-volume - """ - return _get_volume(name).is_provisioned - - -def provision(name): - """Provision the backing storage device of the given volume. - - Args: - name (str): volume name - - CLI Example: - - .. code-block:: bash - - salt '' metalk8s_volumes.provision example-volume - """ - _get_volume(name).provision() - - def is_prepared(name): """Check if the given volume is prepared. @@ -233,16 +200,6 @@ def create(self): # pragma: no cover """Create the backing storage device.""" return - @abc.abstractproperty - def is_provisioned(self): # pragma: no cover - """Check if the backing storage device is provisioned.""" - return - - @abc.abstractmethod - def provision(self): # pragma: no cover - """Provision the backing storage device.""" - return - @abc.abstractproperty def is_cleaned_up(self): # pragma: no cover """Check if the backing storage device is cleaned up.""" @@ -347,32 +304,16 @@ def create(self): self.path, exn )) - @property - def is_provisioned(self): - # A sparse loop device is provisioned when a sparse file is associated - # to a loop device. - command = ' '.join(['losetup', '--associated', self.path]) - pattern = r'\({}\)'.format(re.escape(self.path)) - result = _run_cmd(command) - return re.search(pattern, result['stdout']) is not None - - def provision(self): - command = ' '.join(list(self.PROVISIONING_COMMAND) + [self.path]) - return _run_cmd(command) - def prepare(self, force=False): # We format a "normal" file, not a block device: we need force=True. super(SparseLoopDevice, self).prepare(force=True) @property def is_cleaned_up(self): - return not (self.is_provisioned or self.exists) + return not self.exists def clean_up(self): - LOOP_CLR_FD = 0x4C01 # From /usr/include/linux/loop.h try: - with _open_fd(self.persistent_path, os.O_RDONLY) as fd: - fcntl.ioctl(fd, LOOP_CLR_FD, 0) os.remove(self.path) except OSError as exn: if exn.errno != errno.ENOENT: @@ -420,13 +361,6 @@ def create(self): self.path )) - @property - def is_provisioned(self): - return True # Nothing to do so it's always True. - - def provision(self): - return # Nothing to do - @property def path(self): return self.get('spec.rawBlockDevice.devicePath') diff --git a/salt/_states/metalk8s_volumes.py b/salt/_states/metalk8s_volumes.py index 82904c6404..d739f7e522 100644 --- a/salt/_states/metalk8s_volumes.py +++ b/salt/_states/metalk8s_volumes.py @@ -51,43 +51,6 @@ def present(name): return ret -def provisioned(name): - """Provision the given volume. - - Args: - name (str): Volume name - - Returns: - dict: state return value - """ - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} - # Idempotence. - if __salt__['metalk8s_volumes.is_provisioned'](name): - ret['result'] = True - ret['comment'] = 'Storage for volume {} already provisioned.'\ - .format(name) - return ret - # Dry-run. - if __opts__['test']: - ret['changes'][name] = 'Provisioned' - ret['result'] = None - ret['comment'] = 'Storage for volume {} is going to be provisioned.'\ - .format(name) - return ret - # Let's go for real. - try: - __salt__['metalk8s_volumes.provision'](name) - except CommandExecutionError as exn: - ret['result'] = False - ret['comment'] = 'Storage provisioning for volume {} failed: {}.'\ - .format(name, exn) - else: - ret['changes'][name] = 'Provisioned' - ret['result'] = True - ret['comment'] = 'Storage provisioned for volume {}.'.format(name) - return ret - - def prepared(name): """Prepare the given volume. diff --git a/salt/metalk8s/salt/minion/files/minion-99-metalk8s.conf.j2 b/salt/metalk8s/salt/minion/files/minion-99-metalk8s.conf.j2 index 94330b90ff..784358898b 100644 --- a/salt/metalk8s/salt/minion/files/minion-99-metalk8s.conf.j2 +++ b/salt/metalk8s/salt/minion/files/minion-99-metalk8s.conf.j2 @@ -10,8 +10,3 @@ use_superseded: log_level_logfile: {{ 'debug' if debug else 'info' }} saltenv: {{ saltenv }} - -# Prepare volume at startup (required for loop devices persistency). -startup_states: sls -sls_list: - - /metalk8s/volumes/provisioned diff --git a/salt/metalk8s/volumes/prepared/files/metalk8s-sparse-volume-cleanup b/salt/metalk8s/volumes/prepared/files/metalk8s-sparse-volume-cleanup new file mode 100755 index 0000000000..99a6b66ac5 --- /dev/null +++ b/salt/metalk8s/volumes/prepared/files/metalk8s-sparse-volume-cleanup @@ -0,0 +1,128 @@ +#!/usr/bin/env python2 + +from __future__ import print_function +import argparse +import errno +import os +import stat +import sys + + +SPARSE_FILES_DIR = '/var/lib/metalk8s/storage/sparse/' +LOOP_MAJOR = 7 + + +def parse_args(args=None): + parser = argparse.ArgumentParser( + prog='metalk8s-sparse-volume-cleanup', + help='Cleanup MetalK8s sparse loop volumes', + ) + + parser.add_argument( + '-i', '--uid', + required=True, + help='UID of the MetalK8s Volume object', + dest='volume_id', + ) + + return parser.parse_args(args=args) + + +def check_sparse_file(volume_id): + """Check if the sparse file exists for this volume ID.""" + sparse_file_path = SPARSE_FILES_DIR + volume_id + if not os.path.isfile(sparse_file_path): + die( + "Sparse file {} does not exist for volume '{}'".format( + sparse_file_path, volume_id + ), + exit_code=errno.ENOENT + ) + + +def cleanup(volume_id): + device_path = find_device(volume_id) + + print("Detaching loop device '{}' for volume '{}'".format( + device_path, volume_id + )) + + fd = os.open(device_path, os.O_RDONLY) + try: + fcntl.ioctl(fd, LOOP_CLR_FD, 0) + except IOError as exn: + if exn.errno != errno.ENXIO: + die("Unexpected error when trying to free device {}: {}".format( + device_path, str(exn) + )) + print("Device already freed", file=sys.stderr) + finally: + fd.close() + + print("Loop device for volume '{}' was successfully detached".format( + volume_id + )) + + +def main(): + args = parse_args() + + check_sparse_file(args.volume_id) + cleanup(args.volume_id) + + +# Helpers {{{ +def die(message, exit_code=1): + print(message, file=sys.stderr) + sys.exit(exit_code) + + +def find_device(volume_id): + """Find the device provisioned for this volume ID. + + SparseLoopDevice volumes are either: + - formatted, with the volume ID set in the filesystem annotations, + hence discoverable under /dev/disk/by-uuid/ + - raw, in which case the device is partitioned and the first partition + is labeled with the volume ID, hence discoverable under + /dev/disk/by-partuuid/ + """ + path_by_uuid = "/dev/disk/by-uuid/{}".format(volume_id) + if os.path.exists(path_by_uuid): + device_path = os.path.realpath(path_by_uuid) + else: + path_by_partuuid = "/dev/disk/by-partuuid/{}".format(volume_id) + if not os.path.exists(path_by_partuuid): + die( + "Device for volume '{}' was not found".format(volume_id), + exit_code=errno.ENOENT, + ) + + partition_name = os.path.basename(os.path.realpath(path_by_partuuid)) + device_name = os.path.basename( + os.path.realpath("/sys/class/block/{}/..".format(partition_name)) + ) + device_path = '/dev/{}'.format(match.group(0)) + + if not is_loop_device(device_path): + die("{} (found for volume '{}') is not a loop device".format( + device_path, volume_id + )) + + return device_path + + +def is_loop_device(path): + device_stat = os.stat(path) + return stat.S_ISBLK(device_stat.st_mode) \ + and (_major(device_stat.st_rdev) == LOOP_MAJOR) + + +def _major(value): + return (value >> 8) & 0xff + + +# }}} + +if __name__ == '__main__': + main() diff --git a/salt/metalk8s/volumes/prepared/files/metalk8s-sparse-volume@.service b/salt/metalk8s/volumes/prepared/files/metalk8s-sparse-volume@.service new file mode 100644 index 0000000000..1eab68b7c5 --- /dev/null +++ b/salt/metalk8s/volumes/prepared/files/metalk8s-sparse-volume@.service @@ -0,0 +1,14 @@ +[Unit] +Description=Setup MetalK8s sparse loop volume %I +RequiresMountsFor=/var/lib/metalk8s/storage/sparse +AssertFileNotEmpty=/var/lib/metalk8s/storage/sparse/%i + +[Service] +Type=oneshot +ExecStart=/bin/flock --exclusive --wait 10 /var/lock/metalk8s-sparse-volume.lock \ + -c "/sbin/losetup --find --partscan /var/lib/metalk8s/storage/sparse/%i" +ExecStop=/usr/local/bin/libexec/metalk8s-sparse-volume-cleanup --uid "%i" +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target diff --git a/salt/metalk8s/volumes/prepared/init.sls b/salt/metalk8s/volumes/prepared/init.sls index ca9d2300a0..0a7d6634b3 100644 --- a/salt/metalk8s/volumes/prepared/init.sls +++ b/salt/metalk8s/volumes/prepared/init.sls @@ -3,22 +3,23 @@ include: {%- set volumes_to_create = [] %} {%- set all_volumes = pillar.metalk8s.volumes %} -{%- set target_volume = pillar.get('volume') %} +{%- set target_volume_name = pillar.get('volume') %} {#- If a volume is given we only take this one, otherwise we take them all. #} -{%- if target_volume is not none %} - {%- if target_volume in all_volumes.keys() %} +{%- if target_volume_name is not none %} + {%- set target_volume = all_volumes.get(target_volume_name) %} + {%- if target_volume is not none %} {%- do volumes_to_create.append(target_volume) %} {%- else %} -Volume {{ target_volume }} not found in pillar: +Volume {{ target_volume_name }} not found in pillar: test.configurable_test_state: - - name: {{ target_volume }} + - name: {{ target_volume_name }} - changes: False - result: False - - comment: Volume {{ target_volume }} not found in pillar + - comment: Volume {{ target_volume_name }} not found in pillar {%- endif %} {%- else %} - {%- do volumes_to_create.extend(all_volumes.keys()|list) %} + {%- do volumes_to_create.extend(all_volumes.values()|list) %} {%- endif %} Create the sparse file directory: @@ -26,28 +27,49 @@ Create the sparse file directory: - name: /var/lib/metalk8s/storage/sparse/ - makedirs: True +Set up systemd template unit for sparse loop device provisioning: + file.managed: + - name: /etc/systemd/system/metalk8s-sparse-volume@.service + - source: salt://{{ slspath }}/files/metalk8s-sparse-volume@.service + - user: root + - group : root + - mode: 644 + +Install clean-up script: + file.managed: + - name: /usr/local/libexec/metalk8s-sparse-volume-cleanup + - source: salt://{{ slspath }}/files/metalk8s-sparse-volume-cleanup + - user: root + - group : root + - mode: 755 + {%- for volume in volumes_to_create %} + {%- set volume_name = volume.metadata.name %} -Create backing storage for {{ volume }}: +Create backing storage for {{ volume_name }}: metalk8s_volumes.present: - - name: {{ volume }} + - name: {{ volume_name }} - require: - file: Create the sparse file directory -Prepare backing storage for {{ volume }}: +Prepare backing storage for {{ volume_name }}: metalk8s_volumes.prepared: - - name: {{ volume }} + - name: {{ volume_name }} - require: - metalk8s_package_manager: Install e2fsprogs - metalk8s_package_manager: Install xfsprogs - metalk8s_package_manager: Install gdisk - - metalk8s_volumes: Create backing storage for {{ volume }} + - metalk8s_volumes: Create backing storage for {{ volume_name }} + {%- if 'sparseLoopDevice' in volume.spec %} -Provision backing storage for {{ volume }}: - metalk8s_volumes.provisioned: - - name: {{ volume }} +Provision backing storage for {{ volume_name }}: + service.running: + - name: metalk8s-sparse-volume@{{ volume.metadata.uid }} + - enable: true - require: - - metalk8s_volumes: Prepare backing storage for {{ volume }} + - metalk8s_volumes: Prepare backing storage for {{ volume_name }} + - file: Set up systemd template unit for sparse loop device provisioning + {%- endif %} - require_in: - module: Update pillar after volume provisioning diff --git a/salt/metalk8s/volumes/provisioned/init.sls b/salt/metalk8s/volumes/provisioned/init.sls deleted file mode 100644 index fbe1ebc7f0..0000000000 --- a/salt/metalk8s/volumes/provisioned/init.sls +++ /dev/null @@ -1,8 +0,0 @@ -{%- for volume in pillar.metalk8s.volumes.keys() %} - -Provision backing storage for {{ volume }}: - metalk8s_volumes.provisioned: - - name: {{ volume }} - - parallel: True - -{%- endfor %} diff --git a/salt/metalk8s/volumes/unprepared/init.sls b/salt/metalk8s/volumes/unprepared/init.sls index 8229446e14..91275e1d61 100644 --- a/salt/metalk8s/volumes/unprepared/init.sls +++ b/salt/metalk8s/volumes/unprepared/init.sls @@ -1,19 +1,27 @@ {%- set volumes = pillar.metalk8s.volumes %} -{%- set volume = pillar.get('volume', '') %} +{%- set volume_name = pillar.get('volume', '') %} +{%- set to_remove = volumes.get(volume_name) %} +{%- if to_remove is not none %} + {%- if 'sparseLoopDevice' in to_remove.spec %} +Disable systemd provisioning of loop device for {{ volume_name }}: + service.disabled: + - name: metalk8s-sparse-volume@{{ to_remove.metadata.uid }} + - require_in: + - metalk8s_volumes: Clean up backing storage for {{ volume_name }} -{%- if volume in volumes.keys() %} -Clean up backing storage for {{ volume }}: + {%- endif %} +Clean up backing storage for {{ volume_name }}: metalk8s_volumes.removed: - - name: {{ volume }} + - name: {{ volume_name }} {%- else %} -{%- do salt.log.warning('Volume ' ~ volume ~ ' not found in pillar') -%} +{%- do salt.log.warning('Volume ' ~ volume_name ~ ' not found in pillar') -%} -Volume {{ volume }} not found in pillar: +Volume {{ volume_name }} not found in pillar: test.configurable_test_state: - - name: {{ volume }} + - name: {{ volume_name }} - changes: False - result: True - - comment: Volume {{ volume }} not found in pillar + - comment: Volume {{ volume_name }} not found in pillar {%- endif %} diff --git a/salt/tests/unit/modules/files/test_metalk8s_volumes.yaml b/salt/tests/unit/modules/files/test_metalk8s_volumes.yaml index e3b5b56826..fb978d1ee6 100644 --- a/salt/tests/unit/modules/files/test_metalk8s_volumes.yaml +++ b/salt/tests/unit/modules/files/test_metalk8s_volumes.yaml @@ -465,127 +465,6 @@ create: pillar_volumes: *volumes_details raise_msg: unsupported Volume type for Volume my-invalid-type-volume -is_provisioned: - ## SPARSE volume - # sparse file associated with a loop device - - name: my-sparse-volume - pillar_volumes: *volumes_details - losetup_output: | - /dev/loop3: [64769]:41159 (/var/lib/metalk8s/storage/sparse/f1d78810-3787-4ca4-b712-50a269e42560) - result: True - - # sparse file not associated with any device - - name: my-sparse-volume - pillar_volumes: *volumes_details - losetup_output: | - result: False - - # error when checking sparse file association - - name: my-sparse-volume - pillar_volumes: *volumes_details - raises: True - result: "error while trying to run `losetup --associated /var/lib/metalk8s/storage/sparse/f1d78810-3787-4ca4-b712-50a269e42560`: An error has occurred" - - ## SPARSE block volume - # sparse file associated with a loop device - - name: my-sparse-block-volume - pillar_volumes: *volumes_details - losetup_output: | - /dev/loop4: [64769]:41159 (/var/lib/metalk8s/storage/sparse/8474cda7-0dbe-40fc-9842-3cb0404a725a) - result: True - - # sparse file not associated with any device - - name: my-sparse-block-volume - pillar_volumes: *volumes_details - losetup_output: | - result: False - - # error when checking sparse file association - - name: my-sparse-block-volume - pillar_volumes: *volumes_details - raises: True - result: "error while trying to run `losetup --associated /var/lib/metalk8s/storage/sparse/8474cda7-0dbe-40fc-9842-3cb0404a725a`: An error has occurred" - - ## RAW BLOCK DEVICE volume - # raw block device always provisioned (nothing to provision) - - name: my-raw-block-device-volume - pillar_volumes: *volumes_details - result: True - - ## RAW BLOCK DEVICE block disk volume - # raw block device always provisioned (nothing to provision) - - name: my-raw-block-device-block-disk-volume - pillar_volumes: *volumes_details - result: True - - ## RAW BLOCK DEVICE block partition volume - # raw block device always provisioned (nothing to provision) - - name: my-raw-block-device-block-partition-volume - pillar_volumes: *volumes_details - result: True - - ## RAW BLOCK DEVICE block lvm volume - # raw block device always provisioned (nothing to provision) - - name: my-raw-block-device-block-lvm-volume - pillar_volumes: *volumes_details - result: True - - ## Invalid volumes - # specified volume is not in the pillar - - name: unknown-volume - pillar_volumes: *volumes_details - raises: True - result: volume unknown-volume not found in pillar - -provision: - ## SPARSE volume - # provision a sparse file with a loop device - - name: my-sparse-volume - pillar_volumes: *volumes_details - losetup_output: | - - # error when searching a loop device for the sparse file - - name: my-sparse-volume - pillar_volumes: *volumes_details - raise_msg: "error while trying to run `losetup --find /var/lib/metalk8s/storage/sparse/f1d78810-3787-4ca4-b712-50a269e42560`: An error has occurred" - - ## SPARSE block volume - # provision a sparse file with a loop device - - name: my-sparse-block-volume - pillar_volumes: *volumes_details - losetup_output: | - - # error when searching a loop device for the sparse file - - name: my-sparse-block-volume - pillar_volumes: *volumes_details - raise_msg: "error while trying to run `losetup --find --partscan /var/lib/metalk8s/storage/sparse/8474cda7-0dbe-40fc-9842-3cb0404a725a`: An error has occurred" - - ## RAW BLOCK DEVICE volume - # nothing to provision for raw block device - - name: my-raw-block-device-volume - pillar_volumes: *volumes_details - - ## RAW BLOCK DEVICE block disk volume - # nothing to provision for raw block device - - name: my-raw-block-device-block-disk-volume - pillar_volumes: *volumes_details - - ## RAW BLOCK DEVICE block partition volume - # nothing to provision for raw block device - - name: my-raw-block-device-block-partition-volume - pillar_volumes: *volumes_details - - ## RAW BLOCK DEVICE block lvm volume - # nothing to provision for raw block device - - name: my-raw-block-device-block-lvm-volume - pillar_volumes: *volumes_details - - ## Invalid volumes - # specified volume is not in the pillar - - name: unknown-volume - pillar_volumes: *volumes_details - raise_msg: volume unknown-volume not found in pillar - is_prepared: ## SPARSE volume # sparse volume already prepared - right UUID @@ -789,17 +668,9 @@ is_cleaned_up: pillar_volumes: *volumes_details result: True - # sparse file exists and associated with a loop device - - name: my-sparse-volume - pillar_volumes: *volumes_details - is_provisioned: True - exists: True - result: False - - # sparse file exists but not associated with any device + # sparse file exists - name: my-sparse-volume pillar_volumes: *volumes_details - is_provisioned: False exists: True result: False @@ -809,17 +680,9 @@ is_cleaned_up: pillar_volumes: *volumes_details result: True - # sparse file exists and associated with a loop device - - name: my-sparse-block-volume - pillar_volumes: *volumes_details - is_provisioned: True - exists: True - result: False - - # sparse file exists but not associated with any device + # sparse file exists - name: my-sparse-block-volume pillar_volumes: *volumes_details - is_provisioned: False exists: True result: False @@ -871,12 +734,6 @@ clean_up: remove_error: "An error has occurred during remove" raise_msg: "An error has occurred during remove" - # error when running ioctl on the sparse file - - name: my-sparse-volume - pillar_volumes: *volumes_details - ioctl_error: "An error has occurred during ioctl" - raise_msg: "An error has occurred during ioctl" - ## SPARSE block volume # clean up a sparse file - name: my-sparse-block-volume @@ -893,12 +750,6 @@ clean_up: remove_error: "An error has occurred during remove" raise_msg: "An error has occurred during remove" - # error when running ioctl on the sparse file - - name: my-sparse-block-volume - pillar_volumes: *volumes_details - ioctl_error: "An error has occurred during ioctl" - raise_msg: "An error has occurred during ioctl" - ## RAW BLOCK DEVICE volume # nothing to clean up for raw block device - name: my-raw-block-device-volume diff --git a/salt/tests/unit/modules/test_metalk8s_volumes.py b/salt/tests/unit/modules/test_metalk8s_volumes.py index 16a6027e9e..21f1f09374 100644 --- a/salt/tests/unit/modules/test_metalk8s_volumes.py +++ b/salt/tests/unit/modules/test_metalk8s_volumes.py @@ -112,100 +112,6 @@ def test_create(self, name, raise_msg=None, pillar_volumes=None, # This function does not return anything metalk8s_volumes.create(name) - @utils.parameterized_from_cases(YAML_TESTS_CASES["is_provisioned"]) - def test_is_provisioned(self, name, result, raises=False, - pillar_volumes=None, losetup_output=None): - """ - Tests the return of `is_provisioned` function - """ - pillar_dict = { - 'metalk8s': { - 'volumes': pillar_volumes or {} - } - } - - if losetup_output is None: - losetup_cmd_kwargs = { - 'retcode': 1, - 'stderr': 'An error has occurred' - } - else: - losetup_cmd_kwargs = { - 'stdout': losetup_output - } - - salt_dict = { - 'cmd.run_all': MagicMock( - return_value=utils.cmd_output(**losetup_cmd_kwargs) - ) - } - - # Glob is used only for lvm, let simulate that we have 2 lvm volume - glob_mock = MagicMock(return_value=["/dev/dm-1", "/dev/dm-2"]) - - with patch.dict(metalk8s_volumes.__pillar__, pillar_dict), \ - patch.dict(metalk8s_volumes.__salt__, salt_dict), \ - patch("metalk8s_volumes.device_name", device_name_mock), \ - patch("glob.glob", glob_mock): - if raises: - self.assertRaisesRegexp( - Exception, - result, - metalk8s_volumes.is_provisioned, - name - ) - else: - self.assertEqual( - metalk8s_volumes.is_provisioned(name), - result - ) - - @utils.parameterized_from_cases(YAML_TESTS_CASES["provision"]) - def test_provision(self, name, raise_msg=False, - pillar_volumes=None, losetup_output=None): - """ - Tests the return of `provision` function - """ - pillar_dict = { - 'metalk8s': { - 'volumes': pillar_volumes or {} - } - } - - if losetup_output is None: - losetup_cmd_kwargs = { - 'retcode': 1, - 'stderr': 'An error has occurred' - } - else: - losetup_cmd_kwargs = { - 'stdout': losetup_output - } - - salt_dict = { - 'cmd.run_all': MagicMock( - return_value=utils.cmd_output(**losetup_cmd_kwargs) - ) - } - - # Glob is used only for lvm, let simulate that we have 2 lvm volume - glob_mock = MagicMock(return_value=["/dev/dm-1", "/dev/dm-2"]) - - with patch.dict(metalk8s_volumes.__pillar__, pillar_dict), \ - patch.dict(metalk8s_volumes.__salt__, salt_dict), \ - patch("metalk8s_volumes.device_name", device_name_mock), \ - patch("glob.glob", glob_mock): - if raise_msg: - self.assertRaisesRegexp( - Exception, - raise_msg, - metalk8s_volumes.provision, - name - ) - else: - # This function does not return anything - metalk8s_volumes.provision(name) - @utils.parameterized_from_cases(YAML_TESTS_CASES["is_prepared"]) def test_is_prepared(self, name, result, raises=False, uuid_return=None, device_name_return=True, @@ -318,8 +224,7 @@ def test_prepare(self, name, raise_msg=False, @utils.parameterized_from_cases(YAML_TESTS_CASES["is_cleaned_up"]) def test_is_cleaned_up(self, name, result, raises=False, - is_provisioned=False, exists=False, - pillar_volumes=None): + exists=False, pillar_volumes=None): """ Tests the return of `is_cleaned_up` function """ @@ -334,11 +239,7 @@ def test_is_cleaned_up(self, name, result, raises=False, with patch.dict(metalk8s_volumes.__pillar__, pillar_dict), \ patch.object(metalk8s_volumes.SparseLoopDevice, - 'is_provisioned', - is_provisioned), \ - patch.object(metalk8s_volumes.SparseLoopDevice, - 'exists', - exists), \ + 'exists', exists), \ patch("metalk8s_volumes.device_name", device_name_mock), \ patch("glob.glob", glob_mock): if raises: @@ -356,7 +257,7 @@ def test_is_cleaned_up(self, name, result, raises=False, @utils.parameterized_from_cases(YAML_TESTS_CASES["clean_up"]) def test_clean_up(self, name, raise_msg=False, pillar_volumes=None, - remove_error=None, ioctl_error=None): + remove_error=None): """ Tests the return of `clean_up` function """ @@ -372,19 +273,9 @@ def test_clean_up(self, name, raise_msg=False, pillar_volumes=None, remove_error = [remove_error] remove_mock.side_effect = OSError(*remove_error) - ioctl_mock = MagicMock() - if ioctl_error: - ioctl_mock.side_effect = IOError(ioctl_error) - - # Glob is used only for lvm, let simulate that we have 2 lvm volume - glob_mock = MagicMock(return_value=["/dev/dm-1", "/dev/dm-2"]) - with patch.dict(metalk8s_volumes.__pillar__, pillar_dict), \ patch("metalk8s_volumes.device_name", device_name_mock), \ - patch("glob.glob", glob_mock), \ - patch("os.open", MagicMock()), \ - patch("os.remove", remove_mock), \ - patch("fcntl.ioctl", ioctl_mock): + patch("os.remove", remove_mock): if raise_msg: self.assertRaisesRegexp( Exception,