Eager loading on One-to-X foreign keys

In this changeset, I added ORM relationships to the DB models
concerning the already-declared foreign keys.
I also modified the DB query building to now handle a new 'eager'
parameter that, if True, is responsible to also fetch the data
relative to these 'parent' DB entities (no cascading).

Change-Id: Ieea181af9a4b173c54621dcc6c549161f5a35aeb
Partially-Implements: blueprint watcher-versioned-objects
This commit is contained in:
Vincent Françoise 2016-09-23 11:00:41 +02:00
parent aae3f79fef
commit 750e6bf213
13 changed files with 534 additions and 302 deletions

View File

@ -36,7 +36,7 @@ class BaseConnection(object):
@abc.abstractmethod @abc.abstractmethod
def get_goal_list(self, context, filters=None, limit=None, def get_goal_list(self, context, filters=None, limit=None,
marker=None, sort_key=None, sort_dir=None): marker=None, sort_key=None, sort_dir=None, eager=False):
"""Get specific columns for matching goals. """Get specific columns for matching goals.
Return a list of the specified columns for all goals that Return a list of the specified columns for all goals that
@ -50,6 +50,7 @@ class BaseConnection(object):
:param sort_key: Attribute by which results should be sorted. :param sort_key: Attribute by which results should be sorted.
:param sort_dir: direction in which results should be sorted. :param sort_dir: direction in which results should be sorted.
(asc, desc) (asc, desc)
:param eager: If True, also loads One-to-X data (Default: False)
:returns: A list of tuples of the specified columns. :returns: A list of tuples of the specified columns.
""" """
@ -72,31 +73,34 @@ class BaseConnection(object):
""" """
@abc.abstractmethod @abc.abstractmethod
def get_goal_by_id(self, context, goal_id): def get_goal_by_id(self, context, goal_id, eager=False):
"""Return a goal given its ID. """Return a goal given its ID.
:param context: The security context :param context: The security context
:param goal_id: The ID of a goal :param goal_id: The ID of a goal
:param eager: If True, also loads One-to-X data (Default: False)
:returns: A goal :returns: A goal
:raises: :py:class:`~.GoalNotFound` :raises: :py:class:`~.GoalNotFound`
""" """
@abc.abstractmethod @abc.abstractmethod
def get_goal_by_uuid(self, context, goal_uuid): def get_goal_by_uuid(self, context, goal_uuid, eager=False):
"""Return a goal given its UUID. """Return a goal given its UUID.
:param context: The security context :param context: The security context
:param goal_uuid: The UUID of a goal :param goal_uuid: The UUID of a goal
:param eager: If True, also loads One-to-X data (Default: False)
:returns: A goal :returns: A goal
:raises: :py:class:`~.GoalNotFound` :raises: :py:class:`~.GoalNotFound`
""" """
@abc.abstractmethod @abc.abstractmethod
def get_goal_by_name(self, context, goal_name): def get_goal_by_name(self, context, goal_name, eager=False):
"""Return a goal given its name. """Return a goal given its name.
:param context: The security context :param context: The security context
:param goal_name: The name of a goal :param goal_name: The name of a goal
:param eager: If True, also loads One-to-X data (Default: False)
:returns: A goal :returns: A goal
:raises: :py:class:`~.GoalNotFound` :raises: :py:class:`~.GoalNotFound`
""" """
@ -129,9 +133,17 @@ class BaseConnection(object):
:raises: :py:class:`~.Invalid` :raises: :py:class:`~.Invalid`
""" """
def soft_delete_goal(self, goal_id):
"""Soft delete a goal.
:param goal_id: The id or uuid of a goal.
:raises: :py:class:`~.GoalNotFound`
"""
@abc.abstractmethod @abc.abstractmethod
def get_strategy_list(self, context, filters=None, limit=None, def get_strategy_list(self, context, filters=None, limit=None,
marker=None, sort_key=None, sort_dir=None): marker=None, sort_key=None, sort_dir=None,
eager=True):
"""Get specific columns for matching strategies. """Get specific columns for matching strategies.
Return a list of the specified columns for all strategies that Return a list of the specified columns for all strategies that
@ -146,6 +158,7 @@ class BaseConnection(object):
:param sort_key: Attribute by which results should be sorted. :param sort_key: Attribute by which results should be sorted.
:param sort_dir: Direction in which results should be sorted. :param sort_dir: Direction in which results should be sorted.
(asc, desc) (asc, desc)
:param eager: If True, also loads One-to-X data (Default: False)
:returns: A list of tuples of the specified columns. :returns: A list of tuples of the specified columns.
""" """
@ -170,31 +183,34 @@ class BaseConnection(object):
""" """
@abc.abstractmethod @abc.abstractmethod
def get_strategy_by_id(self, context, strategy_id): def get_strategy_by_id(self, context, strategy_id, eager=False):
"""Return a strategy given its ID. """Return a strategy given its ID.
:param context: The security context :param context: The security context
:param strategy_id: The ID of a strategy :param strategy_id: The ID of a strategy
:param eager: If True, also loads One-to-X data (Default: False)
:returns: A strategy :returns: A strategy
:raises: :py:class:`~.StrategyNotFound` :raises: :py:class:`~.StrategyNotFound`
""" """
@abc.abstractmethod @abc.abstractmethod
def get_strategy_by_uuid(self, context, strategy_uuid): def get_strategy_by_uuid(self, context, strategy_uuid, eager=False):
"""Return a strategy given its UUID. """Return a strategy given its UUID.
:param context: The security context :param context: The security context
:param strategy_uuid: The UUID of a strategy :param strategy_uuid: The UUID of a strategy
:param eager: If True, also loads One-to-X data (Default: False)
:returns: A strategy :returns: A strategy
:raises: :py:class:`~.StrategyNotFound` :raises: :py:class:`~.StrategyNotFound`
""" """
@abc.abstractmethod @abc.abstractmethod
def get_strategy_by_name(self, context, strategy_name): def get_strategy_by_name(self, context, strategy_name, eager=False):
"""Return a strategy given its name. """Return a strategy given its name.
:param context: The security context :param context: The security context
:param strategy_name: The name of a strategy :param strategy_name: The name of a strategy
:param eager: If True, also loads One-to-X data (Default: False)
:returns: A strategy :returns: A strategy
:raises: :py:class:`~.StrategyNotFound` :raises: :py:class:`~.StrategyNotFound`
""" """
@ -217,10 +233,17 @@ class BaseConnection(object):
:raises: :py:class:`~.Invalid` :raises: :py:class:`~.Invalid`
""" """
def soft_delete_strategy(self, strategy_id):
"""Soft delete a strategy.
:param strategy_id: The id or uuid of a strategy.
:raises: :py:class:`~.StrategyNotFound`
"""
@abc.abstractmethod @abc.abstractmethod
def get_audit_template_list(self, context, filters=None, def get_audit_template_list(self, context, filters=None,
limit=None, marker=None, sort_key=None, limit=None, marker=None, sort_key=None,
sort_dir=None): sort_dir=None, eager=False):
"""Get specific columns for matching audit templates. """Get specific columns for matching audit templates.
Return a list of the specified columns for all audit templates that Return a list of the specified columns for all audit templates that
@ -234,6 +257,7 @@ class BaseConnection(object):
:param sort_key: Attribute by which results should be sorted. :param sort_key: Attribute by which results should be sorted.
:param sort_dir: direction in which results should be sorted. :param sort_dir: direction in which results should be sorted.
(asc, desc) (asc, desc)
:param eager: If True, also loads One-to-X data (Default: False)
:returns: A list of tuples of the specified columns. :returns: A list of tuples of the specified columns.
""" """
@ -258,37 +282,43 @@ class BaseConnection(object):
""" """
@abc.abstractmethod @abc.abstractmethod
def get_audit_template_by_id(self, context, audit_template_id): def get_audit_template_by_id(self, context, audit_template_id,
eager=False):
"""Return an audit template. """Return an audit template.
:param context: The security context :param context: The security context
:param audit_template_id: The id of an audit template. :param audit_template_id: The id of an audit template.
:param eager: If True, also loads One-to-X data (Default: False)
:returns: An audit template. :returns: An audit template.
:raises: :py:class:`~.AuditTemplateNotFound` :raises: :py:class:`~.AuditTemplateNotFound`
""" """
@abc.abstractmethod @abc.abstractmethod
def get_audit_template_by_uuid(self, context, audit_template_uuid): def get_audit_template_by_uuid(self, context, audit_template_uuid,
eager=False):
"""Return an audit template. """Return an audit template.
:param context: The security context :param context: The security context
:param audit_template_uuid: The uuid of an audit template. :param audit_template_uuid: The uuid of an audit template.
:param eager: If True, also loads One-to-X data (Default: False)
:returns: An audit template. :returns: An audit template.
:raises: :py:class:`~.AuditTemplateNotFound` :raises: :py:class:`~.AuditTemplateNotFound`
""" """
def get_audit_template_by_name(self, context, audit_template_name): def get_audit_template_by_name(self, context, audit_template_name,
eager=False):
"""Return an audit template. """Return an audit template.
:param context: The security context :param context: The security context
:param audit_template_name: The name of an audit template. :param audit_template_name: The name of an audit template.
:param eager: If True, also loads One-to-X data (Default: False)
:returns: An audit template. :returns: An audit template.
:raises: :py:class:`~.AuditTemplateNotFound` :raises: :py:class:`~.AuditTemplateNotFound`
""" """
@abc.abstractmethod @abc.abstractmethod
def destroy_audit_template(self, audit_template_id): def destroy_audit_template(self, audit_template_id):
"""Destroy an audit_template. """Destroy an audit template.
:param audit_template_id: The id or uuid of an audit template. :param audit_template_id: The id or uuid of an audit template.
:raises: :py:class:`~.AuditTemplateNotFound` :raises: :py:class:`~.AuditTemplateNotFound`
@ -306,7 +336,7 @@ class BaseConnection(object):
@abc.abstractmethod @abc.abstractmethod
def soft_delete_audit_template(self, audit_template_id): def soft_delete_audit_template(self, audit_template_id):
"""Soft delete an audit_template. """Soft delete an audit template.
:param audit_template_id: The id or uuid of an audit template. :param audit_template_id: The id or uuid of an audit template.
:raises: :py:class:`~.AuditTemplateNotFound` :raises: :py:class:`~.AuditTemplateNotFound`
@ -314,7 +344,7 @@ class BaseConnection(object):
@abc.abstractmethod @abc.abstractmethod
def get_audit_list(self, context, filters=None, limit=None, def get_audit_list(self, context, filters=None, limit=None,
marker=None, sort_key=None, sort_dir=None): marker=None, sort_key=None, sort_dir=None, eager=False):
"""Get specific columns for matching audits. """Get specific columns for matching audits.
Return a list of the specified columns for all audits that match the Return a list of the specified columns for all audits that match the
@ -328,6 +358,7 @@ class BaseConnection(object):
:param sort_key: Attribute by which results should be sorted. :param sort_key: Attribute by which results should be sorted.
:param sort_dir: direction in which results should be sorted. :param sort_dir: direction in which results should be sorted.
(asc, desc) (asc, desc)
:param eager: If True, also loads One-to-X data (Default: False)
:returns: A list of tuples of the specified columns. :returns: A list of tuples of the specified columns.
""" """
@ -351,21 +382,23 @@ class BaseConnection(object):
""" """
@abc.abstractmethod @abc.abstractmethod
def get_audit_by_id(self, context, audit_id): def get_audit_by_id(self, context, audit_id, eager=False):
"""Return an audit. """Return an audit.
:param context: The security context :param context: The security context
:param audit_id: The id of an audit. :param audit_id: The id of an audit.
:param eager: If True, also loads One-to-X data (Default: False)
:returns: An audit. :returns: An audit.
:raises: :py:class:`~.AuditNotFound` :raises: :py:class:`~.AuditNotFound`
""" """
@abc.abstractmethod @abc.abstractmethod
def get_audit_by_uuid(self, context, audit_uuid): def get_audit_by_uuid(self, context, audit_uuid, eager=False):
"""Return an audit. """Return an audit.
:param context: The security context :param context: The security context
:param audit_uuid: The uuid of an audit. :param audit_uuid: The uuid of an audit.
:param eager: If True, also loads One-to-X data (Default: False)
:returns: An audit. :returns: An audit.
:raises: :py:class:`~.AuditNotFound` :raises: :py:class:`~.AuditNotFound`
""" """
@ -392,13 +425,13 @@ class BaseConnection(object):
"""Soft delete an audit and all associated action plans. """Soft delete an audit and all associated action plans.
:param audit_id: The id or uuid of an audit. :param audit_id: The id or uuid of an audit.
:returns: An audit.
:raises: :py:class:`~.AuditNotFound` :raises: :py:class:`~.AuditNotFound`
""" """
@abc.abstractmethod @abc.abstractmethod
def get_action_list(self, context, filters=None, limit=None, def get_action_list(self, context, filters=None, limit=None,
marker=None, sort_key=None, sort_dir=None): marker=None, sort_key=None, sort_dir=None,
eager=False):
"""Get specific columns for matching actions. """Get specific columns for matching actions.
Return a list of the specified columns for all actions that match the Return a list of the specified columns for all actions that match the
@ -412,6 +445,7 @@ class BaseConnection(object):
:param sort_key: Attribute by which results should be sorted. :param sort_key: Attribute by which results should be sorted.
:param sort_dir: direction in which results should be sorted. :param sort_dir: direction in which results should be sorted.
(asc, desc) (asc, desc)
:param eager: If True, also loads One-to-X data (Default: False)
:returns: A list of tuples of the specified columns. :returns: A list of tuples of the specified columns.
""" """
@ -436,21 +470,23 @@ class BaseConnection(object):
""" """
@abc.abstractmethod @abc.abstractmethod
def get_action_by_id(self, context, action_id): def get_action_by_id(self, context, action_id, eager=False):
"""Return a action. """Return a action.
:param context: The security context :param context: The security context
:param action_id: The id of a action. :param action_id: The id of a action.
:param eager: If True, also loads One-to-X data (Default: False)
:returns: A action. :returns: A action.
:raises: :py:class:`~.ActionNotFound` :raises: :py:class:`~.ActionNotFound`
""" """
@abc.abstractmethod @abc.abstractmethod
def get_action_by_uuid(self, context, action_uuid): def get_action_by_uuid(self, context, action_uuid, eager=False):
"""Return a action. """Return a action.
:param context: The security context :param context: The security context
:param action_uuid: The uuid of a action. :param action_uuid: The uuid of a action.
:param eager: If True, also loads One-to-X data (Default: False)
:returns: A action. :returns: A action.
:raises: :py:class:`~.ActionNotFound` :raises: :py:class:`~.ActionNotFound`
""" """
@ -475,10 +511,17 @@ class BaseConnection(object):
:raises: :py:class:`~.Invalid` :raises: :py:class:`~.Invalid`
""" """
def soft_delete_action(self, action_id):
"""Soft delete an action.
:param action_id: The id or uuid of an action.
:raises: :py:class:`~.ActionNotFound`
"""
@abc.abstractmethod @abc.abstractmethod
def get_action_plan_list( def get_action_plan_list(
self, context, filters=None, limit=None, self, context, filters=None, limit=None,
marker=None, sort_key=None, sort_dir=None): marker=None, sort_key=None, sort_dir=None, eager=False):
"""Get specific columns for matching action plans. """Get specific columns for matching action plans.
Return a list of the specified columns for all action plans that Return a list of the specified columns for all action plans that
@ -492,6 +535,7 @@ class BaseConnection(object):
:param sort_key: Attribute by which results should be sorted. :param sort_key: Attribute by which results should be sorted.
:param sort_dir: direction in which results should be sorted. :param sort_dir: direction in which results should be sorted.
(asc, desc) (asc, desc)
:param eager: If True, also loads One-to-X data (Default: False)
:returns: A list of tuples of the specified columns. :returns: A list of tuples of the specified columns.
""" """
@ -506,21 +550,23 @@ class BaseConnection(object):
""" """
@abc.abstractmethod @abc.abstractmethod
def get_action_plan_by_id(self, context, action_plan_id): def get_action_plan_by_id(self, context, action_plan_id, eager=False):
"""Return an action plan. """Return an action plan.
:param context: The security context :param context: The security context
:param action_plan_id: The id of an action plan. :param action_plan_id: The id of an action plan.
:param eager: If True, also loads One-to-X data (Default: False)
:returns: An action plan. :returns: An action plan.
:raises: :py:class:`~.ActionPlanNotFound` :raises: :py:class:`~.ActionPlanNotFound`
""" """
@abc.abstractmethod @abc.abstractmethod
def get_action_plan_by_uuid(self, context, action_plan__uuid): def get_action_plan_by_uuid(self, context, action_plan__uuid, eager=False):
"""Return a action plan. """Return a action plan.
:param context: The security context :param context: The security context
:param action_plan__uuid: The uuid of an action plan. :param action_plan__uuid: The uuid of an action plan.
:param eager: If True, also loads One-to-X data (Default: False)
:returns: An action plan. :returns: An action plan.
:raises: :py:class:`~.ActionPlanNotFound` :raises: :py:class:`~.ActionPlanNotFound`
""" """
@ -545,9 +591,17 @@ class BaseConnection(object):
:raises: :py:class:`~.Invalid` :raises: :py:class:`~.Invalid`
""" """
def soft_delete_action_plan(self, action_plan_id):
"""Soft delete an action plan.
:param action_plan_id: The id or uuid of an action plan.
:raises: :py:class:`~.ActionPlanNotFound`
"""
@abc.abstractmethod @abc.abstractmethod
def get_efficacy_indicator_list(self, context, filters=None, limit=None, def get_efficacy_indicator_list(self, context, filters=None, limit=None,
marker=None, sort_key=None, sort_dir=None): marker=None, sort_key=None, sort_dir=None,
eager=False):
"""Get specific columns for matching efficacy indicators. """Get specific columns for matching efficacy indicators.
Return a list of the specified columns for all efficacy indicators that Return a list of the specified columns for all efficacy indicators that
@ -564,6 +618,7 @@ class BaseConnection(object):
:param sort_key: Attribute by which results should be sorted. :param sort_key: Attribute by which results should be sorted.
:param sort_dir: Direction in which results should be sorted. :param sort_dir: Direction in which results should be sorted.
(asc, desc) (asc, desc)
:param eager: If True, also loads One-to-X data (Default: False)
:returns: A list of tuples of the specified columns. :returns: A list of tuples of the specified columns.
""" """
@ -588,31 +643,37 @@ class BaseConnection(object):
""" """
@abc.abstractmethod @abc.abstractmethod
def get_efficacy_indicator_by_id(self, context, efficacy_indicator_id): def get_efficacy_indicator_by_id(self, context, efficacy_indicator_id,
eager=False):
"""Return an efficacy indicator given its ID. """Return an efficacy indicator given its ID.
:param context: The security context :param context: The security context
:param efficacy_indicator_id: The ID of an efficacy indicator :param efficacy_indicator_id: The ID of an efficacy indicator
:param eager: If True, also loads One-to-X data (Default: False)
:returns: An efficacy indicator :returns: An efficacy indicator
:raises: :py:class:`~.EfficacyIndicatorNotFound` :raises: :py:class:`~.EfficacyIndicatorNotFound`
""" """
@abc.abstractmethod @abc.abstractmethod
def get_efficacy_indicator_by_uuid(self, context, efficacy_indicator_uuid): def get_efficacy_indicator_by_uuid(self, context, efficacy_indicator_uuid,
eager=False):
"""Return an efficacy indicator given its UUID. """Return an efficacy indicator given its UUID.
:param context: The security context :param context: The security context
:param efficacy_indicator_uuid: The UUID of an efficacy indicator :param efficacy_indicator_uuid: The UUID of an efficacy indicator
:param eager: If True, also loads One-to-X data (Default: False)
:returns: An efficacy indicator :returns: An efficacy indicator
:raises: :py:class:`~.EfficacyIndicatorNotFound` :raises: :py:class:`~.EfficacyIndicatorNotFound`
""" """
@abc.abstractmethod @abc.abstractmethod
def get_efficacy_indicator_by_name(self, context, efficacy_indicator_name): def get_efficacy_indicator_by_name(self, context, efficacy_indicator_name,
eager=False):
"""Return an efficacy indicator given its name. """Return an efficacy indicator given its name.
:param context: The security context :param context: The security context
:param efficacy_indicator_name: The name of an efficacy indicator :param efficacy_indicator_name: The name of an efficacy indicator
:param eager: If True, also loads One-to-X data (Default: False)
:returns: An efficacy indicator :returns: An efficacy indicator
:raises: :py:class:`~.EfficacyIndicatorNotFound` :raises: :py:class:`~.EfficacyIndicatorNotFound`
""" """
@ -626,7 +687,7 @@ class BaseConnection(object):
""" """
@abc.abstractmethod @abc.abstractmethod
def update_efficacy_indicator(self, efficacy_indicator_uuid, values): def update_efficacy_indicator(self, efficacy_indicator_id, values):
"""Update properties of an efficacy indicator. """Update properties of an efficacy indicator.
:param efficacy_indicator_uuid: The UUID of an efficacy indicator :param efficacy_indicator_uuid: The UUID of an efficacy indicator
@ -638,7 +699,7 @@ class BaseConnection(object):
@abc.abstractmethod @abc.abstractmethod
def get_scoring_engine_list( def get_scoring_engine_list(
self, context, columns=None, filters=None, limit=None, self, context, columns=None, filters=None, limit=None,
marker=None, sort_key=None, sort_dir=None): marker=None, sort_key=None, sort_dir=None, eager=False):
"""Get specific columns for matching scoring engines. """Get specific columns for matching scoring engines.
Return a list of the specified columns for all scoring engines that Return a list of the specified columns for all scoring engines that
@ -654,6 +715,7 @@ class BaseConnection(object):
:param sort_key: Attribute by which results should be sorted. :param sort_key: Attribute by which results should be sorted.
:param sort_dir: direction in which results should be sorted. :param sort_dir: direction in which results should be sorted.
(asc, desc) (asc, desc)
:param eager: If True, also loads One-to-X data (Default: False)
:returns: A list of tuples of the specified columns. :returns: A list of tuples of the specified columns.
""" """
@ -668,31 +730,37 @@ class BaseConnection(object):
""" """
@abc.abstractmethod @abc.abstractmethod
def get_scoring_engine_by_id(self, context, scoring_engine_id): def get_scoring_engine_by_id(self, context, scoring_engine_id,
eager=False):
"""Return a scoring engine by its id. """Return a scoring engine by its id.
:param context: The security context :param context: The security context
:param scoring_engine_id: The id of a scoring engine. :param scoring_engine_id: The id of a scoring engine.
:param eager: If True, also loads One-to-X data (Default: False)
:returns: A scoring engine. :returns: A scoring engine.
:raises: :py:class:`~.ScoringEngineNotFound` :raises: :py:class:`~.ScoringEngineNotFound`
""" """
@abc.abstractmethod @abc.abstractmethod
def get_scoring_engine_by_uuid(self, context, scoring_engine_uuid): def get_scoring_engine_by_uuid(self, context, scoring_engine_uuid,
eager=False):
"""Return a scoring engine by its uuid. """Return a scoring engine by its uuid.
:param context: The security context :param context: The security context
:param scoring_engine_uuid: The uuid of a scoring engine. :param scoring_engine_uuid: The uuid of a scoring engine.
:param eager: If True, also loads One-to-X data (Default: False)
:returns: A scoring engine. :returns: A scoring engine.
:raises: :py:class:`~.ScoringEngineNotFound` :raises: :py:class:`~.ScoringEngineNotFound`
""" """
@abc.abstractmethod @abc.abstractmethod
def get_scoring_engine_by_name(self, context, scoring_engine_name): def get_scoring_engine_by_name(self, context, scoring_engine_name,
eager=False):
"""Return a scoring engine by its name. """Return a scoring engine by its name.
:param context: The security context :param context: The security context
:param scoring_engine_name: The name of a scoring engine. :param scoring_engine_name: The name of a scoring engine.
:param eager: If True, also loads One-to-X data (Default: False)
:returns: A scoring engine. :returns: A scoring engine.
:raises: :py:class:`~.ScoringEngineNotFound` :raises: :py:class:`~.ScoringEngineNotFound`
""" """
@ -716,8 +784,8 @@ class BaseConnection(object):
""" """
@abc.abstractmethod @abc.abstractmethod
def get_service_list(self, context, filters=None, limit=None, def get_service_list(self, context, filters=None, limit=None, marker=None,
marker=None, sort_key=None, sort_dir=None): sort_key=None, sort_dir=None, eager=False):
"""Get specific columns for matching services. """Get specific columns for matching services.
Return a list of the specified columns for all services that Return a list of the specified columns for all services that
@ -732,6 +800,7 @@ class BaseConnection(object):
:param sort_key: Attribute by which results should be sorted. :param sort_key: Attribute by which results should be sorted.
:param sort_dir: Direction in which results should be sorted. :param sort_dir: Direction in which results should be sorted.
(asc, desc) (asc, desc)
:param eager: If True, also loads One-to-X data (Default: False)
:returns: A list of tuples of the specified columns. :returns: A list of tuples of the specified columns.
""" """
@ -755,21 +824,23 @@ class BaseConnection(object):
""" """
@abc.abstractmethod @abc.abstractmethod
def get_service_by_id(self, context, service_id): def get_service_by_id(self, context, service_id, eager=False):
"""Return a service given its ID. """Return a service given its ID.
:param context: The security context :param context: The security context
:param service_id: The ID of a service :param service_id: The ID of a service
:param eager: If True, also loads One-to-X data (Default: False)
:returns: A service :returns: A service
:raises: :py:class:`~.ServiceNotFound` :raises: :py:class:`~.ServiceNotFound`
""" """
@abc.abstractmethod @abc.abstractmethod
def get_service_by_name(self, context, service_name): def get_service_by_name(self, context, service_name, eager=False):
"""Return a service given its name. """Return a service given its name.
:param context: The security context :param context: The security context
:param service_name: The name of a service :param service_name: The name of a service
:param eager: If True, also loads One-to-X data (Default: False)
:returns: A service :returns: A service
:raises: :py:class:`~.ServiceNotFound` :raises: :py:class:`~.ServiceNotFound`
""" """

