Merge "Added strategy ID + Action Plan syncing"
This commit is contained in:
commit
ab10201c72
@ -73,6 +73,7 @@ from watcher.api.controllers.v1 import utils as api_utils
|
||||
from watcher.applier import rpcapi
|
||||
from watcher.common import exception
|
||||
from watcher.common import policy
|
||||
from watcher.common import utils
|
||||
from watcher import objects
|
||||
from watcher.objects import action_plan as ap_objects
|
||||
|
||||
@ -117,6 +118,8 @@ class ActionPlan(base.APIBase):
|
||||
"""
|
||||
|
||||
_audit_uuid = None
|
||||
_strategy_uuid = None
|
||||
_strategy_name = None
|
||||
_first_action_uuid = None
|
||||
_efficacy_indicators = None
|
||||
|
||||
@ -177,6 +180,43 @@ class ActionPlan(base.APIBase):
|
||||
elif value and self._efficacy_indicators != value:
|
||||
self._efficacy_indicators = value
|
||||
|
||||
def _get_strategy(self, value):
|
||||
if value == wtypes.Unset:
|
||||
return None
|
||||
strategy = None
|
||||
try:
|
||||
if utils.is_uuid_like(value) or utils.is_int_like(value):
|
||||
strategy = objects.Strategy.get(
|
||||
pecan.request.context, value)
|
||||
else:
|
||||
strategy = objects.Strategy.get_by_name(
|
||||
pecan.request.context, value)
|
||||
except exception.StrategyNotFound:
|
||||
pass
|
||||
if strategy:
|
||||
self.strategy_id = strategy.id
|
||||
return strategy
|
||||
|
||||
def _get_strategy_uuid(self):
|
||||
return self._strategy_uuid
|
||||
|
||||
def _set_strategy_uuid(self, value):
|
||||
if value and self._strategy_uuid != value:
|
||||
self._strategy_uuid = None
|
||||
strategy = self._get_strategy(value)
|
||||
if strategy:
|
||||
self._strategy_uuid = strategy.uuid
|
||||
|
||||
def _get_strategy_name(self):
|
||||
return self._strategy_name
|
||||
|
||||
def _set_strategy_name(self, value):
|
||||
if value and self._strategy_name != value:
|
||||
self._strategy_name = None
|
||||
strategy = self._get_strategy(value)
|
||||
if strategy:
|
||||
self._strategy_name = strategy.name
|
||||
|
||||
uuid = wtypes.wsattr(types.uuid, readonly=True)
|
||||
"""Unique UUID for this action plan"""
|
||||
|
||||
@ -189,6 +229,14 @@ class ActionPlan(base.APIBase):
|
||||
mandatory=True)
|
||||
"""The UUID of the audit this port belongs to"""
|
||||
|
||||
strategy_uuid = wsme.wsproperty(
|
||||
wtypes.text, _get_strategy_uuid, _set_strategy_uuid, mandatory=False)
|
||||
"""Strategy UUID the action plan refers to"""
|
||||
|
||||
strategy_name = wsme.wsproperty(
|
||||
wtypes.text, _get_strategy_name, _set_strategy_name, mandatory=False)
|
||||
"""The name of the strategy this action plan refers to"""
|
||||
|
||||
efficacy_indicators = wsme.wsproperty(
|
||||
types.jsontype, _get_efficacy_indicators, _set_efficacy_indicators,
|
||||
mandatory=True)
|
||||
@ -219,6 +267,10 @@ class ActionPlan(base.APIBase):
|
||||
self.fields.append('efficacy_indicators')
|
||||
|
||||
setattr(self, 'audit_uuid', kwargs.get('audit_id', wtypes.Unset))
|
||||
fields.append('strategy_uuid')
|
||||
setattr(self, 'strategy_uuid', kwargs.get('strategy_id', wtypes.Unset))
|
||||
fields.append('strategy_name')
|
||||
setattr(self, 'strategy_name', kwargs.get('strategy_id', wtypes.Unset))
|
||||
setattr(self, 'first_action_uuid',
|
||||
kwargs.get('first_action_id', wtypes.Unset))
|
||||
|
||||
@ -227,7 +279,8 @@ class ActionPlan(base.APIBase):
|
||||
if not expand:
|
||||
action_plan.unset_fields_except(
|
||||
['uuid', 'state', 'efficacy_indicators', 'global_efficacy',
|
||||
'updated_at', 'audit_uuid', 'first_action_uuid'])
|
||||
'updated_at', 'audit_uuid', 'strategy_uuid', 'strategy_name',
|
||||
'first_action_uuid'])
|
||||
|
||||
action_plan.links = [
|
||||
link.Link.make_link(
|
||||
@ -275,8 +328,8 @@ class ActionPlanCollection(collection.Collection):
|
||||
@staticmethod
|
||||
def convert_with_links(rpc_action_plans, limit, url=None, expand=False,
|
||||
**kwargs):
|
||||
collection = ActionPlanCollection()
|
||||
collection.action_plans = [ActionPlan.convert_with_links(
|
||||
ap_collection = ActionPlanCollection()
|
||||
ap_collection.action_plans = [ActionPlan.convert_with_links(
|
||||
p, expand) for p in rpc_action_plans]
|
||||
|
||||
if 'sort_key' in kwargs:
|
||||
@ -284,13 +337,13 @@ class ActionPlanCollection(collection.Collection):
|
||||
if kwargs['sort_key'] == 'audit_uuid':
|
||||
if 'sort_dir' in kwargs:
|
||||
reverse = True if kwargs['sort_dir'] == 'desc' else False
|
||||
collection.action_plans = sorted(
|
||||
collection.action_plans,
|
||||
ap_collection.action_plans = sorted(
|
||||
ap_collection.action_plans,
|
||||
key=lambda action_plan: action_plan.audit_uuid,
|
||||
reverse=reverse)
|
||||
|
||||
collection.next = collection.get_next(limit, url=url, **kwargs)
|
||||
return collection
|
||||
ap_collection.next = ap_collection.get_next(limit, url=url, **kwargs)
|
||||
return ap_collection
|
||||
|
||||
@classmethod
|
||||
def sample(cls):
|
||||
@ -301,6 +354,7 @@ class ActionPlanCollection(collection.Collection):
|
||||
|
||||
class ActionPlansController(rest.RestController):
|
||||
"""REST controller for Actions."""
|
||||
|
||||
def __init__(self):
|
||||
super(ActionPlansController, self).__init__()
|
||||
|
||||
@ -314,7 +368,8 @@ class ActionPlansController(rest.RestController):
|
||||
|
||||
def _get_action_plans_collection(self, marker, limit,
|
||||
sort_key, sort_dir, expand=False,
|
||||
resource_url=None, audit_uuid=None):
|
||||
resource_url=None, audit_uuid=None,
|
||||
strategy=None):
|
||||
|
||||
limit = api_utils.validate_limit(limit)
|
||||
api_utils.validate_sort_dir(sort_dir)
|
||||
@ -328,6 +383,12 @@ class ActionPlansController(rest.RestController):
|
||||
if audit_uuid:
|
||||
filters['audit_uuid'] = audit_uuid
|
||||
|
||||
if strategy:
|
||||
if utils.is_uuid_like(strategy):
|
||||
filters['strategy_uuid'] = strategy
|
||||
else:
|
||||
filters['strategy_name'] = strategy
|
||||
|
||||
if sort_key == 'audit_uuid':
|
||||
sort_db_key = None
|
||||
else:
|
||||
@ -347,9 +408,9 @@ class ActionPlansController(rest.RestController):
|
||||
sort_dir=sort_dir)
|
||||
|
||||
@wsme_pecan.wsexpose(ActionPlanCollection, types.uuid, int, wtypes.text,
|
||||
wtypes.text, types.uuid)
|
||||
wtypes.text, types.uuid, wtypes.text)
|
||||
def get_all(self, marker=None, limit=None,
|
||||
sort_key='id', sort_dir='asc', audit_uuid=None):
|
||||
sort_key='id', sort_dir='asc', audit_uuid=None, strategy=None):
|
||||
"""Retrieve a list of action plans.
|
||||
|
||||
:param marker: pagination marker for large data sets.
|
||||
@ -358,18 +419,20 @@ class ActionPlansController(rest.RestController):
|
||||
:param sort_dir: direction to sort. "asc" or "desc". Default: asc.
|
||||
:param audit_uuid: Optional UUID of an audit, to get only actions
|
||||
for that audit.
|
||||
:param strategy: strategy UUID or name to filter by
|
||||
"""
|
||||
context = pecan.request.context
|
||||
policy.enforce(context, 'action_plan:get_all',
|
||||
action='action_plan:get_all')
|
||||
|
||||
return self._get_action_plans_collection(
|
||||
marker, limit, sort_key, sort_dir, audit_uuid=audit_uuid)
|
||||
marker, limit, sort_key, sort_dir,
|
||||
audit_uuid=audit_uuid, strategy=strategy)
|
||||
|
||||
@wsme_pecan.wsexpose(ActionPlanCollection, types.uuid, int, wtypes.text,
|
||||
wtypes.text, types.uuid)
|
||||
wtypes.text, types.uuid, wtypes.text)
|
||||
def detail(self, marker=None, limit=None,
|
||||
sort_key='id', sort_dir='asc', audit_uuid=None):
|
||||
sort_key='id', sort_dir='asc', audit_uuid=None, strategy=None):
|
||||
"""Retrieve a list of action_plans with detail.
|
||||
|
||||
:param marker: pagination marker for large data sets.
|
||||
@ -378,6 +441,7 @@ class ActionPlansController(rest.RestController):
|
||||
:param sort_dir: direction to sort. "asc" or "desc". Default: asc.
|
||||
:param audit_uuid: Optional UUID of an audit, to get only actions
|
||||
for that audit.
|
||||
:param strategy: strategy UUID or name to filter by
|
||||
"""
|
||||
context = pecan.request.context
|
||||
policy.enforce(context, 'action_plan:detail',
|
||||
@ -391,9 +455,8 @@ class ActionPlansController(rest.RestController):
|
||||
expand = True
|
||||
resource_url = '/'.join(['action_plans', 'detail'])
|
||||
return self._get_action_plans_collection(
|
||||
marker, limit,
|
||||
sort_key, sort_dir, expand,
|
||||
resource_url, audit_uuid=audit_uuid)
|
||||
marker, limit, sort_key, sort_dir, expand,
|
||||
resource_url, audit_uuid=audit_uuid, strategy=strategy)
|
||||
|
||||
@wsme_pecan.wsexpose(ActionPlan, types.uuid)
|
||||
def get_one(self, action_plan_uuid):
|
||||
@ -491,8 +554,8 @@ class ActionPlansController(rest.RestController):
|
||||
if action_plan_to_update[field] != patch_val:
|
||||
action_plan_to_update[field] = patch_val
|
||||
|
||||
if (field == 'state'
|
||||
and patch_val == objects.action_plan.State.PENDING):
|
||||
if (field == 'state'and
|
||||
patch_val == objects.action_plan.State.PENDING):
|
||||
launch_action_plan = True
|
||||
|
||||
action_plan_to_update.save()
|
||||
|
@ -198,7 +198,7 @@ class Audit(base.APIBase):
|
||||
else:
|
||||
strategy = objects.Strategy.get_by_name(
|
||||
pecan.request.context, value)
|
||||
except exception.GoalNotFound:
|
||||
except exception.StrategyNotFound:
|
||||
pass
|
||||
if strategy:
|
||||
self.strategy_id = strategy.id
|
||||
|
@ -220,7 +220,8 @@ class PurgeCommand(object):
|
||||
if audit not in orphans.audits]
|
||||
orphans.action_plans = [
|
||||
ap for ap in action_plans
|
||||
if ap.audit_id not in audit_ids]
|
||||
if ap.audit_id not in audit_ids or
|
||||
ap.strategy_id not in strategy_ids]
|
||||
|
||||
# Objects with orphan parents are themselves orphans
|
||||
action_plan_ids = [ap.id for ap in action_plans
|
||||
|
@ -347,10 +347,15 @@ class Connection(api.BaseConnection):
|
||||
if filters is None:
|
||||
filters = {}
|
||||
|
||||
plain_fields = ['uuid', 'state', 'audit_id']
|
||||
join_fieldmap = {
|
||||
'audit_uuid': ("uuid", models.Audit),
|
||||
}
|
||||
plain_fields = ['uuid', 'state', 'audit_id', 'strategy_id']
|
||||
join_fieldmap = JoinMap(
|
||||
audit_uuid=NaturalJoinFilter(
|
||||
join_fieldname="uuid", join_model=models.Audit),
|
||||
strategy_uuid=NaturalJoinFilter(
|
||||
join_fieldname="uuid", join_model=models.Strategy),
|
||||
strategy_name=NaturalJoinFilter(
|
||||
join_fieldname="name", join_model=models.Strategy),
|
||||
)
|
||||
|
||||
return self._add_filters(
|
||||
query=query, model=models.ActionPlan, filters=filters,
|
||||
|
@ -211,7 +211,8 @@ class ActionPlan(Base):
|
||||
id = Column(Integer, primary_key=True)
|
||||
uuid = Column(String(36))
|
||||
first_action_id = Column(Integer)
|
||||
audit_id = Column(Integer, ForeignKey('audits.id'), nullable=True)
|
||||
audit_id = Column(Integer, ForeignKey('audits.id'), nullable=False)
|
||||
strategy_id = Column(Integer, ForeignKey('strategies.id'), nullable=False)
|
||||
state = Column(String(20), nullable=True)
|
||||
global_efficacy = Column(JSONEncodedDict, nullable=True)
|
||||
|
||||
|
@ -74,8 +74,8 @@ class ContinuousAuditHandler(base.AuditHandler):
|
||||
|
||||
def do_execute(self, audit, request_context):
|
||||
# execute the strategy
|
||||
solution = self.strategy_context.execute_strategy(audit.uuid,
|
||||
request_context)
|
||||
solution = self.strategy_context.execute_strategy(
|
||||
audit, request_context)
|
||||
|
||||
if audit.audit_type == audit_objects.AuditType.CONTINUOUS.value:
|
||||
a_plan_filters = {'audit_uuid': audit.uuid,
|
||||
|
@ -20,7 +20,7 @@ from watcher.decision_engine.audit import base
|
||||
class OneShotAuditHandler(base.AuditHandler):
|
||||
def do_execute(self, audit, request_context):
|
||||
# execute the strategy
|
||||
solution = self.strategy_context.execute_strategy(audit.uuid,
|
||||
request_context)
|
||||
solution = self.strategy_context.execute_strategy(
|
||||
audit, request_context)
|
||||
|
||||
return solution
|
||||
|
@ -48,10 +48,11 @@ class DefaultPlanner(base.BasePlanner):
|
||||
|
||||
@classmethod
|
||||
def get_config_opts(cls):
|
||||
return [cfg.DictOpt(
|
||||
'weights',
|
||||
help="These weights are used to schedule the actions",
|
||||
default=cls.weights_dict),
|
||||
return [
|
||||
cfg.DictOpt(
|
||||
'weights',
|
||||
help="These weights are used to schedule the actions",
|
||||
default=cls.weights_dict),
|
||||
]
|
||||
|
||||
def create_action(self,
|
||||
@ -113,9 +114,13 @@ class DefaultPlanner(base.BasePlanner):
|
||||
return action_plan
|
||||
|
||||
def _create_action_plan(self, context, audit_id, solution):
|
||||
strategy = objects.Strategy.get_by_name(
|
||||
context, solution.strategy.name)
|
||||
|
||||
action_plan_dict = {
|
||||
'uuid': utils.generate_uuid(),
|
||||
'audit_id': audit_id,
|
||||
'strategy_id': strategy.id,
|
||||
'first_action_id': None,
|
||||
'state': objects.action_plan.State.RECOMMENDED,
|
||||
'global_efficacy': solution.global_efficacy,
|
||||
|
@ -22,6 +22,16 @@ import six
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class BaseStrategyContext(object):
|
||||
|
||||
@abc.abstractmethod
|
||||
def execute_strategy(self, audit_uuid, request_context):
|
||||
def execute_strategy(self, audit, request_context):
|
||||
"""Execute the strategy for the given an audit
|
||||
|
||||
:param audit: Audit object
|
||||
:type audit: :py:class:`~.objects.audit.Audit` instance
|
||||
:param request_context: Current request context
|
||||
:type request_context: :py:class:`~.RequestContext` instance
|
||||
:returns: The computed solution
|
||||
:rtype: :py:class:`~.BaseSolution` instance
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
@ -30,9 +30,7 @@ class DefaultStrategyContext(base.BaseStrategyContext):
|
||||
super(DefaultStrategyContext, self).__init__()
|
||||
LOG.debug("Initializing Strategy Context")
|
||||
|
||||
def execute_strategy(self, audit_uuid, request_context):
|
||||
audit = objects.Audit.get_by_uuid(request_context, audit_uuid)
|
||||
|
||||
def execute_strategy(self, audit, request_context):
|
||||
osc = clients.OpenStackClients()
|
||||
# todo(jed) retrieve in audit parameters (threshold,...)
|
||||
# todo(jed) create ActionPlan
|
||||
|
@ -22,6 +22,8 @@ from watcher._i18n import _LI, _LW
|
||||
from watcher.common import context
|
||||
from watcher.decision_engine.loading import default
|
||||
from watcher import objects
|
||||
from watcher.objects import action_plan as apobjects
|
||||
from watcher.objects import audit as auditobjects
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
@ -54,6 +56,8 @@ class Syncer(object):
|
||||
self.strategy_mapping = dict()
|
||||
|
||||
self.stale_audit_templates_map = {}
|
||||
self.stale_audits_map = {}
|
||||
self.stale_action_plans_map = {}
|
||||
|
||||
@property
|
||||
def available_goals(self):
|
||||
@ -118,7 +122,7 @@ class Syncer(object):
|
||||
|
||||
self.strategy_mapping.update(self._sync_strategy(strategy_map))
|
||||
|
||||
self._sync_audit_templates()
|
||||
self._sync_objects()
|
||||
|
||||
def _sync_goal(self, goal_map):
|
||||
goal_name = goal_map.name
|
||||
@ -177,25 +181,45 @@ class Syncer(object):
|
||||
|
||||
return strategy_mapping
|
||||
|
||||
def _sync_audit_templates(self):
|
||||
# First we find audit templates that are stale because their associated
|
||||
# goal or strategy has been modified and we update them in-memory
|
||||
def _sync_objects(self):
|
||||
# First we find audit templates, audits and action plans that are stale
|
||||
# because their associated goal or strategy has been modified and we
|
||||
# update them in-memory
|
||||
self._find_stale_audit_templates_due_to_goal()
|
||||
self._find_stale_audit_templates_due_to_strategy()
|
||||
|
||||
# Then we handle the case where an audit template became
|
||||
# stale because its related goal does not exist anymore.
|
||||
self._find_stale_audits_due_to_goal()
|
||||
self._find_stale_audits_due_to_strategy()
|
||||
|
||||
self._find_stale_action_plans_due_to_strategy()
|
||||
self._find_stale_action_plans_due_to_audit()
|
||||
|
||||
# Then we handle the case where an audit template, an audit or an
|
||||
# action plan becomes stale because its related goal does not
|
||||
# exist anymore.
|
||||
self._soft_delete_removed_goals()
|
||||
# Then we handle the case where an audit template became
|
||||
# stale because its related strategy does not exist anymore.
|
||||
# Then we handle the case where an audit template, an audit or an
|
||||
# action plan becomes stale because its related strategy does not
|
||||
# exist anymore.
|
||||
self._soft_delete_removed_strategies()
|
||||
|
||||
# Finally, we save into the DB the updated stale audit templates
|
||||
# and soft delete stale audits and action plans
|
||||
for stale_audit_template in self.stale_audit_templates_map.values():
|
||||
stale_audit_template.save()
|
||||
LOG.info(_LI("Audit Template '%s' synced"),
|
||||
stale_audit_template.name)
|
||||
|
||||
for stale_audit in self.stale_audits_map.values():
|
||||
stale_audit.save()
|
||||
LOG.info(_LI("Stale audit '%s' synced and cancelled"),
|
||||
stale_audit.uuid)
|
||||
|
||||
for stale_action_plan in self.stale_action_plans_map.values():
|
||||
stale_action_plan.save()
|
||||
LOG.info(_LI("Stale action plan '%s' synced and cancelled"),
|
||||
stale_action_plan.uuid)
|
||||
|
||||
def _find_stale_audit_templates_due_to_goal(self):
|
||||
for goal_id, synced_goal in self.goal_mapping.items():
|
||||
filters = {"goal_id": goal_id}
|
||||
@ -228,6 +252,72 @@ class Syncer(object):
|
||||
self.stale_audit_templates_map[
|
||||
audit_template.id].strategy_id = synced_strategy.id
|
||||
|
||||
def _find_stale_audits_due_to_goal(self):
|
||||
for goal_id, synced_goal in self.goal_mapping.items():
|
||||
filters = {"goal_id": goal_id}
|
||||
stale_audits = objects.Audit.list(
|
||||
self.ctx, filters=filters)
|
||||
|
||||
# Update the goal ID for the stale audits (w/o saving)
|
||||
for audit in stale_audits:
|
||||
if audit.id not in self.stale_audits_map:
|
||||
audit.goal_id = synced_goal.id
|
||||
self.stale_audits_map[audit.id] = audit
|
||||
else:
|
||||
self.stale_audits_map[audit.id].goal_id = synced_goal.id
|
||||
|
||||
def _find_stale_audits_due_to_strategy(self):
|
||||
for strategy_id, synced_strategy in self.strategy_mapping.items():
|
||||
filters = {"strategy_id": strategy_id}
|
||||
stale_audits = objects.Audit.list(self.ctx, filters=filters)
|
||||
# Update strategy IDs for all stale audits (w/o saving)
|
||||
for audit in stale_audits:
|
||||
if audit.id not in self.stale_audits_map:
|
||||
audit.strategy_id = synced_strategy.id
|
||||
audit.state = auditobjects.State.CANCELLED
|
||||
self.stale_audits_map[audit.id] = audit
|
||||
else:
|
||||
self.stale_audits_map[
|
||||
audit.id].strategy_id = synced_strategy.id
|
||||
self.stale_audits_map[
|
||||
audit.id].state = auditobjects.State.CANCELLED
|
||||
|
||||
def _find_stale_action_plans_due_to_strategy(self):
|
||||
for strategy_id, synced_strategy in self.strategy_mapping.items():
|
||||
filters = {"strategy_id": strategy_id}
|
||||
stale_action_plans = objects.ActionPlan.list(
|
||||
self.ctx, filters=filters)
|
||||
|
||||
# Update strategy IDs for all stale action plans (w/o saving)
|
||||
for action_plan in stale_action_plans:
|
||||
if action_plan.id not in self.stale_action_plans_map:
|
||||
action_plan.strategy_id = synced_strategy.id
|
||||
action_plan.state = apobjects.State.CANCELLED
|
||||
self.stale_action_plans_map[action_plan.id] = action_plan
|
||||
else:
|
||||
self.stale_action_plans_map[
|
||||
action_plan.id].strategy_id = synced_strategy.id
|
||||
self.stale_action_plans_map[
|
||||
action_plan.id].state = apobjects.State.CANCELLED
|
||||
|
||||
def _find_stale_action_plans_due_to_audit(self):
|
||||
for audit_id, synced_audit in self.stale_audits_map.items():
|
||||
filters = {"audit_id": audit_id}
|
||||
stale_action_plans = objects.ActionPlan.list(
|
||||
self.ctx, filters=filters)
|
||||
|
||||
# Update audit IDs for all stale action plans (w/o saving)
|
||||
for action_plan in stale_action_plans:
|
||||
if action_plan.id not in self.stale_action_plans_map:
|
||||
action_plan.audit_id = synced_audit.id
|
||||
action_plan.state = apobjects.State.CANCELLED
|
||||
self.stale_action_plans_map[action_plan.id] = action_plan
|
||||
else:
|
||||
self.stale_action_plans_map[
|
||||
action_plan.id].audit_id = synced_audit.id
|
||||
self.stale_action_plans_map[
|
||||
action_plan.id].state = apobjects.State.CANCELLED
|
||||
|
||||
def _soft_delete_removed_goals(self):
|
||||
removed_goals = [
|
||||
g for g in self.available_goals
|
||||
@ -235,12 +325,24 @@ class Syncer(object):
|
||||
for removed_goal in removed_goals:
|
||||
removed_goal.soft_delete()
|
||||
filters = {"goal_id": removed_goal.id}
|
||||
|
||||
invalid_ats = objects.AuditTemplate.list(self.ctx, filters=filters)
|
||||
for at in invalid_ats:
|
||||
LOG.warning(
|
||||
_LW("Audit Template '%(audit_template)s' references a "
|
||||
"goal that does not exist"),
|
||||
audit_template=at.uuid)
|
||||
"goal that does not exist"), audit_template=at.uuid)
|
||||
|
||||
stale_audits = objects.Audit.list(self.ctx, filters=filters)
|
||||
for audit in stale_audits:
|
||||
LOG.warning(
|
||||
_LW("Audit '%(audit)s' references a "
|
||||
"goal that does not exist"), audit=audit.uuid)
|
||||
if audit.id not in self.stale_audits_map:
|
||||
audit.state = auditobjects.State.CANCELLED
|
||||
self.stale_audits_map[audit.id] = audit
|
||||
else:
|
||||
self.stale_audits_map[
|
||||
audit.id].state = auditobjects.State.CANCELLED
|
||||
|
||||
def _soft_delete_removed_strategies(self):
|
||||
removed_strategies = [
|
||||
@ -265,6 +367,32 @@ class Syncer(object):
|
||||
else:
|
||||
self.stale_audit_templates_map[at.id].strategy_id = None
|
||||
|
||||
stale_audits = objects.Audit.list(self.ctx, filters=filters)
|
||||
for audit in stale_audits:
|
||||
LOG.warning(
|
||||
_LW("Audit '%(audit)s' references a "
|
||||
"strategy that does not exist"), audit=audit.uuid)
|
||||
if audit.id not in self.stale_audits_map:
|
||||
audit.state = auditobjects.State.CANCELLED
|
||||
self.stale_audits_map[audit.id] = audit
|
||||
else:
|
||||
self.stale_audits_map[
|
||||
audit.id].state = auditobjects.State.CANCELLED
|
||||
|
||||
stale_action_plans = objects.ActionPlan.list(
|
||||
self.ctx, filters=filters)
|
||||
for action_plan in stale_action_plans:
|
||||
LOG.warning(
|
||||
_LW("Action Plan '%(action_plan)s' references a "
|
||||
"strategy that does not exist"),
|
||||
action_plan=action_plan.uuid)
|
||||
if action_plan.id not in self.stale_action_plans_map:
|
||||
action_plan.state = apobjects.State.CANCELLED
|
||||
self.stale_action_plans_map[action_plan.id] = action_plan
|
||||
else:
|
||||
self.stale_action_plans_map[
|
||||
action_plan.id].state = apobjects.State.CANCELLED
|
||||
|
||||
def _discover(self):
|
||||
strategies_map = {}
|
||||
goals_map = {}
|
||||
|
@ -97,6 +97,7 @@ class ActionPlan(base.WatcherObject):
|
||||
'id': int,
|
||||
'uuid': obj_utils.str_or_none,
|
||||
'audit_id': obj_utils.int_or_none,
|
||||
'strategy_id': obj_utils.int_or_none,
|
||||
'first_action_id': obj_utils.int_or_none,
|
||||
'state': obj_utils.str_or_none,
|
||||
'global_efficacy': obj_utils.dict_or_none,
|
||||
@ -253,7 +254,7 @@ class ActionPlan(base.WatcherObject):
|
||||
self[field] = current[field]
|
||||
|
||||
def soft_delete(self, context=None):
|
||||
"""soft Delete the Action plan from the DB.
|
||||
"""Soft Delete the Action plan from the DB.
|
||||
|
||||
:param context: Security context. NOTE: This should only
|
||||
be used internally by the indirection_api.
|
||||
|
@ -13,39 +13,18 @@
|
||||
import datetime
|
||||
import itertools
|
||||
import mock
|
||||
import pecan
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_serialization import jsonutils
|
||||
from wsme import types as wtypes
|
||||
|
||||
from watcher.api.controllers.v1 import action_plan as api_action_plan
|
||||
from watcher.applier import rpcapi as aapi
|
||||
from watcher.common import context
|
||||
from watcher.common import utils
|
||||
from watcher.db import api as db_api
|
||||
from watcher import objects
|
||||
from watcher.tests.api import base as api_base
|
||||
from watcher.tests.api import utils as api_utils
|
||||
from watcher.tests import base
|
||||
from watcher.tests.objects import utils as obj_utils
|
||||
|
||||
|
||||
class TestActionPlanObject(base.TestCase):
|
||||
|
||||
@mock.patch.object(objects.EfficacyIndicator,
|
||||
'list', mock.Mock(return_value=[]))
|
||||
@mock.patch.object(pecan, 'request')
|
||||
def test_action_plan_init(self, m_request):
|
||||
m_request.context = context.make_context()
|
||||
act_plan_dict = api_utils.action_plan_post_data()
|
||||
del act_plan_dict['state']
|
||||
del act_plan_dict['audit_id']
|
||||
del act_plan_dict['first_action_id']
|
||||
act_plan = api_action_plan.ActionPlan(**act_plan_dict)
|
||||
self.assertEqual(wtypes.Unset, act_plan.state)
|
||||
|
||||
|
||||
class TestListActionPlan(api_base.FunctionalTest):
|
||||
|
||||
def test_empty(self):
|
||||
@ -53,20 +32,21 @@ class TestListActionPlan(api_base.FunctionalTest):
|
||||
self.assertEqual([], response['action_plans'])
|
||||
|
||||
def _assert_action_plans_fields(self, action_plan):
|
||||
action_plan_fields = ['uuid', 'audit_uuid', 'state', 'global_efficacy',
|
||||
'efficacy_indicators']
|
||||
action_plan_fields = [
|
||||
'uuid', 'audit_uuid', 'strategy_uuid', 'strategy_name',
|
||||
'state', 'global_efficacy', 'efficacy_indicators']
|
||||
for field in action_plan_fields:
|
||||
self.assertIn(field, action_plan)
|
||||
|
||||
def test_one(self):
|
||||
action_plan = obj_utils.create_action_plan_without_audit(self.context)
|
||||
action_plan = obj_utils.create_test_action_plan(self.context)
|
||||
response = self.get_json('/action_plans')
|
||||
self.assertEqual(action_plan.uuid,
|
||||
response['action_plans'][0]["uuid"])
|
||||
self._assert_action_plans_fields(response['action_plans'][0])
|
||||
|
||||
def test_one_soft_deleted(self):
|
||||
action_plan = obj_utils.create_action_plan_without_audit(self.context)
|
||||
action_plan = obj_utils.create_test_action_plan(self.context)
|
||||
action_plan.soft_delete()
|
||||
response = self.get_json('/action_plans',
|
||||
headers={'X-Show-Deleted': 'True'})
|
||||
@ -100,7 +80,7 @@ class TestListActionPlan(api_base.FunctionalTest):
|
||||
self._assert_action_plans_fields(response)
|
||||
|
||||
def test_get_one_soft_deleted(self):
|
||||
action_plan = obj_utils.create_action_plan_without_audit(self.context)
|
||||
action_plan = obj_utils.create_test_action_plan(self.context)
|
||||
action_plan.soft_delete()
|
||||
response = self.get_json('/action_plans/%s' % action_plan['uuid'],
|
||||
headers={'X-Show-Deleted': 'True'})
|
||||
@ -112,15 +92,14 @@ class TestListActionPlan(api_base.FunctionalTest):
|
||||
self.assertEqual(404, response.status_int)
|
||||
|
||||
def test_detail(self):
|
||||
action_plan = obj_utils.create_test_action_plan(self.context,
|
||||
audit_id=None)
|
||||
action_plan = obj_utils.create_test_action_plan(self.context)
|
||||
response = self.get_json('/action_plans/detail')
|
||||
self.assertEqual(action_plan.uuid,
|
||||
response['action_plans'][0]["uuid"])
|
||||
self._assert_action_plans_fields(response['action_plans'][0])
|
||||
|
||||
def test_detail_soft_deleted(self):
|
||||
action_plan = obj_utils.create_action_plan_without_audit(self.context)
|
||||
action_plan = obj_utils.create_test_action_plan(self.context)
|
||||
action_plan.soft_delete()
|
||||
response = self.get_json('/action_plans/detail',
|
||||
headers={'X-Show-Deleted': 'True'})
|
||||
@ -141,7 +120,7 @@ class TestListActionPlan(api_base.FunctionalTest):
|
||||
def test_many(self):
|
||||
action_plan_list = []
|
||||
for id_ in range(5):
|
||||
action_plan = obj_utils.create_action_plan_without_audit(
|
||||
action_plan = obj_utils.create_test_action_plan(
|
||||
self.context, id=id_, uuid=utils.generate_uuid())
|
||||
action_plan_list.append(action_plan.uuid)
|
||||
response = self.get_json('/action_plans')
|
||||
@ -225,7 +204,7 @@ class TestListActionPlan(api_base.FunctionalTest):
|
||||
def test_many_without_soft_deleted(self):
|
||||
action_plan_list = []
|
||||
for id_ in [1, 2, 3]:
|
||||
action_plan = obj_utils.create_action_plan_without_audit(
|
||||
action_plan = obj_utils.create_test_action_plan(
|
||||
self.context, id=id_, uuid=utils.generate_uuid())
|
||||
action_plan_list.append(action_plan.uuid)
|
||||
for id_ in [4, 5]:
|
||||
@ -240,11 +219,11 @@ class TestListActionPlan(api_base.FunctionalTest):
|
||||
def test_many_with_soft_deleted(self):
|
||||
action_plan_list = []
|
||||
for id_ in [1, 2, 3]:
|
||||
action_plan = obj_utils.create_action_plan_without_audit(
|
||||
action_plan = obj_utils.create_test_action_plan(
|
||||
self.context, id=id_, uuid=utils.generate_uuid())
|
||||
action_plan_list.append(action_plan.uuid)
|
||||
for id_ in [4, 5]:
|
||||
action_plan = obj_utils.create_action_plan_without_audit(
|
||||
action_plan = obj_utils.create_test_action_plan(
|
||||
self.context, id=id_, uuid=utils.generate_uuid())
|
||||
action_plan.soft_delete()
|
||||
action_plan_list.append(action_plan.uuid)
|
||||
@ -272,8 +251,7 @@ class TestListActionPlan(api_base.FunctionalTest):
|
||||
|
||||
def test_links(self):
|
||||
uuid = utils.generate_uuid()
|
||||
obj_utils.create_action_plan_without_audit(self.context,
|
||||
id=1, uuid=uuid)
|
||||
obj_utils.create_test_action_plan(self.context, id=1, uuid=uuid)
|
||||
response = self.get_json('/action_plans/%s' % uuid)
|
||||
self.assertIn('links', response.keys())
|
||||
self.assertEqual(2, len(response['links']))
|
||||
@ -284,7 +262,7 @@ class TestListActionPlan(api_base.FunctionalTest):
|
||||
|
||||
def test_collection_links(self):
|
||||
for id_ in range(5):
|
||||
obj_utils.create_action_plan_without_audit(
|
||||
obj_utils.create_test_action_plan(
|
||||
self.context, id=id_, uuid=utils.generate_uuid())
|
||||
response = self.get_json('/action_plans/?limit=3')
|
||||
self.assertEqual(3, len(response['action_plans']))
|
||||
@ -296,9 +274,8 @@ class TestListActionPlan(api_base.FunctionalTest):
|
||||
cfg.CONF.set_override('max_limit', 3, 'api',
|
||||
enforce_type=True)
|
||||
for id_ in range(5):
|
||||
obj_utils.create_action_plan_without_audit(
|
||||
self.context, id=id_, uuid=utils.generate_uuid(),
|
||||
audit_id=None)
|
||||
obj_utils.create_test_action_plan(
|
||||
self.context, id=id_, uuid=utils.generate_uuid())
|
||||
response = self.get_json('/action_plans')
|
||||
self.assertEqual(3, len(response['action_plans']))
|
||||
|
||||
@ -310,7 +287,7 @@ class TestDelete(api_base.FunctionalTest):
|
||||
|
||||
def setUp(self):
|
||||
super(TestDelete, self).setUp()
|
||||
self.action_plan = obj_utils.create_action_plan_without_audit(
|
||||
self.action_plan = obj_utils.create_test_action_plan(
|
||||
self.context)
|
||||
p = mock.patch.object(db_api.BaseConnection, 'destroy_action_plan')
|
||||
self.mock_action_plan_delete = p.start()
|
||||
@ -366,7 +343,7 @@ class TestPatch(api_base.FunctionalTest):
|
||||
|
||||
def setUp(self):
|
||||
super(TestPatch, self).setUp()
|
||||
self.action_plan = obj_utils.create_action_plan_without_audit(
|
||||
self.action_plan = obj_utils.create_test_action_plan(
|
||||
self.context, state=objects.action_plan.State.RECOMMENDED)
|
||||
p = mock.patch.object(db_api.BaseConnection, 'update_action_plan')
|
||||
self.mock_action_plan_update = p.start()
|
||||
@ -459,7 +436,7 @@ class TestPatch(api_base.FunctionalTest):
|
||||
response = self.patch_json(
|
||||
'/action_plans/%s' % self.action_plan.uuid,
|
||||
[{'path': '/state', 'value': new_state,
|
||||
'op': 'replace'}])
|
||||
'op': 'replace'}])
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual(200, response.status_code)
|
||||
applier_mock.assert_called_once_with(mock.ANY,
|
||||
@ -509,7 +486,7 @@ class TestPatchStateTransitionDenied(api_base.FunctionalTest):
|
||||
db_api.BaseConnection, 'update_action_plan',
|
||||
mock.Mock(side_effect=lambda ap: ap.save() or ap))
|
||||
def test_replace_state_pending_denied(self):
|
||||
action_plan = obj_utils.create_action_plan_without_audit(
|
||||
action_plan = obj_utils.create_test_action_plan(
|
||||
self.context, state=self.original_state)
|
||||
|
||||
initial_ap = self.get_json('/action_plans/%s' % action_plan.uuid)
|
||||
@ -533,7 +510,7 @@ class TestPatchStateDeletedNotFound(api_base.FunctionalTest):
|
||||
db_api.BaseConnection, 'update_action_plan',
|
||||
mock.Mock(side_effect=lambda ap: ap.save() or ap))
|
||||
def test_replace_state_pending_not_found(self):
|
||||
action_plan = obj_utils.create_action_plan_without_audit(
|
||||
action_plan = obj_utils.create_test_action_plan(
|
||||
self.context, state=objects.action_plan.State.DELETED)
|
||||
|
||||
response = self.get_json(
|
||||
@ -561,15 +538,14 @@ class TestPatchStateTransitionOk(api_base.FunctionalTest):
|
||||
mock.Mock(side_effect=lambda ap: ap.save() or ap))
|
||||
@mock.patch.object(aapi.ApplierAPI, 'launch_action_plan', mock.Mock())
|
||||
def test_replace_state_pending_ok(self):
|
||||
action_plan = obj_utils.create_action_plan_without_audit(
|
||||
action_plan = obj_utils.create_test_action_plan(
|
||||
self.context, state=self.original_state)
|
||||
|
||||
initial_ap = self.get_json('/action_plans/%s' % action_plan.uuid)
|
||||
|
||||
response = self.patch_json(
|
||||
'/action_plans/%s' % action_plan.uuid,
|
||||
[{'path': '/state', 'value': self.new_state,
|
||||
'op': 'replace'}])
|
||||
[{'path': '/state', 'value': self.new_state, 'op': 'replace'}])
|
||||
updated_ap = self.get_json('/action_plans/%s' % action_plan.uuid)
|
||||
|
||||
self.assertNotEqual(self.new_state, initial_ap['state'])
|
||||
|
@ -155,14 +155,14 @@ class TestPurgeCommand(base.DbTestCase):
|
||||
|
||||
with freezegun.freeze_time(self.expired_date):
|
||||
self.action_plan1 = obj_utils.create_test_action_plan(
|
||||
self.context, audit_id=self.audit1.id,
|
||||
id=self._generate_id(), uuid=None)
|
||||
self.context, id=self._generate_id(), uuid=None,
|
||||
audit_id=self.audit1.id, strategy_id=self.strategy1.id)
|
||||
self.action_plan2 = obj_utils.create_test_action_plan(
|
||||
self.context, audit_id=self.audit2.id,
|
||||
id=self._generate_id(), uuid=None)
|
||||
self.context, id=self._generate_id(), uuid=None,
|
||||
audit_id=self.audit2.id, strategy_id=self.strategy2.id)
|
||||
self.action_plan3 = obj_utils.create_test_action_plan(
|
||||
self.context, audit_id=self.audit3.id,
|
||||
id=self._generate_id(), uuid=None)
|
||||
self.context, id=self._generate_id(), uuid=None,
|
||||
audit_id=self.audit3.id, strategy_id=self.strategy3.id)
|
||||
|
||||
self.action1 = obj_utils.create_test_action(
|
||||
self.context, action_plan_id=self.action_plan1.id,
|
||||
|
@ -124,6 +124,7 @@ def get_test_action_plan(**kwargs):
|
||||
'uuid': kwargs.get('uuid', '76be87bd-3422-43f9-93a0-e85a577e3061'),
|
||||
'state': kwargs.get('state', 'ONGOING'),
|
||||
'audit_id': kwargs.get('audit_id', 1),
|
||||
'strategy_id': kwargs.get('strategy_id', 1),
|
||||
'global_efficacy': kwargs.get('global_efficacy', {}),
|
||||
'first_action_id': kwargs.get('first_action_id', 1),
|
||||
'created_at': kwargs.get('created_at'),
|
||||
|
@ -13,10 +13,11 @@
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import mock
|
||||
|
||||
import uuid
|
||||
|
||||
from apscheduler.schedulers import background
|
||||
import mock
|
||||
|
||||
from watcher.decision_engine.audit import continuous
|
||||
from watcher.decision_engine.audit import oneshot
|
||||
@ -30,13 +31,17 @@ from watcher.tests.objects import utils as obj_utils
|
||||
|
||||
|
||||
class TestOneShotAuditHandler(base.DbTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestOneShotAuditHandler, self).setUp()
|
||||
obj_utils.create_test_goal(self.context, id=1, name="dummy")
|
||||
self.strategy = obj_utils.create_test_strategy(
|
||||
self.context, name='dummy')
|
||||
audit_template = obj_utils.create_test_audit_template(
|
||||
self.context)
|
||||
self.context, strategy_id=self.strategy.id)
|
||||
self.audit = obj_utils.create_test_audit(
|
||||
self.context,
|
||||
strategy_id=self.strategy.id,
|
||||
audit_template_id=audit_template.id)
|
||||
|
||||
@mock.patch.object(manager.CollectorManager, "get_cluster_model_collector")
|
||||
@ -79,11 +84,12 @@ class TestContinuousAuditHandler(base.DbTestCase):
|
||||
obj_utils.create_test_goal(self.context, id=1, name="DUMMY")
|
||||
audit_template = obj_utils.create_test_audit_template(
|
||||
self.context)
|
||||
self.audits = [obj_utils.create_test_audit(
|
||||
self.context,
|
||||
uuid=uuid.uuid4(),
|
||||
audit_template_id=audit_template.id,
|
||||
audit_type=audit_objects.AuditType.CONTINUOUS.value)
|
||||
self.audits = [
|
||||
obj_utils.create_test_audit(
|
||||
self.context,
|
||||
uuid=uuid.uuid4(),
|
||||
audit_template_id=audit_template.id,
|
||||
audit_type=audit_objects.AuditType.CONTINUOUS.value)
|
||||
for i in range(2)]
|
||||
|
||||
@mock.patch.object(background.BackgroundScheduler, 'add_job')
|
||||
|
@ -81,8 +81,3 @@ class FakeDummy1(FakeGoal):
|
||||
class FakeDummy2(FakeGoal):
|
||||
NAME = "dummy_2"
|
||||
DISPLAY_NAME = "Dummy 2"
|
||||
|
||||
|
||||
class FakeOtherDummy2(FakeGoal):
|
||||
NAME = "dummy_2"
|
||||
DISPLAY_NAME = "Other Dummy 2"
|
||||
|
@ -59,11 +59,16 @@ class SolutionFakerSingleHyp(object):
|
||||
|
||||
class TestActionScheduling(base.DbTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestActionScheduling, self).setUp()
|
||||
self.strategy = db_utils.create_test_strategy(name="dummy")
|
||||
self.audit = db_utils.create_test_audit(
|
||||
uuid=utils.generate_uuid(), strategy_id=self.strategy.id)
|
||||
self.default_planner = pbase.DefaultPlanner(mock.Mock())
|
||||
|
||||
def test_schedule_actions(self):
|
||||
default_planner = pbase.DefaultPlanner(mock.Mock())
|
||||
audit = db_utils.create_test_audit(uuid=utils.generate_uuid())
|
||||
solution = dsol.DefaultSolution(
|
||||
goal=mock.Mock(), strategy=mock.Mock())
|
||||
goal=mock.Mock(), strategy=self.strategy)
|
||||
|
||||
parameters = {
|
||||
"source_node": "server1",
|
||||
@ -74,11 +79,12 @@ class TestActionScheduling(base.DbTestCase):
|
||||
input_parameters=parameters)
|
||||
|
||||
with mock.patch.object(
|
||||
pbase.DefaultPlanner, "create_action",
|
||||
wraps=default_planner.create_action) as m_create_action:
|
||||
default_planner.config.weights = {'migrate': 3}
|
||||
action_plan = default_planner.schedule(self.context,
|
||||
audit.id, solution)
|
||||
pbase.DefaultPlanner, "create_action",
|
||||
wraps=self.default_planner.create_action
|
||||
) as m_create_action:
|
||||
self.default_planner.config.weights = {'migrate': 3}
|
||||
action_plan = self.default_planner.schedule(
|
||||
self.context, self.audit.id, solution)
|
||||
|
||||
self.assertIsNotNone(action_plan.uuid)
|
||||
self.assertEqual(1, m_create_action.call_count)
|
||||
@ -87,10 +93,8 @@ class TestActionScheduling(base.DbTestCase):
|
||||
self.assertEqual("migrate", actions[0].action_type)
|
||||
|
||||
def test_schedule_two_actions(self):
|
||||
default_planner = pbase.DefaultPlanner(mock.Mock())
|
||||
audit = db_utils.create_test_audit(uuid=utils.generate_uuid())
|
||||
solution = dsol.DefaultSolution(
|
||||
goal=mock.Mock(), strategy=mock.Mock())
|
||||
goal=mock.Mock(), strategy=self.strategy)
|
||||
|
||||
parameters = {
|
||||
"source_node": "server1",
|
||||
@ -105,11 +109,12 @@ class TestActionScheduling(base.DbTestCase):
|
||||
input_parameters={})
|
||||
|
||||
with mock.patch.object(
|
||||
pbase.DefaultPlanner, "create_action",
|
||||
wraps=default_planner.create_action) as m_create_action:
|
||||
default_planner.config.weights = {'migrate': 3, 'nop': 0}
|
||||
action_plan = default_planner.schedule(self.context,
|
||||
audit.id, solution)
|
||||
pbase.DefaultPlanner, "create_action",
|
||||
wraps=self.default_planner.create_action
|
||||
) as m_create_action:
|
||||
self.default_planner.config.weights = {'migrate': 3, 'nop': 0}
|
||||
action_plan = self.default_planner.schedule(
|
||||
self.context, self.audit.id, solution)
|
||||
self.assertIsNotNone(action_plan.uuid)
|
||||
self.assertEqual(2, m_create_action.call_count)
|
||||
# check order
|
||||
@ -119,10 +124,8 @@ class TestActionScheduling(base.DbTestCase):
|
||||
self.assertEqual("migrate", actions[1].action_type)
|
||||
|
||||
def test_schedule_actions_with_unknown_action(self):
|
||||
default_planner = pbase.DefaultPlanner(mock.Mock())
|
||||
audit = db_utils.create_test_audit(uuid=utils.generate_uuid())
|
||||
solution = dsol.DefaultSolution(
|
||||
goal=mock.Mock(), strategy=mock.Mock())
|
||||
goal=mock.Mock(), strategy=self.strategy)
|
||||
|
||||
parameters = {
|
||||
"src_uuid_node": "server1",
|
||||
@ -137,11 +140,12 @@ class TestActionScheduling(base.DbTestCase):
|
||||
input_parameters={})
|
||||
|
||||
with mock.patch.object(
|
||||
pbase.DefaultPlanner, "create_action",
|
||||
wraps=default_planner.create_action) as m_create_action:
|
||||
default_planner.config.weights = {'migrate': 0}
|
||||
self.assertRaises(KeyError, default_planner.schedule,
|
||||
self.context, audit.id, solution)
|
||||
pbase.DefaultPlanner, "create_action",
|
||||
wraps=self.default_planner.create_action
|
||||
) as m_create_action:
|
||||
self.default_planner.config.weights = {'migrate': 0}
|
||||
self.assertRaises(KeyError, self.default_planner.schedule,
|
||||
self.context, self.audit.id, solution)
|
||||
self.assertEqual(2, m_create_action.call_count)
|
||||
|
||||
|
||||
@ -158,6 +162,7 @@ class TestDefaultPlanner(base.DbTestCase):
|
||||
}
|
||||
|
||||
obj_utils.create_test_audit_template(self.context)
|
||||
self.strategy = obj_utils.create_test_strategy(self.context)
|
||||
|
||||
p = mock.patch.object(db_api.BaseConnection, 'create_action_plan')
|
||||
self.mock_create_action_plan = p.start()
|
||||
@ -179,14 +184,18 @@ class TestDefaultPlanner(base.DbTestCase):
|
||||
action.create()
|
||||
return action
|
||||
|
||||
def test_schedule_scheduled_empty(self):
|
||||
@mock.patch.object(objects.Strategy, 'get_by_name')
|
||||
def test_schedule_scheduled_empty(self, m_get_by_name):
|
||||
m_get_by_name.return_value = self.strategy
|
||||
audit = db_utils.create_test_audit(uuid=utils.generate_uuid())
|
||||
fake_solution = SolutionFakerSingleHyp.build()
|
||||
action_plan = self.default_planner.schedule(self.context,
|
||||
audit.id, fake_solution)
|
||||
self.assertIsNotNone(action_plan.uuid)
|
||||
|
||||
def test_scheduler_warning_empty_action_plan(self):
|
||||
@mock.patch.object(objects.Strategy, 'get_by_name')
|
||||
def test_scheduler_warning_empty_action_plan(self, m_get_by_name):
|
||||
m_get_by_name.return_value = self.strategy
|
||||
audit = db_utils.create_test_audit(uuid=utils.generate_uuid())
|
||||
fake_solution = SolutionFaker.build()
|
||||
action_plan = self.default_planner.schedule(self.context,
|
||||
|
@ -44,7 +44,7 @@ class TestStrategyContext(base.DbTestCase):
|
||||
mock_call.return_value = strategies.DummyStrategy(
|
||||
config=mock.Mock())
|
||||
solution = self.strategy_context.execute_strategy(
|
||||
self.audit.uuid, self.context)
|
||||
self.audit, self.context)
|
||||
self.assertIsInstance(solution, default.DefaultSolution)
|
||||
|
||||
@mock.patch.object(manager.CollectorManager, "get_cluster_model_collector",
|
||||
@ -65,8 +65,7 @@ class TestStrategyContext(base.DbTestCase):
|
||||
uuid=utils.generate_uuid(),
|
||||
)
|
||||
|
||||
solution = self.strategy_context.execute_strategy(
|
||||
audit.uuid, self.context)
|
||||
solution = self.strategy_context.execute_strategy(audit, self.context)
|
||||
|
||||
self.assertEqual(len(solution.actions), 3)
|
||||
|
||||
@ -92,7 +91,6 @@ class TestStrategyContext(base.DbTestCase):
|
||||
uuid=utils.generate_uuid(),
|
||||
)
|
||||
|
||||
solution = self.strategy_context.execute_strategy(
|
||||
audit.uuid, self.context)
|
||||
solution = self.strategy_context.execute_strategy(audit, self.context)
|
||||
|
||||
self.assertEqual(solution, expected_strategy)
|
||||
|
@ -21,6 +21,7 @@ from watcher.common import utils
|
||||
from watcher.decision_engine.loading import default
|
||||
from watcher.decision_engine import sync
|
||||
from watcher import objects
|
||||
from watcher.objects import action_plan as ap_objects
|
||||
from watcher.tests.db import base
|
||||
from watcher.tests.decision_engine import fake_goals
|
||||
from watcher.tests.decision_engine import fake_strategies
|
||||
@ -73,6 +74,27 @@ class TestSyncer(base.DbTestCase):
|
||||
self.addCleanup(p_goals_load.stop)
|
||||
self.addCleanup(p_strategies.stop)
|
||||
|
||||
@staticmethod
|
||||
def _find_created_modified_unmodified_ids(befores, afters):
|
||||
created = {
|
||||
a_item.id: a_item for a_item in afters
|
||||
if a_item.uuid not in (b_item.uuid for b_item in befores)
|
||||
}
|
||||
|
||||
modified = {
|
||||
a_item.id: a_item for a_item in afters
|
||||
if a_item.as_dict() not in (
|
||||
b_items.as_dict() for b_items in befores)
|
||||
}
|
||||
|
||||
unmodified = {
|
||||
a_item.id: a_item for a_item in afters
|
||||
if a_item.as_dict() in (
|
||||
b_items.as_dict() for b_items in befores)
|
||||
}
|
||||
|
||||
return created, modified, unmodified
|
||||
|
||||
@mock.patch.object(objects.Strategy, "soft_delete")
|
||||
@mock.patch.object(objects.Strategy, "save")
|
||||
@mock.patch.object(objects.Strategy, "create")
|
||||
@ -257,15 +279,18 @@ class TestSyncer(base.DbTestCase):
|
||||
strategy1 = objects.Strategy(
|
||||
self.ctx, id=1, name="strategy_1", uuid=utils.generate_uuid(),
|
||||
display_name="Strategy 1", goal_id=goal1.id)
|
||||
# Should stay unmodified after sync()
|
||||
# Should be modified after sync() because its related goal has been
|
||||
# modified
|
||||
strategy2 = objects.Strategy(
|
||||
self.ctx, id=2, name="strategy_2", uuid=utils.generate_uuid(),
|
||||
display_name="Strategy 2", goal_id=goal2.id)
|
||||
# Should be modified by the sync()
|
||||
# Should be modified after sync() because its strategy name has been
|
||||
# modified
|
||||
strategy3 = objects.Strategy(
|
||||
self.ctx, id=3, name="strategy_3", uuid=utils.generate_uuid(),
|
||||
display_name="Original", goal_id=goal2.id)
|
||||
# Should be modified by the sync()
|
||||
display_name="Original", goal_id=goal1.id)
|
||||
# Should be modified after sync() because both its related goal
|
||||
# and its strategy name have been modified
|
||||
strategy4 = objects.Strategy(
|
||||
self.ctx, id=4, name="strategy_4", uuid=utils.generate_uuid(),
|
||||
display_name="Original", goal_id=goal2.id)
|
||||
@ -279,18 +304,18 @@ class TestSyncer(base.DbTestCase):
|
||||
|
||||
# Should stay unmodified after sync()
|
||||
audit_template1 = objects.AuditTemplate(
|
||||
self.ctx, id=1, uuid=utils.generate_uuid(),
|
||||
name="Synced AT1", goal_id=goal1.id, strategy_id=strategy1.id)
|
||||
self.ctx, id=1, name="Synced AT1", uuid=utils.generate_uuid(),
|
||||
goal_id=goal1.id, strategy_id=strategy1.id)
|
||||
# Should be modified by the sync() because its associated goal
|
||||
# should be modified
|
||||
# has been modified (compared to the defined fake goals)
|
||||
audit_template2 = objects.AuditTemplate(
|
||||
self.ctx, id=2, name="Synced AT2", uuid=utils.generate_uuid(),
|
||||
goal_id=goal2.id, strategy_id=strategy2.id)
|
||||
# Should be modified by the sync() because its associated strategy
|
||||
# should be modified
|
||||
# has been modified (compared to the defined fake strategies)
|
||||
audit_template3 = objects.AuditTemplate(
|
||||
self.ctx, id=3, name="Synced AT3", uuid=utils.generate_uuid(),
|
||||
goal_id=goal2.id, strategy_id=strategy3.id)
|
||||
goal_id=goal1.id, strategy_id=strategy3.id)
|
||||
# Modified because of both because its associated goal and associated
|
||||
# strategy should be modified
|
||||
audit_template4 = objects.AuditTemplate(
|
||||
@ -301,9 +326,70 @@ class TestSyncer(base.DbTestCase):
|
||||
audit_template3.create()
|
||||
audit_template4.create()
|
||||
|
||||
before_audit_templates = objects.AuditTemplate.list(self.ctx)
|
||||
# Should stay unmodified after sync()
|
||||
audit1 = objects.Audit(
|
||||
self.ctx, id=1, uuid=utils.generate_uuid(),
|
||||
goal_id=goal1.id, strategy_id=strategy1.id)
|
||||
# Should be modified by the sync() because its associated goal
|
||||
# has been modified (compared to the defined fake goals)
|
||||
audit2 = objects.Audit(
|
||||
self.ctx, id=2, uuid=utils.generate_uuid(),
|
||||
goal_id=goal2.id, strategy_id=strategy2.id)
|
||||
# Should be modified by the sync() because its associated strategy
|
||||
# has been modified (compared to the defined fake strategies)
|
||||
audit3 = objects.Audit(
|
||||
self.ctx, id=3, uuid=utils.generate_uuid(),
|
||||
goal_id=goal1.id, strategy_id=strategy3.id)
|
||||
# Modified because of both because its associated goal and associated
|
||||
# strategy should be modified (compared to the defined fake
|
||||
# goals/strategies)
|
||||
audit4 = objects.Audit(
|
||||
self.ctx, id=4, uuid=utils.generate_uuid(),
|
||||
goal_id=goal2.id, strategy_id=strategy4.id)
|
||||
|
||||
audit1.create()
|
||||
audit2.create()
|
||||
audit3.create()
|
||||
audit4.create()
|
||||
|
||||
# Should stay unmodified after sync()
|
||||
action_plan1 = objects.ActionPlan(
|
||||
self.ctx, id=1, uuid=utils.generate_uuid(),
|
||||
audit_id=audit1.id, strategy_id=strategy1.id,
|
||||
first_action_id=None, state='DOESNOTMATTER',
|
||||
global_efficacy={})
|
||||
# Stale after syncing because the goal of the audit has been modified
|
||||
# (compared to the defined fake goals)
|
||||
action_plan2 = objects.ActionPlan(
|
||||
self.ctx, id=2, uuid=utils.generate_uuid(),
|
||||
audit_id=audit2.id, strategy_id=strategy2.id,
|
||||
first_action_id=None, state='DOESNOTMATTER',
|
||||
global_efficacy={})
|
||||
# Stale after syncing because the strategy has been modified
|
||||
# (compared to the defined fake strategies)
|
||||
action_plan3 = objects.ActionPlan(
|
||||
self.ctx, id=3, uuid=utils.generate_uuid(),
|
||||
audit_id=audit3.id, strategy_id=strategy3.id,
|
||||
first_action_id=None, state='DOESNOTMATTER',
|
||||
global_efficacy={})
|
||||
# Stale after syncing because both the strategy and the related audit
|
||||
# have been modified (compared to the defined fake goals/strategies)
|
||||
action_plan4 = objects.ActionPlan(
|
||||
self.ctx, id=4, uuid=utils.generate_uuid(),
|
||||
audit_id=audit4.id, strategy_id=strategy4.id,
|
||||
first_action_id=None, state='DOESNOTMATTER',
|
||||
global_efficacy={})
|
||||
|
||||
action_plan1.create()
|
||||
action_plan2.create()
|
||||
action_plan3.create()
|
||||
action_plan4.create()
|
||||
|
||||
before_goals = objects.Goal.list(self.ctx)
|
||||
before_strategies = objects.Strategy.list(self.ctx)
|
||||
before_audit_templates = objects.AuditTemplate.list(self.ctx)
|
||||
before_audits = objects.Audit.list(self.ctx)
|
||||
before_action_plans = objects.ActionPlan.list(self.ctx)
|
||||
|
||||
# ### Action under test ### #
|
||||
|
||||
@ -314,30 +400,51 @@ class TestSyncer(base.DbTestCase):
|
||||
|
||||
# ### Assertions ### #
|
||||
|
||||
after_audit_templates = objects.AuditTemplate.list(self.ctx)
|
||||
after_goals = objects.Goal.list(self.ctx)
|
||||
after_strategies = objects.Strategy.list(self.ctx)
|
||||
after_audit_templates = objects.AuditTemplate.list(self.ctx)
|
||||
after_audits = objects.Audit.list(self.ctx)
|
||||
after_action_plans = objects.ActionPlan.list(self.ctx)
|
||||
|
||||
self.assertEqual(2, len(before_goals))
|
||||
self.assertEqual(4, len(before_strategies))
|
||||
self.assertEqual(4, len(before_audit_templates))
|
||||
self.assertEqual(4, len(before_audits))
|
||||
self.assertEqual(4, len(before_action_plans))
|
||||
self.assertEqual(2, len(after_goals))
|
||||
self.assertEqual(4, len(after_strategies))
|
||||
self.assertEqual(4, len(after_audit_templates))
|
||||
self.assertEqual(4, len(after_audits))
|
||||
self.assertEqual(4, len(after_action_plans))
|
||||
|
||||
self.assertEqual(
|
||||
{"dummy_1", "dummy_2"},
|
||||
set([g.name for g in after_goals]))
|
||||
self.assertEqual(
|
||||
{"strategy_1", "strategy_2", "strategy_3", "strategy_4"},
|
||||
set([s.name for s in after_strategies]))
|
||||
created_goals = {
|
||||
ag.name: ag for ag in after_goals
|
||||
if ag.uuid not in [bg.uuid for bg in before_goals]
|
||||
}
|
||||
created_strategies = {
|
||||
a_s.name: a_s for a_s in after_strategies
|
||||
if a_s.uuid not in [b_s.uuid for b_s in before_strategies]
|
||||
}
|
||||
|
||||
created_goals, modified_goals, unmodified_goals = (
|
||||
self._find_created_modified_unmodified_ids(
|
||||
before_goals, after_goals))
|
||||
|
||||
created_strategies, modified_strategies, unmodified_strategies = (
|
||||
self._find_created_modified_unmodified_ids(
|
||||
before_strategies, after_strategies))
|
||||
|
||||
(created_audit_templates, modified_audit_templates,
|
||||
unmodified_audit_templates) = (
|
||||
self._find_created_modified_unmodified_ids(
|
||||
before_audit_templates, after_audit_templates))
|
||||
|
||||
created_audits, modified_audits, unmodified_audits = (
|
||||
self._find_created_modified_unmodified_ids(
|
||||
before_audits, after_audits))
|
||||
|
||||
(created_action_plans, modified_action_plans,
|
||||
unmodified_action_plans) = (
|
||||
self._find_created_modified_unmodified_ids(
|
||||
before_action_plans, after_action_plans))
|
||||
|
||||
dummy_1_spec = [
|
||||
{'description': 'Dummy indicator', 'name': 'dummy',
|
||||
@ -351,40 +458,34 @@ class TestSyncer(base.DbTestCase):
|
||||
|
||||
self.assertEqual(1, len(created_goals))
|
||||
self.assertEqual(3, len(created_strategies))
|
||||
|
||||
modified_audit_templates = {
|
||||
a_at.id for a_at in after_audit_templates
|
||||
if a_at.goal_id not in (
|
||||
# initial goal IDs
|
||||
b_at.goal_id for b_at in before_audit_templates) or
|
||||
a_at.strategy_id not in (
|
||||
# initial strategy IDs
|
||||
b_at.strategy_id for b_at in before_audit_templates
|
||||
if b_at.strategy_id is not None)
|
||||
}
|
||||
|
||||
unmodified_audit_templates = {
|
||||
a_at.id for a_at in after_audit_templates
|
||||
if a_at.goal_id in (
|
||||
# initial goal IDs
|
||||
b_at.goal_id for b_at in before_audit_templates) and
|
||||
a_at.strategy_id in (
|
||||
# initial strategy IDs
|
||||
b_at.strategy_id for b_at in before_audit_templates
|
||||
if b_at.strategy_id is not None)
|
||||
}
|
||||
self.assertEqual(0, len(created_audits))
|
||||
self.assertEqual(0, len(created_action_plans))
|
||||
|
||||
self.assertEqual(2, strategy2.goal_id)
|
||||
self.assertIn(strategy2.name, created_strategies)
|
||||
self.assertNotEqual(strategy2.id,
|
||||
created_strategies[strategy2.name].id)
|
||||
|
||||
self.assertEqual(set([audit_template2.id,
|
||||
audit_template3.id,
|
||||
audit_template4.id]),
|
||||
modified_audit_templates)
|
||||
self.assertNotEqual(
|
||||
set([strategy2.id, strategy3.id, strategy4.id]),
|
||||
set(modified_strategies))
|
||||
self.assertEqual(set([strategy1.id]), set(unmodified_strategies))
|
||||
|
||||
self.assertEqual(
|
||||
set([audit_template2.id, audit_template3.id, audit_template4.id]),
|
||||
set(modified_audit_templates))
|
||||
self.assertEqual(set([audit_template1.id]),
|
||||
unmodified_audit_templates)
|
||||
set(unmodified_audit_templates))
|
||||
|
||||
self.assertEqual(
|
||||
set([audit2.id, audit3.id, audit4.id]),
|
||||
set(modified_audits))
|
||||
self.assertEqual(set([audit1.id]), set(unmodified_audits))
|
||||
|
||||
self.assertEqual(
|
||||
set([action_plan2.id, action_plan3.id, action_plan4.id]),
|
||||
set(modified_action_plans))
|
||||
self.assertTrue(
|
||||
all(ap.state == ap_objects.State.CANCELLED
|
||||
for ap in modified_action_plans.values()))
|
||||
self.assertEqual(set([action_plan1.id]), set(unmodified_action_plans))
|
||||
|
||||
def test_end2end_sync_goals_with_removed_goal_and_strategy(self):
|
||||
# ### Setup ### #
|
||||
@ -417,11 +518,13 @@ class TestSyncer(base.DbTestCase):
|
||||
strategy1 = objects.Strategy(
|
||||
self.ctx, id=1, name="strategy_1", uuid=utils.generate_uuid(),
|
||||
display_name="Strategy 1", goal_id=goal1.id)
|
||||
# To be removed by the sync()
|
||||
# To be removed by the sync() because strategy entry point does not
|
||||
# exist anymore
|
||||
strategy2 = objects.Strategy(
|
||||
self.ctx, id=2, name="strategy_2", uuid=utils.generate_uuid(),
|
||||
display_name="Strategy 2", goal_id=goal1.id)
|
||||
# To be removed by the sync()
|
||||
# To be removed by the sync() because the goal has been soft deleted
|
||||
# and because the strategy entry point does not exist anymore
|
||||
strategy3 = objects.Strategy(
|
||||
self.ctx, id=3, name="strategy_3", uuid=utils.generate_uuid(),
|
||||
display_name="Original", goal_id=goal2.id)
|
||||
@ -435,9 +538,9 @@ class TestSyncer(base.DbTestCase):
|
||||
# The strategy of this audit template will be dereferenced
|
||||
# as it does not exist anymore
|
||||
audit_template1 = objects.AuditTemplate(
|
||||
self.ctx, id=1, uuid=utils.generate_uuid(),
|
||||
name="Synced AT1", goal_id=goal1.id, strategy_id=strategy1.id)
|
||||
# Stale even after syncing because the goal has been soft deleted
|
||||
self.ctx, id=1, name="Synced AT1", uuid=utils.generate_uuid(),
|
||||
goal_id=goal1.id, strategy_id=strategy1.id)
|
||||
# Stale after syncing because the goal has been soft deleted
|
||||
audit_template2 = objects.AuditTemplate(
|
||||
self.ctx, id=2, name="Synced AT2", uuid=utils.generate_uuid(),
|
||||
goal_id=goal2.id, strategy_id=strategy2.id)
|
||||
@ -445,9 +548,39 @@ class TestSyncer(base.DbTestCase):
|
||||
audit_template1.create()
|
||||
audit_template2.create()
|
||||
|
||||
before_audit_templates = objects.AuditTemplate.list(self.ctx)
|
||||
# Should stay unmodified after sync()
|
||||
audit1 = objects.Audit(
|
||||
self.ctx, id=1, uuid=utils.generate_uuid(),
|
||||
goal_id=goal1.id, strategy_id=strategy1.id)
|
||||
# Stale after syncing because the goal has been soft deleted
|
||||
audit2 = objects.Audit(
|
||||
self.ctx, id=2, uuid=utils.generate_uuid(),
|
||||
goal_id=goal2.id, strategy_id=strategy2.id)
|
||||
audit1.create()
|
||||
audit2.create()
|
||||
|
||||
# Stale after syncing because its related strategy has been be
|
||||
# soft deleted
|
||||
action_plan1 = objects.ActionPlan(
|
||||
self.ctx, id=1, uuid=utils.generate_uuid(),
|
||||
audit_id=audit1.id, strategy_id=strategy1.id,
|
||||
first_action_id=None, state='DOESNOTMATTER',
|
||||
global_efficacy={})
|
||||
# Stale after syncing because its related goal has been soft deleted
|
||||
action_plan2 = objects.ActionPlan(
|
||||
self.ctx, id=2, uuid=utils.generate_uuid(),
|
||||
audit_id=audit2.id, strategy_id=strategy2.id,
|
||||
first_action_id=None, state='DOESNOTMATTER',
|
||||
global_efficacy={})
|
||||
|
||||
action_plan1.create()
|
||||
action_plan2.create()
|
||||
|
||||
before_goals = objects.Goal.list(self.ctx)
|
||||
before_strategies = objects.Strategy.list(self.ctx)
|
||||
before_audit_templates = objects.AuditTemplate.list(self.ctx)
|
||||
before_audits = objects.Audit.list(self.ctx)
|
||||
before_action_plans = objects.ActionPlan.list(self.ctx)
|
||||
|
||||
# ### Action under test ### #
|
||||
|
||||
@ -458,54 +591,66 @@ class TestSyncer(base.DbTestCase):
|
||||
|
||||
# ### Assertions ### #
|
||||
|
||||
after_audit_templates = objects.AuditTemplate.list(self.ctx)
|
||||
after_goals = objects.Goal.list(self.ctx)
|
||||
after_strategies = objects.Strategy.list(self.ctx)
|
||||
after_audit_templates = objects.AuditTemplate.list(self.ctx)
|
||||
after_audits = objects.Audit.list(self.ctx)
|
||||
after_action_plans = objects.ActionPlan.list(self.ctx)
|
||||
|
||||
self.assertEqual(2, len(before_goals))
|
||||
self.assertEqual(3, len(before_strategies))
|
||||
self.assertEqual(2, len(before_audit_templates))
|
||||
self.assertEqual(2, len(before_audits))
|
||||
self.assertEqual(2, len(before_action_plans))
|
||||
self.assertEqual(1, len(after_goals))
|
||||
self.assertEqual(1, len(after_strategies))
|
||||
self.assertEqual(2, len(after_audit_templates))
|
||||
self.assertEqual(2, len(after_audits))
|
||||
self.assertEqual(2, len(after_action_plans))
|
||||
self.assertEqual(
|
||||
{"dummy_1"},
|
||||
set([g.name for g in after_goals]))
|
||||
self.assertEqual(
|
||||
{"strategy_1"},
|
||||
set([s.name for s in after_strategies]))
|
||||
created_goals = [ag for ag in after_goals
|
||||
if ag.uuid not in [bg.uuid for bg in before_goals]]
|
||||
created_strategies = [
|
||||
a_s for a_s in after_strategies
|
||||
if a_s.uuid not in [b_s.uuid for b_s in before_strategies]]
|
||||
|
||||
created_goals, modified_goals, unmodified_goals = (
|
||||
self._find_created_modified_unmodified_ids(
|
||||
before_goals, after_goals))
|
||||
|
||||
created_strategies, modified_strategies, unmodified_strategies = (
|
||||
self._find_created_modified_unmodified_ids(
|
||||
before_strategies, after_strategies))
|
||||
|
||||
(created_audit_templates, modified_audit_templates,
|
||||
unmodified_audit_templates) = (
|
||||
self._find_created_modified_unmodified_ids(
|
||||
before_audit_templates, after_audit_templates))
|
||||
|
||||
created_audits, modified_audits, unmodified_audits = (
|
||||
self._find_created_modified_unmodified_ids(
|
||||
before_audits, after_audits))
|
||||
|
||||
(created_action_plans, modified_action_plans,
|
||||
unmodified_action_plans) = (
|
||||
self._find_created_modified_unmodified_ids(
|
||||
before_action_plans, after_action_plans))
|
||||
|
||||
self.assertEqual(0, len(created_goals))
|
||||
self.assertEqual(0, len(created_strategies))
|
||||
|
||||
modified_audit_templates = {
|
||||
a_at.id for a_at in after_audit_templates
|
||||
if a_at.goal_id not in (
|
||||
# initial goal IDs
|
||||
b_at.goal_id for b_at in before_audit_templates) or
|
||||
a_at.strategy_id not in (
|
||||
# initial strategy IDs
|
||||
b_at.strategy_id for b_at in before_audit_templates
|
||||
if b_at.strategy_id is not None)
|
||||
}
|
||||
|
||||
unmodified_audit_templates = {
|
||||
a_at.id for a_at in after_audit_templates
|
||||
if a_at.goal_id in (
|
||||
# initial goal IDs
|
||||
b_at.goal_id for b_at in before_audit_templates) and
|
||||
a_at.strategy_id in (
|
||||
# initial strategy IDs
|
||||
b_at.strategy_id for b_at in before_audit_templates
|
||||
if b_at.strategy_id is not None)
|
||||
}
|
||||
self.assertEqual(0, len(created_audits))
|
||||
self.assertEqual(0, len(created_action_plans))
|
||||
|
||||
self.assertEqual(set([audit_template2.id]),
|
||||
modified_audit_templates)
|
||||
set(modified_audit_templates))
|
||||
self.assertEqual(set([audit_template1.id]),
|
||||
unmodified_audit_templates)
|
||||
set(unmodified_audit_templates))
|
||||
|
||||
self.assertEqual(set([audit2.id]), set(modified_audits))
|
||||
self.assertEqual(set([audit1.id]), set(unmodified_audits))
|
||||
|
||||
self.assertEqual(set([action_plan2.id]), set(modified_action_plans))
|
||||
self.assertTrue(
|
||||
all(ap.state == ap_objects.State.CANCELLED
|
||||
for ap in modified_action_plans.values()))
|
||||
self.assertEqual(set([action_plan1.id]), set(unmodified_action_plans))
|
||||
|
@ -100,16 +100,6 @@ def create_test_action_plan(context, **kw):
|
||||
return action_plan
|
||||
|
||||
|
||||
def create_action_plan_without_audit(context, **kw):
|
||||
"""Create and return a test action_plan object.
|
||||
|
||||
Create a action plan in the DB and return a ActionPlan object with
|
||||
appropriate attributes.
|
||||
"""
|
||||
kw['audit_id'] = None
|
||||
return create_test_action_plan(context, **kw)
|
||||
|
||||
|
||||
def get_test_action(context, **kw):
|
||||
"""Return a Action object with appropriate attributes.
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user