Merge pull request #73 from ryanpetrello/linux

Convert route management from BSD `route` to Linux `/sbin/ip`
This commit is contained in:
Rosario Di Somma 2014-08-07 17:06:28 -04:00
commit 195c0fc038
4 changed files with 233 additions and 227 deletions

View File

@ -16,6 +16,7 @@
import functools import functools
import logging
import re import re
import netaddr import netaddr
@ -23,6 +24,8 @@ import netaddr
from akanda.router import models from akanda.router import models
from akanda.router.drivers import base from akanda.router.drivers import base
LOG = logging.getLogger(__name__)
GENERIC_IFNAME = 'ge' GENERIC_IFNAME = 'ge'
PHYSICAL_INTERFACES = ['lo', 'eth', 'em', 're', 'en', 'vio', 'vtnet'] PHYSICAL_INTERFACES = ['lo', 'eth', 'em', 're', 'en', 'vio', 'vtnet']
@ -158,6 +161,141 @@ class IPManager(base.Manager):
self.update_interface(primary) self.update_interface(primary)
return ip_str return ip_str
def update_default_gateway(self, config):
# Track whether we have set the default gateways, by IP
# version.
gw_set = {
4: False,
6: False,
}
ifname = None
for net in config.networks:
if not net.is_external_network:
continue
ifname = net.interface.ifname
# The default v4 gateway is pulled out as a special case
# because we only want one but we might have multiple v4
# subnets on the external network. However, sometimes the RUG
# can't figure out what that value is, because it thinks we
# don't have any external IP addresses, yet. In that case, it
# doesn't give us a default.
if config.default_v4_gateway:
self._set_default_gateway(config.default_v4_gateway, ifname)
gw_set[4] = True
# Look through our networks and make sure we have a default
# gateway set for each IP version, if we have an IP for that
# version on the external net. If we haven't already set the
# v4 gateway, this picks the gateway for the first subnet we
# find, which might be wrong.
for net in config.networks:
if not net.is_external_network:
continue
for subnet in net.subnets:
if subnet.gateway_ip and not gw_set[subnet.gateway_ip.version]:
self._set_default_gateway(
subnet.gateway_ip,
net.interface.ifname
)
gw_set[subnet.gateway_ip.version] = True
def update_host_routes(self, config, cache):
db = cache.get_or_create('host_routes', lambda: {})
for net in config.networks:
# For each subnet...
for subnet in net.subnets:
cidr = str(subnet.cidr)
# determine the set of previously written routes for this cidr
if cidr not in db:
db[cidr] = set()
current = db[cidr]
# build a set of new routes for this cidr
latest = set()
for r in subnet.host_routes:
latest.add((r.destination, r.next_hop))
# If the set of previously written routes contains routes that
# aren't defined in the new config, run commands to delete them
for x in current - latest:
if self._alter_route(net.interface.ifname, 'del', *x):
current.remove(x)
# If the new config contains routes that aren't defined in the
# set of previously written routes, run commands to add them
for x in latest - current:
if self._alter_route(net.interface.ifname, 'add', *x):
current.add(x)
if not current:
del db[cidr]
cache.set('host_routes', db)
def _get_default_gateway(self, version):
current = None
try:
cmd_out = self.sudo('-%s' % version, 'route', 'show')
except:
# assume the route is missing and use defaults
pass
else:
for l in cmd_out.splitlines():
l = l.strip()
if l.startswith('default'):
match = re.search('via (?P<gateway>[^ ]+)', l)
if match:
return match.group('gateway')
return current
def _set_default_gateway(self, gateway_ip, ifname):
version = 4
if gateway_ip.version == 6:
version = 6
current = self._get_default_gateway(version)
desired = str(gateway_ip)
ifname = self.generic_to_host(ifname)
if current and current != desired:
# Remove the current gateway and add the desired one
self.sudo(
'-%s' % version, 'route', 'del', 'default', 'via', current,
'dev', ifname
)
return self.sudo(
'-%s' % version, 'route', 'add', 'default', 'via', desired,
'dev', ifname
)
if not current:
# Add the desired gateway
return self.sudo(
'-%s' % version, 'route', 'add', 'default', 'via', desired,
'dev', ifname
)
def _alter_route(self, ifname, action, destination, next_hop):
version = destination.version
ifname = self.generic_to_host(ifname)
try:
LOG.debug(self.sudo(
'-%s' % version, 'route', action, str(destination), 'via',
str(next_hop), 'dev', ifname
))
return True
except RuntimeError as e:
# Since these are user-supplied custom routes, it's very possible
# that adding/removing them will fail. A failure to apply one of
# these custom rules, however, should *not* cause an overall router
# failure.
LOG.warn('Route could not be %sed: %s' % (action, unicode(e)))
return False
def get_rug_address(): def get_rug_address():
""" Return the RUG address """ """ Return the RUG address """

