Make network segment table available for standalone plugin

Some standalone plugins, such as networking-ovn, are expecting
to store provider network information in neutron database, but
we have no table, like other extensions do, for provider
network extension defined.

This patch moves NetworkSegment table out of the ML2 code tree.
The API methods to operate segments are also moved, but stub
methods are still kept in the ml2 db API.

Co-Author: Miguel Lavalle <malavall@us.ibm.com>
Change-Id: I2c4f78fce591486ded63252af13fc0c60d02a3e8
Partially-Implements: blueprint routed-networks
This commit is contained in:
gong yong sheng 2015-11-06 16:23:55 +08:00 committed by Miguel Lavalle
parent af8d0573ce
commit c8fca1c96f
6 changed files with 212 additions and 110 deletions

View File

@ -1 +1 @@
7bbb25278f53
89ab9a816d70

View File

@ -0,0 +1,57 @@
# 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.
"""Rename ml2_network_segments table
Revision ID: 89ab9a816d70
Revises: 7bbb25278f53
Create Date: 2016-03-22 00:22:47.618593
"""
# revision identifiers, used by Alembic.
revision = '89ab9a816d70'
down_revision = '7bbb25278f53'
from alembic import op
from sqlalchemy.engine import reflection
TABLE_NAME = 'ml2_port_binding_levels'
OLD_REFERRED_TABLE_NAME = 'ml2_network_segments'
NEW_REFERRED_TABLE_NAME = 'networksegments'
def upgrade():
fk_name = delete_foreign_key_constraint()
op.rename_table(OLD_REFERRED_TABLE_NAME, NEW_REFERRED_TABLE_NAME)
op.create_foreign_key(
constraint_name=fk_name,
source_table=TABLE_NAME,
referent_table=NEW_REFERRED_TABLE_NAME,
local_cols=['segment_id'],
remote_cols=['id'],
ondelete="SET NULL"
)
def delete_foreign_key_constraint():
inspector = reflection.Inspector.from_engine(op.get_bind())
fk_constraints = inspector.get_foreign_keys(TABLE_NAME)
for fk in fk_constraints:
if fk['referred_table'] == OLD_REFERRED_TABLE_NAME:
op.drop_constraint(
constraint_name=fk['name'],
table_name=TABLE_NAME,
type_='foreignkey'
)
return fk['name']

View File

@ -48,6 +48,7 @@ from neutron.db.qos import models as qos_models # noqa
from neutron.db.quota import models # noqa
from neutron.db import rbac_db_models # noqa
from neutron.db import securitygroups_db # noqa
from neutron.db import segments_db # noqa
from neutron.db import servicetype_db # noqa
from neutron.db import tag_db # noqa
from neutron.ipam.drivers.neutrondb_ipam import db_models # noqa

143
neutron/db/segments_db.py Normal file
View File

