Skip to content

Commit b5d707e

Browse files
Jenkinsopenstack-gerrit
authored andcommitted
Merge "Support to add/remove multi users for "group add/remove user""
2 parents 2fdd4ff + ef5a7ca commit b5d707e

5 files changed

Lines changed: 193 additions & 59 deletions

File tree

doc/source/command-objects/group.rst

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Add user to group
1616
[--group-domain <group-domain>]
1717
[--user-domain <user-domain>]
1818
<group>
19-
<user>
19+
<user> [<user> ...]
2020
2121
.. option:: --group-domain <group-domain>
2222

@@ -38,7 +38,8 @@ Add user to group
3838

3939
.. describe:: <user>
4040

41-
User to add to <group> (name or ID)
41+
User(s) to add to <group> (name or ID)
42+
(repeat option to add multiple users)
4243

4344
group contains user
4445
-------------------
@@ -172,7 +173,7 @@ Remove user from group
172173
[--group-domain <group-domain>]
173174
[--user-domain <user-domain>]
174175
<group>
175-
<user>
176+
<user> [<user> ...]
176177
177178
.. option:: --group-domain <group-domain>
178179
@@ -194,7 +195,8 @@ Remove user from group
194195
195196
.. describe:: <user>
196197
197-
User to remove from <group> (name or ID)
198+
User(s) to remove from <group> (name or ID)
199+
(repeat option to remove multiple users)
198200
199201
group set
200202
---------

openstackclient/identity/v3/group.py

Lines changed: 48 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@ def get_parser(self, prog_name):
4444
parser.add_argument(
4545
'user',
4646
metavar='<user>',
47-
help=_('User to add to <group> (name or ID)'),
47+
nargs='+',
48+
help=_('User(s) to add to <group> (name or ID) '
49+
'(repeat option to add multiple users)'),
4850
)
4951
common.add_group_domain_option_to_parser(parser)
5052
common.add_user_domain_option_to_parser(parser)
@@ -53,20 +55,32 @@ def get_parser(self, prog_name):
5355
def take_action(self, parsed_args):
5456
identity_client = self.app.client_manager.identity
5557

56-
user_id = common.find_user(identity_client,
57-
parsed_args.user,
58-
parsed_args.user_domain).id
5958
group_id = common.find_group(identity_client,
6059
parsed_args.group,
6160
parsed_args.group_domain).id
6261

