diff --git a/nova/tests/baremetal/db/test_bm_node.py b/nova/tests/baremetal/db/test_bm_node.py index 3caa49cf13..204a6bf7b0 100644 --- a/nova/tests/baremetal/db/test_bm_node.py +++ b/nova/tests/baremetal/db/test_bm_node.py @@ -65,7 +65,7 @@ class BareMetalNodesTestCase(base.BMDBTestCase): self.assertEquals(r['pm_address'], '1') self.assertRaises( - exception.InstanceNotFound, + exception.NodeNotFound, db.bm_node_get, self.context, -1) @@ -113,7 +113,7 @@ class BareMetalNodesTestCase(base.BMDBTestCase): db.bm_node_destroy(self.context, self.ids[0]) self.assertRaises( - exception.InstanceNotFound, + exception.NodeNotFound, db.bm_node_get, self.context, self.ids[0]) @@ -147,7 +147,7 @@ class BareMetalNodesTestCase(base.BMDBTestCase): self.assertEqual(self.ids[1], if_x['bm_node_id']) self.assertRaises( - exception.InstanceNotFound, + exception.NodeNotFound, db.bm_node_get, self.context, self.ids[0]) diff --git a/nova/tests/baremetal/db/utils.py b/nova/tests/baremetal/db/utils.py index 8003054020..e3a7b84894 100644 --- a/nova/tests/baremetal/db/utils.py +++ b/nova/tests/baremetal/db/utils.py @@ -22,6 +22,7 @@ from nova.virt.baremetal.db.sqlalchemy import models as bm_models def new_bm_node(**kwargs): h = bm_models.BareMetalNode() h.id = kwargs.pop('id', None) + h.uuid = kwargs.pop('uuid', None) h.service_host = kwargs.pop('service_host', None) h.instance_uuid = kwargs.pop('instance_uuid', None) h.cpus = kwargs.pop('cpus', 1) diff --git a/nova/tests/baremetal/test_driver.py b/nova/tests/baremetal/test_driver.py index 21956b7b18..160ca35dfd 100644 --- a/nova/tests/baremetal/test_driver.py +++ b/nova/tests/baremetal/test_driver.py @@ -111,7 +111,7 @@ class BareMetalDriverWithDBTestCase(bm_db_base.BMDBTestCase): nic['port_no'], ) result['instance'] = utils.get_test_instance() - result['instance']['node'] = result['node']['id'] + result['instance']['node'] = result['node']['uuid'] result['spawn_params'] = dict( admin_password='test_pass', block_device_info=None, @@ -139,7 +139,7 @@ class BareMetalDriverWithDBTestCase(bm_db_base.BMDBTestCase): self.assertEqual(stats['cpu_arch'], 'test') self.assertEqual(stats['test_spec'], 'test_value') self.assertEqual(stats['hypervisor_type'], 'baremetal') - self.assertEqual(stats['hypervisor_hostname'], '123') + self.assertEqual(stats['hypervisor_hostname'], node['node']['uuid']) self.assertEqual(stats['host'], 'test_host') self.assertEqual(stats['vcpus'], 2) self.assertEqual(stats['host_memory_total'], 2048) @@ -153,7 +153,15 @@ class BareMetalDriverWithDBTestCase(bm_db_base.BMDBTestCase): def test_macs_for_instance(self): node = self._create_node() - expected = set(['01:23:45:67:89:01', '01:23:45:67:89:02']) + expected = set([nic['address'] for nic in node['nic_info']]) + self.assertEqual( + expected, self.driver.macs_for_instance(node['instance'])) + + def test_macs_for_instance_after_spawn(self): + node = self._create_node() + self.driver.spawn(**node['spawn_params']) + + expected = set([nic['address'] for nic in node['nic_info']]) self.assertEqual( expected, self.driver.macs_for_instance(node['instance'])) @@ -184,12 +192,12 @@ class BareMetalDriverWithDBTestCase(bm_db_base.BMDBTestCase): def test_spawn_node_not_found(self): node = self._create_node() db.bm_node_update(self.context, node['node']['id'], - {'id': 9876}) + {'uuid': 'hide-this-node'}) self.assertRaises(exception.NovaException, self.driver.spawn, **node['spawn_params']) - row = db.bm_node_get(self.context, 9876) + row = db.bm_node_get(self.context, node['node']['id']) self.assertEqual(row['task_state'], None) def test_spawn_fails(self): @@ -247,18 +255,18 @@ class BareMetalDriverWithDBTestCase(bm_db_base.BMDBTestCase): def test_get_available_resources(self): node = self._create_node() - resources = self.driver.get_available_resource(node['node']['id']) + resources = self.driver.get_available_resource(node['node']['uuid']) self.assertEqual(resources['memory_mb'], node['node_info']['memory_mb']) self.assertEqual(resources['memory_mb_used'], 0) self.driver.spawn(**node['spawn_params']) - resources = self.driver.get_available_resource(node['node']['id']) + resources = self.driver.get_available_resource(node['node']['uuid']) self.assertEqual(resources['memory_mb_used'], node['node_info']['memory_mb']) self.driver.destroy(**node['destroy_params']) - resources = self.driver.get_available_resource(node['node']['id']) + resources = self.driver.get_available_resource(node['node']['uuid']) self.assertEqual(resources['memory_mb_used'], 0) def test_get_available_nodes(self): @@ -281,7 +289,7 @@ class BareMetalDriverWithDBTestCase(bm_db_base.BMDBTestCase): ] node2 = self._create_node(node_info=node_info, nic_info=nic_info) self.assertEqual(2, len(self.driver.get_available_nodes())) - self.assertEqual(['123', '456'], + self.assertEqual([node1['node']['uuid'], node2['node']['uuid']], self.driver.get_available_nodes()) node1['instance']['hostname'] = 'test-host-1' @@ -298,5 +306,5 @@ class BareMetalDriverWithDBTestCase(bm_db_base.BMDBTestCase): self.driver.destroy(**node2['destroy_params']) self.assertEqual(2, len(self.driver.get_available_nodes())) - self.assertEqual(['123', '456'], + self.assertEqual([node1['node']['uuid'], node2['node']['uuid']], self.driver.get_available_nodes()) diff --git a/nova/virt/baremetal/db/api.py b/nova/virt/baremetal/db/api.py index 44a5c1bcdf..91edc05d93 100644 --- a/nova/virt/baremetal/db/api.py +++ b/nova/virt/baremetal/db/api.py @@ -101,6 +101,10 @@ def bm_node_get_by_instance_uuid(context, instance_uuid): instance_uuid) +def bm_node_get_by_node_uuid(context, node_uuid): + return IMPL.bm_node_get_by_node_uuid(context, node_uuid) + + def bm_node_create(context, values): return IMPL.bm_node_create(context, values) @@ -113,8 +117,8 @@ def bm_node_update(context, bm_node_id, values): return IMPL.bm_node_update(context, bm_node_id, values) -def bm_node_set_uuid_safe(context, bm_node_id, uuid): - return IMPL.bm_node_set_uuid_safe(context, bm_node_id, uuid) +def bm_node_associate_and_update(context, node_uuid, values): + return IMPL.bm_node_associate_and_update(context, node_uuid, values) def bm_pxe_ip_create(context, address, server_address): diff --git a/nova/virt/baremetal/db/sqlalchemy/api.py b/nova/virt/baremetal/db/sqlalchemy/api.py index 4a27e48be1..e06bcd7d2f 100644 --- a/nova/virt/baremetal/db/sqlalchemy/api.py +++ b/nova/virt/baremetal/db/sqlalchemy/api.py @@ -20,6 +20,8 @@ """Implementation of SQLAlchemy backend.""" +import uuid + from sqlalchemy.sql.expression import asc from sqlalchemy.sql.expression import literal_column @@ -132,7 +134,7 @@ def bm_node_get(context, bm_node_id): first() if not result: - raise exception.InstanceNotFound(instance_id=bm_node_id) + raise exception.NodeNotFound(node_id=bm_node_id) return result @@ -152,8 +154,22 @@ def bm_node_get_by_instance_uuid(context, instance_uuid): return result +@sqlalchemy_api.require_admin_context +def bm_node_get_by_node_uuid(context, bm_node_uuid): + result = model_query(context, models.BareMetalNode, read_deleted="no").\ + filter_by(uuid=bm_node_uuid).\ + first() + + if not result: + raise exception.NodeNotFoundByUUID(node_uuid=bm_node_uuid) + + return result + + @sqlalchemy_api.require_admin_context def bm_node_create(context, values): + if not values.get('uuid'): + values['uuid'] = str(uuid.uuid4()) bm_node_ref = models.BareMetalNode() bm_node_ref.update(values) _save(bm_node_ref) @@ -167,11 +183,11 @@ def bm_node_update(context, bm_node_id, values): update(values) if not rows: - raise exception.InstanceNotFound(instance_id=bm_node_id) + raise exception.NodeNotFound(node_id=bm_node_id) @sqlalchemy_api.require_admin_context -def bm_node_set_uuid_safe(context, bm_node_id, values): +def bm_node_associate_and_update(context, node_uuid, values): """Associate an instance to a node safely Associate an instance to a node only if that node is not yet assocated. @@ -182,21 +198,21 @@ def bm_node_set_uuid_safe(context, bm_node_id, values): """ if 'instance_uuid' not in values: raise exception.NovaException(_( - "instance_uuid must be supplied to bm_node_set_uuid_safe")) + "instance_uuid must be supplied to bm_node_associate_and_update")) session = db_session.get_session() with session.begin(): query = model_query(context, models.BareMetalNode, session=session, read_deleted="no").\ - filter_by(id=bm_node_id) + filter_by(uuid=node_uuid) count = query.filter_by(instance_uuid=None).\ update(values, synchronize_session=False) if count != 1: raise exception.NovaException(_( - "Failed to associate instance %(uuid)s to baremetal node " - "%(id)s.") % {'id': bm_node_id, - 'uuid': values['instance_uuid']}) + "Failed to associate instance %(i_uuid)s to baremetal node " + "%(n_uuid)s.") % {'i_uuid': values['instance_uuid'], + 'n_uuid': node_uuid}) ref = query.first() return ref @@ -270,7 +286,7 @@ def bm_pxe_ip_get_by_bm_node_id(context, bm_node_id): first() if not result: - raise exception.InstanceNotFound(instance_id=bm_node_id) + raise exception.NodeNotFound(node_id=bm_node_id) return result @@ -285,7 +301,7 @@ def bm_pxe_ip_associate(context, bm_node_id): filter_by(id=bm_node_id).\ first() if not node_ref: - raise exception.InstanceNotFound(instance_id=bm_node_id) + raise exception.NodeNotFound(node_id=bm_node_id) # Check if the node already has a pxe_ip ip_ref = model_query(context, models.BareMetalPxeIp, @@ -408,6 +424,6 @@ def bm_interface_get_all_by_bm_node_id(context, bm_node_id): all() if not result: - raise exception.InstanceNotFound(instance_id=bm_node_id) + raise exception.NodeNotFound(node_id=bm_node_id) return result diff --git a/nova/virt/baremetal/db/sqlalchemy/migrate_repo/versions/003_add_uuid_to_bm_nodes.py b/nova/virt/baremetal/db/sqlalchemy/migrate_repo/versions/003_add_uuid_to_bm_nodes.py new file mode 100644 index 0000000000..cc9a9316de --- /dev/null +++ b/nova/virt/baremetal/db/sqlalchemy/migrate_repo/versions/003_add_uuid_to_bm_nodes.py @@ -0,0 +1,40 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2013 Hewlett-Packard Development Company, L.P. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from sqlalchemy import Column, MetaData, String, Table, Index + + +def upgrade(migrate_engine): + meta = MetaData() + meta.bind = migrate_engine + + t = Table('bm_nodes', meta, autoload=True) + uuid_col = Column('uuid', String(36)) + t.create_column(uuid_col) + + uuid_ux = Index('uuid_ux', t.c.uuid, unique=True) + uuid_ux.create(migrate_engine) + + +def downgrade(migrate_engine): + meta = MetaData() + meta.bind = migrate_engine + + t = Table('bm_nodes', meta, autoload=True) + uuid_col = Column('uuid', String(length=36)) + + t.drop_column(uuid_col) diff --git a/nova/virt/baremetal/db/sqlalchemy/models.py b/nova/virt/baremetal/db/sqlalchemy/models.py index fe86d72446..e1a8ebb3ac 100644 --- a/nova/virt/baremetal/db/sqlalchemy/models.py +++ b/nova/virt/baremetal/db/sqlalchemy/models.py @@ -34,6 +34,7 @@ class BareMetalNode(BASE, models.NovaBase): __tablename__ = 'bm_nodes' id = Column(Integer, primary_key=True) + uuid = Column(String(36)) service_host = Column(String(255)) instance_uuid = Column(String(36), nullable=True) cpus = Column(Integer) diff --git a/nova/virt/baremetal/driver.py b/nova/virt/baremetal/driver.py index d3d45682bf..b95a3ad619 100755 --- a/nova/virt/baremetal/driver.py +++ b/nova/virt/baremetal/driver.py @@ -186,17 +186,17 @@ class BareMetalDriver(driver.ComputeDriver): return l def _require_node(self, instance): - """Get a node_id out of a manager instance dict. + """Get a node's uuid out of a manager instance dict. - The compute manager is meant to know the node id, so a missing node is + The compute manager is meant to know the node uuid, so missing uuid a significant issue - it may mean we've been passed someone elses data. """ - node_id = instance.get('node') - if not node_id: + node_uuid = instance.get('node') + if not node_uuid: raise exception.NovaException(_( "Baremetal node id not supplied to driver for %r") % instance['uuid']) - return node_id + return node_uuid def _attach_block_devices(self, instance, block_device_info): block_device_mapping = driver.\ @@ -230,18 +230,19 @@ class BareMetalDriver(driver.ComputeDriver): def macs_for_instance(self, instance): context = nova_context.get_admin_context() - node_id = self._require_node(instance) - return set(iface['address'] for iface in - db.bm_interface_get_all_by_bm_node_id(context, node_id)) + node_uuid = self._require_node(instance) + node = db.bm_node_get_by_node_uuid(context, node_uuid) + ifaces = db.bm_interface_get_all_by_bm_node_id(context, node['id']) + return set(iface['address'] for iface in ifaces) def spawn(self, context, instance, image_meta, injected_files, admin_password, network_info=None, block_device_info=None): - node_id = self._require_node(instance) + node_uuid = self._require_node(instance) # NOTE(deva): this db method will raise an exception if the node is # already in use. We call it here to ensure no one else # allocates this node before we begin provisioning it. - node = db.bm_node_set_uuid_safe(context, node_id, + node = db.bm_node_associate_and_update(context, node_uuid, {'instance_uuid': instance['uuid'], 'task_state': baremetal_states.BUILDING}) @@ -265,7 +266,8 @@ class BareMetalDriver(driver.ComputeDriver): with excutils.save_and_reraise_exception(): LOG.error(_("Error deploying instance %(instance)s " "on baremetal node %(node)s.") % - {'instance': instance['uuid'], 'node': node['id']}) + {'instance': instance['uuid'], + 'node': node['uuid']}) # Do not set instance=None yet. This prevents another # spawn() while we are cleaning up. @@ -408,7 +410,7 @@ class BareMetalDriver(driver.ComputeDriver): 'local_gb_used': local_gb_used, 'hypervisor_type': self.get_hypervisor_type(), 'hypervisor_version': self.get_hypervisor_version(), - 'hypervisor_hostname': str(node['id']), + 'hypervisor_hostname': str(node['uuid']), 'cpu_info': 'baremetal cpu', } return dic @@ -418,7 +420,7 @@ class BareMetalDriver(driver.ComputeDriver): def get_available_resource(self, nodename): context = nova_context.get_admin_context() - node = db.bm_node_get(context, nodename) + node = db.bm_node_get_by_node_uuid(context, nodename) dic = self._node_resource(node) return dic @@ -438,7 +440,7 @@ class BareMetalDriver(driver.ComputeDriver): service_host=CONF.host) for node in nodes: res = self._node_resource(node) - nodename = str(node['id']) + nodename = str(node['uuid']) data = {} data['vcpus'] = res['vcpus'] data['vcpus_used'] = res['vcpus_used'] @@ -489,5 +491,5 @@ class BareMetalDriver(driver.ComputeDriver): def get_available_nodes(self): context = nova_context.get_admin_context() - return [str(n['id']) for n in + return [str(n['uuid']) for n in db.bm_node_get_unassociated(context, service_host=CONF.host)] diff --git a/nova/virt/baremetal/pxe.py b/nova/virt/baremetal/pxe.py index 0abede93c0..813f95c059 100644 --- a/nova/virt/baremetal/pxe.py +++ b/nova/virt/baremetal/pxe.py @@ -413,7 +413,7 @@ class PXE(base.NodeDriver): 'pxe_config_path': None, 'root_mb': 0, 'swap_mb': 0}) - except exception.InstanceNotFound: + except exception.NodeNotFound: pass try: @@ -464,7 +464,7 @@ class PXE(base.NodeDriver): raise utils.LoopingCallDone() elif status == baremetal_states.DEPLOYFAIL: locals['error'] = _("PXE deploy failed for instance %s") - except exception.InstanceNotFound: + except exception.NodeNotFound: locals['error'] = _("Baremetal node deleted while waiting " "for deployment of instance %s")