From 6b4c38c04177ff194d05368cd4aff69958075167 Mon Sep 17 00:00:00 2001 From: Surya Seetharaman Date: Fri, 11 May 2018 17:12:34 +0200 Subject: [PATCH] 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 --- nova/cmd/manage.py | 6 +++ nova/objects/instance.py | 15 ++++++ nova/tests/functional/db/test_instance.py | 46 +++++++++++++++++++ ...to-populate-inst.avz-29fed2fe57a9764d.yaml | 10 ++++ 4 files changed, 77 insertions(+) create mode 100644 releasenotes/notes/migration-tool-to-populate-inst.avz-29fed2fe57a9764d.yaml diff --git a/nova/cmd/manage.py b/nova/cmd/manage.py index 56e10f366342..bdd2b80a2293 100644 --- a/nova/cmd/manage.py +++ b/nova/cmd/manage.py @@ -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): diff --git a/nova/objects/instance.py b/nova/objects/instance.py index 9f0daf94ad59..8fa6187e6881 100644 --- a/nova/objects/instance.py +++ b/nova/objects/instance.py @@ -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 diff --git a/nova/tests/functional/db/test_instance.py b/nova/tests/functional/db/test_instance.py index 9e800c755d14..b14a763dd5ad 100644 --- a/nova/tests/functional/db/test_instance.py +++ b/nova/tests/functional/db/test_instance.py @@ -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) diff --git a/releasenotes/notes/migration-tool-to-populate-inst.avz-29fed2fe57a9764d.yaml b/releasenotes/notes/migration-tool-to-populate-inst.avz-29fed2fe57a9764d.yaml new file mode 100644 index 000000000000..409234e115ee --- /dev/null +++ b/releasenotes/notes/migration-tool-to-populate-inst.avz-29fed2fe57a9764d.yaml @@ -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