Better cleanup for Tempest tests
When running our Tempest tests, we are now cleaning up all the objects we have created within the WatcherDB via a soft_delete. Partially Implements: blueprint deletion-of-actions-plan Change-Id: Ibdcfd2be37094377d09ad77d5c20298ee2baa4d0
This commit is contained in:
parent
a0b5f5aa1d
commit
79850cc89c
@ -174,11 +174,6 @@ class InfraOptimClientJSON(base.BaseInfraOptimClient):
|
||||
"""Lists details of all existing action plan"""
|
||||
return self._list_request('/action_plans/detail', **kwargs)
|
||||
|
||||
@base.handle_errors
|
||||
def list_action_plan_by_audit(self, audit_uuid):
|
||||
"""Lists all action plans associated with an audit"""
|
||||
return self._list_request('/action_plans', audit_uuid=audit_uuid)
|
||||
|
||||
@base.handle_errors
|
||||
def show_action_plan(self, action_plan_uuid):
|
||||
"""Gets a specific action plan
|
||||
@ -190,7 +185,7 @@ class InfraOptimClientJSON(base.BaseInfraOptimClient):
|
||||
|
||||
@base.handle_errors
|
||||
def delete_action_plan(self, action_plan_uuid):
|
||||
"""Deletes an action_plan having the specified UUID
|
||||
"""Deletes an action plan having the specified UUID
|
||||
|
||||
:param action_plan_uuid: The unique identifier of the action_plan
|
||||
:return: A tuple with the server response and the response body
|
||||
@ -198,6 +193,18 @@ class InfraOptimClientJSON(base.BaseInfraOptimClient):
|
||||
|
||||
return self._delete_request('/action_plans', action_plan_uuid)
|
||||
|
||||
@base.handle_errors
|
||||
def delete_action_plans_by_audit(self, audit_uuid):
|
||||
"""Deletes an action plan having the specified UUID
|
||||
|
||||
:param audit_uuid: The unique identifier of the related Audit
|
||||
"""
|
||||
|
||||
_, action_plans = self.list_action_plans(audit_uuid=audit_uuid)
|
||||
|
||||
for action_plan in action_plans:
|
||||
self.delete_action_plan(action_plan['uuid'])
|
||||
|
||||
@base.handle_errors
|
||||
def update_action_plan(self, action_plan_uuid, patch):
|
||||
"""Update the specified action plan
|
||||
|
@ -18,31 +18,17 @@ import functools
|
||||
|
||||
from tempest import test
|
||||
from tempest_lib.common.utils import data_utils
|
||||
from tempest_lib import exceptions as lib_exc
|
||||
|
||||
from watcher_tempest_plugin import infra_optim_clients as clients
|
||||
|
||||
|
||||
def creates(resource):
|
||||
"""Decorator that adds resources to the appropriate cleanup list."""
|
||||
|
||||
def decorator(f):
|
||||
@functools.wraps(f)
|
||||
def wrapper(cls, *args, **kwargs):
|
||||
resp, body = f(cls, *args, **kwargs)
|
||||
|
||||
if 'uuid' in body:
|
||||
cls.created_objects[resource].add(body['uuid'])
|
||||
|
||||
return resp, body
|
||||
return wrapper
|
||||
return decorator
|
||||
|
||||
|
||||
class BaseInfraOptimTest(test.BaseTestCase):
|
||||
"""Base class for Infrastructure Optimization API tests."""
|
||||
|
||||
RESOURCE_TYPES = ['audit_template', 'audit']
|
||||
# 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')
|
||||
|
||||
@classmethod
|
||||
def setup_credentials(cls):
|
||||
@ -58,19 +44,52 @@ class BaseInfraOptimTest(test.BaseTestCase):
|
||||
def resource_setup(cls):
|
||||
super(BaseInfraOptimTest, cls).resource_setup()
|
||||
|
||||
cls.created_objects = {}
|
||||
for resource in cls.RESOURCE_TYPES:
|
||||
cls.created_objects[resource] = set()
|
||||
# Set of all created audit templates UUIDs
|
||||
cls.created_audit_templates = set()
|
||||
# Set of all created audit UUIDs
|
||||
cls.created_audits = set()
|
||||
# Set of all created audit UUIDs. We use it to build the list of
|
||||
# action plans to delete (including potential orphan one(s))
|
||||
cls.created_action_plans_audit_uuids = set()
|
||||
|
||||
@classmethod
|
||||
def resource_cleanup(cls):
|
||||
"""Ensure that all created objects get destroyed."""
|
||||
try:
|
||||
for resource in cls.RESOURCE_TYPES:
|
||||
obj_uuids = cls.created_objects[resource]
|
||||
delete_method = getattr(cls.client, 'delete_%s' % resource)
|
||||
for obj_uuid in obj_uuids:
|
||||
delete_method(obj_uuid, ignore_errors=lib_exc.NotFound)
|
||||
action_plans_to_be_deleted = set()
|
||||
# Phase 1: Make sure all objects are in an idle state
|
||||
for audit_uuid in cls.created_audits:
|
||||
test.call_until_true(
|
||||
func=functools.partial(
|
||||
cls.is_audit_idle, audit_uuid),
|
||||
duration=30,
|
||||
sleep_for=.5
|
||||
)
|
||||
|
||||
for audit_uuid in cls.created_action_plans_audit_uuids:
|
||||
_, action_plans = cls.client.list_action_plans(
|
||||
audit_uuid=audit_uuid)
|
||||
action_plans_to_be_deleted.update(
|
||||
ap['uuid'] for ap in action_plans['action_plans'])
|
||||
|
||||
for action_plan in action_plans['action_plans']:
|
||||
test.call_until_true(
|
||||
func=functools.partial(
|
||||
cls.is_action_plan_idle, action_plan['uuid']),
|
||||
duration=30,
|
||||
sleep_for=.5
|
||||
)
|
||||
|
||||
# Phase 2: Delete them all
|
||||
for action_plan_uuid in action_plans_to_be_deleted:
|
||||
cls.delete_action_plan(action_plan_uuid)
|
||||
|
||||
for audit_uuid in cls.created_audits.copy():
|
||||
cls.delete_audit(audit_uuid)
|
||||
|
||||
for audit_template_uuid in cls.created_audit_templates.copy():
|
||||
cls.delete_audit_template(audit_template_uuid)
|
||||
|
||||
finally:
|
||||
super(BaseInfraOptimTest, cls).resource_cleanup()
|
||||
|
||||
@ -95,7 +114,6 @@ class BaseInfraOptimTest(test.BaseTestCase):
|
||||
# ### AUDIT TEMPLATES ### #
|
||||
|
||||
@classmethod
|
||||
@creates('audit_template')
|
||||
def create_audit_template(cls, name=None, description=None, goal=None,
|
||||
host_aggregate=None, extra=None):
|
||||
"""Wrapper utility for creating a test audit template
|
||||
@ -111,12 +129,14 @@ class BaseInfraOptimTest(test.BaseTestCase):
|
||||
Default: {}
|
||||
:return: A tuple with The HTTP response and its body
|
||||
"""
|
||||
|
||||
description = description or data_utils.rand_name(
|
||||
'test-audit_template')
|
||||
resp, body = cls.client.create_audit_template(
|
||||
name=name, description=description, goal=goal,
|
||||
host_aggregate=host_aggregate, extra=extra)
|
||||
|
||||
cls.created_audit_templates.add(body['uuid'])
|
||||
|
||||
return resp, body
|
||||
|
||||
@classmethod
|
||||
@ -126,18 +146,16 @@ class BaseInfraOptimTest(test.BaseTestCase):
|
||||
:param uuid: The unique identifier of the audit template
|
||||
:return: Server response
|
||||
"""
|
||||
resp, _ = cls.client.delete_audit_template(uuid)
|
||||
|
||||
resp, body = cls.client.delete_audit_template(uuid)
|
||||
|
||||
if uuid in cls.created_objects['audit_template']:
|
||||
cls.created_objects['audit_template'].remove(uuid)
|
||||
if uuid in cls.created_audit_templates:
|
||||
cls.created_audit_templates.remove(uuid)
|
||||
|
||||
return resp
|
||||
|
||||
# ### AUDITS ### #
|
||||
|
||||
@classmethod
|
||||
@creates('audit')
|
||||
def create_audit(cls, audit_template_uuid, type='ONESHOT',
|
||||
state='PENDING', deadline=None):
|
||||
"""Wrapper utility for creating a test audit
|
||||
@ -151,6 +169,10 @@ class BaseInfraOptimTest(test.BaseTestCase):
|
||||
resp, body = cls.client.create_audit(
|
||||
audit_template_uuid=audit_template_uuid, type=type,
|
||||
state=state, deadline=deadline)
|
||||
|
||||
cls.created_audits.add(body['uuid'])
|
||||
cls.created_action_plans_audit_uuids.add(body['uuid'])
|
||||
|
||||
return resp, body
|
||||
|
||||
@classmethod
|
||||
@ -160,10 +182,10 @@ class BaseInfraOptimTest(test.BaseTestCase):
|
||||
:param audit_uuid: The unique identifier of the audit.
|
||||
:return: the HTTP response
|
||||
"""
|
||||
resp, body = cls.client.delete_audit(audit_uuid)
|
||||
resp, _ = cls.client.delete_audit(audit_uuid)
|
||||
|
||||
if audit_uuid in cls.created_objects['audit']:
|
||||
cls.created_objects['audit'].remove(audit_uuid)
|
||||
if audit_uuid in cls.created_audits:
|
||||
cls.created_audits.remove(audit_uuid)
|
||||
|
||||
return resp
|
||||
|
||||
@ -172,8 +194,39 @@ class BaseInfraOptimTest(test.BaseTestCase):
|
||||
_, audit = cls.client.show_audit(audit_uuid)
|
||||
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
|
||||
|
||||
@classmethod
|
||||
def is_audit_idle(cls, audit_uuid):
|
||||
_, audit = cls.client.show_audit(audit_uuid)
|
||||
return audit.get('state') in cls.IDLE_STATES
|
||||
|
||||
# ### ACTION PLANS ### #
|
||||
|
||||
@classmethod
|
||||
def create_action_plan(cls, audit_template_uuid, **audit_kwargs):
|
||||
"""Wrapper utility for creating a test action plan
|
||||
|
||||
:param audit_template_uuid: Audit template UUID to use
|
||||
:param audit_kwargs: Dict of audit properties to set
|
||||
:return: The action plan as dict
|
||||
"""
|
||||
_, audit = cls.create_audit(audit_template_uuid, **audit_kwargs)
|
||||
audit_uuid = audit['uuid']
|
||||
|
||||
assert test.call_until_true(
|
||||
func=functools.partial(cls.has_audit_succeeded, audit_uuid),
|
||||
duration=30,
|
||||
sleep_for=.5
|
||||
)
|
||||
|
||||
_, action_plans = cls.client.list_action_plans(audit_uuid=audit_uuid)
|
||||
|
||||
return action_plans['action_plans'][0]
|
||||
|
||||
@classmethod
|
||||
def delete_action_plan(cls, action_plan_uuid):
|
||||
"""Deletes an action plan having the specified UUID
|
||||
@ -181,14 +234,20 @@ class BaseInfraOptimTest(test.BaseTestCase):
|
||||
:param action_plan_uuid: The unique identifier of the action plan.
|
||||
:return: the HTTP response
|
||||
"""
|
||||
resp, body = cls.client.delete_action_plan(action_plan_uuid)
|
||||
resp, _ = cls.client.delete_action_plan(action_plan_uuid)
|
||||
|
||||
if action_plan_uuid in cls.created_objects['action_plan']:
|
||||
cls.created_objects['action_plan'].remove(action_plan_uuid)
|
||||
if action_plan_uuid in cls.created_action_plans_audit_uuids:
|
||||
cls.created_action_plans_audit_uuids.remove(action_plan_uuid)
|
||||
|
||||
return resp
|
||||
|
||||
@classmethod
|
||||
def has_action_plan_finished(cls, action_plan_uuid):
|
||||
_, action_plan = cls.client.show_action_plan(action_plan_uuid)
|
||||
return action_plan.get('state') in ('FAILED', 'SUCCEEDED', 'CANCELLED')
|
||||
return action_plan.get('state') in cls.FINISHED_STATES
|
||||
|
||||
@classmethod
|
||||
def is_action_plan_idle(cls, action_plan_uuid):
|
||||
"""This guard makes sure your action plan is not running"""
|
||||
_, action_plan = cls.client.show_action_plan(action_plan_uuid)
|
||||
return action_plan.get('state') in cls.IDLE_STATES
|
||||
|
@ -38,8 +38,8 @@ class TestShowListAction(base.BaseInfraOptimTest):
|
||||
duration=30,
|
||||
sleep_for=.5
|
||||
)
|
||||
_, action_plans = cls.client.list_action_plan_by_audit(
|
||||
cls.audit['uuid'])
|
||||
_, action_plans = cls.client.list_action_plans(
|
||||
audit_uuid=cls.audit['uuid'])
|
||||
cls.action_plan = action_plans['action_plans'][0]
|
||||
|
||||
@test.attr(type='smoke')
|
||||
|
@ -37,7 +37,8 @@ class TestCreateDeleteExecuteActionPlan(base.BaseInfraOptimTest):
|
||||
duration=30,
|
||||
sleep_for=.5
|
||||
))
|
||||
_, action_plans = self.client.list_action_plan_by_audit(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'])
|
||||
@ -55,7 +56,8 @@ class TestCreateDeleteExecuteActionPlan(base.BaseInfraOptimTest):
|
||||
duration=30,
|
||||
sleep_for=.5
|
||||
))
|
||||
_, action_plans = self.client.list_action_plan_by_audit(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'])
|
||||
@ -75,7 +77,8 @@ class TestCreateDeleteExecuteActionPlan(base.BaseInfraOptimTest):
|
||||
duration=30,
|
||||
sleep_for=.5
|
||||
))
|
||||
_, action_plans = self.client.list_action_plan_by_audit(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'])
|
||||
@ -112,8 +115,8 @@ class TestShowListActionPlan(base.BaseInfraOptimTest):
|
||||
duration=30,
|
||||
sleep_for=.5
|
||||
)
|
||||
_, action_plans = cls.client.list_action_plan_by_audit(
|
||||
cls.audit['uuid'])
|
||||
_, action_plans = cls.client.list_action_plans(
|
||||
audit_uuid=cls.audit['uuid'])
|
||||
cls.action_plan = action_plans['action_plans'][0]
|
||||
|
||||
@test.attr(type='smoke')
|
||||
@ -155,7 +158,7 @@ class TestShowListActionPlan(base.BaseInfraOptimTest):
|
||||
def test_list_with_limit(self):
|
||||
# We create 3 extra audits to exceed the limit we fix
|
||||
for _ in range(3):
|
||||
self.create_audit(self.audit_template['uuid'])
|
||||
self.create_action_plan(self.audit_template['uuid'])
|
||||
|
||||
_, body = self.client.list_action_plans(limit=3)
|
||||
|
||||
|
@ -117,7 +117,8 @@ class TestExecuteBasicStrategy(base.BaseInfraOptimScenarioTest):
|
||||
duration=300,
|
||||
sleep_for=2
|
||||
))
|
||||
_, action_plans = self.client.list_action_plan_by_audit(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'])
|
||||
|
@ -45,7 +45,8 @@ class TestExecuteDummyStrategy(base.BaseInfraOptimScenarioTest):
|
||||
duration=30,
|
||||
sleep_for=.5
|
||||
))
|
||||
_, action_plans = self.client.list_action_plan_by_audit(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'])
|
||||
|
Loading…
Reference in New Issue
Block a user