# iproute2 package that supports namespaces).
# use_namespaces = True
-# The DHCP server can assist with providing metadata support on isolated
-# networks. Setting this value to True will cause the DHCP server to append
-# specific host routes to the DHCP request. The metadata service will only
-# be activated when the subnet gateway_ip is None. The guest instance must
-# be configured to request host routes via DHCP (Option 121).
+# The DHCP server can assist with providing metadata support on isolated
+# networks. Setting this value to True will cause the DHCP server to append
+# specific host routes to the DHCP request. The metadata service will only
+# be activated when the subnet gateway_ip is None. The guest instance must
+# be configured to request host routes via DHCP (Option 121).
# enable_isolated_metadata = False
+# Allows for serving metadata requests coming from a dedicated metadata
+# access network whose cidr is 169.254.169.254/16 (or larger prefix), and
+# is connected to a Quantum router from which the VMs send metadata
+# request. In this case DHCP Option 121 will not be injected in VMs, as
+# they will be able to reach 169.254.169.254 through a router.
+# This option requires enable_isolated_metadata = True
+# enable_metadata_network = False
-[DEFAULT]
-
-# The following flag will cause a host route to the metadata server
-# to be injected into instances. The metadata server will be reached
-# via the dhcp server.
-metadata_dhcp_host_route = False
-
[DATABASE]
# This line MUST be changed to actually run the plugin.
# Example:
# is not specified. If it is empty or reference a non-existent cluster
# the first cluster specified in this configuration file will be used
# default_cluster_name =
+# The following flag enables the creation of a dedicated connection
+# to the metadata proxy for metadata server access via Quantum router
+# enable_metadata_access_network = True
#[CLUSTER:example]
# This is uuid of the default NVP Transport zone that will be used for
from quantum.agent.linux import interface
from quantum.agent.linux import ip_lib
from quantum.agent import rpc as agent_rpc
+from quantum.common import constants
from quantum.common import exceptions
from quantum.common import topics
from quantum import context
LOG = logging.getLogger(__name__)
NS_PREFIX = 'qdhcp-'
-METADATA_DEFAULT_IP = '169.254.169.254/16'
+METADATA_DEFAULT_PREFIX = 16
+METADATA_DEFAULT_IP = '169.254.169.254/%d' % METADATA_DEFAULT_PREFIX
METADATA_PORT = 80
cfg.BoolOpt('use_namespaces', default=True,
help=_("Allow overlapping IP.")),
cfg.BoolOpt('enable_isolated_metadata', default=False,
- help=_("Support Metadata requests on isolated networks."))
+ help=_("Support Metadata requests on isolated networks.")),
+ cfg.BoolOpt('enable_metadata_network', default=False,
+ help=_("Allows for serving metadata requests from a "
+ "dedicate network. Requires "
+ "enable isolated_metadata = True "))
]
def __init__(self, conf):
self.call_driver('reload_allocations', network)
def enable_isolated_metadata_proxy(self, network):
+
+ # The proxy might work for either a single network
+ # or all the networks connected via a router
+ # to the one passed as a parameter
+ quantum_lookup_param = '--network_id=%s' % network.id
+ meta_cidr = netaddr.IPNetwork(METADATA_DEFAULT_IP)
+ has_metadata_subnet = any(netaddr.IPNetwork(s.cidr) in meta_cidr
+ for s in network.subnets)
+ if (self.conf.enable_metadata_network and has_metadata_subnet):
+ router_ports = [port for port in network.ports
+ if (port.device_owner ==
+ constants.DEVICE_OWNER_ROUTER_INTF)]
+ if router_ports:
+ # Multiple router ports should not be allowed
+ if len(router_ports) > 1:
+ LOG.warning(_("%(port_num)d router ports found on the "
+ "metadata access network. Only the port "
+ "%(port_id)s, for router %(router_id)s "
+ "will be considered"),
+ {'port_num': len(router_ports),
+ 'port_id': router_ports[0].id,
+ 'router_id': router_ports[0].device_id})
+ quantum_lookup_param = ('--router_id=%s' %
+ router_ports[0].device_id)
+
def callback(pid_file):
return ['quantum-ns-metadata-proxy',
'--pid_file=%s' % pid_file,
- '--network_id=%s' % network.id,
+ quantum_lookup_param,
'--state_path=%s' % self.conf.state_path,
'--metadata_port=%d' % METADATA_PORT]
-
pm = external_process.ProcessManager(
self.conf,
network.id,
ip_cidr = '%s/%s' % (fixed_ip.ip_address, net.prefixlen)
ip_cidrs.append(ip_cidr)
- if self.conf.enable_isolated_metadata and self.conf.use_namespaces:
+ if (self.conf.enable_isolated_metadata and
+ self.conf.use_namespaces and
+ not self.conf.enable_metadata_network):
ip_cidrs.append(METADATA_DEFAULT_IP)
self.driver.init_l3(interface_name, ip_cidrs,
self.root_helper)
device.route.pullup_route(interface_name)
+ if self.conf.enable_metadata_network:
+ meta_cidr = netaddr.IPNetwork(METADATA_DEFAULT_IP)
+ metadata_subnets = [s for s in network.subnets if
+ netaddr.IPNetwork(s.cidr) in meta_cidr]
+ if metadata_subnets:
+ # Add a gateway so that packets can be routed back to VMs
+ device = ip_lib.IPDevice(interface_name,
+ self.root_helper,
+ namespace)
+ # Only 1 subnet on metadata access network
+ gateway_ip = metadata_subnets[0].gateway_ip
+ device.route.add_gateway(gateway_ip)
+
return interface_name
def destroy(self, network, device_name):
from quantum.extensions import providernet as pnet
from quantum.extensions import securitygroup as ext_sg
from quantum.openstack.common import rpc
+from quantum.plugins.nicira.nicira_nvp_plugin.common import (metadata_access
+ as nvp_meta)
from quantum.plugins.nicira.nicira_nvp_plugin.common import (securitygroups
as nvp_sec)
from quantum import policy
NVPCluster objects, 'plugin_config' is a dictionary with plugin
parameters (currently only 'max_lp_per_bridged_ls').
"""
- nvp_options = cfg.CONF.NVP
+ # Warn if metadata_dhcp_host_route option is specified
+ if cfg.CONF.metadata_dhcp_host_route:
+ LOG.warning(_("The metadata_dhcp_host_route is now obsolete, and "
+ "will have no effect. Instead, please set the "
+ "enable_isolated_metadata option in dhcp_agent.ini"))
nvp_conf = config.ClusterConfigOptions(cfg.CONF)
cluster_names = config.register_cluster_groups(nvp_conf)
nvp_conf.log_opt_values(LOG, logging.DEBUG)
'default_l3_gw_service_uuid':
nvp_conf[cluster_name].default_l3_gw_service_uuid})
LOG.debug(_("Cluster options:%s"), clusters_options)
- return nvp_options, clusters_options
+ return cfg.CONF.NVP, clusters_options
class NVPRpcCallbacks(dhcp_rpc_base.DhcpRpcCallbackMixin):
l3_db.L3_NAT_db_mixin,
portsecurity_db.PortSecurityDbMixin,
securitygroups_db.SecurityGroupDbMixin,
- nvp_sec.NVPSecurityGroups, qos_db.NVPQoSDbMixin):
+ nvp_sec.NVPSecurityGroups,
+ qos_db.NVPQoSDbMixin,
+ nvp_meta.NvpMetadataAccess):
"""
NvpPluginV2 is a Quantum plugin that provides L2 Virtual Network
functionality using NVP.
"logical network %s"), network.id)
raise nvp_exc.NvpNoMorePortsException(network=network.id)
- def _ensure_metadata_host_route(self, context, fixed_ip_data,
- is_delete=False):
- subnet = self._get_subnet(context, fixed_ip_data['subnet_id'])
- metadata_routes = [r for r in subnet.routes
- if r['destination'] == '169.254.169.254/32']
- if metadata_routes:
- # We should have only a single metadata route at any time
- # because the route logic forbids two routes with the same
- # destination. Update next hop with the provided IP address
- if not is_delete:
- metadata_routes[0].nexthop = fixed_ip_data['ip_address']
- else:
- context.session.delete(metadata_routes[0])
- else:
- # add the metadata route
- route = models_v2.Route(subnet_id=subnet.id,
- destination='169.254.169.254/32',
- nexthop=fixed_ip_data['ip_address'])
- context.session.add(route)
-
def setup_rpc(self):
# RPC support for dhcp
self.topic = topics.PLUGIN
with context.session.begin(subtransactions=True):
# First we allocate port in quantum database
quantum_db = super(NvpPluginV2, self).create_port(context, port)
- # If we have just created a dhcp port, and metadata request are
- # forwarded there, we need to verify the appropriate host route is
- # in place
- if (cfg.CONF.metadata_dhcp_host_route and
- (quantum_db.get('device_owner') ==
- constants.DEVICE_OWNER_DHCP)):
- if (quantum_db.get('fixed_ips') and
- len(quantum_db.get('fixed_ips'))):
- self._ensure_metadata_host_route(
- context, quantum_db.get('fixed_ips')[0])
# Update fields obtained from quantum db (eg: MAC address)
port["port"].update(quantum_db)
# port security extension checks
# copy values over
ret_port.update(port['port'])
- # TODO(salvatore-orlando): We might need transaction management
- # but the change for metadata support should not be too disruptive
- fixed_ip_data = port['port'].get('fixed_ips')
- if (cfg.CONF.metadata_dhcp_host_route and
- ret_port.get('device_owner') == constants.DEVICE_OWNER_DHCP
- and fixed_ip_data):
- self._ensure_metadata_host_route(context,
- fixed_ip_data[0],
- is_delete=True)
-
# populate port_security setting
if psec.PORTSECURITY not in port['port']:
ret_port[psec.PORTSECURITY] = self._get_port_security_binding(
order=NVP_EXTGW_NAT_RULES_ORDER,
match_criteria={'source_ip_addresses': subnet['cidr']})
+ # Ensure the NVP logical router has a connection to a 'metadata access'
+ # network (with a proxy listening on its DHCP port), by creating it
+ # if needed.
+ self._handle_metadata_access_network(context, router_id)
LOG.debug(_("Add_router_interface completed for subnet:%(subnet_id)s "
"and router:%(router_id)s"),
{'subnet_id': subnet_id, 'router_id': router_id})
{'q_port_id': port_id,
'nvp_port_id': lport['uuid']})
return
+
+ # Ensure the connection to the 'metadata access network'
+ # is removed (with the network) if this the last subnet
+ # on the router
+ self._handle_metadata_access_network(context, router_id)
try:
if not subnet:
subnet = self._get_subnet(context, subnet_id)
"(default -1 meaning do not time out)")),
cfg.StrOpt('default_cluster_name',
help=_("Default cluster name")),
+ cfg.BoolOpt('enable_metadata_access_network', default=True,
+ help=_("Enables dedicated connection to the metadata proxy "
+ "for metadata server access via Quantum router")),
]
cluster_opts = [
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 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 equired 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.
+#
+# @author: Salvatore Orlando, VMware
+
+import netaddr
+
+from quantum.api.v2 import attributes
+from quantum.common import constants
+from quantum.common import exceptions as q_exc
+from quantum.db import l3_db
+from quantum.openstack.common import cfg
+from quantum.openstack.common import log as logging
+from quantum.openstack.common.notifier import api as notifier_api
+from quantum.plugins.nicira.nicira_nvp_plugin.common import (exceptions
+ as nvp_exc)
+from quantum.plugins.nicira.nicira_nvp_plugin import NvpApiClient
+
+
+LOG = logging.getLogger(__name__)
+
+METADATA_DEFAULT_PREFIX = 30
+METADATA_SUBNET_CIDR = '169.254.169.252/%d' % METADATA_DEFAULT_PREFIX
+METADATA_GATEWAY_IP = '169.254.169.253'
+
+
+class NvpMetadataAccess(object):
+
+ def _find_metadata_port(self, context, ports):
+ for port in ports:
+ for fixed_ip in port['fixed_ips']:
+ cidr = netaddr.IPNetwork(
+ self.get_subnet(context, fixed_ip['subnet_id'])['cidr'])
+ if cidr in netaddr.IPNetwork(METADATA_SUBNET_CIDR):
+ return port
+
+ def _create_metadata_access_network(self, context, router_id):
+ # This will still ensure atomicity on Quantum DB
+ # context.elevated() creates a deep-copy context
+ ctx_elevated = context.elevated()
+ with ctx_elevated.session.begin(subtransactions=True):
+ # Add network
+ # Network name is likely to be truncated on NVP
+
+ net_data = {'name': ('meta-%s' % router_id)[:40],
+ 'tenant_id': '', # intentionally not set
+ 'admin_state_up': True,
+ 'port_security_enabled': False,
+ 'shared': False,
+ 'status': constants.NET_STATUS_ACTIVE}
+ meta_net = self.create_network(ctx_elevated,
+ {'network': net_data})
+ # Add subnet
+ subnet_data = {'network_id': meta_net['id'],
+ 'tenant_id': '', # intentionally not set
+ 'name': 'meta-%s' % router_id,
+ 'ip_version': 4,
+ 'shared': False,
+ 'cidr': METADATA_SUBNET_CIDR,
+ 'enable_dhcp': True,
+ # Ensure default allocation pool is generated
+ 'allocation_pools': attributes.ATTR_NOT_SPECIFIED,
+ 'gateway_ip': METADATA_GATEWAY_IP,
+ 'dns_nameservers': [],
+ 'host_routes': []}
+ meta_sub = self.create_subnet(ctx_elevated,
+ {'subnet': subnet_data})
+ self.add_router_interface(ctx_elevated, router_id,
+ {'subnet_id': meta_sub['id']})
+ # We need to send a notification to the dhcp agent in order
+ # to start the metadata agent proxy
+ # Note: the publisher id is the same used in the api module
+ notifier_api.notify(context,
+ notifier_api.publisher_id('network'),
+ 'network.create.end',
+ notifier_api.CONF.default_notification_level,
+ {'network': meta_net})
+
+ def _destroy_metadata_access_network(self, context, router_id, ports):
+
+ # context.elevated() creates a deep-copy context
+ ctx_elevated = context.elevated()
+ # This will still ensure atomicity on Quantum DB
+ with ctx_elevated.session.begin(subtransactions=True):
+ if ports:
+ meta_port = self._find_metadata_port(ctx_elevated, ports)
+ if not meta_port:
+ return
+ meta_net_id = meta_port['network_id']
+ self.remove_router_interface(
+ ctx_elevated, router_id, {'port_id': meta_port['id']})
+ # Remove network (this will remove the subnet too)
+ self.delete_network(ctx_elevated, meta_net_id)
+ # We need to send a notification to the dhcp agent in order
+ # to stop the metadata agent proxy
+ # Note: the publisher id is the same used in the api module
+ notifier_api.notify(
+ context,
+ notifier_api.publisher_id('network'),
+ 'network.delete.end',
+ notifier_api.CONF.default_notification_level,
+ {'network_id': meta_net_id})
+
+ def _handle_metadata_access_network(self, context, router_id):
+ if not cfg.CONF.NVP.enable_metadata_access_network:
+ LOG.debug(_("Metadata access network is disabled"))
+ return
+ # As we'll use a different device_owner for metadata interface
+ # this query will return only 'real' router interfaces
+ ctx_elevated = context.elevated()
+ device_filter = {'device_id': [router_id],
+ 'device_owner': [l3_db.DEVICE_OWNER_ROUTER_INTF]}
+ with ctx_elevated.session.begin(subtransactions=True):
+ ports = self.get_ports(ctx_elevated, filters=device_filter)
+ try:
+ if ports:
+ if not self._find_metadata_port(ctx_elevated, ports):
+ self._create_metadata_access_network(context,
+ router_id)
+ elif len(ports) == 1:
+ # The only port left if the metadata port
+ self._destroy_metadata_access_network(context,
+ router_id,
+ ports)
+ else:
+ LOG.debug(_("No router interface found for router '%s'. "
+ "No metadata access network should be "
+ "created or destroyed"), router_id)
+ # TODO(salvatore-orlando): A better exception handling in the
+ # NVP plugin would allow us to improve error handling here
+ except (q_exc.QuantumException, nvp_exc.NvpPluginException,
+ NvpApiClient.NvpApiException):
+ # Any exception here should be regarded as non-fatal
+ LOG.exception(_("An error occurred while operating on the "
+ "metadata access network for router:'%s'"),
+ router_id)
import mock
from oslo.config import cfg
+import netaddr
import webob.exc
import quantum.common.test_lib as test_lib
instance.return_value.get_nvp_version.return_value = "2.999"
instance.return_value.request.side_effect = _fake_request
super(NiciraPluginV2TestCase, self).setUp(self._plugin_name)
+ cfg.CONF.set_override('enable_metadata_access_network', False, 'NVP')
def tearDown(self):
self.fc.reset_all()
class TestNiciraL3NatTestCase(test_l3_plugin.L3NatDBTestCase,
NiciraPluginV2TestCase):
- def test_floatingip_with_assoc_fails(self):
- self._test_floatingip_with_assoc_fails(
- 'quantum.plugins.nicira.nicira_nvp_plugin.'
- 'QuantumPlugin.NvpPluginV2')
+ def test_floatingip_with_assoc_fails(self):
+ self._test_floatingip_with_assoc_fails(
+ 'quantum.plugins.nicira.nicira_nvp_plugin.'
+ 'QuantumPlugin.NvpPluginV2')
+
+ def _nvp_metadata_setup(self):
+ cfg.CONF.set_override('enable_metadata_access_network', True, 'NVP')
+
+ def _nvp_metadata_teardown(self):
+ cfg.CONF.set_override('enable_metadata_access_network', False, 'NVP')
+
+ def test_router_add_interface_subnet_with_metadata_access(self):
+ self._nvp_metadata_setup()
+ notifications = ['router.create.start',
+ 'router.create.end',
+ 'network.create.start',
+ 'network.create.end',
+ 'subnet.create.start',
+ 'subnet.create.end',
+ 'router.interface.create',
+ 'network.create.end',
+ 'router.interface.create',
+ 'router.interface.delete',
+ 'router.interface.delete',
+ 'network.delete.end']
+ self.test_router_add_interface_subnet(exp_notifications=notifications)
+ self._nvp_metadata_teardown()
+
+ def test_router_add_interface_port_with_metadata_access(self):
+ self._nvp_metadata_setup()
+ self.test_router_add_interface_port()
+ self._nvp_metadata_teardown()
+
+ def test_router_add_interface_dupsubnet_returns_400_with_metadata(self):
+ self._nvp_metadata_setup()
+ self.test_router_add_interface_dup_subnet1_returns_400()
+ self._nvp_metadata_teardown()
+
+ def test_router_add_interface_overlapped_cidr_returns_400_with(self):
+ self._nvp_metadata_setup()
+ self.test_router_add_interface_overlapped_cidr_returns_400()
+ self._nvp_metadata_teardown()
+
+ def test_router_remove_interface_inuse_returns_409_with_metadata(self):
+ self._nvp_metadata_setup()
+ self.test_router_remove_interface_inuse_returns_409()
+ self._nvp_metadata_teardown()
+
+ def test_router_remove_iface_wrong_sub_returns_409_with_metadata(self):
+ self._nvp_metadata_setup()
+ self.test_router_remove_interface_wrong_subnet_returns_409()
+ self._nvp_metadata_teardown()
+
+ def test_router_delete_with_metadata_access(self):
+ self._nvp_metadata_setup()
+ self.test_router_delete()
+ self._nvp_metadata_teardown()
+
+ def test_router_delete_with_port_existed_returns_409_with_metadata(self):
+ self._nvp_metadata_setup()
+ self.test_router_delete_with_port_existed_returns_409()
+ self._nvp_metadata_teardown()
+
+ def test_metadatata_network_created_with_router_interface_add(self):
+ self._nvp_metadata_setup()
+ with self.router() as r:
+ with self.subnet() as s:
+ body = self._router_interface_action('add',
+ r['router']['id'],
+ s['subnet']['id'],
+ None)
+ r_ports = self._list('ports')['ports']
+ self.assertEqual(len(r_ports), 2)
+ ips = []
+ for port in r_ports:
+ ips.extend([netaddr.IPAddress(fixed_ip['ip_address'])
+ for fixed_ip in port['fixed_ips']])
+ meta_cidr = netaddr.IPNetwork('169.254.0.0/16')
+ self.assertTrue(any([ip in meta_cidr for ip in ips]))
+ # Needed to avoid 409
+ body = self._router_interface_action('remove',
+ r['router']['id'],
+ s['subnet']['id'],
+ None)
+ self._nvp_metadata_teardown()
+
+ def test_metadatata_network_removed_with_router_interface_remove(self):
+ self._nvp_metadata_setup()
+ with self.router() as r:
+ with self.subnet() as s:
+ self._router_interface_action('add', r['router']['id'],
+ s['subnet']['id'], None)
+ subnets = self._list('subnets')['subnets']
+ self.assertEqual(len(subnets), 2)
+ meta_cidr = netaddr.IPNetwork('169.254.0.0/16')
+ for subnet in subnets:
+ cidr = netaddr.IPNetwork(subnet['cidr'])
+ if meta_cidr == cidr or meta_cidr in cidr.supernet(16):
+ meta_sub_id = subnet['id']
+ meta_net_id = subnet['network_id']
+ ports = self._list(
+ 'ports',
+ query_params='network_id=%s' % meta_net_id)['ports']
+ self.assertEqual(len(ports), 1)
+ meta_port_id = ports[0]['id']
+ self._router_interface_action('remove', r['router']['id'],
+ s['subnet']['id'], None)
+ self._show('networks', meta_net_id,
+ webob.exc.HTTPNotFound.code)
+ self._show('ports', meta_port_id,
+ webob.exc.HTTPNotFound.code)
+ self._show('subnets', meta_sub_id,
+ webob.exc.HTTPNotFound.code)
+ self._nvp_metadata_teardown()
class NvpQoSTestExtensionManager(object):
# 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 os
import socket
import sys
from quantum.agent.common import config
from quantum.agent import dhcp_agent
from quantum.agent.linux import interface
+from quantum.common import constants
from quantum.common import exceptions
from quantum.openstack.common import jsonutils
fake_subnet2 = FakeModel('dddddddd-dddd-dddd-dddddddddddd',
network_id='12345678-1234-5678-1234567890ab',
- enable_dhcp=False)
+ cidr='172.9.9.0/24', enable_dhcp=False)
fake_subnet3 = FakeModel('bbbbbbbb-1111-2222-bbbbbbbbbbbb',
network_id='12345678-1234-5678-1234567890ab',
cidr='192.168.1.1/24', enable_dhcp=True)
+fake_meta_subnet = FakeModel('bbbbbbbb-1111-2222-bbbbbbbbbbbb',
+ network_id='12345678-1234-5678-1234567890ab',
+ cidr='169.254.169.252/30',
+ gateway_ip='169.254.169.253', enable_dhcp=True)
+
fake_fixed_ip = FakeModel('', subnet=fake_subnet1, ip_address='172.9.9.9')
+fake_meta_fixed_ip = FakeModel('', subnet=fake_meta_subnet,
+ ip_address='169.254.169.254')
fake_port1 = FakeModel('12345678-1234-aaaa-1234567890ab',
mac_address='aa:bb:cc:dd:ee:ff',
mac_address='aa:bb:cc:dd:ee:99',
network_id='12345678-1234-5678-1234567890ab')
+fake_meta_port = FakeModel('12345678-1234-aaaa-1234567890ab',
+ mac_address='aa:bb:cc:dd:ee:ff',
+ network_id='12345678-1234-5678-1234567890ab',
+ device_owner=constants.DEVICE_OWNER_ROUTER_INTF,
+ device_id='forzanapoli',
+ fixed_ips=[fake_meta_fixed_ip])
+
fake_network = FakeModel('12345678-1234-5678-1234567890ab',
tenant_id='aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa',
admin_state_up=True,
subnets=[fake_subnet1, fake_subnet2],
ports=[fake_port1])
+fake_meta_network = FakeModel('12345678-1234-5678-1234567890ab',
+ tenant_id='aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa',
+ admin_state_up=True,
+ subnets=[fake_meta_subnet],
+ ports=[fake_meta_port])
+
fake_down_network = FakeModel('12345678-dddd-dddd-1234567890ab',
tenant_id='aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa',
admin_state_up=False,
mock.call().disable()
])
+ def test_enable_isolated_metadata_proxy_with_metadata_network(self):
+ cfg.CONF.set_override('enable_metadata_network', True)
+ class_path = 'quantum.agent.linux.ip_lib.IPWrapper'
+ self.external_process_p.stop()
+ # Ensure the mock is restored if this test fail
+ try:
+ with mock.patch(class_path) as ip_wrapper:
+ self.dhcp.enable_isolated_metadata_proxy(fake_meta_network)
+ ip_wrapper.assert_has_calls([mock.call(
+ 'sudo',
+ 'qdhcp-12345678-1234-5678-1234567890ab'),
+ mock.call().netns.execute(['quantum-ns-metadata-proxy',
+ mock.ANY,
+ '--router_id=forzanapoli',
+ mock.ANY,
+ mock.ANY])
+ ])
+ finally:
+ self.external_process_p.start()
+ cfg.CONF.set_override('enable_metadata_network', False)
+
def test_network_create_end(self):
payload = dict(network=dict(id=fake_network.id))
self.device_exists = self.device_exists_p.start()
self.dvr_cls_p = mock.patch('quantum.agent.linux.interface.NullDriver')
+ self.iproute_cls_p = mock.patch('quantum.agent.linux.'
+ 'ip_lib.IpRouteCommand')
driver_cls = self.dvr_cls_p.start()
+ iproute_cls = self.iproute_cls_p.start()
self.mock_driver = mock.MagicMock()
self.mock_driver.DEV_NAME_LEN = (
interface.LinuxInterfaceDriver.DEV_NAME_LEN)
+ self.mock_iproute = mock.MagicMock()
driver_cls.return_value = self.mock_driver
+ iproute_cls.return_value = self.mock_iproute
def tearDown(self):
self.dvr_cls_p.stop()
self.device_exists_p.stop()
+ self.iproute_cls_p.stop()
cfg.CONF.reset()
- def _test_setup_helper(self, device_exists, reuse_existing=False):
+ def _test_setup_helper(self, device_exists, reuse_existing=False,
+ metadata_access_network=False,
+ net=None, port=None):
+ net = net or fake_network
+ port = port or fake_port1
plugin = mock.Mock()
- plugin.get_dhcp_port.return_value = fake_port1
+ plugin.get_dhcp_port.return_value = port or fake_port1
self.device_exists.return_value = device_exists
self.mock_driver.get_device_name.return_value = 'tap12345678-12'
dh = dhcp_agent.DeviceManager(cfg.CONF, plugin)
- interface_name = dh.setup(fake_network, reuse_existing)
+ interface_name = dh.setup(net, reuse_existing)
self.assertEqual(interface_name, 'tap12345678-12')
plugin.assert_has_calls([
- mock.call.get_dhcp_port(fake_network.id, mock.ANY)])
+ mock.call.get_dhcp_port(net.id, mock.ANY)])
- namespace = dhcp_agent.NS_PREFIX + fake_network.id
+ namespace = dhcp_agent.NS_PREFIX + net.id
+ if metadata_access_network:
+ expected_ips = ['169.254.169.254/30']
+ else:
+ expected_ips = ['172.9.9.9/24', '169.254.169.254/16']
expected = [mock.call.init_l3('tap12345678-12',
- ['172.9.9.9/24', '169.254.169.254/16'],
+ expected_ips,
namespace=namespace)]
if not reuse_existing:
expected.insert(0,
- mock.call.plug(fake_network.id,
- fake_port1.id,
+ mock.call.plug(net.id,
+ port.id,
'tap12345678-12',
'aa:bb:cc:dd:ee:ff',
namespace=namespace))
+ if metadata_access_network:
+ self.mock_iproute.assert_has_calls(
+ [mock.call.add_gateway('169.254.169.253')])
self.mock_driver.assert_has_calls(expected)
def test_setup_device_exists_reuse(self):
self._test_setup_helper(True, True)
+ def test_setup_with_metadata_access_network(self):
+ cfg.CONF.set_override('enable_metadata_network', True)
+ self._test_setup_helper(False, metadata_access_network=True,
+ net=fake_meta_network, port=fake_meta_port)
+
def test_destroy(self):
fake_network = FakeModel('12345678-1234-5678-1234567890ab',
tenant_id='aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa')
fip['floatingip']['router_id'], None,
expected_code=exc.HTTPConflict.code)
- def test_router_add_interface_subnet(self):
+ def test_router_add_interface_subnet(self, exp_notifications=None):
+ if not exp_notifications:
+ exp_notifications = ['router.create.start',
+ 'router.create.end',
+ 'network.create.start',
+ 'network.create.end',
+ 'subnet.create.start',
+ 'subnet.create.end',
+ 'router.interface.create',
+ 'router.interface.delete']
with self.router() as r:
with self.subnet() as s:
body = self._router_interface_action('add',
body = self._show('ports', r_port_id,
expected_code=exc.HTTPNotFound.code)
- self.assertEqual(len(test_notifier.NOTIFICATIONS), 8)
self.assertEqual(
set(n['event_type'] for n in test_notifier.NOTIFICATIONS),
- set(['router.create.start',
- 'router.create.end',
- 'network.create.start',
- 'network.create.end',
- 'subnet.create.start',
- 'subnet.create.end',
- 'router.interface.create',
- 'router.interface.delete']))
+ set(exp_notifications))
def test_router_add_interface_subnet_with_bad_tenant_returns_404(self):
with mock.patch('quantum.context.Context.to_dict') as tdict: