Merge "Subnet: Add "subnet set" command using SDK"

This commit is contained in:
Jenkins 2016-03-25 15:47:15 +00:00 committed by Gerrit Code Review
commit ef1faf7792
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 to delete (name or ID)
subnet create subnet create
-------------- -------------
Create new subnet Create new subnet
@ -143,6 +143,65 @@ List subnets
List additional fields in output 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 subnet show
----------- -----------

View File

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

View File

@ -17,6 +17,7 @@ import copy
from json.encoder import JSONEncoder from json.encoder import JSONEncoder
from openstackclient.common import command from openstackclient.common import command
from openstackclient.common import exceptions
from openstackclient.common import parseractions from openstackclient.common import parseractions
from openstackclient.common import utils from openstackclient.common import utils
from openstackclient.identity import common as identity_common 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): def _get_columns(item):
columns = list(item.keys()) columns = list(item.keys())
if 'tenant_id' in columns: if 'tenant_id' in columns:
@ -70,11 +104,12 @@ def convert_entries_to_gateway(entries):
return changed_entries return changed_entries
def _get_attrs(client_manager, parsed_args): def _get_attrs(client_manager, parsed_args, is_create=True):
attrs = {} 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) attrs['name'] = str(parsed_args.name)
if is_create:
if 'project' in parsed_args and parsed_args.project is not None: if 'project' in parsed_args and parsed_args.project is not None:
identity_client = client_manager.identity identity_client = client_manager.identity
project_id = identity_common.find_project( project_id = identity_common.find_project(
@ -83,23 +118,15 @@ def _get_attrs(client_manager, parsed_args):
parsed_args.project_domain, parsed_args.project_domain,
).id ).id
attrs['tenant_id'] = project_id attrs['tenant_id'] = project_id
client = client_manager.network client = client_manager.network
attrs['network_id'] = client.find_network(parsed_args.network, attrs['network_id'] = client.find_network(parsed_args.network,
ignore_missing=False).id ignore_missing=False).id
if parsed_args.subnet_pool is not None: if parsed_args.subnet_pool is not None:
subnet_pool = client.find_subnet_pool(parsed_args.subnet_pool, subnet_pool = client.find_subnet_pool(parsed_args.subnet_pool,
ignore_missing=False) ignore_missing=False)
attrs['subnetpool_id'] = subnet_pool.id attrs['subnetpool_id'] = subnet_pool.id
if parsed_args.use_default_subnet_pool: if parsed_args.use_default_subnet_pool:
attrs['use_default_subnetpool'] = True 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: if parsed_args.prefix_length is not None:
attrs['prefixlen'] = parsed_args.prefix_length attrs['prefixlen'] = parsed_args.prefix_length
if parsed_args.subnet_range is not None: 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 attrs['ipv6_ra_mode'] = parsed_args.ipv6_ra_mode
if parsed_args.ipv6_address_mode is not None: if parsed_args.ipv6_address_mode is not None:
attrs['ipv6_address_mode'] = parsed_args.ipv6_address_mode 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 attrs['allocation_pools'] = parsed_args.allocation_pools
if parsed_args.enable_dhcp is not None: if parsed_args.dhcp:
attrs['enable_dhcp'] = parsed_args.enable_dhcp attrs['enable_dhcp'] = True
if parsed_args.dns_nameservers is not None: 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 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 # Change 'gateway' entry to 'nexthop' to match the API
attrs['host_routes'] = convert_entries_to_nexthop( attrs['host_routes'] = convert_entries_to_nexthop(
parsed_args.host_routes) parsed_args.host_routes)
return attrs return attrs
@ -163,38 +206,18 @@ class CreateSubnet(command.ShowOne):
'(required if --subnet-pool is not specified, ' '(required if --subnet-pool is not specified, '
'optional otherwise)', '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 = parser.add_mutually_exclusive_group()
dhcp_enable_group.add_argument( dhcp_enable_group.add_argument(
'--dhcp', '--dhcp',
dest='enable_dhcp',
action='store_true', action='store_true',
default=True, default=True,
help='Enable DHCP (default)', help='Enable DHCP (default)',
) )
dhcp_enable_group.add_argument( dhcp_enable_group.add_argument(
'--no-dhcp', '--no-dhcp',
dest='enable_dhcp', action='store_true',
action='store_false',
help='Disable DHCP', 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( parser.add_argument(
'--gateway', '--gateway',
metavar='<gateway>', metavar='<gateway>',
@ -207,18 +230,6 @@ class CreateSubnet(command.ShowOne):
"e.g.: --gateway 192.168.9.1, --gateway auto, --gateway none" "e.g.: --gateway 192.168.9.1, --gateway auto, --gateway none"
"(default is 'auto')", "(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( parser.add_argument(
'--ip-version', '--ip-version',
type=int, type=int,
@ -246,7 +257,7 @@ class CreateSubnet(command.ShowOne):
metavar='<network>', metavar='<network>',
help='Network this subnet belongs to (name or ID)', help='Network this subnet belongs to (name or ID)',
) )
_get_common_parse_arguments(parser)
return parser return parser
def take_action(self, parsed_args): def take_action(self, parsed_args):
@ -309,6 +320,56 @@ class ListSubnet(command.Lister):
) for s in data)) ) 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): class ShowSubnet(command.ShowOne):
"""Show subnet details""" """Show subnet details"""

