Merge "Change to use iproute2 instead of brctl"
This commit is contained in:
commit
70c5c5f249
@ -52,16 +52,16 @@ def get_bridge_names():
|
|||||||
|
|
||||||
|
|
||||||
class BridgeDevice(ip_lib.IPDevice):
|
class BridgeDevice(ip_lib.IPDevice):
|
||||||
def _brctl(self, cmd):
|
def _ip_link(self, cmd):
|
||||||
cmd = ['brctl'] + cmd
|
cmd = ['ip', 'link'] + cmd
|
||||||
ip_wrapper = ip_lib.IPWrapper(self.namespace)
|
ip_wrapper = ip_lib.IPWrapper(self.namespace)
|
||||||
return ip_wrapper.netns.execute(cmd, run_as_root=True)
|
return ip_wrapper.netns.execute(cmd, run_as_root=True)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def addbr(cls, name, namespace=None):
|
def addbr(cls, name, namespace=None):
|
||||||
bridge = cls(name, namespace)
|
bridge = cls(name, namespace, 'bridge')
|
||||||
try:
|
try:
|
||||||
bridge._brctl(['addbr', bridge.name])
|
bridge.link.create()
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
with excutils.save_and_reraise_exception() as ectx:
|
with excutils.save_and_reraise_exception() as ectx:
|
||||||
ectx.reraise = not bridge.exists()
|
ectx.reraise = not bridge.exists()
|
||||||
@ -78,19 +78,21 @@ class BridgeDevice(ip_lib.IPDevice):
|
|||||||
return cls(name)
|
return cls(name)
|
||||||
|
|
||||||
def delbr(self):
|
def delbr(self):
|
||||||
return self._brctl(['delbr', self.name])
|
return self.link.delete()
|
||||||
|
|
||||||
def addif(self, interface):
|
def addif(self, interface):
|
||||||
return self._brctl(['addif', self.name, interface])
|
return self._ip_link(['set', 'dev', interface, 'master', self.name])
|
||||||
|
|
||||||
def delif(self, interface):
|
def delif(self, interface):
|
||||||
return self._brctl(['delif', self.name, interface])
|
return self._ip_link(['set', 'dev', interface, 'nomaster'])
|
||||||
|
|
||||||
def setfd(self, fd):
|
def setfd(self, fd):
|
||||||
return self._brctl(['setfd', self.name, str(fd)])
|
return self._ip_link(['set', 'dev', self.name, 'type', 'bridge',
|
||||||
|
'forward_delay', str(fd)])
|
||||||
|
|
||||||
def disable_stp(self):
|
def disable_stp(self):
|
||||||
return self._brctl(['stp', self.name, 'off'])
|
return self._ip_link(['set', 'dev', self.name, 'type', 'bridge',
|
||||||
|
'stp_state', 0])
|
||||||
|
|
||||||
def owns_interface(self, interface):
|
def owns_interface(self, interface):
|
||||||
return os.path.exists(
|
return os.path.exists(
|
||||||
|
@ -249,9 +249,10 @@ class IPWrapper(SubProcessBase):
|
|||||||
|
|
||||||
|
|
||||||
class IPDevice(SubProcessBase):
|
class IPDevice(SubProcessBase):
|
||||||
def __init__(self, name, namespace=None):
|
def __init__(self, name, namespace=None, kind='link'):
|
||||||
super(IPDevice, self).__init__(namespace=namespace)
|
super(IPDevice, self).__init__(namespace=namespace)
|
||||||
self._name = name
|
self._name = name
|
||||||
|
self.kind = kind
|
||||||
self.link = IpLinkCommand(self)
|
self.link = IpLinkCommand(self)
|
||||||
self.addr = IpAddrCommand(self)
|
self.addr = IpAddrCommand(self)
|
||||||
self.route = IpRouteCommand(self)
|
self.route = IpRouteCommand(self)
|
||||||
@ -493,6 +494,10 @@ class IpDeviceCommandBase(IpCommandBase):
|
|||||||
def name(self):
|
def name(self):
|
||||||
return self._parent.name
|
return self._parent.name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def kind(self):
|
||||||
|
return self._parent.kind
|
||||||
|
|
||||||
|
|
||||||
class IpLinkCommand(IpDeviceCommandBase):
|
class IpLinkCommand(IpDeviceCommandBase):
|
||||||
COMMAND = 'link'
|
COMMAND = 'link'
|
||||||
@ -536,6 +541,10 @@ class IpLinkCommand(IpDeviceCommandBase):
|
|||||||
privileged.set_link_attribute(
|
privileged.set_link_attribute(
|
||||||
self.name, self._parent.namespace, ifalias=alias_name)
|
self.name, self._parent.namespace, ifalias=alias_name)
|
||||||
|
|
||||||
|
def create(self):
|
||||||
|
privileged.create_interface(self.name, self._parent.namespace,
|
||||||
|
self.kind)
|
||||||
|
|
||||||
def delete(self):
|
def delete(self):
|
||||||
privileged.delete_interface(self.name, self._parent.namespace)
|
privileged.delete_interface(self.name, self._parent.namespace)
|
||||||
|
|
||||||
|
@ -90,6 +90,19 @@ class IpAddressAlreadyExists(RuntimeError):
|
|||||||
super(IpAddressAlreadyExists, self).__init__(message)
|
super(IpAddressAlreadyExists, self).__init__(message)
|
||||||
|
|
||||||
|
|
||||||
|
class InterfaceAlreadyExists(RuntimeError):
|
||||||
|
message = _("Interface %(device)s already exists.")
|
||||||
|
|
||||||
|
def __init__(self, message=None, device=None):
|
||||||
|
# NOTE(slaweq): 'message' can be passed as an optional argument
|
||||||
|
# because of how privsep daemon works. If exception is raised in
|
||||||
|
# function called by privsep daemon, it will then try to reraise it
|
||||||
|
# and will call it always with passing only message from originally
|
||||||
|
# raised exception.
|
||||||
|
message = message or self.message % {'device': device}
|
||||||
|
super(InterfaceAlreadyExists, self).__init__(message)
|
||||||
|
|
||||||
|
|
||||||
def _make_route_dict(destination, nexthop, device, scope):
|
def _make_route_dict(destination, nexthop, device, scope):
|
||||||
return {'destination': destination,
|
return {'destination': destination,
|
||||||
'nexthop': nexthop,
|
'nexthop': nexthop,
|
||||||
@ -276,6 +289,10 @@ def create_interface(ifname, namespace, kind, **kwargs):
|
|||||||
link_key = "vxlan_link" if kind == "vxlan" else "link"
|
link_key = "vxlan_link" if kind == "vxlan" else "link"
|
||||||
kwargs[link_key] = _get_link_id(physical_interface, namespace)
|
kwargs[link_key] = _get_link_id(physical_interface, namespace)
|
||||||
return ip.link("add", ifname=ifname, kind=kind, **kwargs)
|
return ip.link("add", ifname=ifname, kind=kind, **kwargs)
|
||||||
|
except NetlinkError as e:
|
||||||
|
if e.code == errno.EEXIST:
|
||||||
|
raise InterfaceAlreadyExists(device=ifname)
|
||||||
|
raise
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
if e.errno == errno.ENOENT:
|
if e.errno == errno.ENOENT:
|
||||||
raise NetworkNamespaceNotFound(netns_name=namespace)
|
raise NetworkNamespaceNotFound(netns_name=namespace)
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
import mock
|
import mock
|
||||||
|
|
||||||
from neutron.agent.linux import bridge_lib
|
from neutron.agent.linux import bridge_lib
|
||||||
|
from neutron.privileged.agent.linux import ip_lib as priv_lib
|
||||||
from neutron.tests import base
|
from neutron.tests import base
|
||||||
|
|
||||||
|
|
||||||
@ -34,6 +35,10 @@ class BridgeLibTest(base.BaseTestCase):
|
|||||||
return_value=True).start()
|
return_value=True).start()
|
||||||
ip_wrapper = mock.patch('neutron.agent.linux.ip_lib.IPWrapper').start()
|
ip_wrapper = mock.patch('neutron.agent.linux.ip_lib.IPWrapper').start()
|
||||||
self.execute = ip_wrapper.return_value.netns.execute
|
self.execute = ip_wrapper.return_value.netns.execute
|
||||||
|
self.create_p = mock.patch.object(priv_lib, 'create_interface')
|
||||||
|
self.create = self.create_p.start()
|
||||||
|
self.delete_p = mock.patch.object(priv_lib, 'delete_interface')
|
||||||
|
self.delete = self.delete_p.start()
|
||||||
|
|
||||||
def _verify_bridge_mock(self, cmd):
|
def _verify_bridge_mock(self, cmd):
|
||||||
self.execute.assert_called_once_with(cmd, run_as_root=True)
|
self.execute.assert_called_once_with(cmd, run_as_root=True)
|
||||||
@ -62,13 +67,16 @@ class BridgeLibTest(base.BaseTestCase):
|
|||||||
def _test_br(self, namespace=None):
|
def _test_br(self, namespace=None):
|
||||||
br = bridge_lib.BridgeDevice.addbr(self._BR_NAME, namespace)
|
br = bridge_lib.BridgeDevice.addbr(self._BR_NAME, namespace)
|
||||||
self.assertEqual(namespace, br.namespace)
|
self.assertEqual(namespace, br.namespace)
|
||||||
self._verify_bridge_mock(['brctl', 'addbr', self._BR_NAME])
|
self.create.assert_called_once_with(self._BR_NAME, br.namespace,
|
||||||
|
'bridge')
|
||||||
|
|
||||||
br.setfd(0)
|
br.setfd(0)
|
||||||
self._verify_bridge_mock(['brctl', 'setfd', self._BR_NAME, '0'])
|
self._verify_bridge_mock(['ip', 'link', 'set', 'dev', self._BR_NAME,
|
||||||
|
'type', 'bridge', 'forward_delay', '0'])
|
||||||
|
|
||||||
br.disable_stp()
|
br.disable_stp()
|
||||||
self._verify_bridge_mock(['brctl', 'stp', self._BR_NAME, 'off'])
|
self._verify_bridge_mock(['ip', 'link', 'set', 'dev', self._BR_NAME,
|
||||||
|
'type', 'bridge', 'stp_state', 0])
|
||||||
|
|
||||||
br.disable_ipv6()
|
br.disable_ipv6()
|
||||||
cmd = 'net.ipv6.conf.%s.disable_ipv6=1' % self._BR_NAME
|
cmd = 'net.ipv6.conf.%s.disable_ipv6=1' % self._BR_NAME
|
||||||
@ -76,14 +84,15 @@ class BridgeLibTest(base.BaseTestCase):
|
|||||||
|
|
||||||
br.addif(self._IF_NAME)
|
br.addif(self._IF_NAME)
|
||||||
self._verify_bridge_mock(
|
self._verify_bridge_mock(
|
||||||
['brctl', 'addif', self._BR_NAME, self._IF_NAME])
|
['ip', 'link', 'set', 'dev', self._IF_NAME,
|
||||||
|
'master', self._BR_NAME])
|
||||||
|
|
||||||
br.delif(self._IF_NAME)
|
br.delif(self._IF_NAME)
|
||||||
self._verify_bridge_mock(
|
self._verify_bridge_mock(
|
||||||
['brctl', 'delif', self._BR_NAME, self._IF_NAME])
|
['ip', 'link', 'set', 'dev', self._IF_NAME, 'nomaster'])
|
||||||
|
|
||||||
br.delbr()
|
br.delbr()
|
||||||
self._verify_bridge_mock(['brctl', 'delbr', self._BR_NAME])
|
self.delete.assert_called_once_with(self._BR_NAME, br.namespace)
|
||||||
|
|
||||||
def test_addbr_with_namespace(self):
|
def test_addbr_with_namespace(self):
|
||||||
self._test_br(self._NAMESPACE)
|
self._test_br(self._NAMESPACE)
|
||||||
@ -92,7 +101,7 @@ class BridgeLibTest(base.BaseTestCase):
|
|||||||
self._test_br()
|
self._test_br()
|
||||||
|
|
||||||
def test_addbr_exists(self):
|
def test_addbr_exists(self):
|
||||||
self.execute.side_effect = RuntimeError()
|
self.create.side_effect = RuntimeError()
|
||||||
with mock.patch.object(bridge_lib.BridgeDevice, 'exists',
|
with mock.patch.object(bridge_lib.BridgeDevice, 'exists',
|
||||||
return_value=True):
|
return_value=True):
|
||||||
bridge_lib.BridgeDevice.addbr(self._BR_NAME)
|
bridge_lib.BridgeDevice.addbr(self._BR_NAME)
|
||||||
|
@ -771,6 +771,12 @@ class TestIpLinkCommand(TestIPCmdBase):
|
|||||||
set_link_attribute.assert_called_once_with(
|
set_link_attribute.assert_called_once_with(
|
||||||
self.parent.name, self.parent.namespace, ifalias='openvswitch')
|
self.parent.name, self.parent.namespace, ifalias='openvswitch')
|
||||||
|
|
||||||
|
@mock.patch.object(priv_lib, 'create_interface')
|
||||||
|
def test_create(self, create):
|
||||||
|
self.link_cmd.create()
|
||||||
|
create.assert_called_once_with(self.parent.name, self.parent.namespace,
|
||||||
|
self.parent.kind)
|
||||||
|
|
||||||
@mock.patch.object(priv_lib, 'delete_interface')
|
@mock.patch.object(priv_lib, 'delete_interface')
|
||||||
def test_delete(self, delete):
|
def test_delete(self, delete):
|
||||||
self.link_cmd.delete()
|
self.link_cmd.delete()
|
||||||
|
Loading…
Reference in New Issue
Block a user