diff options
| author | Joshua Harlow <harlowja@yahoo-inc.com> | 2013-05-08 15:47:47 -0700 |
|---|---|---|
| committer | Phil Day <philip.day@hp.com> | 2013-06-26 21:08:13 +0100 |
| commit | 5ff0278561d4008bc710440129f4a7b5c38cdd98 (patch) | |
| tree | cd3ac4c33477a1ed089822514c07736d84bded85 | |
| parent | 7696c3c11f0de855cbc53cc04ee7d2be07ae3b9c (diff) | |
| download | nova-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
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, |
