Skip to content

Commit 1819edf

Browse files
Jenkinsopenstack-gerrit
authored andcommitted
Merge "Add --address-scope option "subnet pool create/set""
2 parents 85c47b7 + b3649a5 commit 1819edf

4 files changed

Lines changed: 176 additions & 8 deletions

File tree

doc/source/command-objects/subnet-pool.rst

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ Create subnet pool
1818
[--min-prefix-length <min-prefix-length>]
1919
[--max-prefix-length <max-prefix-length>]
2020
[--project <project> [--project-domain <project-domain>]]
21+
[--address-scope <address-scope>]
2122
<name>
2223
2324
.. option:: --pool-prefix <pool-prefix>
@@ -46,6 +47,11 @@ Create subnet pool
4647
Domain the project belongs to (name or ID). This can be used in case
4748
collisions between project names exist.
4849
50+
.. option:: --address-scope <address-scope>
51+
52+
Set address scope associated with the subnet pool (name or ID).
53+
Prefixes must be unique across address scopes.
54+
4955
.. _subnet_pool_create-name:
5056
.. describe:: <name>
5157
@@ -96,6 +102,7 @@ Set subnet pool properties
96102
[--default-prefix-length <default-prefix-length>]
97103
[--min-prefix-length <min-prefix-length>]
98104
[--max-prefix-length <max-prefix-length>]
105+
[--address-scope <address-scope> | --no-address-scope]
99106
<subnet-pool>
100107
101108
.. option:: --name <name>
@@ -119,6 +126,15 @@ Set subnet pool properties
119126
120127
Set subnet pool maximum prefix length
121128
129+
.. option:: --address-scope <address-scope>
130+
131+
Set address scope associated with the subnet pool (name or ID).
132+
Prefixes must be unique across address scopes.
133+
134+
.. option:: --no-address-scope
135+
136+
Remove address scope associated with the subnet pool
137+
122138
.. _subnet_pool_set-subnet-pool:
123139
.. describe:: <subnet-pool>
124140

openstackclient/network/v2/subnet_pool.py

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ def _get_columns(item):
3535

3636
def _get_attrs(client_manager, parsed_args):
3737
attrs = {}
38+
network_client = client_manager.network
39+
3840
if parsed_args.name is not None:
3941
attrs['name'] = str(parsed_args.name)
4042
if parsed_args.prefixes is not None:
@@ -46,6 +48,12 @@ def _get_attrs(client_manager, parsed_args):
4648
if parsed_args.max_prefix_length is not None:
4749
attrs['max_prefixlen'] = parsed_args.max_prefix_length
4850

51+
if parsed_args.address_scope is not None:
52+
attrs['address_scope_id'] = network_client.find_address_scope(
53+
parsed_args.address_scope, ignore_missing=False).id
54+
if 'no_address_scope' in parsed_args and parsed_args.no_address_scope:
55+
attrs['address_scope_id'] = None
56+
4957
# "subnet pool set" command doesn't support setting project.
5058
if 'project' in parsed_args and parsed_args.project is not None:
5159
identity_client = client_manager.identity
@@ -105,7 +113,13 @@ def get_parser(self, prog_name):
105113
help="Owner's project (name or ID)",
106114
)
107115
identity_common.add_project_domain_option_to_parser(parser)
108-
116+
parser.add_argument(
117+
'--address-scope',
118+
metavar='<address-scope>',
119+
help="Set address scope associated with the subnet pool "
120+
"(name or ID). Prefixes must be unique across address "
121+
"scopes.",
122+
)
109123
return parser
110124

111125
def take_action(self, parsed_args):
@@ -204,7 +218,19 @@ def get_parser(self, prog_name):
204218
help='Set subnet pool name',
205219
)
206220
_add_prefix_options(parser)
207-
221+
address_scope_group = parser.add_mutually_exclusive_group()
222+
address_scope_group.add_argument(
223+
'--address-scope',
224+
metavar='<address-scope>',
225+
help="Set address scope associated with the subnet pool "
226+
"(name or ID). Prefixes must be unique across address "
227+
"scopes.",
228+
)
229+
address_scope_group.add_argument(
230+
'--no-address-scope',
231+
action='store_true',
232+
help="Remove address scope associated with the subnet pool",
233+
)
208234
return parser
209235

