102
102
if instance['locked'] and not context.is_admin:
103
103
raise exception.InstanceIsLocked(instance_uuid=instance['uuid'])
104
104
# NOTE(danms): at this point, we have verified that either
105
# theinstance is not locked, or the user is suffiently endowed
105
# the instance is not locked, or the user is sufficiently endowed
106
106
# that it doesn't matter. While the following statement may be
107
107
# interpreted as the "the instance is not locked", it actually
108
108
# refers to the whole condition.
373
373
access_ip_v4, access_ip_v6,
374
374
requested_networks, config_drive,
375
375
block_device_mapping, auto_disk_config,
376
reservation_id=None, create_instance_here=False,
377
scheduler_hints=None):
376
reservation_id=None, scheduler_hints=None):
378
377
"""Verify all the input parameters regardless of the provisioning
379
378
strategy being performed and schedule the instance(s) for
496
495
LOG.debug(_("Going to run %s instances...") % num_instances)
498
if create_instance_here:
499
instance = self.create_db_entry_for_new_instance(
500
context, instance_type, image, base_options,
501
security_group, block_device_mapping,
504
# Reservations committed; don't double-commit
505
quota_reservations = None
507
# Tells scheduler we created the instance already.
508
base_options['uuid'] = instance['uuid']
511
# We need to wait for the scheduler to create the instance
512
# DB entries, because the instance *could* be # created in
516
497
filter_properties = dict(scheduler_hints=scheduler_hints)
517
498
if context.is_admin and forced_host:
518
499
filter_properties['force_hosts'] = [forced_host]
520
# TODO(comstud): We should use rpc.multicall when we can
521
# retrieve the full instance dictionary from the scheduler.
522
# Otherwise, we could exceed the AMQP max message size limit.
523
# This would require the schedulers' schedule_run_instances
524
# methods to return an iterator vs a list.
525
instances = self._schedule_run_instance(
527
context, base_options,
529
availability_zone, injected_files,
530
admin_password, image,
531
num_instances, requested_networks,
532
block_device_mapping, security_group,
533
filter_properties, quota_reservations)
535
if create_instance_here:
536
return ([instance], reservation_id)
504
for i in xrange(num_instances):
505
options = base_options.copy()
506
instance = self.create_db_entry_for_new_instance(
507
context, instance_type, image, options,
508
security_group, block_device_mapping)
509
instances.append(instance)
510
instance_uuids.append(instance['uuid'])
512
# Clean up as best we can.
513
with excutils.save_and_reraise_exception():
515
for instance_uuid in instance_uuids:
516
self.db.instance_destroy(context,
519
QUOTAS.rollback(context, quota_reservations)
521
# Commit the reservations
522
QUOTAS.commit(context, quota_reservations)
525
'image': jsonutils.to_primitive(image),
526
'instance_properties': base_options,
527
'instance_type': instance_type,
528
'instance_uuids': instance_uuids,
529
'block_device_mapping': block_device_mapping,
530
'security_group': security_group,
533
self.scheduler_rpcapi.run_instance(context,
534
request_spec=request_spec,
535
admin_password=admin_password, injected_files=injected_files,
536
requested_networks=requested_networks, is_first_time=True,
537
filter_properties=filter_properties)
537
539
return (instances, reservation_id)
698
700
#NOTE(bcwaldon): No policy check since this is only used by scheduler and
699
701
# the compute api. That should probably be cleaned up, though.
700
702
def create_db_entry_for_new_instance(self, context, instance_type, image,
701
base_options, security_group, block_device_mapping, reservations):
703
base_options, security_group, block_device_mapping):
702
704
"""Create an entry in the DB for this new instance,
703
705
including any related table updates (such as security group,
724
726
notifications.send_update_with_states(context, instance, None,
725
727
vm_states.BUILDING, None, None, service="api")
727
# Commit the reservations
729
QUOTAS.commit(context, reservations)
733
def _schedule_run_instance(self,
735
context, base_options,
737
availability_zone, injected_files,
738
admin_password, image,
741
block_device_mapping,
745
"""Send a run_instance request to the schedulers for processing."""
747
pid = context.project_id
748
uid = context.user_id
750
LOG.debug(_("Sending create to scheduler for %(pid)s/%(uid)s's") %
754
'image': jsonutils.to_primitive(image),
755
'instance_properties': base_options,
756
'instance_type': instance_type,
757
'num_instances': num_instances,
758
'block_device_mapping': block_device_mapping,
759
'security_group': security_group,
762
return self.scheduler_rpcapi.run_instance(context,
763
request_spec=request_spec,
764
admin_password=admin_password, injected_files=injected_files,
765
requested_networks=requested_networks, is_first_time=True,
766
filter_properties=filter_properties,
767
reservations=quota_reservations, call=use_call)
769
731
def _check_create_policies(self, context, availability_zone,
770
732
requested_networks, block_device_mapping):
771
733
"""Check policies for create()."""
795
757
scheduler. The scheduler will determine where the instance(s)
796
758
go and will handle creating the DB entries.
798
Returns a tuple of (instances, reservation_id) where instances
799
could be 'None' or a list of instance dicts depending on if
800
we waited for information from the scheduler or not.
760
Returns a tuple of (instances, reservation_id)
803
763
self._check_create_policies(context, availability_zone,
804
764
requested_networks, block_device_mapping)
806
# We can create the DB entry for the instance here if we're
807
# only going to create 1 instance.
808
# This speeds up API responses for builds
809
# as we don't need to wait for the scheduler.
810
create_instance_here = max_count == 1 or max_count is None
812
(instances, reservation_id) = self._create_instance(
766
return self._create_instance(
813
767
context, instance_type,
814
768
image_href, kernel_id, ramdisk_id,
815
769
min_count, max_count,
820
774
access_ip_v4, access_ip_v6,
821
775
requested_networks, config_drive,
822
776
block_device_mapping, auto_disk_config,
823
create_instance_here=create_instance_here,
824
777
scheduler_hints=scheduler_hints)
826
if create_instance_here or instances is None:
827
return (instances, reservation_id)
830
for instance in instances:
831
if instance.get('_is_precooked', False):
832
inst_ret_list.append(instance)
834
# Scheduler only gives us the 'id'. We need to pull
835
# in the created instances from the DB
836
instance = self.db.instance_get(context, instance['id'])
837
inst_ret_list.append(dict(instance.iteritems()))
839
return (inst_ret_list, reservation_id)
841
779
def trigger_provider_fw_rules_refresh(self, context):
842
780
"""Called when a rule is added/removed from a provider firewall"""
1783
1724
def detach_volume(self, context, volume_id):
1784
1725
"""Detach a volume from an instance."""
1785
1726
volume = self.volume_api.get(context, volume_id)
1727
if volume['attach_status'] == 'detached':
1728
msg = _("Volume must be attached in order to detach.")
1729
raise exception.InvalidVolume(reason=msg)
1786
1731
instance_uuid = volume['instance_uuid']
1787
1732
instance = self.db.instance_get_by_uuid(context.elevated(),
1823
1770
_metadata.update(metadata)
1825
1772
self._check_metadata_properties_quota(context, _metadata)
1826
self.db.instance_metadata_update(context, instance['uuid'],
1773
metadata = self.db.instance_metadata_update(context, instance['uuid'],
1827
1774
_metadata, True)
1775
instance['metadata'] = metadata
1776
notifications.send_update(context, instance, instance)
1828
1777
diff = utils.diff_dict(orig, _metadata)
1829
1778
self.compute_rpcapi.change_instance_metadata(context,
1830
1779
instance=instance,