From 8b0e091577e77d3dac5bbae50e0d42e89bcfa39e Mon Sep 17 00:00:00 2001 From: Eli Qiao Date: Thu, 4 Sep 2014 14:49:28 +0800 Subject: [PATCH] Port floating_ip_dns extention to v2.1 This patch ports floating_ip_dns extention from v2 to v2.1, and have v2 unit test cases shared between v2.1 and v2. Partially implements blueprint v2-on-v3-api Change-Id: Ia1316697c141fde2b431ba79aebae5986687a4fa --- ...ing-ip-dns-create-or-update-entry-req.json | 6 + ...ng-ip-dns-create-or-update-entry-resp.json | 9 + .../floating-ip-dns-create-or-update-req.json | 7 + ...floating-ip-dns-create-or-update-resp.json | 8 + .../floating-ip-dns-entry-get-resp.json | 9 + .../floating-ip-dns-entry-list-resp.json | 11 + .../floating-ip-dns-list-resp.json | 10 + etc/nova/policy.json | 2 + .../compute/plugins/v3/floating_ip_dns.py | 282 ++++++++++++++++++ .../compute/contrib/test_floating_ip_dns.py | 43 ++- nova/tests/fake_policy.py | 1 + ...ip-dns-create-or-update-entry-req.json.tpl | 6 + ...p-dns-create-or-update-entry-resp.json.tpl | 9 + ...ating-ip-dns-create-or-update-req.json.tpl | 7 + ...ting-ip-dns-create-or-update-resp.json.tpl | 8 + .../floating-ip-dns-entry-get-resp.json.tpl | 9 + .../floating-ip-dns-entry-list-resp.json.tpl | 11 + .../floating-ip-dns-list-resp.json.tpl | 10 + .../integrated/v3/test_floating_ip_dns.py | 91 ++++++ setup.cfg | 1 + 20 files changed, 527 insertions(+), 13 deletions(-) create mode 100644 doc/v3/api_samples/os-floating-ip-dns/floating-ip-dns-create-or-update-entry-req.json create mode 100644 doc/v3/api_samples/os-floating-ip-dns/floating-ip-dns-create-or-update-entry-resp.json create mode 100644 doc/v3/api_samples/os-floating-ip-dns/floating-ip-dns-create-or-update-req.json create mode 100644 doc/v3/api_samples/os-floating-ip-dns/floating-ip-dns-create-or-update-resp.json create mode 100644 doc/v3/api_samples/os-floating-ip-dns/floating-ip-dns-entry-get-resp.json create mode 100644 doc/v3/api_samples/os-floating-ip-dns/floating-ip-dns-entry-list-resp.json create mode 100644 doc/v3/api_samples/os-floating-ip-dns/floating-ip-dns-list-resp.json create mode 100644 nova/api/openstack/compute/plugins/v3/floating_ip_dns.py create mode 100644 nova/tests/integrated/v3/api_samples/os-floating-ip-dns/floating-ip-dns-create-or-update-entry-req.json.tpl create mode 100644 nova/tests/integrated/v3/api_samples/os-floating-ip-dns/floating-ip-dns-create-or-update-entry-resp.json.tpl create mode 100644 nova/tests/integrated/v3/api_samples/os-floating-ip-dns/floating-ip-dns-create-or-update-req.json.tpl create mode 100644 nova/tests/integrated/v3/api_samples/os-floating-ip-dns/floating-ip-dns-create-or-update-resp.json.tpl create mode 100644 nova/tests/integrated/v3/api_samples/os-floating-ip-dns/floating-ip-dns-entry-get-resp.json.tpl create mode 100644 nova/tests/integrated/v3/api_samples/os-floating-ip-dns/floating-ip-dns-entry-list-resp.json.tpl create mode 100644 nova/tests/integrated/v3/api_samples/os-floating-ip-dns/floating-ip-dns-list-resp.json.tpl create mode 100644 nova/tests/integrated/v3/test_floating_ip_dns.py diff --git a/doc/v3/api_samples/os-floating-ip-dns/floating-ip-dns-create-or-update-entry-req.json b/doc/v3/api_samples/os-floating-ip-dns/floating-ip-dns-create-or-update-entry-req.json new file mode 100644 index 0000000000..fa7b6af717 --- /dev/null +++ b/doc/v3/api_samples/os-floating-ip-dns/floating-ip-dns-create-or-update-entry-req.json @@ -0,0 +1,6 @@ +{ + "dns_entry": { + "ip": "192.168.53.11", + "dns_type": "A" + } +} diff --git a/doc/v3/api_samples/os-floating-ip-dns/floating-ip-dns-create-or-update-entry-resp.json b/doc/v3/api_samples/os-floating-ip-dns/floating-ip-dns-create-or-update-entry-resp.json new file mode 100644 index 0000000000..70c9038864 --- /dev/null +++ b/doc/v3/api_samples/os-floating-ip-dns/floating-ip-dns-create-or-update-entry-resp.json @@ -0,0 +1,9 @@ +{ + "dns_entry": { + "domain": "domain1.example.org", + "id": null, + "ip": "192.168.1.1", + "name": "instance1", + "type": "A" + } +} \ No newline at end of file diff --git a/doc/v3/api_samples/os-floating-ip-dns/floating-ip-dns-create-or-update-req.json b/doc/v3/api_samples/os-floating-ip-dns/floating-ip-dns-create-or-update-req.json new file mode 100644 index 0000000000..ce4802451d --- /dev/null +++ b/doc/v3/api_samples/os-floating-ip-dns/floating-ip-dns-create-or-update-req.json @@ -0,0 +1,7 @@ +{ + "domain_entry": { + "domain": "domain1.example.org", + "scope": "public", + "project": "project1" + } +} diff --git a/doc/v3/api_samples/os-floating-ip-dns/floating-ip-dns-create-or-update-resp.json b/doc/v3/api_samples/os-floating-ip-dns/floating-ip-dns-create-or-update-resp.json new file mode 100644 index 0000000000..db43e92d34 --- /dev/null +++ b/doc/v3/api_samples/os-floating-ip-dns/floating-ip-dns-create-or-update-resp.json @@ -0,0 +1,8 @@ +{ + "domain_entry": { + "availability_zone": null, + "domain": "domain1.example.org", + "project": "project1", + "scope": "public" + } +} \ No newline at end of file diff --git a/doc/v3/api_samples/os-floating-ip-dns/floating-ip-dns-entry-get-resp.json b/doc/v3/api_samples/os-floating-ip-dns/floating-ip-dns-entry-get-resp.json new file mode 100644 index 0000000000..84ee3930a3 --- /dev/null +++ b/doc/v3/api_samples/os-floating-ip-dns/floating-ip-dns-entry-get-resp.json @@ -0,0 +1,9 @@ +{ + "dns_entry": { + "domain": "domain1.example.org", + "id": null, + "ip": "192.168.1.1", + "name": "instance1", + "type": null + } +} \ No newline at end of file diff --git a/doc/v3/api_samples/os-floating-ip-dns/floating-ip-dns-entry-list-resp.json b/doc/v3/api_samples/os-floating-ip-dns/floating-ip-dns-entry-list-resp.json new file mode 100644 index 0000000000..3263de43ab --- /dev/null +++ b/doc/v3/api_samples/os-floating-ip-dns/floating-ip-dns-entry-list-resp.json @@ -0,0 +1,11 @@ +{ + "dns_entries": [ + { + "domain": "domain1.example.org", + "id": null, + "ip": "192.168.1.1", + "name": "instance1", + "type": null + } + ] +} \ No newline at end of file diff --git a/doc/v3/api_samples/os-floating-ip-dns/floating-ip-dns-list-resp.json b/doc/v3/api_samples/os-floating-ip-dns/floating-ip-dns-list-resp.json new file mode 100644 index 0000000000..8882c23a3d --- /dev/null +++ b/doc/v3/api_samples/os-floating-ip-dns/floating-ip-dns-list-resp.json @@ -0,0 +1,10 @@ +{ + "domain_entries": [ + { + "availability_zone": null, + "domain": "domain1.example.org", + "project": "project1", + "scope": "public" + } + ] +} \ No newline at end of file diff --git a/etc/nova/policy.json b/etc/nova/policy.json index 8d541dc718..42bf9d1027 100644 --- a/etc/nova/policy.json +++ b/etc/nova/policy.json @@ -157,6 +157,8 @@ "compute_extension:v3:flavor-manage:discoverable": "", "compute_extension:v3:flavor-manage": "rule:admin_api", "compute_extension:floating_ip_dns": "", + "compute_extension:v3:os-floating-ip-dns": "", + "compute_extension:v3:os-floating-ip-dns:discoverable": "", "compute_extension:floating_ip_pools": "", "compute_extension:v3:os-floating-ip-pools": "", "compute_extension:v3:os-floating-ip-pools:discoverable": "", diff --git a/nova/api/openstack/compute/plugins/v3/floating_ip_dns.py b/nova/api/openstack/compute/plugins/v3/floating_ip_dns.py new file mode 100644 index 0000000000..d350c36125 --- /dev/null +++ b/nova/api/openstack/compute/plugins/v3/floating_ip_dns.py @@ -0,0 +1,282 @@ +# Copyright 2011 Andrew Bogott for the Wikimedia Foundation +# +# 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. + +import urllib + +import webob + +from nova.api.openstack import extensions +from nova.api.openstack import wsgi +from nova import exception +from nova.i18n import _ +from nova import network +from nova import utils + + +ALIAS = "os-floating-ip-dns" +authorize = extensions.extension_authorizer('compute', 'v3:' + ALIAS) + + +def _translate_dns_entry_view(dns_entry): + result = {} + result['ip'] = dns_entry.get('ip') + result['id'] = dns_entry.get('id') + result['type'] = dns_entry.get('type') + result['domain'] = dns_entry.get('domain') + result['name'] = dns_entry.get('name') + return {'dns_entry': result} + + +def _translate_dns_entries_view(dns_entries): + return {'dns_entries': [_translate_dns_entry_view(entry)['dns_entry'] + for entry in dns_entries]} + + +def _translate_domain_entry_view(domain_entry): + result = {} + result['domain'] = domain_entry.get('domain') + result['scope'] = domain_entry.get('scope') + result['project'] = domain_entry.get('project') + result['availability_zone'] = domain_entry.get('availability_zone') + return {'domain_entry': result} + + +def _translate_domain_entries_view(domain_entries): + return {'domain_entries': + [_translate_domain_entry_view(entry)['domain_entry'] + for entry in domain_entries]} + + +def _unquote_domain(domain): + """Unquoting function for receiving a domain name in a URL. + + Domain names tend to have .'s in them. Urllib doesn't quote dots, + but Routes tends to choke on them, so we need an extra level of + by-hand quoting here. + """ + return urllib.unquote(domain).replace('%2E', '.') + + +def _create_dns_entry(ip, name, domain): + return {'ip': ip, 'name': name, 'domain': domain} + + +def _create_domain_entry(domain, scope=None, project=None, av_zone=None): + return {'domain': domain, 'scope': scope, 'project': project, + 'availability_zone': av_zone} + + +class FloatingIPDNSDomainController(object): + """DNS domain controller for OpenStack API.""" + + def __init__(self): + super(FloatingIPDNSDomainController, self).__init__() + self.network_api = network.API() + + @extensions.expected_errors(501) + def index(self, req): + """Return a list of available DNS domains.""" + context = req.environ['nova.context'] + authorize(context) + + try: + domains = self.network_api.get_dns_domains(context) + except NotImplementedError: + msg = _("Unable to create dns domain") + raise webob.exc.HTTPNotImplemented(explanation=msg) + + domainlist = [_create_domain_entry(domain['domain'], + domain.get('scope'), + domain.get('project'), + domain.get('availability_zone')) + for domain in domains] + + return _translate_domain_entries_view(domainlist) + + @extensions.expected_errors((422, 501)) + def update(self, req, id, body): + """Add or modify domain entry.""" + context = req.environ['nova.context'] + authorize(context) + fqdomain = _unquote_domain(id) + try: + entry = body['domain_entry'] + scope = entry['scope'] + except (TypeError, KeyError): + raise webob.exc.HTTPUnprocessableEntity() + project = entry.get('project', None) + av_zone = entry.get('availability_zone', None) + if (scope not in ('private', 'public') or + project and av_zone or + scope == 'private' and project or + scope == 'public' and av_zone): + raise webob.exc.HTTPUnprocessableEntity() + try: + if scope == 'private': + create_dns_domain = self.network_api.create_private_dns_domain + area_name, area = 'availability_zone', av_zone + else: + create_dns_domain = self.network_api.create_public_dns_domain + area_name, area = 'project', project + except NotImplementedError: + msg = _("Unable to create dns domain") + raise webob.exc.HTTPNotImplemented(explanation=msg) + + create_dns_domain(context, fqdomain, area) + return _translate_domain_entry_view({'domain': fqdomain, + 'scope': scope, + area_name: area}) + + @extensions.expected_errors((404, 501)) + @wsgi.response(202) + def delete(self, req, id): + """Delete the domain identified by id.""" + context = req.environ['nova.context'] + authorize(context) + domain = _unquote_domain(id) + + # Delete the whole domain + try: + self.network_api.delete_dns_domain(context, domain) + except NotImplementedError: + msg = _("Unable to delete dns domain") + raise webob.exc.HTTPNotImplemented(explanation=msg) + except exception.NotFound as e: + raise webob.exc.HTTPNotFound(explanation=e.format_message()) + + +class FloatingIPDNSEntryController(object): + """DNS Entry controller for OpenStack API.""" + + def __init__(self): + super(FloatingIPDNSEntryController, self).__init__() + self.network_api = network.API() + + @extensions.expected_errors((422, 404, 501)) + def show(self, req, domain_id, id): + """Return the DNS entry that corresponds to domain_id and id.""" + context = req.environ['nova.context'] + authorize(context) + domain = _unquote_domain(domain_id) + + floating_ip = None + # Check whether id is a valid ipv4/ipv6 address. + if utils.is_valid_ipv4(id) or utils.is_valid_ipv6(id): + floating_ip = id + + try: + if floating_ip: + entries = self.network_api.get_dns_entries_by_address(context, + floating_ip, + domain) + else: + entries = self.network_api.get_dns_entries_by_name(context, + id, + domain) + except NotImplementedError: + msg = _("Unable to get dns domain") + raise webob.exc.HTTPNotImplemented(explanation=msg) + + if not entries: + explanation = _("DNS entries not found.") + raise webob.exc.HTTPNotFound(explanation=explanation) + + if floating_ip: + entrylist = [_create_dns_entry(floating_ip, entry, domain) + for entry in entries] + dns_entries = _translate_dns_entries_view(entrylist) + return wsgi.ResponseObject(dns_entries) + + entry = _create_dns_entry(entries[0], id, domain) + return _translate_dns_entry_view(entry) + + @extensions.expected_errors((422, 501)) + def update(self, req, domain_id, id, body): + """Add or modify dns entry.""" + context = req.environ['nova.context'] + authorize(context) + domain = _unquote_domain(domain_id) + name = id + try: + entry = body['dns_entry'] + address = entry['ip'] + dns_type = entry['dns_type'] + except (TypeError, KeyError): + raise webob.exc.HTTPUnprocessableEntity() + + try: + entries = self.network_api.get_dns_entries_by_name(context, + name, domain) + if not entries: + # create! + self.network_api.add_dns_entry(context, address, name, + dns_type, domain) + else: + # modify! + self.network_api.modify_dns_entry(context, name, + address, domain) + except NotImplementedError: + msg = _("Unable to update dns domain") + raise webob.exc.HTTPNotImplemented(explanation=msg) + + return _translate_dns_entry_view({'ip': address, + 'name': name, + 'type': dns_type, + 'domain': domain}) + + @extensions.expected_errors((404, 501)) + @wsgi.response(202) + def delete(self, req, domain_id, id): + """Delete the entry identified by req and id.""" + context = req.environ['nova.context'] + authorize(context) + domain = _unquote_domain(domain_id) + name = id + + try: + self.network_api.delete_dns_entry(context, name, domain) + except NotImplementedError: + msg = _("Unable to delete dns domain") + raise webob.exc.HTTPNotImplemented(explanation=msg) + except exception.NotFound as e: + raise webob.exc.HTTPNotFound(explanation=e.format_message()) + + +class FloatingIpDns(extensions.V3APIExtensionBase): + """Floating IP DNS support.""" + + name = "FloatingIpDns" + alias = ALIAS + version = 1 + + def get_resources(self): + resources = [] + + res = extensions.ResourceExtension(ALIAS, + controller=FloatingIPDNSDomainController()) + resources.append(res) + + res = extensions.ResourceExtension('entries', + controller=FloatingIPDNSEntryController(), + parent={'member_name': 'domain', + 'collection_name': 'os-floating-ip-dns'}) + resources.append(res) + + return resources + + def get_controller_extensions(self): + """It's an abstract function V3APIExtensionBase and the extension + will not be loaded without it. + """ + return [] diff --git a/nova/tests/api/openstack/compute/contrib/test_floating_ip_dns.py b/nova/tests/api/openstack/compute/contrib/test_floating_ip_dns.py index 7eeab5c0c4..ff4d4baf6d 100644 --- a/nova/tests/api/openstack/compute/contrib/test_floating_ip_dns.py +++ b/nova/tests/api/openstack/compute/contrib/test_floating_ip_dns.py @@ -19,7 +19,9 @@ import urllib from lxml import etree import webob -from nova.api.openstack.compute.contrib import floating_ip_dns +from nova.api.openstack.compute.contrib import floating_ip_dns as fipdns_v2 +from nova.api.openstack.compute.plugins.v3 import floating_ip_dns as \ + fipdns_v21 from nova import context from nova import db from nova import exception @@ -92,7 +94,9 @@ def network_create_public_dns_domain(self, context, domain, project): pass -class FloatingIpDNSTest(test.TestCase): +class FloatingIpDNSTestV21(test.TestCase): + floating_ip_dns = fipdns_v21 + def _create_floating_ip(self): """Create a floating ip object.""" host = "fake_host" @@ -107,8 +111,11 @@ class FloatingIpDNSTest(test.TestCase): db.floating_ip_destroy(self.context, test_ipv4_address) db.floating_ip_destroy(self.context, test_ipv6_address) + def _check_status(self, expected_status, res, controller_methord): + self.assertEqual(expected_status, controller_methord.wsgi_code) + def setUp(self): - super(FloatingIpDNSTest, self).setUp() + super(FloatingIpDNSTestV21, self).setUp() self.stubs.Set(network.api.API, "get_dns_domains", network_get_dns_domains) self.stubs.Set(network.api.API, "get_dns_entries_by_address", @@ -129,13 +136,14 @@ class FloatingIpDNSTest(test.TestCase): self.context = context.get_admin_context() self._create_floating_ip() - temp = floating_ip_dns.FloatingIPDNSDomainController() + temp = self.floating_ip_dns.FloatingIPDNSDomainController() self.domain_controller = temp - self.entry_controller = floating_ip_dns.FloatingIPDNSEntryController() + self.entry_controller = self.floating_ip_dns.\ + FloatingIPDNSEntryController() def tearDown(self): self._delete_floating_ip() - super(FloatingIpDNSTest, self).tearDown() + super(FloatingIpDNSTestV21, self).tearDown() def test_dns_domains_list(self): req = fakes.HTTPRequest.blank('/v2/123/os-floating-ip-dns') @@ -283,7 +291,7 @@ class FloatingIpDNSTest(test.TestCase): (_quote_domain(domain), name)) res = self.entry_controller.delete(req, _quote_domain(domain), name) - self.assertEqual(202, res.status_int) + self._check_status(202, res, self.entry_controller.delete) self.assertEqual([(name, domain)], calls) def test_delete_entry_notfound(self): @@ -312,7 +320,7 @@ class FloatingIpDNSTest(test.TestCase): _quote_domain(domain)) res = self.domain_controller.delete(req, _quote_domain(domain)) - self.assertEqual(202, res.status_int) + self._check_status(202, res, self.domain_controller.delete) self.assertEqual([domain], calls) def test_delete_domain_notfound(self): @@ -338,9 +346,18 @@ class FloatingIpDNSTest(test.TestCase): self.assertEqual(entry['dns_entry']['ip'], test_ipv4_address2) -class FloatingIpDNSSerializerTest(test.TestCase): +class FloatingIpDNSTestV2(FloatingIpDNSTestV21): + floating_ip_dns = fipdns_v2 + + def _check_status(self, expected_status, res, controller_methord): + self.assertEqual(expected_status, res.status_int) + + +class FloatingIpDNSSerializerTestV2(test.TestCase): + floating_ip_dns = fipdns_v2 + def test_domains(self): - serializer = floating_ip_dns.DomainsTemplate() + serializer = self.floating_ip_dns.DomainsTemplate() text = serializer.serialize(dict( domain_entries=[ dict(domain=domain, scope='public', project='testproject'), @@ -355,7 +372,7 @@ class FloatingIpDNSSerializerTest(test.TestCase): self.assertEqual('avzone', tree[1].get('availability_zone')) def test_domain_serializer(self): - serializer = floating_ip_dns.DomainTemplate() + serializer = self.floating_ip_dns.DomainTemplate() text = serializer.serialize(dict( domain_entry=dict(domain=domain, scope='public', @@ -367,7 +384,7 @@ class FloatingIpDNSSerializerTest(test.TestCase): self.assertEqual('testproject', tree.get('project')) def test_entries_serializer(self): - serializer = floating_ip_dns.FloatingIPDNSsTemplate() + serializer = self.floating_ip_dns.FloatingIPDNSsTemplate() text = serializer.serialize(dict( dns_entries=[ dict(ip=test_ipv4_address, @@ -394,7 +411,7 @@ class FloatingIpDNSSerializerTest(test.TestCase): self.assertEqual(name2, tree[1].get('name')) def test_entry_serializer(self): - serializer = floating_ip_dns.FloatingIPDNSTemplate() + serializer = self.floating_ip_dns.FloatingIPDNSTemplate() text = serializer.serialize(dict( dns_entry=dict( ip=test_ipv4_address, diff --git a/nova/tests/fake_policy.py b/nova/tests/fake_policy.py index be5af28071..f9c89f3c72 100644 --- a/nova/tests/fake_policy.py +++ b/nova/tests/fake_policy.py @@ -214,6 +214,7 @@ policy_data = """ "compute_extension:v3:flavor-manage": "", "compute_extension:v3:flavors:discoverable": "", "compute_extension:floating_ip_dns": "", + "compute_extension:v3:os-floating-ip-dns": "", "compute_extension:floating_ip_pools": "", "compute_extension:v3:os-floating-ip-pools": "", "compute_extension:floating_ips": "", diff --git a/nova/tests/integrated/v3/api_samples/os-floating-ip-dns/floating-ip-dns-create-or-update-entry-req.json.tpl b/nova/tests/integrated/v3/api_samples/os-floating-ip-dns/floating-ip-dns-create-or-update-entry-req.json.tpl new file mode 100644 index 0000000000..000c5e1231 --- /dev/null +++ b/nova/tests/integrated/v3/api_samples/os-floating-ip-dns/floating-ip-dns-create-or-update-entry-req.json.tpl @@ -0,0 +1,6 @@ +{ + "dns_entry": { + "ip": "%(ip)s", + "dns_type": "%(dns_type)s" + } +} diff --git a/nova/tests/integrated/v3/api_samples/os-floating-ip-dns/floating-ip-dns-create-or-update-entry-resp.json.tpl b/nova/tests/integrated/v3/api_samples/os-floating-ip-dns/floating-ip-dns-create-or-update-entry-resp.json.tpl new file mode 100644 index 0000000000..3ec0743ba7 --- /dev/null +++ b/nova/tests/integrated/v3/api_samples/os-floating-ip-dns/floating-ip-dns-create-or-update-entry-resp.json.tpl @@ -0,0 +1,9 @@ +{ + "dns_entry": { + "domain": "%(domain)s", + "id": null, + "ip": "%(ip)s", + "name": "%(name)s", + "type": "%(dns_type)s" + } +} diff --git a/nova/tests/integrated/v3/api_samples/os-floating-ip-dns/floating-ip-dns-create-or-update-req.json.tpl b/nova/tests/integrated/v3/api_samples/os-floating-ip-dns/floating-ip-dns-create-or-update-req.json.tpl new file mode 100644 index 0000000000..c2d7976d30 --- /dev/null +++ b/nova/tests/integrated/v3/api_samples/os-floating-ip-dns/floating-ip-dns-create-or-update-req.json.tpl @@ -0,0 +1,7 @@ +{ + "domain_entry": { + "domain": "%(domain)s", + "scope": "%(scope)s", + "project": "%(project)s" + } +} diff --git a/nova/tests/integrated/v3/api_samples/os-floating-ip-dns/floating-ip-dns-create-or-update-resp.json.tpl b/nova/tests/integrated/v3/api_samples/os-floating-ip-dns/floating-ip-dns-create-or-update-resp.json.tpl new file mode 100644 index 0000000000..a14d395d23 --- /dev/null +++ b/nova/tests/integrated/v3/api_samples/os-floating-ip-dns/floating-ip-dns-create-or-update-resp.json.tpl @@ -0,0 +1,8 @@ +{ + "domain_entry": { + "availability_zone": null, + "domain": "%(domain)s", + "project": "%(project)s", + "scope": "%(scope)s" + } +} diff --git a/nova/tests/integrated/v3/api_samples/os-floating-ip-dns/floating-ip-dns-entry-get-resp.json.tpl b/nova/tests/integrated/v3/api_samples/os-floating-ip-dns/floating-ip-dns-entry-get-resp.json.tpl new file mode 100644 index 0000000000..8edd0603f7 --- /dev/null +++ b/nova/tests/integrated/v3/api_samples/os-floating-ip-dns/floating-ip-dns-entry-get-resp.json.tpl @@ -0,0 +1,9 @@ +{ + "dns_entry": { + "domain": "%(domain)s", + "id": null, + "ip": "%(ip)s", + "name": "%(name)s", + "type": null + } +} diff --git a/nova/tests/integrated/v3/api_samples/os-floating-ip-dns/floating-ip-dns-entry-list-resp.json.tpl b/nova/tests/integrated/v3/api_samples/os-floating-ip-dns/floating-ip-dns-entry-list-resp.json.tpl new file mode 100644 index 0000000000..831cda7b55 --- /dev/null +++ b/nova/tests/integrated/v3/api_samples/os-floating-ip-dns/floating-ip-dns-entry-list-resp.json.tpl @@ -0,0 +1,11 @@ +{ + "dns_entries": [ + { + "domain": "%(domain)s", + "id": null, + "ip": "%(ip)s", + "name": "%(name)s", + "type": null + } + ] +} diff --git a/nova/tests/integrated/v3/api_samples/os-floating-ip-dns/floating-ip-dns-list-resp.json.tpl b/nova/tests/integrated/v3/api_samples/os-floating-ip-dns/floating-ip-dns-list-resp.json.tpl new file mode 100644 index 0000000000..a6055cfecc --- /dev/null +++ b/nova/tests/integrated/v3/api_samples/os-floating-ip-dns/floating-ip-dns-list-resp.json.tpl @@ -0,0 +1,10 @@ +{ + "domain_entries": [ + { + "availability_zone": null, + "domain": "%(domain)s", + "project": "%(project)s", + "scope": "%(scope)s" + } + ] +} diff --git a/nova/tests/integrated/v3/test_floating_ip_dns.py b/nova/tests/integrated/v3/test_floating_ip_dns.py new file mode 100644 index 0000000000..b15b6dbb55 --- /dev/null +++ b/nova/tests/integrated/v3/test_floating_ip_dns.py @@ -0,0 +1,91 @@ +# Copyright 2014 IBM Corp. +# +# 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 nova.tests.integrated.v3 import api_sample_base + + +class FloatingIpDNSTest(api_sample_base.ApiSampleTestBaseV3): + extension_name = "os-floating-ip-dns" + + domain = 'domain1.example.org' + name = 'instance1' + scope = 'public' + project = 'project1' + dns_type = 'A' + ip = '192.168.1.1' + + def _create_or_update(self): + subs = {'domain': self.domain, + 'project': self.project, + 'scope': self.scope} + response = self._do_put('os-floating-ip-dns/%s' % self.domain, + 'floating-ip-dns-create-or-update-req', subs) + self._verify_response('floating-ip-dns-create-or-update-resp', subs, + response, 200) + + def _create_or_update_entry(self): + subs = {'ip': self.ip, 'dns_type': self.dns_type} + response = self._do_put('os-floating-ip-dns/%s/entries/%s' + % (self.domain, self.name), + 'floating-ip-dns-create-or-update-entry-req', + subs) + subs.update({'name': self.name, 'domain': self.domain}) + self._verify_response('floating-ip-dns-create-or-update-entry-resp', + subs, response, 200) + + def test_floating_ip_dns_list(self): + self._create_or_update() + response = self._do_get('os-floating-ip-dns') + subs = {'domain': self.domain, + 'project': self.project, + 'scope': self.scope} + self._verify_response('floating-ip-dns-list-resp', subs, + response, 200) + + def test_floating_ip_dns_create_or_update(self): + self._create_or_update() + + def test_floating_ip_dns_delete(self): + self._create_or_update() + response = self._do_delete('os-floating-ip-dns/%s' % self.domain) + self.assertEqual(response.status_code, 202) + + def test_floating_ip_dns_create_or_update_entry(self): + self._create_or_update_entry() + + def test_floating_ip_dns_entry_get(self): + self._create_or_update_entry() + response = self._do_get('os-floating-ip-dns/%s/entries/%s' + % (self.domain, self.name)) + subs = {'domain': self.domain, + 'ip': self.ip, + 'name': self.name} + self._verify_response('floating-ip-dns-entry-get-resp', subs, + response, 200) + + def test_floating_ip_dns_entry_delete(self): + self._create_or_update_entry() + response = self._do_delete('os-floating-ip-dns/%s/entries/%s' + % (self.domain, self.name)) + self.assertEqual(response.status_code, 202) + + def test_floating_ip_dns_entry_list(self): + self._create_or_update_entry() + response = self._do_get('os-floating-ip-dns/%s/entries/%s' + % (self.domain, self.ip)) + subs = {'domain': self.domain, + 'ip': self.ip, + 'name': self.name} + self._verify_response('floating-ip-dns-entry-list-resp', subs, + response, 200) diff --git a/setup.cfg b/setup.cfg index ddd3fdc43c..65da974910 100644 --- a/setup.cfg +++ b/setup.cfg @@ -87,6 +87,7 @@ nova.api.v3.extensions = flavor_access = nova.api.openstack.compute.plugins.v3.flavor_access:FlavorAccess flavor_rxtx = nova.api.openstack.compute.plugins.v3.flavor_rxtx:FlavorRxtx flavor_manage = nova.api.openstack.compute.plugins.v3.flavor_manage:FlavorManage + floating_ip_dns = nova.api.openstack.compute.plugins.v3.floating_ip_dns:FloatingIpDns floating_ip_pools = nova.api.openstack.compute.plugins.v3.floating_ip_pools:FloatingIpPools floating_ips_bulk = nova.api.openstack.compute.plugins.v3.floating_ips_bulk:FloatingIpsBulk fping = nova.api.openstack.compute.plugins.v3.fping:Fping