View File

@ -24,7 +24,10 @@ from oslo_config import cfg
from oslo_db import exception as db_exc from oslo_db import exception as db_exc
from oslo_db.sqlalchemy import session as db_session from oslo_db.sqlalchemy import session as db_session
from oslo_db.sqlalchemy import utils as db_utils from oslo_db.sqlalchemy import utils as db_utils
from oslo_utils import timeutils
from sqlalchemy.inspection import inspect
from sqlalchemy.orm import exc from sqlalchemy.orm import exc
from sqlalchemy.orm import joinedload
from watcher._i18n import _ from watcher._i18n import _
from watcher.common import exception from watcher.common import exception
@ -34,7 +37,6 @@ from watcher.db.sqlalchemy import models
from watcher.objects import action as action_objects from watcher.objects import action as action_objects
from watcher.objects import action_plan as ap_objects from watcher.objects import action_plan as ap_objects
from watcher.objects import audit as audit_objects from watcher.objects import audit as audit_objects
from watcher.objects import utils as objutils
CONF = cfg.CONF CONF = cfg.CONF
@ -133,8 +135,9 @@ class Connection(api.BaseConnection):
def __add_simple_filter(self, query, model, fieldname, value, operator_): def __add_simple_filter(self, query, model, fieldname, value, operator_):
field = getattr(model, fieldname) field = getattr(model, fieldname)
if field.type.python_type is datetime.datetime: if field.type.python_type is datetime.datetime and value:
value = objutils.datetime_or_str_or_none(value) if not isinstance(value, datetime.datetime):
value = timeutils.parse_isotime(value)
return query.filter(self.valid_operators[operator_](field, value)) return query.filter(self.valid_operators[operator_](field, value))
@ -233,8 +236,20 @@ class Connection(api.BaseConnection):
return query return query
def _get(self, context, model, fieldname, value): @staticmethod
def _set_eager_options(model, query):
relationships = inspect(model).relationships
for relationship in relationships:
if not relationship.uselist:
# We have a One-to-X relationship
query = query.options(joinedload(relationship.key))
return query
def _get(self, context, model, fieldname, value, eager):
query = model_query(model) query = model_query(model)
if eager:
query = self._set_eager_options(model, query)
query = query.filter(getattr(model, fieldname) == value) query = query.filter(getattr(model, fieldname) == value)
if not context.show_deleted: if not context.show_deleted:
query = query.filter(model.deleted_at.is_(None)) query = query.filter(model.deleted_at.is_(None))
@ -246,7 +261,8 @@ class Connection(api.BaseConnection):
return obj return obj
def _update(self, model, id_, values): @staticmethod
def _update(model, id_, values):
session = get_session() session = get_session()
with session.begin(): with session.begin():
query = model_query(model, session=session) query = model_query(model, session=session)
@ -259,7 +275,8 @@ class Connection(api.BaseConnection):
ref.update(values) ref.update(values)
return ref return ref
def _soft_delete(self, model, id_): @staticmethod
def _soft_delete(model, id_):
session = get_session() session = get_session()
with session.begin(): with session.begin():
query = model_query(model, session=session) query = model_query(model, session=session)
@ -271,7 +288,8 @@ class Connection(api.BaseConnection):
query.soft_delete() query.soft_delete()
def _destroy(self, model, id_): @staticmethod
def _destroy(model, id_):
session = get_session() session = get_session()
with session.begin(): with session.begin():
query = model_query(model, session=session) query = model_query(model, session=session)
@ -398,10 +416,11 @@ class Connection(api.BaseConnection):
# ### GOALS ### # # ### GOALS ### #
def get_goal_list(self, context, filters=None, limit=None, def get_goal_list(self, context, filters=None, limit=None, marker=None,
marker=None, sort_key=None, sort_dir=None): sort_key=None, sort_dir=None, eager=False):
query = model_query(models.Goal) query = model_query(models.Goal)
if eager:
query = self._set_eager_options(models.Goal, query)
query = self._add_goals_filters(query, filters) query = self._add_goals_filters(query, filters)
if not context.show_deleted: if not context.show_deleted:
query = query.filter_by(deleted_at=None) query = query.filter_by(deleted_at=None)
@ -422,21 +441,24 @@ class Connection(api.BaseConnection):
raise exception.GoalAlreadyExists(uuid=values['uuid']) raise exception.GoalAlreadyExists(uuid=values['uuid'])
return goal return goal
def _get_goal(self, context, fieldname, value): def _get_goal(self, context, fieldname, value, eager):
try: try:
return self._get(context, model=models.Goal, return self._get(context, model=models.Goal,
fieldname=fieldname, value=value) fieldname=fieldname, value=value, eager=eager)
except exception.ResourceNotFound: except exception.ResourceNotFound:
raise exception.GoalNotFound(goal=value) raise exception.GoalNotFound(goal=value)
def get_goal_by_id(self, context, goal_id): def get_goal_by_id(self, context, goal_id, eager=False):
return self._get_goal(context, fieldname="id", value=goal_id) return self._get_goal(
context, fieldname="id", value=goal_id, eager=eager)
def get_goal_by_uuid(self, context, goal_uuid): def get_goal_by_uuid(self, context, goal_uuid, eager=False):
return self._get_goal(context, fieldname="uuid", value=goal_uuid) return self._get_goal(
context, fieldname="uuid", value=goal_uuid, eager=eager)
def get_goal_by_name(self, context, goal_name): def get_goal_by_name(self, context, goal_name, eager=False):
return self._get_goal(context, fieldname="name", value=goal_name) return self._get_goal(
context, fieldname="name", value=goal_name, eager=eager)
def destroy_goal(self, goal_id): def destroy_goal(self, goal_id):
try: try:
@ -463,9 +485,11 @@ class Connection(api.BaseConnection):
# ### STRATEGIES ### # # ### STRATEGIES ### #
def get_strategy_list(self, context, filters=None, limit=None, def get_strategy_list(self, context, filters=None, limit=None,
marker=None, sort_key=None, sort_dir=None): marker=None, sort_key=None, sort_dir=None,
eager=True):
query = model_query(models.Strategy) query = model_query(models.Strategy)
if eager:
query = self._set_eager_options(models.Strategy, query)
query = self._add_strategies_filters(query, filters) query = self._add_strategies_filters(query, filters)
if not context.show_deleted: if not context.show_deleted:
query = query.filter_by(deleted_at=None) query = query.filter_by(deleted_at=None)
@ -486,23 +510,24 @@ class Connection(api.BaseConnection):
raise exception.StrategyAlreadyExists(uuid=values['uuid']) raise exception.StrategyAlreadyExists(uuid=values['uuid'])
return strategy return strategy
def _get_strategy(self, context, fieldname, value): def _get_strategy(self, context, fieldname, value, eager):
try: try:
return self._get(context, model=models.Strategy, return self._get(context, model=models.Strategy,
fieldname=fieldname, value=value) fieldname=fieldname, value=value, eager=eager)
except exception.ResourceNotFound: except exception.ResourceNotFound:
raise exception.StrategyNotFound(strategy=value) raise exception.StrategyNotFound(strategy=value)
def get_strategy_by_id(self, context, strategy_id): def get_strategy_by_id(self, context, strategy_id, eager=False):
return self._get_strategy(context, fieldname="id", value=strategy_id)
def get_strategy_by_uuid(self, context, strategy_uuid):
return self._get_strategy( return self._get_strategy(
context, fieldname="uuid", value=strategy_uuid) context, fieldname="id", value=strategy_id, eager=eager)
def get_strategy_by_name(self, context, strategy_name): def get_strategy_by_uuid(self, context, strategy_uuid, eager=False):
return self._get_strategy( return self._get_strategy(
context, fieldname="name", value=strategy_name) context, fieldname="uuid", value=strategy_uuid, eager=eager)
def get_strategy_by_name(self, context, strategy_name, eager=False):
return self._get_strategy(
context, fieldname="name", value=strategy_name, eager=eager)
def destroy_strategy(self, strategy_id): def destroy_strategy(self, strategy_id):
try: try:
@ -529,9 +554,12 @@ class Connection(api.BaseConnection):
# ### AUDIT TEMPLATES ### # # ### AUDIT TEMPLATES ### #
def get_audit_template_list(self, context, filters=None, limit=None, def get_audit_template_list(self, context, filters=None, limit=None,
marker=None, sort_key=None, sort_dir=None): marker=None, sort_key=None, sort_dir=None,
eager=False):
query = model_query(models.AuditTemplate) query = model_query(models.AuditTemplate)
if eager:
query = self._set_eager_options(models.AuditTemplate, query)
query = self._add_audit_templates_filters(query, filters) query = self._add_audit_templates_filters(query, filters)
if not context.show_deleted: if not context.show_deleted:
query = query.filter_by(deleted_at=None) query = query.filter_by(deleted_at=None)
@ -561,24 +589,27 @@ class Connection(api.BaseConnection):
audit_template=values['name']) audit_template=values['name'])
return audit_template return audit_template
def _get_audit_template(self, context, fieldname, value): def _get_audit_template(self, context, fieldname, value, eager):
try: try:
return self._get(context, model=models.AuditTemplate, return self._get(context, model=models.AuditTemplate,
fieldname=fieldname, value=value) fieldname=fieldname, value=value, eager=eager)
except exception.ResourceNotFound: except exception.ResourceNotFound:
raise exception.AuditTemplateNotFound(audit_template=value) raise exception.AuditTemplateNotFound(audit_template=value)
def get_audit_template_by_id(self, context, audit_template_id): def get_audit_template_by_id(self, context, audit_template_id,
eager=False):
return self._get_audit_template( return self._get_audit_template(
context, fieldname="id", value=audit_template_id) context, fieldname="id", value=audit_template_id, eager=eager)
def get_audit_template_by_uuid(self, context, audit_template_uuid): def get_audit_template_by_uuid(self, context, audit_template_uuid,
eager=False):
return self._get_audit_template( return self._get_audit_template(
context, fieldname="uuid", value=audit_template_uuid) context, fieldname="uuid", value=audit_template_uuid, eager=eager)
def get_audit_template_by_name(self, context, audit_template_name): def get_audit_template_by_name(self, context, audit_template_name,
eager=False):
return self._get_audit_template( return self._get_audit_template(
context, fieldname="name", value=audit_template_name) context, fieldname="name", value=audit_template_name, eager=eager)
def destroy_audit_template(self, audit_template_id): def destroy_audit_template(self, audit_template_id):
try: try:
@ -609,8 +640,10 @@ class Connection(api.BaseConnection):
# ### AUDITS ### # # ### AUDITS ### #
def get_audit_list(self, context, filters=None, limit=None, marker=None, def get_audit_list(self, context, filters=None, limit=None, marker=None,
sort_key=None, sort_dir=None): sort_key=None, sort_dir=None, eager=False):
query = model_query(models.Audit) query = model_query(models.Audit)
if eager:
query = self._set_eager_options(models.Audit, query)
query = self._add_audits_filters(query, filters) query = self._add_audits_filters(query, filters)
if not context.show_deleted: if not context.show_deleted:
query = query.filter( query = query.filter(
@ -636,30 +669,20 @@ class Connection(api.BaseConnection):
raise exception.AuditAlreadyExists(uuid=values['uuid']) raise exception.AuditAlreadyExists(uuid=values['uuid'])
return audit return audit
def get_audit_by_id(self, context, audit_id): def _get_audit(self, context, fieldname, value, eager):
query = model_query(models.Audit)
query = query.filter_by(id=audit_id)
try: try:
audit = query.one() return self._get(context, model=models.Audit,
if not context.show_deleted: fieldname=fieldname, value=value, eager=eager)
if audit.state == audit_objects.State.DELETED: except exception.ResourceNotFound:
raise exception.AuditNotFound(audit=audit_id) raise exception.AuditNotFound(audit=value)
return audit
except exc.NoResultFound:
raise exception.AuditNotFound(audit=audit_id)
def get_audit_by_uuid(self, context, audit_uuid): def get_audit_by_id(self, context, audit_id, eager=False):
query = model_query(models.Audit) return self._get_audit(
query = query.filter_by(uuid=audit_uuid) context, fieldname="id", value=audit_id, eager=eager)
try: def get_audit_by_uuid(self, context, audit_uuid, eager=False):
audit = query.one() return self._get_audit(
if not context.show_deleted: context, fieldname="uuid", value=audit_uuid, eager=eager)
if audit.state == audit_objects.State.DELETED:
raise exception.AuditNotFound(audit=audit_uuid)
return audit
except exc.NoResultFound:
raise exception.AuditNotFound(audit=audit_uuid)
def destroy_audit(self, audit_id): def destroy_audit(self, audit_id):
def is_audit_referenced(session, audit_id): def is_audit_referenced(session, audit_id):
@ -704,8 +727,10 @@ class Connection(api.BaseConnection):
# ### ACTIONS ### # # ### ACTIONS ### #
def get_action_list(self, context, filters=None, limit=None, marker=None, def get_action_list(self, context, filters=None, limit=None, marker=None,
sort_key=None, sort_dir=None): sort_key=None, sort_dir=None, eager=False):
query = model_query(models.Action) query = model_query(models.Action)
if eager:
query = self._set_eager_options(models.Action, query)
query = self._add_actions_filters(query, filters) query = self._add_actions_filters(query, filters)
if not context.show_deleted: if not context.show_deleted:
query = query.filter( query = query.filter(
@ -726,31 +751,20 @@ class Connection(api.BaseConnection):
raise exception.ActionAlreadyExists(uuid=values['uuid']) raise exception.ActionAlreadyExists(uuid=values['uuid'])
return action return action
def get_action_by_id(self, context, action_id): def _get_action(self, context, fieldname, value, eager):
query = model_query(models.Action)
query = query.filter_by(id=action_id)
try: try:
action = query.one() return self._get(context, model=models.Action,
if not context.show_deleted: fieldname=fieldname, value=value, eager=eager)
if action.state == action_objects.State.DELETED: except exception.ResourceNotFound:
raise exception.ActionNotFound( raise exception.ActionNotFound(action=value)
action=action_id)
return action
except exc.NoResultFound:
raise exception.ActionNotFound(action=action_id)
def get_action_by_uuid(self, context, action_uuid): def get_action_by_id(self, context, action_id, eager=False):
query = model_query(models.Action) return self._get_action(
query = query.filter_by(uuid=action_uuid) context, fieldname="id", value=action_id, eager=eager)
try:
action = query.one() def get_action_by_uuid(self, context, action_uuid, eager=False):
if not context.show_deleted: return self._get_action(
if action.state == action_objects.State.DELETED: context, fieldname="uuid", value=action_uuid, eager=eager)
raise exception.ActionNotFound(
action=action_uuid)
return action
except exc.NoResultFound:
raise exception.ActionNotFound(action=action_uuid)
def destroy_action(self, action_id): def destroy_action(self, action_id):
session = get_session() session = get_session()
@ -765,12 +779,12 @@ class Connection(api.BaseConnection):
# NOTE(dtantsur): this can lead to very strange errors # NOTE(dtantsur): this can lead to very strange errors
if 'uuid' in values: if 'uuid' in values:
raise exception.Invalid( raise exception.Invalid(
message=_("Cannot overwrite UUID for an existing " message=_("Cannot overwrite UUID for an existing Action."))
"Action."))
return self._do_update_action(action_id, values) return self._do_update_action(action_id, values)
def _do_update_action(self, action_id, values): @staticmethod
def _do_update_action(action_id, values):
session = get_session() session = get_session()
with session.begin(): with session.begin():
query = model_query(models.Action, session=session) query = model_query(models.Action, session=session)
@ -799,9 +813,11 @@ class Connection(api.BaseConnection):
# ### ACTION PLANS ### # # ### ACTION PLANS ### #
def get_action_plan_list( def get_action_plan_list(
self, context, filters=None, limit=None, self, context, filters=None, limit=None, marker=None,
marker=None, sort_key=None, sort_dir=None): sort_key=None, sort_dir=None, eager=False):
query = model_query(models.ActionPlan) query = model_query(models.ActionPlan)
if eager:
query = self._set_eager_options(models.ActionPlan, query)
query = self._add_action_plans_filters(query, filters) query = self._add_action_plans_filters(query, filters)
if not context.show_deleted: if not context.show_deleted:
query = query.filter( query = query.filter(
@ -824,32 +840,20 @@ class Connection(api.BaseConnection):
raise exception.ActionPlanAlreadyExists(uuid=values['uuid']) raise exception.ActionPlanAlreadyExists(uuid=values['uuid'])
return action_plan return action_plan
def get_action_plan_by_id(self, context, action_plan_id): def _get_action_plan(self, context, fieldname, value, eager):
query = model_query(models.ActionPlan)
query = query.filter_by(id=action_plan_id)
try: try:
action_plan = query.one() return self._get(context, model=models.ActionPlan,
if not context.show_deleted: fieldname=fieldname, value=value, eager=eager)
if action_plan.state == ap_objects.State.DELETED: except exception.ResourceNotFound:
raise exception.ActionPlanNotFound( raise exception.ActionPlanNotFound(action_plan=value)
action_plan=action_plan_id)
return action_plan
except exc.NoResultFound:
raise exception.ActionPlanNotFound(action_plan=action_plan_id)
def get_action_plan_by_uuid(self, context, action_plan__uuid): def get_action_plan_by_id(self, context, action_plan_id, eager=False):
query = model_query(models.ActionPlan) return self._get_action_plan(
query = query.filter_by(uuid=action_plan__uuid) context, fieldname="id", value=action_plan_id, eager=eager)
try: def get_action_plan_by_uuid(self, context, action_plan_uuid, eager=False):
action_plan = query.one() return self._get_action_plan(
if not context.show_deleted: context, fieldname="uuid", value=action_plan_uuid, eager=eager)
if action_plan.state == ap_objects.State.DELETED:
raise exception.ActionPlanNotFound(
action_plan=action_plan__uuid)
return action_plan
except exc.NoResultFound:
raise exception.ActionPlanNotFound(action_plan=action_plan__uuid)
def destroy_action_plan(self, action_plan_id): def destroy_action_plan(self, action_plan_id):
def is_action_plan_referenced(session, action_plan_id): def is_action_plan_referenced(session, action_plan_id):
@ -883,7 +887,8 @@ class Connection(api.BaseConnection):
return self._do_update_action_plan(action_plan_id, values) return self._do_update_action_plan(action_plan_id, values)
def _do_update_action_plan(self, action_plan_id, values): @staticmethod
def _do_update_action_plan(action_plan_id, values):
session = get_session() session = get_session()
with session.begin(): with session.begin():
query = model_query(models.ActionPlan, session=session) query = model_query(models.ActionPlan, session=session)
@ -912,9 +917,12 @@ class Connection(api.BaseConnection):
# ### EFFICACY INDICATORS ### # # ### EFFICACY INDICATORS ### #
def get_efficacy_indicator_list(self, context, filters=None, limit=None, def get_efficacy_indicator_list(self, context, filters=None, limit=None,
marker=None, sort_key=None, sort_dir=None): marker=None, sort_key=None, sort_dir=None,
eager=False):
query = model_query(models.EfficacyIndicator) query = model_query(models.EfficacyIndicator)
if eager:
query = self._set_eager_options(models.EfficacyIndicator, query)
query = self._add_efficacy_indicators_filters(query, filters) query = self._add_efficacy_indicators_filters(query, filters)
if not context.show_deleted: if not context.show_deleted:
query = query.filter_by(deleted_at=None) query = query.filter_by(deleted_at=None)
@ -935,24 +943,30 @@ class Connection(api.BaseConnection):
raise exception.EfficacyIndicatorAlreadyExists(uuid=values['uuid']) raise exception.EfficacyIndicatorAlreadyExists(uuid=values['uuid'])
return efficacy_indicator return efficacy_indicator
def _get_efficacy_indicator(self, context, fieldname, value): def _get_efficacy_indicator(self, context, fieldname, value, eager):
try: try:
return self._get(context, model=models.EfficacyIndicator, return self._get(context, model=models.EfficacyIndicator,
fieldname=fieldname, value=value) fieldname=fieldname, value=value, eager=eager)
except exception.ResourceNotFound: except exception.ResourceNotFound:
raise exception.EfficacyIndicatorNotFound(efficacy_indicator=value) raise exception.EfficacyIndicatorNotFound(efficacy_indicator=value)
def get_efficacy_indicator_by_id(self, context, efficacy_indicator_id): def get_efficacy_indicator_by_id(self, context, efficacy_indicator_id,
eager=False):
return self._get_efficacy_indicator( return self._get_efficacy_indicator(
context, fieldname="id", value=efficacy_indicator_id) context, fieldname="id",
value=efficacy_indicator_id, eager=eager)
def get_efficacy_indicator_by_uuid(self, context, efficacy_indicator_uuid): def get_efficacy_indicator_by_uuid(self, context, efficacy_indicator_uuid,
eager=False):
return self._get_efficacy_indicator( return self._get_efficacy_indicator(
context, fieldname="uuid", value=efficacy_indicator_uuid) context, fieldname="uuid",
value=efficacy_indicator_uuid, eager=eager)
def get_efficacy_indicator_by_name(self, context, efficacy_indicator_name): def get_efficacy_indicator_by_name(self, context, efficacy_indicator_name,
eager=False):
return self._get_efficacy_indicator( return self._get_efficacy_indicator(
context, fieldname="name", value=efficacy_indicator_name) context, fieldname="name",
value=efficacy_indicator_name, eager=eager)
def update_efficacy_indicator(self, efficacy_indicator_id, values): def update_efficacy_indicator(self, efficacy_indicator_id, values):
if 'uuid' in values: if 'uuid' in values:
@ -996,8 +1010,10 @@ class Connection(api.BaseConnection):
def get_scoring_engine_list( def get_scoring_engine_list(
self, context, columns=None, filters=None, limit=None, self, context, columns=None, filters=None, limit=None,
marker=None, sort_key=None, sort_dir=None): marker=None, sort_key=None, sort_dir=None, eager=False):
query = model_query(models.ScoringEngine) query = model_query(models.ScoringEngine)
if eager:
query = self._set_eager_options(models.ScoringEngine, query)
query = self._add_scoring_engine_filters(query, filters) query = self._add_scoring_engine_filters(query, filters)
if not context.show_deleted: if not context.show_deleted:
query = query.filter_by(deleted_at=None) query = query.filter_by(deleted_at=None)
@ -1019,24 +1035,27 @@ class Connection(api.BaseConnection):
raise exception.ScoringEngineAlreadyExists(uuid=values['uuid']) raise exception.ScoringEngineAlreadyExists(uuid=values['uuid'])
return scoring_engine return scoring_engine
def _get_scoring_engine(self, context, fieldname, value): def _get_scoring_engine(self, context, fieldname, value, eager):
try: try:
return self._get(context, model=models.ScoringEngine, return self._get(context, model=models.ScoringEngine,
fieldname=fieldname, value=value) fieldname=fieldname, value=value, eager=eager)
except exception.ResourceNotFound: except exception.ResourceNotFound:
raise exception.ScoringEngineNotFound(scoring_engine=value) raise exception.ScoringEngineNotFound(scoring_engine=value)
def get_scoring_engine_by_id(self, context, scoring_engine_id): def get_scoring_engine_by_id(self, context, scoring_engine_id,
eager=False):
return self._get_scoring_engine( return self._get_scoring_engine(
context, fieldname="id", value=scoring_engine_id) context, fieldname="id", value=scoring_engine_id, eager=eager)
def get_scoring_engine_by_uuid(self, context, scoring_engine_uuid): def get_scoring_engine_by_uuid(self, context, scoring_engine_uuid,
eager=False):
return self._get_scoring_engine( return self._get_scoring_engine(
context, fieldname="uuid", value=scoring_engine_uuid) context, fieldname="uuid", value=scoring_engine_uuid, eager=eager)
def get_scoring_engine_by_name(self, context, scoring_engine_name): def get_scoring_engine_by_name(self, context, scoring_engine_name,
eager=False):
return self._get_scoring_engine( return self._get_scoring_engine(
context, fieldname="name", value=scoring_engine_name) context, fieldname="name", value=scoring_engine_name, eager=eager)
def destroy_scoring_engine(self, scoring_engine_id): def destroy_scoring_engine(self, scoring_engine_id):
try: try:
@ -1046,9 +1065,9 @@ class Connection(api.BaseConnection):
scoring_engine=scoring_engine_id) scoring_engine=scoring_engine_id)
def update_scoring_engine(self, scoring_engine_id, values): def update_scoring_engine(self, scoring_engine_id, values):
if 'id' in values: if 'uuid' in values:
raise exception.Invalid( raise exception.Invalid(
message=_("Cannot overwrite ID for an existing " message=_("Cannot overwrite UUID for an existing "
"Scoring Engine.")) "Scoring Engine."))
try: try:
@ -1077,9 +1096,11 @@ class Connection(api.BaseConnection):
query=query, model=models.Service, filters=filters, query=query, model=models.Service, filters=filters,
plain_fields=plain_fields) plain_fields=plain_fields)
def get_service_list(self, context, filters=None, limit=None, def get_service_list(self, context, filters=None, limit=None, marker=None,
marker=None, sort_key=None, sort_dir=None): sort_key=None, sort_dir=None, eager=False):
query = model_query(models.Service) query = model_query(models.Service)
if eager:
query = self._set_eager_options(models.Service, query)
query = self._add_services_filters(query, filters) query = self._add_services_filters(query, filters)
if not context.show_deleted: if not context.show_deleted:
query = query.filter_by(deleted_at=None) query = query.filter_by(deleted_at=None)
@ -1096,18 +1117,20 @@ class Connection(api.BaseConnection):
host=values['host']) host=values['host'])
return service return service
def _get_service(self, context, fieldname, value): def _get_service(self, context, fieldname, value, eager):
try: try:
return self._get(context, model=models.Service, return self._get(context, model=models.Service,
fieldname=fieldname, value=value) fieldname=fieldname, value=value, eager=eager)
except exception.ResourceNotFound: except exception.ResourceNotFound:
raise exception.ServiceNotFound(service=value) raise exception.ServiceNotFound(service=value)
def get_service_by_id(self, context, service_id): def get_service_by_id(self, context, service_id, eager=False):
return self._get_service(context, fieldname="id", value=service_id) return self._get_service(
context, fieldname="id", value=service_id, eager=eager)
def get_service_by_name(self, context, service_name): def get_service_by_name(self, context, service_name, eager=False):
return self._get_service(context, fieldname="name", value=service_name) return self._get_service(
context, fieldname="name", value=service_name, eager=eager)
def destroy_service(self, service_id): def destroy_service(self, service_id):
try: try:

