From 4dd2a188f19f6bd047f1797d4117e238b0c05bbd Mon Sep 17 00:00:00 2001 From: yuntongjin Date: Thu, 12 Mar 2015 16:11:24 +0800 Subject: [PATCH] Versioned object - WatchRule and WatchData Prototype implementation of oslo.versionedobjects. This patch adds WatchRule and WatchData object. Implements: blueprint versioned-objects Co-Authored-By: ShaoHe Feng Co-Authored-By: He Jie Xu Co-Authored-By: Grzegorz Grasza Change-Id: I29fc0fc99c0c425291246dc3889393cbcc0b82cf --- heat/db/api.py | 4 ++ heat/db/sqlalchemy/api.py | 6 +++ heat/engine/service.py | 9 ++-- heat/engine/watchrule.py | 21 +++++---- heat/objects/watch_data.py | 66 ++++++++++++++++++++++++++++ heat/objects/watch_rule.py | 90 ++++++++++++++++++++++++++++++++++++++ heat/tests/test_watch.py | 14 +++--- 7 files changed, 192 insertions(+), 18 deletions(-) create mode 100644 heat/objects/watch_data.py create mode 100644 heat/objects/watch_rule.py diff --git a/heat/db/api.py b/heat/db/api.py index 5e4e9d971..77d98d988 100644 --- a/heat/db/api.py +++ b/heat/db/api.py @@ -256,6 +256,10 @@ def watch_data_get_all(context): return IMPL.watch_data_get_all(context) +def watch_data_get_all_by_watch_rule_id(context, watch_rule_id): + return IMPL.watch_data_get_all_by_watch_rule_id(context, watch_rule_id) + + def software_config_create(context, values): return IMPL.software_config_create(context, values) diff --git a/heat/db/sqlalchemy/api.py b/heat/db/sqlalchemy/api.py index c6d675ba9..520d18269 100644 --- a/heat/db/sqlalchemy/api.py +++ b/heat/db/sqlalchemy/api.py @@ -725,6 +725,12 @@ def watch_data_get_all(context): return results +def watch_data_get_all_by_watch_rule_id(context, watch_rule_id): + results = model_query(context, models.WatchData).filter_by( + watch_rule_id=watch_rule_id).all() + return results + + def software_config_create(context, values): obj_ref = models.SoftwareConfig() obj_ref.update(values) diff --git a/heat/engine/service.py b/heat/engine/service.py index 66e381ca7..56377d666 100644 --- a/heat/engine/service.py +++ b/heat/engine/service.py @@ -35,7 +35,6 @@ from heat.common.i18n import _LW from heat.common import identifier from heat.common import messaging as rpc_messaging from heat.common import service_utils -from heat.db import api as db_api from heat.engine import api from heat.engine import attributes from heat.engine import clients @@ -55,6 +54,8 @@ from heat.objects import event as event_object from heat.objects import resource as resource_objects from heat.objects import snapshot as snapshot_object from heat.objects import stack as stack_object +from heat.objects import watch_data +from heat.objects import watch_rule from heat.openstack.common import service from heat.openstack.common import threadgroup from heat.rpc import api as rpc_api @@ -1335,7 +1336,7 @@ class EngineService(service.Service): if watch_name: yield watchrule.WatchRule.load(cnxt, watch_name) else: - for wr in db_api.watch_rule_get_all(cnxt): + for wr in watch_rule.WatchRule.get_all(cnxt): if watchrule.rule_can_use_sample(wr, stats_data): yield watchrule.WatchRule.load(cnxt, watch=wr) @@ -1364,7 +1365,7 @@ class EngineService(service.Service): wrn = [watch_name] else: try: - wrn = [w.name for w in db_api.watch_rule_get_all(cnxt)] + wrn = [w.name for w in watch_rule.WatchRule.get_all(cnxt)] except Exception as ex: LOG.warn(_LW('show_watch (all) db error %s'), ex) return @@ -1393,7 +1394,7 @@ class EngineService(service.Service): return try: - wds = db_api.watch_data_get_all(cnxt) + wds = watch_data.WatchData.get_all(cnxt) except Exception as ex: LOG.warn(_LW('show_metric (all) db error %s'), ex) return diff --git a/heat/engine/watchrule.py b/heat/engine/watchrule.py index 617ede564..d7745a19c 100644 --- a/heat/engine/watchrule.py +++ b/heat/engine/watchrule.py @@ -21,10 +21,11 @@ from heat.common import exception from heat.common.i18n import _ from heat.common.i18n import _LI from heat.common.i18n import _LW -from heat.db import api as db_api from heat.engine import stack from heat.engine import timestamp from heat.objects import stack as stack_object +from heat.objects import watch_data as watch_data_objects +from heat.objects import watch_rule as watch_rule_objects from heat.rpc import api as rpc_api LOG = logging.getLogger(__name__) @@ -48,8 +49,10 @@ class WatchRule(object): NORMAL: 'OKActions', NODATA: 'InsufficientDataActions'} - created_at = timestamp.Timestamp(db_api.watch_rule_get, 'created_at') - updated_at = timestamp.Timestamp(db_api.watch_rule_get, 'updated_at') + created_at = timestamp.Timestamp(watch_rule_objects.WatchRule.get_by_id, + 'created_at') + updated_at = timestamp.Timestamp(watch_rule_objects.WatchRule.get_by_id, + 'updated_at') def __init__(self, context, watch_name, rule, stack_id=None, state=NODATA, wid=None, watch_data=None, @@ -77,7 +80,8 @@ class WatchRule(object): ''' if watch is None: try: - watch = db_api.watch_rule_get_by_name(context, watch_name) + watch = watch_rule_objects.WatchRule.get_by_name(context, + watch_name) except Exception as ex: LOG.warn(_LW('WatchRule.load (%(watch_name)s) db error ' '%(ex)s'), {'watch_name': watch_name, 'ex': ex}) @@ -107,17 +111,18 @@ class WatchRule(object): } if not self.id: - wr = db_api.watch_rule_create(self.context, wr_values) + wr = watch_rule_objects.WatchRule.create(self.context, wr_values) self.id = wr.id else: - db_api.watch_rule_update(self.context, self.id, wr_values) + watch_rule_objects.WatchRule.update_by_id(self.context, self.id, + wr_values) def destroy(self): ''' Delete the watchrule from the database. ''' if self.id: - db_api.watch_rule_delete(self.context, self.id) + watch_rule_objects.WatchRule.delete(self.context, self.id) def do_data_cmp(self, data, threshold): op = self.rule['ComparisonOperator'] @@ -320,7 +325,7 @@ class WatchRule(object): 'data': data, 'watch_rule_id': self.id } - wd = db_api.watch_data_create(None, watch_data) + wd = watch_data_objects.WatchData.create(self.context, watch_data) LOG.debug('new watch:%(name)s data:%(data)s' % {'name': self.name, 'data': str(wd.data)}) diff --git a/heat/objects/watch_data.py b/heat/objects/watch_data.py new file mode 100644 index 000000000..4f0efc358 --- /dev/null +++ b/heat/objects/watch_data.py @@ -0,0 +1,66 @@ +# Copyright 2014 Intel 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 +# 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. + +""" +WatchData object +""" + +from oslo_versionedobjects import base +from oslo_versionedobjects import fields + +from heat.db import api as db_api +from heat.objects import fields as heat_fields + + +class WatchData(base.VersionedObject, base.VersionedObjectDictCompat): + + fields = { + 'id': fields.IntegerField(nullable=False), + 'data': heat_fields.JsonField(nullable=True), + 'watch_rule_id': fields.StringField(nullable=False), + 'watch_rule': fields.ObjectField('WatchRule'), + 'created_at': fields.DateTimeField(read_only=True), + 'updated_at': fields.DateTimeField(nullable=True), + } + + @staticmethod + def _from_db_object(context, rule, db_data): + from heat.objects import watch_rule + for field in rule.fields: + if field == 'watch_rule': + rule[field] = watch_rule.WatchRule._from_db_object( + context, + watch_rule.WatchRule(), + db_data['watch_rule']) + else: + rule[field] = db_data[field] + rule._context = context + rule.obj_reset_changes() + return rule + + @classmethod + def create(cls, context, values): + db_data = db_api.watch_data_create(context, values) + return cls._from_db_object(context, cls(), db_data) + + @classmethod + def get_all(cls, context): + return [cls._from_db_object(context, cls(), db_data) + for db_data in db_api.watch_data_get_all(context)] + + @classmethod + def get_all_by_watch_rule_id(cls, context, watch_rule_id): + return (cls._from_db_object(context, cls(), db_data) + for db_data in db_api.watch_data_get_all_by_watch_rule_id( + context, watch_rule_id)) diff --git a/heat/objects/watch_rule.py b/heat/objects/watch_rule.py new file mode 100644 index 000000000..d63cd4a07 --- /dev/null +++ b/heat/objects/watch_rule.py @@ -0,0 +1,90 @@ +# Copyright 2014 Intel 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 +# 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. + +""" +WatchRule object +""" + +from oslo_versionedobjects import base +from oslo_versionedobjects import fields + +from heat.db import api as db_api +from heat.objects import fields as heat_fields +from heat.objects import stack +from heat.objects import watch_data + + +class WatchRule(base.VersionedObject, base.VersionedObjectDictCompat): + + fields = { + 'id': fields.IntegerField(nullable=False), + 'name': fields.StringField(nullable=True), + 'rule': heat_fields.JsonField(nullable=True), + 'state': fields.StringField(nullable=True), + 'last_evaluated': fields.DateTimeField(nullable=True), + 'stack_id': fields.StringField(nullable=False), + 'stack': fields.ObjectField(stack.Stack), + 'watch_data': fields.ListOfObjectsField(watch_data.WatchData), + 'created_at': fields.DateTimeField(read_only=True), + 'updated_at': fields.DateTimeField(nullable=True), + } + + @staticmethod + def _from_db_object(context, rule, db_rule): + for field in rule.fields: + if field == 'stack': + rule[field] = stack.Stack._from_db_object( + context, stack.Stack(), db_rule[field]) + elif field == 'watch_data': + rule[field] = watch_data.WatchData.get_all_by_watch_rule_id( + context, db_rule['id']) + else: + rule[field] = db_rule[field] + rule._context = context + rule.obj_reset_changes() + return rule + + @classmethod + def get_by_id(cls, context, rule_id): + db_rule = db_api.watch_rule_get(context, rule_id) + return cls._from_db_object(context, cls(), db_rule) + + @classmethod + def get_by_name(cls, context, watch_rule_name): + db_rule = db_api.watch_rule_get_by_name(context, watch_rule_name) + return cls._from_db_object(context, cls(), db_rule) + + @classmethod + def get_all(cls, context): + return [cls._from_db_object(context, cls(), db_rule) + for db_rule in db_api.watch_rule_get_all(context)] + + @classmethod + def get_all_by_stack(cls, context, stack_id): + return [cls._from_db_object(context, cls(), db_rule) + for db_rule in db_api.watch_rule_get_all_by_stack(context, + stack_id)] + + @classmethod + def update_by_id(cls, context, watch_id, values): + db_api.watch_rule_update(context, watch_id, values) + + @classmethod + def create(cls, context, values): + return cls._from_db_object(context, cls(), + db_api.watch_rule_create(context, values)) + + @classmethod + def delete(cls, context, watch_id): + db_api.watch_rule_delete(context, watch_id) diff --git a/heat/tests/test_watch.py b/heat/tests/test_watch.py index 62ee9c284..d13e78b1a 100644 --- a/heat/tests/test_watch.py +++ b/heat/tests/test_watch.py @@ -18,10 +18,10 @@ import mox from oslo_utils import timeutils from heat.common import exception -from heat.db import api as db_api from heat.engine import parser from heat.engine import template from heat.engine import watchrule +from heat.objects import watch_rule from heat.tests import common from heat.tests import utils @@ -321,7 +321,7 @@ class WatchRuleTest(common.HeatTestCase): stack_id=self.stack_id, rule=rule) self.wr.store() - dbwr = db_api.watch_rule_get_by_name(self.ctx, 'storetest') + dbwr = watch_rule.WatchRule.get_by_name(self.ctx, 'storetest') self.assertIsNotNone(dbwr) self.assertEqual('storetest', dbwr.name) self.assertEqual(watchrule.WatchRule.NODATA, dbwr.state) @@ -624,8 +624,9 @@ class WatchRuleTest(common.HeatTestCase): "Dimensions": []}} self.wr.create_watch_data(data) - dbwr = db_api.watch_rule_get_by_name(self.ctx, 'create_data_test') - self.assertEqual(data, dbwr.watch_data[0].data) + obj_wr = watch_rule.WatchRule.get_by_name(self.ctx, 'create_data_test') + obj_wds = [wd for wd in obj_wr.watch_data] + self.assertEqual(data, obj_wds[0].data) # Note, would be good to write another datapoint and check it # but sqlite seems to not interpret the backreference correctly @@ -654,8 +655,9 @@ class WatchRuleTest(common.HeatTestCase): "Dimensions": []}} self.wr.create_watch_data(data) - dbwr = db_api.watch_rule_get_by_name(self.ctx, 'create_data_test') - self.assertEqual([], dbwr.watch_data) + obj_wr = watch_rule.WatchRule.get_by_name(self.ctx, 'create_data_test') + obj_wds = [wd for wd in obj_wr.watch_data] + self.assertEqual([], obj_wds) def test_create_watch_data_match(self): rule = {u'EvaluationPeriods': u'1',