Skip to content

Commit 45496fe

Browse files
kiwikDean Troyer
authored andcommitted
Create server with security group ID and name
Both resource ID and name are supported to identify an object in openstackclient to make user easy to input, for security group, nova only support security group name in API when launch a new server, this patch convert ID to name, then pass name to nova API, and check the security group exist before creating server. Change-Id: I1ed4a967fb9de3f91c8945a1ef63f6c7b6b2dfb2 Closes-Bug: #1687814
1 parent 411cda7 commit 45496fe

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) '
@@ -677,6 +677,22 @@ def take_action(self, parsed_args):
677677
# decide the default behavior.
678678
nics = []
679679

680+
# Check security group exist and convert ID to name
681+
security_group_names = []
682+
if self.app.client_manager.is_network_endpoint_enabled():
683+
network_client = self.app.client_manager.network
684+
for each_sg in parsed_args.security_group:
685+
sg = network_client.find_security_group(each_sg,
686+
ignore_missing=False)
687+
# Use security group ID to avoid multiple security group have
688+
# same name in neutron networking backend
689+
security_group_names.append(sg.id)
690+
else:
691+
# Handle nova-network case
692+
for each_sg in parsed_args.security_group:
693+
sg = compute_client.api.security_group_find(each_sg)
694+
security_group_names.append(sg['name'])
695+
680696
hints = {}
681697
for hint in parsed_args.hint:
682698
key, _sep, value = hint.partition('=')
@@ -706,7 +722,7 @@ def take_action(self, parsed_args):
706722
reservation_id=None,
707723
min_count=parsed_args.min,
708724
max_count=parsed_args.max,
709-
security_groups=parsed_args.security_group,
725+
security_groups=security_group_names,
710726
userdata=userdata,
711727
key_name=parsed_args.key_name,
712728
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
@@ -410,6 +410,52 @@ def test_server_create_with_none_network(self):
410410
self.assertIsNotNone(server['addresses'])
411411
self.assertEqual('', server['addresses'])
412412

413+
def test_server_create_with_security_group(self):
414+
"""Test server create with security group ID and name"""
415+
if not self.haz_network:
416+
# NOTE(dtroyer): As of Ocata release Nova forces nova-network to
417+
# run in a cells v1 configuration. Security group
418+
# and network functions currently do not work in
419+
# the gate jobs so we have to skip this. It is
420+
# known to work tested against a Mitaka nova-net
421+
# DevStack without cells.
422+
self.skipTest("No Network service present")
423+
# Create two security group, use name and ID to create server
424+
sg_name1 = uuid.uuid4().hex
425+
security_group1 = json.loads(self.openstack(
426+
'security group create -f json ' + sg_name1
427+
))
428+
self.addCleanup(self.openstack, 'security group delete ' + sg_name1)
429+
sg_name2 = uuid.uuid4().hex
430+
security_group2 = json.loads(self.openstack(
431+
'security group create -f json ' + sg_name2
432+
))
433+
self.addCleanup(self.openstack, 'security group delete ' + sg_name2)
434+
435+
server_name = uuid.uuid4().hex
436+
server = json.loads(self.openstack(
437+
'server create -f json ' +
438+
'--flavor ' + self.flavor_name + ' ' +
439+
'--image ' + self.image_name + ' ' +
440+
# Security group id is integer in nova-network, convert to string
441+
'--security-group ' + str(security_group1['id']) + ' ' +
442+
'--security-group ' + security_group2['name'] + ' ' +
443+
self.network_arg + ' ' +
444+
server_name
445+
))
446+
self.addCleanup(self.openstack, 'server delete --wait ' + server_name)
447+
448+
self.assertIsNotNone(server['id'])
449+
self.assertEqual(server_name, server['name'])
450+
self.assertIn(str(security_group1['id']), server['security_groups'])
451+
self.assertIn(str(security_group2['id']), server['security_groups'])
452+
self.wait_for_status(server_name, 'ACTIVE')
453+
server = json.loads(self.openstack(
454+
'server show -f json ' + server_name
455+
))
456+
self.assertIn(sg_name1, server['security_groups'])
457+
self.assertIn(sg_name2, server['security_groups'])
458+
413459
def test_server_create_with_empty_network_option_latest(self):
414460
"""Test server create with empty network option in nova 2.latest."""
415461
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
@@ -24,6 +24,7 @@
2424
from openstackclient.compute.v2 import server
2525
from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes
2626
from openstackclient.tests.unit.image.v2 import fakes as image_fakes
27+
from openstackclient.tests.unit.network.v2 import fakes as network_fakes
2728
from openstackclient.tests.unit import utils
2829
from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes
2930

