From c175f2b42c42954454df76476efaad6ea5e3ea5f Mon Sep 17 00:00:00 2001 From: Masahito Muroi Date: Tue, 12 Dec 2017 17:24:42 +0900 Subject: [PATCH] Support resource_properties key in instance reservation This patch adds a 'resource_properties' key for instance reservation as a required parameter. If a user specifies the key when they create instance reservations, Blazar picks up hypervisors that match the key for the reservation. Partially Implements: blueprint flavors-extra-specs Change-Id: Ia8877c667116739d75381776555f91e529c1c21d --- api-ref/source/v1/leases.inc | 6 ++ blazar/plugins/instances/instance_plugin.py | 16 +++-- .../plugins/instances/test_instance_plugin.py | 70 ++++++++++++++----- doc/api_samples/leases/lease-create-req.json | 3 +- doc/api_samples/leases/lease-create-resp.json | 1 + .../leases/lease-details-resp.json | 1 + doc/api_samples/leases/lease-list-resp.json | 1 + doc/api_samples/leases/lease-update-resp.json | 1 + 8 files changed, 77 insertions(+), 22 deletions(-) diff --git a/api-ref/source/v1/leases.inc b/api-ref/source/v1/leases.inc index eafafe63..29850cb9 100644 --- a/api-ref/source/v1/leases.inc +++ b/api-ref/source/v1/leases.inc @@ -92,6 +92,7 @@ are in the ``reservation`` object. - reservation.memory_mb: reservation_memory_mb - reservation.disk_gb: reservation_disk_gb - reservation.affinity : reservation_affinity + - reservation.resource_properties: reservation_resource_properties - reservation.flavor_id: reservation_flavor_id - reservation.server_group_id: reservation_server_group_id - reservation.aggregate_id: reservation_aggregate_id @@ -158,6 +159,7 @@ are in the ``reservation`` object. - reservation.memory_mb: reservation_memory_mb - reservation.disk_gb: reservation_disk_gb - reservation.affinity : reservation_affinity + - reservation.resource_properties: reservation_resource_properties **Example of Create Lease Request** @@ -230,6 +232,7 @@ are in the ``reservation`` object. - reservation.memory_mb: reservation_memory_mb - reservation.disk_gb: reservation_disk_gb - reservation.affinity : reservation_affinity + - reservation.resource_properties: reservation_resource_properties - reservation.flavor_id: reservation_flavor_id - reservation.server_group_id: reservation_server_group_id - reservation.aggregate_id: reservation_aggregate_id @@ -330,6 +333,7 @@ are in the ``reservation`` object. - reservation.memory_mb: reservation_memory_mb - reservation.disk_gb: reservation_disk_gb - reservation.affinity : reservation_affinity + - reservation.resource_properties: reservation_resource_properties - reservation.flavor_id: reservation_flavor_id - reservation.server_group_id: reservation_server_group_id - reservation.aggregate_id: reservation_aggregate_id @@ -400,6 +404,7 @@ are in the ``reservation`` object. - reservation.memory_mb: reservation_memory_mb_optional - reservation.disk_gb: reservation_disk_gb_optional - reservation.affinity : reservation_affinity_optional + - reservation.resource_properties: reservation_resource_properties_optional **Example of Update Lease Request** @@ -472,6 +477,7 @@ are in the ``reservation`` object. - reservation.memory_mb: reservation_memory_mb - reservation.disk_gb: reservation_disk_gb - reservation.affinity : reservation_affinity + - reservation.resource_properties: reservation_resource_properties - reservation.flavor_id: reservation_flavor_id - reservation.server_group_id: reservation_server_group_id - reservation.aggregate_id: reservation_aggregate_id diff --git a/blazar/plugins/instances/instance_plugin.py b/blazar/plugins/instances/instance_plugin.py index f0e5edc7..edef8ab7 100644 --- a/blazar/plugins/instances/instance_plugin.py +++ b/blazar/plugins/instances/instance_plugin.py @@ -114,6 +114,7 @@ class VirtualInstancePlugin(base.BasePlugin, nova.NovaClientWrapper): return max_vcpus, max_memory, max_disk def query_available_hosts(self, cpus=None, memory=None, disk=None, + resource_properties=None, start_date=None, end_date=None, excludes_res=None): """Query hosts that are available for a reservation. @@ -129,8 +130,10 @@ class VirtualInstancePlugin(base.BasePlugin, nova.NovaClientWrapper): filters = plugins_utils.convert_requirements(flavor_definitions) - hosts = db_api.reservable_host_get_all_by_queries(filters) + if resource_properties: + filters += plugins_utils.convert_requirements(resource_properties) + hosts = db_api.reservable_host_get_all_by_queries(filters) free_hosts, reserved_hosts = \ self.filter_hosts_by_reservation(hosts, start_date, end_date, excludes_res) @@ -168,6 +171,7 @@ class VirtualInstancePlugin(base.BasePlugin, nova.NovaClientWrapper): 'cpus': values['vcpus'], 'memory': values['memory_mb'], 'disk': values['disk_gb'], + 'resource_properties': values['resource_properties'], 'start_date': values['start_date'], 'end_date': values['end_date'] } @@ -299,7 +303,8 @@ class VirtualInstancePlugin(base.BasePlugin, nova.NovaClientWrapper): def validate_reservation_param(self, values): marshall_attributes = set(['vcpus', 'memory_mb', 'disk_gb', - 'amount', 'affinity']) + 'amount', 'affinity', + 'resource_properties']) missing_attr = marshall_attributes - set(values.keys()) if missing_attr: raise mgr_exceptions.MissingParameter(param=','.join(missing_attr)) @@ -327,6 +332,7 @@ class VirtualInstancePlugin(base.BasePlugin, nova.NovaClientWrapper): 'disk_gb': values['disk_gb'], 'amount': values['amount'], 'affinity': bool_from_string(values['affinity']), + 'resource_properties': values['resource_properties'] } instance_reservation = db_api.instance_reservation_create( instance_reservation_val) @@ -383,7 +389,8 @@ class VirtualInstancePlugin(base.BasePlugin, nova.NovaClientWrapper): reservation = db_api.reservation_get(reservation_id) lease = db_api.lease_get(reservation['lease_id']) - updatable = ['vcpus', 'memory_mb', 'disk_gb', 'affinity', 'amount'] + updatable = ['vcpus', 'memory_mb', 'disk_gb', 'affinity', 'amount', + 'resource_properties'] if (not any([k in updatable for k in new_values.keys()]) and new_values['start_date'] >= lease['start_date'] and new_values['end_date'] <= lease['end_date']): @@ -529,7 +536,8 @@ class VirtualInstancePlugin(base.BasePlugin, nova.NovaClientWrapper): values['start_date'] = max(datetime.datetime.utcnow(), lease['start_date']) values['end_date'] = lease['end_date'] - specs = ['vcpus', 'memory_mb', 'disk_gb', 'affinity', 'amount'] + specs = ['vcpus', 'memory_mb', 'disk_gb', 'affinity', 'amount', + 'resource_properties'] for key in specs: values[key] = reservation[key] changed_hosts = self.pickup_hosts(reservation['id'], values) diff --git a/blazar/tests/plugins/instances/test_instance_plugin.py b/blazar/tests/plugins/instances/test_instance_plugin.py index e23dd438..5f7931b0 100644 --- a/blazar/tests/plugins/instances/test_instance_plugin.py +++ b/blazar/tests/plugins/instances/test_instance_plugin.py @@ -36,10 +36,12 @@ class TestVirtualInstancePlugin(tests.TestCase): super(TestVirtualInstancePlugin, self).setUp() def get_input_values(self, vcpus, memory, disk, amount, affinity, - start, end, lease_id): - return {'vcpus': vcpus, 'memory_mb': memory, 'disk_gb': disk, - 'amount': amount, 'affinity': affinity, 'start_date': start, - 'end_date': end, 'lease_id': lease_id} + start, end, lease_id, resource_properties): + values = {'vcpus': vcpus, 'memory_mb': memory, 'disk_gb': disk, + 'amount': amount, 'affinity': affinity, 'start_date': start, + 'end_date': end, 'lease_id': lease_id, + 'resource_properties': resource_properties} + return values def generate_host_info(self, id, vcpus, memory, disk): return {'id': id, 'vcpus': vcpus, @@ -93,7 +95,7 @@ class TestVirtualInstancePlugin(tests.TestCase): inputs = self.get_input_values(2, 4018, 10, 1, False, '2030-01-01 08:00', '2030-01-01 08:00', - 'lease-1') + 'lease-1', '') expected_ret = 'instance-reservation-id1' @@ -102,7 +104,8 @@ class TestVirtualInstancePlugin(tests.TestCase): self.assertEqual(expected_ret, ret) pickup_hosts_value = {} for key in ['vcpus', 'memory_mb', 'disk_gb', 'amount', 'affinity', - 'lease_id', 'start_date', 'end_date']: + 'lease_id', 'start_date', 'end_date', + 'resource_properties']: pickup_hosts_value[key] = inputs[key] mock_pickup_hosts.assert_called_once_with('res_id1', pickup_hosts_value) @@ -122,7 +125,7 @@ class TestVirtualInstancePlugin(tests.TestCase): plugin = instance_plugin.VirtualInstancePlugin() inputs = self.get_input_values(2, 4018, 10, 1, True, '2030-01-01 08:00', '2030-01-01 08:00', - 'lease-1') + 'lease-1', '') self.assertRaises(exceptions.BlazarException, plugin.reserve_resource, 'reservation_id', inputs) self.assertRaises(exceptions.BlazarException, @@ -197,6 +200,7 @@ class TestVirtualInstancePlugin(tests.TestCase): 'memory_mb': 1024, 'disk_gb': 20, 'amount': 2, + 'resource_properties': '', 'start_date': '2030-01-01 08:00', 'end_date': '2030-01-01 12:00' } @@ -238,6 +242,7 @@ class TestVirtualInstancePlugin(tests.TestCase): 'memory_mb': 1024, 'disk_gb': 20, 'amount': 2, + 'resource_properties': '', 'start_date': '2030-01-01 08:00', 'end_date': '2030-01-01 12:00' } @@ -291,6 +296,7 @@ class TestVirtualInstancePlugin(tests.TestCase): 'memory_mb': 1024, 'disk_gb': 20, 'amount': 2, + 'resource_properties': '', 'start_date': '2030-01-01 08:00', 'end_date': '2030-01-01 12:00' } @@ -344,6 +350,7 @@ class TestVirtualInstancePlugin(tests.TestCase): 'memory_mb': 1024, 'disk_gb': 20, 'amount': 2, + 'resource_properties': '', 'start_date': '2030-01-01 08:00', 'end_date': '2030-01-01 12:00' } @@ -563,22 +570,38 @@ class TestVirtualInstancePlugin(tests.TestCase): # case: new amount is less than old amount values = self.get_input_values(1, 1024, 10, 1, False, '2020-07-01 10:00', '2020-07-01 11:00', - 'lease-1') + 'lease-1', '') expect = {'added': set([]), 'removed': set(['host-id1', 'host-id2', 'host-id3'])} ret = plugin.pickup_hosts(reservation['id'], values) self.assertEqual(expect['added'], ret['added']) self.assertTrue(len(ret['removed']) == 2) self.assertTrue(all([h in expect['removed'] for h in ret['removed']])) + query_params = { + 'cpus': 1, 'memory': 1024, 'disk': 10, + 'resource_properties': '', + 'start_date': '2020-07-01 10:00', + 'end_date': '2020-07-01 11:00', + 'excludes_res': ['reservation-id1'] + } + mock_query_available.assert_called_with(**query_params) # case: new amount is same but change allocations values = self.get_input_values(1, 1024, 10, 3, False, '2020-07-01 10:00', '2020-07-01 11:00', - 'lease-1') + 'lease-1', '["==", "key1", "value1"]') expect = {'added': set(['host-id4']), 'removed': set(['host-id1'])} ret = plugin.pickup_hosts(reservation['id'], values) self.assertEqual(expect['added'], ret['added']) self.assertEqual(expect['removed'], ret['removed']) + query_params = { + 'cpus': 1, 'memory': 1024, 'disk': 10, + 'resource_properties': '["==", "key1", "value1"]', + 'start_date': '2020-07-01 10:00', + 'end_date': '2020-07-01 11:00', + 'excludes_res': ['reservation-id1'] + } + mock_query_available.assert_called_with(**query_params) # case: new amount is greater than old amount mock_query_available.return_value = [ @@ -589,11 +612,19 @@ class TestVirtualInstancePlugin(tests.TestCase): values = self.get_input_values(1, 1024, 10, 4, False, '2020-07-01 10:00', '2020-07-01 11:00', - 'lease-1') + 'lease-1', '') expect = {'added': set(['host-id4']), 'removed': set([])} ret = plugin.pickup_hosts(reservation['id'], values) self.assertEqual(expect['added'], ret['added']) self.assertEqual(expect['removed'], ret['removed']) + query_params = { + 'cpus': 1, 'memory': 1024, 'disk': 10, + 'resource_properties': '', + 'start_date': '2020-07-01 10:00', + 'end_date': '2020-07-01 11:00', + 'excludes_res': ['reservation-id1'] + } + mock_query_available.assert_called_with(**query_params) def test_update_resources(self): reservation = { @@ -673,7 +704,8 @@ class TestVirtualInstancePlugin(tests.TestCase): 'lease_id': 'lease-id1', 'resource_id': 'instance-reservation-id1', 'vcpus': 2, 'memory_mb': 1024, 'disk_gb': 100, - 'amount': 2, 'affinity': False} + 'amount': 2, 'affinity': False, + 'resource_properties': ''} mock_reservation_get = self.patch(db_api, 'reservation_get') mock_reservation_get.return_value = old_reservation @@ -700,11 +732,11 @@ class TestVirtualInstancePlugin(tests.TestCase): mock_pickup_hosts.assert_called_once_with( 'reservation-id1', {'vcpus': 4, 'memory_mb': 1024, 'disk_gb': 200, - 'amount': 2, 'affinity': False}) + 'amount': 2, 'affinity': False, 'resource_properties': ''}) mock_inst_update.assert_called_once_with( 'instance-reservation-id1', {'vcpus': 4, 'memory_mb': 1024, 'disk_gb': 200, - 'amount': 2, 'affinity': False}) + 'amount': 2, 'affinity': False, 'resource_properties': ''}) mock_update_alloc.assert_called_once_with(set(['host-id1']), set(['host-id2']), 'reservation-id1') @@ -847,6 +879,7 @@ class TestVirtualInstancePlugin(tests.TestCase): 'aggregate_id': 'agg-1', 'affinity': False, 'amount': 3, + 'resource_properties': '', 'computehost_allocations': [{ 'id': 'alloc-1', 'compute_host_id': failed_host['id'], 'reservation_id': 'rsrv-1' @@ -880,6 +913,7 @@ class TestVirtualInstancePlugin(tests.TestCase): 'aggregate_id': 'agg-1', 'affinity': False, 'amount': 3, + 'resource_properties': '', 'computehost_allocations': [{ 'id': 'alloc-1', 'compute_host_id': failed_host['id'], 'reservation_id': 'rsrv-1' @@ -990,7 +1024,8 @@ class TestVirtualInstancePlugin(tests.TestCase): 'disk_gb': 256, 'aggregate_id': 'agg-1', 'affinity': False, - 'amount': 3 + 'amount': 3, + 'resource_properties': '' } dummy_lease = { 'name': 'lease-name', @@ -1039,8 +1074,8 @@ class TestVirtualInstancePlugin(tests.TestCase): 'disk_gb': 256, 'aggregate_id': 'agg-1', 'affinity': False, - 'amount': 3 - } + 'amount': 3, + 'resource_properties': ''} dummy_lease = { 'name': 'lease-name', 'start_date': datetime.datetime(2020, 1, 1, 12, 00), @@ -1098,7 +1133,8 @@ class TestVirtualInstancePlugin(tests.TestCase): 'disk_gb': 256, 'aggregate_id': 'agg-1', 'affinity': False, - 'amount': 3 + 'amount': 3, + 'resource_properties': '' } dummy_lease = { 'name': 'lease-name', diff --git a/doc/api_samples/leases/lease-create-req.json b/doc/api_samples/leases/lease-create-req.json index c55675b1..d0336ea4 100644 --- a/doc/api_samples/leases/lease-create-req.json +++ b/doc/api_samples/leases/lease-create-req.json @@ -18,7 +18,8 @@ "vcpus": 2, "memory_mb": 4096, "disk_gb": 100, - "affinity": false + "affinity": false, + "resource_properties": "" } ], "events": [] diff --git a/doc/api_samples/leases/lease-create-resp.json b/doc/api_samples/leases/lease-create-resp.json index 512f2f4a..722dd6e0 100644 --- a/doc/api_samples/leases/lease-create-resp.json +++ b/doc/api_samples/leases/lease-create-resp.json @@ -41,6 +41,7 @@ "memory_mb": 4096, "disk_gb": 100, "affinity": false, + "resource_properties": "", "flavor_id": "ddc45423-f863-4e4e-8e7a-51d27cfec962", "server_group_id": "33cdfc42-5a04-4fcc-b190-1abebaa056bb", "aggregate_id": 11, diff --git a/doc/api_samples/leases/lease-details-resp.json b/doc/api_samples/leases/lease-details-resp.json index 512f2f4a..722dd6e0 100644 --- a/doc/api_samples/leases/lease-details-resp.json +++ b/doc/api_samples/leases/lease-details-resp.json @@ -41,6 +41,7 @@ "memory_mb": 4096, "disk_gb": 100, "affinity": false, + "resource_properties": "", "flavor_id": "ddc45423-f863-4e4e-8e7a-51d27cfec962", "server_group_id": "33cdfc42-5a04-4fcc-b190-1abebaa056bb", "aggregate_id": 11, diff --git a/doc/api_samples/leases/lease-list-resp.json b/doc/api_samples/leases/lease-list-resp.json index 56c4879e..f138a912 100644 --- a/doc/api_samples/leases/lease-list-resp.json +++ b/doc/api_samples/leases/lease-list-resp.json @@ -42,6 +42,7 @@ "memory_mb": 4096, "disk_gb": 100, "affinity": false, + "resource_properties": "", "flavor_id": "ddc45423-f863-4e4e-8e7a-51d27cfec962", "server_group_id": "33cdfc42-5a04-4fcc-b190-1abebaa056bb", "aggregate_id": 11, diff --git a/doc/api_samples/leases/lease-update-resp.json b/doc/api_samples/leases/lease-update-resp.json index fa7069b5..14ed3f9e 100644 --- a/doc/api_samples/leases/lease-update-resp.json +++ b/doc/api_samples/leases/lease-update-resp.json @@ -41,6 +41,7 @@ "memory_mb": 4096, "disk_gb": 100, "affinity": false, + "resource_properties": "", "flavor_id": "ddc45423-f863-4e4e-8e7a-51d27cfec962", "server_group_id": "33cdfc42-5a04-4fcc-b190-1abebaa056bb", "aggregate_id": 11,