Merge "Add support for native OVS binding"
This commit is contained in:
@@ -16,12 +16,12 @@
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
from kuryr_kubernetes.cni.binding import base as b_base
|
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):
|
def connect(self, vif, ifname, netns):
|
||||||
host_ifname = vif.vif_name
|
host_ifname = vif.vif_name
|
||||||
bridge_name = vif.bridge_name
|
|
||||||
|
|
||||||
c_ipdb = b_base.get_ipdb(netns)
|
c_ipdb = b_base.get_ipdb(netns)
|
||||||
h_ipdb = b_base.get_ipdb()
|
h_ipdb = b_base.get_ipdb()
|
||||||
@@ -40,6 +40,18 @@ class BridgeDriver(object):
|
|||||||
h_iface.mtu = vif.network.mtu
|
h_iface.mtu = vif.network.mtu
|
||||||
h_iface.up()
|
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:
|
with h_ipdb.interfaces[bridge_name] as h_br:
|
||||||
h_br.add_port(host_ifname)
|
h_br.add_port(host_ifname)
|
||||||
|
|
||||||
@@ -47,3 +59,17 @@ class BridgeDriver(object):
|
|||||||
# NOTE(ivc): veth pair is destroyed automatically along with the
|
# NOTE(ivc): veth pair is destroyed automatically along with the
|
||||||
# container namespace
|
# container namespace
|
||||||
pass
|
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)
|
||||||
|
58
kuryr_kubernetes/linux_net_utils.py
Normal file
58
kuryr_kubernetes/linux_net_utils.py
Normal 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])
|
@@ -239,8 +239,17 @@ def neutron_to_osvif_vif_ovs(vif_plugin, neutron_port, subnets):
|
|||||||
vif_name=_get_vif_name(neutron_port),
|
vif_name=_get_vif_name(neutron_port),
|
||||||
bridge_name=_get_ovs_hybrid_bridge_name(neutron_port))
|
bridge_name=_get_ovs_hybrid_bridge_name(neutron_port))
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError(_LE(
|
vif = osv_vif.VIFOpenVSwitch(
|
||||||
"Non-hybrid OVS VIF is not supported yet"))
|
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
|
return vif
|
||||||
|
|
||||||
|
61
kuryr_kubernetes/tests/unit/test_linux_net_utils.py
Normal file
61
kuryr_kubernetes/tests/unit/test_linux_net_utils.py
Normal 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)
|
@@ -215,16 +215,34 @@ class TestOSVIFUtils(test_base.TestCase):
|
|||||||
vif_name=vif_name,
|
vif_name=vif_name,
|
||||||
bridge_name=hybrid_bridge)
|
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('kuryr_kubernetes.os_vif_util._make_vif_network')
|
||||||
|
@mock.patch('os_vif.objects.vif.VIFOpenVSwitch')
|
||||||
@mock.patch('os_vif.objects.vif.VIFPortProfileOpenVSwitch')
|
@mock.patch('os_vif.objects.vif.VIFPortProfileOpenVSwitch')
|
||||||
def test_neutron_to_osvif_vif_ovs_native(self,
|
def test_neutron_to_osvif_vif_ovs_native(self,
|
||||||
m_mk_profile,
|
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'
|
vif_plugin = 'ovs'
|
||||||
port_id = mock.sentinel.port_id
|
port_id = mock.sentinel.port_id
|
||||||
mac_address = mock.sentinel.mac_address
|
mac_address = mock.sentinel.mac_address
|
||||||
ovs_bridge = mock.sentinel.ovs_bridge
|
ovs_bridge = mock.sentinel.ovs_bridge
|
||||||
subnets = mock.sentinel.subnets
|
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,
|
port = {'id': port_id,
|
||||||
'mac_address': mac_address,
|
'mac_address': mac_address,
|
||||||
'binding:vif_details': {
|
'binding:vif_details': {
|
||||||
@@ -232,9 +250,13 @@ class TestOSVIFUtils(test_base.TestCase):
|
|||||||
'bridge_name': ovs_bridge},
|
'bridge_name': ovs_bridge},
|
||||||
}
|
}
|
||||||
|
|
||||||
# TODO(ivc): implement proper tests once ovs-native VIFs are supported
|
self.assertEqual(vif, ovu.neutron_to_osvif_vif_ovs(vif_plugin, port,
|
||||||
self.assertRaises(NotImplementedError, ovu.neutron_to_osvif_vif_ovs,
|
subnets))
|
||||||
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):
|
def test_neutron_to_osvif_vif_ovs_no_bridge(self):
|
||||||
vif_plugin = 'ovs'
|
vif_plugin = 'ovs'
|
||||||
|
@@ -32,6 +32,7 @@ kuryr_kubernetes.vif_translators =
|
|||||||
|
|
||||||
kuryr_kubernetes.cni.binding =
|
kuryr_kubernetes.cni.binding =
|
||||||
VIFBridge = kuryr_kubernetes.cni.binding.bridge:BridgeDriver
|
VIFBridge = kuryr_kubernetes.cni.binding.bridge:BridgeDriver
|
||||||
|
VIFOpenVSwitch = kuryr_kubernetes.cni.binding.bridge:VIFOpenVSwitchDriver
|
||||||
|
|
||||||
kuryr_kubernetes.controller.drivers.pod_project =
|
kuryr_kubernetes.controller.drivers.pod_project =
|
||||||
default = kuryr_kubernetes.controller.drivers.default_project:DefaultPodProjectDriver
|
default = kuryr_kubernetes.controller.drivers.default_project:DefaultPodProjectDriver
|
||||||
|
Reference in New Issue
Block a user