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