Merge "Add support for setting volume-type-access"
This commit is contained in:
commit
85c47b76fb
doc/source/command-objects
openstackclient
releasenotes/notes
@ -88,6 +88,8 @@ Set volume type properties
|
|||||||
[--name <name>]
|
[--name <name>]
|
||||||
[--description <description>]
|
[--description <description>]
|
||||||
[--property <key=value> [...] ]
|
[--property <key=value> [...] ]
|
||||||
|
[--project <project>]
|
||||||
|
[--project-domain <project-domain>]
|
||||||
<volume-type>
|
<volume-type>
|
||||||
|
|
||||||
.. option:: --name <name>
|
.. option:: --name <name>
|
||||||
@ -102,6 +104,17 @@ Set volume type properties
|
|||||||
|
|
||||||
.. versionadded:: 2
|
.. versionadded:: 2
|
||||||
|
|
||||||
|
.. option:: --project <project>
|
||||||
|
|
||||||
|
Set volume type access to project (name or ID) (admin only)
|
||||||
|
|
||||||
|
*Volume version 2 only*
|
||||||
|
|
||||||
|
.. option:: --project-domain <project-domain>
|
||||||
|
|
||||||
|
Domain the project belongs to (name or ID).
|
||||||
|
This can be used in case collisions between project names exist.
|
||||||
|
|
||||||
.. option:: --property <key=value>
|
.. option:: --property <key=value>
|
||||||
|
|
||||||
Set a property on this volume type (repeat option to set multiple properties)
|
Set a property on this volume type (repeat option to set multiple properties)
|
||||||
|
@ -243,6 +243,8 @@ class FakeVolumeClient(object):
|
|||||||
self.backups.resource_class = fakes.FakeResource(None, {})
|
self.backups.resource_class = fakes.FakeResource(None, {})
|
||||||
self.volume_types = mock.Mock()
|
self.volume_types = mock.Mock()
|
||||||
self.volume_types.resource_class = fakes.FakeResource(None, {})
|
self.volume_types.resource_class = fakes.FakeResource(None, {})
|
||||||
|
self.volume_type_access = mock.Mock()
|
||||||
|
self.volume_type_access.resource_class = fakes.FakeResource(None, {})
|
||||||
self.restores = mock.Mock()
|
self.restores = mock.Mock()
|
||||||
self.restores.resource_class = fakes.FakeResource(None, {})
|
self.restores.resource_class = fakes.FakeResource(None, {})
|
||||||
self.qos_specs = mock.Mock()
|
self.qos_specs = mock.Mock()
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
import copy
|
import copy
|
||||||
|
|
||||||
from openstackclient.tests import fakes
|
from openstackclient.tests import fakes
|
||||||
|
from openstackclient.tests.identity.v3 import fakes as identity_fakes
|
||||||
|
from openstackclient.tests import utils as tests_utils
|
||||||
from openstackclient.tests.volume.v2 import fakes as volume_fakes
|
from openstackclient.tests.volume.v2 import fakes as volume_fakes
|
||||||
from openstackclient.volume.v2 import volume_type
|
from openstackclient.volume.v2 import volume_type
|
||||||
|
|
||||||
@ -41,6 +43,13 @@ class TestType(volume_fakes.TestVolume):
|
|||||||
self.types_mock = self.app.client_manager.volume.volume_types
|
self.types_mock = self.app.client_manager.volume.volume_types
|
||||||
self.types_mock.reset_mock()
|
self.types_mock.reset_mock()
|
||||||
|
|
||||||
|
self.types_access_mock = (
|
||||||
|
self.app.client_manager.volume.volume_type_access)
|
||||||
|
self.types_access_mock.reset_mock()
|
||||||
|
|
||||||
|
self.projects_mock = self.app.client_manager.identity.projects
|
||||||
|
self.projects_mock.reset_mock()
|
||||||
|
|
||||||
|
|
||||||
class TestTypeCreate(TestType):
|
class TestTypeCreate(TestType):
|
||||||
|
|
||||||
@ -211,6 +220,13 @@ class TestTypeSet(TestType):
|
|||||||
loaded=True,
|
loaded=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Return a project
|
||||||
|
self.projects_mock.get.return_value = fakes.FakeResource(
|
||||||
|
None,
|
||||||
|
copy.deepcopy(identity_fakes.PROJECT),
|
||||||
|
loaded=True,
|
||||||
|
)
|
||||||
|
|
||||||
# Get the command object to test
|
# Get the command object to test
|
||||||
self.cmd = volume_type.SetVolumeType(self.app, None)
|
self.cmd = volume_type.SetVolumeType(self.app, None)
|
||||||
|
|
||||||
@ -286,6 +302,56 @@ class TestTypeSet(TestType):
|
|||||||
self.assertIn('myprop', result)
|
self.assertIn('myprop', result)
|
||||||
self.assertEqual('myvalue', result['myprop'])
|
self.assertEqual('myvalue', result['myprop'])
|
||||||
|
|
||||||
|
def test_type_set_not_called_without_project_argument(self):
|
||||||
|
arglist = [
|
||||||
|
'--project', '',
|
||||||
|
volume_fakes.type_id,
|
||||||
|
]
|
||||||
|
verifylist = [
|
||||||
|
('project', ''),
|
||||||
|
('volume_type', volume_fakes.type_id),
|
||||||
|
]
|
||||||
|
|
||||||
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
|
||||||
|
result = self.cmd.take_action(parsed_args)
|
||||||
|
self.assertIsNone(result)
|
||||||
|
|
||||||
|
self.assertFalse(self.types_access_mock.add_project_access.called)
|
||||||
|
|
||||||
|
def test_type_set_failed_with_missing_volume_type_argument(self):
|
||||||
|
arglist = [
|
||||||
|
'--project', 'identity_fakes.project_id',
|
||||||
|
]
|
||||||
|
verifylist = [
|
||||||
|
('project', 'identity_fakes.project_id'),
|
||||||
|
]
|
||||||
|
|
||||||
|
self.assertRaises(tests_utils.ParserException,
|
||||||
|
self.check_parser,
|
||||||
|
self.cmd,
|
||||||
|
arglist,
|
||||||
|
verifylist)
|
||||||
|
|
||||||
|
def test_type_set_project_access(self):
|
||||||
|
arglist = [
|
||||||
|
'--project', identity_fakes.project_id,
|
||||||
|
volume_fakes.type_id,
|
||||||
|
]
|
||||||
|
verifylist = [
|
||||||
|
('project', identity_fakes.project_id),
|
||||||
|
('volume_type', volume_fakes.type_id),
|
||||||
|
]
|
||||||
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
|
||||||
|
result = self.cmd.take_action(parsed_args)
|
||||||
|
self.assertIsNone(result)
|
||||||
|
|
||||||
|
self.types_access_mock.add_project_access.assert_called_with(
|
||||||
|
volume_fakes.type_id,
|
||||||
|
identity_fakes.project_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestTypeShow(TestType):
|
class TestTypeShow(TestType):
|
||||||
|
|
||||||
|
@ -17,8 +17,10 @@
|
|||||||
import six
|
import six
|
||||||
|
|
||||||
from openstackclient.common import command
|
from openstackclient.common import command
|
||||||
|
from openstackclient.common import exceptions
|
||||||
from openstackclient.common import parseractions
|
from openstackclient.common import parseractions
|
||||||
from openstackclient.common import utils
|
from openstackclient.common import utils
|
||||||
|
from openstackclient.identity import common as identity_common
|
||||||
|
|
||||||
|
|
||||||
class CreateVolumeType(command.ShowOne):
|
class CreateVolumeType(command.ShowOne):
|
||||||
@ -156,19 +158,30 @@ class SetVolumeType(command.Command):
|
|||||||
help='Set a property on this volume type '
|
help='Set a property on this volume type '
|
||||||
'(repeat option to set multiple properties)',
|
'(repeat option to set multiple properties)',
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--project',
|
||||||
|
metavar='<project>',
|
||||||
|
help='Set volume type access to project (name or ID) (admin only)',
|
||||||
|
)
|
||||||
|
identity_common.add_project_domain_option_to_parser(parser)
|
||||||
|
|
||||||
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
|
||||||
|
identity_client = self.app.client_manager.identity
|
||||||
|
|
||||||
volume_type = utils.find_resource(
|
volume_type = utils.find_resource(
|
||||||
volume_client.volume_types, parsed_args.volume_type)
|
volume_client.volume_types, parsed_args.volume_type)
|
||||||
|
|
||||||
if (not parsed_args.name
|
if (not parsed_args.name
|
||||||
and not parsed_args.description
|
and not parsed_args.description
|
||||||
and not parsed_args.property):
|
and not parsed_args.property
|
||||||
|
and not parsed_args.project):
|
||||||
self.app.log.error("No changes requested\n")
|
self.app.log.error("No changes requested\n")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
result = 0
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
if parsed_args.name:
|
if parsed_args.name:
|
||||||
kwargs['name'] = parsed_args.name
|
kwargs['name'] = parsed_args.name
|
||||||
@ -176,13 +189,42 @@ class SetVolumeType(command.Command):
|
|||||||
kwargs['description'] = parsed_args.description
|
kwargs['description'] = parsed_args.description
|
||||||
|
|
||||||
if kwargs:
|
if kwargs:
|
||||||
volume_client.volume_types.update(
|
try:
|
||||||
volume_type.id,
|
volume_client.volume_types.update(
|
||||||
**kwargs
|
volume_type.id,
|
||||||
)
|
**kwargs
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
self.app.log.error("Failed to update volume type name or"
|
||||||
|
" description: " + str(e))
|
||||||
|
result += 1
|
||||||
|
|
||||||
if parsed_args.property:
|
if parsed_args.property:
|
||||||
volume_type.set_keys(parsed_args.property)
|
try:
|
||||||
|
volume_type.set_keys(parsed_args.property)
|
||||||
|
except Exception as e:
|
||||||
|
self.app.log.error("Failed to set volume type property: " +
|
||||||
|
str(e))
|
||||||
|
result += 1
|
||||||
|
|
||||||
|
if parsed_args.project:
|
||||||
|
project_info = None
|
||||||
|
try:
|
||||||
|
project_info = identity_common.find_project(
|
||||||
|
identity_client,
|
||||||
|
parsed_args.project,
|
||||||
|
parsed_args.project_domain)
|
||||||
|
|
||||||
|
volume_client.volume_type_access.add_project_access(
|
||||||
|
volume_type.id, project_info.id)
|
||||||
|
except Exception as e:
|
||||||
|
self.app.log.error("Failed to set volume type access to"
|
||||||
|
" project: " + str(e))
|
||||||
|
result += 1
|
||||||
|
|
||||||
|
if result > 0:
|
||||||
|
raise exceptions.CommandError("Command Failed: One or more of the"
|
||||||
|
" operations failed")
|
||||||
|
|
||||||
|
|
||||||
class ShowVolumeType(command.ShowOne):
|
class ShowVolumeType(command.ShowOne):
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Added support for setting volume type access to project.
|
||||||
|
|
||||||
|
By default, volumes types are public.
|
||||||
|
To create a private volume type, user needs to set is_public boolean
|
||||||
|
field to false at volume type creation time.
|
||||||
|
To control access to a private volume type, user needs to add access
|
||||||
|
of a private volume type to project.
|
||||||
|
|
||||||
|
So, this feature enables user to add private volume type access to a
|
||||||
|
project using below command
|
||||||
|
|
||||||
|
``volume type set --project <project> <volume_type>``.
|
||||||
|
|
||||||
|
[Bug 1554889 'https://bugs.launchpad.net/python-openstackclient/+bug/1554889'_]
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user