Add rally env cleanup command

This command should trigger cleanup of resources for the specified
environment.

Change-Id: I4c638d9a5ae5caadf0e5b9cdb78ec589e770cddf
This commit is contained in:
Andrey Kurilin 2018-07-12 18:11:43 +03:00
parent 760e926643
commit 0b110cb2c0
4 changed files with 137 additions and 6 deletions

View File

@ -20,6 +20,11 @@ Changelog
[Unreleased] [Unreleased]
------------ ------------
Added
~~~~~
* Introducing ``rally env cleanup`` command for performing disaster cleanup.
Changed Changed
~~~~~~~ ~~~~~~~
@ -30,10 +35,11 @@ Changed
allows us to show only those workloads which we are interested in (see the allows us to show only those workloads which we are interested in (see the
examples below). examples below).
Examples: Examples:
1. show only failed workloads
``rally task detailed --filter-by sla-failures`` 1. show only failed workloads
2. show only those workloads which include the next scenario plugin(s) ``rally task detailed --filter-by sla-failures``
``rally task detailed --filter-by scenarios=scenario1[,scenarios2...]`` 2. show only those workloads which include the next scenario plugin(s)
``rally task detailed --filter-by scenarios=scenario1[,scenarios2...]``
Removed Removed
~~~~~~~ ~~~~~~~

View File

@ -33,6 +33,7 @@ _rally()
OPTS["deployment_show"]="--deployment" OPTS["deployment_show"]="--deployment"
OPTS["deployment_use"]="--deployment" OPTS["deployment_use"]="--deployment"
OPTS["env_check"]="--env --json --detailed" OPTS["env_check"]="--env --json --detailed"
OPTS["env_cleanup"]="--json --env"
OPTS["env_create"]="--name --description --extras --from-sysenv --spec --json --no-use" OPTS["env_create"]="--name --description --extras --from-sysenv --spec --json --no-use"
OPTS["env_delete"]="--env --force" OPTS["env_delete"]="--env --force"
OPTS["env_destroy"]="--env --skip-cleanup --json --detailed" OPTS["env_destroy"]="--env --skip-cleanup --json --detailed"

View File

@ -101,6 +101,48 @@ class EnvCommands(object):
self._show(env.data, to_json=to_json, only_spec=False) self._show(env.data, to_json=to_json, only_spec=False)
return 0 return 0
@cliutils.args("--json", action="store_true", dest="to_json",
help="Format output as JSON.")
@cliutils.args("--env", dest="env", type=str,
metavar="<uuid>", required=False,
help="UUID or name of the env.")
@envutils.with_default_env()
def cleanup(self, api, env=None, to_json=False):
"""Perform disaster cleanup for specified environment.
Cases when Rally can leave undeleted resources after performing
workload:
- Rally execution was interrupted and cleanup was not performed
- The environment or a particular platform became unreachable which
fail Rally execution of cleanup
"""
env = env_mgr.EnvManager.get(env)
_print("Cleaning up resources for %s" % env, to_json)
result = env.cleanup()
if to_json:
print(json.dumps(result, indent=2))
return int(any([p["errors"] for p in result.values()]))
print("Cleaning is finished. See the results bellow.")
return_code = 0
for platform in sorted(result):
cleanup_info = result[platform]
print("\nInformation for %s platform." % platform)
print("=" * 80)
print("Status: %s" % cleanup_info["message"])
for key in ("discovered", "deleted", "failed"):
print("Total %s: %s" % (key, cleanup_info[key]))
if cleanup_info["errors"]:
return_code = 1
errors = "\t- ".join(e["message"]
for e in cleanup_info["errors"])
print("Errors:\n\t- %s" % errors)
return return_code
@cliutils.args("--env", dest="env", type=str, @cliutils.args("--env", dest="env", type=str,
metavar="<uuid>", required=False, metavar="<uuid>", required=False,
help="UUID or name of the env.") help="UUID or name of the env.")
@ -124,6 +166,7 @@ class EnvCommands(object):
% (NO, env, result["destroy_info"]["message"]), to_json) % (NO, env, result["destroy_info"]["message"]), to_json)
else: else:
_print("%s Successfully destroyed env %s" % (YES, env), to_json) _print("%s Successfully destroyed env %s" % (YES, env), to_json)
if detailed or to_json: if detailed or to_json:
print(json.dumps(result, indent=2)) print(json.dumps(result, indent=2))
@ -136,7 +179,7 @@ class EnvCommands(object):
help="Delete DB records even if env is not destroyed.") help="Delete DB records even if env is not destroyed.")
@envutils.with_default_env() @envutils.with_default_env()
def delete(self, api, env=None, force=False): def delete(self, api, env=None, force=False):
"""Deletes all records related to Env from db.""" """Deletes all records related to the environment from db."""
env_mgr.EnvManager.get(env).delete(force=force) env_mgr.EnvManager.get(env).delete(force=force)
# TODO(boris-42): clear env variables if default one is deleted # TODO(boris-42): clear env variables if default one is deleted
@ -196,6 +239,7 @@ class EnvCommands(object):
@cliutils.suppress_warnings @cliutils.suppress_warnings
@envutils.with_default_env() @envutils.with_default_env()
def show(self, api, env=None, to_json=False, only_spec=False): def show(self, api, env=None, to_json=False, only_spec=False):
"""Show base information about the environment record."""
env_data = env_mgr.EnvManager.get(env).data env_data = env_mgr.EnvManager.get(env).data
self._show(env_data, to_json=to_json, only_spec=only_spec) self._show(env_data, to_json=to_json, only_spec=only_spec)
@ -206,7 +250,7 @@ class EnvCommands(object):
help="Format output as JSON.") help="Format output as JSON.")
@envutils.with_default_env() @envutils.with_default_env()
def info(self, api, env=None, to_json=False): def info(self, api, env=None, to_json=False):
"""Show environment information.""" """Retrieve and show environment information."""
env = env_mgr.EnvManager.get(env) env = env_mgr.EnvManager.get(env)
env_info = env.get_info() env_info = env.get_info()
return_code = int(any(v.get("error") for v in env_info.values())) return_code = int(any(v.get("error") for v in env_info.values()))

