Skip to content

Commit ef1faf7

Browse files
Jenkinsopenstack-gerrit
authored andcommitted
Merge "Subnet: Add "subnet set" command using SDK"
2 parents 860dbc1 + 2b95e36 commit ef1faf7

6 files changed

Lines changed: 275 additions & 84 deletions

File tree

doc/source/command-objects/subnet.rst

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ Delete a subnet
2121
Subnet to delete (name or ID)
2222

2323
subnet create
24-
--------------
24+
-------------
2525

2626
Create new subnet
2727

@@ -143,6 +143,65 @@ List subnets
143143
144144
List additional fields in output
145145
146+
subnet set
147+
----------
148+
149+
Set subnet properties
150+
151+
.. program:: subnet set
152+
.. code:: bash
153+
154+
os subnet set
155+
[--allocation-pool start=<ip-address>,end=<ip-address>]
156+
[--dhcp | --no-dhcp]
157+
[--dns-nameserver <dns-nameserver>]
158+
[--gateway <gateway-ip>]
159+
[--host-route destination=<subnet>,gateway=<ip-address>]
160+
[--name <new-name>]
161+
<subnet>
162+
163+
.. option:: --allocation-pool start=<ip-address>,end=<ip-address>
164+
165+
Allocation pool IP addresses for this subnet e.g.:
166+
start=192.168.199.2,end=192.168.199.254 (This option can be repeated)
167+
168+
.. option:: --dhcp
169+
170+
Enable DHCP
171+
172+
.. option:: --no-dhcp
173+
174+
Disable DHCP
175+
176+
.. option:: --dns-nameserver <dns-nameserver>
177+
178+
DNS name server for this subnet (This option can be repeated)
179+
180+
.. option:: --gateway <gateway>
181+
182+
Specify a gateway for the subnet. The options are:
183+
<ip-address>: Specific IP address to use as the gateway
184+
'none': This subnet will not use a gateway
185+
e.g.: --gateway 192.168.9.1, --gateway none
186+
187+
.. option:: --host-route destination=<subnet>,gateway=<ip-address>
188+
189+
Additional route for this subnet e.g.:
190+
destination=10.10.0.0/16,gateway=192.168.71.254
191+
destination: destination subnet (in CIDR notation)
192+
gateway: nexthop IP address
193+
(This option can be repeated)
194+
195+
.. option:: --name
196+
197+
Updated name of the subnet
198+
199+
.. _subnet_set-subnet:
200+
.. describe:: <subnet>
201+
202+
Subnet to modify (name or ID)
203+
204+
146205
subnet show
147206
-----------
148207

functional/tests/network/v2/test_subnet.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@
1212

1313
import uuid
1414

15-
import testtools
16-
1715
from functional.common import test
1816

1917

@@ -49,7 +47,6 @@ def test_subnet_list(self):
4947
raw_output = self.openstack('subnet list' + opts)
5048
self.assertIn(self.NAME, raw_output)
5149

52-
@testtools.skip('bug/1542363')
5350
def test_subnet_set(self):
5451
self.openstack('subnet set --no-dhcp ' + self.NAME)
5552
opts = self.get_show_opts(['name', 'enable_dhcp'])

openstackclient/network/v2/subnet.py

Lines changed: 138 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from json.encoder import JSONEncoder
1818

1919
from openstackclient.common import command
20+
from openstackclient.common import exceptions
2021
from openstackclient.common import parseractions
2122
from openstackclient.common import utils
2223
from openstackclient.identity import common as identity_common
@@ -42,6 +43,39 @@ def _format_host_routes(data):
4243
}
4344

4445

46+
def _get_common_parse_arguments(parser):
47+
parser.add_argument(
48+
'--allocation-pool',
49+
metavar='start=<ip-address>,end=<ip-address>',
50+
dest='allocation_pools',
51+
action=parseractions.MultiKeyValueAction,
52+
required_keys=['start', 'end'],
53+
help='Allocation pool IP addresses for this subnet '
54+
'e.g.: start=192.168.199.2,end=192.168.199.254 '
55+
'(This option can be repeated)',
56+
)
57+
parser.add_argument(
58+
'--dns-nameserver',
59+
metavar='<dns-nameserver>',
60+
action='append',
61+
dest='dns_nameservers',
62+
help='DNS name server for this subnet '
63+
'(This option can be repeated)',
64+
)
65+
parser.add_argument(
66+
'--host-route',
67+
metavar='destination=<subnet>,gateway=<ip-address>',
68+
dest='host_routes',
69+
action=parseractions.MultiKeyValueAction,
70+
required_keys=['destination', 'gateway'],
71+
help='Additional route for this subnet '
72+
'e.g.: destination=10.10.0.0/16,gateway=192.168.71.254 '
73+
'destination: destination subnet (in CIDR notation) '
74+
'gateway: nexthop IP address '
75+
'(This option can be repeated)',
76+
)
77+
78+
4579
def _get_columns(item):
4680
columns = list(item.keys())
4781
if 'tenant_id' in columns:
@@ -70,57 +104,66 @@ def convert_entries_to_gateway(entries):
70104
return changed_entries
71105

