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)
downloadnova-057138540df2b067bd3cd2857cceb97d1ecd86d0.tar.gz
nova-057138540df2b067bd3cd2857cceb97d1ecd86d0.tar.xz
nova-057138540df2b067bd3cd2857cceb97d1ecd86d0.zip
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