summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Behrens <cbehrens@codestud.com>2011-11-02 10:58:45 -0700
committerChris Behrens <cbehrens@codestud.com>2011-11-02 11:55:29 -0700
commit057138540df2b067bd3cd2857cceb97d1ecd86d0 (patch)
tree55ff978831ae2c568b5d787be15718a0870dfc92
parent830760b4c79cf9cdc80c6e0047ea206abc21f2c6 (diff)
APIs should not wait on scheduler for builds in single zone deployment
Fixes bug 885349 We can short circuit waiting on the scheduler if we're in a single zone deployment and only building 1 instance. This patch checks for that case and creates the instance DB entry directly in the API (in compute/api) without the call to the scheduler. Change-Id: I98b27156167f057d11fbc56a9ff99d4e2ec423d3
-rw-r--r--nova/api/ec2/cloud.py5
-rw-r--r--nova/api/openstack/servers.py3
-rw-r--r--nova/compute/api.py35
-rw-r--r--nova/scheduler/driver.py3
-rw-r--r--nova/tests/api/ec2/test_cloud.py5
-rw-r--r--nova/tests/api/openstack/test_servers.py8
-rw-r--r--nova/tests/test_compute.py19
7 files changed, 38 insertions, 40 deletions
diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py
index 16ea74025..b2aa120e6 100644
--- a/nova/api/ec2/cloud.py
+++ b/nova/api/ec2/cloud.py
@@ -1437,10 +1437,7 @@ class CloudController(object):
security_group=kwargs.get('security_group'),
availability_zone=kwargs.get('placement', {}).get(
'AvailabilityZone'),
- block_device_mapping=kwargs.get('block_device_mapping', {}),
- # NOTE(comstud): Unfortunately, EC2 requires that the
- # instance DB entries have been created..
- wait_for_instances=True)
+ block_device_mapping=kwargs.get('block_device_mapping', {}))
return self._format_run_instances(context, resv_id)
def _do_instance(self, action, context, ec2_id):
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index 8c1eaae9d..b71e5df31 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -462,8 +462,7 @@ class Controller(object):
user_data=user_data,
availability_zone=availability_zone,
config_drive=config_drive,
- block_device_mapping=block_device_mapping,
- wait_for_instances=not ret_resv_id)
+ block_device_mapping=block_device_mapping)
except quota.QuotaError as error:
self._handle_quota_error(error)
except exception.InstanceTypeMemoryTooSmall as error:
diff --git a/nova/compute/api.py b/nova/compute/api.py
index 436dc79b5..b6ba86eb3 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -45,6 +45,7 @@ LOG = logging.getLogger('nova.compute.api')
FLAGS = flags.FLAGS
+flags.DECLARE('enable_zone_routing', 'nova.scheduler.api')
flags.DECLARE('vncproxy_topic', 'nova.vnc')
flags.DEFINE_integer('find_host_timeout', 30,
'Timeout after NN seconds when looking for a host.')
@@ -189,8 +190,7 @@ class API(base.Base):
injected_files, admin_password, zone_blob,
reservation_id, access_ip_v4, access_ip_v6,
requested_networks, config_drive,
- block_device_mapping,
- wait_for_instances):
+ block_device_mapping, create_instance_here=False):
"""Verify all the input parameters regardless of the provisioning
strategy being performed and schedule the instance(s) for
creation."""
@@ -325,10 +325,18 @@ class API(base.Base):
LOG.debug(_("Going to run %s instances...") % num_instances)
- if wait_for_instances:
- rpc_method = rpc.call
- else:
+ if create_instance_here:
+ instance = self.create_db_entry_for_new_instance(
+ context, instance_type, image, base_options,
+ security_group, block_device_mapping)
+ # Tells scheduler we created the instance already.
+ base_options['id'] = instance['id']
rpc_method = rpc.cast
+ else:
+ # We need to wait for the scheduler to create the instance
+ # DB entries, because the instance *could* be # created in
+ # a child zone.
+ rpc_method = rpc.call
# TODO(comstud): We should use rpc.multicall when we can
# retrieve the full instance dictionary from the scheduler.
@@ -344,6 +352,8 @@ class API(base.Base):
num_instances, requested_networks,
block_device_mapping, security_group)
+ if create_instance_here:
+ return ([instance], reservation_id)
return (instances, reservation_id)
@staticmethod
@@ -534,8 +544,7 @@ class API(base.Base):
injected_files=None, admin_password=None, zone_blob=None,
reservation_id=None, block_device_mapping=None,
access_ip_v4=None, access_ip_v6=None,
- requested_networks=None, config_drive=None,
- wait_for_instances=True):
+ requested_networks=None, config_drive=None):
"""
Provision instances, sending instance information to the
scheduler. The scheduler will determine where the instance(s)
@@ -546,6 +555,13 @@ class API(base.Base):
we waited for information from the scheduler or not.
"""
+ # We can create the DB entry for the instance here if we're
+ # only going to create 1 instance and we're in a single
+ # zone deployment. This speeds up API responses for builds
+ # as we don't need to wait for the scheduler.
+ create_instance_here = (max_count == 1 and
+ not FLAGS.enable_zone_routing)
+
(instances, reservation_id) = self._create_instance(
context, instance_type,
image_href, kernel_id, ramdisk_id,
@@ -557,10 +573,9 @@ class API(base.Base):
reservation_id, access_ip_v4, access_ip_v6,
requested_networks, config_drive,
block_device_mapping,
- wait_for_instances)
+ create_instance_here=create_instance_here)
- if instances is None:
- # wait_for_instances must have been False
+ if create_instance_here or instances is None:
return (instances, reservation_id)
inst_ret_list = []
diff --git a/nova/scheduler/driver.py b/nova/scheduler/driver.py
index 7c79d28c9..af39b2edb 100644
--- a/nova/scheduler/driver.py
+++ b/nova/scheduler/driver.py
@@ -155,6 +155,9 @@ class Scheduler(object):
def create_instance_db_entry(self, context, request_spec):
"""Create instance DB entry based on request_spec"""
base_options = request_spec['instance_properties']
+ if base_options.get('id'):
+ # Instance was already created before calling scheduler
+ return db.instance_get(context, base_options['id'])
image = request_spec['image']
instance_type = request_spec.get('instance_type')
security_group = request_spec.get('security_group', 'default')
diff --git a/nova/tests/api/ec2/test_cloud.py b/nova/tests/api/ec2/test_cloud.py
index c0f3d44d7..f76690831 100644
--- a/nova/tests/api/ec2/test_cloud.py
+++ b/nova/tests/api/ec2/test_cloud.py
@@ -1013,8 +1013,6 @@ class CloudTestCase(test.TestCase):
self.assertDictListUnorderedMatch(result['blockDeviceMapping'],
self._expected_bdms2, 'deviceName')
- self.stubs.UnsetAll()
-
def test_describe_image_attribute(self):
describe_image_attribute = self.cloud.describe_image_attribute
@@ -1216,6 +1214,9 @@ class CloudTestCase(test.TestCase):
self.stubs.UnsetAll()
self.stubs.Set(fake._FakeImageService, 'show', fake_show)
+ # NOTE(comstud): Make 'cast' behave like a 'call' which will
+ # ensure that operations complete
+ self.stubs.Set(rpc, 'cast', rpc.call)
result = run_instances(self.context, **kwargs)
instance = result['instancesSet'][0]
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index 4518ef8e2..31c87e630 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -1354,7 +1354,7 @@ class ServersControllerCreateTest(test.TestCase):
self.instance_cache_num += 1
instance = {
'id': self.instance_cache_num,
- 'display_name': 'server_test',
+ 'display_name': inst['display_name'] or 'test',
'uuid': FAKE_UUID,
'instance_type': dict(inst_type),
'access_ip_v4': '1.2.3.4',
@@ -1390,8 +1390,10 @@ class ServersControllerCreateTest(test.TestCase):
request_spec['instance_properties']))
return instances
- def server_update(context, id, params):
- return instance_create(context, id)
+ def server_update(context, instance_id, params):
+ inst = self.instance_cache[instance_id]
+ inst.update(params)
+ return inst
def fake_method(*args, **kwargs):
pass
diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py
index addb6084d..7295c9f1f 100644
--- a/nova/tests/test_compute.py
+++ b/nova/tests/test_compute.py
@@ -1409,25 +1409,6 @@ class ComputeTestCase(test.TestCase):
self.assertEqual(instance['reservation_id'], resv_id)
db.instance_destroy(self.context, instance['id'])
- def test_reservation_ids_two_instances_no_wait(self):
- """Verify building 2 instances at once without waiting for
- instance IDs results in a reservation_id being returned equal
- to reservation id set in both instances
- """
- (refs, resv_id) = self.compute_api.create(self.context,
- instance_types.get_default_instance_type(), None,
- min_count=2, max_count=2, wait_for_instances=False)
- try:
- self.assertEqual(refs, None)
- self.assertNotEqual(resv_id, None)
- finally:
- instances = self.compute_api.get_all(self.context,
- search_opts={'reservation_id': resv_id})
- self.assertEqual(len(instances), 2)
- for instance in instances:
- self.assertEqual(instance['reservation_id'], resv_id)
- db.instance_destroy(self.context, instance['id'])
-
def test_create_with_specified_reservation_id(self):
"""Verify building instances with a specified
reservation_id results in the correct reservation_id