summaryrefslogtreecommitdiff
path: root/openstackclient
diff options
context:
space:
mode:
Diffstat (limited to 'openstackclient')
-rw-r--r--openstackclient/common/quota.py3
-rw-r--r--openstackclient/identity/v3/role.py4
-rw-r--r--openstackclient/identity/v3/role_assignment.py14
-rw-r--r--openstackclient/tests/common/test_quota.py89
-rw-r--r--openstackclient/tests/compute/v2/fakes.py18
-rw-r--r--openstackclient/tests/identity/v3/fakes.py14
-rw-r--r--openstackclient/tests/identity/v3/test_role.py16
-rw-r--r--openstackclient/tests/identity/v3/test_role_assignment.py142
-rw-r--r--openstackclient/tests/volume/v2/fakes.py6
-rw-r--r--openstackclient/tests/volume/v2/test_type.py130
-rw-r--r--openstackclient/tests/volume/v2/test_volume.py214
-rw-r--r--openstackclient/volume/v2/volume.py109
-rw-r--r--openstackclient/volume/v2/volume_type.py94
13 files changed, 808 insertions, 45 deletions
diff --git a/openstackclient/common/quota.py b/openstackclient/common/quota.py
index 4963eeac..e79fd7ed 100644
--- a/openstackclient/common/quota.py
+++ b/openstackclient/common/quota.py
@@ -97,12 +97,13 @@ class SetQuota(command.Command):
compute_kwargs = {}
for k, v in COMPUTE_QUOTAS.items():
- value = getattr(parsed_args, v, None)
+ value = getattr(parsed_args, k, None)
if value is not None:
compute_kwargs[k] = value
volume_kwargs = {}
for k, v in VOLUME_QUOTAS.items():
+ # TODO(jiaxi): Should use k or v needs discuss
value = getattr(parsed_args, v, None)
if value is not None:
if parsed_args.volume_type:
diff --git a/openstackclient/identity/v3/role.py b/openstackclient/identity/v3/role.py
index 199b7dca..9243639e 100644
--- a/openstackclient/identity/v3/role.py
+++ b/openstackclient/identity/v3/role.py
@@ -101,9 +101,9 @@ def _process_identity_and_resource_options(parsed_args,
kwargs['project'] = common.find_project(
identity_client_manager,
parsed_args.project,
- parsed_args.group_domain,
+ parsed_args.project_domain,
).id
- kwargs['inherited'] = parsed_args.inherited
+ kwargs['os_inherit_extension_inherited'] = parsed_args.inherited
return kwargs
diff --git a/openstackclient/identity/v3/role_assignment.py b/openstackclient/identity/v3/role_assignment.py
index 92168498..169c6cb9 100644
--- a/openstackclient/identity/v3/role_assignment.py
+++ b/openstackclient/identity/v3/role_assignment.py
@@ -64,12 +64,12 @@ class ListRoleAssignment(lister.Lister):
help='Project to filter (name or ID)',
)
common.add_project_domain_option_to_parser(parser)
-
+ common.add_inherited_option_to_parser(parser)
return parser
def _as_tuple(self, assignment):
return (assignment.role, assignment.user, assignment.group,
- assignment.project, assignment.domain)
+ assignment.project, assignment.domain, assignment.inherited)
def take_action(self, parsed_args):
self.log.debug('take_action(%s)' % parsed_args)
@@ -115,14 +115,17 @@ class ListRoleAssignment(lister.Lister):
effective = True if parsed_args.effective else False
self.log.debug('take_action(%s)' % parsed_args)
- columns = ('Role', 'User', 'Group', 'Project', 'Domain')
+ columns = ('Role', 'User', 'Group', 'Project', 'Domain', 'Inherited')
+
+ inherited_to = 'projects' if parsed_args.inherited else None
data = identity_client.role_assignments.list(
domain=domain,
user=user,
group=group,
project=project,
role=role,
- effective=effective)
+ effective=effective,
+ os_inherit_extension_inherited_to=inherited_to)
data_parsed = []
for assignment in data:
@@ -139,6 +142,9 @@ class ListRoleAssignment(lister.Lister):
assignment.domain = ''
assignment.project = ''
+ inherited = scope.get('OS-INHERIT:inherited_to') == 'projects'
+ assignment.inherited = inherited
+
del assignment.scope
if hasattr(assignment, 'user'):
diff --git a/openstackclient/tests/common/test_quota.py b/openstackclient/tests/common/test_quota.py
new file mode 100644
index 00000000..f0013e48
--- /dev/null
+++ b/openstackclient/tests/common/test_quota.py
@@ -0,0 +1,89 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import copy
+
+from openstackclient.common import quota
+from openstackclient.tests.compute.v2 import fakes as compute_fakes
+from openstackclient.tests import fakes
+
+
+class FakeQuotaResource(fakes.FakeResource):
+
+ _keys = {'property': 'value'}
+
+ def set_keys(self, args):
+ self._keys.update(args)
+
+ def unset_keys(self, keys):
+ for key in keys:
+ self._keys.pop(key, None)
+
+ def get_keys(self):
+ return self._keys
+
+
+class TestQuota(compute_fakes.TestComputev2):
+
+ def setUp(self):
+ super(TestQuota, self).setUp()
+ self.quotas_mock = self.app.client_manager.compute.quotas
+ self.quotas_mock.reset_mock()
+
+
+class TestQuotaSet(TestQuota):
+
+ def setUp(self):
+ super(TestQuotaSet, self).setUp()
+
+ self.quotas_mock.find.return_value = FakeQuotaResource(
+ None,
+ copy.deepcopy(compute_fakes.QUOTA),
+ loaded=True,
+ )
+
+ self.quotas_mock.update.return_value = FakeQuotaResource(
+ None,
+ copy.deepcopy(compute_fakes.QUOTA),
+ loaded=True,
+ )
+
+ self.cmd = quota.SetQuota(self.app, None)
+
+ def test_quota_set(self):
+ arglist = [
+ '--floating-ips', str(compute_fakes.floating_ip_num),
+ '--fixed-ips', str(compute_fakes.fix_ip_num),
+ '--injected-files', str(compute_fakes.injected_file_num),
+ '--key-pairs', str(compute_fakes.key_pair_num),
+ compute_fakes.project_name,
+ ]
+ verifylist = [
+ ('floating_ips', compute_fakes.floating_ip_num),
+ ('fixed_ips', compute_fakes.fix_ip_num),
+ ('injected_files', compute_fakes.injected_file_num),
+ ('key_pairs', compute_fakes.key_pair_num),
+ ('project', compute_fakes.project_name),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ self.cmd.take_action(parsed_args)
+
+ kwargs = {
+ 'floating_ips': compute_fakes.floating_ip_num,
+ 'fixed_ips': compute_fakes.fix_ip_num,
+ 'injected_files': compute_fakes.injected_file_num,
+ 'key_pairs': compute_fakes.key_pair_num,
+ }
+
+ self.quotas_mock.update.assert_called_with('project_test', **kwargs)
diff --git a/openstackclient/tests/compute/v2/fakes.py b/openstackclient/tests/compute/v2/fakes.py
index c18dea7e..e798bd40 100644
--- a/openstackclient/tests/compute/v2/fakes.py
+++ b/openstackclient/tests/compute/v2/fakes.py
@@ -62,6 +62,22 @@ FLAVOR = {
'vcpus': flavor_vcpus,
}
+floating_ip_num = 100
+fix_ip_num = 100
+injected_file_num = 100
+key_pair_num = 100
+project_name = 'project_test'
+QUOTA = {
+ 'project': project_name,
+ 'floating-ips': floating_ip_num,
+ 'fix-ips': fix_ip_num,
+ 'injected-files': injected_file_num,
+ 'key-pairs': key_pair_num,
+}
+
+QUOTA_columns = tuple(sorted(QUOTA))
+QUOTA_data = tuple(QUOTA[x] for x in sorted(QUOTA))
+
class FakeComputev2Client(object):
def __init__(self, **kwargs):
@@ -73,6 +89,8 @@ class FakeComputev2Client(object):
self.extensions.resource_class = fakes.FakeResource(None, {})
self.flavors = mock.Mock()
self.flavors.resource_class = fakes.FakeResource(None, {})
+ self.quotas = mock.Mock()
+ self.quotas.resource_class = fakes.FakeResource(None, {})
self.auth_token = kwargs['token']
self.management_url = kwargs['endpoint']
diff --git a/openstackclient/tests/identity/v3/fakes.py b/openstackclient/tests/identity/v3/fakes.py
index ae7a684c..9c4de9cc 100644
--- a/openstackclient/tests/identity/v3/fakes.py
+++ b/openstackclient/tests/identity/v3/fakes.py
@@ -313,6 +313,13 @@ ASSIGNMENT_WITH_PROJECT_ID_AND_USER_ID = {
'role': {'id': role_id},
}
+ASSIGNMENT_WITH_PROJECT_ID_AND_USER_ID_INHERITED = {
+ 'scope': {'project': {'id': project_id},
+ 'OS-INHERIT:inherited_to': 'projects'},
+ 'user': {'id': user_id},
+ 'role': {'id': role_id},
+}
+
ASSIGNMENT_WITH_PROJECT_ID_AND_GROUP_ID = {
'scope': {'project': {'id': project_id}},
'group': {'id': group_id},
@@ -325,6 +332,13 @@ ASSIGNMENT_WITH_DOMAIN_ID_AND_USER_ID = {
'role': {'id': role_id},
}
+ASSIGNMENT_WITH_DOMAIN_ID_AND_USER_ID_INHERITED = {
+ 'scope': {'domain': {'id': domain_id},
+ 'OS-INHERIT:inherited_to': 'projects'},
+ 'user': {'id': user_id},
+ 'role': {'id': role_id},
+}
+
ASSIGNMENT_WITH_DOMAIN_ID_AND_GROUP_ID = {
'scope': {'domain': {'id': domain_id}},
'group': {'id': group_id},
diff --git a/openstackclient/tests/identity/v3/test_role.py b/openstackclient/tests/identity/v3/test_role.py
index 4ff3b95f..4a0ba066 100644
--- a/openstackclient/tests/identity/v3/test_role.py
+++ b/openstackclient/tests/identity/v3/test_role.py
@@ -123,7 +123,7 @@ class TestRoleAdd(TestRole):
kwargs = {
'user': identity_fakes.user_id,
'domain': identity_fakes.domain_id,
- 'inherited': self._is_inheritance_testcase(),
+ 'os_inherit_extension_inherited': self._is_inheritance_testcase(),
}
# RoleManager.grant(role, user=, group=, domain=, project=)
self.roles_mock.grant.assert_called_with(
@@ -156,7 +156,7 @@ class TestRoleAdd(TestRole):
kwargs = {
'user': identity_fakes.user_id,
'project': identity_fakes.project_id,
- 'inherited': self._is_inheritance_testcase(),
+ 'os_inherit_extension_inherited': self._is_inheritance_testcase(),
}
# RoleManager.grant(role, user=, group=, domain=, project=)
self.roles_mock.grant.assert_called_with(
@@ -189,7 +189,7 @@ class TestRoleAdd(TestRole):
kwargs = {
'group': identity_fakes.group_id,
'domain': identity_fakes.domain_id,
- 'inherited': self._is_inheritance_testcase(),
+ 'os_inherit_extension_inherited': self._is_inheritance_testcase(),
}
# RoleManager.grant(role, user=, group=, domain=, project=)
self.roles_mock.grant.assert_called_with(
@@ -222,7 +222,7 @@ class TestRoleAdd(TestRole):
kwargs = {
'group': identity_fakes.group_id,
'project': identity_fakes.project_id,
- 'inherited': self._is_inheritance_testcase(),
+ 'os_inherit_extension_inherited': self._is_inheritance_testcase(),
}
# RoleManager.grant(role, user=, group=, domain=, project=)
self.roles_mock.grant.assert_called_with(
@@ -598,7 +598,7 @@ class TestRoleRemove(TestRole):
kwargs = {
'user': identity_fakes.user_id,
'domain': identity_fakes.domain_id,
- 'inherited': self._is_inheritance_testcase(),
+ 'os_inherit_extension_inherited': self._is_inheritance_testcase(),
}
# RoleManager.revoke(role, user=, group=, domain=, project=)
self.roles_mock.revoke.assert_called_with(
@@ -631,7 +631,7 @@ class TestRoleRemove(TestRole):
kwargs = {
'user': identity_fakes.user_id,
'project': identity_fakes.project_id,
- 'inherited': self._is_inheritance_testcase(),
+ 'os_inherit_extension_inherited': self._is_inheritance_testcase(),
}
# RoleManager.revoke(role, user=, group=, domain=, project=)
self.roles_mock.revoke.assert_called_with(
@@ -665,7 +665,7 @@ class TestRoleRemove(TestRole):
kwargs = {
'group': identity_fakes.group_id,
'domain': identity_fakes.domain_id,
- 'inherited': self._is_inheritance_testcase(),
+ 'os_inherit_extension_inherited': self._is_inheritance_testcase(),
}
# RoleManager.revoke(role, user=, group=, domain=, project=)
self.roles_mock.revoke.assert_called_with(
@@ -698,7 +698,7 @@ class TestRoleRemove(TestRole):
kwargs = {
'group': identity_fakes.group_id,
'project': identity_fakes.project_id,
- 'inherited': self._is_inheritance_testcase(),
+ 'os_inherit_extension_inherited': self._is_inheritance_testcase(),
}
# RoleManager.revoke(role, user=, group=, domain=, project=)
self.roles_mock.revoke.assert_called_with(
diff --git a/openstackclient/tests/identity/v3/test_role_assignment.py b/openstackclient/tests/identity/v3/test_role_assignment.py
index b1ce8b29..9817f53a 100644
--- a/openstackclient/tests/identity/v3/test_role_assignment.py
+++ b/openstackclient/tests/identity/v3/test_role_assignment.py
@@ -86,21 +86,24 @@ class TestRoleAssignmentList(TestRoleAssignment):
effective=False,
role=None,
user=None,
- project=None)
+ project=None,
+ os_inherit_extension_inherited_to=None)
- collist = ('Role', 'User', 'Group', 'Project', 'Domain')
- self.assertEqual(collist, columns)
+ collist = ('Role', 'User', 'Group', 'Project', 'Domain', 'Inherited')
+ self.assertEqual(columns, collist)
datalist = ((
identity_fakes.role_id,
identity_fakes.user_id,
'',
identity_fakes.project_id,
- ''
+ '',
+ False
), (identity_fakes.role_id,
'',
identity_fakes.group_id,
identity_fakes.project_id,
- ''
+ '',
+ False
),)
self.assertEqual(datalist, tuple(data))
@@ -131,6 +134,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
('project', None),
('role', None),
('effective', False),
+ ('inherited', False),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -143,21 +147,24 @@ class TestRoleAssignmentList(TestRoleAssignment):
group=None,
project=None,
role=None,
- effective=False)
+ effective=False,
+ os_inherit_extension_inherited_to=None)
- collist = ('Role', 'User', 'Group', 'Project', 'Domain')
- self.assertEqual(collist, columns)
+ collist = ('Role', 'User', 'Group', 'Project', 'Domain', 'Inherited')
+ self.assertEqual(columns, collist)
datalist = ((
identity_fakes.role_id,
identity_fakes.user_id,
'',
'',
- identity_fakes.domain_id
+ identity_fakes.domain_id,
+ False
), (identity_fakes.role_id,
identity_fakes.user_id,
'',
identity_fakes.project_id,
- ''
+ '',
+ False
),)
self.assertEqual(datalist, tuple(data))
@@ -188,6 +195,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
('project', None),
('role', None),
('effective', False),
+ ('inherited', False),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -200,21 +208,24 @@ class TestRoleAssignmentList(TestRoleAssignment):
effective=False,
project=None,
role=None,
- user=None)
+ user=None,
+ os_inherit_extension_inherited_to=None)
- collist = ('Role', 'User', 'Group', 'Project', 'Domain')
- self.assertEqual(collist, columns)
+ collist = ('Role', 'User', 'Group', 'Project', 'Domain', 'Inherited')
+ self.assertEqual(columns, collist)
datalist = ((
identity_fakes.role_id,
'',
identity_fakes.group_id,
'',
- identity_fakes.domain_id
+ identity_fakes.domain_id,
+ False
), (identity_fakes.role_id,
'',
identity_fakes.group_id,
identity_fakes.project_id,
- ''
+ '',
+ False
),)
self.assertEqual(datalist, tuple(data))
@@ -245,6 +256,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
('project', None),
('role', None),
('effective', False),
+ ('inherited', False),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -257,21 +269,24 @@ class TestRoleAssignmentList(TestRoleAssignment):
effective=False,
project=None,
role=None,
- user=None)
+ user=None,
+ os_inherit_extension_inherited_to=None)
- collist = ('Role', 'User', 'Group', 'Project', 'Domain')
- self.assertEqual(collist, columns)
+ collist = ('Role', 'User', 'Group', 'Project', 'Domain', 'Inherited')
+ self.assertEqual(columns, collist)
datalist = ((
identity_fakes.role_id,
identity_fakes.user_id,
'',
'',
- identity_fakes.domain_id
+ identity_fakes.domain_id,
+ False
), (identity_fakes.role_id,
'',
identity_fakes.group_id,
'',
- identity_fakes.domain_id
+ identity_fakes.domain_id,
+ False
),)
self.assertEqual(datalist, tuple(data))
@@ -302,6 +317,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
('project', identity_fakes.project_name),
('role', None),
('effective', False),
+ ('inherited', False),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -314,21 +330,24 @@ class TestRoleAssignmentList(TestRoleAssignment):
effective=False,
project=self.projects_mock.get(),
role=None,
- user=None)
+ user=None,
+ os_inherit_extension_inherited_to=None)
- collist = ('Role', 'User', 'Group', 'Project', 'Domain')
- self.assertEqual(collist, columns)
+ collist = ('Role', 'User', 'Group', 'Project', 'Domain', 'Inherited')
+ self.assertEqual(columns, collist)
datalist = ((
identity_fakes.role_id,
identity_fakes.user_id,
'',
identity_fakes.project_id,
- ''
+ '',
+ False
), (identity_fakes.role_id,
'',
identity_fakes.group_id,
identity_fakes.project_id,
- ''
+ '',
+ False
),)
self.assertEqual(datalist, tuple(data))
@@ -357,6 +376,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
('project', None),
('role', None),
('effective', True),
+ ('inherited', False),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -369,20 +389,84 @@ class TestRoleAssignmentList(TestRoleAssignment):
effective=True,
project=None,
role=None,
- user=None)
+ user=None,
+ os_inherit_extension_inherited_to=None)
- collist = ('Role', 'User', 'Group', 'Project', 'Domain')
- self.assertEqual(collist, columns)
+ collist = ('Role', 'User', 'Group', 'Project', 'Domain', 'Inherited')
+ self.assertEqual(columns, collist)
datalist = ((
identity_fakes.role_id,
identity_fakes.user_id,
'',
identity_fakes.project_id,
- ''
+ '',
+ False
+ ), (identity_fakes.role_id,
+ identity_fakes.user_id,
+ '',
+ '',
+ identity_fakes.domain_id,
+ False
+ ),)
+ self.assertEqual(tuple(data), datalist)
+
+ def test_role_assignment_list_inherited(self):
+
+ self.role_assignments_mock.list.return_value = [
+ fakes.FakeResource(
+ None,
+ copy.deepcopy(
+ (identity_fakes.
+ ASSIGNMENT_WITH_PROJECT_ID_AND_USER_ID_INHERITED)),
+ loaded=True,
+ ),
+ fakes.FakeResource(
+ None,
+ copy.deepcopy(
+ (identity_fakes.
+ ASSIGNMENT_WITH_DOMAIN_ID_AND_USER_ID_INHERITED)),
+ loaded=True,
+ ),
+ ]
+
+ arglist = ['--inherited']
+ verifylist = [
+ ('user', None),
+ ('group', None),
+ ('domain', None),
+ ('project', None),
+ ('role', None),
+ ('effective', False),
+ ('inherited', True),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # DisplayCommandBase.take_action() returns two tuples
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.role_assignments_mock.list.assert_called_with(
+ domain=None,
+ group=None,
+ effective=False,
+ project=None,
+ role=None,
+ user=None,
+ os_inherit_extension_inherited_to='projects')
+
+ collist = ('Role', 'User', 'Group', 'Project', 'Domain', 'Inherited')
+ self.assertEqual(columns, collist)
+ datalist = ((
+ identity_fakes.role_id,
+ identity_fakes.user_id,
+ '',
+ identity_fakes.project_id,
+ '',
+ True
), (identity_fakes.role_id,
identity_fakes.user_id,
'',
'',
identity_fakes.domain_id,
+ True
),)
self.assertEqual(datalist, tuple(data))
diff --git a/openstackclient/tests/volume/v2/fakes.py b/openstackclient/tests/volume/v2/fakes.py
index a95bc94b..0d8c2024 100644
--- a/openstackclient/tests/volume/v2/fakes.py
+++ b/openstackclient/tests/volume/v2/fakes.py
@@ -15,11 +15,15 @@
import copy
import mock
+from openstackclient.tests.compute.v2 import fakes as compute_fakes
from openstackclient.tests import fakes
from openstackclient.tests.identity.v2_0 import fakes as identity_fakes
from openstackclient.tests.image.v2 import fakes as image_fakes
from openstackclient.tests import utils
+volume_attachment_server = copy.deepcopy(compute_fakes.SERVER)
+volume_attachment_server['device'] = 'device'
+
volume_id = "ce26708d-a7f8-4b4b-9861-4a80256615a6"
volume_name = "fake_volume"
volume_description = "fake description"
@@ -34,7 +38,7 @@ volume_metadata = {
volume_metadata_str = "Alpha='a', Beta='b', Gamma='g'"
volume_snapshot_id = 1
volume_availability_zone = "nova"
-volume_attachments = ["fake_attachments"]
+volume_attachments = [volume_attachment_server]
VOLUME = {
"id": volume_id,
diff --git a/openstackclient/tests/volume/v2/test_type.py b/openstackclient/tests/volume/v2/test_type.py
index c5b27fa5..9a07263b 100644
--- a/openstackclient/tests/volume/v2/test_type.py
+++ b/openstackclient/tests/volume/v2/test_type.py
@@ -19,6 +19,20 @@ from openstackclient.tests.volume.v2 import fakes as volume_fakes
from openstackclient.volume.v2 import volume_type
+class FakeTypeResource(fakes.FakeResource):
+
+ _keys = {'property': 'value'}
+
+ def set_keys(self, args):
+ self._keys.update(args)
+
+ def unset_keys(self, key):
+ self._keys.pop(key, None)
+
+ def get_keys(self):
+ return self._keys
+
+
class TestType(volume_fakes.TestVolume):
def setUp(self):
@@ -184,6 +198,122 @@ class TestTypeShow(TestType):
self.assertEqual(volume_fakes.TYPE_FORMATTED_data, data)
+class TestTypeSet(TestType):
+
+ def setUp(self):
+ super(TestTypeSet, self).setUp()
+
+ self.types_mock.get.return_value = FakeTypeResource(
+ None,
+ copy.deepcopy(volume_fakes.TYPE),
+ loaded=True,
+ )
+
+ # Get the command object to test
+ self.cmd = volume_type.SetVolumeType(self.app, None)
+
+ def test_type_set_name(self):
+ new_name = 'new_name'
+ arglist = [
+ '--name', new_name,
+ volume_fakes.type_id,
+ ]
+ verifylist = [
+ ('name', new_name),
+ ('description', None),
+ ('property', None),
+ ('volume_type', volume_fakes.type_id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'name': new_name,
+ }
+ self.types_mock.update.assert_called_with(
+ volume_fakes.type_id,
+ **kwargs
+ )
+
+ def test_type_set_description(self):
+ new_desc = 'new_desc'
+ arglist = [
+ '--description', new_desc,
+ volume_fakes.type_id,
+ ]
+ verifylist = [
+ ('name', None),
+ ('description', new_desc),
+ ('property', None),
+ ('volume_type', volume_fakes.type_id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'description': new_desc,
+ }
+ self.types_mock.update.assert_called_with(
+ volume_fakes.type_id,
+ **kwargs
+ )
+
+ def test_type_set_property(self):
+ arglist = [
+ '--property', 'myprop=myvalue',
+ volume_fakes.type_id,
+ ]
+ verifylist = [
+ ('name', None),
+ ('description', None),
+ ('property', {'myprop': 'myvalue'}),
+ ('volume_type', volume_fakes.type_id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ self.cmd.take_action(parsed_args)
+
+ result = self.types_mock.get.return_value._keys
+ self.assertIn('myprop', result)
+ self.assertEqual('myvalue', result['myprop'])
+
+
+class TestTypeUnset(TestType):
+
+ def setUp(self):
+ super(TestTypeUnset, self).setUp()
+
+ self.types_mock.get.return_value = FakeTypeResource(
+ None,
+ copy.deepcopy(volume_fakes.TYPE),
+ loaded=True,
+ )
+
+ self.cmd = volume_type.UnsetVolumeType(self.app, None)
+
+ def test_type_unset(self):
+ arglist = [
+ '--property', 'property',
+ volume_fakes.type_id,
+ ]
+ verifylist = [
+ ('property', 'property'),
+ ('volume_type', volume_fakes.type_id),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ self.cmd.take_action(parsed_args)
+
+ result = self.types_mock.get.return_value._keys
+
+ self.assertNotIn('property', result)
+
+
class TestTypeDelete(TestType):
def setUp(self):
super(TestTypeDelete, self).setUp()
diff --git a/openstackclient/tests/volume/v2/test_volume.py b/openstackclient/tests/volume/v2/test_volume.py
index 4fffefa4..348ae377 100644
--- a/openstackclient/tests/volume/v2/test_volume.py
+++ b/openstackclient/tests/volume/v2/test_volume.py
@@ -495,6 +495,220 @@ class TestVolumeCreate(TestVolume):
self.assertEqual(datalist, data)
+class TestVolumeList(TestVolume):
+
+ def setUp(self):
+ super(TestVolumeList, self).setUp()
+
+ self.volumes_mock.list.return_value = [
+ fakes.FakeResource(
+ None,
+ copy.deepcopy(volume_fakes.VOLUME),
+ loaded=True,
+ ),
+ ]
+
+ self.users_mock.get.return_value = [
+ fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.USER),
+ loaded=True,
+ ),
+ ]
+
+ self.projects_mock.get.return_value = [
+ fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.PROJECT),
+ loaded=True,
+ ),
+ ]
+
+ # Get the command object to test
+ self.cmd = volume.ListVolume(self.app, None)
+
+ def test_volume_list_no_options(self):
+ arglist = []
+ verifylist = [
+ ('long', False),
+ ('all_projects', False),
+ ('name', None),
+ ('status', None),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ collist = [
+ 'ID',
+ 'Display Name',
+ 'Status',
+ 'Size',
+ 'Attached to',
+ ]
+ self.assertEqual(collist, columns)
+
+ server = volume_fakes.volume_attachment_server['id']
+ device = volume_fakes.volume_attachment_server['device']
+ msg = 'Attached to %s on %s ' % (server, device)
+ datalist = ((
+ volume_fakes.volume_id,
+ volume_fakes.volume_name,
+ volume_fakes.volume_status,
+ volume_fakes.volume_size,
+ msg,
+ ), )
+ self.assertEqual(datalist, tuple(data))
+
+ def test_volume_list_all_projects_option(self):
+ arglist = [
+ '--all-projects',
+ ]
+ verifylist = [
+ ('long', False),
+ ('all_projects', True),
+ ('name', None),
+ ('status', None),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ collist = [
+ 'ID',
+ 'Display Name',
+ 'Status',
+ 'Size',
+ 'Attached to',
+ ]
+ self.assertEqual(collist, columns)
+
+ server = volume_fakes.volume_attachment_server['id']
+ device = volume_fakes.volume_attachment_server['device']
+ msg = 'Attached to %s on %s ' % (server, device)
+ datalist = ((
+ volume_fakes.volume_id,
+ volume_fakes.volume_name,
+ volume_fakes.volume_status,
+ volume_fakes.volume_size,
+ msg,
+ ), )
+ self.assertEqual(datalist, tuple(data))
+
+ def test_volume_list_name(self):
+ arglist = [
+ '--name', volume_fakes.volume_name,
+ ]
+ verifylist = [
+ ('long', False),
+ ('all_projects', False),
+ ('name', volume_fakes.volume_name),
+ ('status', None),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ collist = (
+ 'ID',
+ 'Display Name',
+ 'Status',
+ 'Size',
+ 'Attached to',
+ )
+ self.assertEqual(collist, tuple(columns))
+
+ server = volume_fakes.volume_attachment_server['id']
+ device = volume_fakes.volume_attachment_server['device']
+ msg = 'Attached to %s on %s ' % (server, device)
+
+ datalist = ((
+ volume_fakes.volume_id,
+ volume_fakes.volume_name,
+ volume_fakes.volume_status,
+ volume_fakes.volume_size,
+ msg,
+ ), )
+ self.assertEqual(datalist, tuple(data))
+
+ def test_volume_list_status(self):
+ arglist = [
+ '--status', volume_fakes.volume_status,
+ ]
+ verifylist = [
+ ('long', False),
+ ('all_projects', False),
+ ('name', None),
+ ('status', volume_fakes.volume_status),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ collist = (
+ 'ID',
+ 'Display Name',
+ 'Status',
+ 'Size',
+ 'Attached to',
+ )
+ self.assertEqual(collist, tuple(columns))
+
+ server = volume_fakes.volume_attachment_server['id']
+ device = volume_fakes.volume_attachment_server['device']
+ msg = 'Attached to %s on %s ' % (server, device)
+ datalist = ((
+ volume_fakes.volume_id,
+ volume_fakes.volume_name,
+ volume_fakes.volume_status,
+ volume_fakes.volume_size,
+ msg,
+ ), )
+ self.assertEqual(datalist, tuple(data))
+
+ def test_volume_list_long(self):
+ arglist = [
+ '--long',
+ ]
+ verifylist = [
+ ('long', True),
+ ('all_projects', False),
+ ('name', None),
+ ('status', None),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ collist = [
+ 'ID',
+ 'Display Name',
+ 'Status',
+ 'Size',
+ 'Type',
+ 'Bootable',
+ 'Attached to',
+ 'Properties',
+ ]
+ self.assertEqual(collist, columns)
+
+ server = volume_fakes.volume_attachment_server['id']
+ device = volume_fakes.volume_attachment_server['device']
+ msg = 'Attached to %s on %s ' % (server, device)
+ datalist = ((
+ volume_fakes.volume_id,
+ volume_fakes.volume_name,
+ volume_fakes.volume_status,
+ volume_fakes.volume_size,
+ volume_fakes.volume_type,
+ '',
+ msg,
+ "Alpha='a', Beta='b', Gamma='g'",
+ ), )
+ self.assertEqual(datalist, tuple(data))
+
+
class TestVolumeShow(TestVolume):
def setUp(self):
super(TestVolumeShow, self).setUp()
diff --git a/openstackclient/volume/v2/volume.py b/openstackclient/volume/v2/volume.py
index d4536f51..fe4a3ff6 100644
--- a/openstackclient/volume/v2/volume.py
+++ b/openstackclient/volume/v2/volume.py
@@ -14,9 +14,12 @@
"""Volume V2 Volume action implementations"""
+import copy
import logging
+import os
from cliff import command
+from cliff import lister
from cliff import show
import six
@@ -189,6 +192,112 @@ class DeleteVolume(command.Command):
return
+class ListVolume(lister.Lister):
+ """List volumes"""
+
+ log = logging.getLogger(__name__ + '.ListVolume')
+
+ def get_parser(self, prog_name):
+ parser = super(ListVolume, self).get_parser(prog_name)
+ parser.add_argument(
+ '--all-projects',
+ action='store_true',
+ default=bool(int(os.environ.get("ALL_PROJECTS", 0))),
+ help='Include all projects (admin only)',
+ )
+ parser.add_argument(
+ '--long',
+ action='store_true',
+ default=False,
+ help='List additional fields in output',
+ )
+ parser.add_argument(
+ '--name',
+ metavar='<name>',
+ help='Filter results by name',
+ )
+ parser.add_argument(
+ '--status',
+ metavar='<status>',
+ help='Filter results by status',
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ self.log.debug('take_action(%s)', parsed_args)
+
+ volume_client = self.app.client_manager.volume
+ compute_client = self.app.client_manager.compute
+
+ def _format_attach(attachments):
+ """Return a formatted string of a volume's attached instances
+
+ :param volume: a volume.attachments field
+ :rtype: a string of formatted instances
+ """
+
+ msg = ''
+ for attachment in attachments:
+ server = attachment['id']
+ if server in server_cache:
+ server = server_cache[server].name
+ device = attachment['device']
+ msg += 'Attached to %s on %s ' % (server, device)
+ return msg
+
+ if parsed_args.long:
+ columns = [
+ 'ID',
+ 'Name',
+ 'Status',
+ 'Size',
+ 'Volume Type',
+ 'Bootable',
+ 'Attachments',
+ 'Metadata',
+ ]
+ column_headers = copy.deepcopy(columns)
+ column_headers[1] = 'Display Name'
+ column_headers[4] = 'Type'
+ column_headers[6] = 'Attached to'
+ column_headers[7] = 'Properties'
+ else:
+ columns = [
+ 'ID',
+ 'Name',
+ 'Status',
+ 'Size',
+ 'Attachments',
+ ]
+ column_headers = copy.deepcopy(columns)
+ column_headers[1] = 'Display Name'
+ column_headers[4] = 'Attached to'
+
+ # Cache the server list
+ server_cache = {}
+ try:
+ for s in compute_client.servers.list():
+ server_cache[s.id] = s
+ except Exception:
+ # Just forget it if there's any trouble
+ pass
+
+ search_opts = {
+ 'all_projects': parsed_args.all_projects,
+ 'display_name': parsed_args.name,
+ 'status': parsed_args.status,
+ }
+
+ data = volume_client.volumes.list(search_opts=search_opts)
+
+ return (column_headers,
+ (utils.get_item_properties(
+ s, columns,
+ formatters={'Metadata': utils.format_dict,
+ 'Attachments': _format_attach},
+ ) for s in data))
+
+
class SetVolume(show.ShowOne):
"""Set volume properties"""
diff --git a/openstackclient/volume/v2/volume_type.py b/openstackclient/volume/v2/volume_type.py
index 7f9a1c4b..fb0342c5 100644
--- a/openstackclient/volume/v2/volume_type.py
+++ b/openstackclient/volume/v2/volume_type.py
@@ -143,6 +143,67 @@ class ListVolumeType(lister.Lister):
) for s in data))
+class SetVolumeType(command.Command):
+ """Set volume type properties"""
+
+ log = logging.getLogger(__name__ + '.SetVolumeType')
+
+ def get_parser(self, prog_name):
+ parser = super(SetVolumeType, self).get_parser(prog_name)
+ parser.add_argument(
+ 'volume_type',
+ metavar='<volume-type>',
+ help='Volume type to modify (name or ID)',
+ )
+ parser.add_argument(
+ '--name',
+ metavar='<name>',
+ help='Set volume type name',
+ )
+ parser.add_argument(
+ '--description',
+ metavar='<name>',
+ help='Set volume type description',
+ )
+ parser.add_argument(
+ '--property',
+ metavar='<key=value>',
+ action=parseractions.KeyValueAction,
+ help='Property to add or modify for this volume type '
+ '(repeat option to set multiple properties)',
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ self.log.debug('take_action(%s)', parsed_args)
+ volume_client = self.app.client_manager.volume
+ volume_type = utils.find_resource(
+ volume_client.volume_types, parsed_args.volume_type)
+
+ if (not parsed_args.name
+ and not parsed_args.description
+ and not parsed_args.property):
+ self.app.log.error("No changes requested\n")
+ return
+
+ kwargs = {}
+ if parsed_args.name:
+ kwargs['name'] = parsed_args.name
+ if parsed_args.description:
+ kwargs['description'] = parsed_args.description
+
+ if kwargs:
+ volume_client.volume_types.update(
+ volume_type.id,
+ **kwargs
+ )
+
+ if parsed_args.property:
+ volume_type.set_keys(parsed_args.property)
+
+ return
+
+
class ShowVolumeType(show.ShowOne):
"""Display volume type details"""
@@ -165,3 +226,36 @@ class ShowVolumeType(show.ShowOne):
properties = utils.format_dict(volume_type._info.pop('extra_specs'))
volume_type._info.update({'properties': properties})
return zip(*sorted(six.iteritems(volume_type._info)))
+
+
+class UnsetVolumeType(command.Command):
+ """Unset volume type properties"""
+
+ log = logging.getLogger(__name__ + '.UnsetVolumeType')
+
+ def get_parser(self, prog_name):
+ parser = super(UnsetVolumeType, self).get_parser(prog_name)
+ parser.add_argument(
+ 'volume_type',
+ metavar='<volume-type>',
+ help='Volume type to modify (name or ID)',
+ )
+ parser.add_argument(
+ '--property',
+ metavar='<key>',
+ default=[],
+ required=True,
+ help='Property to remove from volume type '
+ '(repeat option to remove multiple properties)',
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ self.log.debug('take_action(%s)', parsed_args)
+ volume_client = self.app.client_manager.volume
+ volume_type = utils.find_resource(
+ volume_client.volume_types,
+ parsed_args.volume_type,
+ )
+ volume_type.unset_keys(parsed_args.property)
+ return