Add some options to "volume create" command
Add "--bootable", "--non-bootable", "--read-only" and "--read-write" options to "volume create" command for setting some attributes at the time of crration. Change-Id: I71b4e9fccb4ee0ab1a90e7179d6d2d34dbbae909 Implements: bp cinder-command-support
This commit is contained in:
parent
29587eaa66
commit
7e5a98bca9
@ -24,6 +24,8 @@ Create new volume
|
|||||||
[--property <key=value> [...] ]
|
[--property <key=value> [...] ]
|
||||||
[--hint <key=value> [...] ]
|
[--hint <key=value> [...] ]
|
||||||
[--multi-attach]
|
[--multi-attach]
|
||||||
|
[--bootable | --non-bootable]
|
||||||
|
[--read-only | --read-write]
|
||||||
<name>
|
<name>
|
||||||
|
|
||||||
.. option:: --size <size>
|
.. option:: --size <size>
|
||||||
@ -89,6 +91,22 @@ Create new volume
|
|||||||
|
|
||||||
Allow volume to be attached more than once (default to False)
|
Allow volume to be attached more than once (default to False)
|
||||||
|
|
||||||
|
.. option:: --bootable
|
||||||
|
|
||||||
|
Mark volume as bootable
|
||||||
|
|
||||||
|
.. option:: --non-bootable
|
||||||
|
|
||||||
|
Mark volume as non-bootable (default)
|
||||||
|
|
||||||
|
.. option:: --read-only
|
||||||
|
|
||||||
|
Set volume to read-only access mode
|
||||||
|
|
||||||
|
.. option:: --read-write
|
||||||
|
|
||||||
|
Set volume to read-write access mode (default)
|
||||||
|
|
||||||
.. _volume_create-name:
|
.. _volume_create-name:
|
||||||
.. describe:: <name>
|
.. describe:: <name>
|
||||||
|
|
||||||
|
@ -431,6 +431,142 @@ class TestVolumeCreate(TestVolume):
|
|||||||
self.assertEqual(self.columns, columns)
|
self.assertEqual(self.columns, columns)
|
||||||
self.assertEqual(self.datalist, data)
|
self.assertEqual(self.datalist, data)
|
||||||
|
|
||||||
|
def test_volume_create_with_bootable_and_readonly(self):
|
||||||
|
arglist = [
|
||||||
|
'--bootable',
|
||||||
|
'--read-only',
|
||||||
|
'--size', str(self.new_volume.size),
|
||||||
|
self.new_volume.display_name,
|
||||||
|
]
|
||||||
|
verifylist = [
|
||||||
|
('bootable', True),
|
||||||
|
('non_bootable', False),
|
||||||
|
('read_only', True),
|
||||||
|
('read_write', False),
|
||||||
|
('size', self.new_volume.size),
|
||||||
|
('name', self.new_volume.display_name),
|
||||||
|
]
|
||||||
|
|
||||||
|
parsed_args = self.check_parser(
|
||||||
|
self.cmd, arglist, verifylist)
|
||||||
|
|
||||||
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
|
|
||||||
|
self.volumes_mock.create.assert_called_with(
|
||||||
|
self.new_volume.size,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
self.new_volume.display_name,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(self.columns, columns)
|
||||||
|
self.assertEqual(self.datalist, data)
|
||||||
|
self.volumes_mock.set_bootable.assert_called_with(
|
||||||
|
self.new_volume.id, True)
|
||||||
|
self.volumes_mock.update_readonly_flag.assert_called_with(
|
||||||
|
self.new_volume.id, True)
|
||||||
|
|
||||||
|
def test_volume_create_with_nonbootable_and_readwrite(self):
|
||||||
|
arglist = [
|
||||||
|
'--non-bootable',
|
||||||
|
'--read-write',
|
||||||
|
'--size', str(self.new_volume.size),
|
||||||
|
self.new_volume.display_name,
|
||||||
|
]
|
||||||
|
verifylist = [
|
||||||
|
('bootable', False),
|
||||||
|
('non_bootable', True),
|
||||||
|
('read_only', False),
|
||||||
|
('read_write', True),
|
||||||
|
('size', self.new_volume.size),
|
||||||
|
('name', self.new_volume.display_name),
|
||||||
|
]
|
||||||
|
|
||||||
|
parsed_args = self.check_parser(
|
||||||
|
self.cmd, arglist, verifylist)
|
||||||
|
|
||||||
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
|
|
||||||
|
self.volumes_mock.create.assert_called_with(
|
||||||
|
self.new_volume.size,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
self.new_volume.display_name,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(self.columns, columns)
|
||||||
|
self.assertEqual(self.datalist, data)
|
||||||
|
self.volumes_mock.set_bootable.assert_called_with(
|
||||||
|
self.new_volume.id, False)
|
||||||
|
self.volumes_mock.update_readonly_flag.assert_called_with(
|
||||||
|
self.new_volume.id, False)
|
||||||
|
|
||||||
|
@mock.patch.object(volume.LOG, 'error')
|
||||||
|
def test_volume_create_with_bootable_and_readonly_fail(
|
||||||
|
self, mock_error):
|
||||||
|
|
||||||
|
self.volumes_mock.set_bootable.side_effect = (
|
||||||
|
exceptions.CommandError())
|
||||||
|
|
||||||
|
self.volumes_mock.update_readonly_flag.side_effect = (
|
||||||
|
exceptions.CommandError())
|
||||||
|
|
||||||
|
arglist = [
|
||||||
|
'--bootable',
|
||||||
|
'--read-only',
|
||||||
|
'--size', str(self.new_volume.size),
|
||||||
|
self.new_volume.display_name,
|
||||||
|
]
|
||||||
|
verifylist = [
|
||||||
|
('bootable', True),
|
||||||
|
('non_bootable', False),
|
||||||
|
('read_only', True),
|
||||||
|
('read_write', False),
|
||||||
|
('size', self.new_volume.size),
|
||||||
|
('name', self.new_volume.display_name),
|
||||||
|
]
|
||||||
|
|
||||||
|
parsed_args = self.check_parser(
|
||||||
|
self.cmd, arglist, verifylist)
|
||||||
|
|
||||||
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
|
|
||||||
|
self.volumes_mock.create.assert_called_with(
|
||||||
|
self.new_volume.size,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
self.new_volume.display_name,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(2, mock_error.call_count)
|
||||||
|
self.assertEqual(self.columns, columns)
|
||||||
|
self.assertEqual(self.datalist, data)
|
||||||
|
self.volumes_mock.set_bootable.assert_called_with(
|
||||||
|
self.new_volume.id, True)
|
||||||
|
self.volumes_mock.update_readonly_flag.assert_called_with(
|
||||||
|
self.new_volume.id, True)
|
||||||
|
|
||||||
def test_volume_create_without_size(self):
|
def test_volume_create_without_size(self):
|
||||||
arglist = [
|
arglist = [
|
||||||
self.new_volume.display_name,
|
self.new_volume.display_name,
|
||||||
|
@ -450,6 +450,154 @@ class TestVolumeCreate(TestVolume):
|
|||||||
self.assertEqual(self.columns, columns)
|
self.assertEqual(self.columns, columns)
|
||||||
self.assertEqual(self.datalist, data)
|
self.assertEqual(self.datalist, data)
|
||||||
|
|
||||||
|
def test_volume_create_with_bootable_and_readonly(self):
|
||||||
|
arglist = [
|
||||||
|
'--bootable',
|
||||||
|
'--read-only',
|
||||||
|
'--size', str(self.new_volume.size),
|
||||||
|
self.new_volume.name,
|
||||||
|
]
|
||||||
|
verifylist = [
|
||||||
|
('bootable', True),
|
||||||
|
('non_bootable', False),
|
||||||
|
('read_only', True),
|
||||||
|
('read_write', False),
|
||||||
|
('size', self.new_volume.size),
|
||||||
|
('name', self.new_volume.name),
|
||||||
|
]
|
||||||
|
|
||||||
|
parsed_args = self.check_parser(
|
||||||
|
self.cmd, arglist, verifylist)
|
||||||
|
|
||||||
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
|
|
||||||
|
self.volumes_mock.create.assert_called_with(
|
||||||
|
size=self.new_volume.size,
|
||||||
|
snapshot_id=None,
|
||||||
|
name=self.new_volume.name,
|
||||||
|
description=None,
|
||||||
|
volume_type=None,
|
||||||
|
user_id=None,
|
||||||
|
project_id=None,
|
||||||
|
availability_zone=None,
|
||||||
|
metadata=None,
|
||||||
|
imageRef=None,
|
||||||
|
source_volid=None,
|
||||||
|
consistencygroup_id=None,
|
||||||
|
source_replica=None,
|
||||||
|
multiattach=False,
|
||||||
|
scheduler_hints=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(self.columns, columns)
|
||||||
|
self.assertEqual(self.datalist, data)
|
||||||
|
self.volumes_mock.set_bootable.assert_called_with(
|
||||||
|
self.new_volume.id, True)
|
||||||
|
self.volumes_mock.update_readonly_flag.assert_called_with(
|
||||||
|
self.new_volume.id, True)
|
||||||
|
|
||||||
|
def test_volume_create_with_nonbootable_and_readwrite(self):
|
||||||
|
arglist = [
|
||||||
|
'--non-bootable',
|
||||||
|
'--read-write',
|
||||||
|
'--size', str(self.new_volume.size),
|
||||||
|
self.new_volume.name,
|
||||||
|
]
|
||||||
|
verifylist = [
|
||||||
|
('bootable', False),
|
||||||
|
('non_bootable', True),
|
||||||
|
('read_only', False),
|
||||||
|
('read_write', True),
|
||||||
|
('size', self.new_volume.size),
|
||||||
|
('name', self.new_volume.name),
|
||||||
|
]
|
||||||
|
|
||||||
|
parsed_args = self.check_parser(
|
||||||
|
self.cmd, arglist, verifylist)
|
||||||
|
|
||||||
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
|
|
||||||
|
self.volumes_mock.create.assert_called_with(
|
||||||
|
size=self.new_volume.size,
|
||||||
|
snapshot_id=None,
|
||||||
|
name=self.new_volume.name,
|
||||||
|
description=None,
|
||||||
|
volume_type=None,
|
||||||
|
user_id=None,
|
||||||
|
project_id=None,
|
||||||
|
availability_zone=None,
|
||||||
|
metadata=None,
|
||||||
|
imageRef=None,
|
||||||
|
source_volid=None,
|
||||||
|
consistencygroup_id=None,
|
||||||
|
source_replica=None,
|
||||||
|
multiattach=False,
|
||||||
|
scheduler_hints=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(self.columns, columns)
|
||||||
|
self.assertEqual(self.datalist, data)
|
||||||
|
self.volumes_mock.set_bootable.assert_called_with(
|
||||||
|
self.new_volume.id, False)
|
||||||
|
self.volumes_mock.update_readonly_flag.assert_called_with(
|
||||||
|
self.new_volume.id, False)
|
||||||
|
|
||||||
|
@mock.patch.object(volume.LOG, 'error')
|
||||||
|
def test_volume_create_with_bootable_and_readonly_fail(
|
||||||
|
self, mock_error):
|
||||||
|
|
||||||
|
self.volumes_mock.set_bootable.side_effect = (
|
||||||
|
exceptions.CommandError())
|
||||||
|
|
||||||
|
self.volumes_mock.update_readonly_flag.side_effect = (
|
||||||
|
exceptions.CommandError())
|
||||||
|
|
||||||
|
arglist = [
|
||||||
|
'--bootable',
|
||||||
|
'--read-only',
|
||||||
|
'--size', str(self.new_volume.size),
|
||||||
|
self.new_volume.name,
|
||||||
|
]
|
||||||
|
verifylist = [
|
||||||
|
('bootable', True),
|
||||||
|
('non_bootable', False),
|
||||||
|
('read_only', True),
|
||||||
|
('read_write', False),
|
||||||
|
('size', self.new_volume.size),
|
||||||
|
('name', self.new_volume.name),
|
||||||
|
]
|
||||||
|
|
||||||
|
parsed_args = self.check_parser(
|
||||||
|
self.cmd, arglist, verifylist)
|
||||||
|
|
||||||
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
|
|
||||||
|
self.volumes_mock.create.assert_called_with(
|
||||||
|
size=self.new_volume.size,
|
||||||
|
snapshot_id=None,
|
||||||
|
name=self.new_volume.name,
|
||||||
|
description=None,
|
||||||
|
volume_type=None,
|
||||||
|
user_id=None,
|
||||||
|
project_id=None,
|
||||||
|
availability_zone=None,
|
||||||
|
metadata=None,
|
||||||
|
imageRef=None,
|
||||||
|
source_volid=None,
|
||||||
|
consistencygroup_id=None,
|
||||||
|
source_replica=None,
|
||||||
|
multiattach=False,
|
||||||
|
scheduler_hints=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(2, mock_error.call_count)
|
||||||
|
self.assertEqual(self.columns, columns)
|
||||||
|
self.assertEqual(self.datalist, data)
|
||||||
|
self.volumes_mock.set_bootable.assert_called_with(
|
||||||
|
self.new_volume.id, True)
|
||||||
|
self.volumes_mock.update_readonly_flag.assert_called_with(
|
||||||
|
self.new_volume.id, True)
|
||||||
|
|
||||||
def test_volume_create_with_source_replicated(self):
|
def test_volume_create_with_source_replicated(self):
|
||||||
self.volumes_mock.get.return_value = self.new_volume
|
self.volumes_mock.get.return_value = self.new_volume
|
||||||
arglist = [
|
arglist = [
|
||||||
|
@ -114,6 +114,28 @@ class CreateVolume(command.ShowOne):
|
|||||||
help=_('Set a property on this volume '
|
help=_('Set a property on this volume '
|
||||||
'(repeat option to set multiple properties)'),
|
'(repeat option to set multiple properties)'),
|
||||||
)
|
)
|
||||||
|
bootable_group = parser.add_mutually_exclusive_group()
|
||||||
|
bootable_group.add_argument(
|
||||||
|
"--bootable",
|
||||||
|
action="store_true",
|
||||||
|
help=_("Mark volume as bootable")
|
||||||
|
)
|
||||||
|
bootable_group.add_argument(
|
||||||
|
"--non-bootable",
|
||||||
|
action="store_true",
|
||||||
|
help=_("Mark volume as non-bootable (default)")
|
||||||
|
)
|
||||||
|
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 (default)")
|
||||||
|
)
|
||||||
|
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
@ -166,6 +188,22 @@ class CreateVolume(command.ShowOne):
|
|||||||
parsed_args.property,
|
parsed_args.property,
|
||||||
image,
|
image,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if parsed_args.bootable or parsed_args.non_bootable:
|
||||||
|
try:
|
||||||
|
volume_client.volumes.set_bootable(
|
||||||
|
volume.id, parsed_args.bootable)
|
||||||
|
except Exception as e:
|
||||||
|
LOG.error(_("Failed to set volume bootable property: %s"), e)
|
||||||
|
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)
|
||||||
|
|
||||||
# Map 'metadata' column to 'properties'
|
# Map 'metadata' column to 'properties'
|
||||||
volume._info.update(
|
volume._info.update(
|
||||||
{
|
{
|
||||||
|
@ -132,6 +132,28 @@ class CreateVolume(command.ShowOne):
|
|||||||
help=_("Allow volume to be attached more than once "
|
help=_("Allow volume to be attached more than once "
|
||||||
"(default to False)")
|
"(default to False)")
|
||||||
)
|
)
|
||||||
|
bootable_group = parser.add_mutually_exclusive_group()
|
||||||
|
bootable_group.add_argument(
|
||||||
|
"--bootable",
|
||||||
|
action="store_true",
|
||||||
|
help=_("Mark volume as bootable")
|
||||||
|
)
|
||||||
|
bootable_group.add_argument(
|
||||||
|
"--non-bootable",
|
||||||
|
action="store_true",
|
||||||
|
help=_("Mark volume as non-bootable (default)")
|
||||||
|
)
|
||||||
|
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 (default)")
|
||||||
|
)
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
@ -199,6 +221,22 @@ class CreateVolume(command.ShowOne):
|
|||||||
multiattach=parsed_args.multi_attach,
|
multiattach=parsed_args.multi_attach,
|
||||||
scheduler_hints=parsed_args.hint,
|
scheduler_hints=parsed_args.hint,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if parsed_args.bootable or parsed_args.non_bootable:
|
||||||
|
try:
|
||||||
|
volume_client.volumes.set_bootable(
|
||||||
|
volume.id, parsed_args.bootable)
|
||||||
|
except Exception as e:
|
||||||
|
LOG.error(_("Failed to set volume bootable property: %s"), e)
|
||||||
|
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)
|
||||||
|
|
||||||
# Remove key links from being displayed
|
# Remove key links from being displayed
|
||||||
volume._info.update(
|
volume._info.update(
|
||||||
{
|
{
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- Add ``--bootable``, ``--non-bootable``, ``--read-only`` and ``--read-write``
|
||||||
|
options to ``volume create`` command.
|
||||||
|
[Blueprint `cinder-command-support <https://blueprints.launchpad.net/python-openstackclient/+spec/cinder-command-support>`_]
|
Loading…
Reference in New Issue
Block a user