210236
def take_action(self, parsed_args):

openstackclient/tests/network/v2/fakes.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,43 @@ def setUp(self):
7171
)
7272

7373

74+
class FakeAddressScope(object):
75+
"""Fake one or more address scopes."""
76+
77+
@staticmethod
78+
def create_one_address_scope(attrs=None):
79+
"""Create a fake address scope.
80+
81+
:param Dictionary attrs:
82+
A dictionary with all attributes
83+
:return:
84+
A FakeResource object with name, id, etc.
85+
"""
86+
if attrs is None:
87+
attrs = {}
88+
89+
# Set default attributes.
90+
address_scope_attrs = {
91+
'name': 'address-scope-name-' + uuid.uuid4().hex,
92+
'id': 'address-scope-id-' + uuid.uuid4().hex,
93+
'tenant_id': 'project-id-' + uuid.uuid4().hex,
94+
'shared': False,
95+
'ip_version': 4,
96+
}
97+
98+
# Overwrite default attributes.
99+
address_scope_attrs.update(attrs)
100+
101+
address_scope = fakes.FakeResource(
102+
info=copy.deepcopy(address_scope_attrs),
103+
loaded=True)
104+
105+
# Set attributes with special mapping in OpenStack SDK.
106+
address_scope.project_id = address_scope_attrs['tenant_id']
107+
108+
return address_scope
109+
110+
74111
class FakeAvailabilityZone(object):
75112
"""Fake one or more network availability zones (AZs)."""
76113

openstackclient/tests/network/v2/test_subnet_pool.py

Lines changed: 95 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ class TestCreateSubnetPool(TestSubnetPool):
3838
# The new subnet pool to create.
3939
_subnet_pool = network_fakes.FakeSubnetPool.create_one_subnet_pool()
4040

