Export portinfo thru portbinding ext in NEC plugin
blueprint nec-port-binding * Add host-id support in port-binding extension. * Expose portinfo thourgh binding:profile attr in a port. portinfo is a mapping between neutron port id and OpenFlow switch physical information (datapath_id and port_no) It changes the following in portinfo db model * Add cascade on delete to delete an associated portinfo when deleting the port. * Use joined query for portinfo model to retrieve an associated portinfo when querying a port. Change-Id: Id88d93dc0770a1290714436324b1b53c0b023eeb
This commit is contained in:
parent
795155c743
commit
99310d5174
@ -0,0 +1,65 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
#
|
||||||
|
# Copyright 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.
|
||||||
|
#
|
||||||
|
|
||||||
|
"""NEC Port Binding
|
||||||
|
|
||||||
|
Revision ID: 2a3bae1ceb8
|
||||||
|
Revises: 46a0efbd8f0
|
||||||
|
Create Date: 2013-08-22 11:09:19.955386
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '2a3bae1ceb8'
|
||||||
|
down_revision = '46a0efbd8f0'
|
||||||
|
|
||||||
|
# Change to ['*'] if this migration applies to all plugins
|
||||||
|
|
||||||
|
migration_for_plugins = [
|
||||||
|
'neutron.plugins.nec.nec_plugin.NECPluginV2'
|
||||||
|
]
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from neutron.db import migration
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade(active_plugins=None, options=None):
|
||||||
|
if not migration.should_run(active_plugins, migration_for_plugins):
|
||||||
|
return
|
||||||
|
|
||||||
|
op.create_table(
|
||||||
|
'portbindingports',
|
||||||
|
sa.Column('port_id', sa.String(length=36), nullable=False),
|
||||||
|
sa.Column('host', sa.String(length=255), nullable=False),
|
||||||
|
sa.ForeignKeyConstraint(['port_id'], ['ports.id'], ondelete='CASCADE'),
|
||||||
|
sa.PrimaryKeyConstraint('port_id')
|
||||||
|
)
|
||||||
|
op.create_foreign_key(
|
||||||
|
'portinfos_ibfk_1',
|
||||||
|
source='portinfos', referent='ports',
|
||||||
|
local_cols=['id'], remote_cols=['id'],
|
||||||
|
ondelete='CASCADE')
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade(active_plugins=None, options=None):
|
||||||
|
if not migration.should_run(active_plugins, migration_for_plugins):
|
||||||
|
return
|
||||||
|
|
||||||
|
op.drop_constraint('portinfos_ibfk_1', 'portinfos', 'foreignkey')
|
||||||
|
op.drop_table('portbindingports')
|
@ -33,3 +33,14 @@ class OFCConsistencyBroken(qexc.NeutronException):
|
|||||||
|
|
||||||
class PortInfoNotFound(qexc.NotFound):
|
class PortInfoNotFound(qexc.NotFound):
|
||||||
message = _("PortInfo %(id)s could not be found")
|
message = _("PortInfo %(id)s could not be found")
|
||||||
|
|
||||||
|
|
||||||
|
class ProfilePortInfoInvalidDataPathId(qexc.InvalidInput):
|
||||||
|
message = _('Invalid input for operation: '
|
||||||
|
'portinfo:datapath_id should be a hex string '
|
||||||
|
'with at most 8 bytes')
|
||||||
|
|
||||||
|
|
||||||
|
class ProfilePortInfoInvalidPortNo(qexc.InvalidInput):
|
||||||
|
message = _('Invalid input for operation: '
|
||||||
|
'portinfo:port_no should be [0:65535]')
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
# @author: Ryota MIBU
|
# @author: Ryota MIBU
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy import orm
|
||||||
|
|
||||||
from neutron.db import model_base
|
from neutron.db import model_base
|
||||||
from neutron.db import models_v2
|
from neutron.db import models_v2
|
||||||
@ -74,9 +75,17 @@ class OFCFilter(model_base.BASEV2, models_v2.HasId, HasNeutronId):
|
|||||||
"""Represents a Filter on OpenFlow Network/Controller."""
|
"""Represents a Filter on OpenFlow Network/Controller."""
|
||||||
|
|
||||||
|
|
||||||
class PortInfo(model_base.BASEV2, models_v2.HasId):
|
class PortInfo(model_base.BASEV2):
|
||||||
"""Represents a Virtual Interface."""
|
"""Represents a Virtual Interface."""
|
||||||
|
id = sa.Column(sa.String(36),
|
||||||
|
sa.ForeignKey('ports.id', ondelete="CASCADE"),
|
||||||
|
primary_key=True)
|
||||||
datapath_id = sa.Column(sa.String(36), nullable=False)
|
datapath_id = sa.Column(sa.String(36), nullable=False)
|
||||||
port_no = sa.Column(sa.Integer, nullable=False)
|
port_no = sa.Column(sa.Integer, nullable=False)
|
||||||
vlan_id = sa.Column(sa.Integer, nullable=False)
|
vlan_id = sa.Column(sa.Integer, nullable=False)
|
||||||
mac = sa.Column(sa.String(32), nullable=False)
|
mac = sa.Column(sa.String(32), nullable=False)
|
||||||
|
port = orm.relationship(
|
||||||
|
models_v2.Port,
|
||||||
|
backref=orm.backref("portinfo",
|
||||||
|
lazy='joined', uselist=False,
|
||||||
|
cascade='delete'))
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
from neutron.agent import securitygroups_rpc as sg_rpc
|
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 dhcp_rpc_agent_api
|
||||||
from neutron.api.rpc.agentnotifiers import l3_rpc_agent_api
|
from neutron.api.rpc.agentnotifiers import l3_rpc_agent_api
|
||||||
|
from neutron.api.v2 import attributes as attrs
|
||||||
from neutron.common import constants as const
|
from neutron.common import constants as const
|
||||||
from neutron.common import exceptions as q_exc
|
from neutron.common import exceptions as q_exc
|
||||||
from neutron.common import rpc as q_rpc
|
from neutron.common import rpc as q_rpc
|
||||||
@ -31,6 +32,7 @@ from neutron.db import extraroute_db
|
|||||||
from neutron.db import l3_gwmode_db
|
from neutron.db import l3_gwmode_db
|
||||||
from neutron.db import l3_rpc_base
|
from neutron.db import l3_rpc_base
|
||||||
from neutron.db import portbindings_base
|
from neutron.db import portbindings_base
|
||||||
|
from neutron.db import portbindings_db
|
||||||
from neutron.db import quota_db # noqa
|
from neutron.db import quota_db # noqa
|
||||||
from neutron.db import securitygroups_rpc_base as sg_db_rpc
|
from neutron.db import securitygroups_rpc_base as sg_db_rpc
|
||||||
from neutron.extensions import portbindings
|
from neutron.extensions import portbindings
|
||||||
@ -55,7 +57,7 @@ class NECPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
agentschedulers_db.L3AgentSchedulerDbMixin,
|
agentschedulers_db.L3AgentSchedulerDbMixin,
|
||||||
agentschedulers_db.DhcpAgentSchedulerDbMixin,
|
agentschedulers_db.DhcpAgentSchedulerDbMixin,
|
||||||
packet_filter.PacketFilterMixin,
|
packet_filter.PacketFilterMixin,
|
||||||
portbindings_base.PortBindingBaseMixin):
|
portbindings_db.PortBindingMixin):
|
||||||
"""NECPluginV2 controls an OpenFlow Controller.
|
"""NECPluginV2 controls an OpenFlow Controller.
|
||||||
|
|
||||||
The Neutron NECPluginV2 maps L2 logical networks to L2 virtualized networks
|
The Neutron NECPluginV2 maps L2 logical networks to L2 virtualized networks
|
||||||
@ -342,6 +344,111 @@ class NECPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
'security-group' in self.supported_extension_aliases}}
|
'security-group' in self.supported_extension_aliases}}
|
||||||
return binding
|
return binding
|
||||||
|
|
||||||
|
def _extend_port_dict_binding_portinfo(self, port_res, portinfo):
|
||||||
|
if portinfo:
|
||||||
|
port_res[portbindings.PROFILE] = {
|
||||||
|
'portinfo:datapath_id': portinfo['datapath_id'],
|
||||||
|
'portinfo:port_no': portinfo['port_no'],
|
||||||
|
}
|
||||||
|
elif portbindings.PROFILE in port_res:
|
||||||
|
del port_res[portbindings.PROFILE]
|
||||||
|
|
||||||
|
def _validate_portinfo(self, profile):
|
||||||
|
key_specs = {
|
||||||
|
'portinfo:datapath_id': {'type:string': None, 'required': True},
|
||||||
|
'portinfo:port_no': {'type:non_negative': None, 'required': True,
|
||||||
|
'convert_to': attrs.convert_to_int}
|
||||||
|
}
|
||||||
|
msg = attrs._validate_dict_or_empty(profile, key_specs=key_specs)
|
||||||
|
if msg:
|
||||||
|
raise q_exc.InvalidInput(error_message=msg)
|
||||||
|
|
||||||
|
datapath_id = profile.get('portinfo:datapath_id')
|
||||||
|
port_no = profile.get('portinfo:port_no')
|
||||||
|
try:
|
||||||
|
dpid = int(datapath_id, 16)
|
||||||
|
except ValueError:
|
||||||
|
raise nexc.ProfilePortInfoInvalidDataPathId()
|
||||||
|
if dpid > 0xffffffffffffffffL:
|
||||||
|
raise nexc.ProfilePortInfoInvalidDataPathId()
|
||||||
|
# Make sure dpid is a hex string beginning with 0x.
|
||||||
|
dpid = hex(dpid)
|
||||||
|
|
||||||
|
if int(port_no) > 65535:
|
||||||
|
raise nexc.ProfilePortInfoInvalidPortNo()
|
||||||
|
|
||||||
|
return {'datapath_id': dpid, 'port_no': port_no}
|
||||||
|
|
||||||
|
def _process_portbindings_portinfo_create(self, context, port_data, port):
|
||||||
|
"""Add portinfo according to bindings:profile in create_port().
|
||||||
|
|
||||||
|
:param context: neutron api request context
|
||||||
|
:param port_data: port attributes passed in PUT request
|
||||||
|
:param port: port attributes to be returned
|
||||||
|
"""
|
||||||
|
profile = port_data.get(portbindings.PROFILE)
|
||||||
|
# If portbindings.PROFILE is None, unspecified or an empty dict
|
||||||
|
# it is regarded that portbinding.PROFILE is not set.
|
||||||
|
profile_set = attrs.is_attr_set(profile) and profile
|
||||||
|
if profile_set:
|
||||||
|
portinfo = self._validate_portinfo(profile)
|
||||||
|
portinfo['mac'] = port['mac_address']
|
||||||
|
ndb.add_portinfo(context.session, port['id'], **portinfo)
|
||||||
|
else:
|
||||||
|
portinfo = None
|
||||||
|
self._extend_port_dict_binding_portinfo(port, portinfo)
|
||||||
|
|
||||||
|
def _process_portbindings_portinfo_update(self, context, port_data, port):
|
||||||
|
"""Update portinfo according to bindings:profile in update_port().
|
||||||
|
|
||||||
|
:param context: neutron api request context
|
||||||
|
:param port_data: port attributes passed in PUT request
|
||||||
|
:param port: port attributes to be returned
|
||||||
|
:returns: 'ADD', 'MOD', 'DEL' or None
|
||||||
|
"""
|
||||||
|
if portbindings.PROFILE not in port_data:
|
||||||
|
return
|
||||||
|
profile = port_data.get(portbindings.PROFILE)
|
||||||
|
# If binding:profile is None or an empty dict,
|
||||||
|
# it means binding:.profile needs to be cleared.
|
||||||
|
# TODO(amotoki): Allow Make None in binding:profile in
|
||||||
|
# the API layer. See LP bug #1220011.
|
||||||
|
profile_set = attrs.is_attr_set(profile) and profile
|
||||||
|
cur_portinfo = ndb.get_portinfo(context.session, port['id'])
|
||||||
|
if profile_set:
|
||||||
|
portinfo = self._validate_portinfo(profile)
|
||||||
|
portinfo_changed = 'ADD'
|
||||||
|
if cur_portinfo:
|
||||||
|
if (portinfo['datapath_id'] == cur_portinfo.datapath_id and
|
||||||
|
portinfo['port_no'] == cur_portinfo.port_no):
|
||||||
|
return
|
||||||
|
ndb.del_portinfo(context.session, port['id'])
|
||||||
|
portinfo_changed = 'MOD'
|
||||||
|
portinfo['mac'] = port['mac_address']
|
||||||
|
ndb.add_portinfo(context.session, port['id'], **portinfo)
|
||||||
|
elif cur_portinfo:
|
||||||
|
portinfo_changed = 'DEL'
|
||||||
|
portinfo = None
|
||||||
|
ndb.del_portinfo(context.session, port['id'])
|
||||||
|
self._extend_port_dict_binding_portinfo(port, portinfo)
|
||||||
|
return portinfo_changed
|
||||||
|
|
||||||
|
def extend_port_dict_binding(self, port_res, port_db):
|
||||||
|
super(NECPluginV2, self).extend_port_dict_binding(port_res, port_db)
|
||||||
|
self._extend_port_dict_binding_portinfo(port_res, port_db.portinfo)
|
||||||
|
|
||||||
|
def _process_portbindings_create(self, context, port_data, port):
|
||||||
|
super(NECPluginV2, self)._process_portbindings_create_and_update(
|
||||||
|
context, port_data, port)
|
||||||
|
self._process_portbindings_portinfo_create(context, port_data, port)
|
||||||
|
|
||||||
|
def _process_portbindings_update(self, context, port_data, port):
|
||||||
|
super(NECPluginV2, self)._process_portbindings_create_and_update(
|
||||||
|
context, port_data, port)
|
||||||
|
portinfo_changed = self._process_portbindings_portinfo_update(
|
||||||
|
context, port_data, port)
|
||||||
|
return portinfo_changed
|
||||||
|
|
||||||
def create_port(self, context, port):
|
def create_port(self, context, port):
|
||||||
"""Create a new port entry on DB, then try to activate it."""
|
"""Create a new port entry on DB, then try to activate it."""
|
||||||
LOG.debug(_("NECPluginV2.create_port() called, port=%s ."), port)
|
LOG.debug(_("NECPluginV2.create_port() called, port=%s ."), port)
|
||||||
@ -353,15 +460,50 @@ class NECPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
self._ensure_default_security_group_on_port(context, port)
|
self._ensure_default_security_group_on_port(context, port)
|
||||||
sgids = self._get_security_groups_on_port(context, port)
|
sgids = self._get_security_groups_on_port(context, port)
|
||||||
port = super(NECPluginV2, self).create_port(context, port)
|
port = super(NECPluginV2, self).create_port(context, port)
|
||||||
self._process_portbindings_create_and_update(context,
|
self._process_portbindings_create(context, port_data, port)
|
||||||
port_data,
|
|
||||||
port)
|
|
||||||
self._process_port_create_security_group(
|
self._process_port_create_security_group(
|
||||||
context, port, sgids)
|
context, port, sgids)
|
||||||
self.notify_security_groups_member_updated(context, port)
|
self.notify_security_groups_member_updated(context, port)
|
||||||
|
|
||||||
return self.activate_port_if_ready(context, port)
|
return self.activate_port_if_ready(context, port)
|
||||||
|
|
||||||
|
def _update_ofc_port_if_required(self, context, old_port, new_port,
|
||||||
|
portinfo_changed):
|
||||||
|
def get_ofport_exist(port):
|
||||||
|
return (port['admin_state_up'] and
|
||||||
|
bool(port.get(portbindings.PROFILE)))
|
||||||
|
|
||||||
|
# Determine it is required to update OFC port
|
||||||
|
need_add = False
|
||||||
|
need_del = False
|
||||||
|
need_packet_filter_update = False
|
||||||
|
|
||||||
|
old_ofport_exist = get_ofport_exist(old_port)
|
||||||
|
new_ofport_exist = get_ofport_exist(new_port)
|
||||||
|
|
||||||
|
if old_port['admin_state_up'] != new_port['admin_state_up']:
|
||||||
|
if new_port['admin_state_up']:
|
||||||
|
need_add |= new_ofport_exist
|
||||||
|
else:
|
||||||
|
need_del |= old_ofport_exist
|
||||||
|
|
||||||
|
if portinfo_changed:
|
||||||
|
if portinfo_changed in ['DEL', 'MOD']:
|
||||||
|
need_del |= old_ofport_exist
|
||||||
|
if portinfo_changed in ['ADD', 'MOD']:
|
||||||
|
need_add |= new_ofport_exist
|
||||||
|
need_packet_filter_update |= True
|
||||||
|
|
||||||
|
# Update OFC port if required
|
||||||
|
if need_del:
|
||||||
|
self.deactivate_port(context, new_port)
|
||||||
|
if need_packet_filter_update:
|
||||||
|
self.deactivate_packet_filters_by_port(context, id)
|
||||||
|
if need_add:
|
||||||
|
if need_packet_filter_update:
|
||||||
|
self.activate_packet_filters_by_port(context, id)
|
||||||
|
self.activate_port_if_ready(context, new_port)
|
||||||
|
|
||||||
def update_port(self, context, id, port):
|
def update_port(self, context, id, port):
|
||||||
"""Update port, and handle packetfilters associated with the port.
|
"""Update port, and handle packetfilters associated with the port.
|
||||||
|
|
||||||
@ -375,9 +517,8 @@ class NECPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
with context.session.begin(subtransactions=True):
|
with context.session.begin(subtransactions=True):
|
||||||
old_port = super(NECPluginV2, self).get_port(context, id)
|
old_port = super(NECPluginV2, self).get_port(context, id)
|
||||||
new_port = super(NECPluginV2, self).update_port(context, id, port)
|
new_port = super(NECPluginV2, self).update_port(context, id, port)
|
||||||
self._process_portbindings_create_and_update(context,
|
portinfo_changed = self._process_portbindings_update(
|
||||||
port['port'],
|
context, port['port'], new_port)
|
||||||
new_port)
|
|
||||||
need_port_update_notify = self.update_security_group_on_port(
|
need_port_update_notify = self.update_security_group_on_port(
|
||||||
context, id, port, old_port, new_port)
|
context, id, port, old_port, new_port)
|
||||||
|
|
||||||
@ -386,13 +527,8 @@ class NECPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
if need_port_update_notify:
|
if need_port_update_notify:
|
||||||
self.notifier.port_update(context, new_port)
|
self.notifier.port_update(context, new_port)
|
||||||
|
|
||||||
changed = (old_port['admin_state_up'] != new_port['admin_state_up'])
|
self._update_ofc_port_if_required(context, old_port, new_port,
|
||||||
if changed:
|
portinfo_changed)
|
||||||
if new_port['admin_state_up']:
|
|
||||||
new_port = self.activate_port_if_ready(context, new_port)
|
|
||||||
else:
|
|
||||||
new_port = self.deactivate_port(context, new_port)
|
|
||||||
|
|
||||||
return new_port
|
return new_port
|
||||||
|
|
||||||
def delete_port(self, context, id, l3_port_check=True):
|
def delete_port(self, context, id, l3_port_check=True):
|
||||||
@ -510,10 +646,10 @@ class NECPluginV2RPCCallbacks(object):
|
|||||||
"port_added message (port_id=%s)."), id)
|
"port_added message (port_id=%s)."), id)
|
||||||
continue
|
continue
|
||||||
ndb.del_portinfo(session, id)
|
ndb.del_portinfo(session, id)
|
||||||
ndb.add_portinfo(session, id, datapath_id, p['port_no'],
|
|
||||||
mac=p.get('mac', ''))
|
|
||||||
port = self._get_port(rpc_context, id)
|
port = self._get_port(rpc_context, id)
|
||||||
if port:
|
if port:
|
||||||
|
ndb.add_portinfo(session, id, datapath_id, p['port_no'],
|
||||||
|
mac=p.get('mac', ''))
|
||||||
# NOTE: Make sure that packet filters on this port exist while
|
# NOTE: Make sure that packet filters on this port exist while
|
||||||
# the port is active to avoid unexpected packet transfer.
|
# the port is active to avoid unexpected packet transfer.
|
||||||
if portinfo:
|
if portinfo:
|
||||||
|
@ -15,26 +15,24 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
# @author: Ryota MIBU
|
# @author: Ryota MIBU
|
||||||
|
|
||||||
|
import contextlib
|
||||||
import random
|
import random
|
||||||
|
|
||||||
from neutron.common import constants as q_const
|
from neutron.common import constants as q_const
|
||||||
from neutron.db import api as db_api
|
|
||||||
from neutron.openstack.common import uuidutils
|
from neutron.openstack.common import uuidutils
|
||||||
from neutron.plugins.nec.common import exceptions as nexc
|
from neutron.plugins.nec.common import exceptions as nexc
|
||||||
from neutron.plugins.nec.db import api as ndb
|
from neutron.plugins.nec.db import api as ndb
|
||||||
from neutron.plugins.nec.db import models as nmodels # noqa
|
from neutron.plugins.nec.db import models as nmodels # noqa
|
||||||
from neutron.tests import base
|
from neutron.tests.unit.nec import test_nec_plugin
|
||||||
|
|
||||||
|
|
||||||
class NECPluginV2DBTestBase(base.BaseTestCase):
|
class NECPluginV2DBTestBase(test_nec_plugin.NecPluginV2TestCase):
|
||||||
"""Class conisting of NECPluginV2 DB unit tests."""
|
"""Class conisting of NECPluginV2 DB unit tests."""
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""Setup for tests."""
|
"""Setup for tests."""
|
||||||
super(NECPluginV2DBTestBase, self).setUp()
|
super(NECPluginV2DBTestBase, self).setUp()
|
||||||
ndb.initialize()
|
self.session = self.context.session
|
||||||
self.session = db_api.get_session()
|
|
||||||
self.addCleanup(ndb.clear_db)
|
|
||||||
|
|
||||||
def get_ofc_item_random_params(self):
|
def get_ofc_item_random_params(self):
|
||||||
"""create random parameters for ofc_item test."""
|
"""create random parameters for ofc_item test."""
|
||||||
@ -43,15 +41,18 @@ class NECPluginV2DBTestBase(base.BaseTestCase):
|
|||||||
none = uuidutils.generate_uuid()
|
none = uuidutils.generate_uuid()
|
||||||
return ofc_id, neutron_id, none
|
return ofc_id, neutron_id, none
|
||||||
|
|
||||||
def get_portinfo_random_params(self):
|
@contextlib.contextmanager
|
||||||
"""create random parameters for portinfo test."""
|
def portinfo_random_params(self):
|
||||||
port_id = uuidutils.generate_uuid()
|
with self.port() as port:
|
||||||
datapath_id = hex(random.randint(0, 0xffffffff))
|
params = {'port_id': port['port']['id'],
|
||||||
port_no = random.randint(1, 100)
|
'datapath_id': hex(random.randint(0, 0xffffffff)),
|
||||||
vlan_id = random.randint(q_const.MIN_VLAN_TAG, q_const.MAX_VLAN_TAG)
|
'port_no': random.randint(1, 100),
|
||||||
mac = ':'.join(["%02x" % random.randint(0, 0xff) for x in range(6)])
|
'vlan_id': random.randint(q_const.MIN_VLAN_TAG,
|
||||||
none = uuidutils.generate_uuid()
|
q_const.MAX_VLAN_TAG),
|
||||||
return port_id, datapath_id, port_no, vlan_id, mac, none
|
'mac': ':'.join(["%02x" % random.randint(0, 0xff)
|
||||||
|
for x in range(6)])
|
||||||
|
}
|
||||||
|
yield params
|
||||||
|
|
||||||
|
|
||||||
class NECPluginV2DBTest(NECPluginV2DBTestBase):
|
class NECPluginV2DBTest(NECPluginV2DBTestBase):
|
||||||
@ -122,46 +123,51 @@ class NECPluginV2DBTest(NECPluginV2DBTestBase):
|
|||||||
'ofc_tenant', o)
|
'ofc_tenant', o)
|
||||||
self.assertEqual(None, tenant_none)
|
self.assertEqual(None, tenant_none)
|
||||||
|
|
||||||
|
def _compare_portinfo(self, portinfo, expected):
|
||||||
|
self.assertEqual(portinfo.id, expected['port_id'])
|
||||||
|
self.assertEqual(portinfo.datapath_id, expected['datapath_id'])
|
||||||
|
self.assertEqual(portinfo.port_no, expected['port_no'])
|
||||||
|
self.assertEqual(portinfo.vlan_id, expected['vlan_id'])
|
||||||
|
self.assertEqual(portinfo.mac, expected['mac'])
|
||||||
|
|
||||||
|
def _add_portinfo(self, session, params):
|
||||||
|
return ndb.add_portinfo(session, params['port_id'],
|
||||||
|
params['datapath_id'], params['port_no'],
|
||||||
|
params['vlan_id'], params['mac'])
|
||||||
|
|
||||||
def testd_add_portinfo(self):
|
def testd_add_portinfo(self):
|
||||||
"""test add portinfo."""
|
"""test add portinfo."""
|
||||||
i, d, p, v, m, n = self.get_portinfo_random_params()
|
with self.portinfo_random_params() as params:
|
||||||
portinfo = ndb.add_portinfo(self.session, i, d, p, v, m)
|
portinfo = self._add_portinfo(self.session, params)
|
||||||
self.assertEqual(portinfo.id, i)
|
self._compare_portinfo(portinfo, params)
|
||||||
self.assertEqual(portinfo.datapath_id, d)
|
|
||||||
self.assertEqual(portinfo.port_no, p)
|
|
||||||
self.assertEqual(portinfo.vlan_id, v)
|
|
||||||
self.assertEqual(portinfo.mac, m)
|
|
||||||
|
|
||||||
exception_raised = False
|
exception_raised = False
|
||||||
try:
|
try:
|
||||||
ndb.add_portinfo(self.session, i, d, p, v, m)
|
self._add_portinfo(self.session, params)
|
||||||
except nexc.NECDBException:
|
except nexc.NECDBException:
|
||||||
exception_raised = True
|
exception_raised = True
|
||||||
self.assertTrue(exception_raised)
|
self.assertTrue(exception_raised)
|
||||||
|
|
||||||
def teste_get_portinfo(self):
|
def teste_get_portinfo(self):
|
||||||
"""test get portinfo."""
|
"""test get portinfo."""
|
||||||
i, d, p, v, m, n = self.get_portinfo_random_params()
|
with self.portinfo_random_params() as params:
|
||||||
ndb.add_portinfo(self.session, i, d, p, v, m)
|
self._add_portinfo(self.session, params)
|
||||||
portinfo = ndb.get_portinfo(self.session, i)
|
portinfo = ndb.get_portinfo(self.session, params['port_id'])
|
||||||
self.assertEqual(portinfo.id, i)
|
self._compare_portinfo(portinfo, params)
|
||||||
self.assertEqual(portinfo.datapath_id, d)
|
|
||||||
self.assertEqual(portinfo.port_no, p)
|
|
||||||
self.assertEqual(portinfo.vlan_id, v)
|
|
||||||
self.assertEqual(portinfo.mac, m)
|
|
||||||
|
|
||||||
portinfo_none = ndb.get_portinfo(self.session, n)
|
nonexist_id = uuidutils.generate_uuid()
|
||||||
self.assertEqual(None, portinfo_none)
|
portinfo_none = ndb.get_portinfo(self.session, nonexist_id)
|
||||||
|
self.assertEqual(None, portinfo_none)
|
||||||
|
|
||||||
def testf_del_portinfo(self):
|
def testf_del_portinfo(self):
|
||||||
"""test delete portinfo."""
|
"""test delete portinfo."""
|
||||||
i, d, p, v, m, n = self.get_portinfo_random_params()
|
with self.portinfo_random_params() as params:
|
||||||
ndb.add_portinfo(self.session, i, d, p, v, m)
|
self._add_portinfo(self.session, params)
|
||||||
portinfo = ndb.get_portinfo(self.session, i)
|
portinfo = ndb.get_portinfo(self.session, params['port_id'])
|
||||||
self.assertEqual(portinfo.id, i)
|
self.assertEqual(portinfo.id, params['port_id'])
|
||||||
ndb.del_portinfo(self.session, i)
|
ndb.del_portinfo(self.session, params['port_id'])
|
||||||
portinfo_none = ndb.get_portinfo(self.session, i)
|
portinfo_none = ndb.get_portinfo(self.session, params['port_id'])
|
||||||
self.assertEqual(None, portinfo_none)
|
self.assertEqual(None, portinfo_none)
|
||||||
|
|
||||||
|
|
||||||
class NECPluginV2DBOldMappingTest(NECPluginV2DBTestBase):
|
class NECPluginV2DBOldMappingTest(NECPluginV2DBTestBase):
|
||||||
|
@ -24,15 +24,12 @@ from neutron.common.test_lib import test_config
|
|||||||
from neutron.common import topics
|
from neutron.common import topics
|
||||||
from neutron import context
|
from neutron import context
|
||||||
from neutron.db import db_base_plugin_v2
|
from neutron.db import db_base_plugin_v2
|
||||||
from neutron.extensions import portbindings
|
|
||||||
from neutron import manager
|
from neutron import manager
|
||||||
from neutron.plugins.nec.common import exceptions as nexc
|
from neutron.plugins.nec.common import exceptions as nexc
|
||||||
from neutron.plugins.nec.db import api as ndb
|
from neutron.plugins.nec.db import api as ndb
|
||||||
from neutron.plugins.nec import nec_plugin
|
from neutron.plugins.nec import nec_plugin
|
||||||
from neutron.tests.unit import _test_extension_portbindings as test_bindings
|
|
||||||
from neutron.tests.unit.nec import fake_ofc_manager
|
from neutron.tests.unit.nec import fake_ofc_manager
|
||||||
from neutron.tests.unit import test_db_plugin as test_plugin
|
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.nec.nec_plugin.NECPluginV2'
|
PLUGIN_NAME = 'neutron.plugins.nec.nec_plugin.NECPluginV2'
|
||||||
@ -101,31 +98,13 @@ class TestNecV2HTTPResponse(test_plugin.TestV2HTTPResponse,
|
|||||||
|
|
||||||
|
|
||||||
class TestNecPortsV2(test_plugin.TestPortsV2, NecPluginV2TestCase):
|
class TestNecPortsV2(test_plugin.TestPortsV2, NecPluginV2TestCase):
|
||||||
|
pass
|
||||||
VIF_TYPE = portbindings.VIF_TYPE_OVS
|
|
||||||
HAS_PORT_FILTER = True
|
|
||||||
|
|
||||||
|
|
||||||
class TestNecNetworksV2(test_plugin.TestNetworksV2, NecPluginV2TestCase):
|
class TestNecNetworksV2(test_plugin.TestNetworksV2, NecPluginV2TestCase):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class TestNecPortBinding(test_bindings.PortBindingsTestCase,
|
|
||||||
NecPluginV2TestCase):
|
|
||||||
VIF_TYPE = portbindings.VIF_TYPE_OVS
|
|
||||||
HAS_PORT_FILTER = True
|
|
||||||
FIREWALL_DRIVER = test_sg_rpc.FIREWALL_HYBRID_DRIVER
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
test_sg_rpc.set_firewall_driver(self.FIREWALL_DRIVER)
|
|
||||||
super(TestNecPortBinding, self).setUp()
|
|
||||||
|
|
||||||
|
|
||||||
class TestNecPortBindingNoSG(TestNecPortBinding):
|
|
||||||
HAS_PORT_FILTER = False
|
|
||||||
FIREWALL_DRIVER = test_sg_rpc.FIREWALL_NOOP_DRIVER
|
|
||||||
|
|
||||||
|
|
||||||
class TestNecPortsV2Callback(NecPluginV2TestCase):
|
class TestNecPortsV2Callback(NecPluginV2TestCase):
|
||||||
|
|
||||||
def _get_portinfo(self, port_id):
|
def _get_portinfo(self, port_id):
|
||||||
@ -177,10 +156,10 @@ class TestNecPortsV2Callback(NecPluginV2TestCase):
|
|||||||
self.assertEqual(self.ofc.delete_ofc_port.call_count, 1)
|
self.assertEqual(self.ofc.delete_ofc_port.call_count, 1)
|
||||||
self.assertIsNone(self._get_portinfo(port_id))
|
self.assertIsNone(self._get_portinfo(port_id))
|
||||||
|
|
||||||
# The port is expected to delete when exiting with-clause.
|
# The port and portinfo is expected to delete when exiting with-clause.
|
||||||
|
self.assertEqual(self.ofc.delete_ofc_port.call_count, 1)
|
||||||
|
self.assertIsNone(self._get_portinfo(port_id))
|
||||||
if not portinfo_delete_first:
|
if not portinfo_delete_first:
|
||||||
self.assertEqual(self.ofc.delete_ofc_port.call_count, 1)
|
|
||||||
self.assertIsNotNone(self._get_portinfo(port_id))
|
|
||||||
self.rpcapi_update_ports(removed=[port_id])
|
self.rpcapi_update_ports(removed=[port_id])
|
||||||
|
|
||||||
# Ensure port deletion is called once.
|
# Ensure port deletion is called once.
|
||||||
@ -198,8 +177,8 @@ class TestNecPortsV2Callback(NecPluginV2TestCase):
|
|||||||
def test_portinfo_added_unknown_port(self):
|
def test_portinfo_added_unknown_port(self):
|
||||||
portinfo = {'id': 'dummy-p1', 'port_no': 123}
|
portinfo = {'id': 'dummy-p1', 'port_no': 123}
|
||||||
self.rpcapi_update_ports(added=[portinfo])
|
self.rpcapi_update_ports(added=[portinfo])
|
||||||
self.assertIsNotNone(ndb.get_portinfo(self.context.session,
|
self.assertIsNone(ndb.get_portinfo(self.context.session,
|
||||||
'dummy-p1'))
|
'dummy-p1'))
|
||||||
self.assertEqual(self.ofc.exists_ofc_port.call_count, 0)
|
self.assertEqual(self.ofc.exists_ofc_port.call_count, 0)
|
||||||
self.assertEqual(self.ofc.create_ofc_port.call_count, 0)
|
self.assertEqual(self.ofc.create_ofc_port.call_count, 0)
|
||||||
|
|
||||||
@ -234,8 +213,7 @@ class TestNecPortsV2Callback(NecPluginV2TestCase):
|
|||||||
# No OFC operations are expected.
|
# No OFC operations are expected.
|
||||||
self.assertEqual(self.ofc.create_ofc_port.call_count, 1)
|
self.assertEqual(self.ofc.create_ofc_port.call_count, 1)
|
||||||
self.assertEqual(self.ofc.delete_ofc_port.call_count, 1)
|
self.assertEqual(self.ofc.delete_ofc_port.call_count, 1)
|
||||||
self.assertEqual(ndb.get_portinfo(self.context.session,
|
self.assertIsNone(ndb.get_portinfo(self.context.session, port_id))
|
||||||
port_id).port_no, 456)
|
|
||||||
|
|
||||||
def test_portinfo_change(self):
|
def test_portinfo_change(self):
|
||||||
self._test_portinfo_change()
|
self._test_portinfo_change()
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
# @author: Ryota MIBU
|
# @author: Ryota MIBU
|
||||||
|
|
||||||
|
import mock
|
||||||
|
|
||||||
from neutron import context
|
from neutron import context
|
||||||
from neutron.openstack.common import uuidutils
|
from neutron.openstack.common import uuidutils
|
||||||
from neutron.plugins.nec.common import config
|
from neutron.plugins.nec.common import config
|
||||||
@ -24,6 +26,19 @@ from neutron.plugins.nec import ofc_manager
|
|||||||
from neutron.tests import base
|
from neutron.tests import base
|
||||||
|
|
||||||
|
|
||||||
|
class FakePortInfo(object):
|
||||||
|
def __init__(self, id, datapath_id, port_no=0,
|
||||||
|
vlan_id=65535, mac='00:11:22:33:44:55'):
|
||||||
|
self.data = {'id': id, 'datapath_id': datapath_id,
|
||||||
|
'port_no': port_no, 'vlan_id': vlan_id, 'mac': mac}
|
||||||
|
|
||||||
|
def __getattr__(self, name):
|
||||||
|
if name in self.fields:
|
||||||
|
return self[name]
|
||||||
|
else:
|
||||||
|
raise AttributeError(name)
|
||||||
|
|
||||||
|
|
||||||
class OFCManagerTestBase(base.BaseTestCase):
|
class OFCManagerTestBase(base.BaseTestCase):
|
||||||
"""Class conisting of OFCManager unit tests."""
|
"""Class conisting of OFCManager unit tests."""
|
||||||
|
|
||||||
@ -35,6 +50,7 @@ class OFCManagerTestBase(base.BaseTestCase):
|
|||||||
self.addCleanup(ndb.clear_db)
|
self.addCleanup(ndb.clear_db)
|
||||||
self.ofc = ofc_manager.OFCManager()
|
self.ofc = ofc_manager.OFCManager()
|
||||||
self.ctx = context.get_admin_context()
|
self.ctx = context.get_admin_context()
|
||||||
|
self.addCleanup(mock.patch.stopall)
|
||||||
|
|
||||||
def get_random_params(self):
|
def get_random_params(self):
|
||||||
"""create random parameters for portinfo test."""
|
"""create random parameters for portinfo test."""
|
||||||
@ -98,44 +114,51 @@ class OFCManagerTest(OFCManagerTestBase):
|
|||||||
self.ofc.delete_ofc_network(self.ctx, n, {'tenant_id': t})
|
self.ofc.delete_ofc_network(self.ctx, n, {'tenant_id': t})
|
||||||
self.assertFalse(ndb.get_ofc_item(self.ctx.session, 'ofc_network', n))
|
self.assertFalse(ndb.get_ofc_item(self.ctx.session, 'ofc_network', n))
|
||||||
|
|
||||||
|
def _mock_get_portinfo(self, port_id, datapath_id='0xabc', port_no=1):
|
||||||
|
get_portinfo = mock.patch.object(ndb, 'get_portinfo').start()
|
||||||
|
fake_portinfo = FakePortInfo(id=port_id, datapath_id=datapath_id,
|
||||||
|
port_no=port_no)
|
||||||
|
get_portinfo.return_value = fake_portinfo
|
||||||
|
return get_portinfo
|
||||||
|
|
||||||
def testg_create_ofc_port(self):
|
def testg_create_ofc_port(self):
|
||||||
"""test create ofc_port."""
|
"""test create ofc_port."""
|
||||||
t, n, p, f, none = self.get_random_params()
|
t, n, p, f, none = self.get_random_params()
|
||||||
self.ofc.create_ofc_tenant(self.ctx, t)
|
self.ofc.create_ofc_tenant(self.ctx, t)
|
||||||
self.ofc.create_ofc_network(self.ctx, t, n)
|
self.ofc.create_ofc_network(self.ctx, t, n)
|
||||||
ndb.add_portinfo(self.ctx.session, p, "0xabc", 1, 65535,
|
|
||||||
"00:11:22:33:44:55")
|
|
||||||
self.assertFalse(ndb.get_ofc_item(self.ctx.session, 'ofc_port', p))
|
self.assertFalse(ndb.get_ofc_item(self.ctx.session, 'ofc_port', p))
|
||||||
|
get_portinfo = self._mock_get_portinfo(p)
|
||||||
port = {'tenant_id': t, 'network_id': n}
|
port = {'tenant_id': t, 'network_id': n}
|
||||||
self.ofc.create_ofc_port(self.ctx, p, port)
|
self.ofc.create_ofc_port(self.ctx, p, port)
|
||||||
self.assertTrue(ndb.get_ofc_item(self.ctx.session, 'ofc_port', p))
|
self.assertTrue(ndb.get_ofc_item(self.ctx.session, 'ofc_port', p))
|
||||||
port = ndb.get_ofc_item(self.ctx.session, 'ofc_port', p)
|
port = ndb.get_ofc_item(self.ctx.session, 'ofc_port', p)
|
||||||
self.assertEqual(port.ofc_id, "ofc-" + p[:-4])
|
self.assertEqual(port.ofc_id, "ofc-" + p[:-4])
|
||||||
|
get_portinfo.assert_called_once_with(mock.ANY, p)
|
||||||
|
|
||||||
def testh_exists_ofc_port(self):
|
def testh_exists_ofc_port(self):
|
||||||
"""test exists_ofc_port."""
|
"""test exists_ofc_port."""
|
||||||
t, n, p, f, none = self.get_random_params()
|
t, n, p, f, none = self.get_random_params()
|
||||||
self.ofc.create_ofc_tenant(self.ctx, t)
|
self.ofc.create_ofc_tenant(self.ctx, t)
|
||||||
self.ofc.create_ofc_network(self.ctx, t, n)
|
self.ofc.create_ofc_network(self.ctx, t, n)
|
||||||
ndb.add_portinfo(self.ctx.session, p, "0xabc", 2, 65535,
|
|
||||||
"00:12:22:33:44:55")
|
|
||||||
self.assertFalse(self.ofc.exists_ofc_port(self.ctx, p))
|
self.assertFalse(self.ofc.exists_ofc_port(self.ctx, p))
|
||||||
|
get_portinfo = self._mock_get_portinfo(p)
|
||||||
port = {'tenant_id': t, 'network_id': n}
|
port = {'tenant_id': t, 'network_id': n}
|
||||||
self.ofc.create_ofc_port(self.ctx, p, port)
|
self.ofc.create_ofc_port(self.ctx, p, port)
|
||||||
self.assertTrue(self.ofc.exists_ofc_port(self.ctx, p))
|
self.assertTrue(self.ofc.exists_ofc_port(self.ctx, p))
|
||||||
|
get_portinfo.assert_called_once_with(mock.ANY, p)
|
||||||
|
|
||||||
def testi_delete_ofc_port(self):
|
def testi_delete_ofc_port(self):
|
||||||
"""test delete ofc_port."""
|
"""test delete ofc_port."""
|
||||||
t, n, p, f, none = self.get_random_params()
|
t, n, p, f, none = self.get_random_params()
|
||||||
self.ofc.create_ofc_tenant(self.ctx, t)
|
self.ofc.create_ofc_tenant(self.ctx, t)
|
||||||
self.ofc.create_ofc_network(self.ctx, t, n)
|
self.ofc.create_ofc_network(self.ctx, t, n)
|
||||||
ndb.add_portinfo(self.ctx.session, p, "0xabc", 3, 65535,
|
get_portinfo = self._mock_get_portinfo(p)
|
||||||
"00:13:22:33:44:55")
|
|
||||||
port = {'tenant_id': t, 'network_id': n}
|
port = {'tenant_id': t, 'network_id': n}
|
||||||
self.ofc.create_ofc_port(self.ctx, p, port)
|
self.ofc.create_ofc_port(self.ctx, p, port)
|
||||||
self.assertTrue(ndb.get_ofc_item(self.ctx.session, 'ofc_port', p))
|
self.assertTrue(ndb.get_ofc_item(self.ctx.session, 'ofc_port', p))
|
||||||
self.ofc.delete_ofc_port(self.ctx, p, port)
|
self.ofc.delete_ofc_port(self.ctx, p, port)
|
||||||
self.assertFalse(ndb.get_ofc_item(self.ctx.session, 'ofc_port', p))
|
self.assertFalse(ndb.get_ofc_item(self.ctx.session, 'ofc_port', p))
|
||||||
|
get_portinfo.assert_called_once_with(mock.ANY, p)
|
||||||
|
|
||||||
def testj_create_ofc_packet_filter(self):
|
def testj_create_ofc_packet_filter(self):
|
||||||
"""test create ofc_filter."""
|
"""test create ofc_filter."""
|
||||||
|
280
neutron/tests/unit/nec/test_portbindings.py
Normal file
280
neutron/tests/unit/nec/test_portbindings.py
Normal file
@ -0,0 +1,280 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
# Copyright 2013 NEC Corporation
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
#
|
||||||
|
# @author: Akihiro Motoki, NEC Corporation
|
||||||
|
|
||||||
|
from testtools import matchers
|
||||||
|
from webob import exc
|
||||||
|
|
||||||
|
from neutron.common import exceptions as q_exc
|
||||||
|
from neutron import context
|
||||||
|
from neutron.extensions import portbindings
|
||||||
|
from neutron.tests.unit import _test_extension_portbindings as test_bindings
|
||||||
|
from neutron.tests.unit.nec import test_nec_plugin
|
||||||
|
from neutron.tests.unit import test_security_groups_rpc as test_sg_rpc
|
||||||
|
|
||||||
|
|
||||||
|
class TestNecPortBinding(test_bindings.PortBindingsTestCase,
|
||||||
|
test_nec_plugin.NecPluginV2TestCase):
|
||||||
|
VIF_TYPE = portbindings.VIF_TYPE_OVS
|
||||||
|
HAS_PORT_FILTER = True
|
||||||
|
FIREWALL_DRIVER = test_sg_rpc.FIREWALL_HYBRID_DRIVER
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
test_sg_rpc.set_firewall_driver(self.FIREWALL_DRIVER)
|
||||||
|
super(TestNecPortBinding, self).setUp()
|
||||||
|
|
||||||
|
|
||||||
|
class TestNecPortBindingNoSG(TestNecPortBinding):
|
||||||
|
HAS_PORT_FILTER = False
|
||||||
|
FIREWALL_DRIVER = test_sg_rpc.FIREWALL_NOOP_DRIVER
|
||||||
|
|
||||||
|
|
||||||
|
class TestNecPortBindingHost(
|
||||||
|
test_bindings.PortBindingsHostTestCaseMixin,
|
||||||
|
test_nec_plugin.NecPluginV2TestCase):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class TestNecPortBindingPortInfo(test_nec_plugin.NecPluginV2TestCase):
|
||||||
|
def _get_portinfo(self, datapath_id=None, port_no=None, prefix=None):
|
||||||
|
if datapath_id is None:
|
||||||
|
datapath_id = '0xabc'
|
||||||
|
if port_no is None:
|
||||||
|
port_no = 1
|
||||||
|
if prefix is None:
|
||||||
|
prefix = 'portinfo:'
|
||||||
|
return {prefix + 'datapath_id': datapath_id,
|
||||||
|
prefix + 'port_no': port_no}
|
||||||
|
|
||||||
|
def _check_response_portbinding_profile(self, port, datapath_id=None,
|
||||||
|
port_no=None):
|
||||||
|
expected = self._get_portinfo(datapath_id, port_no, prefix='')
|
||||||
|
profile = port[portbindings.PROFILE]
|
||||||
|
self.assertEqual(len(profile), 2)
|
||||||
|
self.assertEqual(profile['portinfo:datapath_id'],
|
||||||
|
expected['datapath_id'])
|
||||||
|
self.assertEqual(profile['portinfo:port_no'],
|
||||||
|
expected['port_no'])
|
||||||
|
|
||||||
|
def _check_response_portbinding_no_profile(self, port):
|
||||||
|
self.assertIn('status', port)
|
||||||
|
self.assertNotIn(portbindings.PROFILE, port)
|
||||||
|
|
||||||
|
def _get_non_admin_context(self):
|
||||||
|
return context.Context(user_id=None,
|
||||||
|
tenant_id=self._tenant_id,
|
||||||
|
is_admin=False,
|
||||||
|
read_deleted="no")
|
||||||
|
|
||||||
|
def test_port_create_portinfo(self):
|
||||||
|
profile_arg = {portbindings.PROFILE: self._get_portinfo()}
|
||||||
|
with self.port(arg_list=(portbindings.PROFILE,),
|
||||||
|
**profile_arg) as port:
|
||||||
|
port_id = port['port']['id']
|
||||||
|
# Check a response of create_port
|
||||||
|
self._check_response_portbinding_profile(port['port'])
|
||||||
|
self.assertEqual(self.ofc.create_ofc_port.call_count, 1)
|
||||||
|
# Check a response of get_port
|
||||||
|
ctx = context.get_admin_context()
|
||||||
|
port = self._show('ports', port_id, neutron_context=ctx)['port']
|
||||||
|
self._check_response_portbinding_profile(port)
|
||||||
|
# By default user is admin - now test non admin user
|
||||||
|
ctx = self._get_non_admin_context()
|
||||||
|
non_admin_port = self._show(
|
||||||
|
'ports', port_id, neutron_context=ctx)['port']
|
||||||
|
self._check_response_portbinding_no_profile(non_admin_port)
|
||||||
|
# port-update with non admin user should fail
|
||||||
|
self._update('ports', port_id,
|
||||||
|
{'port': profile_arg},
|
||||||
|
expected_code=404,
|
||||||
|
neutron_context=ctx)
|
||||||
|
|
||||||
|
def test_port_update_portinfo(self):
|
||||||
|
profile_arg = {portbindings.PROFILE: self._get_portinfo()}
|
||||||
|
with self.port() as port:
|
||||||
|
self.assertEqual(self.ofc.create_ofc_port.call_count, 0)
|
||||||
|
port_id = port['port']['id']
|
||||||
|
# Check a response of create_port
|
||||||
|
self._check_response_portbinding_no_profile(port['port'])
|
||||||
|
# Check a response of update_port
|
||||||
|
ctx = context.get_admin_context()
|
||||||
|
port = self._update('ports', port_id, {'port': profile_arg},
|
||||||
|
neutron_context=ctx)['port']
|
||||||
|
self.assertEqual(self.ofc.create_ofc_port.call_count, 1)
|
||||||
|
self._check_response_portbinding_profile(port)
|
||||||
|
port = self._show('ports', port_id, neutron_context=ctx)['port']
|
||||||
|
self._check_response_portbinding_profile(port)
|
||||||
|
|
||||||
|
def test_port_update_portinfo_detail(self):
|
||||||
|
with self.port() as port:
|
||||||
|
self.assertEqual(self.ofc.create_ofc_port.call_count, 0)
|
||||||
|
self.assertEqual(self.ofc.delete_ofc_port.call_count, 0)
|
||||||
|
port_id = port['port']['id']
|
||||||
|
ctx = context.get_admin_context()
|
||||||
|
|
||||||
|
# add portinfo
|
||||||
|
profile_arg = {portbindings.PROFILE: self._get_portinfo()}
|
||||||
|
port = self._update('ports', port_id, {'port': profile_arg},
|
||||||
|
neutron_context=ctx)['port']
|
||||||
|
self.assertEqual(self.ofc.create_ofc_port.call_count, 1)
|
||||||
|
self.assertEqual(self.ofc.delete_ofc_port.call_count, 0)
|
||||||
|
|
||||||
|
# portinfo unchanged
|
||||||
|
port = self._update('ports', port_id, {'port': profile_arg},
|
||||||
|
neutron_context=ctx)['port']
|
||||||
|
self.assertEqual(self.ofc.create_ofc_port.call_count, 1)
|
||||||
|
self.assertEqual(self.ofc.delete_ofc_port.call_count, 0)
|
||||||
|
|
||||||
|
# modify portinfo
|
||||||
|
profile_arg = {portbindings.PROFILE:
|
||||||
|
self._get_portinfo(datapath_id='0x1234567890',
|
||||||
|
port_no=99)}
|
||||||
|
port = self._update('ports', port_id, {'port': profile_arg},
|
||||||
|
neutron_context=ctx)['port']
|
||||||
|
self.assertEqual(self.ofc.create_ofc_port.call_count, 2)
|
||||||
|
self.assertEqual(self.ofc.delete_ofc_port.call_count, 1)
|
||||||
|
|
||||||
|
# delete portinfo
|
||||||
|
profile_arg = {portbindings.PROFILE: {}}
|
||||||
|
port = self._update('ports', port_id, {'port': profile_arg},
|
||||||
|
neutron_context=ctx)['port']
|
||||||
|
self.assertEqual(self.ofc.create_ofc_port.call_count, 2)
|
||||||
|
self.assertEqual(self.ofc.delete_ofc_port.call_count, 2)
|
||||||
|
|
||||||
|
def test_port_create_portinfo_with_empty_dict(self):
|
||||||
|
profile_arg = {portbindings.PROFILE: {}}
|
||||||
|
with self.port(arg_list=(portbindings.PROFILE,),
|
||||||
|
**profile_arg) as port:
|
||||||
|
port_id = port['port']['id']
|
||||||
|
|
||||||
|
# Check a response of create_port
|
||||||
|
self._check_response_portbinding_no_profile(port['port'])
|
||||||
|
self.assertEqual(self.ofc.create_ofc_port.call_count, 0)
|
||||||
|
# add portinfo
|
||||||
|
ctx = context.get_admin_context()
|
||||||
|
profile_arg = {portbindings.PROFILE: self._get_portinfo()}
|
||||||
|
port = self._update('ports', port_id, {'port': profile_arg},
|
||||||
|
neutron_context=ctx)['port']
|
||||||
|
self._check_response_portbinding_profile(port)
|
||||||
|
self.assertEqual(self.ofc.create_ofc_port.call_count, 1)
|
||||||
|
self.assertEqual(self.ofc.delete_ofc_port.call_count, 0)
|
||||||
|
|
||||||
|
def test_port_create_portinfo_non_admin(self):
|
||||||
|
with self.network(set_context=True, tenant_id='test') as net1:
|
||||||
|
with self.subnet(network=net1) as subnet1:
|
||||||
|
profile_arg = {portbindings.PROFILE: self._get_portinfo()}
|
||||||
|
try:
|
||||||
|
with self.port(subnet=subnet1,
|
||||||
|
expected_res_status=403,
|
||||||
|
arg_list=(portbindings.PROFILE,),
|
||||||
|
set_context=True, tenant_id='test',
|
||||||
|
**profile_arg):
|
||||||
|
pass
|
||||||
|
except exc.HTTPClientError:
|
||||||
|
pass
|
||||||
|
self.assertEqual(self.ofc.create_ofc_port.call_count, 0)
|
||||||
|
|
||||||
|
def test_port_update_portinfo_non_admin(self):
|
||||||
|
profile_arg = {portbindings.PROFILE: self._get_portinfo()}
|
||||||
|
with self.network() as net1:
|
||||||
|
with self.subnet(network=net1) as subnet1:
|
||||||
|
with self.port(subnet=subnet1) as port:
|
||||||
|
# By default user is admin - now test non admin user
|
||||||
|
# Note that 404 is returned when prohibit by policy.
|
||||||
|
# See comment for PolicyNotAuthorized except clause
|
||||||
|
# in update() in neutron.api.v2.base.Controller.
|
||||||
|
port_id = port['port']['id']
|
||||||
|
ctx = self._get_non_admin_context()
|
||||||
|
port = self._update('ports', port_id,
|
||||||
|
{'port': profile_arg},
|
||||||
|
expected_code=404,
|
||||||
|
neutron_context=ctx)
|
||||||
|
self.assertEqual(self.ofc.create_ofc_port.call_count, 0)
|
||||||
|
|
||||||
|
def test_port_create_portinfo_validation_called(self):
|
||||||
|
# Check validate_portinfo is called.
|
||||||
|
profile_arg = {portbindings.PROFILE:
|
||||||
|
{'portinfo:datapath_id': '0xabc',
|
||||||
|
'portinfo:port_no': 0xffff + 1}}
|
||||||
|
try:
|
||||||
|
with self.port(arg_list=(portbindings.PROFILE,),
|
||||||
|
expected_res_status=400,
|
||||||
|
**profile_arg):
|
||||||
|
pass
|
||||||
|
except exc.HTTPClientError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class TestNecPortBindingValidatePortInfo(test_nec_plugin.NecPluginV2TestCase):
|
||||||
|
|
||||||
|
def test_validate_portinfo_ok(self):
|
||||||
|
profile = {'portinfo:datapath_id': '0x1234567890abcdef',
|
||||||
|
'portinfo:port_no': 123}
|
||||||
|
portinfo = self.plugin._validate_portinfo(profile)
|
||||||
|
self.assertEqual(portinfo['datapath_id'], '0x1234567890abcdef')
|
||||||
|
self.assertEqual(portinfo['port_no'], 123)
|
||||||
|
|
||||||
|
def test_validate_portinfo_ok_without_0x(self):
|
||||||
|
profile = {'portinfo:datapath_id': '1234567890abcdef',
|
||||||
|
'portinfo:port_no': 123}
|
||||||
|
portinfo = self.plugin._validate_portinfo(profile)
|
||||||
|
self.assertEqual(portinfo['datapath_id'], '0x1234567890abcdef')
|
||||||
|
self.assertEqual(portinfo['port_no'], 123)
|
||||||
|
|
||||||
|
def _test_validate_exception(self, profile, expected_msg):
|
||||||
|
e = self.assertRaises(q_exc.InvalidInput,
|
||||||
|
self.plugin._validate_portinfo, profile)
|
||||||
|
self.assertThat(str(e), matchers.StartsWith(expected_msg))
|
||||||
|
|
||||||
|
def test_validate_portinfo_dict_validation(self):
|
||||||
|
expected_msg = ("Invalid input for operation: "
|
||||||
|
"Validation of dictionary's keys failed.")
|
||||||
|
|
||||||
|
profile = {'portinfo:port_no': 123}
|
||||||
|
self._test_validate_exception(profile, expected_msg)
|
||||||
|
|
||||||
|
profile = {'portinfo:datapath_id': '0xabcdef'}
|
||||||
|
self._test_validate_exception(profile, expected_msg)
|
||||||
|
|
||||||
|
def test_validate_portinfo_negative_port_number(self):
|
||||||
|
profile = {'portinfo:datapath_id': '0x1234567890abcdef',
|
||||||
|
'portinfo:port_no': -1}
|
||||||
|
expected_msg = ("Invalid input for operation: "
|
||||||
|
"'-1' should be non-negative.")
|
||||||
|
self._test_validate_exception(profile, expected_msg)
|
||||||
|
|
||||||
|
def test_validate_portinfo_invalid_datapath_id(self):
|
||||||
|
expected_msg = ("Invalid input for operation: "
|
||||||
|
"portinfo:datapath_id should be a hex string")
|
||||||
|
|
||||||
|
# non hexidecimal datapath_id
|
||||||
|
profile = {'portinfo:datapath_id': 'INVALID',
|
||||||
|
'portinfo:port_no': 123}
|
||||||
|
self._test_validate_exception(profile, expected_msg)
|
||||||
|
|
||||||
|
# Too big datapath_id
|
||||||
|
profile = {'portinfo:datapath_id': '0x10000000000000000',
|
||||||
|
'portinfo:port_no': 123}
|
||||||
|
self._test_validate_exception(profile, expected_msg)
|
||||||
|
|
||||||
|
def test_validate_portinfo_too_big_port_number(self):
|
||||||
|
profile = {'portinfo:datapath_id': '0x1234567890abcdef',
|
||||||
|
'portinfo:port_no': 65536}
|
||||||
|
expected_msg = ("Invalid input for operation: "
|
||||||
|
"portinfo:port_no should be [0:65535]")
|
||||||
|
self._test_validate_exception(profile, expected_msg)
|
Loading…
Reference in New Issue
Block a user