From b53da21b9b72b1a24292795fdf05f853971d9e2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vincent=20Fran=C3=A7oise?= Date: Tue, 19 Jan 2016 10:26:56 +0100 Subject: [PATCH] Tempest scenario - execute a dummy strategy This patchset implements the first scenario for Watcher which does the following actions: - create an audit template with the dummy strategy - run the audit to create an action plan - get the action plan - run the action plan - get results and make sure it succeeded Partially Implements: blueprint tempest-basic-set-up Change-Id: Iee74ede0bd1bcd03e8938f2ec8c6884f99e7f99a --- watcher_tempest_plugin/README.rst | 3 + .../tests/scenario/__init__.py | 0 watcher_tempest_plugin/tests/scenario/base.py | 154 ++++++++++++++++++ .../scenario/test_execute_dummy_optim.py | 69 ++++++++ 4 files changed, 226 insertions(+) create mode 100644 watcher_tempest_plugin/tests/scenario/__init__.py create mode 100644 watcher_tempest_plugin/tests/scenario/base.py create mode 100644 watcher_tempest_plugin/tests/scenario/test_execute_dummy_optim.py diff --git a/watcher_tempest_plugin/README.rst b/watcher_tempest_plugin/README.rst index 7f91645f3..2e4cc0cd9 100644 --- a/watcher_tempest_plugin/README.rst +++ b/watcher_tempest_plugin/README.rst @@ -84,6 +84,9 @@ For Keystone V2:: admin_password = auth_version = v2 +In both cases:: + public_network_id = + For more information, please refer to: diff --git a/watcher_tempest_plugin/tests/scenario/__init__.py b/watcher_tempest_plugin/tests/scenario/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/watcher_tempest_plugin/tests/scenario/base.py b/watcher_tempest_plugin/tests/scenario/base.py new file mode 100644 index 000000000..59f4dd403 --- /dev/null +++ b/watcher_tempest_plugin/tests/scenario/base.py @@ -0,0 +1,154 @@ +# -*- encoding: utf-8 -*- +# Copyright (c) 2016 b<>com +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from __future__ import unicode_literals + +import time + +from oslo_log import log + +from tempest import config +from tempest import exceptions +from tempest.scenario import manager +from tempest_lib.common.utils import data_utils + +from watcher_tempest_plugin import infra_optim_clients as clients + +LOG = log.getLogger(__name__) +CONF = config.CONF + + +class BaseInfraOptimScenarioTest(manager.ScenarioTest): + """Base class for Infrastructure Optimization API tests.""" + + @classmethod + def setup_credentials(cls): + cls._check_network_config() + super(BaseInfraOptimScenarioTest, cls).setup_credentials() + cls.mgr = clients.AdminManager() + + @classmethod + def setup_clients(cls): + super(BaseInfraOptimScenarioTest, cls).setup_clients() + cls.client = cls.mgr.io_client + + @classmethod + def resource_setup(cls): + super(BaseInfraOptimScenarioTest, cls).resource_setup() + + @classmethod + def resource_cleanup(cls): + """Ensure that all created objects get destroyed.""" + super(BaseInfraOptimScenarioTest, cls).resource_cleanup() + + @classmethod + def wait_for(cls, condition, timeout=30): + start_time = time.time() + while time.time() - start_time < timeout: + if condition(): + break + time.sleep(.5) + + @classmethod + def _check_network_config(cls): + if not CONF.network.public_network_id: + msg = 'public network not defined.' + LOG.error(msg) + raise exceptions.InvalidConfiguration(msg) + + # ### AUDIT TEMPLATES ### # + + def create_audit_template(self, name=None, description=None, goal=None, + host_aggregate=None, extra=None): + """Wrapper utility for creating a test audit template + + :param name: The name of the audit template. Default: My Audit Template + :param description: The description of the audit template. + Default: AT Description + :param goal: The goal associated within the audit template. + Default: DUMMY + :param host_aggregate: ID of the host aggregate targeted by + this audit template. Default: 1 + :param extra: IMetadata associated to this audit template. + Default: {} + :return: A tuple with The HTTP response and its body + """ + + description = description or data_utils.rand_name( + 'test-audit_template') + resp, body = self.client.create_audit_template( + name=name, description=description, goal=goal, + host_aggregate=host_aggregate, extra=extra) + + self.addCleanup( + self.delete_audit_template, + audit_template_uuid=body["uuid"] + ) + + return resp, body + + def delete_audit_template(self, audit_template_uuid): + """Deletes a audit_template having the specified UUID + + :param audit_template_uuid: The unique identifier of the audit template + :return: Server response + """ + resp, _ = self.client.delete_audit_template(audit_template_uuid) + return resp + + # ### AUDITS ### # + + def create_audit(self, audit_template_uuid, type='ONESHOT', + state='PENDING', deadline=None): + """Wrapper utility for creating a test audit + + :param audit_template_uuid: Audit Template UUID this audit will use + :param type: Audit type (either ONESHOT or CONTINUOUS) + :return: A tuple with The HTTP response and its body + """ + resp, body = self.client.create_audit( + audit_template_uuid=audit_template_uuid, type=type, + state=state, deadline=deadline) + + self.addCleanup(self.delete_audit, audit_uuid=body["uuid"]) + return resp, body + + def delete_audit(self, audit_uuid): + """Deletes an audit having the specified UUID + + :param audit_uuid: The unique identifier of the audit. + :return: the HTTP response + """ + + _, action_plans = self.client.list_action_plans(audit_uuid=audit_uuid) + for action_plan in action_plans.get("action_plans", []): + self.delete_action_plan(action_plan_uuid=action_plan["uuid"]) + + resp, _ = self.client.delete_audit(audit_uuid) + return resp + + # ### ACTION PLANS ### # + + def delete_action_plan(self, action_plan_uuid): + """Deletes an action plan having the specified UUID + + :param action_plan_uuid: The unique identifier of the action plan. + :return: the HTTP response + """ + resp, _ = self.client.delete_action_plan(action_plan_uuid) + return resp diff --git a/watcher_tempest_plugin/tests/scenario/test_execute_dummy_optim.py b/watcher_tempest_plugin/tests/scenario/test_execute_dummy_optim.py new file mode 100644 index 000000000..46ba6b345 --- /dev/null +++ b/watcher_tempest_plugin/tests/scenario/test_execute_dummy_optim.py @@ -0,0 +1,69 @@ +# -*- encoding: utf-8 -*- +# Copyright (c) 2016 b<>com +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import unicode_literals + +import functools + +from tempest import test + +from watcher_tempest_plugin.tests.scenario import base + + +class TestExecuteDummyStrategy(base.BaseInfraOptimScenarioTest): + """Tests for action plans""" + + def test_execute_dummy_action_plan(self): + """Execute an action plan based on the DUMMY strategy + + - create an audit template with the dummy strategy + - run the audit to create an action plan + - get the action plan + - run the action plan + - get results and make sure it succeeded + """ + _, audit_template = self.create_audit_template() + _, audit = self.create_audit(audit_template['uuid']) + + self.assertTrue(test.call_until_true( + func=functools.partial(self.has_audit_succeeded, audit['uuid']), + duration=30, + sleep_for=.5 + )) + _, action_plans = self.client.list_action_plan_by_audit(audit['uuid']) + action_plan = action_plans['action_plans'][0] + + _, action_plan = self.client.show_action_plan(action_plan['uuid']) + + # Execute the action by changing its state to STARTING + _, updated_ap = self.client.update_action_plan( + action_plan['uuid'], + patch=[{'path': '/state', 'op': 'replace', 'value': 'STARTING'}] + ) + + def has_finished(action_plan_uuid): + return self.has_action_plan_finished(action_plan_uuid) + + self.assertTrue(test.call_until_true( + func=functools.partial(has_finished, action_plan['uuid']), + duration=30, + sleep_for=.5 + )) + _, finished_ap = self.client.show_action_plan(action_plan['uuid']) + + self.assertIn(updated_ap['state'], ('STARTING', 'ONGOING')) + self.assertEqual(finished_ap['state'], 'SUCCEEDED')