--- /dev/null
+..
+ 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.
+
+
+ Convention for heading levels in Neutron devref:
+ ======= Heading 0 (reserved for the title in a document)
+ ------- Heading 1
+ ~~~~~~~ Heading 2
+ +++++++ Heading 3
+ ''''''' Heading 4
+ (Avoid deeper levels because they do not render well.)
+
+
+Template for ModelMigrationSync for external repos
+==================================================
+
+This section contains a template for a test which checks that the Python models
+for database tables are synchronized with the alembic migrations that create
+the database schema. This test should be implemented in all driver/plugin
+repositories that were split out from Neutron.
+
+What does the test do?
+----------------------
+
+This test compares models with the result of existing migrations. It is based on
+`ModelsMigrationsSync
+<http://docs.openstack.org/developer/oslo.db/api/sqlalchemy/test_migrations.html#oslo_db.sqlalchemy.test_migrations.ModelsMigrationsSync>`_
+which is provided by oslo.db and was adapted for Neutron. It compares core
+Neutron models and vendor specific models with migrations from Neutron core and
+migrations from the driver/plugin repo. This test is functional - it runs against
+MySQL and PostgreSQL dialects. The detailed description of this test can be
+found in Neutron Database Layer section - `Tests to verify that database
+migrations and models are in sync
+<http://docs.openstack.org/developer/neutron/devref/db_layer.html#module-neutron.tests.functional.db.test_migrations>`_.
+
+Steps for implementing the test
+-------------------------------
+
+1. Import all models in one place
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Create a module ``networking_foo/db/models/head.py`` with the following
+content: ::
+
+ from neutron.db.migration.models import head
+ from networking_foo import models # noqa
+ # Alternatively, import separate modules here if the models are not in one
+ # models.py file
+
+
+ def get_metadata():
+ return head.model_base.BASEV2.metadata
+
+
+2. Implement the test module
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The test uses external.py from Neutron. This file contains lists of table
+names, which were moved out of Neutron: ::
+
+ VPNAAS_TABLES = [...]
+
+ LBAAS_TABLES = [...]
+
+ FWAAS_TABLES = [...]
+
+ # Arista ML2 driver Models moved to openstack/networking-arista
+ REPO_ARISTA_TABLES = [...]
+
+ # Models moved to openstack/networking-cisco
+ REPO_CISCO_TABLES = [...]
+
+ ...
+
+ TABLES = (FWAAS_TABLES + LBAAS_TABLES + VPNAAS_TABLES + ...
+ + REPO_ARISTA_TABLES + REPO_CISCO_TABLES)
+
+
+Create a module ``networking_foo/tests/functional/db/test_migrations.py``
+with the following content: ::
+
+ from oslo_config import cfg
+
+ from neutron.db.migration.alembic_migrations import external
+ from neutron.db.migration import cli as migration
+ from neutron.tests.common import base
+ from neutron.tests.functional.db import test_migrations
+
+ from networking_foo.db.migration.alembic_migrations import env
+ from networking_foo.db.models import head
+
+ # EXTERNAL_TABLES should contain all names of tables that are not related to
+ # current repo.
+ EXTERNAL_TABLES = set(external.TABLES) - set(external.REPO_FOO_TABLES)
+
+
+ class _TestModelsMigrationsFoo(test_migrations._TestModelsMigrations):
+
+ def db_sync(self, engine):
+ cfg.CONF.set_override('connection', engine.url, group='database')
+ for conf in migration.get_alembic_configs():
+ self.alembic_config = conf
+ self.alembic_config.neutron_config = cfg.CONF
+ migration.do_alembic_command(conf, 'upgrade', 'heads')
+
+ def get_metadata(self):
+ return head.get_metadata()
+
+ def include_object(self, object_, name, type_, reflected, compare_to):
+ if type_ == 'table' and (name == 'alembic' or
+ name == env.VERSION_TABLE or
+ name in EXTERNAL_TABLES):
+ return False
+ else:
+ return True
+
+
+ class TestModelsMigrationsMysql(_TestModelsMigrationsFoo,
+ base.MySQLTestCase):
+ pass
+
+
+ class TestModelsMigrationsPsql(_TestModelsMigrationsFoo,
+ base.PostgreSQLTestCase):
+ pass
+
+
+3. Add functional requirements
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A separate file ``networking_foo/tests/functional/requirements.txt`` should be
+created containing the following requirements that are needed for successful
+test execution.
+
+::
+
+ psutil>=1.1.1,<2.0.0
+ psycopg2
+ PyMySQL>=0.6.2 # MIT License
+
+
+Example implementation `in VPNaaS <https://review.openstack.org/209943>`_