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 commit6b4c38c041
) (cherry picked from commit0a481a5292
)
This commit is contained in:
parent
cdf2e9e8fd
commit
487c6dd778
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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