diff --git a/openstack_dashboard/api/neutron.py b/openstack_dashboard/api/neutron.py index 3ca9f77ea..e13281021 100644 --- a/openstack_dashboard/api/neutron.py +++ b/openstack_dashboard/api/neutron.py @@ -1064,6 +1064,12 @@ def list_l3_agent_hosting_router(request, router, **params): return [Agent(a) for a in agents['agents']] +def show_network_ip_availability(request, network_id): + ip_availability = neutronclient(request).show_network_ip_availability( + network_id) + return ip_availability + + def add_network_to_dhcp_agent(request, dhcp_agent, network_id): body = {'network_id': network_id} return neutronclient(request).add_network_to_dhcp_agent(dhcp_agent, body) diff --git a/openstack_dashboard/dashboards/admin/networks/agents/tests.py b/openstack_dashboard/dashboards/admin/networks/agents/tests.py index ef088c2e1..f335c8e23 100644 --- a/openstack_dashboard/dashboards/admin/networks/agents/tests.py +++ b/openstack_dashboard/dashboards/admin/networks/agents/tests.py @@ -109,6 +109,7 @@ class NetworkAgentTests(test.BaseAdminViewTests): @test.create_stubs({api.neutron: ('subnet_list', 'port_list', 'list_dhcp_agent_hosting_networks', + 'show_network_ip_availability', 'is_extension_supported', 'remove_network_from_dhcp_agent',)}) def test_agent_delete(self): @@ -123,9 +124,15 @@ class NetworkAgentTests(test.BaseAdminViewTests): .AndReturn([self.ports.first()]) api.neutron.remove_network_from_dhcp_agent(IsA(http.HttpRequest), agent_id, network_id) + api.neutron.is_extension_supported( + IsA(http.HttpRequest), + 'network-ip-availability').AndReturn(True) api.neutron.is_extension_supported(IsA(http.HttpRequest), 'mac-learning')\ .AndReturn(False) + api.neutron.is_extension_supported(IsA(http.HttpRequest), + 'network-ip-availability')\ + .AndReturn(True) api.neutron.is_extension_supported(IsA(http.HttpRequest), 'dhcp_agent_scheduler')\ .AndReturn(True) @@ -140,6 +147,7 @@ class NetworkAgentTests(test.BaseAdminViewTests): @test.create_stubs({api.neutron: ('subnet_list', 'port_list', 'list_dhcp_agent_hosting_networks', + 'show_network_ip_availability', 'is_extension_supported', 'remove_network_from_dhcp_agent',)}) def test_agent_delete_exception(self): @@ -155,9 +163,15 @@ class NetworkAgentTests(test.BaseAdminViewTests): api.neutron.remove_network_from_dhcp_agent(IsA(http.HttpRequest), agent_id, network_id)\ .AndRaise(self.exceptions.neutron) + api.neutron.is_extension_supported( + IsA(http.HttpRequest), + 'network-ip-availability').AndReturn(True) api.neutron.is_extension_supported(IsA(http.HttpRequest), 'mac-learning')\ .AndReturn(False) + api.neutron.is_extension_supported(IsA(http.HttpRequest), + 'network-ip-availability')\ + .AndReturn(True) api.neutron.is_extension_supported(IsA(http.HttpRequest), 'dhcp_agent_scheduler')\ .AndReturn(True) diff --git a/openstack_dashboard/dashboards/admin/networks/ports/tests.py b/openstack_dashboard/dashboards/admin/networks/ports/tests.py index 8e1ee2fe4..44c426b67 100644 --- a/openstack_dashboard/dashboards/admin/networks/ports/tests.py +++ b/openstack_dashboard/dashboards/admin/networks/ports/tests.py @@ -376,6 +376,7 @@ class NetworkPortTests(test.BaseAdminViewTests): @test.create_stubs({api.neutron: ('port_delete', 'subnet_list', 'port_list', + 'show_network_ip_availability', 'is_extension_supported', 'list_dhcp_agent_hosting_networks',)}) def test_port_delete(self): @@ -384,6 +385,7 @@ class NetworkPortTests(test.BaseAdminViewTests): @test.create_stubs({api.neutron: ('port_delete', 'subnet_list', 'port_list', + 'show_network_ip_availability', 'is_extension_supported', 'list_dhcp_agent_hosting_networks',)}) def test_port_delete_with_mac_learning(self): @@ -400,9 +402,15 @@ class NetworkPortTests(test.BaseAdminViewTests): .AndReturn([self.subnets.first()]) api.neutron.port_list(IsA(http.HttpRequest), network_id=network_id)\ .AndReturn([self.ports.first()]) + api.neutron.is_extension_supported( + IsA(http.HttpRequest), + 'network-ip-availability').AndReturn(True) api.neutron.is_extension_supported(IsA(http.HttpRequest), 'mac-learning')\ .AndReturn(mac_learning) + api.neutron.is_extension_supported(IsA(http.HttpRequest), + 'network-ip-availability')\ + .AndReturn(True) api.neutron.is_extension_supported(IsA(http.HttpRequest), 'dhcp_agent_scheduler')\ .AndReturn(True) @@ -417,6 +425,7 @@ class NetworkPortTests(test.BaseAdminViewTests): @test.create_stubs({api.neutron: ('port_delete', 'subnet_list', 'port_list', + 'show_network_ip_availability', 'is_extension_supported', 'list_dhcp_agent_hosting_networks',)}) def test_port_delete_exception(self): @@ -425,6 +434,7 @@ class NetworkPortTests(test.BaseAdminViewTests): @test.create_stubs({api.neutron: ('port_delete', 'subnet_list', 'port_list', + 'show_network_ip_availability', 'is_extension_supported', 'list_dhcp_agent_hosting_networks')}) def test_port_delete_exception_with_mac_learning(self): @@ -442,9 +452,15 @@ class NetworkPortTests(test.BaseAdminViewTests): .AndReturn([self.subnets.first()]) api.neutron.port_list(IsA(http.HttpRequest), network_id=network_id)\ .AndReturn([self.ports.first()]) + api.neutron.is_extension_supported( + IsA(http.HttpRequest), + 'network-ip-availability').AndReturn(True) api.neutron.is_extension_supported(IsA(http.HttpRequest), 'mac-learning')\ .AndReturn(mac_learning) + api.neutron.is_extension_supported(IsA(http.HttpRequest), + 'network-ip-availability')\ + .AndReturn(True) api.neutron.is_extension_supported(IsA(http.HttpRequest), 'dhcp_agent_scheduler')\ .AndReturn(True) diff --git a/openstack_dashboard/dashboards/admin/networks/subnets/tables.py b/openstack_dashboard/dashboards/admin/networks/subnets/tables.py index d0f91486f..bfe37aefa 100644 --- a/openstack_dashboard/dashboards/admin/networks/subnets/tables.py +++ b/openstack_dashboard/dashboards/admin/networks/subnets/tables.py @@ -88,12 +88,27 @@ class UpdateSubnet(proj_tables.SubnetPolicyTargetMixin, tables.LinkAction): return reverse(self.url, args=(network_id, subnet.id)) +def subnet_ip_availability(availability): + subnet_availability = availability.get("free_ips") + if subnet_availability: + if subnet_availability > 10000: + return ">10000" + else: + return str(subnet_availability) + else: + return str("Not Available") + + class SubnetsTable(tables.DataTable): name = tables.Column("name_or_id", verbose_name=_("Name"), link='horizon:admin:networks:subnets:detail') cidr = tables.Column("cidr", verbose_name=_("CIDR")) ip_version = tables.Column("ipver_str", verbose_name=_("IP Version")) gateway_ip = tables.Column("gateway_ip", verbose_name=_("Gateway IP")) + subnet_used_ips = tables.Column("used_ips", + verbose_name=_("Used IPs")) + subnet_free_ips = tables.Column(subnet_ip_availability, + verbose_name=_("Free IPs")) failure_url = reverse_lazy('horizon:admin:networks:index') def get_object_display(self, subnet): @@ -117,3 +132,13 @@ class SubnetsTable(tables.DataTable): table_actions = (CreateSubnet, DeleteSubnet) row_actions = (UpdateSubnet, DeleteSubnet,) hidden_title = False + + def __init__(self, request, data=None, needs_form_wrapper=None, **kwargs): + super(SubnetsTable, self).__init__( + request, data=data, + needs_form_wrapper=needs_form_wrapper, + **kwargs) + if not api.neutron.is_extension_supported(request, + 'network-ip-availability'): + del self.columns['subnet_used_ips'] + del self.columns['subnet_free_ips'] diff --git a/openstack_dashboard/dashboards/admin/networks/subnets/tests.py b/openstack_dashboard/dashboards/admin/networks/subnets/tests.py index 43d97945c..ff814ef76 100644 --- a/openstack_dashboard/dashboards/admin/networks/subnets/tests.py +++ b/openstack_dashboard/dashboards/admin/networks/subnets/tests.py @@ -31,7 +31,9 @@ NETWORKS_DETAIL_URL = 'horizon:admin:networks:detail' class NetworkSubnetTests(test.BaseAdminViewTests): - @test.create_stubs({api.neutron: ('network_get', 'subnet_get',)}) + @test.create_stubs({api.neutron: ('network_get', + 'subnet_get', + 'is_extension_supported')}) def test_subnet_detail(self): network = self.networks.first() subnet = self.subnets.first() @@ -41,6 +43,10 @@ class NetworkSubnetTests(test.BaseAdminViewTests): api.neutron.subnet_get(IsA(http.HttpRequest), subnet.id)\ .AndReturn(subnet) + api.neutron.is_extension_supported( + IsA(http.HttpRequest), + 'network-ip-availability').AndReturn(True) + self.mox.ReplayAll() url = reverse(DETAIL_URL, args=[subnet.id]) @@ -292,6 +298,7 @@ class NetworkSubnetTests(test.BaseAdminViewTests): 'subnet_list', 'port_list', 'is_extension_supported', + 'show_network_ip_availability', 'list_dhcp_agent_hosting_networks',)}) def test_subnet_delete(self): self._test_subnet_delete() @@ -300,6 +307,7 @@ class NetworkSubnetTests(test.BaseAdminViewTests): 'subnet_list', 'port_list', 'is_extension_supported', + 'show_network_ip_availability', 'list_dhcp_agent_hosting_networks',)}) def test_subnet_delete_with_mac_learning(self): self._test_subnet_delete(mac_learning=True) @@ -307,6 +315,10 @@ class NetworkSubnetTests(test.BaseAdminViewTests): def _test_subnet_delete(self, mac_learning=False): subnet = self.subnets.first() network_id = subnet.network_id + ip_availability = self.ip_availability.get() + api.neutron.show_network_ip_availability(IsA(http.HttpRequest), + network_id).\ + MultipleTimes().AndReturn(ip_availability) api.neutron.list_dhcp_agent_hosting_networks(IsA(http.HttpRequest), network_id).\ AndReturn(self.agents.list()) @@ -315,9 +327,15 @@ class NetworkSubnetTests(test.BaseAdminViewTests): .AndReturn([self.subnets.first()]) api.neutron.port_list(IsA(http.HttpRequest), network_id=network_id)\ .AndReturn([self.ports.first()]) + api.neutron.is_extension_supported( + IsA(http.HttpRequest), + 'network-ip-availability').AndReturn(True) api.neutron.is_extension_supported(IsA(http.HttpRequest), 'mac-learning')\ .AndReturn(mac_learning) + api.neutron.is_extension_supported(IsA(http.HttpRequest), + 'network-ip-availability')\ + .MultipleTimes().AndReturn(True) api.neutron.is_extension_supported(IsA(http.HttpRequest), 'dhcp_agent_scheduler')\ .AndReturn(True) @@ -333,6 +351,7 @@ class NetworkSubnetTests(test.BaseAdminViewTests): 'subnet_list', 'port_list', 'is_extension_supported', + 'show_network_ip_availability', 'list_dhcp_agent_hosting_networks',)}) def test_subnet_delete_exception(self): self._test_subnet_delete_exception() @@ -341,6 +360,7 @@ class NetworkSubnetTests(test.BaseAdminViewTests): 'subnet_list', 'port_list', 'is_extension_supported', + 'show_network_ip_availability', 'list_dhcp_agent_hosting_networks',)}) def test_subnet_delete_exception_with_mac_learning(self): self._test_subnet_delete_exception(mac_learning=True) @@ -348,6 +368,10 @@ class NetworkSubnetTests(test.BaseAdminViewTests): def _test_subnet_delete_exception(self, mac_learning=False): subnet = self.subnets.first() network_id = subnet.network_id + ip_availability = self.ip_availability.get() + api.neutron.show_network_ip_availability(IsA(http.HttpRequest), + network_id).\ + MultipleTimes().AndReturn(ip_availability) api.neutron.list_dhcp_agent_hosting_networks(IsA(http.HttpRequest), network_id).\ AndReturn(self.agents.list()) @@ -357,9 +381,15 @@ class NetworkSubnetTests(test.BaseAdminViewTests): .AndReturn([self.subnets.first()]) api.neutron.port_list(IsA(http.HttpRequest), network_id=network_id)\ .AndReturn([self.ports.first()]) + api.neutron.is_extension_supported( + IsA(http.HttpRequest), + 'network-ip-availability').AndReturn(True) api.neutron.is_extension_supported(IsA(http.HttpRequest), 'mac-learning')\ .AndReturn(mac_learning) + api.neutron.is_extension_supported(IsA(http.HttpRequest), + 'network-ip-availability')\ + .MultipleTimes().AndReturn(True) api.neutron.is_extension_supported(IsA(http.HttpRequest), 'dhcp_agent_scheduler')\ .AndReturn(True) @@ -370,3 +400,57 @@ class NetworkSubnetTests(test.BaseAdminViewTests): res = self.client.post(url, form_data) self.assertRedirectsNoFollow(res, url) + + @test.create_stubs({api.neutron: ('network_get', + 'subnet_list', + 'port_list', + 'is_extension_supported', + 'show_network_ip_availability', + 'list_dhcp_agent_hosting_networks',)}) + def test_network_detail_ip_availability_exception(self): + self._test_network_detail_ip_availability_exception() + + @test.create_stubs({api.neutron: ('network_get', + 'subnet_list', + 'port_list', + 'is_extension_supported', + 'show_network_ip_availability', + 'list_dhcp_agent_hosting_networks',)}) + def test_network_detail_ip_availability_exception_with_mac_learning(self): + self._test_network_detail_ip_availability_exception(mac_learning=True) + + def _test_network_detail_ip_availability_exception(self, + mac_learning=False): + network_id = self.networks.first().id + api.neutron.show_network_ip_availability(IsA(http.HttpRequest), + network_id).\ + MultipleTimes().AndRaise(self.exceptions.neutron) + api.neutron.list_dhcp_agent_hosting_networks(IsA(http.HttpRequest), + network_id).\ + AndReturn(self.agents.list()) + api.neutron.network_get(IsA(http.HttpRequest), network_id).\ + AndReturn(self.networks.first()) + api.neutron.subnet_list(IsA(http.HttpRequest), network_id=network_id).\ + AndReturn([self.subnets.first()]) + api.neutron.port_list(IsA(http.HttpRequest), network_id=network_id).\ + AndReturn([self.ports.first()]) + api.neutron.is_extension_supported( + IsA(http.HttpRequest), + 'network-ip-availability').AndReturn(True) + api.neutron.is_extension_supported(IsA(http.HttpRequest), + 'mac-learning')\ + .AndReturn(mac_learning) + api.neutron.is_extension_supported( + IsA(http.HttpRequest), + 'network-ip-availability').AndReturn(True) + api.neutron.is_extension_supported(IsA(http.HttpRequest), + 'dhcp_agent_scheduler')\ + .MultipleTimes().AndReturn(True) + self.mox.ReplayAll() + + res = self.client.get(reverse('horizon:admin:networks:detail', + args=[network_id])) + + self.assertTemplateUsed(res, 'project/networks/detail.html') + subnets = res.context['subnets_table'].data + self.assertItemsEqual(subnets, [self.subnets.first()]) diff --git a/openstack_dashboard/dashboards/admin/networks/tables.py b/openstack_dashboard/dashboards/admin/networks/tables.py index f80ac7b52..b7707af77 100644 --- a/openstack_dashboard/dashboards/admin/networks/tables.py +++ b/openstack_dashboard/dashboards/admin/networks/tables.py @@ -78,11 +78,6 @@ class EditNetwork(policy.PolicyTargetMixin, tables.LinkAction): policy_rules = (("network", "update_network"),) -# def _get_subnets(network): -# cidrs = [subnet.get('cidr') for subnet in network.subnets] -# return ','.join(cidrs) - - DISPLAY_CHOICES = ( ("up", pgettext_lazy("Admin state of a Network", u"UP")), ("down", pgettext_lazy("Admin state of a Network", u"DOWN")), diff --git a/openstack_dashboard/dashboards/admin/networks/tests.py b/openstack_dashboard/dashboards/admin/networks/tests.py index c316e79be..e40b1f544 100644 --- a/openstack_dashboard/dashboards/admin/networks/tests.py +++ b/openstack_dashboard/dashboards/admin/networks/tests.py @@ -74,6 +74,7 @@ class NetworkTests(test.BaseAdminViewTests): @test.create_stubs({api.neutron: ('network_get', 'subnet_list', 'port_list', + 'show_network_ip_availability', 'list_dhcp_agent_hosting_networks', 'is_extension_supported')}) def test_network_detail(self): @@ -83,12 +84,17 @@ class NetworkTests(test.BaseAdminViewTests): 'subnet_list', 'port_list', 'is_extension_supported', + 'show_network_ip_availability', 'list_dhcp_agent_hosting_networks',)}) def test_network_detail_with_mac_learning(self): self._test_network_detail(mac_learning=True) def _test_network_detail(self, mac_learning=False): network_id = self.networks.first().id + ip_availability = self.ip_availability.get() + api.neutron.show_network_ip_availability(IsA(http.HttpRequest), + network_id).\ + MultipleTimes().AndReturn(ip_availability) api.neutron.list_dhcp_agent_hosting_networks(IsA(http.HttpRequest), network_id).\ AndReturn(self.agents.list()) @@ -98,9 +104,15 @@ class NetworkTests(test.BaseAdminViewTests): .AndReturn([self.subnets.first()]) api.neutron.port_list(IsA(http.HttpRequest), network_id=network_id)\ .AndReturn([self.ports.first()]) + api.neutron.is_extension_supported( + IsA(http.HttpRequest), + 'network-ip-availability').AndReturn(True) api.neutron.is_extension_supported(IsA(http.HttpRequest), 'mac-learning')\ .AndReturn(mac_learning) + api.neutron.is_extension_supported( + IsA(http.HttpRequest), + 'network-ip-availability').AndReturn(True) api.neutron.is_extension_supported( IsA(http.HttpRequest), 'dhcp_agent_scheduler').AndReturn(True) @@ -126,6 +138,7 @@ class NetworkTests(test.BaseAdminViewTests): 'subnet_list', 'port_list', 'is_extension_supported', + 'show_network_ip_availability', 'list_dhcp_agent_hosting_networks',)}) def test_network_detail_network_exception(self): self._test_network_detail_network_exception() @@ -134,12 +147,17 @@ class NetworkTests(test.BaseAdminViewTests): 'subnet_list', 'port_list', 'is_extension_supported', + 'show_network_ip_availability', 'list_dhcp_agent_hosting_networks',)}) def test_network_detail_network_exception_with_mac_learning(self): self._test_network_detail_network_exception(mac_learning=True) def _test_network_detail_network_exception(self, mac_learning=False): network_id = self.networks.first().id + ip_availability = self.ip_availability.get() + api.neutron.show_network_ip_availability(IsA(http.HttpRequest), + network_id).\ + MultipleTimes().AndReturn(ip_availability) api.neutron.network_get(IsA(http.HttpRequest), network_id)\ .AndRaise(self.exceptions.neutron) api.neutron.subnet_list(IsA(http.HttpRequest), network_id=network_id)\ @@ -149,9 +167,15 @@ class NetworkTests(test.BaseAdminViewTests): api.neutron.list_dhcp_agent_hosting_networks(IsA(http.HttpRequest), network_id).\ AndReturn(self.agents.list()) + api.neutron.is_extension_supported( + IsA(http.HttpRequest), + 'network-ip-availability').AndReturn(True) api.neutron.is_extension_supported(IsA(http.HttpRequest), 'mac-learning')\ .AndReturn(mac_learning) + api.neutron.is_extension_supported( + IsA(http.HttpRequest), + 'network-ip-availability').AndReturn(True) api.neutron.is_extension_supported( IsA(http.HttpRequest), 'dhcp_agent_scheduler').AndReturn(True) @@ -191,6 +215,9 @@ class NetworkTests(test.BaseAdminViewTests): AndRaise(self.exceptions.neutron) api.neutron.port_list(IsA(http.HttpRequest), network_id=network_id).\ AndReturn([self.ports.first()]) + api.neutron.is_extension_supported( + IsA(http.HttpRequest), + 'network-ip-availability').AndReturn(True) api.neutron.is_extension_supported(IsA(http.HttpRequest), 'mac-learning')\ .AndReturn(mac_learning) @@ -219,6 +246,7 @@ class NetworkTests(test.BaseAdminViewTests): 'subnet_list', 'port_list', 'is_extension_supported', + 'show_network_ip_availability', 'list_dhcp_agent_hosting_networks',)}) def test_network_detail_port_exception(self): self._test_network_detail_port_exception() @@ -227,12 +255,17 @@ class NetworkTests(test.BaseAdminViewTests): 'subnet_list', 'port_list', 'is_extension_supported', + 'show_network_ip_availability', 'list_dhcp_agent_hosting_networks',)}) def test_network_detail_port_exception_with_mac_learning(self): self._test_network_detail_port_exception(mac_learning=True) def _test_network_detail_port_exception(self, mac_learning=False): network_id = self.networks.first().id + ip_availability = self.ip_availability.get() + api.neutron.show_network_ip_availability(IsA(http.HttpRequest), + network_id).\ + MultipleTimes().AndReturn(ip_availability) api.neutron.list_dhcp_agent_hosting_networks(IsA(http.HttpRequest), network_id).\ AndReturn(self.agents.list()) @@ -242,9 +275,15 @@ class NetworkTests(test.BaseAdminViewTests): AndReturn([self.subnets.first()]) api.neutron.port_list(IsA(http.HttpRequest), network_id=network_id).\ AndRaise(self.exceptions.neutron) + api.neutron.is_extension_supported( + IsA(http.HttpRequest), + 'network-ip-availability').AndReturn(True) api.neutron.is_extension_supported(IsA(http.HttpRequest), 'mac-learning')\ .AndReturn(mac_learning) + api.neutron.is_extension_supported( + IsA(http.HttpRequest), + 'network-ip-availability').AndReturn(True) api.neutron.is_extension_supported(IsA(http.HttpRequest), 'dhcp_agent_scheduler')\ .AndReturn(True) diff --git a/openstack_dashboard/dashboards/admin/networks/views.py b/openstack_dashboard/dashboards/admin/networks/views.py index f11c739a3..8b3e8a255 100644 --- a/openstack_dashboard/dashboards/admin/networks/views.py +++ b/openstack_dashboard/dashboards/admin/networks/views.py @@ -59,6 +59,7 @@ class IndexView(tables.DataTableView): def _get_agents_data(self, network): agents = [] data = _("Unknown") + try: if api.neutron.is_extension_supported(self.request, 'dhcp_agent_scheduler'): @@ -69,7 +70,8 @@ class IndexView(tables.DataTableView): self.request, network) data = len(agents) except Exception: - self.exception = True + msg = _('Unable to list dhcp agents hosting network.') + exceptions.handle(self.request, msg) return data def get_data(self): @@ -87,10 +89,6 @@ class IndexView(tables.DataTableView): tenant = tenant_dict.get(n.tenant_id, None) n.tenant_name = getattr(tenant, 'name', None) n.num_agents = self._get_agents_data(n.id) - - if self.exception: - msg = _('Unable to list dhcp agents hosting network.') - exceptions.handle(self.request, msg) return networks @@ -108,11 +106,47 @@ class DetailView(tables.MultiTableView): template_name = 'project/networks/detail.html' page_title = '{{ network.name | default:network.id }}' + def _get_subnet_availability(self, network_id): + subnet_availabilities_list = {} + try: + availability = api.neutron.\ + show_network_ip_availability(self.request, network_id) + availabilities = availability.get("network_ip_availability", + {}) + subnet_availabilities_list = availabilities.\ + get("subnet_ip_availability", []) + except Exception: + msg = _("Unable to retrieve IP availability.") + exceptions.handle(self.request, msg) + return subnet_availabilities_list + + def _add_subnet_availability(self, subnet_usage_list, subnets_dict): + try: + for subnet_usage in subnet_usage_list: + subnet_id = subnet_usage.get("subnet_id") + subnet_used_ips = subnet_usage.get("used_ips") + subnet_total_ips = subnet_usage.get("total_ips") + subnet_free_ips = subnet_total_ips - subnet_used_ips + for item in subnets_dict: + id = item.get("id") + if id == subnet_id: + item._apidict.update({"used_ips": subnet_used_ips}) + item._apidict.update({"free_ips": subnet_free_ips}) + except Exception: + msg = _("Unable to update subnets with availability.") + exceptions.handle(self.request, msg) + return subnets_dict + def get_subnets_data(self): try: network_id = self.kwargs['network_id'] subnets = api.neutron.subnet_list(self.request, network_id=network_id) + if api.neutron.is_extension_supported(self.request, + 'network-ip-availability'): + subnets_list = self._get_subnet_availability(network_id) + subnets = self._add_subnet_availability(subnets_list, subnets) + except Exception: subnets = [] msg = _('Subnet list can not be retrieved.') diff --git a/openstack_dashboard/test/api_tests/neutron_tests.py b/openstack_dashboard/test/api_tests/neutron_tests.py index a14a20701..51838dd24 100644 --- a/openstack_dashboard/test/api_tests/neutron_tests.py +++ b/openstack_dashboard/test/api_tests/neutron_tests.py @@ -155,6 +155,35 @@ class NeutronApiTests(test.APITestCase): api.neutron.network_delete(self.request, network_id) + def test_get_network_ip_availability(self): + network = {'network': self.api_networks.first()} + mock_ip_availability = self.ip_availability.get() + neutronclient = self.stub_neutronclient() + neutronclient.show_network_ip_availability(network).\ + AndReturn(mock_ip_availability) + + self.mox.ReplayAll() + ret_val = api.neutron.show_network_ip_availability(self.request, + network) + + self.assertIsInstance(ret_val, dict) + + def test_subnet_network_ip_availability(self): + network = {'network': self.api_networks.first()} + mock_ip_availability = self.ip_availability.get() + neutronclient = self.stub_neutronclient() + neutronclient.show_network_ip_availability(network).\ + AndReturn(mock_ip_availability) + + self.mox.ReplayAll() + ip_availability = api.neutron. \ + show_network_ip_availability(self.request, network) + availabilities = ip_availability.get("network_ip_availability", + {}) + ret_val = availabilities.get("subnet_ip_availability", []) + + self.assertIsInstance(ret_val, list) + def test_subnet_list(self): subnets = {'subnets': self.api_subnets.list()} diff --git a/openstack_dashboard/test/test_data/neutron_data.py b/openstack_dashboard/test/test_data/neutron_data.py index 743d4db0c..b192db11f 100644 --- a/openstack_dashboard/test/test_data/neutron_data.py +++ b/openstack_dashboard/test/test_data/neutron_data.py @@ -55,6 +55,7 @@ def data(TEST): TEST.firewalls = utils.TestDataContainer() TEST.fw_policies = utils.TestDataContainer() TEST.fw_rules = utils.TestDataContainer() + TEST.ip_availability = utils.TestDataContainer() # Data return by neutronclient. TEST.api_agents = utils.TestDataContainer() @@ -83,6 +84,7 @@ def data(TEST): TEST.api_firewalls = utils.TestDataContainer() TEST.api_fw_policies = utils.TestDataContainer() TEST.api_fw_rules = utils.TestDataContainer() + TEST.api_ip_availability = utils.TestDataContainer() # 1st network. network_dict = {'admin_state_up': True, @@ -1317,3 +1319,27 @@ def data(TEST): 'binding:host_id': 'host'} TEST.api_ports.add(port_dict) TEST.ports.add(neutron.Port(port_dict)) + + availability = {'network_ip_availability': { + 'used_ips': 2, + 'subnet_ip_availability': [{ + 'used_ips': 1, + 'subnet_id': '2c90f321-9cc7-41b4-a3cf-88110f120a94', + 'subnet_name': 'ipv6-public-subnet', + 'ip_version': 6, + 'cidr': '2001:db8::/64', + 'total_ips': 18446744073709551614}, + {'used_ips': 1, + 'subnet_id': '4d77d5fb-c26c-4ac5-b2ca-fca2f89b0fc1', + 'subnet_name': 'public-subnet', + 'ip_version': 4, + 'cidr': '172.24.4.0/24', + 'total_ips': 253}], + 'network_id': 'd87d5be5-cfca-486f-8db5-a446330e4513', + 'tenant_id': 'd564b2a4fc0544fb89f8a0434dd96863', + 'network_name': 'public', + 'total_ips': 18446744073709551867} + } + + TEST.ip_availability.add(availability) + TEST.api_ip_availability.add(availability) diff --git a/releasenotes/notes/ip-availability-be217ba59cc02b40.yaml b/releasenotes/notes/ip-availability-be217ba59cc02b40.yaml new file mode 100644 index 000000000..1ff9973df --- /dev/null +++ b/releasenotes/notes/ip-availability-be217ba59cc02b40.yaml @@ -0,0 +1,8 @@ +--- +features: + - Horizon support for network IP availability feature. + Enable Horizon admin network dashboard to be able to + display IP availability. Enables 2 columns in the + admin network subnets table to display the allocated + IPs in a given subnet and unallocated free IPs for + each subnet in the network.