Skip to content

Commit 1cf3da8

Browse files
committed
Add Migration.cross_cell_move and get_by_uuid
This adds a new boolean column, cross_cell_move, to the migrations table in the cell database and corresponding field in the Migrations versioned object. This will be used to track cross-cell resize operations. Also, a Migration.get_by_uuid() method is added to the versioned object because while doing a cross-cell resize, we will need to be able to lookup a migration record via its uuid which will be unique across cell databases, unlike its id value which is specific to each cell database. Note that we opt to not store the source/dest cell UUIDs directly in the migrations table because the migrations records are stored in the cell database and we prefer to keep the cell information (which is in the API) separated from the cell database, especially in case the cell UUID maps to something that does not exist later (like if the cell is split). Part of blueprint cross-cell-resize Change-Id: I63411214c412af93501b27eb39f97bf980b21760
1 parent cb0cfc9 commit 1cf3da8

File tree

11 files changed

+111
-13
lines changed

11 files changed

+111
-13
lines changed

nova/api/openstack/compute/migrations.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ def _output(self, req, migrations_obj, add_link=False, add_uuid=False):
5656
del obj['deleted']
5757
del obj['deleted_at']
5858
del obj['hidden']
59+
del obj['cross_cell_move']
5960
if not add_uuid:
6061
del obj['uuid']
6162
if 'memory_total' in obj:
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
2+
# not use this file except in compliance with the License. You may obtain
3+
# a copy of the License at
4+
#
5+
# http://www.apache.org/licenses/LICENSE-2.0
6+
#
7+
# Unless required by applicable law or agreed to in writing, software
8+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10+
# License for the specific language governing permissions and limitations
11+
# under the License.
12+
13+
from sqlalchemy import MetaData, Column, Table
14+
from sqlalchemy import Boolean
15+
16+
17+
def upgrade(migrate_engine):
18+
meta = MetaData(bind=migrate_engine)
19+
20+
for prefix in ('', 'shadow_'):
21+
migrations = Table('%smigrations' % prefix, meta, autoload=True)
22+
if not hasattr(migrations.c, 'cross_cell_move'):
23+
cross_cell_move = Column('cross_cell_move', Boolean, default=False)
24+
migrations.create_column(cross_cell_move)

nova/db/sqlalchemy/models.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -792,6 +792,7 @@ class Migration(BASE, NovaBase, models.SoftDeleteMixin):
792792
disk_total = Column(BigInteger, nullable=True)
793793
disk_processed = Column(BigInteger, nullable=True)
794794
disk_remaining = Column(BigInteger, nullable=True)
795+
cross_cell_move = Column(Boolean, default=False)
795796

