summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--etc/nova/policy.json2
-rw-r--r--nova/api/ec2/cloud.py29
-rw-r--r--nova/api/openstack/compute/contrib/availability_zone.py155
-rw-r--r--nova/availability_zones.py22
-rw-r--r--nova/tests/api/openstack/compute/contrib/test_availability_zone.py244
-rw-r--r--nova/tests/fake_policy.py2
-rw-r--r--nova/tests/integrated/api_samples/os-availability-zone/availability-zone-details-resp.json.tpl48
-rw-r--r--nova/tests/integrated/api_samples/os-availability-zone/availability-zone-details-resp.xml.tpl44
-rw-r--r--nova/tests/integrated/api_samples/os-availability-zone/availability-zone-get-resp.json.tpl18
-rw-r--r--nova/tests/integrated/api_samples/os-availability-zone/availability-zone-get-resp.xml.tpl12
10 files changed, 549 insertions, 27 deletions
diff --git a/etc/nova/policy.json b/etc/nova/policy.json
index fd1f9c2e0..f85ab9758 100644
--- a/etc/nova/policy.json
+++ b/etc/nova/policy.json
@@ -83,6 +83,8 @@
"compute_extension:virtual_storage_arrays": "",
"compute_extension:volumes": "",
"compute_extension:volumetypes": "",
+ "compute_extension:availability_zone:list": "",
+ "compute_extension:availability_zone:detail": "rule:admin_api",
"volume:create": "",
diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py
index 31f486b81..48b0f632f 100644
--- a/nova/api/ec2/cloud.py
+++ b/nova/api/ec2/cloud.py
@@ -250,32 +250,10 @@ class CloudController(object):
else:
return self._describe_availability_zones(context, **kwargs)
- def _get_zones(self, context):
- """Return available and unavailable zones."""
- enabled_services = db.service_get_all(context, False)
- disabled_services = db.service_get_all(context, True)
- enabled_services = availability_zones.set_availability_zones(context,
- enabled_services)
- disabled_services = availability_zones.set_availability_zones(context,
- disabled_services)
-
- available_zones = []
- for zone in [service['availability_zone'] for service
- in enabled_services]:
- if not zone in available_zones:
- available_zones.append(zone)
-
- not_available_zones = []
- zones = [service['available_zones'] for service in disabled_services
- if service['available_zones'] not in available_zones]
- for zone in zones:
- if zone not in not_available_zones:
- not_available_zones.append(zone)
- return (available_zones, not_available_zones)
-
def _describe_availability_zones(self, context, **kwargs):
ctxt = context.elevated()
- available_zones, not_available_zones = self._get_zones(ctxt)
+ available_zones, not_available_zones = \
+ availability_zones.get_availability_zones(ctxt)
result = []
for zone in available_zones:
@@ -291,7 +269,8 @@ class CloudController(object):
def _describe_availability_zones_verbose(self, context, **kwargs):
ctxt = context.elevated()
- available_zones, not_available_zones = self._get_zones(ctxt)
+ available_zones, not_available_zones = \
+ availability_zones.get_availability_zones(ctxt)
# Available services
enabled_services = db.service_get_all(context, False)
diff --git a/nova/api/openstack/compute/contrib/availability_zone.py b/nova/api/openstack/compute/contrib/availability_zone.py
index 2955b68eb..6cde5ca64 100644
--- a/nova/api/openstack/compute/contrib/availability_zone.py
+++ b/nova/api/openstack/compute/contrib/availability_zone.py
@@ -14,14 +14,165 @@
# License for the specific language governing permissions and limitations
# under the License
+from nova.api.openstack import common
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 db
+from nova.openstack.common import cfg
+from nova.openstack.common import log as logging
+from nova import servicegroup
+
+
+LOG = logging.getLogger(__name__)
+CONF = cfg.CONF
+
+authorize_list = extensions.extension_authorizer('compute',
+ 'availability_zone:list')
+authorize_detail = extensions.extension_authorizer('compute',
+ 'availability_zone:detail')
+
+
+def make_availability_zone(elem):
+ elem.set('name', 'zoneName')
+
+ zoneStateElem = xmlutil.SubTemplateElement(elem, 'zoneState',
+ selector='zoneState')
+ zoneStateElem.set('available')
+
+ hostsElem = xmlutil.SubTemplateElement(elem, 'hosts', selector='hosts')
+ hostElem = xmlutil.SubTemplateElement(hostsElem, 'host',
+ selector=xmlutil.get_items)
+ hostElem.set('name', 0)
+
+ svcsElem = xmlutil.SubTemplateElement(hostElem, 'services', selector=1)
+ svcElem = xmlutil.SubTemplateElement(svcsElem, 'service',
+ selector=xmlutil.get_items)
+ svcElem.set('name', 0)
+
+ svcStateElem = xmlutil.SubTemplateElement(svcElem, 'serviceState',
+ selector=1)
+ svcStateElem.set('available')
+ svcStateElem.set('active')
+ svcStateElem.set('updated_at')
+
+ # Attach metadata node
+ elem.append(common.MetadataTemplate())
+
+
+class AvailabilityZonesTemplate(xmlutil.TemplateBuilder):
+ def construct(self):
+ root = xmlutil.TemplateElement('availabilityZones')
+ zoneElem = xmlutil.SubTemplateElement(root, 'availabilityZone',
+ selector='availabilityZoneInfo')
+ make_availability_zone(zoneElem)
+ return xmlutil.MasterTemplate(root, 1, nsmap={
+ Availability_zone.alias: Availability_zone.namespace})
+
+
+class AvailabilityZoneController(wsgi.Controller):
+ """The Availability Zone API controller for the OpenStack API."""
+
+ def __init__(self):
+ super(AvailabilityZoneController, self).__init__()
+ self.servicegroup_api = servicegroup.API()
+
+ def _describe_availability_zones(self, context, **kwargs):
+ ctxt = context.elevated()
+ available_zones, not_available_zones = \
+ availability_zones.get_availability_zones(ctxt)
+
+ result = []
+ for zone in available_zones:
+ # Hide internal_service_availability_zone
+ if zone == CONF.internal_service_availability_zone:
+ continue
+ result.append({'zoneName': zone,
+ 'zoneState': {'available': True},
+ "hosts": None})
+ for zone in not_available_zones:
+ result.append({'zoneName': zone,
+ 'zoneState': {'available': False},
+ "hosts": None})
+ return {'availabilityZoneInfo': result}
+
+ def _describe_availability_zones_verbose(self, context, **kwargs):
+ ctxt = context.elevated()
+ available_zones, not_available_zones = \
+ availability_zones.get_availability_zones(ctxt)
+
+ # Available services
+ enabled_services = db.service_get_all(context, False)
+ enabled_services = availability_zones.set_availability_zones(context,
+ enabled_services)
+ zone_hosts = {}
+ host_services = {}
+ for service in enabled_services:
+ zone_hosts.setdefault(service['availability_zone'], [])
+ if not service['host'] in zone_hosts[service['availability_zone']]:
+ zone_hosts[service['availability_zone']].append(
+ service['host'])
+
+ host_services.setdefault(service['availability_zone'] +
+ service['host'], [])
+ host_services[service['availability_zone'] + service['host']].\
+ append(service)
+
+ result = []
+ for zone in available_zones:
+ hosts = {}
+ for host in zone_hosts[zone]:
+ hosts[host] = {}
+ for service in host_services[zone + host]:
+ alive = self.servicegroup_api.service_is_up(service)
+ hosts[host][service['binary']] = {'available': alive,
+ 'active': True != service['disabled'],
+ 'updated_at': service['updated_at']}
+ result.append({'zoneName': zone,
+ 'zoneState': {'available': True},
+ "hosts": hosts})
+
+ for zone in not_available_zones:
+ result.append({'zoneName': zone,
+ 'zoneState': {'available': False},
+ "hosts": None})
+ return {'availabilityZoneInfo': result}
+
+ @wsgi.serializers(xml=AvailabilityZonesTemplate)
+ def index(self, req):
+ """Returns a summary list of availability zone."""
+ context = req.environ['nova.context']
+ authorize_list(context)
+
+ return self._describe_availability_zones(context)
+
+ @wsgi.serializers(xml=AvailabilityZonesTemplate)
+ def detail(self, req):
+ """Returns a detailed list of availability zone."""
+ context = req.environ['nova.context']
+ authorize_detail(context)
+
+ return self._describe_availability_zones_verbose(context)
class Availability_zone(extensions.ExtensionDescriptor):
- """Add availability_zone to the Create Server v1.1 API."""
+ """1. Add availability_zone to the Create Server v1.1 API.
+ 2. Add availability zones describing.
+ """
name = "AvailabilityZone"
alias = "os-availability-zone"
namespace = ("http://docs.openstack.org/compute/ext/"
"availabilityzone/api/v1.1")
- updated = "2012-08-09T00:00:00+00:00"
+ updated = "2012-12-21T00:00:00+00:00"
+
+ def get_resources(self):
+ resources = []
+
+ res = extensions.ResourceExtension('os-availability-zone',
+ AvailabilityZoneController(),
+ collection_actions={'detail': 'GET'})
+ resources.append(res)
+
+ return resources
diff --git a/nova/availability_zones.py b/nova/availability_zones.py
index 62c83f6ed..09cbd98b8 100644
--- a/nova/availability_zones.py
+++ b/nova/availability_zones.py
@@ -60,3 +60,25 @@ def get_host_availability_zone(context, host):
return list(metadata['availability_zone'])[0]
else:
return CONF.default_availability_zone
+
+
+def get_availability_zones(context):
+ """Return available and unavailable zones."""
+ enabled_services = db.service_get_all(context, False)
+ disabled_services = db.service_get_all(context, True)
+ enabled_services = set_availability_zones(context, enabled_services)
+ disabled_services = set_availability_zones(context, disabled_services)
+
+ available_zones = []
+ for zone in [service['availability_zone'] for service
+ in enabled_services]:
+ if not zone in available_zones:
+ available_zones.append(zone)
+
+ not_available_zones = []
+ zones = [service['available_zones'] for service in disabled_services
+ if service['available_zones'] not in available_zones]
+ for zone in zones:
+ if zone not in not_available_zones:
+ not_available_zones.append(zone)
+ return (available_zones, not_available_zones)
diff --git a/nova/tests/api/openstack/compute/contrib/test_availability_zone.py b/nova/tests/api/openstack/compute/contrib/test_availability_zone.py
new file mode 100644
index 000000000..8abe7f388
--- /dev/null
+++ b/nova/tests/api/openstack/compute/contrib/test_availability_zone.py
@@ -0,0 +1,244 @@
+# Copyright 2012 IBM
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from datetime import datetime
+from lxml import etree
+import webob
+
+from nova.api.openstack.compute.contrib import availability_zone
+from nova import availability_zones
+from nova import context
+from nova import db
+from nova.openstack.common import jsonutils
+from nova import servicegroup
+from nova import test
+from nova.tests.api.openstack import fakes
+
+
+def fake_service_get_all(context, disabled=None):
+ def __fake_service(binary, availability_zone,
+ created_at, updated_at, host, disabled):
+ return {'binary': binary,
+ 'availability_zone': availability_zone,
+ 'available_zones': availability_zone,
+ 'created_at': created_at,
+ 'updated_at': updated_at,
+ 'host': host,
+ 'disabled': disabled}
+
+ if disabled:
+ return [__fake_service("nova-compute", "zone-2",
+ datetime(2012, 11, 14, 9, 53, 25, 0),
+ datetime(2012, 12, 26, 14, 45, 25, 0),
+ "fake_host-1", True),
+ __fake_service("nova-scheduler", "internal",
+ datetime(2012, 11, 14, 9, 57, 3, 0),
+ datetime(2012, 12, 26, 14, 45, 25, 0),
+ "fake_host-1", True),
+ __fake_service("nova-network", "internal",
+ datetime(2012, 11, 16, 7, 25, 46, 0),
+ datetime(2012, 12, 26, 14, 45, 24, 0),
+ "fake_host-2", True)]
+ else:
+ return [__fake_service("nova-compute", "zone-1",
+ datetime(2012, 11, 14, 9, 53, 25, 0),
+ datetime(2012, 12, 26, 14, 45, 25, 0),
+ "fake_host-1", False),
+ __fake_service("nova-sched", "internal",
+ datetime(2012, 11, 14, 9, 57, 03, 0),
+ datetime(2012, 12, 26, 14, 45, 25, 0),
+ "fake_host-1", False),
+ __fake_service("nova-network", "internal",
+ datetime(2012, 11, 16, 7, 25, 46, 0),
+ datetime(2012, 12, 26, 14, 45, 24, 0),
+ "fake_host-2", False)]
+
+
+def fake_service_is_up(self, service):
+ return service['binary'] != u"nova-network"
+
+
+def fake_set_availability_zones(context, services):
+ return services
+
+
+class AvailabilityZoneApiTest(test.TestCase):
+ def setUp(self):
+ super(AvailabilityZoneApiTest, self).setUp()
+ self.stubs.Set(db, 'service_get_all', fake_service_get_all)
+ self.stubs.Set(availability_zones, 'set_availability_zones',
+ fake_set_availability_zones)
+ self.stubs.Set(servicegroup.API, 'service_is_up', fake_service_is_up)
+
+ def test_availability_zone_index(self):
+ req = webob.Request.blank('/v2/fake/os-availability-zone')
+ resp = req.get_response(fakes.wsgi_app())
+ self.assertEqual(resp.status_int, 200)
+ resp_dict = jsonutils.loads(resp.body)
+
+ self.assertTrue('availabilityZoneInfo' in resp_dict)
+ zones = resp_dict['availabilityZoneInfo']
+ self.assertEqual(len(zones), 2)
+ self.assertEqual(zones[0]['zoneName'], u'zone-1')
+ self.assertTrue(zones[0]['zoneState']['available'])
+ self.assertIsNone(zones[0]['hosts'])
+ self.assertEqual(zones[1]['zoneName'], u'zone-2')
+ self.assertFalse(zones[1]['zoneState']['available'])
+ self.assertIsNone(zones[1]['hosts'])
+
+ def test_availability_zone_detail(self):
+ def _formatZone(zone_dict):
+ result = []
+
+ # Zone tree view item
+ result.append({'zoneName': zone_dict['zoneName'],
+ 'zoneState': u'available'
+ if zone_dict['zoneState']['available'] else
+ u'not available'})
+
+ if zone_dict['hosts'] is not None:
+ for (host, services) in zone_dict['hosts'].items():
+ # Host tree view item
+ result.append({'zoneName': u'|- %s' % host,
+ 'zoneState': u''})
+ for (svc, state) in services.items():
+ # Service tree view item
+ result.append({'zoneName': u'| |- %s' % svc,
+ 'zoneState': u'%s %s %s' % (
+ 'enabled' if state['active'] else
+ 'disabled',
+ ':-)' if state['available'] else
+ 'XXX',
+ jsonutils.to_primitive(
+ state['updated_at']))})
+ return result
+
+ def _assertZone(zone, name, status):
+ self.assertEqual(zone['zoneName'], name)
+ self.assertEqual(zone['zoneState'], status)
+
+ availabilityZone = availability_zone.AvailabilityZoneController()
+
+ req = webob.Request.blank('/v2/fake/os-availability-zone/detail')
+ req.method = 'GET'
+ req.environ['nova.context'] = context.get_admin_context()
+ resp_dict = availabilityZone.detail(req)
+
+ self.assertTrue('availabilityZoneInfo' in resp_dict)
+ zones = resp_dict['availabilityZoneInfo']
+ self.assertEqual(len(zones), 3)
+
+ ''' availabilityZoneInfo field content in response body:
+ [{'zoneName': 'zone-1',
+ 'zoneState': {'available': True},
+ 'hosts': {'fake_host-1': {
+ 'nova-compute': {'active': True, 'available': True,
+ 'updated_at': datetime(2012, 12, 26, 14, 45, 25)}}}},
+ {'zoneName': 'internal',
+ 'zoneState': {'available': True},
+ 'hosts': {'fake_host-1': {
+ 'nova-sched': {'active': True, 'available': True,
+ 'updated_at': datetime(2012, 12, 26, 14, 45, 25)}},
+ 'fake_host-2': {
+ 'nova-network': {'active': True, 'available': False,
+ 'updated_at': datetime(2012, 12, 26, 14, 45, 24)}}}},
+ {'zoneName': 'zone-2',
+ 'zoneState': {'available': False},
+ 'hosts': None}]
+ '''
+
+ l0 = [u'zone-1', u'available']
+ l1 = [u'|- fake_host-1', u'']
+ l2 = [u'| |- nova-compute', u'enabled :-) 2012-12-26T14:45:25.000000']
+ l3 = [u'internal', u'available']
+ l4 = [u'|- fake_host-1', u'']
+ l5 = [u'| |- nova-sched', u'enabled :-) 2012-12-26T14:45:25.000000']
+ l6 = [u'|- fake_host-2', u'']
+ l7 = [u'| |- nova-network', u'enabled XXX 2012-12-26T14:45:24.000000']
+ l8 = [u'zone-2', u'not available']
+
+ z0 = _formatZone(zones[0])
+ z1 = _formatZone(zones[1])
+ z2 = _formatZone(zones[2])
+
+ self.assertEqual(len(z0), 3)
+ self.assertEqual(len(z1), 5)
+ self.assertEqual(len(z2), 1)
+
+ _assertZone(z0[0], l0[0], l0[1])
+ _assertZone(z0[1], l1[0], l1[1])
+ _assertZone(z0[2], l2[0], l2[1])
+ _assertZone(z1[0], l3[0], l3[1])
+ _assertZone(z1[1], l4[0], l4[1])
+ _assertZone(z1[2], l5[0], l5[1])
+ _assertZone(z1[3], l6[0], l6[1])
+ _assertZone(z1[4], l7[0], l7[1])
+ _assertZone(z2[0], l8[0], l8[1])
+
+
+class AvailabilityZoneSerializerTest(test.TestCase):
+ def test_availability_zone_index_detail_serializer(self):
+ def _verify_zone(zone_dict, tree):
+ self.assertEqual(tree.tag, 'availabilityZone')
+ self.assertEqual(zone_dict['zoneName'], tree.get('name'))
+ self.assertEqual(str(zone_dict['zoneState']['available']),
+ tree[0].get('available'))
+
+ for _idx, host_child in enumerate(tree[1]):
+ self.assertTrue(host_child.get('name') in zone_dict['hosts'])
+ svcs = zone_dict['hosts'][host_child.get('name')]
+ for _idx, svc_child in enumerate(host_child[0]):
+ self.assertTrue(svc_child.get('name') in svcs)
+ svc = svcs[svc_child.get('name')]
+ self.assertEqual(len(svc_child), 1)
+
+ self.assertEqual(str(svc['available']),
+ svc_child[0].get('available'))
+ self.assertEqual(str(svc['active']),
+ svc_child[0].get('active'))
+ self.assertEqual(str(svc['updated_at']),
+ svc_child[0].get('updated_at'))
+
+ serializer = availability_zone.AvailabilityZonesTemplate()
+ raw_availability_zones = \
+ [{'zoneName': 'zone-1',
+ 'zoneState': {'available': True},
+ 'hosts': {'fake_host-1': {
+ 'nova-compute': {'active': True, 'available': True,
+ 'updated_at':
+ datetime(2012, 12, 26, 14, 45, 25)}}}},
+ {'zoneName': 'internal',
+ 'zoneState': {'available': True},
+ 'hosts': {'fake_host-1': {
+ 'nova-sched': {'active': True, 'available': True,
+ 'updated_at':
+ datetime(2012, 12, 26, 14, 45, 25)}},
+ 'fake_host-2': {
+ 'nova-network': {'active': True,
+ 'available': False,
+ 'updated_at':
+ datetime(2012, 12, 26, 14, 45, 24)}}}},
+ {'zoneName': 'zone-2',
+ 'zoneState': {'available': False},
+ 'hosts': None}]
+
+ text = serializer.serialize(
+ dict(availabilityZoneInfo=raw_availability_zones))
+ tree = etree.fromstring(text)
+
+ self.assertEqual('availabilityZones', tree.tag)
+ self.assertEqual(len(raw_availability_zones), len(tree))
+ for idx, child in enumerate(tree):
+ _verify_zone(raw_availability_zones[idx], child)
diff --git a/nova/tests/fake_policy.py b/nova/tests/fake_policy.py
index acefa856c..04e4adbbd 100644
--- a/nova/tests/fake_policy.py
+++ b/nova/tests/fake_policy.py
@@ -158,6 +158,8 @@ policy_data = """
"compute_extension:volumes": "",
"compute_extension:volumetypes": "",
"compute_extension:zones": "",
+ "compute_extension:availability_zone:list": "",
+ "compute_extension:availability_zone:detail": "is_admin:True",
"volume:create": "",
diff --git a/nova/tests/integrated/api_samples/os-availability-zone/availability-zone-details-resp.json.tpl b/nova/tests/integrated/api_samples/os-availability-zone/availability-zone-details-resp.json.tpl
new file mode 100644
index 000000000..6d44692e1
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-availability-zone/availability-zone-details-resp.json.tpl
@@ -0,0 +1,48 @@
+{
+ "availabilityZoneInfo": [
+ {
+ "zoneName": "zone-1",
+ "zoneState": {
+ "available": true
+ },
+ "hosts": {
+ "fake_host-1": {
+ "nova-compute": {
+ "active": true,
+ "available": true,
+ "updated_at": "2012-12-26T14:45:25.000000"
+ }
+ }
+ }
+ },
+ {
+ "zoneName": "internal",
+ "zoneState": {
+ "available": true
+ },
+ "hosts": {
+ "fake_host-1": {
+ "nova-sched": {
+ "active": true,
+ "available": true,
+ "updated_at": "2012-12-26T14:45:25.000000"
+ }
+ },
+ "fake_host-2": {
+ "nova-network": {
+ "active": true,
+ "available": false,
+ "updated_at": "2012-12-26T14:45:24.000000"
+ }
+ }
+ }
+ },
+ {
+ "zoneName": "zone-2",
+ "zoneState": {
+ "available": false
+ },
+ "hosts": null
+ }
+ ]
+} \ No newline at end of file
diff --git a/nova/tests/integrated/api_samples/os-availability-zone/availability-zone-details-resp.xml.tpl b/nova/tests/integrated/api_samples/os-availability-zone/availability-zone-details-resp.xml.tpl
new file mode 100644
index 000000000..856a64957
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-availability-zone/availability-zone-details-resp.xml.tpl
@@ -0,0 +1,44 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<availabilityZones
+ xmlns:os-availability-zone="http://docs.openstack.org/compute/ext/availabilityzone/api/v1.1">
+ <availabilityZone name="zone-1">
+ <zoneState available="True" />
+ <hosts>
+ <host name="fake_host-1">
+ <services>
+ <service name="nova-compute">
+ <serviceState available="True" active="True"
+ updated_at="2012-12-26 14:45:25" />
+ </service>
+ </services>
+ </host>
+ </hosts>
+ <metadata />
+ </availabilityZone>
+ <availabilityZone name="internal">
+ <zoneState available="True" />
+ <hosts>
+ <host name="fake_host-1">
+ <services>
+ <service name="nova-sched">
+ <serviceState available="True" active="True"
+ updated_at="2012-12-26 14:45:25" />
+ </service>
+ </services>
+ </host>
+ <host name="fake_host-2">
+ <services>
+ <service name="nova-network">
+ <serviceState available="False" active="True"
+ updated_at="2012-12-26 14:45:24" />
+ </service>
+ </services>
+ </host>
+ </hosts>
+ <metadata />
+ </availabilityZone>
+ <availabilityZone name="zone-2">
+ <zoneState available="False" />
+ <metadata />
+ </availabilityZone>
+</availabilityZones> \ No newline at end of file
diff --git a/nova/tests/integrated/api_samples/os-availability-zone/availability-zone-get-resp.json.tpl b/nova/tests/integrated/api_samples/os-availability-zone/availability-zone-get-resp.json.tpl
new file mode 100644
index 000000000..381708aaf
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-availability-zone/availability-zone-get-resp.json.tpl
@@ -0,0 +1,18 @@
+{
+ "availabilityZoneInfo": [
+ {
+ "zoneName": "zone-1",
+ "zoneState": {
+ "available": true
+ },
+ "hosts": null
+ },
+ {
+ "zoneName": "zone-2",
+ "zoneState": {
+ "available": false
+ },
+ "hosts": null
+ }
+ ]
+} \ No newline at end of file
diff --git a/nova/tests/integrated/api_samples/os-availability-zone/availability-zone-get-resp.xml.tpl b/nova/tests/integrated/api_samples/os-availability-zone/availability-zone-get-resp.xml.tpl
new file mode 100644
index 000000000..1eff177de
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-availability-zone/availability-zone-get-resp.xml.tpl
@@ -0,0 +1,12 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<availabilityZones
+ xmlns:os-availability-zone="http://docs.openstack.org/compute/ext/availabilityzone/api/v1.1">
+ <availabilityZone name="zone-1">
+ <zoneState available="True" />
+ <metadata />
+ </availabilityZone>
+ <availabilityZone name="zone-2">
+ <zoneState available="False" />
+ <metadata />
+ </availabilityZone>
+</availabilityZones> \ No newline at end of file