Merge "Add support for NSX/NVP Metadata services"
This commit is contained in:
@@ -169,3 +169,15 @@
|
|||||||
|
|
||||||
# Default DHCP lease time
|
# Default DHCP lease time
|
||||||
# default_lease_time = 43200
|
# default_lease_time = 43200
|
||||||
|
|
||||||
|
[nvp_metadata]
|
||||||
|
# IP address used by Metadata server
|
||||||
|
# metadata_server_address = 127.0.0.1
|
||||||
|
|
||||||
|
# TCP Port used by Metadata server
|
||||||
|
# metadata_server_port = 8775
|
||||||
|
|
||||||
|
# When proxying metadata requests, Neutron signs the Instance-ID header with a
|
||||||
|
# shared secret to prevent spoofing. You may select any string for a secret,
|
||||||
|
# but it MUST match with the configuration used by the Metadata server
|
||||||
|
# metadata_shared_secret =
|
||||||
|
|||||||
@@ -1552,7 +1552,7 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
|||||||
# router, but if it does, it should not happen within a
|
# router, but if it does, it should not happen within a
|
||||||
# transaction, and it should be restored on rollback
|
# transaction, and it should be restored on rollback
|
||||||
self.handle_router_metadata_access(
|
self.handle_router_metadata_access(
|
||||||
context, router_id, do_create=False)
|
context, router_id, interface=None)
|
||||||
# Pre-delete checks
|
# Pre-delete checks
|
||||||
# NOTE(salv-orlando): These checks will be repeated anyway when
|
# NOTE(salv-orlando): These checks will be repeated anyway when
|
||||||
# calling the superclass. This is wasteful, but is the simplest
|
# calling the superclass. This is wasteful, but is the simplest
|
||||||
@@ -1654,7 +1654,8 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
|||||||
# Ensure the NVP logical router has a connection to a 'metadata access'
|
# Ensure the NVP logical router has a connection to a 'metadata access'
|
||||||
# network (with a proxy listening on its DHCP port), by creating it
|
# network (with a proxy listening on its DHCP port), by creating it
|
||||||
# if needed.
|
# if needed.
|
||||||
self.handle_router_metadata_access(context, router_id)
|
self.handle_router_metadata_access(
|
||||||
|
context, router_id, interface=router_iface_info)
|
||||||
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})
|
||||||
@@ -1698,7 +1699,8 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
|||||||
# Ensure the connection to the 'metadata access network'
|
# Ensure the connection to the 'metadata access network'
|
||||||
# is removed (with the network) if this the last subnet
|
# is removed (with the network) if this the last subnet
|
||||||
# on the router
|
# on the router
|
||||||
self.handle_router_metadata_access(context, router_id)
|
self.handle_router_metadata_access(
|
||||||
|
context, router_id, interface=info)
|
||||||
try:
|
try:
|
||||||
if not subnet:
|
if not subnet:
|
||||||
subnet = self._get_subnet(context, subnet_id)
|
subnet = self._get_subnet(context, subnet_id)
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ from neutron.api.v2 import attributes as attr
|
|||||||
from neutron.common import constants as const
|
from neutron.common import constants as const
|
||||||
from neutron.common import exceptions as n_exc
|
from neutron.common import exceptions as n_exc
|
||||||
from neutron.db import db_base_plugin_v2
|
from neutron.db import db_base_plugin_v2
|
||||||
|
from neutron.db import l3_db
|
||||||
|
from neutron.extensions import external_net
|
||||||
from neutron.openstack.common import log as logging
|
from neutron.openstack.common import log as logging
|
||||||
from neutron.plugins.nicira.common import exceptions as p_exc
|
from neutron.plugins.nicira.common import exceptions as p_exc
|
||||||
from neutron.plugins.nicira.nsxlib import lsn as lsn_api
|
from neutron.plugins.nicira.nsxlib import lsn as lsn_api
|
||||||
@@ -29,7 +31,17 @@ from neutron.plugins.nicira import nvplib
|
|||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
# A unique MAC to quickly identify the LSN port used for metadata services
|
||||||
|
# when dhcp on the subnet is off. Inspired by leet-speak for 'metadata'.
|
||||||
|
METADATA_MAC = "fa:15:73:74:d4:74"
|
||||||
|
METADATA_PORT_ID = 'metadata:id'
|
||||||
|
METADATA_PORT_NAME = 'metadata:name'
|
||||||
|
METADATA_DEVICE_ID = 'metadata:device'
|
||||||
|
META_CONF = 'metadata-proxy'
|
||||||
|
DHCP_CONF = 'dhcp'
|
||||||
|
SPECIAL_OWNERS = (const.DEVICE_OWNER_DHCP,
|
||||||
|
const.DEVICE_OWNER_ROUTER_GW,
|
||||||
|
l3_db.DEVICE_OWNER_ROUTER_INTF)
|
||||||
|
|
||||||
dhcp_opts = [
|
dhcp_opts = [
|
||||||
cfg.ListOpt('extra_domain_name_servers',
|
cfg.ListOpt('extra_domain_name_servers',
|
||||||
@@ -44,10 +56,27 @@ dhcp_opts = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
metadata_opts = [
|
||||||
|
cfg.StrOpt('metadata_server_address', default='127.0.0.1',
|
||||||
|
help=_("IP address used by Metadata server.")),
|
||||||
|
cfg.IntOpt('metadata_server_port',
|
||||||
|
default=8775,
|
||||||
|
help=_("TCP Port used by Metadata server.")),
|
||||||
|
cfg.StrOpt('metadata_shared_secret',
|
||||||
|
default='',
|
||||||
|
help=_('Shared secret to sign instance-id request'),
|
||||||
|
secret=True)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def register_dhcp_opts(config):
|
def register_dhcp_opts(config):
|
||||||
config.CONF.register_opts(dhcp_opts, "NVP_DHCP")
|
config.CONF.register_opts(dhcp_opts, "NVP_DHCP")
|
||||||
|
|
||||||
|
|
||||||
|
def register_metadata_opts(config):
|
||||||
|
config.CONF.register_opts(metadata_opts, "NVP_METADATA")
|
||||||
|
|
||||||
|
|
||||||
class LsnManager(object):
|
class LsnManager(object):
|
||||||
"""Manage LSN entities associated with networks."""
|
"""Manage LSN entities associated with networks."""
|
||||||
|
|
||||||
@@ -161,6 +190,24 @@ class LsnManager(object):
|
|||||||
context, network_id, mac_address, raise_on_err=False)
|
context, network_id, mac_address, raise_on_err=False)
|
||||||
if lsn_port_id:
|
if lsn_port_id:
|
||||||
self.lsn_port_delete(context, lsn_id, lsn_port_id)
|
self.lsn_port_delete(context, lsn_id, lsn_port_id)
|
||||||
|
if mac_address == METADATA_MAC:
|
||||||
|
try:
|
||||||
|
lswitch_port = nvplib.get_port_by_neutron_tag(
|
||||||
|
self.cluster, network_id, METADATA_PORT_ID)
|
||||||
|
if lswitch_port:
|
||||||
|
lswitch_port_id = lswitch_port['uuid']
|
||||||
|
nvplib.delete_port(
|
||||||
|
self.cluster, network_id, lswitch_port_id)
|
||||||
|
else:
|
||||||
|
LOG.warn(_("Metadata port not found while attempting "
|
||||||
|
"to delete it from network %s"), network_id)
|
||||||
|
except (n_exc.PortNotFoundOnNetwork,
|
||||||
|
nvplib.NvpApiClient.NvpApiException):
|
||||||
|
LOG.warn(_("Metadata port not found while attempting "
|
||||||
|
"to delete it from network %s"), network_id)
|
||||||
|
else:
|
||||||
|
LOG.warn(_("Unable to find Logical Services Node "
|
||||||
|
"Port with MAC %s"), mac_address)
|
||||||
|
|
||||||
def lsn_port_dhcp_setup(
|
def lsn_port_dhcp_setup(
|
||||||
self, context, network_id, port_id, port_data, subnet_config=None):
|
self, context, network_id, port_id, port_data, subnet_config=None):
|
||||||
@@ -187,6 +234,36 @@ class LsnManager(object):
|
|||||||
else:
|
else:
|
||||||
return (lsn_id, lsn_port_id)
|
return (lsn_id, lsn_port_id)
|
||||||
|
|
||||||
|
def lsn_port_metadata_setup(self, context, lsn_id, subnet):
|
||||||
|
"""Connect subnet to specified LSN."""
|
||||||
|
data = {
|
||||||
|
"mac_address": METADATA_MAC,
|
||||||
|
"ip_address": subnet['cidr'],
|
||||||
|
"subnet_id": subnet['id']
|
||||||
|
}
|
||||||
|
network_id = subnet['network_id']
|
||||||
|
tenant_id = subnet['tenant_id']
|
||||||
|
lswitch_port_id = None
|
||||||
|
try:
|
||||||
|
lswitch_port_id = nvplib.create_lport(
|
||||||
|
self.cluster, network_id, tenant_id,
|
||||||
|
METADATA_PORT_ID, METADATA_PORT_NAME,
|
||||||
|
METADATA_DEVICE_ID, True)['uuid']
|
||||||
|
lsn_port_id = self.lsn_port_create(self.cluster, lsn_id, data)
|
||||||
|
except (n_exc.NotFound, p_exc.NvpPluginException,
|
||||||
|
nvplib.NvpApiClient.NvpApiException):
|
||||||
|
raise p_exc.PortConfigurationError(
|
||||||
|
net_id=network_id, lsn_id=lsn_id, port_id=lswitch_port_id)
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
lsn_api.lsn_port_plug_network(
|
||||||
|
self.cluster, lsn_id, lsn_port_id, lswitch_port_id)
|
||||||
|
except p_exc.LsnConfigurationConflict:
|
||||||
|
self.lsn_port_delete(self.cluster, lsn_id, lsn_port_id)
|
||||||
|
nvplib.delete_port(self.cluster, network_id, lswitch_port_id)
|
||||||
|
raise p_exc.PortConfigurationError(
|
||||||
|
net_id=network_id, lsn_id=lsn_id, port_id=lsn_port_id)
|
||||||
|
|
||||||
def lsn_port_dhcp_configure(self, context, lsn_id, lsn_port_id, subnet):
|
def lsn_port_dhcp_configure(self, context, lsn_id, lsn_port_id, subnet):
|
||||||
"""Enable/disable dhcp services with the given config options."""
|
"""Enable/disable dhcp services with the given config options."""
|
||||||
is_enabled = subnet["enable_dhcp"]
|
is_enabled = subnet["enable_dhcp"]
|
||||||
@@ -214,6 +291,36 @@ class LsnManager(object):
|
|||||||
LOG.error(err_msg)
|
LOG.error(err_msg)
|
||||||
raise p_exc.NvpPluginException(err_msg=err_msg)
|
raise p_exc.NvpPluginException(err_msg=err_msg)
|
||||||
|
|
||||||
|
def lsn_metadata_configure(self, context, subnet_id, is_enabled):
|
||||||
|
"""Configure metadata service for the specified subnet."""
|
||||||
|
subnet = self.plugin.get_subnet(context, subnet_id)
|
||||||
|
network_id = subnet['network_id']
|
||||||
|
meta_conf = cfg.CONF.NVP_METADATA
|
||||||
|
metadata_options = {
|
||||||
|
'metadata_server_ip': meta_conf.metadata_server_address,
|
||||||
|
'metadata_server_port': meta_conf.metadata_server_port,
|
||||||
|
'metadata_proxy_shared_secret': meta_conf.metadata_shared_secret
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
lsn_id = self.lsn_get(context, network_id)
|
||||||
|
lsn_api.lsn_metadata_configure(
|
||||||
|
self.cluster, lsn_id, is_enabled, metadata_options)
|
||||||
|
except (p_exc.LsnNotFound, nvplib.NvpApiClient.NvpApiException):
|
||||||
|
err_msg = (_('Unable to configure metadata access '
|
||||||
|
'for subnet %s') % subnet_id)
|
||||||
|
LOG.error(err_msg)
|
||||||
|
raise p_exc.NvpPluginException(err_msg=err_msg)
|
||||||
|
if is_enabled:
|
||||||
|
try:
|
||||||
|
# test that the lsn port exists
|
||||||
|
self.lsn_port_get(context, network_id, subnet_id)
|
||||||
|
except p_exc.LsnPortNotFound:
|
||||||
|
# this might happen if subnet had dhcp off when created
|
||||||
|
# so create one, and wire it
|
||||||
|
self.lsn_port_metadata_setup(context, lsn_id, subnet)
|
||||||
|
else:
|
||||||
|
self.lsn_port_dispose(context, network_id, METADATA_MAC)
|
||||||
|
|
||||||
def _lsn_port_host_conf(self, context, network_id, subnet_id, data, hdlr):
|
def _lsn_port_host_conf(self, context, network_id, subnet_id, data, hdlr):
|
||||||
lsn_id = None
|
lsn_id = None
|
||||||
lsn_port_id = None
|
lsn_port_id = None
|
||||||
@@ -228,7 +335,7 @@ class LsnManager(object):
|
|||||||
net_id=network_id, lsn_id=lsn_id, port_id=lsn_port_id)
|
net_id=network_id, lsn_id=lsn_id, port_id=lsn_port_id)
|
||||||
|
|
||||||
def lsn_port_dhcp_host_add(self, context, network_id, subnet_id, host):
|
def lsn_port_dhcp_host_add(self, context, network_id, subnet_id, host):
|
||||||
"""Add dhcp host entry from LSN port configuration."""
|
"""Add dhcp host entry to LSN port configuration."""
|
||||||
self._lsn_port_host_conf(context, network_id, subnet_id, host,
|
self._lsn_port_host_conf(context, network_id, subnet_id, host,
|
||||||
lsn_api.lsn_port_dhcp_host_add)
|
lsn_api.lsn_port_dhcp_host_add)
|
||||||
|
|
||||||
@@ -237,6 +344,34 @@ class LsnManager(object):
|
|||||||
self._lsn_port_host_conf(context, network_id, subnet_id, host,
|
self._lsn_port_host_conf(context, network_id, subnet_id, host,
|
||||||
lsn_api.lsn_port_dhcp_host_remove)
|
lsn_api.lsn_port_dhcp_host_remove)
|
||||||
|
|
||||||
|
def lsn_port_meta_host_add(self, context, network_id, subnet_id, host):
|
||||||
|
"""Add metadata host entry to LSN port configuration."""
|
||||||
|
self._lsn_port_host_conf(context, network_id, subnet_id, host,
|
||||||
|
lsn_api.lsn_port_metadata_host_add)
|
||||||
|
|
||||||
|
def lsn_port_meta_host_remove(self, context, network_id, subnet_id, host):
|
||||||
|
"""Remove meta host entry from LSN port configuration."""
|
||||||
|
self._lsn_port_host_conf(context, network_id, subnet_id, host,
|
||||||
|
lsn_api.lsn_port_metadata_host_remove)
|
||||||
|
|
||||||
|
def lsn_port_update(
|
||||||
|
self, context, network_id, subnet_id, dhcp=None, meta=None):
|
||||||
|
"""Update the specified configuration for the LSN port."""
|
||||||
|
if not dhcp and not meta:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
lsn_id, lsn_port_id = self.lsn_port_get(
|
||||||
|
context, network_id, subnet_id, raise_on_err=False)
|
||||||
|
if dhcp and lsn_id and lsn_port_id:
|
||||||
|
lsn_api.lsn_port_host_entries_update(
|
||||||
|
self.cluster, lsn_id, lsn_port_id, DHCP_CONF, dhcp)
|
||||||
|
if meta and lsn_id and lsn_port_id:
|
||||||
|
lsn_api.lsn_port_host_entries_update(
|
||||||
|
self.cluster, lsn_id, lsn_port_id, META_CONF, meta)
|
||||||
|
except nvplib.NvpApiClient.NvpApiException:
|
||||||
|
raise p_exc.PortConfigurationError(
|
||||||
|
net_id=network_id, lsn_id=lsn_id, port_id=lsn_port_id)
|
||||||
|
|
||||||
|
|
||||||
class DhcpAgentNotifyAPI(object):
|
class DhcpAgentNotifyAPI(object):
|
||||||
|
|
||||||
@@ -251,6 +386,33 @@ class DhcpAgentNotifyAPI(object):
|
|||||||
[resource, action, _e] = methodname.split('.')
|
[resource, action, _e] = methodname.split('.')
|
||||||
if resource == 'subnet':
|
if resource == 'subnet':
|
||||||
self._handle_subnet_dhcp_access[action](context, data['subnet'])
|
self._handle_subnet_dhcp_access[action](context, data['subnet'])
|
||||||
|
elif resource == 'port' and action == 'update':
|
||||||
|
self._port_update(context, data['port'])
|
||||||
|
|
||||||
|
def _port_update(self, context, port):
|
||||||
|
# With no fixed IP's there's nothing that can be updated
|
||||||
|
if not port["fixed_ips"]:
|
||||||
|
return
|
||||||
|
network_id = port['network_id']
|
||||||
|
subnet_id = port["fixed_ips"][0]['subnet_id']
|
||||||
|
filters = {'network_id': [network_id]}
|
||||||
|
# Because NVP does not support updating a single host entry we
|
||||||
|
# got to build the whole list from scratch and update in bulk
|
||||||
|
ports = self.plugin.get_ports(context, filters)
|
||||||
|
if not ports:
|
||||||
|
return
|
||||||
|
dhcp_conf = [
|
||||||
|
{'mac_address': p['mac_address'],
|
||||||
|
'ip_address': p["fixed_ips"][0]['ip_address']}
|
||||||
|
for p in ports if is_user_port(p)
|
||||||
|
]
|
||||||
|
meta_conf = [
|
||||||
|
{'instance_id': p['device_id'],
|
||||||
|
'ip_address': p["fixed_ips"][0]['ip_address']}
|
||||||
|
for p in ports if is_user_port(p, check_dev_id=True)
|
||||||
|
]
|
||||||
|
self.lsn_manager.lsn_port_update(
|
||||||
|
context, network_id, subnet_id, dhcp=dhcp_conf, meta=meta_conf)
|
||||||
|
|
||||||
def _subnet_create(self, context, subnet, clean_on_err=True):
|
def _subnet_create(self, context, subnet, clean_on_err=True):
|
||||||
if subnet['enable_dhcp']:
|
if subnet['enable_dhcp']:
|
||||||
@@ -268,7 +430,7 @@ class DhcpAgentNotifyAPI(object):
|
|||||||
}
|
}
|
||||||
try:
|
try:
|
||||||
# This will end up calling handle_port_dhcp_access
|
# This will end up calling handle_port_dhcp_access
|
||||||
# down below
|
# down below as well as handle_port_metadata_access
|
||||||
self.plugin.create_port(context, {'port': dhcp_port})
|
self.plugin.create_port(context, {'port': dhcp_port})
|
||||||
except p_exc.PortConfigurationError as e:
|
except p_exc.PortConfigurationError as e:
|
||||||
err_msg = (_("Error while creating subnet %(cidr)s for "
|
err_msg = (_("Error while creating subnet %(cidr)s for "
|
||||||
@@ -292,7 +454,13 @@ class DhcpAgentNotifyAPI(object):
|
|||||||
context, lsn_id, lsn_port_id, subnet)
|
context, lsn_id, lsn_port_id, subnet)
|
||||||
except p_exc.LsnPortNotFound:
|
except p_exc.LsnPortNotFound:
|
||||||
# It's possible that the subnet was created with dhcp off;
|
# It's possible that the subnet was created with dhcp off;
|
||||||
# check that a dhcp port exists first and provision it
|
# check if the subnet was uplinked onto a router, and if so
|
||||||
|
# remove the patch attachment between the metadata port and
|
||||||
|
# the lsn port, in favor on the one we'll be creating during
|
||||||
|
# _subnet_create
|
||||||
|
self.lsn_manager.lsn_port_dispose(
|
||||||
|
context, network_id, METADATA_MAC)
|
||||||
|
# also, check that a dhcp port exists first and provision it
|
||||||
# accordingly
|
# accordingly
|
||||||
filters = dict(network_id=[network_id],
|
filters = dict(network_id=[network_id],
|
||||||
device_owner=[const.DEVICE_OWNER_DHCP])
|
device_owner=[const.DEVICE_OWNER_DHCP])
|
||||||
@@ -313,10 +481,15 @@ class DhcpAgentNotifyAPI(object):
|
|||||||
ports = self.plugin.get_ports(context, filters=filters)
|
ports = self.plugin.get_ports(context, filters=filters)
|
||||||
if ports:
|
if ports:
|
||||||
# This will end up calling handle_port_dhcp_access
|
# This will end up calling handle_port_dhcp_access
|
||||||
# down below
|
# down below as well as handle_port_metadata_access
|
||||||
self.plugin.delete_port(context, ports[0]['id'])
|
self.plugin.delete_port(context, ports[0]['id'])
|
||||||
|
|
||||||
|
|
||||||
|
def is_user_port(p, check_dev_id=False):
|
||||||
|
usable = p['fixed_ips'] and p['device_owner'] not in SPECIAL_OWNERS
|
||||||
|
return usable if not check_dev_id else usable and p['device_id']
|
||||||
|
|
||||||
|
|
||||||
def check_services_requirements(cluster):
|
def check_services_requirements(cluster):
|
||||||
ver = cluster.api_client.get_nvp_version()
|
ver = cluster.api_client.get_nvp_version()
|
||||||
# It sounds like 4.1 is the first one where DHCP in NSX/NVP
|
# It sounds like 4.1 is the first one where DHCP in NSX/NVP
|
||||||
@@ -374,7 +547,8 @@ def handle_port_dhcp_access(plugin, context, port, action):
|
|||||||
# do something only if there are IP's and dhcp is enabled
|
# do something only if there are IP's and dhcp is enabled
|
||||||
subnet_id = port["fixed_ips"][0]['subnet_id']
|
subnet_id = port["fixed_ips"][0]['subnet_id']
|
||||||
if not plugin.get_subnet(context, subnet_id)['enable_dhcp']:
|
if not plugin.get_subnet(context, subnet_id)['enable_dhcp']:
|
||||||
LOG.info(_("DHCP is disabled: nothing to do"))
|
LOG.info(_("DHCP is disabled for subnet %s: nothing "
|
||||||
|
"to do"), subnet_id)
|
||||||
return
|
return
|
||||||
host_data = {
|
host_data = {
|
||||||
"mac_address": port["mac_address"],
|
"mac_address": port["mac_address"],
|
||||||
@@ -395,11 +569,50 @@ def handle_port_dhcp_access(plugin, context, port, action):
|
|||||||
LOG.info(_("DHCP for port %s configured successfully"), port['id'])
|
LOG.info(_("DHCP for port %s configured successfully"), port['id'])
|
||||||
|
|
||||||
|
|
||||||
def handle_port_metadata_access(context, port, is_delete=False):
|
def handle_port_metadata_access(plugin, context, port, is_delete=False):
|
||||||
# TODO(armando-migliaccio)
|
if is_user_port(port, check_dev_id=True):
|
||||||
LOG.info('%s port with data %s' % (is_delete, port))
|
network_id = port["network_id"]
|
||||||
|
network = plugin.get_network(context, network_id)
|
||||||
|
if network[external_net.EXTERNAL]:
|
||||||
|
LOG.info(_("Network %s is external: nothing to do"), network_id)
|
||||||
|
return
|
||||||
|
subnet_id = port["fixed_ips"][0]['subnet_id']
|
||||||
|
host_data = {
|
||||||
|
"instance_id": port["device_id"],
|
||||||
|
"tenant_id": port["tenant_id"],
|
||||||
|
"ip_address": port["fixed_ips"][0]['ip_address']
|
||||||
|
}
|
||||||
|
LOG.info(_("Configuring metadata entry for port %s"), port)
|
||||||
|
if not is_delete:
|
||||||
|
handler = plugin.lsn_manager.lsn_port_meta_host_add
|
||||||
|
else:
|
||||||
|
handler = plugin.lsn_manager.lsn_port_meta_host_remove
|
||||||
|
try:
|
||||||
|
handler(context, network_id, subnet_id, host_data)
|
||||||
|
except p_exc.PortConfigurationError:
|
||||||
|
if not is_delete:
|
||||||
|
db_base_plugin_v2.NeutronDbPluginV2.delete_port(
|
||||||
|
plugin, context, port['id'])
|
||||||
|
raise
|
||||||
|
LOG.info(_("Metadata for port %s configured successfully"), port['id'])
|
||||||
|
|
||||||
|
|
||||||
def handle_router_metadata_access(plugin, context, router_id, do_create=True):
|
def handle_router_metadata_access(plugin, context, router_id, interface=None):
|
||||||
# TODO(armando-migliaccio)
|
LOG.info(_("Handle metadata access via router: %(r)s and "
|
||||||
LOG.info('%s router %s' % (do_create, router_id))
|
"interface %(i)s") % {'r': router_id, 'i': interface})
|
||||||
|
if interface:
|
||||||
|
try:
|
||||||
|
plugin.get_port(context, interface['port_id'])
|
||||||
|
is_enabled = True
|
||||||
|
except n_exc.NotFound:
|
||||||
|
is_enabled = False
|
||||||
|
subnet_id = interface['subnet_id']
|
||||||
|
try:
|
||||||
|
plugin.lsn_manager.lsn_metadata_configure(
|
||||||
|
context, subnet_id, is_enabled)
|
||||||
|
except p_exc.NvpPluginException:
|
||||||
|
if is_enabled:
|
||||||
|
l3_db.L3_NAT_db_mixin.remove_router_interface(
|
||||||
|
plugin, context, router_id, interface)
|
||||||
|
raise
|
||||||
|
LOG.info(_("Metadata for router %s handled successfully"), router_id)
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ def handle_port_dhcp_access(plugin, context, port_data, action):
|
|||||||
_notify_rpc_agent(context, {'subnet': subnet}, 'subnet.update.end')
|
_notify_rpc_agent(context, {'subnet': subnet}, 'subnet.update.end')
|
||||||
|
|
||||||
|
|
||||||
def handle_port_metadata_access(context, port, is_delete=False):
|
def handle_port_metadata_access(plugin, context, port, is_delete=False):
|
||||||
if (cfg.CONF.NVP.metadata_mode == config.MetadataModes.INDIRECT and
|
if (cfg.CONF.NVP.metadata_mode == config.MetadataModes.INDIRECT and
|
||||||
port.get('device_owner') == const.DEVICE_OWNER_DHCP):
|
port.get('device_owner') == const.DEVICE_OWNER_DHCP):
|
||||||
if port.get('fixed_ips', []) or is_delete:
|
if port.get('fixed_ips', []) or is_delete:
|
||||||
@@ -112,7 +112,7 @@ def handle_port_metadata_access(context, port, is_delete=False):
|
|||||||
context.session.add(route)
|
context.session.add(route)
|
||||||
|
|
||||||
|
|
||||||
def handle_router_metadata_access(plugin, context, router_id, do_create=True):
|
def handle_router_metadata_access(plugin, context, router_id, interface=None):
|
||||||
if cfg.CONF.NVP.metadata_mode != config.MetadataModes.DIRECT:
|
if cfg.CONF.NVP.metadata_mode != config.MetadataModes.DIRECT:
|
||||||
LOG.debug(_("Metadata access network is disabled"))
|
LOG.debug(_("Metadata access network is disabled"))
|
||||||
return
|
return
|
||||||
@@ -128,7 +128,7 @@ def handle_router_metadata_access(plugin, context, router_id, do_create=True):
|
|||||||
plugin, ctx_elevated, filters=device_filter)
|
plugin, ctx_elevated, filters=device_filter)
|
||||||
try:
|
try:
|
||||||
if ports:
|
if ports:
|
||||||
if (do_create and
|
if (interface and
|
||||||
not _find_metadata_port(plugin, ctx_elevated, ports)):
|
not _find_metadata_port(plugin, ctx_elevated, ports)):
|
||||||
_create_metadata_access_network(
|
_create_metadata_access_network(
|
||||||
plugin, ctx_elevated, router_id)
|
plugin, ctx_elevated, router_id)
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ class DhcpMetadataAccess(object):
|
|||||||
self.supported_extension_aliases.remove(
|
self.supported_extension_aliases.remove(
|
||||||
"dhcp_agent_scheduler")
|
"dhcp_agent_scheduler")
|
||||||
nvp_svc.register_dhcp_opts(cfg)
|
nvp_svc.register_dhcp_opts(cfg)
|
||||||
|
nvp_svc.register_metadata_opts(cfg)
|
||||||
self.lsn_manager = nvp_svc.LsnManager(self)
|
self.lsn_manager = nvp_svc.LsnManager(self)
|
||||||
self.agent_notifiers[const.AGENT_TYPE_DHCP] = (
|
self.agent_notifiers[const.AGENT_TYPE_DHCP] = (
|
||||||
nvp_svc.DhcpAgentNotifyAPI(self, self.lsn_manager))
|
nvp_svc.DhcpAgentNotifyAPI(self, self.lsn_manager))
|
||||||
@@ -106,9 +107,10 @@ class DhcpMetadataAccess(object):
|
|||||||
self.handle_port_dhcp_access_delegate(self, context, port_data, action)
|
self.handle_port_dhcp_access_delegate(self, context, port_data, action)
|
||||||
|
|
||||||
def handle_port_metadata_access(self, context, port, is_delete=False):
|
def handle_port_metadata_access(self, context, port, is_delete=False):
|
||||||
self.handle_port_metadata_access_delegate(context, port, is_delete)
|
self.handle_port_metadata_access_delegate(self, context,
|
||||||
|
port, is_delete)
|
||||||
|
|
||||||
def handle_router_metadata_access(self, context,
|
def handle_router_metadata_access(self, context,
|
||||||
router_id, do_create=True):
|
router_id, interface=None):
|
||||||
self.handle_metadata_access_delegate(self, context,
|
self.handle_metadata_access_delegate(self, context,
|
||||||
router_id, do_create)
|
router_id, interface)
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ HTTP_PUT = "PUT"
|
|||||||
SERVICECLUSTER_RESOURCE = "service-cluster"
|
SERVICECLUSTER_RESOURCE = "service-cluster"
|
||||||
LSERVICESNODE_RESOURCE = "lservices-node"
|
LSERVICESNODE_RESOURCE = "lservices-node"
|
||||||
LSERVICESNODEPORT_RESOURCE = "lport/%s" % LSERVICESNODE_RESOURCE
|
LSERVICESNODEPORT_RESOURCE = "lport/%s" % LSERVICESNODE_RESOURCE
|
||||||
|
SUPPORTED_METADATA_OPTIONS = ['metadata_proxy_shared_secret']
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
LOG = log.getLogger(__name__)
|
||||||
|
|
||||||
@@ -82,6 +83,18 @@ def lsn_delete(cluster, lsn_id):
|
|||||||
cluster=cluster)
|
cluster=cluster)
|
||||||
|
|
||||||
|
|
||||||
|
def lsn_port_host_entries_update(
|
||||||
|
cluster, lsn_id, lsn_port_id, conf, hosts_data):
|
||||||
|
hosts_obj = {'hosts': hosts_data}
|
||||||
|
do_request(HTTP_PUT,
|
||||||
|
_build_uri_path(LSERVICESNODEPORT_RESOURCE,
|
||||||
|
parent_resource_id=lsn_id,
|
||||||
|
resource_id=lsn_port_id,
|
||||||
|
extra_action=conf),
|
||||||
|
json.dumps(hosts_obj),
|
||||||
|
cluster=cluster)
|
||||||
|
|
||||||
|
|
||||||
def lsn_port_create(cluster, lsn_id, port_data):
|
def lsn_port_create(cluster, lsn_id, port_data):
|
||||||
port_obj = {
|
port_obj = {
|
||||||
"ip_address": port_data["ip_address"],
|
"ip_address": port_data["ip_address"],
|
||||||
@@ -151,6 +164,18 @@ def lsn_port_plug_network(cluster, lsn_id, lsn_port_id, lswitch_port_id):
|
|||||||
raise nvp_exc.LsnConfigurationConflict(lsn_id=lsn_id)
|
raise nvp_exc.LsnConfigurationConflict(lsn_id=lsn_id)
|
||||||
|
|
||||||
|
|
||||||
|
def _lsn_configure_action(
|
||||||
|
cluster, lsn_id, action, is_enabled, obj):
|
||||||
|
lsn_obj = {"enabled": is_enabled}
|
||||||
|
lsn_obj.update(obj)
|
||||||
|
do_request(HTTP_PUT,
|
||||||
|
_build_uri_path(LSERVICESNODE_RESOURCE,
|
||||||
|
resource_id=lsn_id,
|
||||||
|
extra_action=action),
|
||||||
|
json.dumps(lsn_obj),
|
||||||
|
cluster=cluster)
|
||||||
|
|
||||||
|
|
||||||
def _lsn_port_configure_action(
|
def _lsn_port_configure_action(
|
||||||
cluster, lsn_id, lsn_port_id, action, is_enabled, obj):
|
cluster, lsn_id, lsn_port_id, action, is_enabled, obj):
|
||||||
do_request(HTTP_PUT,
|
do_request(HTTP_PUT,
|
||||||
@@ -179,6 +204,22 @@ def lsn_port_dhcp_configure(
|
|||||||
cluster, lsn_id, lsn_port_id, 'dhcp', is_enabled, dhcp_obj)
|
cluster, lsn_id, lsn_port_id, 'dhcp', is_enabled, dhcp_obj)
|
||||||
|
|
||||||
|
|
||||||
|
def lsn_metadata_configure(
|
||||||
|
cluster, lsn_id, is_enabled=True, metadata_info=None):
|
||||||
|
opts = [
|
||||||
|
"%s=%s" % (opt, metadata_info[opt])
|
||||||
|
for opt in SUPPORTED_METADATA_OPTIONS
|
||||||
|
if metadata_info.get(opt)
|
||||||
|
]
|
||||||
|
meta_obj = {
|
||||||
|
'metadata_server_ip': metadata_info['metadata_server_ip'],
|
||||||
|
'metadata_server_port': metadata_info['metadata_server_port'],
|
||||||
|
'misc_options': opts
|
||||||
|
}
|
||||||
|
_lsn_configure_action(
|
||||||
|
cluster, lsn_id, 'metadata-proxy', is_enabled, meta_obj)
|
||||||
|
|
||||||
|
|
||||||
def _lsn_port_host_action(
|
def _lsn_port_host_action(
|
||||||
cluster, lsn_id, lsn_port_id, host_obj, extra_action, action):
|
cluster, lsn_id, lsn_port_id, host_obj, extra_action, action):
|
||||||
do_request(HTTP_POST,
|
do_request(HTTP_POST,
|
||||||
@@ -199,3 +240,13 @@ def lsn_port_dhcp_host_add(cluster, lsn_id, lsn_port_id, host_data):
|
|||||||
def lsn_port_dhcp_host_remove(cluster, lsn_id, lsn_port_id, host_data):
|
def lsn_port_dhcp_host_remove(cluster, lsn_id, lsn_port_id, host_data):
|
||||||
_lsn_port_host_action(
|
_lsn_port_host_action(
|
||||||
cluster, lsn_id, lsn_port_id, host_data, 'dhcp', 'remove_host')
|
cluster, lsn_id, lsn_port_id, host_data, 'dhcp', 'remove_host')
|
||||||
|
|
||||||
|
|
||||||
|
def lsn_port_metadata_host_add(cluster, lsn_id, lsn_port_id, host_data):
|
||||||
|
_lsn_port_host_action(
|
||||||
|
cluster, lsn_id, lsn_port_id, host_data, 'metadata-proxy', 'add_host')
|
||||||
|
|
||||||
|
|
||||||
|
def lsn_port_metadata_host_remove(cluster, lsn_id, lsn_port_id, host_data):
|
||||||
|
_lsn_port_host_action(cluster, lsn_id, lsn_port_id,
|
||||||
|
host_data, 'metadata-proxy', 'remove_host')
|
||||||
|
|||||||
@@ -36,10 +36,12 @@ class LsnManagerTestCase(base.BaseTestCase):
|
|||||||
self.lsn_id = 'foo_lsn_id'
|
self.lsn_id = 'foo_lsn_id'
|
||||||
self.mac = 'aa:bb:cc:dd:ee:ff'
|
self.mac = 'aa:bb:cc:dd:ee:ff'
|
||||||
self.lsn_port_id = 'foo_lsn_port_id'
|
self.lsn_port_id = 'foo_lsn_port_id'
|
||||||
|
self.tenant_id = 'foo_tenant_id'
|
||||||
self.manager = nvp.LsnManager(mock.Mock())
|
self.manager = nvp.LsnManager(mock.Mock())
|
||||||
self.mock_lsn_api_p = mock.patch.object(nvp, 'lsn_api')
|
self.mock_lsn_api_p = mock.patch.object(nvp, 'lsn_api')
|
||||||
self.mock_lsn_api = self.mock_lsn_api_p.start()
|
self.mock_lsn_api = self.mock_lsn_api_p.start()
|
||||||
nvp.register_dhcp_opts(cfg)
|
nvp.register_dhcp_opts(cfg)
|
||||||
|
nvp.register_metadata_opts(cfg)
|
||||||
self.addCleanup(cfg.CONF.reset)
|
self.addCleanup(cfg.CONF.reset)
|
||||||
self.addCleanup(self.mock_lsn_api_p.stop)
|
self.addCleanup(self.mock_lsn_api_p.stop)
|
||||||
|
|
||||||
@@ -290,6 +292,86 @@ class LsnManagerTestCase(base.BaseTestCase):
|
|||||||
self._test_lsn_port_dhcp_configure_with_subnet(
|
self._test_lsn_port_dhcp_configure_with_subnet(
|
||||||
expected, routes=['8.8.8.8', '9.9.9.9'])
|
expected, routes=['8.8.8.8', '9.9.9.9'])
|
||||||
|
|
||||||
|
def _test_lsn_metadata_configure(self, is_enabled):
|
||||||
|
with mock.patch.object(self.manager, 'lsn_port_dispose') as f:
|
||||||
|
self.manager.plugin.get_subnet.return_value = (
|
||||||
|
{'network_id': self.net_id})
|
||||||
|
self.manager.lsn_metadata_configure(mock.ANY,
|
||||||
|
self.sub_id, is_enabled)
|
||||||
|
expected = {
|
||||||
|
'metadata_server_port': 8775,
|
||||||
|
'metadata_server_ip': '127.0.0.1',
|
||||||
|
'metadata_proxy_shared_secret': ''
|
||||||
|
}
|
||||||
|
self.mock_lsn_api.lsn_metadata_configure.assert_called_once_with(
|
||||||
|
mock.ANY, mock.ANY, is_enabled, expected)
|
||||||
|
if is_enabled:
|
||||||
|
self.assertEqual(
|
||||||
|
1, self.mock_lsn_api.lsn_port_by_subnet_get.call_count)
|
||||||
|
else:
|
||||||
|
self.assertEqual(1, f.call_count)
|
||||||
|
|
||||||
|
def test_lsn_metadata_configure_enabled(self):
|
||||||
|
self._test_lsn_metadata_configure(True)
|
||||||
|
|
||||||
|
def test_lsn_metadata_configure_disabled(self):
|
||||||
|
self._test_lsn_metadata_configure(False)
|
||||||
|
|
||||||
|
def test_lsn_metadata_configure_not_found(self):
|
||||||
|
self.mock_lsn_api.lsn_metadata_configure.side_effect = (
|
||||||
|
p_exc.LsnNotFound(entity='lsn', entity_id=self.lsn_id))
|
||||||
|
self.manager.plugin.get_subnet.return_value = (
|
||||||
|
{'network_id': self.net_id})
|
||||||
|
self.assertRaises(p_exc.NvpPluginException,
|
||||||
|
self.manager.lsn_metadata_configure,
|
||||||
|
mock.ANY, self.sub_id, True)
|
||||||
|
|
||||||
|
def test_lsn_port_metadata_setup(self):
|
||||||
|
subnet = {
|
||||||
|
'cidr': '0.0.0.0/0',
|
||||||
|
'id': self.sub_id,
|
||||||
|
'network_id': self.net_id,
|
||||||
|
'tenant_id': self.tenant_id
|
||||||
|
}
|
||||||
|
with mock.patch.object(nvp.nvplib, 'create_lport') as f:
|
||||||
|
f.return_value = {'uuid': self.port_id}
|
||||||
|
self.manager.lsn_port_metadata_setup(mock.ANY, self.lsn_id, subnet)
|
||||||
|
self.assertEqual(1, self.mock_lsn_api.lsn_port_create.call_count)
|
||||||
|
self.mock_lsn_api.lsn_port_plug_network.assert_called_once_with(
|
||||||
|
mock.ANY, self.lsn_id, mock.ANY, self.port_id)
|
||||||
|
|
||||||
|
def test_lsn_port_metadata_setup_raise_not_found(self):
|
||||||
|
subnet = {
|
||||||
|
'cidr': '0.0.0.0/0',
|
||||||
|
'id': self.sub_id,
|
||||||
|
'network_id': self.net_id,
|
||||||
|
'tenant_id': self.tenant_id
|
||||||
|
}
|
||||||
|
with mock.patch.object(nvp.nvplib, 'create_lport') as f:
|
||||||
|
f.side_effect = n_exc.NotFound
|
||||||
|
self.assertRaises(p_exc.PortConfigurationError,
|
||||||
|
self.manager.lsn_port_metadata_setup,
|
||||||
|
mock.ANY, self.lsn_id, subnet)
|
||||||
|
|
||||||
|
def test_lsn_port_metadata_setup_raise_conflict(self):
|
||||||
|
subnet = {
|
||||||
|
'cidr': '0.0.0.0/0',
|
||||||
|
'id': self.sub_id,
|
||||||
|
'network_id': self.net_id,
|
||||||
|
'tenant_id': self.tenant_id
|
||||||
|
}
|
||||||
|
with mock.patch.object(nvp.nvplib, 'create_lport') as f:
|
||||||
|
with mock.patch.object(nvp.nvplib, 'delete_port') as g:
|
||||||
|
f.return_value = {'uuid': self.port_id}
|
||||||
|
self.mock_lsn_api.lsn_port_plug_network.side_effect = (
|
||||||
|
p_exc.LsnConfigurationConflict(lsn_id=self.lsn_id))
|
||||||
|
self.assertRaises(p_exc.PortConfigurationError,
|
||||||
|
self.manager.lsn_port_metadata_setup,
|
||||||
|
mock.ANY, self.lsn_id, subnet)
|
||||||
|
self.assertEqual(1,
|
||||||
|
self.mock_lsn_api.lsn_port_delete.call_count)
|
||||||
|
self.assertEqual(1, g.call_count)
|
||||||
|
|
||||||
def _test_lsn_port_dispose_with_values(self, lsn_id, lsn_port_id, count):
|
def _test_lsn_port_dispose_with_values(self, lsn_id, lsn_port_id, count):
|
||||||
with mock.patch.object(self.manager,
|
with mock.patch.object(self.manager,
|
||||||
'lsn_port_get_by_mac',
|
'lsn_port_get_by_mac',
|
||||||
@@ -302,6 +384,17 @@ class LsnManagerTestCase(base.BaseTestCase):
|
|||||||
self._test_lsn_port_dispose_with_values(
|
self._test_lsn_port_dispose_with_values(
|
||||||
self.lsn_id, self.lsn_port_id, 1)
|
self.lsn_id, self.lsn_port_id, 1)
|
||||||
|
|
||||||
|
def test_lsn_port_dispose_meta_mac(self):
|
||||||
|
self.mac = nvp.METADATA_MAC
|
||||||
|
with mock.patch.object(nvp.nvplib, 'get_port_by_neutron_tag') as f:
|
||||||
|
with mock.patch.object(nvp.nvplib, 'delete_port') as g:
|
||||||
|
f.return_value = {'uuid': self.port_id}
|
||||||
|
self._test_lsn_port_dispose_with_values(
|
||||||
|
self.lsn_id, self.lsn_port_id, 1)
|
||||||
|
f.assert_called_once_with(
|
||||||
|
mock.ANY, self.net_id, nvp.METADATA_PORT_ID)
|
||||||
|
g.assert_called_once_with(mock.ANY, self.net_id, self.port_id)
|
||||||
|
|
||||||
def test_lsn_port_dispose_lsn_not_found(self):
|
def test_lsn_port_dispose_lsn_not_found(self):
|
||||||
self._test_lsn_port_dispose_with_values(None, None, 0)
|
self._test_lsn_port_dispose_with_values(None, None, 0)
|
||||||
|
|
||||||
@@ -334,6 +427,33 @@ class LsnManagerTestCase(base.BaseTestCase):
|
|||||||
self.manager._lsn_port_host_conf, mock.ANY,
|
self.manager._lsn_port_host_conf, mock.ANY,
|
||||||
self.net_id, self.sub_id, mock.ANY, mock.Mock())
|
self.net_id, self.sub_id, mock.ANY, mock.Mock())
|
||||||
|
|
||||||
|
def _test_lsn_port_update(self, dhcp=None, meta=None):
|
||||||
|
self.manager.lsn_port_update(
|
||||||
|
mock.ANY, self.net_id, self.sub_id, dhcp, meta)
|
||||||
|
count = 1 if dhcp else 0
|
||||||
|
count = count + 1 if meta else count
|
||||||
|
self.assertEqual(count, (self.mock_lsn_api.
|
||||||
|
lsn_port_host_entries_update.call_count))
|
||||||
|
|
||||||
|
def test_lsn_port_update(self):
|
||||||
|
self._test_lsn_port_update()
|
||||||
|
|
||||||
|
def test_lsn_port_update_dhcp_meta(self):
|
||||||
|
self._test_lsn_port_update(mock.ANY, mock.ANY)
|
||||||
|
|
||||||
|
def test_lsn_port_update_dhcp_and_nometa(self):
|
||||||
|
self._test_lsn_port_update(mock.ANY, None)
|
||||||
|
|
||||||
|
def test_lsn_port_update_nodhcp_and_nmeta(self):
|
||||||
|
self._test_lsn_port_update(None, mock.ANY)
|
||||||
|
|
||||||
|
def test_lsn_port_update_raise_error(self):
|
||||||
|
self.mock_lsn_api.lsn_port_host_entries_update.side_effect = (
|
||||||
|
NvpApiException)
|
||||||
|
self.assertRaises(p_exc.PortConfigurationError,
|
||||||
|
self.manager.lsn_port_update,
|
||||||
|
mock.ANY, mock.ANY, mock.ANY, mock.ANY)
|
||||||
|
|
||||||
|
|
||||||
class DhcpAgentNotifyAPITestCase(base.BaseTestCase):
|
class DhcpAgentNotifyAPITestCase(base.BaseTestCase):
|
||||||
|
|
||||||
@@ -343,6 +463,107 @@ class DhcpAgentNotifyAPITestCase(base.BaseTestCase):
|
|||||||
self.plugin = self.notifier.plugin
|
self.plugin = self.notifier.plugin
|
||||||
self.lsn_manager = self.notifier.lsn_manager
|
self.lsn_manager = self.notifier.lsn_manager
|
||||||
|
|
||||||
|
def _test_notify_port_update(
|
||||||
|
self, ports, expected_count, expected_args=None):
|
||||||
|
port = {
|
||||||
|
'id': 'foo_port_id',
|
||||||
|
'network_id': 'foo_network_id',
|
||||||
|
'fixed_ips': [{'subnet_id': 'foo_subnet_id'}]
|
||||||
|
}
|
||||||
|
self.notifier.plugin.get_ports.return_value = ports
|
||||||
|
self.notifier.notify(mock.ANY, {'port': port}, 'port.update.end')
|
||||||
|
self.lsn_manager.lsn_port_update.assert_has_calls(expected_args)
|
||||||
|
|
||||||
|
def test_notify_ports_update_no_ports(self):
|
||||||
|
self._test_notify_port_update(None, 0, [])
|
||||||
|
self._test_notify_port_update([], 0, [])
|
||||||
|
|
||||||
|
def test_notify_ports_update_one_port(self):
|
||||||
|
ports = [{
|
||||||
|
'fixed_ips': [{'subnet_id': 'foo_subnet_id',
|
||||||
|
'ip_address': '1.2.3.4'}],
|
||||||
|
'device_id': 'foo_device_id',
|
||||||
|
'device_owner': 'foo_device_owner',
|
||||||
|
'mac_address': 'fa:16:3e:da:1d:46'
|
||||||
|
}]
|
||||||
|
call_args = mock.call(
|
||||||
|
mock.ANY, 'foo_network_id', 'foo_subnet_id',
|
||||||
|
dhcp=[{'ip_address': '1.2.3.4',
|
||||||
|
'mac_address': 'fa:16:3e:da:1d:46'}],
|
||||||
|
meta=[{'instance_id': 'foo_device_id',
|
||||||
|
'ip_address': '1.2.3.4'}])
|
||||||
|
self._test_notify_port_update(ports, 1, call_args)
|
||||||
|
|
||||||
|
def test_notify_ports_update_ports_with_empty_device_id(self):
|
||||||
|
ports = [{
|
||||||
|
'fixed_ips': [{'subnet_id': 'foo_subnet_id',
|
||||||
|
'ip_address': '1.2.3.4'}],
|
||||||
|
'device_id': '',
|
||||||
|
'device_owner': 'foo_device_owner',
|
||||||
|
'mac_address': 'fa:16:3e:da:1d:46'
|
||||||
|
}]
|
||||||
|
call_args = mock.call(
|
||||||
|
mock.ANY, 'foo_network_id', 'foo_subnet_id',
|
||||||
|
dhcp=[{'ip_address': '1.2.3.4',
|
||||||
|
'mac_address': 'fa:16:3e:da:1d:46'}],
|
||||||
|
meta=[]
|
||||||
|
)
|
||||||
|
self._test_notify_port_update(ports, 1, call_args)
|
||||||
|
|
||||||
|
def test_notify_ports_update_ports_with_no_fixed_ips(self):
|
||||||
|
ports = [{
|
||||||
|
'fixed_ips': [],
|
||||||
|
'device_id': 'foo_device_id',
|
||||||
|
'device_owner': 'foo_device_owner',
|
||||||
|
'mac_address': 'fa:16:3e:da:1d:46'
|
||||||
|
}]
|
||||||
|
call_args = mock.call(
|
||||||
|
mock.ANY, 'foo_network_id', 'foo_subnet_id', dhcp=[], meta=[])
|
||||||
|
self._test_notify_port_update(ports, 1, call_args)
|
||||||
|
|
||||||
|
def test_notify_ports_update_ports_with_no_fixed_ips_and_no_device(self):
|
||||||
|
ports = [{
|
||||||
|
'fixed_ips': [],
|
||||||
|
'device_id': '',
|
||||||
|
'device_owner': 'foo_device_owner',
|
||||||
|
'mac_address': 'fa:16:3e:da:1d:46'
|
||||||
|
}]
|
||||||
|
call_args = mock.call(
|
||||||
|
mock.ANY, 'foo_network_id', 'foo_subnet_id', dhcp=[], meta=[])
|
||||||
|
self._test_notify_port_update(ports, 0, call_args)
|
||||||
|
|
||||||
|
def test_notify_ports_update_with_special_ports(self):
|
||||||
|
ports = [{'fixed_ips': [],
|
||||||
|
'device_id': '',
|
||||||
|
'device_owner': 'network:dhcp',
|
||||||
|
'mac_address': 'fa:16:3e:da:1d:46'},
|
||||||
|
{'fixed_ips': [{'subnet_id': 'foo_subnet_id',
|
||||||
|
'ip_address': '1.2.3.4'}],
|
||||||
|
'device_id': 'foo_device_id',
|
||||||
|
'device_owner': 'network:router_gateway',
|
||||||
|
'mac_address': 'fa:16:3e:da:1d:46'}]
|
||||||
|
call_args = mock.call(
|
||||||
|
mock.ANY, 'foo_network_id', 'foo_subnet_id', dhcp=[], meta=[])
|
||||||
|
self._test_notify_port_update(ports, 0, call_args)
|
||||||
|
|
||||||
|
def test_notify_ports_update_many_ports(self):
|
||||||
|
ports = [{'fixed_ips': [],
|
||||||
|
'device_id': '',
|
||||||
|
'device_owner': 'foo_device_owner',
|
||||||
|
'mac_address': 'fa:16:3e:da:1d:46'},
|
||||||
|
{'fixed_ips': [{'subnet_id': 'foo_subnet_id',
|
||||||
|
'ip_address': '1.2.3.4'}],
|
||||||
|
'device_id': 'foo_device_id',
|
||||||
|
'device_owner': 'foo_device_owner',
|
||||||
|
'mac_address': 'fa:16:3e:da:1d:46'}]
|
||||||
|
call_args = mock.call(
|
||||||
|
mock.ANY, 'foo_network_id', 'foo_subnet_id',
|
||||||
|
dhcp=[{'ip_address': '1.2.3.4',
|
||||||
|
'mac_address': 'fa:16:3e:da:1d:46'}],
|
||||||
|
meta=[{'instance_id': 'foo_device_id',
|
||||||
|
'ip_address': '1.2.3.4'}])
|
||||||
|
self._test_notify_port_update(ports, 1, call_args)
|
||||||
|
|
||||||
def _test_notify_subnet_action(self, action):
|
def _test_notify_subnet_action(self, action):
|
||||||
with mock.patch.object(self.notifier, '_subnet_%s' % action) as f:
|
with mock.patch.object(self.notifier, '_subnet_%s' % action) as f:
|
||||||
self.notifier._handle_subnet_dhcp_access[action] = f
|
self.notifier._handle_subnet_dhcp_access[action] = f
|
||||||
@@ -631,3 +852,148 @@ class DhcpTestCase(base.BaseTestCase):
|
|||||||
def test_handle_delete_user_port_no_fixed_ips(self):
|
def test_handle_delete_user_port_no_fixed_ips(self):
|
||||||
self._test_handle_user_port_no_fixed_ips(
|
self._test_handle_user_port_no_fixed_ips(
|
||||||
'delete_port', self.plugin.lsn_manager.lsn_port_dhcp_host_remove)
|
'delete_port', self.plugin.lsn_manager.lsn_port_dhcp_host_remove)
|
||||||
|
|
||||||
|
|
||||||
|
class MetadataTestCase(base.BaseTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(MetadataTestCase, self).setUp()
|
||||||
|
self.plugin = mock.Mock()
|
||||||
|
self.plugin.lsn_manager = mock.Mock()
|
||||||
|
|
||||||
|
def _test_handle_port_metadata_access_special_owners(
|
||||||
|
self, owner, dev_id='foo_device_id', ips=None):
|
||||||
|
port = {
|
||||||
|
'id': 'foo_port_id',
|
||||||
|
'device_owner': owner,
|
||||||
|
'device_id': dev_id,
|
||||||
|
'fixed_ips': ips or []
|
||||||
|
}
|
||||||
|
nvp.handle_port_metadata_access(self.plugin, mock.ANY, port, mock.ANY)
|
||||||
|
self.assertFalse(
|
||||||
|
self.plugin.lsn_manager.lsn_port_meta_host_add.call_count)
|
||||||
|
self.assertFalse(
|
||||||
|
self.plugin.lsn_manager.lsn_port_meta_host_remove.call_count)
|
||||||
|
|
||||||
|
def test_handle_port_metadata_access_external_network(self):
|
||||||
|
port = {
|
||||||
|
'id': 'foo_port_id',
|
||||||
|
'device_owner': 'foo_device_owner',
|
||||||
|
'device_id': 'foo_device_id',
|
||||||
|
'network_id': 'foo_network_id',
|
||||||
|
'fixed_ips': [{'subnet_id': 'foo_subnet'}]
|
||||||
|
}
|
||||||
|
self.plugin.get_network.return_value = {'router:external': True}
|
||||||
|
nvp.handle_port_metadata_access(self.plugin, mock.ANY, port, mock.ANY)
|
||||||
|
self.assertFalse(
|
||||||
|
self.plugin.lsn_manager.lsn_port_meta_host_add.call_count)
|
||||||
|
self.assertFalse(
|
||||||
|
self.plugin.lsn_manager.lsn_port_meta_host_remove.call_count)
|
||||||
|
|
||||||
|
def test_handle_port_metadata_access_dhcp_port(self):
|
||||||
|
self._test_handle_port_metadata_access_special_owners(
|
||||||
|
'network:dhcp', [{'subnet_id': 'foo_subnet'}])
|
||||||
|
|
||||||
|
def test_handle_port_metadata_access_router_port(self):
|
||||||
|
self._test_handle_port_metadata_access_special_owners(
|
||||||
|
'network:router_interface', [{'subnet_id': 'foo_subnet'}])
|
||||||
|
|
||||||
|
def test_handle_port_metadata_access_no_device_id(self):
|
||||||
|
self._test_handle_port_metadata_access_special_owners(
|
||||||
|
'network:dhcp', '')
|
||||||
|
|
||||||
|
def test_handle_port_metadata_access_no_fixed_ips(self):
|
||||||
|
self._test_handle_port_metadata_access_special_owners(
|
||||||
|
'foo', 'foo', None)
|
||||||
|
|
||||||
|
def _test_handle_port_metadata_access(self, is_delete, raise_exc=False):
|
||||||
|
port = {
|
||||||
|
'id': 'foo_port_id',
|
||||||
|
'device_owner': 'foo_device_id',
|
||||||
|
'network_id': 'foo_network_id',
|
||||||
|
'device_id': 'foo_device_id',
|
||||||
|
'tenant_id': 'foo_tenant_id',
|
||||||
|
'fixed_ips': [
|
||||||
|
{'subnet_id': 'foo_subnet_id', 'ip_address': '1.2.3.4'}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
meta = {
|
||||||
|
'instance_id': port['device_id'],
|
||||||
|
'tenant_id': port['tenant_id'],
|
||||||
|
'ip_address': port['fixed_ips'][0]['ip_address']
|
||||||
|
}
|
||||||
|
self.plugin.get_network.return_value = {'router:external': False}
|
||||||
|
if is_delete:
|
||||||
|
mock_func = self.plugin.lsn_manager.lsn_port_meta_host_remove
|
||||||
|
else:
|
||||||
|
mock_func = self.plugin.lsn_manager.lsn_port_meta_host_add
|
||||||
|
if raise_exc:
|
||||||
|
mock_func.side_effect = p_exc.PortConfigurationError(
|
||||||
|
lsn_id='foo_lsn_id', net_id='foo_net_id', port_id=None)
|
||||||
|
with mock.patch.object(nvp.db_base_plugin_v2.NeutronDbPluginV2,
|
||||||
|
'delete_port') as d:
|
||||||
|
self.assertRaises(p_exc.PortConfigurationError,
|
||||||
|
nvp.handle_port_metadata_access,
|
||||||
|
self.plugin, mock.ANY, port,
|
||||||
|
is_delete=is_delete)
|
||||||
|
if not is_delete:
|
||||||
|
d.assert_called_once_with(mock.ANY, mock.ANY, port['id'])
|
||||||
|
else:
|
||||||
|
self.assertFalse(d.call_count)
|
||||||
|
else:
|
||||||
|
nvp.handle_port_metadata_access(
|
||||||
|
self.plugin, mock.ANY, port, is_delete=is_delete)
|
||||||
|
mock_func.assert_called_once_with(mock.ANY, mock.ANY, mock.ANY, meta)
|
||||||
|
|
||||||
|
def test_handle_port_metadata_access_on_delete_true(self):
|
||||||
|
self._test_handle_port_metadata_access(True)
|
||||||
|
|
||||||
|
def test_handle_port_metadata_access_on_delete_false(self):
|
||||||
|
self._test_handle_port_metadata_access(False)
|
||||||
|
|
||||||
|
def test_handle_port_metadata_access_on_delete_true_raise(self):
|
||||||
|
self._test_handle_port_metadata_access(True, raise_exc=True)
|
||||||
|
|
||||||
|
def test_handle_port_metadata_access_on_delete_false_raise(self):
|
||||||
|
self._test_handle_port_metadata_access(False, raise_exc=True)
|
||||||
|
|
||||||
|
def _test_handle_router_metadata_access(
|
||||||
|
self, is_port_found, raise_exc=False):
|
||||||
|
subnet = {
|
||||||
|
'id': 'foo_subnet_id',
|
||||||
|
'network_id': 'foo_network_id'
|
||||||
|
}
|
||||||
|
interface = {
|
||||||
|
'subnet_id': subnet['id'],
|
||||||
|
'port_id': 'foo_port_id'
|
||||||
|
}
|
||||||
|
mock_func = self.plugin.lsn_manager.lsn_metadata_configure
|
||||||
|
if not is_port_found:
|
||||||
|
self.plugin.get_port.side_effect = n_exc.NotFound
|
||||||
|
if raise_exc:
|
||||||
|
with mock.patch.object(nvp.l3_db.L3_NAT_db_mixin,
|
||||||
|
'remove_router_interface') as d:
|
||||||
|
mock_func.side_effect = p_exc.NvpPluginException(err_msg='')
|
||||||
|
self.assertRaises(p_exc.NvpPluginException,
|
||||||
|
nvp.handle_router_metadata_access,
|
||||||
|
self.plugin, mock.ANY, 'foo_router_id',
|
||||||
|
interface)
|
||||||
|
d.assert_called_once_with(mock.ANY, mock.ANY, 'foo_router_id',
|
||||||
|
interface)
|
||||||
|
else:
|
||||||
|
nvp.handle_router_metadata_access(
|
||||||
|
self.plugin, mock.ANY, 'foo_router_id', interface)
|
||||||
|
mock_func.assert_called_once_with(
|
||||||
|
mock.ANY, subnet['id'], is_port_found)
|
||||||
|
|
||||||
|
def test_handle_router_metadata_access_add_interface(self):
|
||||||
|
self._test_handle_router_metadata_access(True)
|
||||||
|
|
||||||
|
def test_handle_router_metadata_access_delete_interface(self):
|
||||||
|
self._test_handle_router_metadata_access(False)
|
||||||
|
|
||||||
|
def test_handle_router_metadata_access_raise_error_on_add(self):
|
||||||
|
self._test_handle_router_metadata_access(True, raise_exc=True)
|
||||||
|
|
||||||
|
def test_handle_router_metadata_access_raise_error_on_delete(self):
|
||||||
|
self._test_handle_router_metadata_access(True, raise_exc=False)
|
||||||
|
|||||||
@@ -112,6 +112,31 @@ class LSNTestCase(base.BaseTestCase):
|
|||||||
"DELETE",
|
"DELETE",
|
||||||
"/ws.v1/lservices-node/%s" % lsn_id, cluster=self.cluster)
|
"/ws.v1/lservices-node/%s" % lsn_id, cluster=self.cluster)
|
||||||
|
|
||||||
|
def _test_lsn_port_host_entries_update(self, lsn_type, hosts_data):
|
||||||
|
lsn_id = 'foo_lsn_id'
|
||||||
|
lsn_port_id = 'foo_lsn_port_id'
|
||||||
|
lsnlib.lsn_port_host_entries_update(
|
||||||
|
self.cluster, lsn_id, lsn_port_id, lsn_type, hosts_data)
|
||||||
|
self.mock_request.assert_called_once_with(
|
||||||
|
'PUT',
|
||||||
|
'/ws.v1/lservices-node/%s/lport/%s/%s' % (lsn_id,
|
||||||
|
lsn_port_id,
|
||||||
|
lsn_type),
|
||||||
|
json.dumps({'hosts': hosts_data}),
|
||||||
|
cluster=self.cluster)
|
||||||
|
|
||||||
|
def test_lsn_port_dhcp_entries_update(self):
|
||||||
|
hosts_data = [{"ip_address": "11.22.33.44",
|
||||||
|
"mac_address": "aa:bb:cc:dd:ee:ff"},
|
||||||
|
{"ip_address": "44.33.22.11",
|
||||||
|
"mac_address": "ff:ee:dd:cc:bb:aa"}]
|
||||||
|
self._test_lsn_port_host_entries_update("dhcp", hosts_data)
|
||||||
|
|
||||||
|
def test_lsn_port_metadata_entries_update(self):
|
||||||
|
hosts_data = [{"ip_address": "11.22.33.44",
|
||||||
|
"device_id": "foo_vm_uuid"}]
|
||||||
|
self._test_lsn_port_host_entries_update("metadata-proxy", hosts_data)
|
||||||
|
|
||||||
def test_lsn_port_create(self):
|
def test_lsn_port_create(self):
|
||||||
port_data = {
|
port_data = {
|
||||||
"ip_address": "1.2.3.0/24",
|
"ip_address": "1.2.3.0/24",
|
||||||
@@ -230,6 +255,50 @@ class LSNTestCase(base.BaseTestCase):
|
|||||||
self._test_lsn_port_dhcp_configure(
|
self._test_lsn_port_dhcp_configure(
|
||||||
lsn_id, lsn_port_id, is_enabled, opts)
|
lsn_id, lsn_port_id, is_enabled, opts)
|
||||||
|
|
||||||
|
def _test_lsn_metadata_configure(
|
||||||
|
self, lsn_id, is_enabled, opts, expected_opts):
|
||||||
|
lsnlib.lsn_metadata_configure(
|
||||||
|
self.cluster, lsn_id, is_enabled, opts)
|
||||||
|
lsn_obj = {"enabled": is_enabled}
|
||||||
|
lsn_obj.update(expected_opts)
|
||||||
|
self.mock_request.assert_has_calls([
|
||||||
|
mock.call("PUT",
|
||||||
|
"/ws.v1/lservices-node/%s/metadata-proxy" % lsn_id,
|
||||||
|
json.dumps(lsn_obj),
|
||||||
|
cluster=self.cluster),
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_lsn_port_metadata_configure_empty_secret(self):
|
||||||
|
lsn_id = "foo_lsn_id"
|
||||||
|
is_enabled = True
|
||||||
|
opts = {
|
||||||
|
"metadata_server_ip": "1.2.3.4",
|
||||||
|
"metadata_server_port": "8775"
|
||||||
|
}
|
||||||
|
expected_opts = {
|
||||||
|
"metadata_server_ip": "1.2.3.4",
|
||||||
|
"metadata_server_port": "8775",
|
||||||
|
"misc_options": []
|
||||||
|
}
|
||||||
|
self._test_lsn_metadata_configure(
|
||||||
|
lsn_id, is_enabled, opts, expected_opts)
|
||||||
|
|
||||||
|
def test_lsn_metadata_configure_with_secret(self):
|
||||||
|
lsn_id = "foo_lsn_id"
|
||||||
|
is_enabled = True
|
||||||
|
opts = {
|
||||||
|
"metadata_server_ip": "1.2.3.4",
|
||||||
|
"metadata_server_port": "8775",
|
||||||
|
"metadata_proxy_shared_secret": "foo_secret"
|
||||||
|
}
|
||||||
|
expected_opts = {
|
||||||
|
"metadata_server_ip": "1.2.3.4",
|
||||||
|
"metadata_server_port": "8775",
|
||||||
|
"misc_options": ["metadata_proxy_shared_secret=foo_secret"]
|
||||||
|
}
|
||||||
|
self._test_lsn_metadata_configure(
|
||||||
|
lsn_id, is_enabled, opts, expected_opts)
|
||||||
|
|
||||||
def _test_lsn_port_host_action(
|
def _test_lsn_port_host_action(
|
||||||
self, lsn_port_action_func, extra_action, action, host):
|
self, lsn_port_action_func, extra_action, action, host):
|
||||||
lsn_id = "foo_lsn_id"
|
lsn_id = "foo_lsn_id"
|
||||||
@@ -256,3 +325,19 @@ class LSNTestCase(base.BaseTestCase):
|
|||||||
}
|
}
|
||||||
self._test_lsn_port_host_action(
|
self._test_lsn_port_host_action(
|
||||||
lsnlib.lsn_port_dhcp_host_remove, "dhcp", "remove_host", host)
|
lsnlib.lsn_port_dhcp_host_remove, "dhcp", "remove_host", host)
|
||||||
|
|
||||||
|
def test_lsn_port_metadata_host_add(self):
|
||||||
|
host = {
|
||||||
|
"ip_address": "1.2.3.4",
|
||||||
|
"instance_id": "foo_instance_id"
|
||||||
|
}
|
||||||
|
self._test_lsn_port_host_action(lsnlib.lsn_port_metadata_host_add,
|
||||||
|
"metadata-proxy", "add_host", host)
|
||||||
|
|
||||||
|
def test_lsn_port_metadata_host_remove(self):
|
||||||
|
host = {
|
||||||
|
"ip_address": "1.2.3.4",
|
||||||
|
"instance_id": "foo_instance_id"
|
||||||
|
}
|
||||||
|
self._test_lsn_port_host_action(lsnlib.lsn_port_metadata_host_remove,
|
||||||
|
"metadata-proxy", "remove_host", host)
|
||||||
|
|||||||
Reference in New Issue
Block a user