Subnet: Add "subnet set" command using SDK

This patch adds "subnet set" command to osc using sdk.

Implements: blueprint neutron-client

Closes-bug: #1542363

Change-Id: Id3b7f4b9190b4d73ca3ae423321a65f94a6da62e
This commit is contained in:
reedip 2016-03-15 09:37:10 +09:00 committed by Reedip
parent a60e31ad4b
commit 2b95e363d3
6 changed files with 273 additions and 82 deletions

View File

@ -21,7 +21,7 @@ Delete a subnet
Subnet to delete (name or ID)
subnet create
--------------
-------------
Create new subnet
@ -143,6 +143,65 @@ List subnets
List additional fields in output
subnet set
----------
Set subnet properties
.. program:: subnet set
.. code:: bash
os subnet set
[--allocation-pool start=<ip-address>,end=<ip-address>]
[--dhcp | --no-dhcp]
[--dns-nameserver <dns-nameserver>]
[--gateway <gateway-ip>]
[--host-route destination=<subnet>,gateway=<ip-address>]
[--name <new-name>]
<subnet>
.. option:: --allocation-pool start=<ip-address>,end=<ip-address>
Allocation pool IP addresses for this subnet e.g.:
start=192.168.199.2,end=192.168.199.254 (This option can be repeated)
.. option:: --dhcp
Enable DHCP
.. option:: --no-dhcp
Disable DHCP
.. option:: --dns-nameserver <dns-nameserver>
DNS name server for this subnet (This option can be repeated)
.. option:: --gateway <gateway>
Specify a gateway for the subnet. The options are:
<ip-address>: Specific IP address to use as the gateway
'none': This subnet will not use a gateway
e.g.: --gateway 192.168.9.1, --gateway none
.. option:: --host-route destination=<subnet>,gateway=<ip-address>
Additional route for this subnet e.g.:
destination=10.10.0.0/16,gateway=192.168.71.254
destination: destination subnet (in CIDR notation)
gateway: nexthop IP address
(This option can be repeated)
.. option:: --name
Updated name of the subnet
.. _subnet_set-subnet:
.. describe:: <subnet>
Subnet to modify (name or ID)
subnet show
-----------

View File

@ -12,8 +12,6 @@
import uuid
import testtools
from functional.common import test
@ -49,7 +47,6 @@ class SubnetTests(test.TestCase):
raw_output = self.openstack('subnet list' + opts)
self.assertIn(self.NAME, raw_output)
@testtools.skip('bug/1542363')
def test_subnet_set(self):
self.openstack('subnet set --no-dhcp ' + self.NAME)
opts = self.get_show_opts(['name', 'enable_dhcp'])

View File

