neutron/neutron/tests/unit/agent/linux/test_ip_lib.py

1738 lines
68 KiB
Python

# Copyright 2012 OpenStack Foundation
# 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.
import copy
import errno
import socket
import mock
import netaddr
from neutron_lib import constants
from neutron_lib import exceptions
from oslo_utils import uuidutils
import pyroute2
from pyroute2.netlink.rtnl import ifinfmsg
from pyroute2.netlink.rtnl import ndmsg
from pyroute2 import NetlinkError
import testtools
from neutron.agent.common import utils # noqa
from neutron.agent.linux import ip_lib
from neutron.common import utils as common_utils
from neutron import privileged
from neutron.privileged.agent.linux import ip_lib as priv_lib
from neutron.tests import base
NETNS_SAMPLE = [
'12345678-1234-5678-abcd-1234567890ab',
'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb',
'cccccccc-cccc-cccc-cccc-cccccccccccc']
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
""")
GATEWAY_SAMPLE5 = ("""
default via 192.168.99.1 proto static
""")
GATEWAY_SAMPLE6 = ("""
default via 192.168.99.1 proto static metric 100
""")
GATEWAY_SAMPLE7 = ("""
default dev qg-31cd36 metric 1
""")
IPv6_GATEWAY_SAMPLE1 = ("""
default via 2001:470:9:1224:4508:b885:5fb:740b metric 100
2001:db8::/64 proto kernel scope link src 2001:470:9:1224:dfcc:aaff:feb9:76ce
""")
IPv6_GATEWAY_SAMPLE2 = ("""
default via 2001:470:9:1224:4508:b885:5fb:740b metric 100
""")
IPv6_GATEWAY_SAMPLE3 = ("""
2001:db8::/64 proto kernel scope link src 2001:470:9:1224:dfcc:aaff:feb9:76ce
""")
IPv6_GATEWAY_SAMPLE4 = ("""
default via fe80::dfcc:aaff:feb9:76ce
""")
IPv6_GATEWAY_SAMPLE5 = ("""
default via 2001:470:9:1224:4508:b885:5fb:740b metric 1024
""")
DEVICE_ROUTE_SAMPLE = ("10.0.0.0/24 scope link src 10.0.0.2")
SUBNET_SAMPLE1 = ("10.0.0.0/24 dev qr-23380d11-d2 scope link src 10.0.0.1\n"
"10.0.0.0/24 dev tap1d7888a7-10 scope link src 10.0.0.2")
SUBNET_SAMPLE2 = ("10.0.0.0/24 dev tap1d7888a7-10 scope link src 10.0.0.2\n"
"10.0.0.0/24 dev qr-23380d11-d2 scope link src 10.0.0.1")
class TestSubProcessBase(base.BaseTestCase):
def setUp(self):
super(TestSubProcessBase, self).setUp()
self.execute_p = mock.patch('neutron.agent.common.utils.execute')
self.execute = self.execute_p.start()
def test_execute_wrapper(self):
base = ip_lib.SubProcessBase()
base._execute(['o'], 'link', ('list',), run_as_root=True)
self.execute.assert_called_once_with(['ip', '-o', 'link', 'list'],
run_as_root=True,
log_fail_as_error=True)
def test_execute_wrapper_int_options(self):
base = ip_lib.SubProcessBase()
base._execute([4], 'link', ('list',))
self.execute.assert_called_once_with(['ip', '-4', 'link', 'list'],
run_as_root=False,
log_fail_as_error=True)
def test_execute_wrapper_no_options(self):
base = ip_lib.SubProcessBase()
base._execute([], 'link', ('list',))
self.execute.assert_called_once_with(['ip', 'link', 'list'],
run_as_root=False,
log_fail_as_error=True)
def test_run_no_namespace(self):
base = ip_lib.SubProcessBase()
base._run([], 'link', ('list',))
self.execute.assert_called_once_with(['ip', 'link', 'list'],
run_as_root=False,
log_fail_as_error=True)
def test_run_namespace(self):
base = ip_lib.SubProcessBase(namespace='ns')
base._run([], 'link', ('list',))
self.execute.assert_called_once_with(['ip', 'netns', 'exec', 'ns',
'ip', 'link', 'list'],
run_as_root=True,
log_fail_as_error=True)
def test_as_root_namespace(self):
base = ip_lib.SubProcessBase(namespace='ns')
base._as_root([], 'link', ('list',))
self.execute.assert_called_once_with(['ip', 'netns', 'exec', 'ns',
'ip', 'link', 'list'],
run_as_root=True,
log_fail_as_error=True)
class TestIpWrapper(base.BaseTestCase):
def setUp(self):
super(TestIpWrapper, self).setUp()
self.execute_p = mock.patch.object(ip_lib.IPWrapper, '_execute')
self.execute = self.execute_p.start()
@mock.patch.object(priv_lib, 'get_device_names')
def test_get_devices(self, mock_get_devices):
interfaces = ['br01', 'lo', 'gre0']
mock_get_devices.return_value = interfaces
devices = ip_lib.IPWrapper(namespace='foo').get_devices()
for device in devices:
self.assertEqual('br01', device.name)
interfaces.remove(device.name)
@mock.patch.object(priv_lib, 'get_device_names')
def test_get_devices_include_loopback_and_gre(self, mock_get_devices):
interfaces = ['br01', 'lo', 'gre0']
mock_get_devices.return_value = interfaces
devices = ip_lib.IPWrapper(namespace='foo').get_devices(
exclude_loopback=False, exclude_fb_tun_devices=False)
for device in devices:
self.assertIn(device.name, interfaces)
interfaces.remove(device.name)
self.assertEqual(0, len(interfaces))
@mock.patch.object(priv_lib, 'get_device_names')
def test_get_devices_no_netspace(self, mock_get_devices):
mock_get_devices.side_effect = priv_lib.NetworkNamespaceNotFound(
netns_name='foo')
self.assertEqual([], ip_lib.IPWrapper(namespace='foo').get_devices())
@mock.patch.object(pyroute2.netns, 'listnetns')
@mock.patch.object(priv_lib, 'list_netns')
def test_get_namespaces_non_root(self, priv_listnetns, listnetns):
self.config(group='AGENT', use_helper_for_ns_read=False)
listnetns.return_value = NETNS_SAMPLE
retval = ip_lib.list_network_namespaces()
self.assertEqual(retval,
['12345678-1234-5678-abcd-1234567890ab',
'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb',
'cccccccc-cccc-cccc-cccc-cccccccccccc'])
self.assertEqual(1, listnetns.call_count)
self.assertFalse(priv_listnetns.called)
@mock.patch.object(pyroute2.netns, 'listnetns')
@mock.patch.object(priv_lib, 'list_netns')
def test_get_namespaces_root(self, priv_listnetns, listnetns):
self.config(group='AGENT', use_helper_for_ns_read=True)
priv_listnetns.return_value = NETNS_SAMPLE
retval = ip_lib.list_network_namespaces()
self.assertEqual(retval,
['12345678-1234-5678-abcd-1234567890ab',
'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb',
'cccccccc-cccc-cccc-cccc-cccccccccccc'])
self.assertEqual(1, priv_listnetns.call_count)
self.assertFalse(listnetns.called)
@mock.patch.object(priv_lib, 'create_interface')
def test_add_tuntap(self, create):
ip_lib.IPWrapper().add_tuntap('tap0')
create.assert_called_once_with('tap0', None, 'tuntap', mode='tap')
@mock.patch.object(priv_lib, 'create_interface')
def test_add_veth(self, create):
ip_lib.IPWrapper().add_veth('tap0', 'tap1')
create.assert_called_once_with(
'tap0', None, 'veth', peer={'ifname': 'tap1'})
@mock.patch.object(priv_lib, 'create_interface')
def test_add_macvtap(self, create):
ip_lib.IPWrapper().add_macvtap('macvtap0', 'eth0', 'bridge')
create.assert_called_once_with(
'macvtap0', None, 'macvtap', physical_interface='eth0',
mode='bridge')
@mock.patch.object(priv_lib, 'delete_interface')
def test_del_veth(self, delete):
ip_lib.IPWrapper().del_veth('fpr-1234')
delete.assert_called_once_with('fpr-1234', None)
@mock.patch.object(priv_lib, 'create_interface')
def test_add_veth_with_namespaces(self, create):
ns2 = 'ns2'
with mock.patch.object(ip_lib.IPWrapper, 'ensure_namespace') as en:
ip_lib.IPWrapper().add_veth('tap0', 'tap1', namespace2=ns2)
en.assert_has_calls([mock.call(ns2)])
create.assert_called_once_with(
'tap0', None, 'veth',
peer={'ifname': 'tap1', 'net_ns_fd': 'ns2'})
@mock.patch.object(priv_lib, 'create_interface')
def test_add_dummy(self, create):
ip_lib.IPWrapper().add_dummy('dummy0')
create.assert_called_once_with('dummy0', None, 'dummy')
def test_get_device(self):
dev = ip_lib.IPWrapper(namespace='ns').device('eth0')
self.assertEqual(dev.namespace, 'ns')
self.assertEqual(dev.name, 'eth0')
@mock.patch.object(priv_lib, 'create_netns')
def test_ensure_namespace(self, create):
with mock.patch.object(ip_lib, 'IPDevice') as ip_dev:
ip = ip_lib.IPWrapper()
with mock.patch.object(ip.netns, 'exists') as ns_exists:
with mock.patch('neutron.agent.common.utils.execute'):
ns_exists.return_value = False
ip.ensure_namespace('ns')
create.assert_called_once_with('ns')
ns_exists.assert_called_once_with('ns')
ip_dev.assert_has_calls([mock.call('lo', namespace='ns'),
mock.call().link.set_up()])
def test_ensure_namespace_existing(self):
with mock.patch.object(ip_lib, 'IpNetnsCommand') as ip_ns_cmd:
ip_ns_cmd.exists.return_value = True
ns = ip_lib.IPWrapper().ensure_namespace('ns')
self.assertFalse(self.execute.called)
self.assertEqual(ns.namespace, 'ns')
def test_namespace_is_empty_no_devices(self):
ip = ip_lib.IPWrapper(namespace='ns')
with mock.patch.object(ip, 'get_devices') as get_devices:
get_devices.return_value = []
self.assertTrue(ip.namespace_is_empty())
self.assertTrue(get_devices.called)
def test_namespace_is_empty(self):
ip = ip_lib.IPWrapper(namespace='ns')
with mock.patch.object(ip, 'get_devices') as get_devices:
get_devices.return_value = [mock.Mock()]
self.assertFalse(ip.namespace_is_empty())
self.assertTrue(get_devices.called)
def test_garbage_collect_namespace_does_not_exist(self):
with mock.patch.object(ip_lib, 'IpNetnsCommand') as ip_ns_cmd_cls:
ip_ns_cmd_cls.return_value.exists.return_value = False
ip = ip_lib.IPWrapper(namespace='ns')
with mock.patch.object(ip, 'namespace_is_empty') as mock_is_empty:
self.assertFalse(ip.garbage_collect_namespace())
ip_ns_cmd_cls.assert_has_calls([mock.call().exists('ns')])
self.assertNotIn(mock.call().delete('ns'),
ip_ns_cmd_cls.return_value.mock_calls)
self.assertEqual([], mock_is_empty.mock_calls)
def test_garbage_collect_namespace_existing_empty_ns(self):
with mock.patch.object(ip_lib, 'IpNetnsCommand') as ip_ns_cmd_cls:
ip_ns_cmd_cls.return_value.exists.return_value = True
ip = ip_lib.IPWrapper(namespace='ns')
with mock.patch.object(ip, 'namespace_is_empty') as mock_is_empty:
mock_is_empty.return_value = True
self.assertTrue(ip.garbage_collect_namespace())
mock_is_empty.assert_called_once_with()
expected = [mock.call().exists('ns'),
mock.call().delete('ns')]
ip_ns_cmd_cls.assert_has_calls(expected)
def test_garbage_collect_namespace_existing_not_empty(self):
lo_device = mock.Mock()
lo_device.name = 'lo'
tap_device = mock.Mock()
tap_device.name = 'tap1'
with mock.patch.object(ip_lib, 'IpNetnsCommand') as ip_ns_cmd_cls:
ip_ns_cmd_cls.return_value.exists.return_value = True
ip = ip_lib.IPWrapper(namespace='ns')
with mock.patch.object(ip, 'namespace_is_empty') as mock_is_empty:
mock_is_empty.return_value = False
self.assertFalse(ip.garbage_collect_namespace())
mock_is_empty.assert_called_once_with()
expected = [mock.call(ip),
mock.call().exists('ns')]
self.assertEqual(ip_ns_cmd_cls.mock_calls, expected)
self.assertNotIn(mock.call().delete('ns'),
ip_ns_cmd_cls.mock_calls)
@mock.patch.object(priv_lib, 'create_interface')
def test_add_vlan(self, create):
retval = ip_lib.IPWrapper().add_vlan('eth0.1', 'eth0', '1')
self.assertIsInstance(retval, ip_lib.IPDevice)
self.assertEqual(retval.name, 'eth0.1')
create.assert_called_once_with('eth0.1',
None,
'vlan',
physical_interface='eth0',
vlan_id='1')
@mock.patch.object(priv_lib, 'create_interface')
def test_add_vxlan_valid_srcport_length(self, create):
self.call_params = {}
def fake_create_interface(ifname, namespace, kind, **kwargs):
self.call_params = dict(
ifname=ifname,
namespace=namespace,
kind=kind,
**kwargs)
create.side_effect = fake_create_interface
expected_call_params = {
'ifname': 'vxlan0',
'namespace': None,
'kind': 'vxlan',
'vxlan_id': 'vni0',
'vxlan_group': 'group0',
'physical_interface': 'dev0',
'vxlan_ttl': 'ttl0',
'vxlan_tos': 'tos0',
'vxlan_local': 'local0',
'vxlan_proxy': True,
'vxlan_port_range': ('1', '2')}
retval = ip_lib.IPWrapper().add_vxlan('vxlan0', 'vni0',
group='group0',
dev='dev0', ttl='ttl0',
tos='tos0',
local='local0', proxy=True,
srcport=(1, 2))
self.assertIsInstance(retval, ip_lib.IPDevice)
self.assertEqual(retval.name, 'vxlan0')
self.assertDictEqual(expected_call_params, self.call_params)
def test_add_vxlan_invalid_srcport_length(self):
wrapper = ip_lib.IPWrapper()
self.assertRaises(exceptions.NetworkVxlanPortRangeError,
wrapper.add_vxlan, 'vxlan0', 'vni0', group='group0',
dev='dev0', ttl='ttl0', tos='tos0',
local='local0', proxy=True,
srcport=('1', '2', '3'))
def test_add_vxlan_invalid_srcport_range(self):
wrapper = ip_lib.IPWrapper()
self.assertRaises(exceptions.NetworkVxlanPortRangeError,
wrapper.add_vxlan, 'vxlan0', 'vni0', group='group0',
dev='dev0', ttl='ttl0', tos='tos0',
local='local0', proxy=True,
srcport=(2000, 1000))
@mock.patch.object(priv_lib, 'create_interface')
def test_add_vxlan_dstport(self, create):
self.call_params = {}
def fake_create_interface(ifname, namespace, kind, **kwargs):
self.call_params = dict(
ifname=ifname,
namespace=namespace,
kind=kind,
**kwargs)
create.side_effect = fake_create_interface
expected_call_params = {
'ifname': 'vxlan0',
'namespace': None,
'kind': 'vxlan',
'vxlan_id': 'vni0',
'vxlan_group': 'group0',
'physical_interface': 'dev0',
'vxlan_ttl': 'ttl0',
'vxlan_tos': 'tos0',
'vxlan_local': 'local0',
'vxlan_proxy': True,
'vxlan_port_range': ('1', '2'),
'vxlan_port': 4789}
retval = ip_lib.IPWrapper().add_vxlan('vxlan0', 'vni0',
group='group0',
dev='dev0', ttl='ttl0',
tos='tos0',
local='local0', proxy=True,
srcport=(1, 2),
dstport=4789)
self.assertIsInstance(retval, ip_lib.IPDevice)
self.assertEqual(retval.name, 'vxlan0')
self.assertDictEqual(expected_call_params, self.call_params)
def test_add_device_to_namespace(self):
dev = mock.Mock()
ip_lib.IPWrapper(namespace='ns').add_device_to_namespace(dev)
dev.assert_has_calls([mock.call.link.set_netns('ns')])
def test_add_device_to_namespace_is_none(self):
dev = mock.Mock()
ip_lib.IPWrapper().add_device_to_namespace(dev)
self.assertEqual([], dev.mock_calls)
class TestIPDevice(base.BaseTestCase):
def test_eq_same_name(self):
dev1 = ip_lib.IPDevice('tap0')
dev2 = ip_lib.IPDevice('tap0')
self.assertEqual(dev1, dev2)
def test_eq_diff_name(self):
dev1 = ip_lib.IPDevice('tap0')
dev2 = ip_lib.IPDevice('tap1')
self.assertNotEqual(dev1, dev2)
def test_eq_same_namespace(self):
dev1 = ip_lib.IPDevice('tap0', 'ns1')
dev2 = ip_lib.IPDevice('tap0', 'ns1')
self.assertEqual(dev1, dev2)
def test_eq_diff_namespace(self):
dev1 = ip_lib.IPDevice('tap0', namespace='ns1')
dev2 = ip_lib.IPDevice('tap0', namespace='ns2')
self.assertNotEqual(dev1, dev2)
def test_eq_other_is_none(self):
dev1 = ip_lib.IPDevice('tap0', namespace='ns1')
self.assertIsNotNone(dev1)
def test_str(self):
self.assertEqual(str(ip_lib.IPDevice('tap0')), 'tap0')
class TestIPCommandBase(base.BaseTestCase):
def setUp(self):
super(TestIPCommandBase, self).setUp()
self.ip = mock.Mock()
self.ip.namespace = 'namespace'
self.ip_cmd = ip_lib.IpCommandBase(self.ip)
self.ip_cmd.COMMAND = 'foo'
def test_run(self):
self.ip_cmd._run([], ('link', 'show'))
self.ip.assert_has_calls([mock.call._run([], 'foo', ('link', 'show'))])
def test_run_with_options(self):
self.ip_cmd._run(['o'], ('link'))
self.ip.assert_has_calls([mock.call._run(['o'], 'foo', ('link'))])
def test_as_root_namespace_false(self):
self.ip_cmd._as_root([], ('link'))
self.ip.assert_has_calls(
[mock.call._as_root([],
'foo',
('link'),
use_root_namespace=False)])
def test_as_root_namespace_true(self):
self.ip_cmd._as_root([], ('link'), use_root_namespace=True)
self.ip.assert_has_calls(
[mock.call._as_root([],
'foo',
('link'),
use_root_namespace=True)])
def test_as_root_namespace_true_with_options(self):
self.ip_cmd._as_root('o', 'link', use_root_namespace=True)
self.ip.assert_has_calls(
[mock.call._as_root('o',
'foo',
('link'),
use_root_namespace=True)])
class TestIPDeviceCommandBase(base.BaseTestCase):
def setUp(self):
super(TestIPDeviceCommandBase, self).setUp()
self.ip_dev = mock.Mock()
self.ip_dev.name = 'eth0'
self.ip_dev._execute = mock.Mock(return_value='executed')
self.ip_cmd = ip_lib.IpDeviceCommandBase(self.ip_dev)
self.ip_cmd.COMMAND = 'foo'
def test_name_property(self):
self.assertEqual(self.ip_cmd.name, 'eth0')
class TestIPCmdBase(base.BaseTestCase):
def setUp(self):
super(TestIPCmdBase, self).setUp()
self.parent = mock.Mock()
self.parent.name = 'eth0'
def _assert_call(self, options, args):
self.parent._run.assert_has_calls([
mock.call(options, self.command, args)])
def _assert_sudo(self, options, args, use_root_namespace=False):
self.parent._as_root.assert_has_calls(
[mock.call(options, self.command, args,
use_root_namespace=use_root_namespace)])
class TestIpRuleCommand(TestIPCmdBase):
def setUp(self):
super(TestIpRuleCommand, self).setUp()
self.parent._as_root.return_value = ''
self.ns = uuidutils.generate_uuid()
self.parent.namespace = self.ns
self.command = 'rule'
self._mock_priv_list_ip_rules = mock.patch.object(priv_lib,
'list_ip_rules')
self.mock_priv_list_ip_rules = self._mock_priv_list_ip_rules.start()
self.addCleanup(self._stop_mock)
def _stop_mock(self):
self._mock_priv_list_ip_rules.stop()
def _test_add_rule(self, ip, iif, table, priority):
ip_version = netaddr.IPNetwork(ip).version
ip_family = common_utils.get_socket_address_family(ip_version)
table_num = ip_lib.IP_RULE_TABLES.get(table) or int(table)
cmd_args = {'table': table_num,
'priority': priority,
'family': ip_family}
if iif:
cmd_args['iifname'] = iif
else:
cmd_args['src'] = ip
cmd_args['src_len'] = common_utils.get_network_length(ip_version)
with mock.patch.object(priv_lib, 'add_ip_rule') as mock_add_ip_rule:
ip_lib.add_ip_rule('namespace', ip, iif=iif, table=table,
priority=priority)
mock_add_ip_rule.assert_called_once_with('namespace', **cmd_args)
def _test_add_rule_exists(self, ip, table, priority, output):
self.parent._as_root.return_value = output
with mock.patch.object(ip_lib, '_exist_ip_rule', return_value=True) \
as mock_exists:
ip_lib.add_ip_rule(self.ns, ip, table=table, priority=priority)
kwargs = {'from': ip, 'priority': str(priority),
'table': str(table), 'type': 'unicast'}
mock_exists.assert_called_once_with(
common_utils.get_ip_version(ip), **kwargs)
def _test_delete_rule(self, ip, table, priority):
with mock.patch.object(priv_lib, 'delete_ip_rule') as mock_delete:
ip_lib.delete_ip_rule(self.ns, ip, table=table, priority=priority)
args = ip_lib._make_pyroute2_args(ip, None, table, priority, None)
mock_delete.assert_called_with(self.ns, **args)
def test_add_rule_v4(self):
self._test_add_rule('192.168.45.100', None, 2, 100)
def test_add_rule_v4_iif(self):
self._test_add_rule('192.168.45.100', 'iif_name', 2, 100)
def test_add_rule_v6(self):
self._test_add_rule('2001:db8::1', None, 3, 200)
def test_add_rule_table_string(self):
self._test_add_rule('2001:db8::1', None, 'default', 200)
self._test_add_rule('2001:db8::1', None, 'main', 200)
self._test_add_rule('2001:db8::1', None, 'local', 200)
self._test_add_rule('2001:db8::1', None, '100', 200)
def test_delete_rule_v4(self):
self._test_delete_rule('192.168.45.100', 2, 100)
def test_delete_rule_v6(self):
self._test_delete_rule('2001:db8::1', 3, 200)
class TestIpLinkCommand(TestIPCmdBase):
def setUp(self):
super(TestIpLinkCommand, self).setUp()
self.command = 'link'
self.link_cmd = ip_lib.IpLinkCommand(self.parent)
@mock.patch.object(priv_lib, 'set_link_attribute')
def test_set_address(self, set_link_attribute):
self.link_cmd.set_address('aa:bb:cc:dd:ee:ff')
set_link_attribute.assert_called_once_with(
self.parent.name, self.parent.namespace,
address='aa:bb:cc:dd:ee:ff')
@mock.patch.object(priv_lib, 'set_link_flags')
def test_set_allmulticast_on(self, set_link_flags):
self.link_cmd.set_allmulticast_on()
set_link_flags.assert_called_once_with(
self.parent.name, self.parent.namespace, ifinfmsg.IFF_ALLMULTI)
@mock.patch.object(priv_lib, 'set_link_attribute')
def test_set_mtu(self, set_link_attribute):
self.link_cmd.set_mtu(1500)
set_link_attribute.assert_called_once_with(
self.parent.name, self.parent.namespace, mtu=1500)
@mock.patch.object(priv_lib, 'set_link_attribute')
def test_set_up(self, set_link_attribute):
self.link_cmd.set_up()
set_link_attribute.assert_called_once_with(
self.parent.name, self.parent.namespace, state='up')
@mock.patch.object(priv_lib, 'set_link_attribute')
def test_set_down(self, set_link_attribute):
self.link_cmd.set_down()
set_link_attribute.assert_called_once_with(
self.parent.name, self.parent.namespace, state='down')
@mock.patch.object(priv_lib, 'interface_exists', return_value=True)
@mock.patch.object(priv_lib, 'set_link_attribute')
def test_set_netns(self, set_link_attribute, *args):
original_namespace = self.parent.namespace
self.link_cmd.set_netns('foo')
set_link_attribute.assert_called_once_with(
'eth0', original_namespace, net_ns_fd='foo')
self.assertEqual(self.parent.namespace, 'foo')
@mock.patch.object(priv_lib, 'set_link_attribute')
def test_set_name(self, set_link_attribute):
original_name = self.parent.name
self.link_cmd.set_name('tap1')
set_link_attribute.assert_called_once_with(
original_name, self.parent.namespace, ifname='tap1')
self.assertEqual(self.parent.name, 'tap1')
@mock.patch.object(priv_lib, 'set_link_attribute')
def test_set_alias(self, set_link_attribute):
self.link_cmd.set_alias('openvswitch')
set_link_attribute.assert_called_once_with(
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')
def test_delete(self, delete):
self.link_cmd.delete()
delete.assert_called_once_with(self.parent.name, self.parent.namespace)
@mock.patch.object(priv_lib, 'get_link_attributes')
def test_settings_property(self, get_link_attributes):
self.link_cmd.attributes
get_link_attributes.assert_called_once_with(
self.parent.name, self.parent.namespace)
class TestIpAddrCommand(TestIPCmdBase):
def setUp(self):
super(TestIpAddrCommand, self).setUp()
self.parent.name = 'tap0'
self.command = 'addr'
self.addr_cmd = ip_lib.IpAddrCommand(self.parent)
@mock.patch.object(priv_lib, 'add_ip_address')
def test_add_address(self, add):
self.addr_cmd.add('192.168.45.100/24')
add.assert_called_once_with(
4,
'192.168.45.100',
24,
self.parent.name,
self.addr_cmd._parent.namespace,
'global',
'192.168.45.255')
@mock.patch.object(priv_lib, 'add_ip_address')
def test_add_address_scoped(self, add):
self.addr_cmd.add('192.168.45.100/24', scope='link')
add.assert_called_once_with(
4,
'192.168.45.100',
24,
self.parent.name,
self.addr_cmd._parent.namespace,
'link',
'192.168.45.255')
@mock.patch.object(priv_lib, 'add_ip_address')
def test_add_address_no_broadcast(self, add):
self.addr_cmd.add('192.168.45.100/24', add_broadcast=False)
add.assert_called_once_with(
4,
'192.168.45.100',
24,
self.parent.name,
self.addr_cmd._parent.namespace,
'global',
None)
@mock.patch.object(priv_lib, 'delete_ip_address')
def test_del_address(self, delete):
self.addr_cmd.delete('192.168.45.100/24')
delete.assert_called_once_with(
4,
'192.168.45.100',
24,
self.parent.name,
self.addr_cmd._parent.namespace)
@mock.patch.object(priv_lib, 'flush_ip_addresses')
def test_flush(self, flush):
self.addr_cmd.flush(6)
flush.assert_called_once_with(
6, self.parent.name, self.addr_cmd._parent.namespace)
def test_wait_until_address_ready(self):
self.addr_cmd.list = mock.Mock(return_value=[{'tentative': False}])
# this address is not tentative or failed so it should return
self.assertIsNone(self.addr_cmd.wait_until_address_ready(
'2001:470:9:1224:fd91:272:581e:3a32'))
def test_wait_until_address_ready_non_existent_address(self):
self.addr_cmd.list = mock.Mock(return_value=[])
with testtools.ExpectedException(ip_lib.AddressNotReady):
self.addr_cmd.wait_until_address_ready('abcd::1234')
def test_wait_until_address_ready_timeout(self):
tentative_address = 'fe80::3023:39ff:febc:22ae'
self.addr_cmd.list = mock.Mock(return_value=[
dict(scope='link', dadfailed=False, tentative=True, dynamic=False,
cidr=tentative_address + '/64')])
with testtools.ExpectedException(ip_lib.AddressNotReady):
self.addr_cmd.wait_until_address_ready(tentative_address,
wait_time=1)
@mock.patch.object(ip_lib, 'get_devices_with_ip')
def test_list(self, mock_get_dev_ip):
self.addr_cmd._parent.namespace = 'test_ns'
self.addr_cmd.list()
mock_get_dev_ip.assert_called_once_with('test_ns',
name=self.addr_cmd.name)
@mock.patch.object(ip_lib, 'get_devices_with_ip')
def test_list_scope(self, mock_get_dev_ip):
self.addr_cmd._parent.namespace = 'test_ns'
self.addr_cmd.list(scope='link')
mock_get_dev_ip.assert_called_once_with('test_ns',
name=self.addr_cmd.name,
scope='link')
@mock.patch.object(ip_lib, 'get_devices_with_ip')
def test_list_to(self, mock_get_dev_ip):
self.addr_cmd._parent.namespace = 'test_ns'
cidrs = [{'cidr': '1.2.3.4', 'mask': None},
{'cidr': '1.2.3.4/24', 'mask': 24},
{'cidr': '2001:db8::1', 'mask': None},
{'cidr': '2001:db8::1/64', 'mask': 64}]
for cidr in cidrs:
self.addr_cmd.list(to=cidr['cidr'])
args = {'name': self.addr_cmd.name,
'address': common_utils.cidr_to_ip(cidr['cidr'])}
if cidr['mask']:
args['mask'] = cidr['mask']
mock_get_dev_ip.assert_called_once_with('test_ns', **args)
mock_get_dev_ip.reset_mock()
@mock.patch.object(ip_lib, 'get_devices_with_ip')
def test_list_ip_version(self, mock_get_dev_ip):
self.addr_cmd._parent.namespace = 'test_ns'
ip_versions = [
{'ip_version': constants.IP_VERSION_4, 'family': socket.AF_INET},
{'ip_version': constants.IP_VERSION_6, 'family': socket.AF_INET6}]
for ip_version in ip_versions:
self.addr_cmd.list(ip_version=ip_version['ip_version'])
mock_get_dev_ip.assert_called_once_with(
'test_ns', name=self.addr_cmd.name,
family=ip_version['family'])
mock_get_dev_ip.reset_mock()
@mock.patch.object(ip_lib, 'get_devices_with_ip')
def test_list_filters_dynamic_permanent(self, mock_get_dev_ip):
self.addr_cmd._parent.namespace = 'test_ns'
mock_get_dev_ip.return_value = [{'dynamic': True}]
retval = self.addr_cmd.list(filters=['dynamic'])
self.assertEqual(1, len(retval))
retval = self.addr_cmd.list(filters=['permanent'])
self.assertEqual(0, len(retval))
@mock.patch.object(ip_lib, 'get_devices_with_ip')
def test_list_filters_tentative_dadfailed(self, mock_get_dev_ip):
self.addr_cmd._parent.namespace = 'test_ns'
mock_get_dev_ip.return_value = [{'tentative': True,
'dadfailed': False}]
retval = self.addr_cmd.list(filters=['tentative'])
self.assertEqual(1, len(retval))
retval = self.addr_cmd.list(filters=['tentative', 'dadfailed'])
self.assertEqual(0, len(retval))
class TestIpNetnsCommand(TestIPCmdBase):
def setUp(self):
super(TestIpNetnsCommand, self).setUp()
self.command = 'netns'
self.netns_cmd = ip_lib.IpNetnsCommand(self.parent)
@mock.patch.object(priv_lib, 'create_netns')
def test_add_namespace(self, create):
with mock.patch('neutron.agent.common.utils.execute') as execute:
ns = self.netns_cmd.add('ns')
create.assert_called_once_with('ns')
self.assertEqual(ns.namespace, 'ns')
execute.assert_called_once_with(
['ip', 'netns', 'exec', 'ns',
'sysctl', '-w', 'net.ipv4.conf.all.promote_secondaries=1'],
run_as_root=True, check_exit_code=True, extra_ok_codes=None,
log_fail_as_error=True)
@mock.patch.object(priv_lib, 'remove_netns')
def test_delete_namespace(self, remove):
self.netns_cmd.delete('ns')
remove.assert_called_once_with('ns')
def test_execute(self):
self.parent.namespace = 'ns'
with mock.patch('neutron.agent.common.utils.execute') as execute:
self.netns_cmd.execute(['ip', 'link', 'list'])
execute.assert_called_once_with(['ip', 'netns', 'exec', 'ns', 'ip',
'link', 'list'],
run_as_root=True,
check_exit_code=True,
extra_ok_codes=None,
log_fail_as_error=True)
def test_execute_env_var_prepend(self):
self.parent.namespace = 'ns'
with mock.patch('neutron.agent.common.utils.execute') as execute:
env = dict(FOO=1, BAR=2)
self.netns_cmd.execute(['ip', 'link', 'list'], env)
execute.assert_called_once_with(
['ip', 'netns', 'exec', 'ns', 'env'] +
['%s=%s' % (k, v) for k, v in env.items()] +
['ip', 'link', 'list'],
run_as_root=True, check_exit_code=True, extra_ok_codes=None,
log_fail_as_error=True)
def test_execute_nosudo_with_no_namespace(self):
with mock.patch('neutron.agent.common.utils.execute') as execute:
self.parent.namespace = None
self.netns_cmd.execute(['test'])
execute.assert_called_once_with(['test'],
check_exit_code=True,
extra_ok_codes=None,
run_as_root=False,
log_fail_as_error=True)
class TestDeviceExists(base.BaseTestCase):
def test_ensure_device_is_ready(self):
ip_lib_mock = mock.Mock()
with mock.patch.object(ip_lib, 'IPDevice', return_value=ip_lib_mock):
self.assertTrue(ip_lib.ensure_device_is_ready("eth0"))
self.assertTrue(ip_lib_mock.link.set_up.called)
ip_lib_mock.reset_mock()
# device doesn't exists
ip_lib_mock.link.set_up.side_effect = RuntimeError
self.assertFalse(ip_lib.ensure_device_is_ready("eth0"))
def test_ensure_device_is_ready_no_link_address(self):
with mock.patch.object(
priv_lib, 'get_link_attributes') as get_link_attributes, \
mock.patch.object(priv_lib, 'set_link_attribute') as \
set_link_attribute, \
mock.patch.object(priv_lib, 'interface_exists',
return_value=True):
get_link_attributes.return_value = {}
self.assertFalse(ip_lib.ensure_device_is_ready("lo"))
get_link_attributes.assert_called_once_with("lo", None)
set_link_attribute.assert_not_called()
def test_ensure_device_is_ready_no_device(self):
with mock.patch.object(priv_lib, 'interface_exists',
return_value=False):
self.assertFalse(ip_lib.ensure_device_is_ready("lo"))
class TestGetRoutingTable(base.BaseTestCase):
ip_db_interfaces = {
1: {
'family': 0,
'txqlen': 0,
'ipdb_scope': 'system',
'index': 1,
'operstate': 'DOWN',
'num_tx_queues': 1,
'group': 0,
'carrier_changes': 0,
'ipaddr': [],
'neighbours': [],
'ifname': 'lo',
'promiscuity': 0,
'linkmode': 0,
'broadcast': '00:00:00:00:00:00',
'address': '00:00:00:00:00:00',
'vlans': [],
'ipdb_priority': 0,
'qdisc': 'noop',
'mtu': 65536,
'num_rx_queues': 1,
'carrier': 1,
'flags': 8,
'ifi_type': 772,
'ports': []
},
2: {
'family': 0,
'txqlen': 500,
'ipdb_scope': 'system',
'index': 2,
'operstate': 'DOWN',
'num_tx_queues': 1,
'group': 0,
'carrier_changes': 1,
'ipaddr': ['1111:1111:1111:1111::3/64', '10.0.0.3/24'],
'neighbours': [],
'ifname': 'tap-1',
'promiscuity': 0,
'linkmode': 0,
'broadcast': 'ff:ff:ff:ff:ff:ff',
'address': 'b6:d5:f6:a8:2e:62',
'vlans': [],
'ipdb_priority': 0,
'kind': 'tun',
'qdisc': 'fq_codel',
'mtu': 1500,
'num_rx_queues': 1,
'carrier': 0,
'flags': 4099,
'ifi_type': 1,
'ports': []
},
'tap-1': {
'family': 0,
'txqlen': 500,
'ipdb_scope': 'system',
'index': 2,
'operstate': 'DOWN',
'num_tx_queues': 1,
'group': 0,
'carrier_changes': 1,
'ipaddr': ['1111:1111:1111:1111::3/64', '10.0.0.3/24'],
'neighbours': [],
'ifname': 'tap-1',
'promiscuity': 0,
'linkmode': 0,
'broadcast': 'ff:ff:ff:ff:ff:ff',
'address': 'b6:d5:f6:a8:2e:62',
'vlans': [],
'ipdb_priority': 0,
'kind': 'tun',
'qdisc': 'fq_codel',
'mtu': 1500,
'num_rx_queues': 1,
'carrier': 0,
'flags': 4099,
'ifi_type': 1,
'ports': []
},
'lo': {
'family': 0,
'txqlen': 0,
'ipdb_scope': 'system',
'index': 1,
'operstate': 'DOWN',
'num_tx_queues': 1,
'group': 0,
'carrier_changes': 0,
'ipaddr': [],
'neighbours': [],
'ifname': 'lo',
'promiscuity': 0,
'linkmode': 0,
'broadcast': '00:00:00:00:00:00',
'address': '00:00:00:00:00:00',
'vlans': [],
'ipdb_priority': 0,
'qdisc': 'noop',
'mtu': 65536,
'num_rx_queues': 1,
'carrier': 1,
'flags': 8,
'ifi_type': 772,
'ports': []
}
}
ip_db_routes = [
{
'oif': 2,
'dst_len': 24,
'family': 2,
'proto': 3,
'tos': 0,
'dst': '10.0.1.0/24',
'flags': 16,
'ipdb_priority': 0,
'metrics': {},
'scope': 0,
'encap': {},
'src_len': 0,
'table': 254,
'multipath': [],
'type': 1,
'gateway': '10.0.0.1',
'ipdb_scope': 'system'
}, {
'oif': 2,
'type': 1,
'dst_len': 24,
'family': 2,
'proto': 2,
'tos': 0,
'dst': '10.0.0.0/24',
'ipdb_priority': 0,
'metrics': {},
'flags': 16,
'encap': {},
'src_len': 0,
'table': 254,
'multipath': [],
'prefsrc': '10.0.0.3',
'scope': 253,
'ipdb_scope': 'system'
}, {
'oif': 2,
'dst_len': 0,
'family': 2,
'proto': 3,
'tos': 0,
'dst': 'default',
'flags': 16,
'ipdb_priority': 0,
'metrics': {},
'scope': 0,
'encap': {},
'src_len': 0,
'table': 254,
'multipath': [],
'type': 1,
'gateway': '10.0.0.2',
'ipdb_scope': 'system'
}, {
'metrics': {},
'oif': 2,
'dst_len': 64,
'family': socket.AF_INET6,
'proto': 2,
'tos': 0,
'dst': '1111:1111:1111:1111::/64',
'pref': '00',
'ipdb_priority': 0,
'priority': 256,
'flags': 0,
'encap': {},
'src_len': 0,
'table': 254,
'multipath': [],
'type': 1,
'scope': 0,
'ipdb_scope': 'system'
}, {
'metrics': {},
'oif': 2,
'dst_len': 64,
'family': socket.AF_INET6,
'proto': 3,
'tos': 0,
'dst': '1111:1111:1111:1112::/64',
'pref': '00',
'flags': 0,
'ipdb_priority': 0,
'priority': 1024,
'scope': 0,
'encap': {},
'src_len': 0,
'table': 254,
'multipath': [],
'type': 1,
'gateway': '1111:1111:1111:1111::1',
'ipdb_scope': 'system'
}
]
ip_db_multipath_routes = [
{
'dst_len': 24,
'family': socket.AF_INET,
'proto': 3,
'tos': 0,
'dst': '10.0.1.0/24',
'flags': 16,
'ipdb_priority': 0,
'metrics': {},
'scope': 0,
'encap': {},
'src_len': 0,
'table': 254,
'multipath': ({'oif': 1, 'family': socket.AF_INET},
{'oif': 2, 'dst_len': 24, 'family': socket.AF_INET,
'proto': 2, 'tos': 0, 'pref': '00',
'priority': 256, 'flags': 0, 'encap': {},
'src_len': 0, 'table': 254, 'type': 1,
'scope': 0}),
'type': 1,
'gateway': '10.0.0.1',
'ipdb_scope': 'system'
}, {
'metrics': {},
'dst_len': 64,
'family': socket.AF_INET6,
'proto': 2,
'tos': 0,
'dst': '1111:1111:1111:1111::/64',
'pref': '00',
'ipdb_priority': 0,
'priority': 256,
'flags': 0,
'encap': {},
'src_len': 0,
'table': 254,
'multipath': ({'oif': 1, 'family': socket.AF_INET6},
{'oif': 2, 'dst_len': 64, 'family': socket.AF_INET6,
'proto': 2, 'tos': 0, 'pref': '00',
'priority': 256, 'flags': 0, 'encap': {},
'src_len': 0, 'table': 254, 'type': 1,
'scope': 0}),
'type': 1,
'scope': 0,
'ipdb_scope': 'system'
}
]
def setUp(self):
super(TestGetRoutingTable, self).setUp()
self.addCleanup(privileged.default.set_client_mode, True)
privileged.default.set_client_mode(False)
@mock.patch.object(pyroute2, 'IPDB')
@mock.patch.object(pyroute2, 'NetNS')
def test_get_routing_table_nonexistent_namespace(self,
mock_netns, mock_ip_db):
mock_netns.side_effect = OSError(errno.ENOENT, None)
with testtools.ExpectedException(ip_lib.NetworkNamespaceNotFound):
ip_lib.get_routing_table(4, 'ns')
@mock.patch.object(pyroute2, 'IPDB')
@mock.patch.object(pyroute2, 'NetNS')
def test_get_routing_table_other_error(self, mock_netns, mock_ip_db):
expected_exception = OSError(errno.EACCES, None)
mock_netns.side_effect = expected_exception
with testtools.ExpectedException(expected_exception.__class__):
ip_lib.get_routing_table(4, 'ns')
@mock.patch.object(pyroute2, 'IPDB')
@mock.patch.object(pyroute2, 'NetNS')
def _test_get_routing_table(self, version, ip_db_routes, expected,
mock_netns, mock_ip_db):
mock_ip_db_instance = mock_ip_db.return_value
mock_ip_db_enter = mock_ip_db_instance.__enter__.return_value
mock_ip_db_enter.interfaces = self.ip_db_interfaces
mock_ip_db_enter.routes = ip_db_routes
self.assertEqual(expected, ip_lib.get_routing_table(version))
def test_get_routing_table_4(self):
expected = [{'destination': '10.0.1.0/24',
'nexthop': '10.0.0.1',
'device': 'tap-1',
'scope': 'universe'},
{'destination': '10.0.0.0/24',
'nexthop': None,
'device': 'tap-1',
'scope': 'link'},
{'destination': 'default',
'nexthop': '10.0.0.2',
'device': 'tap-1',
'scope': 'universe'}]
self._test_get_routing_table(4, self.ip_db_routes, expected)
def test_get_routing_table_6(self):
expected = [{'destination': '1111:1111:1111:1111::/64',
'nexthop': None,
'device': 'tap-1',
'scope': 'universe'},
{'destination': '1111:1111:1111:1112::/64',
'nexthop': '1111:1111:1111:1111::1',
'device': 'tap-1',
'scope': 'universe'}]
self._test_get_routing_table(6, self.ip_db_routes, expected)
def test_get_routing_table_multipath_4(self):
expected = [{'destination': '10.0.1.0/24',
'nexthop': '10.0.0.1',
'device': 'lo',
'scope': 'universe'},
{'destination': '10.0.1.0/24',
'nexthop': '10.0.0.1',
'device': 'tap-1',
'scope': 'universe'}]
self._test_get_routing_table(4, self.ip_db_multipath_routes, expected)
def test_get_routing_table_multipath_6(self):
expected = [{'destination': '1111:1111:1111:1111::/64',
'nexthop': None,
'device': 'lo',
'scope': 'universe'},
{'destination': '1111:1111:1111:1111::/64',
'nexthop': None,
'device': 'tap-1',
'scope': 'universe'}]
self._test_get_routing_table(6, self.ip_db_multipath_routes, expected)
class TestIpNeighCommand(TestIPCmdBase):
def setUp(self):
super(TestIpNeighCommand, self).setUp()
self.parent.name = 'tap0'
self.command = 'neigh'
self.neigh_cmd = ip_lib.IpNeighCommand(self.parent)
self.addCleanup(privileged.default.set_client_mode, True)
privileged.default.set_client_mode(False)
@mock.patch.object(pyroute2, 'NetNS')
def test_add_entry(self, mock_netns):
mock_netns_instance = mock_netns.return_value
mock_netns_enter = mock_netns_instance.__enter__.return_value
mock_netns_enter.link_lookup.return_value = [1]
self.neigh_cmd.add('192.168.45.100', 'cc:dd:ee:ff:ab:cd')
mock_netns_enter.link_lookup.assert_called_once_with(ifname='tap0')
mock_netns_enter.neigh.assert_called_once_with(
'replace',
dst='192.168.45.100',
lladdr='cc:dd:ee:ff:ab:cd',
family=2,
ifindex=1,
state=ndmsg.states['permanent'])
@mock.patch.object(pyroute2, 'NetNS')
def test_add_entry_nonexistent_namespace(self, mock_netns):
mock_netns.side_effect = OSError(errno.ENOENT, None)
with testtools.ExpectedException(ip_lib.NetworkNamespaceNotFound):
self.neigh_cmd.add('192.168.45.100', 'cc:dd:ee:ff:ab:cd')
@mock.patch.object(pyroute2, 'NetNS')
def test_add_entry_other_error(self, mock_netns):
expected_exception = OSError(errno.EACCES, None)
mock_netns.side_effect = expected_exception
with testtools.ExpectedException(expected_exception.__class__):
self.neigh_cmd.add('192.168.45.100', 'cc:dd:ee:ff:ab:cd')
@mock.patch.object(pyroute2, 'NetNS')
def test_delete_entry(self, mock_netns):
mock_netns_instance = mock_netns.return_value
mock_netns_enter = mock_netns_instance.__enter__.return_value
mock_netns_enter.link_lookup.return_value = [1]
self.neigh_cmd.delete('192.168.45.100', 'cc:dd:ee:ff:ab:cd')
mock_netns_enter.link_lookup.assert_called_once_with(ifname='tap0')
mock_netns_enter.neigh.assert_called_once_with(
'delete',
dst='192.168.45.100',
lladdr='cc:dd:ee:ff:ab:cd',
family=2,
ifindex=1)
@mock.patch.object(priv_lib, '_run_iproute_neigh')
def test_delete_entry_not_exist(self, mock_run_iproute):
# trying to delete a non-existent entry shouldn't raise an error
mock_run_iproute.side_effect = NetlinkError(errno.ENOENT, None)
self.neigh_cmd.delete('192.168.45.100', 'cc:dd:ee:ff:ab:cd')
@mock.patch.object(pyroute2, 'NetNS')
def test_dump_entries(self, mock_netns):
mock_netns_instance = mock_netns.return_value
mock_netns_enter = mock_netns_instance.__enter__.return_value
mock_netns_enter.link_lookup.return_value = [1]
self.neigh_cmd.dump(4)
mock_netns_enter.link_lookup.assert_called_once_with(ifname='tap0')
mock_netns_enter.neigh.assert_called_once_with(
'dump',
family=2,
ifindex=1)
def test_flush(self):
self.neigh_cmd.flush(4, '192.168.0.1')
self._assert_sudo([4], ('flush', 'to', '192.168.0.1'))
class TestArpPing(TestIPCmdBase):
@mock.patch.object(ip_lib, 'IPWrapper')
@mock.patch('eventlet.spawn_n')
def test_send_ipv4_addr_adv_notif(self, spawn_n, mIPWrapper):
spawn_n.side_effect = lambda f: f()
ARPING_COUNT = 3
address = '20.0.0.1'
ip_lib.send_ip_addr_adv_notif(mock.sentinel.ns_name,
mock.sentinel.iface_name,
address,
ARPING_COUNT)
self.assertTrue(spawn_n.called)
mIPWrapper.assert_has_calls([
mock.call(namespace=mock.sentinel.ns_name),
mock.call().netns.execute(mock.ANY, extra_ok_codes=[1]),
mock.call().netns.execute(mock.ANY, extra_ok_codes=[1]),
mock.call().netns.execute(mock.ANY, extra_ok_codes=[1, 2]),
mock.call().netns.execute(mock.ANY, extra_ok_codes=[1, 2]),
mock.call().netns.execute(mock.ANY, extra_ok_codes=[1, 2]),
mock.call().netns.execute(mock.ANY, extra_ok_codes=[1, 2])])
ip_wrapper = mIPWrapper(namespace=mock.sentinel.ns_name)
# Just test that arping is called with the right arguments
for arg in ('-A', '-U'):
arping_cmd = ['arping', arg,
'-I', mock.sentinel.iface_name,
'-c', 1,
'-w', mock.ANY,
address]
ip_wrapper.netns.execute.assert_any_call(arping_cmd,
extra_ok_codes=mock.ANY)
@mock.patch.object(ip_lib, 'IPWrapper')
@mock.patch('eventlet.spawn_n')
def test_send_ipv4_addr_adv_notif_nodev(self, spawn_n, mIPWrapper):
spawn_n.side_effect = lambda f: f()
ip_wrapper = mIPWrapper(namespace=mock.sentinel.ns_name)
ip_wrapper.netns.execute.side_effect = RuntimeError
ARPING_COUNT = 3
address = '20.0.0.1'
with mock.patch.object(ip_lib, 'device_exists_with_ips_and_mac',
return_value=False):
ip_lib.send_ip_addr_adv_notif(mock.sentinel.ns_name,
mock.sentinel.iface_name,
address,
ARPING_COUNT)
# should return early with a single call when ENODEV
mIPWrapper.assert_has_calls([
mock.call(namespace=mock.sentinel.ns_name),
mock.call().netns.execute(mock.ANY, extra_ok_codes=[1])
] * 1)
@mock.patch('eventlet.spawn_n')
def test_no_ipv6_addr_notif(self, spawn_n):
ipv6_addr = 'fd00::1'
ip_lib.send_ip_addr_adv_notif(mock.sentinel.ns_name,
mock.sentinel.iface_name,
ipv6_addr,
3)
self.assertFalse(spawn_n.called)
class TestAddNamespaceToCmd(base.BaseTestCase):
def test_add_namespace_to_cmd_with_namespace(self):
cmd = ['ping', '8.8.8.8']
self.assertEqual(['ip', 'netns', 'exec', 'tmp'] + cmd,
ip_lib.add_namespace_to_cmd(cmd, 'tmp'))
def test_add_namespace_to_cmd_without_namespace(self):
cmd = ['ping', '8.8.8.8']
self.assertEqual(cmd, ip_lib.add_namespace_to_cmd(cmd, None))
class TestSetIpNonlocalBindForHaNamespace(base.BaseTestCase):
def test_setting_failure(self):
"""Make sure message is formatted correctly."""
with mock.patch.object(ip_lib, 'set_ip_nonlocal_bind', return_value=1):
ip_lib.set_ip_nonlocal_bind_for_namespace('foo', value=1)
class TestSysctl(base.BaseTestCase):
def setUp(self):
super(TestSysctl, self).setUp()
self.execute_p = mock.patch.object(ip_lib.IpNetnsCommand, 'execute')
self.execute = self.execute_p.start()
def test_disable_ipv6_when_ipv6_globally_enabled(self):
dev = ip_lib.IPDevice('tap0', 'ns1')
with mock.patch.object(ip_lib.ipv6_utils,
'is_enabled_and_bind_by_default',
return_value=True):
dev.disable_ipv6()
self.execute.assert_called_once_with(
['sysctl', '-w', 'net.ipv6.conf.tap0.disable_ipv6=1'],
log_fail_as_error=True, run_as_root=True)
def test_disable_ipv6_when_ipv6_globally_disabled(self):
dev = ip_lib.IPDevice('tap0', 'ns1')
with mock.patch.object(ip_lib.ipv6_utils,
'is_enabled_and_bind_by_default',
return_value=False):
dev.disable_ipv6()
self.assertFalse(self.execute.called)
class TestConntrack(base.BaseTestCase):
def setUp(self):
super(TestConntrack, self).setUp()
self.execute_p = mock.patch.object(ip_lib.IpNetnsCommand, 'execute')
self.execute = self.execute_p.start()
def test_delete_socket_conntrack_state(self):
device = ip_lib.IPDevice('tap0', 'ns1')
ip_str = '1.1.1.1'
dport = '3378'
protocol = 'tcp'
expect_cmd = ["conntrack", "-D", "-d", ip_str, '-p', protocol,
'--dport', dport]
device.delete_socket_conntrack_state(ip_str, dport, protocol)
self.execute.assert_called_once_with(expect_cmd, check_exit_code=True,
extra_ok_codes=[1])
class ParseIpRuleTestCase(base.BaseTestCase):
BASE_RULE = {
'family': 2, 'dst_len': 0, 'res2': 0, 'tos': 0, 'res1': 0, 'flags': 0,
'header': {
'pid': 18152, 'length': 44, 'flags': 2, 'error': None, 'type': 32,
'sequence_number': 281},
'attrs': {'FRA_TABLE': 255, 'FRA_SUPPRESS_PREFIXLEN': 4294967295},
'table': 255, 'action': 1, 'src_len': 0, 'event': 'RTM_NEWRULE'}
def setUp(self):
super(ParseIpRuleTestCase, self).setUp()
self.rule = copy.deepcopy(self.BASE_RULE)
def test_parse_priority(self):
self.rule['attrs']['FRA_PRIORITY'] = 1000
parsed_rule = ip_lib._parse_ip_rule(self.rule, 4)
self.assertEqual('1000', parsed_rule['priority'])
def test_parse_from_ipv4(self):
self.rule['attrs']['FRA_SRC'] = '192.168.0.1'
self.rule['src_len'] = 24
parsed_rule = ip_lib._parse_ip_rule(self.rule, 4)
self.assertEqual('192.168.0.1/24', parsed_rule['from'])
def test_parse_from_ipv6(self):
self.rule['attrs']['FRA_SRC'] = '2001:db8::1'
self.rule['src_len'] = 64
parsed_rule = ip_lib._parse_ip_rule(self.rule, 6)
self.assertEqual('2001:db8::1/64', parsed_rule['from'])
def test_parse_from_any_ipv4(self):
parsed_rule = ip_lib._parse_ip_rule(self.rule, 4)
self.assertEqual('0.0.0.0/0', parsed_rule['from'])
def test_parse_from_any_ipv6(self):
parsed_rule = ip_lib._parse_ip_rule(self.rule, 6)
self.assertEqual('::/0', parsed_rule['from'])
def test_parse_to_ipv4(self):
self.rule['attrs']['FRA_DST'] = '192.168.10.1'
self.rule['dst_len'] = 24
parsed_rule = ip_lib._parse_ip_rule(self.rule, 4)
self.assertEqual('192.168.10.1/24', parsed_rule['to'])
def test_parse_to_ipv6(self):
self.rule['attrs']['FRA_DST'] = '2001:db8::1'
self.rule['dst_len'] = 64
parsed_rule = ip_lib._parse_ip_rule(self.rule, 6)
self.assertEqual('2001:db8::1/64', parsed_rule['to'])
def test_parse_to_none(self):
parsed_rule = ip_lib._parse_ip_rule(self.rule, 4)
self.assertIsNone(parsed_rule.get('to'))
def test_parse_table(self):
self.rule['attrs']['FRA_TABLE'] = 255
parsed_rule = ip_lib._parse_ip_rule(self.rule, 4)
self.assertEqual('local', parsed_rule['table'])
self.rule['attrs']['FRA_TABLE'] = 254
parsed_rule = ip_lib._parse_ip_rule(self.rule, 4)
self.assertEqual('main', parsed_rule['table'])
self.rule['attrs']['FRA_TABLE'] = 253
parsed_rule = ip_lib._parse_ip_rule(self.rule, 4)
self.assertEqual('default', parsed_rule['table'])
self.rule['attrs']['FRA_TABLE'] = 1000
parsed_rule = ip_lib._parse_ip_rule(self.rule, 4)
self.assertEqual('1000', parsed_rule['table'])
def test_parse_fwmark(self):
self.rule['attrs']['FRA_FWMARK'] = 1000
self.rule['attrs']['FRA_FWMASK'] = 10
parsed_rule = ip_lib._parse_ip_rule(self.rule, 4)
self.assertEqual('0x3e8/0xa', parsed_rule['fwmark'])
def test_parse_fwmark_none(self):
parsed_rule = ip_lib._parse_ip_rule(self.rule, 4)
self.assertIsNone(parsed_rule.get('fwmark'))
def test_parse_iif(self):
self.rule['attrs']['FRA_IIFNAME'] = 'input_interface_name'
parsed_rule = ip_lib._parse_ip_rule(self.rule, 4)
self.assertEqual('input_interface_name', parsed_rule['iif'])
def test_parse_iif_none(self):
parsed_rule = ip_lib._parse_ip_rule(self.rule, 4)
self.assertIsNone(parsed_rule.get('iif'))
def test_parse_oif(self):
self.rule['attrs']['FRA_OIFNAME'] = 'output_interface_name'
parsed_rule = ip_lib._parse_ip_rule(self.rule, 4)
self.assertEqual('output_interface_name', parsed_rule['oif'])
def test_parse_oif_none(self):
parsed_rule = ip_lib._parse_ip_rule(self.rule, 4)
self.assertIsNone(parsed_rule.get('oif'))
class ListIpRulesTestCase(base.BaseTestCase):
def test_list_ip_rules(self):
rule1 = {'family': 2, 'src_len': 24, 'action': 1,
'attrs': {'FRA_SRC': '10.0.0.1', 'FRA_TABLE': 100}}
rule2 = {'family': 2, 'src_len': 0, 'action': 6,
'attrs': {'FRA_TABLE': 255}}
rules = [rule1, rule2]
with mock.patch.object(priv_lib, 'list_ip_rules') as mock_list_rules:
mock_list_rules.return_value = rules
retval = ip_lib.list_ip_rules(mock.ANY, 4)
reference = [
{'type': 'unicast', 'from': '10.0.0.1/24', 'priority': '0',
'table': '100'},
{'type': 'blackhole', 'from': '0.0.0.0/0', 'priority': '0',
'table': 'local'}]
self.assertEqual(reference, retval)
class GetDevicesInfoTestCase(base.BaseTestCase):
DEVICE_LO = {
'index': 2,
'attrs': (('IFLA_IFNAME', 'lo'), ('IFLA_OPERSTATE', 'UP'),
('IFLA_LINKMODE', 0), ('IFLA_MTU', 1000),
('IFLA_PROMISCUITY', 0),
('IFLA_ADDRESS', '5a:76:ed:cc:ce:90'),
('IFLA_BROADCAST', 'ff:ff:ff:ff:ff:f0'), )
}
DEVICE_DUMMY = {
'index': 2,
'attrs': (('IFLA_IFNAME', 'int_01'), ('IFLA_OPERSTATE', 'DOWN'),
('IFLA_LINKMODE', 0), ('IFLA_MTU', 1500),
('IFLA_PROMISCUITY', 0),
('IFLA_ADDRESS', '5a:76:ed:cc:ce:90'),
('IFLA_BROADCAST', 'ff:ff:ff:ff:ff:f0'),
('IFLA_LINKINFO', {
'attrs': (('IFLA_INFO_KIND', 'dummy'), )}))
}
DEVICE_VLAN = {
'index': 5,
'attrs': (('IFLA_IFNAME', 'int_02'), ('IFLA_OPERSTATE', 'DOWN'),
('IFLA_LINKMODE', 0), ('IFLA_MTU', 1400),
('IFLA_PROMISCUITY', 0),
('IFLA_ADDRESS', '5a:76:ed:cc:ce:91'),
('IFLA_BROADCAST', 'ff:ff:ff:ff:ff:f1'),
('IFLA_LINK', 2),
('IFLA_LINKINFO', {'attrs': (
('IFLA_INFO_KIND', 'vlan'),
('IFLA_INFO_DATA', {'attrs': (('IFLA_VLAN_ID', 1000), )})
)}))
}
DEVICE_VXLAN = {
'index': 9,
'attrs': (('IFLA_IFNAME', 'int_03'), ('IFLA_OPERSTATE', 'UP'),
('IFLA_LINKMODE', 0), ('IFLA_MTU', 1300),
('IFLA_PROMISCUITY', 0),
('IFLA_ADDRESS', '5a:76:ed:cc:ce:92'),
('IFLA_BROADCAST', 'ff:ff:ff:ff:ff:f2'),
('IFLA_LINKINFO', {'attrs': (
('IFLA_INFO_KIND', 'vxlan'),
('IFLA_INFO_DATA', {'attrs': (
('IFLA_VXLAN_ID', 1001),
('IFLA_VXLAN_GROUP', '239.1.1.1'),
('IFLA_VXLAN_LINK', 2))})
)}))
}
DEVICE_VETH = {
'index': 11,
'attrs': (('IFLA_IFNAME', 'int_04'), ('IFLA_OPERSTATE', 'UP'),
('IFLA_LINKMODE', 0), ('IFLA_MTU', 900),
('IFLA_PROMISCUITY', 0),
('IFLA_ADDRESS', '5a:76:ed:cc:ce:93'),
('IFLA_BROADCAST', 'ff:ff:ff:ff:ff:f3'),
('IFLA_LINK', 30),
('IFLA_LINKINFO', {
'attrs': (('IFLA_INFO_KIND', 'veth'), )}))
}
def setUp(self):
super(GetDevicesInfoTestCase, self).setUp()
self.mock_getdevs = mock.patch.object(priv_lib,
'get_link_devices').start()
def test_get_devices_info_lo(self):
self.mock_getdevs.return_value = (self.DEVICE_LO, )
ret = ip_lib.get_devices_info('namespace')
expected = {'index': 2,
'name': 'lo',
'operstate': 'UP',
'linkmode': 0,
'mtu': 1000,
'promiscuity': 0,
'mac': '5a:76:ed:cc:ce:90',
'broadcast': 'ff:ff:ff:ff:ff:f0'}
self.assertEqual(expected, ret[0])
def test_get_devices_info_dummy(self):
self.mock_getdevs.return_value = (self.DEVICE_DUMMY, )
ret = ip_lib.get_devices_info('namespace')
expected = {'index': 2,
'name': 'int_01',
'operstate': 'DOWN',
'linkmode': 0,
'mtu': 1500,
'promiscuity': 0,
'mac': '5a:76:ed:cc:ce:90',
'broadcast': 'ff:ff:ff:ff:ff:f0',
'kind': 'dummy'}
self.assertEqual(expected, ret[0])
def test_get_devices_info_vlan(self):
self.mock_getdevs.return_value = (self.DEVICE_VLAN, self.DEVICE_DUMMY)
ret = ip_lib.get_devices_info('namespace')
expected = {'index': 5,
'name': 'int_02',
'operstate': 'DOWN',
'linkmode': 0,
'mtu': 1400,
'promiscuity': 0,
'mac': '5a:76:ed:cc:ce:91',
'broadcast': 'ff:ff:ff:ff:ff:f1',
'kind': 'vlan',
'vlan_id': 1000,
'parent_index': 2,
'parent_name': 'int_01'}
for device in (device for device in ret if device['kind'] == 'vlan'):
self.assertEqual(expected, device)
break
else:
self.fail('No VLAN device found')
def test_get_devices_info_vxlan(self):
self.mock_getdevs.return_value = (self.DEVICE_VXLAN, self.DEVICE_DUMMY)
ret = ip_lib.get_devices_info('namespace')
expected = {'index': 9,
'name': 'int_03',
'operstate': 'UP',
'linkmode': 0,
'mtu': 1300,
'promiscuity': 0,
'mac': '5a:76:ed:cc:ce:92',
'broadcast': 'ff:ff:ff:ff:ff:f2',
'kind': 'vxlan',
'vxlan_id': 1001,
'vxlan_group': '239.1.1.1',
'vxlan_link_index': 2,
'vxlan_link_name': 'int_01'}
for device in (device for device in ret if device['kind'] == 'vxlan'):
self.assertEqual(expected, device)
break
else:
self.fail('No VXLAN device found')
def test_get_devices_info_veth(self):
self.mock_getdevs.return_value = (self.DEVICE_VETH, self.DEVICE_DUMMY)
ret = ip_lib.get_devices_info('namespace')
expected = {'index': 11,
'name': 'int_04',
'operstate': 'UP',
'linkmode': 0,
'mtu': 900,
'promiscuity': 0,
'mac': '5a:76:ed:cc:ce:93',
'broadcast': 'ff:ff:ff:ff:ff:f3',
'kind': 'veth',
'parent_index': 30}
for device in (device for device in ret if device['kind'] == 'veth'):
self.assertEqual(expected, device)
break
else:
self.fail('No VETH device found')