From eaa735f4c5b149ef8214eec84a2abc53db16b04c Mon Sep 17 00:00:00 2001 From: Tetsuro Nakamura Date: Tue, 26 Jun 2018 20:44:05 +0900 Subject: [PATCH] Add create/delete resource class This patch adds two methods to create and to delete a resource class in placement client. Change-Id: Ieb6342bcb5ef15db8021d16ad441c9cdd57d7243 Blueprint: placement-api --- .../tests/utils/openstack/test_placement.py | 49 +++++++++++++++ blazar/utils/openstack/exceptions.py | 8 +++ blazar/utils/openstack/placement.py | 61 +++++++++++++++++++ 3 files changed, 118 insertions(+) diff --git a/blazar/tests/utils/openstack/test_placement.py b/blazar/tests/utils/openstack/test_placement.py index 9daa0be4..8a282b17 100644 --- a/blazar/tests/utils/openstack/test_placement.py +++ b/blazar/tests/utils/openstack/test_placement.py @@ -285,3 +285,52 @@ class TestPlacementClient(tests.TestCase): 'interface': 'public'}, headers={'accept': 'application/json'}, microversion=PLACEMENT_MICROVERSION, raise_exc=False) + + @mock.patch('keystoneauth1.session.Session.request') + def test_create_reservation_class(self, kss_req): + rc_name = 'abc-def' + kss_req.return_value = fake_requests.FakeResponse(200) + + self.client.create_reservation_class(rc_name) + + expected_url = '/resource_classes' + kss_req.assert_called_once_with( + expected_url, 'POST', + endpoint_filter={'service_type': 'placement', + 'interface': 'public'}, + json={'name': 'CUSTOM_RESERVATION_ABC_DEF'}, + headers={'accept': 'application/json'}, + microversion=PLACEMENT_MICROVERSION, raise_exc=False) + + @mock.patch('keystoneauth1.session.Session.request') + def test_create_reservation_class_fail(self, kss_req): + rc_name = 'abc-def' + kss_req.return_value = fake_requests.FakeResponse(400) + + self.assertRaises( + exceptions.ResourceClassCreationFailed, + self.client.create_reservation_class, rc_name) + + @mock.patch('keystoneauth1.session.Session.request') + def test_delete_reservation_class(self, kss_req): + rc_name = 'abc-def' + kss_req.return_value = fake_requests.FakeResponse(200) + + self.client.delete_reservation_class(rc_name) + + expected_url = '/resource_classes/CUSTOM_RESERVATION_ABC_DEF' + kss_req.assert_called_once_with( + expected_url, 'DELETE', + endpoint_filter={'service_type': 'placement', + 'interface': 'public'}, + headers={'accept': 'application/json'}, + microversion=PLACEMENT_MICROVERSION, raise_exc=False) + + @mock.patch('keystoneauth1.session.Session.request') + def test_delete_reservation_class_fail(self, kss_req): + rc_name = 'CUSTOM_RESERVATION_abc-def' + kss_req.return_value = fake_requests.FakeResponse(400) + + self.assertRaises( + exceptions.ResourceClassDeletionFailed, + self.client.delete_reservation_class, rc_name) diff --git a/blazar/utils/openstack/exceptions.py b/blazar/utils/openstack/exceptions.py index f5c8404b..690d50ce 100644 --- a/blazar/utils/openstack/exceptions.py +++ b/blazar/utils/openstack/exceptions.py @@ -25,3 +25,11 @@ class ResourceProviderCreationFailed(exceptions.BlazarException): class ResourceProviderDeletionFailed(exceptions.BlazarException): msg_fmt = _("Failed to delete resource provider %(uuid)s") + + +class ResourceClassCreationFailed(exceptions.BlazarException): + msg_fmt = _("Failed to create resource class '%(resource_class)s'") + + +class ResourceClassDeletionFailed(exceptions.BlazarException): + msg_fmt = _("Failed to delete resource class '%(resource_class)s'") diff --git a/blazar/utils/openstack/placement.py b/blazar/utils/openstack/placement.py index 51a26b1d..633ec998 100644 --- a/blazar/utils/openstack/placement.py +++ b/blazar/utils/openstack/placement.py @@ -194,3 +194,64 @@ class BlazarPlacementClient(object): rp = self.get_resource_provider(rp_name) rp_uuid = rp['uuid'] self.delete_resource_provider(rp_uuid) + + def create_resource_class(self, rc_name): + """Calls the placement API to create a resource class. + + :param rc_name: string name of the resource class to create. This + shall be something like "CUSTOM_RESERVATION_{uuid}". + :raises: ResourceClassCreationFailed error. + """ + url = '/resource_classes' + payload = {'name': rc_name} + resp = self.post(url, payload) + if resp: + LOG.info("Created resource class %s", rc_name) + return + msg = ("Failed to create resource class with placement API for " + "%(rc_name)s. Got %(status_code)d: %(err_text)s.") + args = { + 'rc_name': rc_name, + 'status_code': resp.status_code, + 'err_text': resp.text, + } + LOG.error(msg, args) + raise exceptions.ResourceClassCreationFailed(resource_class=rc_name) + + def delete_resource_class(self, rc_name): + """Calls the placement API to delete a resource class. + + :param rc_name: string name of the resource class to delete. This + shall be something like "CUSTOM_RESERVATION_{uuid}" + :raises: ResourceClassDeletionFailed error. + """ + url = '/resource_classes/%s' % rc_name + resp = self.delete(url) + if resp: + LOG.info("Deleted resource class %s", rc_name) + return + msg = ("Failed to delete resource class with placement API for " + "%(rc_name)s. Got %(status_code)d: %(err_text)s.") + args = { + 'rc_name': rc_name, + 'status_code': resp.status_code, + 'err_text': resp.text, + } + LOG.error(msg, args) + raise exceptions.ResourceClassDeletionFailed(resource_class=rc_name) + + def create_reservation_class(self, reservation_uuid): + """Create the reservation class from the given reservation uuid""" + # Placement API doesn't accept resource classes with lower characters + # and "-"(hyphen) in its name. We should translate the uuid here. + reservation_uuid = reservation_uuid.upper().replace("-", "_") + rc_name = 'CUSTOM_RESERVATION_' + reservation_uuid + self.create_resource_class(rc_name) + + def delete_reservation_class(self, reservation_uuid): + """Delete the reservation class from the given reservation uuid""" + # Placement API doesn't accept resource classes with lower characters + # and "-"(hyphen) in its name. We should translate the uuid here. + reservation_uuid = reservation_uuid.upper().replace("-", "_") + rc_name = 'CUSTOM_RESERVATION_' + reservation_uuid + self.delete_resource_class(rc_name)