Metadata support for NVP plugin
Bug #1121119 Allows the NVP plugin to leverage the metadata proxy, by creating an ad-hoc topology for allowing access to a metadata proxy from a NVP router leveraging existing agents. This patch also removes previous code for metadata support in the NVP plugin, which was based on DHCP Option 121. This is now provided by the dhcp agent as well. Change-Id: If37ef388e063f40bb06908ee2f72c431f29dac31
This commit is contained in:
parent
d7c0e3bacd
commit
0b0362c40b
@ -30,9 +30,16 @@ dhcp_driver = quantum.agent.linux.dhcp.Dnsmasq
|
|||||||
# iproute2 package that supports namespaces).
|
# iproute2 package that supports namespaces).
|
||||||
# use_namespaces = True
|
# use_namespaces = True
|
||||||
|
|
||||||
# The DHCP server can assist with providing metadata support on isolated
|
# The DHCP server can assist with providing metadata support on isolated
|
||||||
# networks. Setting this value to True will cause the DHCP server to append
|
# 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
|
# 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 activated when the subnet gateway_ip is None. The guest instance must
|
||||||
# be configured to request host routes via DHCP (Option 121).
|
# be configured to request host routes via DHCP (Option 121).
|
||||||
# enable_isolated_metadata = False
|
# 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
|
||||||
|
@ -1,10 +1,3 @@
|
|||||||
[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]
|
[DATABASE]
|
||||||
# This line MUST be changed to actually run the plugin.
|
# This line MUST be changed to actually run the plugin.
|
||||||
# Example:
|
# Example:
|
||||||
@ -39,6 +32,9 @@ reconnect_interval = 2
|
|||||||
# is not specified. If it is empty or reference a non-existent cluster
|
# is not specified. If it is empty or reference a non-existent cluster
|
||||||
# the first cluster specified in this configuration file will be used
|
# the first cluster specified in this configuration file will be used
|
||||||
# default_cluster_name =
|
# 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]
|
#[CLUSTER:example]
|
||||||
# This is uuid of the default NVP Transport zone that will be used for
|
# This is uuid of the default NVP Transport zone that will be used for
|
||||||
|
@ -29,6 +29,7 @@ from quantum.agent.linux import external_process
|
|||||||
from quantum.agent.linux import interface
|
from quantum.agent.linux import interface
|
||||||
from quantum.agent.linux import ip_lib
|
from quantum.agent.linux import ip_lib
|
||||||
from quantum.agent import rpc as agent_rpc
|
from quantum.agent import rpc as agent_rpc
|
||||||
|
from quantum.common import constants
|
||||||
from quantum.common import exceptions
|
from quantum.common import exceptions
|
||||||
from quantum.common import topics
|
from quantum.common import topics
|
||||||
from quantum import context
|
from quantum import context
|
||||||
@ -40,7 +41,8 @@ from quantum.openstack.common import uuidutils
|
|||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
NS_PREFIX = 'qdhcp-'
|
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
|
METADATA_PORT = 80
|
||||||
|
|
||||||
|
|
||||||
@ -54,7 +56,11 @@ class DhcpAgent(object):
|
|||||||
cfg.BoolOpt('use_namespaces', default=True,
|
cfg.BoolOpt('use_namespaces', default=True,
|
||||||
help=_("Allow overlapping IP.")),
|
help=_("Allow overlapping IP.")),
|
||||||
cfg.BoolOpt('enable_isolated_metadata', default=False,
|
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):
|
def __init__(self, conf):
|
||||||
@ -245,13 +251,37 @@ class DhcpAgent(object):
|
|||||||
self.call_driver('reload_allocations', network)
|
self.call_driver('reload_allocations', network)
|
||||||
|
|
||||||
def enable_isolated_metadata_proxy(self, 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):
|
def callback(pid_file):
|
||||||
return ['quantum-ns-metadata-proxy',
|
return ['quantum-ns-metadata-proxy',
|
||||||
'--pid_file=%s' % pid_file,
|
'--pid_file=%s' % pid_file,
|
||||||
'--network_id=%s' % network.id,
|
quantum_lookup_param,
|
||||||
'--state_path=%s' % self.conf.state_path,
|
'--state_path=%s' % self.conf.state_path,
|
||||||
'--metadata_port=%d' % METADATA_PORT]
|
'--metadata_port=%d' % METADATA_PORT]
|
||||||
|
|
||||||
pm = external_process.ProcessManager(
|
pm = external_process.ProcessManager(
|
||||||
self.conf,
|
self.conf,
|
||||||
network.id,
|
network.id,
|
||||||
@ -480,7 +510,9 @@ class DeviceManager(object):
|
|||||||
ip_cidr = '%s/%s' % (fixed_ip.ip_address, net.prefixlen)
|
ip_cidr = '%s/%s' % (fixed_ip.ip_address, net.prefixlen)
|
||||||
ip_cidrs.append(ip_cidr)
|
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)
|
ip_cidrs.append(METADATA_DEFAULT_IP)
|
||||||
|
|
||||||
self.driver.init_l3(interface_name, ip_cidrs,
|
self.driver.init_l3(interface_name, ip_cidrs,
|
||||||
@ -492,6 +524,19 @@ class DeviceManager(object):
|
|||||||
self.root_helper)
|
self.root_helper)
|
||||||
device.route.pullup_route(interface_name)
|
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
|
return interface_name
|
||||||
|
|
||||||
def destroy(self, network, device_name):
|
def destroy(self, network, device_name):
|
||||||
|
@ -47,6 +47,8 @@ from quantum.extensions import portsecurity as psec
|
|||||||
from quantum.extensions import providernet as pnet
|
from quantum.extensions import providernet as pnet
|
||||||
from quantum.extensions import securitygroup as ext_sg
|
from quantum.extensions import securitygroup as ext_sg
|
||||||
from quantum.openstack.common import rpc
|
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
|
from quantum.plugins.nicira.nicira_nvp_plugin.common import (securitygroups
|
||||||
as nvp_sec)
|
as nvp_sec)
|
||||||
from quantum import policy
|
from quantum import policy
|
||||||
@ -84,7 +86,11 @@ def parse_config():
|
|||||||
NVPCluster objects, 'plugin_config' is a dictionary with plugin
|
NVPCluster objects, 'plugin_config' is a dictionary with plugin
|
||||||
parameters (currently only 'max_lp_per_bridged_ls').
|
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)
|
nvp_conf = config.ClusterConfigOptions(cfg.CONF)
|
||||||
cluster_names = config.register_cluster_groups(nvp_conf)
|
cluster_names = config.register_cluster_groups(nvp_conf)
|
||||||
nvp_conf.log_opt_values(LOG, logging.DEBUG)
|
nvp_conf.log_opt_values(LOG, logging.DEBUG)
|
||||||
@ -104,7 +110,7 @@ def parse_config():
|
|||||||
'default_l3_gw_service_uuid':
|
'default_l3_gw_service_uuid':
|
||||||
nvp_conf[cluster_name].default_l3_gw_service_uuid})
|
nvp_conf[cluster_name].default_l3_gw_service_uuid})
|
||||||
LOG.debug(_("Cluster options:%s"), clusters_options)
|
LOG.debug(_("Cluster options:%s"), clusters_options)
|
||||||
return nvp_options, clusters_options
|
return cfg.CONF.NVP, clusters_options
|
||||||
|
|
||||||
|
|
||||||
class NVPRpcCallbacks(dhcp_rpc_base.DhcpRpcCallbackMixin):
|
class NVPRpcCallbacks(dhcp_rpc_base.DhcpRpcCallbackMixin):
|
||||||
@ -125,7 +131,9 @@ class NvpPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
|
|||||||
l3_db.L3_NAT_db_mixin,
|
l3_db.L3_NAT_db_mixin,
|
||||||
portsecurity_db.PortSecurityDbMixin,
|
portsecurity_db.PortSecurityDbMixin,
|
||||||
securitygroups_db.SecurityGroupDbMixin,
|
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
|
NvpPluginV2 is a Quantum plugin that provides L2 Virtual Network
|
||||||
functionality using NVP.
|
functionality using NVP.
|
||||||
@ -671,26 +679,6 @@ class NvpPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
|
|||||||
"logical network %s"), network.id)
|
"logical network %s"), network.id)
|
||||||
raise nvp_exc.NvpNoMorePortsException(network=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):
|
def setup_rpc(self):
|
||||||
# RPC support for dhcp
|
# RPC support for dhcp
|
||||||
self.topic = topics.PLUGIN
|
self.topic = topics.PLUGIN
|
||||||
@ -1100,16 +1088,6 @@ class NvpPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
|
|||||||
with context.session.begin(subtransactions=True):
|
with context.session.begin(subtransactions=True):
|
||||||
# First we allocate port in quantum database
|
# First we allocate port in quantum database
|
||||||
quantum_db = super(NvpPluginV2, self).create_port(context, port)
|
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)
|
# Update fields obtained from quantum db (eg: MAC address)
|
||||||
port["port"].update(quantum_db)
|
port["port"].update(quantum_db)
|
||||||
# port security extension checks
|
# port security extension checks
|
||||||
@ -1172,16 +1150,6 @@ class NvpPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
|
|||||||
# copy values over
|
# copy values over
|
||||||
ret_port.update(port['port'])
|
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
|
# populate port_security setting
|
||||||
if psec.PORTSECURITY not in port['port']:
|
if psec.PORTSECURITY not in port['port']:
|
||||||
ret_port[psec.PORTSECURITY] = self._get_port_security_binding(
|
ret_port[psec.PORTSECURITY] = self._get_port_security_binding(
|
||||||
@ -1526,6 +1494,10 @@ class NvpPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
|
|||||||
order=NVP_EXTGW_NAT_RULES_ORDER,
|
order=NVP_EXTGW_NAT_RULES_ORDER,
|
||||||
match_criteria={'source_ip_addresses': subnet['cidr']})
|
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 "
|
LOG.debug(_("Add_router_interface completed for subnet:%(subnet_id)s "
|
||||||
"and router:%(router_id)s"),
|
"and router:%(router_id)s"),
|
||||||
{'subnet_id': subnet_id, 'router_id': router_id})
|
{'subnet_id': subnet_id, 'router_id': router_id})
|
||||||
@ -1585,6 +1557,11 @@ class NvpPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
|
|||||||
{'q_port_id': port_id,
|
{'q_port_id': port_id,
|
||||||
'nvp_port_id': lport['uuid']})
|
'nvp_port_id': lport['uuid']})
|
||||||
return
|
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:
|
try:
|
||||||
if not subnet:
|
if not subnet:
|
||||||
subnet = self._get_subnet(context, subnet_id)
|
subnet = self._get_subnet(context, subnet_id)
|
||||||
|
@ -34,6 +34,9 @@ nvp_opts = [
|
|||||||
"(default -1 meaning do not time out)")),
|
"(default -1 meaning do not time out)")),
|
||||||
cfg.StrOpt('default_cluster_name',
|
cfg.StrOpt('default_cluster_name',
|
||||||
help=_("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 = [
|
cluster_opts = [
|
||||||
|
@ -0,0 +1,150 @@
|
|||||||
|
# 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)
|
@ -19,6 +19,7 @@ import os
|
|||||||
|
|
||||||
import mock
|
import mock
|
||||||
from oslo.config import cfg
|
from oslo.config import cfg
|
||||||
|
import netaddr
|
||||||
import webob.exc
|
import webob.exc
|
||||||
|
|
||||||
import quantum.common.test_lib as test_lib
|
import quantum.common.test_lib as test_lib
|
||||||
@ -82,6 +83,7 @@ class NiciraPluginV2TestCase(test_plugin.QuantumDbPluginV2TestCase):
|
|||||||
instance.return_value.get_nvp_version.return_value = "2.999"
|
instance.return_value.get_nvp_version.return_value = "2.999"
|
||||||
instance.return_value.request.side_effect = _fake_request
|
instance.return_value.request.side_effect = _fake_request
|
||||||
super(NiciraPluginV2TestCase, self).setUp(self._plugin_name)
|
super(NiciraPluginV2TestCase, self).setUp(self._plugin_name)
|
||||||
|
cfg.CONF.set_override('enable_metadata_access_network', False, 'NVP')
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
self.fc.reset_all()
|
self.fc.reset_all()
|
||||||
@ -259,10 +261,120 @@ class TestNiciraSecurityGroup(ext_sg.TestSecurityGroups,
|
|||||||
class TestNiciraL3NatTestCase(test_l3_plugin.L3NatDBTestCase,
|
class TestNiciraL3NatTestCase(test_l3_plugin.L3NatDBTestCase,
|
||||||
NiciraPluginV2TestCase):
|
NiciraPluginV2TestCase):
|
||||||
|
|
||||||
def test_floatingip_with_assoc_fails(self):
|
def test_floatingip_with_assoc_fails(self):
|
||||||
self._test_floatingip_with_assoc_fails(
|
self._test_floatingip_with_assoc_fails(
|
||||||
'quantum.plugins.nicira.nicira_nvp_plugin.'
|
'quantum.plugins.nicira.nicira_nvp_plugin.'
|
||||||
'QuantumPlugin.NvpPluginV2')
|
'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):
|
class NvpQoSTestExtensionManager(object):
|
||||||
|
@ -14,7 +14,6 @@
|
|||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import socket
|
import socket
|
||||||
import sys
|
import sys
|
||||||
@ -27,6 +26,7 @@ import unittest2 as unittest
|
|||||||
from quantum.agent.common import config
|
from quantum.agent.common import config
|
||||||
from quantum.agent import dhcp_agent
|
from quantum.agent import dhcp_agent
|
||||||
from quantum.agent.linux import interface
|
from quantum.agent.linux import interface
|
||||||
|
from quantum.common import constants
|
||||||
from quantum.common import exceptions
|
from quantum.common import exceptions
|
||||||
from quantum.openstack.common import jsonutils
|
from quantum.openstack.common import jsonutils
|
||||||
|
|
||||||
@ -54,13 +54,20 @@ fake_subnet1 = FakeModel('bbbbbbbb-bbbb-bbbb-bbbbbbbbbbbb',
|
|||||||
|
|
||||||
fake_subnet2 = FakeModel('dddddddd-dddd-dddd-dddddddddddd',
|
fake_subnet2 = FakeModel('dddddddd-dddd-dddd-dddddddddddd',
|
||||||
network_id='12345678-1234-5678-1234567890ab',
|
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',
|
fake_subnet3 = FakeModel('bbbbbbbb-1111-2222-bbbbbbbbbbbb',
|
||||||
network_id='12345678-1234-5678-1234567890ab',
|
network_id='12345678-1234-5678-1234567890ab',
|
||||||
cidr='192.168.1.1/24', enable_dhcp=True)
|
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_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',
|
fake_port1 = FakeModel('12345678-1234-aaaa-1234567890ab',
|
||||||
mac_address='aa:bb:cc:dd:ee:ff',
|
mac_address='aa:bb:cc:dd:ee:ff',
|
||||||
@ -71,12 +78,25 @@ fake_port2 = FakeModel('12345678-1234-aaaa-123456789000',
|
|||||||
mac_address='aa:bb:cc:dd:ee:99',
|
mac_address='aa:bb:cc:dd:ee:99',
|
||||||
network_id='12345678-1234-5678-1234567890ab')
|
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',
|
fake_network = FakeModel('12345678-1234-5678-1234567890ab',
|
||||||
tenant_id='aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa',
|
tenant_id='aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa',
|
||||||
admin_state_up=True,
|
admin_state_up=True,
|
||||||
subnets=[fake_subnet1, fake_subnet2],
|
subnets=[fake_subnet1, fake_subnet2],
|
||||||
ports=[fake_port1])
|
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',
|
fake_down_network = FakeModel('12345678-dddd-dddd-1234567890ab',
|
||||||
tenant_id='aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa',
|
tenant_id='aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa',
|
||||||
admin_state_up=False,
|
admin_state_up=False,
|
||||||
@ -417,6 +437,27 @@ class TestDhcpAgentEventHandler(unittest.TestCase):
|
|||||||
mock.call().disable()
|
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):
|
def test_network_create_end(self):
|
||||||
payload = dict(network=dict(id=fake_network.id))
|
payload = dict(network=dict(id=fake_network.id))
|
||||||
|
|
||||||
@ -751,44 +792,61 @@ class TestDeviceManager(unittest.TestCase):
|
|||||||
self.device_exists = self.device_exists_p.start()
|
self.device_exists = self.device_exists_p.start()
|
||||||
|
|
||||||
self.dvr_cls_p = mock.patch('quantum.agent.linux.interface.NullDriver')
|
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()
|
driver_cls = self.dvr_cls_p.start()
|
||||||
|
iproute_cls = self.iproute_cls_p.start()
|
||||||
self.mock_driver = mock.MagicMock()
|
self.mock_driver = mock.MagicMock()
|
||||||
self.mock_driver.DEV_NAME_LEN = (
|
self.mock_driver.DEV_NAME_LEN = (
|
||||||
interface.LinuxInterfaceDriver.DEV_NAME_LEN)
|
interface.LinuxInterfaceDriver.DEV_NAME_LEN)
|
||||||
|
self.mock_iproute = mock.MagicMock()
|
||||||
driver_cls.return_value = self.mock_driver
|
driver_cls.return_value = self.mock_driver
|
||||||
|
iproute_cls.return_value = self.mock_iproute
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
self.dvr_cls_p.stop()
|
self.dvr_cls_p.stop()
|
||||||
self.device_exists_p.stop()
|
self.device_exists_p.stop()
|
||||||
|
self.iproute_cls_p.stop()
|
||||||
cfg.CONF.reset()
|
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 = 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.device_exists.return_value = device_exists
|
||||||
self.mock_driver.get_device_name.return_value = 'tap12345678-12'
|
self.mock_driver.get_device_name.return_value = 'tap12345678-12'
|
||||||
|
|
||||||
dh = dhcp_agent.DeviceManager(cfg.CONF, plugin)
|
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')
|
self.assertEqual(interface_name, 'tap12345678-12')
|
||||||
|
|
||||||
plugin.assert_has_calls([
|
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',
|
expected = [mock.call.init_l3('tap12345678-12',
|
||||||
['172.9.9.9/24', '169.254.169.254/16'],
|
expected_ips,
|
||||||
namespace=namespace)]
|
namespace=namespace)]
|
||||||
|
|
||||||
if not reuse_existing:
|
if not reuse_existing:
|
||||||
expected.insert(0,
|
expected.insert(0,
|
||||||
mock.call.plug(fake_network.id,
|
mock.call.plug(net.id,
|
||||||
fake_port1.id,
|
port.id,
|
||||||
'tap12345678-12',
|
'tap12345678-12',
|
||||||
'aa:bb:cc:dd:ee:ff',
|
'aa:bb:cc:dd:ee:ff',
|
||||||
namespace=namespace))
|
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)
|
self.mock_driver.assert_has_calls(expected)
|
||||||
|
|
||||||
@ -802,6 +860,11 @@ class TestDeviceManager(unittest.TestCase):
|
|||||||
def test_setup_device_exists_reuse(self):
|
def test_setup_device_exists_reuse(self):
|
||||||
self._test_setup_helper(True, True)
|
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):
|
def test_destroy(self):
|
||||||
fake_network = FakeModel('12345678-1234-5678-1234567890ab',
|
fake_network = FakeModel('12345678-1234-5678-1234567890ab',
|
||||||
tenant_id='aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa')
|
tenant_id='aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa')
|
||||||
|
@ -588,7 +588,16 @@ class L3NatDBTestCase(L3NatTestCaseBase):
|
|||||||
fip['floatingip']['router_id'], None,
|
fip['floatingip']['router_id'], None,
|
||||||
expected_code=exc.HTTPConflict.code)
|
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.router() as r:
|
||||||
with self.subnet() as s:
|
with self.subnet() as s:
|
||||||
body = self._router_interface_action('add',
|
body = self._router_interface_action('add',
|
||||||
@ -609,17 +618,9 @@ class L3NatDBTestCase(L3NatTestCaseBase):
|
|||||||
body = self._show('ports', r_port_id,
|
body = self._show('ports', r_port_id,
|
||||||
expected_code=exc.HTTPNotFound.code)
|
expected_code=exc.HTTPNotFound.code)
|
||||||
|
|
||||||
self.assertEqual(len(test_notifier.NOTIFICATIONS), 8)
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
set(n['event_type'] for n in test_notifier.NOTIFICATIONS),
|
set(n['event_type'] for n in test_notifier.NOTIFICATIONS),
|
||||||
set(['router.create.start',
|
set(exp_notifications))
|
||||||
'router.create.end',
|
|
||||||
'network.create.start',
|
|
||||||
'network.create.end',
|
|
||||||
'subnet.create.start',
|
|
||||||
'subnet.create.end',
|
|
||||||
'router.interface.create',
|
|
||||||
'router.interface.delete']))
|
|
||||||
|
|
||||||
def test_router_add_interface_subnet_with_bad_tenant_returns_404(self):
|
def test_router_add_interface_subnet_with_bad_tenant_returns_404(self):
|
||||||
with mock.patch('quantum.context.Context.to_dict') as tdict:
|
with mock.patch('quantum.context.Context.to_dict') as tdict:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user