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:
Ankur Gupta 2016-02-22 11:03:57 -06:00
parent e5eb260522
commit 0b807ac1d0
11 changed files with 287 additions and 11 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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']

View File

@ -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()])

View File

@ -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")),

View File

@ -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)

View File

@ -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.')

View File

@ -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()}

View File

@ -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)

View 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.