Add Network IP Availability Extension
Network IP Availability has been merged in Neutron, and, as a result, a new extension is available [1]. There is a patch in Neutron for adding this api extension [2] and a patch for adding commands for its usage [3]. This change basically implements the new api that can be used to check network usage stats of all the networks or specific networks created by Neutron. This patch will add the API extensions to Horizon, and specifically, add IP usage stats to the subnets network table. Reference --------- [1] https://review.openstack.org/#/c/180803/ [2] https://review.openstack.org/#/c/212955/ [3] https://review.openstack.org/#/c/269926/ Partially Implements Blueprint: ip-capacity Change-Id: I4b88adedc8de975d4aca9aeeb5622ec3410686ec Co-Authored-By: Dariusz Smigiel <dariusz.smigiel@intel.com>
This commit is contained in:
parent
e5eb260522
commit
0b807ac1d0
@ -1064,6 +1064,12 @@ def list_l3_agent_hosting_router(request, router, **params):
|
|||||||
return [Agent(a) for a in agents['agents']]
|
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):
|
def add_network_to_dhcp_agent(request, dhcp_agent, network_id):
|
||||||
body = {'network_id': network_id}
|
body = {'network_id': network_id}
|
||||||
return neutronclient(request).add_network_to_dhcp_agent(dhcp_agent, body)
|
return neutronclient(request).add_network_to_dhcp_agent(dhcp_agent, body)
|
||||||
|
@ -109,6 +109,7 @@ class NetworkAgentTests(test.BaseAdminViewTests):
|
|||||||
@test.create_stubs({api.neutron: ('subnet_list',
|
@test.create_stubs({api.neutron: ('subnet_list',
|
||||||
'port_list',
|
'port_list',
|
||||||
'list_dhcp_agent_hosting_networks',
|
'list_dhcp_agent_hosting_networks',
|
||||||
|
'show_network_ip_availability',
|
||||||
'is_extension_supported',
|
'is_extension_supported',
|
||||||
'remove_network_from_dhcp_agent',)})
|
'remove_network_from_dhcp_agent',)})
|
||||||
def test_agent_delete(self):
|
def test_agent_delete(self):
|
||||||
@ -123,9 +124,15 @@ class NetworkAgentTests(test.BaseAdminViewTests):
|
|||||||
.AndReturn([self.ports.first()])
|
.AndReturn([self.ports.first()])
|
||||||
api.neutron.remove_network_from_dhcp_agent(IsA(http.HttpRequest),
|
api.neutron.remove_network_from_dhcp_agent(IsA(http.HttpRequest),
|
||||||
agent_id, network_id)
|
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),
|
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||||
'mac-learning')\
|
'mac-learning')\
|
||||||
.AndReturn(False)
|
.AndReturn(False)
|
||||||
|
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||||
|
'network-ip-availability')\
|
||||||
|
.AndReturn(True)
|
||||||
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||||
'dhcp_agent_scheduler')\
|
'dhcp_agent_scheduler')\
|
||||||
.AndReturn(True)
|
.AndReturn(True)
|
||||||
@ -140,6 +147,7 @@ class NetworkAgentTests(test.BaseAdminViewTests):
|
|||||||
@test.create_stubs({api.neutron: ('subnet_list',
|
@test.create_stubs({api.neutron: ('subnet_list',
|
||||||
'port_list',
|
'port_list',
|
||||||
'list_dhcp_agent_hosting_networks',
|
'list_dhcp_agent_hosting_networks',
|
||||||
|
'show_network_ip_availability',
|
||||||
'is_extension_supported',
|
'is_extension_supported',
|
||||||
'remove_network_from_dhcp_agent',)})
|
'remove_network_from_dhcp_agent',)})
|
||||||
def test_agent_delete_exception(self):
|
def test_agent_delete_exception(self):
|
||||||
@ -155,9 +163,15 @@ class NetworkAgentTests(test.BaseAdminViewTests):
|
|||||||
api.neutron.remove_network_from_dhcp_agent(IsA(http.HttpRequest),
|
api.neutron.remove_network_from_dhcp_agent(IsA(http.HttpRequest),
|
||||||
agent_id, network_id)\
|
agent_id, network_id)\
|
||||||
.AndRaise(self.exceptions.neutron)
|
.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),
|
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||||
'mac-learning')\
|
'mac-learning')\
|
||||||
.AndReturn(False)
|
.AndReturn(False)
|
||||||
|
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||||
|
'network-ip-availability')\
|
||||||
|
.AndReturn(True)
|
||||||
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||||
'dhcp_agent_scheduler')\
|
'dhcp_agent_scheduler')\
|
||||||
.AndReturn(True)
|
.AndReturn(True)
|
||||||
|
@ -376,6 +376,7 @@ class NetworkPortTests(test.BaseAdminViewTests):
|
|||||||
@test.create_stubs({api.neutron: ('port_delete',
|
@test.create_stubs({api.neutron: ('port_delete',
|
||||||
'subnet_list',
|
'subnet_list',
|
||||||
'port_list',
|
'port_list',
|
||||||
|
'show_network_ip_availability',
|
||||||
'is_extension_supported',
|
'is_extension_supported',
|
||||||
'list_dhcp_agent_hosting_networks',)})
|
'list_dhcp_agent_hosting_networks',)})
|
||||||
def test_port_delete(self):
|
def test_port_delete(self):
|
||||||
@ -384,6 +385,7 @@ class NetworkPortTests(test.BaseAdminViewTests):
|
|||||||
@test.create_stubs({api.neutron: ('port_delete',
|
@test.create_stubs({api.neutron: ('port_delete',
|
||||||
'subnet_list',
|
'subnet_list',
|
||||||
'port_list',
|
'port_list',
|
||||||
|
'show_network_ip_availability',
|
||||||
'is_extension_supported',
|
'is_extension_supported',
|
||||||
'list_dhcp_agent_hosting_networks',)})
|
'list_dhcp_agent_hosting_networks',)})
|
||||||
def test_port_delete_with_mac_learning(self):
|
def test_port_delete_with_mac_learning(self):
|
||||||
@ -400,9 +402,15 @@ class NetworkPortTests(test.BaseAdminViewTests):
|
|||||||
.AndReturn([self.subnets.first()])
|
.AndReturn([self.subnets.first()])
|
||||||
api.neutron.port_list(IsA(http.HttpRequest), network_id=network_id)\
|
api.neutron.port_list(IsA(http.HttpRequest), network_id=network_id)\
|
||||||
.AndReturn([self.ports.first()])
|
.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),
|
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||||
'mac-learning')\
|
'mac-learning')\
|
||||||
.AndReturn(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),
|
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||||
'dhcp_agent_scheduler')\
|
'dhcp_agent_scheduler')\
|
||||||
.AndReturn(True)
|
.AndReturn(True)
|
||||||
@ -417,6 +425,7 @@ class NetworkPortTests(test.BaseAdminViewTests):
|
|||||||
@test.create_stubs({api.neutron: ('port_delete',
|
@test.create_stubs({api.neutron: ('port_delete',
|
||||||
'subnet_list',
|
'subnet_list',
|
||||||
'port_list',
|
'port_list',
|
||||||
|
'show_network_ip_availability',
|
||||||
'is_extension_supported',
|
'is_extension_supported',
|
||||||
'list_dhcp_agent_hosting_networks',)})
|
'list_dhcp_agent_hosting_networks',)})
|
||||||
def test_port_delete_exception(self):
|
def test_port_delete_exception(self):
|
||||||
@ -425,6 +434,7 @@ class NetworkPortTests(test.BaseAdminViewTests):
|
|||||||
@test.create_stubs({api.neutron: ('port_delete',
|
@test.create_stubs({api.neutron: ('port_delete',
|
||||||
'subnet_list',
|
'subnet_list',
|
||||||
'port_list',
|
'port_list',
|
||||||
|
'show_network_ip_availability',
|
||||||
'is_extension_supported',
|
'is_extension_supported',
|
||||||
'list_dhcp_agent_hosting_networks')})
|
'list_dhcp_agent_hosting_networks')})
|
||||||
def test_port_delete_exception_with_mac_learning(self):
|
def test_port_delete_exception_with_mac_learning(self):
|
||||||
@ -442,9 +452,15 @@ class NetworkPortTests(test.BaseAdminViewTests):
|
|||||||
.AndReturn([self.subnets.first()])
|
.AndReturn([self.subnets.first()])
|
||||||
api.neutron.port_list(IsA(http.HttpRequest), network_id=network_id)\
|
api.neutron.port_list(IsA(http.HttpRequest), network_id=network_id)\
|
||||||
.AndReturn([self.ports.first()])
|
.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),
|
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||||
'mac-learning')\
|
'mac-learning')\
|
||||||
.AndReturn(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),
|
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||||
'dhcp_agent_scheduler')\
|
'dhcp_agent_scheduler')\
|
||||||
.AndReturn(True)
|
.AndReturn(True)
|
||||||
|
@ -88,12 +88,27 @@ class UpdateSubnet(proj_tables.SubnetPolicyTargetMixin, tables.LinkAction):
|
|||||||
return reverse(self.url, args=(network_id, subnet.id))
|
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):
|
class SubnetsTable(tables.DataTable):
|
||||||
name = tables.Column("name_or_id", verbose_name=_("Name"),
|
name = tables.Column("name_or_id", verbose_name=_("Name"),
|
||||||
link='horizon:admin:networks:subnets:detail')
|
link='horizon:admin:networks:subnets:detail')
|
||||||
cidr = tables.Column("cidr", verbose_name=_("CIDR"))
|
cidr = tables.Column("cidr", verbose_name=_("CIDR"))
|
||||||
ip_version = tables.Column("ipver_str", verbose_name=_("IP Version"))
|
ip_version = tables.Column("ipver_str", verbose_name=_("IP Version"))
|
||||||
gateway_ip = tables.Column("gateway_ip", verbose_name=_("Gateway IP"))
|
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')
|
failure_url = reverse_lazy('horizon:admin:networks:index')
|
||||||
|
|
||||||
def get_object_display(self, subnet):
|
def get_object_display(self, subnet):
|
||||||
@ -117,3 +132,13 @@ class SubnetsTable(tables.DataTable):
|
|||||||
table_actions = (CreateSubnet, DeleteSubnet)
|
table_actions = (CreateSubnet, DeleteSubnet)
|
||||||
row_actions = (UpdateSubnet, DeleteSubnet,)
|
row_actions = (UpdateSubnet, DeleteSubnet,)
|
||||||
hidden_title = False
|
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']
|
||||||
|
@ -31,7 +31,9 @@ NETWORKS_DETAIL_URL = 'horizon:admin:networks:detail'
|
|||||||
|
|
||||||
class NetworkSubnetTests(test.BaseAdminViewTests):
|
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):
|
def test_subnet_detail(self):
|
||||||
network = self.networks.first()
|
network = self.networks.first()
|
||||||
subnet = self.subnets.first()
|
subnet = self.subnets.first()
|
||||||
@ -41,6 +43,10 @@ class NetworkSubnetTests(test.BaseAdminViewTests):
|
|||||||
api.neutron.subnet_get(IsA(http.HttpRequest), subnet.id)\
|
api.neutron.subnet_get(IsA(http.HttpRequest), subnet.id)\
|
||||||
.AndReturn(subnet)
|
.AndReturn(subnet)
|
||||||
|
|
||||||
|
api.neutron.is_extension_supported(
|
||||||
|
IsA(http.HttpRequest),
|
||||||
|
'network-ip-availability').AndReturn(True)
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
url = reverse(DETAIL_URL, args=[subnet.id])
|
url = reverse(DETAIL_URL, args=[subnet.id])
|
||||||
@ -292,6 +298,7 @@ class NetworkSubnetTests(test.BaseAdminViewTests):
|
|||||||
'subnet_list',
|
'subnet_list',
|
||||||
'port_list',
|
'port_list',
|
||||||
'is_extension_supported',
|
'is_extension_supported',
|
||||||
|
'show_network_ip_availability',
|
||||||
'list_dhcp_agent_hosting_networks',)})
|
'list_dhcp_agent_hosting_networks',)})
|
||||||
def test_subnet_delete(self):
|
def test_subnet_delete(self):
|
||||||
self._test_subnet_delete()
|
self._test_subnet_delete()
|
||||||
@ -300,6 +307,7 @@ class NetworkSubnetTests(test.BaseAdminViewTests):
|
|||||||
'subnet_list',
|
'subnet_list',
|
||||||
'port_list',
|
'port_list',
|
||||||
'is_extension_supported',
|
'is_extension_supported',
|
||||||
|
'show_network_ip_availability',
|
||||||
'list_dhcp_agent_hosting_networks',)})
|
'list_dhcp_agent_hosting_networks',)})
|
||||||
def test_subnet_delete_with_mac_learning(self):
|
def test_subnet_delete_with_mac_learning(self):
|
||||||
self._test_subnet_delete(mac_learning=True)
|
self._test_subnet_delete(mac_learning=True)
|
||||||
@ -307,6 +315,10 @@ class NetworkSubnetTests(test.BaseAdminViewTests):
|
|||||||
def _test_subnet_delete(self, mac_learning=False):
|
def _test_subnet_delete(self, mac_learning=False):
|
||||||
subnet = self.subnets.first()
|
subnet = self.subnets.first()
|
||||||
network_id = subnet.network_id
|
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),
|
api.neutron.list_dhcp_agent_hosting_networks(IsA(http.HttpRequest),
|
||||||
network_id).\
|
network_id).\
|
||||||
AndReturn(self.agents.list())
|
AndReturn(self.agents.list())
|
||||||
@ -315,9 +327,15 @@ class NetworkSubnetTests(test.BaseAdminViewTests):
|
|||||||
.AndReturn([self.subnets.first()])
|
.AndReturn([self.subnets.first()])
|
||||||
api.neutron.port_list(IsA(http.HttpRequest), network_id=network_id)\
|
api.neutron.port_list(IsA(http.HttpRequest), network_id=network_id)\
|
||||||
.AndReturn([self.ports.first()])
|
.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),
|
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||||
'mac-learning')\
|
'mac-learning')\
|
||||||
.AndReturn(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),
|
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||||
'dhcp_agent_scheduler')\
|
'dhcp_agent_scheduler')\
|
||||||
.AndReturn(True)
|
.AndReturn(True)
|
||||||
@ -333,6 +351,7 @@ class NetworkSubnetTests(test.BaseAdminViewTests):
|
|||||||
'subnet_list',
|
'subnet_list',
|
||||||
'port_list',
|
'port_list',
|
||||||
'is_extension_supported',
|
'is_extension_supported',
|
||||||
|
'show_network_ip_availability',
|
||||||
'list_dhcp_agent_hosting_networks',)})
|
'list_dhcp_agent_hosting_networks',)})
|
||||||
def test_subnet_delete_exception(self):
|
def test_subnet_delete_exception(self):
|
||||||
self._test_subnet_delete_exception()
|
self._test_subnet_delete_exception()
|
||||||
@ -341,6 +360,7 @@ class NetworkSubnetTests(test.BaseAdminViewTests):
|
|||||||
'subnet_list',
|
'subnet_list',
|
||||||
'port_list',
|
'port_list',
|
||||||
'is_extension_supported',
|
'is_extension_supported',
|
||||||
|
'show_network_ip_availability',
|
||||||
'list_dhcp_agent_hosting_networks',)})
|
'list_dhcp_agent_hosting_networks',)})
|
||||||
def test_subnet_delete_exception_with_mac_learning(self):
|
def test_subnet_delete_exception_with_mac_learning(self):
|
||||||
self._test_subnet_delete_exception(mac_learning=True)
|
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):
|
def _test_subnet_delete_exception(self, mac_learning=False):
|
||||||
subnet = self.subnets.first()
|
subnet = self.subnets.first()
|
||||||
network_id = subnet.network_id
|
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),
|
api.neutron.list_dhcp_agent_hosting_networks(IsA(http.HttpRequest),
|
||||||
network_id).\
|
network_id).\
|
||||||
AndReturn(self.agents.list())
|
AndReturn(self.agents.list())
|
||||||
@ -357,9 +381,15 @@ class NetworkSubnetTests(test.BaseAdminViewTests):
|
|||||||
.AndReturn([self.subnets.first()])
|
.AndReturn([self.subnets.first()])
|
||||||
api.neutron.port_list(IsA(http.HttpRequest), network_id=network_id)\
|
api.neutron.port_list(IsA(http.HttpRequest), network_id=network_id)\
|
||||||
.AndReturn([self.ports.first()])
|
.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),
|
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||||
'mac-learning')\
|
'mac-learning')\
|
||||||
.AndReturn(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),
|
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||||
'dhcp_agent_scheduler')\
|
'dhcp_agent_scheduler')\
|
||||||
.AndReturn(True)
|
.AndReturn(True)
|
||||||
@ -370,3 +400,57 @@ class NetworkSubnetTests(test.BaseAdminViewTests):
|
|||||||
res = self.client.post(url, form_data)
|
res = self.client.post(url, form_data)
|
||||||
|
|
||||||
self.assertRedirectsNoFollow(res, url)
|
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()])
|
||||||
|
@ -78,11 +78,6 @@ class EditNetwork(policy.PolicyTargetMixin, tables.LinkAction):
|
|||||||
policy_rules = (("network", "update_network"),)
|
policy_rules = (("network", "update_network"),)
|
||||||
|
|
||||||
|
|
||||||
# def _get_subnets(network):
|
|
||||||
# cidrs = [subnet.get('cidr') for subnet in network.subnets]
|
|
||||||
# return ','.join(cidrs)
|
|
||||||
|
|
||||||
|
|
||||||
DISPLAY_CHOICES = (
|
DISPLAY_CHOICES = (
|
||||||
("up", pgettext_lazy("Admin state of a Network", u"UP")),
|
("up", pgettext_lazy("Admin state of a Network", u"UP")),
|
||||||
("down", pgettext_lazy("Admin state of a Network", u"DOWN")),
|
("down", pgettext_lazy("Admin state of a Network", u"DOWN")),
|
||||||
|
@ -74,6 +74,7 @@ class NetworkTests(test.BaseAdminViewTests):
|
|||||||
@test.create_stubs({api.neutron: ('network_get',
|
@test.create_stubs({api.neutron: ('network_get',
|
||||||
'subnet_list',
|
'subnet_list',
|
||||||
'port_list',
|
'port_list',
|
||||||
|
'show_network_ip_availability',
|
||||||
'list_dhcp_agent_hosting_networks',
|
'list_dhcp_agent_hosting_networks',
|
||||||
'is_extension_supported')})
|
'is_extension_supported')})
|
||||||
def test_network_detail(self):
|
def test_network_detail(self):
|
||||||
@ -83,12 +84,17 @@ class NetworkTests(test.BaseAdminViewTests):
|
|||||||
'subnet_list',
|
'subnet_list',
|
||||||
'port_list',
|
'port_list',
|
||||||
'is_extension_supported',
|
'is_extension_supported',
|
||||||
|
'show_network_ip_availability',
|
||||||
'list_dhcp_agent_hosting_networks',)})
|
'list_dhcp_agent_hosting_networks',)})
|
||||||
def test_network_detail_with_mac_learning(self):
|
def test_network_detail_with_mac_learning(self):
|
||||||
self._test_network_detail(mac_learning=True)
|
self._test_network_detail(mac_learning=True)
|
||||||
|
|
||||||
def _test_network_detail(self, mac_learning=False):
|
def _test_network_detail(self, mac_learning=False):
|
||||||
network_id = self.networks.first().id
|
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),
|
api.neutron.list_dhcp_agent_hosting_networks(IsA(http.HttpRequest),
|
||||||
network_id).\
|
network_id).\
|
||||||
AndReturn(self.agents.list())
|
AndReturn(self.agents.list())
|
||||||
@ -98,9 +104,15 @@ class NetworkTests(test.BaseAdminViewTests):
|
|||||||
.AndReturn([self.subnets.first()])
|
.AndReturn([self.subnets.first()])
|
||||||
api.neutron.port_list(IsA(http.HttpRequest), network_id=network_id)\
|
api.neutron.port_list(IsA(http.HttpRequest), network_id=network_id)\
|
||||||
.AndReturn([self.ports.first()])
|
.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),
|
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||||
'mac-learning')\
|
'mac-learning')\
|
||||||
.AndReturn(mac_learning)
|
.AndReturn(mac_learning)
|
||||||
|
api.neutron.is_extension_supported(
|
||||||
|
IsA(http.HttpRequest),
|
||||||
|
'network-ip-availability').AndReturn(True)
|
||||||
api.neutron.is_extension_supported(
|
api.neutron.is_extension_supported(
|
||||||
IsA(http.HttpRequest),
|
IsA(http.HttpRequest),
|
||||||
'dhcp_agent_scheduler').AndReturn(True)
|
'dhcp_agent_scheduler').AndReturn(True)
|
||||||
@ -126,6 +138,7 @@ class NetworkTests(test.BaseAdminViewTests):
|
|||||||
'subnet_list',
|
'subnet_list',
|
||||||
'port_list',
|
'port_list',
|
||||||
'is_extension_supported',
|
'is_extension_supported',
|
||||||
|
'show_network_ip_availability',
|
||||||
'list_dhcp_agent_hosting_networks',)})
|
'list_dhcp_agent_hosting_networks',)})
|
||||||
def test_network_detail_network_exception(self):
|
def test_network_detail_network_exception(self):
|
||||||
self._test_network_detail_network_exception()
|
self._test_network_detail_network_exception()
|
||||||
@ -134,12 +147,17 @@ class NetworkTests(test.BaseAdminViewTests):
|
|||||||
'subnet_list',
|
'subnet_list',
|
||||||
'port_list',
|
'port_list',
|
||||||
'is_extension_supported',
|
'is_extension_supported',
|
||||||
|
'show_network_ip_availability',
|
||||||
'list_dhcp_agent_hosting_networks',)})
|
'list_dhcp_agent_hosting_networks',)})
|
||||||
def test_network_detail_network_exception_with_mac_learning(self):
|
def test_network_detail_network_exception_with_mac_learning(self):
|
||||||
self._test_network_detail_network_exception(mac_learning=True)
|
self._test_network_detail_network_exception(mac_learning=True)
|
||||||
|
|
||||||
def _test_network_detail_network_exception(self, mac_learning=False):
|
def _test_network_detail_network_exception(self, mac_learning=False):
|
||||||
network_id = self.networks.first().id
|
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)\
|
api.neutron.network_get(IsA(http.HttpRequest), network_id)\
|
||||||
.AndRaise(self.exceptions.neutron)
|
.AndRaise(self.exceptions.neutron)
|
||||||
api.neutron.subnet_list(IsA(http.HttpRequest), network_id=network_id)\
|
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),
|
api.neutron.list_dhcp_agent_hosting_networks(IsA(http.HttpRequest),
|
||||||
network_id).\
|
network_id).\
|
||||||
AndReturn(self.agents.list())
|
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),
|
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||||
'mac-learning')\
|
'mac-learning')\
|
||||||
.AndReturn(mac_learning)
|
.AndReturn(mac_learning)
|
||||||
|
api.neutron.is_extension_supported(
|
||||||
|
IsA(http.HttpRequest),
|
||||||
|
'network-ip-availability').AndReturn(True)
|
||||||
api.neutron.is_extension_supported(
|
api.neutron.is_extension_supported(
|
||||||
IsA(http.HttpRequest),
|
IsA(http.HttpRequest),
|
||||||
'dhcp_agent_scheduler').AndReturn(True)
|
'dhcp_agent_scheduler').AndReturn(True)
|
||||||
@ -191,6 +215,9 @@ class NetworkTests(test.BaseAdminViewTests):
|
|||||||
AndRaise(self.exceptions.neutron)
|
AndRaise(self.exceptions.neutron)
|
||||||
api.neutron.port_list(IsA(http.HttpRequest), network_id=network_id).\
|
api.neutron.port_list(IsA(http.HttpRequest), network_id=network_id).\
|
||||||
AndReturn([self.ports.first()])
|
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),
|
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||||
'mac-learning')\
|
'mac-learning')\
|
||||||
.AndReturn(mac_learning)
|
.AndReturn(mac_learning)
|
||||||
@ -219,6 +246,7 @@ class NetworkTests(test.BaseAdminViewTests):
|
|||||||
'subnet_list',
|
'subnet_list',
|
||||||
'port_list',
|
'port_list',
|
||||||
'is_extension_supported',
|
'is_extension_supported',
|
||||||
|
'show_network_ip_availability',
|
||||||
'list_dhcp_agent_hosting_networks',)})
|
'list_dhcp_agent_hosting_networks',)})
|
||||||
def test_network_detail_port_exception(self):
|
def test_network_detail_port_exception(self):
|
||||||
self._test_network_detail_port_exception()
|
self._test_network_detail_port_exception()
|
||||||
@ -227,12 +255,17 @@ class NetworkTests(test.BaseAdminViewTests):
|
|||||||
'subnet_list',
|
'subnet_list',
|
||||||
'port_list',
|
'port_list',
|
||||||
'is_extension_supported',
|
'is_extension_supported',
|
||||||
|
'show_network_ip_availability',
|
||||||
'list_dhcp_agent_hosting_networks',)})
|
'list_dhcp_agent_hosting_networks',)})
|
||||||
def test_network_detail_port_exception_with_mac_learning(self):
|
def test_network_detail_port_exception_with_mac_learning(self):
|
||||||
self._test_network_detail_port_exception(mac_learning=True)
|
self._test_network_detail_port_exception(mac_learning=True)
|
||||||
|
|
||||||
def _test_network_detail_port_exception(self, mac_learning=False):
|
def _test_network_detail_port_exception(self, mac_learning=False):
|
||||||
network_id = self.networks.first().id
|
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),
|
api.neutron.list_dhcp_agent_hosting_networks(IsA(http.HttpRequest),
|
||||||
network_id).\
|
network_id).\
|
||||||
AndReturn(self.agents.list())
|
AndReturn(self.agents.list())
|
||||||
@ -242,9 +275,15 @@ class NetworkTests(test.BaseAdminViewTests):
|
|||||||
AndReturn([self.subnets.first()])
|
AndReturn([self.subnets.first()])
|
||||||
api.neutron.port_list(IsA(http.HttpRequest), network_id=network_id).\
|
api.neutron.port_list(IsA(http.HttpRequest), network_id=network_id).\
|
||||||
AndRaise(self.exceptions.neutron)
|
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),
|
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||||
'mac-learning')\
|
'mac-learning')\
|
||||||
.AndReturn(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),
|
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||||
'dhcp_agent_scheduler')\
|
'dhcp_agent_scheduler')\
|
||||||
.AndReturn(True)
|
.AndReturn(True)
|
||||||
|
@ -59,6 +59,7 @@ class IndexView(tables.DataTableView):
|
|||||||
def _get_agents_data(self, network):
|
def _get_agents_data(self, network):
|
||||||
agents = []
|
agents = []
|
||||||
data = _("Unknown")
|
data = _("Unknown")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if api.neutron.is_extension_supported(self.request,
|
if api.neutron.is_extension_supported(self.request,
|
||||||
'dhcp_agent_scheduler'):
|
'dhcp_agent_scheduler'):
|
||||||
@ -69,7 +70,8 @@ class IndexView(tables.DataTableView):
|
|||||||
self.request, network)
|
self.request, network)
|
||||||
data = len(agents)
|
data = len(agents)
|
||||||
except Exception:
|
except Exception:
|
||||||
self.exception = True
|
msg = _('Unable to list dhcp agents hosting network.')
|
||||||
|
exceptions.handle(self.request, msg)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def get_data(self):
|
def get_data(self):
|
||||||
@ -87,10 +89,6 @@ class IndexView(tables.DataTableView):
|
|||||||
tenant = tenant_dict.get(n.tenant_id, None)
|
tenant = tenant_dict.get(n.tenant_id, None)
|
||||||
n.tenant_name = getattr(tenant, 'name', None)
|
n.tenant_name = getattr(tenant, 'name', None)
|
||||||
n.num_agents = self._get_agents_data(n.id)
|
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
|
return networks
|
||||||
|
|
||||||
|
|
||||||
@ -108,11 +106,47 @@ class DetailView(tables.MultiTableView):
|
|||||||
template_name = 'project/networks/detail.html'
|
template_name = 'project/networks/detail.html'
|
||||||
page_title = '{{ network.name | default:network.id }}'
|
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):
|
def get_subnets_data(self):
|
||||||
try:
|
try:
|
||||||
network_id = self.kwargs['network_id']
|
network_id = self.kwargs['network_id']
|
||||||
subnets = api.neutron.subnet_list(self.request,
|
subnets = api.neutron.subnet_list(self.request,
|
||||||
network_id=network_id)
|
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:
|
except Exception:
|
||||||
subnets = []
|
subnets = []
|
||||||
msg = _('Subnet list can not be retrieved.')
|
msg = _('Subnet list can not be retrieved.')
|
||||||
|
@ -155,6 +155,35 @@ class NeutronApiTests(test.APITestCase):
|
|||||||
|
|
||||||
api.neutron.network_delete(self.request, network_id)
|
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):
|
def test_subnet_list(self):
|
||||||
subnets = {'subnets': self.api_subnets.list()}
|
subnets = {'subnets': self.api_subnets.list()}
|
||||||
|
|
||||||
|
@ -55,6 +55,7 @@ def data(TEST):
|
|||||||
TEST.firewalls = utils.TestDataContainer()
|
TEST.firewalls = utils.TestDataContainer()
|
||||||
TEST.fw_policies = utils.TestDataContainer()
|
TEST.fw_policies = utils.TestDataContainer()
|
||||||
TEST.fw_rules = utils.TestDataContainer()
|
TEST.fw_rules = utils.TestDataContainer()
|
||||||
|
TEST.ip_availability = utils.TestDataContainer()
|
||||||
|
|
||||||
# Data return by neutronclient.
|
# Data return by neutronclient.
|
||||||
TEST.api_agents = utils.TestDataContainer()
|
TEST.api_agents = utils.TestDataContainer()
|
||||||
@ -83,6 +84,7 @@ def data(TEST):
|
|||||||
TEST.api_firewalls = utils.TestDataContainer()
|
TEST.api_firewalls = utils.TestDataContainer()
|
||||||
TEST.api_fw_policies = utils.TestDataContainer()
|
TEST.api_fw_policies = utils.TestDataContainer()
|
||||||
TEST.api_fw_rules = utils.TestDataContainer()
|
TEST.api_fw_rules = utils.TestDataContainer()
|
||||||
|
TEST.api_ip_availability = utils.TestDataContainer()
|
||||||
|
|
||||||
# 1st network.
|
# 1st network.
|
||||||
network_dict = {'admin_state_up': True,
|
network_dict = {'admin_state_up': True,
|
||||||
@ -1317,3 +1319,27 @@ def data(TEST):
|
|||||||
'binding:host_id': 'host'}
|
'binding:host_id': 'host'}
|
||||||
TEST.api_ports.add(port_dict)
|
TEST.api_ports.add(port_dict)
|
||||||
TEST.ports.add(neutron.Port(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)
|
||||||
|
8
releasenotes/notes/ip-availability-be217ba59cc02b40.yaml
Normal file
8
releasenotes/notes/ip-availability-be217ba59cc02b40.yaml
Normal file
@ -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.
|
Loading…
Reference in New Issue
Block a user