Metadata-API fails to retrieve avz for instances created before Pike
In Pike (through change: I8d426f2635232ffc4b510548a905794ca88d7f99) we started setting instance.avilability_zone during schedule time by calculating the avz of the host into which the instance was scheduled into. After this change was introduced, the metadata request for the avz on the instance (through change: I73c3b10e52ab4cfda9dacc0c0ba92d1fcb60bcc9) started using instance.get(availability_zone) instead of doing the upcall. However this would return None for instances older than Pike whose availability_zone was not mentioned during boot time as it would be set to CONF.default_schedule_zone whose default value is None. This patch adds an online_migration tool to populate missing instance.availability_zone values. Change-Id: I2a1d81bfeb1ea006c16d8f403e045e9acedcbe57 Closes-Bug: #1768876
This commit is contained in:
parent
c1e0d2bf20
commit
6b4c38c041
@ -57,6 +57,7 @@ from nova import objects
|
||||
from nova.objects import block_device as block_device_obj
|
||||
from nova.objects import build_request as build_request_obj
|
||||
from nova.objects import host_mapping as host_mapping_obj
|
||||
from nova.objects import instance as instance_obj
|
||||
from nova.objects import instance_group as instance_group_obj
|
||||
from nova.objects import keypair as keypair_obj
|
||||
from nova.objects import quotas as quotas_obj
|
||||
@ -392,6 +393,11 @@ class DbCommands(object):
|
||||
sa_db.migration_migrate_to_uuid,
|
||||
# Added in Queens
|
||||
block_device_obj.BlockDeviceMapping.populate_uuids,
|
||||
# Added in Rocky
|
||||
# NOTE(tssurya): This online migration is going to be backported to
|
||||
# Queens and Pike since instance.avz of instances before Pike
|
||||
# need to be populated if it was not specified during boot time.
|
||||
instance_obj.populate_missing_availability_zones,
|
||||
)
|
||||
|
||||
def __init__(self):
|
||||
|
@ -24,6 +24,7 @@ from sqlalchemy import or_
|
||||
from sqlalchemy.sql import func
|
||||
from sqlalchemy.sql import null
|
||||
|
||||
from nova import availability_zones as avail_zone
|
||||
from nova.cells import opts as cells_opts
|
||||
from nova.cells import rpcapi as cells_rpcapi
|
||||
from nova.cells import utils as cells_utils
|
||||
@ -1234,6 +1235,20 @@ def _make_instance_list(context, inst_list, db_inst_list, expected_attrs):
|
||||
return inst_list
|
||||
|
||||
|
||||
@db_api.pick_context_manager_writer
|
||||
def populate_missing_availability_zones(context, count):
|
||||
instances = (context.session.query(models.Instance).
|
||||
filter_by(availability_zone=None).limit(count).all())
|
||||
count_all = len(instances)
|
||||
count_hit = 0
|
||||
for instance in instances:
|
||||
az = avail_zone.get_instance_availability_zone(context, instance)
|
||||
instance.availability_zone = az
|
||||
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
|
||||
|
@ -14,6 +14,7 @@ from oslo_utils import uuidutils
|
||||
|
||||
from nova.compute import vm_states
|
||||
from nova import context
|
||||
from nova import db
|
||||
from nova import objects
|
||||
from nova import test
|
||||
|
||||
@ -73,3 +74,48 @@ class InstanceObjectTestCase(test.TestCase):
|
||||
instance = objects.Instance.get_by_uuid(
|
||||
self.context, instance.uuid, expected_attrs=['flavor'])
|
||||
self.assertIsNone(instance.flavor.description)
|
||||
|
||||
def test_populate_missing_availability_zones(self):
|
||||
# create two instances once with avz set and other not set.
|
||||
inst1 = self._create_instance(host="fake-host1")
|
||||
uuid1 = inst1.uuid
|
||||
inst2 = self._create_instance(availability_zone="fake",
|
||||
host="fake-host2")
|
||||
self.assertIsNone(inst1.availability_zone)
|
||||
self.assertEqual("fake", inst2.availability_zone)
|
||||
count_all, count_hit = (objects.instance.
|
||||
populate_missing_availability_zones(self.context, 10))
|
||||
# we get only the instance whose avz was None.
|
||||
self.assertEqual(1, count_all)
|
||||
self.assertEqual(1, count_hit)
|
||||
inst1 = objects.Instance.get_by_uuid(self.context, uuid1)
|
||||
# since instance.host was None, avz is set to
|
||||
# CONF.default_availability_zone i.e 'nova' which is the default zone
|
||||
# for compute services.
|
||||
self.assertEqual('nova', inst1.availability_zone)
|
||||
|
||||
# create an instance with avz as None on a host that has avz.
|
||||
host = 'fake-host'
|
||||
agg_meta = {'name': 'az_agg', 'uuid': uuidutils.generate_uuid(),
|
||||
'metadata': {'availability_zone': 'nova-test'}}
|
||||
agg = objects.Aggregate(self.context, **agg_meta)
|
||||
agg.create()
|
||||
agg = objects.Aggregate.get_by_id(self.context, agg.id)
|
||||
values = {
|
||||
'binary': 'nova-compute',
|
||||
'host': host,
|
||||
'topic': 'compute',
|
||||
'disabled': False,
|
||||
}
|
||||
service = db.service_create(self.context, values)
|
||||
agg.add_host(service['host'])
|
||||
inst3 = self._create_instance(host=host)
|
||||
uuid3 = inst3.uuid
|
||||
self.assertIsNone(inst3.availability_zone)
|
||||
count_all, count_hit = (objects.instance.
|
||||
populate_missing_availability_zones(self.context, 10))
|
||||
# we get only the instance whose avz was None i.e inst3.
|
||||
self.assertEqual(1, count_all)
|
||||
self.assertEqual(1, count_hit)
|
||||
inst3 = objects.Instance.get_by_uuid(self.context, uuid3)
|
||||
self.assertEqual('nova-test', inst3.availability_zone)
|
||||
|
@ -0,0 +1,10 @@
|
||||
---
|
||||
upgrade:
|
||||
- |
|
||||
A new online data migration has been added to populate missing
|
||||
instance.availability_zone values for instances older than Pike whose
|
||||
availability_zone was not specified during boot time. This can be run
|
||||
during the normal ``nova-manage db online_data_migrations`` routine.
|
||||
This fixes `Bug 1768876`_
|
||||
|
||||
.. _Bug 1768876: https://bugs.launchpad.net/nova/+bug/1768876
|
Loading…
Reference in New Issue
Block a user