diff --git a/doc/source/command-objects/volume.rst b/doc/source/command-objects/volume.rst index 56c5455283..67598e63c3 100644 --- a/doc/source/command-objects/volume.rst +++ b/doc/source/command-objects/volume.rst @@ -269,6 +269,7 @@ Set volume properties [--name ] [--size ] [--description ] + [--no-property] [--property [...] ] [--image-property [...] ] [--state ] @@ -290,6 +291,12 @@ Set volume properties New volume description +.. option:: --no-property + + Remove all properties from :ref:`\ ` + (specify both :option:`--no-property` and :option:`--property` to + remove the current properties before setting new properties.) + .. option:: --property Set a property on this volume (repeat option to set multiple properties) @@ -304,7 +311,7 @@ Set volume properties Migration policy while re-typing volume ("never" or "on-demand", default is "never" ) - (available only when "--type" option is specified) + (available only when :option:`--type` option is specified) *Volume version 2 only* diff --git a/openstackclient/tests/functional/volume/v1/test_volume.py b/openstackclient/tests/functional/volume/v1/test_volume.py index 992dfd66bf..3f04e07154 100644 --- a/openstackclient/tests/functional/volume/v1/test_volume.py +++ b/openstackclient/tests/functional/volume/v1/test_volume.py @@ -141,8 +141,9 @@ class VolumeTests(common.BaseVolumeTests): '--name ' + new_name + ' --size 2 ' + '--description bbbb ' + - '--property Alpha=c ' + + '--no-property ' + '--property Beta=b ' + + '--property Gamma=c ' + '--bootable ' + name, ) @@ -165,7 +166,7 @@ class VolumeTests(common.BaseVolumeTests): cmd_output["display_description"], ) self.assertEqual( - "Alpha='c', Beta='b'", + "Beta='b', Gamma='c'", cmd_output["properties"], ) self.assertEqual( @@ -176,7 +177,7 @@ class VolumeTests(common.BaseVolumeTests): # Test volume unset raw_output = self.openstack( 'volume unset ' + - '--property Alpha ' + + '--property Beta ' + new_name, ) self.assertOutput('', raw_output) @@ -186,7 +187,7 @@ class VolumeTests(common.BaseVolumeTests): new_name )) self.assertEqual( - "Beta='b'", + "Gamma='c'", cmd_output["properties"], ) diff --git a/openstackclient/tests/functional/volume/v2/test_volume.py b/openstackclient/tests/functional/volume/v2/test_volume.py index 203ca819a5..ce98236f8d 100644 --- a/openstackclient/tests/functional/volume/v2/test_volume.py +++ b/openstackclient/tests/functional/volume/v2/test_volume.py @@ -104,7 +104,7 @@ class VolumeTests(common.BaseVolumeTests): # TODO(qiangjiahui): Add project option to filter tests when we can # specify volume with project - def test_volume_set(self): + def test_volume_set_and_unset(self): """Tests create volume, set, unset, show, delete""" name = uuid.uuid4().hex new_name = name + "_" @@ -144,8 +144,11 @@ class VolumeTests(common.BaseVolumeTests): '--name ' + new_name + ' --size 2 ' + '--description bbbb ' + - '--property Alpha=c ' + + '--no-property ' + '--property Beta=b ' + + '--property Gamma=c ' + + '--image-property a=b ' + + '--image-property c=d ' + '--bootable ' + name, ) @@ -168,9 +171,13 @@ class VolumeTests(common.BaseVolumeTests): cmd_output["description"], ) self.assertEqual( - "Alpha='c', Beta='b'", + "Beta='b', Gamma='c'", cmd_output["properties"], ) + self.assertEqual( + {'a': 'b', 'c': 'd'}, + cmd_output["volume_image_metadata"], + ) self.assertEqual( 'true', cmd_output["bootable"], @@ -179,7 +186,8 @@ class VolumeTests(common.BaseVolumeTests): # Test volume unset raw_output = self.openstack( 'volume unset ' + - '--property Alpha ' + + '--property Beta ' + + '--image-property a ' + new_name, ) self.assertOutput('', raw_output) @@ -189,9 +197,13 @@ class VolumeTests(common.BaseVolumeTests): new_name )) self.assertEqual( - "Beta='b'", + "Gamma='c'", cmd_output["properties"], ) + self.assertEqual( + {'c': 'd'}, + cmd_output["volume_image_metadata"], + ) def test_volume_snapshot(self): """Tests volume create from snapshot""" diff --git a/openstackclient/tests/unit/volume/v1/test_volume.py b/openstackclient/tests/unit/volume/v1/test_volume.py index 6c6d9a1d6f..d46a7ba950 100644 --- a/openstackclient/tests/unit/volume/v1/test_volume.py +++ b/openstackclient/tests/unit/volume/v1/test_volume.py @@ -1071,6 +1071,7 @@ class TestVolumeSet(TestVolume): def test_volume_set_property(self): arglist = [ + '--no-property', '--property', 'myprop=myvalue', self._volume.display_name, ] @@ -1080,6 +1081,7 @@ class TestVolumeSet(TestVolume): ('name', None), ('description', None), ('size', None), + ('no_property', True), ('property', {'myprop': 'myvalue'}), ('volume', self._volume.display_name), ('bootable', False), @@ -1097,6 +1099,10 @@ class TestVolumeSet(TestVolume): self._volume.id, metadata ) + self.volumes_mock.delete_metadata.assert_called_with( + self._volume.id, + self._volume.metadata.keys() + ) self.volumes_mock.update_readonly_flag.assert_not_called() self.assertIsNone(result) diff --git a/openstackclient/tests/unit/volume/v2/test_volume.py b/openstackclient/tests/unit/volume/v2/test_volume.py index 4fef9dd9a6..fbe719f3eb 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume.py +++ b/openstackclient/tests/unit/volume/v2/test_volume.py @@ -1368,6 +1368,24 @@ class TestVolumeSet(TestVolume): # Get the command object to test self.cmd = volume.SetVolume(self.app, None) + def test_volume_set_property(self): + arglist = [ + '--property', 'a=b', + '--property', 'c=d', + self.new_volume.id, + ] + verifylist = [ + ('property', {'a': 'b', 'c': 'd'}), + ('volume', self.new_volume.id), + ('bootable', False), + ('non_bootable', False) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + self.volumes_mock.set_metadata.assert_called_with( + self.new_volume.id, parsed_args.property) + def test_volume_set_image_property(self): arglist = [ '--image-property', 'Alpha=a', diff --git a/openstackclient/volume/v1/volume.py b/openstackclient/volume/v1/volume.py index 739484dfc0..8e1097f516 100644 --- a/openstackclient/volume/v1/volume.py +++ b/openstackclient/volume/v1/volume.py @@ -439,6 +439,15 @@ class SetVolume(command.Command): type=int, help=_('Extend volume size in GB'), ) + parser.add_argument( + "--no-property", + dest="no_property", + action="store_true", + help=_("Remove all properties from " + "(specify both --no-property and --property to " + "remove the current properties before setting " + "new properties.)"), + ) parser.add_argument( '--property', metavar='', @@ -489,6 +498,15 @@ class SetVolume(command.Command): except Exception as e: LOG.error(_("Failed to set volume size: %s"), e) result += 1 + + if parsed_args.no_property: + try: + volume_client.volumes.delete_metadata( + volume.id, volume.metadata.keys()) + except Exception as e: + LOG.error(_("Failed to clean volume properties: %s"), e) + result += 1 + if parsed_args.property: try: volume_client.volumes.set_metadata( diff --git a/openstackclient/volume/v2/volume.py b/openstackclient/volume/v2/volume.py index 78db261bd1..c361d7004c 100644 --- a/openstackclient/volume/v2/volume.py +++ b/openstackclient/volume/v2/volume.py @@ -523,6 +523,15 @@ class SetVolume(command.Command): metavar='', help=_('New volume description'), ) + parser.add_argument( + "--no-property", + dest="no_property", + action="store_true", + help=_("Remove all properties from " + "(specify both --no-property and --property to " + "remove the current properties before setting " + "new properties.)"), + ) parser.add_argument( '--property', metavar='', @@ -561,7 +570,7 @@ class SetVolume(command.Command): choices=['never', 'on-demand'], help=_('Migration policy while re-typing volume ' '("never" or "on-demand", default is "never" ) ' - '(available only when "--type" option is specified)'), + '(available only when --type option is specified)'), ) bootable_group = parser.add_mutually_exclusive_group() bootable_group.add_argument( @@ -607,6 +616,14 @@ class SetVolume(command.Command): LOG.error(_("Failed to set volume size: %s"), e) result += 1 + if parsed_args.no_property: + try: + volume_client.volumes.delete_metadata( + volume.id, volume.metadata.keys()) + except Exception as e: + LOG.error(_("Failed to clean volume properties: %s"), e) + result += 1 + if parsed_args.property: try: volume_client.volumes.set_metadata( diff --git a/releasenotes/notes/support-no-property-in-volume-811e67ff4a199eb6.yaml b/releasenotes/notes/support-no-property-in-volume-811e67ff4a199eb6.yaml new file mode 100644 index 0000000000..fda425e180 --- /dev/null +++ b/releasenotes/notes/support-no-property-in-volume-811e67ff4a199eb6.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Add ``--no-property`` option in ``volume set``, this removes all properties from a volume. + [Blueprint `allow-overwrite-set-options `_]