Add update/delete resource inventory

This patch adds two methods to add and to delete resource inventory
in placement client.

Change-Id: Iee05962c3726411cca015c6155e59161aa023d3e
Blueprint: placement-api
This commit is contained in:
Tetsuro Nakamura 2018-06-27 19:33:59 +09:00
parent 44bc4383ab
commit 99924bd79b
3 changed files with 229 additions and 0 deletions

View File

@ -334,3 +334,127 @@ class TestPlacementClient(tests.TestCase):
self.assertRaises(
exceptions.ResourceClassDeletionFailed,
self.client.delete_reservation_class, rc_name)
@mock.patch('blazar.utils.openstack.placement.'
'BlazarPlacementClient.get_resource_provider')
@mock.patch('blazar.utils.openstack.placement.'
'BlazarPlacementClient.get')
@mock.patch('keystoneauth1.session.Session.request')
def test_update_reservation_inventory(self, kss_req, client_get, get_rp):
host_uuid = uuidutils.generate_uuid()
host_name = "compute-1"
rp_uuid = uuidutils.generate_uuid()
rp_name = "blazar_compute-1"
# Build the mock of current resource provider
mock_get_rp_json = {'uuid': rp_uuid,
'name': rp_name,
'generation': 0,
'parent_provider_uuid': host_uuid}
get_rp.return_value = mock_get_rp_json
# Build the mock of "current" inventory for get_inventory()
curr_gen = 11
mock_get_inv_json = {
'inventories': {
'CUSTOM_RESERVATION_CURR': {
"allocation_ratio": 1.0,
"max_unit": 1,
"min_unit": 1,
"reserved": 0,
"step_size": 1,
"total": 1
},
},
"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 = 12
mock_put_json = {
'inventories': {
'CUSTOM_RESERVATION_CURR': {
"allocation_ratio": 1.0,
"max_unit": 1,
"min_unit": 1,
"reserved": 0,
"step_size": 1,
"total": 1
},
'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)
expected_data = {
'inventories': {
'CUSTOM_RESERVATION_CURR': {
"allocation_ratio": 1.0,
"max_unit": 1,
"min_unit": 1,
"reserved": 0,
"step_size": 1,
"total": 1
},
'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')
def test_delete_reservation_inventory(self, kss_req, get_rp):
host_uuid = uuidutils.generate_uuid()
host_name = "compute-1"
rp_uuid = uuidutils.generate_uuid()
rp_name = "blazar_compute-1"
# Build the mock of current resource provider
mock_get_rp_json = {'uuid': rp_uuid,
'name': rp_name,
'generation': 0,
'parent_provider_uuid': host_uuid}
get_rp.return_value = mock_get_rp_json
kss_req.return_value = fake_requests.FakeResponse(200)
self.client.delete_reservation_inventory(host_name, "curr1")
expected_url = ('/resource_providers/%s/inventories'
'/CUSTOM_RESERVATION_CURR1' % rp_uuid)
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)

View File

@ -33,3 +33,12 @@ class ResourceClassCreationFailed(exceptions.BlazarException):
class ResourceClassDeletionFailed(exceptions.BlazarException):
msg_fmt = _("Failed to delete resource class '%(resource_class)s'")
class ResourceProviderNotFound(exceptions.BlazarException):
msg_fmt = _("No such resource provider %(resource_provider)s")
class InventoryUpdateFailed(exceptions.BlazarException):
msg_fmt = _("Failed to update the inventory of resource provider "
"%(resource_provider)s")

View File

@ -255,3 +255,99 @@ class BlazarPlacementClient(object):
reservation_uuid = reservation_uuid.upper().replace("-", "_")
rc_name = 'CUSTOM_RESERVATION_' + reservation_uuid
self.delete_resource_class(rc_name)
def get_inventory(self, rp_uuid):
"""Calls the placement API to get resource inventory information.
:param rp_uuid: UUID of the resource provider to get
"""
url = '/resource_providers/%s/inventories' % rp_uuid
resp = self.get(url)
if resp:
return resp.json()
raise exceptions.ResourceProviderNotFound(resource_provider=rp_uuid)
def update_inventory(self, rp_uuid, inv_data):
"""Update the inventory for the resource provider.
:param rp_uuid: The resource provider UUID for the operation
:param inv_data: The new inventory for the resource provider
:raises: ResourceProviderNotFound or InventoryUpdateFailed error.
"""
curr = self.get_inventory(rp_uuid)
inventories = curr['inventories']
generation = curr['resource_provider_generation']
inventories.update(inv_data)
payload = {
'inventories': inventories,
'resource_provider_generation': generation,
}
url = '/resource_providers/%s/inventories' % rp_uuid
resp = self.put(url, payload)
if resp:
return resp.json()
# TODO(tetsuro): Try again on 409 conflict errors
raise exceptions.InventoryUpdateFailed(resource_provider=rp_uuid)
def delete_inventory(self, rp_uuid, rc_name):
"""Delete the inventory for the resource provider.
:param rp_uuid: The resource provider UUID for the operation
:param rc_name: The resource class name to delete from inventory
:raises: InventoryUpdateFailed error
"""
url = '/resource_providers/%s/inventories/%s' % (rp_uuid, rc_name)
resp = self.delete(url)
if resp:
return
raise exceptions.InventoryUpdateFailed(resource_provider=rp_uuid)
def update_reservation_inventory(self, host_name, reserv_uuid, num):
"""Update the reservation inventory for the reservation provider.
:param host_name: The name of the target host
:param reserv_uuid: The reservation uuid
:param num: The number of the instances to reserve on the host
:return: The updated inventory record
"""
# Get reservation provider uuid
rp_name = "blazar_" + host_name
rp = self.get_resource_provider(rp_name)
rp_uuid = rp['uuid']
# Build inventory data
reserv_uuid = reserv_uuid.upper().replace("-", "_")
rc_name = 'CUSTOM_RESERVATION_' + reserv_uuid
inv_data = {
rc_name: {
"allocation_ratio": 1.0,
"max_unit": 1,
"min_unit": 1,
"reserved": 0,
"step_size": 1,
"total": num
},
}
return self.update_inventory(rp_uuid, inv_data)
def delete_reservation_inventory(self, host_name, reserv_uuid):
"""Delete the reservation inventory for the reservation provider.
:param host_name: The name of the target host
:param reserv_uuid: The reservation uuid
"""
# Get reservation provider uuid
rp_name = "blazar_" + host_name
rp = self.get_resource_provider(rp_name)
rp_uuid = rp['uuid']
# Convert reservation uuid to resource class name
reserv_uuid = reserv_uuid.upper().replace("-", "_")
rc_name = 'CUSTOM_RESERVATION_' + reserv_uuid
self.delete_inventory(rp_uuid, rc_name)