From b978f909014cbc398c21bfae917fcece89a36e38 Mon Sep 17 00:00:00 2001 From: zengfagao Date: Thu, 26 Feb 2015 13:39:00 -0800 Subject: [PATCH] Add Neutron subnetpool API Add Neutron subnetpool listing, creating, updating, showing and deleting Client API. neutron subnetpool-list neutron subnetpool-create neutron subnetpool-update neutron subnetpool-show neutron subnetpool-delete Change-Id: I19a6782d33609609f43353df8293864e60ead817 Partially-Implements: blueprint subnet-allocation --- neutronclient/common/constants.py | 1 + neutronclient/neutron/v2_0/subnetpool.py | 101 ++++++++++++++ neutronclient/shell.py | 6 + neutronclient/tests/unit/test_cli20.py | 2 +- .../tests/unit/test_cli20_subnetpool.py | 132 ++++++++++++++++++ neutronclient/v2_0/client.py | 28 ++++ 6 files changed, 269 insertions(+), 1 deletion(-) create mode 100644 neutronclient/neutron/v2_0/subnetpool.py create mode 100644 neutronclient/tests/unit/test_cli20_subnetpool.py diff --git a/neutronclient/common/constants.py b/neutronclient/common/constants.py index 305b5df05..525a04058 100644 --- a/neutronclient/common/constants.py +++ b/neutronclient/common/constants.py @@ -36,6 +36,7 @@ TYPE_DICT = "dict" PLURALS = {'networks': 'network', 'ports': 'port', 'subnets': 'subnet', + 'subnetpools': 'subnetpool', 'dns_nameservers': 'dns_nameserver', 'host_routes': 'host_route', 'allocation_pools': 'allocation_pool', diff --git a/neutronclient/neutron/v2_0/subnetpool.py b/neutronclient/neutron/v2_0/subnetpool.py new file mode 100644 index 000000000..54926b0d9 --- /dev/null +++ b/neutronclient/neutron/v2_0/subnetpool.py @@ -0,0 +1,101 @@ +# Copyright 2015 OpenStack Foundation. +# All Rights Reserved +# +# 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 neutronclient.i18n import _ +from neutronclient.neutron import v2_0 as neutronV20 + + +def add_updatable_arguments(parser): + parser.add_argument( + '--min-prefixlen', type=int, + help=_('Subnetpool minimum prefix length.')) + parser.add_argument( + '--max-prefixlen', type=int, + help=_('Subnetpool maximum prefix length.')) + parser.add_argument( + '--default-prefixlen', type=int, + help=_('Subnetpool default prefix length.')) + parser.add_argument( + '--pool-prefix', + action='append', dest='prefixes', + help=_('Subnetpool prefixes (This option can be repeated).')) + + +def updatable_args2body(parsed_args, body, for_create=True): + neutronV20.update_dict(parsed_args, body['subnetpool'], + ['name', 'prefixes', 'default_prefixlen', + 'min_prefixlen', 'max_prefixlen']) + + +class ListSubnetPool(neutronV20.ListCommand): + """List subnetpools that belong to a given tenant.""" + + resource = 'subnetpool' + list_columns = ['id', 'name', 'prefixes', + 'default_prefixlen'] + pagination_support = True + sorting_support = True + + +class ShowSubnetPool(neutronV20.ShowCommand): + """Show information of a given subnetpool.""" + + resource = 'subnetpool' + + +class CreateSubnetPool(neutronV20.CreateCommand): + """Create a subnetpool for a given tenant.""" + + resource = 'subnetpool' + + def add_known_arguments(self, parser): + add_updatable_arguments(parser) + parser.add_argument( + '--shared', + action='store_true', + help=_('Set the subnetpool as shared.')) + parser.add_argument( + 'name', + help=_('Name of subnetpool to create.')) + + def args2body(self, parsed_args): + body = {'subnetpool': {'prefixes': parsed_args.prefixes}} + updatable_args2body(parsed_args, body) + if parsed_args.shared: + body['subnetpool']['shared'] = True + return body + + +class DeleteSubnetPool(neutronV20.DeleteCommand): + """Delete a given subnetpool.""" + + resource = 'subnetpool' + + +class UpdateSubnetPool(neutronV20.UpdateCommand): + """Update subnetpool's information.""" + + resource = 'subnetpool' + + def add_known_arguments(self, parser): + add_updatable_arguments(parser) + parser.add_argument('--name', + help=_('Name of subnetpool to update.')) + + def args2body(self, parsed_args): + body = {'subnetpool': {}} + updatable_args2body(parsed_args, body, for_create=False) + return body diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 8acfc19fc..bf77b4a8c 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -76,6 +76,7 @@ from neutronclient.neutron.v2_0 import router from neutronclient.neutron.v2_0 import securitygroup from neutronclient.neutron.v2_0 import servicetype from neutronclient.neutron.v2_0 import subnet +from neutronclient.neutron.v2_0 import subnetpool from neutronclient.neutron.v2_0.vpn import ikepolicy from neutronclient.neutron.v2_0.vpn import ipsec_site_connection from neutronclient.neutron.v2_0.vpn import ipsecpolicy @@ -142,6 +143,11 @@ COMMAND_V2 = { 'subnet-create': subnet.CreateSubnet, 'subnet-delete': subnet.DeleteSubnet, 'subnet-update': subnet.UpdateSubnet, + 'subnetpool-list': subnetpool.ListSubnetPool, + 'subnetpool-show': subnetpool.ShowSubnetPool, + 'subnetpool-create': subnetpool.CreateSubnetPool, + 'subnetpool-delete': subnetpool.DeleteSubnetPool, + 'subnetpool-update': subnetpool.UpdateSubnetPool, 'port-list': port.ListPort, 'port-show': port.ShowPort, 'port-create': port.CreatePort, diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index ee6d7ac8c..e9432534e 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -222,7 +222,7 @@ class CLITestV20Base(base.BaseTestCase): 'policy_profile', 'ikepolicy', 'ipsecpolicy', 'metering_label', 'metering_label_rule', 'net_partition', - 'fox_socket'] + 'fox_socket', 'subnetpool'] if not cmd_resource: cmd_resource = resource if (resource in non_admin_status_resources): diff --git a/neutronclient/tests/unit/test_cli20_subnetpool.py b/neutronclient/tests/unit/test_cli20_subnetpool.py new file mode 100644 index 000000000..782d74d78 --- /dev/null +++ b/neutronclient/tests/unit/test_cli20_subnetpool.py @@ -0,0 +1,132 @@ +# Copyright 2015 OpenStack Foundation. +# All Rights Reserved +# +# 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 mox3 import mox + +from neutronclient.common import exceptions +from neutronclient.neutron.v2_0 import subnetpool +from neutronclient.tests.unit import test_cli20 + + +class CLITestV20SubnetPoolJSON(test_cli20.CLITestV20Base): + def setUp(self): + super(CLITestV20SubnetPoolJSON, self).setUp(plurals={'tags': 'tag'}) + + def test_create_subnetpool_shared(self): + """Create subnetpool: myname.""" + resource = 'subnetpool' + cmd = subnetpool.CreateSubnetPool(test_cli20.MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + min_prefixlen = 30 + prefix1 = '10.11.12.0/24' + prefix2 = '12.11.13.0/24' + args = [name, '--min-prefixlen', str(min_prefixlen), + '--pool-prefix', prefix1, '--pool-prefix', prefix2, + '--shared'] + position_names = ['name', 'min_prefixlen', 'prefixes', 'shared'] + position_values = [name, min_prefixlen, [prefix1, prefix2], True] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + + def test_create_subnetpool_not_shared(self): + """Create subnetpool: myname.""" + resource = 'subnetpool' + cmd = subnetpool.CreateSubnetPool(test_cli20.MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + min_prefixlen = 30 + prefix1 = '10.11.12.0/24' + prefix2 = '12.11.13.0/24' + args = [name, '--min-prefixlen', str(min_prefixlen), + '--pool-prefix', prefix1, '--pool-prefix', prefix2] + position_names = ['name', 'min_prefixlen', 'prefixes'] + position_values = [name, min_prefixlen, [prefix1, prefix2]] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + + def test_create_subnetpool_with_unicode(self): + """Create subnetpool: u'\u7f51\u7edc'.""" + resource = 'subnetpool' + cmd = subnetpool.CreateSubnetPool(test_cli20.MyApp(sys.stdout), None) + name = u'\u7f51\u7edc' + myid = 'myid' + min_prefixlen = 30 + prefixes = '10.11.12.0/24' + args = [name, '--min-prefixlen', str(min_prefixlen), + '--pool-prefix', prefixes] + position_names = ['name', 'min_prefixlen', 'prefixes'] + position_values = [name, min_prefixlen, [prefixes]] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + + def test_list_subnetpool_pagination(self): + cmd = subnetpool.ListSubnetPool(test_cli20.MyApp(sys.stdout), None) + self.mox.StubOutWithMock(subnetpool.ListSubnetPool, "extend_list") + subnetpool.ListSubnetPool.extend_list(mox.IsA(list), mox.IgnoreArg()) + self._test_list_resources_with_pagination("subnetpools", cmd) + self.mox.VerifyAll() + self.mox.UnsetStubs() + + def test_list_subnetpools_sort(self): + """List subnetpools: --sort-key name --sort-key id --sort-key asc + --sort-key desc + """ + resources = "subnetpools" + cmd = subnetpool.ListSubnetPool(test_cli20.MyApp(sys.stdout), None) + self._test_list_resources(resources, cmd, + sort_key=["name", "id"], + sort_dir=["asc", "desc"]) + + def test_list_subnetpools_limit(self): + """List subnetpools: -P.""" + resources = "subnetpools" + cmd = subnetpool.ListSubnetPool(test_cli20.MyApp(sys.stdout), None) + self._test_list_resources(resources, cmd, page_size=1000) + + def test_update_subnetpool_exception(self): + """Update subnetpool: myid.""" + resource = 'subnetpool' + cmd = subnetpool.UpdateSubnetPool(test_cli20.MyApp(sys.stdout), None) + self.assertRaises(exceptions.CommandError, self._test_update_resource, + resource, cmd, 'myid', ['myid'], {}) + + def test_update_subnetpool(self): + """Update subnetpool: myid --name myname.""" + resource = 'subnetpool' + cmd = subnetpool.UpdateSubnetPool(test_cli20.MyApp(sys.stdout), None) + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--name', 'myname'], + {'name': 'myname'} + ) + + def test_show_subnetpool(self): + """Show subnetpool: --fields id --fields name myid.""" + resource = 'subnetpool' + cmd = subnetpool.ShowSubnetPool(test_cli20.MyApp(sys.stdout), None) + args = ['--fields', 'id', '--fields', 'name', self.test_id] + self._test_show_resource(resource, cmd, self.test_id, args, + ['id', 'name']) + + def test_delete_subnetpool(self): + """Delete subnetpool: subnetpoolid.""" + resource = 'subnetpool' + cmd = subnetpool.DeleteSubnetPool(test_cli20.MyApp(sys.stdout), None) + myid = 'myid' + args = [myid] + self._test_delete_resource(resource, cmd, myid, args) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 5cf112573..52640339e 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -339,6 +339,8 @@ class Client(ClientBase): port_path = "/ports/%s" subnets_path = "/subnets" subnet_path = "/subnets/%s" + subnetpools_path = "/subnetpools" + subnetpool_path = "/subnetpools/%s" quotas_path = "/quotas" quota_path = "/quotas/%s" extensions_path = "/extensions" @@ -604,6 +606,32 @@ class Client(ClientBase): """Deletes the specified subnet.""" return self.delete(self.subnet_path % (subnet)) + @APIParamsCall + def list_subnetpools(self, retrieve_all=True, **_params): + """Fetches a list of all subnetpools for a tenant.""" + return self.list('subnetpools', self.subnetpools_path, retrieve_all, + **_params) + + @APIParamsCall + def show_subnetpool(self, subnetpool, **_params): + """Fetches information of a certain subnetpool.""" + return self.get(self.subnetpool_path % (subnetpool), params=_params) + + @APIParamsCall + def create_subnetpool(self, body=None): + """Creates a new subnetpool.""" + return self.post(self.subnetpools_path, body=body) + + @APIParamsCall + def update_subnetpool(self, subnetpool, body=None): + """Updates a subnetpool.""" + return self.put(self.subnetpool_path % (subnetpool), body=body) + + @APIParamsCall + def delete_subnetpool(self, subnetpool): + """Deletes the specified subnetpool.""" + return self.delete(self.subnetpool_path % (subnetpool)) + @APIParamsCall def list_routers(self, retrieve_all=True, **_params): """Fetches a list of all routers for a tenant."""