Merge "Add online migration for Instance.compute_id"
This commit is contained in:
		@@ -185,6 +185,8 @@ class DbCommands(object):
 | 
			
		||||
        instance_mapping_obj.populate_user_id,
 | 
			
		||||
        # Added in Victoria
 | 
			
		||||
        pci_device_obj.PciDevice.populate_dev_uuids,
 | 
			
		||||
        # Added in 2023.2
 | 
			
		||||
        instance_obj.populate_instance_compute_id,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    @args('--local_cell', action='store_true',
 | 
			
		||||
 
 | 
			
		||||
@@ -28,6 +28,7 @@ from sqlalchemy.sql import func
 | 
			
		||||
from nova import availability_zones as avail_zone
 | 
			
		||||
from nova.compute import task_states
 | 
			
		||||
from nova.compute import vm_states
 | 
			
		||||
from nova import context as nova_context
 | 
			
		||||
from nova.db.main import api as db
 | 
			
		||||
from nova.db.main import models
 | 
			
		||||
from nova import exception
 | 
			
		||||
@@ -1351,6 +1352,30 @@ def populate_missing_availability_zones(context, count):
 | 
			
		||||
    return count_all, count_hit
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@db.pick_context_manager_writer
 | 
			
		||||
def populate_instance_compute_id(context, count):
 | 
			
		||||
    instances = (context.session.query(models.Instance).
 | 
			
		||||
        filter(models.Instance.compute_id == None).  # noqa E711
 | 
			
		||||
        limit(count).all())
 | 
			
		||||
    count_all = count_hit = 0
 | 
			
		||||
    rd_context = nova_context.get_admin_context(read_deleted='yes')
 | 
			
		||||
    for instance in instances:
 | 
			
		||||
        count_all += 1
 | 
			
		||||
        try:
 | 
			
		||||
            node = objects.ComputeNode.get_by_host_and_nodename(rd_context,
 | 
			
		||||
                                                                instance.host,
 | 
			
		||||
                                                                instance.node)
 | 
			
		||||
        except exception.ComputeHostNotFound:
 | 
			
		||||
            LOG.error('Unable to migrate instance because host %s with '
 | 
			
		||||
                      'node %s not found', instance.host, instance.node,
 | 
			
		||||
                      instance=instance)
 | 
			
		||||
            continue
 | 
			
		||||
        instance.compute_id = node.id
 | 
			
		||||
        instance.save(context.session)
 | 
			
		||||
        count_hit += 1
 | 
			
		||||
    return count_all, count_hit
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@base.NovaObjectRegistry.register
 | 
			
		||||
class InstanceList(base.ObjectListBase, base.NovaObject):
 | 
			
		||||
    # Version 2.0: Initial Version
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,7 @@ from nova import context
 | 
			
		||||
from nova.db.main import api as db
 | 
			
		||||
from nova import objects
 | 
			
		||||
from nova import test
 | 
			
		||||
from nova import utils
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class InstanceObjectTestCase(test.TestCase):
 | 
			
		||||
@@ -124,6 +125,63 @@ class InstanceObjectTestCase(test.TestCase):
 | 
			
		||||
        inst3 = objects.Instance.get_by_uuid(self.context, uuid3)
 | 
			
		||||
        self.assertEqual('nova-test', inst3.availability_zone)
 | 
			
		||||
 | 
			
		||||
    def test_populate_instance_compute_id(self):
 | 
			
		||||
        # Create three test nodes, one deleted
 | 
			
		||||
        node_values = dict(vcpus=10, memory_mb=1024, local_gb=10,
 | 
			
		||||
                           vcpus_used=0, memory_mb_used=0, local_gb_used=0,
 | 
			
		||||
                           hypervisor_type='libvirt', hypervisor_version='1',
 | 
			
		||||
                           cpu_info='')
 | 
			
		||||
        node1 = objects.ComputeNode(context=self.context, host='fake_host1',
 | 
			
		||||
                                    hypervisor_hostname='fake_node1',
 | 
			
		||||
                                    **node_values)
 | 
			
		||||
        node1.create()
 | 
			
		||||
        node2 = objects.ComputeNode(context=self.context, host='fake_host2',
 | 
			
		||||
                                    hypervisor_hostname='fake_node2',
 | 
			
		||||
                                    **node_values)
 | 
			
		||||
        node2.create()
 | 
			
		||||
        node3 = objects.ComputeNode(context=self.context, host='fake_host3',
 | 
			
		||||
                                    hypervisor_hostname='fake_node3',
 | 
			
		||||
                                    **node_values)
 | 
			
		||||
        node3.create()
 | 
			
		||||
        node3.destroy()
 | 
			
		||||
 | 
			
		||||
        # Create four test instances across the test nodes, including a
 | 
			
		||||
        # deleted instance and one instance with a missing compute node.
 | 
			
		||||
        self._create_instance(host='fake_host1', node='fake_node1',
 | 
			
		||||
                              compute_id=None)
 | 
			
		||||
        self._create_instance(host='fake_host2', node='fake_node2',
 | 
			
		||||
                              compute_id=None)
 | 
			
		||||
        self._create_instance(host='fake_host3', node='fake_node3',
 | 
			
		||||
                              compute_id=None)
 | 
			
		||||
        to_delete = self._create_instance(host='fake_host1', node='fake_node1',
 | 
			
		||||
                              compute_id=None)
 | 
			
		||||
        to_delete.destroy()
 | 
			
		||||
        self._create_instance(host='fake_host4', node='fake_node4',
 | 
			
		||||
                              compute_id=None)
 | 
			
		||||
 | 
			
		||||
        # Do the actual migration
 | 
			
		||||
        count_all, count_hit = objects.instance.populate_instance_compute_id(
 | 
			
		||||
            self.context, 10)
 | 
			
		||||
 | 
			
		||||
        # We should have found all instances, and fixed all but one which
 | 
			
		||||
        # has a node we cannot find
 | 
			
		||||
        self.assertEqual(5, count_all)
 | 
			
		||||
        self.assertEqual(4, count_hit)
 | 
			
		||||
 | 
			
		||||
        with utils.temporary_mutation(self.context, read_deleted='yes'):
 | 
			
		||||
            instances = objects.InstanceList.get_all(self.context)
 | 
			
		||||
 | 
			
		||||
        # Make sure we found all the instances we expect, including the
 | 
			
		||||
        # deleted one.
 | 
			
		||||
        self.assertEqual(5, len(instances))
 | 
			
		||||
 | 
			
		||||
        # Make sure they all have the compute_id set as we expect, including
 | 
			
		||||
        # the one remaining None because the node is not found.
 | 
			
		||||
        expected_nodes = {n.hypervisor_hostname: n.id
 | 
			
		||||
                          for n in [node1, node2, node3]}
 | 
			
		||||
        for inst in instances:
 | 
			
		||||
            self.assertEqual(inst.compute_id, expected_nodes.get(inst.node))
 | 
			
		||||
 | 
			
		||||
    def test_get_count_by_hosts(self):
 | 
			
		||||
        self._create_instance(host='fake_host1')
 | 
			
		||||
        self._create_instance(host='fake_host1')
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user