add instance in default-vpc by security-group-name

or private-ip-address

add unit test

Change-Id: Iff5421ca84d5d5f6c2f87c9aed636ead6aadef2f
This commit is contained in:
tikitavi 2017-02-05 22:43:23 +03:00
parent ead353712e
commit df67b88930
3 changed files with 137 additions and 82 deletions

View File

@ -494,6 +494,14 @@ def set_check_and_create_default_vpc(check_and_create_default_vpc):
_check_and_create_default_vpc = check_and_create_default_vpc
def get_default_vpc(context):
check_and_create_default_vpc(context)
default_vpc = next((vpc for vpc in db_api.get_items(context, 'vpc')
if vpc.get('is_default')), None)
if not default_vpc:
raise exception.VPCIdNotSpecified()
return default_vpc
# NOTE(ft): following functions are copied from various parts of Nova
_ephemeral = re.compile('^ephemeral(\d|[1-9]\d+)$')

View File

@ -1158,17 +1158,12 @@ class InstanceEngineNeutron(object):
network_interface, multiple_instances):
# TODO(ft): support auto_assign_floating_ip
vpc_network_parameters = self.merge_network_interface_parameters(
security_group,
(security_group,
vpc_network_parameters) = self.merge_network_interface_parameters(
context, security_group,
subnet_id, private_ip_address, security_group_id,
network_interface)
if not vpc_network_parameters and CONF.disable_ec2_classic:
ec2utils.check_and_create_default_vpc(context)
subnet_id = self.get_default_subnet(context)
vpc_network_parameters = [{'device_index': 0,
'subnet_id': subnet_id}]
self.check_network_interface_parameters(vpc_network_parameters,
multiple_instances)
@ -1191,21 +1186,6 @@ class InstanceEngineNeutron(object):
return vpc_id, launch_context
def get_default_subnet(self, context):
default_vpc = next(
(vpc for vpc in db_api.get_items(context, 'vpc')
if vpc.get('is_default')), None)
if not default_vpc:
raise exception.VPCIdNotSpecified()
subnet = next(
(subnet for subnet in db_api.get_items(context, 'subnet')
if subnet['vpc_id'] == default_vpc['id']), None)
if not subnet:
raise exception.MissingInput(
_("No subnets found for the default VPC '%s'. "
"Please specify a subnet.") % default_vpc['id'])
return subnet['id']
def get_launch_extra_parameters(self, context, cleaner, launch_context):
if 'ec2_classic_nics' in launch_context:
nics = launch_context['ec2_classic_nics']
@ -1255,39 +1235,39 @@ class InstanceEngineNeutron(object):
return ec2_network_interfaces
def merge_network_interface_parameters(self,
context,
security_group_names,
subnet_id,
private_ip_address,
security_group_ids,
network_interfaces):
network_interfaces = network_interfaces or []
if ((subnet_id or private_ip_address or security_group_ids or
security_group_names) and
(len(network_interfaces) > 1 or
# NOTE(ft): the only case in AWS when simple subnet_id
# and/or private_ip_address parameters are compatible with
# network_interface parameter is default behavior change of
# public IP association for passed subnet_id by specifying
# the only element in network_interfaces:
# {"device_index": 0,
# "associate_public_ip_address": <boolean>}
# Both keys must be in the dict, and no other keys
# are allowed
# We should support such combination of parameters for
# compatibility purposes, even if we ignore
# associate_public_ip_address in all other code
len(network_interfaces) == 1 and
(len(network_interfaces[0]) != 2 or
('associate_public_ip_address' not in
network_interfaces[0]) or
network_interfaces[0].get('device_index') != 0))):
security_group_names) and network_interfaces):
msg = _(' Network interfaces and an instance-level subnet ID or '
'private IP address or security groups may not be '
'specified on the same request')
raise exception.InvalidParameterCombination(msg)
if subnet_id:
if network_interfaces:
if (CONF.disable_ec2_classic and
len(network_interfaces) == 1 and
# NOTE(tikitavi): the case in AWS CLI when security_group_ids
# and/or private_ip_address parameters are set with
# network_interface parameter having
# associate_public_ip_address setting
# private_ip_address and security_group_ids in that case
# go to network_interface parameter
'associate_public_ip_address' in network_interfaces[0] and
'device_index' in network_interfaces[0] and
network_interfaces[0]['device_index'] == 0 and
('subnet_id' not in network_interfaces[0] or
'network_interface_id' not in network_interfaces[0])):
subnet_id = self.get_default_subnet(context)['id']
network_interfaces[0]['subnet_id'] = subnet_id
return None, network_interfaces
elif subnet_id:
if security_group_names:
msg = _('The parameter groupName cannot be used with '
'the parameter subnet')
@ -1298,7 +1278,25 @@ class InstanceEngineNeutron(object):
param['private_ip_address'] = private_ip_address
if security_group_ids:
param['security_group_id'] = security_group_ids
return [param]
return None, [param]
elif CONF.disable_ec2_classic:
subnet_id = self.get_default_subnet(context)['id']
param = {'device_index': 0,
'subnet_id': subnet_id}
if security_group_ids or security_group_names:
security_group_id = security_group_ids or []
if security_group_names:
security_groups = (
security_group_api.describe_security_groups(
context, group_name=security_group_names)
['securityGroupInfo'])
security_group_id.extend(sg['groupId']
for sg in security_groups)
param['security_group_id'] = security_group_id
if private_ip_address:
param['private_ip_address'] = private_ip_address
return None, [param]
elif private_ip_address:
msg = _('Specifying an IP address is only valid for VPC instances '
'and thus requires a subnet in which to launch')
@ -1307,8 +1305,18 @@ class InstanceEngineNeutron(object):
msg = _('VPC security groups may not be used for a non-VPC launch')
raise exception.InvalidParameterCombination(msg)
else:
# NOTE(ft): only one of this variables is not empty
return network_interfaces
return security_group_names, []
def get_default_subnet(self, context):
default_vpc = ec2utils.get_default_vpc(context)
subnet = next(
(subnet for subnet in db_api.get_items(context, 'subnet')
if subnet['vpc_id'] == default_vpc['id']), None)
if not subnet:
raise exception.MissingInput(
_("No subnets found for the default VPC '%s'. "
"Please specify a subnet.") % default_vpc['id'])
return subnet
def check_network_interface_parameters(self, params, multiple_instances):
# NOTE(ft): we ignore associate_public_ip_address

