diff --git a/heat/api/middleware/fault.py b/heat/api/middleware/fault.py index 6ff7d590c..e827c09cc 100644 --- a/heat/api/middleware/fault.py +++ b/heat/api/middleware/fault.py @@ -84,6 +84,12 @@ class FaultWrapper(wsgi.Middleware): 'Invalid': webob.exc.HTTPBadRequest, 'ResourcePropertyConflict': webob.exc.HTTPBadRequest, 'PropertyUnspecifiedError': webob.exc.HTTPBadRequest, + 'ObjectFieldInvalid': webob.exc.HTTPBadRequest, + 'ReadOnlyFieldError': webob.exc.HTTPBadRequest, + 'ObjectActionError': webob.exc.HTTPBadRequest, + 'IncompatibleObjectVersion': webob.exc.HTTPBadRequest, + 'OrphanedObjectError': webob.exc.HTTPBadRequest, + 'UnsupportedObjectError': webob.exc.HTTPBadRequest, } def _map_exception_to_error(self, class_exception): diff --git a/heat/common/exception.py b/heat/common/exception.py index 87ef18ffd..fd9337dd7 100644 --- a/heat/common/exception.py +++ b/heat/common/exception.py @@ -430,3 +430,27 @@ class EventSendFailed(HeatException): class ServiceNotFound(HeatException): msg_fmt = _("Service %(service_id)s does not found") + + +class UnsupportedObjectError(HeatException): + msg_fmt = _('Unsupported object type %(objtype)s') + + +class OrphanedObjectError(HeatException): + msg_fmt = _('Cannot call %(method)s on orphaned %(objtype)s object') + + +class IncompatibleObjectVersion(HeatException): + msg_fmt = _('Version %(objver)s of %(objname)s is not supported') + + +class ObjectActionError(HeatException): + msg_fmt = _('Object action %(action)s failed because: %(reason)s') + + +class ReadOnlyFieldError(HeatException): + msg_fmt = _('Cannot modify readonly field %(field)s') + + +class ObjectFieldInvalid(HeatException): + msg_fmt = _('Field %(field)s of %(objname)s is not an instance of Field') diff --git a/heat/engine/service.py b/heat/engine/service.py index 99f51ef59..79f7b6d61 100644 --- a/heat/engine/service.py +++ b/heat/engine/service.py @@ -51,6 +51,7 @@ from heat.engine import stack_lock from heat.engine import template as templatem from heat.engine import watchrule from heat.engine import worker +from heat.objects import stack as stack_object from heat.openstack.common import service from heat.openstack.common import threadgroup from heat.rpc import api as rpc_api @@ -306,7 +307,9 @@ class EngineService(service.Service): # Create a periodic_watcher_task per-stack admin_context = context.get_admin_context() - stacks = db_api.stack_get_all(admin_context, tenant_safe=False) + stacks = stack_object.Stack.get_all( + admin_context, + tenant_safe=False) for s in stacks: self.stack_watch.start_watch_task(s.id, admin_context) @@ -394,13 +397,16 @@ class EngineService(service.Service): :param stack_name: Name or UUID of the stack to look up. """ if uuidutils.is_uuid_like(stack_name): - s = db_api.stack_get(cnxt, stack_name, show_deleted=True) + s = stack_object.Stack.get_by_id( + cnxt, + stack_name, + show_deleted=True) # may be the name is in uuid format, so if get by id returns None, # we should get the info by name again if not s: - s = db_api.stack_get_by_name(cnxt, stack_name) + s = stack_object.Stack.get_by_name(cnxt, stack_name) else: - s = db_api.stack_get_by_name(cnxt, stack_name) + s = stack_object.Stack.get_by_name(cnxt, stack_name) if s: stack = parser.Stack.load(cnxt, stack=s) return dict(stack.identifier()) @@ -410,9 +416,11 @@ class EngineService(service.Service): def _get_stack(self, cnxt, stack_identity, show_deleted=False): identity = identifier.HeatIdentifier(**stack_identity) - s = db_api.stack_get(cnxt, identity.stack_id, - show_deleted=show_deleted, - eager_load=True) + s = stack_object.Stack.get_by_id( + cnxt, + identity.stack_id, + show_deleted=show_deleted, + eager_load=True) if s is None: raise exception.StackNotFound(stack_name=identity.stack_name) @@ -485,10 +493,12 @@ class EngineService(service.Service): :param show_nested: if true, count will include nested stacks :returns: a integer representing the number of matched stacks """ - return db_api.stack_count_all(cnxt, filters=filters, - tenant_safe=tenant_safe, - show_deleted=show_deleted, - show_nested=show_nested) + return stack_object.Stack.count_all( + cnxt, + filters=filters, + tenant_safe=tenant_safe, + show_deleted=show_deleted, + show_nested=show_nested) def _validate_deferred_auth_context(self, cnxt, stack): if cfg.CONF.deferred_auth_method != 'password': @@ -508,11 +518,11 @@ class EngineService(service.Service): except Exception as ex: raise exception.StackValidationFailed(message=six.text_type(ex)) - if db_api.stack_get_by_name(cnxt, stack_name): + if stack_object.Stack.get_by_name(cnxt, stack_name): raise exception.StackExists(stack_name=stack_name) tenant_limit = cfg.CONF.max_stacks_per_tenant - if db_api.stack_count_all(cnxt) >= tenant_limit: + if stack_object.Stack.count_all(cnxt) >= tenant_limit: message = _("You have reached the maximum stacks per tenant, %d." " Please delete some stacks.") % tenant_limit raise exception.RequestLimitExceeded(message=message) diff --git a/heat/engine/stack.py b/heat/engine/stack.py index 902c7ce30..c23df1159 100644 --- a/heat/engine/stack.py +++ b/heat/engine/stack.py @@ -41,6 +41,7 @@ from heat.engine import resources from heat.engine import scheduler from heat.engine import template as tmpl from heat.engine import update +from heat.objects import stack as stack_object from heat.rpc import api as rpc_api cfg.CONF.import_opt('error_wait_time', 'heat.common.config') @@ -284,9 +285,11 @@ class Stack(collections.Mapping): show_deleted=True, use_stored_context=False, force_reload=False): '''Retrieve a Stack from the database.''' if stack is None: - stack = db_api.stack_get(context, stack_id, - show_deleted=show_deleted, - eager_load=True) + stack = stack_object.Stack.get_by_id( + context, + stack_id, + show_deleted=show_deleted, + eager_load=True) if stack is None: message = _('No stack exists with id "%s"') % str(stack_id) raise exception.NotFound(message) @@ -302,9 +305,16 @@ class Stack(collections.Mapping): sort_dir=None, filters=None, tenant_safe=True, show_deleted=False, resolve_data=True, show_nested=False): - stacks = db_api.stack_get_all(context, limit, sort_keys, marker, - sort_dir, filters, tenant_safe, - show_deleted, show_nested) or [] + stacks = stack_object.Stack.get_all( + context, + limit, + sort_keys, + marker, + sort_dir, + filters, + tenant_safe, + show_deleted, + show_nested) or [] for stack in stacks: yield cls._from_db(context, stack, resolve_data=resolve_data) @@ -351,7 +361,7 @@ class Stack(collections.Mapping): 'current_traversal': self.current_traversal, } if self.id: - db_api.stack_update(self.context, self.id, s) + stack_object.Stack.update_by_id(self.context, self.id, s) else: if not self.user_creds_id: # Create a context containing a trust_id and trustor_user_id @@ -365,7 +375,7 @@ class Stack(collections.Mapping): s['user_creds_id'] = new_creds.id self.user_creds_id = new_creds.id - new_s = db_api.stack_create(self.context, s) + new_s = stack_object.Stack.create(self.context, s) self.id = new_s.id self.created_time = new_s.created_at @@ -561,7 +571,7 @@ class Stack(collections.Mapping): if self.id is None: return - stack = db_api.stack_get(self.context, self.id) + stack = stack_object.Stack.get_by_id(self.context, self.id) if stack is not None: stack.update_and_save({'action': action, 'status': status, @@ -706,9 +716,10 @@ class Stack(collections.Mapping): Get a Stack containing any in-progress resources from the previous stack state prior to an update. ''' - s = db_api.stack_get_by_name_and_owner_id(self.context, - self._backup_name(), - owner_id=self.id) + s = stack_object.Stack.get_by_name_and_owner_id( + self.context, + self._backup_name(), + owner_id=self.id) if s is not None: LOG.debug('Loaded existing backup stack') return self.load(self.context, stack=s) @@ -1060,7 +1071,7 @@ class Stack(collections.Mapping): if stack_status != self.FAILED: # delete the stack try: - db_api.stack_delete(self.context, self.id) + stack_object.Stack.delete(self.context, self.id) except exception.NotFound: LOG.info(_LI("Tried to delete stack that does not exist " "%s "), self.id) diff --git a/heat/engine/template.py b/heat/engine/template.py index 52c2f8454..2f3177a50 100644 --- a/heat/engine/template.py +++ b/heat/engine/template.py @@ -22,8 +22,8 @@ from stevedore import extension from heat.common import exception from heat.common.i18n import _ -from heat.db import api as db_api from heat.engine import environment +from heat.objects import raw_template as template_object LOG = logging.getLogger(__name__) @@ -125,7 +125,7 @@ class Template(collections.Mapping): def load(cls, context, template_id, t=None): '''Retrieve a Template with the given ID from the database.''' if t is None: - t = db_api.raw_template_get(context, template_id) + t = template_object.RawTemplate.get_by_id(context, template_id) env = environment.Environment(t.environment) return cls(t.template, template_id=template_id, files=t.files, env=env) @@ -137,10 +137,10 @@ class Template(collections.Mapping): 'environment': self.env.user_env_as_dict() } if self.id is None: - new_rt = db_api.raw_template_create(context, rt) + new_rt = template_object.RawTemplate.create(context, rt) self.id = new_rt.id else: - db_api.raw_template_update(context, self.id, rt) + template_object.RawTemplate.update_by_id(context, self.id, rt) return self.id def __iter__(self): diff --git a/heat/engine/watchrule.py b/heat/engine/watchrule.py index 00f61949e..617ede564 100644 --- a/heat/engine/watchrule.py +++ b/heat/engine/watchrule.py @@ -24,6 +24,7 @@ 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.rpc import api as rpc_api LOG = logging.getLogger(__name__) @@ -257,8 +258,10 @@ class WatchRule(object): if self.ACTION_MAP[new_state] not in self.rule: LOG.info(_LI('no action for new state %s'), new_state) else: - s = db_api.stack_get(self.context, self.stack_id, - eager_load=True) + s = stack_object.Stack.get_by_id( + self.context, + self.stack_id, + eager_load=True) stk = stack.Stack.load(self.context, stack=s) if (stk.action != stk.DELETE and stk.status == stk.COMPLETE): diff --git a/heat/objects/__init__.py b/heat/objects/__init__.py new file mode 100755 index 000000000..e69de29bb diff --git a/heat/objects/fields.py b/heat/objects/fields.py new file mode 100644 index 000000000..28a5dc02f --- /dev/null +++ b/heat/objects/fields.py @@ -0,0 +1,36 @@ +# 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. + + +from oslo_serialization import jsonutils as json +from oslo_versionedobjects import fields +import six + + +class Json(fields.FieldType): + def coerce(self, obj, attr, value): + if isinstance(value, six.string_types): + loaded = json.loads(value) + return loaded + return value + + def from_primitive(self, obj, attr, value): + return self.coerce(obj, attr, value) + + def to_primitive(self, obj, attr, value): + return json.dumps(value) + + +class JsonField(fields.AutoTypedField): + pass diff --git a/heat/objects/raw_template.py b/heat/objects/raw_template.py new file mode 100644 index 000000000..813ffe911 --- /dev/null +++ b/heat/objects/raw_template.py @@ -0,0 +1,60 @@ +# 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. + + +""" +RawTemplate 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 RawTemplate( + base.VersionedObject, + base.VersionedObjectDictCompat, + base.ComparableVersionedObject, +): + fields = { + 'id': fields.StringField(), + 'files': heat_fields.JsonField(nullable=True), + 'template': heat_fields.JsonField(), + 'environment': heat_fields.JsonField(), + 'predecessor': fields.IntegerField(), + } + + @staticmethod + def _from_db_object(context, tpl, db_tpl): + for field in tpl.fields: + tpl[field] = db_tpl[field] + tpl._context = context + tpl.obj_reset_changes() + return tpl + + @classmethod + def get_by_id(cls, context, template_id): + raw_template_db = db_api.raw_template_get(context, template_id) + raw_template = cls._from_db_object(context, cls(), raw_template_db) + return raw_template + + @classmethod + def create(cls, context, values): + return db_api.raw_template_create(context, values) + + @classmethod + def update_by_id(cls, context, template_id, values): + return db_api.raw_template_update(context, template_id, values) diff --git a/heat/objects/stack.py b/heat/objects/stack.py new file mode 100755 index 000000000..c7d26380e --- /dev/null +++ b/heat/objects/stack.py @@ -0,0 +1,133 @@ +# 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. + + +""" +Stack object +""" + +from oslo_versionedobjects import base +from oslo_versionedobjects import fields + + +from heat.db import api as db_api +from heat import objects +from heat.objects import fields as heat_fields + + +class Stack( + base.VersionedObject, + base.VersionedObjectDictCompat, + base.ComparableVersionedObject, +): + fields = { + 'id': fields.StringField(), + 'name': fields.StringField(), + 'raw_template_id': fields.IntegerField(), + 'backup': fields.BooleanField(), + 'created_at': fields.DateTimeField(read_only=True), + 'deleted_at': fields.DateTimeField(nullable=True), + 'disable_rollback': fields.BooleanField(), + 'nested_depth': fields.IntegerField(), + 'owner_id': fields.StringField(nullable=True), + 'stack_user_project_id': fields.StringField(nullable=True), + 'tenant': fields.StringField(nullable=True), + 'timeout': fields.IntegerField(nullable=True), + 'updated_at': fields.DateTimeField(nullable=True), + 'user_creds_id': fields.StringField(nullable=True), + 'username': fields.StringField(nullable=True), + 'action': fields.StringField(nullable=True), + 'status': fields.StringField(nullable=True), + 'status_reason': fields.StringField(nullable=True), + 'raw_template': fields.ObjectField('RawTemplate'), + 'convergence': fields.BooleanField(), + 'current_traversal': fields.StringField(), + 'current_deps': heat_fields.JsonField(), + 'prev_raw_template_id': fields.IntegerField(), + 'prev_raw_template': fields.ObjectField('RawTemplate'), + } + + @staticmethod + def _from_db_object(context, stack, db_stack): + for field in stack.fields: + if field == 'raw_template': + stack['raw_template'] = ( + objects.raw_template.RawTemplate.get_by_id( + context, db_stack['raw_template_id'])) + else: + stack[field] = db_stack[field] + stack._context = context + stack.obj_reset_changes() + return stack + + @classmethod + def get_by_id(cls, context, stack_id, **kwargs): + db_stack = db_api.stack_get(context, stack_id, **kwargs) + if not db_stack: + return db_stack + stack = cls._from_db_object(context, cls(context), db_stack) + return stack + + @classmethod + def get_by_name_and_owner_id(cls, context, stack_name, owner_id): + return db_api.stack_get_by_name_and_owner_id(context, stack_name, + owner_id) + + @classmethod + def get_by_name(cls, context, stack_name): + return db_api.stack_get_by_name(context, stack_name) + + @classmethod + def get_all(cls, context, *args, **kwargs): + return db_api.stack_get_all(context, *args, **kwargs) + + @classmethod + def get_all_by_owner_id(cls, context, owner_id): + return db_api.stack_get_all_by_owner_id(context, owner_id) + + @classmethod + def count_all(cls, context, **kwargs): + return db_api.stack_count_all(context, **kwargs) + + @classmethod + def create(cls, context, values): + return db_api.stack_create(context, values) + + @classmethod + def update_by_id(cls, context, stack_id, values): + return db_api.stack_update(context, stack_id, values) + + @classmethod + def delete(cls, context, stack_id): + return db_api.stack_delete(context, stack_id) + + def update_and_save(self, values): + db_stack = self.__class__.update_by_id(self._context, self.id, values) + self.refresh() + return db_stack + + def __eq__(self, another): + self.refresh() # to make test object comparison work well + return super(Stack, self).__eq__(another) + + def refresh(self): + return self.__class__._from_db_object( + self._context, + self, + db_api.stack_get( + self._context, + self.id, + show_deleted=True, + ), + ) diff --git a/heat/tests/test_engine_service.py b/heat/tests/test_engine_service.py index 7869c7aaa..706fb95c5 100644 --- a/heat/tests/test_engine_service.py +++ b/heat/tests/test_engine_service.py @@ -48,6 +48,7 @@ from heat.engine import stack_lock from heat.engine import template as templatem from heat.engine import watchrule from heat.engine import worker +from heat.objects import stack as stack_object from heat.openstack.common import threadgroup from heat.rpc import api as rpc_api from heat.rpc import worker_api @@ -231,14 +232,18 @@ def setup_mocks(mocks, stack, mock_image_constraint=True): 'ec2-user').AndReturn(server_userdata) mocks.StubOutWithMock(fc.servers, 'create') - fc.servers.create(image=744, flavor=3, key_name='test', - name=utils.PhysName(stack.name, 'WebServer'), - security_groups=None, - userdata=server_userdata, scheduler_hints=None, - meta=None, nics=None, - availability_zone=None, - block_device_mapping=None).AndReturn( - fc.servers.list()[4]) + fc.servers.create( + image=744, + flavor=3, + key_name='test', + name=utils.PhysName(stack.name, 'WebServer'), + security_groups=None, + userdata=server_userdata, + scheduler_hints=None, + meta=None, + nics=None, + availability_zone=None, + block_device_mapping=None).AndReturn(fc.servers.list()[4]) return fc @@ -412,7 +417,7 @@ class StackCreateTest(common.HeatTestCase): stack_id = stack.store() stack.create() - db_s = db_api.stack_get(ctx, stack_id) + db_s = stack_object.Stack.get_by_id(ctx, stack_id) self.assertIsNotNone(db_s) self.assertIsNotNone(stack['WebServer']) @@ -427,7 +432,9 @@ class StackCreateTest(common.HeatTestCase): rsrc = stack['WebServer'] self.assertEqual((rsrc.DELETE, rsrc.COMPLETE), rsrc.state) self.assertEqual((stack.DELETE, stack.COMPLETE), rsrc.state) - self.assertIsNone(db_api.stack_get(ctx, stack_id)) + self.assertIsNone(stack_object.Stack.get_by_id(ctx, stack_id)) + + db_s.refresh() self.assertEqual('DELETE', db_s.action) self.assertEqual('COMPLETE', db_s.status, ) @@ -562,7 +569,7 @@ class StackServiceCreateUpdateDeleteTest(common.HeatTestCase): template, {}, None, {'adopt_stack_data': str(adopt_data)}) - stack = db_api.stack_get(self.ctx, result['stack_id']) + stack = stack_object.Stack.get_by_id(self.ctx, result['stack_id']) self.assertEqual(template, stack.raw_template.template) self.assertEqual(environment['parameters'], stack.raw_template.environment['parameters']) @@ -805,7 +812,7 @@ class StackServiceCreateUpdateDeleteTest(common.HeatTestCase): stack = get_wordpress_stack(stack_name, self.ctx) sid = stack.store() - s = db_api.stack_get(self.ctx, sid) + s = stack_object.Stack.get_by_id(self.ctx, sid) self.m.StubOutWithMock(parser.Stack, 'load') parser.Stack.load(self.ctx, stack=s).AndReturn(stack) @@ -832,7 +839,7 @@ class StackServiceCreateUpdateDeleteTest(common.HeatTestCase): stack = get_wordpress_stack(stack_name, self.ctx) sid = stack.store() - st = db_api.stack_get(self.ctx, sid) + st = stack_object.Stack.get_by_id(self.ctx, sid) self.m.StubOutWithMock(parser.Stack, 'load') parser.Stack.load(self.ctx, stack=st).MultipleTimes().AndReturn(stack) @@ -849,7 +856,7 @@ class StackServiceCreateUpdateDeleteTest(common.HeatTestCase): stack = get_wordpress_stack(stack_name, self.ctx) sid = stack.store() - st = db_api.stack_get(self.ctx, sid) + st = stack_object.Stack.get_by_id(self.ctx, sid) self.m.StubOutWithMock(parser.Stack, 'load') parser.Stack.load(self.ctx, stack=st).MultipleTimes().AndReturn(stack) @@ -876,7 +883,7 @@ class StackServiceCreateUpdateDeleteTest(common.HeatTestCase): # Create a fake ThreadGroup too self.man.thread_group_mgr.groups[stack.id] = DummyThreadGroup() - st = db_api.stack_get(self.ctx, sid) + st = stack_object.Stack.get_by_id(self.ctx, sid) self.m.StubOutWithMock(parser.Stack, 'load') parser.Stack.load(self.ctx, stack=st).MultipleTimes().AndReturn(stack) @@ -902,7 +909,7 @@ class StackServiceCreateUpdateDeleteTest(common.HeatTestCase): # Insert a fake lock into the db db_api.stack_lock_create(stack.id, "other-engine-fake-uuid") - st = db_api.stack_get(self.ctx, sid) + st = stack_object.Stack.get_by_id(self.ctx, sid) self.m.StubOutWithMock(parser.Stack, 'load') parser.Stack.load(self.ctx, stack=st).AndReturn(stack) @@ -935,7 +942,7 @@ class StackServiceCreateUpdateDeleteTest(common.HeatTestCase): # Insert a fake lock into the db db_api.stack_lock_create(stack.id, "other-engine-fake-uuid") - st = db_api.stack_get(self.ctx, sid) + st = stack_object.Stack.get_by_id(self.ctx, sid) self.m.StubOutWithMock(parser.Stack, 'load') parser.Stack.load(self.ctx, stack=st).MultipleTimes().AndReturn(stack) @@ -967,7 +974,7 @@ class StackServiceCreateUpdateDeleteTest(common.HeatTestCase): # Insert a fake lock into the db db_api.stack_lock_create(stack.id, "other-engine-fake-uuid") - st = db_api.stack_get(self.ctx, sid) + st = stack_object.Stack.get_by_id(self.ctx, sid) self.m.StubOutWithMock(parser.Stack, 'load') parser.Stack.load(self.ctx, stack=st).MultipleTimes().AndReturn(stack) @@ -1001,7 +1008,7 @@ class StackServiceCreateUpdateDeleteTest(common.HeatTestCase): template = '{ "Template": "data" }' old_stack = get_wordpress_stack(stack_name, self.ctx) sid = old_stack.store() - s = db_api.stack_get(self.ctx, sid) + s = stack_object.Stack.get_by_id(self.ctx, sid) stack = get_wordpress_stack(stack_name, self.ctx) @@ -1046,7 +1053,7 @@ class StackServiceCreateUpdateDeleteTest(common.HeatTestCase): old_stack = get_wordpress_stack_no_params(stack_name, self.ctx) sid = old_stack.store() - s = db_api.stack_get(self.ctx, sid) + s = stack_object.Stack.get_by_id(self.ctx, sid) t = template_format.parse(wp_template_no_default) template = templatem.Template(t) @@ -1095,7 +1102,7 @@ class StackServiceCreateUpdateDeleteTest(common.HeatTestCase): old_stack.timeout_mins = 1 old_stack.disable_rollback = False sid = old_stack.store() - s = db_api.stack_get(self.ctx, sid) + s = stack_object.Stack.get_by_id(self.ctx, sid) stack = get_wordpress_stack(stack_name, self.ctx) @@ -1174,7 +1181,7 @@ class StackServiceCreateUpdateDeleteTest(common.HeatTestCase): old_stack = parser.Stack(self.ctx, stack_name, template) sid = old_stack.store() - s = db_api.stack_get(self.ctx, sid) + s = stack_object.Stack.get_by_id(self.ctx, sid) stack = parser.Stack(self.ctx, stack_name, template) @@ -1231,7 +1238,7 @@ class StackServiceCreateUpdateDeleteTest(common.HeatTestCase): self.assertEqual((create_stack.CREATE, create_stack.COMPLETE), create_stack.state) - s = db_api.stack_get(self.ctx, sid) + s = stack_object.Stack.get_by_id(self.ctx, sid) old_stack = parser.Stack.load(self.ctx, stack=s) @@ -1241,7 +1248,9 @@ class StackServiceCreateUpdateDeleteTest(common.HeatTestCase): old_stack['A'].properties['Foo']) self.m.StubOutWithMock(parser.Stack, 'load') - parser.Stack.load(self.ctx, stack=s).AndReturn(old_stack) + parser.Stack.load( + self.ctx, + stack=s).AndReturn(old_stack) self.m.ReplayAll() @@ -1296,8 +1305,7 @@ class StackServiceCreateUpdateDeleteTest(common.HeatTestCase): old_stack = get_wordpress_stack(stack_name, self.ctx) old_stack.store() sid = old_stack.store() - s = db_api.stack_get(self.ctx, sid) - + s = stack_object.Stack.get_by_id(self.ctx, sid) stack = get_wordpress_stack(stack_name, self.ctx) self._stub_update_mocks(s, old_stack) @@ -1350,7 +1358,7 @@ class StackServiceCreateUpdateDeleteTest(common.HeatTestCase): old_stack['WebServer'].requires_deferred_auth = True sid = old_stack.store() - s = db_api.stack_get(self.ctx, sid) + s = stack_object.Stack.get_by_id(self.ctx, sid) self.ctx = utils.dummy_context(password=None) @@ -1448,7 +1456,7 @@ class StackServiceUpdateActionsNotSupportedTest(common.HeatTestCase): old_stack.status = self.status sid = old_stack.store() - s = db_api.stack_get(self.ctx, sid) + s = stack_object.Stack.get_by_id(self.ctx, sid) self.m.StubOutWithMock(parser, 'Stack') self.m.StubOutWithMock(parser.Stack, 'load') @@ -1479,7 +1487,7 @@ class StackServiceActionsTest(common.HeatTestCase): stack_name = 'service_suspend_test_stack' stack = get_wordpress_stack(stack_name, self.ctx) sid = stack.store() - s = db_api.stack_get(self.ctx, sid) + s = stack_object.Stack.get_by_id(self.ctx, sid) self.m.StubOutWithMock(parser.Stack, 'load') parser.Stack.load(self.ctx, stack=s).AndReturn(stack) @@ -1741,16 +1749,18 @@ class StackServiceTest(common.HeatTestCase): @stack_context('service_name_tenants_test_stack', False) def test_stack_by_name_tenants(self): - self.assertEqual(self.stack.id, - db_api.stack_get_by_name(self.ctx, - self.stack.name).id) + self.assertEqual( + self.stack.id, + stack_object.Stack.get_by_name(self.ctx, self.stack.name).id) ctx2 = utils.dummy_context(tenant_id='stack_service_test_tenant2') - self.assertIsNone(db_api.stack_get_by_name(ctx2, self.stack.name)) + self.assertIsNone(stack_object.Stack.get_by_name( + ctx2, + self.stack.name)) @stack_context('service_event_list_test_stack') def test_stack_event_list(self): self.m.StubOutWithMock(service.EngineService, '_get_stack') - s = db_api.stack_get(self.ctx, self.stack.id) + s = stack_object.Stack.get_by_id(self.ctx, self.stack.id) service.EngineService._get_stack(self.ctx, self.stack.identifier(), show_deleted=True).AndReturn(s) @@ -2179,7 +2189,7 @@ class StackServiceTest(common.HeatTestCase): @stack_context('service_describe_test_stack', False) def test_stack_describe(self): self.m.StubOutWithMock(service.EngineService, '_get_stack') - s = db_api.stack_get(self.ctx, self.stack.id) + s = stack_object.Stack.get_by_id(self.ctx, self.stack.id) service.EngineService._get_stack(self.ctx, self.stack.identifier(), show_deleted=True).AndReturn(s) @@ -2591,7 +2601,7 @@ class StackServiceTest(common.HeatTestCase): test_data = {'food': 'yum'} self.m.StubOutWithMock(service.EngineService, '_get_stack') - s = db_api.stack_get(self.ctx, self.stack.id) + s = stack_object.Stack.get_by_id(self.ctx, self.stack.id) service.EngineService._get_stack(self.ctx, self.stack.identifier()).AndReturn(s) @@ -2620,7 +2630,7 @@ class StackServiceTest(common.HeatTestCase): test_data = {'food': 'yum'} self.m.StubOutWithMock(service.EngineService, '_get_stack') - s = db_api.stack_get(self.ctx, self.stack.id) + s = stack_object.Stack.get_by_id(self.ctx, self.stack.id) service.EngineService._get_stack(self.ctx, self.stack.identifier()).AndReturn(s) self.m.ReplayAll() @@ -2677,7 +2687,7 @@ class StackServiceTest(common.HeatTestCase): rsrc.metadata_set(test_metadata) self.m.StubOutWithMock(service.EngineService, '_get_stack') - s = db_api.stack_get(self.ctx, self.stack.id) + s = stack_object.Stack.get_by_id(self.ctx, self.stack.id) service.EngineService._get_stack(self.ctx, self.stack.identifier()).AndReturn(s) @@ -2699,7 +2709,7 @@ class StackServiceTest(common.HeatTestCase): pre_update_meta = self.stack['WebServer'].metadata_get() self.m.StubOutWithMock(service.EngineService, '_get_stack') - s = db_api.stack_get(self.ctx, self.stack.id) + s = stack_object.Stack.get_by_id(self.ctx, self.stack.id) service.EngineService._get_stack(self.ctx, self.stack.identifier()).AndReturn(s) self.m.StubOutWithMock(instances.Instance, 'metadata_update') @@ -4223,7 +4233,7 @@ class SnapshotServiceTest(common.HeatTestCase): stack = get_wordpress_stack('stack', self.ctx) sid = stack.store() - s = db_api.stack_get(self.ctx, sid) + s = stack_object.Stack.get_by_id(self.ctx, sid) if stub: self.m.StubOutWithMock(parser.Stack, 'load') stack.state_set(stack.CREATE, stack.COMPLETE, 'mock completion') @@ -4324,12 +4334,14 @@ class SnapshotServiceTest(common.HeatTestCase): def test_restore_snapshot_other_stack(self): stack1 = self._create_stack() - stack2 = self._create_stack(stub=False) self.m.ReplayAll() snapshot1 = self.engine.stack_snapshot( self.ctx, stack1.identifier(), 'snap1') self.engine.thread_group_mgr.groups[stack1.id].wait() snapshot_id = snapshot1['id'] + self.m.UnsetStubs() + stack2 = self._create_stack() + self.m.ReplayAll() self.engine.stack_restore(self.ctx, stack2.identifier(), snapshot_id) self.engine.thread_group_mgr.groups[stack2.id].wait() self.assertEqual((stack2.RESTORE, stack2.FAILED), stack2.state) diff --git a/heat/tests/test_stack.py b/heat/tests/test_stack.py index 3bd70d314..02835d6ed 100644 --- a/heat/tests/test_stack.py +++ b/heat/tests/test_stack.py @@ -32,6 +32,7 @@ from heat.engine import resource from heat.engine import scheduler from heat.engine import stack from heat.engine import template +from heat.objects import stack as stack_object from heat.tests import common from heat.tests import fakes from heat.tests import generic_resource as generic_rsrc @@ -265,7 +266,7 @@ class StackTest(common.HeatTestCase): def test_load_parent_resource(self): self.stack = stack.Stack(self.ctx, 'load_parent_resource', self.tmpl) self.stack.store() - stk = db_api.stack_get(self.ctx, self.stack.id) + stk = stack_object.Stack.get_by_id(self.ctx, self.stack.id) t = template.Template.load(self.ctx, stk.raw_template_id) self.m.StubOutWithMock(template.Template, 'load') diff --git a/requirements.txt b/requirements.txt index b775e1d2c..6aa4c62be 100644 --- a/requirements.txt +++ b/requirements.txt @@ -22,6 +22,7 @@ oslo.middleware>=0.3.0 # Apache-2.0 oslo.serialization>=1.2.0 # Apache-2.0 oslo.utils>=1.2.0 # Apache-2.0 osprofiler>=0.3.0 # Apache-2.0 +oslo.versionedobjects>=0.1.0 PasteDeploy>=1.5.0 posix_ipc pycrypto>=2.6