View File

@ -1,148 +0,0 @@
# Copyright 2014 DreamHost, LLC
#
# Author: DreamHost, LLC
#
# 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 logging
from akanda.router.drivers import base
LOG = logging.getLogger(__name__)
class RouteManager(base.Manager):
EXECUTABLE = '/sbin/route'
def __init__(self, root_helper='sudo'):
super(RouteManager, self).__init__(root_helper)
def update_default(self, config):
# Track whether we have set the default gateways, by IP
# version.
gw_set = {
4: False,
6: False,
}
# The default v4 gateway is pulled out as a special case
# because we only want one but we might have multiple v4
# subnets on the external network. However, sometimes the RUG
# can't figure out what that value is, because it thinks we
# don't have any external IP addresses, yet. In that case, it
# doesn't give us a default.
if config.default_v4_gateway:
self._set_default_gateway(config.default_v4_gateway)
gw_set[4] = True
# Look through our networks and make sure we have a default
# gateway set for each IP version, if we have an IP for that
# version on the external net. If we haven't already set the
# v4 gateway, this picks the gateway for the first subnet we
# find, which might be wrong.
for net in config.networks:
if not net.is_external_network:
continue
for subnet in net.subnets:
if subnet.gateway_ip and not gw_set[subnet.gateway_ip.version]:
self._set_default_gateway(subnet.gateway_ip)
gw_set[subnet.gateway_ip.version] = True
def update_host_routes(self, config, cache):
db = cache.get_or_create('host_routes', lambda: {})
for net in config.networks:
# For each subnet...
for subnet in net.subnets:
cidr = str(subnet.cidr)
# determine the set of previously written routes for this cidr
if cidr not in db:
db[cidr] = set()
current = db[cidr]
# build a set of new routes for this cidr
latest = set()
for r in subnet.host_routes:
latest.add((r.destination, r.next_hop))
# If the set of previously written routes contains routes that
# aren't defined in the new config, run commands to delete them
for x in current - latest:
if self._alter_route('delete', *x):
current.remove(x)
# If the new config contains routes that aren't defined in the
# set of previously written routes, run commands to add them
for x in latest - current:
if self._alter_route('add', *x):
current.add(x)
if not current:
del db[cidr]
cache.set('host_routes', db)
def _get_default_gateway(self, version):
current = None
try:
cmd_out = self.sudo('-n', 'get', version, 'default')
except:
# assume the route is missing and use defaults
pass
else:
if 'no such process' in cmd_out.lower():
# There is no gateway
return None
for l in cmd_out.splitlines():
l = l.strip()
if l.startswith('gateway:'):
return l.partition(':')[-1].strip()
return current
def _set_default_gateway(self, gateway_ip):
version = '-inet'
if gateway_ip.version == 6:
version += '6'
current = self._get_default_gateway(version)
desired = str(gateway_ip)
if not current:
# Set the gateway
return self.sudo('add', version, 'default', desired)
if current != desired:
# Update the current gateway
return self.sudo('change', version, 'default', desired)
# Nothing to do
return ''
def _alter_route(self, action, destination, next_hop):
version = '-inet'
if destination.version == 6:
version += '6'
try:
LOG.debug(
self.sudo(action, version, str(destination), str(next_hop))
)
return True
except RuntimeError as e:
# Since these are user-supplied custom routes, it's very possible
# that adding/removing them will fail. A failure to apply one of
# these custom rules, however, should *not* cause an overall router
# failure.
LOG.warn('Route could not be %sed: %s' % (action, unicode(e)))
return False

