From db5e370b0d68c3e71626c99941fe487059b3cf88 Mon Sep 17 00:00:00 2001 From: Romil Gupta Date: Mon, 30 Jun 2014 05:35:08 -0700 Subject: [PATCH] Schema enhancement to support MultiSegment Network Description: Currently, there is nothing in the schema that ensures segments for a network are returned in the same order they were specified when the network was created, or even in a deterministic order. Solution: We need to add another field named 'segment_index' in 'ml2_network_segment' table containing a numeric position index. With segment_index field we can retrieve the segments in the order in which user created. This patch set also fixes ML2 invalid unit test case in test_create_network_multiprovider(). Closes-Bug: #1224978 Closes-Bug: #1377346 Change-Id: I560c34c6fe1c5425469ccdf9b8b4905c123d496d --- .../1f71e54a85e7_ml2_net_seg_model.py | 40 +++++++++++++++++++ .../alembic_migrations/versions/HEAD | 2 +- neutron/plugins/ml2/db.py | 7 +++- neutron/plugins/ml2/managers.py | 5 ++- neutron/plugins/ml2/models.py | 1 + neutron/tests/unit/ml2/test_ml2_plugin.py | 16 +++++--- 6 files changed, 60 insertions(+), 11 deletions(-) create mode 100644 neutron/db/migration/alembic_migrations/versions/1f71e54a85e7_ml2_net_seg_model.py diff --git a/neutron/db/migration/alembic_migrations/versions/1f71e54a85e7_ml2_net_seg_model.py b/neutron/db/migration/alembic_migrations/versions/1f71e54a85e7_ml2_net_seg_model.py new file mode 100644 index 00000000000..f72fbf0efa7 --- /dev/null +++ b/neutron/db/migration/alembic_migrations/versions/1f71e54a85e7_ml2_net_seg_model.py @@ -0,0 +1,40 @@ +# 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. +# + +"""ml2_network_segments models change for multi-segment network. + +Revision ID: 1f71e54a85e7 +Revises: 44621190bc02 +Create Date: 2014-10-15 18:30:51.395295 + +""" + +# revision identifiers, used by Alembic. +revision = '1f71e54a85e7' +down_revision = '44621190bc02' + + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + op.add_column('ml2_network_segments', + sa.Column('segment_index', sa.Integer(), nullable=False, + server_default='0')) + + +def downgrade(): + op.drop_column('ml2_network_segments', 'segment_index') diff --git a/neutron/db/migration/alembic_migrations/versions/HEAD b/neutron/db/migration/alembic_migrations/versions/HEAD index 8c20ac433be..daed716e185 100644 --- a/neutron/db/migration/alembic_migrations/versions/HEAD +++ b/neutron/db/migration/alembic_migrations/versions/HEAD @@ -1 +1 @@ -44621190bc02 +1f71e54a85e7 diff --git a/neutron/plugins/ml2/db.py b/neutron/plugins/ml2/db.py index d8caa9384af..37e91bc791c 100644 --- a/neutron/plugins/ml2/db.py +++ b/neutron/plugins/ml2/db.py @@ -39,7 +39,8 @@ def _make_segment_dict(record): api.SEGMENTATION_ID: record.segmentation_id} -def add_network_segment(session, network_id, segment, is_dynamic=False): +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(), @@ -47,6 +48,7 @@ def add_network_segment(session, network_id, segment, is_dynamic=False): 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) @@ -61,7 +63,8 @@ def add_network_segment(session, network_id, segment, is_dynamic=False): def get_network_segments(session, network_id, filter_dynamic=False): with session.begin(subtransactions=True): query = (session.query(models.NetworkSegment). - filter_by(network_id=network_id)) + filter_by(network_id=network_id). + order_by(models.NetworkSegment.segment_index)) if filter_dynamic is not None: query = query.filter_by(is_dynamic=filter_dynamic) records = query.all() diff --git a/neutron/plugins/ml2/managers.py b/neutron/plugins/ml2/managers.py index 9c6c6fc514e..8700934fe64 100644 --- a/neutron/plugins/ml2/managers.py +++ b/neutron/plugins/ml2/managers.py @@ -152,10 +152,11 @@ class TypeManager(stevedore.named.NamedExtensionManager): with session.begin(subtransactions=True): network_id = network['id'] if segments: - for segment in segments: + for segment_index, segment in enumerate(segments): segment = self.reserve_provider_segment( session, segment) - db.add_network_segment(session, network_id, segment) + db.add_network_segment(session, network_id, + segment, segment_index) else: segment = self.allocate_tenant_segment(session) db.add_network_segment(session, network_id, segment) diff --git a/neutron/plugins/ml2/models.py b/neutron/plugins/ml2/models.py index 80ae1bbb6a3..350c34e4f77 100644 --- a/neutron/plugins/ml2/models.py +++ b/neutron/plugins/ml2/models.py @@ -41,6 +41,7 @@ class NetworkSegment(model_base.BASEV2, models_v2.HasId): 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): diff --git a/neutron/tests/unit/ml2/test_ml2_plugin.py b/neutron/tests/unit/ml2/test_ml2_plugin.py index 9bd8ccd9f61..d1729194c73 100644 --- a/neutron/tests/unit/ml2/test_ml2_plugin.py +++ b/neutron/tests/unit/ml2/test_ml2_plugin.py @@ -583,20 +583,24 @@ class TestMultiSegmentNetworks(Ml2PluginV2TestCase): network_req = self.new_create_request('networks', data) network = self.deserialize(self.fmt, network_req.get_response(self.api)) - tz = network['network'][mpnet.SEGMENTS] - for tz in data['network'][mpnet.SEGMENTS]: + segments = network['network'][mpnet.SEGMENTS] + for segment_index, segment in enumerate(data['network'] + [mpnet.SEGMENTS]): for field in [pnet.NETWORK_TYPE, pnet.PHYSICAL_NETWORK, pnet.SEGMENTATION_ID]: - self.assertEqual(tz.get(field), tz.get(field)) + self.assertEqual(segment.get(field), + segments[segment_index][field]) # Tests get_network() net_req = self.new_show_request('networks', network['network']['id']) network = self.deserialize(self.fmt, net_req.get_response(self.api)) - tz = network['network'][mpnet.SEGMENTS] - for tz in data['network'][mpnet.SEGMENTS]: + segments = network['network'][mpnet.SEGMENTS] + for segment_index, segment in enumerate(data['network'] + [mpnet.SEGMENTS]): for field in [pnet.NETWORK_TYPE, pnet.PHYSICAL_NETWORK, pnet.SEGMENTATION_ID]: - self.assertEqual(tz.get(field), tz.get(field)) + self.assertEqual(segment.get(field), + segments[segment_index][field]) def test_create_network_with_provider_and_multiprovider_fail(self): data = {'network': {'name': 'net1',