Add support for native OVS binding

Currently we use ovs-vsctl command execution to bind pod infra container to
the ovs bridge. As a follow-up work, it should be changed to use ovs-lib
(as in [1]).

[1] https://github.com/openstack/neutron/blob/master/neutron/agent/common/ovs_lib.py

Change-Id: Ic50797c96f477ee6e71712ee2ab6b09dd721f558
Partially-Implements: blueprint kuryr-k8s-integration
This commit is contained in:
Irena Berezovsky 2016-12-18 14:36:21 +02:00
parent 7c03b7f290
commit 2c354b3cd0
6 changed files with 185 additions and 8 deletions

View File

@ -16,12 +16,12 @@
import os
from kuryr_kubernetes.cni.binding import base as b_base
from kuryr_kubernetes import linux_net_utils as net_utils
class BridgeDriver(object):
class BaseBridgeDriver(object):
def connect(self, vif, ifname, netns):
host_ifname = vif.vif_name
bridge_name = vif.bridge_name
c_ipdb = b_base.get_ipdb(netns)
h_ipdb = b_base.get_ipdb()
@ -40,6 +40,18 @@ class BridgeDriver(object):
h_iface.mtu = vif.network.mtu
h_iface.up()
def disconnect(self, vif, ifname, netns):
pass
class BridgeDriver(BaseBridgeDriver):
def connect(self, vif, ifname, netns):
super(BridgeDriver, self).connect(vif, ifname, netns)
host_ifname = vif.vif_name
bridge_name = vif.bridge_name
h_ipdb = b_base.get_ipdb()
with h_ipdb.interfaces[bridge_name] as h_br:
h_br.add_port(host_ifname)
@ -47,3 +59,17 @@ class BridgeDriver(object):
# NOTE(ivc): veth pair is destroyed automatically along with the
# container namespace
pass
class VIFOpenVSwitchDriver(BaseBridgeDriver):
def connect(self, vif, ifname, netns):
super(VIFOpenVSwitchDriver, self).connect(vif, ifname, netns)
#FIXME(irenab) use pod_id (neutron port device_id)
instance_id = 'kuryr'
net_utils.create_ovs_vif_port(vif.bridge_name, vif.vif_name,
vif.port_profile.interface_id,
vif.address, instance_id)
def disconnect(self, vif, ifname, netns):
super(VIFOpenVSwitchDriver, self).disconnect(vif, ifname, netns)
net_utils.delete_ovs_vif_port(vif.bridge_name, vif.vif_name)

View File

@ -0,0 +1,58 @@
# Derived from nova/network/linux_net.py
#
# Copyright (c) 2011 X.commerce, a business unit of eBay Inc.
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# 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.
""" Implements linux net utils"""
from kuryr.lib._i18n import _LE
from oslo_concurrency import processutils
from oslo_log import log as logging
LOG = logging.getLogger(__name__)
def _ovs_vsctl(args, timeout=None):
full_args = ['ovs-vsctl']
if timeout is not None:
full_args += ['--timeout=%s' % timeout]
full_args += args
try:
return processutils.execute(*full_args, run_as_root=True)
except Exception as e:
LOG.error(_LE("Unable to execute %(cmd)s. Exception: %(exception)s"),
{'cmd': full_args, 'exception': e})
raise
def _create_ovs_vif_cmd(bridge, dev, iface_id, mac, instance_id):
cmd = ['--', '--if-exists', 'del-port', dev, '--',
'add-port', bridge, dev,
'--', 'set', 'Interface', dev,
'external-ids:iface-id=%s' % iface_id,
'external-ids:iface-status=active',
'external-ids:attached-mac=%s' % mac,
'external-ids:vm-uuid=%s' % instance_id]
return cmd
def create_ovs_vif_port(bridge, dev, iface_id, mac, instance_id):
_ovs_vsctl(_create_ovs_vif_cmd(bridge, dev, iface_id, mac, instance_id))
def delete_ovs_vif_port(bridge, dev):
_ovs_vsctl(['--', '--if-exists', 'del-port', bridge, dev])

View File

@ -239,8 +239,17 @@ def neutron_to_osvif_vif_ovs(vif_plugin, neutron_port, subnets):
vif_name=_get_vif_name(neutron_port),
bridge_name=_get_ovs_hybrid_bridge_name(neutron_port))
else:
raise NotImplementedError(_LE(
"Non-hybrid OVS VIF is not supported yet"))
vif = osv_vif.VIFOpenVSwitch(
id=neutron_port['id'],
address=neutron_port['mac_address'],
network=network,
has_traffic_filtering=details.get('port_filter', False),
preserve_on_delete=False,
active=_is_port_active(neutron_port),
port_profile=profile,
plugin=vif_plugin,
vif_name=_get_vif_name(neutron_port),
bridge_name=network.bridge)
return vif

View File

