diff --git a/doc/source/user/index.rst b/doc/source/user/index.rst index b13a63bf4..dc1630ee6 100644 --- a/doc/source/user/index.rst +++ b/doc/source/user/index.rst @@ -115,6 +115,7 @@ control which services can be used. Network Object Store Orchestration + Placement Shared File System Workflow @@ -148,6 +149,7 @@ The following services have exposed *Resource* classes. Network Orchestration Object Store + Placement Shared File System Workflow diff --git a/doc/source/user/proxies/placement.rst b/doc/source/user/proxies/placement.rst new file mode 100644 index 000000000..0cc3f8974 --- /dev/null +++ b/doc/source/user/proxies/placement.rst @@ -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 diff --git a/doc/source/user/resources/placement/index.rst b/doc/source/user/resources/placement/index.rst new file mode 100644 index 000000000..7f581b9bb --- /dev/null +++ b/doc/source/user/resources/placement/index.rst @@ -0,0 +1,7 @@ +Placement v1 Resources +====================== + +.. toctree:: + :maxdepth: 1 + + v1/resource_provider diff --git a/doc/source/user/resources/placement/v1/resource_provider.rst b/doc/source/user/resources/placement/v1/resource_provider.rst new file mode 100644 index 000000000..8ab028b4c --- /dev/null +++ b/doc/source/user/resources/placement/v1/resource_provider.rst @@ -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: diff --git a/openstack/_services_mixin.py b/openstack/_services_mixin.py index 48f45f282..0e90577b0 100644 --- a/openstack/_services_mixin.py +++ b/openstack/_services_mixin.py @@ -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 diff --git a/openstack/placement/__init__.py b/openstack/placement/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/openstack/placement/placement_service.py b/openstack/placement/placement_service.py new file mode 100644 index 000000000..045b36a88 --- /dev/null +++ b/openstack/placement/placement_service.py @@ -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, + } diff --git a/openstack/placement/v1/__init__.py b/openstack/placement/v1/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/openstack/placement/v1/_proxy.py b/openstack/placement/v1/_proxy.py new file mode 100644 index 000000000..9f222afef --- /dev/null +++ b/openstack/placement/v1/_proxy.py @@ -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) diff --git a/openstack/placement/v1/resource_provider.py b/openstack/placement/v1/resource_provider.py new file mode 100644 index 000000000..f9ab1a9b3 --- /dev/null +++ b/openstack/placement/v1/resource_provider.py @@ -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') diff --git a/openstack/tests/functional/placement/__init__.py b/openstack/tests/functional/placement/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/openstack/tests/functional/placement/v1/__init__.py b/openstack/tests/functional/placement/v1/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/openstack/tests/functional/placement/v1/test_resource_provider.py b/openstack/tests/functional/placement/v1/test_resource_provider.py new file mode 100644 index 000000000..11bee5298 --- /dev/null +++ b/openstack/tests/functional/placement/v1/test_resource_provider.py @@ -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) diff --git a/openstack/tests/unit/placement/__init__.py b/openstack/tests/unit/placement/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/openstack/tests/unit/placement/v1/__init__.py b/openstack/tests/unit/placement/v1/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/openstack/tests/unit/placement/v1/test_proxy.py b/openstack/tests/unit/placement/v1/test_proxy.py new file mode 100644 index 000000000..210704f09 --- /dev/null +++ b/openstack/tests/unit/placement/v1/test_proxy.py @@ -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, + ) diff --git a/openstack/tests/unit/placement/v1/test_resource_provider.py b/openstack/tests/unit/placement/v1/test_resource_provider.py new file mode 100644 index 000000000..0b13d0a2a --- /dev/null +++ b/openstack/tests/unit/placement/v1/test_resource_provider.py @@ -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, + ) diff --git a/openstack/tests/unit/test_placement_rest.py b/openstack/tests/unit/test_placement_rest.py index cd8104997..6ac80e28a 100644 --- a/openstack/tests/unit/test_placement_rest.py +++ b/openstack/tests/unit/test_placement_rest.py @@ -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 diff --git a/releasenotes/notes/add-placement-support-a2011eb1e900804d.yaml b/releasenotes/notes/add-placement-support-a2011eb1e900804d.yaml new file mode 100644 index 000000000..dd9ee1d43 --- /dev/null +++ b/releasenotes/notes/add-placement-support-a2011eb1e900804d.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + Add initial support for Placement. Currently the following resources are + supported: + + - ``ResourceProvider``