177
183
LOG.warning(_('Hypervisor driver does not '
178
184
'support firewall rules'))
180
def _update_state(self, context, instance_id, state=None):
181
"""Update the state of an instance from the driver info."""
182
instance_ref = self.db.instance_get(context, instance_id)
186
LOG.debug(_('Checking state of %s'), instance_ref['name'])
187
info = self.driver.get_info(instance_ref['name'])
188
except exception.NotFound:
192
state = info['state']
194
state = power_state.FAILED
196
self.db.instance_set_state(context, instance_id, state)
199
def _update_launched_at(self, context, instance_id, launched_at=None):
200
"""Update the launched_at parameter of the given instance."""
201
data = {'launched_at': launched_at or utils.utcnow()}
202
self.db.instance_update(context, instance_id, data)
186
def _get_power_state(self, context, instance):
187
"""Retrieve the power state for the given instance."""
188
LOG.debug(_('Checking state of %s'), instance['name'])
190
return self.driver.get_info(instance['name'])["state"]
191
except exception.NotFound:
192
return power_state.FAILED
204
194
def get_console_topic(self, context, **kwargs):
205
195
"""Retrieves the console host for a project on this host.
390
375
updates['host'] = self.host
391
376
updates['launched_on'] = self.host
392
instance = self.db.instance_update(context,
377
updates['vm_state'] = vm_states.BUILDING
378
updates['task_state'] = task_states.NETWORKING
379
instance = self.db.instance_update(context, instance_id, updates)
395
380
instance['injected_files'] = kwargs.get('injected_files', [])
396
381
instance['admin_pass'] = kwargs.get('admin_password', None)
398
self.db.instance_set_state(context,
403
383
is_vpn = instance['image_ref'] == str(FLAGS.vpn_image_id)
405
385
# NOTE(vish): This could be a cast because we don't do anything
427
412
'ephemerals': ephemerals,
428
413
'block_device_mapping': block_device_mapping}
415
self._instance_update(context,
417
vm_state=vm_states.BUILDING,
418
task_state=task_states.SPAWNING)
430
420
# TODO(vish) check to make sure the availability zone matches
431
self._update_state(context, instance_id, power_state.BUILDING)
434
422
self.driver.spawn(context, instance,
435
423
network_info, block_device_info)
438
426
"virtualization enabled in the BIOS? Details: "
439
427
"%(ex)s") % locals()
440
428
LOG.exception(msg)
442
self._update_launched_at(context, instance_id)
443
self._update_state(context, instance_id)
431
current_power_state = self._get_power_state(context, instance)
432
self._instance_update(context,
434
power_state=current_power_state,
435
vm_state=vm_states.ACTIVE,
437
launched_at=utils.utcnow())
444
439
usage_info = utils.usage_from_instance(instance)
445
440
notifier.notify('compute.%s' % self.host,
446
441
'compute.instance.create',
447
442
notifier.INFO, usage_info)
448
444
except exception.InstanceNotFound:
449
445
# FIXME(wwolf): We are just ignoring InstanceNotFound
450
446
# exceptions here in case the instance was immediately
496
491
"""Terminate an instance on this host."""
497
492
self._shutdown_instance(context, instance_id, 'Terminating')
498
493
instance = self.db.instance_get(context.elevated(), instance_id)
494
self._instance_update(context,
496
vm_state=vm_states.DELETED,
498
terminated_at=utils.utcnow())
500
# TODO(ja): should we keep it in a terminated state for a bit?
501
500
self.db.instance_destroy(context, instance_id)
502
502
usage_info = utils.usage_from_instance(instance)
503
503
notifier.notify('compute.%s' % self.host,
504
504
'compute.instance.delete',
529
532
instance_ref = self.db.instance_get(context, instance_id)
530
533
LOG.audit(_("Rebuilding instance %s"), instance_id, context=context)
532
self._update_state(context, instance_id, power_state.BUILDING)
535
current_power_state = self._get_power_state(context, instance_ref)
536
self._instance_update(context,
538
power_state=current_power_state,
539
vm_state=vm_states.REBUILDING,
534
542
network_info = self._get_instance_nw_info(context, instance_ref)
536
543
self.driver.destroy(instance_ref, network_info)
545
self._instance_update(context,
547
vm_state=vm_states.REBUILDING,
548
task_state=task_states.BLOCK_DEVICE_MAPPING)
537
550
instance_ref.injected_files = kwargs.get('injected_files', [])
538
551
network_info = self.network_api.get_instance_nw_info(context,
540
553
bd_mapping = self._setup_block_device_mapping(context, instance_id)
555
self._instance_update(context,
557
vm_state=vm_states.REBUILDING,
558
task_state=task_states.SPAWNING)
542
560
# pull in new password here since the original password isn't in the db
543
561
instance_ref.admin_pass = kwargs.get('new_pass',
544
562
utils.generate_password(FLAGS.password_length))
546
564
self.driver.spawn(context, instance_ref, network_info, bd_mapping)
548
self._update_launched_at(context, instance_id)
549
self._update_state(context, instance_id)
566
current_power_state = self._get_power_state(context, instance_ref)
567
self._instance_update(context,
569
power_state=current_power_state,
570
vm_state=vm_states.ACTIVE,
572
launched_at=utils.utcnow())
550
574
usage_info = utils.usage_from_instance(instance_ref)
552
575
notifier.notify('compute.%s' % self.host,
553
576
'compute.instance.rebuild',
558
581
@checks_instance_lock
559
582
def reboot_instance(self, context, instance_id):
560
583
"""Reboot an instance on this host."""
561
context = context.elevated()
562
self._update_state(context, instance_id)
563
instance_ref = self.db.instance_get(context, instance_id)
564
584
LOG.audit(_("Rebooting instance %s"), instance_id, context=context)
566
if instance_ref['state'] != power_state.RUNNING:
567
state = instance_ref['state']
585
context = context.elevated()
586
instance_ref = self.db.instance_get(context, instance_id)
588
current_power_state = self._get_power_state(context, instance_ref)
589
self._instance_update(context,
591
power_state=current_power_state,
592
vm_state=vm_states.ACTIVE,
593
task_state=task_states.REBOOTING)
595
if instance_ref['power_state'] != power_state.RUNNING:
596
state = instance_ref['power_state']
568
597
running = power_state.RUNNING
569
598
LOG.warn(_('trying to reboot a non-running '
570
599
'instance: %(instance_id)s (state: %(state)s '
571
600
'expected: %(running)s)') % locals(),
574
self.db.instance_set_state(context,
578
603
network_info = self._get_instance_nw_info(context, instance_ref)
579
604
self.driver.reboot(instance_ref, network_info)
580
self._update_state(context, instance_id)
606
current_power_state = self._get_power_state(context, instance_ref)
607
self._instance_update(context,
609
power_state=current_power_state,
610
vm_state=vm_states.ACTIVE,
582
613
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
583
614
def snapshot_instance(self, context, instance_id, image_id,
593
624
:param rotation: int representing how many backups to keep around;
594
625
None if rotation shouldn't be used (as in the case of snapshots)
627
if image_type == "snapshot":
628
task_state = task_states.IMAGE_SNAPSHOT
629
elif image_type == "backup":
630
task_state = task_states.IMAGE_BACKUP
632
raise Exception(_('Image type not recognized %s') % image_type)
596
634
context = context.elevated()
597
635
instance_ref = self.db.instance_get(context, instance_id)
599
#NOTE(sirp): update_state currently only refreshes the state field
600
# if we add is_snapshotting, we will need this refreshed too,
602
self._update_state(context, instance_id)
637
current_power_state = self._get_power_state(context, instance_ref)
638
self._instance_update(context,
640
power_state=current_power_state,
641
vm_state=vm_states.ACTIVE,
642
task_state=task_state)
604
644
LOG.audit(_('instance %s: snapshotting'), instance_id,
606
if instance_ref['state'] != power_state.RUNNING:
607
state = instance_ref['state']
647
if instance_ref['power_state'] != power_state.RUNNING:
648
state = instance_ref['power_state']
608
649
running = power_state.RUNNING
609
650
LOG.warn(_('trying to snapshot a non-running '
610
651
'instance: %(instance_id)s (state: %(state)s '
611
652
'expected: %(running)s)') % locals())
613
654
self.driver.snapshot(context, instance_ref, image_id)
615
if image_type == 'snapshot':
617
raise exception.ImageRotationNotAllowed()
655
self._instance_update(context, instance_id, task_state=None)
657
if image_type == 'snapshot' and rotation:
658
raise exception.ImageRotationNotAllowed()
660
elif image_type == 'backup' and rotation:
661
instance_uuid = instance_ref['uuid']
662
self.rotate_backups(context, instance_uuid, backup_type, rotation)
618
664
elif image_type == 'backup':
620
instance_uuid = instance_ref['uuid']
621
self.rotate_backups(context, instance_uuid, backup_type,
624
raise exception.RotationRequiredForBackup()
626
raise Exception(_('Image type not recognized %s') % image_type)
665
raise exception.RotationRequiredForBackup()
628
667
def rotate_backups(self, context, instance_uuid, backup_type, rotation):
629
668
"""Delete excess backups associated to an instance.
759
798
@checks_instance_lock
760
799
def rescue_instance(self, context, instance_id):
761
800
"""Rescue an instance on this host."""
762
context = context.elevated()
763
instance_ref = self.db.instance_get(context, instance_id)
764
801
LOG.audit(_('instance %s: rescuing'), instance_id, context=context)
765
self.db.instance_set_state(context,
769
_update_state = lambda result: self._update_state_callback(
770
self, context, instance_id, result)
802
context = context.elevated()
804
instance_ref = self.db.instance_get(context, instance_id)
771
805
network_info = self._get_instance_nw_info(context, instance_ref)
772
self.driver.rescue(context, instance_ref, _update_state, network_info)
773
self._update_state(context, instance_id)
807
# NOTE(blamar): None of the virt drivers use the 'callback' param
808
self.driver.rescue(context, instance_ref, None, network_info)
810
current_power_state = self._get_power_state(context, instance_ref)
811
self._instance_update(context,
813
vm_state=vm_states.RESCUED,
815
power_state=current_power_state)
775
817
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
776
818
@checks_instance_lock
777
819
def unrescue_instance(self, context, instance_id):
778
820
"""Rescue an instance on this host."""
779
context = context.elevated()
780
instance_ref = self.db.instance_get(context, instance_id)
781
821
LOG.audit(_('instance %s: unrescuing'), instance_id, context=context)
782
self.db.instance_set_state(context,
786
_update_state = lambda result: self._update_state_callback(
787
self, context, instance_id, result)
822
context = context.elevated()
824
instance_ref = self.db.instance_get(context, instance_id)
788
825
network_info = self._get_instance_nw_info(context, instance_ref)
789
self.driver.unrescue(instance_ref, _update_state, network_info)
790
self._update_state(context, instance_id)
793
def _update_state_callback(self, context, instance_id, result):
794
"""Update instance state when async task completes."""
795
self._update_state(context, instance_id)
827
# NOTE(blamar): None of the virt drivers use the 'callback' param
828
self.driver.unrescue(instance_ref, None, network_info)
830
current_power_state = self._get_power_state(context, instance_ref)
831
self._instance_update(context,
833
vm_state=vm_states.ACTIVE,
835
power_state=current_power_state)
797
837
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
798
838
@checks_instance_lock
852
892
# Just roll back the record. There's no need to resize down since
853
893
# the 'old' VM already has the preferred attributes
854
self.db.instance_update(context, instance_ref['uuid'],
855
dict(memory_mb=instance_type['memory_mb'],
856
vcpus=instance_type['vcpus'],
857
local_gb=instance_type['local_gb'],
858
instance_type_id=instance_type['id']))
894
self._instance_update(context,
895
instance_ref["uuid"],
896
memory_mb=instance_type['memory_mb'],
897
vcpus=instance_type['vcpus'],
898
local_gb=instance_type['local_gb'],
899
instance_type_id=instance_type['id'])
860
901
self.driver.revert_migration(instance_ref)
861
902
self.db.migration_update(context, migration_id,
1008
1057
@checks_instance_lock
1009
1058
def pause_instance(self, context, instance_id):
1010
1059
"""Pause an instance on this host."""
1011
context = context.elevated()
1012
instance_ref = self.db.instance_get(context, instance_id)
1013
1060
LOG.audit(_('instance %s: pausing'), instance_id, context=context)
1014
self.db.instance_set_state(context,
1016
power_state.NOSTATE,
1018
self.driver.pause(instance_ref,
1019
lambda result: self._update_state_callback(self,
1061
context = context.elevated()
1063
instance_ref = self.db.instance_get(context, instance_id)
1064
self.driver.pause(instance_ref, lambda result: None)
1066
current_power_state = self._get_power_state(context, instance_ref)
1067
self._instance_update(context,
1069
power_state=current_power_state,
1070
vm_state=vm_states.PAUSED,
1024
1073
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
1025
1074
@checks_instance_lock
1026
1075
def unpause_instance(self, context, instance_id):
1027
1076
"""Unpause a paused instance on this host."""
1028
context = context.elevated()
1029
instance_ref = self.db.instance_get(context, instance_id)
1030
1077
LOG.audit(_('instance %s: unpausing'), instance_id, context=context)
1031
self.db.instance_set_state(context,
1033
power_state.NOSTATE,
1035
self.driver.unpause(instance_ref,
1036
lambda result: self._update_state_callback(self,
1078
context = context.elevated()
1080
instance_ref = self.db.instance_get(context, instance_id)
1081
self.driver.unpause(instance_ref, lambda result: None)
1083
current_power_state = self._get_power_state(context, instance_ref)
1084
self._instance_update(context,
1086
power_state=current_power_state,
1087
vm_state=vm_states.ACTIVE,
1041
1090
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
1042
1091
def host_power_action(self, context, host=None, action=None):
1061
1110
@checks_instance_lock
1062
1111
def suspend_instance(self, context, instance_id):
1063
1112
"""Suspend the given instance."""
1064
context = context.elevated()
1065
instance_ref = self.db.instance_get(context, instance_id)
1066
1113
LOG.audit(_('instance %s: suspending'), instance_id, context=context)
1067
self.db.instance_set_state(context, instance_id,
1068
power_state.NOSTATE,
1070
self.driver.suspend(instance_ref,
1071
lambda result: self._update_state_callback(self,
1114
context = context.elevated()
1116
instance_ref = self.db.instance_get(context, instance_id)
1117
self.driver.suspend(instance_ref, lambda result: None)
1119
current_power_state = self._get_power_state(context, instance_ref)
1120
self._instance_update(context,
1122
power_state=current_power_state,
1123
vm_state=vm_states.SUSPENDED,
1076
1126
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
1077
1127
@checks_instance_lock
1078
1128
def resume_instance(self, context, instance_id):
1079
1129
"""Resume the given suspended instance."""
1080
context = context.elevated()
1081
instance_ref = self.db.instance_get(context, instance_id)
1082
1130
LOG.audit(_('instance %s: resuming'), instance_id, context=context)
1083
self.db.instance_set_state(context, instance_id,
1084
power_state.NOSTATE,
1086
self.driver.resume(instance_ref,
1087
lambda result: self._update_state_callback(self,
1131
context = context.elevated()
1133
instance_ref = self.db.instance_get(context, instance_id)
1134
self.driver.resume(instance_ref, lambda result: None)
1136
current_power_state = self._get_power_state(context, instance_ref)
1137
self._instance_update(context,
1139
power_state=current_power_state,
1140
vm_state=vm_states.ACTIVE,
1092
1143
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
1093
1144
def lock_instance(self, context, instance_id):
1498
1549
'block_migration': block_migration}})
1500
1551
# Restore instance state
1501
self.db.instance_update(ctxt,
1503
{'state_description': 'running',
1504
'state': power_state.RUNNING,
1552
current_power_state = self._get_power_state(ctxt, instance_ref)
1553
self._instance_update(ctxt,
1556
power_state=current_power_state,
1557
vm_state=vm_states.ACTIVE,
1506
1560
# Restore volume state
1507
1561
for volume_ref in instance_ref['volumes']:
1508
1562
volume_id = volume_ref['id']
1618
1671
self.update_service_capabilities(
1619
1672
self.driver.get_host_stats(refresh=True))
1621
def _poll_instance_states(self, context):
1674
def _sync_power_states(self, context):
1675
"""Align power states between the database and the hypervisor.
1677
The hypervisor is authoritative for the power_state data, so we
1678
simply loop over all known instances for this host and update the
1679
power_state according to the hypervisor. If the instance is not found
1680
then it will be set to power_state.NOSTATE, because it doesn't exist
1622
1684
vm_instances = self.driver.list_instances_detail()
1623
1685
vm_instances = dict((vm.name, vm) for vm in vm_instances)
1625
# Keep a list of VMs not in the DB, cross them off as we find them
1626
vms_not_found_in_db = list(vm_instances.keys())
1628
1686
db_instances = self.db.instance_get_all_by_host(context, self.host)
1688
num_vm_instances = len(vm_instances)
1689
num_db_instances = len(db_instances)
1691
if num_vm_instances != num_db_instances:
1692
LOG.info(_("Found %(num_db_instances)s in the database and "
1693
"%(num_vm_instances)s on the hypervisor.") % locals())
1630
1695
for db_instance in db_instances:
1631
name = db_instance['name']
1632
db_state = db_instance['state']
1696
name = db_instance["name"]
1697
db_power_state = db_instance['power_state']
1633
1698
vm_instance = vm_instances.get(name)
1635
1700
if vm_instance is None:
1636
# NOTE(justinsb): We have to be very careful here, because a
1637
# concurrent operation could be in progress (e.g. a spawn)
1638
if db_state == power_state.BUILDING:
1639
# TODO(justinsb): This does mean that if we crash during a
1640
# spawn, the machine will never leave the spawning state,
1641
# but this is just the way nova is; this function isn't
1642
# trying to correct that problem.
1643
# We could have a separate task to correct this error.
1644
# TODO(justinsb): What happens during a live migration?
1645
LOG.info(_("Found instance '%(name)s' in DB but no VM. "
1646
"State=%(db_state)s, so assuming spawn is in "
1647
"progress.") % locals())
1650
LOG.info(_("Found instance '%(name)s' in DB but no VM. "
1651
"State=%(db_state)s, so setting state to "
1652
"shutoff.") % locals())
1653
vm_state = power_state.SHUTOFF
1654
if db_instance['state_description'] == 'stopping':
1655
self.db.instance_stop(context, db_instance['id'])
1701
vm_power_state = power_state.NOSTATE
1658
vm_state = vm_instance.state
1659
vms_not_found_in_db.remove(name)
1703
vm_power_state = vm_instance.state
1661
if (db_instance['state_description'] in ['migrating', 'stopping']):
1662
# A situation which db record exists, but no instance"
1663
# sometimes occurs while live-migration at src compute,
1664
# this case should be ignored.
1665
LOG.debug(_("Ignoring %(name)s, as it's currently being "
1666
"migrated.") % locals())
1705
if vm_power_state == db_power_state:
1669
if vm_state != db_state:
1670
LOG.info(_("DB/VM state mismatch. Changing state from "
1671
"'%(db_state)s' to '%(vm_state)s'") % locals())
1672
self._update_state(context, db_instance['id'], vm_state)
1674
# NOTE(justinsb): We no longer auto-remove SHUTOFF instances
1675
# It's quite hard to get them back when we do.
1677
# Are there VMs not in the DB?
1678
for vm_not_found_in_db in vms_not_found_in_db:
1679
name = vm_not_found_in_db
1681
# We only care about instances that compute *should* know about
1682
if name.startswith("instance-"):
1683
# TODO(justinsb): What to do here? Adopt it? Shut it down?
1684
LOG.warning(_("Found VM not in DB: '%(name)s'. Ignoring")
1708
self._instance_update(context,
1710
power_state=vm_power_state)