796797
instance = orm.relationship("Instance", foreign_keys=instance_uuid,
797798
primaryjoin='and_(Migration.instance_uuid == '

nova/objects/migration.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ class Migration(base.NovaPersistentObject, base.NovaObject,
4141
# Version 1.3: Added get_by_id_and_instance()
4242
# Version 1.4: Added migration progress detail
4343
# Version 1.5: Added uuid
44-
VERSION = '1.5'
44+
# Version 1.6: Added cross_cell_move and get_by_uuid().
45+
VERSION = '1.6'
4546

4647
fields = {
4748
'id': fields.IntegerField(),
@@ -65,6 +66,7 @@ class Migration(base.NovaPersistentObject, base.NovaObject,
6566
'disk_total': fields.IntegerField(nullable=True),
6667
'disk_processed': fields.IntegerField(nullable=True),
6768
'disk_remaining': fields.IntegerField(nullable=True),
69+
'cross_cell_move': fields.BooleanField(default=False),
6870
}
6971

7072
@staticmethod
@@ -100,14 +102,16 @@ def obj_make_compatible(self, primitive, target_version):
100102
if target_version < (1, 5):
101103
if 'uuid' in primitive:
102104
del primitive['uuid']
105+
if target_version < (1, 6) and 'cross_cell_move' in primitive:
106+
del primitive['cross_cell_move']
103107

104108
def obj_load_attr(self, attrname):
105109
if attrname == 'migration_type':
106110
# NOTE(danms): The only reason we'd need to load this is if
107111
# some older node sent us one. So, guess the type.
108112
self.migration_type = determine_migration_type(self)
109-
elif attrname == 'hidden':
110-
self.hidden = False
113+
elif attrname in ['hidden', 'cross_cell_move']:
114+
self.obj_set_defaults(attrname)
111115
else:
112116
super(Migration, self).obj_load_attr(attrname)
113117

@@ -124,6 +128,11 @@ def _ensure_uuid(self):
124128
fresh = self.__class__.get_by_id(self.context, self.id)
125129
self.uuid = fresh.uuid
126130

131+
@base.remotable_classmethod
132+
def get_by_uuid(cls, context, migration_uuid):
133+
db_migration = db.migration_get_by_uuid(context, migration_uuid)
134+
return cls._from_db_object(context, cls(), db_migration)
135+
127136
@base.remotable_classmethod
128137
def get_by_id(cls, context, migration_id):
129138
db_migration = db.migration_get(context, migration_id)

nova/tests/functional/api_sample_tests/test_migrations.py

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ def _stub_migrations(stub_self, context, filters):
4848
'deleted_at': None,
4949
'deleted': False,
5050
'uuid': uuids.migration1,
51+
'cross_cell_move': False,
5152
},
5253
{
5354
'id': 5678,
@@ -67,6 +68,7 @@ def _stub_migrations(stub_self, context, filters):
6768
'deleted_at': None,
6869
'deleted': False,
6970
'uuid': uuids.migration2,
71+
'cross_cell_move': False,
7072
}
7173
]
7274
return fake_migrations
@@ -111,7 +113,8 @@ class MigrationsSamplesJsonTestV2_23(api_sample_base.ApiSampleTestBaseV21):
111113
'created_at': datetime.datetime(2016, 0o1, 29, 13, 42, 2),
112114
'updated_at': datetime.datetime(2016, 0o1, 29, 13, 42, 2),
113115
'deleted_at': None,
114-
'deleted': False
116+
'deleted': False,
117+
'cross_cell_move': False,
115118
},
116119
# non in-progress live-migration.
117120
{
@@ -129,7 +132,8 @@ class MigrationsSamplesJsonTestV2_23(api_sample_base.ApiSampleTestBaseV21):
129132
'created_at': datetime.datetime(2016, 0o1, 29, 13, 42, 2),
130133
'updated_at': datetime.datetime(2016, 0o1, 29, 13, 42, 2),
131134
'deleted_at': None,
132-
'deleted': False
135+
'deleted': False,
136+
'cross_cell_move': False,
133137
},
134138
# non in-progress resize.
135139
{
@@ -147,7 +151,8 @@ class MigrationsSamplesJsonTestV2_23(api_sample_base.ApiSampleTestBaseV21):
147151
'created_at': datetime.datetime(2016, 0o1, 22, 13, 42, 2),
148152
'updated_at': datetime.datetime(2016, 0o1, 22, 13, 42, 2),
149153
'deleted_at': None,
150-
'deleted': False
154+
'deleted': False,
155+
'cross_cell_move': False,
151156
},
152157
# in-progress resize.
153158
{
@@ -165,7 +170,8 @@ class MigrationsSamplesJsonTestV2_23(api_sample_base.ApiSampleTestBaseV21):
165170
'created_at': datetime.datetime(2016, 0o1, 22, 13, 42, 2),
166171
'updated_at': datetime.datetime(2016, 0o1, 22, 13, 42, 2),
167172
'deleted_at': None,
168-
'deleted': False
173+
'deleted': False,
174+
'cross_cell_move': False,
169175
}
170176
]
171177

