Merge "Attempt to autodetect interface in nested setups"
This commit is contained in:
commit
58617a9b54
|
@ -16,6 +16,7 @@ import abc
|
|||
import errno
|
||||
|
||||
from oslo_log import log as logging
|
||||
import psutil
|
||||
import pyroute2
|
||||
|
||||
from kuryr_kubernetes.cni.binding import base as b_base
|
||||
|
@ -27,6 +28,7 @@ from kuryr_kubernetes import utils
|
|||
VLAN_KIND = 'vlan'
|
||||
MACVLAN_KIND = 'macvlan'
|
||||
MACVLAN_MODE_BRIDGE = 'bridge'
|
||||
KUBELET_PORT = 10250
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
@ -41,6 +43,44 @@ class NestedDriver(health.HealthHandler, b_base.BaseBindingDriver,
|
|||
def _get_iface_create_args(self, vif):
|
||||
raise NotImplementedError()
|
||||
|
||||
def _detect_iface_name(self, h_ipdb):
|
||||
# Let's try config first
|
||||
if config.CONF.binding.link_iface in h_ipdb.interfaces:
|
||||
LOG.debug(f'Using configured interface '
|
||||
f'{config.CONF.binding.link_iface} as bridge interface.')
|
||||
return config.CONF.binding.link_iface
|
||||
|
||||
# Then let's try choosing the one where kubelet listens to
|
||||
conns = [x for x in psutil.net_connections()
|
||||
if x.status == psutil.CONN_LISTEN
|
||||
and x.laddr.port == KUBELET_PORT]
|
||||
if len(conns) == 1:
|
||||
lookup_addr = conns[0].laddr.ip
|
||||
for name, iface in h_ipdb.interfaces.items():
|
||||
if type(name) is int: # Skip ones duplicated by id
|
||||
continue
|
||||
|
||||
for addr in iface['ipaddr']:
|
||||
if addr[0] == lookup_addr:
|
||||
LOG.debug(f'Using kubelet bind interface {name} as '
|
||||
f'bridge interface.')
|
||||
return name
|
||||
|
||||
# Alright, just try the first non-loopback interface
|
||||
for name, iface in h_ipdb.interfaces.items():
|
||||
if type(name) is int: # Skip ones duplicated by id
|
||||
continue
|
||||
|
||||
if iface['flags'] & pyroute2.netlink.rtnl.ifinfmsg.IFF_LOOPBACK:
|
||||
continue # Skip loopback
|
||||
|
||||
LOG.debug(f'Using interface {name} as bridge interface.')
|
||||
return name
|
||||
|
||||
raise exceptions.CNIBindingFailure('Cannot find bridge interface for '
|
||||
'nested driver to use. Please set '
|
||||
'[binding]link_iface option.')
|
||||
|
||||
def connect(self, vif, ifname, netns, container_id):
|
||||
# NOTE(vikasc): Ideally 'ifname' should be used here but instead a
|
||||
# temporary name is being used while creating the device for
|
||||
|
@ -67,7 +107,7 @@ class NestedDriver(health.HealthHandler, b_base.BaseBindingDriver,
|
|||
with b_base.get_ipdb() as h_ipdb:
|
||||
# TODO(vikasc): evaluate whether we should have stevedore
|
||||
# driver for getting the link device.
|
||||
vm_iface_name = config.CONF.binding.link_iface
|
||||
vm_iface_name = self._detect_iface_name(h_ipdb)
|
||||
mtu = h_ipdb.interfaces[vm_iface_name].mtu
|
||||
if mtu != vif.network.mtu:
|
||||
# NOTE(dulek): This might happen if Neutron and DHCP agent
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
# 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 collections
|
||||
import os
|
||||
from unittest import mock
|
||||
import uuid
|
||||
|
@ -23,6 +24,7 @@ from oslo_config import cfg
|
|||
from oslo_utils import uuidutils
|
||||
|
||||
from kuryr_kubernetes.cni.binding import base
|
||||
from kuryr_kubernetes.cni.binding import nested
|
||||
from kuryr_kubernetes.cni.binding import sriov
|
||||
from kuryr_kubernetes.cni.binding import vhostuser
|
||||
from kuryr_kubernetes import constants as k_const
|
||||
|
@ -180,6 +182,67 @@ class TestBridgeDriver(TestDriverMixin, test_base.TestCase):
|
|||
self._test_disconnect()
|
||||
|
||||
|
||||
class TestNestedDriver(TestDriverMixin, test_base.TestCase):
|
||||
def setUp(self):
|
||||
super(TestNestedDriver, self).setUp()
|
||||
ifaces = {
|
||||
'lo': {'flags': 0x8, 'ipaddr': (('127.0.0.1', 8),)},
|
||||
'first': {'flags': 0, 'ipaddr': (('192.168.0.1', 8),)},
|
||||
'kubelet': {'flags': 0, 'ipaddr': (('192.168.1.1', 8),)},
|
||||
'bridge': {'flags': 0, 'ipaddr': (('192.168.2.1', 8),)},
|
||||
}
|
||||
self.h_ipdb = mock.Mock(interfaces=ifaces)
|
||||
self.h_ipdb_loopback = mock.Mock(interfaces=ifaces)
|
||||
self.sconn = collections.namedtuple(
|
||||
'sconn', ['fd', 'family', 'type', 'laddr', 'raddr', 'status',
|
||||
'pid'])
|
||||
self.addr = collections.namedtuple('addr', ['ip', 'port'])
|
||||
|
||||
@mock.patch.multiple(nested.NestedDriver, __abstractmethods__=set())
|
||||
def test_detect_config(self):
|
||||
driver = nested.NestedDriver()
|
||||
self.addCleanup(CONF.clear_override, 'link_iface', group='binding')
|
||||
CONF.set_override('link_iface', 'bridge', group='binding')
|
||||
iface = driver._detect_iface_name(self.h_ipdb)
|
||||
self.assertEqual('bridge', iface)
|
||||
|
||||
@mock.patch.multiple(nested.NestedDriver, __abstractmethods__=set())
|
||||
@mock.patch('psutil.net_connections')
|
||||
def test_detect_kubelet_port(self, m_net_connections):
|
||||
driver = nested.NestedDriver()
|
||||
m_net_connections.return_value = [
|
||||
self.sconn(-1, 2, 2, laddr=self.addr(ip='192.168.1.1', port=53),
|
||||
raddr=(), status='LISTEN', pid=None),
|
||||
self.sconn(-1, 2, 2, laddr=self.addr(ip='192.168.1.1', port=10250),
|
||||
raddr=(), status='ESTABLISHED', pid=None),
|
||||
self.sconn(-1, 2, 2, laddr=self.addr(ip='192.168.1.1', port=10250),
|
||||
raddr=(), status='LISTEN', pid=None),
|
||||
]
|
||||
iface = driver._detect_iface_name(self.h_ipdb)
|
||||
self.assertEqual('kubelet', iface)
|
||||
|
||||
@mock.patch.multiple(nested.NestedDriver, __abstractmethods__=set())
|
||||
@mock.patch('psutil.net_connections')
|
||||
def test_detect_non_loopback(self, m_net_connections):
|
||||
driver = nested.NestedDriver()
|
||||
m_net_connections.return_value = []
|
||||
|
||||
iface = driver._detect_iface_name(self.h_ipdb)
|
||||
self.assertEqual('first', iface)
|
||||
|
||||
@mock.patch.multiple(nested.NestedDriver, __abstractmethods__=set())
|
||||
@mock.patch('psutil.net_connections')
|
||||
def test_detect_none(self, m_net_connections):
|
||||
driver = nested.NestedDriver()
|
||||
m_net_connections.return_value = []
|
||||
|
||||
self.h_ipdb.interfaces = {
|
||||
'lo': {'flags': 0x8, 'ipaddr': (('127.0.0.1', 8),)},
|
||||
}
|
||||
self.assertRaises(exceptions.CNIBindingFailure,
|
||||
driver._detect_iface_name, self.h_ipdb)
|
||||
|
||||
|
||||
class TestNestedVlanDriver(TestDriverMixin, test_base.TestCase):
|
||||
def setUp(self):
|
||||
super(TestNestedVlanDriver, self).setUp()
|
||||
|
|
Loading…
Reference in New Issue