diff --git a/rally/cmd/commands/__init__.py b/rally/cmd/commands/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/rally/cmd/commands/deployment.py b/rally/cmd/commands/deployment.py new file mode 100644 index 0000000000..4fb28055a9 --- /dev/null +++ b/rally/cmd/commands/deployment.py @@ -0,0 +1,173 @@ +# Copyright 2013: Mirantis Inc. +# All Rights Reserved. +# +# 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. + +""" Rally command: deployment """ + +from __future__ import print_function + +import json +import os +import prettytable +import sys + +from rally.cmd import cliutils +from rally.cmd.commands import use as _use +from rally.cmd import envutils +from rally import db +from rally import exceptions +from rally.orchestrator import api +from rally import osclients + + +class DeploymentCommands(object): + + @cliutils.args('--name', type=str, required=True, + help='A name of the deployment.') + @cliutils.args('--fromenv', action='store_true', + help='Read environment variables instead of config file') + @cliutils.args('--filename', type=str, required=False, + help='A path to the configuration file of the ' + 'deployment.') + @cliutils.args('--no-use', action='store_false', dest='use', + help='Don\'t set new deployment as default for' + ' future operations') + def create(self, name, fromenv=False, filename=None, use=False): + """Create a new deployment on the basis of configuration file. + + :param fromenv: boolean, read environment instead of config file + :param filename: a path to the configuration file + :param name: a name of the deployment + """ + + if fromenv: + required_env_vars = ["OS_USERNAME", "OS_PASSWORD", "OS_AUTH_URL", + "OS_TENANT_NAME"] + + unavailable_vars = [v for v in required_env_vars + if v not in os.environ] + if unavailable_vars: + print("The following environment variables are required but " + "not set: %s" % ' '.join(unavailable_vars)) + return + + config = { + "name": "DummyEngine", + "endpoint": { + "auth_url": os.environ['OS_AUTH_URL'], + "username": os.environ['OS_USERNAME'], + "password": os.environ['OS_PASSWORD'], + "tenant_name": os.environ['OS_TENANT_NAME'] + } + } + else: + if not filename: + print("Either --filename or --fromenv is required") + return + with open(filename) as f: + config = json.load(f) + + deployment = api.create_deploy(config, name) + self.list(deployment_list=[deployment]) + if use: + _use.UseCommands().deployment(deployment['uuid']) + + @cliutils.args('--deploy-id', dest='deploy_id', type=str, required=False, + help='UUID of a deployment.') + @envutils.deploy_id_default + def recreate(self, deploy_id=None): + """Destroy and create an existing deployment. + + :param deploy_id: a UUID of the deployment + """ + api.recreate_deploy(deploy_id) + + @cliutils.args('--deploy-id', dest='deploy_id', type=str, required=False, + help='UUID of a deployment.') + @envutils.deploy_id_default + def destroy(self, deploy_id=None): + """Destroy the deployment. + + Release resources that are allocated for the deployment. The + Deployment, related tasks and their results are also deleted. + + :param deploy_id: a UUID of the deployment + """ + api.destroy_deploy(deploy_id) + + def list(self, deployment_list=None): + """Print list of deployments.""" + headers = ['uuid', 'created_at', 'name', 'status'] + table = prettytable.PrettyTable(headers) + + deployment_list = deployment_list or db.deployment_list() + for t in deployment_list: + r = [str(t[column]) for column in headers] + table.add_row(r) + + print(table) + + @cliutils.args('--deploy-id', dest='deploy_id', type=str, required=False, + help='UUID of a deployment.') + @envutils.deploy_id_default + def config(self, deploy_id=None): + """Print on stdout a config of the deployment in JSON format. + + :param deploy_id: a UUID of the deployment + """ + deploy = db.deployment_get(deploy_id) + print(json.dumps(deploy['config'])) + + @cliutils.args('--deploy-id', dest='deploy_id', type=str, required=False, + help='UUID of a deployment.') + @envutils.deploy_id_default + def endpoint(self, deploy_id=None): + """Print endpoint of the deployment. + + :param deploy_id: a UUID of the deployment + """ + headers = ['auth_url', 'username', 'password', 'tenant_name'] + table = prettytable.PrettyTable(headers) + endpoint = db.deployment_get(deploy_id)['endpoint'] + table.add_row([endpoint.get(m, '') for m in headers]) + print(table) + + @cliutils.args('--deploy-id', dest='deploy_id', type=str, required=False, + help='UUID of a deployment.') + @envutils.deploy_id_default + def check(self, deploy_id=None): + """Check the deployment. + + Check keystone authentication and list all available services. + + :param deploy_id: a UUID of the deployment + """ + headers = ['services', 'type', 'status'] + table = prettytable.PrettyTable(headers) + try: + endpoint = db.deployment_get(deploy_id)['endpoint'] + clients = osclients.Clients(username=endpoint['username'], + password=endpoint['password'], + tenant_name=endpoint['tenant_name'], + auth_url=endpoint['auth_url']) + client = clients.get_verified_keystone_client() + print("keystone endpoints are valid and following services are " + "available:") + for service in client.service_catalog.get_data(): + table.add_row([service['name'], service['type'], 'Available']) + except exceptions.InvalidArgumentsException: + table.add_row(['keystone', 'identity', 'Error']) + print(_("Authentication Issues: %s.") + % sys.exc_info()[1]) + print(table) diff --git a/rally/cmd/commands/task.py b/rally/cmd/commands/task.py new file mode 100644 index 0000000000..cc29c424b3 --- /dev/null +++ b/rally/cmd/commands/task.py @@ -0,0 +1,240 @@ +# Copyright 2013: Mirantis Inc. +# All Rights Reserved. +# +# 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. + +""" Rally command: task """ + +from __future__ import print_function + +import collections +import json +import pprint +import prettytable +import sys + +from rally.cmd import cliutils +from rally.cmd import envutils +from rally import db +from rally import exceptions +from rally.openstack.common.gettextutils import _ +from rally.orchestrator import api +from rally import processing + + +class TaskCommands(object): + + @cliutils.args('--deploy-id', type=str, dest='deploy_id', required=False, + help='UUID of the deployment') + @cliutils.args('--task', + help='Path to the file with full configuration of task') + @envutils.deploy_id_default + def start(self, task, deploy_id=None): + """Run a benchmark task. + + :param task: a file with json configration + :param deploy_id: a UUID of a deployment + """ + with open(task) as task_file: + config_dict = json.load(task_file) + try: + task = api.create_task(deploy_id) + self.list(task_list=[task]) + api.start_task(deploy_id, config_dict, task=task) + self.detailed(task_id=task['uuid']) + except exceptions.InvalidArgumentsException: + print(_("Reason: %s") % sys.exc_info()[1]) + + @cliutils.args('--task-id', type=str, dest='task_id', help='UUID of task') + def abort(self, task_id): + """Force abort task + + :param task_id: Task uuid + """ + api.abort_task(task_id) + + @cliutils.args('--task-id', type=str, dest='task_id', help='UUID of task') + def status(self, task_id): + """Get status of task + + :param task_id: Task uuid + Returns current status of task + """ + task = db.task_get(task_id) + print(_("Task %(task_id)s is %(status)s.") + % {'task_id': task_id, 'status': task['status']}) + + @cliutils.args( + '--task-id', type=str, dest='task_id', + help=('uuid of task, if --task-id is "last" results of most ' + 'recently created task will be displayed.')) + def detailed(self, task_id): + """Get detailed information about task + + :param task_id: Task uuid + Prints detailed information of task. + """ + def _print_atomic_actions_time(raw): + atime_merged = [] + for r in raw: + if 'atomic_actions_time' in r: + for a in r['atomic_actions_time']: + atime_merged.append(a) + + times_by_action = collections.defaultdict(list) + for at in atime_merged: + times_by_action[at['action']].append(at['duration']) + if times_by_action: + atomic_action_table = prettytable.PrettyTable( + ['action', 'max (sec)', 'avg (sec)', 'min (sec)']) + for k, v in times_by_action.iteritems(): + atomic_action_table.add_row([k, max(v), sum(v) / len(v), + min(v)]) + print(atomic_action_table) + print() + + if task_id == "last": + task = db.task_get_detailed_last() + task_id = task.uuid + else: + task = db.task_get_detailed(task_id) + + if task is None: + print("The task %s can not be found" % task_id) + return + + print() + print("=" * 80) + print(_("Task %(task_id)s is %(status)s. Failed: %(failed)s") + % {'task_id': task_id, + 'status': task['status'], + 'failed': task['failed'] + }) + + for result in task["results"]: + key = result["key"] + print("-" * 80) + print() + print("test scenario %s" % key["name"]) + print("args position %s" % key["pos"]) + print("args values:") + pprint.pprint(key["kw"]) + + if not result["data"]["validation"]["is_valid"]: + print("-" * 80) + print(result["data"]["validation"]["exc_msg"]) + continue + + _print_atomic_actions_time(result["data"]["raw"]) + + raw = result["data"]["raw"] + times = map(lambda x: x['time'], + filter(lambda r: not r['error'], raw)) + table = prettytable.PrettyTable(["max (sec)", "avg (sec)", + "min (sec)", "success/total", + "total times"]) + if times: + table.add_row([max(times), sum(times) / len(times), min(times), + float(len(times)) / len(raw), len(raw)]) + else: + table.add_row(['n/a', 'n/a', 'n/a', 0, len(raw)]) + print(table) + + #NOTE(hughsaunders): ssrs=scenario specific results + ssrs = [] + for result in raw: + try: + ssrs.append(result['scenario_output']['data']) + except (KeyError, TypeError): + # No SSRs in this result + pass + if ssrs: + sys.stdout.flush() + keys = set() + for ssr in ssrs: + keys.update(ssr.keys()) + + ssr_table = prettytable.PrettyTable( + ["Key", "max", "avg", "min"]) + for key in keys: + values = [float(ssr[key]) for ssr in ssrs if key in ssr] + + if values: + row = [str(key), + max(values), + sum(values) / len(values), + min(values)] + else: + row = [str(key)] + ['n/a'] * 3 + ssr_table.add_row(row) + print("\nScenario Specific Results\n") + print(ssr_table) + + for result in raw: + if result['scenario_output']['errors']: + print(result['scenario_output']['errors']) + + @cliutils.args('--task-id', type=str, dest='task_id', help='uuid of task') + @cliutils.args('--pretty', type=str, help=('pretty print (pprint) ' + 'or json print (json)')) + def results(self, task_id, pretty=False): + """Print raw results of task. + + :param task_id: Task uuid + :param pretty: Pretty print (pprint) or not (json) + """ + results = map(lambda x: {"key": x["key"], 'result': x['data']['raw']}, + db.task_result_get_all_by_uuid(task_id)) + if not pretty or pretty == 'json': + print(json.dumps(results)) + elif pretty == 'pprint': + print() + pprint.pprint(results) + print() + else: + print(_("Wrong value for --pretty=%s") % pretty) + + def list(self, task_list=None): + """Print a list of all tasks.""" + + headers = ['uuid', 'created_at', 'status', 'failed'] + table = prettytable.PrettyTable(headers) + + task_list = task_list or db.task_list() + + for t in task_list: + r = [t['uuid'], str(t['created_at']), t['status'], t['failed']] + table.add_row(r) + + print(table) + + @cliutils.args('--task-id', type=str, dest='task_id', help='uuid of task') + @cliutils.args('--force', action='store_true', help='force delete') + def delete(self, task_id, force): + """Delete a specific task and related results. + + :param task_id: Task uuid + :param force: Force delete or not + """ + api.delete_task(task_id, force=force) + + @cliutils.args('--plot-type', type=str, help='plot type; available types: ' + ', '.join(processing.PLOTS.keys())) + @cliutils.args('--field-name', type=str, help='field from the task config ' + 'to aggregate the data on: concurrent/times/...') + @cliutils.args('--task-id', type=str, help='uuid of task') + def plot(self, plot_type, aggregated_field, task_id): + if plot_type in processing.PLOTS: + processing.PLOTS[plot_type](task_id, aggregated_field) + else: + print("Plot type '%s' not supported." % plot_type) diff --git a/rally/cmd/commands/use.py b/rally/cmd/commands/use.py new file mode 100644 index 0000000000..158b5befc0 --- /dev/null +++ b/rally/cmd/commands/use.py @@ -0,0 +1,53 @@ +# Copyright 2013: Mirantis Inc. +# All Rights Reserved. +# +# 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. + +""" Rally command: use """ + +import os + +from rally import db +from rally import fileutils + + +class UseCommands(object): + + def _update_openrc_deployment_file(self, deploy_id): + openrc_path = os.path.expanduser('~/.rally/openrc-%s' % deploy_id) + endpoint = db.deployment_get(deploy_id)['endpoint'] + with open(openrc_path, 'w+') as env_file: + env_file.write('export OS_AUTH_URL=%(auth_url)s\n' + 'export OS_USERNAME=%(username)s\n' + 'export OS_PASSWORD=%(password)s\n' + 'export OS_TENANT_NAME=%(tenant_name)s\n' + % endpoint) + expanded_path = os.path.expanduser('~/.rally/openrc') + if os.path.exists(expanded_path): + os.remove(expanded_path) + os.symlink(openrc_path, expanded_path) + + def _update_rally_deployment_file(self, deploy_id): + expanded_path = os.path.expanduser('~/.rally/deployment') + fileutils.update_env_file(expanded_path, 'RALLY_DEPLOYMENT', deploy_id) + + def deployment(self, deploy_id): + """Set the RALLY_DEPLOYMENT env var to be used by all CLI commands + + :param deploy_id: a UUID of a deployment + """ + print('Using deployment : %s' % deploy_id) + if not os.path.exists(os.path.expanduser('~/.rally/')): + os.makedirs(os.path.expanduser('~/.rally/')) + self._update_rally_deployment_file(deploy_id) + self._update_openrc_deployment_file(deploy_id) diff --git a/rally/cmd/main.py b/rally/cmd/main.py index ab4134f076..ede20e0c28 100644 --- a/rally/cmd/main.py +++ b/rally/cmd/main.py @@ -17,405 +17,12 @@ from __future__ import print_function -import collections -import json -import os -import pprint -import prettytable import sys - from rally.cmd import cliutils -from rally.cmd import envutils -from rally import db -from rally import exceptions -from rally import fileutils -from rally.openstack.common.gettextutils import _ -from rally.orchestrator import api -from rally import osclients -from rally import processing - - -class DeploymentCommands(object): - - @cliutils.args('--name', type=str, required=True, - help='A name of the deployment.') - @cliutils.args('--fromenv', action='store_true', - help='Read environment variables instead of config file') - @cliutils.args('--filename', type=str, required=False, - help='A path to the configuration file of the ' - 'deployment.') - @cliutils.args('--no-use', action='store_false', dest='use', - help='Don\'t set new deployment as default for' - ' future operations') - def create(self, name, fromenv=False, filename=None, use=False): - """Create a new deployment on the basis of configuration file. - - :param fromenv: boolean, read environment instead of config file - :param filename: a path to the configuration file - :param name: a name of the deployment - """ - - if fromenv: - required_env_vars = ["OS_USERNAME", "OS_PASSWORD", "OS_AUTH_URL", - "OS_TENANT_NAME"] - - unavailable_vars = [v for v in required_env_vars - if v not in os.environ] - if unavailable_vars: - print("The following environment variables are required but " - "not set: %s" % ' '.join(unavailable_vars)) - return - - config = { - "name": "DummyEngine", - "endpoint": { - "auth_url": os.environ['OS_AUTH_URL'], - "username": os.environ['OS_USERNAME'], - "password": os.environ['OS_PASSWORD'], - "tenant_name": os.environ['OS_TENANT_NAME'] - } - } - else: - if not filename: - print("Either --filename or --fromenv is required") - return - with open(filename) as f: - config = json.load(f) - - deployment = api.create_deploy(config, name) - self.list(deployment_list=[deployment]) - if use: - UseCommands().deployment(deployment['uuid']) - - @cliutils.args('--deploy-id', dest='deploy_id', type=str, required=False, - help='UUID of a deployment.') - @envutils.deploy_id_default - def recreate(self, deploy_id=None): - """Destroy and create an existing deployment. - - :param deploy_id: a UUID of the deployment - """ - api.recreate_deploy(deploy_id) - - @cliutils.args('--deploy-id', dest='deploy_id', type=str, required=False, - help='UUID of a deployment.') - @envutils.deploy_id_default - def destroy(self, deploy_id=None): - """Destroy the deployment. - - Release resources that are allocated for the deployment. The - Deployment, related tasks and their results are also deleted. - - :param deploy_id: a UUID of the deployment - """ - api.destroy_deploy(deploy_id) - - def list(self, deployment_list=None): - """Print list of deployments.""" - headers = ['uuid', 'created_at', 'name', 'status'] - table = prettytable.PrettyTable(headers) - - deployment_list = deployment_list or db.deployment_list() - for t in deployment_list: - r = [str(t[column]) for column in headers] - table.add_row(r) - - print(table) - - @cliutils.args('--deploy-id', dest='deploy_id', type=str, required=False, - help='UUID of a deployment.') - @envutils.deploy_id_default - def config(self, deploy_id=None): - """Print on stdout a config of the deployment in JSON format. - - :param deploy_id: a UUID of the deployment - """ - deploy = db.deployment_get(deploy_id) - print(json.dumps(deploy['config'])) - - @cliutils.args('--deploy-id', dest='deploy_id', type=str, required=False, - help='UUID of a deployment.') - @envutils.deploy_id_default - def endpoint(self, deploy_id=None): - """Print endpoint of the deployment. - - :param deploy_id: a UUID of the deployment - """ - headers = ['auth_url', 'username', 'password', 'tenant_name'] - table = prettytable.PrettyTable(headers) - endpoint = db.deployment_get(deploy_id)['endpoint'] - table.add_row([endpoint.get(m, '') for m in headers]) - print(table) - - @cliutils.args('--deploy-id', dest='deploy_id', type=str, required=False, - help='UUID of a deployment.') - @envutils.deploy_id_default - def check(self, deploy_id=None): - """Check the deployment. - - Check keystone authentication and list all available services. - - :param deploy_id: a UUID of the deployment - """ - headers = ['services', 'type', 'status'] - table = prettytable.PrettyTable(headers) - try: - endpoint = db.deployment_get(deploy_id)['endpoint'] - clients = osclients.Clients(username=endpoint['username'], - password=endpoint['password'], - tenant_name=endpoint['tenant_name'], - auth_url=endpoint['auth_url']) - client = clients.get_verified_keystone_client() - print("keystone endpoints are valid and following services are " - "available:") - for service in client.service_catalog.get_data(): - table.add_row([service['name'], service['type'], 'Available']) - except exceptions.InvalidArgumentsException: - table.add_row(['keystone', 'identity', 'Error']) - print(_("Authentication Issues: %s.") - % sys.exc_info()[1]) - print(table) - - -class TaskCommands(object): - - @cliutils.args('--deploy-id', type=str, dest='deploy_id', required=False, - help='UUID of the deployment') - @cliutils.args('--task', - help='Path to the file with full configuration of task') - @envutils.deploy_id_default - def start(self, task, deploy_id=None): - """Run a benchmark task. - - :param task: a file with json configration - :param deploy_id: a UUID of a deployment - """ - with open(task) as task_file: - config_dict = json.load(task_file) - try: - task = api.create_task(deploy_id) - self.list(task_list=[task]) - api.start_task(deploy_id, config_dict, task=task) - self.detailed(task_id=task['uuid']) - except exceptions.InvalidArgumentsException: - print(_("Reason: %s") % sys.exc_info()[1]) - - @cliutils.args('--task-id', type=str, dest='task_id', help='UUID of task') - def abort(self, task_id): - """Force abort task - - :param task_id: Task uuid - """ - api.abort_task(task_id) - - @cliutils.args('--task-id', type=str, dest='task_id', help='UUID of task') - def status(self, task_id): - """Get status of task - - :param task_id: Task uuid - Returns current status of task - """ - task = db.task_get(task_id) - print(_("Task %(task_id)s is %(status)s.") - % {'task_id': task_id, 'status': task['status']}) - - @cliutils.args( - '--task-id', type=str, dest='task_id', - help=('uuid of task, if --task-id is "last" results of most ' - 'recently created task will be displayed.')) - def detailed(self, task_id): - """Get detailed information about task - - :param task_id: Task uuid - Prints detailed information of task. - """ - def _print_atomic_actions_time(raw): - atime_merged = [] - for r in raw: - if 'atomic_actions_time' in r: - for a in r['atomic_actions_time']: - atime_merged.append(a) - - times_by_action = collections.defaultdict(list) - for at in atime_merged: - times_by_action[at['action']].append(at['duration']) - if times_by_action: - atomic_action_table = prettytable.PrettyTable( - ['action', 'max (sec)', 'avg (sec)', 'min (sec)']) - for k, v in times_by_action.iteritems(): - atomic_action_table.add_row([k, max(v), sum(v) / len(v), - min(v)]) - print(atomic_action_table) - print() - - if task_id == "last": - task = db.task_get_detailed_last() - task_id = task.uuid - else: - task = db.task_get_detailed(task_id) - - if task is None: - print("The task %s can not be found" % task_id) - return - - print() - print("=" * 80) - print(_("Task %(task_id)s is %(status)s. Failed: %(failed)s") - % {'task_id': task_id, - 'status': task['status'], - 'failed': task['failed'] - }) - - for result in task["results"]: - key = result["key"] - print("-" * 80) - print() - print("test scenario %s" % key["name"]) - print("args position %s" % key["pos"]) - print("args values:") - pprint.pprint(key["kw"]) - - if not result["data"]["validation"]["is_valid"]: - print("-" * 80) - print(result["data"]["validation"]["exc_msg"]) - continue - - _print_atomic_actions_time(result["data"]["raw"]) - - raw = result["data"]["raw"] - times = map(lambda x: x['time'], - filter(lambda r: not r['error'], raw)) - table = prettytable.PrettyTable(["max (sec)", "avg (sec)", - "min (sec)", "success/total", - "total times"]) - if times: - table.add_row([max(times), sum(times) / len(times), min(times), - float(len(times)) / len(raw), len(raw)]) - else: - table.add_row(['n/a', 'n/a', 'n/a', 0, len(raw)]) - print(table) - - #NOTE(hughsaunders): ssrs=scenario specific results - ssrs = [] - for result in raw: - try: - ssrs.append(result['scenario_output']['data']) - except (KeyError, TypeError): - # No SSRs in this result - pass - if ssrs: - sys.stdout.flush() - keys = set() - for ssr in ssrs: - keys.update(ssr.keys()) - - ssr_table = prettytable.PrettyTable( - ["Key", "max", "avg", "min"]) - for key in keys: - values = [float(ssr[key]) for ssr in ssrs if key in ssr] - - if values: - row = [str(key), - max(values), - sum(values) / len(values), - min(values)] - else: - row = [str(key)] + ['n/a'] * 3 - ssr_table.add_row(row) - print("\nScenario Specific Results\n") - print(ssr_table) - - for result in raw: - if result['scenario_output']['errors']: - print(result['scenario_output']['errors']) - - @cliutils.args('--task-id', type=str, dest='task_id', help='uuid of task') - @cliutils.args('--pretty', type=str, help=('pretty print (pprint) ' - 'or json print (json)')) - def results(self, task_id, pretty=False): - """Print raw results of task. - - :param task_id: Task uuid - :param pretty: Pretty print (pprint) or not (json) - """ - results = map(lambda x: {"key": x["key"], 'result': x['data']['raw']}, - db.task_result_get_all_by_uuid(task_id)) - if not pretty or pretty == 'json': - print(json.dumps(results)) - elif pretty == 'pprint': - print() - pprint.pprint(results) - print() - else: - print(_("Wrong value for --pretty=%s") % pretty) - - def list(self, task_list=None): - """Print a list of all tasks.""" - - headers = ['uuid', 'created_at', 'status', 'failed'] - table = prettytable.PrettyTable(headers) - - task_list = task_list or db.task_list() - - for t in task_list: - r = [t['uuid'], str(t['created_at']), t['status'], t['failed']] - table.add_row(r) - - print(table) - - @cliutils.args('--task-id', type=str, dest='task_id', help='uuid of task') - @cliutils.args('--force', action='store_true', help='force delete') - def delete(self, task_id, force): - """Delete a specific task and related results. - - :param task_id: Task uuid - :param force: Force delete or not - """ - api.delete_task(task_id, force=force) - - @cliutils.args('--plot-type', type=str, help='plot type; available types: ' - ', '.join(processing.PLOTS.keys())) - @cliutils.args('--field-name', type=str, help='field from the task config ' - 'to aggregate the data on: concurrent/times/...') - @cliutils.args('--task-id', type=str, help='uuid of task') - def plot(self, plot_type, aggregated_field, task_id): - if plot_type in processing.PLOTS: - processing.PLOTS[plot_type](task_id, aggregated_field) - else: - print("Plot type '%s' not supported." % plot_type) - - -class UseCommands(object): - - def _update_openrc_deployment_file(self, deploy_id): - openrc_path = os.path.expanduser('~/.rally/openrc-%s' % deploy_id) - endpoint = db.deployment_get(deploy_id)['endpoint'] - with open(openrc_path, 'w+') as env_file: - env_file.write('export OS_AUTH_URL=%(auth_url)s\n' - 'export OS_USERNAME=%(username)s\n' - 'export OS_PASSWORD=%(password)s\n' - 'export OS_TENANT_NAME=%(tenant_name)s\n' - % endpoint) - expanded_path = os.path.expanduser('~/.rally/openrc') - if os.path.exists(expanded_path): - os.remove(expanded_path) - os.symlink(openrc_path, expanded_path) - - def _update_rally_deployment_file(self, deploy_id): - expanded_path = os.path.expanduser('~/.rally/deployment') - fileutils.update_env_file(expanded_path, 'RALLY_DEPLOYMENT', deploy_id) - - def deployment(self, deploy_id): - """Set the RALLY_DEPLOYMENT env var to be used by all CLI commands - - :param deploy_id: a UUID of a deployment - """ - print('Using deployment : %s' % deploy_id) - if not os.path.exists(os.path.expanduser('~/.rally/')): - os.makedirs(os.path.expanduser('~/.rally/')) - self._update_rally_deployment_file(deploy_id) - self._update_openrc_deployment_file(deploy_id) +from rally.cmd.commands import deployment +from rally.cmd.commands import task +from rally.cmd.commands import use def deprecated(): @@ -426,9 +33,9 @@ def deprecated(): def main(): categories = { - 'task': TaskCommands, - 'deployment': DeploymentCommands, - 'use': UseCommands, + 'deployment': deployment.DeploymentCommands, + 'task': task.TaskCommands, + 'use': use.UseCommands, } cliutils.run(sys.argv, categories) diff --git a/tests/cmd/commands/__init__.py b/tests/cmd/commands/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/cmd/commands/test_deployment.py b/tests/cmd/commands/test_deployment.py new file mode 100644 index 0000000000..65a41e7131 --- /dev/null +++ b/tests/cmd/commands/test_deployment.py @@ -0,0 +1,126 @@ +# Copyright 2013: Mirantis Inc. +# All Rights Reserved. +# +# 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 mock +import os +import uuid + +from rally.cmd.commands import deployment +from rally import exceptions +from rally.openstack.common import test + + +class DeploymentCommandsTestCase(test.BaseTestCase): + def setUp(self): + super(DeploymentCommandsTestCase, self).setUp() + self.deployment = deployment.DeploymentCommands() + + @mock.patch('rally.cmd.commands.deployment.api.create_deploy') + @mock.patch('rally.cmd.commands.deployment.open', + mock.mock_open(read_data='{"some": "json"}'), + create=True) + def test_create(self, mock_create): + self.deployment.create('fake_deploy', False, 'path_to_config.json') + mock_create.assert_called_once_with({'some': 'json'}, 'fake_deploy') + + @mock.patch.dict(os.environ, {'OS_AUTH_URL': 'fake_auth_url', + 'OS_USERNAME': 'fake_username', + 'OS_PASSWORD': 'fake_password', + 'OS_TENANT_NAME': 'fake_tenant_name'}) + @mock.patch('rally.cmd.commands.deployment.api.create_deploy') + def test_createfromenv(self, mock_create): + self.deployment.create('from_env', True) + mock_create.assert_called_once_with( + { + "name": "DummyEngine", + "endpoint": { + "auth_url": 'fake_auth_url', + "username": 'fake_username', + "password": 'fake_password', + "tenant_name": 'fake_tenant_name' + } + }, + 'from_env' + ) + + @mock.patch('rally.cmd.commands.deployment.DeploymentCommands.list') + @mock.patch('rally.cmd.commands.use.UseCommands.deployment') + @mock.patch('rally.cmd.commands.deployment.api.create_deploy', + return_value=dict(uuid='uuid')) + @mock.patch('rally.cmd.commands.deployment.open', + mock.mock_open(read_data='{"uuid": "uuid"}'), + create=True) + def test_create_and_use(self, mock_create, mock_use_deployment, + mock_list): + self.deployment.create('fake_deploy', False, 'path_to_config.json', + True) + mock_create.assert_called_once_with({'uuid': 'uuid'}, 'fake_deploy') + mock_use_deployment.assert_called_once_with('uuid') + + @mock.patch('rally.cmd.commands.deployment.api.recreate_deploy') + def test_recreate(self, mock_recreate): + deploy_id = str(uuid.uuid4()) + self.deployment.recreate(deploy_id) + mock_recreate.assert_called_once_with(deploy_id) + + @mock.patch('rally.cmd.commands.deployment.envutils.' + '_default_deployment_id') + def test_recreate_no_deploy_id(self, mock_default): + mock_default.side_effect = exceptions.InvalidArgumentsException + self.assertRaises(exceptions.InvalidArgumentsException, + self.deployment.recreate, None) + + @mock.patch('rally.cmd.commands.deployment.api.destroy_deploy') + def test_destroy(self, mock_destroy): + deploy_id = str(uuid.uuid4()) + self.deployment.destroy(deploy_id) + mock_destroy.assert_called_once_with(deploy_id) + + @mock.patch('rally.cmd.commands.deployment.envutils.' + '_default_deployment_id') + def test_destroy_no_deploy_id(self, mock_default): + mock_default.side_effect = exceptions.InvalidArgumentsException + self.assertRaises(exceptions.InvalidArgumentsException, + self.deployment.destroy, None) + + @mock.patch('rally.cmd.commands.deployment.db.deployment_get') + def test_config(self, mock_deployment): + deploy_id = str(uuid.uuid4()) + value = {'config': 'config'} + mock_deployment.return_value = value + self.deployment.config(deploy_id) + mock_deployment.assert_called_once_with(deploy_id) + + @mock.patch('rally.cmd.commands.deployment.envutils.' + '_default_deployment_id') + def test_config_no_deploy_id(self, mock_default): + mock_default.side_effect = exceptions.InvalidArgumentsException + self.assertRaises(exceptions.InvalidArgumentsException, + self.deployment.config, None) + + @mock.patch('rally.cmd.commands.deployment.db.deployment_get') + def test_endpoint(self, mock_deployment): + deploy_id = str(uuid.uuid4()) + value = {'endpoint': {}} + mock_deployment.return_value = value + self.deployment.endpoint(deploy_id) + mock_deployment.assert_called_once_with(deploy_id) + + @mock.patch('rally.cmd.commands.deployment.envutils.' + '_default_deployment_id') + def test_deploy_no_deploy_id(self, mock_default): + mock_default.side_effect = exceptions.InvalidArgumentsException + self.assertRaises(exceptions.InvalidArgumentsException, + self.deployment.endpoint, None) diff --git a/tests/cmd/commands/test_task.py b/tests/cmd/commands/test_task.py new file mode 100644 index 0000000000..15b83472c0 --- /dev/null +++ b/tests/cmd/commands/test_task.py @@ -0,0 +1,121 @@ +# Copyright 2013: Mirantis Inc. +# All Rights Reserved. +# +# 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 mock +import uuid + +from rally.cmd.commands import task +from rally import exceptions +from rally.openstack.common import test + + +class TaskCommandsTestCase(test.BaseTestCase): + + def setUp(self): + super(TaskCommandsTestCase, self).setUp() + self.task = task.TaskCommands() + + @mock.patch('rally.cmd.commands.task.TaskCommands.detailed') + @mock.patch('rally.orchestrator.api.create_task', + return_value=dict(uuid='fc1a9bbe-1ead-4740-92b5-0feecf421634', + created_at='2014-01-14 09:14:45.395822', + status='init', + failed=False)) + @mock.patch('rally.cmd.commands.task.api.start_task') + @mock.patch('rally.cmd.commands.task.open', + mock.mock_open(read_data='{"some": "json"}'), + create=True) + def test_start(self, mock_api, mock_create_task, + mock_task_detailed): + deploy_id = str(uuid.uuid4()) + self.task.start('path_to_config.json', deploy_id) + mock_api.assert_called_once_with(deploy_id, {u'some': u'json'}, + task=mock_create_task.return_value) + + @mock.patch('rally.cmd.commands.task.envutils._default_deployment_id') + def test_start_no_deploy_id(self, mock_default): + mock_default.side_effect = exceptions.InvalidArgumentsException + self.assertRaises(exceptions.InvalidArgumentsException, + self.task.start, 'path_to_config.json', None) + + def test_abort(self): + test_uuid = str(uuid.uuid4()) + with mock.patch("rally.cmd.commands.task.api") as mock_api: + mock_api.abort_task = mock.MagicMock() + self.task.abort(test_uuid) + task.api.abort_task.assert_called_once_with(test_uuid) + + def test_status(self): + test_uuid = str(uuid.uuid4()) + value = {'task_id': "task", "status": "status"} + with mock.patch("rally.cmd.commands.task.db") as mock_db: + mock_db.task_get = mock.MagicMock(return_value=value) + self.task.status(test_uuid) + mock_db.task_get.assert_called_once_with(test_uuid) + + @mock.patch('rally.cmd.commands.task.db') + def test_detailed(self, mock_db): + test_uuid = str(uuid.uuid4()) + value = {'task_id': "task", + "status": "status", + "results": [], + "failed": False + } + mock_db.task_get_detailed = mock.MagicMock(return_value=value) + self.task.detailed(test_uuid) + mock_db.task_get_detailed.assert_called_once_with(test_uuid) + + @mock.patch('rally.cmd.commands.task.db') + def test_detailed_wrong_id(self, mock_db): + test_uuid = str(uuid.uuid4()) + mock_db.task_get_detailed = mock.MagicMock(return_value=None) + self.task.detailed(test_uuid) + mock_db.task_get_detailed.assert_called_once_with(test_uuid) + + @mock.patch('rally.cmd.commands.task.db') + def test_results(self, mock_db): + test_uuid = str(uuid.uuid4()) + value = [ + {'key': 'key', 'data': {'raw': 'raw'}} + ] + mock_db.task_result_get_all_by_uuid.return_value = value + self.task.results(test_uuid) + mock_db.task_result_get_all_by_uuid.assert_called_once_with(test_uuid) + + def test_list(self): + db_response = [ + {'uuid': 'a', 'created_at': 'b', 'status': 'c', 'failed': True} + ] + with mock.patch("rally.cmd.commands.task.db") as mock_db: + mock_db.task_list = mock.MagicMock(return_value=db_response) + self.task.list() + mock_db.task_list.assert_called_once_with() + + def test_delete(self): + task_uuid = str(uuid.uuid4()) + force = False + with mock.patch("rally.cmd.commands.task.api") as mock_api: + mock_api.delete_task = mock.Mock() + self.task.delete(task_uuid, force) + mock_api.delete_task.assert_called_once_with(task_uuid, + force=force) + + def test_plot(self): + test_uuid = str(uuid.uuid4()) + mock_plot = mock.Mock() + PLOTS = {"aggregated": mock_plot} + with mock.patch("rally.cmd.commands.task.processing.PLOTS", new=PLOTS): + self.task.plot("aggregated", "concurrent", test_uuid) + mock_plot.assert_called_once_with(test_uuid, "concurrent") diff --git a/tests/cmd/commands/test_use.py b/tests/cmd/commands/test_use.py new file mode 100644 index 0000000000..81b9b35261 --- /dev/null +++ b/tests/cmd/commands/test_use.py @@ -0,0 +1,58 @@ +# Copyright 2013: Mirantis Inc. +# All Rights Reserved. +# +# 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 mock +import os +import uuid + +from rally.cmd.commands import use +from rally.openstack.common import test + + +class UseCommandsTestCase(test.BaseTestCase): + def setUp(self): + super(UseCommandsTestCase, self).setUp() + self.use = use.UseCommands() + + @mock.patch('os.remove') + @mock.patch('os.symlink') + @mock.patch('rally.cmd.commands.use.db.deployment_get') + @mock.patch('os.path.exists') + @mock.patch('rally.cmd.commands.use.fileutils.update_env_file') + def test_deployment(self, mock_env, mock_path, mock_deployment, + mock_symlink, mock_remove): + deploy_id = str(uuid.uuid4()) + endpoint = {'endpoint': {'auth_url': 'fake_auth_url', + 'username': 'fake_username', + 'password': 'fake_password', + 'tenant_name': 'fake_tenant_name'}} + mock_deployment.return_value = endpoint + mock_path.return_value = True + with mock.patch('rally.cmd.commands.use.open', mock.mock_open(), + create=True) as mock_file: + self.use.deployment(deploy_id) + self.assertEqual(2, mock_path.call_count) + mock_env.assert_called_once_with(os.path.expanduser( + '~/.rally/deployment'), 'RALLY_DEPLOYMENT', deploy_id) + mock_file.return_value.write.assert_called_once_with( + 'export OS_AUTH_URL=fake_auth_url\n' + 'export OS_USERNAME=fake_username\n' + 'export OS_PASSWORD=fake_password\n' + 'export OS_TENANT_NAME=fake_tenant_name\n') + mock_symlink.assert_called_once_with( + os.path.expanduser('~/.rally/openrc-%s' % deploy_id), + os.path.expanduser('~/.rally/openrc')) + mock_remove.assert_called_once_with(os.path.expanduser( + '~/.rally/openrc')) diff --git a/tests/cmd/test_main.py b/tests/cmd/test_main.py deleted file mode 100644 index f75d8fe272..0000000000 --- a/tests/cmd/test_main.py +++ /dev/null @@ -1,259 +0,0 @@ -# Copyright 2013: Mirantis Inc. -# All Rights Reserved. -# -# 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 mock -import os -import uuid - -from rally.cmd import main -from rally import exceptions -from rally.openstack.common import test - - -class TaskCommandsTestCase(test.BaseTestCase): - - def setUp(self): - super(TaskCommandsTestCase, self).setUp() - self.task = main.TaskCommands() - - @mock.patch('rally.cmd.main.TaskCommands.detailed') - @mock.patch('rally.orchestrator.api.create_task', - return_value=dict(uuid='fc1a9bbe-1ead-4740-92b5-0feecf421634', - created_at='2014-01-14 09:14:45.395822', - status='init', - failed=False)) - @mock.patch('rally.cmd.main.api.start_task') - @mock.patch('rally.cmd.main.open', - mock.mock_open(read_data='{"some": "json"}'), - create=True) - def test_start(self, mock_api, mock_create_task, - mock_task_detailed): - deploy_id = str(uuid.uuid4()) - self.task.start('path_to_config.json', deploy_id) - mock_api.assert_called_once_with(deploy_id, {u'some': u'json'}, - task=mock_create_task.return_value) - - @mock.patch('rally.cmd.main.envutils._default_deployment_id') - def test_start_no_deploy_id(self, mock_default): - mock_default.side_effect = exceptions.InvalidArgumentsException - self.assertRaises(exceptions.InvalidArgumentsException, - self.task.start, 'path_to_config.json', None) - - def test_abort(self): - test_uuid = str(uuid.uuid4()) - with mock.patch("rally.cmd.main.api") as mock_api: - mock_api.abort_task = mock.MagicMock() - self.task.abort(test_uuid) - main.api.abort_task.assert_called_once_with(test_uuid) - - def test_status(self): - test_uuid = str(uuid.uuid4()) - value = {'task_id': "task", "status": "status"} - with mock.patch("rally.cmd.main.db") as mock_db: - mock_db.task_get = mock.MagicMock(return_value=value) - self.task.status(test_uuid) - mock_db.task_get.assert_called_once_with(test_uuid) - - @mock.patch('rally.cmd.main.db') - def test_detailed(self, mock_db): - test_uuid = str(uuid.uuid4()) - value = {'task_id': "task", - "status": "status", - "results": [], - "failed": False - } - mock_db.task_get_detailed = mock.MagicMock(return_value=value) - self.task.detailed(test_uuid) - mock_db.task_get_detailed.assert_called_once_with(test_uuid) - - @mock.patch('rally.cmd.main.db') - def test_detailed_wrong_id(self, mock_db): - test_uuid = str(uuid.uuid4()) - mock_db.task_get_detailed = mock.MagicMock(return_value=None) - self.task.detailed(test_uuid) - mock_db.task_get_detailed.assert_called_once_with(test_uuid) - - @mock.patch('rally.cmd.main.db') - def test_results(self, mock_db): - test_uuid = str(uuid.uuid4()) - value = [ - {'key': 'key', 'data': {'raw': 'raw'}} - ] - mock_db.task_result_get_all_by_uuid.return_value = value - self.task.results(test_uuid) - mock_db.task_result_get_all_by_uuid.assert_called_once_with(test_uuid) - - def test_list(self): - db_response = [ - {'uuid': 'a', 'created_at': 'b', 'status': 'c', 'failed': True} - ] - with mock.patch("rally.cmd.main.db") as mock_db: - mock_db.task_list = mock.MagicMock(return_value=db_response) - self.task.list() - mock_db.task_list.assert_called_once_with() - - def test_delete(self): - task_uuid = str(uuid.uuid4()) - force = False - with mock.patch("rally.cmd.main.api") as mock_api: - mock_api.delete_task = mock.Mock() - self.task.delete(task_uuid, force) - mock_api.delete_task.assert_called_once_with(task_uuid, - force=force) - - def test_plot(self): - test_uuid = str(uuid.uuid4()) - mock_plot = mock.Mock() - PLOTS = {"aggregated": mock_plot} - with mock.patch("rally.cmd.main.processing.PLOTS", new=PLOTS): - self.task.plot("aggregated", "concurrent", test_uuid) - mock_plot.assert_called_once_with(test_uuid, "concurrent") - - -class DeploymentCommandsTestCase(test.BaseTestCase): - def setUp(self): - super(DeploymentCommandsTestCase, self).setUp() - self.deployment = main.DeploymentCommands() - - @mock.patch('rally.cmd.main.api.create_deploy') - @mock.patch('rally.cmd.main.open', - mock.mock_open(read_data='{"some": "json"}'), - create=True) - def test_create(self, mock_create): - self.deployment.create('fake_deploy', False, 'path_to_config.json') - mock_create.assert_called_once_with({'some': 'json'}, 'fake_deploy') - - @mock.patch.dict(os.environ, {'OS_AUTH_URL': 'fake_auth_url', - 'OS_USERNAME': 'fake_username', - 'OS_PASSWORD': 'fake_password', - 'OS_TENANT_NAME': 'fake_tenant_name'}) - @mock.patch('rally.cmd.main.api.create_deploy') - def test_createfromenv(self, mock_create): - self.deployment.create('from_env', True) - mock_create.assert_called_once_with( - { - "name": "DummyEngine", - "endpoint": { - "auth_url": 'fake_auth_url', - "username": 'fake_username', - "password": 'fake_password', - "tenant_name": 'fake_tenant_name' - } - }, - 'from_env' - ) - - @mock.patch('rally.cmd.main.DeploymentCommands.list') - @mock.patch('rally.cmd.main.UseCommands.deployment') - @mock.patch('rally.cmd.main.api.create_deploy', - return_value=dict(uuid='uuid')) - @mock.patch('rally.cmd.main.open', - mock.mock_open(read_data='{"uuid": "uuid"}'), - create=True) - def test_create_and_use(self, mock_create, mock_use_deployment, - mock_list): - self.deployment.create('fake_deploy', False, 'path_to_config.json', - True) - mock_create.assert_called_once_with({'uuid': 'uuid'}, 'fake_deploy') - mock_use_deployment.assert_called_once_with('uuid') - - @mock.patch('rally.cmd.main.api.recreate_deploy') - def test_recreate(self, mock_recreate): - deploy_id = str(uuid.uuid4()) - self.deployment.recreate(deploy_id) - mock_recreate.assert_called_once_with(deploy_id) - - @mock.patch('rally.cmd.main.envutils._default_deployment_id') - def test_recreate_no_deploy_id(self, mock_default): - mock_default.side_effect = exceptions.InvalidArgumentsException - self.assertRaises(exceptions.InvalidArgumentsException, - self.deployment.recreate, None) - - @mock.patch('rally.cmd.main.api.destroy_deploy') - def test_destroy(self, mock_destroy): - deploy_id = str(uuid.uuid4()) - self.deployment.destroy(deploy_id) - mock_destroy.assert_called_once_with(deploy_id) - - @mock.patch('rally.cmd.main.envutils._default_deployment_id') - def test_destroy_no_deploy_id(self, mock_default): - mock_default.side_effect = exceptions.InvalidArgumentsException - self.assertRaises(exceptions.InvalidArgumentsException, - self.deployment.destroy, None) - - @mock.patch('rally.cmd.main.db.deployment_get') - def test_config(self, mock_deployment): - deploy_id = str(uuid.uuid4()) - value = {'config': 'config'} - mock_deployment.return_value = value - self.deployment.config(deploy_id) - mock_deployment.assert_called_once_with(deploy_id) - - @mock.patch('rally.cmd.main.envutils._default_deployment_id') - def test_config_no_deploy_id(self, mock_default): - mock_default.side_effect = exceptions.InvalidArgumentsException - self.assertRaises(exceptions.InvalidArgumentsException, - self.deployment.config, None) - - @mock.patch('rally.cmd.main.db.deployment_get') - def test_endpoint(self, mock_deployment): - deploy_id = str(uuid.uuid4()) - value = {'endpoint': {}} - mock_deployment.return_value = value - self.deployment.endpoint(deploy_id) - mock_deployment.assert_called_once_with(deploy_id) - - @mock.patch('rally.cmd.main.envutils._default_deployment_id') - def test_deploy_no_deploy_id(self, mock_default): - mock_default.side_effect = exceptions.InvalidArgumentsException - self.assertRaises(exceptions.InvalidArgumentsException, - self.deployment.endpoint, None) - - -class UseCommandsTestCase(test.BaseTestCase): - def setUp(self): - super(UseCommandsTestCase, self).setUp() - self.use = main.UseCommands() - - @mock.patch('os.remove') - @mock.patch('os.symlink') - @mock.patch('rally.cmd.main.db.deployment_get') - @mock.patch('os.path.exists') - @mock.patch('rally.cmd.main.fileutils.update_env_file') - def test_deployment(self, mock_env, mock_path, mock_deployment, - mock_symlink, mock_remove): - deploy_id = str(uuid.uuid4()) - endpoint = {'endpoint': {'auth_url': 'fake_auth_url', - 'username': 'fake_username', - 'password': 'fake_password', - 'tenant_name': 'fake_tenant_name'}} - mock_deployment.return_value = endpoint - mock_path.return_value = True - with mock.patch('rally.cmd.main.open', mock.mock_open(), - create=True) as mock_file: - self.use.deployment(deploy_id) - self.assertEqual(2, mock_path.call_count) - mock_env.assert_called_once_with(os.path.expanduser( - '~/.rally/deployment'), 'RALLY_DEPLOYMENT', deploy_id) - mock_file.return_value.write.assert_called_once_with( - 'export OS_AUTH_URL=fake_auth_url\n' - 'export OS_USERNAME=fake_username\n' - 'export OS_PASSWORD=fake_password\n' - 'export OS_TENANT_NAME=fake_tenant_name\n') - mock_symlink.assert_called_once_with( - os.path.expanduser('~/.rally/openrc-%s' % deploy_id), - os.path.expanduser('~/.rally/openrc')) - mock_remove.assert_called_once_with(os.path.expanduser( - '~/.rally/openrc'))