63-
try:
64-
identity_client.users.add_to_group(user_id, group_id)
65-
except Exception as e:
66-
msg = _("%(user)s not added to group %(group)s: %(e)s") % {
67-
'user': parsed_args.user,
62+
result = 0
63+
for i in parsed_args.user:
64+
try:
65+
user_id = common.find_user(identity_client,
66+
i,
67+
parsed_args.user_domain).id
68+
identity_client.users.add_to_group(user_id, group_id)
69+
except Exception as e:
70+
result += 1
71+
msg = _("%(user)s not added to group %(group)s: %(e)s") % {
72+
'user': i,
73+
'group': parsed_args.group,
74+
'e': e,
75+
}
76+
LOG.error(msg)
77+
if result > 0:
78+
total = len(parsed_args.user)
79+
msg = (_("%(result)s of %(total)s users not added to group "
80+
"%(group)s.")) % {
81+
'result': result,
82+
'total': total,
6883
'group': parsed_args.group,
69-
'e': e,
7084
}
7185
raise exceptions.CommandError(msg)
7286

@@ -286,7 +300,9 @@ def get_parser(self, prog_name):
286300
parser.add_argument(
287301
'user',
288302
metavar='<user>',
289-
help=_('User to remove from <group> (name or ID)'),
303+
nargs='+',
304+
help=_('User(s) to remove from <group> (name or ID) '
305+
'(repeat option to remove multiple users)'),
290306
)
291307
common.add_group_domain_option_to_parser(parser)
292308
common.add_user_domain_option_to_parser(parser)
@@ -295,20 +311,32 @@ def get_parser(self, prog_name):
295311
def take_action(self, parsed_args):
296312
identity_client = self.app.client_manager.identity
297313

298-
user_id = common.find_user(identity_client,
299-
parsed_args.user,
300-
parsed_args.user_domain).id
301314
group_id = common.find_group(identity_client,
302315
parsed_args.group,
303316
parsed_args.group_domain).id
304317

305-
try:
306-
identity_client.users.remove_from_group(user_id, group_id)
307-
except Exception as e:
308-
msg = _("%(user)s not removed from group %(group)s: %(e)s") % {
309-
'user': parsed_args.user,
318+
result = 0
319+
for i in parsed_args.user:
320+
try:
321+
user_id = common.find_user(identity_client,
322+
i,
323+
parsed_args.user_domain).id
324+
identity_client.users.remove_from_group(user_id, group_id)
325+
except Exception as e:
326+
result += 1
327+
msg = _("%(user)s not removed from group %(group)s: %(e)s") % {
328+
'user': i,
329+
'group': parsed_args.group,
330+
'e': e,
331+
}
332+
LOG.error(msg)
333+
if result > 0:
334+
total = len(parsed_args.user)
335+
msg = (_("%(result)s of %(total)s users not removed from group "
336+
"%(group)s.")) % {
337+
'result': result,
338+
'total': total,
310339
'group': parsed_args.group,
311-
'e': e,
312340
}
313341
raise exceptions.CommandError(msg)
314342

openstackclient/tests/unit/identity/v3/fakes.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -770,6 +770,44 @@ def create_one_user(attrs=None):
770770
loaded=True)
771771
return user
772772

773+
@staticmethod
774+
def create_users(attrs=None, count=2):
775+
"""Create multiple fake users.
776+
777+
:param Dictionary attrs:
778+
A dictionary with all attributes
779+
:param int count:
780+
The number of users to fake
781+
:return:
782+
A list of FakeResource objects faking the users
783+
"""
784+
users = []
785+
for i in range(0, count):
786+
user = FakeUser.create_one_user(attrs)
787+
users.append(user)
788+
789+
return users
790+
791+
@staticmethod
792+
def get_users(users=None, count=2):
793+
"""Get an iterable MagicMock object with a list of faked users.
794+
795+
If users list is provided, then initialize the Mock object with
796+
the list. Otherwise create one.
797+
798+
:param List users:
799+
A list of FakeResource objects faking users
800+
:param Integer count:
801+
The number of users to be faked
802+
:return
803+
An iterable Mock object with side_effect set to a list of faked
804+
users
805+
"""
806+
if users is None:
807+
users = FakeUser.create_users(count)
808+
809+
return mock.Mock(side_effect=users)
810+
773811

774812
class FakeGroup(object):
775813
"""Fake one or more group."""

openstackclient/tests/unit/identity/v3/test_group.py

Lines changed: 96 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -42,47 +42,78 @@ def setUp(self):
4242

4343
class TestGroupAddUser(TestGroup):
4444

45-
group = identity_fakes.FakeGroup.create_one_group()
46-
user = identity_fakes.FakeUser.create_one_user()
45+
_group = identity_fakes.FakeGroup.create_one_group()
46+
users = identity_fakes.FakeUser.create_users(count=2)
4747

4848
def setUp(self):
4949
super(TestGroupAddUser, self).setUp()
5050

51-
self.groups_mock.get.return_value = self.group
52-
self.users_mock.get.return_value = self.user
51+
self.groups_mock.get.return_value = self._group
52+
self.users_mock.get = (
53+
identity_fakes.FakeUser.get_users(self.users))
5354
self.users_mock.add_to_group.return_value = None
5455

5556
self.cmd = group.AddUserToGroup(self.app, None)
5657

5758
def test_group_add_user(self):
5859
arglist = [
59-
self.group.name,
60-
self.user.name,
60+
self._group.name,
61+
self.users[0].name,
6162
]
6263
verifylist = [
63-
('group', self.group.name),
64-
('user', self.user.name),
64+
('group', self._group.name),
65+
('user', [self.users[0].name]),
6566
]
6667
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
6768

6869
result = self.cmd.take_action(parsed_args)
6970
self.users_mock.add_to_group.assert_called_once_with(
70-
self.user.id, self.group.id)
71+
self.users[0].id, self._group.id)
7172
self.assertIsNone(result)
7273

73-
def test_group_add_user_with_error(self):
74-
self.users_mock.add_to_group.side_effect = exceptions.CommandError()
74+
def test_group_add_multi_users(self):
7575
arglist = [
76-
self.group.name,
77-
self.user.name,
76+
self._group.name,
77+
self.users[0].name,
78+
self.users[1].name,
7879
]
7980
verifylist = [
80-
('group', self.group.name),
81-
('user', self.user.name),
81+
('group', self._group.name),
82+
('user', [self.users[0].name, self.users[1].name]),
8283
]
8384
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
84-
self.assertRaises(exceptions.CommandError,
85-
self.cmd.take_action, parsed_args)
85+
86+
result = self.cmd.take_action(parsed_args)
87+
calls = [call(self.users[0].id, self._group.id),
88+
call(self.users[1].id, self._group.id)]
89+
self.users_mock.add_to_group.assert_has_calls(calls)
90+
self.assertIsNone(result)
91+
92+
@mock.patch.object(group.LOG, 'error')
93+
def test_group_add_user_with_error(self, mock_error):
94+
self.users_mock.add_to_group.side_effect = [
95+
exceptions.CommandError(), None]
96+
arglist = [
97+
self._group.name,
98+
self.users[0].name,
99+
self.users[1].name,
100+
]
101+
verifylist = [
102+
('group', self._group.name),
103+
('user', [self.users[0].name, self.users[1].name]),
104+
]
105+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
106+
try:
107+
self.cmd.take_action(parsed_args)
108+
self.fail('CommandError should be raised.')
109+
except exceptions.CommandError as e:
110+
msg = "1 of 2 users not added to group %s." % self._group.name
111+
self.assertEqual(msg, str(e))
112+
msg = ("%(user)s not added to group %(group)s: ") % {
113+
'user': self.users[0].name,
114+
'group': self._group.name,
115+
}
116+
mock_error.assert_called_once_with(msg)
86117

