Move every rally command to its own module
Move all the currently supported rally commands (i.e. deployment, task, use) and their equivalent tests that are described as classes in the 'main.py' module (and 'test_main.py' module), to separate modules in a new directory. This is done for organizing the code of the rally command line utility, before it become too big. Implements: blueprint rally-show-command Change-Id: I5982f854dfe64604ae112d93fb7ea68f72657b98
This commit is contained in:
parent
ec32cd04ce
commit
d674da2222
0
rally/cmd/commands/__init__.py
Normal file
0
rally/cmd/commands/__init__.py
Normal file
173
rally/cmd/commands/deployment.py
Normal file
173
rally/cmd/commands/deployment.py
Normal file
@ -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)
|
240
rally/cmd/commands/task.py
Normal file
240
rally/cmd/commands/task.py
Normal file
@ -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)
|
53
rally/cmd/commands/use.py
Normal file
53
rally/cmd/commands/use.py
Normal file
@ -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)
|
@ -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)
|
||||
|
||||
|
0
tests/cmd/commands/__init__.py
Normal file
0
tests/cmd/commands/__init__.py
Normal file
126
tests/cmd/commands/test_deployment.py
Normal file
126
tests/cmd/commands/test_deployment.py
Normal file
@ -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)
|
121
tests/cmd/commands/test_task.py
Normal file
121
tests/cmd/commands/test_task.py
Normal file
@ -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")
|
58
tests/cmd/commands/test_use.py
Normal file
58
tests/cmd/commands/test_use.py
Normal file
@ -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'))
|
@ -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'))
|
Loading…
Reference in New Issue
Block a user