Skip to content

Commit

Permalink
plugin/nec: Make sure resources on OFC is globally unique.
Browse files Browse the repository at this point in the history
Fixes bug 1127664

Network cannot be created in NEC plugin when OFC network ID is
unique inside a tenant. Some OFC implmenetations generate a network
ID unique inside a tenant. In this case generated network IDs on can
be duplicated in system-wide. To fix it, this changes resource ID on
OFC to REST URI to make sure IDs on OFC globally unique.

Fixes bug 1120962

Make sure NEC plugin creates shared networks

In Quantum resource relationship is not limited inside a tenant.
E.g., a non-owner tenant can create a port on a shared network.
To deal with it the provider layer should not be aware of tenants
each resource belongs to even when it has a kind of tenant concept.

This commit changes ofc_manager to pass a parent resource for resource
creation and identify a resouce by REST URI used to access OFC resources.
It decouples Quantum resource access model from OFC resource models.
OFC IDs created before this commit are also looked up.

Primary keys of OFC ID mapping tables are changed to quantum_id because
most of all accesses to these mapping tables are done by quantum_id.
However the current version of alembic does not support changing primary
keys, so new OFC ID mapping tables for tenant, network, port and packet
filter are created. Dropping the previous mapping tables will be done
along with the data migration logic.

This commit also changes the following minor issues.
- Make sure ID on ProgrammableFlow OpenFlow controller (PFC) is less than
  32 chars. The current PFC accepts only 31 chars max as ID and 127 chars
  as a description string.
- Some database accesses created their own session and did not support
  subtransactions. Make sure to use context.session passed from the API layer.
- Removes Unused methods (update_network, update_port) in trema/pfc drivers.

Change-Id: Ib4bb830e5f537c789974fa7b77f06eaeacb65333
  • Loading branch information
