b9287c0678
Fixes bug 1042104 The fix follows the patch that was done on the DHCP agent to enable the user to configure the usage of namespaces. In the event that namspaces are disabled the agent is limited to running only one router. The agent needs to define the router_id that is supported. The process in this case is: 1. create router 2. start agent with router id Change-Id: I2a71dc009c5aea285ff9f903b3faa99b0c9f820f
289 lines
11 KiB
Python
289 lines
11 KiB
Python
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
|
|
# Copyright 2012 Nicira, Inc.
|
|
# 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 time
|
|
import unittest
|
|
|
|
import mock
|
|
|
|
from quantum.agent.common import config
|
|
from quantum.agent import l3_agent
|
|
from quantum.agent.linux import interface
|
|
from quantum.db import l3_db
|
|
from quantum.tests.unit import test_api_v2
|
|
|
|
_uuid = test_api_v2._uuid
|
|
|
|
|
|
class TestBasicRouterOperations(unittest.TestCase):
|
|
|
|
def setUp(self):
|
|
self.conf = config.setup_conf()
|
|
self.conf.register_opts(l3_agent.L3NATAgent.OPTS)
|
|
self.conf.register_opts(interface.OPTS)
|
|
self.conf.set_override('interface_driver',
|
|
'quantum.agent.linux.interface.NullDriver')
|
|
self.conf.root_helper = 'sudo'
|
|
|
|
self.device_exists_p = mock.patch(
|
|
'quantum.agent.linux.ip_lib.device_exists')
|
|
self.device_exists = self.device_exists_p.start()
|
|
|
|
self.utils_exec_p = mock.patch(
|
|
'quantum.agent.linux.utils.execute')
|
|
self.utils_exec = self.utils_exec_p.start()
|
|
|
|
self.dvr_cls_p = mock.patch('quantum.agent.linux.interface.NullDriver')
|
|
driver_cls = self.dvr_cls_p.start()
|
|
self.mock_driver = mock.MagicMock()
|
|
self.mock_driver.DEV_NAME_LEN = (
|
|
interface.LinuxInterfaceDriver.DEV_NAME_LEN)
|
|
driver_cls.return_value = self.mock_driver
|
|
|
|
self.ip_cls_p = mock.patch('quantum.agent.linux.ip_lib.IPWrapper')
|
|
ip_cls = self.ip_cls_p.start()
|
|
self.mock_ip = mock.MagicMock()
|
|
ip_cls.return_value = self.mock_ip
|
|
|
|
self.client_cls_p = mock.patch('quantumclient.v2_0.client.Client')
|
|
client_cls = self.client_cls_p.start()
|
|
self.client_inst = mock.Mock()
|
|
client_cls.return_value = self.client_inst
|
|
|
|
def tearDown(self):
|
|
self.device_exists_p.stop()
|
|
self.client_cls_p.stop()
|
|
self.ip_cls_p.stop()
|
|
self.dvr_cls_p.stop()
|
|
self.utils_exec_p.stop()
|
|
|
|
def testRouterInfoCreate(self):
|
|
id = _uuid()
|
|
ri = l3_agent.RouterInfo(id, self.conf.root_helper,
|
|
self.conf.use_namespaces)
|
|
|
|
self.assertTrue(ri.ns_name().endswith(id))
|
|
|
|
def testAgentCreate(self):
|
|
agent = l3_agent.L3NATAgent(self.conf)
|
|
|
|
self.device_exists.assert_has_calls(
|
|
[mock.call(self.conf.external_network_bridge)])
|
|
|
|
def _test_internal_network_action(self, action):
|
|
port_id = _uuid()
|
|
router_id = _uuid()
|
|
network_id = _uuid()
|
|
ri = l3_agent.RouterInfo(router_id, self.conf.root_helper,
|
|
self.conf.use_namespaces)
|
|
agent = l3_agent.L3NATAgent(self.conf)
|
|
interface_name = agent.get_internal_device_name(port_id)
|
|
cidr = '99.0.1.9/24'
|
|
mac = 'ca:fe:de:ad:be:ef'
|
|
ex_gw_port = {'fixed_ips': [{'ip_address': '20.0.0.30'}]}
|
|
|
|
if action == 'add':
|
|
self.device_exists.return_value = False
|
|
agent.internal_network_added(ri, ex_gw_port, network_id,
|
|
port_id, cidr, mac)
|
|
self.assertEquals(self.mock_driver.plug.call_count, 1)
|
|
self.assertEquals(self.mock_driver.init_l3.call_count, 1)
|
|
elif action == 'remove':
|
|
self.device_exists.return_value = True
|
|
agent.internal_network_removed(ri, ex_gw_port, port_id, cidr)
|
|
self.assertEquals(self.mock_driver.unplug.call_count, 1)
|
|
else:
|
|
raise Exception("Invalid action %s" % action)
|
|
|
|
def testAgentAddInternalNetwork(self):
|
|
self._test_internal_network_action('add')
|
|
|
|
def testAgentRemoveInternalNetwork(self):
|
|
self._test_internal_network_action('remove')
|
|
|
|
def _test_external_gateway_action(self, action):
|
|
router_id = _uuid()
|
|
ri = l3_agent.RouterInfo(router_id, self.conf.root_helper,
|
|
self.conf.use_namespaces)
|
|
agent = l3_agent.L3NATAgent(self.conf)
|
|
internal_cidrs = ['100.0.1.0/24', '200.74.0.0/16']
|
|
ex_gw_port = {'fixed_ips': [{'ip_address': '20.0.0.30',
|
|
'subnet_id': _uuid()}],
|
|
'subnet': {'gateway_ip': '20.0.0.1'},
|
|
'id': _uuid(),
|
|
'network_id': _uuid(),
|
|
'mac_address': 'ca:fe:de:ad:be:ef',
|
|
'ip_cidr': '20.0.0.30/24'}
|
|
|
|
if action == 'add':
|
|
self.device_exists.return_value = False
|
|
agent.external_gateway_added(ri, ex_gw_port, internal_cidrs)
|
|
self.assertEquals(self.mock_driver.plug.call_count, 1)
|
|
self.assertEquals(self.mock_driver.init_l3.call_count, 1)
|
|
self.assertEquals(self.mock_ip.netns.execute.call_count, 1)
|
|
|
|
elif action == 'remove':
|
|
self.device_exists.return_value = True
|
|
agent.external_gateway_removed(ri, ex_gw_port, internal_cidrs)
|
|
self.assertEquals(self.mock_driver.unplug.call_count, 1)
|
|
else:
|
|
raise Exception("Invalid action %s" % action)
|
|
|
|
def testAgentAddExternalGateway(self):
|
|
self._test_external_gateway_action('add')
|
|
|
|
def testAgentRemoveExternalGateway(self):
|
|
self._test_external_gateway_action('remove')
|
|
|
|
def _test_floating_ip_action(self, action):
|
|
router_id = _uuid()
|
|
ri = l3_agent.RouterInfo(router_id, self.conf.root_helper,
|
|
self.conf.use_namespaces)
|
|
agent = l3_agent.L3NATAgent(self.conf)
|
|
floating_ip = '20.0.0.100'
|
|
fixed_ip = '10.0.0.23'
|
|
ex_gw_port = {'fixed_ips': [{'ip_address': '20.0.0.30',
|
|
'subnet_id': _uuid()}],
|
|
'subnet': {'gateway_ip': '20.0.0.1'},
|
|
'id': _uuid(),
|
|
'mac_address': 'ca:fe:de:ad:be:ef',
|
|
'ip_cidr': '20.0.0.30/24'}
|
|
|
|
if action == 'add':
|
|
self.device_exists.return_value = False
|
|
agent.floating_ip_added(ri, ex_gw_port, floating_ip, fixed_ip)
|
|
|
|
elif action == 'remove':
|
|
self.device_exists.return_value = True
|
|
agent.floating_ip_removed(ri, ex_gw_port, floating_ip, fixed_ip)
|
|
else:
|
|
raise Exception("Invalid action %s" % action)
|
|
|
|
def testAgentAddFloatingIP(self):
|
|
self._test_floating_ip_action('add')
|
|
|
|
def testAgentRemoveFloatingIP(self):
|
|
self._test_floating_ip_action('remove')
|
|
|
|
def testProcessRouter(self):
|
|
|
|
agent = l3_agent.L3NATAgent(self.conf)
|
|
router_id = _uuid()
|
|
ri = l3_agent.RouterInfo(router_id, self.conf.root_helper,
|
|
self.conf.use_namespaces)
|
|
|
|
# return data so that state is built up
|
|
ex_gw_port = {'id': _uuid(),
|
|
'network_id': _uuid(),
|
|
'fixed_ips': [{'ip_address': '19.4.4.4',
|
|
'subnet_id': _uuid()}]}
|
|
internal_port = {'id': _uuid(),
|
|
'network_id': _uuid(),
|
|
'admin_state_up': True,
|
|
'fixed_ips': [{'ip_address': '35.4.4.4',
|
|
'subnet_id': _uuid()}],
|
|
'mac_address': 'ca:fe:de:ad:be:ef'}
|
|
|
|
def fake_list_ports1(**kwargs):
|
|
if kwargs['device_owner'] == l3_db.DEVICE_OWNER_ROUTER_GW:
|
|
return {'ports': [ex_gw_port]}
|
|
elif kwargs['device_owner'] == l3_db.DEVICE_OWNER_ROUTER_INTF:
|
|
return {'ports': [internal_port]}
|
|
|
|
fake_subnet = {'subnet': {'cidr': '19.4.4.0/24',
|
|
'gateway_ip': '19.4.4.1'}}
|
|
|
|
fake_floatingips1 = {'floatingips': [
|
|
{'id': _uuid(),
|
|
'floating_ip_address': '8.8.8.8',
|
|
'fixed_ip_address': '7.7.7.7',
|
|
'port_id': _uuid()}]}
|
|
|
|
self.client_inst.list_ports.side_effect = fake_list_ports1
|
|
self.client_inst.show_subnet.return_value = fake_subnet
|
|
self.client_inst.list_floatingips.return_value = fake_floatingips1
|
|
agent.process_router(ri)
|
|
|
|
# remap floating IP to a new fixed ip
|
|
fake_floatingips2 = copy.deepcopy(fake_floatingips1)
|
|
fake_floatingips2['floatingips'][0]['fixed_ip_address'] = '7.7.7.8'
|
|
self.client_inst.list_floatingips.return_value = fake_floatingips2
|
|
agent.process_router(ri)
|
|
|
|
# remove just the floating ips
|
|
self.client_inst.list_floatingips.return_value = {'floatingips': []}
|
|
agent.process_router(ri)
|
|
|
|
# now return no ports so state is torn down
|
|
self.client_inst.list_ports.return_value = {'ports': []}
|
|
agent.process_router(ri)
|
|
|
|
def testSingleLoopRouterRemoval(self):
|
|
agent = l3_agent.L3NATAgent(self.conf)
|
|
|
|
self.client_inst.list_ports.return_value = {'ports': []}
|
|
|
|
self.client_inst.list_routers.return_value = {'routers': [
|
|
{'id': _uuid()}]}
|
|
agent.do_single_loop()
|
|
|
|
self.client_inst.list_routers.return_value = {'routers': []}
|
|
agent.do_single_loop()
|
|
|
|
# verify that remove is called
|
|
self.assertEquals(self.mock_ip.get_devices.call_count, 1)
|
|
|
|
def testDaemonLoop(self):
|
|
|
|
# just take a pass through the loop, then raise on time.sleep()
|
|
time_sleep_p = mock.patch('time.sleep')
|
|
time_sleep = time_sleep_p.start()
|
|
|
|
class ExpectedException(Exception):
|
|
pass
|
|
|
|
time_sleep.side_effect = ExpectedException()
|
|
|
|
agent = l3_agent.L3NATAgent(self.conf)
|
|
self.assertRaises(ExpectedException, agent.daemon_loop)
|
|
|
|
time_sleep_p.stop()
|
|
|
|
def testDestroyNamespace(self):
|
|
|
|
class FakeDev(object):
|
|
def __init__(self, name):
|
|
self.name = name
|
|
|
|
self.mock_ip.get_namespaces.return_value = ['qrouter-foo']
|
|
self.mock_ip.get_devices.return_value = [FakeDev('qr-aaaa'),
|
|
FakeDev('qgw-aaaa')]
|
|
|
|
agent = l3_agent.L3NATAgent(self.conf)
|
|
agent._destroy_all_router_namespaces()
|
|
|
|
def testMain(self):
|
|
agent_mock_p = mock.patch('quantum.agent.l3_agent.L3NATAgent')
|
|
agent_mock = agent_mock_p.start()
|
|
agent_mock.daemon_loop.return_value = None
|
|
|
|
with mock.patch('quantum.agent.l3_agent.sys') as mock_sys:
|
|
mock_sys.argv = []
|
|
l3_agent.main()
|
|
|
|
agent_mock_p.stop()
|