@@ -414,16 +415,24 @@ def test_server_create_with_options(self):
414415
# In base command class ShowOne in cliff, abstract method take_action()
415416
# returns a two-part tuple with a tuple of column names and a tuple of
416417
# data to be shown.
418+
fake_sg = network_fakes.FakeSecurityGroup.create_security_groups()
419+
mock_find_sg = (
420+
network_fakes.FakeSecurityGroup.get_security_groups(fake_sg)
421+
)
422+
self.app.client_manager.network.find_security_group = mock_find_sg
423+
417424
columns, data = self.cmd.take_action(parsed_args)
418425

426+
mock_find_sg.assert_called_once_with('securitygroup',
427+
ignore_missing=False)
419428
# Set expected values
420429
kwargs = dict(
421430
meta={'Beta': 'b'},
422431
files={},
423432
reservation_id=None,
424433
min_count=1,
425434
max_count=1,
426-
security_groups=['securitygroup'],
435+
security_groups=[fake_sg[0].id],
427436
userdata=None,
428437
key_name='keyname',
429438
availability_zone=None,
@@ -443,6 +452,92 @@ def test_server_create_with_options(self):
443452
self.assertEqual(self.columns, columns)
444453
self.assertEqual(self.datalist(), data)
445454

455+
def test_server_create_with_not_exist_security_group(self):
456+
arglist = [
457+
'--image', 'image1',
458+
'--flavor', 'flavor1',
459+
'--key-name', 'keyname',
460+
'--security-group', 'securitygroup',
461+
'--security-group', 'not_exist_sg',
462+
self.new_server.name,
463+
]
464+
verifylist = [
465+
('image', 'image1'),
466+
('flavor', 'flavor1'),
467+
('key_name', 'keyname'),
468+
('security_group', ['securitygroup', 'not_exist_sg']),
469+
('server_name', self.new_server.name),
470+
]
471+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
472+
473+
fake_sg = network_fakes.FakeSecurityGroup.create_security_groups(
474+
count=1)
475+
fake_sg.append(exceptions.NotFound(code=404))
476+
mock_find_sg = (
477+
network_fakes.FakeSecurityGroup.get_security_groups(fake_sg)
478+
)
479+
self.app.client_manager.network.find_security_group = mock_find_sg
480+
481+
self.assertRaises(exceptions.NotFound,
482+
self.cmd.take_action,
483+
parsed_args)
484+
mock_find_sg.assert_called_with('not_exist_sg',
485+
ignore_missing=False)
486+
487+
def test_server_create_with_security_group_in_nova_network(self):
488+
arglist = [
489+
'--image', 'image1',
490+
'--flavor', 'flavor1',
491+
'--key-name', 'keyname',
492+
'--security-group', 'securitygroup',
493+
self.new_server.name,
494+
]
495+
verifylist = [
496+
('image', 'image1'),
497+
('flavor', 'flavor1'),
498+
('key_name', 'keyname'),
499+
('security_group', ['securitygroup']),
500+
('server_name', self.new_server.name),
501+
]
502+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
503+
504+
with mock.patch.object(self.app.client_manager,
505+
'is_network_endpoint_enabled',
506+
return_value=False):
507+
with mock.patch.object(self.app.client_manager.compute.api,
508+
'security_group_find',
509+
return_value={'name': 'fake_sg'}
510+
) as mock_find:
511+
columns, data = self.cmd.take_action(parsed_args)
512+
mock_find.assert_called_once_with('securitygroup')
513+
514+
# Set expected values
515+
kwargs = dict(
516+
meta=None,
517+
files={},
518+
reservation_id=None,
519+
min_count=1,
520+
max_count=1,
521+
security_groups=['fake_sg'],
522+
userdata=None,
523+
key_name='keyname',
524+
availability_zone=None,
525+
block_device_mapping_v2=[],
526+
nics=[],
527+
scheduler_hints={},
528+
config_drive=None,
529+
)
530+
# ServerManager.create(name, image, flavor, **kwargs)
531+
self.servers_mock.create.assert_called_with(
532+
self.new_server.name,
533+
self.image,
534+
self.flavor,
535+
**kwargs
536+
)
537+
538+
self.assertEqual(self.columns, columns)
539+
self.assertEqual(self.datalist(), data)
540+
446541
def test_server_create_with_network(self):
447542
arglist = [
448543
'--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)