summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoshua Harlow <harlowja@yahoo-inc.com>2013-05-08 15:47:47 -0700
committerPhil Day <philip.day@hp.com>2013-06-26 21:08:13 +0100
commit5ff0278561d4008bc710440129f4a7b5c38cdd98 (patch)
treecd3ac4c33477a1ed089822514c07736d84bded85
parent7696c3c11f0de855cbc53cc04ee7d2be07ae3b9c (diff)
downloadnova-5ff0278561d4008bc710440129f4a7b5c38cdd98.tar.gz
nova-5ff0278561d4008bc710440129f4a7b5c38cdd98.tar.xz
nova-5ff0278561d4008bc710440129f4a7b5c38cdd98.zip
Report the az based on the value in the instance table.
When attempting to find the availability zone of an instance do so by first trying to locate the availability zone of the host the instance is active on. If that fails then attempt to use the value that is in the instances 'availability_zone' key instead. For the caching used in the availability_zones module there was a new method added to get and reset the cache that is useful for unit tests, since during each run of a test you do not want to be affected by something in cache from a previous test. Fixes bug 1172246 Change-Id: I6f54a44cc87434120656ccc789cebcc08d434418
-rw-r--r--nova/api/openstack/compute/contrib/extended_availability_zone.py11
-rw-r--r--nova/availability_zones.py40
-rw-r--r--nova/tests/api/openstack/compute/contrib/test_availability_zone.py1
-rw-r--r--nova/tests/api/openstack/compute/contrib/test_extended_availability_zone.py51
-rw-r--r--nova/tests/api/openstack/fakes.py5
5 files changed, 94 insertions, 14 deletions
diff --git a/nova/api/openstack/compute/contrib/extended_availability_zone.py b/nova/api/openstack/compute/contrib/extended_availability_zone.py
index 00765e209..6e77cd6bd 100644
--- a/nova/api/openstack/compute/contrib/extended_availability_zone.py
+++ b/nova/api/openstack/compute/contrib/extended_availability_zone.py
@@ -20,7 +20,7 @@
from nova.api.openstack import extensions
from nova.api.openstack import wsgi
from nova.api.openstack import xmlutil
-from nova import availability_zones
+from nova import availability_zones as avail_zone
authorize = extensions.soft_extension_authorizer('compute',
'extended_availability_zone')
@@ -29,8 +29,13 @@ authorize = extensions.soft_extension_authorizer('compute',
class ExtendedAZController(wsgi.Controller):
def _extend_server(self, context, server, instance):
key = "%s:availability_zone" % Extended_availability_zone.alias
- server[key] = availability_zones.get_instance_availability_zone(
- context, instance)
+ az = avail_zone.get_instance_availability_zone(context, instance)
+ if not az and instance.get('availability_zone'):
+ # Likely hasn't reached a viable compute node yet so give back the
+ # desired availability_zone that *may* exist in the instance
+ # record itself.
+ az = instance['availability_zone']
+ server[key] = az
@wsgi.extends
def show(self, req, resp_obj, id):
diff --git a/nova/availability_zones.py b/nova/availability_zones.py
index 6d111c120..216c46041 100644
--- a/nova/availability_zones.py
+++ b/nova/availability_zones.py
@@ -23,7 +23,7 @@ from nova.openstack.common import memorycache
# NOTE(vish): azs don't change that often, so cache them for an hour to
# avoid hitting the db multiple times on every request.
AZ_CACHE_SECONDS = 60 * 60
-MC = memorycache.get_client()
+MC = None
availability_zone_opts = [
cfg.StrOpt('internal_service_availability_zone',
@@ -38,6 +38,27 @@ CONF = cfg.CONF
CONF.register_opts(availability_zone_opts)
+def _get_cache():
+ global MC
+
+ if MC is None:
+ MC = memorycache.get_client()
+
+ return MC
+
+
+def _reset_cache():
+ """Reset the cache, mainly for testing purposes."""
+
+ global MC
+
+ MC = None
+
+
+def _make_cache_key(host):
+ return "azcache-%s" % host
+
+
def set_availability_zones(context, services):
# Makes sure services isn't a sqlalchemy object
services = [dict(service.iteritems()) for service in services]
@@ -50,6 +71,11 @@ def set_availability_zones(context, services):
az = u','.join(list(metadata[service['host']]))
else:
az = CONF.default_availability_zone
+ # update the cache
+ cache = _get_cache()
+ cache_key = _make_cache_key(service['host'])
+ cache.delete(cache_key)
+ cache.set(cache_key, az, AZ_CACHE_SECONDS)
service['availability_zone'] = az
return services
@@ -62,9 +88,10 @@ def get_host_availability_zone(context, host, conductor_api=None):
metadata = db.aggregate_metadata_get_by_host(
context, host, key='availability_zone')
if 'availability_zone' in metadata:
- return list(metadata['availability_zone'])[0]
+ az = list(metadata['availability_zone'])[0]
else:
- return CONF.default_availability_zone
+ az = CONF.default_availability_zone
+ return az
def get_availability_zones(context):
@@ -95,10 +122,11 @@ def get_instance_availability_zone(context, instance):
if not host:
return None
- cache_key = "azcache-%s" % host
- az = MC.get(cache_key)
+ cache_key = _make_cache_key(host)
+ cache = _get_cache()
+ az = cache.get(cache_key)
if not az:
elevated = context.elevated()
az = get_host_availability_zone(elevated, host)
- MC.set(cache_key, az, AZ_CACHE_SECONDS)
+ cache.set(cache_key, az, AZ_CACHE_SECONDS)
return az
diff --git a/nova/tests/api/openstack/compute/contrib/test_availability_zone.py b/nova/tests/api/openstack/compute/contrib/test_availability_zone.py
index 2ccb9fa31..0b63960ce 100644
--- a/nova/tests/api/openstack/compute/contrib/test_availability_zone.py
+++ b/nova/tests/api/openstack/compute/contrib/test_availability_zone.py
@@ -76,6 +76,7 @@ def fake_set_availability_zones(context, services):
class AvailabilityZoneApiTest(test.TestCase):
def setUp(self):
super(AvailabilityZoneApiTest, self).setUp()
+ availability_zones._reset_cache()
self.stubs.Set(db, 'service_get_all', fake_service_get_all)
self.stubs.Set(availability_zones, 'set_availability_zones',
fake_set_availability_zones)
diff --git a/nova/tests/api/openstack/compute/contrib/test_extended_availability_zone.py b/nova/tests/api/openstack/compute/contrib/test_extended_availability_zone.py
index 814c0fff4..59d60acf2 100644
--- a/nova/tests/api/openstack/compute/contrib/test_extended_availability_zone.py
+++ b/nova/tests/api/openstack/compute/contrib/test_extended_availability_zone.py
@@ -19,6 +19,7 @@ import webob
from nova.api.openstack.compute.contrib import extended_availability_zone
from nova import availability_zones
from nova import compute
+from nova.compute import vm_states
from nova import exception
from nova.openstack.common import jsonutils
from nova import test
@@ -29,14 +30,31 @@ UUID2 = '00000000-0000-0000-0000-000000000002'
UUID3 = '00000000-0000-0000-0000-000000000003'
+def fake_compute_get_az(*args, **kwargs):
+ inst = fakes.stub_instance(1, uuid=UUID3, host="get-host",
+ vm_state=vm_states.ACTIVE,
+ availability_zone='fakeaz')
+ return inst
+
+
+def fake_compute_get_empty(*args, **kwargs):
+ inst = fakes.stub_instance(1, uuid=UUID3, host="",
+ vm_state=vm_states.ACTIVE,
+ availability_zone='fakeaz')
+ return inst
+
+
def fake_compute_get(*args, **kwargs):
- inst = fakes.stub_instance(1, uuid=UUID3, host="get-host")
+ inst = fakes.stub_instance(1, uuid=UUID3, host="get-host",
+ vm_state=vm_states.ACTIVE)
return inst
def fake_compute_get_all(*args, **kwargs):
- inst1 = fakes.stub_instance(1, uuid=UUID1, host="all-host")
- inst2 = fakes.stub_instance(2, uuid=UUID2, host="all-host")
+ inst1 = fakes.stub_instance(1, uuid=UUID1, host="all-host",
+ vm_state=vm_states.ACTIVE)
+ inst2 = fakes.stub_instance(2, uuid=UUID2, host="all-host",
+ vm_state=vm_states.ACTIVE)
return [inst1, inst2]
@@ -44,12 +62,17 @@ def fake_get_host_availability_zone(context, host):
return host
+def fake_get_no_host_availability_zone(context, host):
+ return None
+
+
class ExtendedServerAttributesTest(test.TestCase):
content_type = 'application/json'
prefix = 'OS-EXT-AZ:'
def setUp(self):
super(ExtendedServerAttributesTest, self).setUp()
+ availability_zones._reset_cache()
fakes.stub_out_nw_api(self.stubs)
self.stubs.Set(compute.api.API, 'get', fake_compute_get)
self.stubs.Set(compute.api.API, 'get_all', fake_compute_get_all)
@@ -77,6 +100,28 @@ class ExtendedServerAttributesTest(test.TestCase):
self.assertEqual(server.get('%savailability_zone' % self.prefix),
az)
+ def test_show_no_host_az(self):
+ self.stubs.Set(compute.api.API, 'get', fake_compute_get_az)
+ self.stubs.Set(availability_zones, 'get_host_availability_zone',
+ fake_get_no_host_availability_zone)
+
+ url = '/v2/fake/servers/%s' % UUID3
+ res = self._make_request(url)
+
+ self.assertEqual(res.status_int, 200)
+ self.assertServerAttributes(self._get_server(res.body), 'fakeaz')
+
+ def test_show_empty_host_az(self):
+ self.stubs.Set(compute.api.API, 'get', fake_compute_get_empty)
+ self.stubs.Set(availability_zones, 'get_host_availability_zone',
+ fake_get_no_host_availability_zone)
+
+ url = '/v2/fake/servers/%s' % UUID3
+ res = self._make_request(url)
+
+ self.assertEqual(res.status_int, 200)
+ self.assertServerAttributes(self._get_server(res.body), 'fakeaz')
+
def test_show(self):
url = '/v2/fake/servers/%s' % UUID3
res = self._make_request(url)
diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py
index a4edf9e89..5eaf1a602 100644
--- a/nova/tests/api/openstack/fakes.py
+++ b/nova/tests/api/openstack/fakes.py
@@ -462,7 +462,8 @@ def stub_instance(id, user_id=None, project_id=None, host=None,
security_groups=None, root_device_name=None,
limit=None, marker=None,
launched_at=datetime.datetime.utcnow(),
- terminated_at=datetime.datetime.utcnow()):
+ terminated_at=datetime.datetime.utcnow(),
+ availability_zone=''):
if user_id is None:
user_id = 'fake_user'
@@ -531,7 +532,7 @@ def stub_instance(id, user_id=None, project_id=None, host=None,
"scheduled_at": timeutils.utcnow(),
"launched_at": launched_at,
"terminated_at": terminated_at,
- "availability_zone": "",
+ "availability_zone": availability_zone,
"display_name": display_name or server_name,
"display_description": "",
"locked": False,