From 3a48989eb02187f384cfbf7bb7cd55502741fc68 Mon Sep 17 00:00:00 2001
From: Tom Cocozzello <tjcocozz@us.ibm.com>
Date: Wed, 9 Dec 2015 10:08:16 -0600
Subject: [PATCH] Return names in list role assignments

Utilize the new include names functionality added to
list role assignments (GET /role_assignments?include_names=True).
Which will return the names of the entities instead of their
IDs.

Change-Id: I6dc03baf61ef9354a8a259a9f17ff47ce1665ce7
Depends-On: I4aa77c08660a0cbd021502155938a46121ca76ef
Closes-Bug: #1479569
Implements: blueprint list-assignment-with-names
---
 .../command-objects/role-assignment.rst       |  4 +
 .../identity/v3/role_assignment.py            | 40 ++++++--
 openstackclient/tests/identity/v3/fakes.py    | 29 ++++++
 .../tests/identity/v3/test_role_assignment.py | 96 +++++++++++++++++--
 ...ole_assignment_names-0db89f50259d4be2.yaml |  6 ++
 5 files changed, 162 insertions(+), 13 deletions(-)
 create mode 100644 releasenotes/notes/list_role_assignment_names-0db89f50259d4be2.yaml

diff --git a/doc/source/command-objects/role-assignment.rst b/doc/source/command-objects/role-assignment.rst
index 6bb24cb34c..893ebdc4e9 100644
--- a/doc/source/command-objects/role-assignment.rst
+++ b/doc/source/command-objects/role-assignment.rst
@@ -66,3 +66,7 @@ List role assignments
 .. option:: --inherited
 
     Specifies if the role grant is inheritable to the sub projects
+
+.. option:: --names
+
+    Returns role assignments with names instead of IDs
diff --git a/openstackclient/identity/v3/role_assignment.py b/openstackclient/identity/v3/role_assignment.py
index 169c6cb970..771afeb2f5 100644
--- a/openstackclient/identity/v3/role_assignment.py
+++ b/openstackclient/identity/v3/role_assignment.py
@@ -39,6 +39,11 @@ class ListRoleAssignment(lister.Lister):
             metavar='<role>',
             help='Role to filter (name or ID)',
         )
+        parser.add_argument(
+            '--names',
+            action="store_true",
+            help='Display names instead of IDs',
+        )
         user_or_group = parser.add_mutually_exclusive_group()
         user_or_group.add_argument(
             '--user',
@@ -113,6 +118,7 @@ class ListRoleAssignment(lister.Lister):
                 parsed_args.group_domain,
             )
 
+        include_names = True if parsed_args.names else False
         effective = True if parsed_args.effective else False
         self.log.debug('take_action(%s)' % parsed_args)
         columns = ('Role', 'User', 'Group', 'Project', 'Domain', 'Inherited')
@@ -125,17 +131,26 @@ class ListRoleAssignment(lister.Lister):
             project=project,
             role=role,
             effective=effective,
-            os_inherit_extension_inherited_to=inherited_to)
+            os_inherit_extension_inherited_to=inherited_to,
+            include_names=include_names)
 
         data_parsed = []
         for assignment in data:
             # Removing the extra "scope" layer in the assignment json
             scope = assignment.scope
             if 'project' in scope:
-                setattr(assignment, 'project', scope['project']['id'])
+                if include_names:
+                    prj = '@'.join([scope['project']['name'],
+                                   scope['project']['domain']['name']])
+                    setattr(assignment, 'project', prj)
+                else:
+                    setattr(assignment, 'project', scope['project']['id'])
                 assignment.domain = ''
             elif 'domain' in scope:
-                setattr(assignment, 'domain', scope['domain']['id'])
+                if include_names:
+                    setattr(assignment, 'domain', scope['domain']['name'])
+                else:
+                    setattr(assignment, 'domain', scope['domain']['id'])
                 assignment.project = ''
 
             else:
@@ -148,17 +163,30 @@ class ListRoleAssignment(lister.Lister):
             del assignment.scope
 
             if hasattr(assignment, 'user'):
-                setattr(assignment, 'user', assignment.user['id'])
+                if include_names:
+                    usr = '@'.join([assignment.user['name'],
+                                    assignment.user['domain']['name']])
+                    setattr(assignment, 'user', usr)
+                else:
+                    setattr(assignment, 'user', assignment.user['id'])
                 assignment.group = ''
             elif hasattr(assignment, 'group'):
