placement: Add support for resource providers

We want some functionality in osc-placement. Add it here rather than
there.

Initially only the resource providers resource type is supported.
Additional resources will be added in future patches.

We're skipping the tests for the broken placement API versions, pending
discussions with keystone folks on how to resolve this long-term.

Change-Id: Ibf5f01b842e6fc79eb95c2c21bd69732de849597
Signed-off-by: Stephen Finucane <stephenfin@redhat.com>
This commit is contained in:
Stephen Finucane 2020-08-07 16:17:08 +01:00
parent fd0c9778d4
commit c1a475cae0
19 changed files with 401 additions and 1 deletions

View File

@ -115,6 +115,7 @@ control which services can be used.
Network <proxies/network>
Object Store <proxies/object_store>
Orchestration <proxies/orchestration>
Placement <proxies/placement>
Shared File System <proxies/shared_file_system>
Workflow <proxies/workflow>
@ -148,6 +149,7 @@ The following services have exposed *Resource* classes.
Network <resources/network/index>
Orchestration <resources/orchestration/index>
Object Store <resources/object_store/index>
Placement <resources/placement/index>
Shared File System <resources/shared_file_system/index>
Workflow <resources/workflow/index>

View File

@ -0,0 +1,21 @@
Placement API
=============
.. automodule:: openstack.placement.v1._proxy
The Placement Class
-------------------
The placement high-level interface is available through the ``placement``
member of a :class:`~openstack.connection.Connection` object.
The ``placement`` member will only be added if the service is detected.
Resource Providers
^^^^^^^^^^^^^^^^^^
.. autoclass:: openstack.placement.v1._proxy.Proxy
:noindex:
:members: create_resource_provider, update_resource_provider,
delete_resource_provider, get_resource_provider,
resource_providers

View File

@ -0,0 +1,7 @@
Placement v1 Resources
======================
.. toctree::
:maxdepth: 1
v1/resource_provider

View File

@ -0,0 +1,13 @@
openstack.placement.v1.resource_provider
========================================
.. automodule:: openstack.placement.v1.resource_provider
The ResourceProvider Class
--------------------------
The ``ResourceProvider`` class inherits from
:class:`~openstack.resource.Resource`.
.. autoclass:: openstack.placement.v1.resource_provider.ResourceProvider
:members:

View File

@ -17,6 +17,7 @@ from openstack.message import message_service
from openstack.network import network_service
from openstack.object_store import object_store_service
from openstack.orchestration import orchestration_service
from openstack.placement import placement_service
from openstack.shared_file_system import shared_file_system_service
from openstack.workflow import workflow_service
@ -117,7 +118,7 @@ class ServicesMixin:
monitoring_events = service_description.ServiceDescription(service_type='monitoring-events')
placement = service_description.ServiceDescription(service_type='placement')
placement = placement_service.PlacementService(service_type='placement')
instance_ha = instance_ha_service.InstanceHaService(service_type='instance-ha')
ha = instance_ha

View File

View File

@ -0,0 +1,21 @@
# 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 openstack.placement.v1 import _proxy
from openstack import service_description
class PlacementService(service_description.ServiceDescription):
"""The placement service."""
supported_versions = {
'1': _proxy.Proxy,
}

View File

View File

