diff options
| author | Kei Masumoto <masumotok@nttdata.co.jp> | 2011-08-15 20:31:43 +0000 |
|---|---|---|
| committer | Tarmac <> | 2011-08-15 20:31:43 +0000 |
| commit | ea53d0f37a4f478ffbe18516f99ca26192117e80 (patch) | |
| tree | 3a56196e11ead6af9ead0eb13a345f6c7e63dcf8 /nova/compute | |
| parent | a18d8597c2fb52d77b8b827f2c440787b1165150 (diff) | |
| parent | 7393a114f421f1b54019099777fea34a09a80737 (diff) | |
| download | nova-ea53d0f37a4f478ffbe18516f99ca26192117e80.tar.gz nova-ea53d0f37a4f478ffbe18516f99ca26192117e80.tar.xz nova-ea53d0f37a4f478ffbe18516f99ca26192117e80.zip | |
Adding kvm-block-migration feature.
I wrote some description the below URL. I hope it may help for reviewing.
<http://etherpad.openstack.org/kvm-block-migration>
Diffstat (limited to 'nova/compute')
| -rw-r--r-- | nova/compute/manager.py | 126 |
1 files changed, 102 insertions, 24 deletions
diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 5b98e9ec1..16b8e14b4 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -1226,6 +1226,7 @@ class ComputeManager(manager.SchedulerDependentManager): @exception.wrap_exception(notifier=notifier, publisher_id=publisher_id()) def check_shared_storage_test_file(self, context, filename): """Confirms existence of the tmpfile under FLAGS.instances_path. + Cannot confirm tmpfile return False. :param context: security context :param filename: confirm existence of FLAGS.instances_path/thisfile @@ -1233,7 +1234,9 @@ class ComputeManager(manager.SchedulerDependentManager): """ tmp_file = os.path.join(FLAGS.instances_path, filename) if not os.path.exists(tmp_file): - raise exception.FileNotFound(file_path=tmp_file) + return False + else: + return True @exception.wrap_exception(notifier=notifier, publisher_id=publisher_id()) def cleanup_shared_storage_test_file(self, context, filename): @@ -1256,11 +1259,13 @@ class ComputeManager(manager.SchedulerDependentManager): """ return self.driver.update_available_resource(context, self.host) - def pre_live_migration(self, context, instance_id, time=None): + def pre_live_migration(self, context, instance_id, time=None, + block_migration=False, disk=None): """Preparations for live migration at dest host. :param context: security context :param instance_id: nova.db.sqlalchemy.models.Instance.Id + :param block_migration: if true, prepare for block migration """ if not time: @@ -1312,17 +1317,24 @@ class ComputeManager(manager.SchedulerDependentManager): # onto destination host. self.driver.ensure_filtering_rules_for_instance(instance_ref, network_info) - def live_migration(self, context, instance_id, dest): + # Preparation for block migration + if block_migration: + self.driver.pre_block_migration(context, + instance_ref, + disk) + + def live_migration(self, context, instance_id, + dest, block_migration=False): """Executing live migration. :param context: security context :param instance_id: nova.db.sqlalchemy.models.Instance.Id :param dest: destination host + :param block_migration: if true, do block migration """ # Get instance for error handling. instance_ref = self.db.instance_get(context, instance_id) - i_name = instance_ref.name try: # Checking volume node is working correctly when any volumes @@ -1333,16 +1345,25 @@ class ComputeManager(manager.SchedulerDependentManager): {"method": "check_for_export", "args": {'instance_id': instance_id}}) - # Asking dest host to preparing live migration. + if block_migration: + disk = self.driver.get_instance_disk_info(context, + instance_ref) + else: + disk = None + rpc.call(context, self.db.queue_get_for(context, FLAGS.compute_topic, dest), {"method": "pre_live_migration", - "args": {'instance_id': instance_id}}) + "args": {'instance_id': instance_id, + 'block_migration': block_migration, + 'disk': disk}}) except Exception: + i_name = instance_ref.name msg = _("Pre live migration for %(i_name)s failed at %(dest)s") LOG.error(msg % locals()) - self.recover_live_migration(context, instance_ref) + self.rollback_live_migration(context, instance_ref, + dest, block_migration) raise # Executing live migration @@ -1350,9 +1371,11 @@ class ComputeManager(manager.SchedulerDependentManager): # nothing must be recovered in this version. self.driver.live_migration(context, instance_ref, dest, self.post_live_migration, - self.recover_live_migration) + self.rollback_live_migration, + block_migration) - def post_live_migration(self, ctxt, instance_ref, dest): + def post_live_migration(self, ctxt, instance_ref, + dest, block_migration=False): """Post operations for live migration. This method is called from live_migration @@ -1361,6 +1384,7 @@ class ComputeManager(manager.SchedulerDependentManager): :param ctxt: security context :param instance_id: nova.db.sqlalchemy.models.Instance.Id :param dest: destination host + :param block_migration: if true, do block migration """ @@ -1403,8 +1427,29 @@ class ComputeManager(manager.SchedulerDependentManager): "%(i_name)s cannot inherit floating " "ip.\n%(e)s") % (locals())) - # Restore instance/volume state - self.recover_live_migration(ctxt, instance_ref, dest) + # Define domain at destination host, without doing it, + # pause/suspend/terminate do not work. + rpc.call(ctxt, + self.db.queue_get_for(ctxt, FLAGS.compute_topic, dest), + {"method": "post_live_migration_at_destination", + "args": {'instance_id': instance_ref.id, + 'block_migration': block_migration}}) + + # Restore instance state + self.db.instance_update(ctxt, + instance_ref['id'], + {'state_description': 'running', + 'state': power_state.RUNNING, + 'host': dest}) + # Restore volume state + for volume_ref in instance_ref['volumes']: + volume_id = volume_ref['id'] + self.db.volume_update(ctxt, volume_id, {'status': 'in-use'}) + + # No instance booting at source host, but instance dir + # must be deleted for preparing next block migration + if block_migration: + self.driver.destroy(instance_ref, network_info) LOG.info(_('Migrating %(i_name)s to %(dest)s finished successfully.') % locals()) @@ -1412,31 +1457,64 @@ class ComputeManager(manager.SchedulerDependentManager): "Domain not found: no domain with matching name.\" " "This error can be safely ignored.")) - def recover_live_migration(self, ctxt, instance_ref, host=None, dest=None): - """Recovers Instance/volume state from migrating -> running. + def post_live_migration_at_destination(self, context, + instance_id, block_migration=False): + """Post operations for live migration . - :param ctxt: security context + :param context: security context :param instance_id: nova.db.sqlalchemy.models.Instance.Id - :param host: DB column value is updated by this hostname. - If none, the host instance currently running is selected. + :param block_migration: block_migration """ - if not host: - host = instance_ref['host'] + instance_ref = self.db.instance_get(context, instance_id) + LOG.info(_('Post operation of migraton started for %s .') + % instance_ref.name) + network_info = self._get_instance_nw_info(context, instance_ref) + self.driver.post_live_migration_at_destination(context, + instance_ref, + network_info, + block_migration) - self.db.instance_update(ctxt, + def rollback_live_migration(self, context, instance_ref, + dest, block_migration): + """Recovers Instance/volume state from migrating -> running. + + :param context: security context + :param instance_id: nova.db.sqlalchemy.models.Instance.Id + :param dest: + This method is called from live migration src host. + This param specifies destination host. + """ + host = instance_ref['host'] + self.db.instance_update(context, instance_ref['id'], {'state_description': 'running', 'state': power_state.RUNNING, 'host': host}) - if dest: - volume_api = volume.API() for volume_ref in instance_ref['volumes']: volume_id = volume_ref['id'] - self.db.volume_update(ctxt, volume_id, {'status': 'in-use'}) - if dest: - volume_api.remove_from_compute(ctxt, volume_id, dest) + self.db.volume_update(context, volume_id, {'status': 'in-use'}) + volume.API().remove_from_compute(context, volume_id, dest) + + # Block migration needs empty image at destination host + # before migration starts, so if any failure occurs, + # any empty images has to be deleted. + if block_migration: + rpc.cast(context, + self.db.queue_get_for(context, FLAGS.compute_topic, dest), + {"method": "rollback_live_migration_at_destination", + "args": {'instance_id': instance_ref['id']}}) + + def rollback_live_migration_at_destination(self, context, instance_id): + """ Cleaning up image directory that is created pre_live_migration. + + :param context: security context + :param instance_id: nova.db.sqlalchemy.models.Instance.Id + """ + instances_ref = self.db.instance_get(context, instance_id) + network_info = self._get_instance_nw_info(context, instances_ref) + self.driver.destroy(instances_ref, network_info) def periodic_tasks(self, context=None): """Tasks to be run at a periodic interval.""" |
