diff --git a/zun/db/api.py b/zun/db/api.py index 8e8312f69..21f96e5ce 100644 --- a/zun/db/api.py +++ b/zun/db/api.py @@ -366,34 +366,34 @@ def get_resource_class(context, resource_ident): """Return a resource class. :param context: The security context - :param resource_ident: The id or name of a resource class. + :param resource_ident: The uuid or name of a resource class. :returns: A resource class. """ return _get_dbdriver_instance().get_resource_class( context, resource_ident) -def destroy_resource_class(context, resource_id): +def destroy_resource_class(context, resource_uuid): """Destroy a resource class and all associated interfaces. :param context: Request context - :param resource_id: The id of a resource class. + :param resource_uuid: The uuid of a resource class. """ return _get_dbdriver_instance().destroy_resource_class( - context, resource_id) + context, resource_uuid) -def update_resource_class(context, resource_id, values): +def update_resource_class(context, resource_uuid, values): """Update properties of a resource class. :context: Request context - :param resource_id: The id of a resource class. + :param resource_uuid: The uuid of a resource class. :values: The properties to be updated :returns: A resource class. :raises: ResourceClassNotFound """ return _get_dbdriver_instance().update_resource_class( - context, resource_id, values) + context, resource_uuid, values) def list_inventories(context, filters=None, limit=None, marker=None, diff --git a/zun/db/etcd/api.py b/zun/db/etcd/api.py index a186da06f..2a5849a4a 100644 --- a/zun/db/etcd/api.py +++ b/zun/db/etcd/api.py @@ -76,6 +76,8 @@ def translate_etcd_result(etcd_result, model_type): ret = models.ZunService(data) elif model_type == 'image': ret = models.Image(data) + elif model_type == 'resource_class': + ret = models.ResourceClass(data) else: raise exception.InvalidParameterValue( _('The model_type value: %s is invalid.'), model_type) @@ -436,3 +438,100 @@ class EtcdAPI(object): if len(images) == 0: return None return images[0] + + def list_resource_classes(self, context, filters=None, limit=None, + marker=None, sort_key=None, sort_dir=None): + try: + res = getattr(self.client.read('/resource_classes'), + 'children', None) + except etcd.EtcdKeyNotFound: + return [] + except Exception as e: + LOG.error( + _LE('Error occurred while reading from etcd server: %s'), + six.text_type(e)) + raise + + resource_classes = [] + for r in res: + if r.value is not None: + resource_classes.append( + translate_etcd_result(r, 'resource_class')) + + if filters: + resource_classes = self._filter_resources( + resource_classes, filters) + + return self._process_list_result( + resource_classes, limit=limit, sort_key=sort_key) + + @lockutils.synchronized('etcd_resource_class') + def create_resource_class(self, context, values): + resource_class = models.ResourceClass(values) + resource_class.save() + return resource_class + + def get_resource_class(self, context, ident): + if uuidutils.is_uuid_like(ident): + return self._get_resource_class_by_uuid(context, ident) + else: + return self._get_resource_class_by_name(context, ident) + + def _get_resource_class_by_uuid(self, context, uuid): + try: + resource_class = None + res = self.client.read('/resource_classes/' + uuid) + resource_class = translate_etcd_result(res, 'resource_class') + except etcd.EtcdKeyNotFound: + raise exception.ResourceClassNotFound(resource_class=uuid) + except Exception as e: + LOG.error( + _LE('Error occurred while retriving resource class: %s'), + six.text_type(e)) + raise + return resource_class + + def _get_resource_class_by_name(self, context, name): + try: + rcs = self.list_resource_classes( + context, filters={'name': name}) + except etcd.EtcdKeyNotFound: + raise exception.ResourceClassNotFound(resource_class=name) + except Exception as e: + LOG.error( + _LE('Error occurred while retriving resource class: %s'), + six.text_type(e)) + raise + + if len(rcs) > 1: + raise exception.Conflict('Multiple resource classes exist with ' + 'same name. Please use uuid instead.') + elif len(rcs) == 0: + raise exception.ResourceClassNotFound(resource_class=name) + + return rcs[0] + + @lockutils.synchronized('etcd_resource_class') + def destroy_resource_class(self, context, uuid): + resource_class = self._get_resource_class_by_uuid(context, uuid) + self.client.delete('/resource_classes/' + resource_class.uuid) + + @lockutils.synchronized('etcd_resource_class') + def update_resource_class(self, context, uuid, values): + if 'uuid' in values: + msg = _("Cannot override UUID for an existing resource class.") + raise exception.InvalidParameterValue(err=msg) + try: + target = self.client.read('/resource_classes/' + uuid) + target_value = json.loads(target.value) + target_value.update(values) + target.value = json.dumps(target_value) + self.client.update(target) + except etcd.EtcdKeyNotFound: + raise exception.ResourceClassNotFound(resource_class=uuid) + except Exception as e: + LOG.error( + _LE('Error occurred while updating resource class: %s'), + six.text_type(e)) + raise + return translate_etcd_result(target, 'resource_class') diff --git a/zun/db/etcd/models.py b/zun/db/etcd/models.py index 9889cfebf..3523bc8fd 100644 --- a/zun/db/etcd/models.py +++ b/zun/db/etcd/models.py @@ -154,3 +154,26 @@ class Image(Base): @classmethod def fields(cls): return cls._fields + + +class ResourceClass(Base): + """Represents a resource class.""" + + _path = '/resource_classes' + + _fields = objects.ResourceClass.fields.keys() + + def __init__(self, resource_class_data): + self.path = ResourceClass.path() + for f in ResourceClass.fields(): + setattr(self, f, None) + self.id = 1 + self.update(resource_class_data) + + @classmethod + def path(cls): + return cls._path + + @classmethod + def fields(cls): + return cls._fields diff --git a/zun/db/sqlalchemy/alembic/versions/8192905fd835_add_uuid_to_resource_class.py b/zun/db/sqlalchemy/alembic/versions/8192905fd835_add_uuid_to_resource_class.py new file mode 100644 index 000000000..6fe5c5bb6 --- /dev/null +++ b/zun/db/sqlalchemy/alembic/versions/8192905fd835_add_uuid_to_resource_class.py @@ -0,0 +1,36 @@ +# 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. + +"""add uuid_to_resource_class + +Revision ID: 8192905fd835 +Revises: e4d145e195f4 +Create Date: 2017-02-24 07:00:22.344162 + +""" + +# revision identifiers, used by Alembic. +revision = '8192905fd835' +down_revision = 'e4d145e195f4' +branch_labels = None +depends_on = None + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + op.add_column('resource_class', + sa.Column('uuid', sa.String(length=36), nullable=False)) + op.create_unique_constraint('uniq_resource_class0uuid', + 'resource_class', ['uuid']) + op.drop_index('uniq_container0name', table_name='resource_class') diff --git a/zun/db/sqlalchemy/api.py b/zun/db/sqlalchemy/api.py index f4052a620..d7f9ae7fc 100644 --- a/zun/db/sqlalchemy/api.py +++ b/zun/db/sqlalchemy/api.py @@ -472,23 +472,23 @@ class Connection(object): resource.save() except db_exc.DBDuplicateEntry: raise exception.ResourceClassAlreadyExists( - field='name', value=values['name']) + field='uuid', value=values['uuid']) return resource def get_resource_class(self, context, resource_ident): - if strutils.is_int_like(resource_ident): - return self._get_resource_class_by_id(context, resource_ident) + if uuidutils.is_uuid_like(resource_ident): + return self._get_resource_class_by_uuid(context, resource_ident) else: return self._get_resource_class_by_name(context, resource_ident) - def _get_resource_class_by_id(self, context, resource_id): + def _get_resource_class_by_uuid(self, context, resource_uuid): query = model_query(models.ResourceClass) - query = query.filter_by(id=resource_id) + query = query.filter_by(uuid=resource_uuid) try: return query.one() except NoResultFound: raise exception.ResourceClassNotFound( - resource_class=resource_id) + resource_class=resource_uuid) def _get_resource_class_by_name(self, context, resource_name): query = model_query(models.ResourceClass) diff --git a/zun/db/sqlalchemy/models.py b/zun/db/sqlalchemy/models.py index 56ba87107..57a97e817 100644 --- a/zun/db/sqlalchemy/models.py +++ b/zun/db/sqlalchemy/models.py @@ -194,10 +194,11 @@ class ResourceClass(Base): __tablename__ = 'resource_class' __table_args__ = ( - schema.UniqueConstraint('name', name='uniq_resource_class0name'), + schema.UniqueConstraint('uuid', name='uniq_resource_class0uuid'), table_args() ) id = Column(Integer, primary_key=True, nullable=False) + uuid = Column(String(36), nullable=False) name = Column(String(255), nullable=False) diff --git a/zun/objects/resource_class.py b/zun/objects/resource_class.py index a1f3d72db..aa42fbe97 100644 --- a/zun/objects/resource_class.py +++ b/zun/objects/resource_class.py @@ -20,10 +20,12 @@ from zun.objects import fields as z_fields @base.ZunObjectRegistry.register class ResourceClass(base.ZunPersistentObject, base.ZunObject): # Version 1.0: Initial version - VERSION = '1.0' + # Version 1.1: Add uuid field + VERSION = '1.1' fields = { 'id': fields.IntegerField(read_only=True), + 'uuid': fields.UUIDField(nullable=False), 'name': z_fields.ResourceClassField(nullable=False), } @@ -43,14 +45,14 @@ class ResourceClass(base.ZunPersistentObject, base.ZunObject): for obj in db_objects] @base.remotable_classmethod - def get_by_id(cls, context, rc_id): - """Find a resource class based on id. + def get_by_uuid(cls, context, uuid): + """Find a resource class based on uuid. - :param rc_id: the id of a resource class. + :param uuid: the uuid of a resource class. :param context: Security context :returns: a :class:`ResourceClass` object. """ - db_resource = dbapi.get_resource_class(context, rc_id) + db_resource = dbapi.get_resource_class(context, uuid) resource = ResourceClass._from_db_object(cls(context), db_resource) return resource @@ -112,7 +114,7 @@ class ResourceClass(base.ZunPersistentObject, base.ZunObject): A context should be set when instantiating the object, e.g.: ResourceClass(context) """ - dbapi.destroy_resource_class(context, self.id) + dbapi.destroy_resource_class(context, self.uuid) self.obj_reset_changes() @base.remotable @@ -130,7 +132,7 @@ class ResourceClass(base.ZunPersistentObject, base.ZunObject): object, e.g.: ResourceClass(context) """ updates = self.obj_get_changes() - dbapi.update_resource_class(context, self.id, updates) + dbapi.update_resource_class(context, self.uuid, updates) self.obj_reset_changes() @@ -150,7 +152,7 @@ class ResourceClass(base.ZunPersistentObject, base.ZunObject): A context should be set when instantiating the object, e.g.: ResourceClass(context) """ - current = self.__class__.get_by_id(self._context, rc_id=self.id) + current = self.__class__.get_by_uuid(self._context, self.uuid) for field in self.fields: if self.obj_attr_is_set(field) and \ getattr(self, field) != getattr(current, field): diff --git a/zun/tests/unit/db/test_resource_class.py b/zun/tests/unit/db/test_resource_class.py index b6b0c24df..4d1e7e6d2 100644 --- a/zun/tests/unit/db/test_resource_class.py +++ b/zun/tests/unit/db/test_resource_class.py @@ -11,8 +11,13 @@ # under the License. """Tests for manipulating resource classes via the DB API""" +import json +import mock +import etcd +from etcd import Client as etcd_client from oslo_config import cfg +from oslo_utils import uuidutils import six from zun.common import exception @@ -20,6 +25,8 @@ import zun.conf from zun.db import api as dbapi from zun.tests.unit.db import base from zun.tests.unit.db import utils +from zun.tests.unit.db.utils import FakeEtcdMultipleResult +from zun.tests.unit.db.utils import FakeEtcdResult CONF = zun.conf.CONF @@ -35,16 +42,16 @@ class DbResourceClassTestCase(base.DbTestCase): def test_create_resource_class_already_exists(self): utils.create_test_resource_class( - context=self.context, name='123') + context=self.context, uuid='123') with self.assertRaisesRegexp(exception.ResourceClassAlreadyExists, - 'A resource class with name 123.*'): + 'A resource class with uuid 123.*'): utils.create_test_resource_class( - context=self.context, name='123') + context=self.context, uuid='123') - def test_get_resource_class_by_id(self): + def test_get_resource_class_by_uuid(self): resource = utils.create_test_resource_class(context=self.context) - res = dbapi.get_resource_class(self.context, resource.id) - self.assertEqual(resource.id, res.id) + res = dbapi.get_resource_class(self.context, resource.uuid) + self.assertEqual(resource.uuid, res.uuid) self.assertEqual(resource.name, res.name) def test_get_resource_class_by_name(self): @@ -54,17 +61,16 @@ class DbResourceClassTestCase(base.DbTestCase): self.assertEqual(resource.name, res.name) def test_get_resource_class_that_does_not_exist(self): - bad_id = 1111111 self.assertRaises(exception.ResourceClassNotFound, dbapi.get_resource_class, - self.context, - bad_id) + self.context, uuidutils.generate_uuid()) def test_list_resource_classes(self): names = [] for i in range(1, 6): resource = utils.create_test_resource_class( context=self.context, + uuid=uuidutils.generate_uuid(), name='class'+str(i)) names.append(six.text_type(resource['name'])) res = dbapi.list_resource_classes(self.context) @@ -76,6 +82,7 @@ class DbResourceClassTestCase(base.DbTestCase): for i in range(5): resource = utils.create_test_resource_class( context=self.context, + uuid=uuidutils.generate_uuid(), name='class'+str(i)) names.append(six.text_type(resource.name)) res = dbapi.list_resource_classes(self.context, sort_key='name') @@ -116,3 +123,151 @@ class DbResourceClassTestCase(base.DbTestCase): self.assertRaises(exception.ResourceClassNotFound, dbapi.update_resource_class, self.context, bad_id, {'name': new_name}) + + +class EtcdDbResourceClassTestCase(base.DbTestCase): + + def setUp(self): + cfg.CONF.set_override('db_type', 'etcd') + super(EtcdDbResourceClassTestCase, self).setUp() + + @mock.patch.object(etcd_client, 'read') + @mock.patch.object(etcd_client, 'write') + def test_create_resource_class(self, mock_write, mock_read): + mock_read.side_effect = etcd.EtcdKeyNotFound + utils.create_test_resource_class(context=self.context) + + @mock.patch.object(etcd_client, 'read') + @mock.patch.object(etcd_client, 'write') + def test_create_resource_class_already_exists(self, mock_write, + mock_read): + mock_read.side_effect = etcd.EtcdKeyNotFound + utils.create_test_resource_class(context=self.context, name='123') + mock_read.side_effect = lambda *args: None + self.assertRaises(exception.ResourceExists, + utils.create_test_resource_class, + context=self.context, name='123') + + @mock.patch.object(etcd_client, 'read') + @mock.patch.object(etcd_client, 'write') + def test_get_resource_class_by_uuid(self, mock_write, mock_read): + mock_read.side_effect = etcd.EtcdKeyNotFound + resource_class = utils.create_test_resource_class( + context=self.context) + mock_read.side_effect = lambda *args: FakeEtcdResult( + resource_class.as_dict()) + res = dbapi.get_resource_class(self.context, resource_class.uuid) + self.assertEqual(resource_class.uuid, res.uuid) + self.assertEqual(resource_class.name, res.name) + + @mock.patch.object(etcd_client, 'read') + @mock.patch.object(etcd_client, 'write') + def test_get_resource_class_by_name(self, mock_write, mock_read): + mock_read.side_effect = etcd.EtcdKeyNotFound + rcs = utils.create_test_resource_class(context=self.context) + mock_read.side_effect = lambda *args: FakeEtcdMultipleResult( + [rcs.as_dict()]) + res = dbapi.get_resource_class(self.context, rcs.name) + self.assertEqual(rcs.uuid, res.uuid) + + @mock.patch.object(etcd_client, 'read') + def test_get_resource_class_that_does_not_exist(self, mock_read): + mock_read.side_effect = etcd.EtcdKeyNotFound + self.assertRaises(exception.ResourceClassNotFound, + dbapi.get_resource_class, + self.context, 'fake-ident') + + @mock.patch.object(etcd_client, 'read') + @mock.patch.object(etcd_client, 'write') + def test_list_resource_classes(self, mock_write, mock_read): + names = [] + resource_classes = [] + mock_read.side_effect = etcd.EtcdKeyNotFound + for i in range(1, 6): + res_class = utils.create_test_resource_class( + context=self.context, name='class'+str(i)) + resource_classes.append(res_class.as_dict()) + names.append(six.text_type(res_class['name'])) + mock_read.side_effect = lambda *args: FakeEtcdMultipleResult( + resource_classes) + res = dbapi.list_resource_classes(self.context) + res_names = [r.name for r in res] + self.assertEqual(sorted(names), sorted(res_names)) + + @mock.patch.object(etcd_client, 'read') + @mock.patch.object(etcd_client, 'write') + def test_list_resource_classes_sorted(self, mock_write, mock_read): + names = [] + resource_classes = [] + mock_read.side_effect = etcd.EtcdKeyNotFound + for i in range(1, 6): + res_class = utils.create_test_resource_class( + context=self.context, name='class'+str(i)) + resource_classes.append(res_class.as_dict()) + names.append(six.text_type(res_class['name'])) + mock_read.side_effect = lambda *args: FakeEtcdMultipleResult( + resource_classes) + res = dbapi.list_resource_classes(self.context, sort_key='name') + res_names = [r.name for r in res] + self.assertEqual(sorted(names), res_names) + + @mock.patch.object(etcd_client, 'read') + @mock.patch.object(etcd_client, 'write') + @mock.patch.object(etcd_client, 'delete') + def test_destroy_resource_class(self, mock_delete, + mock_write, mock_read): + mock_read.side_effect = etcd.EtcdKeyNotFound + resource_class = utils.create_test_resource_class( + context=self.context) + mock_read.side_effect = lambda *args: FakeEtcdResult( + resource_class.as_dict()) + dbapi.destroy_resource_class(self.context, resource_class.uuid) + mock_delete.assert_called_once_with( + '/resource_classes/%s' % resource_class.uuid) + + @mock.patch.object(etcd_client, 'read') + def test_destroy_resource_class_that_does_not_exist(self, mock_read): + mock_read.side_effect = etcd.EtcdKeyNotFound + self.assertRaises(exception.ResourceClassNotFound, + dbapi.destroy_resource_class, + self.context, + 'ca3e2a25-2901-438d-8157-de7ffd68d535') + + @mock.patch.object(etcd_client, 'read') + @mock.patch.object(etcd_client, 'write') + @mock.patch.object(etcd_client, 'update') + def test_update_resource_class(self, mock_update, + mock_write, mock_read): + mock_read.side_effect = etcd.EtcdKeyNotFound + resource_class = utils.create_test_resource_class( + context=self.context) + old_name = resource_class.name + new_name = 'new-name' + self.assertNotEqual(old_name, new_name) + mock_read.side_effect = lambda *args: FakeEtcdResult( + resource_class.as_dict()) + dbapi.update_resource_class( + self.context, resource_class.uuid, {'name': new_name}) + self.assertEqual(new_name, json.loads( + mock_update.call_args_list[0][0][0].value)['name']) + + @mock.patch.object(etcd_client, 'read') + def test_update_resource_class_not_found(self, mock_read): + mock_read.side_effect = etcd.EtcdKeyNotFound + new_name = 'new-name' + self.assertRaises(exception.ResourceClassNotFound, + dbapi.update_resource_class, + self.context, + 'ca3e2a25-2901-438d-8157-de7ffd68d535', + {'name': new_name}) + + @mock.patch.object(etcd_client, 'read') + @mock.patch.object(etcd_client, 'write') + def test_update_resource_class_uuid(self, mock_write, mock_read): + mock_read.side_effect = etcd.EtcdKeyNotFound + resource_class = utils.create_test_resource_class( + context=self.context) + self.assertRaises(exception.InvalidParameterValue, + dbapi.update_resource_class, + self.context, resource_class.uuid, + {'uuid': ''}) diff --git a/zun/tests/unit/db/utils.py b/zun/tests/unit/db/utils.py index 777c0f392..9d10e0406 100644 --- a/zun/tests/unit/db/utils.py +++ b/zun/tests/unit/db/utils.py @@ -172,6 +172,7 @@ def create_test_resource_provider(**kw): def get_test_resource_class(**kw): return { 'id': kw.get('id', 42), + 'uuid': kw.get('uuid', '1136bf0e-66db-409d-aa4d-3af94eed8bcc'), 'name': kw.get('name', 'VCPU'), 'created_at': kw.get('created_at'), 'updated_at': kw.get('updated_at'), diff --git a/zun/tests/unit/objects/test_objects.py b/zun/tests/unit/objects/test_objects.py index 56e7110f0..fcc8606d8 100644 --- a/zun/tests/unit/objects/test_objects.py +++ b/zun/tests/unit/objects/test_objects.py @@ -359,7 +359,7 @@ object_data = { 'MyObj': '1.0-34c4b1aadefd177b13f9a2f894cc23cd', 'NUMANode': '1.0-cba878b70b2f8b52f1e031b41ac13b4e', 'NUMATopology': '1.0-b54086eda7e4b2e6145ecb6ee2c925ab', - 'ResourceClass': '1.0-2c41abea55d0f7cb47a97bdb345b37fd', + 'ResourceClass': '1.1-d661c7675b3cd5b8c3618b68ba64324e', 'ResourceProvider': '1.0-92b427359d5a4cf9ec6c72cbe630ee24', 'ZunService': '1.0-2a19ab9987a746621b2ada02d8aadf22', } diff --git a/zun/tests/unit/objects/test_resource_class.py b/zun/tests/unit/objects/test_resource_class.py index 947e24799..ed8179715 100644 --- a/zun/tests/unit/objects/test_resource_class.py +++ b/zun/tests/unit/objects/test_resource_class.py @@ -25,16 +25,6 @@ class TestResourceClassObject(base.DbTestCase): super(TestResourceClassObject, self).setUp() self.fake_resource = utils.get_test_resource_class() - def test_get_by_id(self): - rc_id = self.fake_resource['id'] - with mock.patch.object(self.dbapi, 'get_resource_class', - autospec=True) as mock_get_resource_class: - mock_get_resource_class.return_value = self.fake_resource - resource = objects.ResourceClass.get_by_id(self.context, rc_id) - mock_get_resource_class.assert_called_once_with( - self.context, rc_id) - self.assertEqual(self.context, resource._context) - def test_get_by_name(self): name = self.fake_resource['name'] with mock.patch.object(self.dbapi, 'get_resource_class', @@ -67,51 +57,52 @@ class TestResourceClassObject(base.DbTestCase): self.assertEqual(self.context, resource._context) def test_destroy(self): - rc_id = self.fake_resource['id'] + rc_uuid = self.fake_resource['uuid'] with mock.patch.object(self.dbapi, 'get_resource_class', autospec=True) as mock_get_resource_class: mock_get_resource_class.return_value = self.fake_resource with mock.patch.object(self.dbapi, 'destroy_resource_class', autospec=True) as mock_destroy: - resource = objects.ResourceClass.get_by_id( - self.context, rc_id) + resource = objects.ResourceClass.get_by_uuid( + self.context, rc_uuid) resource.destroy() mock_get_resource_class.assert_called_once_with( - self.context, rc_id) - mock_destroy.assert_called_once_with(None, rc_id) + self.context, rc_uuid) + mock_destroy.assert_called_once_with(None, rc_uuid) self.assertEqual(self.context, resource._context) def test_save(self): - rc_id = self.fake_resource['id'] + rc_uuid = self.fake_resource['uuid'] with mock.patch.object(self.dbapi, 'get_resource_class', autospec=True) as mock_get_resource_class: mock_get_resource_class.return_value = self.fake_resource with mock.patch.object(self.dbapi, 'update_resource_class', autospec=True) as mock_update: - resource = objects.ResourceClass.get_by_id( - self.context, rc_id) + resource = objects.ResourceClass.get_by_uuid( + self.context, rc_uuid) resource.name = 'MEMORY_MB' resource.save() mock_get_resource_class.assert_called_once_with( - self.context, rc_id) + self.context, rc_uuid) mock_update.assert_called_once_with( - None, rc_id, + None, rc_uuid, {'name': 'MEMORY_MB'}) self.assertEqual(self.context, resource._context) def test_refresh(self): - rc_id = self.fake_resource['id'] + rc_uuid = self.fake_resource['uuid'] name = self.fake_resource['name'] new_name = 'MEMORY_MB' returns = [dict(self.fake_resource, name=name), dict(self.fake_resource, name=new_name)] - expected = [mock.call(self.context, rc_id), - mock.call(self.context, rc_id)] + expected = [mock.call(self.context, rc_uuid), + mock.call(self.context, rc_uuid)] with mock.patch.object(self.dbapi, 'get_resource_class', side_effect=returns, autospec=True) as mock_get_resource_class: - resource = objects.ResourceClass.get_by_id(self.context, rc_id) + resource = objects.ResourceClass.get_by_uuid( + self.context, rc_uuid) self.assertEqual(name, resource.name) resource.refresh() self.assertEqual(new_name, resource.name)