@@ -208,7 +214,8 @@ class MigrationsSamplesJsonTestV2_59(MigrationsSamplesJsonTestV2_23):
208214
'updated_at': datetime.datetime(2016, 0o1, 29, 11, 42, 2),
209215
'deleted_at': None,
210216
'deleted': False,
211-
'uuid': '12341d4b-346a-40d0-83c6-5f4f6892b650'
217+
'uuid': '12341d4b-346a-40d0-83c6-5f4f6892b650',
218+
'cross_cell_move': False,
212219
},
213220
# non in-progress live-migration.
214221
{
@@ -227,7 +234,8 @@ class MigrationsSamplesJsonTestV2_59(MigrationsSamplesJsonTestV2_23):
227234
'updated_at': datetime.datetime(2016, 0o1, 29, 12, 42, 2),
228235
'deleted_at': None,
229236
'deleted': False,
230-
'uuid': '22341d4b-346a-40d0-83c6-5f4f6892b650'
237+
'uuid': '22341d4b-346a-40d0-83c6-5f4f6892b650',
238+
'cross_cell_move': False,
231239
},
232240
# non in-progress resize.
233241
{
@@ -246,7 +254,8 @@ class MigrationsSamplesJsonTestV2_59(MigrationsSamplesJsonTestV2_23):
246254
'updated_at': datetime.datetime(2016, 0o6, 23, 13, 42, 2),
247255
'deleted_at': None,
248256
'deleted': False,
249-
'uuid': '32341d4b-346a-40d0-83c6-5f4f6892b650'
257+
'uuid': '32341d4b-346a-40d0-83c6-5f4f6892b650',
258+
'cross_cell_move': False,
250259
},
251260
# in-progress resize.
252261
{
@@ -265,7 +274,8 @@ class MigrationsSamplesJsonTestV2_59(MigrationsSamplesJsonTestV2_23):
265274
'updated_at': datetime.datetime(2016, 0o6, 23, 14, 42, 2),
266275
'deleted_at': None,
267276
'deleted': False,
268-
'uuid': '42341d4b-346a-40d0-83c6-5f4f6892b650'
277+
'uuid': '42341d4b-346a-40d0-83c6-5f4f6892b650',
278+
'cross_cell_move': False,
269279
}
270280
]
271281

nova/tests/unit/api/openstack/compute/test_migrations.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
'deleted_at': None,
5656
'deleted': False,
5757
'uuid': uuids.migration1,
58+
'cross_cell_move': False,
5859
},
5960
# non in-progress live migration
6061
{
@@ -81,6 +82,7 @@
8182
'deleted_at': None,
8283
'deleted': False,
8384
'uuid': uuids.migration2,
85+
'cross_cell_move': False,
8486
},
8587
# in-progress resize
8688
{
@@ -107,6 +109,7 @@
107109
'deleted_at': None,
108110
'deleted': False,
109111
'uuid': uuids.migration3,
112+
'cross_cell_move': False,
110113
},
111114
# non in-progress resize
112115
{
@@ -133,6 +136,7 @@
133136
'deleted_at': None,
134137
'deleted': False,
135138
'uuid': uuids.migration4,
139+
'cross_cell_move': False,
136140
}
137141
]
138142

nova/tests/unit/api/openstack/compute/test_server_migrations.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
'deleted_at': None,
5757
'deleted': False,
5858
'uuid': uuids.migration1,
59+
'cross_cell_move': False,
5960
},
6061
{
6162
'id': 5678,
@@ -81,6 +82,7 @@
8182
'deleted_at': None,
8283
'deleted': False,
8384
'uuid': uuids.migration2,
85+
'cross_cell_move': False,
8486
}
8587
]
8688

nova/tests/unit/compute/test_compute_api.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4188,7 +4188,8 @@ def test_external_instance_event_evacuating_instance(self):
41884188
'disk_total': None, 'disk_processed': None,
41894189
'disk_remaining': None, 'deleted': False,
41904190
'hidden': False, 'created_at': None,
4191-
'updated_at': None, 'deleted_at': None}
4191+
'updated_at': None, 'deleted_at': None,
4192+
'cross_cell_move': False}
41924193

41934194
def migration_get(context, id):
41944195
return migrations[id]

nova/tests/unit/db/test_migrations.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1021,6 +1021,11 @@ def _check_391(self, engine, data):
10211021
self.assertColumnExists(engine, 'shadow_block_device_mapping',
10221022
'volume_type')
10231023

1024+
def _check_397(self, engine, data):
1025+
for prefix in ('', 'shadow_'):
1026+
self.assertColumnExists(
1027+
engine, '%smigrations' % prefix, 'cross_cell_move')
1028+
10241029

10251030
class TestNovaMigrationsSQLite(NovaMigrationsCheckers,
10261031
test_fixtures.OpportunisticDBTestMixin,

nova/tests/unit/objects/test_migration.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ def fake_db_migration(**updates):
5353
'disk_total': 234567,
5454
'disk_processed': 23456,
5555
'disk_remaining': 211111,
56+
'cross_cell_move': False,
5657
}
5758

