From 9e703aec2fe32668f2e9e4bfd19350d35a3783d4 Mon Sep 17 00:00:00 2001 From: Jens Harbott Date: Fri, 13 Apr 2018 10:20:16 +0000 Subject: [PATCH] Add DNS parameters to Floating IP panels This adds support for the "DNS Domain" and "DNS Name" options to the panels for listing and allocation Floating IPs. These options are available as a Neutron extension, so we need to make them conditional based on the availability of that extension. Change-Id: Ife8e19aac44dccbdf11a6a6d4bb50cd3b7ed8d8e --- openstack_dashboard/api/neutron.py | 6 ++- openstack_dashboard/api/rest/network.py | 8 +++- .../dashboards/admin/floating_ips/tests.py | 38 +++++++++++++++++-- .../dashboards/project/floating_ips/forms.py | 21 +++++++++- .../dashboards/project/floating_ips/tables.py | 10 +++++ .../dashboards/project/floating_ips/tests.py | 23 ++++++++++- .../test/unit/api/rest/test_network.py | 4 +- ...ng-ip-dns-attributes-d227f98eb5c39396.yaml | 6 +++ 8 files changed, 105 insertions(+), 11 deletions(-) create mode 100644 releasenotes/notes/floating-ip-dns-attributes-d227f98eb5c39396.yaml diff --git a/openstack_dashboard/api/neutron.py b/openstack_dashboard/api/neutron.py index f3b14e29d8..8e99274e83 100644 --- a/openstack_dashboard/api/neutron.py +++ b/openstack_dashboard/api/neutron.py @@ -492,7 +492,7 @@ class SecurityGroupManager(object): class FloatingIp(base.APIDictWrapper): _attrs = ['id', 'ip', 'fixed_ip', 'port_id', 'instance_id', - 'instance_type', 'pool'] + 'instance_type', 'pool', 'dns_domain', 'dns_name'] def __init__(self, fip): fip['ip'] = fip['floating_ip_address'] @@ -631,6 +631,10 @@ class FloatingIpManager(object): create_dict['floating_ip_address'] = params['floating_ip_address'] if 'description' in params: create_dict['description'] = params['description'] + if 'dns_domain' in params: + create_dict['dns_domain'] = params['dns_domain'] + if 'dns_name' in params: + create_dict['dns_name'] = params['dns_name'] fip = self.client.create_floatingip( {'floatingip': create_dict}).get('floatingip') self._set_instance_info(fip) diff --git a/openstack_dashboard/api/rest/network.py b/openstack_dashboard/api/rest/network.py index 850dee9d0e..bf7872be43 100644 --- a/openstack_dashboard/api/rest/network.py +++ b/openstack_dashboard/api/rest/network.py @@ -61,7 +61,13 @@ class FloatingIP(generic.View): :return: JSON representation of the new floating IP address """ pool = request.DATA['pool_id'] - result = api.neutron.tenant_floating_ip_allocate(request, pool) + params = {} + if 'dns_domain' in request.DATA: + params['dns_domain'] = request.DATA['dns_domain'] + if 'dns_name' in request.DATA: + params['dns_name'] = request.DATA['dns_name'] + result = api.neutron.tenant_floating_ip_allocate(request, pool, + None, params) return result.to_dict() @rest_utils.ajax(data_required=True) diff --git a/openstack_dashboard/dashboards/admin/floating_ips/tests.py b/openstack_dashboard/dashboards/admin/floating_ips/tests.py index fbb1b95b11..efb29b1a53 100644 --- a/openstack_dashboard/dashboards/admin/floating_ips/tests.py +++ b/openstack_dashboard/dashboards/admin/floating_ips/tests.py @@ -27,7 +27,9 @@ class AdminFloatingIpViewTest(test.BaseAdminViewTests): @test.create_mocks({ api.nova: ['server_list'], api.keystone: ['tenant_list'], - api.neutron: ['network_list', 'tenant_floating_ip_list']}) + api.neutron: ['network_list', + 'is_extension_supported', + 'tenant_floating_ip_list']}) def test_index(self): # Use neutron test data fips = self.floating_ips.list() @@ -37,6 +39,7 @@ class AdminFloatingIpViewTest(test.BaseAdminViewTests): self.mock_server_list.return_value = [servers, False] self.mock_tenant_list.return_value = [tenants, False] self.mock_network_list.return_value = self.networks.list() + self.mock_is_extension_supported.return_value = True res = self.client.get(INDEX_URL) self.assertTemplateUsed(res, INDEX_TEMPLATE) @@ -60,14 +63,19 @@ class AdminFloatingIpViewTest(test.BaseAdminViewTests): params = {"router:external": True} self.mock_network_list.assert_called_once_with( test.IsHttpRequest(), **params) + self.mock_is_extension_supported.assert_called_once_with( + test.IsHttpRequest(), 'dns-integration') @test.create_mocks({ - api.neutron: ['tenant_floating_ip_get', 'network_get']}) + api.neutron: ['network_get', + 'is_extension_supported', + 'tenant_floating_ip_get']}) def test_floating_ip_detail_get(self): fip = self.floating_ips.first() network = self.networks.first() self.mock_tenant_floating_ip_get.return_value = fip self.mock_network_get.return_value = network + self.mock_is_extension_supported.return_value = True res = self.client.get(reverse('horizon:admin:floating_ips:detail', args=[fip.id])) @@ -78,6 +86,8 @@ class AdminFloatingIpViewTest(test.BaseAdminViewTests): test.IsHttpRequest(), fip.id) self.mock_network_get.assert_called_once_with( test.IsHttpRequest(), fip.pool) + self.mock_is_extension_supported.assert_called_once_with( + test.IsHttpRequest(), 'dns-integration') @test.create_mocks({api.neutron: ['tenant_floating_ip_get']}) def test_floating_ip_detail_exception(self): @@ -92,23 +102,31 @@ class AdminFloatingIpViewTest(test.BaseAdminViewTests): self.mock_tenant_floating_ip_get.assert_called_once_with( test.IsHttpRequest(), fip.id) - @test.create_mocks({api.neutron: ['tenant_floating_ip_list']}) + @test.create_mocks({api.neutron: ['tenant_floating_ip_list', + 'is_extension_supported']}) def test_index_no_floating_ips(self): self.mock_tenant_floating_ip_list.return_value = [] + self.mock_is_extension_supported.return_value = True res = self.client.get(INDEX_URL) self.assertTemplateUsed(res, INDEX_TEMPLATE) self.mock_tenant_floating_ip_list.assert_called_once_with( test.IsHttpRequest(), all_tenants=True) + self.mock_is_extension_supported.assert_called_once_with( + test.IsHttpRequest(), 'dns-integration') - @test.create_mocks({api.neutron: ['tenant_floating_ip_list']}) + @test.create_mocks({api.neutron: ['tenant_floating_ip_list', + 'is_extension_supported']}) def test_index_error(self): self.mock_tenant_floating_ip_list.side_effect = self.exceptions.neutron + self.mock_is_extension_supported.return_value = True res = self.client.get(INDEX_URL) self.assertTemplateUsed(res, INDEX_TEMPLATE) self.mock_tenant_floating_ip_list.assert_called_once_with( test.IsHttpRequest(), all_tenants=True) + self.mock_is_extension_supported.assert_called_once_with( + test.IsHttpRequest(), 'dns-integration') @test.create_mocks({ api.neutron: ['network_list'], @@ -193,6 +211,7 @@ class AdminFloatingIpViewTest(test.BaseAdminViewTests): @test.create_mocks({ api.neutron: ['tenant_floating_ip_list', 'floating_ip_disassociate', + 'is_extension_supported', 'network_list'], api.nova: ['server_list'], api.keystone: ['tenant_list']}) @@ -207,6 +226,7 @@ class AdminFloatingIpViewTest(test.BaseAdminViewTests): self.mock_tenant_list.return_value = [tenants, False] self.mock_network_list.return_value = self.networks.list() self.mock_floating_ip_disassociate.return_value = None + self.mock_is_extension_supported.return_value = True form_data = { "action": @@ -226,9 +246,12 @@ class AdminFloatingIpViewTest(test.BaseAdminViewTests): test.IsHttpRequest(), **params) self.mock_floating_ip_disassociate.assert_called_once_with( test.IsHttpRequest(), floating_ip.id) + self.mock_is_extension_supported.assert_called_once_with( + test.IsHttpRequest(), 'dns-integration') @test.create_mocks({ api.neutron: ['tenant_floating_ip_list', + 'is_extension_supported', 'network_list'], api.nova: ['server_list'], api.keystone: ['tenant_list']}) @@ -242,6 +265,7 @@ class AdminFloatingIpViewTest(test.BaseAdminViewTests): self.mock_server_list.return_value = [servers, False] self.mock_tenant_list.return_value = [tenants, False] self.mock_network_list.return_value = self.networks.list() + self.mock_is_extension_supported.return_value = True form_data = { "action": @@ -259,9 +283,12 @@ class AdminFloatingIpViewTest(test.BaseAdminViewTests): params = {"router:external": True} self.mock_network_list.assert_called_once_with( test.IsHttpRequest(), **params) + self.mock_is_extension_supported.assert_called_once_with( + test.IsHttpRequest(), 'dns-integration') @test.create_mocks({ api.neutron: ['tenant_floating_ip_list', + 'is_extension_supported', 'network_list'], api.nova: ['server_list'], api.keystone: ['tenant_list']}) @@ -274,6 +301,7 @@ class AdminFloatingIpViewTest(test.BaseAdminViewTests): self.mock_server_list.return_value = [servers, False] self.mock_tenant_list.return_value = [tenants, False] self.mock_network_list.return_value = self.networks.list() + self.mock_is_extension_supported.return_value = True res = self.client.get(INDEX_URL) self.assertTemplateUsed(res, INDEX_TEMPLATE) @@ -297,3 +325,5 @@ class AdminFloatingIpViewTest(test.BaseAdminViewTests): params = {"router:external": True} self.mock_network_list.assert_called_once_with( test.IsHttpRequest(), **params) + self.mock_is_extension_supported.assert_called_once_with( + test.IsHttpRequest(), 'dns-integration') diff --git a/openstack_dashboard/dashboards/project/floating_ips/forms.py b/openstack_dashboard/dashboards/project/floating_ips/forms.py index 27b662c38f..7b9964583f 100644 --- a/openstack_dashboard/dashboards/project/floating_ips/forms.py +++ b/openstack_dashboard/dashboards/project/floating_ips/forms.py @@ -32,12 +32,25 @@ class FloatingIpAllocate(forms.SelfHandlingForm): description = forms.CharField(max_length=255, label=_("Description"), required=False) + dns_domain = forms.CharField(max_length=255, + label=_("DNS Domain"), + required=False) + dns_name = forms.CharField(max_length=255, + label=_("DNS Name"), + required=False) - def __init__(self, *args, **kwargs): - super(FloatingIpAllocate, self).__init__(*args, **kwargs) + def __init__(self, request, *args, **kwargs): + super(FloatingIpAllocate, self).__init__(request, *args, **kwargs) floating_pool_list = kwargs.get('initial', {}).get('pool_list', []) self.fields['pool'].choices = floating_pool_list + dns_supported = api.neutron.is_extension_supported( + request, + "dns-integration") + if not dns_supported: + del self.fields["dns_name"] + del self.fields["dns_domain"] + def handle(self, request, data): try: # Prevent allocating more IP than the quota allows @@ -52,6 +65,10 @@ class FloatingIpAllocate(forms.SelfHandlingForm): param = {} if data['description']: param['description'] = data['description'] + if 'dns_domain' in data and data['dns_domain']: + param['dns_domain'] = data['dns_domain'] + if 'dns_name' in data and data['dns_name']: + param['dns_name'] = data['dns_name'] fip = api.neutron.tenant_floating_ip_allocate( request, pool=data['pool'], diff --git a/openstack_dashboard/dashboards/project/floating_ips/tables.py b/openstack_dashboard/dashboards/project/floating_ips/tables.py index 9e4e59ea2f..c1926e76b7 100644 --- a/openstack_dashboard/dashboards/project/floating_ips/tables.py +++ b/openstack_dashboard/dashboards/project/floating_ips/tables.py @@ -189,6 +189,10 @@ class FloatingIPsTable(tables.DataTable): attrs={'data-type': "ip"}) description = tables.Column("description", verbose_name=_("Description")) + dns_name = tables.Column("dns_name", + verbose_name=_("DNS Name")) + dns_domain = tables.Column("dns_domain", + verbose_name=_("DNS Domain")) fixed_ip = tables.Column(get_instance_info, link=get_instance_link, verbose_name=_("Mapped Fixed IP Address")) @@ -204,6 +208,12 @@ class FloatingIPsTable(tables.DataTable): super(FloatingIPsTable, self).__init__( request, data=data, needs_form_wrapper=needs_form_wrapper, **kwargs) + dns_supported = api.neutron.is_extension_supported( + request, + "dns-integration") + if not dns_supported: + del self.columns["dns_name"] + del self.columns["dns_domain"] def sanitize_id(self, obj_id): return filters.get_int_or_uuid(obj_id) diff --git a/openstack_dashboard/dashboards/project/floating_ips/tests.py b/openstack_dashboard/dashboards/project/floating_ips/tests.py index c317884012..e2c8059ec2 100644 --- a/openstack_dashboard/dashboards/project/floating_ips/tests.py +++ b/openstack_dashboard/dashboards/project/floating_ips/tests.py @@ -214,10 +214,12 @@ class FloatingIpViewTests(test.TestCase): @test.create_mocks({api.nova: ('server_list',), api.neutron: ('floating_ip_disassociate', 'floating_ip_pools_list', + 'is_extension_supported', 'tenant_floating_ip_list')}) def test_disassociate_post(self): floating_ip = self.floating_ips.first() + self.mock_is_extension_supported.return_value = False self.mock_server_list.return_value = [self.servers.list(), False] self.mock_tenant_floating_ip_list.return_value = \ self.floating_ips.list() @@ -237,14 +239,18 @@ class FloatingIpViewTests(test.TestCase): test.IsHttpRequest()) self.mock_floating_ip_disassociate.assert_called_once_with( test.IsHttpRequest(), floating_ip.id) + self.mock_is_extension_supported.assert_called_once_with( + test.IsHttpRequest(), 'dns-integration') @test.create_mocks({api.nova: ('server_list',), api.neutron: ('floating_ip_disassociate', 'floating_ip_pools_list', + 'is_extension_supported', 'tenant_floating_ip_list')}) def test_disassociate_post_with_exception(self): floating_ip = self.floating_ips.first() + self.mock_is_extension_supported.return_value = False self.mock_server_list.return_value = [self.servers.list(), False] self.mock_tenant_floating_ip_list.return_value = \ self.floating_ips.list() @@ -263,8 +269,11 @@ class FloatingIpViewTests(test.TestCase): test.IsHttpRequest()) self.mock_floating_ip_disassociate.assert_called_once_with( test.IsHttpRequest(), floating_ip.id) + self.mock_is_extension_supported.assert_called_once_with( + test.IsHttpRequest(), 'dns-integration') @test.create_mocks({api.neutron: ('tenant_floating_ip_list', + 'is_extension_supported', 'floating_ip_pools_list'), api.nova: ('server_list',), quotas: ('tenant_quota_usages',)}) @@ -273,6 +282,7 @@ class FloatingIpViewTests(test.TestCase): floating_pools = self.pools.list() quota_data = self.neutron_quota_usages.first() + self.mock_is_extension_supported.return_value = False self.mock_tenant_floating_ip_list.return_value = floating_ips self.mock_floating_ip_pools_list.return_value = floating_pools self.mock_server_list.return_value = [self.servers.list(), False] @@ -299,8 +309,12 @@ class FloatingIpViewTests(test.TestCase): self.assert_mock_multiple_calls_with_same_arguments( self.mock_tenant_quota_usages, 3, mock.call(test.IsHttpRequest(), targets=('floatingip', ))) + self.mock_is_extension_supported.assert_called_once_with( + test.IsHttpRequest(), 'dns-integration', + ) @test.create_mocks({api.neutron: ('tenant_floating_ip_list', + 'is_extension_supported', 'floating_ip_pools_list'), api.nova: ('server_list',), quotas: ('tenant_quota_usages',)}) @@ -310,6 +324,7 @@ class FloatingIpViewTests(test.TestCase): quota_data = self.neutron_quota_usages.first() quota_data['floatingip']['available'] = 0 + self.mock_is_extension_supported.return_value = False self.mock_tenant_floating_ip_list.return_value = floating_ips self.mock_floating_ip_pools_list.return_value = floating_pools self.mock_server_list.return_value = [self.servers.list(), False] @@ -333,6 +348,9 @@ class FloatingIpViewTests(test.TestCase): self.assert_mock_multiple_calls_with_same_arguments( self.mock_tenant_quota_usages, 3, mock.call(test.IsHttpRequest(), targets=('floatingip', ))) + self.mock_is_extension_supported.assert_called_once_with( + test.IsHttpRequest(), 'dns-integration', + ) @test.create_mocks({api.neutron: ('floating_ip_pools_list', 'tenant_floating_ip_list', @@ -343,7 +361,7 @@ class FloatingIpViewTests(test.TestCase): @test.update_settings(OPENSTACK_NEUTRON_NETWORK={'enable_quotas': True}) def test_correct_quotas_displayed(self): self.mock_is_service_enabled.return_value = True - self.mock_is_extension_supported.side_effect = [True, False] + self.mock_is_extension_supported.side_effect = [False, True, False] self.mock_is_router_enabled.return_value = True self.mock_tenant_quota_get.return_value = self.neutron_quotas.first() self.mock_tenant_floating_ip_list.return_value = \ @@ -357,8 +375,9 @@ class FloatingIpViewTests(test.TestCase): self.mock_is_service_enabled.assert_called_once_with( test.IsHttpRequest(), 'network') - self.assertEqual(2, self.mock_is_extension_supported.call_count) + self.assertEqual(3, self.mock_is_extension_supported.call_count) self.mock_is_extension_supported.assert_has_calls([ + mock.call(test.IsHttpRequest(), 'dns-integration'), mock.call(test.IsHttpRequest(), 'quotas'), mock.call(test.IsHttpRequest(), 'quota_details'), ]) diff --git a/openstack_dashboard/test/unit/api/rest/test_network.py b/openstack_dashboard/test/unit/api/rest/test_network.py index ea8e82329d..05d30f0ff0 100644 --- a/openstack_dashboard/test/unit/api/rest/test_network.py +++ b/openstack_dashboard/test/unit/api/rest/test_network.py @@ -75,7 +75,9 @@ class RestNetworkApiFloatingIpTests(test.TestCase): self.assertStatusCode(response, 200) self.assertEqual(response.json, fip.to_dict()) self.mock_tenant_floating_ip_allocate.assert_called_once_with(request, - 'pool') + 'pool', + None, + {}) @test.create_mocks({api.neutron: ['floating_ip_associate']}) def test_associate_floating_ip(self): diff --git a/releasenotes/notes/floating-ip-dns-attributes-d227f98eb5c39396.yaml b/releasenotes/notes/floating-ip-dns-attributes-d227f98eb5c39396.yaml new file mode 100644 index 0000000000..f20147437d --- /dev/null +++ b/releasenotes/notes/floating-ip-dns-attributes-d227f98eb5c39396.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + Support has been added to set and display DNS attributes for Floating IPs + (DNS Name and DNS Domain). These attributes are only available if Neutron + has the `dns-integration` extension enabled.