dynamic action description
Add a new table to save the mapping Add logic to update the table when action loading Add logic to show the action description Change-Id: Ia008a8715bcc666ab0fefe444ef612394c775e91 Implements: blueprint dynamic-action-description
This commit is contained in:
parent
0ddfa278ef
commit
a24b7f0b61
@ -0,0 +1,4 @@
|
||||
---
|
||||
features:
|
||||
- Add description property for dynamic action. Admin can see detail information
|
||||
of any specify action.
|
@ -118,6 +118,9 @@ class Action(base.APIBase):
|
||||
action_type = wtypes.text
|
||||
"""Action type"""
|
||||
|
||||
description = wtypes.text
|
||||
"""Action description"""
|
||||
|
||||
input_parameters = types.jsontype
|
||||
"""One or more key/value pairs """
|
||||
|
||||
@ -141,6 +144,7 @@ class Action(base.APIBase):
|
||||
setattr(self, field, kwargs.get(field, wtypes.Unset))
|
||||
|
||||
self.fields.append('action_plan_id')
|
||||
self.fields.append('description')
|
||||
setattr(self, 'action_plan_uuid', kwargs.get('action_plan_id',
|
||||
wtypes.Unset))
|
||||
|
||||
@ -162,6 +166,14 @@ class Action(base.APIBase):
|
||||
@classmethod
|
||||
def convert_with_links(cls, action, expand=True):
|
||||
action = Action(**action.as_dict())
|
||||
try:
|
||||
obj_action_desc = objects.ActionDescription.get_by_type(
|
||||
pecan.request.context, action.action_type)
|
||||
description = obj_action_desc.description
|
||||
except exception.ActionDescriptionNotFound:
|
||||
description = ""
|
||||
setattr(action, 'description', description)
|
||||
|
||||
return cls._convert_with_links(action, pecan.request.host_url, expand)
|
||||
|
||||
@classmethod
|
||||
|
44
watcher/applier/sync.py
Normal file
44
watcher/applier/sync.py
Normal file
@ -0,0 +1,44 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2017 ZTE
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
from watcher.applier.loading import default
|
||||
from watcher.common import context
|
||||
from watcher.common import exception
|
||||
from watcher import objects
|
||||
|
||||
|
||||
class Syncer(object):
|
||||
"""Syncs all available actions with the Watcher DB"""
|
||||
|
||||
def sync(self):
|
||||
ctx = context.make_context()
|
||||
action_loader = default.DefaultActionLoader()
|
||||
available_actions = action_loader.list_available()
|
||||
for action_type in available_actions.keys():
|
||||
load_action = action_loader.load(action_type)
|
||||
load_description = load_action.get_description()
|
||||
try:
|
||||
action_desc = objects.ActionDescription.get_by_type(
|
||||
ctx, action_type)
|
||||
if action_desc.description != load_description:
|
||||
action_desc.description = load_description
|
||||
action_desc.save()
|
||||
except exception.ActionDescriptionNotFound:
|
||||
obj_action_desc = objects.ActionDescription(ctx)
|
||||
obj_action_desc.action_type = action_type
|
||||
obj_action_desc.description = load_description
|
||||
obj_action_desc.create()
|
@ -23,6 +23,7 @@ import sys
|
||||
from oslo_log import log as logging
|
||||
|
||||
from watcher.applier import manager
|
||||
from watcher.applier import sync
|
||||
from watcher.common import service as watcher_service
|
||||
from watcher import conf
|
||||
|
||||
@ -37,6 +38,9 @@ def main():
|
||||
|
||||
applier_service = watcher_service.Service(manager.ApplierManager)
|
||||
|
||||
syncer = sync.Syncer()
|
||||
syncer.sync()
|
||||
|
||||
# Only 1 process
|
||||
launcher = watcher_service.launch(CONF, applier_service)
|
||||
launcher.wait()
|
||||
|
@ -426,6 +426,15 @@ class CronFormatIsInvalid(WatcherException):
|
||||
msg_fmt = _("Provided cron is invalid: %(message)s")
|
||||
|
||||
|
||||
class ActionDescriptionAlreadyExists(Conflict):
|
||||
msg_fmt = _("An action description with type %(action_type)s is "
|
||||
"already exist.")
|
||||
|
||||
|
||||
class ActionDescriptionNotFound(ResourceNotFound):
|
||||
msg_fmt = _("The action description %(action_id)s cannot be found.")
|
||||
|
||||
|
||||
# Model
|
||||
|
||||
class ComputeResourceNotFound(WatcherException):
|
||||
|
@ -0,0 +1,32 @@
|
||||
"""add action description table
|
||||
|
||||
Revision ID: d09a5945e4a0
|
||||
Revises: d098df6021e2
|
||||
Create Date: 2017-07-13 20:33:01.473711
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'd09a5945e4a0'
|
||||
down_revision = 'd098df6021e2'
|
||||
|
||||
from alembic import op
|
||||
import oslo_db
|
||||
import sqlalchemy as sa
|
||||
|
||||
def upgrade():
|
||||
op.create_table('action_descriptions',
|
||||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('deleted_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('deleted', oslo_db.sqlalchemy.types.SoftDeleteInteger(), nullable=True),
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('action_type', sa.String(length=255), nullable=False),
|
||||
sa.Column('description', sa.String(length=255), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('action_type', name='uniq_action_description0action_type')
|
||||
)
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_table('action_descriptions')
|
@ -1127,3 +1127,74 @@ class Connection(api.BaseConnection):
|
||||
return self._soft_delete(models.Service, service_id)
|
||||
except exception.ResourceNotFound:
|
||||
raise exception.ServiceNotFound(service=service_id)
|
||||
|
||||
# ### ACTION_DESCRIPTIONS ### #
|
||||
|
||||
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 get_action_description_list(self, context, filters=None, limit=None,
|
||||
marker=None, sort_key=None,
|
||||
sort_dir=None, eager=False):
|
||||
query = model_query(models.ActionDescription)
|
||||
if eager:
|
||||
query = self._set_eager_options(models.ActionDescription, query)
|
||||
query = self._add_action_descriptions_filters(query, filters)
|
||||
if not context.show_deleted:
|
||||
query = query.filter_by(deleted_at=None)
|
||||
return _paginate_query(models.ActionDescription, limit, marker,
|
||||
sort_key, sort_dir, query)
|
||||
|
||||
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)
|
||||
|
@ -278,3 +278,17 @@ class Service(Base):
|
||||
name = Column(String(255), nullable=False)
|
||||
host = Column(String(255), nullable=False)
|
||||
last_seen_up = Column(DateTime, nullable=True)
|
||||
|
||||
|
||||
class ActionDescription(Base):
|
||||
"""Represents a action description"""
|
||||
|
||||
__tablename__ = 'action_descriptions'
|
||||
__table_args__ = (
|
||||
UniqueConstraint('action_type',
|
||||
name="uniq_action_description0action_type"),
|
||||
table_args()
|
||||
)
|
||||
id = Column(Integer, primary_key=True)
|
||||
action_type = Column(String(255), nullable=False)
|
||||
description = Column(String(255), nullable=False)
|
||||
|
@ -33,3 +33,4 @@ def register_all():
|
||||
__import__('watcher.objects.efficacy_indicator')
|
||||
__import__('watcher.objects.scoring_engine')
|
||||
__import__('watcher.objects.service')
|
||||
__import__('watcher.objects.action_description')
|
||||
|
141
watcher/objects/action_description.py
Normal file
141
watcher/objects/action_description.py
Normal file
@ -0,0 +1,141 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2017 ZTE
|
||||
#
|
||||
# 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.
|
||||
|
||||
from watcher.common import exception
|
||||
from watcher.common import utils
|
||||
from watcher.db import api as db_api
|
||||
from watcher.objects import base
|
||||
from watcher.objects import fields as wfields
|
||||
|
||||
|
||||
@base.WatcherObjectRegistry.register
|
||||
class ActionDescription(base.WatcherPersistentObject, base.WatcherObject,
|
||||
base.WatcherObjectDictCompat):
|
||||
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
dbapi = db_api.get_instance()
|
||||
|
||||
fields = {
|
||||
'id': wfields.IntegerField(),
|
||||
'action_type': wfields.StringField(),
|
||||
'description': wfields.StringField(),
|
||||
}
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get(cls, context, action_id):
|
||||
"""Find a action description based on its id
|
||||
|
||||
:param context: Security context. NOTE: This should only
|
||||
be used internally by the indirection_api.
|
||||
Unfortunately, RPC requires context as the first
|
||||
argument, even though we don't use it.
|
||||
A context should be set when instantiating the
|
||||
object
|
||||
:param action_id: the id of a action description.
|
||||
:returns: a :class:`ActionDescription` object.
|
||||
"""
|
||||
if utils.is_int_like(action_id):
|
||||
db_action = cls.dbapi.get_action_description_by_id(
|
||||
context, action_id)
|
||||
action = ActionDescription._from_db_object(cls(context), db_action)
|
||||
return action
|
||||
else:
|
||||
raise exception.InvalidIdentity(identity=action_id)
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_type(cls, context, action_type):
|
||||
"""Find a action description based on action type
|
||||
|
||||
:param action_type: the action type of a action description.
|
||||
:param context: Security context
|
||||
:returns: a :class:`ActionDescription` object.
|
||||
"""
|
||||
|
||||
db_action = cls.dbapi.get_action_description_by_type(
|
||||
context, action_type)
|
||||
action = cls._from_db_object(cls(context), db_action)
|
||||
return action
|
||||
|
||||
@base.remotable_classmethod
|
||||
def list(cls, context, limit=None, marker=None, filters=None,
|
||||
sort_key=None, sort_dir=None):
|
||||
"""Return a list of :class:`ActionDescription` objects.
|
||||
|
||||
:param context: Security context. NOTE: This should only
|
||||
be used internally by the indirection_api.
|
||||
Unfortunately, RPC requires context as the first
|
||||
argument, even though we don't use it.
|
||||
A context should be set when instantiating the
|
||||
object, e.g.: ActionDescription(context)
|
||||
:param filters: dict mapping the filter key to a value.
|
||||
:param limit: maximum number of resources to return in a single result.
|
||||
:param marker: pagination marker for large data sets.
|
||||
:param sort_key: column to sort results by.
|
||||
:param sort_dir: direction to sort. "asc" or "desc".
|
||||
:returns: a list of :class:`ActionDescription` object.
|
||||
"""
|
||||
db_actions = cls.dbapi.get_action_description_list(
|
||||
context,
|
||||
filters=filters,
|
||||
limit=limit,
|
||||
marker=marker,
|
||||
sort_key=sort_key,
|
||||
sort_dir=sort_dir)
|
||||
|
||||
return [cls._from_db_object(cls(context), obj) for obj in db_actions]
|
||||
|
||||
@base.remotable
|
||||
def create(self):
|
||||
"""Create a :class:`ActionDescription` record in the DB."""
|
||||
values = self.obj_get_changes()
|
||||
db_action = self.dbapi.create_action_description(values)
|
||||
self._from_db_object(self, db_action)
|
||||
|
||||
@base.remotable
|
||||
def save(self):
|
||||
"""Save updates to this :class:`ActionDescription`.
|
||||
|
||||
Updates will be made column by column based on the result
|
||||
of self.what_changed().
|
||||
"""
|
||||
updates = self.obj_get_changes()
|
||||
db_obj = self.dbapi.update_action_description(self.id, updates)
|
||||
obj = self._from_db_object(self, db_obj, eager=False)
|
||||
self.obj_refresh(obj)
|
||||
self.obj_reset_changes()
|
||||
|
||||
def refresh(self):
|
||||
"""Loads updates for this :class:`ActionDescription`.
|
||||
|
||||
Loads a action description with the same id from the database and
|
||||
checks for updated attributes. Updates are applied from
|
||||
the loaded action description column by column, if there
|
||||
are any updates.
|
||||
"""
|
||||
current = self.get(self._context, action_id=self.id)
|
||||
for field in self.fields:
|
||||
if (hasattr(self, base.get_attrname(field)) and
|
||||
self[field] != current[field]):
|
||||
self[field] = current[field]
|
||||
|
||||
def soft_delete(self):
|
||||
"""Soft Delete the :class:`ActionDescription` from the DB."""
|
||||
db_obj = self.dbapi.soft_delete_action_description(self.id)
|
||||
obj = self._from_db_object(
|
||||
self.__class__(self._context), db_obj, eager=False)
|
||||
self.obj_refresh(obj)
|
@ -22,6 +22,7 @@ import types
|
||||
import mock
|
||||
from oslo_config import cfg
|
||||
from oslo_service import service
|
||||
from watcher.applier import sync
|
||||
from watcher.common import service as watcher_service
|
||||
|
||||
from watcher.cmd import applier
|
||||
@ -49,6 +50,7 @@ class TestApplier(base.BaseTestCase):
|
||||
super(TestApplier, self).tearDown()
|
||||
self.conf._parse_cli_opts = self._parse_cli_opts
|
||||
|
||||
@mock.patch.object(sync.Syncer, "sync", mock.Mock())
|
||||
@mock.patch.object(service, "launch")
|
||||
def test_run_applier_app(self, m_launch):
|
||||
applier.main()
|
||||
|
293
watcher/tests/db/test_action_description.py
Normal file
293
watcher/tests/db/test_action_description.py
Normal file
@ -0,0 +1,293 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2017 ZTE
|
||||
#
|
||||
# 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.
|
||||
|
||||
|
||||
"""Tests for manipulating ActionDescription via the DB API"""
|
||||
|
||||
import freezegun
|
||||
|
||||
from watcher.common import exception
|
||||
from watcher.tests.db import base
|
||||
from watcher.tests.db import utils
|
||||
|
||||
|
||||
class TestDbActionDescriptionFilters(base.DbTestCase):
|
||||
|
||||
FAKE_OLDER_DATE = '2015-01-01T09:52:05.219414'
|
||||
FAKE_OLD_DATE = '2016-01-01T09:52:05.219414'
|
||||
FAKE_TODAY = '2017-02-24T09:52:05.219414'
|
||||
|
||||
def setUp(self):
|
||||
super(TestDbActionDescriptionFilters, self).setUp()
|
||||
self.context.show_deleted = True
|
||||
self._data_setup()
|
||||
|
||||
def _data_setup(self):
|
||||
action_desc1_type = "nop"
|
||||
action_desc2_type = "sleep"
|
||||
action_desc3_type = "resize"
|
||||
|
||||
with freezegun.freeze_time(self.FAKE_TODAY):
|
||||
self.action_desc1 = utils.create_test_action_desc(
|
||||
id=1, action_type=action_desc1_type,
|
||||
description="description")
|
||||
with freezegun.freeze_time(self.FAKE_OLD_DATE):
|
||||
self.action_desc2 = utils.create_test_action_desc(
|
||||
id=2, action_type=action_desc2_type,
|
||||
description="description")
|
||||
with freezegun.freeze_time(self.FAKE_OLDER_DATE):
|
||||
self.action_desc3 = utils.create_test_action_desc(
|
||||
id=3, action_type=action_desc3_type,
|
||||
description="description")
|
||||
|
||||
def _soft_delete_action_descs(self):
|
||||
with freezegun.freeze_time(self.FAKE_TODAY):
|
||||
self.dbapi.soft_delete_action_description(self.action_desc1.id)
|
||||
with freezegun.freeze_time(self.FAKE_OLD_DATE):
|
||||
self.dbapi.soft_delete_action_description(self.action_desc2.id)
|
||||
with freezegun.freeze_time(self.FAKE_OLDER_DATE):
|
||||
self.dbapi.soft_delete_action_description(self.action_desc3.id)
|
||||
|
||||
def _update_action_descs(self):
|
||||
with freezegun.freeze_time(self.FAKE_TODAY):
|
||||
self.dbapi.update_action_description(
|
||||
self.action_desc1.id, values={"description":
|
||||
"nop description"})
|
||||
with freezegun.freeze_time(self.FAKE_OLD_DATE):
|
||||
self.dbapi.update_action_description(
|
||||
self.action_desc2.id, values={"description":
|
||||
"sleep description"})
|
||||
with freezegun.freeze_time(self.FAKE_OLDER_DATE):
|
||||
self.dbapi.update_action_description(
|
||||
self.action_desc3.id, values={"description":
|
||||
"resize description"})
|
||||
|
||||
def test_get_action_desc_list_filter_deleted_true(self):
|
||||
with freezegun.freeze_time(self.FAKE_TODAY):
|
||||
self.dbapi.soft_delete_action_description(self.action_desc1.id)
|
||||
|
||||
res = self.dbapi.get_action_description_list(
|
||||
self.context, filters={'deleted': True})
|
||||
|
||||
self.assertEqual([self.action_desc1['action_type']],
|
||||
[r.action_type for r in res])
|
||||
|
||||
def test_get_action_desc_list_filter_deleted_false(self):
|
||||
with freezegun.freeze_time(self.FAKE_TODAY):
|
||||
self.dbapi.soft_delete_action_description(self.action_desc1.id)
|
||||
|
||||
res = self.dbapi.get_action_description_list(
|
||||
self.context, filters={'deleted': False})
|
||||
|
||||
self.assertEqual(
|
||||
set([self.action_desc2['action_type'],
|
||||
self.action_desc3['action_type']]),
|
||||
set([r.action_type for r in res]))
|
||||
|
||||
def test_get_action_desc_list_filter_deleted_at_eq(self):
|
||||
self._soft_delete_action_descs()
|
||||
|
||||
res = self.dbapi.get_action_description_list(
|
||||
self.context, filters={'deleted_at__eq': self.FAKE_TODAY})
|
||||
|
||||
self.assertEqual([self.action_desc1['id']], [r.id for r in res])
|
||||
|
||||
def test_get_action_desc_list_filter_deleted_at_lt(self):
|
||||
self._soft_delete_action_descs()
|
||||
|
||||
res = self.dbapi.get_action_description_list(
|
||||
self.context, filters={'deleted_at__lt': self.FAKE_TODAY})
|
||||
|
||||
self.assertEqual(
|
||||
set([self.action_desc2['id'], self.action_desc3['id']]),
|
||||
set([r.id for r in res]))
|
||||
|
||||
def test_get_action_desc_list_filter_deleted_at_lte(self):
|
||||
self._soft_delete_action_descs()
|
||||
|
||||
res = self.dbapi.get_action_description_list(
|
||||
self.context, filters={'deleted_at__lte': self.FAKE_OLD_DATE})
|
||||
|
||||
self.assertEqual(
|
||||
set([self.action_desc2['id'], self.action_desc3['id']]),
|
||||
set([r.id for r in res]))
|
||||
|
||||
def test_get_action_desc_list_filter_deleted_at_gt(self):
|
||||
self._soft_delete_action_descs()
|
||||
|
||||
res = self.dbapi.get_action_description_list(
|
||||
self.context, filters={'deleted_at__gt': self.FAKE_OLD_DATE})
|
||||
|
||||
self.assertEqual([self.action_desc1['id']], [r.id for r in res])
|
||||
|
||||
def test_get_action_desc_list_filter_deleted_at_gte(self):
|
||||
self._soft_delete_action_descs()
|
||||
|
||||
res = self.dbapi.get_action_description_list(
|
||||
self.context, filters={'deleted_at__gte': self.FAKE_OLD_DATE})
|
||||
|
||||
self.assertEqual(
|
||||
set([self.action_desc1['id'], self.action_desc2['id']]),
|
||||
set([r.id for r in res]))
|
||||
|
||||
# created_at #
|
||||
|
||||
def test_get_action_desc_list_filter_created_at_eq(self):
|
||||
res = self.dbapi.get_action_description_list(
|
||||
self.context, filters={'created_at__eq': self.FAKE_TODAY})
|
||||
|
||||
self.assertEqual([self.action_desc1['id']], [r.id for r in res])
|
||||
|
||||
def test_get_action_desc_list_filter_created_at_lt(self):
|
||||
res = self.dbapi.get_action_description_list(
|
||||
self.context, filters={'created_at__lt': self.FAKE_TODAY})
|
||||
|
||||
self.assertEqual(
|
||||
set([self.action_desc2['id'], self.action_desc3['id']]),
|
||||
set([r.id for r in res]))
|
||||
|
||||
def test_get_action_desc_list_filter_created_at_lte(self):
|
||||
res = self.dbapi.get_action_description_list(
|
||||
self.context, filters={'created_at__lte': self.FAKE_OLD_DATE})
|
||||
|
||||
self.assertEqual(
|
||||
set([self.action_desc2['id'], self.action_desc3['id']]),
|
||||
set([r.id for r in res]))
|
||||
|
||||
def test_get_action_desc_list_filter_created_at_gt(self):
|
||||
res = self.dbapi.get_action_description_list(
|
||||
self.context, filters={'created_at__gt': self.FAKE_OLD_DATE})
|
||||
|
||||
self.assertEqual([self.action_desc1['id']], [r.id for r in res])
|
||||
|
||||
def test_get_action_desc_list_filter_created_at_gte(self):
|
||||
res = self.dbapi.get_action_description_list(
|
||||
self.context, filters={'created_at__gte': self.FAKE_OLD_DATE})
|
||||
|
||||
self.assertEqual(
|
||||
set([self.action_desc1['id'], self.action_desc2['id']]),
|
||||
set([r.id for r in res]))
|
||||
|
||||
# updated_at #
|
||||
|
||||
def test_get_action_desc_list_filter_updated_at_eq(self):
|
||||
self._update_action_descs()
|
||||
|
||||
res = self.dbapi.get_action_description_list(
|
||||
self.context, filters={'updated_at__eq': self.FAKE_TODAY})
|
||||
|
||||
self.assertEqual([self.action_desc1['id']], [r.id for r in res])
|
||||
|
||||
def test_get_action_desc_list_filter_updated_at_lt(self):
|
||||
self._update_action_descs()
|
||||
|
||||
res = self.dbapi.get_action_description_list(
|
||||
self.context, filters={'updated_at__lt': self.FAKE_TODAY})
|
||||
|
||||
self.assertEqual(
|
||||
set([self.action_desc2['id'], self.action_desc3['id']]),
|
||||
set([r.id for r in res]))
|
||||
|
||||
def test_get_action_desc_list_filter_updated_at_lte(self):
|
||||
self._update_action_descs()
|
||||
|
||||
res = self.dbapi.get_action_description_list(
|
||||
self.context, filters={'updated_at__lte': self.FAKE_OLD_DATE})
|
||||
|
||||
self.assertEqual(
|
||||
set([self.action_desc2['id'], self.action_desc3['id']]),
|
||||
set([r.id for r in res]))
|
||||
|
||||
def test_get_action_desc_list_filter_updated_at_gt(self):
|
||||
self._update_action_descs()
|
||||
|
||||
res = self.dbapi.get_action_description_list(
|
||||
self.context, filters={'updated_at__gt': self.FAKE_OLD_DATE})
|
||||
|
||||
self.assertEqual([self.action_desc1['id']], [r.id for r in res])
|
||||
|
||||
def test_get_action_desc_list_filter_updated_at_gte(self):
|
||||
self._update_action_descs()
|
||||
|
||||
res = self.dbapi.get_action_description_list(
|
||||
self.context, filters={'updated_at__gte': self.FAKE_OLD_DATE})
|
||||
|
||||
self.assertEqual(
|
||||
set([self.action_desc1['id'], self.action_desc2['id']]),
|
||||
set([r.id for r in res]))
|
||||
|
||||
|
||||
class DbActionDescriptionTestCase(base.DbTestCase):
|
||||
|
||||
def _create_test_action_desc(self, **kwargs):
|
||||
action_desc = utils.get_test_action_desc(**kwargs)
|
||||
self.dbapi.create_action_description(action_desc)
|
||||
return action_desc
|
||||
|
||||
def test_get_action_desc_list(self):
|
||||
ids = []
|
||||
for i in range(1, 4):
|
||||
action_desc = utils.create_test_action_desc(
|
||||
id=i,
|
||||
action_type="action_%s" % i,
|
||||
description="description_{0}".format(i))
|
||||
ids.append(action_desc['id'])
|
||||
action_descs = self.dbapi.get_action_description_list(self.context)
|
||||
action_desc_ids = [s.id for s in action_descs]
|
||||
self.assertEqual(sorted(ids), sorted(action_desc_ids))
|
||||
|
||||
def test_get_action_desc_list_with_filters(self):
|
||||
action_desc1 = self._create_test_action_desc(
|
||||
id=1,
|
||||
action_type="action_1",
|
||||
description="description_1",
|
||||
)
|
||||
action_desc2 = self._create_test_action_desc(
|
||||
id=2,
|
||||
action_type="action_2",
|
||||
description="description_2",
|
||||
)
|
||||
|
||||
res = self.dbapi.get_action_description_list(
|
||||
self.context, filters={'action_type': 'action_1'})
|
||||
self.assertEqual([action_desc1['id']], [r.id for r in res])
|
||||
|
||||
res = self.dbapi.get_action_description_list(
|
||||
self.context, filters={'action_type': 'action_3'})
|
||||
self.assertEqual([], [r.id for r in res])
|
||||
|
||||
res = self.dbapi.get_action_description_list(
|
||||
self.context,
|
||||
filters={'action_type': 'action_2'})
|
||||
self.assertEqual([action_desc2['id']], [r.id for r in res])
|
||||
|
||||
def test_get_action_desc_by_type(self):
|
||||
created_action_desc = self._create_test_action_desc()
|
||||
action_desc = self.dbapi.get_action_description_by_type(
|
||||
self.context, created_action_desc['action_type'])
|
||||
self.assertEqual(action_desc.action_type,
|
||||
created_action_desc['action_type'])
|
||||
|
||||
def test_get_action_desc_that_does_not_exist(self):
|
||||
self.assertRaises(exception.ActionDescriptionNotFound,
|
||||
self.dbapi.get_action_description_by_id,
|
||||
self.context, 404)
|
||||
|
||||
def test_update_action_desc(self):
|
||||
action_desc = self._create_test_action_desc()
|
||||
res = self.dbapi.update_action_description(
|
||||
action_desc['id'], {'description': 'description_test'})
|
||||
self.assertEqual('description_test', res.description)
|
@ -331,3 +331,26 @@ def create_test_efficacy_indicator(**kwargs):
|
||||
del efficacy_indicator['id']
|
||||
dbapi = db_api.get_instance()
|
||||
return dbapi.create_efficacy_indicator(efficacy_indicator)
|
||||
|
||||
|
||||
def get_test_action_desc(**kwargs):
|
||||
return {
|
||||
'id': kwargs.get('id', 1),
|
||||
'action_type': kwargs.get('action_type', 'nop'),
|
||||
'description': kwargs.get('description', 'Logging a NOP message'),
|
||||
'created_at': kwargs.get('created_at'),
|
||||
'updated_at': kwargs.get('updated_at'),
|
||||
'deleted_at': kwargs.get('deleted_at'),
|
||||
}
|
||||
|
||||
|
||||
def create_test_action_desc(**kwargs):
|
||||
"""Create test action description entry in DB and return ActionDescription.
|
||||
|
||||
Function to be used to create test ActionDescription objects in the DB.
|
||||
:param kwargs: kwargs with overriding values for service's attributes.
|
||||
:returns: Test ActionDescription DB object.
|
||||
"""
|
||||
action_desc = get_test_action_desc(**kwargs)
|
||||
dbapi = db_api.get_instance()
|
||||
return dbapi.create_action_description(action_desc)
|
||||
|
120
watcher/tests/objects/test_action_description.py
Normal file
120
watcher/tests/objects/test_action_description.py
Normal file
@ -0,0 +1,120 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright 2017 ZTE
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import datetime
|
||||
|
||||
import iso8601
|
||||
import mock
|
||||
|
||||
from watcher.db.sqlalchemy import api as db_api
|
||||
from watcher import objects
|
||||
from watcher.tests.db import base
|
||||
from watcher.tests.db import utils
|
||||
|
||||
|
||||
class TestActionDescriptionObject(base.DbTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestActionDescriptionObject, self).setUp()
|
||||
self.fake_action_desc = utils.get_test_action_desc(
|
||||
created_at=datetime.datetime.utcnow())
|
||||
|
||||
@mock.patch.object(db_api.Connection, 'get_action_description_by_id')
|
||||
def test_get_by_id(self, mock_get_action_desc):
|
||||
action_desc_id = self.fake_action_desc['id']
|
||||
mock_get_action_desc.return_value = self.fake_action_desc
|
||||
action_desc = objects.ActionDescription.get(
|
||||
self.context, action_desc_id)
|
||||
mock_get_action_desc.assert_called_once_with(
|
||||
self.context, action_desc_id)
|
||||
self.assertEqual(self.context, action_desc._context)
|
||||
|
||||
@mock.patch.object(db_api.Connection, 'get_action_description_list')
|
||||
def test_list(self, mock_get_list):
|
||||
mock_get_list.return_value = [self.fake_action_desc]
|
||||
action_desc = objects.ActionDescription.list(self.context)
|
||||
self.assertEqual(1, mock_get_list.call_count)
|
||||
self.assertEqual(1, len(action_desc))
|
||||
self.assertIsInstance(action_desc[0], objects.ActionDescription)
|
||||
self.assertEqual(self.context, action_desc[0]._context)
|
||||
|
||||
@mock.patch.object(db_api.Connection, 'create_action_description')
|
||||
def test_create(self, mock_create_action_desc):
|
||||
mock_create_action_desc.return_value = self.fake_action_desc
|
||||
action_desc = objects.ActionDescription(
|
||||
self.context, **self.fake_action_desc)
|
||||
|
||||
action_desc.create()
|
||||
expected_action_desc = self.fake_action_desc.copy()
|
||||
expected_action_desc['created_at'] = expected_action_desc[
|
||||
'created_at'].replace(tzinfo=iso8601.iso8601.Utc())
|
||||
|
||||
mock_create_action_desc.assert_called_once_with(expected_action_desc)
|
||||
self.assertEqual(self.context, action_desc._context)
|
||||
|
||||
@mock.patch.object(db_api.Connection, 'update_action_description')
|
||||
@mock.patch.object(db_api.Connection, 'get_action_description_by_id')
|
||||
def test_save(self, mock_get_action_desc, mock_update_action_desc):
|
||||
mock_get_action_desc.return_value = self.fake_action_desc
|
||||
fake_saved_action_desc = self.fake_action_desc.copy()
|
||||
fake_saved_action_desc['updated_at'] = datetime.datetime.utcnow()
|
||||
mock_update_action_desc.return_value = fake_saved_action_desc
|
||||
_id = self.fake_action_desc['id']
|
||||
action_desc = objects.ActionDescription.get(self.context, _id)
|
||||
action_desc.description = 'This is a test'
|
||||
action_desc.save()
|
||||
|
||||
mock_get_action_desc.assert_called_once_with(self.context, _id)
|
||||
mock_update_action_desc.assert_called_once_with(
|
||||
_id, {'description': 'This is a test'})
|
||||
self.assertEqual(self.context, action_desc._context)
|
||||
|
||||
@mock.patch.object(db_api.Connection, 'get_action_description_by_id')
|
||||
def test_refresh(self, mock_get_action_desc):
|
||||
returns = [dict(self.fake_action_desc, description="Test message1"),
|
||||
dict(self.fake_action_desc, description="Test message2")]
|
||||
mock_get_action_desc.side_effect = returns
|
||||
_id = self.fake_action_desc['id']
|
||||
expected = [mock.call(self.context, _id),
|
||||
mock.call(self.context, _id)]
|
||||
action_desc = objects.ActionDescription.get(self.context, _id)
|
||||
self.assertEqual("Test message1", action_desc.description)
|
||||
action_desc.refresh()
|
||||
self.assertEqual("Test message2", action_desc.description)
|
||||
self.assertEqual(expected, mock_get_action_desc.call_args_list)
|
||||
self.assertEqual(self.context, action_desc._context)
|
||||
|
||||
@mock.patch.object(db_api.Connection, 'soft_delete_action_description')
|
||||
@mock.patch.object(db_api.Connection, 'get_action_description_by_id')
|
||||
def test_soft_delete(self, mock_get_action_desc, mock_soft_delete):
|
||||
mock_get_action_desc.return_value = self.fake_action_desc
|
||||
fake_deleted_action_desc = self.fake_action_desc.copy()
|
||||
fake_deleted_action_desc['deleted_at'] = datetime.datetime.utcnow()
|
||||
mock_soft_delete.return_value = fake_deleted_action_desc
|
||||
|
||||
expected_action_desc = fake_deleted_action_desc.copy()
|
||||
expected_action_desc['created_at'] = expected_action_desc[
|
||||
'created_at'].replace(tzinfo=iso8601.iso8601.Utc())
|
||||
expected_action_desc['deleted_at'] = expected_action_desc[
|
||||
'deleted_at'].replace(tzinfo=iso8601.iso8601.Utc())
|
||||
|
||||
_id = self.fake_action_desc['id']
|
||||
action_desc = objects.ActionDescription.get(self.context, _id)
|
||||
action_desc.soft_delete()
|
||||
mock_get_action_desc.assert_called_once_with(self.context, _id)
|
||||
mock_soft_delete.assert_called_once_with(_id)
|
||||
self.assertEqual(self.context, action_desc._context)
|
||||
self.assertEqual(expected_action_desc, action_desc.as_dict())
|
@ -419,6 +419,7 @@ expected_object_fingerprints = {
|
||||
'ScoringEngine': '1.0-4abbe833544000728e17bd9e83f97576',
|
||||
'Service': '1.0-4b35b99ada9677a882c9de2b30212f35',
|
||||
'MyObj': '1.5-23c516d1e842f365f694e688d34e47c3',
|
||||
'ActionDescription': '1.0-5761a3d16651046e7a0c357b57a6583e'
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user