def __exit__(self, exception_type, exception_value, traceback): try: for (key_desc, _) in reversed(self._key_descs): (_, return_code, message) = Manager.Methods.UnsetKey(get_object(TOP_OBJECT), {"key_desc": key_desc}) if return_code != StratisdErrors.OK: raise RuntimeError( "Unsetting the key using stratisd failed with an error: %s" % message) except RuntimeError as rexc: if exception_value is None: raise rexc raise rexc from exception_value
def get_pools(name=None): """ Returns a list of all pools found by GetManagedObjects, or a list of pools with names matching the specified name, if passed. :param name: filter for pool name :type name: str or NoneType :return: list of pool information found :rtype: list of (str * MOPool) """ managed_objects = ObjectManager.Methods.GetManagedObjects( get_object(TOP_OBJECT), {}) return [(op, MOPool(info)) for op, info in pools(props={} if name is None else { "Name": name }).search(managed_objects)]
def setUp(self): """ Start the stratisd daemon with the simulator. """ super().setUp() self._proxy = get_object(TOP_OBJECT) self._blockdevs = ["/dev/one", "/dev/two", "/dev/red", "/dev/blue"] Manager.Methods.CreatePool( self._proxy, { "name": self._POOLNAME, "redundancy": (True, 0), "devices": self._blockdevs, }, ) Manager.Methods.ConfigureSimulator(self._proxy, {"denominator": 8})
def setUp(self): """ Start the stratisd daemon with the simulator. """ self._service = Service() self._service.setUp() time.sleep(1) self._proxy = get_object(TOP_OBJECT) self._errors = StratisdErrorsGen.get_object() Manager.CreatePool( self._proxy, name=self._POOLNAME, redundancy=0, force=False, devices=[d.device_node for d in _device_list(_DEVICES, 1)] ) Manager.ConfigureSimulator(self._proxy, denominator=8)
def create_volumes(namespace): """ Create volumes in a pool. :raises StratisCliRuntimeError: """ proxy = get_object(TOP_OBJECT) pool_object = get_pool(proxy, namespace.pool) volume_list = [(x, '', None) for x in namespace.volume] (_, rc, message) = \ Pool.CreateFilesystems(pool_object, specs=volume_list) if rc != StratisdErrorsGen().get_object().OK: raise StratisCliRuntimeError(rc, message) return
def destroy_pool(namespace): """ Destroy a stratis pool. If no pool exists, the method succeeds. :raises StratisCliRuntimeError: """ proxy = get_object(TOP_OBJECT) (rc, message) = Manager.DestroyPool(proxy, name=namespace.name) stratisd_errors = StratisdErrorsGen.get_object() if rc != stratisd_errors.OK: raise StratisCliRuntimeError(rc, message) return
def get_cache(top, pool): """ Get cache given ``pool``. :param top: the top object :param str pool: the name of the pool :returns: the corresponding object :rtype: ProxyObject :raises StratisCliRuntimeError: if failure to get object """ (cache_object_path, rc, message) = \ Manager.GetCacheObjectPath(top, name=pool) if rc != StratisdErrorsGen.get_object().OK: raise StratisCliRuntimeError(rc, message) return get_object(cache_object_path)
def setUp(self): """ Start the stratisd daemon with the simulator. """ self._service = Service() self._service.setUp() time.sleep(1) self._proxy = get_object(TOP_OBJECT) Manager.Methods.CreatePool( self._proxy, { 'name': self._POOLNAME, 'redundancy': (True, 0), 'force': False, 'devices': [] } ) Manager.Methods.ConfigureSimulator(self._proxy, {'denominator': 8})
def list_pools(namespace): """ List all stratis pools. :raises StratisCliRuntimeError: """ # pylint: disable=unused-argument proxy = get_object(TOP_OBJECT) (result, rc, message) = Manager.ListPools(proxy) stratisd_errors = StratisdErrorsGen.get_object() if rc != stratisd_errors.OK: raise StratisCliRuntimeError(rc, message) for item in result: print(item) return
def testNonExisting(self): """ A proxy object is returned from a non-existant path. """ proxy = get_object('/this/is/not/an/object/path') self.assertIsNotNone(proxy) with self.assertRaises(DPClientInvocationError) as context: ObjectManager.Methods.GetManagedObjects(proxy, {}) cause = context.exception.__cause__ self.assertIsInstance(cause, dbus.exceptions.DBusException) self.assertEqual(cause.get_dbus_name(), 'org.freedesktop.DBus.Error.UnknownMethod') with self.assertRaises(DPClientInvocationError) as context: Manager.Properties.Version.Get(proxy) cause = context.exception.__cause__ self.assertIsInstance(cause, dbus.exceptions.DBusException) self.assertEqual(cause.get_dbus_name(), 'org.freedesktop.DBus.Error.UnknownMethod')
def testNewName(self): """ Test rename to new name. """ filesystem = get_object(self._filesystem_object_path) (result, rc, _) = Filesystem.Methods.SetName(filesystem, {'name': "new"}) self.assertEqual(rc, StratisdErrors.OK) self.assertTrue(result) managed_objects = \ ObjectManager.Methods.GetManagedObjects(self._proxy, {}) (fs_object_path, _) = next( filesystems(managed_objects, {'Name': 'new'})) self.assertEqual(self._filesystem_object_path, fs_object_path) fs_object_path = \ next(filesystems(managed_objects, {'Name': self._fs_name}), None) self.assertIsNone(fs_object_path)
def create_pool(namespace): """ Create a stratis pool. :raises StratisCliRuntimeError: """ stratisd_errors = StratisdErrorsGen.get_object() proxy = get_object(TOP_OBJECT) (_, rc, message) = Manager.CreatePool(proxy, name=namespace.name, redundancy=0, force=namespace.force, devices=namespace.device) if rc != stratisd_errors.OK: raise StratisCliRuntimeError(rc, message) return
def testNewName(self): """ Test rename to new name. """ filesystem = get_object(self._filesystem_object_path) (result, rc, _) = checked_call(Filesystem.SetName(filesystem, name="new"), FilesystemSpec.OUTPUT_SIGS[_FN.SetName]) self.assertEqual(rc, self._errors.OK) self.assertTrue(result) managed_objects = get_managed_objects(self._proxy) (fs_object_path, _) = next(managed_objects.filesystems({'Name': 'new'})) self.assertEqual(self._filesystem_object_path, fs_object_path) fs_object_path = \ next(managed_objects.filesystems({'Name': self._fs_name}), None) self.assertIsNone(fs_object_path)
def _test_prediction(self, pool_name, *, fs_specs=None, overprovision=True): """ Helper function to verify that the prediction matches the reality to an acceptable degree. :param str pool_name: the name of the pool to test :param fs_specs: filesystems to create and test :type fs_specs: list of of str * Range or NoneType :param bool overprovision: True if overprovisioning is allowed """ proxy = get_object(TOP_OBJECT) managed_objects = ObjectManager.Methods.GetManagedObjects(proxy, {}) pool_object_path, pool = next( pools(props={"Name": pool_name}) .require_unique_match(True) .search(managed_objects) ) mopool = MOPool(pool) physical_sizes = _get_block_device_sizes(pool_object_path, managed_objects) pre_prediction = _call_predict_usage( mopool.Encrypted(), physical_sizes, overprovision=overprovision ) self._check_prediction(pre_prediction, mopool) change = _possibly_add_filesystems(pool_object_path, fs_specs=fs_specs) post_prediction = _call_predict_usage( mopool.Encrypted(), physical_sizes, fs_specs=fs_specs, overprovision=overprovision, ) self._check_fs_prediction( pre_prediction, post_prediction, change, overprovision=overprovision )
def get_volume(top, pool, name): """ Get volume given ``name`` and ``pool``. :param top: the top object :param str pool: the object path of the pool :param str name: the name of the volume :returns: the corresponding object :rtype: ProxyObject :raises StratisCliRuntimeError: if failure to get object """ (volume_object_path, rc, message) = Manager.GetFilesystemObjectPath(top, pool_name=pool, filesystem_name=name) if rc != StratisdErrorsGen.get_object().OK: raise StratisCliRuntimeError(rc, message) return get_object(volume_object_path)
def test_props(self): """ Test reading some filesystem properties. """ filesystem = get_object(self._filesystem_object_path) name = Filesystem.Properties.Name.Get(filesystem) self.assertEqual(self._FSNAME, name) uuid = Filesystem.Properties.Uuid.Get(filesystem) # must be a 32 character string self.assertEqual(32, len(uuid)) created = Filesystem.Properties.Created.Get(filesystem) # Should be a UTC rfc3339 string, which should end in Z self.assertTrue(created.endswith("Z")) # I think this is also always true self.assertEqual(len(created), 20) devnode = Filesystem.Properties.Devnode.Get(filesystem) self.assertTrue(isabs(devnode))
def testNewName(self): """ Test rename to new name. """ filesystem = get_object(self._filesystem_object_path) (result, rc, _) = Filesystem.Methods.SetName(filesystem, {"name": "new"}) self.assertEqual(rc, StratisdErrors.OK) self.assertTrue(result) managed_objects = ObjectManager.Methods.GetManagedObjects( self._proxy, {}) (fs_object_path, _) = next(filesystems(props={ "Name": "new" }).search(managed_objects)) self.assertEqual(self._filesystem_object_path, fs_object_path) fs_object_path = next( filesystems(props={ "Name": self._FSNAME }).search(managed_objects), None) self.assertIsNone(fs_object_path)
def test_duplicate_pool_name(self): # pylint: disable=too-many-locals """ Create more than one pool with the same name, then dynamically fix it :return: None """ pool_name = random_string(12) pool_tokens = [] encrypted_indices = [] unencrypted_indices = [] num_pools = 3 keys = [ ("key_desc_1", "key_data_1"), ("key_desc_2", "key_data_2"), ("key_desc_3", "key_data_3"), ] # Create some pools with duplicate names for i in range(num_pools): this_pool = self._lb_mgr.create_devices(i + 1) devnodes = self._lb_mgr.device_files(this_pool) with OptionalKeyServiceContextManager( key_spec=keys) as key_descriptions: key_description = (key_descriptions[random.randint( 0, len(key_descriptions) - 1)] if random.choice([True, False]) else None) create_pool(pool_name, devnodes, key_description=key_description) if key_description is None: unencrypted_indices.append(i) else: encrypted_indices.append(i) pool_tokens.append(this_pool) remove_stratis_dm_devices() self._lb_mgr.unplug(this_pool) wait_for_udev(STRATIS_FS_TYPE, []) all_tokens = [dev for sublist in pool_tokens for dev in sublist] random.shuffle(all_tokens) with OptionalKeyServiceContextManager(key_spec=keys): self._lb_mgr.hotplug(all_tokens) (luks_tokens, non_luks_tokens) = ( [ dev for sublist in (pool_tokens[i] for i in encrypted_indices) for dev in sublist ], [ dev for sublist in (pool_tokens[i] for i in unencrypted_indices) for dev in sublist ], ) wait_for_udev(CRYPTO_LUKS_FS_TYPE, self._lb_mgr.device_files(luks_tokens)) wait_for_udev(STRATIS_FS_TYPE, self._lb_mgr.device_files(non_luks_tokens)) (valid, variant_pool_uuids) = FetchPropertiesR1.Methods.GetProperties( get_object(TOP_OBJECT), {"properties": [LOCKED_POOL_UUIDS_PROP_NAME] })[LOCKED_POOL_UUIDS_PROP_NAME] self.assertTrue(valid) for pool_uuid in variant_pool_uuids: ((option, _), exit_code, _) = ManagerR1.Methods.UnlockPool(get_object(TOP_OBJECT), {"pool_uuid": pool_uuid}) self.assertEqual(exit_code, StratisdErrors.OK) self.assertEqual(option, True) wait_for_udev_count(len(all_tokens)) # The number of pools should never exceed one, since all the pools # previously formed in the test have the same name. self.assertEqual(len(get_pools()), 1) # Dynamically rename all active pools to a randomly chosen name, # then generate synthetic add events for every loopbacked device. # After num_pools - 1 iterations, all pools should have been set up. for _ in range(num_pools - 1): current_pools = get_pools() # Rename all active pools to a randomly selected new name for object_path, _ in current_pools: PoolR1.Methods.SetName(get_object(object_path), {"name": random_string(10)}) self._lb_mgr.generate_synthetic_udev_events( all_tokens, UDEV_ADD_EVENT) settle() self.assertEqual(len(get_pools()), len(current_pools) + 1) self.assertEqual(len(get_pools()), num_pools) remove_stratis_dm_devices()
def testExecution(self): """ An exception is raised if the volume does not exist. """ self.assertIsNotNone(get_cache(get_object(TOP_OBJECT), self._POOLNAME))
def testInvalid(self): """ An invalid path causes an exception to be raised. """ with self.assertRaises(ValueError): get_object('abc')
def _simple_event_test(self, *, key_spec=None): # pylint: disable=too-many-locals """ A simple test of event-based discovery. * Create just one pool. * Stop the daemon. * Unplug the devices. * Start the daemon. * Plug the devices in one by one. The pool should come up when the last device is plugged in. :param key_spec: specification for a key to be inserted into the kernel keyring consisting of the key description and key data :type key_spec: (str, bytes) or NoneType """ num_devices = 3 udev_wait_type = STRATIS_FS_TYPE if key_spec is None else CRYPTO_LUKS_FS_TYPE device_tokens = self._lb_mgr.create_devices(num_devices) devnodes = self._lb_mgr.device_files(device_tokens) key_spec = None if key_spec is None else [key_spec] with OptionalKeyServiceContextManager( key_spec=key_spec) as key_descriptions: key_description = None if key_spec is None else key_descriptions[0] self.assertEqual(len(get_pools()), 0) (_, (pool_object_path, _)) = create_pool(random_string(5), devnodes, key_description=key_description) pool_uuid = PoolR1.Properties.Uuid.Get( get_object(pool_object_path)) self.assertEqual(len(get_pools()), 1) remove_stratis_dm_devices() self._lb_mgr.unplug(device_tokens) wait_for_udev(udev_wait_type, []) with OptionalKeyServiceContextManager(key_spec=key_spec): self.assertEqual(len(get_pools()), 0) indices = list(range(num_devices)) random.shuffle(indices) tokens_up = [] for index in indices[:-1]: tokens_up.append(device_tokens[index]) self._lb_mgr.hotplug([tokens_up[-1]]) wait_for_udev(udev_wait_type, self._lb_mgr.device_files(tokens_up)) self.assertEqual(len(get_pools()), 0) ((option, unlock_uuids), exit_code, _) = ManagerR1.Methods.UnlockPool(get_object(TOP_OBJECT), {"pool_uuid": pool_uuid}) if key_spec is None: self.assertNotEqual(exit_code, StratisdErrors.OK) self.assertEqual(option, False) else: self.assertEqual(exit_code, StratisdErrors.OK) self.assertEqual(option, True) self.assertEqual(len(unlock_uuids), num_devices - 1) self.assertEqual(len(get_pools()), 0) tokens_up.append(device_tokens[indices[-1]]) self._lb_mgr.hotplug([tokens_up[-1]]) wait_for_udev(udev_wait_type, self._lb_mgr.device_files(tokens_up)) ((option, unlock_uuids), exit_code, _) = ManagerR1.Methods.UnlockPool(get_object(TOP_OBJECT), {"pool_uuid": pool_uuid}) if key_spec is None: self.assertNotEqual(exit_code, StratisdErrors.OK) self.assertEqual(option, False) else: self.assertEqual(exit_code, StratisdErrors.OK) self.assertEqual(option, True) self.assertEqual(len(unlock_uuids), 1) wait_for_udev_count(num_devices) self.assertEqual(len(get_pools()), 1) remove_stratis_dm_devices()
def testArguments(self): """ Incorrect arguments should cause a type error. """ with self.assertRaises(TypeError): Manager.Properties.Version.Get(get_object(TOP_OBJECT), {})
def testNonExisting(self): """ A proxy object is returned from a non-existant path. """ self.assertIsNotNone(get_object('/this/is/not/an/object/path'))
def testStratisVersion(self): """ Getting version should just succeed. """ # pylint: disable=no-self-use Manager.Properties.Version(get_object(TOP_OBJECT))
def setUp(self): """ Start stratisd. """ super().setUp() self._proxy = get_object(TOP_OBJECT)
def _simple_initial_discovery_test(self, *, key_spec=None, take_down_dm=False): # pylint: disable=too-many-locals """ A simple test of discovery on start up. * Create just one pool * Stop the daemon * Restart the daemon and verify that the pool is found :param key_spec: specification for a key to be inserted into the kernel keyring consisting of the key description and key data :type key_spec: (str, bytes) or NoneType :param bool take_down_dm: if True take down all Stratis devicemapper devices once stratisd is shut down """ num_devices = 3 device_tokens = self._lb_mgr.create_devices(num_devices) devnodes = self._lb_mgr.device_files(device_tokens) key_spec = None if key_spec is None else [key_spec] with OptionalKeyServiceContextManager( key_spec=key_spec) as key_descriptions: key_description = None if key_spec is None else key_descriptions[0] self.wait_for_pools(0) (_, (pool_object_path, device_object_paths)) = create_pool( random_string(5), devnodes, key_description=key_description) pool_uuid = Pool.Properties.Uuid.Get(get_object(pool_object_path)) self.wait_for_pools(1) wait_for_udev(STRATIS_FS_TYPE, get_devnodes(device_object_paths)) if take_down_dm: remove_stratis_dm_devices() with OptionalKeyServiceContextManager(key_spec=key_spec): ((option, unlock_uuids), exit_code, _) = Manager.Methods.UnlockPool( get_object(TOP_OBJECT), { "pool_uuid": pool_uuid, "unlock_method": str(EncryptionMethod.KEYRING), }, ) if key_spec is None: self.assertNotEqual(exit_code, StratisdErrors.OK) self.assertEqual(option, False) else: self.assertEqual(exit_code, StratisdErrors.OK) self.assertEqual(option, take_down_dm) self.assertEqual(len(unlock_uuids), num_devices if take_down_dm else 0) wait_for_udev_count(num_devices) self.wait_for_pools(1) remove_stratis_dm_devices()
def testNonExistingVolume(self): """ An exception is raised if the volume does not exist. """ self.assertIsNotNone(get_pool(get_object(TOP_OBJECT), self._POOLNAME))
def test_duplicate_pool_name(self): """ Create more than one pool with the same name, then dynamically fix it :return: None """ pool_name = rs(12) pool_tokens = [] num_pools = 3 self._start_service() # Create some pools with duplicate names for i in range(num_pools): this_pool = [self._lb_mgr.create_device() for _ in range(i + 1)] # Ensure newly created block devices are in udev db. self._settle() pool_tokens.append(this_pool) UdevAdd._create_pool(pool_name, self._device_files(this_pool)) devices = self._device_files(this_pool) self._stop_service_remove_dm_tables() UdevAdd._expected_stratis_block_devices(len(this_pool), devices) for d in this_pool: self._lb_mgr.unplug(d) UdevAdd._expected_stratis_block_devices(0, []) self._start_service() # Hot plug activate each pool in sequence and force a duplicate name # error. plugged = 0 devices_plugged = [] for i in range(num_pools): for d in pool_tokens[i]: self._lb_mgr.hotplug(d) plugged += 1 devices_plugged.extend(self._device_files([d])) self._settle() UdevAdd._expected_stratis_block_devices(plugged, devices_plugged) # They all have the same name, so we should only get 1 pool! self.assertEqual(len(UdevAdd._get_pools()), 1) # Lets dynamically rename the active pools and then hot-plug the other # pools so that they all come up. This simulates what an end user # could do to fix this condition until we have CLI support to assist. for _ in range(num_pools - 1): current_pools = UdevAdd._get_pools() existing_pool_count = len(current_pools) # Change the active pool name to be unique for p in current_pools: Pool.Methods.SetName(get_object(p[0]), {"name": rs(10)}) # Generate synthetic add events for add_index in range(num_pools): for d in pool_tokens[add_index]: self._lb_mgr.generate_udev_add_event(d) self._settle() UdevAdd._expected_stratis_block_devices(plugged, devices_plugged) self.assertEqual(len(UdevAdd._get_pools()), existing_pool_count + 1) self.assertEqual(len(UdevAdd._get_pools()), num_pools)
def setUp(self): """ Start the stratisd daemon with the simulator. """ super().setUp() self._proxy = get_object(TOP_OBJECT)
def testExistingVolume(self): """ The volume should be discovered. """ get_volume(get_object(TOP_OBJECT), self._POOLNAME, self._VOLNAME)