87118

88119
class TestGroupCheckUser(TestGroup):
@@ -463,48 +494,78 @@ def test_group_list_long(self):
463494

464495
class TestGroupRemoveUser(TestGroup):
465496

466-
group = identity_fakes.FakeGroup.create_one_group()
467-
user = identity_fakes.FakeUser.create_one_user()
497+
_group = identity_fakes.FakeGroup.create_one_group()
498+
users = identity_fakes.FakeUser.create_users(count=2)
468499

469500
def setUp(self):
470501
super(TestGroupRemoveUser, self).setUp()
471502

472-
self.groups_mock.get.return_value = self.group
473-
self.users_mock.get.return_value = self.user
503+
self.groups_mock.get.return_value = self._group
504+
self.users_mock.get = (
505+
identity_fakes.FakeUser.get_users(self.users))
474506
self.users_mock.remove_from_group.return_value = None
475507

476508
self.cmd = group.RemoveUserFromGroup(self.app, None)
477509

478510
def test_group_remove_user(self):
479511
arglist = [
480-
self.group.id,
481-
self.user.id,
512+
self._group.id,
513+
self.users[0].id,
482514
]
483515
verifylist = [
484-
('group', self.group.id),
485-
('user', self.user.id),
516+
('group', self._group.id),
517+
('user', [self.users[0].id]),
486518
]
487519
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
488520

489521
result = self.cmd.take_action(parsed_args)
490522
self.users_mock.remove_from_group.assert_called_once_with(
491-
self.user.id, self.group.id)
523+
self.users[0].id, self._group.id)
492524
self.assertIsNone(result)
493525

494-
def test_group_remove_user_with_error(self):
495-
self.users_mock.remove_from_group.side_effect = (
496-
exceptions.CommandError())
526+
def test_group_remove_multi_users(self):
497527
arglist = [
498-
self.group.id,
499-
self.user.id,
528+
self._group.name,
529+
self.users[0].name,
530+
self.users[1].name,
500531
]
501532
verifylist = [
502-
('group', self.group.id),
503-
('user', self.user.id),
533+
('group', self._group.name),
534+
('user', [self.users[0].name, self.users[1].name]),
504535
]
505536
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
506-
self.assertRaises(exceptions.CommandError,
507-
self.cmd.take_action, parsed_args)
537+
538+
result = self.cmd.take_action(parsed_args)
539+
calls = [call(self.users[0].id, self._group.id),
540+
call(self.users[1].id, self._group.id)]
541+
self.users_mock.remove_from_group.assert_has_calls(calls)
542+
self.assertIsNone(result)
543+
544+
@mock.patch.object(group.LOG, 'error')
545+
def test_group_remove_user_with_error(self, mock_error):
546+
self.users_mock.remove_from_group.side_effect = [
547+
exceptions.CommandError(), None]
548+
arglist = [
549+
self._group.id,
550+
self.users[0].id,
551+
self.users[1].id,
552+
]
553+
verifylist = [
554+
('group', self._group.id),
555+
('user', [self.users[0].id, self.users[1].id]),
556+
]
557+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
558+
try:
559+
self.cmd.take_action(parsed_args)
560+
self.fail('CommandError should be raised.')
561+
except exceptions.CommandError as e:
562+
msg = "1 of 2 users not removed from group %s." % self._group.id
563+
self.assertEqual(msg, str(e))
564+
msg = ("%(user)s not removed from group %(group)s: ") % {
565+
'user': self.users[0].id,
566+
'group': self._group.id,
567+
}
568+
mock_error.assert_called_once_with(msg)
508569

509570

510571
class TestGroupSet(TestGroup):
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
features:
3+
- |
4+
Add support to add/remove multi users by ``group add/remove user`` command.
5+
[Blueprint :oscbp:`support-multi-add-remove`]

0 commit comments

Comments
 (0)