oslo.db 12.1.0 has changed the default value for the 'autocommit' parameter of 'LegacyEngineFacade' from 'True' to 'False'. This is a necessary step to ensure compatibility with SQLAlchemy 2.0. However, we are currently relying on the autocommit behavior and need changes to explicitly manage sessions. Until that happens, we need to override the default. Co-Authored-By: Stephen Finucane <stephenfin@redhat.com> Change-Id: I7db39d958d087322bfa0aad70dfbd04de9228dd7
1161 lines
43 KiB
Python
1161 lines
43 KiB
Python
# -*- encoding: utf-8 -*-
|
|
#
|
|
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
"""SQLAlchemy storage backend."""
|
|
|
|
import collections
|
|
import datetime
|
|
import operator
|
|
|
|
from oslo_config import cfg
|
|
from oslo_db import exception as db_exc
|
|
from oslo_db.sqlalchemy import session as db_session
|
|
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 joinedload
|
|
|
|
from watcher._i18n import _
|
|
from watcher.common import exception
|
|
from watcher.common import utils
|
|
from watcher.db import api
|
|
from watcher.db.sqlalchemy import models
|
|
from watcher import objects
|
|
|
|
CONF = cfg.CONF
|
|
|
|
_FACADE = None
|
|
|
|
|
|
def _create_facade_lazily():
|
|
global _FACADE
|
|
if _FACADE is None:
|
|
# FIXME(amoralej): Remove autocommit=True (and ideally use of
|
|
# LegacyEngineFacade) asap since it's not compatible with SQLAlchemy
|
|
# 2.0.
|
|
_FACADE = db_session.EngineFacade.from_config(CONF,
|
|
autocommit=True)
|
|
return _FACADE
|
|
|
|
|
|
def get_engine():
|
|
facade = _create_facade_lazily()
|
|
return facade.get_engine()
|
|
|
|
|
|
def get_session(**kwargs):
|
|
facade = _create_facade_lazily()
|
|
return facade.get_session(**kwargs)
|
|
|
|
|
|
def get_backend():
|
|
"""The backend is this module itself."""
|
|
return Connection()
|
|
|
|
|
|
def model_query(model, *args, **kwargs):
|
|
"""Query helper for simpler session usage.
|
|
|
|
:param session: if present, the session to use
|
|
"""
|
|
session = kwargs.get('session') or get_session()
|
|
query = session.query(model, *args)
|
|
return query
|
|
|
|
|
|
def add_identity_filter(query, value):
|
|
"""Adds an identity filter to a query.
|
|
|
|
Filters results by ID, if supplied value is a valid integer.
|
|
Otherwise attempts to filter results by UUID.
|
|
|
|
:param query: Initial query to add filter to.
|
|
:param value: Value for filtering results by.
|
|
:return: Modified query.
|
|
"""
|
|
if utils.is_int_like(value):
|
|
return query.filter_by(id=value)
|
|
elif utils.is_uuid_like(value):
|
|
return query.filter_by(uuid=value)
|
|
else:
|
|
raise exception.InvalidIdentity(identity=value)
|
|
|
|
|
|
def _paginate_query(model, limit=None, marker=None, sort_key=None,
|
|
sort_dir=None, query=None):
|
|
if not query:
|
|
query = model_query(model)
|
|
sort_keys = ['id']
|
|
if sort_key and sort_key not in sort_keys:
|
|
sort_keys.insert(0, sort_key)
|
|
query = db_utils.paginate_query(query, model, limit, sort_keys,
|
|
marker=marker, sort_dir=sort_dir)
|
|
return query.all()
|
|
|
|
|
|
class JoinMap(utils.Struct):
|
|
"""Mapping for the Join-based queries"""
|
|
|
|
|
|
NaturalJoinFilter = collections.namedtuple(
|
|
'NaturalJoinFilter', ['join_fieldname', 'join_model'])
|
|
|
|
|
|
class Connection(api.BaseConnection):
|
|
"""SqlAlchemy connection."""
|
|
|
|
valid_operators = {
|
|
"": operator.eq,
|
|
"eq": operator.eq,
|
|
"neq": operator.ne,
|
|
"gt": operator.gt,
|
|
"gte": operator.ge,
|
|
"lt": operator.lt,
|
|
"lte": operator.le,
|
|
"in": lambda field, choices: field.in_(choices),
|
|
"notin": lambda field, choices: field.notin_(choices),
|
|
}
|
|
|
|
def __init__(self):
|
|
super(Connection, self).__init__()
|
|
|
|
def __add_simple_filter(self, query, model, fieldname, value, operator_):
|
|
field = getattr(model, fieldname)
|
|
|
|
if (fieldname != 'deleted' and value and
|
|
field.type.python_type is datetime.datetime):
|
|
if not isinstance(value, datetime.datetime):
|
|
value = timeutils.parse_isotime(value)
|
|
|
|
return query.filter(self.valid_operators[operator_](field, value))
|
|
|
|
def __add_join_filter(self, query, model, fieldname, value, operator_):
|
|
query = query.join(model)
|
|
return self.__add_simple_filter(query, model, fieldname,
|
|
value, operator_)
|
|
|
|
def __decompose_filter(self, raw_fieldname):
|
|
"""Decompose a filter name into its 2 subparts
|
|
|
|
A filter can take 2 forms:
|
|
|
|
- "<FIELDNAME>" which is a syntactic sugar for "<FIELDNAME>__eq"
|
|
- "<FIELDNAME>__<OPERATOR>" where <OPERATOR> is the comparison operator
|
|
to be used.
|
|
|
|
Available operators are:
|
|
|
|
- eq
|
|
- neq
|
|
- gt
|
|
- gte
|
|
- lt
|
|
- lte
|
|
- in
|
|
- notin
|
|
"""
|
|
separator = '__'
|
|
fieldname, separator, operator_ = raw_fieldname.partition(separator)
|
|
|
|
if operator_ and operator_ not in self.valid_operators:
|
|
raise exception.InvalidOperator(
|
|
operator=operator_, valid_operators=self.valid_operators)
|
|
|
|
return fieldname, operator_
|
|
|
|
def _add_filters(self, query, model, filters=None,
|
|
plain_fields=None, join_fieldmap=None):
|
|
"""Generic way to add filters to a Watcher model
|
|
|
|
Each filter key provided by the `filters` parameter will be decomposed
|
|
into 2 pieces: the field name and the comparison operator
|
|
|
|
- "": By default, the "eq" is applied if no operator is provided
|
|
- "eq", which stands for "equal" : e.g. {"state__eq": "PENDING"}
|
|
will result in the "WHERE state = 'PENDING'" clause.
|
|
- "neq", which stands for "not equal" : e.g. {"state__neq": "PENDING"}
|
|
will result in the "WHERE state != 'PENDING'" clause.
|
|
- "gt", which stands for "greater than" : e.g.
|
|
{"created_at__gt": "2016-06-06T10:33:22.063176"} will result in the
|
|
"WHERE created_at > '2016-06-06T10:33:22.063176'" clause.
|
|
- "gte", which stands for "greater than or equal to" : e.g.
|
|
{"created_at__gte": "2016-06-06T10:33:22.063176"} will result in the
|
|
"WHERE created_at >= '2016-06-06T10:33:22.063176'" clause.
|
|
- "lt", which stands for "less than" : e.g.
|
|
{"created_at__lt": "2016-06-06T10:33:22.063176"} will result in the
|
|
"WHERE created_at < '2016-06-06T10:33:22.063176'" clause.
|
|
- "lte", which stands for "less than or equal to" : e.g.
|
|
{"created_at__lte": "2016-06-06T10:33:22.063176"} will result in the
|
|
"WHERE created_at <= '2016-06-06T10:33:22.063176'" clause.
|
|
- "in": e.g. {"state__in": ('SUCCEEDED', 'FAILED')} will result in the
|
|
"WHERE state IN ('SUCCEEDED', 'FAILED')" clause.
|
|
|
|
:param query: a :py:class:`sqlalchemy.orm.query.Query` instance
|
|
:param model: the model class the filters should relate to
|
|
:param filters: dict with the following structure {"fieldname": value}
|
|
:param plain_fields: a :py:class:`sqlalchemy.orm.query.Query` instance
|
|
:param join_fieldmap: a :py:class:`sqlalchemy.orm.query.Query` instance
|
|
"""
|
|
soft_delete_mixin_fields = ['deleted', 'deleted_at']
|
|
timestamp_mixin_fields = ['created_at', 'updated_at']
|
|
filters = filters or {}
|
|
|
|
# Special case for 'deleted' because it is a non-boolean flag
|
|
if 'deleted' in filters:
|
|
deleted_filter = filters.pop('deleted')
|
|
op = 'eq' if not bool(deleted_filter) else 'neq'
|
|
filters['deleted__%s' % op] = 0
|
|
|
|
plain_fields = tuple(
|
|
(list(plain_fields) or []) +
|
|
soft_delete_mixin_fields +
|
|
timestamp_mixin_fields)
|
|
join_fieldmap = join_fieldmap or {}
|
|
|
|
for raw_fieldname, value in filters.items():
|
|
fieldname, operator_ = self.__decompose_filter(raw_fieldname)
|
|
if fieldname in plain_fields:
|
|
query = self.__add_simple_filter(
|
|
query, model, fieldname, value, operator_)
|
|
elif fieldname in join_fieldmap:
|
|
join_field, join_model = join_fieldmap[fieldname]
|
|
query = self.__add_join_filter(
|
|
query, join_model, join_field, value, operator_)
|
|
|
|
return query
|
|
|
|
@staticmethod
|
|
def _get_relationships(model):
|
|
return inspect(model).relationships
|
|
|
|
@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 _create(self, model, values):
|
|
obj = model()
|
|
cleaned_values = {k: v for k, v in values.items()
|
|
if k not in self._get_relationships(model)}
|
|
obj.update(cleaned_values)
|
|
obj.save()
|
|
return obj
|
|
|
|
def _get(self, context, model, fieldname, value, eager):
|
|
query = model_query(model)
|
|
if eager:
|
|
query = self._set_eager_options(model, query)
|
|
|
|
query = query.filter(getattr(model, fieldname) == value)
|
|
if not context.show_deleted:
|
|
query = query.filter(model.deleted_at.is_(None))
|
|
|
|
try:
|
|
obj = query.one()
|
|
except exc.NoResultFound:
|
|
raise exception.ResourceNotFound(name=model.__name__, id=value)
|
|
|
|
return obj
|
|
|
|
@staticmethod
|
|
def _update(model, id_, values):
|
|
session = get_session()
|
|
with session.begin():
|
|
query = model_query(model, session=session)
|
|
query = add_identity_filter(query, id_)
|
|
try:
|
|
ref = query.with_for_update().one()
|
|
except exc.NoResultFound:
|
|
raise exception.ResourceNotFound(name=model.__name__, id=id_)
|
|
|
|
ref.update(values)
|
|
return ref
|
|
|
|
@staticmethod
|
|
def _soft_delete(model, id_):
|
|
session = get_session()
|
|
with session.begin():
|
|
query = model_query(model, session=session)
|
|
query = add_identity_filter(query, id_)
|
|
try:
|
|
row = query.one()
|
|
except exc.NoResultFound:
|
|
raise exception.ResourceNotFound(name=model.__name__, id=id_)
|
|
|
|
row.soft_delete(session)
|
|
|
|
return row
|
|
|
|
@staticmethod
|
|
def _destroy(model, id_):
|
|
session = get_session()
|
|
with session.begin():
|
|
query = model_query(model, session=session)
|
|
query = add_identity_filter(query, id_)
|
|
|
|
try:
|
|
query.one()
|
|
except exc.NoResultFound:
|
|
raise exception.ResourceNotFound(name=model.__name__, id=id_)
|
|
|
|
query.delete()
|
|
|
|
def _get_model_list(self, model, add_filters_func, context, filters=None,
|
|
limit=None, marker=None, sort_key=None, sort_dir=None,
|
|
eager=False):
|
|
query = model_query(model)
|
|
if eager:
|
|
query = self._set_eager_options(model, query)
|
|
query = add_filters_func(query, filters)
|
|
if not context.show_deleted:
|
|
query = query.filter(model.deleted_at.is_(None))
|
|
return _paginate_query(model, limit, marker,
|
|
sort_key, sort_dir, query)
|
|
|
|
# NOTE(erakli): _add_..._filters methods should be refactored to have same
|
|
# content. join_fieldmap should be filled with JoinMap instead of dict
|
|
|
|
def _add_goals_filters(self, query, filters):
|
|
if filters is None:
|
|
filters = {}
|
|
|
|
plain_fields = ['uuid', 'name', 'display_name']
|
|
|
|
return self._add_filters(
|
|
query=query, model=models.Goal, filters=filters,
|
|
plain_fields=plain_fields)
|
|
|
|
def _add_strategies_filters(self, query, filters):
|
|
plain_fields = ['uuid', 'name', 'display_name', 'goal_id']
|
|
join_fieldmap = JoinMap(
|
|
goal_uuid=NaturalJoinFilter(
|
|
join_fieldname="uuid", join_model=models.Goal),
|
|
goal_name=NaturalJoinFilter(
|
|
join_fieldname="name", join_model=models.Goal))
|
|
return self._add_filters(
|
|
query=query, model=models.Strategy, filters=filters,
|
|
plain_fields=plain_fields, join_fieldmap=join_fieldmap)
|
|
|
|
def _add_audit_templates_filters(self, query, filters):
|
|
if filters is None:
|
|
filters = {}
|
|
|
|
plain_fields = ['uuid', 'name', 'goal_id', 'strategy_id']
|
|
join_fieldmap = JoinMap(
|
|
goal_uuid=NaturalJoinFilter(
|
|
join_fieldname="uuid", join_model=models.Goal),
|
|
goal_name=NaturalJoinFilter(
|
|
join_fieldname="name", join_model=models.Goal),
|
|
strategy_uuid=NaturalJoinFilter(
|
|
join_fieldname="uuid", join_model=models.Strategy),
|
|
strategy_name=NaturalJoinFilter(
|
|
join_fieldname="name", join_model=models.Strategy),
|
|
)
|
|
|
|
return self._add_filters(
|
|
query=query, model=models.AuditTemplate, filters=filters,
|
|
plain_fields=plain_fields, join_fieldmap=join_fieldmap)
|
|
|
|
def _add_audits_filters(self, query, filters):
|
|
if filters is None:
|
|
filters = {}
|
|
|
|
plain_fields = ['uuid', 'audit_type', 'state', 'goal_id',
|
|
'strategy_id', 'hostname']
|
|
join_fieldmap = {
|
|
'goal_uuid': ("uuid", models.Goal),
|
|
'goal_name': ("name", models.Goal),
|
|
'strategy_uuid': ("uuid", models.Strategy),
|
|
'strategy_name': ("name", models.Strategy),
|
|
}
|
|
|
|
return self._add_filters(
|
|
query=query, model=models.Audit, filters=filters,
|
|
plain_fields=plain_fields, join_fieldmap=join_fieldmap)
|
|
|
|
def _add_action_plans_filters(self, query, filters):
|
|
if filters is None:
|
|
filters = {}
|
|
|
|
plain_fields = ['uuid', 'state', 'audit_id', 'strategy_id']
|
|
join_fieldmap = JoinMap(
|
|
audit_uuid=NaturalJoinFilter(
|
|
join_fieldname="uuid", join_model=models.Audit),
|
|
strategy_uuid=NaturalJoinFilter(
|
|
join_fieldname="uuid", join_model=models.Strategy),
|
|
strategy_name=NaturalJoinFilter(
|
|
join_fieldname="name", join_model=models.Strategy),
|
|
)
|
|
|
|
return self._add_filters(
|
|
query=query, model=models.ActionPlan, filters=filters,
|
|
plain_fields=plain_fields, join_fieldmap=join_fieldmap)
|
|
|
|
def _add_actions_filters(self, query, filters):
|
|
if filters is None:
|
|
filters = {}
|
|
|
|
plain_fields = ['uuid', 'state', 'action_plan_id']
|
|
join_fieldmap = {
|
|
'action_plan_uuid': ("uuid", models.ActionPlan),
|
|
}
|
|
|
|
query = self._add_filters(
|
|
query=query, model=models.Action, filters=filters,
|
|
plain_fields=plain_fields, join_fieldmap=join_fieldmap)
|
|
|
|
if 'audit_uuid' in filters:
|
|
stmt = model_query(models.ActionPlan).join(
|
|
models.Audit,
|
|
models.Audit.id == models.ActionPlan.audit_id)\
|
|
.filter_by(uuid=filters['audit_uuid']).subquery()
|
|
query = query.filter_by(action_plan_id=stmt.c.id)
|
|
|
|
return query
|
|
|
|
def _add_efficacy_indicators_filters(self, query, filters):
|
|
if filters is None:
|
|
filters = {}
|
|
|
|
plain_fields = ['uuid', 'name', 'unit', 'schema', 'action_plan_id']
|
|
join_fieldmap = JoinMap(
|
|
action_plan_uuid=NaturalJoinFilter(
|
|
join_fieldname="uuid", join_model=models.ActionPlan),
|
|
)
|
|
|
|
return self._add_filters(
|
|
query=query, model=models.EfficacyIndicator, filters=filters,
|
|
plain_fields=plain_fields, join_fieldmap=join_fieldmap)
|
|
|
|
def _add_scoring_engine_filters(self, query, filters):
|
|
if filters is None:
|
|
filters = {}
|
|
|
|
plain_fields = ['id', 'description']
|
|
|
|
return self._add_filters(
|
|
query=query, model=models.ScoringEngine, filters=filters,
|
|
plain_fields=plain_fields)
|
|
|
|
def _add_action_descriptions_filters(self, query, filters):
|
|
if not filters:
|
|
filters = {}
|
|
|
|
plain_fields = ['id', 'action_type']
|
|
|
|
return self._add_filters(
|
|
query=query, model=models.ActionDescription, filters=filters,
|
|
plain_fields=plain_fields)
|
|
|
|
def _add_services_filters(self, query, filters):
|
|
if not filters:
|
|
filters = {}
|
|
|
|
plain_fields = ['id', 'name', 'host']
|
|
|
|
return self._add_filters(
|
|
query=query, model=models.Service, filters=filters,
|
|
plain_fields=plain_fields)
|
|
|
|
# ### GOALS ### #
|
|
|
|
def get_goal_list(self, *args, **kwargs):
|
|
return self._get_model_list(models.Goal,
|
|
self._add_goals_filters,
|
|
*args, **kwargs)
|
|
|
|
def create_goal(self, values):
|
|
# ensure defaults are present for new goals
|
|
if not values.get('uuid'):
|
|
values['uuid'] = utils.generate_uuid()
|
|
|
|
try:
|
|
goal = self._create(models.Goal, values)
|
|
except db_exc.DBDuplicateEntry:
|
|
raise exception.GoalAlreadyExists(uuid=values['uuid'])
|
|
return goal
|
|
|
|
def _get_goal(self, context, fieldname, value, eager):
|
|
try:
|
|
return self._get(context, model=models.Goal,
|
|
fieldname=fieldname, value=value, eager=eager)
|
|
except exception.ResourceNotFound:
|
|
raise exception.GoalNotFound(goal=value)
|
|
|
|
def get_goal_by_id(self, context, goal_id, eager=False):
|
|
return self._get_goal(
|
|
context, fieldname="id", value=goal_id, eager=eager)
|
|
|
|
def get_goal_by_uuid(self, context, goal_uuid, eager=False):
|
|
return self._get_goal(
|
|
context, fieldname="uuid", value=goal_uuid, eager=eager)
|
|
|
|
def get_goal_by_name(self, context, goal_name, eager=False):
|
|
return self._get_goal(
|
|
context, fieldname="name", value=goal_name, eager=eager)
|
|
|
|
def destroy_goal(self, goal_id):
|
|
try:
|
|
return self._destroy(models.Goal, goal_id)
|
|
except exception.ResourceNotFound:
|
|
raise exception.GoalNotFound(goal=goal_id)
|
|
|
|
def update_goal(self, goal_id, values):
|
|
if 'uuid' in values:
|
|
raise exception.Invalid(
|
|
message=_("Cannot overwrite UUID for an existing Goal."))
|
|
|
|
try:
|
|
return self._update(models.Goal, goal_id, values)
|
|
except exception.ResourceNotFound:
|
|
raise exception.GoalNotFound(goal=goal_id)
|
|
|
|
def soft_delete_goal(self, goal_id):
|
|
try:
|
|
return self._soft_delete(models.Goal, goal_id)
|
|
except exception.ResourceNotFound:
|
|
raise exception.GoalNotFound(goal=goal_id)
|
|
|
|
# ### STRATEGIES ### #
|
|
|
|
def get_strategy_list(self, *args, **kwargs):
|
|
return self._get_model_list(models.Strategy,
|
|
self._add_strategies_filters,
|
|
*args, **kwargs)
|
|
|
|
def create_strategy(self, values):
|
|
# ensure defaults are present for new strategies
|
|
if not values.get('uuid'):
|
|
values['uuid'] = utils.generate_uuid()
|
|
|
|
try:
|
|
strategy = self._create(models.Strategy, values)
|
|
except db_exc.DBDuplicateEntry:
|
|
raise exception.StrategyAlreadyExists(uuid=values['uuid'])
|
|
return strategy
|
|
|
|
def _get_strategy(self, context, fieldname, value, eager):
|
|
try:
|
|
return self._get(context, model=models.Strategy,
|
|
fieldname=fieldname, value=value, eager=eager)
|
|
except exception.ResourceNotFound:
|
|
raise exception.StrategyNotFound(strategy=value)
|
|
|
|
def get_strategy_by_id(self, context, strategy_id, eager=False):
|
|
return self._get_strategy(
|
|
context, fieldname="id", value=strategy_id, eager=eager)
|
|
|
|
def get_strategy_by_uuid(self, context, strategy_uuid, eager=False):
|
|
return self._get_strategy(
|
|
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):
|
|
try:
|
|
return self._destroy(models.Strategy, strategy_id)
|
|
except exception.ResourceNotFound:
|
|
raise exception.StrategyNotFound(strategy=strategy_id)
|
|
|
|
def update_strategy(self, strategy_id, values):
|
|
if 'uuid' in values:
|
|
raise exception.Invalid(
|
|
message=_("Cannot overwrite UUID for an existing Strategy."))
|
|
|
|
try:
|
|
return self._update(models.Strategy, strategy_id, values)
|
|
except exception.ResourceNotFound:
|
|
raise exception.StrategyNotFound(strategy=strategy_id)
|
|
|
|
def soft_delete_strategy(self, strategy_id):
|
|
try:
|
|
return self._soft_delete(models.Strategy, strategy_id)
|
|
except exception.ResourceNotFound:
|
|
raise exception.StrategyNotFound(strategy=strategy_id)
|
|
|
|
# ### AUDIT TEMPLATES ### #
|
|
|
|
def get_audit_template_list(self, *args, **kwargs):
|
|
return self._get_model_list(models.AuditTemplate,
|
|
self._add_audit_templates_filters,
|
|
*args, **kwargs)
|
|
|
|
def create_audit_template(self, values):
|
|
# ensure defaults are present for new audit_templates
|
|
if not values.get('uuid'):
|
|
values['uuid'] = utils.generate_uuid()
|
|
|
|
query = model_query(models.AuditTemplate)
|
|
query = query.filter_by(name=values.get('name'),
|
|
deleted_at=None)
|
|
|
|
if len(query.all()) > 0:
|
|
raise exception.AuditTemplateAlreadyExists(
|
|
audit_template=values['name'])
|
|
|
|
try:
|
|
audit_template = self._create(models.AuditTemplate, values)
|
|
except db_exc.DBDuplicateEntry:
|
|
raise exception.AuditTemplateAlreadyExists(
|
|
audit_template=values['name'])
|
|
return audit_template
|
|
|
|
def _get_audit_template(self, context, fieldname, value, eager):
|
|
try:
|
|
return self._get(context, model=models.AuditTemplate,
|
|
fieldname=fieldname, value=value, eager=eager)
|
|
except exception.ResourceNotFound:
|
|
raise exception.AuditTemplateNotFound(audit_template=value)
|
|
|
|
def get_audit_template_by_id(self, context, audit_template_id,
|
|
eager=False):
|
|
return self._get_audit_template(
|
|
context, fieldname="id", value=audit_template_id, eager=eager)
|
|
|
|
def get_audit_template_by_uuid(self, context, audit_template_uuid,
|
|
eager=False):
|
|
return self._get_audit_template(
|
|
context, fieldname="uuid", value=audit_template_uuid, eager=eager)
|
|
|
|
def get_audit_template_by_name(self, context, audit_template_name,
|
|
eager=False):
|
|
return self._get_audit_template(
|
|
context, fieldname="name", value=audit_template_name, eager=eager)
|
|
|
|
def destroy_audit_template(self, audit_template_id):
|
|
try:
|
|
return self._destroy(models.AuditTemplate, audit_template_id)
|
|
except exception.ResourceNotFound:
|
|
raise exception.AuditTemplateNotFound(
|
|
audit_template=audit_template_id)
|
|
|
|
def update_audit_template(self, audit_template_id, values):
|
|
if 'uuid' in values:
|
|
raise exception.Invalid(
|
|
message=_("Cannot overwrite UUID for an existing "
|
|
"Audit Template."))
|
|
try:
|
|
return self._update(
|
|
models.AuditTemplate, audit_template_id, values)
|
|
except exception.ResourceNotFound:
|
|
raise exception.AuditTemplateNotFound(
|
|
audit_template=audit_template_id)
|
|
|
|
def soft_delete_audit_template(self, audit_template_id):
|
|
try:
|
|
return self._soft_delete(models.AuditTemplate, audit_template_id)
|
|
except exception.ResourceNotFound:
|
|
raise exception.AuditTemplateNotFound(
|
|
audit_template=audit_template_id)
|
|
|
|
# ### AUDITS ### #
|
|
|
|
def get_audit_list(self, *args, **kwargs):
|
|
return self._get_model_list(models.Audit,
|
|
self._add_audits_filters,
|
|
*args, **kwargs)
|
|
|
|
def create_audit(self, values):
|
|
# ensure defaults are present for new audits
|
|
if not values.get('uuid'):
|
|
values['uuid'] = utils.generate_uuid()
|
|
|
|
query = model_query(models.Audit)
|
|
query = query.filter_by(name=values.get('name'),
|
|
deleted_at=None)
|
|
|
|
if len(query.all()) > 0:
|
|
raise exception.AuditAlreadyExists(
|
|
audit=values['name'])
|
|
|
|
if values.get('state') is None:
|
|
values['state'] = objects.audit.State.PENDING
|
|
|
|
if not values.get('auto_trigger'):
|
|
values['auto_trigger'] = False
|
|
|
|
try:
|
|
audit = self._create(models.Audit, values)
|
|
except db_exc.DBDuplicateEntry:
|
|
raise exception.AuditAlreadyExists(audit=values['uuid'])
|
|
return audit
|
|
|
|
def _get_audit(self, context, fieldname, value, eager):
|
|
try:
|
|
return self._get(context, model=models.Audit,
|
|
fieldname=fieldname, value=value, eager=eager)
|
|
except exception.ResourceNotFound:
|
|
raise exception.AuditNotFound(audit=value)
|
|
|
|
def get_audit_by_id(self, context, audit_id, eager=False):
|
|
return self._get_audit(
|
|
context, fieldname="id", value=audit_id, eager=eager)
|
|
|
|
def get_audit_by_uuid(self, context, audit_uuid, eager=False):
|
|
return self._get_audit(
|
|
context, fieldname="uuid", value=audit_uuid, eager=eager)
|
|
|
|
def get_audit_by_name(self, context, audit_name, eager=False):
|
|
return self._get_audit(
|
|
context, fieldname="name", value=audit_name, eager=eager)
|
|
|
|
def destroy_audit(self, audit_id):
|
|
def is_audit_referenced(session, audit_id):
|
|
"""Checks whether the audit is referenced by action_plan(s)."""
|
|
query = model_query(models.ActionPlan, session=session)
|
|
query = self._add_action_plans_filters(
|
|
query, {'audit_id': audit_id})
|
|
return query.count() != 0
|
|
|
|
session = get_session()
|
|
with session.begin():
|
|
query = model_query(models.Audit, session=session)
|
|
query = add_identity_filter(query, audit_id)
|
|
|
|
try:
|
|
audit_ref = query.one()
|
|
except exc.NoResultFound:
|
|
raise exception.AuditNotFound(audit=audit_id)
|
|
|
|
if is_audit_referenced(session, audit_ref['id']):
|
|
raise exception.AuditReferenced(audit=audit_id)
|
|
|
|
query.delete()
|
|
|
|
def update_audit(self, audit_id, values):
|
|
if 'uuid' in values:
|
|
raise exception.Invalid(
|
|
message=_("Cannot overwrite UUID for an existing "
|
|
"Audit."))
|
|
|
|
try:
|
|
return self._update(models.Audit, audit_id, values)
|
|
except exception.ResourceNotFound:
|
|
raise exception.AuditNotFound(audit=audit_id)
|
|
|
|
def soft_delete_audit(self, audit_id):
|
|
try:
|
|
return self._soft_delete(models.Audit, audit_id)
|
|
except exception.ResourceNotFound:
|
|
raise exception.AuditNotFound(audit=audit_id)
|
|
|
|
# ### ACTIONS ### #
|
|
|
|
def get_action_list(self, *args, **kwargs):
|
|
return self._get_model_list(models.Action,
|
|
self._add_actions_filters,
|
|
*args, **kwargs)
|
|
|
|
def create_action(self, values):
|
|
# ensure defaults are present for new actions
|
|
if not values.get('uuid'):
|
|
values['uuid'] = utils.generate_uuid()
|
|
|
|
if values.get('state') is None:
|
|
values['state'] = objects.action.State.PENDING
|
|
|
|
try:
|
|
action = self._create(models.Action, values)
|
|
except db_exc.DBDuplicateEntry:
|
|
raise exception.ActionAlreadyExists(uuid=values['uuid'])
|
|
return action
|
|
|
|
def _get_action(self, context, fieldname, value, eager):
|
|
try:
|
|
return self._get(context, model=models.Action,
|
|
fieldname=fieldname, value=value, eager=eager)
|
|
except exception.ResourceNotFound:
|
|
raise exception.ActionNotFound(action=value)
|
|
|
|
def get_action_by_id(self, context, action_id, eager=False):
|
|
return self._get_action(
|
|
context, fieldname="id", value=action_id, eager=eager)
|
|
|
|
def get_action_by_uuid(self, context, action_uuid, eager=False):
|
|
return self._get_action(
|
|
context, fieldname="uuid", value=action_uuid, eager=eager)
|
|
|
|
def destroy_action(self, action_id):
|
|
session = get_session()
|
|
with session.begin():
|
|
query = model_query(models.Action, session=session)
|
|
query = add_identity_filter(query, action_id)
|
|
count = query.delete()
|
|
if count != 1:
|
|
raise exception.ActionNotFound(action_id)
|
|
|
|
def update_action(self, action_id, values):
|
|
# NOTE(dtantsur): this can lead to very strange errors
|
|
if 'uuid' in values:
|
|
raise exception.Invalid(
|
|
message=_("Cannot overwrite UUID for an existing Action."))
|
|
|
|
return self._do_update_action(action_id, values)
|
|
|
|
@staticmethod
|
|
def _do_update_action(action_id, values):
|
|
session = get_session()
|
|
with session.begin():
|
|
query = model_query(models.Action, session=session)
|
|
query = add_identity_filter(query, action_id)
|
|
try:
|
|
ref = query.with_for_update().one()
|
|
except exc.NoResultFound:
|
|
raise exception.ActionNotFound(action=action_id)
|
|
|
|
ref.update(values)
|
|
return ref
|
|
|
|
def soft_delete_action(self, action_id):
|
|
try:
|
|
return self._soft_delete(models.Action, action_id)
|
|
except exception.ResourceNotFound:
|
|
raise exception.ActionNotFound(action=action_id)
|
|
|
|
# ### ACTION PLANS ### #
|
|
|
|
def get_action_plan_list(self, *args, **kwargs):
|
|
return self._get_model_list(models.ActionPlan,
|
|
self._add_action_plans_filters,
|
|
*args, **kwargs)
|
|
|
|
def create_action_plan(self, values):
|
|
# ensure defaults are present for new audits
|
|
if not values.get('uuid'):
|
|
values['uuid'] = utils.generate_uuid()
|
|
|
|
try:
|
|
action_plan = self._create(models.ActionPlan, values)
|
|
except db_exc.DBDuplicateEntry:
|
|
raise exception.ActionPlanAlreadyExists(uuid=values['uuid'])
|
|
return action_plan
|
|
|
|
def _get_action_plan(self, context, fieldname, value, eager):
|
|
try:
|
|
return self._get(context, model=models.ActionPlan,
|
|
fieldname=fieldname, value=value, eager=eager)
|
|
except exception.ResourceNotFound:
|
|
raise exception.ActionPlanNotFound(action_plan=value)
|
|
|
|
def get_action_plan_by_id(self, context, action_plan_id, eager=False):
|
|
return self._get_action_plan(
|
|
context, fieldname="id", value=action_plan_id, eager=eager)
|
|
|
|
def get_action_plan_by_uuid(self, context, action_plan_uuid, eager=False):
|
|
return self._get_action_plan(
|
|
context, fieldname="uuid", value=action_plan_uuid, eager=eager)
|
|
|
|
def destroy_action_plan(self, action_plan_id):
|
|
def is_action_plan_referenced(session, action_plan_id):
|
|
"""Checks whether the action_plan is referenced by action(s)."""
|
|
query = model_query(models.Action, session=session)
|
|
query = self._add_actions_filters(
|
|
query, {'action_plan_id': action_plan_id})
|
|
return query.count() != 0
|
|
|
|
session = get_session()
|
|
with session.begin():
|
|
query = model_query(models.ActionPlan, session=session)
|
|
query = add_identity_filter(query, action_plan_id)
|
|
|
|
try:
|
|
action_plan_ref = query.one()
|
|
except exc.NoResultFound:
|
|
raise exception.ActionPlanNotFound(action_plan=action_plan_id)
|
|
|
|
if is_action_plan_referenced(session, action_plan_ref['id']):
|
|
raise exception.ActionPlanReferenced(
|
|
action_plan=action_plan_id)
|
|
|
|
query.delete()
|
|
|
|
def update_action_plan(self, action_plan_id, values):
|
|
if 'uuid' in values:
|
|
raise exception.Invalid(
|
|
message=_("Cannot overwrite UUID for an existing "
|
|
"Action Plan."))
|
|
|
|
return self._do_update_action_plan(action_plan_id, values)
|
|
|
|
@staticmethod
|
|
def _do_update_action_plan(action_plan_id, values):
|
|
session = get_session()
|
|
with session.begin():
|
|
query = model_query(models.ActionPlan, session=session)
|
|
query = add_identity_filter(query, action_plan_id)
|
|
try:
|
|
ref = query.with_for_update().one()
|
|
except exc.NoResultFound:
|
|
raise exception.ActionPlanNotFound(action_plan=action_plan_id)
|
|
|
|
ref.update(values)
|
|
return ref
|
|
|
|
def soft_delete_action_plan(self, action_plan_id):
|
|
try:
|
|
return self._soft_delete(models.ActionPlan, action_plan_id)
|
|
except exception.ResourceNotFound:
|
|
raise exception.ActionPlanNotFound(action_plan=action_plan_id)
|
|
|
|
# ### EFFICACY INDICATORS ### #
|
|
|
|
def get_efficacy_indicator_list(self, *args, **kwargs):
|
|
return self._get_model_list(models.EfficacyIndicator,
|
|
self._add_efficacy_indicators_filters,
|
|
*args, **kwargs)
|
|
|
|
def create_efficacy_indicator(self, values):
|
|
# ensure defaults are present for new efficacy indicators
|
|
if not values.get('uuid'):
|
|
values['uuid'] = utils.generate_uuid()
|
|
|
|
try:
|
|
efficacy_indicator = self._create(models.EfficacyIndicator, values)
|
|
except db_exc.DBDuplicateEntry:
|
|
raise exception.EfficacyIndicatorAlreadyExists(uuid=values['uuid'])
|
|
return efficacy_indicator
|
|
|
|
def _get_efficacy_indicator(self, context, fieldname, value, eager):
|
|
try:
|
|
return self._get(context, model=models.EfficacyIndicator,
|
|
fieldname=fieldname, value=value, eager=eager)
|
|
except exception.ResourceNotFound:
|
|
raise exception.EfficacyIndicatorNotFound(efficacy_indicator=value)
|
|
|
|
def get_efficacy_indicator_by_id(self, context, efficacy_indicator_id,
|
|
eager=False):
|
|
return self._get_efficacy_indicator(
|
|
context, fieldname="id",
|
|
value=efficacy_indicator_id, eager=eager)
|
|
|
|
def get_efficacy_indicator_by_uuid(self, context, efficacy_indicator_uuid,
|
|
eager=False):
|
|
return self._get_efficacy_indicator(
|
|
context, fieldname="uuid",
|
|
value=efficacy_indicator_uuid, eager=eager)
|
|
|
|
def get_efficacy_indicator_by_name(self, context, efficacy_indicator_name,
|
|
eager=False):
|
|
return self._get_efficacy_indicator(
|
|
context, fieldname="name",
|
|
value=efficacy_indicator_name, eager=eager)
|
|
|
|
def update_efficacy_indicator(self, efficacy_indicator_id, values):
|
|
if 'uuid' in values:
|
|
raise exception.Invalid(
|
|
message=_("Cannot overwrite UUID for an existing "
|
|
"efficacy indicator."))
|
|
|
|
try:
|
|
return self._update(
|
|
models.EfficacyIndicator, efficacy_indicator_id, values)
|
|
except exception.ResourceNotFound:
|
|
raise exception.EfficacyIndicatorNotFound(
|
|
efficacy_indicator=efficacy_indicator_id)
|
|
|
|
def soft_delete_efficacy_indicator(self, efficacy_indicator_id):
|
|
try:
|
|
return self._soft_delete(
|
|
models.EfficacyIndicator, efficacy_indicator_id)
|
|
except exception.ResourceNotFound:
|
|
raise exception.EfficacyIndicatorNotFound(
|
|
efficacy_indicator=efficacy_indicator_id)
|
|
|
|
def destroy_efficacy_indicator(self, efficacy_indicator_id):
|
|
try:
|
|
return self._destroy(
|
|
models.EfficacyIndicator, efficacy_indicator_id)
|
|
except exception.ResourceNotFound:
|
|
raise exception.EfficacyIndicatorNotFound(
|
|
efficacy_indicator=efficacy_indicator_id)
|
|
|
|
# ### SCORING ENGINES ### #
|
|
|
|
def get_scoring_engine_list(self, *args, **kwargs):
|
|
return self._get_model_list(models.ScoringEngine,
|
|
self._add_scoring_engine_filters,
|
|
*args, **kwargs)
|
|
|
|
def create_scoring_engine(self, values):
|
|
# ensure defaults are present for new scoring engines
|
|
if not values.get('uuid'):
|
|
values['uuid'] = utils.generate_uuid()
|
|
|
|
try:
|
|
scoring_engine = self._create(models.ScoringEngine, values)
|
|
except db_exc.DBDuplicateEntry:
|
|
raise exception.ScoringEngineAlreadyExists(uuid=values['uuid'])
|
|
return scoring_engine
|
|
|
|
def _get_scoring_engine(self, context, fieldname, value, eager):
|
|
try:
|
|
return self._get(context, model=models.ScoringEngine,
|
|
fieldname=fieldname, value=value, eager=eager)
|
|
except exception.ResourceNotFound:
|
|
raise exception.ScoringEngineNotFound(scoring_engine=value)
|
|
|
|
def get_scoring_engine_by_id(self, context, scoring_engine_id,
|
|
eager=False):
|
|
return self._get_scoring_engine(
|
|
context, fieldname="id", value=scoring_engine_id, eager=eager)
|
|
|
|
def get_scoring_engine_by_uuid(self, context, scoring_engine_uuid,
|
|
eager=False):
|
|
return self._get_scoring_engine(
|
|
context, fieldname="uuid", value=scoring_engine_uuid, eager=eager)
|
|
|
|
def get_scoring_engine_by_name(self, context, scoring_engine_name,
|
|
eager=False):
|
|
return self._get_scoring_engine(
|
|
context, fieldname="name", value=scoring_engine_name, eager=eager)
|
|
|
|
def destroy_scoring_engine(self, scoring_engine_id):
|
|
try:
|
|
return self._destroy(models.ScoringEngine, scoring_engine_id)
|
|
except exception.ResourceNotFound:
|
|
raise exception.ScoringEngineNotFound(
|
|
scoring_engine=scoring_engine_id)
|
|
|
|
def update_scoring_engine(self, scoring_engine_id, values):
|
|
if 'uuid' in values:
|
|
raise exception.Invalid(
|
|
message=_("Cannot overwrite UUID for an existing "
|
|
"Scoring Engine."))
|
|
|
|
try:
|
|
return self._update(
|
|
models.ScoringEngine, scoring_engine_id, values)
|
|
except exception.ResourceNotFound:
|
|
raise exception.ScoringEngineNotFound(
|
|
scoring_engine=scoring_engine_id)
|
|
|
|
def soft_delete_scoring_engine(self, scoring_engine_id):
|
|
try:
|
|
return self._soft_delete(
|
|
models.ScoringEngine, scoring_engine_id)
|
|
except exception.ResourceNotFound:
|
|
raise exception.ScoringEngineNotFound(
|
|
scoring_engine=scoring_engine_id)
|
|
|
|
# ### SERVICES ### #
|
|
|
|
def get_service_list(self, *args, **kwargs):
|
|
return self._get_model_list(models.Service,
|
|
self._add_services_filters,
|
|
*args, **kwargs)
|
|
|
|
def create_service(self, values):
|
|
try:
|
|
service = self._create(models.Service, values)
|
|
except db_exc.DBDuplicateEntry:
|
|
raise exception.ServiceAlreadyExists(name=values['name'],
|
|
host=values['host'])
|
|
return service
|
|
|
|
def _get_service(self, context, fieldname, value, eager):
|
|
try:
|
|
return self._get(context, model=models.Service,
|
|
fieldname=fieldname, value=value, eager=eager)
|
|
except exception.ResourceNotFound:
|
|
raise exception.ServiceNotFound(service=value)
|
|
|
|
def get_service_by_id(self, context, service_id, eager=False):
|
|
return self._get_service(
|
|
context, fieldname="id", value=service_id, eager=eager)
|
|
|
|
def get_service_by_name(self, context, service_name, eager=False):
|
|
return self._get_service(
|
|
context, fieldname="name", value=service_name, eager=eager)
|
|
|
|
def destroy_service(self, service_id):
|
|
try:
|
|
return self._destroy(models.Service, service_id)
|
|
except exception.ResourceNotFound:
|
|
raise exception.ServiceNotFound(service=service_id)
|
|
|
|
def update_service(self, service_id, values):
|
|
try:
|
|
return self._update(models.Service, service_id, values)
|
|
except exception.ResourceNotFound:
|
|
raise exception.ServiceNotFound(service=service_id)
|
|
|
|
def soft_delete_service(self, service_id):
|
|
try:
|
|
return self._soft_delete(models.Service, service_id)
|
|
except exception.ResourceNotFound:
|
|
raise exception.ServiceNotFound(service=service_id)
|
|
|
|
# ### ACTION_DESCRIPTIONS ### #
|
|
|
|
def get_action_description_list(self, *args, **kwargs):
|
|
return self._get_model_list(models.ActionDescription,
|
|
self._add_action_descriptions_filters,
|
|
*args, **kwargs)
|
|
|
|
def create_action_description(self, values):
|
|
try:
|
|
action_description = self._create(models.ActionDescription, values)
|
|
except db_exc.DBDuplicateEntry:
|
|
raise exception.ActionDescriptionAlreadyExists(
|
|
action_type=values['action_type'])
|
|
return action_description
|
|
|
|
def _get_action_description(self, context, fieldname, value, eager):
|
|
try:
|
|
return self._get(context, model=models.ActionDescription,
|
|
fieldname=fieldname, value=value, eager=eager)
|
|
except exception.ResourceNotFound:
|
|
raise exception.ActionDescriptionNotFound(action_id=value)
|
|
|
|
def get_action_description_by_id(self, context,
|
|
action_id, eager=False):
|
|
return self._get_action_description(
|
|
context, fieldname="id", value=action_id, eager=eager)
|
|
|
|
def get_action_description_by_type(self, context,
|
|
action_type, eager=False):
|
|
return self._get_action_description(
|
|
context, fieldname="action_type", value=action_type, eager=eager)
|
|
|
|
def destroy_action_description(self, action_id):
|
|
try:
|
|
return self._destroy(models.ActionDescription, action_id)
|
|
except exception.ResourceNotFound:
|
|
raise exception.ActionDescriptionNotFound(
|
|
action_id=action_id)
|
|
|
|
def update_action_description(self, action_id, values):
|
|
try:
|
|
return self._update(models.ActionDescription,
|
|
action_id, values)
|
|
except exception.ResourceNotFound:
|
|
raise exception.ActionDescriptionNotFound(
|
|
action_id=action_id)
|
|
|
|
def soft_delete_action_description(self, action_id):
|
|
try:
|
|
return self._soft_delete(models.ActionDescription, action_id)
|
|
except exception.ResourceNotFound:
|
|
raise exception.ActionDescriptionNotFound(
|
|
action_id=action_id)
|