Merge "Add functional test for l3-agent metadata proxy"
This commit is contained in:
commit
e7ac942150
@ -14,6 +14,8 @@
|
|||||||
|
|
||||||
import random
|
import random
|
||||||
|
|
||||||
|
import netaddr
|
||||||
|
|
||||||
from neutron.agent.linux import ip_lib
|
from neutron.agent.linux import ip_lib
|
||||||
from neutron.agent.linux import ovs_lib
|
from neutron.agent.linux import ovs_lib
|
||||||
from neutron.agent.linux import utils
|
from neutron.agent.linux import utils
|
||||||
@ -24,6 +26,7 @@ from neutron.tests.functional import base as functional_base
|
|||||||
|
|
||||||
|
|
||||||
BR_PREFIX = 'test-br'
|
BR_PREFIX = 'test-br'
|
||||||
|
PORT_PREFIX = 'test-port'
|
||||||
ICMP_BLOCK_RULE = '-p icmp -j DROP'
|
ICMP_BLOCK_RULE = '-p icmp -j DROP'
|
||||||
VETH_PREFIX = 'tst-vth'
|
VETH_PREFIX = 'tst-vth'
|
||||||
|
|
||||||
@ -49,6 +52,14 @@ class BaseLinuxTestCase(functional_base.BaseSudoTestCase):
|
|||||||
self.skipTest(skip_msg)
|
self.skipTest(skip_msg)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
def _create_namespace(self):
|
||||||
|
ip_cmd = ip_lib.IPWrapper(self.root_helper)
|
||||||
|
name = "func-%s" % uuidutils.generate_uuid()
|
||||||
|
namespace = ip_cmd.ensure_namespace(name)
|
||||||
|
self.addCleanup(namespace.netns.delete, namespace.namespace)
|
||||||
|
|
||||||
|
return namespace
|
||||||
|
|
||||||
def create_resource(self, name_prefix, creation_func, *args, **kwargs):
|
def create_resource(self, name_prefix, creation_func, *args, **kwargs):
|
||||||
"""Create a new resource that does not already exist.
|
"""Create a new resource that does not already exist.
|
||||||
|
|
||||||
@ -74,11 +85,27 @@ class BaseLinuxTestCase(functional_base.BaseSudoTestCase):
|
|||||||
veth1, veth2 = ip_wrapper.add_veth(name1, name2)
|
veth1, veth2 = ip_wrapper.add_veth(name1, name2)
|
||||||
return veth1, veth2
|
return veth1, veth2
|
||||||
|
|
||||||
|
def set_namespace_gateway(self, port_dev, gateway_ip):
|
||||||
|
"""Set gateway for the namespace associated to the port."""
|
||||||
|
if not port_dev.namespace:
|
||||||
|
self.fail('tests should not change test machine gateway')
|
||||||
|
port_dev.route.add_gateway(gateway_ip)
|
||||||
|
|
||||||
|
def shift_ip_cidr(self, ip_cidr, offset=1):
|
||||||
|
"""Shift ip_cidr offset times.
|
||||||
|
|
||||||
|
example: shift_ip_cidr("1.2.3.4/24", 2) ==> "1.2.3.6/24"
|
||||||
|
"""
|
||||||
|
net = netaddr.IPNetwork(ip_cidr)
|
||||||
|
net.value += offset
|
||||||
|
return str(net)
|
||||||
|
|
||||||
|
|
||||||
class BaseOVSLinuxTestCase(BaseLinuxTestCase):
|
class BaseOVSLinuxTestCase(BaseLinuxTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(BaseOVSLinuxTestCase, self).setUp()
|
super(BaseOVSLinuxTestCase, self).setUp()
|
||||||
self.ovs = ovs_lib.BaseOVS(self.root_helper)
|
self.ovs = ovs_lib.BaseOVS(self.root_helper)
|
||||||
|
self.ip = ip_lib.IPWrapper(self.root_helper)
|
||||||
|
|
||||||
def create_ovs_bridge(self, br_prefix=BR_PREFIX):
|
def create_ovs_bridge(self, br_prefix=BR_PREFIX):
|
||||||
br = self.create_resource(br_prefix, self.ovs.add_bridge)
|
br = self.create_resource(br_prefix, self.ovs.add_bridge)
|
||||||
@ -88,6 +115,29 @@ class BaseOVSLinuxTestCase(BaseLinuxTestCase):
|
|||||||
def get_ovs_bridge(self, br_name):
|
def get_ovs_bridge(self, br_name):
|
||||||
return ovs_lib.OVSBridge(br_name, self.root_helper)
|
return ovs_lib.OVSBridge(br_name, self.root_helper)
|
||||||
|
|
||||||
|
def create_ovs_port_in_ns(self, br, ns):
|
||||||
|
def create_port(name):
|
||||||
|
br.add_port(name)
|
||||||
|
self.addCleanup(br.delete_port, name)
|
||||||
|
br.set_db_attribute('Interface', name, 'type', 'internal')
|
||||||
|
return name
|
||||||
|
port_name = self.create_resource(PORT_PREFIX, create_port)
|
||||||
|
port_dev = self.ip.device(port_name)
|
||||||
|
ns.add_device_to_namespace(port_dev)
|
||||||
|
port_dev.link.set_up()
|
||||||
|
return port_dev
|
||||||
|
|
||||||
|
def bind_namespace_to_cidr(self, namespace, br, ip_cidr):
|
||||||
|
"""Bind namespace to cidr (on layer2 and 3).
|
||||||
|
|
||||||
|
Bind the namespace to a subnet by creating an ovs port in the namespace
|
||||||
|
and configuring port ip.
|
||||||
|
"""
|
||||||
|
net = netaddr.IPNetwork(ip_cidr)
|
||||||
|
port_dev = self.create_ovs_port_in_ns(br, namespace)
|
||||||
|
port_dev.addr.add(net.version, str(net), net.broadcast)
|
||||||
|
return port_dev
|
||||||
|
|
||||||
|
|
||||||
class BaseIPVethTestCase(BaseLinuxTestCase):
|
class BaseIPVethTestCase(BaseLinuxTestCase):
|
||||||
SRC_ADDRESS = '192.168.0.1'
|
SRC_ADDRESS = '192.168.0.1'
|
||||||
@ -104,14 +154,6 @@ class BaseIPVethTestCase(BaseLinuxTestCase):
|
|||||||
device.addr.add(ip_version=ip_version, cidr=cidr, broadcast=broadcast)
|
device.addr.add(ip_version=ip_version, cidr=cidr, broadcast=broadcast)
|
||||||
device.link.set_up()
|
device.link.set_up()
|
||||||
|
|
||||||
def _create_namespace(self):
|
|
||||||
ip_cmd = ip_lib.IPWrapper(self.root_helper)
|
|
||||||
name = "func-%s" % uuidutils.generate_uuid()
|
|
||||||
namespace = ip_cmd.ensure_namespace(name)
|
|
||||||
self.addCleanup(namespace.netns.delete, namespace.namespace)
|
|
||||||
|
|
||||||
return namespace
|
|
||||||
|
|
||||||
def prepare_veth_pairs(self, src_addr=None,
|
def prepare_veth_pairs(self, src_addr=None,
|
||||||
dst_addr=None,
|
dst_addr=None,
|
||||||
broadcast_addr=None,
|
broadcast_addr=None,
|
||||||
|
@ -19,11 +19,16 @@ import functools
|
|||||||
import fixtures
|
import fixtures
|
||||||
import mock
|
import mock
|
||||||
from oslo.config import cfg
|
from oslo.config import cfg
|
||||||
|
import webob
|
||||||
|
import webob.dec
|
||||||
|
import webob.exc
|
||||||
|
|
||||||
from neutron.agent.common import config as agent_config
|
from neutron.agent.common import config as agent_config
|
||||||
from neutron.agent.l3 import agent as l3_agent
|
from neutron.agent.l3 import agent as l3_agent
|
||||||
|
from neutron.agent.linux import dhcp
|
||||||
from neutron.agent.linux import external_process
|
from neutron.agent.linux import external_process
|
||||||
from neutron.agent.linux import ip_lib
|
from neutron.agent.linux import ip_lib
|
||||||
|
from neutron.agent.metadata import agent as metadata_agent
|
||||||
from neutron.common import config as common_config
|
from neutron.common import config as common_config
|
||||||
from neutron.common import constants as l3_constants
|
from neutron.common import constants as l3_constants
|
||||||
from neutron.openstack.common import log as logging
|
from neutron.openstack.common import log as logging
|
||||||
@ -36,6 +41,8 @@ from neutron.tests.unit import test_l3_agent
|
|||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
_uuid = uuidutils.generate_uuid
|
_uuid = uuidutils.generate_uuid
|
||||||
|
|
||||||
|
METADATA_REQUEST_TIMEOUT = 60
|
||||||
|
|
||||||
|
|
||||||
class L3AgentTestFramework(base.BaseOVSLinuxTestCase):
|
class L3AgentTestFramework(base.BaseOVSLinuxTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@ -363,3 +370,64 @@ class L3HATestFramework(L3AgentTestFramework):
|
|||||||
|
|
||||||
helpers.wait_until_true(lambda: router2.ha_state == 'master')
|
helpers.wait_until_true(lambda: router2.ha_state == 'master')
|
||||||
helpers.wait_until_true(lambda: router1.ha_state == 'fault')
|
helpers.wait_until_true(lambda: router1.ha_state == 'fault')
|
||||||
|
|
||||||
|
|
||||||
|
class MetadataFakeProxyHandler(object):
|
||||||
|
|
||||||
|
def __init__(self, status):
|
||||||
|
self.status = status
|
||||||
|
|
||||||
|
@webob.dec.wsgify()
|
||||||
|
def __call__(self, req):
|
||||||
|
return webob.Response(status=self.status)
|
||||||
|
|
||||||
|
|
||||||
|
class MetadataL3AgentTestCase(L3AgentTestFramework):
|
||||||
|
|
||||||
|
def _create_metadata_fake_server(self, status):
|
||||||
|
server = metadata_agent.UnixDomainWSGIServer('metadata-fake-server')
|
||||||
|
self.addCleanup(server.stop)
|
||||||
|
server.start(MetadataFakeProxyHandler(status),
|
||||||
|
self.agent.conf.metadata_proxy_socket,
|
||||||
|
workers=0, backlog=4096)
|
||||||
|
|
||||||
|
def test_access_to_metadata_proxy(self):
|
||||||
|
"""Test access to the l3-agent metadata proxy.
|
||||||
|
|
||||||
|
The test creates:
|
||||||
|
* A l3-agent metadata service:
|
||||||
|
* A router (which creates a metadata proxy in the router namespace),
|
||||||
|
* A fake metadata server
|
||||||
|
* A "client" namespace (simulating a vm) with a port on router
|
||||||
|
internal subnet.
|
||||||
|
|
||||||
|
The test queries from the "client" namespace the metadata proxy on
|
||||||
|
http://169.254.169.254 and asserts that the metadata proxy added
|
||||||
|
the X-Forwarded-For and X-Neutron-Router-Id headers to the request
|
||||||
|
and forwarded the http request to the fake metadata server and the
|
||||||
|
response to the "client" namespace.
|
||||||
|
"""
|
||||||
|
router_info = self.generate_router_info(enable_ha=False)
|
||||||
|
router = self.manage_router(self.agent, router_info)
|
||||||
|
self._create_metadata_fake_server(webob.exc.HTTPOk.code)
|
||||||
|
|
||||||
|
# Create and configure client namespace
|
||||||
|
client_ns = self._create_namespace()
|
||||||
|
router_ip_cidr = router.internal_ports[0]['ip_cidr']
|
||||||
|
ip_cidr = self.shift_ip_cidr(router_ip_cidr)
|
||||||
|
br_int = self.get_ovs_bridge(self.agent.conf.ovs_integration_bridge)
|
||||||
|
port = self.bind_namespace_to_cidr(client_ns, br_int, ip_cidr)
|
||||||
|
self.set_namespace_gateway(port, router_ip_cidr.partition('/')[0])
|
||||||
|
|
||||||
|
# Query metadata proxy
|
||||||
|
url = 'http://%(host)s:%(port)s' % {'host': dhcp.METADATA_DEFAULT_IP,
|
||||||
|
'port': dhcp.METADATA_PORT}
|
||||||
|
cmd = 'curl', '--max-time', METADATA_REQUEST_TIMEOUT, '-D-', url
|
||||||
|
try:
|
||||||
|
raw_headers = client_ns.netns.execute(cmd)
|
||||||
|
except RuntimeError:
|
||||||
|
self.fail('metadata proxy unreachable on %s before timeout' % url)
|
||||||
|
|
||||||
|
# Check status code
|
||||||
|
firstline = raw_headers.splitlines()[0]
|
||||||
|
self.assertIn(str(webob.exc.HTTPOk.code), firstline.split())
|
||||||
|
@ -13,3 +13,6 @@ kill_tox_python: KillFilter, root, $BASE_PATH/bin/python, -9
|
|||||||
|
|
||||||
# enable ping from namespace
|
# enable ping from namespace
|
||||||
ping_filter: CommandFilter, ping, root
|
ping_filter: CommandFilter, ping, root
|
||||||
|
|
||||||
|
# enable curl from namespace
|
||||||
|
curl_filter: CommandFilter, curl, root
|
||||||
|
Loading…
Reference in New Issue
Block a user