Merge "Remove mlnx plugin"
This commit is contained in:
commit
b8c359a4fd
@ -1,38 +1,3 @@
|
||||
[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
|
||||
@ -67,13 +32,3 @@
|
||||
[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
|
||||
|
@ -0,0 +1,75 @@
|
||||
# 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'),
|
||||
)
|
@ -1 +1 @@
|
||||
408cfbf6923c
|
||||
28c0ffb8ebbd
|
@ -64,7 +64,6 @@ from neutron.plugins.ml2.drivers import type_gre # noqa
|
||||
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
|
||||
|
@ -1,8 +1,4 @@
|
||||
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
|
@ -1,62 +0,0 @@
|
||||
# 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)
|
@ -18,27 +18,8 @@ from oslo.config import cfg
|
||||
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,
|
||||
@ -66,12 +47,9 @@ agent_opts = [
|
||||
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)
|
||||
|
@ -1,256 +0,0 @@
|
||||
# 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)
|
@ -1,86 +0,0 @@
|
||||
# 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)
|
@ -1,541 +0,0 @@
|
||||
# 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
|
@ -1,107 +0,0 @@
|
||||
# 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)
|
@ -1,34 +0,0 @@
|
||||
# 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
|
@ -25,13 +25,6 @@ class ConfigurationTest(base.BaseTestCase):
|
||||
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))
|
||||
|
@ -1,204 +0,0 @@
|
||||
# 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))
|
@ -1,118 +0,0 @@
|
||||
# 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
|
@ -1,87 +0,0 @@
|
||||
# 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)
|
@ -1,92 +0,0 @@
|
||||
# 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)
|
@ -1,135 +0,0 @@
|
||||
# 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')
|
@ -133,7 +133,6 @@ neutron.core_plugins =
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user