b6bd7e49d1
Change-Id: I6909c10471039f1e68224679ceeb2867ab5a3a47
1032 lines
46 KiB
Python
1032 lines
46 KiB
Python
# 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.
|
|
|
|
import copy
|
|
|
|
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.callbacks import events
|
|
from neutron_lib.callbacks import registry
|
|
from neutron_lib.callbacks import resources
|
|
from neutron_lib import context as n_context
|
|
from neutron_lib.db import api as db_api
|
|
from neutron_lib.db import resource_extend
|
|
from neutron_lib.db import utils as db_utils
|
|
from neutron_lib.plugins import constants as plugin_constants
|
|
from neutron_lib.plugins import directory
|
|
from oslo_config import cfg
|
|
from oslo_log import log as logging
|
|
from oslo_utils import excutils
|
|
from oslo_utils import uuidutils
|
|
|
|
from neutron.db import agents_db
|
|
from neutron.db import agentschedulers_db
|
|
from neutron.db import allowedaddresspairs_db as addr_pair_db
|
|
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_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
|
|
from neutron.db import models_v2
|
|
from neutron.db import portsecurity_db
|
|
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
|
|
from vmware_nsx.common import exceptions as nsx_exc
|
|
from vmware_nsx.common import locking
|
|
from vmware_nsx.common import managers as nsx_managers
|
|
from vmware_nsx.common import utils as com_utils
|
|
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 advancedserviceproviders as as_providers
|
|
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
|
|
from vmware_nsx.services.lbaas.octavia import octavia_listener
|
|
from vmware_nsx.services.lbaas.octavia import tvd_wrapper as octavia_tvd
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
TVD_PLUGIN_TYPE = "Nsx-TVD"
|
|
|
|
|
|
@resource_extend.has_resource_extenders
|
|
class NsxTVDPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|
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 = [projectpluginmap.ALIAS]
|
|
|
|
__native_bulk_support = True
|
|
__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")
|
|
self.init_is_complete = False
|
|
# 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()
|
|
|
|
self._unsubscribe_callback_events()
|
|
|
|
registry.subscribe(self.spawn_complete,
|
|
resources.PROCESS,
|
|
events.AFTER_SPAWN)
|
|
|
|
registry.subscribe(self.init_complete,
|
|
resources.PROCESS,
|
|
events.AFTER_INIT)
|
|
|
|
@staticmethod
|
|
def plugin_type():
|
|
return TVD_PLUGIN_TYPE
|
|
|
|
@staticmethod
|
|
def is_tvd_plugin():
|
|
return True
|
|
|
|
@com_utils.retry_upon_exception(Exception, 0.5, 2,
|
|
cfg.CONF.nsx_tvd.init_retries)
|
|
def _call_plugin_init_with_retry(self, map_type, plugin_class):
|
|
try:
|
|
self.plugins[map_type] = plugin_class()
|
|
except Exception as e:
|
|
with excutils.save_and_reraise_exception():
|
|
LOG.warning("%s plugin failed to initialized: %s",
|
|
map_type.upper(), e)
|
|
|
|
def _init_plugin(self, map_type, plugin_class):
|
|
if map_type not in cfg.CONF.nsx_tvd.enabled_plugins:
|
|
# skip this plugin
|
|
LOG.info("%s plugin was not enabled by the configuration",
|
|
map_type.upper())
|
|
return
|
|
try:
|
|
self._call_plugin_init_with_retry(map_type, plugin_class)
|
|
except Exception as e:
|
|
LOG.warning("%s plugin will not be supported",
|
|
map_type.upper())
|
|
if map_type == self.default_plugin:
|
|
msg = (_("The default plugin %(def)s failed to start. "
|
|
"Reason: %(reason)s") % {'def': self.default_plugin,
|
|
'reason': e})
|
|
LOG.error(msg)
|
|
raise nsx_exc.NsxPluginException(err_msg=msg)
|
|
else:
|
|
LOG.info("%s plugin will be supported", map_type.upper())
|
|
|
|
def init_plugins(self):
|
|
# initialize all supported plugins
|
|
self.plugins = {}
|
|
self.as_providers = {}
|
|
# update the default plugin for new projects
|
|
self.default_plugin = cfg.CONF.nsx_tvd.default_plugin
|
|
plugins = [(projectpluginmap.NsxPlugins.NSX_T, t.NsxV3Plugin),
|
|
(projectpluginmap.NsxPlugins.NSX_V, v.NsxVPluginV2),
|
|
(projectpluginmap.NsxPlugins.DVS, dvs.NsxDvsV2)]
|
|
for (map_type, plugin_class) in plugins:
|
|
self._init_plugin(map_type, plugin_class)
|
|
if not len(self.plugins):
|
|
msg = _("No active plugins were found")
|
|
raise nsx_exc.NsxPluginException(err_msg=msg)
|
|
|
|
for k, val in self.plugins.items():
|
|
if as_providers.ALIAS in val.supported_extension_aliases:
|
|
self.as_providers[k] = val
|
|
LOG.info("NSX-TVD plugin will use %s as the default plugin",
|
|
self.default_plugin)
|
|
|
|
# validate the availability zones configuration
|
|
self.init_availability_zones()
|
|
|
|
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.extend(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': [],
|
|
'port': [],
|
|
'security_group': []}
|
|
|
|
# router size and type are supported only by the V plugin
|
|
if plugin_type in [t.NsxV3Plugin.plugin_type(),
|
|
dvs.NsxDvsV2.plugin_type()]:
|
|
self._unsupported_fields[plugin_type]['router'] = [
|
|
'router_size', 'router_type']
|
|
|
|
# port mac learning, and provider sg are not supported by
|
|
# the dvs plugin
|
|
if plugin_type in [dvs.NsxDvsV2.plugin_type()]:
|
|
self._unsupported_fields[plugin_type]['port'] = [
|
|
'mac_learning_enabled', 'provider_security_groups']
|
|
|
|
# security group policy can be supported only by nsx-v
|
|
if plugin_type in [t.NsxV3Plugin.plugin_type(),
|
|
dvs.NsxDvsV2.plugin_type()]:
|
|
self._unsupported_fields[plugin_type]['security_group'] = [
|
|
'policy']
|
|
|
|
def init_availability_zones(self):
|
|
# Make sure there are no overlaps between v/t availability zones
|
|
if (self.plugins.get(projectpluginmap.NsxPlugins.NSX_V) and
|
|
self.plugins.get(projectpluginmap.NsxPlugins.NSX_T) and
|
|
bool(set(cfg.CONF.nsxv.availability_zones) &
|
|
set(cfg.CONF.nsx_v3.availability_zones))):
|
|
msg = _("Cannot use the same availability zones in NSX-V and T")
|
|
raise nsx_exc.NsxPluginException(err_msg=msg)
|
|
|
|
def _get_octavia_objects(self, plugin_type):
|
|
plugin = self.get_plugin_by_type(plugin_type)
|
|
if plugin:
|
|
return plugin._get_octavia_objects()
|
|
return {'loadbalancer': None, 'listener': None, 'pool': None,
|
|
'member': None, 'healthmonitor': None, 'l7policy': None,
|
|
'l7rule': None}
|
|
|
|
def init_complete(self, resource, event, trigger, payload=None):
|
|
with locking.LockManager.get_lock('plugin-init-complete-tvd'):
|
|
if self.init_is_complete:
|
|
# Should be called only once per worker
|
|
return
|
|
self.init_octavia()
|
|
self.init_is_complete = True
|
|
|
|
def init_octavia(self):
|
|
# Init Octavia listener and endpoints
|
|
v_objects = self._get_octavia_objects(
|
|
projectpluginmap.NsxPlugins.NSX_V)
|
|
t_objects = self._get_octavia_objects(
|
|
projectpluginmap.NsxPlugins.NSX_T)
|
|
|
|
self.octavia_listener = octavia_listener.NSXOctaviaListener(
|
|
loadbalancer=octavia_tvd.OctaviaTVDWrapper(
|
|
v_objects['loadbalancer'], t_objects['loadbalancer']),
|
|
listener=octavia_tvd.OctaviaTVDWrapper(
|
|
v_objects['listener'], t_objects['listener']),
|
|
pool=octavia_tvd.OctaviaTVDWrapper(
|
|
v_objects['pool'], t_objects['pool']),
|
|
member=octavia_tvd.OctaviaTVDWrapper(
|
|
v_objects['member'], t_objects['member']),
|
|
healthmonitor=octavia_tvd.OctaviaTVDWrapper(
|
|
v_objects['healthmonitor'], t_objects['healthmonitor']),
|
|
l7policy=octavia_tvd.OctaviaTVDWrapper(
|
|
v_objects['l7policy'], t_objects['l7policy']),
|
|
l7rule=octavia_tvd.OctaviaTVDWrapper(
|
|
v_objects['l7rule'], t_objects['l7rule']))
|
|
|
|
def spawn_complete(self, resource, event, trigger, payload=None):
|
|
# This method should run only once, but after init_complete
|
|
if not self.init_is_complete:
|
|
self.init_complete(None, None, None)
|
|
self.init_octavia_stats_collector()
|
|
|
|
def init_octavia_stats_collector(self):
|
|
self.octavia_stats_collector = (
|
|
octavia_listener.NSXOctaviaStatisticsCollector(
|
|
self,
|
|
octavia_tvd.stats_getter))
|
|
|
|
def start_rpc_listeners(self):
|
|
# Run the start_rpc_listeners of one of the sub-plugins
|
|
for plugin_type in self.plugins:
|
|
plugin = self.plugins[plugin_type]
|
|
if plugin.rpc_workers_supported():
|
|
return plugin.start_rpc_listeners()
|
|
|
|
def _unsubscribe_callback_events(self):
|
|
# unsubscribe the callback that should be called on all plugins
|
|
# other that NSX-T.
|
|
registry.unsubscribe_all(
|
|
l3_db.L3_NAT_dbonly_mixin._prevent_l3_port_delete_callback)
|
|
|
|
# Instead we will subscribe our internal callback.
|
|
registry.subscribe(self._prevent_l3_port_delete_callback,
|
|
resources.PORT, events.BEFORE_DELETE)
|
|
|
|
@staticmethod
|
|
def _prevent_l3_port_delete_callback(resource, event,
|
|
trigger, payload=None):
|
|
"""Register a callback to replace the default one
|
|
|
|
This callback will prevent port deleting only if the port plugin
|
|
is not NSX-T (in NSX-T plugin it was already handled)
|
|
"""
|
|
context = payload.context
|
|
port_id = payload.resource_id
|
|
port_check = payload.metadata['port_check']
|
|
l3plugin = directory.get_plugin(plugin_constants.L3)
|
|
if l3plugin and port_check:
|
|
# if not nsx-t - call super code
|
|
core_plugin = directory.get_plugin()
|
|
db_port = core_plugin._get_port(context, port_id)
|
|
p = core_plugin._get_plugin_from_net_id(
|
|
context, db_port['network_id'])
|
|
if p.plugin_type() != projectpluginmap.NsxPlugins.NSX_T:
|
|
l3plugin.prevent_l3_port_deletion(context, port_id)
|
|
|
|
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_for_request(context, filters)
|
|
if p:
|
|
return p._list_availability_zones(context, filters=filters)
|
|
return []
|
|
|
|
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 = 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)
|
|
|
|
@db_api.retry_if_session_inactive()
|
|
def create_network_bulk(self, context, networks):
|
|
#Implement create bulk so that the plugin calculation will be done once
|
|
objects = []
|
|
items = networks['networks']
|
|
|
|
# look at the first network to find out the project & plugin
|
|
net_data = items[0]['network']
|
|
tenant_id = net_data['tenant_id']
|
|
self._ensure_default_security_group(context, tenant_id)
|
|
p = self._get_plugin_from_project(context, tenant_id)
|
|
|
|
# create all networks one by one
|
|
try:
|
|
with db_api.CONTEXT_WRITER.using(context):
|
|
for item in items:
|
|
objects.append(p.create_network(context, item))
|
|
except Exception:
|
|
with excutils.save_and_reraise_exception():
|
|
LOG.error("An exception occurred while creating "
|
|
"the networks:%(item)s",
|
|
{'item': item})
|
|
return objects
|
|
|
|
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_plugin_for_request(self, context, filters, keys=None):
|
|
project_id = context.project_id
|
|
if filters:
|
|
if filters.get('tenant_id'):
|
|
project_id = filters.get('tenant_id')
|
|
elif filters.get('project_id'):
|
|
project_id = filters.get('project_id')
|
|
else:
|
|
# we have specific filters on the request. If those are
|
|
# specific enough, we should not filter by project
|
|
if filters.get('id'):
|
|
return
|
|
if keys:
|
|
for key in keys:
|
|
if filters.get(key):
|
|
return
|
|
# If there are multiple tenants/projects being requested then
|
|
# we will not filter according to the plugin
|
|
if isinstance(project_id, list):
|
|
return
|
|
return self._get_plugin_from_project(context, project_id)
|
|
|
|
def get_networks(self, context, filters=None, fields=None,
|
|
sorts=None, limit=None, marker=None,
|
|
page_reverse=False):
|
|
# Read project plugin to filter relevant projects according to
|
|
# plugin
|
|
req_p = self._get_plugin_for_request(context, filters,
|
|
keys=['shared'])
|
|
filters = filters or {}
|
|
with db_api.CONTEXT_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'])
|
|
if p == req_p or req_p is None:
|
|
p._extend_get_network_dict_provider(context, net)
|
|
else:
|
|
networks.remove(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):
|
|
net_id = port['port']['network_id']
|
|
p = self._get_plugin_from_net_id(context, net_id)
|
|
self._validate_obj_extensions(
|
|
port['port'], p.plugin_type(), 'port')
|
|
new_port = p.create_port(context, port)
|
|
self._cleanup_obj_fields(
|
|
new_port, p.plugin_type(), 'port')
|
|
return new_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'])
|
|
self._validate_obj_extensions(
|
|
port['port'], p.plugin_type(), 'port')
|
|
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'])
|
|
port = p.get_port(context, id, fields=fields)
|
|
self._cleanup_obj_fields(
|
|
port, p.plugin_type(), 'port')
|
|
return port
|
|
|
|
def get_ports(self, context, filters=None, fields=None,
|
|
sorts=None, limit=None, marker=None,
|
|
page_reverse=False):
|
|
# Read project plugin to filter relevant projects according to
|
|
# plugin
|
|
req_p = self._get_plugin_for_request(context, filters,
|
|
keys=['device_id',
|
|
'network_id',
|
|
'fixed_ips'])
|
|
filters = filters or {}
|
|
with db_api.CONTEXT_READER.using(context):
|
|
ports = (
|
|
super(NsxTVDPlugin, self).get_ports(
|
|
context, filters, fields, sorts,
|
|
limit, marker, page_reverse))
|
|
# Add port extensions
|
|
for port in ports[:]:
|
|
port_model = None
|
|
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'])
|
|
if p == req_p or req_p is None:
|
|
if hasattr(p, '_extend_get_port_dict_qos_and_binding'):
|
|
p._extend_get_port_dict_qos_and_binding(context, port)
|
|
else:
|
|
if not port_model:
|
|
port_model = port
|
|
p._extend_port_dict_binding(port, port_model)
|
|
if hasattr(p,
|
|
'_remove_provider_security_groups_from_list'):
|
|
p._remove_provider_security_groups_from_list(port)
|
|
self._cleanup_obj_fields(
|
|
port, p.plugin_type(), 'port')
|
|
else:
|
|
ports.remove(port)
|
|
return (ports if not fields else
|
|
[db_utils.resource_fields(port, fields) for port in ports])
|
|
|
|
def _get_subnet_plugin_by_id(self, context, subnet_id):
|
|
db_subnet = self._get_subnet_object(context, subnet_id)
|
|
return self._get_plugin_from_net_id(context, db_subnet.network_id)
|
|
|
|
def get_subnet(self, context, id, fields=None):
|
|
p = self._get_subnet_plugin_by_id(context, 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):
|
|
# Check if we need to invoke metadata search. Here we are unable to
|
|
# filter according to projects as this is from the nova api service
|
|
# so we invoke on all plugins that support this extension
|
|
if ((fields and as_providers.ADV_SERVICE_PROVIDERS in fields) or
|
|
(filters and filters.get(as_providers.ADV_SERVICE_PROVIDERS))):
|
|
for plugin in self.as_providers.values():
|
|
f = copy.copy(filters)
|
|
subnets = plugin.get_subnets(context, filters=f,
|
|
fields=fields, sorts=sorts,
|
|
limit=limit, marker=marker,
|
|
page_reverse=page_reverse)
|
|
if subnets:
|
|
return subnets
|
|
return []
|
|
# Read project plugin to filter relevant projects according to
|
|
# plugin
|
|
req_p = self._get_plugin_for_request(context, filters)
|
|
filters = filters or {}
|
|
subnets = super(NsxTVDPlugin, self).get_subnets(
|
|
context, filters=filters, fields=fields, sorts=sorts,
|
|
limit=limit, marker=marker, page_reverse=page_reverse)
|
|
for subnet in subnets[:]:
|
|
p = self._get_plugin_from_project(context, subnet['tenant_id'])
|
|
if req_p and p != req_p:
|
|
subnets.remove(subnet)
|
|
return subnets
|
|
|
|
def delete_subnet(self, context, id):
|
|
p = self._get_subnet_plugin_by_id(context, id)
|
|
p.delete_subnet(context, id)
|
|
|
|
def _get_subnet_plugin(self, context, subnet_data):
|
|
# get the plugin of the associated network
|
|
net_id = subnet_data['network_id']
|
|
net_plugin = self._get_plugin_from_net_id(context, net_id)
|
|
# make sure it matches the plugin of the current tenant
|
|
tenant_id = subnet_data['tenant_id']
|
|
tenant_plugin = self._get_plugin_from_project(context, tenant_id)
|
|
if tenant_plugin.plugin_type() != net_plugin.plugin_type():
|
|
err_msg = (_('Subnet should belong to the %s plugin '
|
|
'as the network') % net_plugin.plugin_type())
|
|
raise n_exc.InvalidInput(error_message=err_msg)
|
|
return net_plugin
|
|
|
|
def create_subnet(self, context, subnet):
|
|
p = self._get_subnet_plugin(context, subnet['subnet'])
|
|
return p.create_subnet(context, subnet)
|
|
|
|
def create_subnet_bulk(self, context, subnets):
|
|
# look at the first subnet to find out the project & plugin
|
|
items = subnets['subnets']
|
|
p = self._get_subnet_plugin(context, items[0]['subnet'])
|
|
return p.create_subnet_bulk(context, subnets)
|
|
|
|
def update_subnet(self, context, id, subnet):
|
|
p = self._get_subnet_plugin_by_id(context, 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_object(
|
|
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 = 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(
|
|
new_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 delete_router(self, context, id):
|
|
p = self._get_plugin_from_router_id(context, id)
|
|
p.delete_router(context, id)
|
|
|
|
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 get_routers(self, context, filters=None, fields=None,
|
|
sorts=None, limit=None, marker=None,
|
|
page_reverse=False):
|
|
# Read project plugin to filter relevant projects according to
|
|
# plugin
|
|
req_p = self._get_plugin_for_request(context, filters)
|
|
routers = super(NsxTVDPlugin, self).get_routers(
|
|
context, filters=filters, fields=fields, sorts=sorts,
|
|
limit=limit, marker=marker, page_reverse=page_reverse)
|
|
for router in routers[:]:
|
|
p = self._get_plugin_from_project(context, router['tenant_id'])
|
|
if req_p and p != req_p:
|
|
routers.remove(router)
|
|
return routers
|
|
|
|
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 get_floatingip(self, context, id, fields=None):
|
|
fip = self._get_floatingip(context, id)
|
|
net_id = fip['floating_network_id']
|
|
p = self._get_plugin_from_net_id(context, net_id)
|
|
return p.get_floatingip(context, id, fields=fields)
|
|
|
|
def get_floatingips(self, context, filters=None, fields=None,
|
|
sorts=None, limit=None, marker=None,
|
|
page_reverse=False):
|
|
# Read project plugin to filter relevant projects according to
|
|
# plugin
|
|
req_p = self._get_plugin_for_request(context, filters,
|
|
keys=['port_id'])
|
|
fips = super(NsxTVDPlugin, self).get_floatingips(
|
|
context, filters=filters, fields=fields, sorts=sorts,
|
|
limit=limit, marker=marker, page_reverse=page_reverse)
|
|
for fip in fips[:]:
|
|
p = self._get_plugin_from_project(context,
|
|
fip['tenant_id'])
|
|
if req_p and p != req_p:
|
|
fips.remove(fip)
|
|
return fips
|
|
|
|
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):
|
|
sg = self._get_security_group(context, sg_id)
|
|
return self._get_plugin_from_project(context, sg['tenant_id'])
|
|
|
|
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)
|
|
self._validate_obj_extensions(
|
|
security_group['security_group'], p.plugin_type(),
|
|
'security_group')
|
|
|
|
new_sg = p.create_security_group(context, security_group,
|
|
default_sg=default_sg)
|
|
self._cleanup_obj_fields(
|
|
new_sg, p.plugin_type(), 'security_group')
|
|
return new_sg
|
|
|
|
def delete_security_group(self, context, id):
|
|
p = self._get_plugin_from_sg_id(context, id)
|
|
p.delete_security_group(context, id)
|
|
|
|
def update_security_group(self, context, id, security_group):
|
|
p = self._get_plugin_from_sg_id(context, id)
|
|
self._validate_obj_extensions(
|
|
security_group['security_group'], p.plugin_type(),
|
|
'security_group')
|
|
return p.update_security_group(context, id, security_group)
|
|
|
|
def get_security_group(self, context, id, fields=None):
|
|
p = self._get_plugin_from_sg_id(context, id)
|
|
sg = p.get_security_group(context, id, fields=fields)
|
|
self._cleanup_obj_fields(
|
|
sg, p.plugin_type(), 'security_group')
|
|
return sg
|
|
|
|
def get_security_groups(self, context, filters=None, fields=None,
|
|
sorts=None, limit=None,
|
|
marker=None, page_reverse=False, default_sg=False):
|
|
# Read project plugin to filter relevant projects according to
|
|
# plugin
|
|
req_p = self._get_plugin_for_request(context, filters)
|
|
sgs = super(NsxTVDPlugin, self).get_security_groups(
|
|
context, filters=filters, fields=fields, sorts=sorts,
|
|
limit=limit, marker=marker, page_reverse=page_reverse,
|
|
default_sg=default_sg)
|
|
for sg in sgs[:]:
|
|
p = self._get_plugin_from_project(context, sg['tenant_id'])
|
|
if req_p and p != req_p:
|
|
sgs.remove(sg)
|
|
return sgs
|
|
|
|
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)
|
|
|
|
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)
|
|
|
|
def delete_security_group_rule(self, context, id):
|
|
rule_db = self._get_security_group_rule(context, id)
|
|
sg_id = rule_db['security_group_id']
|
|
p = self._get_plugin_from_sg_id(context, sg_id)
|
|
p.delete_security_group_rule(context, id)
|
|
|
|
def get_security_group_rules(self, context, filters=None, fields=None,
|
|
sorts=None, limit=None, marker=None,
|
|
page_reverse=False):
|
|
# Read project plugin to filter relevant projects according to
|
|
# plugin
|
|
req_p = self._get_plugin_for_request(context, filters)
|
|
rules = super(NsxTVDPlugin, self).get_security_group_rules(
|
|
context, filters=filters, fields=fields, sorts=sorts,
|
|
limit=limit, marker=marker, page_reverse=page_reverse)
|
|
for rule in rules[:]:
|
|
p = self._get_plugin_from_project(context, rule['tenant_id'])
|
|
if req_p and p != req_p:
|
|
rules.remove(rule)
|
|
return rules
|
|
|
|
@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_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_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_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,
|
|
internal=False):
|
|
data = project_plugin_map['project_plugin_map']
|
|
|
|
# validations:
|
|
# 1. validate it doesn't already exist
|
|
if nsx_db.get_project_plugin_mapping(
|
|
context.session, data['project']):
|
|
raise projectpluginmap.ProjectPluginAlreadyExists(
|
|
project_id=data['project'])
|
|
if not internal:
|
|
# 2. only admin user is allowed
|
|
if not context.is_admin:
|
|
raise projectpluginmap.ProjectPluginAdminOnly()
|
|
# 3. Validate the project id
|
|
# TODO(asarfaty): Validate project id exists in keystone
|
|
if not uuidutils.is_uuid_like(data['project']):
|
|
raise projectpluginmap.ProjectPluginIllegalId(
|
|
project_id=data['project'])
|
|
# 4. Check that plugin is available
|
|
if data['plugin'] not in self.plugins:
|
|
raise projectpluginmap.ProjectPluginNotAvailable(
|
|
plugin=data['plugin'])
|
|
|
|
# Add the entry to the DB and return it
|
|
LOG.info("Adding mapping between project %(project)s and plugin "
|
|
"%(plugin)s", {'project': data['project'],
|
|
'plugin': data['plugin']})
|
|
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)
|
|
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_type_from_project(self, context, project_id):
|
|
"""Get the correct plugin type 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
|
|
if not project_id:
|
|
# if the project_id is empty - return the default one and do not
|
|
# add to db (used by admin context to get actions)
|
|
return plugin_type
|
|
|
|
mapping = nsx_db.get_project_plugin_mapping(
|
|
context.session, project_id)
|
|
if mapping:
|
|
plugin_type = mapping['plugin']
|
|
else:
|
|
# add a new entry with the default plugin
|
|
try:
|
|
self.create_project_plugin_map(
|
|
context,
|
|
{'project_plugin_map': {'plugin': plugin_type,
|
|
'project': project_id}},
|
|
internal=True)
|
|
except projectpluginmap.ProjectPluginAlreadyExists:
|
|
# Maybe added by another thread
|
|
pass
|
|
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 for project %s", plugin_type, project_id)
|
|
return plugin_type
|
|
|
|
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.get_plugin_type_from_project(context, project_id)
|
|
return self.plugins[plugin_type]
|
|
|
|
def get_housekeeper(self, context, name, fields=None):
|
|
p = self._get_plugin_from_project(context, context.project_id)
|
|
if hasattr(p, 'housekeeper'):
|
|
return p.housekeeper.get(name)
|
|
msg = _("Housekeeper %s not found") % name
|
|
raise nsx_exc.NsxPluginException(err_msg=msg)
|
|
|
|
def get_housekeepers(self, context, filters=None, fields=None, sorts=None,
|
|
limit=None, marker=None, page_reverse=False):
|
|
p = self._get_plugin_for_request(context, filters)
|
|
if p and hasattr(p, 'housekeeper'):
|
|
return p.housekeeper.list()
|
|
return []
|
|
|
|
def update_housekeeper(self, context, name, housekeeper):
|
|
p = self._get_plugin_from_project(context, context.project_id)
|
|
if hasattr(p, 'housekeeper'):
|
|
p.housekeeper.run(context, name)
|
|
return p.housekeeper.get(name)
|
|
|
|
def get_address_scopes(self, context, filters=None, fields=None,
|
|
sorts=None, limit=None, marker=None,
|
|
page_reverse=False):
|
|
# Read project plugin to filter relevant projects according to
|
|
# plugin
|
|
req_p = self._get_plugin_for_request(context, filters)
|
|
address_scopes = super(NsxTVDPlugin, self).get_address_scopes(
|
|
context, filters=filters, fields=fields, sorts=sorts,
|
|
limit=limit, marker=marker, page_reverse=page_reverse)
|
|
for address_scope in address_scopes[:]:
|
|
p = self._get_plugin_from_project(context,
|
|
address_scope['tenant_id'])
|
|
if req_p and p != req_p:
|
|
address_scopes.remove(address_scope)
|
|
return address_scopes
|
|
|
|
def get_subnetpools(self, context, filters=None, fields=None,
|
|
sorts=None, limit=None, marker=None,
|
|
page_reverse=False):
|
|
# Read project plugin to filter relevant projects according to
|
|
# plugin
|
|
req_p = self._get_plugin_for_request(context, filters)
|
|
pools = super(NsxTVDPlugin, self).get_subnetpools(
|
|
context, filters=filters, fields=fields, sorts=sorts,
|
|
limit=limit, marker=marker, page_reverse=page_reverse)
|
|
for pool in pools[:]:
|
|
p = self._get_plugin_from_project(context,
|
|
pool['tenant_id'])
|
|
if req_p and p != req_p:
|
|
pools.remove(pool)
|
|
return pools
|
|
|
|
def get_nsx_policy(self, context, id, fields=None):
|
|
# Extension supported only by the nsxv plugin
|
|
p = self._get_plugin_from_project(context, context.project_id)
|
|
if p.plugin_type() != v.NsxVPluginV2.plugin_type():
|
|
err_msg = (_('Can not support %(field)s extension for '
|
|
'%(p)s plugin') % {
|
|
'field': 'nsx-policy',
|
|
'p': p.plugin_type()})
|
|
raise n_exc.InvalidInput(error_message=err_msg)
|
|
|
|
return p.get_nsx_policy(context, id, fields=fields)
|
|
|
|
def get_nsx_policies(self, context, filters=None, fields=None,
|
|
sorts=None, limit=None, marker=None,
|
|
page_reverse=False):
|
|
# Extension supported only by the nsxv plugin
|
|
p = self._get_plugin_from_project(context, context.project_id)
|
|
if p.plugin_type() != v.NsxVPluginV2.plugin_type():
|
|
return []
|
|
return p.get_nsx_policies(context, filters=filters, fields=fields,
|
|
sorts=sorts, limit=limit, marker=marker,
|
|
page_reverse=page_reverse)
|