41+
_address_scope = network_fakes.FakeAddressScope.create_one_address_scope()
42+
4143
columns = (
4244
'address_scope_id',
4345
'default_prefixlen',
@@ -76,6 +78,9 @@ def setUp(self):
7678
# Get the command object to test
7779
self.cmd = subnet_pool.CreateSubnetPool(self.app, self.namespace)
7880

81+
self.network.find_address_scope = mock.Mock(
82+
return_value=self._address_scope)
83+
7984
# Set identity client. And get a shortcut to Identity client.
8085
identity_client = identity_fakes_v3.FakeIdentityv3Client(
8186
endpoint=fakes.AUTH_URL,
@@ -193,6 +198,29 @@ def test_create_project_domain(self):
193198
self.assertEqual(self.columns, columns)
194199
self.assertEqual(self.data, data)
195200

201+
def test_create_address_scope_option(self):
202+
arglist = [
203+
'--pool-prefix', '10.0.10.0/24',
204+
'--address-scope', self._address_scope.id,
205+
self._subnet_pool.name,
206+
]
207+
verifylist = [
208+
('prefixes', ['10.0.10.0/24']),
209+
('address_scope', self._address_scope.id),
210+
('name', self._subnet_pool.name),
211+
]
212+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
213+
214+
columns, data = (self.cmd.take_action(parsed_args))
215+
216+
self.network.create_subnet_pool.assert_called_once_with(**{
217+
'prefixes': ['10.0.10.0/24'],
218+
'address_scope_id': self._address_scope.id,
219+
'name': self._subnet_pool.name,
220+
})
221+
self.assertEqual(self.columns, columns)
222+
self.assertEqual(self.data, data)
223+
196224

197225
class TestDeleteSubnetPool(TestSubnetPool):
198226

@@ -301,6 +329,8 @@ class TestSetSubnetPool(TestSubnetPool):
301329
# The subnet_pool to set.
302330
_subnet_pool = network_fakes.FakeSubnetPool.create_one_subnet_pool()
303331

332+
_address_scope = network_fakes.FakeAddressScope.create_one_address_scope()
333+
304334
def setUp(self):
305335
super(TestSetSubnetPool, self).setUp()
306336

@@ -309,21 +339,24 @@ def setUp(self):
309339
self.network.find_subnet_pool = mock.Mock(
310340
return_value=self._subnet_pool)
311341

342+
self.network.find_address_scope = mock.Mock(
343+
return_value=self._address_scope)
344+
312345
# Get the command object to test
313346
self.cmd = subnet_pool.SetSubnetPool(self.app, self.namespace)
314347

315348
def test_set_this(self):
316349
arglist = [
317-
self._subnet_pool.name,
318350
'--name', 'noob',
319351
'--default-prefix-length', '8',
320352
'--min-prefix-length', '8',
353+
self._subnet_pool.name,
321354
]
322355
verifylist = [
323-
('subnet_pool', self._subnet_pool.name),
324356
('name', 'noob'),
325357
('default_prefix_length', '8'),
326358
('min_prefix_length', '8'),
359+
('subnet_pool', self._subnet_pool.name),
327360
]
328361
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
329362

@@ -340,15 +373,15 @@ def test_set_this(self):
340373

341374
def test_set_that(self):
342375
arglist = [
343-
self._subnet_pool.name,
344376
'--pool-prefix', '10.0.1.0/24',
345377
'--pool-prefix', '10.0.2.0/24',
346378
'--max-prefix-length', '16',
379+
self._subnet_pool.name,
347380
]
348381
verifylist = [
349-
('subnet_pool', self._subnet_pool.name),
350382
('prefixes', ['10.0.1.0/24', '10.0.2.0/24']),
351383
('max_prefix_length', '16'),
384+
('subnet_pool', self._subnet_pool.name),
352385
]
353386
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
354387

@@ -374,17 +407,73 @@ def test_set_nothing(self):
374407

375408
def test_set_len_negative(self):
376409
arglist = [
377-
self._subnet_pool.name,
378410
'--max-prefix-length', '-16',
411+
self._subnet_pool.name,
379412
]
380413
verifylist = [
381-
('subnet_pool', self._subnet_pool.name),
382414
('max_prefix_length', '-16'),
415+
('subnet_pool', self._subnet_pool.name),
383416
]
384417

385418
self.assertRaises(argparse.ArgumentTypeError, self.check_parser,
386419
self.cmd, arglist, verifylist)
387420

421+
def test_set_address_scope(self):
422+
arglist = [
423+
'--address-scope', self._address_scope.id,
424+
self._subnet_pool.name,
425+
]
426+
verifylist = [
427+
('address_scope', self._address_scope.id),
428+
('subnet_pool', self._subnet_pool.name),
429+
]
430+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
431+
432+
result = self.cmd.take_action(parsed_args)
433+
434+
attrs = {
435+
'address_scope_id': self._address_scope.id,
436+
}
437+
self.network.update_subnet_pool.assert_called_once_with(
438+
self._subnet_pool, **attrs)
439+
self.assertIsNone(result)
440+
441+
def test_set_no_address_scope(self):
442+
arglist = [
443+
'--no-address-scope',
444+
self._subnet_pool.name,
445+
]
446+
verifylist = [
447+
('no_address_scope', True),
448+
('subnet_pool', self._subnet_pool.name),
449+
]
450+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
451+
452+
result = self.cmd.take_action(parsed_args)
453+
454+
attrs = {
455+
'address_scope_id': None,
456+
}
457+
self.network.update_subnet_pool.assert_called_once_with(
458+
self._subnet_pool, **attrs)
459+
self.assertIsNone(result)
460+
461+
def test_set_no_address_scope_conflict(self):
462+
arglist = [
463+
'--address-scope', self._address_scope.id,
464+
'--no-address-scope',
465+
self._subnet_pool.name,
466+
]
467+
verifylist = [
468+
('address_scope', self._address_scope.id),
469+
('no_address_scope', True),
470+
('subnet_pool', self._subnet_pool.name),
471+
]
472+
473+
# Exclusive arguments will conflict here.
474+
self.assertRaises(tests_utils.ParserException, self.check_parser,
475+
self.cmd, arglist, verifylist)
476+
388477

389478
class TestShowSubnetPool(TestSubnetPool):
390479

0 commit comments

Comments
 (0)