Add resource provider objects

Spec: https://review.openstack.org/#/c/427007/

Change-Id: Iadeb659e6f778f480a99b0bd7d418dc044d236cd
Implements: blueprint expose-host-capabilities
This commit is contained in:
Hongbin Lu 2017-02-13 15:59:00 -06:00
parent 18f9225ae8
commit 0398d43837
3 changed files with 306 additions and 3 deletions

View File

@ -13,12 +13,17 @@
from zun.objects import container
from zun.objects import image
from zun.objects import resource_provider
from zun.objects import zun_service
Container = container.Container
ZunService = zun_service.ZunService
Image = image.Image
ResourceProvider = resource_provider.ResourceProvider
__all__ = (Container,
ZunService,
Image)
__all__ = (
Container,
ZunService,
Image,
ResourceProvider,
)

View File

@ -0,0 +1,161 @@
# 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_versionedobjects import fields
from zun.db import api as dbapi
from zun.objects import base
@base.ZunObjectRegistry.register
class ResourceProvider(base.ZunPersistentObject, base.ZunObject):
# Version 1.0: Initial version
VERSION = '1.0'
fields = {
'id': fields.IntegerField(read_only=True),
'uuid': fields.UUIDField(nullable=False),
'name': fields.StringField(nullable=False),
'root_provider': fields.UUIDField(nullable=False),
'parent_provider': fields.UUIDField(nullable=True),
'can_host': fields.IntegerField(default=0),
}
@staticmethod
def _from_db_object(provider, db_provider):
"""Converts a database entity to a formal object."""
for field in provider.fields:
setattr(provider, field, db_provider[field])
provider.obj_reset_changes()
return provider
@staticmethod
def _from_db_object_list(db_objects, cls, context):
"""Converts a list of database entities to a list of formal objects."""
return [ResourceProvider._from_db_object(cls(context), obj)
for obj in db_objects]
@base.remotable_classmethod
def get_by_uuid(cls, context, uuid):
"""Find a resource provider based on uuid.
:param uuid: the uuid of a resource provider.
:param context: Security context
:returns: a :class:`ResourceProvider` object.
"""
db_provider = dbapi.get_resource_provider(context, uuid)
provider = ResourceProvider._from_db_object(cls(context), db_provider)
return provider
@base.remotable_classmethod
def get_by_name(cls, context, name):
"""Find a resource provider based on name.
:param name: the logical name of a resource provider.
:param context: Security context
:returns: a :class:`ResourceProvider` object.
"""
db_provider = dbapi.get_resource_provider(context, name)
provider = ResourceProvider._from_db_object(cls(context), db_provider)
return provider
@base.remotable_classmethod
def list(cls, context, limit=None, marker=None,
sort_key=None, sort_dir=None, filters=None):
"""Return a list of ResourceProvider objects.
:param context: Security context.
:param limit: maximum number of resources to return in a single result.
:param marker: pagination marker for large data sets.
:param sort_key: column to sort results by.
:param sort_dir: direction to sort. "asc" or "desc".
:param filters: filters when list resource providers.
:returns: a list of :class:`ResourceProvider` object.
"""
db_providers = dbapi.list_resource_providers(
context, limit=limit, marker=marker, sort_key=sort_key,
sort_dir=sort_dir, filters=filters)
return ResourceProvider._from_db_object_list(
db_providers, cls, context)
@base.remotable
def create(self, context):
"""Create a ResourceProvider record in the DB.
:param context: Security context. NOTE: This should only
be used internally by the indirection_api.
Unfortunately, RPC requires context as the first
argument, even though we don't use it.
A context should be set when instantiating the
object, e.g.: ResourceProvider(context)
"""
values = self.obj_get_changes()
db_provider = dbapi.create_resource_provider(context, values)
self._from_db_object(self, db_provider)
@base.remotable
def destroy(self, context=None):
"""Delete the ResourceProvider from the DB.
:param context: Security context. NOTE: This should only
be used internally by the indirection_api.
Unfortunately, RPC requires context as the first
argument, even though we don't use it.
A context should be set when instantiating the
object, e.g.: ResourceProvider(context)
"""
dbapi.destroy_resource_provider(context, self.uuid)
self.obj_reset_changes()
@base.remotable
def save(self, context=None):
"""Save updates to this ResourceProvider.
Updates will be made column by column based on the result
of self.what_changed().
:param context: Security context. NOTE: This should only
be used internally by the indirection_api.
Unfortunately, RPC requires context as the first
argument, even though we don't use it.
A context should be set when instantiating the
object, e.g.: ResourceProvider(context)
"""
updates = self.obj_get_changes()
dbapi.update_resource_provider(context, self.uuid, updates)
self.obj_reset_changes()
@base.remotable
def refresh(self, context=None):
"""Loads updates for this ResourceProvider.
Loads a resource provider with the same uuid from the database and
checks for updated attributes. Updates are applied from
the loaded resource provider column by column, if there are any
updates.
:param context: Security context. NOTE: This should only
be used internally by the indirection_api.
Unfortunately, RPC requires context as the first
argument, even though we don't use it.
A context should be set when instantiating the
object, e.g.: ResourceProvider(context)
"""
current = self.__class__.get_by_uuid(self._context, uuid=self.uuid)
for field in self.fields:
if self.obj_attr_is_set(field) and \
getattr(self, field) != getattr(current, field):
setattr(self, field, getattr(current, field))

