From 1daade76b2318c85089f840ced3fe64147f66356 Mon Sep 17 00:00:00 2001 From: Valeriy Ponomaryov <vponomaryov@mirantis.com> Date: Mon, 21 Nov 2016 18:20:45 +0200 Subject: [PATCH] [Dummy driver] Add possibility to set delays for driver methods Dummy driver was designed to test manila main functionality that is storage-agnostic. So, add possibility to set delays for each dummy share driver method, to be able to simulate storage more correctly by having time delays similar to real storage-drivers. Two new config options were added: - dummy_driver_default_driver_method_delay (float) - dummy_driver_driver_methods_delays (dict of string-keys and int/float values) Configuration example: [DEFAULT] enabled_share_backends = fake_backend_name [fake_backend_name] ... dummy_driver_default_driver_method_delay = 2.0 dummy_driver_driver_methods_delays = create_share:5.15,delete_share:1 ... Change-Id: I36598f74762f3631c6c8502148ea9989ad544b5a --- manila/tests/share/drivers/dummy.py | 137 +++++++++++++++++++++++++++- 1 file changed, 136 insertions(+), 1 deletion(-) diff --git a/manila/tests/share/drivers/dummy.py b/manila/tests/share/drivers/dummy.py index aad6d01037..ab8e53aa2d 100644 --- a/manila/tests/share/drivers/dummy.py +++ b/manila/tests/share/drivers/dummy.py @@ -29,6 +29,10 @@ This driver simulates support of: """ +import functools +import time + +from oslo_config import cfg from oslo_log import log from manila.common import constants @@ -40,17 +44,111 @@ from manila.share import utils as share_utils LOG = log.getLogger(__name__) +dummy_opts = [ + cfg.FloatOpt( + "dummy_driver_default_driver_method_delay", + help="Defines default time delay in seconds for each dummy driver " + "method. To redefine some specific method delay use other " + "'dummy_driver_driver_methods_delays' config opt. Optional.", + default=2.0, + min=0, + ), + cfg.DictOpt( + "dummy_driver_driver_methods_delays", + help="It is dictionary-like config option, that consists of " + "driver method names as keys and integer/float values that are " + "time delay in seconds. Optional.", + default={ + "ensure_share": "1.05", + "create_share": "3.98", + "get_pool": "0.5", + "do_setup": "0.05", + + "_get_pools_info": "0.1", + "_update_share_stats": "0.3", + + "create_replica": "3.99", + "delete_replica": "2.98", + "promote_replica": "0.75", + "update_replica_state": "0.85", + "create_replicated_snapshot": "4.15", + "delete_replicated_snapshot": "3.16", + "update_replicated_snapshot": "1.17", + + "migration_start": 1.01, + "migration_continue": 1.02, # it will be called 4 times + "migration_complete": 1.03, + "migration_cancel": 1.04, + "migration_get_progress": 1.05, + "migration_check_compatibility": 0.05, + }, + ), +] + +CONF = cfg.CONF + + +def slow_me_down(f): + + @functools.wraps(f) + def wrapped_func(self, *args, **kwargs): + sleep_time = self.configuration.safe_get( + "dummy_driver_driver_methods_delays").get( + f.__name__, + self.configuration.safe_get( + "dummy_driver_default_driver_method_delay") + ) + time.sleep(float(sleep_time)) + return f(self, *args, **kwargs) + + return wrapped_func + + class DummyDriver(driver.ShareDriver): """Dummy share driver that implements all share driver interfaces.""" def __init__(self, *args, **kwargs): """Do initialization.""" - super(self.__class__, self).__init__([False, True], *args, **kwargs) + super(self.__class__, self).__init__( + [False, True], *args, config_opts=[dummy_opts], **kwargs) + self._verify_configuration() self.private_storage = kwargs.get('private_storage') self.backend_name = self.configuration.safe_get( "share_backend_name") or "DummyDriver" self.migration_progress = {} + def _verify_configuration(self): + allowed_driver_methods = [m for m in dir(self) if m[0] != '_'] + allowed_driver_methods.extend([ + "_setup_server", + "_teardown_server", + "_get_pools_info", + "_update_share_stats", + ]) + disallowed_driver_methods = ( + "get_admin_network_allocations_number", + "get_network_allocations_number", + "get_share_server_pools", + ) + for k, v in self.configuration.safe_get( + "dummy_driver_driver_methods_delays").items(): + if k not in allowed_driver_methods: + raise exception.BadConfigurationException(reason=( + "Dummy driver does not have '%s' method." % k + )) + elif k in disallowed_driver_methods: + raise exception.BadConfigurationException(reason=( + "Method '%s' does not support delaying." % k + )) + try: + float(v) + except (TypeError, ValueError): + raise exception.BadConfigurationException(reason=( + "Wrong value (%(v)s) for '%(k)s' dummy driver method time " + "delay is set in 'dummy_driver_driver_methods_delays' " + "config option." % {"k": k, "v": v} + )) + def _get_share_name(self, share): return "share_%(s_id)s_%(si_id)s" % { "s_id": share["share_id"].replace("-", "_"), @@ -97,10 +195,12 @@ class DummyDriver(driver.ShareDriver): return self._generate_export_locations( mountpoint, share_server=share_server) + @slow_me_down def create_share(self, context, share, share_server=None): """Is called to create share.""" return self._create_share(share, share_server=share_server) + @slow_me_down def create_share_from_snapshot(self, context, share, snapshot, share_server=None): """Is called to create share from snapshot.""" @@ -115,26 +215,32 @@ class DummyDriver(driver.ShareDriver): ) return {"provider_location": snapshot_name} + @slow_me_down def create_snapshot(self, context, snapshot, share_server=None): """Is called to create snapshot.""" return self._create_snapshot(snapshot) + @slow_me_down def delete_share(self, context, share, share_server=None): """Is called to remove share.""" self.private_storage.delete(share["id"]) + @slow_me_down def delete_snapshot(self, context, snapshot, share_server=None): """Is called to remove snapshot.""" self.private_storage.delete(snapshot["id"]) + @slow_me_down def get_pool(self, share): """Return pool name where the share resides on.""" pool_name = share_utils.extract_host(share["host"], level="pool") return pool_name + @slow_me_down def ensure_share(self, context, share, share_server=None): """Invoked to ensure that share is exported.""" + @slow_me_down def update_access(self, context, share, access_rules, add_rules, delete_rules, share_server=None): """Update access rules for given share.""" @@ -149,27 +255,34 @@ class DummyDriver(driver.ShareDriver): "access_type": access_type, "share_proto": share_proto} raise exception.InvalidShareAccess(reason=msg) + @slow_me_down def do_setup(self, context): """Any initialization the share driver does while starting.""" + @slow_me_down def manage_existing(self, share, driver_options): """Brings an existing share under Manila management.""" return {"size": 1, "export_locations": self._create_share(share)} + @slow_me_down def unmanage(self, share): """Removes the specified share from Manila management.""" + @slow_me_down def manage_existing_snapshot(self, snapshot, driver_options): """Brings an existing snapshot under Manila management.""" self._create_snapshot(snapshot) return {"size": 1, "provider_location": snapshot["provider_location"]} + @slow_me_down def unmanage_snapshot(self, snapshot): """Removes the specified snapshot from Manila management.""" + @slow_me_down def extend_share(self, share, new_size, share_server=None): """Extends size of existing share.""" + @slow_me_down def shrink_share(self, share, new_size, share_server=None): """Shrinks size of existing share.""" @@ -180,6 +293,7 @@ class DummyDriver(driver.ShareDriver): def get_admin_network_allocations_number(self): return 1 + @slow_me_down def _setup_server(self, network_info, metadata=None): """Sets up and configures share server with given network parameters. @@ -197,9 +311,11 @@ class DummyDriver(driver.ShareDriver): } return server_details + @slow_me_down def _teardown_server(self, server_details, security_services=None): """Tears down share server.""" + @slow_me_down def _get_pools_info(self): pools = [{ "pool_name": "fake_pool_for_%s" % self.backend_name, @@ -211,6 +327,7 @@ class DummyDriver(driver.ShareDriver): pools[0]["replication_type"] = "readable" return pools + @slow_me_down def _update_share_stats(self, data=None): """Retrieve stats info from share group.""" data = { @@ -231,28 +348,33 @@ class DummyDriver(driver.ShareDriver): """Return list of pools related to a particular share server.""" return [] + @slow_me_down def create_consistency_group(self, context, cg_dict, share_server=None): """Create a consistency group.""" LOG.debug( "Successfully created dummy Consistency Group with ID: %s.", cg_dict["id"]) + @slow_me_down def delete_consistency_group(self, context, cg_dict, share_server=None): """Delete a consistency group.""" LOG.debug( "Successfully deleted dummy consistency group with ID %s.", cg_dict["id"]) + @slow_me_down def create_cgsnapshot(self, context, snap_dict, share_server=None): """Create a consistency group snapshot.""" LOG.debug("Successfully created CG snapshot %s." % snap_dict["id"]) return None, None + @slow_me_down def delete_cgsnapshot(self, context, snap_dict, share_server=None): """Delete a consistency group snapshot.""" LOG.debug("Successfully deleted CG snapshot %s." % snap_dict["id"]) return None, None + @slow_me_down def create_consistency_group_from_cgsnapshot( self, context, cg_dict, cgsnapshot_dict, share_server=None): """Create a consistency group from a cgsnapshot.""" @@ -262,6 +384,7 @@ class DummyDriver(driver.ShareDriver): {"cg_id": cg_dict["id"], "cg_snap_id": cgsnapshot_dict["id"]}) return None, [] + @slow_me_down def create_replica(self, context, replica_list, new_replica, access_rules, replica_snapshots, share_server=None): """Replicate the active replica to a new replica on this backend.""" @@ -280,11 +403,13 @@ class DummyDriver(driver.ShareDriver): "access_rules_status": constants.STATUS_ACTIVE, } + @slow_me_down def delete_replica(self, context, replica_list, replica_snapshots, replica, share_server=None): """Delete a replica.""" self.private_storage.delete(replica["id"]) + @slow_me_down def promote_replica(self, context, replica_list, replica, access_rules, share_server=None): """Promote a replica to 'active' replica state.""" @@ -298,12 +423,14 @@ class DummyDriver(driver.ShareDriver): {"id": r["id"], "replica_state": replica_state}) return return_replica_list + @slow_me_down def update_replica_state(self, context, replica_list, replica, access_rules, replica_snapshots, share_server=None): """Update the replica_state of a replica.""" return constants.REPLICA_STATE_IN_SYNC + @slow_me_down def create_replicated_snapshot(self, context, replica_list, replica_snapshots, share_server=None): """Create a snapshot on active instance and update across the replicas. @@ -315,6 +442,7 @@ class DummyDriver(driver.ShareDriver): {"id": r["id"], "status": constants.STATUS_AVAILABLE}) return return_replica_snapshots + @slow_me_down def delete_replicated_snapshot(self, context, replica_list, replica_snapshots, share_server=None): """Delete a snapshot by deleting its instances across the replicas.""" @@ -324,6 +452,7 @@ class DummyDriver(driver.ShareDriver): {"id": r["id"], "status": constants.STATUS_DELETED}) return return_replica_snapshots + @slow_me_down def update_replicated_snapshot(self, context, replica_list, share_replica, replica_snapshots, replica_snapshot, share_server=None): @@ -331,6 +460,7 @@ class DummyDriver(driver.ShareDriver): return { "id": replica_snapshot["id"], "status": constants.STATUS_AVAILABLE} + @slow_me_down def migration_check_compatibility( self, context, source_share, destination_share, share_server=None, destination_share_server=None): @@ -342,6 +472,7 @@ class DummyDriver(driver.ShareDriver): 'nondisruptive': True, } + @slow_me_down def migration_start( self, context, source_share, destination_share, share_server=None, destination_share_server=None): @@ -353,6 +484,7 @@ class DummyDriver(driver.ShareDriver): source_share["id"]) self.migration_progress[source_share['share_id']] = 0 + @slow_me_down def migration_continue( self, context, source_share, destination_share, share_server=None, destination_share_server=None): @@ -369,6 +501,7 @@ class DummyDriver(driver.ShareDriver): return self.migration_progress[source_share["id"]] == 100 + @slow_me_down def migration_complete( self, context, source_share, destination_share, share_server=None, destination_share_server=None): @@ -394,6 +527,7 @@ class DummyDriver(driver.ShareDriver): return self._generate_export_locations( mountpoint, share_server=share_server) + @slow_me_down def migration_cancel( self, context, source_share, destination_share, share_server=None, destination_share_server=None): @@ -403,6 +537,7 @@ class DummyDriver(driver.ShareDriver): source_share["id"]) self.migration_progress.pop(source_share["id"], None) + @slow_me_down def migration_get_progress( self, context, source_share, destination_share, share_server=None, destination_share_server=None):