-                setattr(assignment, 'group', assignment.group['id'])
+                if include_names:
+                    grp = '@'.join([assignment.group['name'],
+                                    assignment.group['domain']['name']])
+                    setattr(assignment, 'group', grp)
+                else:
+                    setattr(assignment, 'group', assignment.group['id'])
                 assignment.user = ''
             else:
                 assignment.user = ''
                 assignment.group = ''
 
             if hasattr(assignment, 'role'):
-                setattr(assignment, 'role', assignment.role['id'])
+                if include_names:
+                    setattr(assignment, 'role', assignment.role['name'])
+                else:
+                    setattr(assignment, 'role', assignment.role['id'])
             else:
                 assignment.role = ''
 
diff --git a/openstackclient/tests/identity/v3/fakes.py b/openstackclient/tests/identity/v3/fakes.py
index 9fe341ed6a..59b08973b1 100644
--- a/openstackclient/tests/identity/v3/fakes.py
+++ b/openstackclient/tests/identity/v3/fakes.py
@@ -314,6 +314,22 @@ ASSIGNMENT_WITH_PROJECT_ID_AND_USER_ID = {
     'role': {'id': role_id},
 }
 
+ASSIGNMENT_WITH_PROJECT_ID_AND_USER_ID_INCLUDE_NAMES = {
+    'scope': {
+        'project': {
+            'domain': {'id': domain_id,
+                       'name': domain_name},
+            'id': project_id,
+            'name': project_name}},
+    'user': {
+        'domain': {'id': domain_id,
+                   'name': domain_name},
+        'id': user_id,
+        'name': user_name},
+    'role': {'id': role_id,
+             'name': role_name},
+}
+
 ASSIGNMENT_WITH_PROJECT_ID_AND_USER_ID_INHERITED = {
     'scope': {'project': {'id': project_id},
               'OS-INHERIT:inherited_to': 'projects'},
@@ -333,6 +349,19 @@ ASSIGNMENT_WITH_DOMAIN_ID_AND_USER_ID = {
     'role': {'id': role_id},
 }
 
+ASSIGNMENT_WITH_DOMAIN_ID_AND_USER_ID_INCLUDE_NAMES = {
+    'scope': {
+        'domain': {'id': domain_id,
+                   'name': domain_name}},
+    'user': {
+        'domain': {'id': domain_id,
+                   'name': domain_name},
+        'id': user_id,
+        'name': user_name},
+    'role': {'id': role_id,
+             'name': role_name},
+}
+
 ASSIGNMENT_WITH_DOMAIN_ID_AND_USER_ID_INHERITED = {
     'scope': {'domain': {'id': domain_id},
               'OS-INHERIT:inherited_to': 'projects'},
diff --git a/openstackclient/tests/identity/v3/test_role_assignment.py b/openstackclient/tests/identity/v3/test_role_assignment.py
index 5723a33477..067a9537e6 100644
--- a/openstackclient/tests/identity/v3/test_role_assignment.py
+++ b/openstackclient/tests/identity/v3/test_role_assignment.py
@@ -96,7 +96,8 @@ class TestRoleAssignmentList(TestRoleAssignment):
             role=None,
             user=None,
             project=None,
-            os_inherit_extension_inherited_to=None)
+            os_inherit_extension_inherited_to=None,
+            include_names=False)
 
         self.assertEqual(self.columns, columns)
         datalist = ((
@@ -143,6 +144,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
             ('role', None),
             ('effective', False),
             ('inherited', False),
+            ('names', False),
         ]
         parsed_args = self.check_parser(self.cmd, arglist, verifylist)
 
@@ -156,7 +158,8 @@ class TestRoleAssignmentList(TestRoleAssignment):
             project=None,
             role=None,
             effective=False,
-            os_inherit_extension_inherited_to=None)
+            os_inherit_extension_inherited_to=None,
+            include_names=False)
 
         self.assertEqual(self.columns, columns)
         datalist = ((
@@ -203,6 +206,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
             ('role', None),
             ('effective', False),
             ('inherited', False),
+            ('names', False),
         ]
         parsed_args = self.check_parser(self.cmd, arglist, verifylist)
 
@@ -216,7 +220,8 @@ class TestRoleAssignmentList(TestRoleAssignment):
             project=None,
             role=None,
             user=None,
-            os_inherit_extension_inherited_to=None)
+            os_inherit_extension_inherited_to=None,
+            include_names=False)
 
         self.assertEqual(self.columns, columns)
         datalist = ((
@@ -263,6 +268,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
             ('role', None),
             ('effective', False),
             ('inherited', False),
+            ('names', False),
         ]
         parsed_args = self.check_parser(self.cmd, arglist, verifylist)
 
@@ -276,7 +282,8 @@ class TestRoleAssignmentList(TestRoleAssignment):
             project=None,
             role=None,
             user=None,
-            os_inherit_extension_inherited_to=None)
+            os_inherit_extension_inherited_to=None,
+            include_names=False)
 
         self.assertEqual(self.columns, columns)
         datalist = ((
@@ -323,6 +330,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
             ('role', None),
             ('effective', False),
             ('inherited', False),
+            ('names', False),
         ]
         parsed_args = self.check_parser(self.cmd, arglist, verifylist)
 
@@ -336,7 +344,8 @@ class TestRoleAssignmentList(TestRoleAssignment):
             project=self.projects_mock.get(),
             role=None,
             user=None,
-            os_inherit_extension_inherited_to=None)
+            os_inherit_extension_inherited_to=None,
+            include_names=False)
 
         self.assertEqual(self.columns, columns)
         datalist = ((
@@ -381,6 +390,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
             ('role', None),
             ('effective', True),
             ('inherited', False),
+            ('names', False),
         ]
         parsed_args = self.check_parser(self.cmd, arglist, verifylist)
 
@@ -394,7 +404,8 @@ class TestRoleAssignmentList(TestRoleAssignment):
             project=None,
             role=None,
             user=None,
-            os_inherit_extension_inherited_to=None)
+            os_inherit_extension_inherited_to=None,
+            include_names=False)
 
         self.assertEqual(self.columns, columns)
         datalist = ((
@@ -441,6 +452,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
             ('role', None),
             ('effective', False),
             ('inherited', True),
+            ('names', False),
         ]
         parsed_args = self.check_parser(self.cmd, arglist, verifylist)
 
@@ -454,7 +466,8 @@ class TestRoleAssignmentList(TestRoleAssignment):
             project=None,
             role=None,
             user=None,
-            os_inherit_extension_inherited_to='projects')
+            os_inherit_extension_inherited_to='projects',
+            include_names=False)
 
         self.assertEqual(self.columns, columns)
         datalist = ((
@@ -472,3 +485,72 @@ class TestRoleAssignmentList(TestRoleAssignment):
             True
             ),)
         self.assertEqual(datalist, tuple(data))
