Skip to content

Commit 15069ef

Browse files
Jenkinsopenstack-gerrit
authored andcommitted
Merge "Support "--no" option in aggregate set"
2 parents 2026dee + 6371556 commit 15069ef

5 files changed

Lines changed: 131 additions & 15 deletions

File tree

doc/source/command-objects/aggregate.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ Set aggregate properties
116116
[--name <new-name>]
117117
[--zone <availability-zone>]
118118
[--property <key=value> [...] ]
119+
[--no-property]
119120
<aggregate>
120121
121122
.. option:: --name <name>
@@ -131,6 +132,12 @@ Set aggregate properties
131132
Property to set on :ref:`\<aggregate\> <aggregate_set-aggregate>`
132133
(repeat option to set multiple properties)
133134

135+
.. option:: --no-property
136+
137+
Remove all properties from :ref:`\<aggregate\> <aggregate_set-aggregate>`
138+
(specify both --property and --no-property to
139+
overwrite the current properties)
140+
134141
.. _aggregate_set-aggregate:
135142
.. describe:: <aggregate>
136143

openstackclient/compute/v2/aggregate.py

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,14 @@ def get_parser(self, prog_name):
248248
help=_("Property to set on <aggregate> "
249249
"(repeat option to set multiple properties)")
250250
)
251+
parser.add_argument(
252+
"--no-property",
253+
dest="no_property",
254+
action="store_true",
255+
help=_("Remove all properties from <aggregate> "
256+
"(specify both --property and --no-property to "
257+
"overwrite the current properties)"),
258+
)
251259
return parser
252260

253261
def take_action(self, parsed_args):
@@ -269,10 +277,23 @@ def take_action(self, parsed_args):
269277
kwargs
270278
)
271279

280+
set_property = {}
281+
if parsed_args.no_property:
282+
# NOTE(RuiChen): "availability_zone" is removed from response of
283+
# aggregate show and create commands, don't see it
284+
# anywhere, so pop it, avoid the unexpected server
285+
# exception(can't unset the availability zone from
286+
# aggregate metadata in nova).
287+
set_property.update({key: None
288+
for key in aggregate.metadata.keys()
289+
if key != 'availability_zone'})
272290
if parsed_args.property:
291+
set_property.update(parsed_args.property)
292+
293+
if set_property:
273294
compute_client.aggregates.set_metadata(
274295
aggregate,
275-
parsed_args.property
296+
set_property
276297
)
277298

278299

@@ -326,7 +347,6 @@ def get_parser(self, prog_name):
326347
"--property",
327348
metavar="<key>",
328349
action='append',
329-
required=True,
330350
help=_("Property to remove from aggregate "
331351
"(repeat option to remove multiple properties)")
332352
)
@@ -338,6 +358,9 @@ def take_action(self, parsed_args):
338358
compute_client.aggregates,
339359
parsed_args.aggregate)
340360

341-
unset_property = {key: None for key in parsed_args.property}
342-
compute_client.aggregates.set_metadata(aggregate,
343-
unset_property)
361+
unset_property = {}
362+
if parsed_args.property:
363+
unset_property.update({key: None for key in parsed_args.property})
364+
if unset_property:
365+
compute_client.aggregates.set_metadata(aggregate,
366+
unset_property)

openstackclient/tests/unit/compute/v2/fakes.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ def create_one_aggregate(attrs=None):
8383
"id": "aggregate-id-" + uuid.uuid4().hex,
8484
"metadata": {
8585
"availability_zone": "ag_zone",
86+
"key1": "value1",
8687
}
8788
}
8889

openstackclient/tests/unit/compute/v2/test_aggregate.py

Lines changed: 88 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121

2222
from openstackclient.compute.v2 import aggregate
2323
from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes
24-
from openstackclient.tests.unit import utils as tests_utils
2524

2625

2726
class TestAggregate(compute_fakes.TestComputev2):
@@ -235,7 +234,8 @@ class TestAggregateList(TestAggregate):
235234
TestAggregate.fake_ag.id,
236235
TestAggregate.fake_ag.name,
237236
TestAggregate.fake_ag.availability_zone,
238-
{},
237+
{key: value for key, value in TestAggregate.fake_ag.metadata.items()
238+
if key != 'availability_zone'},
239239
), )
240240

241241
def setUp(self):
@@ -371,6 +371,62 @@ def test_aggregate_set_with_property(self):
371371
self.fake_ag, parsed_args.property)
372372
self.assertIsNone(result)
373373