@ -0,0 +1,114 @@
# 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 openstack.placement.v1 import resource_provider as _resource_provider
from openstack import proxy
class Proxy(proxy.Proxy):
def create_resource_provider(self, **attrs):
"""Create a new resource provider from attributes.
:param attrs: Keyword arguments which will be used to create a
:class:`~openstack.placement.v1.resource_provider.ResourceProvider`,
comprised of the properties on the ResourceProvider class.
:returns: The results of resource provider creation
:rtype: :class:`~openstack.placement.v1.resource_provider.ResourceProvider`
""" # noqa: E501
return self._create(_resource_provider.ResourceProvider, **attrs)
def delete_resource_provider(self, resource_provider, ignore_missing=True):
"""Delete a resource provider
:param resource_provider: The value can be either the ID of a resource
provider or an
:class:`~openstack.placement.v1.resource_provider.ResourceProvider`,
instance.
:param bool ignore_missing: When set to ``False``
:class:`~openstack.exceptions.ResourceNotFound` will be raised when
the resource provider does not exist. When set to ``True``, no
exception will be set when attempting to delete a nonexistent
resource provider.
:returns: ``None``
"""
self._delete(
_resource_provider.ResourceProvider,
resource_provider,
ignore_missing=ignore_missing,
)
def update_resource_provider(self, resource_provider, **attrs):
"""Update a flavor
:param resource_provider: The value can be either the ID of a resource
provider or an
:class:`~openstack.placement.v1.resource_provider.ResourceProvider`,
instance.
:attrs kwargs: The attributes to update on the resource provider
represented by ``resource_provider``.
:returns: The updated resource provider
:rtype: :class:`~openstack.placement.v1.resource_provider.ResourceProvider`
""" # noqa: E501
return self._update(
_resource_provider.ResourceProvider, resource_provider, **attrs,
)
def get_resource_provider(self, resource_provider):
"""Get a single resource_provider.
:param resource_provider: The value can be either the ID of a resource
provider or an
:class:`~openstack.placement.v1.resource_provider.ResourceProvider`,
instance.
:returns: An instance of
:class:`~openstack.placement.v1.resource_provider.ResourceProvider`
:raises: :class:`~openstack.exceptions.ResourceNotFound` when no
resource provider matching the criteria could be found.
"""
return self._get(
_resource_provider.ResourceProvider, resource_provider,
)
def find_resource_provider(self, name_or_id, ignore_missing=True):
"""Find a single resource_provider.
:param name_or_id: The name or ID of a resource provider.
:param bool ignore_missing: When set to ``False``
:class:`~openstack.exceptions.ResourceNotFound` will be raised when
the resource does not exist. When set to ``True``, None will be
returned when attempting to find a nonexistent resource.
:returns: An instance of
:class:`~openstack.placement.v1.resource_provider.ResourceProvider`
:raises: :class:`~openstack.exceptions.ResourceNotFound` when no
resource provider matching the criteria could be found.
"""
return self._find(
_resource_provider.ResourceProvider,
name_or_id,
ignore_missing=ignore_missing,
)
def resource_providers(self, **query):
"""Retrieve a generator of resource providers.
:param kwargs query: Optional query parameters to be sent to
restrict the resource providers to be returned.
:returns: A generator of resource provider instances.
"""
return self._list(_resource_provider.ResourceProvider, **query)

View File

@ -0,0 +1,56 @@
# 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 openstack import resource
class ResourceProvider(resource.Resource):
resource_key = None
resources_key = 'resource_providers'
base_path = '/resource_providers'
# Capabilities
allow_create = True
allow_fetch = True
allow_commit = True
allow_delete = True
allow_list = True
# Filters
_query_mapping = resource.QueryParameters(
'name', 'member_of', 'resources', 'in_tree', 'required', id='uuid',
)
# The parent_provider_uuid and root_provider_uuid fields were introduced in
# 1.14
# The required query parameter was added in 1.18
# The create operation started returning a body in 1.20
_max_microversion = '1.20'
# Properties
#: The UUID of a resource provider.
id = resource.Body('uuid', alternate_id=True)
#: A consistent view marker that assists with the management of concurrent
#: resource provider updates.
generation = resource.Body('generation')
#: Links pertaining to this flavor. This is a list of dictionaries,
#: each including keys ``href`` and ``rel``.
links = resource.Body('links')
#: The name of this resource provider.
name = resource.Body('name')
#: The UUID of the immediate parent of the resource provider.
parent_provider_id = resource.Body('parent_provider_uuid')
#: Read-only UUID of the top-most provider in this provider tree.
root_provider_id = resource.Body('root_provider_uuid')

View File

