@@ -4779,8 +4779,18 @@ def _finish_revert_snapshot_based_resize_at_source(
4779
4779
self.host, instance=instance)
4780
4780
# TODO(mriedem): Calculate provider mappings when we support
4781
4781
# cross-cell resize/migrate with ports having resource requests.
4782
- self._finish_revert_resize_network_migrate_finish(
4783
- ctxt, instance, migration, provider_mappings=None)
4782
+ # NOTE(hanrong): we need to change migration.dest_compute to
4783
+ # source host temporarily.
4784
+ # "network_api.migrate_instance_finish" will setup the network
4785
+ # for the instance on the destination host. For revert resize,
4786
+ # the instance will back to the source host, the setup of the
4787
+ # network for instance should be on the source host. So set
4788
+ # the migration.dest_compute to source host at here.
4789
+ with utils.temporary_mutation(
4790
+ migration, dest_compute=migration.source_compute
4791
+ ):
4792
+ self.network_api.migrate_instance_finish(
4793
+ ctxt, instance, migration, provider_mappings=None)
4784
4794
network_info = self.network_api.get_instance_nw_info(ctxt, instance)
4785
4795
4786
4796
# Remember that prep_snapshot_based_resize_at_source destroyed the
@@ -4872,50 +4882,6 @@ def revert_resize(self, context, instance, migration, request_spec):
4872
4882
self.compute_rpcapi.finish_revert_resize(context, instance,
4873
4883
migration, migration.source_compute, request_spec)
4874
4884
4875
- def _finish_revert_resize_network_migrate_finish(
4876
- self, context, instance, migration, provider_mappings):
4877
- """Causes port binding to be updated. In some Neutron or port
4878
- configurations - see NetworkModel.get_bind_time_events() - we
4879
- expect the vif-plugged event from Neutron immediately and wait for it.
4880
- The rest of the time, the event is expected further along in the
4881
- virt driver, so we don't wait here.
4882
-
4883
- :param context: The request context.
4884
- :param instance: The instance undergoing the revert resize.
4885
- :param migration: The Migration object of the resize being reverted.
4886
- :param provider_mappings: a dict of list of resource provider uuids
4887
- keyed by port uuid
4888
- :raises: eventlet.timeout.Timeout or
4889
- exception.VirtualInterfacePlugException.
4890
- """
4891
- network_info = instance.get_network_info()
4892
- events = []
4893
- deadline = CONF.vif_plugging_timeout
4894
- if deadline and network_info:
4895
- events = network_info.get_bind_time_events(migration)
4896
- if events:
4897
- LOG.debug('Will wait for bind-time events: %s', events)
4898
- error_cb = self._neutron_failed_migration_callback
4899
- try:
4900
- with self.virtapi.wait_for_instance_event(instance, events,
4901
- deadline=deadline,
4902
- error_callback=error_cb):
4903
- # NOTE(hanrong): we need to change migration.dest_compute to
4904
- # source host temporarily.
4905
- # "network_api.migrate_instance_finish" will setup the network
4906
- # for the instance on the destination host. For revert resize,
4907
- # the instance will back to the source host, the setup of the
4908
- # network for instance should be on the source host. So set
4909
- # the migration.dest_compute to source host at here.
4910
- with utils.temporary_mutation(
4911
- migration, dest_compute=migration.source_compute):
4912
- self.network_api.migrate_instance_finish(
4913
- context, instance, migration, provider_mappings)
4914
- except eventlet.timeout.Timeout:
4915
- with excutils.save_and_reraise_exception():
4916
- LOG.error('Timeout waiting for Neutron events: %s', events,
4917
- instance=instance)
4918
-
4919
4885
@wrap_exception()
4920
4886
@reverts_task_state
4921
4887
@wrap_instance_event(prefix='compute')
@@ -4973,8 +4939,18 @@ def _finish_revert_resize(
4973
4939
4974
4940
self.network_api.setup_networks_on_host(context, instance,
4975
4941
migration.source_compute)
4976
- self._finish_revert_resize_network_migrate_finish(
4977
- context, instance, migration, provider_mappings)
4942
+ # NOTE(hanrong): we need to change migration.dest_compute to
4943
+ # source host temporarily. "network_api.migrate_instance_finish"
4944
+ # will setup the network for the instance on the destination host.
4945
+ # For revert resize, the instance will back to the source host, the
4946
+ # setup of the network for instance should be on the source host.
4947
+ # So set the migration.dest_compute to source host at here.
4948
+ with utils.temporary_mutation(
4949
+ migration, dest_compute=migration.source_compute):
4950
+ self.network_api.migrate_instance_finish(context,
4951
+ instance,
4952
+ migration,
4953
+ provider_mappings)
4978
4954
network_info = self.network_api.get_instance_nw_info(context,
4979
4955
instance)
4980
4956
@@ -5051,8 +5027,7 @@ def _fill_provider_mapping_based_on_allocs(
5051
5027
# the provider mappings. If the instance has ports with
5052
5028
# resource request then the port update will fail in
5053
5029
# _update_port_binding_for_instance() called via
5054
- # _finish_revert_resize_network_migrate_finish() in
5055
- # finish_revert_resize.
5030
+ # migrate_instance_finish() in finish_revert_resize.
5056
5031
provider_mappings = None
5057
5032
return provider_mappings
5058
5033
@@ -8255,8 +8230,8 @@ def pre_live_migration(self, context, instance, disk, migrate_data):
8255
8230
return migrate_data
8256
8231
8257
8232
@staticmethod
8258
- def _neutron_failed_migration_callback (event_name, instance):
8259
- msg = ('Neutron reported failure during migration '
8233
+ def _neutron_failed_live_migration_callback (event_name, instance):
8234
+ msg = ('Neutron reported failure during live migration '
8260
8235
'with %(event)s for instance %(uuid)s')
8261
8236
msg_args = {'event': event_name, 'uuid': instance.uuid}
8262
8237
if CONF.vif_plugging_is_fatal:
@@ -8352,7 +8327,7 @@ class _BreakWaitForInstanceEvent(Exception):
8352
8327
disk = None
8353
8328
8354
8329
deadline = CONF.vif_plugging_timeout
8355
- error_cb = self._neutron_failed_migration_callback
8330
+ error_cb = self._neutron_failed_live_migration_callback
8356
8331
# In order to avoid a race with the vif plugging that the virt
8357
8332
# driver does on the destination host, we register our events
8358
8333
# to wait for before calling pre_live_migration. Then if the
@@ -8460,8 +8435,9 @@ def _do_live_migration(self, context, dest, instance, block_migration,
8460
8435
# host attachment. We fetch BDMs before that to retain connection_info
8461
8436
# and attachment_id relating to the source host for post migration
8462
8437
# cleanup.
8463
- post_live_migration = functools.partial(self._post_live_migration,
8464
- source_bdms=source_bdms)
8438
+ post_live_migration = functools.partial(
8439
+ self._post_live_migration_update_host, source_bdms=source_bdms
8440
+ )
8465
8441
rollback_live_migration = functools.partial(
8466
8442
self._rollback_live_migration, source_bdms=source_bdms)
8467
8443
@@ -8588,15 +8564,41 @@ def live_migration_abort(self, context, instance, migration_id):
8588
8564
migration, future = (
8589
8565
self._waiting_live_migrations.pop(instance.uuid))
8590
8566
if future and future.cancel():
8591
- # If we got here, we've successfully aborted the queued
8592
- # migration and _do_live_migration won't run so we need
8593
- # to set the migration status to cancelled and send the
8594
- # notification. If Future.cancel() fails, it means
8595
- # _do_live_migration is running and the migration status
8596
- # is preparing, and _do_live_migration() itself will attempt
8597
- # to pop the queued migration, hit a KeyError, and rollback,
8598
- # set the migration to cancelled and send the
8599
- # live.migration.abort.end notification.
8567
+ # If we got here, we've successfully dropped a queued
8568
+ # migration from the queue, so _do_live_migration won't run
8569
+ # and we only need to revert minor changes introduced by Nova
8570
+ # control plane (port bindings, resource allocations and
8571
+ # instance's PCI devices), restore VM's state, set the
8572
+ # migration's status to cancelled and send the notification.
8573
+ # If Future.cancel() fails, it means _do_live_migration is
8574
+ # running and the migration status is preparing, and
8575
+ # _do_live_migration() itself will attempt to pop the queued
8576
+ # migration, hit a KeyError, and rollback, set the migration
8577
+ # to cancelled and send the live.migration.abort.end
8578
+ # notification.
8579
+ self._revert_allocation(context, instance, migration)
8580
+ try:
8581
+ # This call will delete any inactive destination host
8582
+ # port bindings.
8583
+ self.network_api.setup_networks_on_host(
8584
+ context, instance, host=migration.dest_compute,
8585
+ teardown=True)
8586
+ except exception.PortBindingDeletionFailed as e:
8587
+ # Removing the inactive port bindings from the destination
8588
+ # host is not critical so just log an error but don't fail.
8589
+ LOG.error(
8590
+ 'Network cleanup failed for destination host %s '
8591
+ 'during live migration rollback. You may need to '
8592
+ 'manually clean up resources in the network service. '
8593
+ 'Error: %s', migration.dest_compute, str(e))
8594
+ except Exception:
8595
+ with excutils.save_and_reraise_exception():
8596
+ LOG.exception(
8597
+ 'An error occurred while cleaning up networking '
8598
+ 'during live migration rollback.',
8599
+ instance=instance)
8600
+ instance.task_state = None
8601
+ instance.save(expected_task_state=[task_states.MIGRATING])
8600
8602
self._set_migration_status(migration, 'cancelled')
8601
8603
except KeyError:
8602
8604
migration = objects.Migration.get_by_id(context, migration_id)
@@ -8707,6 +8709,42 @@ def _post_live_migration_remove_source_vol_connections(
8707
8709
bdm.attachment_id, self.host,
8708
8710
str(e), instance=instance)
8709
8711
8712
+ # TODO(sean-k-mooney): add typing
8713
+ def _post_live_migration_update_host(
8714
+ self, ctxt, instance, dest, block_migration=False,
8715
+ migrate_data=None, source_bdms=None
8716
+ ):
8717
+ try:
8718
+ self._post_live_migration(
8719
+ ctxt, instance, dest, block_migration, migrate_data,
8720
+ source_bdms)
8721
+ except Exception:
8722
+ # Restore the instance object
8723
+ node_name = None
8724
+ try:
8725
+ # get node name of compute, where instance will be
8726
+ # running after migration, that is destination host
8727
+ compute_node = self._get_compute_info(ctxt, dest)
8728
+ node_name = compute_node.hypervisor_hostname
8729
+ except exception.ComputeHostNotFound:
8730
+ LOG.exception('Failed to get compute_info for %s', dest)
8731
+
8732
+ # we can never rollback from post live migration and we can only
8733
+ # get here if the instance is running on the dest so we ensure
8734
+ # the instance.host is set correctly and reraise the original
8735
+ # exception unmodified.
8736
+ if instance.host != dest:
8737
+ # apply saves the new fields while drop actually removes the
8738
+ # migration context from the instance, so migration persists.
8739
+ instance.apply_migration_context()
8740
+ instance.drop_migration_context()
8741
+ instance.host = dest
8742
+ instance.task_state = None
8743
+ instance.node = node_name
8744
+ instance.progress = 0
8745
+ instance.save()
8746
+ raise
8747
+
8710
8748
@wrap_exception()
8711
8749
@wrap_instance_fault
8712
8750
def _post_live_migration(self, ctxt, instance, dest,
@@ -8718,7 +8756,7 @@ def _post_live_migration(self, ctxt, instance, dest,
8718
8756
and mainly updating database record.
8719
8757
8720
8758
:param ctxt: security context
8721
- :param instance: instance dict
8759
+ :param instance: instance object
8722
8760
:param dest: destination host
8723
8761
:param block_migration: if true, prepare for block migration
8724
8762
:param migrate_data: if not None, it is a dict which has data
0 commit comments