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>
This commit is contained in:
parent
45ac2b62fb
commit
ece30e8f70
openstackclient
releasenotes/notes
@ -670,7 +670,7 @@ class AddNetwork(command.Command):
|
|||||||
|
|
||||||
|
|
||||||
class AddServerSecurityGroup(command.Command):
|
class AddServerSecurityGroup(command.Command):
|
||||||
_description = _("Add security group to server")
|
_description = _("Add security group(s) to server")
|
||||||
|
|
||||||
def get_parser(self, prog_name):
|
def get_parser(self, prog_name):
|
||||||
parser = super().get_parser(prog_name)
|
parser = super().get_parser(prog_name)
|
||||||
@ -680,9 +680,13 @@ class AddServerSecurityGroup(command.Command):
|
|||||||
help=_('Server (name or ID)'),
|
help=_('Server (name or ID)'),
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'group',
|
'security_groups',
|
||||||
metavar='<group>',
|
metavar='<security-group>',
|
||||||
help=_('Security group to add (name or ID)'),
|
nargs='+',
|
||||||
|
help=_(
|
||||||
|
'Security group(s) to add to the server (name or ID) '
|
||||||
|
'(repeat option to add multiple groups)'
|
||||||
|
),
|
||||||
)
|
)
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
@ -694,14 +698,43 @@ class AddServerSecurityGroup(command.Command):
|
|||||||
)
|
)
|
||||||
if self.app.client_manager.is_network_endpoint_enabled():
|
if self.app.client_manager.is_network_endpoint_enabled():
|
||||||
# the server handles both names and IDs for neutron SGs, so just
|
# the server handles both names and IDs for neutron SGs, so just
|
||||||
# pass things through
|
# pass things through if using neutron
|
||||||
security_group = parsed_args.group
|
security_groups = parsed_args.security_groups
|
||||||
else:
|
else:
|
||||||
# however, if using nova-network then it needs a name, not an ID
|
# however, if using nova-network then it needs names, not IDs
|
||||||
security_group = compute_v2.find_security_group(
|
security_groups = []
|
||||||
compute_client, parsed_args.group
|
for security_group in parsed_args.security_groups:
|
||||||
)['name']
|
security_groups.append(
|
||||||
compute_client.add_security_group_to_server(server, security_group)
|
compute_v2.find_security_group(
|
||||||
|
compute_client, security_group
|
||||||
|
)['name']
|
||||||
|
)
|
||||||
|
|
||||||
|
errors = 0
|
||||||
|
for security_group in security_groups:
|
||||||
|
try:
|
||||||
|
compute_client.add_security_group_to_server(
|
||||||
|
server, security_group
|
||||||
|
)
|
||||||
|
except sdk_exceptions.HttpException as e:
|
||||||
|
errors += 1
|
||||||
|
LOG.error(
|
||||||
|
_(
|
||||||
|
"Failed to add security group with name or ID "
|
||||||
|
"'%(security_group)s' to server '%(server)s': %(e)s"
|
||||||
|
),
|
||||||
|
{
|
||||||
|
'security_group': security_group,
|
||||||
|
'server': server.id,
|
||||||
|
'e': e,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if errors > 0:
|
||||||
|
msg = _(
|
||||||
|
"%(errors)d of %(total)d security groups were not added."
|
||||||
|
) % {'errors': errors, 'total': len(security_groups)}
|
||||||
|
raise exceptions.CommandError(msg)
|
||||||
|
|
||||||
|
|
||||||
class AddServerVolume(command.ShowOne):
|
class AddServerVolume(command.ShowOne):
|
||||||
@ -1328,6 +1361,7 @@ class CreateServer(command.ShowOne):
|
|||||||
metavar='<security-group>',
|
metavar='<security-group>',
|
||||||
action='append',
|
action='append',
|
||||||
default=[],
|
default=[],
|
||||||
|
dest='security_groups',
|
||||||
help=_(
|
help=_(
|
||||||
'Security group to assign to this server (name or ID) '
|
'Security group to assign to this server (name or ID) '
|
||||||
'(repeat option to set multiple groups)'
|
'(repeat option to set multiple groups)'
|
||||||
@ -1948,21 +1982,22 @@ class CreateServer(command.ShowOne):
|
|||||||
# 'auto' to maintain legacy behavior if a nic wasn't specified.
|
# 'auto' to maintain legacy behavior if a nic wasn't specified.
|
||||||
networks = 'auto'
|
networks = 'auto'
|
||||||
|
|
||||||
# Check security group exist and convert ID to name
|
# Check security group(s) exist and convert ID to name
|
||||||
security_groups = []
|
security_groups = []
|
||||||
if self.app.client_manager.is_network_endpoint_enabled():
|
if self.app.client_manager.is_network_endpoint_enabled():
|
||||||
network_client = self.app.client_manager.network
|
network_client = self.app.client_manager.network
|
||||||
for each_sg in parsed_args.security_group:
|
for security_group in parsed_args.security_groups:
|
||||||
sg = network_client.find_security_group(
|
sg = network_client.find_security_group(
|
||||||
each_sg, ignore_missing=False
|
security_group, ignore_missing=False
|
||||||
)
|
)
|
||||||
# Use security group ID to avoid multiple security group have
|
# Use security group ID to avoid multiple security group have
|
||||||
# same name in neutron networking backend
|
# same name in neutron networking backend
|
||||||
security_groups.append({'name': sg.id})
|
security_groups.append({'name': sg.id})
|
||||||
else:
|
else: # nova-network
|
||||||
# Handle nova-network case
|
for security_group in parsed_args.security_groups:
|
||||||
for each_sg in parsed_args.security_group:
|
sg = compute_v2.find_security_group(
|
||||||
sg = compute_v2.find_security_group(compute_client, each_sg)
|
compute_client, security_group
|
||||||
|
)
|
||||||
security_groups.append({'name': sg['name']})
|
security_groups.append({'name': sg['name']})
|
||||||
|
|
||||||
hints = {}
|
hints = {}
|
||||||
@ -4016,9 +4051,13 @@ class RemoveServerSecurityGroup(command.Command):
|
|||||||
help=_('Server (name or ID)'),
|
help=_('Server (name or ID)'),
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'group',
|
'security_groups',
|
||||||
metavar='<group>',
|
metavar='<security-group>',
|
||||||
help=_('Security group to remove (name or ID)'),
|
nargs='+',
|
||||||
|
help=_(
|
||||||
|
'Security group(s) to remove from server (name or ID) '
|
||||||
|
'(repeat option to remove multiple groups)'
|
||||||
|
),
|
||||||
)
|
)
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
@ -4031,15 +4070,42 @@ class RemoveServerSecurityGroup(command.Command):
|
|||||||
if self.app.client_manager.is_network_endpoint_enabled():
|
if self.app.client_manager.is_network_endpoint_enabled():
|
||||||
# the server handles both names and IDs for neutron SGs, so just
|
# the server handles both names and IDs for neutron SGs, so just
|
||||||
# pass things through
|
# pass things through
|
||||||
security_group = parsed_args.group
|
security_groups = parsed_args.security_groups
|
||||||
else:
|
else:
|
||||||
# however, if using nova-network then it needs a name, not an ID
|
# however, if using nova-network then it needs names, not IDs
|
||||||
security_group = compute_v2.find_security_group(
|
security_groups = []
|
||||||
compute_client, parsed_args.group
|
for security_group in parsed_args.security_groups:
|
||||||
)['name']
|
security_groups.append(
|
||||||
compute_client.remove_security_group_from_server(
|
compute_v2.find_security_group(
|
||||||
server, security_group
|
compute_client, security_group
|
||||||
)
|
)['name']
|
||||||
|
)
|
||||||
|
|
||||||
|
errors = 0
|
||||||
|
for security_group in security_groups:
|
||||||
|
try:
|
||||||
|
compute_client.remove_security_group_from_server(
|
||||||
|
server, security_group
|
||||||
|
)
|
||||||
|
except sdk_exceptions.HttpException as e:
|
||||||
|
errors += 1
|
||||||
|
LOG.error(
|
||||||
|
_(
|
||||||
|
"Failed to remove security group with name or ID "
|
||||||
|
"'%(security_group)s' from server '%(server)s': %(e)s"
|
||||||
|
),
|
||||||
|
{
|
||||||
|
'security_group': security_group,
|
||||||
|
'server': server.id,
|
||||||
|
'e': e,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if errors > 0:
|
||||||
|
msg = _(
|
||||||
|
"%(errors)d of %(total)d security groups were not removed."
|
||||||
|
) % {'errors': errors, 'total': len(security_groups)}
|
||||||
|
raise exceptions.CommandError(msg)
|
||||||
|
|
||||||
|
|
||||||
class RemoveServerVolume(command.Command):
|
class RemoveServerVolume(command.Command):
|
||||||
|
@ -21,7 +21,7 @@ class HelpTests(base.TestCase):
|
|||||||
"""Functional tests for openstackclient help output."""
|
"""Functional tests for openstackclient help output."""
|
||||||
|
|
||||||
SERVER_COMMANDS = [
|
SERVER_COMMANDS = [
|
||||||
('server add security group', 'Add security group to server'),
|
('server add security group', 'Add security group(s) to server'),
|
||||||
('server add volume', 'Add volume to server'),
|
('server add volume', 'Add volume to server'),
|
||||||
('server backup create', 'Create a server backup image'),
|
('server backup create', 'Create a server backup image'),
|
||||||
('server create', 'Create a new server'),
|
('server create', 'Create a new server'),
|
||||||
|
@ -1166,7 +1166,7 @@ class TestServerAddSecurityGroup(compute_fakes.TestComputev2):
|
|||||||
arglist = [self.server.id, 'fake_sg']
|
arglist = [self.server.id, 'fake_sg']
|
||||||
verifylist = [
|
verifylist = [
|
||||||
('server', self.server.id),
|
('server', self.server.id),
|
||||||
('group', 'fake_sg'),
|
('security_groups', ['fake_sg']),
|
||||||
]
|
]
|
||||||
|
|
||||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
@ -1197,7 +1197,7 @@ class TestServerAddSecurityGroup(compute_fakes.TestComputev2):
|
|||||||
arglist = [self.server.id, 'fake_sg']
|
arglist = [self.server.id, 'fake_sg']
|
||||||
verifylist = [
|
verifylist = [
|
||||||
('server', self.server.id),
|
('server', self.server.id),
|
||||||
('group', 'fake_sg'),
|
('security_groups', ['fake_sg']),
|
||||||
]
|
]
|
||||||
|
|
||||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
@ -1429,7 +1429,7 @@ class TestServerCreate(TestServer):
|
|||||||
('flavor', self.flavor.id),
|
('flavor', self.flavor.id),
|
||||||
('key_name', 'keyname'),
|
('key_name', 'keyname'),
|
||||||
('properties', {'Beta': 'b'}),
|
('properties', {'Beta': 'b'}),
|
||||||
('security_group', [security_group.id]),
|
('security_groups', [security_group.id]),
|
||||||
('hints', {'a': ['b', 'c']}),
|
('hints', {'a': ['b', 'c']}),
|
||||||
('server_group', server_group.id),
|
('server_group', server_group.id),
|
||||||
('config_drive', True),
|
('config_drive', True),
|
||||||
@ -1499,7 +1499,7 @@ class TestServerCreate(TestServer):
|
|||||||
('image', self.image.id),
|
('image', self.image.id),
|
||||||
('flavor', self.flavor.id),
|
('flavor', self.flavor.id),
|
||||||
('key_name', 'keyname'),
|
('key_name', 'keyname'),
|
||||||
('security_group', ['not_exist_sg']),
|
('security_groups', ['not_exist_sg']),
|
||||||
('server_name', self.server.name),
|
('server_name', self.server.name),
|
||||||
]
|
]
|
||||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
@ -1525,7 +1525,7 @@ class TestServerCreate(TestServer):
|
|||||||
verifylist = [
|
verifylist = [
|
||||||
('image', self.image.id),
|
('image', self.image.id),
|
||||||
('flavor', self.flavor.id),
|
('flavor', self.flavor.id),
|
||||||
('security_group', [sg_name]),
|
('security_groups', [sg_name]),
|
||||||
('server_name', self.server.name),
|
('server_name', self.server.name),
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -7416,7 +7416,7 @@ class TestServerRemoveSecurityGroup(TestServer):
|
|||||||
arglist = [self.server.id, 'fake_sg']
|
arglist = [self.server.id, 'fake_sg']
|
||||||
verifylist = [
|
verifylist = [
|
||||||
('server', self.server.id),
|
('server', self.server.id),
|
||||||
('group', 'fake_sg'),
|
('security_groups', ['fake_sg']),
|
||||||
]
|
]
|
||||||
|
|
||||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
@ -7447,7 +7447,7 @@ class TestServerRemoveSecurityGroup(TestServer):
|
|||||||
arglist = [self.server.id, 'fake_sg']
|
arglist = [self.server.id, 'fake_sg']
|
||||||
verifylist = [
|
verifylist = [
|
||||||
('server', self.server.id),
|
('server', self.server.id),
|
||||||
('group', 'fake_sg'),
|
('security_groups', ['fake_sg']),
|
||||||
]
|
]
|
||||||
|
|
||||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
The ``server add security group`` and ``server remove security group``
|
||||||
|
commands now accept multiple security groups.
|
Loading…
x
Reference in New Issue
Block a user