@ -0,0 +1,47 @@
# 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 openstack.placement.v1 import resource_provider
from openstack.tests.functional import base
class TestResourceProvider(base.BaseFunctionalTest):
def setUp(self):
super().setUp()
self._set_operator_cloud(interface='admin')
self.NAME = self.getUniqueString()
sot = self.conn.placement.create_resource_provider(name=self.NAME)
assert isinstance(sot, resource_provider.ResourceProvider)
self.assertEqual(self.NAME, sot.name)
self._resource_provider = sot
def tearDown(self):
sot = self.conn.placement.delete_resource_provider(
self._resource_provider)
self.assertIsNone(sot)
super().tearDown()
def test_find(self):
sot = self.conn.placement.find_resource_provider(self.NAME)
self.assertEqual(self.NAME, sot.name)
def test_get(self):
sot = self.conn.placement.get_resource_provider(
self._resource_provider.id)
self.assertEqual(self.NAME, sot.name)
def test_list(self):
names = [o.name for o in self.conn.placement.resource_providers()]
self.assertIn(self.NAME, names)

View File

@ -0,0 +1,54 @@
# 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 openstack.placement.v1 import _proxy
from openstack.placement.v1 import resource_provider
from openstack.tests.unit import test_proxy_base as test_proxy_base
class TestPlacementProxy(test_proxy_base.TestProxyBase):
def setUp(self):
super().setUp()
self.proxy = _proxy.Proxy(self.session)
def test_resource_provider_create(self):
self.verify_create(
self.proxy.create_resource_provider,
resource_provider.ResourceProvider,
)
def test_resource_provider_delete(self):
self.verify_delete(
self.proxy.delete_resource_provider,
resource_provider.ResourceProvider,
False,
)
def test_resource_provider_update(self):
self.verify_update(
self.proxy.update_resource_provider,
resource_provider.ResourceProvider,
False,
)
def test_resource_provider_get(self):
self.verify_get(
self.proxy.get_resource_provider,
resource_provider.ResourceProvider,
)
def test_resource_providers(self):
self.verify_list_no_kwargs(
self.proxy.resource_providers,
resource_provider.ResourceProvider,
)

View File

@ -0,0 +1,56 @@
# 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 openstack.placement.v1 import resource_provider as rp
from openstack.tests.unit import base
FAKE = {
'uuid': '751cd30a-df22-4ef8-b028-67c1c5aeddc3',
'name': 'fake-name',
'parent_provider_uuid': '9900cc2d-88e8-429d-927a-182adf1577b0',
}
class TestResourceProvider(base.TestCase):
def test_basic(self):
sot = rp.ResourceProvider()
self.assertEqual(None, sot.resource_key)
self.assertEqual('resource_providers', sot.resources_key)
self.assertEqual('/resource_providers', sot.base_path)
self.assertTrue(sot.allow_create)
self.assertTrue(sot.allow_fetch)
self.assertTrue(sot.allow_commit)
self.assertTrue(sot.allow_delete)
self.assertTrue(sot.allow_list)
self.assertFalse(sot.allow_patch)
self.assertDictEqual(
{
'limit': 'limit',
'marker': 'marker',
'name': 'name',
'member_of': 'member_of',
'resources': 'resources',
'in_tree': 'in_tree',
'required': 'required',
'id': 'uuid',
},
sot._query_mapping._mapping)
def test_make_it(self):
sot = rp.ResourceProvider(**FAKE)
self.assertEqual(FAKE['uuid'], sot.id)
self.assertEqual(FAKE['name'], sot.name)
self.assertEqual(
FAKE['parent_provider_uuid'], sot.parent_provider_id,
)

View File

@ -74,6 +74,7 @@ class TestPlacementRest(base.TestCase):
class TestBadPlacementRest(base.TestCase):
def setUp(self):
self.skipTest('Need to re-add support for broken placement versions')
super(TestBadPlacementRest, self).setUp()
# The bad-placement.json is for older placement that was
# missing the status field from its discovery doc. This

View File

@ -0,0 +1,7 @@
---
features:
- |
Add initial support for Placement. Currently the following resources are
supported:
- ``ResourceProvider``