b521fee8cb
Implements: blueprint selflink Change-Id: Ie98aae21ef910f11eaff5a6f6814d16ee1542ff2
461 lines
16 KiB
Python
461 lines
16 KiB
Python
# Copyright (c) 2016 Mirantis, Inc.
|
|
# All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
|
|
import os
|
|
|
|
from kuryr.lib._i18n import _
|
|
from kuryr.lib.binding.drivers import utils as kl_utils
|
|
from kuryr.lib import constants as kl_const
|
|
from os_vif.objects import fixed_ip as osv_fixed_ip
|
|
from os_vif.objects import network as osv_network
|
|
from os_vif.objects import route as osv_route
|
|
from os_vif.objects import subnet as osv_subnet
|
|
from os_vif.objects import vif as osv_vif
|
|
from oslo_config import cfg as oslo_cfg
|
|
from oslo_log import log as logging
|
|
from stevedore import driver as stv_driver
|
|
from vif_plug_ovs import constants as osv_const
|
|
|
|
from kuryr_kubernetes import config
|
|
from kuryr_kubernetes import constants as const
|
|
from kuryr_kubernetes import exceptions as k_exc
|
|
from kuryr_kubernetes.objects import vif as k_vif
|
|
from kuryr_kubernetes import utils
|
|
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
# REVISIT(ivc): consider making this module part of kuryr-lib
|
|
_VIF_TRANSLATOR_NAMESPACE = "kuryr_kubernetes.vif_translators"
|
|
_VIF_MANAGERS = {}
|
|
|
|
|
|
def neutron_to_osvif_network(os_network):
|
|
"""Converts Neutron network to os-vif Subnet.
|
|
|
|
:param os_network: openstack.network.v2.netwrork.Network object.
|
|
:return: an os-vif Network object
|
|
"""
|
|
|
|
obj = osv_network.Network(id=os_network.id)
|
|
|
|
if os_network.name is not None:
|
|
obj.label = os_network.name
|
|
|
|
if os_network.mtu is not None:
|
|
obj.mtu = os_network.mtu
|
|
|
|
# Vlan information will be used later in Sriov binding driver
|
|
if os_network.provider_network_type == 'vlan':
|
|
obj.should_provide_vlan = True
|
|
obj.vlan = os_network.provider_segmentation_id
|
|
|
|
return obj
|
|
|
|
|
|
def neutron_to_osvif_subnet(os_subnet):
|
|
"""Converts Neutron subnet to os-vif Subnet.
|
|
|
|
:param os_subnet: openstack.network.v2.subnet.Subnet object
|
|
:return: an os-vif Subnet object
|
|
"""
|
|
|
|
obj = osv_subnet.Subnet(
|
|
cidr=os_subnet.cidr,
|
|
dns=os_subnet.dns_nameservers,
|
|
routes=_neutron_to_osvif_routes(os_subnet.host_routes))
|
|
|
|
if os_subnet.gateway_ip is not None:
|
|
obj.gateway = os_subnet.gateway_ip
|
|
|
|
return obj
|
|
|
|
|
|
def _neutron_to_osvif_routes(neutron_routes):
|
|
"""Converts Neutron host_routes to os-vif RouteList.
|
|
|
|
:param neutron_routes: list of routes as returned by neutron client's
|
|
'show_subnet' in 'host_routes' attribute
|
|
:return: an os-vif RouteList object
|
|
"""
|
|
|
|
# NOTE(gryf): Nested attributes for OpenStackSDK objects are simple types,
|
|
# like dicts and lists, that's why neutron_routes is a list of dicts.
|
|
obj_list = [osv_route.Route(cidr=route['destination'],
|
|
gateway=route['nexthop'])
|
|
for route in neutron_routes]
|
|
|
|
return osv_route.RouteList(objects=obj_list)
|
|
|
|
|
|
def _make_vif_subnet(subnets, subnet_id):
|
|
"""Makes a copy of an os-vif Subnet from subnets mapping.
|
|
|
|
:param subnets: subnet mapping as returned by PodSubnetsDriver.get_subnets
|
|
:param subnet_id: ID of the subnet to extract from 'subnets' mapping
|
|
:return: a copy of an os-vif Subnet object matching 'subnet_id'
|
|
"""
|
|
|
|
network = subnets[subnet_id]
|
|
|
|
if len(network.subnets.objects) != 1:
|
|
raise k_exc.IntegrityError(_(
|
|
"Network object for subnet %(subnet_id)s is invalid, "
|
|
"must contain a single subnet, but %(num_subnets)s found") % {
|
|
'subnet_id': subnet_id,
|
|
'num_subnets': len(network.subnets.objects)})
|
|
|
|
subnet = network.subnets.objects[0].obj_clone()
|
|
subnet.ips = osv_fixed_ip.FixedIPList(objects=[])
|
|
return subnet
|
|
|
|
|
|
def _make_vif_subnets(neutron_port, subnets):
|
|
"""Gets a list of os-vif Subnet objects for port.
|
|
|
|
:param neutron_port: dict containing port information as returned by
|
|
neutron client's 'show_port' or
|
|
openstack.network.v2.port.Port object
|
|
:param subnets: subnet mapping as returned by PodSubnetsDriver.get_subnets
|
|
:return: list of os-vif Subnet object
|
|
"""
|
|
|
|
vif_subnets = {}
|
|
try:
|
|
fixed_ips = neutron_port.get('fixed_ips', [])
|
|
port_id = neutron_port.get('id')
|
|
except TypeError:
|
|
fixed_ips = neutron_port.fixed_ips
|
|
port_id = neutron_port.get.id
|
|
|
|
for neutron_fixed_ip in fixed_ips:
|
|
subnet_id = neutron_fixed_ip['subnet_id']
|
|
ip_address = neutron_fixed_ip['ip_address']
|
|
|
|
if subnet_id not in subnets:
|
|
continue
|
|
|
|
try:
|
|
subnet = vif_subnets[subnet_id]
|
|
except KeyError:
|
|
subnet = _make_vif_subnet(subnets, subnet_id)
|
|
vif_subnets[subnet_id] = subnet
|
|
|
|
subnet.ips.objects.append(osv_fixed_ip.FixedIP(address=ip_address))
|
|
|
|
if not vif_subnets:
|
|
raise k_exc.IntegrityError(_(
|
|
"No valid subnets found for port %(port_id)s") % {
|
|
'port_id': port_id})
|
|
|
|
return list(vif_subnets.values())
|
|
|
|
|
|
def _make_vif_network(neutron_port, subnets):
|
|
"""Get an os-vif Network object for port.
|
|
|
|
:param neutron_port: dict containing port information as returned by
|
|
neutron client's 'show_port', or
|
|
openstack.network.v2.port.Port object
|
|
:param subnets: subnet mapping as returned by PodSubnetsDriver.get_subnets
|
|
:return: os-vif Network object
|
|
"""
|
|
|
|
# NOTE(gryf): Because we didn't convert macvlan driver, neutron_port can
|
|
# be either a dict or an object
|
|
try:
|
|
network_id = neutron_port.get('network_id')
|
|
port_id = neutron_port.get('id')
|
|
except TypeError:
|
|
network_id = neutron_port.network_id
|
|
port_id = neutron_port.id
|
|
|
|
try:
|
|
network = next(net.obj_clone() for net in subnets.values()
|
|
if net.id == network_id)
|
|
except StopIteration:
|
|
raise k_exc.IntegrityError(_(
|
|
"Port %(port_id)s belongs to network %(network_id)s, "
|
|
"but requested networks are: %(requested_networks)s") % {
|
|
'port_id': port_id,
|
|
'network_id': network_id,
|
|
'requested_networks': [net.id for net in subnets.values()]})
|
|
|
|
network.subnets = osv_subnet.SubnetList(
|
|
objects=_make_vif_subnets(neutron_port, subnets))
|
|
|
|
return network
|
|
|
|
|
|
# TODO(a.perevalov) generalize it with get_veth_pair_names
|
|
# but it's reasonable if we're going to add vhostuser support
|
|
# into kuryr project
|
|
def _get_vhu_vif_name(port_id):
|
|
ifname = osv_const.OVS_VHOSTUSER_PREFIX + port_id
|
|
ifname = ifname[:kl_const.NIC_NAME_LEN]
|
|
return ifname
|
|
|
|
|
|
def _get_vif_name(neutron_port):
|
|
"""Gets a VIF device name for port.
|
|
|
|
:param neutron_port: dict containing port information as returned by
|
|
neutron client's 'show_port', or an port object
|
|
returned by openstack client.
|
|
"""
|
|
|
|
try:
|
|
port_id = neutron_port['id']
|
|
except TypeError:
|
|
port_id = neutron_port.id
|
|
|
|
vif_name, _ = kl_utils.get_veth_pair_names(port_id)
|
|
return vif_name
|
|
|
|
|
|
def _get_ovs_hybrid_bridge_name(os_port):
|
|
"""Gets a name of the Linux bridge name for hybrid OpenVSwitch port.
|
|
|
|
:param os_port: openstack.network.v2.port.Port object
|
|
"""
|
|
return ('qbr' + os_port.id)[:kl_const.NIC_NAME_LEN]
|
|
|
|
|
|
def _is_port_active(neutron_port):
|
|
"""Checks if port is active.
|
|
|
|
:param neutron_port: dict containing port information as returned by
|
|
neutron client's 'show_port' or
|
|
openstack.network.v2.port.Port object
|
|
"""
|
|
try:
|
|
return (neutron_port['status'] == kl_const.PORT_STATUS_ACTIVE)
|
|
except TypeError:
|
|
return (neutron_port.status == kl_const.PORT_STATUS_ACTIVE)
|
|
|
|
|
|
def neutron_to_osvif_vif_ovs(vif_plugin, os_port, subnets):
|
|
"""Converts Neutron port to VIF object for os-vif 'ovs' plugin.
|
|
|
|
:param vif_plugin: name of the os-vif plugin to use (i.e. 'ovs')
|
|
:param os_port: openstack.network.v2.port.Port object
|
|
:param subnets: subnet mapping as returned by PodSubnetsDriver.get_subnets
|
|
:return: os-vif VIF object
|
|
"""
|
|
profile = osv_vif.VIFPortProfileOpenVSwitch(interface_id=os_port.id)
|
|
|
|
details = os_port.binding_vif_details or {}
|
|
ovs_bridge = details.get('bridge_name',
|
|
config.CONF.neutron_defaults.ovs_bridge)
|
|
if not ovs_bridge:
|
|
raise oslo_cfg.RequiredOptError('ovs_bridge', 'neutron_defaults')
|
|
|
|
network = _make_vif_network(os_port, subnets)
|
|
network.bridge = ovs_bridge
|
|
vhostuser_mode = details.get('vhostuser_mode', False)
|
|
|
|
LOG.debug('Detected vhostuser_mode=%s for port %s', vhostuser_mode,
|
|
os_port.id)
|
|
if vhostuser_mode:
|
|
# TODO(a.perevalov) obtain path to mount point from pod's mountVolumes
|
|
vhostuser_mount_point = (config.CONF.vhostuser.mount_point)
|
|
if not vhostuser_mount_point:
|
|
raise oslo_cfg.RequiredOptError('vhostuser_mount_point',
|
|
'neutron_defaults')
|
|
vif_name = _get_vhu_vif_name(os_port.id)
|
|
vif = osv_vif.VIFVHostUser(
|
|
id=os_port.id,
|
|
address=os_port.mac_address,
|
|
network=network,
|
|
has_traffic_filtering=details.get('port_filter', False),
|
|
preserve_on_delete=False,
|
|
active=_is_port_active(os_port),
|
|
port_profile=profile,
|
|
plugin='ovs',
|
|
path=os.path.join(vhostuser_mount_point, vif_name),
|
|
mode=vhostuser_mode,
|
|
vif_name=vif_name,
|
|
bridge_name=network.bridge)
|
|
elif details.get('ovs_hybrid_plug'):
|
|
vif = osv_vif.VIFBridge(
|
|
id=os_port.id,
|
|
address=os_port.mac_address,
|
|
network=network,
|
|
has_traffic_filtering=details.get('port_filter', False),
|
|
preserve_on_delete=False,
|
|
active=_is_port_active(os_port),
|
|
port_profile=profile,
|
|
plugin=vif_plugin,
|
|
vif_name=_get_vif_name(os_port),
|
|
bridge_name=_get_ovs_hybrid_bridge_name(os_port))
|
|
else:
|
|
vif = osv_vif.VIFOpenVSwitch(
|
|
id=os_port.id,
|
|
address=os_port.mac_address,
|
|
network=network,
|
|
has_traffic_filtering=details.get('port_filter', False),
|
|
preserve_on_delete=False,
|
|
active=_is_port_active(os_port),
|
|
port_profile=profile,
|
|
plugin=vif_plugin,
|
|
vif_name=_get_vif_name(os_port),
|
|
bridge_name=network.bridge)
|
|
|
|
return vif
|
|
|
|
|
|
def neutron_to_osvif_vif_nested_vlan(os_port, subnets, vlan_id):
|
|
"""Converts Neutron port to VIF object for VLAN nested containers.
|
|
|
|
:param os_port: openstack.network.v2.port.Port object
|
|
:param subnets: subnet mapping as returned by PodSubnetsDriver.get_subnets
|
|
:param vlan_id: VLAN id associated to the VIF object for the pod
|
|
:return: kuryr-k8s native VIF object for VLAN nested
|
|
"""
|
|
details = os_port.binding_vif_details or {}
|
|
|
|
return k_vif.VIFVlanNested(
|
|
id=os_port.id,
|
|
address=os_port.mac_address,
|
|
network=_make_vif_network(os_port, subnets),
|
|
has_traffic_filtering=details.get('port_filter', False),
|
|
preserve_on_delete=False,
|
|
active=_is_port_active(os_port),
|
|
plugin=const.K8S_OS_VIF_NOOP_PLUGIN,
|
|
vif_name=_get_vif_name(os_port),
|
|
vlan_id=vlan_id)
|
|
|
|
|
|
def neutron_to_osvif_vif_nested_macvlan(neutron_port, subnets):
|
|
"""Converts Neutron port to VIF object for MACVLAN nested containers.
|
|
|
|
:param neutron_port: dict containing port information as returned by
|
|
neutron client's 'show_port'
|
|
:param subnets: subnet mapping as returned by PodSubnetsDriver.get_subnets
|
|
:return: kuryr-k8s native VIF object for MACVLAN nested
|
|
"""
|
|
details = neutron_port.get('binding:vif_details', {})
|
|
|
|
return k_vif.VIFMacvlanNested(
|
|
id=neutron_port['id'],
|
|
address=neutron_port['mac_address'],
|
|
network=_make_vif_network(neutron_port, subnets),
|
|
has_traffic_filtering=details.get('port_filter', False),
|
|
preserve_on_delete=False,
|
|
active=_is_port_active(neutron_port),
|
|
plugin=const.K8S_OS_VIF_NOOP_PLUGIN,
|
|
vif_name=_get_vif_name(neutron_port))
|
|
|
|
|
|
def neutron_to_osvif_vif_sriov(vif_plugin, os_port, subnets):
|
|
"""Converts Neutron port to VIF object for SRIOV containers.
|
|
|
|
:param vif_plugin: name of the os-vif plugin to use (i.e. 'noop')
|
|
:param os_port: openstack.network.v2.port.Port object
|
|
:param subnets: subnet mapping as returned by PodSubnetsDriver.get_subnets
|
|
:return: osv_vif VIFSriov object
|
|
"""
|
|
|
|
details = os_port.binding_vif_details or {}
|
|
network = _make_vif_network(os_port, subnets)
|
|
vlan_name = network.vlan if network.should_provide_vlan else ''
|
|
vif = k_vif.VIFSriov(
|
|
id=os_port.id,
|
|
address=os_port.mac_address,
|
|
network=network,
|
|
has_traffic_filtering=details.get('port_filter', False),
|
|
preserve_on_delete=False,
|
|
active=_is_port_active(os_port),
|
|
plugin=vif_plugin,
|
|
mode='passthrough',
|
|
vlan_name=vlan_name,
|
|
vif_name=_get_vif_name(os_port),
|
|
)
|
|
|
|
return vif
|
|
|
|
|
|
def neutron_to_osvif_vif_dpdk(os_port, subnets, pod):
|
|
"""Converts Neutron port to VIF object for nested dpdk containers.
|
|
|
|
:param os_port: dict containing port information as returned by
|
|
neutron client's 'show_port'
|
|
:param subnets: subnet mapping as returned by PodSubnetsDriver.get_subnets
|
|
:param pod: pod object received by k8s and containing profile details
|
|
:return: os-vif VIF object
|
|
"""
|
|
|
|
details = os_port.get('binding:vif_details', {})
|
|
profile = osv_vif.VIFPortProfileK8sDPDK(
|
|
l3_setup=False,
|
|
selflink=utils.get_res_link(pod))
|
|
|
|
return k_vif.VIFDPDKNested(
|
|
id=os_port['id'],
|
|
port_profile=profile,
|
|
address=os_port['mac_address'],
|
|
network=_make_vif_network(os_port, subnets),
|
|
has_traffic_filtering=details.get('port_filter', False),
|
|
preserve_on_delete=False,
|
|
active=_is_port_active(os_port),
|
|
plugin=const.K8S_OS_VIF_NOOP_PLUGIN,
|
|
pci_address="",
|
|
dev_driver="",
|
|
vif_name=_get_vif_name(os_port))
|
|
|
|
|
|
def neutron_to_osvif_vif(vif_translator, os_port, subnets):
|
|
"""Converts Neutron port to os-vif VIF object.
|
|
|
|
:param vif_translator: name of the traslator for the os-vif plugin to use
|
|
:param os_port: openstack.network.v2.port.Port object
|
|
:param subnets: subnet mapping as returned by PodSubnetsDriver.get_subnets
|
|
:return: os-vif VIF object
|
|
"""
|
|
try:
|
|
mgr = _VIF_MANAGERS[vif_translator]
|
|
except KeyError:
|
|
mgr = stv_driver.DriverManager(
|
|
namespace=_VIF_TRANSLATOR_NAMESPACE,
|
|
name=vif_translator, invoke_on_load=False)
|
|
_VIF_MANAGERS[vif_translator] = mgr
|
|
|
|
return mgr.driver(vif_translator, os_port, subnets)
|
|
|
|
|
|
def osvif_to_neutron_fixed_ips(subnets):
|
|
fixed_ips = []
|
|
|
|
for subnet_id, network in subnets.items():
|
|
ips = []
|
|
if len(network.subnets.objects) > 1:
|
|
raise k_exc.IntegrityError(_(
|
|
"Network object for subnet %(subnet_id)s is invalid, "
|
|
"must contain a single subnet, but %(num_subnets)s found") % {
|
|
'subnet_id': subnet_id,
|
|
'num_subnets': len(network.subnets.objects)})
|
|
|
|
for subnet in network.subnets.objects:
|
|
if subnet.obj_attr_is_set('ips'):
|
|
ips.extend([str(ip.address) for ip in subnet.ips.objects])
|
|
if ips:
|
|
fixed_ips.extend([{'subnet_id': subnet_id, 'ip_address': ip}
|
|
for ip in ips])
|
|
else:
|
|
fixed_ips.append({'subnet_id': subnet_id})
|
|
|
|
return fixed_ips
|