@ -17,6 +17,7 @@ import copy
from json.encoder import JSONEncoder
from openstackclient.common import command
from openstackclient.common import exceptions
from openstackclient.common import parseractions
from openstackclient.common import utils
from openstackclient.identity import common as identity_common
@ -42,6 +43,39 @@ _formatters = {
}
def _get_common_parse_arguments(parser):
parser.add_argument(
'--allocation-pool',
metavar='start=<ip-address>,end=<ip-address>',
dest='allocation_pools',
action=parseractions.MultiKeyValueAction,
required_keys=['start', 'end'],
help='Allocation pool IP addresses for this subnet '
'e.g.: start=192.168.199.2,end=192.168.199.254 '
'(This option can be repeated)',
)
parser.add_argument(
'--dns-nameserver',
metavar='<dns-nameserver>',
action='append',
dest='dns_nameservers',
help='DNS name server for this subnet '
'(This option can be repeated)',
)
parser.add_argument(
'--host-route',
metavar='destination=<subnet>,gateway=<ip-address>',
dest='host_routes',
action=parseractions.MultiKeyValueAction,
required_keys=['destination', 'gateway'],
help='Additional route for this subnet '
'e.g.: destination=10.10.0.0/16,gateway=192.168.71.254 '
'destination: destination subnet (in CIDR notation) '
'gateway: nexthop IP address '
'(This option can be repeated)',
)
def _get_columns(item):
columns = list(item.keys())
if 'tenant_id' in columns:
@ -70,11 +104,12 @@ def convert_entries_to_gateway(entries):
return changed_entries
def _get_attrs(client_manager, parsed_args):
def _get_attrs(client_manager, parsed_args, is_create=True):
attrs = {}
if parsed_args.name is not None:
if 'name' in parsed_args and parsed_args.name is not None:
attrs['name'] = str(parsed_args.name)
if is_create:
if 'project' in parsed_args and parsed_args.project is not None:
identity_client = client_manager.identity
project_id = identity_common.find_project(
@ -83,23 +118,15 @@ def _get_attrs(client_manager, parsed_args):
parsed_args.project_domain,
).id
attrs['tenant_id'] = project_id
client = client_manager.network
attrs['network_id'] = client.find_network(parsed_args.network,
ignore_missing=False).id
if parsed_args.subnet_pool is not None:
subnet_pool = client.find_subnet_pool(parsed_args.subnet_pool,
ignore_missing=False)
attrs['subnetpool_id'] = subnet_pool.id
if parsed_args.use_default_subnet_pool:
attrs['use_default_subnetpool'] = True
if parsed_args.gateway.lower() != 'auto':
if parsed_args.gateway.lower() == 'none':
attrs['gateway_ip'] = None
else:
attrs['gateway_ip'] = parsed_args.gateway
if parsed_args.prefix_length is not None:
attrs['prefixlen'] = parsed_args.prefix_length
if parsed_args.subnet_range is not None:
@ -110,17 +137,33 @@ def _get_attrs(client_manager, parsed_args):
attrs['ipv6_ra_mode'] = parsed_args.ipv6_ra_mode
if parsed_args.ipv6_address_mode is not None:
attrs['ipv6_address_mode'] = parsed_args.ipv6_address_mode
if parsed_args.allocation_pools is not None:
if 'gateway' in parsed_args and parsed_args.gateway is not None:
gateway = parsed_args.gateway.lower()
if not is_create and gateway == 'auto':
raise exceptions.CommandError("Auto option is not available"
" for Subnet Set. Valid options are"
" <ip-address> or none")
elif gateway != 'auto':
if gateway == 'none':
attrs['gateway_ip'] = None
else:
attrs['gateway_ip'] = gateway
if ('allocation_pools' in parsed_args and
parsed_args.allocation_pools is not None):
attrs['allocation_pools'] = parsed_args.allocation_pools
if parsed_args.enable_dhcp is not None:
attrs['enable_dhcp'] = parsed_args.enable_dhcp
if parsed_args.dns_nameservers is not None:
if parsed_args.dhcp:
attrs['enable_dhcp'] = True
elif parsed_args.no_dhcp:
attrs['enable_dhcp'] = False
if ('dns_nameservers' in parsed_args and
parsed_args.dns_nameservers is not None):
attrs['dns_nameservers'] = parsed_args.dns_nameservers
if parsed_args.host_routes is not None:
if 'host_routes' in parsed_args and parsed_args.host_routes is not None:
# Change 'gateway' entry to 'nexthop' to match the API
attrs['host_routes'] = convert_entries_to_nexthop(
parsed_args.host_routes)
return attrs
@ -163,38 +206,18 @@ class CreateSubnet(command.ShowOne):
'(required if --subnet-pool is not specified, '
'optional otherwise)',
)
parser.add_argument(
'--allocation-pool',
metavar='start=<ip-address>,end=<ip-address>',
dest='allocation_pools',
action=parseractions.MultiKeyValueAction,
required_keys=['start', 'end'],
help='Allocation pool IP addresses for this subnet '
'e.g.: start=192.168.199.2,end=192.168.199.254 '
'(This option can be repeated)',
)
dhcp_enable_group = parser.add_mutually_exclusive_group()
dhcp_enable_group.add_argument(
'--dhcp',
dest='enable_dhcp',
action='store_true',
default=True,
help='Enable DHCP (default)',
)
dhcp_enable_group.add_argument(
'--no-dhcp',
dest='enable_dhcp',
action='store_false',
action='store_true',
help='Disable DHCP',
)
parser.add_argument(
'--dns-nameserver',
metavar='<dns-nameserver>',
action='append',
dest='dns_nameservers',
help='DNS name server for this subnet '
'(This option can be repeated)',
)
parser.add_argument(
'--gateway',
metavar='<gateway>',
@ -207,18 +230,6 @@ class CreateSubnet(command.ShowOne):
"e.g.: --gateway 192.168.9.1, --gateway auto, --gateway none"
"(default is 'auto')",
)
parser.add_argument(
'--host-route',
metavar='destination=<subnet>,gateway=<ip-address>',
dest='host_routes',
action=parseractions.MultiKeyValueAction,
required_keys=['destination', 'gateway'],
help='Additional route for this subnet '
'e.g.: destination=10.10.0.0/16,gateway=192.168.71.254 '
'destination: destination subnet (in CIDR notation) '
'gateway: nexthop IP address '
'(This option can be repeated)',
)
parser.add_argument(
'--ip-version',
type=int,
@ -246,7 +257,7 @@ class CreateSubnet(command.ShowOne):
metavar='<network>',
help='Network this subnet belongs to (name or ID)',
)
_get_common_parse_arguments(parser)
return parser
def take_action(self, parsed_args):
@ -309,6 +320,56 @@ class ListSubnet(command.Lister):
) for s in data))
class SetSubnet(command.Command):
"""Set subnet properties"""
def get_parser(self, prog_name):
parser = super(SetSubnet, self).get_parser(prog_name)
parser.add_argument(
'subnet',
metavar="<subnet>",
help=("Subnet to modify (name or ID)")
)
parser.add_argument(
'--name',
metavar='<name>',
help='Updated name of the subnet',
)
dhcp_enable_group = parser.add_mutually_exclusive_group()
dhcp_enable_group.add_argument(
'--dhcp',
action='store_true',
default=None,
help='Enable DHCP',
)
dhcp_enable_group.add_argument(
'--no-dhcp',
action='store_true',
help='Disable DHCP',
)
parser.add_argument(
'--gateway',
metavar='<gateway>',
help="Specify a gateway for the subnet. The options are: "
" <ip-address>: Specific IP address to use as the gateway "
" 'none': This subnet will not use a gateway "
"e.g.: --gateway 192.168.9.1, --gateway none"
)
_get_common_parse_arguments(parser)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.network
obj = client.find_subnet(parsed_args.subnet, ignore_missing=False)
attrs = _get_attrs(self.app.client_manager, parsed_args,
is_create=False)
if not attrs:
msg = "Nothing specified to be set"
raise exceptions.CommandError(msg)
client.update_subnet(obj, **attrs)
return
class ShowSubnet(command.ShowOne):
"""Show subnet details"""