@ -0,0 +1,61 @@
# 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 mock
from oslo_concurrency import processutils as utils
from kuryr_kubernetes import linux_net_utils as linux_net
from kuryr_kubernetes.tests import base as test_base
class LinuxNetworkUtilsTestCase(test_base.TestCase):
def test_ovs_vif_port_cmd(self):
expected = ['--', '--if-exists',
'del-port', 'fake-dev', '--', 'add-port',
'fake-bridge', 'fake-dev',
'--', 'set', 'Interface', 'fake-dev',
'external-ids:iface-id=fake-iface-id',
'external-ids:iface-status=active',
'external-ids:attached-mac=fake-mac',
'external-ids:vm-uuid=fake-instance-uuid']
cmd = linux_net._create_ovs_vif_cmd('fake-bridge', 'fake-dev',
'fake-iface-id', 'fake-mac',
'fake-instance-uuid')
self.assertEqual(expected, cmd)
def test_create_ovs_vif_port(self):
calls = [
mock.call('ovs-vsctl', '--', '--if-exists',
'del-port', 'fake-dev', '--', 'add-port',
'fake-bridge', 'fake-dev',
'--', 'set', 'Interface', 'fake-dev',
'external-ids:iface-id=fake-iface-id',
'external-ids:iface-status=active',
'external-ids:attached-mac=fake-mac',
'external-ids:vm-uuid=fake-instance-uuid',
run_as_root=True)]
with mock.patch.object(utils, 'execute', return_value=('', '')) as ex:
linux_net.create_ovs_vif_port('fake-bridge', 'fake-dev',
'fake-iface-id', 'fake-mac',
'fake-instance-uuid')
ex.assert_has_calls(calls)
def test_delete_ovs_vif_port(self):
calls = [
mock.call('ovs-vsctl', '--', '--if-exists',
'del-port', 'fake-bridge', 'fake-dev',
run_as_root=True)]
with mock.patch.object(utils, 'execute', return_value=('', '')) as ex:
linux_net.delete_ovs_vif_port('fake-bridge', 'fake-dev')
ex.assert_has_calls(calls)

View File

@ -215,16 +215,34 @@ class TestOSVIFUtils(test_base.TestCase):
vif_name=vif_name,
bridge_name=hybrid_bridge)
@mock.patch('kuryr_kubernetes.os_vif_util._get_vif_name')
@mock.patch('kuryr_kubernetes.os_vif_util._is_port_active')
@mock.patch('kuryr_kubernetes.os_vif_util._make_vif_network')
@mock.patch('os_vif.objects.vif.VIFOpenVSwitch')
@mock.patch('os_vif.objects.vif.VIFPortProfileOpenVSwitch')
def test_neutron_to_osvif_vif_ovs_native(self,
m_mk_profile,
m_make_vif_network):
m_mk_vif,
m_make_vif_network,
m_is_port_active,
m_get_vif_name):
vif_plugin = 'ovs'
port_id = mock.sentinel.port_id
mac_address = mock.sentinel.mac_address
ovs_bridge = mock.sentinel.ovs_bridge
subnets = mock.sentinel.subnets
port_profile = mock.sentinel.port_profile
network = mock.sentinel.network
port_active = mock.sentinel.port_active
vif_name = mock.sentinel.vif_name
vif = mock.sentinel.vif
m_mk_profile.return_value = port_profile
m_make_vif_network.return_value = network
m_is_port_active.return_value = port_active
m_get_vif_name.return_value = vif_name
m_mk_vif.return_value = vif
port = {'id': port_id,
'mac_address': mac_address,
'binding:vif_details': {
@ -232,9 +250,13 @@ class TestOSVIFUtils(test_base.TestCase):
'bridge_name': ovs_bridge},
}
# TODO(ivc): implement proper tests once ovs-native VIFs are supported
self.assertRaises(NotImplementedError, ovu.neutron_to_osvif_vif_ovs,
vif_plugin, port, subnets)
self.assertEqual(vif, ovu.neutron_to_osvif_vif_ovs(vif_plugin, port,
subnets))
m_mk_profile.assert_called_once_with(interface_id=port_id)
m_make_vif_network.assert_called_once_with(port, subnets)
m_is_port_active.assert_called_once_with(port)
m_get_vif_name.assert_called_once_with(port)
self.assertEqual(ovs_bridge, network.bridge)
def test_neutron_to_osvif_vif_ovs_no_bridge(self):
vif_plugin = 'ovs'

View File

@ -32,6 +32,7 @@ kuryr_kubernetes.vif_translators =
kuryr_kubernetes.cni.binding =
VIFBridge = kuryr_kubernetes.cni.binding.bridge:BridgeDriver
VIFOpenVSwitch = kuryr_kubernetes.cni.binding.bridge:VIFOpenVSwitchDriver
kuryr_kubernetes.controller.drivers.pod_project =
default = kuryr_kubernetes.controller.drivers.default_project:DefaultPodProjectDriver