@ -0,0 +1,143 @@
# 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_log import log as logging
from oslo_utils import uuidutils
import sqlalchemy as sa
from sqlalchemy.orm import exc
from neutron._i18n import _LI
from neutron.db import model_base
LOG = logging.getLogger(__name__)
"""
Some standalone plugins need a DB table to store provider
network information. Initially there was no such table,
but in Mitaka the ML2 NetworkSegment table was promoted here.
"""
class NetworkSegment(model_base.BASEV2, model_base.HasId):
"""Represent persistent state of a network segment.
A network segment is a portion of a neutron network with a
specific physical realization. A neutron network can consist of
one or more segments.
"""
network_id = sa.Column(sa.String(36),
sa.ForeignKey('networks.id', ondelete="CASCADE"),
nullable=False)
network_type = sa.Column(sa.String(32), nullable=False)
physical_network = sa.Column(sa.String(64))
segmentation_id = sa.Column(sa.Integer)
is_dynamic = sa.Column(sa.Boolean, default=False, nullable=False,
server_default=sa.sql.false())
segment_index = sa.Column(sa.Integer, nullable=False, server_default='0')
NETWORK_TYPE = NetworkSegment.network_type.name
PHYSICAL_NETWORK = NetworkSegment.physical_network.name
SEGMENTATION_ID = NetworkSegment.segmentation_id.name
def _make_segment_dict(record):
"""Make a segment dictionary out of a DB record."""
return {'id': record.id,
NETWORK_TYPE: record.network_type,
PHYSICAL_NETWORK: record.physical_network,
SEGMENTATION_ID: record.segmentation_id}
def add_network_segment(session, network_id, segment, segment_index=0,
is_dynamic=False):
with session.begin(subtransactions=True):
record = NetworkSegment(
id=uuidutils.generate_uuid(),
network_id=network_id,
network_type=segment.get(NETWORK_TYPE),
physical_network=segment.get(PHYSICAL_NETWORK),
segmentation_id=segment.get(SEGMENTATION_ID),
segment_index=segment_index,
is_dynamic=is_dynamic
)
session.add(record)
segment['id'] = record.id
LOG.info(_LI("Added segment %(id)s of type %(network_type)s for network "
"%(network_id)s"),
{'id': record.id,
'network_type': record.network_type,
'network_id': record.network_id})
def get_network_segments(session, network_id, filter_dynamic=False):
return get_networks_segments(
session, [network_id], filter_dynamic)[network_id]
def get_networks_segments(session, network_ids, filter_dynamic=False):
with session.begin(subtransactions=True):
query = (session.query(NetworkSegment).
filter(NetworkSegment.network_id.in_(network_ids)).
order_by(NetworkSegment.segment_index))
if filter_dynamic is not None:
query = query.filter_by(is_dynamic=filter_dynamic)
records = query.all()
result = {net_id: [] for net_id in network_ids}
for record in records:
result[record.network_id].append(_make_segment_dict(record))
return result
def get_segment_by_id(session, segment_id):
with session.begin(subtransactions=True):
try:
record = (session.query(NetworkSegment).
filter_by(id=segment_id).
one())
return _make_segment_dict(record)
except exc.NoResultFound:
return
def get_dynamic_segment(session, network_id, physical_network=None,
segmentation_id=None):
"""Return a dynamic segment for the filters provided if one exists."""
with session.begin(subtransactions=True):
query = (session.query(NetworkSegment).
filter_by(network_id=network_id, is_dynamic=True))
if physical_network:
query = query.filter_by(physical_network=physical_network)
if segmentation_id:
query = query.filter_by(segmentation_id=segmentation_id)
record = query.first()
if record:
return _make_segment_dict(record)
else:
LOG.debug("No dynamic segment found for "
"Network:%(network_id)s, "
"Physical network:%(physnet)s, "
"segmentation_id:%(segmentation_id)s",
{'network_id': network_id,
'physnet': physical_network,
'segmentation_id': segmentation_id})
return None
def delete_network_segment(session, segment_id):
"""Release a dynamic segment for the params provided if one exists."""
with session.begin(subtransactions=True):
(session.query(NetworkSegment).
filter_by(id=segment_id).delete())

View File

