Skip to content

Commit 3fabbe9

Browse files
Jenkinsopenstack-gerrit
authored andcommitted
Merge "Create server with security group ID and name"
2 parents 78cee3f + 45496fe commit 3fabbe9

5 files changed

Lines changed: 168 additions & 4 deletions

File tree

doc/source/command-objects/server.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ Create a new server
164164

165165
Create server with this flavor (name or ID)
166166

167-
.. option:: --security-group <security-group-name>
167+
.. option:: --security-group <security-group>
168168

169169
Security group to assign to this server (name or ID)
170170
(repeat option to set multiple groups)

openstackclient/compute/v2/server.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,7 @@ def get_parser(self, prog_name):
407407
)
408408
parser.add_argument(
409409
'--security-group',
410-
metavar='<security-group-name>',
410+
metavar='<security-group>',
411411
action='append',
412412
default=[],
413413
help=_('Security group to assign to this server (name or ID) '
@@ -695,6 +695,22 @@ def take_action(self, parsed_args):
695695
# decide the default behavior.
696696
nics = []
697697

698+
# Check security group exist and convert ID to name
699+
security_group_names = []
700+
if self.app.client_manager.is_network_endpoint_enabled():
701+
network_client = self.app.client_manager.network
702+
for each_sg in parsed_args.security_group:
703+
sg = network_client.find_security_group(each_sg,
704+
ignore_missing=False)
705+
# Use security group ID to avoid multiple security group have
706+
# same name in neutron networking backend
707+
security_group_names.append(sg.id)
708+
else:
709+
# Handle nova-network case
710+
for each_sg in parsed_args.security_group:
711+
sg = compute_client.api.security_group_find(each_sg)
712+
security_group_names.append(sg['name'])
713+
698714
hints = {}
699715
for hint in parsed_args.hint:
700716
key, _sep, value = hint.partition('=')
@@ -724,7 +740,7 @@ def take_action(self, parsed_args):
724740
reservation_id=None,
725741
min_count=parsed_args.min,
726742
max_count=parsed_args.max,
727-
security_groups=parsed_args.security_group,
743+
security_groups=security_group_names,
728744
userdata=userdata,
729745
key_name=parsed_args.key_name,
730746
availability_zone=parsed_args.availability_zone,

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

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,52 @@ def test_server_create_with_none_network(self):
517517
self.assertIsNotNone(server['addresses'])
518518
self.assertEqual('', server['addresses'])
519519

520+
def test_server_create_with_security_group(self):
521+
"""Test server create with security group ID and name"""
522+
if not self.haz_network:
523+
# NOTE(dtroyer): As of Ocata release Nova forces nova-network to
524+
# run in a cells v1 configuration. Security group
525+
# and network functions currently do not work in
526+
# the gate jobs so we have to skip this. It is
527+
# known to work tested against a Mitaka nova-net
528+
# DevStack without cells.
529+
self.skipTest("No Network service present")
530+
# Create two security group, use name and ID to create server
531+
sg_name1 = uuid.uuid4().hex
532+
security_group1 = json.loads(self.openstack(
533+
'security group create -f json ' + sg_name1
534+
))
535+
self.addCleanup(self.openstack, 'security group delete ' + sg_name1)
536+
sg_name2 = uuid.uuid4().hex
537+
security_group2 = json.loads(self.openstack(
538+
'security group create -f json ' + sg_name2
539+
))
540+
self.addCleanup(self.openstack, 'security group delete ' + sg_name2)
541+
542+
server_name = uuid.uuid4().hex
543+
server = json.loads(self.openstack(
544+
'server create -f json ' +
545+
'--flavor ' + self.flavor_name + ' ' +
546+
'--image ' + self.image_name + ' ' +
547+
# Security group id is integer in nova-network, convert to string
548+
'--security-group ' + str(security_group1['id']) + ' ' +
549+
'--security-group ' + security_group2['name'] + ' ' +
550+
self.network_arg + ' ' +
551+
server_name
552+
))
553+
self.addCleanup(self.openstack, 'server delete --wait ' + server_name)
554+
555+
self.assertIsNotNone(server['id'])
556+
self.assertEqual(server_name, server['name'])
557+
self.assertIn(str(security_group1['id']), server['security_groups'])
558+
self.assertIn(str(security_group2['id']), server['security_groups'])
559+
self.wait_for_status(server_name, 'ACTIVE')
560+
server = json.loads(self.openstack(
561+
'server show -f json ' + server_name
562+
))
563+
self.assertIn(sg_name1, server['security_groups'])
564+
self.assertIn(sg_name2, server['security_groups'])
565+
520566
def test_server_create_with_empty_network_option_latest(self):
521567
"""Test server create with empty network option in nova 2.latest."""
522568
server_name = uuid.uuid4().hex

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

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
from openstackclient.compute.v2 import server
2626
from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes
2727
from openstackclient.tests.unit.image.v2 import fakes as image_fakes
28+
from openstackclient.tests.unit.network.v2 import fakes as network_fakes
2829
from openstackclient.tests.unit import utils
2930
from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes
3031

@@ -421,16 +422,24 @@ def test_server_create_with_options(self):
421422
# In base command class ShowOne in cliff, abstract method take_action()
422423
# returns a two-part tuple with a tuple of column names and a tuple of
423424
# data to be shown.
425+
fake_sg = network_fakes.FakeSecurityGroup.create_security_groups()
426+
mock_find_sg = (
427+
network_fakes.FakeSecurityGroup.get_security_groups(fake_sg)
428+
)
429+
self.app.client_manager.network.find_security_group = mock_find_sg
430+
424431
columns, data = self.cmd.take_action(parsed_args)
425432

433+
mock_find_sg.assert_called_once_with('securitygroup',
434+
ignore_missing=False)
426435
# Set expected values
427436
kwargs = dict(
428437
meta={'Beta': 'b'},
429438
files={},
430439
reservation_id=None,
431440
min_count=1,
432441
max_count=1,
433-
security_groups=['securitygroup'],
442+
security_groups=[fake_sg[0].id],
434443
userdata=None,
435444
key_name='keyname',
436445
availability_zone=None,
@@ -450,6 +459,92 @@ def test_server_create_with_options(self):
450459
self.assertEqual(self.columns, columns)
451460
self.assertEqual(self.datalist(), data)
452461

462+
def test_server_create_with_not_exist_security_group(self):
463+
arglist = [
464+
'--image', 'image1',
465+
'--flavor', 'flavor1',
466+
'--key-name', 'keyname',
467+
'--security-group', 'securitygroup',
468+
'--security-group', 'not_exist_sg',
469+
self.new_server.name,
470+
]
471+
verifylist = [
472+
('image', 'image1'),
473+
('flavor', 'flavor1'),
474+
('key_name', 'keyname'),
475+
('security_group', ['securitygroup', 'not_exist_sg']),
476+
('server_name', self.new_server.name),
477+
]
478+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
479+
480+
fake_sg = network_fakes.FakeSecurityGroup.create_security_groups(
481+
count=1)
482+
fake_sg.append(exceptions.NotFound(code=404))
483+
mock_find_sg = (
484+
network_fakes.FakeSecurityGroup.get_security_groups(fake_sg)
485+
)
486+
self.app.client_manager.network.find_security_group = mock_find_sg
487+
488+
self.assertRaises(exceptions.NotFound,
489+
self.cmd.take_action,
490+
parsed_args)
491+
mock_find_sg.assert_called_with('not_exist_sg',
492+
ignore_missing=False)
493+
494+
def test_server_create_with_security_group_in_nova_network(self):
495+
arglist = [
496+
'--image', 'image1',
497+
'--flavor', 'flavor1',
498+
'--key-name', 'keyname',
499+
'--security-group', 'securitygroup',
500+
self.new_server.name,
501+
]
502+
verifylist = [
503+
('image', 'image1'),
504+
('flavor', 'flavor1'),
505+
('key_name', 'keyname'),
506+
('security_group', ['securitygroup']),
507+
('server_name', self.new_server.name),
508+
]
509+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
510+
511+
with mock.patch.object(self.app.client_manager,
512+
'is_network_endpoint_enabled',
513+
return_value=False):
514+
with mock.patch.object(self.app.client_manager.compute.api,
515+
'security_group_find',
516+
return_value={'name': 'fake_sg'}
517+
) as mock_find:
518+
columns, data = self.cmd.take_action(parsed_args)
519+
mock_find.assert_called_once_with('securitygroup')
520+
521+
# Set expected values
522+
kwargs = dict(
523+
meta=None,
524+
files={},
525+
reservation_id=None,
526+
min_count=1,
527+
max_count=1,
528+
security_groups=['fake_sg'],
529+
userdata=None,
530+
key_name='keyname',
531+
availability_zone=None,
532+
block_device_mapping_v2=[],
533+
nics=[],
534+
scheduler_hints={},
535+
config_drive=None,
536+
)
537+
# ServerManager.create(name, image, flavor, **kwargs)
538+
self.servers_mock.create.assert_called_with(
539+
self.new_server.name,
540+
self.image,
541+
self.flavor,
542+
**kwargs
543+
)
544+
545+
self.assertEqual(self.columns, columns)
546+
self.assertEqual(self.datalist(), data)
547+
453548
def test_server_create_with_network(self):
454549
arglist = [
455550
'--image', 'image1',
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
fixes:
3+
- |
4+
Allow security groups in ``server create`` command to be specified by name or ID with
5+
the ``--security-group`` option. This also checks that the security group exist before
6+
creating the server.
7+
[Bug `1687814 <https://bugs.launchpad.net/python-openstackclient/+bug/1687814>`_]

0 commit comments

Comments
 (0)