72106

73-
def _get_attrs(client_manager, parsed_args):
107+
def _get_attrs(client_manager, parsed_args, is_create=True):
74108
attrs = {}
75-
if parsed_args.name is not None:
109+
if 'name' in parsed_args and parsed_args.name is not None:
76110
attrs['name'] = str(parsed_args.name)
77111

78-
if 'project' in parsed_args and parsed_args.project is not None:
79-
identity_client = client_manager.identity
80-
project_id = identity_common.find_project(
81-
identity_client,
82-
parsed_args.project,
83-
parsed_args.project_domain,
84-
).id
85-
attrs['tenant_id'] = project_id
86-
87-
client = client_manager.network
88-
attrs['network_id'] = client.find_network(parsed_args.network,
89-
ignore_missing=False).id
90-
91-
if parsed_args.subnet_pool is not None:
92-
subnet_pool = client.find_subnet_pool(parsed_args.subnet_pool,
93-
ignore_missing=False)
94-
attrs['subnetpool_id'] = subnet_pool.id
95-
96-
if parsed_args.use_default_subnet_pool:
97-
attrs['use_default_subnetpool'] = True
98-
if parsed_args.gateway.lower() != 'auto':
99-
if parsed_args.gateway.lower() == 'none':
100-
attrs['gateway_ip'] = None
101-
else:
102-
attrs['gateway_ip'] = parsed_args.gateway
103-
if parsed_args.prefix_length is not None:
104-
attrs['prefixlen'] = parsed_args.prefix_length
105-
if parsed_args.subnet_range is not None:
106-
attrs['cidr'] = parsed_args.subnet_range
107-
if parsed_args.ip_version is not None:
108-
attrs['ip_version'] = parsed_args.ip_version
109-
if parsed_args.ipv6_ra_mode is not None:
110-
attrs['ipv6_ra_mode'] = parsed_args.ipv6_ra_mode
111-
if parsed_args.ipv6_address_mode is not None:
112-
attrs['ipv6_address_mode'] = parsed_args.ipv6_address_mode
113-
if parsed_args.allocation_pools is not None:
112+
if is_create:
113+
if 'project' in parsed_args and parsed_args.project is not None:
114+
identity_client = client_manager.identity
115+
project_id = identity_common.find_project(
116+
identity_client,
117+
parsed_args.project,
118+
parsed_args.project_domain,
119+
).id
120+
attrs['tenant_id'] = project_id
121+
client = client_manager.network
122+
attrs['network_id'] = client.find_network(parsed_args.network,
123+
ignore_missing=False).id
124+
if parsed_args.subnet_pool is not None:
125+
subnet_pool = client.find_subnet_pool(parsed_args.subnet_pool,
126+
ignore_missing=False)
127+
attrs['subnetpool_id'] = subnet_pool.id
128+
if parsed_args.use_default_subnet_pool:
129+
attrs['use_default_subnetpool'] = True
130+
if parsed_args.prefix_length is not None:
131+
attrs['prefixlen'] = parsed_args.prefix_length
132+
if parsed_args.subnet_range is not None:
133+
attrs['cidr'] = parsed_args.subnet_range
134+
if parsed_args.ip_version is not None:
135+
attrs['ip_version'] = parsed_args.ip_version
136+
if parsed_args.ipv6_ra_mode is not None:
137+
attrs['ipv6_ra_mode'] = parsed_args.ipv6_ra_mode
138+
if parsed_args.ipv6_address_mode is not None:
139+
attrs['ipv6_address_mode'] = parsed_args.ipv6_address_mode
140+
141+
if 'gateway' in parsed_args and parsed_args.gateway is not None:
142+
gateway = parsed_args.gateway.lower()
143+
144+
if not is_create and gateway == 'auto':
145+
raise exceptions.CommandError("Auto option is not available"
146+
" for Subnet Set. Valid options are"
147+
" <ip-address> or none")
148+
elif gateway != 'auto':
149+
if gateway == 'none':
150+
attrs['gateway_ip'] = None
151+
else:
152+
attrs['gateway_ip'] = gateway
153+
if ('allocation_pools' in parsed_args and
154+
parsed_args.allocation_pools is not None):
114155
attrs['allocation_pools'] = parsed_args.allocation_pools
115-
if parsed_args.enable_dhcp is not None:
116-
attrs['enable_dhcp'] = parsed_args.enable_dhcp
117-
if parsed_args.dns_nameservers is not None:
156+
if parsed_args.dhcp:
157+
attrs['enable_dhcp'] = True
158+
elif parsed_args.no_dhcp:
159+
attrs['enable_dhcp'] = False
160+
if ('dns_nameservers' in parsed_args and
161+
parsed_args.dns_nameservers is not None):
118162
attrs['dns_nameservers'] = parsed_args.dns_nameservers
119-
if parsed_args.host_routes is not None:
163+
if 'host_routes' in parsed_args and parsed_args.host_routes is not None:
120164
# Change 'gateway' entry to 'nexthop' to match the API
121165
attrs['host_routes'] = convert_entries_to_nexthop(
122166
parsed_args.host_routes)
123-
124167
return attrs
125168