View File

@ -0,0 +1,137 @@
# 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.
import mock
from oslo_utils import uuidutils
from testtools.matchers import HasLength
from zun import objects
from zun.tests.unit.db import base
from zun.tests.unit.db import utils
class TestResourceProviderObject(base.DbTestCase):
def setUp(self):
super(TestResourceProviderObject, self).setUp()
self.fake_provider = utils.get_test_resource_provider()
def test_get_by_uuid(self):
uuid = self.fake_provider['uuid']
with mock.patch.object(self.dbapi, 'get_resource_provider',
autospec=True) as mock_get_resource_provider:
mock_get_resource_provider.return_value = self.fake_provider
provider = objects.ResourceProvider.get_by_uuid(self.context, uuid)
mock_get_resource_provider.assert_called_once_with(
self.context, uuid)
self.assertEqual(self.context, provider._context)
def test_get_by_name(self):
name = self.fake_provider['name']
with mock.patch.object(self.dbapi, 'get_resource_provider',
autospec=True) as mock_get_resource_provider:
mock_get_resource_provider.return_value = self.fake_provider
provider = objects.ResourceProvider.get_by_name(self.context, name)
mock_get_resource_provider.assert_called_once_with(
self.context, name)
self.assertEqual(self.context, provider._context)
def test_list(self):
with mock.patch.object(self.dbapi, 'list_resource_providers',
autospec=True) as mock_get_list:
mock_get_list.return_value = [self.fake_provider]
providers = objects.ResourceProvider.list(self.context)
self.assertEqual(1, mock_get_list.call_count)
self.assertThat(providers, HasLength(1))
self.assertIsInstance(providers[0], objects.ResourceProvider)
self.assertEqual(self.context, providers[0]._context)
def test_list_with_filters(self):
with mock.patch.object(self.dbapi, 'list_resource_providers',
autospec=True) as mock_get_list:
mock_get_list.return_value = [self.fake_provider]
filt = {'name': 'testprovider'}
providers = objects.ResourceProvider.list(
self.context, filters=filt)
self.assertEqual(1, mock_get_list.call_count)
self.assertThat(providers, HasLength(1))
self.assertIsInstance(providers[0], objects.ResourceProvider)
self.assertEqual(self.context, providers[0]._context)
mock_get_list.assert_called_once_with(
self.context, filters=filt, limit=None, marker=None,
sort_key=None, sort_dir=None)
def test_create(self):
with mock.patch.object(self.dbapi, 'create_resource_provider',
autospec=True) as mock_create:
mock_create.return_value = self.fake_provider
provider = objects.ResourceProvider(
self.context, **self.fake_provider)
provider.create(self.context)
mock_create.assert_called_once_with(
self.context, self.fake_provider)
self.assertEqual(self.context, provider._context)
def test_destroy(self):
uuid = self.fake_provider['uuid']
with mock.patch.object(self.dbapi, 'get_resource_provider',
autospec=True) as mock_get_resource_provider:
mock_get_resource_provider.return_value = self.fake_provider
with mock.patch.object(self.dbapi, 'destroy_resource_provider',
autospec=True) as mock_destroy:
provider = objects.ResourceProvider.get_by_uuid(
self.context, uuid)
provider.destroy()
mock_get_resource_provider.assert_called_once_with(
self.context, uuid)
mock_destroy.assert_called_once_with(None, uuid)
self.assertEqual(self.context, provider._context)
def test_save(self):
uuid = self.fake_provider['uuid']
with mock.patch.object(self.dbapi, 'get_resource_provider',
autospec=True) as mock_get_resource_provider:
mock_get_resource_provider.return_value = self.fake_provider
with mock.patch.object(self.dbapi, 'update_resource_provider',
autospec=True) as mock_update:
provider = objects.ResourceProvider.get_by_uuid(
self.context, uuid)
provider.name = 'provider2'
provider.root_provider = '09d0fcb9-155e-434a-ad76-3620b6382a37'
provider.save()
mock_get_resource_provider.assert_called_once_with(
self.context, uuid)
mock_update.assert_called_once_with(
None, uuid,
{'name': 'provider2',
'root_provider': '09d0fcb9-155e-434a-ad76-3620b6382a37'})
self.assertEqual(self.context, provider._context)
def test_refresh(self):
uuid = self.fake_provider['uuid']
new_uuid = uuidutils.generate_uuid()
returns = [dict(self.fake_provider, uuid=uuid),
dict(self.fake_provider, uuid=new_uuid)]
expected = [mock.call(self.context, uuid),
mock.call(self.context, uuid)]
with mock.patch.object(self.dbapi, 'get_resource_provider',
side_effect=returns,
autospec=True) as mock_get_resource_provider:
provider = objects.ResourceProvider.get_by_uuid(self.context, uuid)
self.assertEqual(uuid, provider.uuid)
provider.refresh()
self.assertEqual(new_uuid, provider.uuid)
self.assertEqual(
expected, mock_get_resource_provider.call_args_list)
self.assertEqual(self.context, provider._context)