View File

@ -153,6 +153,86 @@ class EnvCommandsTestCase(test.TestCase):
mock.call(mock.ANY) mock.call(mock.ANY)
]) ])
@mock.patch("rally.env.env_mgr.EnvManager.get")
@mock.patch("rally.cli.commands.env.print")
def test_cleanup(self, mock_print, mock_env_manager_get):
env_ = mock.Mock()
env_inst = mock_env_manager_get.return_value
env_inst.cleanup.return_value = {
"existing@docker": {
"message": "Success",
"discovered": 5,
"deleted": 5,
"failed": 0,
"errors": []
},
"existing@openstack": {
"message": "It is OpenStack. several failures are ok :)",
"discovered": 10,
"deleted": 8,
"failed": 2,
"errors": [
{"message": "Port disappeared",
"traceback": "traceback"}
]
}
}
self.assertEqual(1, self.env.cleanup(self.api, env_))
mock_env_manager_get.assert_called_once_with(env_)
env_inst.cleanup.assert_called_once_with()
actual_print = "\n".join(
[call_args[0]
for call_args, _call_kwargs in mock_print.call_args_list])
expected_print = (
"Cleaning up resources for %(env)s\n"
"Cleaning is finished. See the results bellow.\n"
"\n"
"Information for existing@docker platform.\n"
"%(hr)s\n"
"Status: Success\n"
"Total discovered: 5\n"
"Total deleted: 5\n"
"Total failed: 0\n"
"\n"
"Information for existing@openstack platform.\n"
"%(hr)s\n"
"Status: It is OpenStack. several failures are ok :)\n"
"Total discovered: 10\n"
"Total deleted: 8\n"
"Total failed: 2\n"
"Errors:\n"
"\t- Port disappeared" % {"env": env_inst, "hr": "=" * 80})
self.assertEqual(expected_print, actual_print)
@mock.patch("rally.env.env_mgr.EnvManager.get")
@mock.patch("rally.cli.commands.env.print")
def test_cleanup_to_json(self, mock_print, mock_env_manager_get):
env_ = mock.Mock()
env_inst = mock_env_manager_get.return_value
env_inst.cleanup.return_value = {
"existing@docker": {
"message": "Success",
"discovered": 5,
"deleted": 5,
"failed": 0,
"errors": []
},
"existing@openstack": {
"message": "It is OpenStack. several failures are ok :)",
"discovered": 10,
"deleted": 8,
"failed": 2,
"errors": [
{"message": "Port disappeared",
"traceback": "traceback"}
]
}
}
self.assertEqual(1, self.env.cleanup(self.api, env_, to_json=True))
mock_print.assert_called_once_with(
json.dumps(env_inst.cleanup.return_value, indent=2))
@mock.patch("rally.env.env_mgr.EnvManager.get") @mock.patch("rally.env.env_mgr.EnvManager.get")
@mock.patch("rally.cli.commands.env.print") @mock.patch("rally.cli.commands.env.print")
def test_destroy(self, mock_print, mock_env_manager_get): def test_destroy(self, mock_print, mock_env_manager_get):