-[mlnx]
-# (StrOpt) Type of network to allocate for tenant networks. The
-# default value is 'vlan' You MUST configure network_vlan_ranges below
-# in order for tenant networks to provide connectivity between hosts.
-# Set to 'none' to disable creation of tenant networks.
-#
-# tenant_network_type = vlan
-# Example: tenant_network_type = vlan
-
-# (ListOpt) Comma-separated list of
-# <physical_network>[:<vlan_min>:<vlan_max>] tuples enumerating ranges
-# of VLAN IDs on named physical networks that are available for
-# allocation. All physical networks listed are available for flat and
-# VLAN provider network creation. Specified ranges of VLAN IDs are
-# available for tenant network allocation if tenant_network_type is
-# 'vlan'. If empty, only local networks may be created.
-#
-# network_vlan_ranges =
-# Example: network_vlan_ranges = default:1:100
-
-# (ListOpt) Comma-separated list of
-# <physical_network>:<physical_network_type> tuples mapping physical
-# network names to physical network types. All physical
-# networks listed in network_vlan_ranges should have
-# mappings to appropriate physical network type.
-# Type of the physical network can be either eth (Ethernet) or
-# ib (InfiniBand). If empty, physical network eth type is assumed.
-#
-# physical_network_type_mappings =
-# Example: physical_network_type_mappings = default:eth
-
-# (StrOpt) Type of the physical network, can be either 'eth' or 'ib'
-# The default value is 'eth'
-# physical_network_type = eth
-
[eswitch]
# (ListOpt) Comma-separated list of
# <physical_network>:<physical_interface> tuples mapping physical
[agent]
# Agent's polling interval in seconds
# polling_interval = 2
-
-# (BoolOpt) Enable server RPC compatibility with old (pre-havana)
-# agents.
-#
-# rpc_support_old_agents = False
-
-[securitygroup]
-# Controls if neutron security group is enabled or not.
-# It should be false when you use nova security group.
-# enable_security_group = True
--- /dev/null
+# Copyright 2014 OpenStack Foundation
+#
+# 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.
+#
+
+"""remove mlnx plugin
+
+Revision ID: 28c0ffb8ebbd
+Revises: 408cfbf6923c
+Create Date: 2014-12-08 23:58:49.288830
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '28c0ffb8ebbd'
+down_revision = '408cfbf6923c'
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade():
+ op.drop_table('mlnx_network_bindings')
+ op.drop_table('segmentation_id_allocation')
+ op.drop_table('port_profile')
+
+
+def downgrade():
+ op.create_table(
+ 'port_profile',
+ sa.Column(
+ 'port_id', sa.String(length=36), nullable=False),
+ sa.Column(
+ 'vnic_type', sa.String(length=32), nullable=False),
+ sa.ForeignKeyConstraint(['port_id'], ['ports.id'],
+ ondelete='CASCADE'),
+ sa.PrimaryKeyConstraint('port_id'),
+ )
+ op.create_table(
+ 'segmentation_id_allocation',
+ sa.Column('physical_network',
+ sa.String(length=64),
+ nullable=False),
+ sa.Column('segmentation_id',
+ sa.Integer(),
+ autoincrement=False,
+ nullable=False),
+ sa.Column('allocated',
+ sa.Boolean(),
+ server_default=sa.sql.false(),
+ nullable=False),
+ sa.PrimaryKeyConstraint('physical_network', 'segmentation_id')
+ )
+ op.create_table(
+ 'mlnx_network_bindings',
+ sa.Column('network_id', sa.String(length=36), nullable=False),
+ sa.Column('network_type', sa.String(length=32), nullable=False),
+ sa.Column('physical_network', sa.String(length=64), nullable=True),
+ sa.Column('segmentation_id',
+ sa.Integer(),
+ autoincrement=False, nullable=False),
+ sa.ForeignKeyConstraint(['network_id'],
+ ['networks.id']),
+ sa.PrimaryKeyConstraint('network_id'),
+ )
-408cfbf6923c
\ No newline at end of file
+28c0ffb8ebbd
\ No newline at end of file
from neutron.plugins.ml2.drivers import type_vlan # noqa
from neutron.plugins.ml2.drivers import type_vxlan # noqa
from neutron.plugins.ml2 import models # noqa
-from neutron.plugins.mlnx.db import mlnx_models_v2 # noqa
from neutron.plugins.nec.db import models as nec_models # noqa
from neutron.plugins.nec.db import packetfilter as nec_packetfilter # noqa
from neutron.plugins.nec.db import router # noqa
-Mellanox Neutron Plugin
-
-This plugin implements Neutron v2 APIs with support for
-Mellanox embedded switch functionality as part of the
-VPI (Ethernet/InfiniBand) HCA.
-
-For more details on the plugin, please refer to the following link:
-https://wiki.openstack.org/wiki/Mellanox-Quantum
+The Neutron Mellanox plugin has removed from the tree in Kilo.
+This directory includes Mellanox L2 agent for MLNX mechanism driver.
+For more details, please refer to the following link:
+https://wiki.openstack.org/wiki/Mellanox-Neutron-ML2
\ No newline at end of file
+++ /dev/null
-# Copyright 2013 Mellanox Technologies, Ltd
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-# implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from oslo.config import cfg
-from oslo import messaging
-
-from neutron.agent import securitygroups_rpc as sg_rpc
-from neutron.common import rpc as n_rpc
-from neutron.common import topics
-from neutron.openstack.common import log as logging
-
-LOG = logging.getLogger(__name__)
-
-
-class AgentNotifierApi(sg_rpc.SecurityGroupAgentRpcApiMixin):
- """Agent side of the Embedded Switch RPC API.
-
- API version history:
- 1.0 - Initial version.
- 1.1 - Added get_active_networks_info, create_dhcp_port,
- and update_dhcp_port methods.
- """
- def __init__(self, topic):
- self.topic = topic
- self.topic_network_delete = topics.get_topic_name(topic,
- topics.NETWORK,
- topics.DELETE)
- self.topic_port_update = topics.get_topic_name(topic,
- topics.PORT,
- topics.UPDATE)
- target = messaging.Target(topic=topic, version='1.0')
- self.client = n_rpc.get_client(target)
-
- def network_delete(self, context, network_id):
- LOG.debug("Sending delete network message")
- cctxt = self.client.prepare(topic=self.topic_network_delete,
- fanout=True)
- cctxt.cast(context, 'network_delete', network_id=network_id)
-
- def port_update(self, context, port, physical_network,
- network_type, vlan_id):
- LOG.debug("Sending update port message")
- kwargs = {'port': port,
- 'network_type': network_type,
- 'physical_network': physical_network,
- 'segmentation_id': vlan_id}
- if cfg.CONF.AGENT.rpc_support_old_agents:
- kwargs['vlan_id'] = vlan_id
- cctxt = self.client.prepare(topic=self.topic_port_update, fanout=True)
- cctxt.cast(context, 'port_update', **kwargs)
from neutron.agent.common import config
from neutron.plugins.mlnx.common import constants
-DEFAULT_VLAN_RANGES = ['default:1:1000']
DEFAULT_INTERFACE_MAPPINGS = []
-vlan_opts = [
- cfg.StrOpt('tenant_network_type', default='vlan',
- help=_("Network type for tenant networks "
- "(local, vlan, or none)")),
- cfg.ListOpt('network_vlan_ranges',
- default=DEFAULT_VLAN_RANGES,
- help=_("List of <physical_network>:<vlan_min>:<vlan_max> "
- "or <physical_network>")),
- cfg.ListOpt('physical_network_type_mappings',
- default=[],
- help=_("List of <physical_network>:<physical_network_type> "
- " with physical_network_type is either eth or ib")),
- cfg.StrOpt('physical_network_type', default='eth',
- help=_("Physical network type for provider network "
- "(eth or ib)"))
-]
-
-
eswitch_opts = [
cfg.ListOpt('physical_interface_mappings',
default=DEFAULT_INTERFACE_MAPPINGS,
cfg.IntOpt('polling_interval', default=2,
help=_("The number of seconds the agent will wait between "
"polling for local device changes.")),
- cfg.BoolOpt('rpc_support_old_agents', default=False,
- help=_("Enable server RPC compatibility with old agents")),
]
-cfg.CONF.register_opts(vlan_opts, "MLNX")
cfg.CONF.register_opts(eswitch_opts, "ESWITCH")
cfg.CONF.register_opts(agent_opts, "AGENT")
config.register_agent_state_opts_helper(cfg.CONF)
+++ /dev/null
-# Copyright 2013 Mellanox Technologies, Ltd
-#
-# 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 six import moves
-from sqlalchemy.orm import exc
-
-from neutron.common import exceptions as n_exc
-import neutron.db.api as db
-from neutron.db import models_v2
-from neutron.db import securitygroups_db as sg_db
-from neutron.i18n import _LW
-from neutron import manager
-from neutron.openstack.common import log as logging
-from neutron.plugins.mlnx.common import config # noqa
-from neutron.plugins.mlnx.db import mlnx_models_v2
-
-LOG = logging.getLogger(__name__)
-
-
-def _remove_non_allocatable_vlans(session, allocations,
- physical_network, vlan_ids):
- if physical_network in allocations:
- for entry in allocations[physical_network]:
- try:
- # see if vlan is allocatable
- vlan_ids.remove(entry.segmentation_id)
- except KeyError:
- # it's not allocatable, so check if its allocated
- if not entry.allocated:
- # it's not, so remove it from table
- LOG.debug(
- "Removing vlan %(seg_id)s on "
- "physical network "
- "%(net)s from pool",
- {'seg_id': entry.segmentation_id,
- 'net': physical_network})
- session.delete(entry)
- del allocations[physical_network]
-
-
-def _add_missing_allocatable_vlans(session, physical_network, vlan_ids):
- for vlan_id in sorted(vlan_ids):
- entry = mlnx_models_v2.SegmentationIdAllocation(physical_network,
- vlan_id)
- session.add(entry)
-
-
-def _remove_unconfigured_vlans(session, allocations):
- for entries in allocations.itervalues():
- for entry in entries:
- if not entry.allocated:
- LOG.debug("Removing vlan %(seg_id)s on physical "
- "network %(net)s from pool",
- {'seg_id': entry.segmentation_id,
- 'net': entry.physical_network})
- session.delete(entry)
-
-
-def sync_network_states(network_vlan_ranges):
- """Synchronize network_states table with current configured VLAN ranges."""
-
- session = db.get_session()
- with session.begin():
- # get existing allocations for all physical networks
- allocations = dict()
- entries = (session.query(mlnx_models_v2.SegmentationIdAllocation).
- all())
- for entry in entries:
- allocations.setdefault(entry.physical_network, set()).add(entry)
-
- # process vlan ranges for each configured physical network
- for physical_network, vlan_ranges in network_vlan_ranges.iteritems():
- # determine current configured allocatable vlans for this
- # physical network
- vlan_ids = set()
- for vlan_range in vlan_ranges:
- vlan_ids |= set(moves.xrange(vlan_range[0], vlan_range[1] + 1))
-
- # remove from table unallocated vlans not currently allocatable
- _remove_non_allocatable_vlans(session, allocations,
- physical_network, vlan_ids)
-
- # add missing allocatable vlans to table
- _add_missing_allocatable_vlans(session, physical_network, vlan_ids)
-
- # remove from table unallocated vlans for any unconfigured physical
- # networks
- _remove_unconfigured_vlans(session, allocations)
-
-
-def get_network_state(physical_network, segmentation_id):
- """Get entry of specified network."""
- session = db.get_session()
- qry = session.query(mlnx_models_v2.SegmentationIdAllocation)
- qry = qry.filter_by(physical_network=physical_network,
- segmentation_id=segmentation_id)
- return qry.first()
-
-
-def reserve_network(session):
- with session.begin(subtransactions=True):
- entry = (session.query(mlnx_models_v2.SegmentationIdAllocation).
- filter_by(allocated=False).
- with_lockmode('update').
- first())
- if not entry:
- raise n_exc.NoNetworkAvailable()
- LOG.debug("Reserving vlan %(seg_id)s on physical network "
- "%(net)s from pool",
- {'seg_id': entry.segmentation_id,
- 'net': entry.physical_network})
- entry.allocated = True
- return (entry.physical_network, entry.segmentation_id)
-
-
-def reserve_specific_network(session, physical_network, segmentation_id):
- with session.begin(subtransactions=True):
- log_args = {'seg_id': segmentation_id, 'phy_net': physical_network}
- try:
- entry = (session.query(mlnx_models_v2.SegmentationIdAllocation).
- filter_by(physical_network=physical_network,
- segmentation_id=segmentation_id).
- with_lockmode('update').one())
- if entry.allocated:
- raise n_exc.VlanIdInUse(vlan_id=segmentation_id,
- physical_network=physical_network)
- LOG.debug("Reserving specific vlan %(seg_id)s "
- "on physical network %(phy_net)s from pool",
- log_args)
- entry.allocated = True
- except exc.NoResultFound:
- LOG.debug("Reserving specific vlan %(seg_id)s on "
- "physical network %(phy_net)s outside pool",
- log_args)
- entry = mlnx_models_v2.SegmentationIdAllocation(physical_network,
- segmentation_id)
- entry.allocated = True
- session.add(entry)
-
-
-def release_network(session, physical_network,
- segmentation_id, network_vlan_ranges):
- with session.begin(subtransactions=True):
- log_args = {'seg_id': segmentation_id, 'phy_net': physical_network}
- try:
- state = (session.query(mlnx_models_v2.SegmentationIdAllocation).
- filter_by(physical_network=physical_network,
- segmentation_id=segmentation_id).
- with_lockmode('update').
- one())
- state.allocated = False
- inside = False
- for vlan_range in network_vlan_ranges.get(physical_network, []):
- if (segmentation_id >= vlan_range[0] and
- segmentation_id <= vlan_range[1]):
- inside = True
- break
- if inside:
- LOG.debug("Releasing vlan %(seg_id)s "
- "on physical network "
- "%(phy_net)s to pool",
- log_args)
- else:
- LOG.debug("Releasing vlan %(seg_id)s "
- "on physical network "
- "%(phy_net)s outside pool",
- log_args)
- session.delete(state)
- except exc.NoResultFound:
- LOG.warning(_LW("vlan_id %(seg_id)s on physical network "
- "%(phy_net)s not found"),
- log_args)
-
-
-def add_network_binding(session, network_id, network_type,
- physical_network, vlan_id):
- with session.begin(subtransactions=True):
- binding = mlnx_models_v2.NetworkBinding(network_id, network_type,
- physical_network, vlan_id)
- session.add(binding)
-
-
-def get_network_binding(session, network_id):
- return (session.query(mlnx_models_v2.NetworkBinding).
- filter_by(network_id=network_id).first())
-
-
-def add_port_profile_binding(session, port_id, vnic_type):
- with session.begin(subtransactions=True):
- binding = mlnx_models_v2.PortProfileBinding(port_id, vnic_type)
- session.add(binding)
-
-
-def get_port_profile_binding(session, port_id):
- return (session.query(mlnx_models_v2.PortProfileBinding).
- filter_by(port_id=port_id).first())
-
-
-def get_port_from_device(device):
- """Get port from database."""
- LOG.debug("get_port_from_device() called")
- session = db.get_session()
- sg_binding_port = sg_db.SecurityGroupPortBinding.port_id
-
- query = session.query(models_v2.Port,
- sg_db.SecurityGroupPortBinding.security_group_id)
- query = query.outerjoin(sg_db.SecurityGroupPortBinding,
- models_v2.Port.id == sg_binding_port)
- query = query.filter(models_v2.Port.id.startswith(device))
- port_and_sgs = query.all()
- if not port_and_sgs:
- return
- port = port_and_sgs[0][0]
- plugin = manager.NeutronManager.get_plugin()
- port_dict = plugin._make_port_dict(port)
- port_dict['security_groups'] = [
- sg_id for port_in_db, sg_id in port_and_sgs if sg_id
- ]
- port_dict['security_group_rules'] = []
- port_dict['security_group_source_groups'] = []
- port_dict['fixed_ips'] = [ip['ip_address']
- for ip in port['fixed_ips']]
- return port_dict
-
-
-def get_port_from_device_mac(device_mac):
- """Get port from database."""
- LOG.debug("Get_port_from_device_mac() called")
- session = db.get_session()
- qry = session.query(models_v2.Port).filter_by(mac_address=device_mac)
- return qry.first()
-
-
-def set_port_status(port_id, status):
- """Set the port status."""
- LOG.debug("Set_port_status as %s called", status)
- session = db.get_session()
- try:
- port = session.query(models_v2.Port).filter_by(id=port_id).one()
- port['status'] = status
- session.merge(port)
- session.flush()
- except exc.NoResultFound:
- raise n_exc.PortNotFound(port_id=port_id)
+++ /dev/null
-# Copyright 2013 Mellanox Technologies, Ltd
-#
-# 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 sqlalchemy as sa
-from sqlalchemy import sql
-
-from neutron.db import model_base
-
-
-class SegmentationIdAllocation(model_base.BASEV2):
- """Represents allocation state of segmentation_id on physical network."""
- __tablename__ = 'segmentation_id_allocation'
-
- physical_network = sa.Column(sa.String(64), nullable=False,
- primary_key=True)
- segmentation_id = sa.Column(sa.Integer, nullable=False, primary_key=True,
- autoincrement=False)
- allocated = sa.Column(sa.Boolean, nullable=False, default=False,
- server_default=sql.false())
-
- def __init__(self, physical_network, segmentation_id):
- self.physical_network = physical_network
- self.segmentation_id = segmentation_id
- self.allocated = False
-
- def __repr__(self):
- return "<SegmentationIdAllocation(%s,%d,%s)>" % (self.physical_network,
- self.segmentation_id,
- self.allocated)
-
-
-class NetworkBinding(model_base.BASEV2):
- """Represents binding of virtual network.
-
- Binds network to physical_network and segmentation_id
- """
- __tablename__ = 'mlnx_network_bindings'
-
- network_id = sa.Column(sa.String(36),
- sa.ForeignKey('networks.id', ondelete="CASCADE"),
- primary_key=True)
- network_type = sa.Column(sa.String(32), nullable=False)
- physical_network = sa.Column(sa.String(64))
- segmentation_id = sa.Column(sa.Integer, nullable=False)
-
- def __init__(self, network_id, network_type, physical_network, vlan_id):
- self.network_id = network_id
- self.network_type = network_type
- self.physical_network = physical_network
- self.segmentation_id = vlan_id
-
- def __repr__(self):
- return "<NetworkBinding(%s,%s,%s,%d)>" % (self.network_id,
- self.network_type,
- self.physical_network,
- self.segmentation_id)
-
-
-class PortProfileBinding(model_base.BASEV2):
- """Represents port profile binding to the port on virtual network."""
- __tablename__ = 'port_profile'
-
- port_id = sa.Column(sa.String(36),
- sa.ForeignKey('ports.id', ondelete="CASCADE"),
- primary_key=True)
- vnic_type = sa.Column(sa.String(32), nullable=False)
-
- def __init__(self, port_id, vnic_type):
- self.port_id = port_id
- self.vnic_type = vnic_type
-
- def __repr__(self):
- return "<PortProfileBinding(%s,%s)>" % (self.port_id,
- self.vnic_type)
+++ /dev/null
-# Copyright 2013 Mellanox Technologies, Ltd
-#
-# 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 sys
-
-from oslo.config import cfg
-from oslo.utils import importutils
-
-from neutron.agent import securitygroups_rpc as sg_rpc
-from neutron.api.rpc.agentnotifiers import dhcp_rpc_agent_api
-from neutron.api.rpc.agentnotifiers import l3_rpc_agent_api
-from neutron.api.rpc.handlers import dhcp_rpc
-from neutron.api.rpc.handlers import l3_rpc
-from neutron.api.rpc.handlers import metadata_rpc
-from neutron.api.rpc.handlers import securitygroups_rpc
-from neutron.api.v2 import attributes
-from neutron.common import constants as q_const
-from neutron.common import exceptions as n_exc
-from neutron.common import rpc as n_rpc
-from neutron.common import topics
-from neutron.common import utils
-from neutron.db import agents_db
-from neutron.db import agentschedulers_db
-from neutron.db import db_base_plugin_v2
-from neutron.db import external_net_db
-from neutron.db import extraroute_db
-from neutron.db import l3_agentschedulers_db
-from neutron.db import l3_gwmode_db
-from neutron.db import portbindings_db
-from neutron.db import quota_db # noqa
-from neutron.db import securitygroups_rpc_base as sg_db_rpc
-from neutron.extensions import portbindings
-from neutron.extensions import providernet as provider
-from neutron.i18n import _LE, _LI
-from neutron.openstack.common import log as logging
-from neutron.plugins.common import constants as svc_constants
-from neutron.plugins.common import utils as plugin_utils
-from neutron.plugins.mlnx import agent_notify_api
-from neutron.plugins.mlnx.common import constants
-from neutron.plugins.mlnx.db import mlnx_db_v2 as db
-from neutron.plugins.mlnx import rpc_callbacks
-
-LOG = logging.getLogger(__name__)
-
-
-class MellanoxEswitchPlugin(db_base_plugin_v2.NeutronDbPluginV2,
- external_net_db.External_net_db_mixin,
- extraroute_db.ExtraRoute_db_mixin,
- l3_gwmode_db.L3_NAT_db_mixin,
- sg_db_rpc.SecurityGroupServerRpcMixin,
- l3_agentschedulers_db.L3AgentSchedulerDbMixin,
- agentschedulers_db.DhcpAgentSchedulerDbMixin,
- portbindings_db.PortBindingMixin):
- """Realization of Neutron API on Mellanox HCA embedded switch technology.
-
- Current plugin provides embedded HCA Switch connectivity.
- Code is based on the Linux Bridge plugin content to
- support consistency with L3 & DHCP Agents.
-
- A new VLAN is created for each network. An agent is relied upon
- to perform the actual HCA configuration on each host.
-
- The provider extension is also supported.
-
- The port binding extension enables an external application relay
- information to and from the plugin.
- """
-
- # This attribute specifies whether the plugin supports or not
- # bulk operations. Name mangling is used in order to ensure it
- # is qualified by class
- __native_bulk_support = True
-
- _supported_extension_aliases = ["provider", "external-net", "router",
- "ext-gw-mode", "binding", "quotas",
- "security-group", "agent", "extraroute",
- "l3_agent_scheduler",
- "dhcp_agent_scheduler"]
-
- @property
- def supported_extension_aliases(self):
- if not hasattr(self, '_aliases'):
- aliases = self._supported_extension_aliases[:]
- sg_rpc.disable_security_group_extension_by_config(aliases)
- self._aliases = aliases
- return self._aliases
-
- def __init__(self):
- """Start Mellanox Neutron Plugin."""
- super(MellanoxEswitchPlugin, self).__init__()
- self._parse_network_config()
- db.sync_network_states(self.network_vlan_ranges)
- self._set_tenant_network_type()
- self.vnic_type = cfg.CONF.ESWITCH.vnic_type
- self.base_binding_dict = {
- portbindings.VIF_TYPE: self.vnic_type,
- portbindings.VIF_DETAILS: {
- # TODO(rkukura): Replace with new VIF security details
- portbindings.CAP_PORT_FILTER:
- 'security-group' in self.supported_extension_aliases}}
- self._setup_rpc()
- self.network_scheduler = importutils.import_object(
- cfg.CONF.network_scheduler_driver
- )
- self.router_scheduler = importutils.import_object(
- cfg.CONF.router_scheduler_driver
- )
- LOG.debug("Mellanox Embedded Switch Plugin initialisation complete")
-
- def _setup_rpc(self):
- # RPC support
- self.service_topics = {svc_constants.CORE: topics.PLUGIN,
- svc_constants.L3_ROUTER_NAT: topics.L3PLUGIN}
- self.conn = n_rpc.create_connection(new=True)
- self.endpoints = [rpc_callbacks.MlnxRpcCallbacks(),
- securitygroups_rpc.SecurityGroupServerRpcCallback(),
- dhcp_rpc.DhcpRpcCallback(),
- l3_rpc.L3RpcCallback(),
- agents_db.AgentExtRpcCallback(),
- metadata_rpc.MetadataRpcCallback()]
- for svc_topic in self.service_topics.values():
- self.conn.create_consumer(svc_topic, self.endpoints, fanout=False)
- # Consume from all consumers in threads
- self.conn.consume_in_threads()
- self.notifier = agent_notify_api.AgentNotifierApi(topics.AGENT)
- self.agent_notifiers[q_const.AGENT_TYPE_DHCP] = (
- dhcp_rpc_agent_api.DhcpAgentNotifyAPI()
- )
- self.agent_notifiers[q_const.AGENT_TYPE_L3] = (
- l3_rpc_agent_api.L3AgentNotifyAPI()
- )
-
- def _parse_network_config(self):
- self._parse_physical_network_types()
- self._parse_network_vlan_ranges()
- for network in self.network_vlan_ranges.keys():
- if not self.phys_network_type_maps.get(network):
- self.phys_network_type_maps[network] = self.physical_net_type
-
- def _parse_physical_network_types(self):
- """Parse physical network types configuration.
-
- Verify default physical network type is valid.
- Parse physical network mappings.
- """
- self.physical_net_type = cfg.CONF.MLNX.physical_network_type
- if self.physical_net_type not in (constants.TYPE_ETH,
- constants.TYPE_IB):
- LOG.error(_LE("Invalid physical network type %(type)s. "
- "Server terminated!"),
- {'type': self.physical_net_type})
- raise SystemExit(1)
- try:
- self.phys_network_type_maps = utils.parse_mappings(
- cfg.CONF.MLNX.physical_network_type_mappings)
- except ValueError as e:
- LOG.error(_LE("Parsing physical_network_type failed: %s. "
- "Server terminated!"), e)
- raise SystemExit(1)
- for network, type in self.phys_network_type_maps.iteritems():
- if type not in (constants.TYPE_ETH, constants.TYPE_IB):
- LOG.error(_LE("Invalid physical network type %(type)s "
- "for network %(net)s. Server terminated!"),
- {'net': network, 'type': type})
- raise SystemExit(1)
- LOG.info(_LI("Physical Network type mappings: %s"),
- self.phys_network_type_maps)
-
- def _parse_network_vlan_ranges(self):
- try:
- self.network_vlan_ranges = plugin_utils.parse_network_vlan_ranges(
- cfg.CONF.MLNX.network_vlan_ranges)
- except Exception as ex:
- LOG.error(_LE("%s. Server terminated!"), ex)
- sys.exit(1)
- LOG.info(_LI("Network VLAN ranges: %s"), self.network_vlan_ranges)
-
- def _extend_network_dict_provider(self, context, network):
- binding = db.get_network_binding(context.session, network['id'])
- network[provider.NETWORK_TYPE] = binding.network_type
- if binding.network_type == svc_constants.TYPE_FLAT:
- network[provider.PHYSICAL_NETWORK] = binding.physical_network
- network[provider.SEGMENTATION_ID] = None
- elif binding.network_type == svc_constants.TYPE_LOCAL:
- network[provider.PHYSICAL_NETWORK] = None
- network[provider.SEGMENTATION_ID] = None
- else:
- network[provider.PHYSICAL_NETWORK] = binding.physical_network
- network[provider.SEGMENTATION_ID] = binding.segmentation_id
-
- def _set_tenant_network_type(self):
- self.tenant_network_type = cfg.CONF.MLNX.tenant_network_type
- if self.tenant_network_type not in [svc_constants.TYPE_VLAN,
- svc_constants.TYPE_LOCAL,
- svc_constants.TYPE_NONE]:
- LOG.error(_LE("Invalid tenant_network_type: %s. "
- "Service terminated!"),
- self.tenant_network_type)
- sys.exit(1)
-
- def _process_provider_create(self, context, attrs):
- network_type = attrs.get(provider.NETWORK_TYPE)
- physical_network = attrs.get(provider.PHYSICAL_NETWORK)
- segmentation_id = attrs.get(provider.SEGMENTATION_ID)
-
- network_type_set = attributes.is_attr_set(network_type)
- physical_network_set = attributes.is_attr_set(physical_network)
- segmentation_id_set = attributes.is_attr_set(segmentation_id)
-
- if not (network_type_set or physical_network_set or
- segmentation_id_set):
- return (None, None, None)
-
- if not network_type_set:
- msg = _("provider:network_type required")
- raise n_exc.InvalidInput(error_message=msg)
- elif network_type == svc_constants.TYPE_FLAT:
- self._process_flat_net(segmentation_id_set)
- segmentation_id = constants.FLAT_VLAN_ID
-
- elif network_type == svc_constants.TYPE_VLAN:
- self._process_vlan_net(segmentation_id, segmentation_id_set)
-
- elif network_type == svc_constants.TYPE_LOCAL:
- self._process_local_net(physical_network_set,
- segmentation_id_set)
- segmentation_id = constants.LOCAL_VLAN_ID
- physical_network = None
-
- else:
- msg = _LE("provider:network_type %s not supported") % network_type
- raise n_exc.InvalidInput(error_message=msg)
- physical_network = self._process_net_type(network_type,
- physical_network,
- physical_network_set)
- return (network_type, physical_network, segmentation_id)
-
- def _process_flat_net(self, segmentation_id_set):
- if segmentation_id_set:
- msg = _LE("provider:segmentation_id specified for flat network")
- raise n_exc.InvalidInput(error_message=msg)
-
- def _process_vlan_net(self, segmentation_id, segmentation_id_set):
- if not segmentation_id_set:
- msg = _LE("provider:segmentation_id required")
- raise n_exc.InvalidInput(error_message=msg)
- if not utils.is_valid_vlan_tag(segmentation_id):
- msg = (_LE("provider:segmentation_id out of range "
- "(%(min_id)s through %(max_id)s)") %
- {'min_id': q_const.MIN_VLAN_TAG,
- 'max_id': q_const.MAX_VLAN_TAG})
- raise n_exc.InvalidInput(error_message=msg)
-
- def _process_local_net(self, physical_network_set, segmentation_id_set):
- if physical_network_set:
- msg = _LE("provider:physical_network specified for local "
- "network")
- raise n_exc.InvalidInput(error_message=msg)
- if segmentation_id_set:
- msg = _LE("provider:segmentation_id specified for local "
- "network")
- raise n_exc.InvalidInput(error_message=msg)
-
- def _process_net_type(self, network_type,
- physical_network,
- physical_network_set):
- if network_type in [svc_constants.TYPE_VLAN,
- svc_constants.TYPE_FLAT]:
- if physical_network_set:
- if physical_network not in self.network_vlan_ranges:
- msg = _LE("Unknown provider:physical_network "
- "%s") % physical_network
- raise n_exc.InvalidInput(error_message=msg)
- elif 'default' in self.network_vlan_ranges:
- physical_network = 'default'
- else:
- msg = _LE("provider:physical_network required")
- raise n_exc.InvalidInput(error_message=msg)
- return physical_network
-
- def _check_port_binding_for_net_type(self, vnic_type, net_type):
- """
- VIF_TYPE_DIRECT is valid only for Ethernet fabric
- """
- if net_type == constants.TYPE_ETH:
- return vnic_type in (constants.VIF_TYPE_DIRECT,
- constants.VIF_TYPE_HOSTDEV)
- elif net_type == constants.TYPE_IB:
- return vnic_type == constants.VIF_TYPE_HOSTDEV
- return False
-
- def _process_port_binding_create(self, context, attrs):
- binding_profile = attrs.get(portbindings.PROFILE)
- binding_profile_set = attributes.is_attr_set(binding_profile)
-
- net_binding = db.get_network_binding(context.session,
- attrs.get('network_id'))
- phy_net = net_binding.physical_network
-
- if not binding_profile_set:
- return self.vnic_type
- if constants.VNIC_TYPE in binding_profile:
- vnic_type = binding_profile[constants.VNIC_TYPE]
- phy_net_type = self.phys_network_type_maps[phy_net]
- if vnic_type in (constants.VIF_TYPE_DIRECT,
- constants.VIF_TYPE_HOSTDEV):
- if self._check_port_binding_for_net_type(vnic_type,
- phy_net_type):
- self.base_binding_dict[portbindings.VIF_TYPE] = vnic_type
- return vnic_type
- else:
- msg = (_LE("Unsupported vnic type %(vnic_type)s "
- "for physical network type %(net_type)s") %
- {'vnic_type': vnic_type, 'net_type': phy_net_type})
- else:
- msg = _LE("Invalid vnic_type on port_create")
- else:
- msg = _LE("vnic_type is not defined in port profile")
- raise n_exc.InvalidInput(error_message=msg)
-
- def create_network(self, context, network):
- (network_type, physical_network,
- vlan_id) = self._process_provider_create(context,
- network['network'])
- session = context.session
- with session.begin(subtransactions=True):
- #set up default security groups
- tenant_id = self._get_tenant_id_for_create(
- context, network['network'])
- self._ensure_default_security_group(context, tenant_id)
-
- if not network_type:
- # tenant network
- network_type = self.tenant_network_type
- if network_type == svc_constants.TYPE_NONE:
- raise n_exc.TenantNetworksDisabled()
- elif network_type == svc_constants.TYPE_VLAN:
- physical_network, vlan_id = db.reserve_network(session)
- else: # TYPE_LOCAL
- vlan_id = constants.LOCAL_VLAN_ID
- else:
- # provider network
- if network_type in [svc_constants.TYPE_VLAN,
- svc_constants.TYPE_FLAT]:
- db.reserve_specific_network(session,
- physical_network,
- vlan_id)
- net = super(MellanoxEswitchPlugin, self).create_network(context,
- network)
- db.add_network_binding(session, net['id'],
- network_type,
- physical_network,
- vlan_id)
-
- self._process_l3_create(context, net, network['network'])
- self._extend_network_dict_provider(context, net)
- # note - exception will rollback entire transaction
- LOG.debug("Created network: %s", net['id'])
- return net
-
- def update_network(self, context, net_id, network):
- LOG.debug("Update network")
- provider._raise_if_updates_provider_attributes(network['network'])
-
- session = context.session
- with session.begin(subtransactions=True):
- net = super(MellanoxEswitchPlugin, self).update_network(context,
- net_id,
- network)
- self._process_l3_update(context, net, network['network'])
- self._extend_network_dict_provider(context, net)
- return net
-
- def delete_network(self, context, net_id):
- LOG.debug("Delete network")
- session = context.session
- with session.begin(subtransactions=True):
- binding = db.get_network_binding(session, net_id)
- self._process_l3_delete(context, net_id)
- super(MellanoxEswitchPlugin, self).delete_network(context,
- net_id)
- if binding.segmentation_id != constants.LOCAL_VLAN_ID:
- db.release_network(session, binding.physical_network,
- binding.segmentation_id,
- self.network_vlan_ranges)
- # the network_binding record is deleted via cascade from
- # the network record, so explicit removal is not necessary
- self.notifier.network_delete(context, net_id)
-
- def get_network(self, context, net_id, fields=None):
- session = context.session
- with session.begin(subtransactions=True):
- net = super(MellanoxEswitchPlugin, self).get_network(context,
- net_id,
- None)
- self._extend_network_dict_provider(context, net)
- return self._fields(net, fields)
-
- def get_networks(self, context, filters=None, fields=None,
- sorts=None, limit=None, marker=None, page_reverse=False):
- session = context.session
- with session.begin(subtransactions=True):
- nets = super(MellanoxEswitchPlugin,
- self).get_networks(context, filters, None, sorts,
- limit, marker, page_reverse)
- for net in nets:
- self._extend_network_dict_provider(context, net)
-
- return [self._fields(net, fields) for net in nets]
-
- def _extend_port_dict_binding(self, context, port):
- port_binding = db.get_port_profile_binding(context.session,
- port['id'])
- if port_binding:
- port[portbindings.VIF_TYPE] = port_binding.vnic_type
- binding = db.get_network_binding(context.session,
- port['network_id'])
- fabric = binding.physical_network
- port[portbindings.PROFILE] = {'physical_network': fabric}
- return port
-
- def create_port(self, context, port):
- LOG.debug("create_port with %s", port)
- session = context.session
- port_data = port['port']
- with session.begin(subtransactions=True):
- self._ensure_default_security_group_on_port(context, port)
- sgids = self._get_security_groups_on_port(context, port)
- # Set port status as 'DOWN'. This will be updated by agent
- port['port']['status'] = q_const.PORT_STATUS_DOWN
-
- vnic_type = self._process_port_binding_create(context,
- port['port'])
-
- port = super(MellanoxEswitchPlugin,
- self).create_port(context, port)
-
- self._process_portbindings_create_and_update(context,
- port_data,
- port)
- db.add_port_profile_binding(context.session, port['id'], vnic_type)
-
- self._process_port_create_security_group(
- context, port, sgids)
- self.notify_security_groups_member_updated(context, port)
- return self._extend_port_dict_binding(context, port)
-
- def get_port(self, context, id, fields=None):
- port = super(MellanoxEswitchPlugin, self).get_port(context,
- id,
- fields)
- self._extend_port_dict_binding(context, port)
- return self._fields(port, fields)
-
- def get_ports(self, context, filters=None, fields=None,
- sorts=None, limit=None, marker=None, page_reverse=False):
- res_ports = []
- ports = super(MellanoxEswitchPlugin,
- self).get_ports(context, filters, fields, sorts,
- limit, marker, page_reverse)
- for port in ports:
- port = self._extend_port_dict_binding(context, port)
- res_ports.append(self._fields(port, fields))
- return res_ports
-
- def update_port(self, context, port_id, port):
- original_port = self.get_port(context, port_id)
- session = context.session
- need_port_update_notify = False
-
- with session.begin(subtransactions=True):
- updated_port = super(MellanoxEswitchPlugin, self).update_port(
- context, port_id, port)
- self._process_portbindings_create_and_update(context,
- port['port'],
- updated_port)
- need_port_update_notify = self.update_security_group_on_port(
- context, port_id, port, original_port, updated_port)
-
- need_port_update_notify |= self.is_security_group_member_updated(
- context, original_port, updated_port)
-
- if original_port['admin_state_up'] != updated_port['admin_state_up']:
- need_port_update_notify = True
-
- if need_port_update_notify:
- binding = db.get_network_binding(context.session,
- updated_port['network_id'])
- self.notifier.port_update(context, updated_port,
- binding.physical_network,
- binding.network_type,
- binding.segmentation_id)
- return self._extend_port_dict_binding(context, updated_port)
-
- def delete_port(self, context, port_id, l3_port_check=True):
- # if needed, check to see if this is a port owned by
- # and l3-router. If so, we should prevent deletion.
- if l3_port_check:
- self.prevent_l3_port_deletion(context, port_id)
-
- session = context.session
- with session.begin(subtransactions=True):
- router_ids = self.disassociate_floatingips(
- context, port_id, do_notify=False)
- port = self.get_port(context, port_id)
- self._delete_port_security_group_bindings(context, port_id)
- super(MellanoxEswitchPlugin, self).delete_port(context, port_id)
-
- # now that we've left db transaction, we are safe to notify
- self.notify_routers_updated(context, router_ids)
- self.notify_security_groups_member_updated(context, port)
-
- @classmethod
- def get_port_from_device(cls, device):
- """Get port according to device.
-
- To maintain compatibility with Linux Bridge L2 Agent for DHCP/L3
- services get device either by linux bridge plugin
- device name convention or by mac address
- """
- port = db.get_port_from_device(
- device[len(q_const.TAP_DEVICE_PREFIX):])
- if port:
- port['device'] = device
- else:
- port = db.get_port_from_device_mac(device)
- if port:
- port['device'] = device
- return port
+++ /dev/null
-# Copyright 2013 Mellanox Technologies, Ltd
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-# implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from oslo.config import cfg
-from oslo import messaging
-
-from neutron.common import constants as q_const
-from neutron.db import api as db_api
-from neutron import manager
-from neutron.openstack.common import log as logging
-from neutron.plugins.mlnx.db import mlnx_db_v2 as db
-
-LOG = logging.getLogger(__name__)
-
-
-class MlnxRpcCallbacks(object):
- # History
- # 1.1 Support Security Group RPC
- # 1.2 Support get_devices_details_list
- target = messaging.Target(version='1.2')
-
- def get_device_details(self, rpc_context, **kwargs):
- """Agent requests device details."""
- agent_id = kwargs.get('agent_id')
- device = kwargs.get('device')
- LOG.debug("Device %(device)s details requested from %(agent_id)s",
- {'device': device, 'agent_id': agent_id})
- plugin = manager.NeutronManager.get_plugin()
- port = plugin.get_port_from_device(device)
- if port:
- binding = db.get_network_binding(db_api.get_session(),
- port['network_id'])
- entry = {'device': device,
- 'physical_network': binding.physical_network,
- 'network_type': binding.network_type,
- 'segmentation_id': binding.segmentation_id,
- 'network_id': port['network_id'],
- 'port_mac': port['mac_address'],
- 'port_id': port['id'],
- 'admin_state_up': port['admin_state_up']}
- if cfg.CONF.AGENT.rpc_support_old_agents:
- entry['vlan_id'] = binding.segmentation_id
- new_status = (q_const.PORT_STATUS_ACTIVE if port['admin_state_up']
- else q_const.PORT_STATUS_DOWN)
- if port['status'] != new_status:
- db.set_port_status(port['id'], new_status)
- else:
- entry = {'device': device}
- LOG.debug("%s can not be found in database", device)
- return entry
-
- def get_devices_details_list(self, rpc_context, **kwargs):
- return [
- self.get_device_details(
- rpc_context,
- device=device,
- **kwargs
- )
- for device in kwargs.pop('devices', [])
- ]
-
- def update_device_down(self, rpc_context, **kwargs):
- """Device no longer exists on agent."""
- agent_id = kwargs.get('agent_id')
- device = kwargs.get('device')
- LOG.debug("Device %(device)s no longer exists on %(agent_id)s",
- {'device': device, 'agent_id': agent_id})
- plugin = manager.NeutronManager.get_plugin()
- port = plugin.get_port_from_device(device)
- if port:
- entry = {'device': device,
- 'exists': True}
- if port['status'] != q_const.PORT_STATUS_DOWN:
- # Set port status to DOWN
- db.set_port_status(port['id'], q_const.PORT_STATUS_DOWN)
- else:
- entry = {'device': device,
- 'exists': False}
- LOG.debug("%s can not be found in database", device)
- return entry
-
- def update_device_up(self, rpc_context, **kwargs):
- """Device is up on agent."""
- agent_id = kwargs.get('agent_id')
- device = kwargs.get('device')
- LOG.debug("Device %(device)s up %(agent_id)s",
- {'device': device, 'agent_id': agent_id})
- plugin = manager.NeutronManager.get_plugin()
- port = plugin.get_port_from_device(device)
- if port:
- if port['status'] != q_const.PORT_STATUS_ACTIVE:
- # Set port status to ACTIVE
- db.set_port_status(port['id'], q_const.PORT_STATUS_ACTIVE)
- else:
- LOG.debug("%s can not be found in database", device)
+++ /dev/null
-# Copyright (c) 2013 OpenStack Foundation
-#
-# 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.tests.unit.mlnx import test_mlnx_plugin
-from neutron.tests.unit.openvswitch import test_agent_scheduler
-
-
-class MlnxAgentSchedulerTestCase(
- test_agent_scheduler.OvsAgentSchedulerTestCase):
- plugin_str = test_mlnx_plugin.PLUGIN_NAME
- l3_plugin = None
-
-
-class MlnxL3AgentNotifierTestCase(
- test_agent_scheduler.OvsL3AgentNotifierTestCase):
- plugin_str = test_mlnx_plugin.PLUGIN_NAME
- l3_plugin = None
-
-
-class MlnxDhcpAgentNotifierTestCase(
- test_agent_scheduler.OvsDhcpAgentNotifierTestCase):
- plugin_str = test_mlnx_plugin.PLUGIN_NAME
def test_defaults(self):
self.assertEqual(2,
cfg.CONF.AGENT.polling_interval)
- self.assertEqual('vlan',
- cfg.CONF.MLNX.tenant_network_type)
- self.assertEqual(1,
- len(cfg.CONF.MLNX.network_vlan_ranges))
- self.assertEqual('eth',
- cfg.CONF.MLNX.physical_network_type)
- self.assertFalse(cfg.CONF.MLNX.physical_network_type_mappings)
self.assertEqual(0,
len(cfg.CONF.ESWITCH.
physical_interface_mappings))
+++ /dev/null
-# Copyright (c) 2013 OpenStack Foundation
-#
-# 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 six import moves
-from testtools import matchers
-
-from neutron.common import exceptions as n_exc
-from neutron.db import api as db
-from neutron.plugins.mlnx.db import mlnx_db_v2 as mlnx_db
-from neutron.tests.unit import test_db_plugin as test_plugin
-from neutron.tests.unit import testlib_api
-
-PHYS_NET = 'physnet1'
-PHYS_NET_2 = 'physnet2'
-NET_TYPE = 'vlan'
-VLAN_MIN = 10
-VLAN_MAX = 19
-VLAN_RANGES = {PHYS_NET: [(VLAN_MIN, VLAN_MAX)]}
-UPDATED_VLAN_RANGES = {PHYS_NET: [(VLAN_MIN + 5, VLAN_MAX + 5)],
- PHYS_NET_2: [(VLAN_MIN + 20, VLAN_MAX + 20)]}
-TEST_NETWORK_ID = 'abcdefghijklmnopqrstuvwxyz'
-
-
-class SegmentationIdAllocationTest(testlib_api.SqlTestCase):
- def setUp(self):
- super(SegmentationIdAllocationTest, self).setUp()
- mlnx_db.sync_network_states(VLAN_RANGES)
- self.session = db.get_session()
-
- def test_sync_segmentationIdAllocation(self):
- self.assertIsNone(mlnx_db.get_network_state(PHYS_NET,
- VLAN_MIN - 1))
- self.assertFalse(mlnx_db.get_network_state(PHYS_NET,
- VLAN_MIN).allocated)
- self.assertFalse(mlnx_db.get_network_state(PHYS_NET,
- VLAN_MIN + 1).allocated)
- self.assertFalse(mlnx_db.get_network_state(PHYS_NET,
- VLAN_MAX - 1).allocated)
- self.assertFalse(mlnx_db.get_network_state(PHYS_NET,
- VLAN_MAX).allocated)
- self.assertIsNone(mlnx_db.get_network_state(PHYS_NET,
- VLAN_MAX + 1))
-
- mlnx_db.sync_network_states(UPDATED_VLAN_RANGES)
-
- self.assertIsNone(mlnx_db.get_network_state(PHYS_NET,
- VLAN_MIN + 5 - 1))
- self.assertFalse(mlnx_db.get_network_state(PHYS_NET,
- VLAN_MIN + 5).allocated)
- self.assertFalse(mlnx_db.get_network_state(PHYS_NET,
- VLAN_MIN + 5 + 1).allocated)
- self.assertFalse(mlnx_db.get_network_state(PHYS_NET,
- VLAN_MAX + 5 - 1).allocated)
- self.assertFalse(mlnx_db.get_network_state(PHYS_NET,
- VLAN_MAX + 5).allocated)
- self.assertIsNone(mlnx_db.get_network_state(PHYS_NET,
- VLAN_MAX + 5 + 1))
-
- self.assertIsNone(mlnx_db.get_network_state(PHYS_NET_2,
- VLAN_MIN + 20 - 1))
- self.assertFalse(mlnx_db.get_network_state(PHYS_NET_2,
- VLAN_MIN + 20).allocated)
- self.assertFalse(
- mlnx_db.get_network_state(PHYS_NET_2,
- VLAN_MIN + 20 + 1).allocated)
- self.assertFalse(
- mlnx_db.get_network_state(PHYS_NET_2,
- VLAN_MAX + 20 - 1).allocated)
- self.assertFalse(mlnx_db.get_network_state(PHYS_NET_2,
- VLAN_MAX + 20).allocated)
- self.assertIsNone(mlnx_db.get_network_state(PHYS_NET_2,
- VLAN_MAX + 20 + 1))
-
- mlnx_db.sync_network_states(VLAN_RANGES)
-
- self.assertIsNone(mlnx_db.get_network_state(PHYS_NET,
- VLAN_MIN - 1))
- self.assertFalse(mlnx_db.get_network_state(PHYS_NET,
- VLAN_MIN).allocated)
- self.assertFalse(mlnx_db.get_network_state(PHYS_NET,
- VLAN_MIN + 1).allocated)
- self.assertFalse(mlnx_db.get_network_state(PHYS_NET,
- VLAN_MAX - 1).allocated)
- self.assertFalse(mlnx_db.get_network_state(PHYS_NET,
- VLAN_MAX).allocated)
- self.assertIsNone(mlnx_db.get_network_state(PHYS_NET,
- VLAN_MAX + 1))
-
- self.assertIsNone(mlnx_db.get_network_state(PHYS_NET_2,
- VLAN_MIN + 20))
- self.assertIsNone(mlnx_db.get_network_state(PHYS_NET_2,
- VLAN_MAX + 20))
-
- def test_segmentationId_pool(self):
- vlan_ids = set()
- for x in moves.xrange(VLAN_MIN, VLAN_MAX + 1):
- physical_network, vlan_id = mlnx_db.reserve_network(self.session)
- self.assertEqual(physical_network, PHYS_NET)
- self.assertThat(vlan_id, matchers.GreaterThan(VLAN_MIN - 1))
- self.assertThat(vlan_id, matchers.LessThan(VLAN_MAX + 1))
- vlan_ids.add(vlan_id)
-
- self.assertRaises(n_exc.NoNetworkAvailable,
- mlnx_db.reserve_network,
- self.session)
- for vlan_id in vlan_ids:
- mlnx_db.release_network(self.session, PHYS_NET,
- vlan_id, VLAN_RANGES)
-
- def test_specific_segmentationId_inside_pool(self):
- vlan_id = VLAN_MIN + 5
- self.assertFalse(mlnx_db.get_network_state(PHYS_NET,
- vlan_id).allocated)
- mlnx_db.reserve_specific_network(self.session, PHYS_NET, vlan_id)
- self.assertTrue(mlnx_db.get_network_state(PHYS_NET,
- vlan_id).allocated)
-
- self.assertRaises(n_exc.VlanIdInUse,
- mlnx_db.reserve_specific_network,
- self.session,
- PHYS_NET,
- vlan_id)
-
- mlnx_db.release_network(self.session, PHYS_NET, vlan_id, VLAN_RANGES)
- self.assertFalse(mlnx_db.get_network_state(PHYS_NET,
- vlan_id).allocated)
-
- def test_specific_segmentationId_outside_pool(self):
- vlan_id = VLAN_MAX + 5
- self.assertIsNone(mlnx_db.get_network_state(PHYS_NET, vlan_id))
- mlnx_db.reserve_specific_network(self.session, PHYS_NET, vlan_id)
- self.assertTrue(mlnx_db.get_network_state(PHYS_NET,
- vlan_id).allocated)
-
- self.assertRaises(n_exc.VlanIdInUse,
- mlnx_db.reserve_specific_network,
- self.session,
- PHYS_NET,
- vlan_id)
-
- mlnx_db.release_network(self.session, PHYS_NET, vlan_id, VLAN_RANGES)
- self.assertIsNone(mlnx_db.get_network_state(PHYS_NET, vlan_id))
-
-
-class NetworkBindingsTest(test_plugin.NeutronDbPluginV2TestCase):
- def setUp(self):
- super(NetworkBindingsTest, self).setUp()
- self.session = db.get_session()
-
- def test_add_network_binding(self):
- with self.network() as network:
- TEST_NETWORK_ID = network['network']['id']
- self.assertIsNone(mlnx_db.get_network_binding(self.session,
- TEST_NETWORK_ID))
- mlnx_db.add_network_binding(self.session,
- TEST_NETWORK_ID,
- NET_TYPE,
- PHYS_NET,
- 1234)
- binding = mlnx_db.get_network_binding(self.session,
- TEST_NETWORK_ID)
- self.assertIsNotNone(binding)
- self.assertEqual(binding.network_id, TEST_NETWORK_ID)
- self.assertEqual(binding.network_type, NET_TYPE)
- self.assertEqual(binding.physical_network, PHYS_NET)
- self.assertEqual(binding.segmentation_id, 1234)
-
- self.assertTrue(repr(binding))
-
-
-class PortProfileBindingTest(test_plugin.NeutronDbPluginV2TestCase):
- def setUp(self):
- super(PortProfileBindingTest, self).setUp()
- self.session = db.get_session()
-
- def test_add_port_profile_binding(self):
- with self.port() as port:
- TEST_PORT_ID = port['port']['id']
- VNIC_TYPE = 'normal'
-
- self.assertIsNone(mlnx_db.get_port_profile_binding(self.session,
- TEST_PORT_ID))
- mlnx_db.add_port_profile_binding(self.session,
- TEST_PORT_ID,
- VNIC_TYPE)
- binding = mlnx_db.get_port_profile_binding(self.session,
- TEST_PORT_ID)
- self.assertIsNotNone(binding)
- self.assertEqual(binding.port_id, TEST_PORT_ID)
- self.assertEqual(binding.vnic_type, VNIC_TYPE)
-
- self.assertTrue(repr(binding))
+++ /dev/null
-# Copyright (c) 2013 OpenStack Foundation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-# implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from oslo.config import cfg
-from webob import exc
-
-from neutron.extensions import portbindings
-from neutron.plugins.mlnx.common import constants
-from neutron.tests.unit import _test_extension_portbindings as test_bindings
-from neutron.tests.unit import test_db_plugin as test_plugin
-from neutron.tests.unit import test_security_groups_rpc as test_sg_rpc
-
-
-PLUGIN_NAME = ('neutron.plugins.mlnx.mlnx_plugin.MellanoxEswitchPlugin')
-
-
-class MlnxPluginV2TestCase(test_plugin.NeutronDbPluginV2TestCase):
- _plugin_name = PLUGIN_NAME
-
- def setUp(self):
- super(MlnxPluginV2TestCase, self).setUp(self._plugin_name)
- self.port_create_status = 'DOWN'
-
-
-class TestMlnxBasicGet(test_plugin.TestBasicGet, MlnxPluginV2TestCase):
- pass
-
-
-class TestMlnxV2HTTPResponse(test_plugin.TestV2HTTPResponse,
- MlnxPluginV2TestCase):
- pass
-
-
-class TestMlnxPortsV2(test_plugin.TestPortsV2,
- MlnxPluginV2TestCase):
- pass
-
-
-class TestMlnxNetworksV2(test_plugin.TestNetworksV2, MlnxPluginV2TestCase):
- pass
-
-
-class TestMlnxPortBinding(MlnxPluginV2TestCase,
- test_bindings.PortBindingsTestCase):
- VIF_TYPE = constants.VIF_TYPE_DIRECT
- ENABLE_SG = False
- HAS_PORT_FILTER = False
-
- def setUp(self, firewall_driver=None):
- cfg.CONF.set_override(
- 'enable_security_group', self.ENABLE_SG,
- group='SECURITYGROUP')
- super(TestMlnxPortBinding, self).setUp()
-
- def _check_default_port_binding_profole(self, port,
- expected_vif_type=None):
- if expected_vif_type is None:
- expected_vif_type = constants.VIF_TYPE_DIRECT
- p = port['port']
- self.assertIn('id', p)
- self.assertEqual(expected_vif_type, p[portbindings.VIF_TYPE])
- self.assertEqual({'physical_network': 'default'},
- p[portbindings.PROFILE])
-
- def test_create_port_no_binding_profile(self):
- with self.port() as port:
- self._check_default_port_binding_profole(port)
-
- def test_create_port_binding_profile_none(self):
- profile_arg = {portbindings.PROFILE: None}
- with self.port(arg_list=(portbindings.PROFILE,),
- **profile_arg) as port:
- self._check_default_port_binding_profole(port)
-
- def test_create_port_binding_profile_vif_type(self):
- for vif_type in [constants.VIF_TYPE_HOSTDEV,
- constants.VIF_TYPE_DIRECT]:
- profile_arg = {portbindings.PROFILE:
- {constants.VNIC_TYPE: vif_type}}
- with self.port(arg_list=(portbindings.PROFILE,),
- **profile_arg) as port:
- self._check_default_port_binding_profole(
- port, expected_vif_type=vif_type)
- self._delete('ports', port['port']['id'])
- self._delete('networks', port['port']['network_id'])
-
- def test_create_port_binding_profile_with_empty_dict(self):
- profile_arg = {portbindings.PROFILE: {}}
- try:
- with self.port(arg_list=(portbindings.PROFILE,),
- expected_res_status=400, **profile_arg):
- pass
- except exc.HTTPClientError:
- pass
-
-
-class TestMlnxPortBindingNoSG(TestMlnxPortBinding):
- HAS_PORT_FILTER = False
- ENABLE_SG = False
- FIREWALL_DRIVER = test_sg_rpc.FIREWALL_NOOP_DRIVER
-
-
-class TestMlnxPortBindingHost(
- MlnxPluginV2TestCase,
- test_bindings.PortBindingsHostTestCaseMixin):
- pass
+++ /dev/null
-# Copyright (c) 2014 OpenStack Foundation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-# implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import mock
-from oslo.config import cfg
-
-#NOTE this import loads tests required options
-from neutron.plugins.mlnx.common import config # noqa
-from neutron.plugins.mlnx.common import constants
-from neutron.plugins.mlnx import mlnx_plugin
-from neutron.tests.unit import testlib_api
-
-
-class TestMlnxPluginConfig(testlib_api.SqlTestCase):
- expected_vlan_mappings = {'physnet1': [(1, 1000)],
- 'physnet2': [(1, 1000)]}
- expected_network_types = {'physnet1': constants.TYPE_ETH,
- 'physnet2': constants.TYPE_IB}
- config_vlan_ranges = ['physnet1:1:1000', 'physnet2:1:1000']
- config_network_types = ['physnet1:eth', 'physnet2:ib']
-
- def setUp(self):
- super(TestMlnxPluginConfig, self).setUp()
- cfg.CONF.set_override(group='MLNX',
- name='network_vlan_ranges',
- override=self.config_vlan_ranges)
-
- def _create_mlnx_plugin(self):
- with mock.patch('neutron.plugins.mlnx.db.mlnx_db_v2'):
- return mlnx_plugin.MellanoxEswitchPlugin()
-
- def _assert_expected_config(self):
- plugin = self._create_mlnx_plugin()
- self.assertEqual(plugin.network_vlan_ranges,
- self.expected_vlan_mappings)
- self.assertEqual(plugin.phys_network_type_maps,
- self.expected_network_types)
-
- def test_vlan_ranges_with_network_type(self):
- cfg.CONF.set_override(group='MLNX',
- name='physical_network_type_mappings',
- override=self.config_network_types)
- self._assert_expected_config()
-
- def test_vlan_ranges_partial_network_type(self):
- cfg.CONF.set_override(group='MLNX',
- name='physical_network_type_mappings',
- override=self.config_network_types[:1])
- cfg.CONF.set_override(group='MLNX',
- name='physical_network_type',
- override=constants.TYPE_IB)
- self._assert_expected_config()
-
- def test_vlan_ranges_no_network_type(self):
- cfg.CONF.set_override(group='MLNX',
- name='physical_network_type',
- override=constants.TYPE_IB)
- cfg.CONF.set_override(group='MLNX',
- name='physical_network_type_mappings',
- override=[])
- self.expected_network_types.update({'physnet1': constants.TYPE_IB})
- self._assert_expected_config()
- self.expected_network_types.update({'physnet1': constants.TYPE_ETH})
-
- def test_parse_physical_network_mappings_invalid_type(self):
- cfg.CONF.set_override(group='MLNX',
- name='physical_network_type_mappings',
- override=['physnet:invalid-type'])
- self.assertRaises(SystemExit, self._create_mlnx_plugin)
-
- def test_invalid_network_type(self):
- cfg.CONF.set_override(group='MLNX',
- name='physical_network_type',
- override='invalid-type')
- self.assertRaises(SystemExit, self._create_mlnx_plugin)
+++ /dev/null
-# Copyright (c) 2013 OpenStack Foundation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-# implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import mock
-import webob.exc
-
-from neutron.api.v2 import attributes
-from neutron.extensions import securitygroup as ext_sg
-from neutron.plugins.mlnx.db import mlnx_db_v2 as mlnx_db
-from neutron.tests.unit import test_extension_security_group as test_sg
-from neutron.tests.unit import test_security_groups_rpc as test_sg_rpc
-
-
-PLUGIN_NAME = ('neutron.plugins.mlnx.'
- 'mlnx_plugin.MellanoxEswitchPlugin')
-NOTIFIER = ('neutron.plugins.mlnx.'
- 'agent_notify_api.AgentNotifierApi')
-
-
-class MlnxSecurityGroupsTestCase(test_sg.SecurityGroupDBTestCase):
- _plugin_name = PLUGIN_NAME
-
- def setUp(self, plugin=None):
- test_sg_rpc.set_firewall_driver(test_sg_rpc.FIREWALL_IPTABLES_DRIVER)
- notifier_p = mock.patch(NOTIFIER)
- notifier_cls = notifier_p.start()
- self.notifier = mock.Mock()
- notifier_cls.return_value = self.notifier
- self._attribute_map_bk_ = {}
- for item in attributes.RESOURCE_ATTRIBUTE_MAP:
- self._attribute_map_bk_[item] = (attributes.
- RESOURCE_ATTRIBUTE_MAP[item].
- copy())
- super(MlnxSecurityGroupsTestCase, self).setUp(PLUGIN_NAME)
-
- def tearDown(self):
- attributes.RESOURCE_ATTRIBUTE_MAP = self._attribute_map_bk_
- super(MlnxSecurityGroupsTestCase, self).tearDown()
-
-
-class TestMlnxSecurityGroups(MlnxSecurityGroupsTestCase,
- test_sg.TestSecurityGroups,
- test_sg_rpc.SGNotificationTestMixin):
- pass
-
-
-class TestMlnxSecurityGroupsDB(MlnxSecurityGroupsTestCase):
- def test_security_group_get_port_from_device(self):
- with self.network() as n:
- with self.subnet(n):
- with self.security_group() as sg:
- security_group_id = sg['security_group']['id']
- res = self._create_port(self.fmt, n['network']['id'])
- port = self.deserialize(self.fmt, res)
- fixed_ips = port['port']['fixed_ips']
- data = {'port': {'fixed_ips': fixed_ips,
- 'name': port['port']['name'],
- ext_sg.SECURITYGROUPS:
- [security_group_id]}}
-
- req = self.new_update_request('ports', data,
- port['port']['id'])
- if res.status_int >= 400:
- raise webob.exc.HTTPClientError(code=res.status_int)
- res = self.deserialize(self.fmt,
- req.get_response(self.api))
- port_id = res['port']['id']
- device_id = port_id[:8]
- port_dict = mlnx_db.get_port_from_device(device_id)
- self.assertEqual(port_id, port_dict['id'])
- self.assertEqual([security_group_id],
- port_dict[ext_sg.SECURITYGROUPS])
- self.assertEqual([], port_dict['security_group_rules'])
- self.assertEqual([fixed_ips[0]['ip_address']],
- port_dict['fixed_ips'])
- self._delete('ports', port['port']['id'])
-
- def test_security_group_get_port_from_device_with_no_port(self):
- port_dict = mlnx_db.get_port_from_device('bad_device_id')
- self.assertIsNone(port_dict)
+++ /dev/null
-# Copyright 2013 Mellanox Technologies, Ltd
-#
-# 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.
-
-"""
-Unit Tests for Mellanox RPC (major reuse of linuxbridge rpc unit tests)
-"""
-
-import contextlib
-import mock
-
-from oslo.config import cfg
-from oslo_context import context as oslo_context
-
-from neutron.agent import rpc as agent_rpc
-from neutron.common import topics
-from neutron.plugins.mlnx import agent_notify_api
-from neutron.tests import base
-
-
-class rpcApiTestCase(base.BaseTestCase):
-
- def _test_mlnx_api(self, rpcapi, topic, method, rpc_method, **kwargs):
- ctxt = oslo_context.RequestContext('fake_user', 'fake_project')
- expected_retval = 'foo' if rpc_method == 'call' else None
- expected_version = kwargs.pop('version', None)
- fanout = kwargs.pop('fanout', False)
-
- with contextlib.nested(
- mock.patch.object(rpcapi.client, rpc_method),
- mock.patch.object(rpcapi.client, 'prepare'),
- ) as (
- rpc_mock, prepare_mock
- ):
- prepare_mock.return_value = rpcapi.client
- rpc_mock.return_value = expected_retval
- retval = getattr(rpcapi, method)(ctxt, **kwargs)
-
- prepare_args = {}
- if expected_version:
- prepare_args['version'] = expected_version
- if fanout:
- prepare_args['fanout'] = True
- if topic:
- prepare_args['topic'] = topic
- prepare_mock.assert_called_once_with(**prepare_args)
-
- if method == 'port_update':
- kwargs['segmentation_id'] = kwargs['vlan_id']
- if not cfg.CONF.AGENT.rpc_support_old_agents:
- del kwargs['vlan_id']
-
- self.assertEqual(retval, expected_retval)
- rpc_mock.assert_called_once_with(ctxt, method, **kwargs)
-
- def test_delete_network(self):
- rpcapi = agent_notify_api.AgentNotifierApi(topics.AGENT)
- self._test_mlnx_api(
- rpcapi,
- topics.get_topic_name(topics.AGENT,
- topics.NETWORK,
- topics.DELETE),
- 'network_delete', rpc_method='cast', fanout=True,
- network_id='fake_request_spec')
-
- def test_port_update(self):
- cfg.CONF.set_override('rpc_support_old_agents', False, 'AGENT')
- rpcapi = agent_notify_api.AgentNotifierApi(topics.AGENT)
- self._test_mlnx_api(
- rpcapi,
- topics.get_topic_name(topics.AGENT,
- topics.PORT,
- topics.UPDATE),
- 'port_update', rpc_method='cast', fanout=True,
- port='fake_port',
- network_type='vlan',
- physical_network='fake_net',
- vlan_id='fake_vlan_id')
-
- def test_port_update_old_agent(self):
- cfg.CONF.set_override('rpc_support_old_agents', True, 'AGENT')
- rpcapi = agent_notify_api.AgentNotifierApi(topics.AGENT)
- self._test_mlnx_api(
- rpcapi,
- topics.get_topic_name(topics.AGENT,
- topics.PORT,
- topics.UPDATE),
- 'port_update', rpc_method='cast', fanout=True,
- port='fake_port',
- network_type='vlan',
- physical_network='fake_net',
- vlan_id='fake_vlan_id')
-
- def test_device_details(self):
- rpcapi = agent_rpc.PluginApi(topics.PLUGIN)
- self._test_mlnx_api(rpcapi, None,
- 'get_device_details', rpc_method='call',
- device='fake_device',
- agent_id='fake_agent_id',
- host='fake_host')
-
- def test_devices_details_list(self):
- rpcapi = agent_rpc.PluginApi(topics.PLUGIN)
- self._test_mlnx_api(rpcapi, None,
- 'get_devices_details_list', rpc_method='call',
- devices=['fake_device1', 'fake_device1'],
- agent_id='fake_agent_id', host='fake_host',
- version='1.3')
-
- def test_update_device_down(self):
- rpcapi = agent_rpc.PluginApi(topics.PLUGIN)
- self._test_mlnx_api(rpcapi, None,
- 'update_device_down', rpc_method='call',
- device='fake_device',
- agent_id='fake_agent_id',
- host='fake_host')
-
- def test_update_device_up(self):
- rpcapi = agent_rpc.PluginApi(topics.PLUGIN)
- self._test_mlnx_api(rpcapi, None,
- 'update_device_up', rpc_method='call',
- device='fake_device',
- agent_id='fake_agent_id',
- host='fake_host')
ibm = neutron.plugins.ibm.sdnve_neutron_plugin:SdnvePluginV2
midonet = neutron.plugins.midonet.plugin:MidonetPluginV2
ml2 = neutron.plugins.ml2.plugin:Ml2Plugin
- mlnx = neutron.plugins.mlnx.mlnx_plugin:MellanoxEswitchPlugin
nec = neutron.plugins.nec.nec_plugin:NECPluginV2
nuage = neutron.plugins.nuage.plugin:NuagePlugin
metaplugin = neutron.plugins.metaplugin.meta_neutron_plugin:MetaPluginV2