Add "--read-only" and "--read-write" options in "volume set"
Add "--read-only" and "--read-write" options in "volume set" command to set volume access mode. Implements: bp cinder-command-support Change-Id: I76ba85c7d3ff0eb026a9cbd794368d8b2b0d17fe
This commit is contained in:
parent
5e3ec1b42f
commit
daffce3a6a
@ -213,6 +213,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>
|
||||||
@ -239,6 +240,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
|
||||||
|
@ -844,8 +844,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',
|
||||||
@ -860,15 +859,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',
|
||||||
@ -883,12 +878,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 = [
|
||||||
@ -896,6 +888,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),
|
||||||
@ -916,6 +910,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):
|
||||||
@ -943,6 +938,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):
|
||||||
|
|
||||||
|
@ -1033,6 +1033,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)
|
||||||
]
|
]
|
||||||
@ -1042,6 +1044,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):
|
||||||
@ -1090,6 +1093,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):
|
||||||
|
|
||||||
|
@ -388,38 +388,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):
|
||||||
|
@ -473,6 +473,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):
|
||||||
@ -523,6 +534,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>`_]
|
Loading…
Reference in New Issue
Block a user