diff --git a/blazar/tests/utils/openstack/test_placement.py b/blazar/tests/utils/openstack/test_placement.py index 6787112f..fadb262c 100644 --- a/blazar/tests/utils/openstack/test_placement.py +++ b/blazar/tests/utils/openstack/test_placement.py @@ -130,6 +130,28 @@ class TestPlacementClient(tests.TestCase): 'parent_provider_uuid': parent_uuid} self.assertEqual(expected, result) + @mock.patch('keystoneauth1.session.Session.request') + def test_get_resource_provider_no_rp(self, kss_req): + rp_name = 'blazar' + + mock_json_data = { + 'resource_providers': [] + } + + kss_req.return_value = fake_requests.FakeResponse( + 200, content=json.dumps(mock_json_data)) + + result = self.client.get_resource_provider(rp_name) + + expected_url = '/resource_providers?name=blazar' + kss_req.assert_called_once_with( + expected_url, 'GET', + endpoint_filter={'service_type': 'placement', + 'interface': 'public'}, + headers={'accept': 'application/json'}, + microversion=PLACEMENT_MICROVERSION, raise_exc=False) + self.assertEqual(None, result) + @mock.patch('keystoneauth1.session.Session.request') def test_get_resource_provider_fail(self, kss_req): rp_name = 'blazar' @@ -286,6 +308,31 @@ class TestPlacementClient(tests.TestCase): headers={'accept': 'application/json'}, microversion=PLACEMENT_MICROVERSION, raise_exc=False) + @mock.patch('keystoneauth1.session.Session.request') + def test_delete_reservation_provider_no_rp(self, kss_req): + host_name = "compute-1" + rp_name = "blazar_compute-1" + get_json_mock = { + 'resource_providers': [] + } + mock_call1 = fake_requests.FakeResponse( + 200, content=json.dumps(get_json_mock)) + mock_call2 = fake_requests.FakeResponse(200) + kss_req.side_effect = [mock_call1, mock_call2] + + self.client.delete_reservation_provider(host_name) + + expected_url_get = "/resource_providers?name=%s" % rp_name + kss_req.assert_any_call( + expected_url_get, 'GET', + endpoint_filter={'service_type': 'placement', + 'interface': 'public'}, + headers={'accept': 'application/json'}, + microversion=PLACEMENT_MICROVERSION, raise_exc=False) + + # Ensure that mock_call2 for delete is not called + self.assertEqual(kss_req.call_count, 1) + @mock.patch('keystoneauth1.session.Session.request') def test_create_reservation_class(self, kss_req): rc_name = 'abc-def' @@ -429,6 +476,84 @@ class TestPlacementClient(tests.TestCase): microversion=PLACEMENT_MICROVERSION, raise_exc=False) self.assertEqual(mock_put_json, result) + @mock.patch('blazar.utils.openstack.placement.' + 'BlazarPlacementClient.get_resource_provider') + @mock.patch('blazar.utils.openstack.placement.' + 'BlazarPlacementClient.create_reservation_provider') + @mock.patch('blazar.utils.openstack.placement.' + 'BlazarPlacementClient.get') + @mock.patch('keystoneauth1.session.Session.request') + def test_update_reservation_inventory_no_rp( + self, kss_req, client_get, create_rp, get_rp): + host_uuid = uuidutils.generate_uuid() + host_name = "compute-1" + rp_uuid = uuidutils.generate_uuid() + rp_name = "blazar_compute-1" + + # Build the mock that there is no existing reservation provider + get_rp.return_value = None + + # Build the mock of created resource provider + mock_post_rp_json = {'uuid': rp_uuid, + 'name': rp_name, + 'generation': 0, + 'parent_provider_uuid': host_uuid} + create_rp.return_value = mock_post_rp_json + + # Build the mock of "current" inventory for get_inventory() + curr_gen = 0 + mock_get_inv_json = { + 'inventories': {}, + "resource_provider_generation": curr_gen + } + client_get.return_value = fake_requests.FakeResponse( + 200, content=json.dumps(mock_get_inv_json)) + + # Build the mock of "updated" inventory for update_inventory() + update_gen = 1 + mock_put_json = { + 'inventories': { + 'CUSTOM_RESERVATION_ADD': { + "allocation_ratio": 1.0, + "max_unit": 1, + "min_unit": 1, + "reserved": 0, + "step_size": 1, + "total": 3 + }, + }, + "resource_provider_generation": update_gen + } + kss_req.return_value = fake_requests.FakeResponse( + 200, content=json.dumps(mock_put_json)) + + result = self.client.update_reservation_inventory(host_name, 'add', 3) + + # Ensure that the create_reservation_provider() is called. + create_rp.assert_called_once_with(host_name) + + expected_data = { + 'inventories': { + 'CUSTOM_RESERVATION_ADD': { + "allocation_ratio": 1.0, + "max_unit": 1, + "min_unit": 1, + "reserved": 0, + "step_size": 1, + "total": 3 + }, + }, + "resource_provider_generation": curr_gen + } + expected_url = '/resource_providers/%s/inventories' % rp_uuid + kss_req.assert_called_once_with( + expected_url, 'PUT', json=expected_data, + endpoint_filter={'service_type': 'placement', + 'interface': 'public'}, + headers={'accept': 'application/json'}, + microversion=PLACEMENT_MICROVERSION, raise_exc=False) + self.assertEqual(mock_put_json, result) + @mock.patch('blazar.utils.openstack.placement.' 'BlazarPlacementClient.get_resource_provider') @mock.patch('keystoneauth1.session.Session.request') @@ -458,3 +583,14 @@ class TestPlacementClient(tests.TestCase): 'interface': 'public'}, headers={'accept': 'application/json'}, microversion=PLACEMENT_MICROVERSION, raise_exc=False) + + @mock.patch('blazar.utils.openstack.placement.' + 'BlazarPlacementClient.get_resource_provider') + def test_delete_reservation_inventory_no_rp(self, get_rp): + host_name = "compute-1" + # Build the mock that there is no existing reservation provider + get_rp.return_value = None + + self.assertRaises( + exceptions.ResourceProviderNotFound, + self.client.delete_reservation_inventory, host_name, "curr1") diff --git a/blazar/utils/openstack/placement.py b/blazar/utils/openstack/placement.py index dfbc4514..bd543981 100644 --- a/blazar/utils/openstack/placement.py +++ b/blazar/utils/openstack/placement.py @@ -98,14 +98,18 @@ class BlazarPlacementClient(object): """Calls the placement API for a resource provider record. :param rp_name: Name of the resource provider - :return: A dict of resource provider information. + :return: A dict of resource provider information + or None if the resource provider doesn't exist. :raise: ResourceProviderRetrievalFailed on error. """ url = "/resource_providers?name=%s" % rp_name resp = self.get(url) if resp: json_resp = resp.json() - return json_resp['resource_providers'][0] + if json_resp['resource_providers']: + return json_resp['resource_providers'][0] + else: + return None msg = ("Failed to get resource provider %(name)s. " "Got %(status_code)d: %(err_text)s.") @@ -192,6 +196,10 @@ class BlazarPlacementClient(object): """Delete the reservation provider, the child of the given host""" rp_name = "blazar_" + host_name rp = self.get_resource_provider(rp_name) + if rp is None: + # If the reservation provider doesn't exist, + # no operation will be performed. + return rp_uuid = rp['uuid'] self.delete_resource_provider(rp_uuid) @@ -319,6 +327,10 @@ class BlazarPlacementClient(object): # Get reservation provider uuid rp_name = "blazar_" + host_name rp = self.get_resource_provider(rp_name) + if rp is None: + # If the reservation provider is not created yet, + # this function creates it. + rp = self.create_reservation_provider(host_name) rp_uuid = rp['uuid'] # Build inventory data @@ -341,10 +353,15 @@ class BlazarPlacementClient(object): :param host_name: The name of the target host :param reserv_uuid: The reservation uuid + :raises: ResourceProviderNotFound if the reservation + provider is not found """ # Get reservation provider uuid rp_name = "blazar_" + host_name rp = self.get_resource_provider(rp_name) + if rp is None: + raise exceptions.ResourceProviderNotFound( + resource_provider=rp_name) rp_uuid = rp['uuid'] # Convert reservation uuid to resource class name