diff --git a/etc/rally/rally.conf.sample b/etc/rally/rally.conf.sample index c29a6be1..fe912439 100644 --- a/etc/rally/rally.conf.sample +++ b/etc/rally/rally.conf.sample @@ -509,6 +509,11 @@ # Time to wait for a VM to become pingable (floating point value) #vm_ping_timeout = 120.0 +# Watcher audit launch interval (floating point value) +#watcher_audit_launch_poll_interval = 2.0 + +# Watcher audit launch timeout (integer value) +#watcher_audit_launch_timeout = 300 [cleanup] @@ -712,4 +717,4 @@ #project_domain = default # ID of domain in which users will be created. (string value) -#user_domain = default +#user_domain = default \ No newline at end of file diff --git a/rally-jobs/rally-watcher.yaml b/rally-jobs/rally-watcher.yaml index d4f8cc4e..b0f5e5eb 100644 --- a/rally-jobs/rally-watcher.yaml +++ b/rally-jobs/rally-watcher.yaml @@ -1,4 +1,27 @@ --- + Watcher.create_audit_and_delete: + - + runner: + type: "constant" + times: 10 + concurrency: 2 + context: + users: + tenants: 2 + users_per_tenant: 2 + audit_templates: + audit_templates_per_admin: 5 + fill_strategy: "round_robin" + params: + - goal: + name: "dummy" + strategy: + name: "dummy" + extra: {} + sla: + failure_rate: + max: 0 + Watcher.create_audit_template_and_delete: - args: @@ -7,14 +30,38 @@ strategy: name: "dummy" extra: {} + runner: + type: "constant" + times: 10 + concurrency: 2 + sla: + failure_rate: + max: 0 + + Watcher.list_audit_templates: + - runner: type: "constant" times: 10 concurrency: 2 context: users: - tenants: 3 + tenants: 2 users_per_tenant: 2 + audit_templates: + audit_templates_per_admin: 5 + fill_strategy: "random" + params: + - goal: + name: "workload_balancing" + strategy: + name: "workload_stabilization" + extra: {} + - goal: + name: "dummy" + strategy: + name: "dummy" + extra: {} sla: failure_rate: max: 0 \ No newline at end of file diff --git a/rally/plugins/openstack/cleanup/resources.py b/rally/plugins/openstack/cleanup/resources.py index ca5c2a2f..9f76bb7c 100644 --- a/rally/plugins/openstack/cleanup/resources.py +++ b/rally/plugins/openstack/cleanup/resources.py @@ -753,13 +753,17 @@ class FuelEnvironment(base.ResourceManager): # WATCHER -@base.resource("watcher", "audit_template", order=1500, - admin_required=True, tenant_resource=True) -class WatcherTemplate(SynchronizedDeletion, base.ResourceManager): +_watcher_order = get_order(1500) + + +class WatcherMixin(SynchronizedDeletion, base.ResourceManager): def id(self): return self.raw_resource.uuid + def list(self): + return self._manager().list(limit=0) + def is_deleted(self): from watcherclient.common.apiclient import exceptions try: @@ -768,8 +772,27 @@ class WatcherTemplate(SynchronizedDeletion, base.ResourceManager): except exceptions.NotFound: return True - def list(self): - return self._manager().list(limit=0) + +@base.resource("watcher", "audit_template", order=next(_watcher_order), + admin_required=True, perform_for_admin_only=True) +class WatcherTemplate(WatcherMixin): + pass + + +@base.resource("watcher", "action_plan", order=next(_watcher_order), + admin_required=True, perform_for_admin_only=True) +class WatcherActionPlan(WatcherMixin): + + def name(self): + return self.raw_resource.uuid + + +@base.resource("watcher", "audit", order=next(_watcher_order), + admin_required=True, perform_for_admin_only=True) +class WatcherAudit(WatcherMixin): + + def name(self): + return self.raw_resource.uuid # KEYSTONE diff --git a/rally/plugins/openstack/context/watcher/__init__.py b/rally/plugins/openstack/context/watcher/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/rally/plugins/openstack/context/watcher/audit_templates.py b/rally/plugins/openstack/context/watcher/audit_templates.py new file mode 100644 index 00000000..a9cbb45d --- /dev/null +++ b/rally/plugins/openstack/context/watcher/audit_templates.py @@ -0,0 +1,113 @@ +# 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 random + +import six + +from rally.common.i18n import _ +from rally.common import logging +from rally import consts +from rally import osclients +from rally.plugins.openstack.cleanup import manager as resource_manager +from rally.plugins.openstack.scenarios.watcher import utils as watcher_utils +from rally.plugins.openstack import types +from rally.task import context + + +LOG = logging.getLogger(__name__) + + +@context.configure(name="audit_templates", order=550) +class AuditTemplateGenerator(context.Context): + """Context class for adding temporary audit template for benchmarks.""" + + CONFIG_SCHEMA = { + "type": "object", + "$schema": consts.JSON_SCHEMA, + "fill_strategy": {"enum": ["round_robin", "random", None]}, + "params": { + "type": "array", + "minItems": 1, + "uniqueItems": True, + "items": { + "type": "object", + "properties": { + "goal": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + } + }, + "strategy": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + } + }, + "extra": { + "type": "object" + }, + }, + }, + }, + "additionalProperties": True, + "required": ["params"] + } + + DEFAULT_CONFIG = { + "audit_templates_per_admin": 1, + "fill_strategy": "round_robin" + } + + @logging.log_task_wrapper(LOG.info, _("Enter context: `Audit Templates`")) + def setup(self): + watcher_scenario = watcher_utils.WatcherScenario( + {"admin": self.context["admin"], "task": self.context["task"], + "config": { + "api_versions": self.context["config"].get( + "api_versions", [])} + }) + + clients = osclients.Clients(self.context["admin"]["credential"]) + + self.context["audit_templates"] = [] + for i in six.moves.range(self.config["audit_templates_per_admin"]): + cfg_size = len(self.config["params"]) + if self.config["fill_strategy"] == "round_robin": + audit_params = self.config["params"][i % cfg_size] + elif self.config["fill_strategy"] == "random": + audit_params = random.choice(self.config["params"]) + + goal_id = types.WatcherGoal.transform( + clients=clients, + resource_config=audit_params["goal"]) + strategy_id = types.WatcherStrategy.transform( + clients=clients, + resource_config=audit_params["strategy"]) + extra = audit_params.get("extra") or {} + + audit_template = watcher_scenario._create_audit_template( + goal_id, strategy_id, extra) + self.context["audit_templates"].append(audit_template.uuid) + + @logging.log_task_wrapper(LOG.info, _("Exit context: `Audit Templates`")) + def cleanup(self): + resource_manager.cleanup(names=["watcher.action_plan", + "watcher.audit_template"], + admin=self.context.get("admin", [])) diff --git a/rally/plugins/openstack/scenarios/watcher/basic.py b/rally/plugins/openstack/scenarios/watcher/basic.py index cd19d6c8..bd153562 100644 --- a/rally/plugins/openstack/scenarios/watcher/basic.py +++ b/rally/plugins/openstack/scenarios/watcher/basic.py @@ -38,3 +38,47 @@ class Watcher(utils.WatcherScenario): extra = extra or {} audit_template = self._create_audit_template(goal, strategy, extra) self._delete_audit_template(audit_template.uuid) + + @validation.required_services(consts.Service.WATCHER) + @scenario.configure() + def list_audit_templates(self, name=None, goal=None, strategy=None, + limit=None, sort_key=None, sort_dir=None, + detail=False): + """List existing audit templates. + + Audit templates are being created by Audit Template Context. + + :param name: Name of the audit template + :param goal: Name of the goal + :param strategy: Name of the strategy + :param limit: The maximum number of results to return per + request, if: + 1) limit > 0, the maximum number of audit templates to return. + 2) limit == 0, return the entire list of audit_templates. + 3) limit param is NOT specified (None), the number of items + returned respect the maximum imposed by the Watcher API + (see Watcher's api.max_limit option). + :param sort_key: Optional, field used for sorting. + :param sort_dir: Optional, direction of sorting, either 'asc' (the + default) or 'desc'. + :param detail: Optional, boolean whether to return detailed information + about audit_templates. + """ + + self._list_audit_templates(name=name, goal=goal, strategy=strategy, + limit=limit, sort_key=sort_key, + sort_dir=sort_dir, detail=detail) + + @validation.required_services(consts.Service.WATCHER) + @validation.required_contexts("audit_templates") + @scenario.configure(context={"admin_cleanup": ["watcher"]}) + def create_audit_and_delete(self): + """Create and delete audit. + + Create Audit, wait until whether Audit is in SUCCEEDED state or in + FAILED and delete audit. + """ + + audit_template_uuid = self.context["audit_templates"][0] + audit = self._create_audit(audit_template_uuid) + self._delete_audit(audit) diff --git a/rally/plugins/openstack/scenarios/watcher/utils.py b/rally/plugins/openstack/scenarios/watcher/utils.py index e11d4007..3f56595d 100644 --- a/rally/plugins/openstack/scenarios/watcher/utils.py +++ b/rally/plugins/openstack/scenarios/watcher/utils.py @@ -10,8 +10,24 @@ # License for the specific language governing permissions and limitations # under the License. +from oslo_config import cfg + from rally.plugins.openstack import scenario from rally.task import atomic +from rally.task import utils + +CONF = cfg.CONF + + +WATCHER_BENCHMARK_OPTS = [ + cfg.FloatOpt("watcher_audit_launch_poll_interval", default=2.0, + help="Watcher audit launch interval"), + cfg.IntOpt("watcher_audit_launch_timeout", default=300, + help="Watcher audit launch timeout") +] + +benchmark_group = cfg.OptGroup(name="benchmark", title="benchmark options") +CONF.register_opts(WATCHER_BENCHMARK_OPTS, group=benchmark_group) class WatcherScenario(scenario.OpenStackScenario): @@ -39,3 +55,32 @@ class WatcherScenario(scenario.OpenStackScenario): :param audit_template: Audit Template object """ self.admin_clients("watcher").audit_template.delete(audit_template) + + @atomic.action_timer("watcher.list_audit_templates") + def _list_audit_templates(self, name=None, goal=None, strategy=None, + limit=None, sort_key=None, sort_dir=None, + detail=False): + return self.admin_clients("watcher").audit_template.list( + name=name, goal=goal, strategy=strategy, limit=limit, + sort_key=sort_key, sort_dir=sort_dir, detail=detail) + + @atomic.action_timer("watcher.create_audit") + def _create_audit(self, audit_template_uuid): + audit = self.admin_clients("watcher").audit.create( + audit_template_uuid=audit_template_uuid, + audit_type="ONESHOT") + utils.wait_for_status( + audit, + ready_statuses=["SUCCEEDED"], + failure_statuses=["FAILED"], + status_attr="state", + update_resource=utils.get_from_manager(), + timeout=CONF.benchmark.watcher_audit_launch_timeout, + check_interval=CONF.benchmark.watcher_audit_launch_poll_interval, + id_attr="uuid" + ) + return audit + + @atomic.action_timer("watcher.delete_audit") + def _delete_audit(self, audit): + self.admin_clients("watcher").audit.delete(audit.uuid) diff --git a/samples/tasks/scenarios/watcher/create-audit-and-delete.json b/samples/tasks/scenarios/watcher/create-audit-and-delete.json new file mode 100644 index 00000000..37b66cb3 --- /dev/null +++ b/samples/tasks/scenarios/watcher/create-audit-and-delete.json @@ -0,0 +1,32 @@ +{ + "Watcher.create_audit_and_delete": [ + { + "runner": { + "type": "constant", + "times": 10, + "concurrency": 2 + }, + "context": { + "users": { + "tenants": 2, + "users_per_tenant": 2 + }, + "audit_templates": { + "audit_templates_per_admin": 5, + "fill_strategy": "round_robin", + "params": [ + { + "goal": { + "name": "dummy" + }, + "strategy": { + "name": "dummy" + }, + "extra": {} + } + ] + } + } + } + ] +} diff --git a/samples/tasks/scenarios/watcher/create-audit-and-delete.yaml b/samples/tasks/scenarios/watcher/create-audit-and-delete.yaml new file mode 100644 index 00000000..9f845803 --- /dev/null +++ b/samples/tasks/scenarios/watcher/create-audit-and-delete.yaml @@ -0,0 +1,20 @@ +--- + Watcher.create_audit_and_delete: + - + runner: + type: "constant" + times: 10 + concurrency: 2 + context: + users: + tenants: 2 + users_per_tenant: 2 + audit_templates: + audit_templates_per_admin: 5 + fill_strategy: "round_robin" + params: + - goal: + name: "dummy" + strategy: + name: "dummy" + extra: {} diff --git a/samples/tasks/scenarios/watcher/list-audit-templates.json b/samples/tasks/scenarios/watcher/list-audit-templates.json new file mode 100644 index 00000000..4198c502 --- /dev/null +++ b/samples/tasks/scenarios/watcher/list-audit-templates.json @@ -0,0 +1,37 @@ +{ + "Watcher.list_audit_templates": [ + { + "runner": { + "type": "constant", + "times": 10, + "concurrency": 1 + }, + "context": { + "audit_templates": { + "audit_templates_per_admin": 5, + "fill_strategy": "random", + "params": [ + { + "goal": { + "name": "workload_balancing" + }, + "strategy": { + "name": "workload_stabilization" + }, + "extra": {} + }, + { + "goal": { + "name": "dummy" + }, + "strategy": { + "name": "dummy" + }, + "extra": {} + } + ] + } + } + } + ] +} diff --git a/samples/tasks/scenarios/watcher/list-audit-templates.yaml b/samples/tasks/scenarios/watcher/list-audit-templates.yaml new file mode 100644 index 00000000..6728f6d0 --- /dev/null +++ b/samples/tasks/scenarios/watcher/list-audit-templates.yaml @@ -0,0 +1,22 @@ +--- + Watcher.list_audit_templates: + - + runner: + type: "constant" + times: 10 + concurrency: 1 + context: + audit_templates: + audit_templates_per_admin: 5 + fill_strategy: "random" + params: + - goal: + name: "workload_balancing" + strategy: + name: "workload_stabilization" + extra: {} + - goal: + name: "dummy" + strategy: + name: "dummy" + extra: {} diff --git a/tests/ci/osresources.py b/tests/ci/osresources.py index f79b743f..73c5be92 100755 --- a/tests/ci/osresources.py +++ b/tests/ci/osresources.py @@ -223,6 +223,8 @@ class Watcher(ResourceManager): REQUIRED_SERVICE = consts.Service.WATCHER + REPR_KEYS = ("uuid", "name") + def list_audits(self): return self.client.audit.list() @@ -232,12 +234,12 @@ class Watcher(ResourceManager): def list_goals(self): return self.client.goal.list() + def list_strategies(self): + return self.client.strategy.list() + def list_action_plans(self): return self.client.action_plan.list() - def list_actions(self): - return self.client.action.list() - class CloudResources(object): """List and compare cloud resources. diff --git a/tests/unit/plugins/openstack/cleanup/test_resources.py b/tests/unit/plugins/openstack/cleanup/test_resources.py index 1df58d20..03114221 100644 --- a/tests/unit/plugins/openstack/cleanup/test_resources.py +++ b/tests/unit/plugins/openstack/cleanup/test_resources.py @@ -813,3 +813,65 @@ class WatcherTemplateTestCase(test.TestCase): self.assertEqual("audit_template", watcher._resource) watcher._manager().list.assert_called_once_with(limit=0) + + +class WatcherAuditTestCase(test.TestCase): + + def test_id(self): + watcher = resources.WatcherAudit() + watcher.raw_resource = mock.MagicMock(uuid=100) + self.assertEqual(100, watcher.id()) + + def test_name(self): + watcher = resources.WatcherAudit() + watcher.raw_resource = mock.MagicMock(uuid="name") + self.assertEqual("name", watcher.name()) + + @mock.patch("%s.WatcherAudit._manager" % BASE) + def test_is_deleted(self, mock__manager): + mock__manager.return_value.get.return_value = None + watcher = resources.WatcherAudit() + watcher.id = mock.Mock() + self.assertFalse(watcher.is_deleted()) + mock__manager.side_effect = [watcher_exceptions.NotFound()] + self.assertTrue(watcher.is_deleted()) + + def test_list(self): + watcher = resources.WatcherAudit() + watcher._manager = mock.MagicMock() + + watcher.list() + + self.assertEqual("audit", watcher._resource) + watcher._manager().list.assert_called_once_with(limit=0) + + +class WatcherActionPlanTestCase(test.TestCase): + + def test_id(self): + watcher = resources.WatcherActionPlan() + watcher.raw_resource = mock.MagicMock(uuid=100) + self.assertEqual(100, watcher.id()) + + def test_name(self): + watcher = resources.WatcherActionPlan() + watcher.raw_resource = mock.MagicMock(uuid="name") + self.assertEqual("name", watcher.name()) + + @mock.patch("%s.WatcherActionPlan._manager" % BASE) + def test_is_deleted(self, mock__manager): + mock__manager.return_value.get.return_value = None + watcher = resources.WatcherActionPlan() + watcher.id = mock.Mock() + self.assertFalse(watcher.is_deleted()) + mock__manager.side_effect = [watcher_exceptions.NotFound()] + self.assertTrue(watcher.is_deleted()) + + def test_list(self): + watcher = resources.WatcherActionPlan() + watcher._manager = mock.MagicMock() + + watcher.list() + + self.assertEqual("action_plan", watcher._resource) + watcher._manager().list.assert_called_once_with(limit=0) \ No newline at end of file diff --git a/tests/unit/plugins/openstack/context/watcher/__init__.py b/tests/unit/plugins/openstack/context/watcher/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/unit/plugins/openstack/context/watcher/test_audit_templates.py b/tests/unit/plugins/openstack/context/watcher/test_audit_templates.py new file mode 100644 index 00000000..b3043fbc --- /dev/null +++ b/tests/unit/plugins/openstack/context/watcher/test_audit_templates.py @@ -0,0 +1,96 @@ +# 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 + +from rally.plugins.openstack.context.watcher import audit_templates +from tests.unit import fakes +from tests.unit import test + + +CTX = "rally.plugins.openstack.context.watcher" +SCN = "rally.plugins.openstack.scenarios.watcher" +TYP = "rally.plugins.openstack.types" + + +class AuditTemplateTestCase(test.ScenarioTestCase): + + @mock.patch("%s.utils.WatcherScenario._create_audit_template" % SCN, + return_value=mock.MagicMock()) + @mock.patch("%s.WatcherStrategy.transform" % TYP, + return_value=mock.MagicMock()) + @mock.patch("%s.WatcherGoal.transform" % TYP, + return_value=mock.MagicMock()) + @mock.patch("%s.audit_templates.osclients" % CTX, + return_value=fakes.FakeClients()) + def test_setup(self, mock_osclients, mock_watcher_goal_transform, + mock_watcher_strategy_transform, + mock_watcher_scenario__create_audit_template): + + users = [{"id": 1, "tenant_id": 1, "credential": mock.MagicMock()}] + self.context.update({ + "config": { + "audit_templates": { + "audit_templates_per_admin": 1, + "fill_strategy": "random", + "params": [ + { + "goal": { + "name": "workload_balancing" + }, + "strategy": { + "name": "workload_stabilization" + }, + "extra": {} + }, + { + "goal": { + "name": "workload_balancing" + }, + "strategy": { + "name": "workload_stabilization" + }, + "extra": {} + } + ] + }, + }, + "admin": { + "credential": mock.MagicMock() + }, + "users": users + }) + audit_template = audit_templates.AuditTemplateGenerator(self.context) + audit_template.setup() + goal_id = mock_watcher_goal_transform.return_value + strategy_id = mock_watcher_strategy_transform.return_value + mock_calls = [mock.call(goal_id, strategy_id, {})] + mock_watcher_scenario__create_audit_template.assert_has_calls( + mock_calls) + + @mock.patch("%s.audit_templates.resource_manager.cleanup" % CTX) + def test_cleanup(self, mock_cleanup): + audit_templates_mocks = [mock.Mock() for i in range(2)] + self.context.update({ + "admin": { + "credential": mock.MagicMock() + }, + "audit_templates": audit_templates_mocks + }) + audit_templates_ctx = audit_templates.AuditTemplateGenerator( + self.context) + audit_templates_ctx.cleanup() + mock_cleanup.assert_called_once_with( + names=["watcher.action_plan", "watcher.audit_template"], + admin=self.context["admin"]) diff --git a/tests/unit/plugins/openstack/scenarios/watcher/test_basic.py b/tests/unit/plugins/openstack/scenarios/watcher/test_basic.py index d343d1fc..221810c1 100644 --- a/tests/unit/plugins/openstack/scenarios/watcher/test_basic.py +++ b/tests/unit/plugins/openstack/scenarios/watcher/test_basic.py @@ -33,3 +33,22 @@ class WatcherTestCase(test.ScenarioTestCase): {}) scenario._delete_audit_template.assert_called_once_with( audit_template.uuid) + + def test_list_audit_template(self): + scenario = basic.Watcher(self.context) + scenario._list_audit_templates = mock.MagicMock() + scenario.list_audit_templates() + scenario._list_audit_templates.assert_called_once_with( + detail=False, goal=None, limit=None, name=None, sort_dir=None, + sort_key=None, strategy=None) + + def test_create_audit_and_delete(self): + mock_audit = mock.MagicMock() + scenario = basic.Watcher(self.context) + scenario.context = mock.MagicMock() + scenario._create_audit = mock.MagicMock(return_value=mock_audit) + scenario.sleep_between = mock.MagicMock() + scenario._delete_audit = mock.MagicMock() + scenario.create_audit_and_delete() + scenario._create_audit.assert_called_once_with(mock.ANY) + scenario._delete_audit.assert_called_once_with(mock_audit) diff --git a/tests/unit/plugins/openstack/scenarios/watcher/test_utils.py b/tests/unit/plugins/openstack/scenarios/watcher/test_utils.py index 481e6c23..0cf9e57e 100644 --- a/tests/unit/plugins/openstack/scenarios/watcher/test_utils.py +++ b/tests/unit/plugins/openstack/scenarios/watcher/test_utils.py @@ -14,10 +14,13 @@ # under the License. import mock +from oslo_config import cfg from rally.plugins.openstack.scenarios.watcher import utils from tests.unit import test +CONF = cfg.CONF + class WatcherScenarioTestCase(test.ScenarioTestCase): @@ -37,6 +40,16 @@ class WatcherScenarioTestCase(test.ScenarioTestCase): self._test_atomic_action_timer(watcher_scenario.atomic_actions(), "watcher.create_audit_template") + def test_list_audit_templates(self): + audit_templates_list = [] + watcher_scenario = utils.WatcherScenario(self.context) + self.admin_clients( + "watcher").audit_template.list.return_value = audit_templates_list + return_audit_templates_list = watcher_scenario._list_audit_templates() + self.assertEqual(audit_templates_list, return_audit_templates_list) + self._test_atomic_action_timer(watcher_scenario.atomic_actions(), + "watcher.list_audit_templates") + def test_delete_audit_template(self): watcher_scenario = utils.WatcherScenario(self.context) watcher_scenario._delete_audit_template("fake_audit_template") @@ -45,3 +58,31 @@ class WatcherScenarioTestCase(test.ScenarioTestCase): "fake_audit_template") self._test_atomic_action_timer(watcher_scenario.atomic_actions(), "watcher.delete_audit_template") + + def test_create_audit(self): + mock_audit_template = mock.Mock() + watcher_scenario = utils.WatcherScenario(self.context) + audit = watcher_scenario._create_audit(mock_audit_template) + self.mock_wait_for_status.mock.assert_called_once_with( + audit, + ready_statuses=["SUCCEEDED"], + failure_statuses=["FAILED"], + status_attr="state", + update_resource=self.mock_get_from_manager.mock.return_value, + check_interval=CONF.benchmark.watcher_audit_launch_poll_interval, + timeout=CONF.benchmark.watcher_audit_launch_timeout, + id_attr="uuid") + self.mock_get_from_manager.mock.assert_called_once_with() + self.admin_clients("watcher").audit.create.assert_called_once_with( + audit_template_uuid=mock_audit_template, audit_type="ONESHOT") + self._test_atomic_action_timer(watcher_scenario.atomic_actions(), + "watcher.create_audit") + + def test_delete_audit(self): + mock_audit = mock.Mock() + watcher_scenario = utils.WatcherScenario(self.context) + watcher_scenario._delete_audit(mock_audit) + self.admin_clients("watcher").audit.delete.assert_called_once_with( + mock_audit.uuid) + self._test_atomic_action_timer(watcher_scenario.atomic_actions(), + "watcher.delete_audit")