+
+    def test_role_assignment_list_include_names(self):
+
+        self.role_assignments_mock.list.return_value = [
+            fakes.FakeResource(
+                None,
+                copy.deepcopy(
+                    identity_fakes
+                    .ASSIGNMENT_WITH_PROJECT_ID_AND_USER_ID_INCLUDE_NAMES),
+                loaded=True,
+            ),
+            fakes.FakeResource(
+                None,
+                copy.deepcopy(
+                    identity_fakes
+                    .ASSIGNMENT_WITH_DOMAIN_ID_AND_USER_ID_INCLUDE_NAMES),
+                loaded=True,
+            ),
+        ]
+
+        arglist = ['--names']
+        verifylist = [
+            ('user', None),
+            ('group', None),
+            ('domain', None),
+            ('project', None),
+            ('role', None),
+            ('effective', False),
+            ('inherited', False),
+            ('names', True),
+        ]
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+        # DisplayCommandBase.take_action() returns two tuples
+
+        # This test will not run correctly until the patch in the python
+        # client is merged. Once that is done 'data' should return the
+        # correct information
+        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=None,
+            include_names=True)
+
+        collist = ('Role', 'User', 'Group', 'Project', 'Domain', 'Inherited')
+        self.assertEqual(columns, collist)
+
+        datalist1 = ((
+            identity_fakes.role_name,
+            '@'.join([identity_fakes.user_name, identity_fakes.domain_name]),
+            '',
+            '@'.join([identity_fakes.project_name,
+                     identity_fakes.domain_name]),
+            '',
+            False
+        ), (identity_fakes.role_name,
+            '@'.join([identity_fakes.user_name, identity_fakes.domain_name]),
+            '',
+            '',
+            identity_fakes.domain_name,
+            False
+            ),)
+        self.assertEqual(tuple(data), datalist1)
diff --git a/releasenotes/notes/list_role_assignment_names-0db89f50259d4be2.yaml b/releasenotes/notes/list_role_assignment_names-0db89f50259d4be2.yaml
new file mode 100644
index 0000000000..6f5df8d89c
--- /dev/null
+++ b/releasenotes/notes/list_role_assignment_names-0db89f50259d4be2.yaml
@@ -0,0 +1,6 @@
+---
+features:
+  - >
+    [`bug 1479569 <https://bugs.launchpad.net/python-keystoneclient/+bug/1479569>`_]
+    Add an optional ``--names`` argument to the `role assignment list`` command. This
+    will output names instead of IDs for users, groups, roles, projects, and domains.
\ No newline at end of file