From 487c6dd778312780740d8cb9a0b51f3fa177c1c4 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. Conflicts: nova/cmd/manage.py nova/tests/functional/db/test_instance.py NOTE(mriedem): The conflicts are due to the following changes which were added in Queens: I6db4eb46df0d7ec025b969a46621823957503958 I5b4b235b88367c361d38371d430d67ff583a906c I4b33751b6793f60c6f2703c379c36387c49d866d Change-Id: I2a1d81bfeb1ea006c16d8f403e045e9acedcbe57 Closes-Bug: #1768876 (cherry picked from commit 6b4c38c04177ff194d05368cd4aff69958075167) (cherry picked from commit 0a481a52929626c5fab8fd6fd50cca6882db3bd9) --- nova/cmd/manage.py | 5 ++ nova/objects/instance.py | 15 ++++++ nova/tests/functional/db/test_instance.py | 48 +++++++++++++++++++ ...to-populate-inst.avz-29fed2fe57a9764d.yaml | 10 ++++ 4 files changed, 78 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 40861379c42f..5bea13d5d13b 100644 --- a/nova/cmd/manage.py +++ b/nova/cmd/manage.py @@ -668,6 +668,11 @@ class DbCommands(object): quotas_obj.migrate_quota_limits_to_api_db, # Added in Pike quotas_obj.migrate_quota_classes_to_api_db, + # 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 e175eaf25009..c0f0cd5062f8 100644 --- a/nova/objects/instance.py +++ b/nova/objects/instance.py @@ -25,6 +25,7 @@ from sqlalchemy.orm import joinedload 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 @@ -1204,6 +1205,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 75fcce8577d8..38b530134ac8 100644 --- a/nova/tests/functional/db/test_instance.py +++ b/nova/tests/functional/db/test_instance.py @@ -10,8 +10,11 @@ # License for the specific language governing permissions and limitations # under the License. +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 @@ -40,3 +43,48 @@ class InstanceObjectTestCase(test.TestCase): self.context, self.context.project_id, self.context.user_id, vm_states.ACTIVE) self.assertEqual(1, count) + + 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