Merge "Create server with security group ID and name"
This commit is contained in:
@@ -164,7 +164,7 @@ Create a new server
|
|||||||
|
|
||||||
Create server with this flavor (name or ID)
|
Create server with this flavor (name or ID)
|
||||||
|
|
||||||
.. option:: --security-group <security-group-name>
|
.. option:: --security-group <security-group>
|
||||||
|
|
||||||
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)
|
||||||
|
@@ -407,7 +407,7 @@ class CreateServer(command.ShowOne):
|
|||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--security-group',
|
'--security-group',
|
||||||
metavar='<security-group-name>',
|
metavar='<security-group>',
|
||||||
action='append',
|
action='append',
|
||||||
default=[],
|
default=[],
|
||||||
help=_('Security group to assign to this server (name or ID) '
|
help=_('Security group to assign to this server (name or ID) '
|
||||||
@@ -695,6 +695,22 @@ class CreateServer(command.ShowOne):
|
|||||||
# decide the default behavior.
|
# decide the default behavior.
|
||||||
nics = []
|
nics = []
|
||||||
|
|
||||||
|
# Check security group exist and convert ID to name
|
||||||
|
security_group_names = []
|
||||||
|
if self.app.client_manager.is_network_endpoint_enabled():
|
||||||
|
network_client = self.app.client_manager.network
|
||||||
|
for each_sg in parsed_args.security_group:
|
||||||
|
sg = network_client.find_security_group(each_sg,
|
||||||
|
ignore_missing=False)
|
||||||
|
# Use security group ID to avoid multiple security group have
|
||||||
|
# same name in neutron networking backend
|
||||||
|
security_group_names.append(sg.id)
|
||||||
|
else:
|
||||||
|
# Handle nova-network case
|
||||||
|
for each_sg in parsed_args.security_group:
|
||||||
|
sg = compute_client.api.security_group_find(each_sg)
|
||||||
|
security_group_names.append(sg['name'])
|
||||||
|
|
||||||
hints = {}
|
hints = {}
|
||||||
for hint in parsed_args.hint:
|
for hint in parsed_args.hint:
|
||||||
key, _sep, value = hint.partition('=')
|
key, _sep, value = hint.partition('=')
|
||||||
@@ -724,7 +740,7 @@ class CreateServer(command.ShowOne):
|
|||||||
reservation_id=None,
|
reservation_id=None,
|
||||||
min_count=parsed_args.min,
|
min_count=parsed_args.min,
|
||||||
max_count=parsed_args.max,
|
max_count=parsed_args.max,
|
||||||
security_groups=parsed_args.security_group,
|
security_groups=security_group_names,
|
||||||
userdata=userdata,
|
userdata=userdata,
|
||||||
key_name=parsed_args.key_name,
|
key_name=parsed_args.key_name,
|
||||||
availability_zone=parsed_args.availability_zone,
|
availability_zone=parsed_args.availability_zone,
|
||||||
|
@@ -517,6 +517,52 @@ class ServerTests(common.ComputeTestCase):
|
|||||||
self.assertIsNotNone(server['addresses'])
|
self.assertIsNotNone(server['addresses'])
|
||||||
self.assertEqual('', server['addresses'])
|
self.assertEqual('', server['addresses'])
|
||||||
|
|
||||||
|
def test_server_create_with_security_group(self):
|
||||||
|
"""Test server create with security group ID and name"""
|
||||||
|
if not self.haz_network:
|
||||||
|
# NOTE(dtroyer): As of Ocata release Nova forces nova-network to
|
||||||
|
# run in a cells v1 configuration. Security group
|
||||||
|
# and network functions currently do not work in
|
||||||
|
# the gate jobs so we have to skip this. It is
|
||||||
|
# known to work tested against a Mitaka nova-net
|
||||||
|
# DevStack without cells.
|
||||||
|
self.skipTest("No Network service present")
|
||||||
|
# Create two security group, use name and ID to create server
|
||||||
|
sg_name1 = uuid.uuid4().hex
|
||||||
|
security_group1 = json.loads(self.openstack(
|
||||||
|
'security group create -f json ' + sg_name1
|
||||||
|
))
|
||||||
|
self.addCleanup(self.openstack, 'security group delete ' + sg_name1)
|
||||||
|
sg_name2 = uuid.uuid4().hex
|
||||||
|
security_group2 = json.loads(self.openstack(
|
||||||
|
'security group create -f json ' + sg_name2
|
||||||
|
))
|
||||||
|
self.addCleanup(self.openstack, 'security group delete ' + sg_name2)
|
||||||
|
|
||||||
|
server_name = uuid.uuid4().hex
|
||||||
|
server = json.loads(self.openstack(
|
||||||
|
'server create -f json ' +
|
||||||
|
'--flavor ' + self.flavor_name + ' ' +
|
||||||
|
'--image ' + self.image_name + ' ' +
|
||||||
|
# Security group id is integer in nova-network, convert to string
|
||||||
|
'--security-group ' + str(security_group1['id']) + ' ' +
|
||||||
|
'--security-group ' + security_group2['name'] + ' ' +
|
||||||
|
self.network_arg + ' ' +
|
||||||
|
server_name
|
||||||
|
))
|
||||||
|
self.addCleanup(self.openstack, 'server delete --wait ' + server_name)
|
||||||
|
|
||||||
|
self.assertIsNotNone(server['id'])
|
||||||
|
self.assertEqual(server_name, server['name'])
|
||||||
|
self.assertIn(str(security_group1['id']), server['security_groups'])
|
||||||
|
self.assertIn(str(security_group2['id']), server['security_groups'])
|
||||||
|
self.wait_for_status(server_name, 'ACTIVE')
|
||||||
|
server = json.loads(self.openstack(
|
||||||
|
'server show -f json ' + server_name
|
||||||
|
))
|
||||||
|
self.assertIn(sg_name1, server['security_groups'])
|
||||||
|
self.assertIn(sg_name2, server['security_groups'])
|
||||||
|
|
||||||
def test_server_create_with_empty_network_option_latest(self):
|
def test_server_create_with_empty_network_option_latest(self):
|
||||||
"""Test server create with empty network option in nova 2.latest."""
|
"""Test server create with empty network option in nova 2.latest."""
|
||||||
server_name = uuid.uuid4().hex
|
server_name = uuid.uuid4().hex
|
||||||
|
@@ -25,6 +25,7 @@ from oslo_utils import timeutils
|
|||||||
from openstackclient.compute.v2 import server
|
from openstackclient.compute.v2 import server
|
||||||
from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes
|
from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes
|
||||||
from openstackclient.tests.unit.image.v2 import fakes as image_fakes
|
from openstackclient.tests.unit.image.v2 import fakes as image_fakes
|
||||||
|
from openstackclient.tests.unit.network.v2 import fakes as network_fakes
|
||||||
from openstackclient.tests.unit import utils
|
from openstackclient.tests.unit import utils
|
||||||
from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes
|
from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes
|
||||||
|
|
||||||
@@ -421,8 +422,16 @@ class TestServerCreate(TestServer):
|
|||||||
# In base command class ShowOne in cliff, abstract method take_action()
|
# In base command class ShowOne in cliff, abstract method take_action()
|
||||||
# returns a two-part tuple with a tuple of column names and a tuple of
|
# returns a two-part tuple with a tuple of column names and a tuple of
|
||||||
# data to be shown.
|
# data to be shown.
|
||||||
|
fake_sg = network_fakes.FakeSecurityGroup.create_security_groups()
|
||||||
|
mock_find_sg = (
|
||||||
|
network_fakes.FakeSecurityGroup.get_security_groups(fake_sg)
|
||||||
|
)
|
||||||
|
self.app.client_manager.network.find_security_group = mock_find_sg
|
||||||
|
|
||||||
columns, data = self.cmd.take_action(parsed_args)
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
|
|
||||||
|
mock_find_sg.assert_called_once_with('securitygroup',
|
||||||
|
ignore_missing=False)
|
||||||
# Set expected values
|
# Set expected values
|
||||||
kwargs = dict(
|
kwargs = dict(
|
||||||
meta={'Beta': 'b'},
|
meta={'Beta': 'b'},
|
||||||
@@ -430,7 +439,7 @@ class TestServerCreate(TestServer):
|
|||||||
reservation_id=None,
|
reservation_id=None,
|
||||||
min_count=1,
|
min_count=1,
|
||||||
max_count=1,
|
max_count=1,
|
||||||
security_groups=['securitygroup'],
|
security_groups=[fake_sg[0].id],
|
||||||
userdata=None,
|
userdata=None,
|
||||||
key_name='keyname',
|
key_name='keyname',
|
||||||
availability_zone=None,
|
availability_zone=None,
|
||||||
@@ -450,6 +459,92 @@ class TestServerCreate(TestServer):
|
|||||||
self.assertEqual(self.columns, columns)
|
self.assertEqual(self.columns, columns)
|
||||||
self.assertEqual(self.datalist(), data)
|
self.assertEqual(self.datalist(), data)
|
||||||
|
|
||||||
|
def test_server_create_with_not_exist_security_group(self):
|
||||||
|
arglist = [
|
||||||
|
'--image', 'image1',
|
||||||
|
'--flavor', 'flavor1',
|
||||||
|
'--key-name', 'keyname',
|
||||||
|
'--security-group', 'securitygroup',
|
||||||
|
'--security-group', 'not_exist_sg',
|
||||||
|
self.new_server.name,
|
||||||
|
]
|
||||||
|
verifylist = [
|
||||||
|
('image', 'image1'),
|
||||||
|
('flavor', 'flavor1'),
|
||||||
|
('key_name', 'keyname'),
|
||||||
|
('security_group', ['securitygroup', 'not_exist_sg']),
|
||||||
|
('server_name', self.new_server.name),
|
||||||
|
]
|
||||||
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
|
||||||
|
fake_sg = network_fakes.FakeSecurityGroup.create_security_groups(
|
||||||
|
count=1)
|
||||||
|
fake_sg.append(exceptions.NotFound(code=404))
|
||||||
|
mock_find_sg = (
|
||||||
|
network_fakes.FakeSecurityGroup.get_security_groups(fake_sg)
|
||||||
|
)
|
||||||
|
self.app.client_manager.network.find_security_group = mock_find_sg
|
||||||
|
|
||||||
|
self.assertRaises(exceptions.NotFound,
|
||||||
|
self.cmd.take_action,
|
||||||
|
parsed_args)
|
||||||
|
mock_find_sg.assert_called_with('not_exist_sg',
|
||||||
|
ignore_missing=False)
|
||||||
|
|
||||||
|
def test_server_create_with_security_group_in_nova_network(self):
|
||||||
|
arglist = [
|
||||||
|
'--image', 'image1',
|
||||||
|
'--flavor', 'flavor1',
|
||||||
|
'--key-name', 'keyname',
|
||||||
|
'--security-group', 'securitygroup',
|
||||||
|
self.new_server.name,
|
||||||
|
]
|
||||||
|
verifylist = [
|
||||||
|
('image', 'image1'),
|
||||||
|
('flavor', 'flavor1'),
|
||||||
|
('key_name', 'keyname'),
|
||||||
|
('security_group', ['securitygroup']),
|
||||||
|
('server_name', self.new_server.name),
|
||||||
|
]
|
||||||
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
|
||||||
|
with mock.patch.object(self.app.client_manager,
|
||||||
|
'is_network_endpoint_enabled',
|
||||||
|
return_value=False):
|
||||||
|
with mock.patch.object(self.app.client_manager.compute.api,
|
||||||
|
'security_group_find',
|
||||||
|
return_value={'name': 'fake_sg'}
|
||||||
|
) as mock_find:
|
||||||
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
|
mock_find.assert_called_once_with('securitygroup')
|
||||||
|
|
||||||
|
# Set expected values
|
||||||
|
kwargs = dict(
|
||||||
|
meta=None,
|
||||||
|
files={},
|
||||||
|
reservation_id=None,
|
||||||
|
min_count=1,
|
||||||
|
max_count=1,
|
||||||
|
security_groups=['fake_sg'],
|
||||||
|
userdata=None,
|
||||||
|
key_name='keyname',
|
||||||
|
availability_zone=None,
|
||||||
|
block_device_mapping_v2=[],
|
||||||
|
nics=[],
|
||||||
|
scheduler_hints={},
|
||||||
|
config_drive=None,
|
||||||
|
)
|
||||||
|
# ServerManager.create(name, image, flavor, **kwargs)
|
||||||
|
self.servers_mock.create.assert_called_with(
|
||||||
|
self.new_server.name,
|
||||||
|
self.image,
|
||||||
|
self.flavor,
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(self.columns, columns)
|
||||||
|
self.assertEqual(self.datalist(), data)
|
||||||
|
|
||||||
def test_server_create_with_network(self):
|
def test_server_create_with_network(self):
|
||||||
arglist = [
|
arglist = [
|
||||||
'--image', 'image1',
|
'--image', 'image1',
|
||||||
|
7
releasenotes/notes/bug-1687814-743ad8418923d5e3.yaml
Normal file
7
releasenotes/notes/bug-1687814-743ad8418923d5e3.yaml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
fixes:
|
||||||
|
- |
|
||||||
|
Allow security groups in ``server create`` command to be specified by name or ID with
|
||||||
|
the ``--security-group`` option. This also checks that the security group exist before
|
||||||
|
creating the server.
|
||||||
|
[Bug `1687814 <https://bugs.launchpad.net/python-openstackclient/+bug/1687814>`_]
|
Reference in New Issue
Block a user