diff --git a/nova/tests/unit/virt/ironic/test_driver.py b/nova/tests/unit/virt/ironic/test_driver.py index 103a9b8d084e..2e5e05f856dc 100644 --- a/nova/tests/unit/virt/ironic/test_driver.py +++ b/nova/tests/unit/virt/ironic/test_driver.py @@ -75,6 +75,12 @@ def _get_properties(): 'capabilities': None} +def _get_instance_info(): + return {'vcpus': 1, + 'memory_mb': 1024, + 'local_gb': 10} + + def _get_stats(): return {'cpu_arch': 'x86_64'} @@ -242,8 +248,10 @@ class IronicDriverTestCase(test.NoDBTestCase): node_uuid = uuidutils.generate_uuid() props = _get_properties() stats = _get_stats() + instance_info = _get_instance_info() node = ironic_utils.get_test_node(uuid=node_uuid, instance_uuid=self.instance_uuid, + instance_info=instance_info, properties=props) result = self.driver._node_resource(node) @@ -260,12 +268,12 @@ class IronicDriverTestCase(test.NoDBTestCase): gotkeys = result.keys() gotkeys.sort() self.assertEqual(wantkeys, gotkeys) - self.assertEqual(props['cpus'], result['vcpus']) - self.assertEqual(props['cpus'], result['vcpus_used']) - self.assertEqual(props['memory_mb'], result['memory_mb']) - self.assertEqual(props['memory_mb'], result['memory_mb_used']) - self.assertEqual(props['local_gb'], result['local_gb']) - self.assertEqual(props['local_gb'], result['local_gb_used']) + self.assertEqual(instance_info['vcpus'], result['vcpus']) + self.assertEqual(instance_info['vcpus'], result['vcpus_used']) + self.assertEqual(instance_info['memory_mb'], result['memory_mb']) + self.assertEqual(instance_info['memory_mb'], result['memory_mb_used']) + self.assertEqual(instance_info['local_gb'], result['local_gb']) + self.assertEqual(instance_info['local_gb'], result['local_gb_used']) self.assertEqual(node_uuid, result['hypervisor_hostname']) self.assertEqual(stats, jsonutils.loads(result['stats'])) @@ -363,19 +371,21 @@ class IronicDriverTestCase(test.NoDBTestCase): node_uuid = uuidutils.generate_uuid() props = _get_properties() stats = _get_stats() + instance_info = _get_instance_info() node = ironic_utils.get_test_node( uuid=node_uuid, instance_uuid=uuidutils.generate_uuid(), provision_state=ironic_states.ACTIVE, - properties=props) + properties=props, + instance_info=instance_info) result = self.driver._node_resource(node) - self.assertEqual(props['cpus'], result['vcpus']) - self.assertEqual(props['cpus'], result['vcpus_used']) - self.assertEqual(props['memory_mb'], result['memory_mb']) - self.assertEqual(props['memory_mb'], result['memory_mb_used']) - self.assertEqual(props['local_gb'], result['local_gb']) - self.assertEqual(props['local_gb'], result['local_gb_used']) + self.assertEqual(instance_info['vcpus'], result['vcpus']) + self.assertEqual(instance_info['vcpus'], result['vcpus_used']) + self.assertEqual(instance_info['memory_mb'], result['memory_mb']) + self.assertEqual(instance_info['memory_mb'], result['memory_mb_used']) + self.assertEqual(instance_info['local_gb'], result['local_gb']) + self.assertEqual(instance_info['local_gb'], result['local_gb_used']) self.assertEqual(node_uuid, result['hypervisor_hostname']) self.assertEqual(stats, jsonutils.loads(result['stats'])) @@ -418,6 +428,32 @@ class IronicDriverTestCase(test.NoDBTestCase): self.assertEqual(expected_props, parsed) self.assertEqual(4, mock_warning.call_count) + @mock.patch.object(ironic_driver.LOG, 'warning') + def test__parse_node_instance_info(self, mock_warning): + instance_info = _get_instance_info() + node = ironic_utils.get_test_node( + uuid=uuidutils.generate_uuid(), + instance_info=instance_info) + parsed = self.driver._parse_node_instance_info(node) + + self.assertEqual(instance_info, parsed) + self.assertFalse(mock_warning.called) + + @mock.patch.object(ironic_driver.LOG, 'warning') + def test__parse_node_instance_info_bad_values(self, mock_warning): + instance_info = _get_instance_info() + instance_info['vcpus'] = 'bad-value' + instance_info['memory_mb'] = 'bad-value' + instance_info['local_gb'] = 'bad-value' + node = ironic_utils.get_test_node( + uuid=uuidutils.generate_uuid(), + instance_info=instance_info) + parsed = self.driver._parse_node_instance_info(node) + + expected = {'vcpus': 0, 'memory_mb': 0, 'local_gb': 0} + self.assertEqual(expected, parsed) + self.assertEqual(3, mock_warning.call_count) + @mock.patch.object(ironic_driver.LOG, 'warning') def test__parse_node_properties_canonicalize_cpu_arch(self, mock_warning): props = _get_properties() @@ -843,6 +879,12 @@ class IronicDriverTestCase(test.NoDBTestCase): 'value': str(flavor['swap'])}, {'path': '/instance_info/display_name', 'value': instance.display_name, 'op': 'add'}, + {'path': '/instance_info/vcpus', 'op': 'add', + 'value': str(instance.vcpus)}, + {'path': '/instance_info/memory_mb', 'op': 'add', + 'value': str(instance.memory_mb)}, + {'path': '/instance_info/local_gb', 'op': 'add', + 'value': str(node.properties.get('local_gb', 0))}, {'path': '/instance_uuid', 'op': 'add', 'value': instance.uuid}] mock_update.assert_called_once_with(node.uuid, expected_patch) diff --git a/nova/tests/unit/virt/ironic/test_patcher.py b/nova/tests/unit/virt/ironic/test_patcher.py index 4879098dc9ec..2309da50799b 100644 --- a/nova/tests/unit/virt/ironic/test_patcher.py +++ b/nova/tests/unit/virt/ironic/test_patcher.py @@ -32,19 +32,31 @@ class IronicDriverFieldsTestCase(test.NoDBTestCase): self.flavor = ironic_utils.get_test_flavor() self.ctx = nova_context.get_admin_context() self.instance = fake_instance.fake_instance_obj(self.ctx) + self.node = ironic_utils.get_test_node(driver='fake') # Generic expected patches - self._expected_deploy_patch = [{'path': '/instance_info/image_source', - 'value': self.image_meta['id'], - 'op': 'add'}, - {'path': '/instance_info/root_gb', - 'value': str(self.instance['root_gb']), - 'op': 'add'}, - {'path': '/instance_info/swap_mb', - 'value': str(self.flavor['swap']), - 'op': 'add'}, - {'path': '/instance_info/display_name', - 'value': self.instance['display_name'], - 'op': 'add'}] + self._expected_deploy_patch = [ + {'path': '/instance_info/image_source', + 'value': self.image_meta['id'], + 'op': 'add'}, + {'path': '/instance_info/root_gb', + 'value': str(self.instance['root_gb']), + 'op': 'add'}, + {'path': '/instance_info/swap_mb', + 'value': str(self.flavor['swap']), + 'op': 'add'}, + {'path': '/instance_info/display_name', + 'value': self.instance['display_name'], + 'op': 'add'}, + {'path': '/instance_info/vcpus', + 'value': str(self.instance['vcpus']), + 'op': 'add'}, + {'path': '/instance_info/memory_mb', + 'value': str(self.instance['memory_mb']), + 'op': 'add'}, + {'path': '/instance_info/local_gb', + 'value': str(self.node.properties.get('local_gb', 0)), + 'op': 'add'} + ] self._expected_cleanup_patch = [] def test_create_generic(self): diff --git a/nova/tests/unit/virt/ironic/utils.py b/nova/tests/unit/virt/ironic/utils.py index d43f29028aaa..5b666e074251 100644 --- a/nova/tests/unit/virt/ironic/utils.py +++ b/nova/tests/unit/virt/ironic/utils.py @@ -39,6 +39,7 @@ def get_test_node(**kw): ironic_states.NOSTATE), 'last_error': kw.get('last_error'), 'instance_uuid': kw.get('instance_uuid'), + 'instance_info': kw.get('instance_info'), 'driver': kw.get('driver', 'fake'), 'driver_info': kw.get('driver_info', {}), 'properties': kw.get('properties', {}), diff --git a/nova/virt/ironic/driver.py b/nova/virt/ironic/driver.py index 612e95bb49a7..e0c3680a4007 100644 --- a/nova/virt/ironic/driver.py +++ b/nova/virt/ironic/driver.py @@ -256,6 +256,23 @@ class IronicDriver(virt_driver.ComputeDriver): properties['capabilities'] = node.properties.get('capabilities') return properties + def _parse_node_instance_info(self, node): + """Helper method to parse the node's instance info.""" + instance_info = {} + + for prop in ('vcpus', 'memory_mb', 'local_gb'): + try: + instance_info[prop] = int(node.instance_info.get(prop, 0)) + except (TypeError, ValueError): + LOG.warning(_LW('Node %(uuid)s has a malformed "%(prop)s". ' + 'It should be an integer but its value ' + 'is "%(value)s".'), + {'uuid': node.uuid, 'prop': prop, + 'value': node.instance_info.get(prop)}) + instance_info[prop] = 0 + + return instance_info + def _node_resource(self, node): """Helper method to create resource dict from node stats.""" properties = self._parse_node_properties(node) @@ -303,9 +320,14 @@ class IronicDriver(virt_driver.ComputeDriver): # Node is in the process of deploying, is deployed, or is in # the process of cleaning up from a deploy. Report all of its # resources as in use. - vcpus_used = vcpus - memory_mb_used = memory_mb - local_gb_used = local_gb + instance_info = self._parse_node_instance_info(node) + + # Use instance_info instead of properties here is because the + # properties of a deployed node can be changed which will count + # as available resources. + vcpus_used = vcpus = instance_info['vcpus'] + memory_mb_used = memory_mb = instance_info['memory_mb'] + local_gb_used = local_gb = instance_info['local_gb'] elif self._node_resources_unavailable(node): # The node's current state is such that it should not present any # of its resources to Nova diff --git a/nova/virt/ironic/patcher.py b/nova/virt/ironic/patcher.py index a412c8f3a9ed..20fd9c1e7485 100644 --- a/nova/virt/ironic/patcher.py +++ b/nova/virt/ironic/patcher.py @@ -64,6 +64,12 @@ class GenericDriverFields(object): 'value': str(flavor['swap'])}) patch.append({'path': '/instance_info/display_name', 'op': 'add', 'value': instance.display_name}) + patch.append({'path': '/instance_info/vcpus', 'op': 'add', + 'value': str(instance.vcpus)}) + patch.append({'path': '/instance_info/memory_mb', 'op': 'add', + 'value': str(instance.memory_mb)}) + patch.append({'path': '/instance_info/local_gb', 'op': 'add', + 'value': str(self.node.properties.get('local_gb', 0))}) if instance.ephemeral_gb: patch.append({'path': '/instance_info/ephemeral_gb',