126169

@@ -163,38 +206,18 @@ def get_parser(self, prog_name):
163206
'(required if --subnet-pool is not specified, '
164207
'optional otherwise)',
165208
)
166-
parser.add_argument(
167-
'--allocation-pool',
168-
metavar='start=<ip-address>,end=<ip-address>',
169-
dest='allocation_pools',
170-
action=parseractions.MultiKeyValueAction,
171-
required_keys=['start', 'end'],
172-
help='Allocation pool IP addresses for this subnet '
173-
'e.g.: start=192.168.199.2,end=192.168.199.254 '
174-
'(This option can be repeated)',
175-
)
176209
dhcp_enable_group = parser.add_mutually_exclusive_group()
177210
dhcp_enable_group.add_argument(
178211
'--dhcp',
179-
dest='enable_dhcp',
180212
action='store_true',
181213
default=True,
182214
help='Enable DHCP (default)',
183215
)
184216
dhcp_enable_group.add_argument(
185217
'--no-dhcp',
186-
dest='enable_dhcp',
187-
action='store_false',
218+
action='store_true',
188219
help='Disable DHCP',
189220
)
190-
parser.add_argument(
191-
'--dns-nameserver',
192-
metavar='<dns-nameserver>',
193-
action='append',
194-
dest='dns_nameservers',
195-
help='DNS name server for this subnet '
196-
'(This option can be repeated)',
197-
)
198221
parser.add_argument(
199222
'--gateway',
200223
metavar='<gateway>',
@@ -207,18 +230,6 @@ def get_parser(self, prog_name):
207230
"e.g.: --gateway 192.168.9.1, --gateway auto, --gateway none"
208231
"(default is 'auto')",
209232
)
210-
parser.add_argument(
211-
'--host-route',
212-
metavar='destination=<subnet>,gateway=<ip-address>',
213-
dest='host_routes',
214-
action=parseractions.MultiKeyValueAction,
215-
required_keys=['destination', 'gateway'],
216-
help='Additional route for this subnet '
217-
'e.g.: destination=10.10.0.0/16,gateway=192.168.71.254 '
218-
'destination: destination subnet (in CIDR notation) '
219-
'gateway: nexthop IP address '
220-
'(This option can be repeated)',
221-
)
222233
parser.add_argument(
223234
'--ip-version',
224235
type=int,
@@ -246,7 +257,7 @@ def get_parser(self, prog_name):
246257
metavar='<network>',
247258
help='Network this subnet belongs to (name or ID)',
248259
)
249-
260+
_get_common_parse_arguments(parser)
250261
return parser
251262

252263
def take_action(self, parsed_args):
@@ -309,6 +320,56 @@ def take_action(self, parsed_args):
309320
) for s in data))
310321

311322

323+
class SetSubnet(command.Command):
324+
"""Set subnet properties"""
325+
326+
def get_parser(self, prog_name):
327+
parser = super(SetSubnet, self).get_parser(prog_name)
328+
parser.add_argument(
329+
'subnet',
330+
metavar="<subnet>",
331+
help=("Subnet to modify (name or ID)")
332+
)
333+
parser.add_argument(
334+
'--name',
335+
metavar='<name>',
336+
help='Updated name of the subnet',
337+
)
338+
dhcp_enable_group = parser.add_mutually_exclusive_group()
339+
dhcp_enable_group.add_argument(
340+
'--dhcp',
341+
action='store_true',
342+
default=None,
343+
help='Enable DHCP',
344+
)
345+
dhcp_enable_group.add_argument(
346+
'--no-dhcp',
347+
action='store_true',
348+
help='Disable DHCP',
349+
)
350+
parser.add_argument(
351+
'--gateway',
352+
metavar='<gateway>',
353+
help="Specify a gateway for the subnet. The options are: "
354+
" <ip-address>: Specific IP address to use as the gateway "
355+
" 'none': This subnet will not use a gateway "
356+
"e.g.: --gateway 192.168.9.1, --gateway none"
357+
)
358+
_get_common_parse_arguments(parser)
359+
return parser
360+
361+
def take_action(self, parsed_args):
362+
client = self.app.client_manager.network
363+
obj = client.find_subnet(parsed_args.subnet, ignore_missing=False)
364+
attrs = _get_attrs(self.app.client_manager, parsed_args,
365+
is_create=False)
366+
if not attrs:
367+
msg = "Nothing specified to be set"
368+
raise exceptions.CommandError(msg)
369+
client.update_subnet(obj, **attrs)
370+
return
371+
372+
312373
class ShowSubnet(command.ShowOne):
313374
"""Show subnet details"""
314375

0 commit comments

Comments
 (0)