From 269e73c11b19cbb17e92b6062a7844d9705d1a66 Mon Sep 17 00:00:00 2001 From: Nashwan Azhari Date: Thu, 11 Apr 2024 13:00:41 +0300 Subject: [PATCH 1/8] Add '/deployments' resource operations. Signed-off-by: Nashwan Azhari --- coriolisclient/cli/deployments.py | 279 ++++++++++++++++++++++++++++++ coriolisclient/client.py | 2 + coriolisclient/v1/deployments.py | 85 +++++++++ setup.cfg | 6 + 4 files changed, 372 insertions(+) create mode 100644 coriolisclient/cli/deployments.py create mode 100644 coriolisclient/v1/deployments.py diff --git a/coriolisclient/cli/deployments.py b/coriolisclient/cli/deployments.py new file mode 100644 index 0000000..0337cd5 --- /dev/null +++ b/coriolisclient/cli/deployments.py @@ -0,0 +1,279 @@ +# Copyright (c) 2024 Cloudbase Solutions Srl +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +Command-line interface sub-commands related to deployments. +""" + +import os + +from cliff import command +from cliff import lister +from cliff import show + +from coriolisclient.cli import formatter +from coriolisclient.cli import utils as cli_utils + + +class DeploymentFormatter(formatter.EntityFormatter): + + columns = ("ID", + "Status", + "Instances", + "Notes", + "Created", + ) + + def _get_sorted_list(self, obj_list): + return sorted(obj_list, key=lambda o: o.created_at) + + def _get_formatted_data(self, obj): + data = (obj.id, + obj.last_execution_status, + "\n".join(obj.instances), + obj.notes, + obj.created_at, + ) + return data + + +class DeploymentDetailFormatter(formatter.EntityFormatter): + + def __init__(self, show_instances_data=False): + self.columns = [ + "id", + "status", + "created", + "last_updated", + "replica_id", + "replica_scenario_type", + "reservation_id", + "instances", + "notes", + "origin_endpoint_id", + "origin_minion_pool_id", + "destination_endpoint_id", + "destination_minion_pool_id", + "instance_osmorphing_minion_pool_mappings", + "replication_count", + "shutdown_instances", + "destination_environment", + "source_environment", + "network_map", + "disk_storage_mappings", + "storage_backend_mappings", + "default_storage_backend", + "user_scripts", + "tasks", + "transfer_result" + ] + + if show_instances_data: + self.columns.append("instances_data") + + def _format_instances(self, obj): + return os.linesep.join(sorted(obj.instances)) + + def _format_progress_updates(self, task_dict): + return ("%(ls)s" % {"ls": os.linesep}).join( + [self._format_progress_update(p) for p in + sorted(task_dict.get("progress_updates", []), + key=lambda p: (p.get('index', 0), p["created_at"]))]) + + def _format_task(self, task): + d = task.to_dict() + d["depends_on"] = ", ".join(d.get("depends_on") or []) + + progress_updates_format = "progress_updates:" + progress_updates = self._format_progress_updates(d) + if progress_updates: + progress_updates_format += os.linesep + progress_updates_format += progress_updates + + return os.linesep.join( + ["%s: %s" % (k, d.get(k) or "") for k in + ['id', + 'task_type', + 'instance', + 'status', + 'depends_on', + 'exception_details']] + + [progress_updates_format]) + + def _format_tasks(self, obj): + return ("%(ls)s%(ls)s" % {"ls": os.linesep}).join( + [self._format_task(t) for t in obj.tasks]) + + def _get_formatted_data(self, obj): + storage_mappings = obj.to_dict().get("storage_mappings", {}) + default_storage, backend_mappings, disk_mappings = ( + cli_utils.parse_storage_mappings(storage_mappings)) + data = [obj.id, + obj.last_execution_status, + obj.created_at, + obj.updated_at, + getattr(obj, 'replica_id', ""), + getattr(obj, 'replica_scenario_type', ""), + obj.reservation_id, + self._format_instances(obj), + obj.notes, + obj.origin_endpoint_id, + obj.origin_minion_pool_id, + obj.destination_endpoint_id, + obj.destination_minion_pool_id, + cli_utils.format_json_for_object_property( + obj, 'instance_osmorphing_minion_pool_mappings'), + getattr(obj, 'replication_count', None), + getattr(obj, 'shutdown_instances', False), + cli_utils.format_json_for_object_property( + obj, prop_name="destination_environment"), + cli_utils.format_json_for_object_property( + obj, 'source_environment'), + cli_utils.format_json_for_object_property(obj, 'network_map'), + cli_utils.format_mapping(disk_mappings), + cli_utils.format_mapping(backend_mappings), + default_storage, + cli_utils.format_json_for_object_property(obj, 'user_scripts'), + self._format_tasks(obj), + cli_utils.format_json_for_object_property( + obj, 'transfer_result') + ] + + if "instances_data" in self.columns: + data.append(obj.info) + + return data + + +class CreateDeployment(show.ShowOne): + """Start a new deployment from an existing replica""" + def get_parser(self, prog_name): + parser = super(CreateDeployment, self).get_parser(prog_name) + parser.add_argument('replica', + help='The ID of the replica to migrate') + parser.add_argument('--force', + help='Force the deployment in case of a replica ' + 'with failed executions', action='store_true', + default=False) + parser.add_argument('--dont-clone-disks', + help='Retain the replica disks by cloning them', + action='store_false', dest="clone_disks", + default=True) + parser.add_argument('--skip-os-morphing', + help='Skip the OS morphing process', + action='store_true', + default=False) + parser.add_argument('--user-script-global', action='append', + required=False, + dest="global_scripts", + help='A script that will run for a particular ' + 'os_type. This option can be used multiple ' + 'times. Use: linux=/path/to/script.sh or ' + 'windows=/path/to/script.ps1') + parser.add_argument('--user-script-instance', action='append', + required=False, + dest="instance_scripts", + help='A script that will run for a particular ' + 'instance specified by the --instance option. ' + 'This option can be used multiple times. ' + 'Use: "instance_name"=/path/to/script.sh.' + ' This option overwrites any OS specific script ' + 'specified in --user-script-global for this ' + 'instance') + cli_utils.add_minion_pool_args_to_parser( + parser, include_origin_pool_arg=False, + include_destination_pool_arg=False, + include_osmorphing_pool_mappings_arg=True) + + return parser + + def take_action(self, args): + m = self.app.client_manager.coriolis.deployments + user_scripts = cli_utils.compose_user_scripts( + args.global_scripts, args.instance_scripts) + instance_osmorphing_minion_pool_mappings = None + if args.instance_osmorphing_minion_pool_mappings: + instance_osmorphing_minion_pool_mappings = { + mp['instance_id']: mp['pool_id'] + for mp in args.instance_osmorphing_minion_pool_mappings} + + deployment = m.create_from_replica( + args.replica, + args.clone_disks, + args.force, + args.skip_os_morphing, + user_scripts=user_scripts, + instance_osmorphing_minion_pool_mappings=( + instance_osmorphing_minion_pool_mappings)) + + return DeploymentDetailFormatter().get_formatted_entity(deployment) + + +class ShowDeployment(show.ShowOne): + """Show a deployment""" + + def get_parser(self, prog_name): + parser = super(ShowDeployment, self).get_parser(prog_name) + parser.add_argument('id', help='The deployment\'s id') + parser.add_argument('--show-instances-data', action='store_true', + help='Includes the instances data used for tasks ' + 'execution, this is useful for troubleshooting', + default=False) + return parser + + def take_action(self, args): + deployment = self.app.client_manager.coriolis.deployments.get(args.id) + return DeploymentDetailFormatter( + args.show_instances_data).get_formatted_entity(deployment) + + +class CancelDeployment(command.Command): + """Cancel a deployment""" + + def get_parser(self, prog_name): + parser = super(CancelDeployment, self).get_parser(prog_name) + parser.add_argument('id', help='The deployment\'s id') + parser.add_argument('--force', + help='Perform a forced termination of running ' + 'tasks', action='store_true', + default=False) + return parser + + def take_action(self, args): + self.app.client_manager.coriolis.deployments.cancel(args.id, args.force) + + +class DeleteDeployment(command.Command): + """Delete a deployment""" + + def get_parser(self, prog_name): + parser = super(DeleteDeployment, self).get_parser(prog_name) + parser.add_argument('id', help='The deployment\'s id') + return parser + + def take_action(self, args): + self.app.client_manager.coriolis.deployments.delete(args.id) + + +class ListDeployment(lister.Lister): + """List deployments""" + + def get_parser(self, prog_name): + parser = super(ListDeployment, self).get_parser(prog_name) + return parser + + def take_action(self, args): + obj_list = self.app.client_manager.coriolis.deployments.list() + return DeploymentFormatter().list_objects(obj_list) diff --git a/coriolisclient/client.py b/coriolisclient/client.py index d032d3d..50594b0 100644 --- a/coriolisclient/client.py +++ b/coriolisclient/client.py @@ -17,6 +17,7 @@ from keystoneauth1 import adapter +from coriolisclient.v1 import deployments from coriolisclient.v1 import diagnostics from coriolisclient.v1 import endpoint_destination_minion_pool_options from coriolisclient.v1 import endpoint_destination_options @@ -84,6 +85,7 @@ def __init__(self, session=None, *args, **kwargs): endpoint_source_options.EndpointSourceOptionsManager(httpclient)) self.endpoint_storage = endpoint_storage.EndpointStorageManager( httpclient) + self.deployments = deployments.DeploymentManager(httpclient) self.migrations = migrations.MigrationManager(httpclient) self.minion_pools = minion_pools.MinionPoolManager(httpclient) self.providers = providers.ProvidersManager(httpclient) diff --git a/coriolisclient/v1/deployments.py b/coriolisclient/v1/deployments.py new file mode 100644 index 0000000..99990b2 --- /dev/null +++ b/coriolisclient/v1/deployments.py @@ -0,0 +1,85 @@ +# Copyright (c) 2024 Cloudbase Solutions Srl +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from coriolisclient import base +from coriolisclient.v1 import common + + +class Deployment(base.Resource): + _tasks = None + + @property + def source_environment(self): + source_env = self._info.get("source_environment") + if source_env is not None: + return common.SourceEnvironment(None, source_env, loaded=True) + + @property + def destination_environment(self): + dest_env = self._info.get("destination_environment") + if dest_env is not None: + return common.DestinationEnvironment(None, dest_env, loaded=True) + + @property + def transfer_result(self): + res = self._info.get("transfer_result") + if res is not None: + return common.TransferResult(None, res, loaded=True) + + @property + def tasks(self): + if self._info.get('tasks') is None: + self.get() + return [common.Task(None, d, loaded=True) for d in + self._info.get('tasks', [])] + + +class DeploymentManager(base.BaseManager): + resource_class = Deployment + + def __init__(self, api): + super(DeploymentManager, self).__init__(api) + + def list(self, detail=False): + path = "/deployments" + if detail: + path = "%s/detail" % path + return self._list(path, 'deployments') + + def get(self, deployment): + return self._get('/deployments/%s' % base.getid(deployment), 'deployment') + + def create_from_replica(self, replica_id, clone_disks=True, force=False, + skip_os_morphing=False, user_scripts=None, + instance_osmorphing_minion_pool_mappings=None): + data = { + "deployment": { + "replica_id": replica_id, + "clone_disks": clone_disks, + "force": force, + "skip_os_morphing": skip_os_morphing, + "user_scripts": user_scripts}} + if instance_osmorphing_minion_pool_mappings is not None: + data['deployment']['instance_osmorphing_minion_pool_mappings'] = ( + instance_osmorphing_minion_pool_mappings) + return self._post('/deployments', data, 'deployment') + + def delete(self, deployment): + return self._delete('/deployment/%s' % base.getid(deployment)) + + def cancel(self, deployment, force=False): + return self.client.post( + '/deployments/%s/actions' % base.getid(deployment), + json={'cancel': {'force': force}}) diff --git a/setup.cfg b/setup.cfg index eeaabb9..e39bbbe 100644 --- a/setup.cfg +++ b/setup.cfg @@ -51,6 +51,12 @@ coriolis.v1 = migration_list = coriolisclient.cli.migrations:ListMigration migration_show = coriolisclient.cli.migrations:ShowMigration + deployment_cancel = coriolisclient.cli.deployments:CancelDeployment + deployment_create = coriolisclient.cli.deployments:CreateDeployment + deployment_delete = coriolisclient.cli.deployments:DeleteDeployment + deployment_list = coriolisclient.cli.deployments:ListDeployment + deployment_show = coriolisclient.cli.deployments:ShowDeployment + provider_list = coriolisclient.cli.providers:ListProvider provider_schema_list = coriolisclient.cli.providers:ListProviderSchemas From 59c7f562a6f7113e45381e521af856b12162b434 Mon Sep 17 00:00:00 2001 From: Nashwan Azhari Date: Thu, 11 Apr 2024 13:14:08 +0300 Subject: [PATCH 2/8] Add '--scenario' argument to Replica creation. Signed-off-by: Nashwan Azhari --- coriolisclient/cli/deployments.py | 4 ++-- coriolisclient/cli/replicas.py | 21 +++++++++++++++++++++ coriolisclient/v1/replicas.py | 2 ++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/coriolisclient/cli/deployments.py b/coriolisclient/cli/deployments.py index 0337cd5..eba108d 100644 --- a/coriolisclient/cli/deployments.py +++ b/coriolisclient/cli/deployments.py @@ -124,8 +124,8 @@ def _get_formatted_data(self, obj): obj.last_execution_status, obj.created_at, obj.updated_at, - getattr(obj, 'replica_id', ""), - getattr(obj, 'replica_scenario_type', ""), + obj.replica_id, + obj.replica_scenario_type, obj.reservation_id, self._format_instances(obj), obj.notes, diff --git a/coriolisclient/cli/replicas.py b/coriolisclient/cli/replicas.py index b4ab7be..29a3cbf 100644 --- a/coriolisclient/cli/replicas.py +++ b/coriolisclient/cli/replicas.py @@ -28,6 +28,10 @@ from coriolisclient.cli import utils as cli_utils +REPLICA_SCENARIO_REPLICA = "replica" +REPLICA_SCENARIO_LIVE_MIGRATION = "live_migration" + + class ReplicaFormatter(formatter.EntityFormatter): columns = ("ID", @@ -63,6 +67,7 @@ def __init__(self, show_instances_data=False): "id", "created", "last_updated", + "scenario_type", "reservation_id", "instances", "notes", @@ -102,6 +107,7 @@ def _get_formatted_data(self, obj): data = [obj.id, obj.created_at, obj.updated_at, + getattr(obj, "scenario", ""), obj.reservation_id, self._format_instances(obj), obj.notes, @@ -141,6 +147,20 @@ def get_parser(self, prog_name): dest="instances", metavar="INSTANCE_IDENTIFIER", help='The identifier of a source instance to be ' 'replicated. Can be specified multiple times') + parser.add_argument('--scenario', + dest="scenario", metavar="SCENARIO", + choices=[ + REPLICA_SCENARIO_REPLICA, + REPLICA_SCENARIO_LIVE_MIGRATION], + default=REPLICA_SCENARIO_REPLICA, + help='The type of scenario to use when creating ' + 'the Replica. "replica" will create a ' + 'monthly-billed Replica which can be ' + 'executed and deployed as many times as ' + 'desired, while "live_migration" will ' + 'create a Replica which can be synced ' + 'as many times as needed but only ' + 'deployed once.') parser.add_argument('--notes', dest='notes', help='Notes about the replica') parser.add_argument('--user-script-global', action='append', @@ -202,6 +222,7 @@ def take_action(self, args): source_environment, destination_environment, args.instances, + args.scenario, network_map=network_map, notes=args.notes, storage_mappings=storage_mappings, diff --git a/coriolisclient/v1/replicas.py b/coriolisclient/v1/replicas.py index e82c023..48a3b5c 100644 --- a/coriolisclient/v1/replicas.py +++ b/coriolisclient/v1/replicas.py @@ -58,6 +58,7 @@ def get(self, replica): def create(self, origin_endpoint_id, destination_endpoint_id, source_environment, destination_environment, instances, + replica_scenario, network_map=None, notes=None, storage_mappings=None, origin_minion_pool_id=None, destination_minion_pool_id=None, instance_osmorphing_minion_pool_mappings=None, @@ -73,6 +74,7 @@ def create(self, origin_endpoint_id, destination_endpoint_id, "destination_endpoint_id": destination_endpoint_id, "destination_environment": destination_environment, "instances": instances, + "scenario": replica_scenario, "network_map": network_map, "notes": notes, "storage_mappings": storage_mappings, From 526770d2207e7c3b8efb4f1978ba90d4b90817ba Mon Sep 17 00:00:00 2001 From: Nashwan Azhari Date: Fri, 12 Apr 2024 16:27:24 +0300 Subject: [PATCH 3/8] Add 'replica_id' field to `deployment list` subcommand. Signed-off-by: Nashwan Azhari --- coriolisclient/cli/deployments.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/coriolisclient/cli/deployments.py b/coriolisclient/cli/deployments.py index eba108d..0ef00f1 100644 --- a/coriolisclient/cli/deployments.py +++ b/coriolisclient/cli/deployments.py @@ -30,6 +30,7 @@ class DeploymentFormatter(formatter.EntityFormatter): columns = ("ID", + "Replica ID", "Status", "Instances", "Notes", @@ -41,6 +42,7 @@ def _get_sorted_list(self, obj_list): def _get_formatted_data(self, obj): data = (obj.id, + obj.replica_id, obj.last_execution_status, "\n".join(obj.instances), obj.notes, From 06c80f8cbec653360f388ce955477d5ffc16665f Mon Sep 17 00:00:00 2001 From: Nashwan Azhari Date: Fri, 12 Apr 2024 16:27:39 +0300 Subject: [PATCH 4/8] Add 'scenario' field to replica list` subcommand. Signed-off-by: Nashwan Azhari --- coriolisclient/cli/replicas.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/coriolisclient/cli/replicas.py b/coriolisclient/cli/replicas.py index 29a3cbf..a99d195 100644 --- a/coriolisclient/cli/replicas.py +++ b/coriolisclient/cli/replicas.py @@ -35,6 +35,7 @@ class ReplicaFormatter(formatter.EntityFormatter): columns = ("ID", + "Scenario", "Instances", "Notes", "Last Execution Status", @@ -52,6 +53,7 @@ def _format_last_execution(self, obj): def _get_formatted_data(self, obj): data = (obj.id, + getattr(obj, "scenario", "replica"), "\n".join(obj.instances), obj.notes, obj.last_execution_status, From e6b795e01acf283ab0fdf672e2329848bf9125f6 Mon Sep 17 00:00:00 2001 From: Daniel Vincze Date: Thu, 8 Aug 2024 17:00:00 +0300 Subject: [PATCH 5/8] Remove `migration` subcommands --- coriolisclient/cli/migrations.py | 375 ------------- coriolisclient/client.py | 2 - coriolisclient/tests/cli/test_migrations.py | 568 -------------------- coriolisclient/v1/migrations.py | 125 ----- 4 files changed, 1070 deletions(-) delete mode 100644 coriolisclient/cli/migrations.py delete mode 100644 coriolisclient/tests/cli/test_migrations.py delete mode 100644 coriolisclient/v1/migrations.py diff --git a/coriolisclient/cli/migrations.py b/coriolisclient/cli/migrations.py deleted file mode 100644 index 7c10b56..0000000 --- a/coriolisclient/cli/migrations.py +++ /dev/null @@ -1,375 +0,0 @@ -# Copyright (c) 2016 Cloudbase Solutions Srl -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -""" -Command-line interface sub-commands related to migrations. -""" - -import os - -from cliff import command -from cliff import lister -from cliff import show - -from coriolisclient.cli import formatter -from coriolisclient.cli import utils as cli_utils - - -class MigrationFormatter(formatter.EntityFormatter): - - columns = ("ID", - "Status", - "Instances", - "Notes", - "Created", - ) - - def _get_sorted_list(self, obj_list): - return sorted(obj_list, key=lambda o: o.created_at) - - def _get_formatted_data(self, obj): - data = (obj.id, - obj.last_execution_status, - "\n".join(obj.instances), - obj.notes, - obj.created_at, - ) - return data - - -class MigrationDetailFormatter(formatter.EntityFormatter): - - def __init__(self, show_instances_data=False): - self.columns = [ - "id", - "status", - "created", - "last_updated", - "reservation_id", - "instances", - "notes", - "origin_endpoint_id", - "origin_minion_pool_id", - "destination_endpoint_id", - "destination_minion_pool_id", - "instance_osmorphing_minion_pool_mappings", - "replication_count", - "shutdown_instances", - "destination_environment", - "source_environment", - "network_map", - "disk_storage_mappings", - "storage_backend_mappings", - "default_storage_backend", - "user_scripts", - "tasks", - "transfer_result" - ] - - if show_instances_data: - self.columns.append("instances_data") - - def _format_instances(self, obj): - return os.linesep.join(sorted(obj.instances)) - - def _format_progress_updates(self, task_dict): - return ("%(ls)s" % {"ls": os.linesep}).join( - [self._format_progress_update(p) for p in - sorted(task_dict.get("progress_updates", []), - key=lambda p: (p.get('index', 0), p["created_at"]))]) - - def _format_task(self, task): - d = task.to_dict() - d["depends_on"] = ", ".join(d.get("depends_on") or []) - - progress_updates_format = "progress_updates:" - progress_updates = self._format_progress_updates(d) - if progress_updates: - progress_updates_format += os.linesep - progress_updates_format += progress_updates - - return os.linesep.join( - ["%s: %s" % (k, d.get(k) or "") for k in - ['id', - 'task_type', - 'instance', - 'status', - 'depends_on', - 'exception_details']] + - [progress_updates_format]) - - def _format_tasks(self, obj): - return ("%(ls)s%(ls)s" % {"ls": os.linesep}).join( - [self._format_task(t) for t in obj.tasks]) - - def _get_formatted_data(self, obj): - storage_mappings = obj.to_dict().get("storage_mappings", {}) - default_storage, backend_mappings, disk_mappings = ( - cli_utils.parse_storage_mappings(storage_mappings)) - data = [obj.id, - obj.last_execution_status, - obj.created_at, - obj.updated_at, - obj.reservation_id, - self._format_instances(obj), - obj.notes, - obj.origin_endpoint_id, - obj.origin_minion_pool_id, - obj.destination_endpoint_id, - obj.destination_minion_pool_id, - cli_utils.format_json_for_object_property( - obj, 'instance_osmorphing_minion_pool_mappings'), - getattr(obj, 'replication_count', None), - getattr(obj, 'shutdown_instances', False), - cli_utils.format_json_for_object_property( - obj, prop_name="destination_environment"), - cli_utils.format_json_for_object_property( - obj, 'source_environment'), - cli_utils.format_json_for_object_property(obj, 'network_map'), - cli_utils.format_mapping(disk_mappings), - cli_utils.format_mapping(backend_mappings), - default_storage, - cli_utils.format_json_for_object_property(obj, 'user_scripts'), - self._format_tasks(obj), - cli_utils.format_json_for_object_property( - obj, 'transfer_result') - ] - - if "instances_data" in self.columns: - data.append(obj.info) - - return data - - -class CreateMigration(show.ShowOne): - """Start a new migration""" - def get_parser(self, prog_name): - parser = super(CreateMigration, self).get_parser(prog_name) - parser.add_argument('--origin-endpoint', required=True, - help='The origin endpoint id') - parser.add_argument('--destination-endpoint', required=True, - help='The destination endpoint id') - parser.add_argument('--instance', action='append', required=True, - dest="instances", metavar="INSTANCE_IDENTIFIER", - help='The identifier of a source instance to be ' - 'migrated. Can be specified multiple times') - parser.add_argument('--notes', dest='notes', - help='Notes about the migration') - parser.add_argument('--user-script-global', action='append', - required=False, - dest="global_scripts", - help='A script that will run for a particular ' - 'os_type. This option can be used multiple ' - 'times. Use: linux=/path/to/script.sh or ' - 'windows=/path/to/script.ps1') - parser.add_argument('--user-script-instance', action='append', - required=False, - dest="instance_scripts", - help='A script that will run for a particular ' - 'instance specified by the --instance option. ' - 'This option can be used multiple times. ' - 'Use: "instance_name"=/path/to/script.sh.' - ' This option overwrites any OS specific script ' - 'specified in --user-script-global for this ' - 'instance') - parser.add_argument('--skip-os-morphing', - help='Skip the OS morphing process', - action='store_true', - default=False) - parser.add_argument('--replication-count', - type=int, - help='Number of times to perform a replica sync ' - 'before deploying the migrated instance.') - parser.add_argument('--shutdown-instances', - action='store_true', - help='Whether or not to shut down the instance on ' - 'the source platform before performing the ' - 'final Replica sync') - - cli_utils.add_args_for_json_option_to_parser( - parser, 'destination-environment') - cli_utils.add_args_for_json_option_to_parser(parser, 'network-map') - cli_utils.add_args_for_json_option_to_parser( - parser, 'source-environment') - cli_utils.add_storage_mappings_arguments_to_parser(parser) - cli_utils.add_minion_pool_args_to_parser( - parser, include_origin_pool_arg=True, - include_destination_pool_arg=True, - include_osmorphing_pool_mappings_arg=True) - - return parser - - def take_action(self, args): - destination_environment = cli_utils.get_option_value_from_args( - args, 'destination-environment') - source_environment = cli_utils.get_option_value_from_args( - args, 'source-environment') - network_map = cli_utils.get_option_value_from_args( - args, 'network-map') - storage_mappings = cli_utils.get_storage_mappings_dict_from_args(args) - endpoints = self.app.client_manager.coriolis.endpoints - origin_endpoint_id = endpoints.get_endpoint_id_for_name( - args.origin_endpoint) - destination_endpoint_id = endpoints.get_endpoint_id_for_name( - args.destination_endpoint) - user_scripts = cli_utils.compose_user_scripts( - args.global_scripts, args.instance_scripts) - instance_osmorphing_minion_pool_mappings = None - if args.instance_osmorphing_minion_pool_mappings: - instance_osmorphing_minion_pool_mappings = { - mp['instance_id']: mp['pool_id'] - for mp in args.instance_osmorphing_minion_pool_mappings} - - migration = self.app.client_manager.coriolis.migrations.create( - origin_endpoint_id, - destination_endpoint_id, - source_environment, - destination_environment, - args.instances, - network_map=network_map, - notes=args.notes, - storage_mappings=storage_mappings, - skip_os_morphing=args.skip_os_morphing, - replication_count=args.replication_count, - shutdown_instances=args.shutdown_instances, - origin_minion_pool_id=args.origin_minion_pool_id, - destination_minion_pool_id=args.destination_minion_pool_id, - instance_osmorphing_minion_pool_mappings=( - instance_osmorphing_minion_pool_mappings), - user_scripts=user_scripts) - - return MigrationDetailFormatter().get_formatted_entity(migration) - - -class CreateMigrationFromReplica(show.ShowOne): - """Start a new migration from an existing replica""" - def get_parser(self, prog_name): - parser = super(CreateMigrationFromReplica, self).get_parser(prog_name) - parser.add_argument('replica', - help='The ID of the replica to migrate') - parser.add_argument('--force', - help='Force the migration in case of a replica ' - 'with failed executions', action='store_true', - default=False) - parser.add_argument('--dont-clone-disks', - help='Retain the replica disks by cloning them', - action='store_false', dest="clone_disks", - default=True) - parser.add_argument('--skip-os-morphing', - help='Skip the OS morphing process', - action='store_true', - default=False) - parser.add_argument('--user-script-global', action='append', - required=False, - dest="global_scripts", - help='A script that will run for a particular ' - 'os_type. This option can be used multiple ' - 'times. Use: linux=/path/to/script.sh or ' - 'windows=/path/to/script.ps1') - parser.add_argument('--user-script-instance', action='append', - required=False, - dest="instance_scripts", - help='A script that will run for a particular ' - 'instance specified by the --instance option. ' - 'This option can be used multiple times. ' - 'Use: "instance_name"=/path/to/script.sh.' - ' This option overwrites any OS specific script ' - 'specified in --user-script-global for this ' - 'instance') - cli_utils.add_minion_pool_args_to_parser( - parser, include_origin_pool_arg=False, - include_destination_pool_arg=False, - include_osmorphing_pool_mappings_arg=True) - - return parser - - def take_action(self, args): - m = self.app.client_manager.coriolis.migrations - user_scripts = cli_utils.compose_user_scripts( - args.global_scripts, args.instance_scripts) - instance_osmorphing_minion_pool_mappings = None - if args.instance_osmorphing_minion_pool_mappings: - instance_osmorphing_minion_pool_mappings = { - mp['instance_id']: mp['pool_id'] - for mp in args.instance_osmorphing_minion_pool_mappings} - - migration = m.create_from_replica( - args.replica, - args.clone_disks, - args.force, - args.skip_os_morphing, - user_scripts=user_scripts, - instance_osmorphing_minion_pool_mappings=( - instance_osmorphing_minion_pool_mappings)) - - return MigrationDetailFormatter().get_formatted_entity(migration) - - -class ShowMigration(show.ShowOne): - """Show a migration""" - - def get_parser(self, prog_name): - parser = super(ShowMigration, self).get_parser(prog_name) - parser.add_argument('id', help='The migration\'s id') - parser.add_argument('--show-instances-data', action='store_true', - help='Includes the instances data used for tasks ' - 'execution, this is useful for troubleshooting', - default=False) - return parser - - def take_action(self, args): - migration = self.app.client_manager.coriolis.migrations.get(args.id) - return MigrationDetailFormatter( - args.show_instances_data).get_formatted_entity(migration) - - -class CancelMigration(command.Command): - """Cancel a migration""" - - def get_parser(self, prog_name): - parser = super(CancelMigration, self).get_parser(prog_name) - parser.add_argument('id', help='The migration\'s id') - parser.add_argument('--force', - help='Perform a forced termination of running ' - 'tasks', action='store_true', - default=False) - return parser - - def take_action(self, args): - self.app.client_manager.coriolis.migrations.cancel(args.id, args.force) - - -class DeleteMigration(command.Command): - """Delete a migration""" - - def get_parser(self, prog_name): - parser = super(DeleteMigration, self).get_parser(prog_name) - parser.add_argument('id', help='The migration\'s id') - return parser - - def take_action(self, args): - self.app.client_manager.coriolis.migrations.delete(args.id) - - -class ListMigration(lister.Lister): - """List migrations""" - - def get_parser(self, prog_name): - parser = super(ListMigration, self).get_parser(prog_name) - return parser - - def take_action(self, args): - obj_list = self.app.client_manager.coriolis.migrations.list() - return MigrationFormatter().list_objects(obj_list) diff --git a/coriolisclient/client.py b/coriolisclient/client.py index 50594b0..95a1dfa 100644 --- a/coriolisclient/client.py +++ b/coriolisclient/client.py @@ -32,7 +32,6 @@ from coriolisclient.v1 import licensing_reservations from coriolisclient.v1 import licensing_server from coriolisclient.v1 import logging as coriolis_logging -from coriolisclient.v1 import migrations from coriolisclient.v1 import minion_pools from coriolisclient.v1 import providers from coriolisclient.v1 import regions @@ -86,7 +85,6 @@ def __init__(self, session=None, *args, **kwargs): self.endpoint_storage = endpoint_storage.EndpointStorageManager( httpclient) self.deployments = deployments.DeploymentManager(httpclient) - self.migrations = migrations.MigrationManager(httpclient) self.minion_pools = minion_pools.MinionPoolManager(httpclient) self.providers = providers.ProvidersManager(httpclient) self.replicas = replicas.ReplicaManager(httpclient) diff --git a/coriolisclient/tests/cli/test_migrations.py b/coriolisclient/tests/cli/test_migrations.py deleted file mode 100644 index 85d9720..0000000 --- a/coriolisclient/tests/cli/test_migrations.py +++ /dev/null @@ -1,568 +0,0 @@ -# Copyright 2024 Cloudbase Solutions Srl -# All Rights Reserved. - -import os -from unittest import mock - -from cliff import command -from cliff import lister -from cliff import show - -from coriolisclient.cli import formatter -from coriolisclient.cli import migrations -from coriolisclient.cli import utils as cli_utils -from coriolisclient.tests import test_base - - -class MigrationFormatterTestCase(test_base.CoriolisBaseTestCase): - """Test suite for the Coriolis Client Migration Formatter.""" - - def setUp(self): - super(MigrationFormatterTestCase, self).setUp() - self.migration = migrations.MigrationFormatter() - - def test_get_sorted_list(self): - obj1 = mock.Mock() - obj2 = mock.Mock() - obj3 = mock.Mock() - obj1.created_at = "date1" - obj2.created_at = "date2" - obj3.created_at = "date3" - obj_list = [obj2, obj1, obj3] - - result = self.migration._get_sorted_list(obj_list) - - self.assertEqual( - [obj1, obj2, obj3], - result - ) - - def test_get_formatted_data(self): - obj = mock.Mock() - obj.id = mock.sentinel.id - obj.last_execution_status = mock.sentinel.last_execution_status - obj.instances = ["mock_instance3", "mock_instance1", "mock_instance2"] - obj.notes = mock.sentinel.notes - obj.created_at = mock.sentinel.created_at - - result = self.migration._get_formatted_data(obj) - - self.assertEqual( - ( - mock.sentinel.id, - mock.sentinel.last_execution_status, - ('mock_instance3%(ls)smock_instance1%(ls)smock_instance2' - % {"ls": os.linesep}), - mock.sentinel.notes, - mock.sentinel.created_at - ), - result - ) - - -class MigrationDetailFormatterTestCase(test_base.CoriolisBaseTestCase): - """Test suite for the Coriolis Client Migration Detail Formatter.""" - - def setUp(self): - super(MigrationDetailFormatterTestCase, self).setUp() - self.migration = migrations.MigrationDetailFormatter( - show_instances_data=True) - - def test_format_instances(self): - obj = mock.Mock() - obj.instances = ["mock_instance3", "mock_instance1", "mock_instance2"] - expected_result = ( - 'mock_instance1%(ls)smock_instance2%(ls)smock_instance3' - % {"ls": os.linesep} - ) - - result = self.migration._format_instances(obj) - - self.assertEqual( - expected_result, - result - ) - - @mock.patch.object(formatter.EntityFormatter, '_format_progress_update') - def test_format_progress_updates(self, mock_format_progress_update): - update1 = {"created_at": "date2", "message2": "message2"} - update2 = {"created_at": "date3", "message3": "message3"} - update3 = {"index": 2, "created_at": "date1"} - ret_update1 = "date2 [10] message2" - ret_update2 = "date3 [20] message3" - ret_update3 = "date1 [30] None" - task_dict = {"progress_updates": [update3, update1, update2]} - mock_format_progress_update.side_effect = [ - ret_update1, ret_update2, ret_update3] - expected_result = ( - 'date2 [10] message2%(ls)sdate3 [20] message3%(ls)s' - 'date1 [30] None' % {"ls": os.linesep} - ) - - result = self.migration._format_progress_updates(task_dict) - - self.assertEqual( - expected_result, - result - ) - - @mock.patch.object(migrations.MigrationDetailFormatter, - '_format_progress_updates') - def test_format_task(self, mock_format_progress_updates): - mock_task = mock.Mock() - task = { - "depends_on": ["mock_dep1", "mock_dep2"], - "id": mock.sentinel.id, - "task_type": mock.sentinel.task_type, - "instance": mock.sentinel.instance, - "status": mock.sentinel.status, - "exception_details": mock.sentinel.exception_details - } - mock_task.to_dict.return_value = task - mock_format_progress_updates.return_value = ( - 'date2 [10] message2%(ls)sdate3 [20] message3%(ls)s' - 'date1 [30] None' % {"ls": os.linesep} - ) - expected_result = ( - 'id: sentinel.id%(ls)s' - 'task_type: sentinel.task_type%(ls)s' - 'instance: sentinel.instance%(ls)s' - 'status: sentinel.status%(ls)s' - 'depends_on: mock_dep1, mock_dep2%(ls)s' - 'exception_details: sentinel.exception_details%(ls)s' - 'progress_updates:%(ls)s' - 'date2 [10] message2%(ls)s' - 'date3 [20] message3%(ls)s' - 'date1 [30] None' % {"ls": os.linesep}) - - result = self.migration._format_task(mock_task) - - self.assertEqual( - expected_result, - result - ) - - @mock.patch.object(migrations.MigrationDetailFormatter, - '_format_progress_updates') - def test_format_task_no_progress_updates( - self, mock_format_progress_updates): - mock_task = mock.Mock() - task = { - "depends_on": ["mock_dep1", "mock_dep2"], - "id": mock.sentinel.id, - "task_type": mock.sentinel.task_type, - "instance": mock.sentinel.instance, - "status": mock.sentinel.status, - "exception_details": mock.sentinel.exception_details - } - mock_task.to_dict.return_value = task - mock_format_progress_updates.return_value = None - expected_result = ( - 'id: sentinel.id%(ls)s' - 'task_type: sentinel.task_type%(ls)s' - 'instance: sentinel.instance%(ls)s' - 'status: sentinel.status%(ls)s' - 'depends_on: mock_dep1, mock_dep2%(ls)s' - 'exception_details: sentinel.exception_details%(ls)s' - 'progress_updates:' % {"ls": os.linesep} - ) - - result = self.migration._format_task(mock_task) - - self.assertEqual( - expected_result, - result - ) - - @mock.patch.object(migrations.MigrationDetailFormatter, '_format_task') - def test_format_tasks(self, mock_format_task): - obj = mock.Mock() - obj.tasks = [mock.sentinel.task1, mock.sentinel.task2] - mock_format_task.side_effect = ["task1", "task2"] - expected_result = 'task1%(ls)s%(ls)stask2' % {"ls": os.linesep} - - result = self.migration._format_tasks(obj) - - self.assertEqual( - expected_result, - result - ) - - @mock.patch.object(migrations.MigrationDetailFormatter, '_format_tasks') - @mock.patch.object(cli_utils, 'format_mapping') - @mock.patch.object(cli_utils, 'format_json_for_object_property') - @mock.patch.object(migrations.MigrationDetailFormatter, - '_format_instances') - @mock.patch.object(cli_utils, 'parse_storage_mappings') - def test_get_formatted_data( - self, - mock_parse_storage_mappings, - mock_format_instances, - mock_format_json_for_object_property, - mock_format_mapping, - mock_format_tasks - ): - mock_obj = mock.Mock() - obj = { - "storage_mappings": {'default_storage': mock.sentinel.storage} - } - mock_obj.to_dict.return_value = obj - mock_obj.id = mock.sentinel.id - mock_obj.last_execution_status = mock.sentinel.last_execution_status - mock_obj.created_at = mock.sentinel.created_at - mock_obj.updated_at = mock.sentinel.updated_at - mock_obj.reservation_id = mock.sentinel.reservation_id - mock_format_instances.return_value = mock.sentinel.formatted_instances - mock_obj.notes = mock.sentinel.notes - mock_obj.origin_endpoint_id = mock.sentinel.origin_endpoint_id - mock_obj.origin_minion_pool_id = mock.sentinel.origin_minion_pool_id - mock_obj.destination_endpoint_id = \ - mock.sentinel.destination_endpoint_id - mock_obj.destination_minion_pool_id = \ - mock.sentinel.destination_minion_pool_id - mock_obj.replication_count = mock.sentinel.replication_count - mock_obj.shutdown_instances = mock.sentinel.shutdown_instances - mock_parse_storage_mappings.return_value = ( - mock.sentinel.default_storage, - mock.sentinel.backend_mappings, - mock.sentinel.disk_mappings - ) - mock_format_json_for_object_property.side_effect = [ - mock.sentinel.instance_osmorphing_minion_pool_mappings, - mock.sentinel.destination_environment, - mock.sentinel.source_environment, - mock.sentinel.network_map, - mock.sentinel.user_scripts, - mock.sentinel.transfer_result, - ] - mock_format_mapping.side_effect = [ - mock.sentinel.disk_mapping, mock.sentinel.backend_mappings] - mock_format_tasks.return_value = mock.sentinel.formatted_tasks - mock_obj.info = mock.sentinel.info - expected_result = [ - mock.sentinel.id, - mock.sentinel.last_execution_status, - mock.sentinel.created_at, - mock.sentinel.updated_at, - mock.sentinel.reservation_id, - mock.sentinel.formatted_instances, - mock.sentinel.notes, - mock.sentinel.origin_endpoint_id, - mock.sentinel.origin_minion_pool_id, - mock.sentinel.destination_endpoint_id, - mock.sentinel.destination_minion_pool_id, - mock.sentinel.instance_osmorphing_minion_pool_mappings, - mock.sentinel.replication_count, - mock.sentinel.shutdown_instances, - mock.sentinel.destination_environment, - mock.sentinel.source_environment, - mock.sentinel.network_map, - mock.sentinel.disk_mapping, - mock.sentinel.backend_mappings, - mock.sentinel.default_storage, - mock.sentinel.user_scripts, - mock.sentinel.formatted_tasks, - mock.sentinel.transfer_result, - mock.sentinel.info - ] - - result = self.migration._get_formatted_data(mock_obj) - - self.assertEqual( - expected_result, - result - ) - - -class CreateMigrationTestCase(test_base.CoriolisBaseTestCase): - """Test suite for the Coriolis Client Create Migration.""" - - def setUp(self): - self.mock_app = mock.Mock() - super(CreateMigrationTestCase, self).setUp() - self.migration = migrations.CreateMigration( - self.mock_app, mock.sentinel.app_args) - - @mock.patch.object(cli_utils, 'add_args_for_json_option_to_parser') - @mock.patch.object(show.ShowOne, 'get_parser') - def test_get_parser( - self, - mock_get_parser, - *_ - ): - result = self.migration.get_parser(mock.sentinel.prog_name) - - self.assertEqual( - mock_get_parser.return_value, - result - ) - mock_get_parser.assert_called_once_with(mock.sentinel.prog_name) - - @mock.patch.object(cli_utils, 'compose_user_scripts') - @mock.patch.object(cli_utils, 'get_storage_mappings_dict_from_args') - @mock.patch.object(cli_utils, 'get_option_value_from_args') - @mock.patch.object(migrations.MigrationDetailFormatter, - 'get_formatted_entity') - def test_take_action( - self, - mock_get_formatted_entity, - mock_get_option_value_from_args, - mock_get_storage_mappings_dict_from_args, - mock_compose_user_scripts - ): - args = mock.Mock() - args.instances = mock.sentinel.instances - args.notes = mock.sentinel.notes - args.skip_os_morphing = mock.sentinel.skip_os_morphing - args.replication_count = mock.sentinel.replication_count - args.shutdown_instances = mock.sentinel.shutdown_instances - args.origin_minion_pool_id = mock.sentinel.origin_minion_pool_id - args.destination_minion_pool_id = \ - mock.sentinel.destination_minion_pool_id - args.instance_osmorphing_minion_pool_mappings = [ - {'instance_id': "instance_id1", 'pool_id': "pool_id1"}, - {'instance_id': "instance_id2", 'pool_id': "pool_id2"} - ] - mock_endpoints = mock.Mock() - mock_migrations = mock.Mock() - self.mock_app.client_manager.coriolis.endpoints = mock_endpoints - mock_endpoints.get_endpoint_id_for_name.side_effect = [ - mock.sentinel.origin_endpoint_id, - mock.sentinel.destination_endpoint_id - ] - self.mock_app.client_manager.coriolis.migrations.create = \ - mock_migrations - mock_get_option_value_from_args.side_effect = [ - mock.sentinel.destination_environment, - mock.sentinel.source_environment, - mock.sentinel.network_map, - ] - - result = self.migration.take_action(args) - - self.assertEqual( - mock_get_formatted_entity.return_value, - result - ) - mock_migrations.assert_called_once_with( - mock.sentinel.origin_endpoint_id, - mock.sentinel.destination_endpoint_id, - mock.sentinel.source_environment, - mock.sentinel.destination_environment, - mock.sentinel.instances, - network_map=mock.sentinel.network_map, - notes=mock.sentinel.notes, - storage_mappings=(mock_get_storage_mappings_dict_from_args. - return_value), - skip_os_morphing=mock.sentinel.skip_os_morphing, - replication_count=mock.sentinel.replication_count, - shutdown_instances=mock.sentinel.shutdown_instances, - origin_minion_pool_id=mock.sentinel.origin_minion_pool_id, - destination_minion_pool_id= - mock.sentinel.destination_minion_pool_id, - instance_osmorphing_minion_pool_mappings={ - 'instance_id1': 'pool_id1', 'instance_id2': 'pool_id2'}, - user_scripts=mock_compose_user_scripts.return_value - ) - mock_get_formatted_entity.assert_called_once_with( - mock_migrations.return_value) - - -class CreateMigrationFromReplicaTestCase(test_base.CoriolisBaseTestCase): - """Test suite for the Coriolis Client Create Migration From Replica.""" - - def setUp(self): - self.mock_app = mock.Mock() - super(CreateMigrationFromReplicaTestCase, self).setUp() - self.migration = migrations.CreateMigrationFromReplica( - self.mock_app, mock.sentinel.app_args) - - @mock.patch.object(cli_utils, 'add_args_for_json_option_to_parser') - @mock.patch.object(show.ShowOne, 'get_parser') - def test_get_parser( - self, - mock_get_parser, - *_ - ): - result = self.migration.get_parser(mock.sentinel.prog_name) - - self.assertEqual( - mock_get_parser.return_value, - result - ) - mock_get_parser.assert_called_once_with(mock.sentinel.prog_name) - - @mock.patch.object(cli_utils, 'compose_user_scripts') - @mock.patch.object(migrations.MigrationDetailFormatter, - 'get_formatted_entity') - def test_take_action( - self, - mock_get_formatted_entity, - mock_compose_user_scripts - ): - args = mock.Mock() - args.replica = mock.sentinel.replica - args.clone_disks = mock.sentinel.clone_disks - args.force = mock.sentinel.force - args.skip_os_morphing = mock.sentinel.skip_os_morphing - args.instance_osmorphing_minion_pool_mappings = [ - {'instance_id': "instance_id1", 'pool_id': "pool_id1"}, - {'instance_id': "instance_id2", 'pool_id': "pool_id2"} - ] - mock_migrations = mock.Mock() - self.mock_app.client_manager.coriolis.migrations = mock_migrations - - result = self.migration.take_action(args) - - self.assertEqual( - mock_get_formatted_entity.return_value, - result - ) - mock_migrations.create_from_replica.assert_called_once_with( - mock.sentinel.replica, - mock.sentinel.clone_disks, - mock.sentinel.force, - mock.sentinel.skip_os_morphing, - user_scripts=mock_compose_user_scripts.return_value, - instance_osmorphing_minion_pool_mappings={ - 'instance_id1': 'pool_id1', 'instance_id2': 'pool_id2'} - ) - mock_get_formatted_entity.assert_called_once_with( - mock_migrations.create_from_replica.return_value) - - -class ShowMigrationTestCase(test_base.CoriolisBaseTestCase): - """Test suite for the Coriolis Client Show Migration.""" - - def setUp(self): - self.mock_app = mock.Mock() - super(ShowMigrationTestCase, self).setUp() - self.migration = migrations.ShowMigration( - self.mock_app, mock.sentinel.app_args) - - @mock.patch.object(show.ShowOne, 'get_parser') - def test_get_parser(self, mock_get_parser): - result = self.migration.get_parser(mock.sentinel.prog_name) - - self.assertEqual( - mock_get_parser.return_value, - result - ) - mock_get_parser.assert_called_once_with(mock.sentinel.prog_name) - - @mock.patch.object(migrations.MigrationDetailFormatter, - 'get_formatted_entity') - def test_take_action(self, mock_get_formatted_entity): - args = mock.Mock() - args.id = mock.sentinel.id - mock_migration = mock.Mock() - self.mock_app.client_manager.coriolis.migrations.get = mock_migration - - result = self.migration.take_action(args) - - self.assertEqual( - mock_get_formatted_entity.return_value, - result - ) - mock_migration.assert_called_once_with(mock.sentinel.id) - mock_get_formatted_entity.assert_called_once_with( - mock_migration.return_value) - - -class CancelMigrationTestCase(test_base.CoriolisBaseTestCase): - """Test suite for the Coriolis Client Cancel Migration.""" - - def setUp(self): - self.mock_app = mock.Mock() - super(CancelMigrationTestCase, self).setUp() - self.migration = migrations.CancelMigration( - self.mock_app, mock.sentinel.app_args) - - @mock.patch.object(command.Command, 'get_parser') - def test_get_parser(self, mock_get_parser): - result = self.migration.get_parser(mock.sentinel.prog_name) - - self.assertEqual( - mock_get_parser.return_value, - result - ) - mock_get_parser.assert_called_once_with(mock.sentinel.prog_name) - - def test_take_action(self): - args = mock.Mock() - args.id = mock.sentinel.id - args.force = mock.sentinel.force - mock_migration = mock.Mock() - self.mock_app.client_manager.coriolis.migrations.cancel = \ - mock_migration - - self.migration.take_action(args) - - mock_migration.assert_called_once_with( - mock.sentinel.id, mock.sentinel.force) - - -class DeleteMigrationTestCase(test_base.CoriolisBaseTestCase): - """Test suite for the Coriolis Client Delete Migration.""" - - def setUp(self): - self.mock_app = mock.Mock() - super(DeleteMigrationTestCase, self).setUp() - self.migration = migrations.DeleteMigration( - self.mock_app, mock.sentinel.app_args) - - @mock.patch.object(command.Command, 'get_parser') - def test_get_parser(self, mock_get_parser): - result = self.migration.get_parser(mock.sentinel.prog_name) - - self.assertEqual( - mock_get_parser.return_value, - result - ) - mock_get_parser.assert_called_once_with(mock.sentinel.prog_name) - - def test_take_action(self): - args = mock.Mock() - args.id = mock.sentinel.id - mock_migration = mock.Mock() - self.mock_app.client_manager.coriolis.migrations.delete = \ - mock_migration - - self.migration.take_action(args) - - mock_migration.assert_called_once_with(mock.sentinel.id) - - -class ListMigrationTestCase(test_base.CoriolisBaseTestCase): - """Test suite for the Coriolis Client List Migration.""" - - def setUp(self): - self.mock_app = mock.Mock() - super(ListMigrationTestCase, self).setUp() - self.migration = migrations.ListMigration( - self.mock_app, mock.sentinel.app_args) - - @mock.patch.object(lister.Lister, 'get_parser') - def test_get_parser(self, mock_get_parser): - result = self.migration.get_parser(mock.sentinel.prog_name) - - self.assertEqual( - mock_get_parser.return_value, - result - ) - mock_get_parser.assert_called_once_with(mock.sentinel.prog_name) - - @mock.patch.object(migrations.MigrationFormatter, 'list_objects') - def test_take_action(self, mock_list_objects): - args = mock.Mock() - mock_migration = mock.Mock() - self.mock_app.client_manager.coriolis.migrations.list = \ - mock_migration - - result = self.migration.take_action(args) - - self.assertEqual( - mock_list_objects.return_value, - result - ) - mock_list_objects.assert_called_once_with(mock_migration.return_value) diff --git a/coriolisclient/v1/migrations.py b/coriolisclient/v1/migrations.py deleted file mode 100644 index e625abd..0000000 --- a/coriolisclient/v1/migrations.py +++ /dev/null @@ -1,125 +0,0 @@ -# Copyright (c) 2016 Cloudbase Solutions Srl -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from coriolisclient import base -from coriolisclient.v1 import common - - -class Migration(base.Resource): - _tasks = None - - @property - def source_environment(self): - source_env = self._info.get("source_environment") - if source_env is not None: - return common.SourceEnvironment(None, source_env, loaded=True) - - @property - def destination_environment(self): - dest_env = self._info.get("destination_environment") - if dest_env is not None: - return common.DestinationEnvironment(None, dest_env, loaded=True) - - @property - def transfer_result(self): - res = self._info.get("transfer_result") - if res is not None: - return common.TransferResult(None, res, loaded=True) - - @property - def tasks(self): - if self._info.get('tasks') is None: - self.get() - return [common.Task(None, d, loaded=True) for d in - self._info.get('tasks', [])] - - -class MigrationManager(base.BaseManager): - resource_class = Migration - - def __init__(self, api): - super(MigrationManager, self).__init__(api) - - def list(self, detail=False): - path = "/migrations" - if detail: - path = "%s/detail" % path - return self._list(path, 'migrations') - - def get(self, migration): - return self._get('/migrations/%s' % base.getid(migration), 'migration') - - def create(self, origin_endpoint_id, destination_endpoint_id, - source_environment, destination_environment, instances, - network_map=None, notes=None, storage_mappings=None, - skip_os_morphing=False, replication_count=None, - shutdown_instances=None, user_scripts=None, - origin_minion_pool_id=None, destination_minion_pool_id=None, - instance_osmorphing_minion_pool_mappings=None): - if not network_map: - network_map = destination_environment.get('network_map', {}) - if not storage_mappings: - storage_mappings = destination_environment.get( - 'storage_mappings', {}) - - data = { - "migration": { - "origin_endpoint_id": origin_endpoint_id, - "destination_endpoint_id": destination_endpoint_id, - "destination_environment": destination_environment, - "instances": instances, - "skip_os_morphing": skip_os_morphing, - "network_map": network_map, - "notes": notes, - "storage_mappings": storage_mappings, - "user_scripts": user_scripts}} - if source_environment is not None: - data['migration']['source_environment'] = source_environment - if shutdown_instances is not None: - data['migration']['shutdown_instances'] = shutdown_instances - if replication_count is not None: - data['migration']['replication_count'] = replication_count - if origin_minion_pool_id is not None: - data['migration']['origin_minion_pool_id'] = origin_minion_pool_id - if destination_minion_pool_id is not None: - data['migration']['destination_minion_pool_id'] = ( - destination_minion_pool_id) - if instance_osmorphing_minion_pool_mappings: - data['migration']['instance_osmorphing_minion_pool_mappings'] = ( - instance_osmorphing_minion_pool_mappings) - - return self._post('/migrations', data, 'migration') - - def create_from_replica(self, replica_id, clone_disks=True, force=False, - skip_os_morphing=False, user_scripts=None, - instance_osmorphing_minion_pool_mappings=None): - data = {"migration": { - "replica_id": replica_id, - "clone_disks": clone_disks, - "force": force, - "skip_os_morphing": skip_os_morphing, - "user_scripts": user_scripts}} - if instance_osmorphing_minion_pool_mappings is not None: - data['migration']['instance_osmorphing_minion_pool_mappings'] = ( - instance_osmorphing_minion_pool_mappings) - return self._post('/migrations', data, 'migration') - - def delete(self, migration): - return self._delete('/migrations/%s' % base.getid(migration)) - - def cancel(self, migration, force=False): - return self.client.post( - '/migrations/%s/actions' % base.getid(migration), - json={'cancel': {'force': force}}) From 45430cb355ec252f30746ee2a39f3e21f2687db9 Mon Sep 17 00:00:00 2001 From: Daniel Vincze Date: Thu, 8 Aug 2024 17:07:44 +0300 Subject: [PATCH 6/8] Fix existing unit tests --- coriolisclient/cli/deployments.py | 3 ++- coriolisclient/tests/cli/test_replicas.py | 6 ++++++ coriolisclient/v1/deployments.py | 3 ++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/coriolisclient/cli/deployments.py b/coriolisclient/cli/deployments.py index 0ef00f1..ac33550 100644 --- a/coriolisclient/cli/deployments.py +++ b/coriolisclient/cli/deployments.py @@ -254,7 +254,8 @@ def get_parser(self, prog_name): return parser def take_action(self, args): - self.app.client_manager.coriolis.deployments.cancel(args.id, args.force) + self.app.client_manager.coriolis.deployments.cancel( + args.id, args.force) class DeleteDeployment(command.Command): diff --git a/coriolisclient/tests/cli/test_replicas.py b/coriolisclient/tests/cli/test_replicas.py index 834da82..293d982 100644 --- a/coriolisclient/tests/cli/test_replicas.py +++ b/coriolisclient/tests/cli/test_replicas.py @@ -80,6 +80,7 @@ def test_get_formatted_data(self): obj.last_execution_status = mock.sentinel.last_execution_status obj.instances = ["mock_instance3", "mock_instance1", "mock_instance2"] obj.notes = mock.sentinel.notes + obj.scenario = mock.sentinel.scenario obj.created_at = mock.sentinel.created_at result = self.replica._get_formatted_data(obj) @@ -87,6 +88,7 @@ def test_get_formatted_data(self): self.assertEqual( ( mock.sentinel.id, + mock.sentinel.scenario, ('mock_instance3%(ls)smock_instance1%(ls)smock_instance2' % {"ls": "\n"}), mock.sentinel.notes, @@ -211,10 +213,12 @@ def test_get_formatted_data( mock_format_executions.return_value = \ mock.sentinel.formatted_executions mock_obj.info = mock.sentinel.info + mock_obj.scenario = mock.sentinel.scenario expected_result = [ mock.sentinel.id, mock.sentinel.created_at, mock.sentinel.updated_at, + mock.sentinel.scenario, mock.sentinel.reservation_id, mock.sentinel.formatted_instances, mock.sentinel.notes, @@ -281,6 +285,7 @@ def test_take_action( args = mock.Mock() args.instances = mock.sentinel.instances args.notes = mock.sentinel.notes + args.scenario = mock.sentinel.scenario args.origin_minion_pool_id = mock.sentinel.origin_minion_pool_id args.destination_minion_pool_id = \ mock.sentinel.destination_minion_pool_id @@ -315,6 +320,7 @@ def test_take_action( mock.sentinel.source_environment, mock.sentinel.destination_environment, mock.sentinel.instances, + mock.sentinel.scenario, network_map=mock.sentinel.network_map, notes=mock.sentinel.notes, storage_mappings=(mock_get_storage_mappings_dict_from_args. diff --git a/coriolisclient/v1/deployments.py b/coriolisclient/v1/deployments.py index 99990b2..8887921 100644 --- a/coriolisclient/v1/deployments.py +++ b/coriolisclient/v1/deployments.py @@ -59,7 +59,8 @@ def list(self, detail=False): return self._list(path, 'deployments') def get(self, deployment): - return self._get('/deployments/%s' % base.getid(deployment), 'deployment') + return self._get( + '/deployments/%s' % base.getid(deployment), 'deployment') def create_from_replica(self, replica_id, clone_disks=True, force=False, skip_os_morphing=False, user_scripts=None, From b5d4dc089dd8a95938528101d7ee0ffd38b3397c Mon Sep 17 00:00:00 2001 From: Daniel Vincze Date: Wed, 27 Nov 2024 19:08:47 +0200 Subject: [PATCH 7/8] Update transfer nomenclature --- coriolisclient/cli/deployments.py | 28 ++- ...a_executions.py => transfer_executions.py} | 87 ++++---- ...ica_schedules.py => transfer_schedules.py} | 90 ++++---- .../cli/{replicas.py => transfers.py} | 99 ++++----- coriolisclient/client.py | 14 +- ...cutions.py => test_transfer_executions.py} | 156 ++++++------- ...chedules.py => test_transfer_schedules.py} | 202 ++++++++--------- .../{test_replicas.py => test_transfers.py} | 180 +++++++-------- coriolisclient/tests/v1/test_migrations.py | 206 ------------------ .../tests/v1/test_replica_executions.py | 112 ---------- .../tests/v1/test_transfer_executions.py | 112 ++++++++++ ...chedules.py => test_transfer_schedules.py} | 68 +++--- .../{test_replicas.py => test_transfers.py} | 110 +++++----- coriolisclient/v1/deployments.py | 10 +- ...a_executions.py => transfer_executions.py} | 35 +-- ...ica_schedules.py => transfer_schedules.py} | 35 +-- .../v1/{replicas.py => transfers.py} | 52 ++--- samples/replica_samples.py | 22 +- setup.cfg | 43 ++-- 19 files changed, 726 insertions(+), 935 deletions(-) rename coriolisclient/cli/{replica_executions.py => transfer_executions.py} (60%) rename coriolisclient/cli/{replica_schedules.py => transfer_schedules.py} (76%) rename coriolisclient/cli/{replicas.py => transfers.py} (82%) rename coriolisclient/tests/cli/{test_replica_executions.py => test_transfer_executions.py} (67%) rename coriolisclient/tests/cli/{test_replica_schedules.py => test_transfer_schedules.py} (61%) rename coriolisclient/tests/cli/{test_replicas.py => test_transfers.py} (76%) delete mode 100644 coriolisclient/tests/v1/test_migrations.py delete mode 100644 coriolisclient/tests/v1/test_replica_executions.py create mode 100644 coriolisclient/tests/v1/test_transfer_executions.py rename coriolisclient/tests/v1/{test_replica_schedules.py => test_transfer_schedules.py} (51%) rename coriolisclient/tests/v1/{test_replicas.py => test_transfers.py} (58%) rename coriolisclient/v1/{replica_executions.py => transfer_executions.py} (59%) rename coriolisclient/v1/{replica_schedules.py => transfer_schedules.py} (68%) rename coriolisclient/v1/{replicas.py => transfers.py} (70%) diff --git a/coriolisclient/cli/deployments.py b/coriolisclient/cli/deployments.py index ac33550..31eb7ee 100644 --- a/coriolisclient/cli/deployments.py +++ b/coriolisclient/cli/deployments.py @@ -30,7 +30,7 @@ class DeploymentFormatter(formatter.EntityFormatter): columns = ("ID", - "Replica ID", + "Transfer ID", "Status", "Instances", "Notes", @@ -42,7 +42,7 @@ def _get_sorted_list(self, obj_list): def _get_formatted_data(self, obj): data = (obj.id, - obj.replica_id, + obj.transfer_id, obj.last_execution_status, "\n".join(obj.instances), obj.notes, @@ -59,8 +59,8 @@ def __init__(self, show_instances_data=False): "status", "created", "last_updated", - "replica_id", - "replica_scenario_type", + "transfer_id", + "transfer_scenario_type", "reservation_id", "instances", "notes", @@ -69,7 +69,6 @@ def __init__(self, show_instances_data=False): "destination_endpoint_id", "destination_minion_pool_id", "instance_osmorphing_minion_pool_mappings", - "replication_count", "shutdown_instances", "destination_environment", "source_environment", @@ -126,8 +125,8 @@ def _get_formatted_data(self, obj): obj.last_execution_status, obj.created_at, obj.updated_at, - obj.replica_id, - obj.replica_scenario_type, + obj.transfer_id, + obj.transfer_scenario_type, obj.reservation_id, self._format_instances(obj), obj.notes, @@ -137,7 +136,6 @@ def _get_formatted_data(self, obj): obj.destination_minion_pool_id, cli_utils.format_json_for_object_property( obj, 'instance_osmorphing_minion_pool_mappings'), - getattr(obj, 'replication_count', None), getattr(obj, 'shutdown_instances', False), cli_utils.format_json_for_object_property( obj, prop_name="destination_environment"), @@ -160,17 +158,17 @@ def _get_formatted_data(self, obj): class CreateDeployment(show.ShowOne): - """Start a new deployment from an existing replica""" + """Start a new deployment from an existing transfer""" def get_parser(self, prog_name): parser = super(CreateDeployment, self).get_parser(prog_name) - parser.add_argument('replica', - help='The ID of the replica to migrate') + parser.add_argument('transfer', + help='The ID of the transfer to migrate') parser.add_argument('--force', - help='Force the deployment in case of a replica ' + help='Force the deployment in case of a transfer ' 'with failed executions', action='store_true', default=False) parser.add_argument('--dont-clone-disks', - help='Retain the replica disks by cloning them', + help='Retain the transfer disks by cloning them', action='store_false', dest="clone_disks", default=True) parser.add_argument('--skip-os-morphing', @@ -211,8 +209,8 @@ def take_action(self, args): mp['instance_id']: mp['pool_id'] for mp in args.instance_osmorphing_minion_pool_mappings} - deployment = m.create_from_replica( - args.replica, + deployment = m.create_from_transfer( + args.transfer, args.clone_disks, args.force, args.skip_os_morphing, diff --git a/coriolisclient/cli/replica_executions.py b/coriolisclient/cli/transfer_executions.py similarity index 60% rename from coriolisclient/cli/replica_executions.py rename to coriolisclient/cli/transfer_executions.py index f102284..a585f84 100644 --- a/coriolisclient/cli/replica_executions.py +++ b/coriolisclient/cli/transfer_executions.py @@ -13,7 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. """ -Command-line interface sub-commands related to replicas. +Command-line interface sub-commands related to transfers. """ import os @@ -24,9 +24,9 @@ from coriolisclient.cli import formatter -class ReplicaExecutionFormatter(formatter.EntityFormatter): +class TransferExecutionFormatter(formatter.EntityFormatter): - columns = ("Replica ID", + columns = ("Transfer ID", "ID", "Status", "Created", @@ -44,10 +44,10 @@ def _get_formatted_data(self, obj): return data -class ReplicaExecutionDetailFormatter(formatter.EntityFormatter): +class TransferExecutionDetailFormatter(formatter.EntityFormatter): columns = ("id", - "replica_id", + "transfer_id", "status", "created", "last_updated", @@ -100,49 +100,50 @@ def _get_formatted_data(self, obj): return data -class CreateReplicaExecution(show.ShowOne): - """Start a replica execution""" +class CreateTransferExecution(show.ShowOne): + """Start a transfer execution""" def get_parser(self, prog_name): - parser = super(CreateReplicaExecution, self).get_parser(prog_name) - parser.add_argument('replica', - help='The ID of the replica to execute') + parser = super(CreateTransferExecution, self).get_parser(prog_name) + parser.add_argument('transfer', + help='The ID of the transfer to execute') parser.add_argument('--shutdown-instances', help='Shutdown instances before executing the ' - 'replica', action='store_true', + 'transfer', action='store_true', default=False) return parser def take_action(self, args): - execution = self.app.client_manager.coriolis.replica_executions.create( - args.replica, args.shutdown_instances) + execution = ( + self.app.client_manager.coriolis.transfer_executions.create( + args.transfer, args.shutdown_instances)) - return ReplicaExecutionDetailFormatter().get_formatted_entity( + return TransferExecutionDetailFormatter().get_formatted_entity( execution) -class ShowReplicaExecution(show.ShowOne): - """Show a replica execution""" +class ShowTransferExecution(show.ShowOne): + """Show a transfer execution""" def get_parser(self, prog_name): - parser = super(ShowReplicaExecution, self).get_parser(prog_name) - parser.add_argument('replica', help='The replica\'s id') - parser.add_argument('id', help='The replica execution\'s id') + parser = super(ShowTransferExecution, self).get_parser(prog_name) + parser.add_argument('transfer', help='The transfer\'s id') + parser.add_argument('id', help='The transfer execution\'s id') return parser def take_action(self, args): - execution = self.app.client_manager.coriolis.replica_executions.get( - args.replica, args.id) - return ReplicaExecutionDetailFormatter().get_formatted_entity( + execution = self.app.client_manager.coriolis.transfer_executions.get( + args.transfer, args.id) + return TransferExecutionDetailFormatter().get_formatted_entity( execution) -class CancelReplicaExecution(command.Command): - """Cancel a replica execution""" +class CancelTransferExecution(command.Command): + """Cancel a transfer execution""" def get_parser(self, prog_name): - parser = super(CancelReplicaExecution, self).get_parser(prog_name) - parser.add_argument('replica', help='The replica\'s id') - parser.add_argument('id', help='The replica execution\'s id') + parser = super(CancelTransferExecution, self).get_parser(prog_name) + parser.add_argument('transfer', help='The transfer\'s id') + parser.add_argument('id', help='The transfer execution\'s id') parser.add_argument('--force', help='Perform a forced termination of running ' 'tasks', action='store_true', @@ -150,33 +151,33 @@ def get_parser(self, prog_name): return parser def take_action(self, args): - self.app.client_manager.coriolis.replica_executions.cancel( - args.replica, args.id, args.force) + self.app.client_manager.coriolis.transfer_executions.cancel( + args.transfer, args.id, args.force) -class DeleteReplicaExecution(command.Command): - """Delete a replica execution""" +class DeleteTransferExecution(command.Command): + """Delete a transfer execution""" def get_parser(self, prog_name): - parser = super(DeleteReplicaExecution, self).get_parser(prog_name) - parser.add_argument('replica', help='The replica\'s id') - parser.add_argument('id', help='The replica execution\'s id') + parser = super(DeleteTransferExecution, self).get_parser(prog_name) + parser.add_argument('transfer', help='The transfer\'s id') + parser.add_argument('id', help='The transfer execution\'s id') return parser def take_action(self, args): - self.app.client_manager.coriolis.replica_executions.delete( - args.replica, args.id) + self.app.client_manager.coriolis.transfer_executions.delete( + args.transfer, args.id) -class ListReplicaExecution(lister.Lister): - """List replica executions""" +class ListTransferExecution(lister.Lister): + """List transfer executions""" def get_parser(self, prog_name): - parser = super(ListReplicaExecution, self).get_parser(prog_name) - parser.add_argument('replica', help='The replica\'s id') + parser = super(ListTransferExecution, self).get_parser(prog_name) + parser.add_argument('transfer', help='The transfer\'s id') return parser def take_action(self, args): - obj_list = self.app.client_manager.coriolis.replica_executions.list( - args.replica) - return ReplicaExecutionFormatter().list_objects(obj_list) + obj_list = self.app.client_manager.coriolis.transfer_executions.list( + args.transfer) + return TransferExecutionFormatter().list_objects(obj_list) diff --git a/coriolisclient/cli/replica_schedules.py b/coriolisclient/cli/transfer_schedules.py similarity index 76% rename from coriolisclient/cli/replica_schedules.py rename to coriolisclient/cli/transfer_schedules.py index 7287071..90f8a67 100644 --- a/coriolisclient/cli/replica_schedules.py +++ b/coriolisclient/cli/transfer_schedules.py @@ -13,7 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. """ -Command-line interface sub-commands related to replicas. +Command-line interface sub-commands related to transfers. """ import argparse @@ -42,9 +42,9 @@ def __call__(self, parser, namespace, value, option_string=None): setattr(namespace, self.dest, value) -class ReplicaScheduleFormatter(formatter.EntityFormatter): +class TransferScheduleFormatter(formatter.EntityFormatter): - columns = ("Replica ID", + columns = ("Transfer ID", "ID", "Schedule", "Created", @@ -55,7 +55,7 @@ def _get_sorted_list(self, obj_list): return sorted(obj_list, key=lambda o: o.created_at) def _get_formatted_data(self, obj): - data = (obj.replica_id, + data = (obj.transfer_id, obj.id, obj.schedule, obj.created_at, @@ -63,10 +63,10 @@ def _get_formatted_data(self, obj): return data -class ReplicaScheduleDetailFormatter(formatter.EntityFormatter): +class TransferScheduleDetailFormatter(formatter.EntityFormatter): columns = ("id", - "replica_id", + "transfer_id", "schedule", "created", "last_updated", @@ -76,7 +76,7 @@ class ReplicaScheduleDetailFormatter(formatter.EntityFormatter): def _get_formatted_data(self, obj): data = (obj.id, - obj.replica_id, + obj.transfer_id, obj.schedule, obj.created_at, obj.updated_at, @@ -86,12 +86,12 @@ def _get_formatted_data(self, obj): return data -class CreateReplicaSchedule(show.ShowOne): - """Start a replica schedule""" +class CreateTransferSchedule(show.ShowOne): + """Start a transfer schedule""" def get_parser(self, prog_name): - parser = super(CreateReplicaSchedule, self).get_parser(prog_name) - parser.add_argument('replica', - help='The ID of the replica') + parser = super(CreateTransferSchedule, self).get_parser(prog_name) + parser.add_argument('transfer', + help='The ID of the transfer') _add_schedule_group(parser) parser.add_argument('--expires-at', help='ISO8601 formatted date', @@ -113,35 +113,35 @@ def take_action(self, args): "Please provide at least one value in the Schedule group") exp = _parse_expiration_date(args.expires_at) - schedule = self.app.client_manager.coriolis.replica_schedules.create( - args.replica, parsed_schedule, + schedule = self.app.client_manager.coriolis.transfer_schedules.create( + args.transfer, parsed_schedule, args.disabled is False, exp, args.shutdown_instance) - return ReplicaScheduleDetailFormatter().get_formatted_entity( + return TransferScheduleDetailFormatter().get_formatted_entity( schedule) -class ShowReplicaSchedule(show.ShowOne): - """Show a replica schedule""" +class ShowTransferSchedule(show.ShowOne): + """Show a transfer schedule""" def get_parser(self, prog_name): - parser = super(ShowReplicaSchedule, self).get_parser(prog_name) - parser.add_argument('replica', help='The replica\'s id') - parser.add_argument('id', help='The replica schedule\'s id') + parser = super(ShowTransferSchedule, self).get_parser(prog_name) + parser.add_argument('transfer', help='The transfer\'s id') + parser.add_argument('id', help='The transfer schedule\'s id') return parser def take_action(self, args): - schedule = self.app.client_manager.coriolis.replica_schedules.get( - args.replica, args.id) - return ReplicaScheduleDetailFormatter().get_formatted_entity( + schedule = self.app.client_manager.coriolis.transfer_schedules.get( + args.transfer, args.id) + return TransferScheduleDetailFormatter().get_formatted_entity( schedule) -class UpdateReplicaSchedule(show.ShowOne): - """Updates a replica schedule""" +class UpdateTransferSchedule(show.ShowOne): + """Updates a transfer schedule""" def get_parser(self, prog_name): - parser = super(UpdateReplicaSchedule, self).get_parser(prog_name) - parser.add_argument('replica', help='The replica\'s id') - parser.add_argument('id', help='The replica schedule\'s id') + parser = super(UpdateTransferSchedule, self).get_parser(prog_name) + parser.add_argument('transfer', help='The transfer\'s id') + parser.add_argument('id', help='The transfer schedule\'s id') _add_schedule_group(parser) expires_parser = parser.add_mutually_exclusive_group(required=False) expires_parser.add_argument( @@ -199,33 +199,33 @@ def take_action(self, args): if args.enabled is not None: updated_values["enabled"] = args.enabled - schedule = self.app.client_manager.coriolis.replica_schedules.update( - args.replica, args.id, updated_values) + schedule = self.app.client_manager.coriolis.transfer_schedules.update( + args.transfer, args.id, updated_values) - return ReplicaScheduleDetailFormatter().get_formatted_entity( + return TransferScheduleDetailFormatter().get_formatted_entity( schedule) -class DeleteReplicaSchedule(command.Command): - """Delete a replica schedule""" +class DeleteTransferSchedule(command.Command): + """Delete a transfer schedule""" def get_parser(self, prog_name): - parser = super(DeleteReplicaSchedule, self).get_parser(prog_name) - parser.add_argument('replica', help='The replica\'s id') - parser.add_argument('id', help='The replica schedule\'s id') + parser = super(DeleteTransferSchedule, self).get_parser(prog_name) + parser.add_argument('transfer', help='The transfer\'s id') + parser.add_argument('id', help='The transfer schedule\'s id') return parser def take_action(self, args): - self.app.client_manager.coriolis.replica_schedules.delete( - args.replica, args.id) + self.app.client_manager.coriolis.transfer_schedules.delete( + args.transfer, args.id) -class ListReplicaSchedule(lister.Lister): - """List replica schedules""" +class ListTransferSchedule(lister.Lister): + """List transfer schedules""" def get_parser(self, prog_name): - parser = super(ListReplicaSchedule, self).get_parser(prog_name) - parser.add_argument('replica', help='The replica\'s id') + parser = super(ListTransferSchedule, self).get_parser(prog_name) + parser.add_argument('transfer', help='The transfer\'s id') parser.add_argument('--hide-expired', help='Hide expired schedules', action='store_true', @@ -233,9 +233,9 @@ def get_parser(self, prog_name): return parser def take_action(self, args): - obj_list = self.app.client_manager.coriolis.replica_schedules.list( - args.replica, hide_expired=args.hide_expired) - return ReplicaScheduleFormatter().list_objects(obj_list) + obj_list = self.app.client_manager.coriolis.transfer_schedules.list( + args.transfer, hide_expired=args.hide_expired) + return TransferScheduleFormatter().list_objects(obj_list) def _add_schedule_group(parser): diff --git a/coriolisclient/cli/replicas.py b/coriolisclient/cli/transfers.py similarity index 82% rename from coriolisclient/cli/replicas.py rename to coriolisclient/cli/transfers.py index a99d195..c8ede4c 100644 --- a/coriolisclient/cli/replicas.py +++ b/coriolisclient/cli/transfers.py @@ -14,7 +14,7 @@ # limitations under the License. """ -Command-line interface sub-commands related to replicas. +Command-line interface sub-commands related to transfers. """ import os @@ -24,15 +24,15 @@ from cliff import show from coriolisclient.cli import formatter -from coriolisclient.cli import replica_executions +from coriolisclient.cli import transfer_executions from coriolisclient.cli import utils as cli_utils -REPLICA_SCENARIO_REPLICA = "replica" -REPLICA_SCENARIO_LIVE_MIGRATION = "live_migration" +TRANSFER_SCENARIO_REPLICA = "replica" +TRANSFER_SCENARIO_LIVE_MIGRATION = "live_migration" -class ReplicaFormatter(formatter.EntityFormatter): +class TransferFormatter(formatter.EntityFormatter): columns = ("ID", "Scenario", @@ -62,7 +62,7 @@ def _get_formatted_data(self, obj): return data -class ReplicaDetailFormatter(formatter.EntityFormatter): +class TransferDetailFormatter(formatter.EntityFormatter): def __init__(self, show_instances_data=False): self.columns = [ @@ -137,10 +137,10 @@ def _get_formatted_data(self, obj): return data -class CreateReplica(show.ShowOne): - """Create a new replica""" +class CreateTransfer(show.ShowOne): + """Create a new transfer""" def get_parser(self, prog_name): - parser = super(CreateReplica, self).get_parser(prog_name) + parser = super(CreateTransfer, self).get_parser(prog_name) parser.add_argument('--origin-endpoint', required=True, help='The origin endpoint id') parser.add_argument('--destination-endpoint', required=True, @@ -148,23 +148,24 @@ def get_parser(self, prog_name): parser.add_argument('--instance', action='append', required=True, dest="instances", metavar="INSTANCE_IDENTIFIER", help='The identifier of a source instance to be ' - 'replicated. Can be specified multiple times') + 'transferred. Can be specified multiple ' + 'times') parser.add_argument('--scenario', dest="scenario", metavar="SCENARIO", choices=[ - REPLICA_SCENARIO_REPLICA, - REPLICA_SCENARIO_LIVE_MIGRATION], - default=REPLICA_SCENARIO_REPLICA, + TRANSFER_SCENARIO_REPLICA, + TRANSFER_SCENARIO_LIVE_MIGRATION], + default=TRANSFER_SCENARIO_REPLICA, help='The type of scenario to use when creating ' - 'the Replica. "replica" will create a ' + 'the Transfer. "replica" will create a ' 'monthly-billed Replica which can be ' 'executed and deployed as many times as ' 'desired, while "live_migration" will ' - 'create a Replica which can be synced ' + 'create a Transfer which can be synced ' 'as many times as needed but only ' 'deployed once.') parser.add_argument('--notes', dest='notes', - help='Notes about the replica') + help='Notes about the transfer') parser.add_argument('--user-script-global', action='append', required=False, dest="global_scripts", @@ -218,7 +219,7 @@ def take_action(self, args): user_scripts = cli_utils.compose_user_scripts( args.global_scripts, args.instance_scripts) - replica = self.app.client_manager.coriolis.replicas.create( + transfer = self.app.client_manager.coriolis.transfers.create( origin_endpoint_id, destination_endpoint_id, source_environment, @@ -234,15 +235,15 @@ def take_action(self, args): instance_osmorphing_minion_pool_mappings), user_scripts=user_scripts) - return ReplicaDetailFormatter().get_formatted_entity(replica) + return TransferDetailFormatter().get_formatted_entity(transfer) -class ShowReplica(show.ShowOne): - """Show a replica""" +class ShowTransfer(show.ShowOne): + """Show a transfer""" def get_parser(self, prog_name): - parser = super(ShowReplica, self).get_parser(prog_name) - parser.add_argument('id', help='The replica\'s id') + parser = super(ShowTransfer, self).get_parser(prog_name) + parser.add_argument('id', help='The transfer\'s id') parser.add_argument('--show-instances-data', action='store_true', help='Includes the instances data used for tasks ' 'execution, this is useful for troubleshooting', @@ -250,57 +251,57 @@ def get_parser(self, prog_name): return parser def take_action(self, args): - replica = self.app.client_manager.coriolis.replicas.get(args.id) - return ReplicaDetailFormatter( - args.show_instances_data).get_formatted_entity(replica) + transfer = self.app.client_manager.coriolis.transfers.get(args.id) + return TransferDetailFormatter( + args.show_instances_data).get_formatted_entity(transfer) -class DeleteReplica(command.Command): - """Delete a replica""" +class DeleteTransfer(command.Command): + """Delete a transfer""" def get_parser(self, prog_name): - parser = super(DeleteReplica, self).get_parser(prog_name) - parser.add_argument('id', help='The replica\'s id') + parser = super(DeleteTransfer, self).get_parser(prog_name) + parser.add_argument('id', help='The transfer\'s id') return parser def take_action(self, args): - self.app.client_manager.coriolis.replicas.delete(args.id) + self.app.client_manager.coriolis.transfers.delete(args.id) -class DeleteReplicaDisks(show.ShowOne): - """Delete replica target disks""" +class DeleteTransferDisks(show.ShowOne): + """Delete transfer target disks""" def get_parser(self, prog_name): - parser = super(DeleteReplicaDisks, self).get_parser(prog_name) - parser.add_argument('id', help='The replica\'s id') + parser = super(DeleteTransferDisks, self).get_parser(prog_name) + parser.add_argument('id', help='The transfer\'s id') return parser def take_action(self, args): - execution = self.app.client_manager.coriolis.replicas.delete_disks( + execution = self.app.client_manager.coriolis.transfers.delete_disks( args.id) - return replica_executions.ReplicaExecutionDetailFormatter( + return transfer_executions.TransferExecutionDetailFormatter( ).get_formatted_entity(execution) -class ListReplica(lister.Lister): - """List replicas""" +class ListTransfer(lister.Lister): + """List transfers""" def get_parser(self, prog_name): - parser = super(ListReplica, self).get_parser(prog_name) + parser = super(ListTransfer, self).get_parser(prog_name) return parser def take_action(self, args): - obj_list = self.app.client_manager.coriolis.replicas.list() - return ReplicaFormatter().list_objects(obj_list) + obj_list = self.app.client_manager.coriolis.transfers.list() + return TransferFormatter().list_objects(obj_list) -class UpdateReplica(show.ShowOne): - """Create a new replica""" +class UpdateTransfer(show.ShowOne): + """Create a new transfer""" def get_parser(self, prog_name): - parser = super(UpdateReplica, self).get_parser(prog_name) - parser.add_argument('id', help='The replica\'s id') + parser = super(UpdateTransfer, self).get_parser(prog_name) + parser.add_argument('id', help='The transfer\'s id') parser.add_argument('--notes', dest='notes', - help='Notes about the replica.') + help='Notes about the transfer.') parser.add_argument('--user-script-global', action='append', required=False, dest="global_scripts", @@ -375,10 +376,10 @@ def take_action(self, args): if not updated_properties: raise ValueError( "No options provided for update. Please run `coriolis help " - "replica update` for details on accepted parameters.") + "transfer update` for details on accepted parameters.") - execution = self.app.client_manager.coriolis.replicas.update( + execution = self.app.client_manager.coriolis.transfers.update( args.id, updated_properties) - return replica_executions.ReplicaExecutionDetailFormatter( + return transfer_executions.TransferExecutionDetailFormatter( ).get_formatted_entity(execution) diff --git a/coriolisclient/client.py b/coriolisclient/client.py index 95a1dfa..60ef840 100644 --- a/coriolisclient/client.py +++ b/coriolisclient/client.py @@ -35,10 +35,10 @@ from coriolisclient.v1 import minion_pools from coriolisclient.v1 import providers from coriolisclient.v1 import regions -from coriolisclient.v1 import replica_executions -from coriolisclient.v1 import replica_schedules -from coriolisclient.v1 import replicas from coriolisclient.v1 import services +from coriolisclient.v1 import transfer_executions +from coriolisclient.v1 import transfer_schedules +from coriolisclient.v1 import transfers LOG = logging.getLogger(__name__) @@ -87,11 +87,11 @@ def __init__(self, session=None, *args, **kwargs): self.deployments = deployments.DeploymentManager(httpclient) self.minion_pools = minion_pools.MinionPoolManager(httpclient) self.providers = providers.ProvidersManager(httpclient) - self.replicas = replicas.ReplicaManager(httpclient) - self.replica_schedules = replica_schedules.ReplicaScheduleManager( - httpclient) - self.replica_executions = replica_executions.ReplicaExecutionManager( + self.transfers = transfers.TransferManager(httpclient) + self.transfer_schedules = transfer_schedules.TransferScheduleManager( httpclient) + self.transfer_executions = ( + transfer_executions.TransferExecutionManager(httpclient)) self.regions = regions.RegionManager(httpclient) self.services = services.ServiceManager(httpclient) self.logging = coriolis_logging.CoriolisLogDownloadManager(httpclient) diff --git a/coriolisclient/tests/cli/test_replica_executions.py b/coriolisclient/tests/cli/test_transfer_executions.py similarity index 67% rename from coriolisclient/tests/cli/test_replica_executions.py rename to coriolisclient/tests/cli/test_transfer_executions.py index 1d85a27..3366846 100644 --- a/coriolisclient/tests/cli/test_replica_executions.py +++ b/coriolisclient/tests/cli/test_transfer_executions.py @@ -9,16 +9,16 @@ from cliff import show from coriolisclient.cli import formatter -from coriolisclient.cli import replica_executions +from coriolisclient.cli import transfer_executions from coriolisclient.tests import test_base -class ReplicaExecutionFormatterTestCase(test_base.CoriolisBaseTestCase): +class TransferExecutionFormatterTestCase(test_base.CoriolisBaseTestCase): """Test suite for the Coriolis Replic Execution Formatter.""" def setUp(self): - super(ReplicaExecutionFormatterTestCase, self).setUp() - self.replica = replica_executions.ReplicaExecutionFormatter() + super(TransferExecutionFormatterTestCase, self).setUp() + self.transfer = transfer_executions.TransferExecutionFormatter() def test_get_sorted_list(self): obj1 = mock.Mock() @@ -29,7 +29,7 @@ def test_get_sorted_list(self): obj3.created_at = "date3" obj_list = [obj2, obj1, obj3] - result = self.replica._get_sorted_list(obj_list) + result = self.transfer._get_sorted_list(obj_list) self.assertEqual( [obj1, obj2, obj3], @@ -43,7 +43,7 @@ def test_get_formatted_data(self): obj.status = mock.sentinel.status obj.created_at = mock.sentinel.created_at - result = self.replica._get_formatted_data(obj) + result = self.transfer._get_formatted_data(obj) self.assertEqual( ( @@ -56,12 +56,12 @@ def test_get_formatted_data(self): ) -class ReplicaExecutionDetailFormatterTestCase(test_base.CoriolisBaseTestCase): - """Test suite for the Coriolis Replica Execution Detail Formatter.""" +class TransferExecutionDetailFormatterTestCase(test_base.CoriolisBaseTestCase): + """Test suite for the Coriolis Transfer Execution Detail Formatter.""" def setUp(self): - super(ReplicaExecutionDetailFormatterTestCase, self).setUp() - self.replica = replica_executions.ReplicaExecutionDetailFormatter() + super(TransferExecutionDetailFormatterTestCase, self).setUp() + self.transfer = transfer_executions.TransferExecutionDetailFormatter() def test_format_instances(self): obj = mock.Mock() @@ -75,7 +75,7 @@ def test_format_instances(self): % {"ls": os.linesep} ) - result = self.replica._format_instances(obj) + result = self.transfer._format_instances(obj) self.assertEqual( expected_result, @@ -98,14 +98,14 @@ def test_format_progress_updates(self, mock_format_progress_update): 'date1 [30] None' % {"ls": os.linesep} ) - result = self.replica._format_progress_updates(task_dict) + result = self.transfer._format_progress_updates(task_dict) self.assertEqual( expected_result, result ) - @mock.patch.object(replica_executions.ReplicaExecutionDetailFormatter, + @mock.patch.object(transfer_executions.TransferExecutionDetailFormatter, '_format_progress_updates') def test_format_task(self, mock_format_progress_updates): mock_task = mock.Mock() @@ -134,14 +134,14 @@ def test_format_task(self, mock_format_progress_updates): 'date3 [20] message3%(ls)s' 'date1 [30] None' % {"ls": os.linesep}) - result = self.replica._format_task(mock_task) + result = self.transfer._format_task(mock_task) self.assertEqual( expected_result, result ) - @mock.patch.object(replica_executions.ReplicaExecutionDetailFormatter, + @mock.patch.object(transfer_executions.TransferExecutionDetailFormatter, '_format_progress_updates') def test_format_task_no_progress_updates( self, mock_format_progress_updates): @@ -166,14 +166,14 @@ def test_format_task_no_progress_updates( 'progress_updates:' % {"ls": os.linesep} ) - result = self.replica._format_task(mock_task) + result = self.transfer._format_task(mock_task) self.assertEqual( expected_result, result ) - @mock.patch.object(replica_executions.ReplicaExecutionDetailFormatter, + @mock.patch.object(transfer_executions.TransferExecutionDetailFormatter, '_format_task') def test_format_tasks(self, mock_format_task): obj = mock.Mock() @@ -181,16 +181,16 @@ def test_format_tasks(self, mock_format_task): mock_format_task.side_effect = ["task1", "task2"] expected_result = 'task1%(ls)s%(ls)stask2' % {"ls": os.linesep} - result = self.replica._format_tasks(obj) + result = self.transfer._format_tasks(obj) self.assertEqual( expected_result, result ) - @mock.patch.object(replica_executions.ReplicaExecutionDetailFormatter, + @mock.patch.object(transfer_executions.TransferExecutionDetailFormatter, '_format_tasks') - @mock.patch.object(replica_executions.ReplicaExecutionDetailFormatter, + @mock.patch.object(transfer_executions.TransferExecutionDetailFormatter, '_format_instances') def test_get_formatted_data( self, @@ -219,7 +219,7 @@ def test_get_formatted_data( mock.sentinel.formatted_tasks, ) - result = self.replica._get_formatted_data(mock_obj) + result = self.transfer._get_formatted_data(mock_obj) self.assertEqual( expected_result, @@ -227,13 +227,13 @@ def test_get_formatted_data( ) -class CreateReplicaExecutionTestCase(test_base.CoriolisBaseTestCase): - """Test suite for the Coriolis Create Replica Execution.""" +class CreateTransferExecutionTestCase(test_base.CoriolisBaseTestCase): + """Test suite for the Coriolis Create Transfer Execution.""" def setUp(self): self.mock_app = mock.Mock() - super(CreateReplicaExecutionTestCase, self).setUp() - self.replica = replica_executions.CreateReplicaExecution( + super(CreateTransferExecutionTestCase, self).setUp() + self.transfer = transfer_executions.CreateTransferExecution( self.mock_app, mock.sentinel.app_args) @mock.patch.object(show.ShowOne, 'get_parser') @@ -241,7 +241,7 @@ def test_get_parser( self, mock_get_parser ): - result = self.replica.get_parser(mock.sentinel.prog_name) + result = self.transfer.get_parser(mock.sentinel.prog_name) self.assertEqual( mock_get_parser.return_value, @@ -249,38 +249,38 @@ def test_get_parser( ) mock_get_parser.assert_called_once_with(mock.sentinel.prog_name) - @mock.patch.object(replica_executions.ReplicaExecutionDetailFormatter, + @mock.patch.object(transfer_executions.TransferExecutionDetailFormatter, 'get_formatted_entity') def test_take_action( self, mock_get_formatted_entity ): args = mock.Mock() - args.replica = mock.sentinel.replica + args.transfer = mock.sentinel.transfer args.shutdown_instances = mock.sentinel.shutdown_instances mock_execution = mock.Mock() - self.mock_app.client_manager.coriolis.replica_executions.create = \ + self.mock_app.client_manager.coriolis.transfer_executions.create = \ mock_execution - result = self.replica.take_action(args) + result = self.transfer.take_action(args) self.assertEqual( mock_get_formatted_entity.return_value, result ) mock_execution.assert_called_once_with( - mock.sentinel.replica, mock.sentinel.shutdown_instances) + mock.sentinel.transfer, mock.sentinel.shutdown_instances) mock_get_formatted_entity.assert_called_once_with( mock_execution.return_value) -class ShowReplicaExecutionTestCase(test_base.CoriolisBaseTestCase): - """Test suite for the Coriolis Client Show Replica Execution.""" +class ShowTransferExecutionTestCase(test_base.CoriolisBaseTestCase): + """Test suite for the Coriolis Client Show Transfer Execution.""" def setUp(self): self.mock_app = mock.Mock() - super(ShowReplicaExecutionTestCase, self).setUp() - self.replica = replica_executions.ShowReplicaExecution( + super(ShowTransferExecutionTestCase, self).setUp() + self.transfer = transfer_executions.ShowTransferExecution( self.mock_app, mock.sentinel.app_args) @mock.patch.object(show.ShowOne, 'get_parser') @@ -288,7 +288,7 @@ def test_get_parser( self, mock_get_parser ): - result = self.replica.get_parser(mock.sentinel.prog_name) + result = self.transfer.get_parser(mock.sentinel.prog_name) self.assertEqual( mock_get_parser.return_value, @@ -296,37 +296,37 @@ def test_get_parser( ) mock_get_parser.assert_called_once_with(mock.sentinel.prog_name) - @mock.patch.object(replica_executions.ReplicaExecutionDetailFormatter, + @mock.patch.object(transfer_executions.TransferExecutionDetailFormatter, 'get_formatted_entity') def test_take_action( self, mock_get_formatted_entity ): args = mock.Mock() - args.replica = mock.sentinel.replica + args.transfer = mock.sentinel.transfer args.id = mock.sentinel.id execution = mock.Mock() - self.mock_app.client_manager.coriolis.replica_executions.get = \ + self.mock_app.client_manager.coriolis.transfer_executions.get = \ execution - result = self.replica.take_action(args) + result = self.transfer.take_action(args) self.assertEqual( mock_get_formatted_entity.return_value, result ) - execution.assert_called_once_with(args.replica, args.id) + execution.assert_called_once_with(args.transfer, args.id) mock_get_formatted_entity.assert_called_once_with( execution.return_value) -class CancelReplicaExecutionTestCase(test_base.CoriolisBaseTestCase): - """Test suite for the Coriolis Client Cancel Replica Execution.""" +class CancelTransferExecutionTestCase(test_base.CoriolisBaseTestCase): + """Test suite for the Coriolis Client Cancel Transfer Execution.""" def setUp(self): self.mock_app = mock.Mock() - super(CancelReplicaExecutionTestCase, self).setUp() - self.replica = replica_executions.CancelReplicaExecution( + super(CancelTransferExecutionTestCase, self).setUp() + self.transfer = transfer_executions.CancelTransferExecution( self.mock_app, mock.sentinel.app_args) @mock.patch.object(command.Command, 'get_parser') @@ -334,7 +334,7 @@ def test_get_parser( self, mock_get_parser ): - result = self.replica.get_parser(mock.sentinel.prog_name) + result = self.transfer.get_parser(mock.sentinel.prog_name) self.assertEqual( mock_get_parser.return_value, @@ -344,26 +344,26 @@ def test_get_parser( def test_take_action(self): args = mock.Mock() - args.replica = mock.sentinel.replica + args.transfer = mock.sentinel.transfer args.id = mock.sentinel.id args.force = False - replica_execution = mock.Mock() - self.mock_app.client_manager.coriolis.replica_executions = \ - replica_execution + transfer_execution = mock.Mock() + self.mock_app.client_manager.coriolis.transfer_executions = \ + transfer_execution - self.replica.take_action(args) + self.transfer.take_action(args) - replica_execution.cancel.assert_called_once_with( - mock.sentinel.replica, mock.sentinel.id, False) + transfer_execution.cancel.assert_called_once_with( + mock.sentinel.transfer, mock.sentinel.id, False) -class DeleteReplicaExecutionTestCase(test_base.CoriolisBaseTestCase): - """Test suite for the Coriolis Client Delete Replica Execution.""" +class DeleteTransferExecutionTestCase(test_base.CoriolisBaseTestCase): + """Test suite for the Coriolis Client Delete Transfer Execution.""" def setUp(self): self.mock_app = mock.Mock() - super(DeleteReplicaExecutionTestCase, self).setUp() - self.replica = replica_executions.DeleteReplicaExecution( + super(DeleteTransferExecutionTestCase, self).setUp() + self.transfer = transfer_executions.DeleteTransferExecution( self.mock_app, mock.sentinel.app_args) @mock.patch.object(command.Command, 'get_parser') @@ -371,7 +371,7 @@ def test_get_parser( self, mock_get_parser ): - result = self.replica.get_parser(mock.sentinel.prog_name) + result = self.transfer.get_parser(mock.sentinel.prog_name) self.assertEqual( mock_get_parser.return_value, @@ -381,25 +381,25 @@ def test_get_parser( def test_take_action(self): args = mock.Mock() - args.replica = mock.sentinel.replica + args.transfer = mock.sentinel.transfer args.id = mock.sentinel.id - replica_execution = mock.Mock() - self.mock_app.client_manager.coriolis.replica_executions = \ - replica_execution + transfer_execution = mock.Mock() + self.mock_app.client_manager.coriolis.transfer_executions = \ + transfer_execution - self.replica.take_action(args) + self.transfer.take_action(args) - replica_execution.delete.assert_called_once_with( - mock.sentinel.replica, mock.sentinel.id) + transfer_execution.delete.assert_called_once_with( + mock.sentinel.transfer, mock.sentinel.id) -class ListReplicaExecutionTestCase(test_base.CoriolisBaseTestCase): - """Test suite for the Coriolis Client List Replica Execution.""" +class ListTransferExecutionTestCase(test_base.CoriolisBaseTestCase): + """Test suite for the Coriolis Client List Transfer Execution.""" def setUp(self): self.mock_app = mock.Mock() - super(ListReplicaExecutionTestCase, self).setUp() - self.replica = replica_executions.ListReplicaExecution( + super(ListTransferExecutionTestCase, self).setUp() + self.transfer = transfer_executions.ListTransferExecution( self.mock_app, mock.sentinel.app_args) @mock.patch.object(lister.Lister, 'get_parser') @@ -407,7 +407,7 @@ def test_get_parser( self, mock_get_parser ): - result = self.replica.get_parser(mock.sentinel.prog_name) + result = self.transfer.get_parser(mock.sentinel.prog_name) self.assertEqual( mock_get_parser.return_value, @@ -415,24 +415,24 @@ def test_get_parser( ) mock_get_parser.assert_called_once_with(mock.sentinel.prog_name) - @mock.patch.object(replica_executions.ReplicaExecutionFormatter, + @mock.patch.object(transfer_executions.TransferExecutionFormatter, 'list_objects') def test_take_action( self, mock_list_objects ): args = mock.Mock() - args.replica = mock.sentinel.replica - mock_replica_list = mock.Mock() - self.mock_app.client_manager.coriolis.replica_executions.list = \ - mock_replica_list + args.transfer = mock.sentinel.transfer + mock_transfer_list = mock.Mock() + self.mock_app.client_manager.coriolis.transfer_executions.list = \ + mock_transfer_list - result = self.replica.take_action(args) + result = self.transfer.take_action(args) self.assertEqual( mock_list_objects.return_value, result ) - mock_replica_list.assert_called_once_with(mock.sentinel.replica) + mock_transfer_list.assert_called_once_with(mock.sentinel.transfer) mock_list_objects.assert_called_once_with( - mock_replica_list.return_value) + mock_transfer_list.return_value) diff --git a/coriolisclient/tests/cli/test_replica_schedules.py b/coriolisclient/tests/cli/test_transfer_schedules.py similarity index 61% rename from coriolisclient/tests/cli/test_replica_schedules.py rename to coriolisclient/tests/cli/test_transfer_schedules.py index 926293c..25bb2b9 100644 --- a/coriolisclient/tests/cli/test_replica_schedules.py +++ b/coriolisclient/tests/cli/test_transfer_schedules.py @@ -9,17 +9,17 @@ from cliff import lister from cliff import show -from coriolisclient.cli import replica_schedules +from coriolisclient.cli import transfer_schedules from coriolisclient import exceptions from coriolisclient.tests import test_base class RangeActionTestCase(test_base.CoriolisBaseTestCase): - """Test suite for the Coriolis Replica Range Action.""" + """Test suite for the Coriolis Transfer Range Action.""" def setUp(self): super(RangeActionTestCase, self).setUp() - self.range_action = replica_schedules.RangeAction( + self.range_action = transfer_schedules.RangeAction( 1, 10, ["mock_option"], "dest") def test__call__(self): @@ -48,12 +48,12 @@ def test__call__argument_error(self): ) -class ReplicaScheduleFormatterTestCase(test_base.CoriolisBaseTestCase): - """Test suite for the Coriolis Replica Schedule Formatter.""" +class TransferScheduleFormatterTestCase(test_base.CoriolisBaseTestCase): + """Test suite for the Coriolis Transfer Schedule Formatter.""" def setUp(self): - super(ReplicaScheduleFormatterTestCase, self).setUp() - self.replica = replica_schedules.ReplicaScheduleFormatter() + super(TransferScheduleFormatterTestCase, self).setUp() + self.transfer = transfer_schedules.TransferScheduleFormatter() def test_get_sorted_list(self): obj1 = mock.Mock() @@ -64,7 +64,7 @@ def test_get_sorted_list(self): obj3.created_at = "date3" obj_list = [obj2, obj1, obj3] - result = self.replica._get_sorted_list(obj_list) + result = self.transfer._get_sorted_list(obj_list) self.assertEqual( [obj1, obj2, obj3], @@ -73,17 +73,17 @@ def test_get_sorted_list(self): def test_get_formatted_data(self): obj = mock.Mock() - obj.replica_id = mock.sentinel.replica_id + obj.transfer_id = mock.sentinel.transfer_id obj.id = mock.sentinel.id obj.schedule = mock.sentinel.schedule obj.created_at = mock.sentinel.created_at obj.expiration_date = mock.sentinel.expiration_date - result = self.replica._get_formatted_data(obj) + result = self.transfer._get_formatted_data(obj) self.assertEqual( ( - mock.sentinel.replica_id, + mock.sentinel.transfer_id, mock.sentinel.id, mock.sentinel.schedule, mock.sentinel.created_at, @@ -93,17 +93,17 @@ def test_get_formatted_data(self): ) -class ReplicaScheduleDetailFormatterTestCase(test_base.CoriolisBaseTestCase): - """Test suite for the Coriolis Replica Schedule Detail Formatter.""" +class TransferScheduleDetailFormatterTestCase(test_base.CoriolisBaseTestCase): + """Test suite for the Coriolis Transfer Schedule Detail Formatter.""" def setUp(self): - super(ReplicaScheduleDetailFormatterTestCase, self).setUp() - self.replica = replica_schedules.ReplicaScheduleDetailFormatter() + super(TransferScheduleDetailFormatterTestCase, self).setUp() + self.transfer = transfer_schedules.TransferScheduleDetailFormatter() def test_get_formatted_data(self): obj = mock.Mock() obj.id = mock.sentinel.id - obj.replica_id = mock.sentinel.replica_id + obj.transfer_id = mock.sentinel.transfer_id obj.schedule = mock.sentinel.schedule obj.created_at = mock.sentinel.created_at obj.updated_at = mock.sentinel.updated_at @@ -111,12 +111,12 @@ def test_get_formatted_data(self): obj.expiration_date = mock.sentinel.expiration_date obj.shutdown_instance = mock.sentinel.shutdown_instance - result = self.replica._get_formatted_data(obj) + result = self.transfer._get_formatted_data(obj) self.assertEqual( ( mock.sentinel.id, - mock.sentinel.replica_id, + mock.sentinel.transfer_id, mock.sentinel.schedule, mock.sentinel.created_at, mock.sentinel.updated_at, @@ -128,13 +128,13 @@ def test_get_formatted_data(self): ) -class CreateReplicaScheduleTestCase(test_base.CoriolisBaseTestCase): - """Test suite for the Coriolis Create Replica Schedule.""" +class CreateTransferScheduleTestCase(test_base.CoriolisBaseTestCase): + """Test suite for the Coriolis Create Transfer Schedule.""" def setUp(self): self.mock_app = mock.Mock() - super(CreateReplicaScheduleTestCase, self).setUp() - self.replica = replica_schedules.CreateReplicaSchedule( + super(CreateTransferScheduleTestCase, self).setUp() + self.transfer = transfer_schedules.CreateTransferSchedule( self.mock_app, mock.sentinel.app_args) @mock.patch.object(show.ShowOne, 'get_parser') @@ -142,7 +142,7 @@ def test_get_parser( self, mock_get_parser ): - result = self.replica.get_parser(mock.sentinel.prog_name) + result = self.transfer.get_parser(mock.sentinel.prog_name) self.assertEqual( mock_get_parser.return_value, @@ -150,10 +150,10 @@ def test_get_parser( ) mock_get_parser.assert_called_once_with(mock.sentinel.prog_name) - @mock.patch.object(replica_schedules.ReplicaScheduleDetailFormatter, + @mock.patch.object(transfer_schedules.TransferScheduleDetailFormatter, 'get_formatted_entity') - @mock.patch.object(replica_schedules, '_parse_expiration_date') - @mock.patch.object(replica_schedules, '_parse_schedule_group_args') + @mock.patch.object(transfer_schedules, '_parse_expiration_date') + @mock.patch.object(transfer_schedules, '_parse_schedule_group_args') def test_take_action( self, mock_parse_schedule_group_args, @@ -161,23 +161,23 @@ def test_take_action( mock_get_formatted_entity ): args = mock.Mock() - args.replica = mock.sentinel.replica + args.transfer = mock.sentinel.transfer args.disabled = False args.shutdown_instance = mock.sentinel.shutdown_instance mock_schedule_group_args = {"minute": mock.sentinel.minute} mock_parse_schedule_group_args.return_value = mock_schedule_group_args mock_schedule = mock.Mock() - self.mock_app.client_manager.coriolis.replica_schedules.create = \ + self.mock_app.client_manager.coriolis.transfer_schedules.create = \ mock_schedule - result = self.replica.take_action(args) + result = self.transfer.take_action(args) self.assertEqual( mock_get_formatted_entity.return_value, result ) mock_schedule.assert_called_once_with( - mock.sentinel.replica, + mock.sentinel.transfer, mock_schedule_group_args, True, mock_parse_expiration_date.return_value, @@ -186,8 +186,8 @@ def test_take_action( mock_get_formatted_entity.assert_called_once_with( mock_schedule.return_value) - @mock.patch.object(replica_schedules, '_parse_expiration_date') - @mock.patch.object(replica_schedules, '_parse_schedule_group_args') + @mock.patch.object(transfer_schedules, '_parse_expiration_date') + @mock.patch.object(transfer_schedules, '_parse_schedule_group_args') def test_take_action_no_parsed_schedule( self, mock_parse_schedule_group_args, @@ -198,19 +198,19 @@ def test_take_action_no_parsed_schedule( self.assertRaises( exceptions.CoriolisException, - self.replica.take_action, + self.transfer.take_action, args ) mock_parse_expiration_date.assert_not_called() -class ShowReplicaScheduleTestCase(test_base.CoriolisBaseTestCase): - """Test suite for the Coriolis Client Show Replica Schedule.""" +class ShowTransferScheduleTestCase(test_base.CoriolisBaseTestCase): + """Test suite for the Coriolis Client Show Transfer Schedule.""" def setUp(self): self.mock_app = mock.Mock() - super(ShowReplicaScheduleTestCase, self).setUp() - self.replica = replica_schedules.ShowReplicaSchedule( + super(ShowTransferScheduleTestCase, self).setUp() + self.transfer = transfer_schedules.ShowTransferSchedule( self.mock_app, mock.sentinel.app_args) @mock.patch.object(show.ShowOne, 'get_parser') @@ -218,7 +218,7 @@ def test_get_parser( self, mock_get_parser ): - result = self.replica.get_parser(mock.sentinel.prog_name) + result = self.transfer.get_parser(mock.sentinel.prog_name) self.assertEqual( mock_get_parser.return_value, @@ -226,37 +226,37 @@ def test_get_parser( ) mock_get_parser.assert_called_once_with(mock.sentinel.prog_name) - @mock.patch.object(replica_schedules.ReplicaScheduleDetailFormatter, + @mock.patch.object(transfer_schedules.TransferScheduleDetailFormatter, 'get_formatted_entity') def test_take_action( self, mock_get_formatted_entity ): args = mock.Mock() - args.replica = mock.sentinel.replica + args.transfer = mock.sentinel.transfer args.id = mock.sentinel.id schedule = mock.Mock() - self.mock_app.client_manager.coriolis.replica_schedules.get = \ + self.mock_app.client_manager.coriolis.transfer_schedules.get = \ schedule - result = self.replica.take_action(args) + result = self.transfer.take_action(args) self.assertEqual( mock_get_formatted_entity.return_value, result ) - schedule.assert_called_once_with(args.replica, args.id) + schedule.assert_called_once_with(args.transfer, args.id) mock_get_formatted_entity.assert_called_once_with( schedule.return_value) -class UpdateReplicaScheduleTestCase(test_base.CoriolisBaseTestCase): - """Test suite for the Coriolis Client Update Replica Schedule.""" +class UpdateTransferScheduleTestCase(test_base.CoriolisBaseTestCase): + """Test suite for the Coriolis Client Update Transfer Schedule.""" def setUp(self): self.mock_app = mock.Mock() - super(UpdateReplicaScheduleTestCase, self).setUp() - self.replica = replica_schedules.UpdateReplicaSchedule( + super(UpdateTransferScheduleTestCase, self).setUp() + self.transfer = transfer_schedules.UpdateTransferSchedule( self.mock_app, mock.sentinel.app_args) @mock.patch.object(show.ShowOne, 'get_parser') @@ -264,7 +264,7 @@ def test_get_parser( self, mock_get_parser ): - result = self.replica.get_parser(mock.sentinel.prog_name) + result = self.transfer.get_parser(mock.sentinel.prog_name) self.assertEqual( mock_get_parser.return_value, @@ -272,10 +272,10 @@ def test_get_parser( ) mock_get_parser.assert_called_once_with(mock.sentinel.prog_name) - @mock.patch.object(replica_schedules.ReplicaScheduleDetailFormatter, + @mock.patch.object(transfer_schedules.TransferScheduleDetailFormatter, 'get_formatted_entity') - @mock.patch.object(replica_schedules, '_parse_expiration_date') - @mock.patch.object(replica_schedules, '_parse_schedule_group_args') + @mock.patch.object(transfer_schedules, '_parse_expiration_date') + @mock.patch.object(transfer_schedules, '_parse_schedule_group_args') def test_take_action( self, mock_parse_schedule_group_args, @@ -286,13 +286,13 @@ def test_take_action( args.expires = True args.shutdown = True args.enabled = True - args.replica = mock.sentinel.replica + args.transfer = mock.sentinel.transfer args.id = mock.sentinel.id mock_parse_schedule_group_args.return_value = \ {"minute": mock.sentinel.minute} - replica_schedule = mock.Mock() - self.mock_app.client_manager.coriolis.replica_schedules = \ - replica_schedule + transfer_schedule = mock.Mock() + self.mock_app.client_manager.coriolis.transfer_schedules = \ + transfer_schedule expected_updated_values = { "schedule": {"minute": mock.sentinel.minute}, "expiration_date": mock_parse_expiration_date.return_value, @@ -300,23 +300,23 @@ def test_take_action( "enabled": True, } - result = self.replica.take_action(args) + result = self.transfer.take_action(args) self.assertEqual( mock_get_formatted_entity.return_value, result ) - replica_schedule.update.assert_called_once_with( - mock.sentinel.replica, + transfer_schedule.update.assert_called_once_with( + mock.sentinel.transfer, mock.sentinel.id, expected_updated_values ) - @mock.patch.object(replica_schedules.ReplicaScheduleDetailFormatter, + @mock.patch.object(transfer_schedules.TransferScheduleDetailFormatter, 'get_formatted_entity') - @mock.patch.object(replica_schedules, '_parse_expiration_date') - @mock.patch.object(replica_schedules, '_parse_schedule_group_args') + @mock.patch.object(transfer_schedules, '_parse_expiration_date') + @mock.patch.object(transfer_schedules, '_parse_schedule_group_args') def test_take_action_no_updated_values( self, mock_parse_schedule_group_args, @@ -324,18 +324,18 @@ def test_take_action_no_updated_values( mock_get_formatted_entity ): args = mock.Mock() - args.replica = mock.sentinel.replica + args.transfer = mock.sentinel.transfer args.id = mock.sentinel.id args.expires = False args.shutdown = None args.enabled = None mock_parse_schedule_group_args.return_value = {} - replica_schedule = mock.Mock() - self.mock_app.client_manager.coriolis.replica_schedules = \ - replica_schedule + transfer_schedule = mock.Mock() + self.mock_app.client_manager.coriolis.transfer_schedules = \ + transfer_schedule expected_updated_values = {"expiration_date": None} - result = self.replica.take_action(args) + result = self.transfer.take_action(args) self.assertEqual( mock_get_formatted_entity.return_value, @@ -343,20 +343,20 @@ def test_take_action_no_updated_values( ) mock_parse_expiration_date.assert_not_called() - replica_schedule.update.assert_called_once_with( - mock.sentinel.replica, + transfer_schedule.update.assert_called_once_with( + mock.sentinel.transfer, mock.sentinel.id, expected_updated_values ) -class DeleteReplicaScheduleTestCase(test_base.CoriolisBaseTestCase): - """Test suite for the Coriolis Client Delete Replica Schedule.""" +class DeleteTransferScheduleTestCase(test_base.CoriolisBaseTestCase): + """Test suite for the Coriolis Client Delete Transfer Schedule.""" def setUp(self): self.mock_app = mock.Mock() - super(DeleteReplicaScheduleTestCase, self).setUp() - self.replica = replica_schedules.DeleteReplicaSchedule( + super(DeleteTransferScheduleTestCase, self).setUp() + self.transfer = transfer_schedules.DeleteTransferSchedule( self.mock_app, mock.sentinel.app_args) @mock.patch.object(command.Command, 'get_parser') @@ -364,7 +364,7 @@ def test_get_parser( self, mock_get_parser ): - result = self.replica.get_parser(mock.sentinel.prog_name) + result = self.transfer.get_parser(mock.sentinel.prog_name) self.assertEqual( mock_get_parser.return_value, @@ -374,25 +374,25 @@ def test_get_parser( def test_take_action(self): args = mock.Mock() - args.replica = mock.sentinel.replica + args.transfer = mock.sentinel.transfer args.id = mock.sentinel.id - replica_schedule = mock.Mock() - self.mock_app.client_manager.coriolis.replica_schedules = \ - replica_schedule + transfer_schedule = mock.Mock() + self.mock_app.client_manager.coriolis.transfer_schedules = \ + transfer_schedule - self.replica.take_action(args) + self.transfer.take_action(args) - replica_schedule.delete.assert_called_once_with( - mock.sentinel.replica, mock.sentinel.id) + transfer_schedule.delete.assert_called_once_with( + mock.sentinel.transfer, mock.sentinel.id) -class ListReplicaScheduleTestCase(test_base.CoriolisBaseTestCase): - """Test suite for the Coriolis Client List Replica Schedule.""" +class ListTransferScheduleTestCase(test_base.CoriolisBaseTestCase): + """Test suite for the Coriolis Client List Transfer Schedule.""" def setUp(self): self.mock_app = mock.Mock() - super(ListReplicaScheduleTestCase, self).setUp() - self.replica = replica_schedules.ListReplicaSchedule( + super(ListTransferScheduleTestCase, self).setUp() + self.transfer = transfer_schedules.ListTransferSchedule( self.mock_app, mock.sentinel.app_args) @mock.patch.object(lister.Lister, 'get_parser') @@ -400,7 +400,7 @@ def test_get_parser( self, mock_get_parser ): - result = self.replica.get_parser(mock.sentinel.prog_name) + result = self.transfer.get_parser(mock.sentinel.prog_name) self.assertEqual( mock_get_parser.return_value, @@ -408,38 +408,38 @@ def test_get_parser( ) mock_get_parser.assert_called_once_with(mock.sentinel.prog_name) - @mock.patch.object(replica_schedules.ReplicaScheduleFormatter, + @mock.patch.object(transfer_schedules.TransferScheduleFormatter, 'list_objects') def test_take_action( self, mock_list_objects ): args = mock.Mock() - args.replica = mock.sentinel.replica + args.transfer = mock.sentinel.transfer args.hide_expired = mock.sentinel.hide_expired - mock_replica_list = mock.Mock() - self.mock_app.client_manager.coriolis.replica_schedules.list = \ - mock_replica_list + mock_transfer_list = mock.Mock() + self.mock_app.client_manager.coriolis.transfer_schedules.list = \ + mock_transfer_list - result = self.replica.take_action(args) + result = self.transfer.take_action(args) self.assertEqual( mock_list_objects.return_value, result ) - mock_replica_list.assert_called_once_with( - mock.sentinel.replica, hide_expired=mock.sentinel.hide_expired) + mock_transfer_list.assert_called_once_with( + mock.sentinel.transfer, hide_expired=mock.sentinel.hide_expired) mock_list_objects.assert_called_once_with( - mock_replica_list.return_value) + mock_transfer_list.return_value) -class ReplicaSchedulesTestCase(test_base.CoriolisBaseTestCase): - """Test suite for the Coriolis Replica Schedules.""" +class TransferSchedulesTestCase(test_base.CoriolisBaseTestCase): + """Test suite for the Coriolis Transfer Schedules.""" def test_add_schedule_group(self): parser = argparse.ArgumentParser() - replica_schedules._add_schedule_group(parser) + transfer_schedules._add_schedule_group(parser) self.assertIn( "Schedule", @@ -458,7 +458,7 @@ def __getattr__(self, name): "hour": mock.sentinel.hour } - result = replica_schedules._parse_schedule_group_args(args) + result = transfer_schedules._parse_schedule_group_args(args) self.assertEqual( expected_result, @@ -468,7 +468,7 @@ def __getattr__(self, name): def test_parse_expiration_date(self): value = None - result = replica_schedules._parse_expiration_date(value) + result = transfer_schedules._parse_expiration_date(value) self.assertEqual( None, @@ -477,7 +477,7 @@ def test_parse_expiration_date(self): value = "2099-12-31" - result = replica_schedules._parse_expiration_date(value) + result = transfer_schedules._parse_expiration_date(value) self.assertEqual( datetime.datetime(2099, 12, 31, 0, 0), @@ -488,6 +488,6 @@ def test_parse_expiration_date(self): self.assertRaises( exceptions.CoriolisException, - replica_schedules._parse_expiration_date, + transfer_schedules._parse_expiration_date, value ) diff --git a/coriolisclient/tests/cli/test_replicas.py b/coriolisclient/tests/cli/test_transfers.py similarity index 76% rename from coriolisclient/tests/cli/test_replicas.py rename to coriolisclient/tests/cli/test_transfers.py index 293d982..4e6b8d6 100644 --- a/coriolisclient/tests/cli/test_replicas.py +++ b/coriolisclient/tests/cli/test_transfers.py @@ -7,18 +7,18 @@ from cliff import lister from cliff import show -from coriolisclient.cli import replica_executions -from coriolisclient.cli import replicas +from coriolisclient.cli import transfer_executions +from coriolisclient.cli import transfers from coriolisclient.cli import utils as cli_utils from coriolisclient.tests import test_base -class ReplicaFormatterTestCase(test_base.CoriolisBaseTestCase): - """Test suite for the Coriolis Client Replica Formatter.""" +class TransferFormatterTestCase(test_base.CoriolisBaseTestCase): + """Test suite for the Coriolis Client Transfer Formatter.""" def setUp(self): - super(ReplicaFormatterTestCase, self).setUp() - self.replica = replicas.ReplicaFormatter() + super(TransferFormatterTestCase, self).setUp() + self.transfer = transfers.TransferFormatter() def test_get_sorted_list(self): obj1 = mock.Mock() @@ -29,7 +29,7 @@ def test_get_sorted_list(self): obj3.created_at = "date3" obj_list = [obj2, obj1, obj3] - result = self.replica._get_sorted_list(obj_list) + result = self.transfer._get_sorted_list(obj_list) self.assertEqual( [obj1, obj2, obj3], @@ -40,7 +40,7 @@ def test_format_last_execution(self): obj = mock.Mock() obj.executions = None - result = self.replica._format_last_execution(obj) + result = self.transfer._format_last_execution(obj) self.assertEqual( "", @@ -67,7 +67,7 @@ def test_format_last_execution(self): } obj.executions = [execution1, execution3, execution2] - result = self.replica._format_last_execution(obj) + result = self.transfer._format_last_execution(obj) self.assertEqual( "mock_id3 mock_status3", @@ -83,7 +83,7 @@ def test_get_formatted_data(self): obj.scenario = mock.sentinel.scenario obj.created_at = mock.sentinel.created_at - result = self.replica._get_formatted_data(obj) + result = self.transfer._get_formatted_data(obj) self.assertEqual( ( @@ -99,12 +99,12 @@ def test_get_formatted_data(self): ) -class ReplicaDetailFormatterTestCase(test_base.CoriolisBaseTestCase): - """Test suite for the Coriolis Client Replica Detail Formatter.""" +class TransferDetailFormatterTestCase(test_base.CoriolisBaseTestCase): + """Test suite for the Coriolis Client Transfer Detail Formatter.""" def setUp(self): - super(ReplicaDetailFormatterTestCase, self).setUp() - self.replica = replicas.ReplicaDetailFormatter( + super(TransferDetailFormatterTestCase, self).setUp() + self.transfer = transfers.TransferDetailFormatter( show_instances_data=True) def test_format_instances(self): @@ -115,7 +115,7 @@ def test_format_instances(self): % {"ls": "\n"} ) - result = self.replica._format_instances(obj) + result = self.transfer._format_instances(obj) self.assertEqual( expected_result, @@ -130,14 +130,14 @@ def test_format_execution(self): } expected_result = "mock_id mock_status" - result = self.replica._format_execution(execution) + result = self.transfer._format_execution(execution) self.assertEqual( expected_result, result ) - @mock.patch.object(replicas.ReplicaDetailFormatter, '_format_execution') + @mock.patch.object(transfers.TransferDetailFormatter, '_format_execution') def test_format_executions(self, mock_format_execution): executions = mock.Mock() execution1 = mock.Mock() @@ -158,17 +158,17 @@ def test_format_executions(self, mock_format_execution): "mock_id3 mock_status3" % {"ls": "\n"} ) - result = self.replica._format_executions(executions) + result = self.transfer._format_executions(executions) self.assertEqual( expected_result, result ) - @mock.patch.object(replicas.ReplicaDetailFormatter, '_format_executions') + @mock.patch.object(transfers.TransferDetailFormatter, '_format_executions') @mock.patch.object(cli_utils, 'format_mapping') @mock.patch.object(cli_utils, 'format_json_for_object_property') - @mock.patch.object(replicas.ReplicaDetailFormatter, + @mock.patch.object(transfers.TransferDetailFormatter, '_format_instances') @mock.patch.object(cli_utils, 'parse_storage_mappings') def test_get_formatted_data( @@ -238,7 +238,7 @@ def test_get_formatted_data( mock.sentinel.info ] - result = self.replica._get_formatted_data(mock_obj) + result = self.transfer._get_formatted_data(mock_obj) self.assertEqual( expected_result, @@ -246,13 +246,13 @@ def test_get_formatted_data( ) -class CreateReplicaTestCase(test_base.CoriolisBaseTestCase): - """Test suite for the Coriolis Client Create Replica.""" +class CreateTransferTestCase(test_base.CoriolisBaseTestCase): + """Test suite for the Coriolis Client Create Transfer.""" def setUp(self): self.mock_app = mock.Mock() - super(CreateReplicaTestCase, self).setUp() - self.replica = replicas.CreateReplica( + super(CreateTransferTestCase, self).setUp() + self.transfer = transfers.CreateTransfer( self.mock_app, mock.sentinel.app_args) @mock.patch.object(cli_utils, 'add_args_for_json_option_to_parser') @@ -262,7 +262,7 @@ def test_get_parser( mock_get_parser, *_ ): - result = self.replica.get_parser(mock.sentinel.prog_name) + result = self.transfer.get_parser(mock.sentinel.prog_name) self.assertEqual( mock_get_parser.return_value, @@ -273,7 +273,7 @@ def test_get_parser( @mock.patch.object(cli_utils, 'compose_user_scripts') @mock.patch.object(cli_utils, 'get_storage_mappings_dict_from_args') @mock.patch.object(cli_utils, 'get_option_value_from_args') - @mock.patch.object(replicas.ReplicaDetailFormatter, + @mock.patch.object(transfers.TransferDetailFormatter, 'get_formatted_entity') def test_take_action( self, @@ -294,27 +294,27 @@ def test_take_action( {'instance_id': "instance_id2", 'pool_id': "pool_id2"} ] mock_endpoints = mock.Mock() - mock_replicas = mock.Mock() + mock_transfers = mock.Mock() self.mock_app.client_manager.coriolis.endpoints = mock_endpoints mock_endpoints.get_endpoint_id_for_name.side_effect = [ mock.sentinel.origin_endpoint_id, mock.sentinel.destination_endpoint_id ] - self.mock_app.client_manager.coriolis.replicas.create = \ - mock_replicas + self.mock_app.client_manager.coriolis.transfers.create = \ + mock_transfers mock_get_option_value_from_args.side_effect = [ mock.sentinel.destination_environment, mock.sentinel.source_environment, mock.sentinel.network_map, ] - result = self.replica.take_action(args) + result = self.transfer.take_action(args) self.assertEqual( mock_get_formatted_entity.return_value, result ) - mock_replicas.assert_called_once_with( + mock_transfers.assert_called_once_with( mock.sentinel.origin_endpoint_id, mock.sentinel.destination_endpoint_id, mock.sentinel.source_environment, @@ -333,21 +333,21 @@ def test_take_action( user_scripts=mock_compose_user_scripts.return_value ) mock_get_formatted_entity.assert_called_once_with( - mock_replicas.return_value) + mock_transfers.return_value) -class ShowReplicaTestCase(test_base.CoriolisBaseTestCase): - """Test suite for the Coriolis Client Show Replica.""" +class ShowTransferTestCase(test_base.CoriolisBaseTestCase): + """Test suite for the Coriolis Client Show Transfer.""" def setUp(self): self.mock_app = mock.Mock() - super(ShowReplicaTestCase, self).setUp() - self.replica = replicas.ShowReplica( + super(ShowTransferTestCase, self).setUp() + self.transfer = transfers.ShowTransfer( self.mock_app, mock.sentinel.app_args) @mock.patch.object(show.ShowOne, 'get_parser') def test_get_parser(self, mock_get_parser): - result = self.replica.get_parser(mock.sentinel.prog_name) + result = self.transfer.get_parser(mock.sentinel.prog_name) self.assertEqual( mock_get_parser.return_value, @@ -355,37 +355,37 @@ def test_get_parser(self, mock_get_parser): ) mock_get_parser.assert_called_once_with(mock.sentinel.prog_name) - @mock.patch.object(replicas.ReplicaDetailFormatter, + @mock.patch.object(transfers.TransferDetailFormatter, 'get_formatted_entity') def test_take_action(self, mock_get_formatted_entity): args = mock.Mock() args.id = mock.sentinel.id - mock_replica = mock.Mock() - self.mock_app.client_manager.coriolis.replicas.get = mock_replica + mock_transfer = mock.Mock() + self.mock_app.client_manager.coriolis.transfers.get = mock_transfer - result = self.replica.take_action(args) + result = self.transfer.take_action(args) self.assertEqual( mock_get_formatted_entity.return_value, result ) - mock_replica.assert_called_once_with(mock.sentinel.id) + mock_transfer.assert_called_once_with(mock.sentinel.id) mock_get_formatted_entity.assert_called_once_with( - mock_replica.return_value) + mock_transfer.return_value) -class DeleteReplicaTestCase(test_base.CoriolisBaseTestCase): - """Test suite for the Coriolis Client Delete Replica.""" +class DeleteTransferTestCase(test_base.CoriolisBaseTestCase): + """Test suite for the Coriolis Client Delete Transfer.""" def setUp(self): self.mock_app = mock.Mock() - super(DeleteReplicaTestCase, self).setUp() - self.replica = replicas.DeleteReplica( + super(DeleteTransferTestCase, self).setUp() + self.transfer = transfers.DeleteTransfer( self.mock_app, mock.sentinel.app_args) @mock.patch.object(command.Command, 'get_parser') def test_get_parser(self, mock_get_parser): - result = self.replica.get_parser(mock.sentinel.prog_name) + result = self.transfer.get_parser(mock.sentinel.prog_name) self.assertEqual( mock_get_parser.return_value, @@ -396,26 +396,26 @@ def test_get_parser(self, mock_get_parser): def test_take_action(self): args = mock.Mock() args.id = mock.sentinel.id - mock_replica = mock.Mock() - self.mock_app.client_manager.coriolis.replicas.delete = mock_replica + mock_transfer = mock.Mock() + self.mock_app.client_manager.coriolis.transfers.delete = mock_transfer - self.replica.take_action(args) + self.transfer.take_action(args) - mock_replica.assert_called_once_with(mock.sentinel.id) + mock_transfer.assert_called_once_with(mock.sentinel.id) -class DeleteReplicaDisksTestCase(test_base.CoriolisBaseTestCase): - """Test suite for the Coriolis Client Delete Replica Disks.""" +class DeleteTransferDisksTestCase(test_base.CoriolisBaseTestCase): + """Test suite for the Coriolis Client Delete Transfer Disks.""" def setUp(self): self.mock_app = mock.Mock() - super(DeleteReplicaDisksTestCase, self).setUp() - self.replica = replicas.DeleteReplicaDisks( + super(DeleteTransferDisksTestCase, self).setUp() + self.transfer = transfers.DeleteTransferDisks( self.mock_app, mock.sentinel.app_args) @mock.patch.object(command.Command, 'get_parser') def test_get_parser(self, mock_get_parser): - result = self.replica.get_parser(mock.sentinel.prog_name) + result = self.transfer.get_parser(mock.sentinel.prog_name) self.assertEqual( mock_get_parser.return_value, @@ -423,38 +423,38 @@ def test_get_parser(self, mock_get_parser): ) mock_get_parser.assert_called_once_with(mock.sentinel.prog_name) - @mock.patch.object(replica_executions.ReplicaExecutionDetailFormatter, + @mock.patch.object(transfer_executions.TransferExecutionDetailFormatter, 'get_formatted_entity') def test_take_action(self, mock_get_formatted_entity): args = mock.Mock() args.id = mock.sentinel.id - mock_replica = mock.Mock() - self.mock_app.client_manager.coriolis.replicas.delete_disks = \ - mock_replica + mock_transfer = mock.Mock() + self.mock_app.client_manager.coriolis.transfers.delete_disks = \ + mock_transfer - result = self.replica.take_action(args) + result = self.transfer.take_action(args) self.assertEqual( mock_get_formatted_entity.return_value, result ) - mock_replica.assert_called_once_with(mock.sentinel.id) + mock_transfer.assert_called_once_with(mock.sentinel.id) mock_get_formatted_entity.assert_called_once_with( - mock_replica.return_value) + mock_transfer.return_value) -class ListReplicaTestCase(test_base.CoriolisBaseTestCase): - """Test suite for the Coriolis Client List Replica.""" +class ListTransferTestCase(test_base.CoriolisBaseTestCase): + """Test suite for the Coriolis Client List Transfer.""" def setUp(self): self.mock_app = mock.Mock() - super(ListReplicaTestCase, self).setUp() - self.replica = replicas.ListReplica( + super(ListTransferTestCase, self).setUp() + self.transfer = transfers.ListTransfer( self.mock_app, mock.sentinel.app_args) @mock.patch.object(lister.Lister, 'get_parser') def test_get_parser(self, mock_get_parser): - result = self.replica.get_parser(mock.sentinel.prog_name) + result = self.transfer.get_parser(mock.sentinel.prog_name) self.assertEqual( mock_get_parser.return_value, @@ -462,33 +462,33 @@ def test_get_parser(self, mock_get_parser): ) mock_get_parser.assert_called_once_with(mock.sentinel.prog_name) - @mock.patch.object(replicas.ReplicaFormatter, 'list_objects') + @mock.patch.object(transfers.TransferFormatter, 'list_objects') def test_take_action(self, mock_list_objects): args = mock.Mock() - mock_replica = mock.Mock() - self.mock_app.client_manager.coriolis.replicas.list = mock_replica + mock_transfer = mock.Mock() + self.mock_app.client_manager.coriolis.transfers.list = mock_transfer - result = self.replica.take_action(args) + result = self.transfer.take_action(args) self.assertEqual( mock_list_objects.return_value, result ) - mock_list_objects.assert_called_once_with(mock_replica.return_value) + mock_list_objects.assert_called_once_with(mock_transfer.return_value) -class UpdateReplicaTestCase(test_base.CoriolisBaseTestCase): - """Test suite for the Coriolis Client Delete Replica Disks.""" +class UpdateTransferTestCase(test_base.CoriolisBaseTestCase): + """Test suite for the Coriolis Client Delete Transfer Disks.""" def setUp(self): self.mock_app = mock.Mock() - super(UpdateReplicaTestCase, self).setUp() - self.replica = replicas.UpdateReplica( + super(UpdateTransferTestCase, self).setUp() + self.transfer = transfers.UpdateTransfer( self.mock_app, mock.sentinel.app_args) @mock.patch.object(show.ShowOne, 'get_parser') def test_get_parser(self, mock_get_parser): - result = self.replica.get_parser(mock.sentinel.prog_name) + result = self.transfer.get_parser(mock.sentinel.prog_name) self.assertEqual( mock_get_parser.return_value, @@ -496,7 +496,7 @@ def test_get_parser(self, mock_get_parser): ) mock_get_parser.assert_called_once_with(mock.sentinel.prog_name) - @mock.patch.object(replica_executions.ReplicaExecutionDetailFormatter, + @mock.patch.object(transfer_executions.TransferExecutionDetailFormatter, 'get_formatted_entity') @mock.patch.object(cli_utils, 'compose_user_scripts') @mock.patch.object(cli_utils, 'get_storage_mappings_dict_from_args') @@ -520,8 +520,8 @@ def test_take_action( {"instance_id": "mock_instance1", "pool_id": "mock_pool1"}, {"instance_id": "mock_instance2", "pool_id": "mock_pool2"} ] - mock_replica = mock.Mock() - self.mock_app.client_manager.coriolis.replicas = mock_replica + mock_transfer = mock.Mock() + self.mock_app.client_manager.coriolis.transfers = mock_transfer mock_get_option_value_from_args.side_effect = [ mock.sentinel.destination_environment, mock.sentinel.source_environment, @@ -544,7 +544,7 @@ def test_take_action( "user_scripts": mock.sentinel.user_scripts } - result = self.replica.take_action(args) + result = self.transfer.take_action(args) self.assertEqual( mock_get_formatted_entity.return_value, @@ -552,12 +552,12 @@ def test_take_action( ) mock_compose_user_scripts.assert_called_once_with( mock.sentinel.global_scripts, mock.sentinel.instance_scripts) - mock_replica.update.assert_called_once_with( + mock_transfer.update.assert_called_once_with( mock.sentinel.id, expected_updated_properties ) mock_get_formatted_entity.assert_called_once_with( - mock_replica.update.return_value) + mock_transfer.update.return_value) @mock.patch.object(cli_utils, 'compose_user_scripts') @mock.patch.object(cli_utils, 'get_storage_mappings_dict_from_args') @@ -574,18 +574,18 @@ def __getattr__(self, name): args = CustomMock() args.global_scripts = mock.sentinel.global_scripts args.instance_scripts = mock.sentinel.instance_scripts - mock_replica = mock.Mock() - self.mock_app.client_manager.coriolis.replicas = mock_replica + mock_transfer = mock.Mock() + self.mock_app.client_manager.coriolis.transfers = mock_transfer mock_get_option_value_from_args.return_value = None mock_get_storage_mappings_dict_from_args.return_value = None mock_compose_user_scripts.return_value = None self.assertRaises( ValueError, - self.replica.take_action, + self.transfer.take_action, args ) mock_compose_user_scripts.assert_called_once_with( mock.sentinel.global_scripts, mock.sentinel.instance_scripts) - mock_replica.update.assert_not_called() + mock_transfer.update.assert_not_called() diff --git a/coriolisclient/tests/v1/test_migrations.py b/coriolisclient/tests/v1/test_migrations.py deleted file mode 100644 index e139530..0000000 --- a/coriolisclient/tests/v1/test_migrations.py +++ /dev/null @@ -1,206 +0,0 @@ -# Copyright 2024 Cloudbase Solutions Srl -# All Rights Reserved. - -from unittest import mock - -from coriolisclient.tests import test_base -from coriolisclient.v1 import migrations - - -class MigrationTestCase(test_base.CoriolisBaseTestCase): - """Test suite for the Coriolis v1 Migration.""" - - def test_properties(self): - mock_client = mock.Mock() - self.migration = migrations.Migration( - mock_client, - { - "source_environment": { - "source_environment1": mock.sentinel.source_environment}, - "destination_environment": { - "destination_environment1": - mock.sentinel.destination_environment}, - "transfer_result": { - "transfer_result1": mock.sentinel.transfer_result}, - "tasks": [{"task1": mock.sentinel.task1}, - {"task2": mock.sentinel.task2}] - } - ) - self.assertEqual( - ( - mock.sentinel.source_environment, - mock.sentinel.destination_environment, - mock.sentinel.transfer_result, - mock.sentinel.task1, - mock.sentinel.task2 - ), - ( - self.migration.source_environment.source_environment1, - (self.migration.destination_environment. - destination_environment1), - self.migration.transfer_result.transfer_result1, - self.migration.tasks[0].task1, - self.migration.tasks[1].task2 - ) - ) - - @mock.patch.object(migrations.Migration, "get") - def test_properties_none(self, mock_get): - mock_client = mock.Mock() - self.migration = migrations.Migration( - mock_client, - {} - ) - self.assertEqual( - ( - None, - None, - None, - [] - ), - ( - self.migration.source_environment, - self.migration.destination_environment, - self.migration.transfer_result, - self.migration.tasks, - ) - ) - mock_get.assert_called_once() - - -class MigrationManagerTestCase(test_base.CoriolisBaseTestCase): - """Test suite for the Coriolis v1 Migration Manager.""" - - def setUp(self): - mock_client = mock.Mock() - super(MigrationManagerTestCase, self).setUp() - self.migration = migrations.MigrationManager(mock_client) - - @mock.patch.object(migrations.MigrationManager, "_list") - def test_list(self, mock_list): - result = self.migration.list(detail=True) - - self.assertEqual( - mock_list.return_value, - result - ) - mock_list.assert_called_once_with("/migrations/detail", "migrations") - - @mock.patch.object(migrations.MigrationManager, "_get") - def test_get(self, mock_get): - result = self.migration.get(mock.sentinel.migration) - - self.assertEqual( - mock_get.return_value, - result - ) - mock_get.assert_called_once_with( - "/migrations/sentinel.migration", "migration") - - @mock.patch.object(migrations.MigrationManager, "_post") - def test_create(self, mock_post): - expected_data = { - "origin_endpoint_id": mock.sentinel.origin_endpoint_id, - "destination_endpoint_id": mock.sentinel.destination_endpoint_id, - "source_environment": mock.sentinel.source_environment, - "destination_environment": { - "network_map": mock.sentinel.network_map, - "storage_mappings": mock.sentinel.storage_mappings - }, - "instances": mock.sentinel.instances, - "network_map": mock.sentinel.network_map, - "notes": mock.sentinel.notes, - "storage_mappings": mock.sentinel.storage_mappings, - "skip_os_morphing": False, - "replication_count": mock.sentinel.replication_count, - "shutdown_instances": mock.sentinel.shutdown_instances, - "user_scripts": mock.sentinel.user_scripts, - "origin_minion_pool_id": mock.sentinel.origin_minion_pool_id, - "destination_minion_pool_id": - mock.sentinel.destination_minion_pool_id, - "instance_osmorphing_minion_pool_mappings": - mock.sentinel.instance_osmorphing_minion_pool_mappings, - } - expected_data = {"migration": expected_data} - - result = self.migration.create( - mock.sentinel.origin_endpoint_id, - mock.sentinel.destination_endpoint_id, - mock.sentinel.source_environment, - { - "network_map": mock.sentinel.network_map, - "storage_mappings": mock.sentinel.storage_mappings - }, - mock.sentinel.instances, - network_map=None, - notes=mock.sentinel.notes, - storage_mappings=None, - skip_os_morphing=False, - replication_count=mock.sentinel.replication_count, - shutdown_instances=mock.sentinel.shutdown_instances, - user_scripts=mock.sentinel.user_scripts, - origin_minion_pool_id=mock.sentinel.origin_minion_pool_id, - destination_minion_pool_id= - mock.sentinel.destination_minion_pool_id, - instance_osmorphing_minion_pool_mappings= - mock.sentinel.instance_osmorphing_minion_pool_mappings, - ) - - self.assertEqual( - mock_post.return_value, - result - ) - mock_post.assert_called_once_with( - "/migrations", expected_data, "migration") - - @mock.patch.object(migrations.MigrationManager, "_post") - def test_create_from_replica(self, mock_post): - expected_data = { - "replica_id": mock.sentinel.replica_id, - "clone_disks": False, - "force": False, - "skip_os_morphing": False, - "user_scripts": mock.sentinel.user_scripts, - "instance_osmorphing_minion_pool_mappings": - mock.sentinel.instance_osmorphing_minion_pool_mappings, - } - expected_data = {"migration": expected_data} - - result = self.migration.create_from_replica( - mock.sentinel.replica_id, - clone_disks=False, - force=False, - skip_os_morphing=False, - user_scripts=mock.sentinel.user_scripts, - instance_osmorphing_minion_pool_mappings= - mock.sentinel.instance_osmorphing_minion_pool_mappings, - ) - - self.assertEqual( - mock_post.return_value, - result - ) - mock_post.assert_called_once_with( - "/migrations", expected_data, "migration") - - @mock.patch.object(migrations.MigrationManager, "_delete") - def test_delete(self, mock_delete): - result = self.migration.delete(mock.sentinel.migration) - - self.assertEqual( - mock_delete.return_value, - result - ) - mock_delete.assert_called_once_with( - "/migrations/%s" % mock.sentinel.migration) - - def test_cancel(self): - result = self.migration.cancel(mock.sentinel.migration, force=False) - - self.assertEqual( - self.migration.client.post.return_value, - result - ) - self.migration.client.post.assert_called_once_with( - "/migrations/%s/actions" % mock.sentinel.migration, - json={'cancel': {'force': False}}) diff --git a/coriolisclient/tests/v1/test_replica_executions.py b/coriolisclient/tests/v1/test_replica_executions.py deleted file mode 100644 index 53e3d95..0000000 --- a/coriolisclient/tests/v1/test_replica_executions.py +++ /dev/null @@ -1,112 +0,0 @@ -# Copyright 2024 Cloudbase Solutions Srl -# All Rights Reserved. - -from unittest import mock - -from coriolisclient.tests import test_base -from coriolisclient.v1 import replica_executions - - -class ReplicaExecutionTestCase(test_base.CoriolisBaseTestCase): - """Test suite for the Coriolis v1 Replica Execution.""" - - def test_tasks(self): - mock_client = mock.Mock() - self.replica_execution = replica_executions.ReplicaExecution( - mock_client, - { - "tasks": [{"task1": mock.sentinel.task1}, - {"task2": mock.sentinel.task2}], - } - ) - self.assertEqual( - ( - mock.sentinel.task1, - mock.sentinel.task2 - ), - ( - self.replica_execution.tasks[0].task1, - self.replica_execution.tasks[1].task2 - ) - ) - - -class ReplicaExecutionManagerTestCase(test_base.CoriolisBaseTestCase): - """Test suite for the Coriolis v1 Replica Execution Manager.""" - - def setUp(self): - mock_client = mock.Mock() - super(ReplicaExecutionManagerTestCase, self).setUp() - self.replica_execution = replica_executions.ReplicaExecutionManager( - mock_client) - - @mock.patch.object(replica_executions.ReplicaExecutionManager, "_list") - def test_list(self, mock_list): - result = self.replica_execution.list(mock.sentinel.replica) - - self.assertEqual( - mock_list.return_value, - result - ) - mock_list.assert_called_once_with( - '/replicas/%s/executions' % mock.sentinel.replica, "executions") - - @mock.patch.object(replica_executions.ReplicaExecutionManager, "_get") - def test_get(self, mock_get): - result = self.replica_execution.get( - mock.sentinel.replica, mock.sentinel.execution) - - self.assertEqual( - mock_get.return_value, - result - ) - mock_get.assert_called_once_with( - "/replicas/%s/executions/%s" % (mock.sentinel.replica, - mock.sentinel.execution), - "execution") - - @mock.patch.object(replica_executions.ReplicaExecutionManager, "_post") - def test_create(self, mock_post): - expected_data = { - "shutdown_instances": mock.sentinel.shutdown_instances - } - expected_data = {"execution": expected_data} - - result = self.replica_execution.create( - mock.sentinel.replica, - shutdown_instances=mock.sentinel.shutdown_instances - ) - - self.assertEqual( - mock_post.return_value, - result - ) - mock_post.assert_called_once_with( - '/replicas/%s/executions' % mock.sentinel.replica, - expected_data, "execution") - - @mock.patch.object(replica_executions.ReplicaExecutionManager, "_delete") - def test_delete(self, mock_delete): - result = self.replica_execution.delete( - mock.sentinel.replica, mock.sentinel.execution) - - self.assertEqual( - mock_delete.return_value, - result - ) - mock_delete.assert_called_once_with( - "/replicas/%s/executions/%s" % (mock.sentinel.replica, - mock.sentinel.execution),) - - def test_cancel(self): - result = self.replica_execution.cancel( - mock.sentinel.replica, mock.sentinel.execution, force=False) - - self.assertEqual( - self.replica_execution.client.post.return_value, - result - ) - self.replica_execution.client.post.assert_called_once_with( - "/replicas/%s/executions/%s/actions" % (mock.sentinel.replica, - mock.sentinel.execution), - json={'cancel': {'force': False}}) diff --git a/coriolisclient/tests/v1/test_transfer_executions.py b/coriolisclient/tests/v1/test_transfer_executions.py new file mode 100644 index 0000000..90c2044 --- /dev/null +++ b/coriolisclient/tests/v1/test_transfer_executions.py @@ -0,0 +1,112 @@ +# Copyright 2024 Cloudbase Solutions Srl +# All Rights Reserved. + +from unittest import mock + +from coriolisclient.tests import test_base +from coriolisclient.v1 import transfer_executions + + +class TransferExecutionTestCase(test_base.CoriolisBaseTestCase): + """Test suite for the Coriolis v1 Transfer Execution.""" + + def test_tasks(self): + mock_client = mock.Mock() + self.transfer_execution = transfer_executions.TransferExecution( + mock_client, + { + "tasks": [{"task1": mock.sentinel.task1}, + {"task2": mock.sentinel.task2}], + } + ) + self.assertEqual( + ( + mock.sentinel.task1, + mock.sentinel.task2 + ), + ( + self.transfer_execution.tasks[0].task1, + self.transfer_execution.tasks[1].task2 + ) + ) + + +class TransferExecutionManagerTestCase(test_base.CoriolisBaseTestCase): + """Test suite for the Coriolis v1 Transfer Execution Manager.""" + + def setUp(self): + mock_client = mock.Mock() + super(TransferExecutionManagerTestCase, self).setUp() + self.transfer_execution = transfer_executions.TransferExecutionManager( + mock_client) + + @mock.patch.object(transfer_executions.TransferExecutionManager, "_list") + def test_list(self, mock_list): + result = self.transfer_execution.list(mock.sentinel.transfer) + + self.assertEqual( + mock_list.return_value, + result + ) + mock_list.assert_called_once_with( + '/transfers/%s/executions' % mock.sentinel.transfer, "executions") + + @mock.patch.object(transfer_executions.TransferExecutionManager, "_get") + def test_get(self, mock_get): + result = self.transfer_execution.get( + mock.sentinel.transfer, mock.sentinel.execution) + + self.assertEqual( + mock_get.return_value, + result + ) + mock_get.assert_called_once_with( + "/transfers/%s/executions/%s" % (mock.sentinel.transfer, + mock.sentinel.execution), + "execution") + + @mock.patch.object(transfer_executions.TransferExecutionManager, "_post") + def test_create(self, mock_post): + expected_data = { + "shutdown_instances": mock.sentinel.shutdown_instances + } + expected_data = {"execution": expected_data} + + result = self.transfer_execution.create( + mock.sentinel.transfer, + shutdown_instances=mock.sentinel.shutdown_instances + ) + + self.assertEqual( + mock_post.return_value, + result + ) + mock_post.assert_called_once_with( + '/transfers/%s/executions' % mock.sentinel.transfer, + expected_data, "execution") + + @mock.patch.object(transfer_executions.TransferExecutionManager, "_delete") + def test_delete(self, mock_delete): + result = self.transfer_execution.delete( + mock.sentinel.transfer, mock.sentinel.execution) + + self.assertEqual( + mock_delete.return_value, + result + ) + mock_delete.assert_called_once_with( + "/transfers/%s/executions/%s" % (mock.sentinel.transfer, + mock.sentinel.execution)) + + def test_cancel(self): + result = self.transfer_execution.cancel( + mock.sentinel.transfer, mock.sentinel.execution, force=False) + + self.assertEqual( + self.transfer_execution.client.post.return_value, + result + ) + self.transfer_execution.client.post.assert_called_once_with( + "/transfers/%s/executions/%s/actions" % (mock.sentinel.transfer, + mock.sentinel.execution), + json={'cancel': {'force': False}}) diff --git a/coriolisclient/tests/v1/test_replica_schedules.py b/coriolisclient/tests/v1/test_transfer_schedules.py similarity index 51% rename from coriolisclient/tests/v1/test_replica_schedules.py rename to coriolisclient/tests/v1/test_transfer_schedules.py index 6275b6d..b5dbd7d 100644 --- a/coriolisclient/tests/v1/test_replica_schedules.py +++ b/coriolisclient/tests/v1/test_transfer_schedules.py @@ -5,58 +5,58 @@ from unittest import mock from coriolisclient.tests import test_base -from coriolisclient.v1 import replica_schedules +from coriolisclient.v1 import transfer_schedules -class ReplicaScheduleManagerTestCase(test_base.CoriolisBaseTestCase): - """Test suite for the Coriolis v1 Replica Schedule Manager.""" +class TransferScheduleManagerTestCase(test_base.CoriolisBaseTestCase): + """Test suite for the Coriolis v1 Transfer Schedule Manager.""" def setUp(self): mock_client = mock.Mock() - super(ReplicaScheduleManagerTestCase, self).setUp() - self.replica_schedule = replica_schedules.ReplicaScheduleManager( + super(TransferScheduleManagerTestCase, self).setUp() + self.transfer_schedule = transfer_schedules.TransferScheduleManager( mock_client) - @mock.patch.object(replica_schedules.ReplicaScheduleManager, "_list") + @mock.patch.object(transfer_schedules.TransferScheduleManager, "_list") def test_list(self, mock_list): - result = self.replica_schedule.list( - mock.sentinel.replica, hide_expired=False) + result = self.transfer_schedule.list( + mock.sentinel.transfer, hide_expired=False) self.assertEqual( mock_list.return_value, result ) mock_list.assert_called_once_with( - '/replicas/%s/schedules' % mock.sentinel.replica, "schedules") + '/transfers/%s/schedules' % mock.sentinel.transfer, "schedules") - @mock.patch.object(replica_schedules.ReplicaScheduleManager, "_list") + @mock.patch.object(transfer_schedules.TransferScheduleManager, "_list") def test_list_hide_expired(self, mock_list): - result = self.replica_schedule.list( - mock.sentinel.replica, hide_expired=True) + result = self.transfer_schedule.list( + mock.sentinel.transfer, hide_expired=True) self.assertEqual( mock_list.return_value, result ) mock_list.assert_called_once_with( - '/replicas/%s/schedules?show_expired=False' - % mock.sentinel.replica, "schedules") + '/transfers/%s/schedules?show_expired=False' + % mock.sentinel.transfer, "schedules") - @mock.patch.object(replica_schedules.ReplicaScheduleManager, "_get") + @mock.patch.object(transfer_schedules.TransferScheduleManager, "_get") def test_get(self, mock_get): - result = self.replica_schedule.get( - mock.sentinel.replica, mock.sentinel.schedule) + result = self.transfer_schedule.get( + mock.sentinel.transfer, mock.sentinel.schedule) self.assertEqual( mock_get.return_value, result ) mock_get.assert_called_once_with( - "/replicas/%s/schedules/%s" % (mock.sentinel.replica, - mock.sentinel.schedule), + "/transfers/%s/schedules/%s" % (mock.sentinel.transfer, + mock.sentinel.schedule), "schedule") - @mock.patch.object(replica_schedules.ReplicaScheduleManager, "_post") + @mock.patch.object(transfer_schedules.TransferScheduleManager, "_post") def test_create(self, mock_post): expiration_date = datetime.datetime.fromisoformat("2034-11-26") expected_data = { @@ -66,8 +66,8 @@ def test_create(self, mock_post): "shutdown_instance": mock.sentinel.shutdown_instance } - result = self.replica_schedule.create( - mock.sentinel.replica, + result = self.transfer_schedule.create( + mock.sentinel.transfer, mock.sentinel.schedule, True, expiration_date, @@ -79,15 +79,15 @@ def test_create(self, mock_post): result ) mock_post.assert_called_once_with( - '/replicas/%s/schedules' % mock.sentinel.replica, + '/transfers/%s/schedules' % mock.sentinel.transfer, expected_data, "schedule") - @mock.patch.object(replica_schedules.ReplicaScheduleManager, "_put") + @mock.patch.object(transfer_schedules.TransferScheduleManager, "_put") def test_update(self, mock_put): expiration_date = datetime.datetime.fromisoformat("2034-11-26") updated_values = {"expiration_date": expiration_date} - result = self.replica_schedule.update( - mock.sentinel.replica_id, + result = self.transfer_schedule.update( + mock.sentinel.transfer_id, mock.sentinel.schedule_id, updated_values ) @@ -97,28 +97,28 @@ def test_update(self, mock_put): result ) mock_put.assert_called_once_with( - "/replicas/%s/schedules/%s" % (mock.sentinel.replica_id, - mock.sentinel.schedule_id), + "/transfers/%s/schedules/%s" % (mock.sentinel.transfer_id, + mock.sentinel.schedule_id), {"expiration_date": '2034-11-26T00:00:00Z'}, "schedule") - @mock.patch.object(replica_schedules.ReplicaScheduleManager, "_delete") + @mock.patch.object(transfer_schedules.TransferScheduleManager, "_delete") def test_delete(self, mock_delete): - result = self.replica_schedule.delete( - mock.sentinel.replica, mock.sentinel.schedule) + result = self.transfer_schedule.delete( + mock.sentinel.transfer, mock.sentinel.schedule) self.assertEqual( mock_delete.return_value, result ) mock_delete.assert_called_once_with( - "/replicas/%s/schedules/%s" % (mock.sentinel.replica, - mock.sentinel.schedule),) + "/transfers/%s/schedules/%s" % (mock.sentinel.transfer, + mock.sentinel.schedule),) def test_format_rfc3339_datetime(self): dt = datetime.date(2024, 1, 1) - result = self.replica_schedule._format_rfc3339_datetime(dt) + result = self.transfer_schedule._format_rfc3339_datetime(dt) self.assertEqual( "2024-01-01Z", diff --git a/coriolisclient/tests/v1/test_replicas.py b/coriolisclient/tests/v1/test_transfers.py similarity index 58% rename from coriolisclient/tests/v1/test_replicas.py rename to coriolisclient/tests/v1/test_transfers.py index d8f92c0..9d76646 100644 --- a/coriolisclient/tests/v1/test_replicas.py +++ b/coriolisclient/tests/v1/test_transfers.py @@ -4,17 +4,17 @@ from unittest import mock from coriolisclient.tests import test_base -from coriolisclient.v1 import replica_executions -from coriolisclient.v1 import replicas +from coriolisclient.v1 import transfer_executions +from coriolisclient.v1 import transfers -class ReplicaTestCase(test_base.CoriolisBaseTestCase): - """Test suite for the Coriolis v1 Replica.""" +class TransferTestCase(test_base.CoriolisBaseTestCase): + """Test suite for the Coriolis v1 Transfer.""" - @mock.patch.object(replicas.Replica, "get") + @mock.patch.object(transfers.Transfer, "get") def test_properties(self, mock_get): mock_client = mock.Mock() - self.replica = replicas.Replica( + self.transfer = transfers.Transfer( mock_client, { "source_environment": { @@ -38,18 +38,18 @@ def test_properties(self, mock_get): mock.sentinel.execution2 ), ( - self.replica.source_environment.source_environment1, - self.replica.destination_environment.destination_environment1, - self.replica.executions[0].execution1, - self.replica.executions[1].execution2, + self.transfer.source_environment.source_environment1, + self.transfer.destination_environment.destination_environment1, + self.transfer.executions[0].execution1, + self.transfer.executions[1].execution2, ) ) mock_get.assert_not_called() - @mock.patch.object(replicas.Replica, "get") + @mock.patch.object(transfers.Transfer, "get") def test_properties_none(self, mock_get): mock_client = mock.Mock() - self.replica = replicas.Replica( + self.transfer = transfers.Transfer( mock_client, {} ) @@ -61,54 +61,54 @@ def test_properties_none(self, mock_get): [] ), ( - self.replica.source_environment, - self.replica.destination_environment, - self.replica.executions + self.transfer.source_environment, + self.transfer.destination_environment, + self.transfer.executions ) ) mock_get.assert_called_once() -class ReplicaManagerTestCase(test_base.CoriolisBaseTestCase): - """Test suite for the Coriolis v1 Replica Manager.""" +class TransferManagerTestCase(test_base.CoriolisBaseTestCase): + """Test suite for the Coriolis v1 Transfer Manager.""" def setUp(self): mock_client = mock.Mock() - super(ReplicaManagerTestCase, self).setUp() - self.replica = replicas.ReplicaManager(mock_client) + super(TransferManagerTestCase, self).setUp() + self.transfer = transfers.TransferManager(mock_client) - @mock.patch.object(replicas.ReplicaManager, "_list") + @mock.patch.object(transfers.TransferManager, "_list") def test_list(self, mock_list): - result = self.replica.list(detail=False) + result = self.transfer.list(detail=False) self.assertEqual( mock_list.return_value, result ) - mock_list.assert_called_once_with("/replicas", "replicas") + mock_list.assert_called_once_with("/transfers", "transfers") - @mock.patch.object(replicas.ReplicaManager, "_list") + @mock.patch.object(transfers.TransferManager, "_list") def test_list_details(self, mock_list): - result = self.replica.list(detail=True) + result = self.transfer.list(detail=True) self.assertEqual( mock_list.return_value, result ) - mock_list.assert_called_once_with("/replicas/detail", "replicas") + mock_list.assert_called_once_with("/transfers/detail", "transfers") - @mock.patch.object(replicas.ReplicaManager, "_get") + @mock.patch.object(transfers.TransferManager, "_get") def test_get(self, mock_get): - result = self.replica.get(mock.sentinel.replica) + result = self.transfer.get(mock.sentinel.transfer) self.assertEqual( mock_get.return_value, result ) mock_get.assert_called_once_with( - "/replicas/%s" % (mock.sentinel.replica), "replica") + "/transfers/%s" % mock.sentinel.transfer, "transfer") - @mock.patch.object(replicas.ReplicaManager, "_post") + @mock.patch.object(transfers.TransferManager, "_post") def test_create(self, mock_post): expected_data = { "origin_endpoint_id": mock.sentinel.origin_endpoint_id, @@ -119,6 +119,7 @@ def test_create(self, mock_post): "storage_mappings": mock.sentinel.storage_mappings }, "instances": mock.sentinel.instances, + "scenario": mock.sentinel.scenario, "network_map": mock.sentinel.network_map, "notes": mock.sentinel.notes, "storage_mappings": mock.sentinel.storage_mappings, @@ -129,9 +130,9 @@ def test_create(self, mock_post): "instance_osmorphing_minion_pool_mappings": mock.sentinel.instance_osmorphing_minion_pool_mappings, } - expected_data = {"replica": expected_data} + expected_data = {"transfer": expected_data} - result = self.replica.create( + result = self.transfer.create( mock.sentinel.origin_endpoint_id, mock.sentinel.destination_endpoint_id, mock.sentinel.source_environment, @@ -140,6 +141,7 @@ def test_create(self, mock_post): "storage_mappings": mock.sentinel.storage_mappings }, mock.sentinel.instances, + mock.sentinel.scenario, network_map=None, notes=mock.sentinel.notes, storage_mappings=None, @@ -156,52 +158,52 @@ def test_create(self, mock_post): result ) mock_post.assert_called_once_with( - "/replicas", expected_data, "replica") + "/transfers", expected_data, "transfer") - @mock.patch.object(replicas.ReplicaManager, "_delete") + @mock.patch.object(transfers.TransferManager, "_delete") def test_delete(self, mock_delete): - result = self.replica.delete(mock.sentinel.replica) + result = self.transfer.delete(mock.sentinel.transfer) self.assertEqual( mock_delete.return_value, result ) mock_delete.assert_called_once_with( - "/replicas/%s" % mock.sentinel.replica) + "/transfers/%s" % mock.sentinel.transfer) - @mock.patch.object(replica_executions, "ReplicaExecution") - def test_delete_disks(self, mock_ReplicaExecution): - result = self.replica.delete_disks(mock.sentinel.replica) + @mock.patch.object(transfer_executions, "TransferExecution") + def test_delete_disks(self, mock_TransferExecution): + result = self.transfer.delete_disks(mock.sentinel.transfer) self.assertEqual( - mock_ReplicaExecution.return_value, + mock_TransferExecution.return_value, result ) - self.replica.client.post.assert_called_once_with( - "/replicas/%s/actions" % mock.sentinel.replica, + self.transfer.client.post.assert_called_once_with( + "/transfers/%s/actions" % mock.sentinel.transfer, json={'delete-disks': None}) - mock_ReplicaExecution.assert_called_once_with( - self.replica, - (self.replica.client.post.return_value.json.return_value. + mock_TransferExecution.assert_called_once_with( + self.transfer, + (self.transfer.client.post.return_value.json.return_value. get("execution")), loaded=True ) - @mock.patch.object(replica_executions, "ReplicaExecution") - def test_update(self, mock_ReplicaExecution): + @mock.patch.object(transfer_executions, "TransferExecution") + def test_update(self, mock_TransferExecution): updated_values = {"network_map": mock.sentinel.network_map} - result = self.replica.update(mock.sentinel.replica, updated_values) + result = self.transfer.update(mock.sentinel.transfer, updated_values) self.assertEqual( - mock_ReplicaExecution.return_value, + mock_TransferExecution.return_value, result ) - self.replica.client.put.assert_called_once_with( - "/replicas/%s" % mock.sentinel.replica, - json={"replica": updated_values}) - mock_ReplicaExecution.assert_called_once_with( - self.replica, - (self.replica.client.put.return_value.json.return_value. + self.transfer.client.put.assert_called_once_with( + "/transfers/%s" % mock.sentinel.transfer, + json={"transfer": updated_values}) + mock_TransferExecution.assert_called_once_with( + self.transfer, + (self.transfer.client.put.return_value.json.return_value. get("execution")), loaded=True ) diff --git a/coriolisclient/v1/deployments.py b/coriolisclient/v1/deployments.py index 8887921..78558e7 100644 --- a/coriolisclient/v1/deployments.py +++ b/coriolisclient/v1/deployments.py @@ -62,12 +62,12 @@ def get(self, deployment): return self._get( '/deployments/%s' % base.getid(deployment), 'deployment') - def create_from_replica(self, replica_id, clone_disks=True, force=False, - skip_os_morphing=False, user_scripts=None, - instance_osmorphing_minion_pool_mappings=None): + def create_from_transfer(self, transfer_id, clone_disks=True, force=False, + skip_os_morphing=False, user_scripts=None, + instance_osmorphing_minion_pool_mappings=None): data = { "deployment": { - "replica_id": replica_id, + "transfer_id": transfer_id, "clone_disks": clone_disks, "force": force, "skip_os_morphing": skip_os_morphing, @@ -78,7 +78,7 @@ def create_from_replica(self, replica_id, clone_disks=True, force=False, return self._post('/deployments', data, 'deployment') def delete(self, deployment): - return self._delete('/deployment/%s' % base.getid(deployment)) + return self._delete('/deployments/%s' % base.getid(deployment)) def cancel(self, deployment, force=False): return self.client.post( diff --git a/coriolisclient/v1/replica_executions.py b/coriolisclient/v1/transfer_executions.py similarity index 59% rename from coriolisclient/v1/replica_executions.py rename to coriolisclient/v1/transfer_executions.py index 2f906f8..731fc38 100644 --- a/coriolisclient/v1/replica_executions.py +++ b/coriolisclient/v1/transfer_executions.py @@ -17,7 +17,7 @@ from coriolisclient.v1 import common -class ReplicaExecution(base.Resource): +class TransferExecution(base.Resource): _tasks = None @property @@ -28,37 +28,38 @@ def tasks(self): self._info.get('tasks', [])] -class ReplicaExecutionManager(base.BaseManager): - resource_class = ReplicaExecution +class TransferExecutionManager(base.BaseManager): + resource_class = TransferExecution def __init__(self, api): - super(ReplicaExecutionManager, self).__init__(api) + super(TransferExecutionManager, self).__init__(api) - def list(self, replica): + def list(self, transfer): return self._list( - '/replicas/%s/executions' % base.getid(replica), 'executions') + '/transfers/%s/executions' % base.getid(transfer), 'executions') - def get(self, replica, execution): + def get(self, transfer, execution): return self._get( - '/replicas/%(replica_id)s/executions/%(execution_id)s' % - {"replica_id": base.getid(replica), + '/transfers/%(transfer_id)s/executions/%(execution_id)s' % + {"transfer_id": base.getid(transfer), "execution_id": base.getid(execution)}, 'execution') - def create(self, replica, shutdown_instances=False): + def create(self, transfer, shutdown_instances=False): data = {"execution": {"shutdown_instances": shutdown_instances}} return self._post( - '/replicas/%s/executions' % base.getid(replica), data, 'execution') + '/transfers/%s/executions' % + base.getid(transfer), data, 'execution') - def delete(self, replica, execution): + def delete(self, transfer, execution): return self._delete( - '/replicas/%(replica_id)s/executions/%(execution_id)s' % - {"replica_id": base.getid(replica), + '/transfers/%(transfer_id)s/executions/%(execution_id)s' % + {"transfer_id": base.getid(transfer), "execution_id": base.getid(execution)}) - def cancel(self, replica, execution, force=False): + def cancel(self, transfer, execution, force=False): return self.client.post( - '/replicas/%(replica_id)s/executions/%(execution_id)s/actions' % - {"replica_id": base.getid(replica), + '/transfers/%(transfer_id)s/executions/%(execution_id)s/actions' % + {"transfer_id": base.getid(transfer), "execution_id": base.getid(execution)}, json={'cancel': {'force': force}}) diff --git a/coriolisclient/v1/replica_schedules.py b/coriolisclient/v1/transfer_schedules.py similarity index 68% rename from coriolisclient/v1/replica_schedules.py rename to coriolisclient/v1/transfer_schedules.py index 5b83f82..c02e785 100644 --- a/coriolisclient/v1/replica_schedules.py +++ b/coriolisclient/v1/transfer_schedules.py @@ -18,32 +18,33 @@ from coriolisclient import base -class ReplicaSchedule(base.Resource): +class TransferSchedule(base.Resource): _tasks = None -class ReplicaScheduleManager(base.BaseManager): - resource_class = ReplicaSchedule +class TransferScheduleManager(base.BaseManager): + resource_class = TransferSchedule def __init__(self, api): - super(ReplicaScheduleManager, self).__init__(api) + super(TransferScheduleManager, self).__init__(api) - def list(self, replica, hide_expired=False): + def list(self, transfer, hide_expired=False): query = {} - url = '/replicas/%s/schedules' % base.getid(replica) if hide_expired: query["show_expired"] = hide_expired is False + url = '/transfers/%s/schedules' % base.getid(transfer) + if query: url += "?" + urlparse.urlencode(query) return self._list(url, 'schedules') - def get(self, replica, schedule): + def get(self, transfer, schedule): return self._get( - '/replicas/%(replica_id)s/schedules/%(schedule_id)s' % - {"replica_id": base.getid(replica), + '/transfers/%(transfer_id)s/schedules/%(schedule_id)s' % + {"transfer_id": base.getid(transfer), "schedule_id": base.getid(schedule)}, 'schedule') - def create(self, replica, schedule, enabled, expiration_date, + def create(self, transfer, schedule, enabled, expiration_date, shutdown_instance): data = { "schedule": schedule, @@ -54,9 +55,9 @@ def create(self, replica, schedule, enabled, expiration_date, data["expiration_date"] = self._format_rfc3339_datetime( expiration_date) return self._post( - '/replicas/%s/schedules' % base.getid(replica), data, 'schedule') + '/transfers/%s/schedules' % base.getid(transfer), data, 'schedule') - def update(self, replica_id, schedule_id, updated_values): + def update(self, transfer_id, schedule_id, updated_values): expiration_date = updated_values.get("expiration_date") if expiration_date: updated_values = updated_values.copy() @@ -64,15 +65,15 @@ def update(self, replica_id, schedule_id, updated_values): expiration_date) return self._put( - '/replicas/%(replica_id)s/schedules/%(schedule_id)s' % { - "replica_id": base.getid(replica_id), + '/transfers/%(transfer_id)s/schedules/%(schedule_id)s' % { + "transfer_id": base.getid(transfer_id), "schedule_id": base.getid(schedule_id)}, updated_values, 'schedule') - def delete(self, replica, schedule): + def delete(self, transfer, schedule): return self._delete( - '/replicas/%(replica_id)s/schedules/%(schedule_id)s' % - {"replica_id": base.getid(replica), + '/transfers/%(transfer_id)s/schedules/%(schedule_id)s' % + {"transfer_id": base.getid(transfer), "schedule_id": base.getid(schedule)}) @staticmethod diff --git a/coriolisclient/v1/replicas.py b/coriolisclient/v1/transfers.py similarity index 70% rename from coriolisclient/v1/replicas.py rename to coriolisclient/v1/transfers.py index 48a3b5c..ebba87d 100644 --- a/coriolisclient/v1/replicas.py +++ b/coriolisclient/v1/transfers.py @@ -15,10 +15,10 @@ from coriolisclient import base from coriolisclient.v1 import common -from coriolisclient.v1 import replica_executions +from coriolisclient.v1 import transfer_executions -class Replica(base.Resource): +class Transfer(base.Resource): _tasks = None @property @@ -41,24 +41,24 @@ def executions(self): self._info.get('executions', [])] -class ReplicaManager(base.BaseManager): - resource_class = Replica +class TransferManager(base.BaseManager): + resource_class = Transfer def __init__(self, api): - super(ReplicaManager, self).__init__(api) + super(TransferManager, self).__init__(api) def list(self, detail=False): - path = "/replicas" + path = "/transfers" if detail: path = "%s/detail" % path - return self._list(path, 'replicas') + return self._list(path, 'transfers') - def get(self, replica): - return self._get('/replicas/%s' % base.getid(replica), 'replica') + def get(self, transfer): + return self._get('/transfers/%s' % base.getid(transfer), 'transfer') def create(self, origin_endpoint_id, destination_endpoint_id, source_environment, destination_environment, instances, - replica_scenario, + transfer_scenario, network_map=None, notes=None, storage_mappings=None, origin_minion_pool_id=None, destination_minion_pool_id=None, instance_osmorphing_minion_pool_mappings=None, @@ -69,12 +69,12 @@ def create(self, origin_endpoint_id, destination_endpoint_id, storage_mappings = destination_environment.get( 'storage_mappings', {}) data = { - "replica": { + "transfer": { "origin_endpoint_id": origin_endpoint_id, "destination_endpoint_id": destination_endpoint_id, "destination_environment": destination_environment, "instances": instances, - "scenario": replica_scenario, + "scenario": transfer_scenario, "network_map": network_map, "notes": notes, "storage_mappings": storage_mappings, @@ -82,35 +82,35 @@ def create(self, origin_endpoint_id, destination_endpoint_id, } } if source_environment: - data['replica']['source_environment'] = source_environment + data['transfer']['source_environment'] = source_environment if origin_minion_pool_id is not None: - data['replica']['origin_minion_pool_id'] = origin_minion_pool_id + data['transfer']['origin_minion_pool_id'] = origin_minion_pool_id if destination_minion_pool_id is not None: - data['replica']['destination_minion_pool_id'] = ( + data['transfer']['destination_minion_pool_id'] = ( destination_minion_pool_id) if instance_osmorphing_minion_pool_mappings: - data['replica']['instance_osmorphing_minion_pool_mappings'] = ( + data['transfer']['instance_osmorphing_minion_pool_mappings'] = ( instance_osmorphing_minion_pool_mappings) - return self._post('/replicas', data, 'replica') + return self._post('/transfers', data, 'transfer') - def delete(self, replica): - return self._delete('/replicas/%s' % base.getid(replica)) + def delete(self, transfer): + return self._delete('/transfers/%s' % base.getid(transfer)) - def delete_disks(self, replica): + def delete_disks(self, transfer): response = self.client.post( - '/replicas/%s/actions' % base.getid(replica), + '/transfers/%s/actions' % base.getid(transfer), json={'delete-disks': None}) - return replica_executions.ReplicaExecution( + return transfer_executions.TransferExecution( self, response.json().get("execution"), loaded=True) - def update(self, replica, updated_values): + def update(self, transfer, updated_values): data = { - "replica": updated_values + "transfer": updated_values } response = self.client.put( - '/replicas/%s' % base.getid(replica), json=data) + '/transfers/%s' % base.getid(transfer), json=data) - return replica_executions.ReplicaExecution( + return transfer_executions.TransferExecution( self, response.json().get("execution"), loaded=True) diff --git a/samples/replica_samples.py b/samples/replica_samples.py index 1df76dc..7fec360 100644 --- a/samples/replica_samples.py +++ b/samples/replica_samples.py @@ -102,7 +102,7 @@ def create_vm_replicas(coriolis, source_endpoint, destination_endpoint, print("Creating Replica for VM(s): %s" % vm_group) replicas.append( - coriolis.replicas.create( + coriolis.transfers.create( source_endpoint, destination_endpoint, source_options, destination_options, vm_group, @@ -118,7 +118,7 @@ def get_replicas_for_endpoint(coriolis, endpoint, endpoint = coriolis.endpoints.get(endpoint) found = [] - for replica in coriolis.replicas.list(): + for replica in coriolis.transfers.list(): if as_source and replica.origin_endpoint_id == endpoint.id: found.append(replica) @@ -131,7 +131,7 @@ def get_replicas_for_endpoint(coriolis, endpoint, def get_replicas_for_vm(coriolis, vm_name): """ Returns all Replicas which include the given VM name. """ replicas = [] - for replica in coriolis.replicas.list(): + for replica in coriolis.transfers.list(): if vm_name in replica.instances: replicas.append(replica) @@ -141,7 +141,7 @@ def get_replicas_for_vm(coriolis, vm_name): def get_errord_replicas(coriolis): """ Returns a list of all the Replicas whose last execution error'd. """ errord = [] - for replica in coriolis.replicas.list(): + for replica in coriolis.transfers.list(): if not replica.executions: # Replica was never executed continue @@ -162,13 +162,13 @@ def wait_for_replica_execution(coriolis, replica, execution, """ tries = 0 - execution = coriolis.replica_executions.get( + execution = coriolis.transfer_executions.get( replica, execution) while tries < max_tries and execution.status == "RUNNING": print("Waiting on execution %s (currently %s)" % ( execution.id, execution.status)) time.sleep(retry_period) - execution = coriolis.replica_executions.get( + execution = coriolis.transfer_executions.get( replica, execution) if execution.status == "ERROR": @@ -220,15 +220,15 @@ def delete_replica(coriolis, replica, delete_replica_disks=True): the Replica itself. """ if delete_replica_disks: - execution = coriolis.replicas.delete_disks(replica) + execution = coriolis.transfers.delete_disks(replica) wait_for_replica_execution(coriolis, replica, execution) # NOTE: this is redundant as executions are cascade-deleted on # the deletion of the parent Replica anyway: for execution in reversed(replica.executions): - coriolis.replica_executions.delete(execution) + coriolis.transfer_executions.delete(execution) - coriolis.replicas.delete(replica) + coriolis.transfers.delete(replica) def main(): @@ -252,14 +252,14 @@ def main(): # execute the Replica: test_replica = replicas[0] - execution = coriolis.replica_executions.create( + execution = coriolis.transfer_executions.create( test_replica, shutdown_instances=True) # wait for execution to finish: wait_for_replica_execution(coriolis, test_replica, execution) # create a Migration from the Replica: - migration = coriolis.migrations.create_from_replica( + migration = coriolis.migrations.create_from_transfer( test_replica.id, clone_disks=True, skip_os_morphing=False) migration = wait_for_replica_migration(coriolis, migration) print("Migrated VM info is: %s" % ( diff --git a/setup.cfg b/setup.cfg index e39bbbe..b67cc70 100644 --- a/setup.cfg +++ b/setup.cfg @@ -44,13 +44,6 @@ coriolis.v1 = endpoint_source_minion_pool_options_list = coriolisclient.cli.endpoint_source_minion_pool_options:ListEndpointSourceMinionPoolOptions endpoint_destination_minion_pool_options_list = coriolisclient.cli.endpoint_destination_minion_pool_options:ListEndpointDestinationMinionPoolOptions - migration_cancel = coriolisclient.cli.migrations:CancelMigration - migration_create = coriolisclient.cli.migrations:CreateMigration - migration_deploy_replica = coriolisclient.cli.migrations:CreateMigrationFromReplica - migration_delete = coriolisclient.cli.migrations:DeleteMigration - migration_list = coriolisclient.cli.migrations:ListMigration - migration_show = coriolisclient.cli.migrations:ShowMigration - deployment_cancel = coriolisclient.cli.deployments:CancelDeployment deployment_create = coriolisclient.cli.deployments:CreateDeployment deployment_delete = coriolisclient.cli.deployments:DeleteDeployment @@ -60,24 +53,24 @@ coriolis.v1 = provider_list = coriolisclient.cli.providers:ListProvider provider_schema_list = coriolisclient.cli.providers:ListProviderSchemas - replica_create = coriolisclient.cli.replicas:CreateReplica - replica_delete = coriolisclient.cli.replicas:DeleteReplica - replica_disks_delete = coriolisclient.cli.replicas:DeleteReplicaDisks - replica_list = coriolisclient.cli.replicas:ListReplica - replica_show = coriolisclient.cli.replicas:ShowReplica - replica_update = coriolisclient.cli.replicas:UpdateReplica - - replica_execution_cancel = coriolisclient.cli.replica_executions:CancelReplicaExecution - replica_execute = coriolisclient.cli.replica_executions:CreateReplicaExecution - replica_execution_delete = coriolisclient.cli.replica_executions:DeleteReplicaExecution - replica_execution_list = coriolisclient.cli.replica_executions:ListReplicaExecution - replica_execution_show = coriolisclient.cli.replica_executions:ShowReplicaExecution - - replica_schedule_delete = coriolisclient.cli.replica_schedules:DeleteReplicaSchedule - replica_schedule_list = coriolisclient.cli.replica_schedules:ListReplicaSchedule - replica_schedule_show = coriolisclient.cli.replica_schedules:ShowReplicaSchedule - replica_schedule_create = coriolisclient.cli.replica_schedules:CreateReplicaSchedule - replica_schedule_update = coriolisclient.cli.replica_schedules:UpdateReplicaSchedule + transfer_create = coriolisclient.cli.transfers:CreateTransfer + transfer_delete = coriolisclient.cli.transfers:DeleteTransfer + transfer_disks_delete = coriolisclient.cli.transfers:DeleteTransferDisks + transfer_list = coriolisclient.cli.transfers:ListTransfer + transfer_show = coriolisclient.cli.transfers:ShowTransfer + transfer_update = coriolisclient.cli.transfers:UpdateTransfer + + transfer_execution_cancel = coriolisclient.cli.transfer_executions:CancelTransferExecution + transfer_execute = coriolisclient.cli.transfer_executions:CreateTransferExecution + transfer_execution_delete = coriolisclient.cli.transfer_executions:DeleteTransferExecution + transfer_execution_list = coriolisclient.cli.transfer_executions:ListTransferExecution + transfer_execution_show = coriolisclient.cli.transfer_executions:ShowTransferExecution + + transfer_schedule_delete = coriolisclient.cli.transfer_schedules:DeleteTransferSchedule + transfer_schedule_list = coriolisclient.cli.transfer_schedules:ListTransferSchedule + transfer_schedule_show = coriolisclient.cli.transfer_schedules:ShowTransferSchedule + transfer_schedule_create = coriolisclient.cli.transfer_schedules:CreateTransferSchedule + transfer_schedule_update = coriolisclient.cli.transfer_schedules:UpdateTransferSchedule region_create = coriolisclient.cli.regions:CreateRegion region_list = coriolisclient.cli.regions:ListRegions From 05f81fb76d8cac4a9207e891c5946dc345417b29 Mon Sep 17 00:00:00 2001 From: Daniel Vincze Date: Fri, 29 Nov 2024 17:30:12 +0200 Subject: [PATCH 8/8] Add deployments unit tests --- coriolisclient/cli/deployments.py | 4 +- coriolisclient/tests/cli/test_deployments.py | 340 +++++++++++++++++++ coriolisclient/tests/v1/test_deployments.py | 124 +++++++ 3 files changed, 466 insertions(+), 2 deletions(-) create mode 100644 coriolisclient/tests/cli/test_deployments.py create mode 100644 coriolisclient/tests/v1/test_deployments.py diff --git a/coriolisclient/cli/deployments.py b/coriolisclient/cli/deployments.py index 31eb7ee..74c6139 100644 --- a/coriolisclient/cli/deployments.py +++ b/coriolisclient/cli/deployments.py @@ -200,7 +200,7 @@ def get_parser(self, prog_name): return parser def take_action(self, args): - m = self.app.client_manager.coriolis.deployments + d = self.app.client_manager.coriolis.deployments user_scripts = cli_utils.compose_user_scripts( args.global_scripts, args.instance_scripts) instance_osmorphing_minion_pool_mappings = None @@ -209,7 +209,7 @@ def take_action(self, args): mp['instance_id']: mp['pool_id'] for mp in args.instance_osmorphing_minion_pool_mappings} - deployment = m.create_from_transfer( + deployment = d.create_from_transfer( args.transfer, args.clone_disks, args.force, diff --git a/coriolisclient/tests/cli/test_deployments.py b/coriolisclient/tests/cli/test_deployments.py new file mode 100644 index 0000000..f00065f --- /dev/null +++ b/coriolisclient/tests/cli/test_deployments.py @@ -0,0 +1,340 @@ +# Copyright (c) 2024 Cloudbase Solutions Srl +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import argparse +import copy +import os +from unittest import mock + +from coriolisclient.cli import deployments +from coriolisclient.tests import test_base +from coriolisclient.v1 import common +from coriolisclient.v1 import deployments as v1_deployments + +DEPLOYMENT_ID = "depl_id" +DEPLOYMENT_DATA = { + "storage_mappings": None, + "id": DEPLOYMENT_ID, + "last_execution_status": "COMPLETED", + "created_at": None, + "updated_at": None, + "transfer_id": "transfer_1", + "transfer_scenario_type": "replica", + "reservation_id": "res1", + "instances": ["instance1"], + "notes": "", + "origin_endpoint_id": "source1", + "origin_minion_pool_id": None, + "destination_endpoint_id": "dest1", + "destination_minion_pool_id": None, + "instance_osmorphing_minion_pool_mappings": None, + "shutdown_instances": False, + "destination_environment": {"opt1": "env1"}, + "source_environment": None, + "network_map": {"net_source": "net_dest"}, + "user_scripts": None, + "tasks": [], + "transfer_result": None, +} +DEPLOYMENT_FORMATTED_DATA = [ + "depl_id", "COMPLETED", None, None, "transfer_1", "replica", "res1", + "instance1", "", "source1", None, "dest1", None, '{}', False, + '{\n "opt1": "env1"\n}', '{}', '{\n "net_source": "net_dest"\n}', "", "", + None, '{}', "", '{}'] + +DEPLOYMENT_LIST_DATA = { + "id": "1", + "transfer_id": "2", + "last_execution_status": "RUNNING", + "instances": ["instance1", "instance2"], + "notes": "test_notes", + "created_at": "2024-11-28T15:18:25.000000", +} +DEPLOYMENT_LIST_FORMATTED_DATA = ( + "1", "2", "RUNNING", "instance1\ninstance2", "test_notes", + "2024-11-28T15:18:25.000000") +APP_MOCK = mock.MagicMock() + + +class DeploymentFormatterTestCase(test_base.CoriolisBaseTestCase): + + def setUp(self): + super(DeploymentFormatterTestCase, self).setUp() + self.formatter = deployments.DeploymentFormatter() + + def test__get_sorted_list(self): + obj1 = mock.Mock(created_at="2024-10-24T18:51:32.000000") + obj2 = mock.Mock(created_at="2024-11-18T15:18:25.000000") + obj3 = mock.Mock(created_at="2024-11-28T15:18:25.000000") + obj_list = [obj1, obj3, obj2] + self.assertEqual( + [obj1, obj2, obj3], self.formatter._get_sorted_list(obj_list)) + + def test__get_formatted_data(self): + obj = mock.Mock(**DEPLOYMENT_LIST_DATA) + self.assertEqual( + DEPLOYMENT_LIST_FORMATTED_DATA, + self.formatter._get_formatted_data(obj)) + + +class DeploymentDetailFormatterTestCase(test_base.CoriolisBaseTestCase): + + def setUp(self): + super(DeploymentDetailFormatterTestCase, self).setUp() + self.formatter = deployments.DeploymentDetailFormatter( + show_instances_data=True) + self.progress_updates = [ + {"created_at": "2024-10-24T18:51:32.000000", + "index": 0, + "message": "message 0"}, + {"created_at": "2024-11-18T15:18:25.000000", + "index": 2, + "message": "message 2"}, + {"created_at": "2024-11-28T15:18:25.000000", + "index": 1, + "message": "message 1"}, + ] + self.formatted_progress_updates = ( + f"2024-10-24T18:51:32.000000 message 0{os.linesep}" + f"2024-11-28T15:18:25.000000 message 1{os.linesep}" + f"2024-11-18T15:18:25.000000 message 2") + self.tasks_data = [ + { + "id": "1", + "task_type": "type1", + "instance": "instance1", + "status": "COMPLETED", + "depends_on": ["task0"], + "exception_details": None, + "progress_updates": self.progress_updates, + }, + { + "id": "2", + "task_type": "type2", + "instance": "instance1", + "status": "COMPLETED", + "depends_on": ["task0"], + "exception_details": None, + "progress_updates": self.progress_updates, + } + ] + self.formatted_tasks = [ + f'id: 1{os.linesep}' + f'task_type: type1{os.linesep}' + f'instance: instance1{os.linesep}' + f'status: COMPLETED{os.linesep}' + f'depends_on: task0{os.linesep}' + f'exception_details: {os.linesep}' + f'progress_updates:{os.linesep}' + f'{self.formatted_progress_updates}', + + f'id: 2{os.linesep}' + f'task_type: type2{os.linesep}' + f'instance: instance1{os.linesep}' + f'status: COMPLETED{os.linesep}' + f'depends_on: task0{os.linesep}' + f'exception_details: {os.linesep}' + f'progress_updates:{os.linesep}' + f'{self.formatted_progress_updates}' + ] + self.manager_mock = mock.MagicMock() + + def test_init_no_instances_data(self): + formatter = deployments.DeploymentDetailFormatter( + show_instances_data=False) + self.assertNotIn('instances_data', formatter.columns) + + def test__format_instances(self): + obj = mock.Mock(instances=["instance2", "instance3", "instance1"]) + self.assertEqual( + f"instance1{os.linesep}instance2{os.linesep}instance3", + self.formatter._format_instances(obj)) + + def test__format_progress_updates(self): + task_dict = { + "progress_updates": self.progress_updates} + self.assertEqual( + self.formatted_progress_updates, + self.formatter._format_progress_updates(task_dict)) + + def test__format_task(self): + task_data = self.tasks_data[0] + + task = common.Task(self.manager_mock, task_data) + + self.assertEqual( + self.formatted_tasks[0], self.formatter._format_task(task)) + + def test__format_tasks(self): + obj = mock.Mock( + tasks=[common.Task(self.manager_mock, task_data) + for task_data in self.tasks_data]) + expected_result = (f'{self.formatted_tasks[0]}{os.linesep}{os.linesep}' + f'{self.formatted_tasks[1]}') + self.assertEqual(expected_result, self.formatter._format_tasks(obj)) + + def test__get_formatted_data(self): + obj_data = { + **DEPLOYMENT_DATA, + "info": {"depl": "info"}, + } + obj = mock.Mock(**obj_data) + obj.to_dict.return_value = obj_data + expected_result = copy.copy(DEPLOYMENT_FORMATTED_DATA) + expected_result.append(obj_data['info']) + + self.assertEqual( + expected_result, self.formatter._get_formatted_data(obj)) + + +class CreateDeploymentTestCase(test_base.CoriolisBaseTestCase): + + def setUp(self): + super(CreateDeploymentTestCase, self).setUp() + self.mock_app = APP_MOCK + self.cli = deployments.CreateDeployment(self.mock_app, 'app_arg') + + def test_get_parser(self): + parser = self.cli.get_parser('coriolis') + global_script = "linux=/linux/path" + instance_script = "instance1=/instance1/path" + args = parser.parse_args([ + 'transfer_id', '--force', '--dont-clone-disks', + '--skip-os-morphing', '--user-script-global', global_script, + '--user-script-instance', instance_script]) + self.assertEqual( + ('transfer_id', True, False, True, [global_script], + [instance_script]), + (args.transfer, args.force, args.clone_disks, + args.skip_os_morphing, args.global_scripts, + args.instance_scripts)) + + def test_take_action(self): + args = mock.Mock(global_scripts=None, instance_scripts=None) + args.instance_osmorphing_minion_pool_mappings = [ + {"instance_id": "instance1", "pool_id": "pool1"}] + mock_fun = (self.mock_app.client_manager.coriolis.deployments. + create_from_transfer) + mock_fun.return_value = ( + v1_deployments.Deployment(mock.MagicMock(), DEPLOYMENT_DATA)) + expected_pool_mappings = {"instance1": "pool1"} + expected_user_scripts = {"global": {}, "instances": {}} + expected_data = copy.copy(DEPLOYMENT_FORMATTED_DATA) + + columns, data = self.cli.take_action(args) + + self.assertEqual( + deployments.DeploymentDetailFormatter().columns, columns) + self.assertEqual(expected_data, data) + mock_fun.assert_called_once_with( + args.transfer, args.clone_disks, args.force, args.skip_os_morphing, + user_scripts=expected_user_scripts, + instance_osmorphing_minion_pool_mappings=expected_pool_mappings) + + +class ShowDeploymentTestCase(test_base.CoriolisBaseTestCase): + + def setUp(self): + super(ShowDeploymentTestCase, self).setUp() + self.mock_app = APP_MOCK + self.cli = deployments.ShowDeployment(self.mock_app, 'app_args') + + def test_get_parser(self): + parser = self.cli.get_parser('coriolis') + args = parser.parse_args([DEPLOYMENT_ID, '--show-instances-data']) + self.assertEqual( + (DEPLOYMENT_ID, True), + (args.id, args.show_instances_data)) + + def test_take_action(self): + show_instances_data = False + args = mock.Mock( + id=DEPLOYMENT_ID, show_instances_data=show_instances_data) + mock_fun = self.mock_app.client_manager.coriolis.deployments.get + mock_fun.return_value = v1_deployments.Deployment( + mock.MagicMock(), DEPLOYMENT_DATA) + expected_data = DEPLOYMENT_FORMATTED_DATA + + columns, data = self.cli.take_action(args) + + self.assertEqual( + deployments.DeploymentDetailFormatter( + show_instances_data=show_instances_data).columns, columns) + self.assertEqual(expected_data, data) + mock_fun.assert_called_once_with(DEPLOYMENT_ID) + + +class CancelDeploymentTestCase(test_base.CoriolisBaseTestCase): + + def setUp(self): + super(CancelDeploymentTestCase, self).setUp() + self.mock_app = APP_MOCK + self.cli = deployments.CancelDeployment(self.mock_app, 'app_args') + + def test_get_parser(self): + parser = self.cli.get_parser('coriolis') + args = parser.parse_args([DEPLOYMENT_ID, '--force']) + self.assertEqual((DEPLOYMENT_ID, True), (args.id, args.force)) + + def test_take_action(self): + force = True + args = mock.Mock(id=DEPLOYMENT_ID, force=force) + mock_fun = self.mock_app.client_manager.coriolis.deployments.cancel + + self.cli.take_action(args) + + mock_fun.assert_called_once_with(DEPLOYMENT_ID, force) + + +class DeleteDeploymentTestCase(test_base.CoriolisBaseTestCase): + + def setUp(self): + super(DeleteDeploymentTestCase, self).setUp() + self.mock_app = APP_MOCK + self.cli = deployments.DeleteDeployment(self.mock_app, 'app_args') + + def test_get_parser(self): + parser = self.cli.get_parser('coriolis') + args = parser.parse_args([DEPLOYMENT_ID]) + self.assertEqual(DEPLOYMENT_ID, args.id) + + def test_take_action(self): + args = mock.Mock(id=DEPLOYMENT_ID) + mock_fun = self.mock_app.client_manager.coriolis.deployments.delete + + self.cli.take_action(args) + mock_fun.assert_called_once_with(DEPLOYMENT_ID) + + +class ListDeploymentTestCase(test_base.CoriolisBaseTestCase): + + def setUp(self): + super(ListDeploymentTestCase, self).setUp() + self.mock_app = APP_MOCK + self.cli = deployments.ListDeployment(self.mock_app, 'app_args') + + def test_get_parser(self): + parser = self.cli.get_parser('coriolis') + self.assertIsInstance(parser, argparse.ArgumentParser) + + def test_take_action(self): + mock_fun = self.mock_app.client_manager.coriolis.deployments.list + mock_fun.return_value = [ + v1_deployments.Deployment(mock.MagicMock(), DEPLOYMENT_LIST_DATA)] + + columns, data = self.cli.take_action(mock.ANY) + + self.assertEqual(deployments.DeploymentFormatter().columns, columns) + self.assertEqual([DEPLOYMENT_LIST_FORMATTED_DATA], list(data)) diff --git a/coriolisclient/tests/v1/test_deployments.py b/coriolisclient/tests/v1/test_deployments.py new file mode 100644 index 0000000..f92fe9a --- /dev/null +++ b/coriolisclient/tests/v1/test_deployments.py @@ -0,0 +1,124 @@ +# Copyright (c) 2024 Cloudbase Solutions Srl +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from unittest import mock + +from coriolisclient.tests import test_base +from coriolisclient.v1 import common +from coriolisclient.v1 import deployments + +DEPLOYMENT_ID = "1" + + +class DeploymentResourceTestCase(test_base.CoriolisBaseTestCase): + + def setUp(self): + super(DeploymentResourceTestCase, self).setUp() + self.source_env = {"source_opt": "env_value"} + self.dest_env = {"dest_opt": "env_value"} + self.transfer_result = {"result": "value"} + self.task = {"task": "type_1"} + self.info = { + "source_environment": self.source_env, + "destination_environment": self.dest_env, + "transfer_result": self.transfer_result, + "tasks": [self.task], + } + self.deployment = deployments.Deployment(None, self.info) + + def test_source_environment(self): + result = self.deployment.source_environment + + self.assertIsInstance(result, common.SourceEnvironment) + self.assertEqual(self.source_env, result._info) + + def test_destination_environment(self): + result = self.deployment.destination_environment + + self.assertIsInstance(result, common.DestinationEnvironment) + self.assertEqual(self.dest_env, result._info) + + def test_transfer_result(self): + result = self.deployment.transfer_result + + self.assertIsInstance(result, common.TransferResult) + self.assertEqual(self.transfer_result, result._info) + + def test_task(self): + result = self.deployment.tasks + + [self.assertIsInstance(t, common.Task) for t in result] + self.assertEqual(self.task, result[0]._info) + + +class DeploymentManagerTestCase(test_base.CoriolisBaseTestCase): + + def setUp(self): + super(DeploymentManagerTestCase, self).setUp() + mock_client = mock.Mock() + self.deployments = deployments.DeploymentManager(mock_client) + + def test_list(self): + with mock.patch.object(self.deployments, '_list') as mock_list: + result = self.deployments.list(detail=True) + self.assertEqual(mock_list.return_value, result) + mock_list.assert_called_once_with( + '/deployments/detail', 'deployments') + + def test_get(self): + deployment = mock.Mock(uuid=DEPLOYMENT_ID) + with mock.patch.object(self.deployments, '_get') as mock_get: + result = self.deployments.get(deployment) + self.assertEqual(mock_get.return_value, result) + mock_get.assert_called_once_with( + f'/deployments/{DEPLOYMENT_ID}', 'deployment') + + def test_create_from_transfer(self): + with mock.patch.object(self.deployments, '_post') as mock_post: + expected_data = { + "deployment": { + "transfer_id": mock.sentinel.transfer_id, + "clone_disks": True, + "force": False, + "skip_os_morphing": False, + "user_scripts": None, + "instance_osmorphing_minion_pool_mappings": + mock.sentinel.pool_mappings, + } + } + result = self.deployments.create_from_transfer( + mock.sentinel.transfer_id, clone_disks=True, force=False, + skip_os_morphing=False, user_scripts=None, + instance_osmorphing_minion_pool_mappings=( + mock.sentinel.pool_mappings)) + self.assertEqual(mock_post.return_value, result) + mock_post.assert_called_once_with( + "/deployments", expected_data, "deployment") + + def test_delete(self): + with mock.patch.object(self.deployments, '_delete') as mock_delete: + result = self.deployments.delete(DEPLOYMENT_ID) + self.assertEqual(mock_delete.return_value, result) + mock_delete.assert_called_once_with( + f'/deployments/{DEPLOYMENT_ID}') + + def test_cancel(self): + force = False + expected_data = {"cancel": {"force": force}} + with mock.patch.object(self.deployments.client, 'post') as mock_post: + result = self.deployments.cancel(DEPLOYMENT_ID, force=force) + self.assertEqual(mock_post.return_value, result) + mock_post.assert_called_once_with( + f"/deployments/{DEPLOYMENT_ID}/actions", json=expected_data)