506
505
LOG.warning(_('Hypervisor driver does not support '
507
506
'firewall rules'), instance=instance)
508
def handle_lifecycle_event(self, event):
509
LOG.info(_("Lifecycle event %(state)d on VM %(uuid)s") %
510
{'state': event.get_transition(),
511
'uuid': event.get_instance_uuid()})
512
context = nova.context.get_admin_context()
513
instance = self.conductor_api.instance_get_by_uuid(
514
context, event.get_instance_uuid())
515
vm_power_state = None
516
if event.get_transition() == virtevent.EVENT_LIFECYCLE_STOPPED:
517
vm_power_state = power_state.SHUTDOWN
518
elif event.get_transition() == virtevent.EVENT_LIFECYCLE_STARTED:
519
vm_power_state = power_state.RUNNING
520
elif event.get_transition() == virtevent.EVENT_LIFECYCLE_PAUSED:
521
vm_power_state = power_state.PAUSED
522
elif event.get_transition() == virtevent.EVENT_LIFECYCLE_RESUMED:
523
vm_power_state = power_state.RUNNING
525
LOG.warning(_("Unexpected power state %d") %
526
event.get_transition())
528
if vm_power_state is not None:
529
self._sync_instance_power_state(context,
533
def handle_events(self, event):
534
if isinstance(event, virtevent.LifecycleEvent):
535
self.handle_lifecycle_event(event)
537
LOG.debug(_("Ignoring event %s") % event)
539
def init_virt_events(self):
540
self.driver.register_event_listener(self.handle_events)
509
542
def init_host(self):
510
543
"""Initialization for a standalone compute service."""
511
544
self.driver.init_host(host=self.host)
512
545
context = nova.context.get_admin_context()
513
instances = self._get_instances_at_startup(context)
546
instances = self.conductor_api.instance_get_all_by_host(context,
515
549
if CONF.defer_iptables_apply:
516
550
self.driver.filter_defer_apply_on()
552
self.init_virt_events()
519
555
# checking that instance was not already evacuated to other host
520
556
self._destroy_evacuated_instances(context)
933
994
expected_task_state=(task_states.SCHEDULING,
936
def _allocate_network(self, context, instance, requested_networks):
937
"""Allocate networks for an instance and return the network info"""
938
self._instance_update(context, instance['uuid'],
939
vm_state=vm_states.BUILDING,
940
task_state=task_states.NETWORKING,
941
expected_task_state=None)
942
is_vpn = instance['image_ref'] == str(CONF.vpn_image_id)
997
def _allocate_network(self, context, instance, requested_networks, macs,
999
"""Allocate networks for an instance and return the network info."""
1000
instance = self._instance_update(context, instance['uuid'],
1001
vm_state=vm_states.BUILDING,
1002
task_state=task_states.NETWORKING,
1003
expected_task_state=None)
1004
is_vpn = pipelib.is_vpn_image(instance['image_ref'])
944
1006
# allocate and get network info
945
1007
network_info = self.network_api.allocate_for_instance(
946
1008
context, instance, vpn=is_vpn,
947
requested_networks=requested_networks)
1009
requested_networks=requested_networks,
1011
conductor_api=self.conductor_api,
1012
security_groups=security_groups)
948
1013
except Exception:
949
1014
LOG.exception(_('Instance failed network setup'),
950
1015
instance=instance)
1338
1437
self._notify_about_instance_usage(context, instance,
1339
1438
"rebuild.start", extra_usage_info=extra_usage_info)
1341
current_power_state = self._get_power_state(context, instance)
1342
self._instance_update(context,
1344
power_state=current_power_state,
1345
task_state=task_states.REBUILDING,
1346
expected_task_state=task_states.REBUILDING)
1440
instance = self._instance_update(
1441
context, instance['uuid'],
1442
power_state=self._get_power_state(context, instance),
1443
task_state=task_states.REBUILDING,
1444
expected_task_state=task_states.REBUILDING)
1447
self.network_api.setup_networks_on_host(
1448
context, instance, self.host)
1348
1450
network_info = self._get_instance_nw_info(context, instance)
1349
self.driver.destroy(instance, self._legacy_nw_info(network_info))
1351
instance = self._instance_update(context,
1353
task_state=task_states.
1354
REBUILD_BLOCK_DEVICE_MAPPING,
1355
expected_task_state=task_states.REBUILDING)
1453
bdms = self.conductor_api.\
1454
block_device_mapping_get_all_by_instance(
1457
# NOTE(sirp): this detach is necessary b/c we will reattach the
1458
# volumes in _prep_block_devices below.
1459
for bdm in self._get_volume_bdms(bdms):
1460
volume = self.volume_api.get(context, bdm['volume_id'])
1461
self.volume_api.detach(context, volume)
1464
block_device_info = self._get_volume_block_device_info(
1465
self._get_volume_bdms(bdms))
1466
self.driver.destroy(instance,
1467
self._legacy_nw_info(network_info),
1468
block_device_info=block_device_info)
1470
instance = self._instance_update(
1471
context, instance['uuid'],
1472
task_state=task_states.REBUILD_BLOCK_DEVICE_MAPPING,
1473
expected_task_state=task_states.REBUILDING)
1475
block_device_info = self._prep_block_device(
1476
context, instance, bdms)
1357
1478
instance['injected_files'] = injected_files
1358
network_info = self.network_api.get_instance_nw_info(context,
1361
capi = self.conductor_api
1362
bdms = capi.block_device_mapping_get_all_by_instance(
1364
device_info = self._setup_block_device_mapping(context, instance,
1367
instance = self._instance_update(context,
1369
task_state=task_states.
1371
expected_task_state=task_states.
1372
REBUILD_BLOCK_DEVICE_MAPPING)
1373
# pull in new password here since the original password isn't in
1375
admin_password = new_pass
1480
instance = self._instance_update(
1481
context, instance['uuid'],
1482
task_state=task_states.REBUILD_SPAWNING,
1483
expected_task_state=
1484
task_states.REBUILD_BLOCK_DEVICE_MAPPING)
1377
1486
self.driver.spawn(context, instance, image_meta,
1379
self._legacy_nw_info(network_info),
1382
current_power_state = self._get_power_state(context, instance)
1383
instance = self._instance_update(context,
1385
power_state=current_power_state,
1386
vm_state=vm_states.ACTIVE,
1388
expected_task_state=task_states.
1390
launched_at=timeutils.utcnow())
1488
network_info=self._legacy_nw_info(network_info),
1489
block_device_info=block_device_info)
1491
instance = self._instance_update(
1492
context, instance['uuid'],
1493
power_state=self._get_power_state(context, instance),
1494
vm_state=vm_states.ACTIVE,
1496
expected_task_state=task_states.REBUILD_SPAWNING,
1497
launched_at=timeutils.utcnow())
1499
LOG.info(_("bringing vm to original state: '%s'") % orig_vm_state)
1500
if orig_vm_state == vm_states.STOPPED:
1501
instance = self._instance_update(context, instance['uuid'],
1502
vm_state=vm_states.ACTIVE,
1503
task_state=task_states.STOPPING,
1504
terminated_at=timeutils.utcnow(),
1506
self.stop_instance(context, instance['uuid'])
1392
1508
self._notify_about_instance_usage(
1393
1509
context, instance, "rebuild.end",
1566
1682
context = context.elevated()
1568
1683
if new_pass is None:
1569
1684
# Generate a random password
1570
1685
new_pass = utils.generate_password()
1574
for i in xrange(max_tries):
1575
current_power_state = self._get_power_state(context, instance)
1576
expected_state = power_state.RUNNING
1578
if current_power_state != expected_state:
1579
self._instance_update(context, instance['uuid'],
1581
expected_task_state=task_states.
1583
_msg = _('Failed to set admin password. Instance %s is not'
1584
' running') % instance["uuid"]
1687
current_power_state = self._get_power_state(context, instance)
1688
expected_state = power_state.RUNNING
1690
if current_power_state != expected_state:
1691
self._instance_update(context, instance['uuid'],
1693
expected_task_state=task_states.
1695
_msg = _('Failed to set admin password. Instance %s is not'
1696
' running') % instance["uuid"]
1697
raise exception.InstancePasswordSetFailed(
1698
instance=instance['uuid'], reason=_msg)
1701
self.driver.set_admin_password(instance, new_pass)
1702
LOG.audit(_("Root password set"), instance=instance)
1703
self._instance_update(context,
1706
expected_task_state=task_states.
1708
except NotImplementedError:
1709
_msg = _('set_admin_password is not implemented '
1710
'by this driver or guest instance.')
1711
LOG.warn(_msg, instance=instance)
1712
self._instance_update(context,
1715
expected_task_state=task_states.
1717
raise NotImplementedError(_msg)
1718
except exception.UnexpectedTaskStateError:
1719
# interrupted by another (most likely delete) task
1722
except Exception, e:
1723
# Catch all here because this could be anything.
1724
LOG.exception(_('set_admin_password failed: %s') % e,
1726
self._set_instance_error_state(context,
1728
# We create a new exception here so that we won't
1729
# potentially reveal password information to the
1730
# API caller. The real exception is logged above
1731
_msg = _('error setting admin password')
1585
1732
raise exception.InstancePasswordSetFailed(
1586
instance=instance['uuid'], reason=_msg)
1589
self.driver.set_admin_password(instance, new_pass)
1590
LOG.audit(_("Root password set"), instance=instance)
1591
self._instance_update(context,
1594
expected_task_state=task_states.
1597
except NotImplementedError:
1598
# NOTE(dprince): if the driver doesn't implement
1599
# set_admin_password we break to avoid a loop
1600
_msg = _('set_admin_password is not implemented '
1602
LOG.warn(_msg, instance=instance)
1603
self._instance_update(context,
1606
expected_task_state=task_states.
1608
raise exception.InstancePasswordSetFailed(
1609
instance=instance['uuid'], reason=_msg)
1610
except exception.UnexpectedTaskStateError:
1611
# interrupted by another (most likely delete) task
1614
except Exception, e:
1615
# Catch all here because this could be anything.
1616
LOG.exception(_('set_admin_password failed: %s') % e,
1618
if i == max_tries - 1:
1619
self._set_instance_error_state(context,
1621
# We create a new exception here so that we won't
1622
# potentially reveal password information to the
1623
# API caller. The real exception is logged above
1624
_msg = _('error setting admin password')
1625
raise exception.InstancePasswordSetFailed(
1626
instance=instance['uuid'], reason=_msg)
1733
instance=instance['uuid'], reason=_msg)
1630
1735
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
1631
1736
@reverts_task_state
2384
2536
return connect_info
2538
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
2539
@wrap_instance_fault
2540
def get_spice_console(self, context, console_type, instance):
2541
"""Return connection information for a spice console."""
2542
context = context.elevated()
2543
LOG.debug(_("Getting spice console"), instance=instance)
2544
token = str(uuid.uuid4())
2546
if not CONF.spice.enabled:
2547
raise exception.ConsoleTypeInvalid(console_type=console_type)
2549
if console_type == 'spice-html5':
2550
# For essex, spicehtml5proxy_base_url must include the full path
2551
# including the html file (like http://myhost/spice_auto.html)
2552
access_url = '%s?token=%s' % (CONF.spice.html5proxy_base_url,
2555
raise exception.ConsoleTypeInvalid(console_type=console_type)
2557
# Retrieve connect info from driver, and then decorate with our
2559
connect_info = self.driver.get_spice_console(instance)
2560
connect_info['token'] = token
2561
connect_info['access_url'] = access_url
2565
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
2566
@wrap_instance_fault
2567
def validate_console_port(self, ctxt, instance, port, console_type):
2568
if console_type == "spice-html5":
2569
console_info = self.driver.get_spice_console(instance)
2571
console_info = self.driver.get_vnc_console(instance)
2573
return console_info['port'] == port
2386
2575
def _attach_volume_boot(self, context, instance, volume, mountpoint):
2387
2576
"""Attach a volume to an instance at boot time. So actual attach
2388
2577
is done by instance creation"""
2562
2757
except exception.NotFound:
2760
def attach_interface(self, context, instance, network_id, port_id,
2762
"""Use hotplug to add an network adapter to an instance."""
2763
network_info = self.network_api.allocate_port_for_instance(
2764
context, instance, port_id, network_id, requested_ip,
2766
image_meta = _get_image_meta(context, instance['image_ref'])
2767
legacy_net_info = self._legacy_nw_info(network_info)
2768
for (network, mapping) in legacy_net_info:
2769
if mapping['vif_uuid'] == port_id:
2770
self.driver.attach_interface(instance, image_meta,
2771
[(network, mapping)])
2772
return (network, mapping)
2774
def detach_interface(self, context, instance, port_id):
2775
"""Detach an network adapter from an instance."""
2776
network_info = self.network_api.get_instance_nw_info(
2777
context.elevated(), instance, conductor_api=self.conductor_api)
2778
legacy_nwinfo = self._legacy_nw_info(network_info)
2780
for (network, mapping) in legacy_nwinfo:
2781
if mapping['vif_uuid'] == port_id:
2782
condemned = (network, mapping)
2784
if condemned is None:
2785
raise exception.PortNotFound(_("Port %(port_id)s is not "
2786
"attached") % locals())
2788
self.network_api.deallocate_port_for_instance(context, instance,
2791
self.driver.detach_interface(instance, [condemned])
2565
2793
def _get_compute_info(self, context, host):
2566
compute_node_ref = self.conductor_api.service_get_all_compute_by_host(
2794
compute_node_ref = self.conductor_api.service_get_by_compute_host(
2569
return compute_node_ref[0]['compute_node'][0]
2797
return compute_node_ref['compute_node'][0]
2570
2798
except IndexError:
2571
2799
raise exception.NotFound(_("Host %(host)s not found") % locals())
2811
3036
migration = {'source_compute': instance['host'],
2812
3037
'dest_compute': self.host, }
2813
self.network_api.migrate_instance_finish(context, instance, migration)
3038
self.conductor_api.network_migrate_instance_finish(context,
2815
3042
network_info = self._get_instance_nw_info(context, instance)
3043
block_device_info = self._get_instance_volume_block_device_info(
2816
3046
self.driver.post_live_migration_at_destination(context, instance,
2817
3047
self._legacy_nw_info(network_info),
3048
block_migration, block_device_info)
2819
3049
# Restore instance state
2820
3050
current_power_state = self._get_power_state(context, instance)
2821
self._instance_update(context,
2824
power_state=current_power_state,
2825
vm_state=vm_states.ACTIVE,
2827
expected_task_state=task_states.MIGRATING)
3051
instance = self._instance_update(context, instance['uuid'],
3052
host=self.host, power_state=current_power_state,
3053
vm_state=vm_states.ACTIVE, task_state=None,
3054
expected_task_state=task_states.MIGRATING)
2829
3056
# NOTE(vish): this is necessary to update dhcp
2830
3057
self.network_api.setup_networks_on_host(context, instance, self.host)
2832
def _rollback_live_migration(self, context, instance_ref,
3059
def _rollback_live_migration(self, context, instance,
2833
3060
dest, block_migration, migrate_data=None):
2834
3061
"""Recovers Instance/volume state from migrating -> running.
2836
3063
:param context: security context
2837
:param instance_ref: nova.db.sqlalchemy.models.Instance
3064
:param instance: nova.db.sqlalchemy.models.Instance
2839
3066
This method is called from live migration src host.
2840
3067
This param specifies destination host.
3263
3485
vm_instance = self.driver.get_info(db_instance)
3264
3486
vm_power_state = vm_instance['state']
3265
3487
except exception.InstanceNotFound:
3266
vm_power_state = power_state.SHUTDOWN
3488
vm_power_state = power_state.NOSTATE
3267
3489
# Note(maoy): the above get_info call might take a long time,
3268
3490
# for example, because of a broken libvirt driver.
3269
# We re-query the DB to get the latest instance info to minimize
3270
# (not eliminate) race condition.
3271
u = self.conductor_api.instance_get_by_uuid(context,
3272
db_instance['uuid'])
3273
db_power_state = u["power_state"]
3274
vm_state = u['vm_state']
3275
if self.host != u['host']:
3276
# on the sending end of nova-compute _sync_power_state
3277
# may have yielded to the greenthread performing a live
3278
# migration; this in turn has changed the resident-host
3279
# for the VM; However, the instance is still active, it
3280
# is just in the process of migrating to another host.
3281
# This implies that the compute source must relinquish
3282
# control to the compute destination.
3283
LOG.info(_("During the sync_power process the "
3284
"instance has moved from "
3285
"host %(src)s to host %(dst)s") %
3288
instance=db_instance)
3290
elif u['task_state'] is not None:
3291
# on the receiving end of nova-compute, it could happen
3292
# that the DB instance already report the new resident
3293
# but the actual VM has not showed up on the hypervisor
3294
# yet. In this case, let's allow the loop to continue
3295
# and run the state sync in a later round
3296
LOG.info(_("During sync_power_state the instance has a "
3297
"pending task. Skip."), instance=db_instance)
3299
if vm_power_state != db_power_state:
3300
# power_state is always updated from hypervisor to db
3301
self._instance_update(context,
3302
db_instance['uuid'],
3303
power_state=vm_power_state)
3304
db_power_state = vm_power_state
3305
# Note(maoy): Now resolve the discrepancy between vm_state and
3306
# vm_power_state. We go through all possible vm_states.
3307
if vm_state in (vm_states.BUILDING,
3310
vm_states.SUSPENDED,
3313
# TODO(maoy): we ignore these vm_state for now.
3315
elif vm_state == vm_states.ACTIVE:
3316
# The only rational power state should be RUNNING
3317
if vm_power_state in (power_state.SHUTDOWN,
3491
self._sync_instance_power_state(context,
3495
def _sync_instance_power_state(self, context, db_instance, vm_power_state):
3496
"""Align instance power state between the database and hypervisor.
3498
If the instance is not found on the hypervisor, but is in the database,
3499
then a stop() API will be called on the instance."""
3501
# We re-query the DB to get the latest instance info to minimize
3502
# (not eliminate) race condition.
3503
u = self.conductor_api.instance_get_by_uuid(context,
3504
db_instance['uuid'])
3505
db_power_state = u["power_state"]
3506
vm_state = u['vm_state']
3508
if self.host != u['host']:
3509
# on the sending end of nova-compute _sync_power_state
3510
# may have yielded to the greenthread performing a live
3511
# migration; this in turn has changed the resident-host
3512
# for the VM; However, the instance is still active, it
3513
# is just in the process of migrating to another host.
3514
# This implies that the compute source must relinquish
3515
# control to the compute destination.
3516
LOG.info(_("During the sync_power process the "
3517
"instance has moved from "
3518
"host %(src)s to host %(dst)s") %
3521
instance=db_instance)
3523
elif u['task_state'] is not None:
3524
# on the receiving end of nova-compute, it could happen
3525
# that the DB instance already report the new resident
3526
# but the actual VM has not showed up on the hypervisor
3527
# yet. In this case, let's allow the loop to continue
3528
# and run the state sync in a later round
3529
LOG.info(_("During sync_power_state the instance has a "
3530
"pending task. Skip."), instance=db_instance)
3533
if vm_power_state != db_power_state:
3534
# power_state is always updated from hypervisor to db
3535
self._instance_update(context,
3536
db_instance['uuid'],
3537
power_state=vm_power_state)
3538
db_power_state = vm_power_state
3540
# Note(maoy): Now resolve the discrepancy between vm_state and
3541
# vm_power_state. We go through all possible vm_states.
3542
if vm_state in (vm_states.BUILDING,
3545
vm_states.SUSPENDED,
3548
# TODO(maoy): we ignore these vm_state for now.
3550
elif vm_state == vm_states.ACTIVE:
3551
# The only rational power state should be RUNNING
3552
if vm_power_state in (power_state.SHUTDOWN,
3553
power_state.CRASHED):
3554
LOG.warn(_("Instance shutdown by itself. Calling "
3555
"the stop API."), instance=db_instance)
3557
# Note(maoy): here we call the API instead of
3558
# brutally updating the vm_state in the database
3559
# to allow all the hooks and checks to be performed.
3560
self.conductor_api.compute_stop(context, db_instance)
3562
# Note(maoy): there is no need to propagate the error
3563
# because the same power_state will be retrieved next
3565
# For example, there might be another task scheduled.
3566
LOG.exception(_("error during stop() in "
3567
"sync_power_state."),
3568
instance=db_instance)
3569
elif vm_power_state == power_state.SUSPENDED:
3570
LOG.warn(_("Instance is suspended unexpectedly. Calling "
3571
"the stop API."), instance=db_instance)
3573
self.conductor_api.compute_stop(context, db_instance)
3575
LOG.exception(_("error during stop() in "
3576
"sync_power_state."),
3577
instance=db_instance)
3578
elif vm_power_state == power_state.PAUSED:
3579
# Note(maoy): a VM may get into the paused state not only
3580
# because the user request via API calls, but also
3581
# due to (temporary) external instrumentations.
3582
# Before the virt layer can reliably report the reason,
3583
# we simply ignore the state discrepancy. In many cases,
3584
# the VM state will go back to running after the external
3585
# instrumentation is done. See bug 1097806 for details.
3586
LOG.warn(_("Instance is paused unexpectedly. Ignore."),
3587
instance=db_instance)
3588
elif vm_power_state == power_state.NOSTATE:
3589
# Occasionally, depending on the status of the hypervisor,
3590
# which could be restarting for example, an instance may
3591
# not be found. Therefore just log the condidtion.
3592
LOG.warn(_("Instance is unexpectedly not found. Ignore."),
3593
instance=db_instance)
3594
elif vm_state == vm_states.STOPPED:
3595
if vm_power_state not in (power_state.NOSTATE,
3596
power_state.SHUTDOWN,
3318
3597
power_state.CRASHED):
3319
LOG.warn(_("Instance shutdown by itself. Calling "
3320
"the stop API."), instance=db_instance)
3322
# Note(maoy): here we call the API instead of
3323
# brutally updating the vm_state in the database
3324
# to allow all the hooks and checks to be performed.
3325
self.compute_api.stop(context, db_instance)
3327
# Note(maoy): there is no need to propagate the error
3328
# because the same power_state will be retrieved next
3330
# For example, there might be another task scheduled.
3331
LOG.exception(_("error during stop() in "
3332
"sync_power_state."),
3333
instance=db_instance)
3334
elif vm_power_state in (power_state.PAUSED,
3335
power_state.SUSPENDED):
3336
LOG.warn(_("Instance is paused or suspended "
3337
"unexpectedly. Calling "
3338
"the stop API."), instance=db_instance)
3340
self.compute_api.stop(context, db_instance)
3342
LOG.exception(_("error during stop() in "
3343
"sync_power_state."),
3344
instance=db_instance)
3345
elif vm_state == vm_states.STOPPED:
3346
if vm_power_state not in (power_state.NOSTATE,
3347
power_state.SHUTDOWN,
3348
power_state.CRASHED):
3349
LOG.warn(_("Instance is not stopped. Calling "
3350
"the stop API."), instance=db_instance)
3352
# Note(maoy): this assumes that the stop API is
3354
self.compute_api.stop(context, db_instance)
3356
LOG.exception(_("error during stop() in "
3357
"sync_power_state."),
3358
instance=db_instance)
3359
elif vm_state in (vm_states.SOFT_DELETED,
3361
if vm_power_state not in (power_state.NOSTATE,
3362
power_state.SHUTDOWN):
3363
# Note(maoy): this should be taken care of periodically in
3364
# _cleanup_running_deleted_instances().
3365
LOG.warn(_("Instance is not (soft-)deleted."),
3366
instance=db_instance)
3598
LOG.warn(_("Instance is not stopped. Calling "
3599
"the stop API."), instance=db_instance)
3601
# Note(maoy): this assumes that the stop API is
3603
self.conductor_api.compute_stop(context, db_instance)
3605
LOG.exception(_("error during stop() in "
3606
"sync_power_state."),
3607
instance=db_instance)
3608
elif vm_state in (vm_states.SOFT_DELETED,
3610
if vm_power_state not in (power_state.NOSTATE,
3611
power_state.SHUTDOWN):
3612
# Note(maoy): this should be taken care of periodically in
3613
# _cleanup_running_deleted_instances().
3614
LOG.warn(_("Instance is not (soft-)deleted."),
3615
instance=db_instance)
3368
3617
@manager.periodic_task
3369
3618
def _reclaim_queued_deletes(self, context):