summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZuul <zuul@review.openstack.org>2018-02-13 23:25:48 +0000
committerGerrit Code Review <review@openstack.org>2018-02-13 23:25:48 +0000
commit87036b4b27945b6ae34b57e6ee15dd76eb7f726a (patch)
tree89fe800920a691c4e558444aeb0b63eedd6a6037
parent942d87edd5c7074303e28ffaa2cc1365032e393d (diff)
parent02a1551f6455792ee050d6009c94514402b7df04 (diff)
Merge "Ensure resource classes correctly"
-rw-r--r--nova/scheduler/client/report.py58
-rw-r--r--nova/tests/functional/api/openstack/placement/test_report_client.py16
-rw-r--r--nova/tests/unit/scheduler/client/test_report.py53
3 files changed, 79 insertions, 48 deletions
diff --git a/nova/scheduler/client/report.py b/nova/scheduler/client/report.py
index 1d076e5..549005a 100644
--- a/nova/scheduler/client/report.py
+++ b/nova/scheduler/client/report.py
@@ -1057,9 +1057,7 @@ class SchedulerReportClient(object):
1057 parent_provider_uuid=parent_provider_uuid) 1057 parent_provider_uuid=parent_provider_uuid)
1058 1058
1059 # Auto-create custom resource classes coming from a virt driver 1059 # Auto-create custom resource classes coming from a virt driver
1060 for rc_name in inv_data: 1060 self._ensure_resource_classes(context, set(inv_data))
1061 if rc_name not in fields.ResourceClass.STANDARD:
1062 self._ensure_resource_class(context, rc_name)
1063 1061
1064 if inv_data: 1062 if inv_data:
1065 self._update_inventory(context, rp_uuid, inv_data) 1063 self._update_inventory(context, rp_uuid, inv_data)
@@ -1221,34 +1219,38 @@ class SchedulerReportClient(object):
1221 raise exception.ResourceProviderUpdateFailed(url=url, error=resp.text) 1219 raise exception.ResourceProviderUpdateFailed(url=url, error=resp.text)
1222 1220
1223 @safe_connect 1221 @safe_connect
1224 def _ensure_resource_class(self, context, name): 1222 def _ensure_resource_classes(self, context, names):
1225 """Make sure a custom resource class exists. 1223 """Make sure resource classes exist.
1226
1227 PUT the resource class using microversion 1.7.
1228
1229 Returns the name of the resource class if it was successfully
1230 created or already exists. Otherwise None.
1231 1224
1232 :param context: The security context 1225 :param context: The security context
1233 :param name: String name of the resource class to check/create. 1226 :param names: Iterable of string names of the resource classes to
1234 :raises: `exception.InvalidResourceClass` upon error. 1227 check/create. Must not be None.
1228 :raises: exception.InvalidResourceClass if an attempt is made to create
1229 an invalid resource class.
1235 """ 1230 """
1236 # no payload on the put request 1231 # Placement API version that supports PUT /resource_classes/CUSTOM_*
1237 response = self.put("/resource_classes/%s" % name, None, version="1.7", 1232 # to create (or validate the existence of) a consumer-specified
1238 global_request_id=context.global_id) 1233 # resource class.
1239 if 200 <= response.status_code < 300: 1234 version = '1.7'
1240 return name 1235 to_ensure = set(n for n in names
1241 else: 1236 if n.startswith(fields.ResourceClass.CUSTOM_NAMESPACE))
1242 msg = ("Failed to ensure resource class record with placement API " 1237
1243 "for resource class %(rc_name)s. Got %(status_code)d: " 1238 for name in to_ensure:
1244 "%(err_text)s.") 1239 # no payload on the put request
1245 args = { 1240 resp = self.put(
1246 'rc_name': name, 1241 "/resource_classes/%s" % name, None, version=version,
1247 'status_code': response.status_code, 1242 global_request_id=context.global_id)
1248 'err_text': response.text, 1243 if not resp:
1249 } 1244 msg = ("Failed to ensure resource class record with placement "
1250 LOG.error(msg, args) 1245 "API for resource class %(rc_name)s. Got "
1251 raise exception.InvalidResourceClass(resource_class=name) 1246 "%(status_code)d: %(err_text)s.")
1247 args = {
1248 'rc_name': name,
1249 'status_code': resp.status_code,
1250 'err_text': resp.text,
1251 }
1252 LOG.error(msg, args)
1253 raise exception.InvalidResourceClass(resource_class=name)
1252 1254
1253 def update_compute_node(self, context, compute_node): 1255 def update_compute_node(self, context, compute_node):
1254 """Creates or updates stats for the supplied compute node. 1256 """Creates or updates stats for the supplied compute node.
diff --git a/nova/tests/functional/api/openstack/placement/test_report_client.py b/nova/tests/functional/api/openstack/placement/test_report_client.py
index 06ccf0d..7acea0c 100644
--- a/nova/tests/functional/api/openstack/placement/test_report_client.py
+++ b/nova/tests/functional/api/openstack/placement/test_report_client.py
@@ -207,7 +207,7 @@ class SchedulerReportClientTests(test.TestCase):
207 # Try setting some invalid inventory and make sure the report 207 # Try setting some invalid inventory and make sure the report
208 # client raises the expected error. 208 # client raises the expected error.
209 inv_data = { 209 inv_data = {
210 'BAD_FOO': { 210 'CUSTOM_BOGU$_CLA$$': {
211 'total': 100, 211 'total': 100,
212 'reserved': 0, 212 'reserved': 0,
213 'min_unit': 1, 213 'min_unit': 1,
@@ -279,18 +279,8 @@ class SchedulerReportClientTests(test.TestCase):
279 } 279 }
280 with interceptor.RequestsInterceptor(app=self.app, url=self.url): 280 with interceptor.RequestsInterceptor(app=self.app, url=self.url):
281 self.client.update_compute_node(self.context, self.compute_node) 281 self.client.update_compute_node(self.context, self.compute_node)
282 # Simulate that our locally-running code has an outdated notion of 282 self.client.set_inventory_for_provider(
283 # standard resource classes. 283 self.context, self.compute_uuid, self.compute_name, inv)
284 with mock.patch.object(fields.ResourceClass, 'STANDARD',
285 ('VCPU', 'MEMORY_MB', 'DISK_GB')):
286 # TODO(efried): Once bug #1746615 is fixed, this will no longer
287 # raise, and can be replaced with:
288 # self.client.set_inventory_for_provider(
289 # self.context, self.compute_uuid, self.compute_name, inv)
290 self.assertRaises(
291 exception.InvalidResourceClass,
292 self.client.set_inventory_for_provider,
293 self.context, self.compute_uuid, self.compute_name, inv)
294 284
295 @mock.patch('keystoneauth1.session.Session.get_endpoint', 285 @mock.patch('keystoneauth1.session.Session.get_endpoint',
296 return_value='http://localhost:80/placement') 286 return_value='http://localhost:80/placement')
diff --git a/nova/tests/unit/scheduler/client/test_report.py b/nova/tests/unit/scheduler/client/test_report.py
index 900af83..6114c9e 100644
--- a/nova/tests/unit/scheduler/client/test_report.py
+++ b/nova/tests/unit/scheduler/client/test_report.py
@@ -3119,7 +3119,7 @@ There was a conflict when trying to complete your request.
3119 @mock.patch('nova.scheduler.client.report.SchedulerReportClient.' 3119 @mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
3120 '_delete_inventory') 3120 '_delete_inventory')
3121 @mock.patch('nova.scheduler.client.report.SchedulerReportClient.' 3121 @mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
3122 '_ensure_resource_class') 3122 '_ensure_resource_classes')
3123 @mock.patch('nova.scheduler.client.report.SchedulerReportClient.' 3123 @mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
3124 '_ensure_resource_provider') 3124 '_ensure_resource_provider')
3125 def test_set_inventory_for_provider_no_custom(self, mock_erp, mock_erc, 3125 def test_set_inventory_for_provider_no_custom(self, mock_erp, mock_erc,
@@ -3166,7 +3166,8 @@ There was a conflict when trying to complete your request.
3166 parent_provider_uuid=None, 3166 parent_provider_uuid=None,
3167 ) 3167 )
3168 # No custom resource classes to ensure... 3168 # No custom resource classes to ensure...
3169 self.assertFalse(mock_erc.called) 3169 mock_erc.assert_called_once_with(self.context,
3170 set(['VCPU', 'MEMORY_MB', 'DISK_GB']))
3170 mock_upd.assert_called_once_with( 3171 mock_upd.assert_called_once_with(
3171 self.context, 3172 self.context,
3172 mock.sentinel.rp_uuid, 3173 mock.sentinel.rp_uuid,
@@ -3179,7 +3180,7 @@ There was a conflict when trying to complete your request.
3179 @mock.patch('nova.scheduler.client.report.SchedulerReportClient.' 3180 @mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
3180 '_delete_inventory') 3181 '_delete_inventory')
3181 @mock.patch('nova.scheduler.client.report.SchedulerReportClient.' 3182 @mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
3182 '_ensure_resource_class') 3183 '_ensure_resource_classes')
3183 @mock.patch('nova.scheduler.client.report.SchedulerReportClient.' 3184 @mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
3184 '_ensure_resource_provider') 3185 '_ensure_resource_provider')
3185 def test_set_inventory_for_provider_no_inv(self, mock_erp, mock_erc, 3186 def test_set_inventory_for_provider_no_inv(self, mock_erp, mock_erc,
@@ -3200,7 +3201,7 @@ There was a conflict when trying to complete your request.
3200 mock.sentinel.rp_name, 3201 mock.sentinel.rp_name,
3201 parent_provider_uuid=None, 3202 parent_provider_uuid=None,
3202 ) 3203 )
3203 self.assertFalse(mock_erc.called) 3204 mock_erc.assert_called_once_with(self.context, set())
3204 self.assertFalse(mock_upd.called) 3205 self.assertFalse(mock_upd.called)
3205 mock_del.assert_called_once_with(self.context, mock.sentinel.rp_uuid) 3206 mock_del.assert_called_once_with(self.context, mock.sentinel.rp_uuid)
3206 3207
@@ -3209,7 +3210,7 @@ There was a conflict when trying to complete your request.
3209 @mock.patch('nova.scheduler.client.report.SchedulerReportClient.' 3210 @mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
3210 '_delete_inventory') 3211 '_delete_inventory')
3211 @mock.patch('nova.scheduler.client.report.SchedulerReportClient.' 3212 @mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
3212 '_ensure_resource_class') 3213 '_ensure_resource_classes')
3213 @mock.patch('nova.scheduler.client.report.SchedulerReportClient.' 3214 @mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
3214 '_ensure_resource_provider') 3215 '_ensure_resource_provider')
3215 def test_set_inventory_for_provider_with_custom(self, mock_erp, 3216 def test_set_inventory_for_provider_with_custom(self, mock_erp,
@@ -3265,7 +3266,9 @@ There was a conflict when trying to complete your request.
3265 mock.sentinel.rp_name, 3266 mock.sentinel.rp_name,
3266 parent_provider_uuid=None, 3267 parent_provider_uuid=None,
3267 ) 3268 )
3268 mock_erc.assert_called_once_with(self.context, 'CUSTOM_IRON_SILVER') 3269 mock_erc.assert_called_once_with(
3270 self.context,
3271 set(['VCPU', 'MEMORY_MB', 'DISK_GB', 'CUSTOM_IRON_SILVER']))
3269 mock_upd.assert_called_once_with( 3272 mock_upd.assert_called_once_with(
3270 self.context, 3273 self.context,
3271 mock.sentinel.rp_uuid, 3274 mock.sentinel.rp_uuid,
@@ -3276,7 +3279,7 @@ There was a conflict when trying to complete your request.
3276 @mock.patch('nova.scheduler.client.report.SchedulerReportClient.' 3279 @mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
3277 '_delete_inventory', new=mock.Mock()) 3280 '_delete_inventory', new=mock.Mock())
3278 @mock.patch('nova.scheduler.client.report.SchedulerReportClient.' 3281 @mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
3279 '_ensure_resource_class', new=mock.Mock()) 3282 '_ensure_resource_classes', new=mock.Mock())
3280 @mock.patch('nova.scheduler.client.report.SchedulerReportClient.' 3283 @mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
3281 '_ensure_resource_provider') 3284 '_ensure_resource_provider')
3282 def test_set_inventory_for_provider_with_parent(self, mock_erp): 3285 def test_set_inventory_for_provider_with_parent(self, mock_erp):
@@ -3588,3 +3591,39 @@ class TestAllocations(SchedulerReportClientTestCase):
3588 # With a 409, only the error should be called 3591 # With a 409, only the error should be called
3589 self.assertEqual(0, mock_log.info.call_count) 3592 self.assertEqual(0, mock_log.info.call_count)
3590 self.assertEqual(1, mock_log.error.call_count) 3593 self.assertEqual(1, mock_log.error.call_count)
3594
3595
3596class TestResourceClass(SchedulerReportClientTestCase):
3597 def setUp(self):
3598 super(TestResourceClass, self).setUp()
3599 _put_patch = mock.patch(
3600 "nova.scheduler.client.report.SchedulerReportClient.put")
3601 self.addCleanup(_put_patch.stop)
3602 self.mock_put = _put_patch.start()
3603
3604 def test_ensure_resource_classes(self):
3605 rcs = ['VCPU', 'CUSTOM_FOO', 'MEMORY_MB', 'CUSTOM_BAR']
3606 self.client._ensure_resource_classes(self.context, rcs)
3607 self.mock_put.assert_has_calls([
3608 mock.call('/resource_classes/%s' % rc, None, version='1.7',
3609 global_request_id=self.context.global_id)
3610 for rc in ('CUSTOM_FOO', 'CUSTOM_BAR')
3611 ], any_order=True)
3612
3613 def test_ensure_resource_classes_none(self):
3614 for empty in ([], (), set(), {}):
3615 self.client._ensure_resource_classes(self.context, empty)
3616 self.mock_put.assert_not_called()
3617
3618 def test_ensure_resource_classes_put_fail(self):
3619 resp = requests.Response()
3620 resp.status_code = 503
3621 self.mock_put.return_value = resp
3622 rcs = ['VCPU', 'MEMORY_MB', 'CUSTOM_BAD']
3623 self.assertRaises(
3624 exception.InvalidResourceClass,
3625 self.client._ensure_resource_classes, self.context, rcs)
3626 # Only called with the "bad" one
3627 self.mock_put.assert_called_once_with(
3628 '/resource_classes/CUSTOM_BAD', None, version='1.7',
3629 global_request_id=self.context.global_id)