View File

@ -20,7 +20,6 @@ import random
import mock
from novaclient import exceptions as nova_exception
from oslotest import base as test_base
import six
from ec2api.api import instance as instance_api
@ -1169,98 +1168,138 @@ class InstanceTestCase(base.ApiTestCase):
# TODO(ft): add tests for get_vpc_default_security_group_id,
class InstancePrivateTestCase(test_base.BaseTestCase):
class InstancePrivateTestCase(base.BaseTestCase):
def test_merge_network_interface_parameters(self):
fake_context = base.create_context()
engine = instance_api.InstanceEngineNeutron()
self.assertRaises(
exception.InvalidParameterCombination,
engine.merge_network_interface_parameters,
None, 'subnet-1', None, None,
fake_context, None, 'subnet-1', None, None,
[{'device_index': 0, 'private_ip_address': '10.10.10.10'}])
self.assertRaises(
exception.InvalidParameterCombination,
engine.merge_network_interface_parameters,
None, None, '10.10.10.10', None,
fake_context, None, None, '10.10.10.10', None,
[{'device_index': 0, 'subnet_id': 'subnet-1'}])
self.assertRaises(
exception.InvalidParameterCombination,
engine.merge_network_interface_parameters,
['default'], None, None, None,
fake_context, ['default'], None, None, None,
[{'device_index': 0, 'subnet_id': 'subnet-1'}])
self.assertRaises(
exception.InvalidParameterCombination,
engine.merge_network_interface_parameters,
None, None, None, ['sg-1'],
fake_context, None, None, None, ['sg-1'],
[{'device_index': 0, 'subnet_id': 'subnet-1'}])
self.assertRaises(
exception.InvalidParameterCombination,
engine.merge_network_interface_parameters,
None, 'subnet-1', None, None,
fake_context, None, 'subnet-1', None, None,
[{'device_index': 1, 'associate_public_ip_address': True}])
self.assertRaises(
exception.InvalidParameterCombination,
engine.merge_network_interface_parameters,
None, 'subnet-1', None, None,
fake_context, None, 'subnet-1', None, None,
[{'device_index': 0, 'associate_public_ip_address': True},
{'device_index': 1, 'subnet_id': 'subnet-2'}])
self.assertRaises(
exception.InvalidParameterCombination,
engine.merge_network_interface_parameters,
None, 'subnet-1', None, None,
fake_context, None, 'subnet-1', None, None,
[{'device_index': 0}])
self.assertRaises(
exception.InvalidParameterCombination,
engine.merge_network_interface_parameters,
['default'], 'subnet-1', None, None, None)
fake_context, ['default'], 'subnet-1', None, None, None)
self.assertRaises(
exception.InvalidParameterCombination,
engine.merge_network_interface_parameters,
None, None, '10.10.10.10', None, None)
fake_context, None, None, '10.10.10.10', None, None)
self.assertRaises(
exception.InvalidParameterCombination,
engine.merge_network_interface_parameters,
None, None, None, ['sg-1'], None)
fake_context, None, None, None, ['sg-1'], None)
self.assertEqual(
([{'device_index': 0,
'subnet_id': 'subnet-1'}]),
(None, [{'device_index': 0,
'subnet_id': 'subnet-1'}]),
engine.merge_network_interface_parameters(
None, 'subnet-1', None, None, None))
fake_context, None, 'subnet-1', None, None, None))
self.assertEqual(
([{'device_index': 0,
'subnet_id': 'subnet-1',
'private_ip_address': '10.10.10.10'}]),
(None, [{'device_index': 0,
'subnet_id': 'subnet-1',
'private_ip_address': '10.10.10.10'}]),
engine.merge_network_interface_parameters(
None, 'subnet-1', '10.10.10.10', None, None))
fake_context, None, 'subnet-1', '10.10.10.10', None, None))
self.assertEqual(
([{'device_index': 0,
'subnet_id': 'subnet-1',
'private_ip_address': '10.10.10.10',
'security_group_id': ['sg-1']}]),
(None, [{'device_index': 0,
'subnet_id': 'subnet-1',
'private_ip_address': '10.10.10.10',
'security_group_id': ['sg-1']}]),
engine.merge_network_interface_parameters(
None, 'subnet-1', '10.10.10.10', ['sg-1'], None))
fake_context, None, 'subnet-1', '10.10.10.10', ['sg-1'], None))
self.assertEqual(
([{'device_index': 0,
'subnet_id': 'subnet-1',
'security_group_id': ['sg-1']}]),
(None, [{'device_index': 0,
'subnet_id': 'subnet-1',
'security_group_id': ['sg-1']}]),
engine.merge_network_interface_parameters(
None, 'subnet-1', None, ['sg-1'], None))
fake_context, None, 'subnet-1', None, ['sg-1'], None))
self.assertEqual(
([{'device_index': 0,
'subnet_id': 'subnet-1'}]),
(None, [{'device_index': 0,
'subnet_id': 'subnet-1'}]),
engine.merge_network_interface_parameters(
None, None, None, None,
fake_context, None, None, None, None,
[{'device_index': 0, 'subnet_id': 'subnet-1'}]))
self.assertEqual([],
self.assertEqual((['default'], []),
engine.merge_network_interface_parameters(
['default'], None, None, None, None))
self.assertEqual([],
fake_context, ['default'], None, None, None,
None))
self.assertEqual((None, []),
engine.merge_network_interface_parameters(
None, None, None, None, None))
fake_context, None, None, None, None, None))
self.configure(disable_ec2_classic=True)
self.db_api = self.mock_db()
self.db_api.set_mock_items(fakes.DB_VPC_DEFAULT,
fakes.DB_SUBNET_DEFAULT)
self.assertEqual(
(None, [{'device_index': 0,
'subnet_id': fakes.ID_EC2_SUBNET_DEFAULT}]),
engine.merge_network_interface_parameters(
fake_context, None, None, None, None, None))
self.assertEqual(
(None, [{'device_index': 0,
'subnet_id': fakes.ID_EC2_SUBNET_DEFAULT,
'security_group_id': ['sg-id'],
'associate_public_ip_address': True}]),
engine.merge_network_interface_parameters(
fake_context, None, None, None, None,
[{'device_index': 0,
'associate_public_ip_address': True,
'security_group_id': ['sg-id']}]))
with mock.patch('ec2api.api.security_group.describe_security_groups'
) as describe_sg:
describe_sg.return_value = {
'securityGroupInfo': [{'groupId': 'sg-named-id'}]
}
self.assertEqual((None, [{'device_index': 0,
'subnet_id': fakes.ID_EC2_SUBNET_DEFAULT,
'security_group_id': ['sg-id',
'sg-named-id'],
'private_ip_address': 'private-ip'}]),
engine.merge_network_interface_parameters(
fake_context, ['sg-name'], None,
'private-ip', ['sg-id'], None))
describe_sg.assert_called_once_with(mock.ANY,
group_name=['sg-name'])
def test_check_network_interface_parameters(self):
engine = instance_api.InstanceEngineNeutron()