Merge "Refactored Watcher objects to use OVO"

This commit is contained in:
Jenkins
2016-11-10 09:37:58 +00:00
committed by Gerrit Code Review
36 changed files with 1109 additions and 1492 deletions

View File

@@ -0,0 +1,22 @@
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# 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.
from watcher import objects
# NOTE(comstud): Make sure we have all of the objects loaded. We do this
# at module import time, because we may be using mock decorators in our
# tests that run at import time.
objects.register_all()

View File

@@ -64,7 +64,7 @@ class FunctionalTest(base.DbTestCase):
def _make_app(self, enable_acl=False):
# Determine where we are so we can set up paths in the config
root_dir = self.path_get()
root_dir = self.get_path()
self.config = {
'app': {

View File

@@ -250,7 +250,7 @@ class TestPatch(api_base.FunctionalTest):
def setUp(self):
super(TestPatch, self).setUp()
obj_utils.create_test_audit_template(self.context)
self.audit = obj_utils.create_test_audit(self.context)
self.audit = obj_utils.create_test_audit(self.context, )
p = mock.patch.object(db_api.BaseConnection, 'update_audit')
self.mock_audit_update = p.start()
self.mock_audit_update.side_effect = self._simulate_rpc_audit_update
@@ -313,15 +313,15 @@ class TestPatch(api_base.FunctionalTest):
def test_remove_ok(self):
response = self.get_json('/audits/%s' % self.audit.uuid)
self.assertIsNotNone(response['state'])
self.assertIsNotNone(response['interval'])
response = self.patch_json('/audits/%s' % self.audit.uuid,
[{'path': '/state', 'op': 'remove'}])
[{'path': '/interval', 'op': 'remove'}])
self.assertEqual('application/json', response.content_type)
self.assertEqual(200, response.status_code)
response = self.get_json('/audits/%s' % self.audit.uuid)
self.assertIsNone(response['state'])
self.assertIsNone(response['interval'])
def test_remove_uuid(self):
response = self.patch_json('/audits/%s' % self.audit.uuid,

View File

@@ -80,7 +80,7 @@ class TestDefaultWorkFlowEngine(base.DbTestCase):
'next': next,
}
new_action = objects.Action(self.context, **action)
new_action.create(self.context)
new_action.create()
new_action.save()
return new_action

View File

@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
# Copyright 2010-2011 OpenStack Foundation
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
#
@@ -35,8 +33,11 @@ from watcher.tests import policy_fixture
CONF = cfg.CONF
log.register_options(CONF)
CONF.set_override('use_stderr', False, enforce_type=True)
try:
log.register_options(CONF)
except cfg.ArgsAlreadyParsedError:
pass
CONF.set_override('use_stderr', False)
class BaseTestCase(testscenarios.WithScenarios, base.BaseTestCase):
@@ -76,6 +77,9 @@ class TestCase(BaseTestCase):
}
}
}
objects_base.WatcherObject.indirection_api = None
self.context = watcher_context.RequestContext(
auth_token_info=self.token_info,
project_id='fake_project',
@@ -104,19 +108,21 @@ class TestCase(BaseTestCase):
self._reset_singletons()
self._base_test_obj_backup = copy.copy(
objects_base.WatcherObject._obj_classes)
objects_base.WatcherObjectRegistry._registry._obj_classes)
self.addCleanup(self._restore_obj_registry)
self.addCleanup(self._reset_singletons)
def _reset_singletons(self):
service.Singleton._instances.clear()
def _restore_obj_registry(self):
objects_base.WatcherObject._obj_classes = self._base_test_obj_backup
def reset_pecan():
pecan.set_config({}, overwrite=True)
def tearDown(self):
super(TestCase, self).tearDown()
pecan.set_config({}, overwrite=True)
self.addCleanup(reset_pecan)
def _restore_obj_registry(self):
objects_base.WatcherObjectRegistry._registry._obj_classes = (
self._base_test_obj_backup)
def config(self, **kw):
"""Override config options for a test."""
@@ -124,7 +130,7 @@ class TestCase(BaseTestCase):
for k, v in kw.items():
CONF.set_override(k, v, group, enforce_type=True)
def path_get(self, project_file=None):
def get_path(self, project_file=None):
"""Get the absolute path to a file. Used for testing the API.
:param project_file: File whose path to return. Default: None.

View File

@@ -20,6 +20,7 @@ import freezegun
import mock
from watcher.common import context as watcher_context
from watcher.common import utils
from watcher.db import purge
from watcher.db.sqlalchemy import api as dbapi
from watcher.tests.db import base
@@ -101,27 +102,33 @@ class TestPurgeCommand(base.DbTestCase):
with freezegun.freeze_time(self.expired_date):
self.goal1 = obj_utils.create_test_goal(
self.context, id=self._generate_id(), uuid=None,
self.context, id=self._generate_id(),
uuid=utils.generate_uuid(),
name=goal1_name, display_name=goal1_name.lower())
self.goal2 = obj_utils.create_test_goal(
self.context, id=self._generate_id(), uuid=None,
self.context, id=self._generate_id(),
uuid=utils.generate_uuid(),
name=goal2_name, display_name=goal2_name.lower())
self.goal3 = obj_utils.create_test_goal(
self.context, id=self._generate_id(), uuid=None,
self.context, id=self._generate_id(),
uuid=utils.generate_uuid(),
name=goal3_name, display_name=goal3_name.lower())
self.goal1.soft_delete()
with freezegun.freeze_time(self.expired_date):
self.strategy1 = obj_utils.create_test_strategy(
self.context, id=self._generate_id(), uuid=None,
self.context, id=self._generate_id(),
uuid=utils.generate_uuid(),
name=strategy1_name, display_name=strategy1_name.lower(),
goal_id=self.goal1.id)
self.strategy2 = obj_utils.create_test_strategy(
self.context, id=self._generate_id(), uuid=None,
self.context, id=self._generate_id(),
uuid=utils.generate_uuid(),
name=strategy2_name, display_name=strategy2_name.lower(),
goal_id=self.goal2.id)
self.strategy3 = obj_utils.create_test_strategy(
self.context, id=self._generate_id(), uuid=None,
self.context, id=self._generate_id(),
uuid=utils.generate_uuid(),
name=strategy3_name, display_name=strategy3_name.lower(),
goal_id=self.goal3.id)
self.strategy1.soft_delete()
@@ -129,50 +136,61 @@ class TestPurgeCommand(base.DbTestCase):
with freezegun.freeze_time(self.expired_date):
self.audit_template1 = obj_utils.create_test_audit_template(
self.context, name=self.audit_template1_name,
id=self._generate_id(), uuid=None, goal_id=self.goal1.id,
id=self._generate_id(),
uuid=utils.generate_uuid(), goal_id=self.goal1.id,
strategy_id=self.strategy1.id)
self.audit_template2 = obj_utils.create_test_audit_template(
self.context, name=self.audit_template2_name,
id=self._generate_id(), uuid=None, goal_id=self.goal2.id,
id=self._generate_id(),
uuid=utils.generate_uuid(), goal_id=self.goal2.id,
strategy_id=self.strategy2.id)
self.audit_template3 = obj_utils.create_test_audit_template(
self.context, name=self.audit_template3_name,
id=self._generate_id(), uuid=None, goal_id=self.goal3.id,
id=self._generate_id(),
uuid=utils.generate_uuid(), goal_id=self.goal3.id,
strategy_id=self.strategy3.id)
self.audit_template1.soft_delete()
with freezegun.freeze_time(self.expired_date):
self.audit1 = obj_utils.create_test_audit(
self.context, id=self._generate_id(), uuid=None,
self.context, id=self._generate_id(),
uuid=utils.generate_uuid(),
goal_id=self.goal1.id, strategy_id=self.strategy1.id)
self.audit2 = obj_utils.create_test_audit(
self.context, id=self._generate_id(), uuid=None,
self.context, id=self._generate_id(),
uuid=utils.generate_uuid(),
goal_id=self.goal2.id, strategy_id=self.strategy2.id)
self.audit3 = obj_utils.create_test_audit(
self.context, id=self._generate_id(), uuid=None,
self.context, id=self._generate_id(),
uuid=utils.generate_uuid(),
goal_id=self.goal3.id, strategy_id=self.strategy3.id)
self.audit1.soft_delete()
with freezegun.freeze_time(self.expired_date):
self.action_plan1 = obj_utils.create_test_action_plan(
self.context, id=self._generate_id(), uuid=None,
audit_id=self.audit1.id, strategy_id=self.strategy1.id)
self.context, audit_id=self.audit1.id,
id=self._generate_id(), uuid=utils.generate_uuid(),
strategy_id=self.strategy1.id)
self.action_plan2 = obj_utils.create_test_action_plan(
self.context, id=self._generate_id(), uuid=None,
audit_id=self.audit2.id, strategy_id=self.strategy2.id)
self.context, audit_id=self.audit2.id,
id=self._generate_id(),
strategy_id=self.strategy2.id,
uuid=utils.generate_uuid())
self.action_plan3 = obj_utils.create_test_action_plan(
self.context, id=self._generate_id(), uuid=None,
audit_id=self.audit3.id, strategy_id=self.strategy3.id)
self.context, audit_id=self.audit3.id,
id=self._generate_id(), uuid=utils.generate_uuid(),
strategy_id=self.strategy3.id)
self.action1 = obj_utils.create_test_action(
self.context, action_plan_id=self.action_plan1.id,
id=self._generate_id(), uuid=None)
id=self._generate_id(),
uuid=utils.generate_uuid())
self.action2 = obj_utils.create_test_action(
self.context, action_plan_id=self.action_plan2.id,
id=self._generate_id(), uuid=None)
id=self._generate_id(), uuid=utils.generate_uuid())
self.action3 = obj_utils.create_test_action(
self.context, action_plan_id=self.action_plan3.id,
id=self._generate_id(), uuid=None)
id=self._generate_id(), uuid=utils.generate_uuid())
self.action_plan1.soft_delete()
@mock.patch.object(dbapi.Connection, "destroy_action")
@@ -249,30 +267,38 @@ class TestPurgeCommand(base.DbTestCase):
audit_template4 = obj_utils.create_test_audit_template(
self.context, goal_id=404, # Does not exist
name=self.generate_unique_name(prefix="Audit Template 4 "),
strategy_id=None, id=self._generate_id(), uuid=None)
strategy_id=None, id=self._generate_id(),
uuid=utils.generate_uuid())
audit4 = obj_utils.create_test_audit(
self.context, audit_template_id=audit_template4.id,
id=self._generate_id(), uuid=None)
id=self._generate_id(),
uuid=utils.generate_uuid())
action_plan4 = obj_utils.create_test_action_plan(
self.context, audit_id=audit4.id,
id=self._generate_id(), uuid=None)
id=self._generate_id(),
uuid=utils.generate_uuid())
action4 = obj_utils.create_test_action(
self.context, action_plan_id=action_plan4.id,
id=self._generate_id(), uuid=None)
id=self._generate_id(),
uuid=utils.generate_uuid())
audit_template5 = obj_utils.create_test_audit_template(
self.context, goal_id=self.goal1.id,
name=self.generate_unique_name(prefix="Audit Template 5 "),
strategy_id=None, id=self._generate_id(), uuid=None)
strategy_id=None, id=self._generate_id(),
uuid=utils.generate_uuid())
audit5 = obj_utils.create_test_audit(
self.context, audit_template_id=audit_template5.id,
id=self._generate_id(), uuid=None)
id=self._generate_id(),
uuid=utils.generate_uuid())
action_plan5 = obj_utils.create_test_action_plan(
self.context, audit_id=audit5.id,
id=self._generate_id(), uuid=None)
id=self._generate_id(),
uuid=utils.generate_uuid())
action5 = obj_utils.create_test_action(
self.context, action_plan_id=action_plan5.id,
id=self._generate_id(), uuid=None)
id=self._generate_id(),
uuid=utils.generate_uuid())
self.goal2.soft_delete()
self.strategy2.soft_delete()
@@ -338,30 +364,38 @@ class TestPurgeCommand(base.DbTestCase):
audit_template4 = obj_utils.create_test_audit_template(
self.context, goal_id=404, # Does not exist
name=self.generate_unique_name(prefix="Audit Template 4 "),
strategy_id=None, id=self._generate_id(), uuid=None)
strategy_id=None, id=self._generate_id(),
uuid=utils.generate_uuid())
audit4 = obj_utils.create_test_audit(
self.context, audit_template_id=audit_template4.id,
id=self._generate_id(), uuid=None)
id=self._generate_id(),
uuid=utils.generate_uuid())
action_plan4 = obj_utils.create_test_action_plan(
self.context, audit_id=audit4.id,
id=self._generate_id(), uuid=None)
id=self._generate_id(),
uuid=utils.generate_uuid())
action4 = obj_utils.create_test_action(
self.context, action_plan_id=action_plan4.id,
id=self._generate_id(), uuid=None)
id=self._generate_id(),
uuid=utils.generate_uuid())
audit_template5 = obj_utils.create_test_audit_template(
self.context, goal_id=self.goal1.id,
name=self.generate_unique_name(prefix="Audit Template 5 "),
strategy_id=None, id=self._generate_id(), uuid=None)
strategy_id=None, id=self._generate_id(),
uuid=utils.generate_uuid())
audit5 = obj_utils.create_test_audit(
self.context, audit_template_id=audit_template5.id,
id=self._generate_id(), uuid=None)
id=self._generate_id(),
uuid=utils.generate_uuid())
action_plan5 = obj_utils.create_test_action_plan(
self.context, audit_id=audit5.id,
id=self._generate_id(), uuid=None)
id=self._generate_id(),
uuid=utils.generate_uuid())
action5 = obj_utils.create_test_action(
self.context, action_plan_id=action_plan5.id,
id=self._generate_id(), uuid=None)
id=self._generate_id(),
uuid=utils.generate_uuid())
self.goal2.soft_delete()
self.strategy2.soft_delete()

View File

@@ -56,7 +56,7 @@ def get_test_audit(**kwargs):
'id': kwargs.get('id', 1),
'uuid': kwargs.get('uuid', '10a47dd1-4874-4298-91cf-eff046dbdb8d'),
'audit_type': kwargs.get('audit_type', 'ONESHOT'),
'state': kwargs.get('state'),
'state': kwargs.get('state', objects.audit.State.PENDING),
'created_at': kwargs.get('created_at'),
'updated_at': kwargs.get('updated_at'),
'deleted_at': kwargs.get('deleted_at'),

View File

@@ -21,7 +21,6 @@ from watcher.common import utils
from watcher.decision_engine.loading import default
from watcher.decision_engine import sync
from watcher import objects
from watcher.objects import action_plan as ap_objects
from watcher.tests.db import base
from watcher.tests.decision_engine import fake_goals
from watcher.tests.decision_engine import fake_strategies
@@ -329,22 +328,30 @@ class TestSyncer(base.DbTestCase):
# Should stay unmodified after sync()
audit1 = objects.Audit(
self.ctx, id=1, uuid=utils.generate_uuid(),
audit_type=objects.audit.AuditType.ONESHOT.value,
state=objects.audit.State.PENDING,
goal_id=goal1.id, strategy_id=strategy1.id)
# Should be modified by the sync() because its associated goal
# has been modified (compared to the defined fake goals)
audit2 = objects.Audit(
self.ctx, id=2, uuid=utils.generate_uuid(),
audit_type=objects.audit.AuditType.ONESHOT.value,
state=objects.audit.State.PENDING,
goal_id=goal2.id, strategy_id=strategy2.id)
# Should be modified by the sync() because its associated strategy
# has been modified (compared to the defined fake strategies)
audit3 = objects.Audit(
self.ctx, id=3, uuid=utils.generate_uuid(),
audit_type=objects.audit.AuditType.ONESHOT.value,
state=objects.audit.State.PENDING,
goal_id=goal1.id, strategy_id=strategy3.id)
# Modified because of both because its associated goal and associated
# strategy should be modified (compared to the defined fake
# goals/strategies)
audit4 = objects.Audit(
self.ctx, id=4, uuid=utils.generate_uuid(),
audit_type=objects.audit.AuditType.ONESHOT.value,
state=objects.audit.State.PENDING,
goal_id=goal2.id, strategy_id=strategy4.id)
audit1.create()
@@ -483,7 +490,7 @@ class TestSyncer(base.DbTestCase):
set([action_plan2.id, action_plan3.id, action_plan4.id]),
set(modified_action_plans))
self.assertTrue(
all(ap.state == ap_objects.State.CANCELLED
all(ap.state == objects.action_plan.State.CANCELLED
for ap in modified_action_plans.values()))
self.assertEqual(set([action_plan1.id]), set(unmodified_action_plans))
@@ -551,10 +558,14 @@ class TestSyncer(base.DbTestCase):
# Should stay unmodified after sync()
audit1 = objects.Audit(
self.ctx, id=1, uuid=utils.generate_uuid(),
audit_type=objects.audit.AuditType.ONESHOT.value,
state=objects.audit.State.PENDING,
goal_id=goal1.id, strategy_id=strategy1.id)
# Stale after syncing because the goal has been soft deleted
audit2 = objects.Audit(
self.ctx, id=2, uuid=utils.generate_uuid(),
audit_type=objects.audit.AuditType.ONESHOT.value,
state=objects.audit.State.PENDING,
goal_id=goal2.id, strategy_id=strategy2.id)
audit1.create()
audit2.create()
@@ -651,6 +662,6 @@ class TestSyncer(base.DbTestCase):
self.assertEqual(set([action_plan2.id]), set(modified_action_plans))
self.assertTrue(
all(ap.state == ap_objects.State.CANCELLED
all(ap.state == objects.action_plan.State.CANCELLED
for ap in modified_action_plans.values()))
self.assertEqual(set([action_plan1.id]), set(unmodified_action_plans))

View File

@@ -1,4 +1,4 @@
# Copyright 2015 IBM Corp.
# Copyright 2013 IBM Corp.
#
# 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
@@ -12,32 +12,38 @@
# License for the specific language governing permissions and limitations
# under the License.
import contextlib
import datetime
import gettext
import iso8601
import netaddr
from oslo_utils import timeutils
import mock
from oslo_versionedobjects import base as object_base
from oslo_versionedobjects import exception as object_exception
from oslo_versionedobjects import fixture as object_fixture
import six
from watcher.common import context
from watcher.objects import base
from watcher.objects import utils
from watcher.objects import fields
from watcher.tests import base as test_base
gettext.install('watcher')
class MyObj(base.WatcherObject):
VERSION = '1.0'
@base.WatcherObjectRegistry.register
class MyObj(base.WatcherPersistentObject, base.WatcherObject,
base.WatcherObjectDictCompat):
VERSION = '1.5'
fields = {'foo': int,
'bar': str,
'missing': str,
}
fields = {'foo': fields.IntegerField(),
'bar': fields.StringField(),
'missing': fields.StringField()}
def obj_load_attr(self, attrname):
setattr(self, attrname, 'loaded!')
@object_base.remotable_classmethod
def query(cls, context):
obj = cls(context)
obj.foo = 1
@@ -45,24 +51,29 @@ class MyObj(base.WatcherObject):
obj.obj_reset_changes()
return obj
def marco(self, context):
@object_base.remotable
def marco(self, context=None):
return 'polo'
def update_test(self, context):
if context.project_id == 'alternate':
@object_base.remotable
def update_test(self, context=None):
if context and context.user == 'alternate':
self.bar = 'alternate-context'
else:
self.bar = 'updated'
def save(self, context):
@object_base.remotable
def save(self, context=None):
self.obj_reset_changes()
def refresh(self, context):
@object_base.remotable
def refresh(self, context=None):
self.foo = 321
self.bar = 'refreshed'
self.obj_reset_changes()
def modify_save_modify(self, context):
@object_base.remotable
def modify_save_modify(self, context=None):
self.bar = 'meow'
self.save()
self.foo = 42
@@ -73,232 +84,361 @@ class MyObj2(object):
def obj_name(cls):
return 'MyObj'
@object_base.remotable_classmethod
def get(cls, *args, **kwargs):
pass
class DummySubclassedObject(MyObj):
fields = {'new_field': str}
@base.WatcherObjectRegistry.register_if(False)
class WatcherTestSubclassedObject(MyObj):
fields = {'new_field': fields.StringField()}
class TestMetaclass(test_base.TestCase):
def test_obj_tracking(self):
@six.add_metaclass(base.WatcherObjectMetaclass)
class NewBaseClass(object):
fields = {}
@classmethod
def obj_name(cls):
return cls.__name__
class Test1(NewBaseClass):
@staticmethod
def obj_name():
return 'fake1'
class Test2(NewBaseClass):
pass
class Test2v2(NewBaseClass):
@staticmethod
def obj_name():
return 'Test2'
expected = {'fake1': [Test1], 'Test2': [Test2, Test2v2]}
self.assertEqual(expected, NewBaseClass._obj_classes)
# The following should work, also.
self.assertEqual(expected, Test1._obj_classes)
self.assertEqual(expected, Test2._obj_classes)
class _LocalTest(test_base.TestCase):
def setUp(self):
super(_LocalTest, self).setUp()
# Just in case
base.WatcherObject.indirection_api = None
class TestUtils(test_base.TestCase):
def test_datetime_or_none(self):
naive_dt = datetime.datetime.now()
dt = timeutils.parse_isotime(timeutils.isotime(naive_dt))
self.assertEqual(dt, utils.datetime_or_none(dt, tzinfo_aware=True))
self.assertEqual(naive_dt.replace(tzinfo=iso8601.iso8601.Utc(),
microsecond=0),
utils.datetime_or_none(dt, tzinfo_aware=True))
self.assertIsNone(utils.datetime_or_none(None))
self.assertRaises(ValueError, utils.datetime_or_none, 'foo')
def test_datetime_or_none_tzinfo_naive(self):
naive_dt = datetime.datetime.utcnow()
self.assertEqual(naive_dt, utils.datetime_or_none(naive_dt,
tzinfo_aware=False))
self.assertIsNone(utils.datetime_or_none(None))
self.assertRaises(ValueError, utils.datetime_or_none, 'foo')
def test_datetime_or_str_or_none(self):
dts = timeutils.isotime()
dt = timeutils.parse_isotime(dts)
self.assertEqual(dt, utils.datetime_or_str_or_none(dt,
tzinfo_aware=True))
self.assertIsNone(utils.datetime_or_str_or_none(None,
tzinfo_aware=True))
self.assertEqual(dt, utils.datetime_or_str_or_none(dts,
tzinfo_aware=True))
self.assertRaises(ValueError, utils.datetime_or_str_or_none, 'foo')
def test_int_or_none(self):
self.assertEqual(1, utils.int_or_none(1))
self.assertEqual(1, utils.int_or_none('1'))
self.assertIsNone(utils.int_or_none(None))
self.assertRaises(ValueError, utils.int_or_none, 'foo')
def test_str_or_none(self):
class Obj(object):
pass
self.assertEqual('foo', utils.str_or_none('foo'))
self.assertEqual('1', utils.str_or_none(1))
self.assertIsNone(utils.str_or_none(None))
def test_ip_or_none(self):
ip4 = netaddr.IPAddress('1.2.3.4', 4)
ip6 = netaddr.IPAddress('1::2', 6)
self.assertEqual(ip4, utils.ip_or_none(4)('1.2.3.4'))
self.assertEqual(ip6, utils.ip_or_none(6)('1::2'))
self.assertIsNone(utils.ip_or_none(4)(None))
self.assertIsNone(utils.ip_or_none(6)(None))
self.assertRaises(netaddr.AddrFormatError, utils.ip_or_none(4), 'foo')
self.assertRaises(netaddr.AddrFormatError, utils.ip_or_none(6), 'foo')
def test_dt_serializer(self):
class Obj(object):
foo = utils.dt_serializer('bar')
obj = Obj()
obj.bar = timeutils.parse_isotime('1955-11-05T00:00:00Z')
self.assertEqual('1955-11-05T00:00:00Z', obj.foo())
obj.bar = None
self.assertIsNone(obj.foo())
obj.bar = 'foo'
self.assertRaises(AttributeError, obj.foo)
def test_dt_deserializer(self):
dt = timeutils.parse_isotime('1955-11-05T00:00:00Z')
self.assertEqual(dt, utils.dt_deserializer(timeutils.isotime(dt)))
self.assertIsNone(utils.dt_deserializer(None))
self.assertRaises(ValueError, utils.dt_deserializer, 'foo')
def test_obj_to_primitive_list(self):
class MyList(base.ObjectListBase, base.WatcherObject):
pass
mylist = MyList(self.context)
mylist.objects = [1, 2, 3]
self.assertEqual([1, 2, 3], base.obj_to_primitive(mylist))
def test_obj_to_primitive_dict(self):
myobj = MyObj(self.context)
myobj.foo = 1
myobj.bar = 'foo'
self.assertEqual({'foo': 1, 'bar': 'foo'},
base.obj_to_primitive(myobj))
def test_obj_to_primitive_recursive(self):
class MyList(base.ObjectListBase, base.WatcherObject):
pass
mylist = MyList(self.context)
mylist.objects = [MyObj(self.context), MyObj(self.context)]
for i, value in enumerate(mylist):
value.foo = i
self.assertEqual([{'foo': 0}, {'foo': 1}],
base.obj_to_primitive(mylist))
@contextlib.contextmanager
def things_temporarily_local():
# Temporarily go non-remote so the conductor handles
# this request directly
_api = base.WatcherObject.indirection_api
base.WatcherObject.indirection_api = None
yield
base.WatcherObject.indirection_api = _api
class TestObjectListBase(test_base.TestCase):
class _TestObject(object):
def test_hydration_type_error(self):
primitive = {'watcher_object.name': 'MyObj',
'watcher_object.namespace': 'watcher',
'watcher_object.version': '1.5',
'watcher_object.data': {'foo': 'a'}}
self.assertRaises(ValueError, MyObj.obj_from_primitive, primitive)
def test_list_like_operations(self):
class Foo(base.ObjectListBase, base.WatcherObject):
pass
def test_hydration(self):
primitive = {'watcher_object.name': 'MyObj',
'watcher_object.namespace': 'watcher',
'watcher_object.version': '1.5',
'watcher_object.data': {'foo': 1}}
obj = MyObj.obj_from_primitive(primitive)
self.assertEqual(1, obj.foo)
objlist = Foo(self.context)
objlist._context = 'foo'
objlist.objects = [1, 2, 3]
self.assertEqual(list(objlist), objlist.objects)
self.assertEqual(3, len(objlist))
self.assertIn(2, objlist)
self.assertEqual([1], list(objlist[:1]))
self.assertEqual('foo', objlist[:1]._context)
self.assertEqual(3, objlist[2])
self.assertEqual(1, objlist.count(1))
self.assertEqual(1, objlist.index(2))
def test_hydration_bad_ns(self):
primitive = {'watcher_object.name': 'MyObj',
'watcher_object.namespace': 'foo',
'watcher_object.version': '1.5',
'watcher_object.data': {'foo': 1}}
self.assertRaises(object_exception.UnsupportedObjectError,
MyObj.obj_from_primitive, primitive)
def test_serialization(self):
class Foo(base.ObjectListBase, base.WatcherObject):
pass
class Bar(base.WatcherObject):
fields = {'foo': str}
obj = Foo(self.context)
obj.objects = []
for i in 'abc':
bar = Bar(self.context)
bar.foo = i
obj.objects.append(bar)
obj2 = base.WatcherObject.obj_from_primitive(obj.obj_to_primitive())
self.assertFalse(obj is obj2)
self.assertEqual([x.foo for x in obj],
[y.foo for y in obj2])
def _test_object_list_version_mappings(self, list_obj_class):
# Figure out what sort of object this list is for
list_field = list_obj_class.fields['objects']
item_obj_field = list_field._type._element_type
item_obj_name = item_obj_field._type._obj_name
# Look through all object classes of this type and make sure that
# the versions we find are covered by the parent list class
for item_class in base.WatcherObject._obj_classes[item_obj_name]:
self.assertIn(
item_class.VERSION,
list_obj_class.child_versions.values())
def test_object_version_mappings(self):
# Find all object list classes and make sure that they at least handle
# all the current object versions
for obj_classes in base.WatcherObject._obj_classes.values():
for obj_class in obj_classes:
if issubclass(obj_class, base.ObjectListBase):
self._test_object_list_version_mappings(obj_class)
def test_list_changes(self):
class Foo(base.ObjectListBase, base.WatcherObject):
pass
class Bar(base.WatcherObject):
fields = {'foo': str}
obj = Foo(self.context, objects=[])
self.assertEqual(set(['objects']), obj.obj_what_changed())
obj.objects.append(Bar(self.context, foo='test'))
self.assertEqual(set(['objects']), obj.obj_what_changed())
def test_dehydration(self):
expected = {'watcher_object.name': 'MyObj',
'watcher_object.namespace': 'watcher',
'watcher_object.version': '1.5',
'watcher_object.data': {'foo': 1}}
obj = MyObj(self.context)
obj.foo = 1
obj.obj_reset_changes()
# This should still look dirty because the child is dirty
self.assertEqual(set(['objects']), obj.obj_what_changed())
obj.objects[0].obj_reset_changes()
# This should now look clean because the child is clean
self.assertEqual(set(), obj.obj_what_changed())
self.assertEqual(expected, obj.obj_to_primitive())
def test_get_updates(self):
obj = MyObj(self.context)
self.assertEqual({}, obj.obj_get_changes())
obj.foo = 123
self.assertEqual({'foo': 123}, obj.obj_get_changes())
obj.bar = 'test'
self.assertEqual({'foo': 123, 'bar': 'test'}, obj.obj_get_changes())
obj.obj_reset_changes()
self.assertEqual({}, obj.obj_get_changes())
def test_object_property(self):
obj = MyObj(self.context, foo=1)
self.assertEqual(1, obj.foo)
def test_object_property_type_error(self):
obj = MyObj(self.context)
def fail():
obj.foo = 'a'
self.assertRaises(ValueError, fail)
def test_load(self):
obj = MyObj(self.context)
self.assertEqual('loaded!', obj.bar)
def test_load_in_base(self):
@base.WatcherObjectRegistry.register_if(False)
class Foo(base.WatcherPersistentObject, base.WatcherObject,
base.WatcherObjectDictCompat):
fields = {'foobar': fields.IntegerField()}
obj = Foo(self.context)
self.assertRaisesRegex(
NotImplementedError, "Cannot load 'foobar' in the base class",
getattr, obj, 'foobar')
def test_loaded_in_primitive(self):
obj = MyObj(self.context)
obj.foo = 1
obj.obj_reset_changes()
self.assertEqual('loaded!', obj.bar)
expected = {'watcher_object.name': 'MyObj',
'watcher_object.namespace': 'watcher',
'watcher_object.version': '1.5',
'watcher_object.changes': ['bar'],
'watcher_object.data': {'foo': 1,
'bar': 'loaded!'}}
self.assertEqual(expected, obj.obj_to_primitive())
def test_changes_in_primitive(self):
obj = MyObj(self.context)
obj.foo = 123
self.assertEqual(set(['foo']), obj.obj_what_changed())
primitive = obj.obj_to_primitive()
self.assertIn('watcher_object.changes', primitive)
obj2 = MyObj.obj_from_primitive(primitive)
self.assertEqual(set(['foo']), obj2.obj_what_changed())
obj2.obj_reset_changes()
self.assertEqual(set(), obj2.obj_what_changed())
def test_unknown_objtype(self):
self.assertRaises(object_exception.UnsupportedObjectError,
base.WatcherObject.obj_class_from_name, 'foo', '1.0')
def test_with_alternate_context(self):
ctxt1 = context.RequestContext('foo', 'foo')
ctxt2 = context.RequestContext(user='alternate')
obj = MyObj.query(ctxt1)
obj.update_test(ctxt2)
self.assertEqual('alternate-context', obj.bar)
def test_orphaned_object(self):
obj = MyObj.query(self.context)
obj._context = None
self.assertRaises(object_exception.OrphanedObjectError,
obj.update_test)
def test_changed_1(self):
obj = MyObj.query(self.context)
obj.foo = 123
self.assertEqual(set(['foo']), obj.obj_what_changed())
obj.update_test(self.context)
self.assertEqual(set(['foo', 'bar']), obj.obj_what_changed())
self.assertEqual(123, obj.foo)
def test_changed_2(self):
obj = MyObj.query(self.context)
obj.foo = 123
self.assertEqual(set(['foo']), obj.obj_what_changed())
obj.save()
self.assertEqual(set([]), obj.obj_what_changed())
self.assertEqual(123, obj.foo)
def test_changed_3(self):
obj = MyObj.query(self.context)
obj.foo = 123
self.assertEqual(set(['foo']), obj.obj_what_changed())
obj.refresh()
self.assertEqual(set([]), obj.obj_what_changed())
self.assertEqual(321, obj.foo)
self.assertEqual('refreshed', obj.bar)
def test_changed_4(self):
obj = MyObj.query(self.context)
obj.bar = 'something'
self.assertEqual(set(['bar']), obj.obj_what_changed())
obj.modify_save_modify(self.context)
self.assertEqual(set(['foo']), obj.obj_what_changed())
self.assertEqual(42, obj.foo)
self.assertEqual('meow', obj.bar)
def test_static_result(self):
obj = MyObj.query(self.context)
self.assertEqual('bar', obj.bar)
result = obj.marco()
self.assertEqual('polo', result)
def test_updates(self):
obj = MyObj.query(self.context)
self.assertEqual(1, obj.foo)
obj.update_test()
self.assertEqual('updated', obj.bar)
def test_base_attributes(self):
dt = datetime.datetime(1955, 11, 5, 0, 0, tzinfo=iso8601.iso8601.Utc())
datatime = fields.DateTimeField()
obj = MyObj(self.context)
obj.created_at = dt
obj.updated_at = dt
expected = {'watcher_object.name': 'MyObj',
'watcher_object.namespace': 'watcher',
'watcher_object.version': '1.5',
'watcher_object.changes':
['created_at', 'updated_at'],
'watcher_object.data':
{'created_at': datatime.stringify(dt),
'updated_at': datatime.stringify(dt),
}
}
actual = obj.obj_to_primitive()
# watcher_object.changes is built from a set and order is undefined
self.assertEqual(sorted(expected['watcher_object.changes']),
sorted(actual['watcher_object.changes']))
del expected[
'watcher_object.changes'], actual['watcher_object.changes']
self.assertEqual(expected, actual)
def test_contains(self):
obj = MyObj(self.context)
self.assertNotIn('foo', obj)
obj.foo = 1
self.assertIn('foo', obj)
self.assertNotIn('does_not_exist', obj)
def test_obj_attr_is_set(self):
obj = MyObj(self.context, foo=1)
self.assertTrue(obj.obj_attr_is_set('foo'))
self.assertFalse(obj.obj_attr_is_set('bar'))
self.assertRaises(AttributeError, obj.obj_attr_is_set, 'bang')
def test_get(self):
obj = MyObj(self.context, foo=1)
# Foo has value, should not get the default
self.assertEqual(obj.get('foo', 2), 1)
# Foo has value, should return the value without error
self.assertEqual(obj.get('foo'), 1)
# Bar is not loaded, so we should get the default
self.assertEqual(obj.get('bar', 'not-loaded'), 'not-loaded')
# Bar without a default should lazy-load
self.assertEqual(obj.get('bar'), 'loaded!')
# Bar now has a default, but loaded value should be returned
self.assertEqual(obj.get('bar', 'not-loaded'), 'loaded!')
# Invalid attribute should raise AttributeError
self.assertRaises(AttributeError, obj.get, 'nothing')
# ...even with a default
self.assertRaises(AttributeError, obj.get, 'nothing', 3)
def test_object_inheritance(self):
base_fields = (
list(base.WatcherObject.fields) +
list(base.WatcherPersistentObject.fields))
myobj_fields = ['foo', 'bar', 'missing'] + base_fields
myobj3_fields = ['new_field']
self.assertTrue(issubclass(WatcherTestSubclassedObject, MyObj))
self.assertEqual(len(myobj_fields), len(MyObj.fields))
self.assertEqual(set(myobj_fields), set(MyObj.fields.keys()))
self.assertEqual(len(myobj_fields) + len(myobj3_fields),
len(WatcherTestSubclassedObject.fields))
self.assertEqual(set(myobj_fields) | set(myobj3_fields),
set(WatcherTestSubclassedObject.fields.keys()))
def test_get_changes(self):
obj = MyObj(self.context)
self.assertEqual({}, obj.obj_get_changes())
obj.foo = 123
self.assertEqual({'foo': 123}, obj.obj_get_changes())
obj.bar = 'test'
self.assertEqual({'foo': 123, 'bar': 'test'}, obj.obj_get_changes())
obj.obj_reset_changes()
self.assertEqual({}, obj.obj_get_changes())
def test_obj_fields(self):
@base.WatcherObjectRegistry.register_if(False)
class TestObj(base.WatcherPersistentObject, base.WatcherObject,
base.WatcherObjectDictCompat):
fields = {'foo': fields.IntegerField()}
obj_extra_fields = ['bar']
@property
def bar(self):
return 'this is bar'
obj = TestObj(self.context)
self.assertEqual(set(['created_at', 'updated_at', 'deleted_at',
'foo', 'bar']),
set(obj.obj_fields))
def test_refresh_object(self):
@base.WatcherObjectRegistry.register_if(False)
class TestObj(base.WatcherPersistentObject, base.WatcherObject,
base.WatcherObjectDictCompat):
fields = {'foo': fields.IntegerField(),
'bar': fields.StringField()}
obj = TestObj(self.context)
current_obj = TestObj(self.context)
obj.foo = 10
obj.bar = 'obj.bar'
current_obj.foo = 2
current_obj.bar = 'current.bar'
obj.obj_refresh(current_obj)
self.assertEqual(obj.foo, 2)
self.assertEqual(obj.bar, 'current.bar')
def test_obj_constructor(self):
obj = MyObj(self.context, foo=123, bar='abc')
self.assertEqual(123, obj.foo)
self.assertEqual('abc', obj.bar)
self.assertEqual(set(['foo', 'bar']), obj.obj_what_changed())
def test_assign_value_without_DictCompat(self):
class TestObj(base.WatcherObject):
fields = {'foo': fields.IntegerField(),
'bar': fields.StringField()}
obj = TestObj(self.context)
obj.foo = 10
err_message = ''
try:
obj['bar'] = 'value'
except TypeError as e:
err_message = six.text_type(e)
finally:
self.assertIn("'TestObj' object does not support item assignment",
err_message)
class TestObject(_LocalTest, _TestObject):
pass
# The hashes are help developers to check if the change of objects need a
# version bump. It is md5 hash of object fields and remotable methods.
# The fingerprint values should only be changed if there is a version bump.
expected_object_fingerprints = {
'Goal': '1.0-93881622db05e7b67a65ca885b4a022e',
'Strategy': '1.0-e60f62cc854c6e63fb1c3befbfc8629e',
'AuditTemplate': '1.0-7432ee4d3ce0c7cbb9d11a4565ee8eb6',
'Audit': '1.0-ebfc5360d019baf583a10a8a27071c97',
'ActionPlan': '1.0-cc76fd7f0e8479aeff817dd266341de4',
'Action': '1.0-a78f69c0da98e13e601f9646f6b2f883',
'EfficacyIndicator': '1.0-655b71234a82bc7478aff964639c4bb0',
'ScoringEngine': '1.0-4abbe833544000728e17bd9e83f97576',
'Service': '1.0-4b35b99ada9677a882c9de2b30212f35',
'MyObj': '1.5-23c516d1e842f365f694e688d34e47c3',
}
class TestObjectVersions(test_base.TestCase):
def test_object_version_check(self):
classes = base.WatcherObjectRegistry.obj_classes()
checker = object_fixture.ObjectVersionChecker(obj_classes=classes)
# Compute the difference between actual fingerprints and
# expect fingerprints. expect = actual = {} if there is no change.
expect, actual = checker.test_hashes(expected_object_fingerprints)
self.assertEqual(expect, actual,
"Some objects fields or remotable methods have been "
"modified. Please make sure the version of those "
"objects have been bumped and then update "
"expected_object_fingerprints with the new hashes. ")
class TestObjectSerializer(test_base.TestCase):
def test_serialize_entity_primitive(self):
ser = base.WatcherObjectSerializer()
for thing in (1, 'foo', [1, 2], {'foo': 'bar'}):
self.assertEqual(thing, ser.serialize_entity(None, thing))
def test_deserialize_entity_primitive(self):
ser = base.WatcherObjectSerializer()
for thing in (1, 'foo', [1, 2], {'foo': 'bar'}):
self.assertEqual(thing, ser.deserialize_entity(None, thing))
def test_object_serialization(self):
ser = base.WatcherObjectSerializer()
obj = MyObj(self.context)
@@ -321,3 +461,83 @@ class TestObjectSerializer(test_base.TestCase):
self.assertEqual(1, len(thing2))
for item in thing2:
self.assertIsInstance(item, MyObj)
@mock.patch('watcher.objects.base.WatcherObject.indirection_api')
def _test_deserialize_entity_newer(self, obj_version, backported_to,
mock_indirection_api,
my_version='1.6'):
ser = base.WatcherObjectSerializer()
mock_indirection_api.object_backport_versions.return_value \
= 'backported'
@base.WatcherObjectRegistry.register
class MyTestObj(MyObj):
VERSION = my_version
obj = MyTestObj(self.context)
obj.VERSION = obj_version
primitive = obj.obj_to_primitive()
result = ser.deserialize_entity(self.context, primitive)
if backported_to is None:
self.assertFalse(
mock_indirection_api.object_backport_versions.called)
else:
self.assertEqual('backported', result)
versions = object_base.obj_tree_get_versions('MyTestObj')
mock_indirection_api.object_backport_versions.assert_called_with(
self.context, primitive, versions)
def test_deserialize_entity_newer_version_backports(self):
"Test object with unsupported (newer) version"
self._test_deserialize_entity_newer('1.25', '1.6')
def test_deserialize_entity_same_revision_does_not_backport(self):
"Test object with supported revision"
self._test_deserialize_entity_newer('1.6', None)
def test_deserialize_entity_newer_revision_does_not_backport_zero(self):
"Test object with supported revision"
self._test_deserialize_entity_newer('1.6.0', None)
def test_deserialize_entity_newer_revision_does_not_backport(self):
"Test object with supported (newer) revision"
self._test_deserialize_entity_newer('1.6.1', None)
def test_deserialize_entity_newer_version_passes_revision(self):
"Test object with unsupported (newer) version and revision"
self._test_deserialize_entity_newer('1.7', '1.6.1', my_version='1.6.1')
class TestRegistry(test_base.TestCase):
@mock.patch('watcher.objects.base.objects')
def test_hook_chooses_newer_properly(self, mock_objects):
reg = base.WatcherObjectRegistry()
reg.registration_hook(MyObj, 0)
class MyNewerObj(object):
VERSION = '1.123'
@classmethod
def obj_name(cls):
return 'MyObj'
self.assertEqual(MyObj, mock_objects.MyObj)
reg.registration_hook(MyNewerObj, 0)
self.assertEqual(MyNewerObj, mock_objects.MyObj)
@mock.patch('watcher.objects.base.objects')
def test_hook_keeps_newer_properly(self, mock_objects):
reg = base.WatcherObjectRegistry()
reg.registration_hook(MyObj, 0)
class MyOlderObj(object):
VERSION = '1.1'
@classmethod
def obj_name(cls):
return 'MyObj'
self.assertEqual(MyObj, mock_objects.MyObj)
reg.registration_hook(MyOlderObj, 0)
self.assertEqual(MyObj, mock_objects.MyObj)

View File

@@ -14,7 +14,6 @@
# under the License.
import mock
from testtools import matchers
from watcher import objects
from watcher.tests.db import base
@@ -43,7 +42,7 @@ class TestServiceObject(base.DbTestCase):
mock_get_list.return_value = [self.fake_service]
services = objects.Service.list(self.context)
self.assertEqual(1, mock_get_list.call_count, 1)
self.assertThat(services, matchers.HasLength(1))
self.assertEqual(1, len(services))
self.assertIsInstance(services[0], objects.Service)
self.assertEqual(self.context, services[0]._context)