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:
parent
a60e31ad4b
commit
2b95e363d3
@ -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
|
||||
-----------
|
||||
|
||||
|
@ -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'])
|
||||
|
@ -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,57 +104,66 @@ 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 'project' in parsed_args and parsed_args.project is not None:
|
||||
identity_client = client_manager.identity
|
||||
project_id = identity_common.find_project(
|
||||
identity_client,
|
||||
parsed_args.project,
|
||||
parsed_args.project_domain,
|
||||
).id
|
||||
attrs['tenant_id'] = project_id
|
||||
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(
|
||||
identity_client,
|
||||
parsed_args.project,
|
||||
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.prefix_length is not None:
|
||||
attrs['prefixlen'] = parsed_args.prefix_length
|
||||
if parsed_args.subnet_range is not None:
|
||||
attrs['cidr'] = parsed_args.subnet_range
|
||||
if parsed_args.ip_version is not None:
|
||||
attrs['ip_version'] = parsed_args.ip_version
|
||||
if parsed_args.ipv6_ra_mode is not None:
|
||||
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
|
||||
|
||||
client = client_manager.network
|
||||
attrs['network_id'] = client.find_network(parsed_args.network,
|
||||
ignore_missing=False).id
|
||||
if 'gateway' in parsed_args and parsed_args.gateway is not None:
|
||||
gateway = parsed_args.gateway.lower()
|
||||
|
||||
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:
|
||||
attrs['cidr'] = parsed_args.subnet_range
|
||||
if parsed_args.ip_version is not None:
|
||||
attrs['ip_version'] = parsed_args.ip_version
|
||||
if parsed_args.ipv6_ra_mode is not None:
|
||||
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 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"""
|
||||
|
||||
|
@ -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()
|
||||
|
5
releasenotes/notes/subnet-set-bbc26ecc16929302.yaml
Normal file
5
releasenotes/notes/subnet-set-bbc26ecc16929302.yaml
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Add ``subnet set`` command.
|
||||
[Bug `1542363 <https://bugs.launchpad.net/bugs/1542363>`_]
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user