refactoring compute.api.create()

This commit is contained in:
Sandy Walsh
2011-05-26 08:28:57 -07:00
parent 660d1802a6
commit bc176751de
4 changed files with 135 additions and 75 deletions

View File

@@ -54,7 +54,11 @@ def generate_default_hostname(instance_id):
class API(base.Base): class API(base.Base):
"""API for interacting with the compute manager.""" """API for interacting with the compute manager."""
scheduler_rules = None
# Should we create instances all-at-once or as single-shot requests.
# Different schedulers use different approaches.
# This is cached across all API instances.
should_create_all_at_once = None # None implies uninitialized.
def __init__(self, image_service=None, network_api=None, def __init__(self, image_service=None, network_api=None,
volume_api=None, hostname_factory=generate_default_hostname, volume_api=None, hostname_factory=generate_default_hostname,
@@ -219,48 +223,15 @@ class API(base.Base):
'availability_zone': availability_zone, 'availability_zone': availability_zone,
'os_type': os_type} 'os_type': os_type}
return (num_instances, base_options) return (num_instances, base_options, security_groups)
def create_all_at_once(self, context, instance_type, def create_db_entry_for_new_instance(self, context, base_options,
image_id, kernel_id=None, ramdisk_id=None, security_groups, num=1):
min_count=1, max_count=1, """Create an entry in the DB for this new instance,
display_name='', display_description='', including any related table updates (such as security
key_name=None, key_data=None, security_group='default', groups, MAC address, etc). This will called by create()
availability_zone=None, user_data=None, metadata={}, in the majority of situations, but all-at-once style
injected_files=None, zone_blob=None): Schedulers may initiate the call."""
"""Provision the instances by passing the whole request to
the Scheduler for execution."""
self._check_create_parameters(self, context, instance_type,
image_id, kernel_id, ramdisk_id, min_count=1, max_count=1,
display_name, display_description,
key_name, key_data, security_group,
availability_zone, user_data, metadata,
injected_files, zone_blob)
def create(self, context, instance_type,
image_id, kernel_id=None, ramdisk_id=None,
min_count=1, max_count=1,
display_name='', display_description='',
key_name=None, key_data=None, security_group='default',
availability_zone=None, user_data=None, metadata={},
injected_files=None, zone_blob=None):
"""Provision the instances by sending off a series of single
instance requests to the Schedulers. This is fine for trival
Scheduler drivers, but may remove the effectiveness of the
more complicated drivers."""
num_instances, base_options = self._check_create_parameters(
context, instance_type,
image_id, kernel_id, ramdisk_id,
min_count=1, max_count=1,
display_name, display_description,
key_name, key_data, security_group,
availability_zone, user_data, metadata,
injected_files, zone_blob)
instances = []
LOG.debug(_("Going to run %s instances..."), num_instances)
for num in range(num_instances):
instance = dict(mac_address=utils.generate_mac(), instance = dict(mac_address=utils.generate_mac(),
launch_index=num, launch_index=num,
**base_options) **base_options)
@@ -282,24 +253,33 @@ class API(base.Base):
updates['display_name'] = "Server %s" % instance_id updates['display_name'] = "Server %s" % instance_id
instance = self.update(context, instance_id, **updates) instance = self.update(context, instance_id, **updates)
instances.append(instance)
for group_id in security_groups:
self.trigger_security_group_members_refresh(elevated, group_id)
return instance
def _ask_scheduler_to_create_instance(self, context, base_options,
instance_type, zone_blob,
availability_zone, injected_files,
instance_id=None, num_instances=1):
"""Send the run_instance request to the schedulers for processing."""
pid = context.project_id pid = context.project_id
uid = context.user_id uid = context.user_id
if instance_id:
LOG.debug(_("Casting to scheduler for %(pid)s/%(uid)s's" LOG.debug(_("Casting to scheduler for %(pid)s/%(uid)s's"
" instance %(instance_id)s") % locals()) " instance %(instance_id)s (single-shot)") % locals())
else:
LOG.debug(_("Casting to scheduler for %(pid)s/%(uid)s's"
" (all-at-once)") % locals())
# NOTE(sandy): For now we're just going to pass in the filter_class = 'nova.scheduler.host_filter.InstanceTypeFilter'
# instance_type record to the scheduler. In a later phase
# we'll be ripping this whole for-loop out and deferring the
# creation of the Instance record. At that point all this will
# change.
filter_driver = 'nova.scheduler.host_filter.InstanceTypeFilter'
request_spec = { request_spec = {
'instance_properties': base_options, 'instance_properties': base_options,
'instance_type': instance_type, 'instance_type': instance_type,
'filter_driver': filter_driver, 'filter': filter_class,
'blob': zone_blob 'blob': zone_blob,
'num_instances': num_instances
} }
rpc.cast(context, rpc.cast(context,
@@ -311,24 +291,87 @@ class API(base.Base):
"availability_zone": availability_zone, "availability_zone": availability_zone,
"injected_files": injected_files}}) "injected_files": injected_files}})
for group_id in security_groups: def create_all_at_once(self, context, instance_type,
self.trigger_security_group_members_refresh(elevated, group_id) image_id, kernel_id=None, ramdisk_id=None,
min_count=1, max_count=1,
display_name='', display_description='',
key_name=None, key_data=None, security_group='default',
availability_zone=None, user_data=None, metadata={},
injected_files=None, zone_blob=None):
"""Provision the instances by passing the whole request to
the Scheduler for execution. Returns a Reservation ID
related to the creation of all of these instances."""
num_instances, base_options, security_groups = \
self._check_create_parameters(
context, instance_type,
image_id, kernel_id, ramdisk_id,
min_count, max_count,
display_name, display_description,
key_name, key_data, security_group,
availability_zone, user_data, metadata,
injected_files, zone_blob)
return [dict(x.iteritems()) for x in instances] self._ask_scheduler_to_create_instance(context, base_options,
instance_type, zone_blob,
availability_zone, injected_files,
num_instances=num_instances)
return base_options['reservation_id']
def create(self, context, instance_type,
image_id, kernel_id=None, ramdisk_id=None,
min_count=1, max_count=1,
display_name='', display_description='',
key_name=None, key_data=None, security_group='default',
availability_zone=None, user_data=None, metadata={},
injected_files=None, zone_blob=None):
"""
Provision the instances by sending off a series of single
instance requests to the Schedulers. This is fine for trival
Scheduler drivers, but may remove the effectiveness of the
more complicated drivers.
Returns a list of instance dicts.
"""
num_instances, base_options, security_groups = \
self._check_create_parameters(
context, instance_type,
image_id, kernel_id, ramdisk_id,
min_count, max_count,
display_name, display_description,
key_name, key_data, security_group,
availability_zone, user_data, metadata,
injected_files, zone_blob)
instances = []
LOG.debug(_("Going to run %s instances..."), num_instances)
for num in range(num_instances):
instance = self.create_db_entry_for_new_instance(context,
base_options, security_groups, num=num)
instances.append(instance)
instance_id = instance['id']
self._ask_scheduler_to_create_instance(context, base_options,
instance_type, zone_blob,
availability_zone, injected_files,
instance_id=instance_id)
return [x.items() for x in instances]
def smart_create(self, *args, **kwargs): def smart_create(self, *args, **kwargs):
"""Ask the scheduler if we should: 1. do single shot instance """
requests or all-at-once, and 2. defer the DB work until Ask the scheduler if we should do single shot instance requests
a suitable host has been selected (if at all). Cache this or all-at-once.
information and act accordingly."""
if API.scheduler_rules == None: Cache this information on first request and act accordingly.
API.scheduler_rules = scheduler_api.get_scheduler_rules(context) """
should_create_all_at_once, should_defer_database_create = \ if API.should_create_all_at_once == None:
API.scheduler_rules API.should_create_all_at_once = \
scheduler_api.should_create_all_at_once(context)
if should_create_all_at_once: if API.should_create_all_at_once:
return self.create_all_at_once(*args, **kwargs) return self.create_all_at_once(*args, **kwargs)
return self.create(*args, **kwargs) return self.create(*args, **kwargs)

