diff --git a/watcher/api/controllers/v1/action.py b/watcher/api/controllers/v1/action.py index d1fbe387f..8095f011d 100644 --- a/watcher/api/controllers/v1/action.py +++ b/watcher/api/controllers/v1/action.py @@ -151,8 +151,6 @@ class Action(base.APIBase): self.fields = [] fields = list(objects.Action.fields) - # audit_template_uuid is not part of objects.Audit.fields - # because it's an API-only attribute. fields.append('action_plan_uuid') fields.append('next_uuid') for field in fields: diff --git a/watcher/api/controllers/v1/audit.py b/watcher/api/controllers/v1/audit.py index a87ec27a3..77754738b 100644 --- a/watcher/api/controllers/v1/audit.py +++ b/watcher/api/controllers/v1/audit.py @@ -52,7 +52,11 @@ from watcher import objects class AuditPostType(wtypes.Base): - audit_template_uuid = wtypes.wsattr(types.uuid, mandatory=True) + audit_template_uuid = wtypes.wsattr(types.uuid, mandatory=False) + + goal = wtypes.wsattr(wtypes.text, mandatory=False) + + strategy = wtypes.wsattr(wtypes.text, mandatory=False) audit_type = wtypes.wsattr(wtypes.text, mandatory=True) @@ -65,7 +69,10 @@ class AuditPostType(wtypes.Base): default={}) interval = wsme.wsattr(int, mandatory=False) - def as_audit(self): + host_aggregate = wsme.wsattr(wtypes.IntegerType(minimum=1), + mandatory=False) + + def as_audit(self, context): audit_type_values = [val.value for val in objects.audit.AuditType] if self.audit_type not in audit_type_values: raise exception.AuditTypeNotFound(audit_type=self.audit_type) @@ -79,11 +86,39 @@ class AuditPostType(wtypes.Base): raise exception.AuditIntervalNotSpecified( audit_type=self.audit_type) + # If audit_template_uuid was provided, we will provide any + # variables not included in the request, but not override + # those variables that were included. + if self.audit_template_uuid: + try: + audit_template = objects.AuditTemplate.get( + context, self.audit_template_uuid) + except exception.AuditTemplateNotFound: + raise exception.Invalid( + message=_('The audit template UUID or name specified is ' + 'invalid')) + at2a = { + 'goal': 'goal_id', + 'strategy': 'strategy_id', + 'host_aggregate': 'host_aggregate' + } + to_string_fields = set(['goal', 'strategy']) + for k in at2a: + if not getattr(self, k): + try: + at_attr = getattr(audit_template, at2a[k]) + if at_attr and (k in to_string_fields): + at_attr = str(at_attr) + setattr(self, k, at_attr) + except AttributeError: + pass return Audit( - audit_template_id=self.audit_template_uuid, audit_type=self.audit_type, deadline=self.deadline, parameters=self.parameters, + goal_id=self.goal, + host_aggregate=self.host_aggregate, + strategy_id=self.strategy, interval=self.interval) @@ -110,45 +145,84 @@ class Audit(base.APIBase): This class enforces type checking and value constraints, and converts between the internal object model and the API representation of a audit. """ - _audit_template_uuid = None - _audit_template_name = None + _goal_uuid = None + _goal_name = None + _strategy_uuid = None + _strategy_name = None - def _get_audit_template(self, value): + def _get_goal(self, value): if value == wtypes.Unset: return None - audit_template = None + goal = None try: if utils.is_uuid_like(value) or utils.is_int_like(value): - audit_template = objects.AuditTemplate.get( + goal = objects.Goal.get( pecan.request.context, value) else: - audit_template = objects.AuditTemplate.get_by_name( + goal = objects.Goal.get_by_name( pecan.request.context, value) - except exception.AuditTemplateNotFound: + except exception.GoalNotFound: pass - if audit_template: - self.audit_template_id = audit_template.id - return audit_template + if goal: + self.goal_id = goal.id + return goal - def _get_audit_template_uuid(self): - return self._audit_template_uuid + def _get_goal_uuid(self): + return self._goal_uuid - def _set_audit_template_uuid(self, value): - if value and self._audit_template_uuid != value: - self._audit_template_uuid = None - audit_template = self._get_audit_template(value) - if audit_template: - self._audit_template_uuid = audit_template.uuid + def _set_goal_uuid(self, value): + if value and self._goal_uuid != value: + self._goal_uuid = None + goal = self._get_goal(value) + if goal: + self._goal_uuid = goal.uuid - def _get_audit_template_name(self): - return self._audit_template_name + def _get_goal_name(self): + return self._goal_name - def _set_audit_template_name(self, value): - if value and self._audit_template_name != value: - self._audit_template_name = None - audit_template = self._get_audit_template(value) - if audit_template: - self._audit_template_name = audit_template.name + def _set_goal_name(self, value): + if value and self._goal_name != value: + self._goal_name = None + goal = self._get_goal(value) + if goal: + self._goal_name = goal.name + + 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.GoalNotFound: + 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 = types.uuid """Unique UUID for this audit""" @@ -162,17 +236,21 @@ class Audit(base.APIBase): state = wtypes.text """This audit state""" - audit_template_uuid = wsme.wsproperty(wtypes.text, - _get_audit_template_uuid, - _set_audit_template_uuid, - mandatory=True) - """The UUID of the audit template this audit refers to""" + goal_uuid = wsme.wsproperty( + wtypes.text, _get_goal_uuid, _set_goal_uuid, mandatory=True) + """Goal UUID the audit template refers to""" - audit_template_name = wsme.wsproperty(wtypes.text, - _get_audit_template_name, - _set_audit_template_name, - mandatory=False) - """The name of the audit template this audit refers to""" + goal_name = wsme.wsproperty( + wtypes.text, _get_goal_name, _set_goal_name, mandatory=False) + """The name of the goal this audit template refers to""" + + strategy_uuid = wsme.wsproperty( + wtypes.text, _get_strategy_uuid, _set_strategy_uuid, mandatory=False) + """Strategy UUID the audit template refers to""" + + strategy_name = wsme.wsproperty( + wtypes.text, _get_strategy_name, _set_strategy_name, mandatory=False) + """The name of the strategy this audit template refers to""" parameters = {wtypes.text: types.jsontype} """The strategy parameters for this audit""" @@ -183,10 +261,12 @@ class Audit(base.APIBase): interval = wsme.wsattr(int, mandatory=False) """Launch audit periodically (in seconds)""" + host_aggregate = wtypes.IntegerType(minimum=1) + """ID of the Nova host aggregate targeted by the audit template""" + def __init__(self, **kwargs): self.fields = [] fields = list(objects.Audit.fields) - for k in fields: # Skip fields we do not expose. if not hasattr(self, k): @@ -194,27 +274,28 @@ class Audit(base.APIBase): self.fields.append(k) setattr(self, k, kwargs.get(k, wtypes.Unset)) - self.fields.append('audit_template_id') - - # audit_template_uuid & audit_template_name are not part of - # objects.Audit.fields because they're API-only attributes. - fields.append('audit_template_uuid') - setattr(self, 'audit_template_uuid', kwargs.get('audit_template_id', + self.fields.append('goal_id') + self.fields.append('strategy_id') + fields.append('goal_uuid') + setattr(self, 'goal_uuid', kwargs.get('goal_id', wtypes.Unset)) - fields.append('audit_template_name') - setattr(self, 'audit_template_name', kwargs.get('audit_template_id', + fields.append('goal_name') + setattr(self, 'goal_name', kwargs.get('goal_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)) @staticmethod def _convert_with_links(audit, url, expand=True): if not expand: audit.unset_fields_except(['uuid', 'audit_type', 'deadline', - 'state', 'audit_template_uuid', - 'audit_template_name', 'interval']) - - # The numeric ID should not be exposed to - # the user, it's internal only. - audit.audit_template_id = wtypes.Unset + 'state', 'goal_uuid', 'interval', + 'strategy_uuid', 'host_aggregate', + 'goal_name', 'strategy_name']) audit.links = [link.Link.make_link('self', url, 'audits', audit.uuid), @@ -240,7 +321,10 @@ class Audit(base.APIBase): deleted_at=None, updated_at=datetime.datetime.utcnow(), interval=7200) - sample._audit_template_uuid = '7ae81bb3-dec3-4289-8d6c-da80bd8001ae' + + sample.goal_id = '7ae81bb3-dec3-4289-8d6c-da80bd8001ae' + sample.strategy_id = '7ae81bb3-dec3-4289-8d6c-da80bd8001ff' + sample.host_aggregate = 1 return cls._convert_with_links(sample, 'http://localhost:9322', expand) @@ -263,12 +347,12 @@ class AuditCollection(collection.Collection): if 'sort_key' in kwargs: reverse = False - if kwargs['sort_key'] == 'audit_template_uuid': + if kwargs['sort_key'] == 'goal_uuid': if 'sort_dir' in kwargs: reverse = True if kwargs['sort_dir'] == 'desc' else False collection.audits = sorted( collection.audits, - key=lambda audit: audit.audit_template_uuid, + key=lambda audit: audit.goal_uuid, reverse=reverse) collection.next = collection.get_next(limit, url=url, **kwargs) @@ -296,24 +380,34 @@ class AuditsController(rest.RestController): def _get_audits_collection(self, marker, limit, sort_key, sort_dir, expand=False, - resource_url=None, audit_template=None): + resource_url=None, goal=None, + strategy=None, host_aggregate=None): limit = api_utils.validate_limit(limit) api_utils.validate_sort_dir(sort_dir) - marker_obj = None if marker: marker_obj = objects.Audit.get_by_uuid(pecan.request.context, marker) filters = {} - if audit_template: - if utils.is_uuid_like(audit_template): - filters['audit_template_uuid'] = audit_template + if goal: + if utils.is_uuid_like(goal): + filters['goal_uuid'] = goal else: - filters['audit_template_name'] = audit_template + # TODO(michaelgugino): add method to get goal by name. + filters['goal_name'] = goal - if sort_key == 'audit_template_uuid': - sort_db_key = None + if strategy: + if utils.is_uuid_like(strategy): + filters['strategy_uuid'] = strategy + else: + # TODO(michaelgugino): add method to get goal by name. + filters['strategy_name'] = strategy + + if sort_key == 'goal_uuid': + sort_db_key = 'goal_id' + elif sort_key == 'strategy_uuid': + sort_db_key = 'strategy_id' else: sort_db_key = sort_key @@ -328,33 +422,39 @@ class AuditsController(rest.RestController): sort_key=sort_key, sort_dir=sort_dir) - @wsme_pecan.wsexpose(AuditCollection, wtypes.text, types.uuid, int, - wtypes.text, wtypes.text) - def get_all(self, audit_template=None, marker=None, limit=None, - sort_key='id', sort_dir='asc'): + @wsme_pecan.wsexpose(AuditCollection, types.uuid, int, wtypes.text, + wtypes.text, wtypes.text, wtypes.text, int) + def get_all(self, marker=None, limit=None, + sort_key='id', sort_dir='asc', goal=None, + strategy=None, host_aggregate=None): """Retrieve a list of audits. - :param audit_template: Optional UUID or name of an audit :param marker: pagination marker for large data sets. :param limit: maximum number of resources to return in a single result. :param sort_key: column to sort results by. Default: id. :param sort_dir: direction to sort. "asc" or "desc". Default: asc. - template, to get only audits for that audit template. + id. + :param goal: goal UUID or name to filter by + :param strategy: strategy UUID or name to filter by + :param host_aggregate: Optional host_aggregate """ + context = pecan.request.context policy.enforce(context, 'audit:get_all', action='audit:get_all') + return self._get_audits_collection(marker, limit, sort_key, - sort_dir, - audit_template=audit_template) + sort_dir, goal=goal, + strategy=strategy, + host_aggregate=host_aggregate) @wsme_pecan.wsexpose(AuditCollection, wtypes.text, types.uuid, int, wtypes.text, wtypes.text) - def detail(self, audit_template=None, marker=None, limit=None, + def detail(self, goal=None, marker=None, limit=None, sort_key='id', sort_dir='asc'): """Retrieve a list of audits with detail. - :param audit_template: Optional UUID or name of an audit + :param goal: goal UUID or name to filter by :param marker: pagination marker for large data sets. :param limit: maximum number of resources to return in a single result. :param sort_key: column to sort results by. Default: id. @@ -373,7 +473,7 @@ class AuditsController(rest.RestController): return self._get_audits_collection(marker, limit, sort_key, sort_dir, expand, resource_url, - audit_template=audit_template) + goal=goal) @wsme_pecan.wsexpose(Audit, types.uuid) def get_one(self, audit_uuid): @@ -399,23 +499,22 @@ class AuditsController(rest.RestController): context = pecan.request.context policy.enforce(context, 'audit:create', action='audit:create') + audit = audit_p.as_audit(context) - audit = audit_p.as_audit() if self.from_audits: raise exception.OperationNotPermitted - if not audit._audit_template_uuid: + if not audit._goal_uuid: raise exception.Invalid( - message=_('The audit template UUID or name specified is ' - 'invalid')) + message=_('A valid goal_id or audit_template_id ' + 'must be provided')) - audit_template = objects.AuditTemplate.get(pecan.request.context, - audit._audit_template_uuid) - strategy_id = audit_template.strategy_id + strategy_uuid = audit.strategy_uuid no_schema = True - if strategy_id is not None: + if strategy_uuid is not None: # validate parameter when predefined strategy in audit template - strategy = objects.Strategy.get(pecan.request.context, strategy_id) + strategy = objects.Strategy.get(pecan.request.context, + strategy_uuid) schema = strategy.parameters_spec if schema: # validate input parameter with default value feedback @@ -429,7 +528,7 @@ class AuditsController(rest.RestController): 'parameter spec in predefined strategy')) audit_dict = audit.as_dict() - context = pecan.request.context + new_audit = objects.Audit(context, **audit_dict) new_audit.create(context) @@ -463,6 +562,7 @@ class AuditsController(rest.RestController): audit_to_update = objects.Audit.get_by_uuid(pecan.request.context, audit_uuid) + try: audit_dict = audit_to_update.as_dict() audit = Audit(**api_utils.apply_jsonpatch(audit_dict, patch)) diff --git a/watcher/cmd/dbmanage.py b/watcher/cmd/dbmanage.py index 37ea15f53..7835f1086 100644 --- a/watcher/cmd/dbmanage.py +++ b/watcher/cmd/dbmanage.py @@ -59,7 +59,7 @@ class DBCommand(object): @staticmethod def purge(): purge.purge(CONF.command.age_in_days, CONF.command.max_number, - CONF.command.audit_template, CONF.command.exclude_orphans, + CONF.command.goal, CONF.command.exclude_orphans, CONF.command.dry_run) @@ -115,8 +115,8 @@ def add_command_parsers(subparsers): "Prevents the deletion if exceeded. No limit if " "set to None.", type=int, default=None, nargs='?') - parser.add_argument('-t', '--audit-template', - help="UUID or name of the audit template to purge.", + parser.add_argument('-t', '--goal', + help="UUID or name of the goal to purge.", type=str, default=None, nargs='?') parser.add_argument('-e', '--exclude-orphans', action='store_true', help="Flag to indicate whether or not you want to " diff --git a/watcher/db/purge.py b/watcher/db/purge.py index 4a85b1047..975d307a1 100644 --- a/watcher/db/purge.py +++ b/watcher/db/purge.py @@ -145,27 +145,27 @@ class PurgeCommand(object): return expiry_date @classmethod - def get_audit_template_uuid(cls, uuid_or_name): + def get_goal_uuid(cls, uuid_or_name): if uuid_or_name is None: return query_func = None if not utils.is_uuid_like(uuid_or_name): - query_func = objects.AuditTemplate.get_by_name + query_func = objects.Goal.get_by_name else: - query_func = objects.AuditTemplate.get_by_uuid + query_func = objects.Goal.get_by_uuid try: - audit_template = query_func(cls.ctx, uuid_or_name) + goal = query_func(cls.ctx, uuid_or_name) except Exception as exc: LOG.exception(exc) - raise exception.AuditTemplateNotFound(audit_template=uuid_or_name) + raise exception.GoalNotFound(goal=uuid_or_name) - if not audit_template.deleted_at: + if not goal.deleted_at: raise exception.NotSoftDeletedStateError( - name=_('Audit Template'), id=uuid_or_name) + name=_('Goal'), id=uuid_or_name) - return audit_template.uuid + return goal.uuid def _find_goals(self, filters=None): return objects.Goal.list(self.ctx, filters=filters) @@ -209,11 +209,11 @@ class PurgeCommand(object): (audit_template.strategy_id and audit_template.strategy_id not in strategy_ids)] - audit_template_ids = [at.id for at in audit_templates - if at not in orphans.audit_templates] orphans.audits = [ audit for audit in audits - if audit.audit_template_id not in audit_template_ids] + if audit.goal_id not in goal_ids or + (audit.strategy_id and + audit.strategy_id not in strategy_ids)] # Objects with orphan parents are themselves orphans audit_ids = [audit.id for audit in audits @@ -270,6 +270,7 @@ class PurgeCommand(object): related_objs = WatcherObjectsMap() related_objs.strategies = self._find_strategies(filters) related_objs.audit_templates = self._find_audit_templates(filters) + related_objs.audits = self._find_audits(filters) objects_map += related_objs for strategy in objects_map.strategies: @@ -278,13 +279,6 @@ class PurgeCommand(object): filters.update(dict(strategy_id=strategy.id)) related_objs = WatcherObjectsMap() related_objs.audit_templates = self._find_audit_templates(filters) - objects_map += related_objs - - for audit_template in objects_map.audit_templates: - filters = {} - filters.update(base_filters) - filters.update(dict(audit_template_id=audit_template.id)) - related_objs = WatcherObjectsMap() related_objs.audits = self._find_audits(filters) objects_map += related_objs @@ -355,12 +349,9 @@ class PurgeCommand(object): ] # audits - audit_template_ids = [ - audit_template.id - for audit_template in related_objs.audit_templates] related_objs.audits = [ audit for audit in self._objects_map.audits - if audit.audit_template_id in audit_template_ids + if audit.goal_id in goal_ids ] # action plans @@ -443,7 +434,7 @@ class PurgeCommand(object): LOG.info(_LI("Purge process completed")) -def purge(age_in_days, max_number, audit_template, exclude_orphans, dry_run): +def purge(age_in_days, max_number, goal, exclude_orphans, dry_run): """Removes soft deleted objects from the database :param age_in_days: Number of days since deletion (from today) @@ -452,8 +443,8 @@ def purge(age_in_days, max_number, audit_template, exclude_orphans, dry_run): :param max_number: Max number of objects expected to be deleted. Prevents the deletion if exceeded. No limit if set to None. :type max_number: int - :param audit_template: UUID or name of the audit template to purge. - :type audit_template: str + :param goal: UUID or name of the goal to purge. + :type goal: str :param exclude_orphans: Flag to indicate whether or not you want to exclude orphans from deletion (default: False). :type exclude_orphans: bool @@ -467,11 +458,11 @@ def purge(age_in_days, max_number, audit_template, exclude_orphans, dry_run): LOG.info(_LI("[options] age_in_days = %s"), age_in_days) LOG.info(_LI("[options] max_number = %s"), max_number) - LOG.info(_LI("[options] audit_template = %s"), audit_template) + LOG.info(_LI("[options] goal = %s"), goal) LOG.info(_LI("[options] exclude_orphans = %s"), exclude_orphans) LOG.info(_LI("[options] dry_run = %s"), dry_run) - uuid = PurgeCommand.get_audit_template_uuid(audit_template) + uuid = PurgeCommand.get_goal_uuid(goal) cmd = PurgeCommand(age_in_days, max_number, uuid, exclude_orphans, dry_run) diff --git a/watcher/db/sqlalchemy/api.py b/watcher/db/sqlalchemy/api.py index 160292a4e..62c72c1e6 100644 --- a/watcher/db/sqlalchemy/api.py +++ b/watcher/db/sqlalchemy/api.py @@ -330,10 +330,13 @@ class Connection(api.BaseConnection): if filters is None: filters = {} - plain_fields = ['uuid', 'audit_type', 'state', 'audit_template_id'] + plain_fields = ['uuid', 'audit_type', 'state', 'goal_id', + 'strategy_id'] join_fieldmap = { - 'audit_template_uuid': ("uuid", models.AuditTemplate), - 'audit_template_name': ("name", models.AuditTemplate), + 'goal_uuid': ("uuid", models.Goal), + 'goal_name': ("name", models.Goal), + 'strategy_uuid': ("uuid", models.Strategy), + 'strategy_name': ("name", models.Strategy), } return self._add_filters( diff --git a/watcher/db/sqlalchemy/models.py b/watcher/db/sqlalchemy/models.py index 5c52e793b..3c8e19b77 100644 --- a/watcher/db/sqlalchemy/models.py +++ b/watcher/db/sqlalchemy/models.py @@ -173,10 +173,11 @@ class Audit(Base): audit_type = Column(String(20)) state = Column(String(20), nullable=True) deadline = Column(DateTime, nullable=True) - audit_template_id = Column(Integer, ForeignKey('audit_templates.id'), - nullable=False) parameters = Column(JSONEncodedDict, nullable=True) interval = Column(Integer, nullable=True) + host_aggregate = Column(Integer, nullable=True) + goal_id = Column(Integer, ForeignKey('goals.id'), nullable=False) + strategy_id = Column(Integer, ForeignKey('strategies.id'), nullable=True) class Action(Base): diff --git a/watcher/decision_engine/strategy/context/default.py b/watcher/decision_engine/strategy/context/default.py index 851c8b485..4ac263a81 100644 --- a/watcher/decision_engine/strategy/context/default.py +++ b/watcher/decision_engine/strategy/context/default.py @@ -33,23 +33,19 @@ class DefaultStrategyContext(base.BaseStrategyContext): def execute_strategy(self, audit_uuid, request_context): audit = objects.Audit.get_by_uuid(request_context, audit_uuid) - # Retrieve the Audit Template - audit_template = objects.AuditTemplate.get_by_id( - request_context, audit.audit_template_id) - osc = clients.OpenStackClients() - # todo(jed) retrieve in audit_template parameters (threshold,...) + # todo(jed) retrieve in audit parameters (threshold,...) # todo(jed) create ActionPlan - goal = objects.Goal.get_by_id(request_context, audit_template.goal_id) + goal = objects.Goal.get_by_id(request_context, audit.goal_id) - # NOTE(jed56) In the audit_template object, the 'strategy_id' attribute + # NOTE(jed56) In the audit object, the 'strategy_id' attribute # is optional. If the admin wants to force the trigger of a Strategy - # it could specify the Strategy uuid in the Audit Template. + # it could specify the Strategy uuid in the Audit. strategy_name = None - if audit_template.strategy_id: + if audit.strategy_id: strategy = objects.Strategy.get_by_id(request_context, - audit_template.strategy_id) + audit.strategy_id) strategy_name = strategy.name strategy_selector = default.DefaultStrategySelector( diff --git a/watcher/objects/audit.py b/watcher/objects/audit.py index ff04570ed..f8edfca6f 100644 --- a/watcher/objects/audit.py +++ b/watcher/objects/audit.py @@ -84,9 +84,11 @@ class Audit(base.WatcherObject): 'audit_type': obj_utils.str_or_none, 'state': obj_utils.str_or_none, 'deadline': obj_utils.datetime_or_str_or_none, - 'audit_template_id': obj_utils.int_or_none, 'parameters': obj_utils.dict_or_none, 'interval': obj_utils.int_or_none, + 'goal_id': obj_utils.int_or_none, + 'strategy_id': obj_utils.int_or_none, + 'host_aggregate': obj_utils.int_or_none, } @staticmethod diff --git a/watcher/tests/api/v1/test_audits.py b/watcher/tests/api/v1/test_audits.py index 52232fd04..48182c8e7 100644 --- a/watcher/tests/api/v1/test_audits.py +++ b/watcher/tests/api/v1/test_audits.py @@ -34,9 +34,15 @@ from watcher.tests.objects import utils as obj_utils def post_get_test_audit(**kw): audit = api_utils.audit_post_data(**kw) audit_template = db_utils.get_test_audit_template() - del audit['audit_template_id'] - audit['audit_template_uuid'] = kw.get('audit_template_uuid', - audit_template['uuid']) + goal = db_utils.get_test_goal() + del_keys = ['goal_id', 'strategy_id'] + add_keys = {'audit_template_uuid': audit_template['uuid'], + 'goal': goal['uuid'], + } + for k in del_keys: + del audit[k] + for k in add_keys: + audit[k] = kw.get(k, add_keys[k]) return audit @@ -44,21 +50,25 @@ def post_get_test_audit_with_predefined_strategy(**kw): spec = kw.pop('strategy_parameters_spec', {}) strategy_id = 2 strategy = db_utils.get_test_strategy(parameters_spec=spec, id=strategy_id) - + audit = api_utils.audit_post_data(**kw) audit_template = db_utils.get_test_audit_template( strategy_id=strategy['id']) - - audit = api_utils.audit_post_data(**kw) - del audit['audit_template_id'] - audit['audit_template_uuid'] = audit_template['uuid'] - + del_keys = ['goal_id', 'strategy_id'] + add_keys = {'audit_template_uuid': audit_template['uuid'], + } + for k in del_keys: + del audit[k] + for k in add_keys: + audit[k] = kw.get(k, add_keys[k]) return audit class TestAuditObject(base.TestCase): def test_audit_init(self): - audit_dict = api_utils.audit_post_data(audit_template_id=None) + audit_dict = api_utils.audit_post_data(audit_template_id=None, + goal_id=None, + strategy_id=None) del audit_dict['state'] audit = api_audit.Audit(**audit_dict) self.assertEqual(wtypes.Unset, audit.state) @@ -69,13 +79,16 @@ class TestListAudit(api_base.FunctionalTest): def setUp(self): super(TestListAudit, self).setUp() obj_utils.create_test_audit_template(self.context) + obj_utils.create_test_goal(self.context) + obj_utils.create_test_strategy(self.context) def test_empty(self): response = self.get_json('/audits') self.assertEqual([], response['audits']) def _assert_audit_fields(self, audit): - audit_fields = ['audit_type', 'deadline', 'state'] + audit_fields = ['audit_type', 'deadline', 'state', 'goal_uuid', + 'strategy_uuid', 'host_aggregate'] for field in audit_fields: self.assertIn(field, audit) @@ -180,23 +193,23 @@ class TestListAudit(api_base.FunctionalTest): uuids = [s['uuid'] for s in response['audits']] self.assertEqual(sorted(audit_list), sorted(uuids)) - def test_many_with_sort_key_audit_template_uuid(self): - audit_template_list = [] + def test_many_with_sort_key_goal_uuid(self): + goal_list = [] for id_ in range(5): - audit_template = obj_utils.create_test_audit_template( + goal = obj_utils.create_test_goal( self.context, - name='at{0}'.format(id_), + name='gl{0}'.format(id_), uuid=utils.generate_uuid()) obj_utils.create_test_audit( self.context, id=id_, uuid=utils.generate_uuid(), - audit_template_id=audit_template.id) - audit_template_list.append(audit_template.uuid) + goal_id=goal.id) + goal_list.append(goal.uuid) - response = self.get_json('/audits/?sort_key=audit_template_uuid') + response = self.get_json('/audits/?sort_key=goal_uuid') self.assertEqual(5, len(response['audits'])) - uuids = [s['audit_template_uuid'] for s in response['audits']] - self.assertEqual(sorted(audit_template_list), uuids) + uuids = [s['goal_uuid'] for s in response['audits']] + self.assertEqual(sorted(goal_list), uuids) def test_links(self): uuid = utils.generate_uuid() @@ -231,130 +244,6 @@ class TestListAudit(api_base.FunctionalTest): next_marker = response['audits'][-1]['uuid'] self.assertIn(next_marker, response['next']) - def test_filter_by_audit_template_uuid(self): - audit_template_uuid = utils.generate_uuid() - audit_template_name = 'My_Audit_Template' - - audit_template = obj_utils.create_test_audit_template( - self.context, - uuid=audit_template_uuid, - name=audit_template_name) - number_of_audits_with_audit_template_id = 5 - for id_ in range(number_of_audits_with_audit_template_id): - obj_utils.create_test_audit(self.context, id=id_, - uuid=utils.generate_uuid(), - audit_template_id=audit_template.id) - for id_ in range(6, 8): - obj_utils.create_test_audit(self.context, id=id_, - uuid=utils.generate_uuid()) - - response = self.get_json('/audits/?audit_template=%s' - % audit_template_uuid) - - audits = response['audits'] - self.assertEqual(5, len(audits)) - for audit in audits: - self.assertEqual(audit_template_uuid, - audit['audit_template_uuid']) - - def test_detail_filter_by_audit_template_uuid(self): - audit_template_uuid = utils.generate_uuid() - audit_template_name = 'My_Audit_Template' - - audit_template = obj_utils.create_test_audit_template( - self.context, - uuid=audit_template_uuid, - name=audit_template_name) - number_of_audits_with_audit_template_id = 5 - for id_ in range(number_of_audits_with_audit_template_id): - obj_utils.create_test_audit(self.context, id=id_, - uuid=utils.generate_uuid(), - audit_template_id=audit_template.id) - for id_ in range(6, 8): - obj_utils.create_test_audit(self.context, id=id_, - uuid=utils.generate_uuid()) - - response = self.get_json('/audits/detail?audit_template=%s' - % audit_template_uuid) - - audits = response['audits'] - self.assertEqual(5, len(audits)) - for audit in audits: - self.assertEqual(audit_template_uuid, - audit['audit_template_uuid']) - - def test_filter_by_audit_template_name(self): - audit_template_uuid = utils.generate_uuid() - audit_template_name = 'My_Audit_Template' - - audit_template = obj_utils.create_test_audit_template( - self.context, - uuid=audit_template_uuid, - name=audit_template_name) - - number_of_audits_with_audit_template_id = 5 - for id_ in range(number_of_audits_with_audit_template_id): - obj_utils.create_test_audit(self.context, id=id_, - uuid=utils.generate_uuid(), - audit_template_id=audit_template.id) - for id_ in range(6, 8): - obj_utils.create_test_audit(self.context, id=id_, - uuid=utils.generate_uuid()) - - response = self.get_json('/audits/?audit_template=%s' - % audit_template_name) - - audits = response['audits'] - self.assertEqual(5, len(audits)) - for audit in audits: - self.assertEqual(audit_template_uuid, - audit['audit_template_uuid']) - - def test_many_by_soft_deleted_audit_template(self): - audit_list = [] - audit_template1 = obj_utils.create_test_audit_template( - self.context, - uuid=utils.generate_uuid(), - name='at1', - id=3, - ) - - audit_template2 = obj_utils.create_test_audit_template( - self.context, - uuid=utils.generate_uuid(), - name='at2', - id=4, - ) - - for id_ in range(0, 2): - audit = obj_utils.create_test_audit( - self.context, id=id_, - uuid=utils.generate_uuid(), - audit_template_id=audit_template1.id) - audit_list.append(audit.uuid) - - for id_ in range(2, 4): - audit = obj_utils.create_test_audit( - self.context, id=id_, - uuid=utils.generate_uuid(), - audit_template_id=audit_template2.id) - audit_list.append(audit.uuid) - - self.delete('/audit_templates/%s' % audit_template1.uuid) - - response = self.get_json('/audits') - - self.assertEqual(len(audit_list), len(response['audits'])) - - for id_ in range(0, 2): - audit = response['audits'][id_] - self.assertIsNone(audit['audit_template_uuid']) - - for id_ in range(2, 4): - audit = response['audits'][id_] - self.assertEqual(audit_template2.uuid, - audit['audit_template_uuid']) - class TestPatch(api_base.FunctionalTest): @@ -457,6 +346,8 @@ class TestPost(api_base.FunctionalTest): def setUp(self): super(TestPost, self).setUp() obj_utils.create_test_audit_template(self.context) + obj_utils.create_test_goal(self.context) + obj_utils.create_test_strategy(self.context) p = mock.patch.object(db_api.BaseConnection, 'create_audit') self.mock_create_audit = p.start() self.mock_create_audit.side_effect = ( diff --git a/watcher/tests/cmd/test_db_manage.py b/watcher/tests/cmd/test_db_manage.py index e08424d59..74fb24fa5 100644 --- a/watcher/tests/cmd/test_db_manage.py +++ b/watcher/tests/cmd/test_db_manage.py @@ -107,15 +107,15 @@ class TestDBManageRunCommand(base.TestCase): def test_run_db_purge(self, m_purge_cls): m_purge = mock.Mock() m_purge_cls.return_value = m_purge - m_purge_cls.get_audit_template_uuid.return_value = 'Some UUID' + m_purge_cls.get_goal_uuid.return_value = 'Some UUID' cfg.CONF.register_opt(cfg.IntOpt("age_in_days"), group="command") cfg.CONF.register_opt(cfg.IntOpt("max_number"), group="command") - cfg.CONF.register_opt(cfg.StrOpt("audit_template"), group="command") + cfg.CONF.register_opt(cfg.StrOpt("goal"), group="command") cfg.CONF.register_opt(cfg.BoolOpt("exclude_orphans"), group="command") cfg.CONF.register_opt(cfg.BoolOpt("dry_run"), group="command") cfg.CONF.set_default("age_in_days", None, group="command") cfg.CONF.set_default("max_number", None, group="command") - cfg.CONF.set_default("audit_template", None, group="command") + cfg.CONF.set_default("goal", None, group="command") cfg.CONF.set_default("exclude_orphans", True, group="command") cfg.CONF.set_default("dry_run", False, group="command") @@ -130,15 +130,15 @@ class TestDBManageRunCommand(base.TestCase): def test_run_db_purge_negative_max_number(self, m_purge_cls, m_exit): m_purge = mock.Mock() m_purge_cls.return_value = m_purge - m_purge_cls.get_audit_template_uuid.return_value = 'Some UUID' + m_purge_cls.get_goal_uuid.return_value = 'Some UUID' cfg.CONF.register_opt(cfg.IntOpt("age_in_days"), group="command") cfg.CONF.register_opt(cfg.IntOpt("max_number"), group="command") - cfg.CONF.register_opt(cfg.StrOpt("audit_template"), group="command") + cfg.CONF.register_opt(cfg.StrOpt("goal"), group="command") cfg.CONF.register_opt(cfg.BoolOpt("exclude_orphans"), group="command") cfg.CONF.register_opt(cfg.BoolOpt("dry_run"), group="command") cfg.CONF.set_default("age_in_days", None, group="command") cfg.CONF.set_default("max_number", -1, group="command") - cfg.CONF.set_default("audit_template", None, group="command") + cfg.CONF.set_default("goal", None, group="command") cfg.CONF.set_default("exclude_orphans", True, group="command") cfg.CONF.set_default("dry_run", False, group="command") @@ -154,15 +154,15 @@ class TestDBManageRunCommand(base.TestCase): def test_run_db_purge_dry_run(self, m_purge_cls, m_exit): m_purge = mock.Mock() m_purge_cls.return_value = m_purge - m_purge_cls.get_audit_template_uuid.return_value = 'Some UUID' + m_purge_cls.get_goal_uuid.return_value = 'Some UUID' cfg.CONF.register_opt(cfg.IntOpt("age_in_days"), group="command") cfg.CONF.register_opt(cfg.IntOpt("max_number"), group="command") - cfg.CONF.register_opt(cfg.StrOpt("audit_template"), group="command") + cfg.CONF.register_opt(cfg.StrOpt("goal"), group="command") cfg.CONF.register_opt(cfg.BoolOpt("exclude_orphans"), group="command") cfg.CONF.register_opt(cfg.BoolOpt("dry_run"), group="command") cfg.CONF.set_default("age_in_days", None, group="command") cfg.CONF.set_default("max_number", None, group="command") - cfg.CONF.set_default("audit_template", None, group="command") + cfg.CONF.set_default("goal", None, group="command") cfg.CONF.set_default("exclude_orphans", True, group="command") cfg.CONF.set_default("dry_run", True, group="command") diff --git a/watcher/tests/db/test_audit.py b/watcher/tests/db/test_audit.py index cd46127c6..600ceb3b5 100644 --- a/watcher/tests/db/test_audit.py +++ b/watcher/tests/db/test_audit.py @@ -318,58 +318,6 @@ class DbAuditTestCase(base.DbTestCase): self.assertRaises(exception.AuditNotFound, self.dbapi.get_audit_by_id, self.context, 1234) - def test_get_audit_list_with_filter_by_audit_template_uuid(self): - - audit_template = self.dbapi.create_audit_template( - utils.get_test_audit_template( - uuid=w_utils.generate_uuid(), - name='My Audit Template 1', - description='Description of my audit template 1', - host_aggregate=5, - goal='DUMMY', - extra={'automatic': True}) - ) - - audit = self._create_test_audit( - audit_type='ONESHOT', - uuid=w_utils.generate_uuid(), - deadline=None, - state='ONGOING', - audit_template_id=audit_template.id) - - res = self.dbapi.get_audit_list( - self.context, - filters={'audit_template_uuid': audit_template.uuid}) - - for r in res: - self.assertEqual(audit['audit_template_id'], r.audit_template_id) - - def test_get_audit_list_with_filter_by_audit_template_name(self): - - audit_template = self.dbapi.create_audit_template( - utils.get_test_audit_template( - uuid=w_utils.generate_uuid(), - name='My Audit Template 1', - description='Description of my audit template 1', - host_aggregate=5, - goal='DUMMY', - extra={'automatic': True}) - ) - - audit = self._create_test_audit( - audit_type='ONESHOT', - uuid=w_utils.generate_uuid(), - deadline=None, - state='ONGOING', - audit_template_id=audit_template.id) - - res = self.dbapi.get_audit_list( - self.context, - filters={'audit_template_name': audit_template.name}) - - for r in res: - self.assertEqual(audit['audit_template_id'], r.audit_template_id) - def test_update_audit(self): audit = self._create_test_audit() res = self.dbapi.update_audit(audit['id'], {'name': 'updated-model'}) diff --git a/watcher/tests/db/test_purge.py b/watcher/tests/db/test_purge.py index 5e5d557b8..f94b2668e 100644 --- a/watcher/tests/db/test_purge.py +++ b/watcher/tests/db/test_purge.py @@ -143,14 +143,14 @@ class TestPurgeCommand(base.DbTestCase): with freezegun.freeze_time(self.expired_date): self.audit1 = obj_utils.create_test_audit( - self.context, audit_template_id=self.audit_template1.id, - id=self._generate_id(), uuid=None) + self.context, id=self._generate_id(), uuid=None, + goal_id=self.goal1.id, strategy_id=self.strategy1.id) self.audit2 = obj_utils.create_test_audit( - self.context, audit_template_id=self.audit_template2.id, - id=self._generate_id(), uuid=None) + self.context, id=self._generate_id(), uuid=None, + goal_id=self.goal2.id, strategy_id=self.strategy2.id) self.audit3 = obj_utils.create_test_audit( - self.context, audit_template_id=self.audit_template3.id, - id=self._generate_id(), uuid=None) + self.context, id=self._generate_id(), uuid=None, + goal_id=self.goal3.id, strategy_id=self.strategy3.id) self.audit1.soft_delete() with freezegun.freeze_time(self.expired_date): @@ -316,11 +316,11 @@ class TestPurgeCommand(base.DbTestCase): m_destroy_audit_template.assert_called_once_with( self.audit_template1.uuid) - m_destroy_audit.assert_called_once_with( + m_destroy_audit.assert_called_with( self.audit1.uuid) - m_destroy_action_plan.assert_called_once_with( + m_destroy_action_plan.assert_called_with( self.action_plan1.uuid) - m_destroy_action.assert_called_once_with( + m_destroy_action.assert_called_with( self.action1.uuid) @mock.patch.object(dbapi.Connection, "destroy_action") @@ -404,32 +404,23 @@ class TestPurgeCommand(base.DbTestCase): @mock.patch.object(dbapi.Connection, "destroy_audit_template") @mock.patch.object(dbapi.Connection, "destroy_strategy") @mock.patch.object(dbapi.Connection, "destroy_goal") - def test_purge_command_with_audit_template_ok( + def test_purge_command_with_strategy_uuid( self, m_destroy_goal, m_destroy_strategy, m_destroy_audit_template, m_destroy_audit, m_destroy_action_plan, m_destroy_action): - self.cmd.orphans = False - self.cmd.uuid = self.audit_template1.uuid + self.cmd.exclude_orphans = False + self.cmd.uuid = self.strategy1.uuid with freezegun.freeze_time(self.fake_today): self.cmd.execute() self.assertEqual(m_destroy_goal.call_count, 0) - self.assertEqual(m_destroy_strategy.call_count, 0) + self.assertEqual(m_destroy_strategy.call_count, 1) self.assertEqual(m_destroy_audit_template.call_count, 1) self.assertEqual(m_destroy_audit.call_count, 1) self.assertEqual(m_destroy_action_plan.call_count, 1) self.assertEqual(m_destroy_action.call_count, 1) - m_destroy_audit_template.assert_called_once_with( - self.audit_template1.uuid) - m_destroy_audit.assert_called_once_with( - self.audit1.uuid) - m_destroy_action_plan.assert_called_once_with( - self.action_plan1.uuid) - m_destroy_action.assert_called_once_with( - self.action1.uuid) - @mock.patch.object(dbapi.Connection, "destroy_action") @mock.patch.object(dbapi.Connection, "destroy_action_plan") @mock.patch.object(dbapi.Connection, "destroy_audit") @@ -440,7 +431,7 @@ class TestPurgeCommand(base.DbTestCase): self, m_destroy_goal, m_destroy_strategy, m_destroy_audit_template, m_destroy_audit, m_destroy_action_plan, m_destroy_action): - self.cmd.orphans = False + self.cmd.exclude_orphans = True self.cmd.uuid = self.audit_template2.uuid with freezegun.freeze_time(self.fake_today): @@ -463,7 +454,7 @@ class TestPurgeCommand(base.DbTestCase): self, m_destroy_goal, m_destroy_strategy, m_destroy_audit_template, m_destroy_audit, m_destroy_action_plan, m_destroy_action): - self.cmd.orphans = False + self.cmd.exclude_orphans = False self.cmd.uuid = self.audit_template3.uuid with freezegun.freeze_time(self.fake_today): diff --git a/watcher/tests/db/utils.py b/watcher/tests/db/utils.py index 881e4154a..fad11bc68 100644 --- a/watcher/tests/db/utils.py +++ b/watcher/tests/db/utils.py @@ -57,12 +57,14 @@ def get_test_audit(**kwargs): 'audit_type': kwargs.get('audit_type', 'ONESHOT'), 'state': kwargs.get('state'), 'deadline': kwargs.get('deadline'), - 'audit_template_id': kwargs.get('audit_template_id', 1), 'created_at': kwargs.get('created_at'), 'updated_at': kwargs.get('updated_at'), 'deleted_at': kwargs.get('deleted_at'), 'parameters': kwargs.get('parameters', {}), 'interval': kwargs.get('period', 3600), + 'goal_id': kwargs.get('goal_id', 1), + 'strategy_id': kwargs.get('strategy_id', None), + 'host_aggregate': kwargs.get('host_aggregate', 1), } diff --git a/watcher/tests/decision_engine/strategy/context/test_strategy_context.py b/watcher/tests/decision_engine/strategy/context/test_strategy_context.py index 9794e2db5..3bb03b106 100644 --- a/watcher/tests/decision_engine/strategy/context/test_strategy_context.py +++ b/watcher/tests/decision_engine/strategy/context/test_strategy_context.py @@ -59,15 +59,9 @@ class TestStrategyContext(base.DbTestCase): uuid=utils.generate_uuid(), name="dummy") - audit_template = obj_utils.create_test_audit_template( - self.context, - uuid=utils.generate_uuid(), - strategy_id=strategy.id, - name="my_template") - audit = obj_utils.create_test_audit( self.context, - audit_template_id=audit_template.id, + strategy_id=strategy.id, uuid=utils.generate_uuid(), ) @@ -92,15 +86,9 @@ class TestStrategyContext(base.DbTestCase): uuid=utils.generate_uuid(), name=expected_strategy) - audit_template = obj_utils.create_test_audit_template( - self.context, - uuid=utils.generate_uuid(), - strategy_id=strategy.id, - name="my_template") - audit = obj_utils.create_test_audit( self.context, - audit_template_id=audit_template.id, + strategy_id=strategy.id, uuid=utils.generate_uuid(), ) diff --git a/watcher_tempest_plugin/tests/api/admin/test_audit.py b/watcher_tempest_plugin/tests/api/admin/test_audit.py index fa6934e66..8bb6ba464 100644 --- a/watcher_tempest_plugin/tests/api/admin/test_audit.py +++ b/watcher_tempest_plugin/tests/api/admin/test_audit.py @@ -45,6 +45,8 @@ class TestCreateUpdateDeleteAudit(base.BaseInfraOptimTest): ) _, body = self.create_audit(**audit_params) + audit_params.pop('audit_template_uuid') + audit_params['goal_uuid'] = goal['uuid'] self.assert_expected(audit_params, body) _, audit = self.client.show_audit(body['uuid']) @@ -62,6 +64,8 @@ class TestCreateUpdateDeleteAudit(base.BaseInfraOptimTest): ) _, body = self.create_audit(**audit_params) + audit_params.pop('audit_template_uuid') + audit_params['goal_uuid'] = goal['uuid'] self.assert_expected(audit_params, body) _, audit = self.client.show_audit(body['uuid']) @@ -101,6 +105,8 @@ class TestCreateUpdateDeleteAudit(base.BaseInfraOptimTest): ) _, body = self.create_audit(**audit_params) + audit_params.pop('audit_template_uuid') + audit_params['goal_uuid'] = goal['uuid'] self.assert_expected(audit_params, body) _, audit = self.client.show_audit(body['uuid']) @@ -190,5 +196,5 @@ class TestShowListAudit(base.BaseInfraOptimTest): @test.attr(type='smoke') def test_list_audits_related_to_given_audit_template(self): _, body = self.client.list_audits( - audit_template=self.audit_template['uuid']) + goal=self.goal['uuid']) self.assertIn(self.audit['uuid'], [n['uuid'] for n in body['audits']])