374+
def test_aggregate_set_with_no_property_and_property(self):
375+
arglist = [
376+
'--no-property',
377+
'--property', 'key2=value2',
378+
'ag1',
379+
]
380+
verifylist = [
381+
('no_property', True),
382+
('property', {'key2': 'value2'}),
383+
('aggregate', 'ag1'),
384+
]
385+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
386+
result = self.cmd.take_action(parsed_args)
387+
self.aggregate_mock.get.assert_called_once_with(parsed_args.aggregate)
388+
self.assertNotCalled(self.aggregate_mock.update)
389+
self.aggregate_mock.set_metadata.assert_called_once_with(
390+
self.fake_ag, {'key1': None, 'key2': 'value2'})
391+
self.assertIsNone(result)
392+
393+
def test_aggregate_set_with_no_property(self):
394+
arglist = [
395+
'--no-property',
396+
'ag1',
397+
]
398+
verifylist = [
399+
('no_property', True),
400+
('aggregate', 'ag1'),
401+
]
402+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
403+
result = self.cmd.take_action(parsed_args)
404+
self.aggregate_mock.get.assert_called_once_with(parsed_args.aggregate)
405+
self.assertNotCalled(self.aggregate_mock.update)
406+
self.aggregate_mock.set_metadata.assert_called_once_with(
407+
self.fake_ag, {'key1': None})
408+
self.assertIsNone(result)
409+
410+
def test_aggregate_set_with_zone_and_no_property(self):
411+
arglist = [
412+
'--zone', 'new_zone',
413+
'--no-property',
414+
'ag1',
415+
]
416+
verifylist = [
417+
('zone', 'new_zone'),
418+
('no_property', True),
419+
('aggregate', 'ag1'),
420+
]
421+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
422+
result = self.cmd.take_action(parsed_args)
423+
self.aggregate_mock.get.assert_called_once_with(parsed_args.aggregate)
424+
self.aggregate_mock.update.assert_called_once_with(
425+
self.fake_ag, {'availability_zone': parsed_args.zone})
426+
self.aggregate_mock.set_metadata.assert_called_once_with(
427+
self.fake_ag, {'key1': None})
428+
self.assertIsNone(result)
429+
374430

375431
class TestAggregateShow(TestAggregate):
376432

@@ -387,7 +443,10 @@ class TestAggregateShow(TestAggregate):
387443
TestAggregate.fake_ag.hosts,
388444
TestAggregate.fake_ag.id,
389445
TestAggregate.fake_ag.name,
390-
'',
446+
utils.format_dict(
447+
{key: value
448+
for key, value in TestAggregate.fake_ag.metadata.items()
449+
if key != 'availability_zone'}),
391450
)
392451

393452
def setUp(self):
@@ -435,13 +494,32 @@ def test_aggregate_unset(self):
435494
self.fake_ag, {'unset_key': None})
436495
self.assertIsNone(result)
437496

438-
def test_aggregate_unset_no_property(self):
497+
def test_aggregate_unset_multiple_properties(self):
439498
arglist = [
499+
'--property', 'unset_key1',
500+
'--property', 'unset_key2',
440501
'ag1',
441502
]
442-
verifylist = None
443-
self.assertRaises(tests_utils.ParserException,
444-
self.check_parser,
445-
self.cmd,
446-
arglist,
447-
verifylist)
503+
verifylist = [
504+
('property', ['unset_key1', 'unset_key2']),
505+
('aggregate', 'ag1'),
506+
]
507+
508+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
509+
result = self.cmd.take_action(parsed_args)
510+
self.aggregate_mock.set_metadata.assert_called_once_with(
511+
self.fake_ag, {'unset_key1': None, 'unset_key2': None})
512+
self.assertIsNone(result)
513+
514+
def test_aggregate_unset_no_option(self):
515+
arglist = [
516+
'ag1',
517+
]
518+
verifylist = [
519+
('property', None),
520+
('aggregate', 'ag1'),
521+
]
522+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
523+
result = self.cmd.take_action(parsed_args)
524+
self.assertNotCalled(self.aggregate_mock.set_metadata)
525+
self.assertIsNone(result)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
features:
3+
- |
4+
Add ``--no-property`` option in ``aggregate set``.
5+
Supporting ``--no-property`` option will apply user a convenience way to
6+
clean all properties of aggregate in a short command.
7+
[Blueprint `support-no-property-in-aggregate <https://blueprints.launchpad.net/python-openstackclient/+spec/support-no-property-in-aggregate>`_]

0 commit comments

Comments
 (0)