Skip to content

Commit ece30e8

Browse files
committed
compute: Allow adding, removing multiple SGs
We also ensure we call neutron rather than the deprecated nova proxy API in the event that neutron is available. Change-Id: I8315ea164fd3fa6c1d759f16677bfd6c24c4ef63 Signed-off-by: Stephen Finucane <stephenfin@redhat.com>
1 parent 45ac2b6 commit ece30e8

File tree

4 files changed

+108
-37
lines changed

4 files changed

+108
-37
lines changed

openstackclient/compute/v2/server.py

Lines changed: 95 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -670,7 +670,7 @@ def take_action(self, parsed_args):
670670

671671

672672
class AddServerSecurityGroup(command.Command):
673-
_description = _("Add security group to server")
673+
_description = _("Add security group(s) to server")
674674

675675
def get_parser(self, prog_name):
676676
parser = super().get_parser(prog_name)
@@ -680,9 +680,13 @@ def get_parser(self, prog_name):
680680
help=_('Server (name or ID)'),
681681
)
682682
parser.add_argument(
683-
'group',
684-
metavar='<group>',
685-
help=_('Security group to add (name or ID)'),
683+
'security_groups',
684+
metavar='<security-group>',
685+
nargs='+',
686+
help=_(
687+
'Security group(s) to add to the server (name or ID) '
688+
'(repeat option to add multiple groups)'
689+
),
686690
)
687691
return parser
688692

@@ -694,14 +698,43 @@ def take_action(self, parsed_args):
694698
)
695699
if self.app.client_manager.is_network_endpoint_enabled():
696700
# the server handles both names and IDs for neutron SGs, so just
697-
# pass things through
698-
security_group = parsed_args.group
701+
# pass things through if using neutron
702+
security_groups = parsed_args.security_groups
699703
else:
700-
# however, if using nova-network then it needs a name, not an ID
701-
security_group = compute_v2.find_security_group(
702-
compute_client, parsed_args.group
703-
)['name']
704-
compute_client.add_security_group_to_server(server, security_group)
704+
# however, if using nova-network then it needs names, not IDs
705+
security_groups = []
706+
for security_group in parsed_args.security_groups:
707+
security_groups.append(
708+
compute_v2.find_security_group(
709+
compute_client, security_group
710+
)['name']
711+
)
712+
713+
errors = 0
714+
for security_group in security_groups:
715+
try:
716+
compute_client.add_security_group_to_server(
717+
server, security_group
718+
)
719+
except sdk_exceptions.HttpException as e:
720+
errors += 1
721+
LOG.error(
722+
_(
723+
"Failed to add security group with name or ID "
724+
"'%(security_group)s' to server '%(server)s': %(e)s"
725+
),
726+
{
727+
'security_group': security_group,
728+
'server': server.id,
729+
'e': e,
730+
},
731+
)
732+
733+
if errors > 0:
734+
msg = _(
735+
"%(errors)d of %(total)d security groups were not added."
736+
) % {'errors': errors, 'total': len(security_groups)}
737+
raise exceptions.CommandError(msg)
705738

706739

707740
class AddServerVolume(command.ShowOne):
@@ -1328,6 +1361,7 @@ def get_parser(self, prog_name):
13281361
metavar='<security-group>',
13291362
action='append',
13301363
default=[],
1364+
dest='security_groups',
13311365
help=_(
13321366
'Security group to assign to this server (name or ID) '
13331367
'(repeat option to set multiple groups)'
@@ -1948,21 +1982,22 @@ def _match_image(image_api, wanted_properties):
19481982
# 'auto' to maintain legacy behavior if a nic wasn't specified.
19491983
networks = 'auto'
19501984

1951-
# Check security group exist and convert ID to name
1985+
# Check security group(s) exist and convert ID to name
19521986
security_groups = []
19531987
if self.app.client_manager.is_network_endpoint_enabled():
19541988
network_client = self.app.client_manager.network
1955-
for each_sg in parsed_args.security_group:
1989+
for security_group in parsed_args.security_groups:
19561990
sg = network_client.find_security_group(
1957-
each_sg, ignore_missing=False
1991+
security_group, ignore_missing=False
19581992
)
19591993
# Use security group ID to avoid multiple security group have
19601994
# same name in neutron networking backend
19611995
security_groups.append({'name': sg.id})
1962-
else:
1963-
# Handle nova-network case
1964-
for each_sg in parsed_args.security_group:
1965-
sg = compute_v2.find_security_group(compute_client, each_sg)
1996+
else: # nova-network
1997+
for security_group in parsed_args.security_groups:
1998+
sg = compute_v2.find_security_group(
1999+
compute_client, security_group
2000+
)
19662001
security_groups.append({'name': sg['name']})
19672002

19682003
hints = {}
@@ -4016,9 +4051,13 @@ def get_parser(self, prog_name):
40164051
help=_('Server (name or ID)'),
40174052
)
40184053
parser.add_argument(
4019-
'group',
4020-
metavar='<group>',
4021-
help=_('Security group to remove (name or ID)'),
4054+
'security_groups',
4055+
metavar='<security-group>',
4056+
nargs='+',
4057+
help=_(
4058+
'Security group(s) to remove from server (name or ID) '
4059+
'(repeat option to remove multiple groups)'
4060+
),
40224061
)
40234062
return parser
40244063

