diff --git a/watcher_tempest_plugin/services/infra_optim/v1/json/client.py b/watcher_tempest_plugin/services/infra_optim/v1/json/client.py index ee434d98b..2ee27f5d4 100644 --- a/watcher_tempest_plugin/services/infra_optim/v1/json/client.py +++ b/watcher_tempest_plugin/services/infra_optim/v1/json/client.py @@ -213,6 +213,18 @@ class InfraOptimClientJSON(base.BaseInfraOptimClient): return self._patch_request('/action_plans', action_plan_uuid, patch) + @base.handle_errors + def start_action_plan(self, action_plan_uuid): + """Start the specified action plan + + :param action_plan_uuid: The unique identifier of the action_plan + :return: Tuple with the server response and the updated action_plan + """ + + return self._patch_request( + '/action_plans', action_plan_uuid, + [{'path': '/state', 'op': 'replace', 'value': 'PENDING'}]) + # ### GOALS ### # @base.handle_errors diff --git a/watcher_tempest_plugin/tests/api/admin/base.py b/watcher_tempest_plugin/tests/api/admin/base.py index 2c7b618dd..3677958cc 100644 --- a/watcher_tempest_plugin/tests/api/admin/base.py +++ b/watcher_tempest_plugin/tests/api/admin/base.py @@ -214,7 +214,7 @@ class BaseInfraOptimTest(test.BaseTestCase): audit_uuid = audit['uuid'] assert test.call_until_true( - func=functools.partial(cls.has_audit_succeeded, audit_uuid), + func=functools.partial(cls.has_audit_finished, audit_uuid), duration=30, sleep_for=.5 ) diff --git a/watcher_tempest_plugin/tests/api/admin/test_action.py b/watcher_tempest_plugin/tests/api/admin/test_action.py index c57e10d0a..709b25ba8 100644 --- a/watcher_tempest_plugin/tests/api/admin/test_action.py +++ b/watcher_tempest_plugin/tests/api/admin/test_action.py @@ -35,7 +35,7 @@ class TestShowListAction(base.BaseInfraOptimTest): _, cls.audit = cls.create_audit(cls.audit_template['uuid']) assert test.call_until_true( - func=functools.partial(cls.has_audit_succeeded, cls.audit['uuid']), + func=functools.partial(cls.has_audit_finished, cls.audit['uuid']), duration=30, sleep_for=.5 ) @@ -45,19 +45,23 @@ class TestShowListAction(base.BaseInfraOptimTest): @test.attr(type='smoke') def test_show_one_action(self): - _, action_uuid = self.client.list_actions( - action_plan_uuid=self.action_plan['uuid'])['actions'][0]['uuid'] - _, action = self.client.show_action(action_uuid) + _, body = self.client.list_actions( + action_plan_uuid=self.action_plan["uuid"]) + actions = body['actions'] - self.assertEqual(action_uuid, action['uuid']) - self.assertEqual("nop", action['action_type']) + _, action = self.client.show_action(actions[0]["uuid"]) + + self.assertEqual(self.action_plan["uuid"], action['action_plan_uuid']) self.assertEqual("PENDING", action['state']) @test.attr(type='smoke') def test_show_action_with_links(self): - _, action_uuid = self.client.list_actions( - action_plan_uuid=self.action_plan['uuid'])['actions'][0]['uuid'] - _, action = self.client.show_action(action_uuid) + _, body = self.client.list_actions( + action_plan_uuid=self.action_plan["uuid"]) + actions = body['actions'] + + _, action = self.client.show_action(actions[0]["uuid"]) + self.assertIn('links', action.keys()) self.assertEqual(2, len(action['links'])) self.assertIn(action['uuid'], action['links'][0]['href']) diff --git a/watcher_tempest_plugin/tests/api/admin/test_action_plan.py b/watcher_tempest_plugin/tests/api/admin/test_action_plan.py index 710b19b64..a27fc8dc9 100644 --- a/watcher_tempest_plugin/tests/api/admin/test_action_plan.py +++ b/watcher_tempest_plugin/tests/api/admin/test_action_plan.py @@ -29,12 +29,12 @@ class TestCreateDeleteExecuteActionPlan(base.BaseInfraOptimTest): @test.attr(type='smoke') def test_create_action_plan(self): - _, goal = self.client.show_goal("DUMMY") + _, goal = self.client.show_goal("dummy") _, audit_template = self.create_audit_template(goal['uuid']) _, audit = self.create_audit(audit_template['uuid']) self.assertTrue(test.call_until_true( - func=functools.partial(self.has_audit_succeeded, audit['uuid']), + func=functools.partial(self.has_audit_finished, audit['uuid']), duration=30, sleep_for=.5 )) @@ -49,12 +49,12 @@ class TestCreateDeleteExecuteActionPlan(base.BaseInfraOptimTest): @test.attr(type='smoke') def test_delete_action_plan(self): - _, goal = self.client.show_goal("DUMMY") + _, goal = self.client.show_goal("dummy") _, audit_template = self.create_audit_template(goal['uuid']) _, audit = self.create_audit(audit_template['uuid']) self.assertTrue(test.call_until_true( - func=functools.partial(self.has_audit_succeeded, audit['uuid']), + func=functools.partial(self.has_audit_finished, audit['uuid']), duration=30, sleep_for=.5 )) @@ -71,12 +71,12 @@ class TestCreateDeleteExecuteActionPlan(base.BaseInfraOptimTest): @test.attr(type='smoke') def test_execute_dummy_action_plan(self): - _, goal = self.client.show_goal("DUMMY") + _, goal = self.client.show_goal("dummy") _, audit_template = self.create_audit_template(goal['uuid']) _, audit = self.create_audit(audit_template['uuid']) self.assertTrue(test.call_until_true( - func=functools.partial(self.has_audit_succeeded, audit['uuid']), + func=functools.partial(self.has_audit_finished, audit['uuid']), duration=30, sleep_for=.5 )) @@ -86,11 +86,13 @@ class TestCreateDeleteExecuteActionPlan(base.BaseInfraOptimTest): _, action_plan = self.client.show_action_plan(action_plan['uuid']) + if action_plan['state'] in ['SUPERSEDED', 'SUCCEEDED']: + # This means the action plan is superseded so we cannot trigger it, + # or it is empty. + return + # Execute the action by changing its state to PENDING - _, updated_ap = self.client.update_action_plan( - action_plan['uuid'], - patch=[{'path': '/state', 'op': 'replace', 'value': 'PENDING'}] - ) + _, updated_ap = self.client.start_action_plan(action_plan['uuid']) self.assertTrue(test.call_until_true( func=functools.partial( @@ -110,12 +112,12 @@ class TestShowListActionPlan(base.BaseInfraOptimTest): @classmethod def resource_setup(cls): super(TestShowListActionPlan, cls).resource_setup() - _, cls.goal = cls.client.show_goal("DUMMY") + _, cls.goal = cls.client.show_goal("dummy") _, cls.audit_template = cls.create_audit_template(cls.goal['uuid']) _, cls.audit = cls.create_audit(cls.audit_template['uuid']) assert test.call_until_true( - func=functools.partial(cls.has_audit_succeeded, cls.audit['uuid']), + func=functools.partial(cls.has_audit_finished, cls.audit['uuid']), duration=30, sleep_for=.5 ) diff --git a/watcher_tempest_plugin/tests/api/admin/test_audit.py b/watcher_tempest_plugin/tests/api/admin/test_audit.py index b175966c7..8eb99ad92 100644 --- a/watcher_tempest_plugin/tests/api/admin/test_audit.py +++ b/watcher_tempest_plugin/tests/api/admin/test_audit.py @@ -38,7 +38,7 @@ class TestCreateUpdateDeleteAudit(base.BaseInfraOptimTest): @test.attr(type='smoke') def test_create_audit_oneshot(self): - _, goal = self.client.show_goal("DUMMY") + _, goal = self.client.show_goal("dummy") _, audit_template = self.create_audit_template(goal['uuid']) audit_params = dict( @@ -56,7 +56,7 @@ class TestCreateUpdateDeleteAudit(base.BaseInfraOptimTest): @test.attr(type='smoke') def test_create_audit_continuous(self): - _, goal = self.client.show_goal("DUMMY") + _, goal = self.client.show_goal("dummy") _, audit_template = self.create_audit_template(goal['uuid']) audit_params = dict( @@ -85,7 +85,7 @@ class TestCreateUpdateDeleteAudit(base.BaseInfraOptimTest): @test.attr(type='smoke') def test_create_audit_with_invalid_state(self): - _, goal = self.client.show_goal("DUMMY") + _, goal = self.client.show_goal("dummy") _, audit_template = self.create_audit_template(goal['uuid']) audit_params = dict( @@ -98,7 +98,7 @@ class TestCreateUpdateDeleteAudit(base.BaseInfraOptimTest): @test.attr(type='smoke') def test_create_audit_with_no_state(self): - _, goal = self.client.show_goal("DUMMY") + _, goal = self.client.show_goal("dummy") _, audit_template = self.create_audit_template(goal['uuid']) audit_params = dict( @@ -120,7 +120,7 @@ class TestCreateUpdateDeleteAudit(base.BaseInfraOptimTest): @test.attr(type='smoke') def test_delete_audit(self): - _, goal = self.client.show_goal("DUMMY") + _, goal = self.client.show_goal("dummy") _, audit_template = self.create_audit_template(goal['uuid']) _, body = self.create_audit(audit_template['uuid']) audit_uuid = body['uuid'] @@ -158,7 +158,7 @@ class TestShowListAudit(base.BaseInfraOptimTest): @classmethod def resource_setup(cls): super(TestShowListAudit, cls).resource_setup() - _, cls.goal = cls.client.show_goal("DUMMY") + _, cls.goal = cls.client.show_goal("dummy") _, cls.audit_template = cls.create_audit_template(cls.goal['uuid']) _, cls.audit = cls.create_audit(cls.audit_template['uuid']) diff --git a/watcher_tempest_plugin/tests/scenario/base.py b/watcher_tempest_plugin/tests/scenario/base.py index a166d1709..113df5c33 100644 --- a/watcher_tempest_plugin/tests/scenario/base.py +++ b/watcher_tempest_plugin/tests/scenario/base.py @@ -36,6 +36,11 @@ CONF = config.CONF class BaseInfraOptimScenarioTest(manager.ScenarioTest): """Base class for Infrastructure Optimization API tests.""" + # States where the object is waiting for some event to perform a transition + IDLE_STATES = ('RECOMMENDED', 'FAILED', 'SUCCEEDED', 'CANCELLED') + # States where the object can only be DELETED (end of its life-cycle) + FINISHED_STATES = ('FAILED', 'SUCCEEDED', 'CANCELLED', 'SUPERSEDED') + @classmethod def setup_credentials(cls): cls._check_network_config() @@ -142,6 +147,11 @@ class BaseInfraOptimScenarioTest(manager.ScenarioTest): return audit.get('state') == 'SUCCEEDED' + @classmethod + def has_audit_finished(cls, audit_uuid): + _, audit = cls.client.show_audit(audit_uuid) + return audit.get('state') in cls.FINISHED_STATES + # ### ACTION PLANS ### # def delete_action_plan(self, action_plan_uuid): diff --git a/watcher_tempest_plugin/tests/scenario/test_execute_basic_optim.py b/watcher_tempest_plugin/tests/scenario/test_execute_basic_optim.py index 8568b7fa6..e65b672ee 100644 --- a/watcher_tempest_plugin/tests/scenario/test_execute_basic_optim.py +++ b/watcher_tempest_plugin/tests/scenario/test_execute_basic_optim.py @@ -148,24 +148,31 @@ class TestExecuteBasicStrategy(base.BaseInfraOptimScenarioTest): try: self.assertTrue(test.call_until_true( func=functools.partial( - self.has_audit_succeeded, audit['uuid']), + self.has_audit_finished, audit['uuid']), duration=600, sleep_for=2 )) except ValueError: self.fail("The audit has failed!") + _, finished_audit = self.client.show_audit(audit['uuid']) + if finished_audit.get('state') in ('FAILED', 'CANCELLED'): + self.fail("The audit ended in unexpected state: %s!", + finished_audit.get('state')) + _, action_plans = self.client.list_action_plans( audit_uuid=audit['uuid']) action_plan = action_plans['action_plans'][0] _, action_plan = self.client.show_action_plan(action_plan['uuid']) + if action_plan['state'] in ('SUPERSEDED', 'SUCCEEDED'): + # This means the action plan is superseded so we cannot trigger it, + # or it is empty. + return + # Execute the action by changing its state to PENDING - _, updated_ap = self.client.update_action_plan( - action_plan['uuid'], - patch=[{'path': '/state', 'op': 'replace', 'value': 'PENDING'}] - ) + _, updated_ap = self.client.start_action_plan(action_plan['uuid']) self.assertTrue(test.call_until_true( func=functools.partial( diff --git a/watcher_tempest_plugin/tests/scenario/test_execute_dummy_optim.py b/watcher_tempest_plugin/tests/scenario/test_execute_dummy_optim.py index bd016c275..27cdaad6a 100644 --- a/watcher_tempest_plugin/tests/scenario/test_execute_dummy_optim.py +++ b/watcher_tempest_plugin/tests/scenario/test_execute_dummy_optim.py @@ -42,21 +42,26 @@ class TestExecuteDummyStrategy(base.BaseInfraOptimScenarioTest): _, audit = self.create_audit(audit_template['uuid']) self.assertTrue(test.call_until_true( - func=functools.partial(self.has_audit_succeeded, audit['uuid']), + func=functools.partial(self.has_audit_finished, audit['uuid']), duration=30, sleep_for=.5 )) + + self.assertTrue(self.has_audit_succeeded(audit['uuid'])) + _, action_plans = self.client.list_action_plans( audit_uuid=audit['uuid']) action_plan = action_plans['action_plans'][0] _, action_plan = self.client.show_action_plan(action_plan['uuid']) + if action_plan['state'] in ['SUPERSEDED', 'SUCCEEDED']: + # This means the action plan is superseded so we cannot trigger it, + # or it is empty. + return + # Execute the action by changing its state to PENDING - _, updated_ap = self.client.update_action_plan( - action_plan['uuid'], - patch=[{'path': '/state', 'op': 'replace', 'value': 'PENDING'}] - ) + _, updated_ap = self.client.start_action_plan(action_plan['uuid']) self.assertTrue(test.call_until_true( func=functools.partial(