diff --git a/neutronclient/neutron/v2_0/network_ip_availability.py b/neutronclient/neutron/v2_0/network_ip_availability.py new file mode 100644 index 0000000..d1332a2 --- /dev/null +++ b/neutronclient/neutron/v2_0/network_ip_availability.py @@ -0,0 +1,73 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +from cliff import show +import six + +from neutronclient._i18n import _ +from neutronclient.neutron import v2_0 as neutronV20 + + +class ListIpAvailability(neutronV20.ListCommand): + """List IP usage of networks""" + + resource = 'network_ip_availability' + resource_plural = 'network_ip_availabilities' + list_columns = ['network_id', 'network_name', 'total_ips', 'used_ips'] + paginations_support = True + sorting_support = True + + filter_attrs = [ + {'name': 'ip_version', + 'help': _('Returns IP availability for the network subnets ' + 'with a given IP version. Default: 4'), + 'argparse_kwargs': {'type': int, + 'choices': [4, 6], + 'default': 4} + }, + {'name': 'network_id', + 'help': _('Returns IP availability for the network ' + 'matching a given network ID.')}, + {'name': 'network_name', + 'help': _('Returns IP availability for the network ' + 'matching a given name.')}, + {'name': 'tenant_id', + 'help': _('Returns IP availability for the networks ' + 'with a given tenant ID.')}, + ] + + +class ShowIpAvailability(neutronV20.NeutronCommand, show.ShowOne): + """Show IP usage of specific network""" + + resource = 'network_ip_availability' + + def get_parser(self, prog_name): + parser = super(ShowIpAvailability, self).get_parser(prog_name) + parser.add_argument( + 'network_id', metavar='NETWORK', + help=_('ID or name of network to look up.')) + return parser + + def take_action(self, parsed_args): + self.log.debug('run(%s)', parsed_args) + neutron_client = self.get_client() + _id = neutronV20.find_resourceid_by_name_or_id( + neutron_client, 'network', parsed_args.network_id) + data = neutron_client.show_network_ip_availability(_id) + self.format_output_data(data) + resource = data[self.resource] + if self.resource in data: + return zip(*sorted(six.iteritems(resource))) + else: + return None diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 2a0b334..bb923c2 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -68,6 +68,7 @@ from neutronclient.neutron.v2_0.lb.v2 import pool as lbaas_pool from neutronclient.neutron.v2_0.lb import vip as lb_vip from neutronclient.neutron.v2_0 import metering from neutronclient.neutron.v2_0 import network +from neutronclient.neutron.v2_0 import network_ip_availability from neutronclient.neutron.v2_0.nsx import networkgateway from neutronclient.neutron.v2_0.nsx import qos_queue from neutronclient.neutron.v2_0 import port @@ -441,6 +442,8 @@ COMMAND_V2 = { 'bgp-peer-create': bgp_peer.CreatePeer, 'bgp-peer-update': bgp_peer.UpdatePeer, 'bgp-peer-delete': bgp_peer.DeletePeer, + 'net-ip-availability-list': network_ip_availability.ListIpAvailability, + 'net-ip-availability-show': network_ip_availability.ShowIpAvailability, } COMMANDS = {'2.0': COMMAND_V2} diff --git a/neutronclient/tests/unit/test_cli20_network_ip_availability.py b/neutronclient/tests/unit/test_cli20_network_ip_availability.py new file mode 100644 index 0000000..eb325a8 --- /dev/null +++ b/neutronclient/tests/unit/test_cli20_network_ip_availability.py @@ -0,0 +1,54 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import sys + +from neutronclient.neutron.v2_0 import network_ip_availability +from neutronclient.tests.unit import test_cli20 + + +class CLITestV20NetworkIPAvailability(test_cli20.CLITestV20Base): + + id_field = 'network_id' + + def _test_list_network_ip_availability(self, args, query): + resources = "network_ip_availabilities" + cmd = network_ip_availability.ListIpAvailability(test_cli20.MyApp + (sys.stdout), None) + self._test_list_resources(resources, cmd, + base_args=args, + query=query) + + def test_list_network_ip_availability(self): + self._test_list_network_ip_availability(args=None, + query='ip_version=4') + + def test_list_network_ip_availability_ipv6(self): + self._test_list_network_ip_availability( + args=['--ip-version', '6'], query='ip_version=6') + + def test_list_network_ip_availability_net_id_and_ipv4(self): + self._test_list_network_ip_availability( + args=['--ip-version', '4', '--network-id', 'myid'], + query='ip_version=4&network_id=myid') + + def test_list_network_ip_availability_net_name_and_tenant_id(self): + self._test_list_network_ip_availability( + args=['--network-name', 'foo', '--tenant-id', 'mytenant'], + query='network_name=foo&tenant_id=mytenant&ip_version=4') + + def test_show_network_ip_availability(self): + resource = "network_ip_availability" + cmd = network_ip_availability.ShowIpAvailability( + test_cli20.MyApp(sys.stdout), None) + self._test_show_resource(resource, cmd, self.test_id, + args=[self.test_id]) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 6db7537..dbe3af5 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -526,6 +526,8 @@ class Client(ClientBase): bgp_speaker_path = "/bgp-speakers/%s" bgp_peers_path = "/bgp-peers" bgp_peer_path = "/bgp-peers/%s" + network_ip_availabilities_path = '/network-ip-availabilities' + network_ip_availability_path = '/network-ip-availabilities/%s' # API has no way to report plurals, so we have to hard code them EXTED_PLURALS = {'routers': 'router', @@ -569,6 +571,7 @@ class Client(ClientBase): 'flavors': 'flavor', 'bgp_speakers': 'bgp_speaker', 'bgp_peers': 'bgp_peer', + 'network_ip_availabilities': 'network_ip_availability', } @APIParamsCall @@ -1986,6 +1989,19 @@ class Client(ClientBase): """Deletes the specified BGP peer.""" return self.delete(self.bgp_peer_path % peer_id) + @APIParamsCall + def list_network_ip_availabilities(self, retrieve_all=True, **_params): + """Fetches IP availibility information for all networks""" + return self.list('network_ip_availabilities', + self.network_ip_availabilities_path, + retrieve_all, **_params) + + @APIParamsCall + def show_network_ip_availability(self, network, **_params): + """Fetches IP availability information for a specified network""" + return self.get(self.network_ip_availability_path % (network), + params=_params) + def __init__(self, **kwargs): """Initialize a new client for the Neutron v2.0 API.""" super(Client, self).__init__(**kwargs) diff --git a/releasenotes/notes/network-ip-availability-ac9a462f42fe9db4.yaml b/releasenotes/notes/network-ip-availability-ac9a462f42fe9db4.yaml new file mode 100644 index 0000000..7a120fd --- /dev/null +++ b/releasenotes/notes/network-ip-availability-ac9a462f42fe9db4.yaml @@ -0,0 +1,9 @@ +--- +features: + - | + CLI support for network IP availability + + * The ``net-ip-availability-list`` command provides a list of IP + usage statistics for all networks. + * The ``net-ip-availability-show`` command provides IP usage stats + for a specific network.