def assign_private_ip_addresses(context, network_interface_id, private_ip_address=None, secondary_private_ip_address_count=None, allow_reassignment=False): # TODO(Alex): allow_reassignment is not supported at the moment network_interface = ec2utils.get_db_item(context, network_interface_id) subnet = db_api.get_item_by_id(context, network_interface['subnet_id']) neutron = clients.neutron(context) os_subnet = neutron.show_subnet(subnet['os_id'])['subnet'] os_port = neutron.show_port(network_interface['os_id'])['port'] subnet_ipnet = netaddr.IPNetwork(os_subnet['cidr']) fixed_ips = os_port['fixed_ips'] or [] if private_ip_address is not None: for ip_address in private_ip_address: if netaddr.IPAddress(ip_address) not in subnet_ipnet: raise exception.InvalidParameterValue( value=str(ip_address), parameter='PrivateIpAddress', reason='IP address is out of the subnet range') fixed_ips.append({'ip_address': str(ip_address)}) elif secondary_private_ip_address_count > 0: for _i in range(secondary_private_ip_address_count): fixed_ips.append({'subnet_id': os_subnet['id']}) try: neutron.update_port(os_port['id'], {'port': {'fixed_ips': fixed_ips}}) except neutron_exception.IpAddressGenerationFailureClient: raise exception.InsufficientFreeAddressesInSubnet() except neutron_exception.IpAddressInUseClient: msg = _('Some of %(addresses)s is assigned, but move is not ' 'allowed.') % { 'addresses': private_ip_address } raise exception.InvalidParameterValue(msg) except neutron_exception.BadRequest as ex: # NOTE(ft):AWS returns PrivateIpAddressLimitExceeded, but Neutron does # general InvalidInput (converted to BadRequest) in the same case. msg = _('Specified network interface parameters are invalid. ' 'Reason: %(reason)s') % { 'reason': ex.message } raise exception.InvalidParameterValue(msg) return True
def create_network_interface(context, subnet_id, private_ip_address=None, private_ip_addresses=None, secondary_private_ip_address_count=None, description=None, security_group_id=None): subnet = ec2utils.get_db_item(context, subnet_id) if subnet is None: raise exception.InvalidSubnetIDNotFound(id=subnet_id) neutron = clients.neutron(context) os_subnet = neutron.show_subnet(subnet['os_id'])['subnet'] # NOTE(Alex): Combine and check ip addresses. Neutron will accept # ip_address as a parameter for specified address and subnet_id for # address to auto-allocate. # TODO(Alex): Implement better diagnostics. subnet_ipnet = netaddr.IPNetwork(os_subnet['cidr']) if not private_ip_addresses: private_ip_addresses = [] if private_ip_address is not None: private_ip_addresses.insert(0, { 'private_ip_address': private_ip_address, 'primary': True }) primary_ip = None fixed_ips = [] for ip in private_ip_addresses: ip_address = netaddr.IPAddress(ip['private_ip_address']) if ip_address not in subnet_ipnet: raise exception.InvalidParameterValue( value=str(ip_address), parameter='PrivateIpAddresses', reason='IP address is out of the subnet range') if ip.get('primary', False): if primary_ip is not None: raise exception.InvalidParameterValue( value=str(ip_address), parameter='PrivateIpAddresses', reason='More than one primary ip is supplied') else: primary_ip = str(ip_address) fixed_ips.insert(0, {'ip_address': primary_ip}) else: fixed_ips.append({'ip_address': str(ip_address)}) if not fixed_ips and not secondary_private_ip_address_count: secondary_private_ip_address_count = 1 if secondary_private_ip_address_count is None: secondary_private_ip_address_count = 0 if secondary_private_ip_address_count > 0: for _i in range(secondary_private_ip_address_count): fixed_ips.append({'subnet_id': os_subnet['id']}) vpc = db_api.get_item_by_id(context, subnet['vpc_id']) vpc_id = vpc['id'] dhcp_options_id = vpc.get('dhcp_options_id', None) if not security_group_id: default_groups = security_group_api.describe_security_groups( context, filter=[{ 'name': 'vpc-id', 'value': [vpc_id] }, { 'name': 'group-name', 'value': ['default'] }])['securityGroupInfo'] security_group_id = [ default_group['groupId'] for default_group in default_groups ] security_groups = db_api.get_items_by_ids(context, security_group_id) if any(security_group['vpc_id'] != vpc['id'] for security_group in security_groups): msg = _('You have specified two resources that belong to ' 'different networks.') raise exception.InvalidGroupNotFound(msg) os_groups = [security_group['os_id'] for security_group in security_groups] with common.OnCrashCleaner() as cleaner: os_port_body = { 'port': { 'network_id': os_subnet['network_id'], 'security_groups': os_groups } } os_port_body['port']['fixed_ips'] = fixed_ips try: os_port = neutron.create_port(os_port_body)['port'] except (neutron_exception.IpAddressGenerationFailureClient, neutron_exception.OverQuotaClient): raise exception.InsufficientFreeAddressesInSubnet() except (neutron_exception.IpAddressInUseClient, neutron_exception.BadRequest) as ex: # NOTE(ft): AWS returns InvalidIPAddress.InUse for a primary IP # address, but InvalidParameterValue for secondary one. # AWS returns PrivateIpAddressLimitExceeded, but Neutron does # general InvalidInput (converted to BadRequest) in the same case. msg = _('Specified network interface parameters are invalid. ' 'Reason: %(reason)s') % { 'reason': ex.message } raise exception.InvalidParameterValue(msg) cleaner.addCleanup(neutron.delete_port, os_port['id']) if primary_ip is None: primary_ip = os_port['fixed_ips'][0]['ip_address'] network_interface = db_api.add_item( context, 'eni', { 'os_id': os_port['id'], 'vpc_id': subnet['vpc_id'], 'subnet_id': subnet['id'], 'description': description, 'private_ip_address': primary_ip }) cleaner.addCleanup(db_api.delete_item, context, network_interface['id']) network_interface_id = network_interface['id'] neutron.update_port(os_port['id'], {'port': { 'name': network_interface_id }}) if dhcp_options_id: dhcp_options._add_dhcp_opts_to_port( context, db_api.get_item_by_id(context, dhcp_options_id), network_interface, os_port) security_groups = security_group_api._format_security_groups_ids_names( context) return { 'networkInterface': _format_network_interface(context, network_interface, os_port, security_groups=security_groups) }