280
301
self.driver.init_host(host=self.host)
281
302
context = nova.context.get_admin_context()
282
303
instances = self.db.instance_get_all_by_host(context, self.host)
283
for instance in instances:
284
db_state = instance['power_state']
285
drv_state = self._get_power_state(context, instance)
287
expect_running = (db_state == power_state.RUNNING and
288
drv_state != db_state)
290
LOG.debug(_('Current state is %(drv_state)s, state in DB is '
291
'%(db_state)s.'), locals(), instance=instance)
293
net_info = compute_utils.get_nw_info_for_instance(instance)
294
if ((expect_running and FLAGS.resume_guests_state_on_host_boot) or
295
FLAGS.start_guests_on_host_boot):
296
LOG.info(_('Rebooting instance after nova-compute restart.'),
297
locals(), instance=instance)
299
self.driver.resume_state_on_host_boot(context, instance,
300
self._legacy_nw_info(net_info))
301
except NotImplementedError:
302
LOG.warning(_('Hypervisor driver does not support '
303
'resume guests'), instance=instance)
305
elif drv_state == power_state.RUNNING:
306
# VMWareAPI drivers will raise an exception
308
self.driver.ensure_filtering_rules_for_instance(instance,
309
self._legacy_nw_info(net_info))
310
except NotImplementedError:
311
LOG.warning(_('Hypervisor driver does not support '
312
'firewall rules'), instance=instance)
305
if FLAGS.defer_iptables_apply:
306
self.driver.filter_defer_apply_on()
309
for count, instance in enumerate(instances):
310
db_state = instance['power_state']
311
drv_state = self._get_power_state(context, instance)
313
expect_running = (db_state == power_state.RUNNING and
314
drv_state != db_state)
316
LOG.debug(_('Current state is %(drv_state)s, state in DB is '
317
'%(db_state)s.'), locals(), instance=instance)
319
net_info = compute_utils.get_nw_info_for_instance(instance)
321
# We're calling plug_vifs to ensure bridge and iptables
322
# filters are present, calling it once is enough.
324
legacy_net_info = self._legacy_nw_info(net_info)
325
self.driver.plug_vifs(instance, legacy_net_info)
327
if ((expect_running and FLAGS.resume_guests_state_on_host_boot)
328
or FLAGS.start_guests_on_host_boot):
330
_('Rebooting instance after nova-compute restart.'),
331
locals(), instance=instance)
333
self.driver.resume_state_on_host_boot(context,
335
self._legacy_nw_info(net_info))
336
except NotImplementedError:
337
LOG.warning(_('Hypervisor driver does not support '
338
'resume guests'), instance=instance)
340
elif drv_state == power_state.RUNNING:
341
# VMWareAPI drivers will raise an exception
343
self.driver.ensure_filtering_rules_for_instance(
345
self._legacy_nw_info(net_info))
346
except NotImplementedError:
347
LOG.warning(_('Hypervisor driver does not support '
348
'firewall rules'), instance=instance)
351
if FLAGS.defer_iptables_apply:
352
self.driver.filter_defer_apply_off()
314
354
def _get_power_state(self, context, instance):
315
355
"""Retrieve the power state for the given instance."""
463
520
instance = self._spawn(context, instance, image_meta,
464
521
network_info, block_device_info,
465
522
injected_files, admin_password)
523
except exception.InstanceNotFound:
524
raise # the instance got deleted during the spawn
466
525
except Exception:
467
with excutils.save_and_reraise_exception():
468
self._deallocate_network(context, instance)
470
if (is_first_time and not instance['access_ip_v4']
471
and not instance['access_ip_v6']):
472
self._update_access_ip(context, instance, network_info)
474
self._notify_about_instance_usage(
475
context, instance, "create.end", network_info=network_info)
476
except exception.InstanceNotFound:
477
LOG.warn(_("Instance not found."), instance_uuid=instance_uuid)
478
except Exception as e:
526
# try to re-schedule instance:
527
self._reschedule_or_reraise(context, instance,
528
requested_networks, admin_password, injected_files,
529
is_first_time, request_spec, filter_properties)
532
if (is_first_time and not instance['access_ip_v4']
533
and not instance['access_ip_v6']):
534
self._update_access_ip(context, instance, network_info)
536
self._notify_about_instance_usage(context, instance,
537
"create.end", network_info=network_info,
538
extra_usage_info=extra_usage_info)
479
540
with excutils.save_and_reraise_exception():
480
self._set_instance_error_state(context, instance_uuid)
541
self._set_instance_error_state(context, instance['uuid'])
543
def _reschedule_or_reraise(self, context, instance, requested_networks,
544
admin_password, injected_files, is_first_time,
545
request_spec, filter_properties):
546
"""Try to re-schedule the build or re-raise the original build error to
547
error out the instance.
549
type_, value, tb = sys.exc_info() # save original exception
551
instance_uuid = instance['uuid']
553
def _log_original_error():
554
LOG.error(_('Build error: %s') %
555
traceback.format_exception(type_, value, tb),
556
instance_uuid=instance_uuid)
559
self._deallocate_network(context, instance)
561
# do not attempt retry if network de-allocation occurs:
562
_log_original_error()
566
rescheduled = self._reschedule(context, instance_uuid,
567
requested_networks, admin_password, injected_files,
568
is_first_time, request_spec, filter_properties)
571
LOG.exception(_("Error trying to reschedule"),
572
instance_uuid=instance_uuid)
575
# log the original build error
576
_log_original_error()
579
raise type_, value, tb
581
def _reschedule(self, context, instance_uuid, requested_networks,
582
admin_password, injected_files, is_first_time, request_spec,
585
retry = filter_properties.get('retry', None)
587
# no retry information, do not reschedule.
588
LOG.debug(_("Retry info not present, will not reschedule"),
589
instance_uuid=instance_uuid)
593
LOG.debug(_("No request spec, will not reschedule"),
594
instance_uuid=instance_uuid)
597
request_spec['num_instances'] = 1
599
LOG.debug(_("Re-scheduling instance: attempt %d"),
600
retry['num_attempts'], instance_uuid=instance_uuid)
601
self.scheduler_rpcapi.run_instance(context,
602
request_spec, admin_password, injected_files,
603
requested_networks, is_first_time, filter_properties,
604
reservations=None, call=False)
482
607
@manager.periodic_task
483
608
def _check_instance_build_time(self, context):
763
913
terminated_at=timeutils.utcnow())
764
914
self.db.instance_destroy(context, instance_uuid)
765
with utils.temporary_mutation(context, read_deleted="yes"):
766
system_meta = self.db.instance_system_metadata_get(context,
915
system_meta = self.db.instance_system_metadata_get(context,
768
917
self._notify_about_instance_usage(context, instance, "delete.end",
769
918
system_metadata=system_meta)
771
920
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
772
922
@checks_instance_lock
773
923
@wrap_instance_fault
774
def terminate_instance(self, context, instance_uuid):
924
def terminate_instance(self, context, instance=None, instance_uuid=None):
775
925
"""Terminate an instance on this host."""
776
@utils.synchronized(instance_uuid)
777
def do_terminate_instance():
778
elevated = context.elevated()
779
instance = self.db.instance_get_by_uuid(elevated, instance_uuid)
927
elevated = context.elevated()
929
instance = self.db.instance_get_by_uuid(elevated,
932
@utils.synchronized(instance['uuid'])
933
def do_terminate_instance(instance):
781
935
self._delete_instance(context, instance)
782
936
except exception.InstanceTerminationFailure as error:
783
937
msg = _('%s. Setting instance vm_state to ERROR')
784
LOG.error(msg % error, instance_uuid=instance_uuid)
785
self._set_instance_error_state(context, instance_uuid)
938
LOG.error(msg % error, instance=instance)
939
self._set_instance_error_state(context, instance['uuid'])
786
940
except exception.InstanceNotFound as e:
787
LOG.warn(e, instance_uuid=instance_uuid)
788
do_terminate_instance()
941
LOG.warn(e, instance=instance)
943
do_terminate_instance(instance)
790
945
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
791
947
@checks_instance_lock
792
948
@wrap_instance_fault
793
def stop_instance(self, context, instance_uuid):
949
def stop_instance(self, context, instance=None, instance_uuid=None):
794
950
"""Stopping an instance on this host.
796
952
Alias for power_off_instance for compatibility"""
797
self.power_off_instance(context, instance_uuid,
953
self.power_off_instance(context, instance=instance,
954
instance_uuid=instance_uuid,
798
955
final_state=vm_states.STOPPED)
800
957
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
801
959
@checks_instance_lock
802
960
@wrap_instance_fault
803
def start_instance(self, context, instance_uuid):
961
def start_instance(self, context, instance=None, instance_uuid=None):
804
962
"""Starting an instance on this host.
806
964
Alias for power_on_instance for compatibility"""
807
self.power_on_instance(context, instance_uuid)
965
self.power_on_instance(context, instance=instance,
966
instance_uuid=instance_uuid)
809
968
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
810
970
@checks_instance_lock
811
971
@wrap_instance_fault
812
def power_off_instance(self, context, instance_uuid,
972
def power_off_instance(self, context, instance=None, instance_uuid=None,
813
973
final_state=vm_states.SOFT_DELETED):
814
974
"""Power off an instance on this host."""
815
instance = self.db.instance_get_by_uuid(context, instance_uuid)
976
instance = self.db.instance_get_by_uuid(context, instance_uuid)
816
977
self._notify_about_instance_usage(context, instance, "power_off.start")
817
978
self.driver.power_off(instance)
818
979
current_power_state = self._get_power_state(context, instance)
819
980
self._instance_update(context,
821
982
power_state=current_power_state,
822
983
vm_state=final_state,
824
985
self._notify_about_instance_usage(context, instance, "power_off.end")
826
987
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
827
989
@checks_instance_lock
828
990
@wrap_instance_fault
829
def power_on_instance(self, context, instance_uuid):
991
def power_on_instance(self, context, instance=None, instance_uuid=None):
830
992
"""Power on an instance on this host."""
831
instance = self.db.instance_get_by_uuid(context, instance_uuid)
994
instance = self.db.instance_get_by_uuid(context, instance_uuid)
832
995
self._notify_about_instance_usage(context, instance, "power_on.start")
833
996
self.driver.power_on(instance)
834
997
current_power_state = self._get_power_state(context, instance)
835
998
self._instance_update(context,
837
1000
power_state=current_power_state,
838
1001
vm_state=vm_states.ACTIVE,
839
1002
task_state=None)
840
1003
self._notify_about_instance_usage(context, instance, "power_on.end")
842
1005
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
843
1007
@checks_instance_lock
844
1008
@wrap_instance_fault
845
def rebuild_instance(self, context, instance_uuid, orig_image_ref,
846
image_ref, **kwargs):
1009
def rebuild_instance(self, context, orig_image_ref,
1010
image_ref, instance=None, instance_uuid=None, **kwargs):
847
1011
"""Destroy and re-make this instance.
849
1013
A 'rebuild' effectively purges all existing data from the system and
850
1014
remakes the VM with given 'metadata' and 'personalities'.
852
1016
:param context: `nova.RequestContext` object
853
:param instance_uuid: Instance Identifier (UUID)
1017
:param instance_uuid: (Deprecated) Instance Identifier (UUID)
1018
:param instance: Instance dict
854
1019
:param orig_image_ref: Original image_ref before rebuild
855
1020
:param image_ref: New image_ref for rebuild
856
1021
:param injected_files: Files to inject
857
1022
:param new_pass: password to set on rebuilt instance
860
self._rebuild_instance(context, instance_uuid, orig_image_ref,
862
except exception.ImageNotFound:
863
LOG.error(_('Cannot rebuild instance because the given image does '
865
context=context, instance_uuid=instance_uuid)
866
self._set_instance_error_state(context, instance_uuid)
867
except Exception as exc:
868
LOG.error(_('Cannot rebuild instance: %(exc)s'), locals(),
869
context=context, instance_uuid=instance_uuid)
870
self._set_instance_error_state(context, instance_uuid)
872
def _rebuild_instance(self, context, instance_uuid, orig_image_ref,
874
1024
context = context.elevated()
876
LOG.audit(_("Rebuilding instance"), context=context,
877
instance_uuid=instance_uuid)
879
instance = self.db.instance_get_by_uuid(context, instance_uuid)
881
# This instance.exists message should contain the original
882
# image_ref, not the new one. Since the DB has been updated
883
# to point to the new one... we have to override it.
884
orig_image_ref_url = utils.generate_image_url(orig_image_ref)
885
extra_usage_info = {'image_ref_url': orig_image_ref_url}
886
compute_utils.notify_usage_exists(context, instance,
887
current_period=True, extra_usage_info=extra_usage_info)
889
# This message should contain the new image_ref
890
self._notify_about_instance_usage(context, instance,
893
current_power_state = self._get_power_state(context, instance)
894
self._instance_update(context,
896
power_state=current_power_state,
897
task_state=task_states.REBUILDING)
899
network_info = self._get_instance_nw_info(context, instance)
900
self.driver.destroy(instance, self._legacy_nw_info(network_info))
902
instance = self._instance_update(context,
904
task_state=task_states.\
905
REBUILD_BLOCK_DEVICE_MAPPING)
907
instance.injected_files = kwargs.get('injected_files', [])
908
network_info = self.network_api.get_instance_nw_info(context,
910
device_info = self._setup_block_device_mapping(context, instance)
912
instance = self._instance_update(context,
914
task_state=task_states.\
916
# pull in new password here since the original password isn't in the db
917
instance.admin_pass = kwargs.get('new_pass',
918
utils.generate_password(FLAGS.password_length))
920
image_meta = _get_image_meta(context, image_ref)
922
self.driver.spawn(context, instance, image_meta,
923
self._legacy_nw_info(network_info), device_info)
925
current_power_state = self._get_power_state(context, instance)
926
instance = self._instance_update(context,
928
power_state=current_power_state,
929
vm_state=vm_states.ACTIVE,
931
launched_at=timeutils.utcnow())
933
self._notify_about_instance_usage(context, instance, "rebuild.end",
934
network_info=network_info)
1027
instance = self.db.instance_get_by_uuid(context, instance_uuid)
1029
with self._error_out_instance_on_exception(context, instance['uuid']):
1030
LOG.audit(_("Rebuilding instance"), context=context,
1033
image_meta = _get_image_meta(context, image_ref)
1035
# This instance.exists message should contain the original
1036
# image_ref, not the new one. Since the DB has been updated
1037
# to point to the new one... we have to override it.
1038
orig_image_ref_url = utils.generate_image_url(orig_image_ref)
1039
extra_usage_info = {'image_ref_url': orig_image_ref_url}
1040
compute_utils.notify_usage_exists(context, instance,
1041
current_period=True, extra_usage_info=extra_usage_info)
1043
# This message should contain the new image_ref
1044
extra_usage_info = {'image_name': image_meta['name']}
1045
self._notify_about_instance_usage(context, instance,
1046
"rebuild.start", extra_usage_info=extra_usage_info)
1048
current_power_state = self._get_power_state(context, instance)
1049
self._instance_update(context,
1051
power_state=current_power_state,
1052
task_state=task_states.REBUILDING)
1054
network_info = self._get_instance_nw_info(context, instance)
1055
self.driver.destroy(instance, self._legacy_nw_info(network_info))
1057
instance = self._instance_update(context,
1059
task_state=task_states.\
1060
REBUILD_BLOCK_DEVICE_MAPPING)
1062
instance.injected_files = kwargs.get('injected_files', [])
1063
network_info = self.network_api.get_instance_nw_info(context,
1065
device_info = self._setup_block_device_mapping(context, instance)
1067
instance = self._instance_update(context,
1069
task_state=task_states.\
1071
# pull in new password here since the original password isn't in
1073
admin_password = kwargs.get('new_pass',
1074
utils.generate_password(FLAGS.password_length))
1076
self.driver.spawn(context, instance, image_meta,
1078
self._legacy_nw_info(network_info),
1081
current_power_state = self._get_power_state(context, instance)
1082
instance = self._instance_update(context,
1084
power_state=current_power_state,
1085
vm_state=vm_states.ACTIVE,
1087
launched_at=timeutils.utcnow())
1089
self._notify_about_instance_usage(
1090
context, instance, "rebuild.end",
1091
network_info=network_info,
1092
extra_usage_info=extra_usage_info)
936
1094
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
937
1096
@checks_instance_lock
938
1097
@wrap_instance_fault
939
def reboot_instance(self, context, instance_uuid, reboot_type="SOFT"):
1098
def reboot_instance(self, context, instance=None, instance_uuid=None,
1099
reboot_type="SOFT"):
940
1100
"""Reboot an instance on this host."""
941
LOG.audit(_("Rebooting instance"), context=context,
942
instance_uuid=instance_uuid)
943
1101
context = context.elevated()
944
instance = self.db.instance_get_by_uuid(context, instance_uuid)
1103
instance = self.db.instance_get_by_uuid(context, instance_uuid)
1104
LOG.audit(_("Rebooting instance"), context=context, instance=instance)
946
1106
self._notify_about_instance_usage(context, instance, "reboot.start")
948
1108
current_power_state = self._get_power_state(context, instance)
949
self._instance_update(context,
1109
self._instance_update(context, instance['uuid'],
951
1110
power_state=current_power_state,
952
1111
vm_state=vm_states.ACTIVE)
988
1155
None if rotation shouldn't be used (as in the case of snapshots)
990
1157
context = context.elevated()
991
instance_ref = self.db.instance_get_by_uuid(context, instance_uuid)
993
current_power_state = self._get_power_state(context, instance_ref)
1160
instance = self.db.instance_get_by_uuid(context, instance_uuid)
1162
current_power_state = self._get_power_state(context, instance)
994
1163
self._instance_update(context,
995
instance_ref['uuid'],
996
power_state=current_power_state,
997
vm_state=vm_states.ACTIVE)
999
LOG.audit(_('instance %s: snapshotting'), context=context,
1000
instance_uuid=instance_uuid)
1002
if instance_ref['power_state'] != power_state.RUNNING:
1003
state = instance_ref['power_state']
1165
power_state=current_power_state)
1167
LOG.audit(_('instance snapshotting'), context=context,
1170
if instance['power_state'] != power_state.RUNNING:
1171
state = instance['power_state']
1004
1172
running = power_state.RUNNING
1005
1173
LOG.warn(_('trying to snapshot a non-running '
1006
1174
'instance: (state: %(state)s '
1007
1175
'expected: %(running)s)') % locals(),
1008
instance_uuid=instance_uuid)
1010
1178
self._notify_about_instance_usage(
1011
context, instance_ref, "snapshot.start")
1179
context, instance, "snapshot.start")
1014
self.driver.snapshot(context, instance_ref, image_id)
1016
self._instance_update(context, instance_ref['uuid'],
1181
self.driver.snapshot(context, instance, image_id)
1182
self._instance_update(context, instance['uuid'], task_state=None)
1019
1184
if image_type == 'snapshot' and rotation:
1020
1185
raise exception.ImageRotationNotAllowed()
1022
1187
elif image_type == 'backup' and rotation:
1023
self.rotate_backups(context, instance_uuid, backup_type, rotation)
1188
self._rotate_backups(context, instance, backup_type, rotation)
1025
1190
elif image_type == 'backup':
1026
1191
raise exception.RotationRequiredForBackup()
1028
1193
self._notify_about_instance_usage(
1029
context, instance_ref, "snapshot.end")
1194
context, instance, "snapshot.end")
1031
1196
@wrap_instance_fault
1032
def rotate_backups(self, context, instance_uuid, backup_type, rotation):
1197
def _rotate_backups(self, context, instance, backup_type, rotation):
1033
1198
"""Delete excess backups associated to an instance.
1035
1200
Instances are allowed a fixed number of backups (the rotation number);
1093
1260
# Generate a random password
1094
1261
new_pass = utils.generate_password(FLAGS.password_length)
1264
instance = self.db.instance_get_by_uuid(context, instance_uuid)
1098
1268
for i in xrange(max_tries):
1099
instance_ref = self.db.instance_get_by_uuid(context, instance_uuid)
1101
current_power_state = self._get_power_state(context, instance_ref)
1269
current_power_state = self._get_power_state(context, instance)
1102
1270
expected_state = power_state.RUNNING
1104
1272
if current_power_state != expected_state:
1105
self._instance_update(context, instance_ref['uuid'],
1273
self._instance_update(context, instance['uuid'],
1106
1274
task_state=None)
1107
1275
_msg = _('Failed to set admin password. Instance %s is not'
1108
' running') % instance_ref["uuid"]
1109
raise exception.Invalid(_msg)
1276
' running') % instance["uuid"]
1277
raise exception.InstancePasswordSetFailed(
1278
instance=instance['uuid'], reason=_msg)
1112
self.driver.set_admin_password(instance_ref, new_pass)
1113
LOG.audit(_("Root password set"), instance=instance_ref)
1281
self.driver.set_admin_password(instance, new_pass)
1282
LOG.audit(_("Root password set"), instance=instance)
1114
1283
self._instance_update(context,
1115
instance_ref['uuid'],
1116
1285
task_state=None)
1118
1287
except NotImplementedError:
1119
1288
# NOTE(dprince): if the driver doesn't implement
1120
1289
# set_admin_password we break to avoid a loop
1121
LOG.warn(_('set_admin_password is not implemented '
1122
'by this driver.'), instance=instance_ref)
1290
_msg = _('set_admin_password is not implemented '
1292
LOG.warn(_msg, instance=instance)
1123
1293
self._instance_update(context,
1124
instance_ref['uuid'],
1125
1295
task_state=None)
1296
raise exception.InstancePasswordSetFailed(
1297
instance=instance['uuid'], reason=_msg)
1127
1298
except Exception, e:
1128
1299
# Catch all here because this could be anything.
1129
LOG.exception(e, instance=instance_ref)
1300
LOG.exception(_('set_admin_password failed: %s') % e,
1130
1302
if i == max_tries - 1:
1131
1303
self._set_instance_error_state(context,
1132
instance_ref['uuid'])
1133
1305
# We create a new exception here so that we won't
1134
1306
# potentially reveal password information to the
1135
1307
# API caller. The real exception is logged above
1136
_msg = _('Error setting admin password')
1137
raise exception.NovaException(_msg)
1308
_msg = _('error setting admin password')
1309
raise exception.InstancePasswordSetFailed(
1310
instance=instance['uuid'], reason=_msg)
1141
1314
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
1142
1316
@checks_instance_lock
1143
1317
@wrap_instance_fault
1144
def inject_file(self, context, instance_uuid, path, file_contents):
1318
def inject_file(self, context, path, file_contents, instance_uuid=None,
1145
1320
"""Write a file to the specified path in an instance on this host."""
1146
1321
context = context.elevated()
1147
instance_ref = self.db.instance_get_by_uuid(context, instance_uuid)
1148
current_power_state = self._get_power_state(context, instance_ref)
1323
instance = self.db.instance_get_by_uuid(context, instance_uuid)
1324
current_power_state = self._get_power_state(context, instance)
1149
1325
expected_state = power_state.RUNNING
1150
1326
if current_power_state != expected_state:
1151
1327
LOG.warn(_('trying to inject a file into a non-running '
1152
1328
'(state: %(current_power_state)s '
1153
1329
'expected: %(expected_state)s)') % locals(),
1154
instance=instance_ref)
1155
1331
LOG.audit(_('injecting file to %(path)s') % locals(),
1156
instance=instance_ref)
1157
self.driver.inject_file(instance_ref, path, file_contents)
1159
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
1160
@checks_instance_lock
1161
@wrap_instance_fault
1162
def agent_update(self, context, instance_uuid, url, md5hash):
1163
"""Update agent running on an instance on this host."""
1164
context = context.elevated()
1165
instance_ref = self.db.instance_get_by_uuid(context, instance_uuid)
1166
current_power_state = self._get_power_state(context, instance_ref)
1167
expected_state = power_state.RUNNING
1168
if current_power_state != expected_state:
1169
LOG.warn(_('trying to update agent on a non-running '
1170
'(state: %(current_power_state)s '
1171
'expected: %(expected_state)s)') % locals(),
1172
instance=instance_ref)
1173
LOG.audit(_('updating agent to %(url)s') % locals(),
1174
instance=instance_ref)
1175
self.driver.agent_update(instance_ref, url, md5hash)
1177
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
1178
@checks_instance_lock
1179
@wrap_instance_fault
1180
def rescue_instance(self, context, instance_uuid, **kwargs):
1333
self.driver.inject_file(instance, path, file_contents)
1335
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
1337
@checks_instance_lock
1338
@wrap_instance_fault
1339
def rescue_instance(self, context, instance=None, instance_uuid=None,
1340
rescue_password=None):
1182
1342
Rescue an instance on this host.
1183
1343
:param rescue_password: password to set on rescue instance
1186
LOG.audit(_('Rescuing'), context=context, instance_uuid=instance_uuid)
1187
1345
context = context.elevated()
1189
instance_ref = self.db.instance_get_by_uuid(context, instance_uuid)
1190
instance_ref.admin_pass = kwargs.get('rescue_password',
1191
utils.generate_password(FLAGS.password_length))
1192
network_info = self._get_instance_nw_info(context, instance_ref)
1193
image_meta = _get_image_meta(context, instance_ref['image_ref'])
1195
with self.error_out_instance_on_exception(context, instance_uuid):
1196
self.driver.rescue(context, instance_ref,
1197
self._legacy_nw_info(network_info), image_meta)
1199
current_power_state = self._get_power_state(context, instance_ref)
1347
instance = self.db.instance_get_by_uuid(context, instance_uuid)
1349
LOG.audit(_('Rescuing'), context=context, instance=instance)
1351
admin_password = (rescue_password if rescue_password else
1352
utils.generate_password(FLAGS.password_length))
1354
network_info = self._get_instance_nw_info(context, instance)
1355
image_meta = _get_image_meta(context, instance['image_ref'])
1357
with self._error_out_instance_on_exception(context, instance['uuid']):
1358
self.driver.rescue(context, instance,
1359
self._legacy_nw_info(network_info), image_meta,
1362
current_power_state = self._get_power_state(context, instance)
1200
1363
self._instance_update(context,
1202
1365
vm_state=vm_states.RESCUED,
1203
1366
task_state=None,
1204
1367
power_state=current_power_state)
1206
1369
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
1207
1371
@checks_instance_lock
1208
1372
@wrap_instance_fault
1209
def unrescue_instance(self, context, instance_uuid):
1373
def unrescue_instance(self, context, instance=None, instance_uuid=None):
1210
1374
"""Rescue an instance on this host."""
1211
LOG.audit(_('Unrescuing'), context=context,
1212
instance_uuid=instance_uuid)
1213
1375
context = context.elevated()
1215
instance_ref = self.db.instance_get_by_uuid(context, instance_uuid)
1216
network_info = self._get_instance_nw_info(context, instance_ref)
1218
with self.error_out_instance_on_exception(context, instance_uuid):
1219
self.driver.unrescue(instance_ref,
1377
instance = self.db.instance_get_by_uuid(context, instance_uuid)
1379
LOG.audit(_('Unrescuing'), context=context, instance=instance)
1381
network_info = self._get_instance_nw_info(context, instance)
1383
with self._error_out_instance_on_exception(context, instance['uuid']):
1384
self.driver.unrescue(instance,
1220
1385
self._legacy_nw_info(network_info))
1222
current_power_state = self._get_power_state(context, instance_ref)
1387
current_power_state = self._get_power_state(context, instance)
1223
1388
self._instance_update(context,
1225
1390
vm_state=vm_states.ACTIVE,
1226
1391
task_state=None,
1227
1392
power_state=current_power_state)
1229
1394
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
1230
@checks_instance_lock
1231
@wrap_instance_fault
1232
def confirm_resize(self, context, instance_uuid, migration_id):
1396
@checks_instance_lock
1397
@wrap_instance_fault
1398
def change_instance_metadata(self, context, diff, instance=None,
1399
instance_uuid=None):
1400
"""Update the metadata published to the instance."""
1402
instance = self.db.instance_get_by_uuid(context, instance_uuid)
1403
LOG.debug(_("Changing instance metadata according to %(diff)r") %
1404
locals(), instance=instance)
1405
self.driver.change_instance_metadata(context, instance, diff)
1407
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
1408
@checks_instance_lock
1409
@wrap_instance_fault
1410
def confirm_resize(self, context, migration_id, instance_uuid=None,
1411
instance=None, reservations=None):
1233
1412
"""Destroys the source instance."""
1234
1413
migration_ref = self.db.migration_get(context, migration_id)
1235
instance_ref = self.db.instance_get_by_uuid(context,
1236
migration_ref.instance_uuid)
1415
instance = self.db.instance_get_by_uuid(context,
1416
migration_ref.instance_uuid)
1238
self._notify_about_instance_usage(context, instance_ref,
1418
self._notify_about_instance_usage(context, instance,
1239
1419
"resize.confirm.start")
1241
# NOTE(tr3buchet): tear down networks on source host
1242
self.network_api.setup_networks_on_host(context, instance_ref,
1243
migration_ref['source_compute'], teardown=True)
1245
network_info = self._get_instance_nw_info(context, instance_ref)
1246
self.driver.confirm_migration(migration_ref, instance_ref,
1247
self._legacy_nw_info(network_info))
1249
self._notify_about_instance_usage(
1250
context, instance_ref, "resize.confirm.end",
1251
network_info=network_info)
1421
with self._error_out_instance_on_exception(context, instance['uuid'],
1423
# NOTE(tr3buchet): tear down networks on source host
1424
self.network_api.setup_networks_on_host(context, instance,
1425
migration_ref['source_compute'], teardown=True)
1427
network_info = self._get_instance_nw_info(context, instance)
1428
self.driver.confirm_migration(migration_ref, instance,
1429
self._legacy_nw_info(network_info))
1431
self._notify_about_instance_usage(
1432
context, instance, "resize.confirm.end",
1433
network_info=network_info)
1435
self._quota_commit(context, reservations)
1253
1437
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
1254
1439
@checks_instance_lock
1255
1440
@wrap_instance_fault
1256
def revert_resize(self, context, instance_uuid, migration_id):
1441
def revert_resize(self, context, migration_id, instance=None,
1442
instance_uuid=None, reservations=None):
1257
1443
"""Destroys the new instance on the destination machine.
1259
1445
Reverts the model changes, and powers on the old instance on the
1286
1478
migration_ref = self.db.migration_get(context, migration_id)
1287
instance_ref = self.db.instance_get_by_uuid(context,
1288
migration_ref.instance_uuid)
1289
network_info = self._get_instance_nw_info(context, instance_ref)
1291
self._notify_about_instance_usage(
1292
context, instance_ref, "resize.revert.start")
1294
old_instance_type = migration_ref['old_instance_type_id']
1295
instance_type = instance_types.get_instance_type(old_instance_type)
1297
self.driver.finish_revert_migration(instance_ref,
1298
self._legacy_nw_info(network_info))
1300
# Just roll back the record. There's no need to resize down since
1301
# the 'old' VM already has the preferred attributes
1302
self._instance_update(context,
1303
instance_ref['uuid'],
1304
memory_mb=instance_type['memory_mb'],
1305
host=migration_ref['source_compute'],
1306
vcpus=instance_type['vcpus'],
1307
root_gb=instance_type['root_gb'],
1308
ephemeral_gb=instance_type['ephemeral_gb'],
1309
instance_type_id=instance_type['id'],
1310
launched_at=timeutils.utcnow(),
1311
vm_state=vm_states.ACTIVE,
1314
self.db.migration_update(context, migration_id,
1315
{'status': 'reverted'})
1317
self._notify_about_instance_usage(
1318
context, instance_ref, "resize.revert.end")
1480
instance = self.db.instance_get_by_uuid(context,
1481
migration_ref.instance_uuid)
1483
with self._error_out_instance_on_exception(context, instance['uuid'],
1485
network_info = self._get_instance_nw_info(context, instance)
1487
self._notify_about_instance_usage(
1488
context, instance, "resize.revert.start")
1490
old_instance_type = migration_ref['old_instance_type_id']
1491
instance_type = instance_types.get_instance_type(old_instance_type)
1493
self.driver.finish_revert_migration(instance,
1494
self._legacy_nw_info(network_info))
1496
# Just roll back the record. There's no need to resize down since
1497
# the 'old' VM already has the preferred attributes
1498
self._instance_update(context,
1500
memory_mb=instance_type['memory_mb'],
1501
host=migration_ref['source_compute'],
1502
vcpus=instance_type['vcpus'],
1503
root_gb=instance_type['root_gb'],
1504
ephemeral_gb=instance_type['ephemeral_gb'],
1505
instance_type_id=instance_type['id'],
1506
launched_at=timeutils.utcnow(),
1507
vm_state=vm_states.ACTIVE,
1510
self.db.migration_update(context, migration_id,
1511
{'status': 'reverted'})
1513
self._notify_about_instance_usage(
1514
context, instance, "resize.revert.end")
1516
self._quota_commit(context, reservations)
1519
def _quota_commit(context, reservations):
1521
QUOTAS.commit(context, reservations)
1524
def _quota_rollback(context, reservations):
1526
QUOTAS.rollback(context, reservations)
1320
1528
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
1321
1530
@checks_instance_lock
1322
1531
@wrap_instance_fault
1323
def prep_resize(self, context, instance_uuid, instance_type_id, image,
1532
def prep_resize(self, context, image, instance=None, instance_uuid=None,
1533
instance_type=None, instance_type_id=None,
1325
1535
"""Initiates the process of moving a running instance to another host.
1327
1537
Possibly changes the RAM and disk size in the process.
1330
1540
context = context.elevated()
1332
instance_ref = self.db.instance_get_by_uuid(context, instance_uuid)
1334
compute_utils.notify_usage_exists(
1335
context, instance_ref, current_period=True)
1336
self._notify_about_instance_usage(
1337
context, instance_ref, "resize.prep.start")
1339
same_host = instance_ref['host'] == FLAGS.host
1340
if same_host and not FLAGS.allow_resize_to_same_host:
1341
self._set_instance_error_state(context, instance_uuid)
1342
msg = _('destination same as source!')
1343
raise exception.MigrationError(msg)
1345
old_instance_type_id = instance_ref['instance_type_id']
1346
old_instance_type = instance_types.get_instance_type(
1347
old_instance_type_id)
1348
new_instance_type = instance_types.get_instance_type(instance_type_id)
1350
migration_ref = self.db.migration_create(context,
1351
{'instance_uuid': instance_ref['uuid'],
1352
'source_compute': instance_ref['host'],
1353
'dest_compute': FLAGS.host,
1354
'dest_host': self.driver.get_host_ip_addr(),
1355
'old_instance_type_id': old_instance_type['id'],
1356
'new_instance_type_id': instance_type_id,
1357
'status': 'pre-migrating'})
1359
LOG.audit(_('Migrating'), context=context, instance=instance_ref)
1360
self.compute_rpcapi.resize_instance(context, instance_ref,
1361
migration_ref['id'], image)
1363
extra_usage_info = dict(new_instance_type=new_instance_type['name'],
1364
new_instance_type_id=new_instance_type['id'])
1366
self._notify_about_instance_usage(
1367
context, instance_ref, "resize.prep.end",
1368
extra_usage_info=extra_usage_info)
1543
instance = self.db.instance_get_by_uuid(context, instance_uuid)
1545
if not instance_type:
1546
instance_type = instance_types.get_instance_type(instance_type_id)
1548
with self._error_out_instance_on_exception(context, instance['uuid'],
1550
compute_utils.notify_usage_exists(
1551
context, instance, current_period=True)
1552
self._notify_about_instance_usage(
1553
context, instance, "resize.prep.start")
1555
same_host = instance['host'] == FLAGS.host
1556
if same_host and not FLAGS.allow_resize_to_same_host:
1557
self._set_instance_error_state(context, instance['uuid'])
1558
msg = _('destination same as source!')
1559
raise exception.MigrationError(msg)
1561
# TODO(russellb): no-db-compute: Send the old instance type info
1562
# that is needed via rpc so db access isn't required here.
1563
old_instance_type_id = instance['instance_type_id']
1564
old_instance_type = instance_types.get_instance_type(
1565
old_instance_type_id)
1567
migration_ref = self.db.migration_create(context,
1568
{'instance_uuid': instance['uuid'],
1569
'source_compute': instance['host'],
1570
'dest_compute': FLAGS.host,
1571
'dest_host': self.driver.get_host_ip_addr(),
1572
'old_instance_type_id': old_instance_type['id'],
1573
'new_instance_type_id': instance_type['id'],
1574
'status': 'pre-migrating'})
1576
LOG.audit(_('Migrating'), context=context, instance=instance)
1577
self.compute_rpcapi.resize_instance(context, instance,
1578
migration_ref['id'], image, reservations)
1580
extra_usage_info = dict(
1581
new_instance_type=instance_type['name'],
1582
new_instance_type_id=instance_type['id'])
1584
self._notify_about_instance_usage(
1585
context, instance, "resize.prep.end",
1586
extra_usage_info=extra_usage_info)
1370
1588
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
1371
1590
@checks_instance_lock
1372
1591
@wrap_instance_fault
1373
def resize_instance(self, context, instance_uuid, migration_id, image):
1592
def resize_instance(self, context, migration_id, image, instance=None,
1593
instance_uuid=None, reservations=None):
1374
1594
"""Starts the migration of a running instance to another host."""
1375
1595
migration_ref = self.db.migration_get(context, migration_id)
1376
instance_ref = self.db.instance_get_by_uuid(context,
1377
migration_ref.instance_uuid)
1378
instance_type_ref = self.db.instance_type_get(context,
1379
migration_ref.new_instance_type_id)
1382
network_info = self._get_instance_nw_info(context, instance_ref)
1383
except Exception, error:
1384
with excutils.save_and_reraise_exception():
1385
msg = _('%s. Setting instance vm_state to ERROR')
1386
LOG.error(msg % error)
1387
self._set_instance_error_state(context, instance_uuid)
1389
self.db.migration_update(context,
1391
{'status': 'migrating'})
1393
self._instance_update(context, instance_uuid,
1394
task_state=task_states.RESIZE_MIGRATING)
1396
self._notify_about_instance_usage(
1397
context, instance_ref, "resize.start", network_info=network_info)
1597
instance = self.db.instance_get_by_uuid(context,
1598
migration_ref.instance_uuid)
1600
with self._error_out_instance_on_exception(context, instance['uuid'],
1602
instance_type_ref = self.db.instance_type_get(context,
1603
migration_ref.new_instance_type_id)
1605
network_info = self._get_instance_nw_info(context, instance)
1607
self.db.migration_update(context,
1609
{'status': 'migrating'})
1611
self._instance_update(context, instance['uuid'],
1612
task_state=task_states.RESIZE_MIGRATING)
1614
self._notify_about_instance_usage(
1615
context, instance, "resize.start", network_info=network_info)
1400
1617
disk_info = self.driver.migrate_disk_and_power_off(
1401
context, instance_ref, migration_ref['dest_host'],
1618
context, instance, migration_ref['dest_host'],
1402
1619
instance_type_ref, self._legacy_nw_info(network_info))
1403
except Exception, error:
1404
with excutils.save_and_reraise_exception():
1405
LOG.error(_('%s. Setting instance vm_state to ERROR') % error,
1406
instance=instance_ref)
1407
self._set_instance_error_state(context, instance_uuid)
1409
self.db.migration_update(context,
1411
{'status': 'post-migrating'})
1413
self._instance_update(context, instance_uuid,
1414
task_state=task_states.RESIZE_MIGRATED)
1416
self.compute_rpcapi.finish_resize(context, instance_ref, migration_id,
1417
image, disk_info, migration_ref['dest_compute'])
1419
self._notify_about_instance_usage(context, instance_ref, "resize.end",
1420
network_info=network_info)
1422
def _finish_resize(self, context, instance_ref, migration_ref, disk_info,
1621
self.db.migration_update(context,
1623
{'status': 'post-migrating'})
1625
self._instance_update(context, instance['uuid'],
1626
task_state=task_states.RESIZE_MIGRATED)
1628
self.compute_rpcapi.finish_resize(context, instance, migration_id,
1629
image, disk_info, migration_ref['dest_compute'], reservations)
1631
self._notify_about_instance_usage(context, instance, "resize.end",
1632
network_info=network_info)
1634
def _finish_resize(self, context, instance, migration_ref, disk_info,
1424
1636
resize_instance = False
1425
1637
old_instance_type_id = migration_ref['old_instance_type_id']
1438
1650
resize_instance = True
1440
1652
# NOTE(tr3buchet): setup networks on destination host
1441
self.network_api.setup_networks_on_host(context, instance_ref,
1653
self.network_api.setup_networks_on_host(context, instance,
1442
1654
migration_ref['dest_compute'])
1444
network_info = self._get_instance_nw_info(context, instance_ref)
1656
network_info = self._get_instance_nw_info(context, instance)
1446
self._instance_update(context, instance_ref.uuid,
1658
self._instance_update(context, instance['uuid'],
1447
1659
task_state=task_states.RESIZE_FINISH)
1449
1661
self._notify_about_instance_usage(
1450
context, instance_ref, "finish_resize.start",
1662
context, instance, "finish_resize.start",
1451
1663
network_info=network_info)
1453
self.driver.finish_migration(context, migration_ref, instance_ref,
1665
self.driver.finish_migration(context, migration_ref, instance,
1455
1667
self._legacy_nw_info(network_info),
1456
1668
image, resize_instance)
1458
instance_ref = self._instance_update(context,
1460
vm_state=vm_states.RESIZED,
1461
host=migration_ref['dest_compute'],
1462
launched_at=timeutils.utcnow(),
1670
instance = self._instance_update(context,
1672
vm_state=vm_states.RESIZED,
1673
host=migration_ref['dest_compute'],
1674
launched_at=timeutils.utcnow(),
1465
1677
self.db.migration_update(context, migration_ref.id,
1466
1678
{'status': 'finished'})
1468
1680
self._notify_about_instance_usage(
1469
context, instance_ref, "finish_resize.end",
1681
context, instance, "finish_resize.end",
1470
1682
network_info=network_info)
1472
1684
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
1473
1686
@checks_instance_lock
1474
1687
@wrap_instance_fault
1475
def finish_resize(self, context, instance_uuid, migration_id, disk_info,
1688
def finish_resize(self, context, migration_id, disk_info, image,
1689
instance_uuid=None, instance=None, reservations=None):
1477
1690
"""Completes the migration process.
1479
1692
Sets up the newly transferred disk and turns on the instance at its
1483
1696
migration_ref = self.db.migration_get(context, migration_id)
1484
instance_ref = self.db.instance_get_by_uuid(context,
1485
migration_ref.instance_uuid)
1698
instance = self.db.instance_get_by_uuid(context,
1699
migration_ref.instance_uuid)
1488
self._finish_resize(context, instance_ref, migration_ref,
1702
self._finish_resize(context, instance, migration_ref,
1489
1703
disk_info, image)
1704
self._quota_commit(context, reservations)
1490
1705
except Exception, error:
1706
self._quota_rollback(context, reservations)
1491
1707
with excutils.save_and_reraise_exception():
1492
1708
LOG.error(_('%s. Setting instance vm_state to ERROR') % error,
1493
instance=instance_ref)
1494
self._set_instance_error_state(context, instance_ref.uuid)
1710
self._set_instance_error_state(context, instance['uuid'])
1496
1712
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
1497
1714
@checks_instance_lock
1498
1715
@wrap_instance_fault
1499
def add_fixed_ip_to_instance(self, context, instance_uuid, network_id):
1716
def add_fixed_ip_to_instance(self, context, network_id, instance=None,
1717
instance_uuid=None):
1500
1718
"""Calls network_api to add new fixed_ip to instance
1501
1719
then injects the new network info and resets instance networking.
1504
instance_ref = self.db.instance_get_by_uuid(context, instance_uuid)
1723
instance = self.db.instance_get_by_uuid(context, instance_uuid)
1505
1724
self._notify_about_instance_usage(
1506
context, instance_ref, "create_ip.start")
1725
context, instance, "create_ip.start")
1508
instance_id = instance_ref['id']
1509
1727
self.network_api.add_fixed_ip_to_instance(context,
1513
network_info = self.inject_network_info(context,
1514
instance_ref['uuid'])
1515
self.reset_network(context, instance_ref['uuid'])
1731
network_info = self._inject_network_info(context, instance=instance)
1732
self.reset_network(context, instance)
1517
1734
self._notify_about_instance_usage(
1518
context, instance_ref, "create_ip.end", network_info=network_info)
1735
context, instance, "create_ip.end", network_info=network_info)
1520
1737
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
1521
1739
@checks_instance_lock
1522
1740
@wrap_instance_fault
1523
def remove_fixed_ip_from_instance(self, context, instance_uuid, address):
1741
def remove_fixed_ip_from_instance(self, context, address, instance=None,
1742
instance_uuid=None):
1524
1743
"""Calls network_api to remove existing fixed_ip from instance
1525
1744
by injecting the altered network info and resetting
1526
1745
instance networking.
1528
instance_ref = self.db.instance_get_by_uuid(context, instance_uuid)
1748
instance = self.db.instance_get_by_uuid(context, instance_uuid)
1529
1749
self._notify_about_instance_usage(
1530
context, instance_ref, "delete_ip.start")
1750
context, instance, "delete_ip.start")
1532
1752
self.network_api.remove_fixed_ip_from_instance(context,
1536
network_info = self.inject_network_info(context,
1537
instance_ref['uuid'])
1538
self.reset_network(context, instance_ref['uuid'])
1756
network_info = self._inject_network_info(context,
1758
self.reset_network(context, instance)
1540
1760
self._notify_about_instance_usage(
1541
context, instance_ref, "delete_ip.end", network_info=network_info)
1761
context, instance, "delete_ip.end", network_info=network_info)
1543
1763
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
1544
1765
@checks_instance_lock
1545
1766
@wrap_instance_fault
1546
def pause_instance(self, context, instance_uuid):
1767
def pause_instance(self, context, instance=None, instance_uuid=None):
1547
1768
"""Pause an instance on this host."""
1548
1769
context = context.elevated()
1549
instance_ref = self.db.instance_get_by_uuid(context, instance_uuid)
1551
LOG.audit(_('Pausing'), context=context, instance=instance_ref)
1552
self.driver.pause(instance_ref)
1554
current_power_state = self._get_power_state(context, instance_ref)
1771
instance = self.db.instance_get_by_uuid(context, instance_uuid)
1773
LOG.audit(_('Pausing'), context=context, instance=instance)
1774
self.driver.pause(instance)
1776
current_power_state = self._get_power_state(context, instance)
1555
1777
self._instance_update(context,
1556
instance_ref['uuid'],
1557
1779
power_state=current_power_state,
1558
1780
vm_state=vm_states.PAUSED,
1559
1781
task_state=None)
1561
1783
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
1562
1785
@checks_instance_lock
1563
1786
@wrap_instance_fault
1564
def unpause_instance(self, context, instance_uuid):
1787
def unpause_instance(self, context, instance=None, instance_uuid=None):
1565
1788
"""Unpause a paused instance on this host."""
1566
1789
context = context.elevated()
1567
instance_ref = self.db.instance_get_by_uuid(context, instance_uuid)
1569
LOG.audit(_('Unpausing'), context=context, instance=instance_ref)
1570
self.driver.unpause(instance_ref)
1572
current_power_state = self._get_power_state(context, instance_ref)
1791
instance = self.db.instance_get_by_uuid(context, instance_uuid)
1793
LOG.audit(_('Unpausing'), context=context, instance=instance)
1794
self.driver.unpause(instance)
1796
current_power_state = self._get_power_state(context, instance)
1573
1797
self._instance_update(context,
1574
instance_ref['uuid'],
1575
1799
power_state=current_power_state,
1576
1800
vm_state=vm_states.ACTIVE,
1577
1801
task_state=None)
1593
1817
return self.driver.set_host_enabled(host, enabled)
1595
1819
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
1820
def get_host_uptime(self, context, host):
1821
"""Returns the result of calling "uptime" on the target host."""
1822
return self.driver.get_host_uptime(host)
1824
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
1596
1825
@wrap_instance_fault
1597
def get_diagnostics(self, context, instance_uuid):
1826
def get_diagnostics(self, context, instance=None, instance_uuid=None):
1598
1827
"""Retrieve diagnostics for an instance on this host."""
1599
instance_ref = self.db.instance_get_by_uuid(context, instance_uuid)
1600
current_power_state = self._get_power_state(context, instance_ref)
1829
instance = self.db.instance_get_by_uuid(context, instance_uuid)
1830
current_power_state = self._get_power_state(context, instance)
1601
1831
if current_power_state == power_state.RUNNING:
1602
1832
LOG.audit(_("Retrieving diagnostics"), context=context,
1603
instance=instance_ref)
1604
return self.driver.get_diagnostics(instance_ref)
1834
return self.driver.get_diagnostics(instance)
1606
1836
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
1607
1838
@checks_instance_lock
1608
1839
@wrap_instance_fault
1609
def suspend_instance(self, context, instance_uuid):
1840
def suspend_instance(self, context, instance=None, instance_uuid=None):
1610
1841
"""Suspend the given instance."""
1611
1842
context = context.elevated()
1612
instance_ref = self.db.instance_get_by_uuid(context, instance_uuid)
1614
LOG.audit(_('Suspending'), context=context, instance=instance_ref)
1615
self.driver.suspend(instance_ref)
1617
current_power_state = self._get_power_state(context, instance_ref)
1844
instance = self.db.instance_get_by_uuid(context, instance_uuid)
1846
LOG.audit(_('Suspending'), context=context, instance=instance)
1847
with self._error_out_instance_on_exception(context, instance['uuid']):
1848
self.driver.suspend(instance)
1850
current_power_state = self._get_power_state(context, instance)
1618
1851
self._instance_update(context,
1619
instance_ref['uuid'],
1620
1853
power_state=current_power_state,
1621
1854
vm_state=vm_states.SUSPENDED,
1622
1855
task_state=None)
1624
self._notify_about_instance_usage(context, instance_ref, 'suspend')
1857
self._notify_about_instance_usage(context, instance, 'suspend')
1626
1859
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
1627
1861
@checks_instance_lock
1628
1862
@wrap_instance_fault
1629
def resume_instance(self, context, instance_uuid):
1863
def resume_instance(self, context, instance=None, instance_uuid=None):
1630
1864
"""Resume the given suspended instance."""
1631
1865
context = context.elevated()
1632
instance_ref = self.db.instance_get_by_uuid(context, instance_uuid)
1634
LOG.audit(_('Resuming'), context=context, instance=instance_ref)
1635
self.driver.resume(instance_ref)
1637
current_power_state = self._get_power_state(context, instance_ref)
1867
instance = self.db.instance_get_by_uuid(context, instance_uuid)
1869
LOG.audit(_('Resuming'), context=context, instance=instance)
1870
self.driver.resume(instance)
1872
current_power_state = self._get_power_state(context, instance)
1638
1873
self._instance_update(context,
1639
instance_ref['uuid'],
1640
1875
power_state=current_power_state,
1641
1876
vm_state=vm_states.ACTIVE,
1642
1877
task_state=None)
1644
self._notify_about_instance_usage(context, instance_ref, 'resume')
1879
self._notify_about_instance_usage(context, instance, 'resume')
1646
1881
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
1647
1882
@wrap_instance_fault
1648
1883
def lock_instance(self, context, instance_uuid):
1649
"""Lock the given instance."""
1884
"""Lock the given instance.
1886
This isn't actually used in the current code. The same thing is now
1887
done directly in nova.compute.api. This must stay here for backwards
1888
compatibility of the rpc API.
1650
1890
context = context.elevated()
1652
1892
LOG.debug(_('Locking'), context=context, instance_uuid=instance_uuid)
1816
2104
'volume_id': volume_id,
1817
2105
'volume_size': None,
1818
2106
'no_device': None}
1819
self.db.block_device_mapping_create(context, values)
2107
self.db.block_device_mapping_update_or_create(context, values)
1822
2109
def _detach_volume(self, context, instance, bdm):
1823
2110
"""Do the actual driver detach using block device mapping."""
1824
instance_name = instance['name']
1825
instance_uuid = instance['uuid']
1826
2111
mp = bdm['device_name']
1827
2112
volume_id = bdm['volume_id']
1829
2114
LOG.audit(_('Detach volume %(volume_id)s from mountpoint %(mp)s'),
1830
2115
locals(), context=context, instance=instance)
1832
if instance_name not in self.driver.list_instances():
2117
if instance['name'] not in self.driver.list_instances():
1833
2118
LOG.warn(_('Detaching volume from unknown instance'),
1834
2119
context=context, instance=instance)
1835
2120
self.driver.detach_volume(jsonutils.loads(bdm['connection_info']),
1839
2124
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
1840
2126
@checks_instance_lock
1841
2127
@wrap_instance_fault
1842
def detach_volume(self, context, instance_uuid, volume_id):
2128
def detach_volume(self, context, volume_id, instance_uuid=None,
1843
2130
"""Detach a volume from an instance."""
1844
instance_ref = self.db.instance_get_by_uuid(context, instance_uuid)
1845
bdm = self._get_instance_volume_bdm(context, instance_uuid, volume_id)
1846
self._detach_volume(context, instance_ref, bdm)
2132
instance = self.db.instance_get_by_uuid(context, instance_uuid)
2134
bdm = self._get_instance_volume_bdm(context, instance['uuid'],
2136
self._detach_volume(context, instance, bdm)
1847
2137
volume = self.volume_api.get(context, volume_id)
1848
connector = self.driver.get_volume_connector(instance_ref)
2138
connector = self.driver.get_volume_connector(instance)
1849
2139
self.volume_api.terminate_connection(context, volume, connector)
1850
2140
self.volume_api.detach(context.elevated(), volume)
1851
2141
self.db.block_device_mapping_destroy_by_instance_and_volume(
1852
context, instance_uuid, volume_id)
2142
context, instance['uuid'], volume_id)
1855
2144
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
1856
def remove_volume_connection(self, context, instance_id, volume_id):
2145
def remove_volume_connection(self, context, volume_id, instance=None,
1857
2147
"""Remove a volume connection using the volume api"""
1858
2148
# NOTE(vish): We don't want to actually mark the volume
1859
2149
# detached, or delete the bdm, just remove the
1860
2150
# connection from this host.
1862
instance_ref = self.db.instance_get(context, instance_id)
2153
instance = self.db.instance_get(context, instance_id)
1863
2154
bdm = self._get_instance_volume_bdm(context,
1864
instance_ref['uuid'],
1866
self._detach_volume(context, instance_ref, bdm)
2157
self._detach_volume(context, instance, bdm)
1867
2158
volume = self.volume_api.get(context, volume_id)
1868
connector = self.driver.get_volume_connector(instance_ref)
2159
connector = self.driver.get_volume_connector(instance)
1869
2160
self.volume_api.terminate_connection(context, volume, connector)
1870
2161
except exception.NotFound:
1873
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
1874
def compare_cpu(self, context, cpu_info):
1875
"""Checks that the host cpu is compatible with a cpu given by xml.
1877
:param context: security context
1878
:param cpu_info: json string obtained from virConnect.getCapabilities
1879
:returns: See driver.compare_cpu
1882
return self.driver.compare_cpu(cpu_info)
1884
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
1885
def create_shared_storage_test_file(self, context):
1886
"""Makes tmpfile under FLAGS.instance_path.
1888
This method enables compute nodes to recognize that they mounts
1889
same shared storage. (create|check|creanup)_shared_storage_test_file()
1892
:param context: security context
1893
:returns: tmpfile name(basename)
1896
dirpath = FLAGS.instances_path
1897
fd, tmp_file = tempfile.mkstemp(dir=dirpath)
1898
LOG.debug(_("Creating tmpfile %s to notify to other "
1899
"compute nodes that they should mount "
1900
"the same storage.") % tmp_file)
1902
return os.path.basename(tmp_file)
1904
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
1905
def check_shared_storage_test_file(self, context, filename):
1906
"""Confirms existence of the tmpfile under FLAGS.instances_path.
1907
Cannot confirm tmpfile return False.
1909
:param context: security context
1910
:param filename: confirm existence of FLAGS.instances_path/thisfile
1913
tmp_file = os.path.join(FLAGS.instances_path, filename)
1914
if not os.path.exists(tmp_file):
1919
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
1920
def cleanup_shared_storage_test_file(self, context, filename):
1921
"""Removes existence of the tmpfile under FLAGS.instances_path.
1923
:param context: security context
1924
:param filename: remove existence of FLAGS.instances_path/thisfile
1927
tmp_file = os.path.join(FLAGS.instances_path, filename)
1930
2164
def get_instance_disk_info(self, context, instance_name):
1931
2165
"""Getting infomation of instance's current disk.
2167
DEPRECATED: This method is no longer used by any current code, but it
2168
is left here to provide backwards compatibility in the rpcapi.
1933
2170
Implementation nova.virt.libvirt.connection.
1935
2172
:param context: security context
1939
2176
return self.driver.get_instance_disk_info(instance_name)
1941
def pre_live_migration(self, context, instance_id,
2178
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
2179
def compare_cpu(self, context, cpu_info):
2180
raise rpc_common.RPCException(message=_('Deprecated from version 1.2'))
2182
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
2183
def create_shared_storage_test_file(self, context):
2184
raise rpc_common.RPCException(message=_('Deprecated from version 1.2'))
2186
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
2187
def check_shared_storage_test_file(self, context, filename):
2188
raise rpc_common.RPCException(message=_('Deprecated from version 1.2'))
2190
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
2191
def cleanup_shared_storage_test_file(self, context, filename):
2192
raise rpc_common.RPCException(message=_('Deprecated from version 1.2'))
2194
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
2195
def check_can_live_migrate_destination(self, ctxt, block_migration=False,
2196
disk_over_commit=False,
2197
instance_id=None, instance=None):
2198
"""Check if it is possible to execute live migration.
2200
This runs checks on the destination host, and then calls
2201
back to the source host to check the results.
2203
:param context: security context
2204
:param instance: dict of instance data
2205
:param instance_id: (deprecated and only supplied if no instance passed
2206
in) nova.db.sqlalchemy.models.Instance.Id
2207
:param block_migration: if true, prepare for block migration
2208
:param disk_over_commit: if true, allow disk over commit
2210
Returns a mapping of values required in case of block migration
2214
instance = self.db.instance_get(ctxt, instance_id)
2215
dest_check_data = self.driver.check_can_live_migrate_destination(ctxt,
2216
instance, block_migration, disk_over_commit)
2218
self.compute_rpcapi.check_can_live_migrate_source(ctxt,
2219
instance, dest_check_data)
2221
self.driver.check_can_live_migrate_destination_cleanup(ctxt,
2223
if dest_check_data and 'migrate_data' in dest_check_data:
2224
return dest_check_data['migrate_data']
2226
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
2227
def check_can_live_migrate_source(self, ctxt, dest_check_data,
2228
instance_id=None, instance=None):
2229
"""Check if it is possible to execute live migration.
2231
This checks if the live migration can succeed, based on the
2232
results from check_can_live_migrate_destination.
2234
:param context: security context
2235
:param instance: dict of instance data
2236
:param instance_id: (deprecated and only supplied if no instance passed
2237
in) nova.db.sqlalchemy.models.Instance.Id
2238
:param dest_check_data: result of check_can_live_migrate_destination
2241
instance = self.db.instance_get(ctxt, instance_id)
2242
self.driver.check_can_live_migrate_source(ctxt, instance,
2245
def pre_live_migration(self, context, instance=None, instance_id=None,
1942
2246
block_migration=False, disk=None):
1943
2247
"""Preparations for live migration at dest host.
1997
2283
# This nwfilter is necessary on the destination host.
1998
2284
# In addition, this method is creating filtering rule
1999
2285
# onto destination host.
2000
self.driver.ensure_filtering_rules_for_instance(instance_ref,
2286
self.driver.ensure_filtering_rules_for_instance(instance,
2001
2287
self._legacy_nw_info(network_info))
2003
2289
# Preparation for block migration
2004
2290
if block_migration:
2005
self.driver.pre_block_migration(context,
2291
self.driver.pre_block_migration(context, instance, disk)
2009
def live_migration(self, context, instance_id,
2010
dest, block_migration=False):
2293
def live_migration(self, context, dest, block_migration=False,
2294
instance=None, instance_id=None, migrate_data=None):
2011
2295
"""Executing live migration.
2013
2297
:param context: security context
2014
:param instance_id: nova.db.sqlalchemy.models.Instance.Id
2298
:param instance_id: (deprecated) nova.db.sqlalchemy.models.Instance.Id
2299
:param instance: instance dict
2015
2300
:param dest: destination host
2016
2301
:param block_migration: if true, prepare for block migration
2302
:param migrate_data: implementation specific params
2019
2305
# Get instance for error handling.
2020
instance_ref = self.db.instance_get(context, instance_id)
2307
instance = self.db.instance_get(context, instance_id)
2023
2310
# Checking volume node is working correctly when any volumes
2024
2311
# are attached to instances.
2025
if self._get_instance_volume_bdms(context, instance_ref['uuid']):
2312
if self._get_instance_volume_bdms(context, instance['uuid']):
2026
2313
rpc.call(context,
2027
2314
FLAGS.volume_topic,
2028
2315
{'method': 'check_for_export',
2029
'args': {'instance_id': instance_id}})
2316
'args': {'instance_id': instance['id']}})
2031
2318
if block_migration:
2032
disk = self.driver.get_instance_disk_info(instance_ref.name)
2319
disk = self.driver.get_instance_disk_info(instance['name'])
2036
self.compute_rpcapi.pre_live_migration(context, instance_ref,
2323
self.compute_rpcapi.pre_live_migration(context, instance,
2037
2324
block_migration, disk, dest)
2039
2326
except Exception:
2040
2327
with excutils.save_and_reraise_exception():
2041
instance_uuid = instance_ref['uuid']
2328
instance_uuid = instance['uuid']
2042
2329
LOG.exception(_('Pre live migration failed at %(dest)s'),
2043
locals(), instance=instance_ref)
2044
self.rollback_live_migration(context, instance_ref, dest,
2330
locals(), instance=instance)
2331
self.rollback_live_migration(context, instance, dest,
2045
2332
block_migration)
2047
2334
# Executing live migration
2048
2335
# live_migration might raises exceptions, but
2049
2336
# nothing must be recovered in this version.
2050
self.driver.live_migration(context, instance_ref, dest,
2051
self.post_live_migration,
2337
self.driver.live_migration(context, instance, dest,
2338
self._post_live_migration,
2052
2339
self.rollback_live_migration,
2340
block_migration, migrate_data)
2055
def post_live_migration(self, ctxt, instance_ref,
2342
def _post_live_migration(self, ctxt, instance_ref,
2056
2343
dest, block_migration=False):
2057
2344
"""Post operations for live migration.