View File

@ -14,6 +14,7 @@
import copy
import mock
from openstackclient.common import exceptions
from openstackclient.common import utils
from openstackclient.network.v2 import subnet as subnet_v2
from openstackclient.tests import fakes
@ -203,9 +204,9 @@ class TestCreateSubnet(TestSubnet):
self.network.find_network = mock.Mock(return_value=self._network)
arglist = [
self._subnet.name,
"--subnet-range", self._subnet.cidr,
"--network", self._subnet.network_id,
self._subnet.name,
]
verifylist = [
('name', self._subnet.name),
@ -266,7 +267,7 @@ class TestCreateSubnet(TestSubnet):
('ip_version', self._subnet_from_pool.ip_version),
('gateway', self._subnet_from_pool.gateway_ip),
('dns_nameservers', self._subnet_from_pool.dns_nameservers),
('enable_dhcp', self._subnet_from_pool.enable_dhcp),
('dhcp', self._subnet_from_pool.enable_dhcp),
('host_routes', subnet_v2.convert_entries_to_gateway(
self._subnet_from_pool.host_routes)),
('subnet_pool', self._subnet_from_pool.subnetpool_id),
@ -332,7 +333,7 @@ class TestCreateSubnet(TestSubnet):
('ipv6_address_mode', self._subnet_ipv6.ipv6_address_mode),
('gateway', self._subnet_ipv6.gateway_ip),
('dns_nameservers', self._subnet_ipv6.dns_nameservers),
('enable_dhcp', self._subnet_ipv6.enable_dhcp),
('dhcp', self._subnet_ipv6.enable_dhcp),
('host_routes', subnet_v2.convert_entries_to_gateway(
self._subnet_ipv6.host_routes)),
('allocation_pools', self._subnet_ipv6.allocation_pools),
@ -469,6 +470,73 @@ class TestListSubnet(TestSubnet):
self.assertEqual(self.data_long, list(data))
class TestSetSubnet(TestSubnet):
_subnet = network_fakes.FakeSubnet.create_one_subnet()
def setUp(self):
super(TestSetSubnet, self).setUp()
self.network.update_subnet = mock.Mock(return_value=None)
self.network.find_subnet = mock.Mock(return_value=self._subnet)
self.cmd = subnet_v2.SetSubnet(self.app, self.namespace)
def test_set_this(self):
arglist = [
"--name", "new_subnet",
"--dhcp",
"--gateway", self._subnet.gateway_ip,
self._subnet.name,
]
verifylist = [
('name', "new_subnet"),
('dhcp', True),
('gateway', self._subnet.gateway_ip),
('subnet', self._subnet.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
attrs = {
'enable_dhcp': True,
'gateway_ip': self._subnet.gateway_ip,
'name': "new_subnet",
}
self.network.update_subnet.assert_called_with(self._subnet, **attrs)
self.assertIsNone(result)
def test_set_that(self):
arglist = [
"--name", "new_subnet",
"--no-dhcp",
"--gateway", "none",
self._subnet.name,
]
verifylist = [
('name', "new_subnet"),
('no_dhcp', True),
('gateway', "none"),
('subnet', self._subnet.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
attrs = {
'enable_dhcp': False,
'gateway_ip': None,
'name': "new_subnet",
}
self.network.update_subnet.assert_called_with(self._subnet, **attrs)
self.assertIsNone(result)
def test_set_nothing(self):
arglist = [self._subnet.name, ]
verifylist = [('subnet', self._subnet.name)]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.assertRaises(exceptions.CommandError, self.cmd.take_action,
parsed_args)
class TestShowSubnet(TestSubnet):
# The subnets to be shown
_subnet = network_fakes.FakeSubnet.create_one_subnet()

View File

@ -0,0 +1,5 @@
---
features:
- |
Add ``subnet set`` command.
[Bug `1542363 <https://bugs.launchpad.net/bugs/1542363>`_]

View File

@ -355,6 +355,7 @@ openstack.network.v2 =
subnet_create = openstackclient.network.v2.subnet:CreateSubnet
subnet_delete = openstackclient.network.v2.subnet:DeleteSubnet
subnet_list = openstackclient.network.v2.subnet:ListSubnet
subnet_set = openstackclient.network.v2.subnet:SetSubnet
subnet_show = openstackclient.network.v2.subnet:ShowSubnet
subnet_pool_create = openstackclient.network.v2.subnet_pool:CreateSubnetPool