Merge "Add support for NSX/NVP DHCP services"
This commit is contained in:
commit
8b33d25fe0
@ -34,6 +34,12 @@
|
||||
# To be specified for providing a predefined gateway tenant for connecting their networks.
|
||||
# default_l2_gw_service_uuid =
|
||||
|
||||
# (Optional) UUID for the default service cluster. A service cluster is introduced to
|
||||
# represent a group of gateways and it is needed in order to use Logical Services like
|
||||
# dhcp and metadata in the logical space. NOTE: If agent_mode is set to 'agentless' this
|
||||
# config parameter *MUST BE* set to a valid pre-existent service cluster uuid.
|
||||
# default_service_cluster_uuid =
|
||||
|
||||
# Name of the default interface name to be used on network-gateway. This value
|
||||
# will be used for any device associated with a network gateway for which an
|
||||
# interface name was not specified
|
||||
@ -43,7 +49,6 @@
|
||||
# number of network gateways allowed per tenant, -1 means unlimited
|
||||
# quota_network_gateway = 5
|
||||
|
||||
|
||||
[nvp]
|
||||
# Maximum number of ports for each bridged logical switch
|
||||
# The recommended value for this parameter varies with NVP version
|
||||
@ -154,3 +159,13 @@
|
||||
# (Optional) Asynchronous task status check interval
|
||||
# default is 2000 (millisecond)
|
||||
# task_status_check_interval = 2000
|
||||
|
||||
[nvp_dhcp]
|
||||
# (Optional) Comma separated list of additional dns servers. Default is an empty list
|
||||
# extra_domain_name_servers =
|
||||
|
||||
# Domain to use for building the hostnames
|
||||
# domain_name = openstacklocal
|
||||
|
||||
# Default DHCP lease time
|
||||
# default_lease_time = 43200
|
||||
|
@ -24,6 +24,7 @@ from neutron.api import api_common
|
||||
from neutron.api.rpc.agentnotifiers import dhcp_rpc_agent_api
|
||||
from neutron.api.v2 import attributes
|
||||
from neutron.api.v2 import resource as wsgi_resource
|
||||
from neutron.common import constants as const
|
||||
from neutron.common import exceptions
|
||||
from neutron.openstack.common import log as logging
|
||||
from neutron.openstack.common.notifier import api as notifier_api
|
||||
@ -68,7 +69,12 @@ class Controller(object):
|
||||
self._policy_attrs = [name for (name, info) in self._attr_info.items()
|
||||
if info.get('required_by_policy')]
|
||||
self._publisher_id = notifier_api.publisher_id('network')
|
||||
self._dhcp_agent_notifier = dhcp_rpc_agent_api.DhcpAgentNotifyAPI()
|
||||
# use plugin's dhcp notifier, if this is already instantiated
|
||||
agent_notifiers = getattr(plugin, 'agent_notifiers', {})
|
||||
self._dhcp_agent_notifier = (
|
||||
agent_notifiers.get(const.AGENT_TYPE_DHCP) or
|
||||
dhcp_rpc_agent_api.DhcpAgentNotifyAPI()
|
||||
)
|
||||
self._member_actions = member_actions
|
||||
self._primary_key = self._get_primary_key()
|
||||
if self._allow_pagination and self._native_pagination:
|
||||
|
@ -161,6 +161,8 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
# TODO(salv-orlando): Replace These dicts with
|
||||
# collections.defaultdict for better handling of default values
|
||||
# Routines for managing logical ports in NVP
|
||||
self.port_special_owners = [l3_db.DEVICE_OWNER_ROUTER_GW,
|
||||
l3_db.DEVICE_OWNER_ROUTER_INTF]
|
||||
self._port_drivers = {
|
||||
'create': {l3_db.DEVICE_OWNER_ROUTER_GW:
|
||||
self._nvp_create_ext_gw_port,
|
||||
@ -469,9 +471,7 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
True)
|
||||
nicira_db.add_neutron_nvp_port_mapping(
|
||||
context.session, port_data['id'], lport['uuid'])
|
||||
if (not port_data['device_owner'] in
|
||||
(l3_db.DEVICE_OWNER_ROUTER_GW,
|
||||
l3_db.DEVICE_OWNER_ROUTER_INTF)):
|
||||
if port_data['device_owner'] not in self.port_special_owners:
|
||||
nvplib.plug_interface(self.cluster, selected_lswitch['uuid'],
|
||||
lport['uuid'], "VifAttachment",
|
||||
port_data['id'])
|
||||
|
@ -115,6 +115,9 @@ cluster_opts = [
|
||||
cfg.StrOpt('default_l2_gw_service_uuid',
|
||||
help=_("Unique identifier of the NVP L2 Gateway service "
|
||||
"which will be used by default for network gateways")),
|
||||
cfg.StrOpt('default_service_cluster_uuid',
|
||||
help=_("Unique identifier of the Service Cluster which will "
|
||||
"be used by logical services like dhcp and metadata")),
|
||||
cfg.StrOpt('default_interface_name', default='breth0',
|
||||
help=_("Name of the interface on a L2 Gateway transport node"
|
||||
"which should be used by default when setting up a "
|
||||
|
@ -80,3 +80,30 @@ class NvpServiceOverQuota(q_exc.Conflict):
|
||||
|
||||
class NvpVcnsDriverException(NvpServicePluginException):
|
||||
message = _("Error happened in NVP VCNS Driver: %(err_msg)s")
|
||||
|
||||
|
||||
class ServiceClusterUnavailable(NvpPluginException):
|
||||
message = _("Service cluster: '%(cluster_id)s' is unavailable. Please, "
|
||||
"check NVP setup and/or configuration")
|
||||
|
||||
|
||||
class PortConfigurationError(NvpPluginException):
|
||||
message = _("An error occurred while connecting LSN %(lsn_id)s "
|
||||
"and network %(net_id)s via port %(port_id)s")
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(PortConfigurationError, self).__init__(**kwargs)
|
||||
self.port_id = kwargs.get('port_id')
|
||||
|
||||
|
||||
class LsnNotFound(q_exc.NotFound):
|
||||
message = _('Unable to find LSN for %(entity)s %(entity_id)s')
|
||||
|
||||
|
||||
class LsnPortNotFound(q_exc.NotFound):
|
||||
message = (_('Unable to find port for LSN %(lsn_id)s '
|
||||
'and %(entity)s %(entity_id)s'))
|
||||
|
||||
|
||||
class LsnConfigurationConflict(NvpPluginException):
|
||||
message = _("Configuration conflict on Logical Service Node %(lsn_id)s")
|
||||
|
@ -16,9 +16,19 @@
|
||||
# under the License.
|
||||
|
||||
from neutron.openstack.common import log
|
||||
from neutron.version import version_info
|
||||
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
MAX_DISPLAY_NAME_LEN = 40
|
||||
NEUTRON_VERSION = version_info.release_string()
|
||||
|
||||
|
||||
def get_tags(**kwargs):
|
||||
tags = ([dict(tag=value, scope=key)
|
||||
for key, value in kwargs.iteritems()])
|
||||
tags.append({"tag": NEUTRON_VERSION, "scope": "quantum"})
|
||||
return tags
|
||||
|
||||
|
||||
def check_and_truncate(display_name):
|
||||
|
405
neutron/plugins/nicira/dhcp_meta/nvp.py
Normal file
405
neutron/plugins/nicira/dhcp_meta/nvp.py
Normal file
@ -0,0 +1,405 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 VMware, Inc.
|
||||
# All Rights Reserved
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
from oslo.config import cfg
|
||||
|
||||
from neutron.api.v2 import attributes as attr
|
||||
from neutron.common import constants as const
|
||||
from neutron.common import exceptions as n_exc
|
||||
from neutron.db import db_base_plugin_v2
|
||||
from neutron.openstack.common import log as logging
|
||||
from neutron.plugins.nicira.common import exceptions as p_exc
|
||||
from neutron.plugins.nicira.nsxlib import lsn as lsn_api
|
||||
from neutron.plugins.nicira import nvplib
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
dhcp_opts = [
|
||||
cfg.ListOpt('extra_domain_name_servers',
|
||||
default=[],
|
||||
help=_('Comma separated list of additional '
|
||||
'domain name servers')),
|
||||
cfg.StrOpt('domain_name',
|
||||
default='openstacklocal',
|
||||
help=_('Domain to use for building the hostnames')),
|
||||
cfg.IntOpt('default_lease_time', default=43200,
|
||||
help=_("Default DHCP lease time")),
|
||||
]
|
||||
|
||||
|
||||
def register_dhcp_opts(config):
|
||||
config.CONF.register_opts(dhcp_opts, "NVP_DHCP")
|
||||
|
||||
|
||||
class LsnManager(object):
|
||||
"""Manage LSN entities associated with networks."""
|
||||
|
||||
def __init__(self, plugin):
|
||||
self.plugin = plugin
|
||||
|
||||
@property
|
||||
def cluster(self):
|
||||
return self.plugin.cluster
|
||||
|
||||
def lsn_get(self, context, network_id, raise_on_err=True):
|
||||
"""Retrieve the LSN id associated to the network."""
|
||||
try:
|
||||
return lsn_api.lsn_for_network_get(self.cluster, network_id)
|
||||
except (n_exc.NotFound, nvplib.NvpApiClient.NvpApiException):
|
||||
logger = raise_on_err and LOG.error or LOG.warn
|
||||
logger(_('Unable to find Logical Service Node for '
|
||||
'network %s'), network_id)
|
||||
if raise_on_err:
|
||||
raise p_exc.LsnNotFound(entity='network',
|
||||
entity_id=network_id)
|
||||
|
||||
def lsn_create(self, context, network_id):
|
||||
"""Create a LSN associated to the network."""
|
||||
try:
|
||||
return lsn_api.lsn_for_network_create(self.cluster, network_id)
|
||||
except nvplib.NvpApiClient.NvpApiException:
|
||||
err_msg = _('Unable to create LSN for network %s') % network_id
|
||||
raise p_exc.NvpPluginException(err_msg=err_msg)
|
||||
|
||||
def lsn_delete(self, context, lsn_id):
|
||||
"""Delete a LSN given its id."""
|
||||
try:
|
||||
lsn_api.lsn_delete(self.cluster, lsn_id)
|
||||
except (n_exc.NotFound, nvplib.NvpApiClient.NvpApiException):
|
||||
LOG.warn(_('Unable to delete Logical Service Node %s'), lsn_id)
|
||||
|
||||
def lsn_delete_by_network(self, context, network_id):
|
||||
"""Delete a LSN associated to the network."""
|
||||
lsn_id = self.lsn_get(context, network_id, raise_on_err=False)
|
||||
if lsn_id:
|
||||
self.lsn_delete(context, lsn_id)
|
||||
|
||||
def lsn_port_get(self, context, network_id, subnet_id, raise_on_err=True):
|
||||
"""Retrieve LSN and LSN port for the network and the subnet."""
|
||||
lsn_id = self.lsn_get(context, network_id, raise_on_err=raise_on_err)
|
||||
if lsn_id:
|
||||
try:
|
||||
lsn_port_id = lsn_api.lsn_port_by_subnet_get(
|
||||
self.cluster, lsn_id, subnet_id)
|
||||
except (n_exc.NotFound, nvplib.NvpApiClient.NvpApiException):
|
||||
logger = raise_on_err and LOG.error or LOG.warn
|
||||
logger(_('Unable to find Logical Service Node Port for '
|
||||
'LSN %(lsn_id)s and subnet %(subnet_id)s')
|
||||
% {'lsn_id': lsn_id, 'subnet_id': subnet_id})
|
||||
if raise_on_err:
|
||||
raise p_exc.LsnPortNotFound(lsn_id=lsn_id,
|
||||
entity='subnet',
|
||||
entity_id=subnet_id)
|
||||
return (lsn_id, None)
|
||||
else:
|
||||
return (lsn_id, lsn_port_id)
|
||||
else:
|
||||
return (None, None)
|
||||
|
||||
def lsn_port_get_by_mac(self, context, network_id, mac, raise_on_err=True):
|
||||
"""Retrieve LSN and LSN port given network and mac address."""
|
||||
lsn_id = self.lsn_get(context, network_id, raise_on_err=raise_on_err)
|
||||
if lsn_id:
|
||||
try:
|
||||
lsn_port_id = lsn_api.lsn_port_by_mac_get(
|
||||
self.cluster, lsn_id, mac)
|
||||
except (n_exc.NotFound, nvplib.NvpApiClient.NvpApiException):
|
||||
logger = raise_on_err and LOG.error or LOG.warn
|
||||
logger(_('Unable to find Logical Service Node Port for '
|
||||
'LSN %(lsn_id)s and mac address %(mac)s')
|
||||
% {'lsn_id': lsn_id, 'mac': mac})
|
||||
if raise_on_err:
|
||||
raise p_exc.LsnPortNotFound(lsn_id=lsn_id,
|
||||
entity='MAC',
|
||||
entity_id=mac)
|
||||
return (lsn_id, None)
|
||||
else:
|
||||
return (lsn_id, lsn_port_id)
|
||||
else:
|
||||
return (None, None)
|
||||
|
||||
def lsn_port_create(self, context, lsn_id, subnet_info):
|
||||
"""Create and return LSN port for associated subnet."""
|
||||
try:
|
||||
return lsn_api.lsn_port_create(self.cluster, lsn_id, subnet_info)
|
||||
except n_exc.NotFound:
|
||||
raise p_exc.LsnNotFound(entity='', entity_id=lsn_id)
|
||||
except nvplib.NvpApiClient.NvpApiException:
|
||||
err_msg = _('Unable to create port for LSN %s') % lsn_id
|
||||
raise p_exc.NvpPluginException(err_msg=err_msg)
|
||||
|
||||
def lsn_port_delete(self, context, lsn_id, lsn_port_id):
|
||||
"""Delete a LSN port from the Logical Service Node."""
|
||||
try:
|
||||
lsn_api.lsn_port_delete(self.cluster, lsn_id, lsn_port_id)
|
||||
except (n_exc.NotFound, nvplib.NvpApiClient.NvpApiException):
|
||||
LOG.warn(_('Unable to delete LSN Port %s'), lsn_port_id)
|
||||
|
||||
def lsn_port_dispose(self, context, network_id, mac_address):
|
||||
"""Delete a LSN port given the network and the mac address."""
|
||||
# NOTE(armando-migliaccio): dispose and delete are functionally
|
||||
# equivalent, but they use different paraments to identify LSN
|
||||
# and LSN port resources.
|
||||
lsn_id, lsn_port_id = self.lsn_port_get_by_mac(
|
||||
context, network_id, mac_address, raise_on_err=False)
|
||||
if lsn_port_id:
|
||||
self.lsn_port_delete(context, lsn_id, lsn_port_id)
|
||||
|
||||
def lsn_port_dhcp_setup(
|
||||
self, context, network_id, port_id, port_data, subnet_config=None):
|
||||
"""Connect network to LSN via specified port and port_data."""
|
||||
try:
|
||||
lsn_id = None
|
||||
lswitch_port_id = nvplib.get_port_by_neutron_tag(
|
||||
self.cluster, network_id, port_id)['uuid']
|
||||
lsn_id = self.lsn_get(context, network_id)
|
||||
lsn_port_id = self.lsn_port_create(context, lsn_id, port_data)
|
||||
except (n_exc.NotFound, p_exc.NvpPluginException):
|
||||
raise p_exc.PortConfigurationError(
|
||||
net_id=network_id, lsn_id=lsn_id, port_id=port_id)
|
||||
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)
|
||||
raise p_exc.PortConfigurationError(
|
||||
net_id=network_id, lsn_id=lsn_id, port_id=port_id)
|
||||
if subnet_config:
|
||||
self.lsn_port_dhcp_configure(
|
||||
context, lsn_id, lsn_port_id, subnet_config)
|
||||
else:
|
||||
return (lsn_id, lsn_port_id)
|
||||
|
||||
def lsn_port_dhcp_configure(self, context, lsn_id, lsn_port_id, subnet):
|
||||
"""Enable/disable dhcp services with the given config options."""
|
||||
is_enabled = subnet["enable_dhcp"]
|
||||
dhcp_options = {
|
||||
"domain_name": cfg.CONF.NVP_DHCP.domain_name,
|
||||
"default_lease_time": cfg.CONF.NVP_DHCP.default_lease_time,
|
||||
}
|
||||
dns_servers = cfg.CONF.NVP_DHCP.extra_domain_name_servers
|
||||
dns_servers.extend(subnet["dns_nameservers"])
|
||||
if subnet['gateway_ip']:
|
||||
dhcp_options["routers"] = subnet["gateway_ip"]
|
||||
if dns_servers:
|
||||
dhcp_options["domain_name_servers"] = ",".join(dns_servers)
|
||||
if subnet["host_routes"]:
|
||||
dhcp_options["classless_static_routes"] = (
|
||||
",".join(subnet["host_routes"])
|
||||
)
|
||||
try:
|
||||
lsn_api.lsn_port_dhcp_configure(
|
||||
self.cluster, lsn_id, lsn_port_id, is_enabled, dhcp_options)
|
||||
except (n_exc.NotFound, nvplib.NvpApiClient.NvpApiException):
|
||||
err_msg = (_('Unable to configure dhcp for Logical Service '
|
||||
'Node %(lsn_id)s and port %(lsn_port_id)s')
|
||||
% {'lsn_id': lsn_id, 'lsn_port_id': lsn_port_id})
|
||||
LOG.error(err_msg)
|
||||
raise p_exc.NvpPluginException(err_msg=err_msg)
|
||||
|
||||
def _lsn_port_host_conf(self, context, network_id, subnet_id, data, hdlr):
|
||||
lsn_id = None
|
||||
lsn_port_id = None
|
||||
try:
|
||||
lsn_id, lsn_port_id = self.lsn_port_get(
|
||||
context, network_id, subnet_id)
|
||||
hdlr(self.cluster, lsn_id, lsn_port_id, data)
|
||||
except (n_exc.NotFound, nvplib.NvpApiClient.NvpApiException):
|
||||
LOG.error(_('Error while configuring LSN '
|
||||
'port %s'), lsn_port_id)
|
||||
raise p_exc.PortConfigurationError(
|
||||
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):
|
||||
"""Add dhcp host entry from LSN port configuration."""
|
||||
self._lsn_port_host_conf(context, network_id, subnet_id, host,
|
||||
lsn_api.lsn_port_dhcp_host_add)
|
||||
|
||||
def lsn_port_dhcp_host_remove(self, context, network_id, subnet_id, host):
|
||||
"""Remove dhcp host entry from LSN port configuration."""
|
||||
self._lsn_port_host_conf(context, network_id, subnet_id, host,
|
||||
lsn_api.lsn_port_dhcp_host_remove)
|
||||
|
||||
|
||||
class DhcpAgentNotifyAPI(object):
|
||||
|
||||
def __init__(self, plugin, lsn_manager):
|
||||
self.plugin = plugin
|
||||
self.lsn_manager = lsn_manager
|
||||
self._handle_subnet_dhcp_access = {'create': self._subnet_create,
|
||||
'update': self._subnet_update,
|
||||
'delete': self._subnet_delete}
|
||||
|
||||
def notify(self, context, data, methodname):
|
||||
[resource, action, _e] = methodname.split('.')
|
||||
if resource == 'subnet':
|
||||
self._handle_subnet_dhcp_access[action](context, data['subnet'])
|
||||
|
||||
def _subnet_create(self, context, subnet, clean_on_err=True):
|
||||
if subnet['enable_dhcp']:
|
||||
network_id = subnet['network_id']
|
||||
# Create port for DHCP service
|
||||
dhcp_port = {
|
||||
"name": "",
|
||||
"admin_state_up": True,
|
||||
"device_id": "",
|
||||
"device_owner": const.DEVICE_OWNER_DHCP,
|
||||
"network_id": network_id,
|
||||
"tenant_id": subnet["tenant_id"],
|
||||
"mac_address": attr.ATTR_NOT_SPECIFIED,
|
||||
"fixed_ips": [{"subnet_id": subnet['id']}]
|
||||
}
|
||||
try:
|
||||
# This will end up calling handle_port_dhcp_access
|
||||
# down below
|
||||
self.plugin.create_port(context, {'port': dhcp_port})
|
||||
except p_exc.PortConfigurationError as e:
|
||||
err_msg = (_("Error while creating subnet %(cidr)s for "
|
||||
"network %(network)s. Please, contact "
|
||||
"administrator") %
|
||||
{"cidr": subnet["cidr"],
|
||||
"network": network_id})
|
||||
LOG.error(err_msg)
|
||||
db_base_plugin_v2.NeutronDbPluginV2.delete_port(
|
||||
self.plugin, context, e.port_id)
|
||||
if clean_on_err:
|
||||
self.plugin.delete_subnet(context, subnet['id'])
|
||||
raise n_exc.Conflict()
|
||||
|
||||
def _subnet_update(self, context, subnet):
|
||||
network_id = subnet['network_id']
|
||||
try:
|
||||
lsn_id, lsn_port_id = self.lsn_manager.lsn_port_get(
|
||||
context, network_id, subnet['id'])
|
||||
self.lsn_manager.lsn_port_dhcp_configure(
|
||||
context, lsn_id, lsn_port_id, subnet)
|
||||
except p_exc.LsnPortNotFound:
|
||||
# It's possible that the subnet was created with dhcp off;
|
||||
# check that a dhcp port exists first and provision it
|
||||
# accordingly
|
||||
filters = dict(network_id=[network_id],
|
||||
device_owner=[const.DEVICE_OWNER_DHCP])
|
||||
ports = self.plugin.get_ports(context, filters=filters)
|
||||
if ports:
|
||||
handle_port_dhcp_access(
|
||||
self.plugin, context, ports[0], 'create_port')
|
||||
else:
|
||||
self._subnet_create(context, subnet, clean_on_err=False)
|
||||
|
||||
def _subnet_delete(self, context, subnet):
|
||||
# FIXME(armando-migliaccio): it looks like that a subnet filter
|
||||
# is ineffective; so filter by network for now.
|
||||
network_id = subnet['network_id']
|
||||
filters = dict(network_id=[network_id],
|
||||
device_owner=[const.DEVICE_OWNER_DHCP])
|
||||
# FIXME(armando-migliaccio): this may be race-y
|
||||
ports = self.plugin.get_ports(context, filters=filters)
|
||||
if ports:
|
||||
# This will end up calling handle_port_dhcp_access
|
||||
# down below
|
||||
self.plugin.delete_port(context, ports[0]['id'])
|
||||
|
||||
|
||||
def check_services_requirements(cluster):
|
||||
ver = cluster.api_client.get_nvp_version()
|
||||
# It sounds like 4.1 is the first one where DHCP in NSX/NVP
|
||||
# will have the experimental feature
|
||||
if ver.major >= 4 and ver.minor >= 1:
|
||||
cluster_id = cfg.CONF.default_service_cluster_uuid
|
||||
if not lsn_api.service_cluster_exists(cluster, cluster_id):
|
||||
raise p_exc.ServiceClusterUnavailable(cluster_id=cluster_id)
|
||||
else:
|
||||
raise p_exc.NvpInvalidVersion(version=ver)
|
||||
|
||||
|
||||
def handle_network_dhcp_access(plugin, context, network, action):
|
||||
LOG.info(_("Performing DHCP %(action)s for resource: %(resource)s")
|
||||
% {"action": action, "resource": network})
|
||||
if action == 'create_network':
|
||||
network_id = network['id']
|
||||
plugin.lsn_manager.lsn_create(context, network_id)
|
||||
elif action == 'delete_network':
|
||||
# NOTE(armando-migliaccio): on delete_network, network
|
||||
# is just the network id
|
||||
network_id = network
|
||||
plugin.lsn_manager.lsn_delete_by_network(context, network_id)
|
||||
LOG.info(_("Logical Services Node for network "
|
||||
"%s configured successfully"), network_id)
|
||||
|
||||
|
||||
def handle_port_dhcp_access(plugin, context, port, action):
|
||||
LOG.info(_("Performing DHCP %(action)s for resource: %(resource)s")
|
||||
% {"action": action, "resource": port})
|
||||
if port["device_owner"] == const.DEVICE_OWNER_DHCP:
|
||||
network_id = port["network_id"]
|
||||
if action == "create_port":
|
||||
# at this point the port must have a subnet and a fixed ip
|
||||
subnet_id = port["fixed_ips"][0]['subnet_id']
|
||||
subnet = plugin.get_subnet(context, subnet_id)
|
||||
subnet_data = {
|
||||
"mac_address": port["mac_address"],
|
||||
"ip_address": subnet['cidr'],
|
||||
"subnet_id": subnet['id']
|
||||
}
|
||||
try:
|
||||
plugin.lsn_manager.lsn_port_dhcp_setup(
|
||||
context, network_id, port['id'], subnet_data, subnet)
|
||||
except p_exc.PortConfigurationError:
|
||||
err_msg = (_("Error while configuring DHCP for "
|
||||
"port %s"), port['id'])
|
||||
LOG.error(err_msg)
|
||||
raise n_exc.NeutronException()
|
||||
elif action == "delete_port":
|
||||
plugin.lsn_manager.lsn_port_dispose(context, network_id,
|
||||
port['mac_address'])
|
||||
elif port["device_owner"] != const.DEVICE_OWNER_DHCP:
|
||||
if port.get("fixed_ips"):
|
||||
# do something only if there are IP's and dhcp is enabled
|
||||
subnet_id = port["fixed_ips"][0]['subnet_id']
|
||||
if not plugin.get_subnet(context, subnet_id)['enable_dhcp']:
|
||||
LOG.info(_("DHCP is disabled: nothing to do"))
|
||||
return
|
||||
host_data = {
|
||||
"mac_address": port["mac_address"],
|
||||
"ip_address": port["fixed_ips"][0]['ip_address']
|
||||
}
|
||||
network_id = port["network_id"]
|
||||
if action == "create_port":
|
||||
handler = plugin.lsn_manager.lsn_port_dhcp_host_add
|
||||
elif action == "delete_port":
|
||||
handler = plugin.lsn_manager.lsn_port_dhcp_host_remove
|
||||
try:
|
||||
handler(context, network_id, subnet_id, host_data)
|
||||
except p_exc.PortConfigurationError:
|
||||
if action == 'create_port':
|
||||
db_base_plugin_v2.NeutronDbPluginV2.delete_port(
|
||||
plugin, context, port['id'])
|
||||
raise
|
||||
LOG.info(_("DHCP for port %s configured successfully"), port['id'])
|
||||
|
||||
|
||||
def handle_port_metadata_access(context, port, is_delete=False):
|
||||
# TODO(armando-migliaccio)
|
||||
LOG.info('%s port with data %s' % (is_delete, port))
|
||||
|
||||
|
||||
def handle_router_metadata_access(plugin, context, router_id, do_create=True):
|
||||
# TODO(armando-migliaccio)
|
||||
LOG.info('%s router %s' % (do_create, router_id))
|
@ -225,7 +225,7 @@ def _destroy_metadata_access_network(plugin, context, router_id, ports):
|
||||
# must re-add the router interface
|
||||
plugin.add_router_interface(context, router_id,
|
||||
{'subnet_id': meta_sub_id})
|
||||
# Tell to stop the metadata agent proxy
|
||||
# Tell to stop the metadata agent proxy
|
||||
_notify_rpc_agent(
|
||||
context, {'network': {'id': meta_net_id}}, 'network.delete.end')
|
||||
|
||||
|
@ -22,10 +22,15 @@ from neutron.api.rpc.agentnotifiers import dhcp_rpc_agent_api
|
||||
from neutron.common import constants as const
|
||||
from neutron.common import topics
|
||||
from neutron.openstack.common import importutils
|
||||
from neutron.openstack.common import log as logging
|
||||
from neutron.openstack.common import rpc
|
||||
from neutron.plugins.nicira.common import config
|
||||
from neutron.plugins.nicira.common import exceptions as nvp_exc
|
||||
from neutron.plugins.nicira.dhcp_meta import nvp as nvp_svc
|
||||
from neutron.plugins.nicira.dhcp_meta import rpc as nvp_rpc
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DhcpMetadataAccess(object):
|
||||
|
||||
@ -33,30 +38,22 @@ class DhcpMetadataAccess(object):
|
||||
"""Initialize support for DHCP and Metadata services."""
|
||||
if cfg.CONF.NVP.agent_mode == config.AgentModes.AGENT:
|
||||
self._setup_rpc_dhcp_metadata()
|
||||
self.handle_network_dhcp_access_delegate = (
|
||||
nvp_rpc.handle_network_dhcp_access
|
||||
)
|
||||
self.handle_port_dhcp_access_delegate = (
|
||||
nvp_rpc.handle_port_dhcp_access
|
||||
)
|
||||
self.handle_port_metadata_access_delegate = (
|
||||
nvp_rpc.handle_port_metadata_access
|
||||
)
|
||||
self.handle_metadata_access_delegate = (
|
||||
nvp_rpc.handle_router_metadata_access
|
||||
)
|
||||
mod = nvp_rpc
|
||||
elif cfg.CONF.NVP.agent_mode == config.AgentModes.AGENTLESS:
|
||||
# In agentless mode the following extensions, and related
|
||||
# operations, are not supported; so do not publish them
|
||||
if "agent" in self.supported_extension_aliases:
|
||||
self.supported_extension_aliases.remove("agent")
|
||||
if "dhcp_agent_scheduler" in self.supported_extension_aliases:
|
||||
self.supported_extension_aliases.remove(
|
||||
"dhcp_agent_scheduler")
|
||||
# TODO(armando-migliaccio): agentless support is not yet complete
|
||||
# so it's better to raise an exception for now, in case some admin
|
||||
# decides to jump the gun
|
||||
raise NotImplementedError()
|
||||
self._setup_nvp_dhcp_metadata()
|
||||
mod = nvp_svc
|
||||
self.handle_network_dhcp_access_delegate = (
|
||||
mod.handle_network_dhcp_access
|
||||
)
|
||||
self.handle_port_dhcp_access_delegate = (
|
||||
mod.handle_port_dhcp_access
|
||||
)
|
||||
self.handle_port_metadata_access_delegate = (
|
||||
mod.handle_port_metadata_access
|
||||
)
|
||||
self.handle_metadata_access_delegate = (
|
||||
mod.handle_router_metadata_access
|
||||
)
|
||||
|
||||
def _setup_rpc_dhcp_metadata(self):
|
||||
self.topic = topics.PLUGIN
|
||||
@ -71,6 +68,36 @@ class DhcpMetadataAccess(object):
|
||||
cfg.CONF.network_scheduler_driver
|
||||
)
|
||||
|
||||
def _setup_nvp_dhcp_metadata(self):
|
||||
# In agentless mode the following extensions, and related
|
||||
# operations, are not supported; so do not publish them
|
||||
if "agent" in self.supported_extension_aliases:
|
||||
self.supported_extension_aliases.remove("agent")
|
||||
if "dhcp_agent_scheduler" in self.supported_extension_aliases:
|
||||
self.supported_extension_aliases.remove(
|
||||
"dhcp_agent_scheduler")
|
||||
nvp_svc.register_dhcp_opts(cfg)
|
||||
self.lsn_manager = nvp_svc.LsnManager(self)
|
||||
self.agent_notifiers[const.AGENT_TYPE_DHCP] = (
|
||||
nvp_svc.DhcpAgentNotifyAPI(self, self.lsn_manager))
|
||||
# In agentless mode, ports whose owner is DHCP need to
|
||||
# be special cased; so add it to the list of special
|
||||
# owners list
|
||||
if const.DEVICE_OWNER_DHCP not in self.port_special_owners:
|
||||
self.port_special_owners.append(const.DEVICE_OWNER_DHCP)
|
||||
try:
|
||||
error = None
|
||||
nvp_svc.check_services_requirements(self.cluster)
|
||||
except nvp_exc.NvpInvalidVersion:
|
||||
error = _("Unable to run Neutron with config option '%s', as NVP "
|
||||
"does not support it") % config.AgentModes.AGENTLESS
|
||||
except nvp_exc.ServiceClusterUnavailable:
|
||||
error = _("Unmet dependency for config option "
|
||||
"'%s'") % config.AgentModes.AGENTLESS
|
||||
if error:
|
||||
LOG.exception(error)
|
||||
raise nvp_exc.NvpPluginException(err_msg=error)
|
||||
|
||||
def handle_network_dhcp_access(self, context, network, action):
|
||||
self.handle_network_dhcp_access_delegate(self, context,
|
||||
network, action)
|
||||
|
16
neutron/plugins/nicira/nsxlib/__init__.py
Normal file
16
neutron/plugins/nicira/nsxlib/__init__.py
Normal file
@ -0,0 +1,16 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 VMware, Inc.
|
||||
# All Rights Reserved
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
201
neutron/plugins/nicira/nsxlib/lsn.py
Normal file
201
neutron/plugins/nicira/nsxlib/lsn.py
Normal file
@ -0,0 +1,201 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 VMware, Inc.
|
||||
# All Rights Reserved
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import json
|
||||
|
||||
from neutron.common import exceptions as exception
|
||||
from neutron.openstack.common import log
|
||||
from neutron.plugins.nicira.common import exceptions as nvp_exc
|
||||
from neutron.plugins.nicira.common import utils
|
||||
from neutron.plugins.nicira import NvpApiClient
|
||||
from neutron.plugins.nicira.nvplib import _build_uri_path
|
||||
from neutron.plugins.nicira.nvplib import do_request
|
||||
|
||||
HTTP_GET = "GET"
|
||||
HTTP_POST = "POST"
|
||||
HTTP_DELETE = "DELETE"
|
||||
HTTP_PUT = "PUT"
|
||||
|
||||
SERVICECLUSTER_RESOURCE = "service-cluster"
|
||||
LSERVICESNODE_RESOURCE = "lservices-node"
|
||||
LSERVICESNODEPORT_RESOURCE = "lport/%s" % LSERVICESNODE_RESOURCE
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
def service_cluster_exists(cluster, svc_cluster_id):
|
||||
exists = False
|
||||
try:
|
||||
exists = (
|
||||
svc_cluster_id and
|
||||
do_request(HTTP_GET,
|
||||
_build_uri_path(SERVICECLUSTER_RESOURCE,
|
||||
resource_id=svc_cluster_id),
|
||||
cluster=cluster) is not None)
|
||||
except exception.NotFound:
|
||||
pass
|
||||
return exists
|
||||
|
||||
|
||||
def lsn_for_network_create(cluster, network_id):
|
||||
lsn_obj = {
|
||||
"service_cluster_uuid": cluster.default_service_cluster_uuid,
|
||||
"tags": utils.get_tags(n_network_id=network_id)
|
||||
}
|
||||
return do_request(HTTP_POST,
|
||||
_build_uri_path(LSERVICESNODE_RESOURCE),
|
||||
json.dumps(lsn_obj),
|
||||
cluster=cluster)["uuid"]
|
||||
|
||||
|
||||
def lsn_for_network_get(cluster, network_id):
|
||||
filters = {"tag": network_id, "tag_scope": "n_network_id"}
|
||||
results = do_request(HTTP_GET,
|
||||
_build_uri_path(LSERVICESNODE_RESOURCE,
|
||||
fields="uuid",
|
||||
filters=filters),
|
||||
cluster=cluster)['results']
|
||||
if not results:
|
||||
raise exception.NotFound()
|
||||
elif len(results) == 1:
|
||||
return results[0]['uuid']
|
||||
|
||||
|
||||
def lsn_delete(cluster, lsn_id):
|
||||
do_request(HTTP_DELETE,
|
||||
_build_uri_path(LSERVICESNODE_RESOURCE,
|
||||
resource_id=lsn_id),
|
||||
cluster=cluster)
|
||||
|
||||
|
||||
def lsn_port_create(cluster, lsn_id, port_data):
|
||||
port_obj = {
|
||||
"ip_address": port_data["ip_address"],
|
||||
"mac_address": port_data["mac_address"],
|
||||
"tags": utils.get_tags(n_mac_address=port_data["mac_address"],
|
||||
n_subnet_id=port_data["subnet_id"]),
|
||||
"type": "LogicalServicesNodePortConfig",
|
||||
}
|
||||
return do_request(HTTP_POST,
|
||||
_build_uri_path(LSERVICESNODEPORT_RESOURCE,
|
||||
parent_resource_id=lsn_id),
|
||||
json.dumps(port_obj),
|
||||
cluster=cluster)["uuid"]
|
||||
|
||||
|
||||
def lsn_port_delete(cluster, lsn_id, lsn_port_id):
|
||||
return do_request(HTTP_DELETE,
|
||||
_build_uri_path(LSERVICESNODEPORT_RESOURCE,
|
||||
parent_resource_id=lsn_id,
|
||||
resource_id=lsn_port_id),
|
||||
cluster=cluster)
|
||||
|
||||
|
||||
def _lsn_port_get(cluster, lsn_id, filters):
|
||||
results = do_request(HTTP_GET,
|
||||
_build_uri_path(LSERVICESNODEPORT_RESOURCE,
|
||||
parent_resource_id=lsn_id,
|
||||
fields="uuid",
|
||||
filters=filters),
|
||||
cluster=cluster)['results']
|
||||
if not results:
|
||||
raise exception.NotFound()
|
||||
elif len(results) == 1:
|
||||
return results[0]['uuid']
|
||||
|
||||
|
||||
def lsn_port_by_mac_get(cluster, lsn_id, mac_address):
|
||||
filters = {"tag": mac_address, "tag_scope": "n_mac_address"}
|
||||
return _lsn_port_get(cluster, lsn_id, filters)
|
||||
|
||||
|
||||
def lsn_port_by_subnet_get(cluster, lsn_id, subnet_id):
|
||||
filters = {"tag": subnet_id, "tag_scope": "n_subnet_id"}
|
||||
return _lsn_port_get(cluster, lsn_id, filters)
|
||||
|
||||
|
||||
def lsn_port_plug_network(cluster, lsn_id, lsn_port_id, lswitch_port_id):
|
||||
patch_obj = {
|
||||
"type": "PatchAttachment",
|
||||
"peer_port_uuid": lswitch_port_id
|
||||
}
|
||||
try:
|
||||
do_request(HTTP_PUT,
|
||||
_build_uri_path(LSERVICESNODEPORT_RESOURCE,
|
||||
parent_resource_id=lsn_id,
|
||||
resource_id=lsn_port_id,
|
||||
is_attachment=True),
|
||||
json.dumps(patch_obj),
|
||||
cluster=cluster)
|
||||
except NvpApiClient.Conflict:
|
||||
# This restriction might be lifted at some point
|
||||
msg = (_("Attempt to plug Logical Services Node %(lsn)s into "
|
||||
"network with port %(port)s failed. PatchAttachment "
|
||||
"already exists with another port") %
|
||||
{'lsn': lsn_id, 'port': lswitch_port_id})
|
||||
LOG.exception(msg)
|
||||
raise nvp_exc.LsnConfigurationConflict(lsn_id=lsn_id)
|
||||
|
||||
|
||||
def _lsn_port_configure_action(
|
||||
cluster, lsn_id, lsn_port_id, action, is_enabled, obj):
|
||||
do_request(HTTP_PUT,
|
||||
_build_uri_path(LSERVICESNODE_RESOURCE,
|
||||
resource_id=lsn_id,
|
||||
extra_action=action),
|
||||
json.dumps({"enabled": is_enabled}),
|
||||
cluster=cluster)
|
||||
do_request(HTTP_PUT,
|
||||
_build_uri_path(LSERVICESNODEPORT_RESOURCE,
|
||||
parent_resource_id=lsn_id,
|
||||
resource_id=lsn_port_id,
|
||||
extra_action=action),
|
||||
json.dumps(obj),
|
||||
cluster=cluster)
|
||||
|
||||
|
||||
def lsn_port_dhcp_configure(
|
||||
cluster, lsn_id, lsn_port_id, is_enabled=True, dhcp_options=None):
|
||||
dhcp_options = dhcp_options or {}
|
||||
opts = ["%s=%s" % (key, val) for key, val in dhcp_options.iteritems()]
|
||||
dhcp_obj = {
|
||||
'options': {'options': opts}
|
||||
}
|
||||
_lsn_port_configure_action(
|
||||
cluster, lsn_id, lsn_port_id, 'dhcp', is_enabled, dhcp_obj)
|
||||
|
||||
|
||||
def _lsn_port_host_action(
|
||||
cluster, lsn_id, lsn_port_id, host_obj, extra_action, action):
|
||||
do_request(HTTP_POST,
|
||||
_build_uri_path(LSERVICESNODEPORT_RESOURCE,
|
||||
parent_resource_id=lsn_id,
|
||||
resource_id=lsn_port_id,
|
||||
extra_action=extra_action,
|
||||
filters={"action": action}),
|
||||
json.dumps(host_obj),
|
||||
cluster=cluster)
|
||||
|
||||
|
||||
def lsn_port_dhcp_host_add(cluster, lsn_id, lsn_port_id, host_data):
|
||||
_lsn_port_host_action(
|
||||
cluster, lsn_id, lsn_port_id, host_data, 'dhcp', 'add_host')
|
||||
|
||||
|
||||
def lsn_port_dhcp_host_remove(cluster, lsn_id, lsn_port_id, host_data):
|
||||
_lsn_port_host_action(
|
||||
cluster, lsn_id, lsn_port_id, host_data, 'dhcp', 'remove_host')
|
@ -122,7 +122,8 @@ def _build_uri_path(resource,
|
||||
relations=None,
|
||||
filters=None,
|
||||
types=None,
|
||||
is_attachment=False):
|
||||
is_attachment=False,
|
||||
extra_action=None):
|
||||
resources = resource.split('/')
|
||||
res_path = resources[0] + (resource_id and "/%s" % resource_id or '')
|
||||
if len(resources) > 1:
|
||||
@ -132,6 +133,8 @@ def _build_uri_path(resource,
|
||||
res_path)
|
||||
if is_attachment:
|
||||
res_path = "%s/attachment" % res_path
|
||||
elif extra_action:
|
||||
res_path = "%s/%s" % (res_path, extra_action)
|
||||
params = []
|
||||
params.append(fields and "fields=%s" % fields)
|
||||
params.append(relations and "relations=%s" % relations)
|
||||
|
@ -6,6 +6,7 @@ nvp_user = foo
|
||||
nvp_password = bar
|
||||
default_l3_gw_service_uuid = whatever
|
||||
default_l2_gw_service_uuid = whatever
|
||||
default_service_cluster_uuid = whatever
|
||||
default_interface_name = whatever
|
||||
req_timeout = 14
|
||||
http_timeout = 13
|
||||
|
633
neutron/tests/unit/nicira/test_dhcpmeta.py
Normal file
633
neutron/tests/unit/nicira/test_dhcpmeta.py
Normal file
@ -0,0 +1,633 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 VMware, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import mock
|
||||
|
||||
from oslo.config import cfg
|
||||
|
||||
from neutron.common import exceptions as n_exc
|
||||
from neutron.plugins.nicira.common import exceptions as p_exc
|
||||
from neutron.plugins.nicira.dhcp_meta import nvp
|
||||
from neutron.plugins.nicira.NvpApiClient import NvpApiException
|
||||
from neutron.tests import base
|
||||
|
||||
|
||||
class LsnManagerTestCase(base.BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(LsnManagerTestCase, self).setUp()
|
||||
self.net_id = 'foo_network_id'
|
||||
self.sub_id = 'foo_subnet_id'
|
||||
self.port_id = 'foo_port_id'
|
||||
self.lsn_id = 'foo_lsn_id'
|
||||
self.mac = 'aa:bb:cc:dd:ee:ff'
|
||||
self.lsn_port_id = 'foo_lsn_port_id'
|
||||
self.manager = nvp.LsnManager(mock.Mock())
|
||||
self.mock_lsn_api_p = mock.patch.object(nvp, 'lsn_api')
|
||||
self.mock_lsn_api = self.mock_lsn_api_p.start()
|
||||
nvp.register_dhcp_opts(cfg)
|
||||
self.addCleanup(cfg.CONF.reset)
|
||||
self.addCleanup(self.mock_lsn_api_p.stop)
|
||||
|
||||
def test_lsn_get(self):
|
||||
self.mock_lsn_api.lsn_for_network_get.return_value = self.lsn_id
|
||||
expected = self.manager.lsn_get(mock.ANY, self.net_id)
|
||||
self.mock_lsn_api.lsn_for_network_get.assert_called_once_with(
|
||||
mock.ANY, self.net_id)
|
||||
self.assertEqual(expected, self.lsn_id)
|
||||
|
||||
def _test_lsn_get_raise_not_found_with_exc(self, exc):
|
||||
self.mock_lsn_api.lsn_for_network_get.side_effect = exc
|
||||
self.assertRaises(p_exc.LsnNotFound,
|
||||
self.manager.lsn_get,
|
||||
mock.ANY, self.net_id)
|
||||
self.mock_lsn_api.lsn_for_network_get.assert_called_once_with(
|
||||
mock.ANY, self.net_id)
|
||||
|
||||
def test_lsn_get_raise_not_found_with_not_found(self):
|
||||
self._test_lsn_get_raise_not_found_with_exc(n_exc.NotFound)
|
||||
|
||||
def test_lsn_get_raise_not_found_with_api_error(self):
|
||||
self._test_lsn_get_raise_not_found_with_exc(NvpApiException)
|
||||
|
||||
def _test_lsn_get_silent_raise_with_exc(self, exc):
|
||||
self.mock_lsn_api.lsn_for_network_get.side_effect = exc
|
||||
expected = self.manager.lsn_get(
|
||||
mock.ANY, self.net_id, raise_on_err=False)
|
||||
self.mock_lsn_api.lsn_for_network_get.assert_called_once_with(
|
||||
mock.ANY, self.net_id)
|
||||
self.assertIsNone(expected)
|
||||
|
||||
def test_lsn_get_silent_raise_with_not_found(self):
|
||||
self._test_lsn_get_silent_raise_with_exc(n_exc.NotFound)
|
||||
|
||||
def test_lsn_get_silent_raise_with_api_error(self):
|
||||
self._test_lsn_get_silent_raise_with_exc(NvpApiException)
|
||||
|
||||
def test_lsn_create(self):
|
||||
self.mock_lsn_api.lsn_for_network_create.return_value = self.lsn_id
|
||||
self.manager.lsn_create(mock.ANY, self.net_id)
|
||||
self.mock_lsn_api.lsn_for_network_create.assert_called_once_with(
|
||||
mock.ANY, self.net_id)
|
||||
|
||||
def test_lsn_create_raise_api_error(self):
|
||||
self.mock_lsn_api.lsn_for_network_create.side_effect = NvpApiException
|
||||
self.assertRaises(p_exc.NvpPluginException,
|
||||
self.manager.lsn_create,
|
||||
mock.ANY, self.net_id)
|
||||
self.mock_lsn_api.lsn_for_network_create.assert_called_once_with(
|
||||
mock.ANY, self.net_id)
|
||||
|
||||
def test_lsn_delete(self):
|
||||
self.manager.lsn_delete(mock.ANY, self.lsn_id)
|
||||
self.mock_lsn_api.lsn_delete.assert_called_once_with(
|
||||
mock.ANY, self.lsn_id)
|
||||
|
||||
def _test_lsn_delete_with_exc(self, exc):
|
||||
self.mock_lsn_api.lsn_delete.side_effect = exc
|
||||
self.manager.lsn_delete(mock.ANY, self.lsn_id)
|
||||
self.mock_lsn_api.lsn_delete.assert_called_once_with(
|
||||
mock.ANY, self.lsn_id)
|
||||
|
||||
def test_lsn_delete_with_not_found(self):
|
||||
self._test_lsn_delete_with_exc(n_exc.NotFound)
|
||||
|
||||
def test_lsn_delete_api_exception(self):
|
||||
self._test_lsn_delete_with_exc(NvpApiException)
|
||||
|
||||
def test_lsn_delete_by_network(self):
|
||||
self.mock_lsn_api.lsn_for_network_get.return_value = self.lsn_id
|
||||
with mock.patch.object(self.manager, 'lsn_delete') as f:
|
||||
self.manager.lsn_delete_by_network(mock.ANY, self.net_id)
|
||||
self.mock_lsn_api.lsn_for_network_get.assert_called_once_with(
|
||||
mock.ANY, self.net_id)
|
||||
f.assert_called_once_with(mock.ANY, self.lsn_id)
|
||||
|
||||
def _test_lsn_delete_by_network_with_exc(self, exc):
|
||||
self.mock_lsn_api.lsn_for_network_get.side_effect = exc
|
||||
with mock.patch.object(nvp.LOG, 'warn') as l:
|
||||
self.manager.lsn_delete_by_network(mock.ANY, self.net_id)
|
||||
self.assertEqual(1, l.call_count)
|
||||
|
||||
def test_lsn_delete_by_network_with_not_found(self):
|
||||
self._test_lsn_delete_by_network_with_exc(n_exc.NotFound)
|
||||
|
||||
def test_lsn_delete_by_network_with_not_api_error(self):
|
||||
self._test_lsn_delete_by_network_with_exc(NvpApiException)
|
||||
|
||||
def test_lsn_port_get(self):
|
||||
self.mock_lsn_api.lsn_port_by_subnet_get.return_value = (
|
||||
self.lsn_port_id)
|
||||
with mock.patch.object(
|
||||
self.manager, 'lsn_get', return_value=self.lsn_id):
|
||||
expected = self.manager.lsn_port_get(
|
||||
mock.ANY, self.net_id, self.sub_id)
|
||||
self.assertEqual(expected, (self.lsn_id, self.lsn_port_id))
|
||||
|
||||
def test_lsn_port_get_lsn_not_found_on_raise(self):
|
||||
with mock.patch.object(
|
||||
self.manager, 'lsn_get',
|
||||
side_effect=p_exc.LsnNotFound(entity='network',
|
||||
entity_id=self.net_id)):
|
||||
self.assertRaises(p_exc.LsnNotFound,
|
||||
self.manager.lsn_port_get,
|
||||
mock.ANY, self.net_id, self.sub_id)
|
||||
|
||||
def test_lsn_port_get_lsn_not_found_silent_raise(self):
|
||||
with mock.patch.object(self.manager, 'lsn_get', return_value=None):
|
||||
expected = self.manager.lsn_port_get(
|
||||
mock.ANY, self.net_id, self.sub_id, raise_on_err=False)
|
||||
self.assertEqual(expected, (None, None))
|
||||
|
||||
def test_lsn_port_get_port_not_found_on_raise(self):
|
||||
self.mock_lsn_api.lsn_port_by_subnet_get.side_effect = n_exc.NotFound
|
||||
with mock.patch.object(
|
||||
self.manager, 'lsn_get', return_value=self.lsn_id):
|
||||
self.assertRaises(p_exc.LsnPortNotFound,
|
||||
self.manager.lsn_port_get,
|
||||
mock.ANY, self.net_id, self.sub_id)
|
||||
|
||||
def test_lsn_port_get_port_not_found_silent_raise(self):
|
||||
self.mock_lsn_api.lsn_port_by_subnet_get.side_effect = n_exc.NotFound
|
||||
with mock.patch.object(
|
||||
self.manager, 'lsn_get', return_value=self.lsn_id):
|
||||
expected = self.manager.lsn_port_get(
|
||||
mock.ANY, self.net_id, self.sub_id, raise_on_err=False)
|
||||
self.assertEqual(expected, (self.lsn_id, None))
|
||||
|
||||
def test_lsn_port_create(self):
|
||||
self.mock_lsn_api.lsn_port_create.return_value = self.lsn_port_id
|
||||
expected = self.manager.lsn_port_create(mock.ANY, mock.ANY, mock.ANY)
|
||||
self.assertEqual(expected, self.lsn_port_id)
|
||||
|
||||
def _test_lsn_port_create_with_exc(self, exc, expected):
|
||||
self.mock_lsn_api.lsn_port_create.side_effect = exc
|
||||
self.assertRaises(expected,
|
||||
self.manager.lsn_port_create,
|
||||
mock.ANY, mock.ANY, mock.ANY)
|
||||
|
||||
def test_lsn_port_create_with_not_found(self):
|
||||
self._test_lsn_port_create_with_exc(n_exc.NotFound, p_exc.LsnNotFound)
|
||||
|
||||
def test_lsn_port_create_api_exception(self):
|
||||
self._test_lsn_port_create_with_exc(NvpApiException,
|
||||
p_exc.NvpPluginException)
|
||||
|
||||
def test_lsn_port_delete(self):
|
||||
self.manager.lsn_port_delete(mock.ANY, mock.ANY, mock.ANY)
|
||||
self.assertEqual(1, self.mock_lsn_api.lsn_port_delete.call_count)
|
||||
|
||||
def _test_lsn_port_delete_with_exc(self, exc):
|
||||
self.mock_lsn_api.lsn_port_delete.side_effect = exc
|
||||
with mock.patch.object(nvp.LOG, 'warn') as l:
|
||||
self.manager.lsn_port_delete(mock.ANY, mock.ANY, mock.ANY)
|
||||
self.assertEqual(1, self.mock_lsn_api.lsn_port_delete.call_count)
|
||||
self.assertEqual(1, l.call_count)
|
||||
|
||||
def test_lsn_port_delete_with_not_found(self):
|
||||
self._test_lsn_port_delete_with_exc(n_exc.NotFound)
|
||||
|
||||
def test_lsn_port_delete_api_exception(self):
|
||||
self._test_lsn_port_delete_with_exc(NvpApiException)
|
||||
|
||||
def _test_lsn_port_dhcp_setup(self, ret_val, sub):
|
||||
self.mock_lsn_api.lsn_port_create.return_value = self.lsn_port_id
|
||||
with mock.patch.object(
|
||||
self.manager, 'lsn_get', return_value=self.lsn_id):
|
||||
with mock.patch.object(nvp.nvplib, 'get_port_by_neutron_tag'):
|
||||
expected = self.manager.lsn_port_dhcp_setup(
|
||||
mock.ANY, mock.ANY, mock.ANY, mock.ANY, subnet_config=sub)
|
||||
self.assertEqual(
|
||||
1, self.mock_lsn_api.lsn_port_create.call_count)
|
||||
self.assertEqual(
|
||||
1, self.mock_lsn_api.lsn_port_plug_network.call_count)
|
||||
self.assertEqual(expected, ret_val)
|
||||
|
||||
def test_lsn_port_dhcp_setup(self):
|
||||
self._test_lsn_port_dhcp_setup((self.lsn_id, self.lsn_port_id), None)
|
||||
|
||||
def test_lsn_port_dhcp_setup_with_config(self):
|
||||
with mock.patch.object(self.manager, 'lsn_port_dhcp_configure') as f:
|
||||
self._test_lsn_port_dhcp_setup(None, mock.ANY)
|
||||
self.assertEqual(1, f.call_count)
|
||||
|
||||
def test_lsn_port_dhcp_setup_with_not_found(self):
|
||||
with mock.patch.object(nvp.nvplib, 'get_port_by_neutron_tag') as f:
|
||||
f.side_effect = n_exc.NotFound
|
||||
self.assertRaises(p_exc.PortConfigurationError,
|
||||
self.manager.lsn_port_dhcp_setup,
|
||||
mock.ANY, mock.ANY, mock.ANY, mock.ANY)
|
||||
|
||||
def test_lsn_port_dhcp_setup_with_conflict(self):
|
||||
self.mock_lsn_api.lsn_port_plug_network.side_effect = (
|
||||
p_exc.LsnConfigurationConflict(lsn_id=self.lsn_id))
|
||||
with mock.patch.object(nvp.nvplib, 'get_port_by_neutron_tag'):
|
||||
with mock.patch.object(self.manager, 'lsn_port_delete') as g:
|
||||
self.assertRaises(p_exc.PortConfigurationError,
|
||||
self.manager.lsn_port_dhcp_setup,
|
||||
mock.ANY, mock.ANY, mock.ANY, mock.ANY)
|
||||
self.assertEqual(1, g.call_count)
|
||||
|
||||
def _test_lsn_port_dhcp_configure_with_subnet(
|
||||
self, expected, dns=None, gw=None, routes=None):
|
||||
subnet = {
|
||||
'enable_dhcp': True,
|
||||
'dns_nameservers': dns or [],
|
||||
'gateway_ip': gw,
|
||||
'host_routes': routes
|
||||
}
|
||||
self.manager.lsn_port_dhcp_configure(mock.ANY, self.lsn_id,
|
||||
self.lsn_port_id, subnet)
|
||||
self.mock_lsn_api.lsn_port_dhcp_configure.assert_called_once_with(
|
||||
mock.ANY, self.lsn_id, self.lsn_port_id, subnet['enable_dhcp'],
|
||||
expected)
|
||||
|
||||
def test_lsn_port_dhcp_configure(self):
|
||||
expected = {
|
||||
'routers': '127.0.0.1',
|
||||
'default_lease_time': cfg.CONF.NVP_DHCP.default_lease_time,
|
||||
'domain_name': cfg.CONF.NVP_DHCP.domain_name
|
||||
}
|
||||
self._test_lsn_port_dhcp_configure_with_subnet(
|
||||
expected, dns=[], gw='127.0.0.1', routes=[])
|
||||
|
||||
def test_lsn_port_dhcp_configure_gatewayless(self):
|
||||
expected = {
|
||||
'default_lease_time': cfg.CONF.NVP_DHCP.default_lease_time,
|
||||
'domain_name': cfg.CONF.NVP_DHCP.domain_name
|
||||
}
|
||||
self._test_lsn_port_dhcp_configure_with_subnet(expected, gw=None)
|
||||
|
||||
def test_lsn_port_dhcp_configure_with_extra_dns_servers(self):
|
||||
expected = {
|
||||
'default_lease_time': cfg.CONF.NVP_DHCP.default_lease_time,
|
||||
'domain_name_servers': '8.8.8.8,9.9.9.9',
|
||||
'domain_name': cfg.CONF.NVP_DHCP.domain_name
|
||||
}
|
||||
self._test_lsn_port_dhcp_configure_with_subnet(
|
||||
expected, dns=['8.8.8.8', '9.9.9.9'])
|
||||
|
||||
def test_lsn_port_dhcp_configure_with_host_routes(self):
|
||||
expected = {
|
||||
'default_lease_time': cfg.CONF.NVP_DHCP.default_lease_time,
|
||||
'domain_name': cfg.CONF.NVP_DHCP.domain_name,
|
||||
'classless_static_routes': '8.8.8.8,9.9.9.9'
|
||||
}
|
||||
self._test_lsn_port_dhcp_configure_with_subnet(
|
||||
expected, routes=['8.8.8.8', '9.9.9.9'])
|
||||
|
||||
def _test_lsn_port_dispose_with_values(self, lsn_id, lsn_port_id, count):
|
||||
with mock.patch.object(self.manager,
|
||||
'lsn_port_get_by_mac',
|
||||
return_value=(lsn_id, lsn_port_id)):
|
||||
self.manager.lsn_port_dispose(mock.ANY, self.net_id, self.mac)
|
||||
self.assertEqual(count,
|
||||
self.mock_lsn_api.lsn_port_delete.call_count)
|
||||
|
||||
def test_lsn_port_dispose(self):
|
||||
self._test_lsn_port_dispose_with_values(
|
||||
self.lsn_id, self.lsn_port_id, 1)
|
||||
|
||||
def test_lsn_port_dispose_lsn_not_found(self):
|
||||
self._test_lsn_port_dispose_with_values(None, None, 0)
|
||||
|
||||
def test_lsn_port_dispose_lsn_port_not_found(self):
|
||||
self._test_lsn_port_dispose_with_values(self.lsn_id, None, 0)
|
||||
|
||||
def test_lsn_port_dispose_api_error(self):
|
||||
self.mock_lsn_api.lsn_port_delete.side_effect = NvpApiException
|
||||
with mock.patch.object(nvp.LOG, 'warn') as l:
|
||||
self.manager.lsn_port_dispose(mock.ANY, self.net_id, self.mac)
|
||||
self.assertEqual(1, l.call_count)
|
||||
|
||||
def test_lsn_port_host_conf(self):
|
||||
with mock.patch.object(self.manager,
|
||||
'lsn_port_get',
|
||||
return_value=(self.lsn_id, self.lsn_port_id)):
|
||||
f = mock.Mock()
|
||||
self.manager._lsn_port_host_conf(mock.ANY, self.net_id,
|
||||
self.sub_id, mock.ANY, f)
|
||||
self.assertEqual(1, f.call_count)
|
||||
|
||||
def test_lsn_port_host_conf_lsn_port_not_found(self):
|
||||
with mock.patch.object(
|
||||
self.manager,
|
||||
'lsn_port_get',
|
||||
side_effect=p_exc.LsnPortNotFound(lsn_id=self.lsn_id,
|
||||
entity='subnet',
|
||||
entity_id=self.sub_id)):
|
||||
self.assertRaises(p_exc.PortConfigurationError,
|
||||
self.manager._lsn_port_host_conf, mock.ANY,
|
||||
self.net_id, self.sub_id, mock.ANY, mock.Mock())
|
||||
|
||||
|
||||
class DhcpAgentNotifyAPITestCase(base.BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(DhcpAgentNotifyAPITestCase, self).setUp()
|
||||
self.notifier = nvp.DhcpAgentNotifyAPI(mock.Mock(), mock.Mock())
|
||||
self.plugin = self.notifier.plugin
|
||||
self.lsn_manager = self.notifier.lsn_manager
|
||||
|
||||
def _test_notify_subnet_action(self, action):
|
||||
with mock.patch.object(self.notifier, '_subnet_%s' % action) as f:
|
||||
self.notifier._handle_subnet_dhcp_access[action] = f
|
||||
subnet = {'subnet': mock.ANY}
|
||||
self.notifier.notify(
|
||||
mock.ANY, subnet, 'subnet.%s.end' % action)
|
||||
f.assert_called_once_with(mock.ANY, subnet)
|
||||
|
||||
def test_notify_subnet_create(self):
|
||||
self._test_notify_subnet_action('create')
|
||||
|
||||
def test_notify_subnet_update(self):
|
||||
self._test_notify_subnet_action('update')
|
||||
|
||||
def test_notify_subnet_delete(self):
|
||||
self._test_notify_subnet_action('delete')
|
||||
|
||||
def _test_subnet_create(self, enable_dhcp, exc=None,
|
||||
exc_obj=None, call_notify=True):
|
||||
subnet = {
|
||||
'id': 'foo_subnet_id',
|
||||
'enable_dhcp': enable_dhcp,
|
||||
'network_id': 'foo_network_id',
|
||||
'tenant_id': 'foo_tenant_id',
|
||||
'cidr': '0.0.0.0/0'
|
||||
}
|
||||
if exc:
|
||||
self.plugin.create_port.side_effect = exc_obj or exc
|
||||
self.assertRaises(exc,
|
||||
self.notifier.notify,
|
||||
mock.ANY,
|
||||
{'subnet': subnet},
|
||||
'subnet.create.end')
|
||||
self.plugin.delete_subnet.assert_called_with(
|
||||
mock.ANY, subnet['id'])
|
||||
else:
|
||||
if call_notify:
|
||||
self.notifier.notify(
|
||||
mock.ANY, {'subnet': subnet}, 'subnet.create.end')
|
||||
if enable_dhcp:
|
||||
dhcp_port = {
|
||||
'name': '',
|
||||
'admin_state_up': True,
|
||||
'network_id': 'foo_network_id',
|
||||
'tenant_id': 'foo_tenant_id',
|
||||
'device_owner': 'network:dhcp',
|
||||
'mac_address': mock.ANY,
|
||||
'fixed_ips': [{'subnet_id': 'foo_subnet_id'}],
|
||||
'device_id': ''
|
||||
}
|
||||
self.plugin.create_port.assert_called_once_with(
|
||||
mock.ANY, {'port': dhcp_port})
|
||||
else:
|
||||
self.assertEqual(0, self.plugin.create_port.call_count)
|
||||
|
||||
def test_subnet_create_enabled_dhcp(self):
|
||||
self._test_subnet_create(True)
|
||||
|
||||
def test_subnet_create_disabled_dhcp(self):
|
||||
self._test_subnet_create(False)
|
||||
|
||||
def test_subnet_create_raise_port_config_error(self):
|
||||
with mock.patch.object(nvp.db_base_plugin_v2.NeutronDbPluginV2,
|
||||
'delete_port') as d:
|
||||
self._test_subnet_create(
|
||||
True,
|
||||
exc=n_exc.Conflict,
|
||||
exc_obj=p_exc.PortConfigurationError(lsn_id='foo_lsn_id',
|
||||
net_id='foo_net_id',
|
||||
port_id='foo_port_id'))
|
||||
d.assert_called_once_with(self.plugin, mock.ANY, 'foo_port_id')
|
||||
|
||||
def test_subnet_update(self):
|
||||
subnet = {
|
||||
'id': 'foo_subnet_id',
|
||||
'network_id': 'foo_network_id',
|
||||
}
|
||||
self.lsn_manager.lsn_port_get.return_value = ('foo_lsn_id',
|
||||
'foo_lsn_port_id')
|
||||
self.notifier.notify(
|
||||
mock.ANY, {'subnet': subnet}, 'subnet.update.end')
|
||||
self.lsn_manager.lsn_port_dhcp_configure.assert_called_once_with(
|
||||
mock.ANY, 'foo_lsn_id', 'foo_lsn_port_id', subnet)
|
||||
|
||||
def test_subnet_update_raise_lsn_not_found(self):
|
||||
subnet = {
|
||||
'id': 'foo_subnet_id',
|
||||
'network_id': 'foo_network_id',
|
||||
}
|
||||
self.lsn_manager.lsn_port_get.side_effect = (
|
||||
p_exc.LsnNotFound(entity='network',
|
||||
entity_id=subnet['network_id']))
|
||||
self.assertRaises(p_exc.LsnNotFound,
|
||||
self.notifier.notify,
|
||||
mock.ANY, {'subnet': subnet}, 'subnet.update.end')
|
||||
|
||||
def _test_subnet_update_lsn_port_not_found(self, dhcp_port):
|
||||
subnet = {
|
||||
'id': 'foo_subnet_id',
|
||||
'enable_dhcp': True,
|
||||
'network_id': 'foo_network_id',
|
||||
'tenant_id': 'foo_tenant_id'
|
||||
}
|
||||
self.lsn_manager.lsn_port_get.side_effect = (
|
||||
p_exc.LsnPortNotFound(lsn_id='foo_lsn_id',
|
||||
entity='subnet',
|
||||
entity_id=subnet['id']))
|
||||
self.notifier.plugin.get_ports.return_value = dhcp_port
|
||||
count = 0 if dhcp_port is None else 1
|
||||
with mock.patch.object(nvp, 'handle_port_dhcp_access') as h:
|
||||
self.notifier.notify(
|
||||
mock.ANY, {'subnet': subnet}, 'subnet.update.end')
|
||||
self.assertEqual(count, h.call_count)
|
||||
if not dhcp_port:
|
||||
self._test_subnet_create(enable_dhcp=True,
|
||||
exc=None, call_notify=False)
|
||||
|
||||
def test_subnet_update_lsn_port_not_found_without_dhcp_port(self):
|
||||
self._test_subnet_update_lsn_port_not_found(None)
|
||||
|
||||
def test_subnet_update_lsn_port_not_found_with_dhcp_port(self):
|
||||
self._test_subnet_update_lsn_port_not_found([mock.ANY])
|
||||
|
||||
def _test_subnet_delete(self, ports=None):
|
||||
subnet = {
|
||||
'id': 'foo_subnet_id',
|
||||
'network_id': 'foo_network_id',
|
||||
'cidr': '0.0.0.0/0'
|
||||
}
|
||||
self.plugin.get_ports.return_value = ports
|
||||
self.notifier.notify(mock.ANY, {'subnet': subnet}, 'subnet.delete.end')
|
||||
filters = {
|
||||
'network_id': [subnet['network_id']],
|
||||
'device_owner': ['network:dhcp']
|
||||
}
|
||||
self.plugin.get_ports.assert_called_once_with(
|
||||
mock.ANY, filters=filters)
|
||||
if ports:
|
||||
self.plugin.delete_port.assert_called_once_with(
|
||||
mock.ANY, ports[0]['id'])
|
||||
else:
|
||||
self.assertEqual(0, self.plugin.delete_port.call_count)
|
||||
|
||||
def test_subnet_delete_enabled_dhcp_no_ports(self):
|
||||
self._test_subnet_delete()
|
||||
|
||||
def test_subnet_delete_enabled_dhcp_with_dhcp_port(self):
|
||||
self._test_subnet_delete([{'id': 'foo_port_id'}])
|
||||
|
||||
|
||||
class DhcpTestCase(base.BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(DhcpTestCase, self).setUp()
|
||||
self.plugin = mock.Mock()
|
||||
self.plugin.lsn_manager = mock.Mock()
|
||||
|
||||
def test_handle_create_network(self):
|
||||
network = {'id': 'foo_network_id'}
|
||||
nvp.handle_network_dhcp_access(
|
||||
self.plugin, mock.ANY, network, 'create_network')
|
||||
self.plugin.lsn_manager.lsn_create.assert_called_once_with(
|
||||
mock.ANY, network['id'])
|
||||
|
||||
def test_handle_delete_network(self):
|
||||
network_id = 'foo_network_id'
|
||||
self.plugin.lsn_manager.lsn_delete_by_network.return_value = (
|
||||
'foo_lsn_id')
|
||||
nvp.handle_network_dhcp_access(
|
||||
self.plugin, mock.ANY, network_id, 'delete_network')
|
||||
self.plugin.lsn_manager.lsn_delete_by_network.assert_called_once_with(
|
||||
mock.ANY, 'foo_network_id')
|
||||
|
||||
def _test_handle_create_dhcp_owner_port(self, exc=None):
|
||||
subnet = {
|
||||
'cidr': '0.0.0.0/0',
|
||||
'id': 'foo_subnet_id'
|
||||
}
|
||||
port = {
|
||||
'id': 'foo_port_id',
|
||||
'device_owner': 'network:dhcp',
|
||||
'mac_address': 'aa:bb:cc:dd:ee:ff',
|
||||
'network_id': 'foo_network_id',
|
||||
'fixed_ips': [{'subnet_id': subnet['id']}]
|
||||
}
|
||||
expected_data = {
|
||||
'subnet_id': subnet['id'],
|
||||
'ip_address': subnet['cidr'],
|
||||
'mac_address': port['mac_address']
|
||||
}
|
||||
self.plugin.get_subnet.return_value = subnet
|
||||
if exc is None:
|
||||
nvp.handle_port_dhcp_access(
|
||||
self.plugin, mock.ANY, port, 'create_port')
|
||||
(self.plugin.lsn_manager.lsn_port_dhcp_setup.
|
||||
assert_called_once_with(mock.ANY, port['network_id'],
|
||||
port['id'], expected_data, subnet))
|
||||
else:
|
||||
self.plugin.lsn_manager.lsn_port_dhcp_setup.side_effect = exc
|
||||
self.assertRaises(n_exc.NeutronException,
|
||||
nvp.handle_port_dhcp_access,
|
||||
self.plugin, mock.ANY, port, 'create_port')
|
||||
|
||||
def test_handle_create_dhcp_owner_port(self):
|
||||
self._test_handle_create_dhcp_owner_port()
|
||||
|
||||
def test_handle_create_dhcp_owner_port_raise_port_config_error(self):
|
||||
config_error = p_exc.PortConfigurationError(lsn_id='foo_lsn_id',
|
||||
net_id='foo_net_id',
|
||||
port_id='foo_port_id')
|
||||
self._test_handle_create_dhcp_owner_port(exc=config_error)
|
||||
|
||||
def test_handle_delete_dhcp_owner_port(self):
|
||||
port = {
|
||||
'id': 'foo_port_id',
|
||||
'device_owner': 'network:dhcp',
|
||||
'network_id': 'foo_network_id',
|
||||
'fixed_ips': [],
|
||||
'mac_address': 'aa:bb:cc:dd:ee:ff'
|
||||
}
|
||||
nvp.handle_port_dhcp_access(self.plugin, mock.ANY, port, 'delete_port')
|
||||
self.plugin.lsn_manager.lsn_port_dispose.assert_called_once_with(
|
||||
mock.ANY, port['network_id'], port['mac_address'])
|
||||
|
||||
def _test_handle_user_port(self, action, handler):
|
||||
port = {
|
||||
'id': 'foo_port_id',
|
||||
'device_owner': 'foo_device_owner',
|
||||
'network_id': 'foo_network_id',
|
||||
'mac_address': 'aa:bb:cc:dd:ee:ff',
|
||||
'fixed_ips': [{'subnet_id': 'foo_subnet_id',
|
||||
'ip_address': '1.2.3.4'}]
|
||||
}
|
||||
expected_data = {
|
||||
'ip_address': '1.2.3.4',
|
||||
'mac_address': 'aa:bb:cc:dd:ee:ff'
|
||||
}
|
||||
self.plugin.get_subnet.return_value = {'enable_dhcp': True}
|
||||
nvp.handle_port_dhcp_access(self.plugin, mock.ANY, port, action)
|
||||
handler.assert_called_once_with(
|
||||
mock.ANY, port['network_id'], 'foo_subnet_id', expected_data)
|
||||
|
||||
def test_handle_create_user_port(self):
|
||||
self._test_handle_user_port(
|
||||
'create_port', self.plugin.lsn_manager.lsn_port_dhcp_host_add)
|
||||
|
||||
def test_handle_delete_user_port(self):
|
||||
self._test_handle_user_port(
|
||||
'delete_port', self.plugin.lsn_manager.lsn_port_dhcp_host_remove)
|
||||
|
||||
def _test_handle_user_port_disabled_dhcp(self, action, handler):
|
||||
port = {
|
||||
'id': 'foo_port_id',
|
||||
'device_owner': 'foo_device_owner',
|
||||
'network_id': 'foo_network_id',
|
||||
'mac_address': 'aa:bb:cc:dd:ee:ff',
|
||||
'fixed_ips': [{'subnet_id': 'foo_subnet_id',
|
||||
'ip_address': '1.2.3.4'}]
|
||||
}
|
||||
self.plugin.get_subnet.return_value = {'enable_dhcp': False}
|
||||
nvp.handle_port_dhcp_access(self.plugin, mock.ANY, port, action)
|
||||
self.assertEqual(0, handler.call_count)
|
||||
|
||||
def test_handle_create_user_port_disabled_dhcp(self):
|
||||
self._test_handle_user_port_disabled_dhcp(
|
||||
'create_port', self.plugin.lsn_manager.lsn_port_dhcp_host_add)
|
||||
|
||||
def test_handle_delete_user_port_disabled_dhcp(self):
|
||||
self._test_handle_user_port_disabled_dhcp(
|
||||
'delete_port', self.plugin.lsn_manager.lsn_port_dhcp_host_remove)
|
||||
|
||||
def _test_handle_user_port_no_fixed_ips(self, action, handler):
|
||||
port = {
|
||||
'id': 'foo_port_id',
|
||||
'device_owner': 'foo_device_owner',
|
||||
'network_id': 'foo_network_id',
|
||||
'fixed_ips': []
|
||||
}
|
||||
nvp.handle_port_dhcp_access(self.plugin, mock.ANY, port, action)
|
||||
self.assertEqual(0, handler.call_count)
|
||||
|
||||
def test_handle_create_user_port_no_fixed_ips(self):
|
||||
self._test_handle_user_port_no_fixed_ips(
|
||||
'create_port', self.plugin.lsn_manager.lsn_port_dhcp_host_add)
|
||||
|
||||
def test_handle_delete_user_port_no_fixed_ips(self):
|
||||
self._test_handle_user_port_no_fixed_ips(
|
||||
'delete_port', self.plugin.lsn_manager.lsn_port_dhcp_host_remove)
|
258
neutron/tests/unit/nicira/test_lsn_lib.py
Normal file
258
neutron/tests/unit/nicira/test_lsn_lib.py
Normal file
@ -0,0 +1,258 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 VMware, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import json
|
||||
import mock
|
||||
|
||||
from neutron.common import exceptions
|
||||
from neutron.plugins.nicira.common import exceptions as nvp_exc
|
||||
from neutron.plugins.nicira.common import utils
|
||||
from neutron.plugins.nicira.nsxlib import lsn as lsnlib
|
||||
from neutron.plugins.nicira import NvpApiClient
|
||||
from neutron.tests import base
|
||||
|
||||
|
||||
class LSNTestCase(base.BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(LSNTestCase, self).setUp()
|
||||
self.mock_request_p = mock.patch.object(lsnlib, 'do_request')
|
||||
self.mock_request = self.mock_request_p.start()
|
||||
self.cluster = mock.Mock()
|
||||
self.cluster.default_service_cluster_uuid = 'foo'
|
||||
self.addCleanup(self.mock_request_p.stop)
|
||||
|
||||
def test_service_cluster_None(self):
|
||||
self.mock_request.return_value = None
|
||||
expected = lsnlib.service_cluster_exists(None, None)
|
||||
self.assertFalse(expected)
|
||||
|
||||
def test_service_cluster_found(self):
|
||||
self.mock_request.return_value = {
|
||||
"results": [
|
||||
{
|
||||
"_href": "/ws.v1/service-cluster/foo_uuid",
|
||||
"display_name": "foo_name",
|
||||
"uuid": "foo_uuid",
|
||||
"tags": [],
|
||||
"_schema": "/ws.v1/schema/ServiceClusterConfig",
|
||||
"gateways": []
|
||||
}
|
||||
],
|
||||
"result_count": 1
|
||||
}
|
||||
expected = lsnlib.service_cluster_exists(None, 'foo_uuid')
|
||||
self.assertTrue(expected)
|
||||
|
||||
def test_service_cluster_not_found(self):
|
||||
self.mock_request.side_effect = exceptions.NotFound()
|
||||
expected = lsnlib.service_cluster_exists(None, 'foo_uuid')
|
||||
self.assertFalse(expected)
|
||||
|
||||
def test_lsn_for_network_create(self):
|
||||
net_id = "foo_network_id"
|
||||
tags = utils.get_tags(n_network_id=net_id)
|
||||
obj = {"service_cluster_uuid": "foo", "tags": tags}
|
||||
lsnlib.lsn_for_network_create(self.cluster, net_id)
|
||||
self.mock_request.assert_called_once_with(
|
||||
"POST", "/ws.v1/lservices-node",
|
||||
json.dumps(obj), cluster=self.cluster)
|
||||
|
||||
def test_lsn_for_network_get(self):
|
||||
net_id = "foo_network_id"
|
||||
lsn_id = "foo_lsn_id"
|
||||
self.mock_request.return_value = {
|
||||
"results": [{"uuid": "foo_lsn_id"}],
|
||||
"result_count": 1
|
||||
}
|
||||
result = lsnlib.lsn_for_network_get(self.cluster, net_id)
|
||||
self.assertEqual(lsn_id, result)
|
||||
self.mock_request.assert_called_once_with(
|
||||
"GET",
|
||||
("/ws.v1/lservices-node?fields=uuid&tag_scope="
|
||||
"n_network_id&tag=%s" % net_id),
|
||||
cluster=self.cluster)
|
||||
|
||||
def test_lsn_for_network_get_none(self):
|
||||
net_id = "foo_network_id"
|
||||
self.mock_request.return_value = {
|
||||
"results": [{"uuid": "foo_lsn_id1"}, {"uuid": "foo_lsn_id2"}],
|
||||
"result_count": 2
|
||||
}
|
||||
result = lsnlib.lsn_for_network_get(self.cluster, net_id)
|
||||
self.assertIsNone(result)
|
||||
|
||||
def test_lsn_for_network_get_raise_not_found(self):
|
||||
net_id = "foo_network_id"
|
||||
self.mock_request.return_value = {
|
||||
"results": [], "result_count": 0
|
||||
}
|
||||
self.assertRaises(exceptions.NotFound,
|
||||
lsnlib.lsn_for_network_get,
|
||||
self.cluster, net_id)
|
||||
|
||||
def test_lsn_delete(self):
|
||||
lsn_id = "foo_id"
|
||||
lsnlib.lsn_delete(self.cluster, lsn_id)
|
||||
self.mock_request.assert_called_once_with(
|
||||
"DELETE",
|
||||
"/ws.v1/lservices-node/%s" % lsn_id, cluster=self.cluster)
|
||||
|
||||
def test_lsn_port_create(self):
|
||||
port_data = {
|
||||
"ip_address": "1.2.3.0/24",
|
||||
"mac_address": "aa:bb:cc:dd:ee:ff",
|
||||
"subnet_id": "foo_subnet_id"
|
||||
}
|
||||
port_id = "foo_port_id"
|
||||
self.mock_request.return_value = {"uuid": port_id}
|
||||
lsn_id = "foo_lsn_id"
|
||||
result = lsnlib.lsn_port_create(self.cluster, lsn_id, port_data)
|
||||
self.assertEqual(result, port_id)
|
||||
tags = utils.get_tags(n_subnet_id=port_data["subnet_id"],
|
||||
n_mac_address=port_data["mac_address"])
|
||||
port_obj = {
|
||||
"ip_address": port_data["ip_address"],
|
||||
"mac_address": port_data["mac_address"],
|
||||
"type": "LogicalServicesNodePortConfig",
|
||||
"tags": tags
|
||||
}
|
||||
self.mock_request.assert_called_once_with(
|
||||
"POST", "/ws.v1/lservices-node/%s/lport" % lsn_id,
|
||||
json.dumps(port_obj), cluster=self.cluster)
|
||||
|
||||
def test_lsn_port_delete(self):
|
||||
lsn_id = "foo_lsn_id"
|
||||
lsn_port_id = "foo_port_id"
|
||||
lsnlib.lsn_port_delete(self.cluster, lsn_id, lsn_port_id)
|
||||
self.mock_request.assert_called_once_with(
|
||||
"DELETE",
|
||||
"/ws.v1/lservices-node/%s/lport/%s" % (lsn_id, lsn_port_id),
|
||||
cluster=self.cluster)
|
||||
|
||||
def test_lsn_port_get_with_filters(self):
|
||||
lsn_id = "foo_lsn_id"
|
||||
port_id = "foo_port_id"
|
||||
filters = {"tag": "foo_tag", "tag_scope": "foo_scope"}
|
||||
self.mock_request.return_value = {
|
||||
"results": [{"uuid": port_id}],
|
||||
"result_count": 1
|
||||
}
|
||||
result = lsnlib._lsn_port_get(self.cluster, lsn_id, filters)
|
||||
self.assertEqual(result, port_id)
|
||||
self.mock_request.assert_called_once_with(
|
||||
"GET",
|
||||
("/ws.v1/lservices-node/%s/lport?fields=uuid&tag_scope=%s&"
|
||||
"tag=%s" % (lsn_id, filters["tag_scope"], filters["tag"])),
|
||||
cluster=self.cluster)
|
||||
|
||||
def test_lsn_port_get_with_filters_return_none(self):
|
||||
self.mock_request.return_value = {
|
||||
"results": [{"uuid": "foo1"}, {"uuid": "foo2"}],
|
||||
"result_count": 2
|
||||
}
|
||||
result = lsnlib._lsn_port_get(self.cluster, "lsn_id", None)
|
||||
self.assertIsNone(result)
|
||||
|
||||
def test_lsn_port_get_with_filters_raises_not_found(self):
|
||||
self.mock_request.return_value = {"results": [], "result_count": 0}
|
||||
self.assertRaises(exceptions.NotFound,
|
||||
lsnlib._lsn_port_get,
|
||||
self.cluster, "lsn_id", None)
|
||||
|
||||
def test_lsn_port_plug_network(self):
|
||||
lsn_id = "foo_lsn_id"
|
||||
lsn_port_id = "foo_lsn_port_id"
|
||||
lswitch_port_id = "foo_lswitch_port_id"
|
||||
lsnlib.lsn_port_plug_network(
|
||||
self.cluster, lsn_id, lsn_port_id, lswitch_port_id)
|
||||
self.mock_request.assert_called_once_with(
|
||||
"PUT",
|
||||
("/ws.v1/lservices-node/%s/lport/%s/"
|
||||
"attachment") % (lsn_id, lsn_port_id),
|
||||
json.dumps({"peer_port_uuid": lswitch_port_id,
|
||||
"type": "PatchAttachment"}),
|
||||
cluster=self.cluster)
|
||||
|
||||
def test_lsn_port_plug_network_raise_conflict(self):
|
||||
lsn_id = "foo_lsn_id"
|
||||
lsn_port_id = "foo_lsn_port_id"
|
||||
lswitch_port_id = "foo_lswitch_port_id"
|
||||
self.mock_request.side_effect = NvpApiClient.Conflict
|
||||
self.assertRaises(
|
||||
nvp_exc.LsnConfigurationConflict,
|
||||
lsnlib.lsn_port_plug_network,
|
||||
self.cluster, lsn_id, lsn_port_id, lswitch_port_id)
|
||||
|
||||
def _test_lsn_port_dhcp_configure(
|
||||
self, lsn_id, lsn_port_id, is_enabled, opts):
|
||||
lsnlib.lsn_port_dhcp_configure(
|
||||
self.cluster, lsn_id, lsn_port_id, is_enabled, opts)
|
||||
opt_array = ["%s=%s" % (key, val) for key, val in opts.iteritems()]
|
||||
self.mock_request.assert_has_calls([
|
||||
mock.call("PUT", "/ws.v1/lservices-node/%s/dhcp" % lsn_id,
|
||||
json.dumps({"enabled": is_enabled}),
|
||||
cluster=self.cluster),
|
||||
mock.call("PUT",
|
||||
("/ws.v1/lservices-node/%s/"
|
||||
"lport/%s/dhcp") % (lsn_id, lsn_port_id),
|
||||
json.dumps({"options": {"options": opt_array}}),
|
||||
cluster=self.cluster)
|
||||
])
|
||||
|
||||
def test_lsn_port_dhcp_configure_empty_opts(self):
|
||||
lsn_id = "foo_lsn_id"
|
||||
lsn_port_id = "foo_lsn_port_id"
|
||||
is_enabled = False
|
||||
opts = {}
|
||||
self._test_lsn_port_dhcp_configure(
|
||||
lsn_id, lsn_port_id, is_enabled, opts)
|
||||
|
||||
def test_lsn_port_dhcp_configure_with_opts(self):
|
||||
lsn_id = "foo_lsn_id"
|
||||
lsn_port_id = "foo_lsn_port_id"
|
||||
is_enabled = True
|
||||
opts = {"opt1": "val1", "opt2": "val2"}
|
||||
self._test_lsn_port_dhcp_configure(
|
||||
lsn_id, lsn_port_id, is_enabled, opts)
|
||||
|
||||
def _test_lsn_port_host_action(
|
||||
self, lsn_port_action_func, extra_action, action, host):
|
||||
lsn_id = "foo_lsn_id"
|
||||
lsn_port_id = "foo_lsn_port_id"
|
||||
lsn_port_action_func(self.cluster, lsn_id, lsn_port_id, host)
|
||||
self.mock_request.assert_called_once_with(
|
||||
"POST",
|
||||
("/ws.v1/lservices-node/%s/lport/"
|
||||
"%s/%s?action=%s") % (lsn_id, lsn_port_id, extra_action, action),
|
||||
json.dumps(host), cluster=self.cluster)
|
||||
|
||||
def test_lsn_port_dhcp_host_add(self):
|
||||
host = {
|
||||
"ip_address": "1.2.3.4",
|
||||
"mac_address": "aa:bb:cc:dd:ee:ff"
|
||||
}
|
||||
self._test_lsn_port_host_action(
|
||||
lsnlib.lsn_port_dhcp_host_add, "dhcp", "add_host", host)
|
||||
|
||||
def test_lsn_port_dhcp_host_remove(self):
|
||||
host = {
|
||||
"ip_address": "1.2.3.4",
|
||||
"mac_address": "aa:bb:cc:dd:ee:ff"
|
||||
}
|
||||
self._test_lsn_port_host_action(
|
||||
lsnlib.lsn_port_dhcp_host_remove, "dhcp", "remove_host", host)
|
@ -1501,6 +1501,68 @@ class NvplibMiscTestCase(base.BaseTestCase):
|
||||
result = utils.check_and_truncate(name)
|
||||
self.assertEqual(len(result), utils.MAX_DISPLAY_NAME_LEN)
|
||||
|
||||
def test_build_uri_path_plain(self):
|
||||
result = nvplib._build_uri_path('RESOURCE')
|
||||
self.assertEqual("%s/%s" % (nvplib.URI_PREFIX, 'RESOURCE'), result)
|
||||
|
||||
def test_build_uri_path_with_field(self):
|
||||
result = nvplib._build_uri_path('RESOURCE', fields='uuid')
|
||||
expected = "%s/%s?fields=uuid" % (nvplib.URI_PREFIX, 'RESOURCE')
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
def test_build_uri_path_with_filters(self):
|
||||
filters = {"tag": 'foo', "tag_scope": "scope_foo"}
|
||||
result = nvplib._build_uri_path('RESOURCE', filters=filters)
|
||||
expected = (
|
||||
"%s/%s?tag_scope=scope_foo&tag=foo" %
|
||||
(nvplib.URI_PREFIX, 'RESOURCE'))
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
def test_build_uri_path_with_resource_id(self):
|
||||
res = 'RESOURCE'
|
||||
res_id = 'resource_id'
|
||||
result = nvplib._build_uri_path(res, resource_id=res_id)
|
||||
expected = "%s/%s/%s" % (nvplib.URI_PREFIX, res, res_id)
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
def test_build_uri_path_with_parent_and_resource_id(self):
|
||||
parent_res = 'RESOURCE_PARENT'
|
||||
child_res = 'RESOURCE_CHILD'
|
||||
res = '%s/%s' % (child_res, parent_res)
|
||||
par_id = 'parent_resource_id'
|
||||
res_id = 'resource_id'
|
||||
result = nvplib._build_uri_path(
|
||||
res, parent_resource_id=par_id, resource_id=res_id)
|
||||
expected = ("%s/%s/%s/%s/%s" %
|
||||
(nvplib.URI_PREFIX, parent_res, par_id, child_res, res_id))
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
def test_build_uri_path_with_attachment(self):
|
||||
parent_res = 'RESOURCE_PARENT'
|
||||
child_res = 'RESOURCE_CHILD'
|
||||
res = '%s/%s' % (child_res, parent_res)
|
||||
par_id = 'parent_resource_id'
|
||||
res_id = 'resource_id'
|
||||
result = nvplib._build_uri_path(res, parent_resource_id=par_id,
|
||||
resource_id=res_id, is_attachment=True)
|
||||
expected = ("%s/%s/%s/%s/%s/%s" %
|
||||
(nvplib.URI_PREFIX, parent_res,
|
||||
par_id, child_res, res_id, 'attachment'))
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
def test_build_uri_path_with_extra_action(self):
|
||||
parent_res = 'RESOURCE_PARENT'
|
||||
child_res = 'RESOURCE_CHILD'
|
||||
res = '%s/%s' % (child_res, parent_res)
|
||||
par_id = 'parent_resource_id'
|
||||
res_id = 'resource_id'
|
||||
result = nvplib._build_uri_path(res, parent_resource_id=par_id,
|
||||
resource_id=res_id, extra_action='doh')
|
||||
expected = ("%s/%s/%s/%s/%s/%s" %
|
||||
(nvplib.URI_PREFIX, parent_res,
|
||||
par_id, child_res, res_id, 'doh'))
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
|
||||
def _nicira_method(method_name, module_name='nvplib'):
|
||||
return '%s.%s.%s' % ('neutron.plugins.nicira', module_name, method_name)
|
||||
|
@ -27,7 +27,9 @@ from neutron.openstack.common import uuidutils
|
||||
from neutron.plugins.nicira.common import config # noqa
|
||||
from neutron.plugins.nicira.common import exceptions
|
||||
from neutron.plugins.nicira.common import sync
|
||||
from neutron.plugins.nicira.nsxlib import lsn as lsnlib
|
||||
from neutron.plugins.nicira import nvp_cluster
|
||||
from neutron.plugins.nicira import NvpApiClient as nvp_client
|
||||
from neutron.tests.unit.nicira import get_fake_conf
|
||||
from neutron.tests.unit.nicira import PLUGIN_NAME
|
||||
|
||||
@ -147,17 +149,49 @@ class ConfigurationTest(testtools.TestCase):
|
||||
self.assertIn('extensions', cfg.CONF.api_extensions_path)
|
||||
|
||||
def test_agentless_extensions(self):
|
||||
self.skipTest('Enable once agentless support is added')
|
||||
q_config.parse(['--config-file', NVP_BASE_CONF_PATH,
|
||||
'--config-file', NVP_INI_AGENTLESS_PATH])
|
||||
cfg.CONF.set_override('core_plugin', PLUGIN_NAME)
|
||||
self.assertEqual(config.AgentModes.AGENTLESS,
|
||||
cfg.CONF.NVP.agent_mode)
|
||||
plugin = NeutronManager().get_plugin()
|
||||
self.assertNotIn('agent',
|
||||
plugin.supported_extension_aliases)
|
||||
self.assertNotIn('dhcp_agent_scheduler',
|
||||
plugin.supported_extension_aliases)
|
||||
# The version returned from NVP does not really matter here
|
||||
with mock.patch.object(nvp_client.NVPApiHelper,
|
||||
'get_nvp_version',
|
||||
return_value=nvp_client.NVPVersion("9.9")):
|
||||
with mock.patch.object(lsnlib,
|
||||
'service_cluster_exists',
|
||||
return_value=True):
|
||||
plugin = NeutronManager().get_plugin()
|
||||
self.assertNotIn('agent',
|
||||
plugin.supported_extension_aliases)
|
||||
self.assertNotIn('dhcp_agent_scheduler',
|
||||
plugin.supported_extension_aliases)
|
||||
|
||||
def test_agentless_extensions_version_fail(self):
|
||||
q_config.parse(['--config-file', NVP_BASE_CONF_PATH,
|
||||
'--config-file', NVP_INI_AGENTLESS_PATH])
|
||||
cfg.CONF.set_override('core_plugin', PLUGIN_NAME)
|
||||
self.assertEqual(config.AgentModes.AGENTLESS,
|
||||
cfg.CONF.NVP.agent_mode)
|
||||
with mock.patch.object(nvp_client.NVPApiHelper,
|
||||
'get_nvp_version',
|
||||
return_value=nvp_client.NVPVersion("3.2")):
|
||||
self.assertRaises(exceptions.NvpPluginException, NeutronManager)
|
||||
|
||||
def test_agentless_extensions_unmet_deps_fail(self):
|
||||
q_config.parse(['--config-file', NVP_BASE_CONF_PATH,
|
||||
'--config-file', NVP_INI_AGENTLESS_PATH])
|
||||
cfg.CONF.set_override('core_plugin', PLUGIN_NAME)
|
||||
self.assertEqual(config.AgentModes.AGENTLESS,
|
||||
cfg.CONF.NVP.agent_mode)
|
||||
with mock.patch.object(nvp_client.NVPApiHelper,
|
||||
'get_nvp_version',
|
||||
return_value=nvp_client.NVPVersion("3.2")):
|
||||
with mock.patch.object(lsnlib,
|
||||
'service_cluster_exists',
|
||||
return_value=False):
|
||||
self.assertRaises(exceptions.NvpPluginException,
|
||||
NeutronManager)
|
||||
|
||||
def test_agent_extensions(self):
|
||||
q_config.parse(['--config-file', NVP_BASE_CONF_PATH,
|
||||
|
Loading…
Reference in New Issue
Block a user