Merge "Add "--read-only" and "--read-write" options in "volume set""
This commit is contained in:
		| @@ -255,6 +255,7 @@ Set volume properties | |||||||
|         [--image-property <key=value> [...] ] |         [--image-property <key=value> [...] ] | ||||||
|         [--state <state>] |         [--state <state>] | ||||||
|         [--bootable | --non-bootable] |         [--bootable | --non-bootable] | ||||||
|  |         [--read-only | --read-write] | ||||||
|         <volume> |         <volume> | ||||||
|  |  | ||||||
| .. option:: --name <name> | .. option:: --name <name> | ||||||
| @@ -281,6 +282,14 @@ Set volume properties | |||||||
|  |  | ||||||
|     Mark volume as non-bootable |     Mark volume as non-bootable | ||||||
|  |  | ||||||
|  | .. option:: --read-only | ||||||
|  |  | ||||||
|  |     Set volume to read-only access mode | ||||||
|  |  | ||||||
|  | .. option:: --read-write | ||||||
|  |  | ||||||
|  |     Set volume to read-write access mode | ||||||
|  |  | ||||||
| .. option:: --image-property <key=value> | .. option:: --image-property <key=value> | ||||||
|  |  | ||||||
|     Set an image property on this volume |     Set an image property on this volume | ||||||
|   | |||||||
| @@ -906,8 +906,7 @@ class TestVolumeSet(TestVolume): | |||||||
|         ) |         ) | ||||||
|         self.assertIsNone(result) |         self.assertIsNone(result) | ||||||
|  |  | ||||||
|     @mock.patch.object(volume.LOG, 'error') |     def test_volume_set_size_smaller(self): | ||||||
|     def test_volume_set_size_smaller(self, mock_log_error): |  | ||||||
|         self._volume.status = 'available' |         self._volume.status = 'available' | ||||||
|         arglist = [ |         arglist = [ | ||||||
|             '--size', '1', |             '--size', '1', | ||||||
| @@ -922,15 +921,11 @@ class TestVolumeSet(TestVolume): | |||||||
|         ] |         ] | ||||||
|         parsed_args = self.check_parser(self.cmd, arglist, verifylist) |         parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||||||
|  |  | ||||||
|         result = self.cmd.take_action(parsed_args) |         self.assertRaises(exceptions.CommandError, | ||||||
|  |                           self.cmd.take_action, | ||||||
|  |                           parsed_args) | ||||||
|  |  | ||||||
|         mock_log_error.assert_called_with("New size must be greater " |     def test_volume_set_size_not_available(self): | ||||||
|                                           "than %s GB", |  | ||||||
|                                           self._volume.size) |  | ||||||
|         self.assertIsNone(result) |  | ||||||
|  |  | ||||||
|     @mock.patch.object(volume.LOG, 'error') |  | ||||||
|     def test_volume_set_size_not_available(self, mock_log_error): |  | ||||||
|         self._volume.status = 'error' |         self._volume.status = 'error' | ||||||
|         arglist = [ |         arglist = [ | ||||||
|             '--size', '130', |             '--size', '130', | ||||||
| @@ -945,12 +940,9 @@ class TestVolumeSet(TestVolume): | |||||||
|         ] |         ] | ||||||
|         parsed_args = self.check_parser(self.cmd, arglist, verifylist) |         parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||||||
|  |  | ||||||
|         result = self.cmd.take_action(parsed_args) |         self.assertRaises(exceptions.CommandError, | ||||||
|  |                           self.cmd.take_action, | ||||||
|         mock_log_error.assert_called_with("Volume is in %s state, it must be " |                           parsed_args) | ||||||
|                                           "available before size can be " |  | ||||||
|                                           "extended", 'error') |  | ||||||
|         self.assertIsNone(result) |  | ||||||
|  |  | ||||||
|     def test_volume_set_property(self): |     def test_volume_set_property(self): | ||||||
|         arglist = [ |         arglist = [ | ||||||
| @@ -958,6 +950,8 @@ class TestVolumeSet(TestVolume): | |||||||
|             self._volume.display_name, |             self._volume.display_name, | ||||||
|         ] |         ] | ||||||
|         verifylist = [ |         verifylist = [ | ||||||
|  |             ('read_only', False), | ||||||
|  |             ('read_write', False), | ||||||
|             ('name', None), |             ('name', None), | ||||||
|             ('description', None), |             ('description', None), | ||||||
|             ('size', None), |             ('size', None), | ||||||
| @@ -978,6 +972,7 @@ class TestVolumeSet(TestVolume): | |||||||
|             self._volume.id, |             self._volume.id, | ||||||
|             metadata |             metadata | ||||||
|         ) |         ) | ||||||
|  |         self.volumes_mock.update_readonly_flag.assert_not_called() | ||||||
|         self.assertIsNone(result) |         self.assertIsNone(result) | ||||||
|  |  | ||||||
|     def test_volume_set_bootable(self): |     def test_volume_set_bootable(self): | ||||||
| @@ -1005,6 +1000,44 @@ class TestVolumeSet(TestVolume): | |||||||
|             self.volumes_mock.set_bootable.assert_called_with( |             self.volumes_mock.set_bootable.assert_called_with( | ||||||
|                 self._volume.id, verifylist[index][0][1]) |                 self._volume.id, verifylist[index][0][1]) | ||||||
|  |  | ||||||
|  |     def test_volume_set_readonly(self): | ||||||
|  |         arglist = [ | ||||||
|  |             '--read-only', | ||||||
|  |             self._volume.id | ||||||
|  |         ] | ||||||
|  |         verifylist = [ | ||||||
|  |             ('read_only', True), | ||||||
|  |             ('read_write', False), | ||||||
|  |             ('volume', self._volume.id) | ||||||
|  |         ] | ||||||
|  |  | ||||||
|  |         parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||||||
|  |  | ||||||
|  |         result = self.cmd.take_action(parsed_args) | ||||||
|  |         self.volumes_mock.update_readonly_flag.assert_called_once_with( | ||||||
|  |             self._volume.id, | ||||||
|  |             True) | ||||||
|  |         self.assertIsNone(result) | ||||||
|  |  | ||||||
|  |     def test_volume_set_read_write(self): | ||||||
|  |         arglist = [ | ||||||
|  |             '--read-write', | ||||||
|  |             self._volume.id | ||||||
|  |         ] | ||||||
|  |         verifylist = [ | ||||||
|  |             ('read_only', False), | ||||||
|  |             ('read_write', True), | ||||||
|  |             ('volume', self._volume.id) | ||||||
|  |         ] | ||||||
|  |  | ||||||
|  |         parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||||||
|  |  | ||||||
|  |         result = self.cmd.take_action(parsed_args) | ||||||
|  |         self.volumes_mock.update_readonly_flag.assert_called_once_with( | ||||||
|  |             self._volume.id, | ||||||
|  |             False) | ||||||
|  |         self.assertIsNone(result) | ||||||
|  |  | ||||||
|  |  | ||||||
| class TestVolumeShow(TestVolume): | class TestVolumeShow(TestVolume): | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1123,6 +1123,8 @@ class TestVolumeSet(TestVolume): | |||||||
|             self.new_volume.id |             self.new_volume.id | ||||||
|         ] |         ] | ||||||
|         verifylist = [ |         verifylist = [ | ||||||
|  |             ('read_only', False), | ||||||
|  |             ('read_write', False), | ||||||
|             ('state', 'error'), |             ('state', 'error'), | ||||||
|             ('volume', self.new_volume.id) |             ('volume', self.new_volume.id) | ||||||
|         ] |         ] | ||||||
| @@ -1132,6 +1134,7 @@ class TestVolumeSet(TestVolume): | |||||||
|         result = self.cmd.take_action(parsed_args) |         result = self.cmd.take_action(parsed_args) | ||||||
|         self.volumes_mock.reset_state.assert_called_with( |         self.volumes_mock.reset_state.assert_called_with( | ||||||
|             self.new_volume.id, 'error') |             self.new_volume.id, 'error') | ||||||
|  |         self.volumes_mock.update_readonly_flag.assert_not_called() | ||||||
|         self.assertIsNone(result) |         self.assertIsNone(result) | ||||||
|  |  | ||||||
|     def test_volume_set_state_failed(self): |     def test_volume_set_state_failed(self): | ||||||
| @@ -1180,6 +1183,44 @@ class TestVolumeSet(TestVolume): | |||||||
|             self.volumes_mock.set_bootable.assert_called_with( |             self.volumes_mock.set_bootable.assert_called_with( | ||||||
|                 self.new_volume.id, verifylist[index][0][1]) |                 self.new_volume.id, verifylist[index][0][1]) | ||||||
|  |  | ||||||
|  |     def test_volume_set_readonly(self): | ||||||
|  |         arglist = [ | ||||||
|  |             '--read-only', | ||||||
|  |             self.new_volume.id | ||||||
|  |         ] | ||||||
|  |         verifylist = [ | ||||||
|  |             ('read_only', True), | ||||||
|  |             ('read_write', False), | ||||||
|  |             ('volume', self.new_volume.id) | ||||||
|  |         ] | ||||||
|  |  | ||||||
|  |         parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||||||
|  |  | ||||||
|  |         result = self.cmd.take_action(parsed_args) | ||||||
|  |         self.volumes_mock.update_readonly_flag.assert_called_once_with( | ||||||
|  |             self.new_volume.id, | ||||||
|  |             True) | ||||||
|  |         self.assertIsNone(result) | ||||||
|  |  | ||||||
|  |     def test_volume_set_read_write(self): | ||||||
|  |         arglist = [ | ||||||
|  |             '--read-write', | ||||||
|  |             self.new_volume.id | ||||||
|  |         ] | ||||||
|  |         verifylist = [ | ||||||
|  |             ('read_only', False), | ||||||
|  |             ('read_write', True), | ||||||
|  |             ('volume', self.new_volume.id) | ||||||
|  |         ] | ||||||
|  |  | ||||||
|  |         parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||||||
|  |  | ||||||
|  |         result = self.cmd.take_action(parsed_args) | ||||||
|  |         self.volumes_mock.update_readonly_flag.assert_called_once_with( | ||||||
|  |             self.new_volume.id, | ||||||
|  |             False) | ||||||
|  |         self.assertIsNone(result) | ||||||
|  |  | ||||||
|  |  | ||||||
| class TestVolumeShow(TestVolume): | class TestVolumeShow(TestVolume): | ||||||
|  |  | ||||||
|   | |||||||
| @@ -419,38 +419,78 @@ class SetVolume(command.Command): | |||||||
|             action="store_true", |             action="store_true", | ||||||
|             help=_("Mark volume as non-bootable") |             help=_("Mark volume as non-bootable") | ||||||
|         ) |         ) | ||||||
|  |         readonly_group = parser.add_mutually_exclusive_group() | ||||||
|  |         readonly_group.add_argument( | ||||||
|  |             "--read-only", | ||||||
|  |             action="store_true", | ||||||
|  |             help=_("Set volume to read-only access mode") | ||||||
|  |         ) | ||||||
|  |         readonly_group.add_argument( | ||||||
|  |             "--read-write", | ||||||
|  |             action="store_true", | ||||||
|  |             help=_("Set volume to read-write access mode") | ||||||
|  |         ) | ||||||
|         return parser |         return parser | ||||||
|  |  | ||||||
|     def take_action(self, parsed_args): |     def take_action(self, parsed_args): | ||||||
|         volume_client = self.app.client_manager.volume |         volume_client = self.app.client_manager.volume | ||||||
|         volume = utils.find_resource(volume_client.volumes, parsed_args.volume) |         volume = utils.find_resource(volume_client.volumes, parsed_args.volume) | ||||||
|  |  | ||||||
|  |         result = 0 | ||||||
|         if parsed_args.size: |         if parsed_args.size: | ||||||
|             if volume.status != 'available': |             try: | ||||||
|                 LOG.error(_("Volume is in %s state, it must be available " |                 if volume.status != 'available': | ||||||
|                             "before size can be extended"), volume.status) |                     msg = (_("Volume is in %s state, it must be available " | ||||||
|                 return |                            "before size can be extended") % volume.status) | ||||||
|             if parsed_args.size <= volume.size: |                     raise exceptions.CommandError(msg) | ||||||
|                 LOG.error(_("New size must be greater than %s GB"), |                 if parsed_args.size <= volume.size: | ||||||
|                           volume.size) |                     msg = (_("New size must be greater than %s GB") | ||||||
|                 return |                            % volume.size) | ||||||
|             volume_client.volumes.extend(volume.id, parsed_args.size) |                     raise exceptions.CommandError(msg) | ||||||
|  |                 volume_client.volumes.extend(volume.id, parsed_args.size) | ||||||
|  |             except Exception as e: | ||||||
|  |                 LOG.error(_("Failed to set volume size: %s"), e) | ||||||
|  |                 result += 1 | ||||||
|         if parsed_args.property: |         if parsed_args.property: | ||||||
|             volume_client.volumes.set_metadata(volume.id, parsed_args.property) |             try: | ||||||
|  |                 volume_client.volumes.set_metadata( | ||||||
|  |                     volume.id, | ||||||
|  |                     parsed_args.property) | ||||||
|  |             except Exception as e: | ||||||
|  |                 LOG.error(_("Failed to set volume property: %s"), e) | ||||||
|  |                 result += 1 | ||||||
|         if parsed_args.bootable or parsed_args.non_bootable: |         if parsed_args.bootable or parsed_args.non_bootable: | ||||||
|             try: |             try: | ||||||
|                 volume_client.volumes.set_bootable( |                 volume_client.volumes.set_bootable( | ||||||
|                     volume.id, parsed_args.bootable) |                     volume.id, parsed_args.bootable) | ||||||
|             except Exception as e: |             except Exception as e: | ||||||
|                 LOG.error(_("Failed to set volume bootable property: %s"), e) |                 LOG.error(_("Failed to set volume bootable property: %s"), e) | ||||||
|  |                 result += 1 | ||||||
|  |         if parsed_args.read_only or parsed_args.read_write: | ||||||
|  |             try: | ||||||
|  |                 volume_client.volumes.update_readonly_flag( | ||||||
|  |                     volume.id, | ||||||
|  |                     parsed_args.read_only) | ||||||
|  |             except Exception as e: | ||||||
|  |                 LOG.error(_("Failed to set volume read-only access " | ||||||
|  |                             "mode flag: %s"), e) | ||||||
|  |                 result += 1 | ||||||
|         kwargs = {} |         kwargs = {} | ||||||
|         if parsed_args.name: |         if parsed_args.name: | ||||||
|             kwargs['display_name'] = parsed_args.name |             kwargs['display_name'] = parsed_args.name | ||||||
|         if parsed_args.description: |         if parsed_args.description: | ||||||
|             kwargs['display_description'] = parsed_args.description |             kwargs['display_description'] = parsed_args.description | ||||||
|         if kwargs: |         if kwargs: | ||||||
|             volume_client.volumes.update(volume.id, **kwargs) |             try: | ||||||
|  |                 volume_client.volumes.update(volume.id, **kwargs) | ||||||
|  |             except Exception as e: | ||||||
|  |                 LOG.error(_("Failed to update volume display name " | ||||||
|  |                           "or display description: %s"), e) | ||||||
|  |                 result += 1 | ||||||
|  |  | ||||||
|  |         if result > 0: | ||||||
|  |             raise exceptions.CommandError(_("One or more of the " | ||||||
|  |                                           "set operations failed")) | ||||||
|  |  | ||||||
|  |  | ||||||
| class ShowVolume(command.ShowOne): | class ShowVolume(command.ShowOne): | ||||||
|   | |||||||
| @@ -520,6 +520,17 @@ class SetVolume(command.Command): | |||||||
|             action="store_true", |             action="store_true", | ||||||
|             help=_("Mark volume as non-bootable") |             help=_("Mark volume as non-bootable") | ||||||
|         ) |         ) | ||||||
|  |         readonly_group = parser.add_mutually_exclusive_group() | ||||||
|  |         readonly_group.add_argument( | ||||||
|  |             "--read-only", | ||||||
|  |             action="store_true", | ||||||
|  |             help=_("Set volume to read-only access mode") | ||||||
|  |         ) | ||||||
|  |         readonly_group.add_argument( | ||||||
|  |             "--read-write", | ||||||
|  |             action="store_true", | ||||||
|  |             help=_("Set volume to read-write access mode") | ||||||
|  |         ) | ||||||
|         return parser |         return parser | ||||||
|  |  | ||||||
|     def take_action(self, parsed_args): |     def take_action(self, parsed_args): | ||||||
| @@ -570,6 +581,15 @@ class SetVolume(command.Command): | |||||||
|             except Exception as e: |             except Exception as e: | ||||||
|                 LOG.error(_("Failed to set volume bootable property: %s"), e) |                 LOG.error(_("Failed to set volume bootable property: %s"), e) | ||||||
|                 result += 1 |                 result += 1 | ||||||
|  |         if parsed_args.read_only or parsed_args.read_write: | ||||||
|  |             try: | ||||||
|  |                 volume_client.volumes.update_readonly_flag( | ||||||
|  |                     volume.id, | ||||||
|  |                     parsed_args.read_only) | ||||||
|  |             except Exception as e: | ||||||
|  |                 LOG.error(_("Failed to set volume read-only access " | ||||||
|  |                             "mode flag: %s"), e) | ||||||
|  |                 result += 1 | ||||||
|  |  | ||||||
|         kwargs = {} |         kwargs = {} | ||||||
|         if parsed_args.name: |         if parsed_args.name: | ||||||
|   | |||||||
| @@ -0,0 +1,5 @@ | |||||||
|  | --- | ||||||
|  | features: | ||||||
|  |   - | | ||||||
|  |     Add ``--read-only`` and ``--read-write`` options to ``volume set`` command. | ||||||
|  |     [Blueprint `cinder-command-support <https://blueprints.launchpad.net/python-openstackclient/+spec/cinder-command-support>`_] | ||||||
		Reference in New Issue
	
	Block a user
	 Jenkins
					Jenkins