amotoki committed Feb 18, 2013
1 parent 4298be0 commit 7a7675c
Show file tree
Hide file tree
Showing 15 changed files with 1,389 additions and 459 deletions.
@@ -0,0 +1,84 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2013 OpenStack LLC
#
# 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 plugin sharednet
Revision ID: 3b54bf9e29f7
Revises: 54c2c487e913
Create Date: 2013-02-17 09:21:48.287134
"""

# revision identifiers, used by Alembic.
revision = '3b54bf9e29f7'
down_revision = '54c2c487e913'

# Change to ['*'] if this migration applies to all plugins

migration_for_plugins = [
'quantum.plugins.nec.nec_plugin.NECPluginV2'
]

from alembic import op
import sqlalchemy as sa


from quantum.db import migration


def upgrade(active_plugin=None, options=None):
if not migration.should_run(active_plugin, migration_for_plugins):
return

op.create_table(
'ofctenantmappings',
sa.Column('ofc_id', sa.String(length=255), nullable=False),
sa.Column('quantum_id', sa.String(length=36), nullable=False),
sa.PrimaryKeyConstraint('quantum_id'),
sa.UniqueConstraint('ofc_id')
)
op.create_table(
'ofcnetworkmappings',
sa.Column('ofc_id', sa.String(length=255), nullable=False),
sa.Column('quantum_id', sa.String(length=36), nullable=False),
sa.PrimaryKeyConstraint('quantum_id'),
sa.UniqueConstraint('ofc_id')
)
op.create_table(
'ofcportmappings',
sa.Column('ofc_id', sa.String(length=255), nullable=False),
sa.Column('quantum_id', sa.String(length=36), nullable=False),
sa.PrimaryKeyConstraint('quantum_id'),
sa.UniqueConstraint('ofc_id')
)
op.create_table(
'ofcfiltermappings',
sa.Column('ofc_id', sa.String(length=255), nullable=False),
sa.Column('quantum_id', sa.String(length=36), nullable=False),
sa.PrimaryKeyConstraint('quantum_id'),
sa.UniqueConstraint('ofc_id')
)


def downgrade(active_plugin=None, options=None):
if not migration.should_run(active_plugin, migration_for_plugins):
return

op.drop_table('ofcfiltermappings')
op.drop_table('ofcportmappings')
op.drop_table('ofcnetworkmappings')
op.drop_table('ofctenantmappings')
7 changes: 4 additions & 3 deletions quantum/plugins/nec/common/ofc_client.py
Expand Up @@ -54,9 +54,10 @@ def get_connection_type(self):
return httplib.HTTPConnection

def do_request(self, method, action, body=None):
LOG.debug(_("Client request: %(method)s %(action)s [%(body)s]"),
locals())

LOG.debug(_("Client request: %(host)s:%(port)s "
"%(method)s %(action)s [%(body)s]"),
{'host': self.host, 'port': self.port,
'method': method, 'action': action, 'body': body})
if type(body) is dict:
body = json.dumps(body)
try:
Expand Down
141 changes: 110 additions & 31 deletions quantum/plugins/nec/db/api.py
Expand Up @@ -34,6 +34,26 @@
OFP_VLAN_NONE = 0xffff


resource_map = {'ofc_tenant': nmodels.OFCTenantMapping,
'ofc_network': nmodels.OFCNetworkMapping,
'ofc_port': nmodels.OFCPortMapping,
'ofc_packet_filter': nmodels.OFCFilterMapping}

old_resource_map = {'ofc_tenant': nmodels.OFCTenant,
'ofc_network': nmodels.OFCNetwork,
'ofc_port': nmodels.OFCPort,
'ofc_packet_filter': nmodels.OFCFilter}


# utitlity methods

def _get_resource_model(resource, old_style):
if old_style:
return old_resource_map[resource]
else:
return resource_map[resource]


def initialize():
db.configure_db()

Expand All @@ -42,30 +62,52 @@ def clear_db(base=model_base.BASEV2):
db.clear_db(base)


def get_ofc_item(model, id):
session = db.get_session()
def get_ofc_item(session, resource, quantum_id, old_style=False):
try:
return (session.query(model).
filter_by(id=id).
one())
model = _get_resource_model(resource, old_style)
return session.query(model).filter_by(quantum_id=quantum_id).one()
except sa.orm.exc.NoResultFound:
return None


def find_ofc_item(model, quantum_id):
session = db.get_session()
def get_ofc_id(session, resource, quantum_id, old_style=False):
ofc_item = get_ofc_item(session, resource, quantum_id, old_style)
if ofc_item:
if old_style:
return ofc_item.id
else:
return ofc_item.ofc_id
else:
return None


def exists_ofc_item(session, resource, quantum_id, old_style=False):
if get_ofc_item(session, resource, quantum_id, old_style):
return True
else:
return False


def find_ofc_item(session, resource, ofc_id, old_style=False):
try:
return (session.query(model).
filter_by(quantum_id=quantum_id).
one())
model = _get_resource_model(resource, old_style)
if old_style:
params = dict(id=ofc_id)
else:
params = dict(ofc_id=ofc_id)
return (session.query(model).filter_by(**params).one())
except sa.orm.exc.NoResultFound:
return None


def add_ofc_item(model, id, quantum_id):
session = db.get_session()
def add_ofc_item(session, resource, quantum_id, ofc_id, old_style=False):
try:
item = model(id=id, quantum_id=quantum_id)
model = _get_resource_model(resource, old_style)
if old_style:
params = dict(quantum_id=quantum_id, id=ofc_id)
else:
params = dict(quantum_id=quantum_id, ofc_id=ofc_id)
item = model(**params)
session.add(item)
session.flush()
except Exception as exc:
Expand All @@ -74,21 +116,61 @@ def add_ofc_item(model, id, quantum_id):
return item


def del_ofc_item(model, id):
session = db.get_session()
def del_ofc_item(session, resource, quantum_id, old_style=False,
warning=True):
try:
item = (session.query(model).
filter_by(id=id).
one())
model = _get_resource_model(resource, old_style)
item = session.query(model).filter_by(quantum_id=quantum_id).one()
session.delete(item)
session.flush()
return True
except sa.orm.exc.NoResultFound:
LOG.warning(_("_del_ofc_item(): NotFound item "
"(model=%(model)s, id=%(id)s) "), locals())


def get_portinfo(id):
session = db.get_session()
if warning:
LOG.warning(_("_del_ofc_item(): NotFound item "
"(model=%(model)s, id=%(id)s) "),
{'model': model, 'id': quantum_id})
return False


def get_ofc_id_lookup_both(session, resource, quantum_id):
ofc_id = get_ofc_id(session, resource, quantum_id)
# Lookup old style of OFC mapping table
if not ofc_id:
ofc_id = get_ofc_id(session, resource, quantum_id,
old_style=True)
if not ofc_id:
reason = (_("NotFound %(resource)s for quantum_id=%(id)s.")
% {'resource': resource, 'id': quantum_id})
raise nexc.OFCConsistencyBroken(reason=reason)
return ofc_id


def exists_ofc_item_lookup_both(session, resource, quantum_id):
if exists_ofc_item(session, resource, quantum_id):
return True
# Check old style of OFC mapping table
if exists_ofc_item(session, resource, quantum_id,
old_style=True):
return True
return False


def del_ofc_item_lookup_both(session, resource, quantum_id):
# Delete the mapping from new style of OFC mapping table
if del_ofc_item(session, resource, quantum_id,
old_style=False, warning=False):
return
# Delete old style of OFC mapping table
if del_ofc_item(session, resource, quantum_id,
old_style=True, warning=False):
return
# The specified resource not found
LOG.warning(_("_del_ofc_item(): NotFound item "
"(resource=%(resource)s, id=%(id)s) "),
{'resource': resource, 'id': quantum_id})


def get_portinfo(session, id):
try:
return (session.query(nmodels.PortInfo).
filter_by(id=id).
Expand All @@ -97,8 +179,8 @@ def get_portinfo(id):
return None


def add_portinfo(id, datapath_id='', port_no=0, vlan_id=OFP_VLAN_NONE, mac=''):
session = db.get_session()
def add_portinfo(session, id, datapath_id='', port_no=0,
vlan_id=OFP_VLAN_NONE, mac=''):
try:
portinfo = nmodels.PortInfo(id=id, datapath_id=datapath_id,
port_no=port_no, vlan_id=vlan_id, mac=mac)
Expand All @@ -110,12 +192,9 @@ def add_portinfo(id, datapath_id='', port_no=0, vlan_id=OFP_VLAN_NONE, mac=''):
return portinfo


def del_portinfo(id):
session = db.get_session()
def del_portinfo(session, id):
try:
portinfo = (session.query(nmodels.PortInfo).
filter_by(id=id).
one())
portinfo = session.query(nmodels.PortInfo).filter_by(id=id).one()
session.delete(portinfo)
session.flush()
except sa.orm.exc.NoResultFound:
Expand Down
32 changes: 32 additions & 0 deletions quantum/plugins/nec/db/models.py
Expand Up @@ -21,6 +21,38 @@
from quantum.db import models_v2


"""New mapping tables"""


class OFCId(object):
"""Resource ID on OpenFlow Controller"""
ofc_id = sa.Column(sa.String(255), unique=True, nullable=False)


class QuantumId(object):
"""Logical ID on Quantum"""
quantum_id = sa.Column(sa.String(36), primary_key=True)


class OFCTenantMapping(model_base.BASEV2, QuantumId, OFCId):
"""Represents a Tenant on OpenFlow Network/Controller."""


class OFCNetworkMapping(model_base.BASEV2, QuantumId, OFCId):
"""Represents a Network on OpenFlow Network/Controller."""


class OFCPortMapping(model_base.BASEV2, QuantumId, OFCId):
"""Represents a Port on OpenFlow Network/Controller."""


class OFCFilterMapping(model_base.BASEV2, QuantumId, OFCId):
"""Represents a Filter on OpenFlow Network/Controller."""


"""Old mapping tables"""


class HasQuantumId(object):
"""Logical ID on Quantum"""
quantum_id = sa.Column(sa.String(36), nullable=False)
Expand Down
4 changes: 3 additions & 1 deletion quantum/plugins/nec/drivers/__init__.py
Expand Up @@ -26,7 +26,9 @@
'trema_port': DRIVER_PATH % "trema.TremaPortBaseDriver",
'trema_portmac': DRIVER_PATH % "trema.TremaPortMACBaseDriver",
'trema_mac': DRIVER_PATH % "trema.TremaMACBaseDriver",
'pfc': DRIVER_PATH % "pfc.PFCDriver"}
'pfc': DRIVER_PATH % "pfc.PFCV4Driver",
'pfc_v3': DRIVER_PATH % "pfc.PFCV3Driver",
'pfc_v4': DRIVER_PATH % "pfc.PFCV4Driver"}


def get_driver(driver_name):
Expand Down

0 comments on commit 7a7675c

Please sign in to comment.