View File

@ -27,6 +27,7 @@ from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import ForeignKey from sqlalchemy import ForeignKey
from sqlalchemy import Integer from sqlalchemy import Integer
from sqlalchemy import Numeric from sqlalchemy import Numeric
from sqlalchemy import orm
from sqlalchemy import String from sqlalchemy import String
from sqlalchemy import Text from sqlalchemy import Text
from sqlalchemy.types import TypeDecorator, TEXT from sqlalchemy.types import TypeDecorator, TEXT
@ -57,6 +58,7 @@ def table_args():
class JsonEncodedType(TypeDecorator): class JsonEncodedType(TypeDecorator):
"""Abstract base type serialized as json-encoded string in db.""" """Abstract base type serialized as json-encoded string in db."""
type = None type = None
impl = TEXT impl = TEXT
@ -81,11 +83,13 @@ class JsonEncodedType(TypeDecorator):
class JSONEncodedDict(JsonEncodedType): class JSONEncodedDict(JsonEncodedType):
"""Represents dict serialized as json-encoded string in db.""" """Represents dict serialized as json-encoded string in db."""
type = dict type = dict
class JSONEncodedList(JsonEncodedType): class JSONEncodedList(JsonEncodedType):
"""Represents list serialized as json-encoded string in db.""" """Represents list serialized as json-encoded string in db."""
type = list type = list
@ -111,23 +115,6 @@ class WatcherBase(models.SoftDeleteMixin,
Base = declarative_base(cls=WatcherBase) Base = declarative_base(cls=WatcherBase)
class Strategy(Base):
"""Represents a strategy."""
__tablename__ = 'strategies'
__table_args__ = (
UniqueConstraint('uuid', name='uniq_strategies0uuid'),
UniqueConstraint('name', 'deleted', name='uniq_strategies0name'),
table_args()
)
id = Column(Integer, primary_key=True)
uuid = Column(String(36))
name = Column(String(63), nullable=False)
display_name = Column(String(63), nullable=False)
goal_id = Column(Integer, ForeignKey('goals.id'), nullable=False)
parameters_spec = Column(JSONEncodedDict, nullable=True)
class Goal(Base): class Goal(Base):
"""Represents a goal.""" """Represents a goal."""
@ -137,13 +124,32 @@ class Goal(Base):
UniqueConstraint('name', 'deleted', name='uniq_goals0name'), UniqueConstraint('name', 'deleted', name='uniq_goals0name'),
table_args(), table_args(),
) )
id = Column(Integer, primary_key=True) id = Column(Integer, primary_key=True, autoincrement=True)
uuid = Column(String(36)) uuid = Column(String(36))
name = Column(String(63), nullable=False) name = Column(String(63), nullable=False)
display_name = Column(String(63), nullable=False) display_name = Column(String(63), nullable=False)
efficacy_specification = Column(JSONEncodedList, nullable=False) efficacy_specification = Column(JSONEncodedList, nullable=False)
class Strategy(Base):
"""Represents a strategy."""
__tablename__ = 'strategies'
__table_args__ = (
UniqueConstraint('uuid', name='uniq_strategies0uuid'),
UniqueConstraint('name', 'deleted', name='uniq_strategies0name'),
table_args()
)
id = Column(Integer, primary_key=True, autoincrement=True)
uuid = Column(String(36))
name = Column(String(63), nullable=False)
display_name = Column(String(63), nullable=False)
goal_id = Column(Integer, ForeignKey('goals.id'), nullable=False)
parameters_spec = Column(JSONEncodedDict, nullable=True)
goal = orm.relationship(Goal, foreign_keys=goal_id, lazy=None)
class AuditTemplate(Base): class AuditTemplate(Base):
"""Represents an audit template.""" """Represents an audit template."""
@ -163,6 +169,9 @@ class AuditTemplate(Base):
version = Column(String(15), nullable=True) version = Column(String(15), nullable=True)
scope = Column(JSONEncodedList) scope = Column(JSONEncodedList)
goal = orm.relationship(Goal, foreign_keys=goal_id, lazy=None)
strategy = orm.relationship(Strategy, foreign_keys=strategy_id, lazy=None)
class Audit(Base): class Audit(Base):
"""Represents an audit.""" """Represents an audit."""
@ -172,7 +181,7 @@ class Audit(Base):
UniqueConstraint('uuid', name='uniq_audits0uuid'), UniqueConstraint('uuid', name='uniq_audits0uuid'),
table_args() table_args()
) )
id = Column(Integer, primary_key=True) id = Column(Integer, primary_key=True, autoincrement=True)
uuid = Column(String(36)) uuid = Column(String(36))
audit_type = Column(String(20)) audit_type = Column(String(20))
state = Column(String(20), nullable=True) state = Column(String(20), nullable=True)
@ -183,24 +192,8 @@ class Audit(Base):
strategy_id = Column(Integer, ForeignKey('strategies.id'), nullable=True) strategy_id = Column(Integer, ForeignKey('strategies.id'), nullable=True)
scope = Column(JSONEncodedList, nullable=True) scope = Column(JSONEncodedList, nullable=True)
goal = orm.relationship(Goal, foreign_keys=goal_id, lazy=None)
class Action(Base): strategy = orm.relationship(Strategy, foreign_keys=strategy_id, lazy=None)
"""Represents an action."""
__tablename__ = 'actions'
__table_args__ = (
UniqueConstraint('uuid', name='uniq_actions0uuid'),
table_args()
)
id = Column(Integer, primary_key=True)
uuid = Column(String(36), nullable=False)
action_plan_id = Column(Integer, ForeignKey('action_plans.id'),
nullable=False)
# only for the first version
action_type = Column(String(255), nullable=False)
input_parameters = Column(JSONEncodedDict, nullable=True)
state = Column(String(20), nullable=True)
next = Column(String(36), nullable=True)
class ActionPlan(Base): class ActionPlan(Base):
@ -211,7 +204,7 @@ class ActionPlan(Base):
UniqueConstraint('uuid', name='uniq_action_plans0uuid'), UniqueConstraint('uuid', name='uniq_action_plans0uuid'),
table_args() table_args()
) )
id = Column(Integer, primary_key=True) id = Column(Integer, primary_key=True, autoincrement=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=False) audit_id = Column(Integer, ForeignKey('audits.id'), nullable=False)
@ -219,6 +212,31 @@ class ActionPlan(Base):
state = Column(String(20), nullable=True) state = Column(String(20), nullable=True)
global_efficacy = Column(JSONEncodedDict, nullable=True) global_efficacy = Column(JSONEncodedDict, nullable=True)
audit = orm.relationship(Audit, foreign_keys=audit_id, lazy=None)
strategy = orm.relationship(Strategy, foreign_keys=strategy_id, lazy=None)
class Action(Base):
"""Represents an action."""
__tablename__ = 'actions'
__table_args__ = (
UniqueConstraint('uuid', name='uniq_actions0uuid'),
table_args()
)
id = Column(Integer, primary_key=True, autoincrement=True)
uuid = Column(String(36), nullable=False)
action_plan_id = Column(Integer, ForeignKey('action_plans.id'),
nullable=False)
# only for the first version
action_type = Column(String(255), nullable=False)
input_parameters = Column(JSONEncodedDict, nullable=True)
state = Column(String(20), nullable=True)
next = Column(String(36), nullable=True)
action_plan = orm.relationship(
ActionPlan, foreign_keys=action_plan_id, lazy=None)
class EfficacyIndicator(Base): class EfficacyIndicator(Base):
"""Represents an efficacy indicator.""" """Represents an efficacy indicator."""
@ -228,7 +246,7 @@ class EfficacyIndicator(Base):
UniqueConstraint('uuid', name='uniq_efficacy_indicators0uuid'), UniqueConstraint('uuid', name='uniq_efficacy_indicators0uuid'),
table_args() table_args()
) )
id = Column(Integer, primary_key=True) id = Column(Integer, primary_key=True, autoincrement=True)
uuid = Column(String(36)) uuid = Column(String(36))
name = Column(String(63)) name = Column(String(63))
description = Column(String(255), nullable=True) description = Column(String(255), nullable=True)
@ -237,6 +255,9 @@ class EfficacyIndicator(Base):
action_plan_id = Column(Integer, ForeignKey('action_plans.id'), action_plan_id = Column(Integer, ForeignKey('action_plans.id'),
nullable=False) nullable=False)
action_plan = orm.relationship(
ActionPlan, foreign_keys=action_plan_id, lazy=None)
class ScoringEngine(Base): class ScoringEngine(Base):
"""Represents a scoring engine.""" """Represents a scoring engine."""
@ -247,7 +268,7 @@ class ScoringEngine(Base):
UniqueConstraint('name', 'deleted', name='uniq_scoring_engines0name'), UniqueConstraint('name', 'deleted', name='uniq_scoring_engines0name'),
table_args() table_args()
) )
id = Column(Integer, primary_key=True) id = Column(Integer, primary_key=True, autoincrement=True)
uuid = Column(String(36), nullable=False) uuid = Column(String(36), nullable=False)
name = Column(String(63), nullable=False) name = Column(String(63), nullable=False)
description = Column(String(255), nullable=True) description = Column(String(255), nullable=True)

