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.