NSX TVD: V, T and simple DVS Coexist in the same plugin

Introduce a plugin that can work with all of the VC and NSX
offerings under the same umbrella of a single plugin.

Co-Authored-By: Adit Sarfaty <asarfaty@vmware.com>

Change-Id: I0449d64e3cf79b7a3a846dacba95e8854d53bdf8
This commit is contained in:
Gary Kotton 2017-11-20 00:28:37 -08:00
parent 315e0befe0
commit 792a6a0103
25 changed files with 1403 additions and 61 deletions

View File

@ -33,7 +33,6 @@ function nsxv_configure_service {
if [[ "$NSX_L2GW_DRIVER" != "" ]]; then
iniset /$Q_PLUGIN_CONF_FILE DEFAULT nsx_l2gw_driver $NSX_L2GW_DRIVER
fi
iniset /$Q_PLUGIN_CONF_FILE DEFAULT nsx_extension_drivers vmware_nsxv_dns
_nsxv_ini_set password "$NSXV_PASSWORD"
_nsxv_ini_set user "$NSXV_USER"
_nsxv_ini_set vdn_scope_id "$NSXV_VDN_SCOPE_ID"
@ -104,7 +103,6 @@ function nsxv3_configure_service {
if [[ "$NSX_L2GW_DRIVER" != "" ]]; then
iniset /$Q_PLUGIN_CONF_FILE DEFAULT nsx_l2gw_driver $NSX_L2GW_DRIVER
fi
iniset /$Q_PLUGIN_CONF_FILE DEFAULT nsx_extension_drivers vmware_nsxv3_dns
_nsxv3_ini_set nsx_api_user $NSX_USER
_nsxv3_ini_set nsx_api_password $NSX_PASSWORD
_nsxv3_ini_set retries $NSX_RETRIES

253
devstack/lib/vmware_nsx_tvd Normal file
View File

@ -0,0 +1,253 @@
#!/bin/bash
# Copyright 2015 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.
# Neutron VMware NSX plugin
# -------------------------
# Settings previously defined in devstack:lib/neutron-legacy
NEUTRON_CONF_DIR=/etc/neutron
export NEUTRON_TEST_CONFIG_FILE=${NEUTRON_TEST_CONFIG_FILE:-"$NEUTRON_CONF_DIR/debug.ini"}
Q_DHCP_CONF_FILE=$NEUTRON_CONF_DIR/dhcp_agent.ini
# The interface which has connectivity to the NSX Gateway uplink
NSX_GATEWAY_NETWORK_INTERFACE=${NSX_GATEWAY_NETWORK_INTERFACE:-}
# Override default 'True' in devstack:lib/neutron_plugins/services/l3
Q_USE_PROVIDERNET_FOR_PUBLIC=False
# Native support from platform
NATIVE_DHCP_METADATA=${NATIVE_DHCP_METADATA:-True}
NATIVE_METADATA_ROUTE=${NATIVE_METADATA_ROUTE:-169.254.169.254/31}
METADATA_PROXY_SHARED_SECRET=${METADATA_PROXY_SHARED_SECRET:-}
# Default AZ
NSX_DEFAULT_AZ=${NSX_DEFAULT_AZ:-defaultv3}
# Save trace setting
NSX_XTRACE=$(set +o | grep xtrace)
set +o xtrace
# File to store client certificate and PK
CLIENT_CERT_FILE=${DEST}/data/neutron/client.pem
source $TOP_DIR/lib/neutron_plugins/ovs_base
dir=${GITDIR['vmware-nsx']}/devstack
source $dir/lib/nsx_common
function _version { echo "$@" | awk -F. '{ printf("%d%03d%03d%03d\n", $1,$2,$3,$4); }'; }
function _ovsdb_connection {
managers=(${NSX_MANAGER//,/ })
NSX_MGR_IP=${managers[0]}
NSX_VER=$(curl -1 -s -k -u "$NSX_USER:$NSX_PASSWORD" -H 'Accept: application/json' https://$NSX_MGR_IP/api/v1/node | python -c 'import sys, json; print json.load(sys.stdin)["node_version"][:5]')
if [ $(_version $NSX_VER) -ge $(_version 1.1.0) ]; then
echo "unix:/var/run/vmware/nsx-agent/nsxagent_ovsdb.sock"
else
echo "tcp:127.0.0.1:6632"
fi
}
function setup_integration_bridge {
die_if_not_set $LINENO NSX_MANAGER "NSX_MANAGER has not been set!"
die_if_not_set $LINENO NSX_USER "NSX_USER has not been set!"
die_if_not_set $LINENO NSX_PASSWORD "NSX_PASSWORD has not been set!"
# Ensure that the OVS params are set for the OVS utils
iniset $NEUTRON_CONF DEFAULT ovs_integration_bridge $OVS_BRIDGE
iniset $NEUTRON_CONF OVS ovsdb_connection $(_ovsdb_connection)
iniset $NEUTRON_CONF OVS ovsdb_interface vsctl
_neutron_ovs_base_setup_bridge $OVS_BRIDGE
sudo ovs-vsctl set bridge $OVS_BRIDGE external_ids:bridge-id=nsx-managed
sudo ovs-vsctl set-manager $(_ovsdb_connection)
}
function is_neutron_ovs_base_plugin {
# This allows the deployer to decide whether devstack should install OVS.
# By default, we install OVS, to change this behavior add "OVS_BASE=1" to your localrc file.
# Note: Any KVM compute must have OVS installed on it.
return ${OVS_BASE:-0}
}
function neutron_plugin_create_nova_conf {
if [[ "$VIRT_DRIVER" != 'vsphere' ]]; then
# if n-cpu or octavia is enabled, then setup integration bridge
if is_service_enabled n-cpu || is_service_enabled octavia ; then
setup_integration_bridge
if is_service_enabled n-cpu ; then
iniset $NOVA_CONF neutron ovs_bridge $OVS_BRIDGE
fi
fi
fi
# if n-api is enabled, then setup the metadata_proxy_shared_secret
if is_service_enabled n-api; then
iniset $NOVA_CONF neutron service_metadata_proxy True
if [[ "$NATIVE_DHCP_METADATA" == "True" ]]; then
iniset $NOVA_CONF neutron metadata_proxy_shared_secret $METADATA_PROXY_SHARED_SECRET
if [[ "$METADATA_PROXY_USE_HTTPS" == "True" ]]; then
iniset $NOVA_CONF DEFAULT enabled_ssl_apis metadata
if [[ "$METADATA_PROXY_CERT_FILE" != "" ]]; then
iniset $NOVA_CONF wsgi ssl_cert_file $METADATA_PROXY_CERT_FILE
fi
if [[ "$METADATA_PROXY_PRIV_KEY_FILE" != "" ]]; then
iniset $NOVA_CONF wsgi ssl_key_file $METADATA_PROXY_PRIV_KEY_FILE
fi
fi
fi
fi
}
function neutron_plugin_install_agent_packages {
# VMware NSX Plugin does not run q-agt, but it currently needs dhcp and metadata agents
_neutron_ovs_base_install_agent_packages
}
function neutron_plugin_configure_common {
Q_PLUGIN_CONF_PATH=etc/neutron/plugins/vmware
Q_PLUGIN_CONF_FILENAME=nsx.ini
Q_PLUGIN_SRC_CONF_PATH=vmware-nsx/etc
VMWARE_NSX_DIR=vmware-nsx
# Uses oslo config generator to generate sample configuration file
(cd $DEST/$VMWARE_NSX_DIR && exec ./tools/generate_config_file_samples.sh)
mkdir -p /$Q_PLUGIN_CONF_PATH
cp $DEST/$Q_PLUGIN_SRC_CONF_PATH/nsx.ini.sample /$Q_PLUGIN_CONF_PATH/$Q_PLUGIN_CONF_FILENAME
sudo install -d -o $STACK_USER $NEUTRON_CONF_DIR/policy.d
cp -vr $DEST/$Q_PLUGIN_SRC_CONF_PATH/policy.d $NEUTRON_CONF_DIR/policy.d
Q_PLUGIN_CLASS="vmware_nsxtvd"
}
function neutron_plugin_configure_debug_command {
sudo ovs-vsctl --no-wait -- --may-exist add-br $PUBLIC_BRIDGE
iniset $NEUTRON_TEST_CONFIG_FILE DEFAULT external_network_bridge "$PUBLIC_BRIDGE"
}
function neutron_plugin_configure_dhcp_agent {
setup_integration_bridge
iniset $Q_DHCP_CONF_FILE DEFAULT enable_isolated_metadata True
iniset $Q_DHCP_CONF_FILE DEFAULT enable_metadata_network True
iniset $Q_DHCP_CONF_FILE DEFAULT ovs_use_veth True
iniset $Q_DHCP_CONF_FILE DEFAULT ovs_integration_bridge $OVS_BRIDGE
iniset $Q_DHCP_CONF_FILE OVS ovsdb_connection $(_ovsdb_connection)
iniset $Q_DHCP_CONF_FILE OVS ovsdb_interface vsctl
}
function neutron_plugin_configure_l3_agent {
# VMware NSX plugin does not run L3 agent
die $LINENO "q-l3 should not be executed with VMware NSX plugin!"
}
function neutron_plugin_configure_plugin_agent {
# VMware NSX plugin does not run L2 agent
die $LINENO "q-agt must not be executed with VMware NSX plugin!"
}
function neutron_plugin_configure_service {
nsxv3_configure_service
nsxv_configure_service
iniset /$Q_PLUGIN_CONF_FILE nsx_tvd nsx_v_extension_drivers vmware_nsxv_dns
iniset /$Q_PLUGIN_CONF_FILE nsx_tvd nsx_v3_extension_drivers vmware_nsxv3_dns
iniset /$Q_PLUGIN_CONF_FILE DEFAULT default_availability_zones $NSX_DEFAULT_AZ
}
function neutron_plugin_setup_interface_driver {
local conf_file=$1
iniset $conf_file DEFAULT interface_driver neutron.agent.linux.interface.OVSInterfaceDriver
}
function neutron_plugin_check_adv_test_requirements {
is_service_enabled q-dhcp && return 0
}
function init_vmware_nsx_tvd {
if (is_service_enabled q-svc || is_service_enabled neutron-api) && [[ "$NATIVE_DHCP_METADATA" == "True" ]]; then
if ! is_set DHCP_PROFILE_UUID; then
die $LINENO "DHCP profile needs to be configured!"
fi
if ! is_set METADATA_PROXY_UUID; then
die $LINENO "Metadata proxy needs to be configured!"
fi
if is_service_enabled q-dhcp q-meta; then
die $LINENO "Native support does not require DHCP and Metadata agents!"
fi
fi
# Generate client certificate
if [[ "$NSX_USE_CLIENT_CERT_AUTH" == "True" ]]; then
nsxadmin -o generate -r certificate
fi
if ! is_set NSX_GATEWAY_NETWORK_INTERFACE; then
echo "NSX_GATEWAY_NETWORK_INTERFACE not set not configuring routes"
return
fi
if ! is_set NSX_GATEWAY_NETWORK_CIDR; then
NSX_GATEWAY_NETWORK_CIDR=$PUBLIC_NETWORK_GATEWAY/${FLOATING_RANGE#*/}
echo "The IP address to set on $PUBLIC_BRIDGE was not specified. "
echo "Defaulting to $NSX_GATEWAY_NETWORK_CIDR"
fi
# Make sure the interface is up, but not configured
sudo ip link set $NSX_GATEWAY_NETWORK_INTERFACE up
# Save and then flush the IP addresses on the interface
addresses=$(ip addr show dev $NSX_GATEWAY_NETWORK_INTERFACE | grep inet | awk {'print $2'})
sudo ip addr flush $NSX_GATEWAY_NETWORK_INTERFACE
# Use the PUBLIC Bridge to route traffic to the NSX gateway
# NOTE(armando-migliaccio): if running in a nested environment this will work
# only with mac learning enabled, portsecurity and security profiles disabled
# The public bridge might not exist for the NSX plugin if Q_USE_DEBUG_COMMAND is off
# Try to create it anyway
sudo ovs-vsctl --may-exist add-br $PUBLIC_BRIDGE
sudo ovs-vsctl --may-exist add-port $PUBLIC_BRIDGE $NSX_GATEWAY_NETWORK_INTERFACE
# Flush all existing addresses on public bridge
sudo ip addr flush dev $PUBLIC_BRIDGE
nsx_gw_net_if_mac=$(ip link show $NSX_GATEWAY_NETWORK_INTERFACE | awk '/ether/ {print $2}')
sudo ip link set address $nsx_gw_net_if_mac dev $PUBLIC_BRIDGE
for address in $addresses; do
sudo ip addr add dev $PUBLIC_BRIDGE $address
done
sudo ip addr add dev $PUBLIC_BRIDGE $NSX_GATEWAY_NETWORK_CIDR
sudo ip link set $PUBLIC_BRIDGE up
}
function stop_vmware_nsx_tvd {
# Clean client certificate if exists
nsxadmin -o clean -r certificate
if ! is_set NSX_GATEWAY_NETWORK_INTERFACE; then
echo "NSX_GATEWAY_NETWORK_INTERFACE was not configured."
return
fi
if ! is_set NSX_GATEWAY_NETWORK_CIDR; then
NSX_GATEWAY_NETWORK_CIDR=$PUBLIC_NETWORK_GATEWAY/${FLOATING_RANGE#*/}
echo "The IP address expected on $PUBLIC_BRIDGE was not specified. "
echo "Defaulting to "$NSX_GATEWAY_NETWORK_CIDR
fi
sudo ip addr del $NSX_GATEWAY_NETWORK_CIDR dev $PUBLIC_BRIDGE
# Save and then flush remaining addresses on the interface
addresses=$(ip addr show dev $PUBLIC_BRIDGE | grep inet | awk {'print $2'})
sudo ip addr flush $PUBLIC_BRIDGE
# Try to detach physical interface from PUBLIC_BRIDGE
sudo ovs-vsctl del-port $NSX_GATEWAY_NETWORK_INTERFACE
# Restore addresses on NSX_GATEWAY_NETWORK_INTERFACE
for address in $addresses; do
sudo ip addr add dev $NSX_GATEWAY_NETWORK_INTERFACE $address
done
}
# Restore xtrace
$NSX_XTRACE

View File

@ -84,6 +84,7 @@ function neutron_plugin_configure_plugin_agent {
function neutron_plugin_configure_service {
nsxv_configure_service
iniset /$Q_PLUGIN_CONF_FILE DEFAULT nsx_extension_drivers vmware_nsxv_dns
}
function neutron_plugin_setup_interface_driver {

View File

@ -155,6 +155,7 @@ function neutron_plugin_configure_plugin_agent {
function neutron_plugin_configure_service {
nsxv3_configure_service
iniset /$Q_PLUGIN_CONF_FILE DEFAULT nsx_extension_drivers vmware_nsxv3_dns
}
function neutron_plugin_setup_interface_driver {

View File

@ -71,6 +71,28 @@ elif [[ $Q_PLUGIN == 'vmware_nsx_v3' ]]; then
python $dir/tools/nsxv3_cleanup.py --mgr-ip $NSX_MANAGER --user $NSX_USER --password $NSX_PASSWORD
fi
fi
elif [[ $Q_PLUGIN == 'vmware_nsx_tvd' ]]; then
source $dir/lib/vmware_nsx_tvd
if [[ "$1" == "stack" && "$2" == "post-config" ]]; then
init_vmware_nsx_tvd
elif [[ "$1" == "unstack" ]]; then
db_connection=$(iniget $NEUTRON_CONF database connection)
stop_vmware_nsx_v3
# only clean up when q-svc (legacy support) or neutron-api is enabled
if is_service_enabled q-svc || is_service_enabled neutron-api; then
NSX_MANAGER=${NSX_MANAGERS:-$NSX_MANAGER}
IFS=','
NSX_MANAGER=($NSX_MANAGER)
unset IFS
python $dir/tools/nsxv3_cleanup.py --mgr-ip $NSX_MANAGER --user $NSX_USER --password $NSX_PASSWORD --db-connection $db_connection
python $dir/tools/nsxv_cleanup.py --vsm-ip ${NSXV_MANAGER_URI/https:\/\/} --user $NSXV_USER --password $NSXV_PASSWORD --db-connection $db_connection
fi
elif [[ "$1" == 'clean' ]]; then
if is_service_enabled q-svc || is_service_enabled neutron-api; then
python $dir/tools/nsxv3_cleanup.py --mgr-ip $NSX_MANAGER --user $NSX_USER --password $NSX_PASSWORD
python $dir/tools/nsxv_cleanup.py --vsm-ip ${NSXV_MANAGER_URI/https:\/\/} --user $NSXV_USER --password $NSXV_PASSWORD
fi
fi
elif [[ $Q_PLUGIN == 'vmware_dvs' ]]; then
source $dir/lib/vmware_dvs
fi

View File

@ -33,6 +33,7 @@ neutron.core_plugins =
vmware_nsxv = vmware_nsx.plugin:NsxVPlugin
vmware_nsxv3 = vmware_nsx.plugin:NsxV3Plugin
vmware_dvs = vmware_nsx.plugin:NsxDvsPlugin
vmware_nsxtvd = vmware_nsx.plugin:NsxTVDPlugin
firewall_drivers =
vmware_nsxv_edge = vmware_nsx.services.fwaas.nsx_v.edge_fwaas_driver:EdgeFwaasDriver
vmware_nsxv3_edge = vmware_nsx.services.fwaas.nsx_v3.edge_fwaas_driver_v1:EdgeFwaasV3DriverV1
@ -68,6 +69,9 @@ openstack.nsxclient.v2 =
security_group_set = vmware_nsx.osc.v2.security_group:NsxSetSecurityGroup
subnet_create = vmware_nsx.osc.v2.subnet:NsxCreateSubnet
subnet_set = vmware_nsx.osc.v2.subnet:NsxSetSubnet
project_plugin_create = vmware_nsx.osc.v2.project_plugin_map:CreateProjectPluginMap
project_plugin_show = vmware_nsx.osc.v2.project_plugin_map:ShowProjectPluginMap
project_plugin_list = vmware_nsx.osc.v2.project_plugin_map:ListProjectPluginMap
[build_sphinx]
source-dir = doc/source

View File

@ -78,7 +78,7 @@ class ConfiguredAvailabilityZones(object):
default_name = DEFAULT_NAME
def __init__(self, az_conf, az_class):
def __init__(self, az_conf, az_class, validate_default=True):
self.availability_zones = {}
# Add the configured availability zones
@ -100,11 +100,16 @@ class ConfiguredAvailabilityZones(object):
reason=_("The NSX plugin supports only 1 default AZ"))
default_az_name = cfg.CONF.default_availability_zones[0]
if (default_az_name not in self.availability_zones):
if validate_default:
raise nsx_exc.NsxInvalidConfiguration(
opt_name="default_availability_zones",
opt_value=cfg.CONF.default_availability_zones,
reason=_("The default AZ is not defined in the NSX "
"plugin"))
else:
self._default_az = self.availability_zones[
self.default_name]
else:
self._default_az = self.availability_zones[default_az_name]
else:
self._default_az = self.availability_zones[self.default_name]

View File

@ -813,12 +813,26 @@ nsxv3_az_opts = [
"on router ports.")),
]
nsx_tvd_opts = [
cfg.ListOpt('nsx_v_extension_drivers',
default=[],
help=_("An ordered list of NSX-V extension driver "
"entrypoints to be loaded from the "
"vmware_nsx.extension_drivers namespace.")),
cfg.ListOpt('nsx_v3_extension_drivers',
default=[],
help=_("An ordered list of NSX-T extension driver "
"entrypoints to be loaded from the "
"vmware_nsx.extension_drivers namespace.")),
]
# Register the configuration options
cfg.CONF.register_opts(connection_opts)
cfg.CONF.register_opts(cluster_opts)
cfg.CONF.register_opts(nsx_common_opts)
cfg.CONF.register_opts(nsx_v3_opts, group="nsx_v3")
cfg.CONF.register_opts(nsxv_opts, group="nsxv")
cfg.CONF.register_opts(nsx_tvd_opts, group="nsx_tvd")
cfg.CONF.register_opts(base_opts, group="NSX")
cfg.CONF.register_opts(sync_opts, group="NSX_SYNC")
@ -891,3 +905,9 @@ def validate_nsxv_config_options():
error = _("dvs host/vcenter credentials must be defined to use "
"dvs features")
raise nsx_exc.NsxPluginException(err_msg=error)
def validate_nsx_config_options():
if cfg.CONF.nsx_extension_drivers:
error = _("nsx_extension_drivers should not be configured!")
raise nsx_exc.NsxPluginException(err_msg=error)

View File

@ -24,15 +24,18 @@ LOG = log.getLogger(__name__)
class ExtensionManager(stevedore.named.NamedExtensionManager):
"""Manage extension drivers using drivers."""
def __init__(self):
def __init__(self, extension_drivers=None):
# Ordered list of extension drivers, defining
# the order in which the drivers are called.
self.ordered_ext_drivers = []
if extension_drivers is None:
extension_drivers = cfg.CONF.nsx_extension_drivers
LOG.info("Configured extension driver names: %s",
cfg.CONF.nsx_extension_drivers)
extension_drivers)
super(ExtensionManager, self).__init__('vmware_nsx.extension_drivers',
cfg.CONF.nsx_extension_drivers,
extension_drivers,
invoke_on_load=True,
name_order=True)
LOG.info("Loaded extension driver names: %s", self.names())

View File

@ -20,6 +20,7 @@ from sqlalchemy.orm import exc
from oslo_db import exception as db_exc
from oslo_log import log as logging
from oslo_utils import excutils
from oslo_utils import uuidutils
import neutron.db.api as db
@ -289,15 +290,19 @@ def get_neutron_from_nsx_router_id(session, nsx_router_id):
LOG.debug("Couldn't find router with nsx id %s", nsx_router_id)
def get_nsx_security_group_id(session, neutron_id):
def get_nsx_security_group_id(session, neutron_id, moref=False):
"""Return the id of a security group in the NSX backend.
Note: security groups are called 'security profiles' in NSX
"""
try:
mapping = (session.query(nsx_models.NeutronNsxSecurityGroupMapping).
mappings = (session.query(nsx_models.NeutronNsxSecurityGroupMapping).
filter_by(neutron_id=neutron_id).
one())
all())
for mapping in mappings:
if moref and not uuidutils.is_uuid_like(mapping['nsx_id']):
return mapping['nsx_id']
if not moref and uuidutils.is_uuid_like(mapping['nsx_id']):
return mapping['nsx_id']
except exc.NoResultFound:
LOG.debug("NSX identifiers for neutron security group %s not yet "
@ -437,14 +442,29 @@ def save_sg_mappings(context, sg_id, nsgroup_id, section_id):
nsx_id=nsgroup_id))
def get_sg_mappings(session, sg_id):
nsgroup_mapping = session.query(
def get_sg_mappings(session, sg_id, moref=False):
nsgroup_mappings = session.query(
nsx_models.NeutronNsxSecurityGroupMapping
).filter_by(neutron_id=sg_id).one()
section_mapping = session.query(
).filter_by(neutron_id=sg_id).all()
nsgroup_mapping = section_mapping = None
for mapping in nsgroup_mappings:
if moref and not uuidutils.is_uuid_like(mapping['nsx_id']):
nsgroup_mapping = mapping['nsx_id']
break
if not moref and uuidutils.is_uuid_like(mapping['nsx_id']):
nsgroup_mapping = mapping['nsx_id']
break
section_mappings = session.query(
nsx_models.NeutronNsxFirewallSectionMapping
).filter_by(neutron_id=sg_id).one()
return nsgroup_mapping.nsx_id, section_mapping.nsx_id
).filter_by(neutron_id=sg_id).all()
for mapping in section_mappings:
if moref and not uuidutils.is_uuid_like(mapping['nsx_id']):
section_mapping = mapping['nsx_id']
break
if not moref and uuidutils.is_uuid_like(mapping['nsx_id']):
section_mapping = mapping['nsx_id']
break
return nsgroup_mapping, section_mapping
def get_sg_rule_mapping(session, rule_id):
@ -652,3 +672,23 @@ def get_nsx_lbaas_l7policy_binding(session, l7policy_id):
def delete_nsx_lbaas_l7policy_binding(session, l7policy_id):
return (session.query(nsx_models.NsxLbaasL7Policy).
filter_by(l7policy_id=l7policy_id).delete())
def add_project_plugin_mapping(session, project, plugin):
with session.begin(subtransactions=True):
binding = nsx_models.NsxProjectPluginMapping(
project=project, plugin=plugin)
session.add(binding)
return binding
def get_project_plugin_mapping(session, project):
try:
return session.query(nsx_models.NsxProjectPluginMapping).filter_by(
project=project).one()
except exc.NoResultFound:
return
def get_project_plugin_mappings(session):
return session.query(nsx_models.NsxProjectPluginMapping).all()

View File

@ -1 +1 @@
ea7a72ab9643
9799427fc0e1

View File

@ -0,0 +1,41 @@
# Copyright 2017 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.
"""nsx map project to plugin
Revision ID: 9799427fc0e1
Revises: ea7a72ab9643
Create Date: 2017-06-12 16:59:48.021909
"""
# revision identifiers, used by Alembic.
revision = '9799427fc0e1'
down_revision = 'ea7a72ab9643'
from alembic import op
import sqlalchemy as sa
plugin_type_enum = sa.Enum('dvs', 'nsx-v', 'nsx-t',
name='nsx_plugin_type')
def upgrade():
op.create_table(
'nsx_project_plugin_mappings',
sa.Column('project', sa.String(36), nullable=False),
sa.Column('plugin', plugin_type_enum, nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.PrimaryKeyConstraint('project'))

View File

@ -481,3 +481,10 @@ class NsxLbaasL7Policy(model_base.BASEV2, models.TimestampMixin):
primary_key=True)
lb_rule_id = sa.Column(sa.String(36), nullable=False)
lb_vs_id = sa.Column(sa.String(36), nullable=False)
class NsxProjectPluginMapping(model_base.BASEV2, models.TimestampMixin):
"""Stores the mapping between the neutron plugin and the project id"""
__tablename__ = 'nsx_project_plugin_mappings'
project = sa.Column(sa.String(36), primary_key=True)
plugin = sa.Column(sa.Enum('dvs', 'nsx-v', 'nsx-t'), nullable=False)

View File

@ -0,0 +1,133 @@
# Copyright 2017 VMware. 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 abc
from neutron.api.v2 import resource_helper
from neutron_lib.api import extensions
from neutron_lib.db import constants as db_const
from neutron_lib import exceptions as nexception
from vmware_nsx._i18n import _
PROJECT_PLUGIN_RESOURCE_NAME = "project_plugin_map"
# Use dash for alias and collection name
EXT_ALIAS = PROJECT_PLUGIN_RESOURCE_NAME.replace('_', '-')
PROJECT_PLUGINS = "project_plugin_maps"
class NsxPlugins(object):
NSX_V = 'nsx-v'
NSX_T = 'nsx-t'
DVS = 'dvs'
VALID_TYPES = [NsxPlugins.NSX_V,
NsxPlugins.NSX_T,
NsxPlugins.DVS]
RESOURCE_ATTRIBUTE_MAP = {
PROJECT_PLUGINS: {
'id': {
'allow_post': False, 'allow_put': False,
'validate': {'type:uuid': None},
'is_visible': True},
# project is the id of the project mapped by this entry
'project': {
'allow_post': True, 'allow_put': False, 'is_visible': True},
'plugin': {
'allow_post': True, 'allow_put': False, 'is_visible': True,
'validate': {'type:values': VALID_TYPES}},
# tenant id is the id of tenant/project owning this entry
'tenant_id': {'allow_post': True, 'allow_put': False,
'validate': {
'type:string': db_const.PROJECT_ID_FIELD_SIZE},
'required_by_policy': True,
'is_visible': True},
}
}
class Projectpluginmap(extensions.ExtensionDescriptor):
@classmethod
def get_name(cls):
return "Project Plugin Mapping"
@classmethod
def get_alias(cls):
return EXT_ALIAS
@classmethod
def get_description(cls):
return "Per Project Core Plugin."
@classmethod
def get_updated(cls):
return "2017-12-05T00:00:00-00:00"
@classmethod
def get_resources(cls):
"""Returns Ext Resources."""
plural_mappings = resource_helper.build_plural_mappings(
{}, RESOURCE_ATTRIBUTE_MAP)
member_actions = {}
return resource_helper.build_resource_info(plural_mappings,
RESOURCE_ATTRIBUTE_MAP,
None,
action_map=member_actions,
register_quota=True,
translate_name=True)
def get_extended_resources(self, version):
if version == "2.0":
return RESOURCE_ATTRIBUTE_MAP
else:
return {}
class ProjectPluginReadOnly(nexception.NotAuthorized):
message = _("Project Plugin map entries cannot be modified.")
class ProjectPluginAlreadyExists(nexception.Conflict):
message = _("Project Plugin map already exists for project "
"%(project_id)s.")
class ProjectPluginMapPluginBase(object):
@abc.abstractmethod
def create_project_plugin_map(self, context, project_plugin_map):
pass
@abc.abstractmethod
def update_project_plugin_map(self, context, id, project_plugin_map):
raise ProjectPluginReadOnly()
@abc.abstractmethod
def get_project_plugin_map(self, context, id, fields=None):
pass
@abc.abstractmethod
def delete_project_plugin_map(self, context, id):
# TODO(asarfaty): delete when the project is deleted?
raise ProjectPluginReadOnly()
@abc.abstractmethod
def get_project_plugin_maps(self, context, filters=None, fields=None,
sorts=None, limit=None, marker=None,
page_reverse=False):
pass

View File

@ -0,0 +1,131 @@
# 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.
#
"""Project Plugin mapping action implementations"""
from openstack.network import network_service
from openstack import resource2 as resource
from openstackclient.i18n import _
from osc_lib.command import command
from osc_lib import utils
project_plugin_maps_path = "/project-plugin-maps"
class ProjectPluginMap(resource.Resource):
resource_key = 'project_plugin_map'
resources_key = 'project_plugin_maps'
base_path = '/project-plugin-maps'
service = network_service.NetworkService()
# capabilities
allow_create = True
allow_get = True
allow_update = False
allow_delete = False
allow_list = True
_query_mapping = resource.QueryParameters(
'plugin', 'project', 'tenant_id')
# Properties
id = resource.Body('id')
project = resource.Body('project')
plugin = resource.Body('plugin')
tenant_id = resource.Body('tenant_id')
def _get_columns(item):
columns = ['project', 'plugin']
return columns, columns
def _get_attrs(parsed_args):
attrs = {}
if parsed_args.project is not None:
attrs['project'] = parsed_args.project
if parsed_args.plugin is not None:
attrs['plugin'] = parsed_args.plugin
return attrs
class CreateProjectPluginMap(command.ShowOne):
_description = _("Create project plugin map")
def get_parser(self, prog_name):
parser = super(CreateProjectPluginMap, self).get_parser(prog_name)
parser.add_argument(
'project',
metavar="<project>",
help=_("project")
)
parser.add_argument(
'--plugin',
metavar="<plugin>",
required=True,
help=_('Plugin.)')
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.network
attrs = _get_attrs(parsed_args)
obj = client._create(ProjectPluginMap, **attrs)
display_columns, columns = _get_columns(obj)
data = utils.get_item_properties(obj, columns, formatters={})
return (display_columns, data)
class ListProjectPluginMap(command.Lister):
_description = _("List project plugin mappings")
def take_action(self, parsed_args):
client = self.app.client_manager.network
columns = (
'project',
'plugin'
)
column_headers = (
'Project ID',
'Plugin',
)
client = self.app.client_manager.network
data = client._list(ProjectPluginMap)
return (column_headers,
(utils.get_item_properties(
s, columns,
) for s in data))
class ShowProjectPluginMap(command.ShowOne):
_description = _("Display project plugins mapping")
def get_parser(self, prog_name):
parser = super(ShowProjectPluginMap, self).get_parser(prog_name)
parser.add_argument(
'id',
metavar='<id>',
help=_('id')
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.network
obj = client._get(ProjectPluginMap, parsed_args.id)
display_columns, columns = _get_columns(obj)
data = utils.get_item_properties(obj, columns)
return display_columns, data

View File

@ -21,6 +21,7 @@
from neutron.db.models import securitygroup # noqa
from vmware_nsx.plugins.dvs import plugin as dvs
from vmware_nsx.plugins.nsx import plugin as nsx
from vmware_nsx.plugins.nsx_mh import plugin as nsx_mh
from vmware_nsx.plugins.nsx_v import plugin as nsx_v
from vmware_nsx.plugins.nsx_v3 import plugin as nsx_v3
@ -29,3 +30,4 @@ NsxDvsPlugin = dvs.NsxDvsV2
NsxPlugin = nsx_mh.NsxPluginV2
NsxVPlugin = nsx_v.NsxVPluginV2
NsxV3Plugin = nsx_v3.NsxV3Plugin
NsxTVDPlugin = nsx.NsxTVDPlugin

View File

@ -47,6 +47,10 @@ class NsxPluginBase(db_base_plugin_v2.NeutronDbPluginV2,
address_scope_db.AddressScopeDbMixin):
"""Common methods for NSX-V and NSX-V3 plugins"""
@property
def plugin_type(self):
return "Unknown"
@staticmethod
@resource_extend.extends([net_def.COLLECTION_NAME])
def _ext_extend_network_dict(result, netdb):

View File

@ -61,6 +61,7 @@ from vmware_nsx.db import nsxv_db
from vmware_nsx.dhcp_meta import modes as dhcpmeta_modes
from vmware_nsx.dvs import dvs
from vmware_nsx.dvs import dvs_utils
from vmware_nsx.extensions import projectpluginmap
from vmware_nsx.plugins.common import plugin as nsx_plugin_common
LOG = logging.getLogger(__name__)
@ -115,6 +116,14 @@ class NsxDvsV2(addr_pair_db.AllowedAddressPairsMixin,
self._dvs = dvs.SingleDvsManager()
self.setup_dhcpmeta_access()
@staticmethod
def plugin_type():
return projectpluginmap.NsxPlugins.DVS
@staticmethod
def is_tvd_plugin():
return False
@staticmethod
@resource_extend.extends([port_def.COLLECTION_NAME])
def _extend_port_dict_binding(result, portdb):

View File

View File

@ -0,0 +1,574 @@
# Copyright 2014 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 neutron_lib.api.definitions import network as net_def
from neutron_lib.api.definitions import port as port_def
from neutron_lib.api.definitions import subnet as subnet_def
from neutron_lib import context as n_context
from neutron_lib.plugins import directory
from oslo_log import log as logging
from neutron.db import _resource_extend as resource_extend
from neutron.db import _utils as db_utils
from neutron.db import agents_db
from neutron.db import allowedaddresspairs_db as addr_pair_db
from neutron.db import api as db_api
from neutron.db.availability_zone import router as router_az_db
from neutron.db import external_net_db
from neutron.db import extradhcpopt_db
from neutron.db import extraroute_db
from neutron.db import l3_gwmode_db
from neutron.db.models import l3 as l3_db_models
from neutron.db.models import securitygroup as securitygroup_model # noqa
from neutron.db import models_v2
from neutron.db import portsecurity_db
from neutron.db import quota_db # noqa
from neutron.db import securitygroups_db
from neutron.quota import resource_registry
from neutron_lib.api import validators
from neutron_lib import exceptions as n_exc
from vmware_nsx.common import availability_zones as nsx_com_az
from vmware_nsx.common import config # noqa
from vmware_nsx.common import exceptions as nsx_exc
from vmware_nsx.common import managers as nsx_managers
from vmware_nsx.db import (
routertype as rt_rtr)
from vmware_nsx.db import db as nsx_db
from vmware_nsx.db import nsx_portbindings_db as pbin_db
from vmware_nsx.extensions import projectpluginmap
from vmware_nsx.plugins.common import plugin as nsx_plugin_common
from vmware_nsx.plugins.dvs import plugin as dvs
from vmware_nsx.plugins.nsx_v import plugin as v
from vmware_nsx.plugins.nsx_v3 import plugin as t
LOG = logging.getLogger(__name__)
TVD_PLUGIN_TYPE = "Nsx-TVD"
@resource_extend.has_resource_extenders
class NsxTVDPlugin(addr_pair_db.AllowedAddressPairsMixin,
agents_db.AgentDbMixin,
nsx_plugin_common.NsxPluginBase,
rt_rtr.RouterType_mixin,
external_net_db.External_net_db_mixin,
extraroute_db.ExtraRoute_db_mixin,
extradhcpopt_db.ExtraDhcpOptMixin,
router_az_db.RouterAvailabilityZoneMixin,
l3_gwmode_db.L3_NAT_db_mixin,
pbin_db.NsxPortBindingMixin,
portsecurity_db.PortSecurityDbMixin,
securitygroups_db.SecurityGroupDbMixin,
nsx_com_az.NSXAvailabilityZonesPluginCommon,
projectpluginmap.ProjectPluginMapPluginBase):
supported_extension_aliases = ['project-plugin-map']
__native_bulk_support = False
__native_pagination_support = True
__native_sorting_support = True
@resource_registry.tracked_resources(
network=models_v2.Network,
port=models_v2.Port,
subnet=models_v2.Subnet,
subnetpool=models_v2.SubnetPool,
security_group=securitygroup_model.SecurityGroup,
security_group_rule=securitygroup_model.SecurityGroupRule,
router=l3_db_models.Router,
floatingip=l3_db_models.FloatingIP)
def __init__(self):
self._extension_manager = nsx_managers.ExtensionManager()
LOG.info("Start NSX TVD Plugin")
LOG.info("This plugin is experimental!")
# Validate configuration
config.validate_nsx_config_options()
super(NsxTVDPlugin, self).__init__()
# init the different supported plugins
self.init_plugins()
# init the extensions supported by any of the plugins
self.init_extensions()
@staticmethod
def plugin_type():
return TVD_PLUGIN_TYPE
@staticmethod
def is_tvd_plugin():
return True
def init_plugins(self):
# initialize all supported plugins
self.plugins = {}
try:
self.plugins[projectpluginmap.NsxPlugins.NSX_T] = t.NsxV3Plugin()
except Exception as e:
LOG.info("NSX-T plugin will not be supported: %s", e)
try:
self.plugins[projectpluginmap.NsxPlugins.NSX_V] = v.NsxVPluginV2()
except Exception as e:
LOG.info("NSX-V plugin will not be supported: %s", e)
try:
self.plugins[projectpluginmap.NsxPlugins.DVS] = dvs.NsxDvsV2()
except Exception as e:
LOG.info("DVS plugin will not be supported: %s", e)
if not len(self.plugins):
msg = _("No active plugins were found")
raise nsx_exc.NsxPluginException(err_msg=msg)
# update the default plugin for new projects
# TODO(asarfaty): make the default configurable?
if projectpluginmap.NsxPlugins.NSX_T in self.plugins:
self.default_plugin = projectpluginmap.NsxPlugins.NSX_T
else:
self.default_plugin = self.plugins[0].key
LOG.info("NSX-TVD plugin will use %s as the default plugin",
self.default_plugin)
def get_plugin_by_type(self, plugin_type):
return self.plugins.get(plugin_type)
def init_extensions(self):
# Support all the extensions supported by any of the plugins
extensions = []
for plugin in self.plugins:
extensions.extend(self.plugins[plugin].supported_extension_aliases)
self.supported_extension_aliases = list(set(extensions))
# mark extensions which are supported by only one of the plugins
self._unsupported_fields = {}
for plugin in self.plugins:
# TODO(asarfaty): add other resources here
plugin_type = self.plugins[plugin].plugin_type()
self._unsupported_fields[plugin_type] = {'router': []}
# router size and type are supported only by the V plugin
self._unsupported_fields[t.NsxV3Plugin.plugin_type()]['router'] = [
'router_size', 'router_type']
def _validate_obj_extensions(self, data, plugin_type, obj_type):
"""prevent configuration of unsupported extensions"""
for field in self._unsupported_fields[plugin_type][obj_type]:
if validators.is_attr_set(data.get(field)):
err_msg = (_('Can not support %(field)s extension for '
'%(obj_type)s %(p)s plugin') % {
'field': field,
'obj_type': obj_type,
'p': plugin_type})
raise n_exc.InvalidInput(error_message=err_msg)
def _cleanup_obj_fields(self, data, plugin_type, obj_type):
"""Remove data of unsupported extensions"""
for field in self._unsupported_fields[plugin_type][obj_type]:
if field in data:
del data[field]
def _list_availability_zones(self, context, filters=None):
p = self._get_plugin_from_project(context, context.project_id)
return p._list_availability_zones(context, filters=filters)
def validate_availability_zones(self, context, resource_type,
availability_zones):
p = self._get_plugin_from_project(context, context.project_id)
return p.validate_availability_zones(context, resource_type,
availability_zones)
def _get_plugin_from_net_id(self, context, net_id):
# get the network using the super plugin - here we use the
# _get_network (so as not to call the make dict method)
network = super(NsxTVDPlugin, self)._get_network(context, net_id)
return self._get_plugin_from_project(context, network['tenant_id'])
def get_network_availability_zones(self, net_db):
ctx = n_context.get_admin_context()
p = self._get_plugin_from_project(ctx, net_db['tenant_id'])
return p.get_network_availability_zones(net_db)
def create_network(self, context, network):
net_data = network['network']
tenant_id = net_data['tenant_id']
self._ensure_default_security_group(context, tenant_id)
p = self._get_plugin_from_project(context, tenant_id)
return p.create_network(context, network)
def delete_network(self, context, id):
p = self._get_plugin_from_net_id(context, id)
p.delete_network(context, id)
def get_network(self, context, id, fields=None):
p = self._get_plugin_from_net_id(context, id)
return p.get_network(context, id, fields=fields)
def get_networks(self, context, filters=None, fields=None,
sorts=None, limit=None, marker=None,
page_reverse=False):
filters = filters or {}
with db_api.context_manager.reader.using(context):
networks = (
super(NsxTVDPlugin, self).get_networks(
context, filters, fields, sorts,
limit, marker, page_reverse))
for net in networks:
p = self._get_plugin_from_project(context, net['tenant_id'])
p._extend_get_network_dict_provider(context, net)
return (networks if not fields else
[db_utils.resource_fields(network,
fields) for network in networks])
def update_network(self, context, id, network):
p = self._get_plugin_from_net_id(context, id)
return p.update_network(context, id, network)
def create_port(self, context, port):
id = port['port']['network_id']
p = self._get_plugin_from_net_id(context, id)
return p.create_port(context, port)
def update_port(self, context, id, port):
db_port = self._get_port(context, id)
p = self._get_plugin_from_net_id(context, db_port['network_id'])
return p.update_port(context, id, port)
def delete_port(self, context, id, **kwargs):
db_port = self._get_port(context, id)
p = self._get_plugin_from_net_id(context, db_port['network_id'])
p.delete_port(context, id, **kwargs)
def get_port(self, context, id, fields=None):
db_port = self._get_port(context, id)
p = self._get_plugin_from_net_id(context, db_port['network_id'])
return p.get_port(context, id, fields=fields)
def get_ports(self, context, filters=None, fields=None,
sorts=None, limit=None, marker=None,
page_reverse=False):
filters = filters or {}
with db_api.context_manager.reader.using(context):
ports = (
super(NsxTVDPlugin, self).get_ports(
context, filters, fields, sorts,
limit, marker, page_reverse))
# Add port extensions
for port in ports:
if 'id' in port:
port_model = self._get_port(context, port['id'])
resource_extend.apply_funcs('ports', port, port_model)
p = self._get_plugin_from_net_id(context, port['network_id'])
p._extend_get_port_dict_qos_and_binding(context, port)
p._remove_provider_security_groups_from_list(port)
return (ports if not fields else
[db_utils.resource_fields(port, fields) for port in ports])
def get_subnet(self, context, id, fields=None):
db_subnet = self._get_subnet(context, id)
p = self._get_plugin_from_net_id(context, db_subnet['network_id'])
return p.get_subnet(context, id, fields=fields)
def get_subnets(self, context, filters=None, fields=None, sorts=None,
limit=None, marker=None, page_reverse=False):
# The subnets is tricky as the metadata requests make use of the
# get subnet. So there are two use cases here:
# 1. that the metadata request returns a value
# 2. that this is a general subnet query.
# If none found then we return default plugin subnets
default_plugin_subnets = []
for plugin in self.plugins.values():
subnets = plugin.get_subnets(context, filters=filters,
fields=fields, sorts=sorts,
limit=limit, marker=marker,
page_reverse=page_reverse)
if subnets:
return subnets
if self.plugins[self.default_plugin] == plugin:
default_plugin_subnets = subnets
return default_plugin_subnets
def delete_subnet(self, context, id):
db_subnet = self._get_subnet(context, id)
p = self._get_plugin_from_net_id(context, db_subnet['network_id'])
p.delete_subnet(context, id)
def create_subnet(self, context, subnet):
id = subnet['subnet']['network_id']
p = self._get_plugin_from_net_id(context, id)
return p.create_subnet(context, subnet)
def update_subnet(self, context, id, subnet):
db_subnet = self._get_subnet(context, id)
p = self._get_plugin_from_net_id(context, db_subnet['network_id'])
return p.update_subnet(context, id, subnet)
def get_router_availability_zones(self, router):
ctx = n_context.get_admin_context()
p = self._get_plugin_from_project(ctx, router['tenant_id'])
return p.get_router_availability_zones(router)
def _validate_router_gw_plugin(self, context, router_plugin,
gw_info):
if gw_info and gw_info.get('network_id'):
net_plugin = self._get_plugin_from_net_id(
context, gw_info['network_id'])
if net_plugin.plugin_type() != router_plugin.plugin_type():
err_msg = (_('Router gateway should belong to the %s plugin '
'as the router') % router_plugin.plugin_type())
raise n_exc.InvalidInput(error_message=err_msg)
def _validate_router_interface_plugin(self, context, router_plugin,
interface_info):
is_port, is_sub = self._validate_interface_info(interface_info)
if is_port:
net_id = self.get_port(
context, interface_info['port_id'])['network_id']
elif is_sub:
net_id = self.get_subnet(
context, interface_info['subnet_id'])['network_id']
net_plugin = self._get_plugin_from_net_id(context, net_id)
if net_plugin.plugin_type() != router_plugin.plugin_type():
err_msg = (_('Router interface should belong to the %s plugin '
'as the router') % router_plugin.plugin_type())
raise n_exc.InvalidInput(error_message=err_msg)
def _get_plugin_from_router_id(self, context, router_id):
# get the router using the super plugin - here we use the
# _get_router (so as not to call the make dict method)
router = super(NsxTVDPlugin, self)._get_router(context, router_id)
return self._get_plugin_from_project(context, router['tenant_id'])
def create_router(self, context, router):
tenant_id = router['router']['tenant_id']
self._ensure_default_security_group(context, tenant_id)
p = self._get_plugin_from_project(context, tenant_id)
self._validate_router_gw_plugin(context, p, router['router'].get(
'external_gateway_info'))
self._validate_obj_extensions(
router['router'], p.plugin_type(), 'router')
new_router = p.create_router(context, router)
self._cleanup_obj_fields(
router['router'], p.plugin_type(), 'router')
return new_router
def update_router(self, context, router_id, router):
p = self._get_plugin_from_router_id(context, router_id)
self._validate_router_gw_plugin(context, p, router['router'].get(
'external_gateway_info'))
self._validate_obj_extensions(
router['router'], p.plugin_type(), 'router')
return p.update_router(context, router_id, router)
def get_router(self, context, id, fields=None):
p = self._get_plugin_from_router_id(context, id)
router = p.get_router(context, id, fields=fields)
self._cleanup_obj_fields(router, p.plugin_type(), 'router')
return router
def add_router_interface(self, context, router_id, interface_info):
p = self._get_plugin_from_router_id(context, router_id)
self._validate_router_interface_plugin(context, p, interface_info)
return p.add_router_interface(context, router_id, interface_info)
def remove_router_interface(self, context, router_id, interface_info):
p = self._get_plugin_from_router_id(context, router_id)
return p.remove_router_interface(context, router_id, interface_info)
def _validate_fip_router_plugin(self, context, fip_plugin, fip_data):
if 'router_id' in fip_data:
router_plugin = self._get_plugin_from_router_id(
context, fip_data['router_id'])
if router_plugin.plugin_type() != fip_plugin.plugin_type():
err_msg = (_('Floatingip router should belong to the %s '
'plugin as the floatingip') %
fip_plugin.plugin_type())
raise n_exc.InvalidInput(error_message=err_msg)
def create_floatingip(self, context, floatingip):
net_id = floatingip['floatingip']['floating_network_id']
p = self._get_plugin_from_net_id(context, net_id)
self._validate_fip_router_plugin(context, p, floatingip['floatingip'])
return p.create_floatingip(context, floatingip)
def update_floatingip(self, context, id, floatingip):
fip = self._get_floatingip(context, id)
net_id = fip['floating_network_id']
p = self._get_plugin_from_net_id(context, net_id)
self._validate_fip_router_plugin(context, p, floatingip['floatingip'])
return p.update_floatingip(context, id, floatingip)
def delete_floatingip(self, context, id):
fip = self._get_floatingip(context, id)
net_id = fip['floating_network_id']
p = self._get_plugin_from_net_id(context, net_id)
return p.delete_floatingip(context, id)
def disassociate_floatingips(self, context, port_id):
db_port = self._get_port(context, port_id)
p = self._get_plugin_from_net_id(context, db_port['network_id'])
return p.disassociate_floatingips(context, port_id)
def _get_plugin_from_sg_id(self, context, sg_id):
# get the router using the super plugin - here we use the
# _get_router (so as not to call the make dict method)
sg = super(NsxTVDPlugin, self)._get_security_group(context, sg_id)
return self._get_plugin_from_project(context, sg['tenant_id'])
# TODO(asarfaty): no need to create on both any more?
def create_security_group(self, context, security_group,
default_sg=False):
if not default_sg:
secgroup = security_group['security_group']
tenant_id = secgroup['tenant_id']
self._ensure_default_security_group(context, tenant_id)
p = self._get_plugin_from_project(context, context.project_id)
return p.create_security_group(context, security_group,
default_sg=default_sg)
def delete_security_group(self, context, id):
p = self._get_plugin_from_sg_id(context, id)
p.delete_security_group(context, id)
# self._v.delete_security_group(context, id, delete_base=False)
# self._t.delete_security_group(context, id)
def update_security_group(self, context, id, security_group):
p = self._get_plugin_from_sg_id(context, id)
return p.update_security_group(context, id, security_group)
#self._t.update_security_group(context, id, security_group)
#return self._v.update_security_group(context, id, security_group)
def create_security_group_rule_bulk(self, context, security_group_rules):
p = self._get_plugin_from_project(context, context.project_id)
return p.create_security_group_rule_bulk(context,
security_group_rules)
# sg_rules = security_group_rules['security_group_rules']
# for r in sg_rules:
# r['security_group_rule']['id'] = (
# r['security_group_rule'].get('id') or
# uuidutils.generate_uuid())
# sgs = self._t.create_security_group_rule_bulk(context,
# security_group_rules)
# self._v.create_security_group_rule_bulk(context,
# security_group_rules,
# base_create=False)
# return sgs
def create_security_group_rule(self, context, security_group_rule):
p = self._get_plugin_from_project(context, context.project_id)
return p.create_security_group_rule(context, security_group_rule)
# security_group_rule['security_group_rule']['id'] = (
# security_group_rule['security_group_rule'].get('id') or
# uuidutils.generate_uuid())
# sg = self._t.create_security_group_rule(context, security_group_rule)
# self._v.create_security_group_rule(context, security_group_rule,
# create_base=False)
# return sg
def delete_security_group_rule(self, context, id):
p = self._get_plugin_from_sg_id(context, id)
p.delete_security_group_rule(context, id)
# self._v.delete_security_group_rule(context, id, delete_base=False)
# self._t.delete_security_group_rule(context, id)
@staticmethod
@resource_extend.extends([net_def.COLLECTION_NAME])
def _ext_extend_network_dict(result, netdb):
ctx = n_context.get_admin_context()
# get the core plugin as this is a static method with no 'self'
plugin = directory.get_plugin()
p = plugin._get_plugin_from_project(ctx, netdb['tenant_id'])
with db_api.context_manager.writer.using(ctx):
p._extension_manager.extend_network_dict(
ctx.session, netdb, result)
@staticmethod
@resource_extend.extends([port_def.COLLECTION_NAME])
def _ext_extend_port_dict(result, portdb):
ctx = n_context.get_admin_context()
# get the core plugin as this is a static method with no 'self'
plugin = directory.get_plugin()
p = plugin._get_plugin_from_project(ctx, portdb['tenant_id'])
with db_api.context_manager.writer.using(ctx):
p._extension_manager.extend_port_dict(
ctx.session, portdb, result)
@staticmethod
@resource_extend.extends([subnet_def.COLLECTION_NAME])
def _ext_extend_subnet_dict(result, subnetdb):
ctx = n_context.get_admin_context()
# get the core plugin as this is a static method with no 'self'
plugin = directory.get_plugin()
p = plugin._get_plugin_from_project(ctx, subnetdb['tenant_id'])
with db_api.context_manager.writer.using(ctx):
p._extension_manager.extend_subnet_dict(
ctx.session, subnetdb, result)
def _get_project_plugin_dict(self, data):
return {'id': data['project'],
'project': data['project'],
'plugin': data['plugin'],
'tenant_id': data['project']}
def create_project_plugin_map(self, context, project_plugin_map):
# TODO(asarfaty): Validate project id exists
data = project_plugin_map['project_plugin_map']
if nsx_db.get_project_plugin_mapping(
context.session, data['project']):
raise projectpluginmap.ProjectPluginAlreadyExists(
project_id=data['project'])
nsx_db.add_project_plugin_mapping(context.session,
data['project'],
data['plugin'])
return self._get_project_plugin_dict(data)
def get_project_plugin_map(self, context, id, fields=None):
data = nsx_db.get_project_plugin_mapping(context.session, id)
if data:
return self._get_project_plugin_dict(data)
else:
raise n_exc.ObjectNotFound(id=id)
def get_project_plugin_maps(self, context, filters=None, fields=None,
sorts=None, limit=None, marker=None,
page_reverse=False):
# TODO(asarfaty) filter the results
mappings = nsx_db.get_project_plugin_mappings(context.session)
return [self._get_project_plugin_dict(data) for data in mappings]
def _get_plugin_from_project(self, context, project_id):
"""Get the correct plugin for this project.
Look for the project in the DB.
If not there - add an entry with the default plugin
"""
plugin_type = self.default_plugin
mapping = nsx_db.get_project_plugin_mapping(
context.session, project_id)
if mapping:
plugin_type = mapping['plugin']
else:
self.create_project_plugin_map(context,
{'project_plugin_map': {'plugin': plugin_type,
'project': project_id}})
if not self.plugins.get(plugin_type):
msg = (_("Cannot use unsupported plugin %(plugin)s for project "
"%(project)s") % {'plugin': plugin_type,
'project': project_id})
raise nsx_exc.NsxPluginException(err_msg=msg)
LOG.debug("Using %s plugin", plugin_type)
return self.plugins[plugin_type]

View File

@ -0,0 +1,24 @@
# Copyright 2014 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
def is_tvd_core_plugin():
core_plugin = cfg.CONF.core_plugin
if (core_plugin.endswith('NsxTVDPlugin') or
core_plugin.endswith('vmware_nsxtvd')):
return True
return False

View File

@ -204,10 +204,11 @@ class NsxVAvailabilityZone(common_az.ConfiguredAvailabilityZone):
class NsxVAvailabilityZones(common_az.ConfiguredAvailabilityZones):
def __init__(self):
def __init__(self, validate_default=False):
super(NsxVAvailabilityZones, self).__init__(
cfg.CONF.nsxv.availability_zones,
NsxVAvailabilityZone)
NsxVAvailabilityZone,
validate_default=validate_default)
def get_inventory(self):
"""Return a set of relevant resources in all the availability zones

View File

@ -120,12 +120,14 @@ from vmware_nsx.extensions import dhcp_mtu as ext_dhcp_mtu
from vmware_nsx.extensions import dns_search_domain as ext_dns_search_domain
from vmware_nsx.extensions import maclearning as mac_ext
from vmware_nsx.extensions import nsxpolicy
from vmware_nsx.extensions import projectpluginmap
from vmware_nsx.extensions import providersecuritygroup as provider_sg
from vmware_nsx.extensions import routersize
from vmware_nsx.extensions import secgroup_rule_local_ip_prefix
from vmware_nsx.extensions import securitygrouplogging as sg_logging
from vmware_nsx.extensions import securitygrouppolicy as sg_policy
from vmware_nsx.plugins.common import plugin as nsx_plugin_common
from vmware_nsx.plugins.nsx import utils as tvd_utils
from vmware_nsx.plugins.nsx_v import availability_zones as nsx_az
from vmware_nsx.plugins.nsx_v import managers
from vmware_nsx.plugins.nsx_v import md_proxy as nsx_v_md_proxy
@ -217,8 +219,14 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
router=l3_db_models.Router,
floatingip=l3_db_models.FloatingIP)
def __init__(self):
self._extension_manager = nsx_managers.ExtensionManager()
self._is_sub_plugin = tvd_utils.is_tvd_core_plugin()
super(NsxVPluginV2, self).__init__()
if self._is_sub_plugin:
extension_drivers = cfg.CONF.nsx_tvd.nsx_v_extension_drivers
else:
extension_drivers = cfg.CONF.nsx_extension_drivers
self._extension_manager = nsx_managers.ExtensionManager(
extension_drivers=extension_drivers)
# Bind the dummy L3 notifications
self.l3_rpc_notifier = l3_rpc_agent_api.L3NotifyAPI()
self.init_is_complete = False
@ -311,6 +319,14 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
# Bind QoS notifications
qos_driver.register(self)
@staticmethod
def plugin_type():
return projectpluginmap.NsxPlugins.NSX_V
@staticmethod
def is_tvd_plugin():
return False
def init_complete(self, resource, event, trigger, payload=None):
with locking.LockManager.get_lock('plugin-init-complete'):
if self.init_is_complete:
@ -935,7 +951,8 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
if vnic_id is None or added_sgids is None:
return
for add_sg in added_sgids:
nsx_sg_id = nsx_db.get_nsx_security_group_id(session, add_sg)
nsx_sg_id = nsx_db.get_nsx_security_group_id(session, add_sg,
moref=True)
if nsx_sg_id is None:
LOG.warning("NSX security group not found for %s", add_sg)
else:
@ -958,7 +975,8 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
return
# Remove vnic from delete security groups binding
for del_sg in deleted_sgids:
nsx_sg_id = nsx_db.get_nsx_security_group_id(session, del_sg)
nsx_sg_id = nsx_db.get_nsx_security_group_id(session, del_sg,
moref=True)
if nsx_sg_id is None:
LOG.warning("NSX security group not found for %s", del_sg)
else:
@ -994,7 +1012,9 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
return '%s.%03d' % (device_id, port_index)
def init_availability_zones(self):
self._availability_zones_data = nsx_az.NsxVAvailabilityZones()
validate_default = not self._is_sub_plugin
self._availability_zones_data = nsx_az.NsxVAvailabilityZones(
validate_default=validate_default)
def _list_availability_zones(self, context, filters=None):
#TODO(asarfaty): We may need to use the filters arg, but now it
@ -1010,13 +1030,19 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
obj_data):
if az_def.AZ_HINTS in obj_data:
self.validate_availability_zones(context, resource_type,
obj_data[az_def.AZ_HINTS])
obj_data[az_def.AZ_HINTS],
force=True)
def validate_availability_zones(self, context, resource_type,
availability_zones):
availability_zones, force=False):
"""Verify that the availability zones exist, and only 1 hint
was set.
"""
# This method is called directly from this plugin but also from
# registered callbacks
if self._is_sub_plugin and not force:
# validation should be done together for both plugins
return
return self.validate_obj_azs(availability_zones)
def _prepare_spoofguard_policy(self, network_type, net_data, net_morefs):
@ -4137,7 +4163,8 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
def update_security_group(self, context, id, security_group):
s = security_group['security_group']
self._validate_security_group(context, s, False, id=id)
nsx_sg_id = nsx_db.get_nsx_security_group_id(context.session, id)
nsx_sg_id = nsx_db.get_nsx_security_group_id(context.session, id,
moref=True)
section_uri = self._get_section_uri(context.session, id)
section_needs_update = False
@ -4190,7 +4217,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
return sg_data
def delete_security_group(self, context, id):
def delete_security_group(self, context, id, delete_base=True):
"""Delete a security group."""
self._prevent_non_admin_delete_provider_sg(context, id)
self._prevent_non_admin_delete_policy_sg(context, id)
@ -4200,8 +4227,10 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
section_uri = self._get_section_uri(context.session, id)
# Find nsx security group
nsx_sg_id = nsx_db.get_nsx_security_group_id(context.session, id)
nsx_sg_id = nsx_db.get_nsx_security_group_id(context.session, id,
moref=True)
if delete_base:
# Delete neutron security group
super(NsxVPluginV2, self).delete_security_group(context, id)
@ -4228,7 +4257,8 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
if nsx_sg_id is None:
# Find nsx security group for neutron security group
nsx_sg_id = nsx_db.get_nsx_security_group_id(
context.session, rule['security_group_id'])
context.session, rule['security_group_id'],
moref=True)
# Find the remote nsx security group id, which might be the current
# one. In case of the default security-group, the associated
@ -4237,7 +4267,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
remote_nsx_sg_id = nsx_sg_id
else:
remote_nsx_sg_id = nsx_db.get_nsx_security_group_id(
context.session, rule['remote_group_id'])
context.session, rule['remote_group_id'], moref=True)
# Get source and destination containers from rule
if rule['direction'] == 'ingress':
@ -4283,12 +4313,15 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
tag='Project_%s' % rule['tenant_id'])
return nsx_rule
def create_security_group_rule(self, context, security_group_rule):
def create_security_group_rule(self, context, security_group_rule,
create_base=True):
"""Create a single security group rule."""
bulk_rule = {'security_group_rules': [security_group_rule]}
return self.create_security_group_rule_bulk(context, bulk_rule)[0]
return self.create_security_group_rule_bulk(
context, bulk_rule, create_base=create_base)[0]
def create_security_group_rule_bulk(self, context, security_group_rules):
def create_security_group_rule_bulk(self, context, security_group_rules,
create_base=True):
"""Create security group rules.
:param security_group_rules: list of rules to create
@ -4321,7 +4354,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
rule = r['security_group_rule']
if not self._check_local_ip_prefix(context, rule):
rule[secgroup_rule_local_ip_prefix.LOCAL_IP_PREFIX] = None
rule['id'] = uuidutils.generate_uuid()
rule['id'] = rule.get('id') or uuidutils.generate_uuid()
ruleids.add(rule['id'])
nsx_rules.append(
self._create_nsx_rule(context, rule,
@ -4340,18 +4373,23 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
# Save new rules in Database, including mappings between Nsx rules
# and Neutron security-groups rules
with db_api.context_manager.writer.using(context):
if create_base:
new_rule_list = super(
NsxVPluginV2, self).create_security_group_rule_bulk_native(
NsxVPluginV2,
self).create_security_group_rule_bulk_native(
context, security_group_rules)
for i, r in enumerate(sg_rules):
self._process_security_group_rule_properties(
context, new_rule_list[i],
r['security_group_rule'])
else:
new_rule_list = sg_rules
for pair in rule_pairs:
neutron_rule_id = pair['neutron_id']
nsx_rule_id = pair['nsx_id']
if neutron_rule_id in ruleids:
nsxv_db.add_neutron_nsx_rule_mapping(
context.session, neutron_rule_id, nsx_rule_id)
for i, r in enumerate(sg_rules):
self._process_security_group_rule_properties(
context, new_rule_list[i], r['security_group_rule'])
except Exception:
with excutils.save_and_reraise_exception():
for nsx_rule_id in [p['nsx_id'] for p in rule_pairs
@ -4363,7 +4401,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
LOG.exception("Failed to create security group rule")
return new_rule_list
def delete_security_group_rule(self, context, id):
def delete_security_group_rule(self, context, id, delete_base=True):
"""Delete a security group rule."""
rule_db = self._get_security_group_rule(context, id)
security_group_id = rule_db['security_group_id']
@ -4383,7 +4421,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
LOG.debug("Security group rule %(id)s deleted, backend "
"nsx-rule %(nsx_rule_id)s doesn't exist.",
{'id': id, 'nsx_rule_id': nsx_rule_id})
if delete_base:
securitygroup.SecurityGroupRule.delete_objects(context, id=id)
def _remove_vnic_from_spoofguard_policy(self, session, net_id, vnic_id):

View File

@ -204,10 +204,11 @@ class NsxV3AvailabilityZones(common_az.ConfiguredAvailabilityZones):
default_name = DEFAULT_NAME
def __init__(self):
def __init__(self, validate_default=False):
super(NsxV3AvailabilityZones, self).__init__(
cfg.CONF.nsx_v3.availability_zones,
NsxV3AvailabilityZone)
NsxV3AvailabilityZone,
validate_default=validate_default)
def dhcp_relay_configured(self):
for az in self.availability_zones.values():

View File

@ -99,9 +99,11 @@ from vmware_nsx.db import maclearning as mac_db
from vmware_nsx.dhcp_meta import rpc as nsx_rpc
from vmware_nsx.extensions import advancedserviceproviders as as_providers
from vmware_nsx.extensions import maclearning as mac_ext
from vmware_nsx.extensions import projectpluginmap
from vmware_nsx.extensions import providersecuritygroup as provider_sg
from vmware_nsx.extensions import securitygrouplogging as sg_logging
from vmware_nsx.plugins.common import plugin as nsx_plugin_common
from vmware_nsx.plugins.nsx import utils as tvd_utils
from vmware_nsx.plugins.nsx_v3 import availability_zones as nsx_az
from vmware_nsx.plugins.nsx_v3 import utils as v3_utils
from vmware_nsx.services.fwaas.common import utils as fwaas_utils
@ -210,9 +212,15 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
router=l3_db_models.Router,
floatingip=l3_db_models.FloatingIP)
def __init__(self):
self._is_sub_plugin = tvd_utils.is_tvd_core_plugin()
nsxlib_utils.set_is_attr_callback(validators.is_attr_set)
self._extend_fault_map()
self._extension_manager = managers.ExtensionManager()
if self._is_sub_plugin:
extension_drivers = cfg.CONF.nsx_tvd.nsx_v3_extension_drivers
else:
extension_drivers = cfg.CONF.nsx_extension_drivers
self._extension_manager = managers.ExtensionManager(
extension_drivers=extension_drivers)
super(NsxV3Plugin, self).__init__()
# Bind the dummy L3 notifications
self.l3_rpc_notifier = l3_rpc_agent_api.L3NotifyAPI()
@ -298,6 +306,14 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
# Register NSXv3 trunk driver to support trunk extensions
self.trunk_driver = trunk_driver.NsxV3TrunkDriver.create(self)
@staticmethod
def plugin_type():
return projectpluginmap.NsxPlugins.NSX_T
@staticmethod
def is_tvd_plugin():
return False
def init_complete(self, resource, event, trigger, payload=None):
with locking.LockManager.get_lock('plugin-init-complete'):
if self.init_is_complete:
@ -364,7 +380,9 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
"DHCP metadata")
LOG.error(msg)
raise n_exc.InvalidInput(error_message=msg)
self._availability_zones_data = nsx_az.NsxV3AvailabilityZones()
validate_default = not self._is_sub_plugin
self._availability_zones_data = nsx_az.NsxV3AvailabilityZones(
validate_default=validate_default)
def _init_nsx_profiles(self):
LOG.debug("Initializing NSX v3 port spoofguard switching profile")
@ -1027,8 +1045,8 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
# validate the availability zone, and get the AZ object
if az_def.AZ_HINTS in net_data:
self.validate_availability_zones(context, 'network',
net_data[az_def.AZ_HINTS])
self._validate_availability_zones_forced(
context, 'network', net_data[az_def.AZ_HINTS])
az = self.get_obj_az_by_hints(net_data)
self._ensure_default_security_group(context, tenant_id)
@ -1097,6 +1115,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
{'network': {'vlan_transparent': vlt}})
rollback_network = True
is_ddi_network = self._is_ddi_supported_on_network(
context, created_net['id'])
if (is_backend_network and
@ -3265,7 +3284,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
# validate the availability zone
if az_def.AZ_HINTS in r:
self.validate_availability_zones(context, 'router',
self._validate_availability_zones_forced(context, 'router',
r[az_def.AZ_HINTS])
gw_info = self._extract_external_gw(context, router, is_extract=True)
@ -4360,8 +4379,19 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
result[(az, 'network')] = True
return result
def validate_availability_zones(self, context, resource_type,
def _validate_availability_zones_forced(self, context, resource_type,
availability_zones):
return self.validate_availability_zones(context, resource_type,
availability_zones,
force=True)
def validate_availability_zones(self, context, resource_type,
availability_zones, force=False):
# This method is called directly from this plugin but also from
# registered callbacks
if self._is_sub_plugin and not force:
# validation should be done together for both plugins
return
# If no native_dhcp_metadata - use neutron AZs
if not cfg.CONF.nsx_v3.native_dhcp_metadata:
return super(NsxV3Plugin, self).validate_availability_zones(