View File

@ -19,8 +19,7 @@ import os
import re import re
from akanda.router import models from akanda.router import models
from akanda.router.drivers import (bird, dnsmasq, ip, metadata, pf, from akanda.router.drivers import (bird, dnsmasq, ip, metadata, pf, arp)
route, arp)
class Manager(object): class Manager(object):
@ -88,8 +87,8 @@ class Manager(object):
mgr.update_conf(rule_data) mgr.update_conf(rule_data)
def update_routes(self, cache): def update_routes(self, cache):
mgr = route.RouteManager() mgr = ip.IPManager()
mgr.update_default(self.config) mgr.update_default_gateway(self.config)
mgr.update_host_routes(self.config, cache) mgr.update_host_routes(self.config, cache)
def update_arp(self): def update_arp(self):

View File

@ -22,67 +22,52 @@ import netaddr
from dogpile.cache import make_region from dogpile.cache import make_region
from akanda.router import models from akanda.router import models
from akanda.router.drivers import route from akanda.router.drivers import ip
class RouteTest(unittest2.TestCase): class RouteTest(unittest2.TestCase):
def setUp(self): def setUp(self):
self.mgr = route.RouteManager() super(RouteTest, self).setUp()
self.mgr = ip.IPManager()
self.host_patch = mock.patch.object(
self.mgr, 'generic_to_host', lambda x: x.replace('ge', 'eth')
)
self.host_patch.start()
def tearDown(self):
super(RouteTest, self).tearDown()
self.host_patch.stop()
def test_get_default_gateway_v6_missing(self): def test_get_default_gateway_v6_missing(self):
output = 'route: writing to routing socket: No such process\n' output = ''
with mock.patch.object(self.mgr, 'sudo') as sudo: with mock.patch.object(self.mgr, 'sudo') as sudo:
sudo.return_value = output sudo.return_value = output
self.assertEqual( self.assertEqual(
None, None,
self.mgr._get_default_gateway('-inet6') self.mgr._get_default_gateway(6)
) )
sudo.assert_called_with('-n', 'get', '-inet6', 'default') sudo.assert_called_with('-6', 'route', 'show')
def test_get_default_gateway_v6(self): def test_get_default_gateway_v6(self):
output = """ output = "default via fe80::f816:3eff:fe33:deac dev eth2 metric 1024"
route to: ::
destination: ::
mask: default
gateway: fdee:9f85:83be::1
interface: vio1
if address: fdee:9f85:83be:0:f816:3eff:fe7b:6263
priority: 8 (static)
flags: <UP,GATEWAY,DONE,STATIC>
use mtu expire
0 0 0
"""
with mock.patch.object(self.mgr, 'sudo') as sudo: with mock.patch.object(self.mgr, 'sudo') as sudo:
sudo.return_value = output sudo.return_value = output
self.assertEqual( self.assertEqual(
'fdee:9f85:83be::1', 'fe80::f816:3eff:fe33:deac',
self.mgr._get_default_gateway('-inet6') self.mgr._get_default_gateway(6)
) )
sudo.assert_called_with('-n', 'get', '-inet6', 'default') sudo.assert_called_with('-6', 'route', 'show')
def test_get_default_gateway_v4(self): def test_get_default_gateway_v4(self):
output = """ output = "default via 192.168.122.1 dev eth0 metric 100"
route to: default
destination: default
mask: default
gateway: 192.168.122.1
interface: vio0
if address: 192.168.122.240
priority: 8 (static)
flags: <UP,GATEWAY,DONE,STATIC>
label: DHCLIENT 20978
use mtu expire
73687 0 0
sockaddrs: <DST,GATEWAY,NETMASK,IFP,IFA,LABEL>
"""
with mock.patch.object(self.mgr, 'sudo') as sudo: with mock.patch.object(self.mgr, 'sudo') as sudo:
sudo.return_value = output sudo.return_value = output
self.assertEqual( self.assertEqual(
'192.168.122.1', '192.168.122.1',
self.mgr._get_default_gateway('-inet') self.mgr._get_default_gateway(4)
) )
sudo.assert_called_with('-n', 'get', '-inet', 'default') sudo.assert_called_with('-4', 'route', 'show')
def test_set_default_v4_matches_current(self): def test_set_default_v4_matches_current(self):
ip_s = '192.168.122.1' ip_s = '192.168.122.1'
@ -93,7 +78,7 @@ sockaddrs: <DST,GATEWAY,NETMASK,IFP,IFA,LABEL>
get.return_value = ip_s get.return_value = ip_s
with mock.patch.object(self.mgr, 'sudo') as sudo: with mock.patch.object(self.mgr, 'sudo') as sudo:
sudo.side_effect = AssertionError('should not be called') sudo.side_effect = AssertionError('should not be called')
self.mgr._set_default_gateway(ip) self.mgr._set_default_gateway(ip, 'ge1')
def test_set_default_v4_changes_current(self): def test_set_default_v4_changes_current(self):
ip_s = '192.168.122.1' ip_s = '192.168.122.1'
@ -101,10 +86,19 @@ sockaddrs: <DST,GATEWAY,NETMASK,IFP,IFA,LABEL>
ip.version = 4 ip.version = 4
ip.__str__.return_value = ip_s ip.__str__.return_value = ip_s
with mock.patch.object(self.mgr, '_get_default_gateway') as get: with mock.patch.object(self.mgr, '_get_default_gateway') as get:
get.return_value = '192.168.122.254' get.return_value = '192.168.122.254'
with mock.patch.object(self.mgr, 'sudo') as sudo: with mock.patch.object(self.mgr, 'sudo') as sudo:
self.mgr._set_default_gateway(ip) self.mgr._set_default_gateway(ip, 'ge1')
sudo.assert_called_with('change', '-inet', 'default', ip_s) assert sudo.call_args_list == [
mock.call(
'-4', 'route', 'del', 'default', 'via',
get.return_value, 'dev', 'eth1'
),
mock.call(
'-4', 'route', 'add', 'default', 'via', ip_s,
'dev', 'eth1'
)
]
def test_set_default_v4_no_current(self): def test_set_default_v4_no_current(self):
ip_s = '192.168.122.1' ip_s = '192.168.122.1'
@ -114,8 +108,11 @@ sockaddrs: <DST,GATEWAY,NETMASK,IFP,IFA,LABEL>
with mock.patch.object(self.mgr, '_get_default_gateway') as get: with mock.patch.object(self.mgr, '_get_default_gateway') as get:
get.return_value = None get.return_value = None
with mock.patch.object(self.mgr, 'sudo') as sudo: with mock.patch.object(self.mgr, 'sudo') as sudo:
self.mgr._set_default_gateway(ip) self.mgr._set_default_gateway(ip, 'ge1')
sudo.assert_called_with('add', '-inet', 'default', ip_s) sudo.assert_called_with(
'-4', 'route', 'add', 'default', 'via', '192.168.122.1',
'dev', 'eth1'
)
def test_set_default_v6_matches_current(self): def test_set_default_v6_matches_current(self):
ip_s = 'fe80::5054:ff:fee2:1d4f' ip_s = 'fe80::5054:ff:fee2:1d4f'
@ -126,7 +123,7 @@ sockaddrs: <DST,GATEWAY,NETMASK,IFP,IFA,LABEL>
get.return_value = ip_s get.return_value = ip_s
with mock.patch.object(self.mgr, 'sudo') as sudo: with mock.patch.object(self.mgr, 'sudo') as sudo:
sudo.side_effect = AssertionError('should not be called') sudo.side_effect = AssertionError('should not be called')
self.mgr._set_default_gateway(ip) self.mgr._set_default_gateway(ip, 'ge1')
def test_set_default_v6_changes_current(self): def test_set_default_v6_changes_current(self):
ip_s = 'fe80::5054:ff:fee2:1d4f' ip_s = 'fe80::5054:ff:fee2:1d4f'
@ -136,19 +133,32 @@ sockaddrs: <DST,GATEWAY,NETMASK,IFP,IFA,LABEL>
with mock.patch.object(self.mgr, '_get_default_gateway') as get: with mock.patch.object(self.mgr, '_get_default_gateway') as get:
get.return_value = 'fe80::5054:ff:fee2:aaaa' get.return_value = 'fe80::5054:ff:fee2:aaaa'
with mock.patch.object(self.mgr, 'sudo') as sudo: with mock.patch.object(self.mgr, 'sudo') as sudo:
self.mgr._set_default_gateway(ip) self.mgr._set_default_gateway(ip, 'ge1')
sudo.assert_called_with('change', '-inet6', 'default', ip_s) assert sudo.call_args_list == [
mock.call(
'-6', 'route', 'del', 'default', 'via',
get.return_value, 'dev', 'eth1'
),
mock.call(
'-6', 'route', 'add', 'default', 'via', ip_s,
'dev', 'eth1'
)
]
def test_set_default_v6_no_current(self): def test_set_default_v6_no_current(self):
ip_s = 'fe80::5054:ff:fee2:1d4f' ip_s = 'fe80::5054:ff:fee2:1d4f'
ip = mock.MagicMock() ip = mock.MagicMock()
ip.version = 6 ip.version = 6
ip.__str__.return_value = ip_s ip.__str__.return_value = ip_s
self.mgr.generic_mapping = {'ge1', 'eth1'}
with mock.patch.object(self.mgr, '_get_default_gateway') as get: with mock.patch.object(self.mgr, '_get_default_gateway') as get:
get.return_value = None get.return_value = None
with mock.patch.object(self.mgr, 'sudo') as sudo: with mock.patch.object(self.mgr, 'sudo') as sudo:
self.mgr._set_default_gateway(ip) self.mgr._set_default_gateway(ip, 'ge1')
sudo.assert_called_with('add', '-inet6', 'default', ip_s) sudo.assert_called_with(
'-6', 'route', 'add', 'default', 'via', ip_s,
'dev', 'eth1'
)
def test_update_default_no_inputs(self): def test_update_default_no_inputs(self):
c = models.Configuration({}) c = models.Configuration({})
@ -156,13 +166,13 @@ sockaddrs: <DST,GATEWAY,NETMASK,IFP,IFA,LABEL>
set.side_effect = AssertionError( set.side_effect = AssertionError(
'should not try to set default gw' 'should not try to set default gw'
) )
self.mgr.update_default(c) self.mgr.update_default_gateway(c)
def test_update_default_v4_from_gateway(self): def test_update_default_v4_from_gateway(self):
c = models.Configuration({'default_v4_gateway': '172.16.77.1'}) c = models.Configuration({'default_v4_gateway': '172.16.77.1'})
with mock.patch.object(self.mgr, '_set_default_gateway') as set: with mock.patch.object(self.mgr, '_set_default_gateway') as set:
self.mgr.update_default(c) self.mgr.update_default_gateway(c)
set.assert_called_once_with(c.default_v4_gateway) set.assert_called_once_with(c.default_v4_gateway, None)
def test_update_default_v4_from_subnet(self): def test_update_default_v4_from_subnet(self):
subnet = dict( subnet = dict(
@ -181,10 +191,10 @@ sockaddrs: <DST,GATEWAY,NETMASK,IFP,IFA,LABEL>
) )
c = models.Configuration({'networks': [network]}) c = models.Configuration({'networks': [network]})
with mock.patch.object(self.mgr, '_set_default_gateway') as set: with mock.patch.object(self.mgr, '_set_default_gateway') as set:
self.mgr.update_default(c) self.mgr.update_default_gateway(c)
net = c.networks[0] net = c.networks[0]
snet = net.subnets[0] snet = net.subnets[0]
set.assert_called_once_with(snet.gateway_ip) set.assert_called_once_with(snet.gateway_ip, 'ge0')
def test_update_multiple_v4_subnets(self): def test_update_multiple_v4_subnets(self):
subnet = dict( subnet = dict(
@ -209,10 +219,10 @@ sockaddrs: <DST,GATEWAY,NETMASK,IFP,IFA,LABEL>
) )
c = models.Configuration({'networks': [network]}) c = models.Configuration({'networks': [network]})
with mock.patch.object(self.mgr, '_set_default_gateway') as set: with mock.patch.object(self.mgr, '_set_default_gateway') as set:
self.mgr.update_default(c) self.mgr.update_default_gateway(c)
net = c.networks[0] net = c.networks[0]
snet = net.subnets[0] snet = net.subnets[0]
set.assert_called_once_with(snet.gateway_ip) set.assert_called_once_with(snet.gateway_ip, 'ge0')
def test_update_default_v6(self): def test_update_default_v6(self):
subnet = dict( subnet = dict(
@ -231,10 +241,10 @@ sockaddrs: <DST,GATEWAY,NETMASK,IFP,IFA,LABEL>
) )
c = models.Configuration({'networks': [network]}) c = models.Configuration({'networks': [network]})
with mock.patch.object(self.mgr, '_set_default_gateway') as set: with mock.patch.object(self.mgr, '_set_default_gateway') as set:
self.mgr.update_default(c) self.mgr.update_default_gateway(c)
net = c.networks[0] net = c.networks[0]
snet = net.subnets[0] snet = net.subnets[0]
set.assert_called_once_with(snet.gateway_ip) set.assert_called_once_with(snet.gateway_ip, 'ge0')
def test_update_default_multiple_v6(self): def test_update_default_multiple_v6(self):
subnet = dict( subnet = dict(
@ -259,12 +269,12 @@ sockaddrs: <DST,GATEWAY,NETMASK,IFP,IFA,LABEL>
) )
c = models.Configuration({'networks': [network]}) c = models.Configuration({'networks': [network]})
with mock.patch.object(self.mgr, '_set_default_gateway') as set: with mock.patch.object(self.mgr, '_set_default_gateway') as set:
self.mgr.update_default(c) self.mgr.update_default_gateway(c)
net = c.networks[0] net = c.networks[0]
snet = net.subnets[0] snet = net.subnets[0]
set.assert_called_once_with(snet.gateway_ip) set.assert_called_once_with(snet.gateway_ip, 'ge0')
@mock.patch.object(route.RouteManager, '_set_default_gateway', @mock.patch.object(ip.IPManager, '_set_default_gateway',
lambda *a, **kw: None) lambda *a, **kw: None)
def test_custom_host_routes(self): def test_custom_host_routes(self):
subnet = dict( subnet = dict(
@ -290,7 +300,8 @@ sockaddrs: <DST,GATEWAY,NETMASK,IFP,IFA,LABEL>
# ...so let's add one! # ...so let's add one!
self.mgr.update_host_routes(c, cache) self.mgr.update_host_routes(c, cache)
sudo.assert_called_once_with( sudo.assert_called_once_with(
'add', '-inet', '192.240.128.0/20', '192.168.89.2' '-4', 'route', 'add', '192.240.128.0/20', 'via',
'192.168.89.2', 'dev', 'eth0'
) )
# db[subnet.cidr] should contain the above route # db[subnet.cidr] should contain the above route
@ -311,7 +322,8 @@ sockaddrs: <DST,GATEWAY,NETMASK,IFP,IFA,LABEL>
c = models.Configuration({'networks': [network]}) c = models.Configuration({'networks': [network]})
self.mgr.update_host_routes(c, cache) self.mgr.update_host_routes(c, cache)
sudo.assert_called_once_with( sudo.assert_called_once_with(
'delete', '-inet', '192.240.128.0/20', '192.168.89.2' '-4', 'route', 'del', '192.240.128.0/20', 'via',
'192.168.89.2', 'dev', 'eth0'
) )
self.assertEqual(len(cache.get('host_routes')), 0) self.assertEqual(len(cache.get('host_routes')), 0)
@ -327,8 +339,10 @@ sockaddrs: <DST,GATEWAY,NETMASK,IFP,IFA,LABEL>
c = models.Configuration({'networks': [network]}) c = models.Configuration({'networks': [network]})
self.mgr.update_host_routes(c, cache) self.mgr.update_host_routes(c, cache)
self.assertEqual(sudo.call_args_list, [ self.assertEqual(sudo.call_args_list, [
mock.call('add', '-inet', '192.240.128.0/20', '192.168.89.2'), mock.call('-4', 'route', 'add', '192.240.128.0/20',
mock.call('add', '-inet', '192.220.128.0/20', '192.168.89.3'), 'via', '192.168.89.2', 'dev', 'eth0'),
mock.call('-4', 'route', 'add', '192.220.128.0/20',
'via', '192.168.89.3', 'dev', 'eth0'),
]) ])
# ...let's remove one and add another... # ...let's remove one and add another...
@ -343,9 +357,10 @@ sockaddrs: <DST,GATEWAY,NETMASK,IFP,IFA,LABEL>
c = models.Configuration({'networks': [network]}) c = models.Configuration({'networks': [network]})
self.mgr.update_host_routes(c, cache) self.mgr.update_host_routes(c, cache)
self.assertEqual(sudo.call_args_list, [ self.assertEqual(sudo.call_args_list, [
mock.call('delete', '-inet', '192.220.128.0/20', mock.call('-4', 'route', 'del', '192.220.128.0/20',
'192.168.89.3'), 'via', '192.168.89.3', 'dev', 'eth0'),
mock.call('add', '-inet', '192.185.128.0/20', '192.168.89.4') mock.call('-4', 'route', 'add', '192.185.128.0/20',
'via', '192.168.89.4', 'dev', 'eth0')
]) ])
# ...let's add another subnet... # ...let's add another subnet...
@ -364,7 +379,8 @@ sockaddrs: <DST,GATEWAY,NETMASK,IFP,IFA,LABEL>
c = models.Configuration({'networks': [network]}) c = models.Configuration({'networks': [network]})
self.mgr.update_host_routes(c, cache) self.mgr.update_host_routes(c, cache)
self.assertEqual(sudo.call_args_list, [ self.assertEqual(sudo.call_args_list, [
mock.call('add', '-inet', '192.240.128.0/20', '192.168.90.1') mock.call('-4', 'route', 'add', '192.240.128.0/20',
'via', '192.168.90.1', 'dev', 'eth0')
]) ])
self.assertEqual(len(cache.get('host_routes')), 2) self.assertEqual(len(cache.get('host_routes')), 2)
@ -375,12 +391,12 @@ sockaddrs: <DST,GATEWAY,NETMASK,IFP,IFA,LABEL>
c = models.Configuration({'networks': [network]}) c = models.Configuration({'networks': [network]})
self.mgr.update_host_routes(c, cache) self.mgr.update_host_routes(c, cache)
self.assertEqual(sudo.call_args_list, [ self.assertEqual(sudo.call_args_list, [
mock.call('delete', '-inet', '192.185.128.0/20', mock.call('-4', 'route', 'del', '192.185.128.0/20',
'192.168.89.4'), 'via', '192.168.89.4', 'dev', 'eth0'),
mock.call('delete', '-inet', '192.240.128.0/20', mock.call('-4', 'route', 'del', '192.240.128.0/20',
'192.168.89.2'), 'via', '192.168.89.2', 'dev', 'eth0'),
mock.call('delete', '-inet', '192.240.128.0/20', mock.call('-4', 'route', 'del', '192.240.128.0/20',
'192.168.90.1'), 'via', '192.168.90.1', 'dev', 'eth0'),
]) ])
self.assertEqual(len(cache.get('host_routes')), 0) self.assertEqual(len(cache.get('host_routes')), 0)
@ -409,6 +425,7 @@ sockaddrs: <DST,GATEWAY,NETMASK,IFP,IFA,LABEL>
self.mgr.update_host_routes(c, cache) self.mgr.update_host_routes(c, cache)
sudo.assert_called_once_with( sudo.assert_called_once_with(
'add', '-inet', '192.240.128.0/20', '192.168.89.2' '-4', 'route', 'add', '192.240.128.0/20', 'via',
'192.168.89.2', 'dev', 'eth0'
) )
self.assertEqual(len(cache.get('host_routes')), 0) self.assertEqual(len(cache.get('host_routes')), 0)