Add Migration object
This adds a migration object implementation and tests. Related to blueprint compute-api-objects Change-Id: Iec0e45f4768cf3fc2f31c1c23c1aadfdefefa797
This commit is contained in:
105
nova/objects/migration.py
Normal file
105
nova/objects/migration.py
Normal file
@@ -0,0 +1,105 @@
|
||||
# Copyright 2013 IBM Corp.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from nova import db
|
||||
from nova.objects import base
|
||||
from nova.objects import instance as instance_obj
|
||||
from nova.objects import utils
|
||||
|
||||
|
||||
class Migration(base.NovaObject):
|
||||
fields = {
|
||||
'id': int,
|
||||
'source_compute': utils.str_or_none,
|
||||
'dest_compute': utils.str_or_none,
|
||||
'source_node': utils.str_or_none,
|
||||
'dest_node': utils.str_or_none,
|
||||
'dest_host': utils.str_or_none,
|
||||
'old_instance_type_id': utils.int_or_none,
|
||||
'new_instance_type_id': utils.int_or_none,
|
||||
'instance_uuid': utils.str_or_none,
|
||||
'status': utils.str_or_none,
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _from_db_object(context, migration, db_migration):
|
||||
for key in migration.fields:
|
||||
migration[key] = db_migration[key]
|
||||
migration._context = context
|
||||
migration.obj_reset_changes()
|
||||
return migration
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_id(cls, context, migration_id):
|
||||
db_migration = db.migration_get(context, migration_id)
|
||||
return cls._from_db_object(context, cls(), db_migration)
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_instance_and_status(cls, context, instance_uuid, status):
|
||||
db_migration = db.migration_get_by_instance_and_status(
|
||||
context, instance_uuid, status)
|
||||
return cls._from_db_object(context, cls(), db_migration)
|
||||
|
||||
@base.remotable
|
||||
def create(self, context):
|
||||
updates = {}
|
||||
for key in self.obj_what_changed():
|
||||
updates[key] = self[key]
|
||||
updates.pop('id', None)
|
||||
db_migration = db.migration_create(context, updates)
|
||||
self._from_db_object(context, self, db_migration)
|
||||
|
||||
@base.remotable
|
||||
def save(self, context):
|
||||
updates = {}
|
||||
for key in self.obj_what_changed():
|
||||
updates[key] = self[key]
|
||||
updates.pop('id', None)
|
||||
db_migration = db.migration_update(context, self.id, updates)
|
||||
self._from_db_object(context, self, db_migration)
|
||||
self.obj_reset_changes()
|
||||
|
||||
@property
|
||||
def instance(self):
|
||||
return instance_obj.Instance.get_by_uuid(self._context,
|
||||
self.instance_uuid)
|
||||
|
||||
|
||||
def _make_list(context, list_obj, item_cls, db_list):
|
||||
list_obj.objects = []
|
||||
for db_item in db_list:
|
||||
item = item_cls._from_db_object(context, item_cls(), db_item)
|
||||
list_obj.objects.append(item)
|
||||
list_obj.obj_reset_changes()
|
||||
return list_obj
|
||||
|
||||
|
||||
class MigrationList(base.ObjectListBase, base.NovaObject):
|
||||
@base.remotable_classmethod
|
||||
def get_unconfirmed_by_dest_compute(cls, context, confirm_window,
|
||||
dest_compute):
|
||||
db_migrations = db.migration_get_unconfirmed_by_dest_compute(
|
||||
context, confirm_window, dest_compute)
|
||||
return _make_list(context, MigrationList(), Migration, db_migrations)
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_in_progress_by_host_and_node(cls, context, host, node):
|
||||
db_migrations = db.migration_get_in_progress_by_host_and_node(
|
||||
context, host, node)
|
||||
return _make_list(context, MigrationList(), Migration, db_migrations)
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_filters(cls, context, filters):
|
||||
db_migrations = db.migration_get_all_by_filters(context, filters)
|
||||
return _make_list(context, MigrationList(), Migration, db_migrations)
|
||||
161
nova/tests/objects/test_migration.py
Normal file
161
nova/tests/objects/test_migration.py
Normal file
@@ -0,0 +1,161 @@
|
||||
# Copyright 2013 IBM Corp.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import datetime
|
||||
|
||||
from nova import context
|
||||
from nova import db
|
||||
from nova.objects import migration
|
||||
from nova.openstack.common import timeutils
|
||||
from nova.tests import fake_instance
|
||||
from nova.tests.objects import test_objects
|
||||
|
||||
|
||||
NOW = timeutils.utcnow().replace(microsecond=0)
|
||||
fake_migration = {
|
||||
'created_at': NOW,
|
||||
'updated_at': None,
|
||||
'deleted_at': None,
|
||||
'deleted': False,
|
||||
'id': 123,
|
||||
'source_compute': 'compute-source',
|
||||
'dest_compute': 'compute-dest',
|
||||
'source_node': 'node-source',
|
||||
'dest_node': 'node-dest',
|
||||
'dest_host': 'host-dest',
|
||||
'old_instance_type_id': 42,
|
||||
'new_instance_type_id': 84,
|
||||
'instance_uuid': 'fake-uuid',
|
||||
'status': 'migrating',
|
||||
}
|
||||
|
||||
|
||||
class _TestMigrationObject(object):
|
||||
def _compare(self, obj, db_obj):
|
||||
for key in obj.fields:
|
||||
obj_val = obj[key]
|
||||
if isinstance(obj_val, datetime.datetime):
|
||||
obj_val = obj_val.replace(tzinfo=None)
|
||||
db_val = db_obj[key]
|
||||
self.assertEqual(db_val, obj_val)
|
||||
|
||||
def test_get_by_id(self):
|
||||
ctxt = context.get_admin_context()
|
||||
self.mox.StubOutWithMock(db, 'migration_get')
|
||||
db.migration_get(ctxt, fake_migration['id']).AndReturn(fake_migration)
|
||||
self.mox.ReplayAll()
|
||||
mig = migration.Migration.get_by_id(ctxt, fake_migration['id'])
|
||||
self._compare(mig, fake_migration)
|
||||
|
||||
def test_get_by_instance_and_status(self):
|
||||
ctxt = context.get_admin_context()
|
||||
self.mox.StubOutWithMock(db, 'migration_get_by_instance_and_status')
|
||||
db.migration_get_by_instance_and_status(ctxt,
|
||||
fake_migration['id'],
|
||||
'migrating'
|
||||
).AndReturn(fake_migration)
|
||||
self.mox.ReplayAll()
|
||||
mig = migration.Migration.get_by_instance_and_status(
|
||||
ctxt, fake_migration['id'], 'migrating')
|
||||
self._compare(mig, fake_migration)
|
||||
|
||||
def test_create(self):
|
||||
ctxt = context.get_admin_context()
|
||||
self.mox.StubOutWithMock(db, 'migration_create')
|
||||
db.migration_create(ctxt, {'source_compute': 'foo'}).AndReturn(
|
||||
fake_migration)
|
||||
self.mox.ReplayAll()
|
||||
mig = migration.Migration()
|
||||
mig.source_compute = 'foo'
|
||||
mig.create(ctxt)
|
||||
self.assertEqual(fake_migration['dest_compute'], mig.dest_compute)
|
||||
|
||||
def test_save(self):
|
||||
ctxt = context.get_admin_context()
|
||||
self.mox.StubOutWithMock(db, 'migration_update')
|
||||
db.migration_update(ctxt, 123, {'source_compute': 'foo'}
|
||||
).AndReturn(fake_migration)
|
||||
self.mox.ReplayAll()
|
||||
mig = migration.Migration()
|
||||
mig.id = 123
|
||||
mig.source_compute = 'foo'
|
||||
mig.save(ctxt)
|
||||
self.assertEqual(fake_migration['dest_compute'], mig.dest_compute)
|
||||
|
||||
def test_instance(self):
|
||||
ctxt = context.get_admin_context()
|
||||
fake_inst = fake_instance.fake_db_instance()
|
||||
self.mox.StubOutWithMock(db, 'instance_get_by_uuid')
|
||||
db.instance_get_by_uuid(ctxt, fake_migration['instance_uuid'],
|
||||
columns_to_join=[]
|
||||
).AndReturn(fake_inst)
|
||||
mig = migration.Migration._from_db_object(ctxt,
|
||||
migration.Migration(),
|
||||
fake_migration)
|
||||
mig._context = ctxt
|
||||
self.mox.ReplayAll()
|
||||
self.assertEqual(mig.instance.host, fake_inst['host'])
|
||||
|
||||
def test_get_unconfirmed_by_dest_compute(self):
|
||||
ctxt = context.get_admin_context()
|
||||
db_migrations = [fake_migration, dict(fake_migration, id=456)]
|
||||
self.mox.StubOutWithMock(
|
||||
db, 'migration_get_unconfirmed_by_dest_compute')
|
||||
db.migration_get_unconfirmed_by_dest_compute(
|
||||
ctxt, 'window', 'foo').AndReturn(db_migrations)
|
||||
self.mox.ReplayAll()
|
||||
migrations = (
|
||||
migration.MigrationList.get_unconfirmed_by_dest_compute(
|
||||
ctxt, 'window', 'foo'))
|
||||
self.assertEqual(2, len(migrations))
|
||||
for index, db_migration in enumerate(db_migrations):
|
||||
self._compare(migrations[index], db_migration)
|
||||
|
||||
def test_get_in_progress_by_host_and_node(self):
|
||||
ctxt = context.get_admin_context()
|
||||
db_migrations = [fake_migration, dict(fake_migration, id=456)]
|
||||
self.mox.StubOutWithMock(
|
||||
db, 'migration_get_in_progress_by_host_and_node')
|
||||
db.migration_get_in_progress_by_host_and_node(
|
||||
ctxt, 'host', 'node').AndReturn(db_migrations)
|
||||
self.mox.ReplayAll()
|
||||
migrations = (
|
||||
migration.MigrationList.get_in_progress_by_host_and_node(
|
||||
ctxt, 'host', 'node'))
|
||||
self.assertEqual(2, len(migrations))
|
||||
for index, db_migration in enumerate(db_migrations):
|
||||
self._compare(migrations[index], db_migration)
|
||||
|
||||
def test_get_by_filters(self):
|
||||
ctxt = context.get_admin_context()
|
||||
db_migrations = [fake_migration, dict(fake_migration, id=456)]
|
||||
self.mox.StubOutWithMock(
|
||||
db, 'migration_get_all_by_filters')
|
||||
filters = {'foo': 'bar'}
|
||||
db.migration_get_all_by_filters(ctxt, filters).AndReturn(db_migrations)
|
||||
self.mox.ReplayAll()
|
||||
migrations = migration.MigrationList.get_by_filters(ctxt, filters)
|
||||
self.assertEqual(2, len(migrations))
|
||||
for index, db_migration in enumerate(db_migrations):
|
||||
self._compare(migrations[index], db_migration)
|
||||
|
||||
|
||||
class TestMigrationObject(test_objects._LocalTest,
|
||||
_TestMigrationObject):
|
||||
pass
|
||||
|
||||
|
||||
class TestRemoteMigrationObject(test_objects._RemoteTest,
|
||||
_TestMigrationObject):
|
||||
pass
|
||||
Reference in New Issue
Block a user