@ -20,13 +20,13 @@ import six
from sqlalchemy import or_
from sqlalchemy.orm import exc
from neutron._i18n import _LE, _LI
from neutron._i18n import _LE
from neutron.common import constants as n_const
from neutron.db import models_v2
from neutron.db import securitygroups_db as sg_db
from neutron.db import segments_db
from neutron.extensions import portbindings
from neutron import manager
from neutron.plugins.ml2 import driver_api as api
from neutron.plugins.ml2 import models
LOG = log.getLogger(__name__)
@ -34,96 +34,18 @@ LOG = log.getLogger(__name__)
# limit the number of port OR LIKE statements in one query
MAX_PORTS_PER_QUERY = 500
# The API methods from segments_db
add_network_segment = segments_db.add_network_segment
def _make_segment_dict(record):
"""Make a segment dictionary out of a DB record."""
return {api.ID: record.id,
api.NETWORK_TYPE: record.network_type,
api.PHYSICAL_NETWORK: record.physical_network,
api.SEGMENTATION_ID: record.segmentation_id}
get_network_segments = segments_db.get_network_segments
get_networks_segments = segments_db.get_networks_segments
def add_network_segment(session, network_id, segment, segment_index=0,
is_dynamic=False):
with session.begin(subtransactions=True):
record = models.NetworkSegment(
id=uuidutils.generate_uuid(),
network_id=network_id,
network_type=segment.get(api.NETWORK_TYPE),
physical_network=segment.get(api.PHYSICAL_NETWORK),
segmentation_id=segment.get(api.SEGMENTATION_ID),
segment_index=segment_index,
is_dynamic=is_dynamic
)
session.add(record)
segment[api.ID] = record.id
LOG.info(_LI("Added segment %(id)s of type %(network_type)s for network"
" %(network_id)s"),
{'id': record.id,
'network_type': record.network_type,
'network_id': record.network_id})
get_segment_by_id = segments_db.get_segment_by_id
get_dynamic_segment = segments_db.get_dynamic_segment
def get_network_segments(session, network_id, filter_dynamic=False):
return get_networks_segments(
session, [network_id], filter_dynamic)[network_id]
def get_networks_segments(session, network_ids, filter_dynamic=False):
with session.begin(subtransactions=True):
query = (session.query(models.NetworkSegment).
filter(models.NetworkSegment.network_id.in_(network_ids)).
order_by(models.NetworkSegment.segment_index))
if filter_dynamic is not None:
query = query.filter_by(is_dynamic=filter_dynamic)
records = query.all()
result = {net_id: [] for net_id in network_ids}
for record in records:
result[record.network_id].append(_make_segment_dict(record))
return result
def get_segment_by_id(session, segment_id):
with session.begin(subtransactions=True):
try:
record = (session.query(models.NetworkSegment).
filter_by(id=segment_id).
one())
return _make_segment_dict(record)
except exc.NoResultFound:
return
def get_dynamic_segment(session, network_id, physical_network=None,
segmentation_id=None):
"""Return a dynamic segment for the filters provided if one exists."""
with session.begin(subtransactions=True):
query = (session.query(models.NetworkSegment).
filter_by(network_id=network_id, is_dynamic=True))
if physical_network:
query = query.filter_by(physical_network=physical_network)
if segmentation_id:
query = query.filter_by(segmentation_id=segmentation_id)
record = query.first()
if record:
return _make_segment_dict(record)
else:
LOG.debug("No dynamic segment found for "
"Network:%(network_id)s, "
"Physical network:%(physnet)s, "
"segmentation_id:%(segmentation_id)s",
{'network_id': network_id,
'physnet': physical_network,
'segmentation_id': segmentation_id})
return None
def delete_network_segment(session, segment_id):
"""Release a dynamic segment for the params provided if one exists."""
with session.begin(subtransactions=True):
(session.query(models.NetworkSegment).
filter_by(id=segment_id).delete())
delete_network_segment = segments_db.delete_network_segment
def add_port_binding(session, port_id):

View File

@ -23,27 +23,6 @@ from neutron.extensions import portbindings
BINDING_PROFILE_LEN = 4095
class NetworkSegment(model_base.BASEV2, model_base.HasId):
"""Represent persistent state of a network segment.
A network segment is a portion of a neutron network with a
specific physical realization. A neutron network can consist of
one or more segments.
"""
__tablename__ = 'ml2_network_segments'
network_id = sa.Column(sa.String(36),
sa.ForeignKey('networks.id', ondelete="CASCADE"),
nullable=False)
network_type = sa.Column(sa.String(32), nullable=False)
physical_network = sa.Column(sa.String(64))
segmentation_id = sa.Column(sa.Integer)
is_dynamic = sa.Column(sa.Boolean, default=False, nullable=False,
server_default=sa.sql.false())
segment_index = sa.Column(sa.Integer, nullable=False, server_default='0')
class PortBinding(model_base.BASEV2):
"""Represent binding-related state of a port.
@ -95,7 +74,7 @@ class PortBindingLevel(model_base.BASEV2):
level = sa.Column(sa.Integer, primary_key=True, autoincrement=False)
driver = sa.Column(sa.String(64))
segment_id = sa.Column(sa.String(36),
sa.ForeignKey('ml2_network_segments.id',
sa.ForeignKey('networksegments.id',
ondelete="SET NULL"))