5859
if updates:
@@ -108,6 +109,7 @@ def test_create(self, mock_create):
108109
mig.create()
109110
self.assertEqual(fake_migration['dest_compute'], mig.dest_compute)
110111
self.assertIn('uuid', mig)
112+
self.assertFalse(mig.cross_cell_move)
111113
mock_create.assert_called_once_with(ctxt,
112114
{'source_compute': 'foo',
113115
'migration_type': 'resize',
@@ -249,6 +251,16 @@ def test_migrate_unset_type_migration(self):
249251
self.assertEqual('migration', mig.migration_type)
250252
self.assertTrue(mig.obj_attr_is_set('migration_type'))
251253

254+
def test_obj_load_attr_hidden(self):
255+
mig = objects.Migration()
256+
self.assertFalse(mig.hidden)
257+
self.assertIn('hidden', mig)
258+
259+
def test_obj_load_attr_cross_cell_move(self):
260+
mig = objects.Migration()
261+
self.assertFalse(mig.cross_cell_move)
262+
self.assertIn('cross_cell_move', mig)
263+
252264
@mock.patch('nova.db.api.migration_get_by_id_and_instance')
253265
def test_get_by_id_and_instance(self, fake_get):
254266
ctxt = context.get_admin_context()
@@ -276,6 +288,35 @@ def test_create_uuid_on_load(self):
276288
mig = objects.Migration.get_by_id(self.context, db_mig.id)
277289
self.assertEqual(uuid, mig.uuid)
278290

291+
def test_obj_make_compatible(self):
292+
mig = objects.Migration(
293+
cross_cell_move=True, # added in 1.6
294+
uuid=uuidsentinel.migration, # added in 1.5
295+
memory_total=1024, memory_processed=0, memory_remaining=0, # 1.4
296+
disk_total=20, disk_processed=0, disk_remaining=0,
297+
migration_type='resize', hidden=False, # added in 1.2
298+
source_compute='fake-host' # added in 1.0
299+
)
300+
data = lambda x: x['nova_object.data']
301+
primitive = data(mig.obj_to_primitive(target_version='1.5'))
302+
self.assertIn('uuid', primitive)
303+
self.assertNotIn('cross_cell_resize', primitive)
304+
primitive = data(mig.obj_to_primitive(target_version='1.4'))
305+
self.assertIn('memory_total', primitive)
306+
self.assertNotIn('uuid', primitive)
307+
primitive = data(mig.obj_to_primitive(target_version='1.3'))
308+
self.assertIn('migration_type', primitive)
309+
self.assertNotIn('memory_total', primitive)
310+
primitive = data(mig.obj_to_primitive(target_version='1.1'))
311+
self.assertIn('source_compute', primitive)
312+
self.assertNotIn('migration_type', primitive)
313+
314+
@mock.patch('nova.db.api.migration_get_by_uuid')
315+
def test_get_by_uuid(self, mock_db_get):
316+
mock_db_get.return_value = fake_db_migration(uuid=uuidsentinel.mig)
317+
mig = objects.Migration.get_by_uuid(self.context, uuidsentinel.mig)
318+
self.assertEqual(uuidsentinel.mig, mig.uuid)
319+
279320

280321
class TestMigrationObject(test_objects._LocalTest,
281322
_TestMigrationObject):

nova/tests/unit/objects/test_objects.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1096,7 +1096,7 @@ def obj_name(cls):
10961096
'LibvirtLiveMigrateBDMInfo': '1.1-5f4a68873560b6f834b74e7861d71aaf',
10971097
'LibvirtLiveMigrateData': '1.9-7082cc7dd48ca49df71fe3846521b2f3',
10981098
'MemoryDiagnostics': '1.0-2c995ae0f2223bb0f8e523c5cc0b83da',
1099-
'Migration': '1.5-48bebaada664ee15bc23b35b2b814d75',
1099+
'Migration': '1.6-fd6b1abfd8e3ce945348e7b5f04baa28',
11001100
'MigrationContext': '1.1-9fb17b0b521370957a884636499df52d',
11011101
'MigrationList': '1.4-983a9c29d4f1e747ce719dc9063b729b',
11021102
'MonitorMetric': '1.1-53b1db7c4ae2c531db79761e7acc52ba',

0 commit comments

Comments
 (0)