net: explicitly set mac on linux bridge

The default behavior on a linux bridge is to use the lowest mac of
all attached interface. This isn't a problem when the bridge is first
set up, because the bridged interface is the only interface attached.
But, when instance interfaces are attached, there is a chance a lower
mac could be assigned and the bridge will switch macs. This patch
always sets the mac on the bridge to the mac of the bridged interface.

Change-Id: Ibf5333516a4c5487a3ee7c662e07028789bdd286
Closes-bug: #1488686
This commit is contained in:
Andrew Melton 2015-08-25 16:02:10 -07:00 committed by Matt Riedemann
parent 3f26a62007
commit 33fba48726
2 changed files with 28 additions and 6 deletions

View File

@ -24,6 +24,7 @@ import re
import time
import netaddr
import netifaces
from oslo_concurrency import processutils
from oslo_config import cfg
from oslo_log import log as logging
@ -1613,10 +1614,6 @@ class LinuxBridgeInterfaceDriver(LinuxNetInterfaceDriver):
_execute('brctl', 'setfd', bridge, 0, run_as_root=True)
# _execute('brctl setageing %s 10' % bridge, run_as_root=True)
_execute('brctl', 'stp', bridge, 'off', run_as_root=True)
# (danwent) bridge device MAC address can't be set directly.
# instead it inherits the MAC address of the first device on the
# bridge, which will either be the vlan interface, or a
# physical NIC.
_execute('ip', 'link', 'set', bridge, 'up', run_as_root=True)
if interface:
@ -1629,6 +1626,18 @@ class LinuxBridgeInterfaceDriver(LinuxNetInterfaceDriver):
msg = _('Failed to add interface: %s') % err
raise exception.NovaException(msg)
# NOTE(apmelton): Linux bridge's default behavior is to use the
# lowest mac of all plugged interfaces. This isn't a problem when
# it is first created and the only interface is the bridged
# interface. But, as instance interfaces are plugged, there is a
# chance for the mac to change. So, set it here so that it won't
# change in the future.
if not CONF.fake_network:
interface_addrs = netifaces.ifaddresses(interface)
interface_mac = interface_addrs[netifaces.AF_LINK][0]['addr']
_execute('ip', 'link', 'set', bridge, 'address', interface_mac,
run_as_root=True)
out, err = _execute('ip', 'link', 'set', interface, 'up',
check_exit_code=False, run_as_root=True)

View File

@ -21,6 +21,7 @@ import time
import mock
from mox3 import mox
import netifaces
from oslo_concurrency import processutils
from oslo_config import cfg
from oslo_log import log as logging
@ -1127,11 +1128,20 @@ class LinuxNetworkTestCase(test.NoDBTestCase):
self._test_add_metadata_forward_rule(expected)
def test_ensure_bridge_brings_up_interface(self):
# We have to bypass the CONF.fake_network check so that netifaces
# is actually called.
self.flags(fake_network=False)
fake_mac = 'aa:bb:cc:00:11:22'
fake_ifaces = {
netifaces.AF_LINK: [{'addr': fake_mac}]
}
calls = {
'device_exists': [mock.call('bridge')],
'_execute': [
mock.call('brctl', 'addif', 'bridge', 'eth0',
run_as_root=True, check_exit_code=False),
mock.call('ip', 'link', 'set', 'bridge', 'address', fake_mac,
run_as_root=True),
mock.call('ip', 'link', 'set', 'eth0', 'up',
run_as_root=True, check_exit_code=False),
mock.call('ip', 'route', 'show', 'dev', 'eth0'),
@ -1141,12 +1151,15 @@ class LinuxNetworkTestCase(test.NoDBTestCase):
}
with contextlib.nested(
mock.patch.object(linux_net, 'device_exists', return_value=True),
mock.patch.object(linux_net, '_execute', return_value=('', ''))
) as (device_exists, _execute):
mock.patch.object(linux_net, '_execute', return_value=('', '')),
mock.patch.object(netifaces, 'ifaddresses')
) as (device_exists, _execute, ifaddresses):
ifaddresses.return_value = fake_ifaces
driver = linux_net.LinuxBridgeInterfaceDriver()
driver.ensure_bridge('bridge', 'eth0')
device_exists.assert_has_calls(calls['device_exists'])
_execute.assert_has_calls(calls['_execute'])
ifaddresses.assert_called_once_with('eth0')
def test_ensure_bridge_brclt_addif_exception(self):
def fake_execute(*cmd, **kwargs):