self.name = name
self.link = IpLinkCommand(self)
self.addr = IpAddrCommand(self)
+ self.route = IpRouteCommand(self)
def __eq__(self, other):
return (other is not None and self.name == other.name
def flush(self):
self._as_root('flush', self.name)
- def list(self, scope=None, to=None, filters=[]):
+ def list(self, scope=None, to=None, filters=None):
+ if filters is None:
+ filters = []
+
retval = []
if scope:
if parts[0] == 'inet6':
version = 6
scope = parts[3]
+ broadcast = '::'
else:
version = 4
+ broadcast = parts[3]
scope = parts[5]
retval.append(dict(cidr=parts[1],
+ broadcast=broadcast,
scope=scope,
ip_version=version,
dynamic=('dynamic' == parts[-1])))
return retval
+class IpRouteCommand(IpDeviceCommandBase):
+ COMMAND = 'route'
+
+ def add_gateway(self, gateway, metric=None):
+ args = ['add', 'default', 'via', gateway]
+ if metric:
+ args += ['metric', metric]
+ args += ['dev', self.name]
+ self._as_root(*args)
+
+ def delete_gateway(self, gateway):
+ self._as_root('del',
+ 'default',
+ 'via',
+ gateway,
+ 'dev',
+ self.name)
+
+ def get_gateway(self, scope=None, filters=None):
+ if filters is None:
+ filters = []
+
+ retval = None
+
+ if scope:
+ filters += ['scope', scope]
+
+ route_list_lines = self._run('list', 'dev', self.name,
+ *filters).split('\n')
+ default_route_line = next((x.strip() for x in
+ route_list_lines if
+ x.strip().startswith('default')), None)
+ if default_route_line:
+ gateway_index = 2
+ parts = default_route_line.split()
+ retval = dict(gateway=parts[gateway_index])
+ metric_index = 4
+ parts_has_metric = (len(parts) > metric_index)
+ if parts_has_metric:
+ retval.update(metric=int(parts[metric_index]))
+
+ return retval
+
+
class IpNetnsCommand(IpCommandBase):
COMMAND = 'netns'
import pyudev
from sqlalchemy.ext.sqlsoup import SqlSoup
+from quantum.agent.linux import ip_lib
from quantum.agent.linux import utils
from quantum.agent import rpc as agent_rpc
from quantum.common import config as logging_config
def __init__(self, interface_mappings, root_helper):
self.interface_mappings = interface_mappings
self.root_helper = root_helper
+ self.ip = ip_lib.IPWrapper(self.root_helper)
def device_exists(self, device):
"""Check if ethernet device exists."""
self.ensure_bridge(bridge_name, interface)
return interface
+ def get_interface_details(self, interface):
+ device = self.ip.device(interface)
+ ips = device.addr.list(scope='global')
+
+ # Update default gateway if necessary
+ gateway = device.route.get_gateway(scope='global')
+ return ips, gateway
+
def ensure_flat_bridge(self, network_id, physical_interface):
"""Create a non-vlan bridge unless it already exists."""
bridge_name = self.get_bridge_name(network_id)
- self.ensure_bridge(bridge_name, physical_interface)
+ ips, gateway = self.get_interface_details(physical_interface)
+ self.ensure_bridge(bridge_name, physical_interface, ips, gateway)
return physical_interface
def ensure_vlan(self, physical_interface, vlan_id):
LOG.debug("Done creating subinterface %s" % interface)
return interface
- def ensure_bridge(self, bridge_name, interface):
+ def update_interface_ip_details(self, destination, source, ips,
+ gateway):
+ if ips or gateway:
+ dst_device = self.ip.device(destination)
+ src_device = self.ip.device(source)
+
+ # Append IP's to bridge if necessary
+ for ip in ips:
+ dst_device.addr.add(ip_version=ip['ip_version'],
+ cidr=ip['cidr'],
+ broadcast=ip['broadcast'])
+
+ if gateway:
+ # Ensure that the gateway can be updated by changing the metric
+ metric = 100
+ if 'metric' in gateway:
+ metric = gateway['metric'] - 1
+ dst_device.route.add_gateway(gateway=gateway['gateway'],
+ metric=metric)
+ src_device.route.delete_gateway(gateway=gateway['gateway'])
+
+ # Remove IP's from interface
+ for ip in ips:
+ src_device.addr.delete(ip_version=ip['ip_version'],
+ cidr=ip['cidr'])
+
+ def ensure_bridge(self, bridge_name, interface, ips=None, gateway=None):
"""
Create a bridge unless it already exists.
"""
LOG.debug("Done starting bridge %s for subinterface %s" %
(bridge_name, interface))
+ # Update IP info if necessary
+ self.update_interface_ip_details(bridge_name, interface, ips, gateway)
+
# Check if the interface is part of the bridge
if not self.interface_exists_on_bridge(bridge_name, interface):
try:
for interface in interfaces_on_bridge:
self.remove_interface(bridge_name, interface)
for physical_interface in self.interface_mappings.itervalues():
- if interface.startswith(physical_interface):
- self.delete_vlan(interface)
+ if physical_interface == interface:
+ # This is a flat network => return IP's from bridge to
+ # interface
+ ips, gateway = self.get_interface_details(bridge_name)
+ self.update_interface_ip_details(interface,
+ bridge_name,
+ ips, gateway)
+ else:
+ if interface.startswith(physical_interface):
+ self.delete_vlan(interface)
LOG.debug("Deleting bridge %s" % bridge_name)
if utils.execute(['ip', 'link', 'set', bridge_name, 'down'],
LOG.debug("network_delete received")
network_id = kwargs.get('network_id')
bridge_name = self.linux_br.get_bridge_name(network_id)
- # (TODO) garyk delete the bridge interface
LOG.debug("Delete %s", bridge_name)
+ self.linux_br.delete_vlan_bridge(bridge_name)
def port_update(self, context, **kwargs):
LOG.debug("port_update received")
valid_lft forever preferred_lft forever
""")
+GATEWAY_SAMPLE1 = ("""
+default via 10.35.19.254 metric 100
+10.35.16.0/22 proto kernel scope link src 10.35.17.97
+""")
+
+GATEWAY_SAMPLE2 = ("""
+default via 10.35.19.254 metric 100
+""")
+
+GATEWAY_SAMPLE3 = ("""
+10.35.16.0/22 proto kernel scope link src 10.35.17.97
+""")
+
+GATEWAY_SAMPLE4 = ("""
+default via 10.35.19.254
+""")
+
class TestSubProcessBase(unittest.TestCase):
def setUp(self):
def test_list(self):
expected = [
dict(ip_version=4, scope='global',
- dynamic=False, cidr='172.16.77.240/24'),
+ dynamic=False, cidr='172.16.77.240/24',
+ broadcast='172.16.77.255'),
dict(ip_version=6, scope='global',
- dynamic=True, cidr='2001:470:9:1224:5595:dd51:6ba2:e788/64'),
+ dynamic=True, cidr='2001:470:9:1224:5595:dd51:6ba2:e788/64',
+ broadcast='::'),
dict(ip_version=6, scope='global',
- dynamic=True, cidr='2001:470:9:1224:fd91:272:581e:3a32/64'),
+ dynamic=True, cidr='2001:470:9:1224:fd91:272:581e:3a32/64',
+ broadcast='::'),
dict(ip_version=6, scope='global',
- dynamic=True, cidr='2001:470:9:1224:4508:b885:5fb:740b/64'),
+ dynamic=True, cidr='2001:470:9:1224:4508:b885:5fb:740b/64',
+ broadcast='::'),
dict(ip_version=6, scope='global',
- dynamic=True, cidr='2001:470:9:1224:dfcc:aaff:feb9:76ce/64'),
+ dynamic=True, cidr='2001:470:9:1224:dfcc:aaff:feb9:76ce/64',
+ broadcast='::'),
dict(ip_version=6, scope='link',
- dynamic=False, cidr='fe80::dfcc:aaff:feb9:76ce/64')]
+ dynamic=False, cidr='fe80::dfcc:aaff:feb9:76ce/64',
+ broadcast='::')]
self.parent._run = mock.Mock(return_value=ADDR_SAMPLE)
self.assertEquals(self.addr_cmd.list(), expected)
def test_list_filtered(self):
expected = [
dict(ip_version=4, scope='global',
- dynamic=False, cidr='172.16.77.240/24')]
+ dynamic=False, cidr='172.16.77.240/24',
+ broadcast='172.16.77.255')]
output = '\n'.join(ADDR_SAMPLE.split('\n')[0:4])
self.parent._run.return_value = output
self._assert_call([], ('show', 'tap0', 'permanent', 'scope', 'global'))
+class TestIpRouteCommand(TestIPCmdBase):
+ def setUp(self):
+ super(TestIpRouteCommand, self).setUp()
+ self.parent.name = 'eth0'
+ self.command = 'route'
+ self.route_cmd = ip_lib.IpRouteCommand(self.parent)
+
+ def test_add_gateway(self):
+ gateway = '192.168.45.100'
+ metric = 100
+ self.route_cmd.add_gateway(gateway, metric)
+ self._assert_sudo([],
+ ('add', 'default', 'via', gateway,
+ 'metric', metric,
+ 'dev', self.parent.name))
+
+ def test_del_gateway(self):
+ gateway = '192.168.45.100'
+ self.route_cmd.delete_gateway(gateway)
+ self._assert_sudo([],
+ ('del', 'default', 'via', gateway,
+ 'dev', self.parent.name))
+
+ def test_get_gateway(self):
+ test_cases = [{'sample': GATEWAY_SAMPLE1,
+ 'expected': {'gateway':'10.35.19.254',
+ 'metric': 100}},
+ {'sample': GATEWAY_SAMPLE2,
+ 'expected': {'gateway':'10.35.19.254',
+ 'metric': 100}},
+ {'sample': GATEWAY_SAMPLE3,
+ 'expected': None},
+ {'sample': GATEWAY_SAMPLE4,
+ 'expected': {'gateway': '10.35.19.254'}}]
+ for test_case in test_cases:
+ self.parent._run = mock.Mock(return_value=test_case['sample'])
+ self.assertEquals(self.route_cmd.get_gateway(),
+ test_case['expected'])
+
+
class TestIpNetnsCommand(TestIPCmdBase):
def setUp(self):
super(TestIpNetnsCommand, self).setUp()