24c4b351ea
It seems that latest crio versions moved removal of network namespace to later in the container lifecycle. This means that sometimes the network namespace of a container will hang after pod is already gone from the API and Kuryr assigned it's VIF to another pod. This might cause VLAN ID conflicts, but normally that's not an issue as kuryr-daemon is removing interfaces from container namespace on CNI DEL. It may however happen that if kuryr-daemon was down when CNI DEL happened, the info about the VIF saved in KuryrPort will already be gone. In that case we simply returned success to the CNI without doing any unplugging. Now if the netns is not removed immediately after that we might end up with VLAN ID conflicts. This commit makes sure that even if VIF info is gone, kuryr-daemon will at least attempt to remove the container interface from the container netns. This should limit the problem. Change-Id: Ie7d4966473c83554786e79aea0d28a26de902a66 Closes-Bug: 1892388
178 lines
6.4 KiB
Python
178 lines
6.4 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 abc
|
|
import errno
|
|
|
|
import os_vif
|
|
from os_vif.objects import vif as osv_objects
|
|
from oslo_log import log as logging
|
|
import pyroute2
|
|
from stevedore import driver as stv_driver
|
|
|
|
from kuryr_kubernetes import config
|
|
from kuryr_kubernetes import constants
|
|
from kuryr_kubernetes import utils
|
|
|
|
_BINDING_NAMESPACE = 'kuryr_kubernetes.cni.binding'
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
class BaseBindingDriver(object, metaclass=abc.ABCMeta):
|
|
"""Interface to attach ports to pods."""
|
|
|
|
def _remove_ifaces(self, ipdb, ifnames, netns='host'):
|
|
"""Check if any of `ifnames` exists and remove it.
|
|
|
|
:param ipdb: ipdb of the network namespace to check
|
|
:param ifnames: iterable of interface names to remove
|
|
:param netns: network namespace name (used for logging)
|
|
"""
|
|
for ifname in ifnames:
|
|
if ifname in ipdb.interfaces:
|
|
LOG.warning('Found hanging interface %(ifname)s inside '
|
|
'%(netns)s netns. Most likely it is a leftover '
|
|
'from a kuryr-daemon restart. Trying to delete '
|
|
'it.', {'ifname': ifname, 'netns': netns})
|
|
with ipdb.interfaces[ifname] as iface:
|
|
iface.remove()
|
|
|
|
@abc.abstractmethod
|
|
def connect(self, vif, ifname, netns, container_id):
|
|
raise NotImplementedError()
|
|
|
|
@abc.abstractmethod
|
|
def disconnect(self, vif, ifname, netns, container_id):
|
|
raise NotImplementedError()
|
|
|
|
|
|
def _get_binding_driver(vif):
|
|
mgr = stv_driver.DriverManager(namespace=_BINDING_NAMESPACE,
|
|
name=type(vif).__name__,
|
|
invoke_on_load=True)
|
|
return mgr.driver
|
|
|
|
|
|
def get_ipdb(netns=None):
|
|
if netns:
|
|
netns = utils.convert_netns(netns)
|
|
ipdb = pyroute2.IPDB(nl=pyroute2.NetNS(netns))
|
|
else:
|
|
ipdb = pyroute2.IPDB()
|
|
return ipdb
|
|
|
|
|
|
def _enable_ipv6(netns):
|
|
# Docker disables IPv6 for --net=none containers
|
|
# TODO(apuimedo) remove when it is no longer the case
|
|
try:
|
|
netns = utils.convert_netns(netns)
|
|
path = utils.convert_netns('/proc/self/ns/net')
|
|
self_ns_fd = open(path)
|
|
pyroute2.netns.setns(netns)
|
|
path = utils.convert_netns('/proc/sys/net/ipv6/conf/all/disable_ipv6')
|
|
with open(path, 'w') as disable_ipv6:
|
|
disable_ipv6.write('0')
|
|
except Exception:
|
|
raise
|
|
finally:
|
|
pyroute2.netns.setns(self_ns_fd)
|
|
|
|
|
|
def _configure_l3(vif, ifname, netns, is_default_gateway):
|
|
with get_ipdb(netns) as ipdb:
|
|
with ipdb.interfaces[ifname] as iface:
|
|
for subnet in vif.network.subnets.objects:
|
|
if subnet.cidr.version == 6:
|
|
_enable_ipv6(netns)
|
|
for fip in subnet.ips.objects:
|
|
iface.add_ip('%s/%s' % (fip.address,
|
|
subnet.cidr.prefixlen))
|
|
|
|
routes = ipdb.routes
|
|
for subnet in vif.network.subnets.objects:
|
|
for route in subnet.routes.objects:
|
|
routes.add(gateway=str(route.gateway),
|
|
dst=str(route.cidr)).commit()
|
|
if is_default_gateway and hasattr(subnet, 'gateway'):
|
|
try:
|
|
routes.add(gateway=str(subnet.gateway),
|
|
dst='default').commit()
|
|
except pyroute2.NetlinkError as ex:
|
|
if ex.code != errno.EEXIST:
|
|
raise
|
|
LOG.debug("Default route already exists in pod for vif=%s."
|
|
" Did not overwrite with requested gateway=%s",
|
|
vif, subnet.gateway)
|
|
|
|
|
|
def _need_configure_l3(vif):
|
|
if isinstance(vif, osv_objects.VIFVHostUser):
|
|
return False
|
|
if not hasattr(vif, 'physnet'):
|
|
# NOTE(danil): non-sriov vif. Figure out if it is nested-dpdk
|
|
if vif.obj_attr_is_set('port_profile') and hasattr(vif.port_profile,
|
|
'l3_setup'):
|
|
return vif.port_profile.l3_setup
|
|
# NOTE(danil): by default kuryr-kubernetes has to setup l3
|
|
return True
|
|
# NOTE(danil): sriov vif. Figure out what driver should compute it
|
|
physnet = vif.physnet
|
|
mapping_res = config.CONF.sriov.physnet_resource_mappings
|
|
try:
|
|
resource = mapping_res[physnet]
|
|
except KeyError:
|
|
LOG.exception("No resource name for physnet %s", physnet)
|
|
raise
|
|
mapping_driver = config.CONF.sriov.resource_driver_mappings
|
|
try:
|
|
driver_name = mapping_driver[resource]
|
|
except KeyError:
|
|
LOG.exception("No driver for resource_name %s", resource)
|
|
raise
|
|
if driver_name in constants.USERSPACE_DRIVERS:
|
|
LOG.info("_configure_l3 will not be called for vif %s "
|
|
"because of it's driver", vif)
|
|
return False
|
|
# NOTE(danil): sriov vif computed by kernel driver
|
|
return True
|
|
|
|
|
|
def connect(vif, instance_info, ifname, netns=None, report_health=None,
|
|
is_default_gateway=True, container_id=None):
|
|
driver = _get_binding_driver(vif)
|
|
if report_health:
|
|
report_health(driver.is_alive())
|
|
os_vif.plug(vif, instance_info)
|
|
driver.connect(vif, ifname, netns, container_id)
|
|
if _need_configure_l3(vif):
|
|
_configure_l3(vif, ifname, netns, is_default_gateway)
|
|
|
|
|
|
def disconnect(vif, instance_info, ifname, netns=None, report_health=None,
|
|
container_id=None, **kwargs):
|
|
driver = _get_binding_driver(vif)
|
|
if report_health:
|
|
report_health(driver.is_alive())
|
|
driver.disconnect(vif, ifname, netns, container_id)
|
|
os_vif.unplug(vif, instance_info)
|
|
|
|
|
|
def cleanup(ifname, netns):
|
|
with get_ipdb(netns) as c_ipdb:
|
|
if ifname in c_ipdb.interfaces:
|
|
with c_ipdb.interfaces[ifname] as iface:
|
|
iface.remove()
|