64
68
'Console proxy host to use to connect to instances on'
66
70
flags.DEFINE_integer('live_migration_retry_count', 30,
67
("Retry count needed in live_migration."
68
" sleep 1 sec for each count"))
71
"Retry count needed in live_migration."
72
" sleep 1 sec for each count")
73
flags.DEFINE_integer("rescue_timeout", 0,
74
"Automatically unrescue an instance after N seconds."
75
" Set to 0 to disable.")
70
77
LOG = logging.getLogger('nova.compute.manager')
114
121
# and redocument the module docstring
115
122
if not compute_driver:
116
123
compute_driver = FLAGS.compute_driver
117
self.driver = utils.import_object(compute_driver)
126
self.driver = utils.check_isinstance(
127
utils.import_object(compute_driver),
128
driver.ComputeDriver)
129
except ImportError as e:
130
LOG.error(_("Unable to load the virtualization driver: %s") % (e))
118
133
self.network_manager = utils.import_object(FLAGS.network_manager)
119
134
self.volume_manager = utils.import_object(FLAGS.volume_manager)
120
135
super(ComputeManager, self).__init__(*args, **kwargs)
126
141
self.driver.init_host(host=self.host)
143
def periodic_tasks(self, context=None):
144
"""Tasks to be run at a periodic interval."""
145
super(ComputeManager, self).periodic_tasks(context)
146
if FLAGS.rescue_timeout > 0:
147
self.driver.poll_rescued_instances(FLAGS.rescue_timeout)
128
149
def _update_state(self, context, instance_id):
129
150
"""Update the state of an instance from the driver info."""
130
151
# FIXME(ja): include other fields from state?
220
241
self.db.instance_update(context,
222
243
{'launched_at': now})
223
except Exception: # pylint: disable-msg=W0702
224
LOG.exception(_("instance %s: Failed to spawn"), instance_id,
244
except Exception: # pylint: disable=W0702
245
LOG.exception(_("Instance '%s' failed to spawn. Is virtualization"
246
" enabled in the BIOS?"), instance_id,
226
248
self.db.instance_set_state(context,
228
250
power_state.SHUTDOWN)
429
451
instance_ref = self.db.instance_get(context, instance_id)
430
452
migration_ref = self.db.migration_get(context, migration_id)
432
#TODO(mdietz): we may want to split these into separate methods.
433
if migration_ref['source_compute'] == FLAGS.host:
434
self.driver._start(instance_ref)
435
self.db.migration_update(context, migration_id,
436
{'status': 'reverted'})
438
self.driver.destroy(instance_ref)
439
topic = self.db.queue_get_for(context, FLAGS.compute_topic,
440
instance_ref['host'])
441
rpc.cast(context, topic,
442
{'method': 'revert_resize',
444
'migration_id': migration_ref['id'],
445
'instance_id': instance_id, },
448
@exception.wrap_exception
449
@checks_instance_lock
450
def prep_resize(self, context, instance_id):
454
self.driver.destroy(instance_ref)
455
topic = self.db.queue_get_for(context, FLAGS.compute_topic,
456
instance_ref['host'])
457
rpc.cast(context, topic,
458
{'method': 'finish_revert_resize',
460
'migration_id': migration_ref['id'],
461
'instance_id': instance_id, },
464
@exception.wrap_exception
465
@checks_instance_lock
466
def finish_revert_resize(self, context, instance_id, migration_id):
467
"""Finishes the second half of reverting a resize, powering back on
468
the source instance and reverting the resized attributes in the
470
instance_ref = self.db.instance_get(context, instance_id)
471
migration_ref = self.db.migration_get(context, migration_id)
472
instance_type = self.db.instance_type_get_by_flavor_id(context,
473
migration_ref['old_flavor_id'])
475
# Just roll back the record. There's no need to resize down since
476
# the 'old' VM already has the preferred attributes
477
self.db.instance_update(context, instance_id,
478
dict(memory_mb=instance_type['memory_mb'],
479
vcpus=instance_type['vcpus'],
480
local_gb=instance_type['local_gb']))
482
self.driver.revert_resize(instance_ref)
483
self.db.migration_update(context, migration_id,
484
{'status': 'reverted'})
486
@exception.wrap_exception
487
@checks_instance_lock
488
def prep_resize(self, context, instance_id, flavor_id):
451
489
"""Initiates the process of moving a running instance to another
452
490
host, possibly changing the RAM and disk size in the process"""
453
491
context = context.elevated()
456
494
raise exception.Error(_(
457
495
'Migration error: destination same as source!'))
497
instance_type = self.db.instance_type_get_by_flavor_id(context,
459
499
migration_ref = self.db.migration_create(context,
460
500
{'instance_id': instance_id,
461
501
'source_compute': instance_ref['host'],
462
502
'dest_compute': FLAGS.host,
463
503
'dest_host': self.driver.get_host_ip_addr(),
504
'old_flavor_id': instance_type['flavorid'],
505
'new_flavor_id': flavor_id,
464
506
'status': 'pre-migrating'})
465
508
LOG.audit(_('instance %s: migrating to '), instance_id,
467
510
topic = self.db.queue_get_for(context, FLAGS.compute_topic,
487
530
self.db.migration_update(context, migration_id,
488
531
{'status': 'post-migrating', })
490
#TODO(mdietz): This is where we would update the VM record
492
533
service = self.db.service_get_by_host_and_topic(context,
493
534
migration_ref['dest_compute'], FLAGS.compute_topic)
494
535
topic = self.db.queue_get_for(context, FLAGS.compute_topic,
509
550
migration_ref = self.db.migration_get(context, migration_id)
510
551
instance_ref = self.db.instance_get(context,
511
552
migration_ref['instance_id'])
553
# TODO(mdietz): apply the rest of the instance_type attributes going
554
# after they're supported
555
instance_type = self.db.instance_type_get_by_flavor_id(context,
556
migration_ref['new_flavor_id'])
557
self.db.instance_update(context, instance_id,
558
dict(instance_type=instance_type['name'],
559
memory_mb=instance_type['memory_mb'],
560
vcpus=instance_type['vcpus'],
561
local_gb=instance_type['local_gb']))
563
# reload the updated instance ref
564
# FIXME(mdietz): is there reload functionality?
565
instance_ref = self.db.instance_get(context, instance_id)
513
566
self.driver.finish_resize(instance_ref, disk_info)
515
568
self.db.migration_update(context, migration_id,
793
846
return self.driver.update_available_resource(context, self.host)
795
def pre_live_migration(self, context, instance_id):
848
def pre_live_migration(self, context, instance_id, time=None):
796
849
"""Preparations for live migration at dest host.
798
851
:param context: security context
969
1025
for volume in instance_ref['volumes']:
970
1026
self.db.volume_update(ctxt, volume['id'], {'status': 'in-use'})
1028
def periodic_tasks(self, context=None):
1029
"""Tasks to be run at a periodic interval."""
1030
error_list = super(ComputeManager, self).periodic_tasks(context)
1031
if error_list is None:
1035
self._poll_instance_states(context)
1036
except Exception as ex:
1037
LOG.warning(_("Error during instance poll: %s"),
1039
error_list.append(ex)
1042
def _poll_instance_states(self, context):
1043
vm_instances = self.driver.list_instances_detail()
1044
vm_instances = dict((vm.name, vm) for vm in vm_instances)
1046
# Keep a list of VMs not in the DB, cross them off as we find them
1047
vms_not_found_in_db = list(vm_instances.keys())
1049
db_instances = self.db.instance_get_all_by_host(context, self.host)
1051
for db_instance in db_instances:
1052
name = db_instance['name']
1053
vm_instance = vm_instances.get(name)
1054
if vm_instance is None:
1055
LOG.info(_("Found instance '%(name)s' in DB but no VM. "
1056
"Setting state to shutoff.") % locals())
1057
vm_state = power_state.SHUTOFF
1059
vm_state = vm_instance.state
1060
vms_not_found_in_db.remove(name)
1062
db_state = db_instance['state']
1063
if vm_state != db_state:
1064
LOG.info(_("DB/VM state mismatch. Changing state from "
1065
"'%(db_state)s' to '%(vm_state)s'") % locals())
1066
self.db.instance_set_state(context,
1070
if vm_state == power_state.SHUTOFF:
1071
# TODO(soren): This is what the compute manager does when you
1072
# terminate an instance. At some point I figure we'll have a
1073
# "terminated" state and some sort of cleanup job that runs
1074
# occasionally, cleaning them out.
1075
self.db.instance_destroy(context, db_instance['id'])
1077
# Are there VMs not in the DB?
1078
for vm_not_found_in_db in vms_not_found_in_db:
1079
name = vm_not_found_in_db
1080
# TODO(justinsb): What to do here? Adopt it? Shut it down?
1081
LOG.warning(_("Found VM not in DB: '%(name)s'. Ignoring")