View File

@@ -72,6 +72,13 @@ class Scheduler(object):
for service in services for service in services
if self.service_is_up(service)] if self.service_is_up(service)]
def should_create_all_at_once(self, context=None, *args, **kwargs):
"""
Does this driver prefer single-shot requests or all-at-once?
By default, prefer single-shot.
"""
return False
def schedule(self, context, topic, *_args, **_kwargs): def schedule(self, context, topic, *_args, **_kwargs):
"""Must override at least this method for scheduler to work.""" """Must override at least this method for scheduler to work."""
raise NotImplementedError(_("Must implement a fallback schedule")) raise NotImplementedError(_("Must implement a fallback schedule"))

View File

@@ -74,6 +74,10 @@ class SchedulerManager(manager.Manager):
"""Select a list of hosts best matching the provided specs.""" """Select a list of hosts best matching the provided specs."""
return self.driver.select(context, *args, **kwargs) return self.driver.select(context, *args, **kwargs)
def get_scheduler_rules(self, context=None, *args, **kwargs):
"""Ask the driver how requests should be made of it."""
return self.driver.get_scheduler_rules(context, *args, **kwargs)
def _schedule(self, method, context, topic, *args, **kwargs): def _schedule(self, method, context, topic, *args, **kwargs):
"""Tries to call schedule_* method on the driver to retrieve host. """Tries to call schedule_* method on the driver to retrieve host.

View File

@@ -157,6 +157,12 @@ class ZoneAwareScheduler(driver.Scheduler):
self._provision_resource_from_blob(context, item, instance_id, self._provision_resource_from_blob(context, item, instance_id,
request_spec, kwargs) request_spec, kwargs)
def should_create_all_at_once(self, context=None, *args, **kwargs):
"""
This driver prefers all-at-once requests.
"""
return True
def schedule_run_instance(self, context, instance_id, request_spec, def schedule_run_instance(self, context, instance_id, request_spec,
*args, **kwargs): *args, **kwargs):
"""This method is called from nova.compute.api to provision """This method is called from nova.compute.api to provision