Implement etcd API for ResourceClass
Porposed change of this commit is: 1.Add etcd db model and implements etcd API for ResourceClass. 2.Add 'uuid' field into ResourceClass. Part of blueprint expose-host-capabilities Change-Id: I9e5678e8742fd4c6a26b9ced6d6eccd6b7976363
This commit is contained in:
parent
3a84375531
commit
cf1ad08b0f
@ -366,34 +366,34 @@ def get_resource_class(context, resource_ident):
|
|||||||
"""Return a resource class.
|
"""Return a resource class.
|
||||||
|
|
||||||
:param context: The security context
|
: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.
|
:returns: A resource class.
|
||||||
"""
|
"""
|
||||||
return _get_dbdriver_instance().get_resource_class(
|
return _get_dbdriver_instance().get_resource_class(
|
||||||
context, resource_ident)
|
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.
|
"""Destroy a resource class and all associated interfaces.
|
||||||
|
|
||||||
:param context: Request context
|
: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(
|
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.
|
"""Update properties of a resource class.
|
||||||
|
|
||||||
:context: Request context
|
: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
|
:values: The properties to be updated
|
||||||
:returns: A resource class.
|
:returns: A resource class.
|
||||||
:raises: ResourceClassNotFound
|
:raises: ResourceClassNotFound
|
||||||
"""
|
"""
|
||||||
return _get_dbdriver_instance().update_resource_class(
|
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,
|
def list_inventories(context, filters=None, limit=None, marker=None,
|
||||||
|
@ -76,6 +76,8 @@ def translate_etcd_result(etcd_result, model_type):
|
|||||||
ret = models.ZunService(data)
|
ret = models.ZunService(data)
|
||||||
elif model_type == 'image':
|
elif model_type == 'image':
|
||||||
ret = models.Image(data)
|
ret = models.Image(data)
|
||||||
|
elif model_type == 'resource_class':
|
||||||
|
ret = models.ResourceClass(data)
|
||||||
else:
|
else:
|
||||||
raise exception.InvalidParameterValue(
|
raise exception.InvalidParameterValue(
|
||||||
_('The model_type value: %s is invalid.'), model_type)
|
_('The model_type value: %s is invalid.'), model_type)
|
||||||
@ -436,3 +438,100 @@ class EtcdAPI(object):
|
|||||||
if len(images) == 0:
|
if len(images) == 0:
|
||||||
return None
|
return None
|
||||||
return images[0]
|
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')
|
||||||
|
@ -154,3 +154,26 @@ class Image(Base):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def fields(cls):
|
def fields(cls):
|
||||||
return cls._fields
|
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
|
||||||
|
@ -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')
|
@ -472,23 +472,23 @@ class Connection(object):
|
|||||||
resource.save()
|
resource.save()
|
||||||
except db_exc.DBDuplicateEntry:
|
except db_exc.DBDuplicateEntry:
|
||||||
raise exception.ResourceClassAlreadyExists(
|
raise exception.ResourceClassAlreadyExists(
|
||||||
field='name', value=values['name'])
|
field='uuid', value=values['uuid'])
|
||||||
return resource
|
return resource
|
||||||
|
|
||||||
def get_resource_class(self, context, resource_ident):
|
def get_resource_class(self, context, resource_ident):
|
||||||
if strutils.is_int_like(resource_ident):
|
if uuidutils.is_uuid_like(resource_ident):
|
||||||
return self._get_resource_class_by_id(context, resource_ident)
|
return self._get_resource_class_by_uuid(context, resource_ident)
|
||||||
else:
|
else:
|
||||||
return self._get_resource_class_by_name(context, resource_ident)
|
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 = model_query(models.ResourceClass)
|
||||||
query = query.filter_by(id=resource_id)
|
query = query.filter_by(uuid=resource_uuid)
|
||||||
try:
|
try:
|
||||||
return query.one()
|
return query.one()
|
||||||
except NoResultFound:
|
except NoResultFound:
|
||||||
raise exception.ResourceClassNotFound(
|
raise exception.ResourceClassNotFound(
|
||||||
resource_class=resource_id)
|
resource_class=resource_uuid)
|
||||||
|
|
||||||
def _get_resource_class_by_name(self, context, resource_name):
|
def _get_resource_class_by_name(self, context, resource_name):
|
||||||
query = model_query(models.ResourceClass)
|
query = model_query(models.ResourceClass)
|
||||||
|
@ -194,10 +194,11 @@ class ResourceClass(Base):
|
|||||||
|
|
||||||
__tablename__ = 'resource_class'
|
__tablename__ = 'resource_class'
|
||||||
__table_args__ = (
|
__table_args__ = (
|
||||||
schema.UniqueConstraint('name', name='uniq_resource_class0name'),
|
schema.UniqueConstraint('uuid', name='uniq_resource_class0uuid'),
|
||||||
table_args()
|
table_args()
|
||||||
)
|
)
|
||||||
id = Column(Integer, primary_key=True, nullable=False)
|
id = Column(Integer, primary_key=True, nullable=False)
|
||||||
|
uuid = Column(String(36), nullable=False)
|
||||||
name = Column(String(255), nullable=False)
|
name = Column(String(255), nullable=False)
|
||||||
|
|
||||||
|
|
||||||
|
@ -20,10 +20,12 @@ from zun.objects import fields as z_fields
|
|||||||
@base.ZunObjectRegistry.register
|
@base.ZunObjectRegistry.register
|
||||||
class ResourceClass(base.ZunPersistentObject, base.ZunObject):
|
class ResourceClass(base.ZunPersistentObject, base.ZunObject):
|
||||||
# Version 1.0: Initial version
|
# Version 1.0: Initial version
|
||||||
VERSION = '1.0'
|
# Version 1.1: Add uuid field
|
||||||
|
VERSION = '1.1'
|
||||||
|
|
||||||
fields = {
|
fields = {
|
||||||
'id': fields.IntegerField(read_only=True),
|
'id': fields.IntegerField(read_only=True),
|
||||||
|
'uuid': fields.UUIDField(nullable=False),
|
||||||
'name': z_fields.ResourceClassField(nullable=False),
|
'name': z_fields.ResourceClassField(nullable=False),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,14 +45,14 @@ class ResourceClass(base.ZunPersistentObject, base.ZunObject):
|
|||||||
for obj in db_objects]
|
for obj in db_objects]
|
||||||
|
|
||||||
@base.remotable_classmethod
|
@base.remotable_classmethod
|
||||||
def get_by_id(cls, context, rc_id):
|
def get_by_uuid(cls, context, uuid):
|
||||||
"""Find a resource class based on id.
|
"""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
|
:param context: Security context
|
||||||
:returns: a :class:`ResourceClass` object.
|
: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)
|
resource = ResourceClass._from_db_object(cls(context), db_resource)
|
||||||
return resource
|
return resource
|
||||||
|
|
||||||
@ -112,7 +114,7 @@ class ResourceClass(base.ZunPersistentObject, base.ZunObject):
|
|||||||
A context should be set when instantiating the
|
A context should be set when instantiating the
|
||||||
object, e.g.: ResourceClass(context)
|
object, e.g.: ResourceClass(context)
|
||||||
"""
|
"""
|
||||||
dbapi.destroy_resource_class(context, self.id)
|
dbapi.destroy_resource_class(context, self.uuid)
|
||||||
self.obj_reset_changes()
|
self.obj_reset_changes()
|
||||||
|
|
||||||
@base.remotable
|
@base.remotable
|
||||||
@ -130,7 +132,7 @@ class ResourceClass(base.ZunPersistentObject, base.ZunObject):
|
|||||||
object, e.g.: ResourceClass(context)
|
object, e.g.: ResourceClass(context)
|
||||||
"""
|
"""
|
||||||
updates = self.obj_get_changes()
|
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()
|
self.obj_reset_changes()
|
||||||
|
|
||||||
@ -150,7 +152,7 @@ class ResourceClass(base.ZunPersistentObject, base.ZunObject):
|
|||||||
A context should be set when instantiating the
|
A context should be set when instantiating the
|
||||||
object, e.g.: ResourceClass(context)
|
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:
|
for field in self.fields:
|
||||||
if self.obj_attr_is_set(field) and \
|
if self.obj_attr_is_set(field) and \
|
||||||
getattr(self, field) != getattr(current, field):
|
getattr(self, field) != getattr(current, field):
|
||||||
|
@ -11,8 +11,13 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
"""Tests for manipulating resource classes via the DB API"""
|
"""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_config import cfg
|
||||||
|
from oslo_utils import uuidutils
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from zun.common import exception
|
from zun.common import exception
|
||||||
@ -20,6 +25,8 @@ import zun.conf
|
|||||||
from zun.db import api as dbapi
|
from zun.db import api as dbapi
|
||||||
from zun.tests.unit.db import base
|
from zun.tests.unit.db import base
|
||||||
from zun.tests.unit.db import utils
|
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
|
CONF = zun.conf.CONF
|
||||||
|
|
||||||
@ -35,16 +42,16 @@ class DbResourceClassTestCase(base.DbTestCase):
|
|||||||
|
|
||||||
def test_create_resource_class_already_exists(self):
|
def test_create_resource_class_already_exists(self):
|
||||||
utils.create_test_resource_class(
|
utils.create_test_resource_class(
|
||||||
context=self.context, name='123')
|
context=self.context, uuid='123')
|
||||||
with self.assertRaisesRegexp(exception.ResourceClassAlreadyExists,
|
with self.assertRaisesRegexp(exception.ResourceClassAlreadyExists,
|
||||||
'A resource class with name 123.*'):
|
'A resource class with uuid 123.*'):
|
||||||
utils.create_test_resource_class(
|
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)
|
resource = utils.create_test_resource_class(context=self.context)
|
||||||
res = dbapi.get_resource_class(self.context, resource.id)
|
res = dbapi.get_resource_class(self.context, resource.uuid)
|
||||||
self.assertEqual(resource.id, res.id)
|
self.assertEqual(resource.uuid, res.uuid)
|
||||||
self.assertEqual(resource.name, res.name)
|
self.assertEqual(resource.name, res.name)
|
||||||
|
|
||||||
def test_get_resource_class_by_name(self):
|
def test_get_resource_class_by_name(self):
|
||||||
@ -54,17 +61,16 @@ class DbResourceClassTestCase(base.DbTestCase):
|
|||||||
self.assertEqual(resource.name, res.name)
|
self.assertEqual(resource.name, res.name)
|
||||||
|
|
||||||
def test_get_resource_class_that_does_not_exist(self):
|
def test_get_resource_class_that_does_not_exist(self):
|
||||||
bad_id = 1111111
|
|
||||||
self.assertRaises(exception.ResourceClassNotFound,
|
self.assertRaises(exception.ResourceClassNotFound,
|
||||||
dbapi.get_resource_class,
|
dbapi.get_resource_class,
|
||||||
self.context,
|
self.context, uuidutils.generate_uuid())
|
||||||
bad_id)
|
|
||||||
|
|
||||||
def test_list_resource_classes(self):
|
def test_list_resource_classes(self):
|
||||||
names = []
|
names = []
|
||||||
for i in range(1, 6):
|
for i in range(1, 6):
|
||||||
resource = utils.create_test_resource_class(
|
resource = utils.create_test_resource_class(
|
||||||
context=self.context,
|
context=self.context,
|
||||||
|
uuid=uuidutils.generate_uuid(),
|
||||||
name='class'+str(i))
|
name='class'+str(i))
|
||||||
names.append(six.text_type(resource['name']))
|
names.append(six.text_type(resource['name']))
|
||||||
res = dbapi.list_resource_classes(self.context)
|
res = dbapi.list_resource_classes(self.context)
|
||||||
@ -76,6 +82,7 @@ class DbResourceClassTestCase(base.DbTestCase):
|
|||||||
for i in range(5):
|
for i in range(5):
|
||||||
resource = utils.create_test_resource_class(
|
resource = utils.create_test_resource_class(
|
||||||
context=self.context,
|
context=self.context,
|
||||||
|
uuid=uuidutils.generate_uuid(),
|
||||||
name='class'+str(i))
|
name='class'+str(i))
|
||||||
names.append(six.text_type(resource.name))
|
names.append(six.text_type(resource.name))
|
||||||
res = dbapi.list_resource_classes(self.context, sort_key='name')
|
res = dbapi.list_resource_classes(self.context, sort_key='name')
|
||||||
@ -116,3 +123,151 @@ class DbResourceClassTestCase(base.DbTestCase):
|
|||||||
self.assertRaises(exception.ResourceClassNotFound,
|
self.assertRaises(exception.ResourceClassNotFound,
|
||||||
dbapi.update_resource_class, self.context,
|
dbapi.update_resource_class, self.context,
|
||||||
bad_id, {'name': new_name})
|
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': ''})
|
||||||
|
@ -172,6 +172,7 @@ def create_test_resource_provider(**kw):
|
|||||||
def get_test_resource_class(**kw):
|
def get_test_resource_class(**kw):
|
||||||
return {
|
return {
|
||||||
'id': kw.get('id', 42),
|
'id': kw.get('id', 42),
|
||||||
|
'uuid': kw.get('uuid', '1136bf0e-66db-409d-aa4d-3af94eed8bcc'),
|
||||||
'name': kw.get('name', 'VCPU'),
|
'name': kw.get('name', 'VCPU'),
|
||||||
'created_at': kw.get('created_at'),
|
'created_at': kw.get('created_at'),
|
||||||
'updated_at': kw.get('updated_at'),
|
'updated_at': kw.get('updated_at'),
|
||||||
|
@ -359,7 +359,7 @@ object_data = {
|
|||||||
'MyObj': '1.0-34c4b1aadefd177b13f9a2f894cc23cd',
|
'MyObj': '1.0-34c4b1aadefd177b13f9a2f894cc23cd',
|
||||||
'NUMANode': '1.0-cba878b70b2f8b52f1e031b41ac13b4e',
|
'NUMANode': '1.0-cba878b70b2f8b52f1e031b41ac13b4e',
|
||||||
'NUMATopology': '1.0-b54086eda7e4b2e6145ecb6ee2c925ab',
|
'NUMATopology': '1.0-b54086eda7e4b2e6145ecb6ee2c925ab',
|
||||||
'ResourceClass': '1.0-2c41abea55d0f7cb47a97bdb345b37fd',
|
'ResourceClass': '1.1-d661c7675b3cd5b8c3618b68ba64324e',
|
||||||
'ResourceProvider': '1.0-92b427359d5a4cf9ec6c72cbe630ee24',
|
'ResourceProvider': '1.0-92b427359d5a4cf9ec6c72cbe630ee24',
|
||||||
'ZunService': '1.0-2a19ab9987a746621b2ada02d8aadf22',
|
'ZunService': '1.0-2a19ab9987a746621b2ada02d8aadf22',
|
||||||
}
|
}
|
||||||
|
@ -25,16 +25,6 @@ class TestResourceClassObject(base.DbTestCase):
|
|||||||
super(TestResourceClassObject, self).setUp()
|
super(TestResourceClassObject, self).setUp()
|
||||||
self.fake_resource = utils.get_test_resource_class()
|
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):
|
def test_get_by_name(self):
|
||||||
name = self.fake_resource['name']
|
name = self.fake_resource['name']
|
||||||
with mock.patch.object(self.dbapi, 'get_resource_class',
|
with mock.patch.object(self.dbapi, 'get_resource_class',
|
||||||
@ -67,51 +57,52 @@ class TestResourceClassObject(base.DbTestCase):
|
|||||||
self.assertEqual(self.context, resource._context)
|
self.assertEqual(self.context, resource._context)
|
||||||
|
|
||||||
def test_destroy(self):
|
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',
|
with mock.patch.object(self.dbapi, 'get_resource_class',
|
||||||
autospec=True) as mock_get_resource_class:
|
autospec=True) as mock_get_resource_class:
|
||||||
mock_get_resource_class.return_value = self.fake_resource
|
mock_get_resource_class.return_value = self.fake_resource
|
||||||
with mock.patch.object(self.dbapi, 'destroy_resource_class',
|
with mock.patch.object(self.dbapi, 'destroy_resource_class',
|
||||||
autospec=True) as mock_destroy:
|
autospec=True) as mock_destroy:
|
||||||
resource = objects.ResourceClass.get_by_id(
|
resource = objects.ResourceClass.get_by_uuid(
|
||||||
self.context, rc_id)
|
self.context, rc_uuid)
|
||||||
resource.destroy()
|
resource.destroy()
|
||||||
mock_get_resource_class.assert_called_once_with(
|
mock_get_resource_class.assert_called_once_with(
|
||||||
self.context, rc_id)
|
self.context, rc_uuid)
|
||||||
mock_destroy.assert_called_once_with(None, rc_id)
|
mock_destroy.assert_called_once_with(None, rc_uuid)
|
||||||
self.assertEqual(self.context, resource._context)
|
self.assertEqual(self.context, resource._context)
|
||||||
|
|
||||||
def test_save(self):
|
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',
|
with mock.patch.object(self.dbapi, 'get_resource_class',
|
||||||
autospec=True) as mock_get_resource_class:
|
autospec=True) as mock_get_resource_class:
|
||||||
mock_get_resource_class.return_value = self.fake_resource
|
mock_get_resource_class.return_value = self.fake_resource
|
||||||
with mock.patch.object(self.dbapi, 'update_resource_class',
|
with mock.patch.object(self.dbapi, 'update_resource_class',
|
||||||
autospec=True) as mock_update:
|
autospec=True) as mock_update:
|
||||||
resource = objects.ResourceClass.get_by_id(
|
resource = objects.ResourceClass.get_by_uuid(
|
||||||
self.context, rc_id)
|
self.context, rc_uuid)
|
||||||
resource.name = 'MEMORY_MB'
|
resource.name = 'MEMORY_MB'
|
||||||
resource.save()
|
resource.save()
|
||||||
|
|
||||||
mock_get_resource_class.assert_called_once_with(
|
mock_get_resource_class.assert_called_once_with(
|
||||||
self.context, rc_id)
|
self.context, rc_uuid)
|
||||||
mock_update.assert_called_once_with(
|
mock_update.assert_called_once_with(
|
||||||
None, rc_id,
|
None, rc_uuid,
|
||||||
{'name': 'MEMORY_MB'})
|
{'name': 'MEMORY_MB'})
|
||||||
self.assertEqual(self.context, resource._context)
|
self.assertEqual(self.context, resource._context)
|
||||||
|
|
||||||
def test_refresh(self):
|
def test_refresh(self):
|
||||||
rc_id = self.fake_resource['id']
|
rc_uuid = self.fake_resource['uuid']
|
||||||
name = self.fake_resource['name']
|
name = self.fake_resource['name']
|
||||||
new_name = 'MEMORY_MB'
|
new_name = 'MEMORY_MB'
|
||||||
returns = [dict(self.fake_resource, name=name),
|
returns = [dict(self.fake_resource, name=name),
|
||||||
dict(self.fake_resource, name=new_name)]
|
dict(self.fake_resource, name=new_name)]
|
||||||
expected = [mock.call(self.context, rc_id),
|
expected = [mock.call(self.context, rc_uuid),
|
||||||
mock.call(self.context, rc_id)]
|
mock.call(self.context, rc_uuid)]
|
||||||
with mock.patch.object(self.dbapi, 'get_resource_class',
|
with mock.patch.object(self.dbapi, 'get_resource_class',
|
||||||
side_effect=returns,
|
side_effect=returns,
|
||||||
autospec=True) as mock_get_resource_class:
|
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)
|
self.assertEqual(name, resource.name)
|
||||||
resource.refresh()
|
resource.refresh()
|
||||||
self.assertEqual(new_name, resource.name)
|
self.assertEqual(new_name, resource.name)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user