View File

@ -14,6 +14,7 @@
import copy import copy
import mock import mock
from openstackclient.common import exceptions
from openstackclient.common import utils from openstackclient.common import utils
from openstackclient.network.v2 import subnet as subnet_v2 from openstackclient.network.v2 import subnet as subnet_v2
from openstackclient.tests import fakes from openstackclient.tests import fakes
@ -203,9 +204,9 @@ class TestCreateSubnet(TestSubnet):
self.network.find_network = mock.Mock(return_value=self._network) self.network.find_network = mock.Mock(return_value=self._network)
arglist = [ arglist = [
self._subnet.name,
"--subnet-range", self._subnet.cidr, "--subnet-range", self._subnet.cidr,
"--network", self._subnet.network_id, "--network", self._subnet.network_id,
self._subnet.name,
] ]
verifylist = [ verifylist = [
('name', self._subnet.name), ('name', self._subnet.name),
@ -266,7 +267,7 @@ class TestCreateSubnet(TestSubnet):
('ip_version', self._subnet_from_pool.ip_version), ('ip_version', self._subnet_from_pool.ip_version),
('gateway', self._subnet_from_pool.gateway_ip), ('gateway', self._subnet_from_pool.gateway_ip),
('dns_nameservers', self._subnet_from_pool.dns_nameservers), ('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( ('host_routes', subnet_v2.convert_entries_to_gateway(
self._subnet_from_pool.host_routes)), self._subnet_from_pool.host_routes)),
('subnet_pool', self._subnet_from_pool.subnetpool_id), ('subnet_pool', self._subnet_from_pool.subnetpool_id),
@ -332,7 +333,7 @@ class TestCreateSubnet(TestSubnet):
('ipv6_address_mode', self._subnet_ipv6.ipv6_address_mode), ('ipv6_address_mode', self._subnet_ipv6.ipv6_address_mode),
('gateway', self._subnet_ipv6.gateway_ip), ('gateway', self._subnet_ipv6.gateway_ip),
('dns_nameservers', self._subnet_ipv6.dns_nameservers), ('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( ('host_routes', subnet_v2.convert_entries_to_gateway(
self._subnet_ipv6.host_routes)), self._subnet_ipv6.host_routes)),
('allocation_pools', self._subnet_ipv6.allocation_pools), ('allocation_pools', self._subnet_ipv6.allocation_pools),
@ -469,6 +470,73 @@ class TestListSubnet(TestSubnet):
self.assertEqual(self.data_long, list(data)) 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): class TestShowSubnet(TestSubnet):
# The subnets to be shown # The subnets to be shown
_subnet = network_fakes.FakeSubnet.create_one_subnet() _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_create = openstackclient.network.v2.subnet:CreateSubnet
subnet_delete = openstackclient.network.v2.subnet:DeleteSubnet subnet_delete = openstackclient.network.v2.subnet:DeleteSubnet
subnet_list = openstackclient.network.v2.subnet:ListSubnet subnet_list = openstackclient.network.v2.subnet:ListSubnet
subnet_set = openstackclient.network.v2.subnet:SetSubnet
subnet_show = openstackclient.network.v2.subnet:ShowSubnet subnet_show = openstackclient.network.v2.subnet:ShowSubnet
subnet_pool_create = openstackclient.network.v2.subnet_pool:CreateSubnetPool subnet_pool_create = openstackclient.network.v2.subnet_pool:CreateSubnetPool