@@ -4031,15 +4070,42 @@ def take_action(self, parsed_args):
40314070
if self.app.client_manager.is_network_endpoint_enabled():
40324071
# the server handles both names and IDs for neutron SGs, so just
40334072
# pass things through
4034-
security_group = parsed_args.group
4073+
security_groups = parsed_args.security_groups
40354074
else:
4036-
# however, if using nova-network then it needs a name, not an ID
4037-
security_group = compute_v2.find_security_group(
4038-
compute_client, parsed_args.group
4039-
)['name']
4040-
compute_client.remove_security_group_from_server(
4041-
server, security_group
4042-
)
4075+
# however, if using nova-network then it needs names, not IDs
4076+
security_groups = []
4077+
for security_group in parsed_args.security_groups:
4078+
security_groups.append(
4079+
compute_v2.find_security_group(
4080+
compute_client, security_group
4081+
)['name']
4082+
)
4083+
4084+
errors = 0
4085+
for security_group in security_groups:
4086+
try:
4087+
compute_client.remove_security_group_from_server(
4088+
server, security_group
4089+
)
4090+
except sdk_exceptions.HttpException as e:
4091+
errors += 1
4092+
LOG.error(
4093+
_(
4094+
"Failed to remove security group with name or ID "
4095+
"'%(security_group)s' from server '%(server)s': %(e)s"
4096+
),
4097+
{
4098+
'security_group': security_group,
4099+
'server': server.id,
4100+
'e': e,
4101+
},
4102+
)
4103+
4104+
if errors > 0:
4105+
msg = _(
4106+
"%(errors)d of %(total)d security groups were not removed."
4107+
) % {'errors': errors, 'total': len(security_groups)}
4108+
raise exceptions.CommandError(msg)
40434109

40444110

40454111
class RemoveServerVolume(command.Command):

openstackclient/tests/functional/common/test_help.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class HelpTests(base.TestCase):
2121
"""Functional tests for openstackclient help output."""
2222

2323
SERVER_COMMANDS = [
24-
('server add security group', 'Add security group to server'),
24+
('server add security group', 'Add security group(s) to server'),
2525
('server add volume', 'Add volume to server'),
2626
('server backup create', 'Create a server backup image'),
2727
('server create', 'Create a new server'),

openstackclient/tests/unit/compute/v2/test_server.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1166,7 +1166,7 @@ def test_server_add_security_group__nova_network(self):
11661166
arglist = [self.server.id, 'fake_sg']
11671167
verifylist = [
11681168
('server', self.server.id),
1169-
('group', 'fake_sg'),
1169+
('security_groups', ['fake_sg']),
11701170
]
11711171

11721172
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -1197,7 +1197,7 @@ def test_server_add_security_group(self):
11971197
arglist = [self.server.id, 'fake_sg']
11981198
verifylist = [
11991199
('server', self.server.id),
1200-
('group', 'fake_sg'),
1200+
('security_groups', ['fake_sg']),
12011201
]
12021202

12031203
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -1429,7 +1429,7 @@ def test_server_create_with_options(self):
14291429
('flavor', self.flavor.id),
14301430
('key_name', 'keyname'),
14311431
('properties', {'Beta': 'b'}),
1432-
('security_group', [security_group.id]),
1432+
('security_groups', [security_group.id]),
14331433
('hints', {'a': ['b', 'c']}),
14341434
('server_group', server_group.id),
14351435
('config_drive', True),
@@ -1499,7 +1499,7 @@ def test_server_create_with_not_exist_security_group(self):
14991499
('image', self.image.id),
15001500
('flavor', self.flavor.id),
15011501
('key_name', 'keyname'),
1502-
('security_group', ['not_exist_sg']),
1502+
('security_groups', ['not_exist_sg']),
15031503
('server_name', self.server.name),
15041504
]
15051505
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -1525,7 +1525,7 @@ def test_server_create_with_security_group_in_nova_network(self):
15251525
verifylist = [
15261526
('image', self.image.id),
15271527
('flavor', self.flavor.id),
1528-
('security_group', [sg_name]),
1528+
('security_groups', [sg_name]),
15291529
('server_name', self.server.name),
15301530
]
15311531

@@ -7416,7 +7416,7 @@ def test_server_remove_security_group__nova_network(self):
74167416
arglist = [self.server.id, 'fake_sg']
74177417
verifylist = [
74187418
('server', self.server.id),
7419-
('group', 'fake_sg'),
7419+
('security_groups', ['fake_sg']),
74207420
]
74217421

74227422
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -7447,7 +7447,7 @@ def test_server_remove_security_group(self):
74477447
arglist = [self.server.id, 'fake_sg']
74487448
verifylist = [
74497449
('server', self.server.id),
7450-
('group', 'fake_sg'),
7450+
('security_groups', ['fake_sg']),
74517451
]
74527452

74537453
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
features:
3+
- |
4+
The ``server add security group`` and ``server remove security group``
5+
commands now accept multiple security groups.

0 commit comments

Comments
 (0)