diff --git a/nova/cmd/manage.py b/nova/cmd/manage.py index 3562e87f19f9..d726076c43f2 100644 --- a/nova/cmd/manage.py +++ b/nova/cmd/manage.py @@ -81,6 +81,7 @@ from nova import exception from nova.i18n import _ from nova import objects from nova.objects import flavor as flavor_obj +from nova.objects import instance as instance_obj from nova import quota from nova import rpc from nova import utils @@ -725,6 +726,7 @@ class DbCommands(object): db.aggregate_uuids_online_data_migration, flavor_obj.migrate_flavors, flavor_obj.migrate_flavor_reset_autoincrement, + instance_obj.migrate_instance_keypairs, ) def __init__(self): diff --git a/nova/objects/instance.py b/nova/objects/instance.py index d8d9bde8f115..34a742ac1e97 100644 --- a/nova/objects/instance.py +++ b/nova/objects/instance.py @@ -20,13 +20,16 @@ from oslo_log import log as logging from oslo_serialization import jsonutils from oslo_utils import timeutils from oslo_utils import versionutils +from sqlalchemy.orm import joinedload from nova.cells import opts as cells_opts from nova.cells import rpcapi as cells_rpcapi from nova.cells import utils as cells_utils from nova import db +from nova.db.sqlalchemy import api as db_api +from nova.db.sqlalchemy import models from nova import exception -from nova.i18n import _LE +from nova.i18n import _LE, _LW from nova import notifications from nova import objects from nova.objects import base @@ -1260,3 +1263,38 @@ class InstanceList(base.ObjectListBase, base.NovaObject): instance.obj_reset_changes(['fault']) return faults_by_uuid.keys() + + +@db_api.main_context_manager.writer +def _migrate_instance_keypairs(ctxt, count): + db_extras = ctxt.session.query(models.InstanceExtra).\ + options(joinedload('instance')).\ + filter_by(keypairs=None).\ + filter_by(deleted=0).\ + limit(count).\ + all() + + count_all = len(db_extras) + count_hit = 0 + for db_extra in db_extras: + key_name = db_extra.instance.key_name + keypairs = objects.KeyPairList(objects=[]) + if key_name: + try: + key = objects.KeyPair.get_by_name(ctxt, + db_extra.instance.user_id, + key_name) + keypairs.objects.append(key) + except exception.KeypairNotFound: + LOG.warning( + _LW('Instance %(uuid)s keypair %(keyname)s not found'), + {'uuid': db_extra.instance_uuid, 'keyname': key_name}) + db_extra.keypairs = jsonutils.dumps(keypairs.obj_to_primitive()) + db_extra.save(ctxt.session) + count_hit += 1 + + return count_all, count_hit + + +def migrate_instance_keypairs(ctxt, count): + return _migrate_instance_keypairs(ctxt, count) diff --git a/nova/tests/unit/objects/test_instance.py b/nova/tests/unit/objects/test_instance.py index 033263705b0e..820788cd1eb0 100644 --- a/nova/tests/unit/objects/test_instance.py +++ b/nova/tests/unit/objects/test_instance.py @@ -23,6 +23,7 @@ from oslo_utils import timeutils from nova.cells import rpcapi as cells_rpcapi from nova.compute import flavors +from nova import context from nova import db from nova import exception from nova.network import model as network_model @@ -1731,3 +1732,41 @@ class TestInstanceObjectMisc(test.TestCase): self.assertEqual(['metadata', 'extra', 'extra.numa_topology'], instance._expected_cols(['metadata', 'numa_topology'])) + + def test_migrate_instance_keypairs(self): + ctxt = context.RequestContext('foo', 'bar') + key = objects.KeyPair(context=ctxt, + user_id=ctxt.user_id, + name='testkey', + public_key='keydata', + type='ssh') + key.create() + inst1 = objects.Instance(context=ctxt, + user_id=ctxt.user_id, + project_id=ctxt.project_id, + key_name='testkey') + inst1.create() + inst2 = objects.Instance(context=ctxt, + user_id=ctxt.user_id, + project_id=ctxt.project_id, + key_name='testkey', + keypairs=objects.KeyPairList( + objects=[key])) + inst2.create() + inst3 = objects.Instance(context=ctxt, + user_id=ctxt.user_id, + project_id=ctxt.project_id, + key_name='missingkey') + inst3.create() + + hit, done = instance.migrate_instance_keypairs(ctxt, 10) + self.assertEqual(2, hit) + self.assertEqual(2, done) + db_extra = db.instance_extra_get_by_instance_uuid( + ctxt, inst1.uuid, ['keypairs']) + self.assertIsNotNone(db_extra.keypairs) + db_extra = db.instance_extra_get_by_instance_uuid( + ctxt, inst3.uuid, ['keypairs']) + obj = base.NovaObject.obj_from_primitive( + jsonutils.loads(db_extra['keypairs'])) + self.assertEqual([], obj.objects)