View File

@ -504,24 +504,6 @@ class TestPatchStateTransitionDenied(api_base.FunctionalTest):
self.assertTrue(response.json['error_message']) self.assertTrue(response.json['error_message'])
class TestPatchStateDeletedNotFound(api_base.FunctionalTest):
@mock.patch.object(
db_api.BaseConnection, 'update_action_plan',
mock.Mock(side_effect=lambda ap: ap.save() or ap))
def test_replace_state_pending_not_found(self):
action_plan = obj_utils.create_test_action_plan(
self.context, state=objects.action_plan.State.DELETED)
response = self.get_json(
'/action_plans/%s' % action_plan.uuid,
expect_errors=True
)
self.assertEqual(404, response.status_code)
self.assertEqual('application/json', response.content_type)
self.assertTrue(response.json['error_message'])
class TestPatchStateTransitionOk(api_base.FunctionalTest): class TestPatchStateTransitionOk(api_base.FunctionalTest):
scenarios = [ scenarios = [

View File

@ -245,12 +245,32 @@ class DbActionTestCase(base.DbTestCase):
def test_get_action_list(self): def test_get_action_list(self):
uuids = [] uuids = []
for _ in range(1, 6): for _ in range(1, 4):
action = utils.create_test_action(uuid=w_utils.generate_uuid()) action = utils.create_test_action(uuid=w_utils.generate_uuid())
uuids.append(six.text_type(action['uuid'])) uuids.append(six.text_type(action['uuid']))
res = self.dbapi.get_action_list(self.context) actions = self.dbapi.get_action_list(self.context)
res_uuids = [r.uuid for r in res] action_uuids = [a.uuid for a in actions]
self.assertEqual(uuids.sort(), res_uuids.sort()) self.assertEqual(3, len(action_uuids))
self.assertEqual(sorted(uuids), sorted(action_uuids))
for action in actions:
self.assertIsNone(action.action_plan)
def test_get_action_list_eager(self):
_action_plan = utils.get_test_action_plan()
action_plan = self.dbapi.create_action_plan(_action_plan)
uuids = []
for i in range(1, 4):
action = utils.create_test_action(
id=i, uuid=w_utils.generate_uuid(),
action_plan_id=action_plan.id)
uuids.append(six.text_type(action['uuid']))
actions = self.dbapi.get_action_list(self.context, eager=True)
action_map = {a.uuid: a for a in actions}
self.assertEqual(sorted(uuids), sorted(action_map.keys()))
eager_action = action_map[action.uuid]
self.assertEqual(
action_plan.as_dict(), eager_action.action_plan.as_dict())
def test_get_action_list_with_filters(self): def test_get_action_list_with_filters(self):
audit = utils.create_test_audit(uuid=w_utils.generate_uuid()) audit = utils.create_test_audit(uuid=w_utils.generate_uuid())
@ -299,8 +319,8 @@ class DbActionTestCase(base.DbTestCase):
self.context, self.context,
filters={'action_plan_uuid': action_plan['uuid']}) filters={'action_plan_uuid': action_plan['uuid']})
self.assertEqual( self.assertEqual(
[action1['id'], action3['id']].sort(), sorted([action1['id'], action3['id']]),
[r.id for r in res].sort()) sorted([r.id for r in res]))
res = self.dbapi.get_action_list( res = self.dbapi.get_action_list(
self.context, self.context,

View File

@ -242,16 +242,40 @@ class DbActionPlanTestCase(base.DbTestCase):
def test_get_action_plan_list(self): def test_get_action_plan_list(self):
uuids = [] uuids = []
for i in range(1, 6): for _ in range(1, 4):
audit = utils.create_test_action_plan(uuid=w_utils.generate_uuid()) action_plan = utils.create_test_action_plan(
uuids.append(six.text_type(audit['uuid'])) uuid=w_utils.generate_uuid())
res = self.dbapi.get_action_plan_list(self.context) uuids.append(six.text_type(action_plan['uuid']))
res_uuids = [r.uuid for r in res] action_plans = self.dbapi.get_action_plan_list(self.context)
self.assertEqual(uuids.sort(), res_uuids.sort()) action_plan_uuids = [ap.uuid for ap in action_plans]
self.assertEqual(sorted(uuids), sorted(action_plan_uuids))
for action_plan in action_plans:
self.assertIsNone(action_plan.audit)
self.assertIsNone(action_plan.strategy)
def test_get_action_plan_list_eager(self):
_strategy = utils.get_test_strategy()
strategy = self.dbapi.create_strategy(_strategy)
_audit = utils.get_test_audit()
audit = self.dbapi.create_audit(_audit)
uuids = []
for _ in range(1, 4):
action_plan = utils.create_test_action_plan(
uuid=w_utils.generate_uuid())
uuids.append(six.text_type(action_plan['uuid']))
action_plans = self.dbapi.get_action_plan_list(
self.context, eager=True)
action_plan_map = {a.uuid: a for a in action_plans}
self.assertEqual(sorted(uuids), sorted(action_plan_map.keys()))
eager_action_plan = action_plan_map[action_plan.uuid]
self.assertEqual(
strategy.as_dict(), eager_action_plan.strategy.as_dict())
self.assertEqual(audit.as_dict(), eager_action_plan.audit.as_dict())
def test_get_action_plan_list_with_filters(self): def test_get_action_plan_list_with_filters(self):
audit = self._create_test_audit( audit = self._create_test_audit(
id=1, id=2,
audit_type='ONESHOT', audit_type='ONESHOT',
uuid=w_utils.generate_uuid(), uuid=w_utils.generate_uuid(),
deadline=None, deadline=None,

View File

@ -20,7 +20,7 @@ import six
from watcher.common import exception from watcher.common import exception
from watcher.common import utils as w_utils from watcher.common import utils as w_utils
from watcher.objects import audit as audit_objects from watcher import objects
from watcher.tests.db import base from watcher.tests.db import base
from watcher.tests.db import utils from watcher.tests.db import utils
@ -48,11 +48,11 @@ class TestDbAuditFilters(base.DbTestCase):
with freezegun.freeze_time(self.FAKE_OLD_DATE): with freezegun.freeze_time(self.FAKE_OLD_DATE):
self.audit2 = utils.create_test_audit( self.audit2 = utils.create_test_audit(
audit_template_id=self.audit_template.id, id=2, uuid=None, audit_template_id=self.audit_template.id, id=2, uuid=None,
state=audit_objects.State.FAILED) state=objects.audit.State.FAILED)
with freezegun.freeze_time(self.FAKE_OLDER_DATE): with freezegun.freeze_time(self.FAKE_OLDER_DATE):
self.audit3 = utils.create_test_audit( self.audit3 = utils.create_test_audit(
audit_template_id=self.audit_template.id, id=3, uuid=None, audit_template_id=self.audit_template.id, id=3, uuid=None,
state=audit_objects.State.CANCELLED) state=objects.audit.State.CANCELLED)
def _soft_delete_audits(self): def _soft_delete_audits(self):
with freezegun.freeze_time(self.FAKE_TODAY): with freezegun.freeze_time(self.FAKE_TODAY):
@ -66,15 +66,15 @@ class TestDbAuditFilters(base.DbTestCase):
with freezegun.freeze_time(self.FAKE_TODAY): with freezegun.freeze_time(self.FAKE_TODAY):
self.dbapi.update_audit( self.dbapi.update_audit(
self.audit1.uuid, self.audit1.uuid,
values={"state": audit_objects.State.SUCCEEDED}) values={"state": objects.audit.State.SUCCEEDED})
with freezegun.freeze_time(self.FAKE_OLD_DATE): with freezegun.freeze_time(self.FAKE_OLD_DATE):
self.dbapi.update_audit( self.dbapi.update_audit(
self.audit2.uuid, self.audit2.uuid,
values={"state": audit_objects.State.SUCCEEDED}) values={"state": objects.audit.State.SUCCEEDED})
with freezegun.freeze_time(self.FAKE_OLDER_DATE): with freezegun.freeze_time(self.FAKE_OLDER_DATE):
self.dbapi.update_audit( self.dbapi.update_audit(
self.audit3.uuid, self.audit3.uuid,
values={"state": audit_objects.State.SUCCEEDED}) values={"state": objects.audit.State.SUCCEEDED})
def test_get_audit_list_filter_deleted_true(self): def test_get_audit_list_filter_deleted_true(self):
with freezegun.freeze_time(self.FAKE_TODAY): with freezegun.freeze_time(self.FAKE_TODAY):
@ -230,8 +230,8 @@ class TestDbAuditFilters(base.DbTestCase):
def test_get_audit_list_filter_state_in(self): def test_get_audit_list_filter_state_in(self):
res = self.dbapi.get_audit_list( res = self.dbapi.get_audit_list(
self.context, self.context,
filters={'state__in': (audit_objects.State.FAILED, filters={'state__in': (objects.audit.State.FAILED,
audit_objects.State.CANCELLED)}) objects.audit.State.CANCELLED)})
self.assertEqual( self.assertEqual(
[self.audit2['id'], self.audit3['id']], [self.audit2['id'], self.audit3['id']],
@ -240,8 +240,8 @@ class TestDbAuditFilters(base.DbTestCase):
def test_get_audit_list_filter_state_notin(self): def test_get_audit_list_filter_state_notin(self):
res = self.dbapi.get_audit_list( res = self.dbapi.get_audit_list(
self.context, self.context,
filters={'state__notin': (audit_objects.State.FAILED, filters={'state__notin': (objects.audit.State.FAILED,
audit_objects.State.CANCELLED)}) objects.audit.State.CANCELLED)})
self.assertEqual( self.assertEqual(
[self.audit1['id']], [self.audit1['id']],
@ -257,29 +257,52 @@ class DbAuditTestCase(base.DbTestCase):
def test_get_audit_list(self): def test_get_audit_list(self):
uuids = [] uuids = []
for _ in range(1, 6): for _ in range(1, 4):
audit = utils.create_test_audit(uuid=w_utils.generate_uuid()) audit = utils.create_test_audit(uuid=w_utils.generate_uuid())
uuids.append(six.text_type(audit['uuid'])) uuids.append(six.text_type(audit['uuid']))
res = self.dbapi.get_audit_list(self.context) audits = self.dbapi.get_audit_list(self.context)
res_uuids = [r.uuid for r in res] audit_uuids = [a.uuid for a in audits]
self.assertEqual(uuids.sort(), res_uuids.sort()) self.assertEqual(sorted(uuids), sorted(audit_uuids))
for audit in audits:
self.assertIsNone(audit.goal)
self.assertIsNone(audit.strategy)
def test_get_audit_list_eager(self):
_goal = utils.get_test_goal()
goal = self.dbapi.create_goal(_goal)
_strategy = utils.get_test_strategy()
strategy = self.dbapi.create_strategy(_strategy)
uuids = []
for i in range(1, 4):
audit = utils.create_test_audit(
id=i, uuid=w_utils.generate_uuid(),
goal_id=goal.id, strategy_id=strategy.id)
uuids.append(six.text_type(audit['uuid']))
audits = self.dbapi.get_audit_list(self.context, eager=True)
audit_map = {a.uuid: a for a in audits}
self.assertEqual(sorted(uuids), sorted(audit_map.keys()))
eager_audit = audit_map[audit.uuid]
self.assertEqual(goal.as_dict(), eager_audit.goal.as_dict())
self.assertEqual(strategy.as_dict(), eager_audit.strategy.as_dict())
def test_get_audit_list_with_filters(self): def test_get_audit_list_with_filters(self):
audit1 = self._create_test_audit( audit1 = self._create_test_audit(
id=1, id=1,
audit_type='ONESHOT', audit_type=objects.audit.AuditType.ONESHOT.value,
uuid=w_utils.generate_uuid(), uuid=w_utils.generate_uuid(),
deadline=None, deadline=None,
state=audit_objects.State.ONGOING) state=objects.audit.State.ONGOING)
audit2 = self._create_test_audit( audit2 = self._create_test_audit(
id=2, id=2,
audit_type='CONTINUOUS', audit_type='CONTINUOUS',
uuid=w_utils.generate_uuid(), uuid=w_utils.generate_uuid(),
deadline=None, deadline=None,
state=audit_objects.State.PENDING) state=objects.audit.State.PENDING)
res = self.dbapi.get_audit_list(self.context, res = self.dbapi.get_audit_list(
filters={'audit_type': 'ONESHOT'}) self.context,
filters={'audit_type': objects.audit.AuditType.ONESHOT.value})
self.assertEqual([audit1['id']], [r.id for r in res]) self.assertEqual([audit1['id']], [r.id for r in res])
res = self.dbapi.get_audit_list(self.context, res = self.dbapi.get_audit_list(self.context,
@ -288,12 +311,12 @@ class DbAuditTestCase(base.DbTestCase):
res = self.dbapi.get_audit_list( res = self.dbapi.get_audit_list(
self.context, self.context,
filters={'state': audit_objects.State.ONGOING}) filters={'state': objects.audit.State.ONGOING})
self.assertEqual([audit1['id']], [r.id for r in res]) self.assertEqual([audit1['id']], [r.id for r in res])
res = self.dbapi.get_audit_list( res = self.dbapi.get_audit_list(
self.context, self.context,
filters={'state': audit_objects.State.PENDING}) filters={'state': objects.audit.State.PENDING})
self.assertEqual([audit2['id']], [r.id for r in res]) self.assertEqual([audit2['id']], [r.id for r in res])
def test_get_audit_list_with_filter_by_uuid(self): def test_get_audit_list_with_filter_by_uuid(self):

View File

@ -232,14 +232,40 @@ class DbAuditTemplateTestCase(base.DbTestCase):
def test_get_audit_template_list(self): def test_get_audit_template_list(self):
uuids = [] uuids = []
for i in range(1, 6): for i in range(1, 4):
audit_template = utils.create_test_audit_template( audit_template = utils.create_test_audit_template(
id=i,
uuid=w_utils.generate_uuid(), uuid=w_utils.generate_uuid(),
name='My Audit Template {0}'.format(i)) name='My Audit Template {0}'.format(i))
uuids.append(six.text_type(audit_template['uuid'])) uuids.append(six.text_type(audit_template['uuid']))
res = self.dbapi.get_audit_template_list(self.context) audit_templates = self.dbapi.get_audit_template_list(self.context)
res_uuids = [r.uuid for r in res] audit_template_uuids = [at.uuid for at in audit_templates]
self.assertEqual(uuids.sort(), res_uuids.sort()) self.assertEqual(sorted(uuids), sorted(audit_template_uuids))
for audit_template in audit_templates:
self.assertIsNone(audit_template.goal)
self.assertIsNone(audit_template.strategy)
def test_get_audit_template_list_eager(self):
_goal = utils.get_test_goal()
goal = self.dbapi.create_goal(_goal)
_strategy = utils.get_test_strategy()
strategy = self.dbapi.create_strategy(_strategy)
uuids = []
for i in range(1, 4):
audit_template = utils.create_test_audit_template(
id=i, uuid=w_utils.generate_uuid(),
name='My Audit Template {0}'.format(i),
goal_id=goal.id, strategy_id=strategy.id)
uuids.append(six.text_type(audit_template['uuid']))
audit_templates = self.dbapi.get_audit_template_list(
self.context, eager=True)
audit_template_map = {a.uuid: a for a in audit_templates}
self.assertEqual(sorted(uuids), sorted(audit_template_map.keys()))
eager_audit_template = audit_template_map[audit_template.uuid]
self.assertEqual(goal.as_dict(), eager_audit_template.goal.as_dict())
self.assertEqual(
strategy.as_dict(), eager_audit_template.strategy.as_dict())
def test_get_audit_template_list_with_filters(self): def test_get_audit_template_list_with_filters(self):
audit_template1 = self._create_test_audit_template( audit_template1 = self._create_test_audit_template(

View File

@ -256,14 +256,37 @@ class DbEfficacyIndicatorTestCase(base.DbTestCase):
def test_get_efficacy_indicator_list(self): def test_get_efficacy_indicator_list(self):
uuids = [] uuids = []
action_plan = self._create_test_action_plan() action_plan = self._create_test_action_plan()
for id_ in range(1, 6): for id_ in range(1, 4):
efficacy_indicator = utils.create_test_efficacy_indicator( efficacy_indicator = utils.create_test_efficacy_indicator(
action_plan_id=action_plan.id, id=id_, uuid=None, action_plan_id=action_plan.id, id=id_, uuid=None,
name="efficacy_indicator", description="Test Indicator ") name="efficacy_indicator", description="Test Indicator ")
uuids.append(six.text_type(efficacy_indicator['uuid'])) uuids.append(six.text_type(efficacy_indicator['uuid']))
res = self.dbapi.get_efficacy_indicator_list(self.context) efficacy_indicators = self.dbapi.get_efficacy_indicator_list(
res_uuids = [r.uuid for r in res] self.context)
self.assertEqual(uuids.sort(), res_uuids.sort()) efficacy_indicator_uuids = [ei.uuid for ei in efficacy_indicators]
self.assertEqual(sorted(uuids), sorted(efficacy_indicator_uuids))
for efficacy_indicator in efficacy_indicators:
self.assertIsNone(efficacy_indicator.action_plan)
def test_get_efficacy_indicator_list_eager(self):
_action_plan = utils.get_test_action_plan()
action_plan = self.dbapi.create_action_plan(_action_plan)
uuids = []
for i in range(1, 4):
efficacy_indicator = utils.create_test_efficacy_indicator(
id=i, uuid=w_utils.generate_uuid(),
action_plan_id=action_plan.id)
uuids.append(six.text_type(efficacy_indicator['uuid']))
efficacy_indicators = self.dbapi.get_efficacy_indicator_list(
self.context, eager=True)
efficacy_indicator_map = {a.uuid: a for a in efficacy_indicators}
self.assertEqual(sorted(uuids), sorted(efficacy_indicator_map.keys()))
eager_efficacy_indicator = efficacy_indicator_map[
efficacy_indicator.uuid]
self.assertEqual(
action_plan.as_dict(),
eager_efficacy_indicator.action_plan.as_dict())
def test_get_efficacy_indicator_list_with_filters(self): def test_get_efficacy_indicator_list_with_filters(self):
audit = utils.create_test_audit(uuid=w_utils.generate_uuid()) audit = utils.create_test_audit(uuid=w_utils.generate_uuid())
@ -311,8 +334,8 @@ class DbEfficacyIndicatorTestCase(base.DbTestCase):
self.context, self.context,
filters={'action_plan_uuid': action_plan['uuid']}) filters={'action_plan_uuid': action_plan['uuid']})
self.assertEqual( self.assertEqual(
[efficacy_indicator1['id'], efficacy_indicator3['id']].sort(), sorted([efficacy_indicator1['id'], efficacy_indicator3['id']]),
[r.id for r in res].sort()) sorted([r.id for r in res]))
def test_get_efficacy_indicator_list_with_filter_by_uuid(self): def test_get_efficacy_indicator_list_with_filter_by_uuid(self):
efficacy_indicator = self._create_test_efficacy_indicator() efficacy_indicator = self._create_test_efficacy_indicator()

View File

@ -230,16 +230,16 @@ class DbGoalTestCase(base.DbTestCase):
def test_get_goal_list(self): def test_get_goal_list(self):
uuids = [] uuids = []
for i in range(1, 6): for i in range(1, 4):
goal = utils.create_test_goal( goal = utils.create_test_goal(
id=i, id=i,
uuid=w_utils.generate_uuid(), uuid=w_utils.generate_uuid(),
name="GOAL_%s" % i, name="GOAL_%s" % i,
display_name='My Goal %s' % i) display_name='My Goal %s' % i)
uuids.append(six.text_type(goal['uuid'])) uuids.append(six.text_type(goal['uuid']))
res = self.dbapi.get_goal_list(self.context) goals = self.dbapi.get_goal_list(self.context)
res_uuids = [r.uuid for r in res] goal_uuids = [g.uuid for g in goals]
self.assertEqual(uuids.sort(), res_uuids.sort()) self.assertEqual(sorted(uuids), sorted(goal_uuids))
def test_get_goal_list_with_filters(self): def test_get_goal_list_with_filters(self):
goal1 = self._create_test_goal( goal1 = self._create_test_goal(

View File

@ -235,7 +235,7 @@ class DbScoringEngineTestCase(base.DbTestCase):
def test_get_scoring_engine_list(self): def test_get_scoring_engine_list(self):
names = [] names = []
for i in range(1, 6): for i in range(1, 4):
scoring_engine = utils.create_test_scoring_engine( scoring_engine = utils.create_test_scoring_engine(
id=i, id=i,
uuid=w_utils.generate_uuid(), uuid=w_utils.generate_uuid(),
@ -243,9 +243,9 @@ class DbScoringEngineTestCase(base.DbTestCase):
description='My ScoringEngine {0}'.format(i), description='My ScoringEngine {0}'.format(i),
metainfo='a{0}=b{0}'.format(i)) metainfo='a{0}=b{0}'.format(i))
names.append(six.text_type(scoring_engine['name'])) names.append(six.text_type(scoring_engine['name']))
res = self.dbapi.get_scoring_engine_list(self.context) scoring_engines = self.dbapi.get_scoring_engine_list(self.context)
res_names = [r.name for r in res] scoring_engines_names = [se.name for se in scoring_engines]
self.assertEqual(names.sort(), res_names.sort()) self.assertEqual(sorted(names), sorted(scoring_engines_names))
def test_get_scoring_engine_list_with_filters(self): def test_get_scoring_engine_list_with_filters(self):
scoring_engine1 = self._create_test_scoring_engine( scoring_engine1 = self._create_test_scoring_engine(
@ -310,7 +310,7 @@ class DbScoringEngineTestCase(base.DbTestCase):
self.assertRaises(exception.Invalid, self.assertRaises(exception.Invalid,
self.dbapi.update_scoring_engine, self.dbapi.update_scoring_engine,
scoring_engine['id'], scoring_engine['id'],
{'id': 5}) {'uuid': w_utils.generate_uuid()})
def test_update_scoring_engine_that_does_not_exist(self): def test_update_scoring_engine_that_does_not_exist(self):
self.assertRaises(exception.ScoringEngineNotFound, self.assertRaises(exception.ScoringEngineNotFound,

View File

@ -18,7 +18,6 @@
"""Tests for manipulating Service via the DB API""" """Tests for manipulating Service via the DB API"""
import freezegun import freezegun
import six
from oslo_utils import timeutils from oslo_utils import timeutils
@ -237,15 +236,15 @@ class DbServiceTestCase(base.DbTestCase):
def test_get_service_list(self): def test_get_service_list(self):
ids = [] ids = []
for i in range(1, 6): for i in range(1, 4):
service = utils.create_test_service( service = utils.create_test_service(
id=i, id=i,
name="SERVICE_ID_%s" % i, name="SERVICE_ID_%s" % i,
host="controller_{0}".format(i)) host="controller_{0}".format(i))
ids.append(six.text_type(service['id'])) ids.append(service['id'])
res = self.dbapi.get_service_list(self.context) services = self.dbapi.get_service_list(self.context)
res_ids = [r.id for r in res] service_ids = [s.id for s in services]
self.assertEqual(ids.sort(), res_ids.sort()) self.assertEqual(sorted(ids), sorted(service_ids))
def test_get_service_list_with_filters(self): def test_get_service_list_with_filters(self):
service1 = self._create_test_service( service1 = self._create_test_service(

View File

@ -245,17 +245,37 @@ class DbStrategyTestCase(base.DbTestCase):
return strategy return strategy
def test_get_strategy_list(self): def test_get_strategy_list(self):
ids = [] uuids = []
for i in range(1, 6): for i in range(1, 4):
strategy = utils.create_test_strategy( strategy = utils.create_test_strategy(
id=i, id=i,
uuid=w_utils.generate_uuid(), uuid=w_utils.generate_uuid(),
name="STRATEGY_ID_%s" % i, name="STRATEGY_ID_%s" % i,
display_name='My Strategy {0}'.format(i)) display_name='My Strategy {0}'.format(i))
ids.append(six.text_type(strategy['uuid'])) uuids.append(six.text_type(strategy['uuid']))
res = self.dbapi.get_strategy_list(self.context) strategies = self.dbapi.get_strategy_list(self.context)
res_ids = [r.display_name for r in res] strategy_uuids = [s.uuid for s in strategies]
self.assertEqual(ids.sort(), res_ids.sort()) self.assertEqual(sorted(uuids), sorted(strategy_uuids))
for strategy in strategies:
self.assertIsNone(strategy.goal)
def test_get_strategy_list_eager(self):
_goal = utils.get_test_goal()
goal = self.dbapi.create_goal(_goal)
uuids = []
for i in range(1, 4):
strategy = utils.create_test_strategy(
id=i,
uuid=w_utils.generate_uuid(),
name="STRATEGY_ID_%s" % i,
display_name='My Strategy {0}'.format(i),
goal_id=goal.id)
uuids.append(six.text_type(strategy['uuid']))
strategys = self.dbapi.get_strategy_list(self.context, eager=True)
strategy_map = {a.uuid: a for a in strategys}
self.assertEqual(sorted(uuids), sorted(strategy_map.keys()))
eager_strategy = strategy_map[strategy.uuid]
self.assertEqual(goal.as_dict(), eager_strategy.goal.as_dict())
def test_get_strategy_list_with_filters(self): def test_get_strategy_list_with_filters(self):
strategy1 = self._create_test_strategy( strategy1 = self._create_test_strategy(