]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Make Juno migrations config independent
authorSalvatore Orlando <salv.orlando@gmail.com>
Wed, 23 Jul 2014 19:16:21 +0000 (12:16 -0700)
committerHenry Gessau <gessau@cisco.com>
Fri, 12 Sep 2014 03:09:45 +0000 (23:09 -0400)
This patch amends migrations added after the icehouse release
and before the healing migration.
Migrations are changed in a way that they are not anymore
dependent on configuration parameters but are anyway aware of
the fact that the database has not yet been healed.

To this aim, amended migrations now will need to inspect the
current schema and cannot be anymore be used in offline mode;
this is consistent with the behaviour of the healing migration.

This patch does not remove the logic for generating and
managing configuration-dependent migrations. For this reason
upgrade and downgrade routines still accept the active_plugins
parameter, which will not be used.

Change-Id: I9d55a01c64ef555b7774099f497c9eea596aea6e
Partially-implements: blueprint reorganize-migrations

13 files changed:
neutron/db/migration/__init__.py
neutron/db/migration/alembic_migrations/versions/10cd28e692e9_nuage_extraroute.py
neutron/db/migration/alembic_migrations/versions/1b837a7125a9_cisco_apic_driver.py
neutron/db/migration/alembic_migrations/versions/1e5dd1d09b22_set_not_null_fields_lb_stats.py
neutron/db/migration/alembic_migrations/versions/2db5203cb7a9_nuage_floatingip.py
neutron/db/migration/alembic_migrations/versions/33c3db036fe4_set_length_of_description_field_metering.py
neutron/db/migration/alembic_migrations/versions/4eca4a84f08a_remove_ml2_cisco_cred_db.py
neutron/db/migration/alembic_migrations/versions/5446f2a45467_set_server_default.py
neutron/db/migration/alembic_migrations/versions/54f7549a0e5f_set_not_null_peer_address.py
neutron/db/migration/alembic_migrations/versions/6be312499f9_set_not_null_vlan_id_cisco.py
neutron/db/migration/alembic_migrations/versions/b65aa907aec_set_length_of_protocol_field.py
neutron/db/migration/alembic_migrations/versions/d06e871c0d5_set_admin_state_up_not_null_ml2.py
neutron/tests/unit/test_db_migration.py

index 2f8fae257a182303474807b773dc9277115437b1..56f51f79906d2049e78968d33f5a2806eae86892 100644 (file)
@@ -14,6 +14,9 @@
 #
 # @author: Mark McClain, DreamHost
 
+import functools
+
+from alembic import context
 from alembic import op
 import sqlalchemy as sa
 
@@ -22,6 +25,75 @@ OVS_PLUGIN = ('neutron.plugins.openvswitch.ovs_neutron_plugin'
 CISCO_PLUGIN = 'neutron.plugins.cisco.network_plugin.PluginV2'
 
 
+def skip_if_offline(func):
+    """Decorator for skipping migrations in offline mode."""
+    @functools.wraps(func)
+    def decorator(*args, **kwargs):
+        if context.is_offline_mode():
+            return
+        return func(*args, **kwargs)
+
+    return decorator
+
+
+def raise_if_offline(func):
+    """Decorator for raising if a function is called in offline mode."""
+    @functools.wraps(func)
+    def decorator(*args, **kwargs):
+        if context.is_offline_mode():
+            raise RuntimeError(_("%s cannot be called while in offline mode") %
+                               func.__name__)
+        return func(*args, **kwargs)
+
+    return decorator
+
+
+@raise_if_offline
+def schema_has_table(table_name):
+    """Check whether the specified table exists in the current schema.
+
+    This method cannot be executed in offline mode.
+    """
+    bind = op.get_bind()
+    insp = sa.engine.reflection.Inspector.from_engine(bind)
+    return table_name in insp.get_table_names()
+
+
+@raise_if_offline
+def schema_has_column(table_name, column_name):
+    """Check whether the specified column exists in the current schema.
+
+    This method cannot be executed in offline mode.
+    """
+    bind = op.get_bind()
+    insp = sa.engine.reflection.Inspector.from_engine(bind)
+    # first check that the table exists
+    if not schema_has_table(table_name):
+        return
+    # check whether column_name exists in table columns
+    return column_name in [column['name'] for column in
+                           insp.get_columns(table_name)]
+
+
+@raise_if_offline
+def alter_column_if_exists(table_name, column_name, **kwargs):
+    """Alter a column only if it exists in the schema."""
+    if schema_has_column(table_name, column_name):
+        op.alter_column(table_name, column_name, **kwargs)
+
+
+@raise_if_offline
+def drop_table_if_exists(table_name):
+    if schema_has_table(table_name):
+        op.drop_table(table_name)
+
+
+@raise_if_offline
+def rename_table_if_exists(old_table_name, new_table_name):
+    if schema_has_table(old_table_name):
+        op.rename_table(old_table_name, new_table_name)
+
+
 def should_run(active_plugins, migrate_plugins):
     if '*' in migrate_plugins:
         return True
index 2949813bb6b98c619c72b17b80d0c3a9be019835..5e624d5e6760764f981c26c44388809b5f125699 100644 (file)
@@ -25,11 +25,6 @@ Create Date: 2014-05-14 14:47:53.148132
 revision = '10cd28e692e9'
 down_revision = '1b837a7125a9'
 
-# Change to ['*'] if this migration applies to all plugins
-
-migration_for_plugins = [
-    'neutron.plugins.nuage.plugin.NuagePlugin'
-]
 
 from alembic import op
 import sqlalchemy as sa
@@ -38,9 +33,6 @@ 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(
         'routerroutes_mapping',
         sa.Column('router_id', sa.String(length=36), nullable=False),
@@ -48,21 +40,27 @@ def upgrade(active_plugins=None, options=None):
         sa.ForeignKeyConstraint(['router_id'], ['routers.id'],
                                 ondelete='CASCADE'),
     )
-    op.create_table(
-        'routerroutes',
-        sa.Column('destination', sa.String(length=64), nullable=False),
-        sa.Column('nexthop', sa.String(length=64), nullable=False),
-        sa.Column('router_id', sa.String(length=36), nullable=False),
-        sa.ForeignKeyConstraint(['router_id'], ['routers.id'],
-                                ondelete='CASCADE'),
-        sa.PrimaryKeyConstraint('destination', 'nexthop',
-                                'router_id'),
-    )
+    # This table might already exist as it might have been created
+    # if another plugin was configured before the nuage one
+    if op.get_bind().engine.dialect.name == 'postgresql':
+        migration.create_table_if_not_exist_psql(
+            'routerroutes',
+            ("(destination VARCHAR(64) NOT NULL,"
+             "nexthop VARCHAR(64) NOT NULL,"
+             "router_id VARCHAR(36) NOT NULL,"
+             "PRIMARY KEY (destination, nexthop, router_id),"
+             "FOREIGN KEY (router_id) REFERENCES routers (id) "
+             "ON DELETE CASCADE ON UPDATE CASCADE)"))
+    else:
+        op.execute("CREATE TABLE IF NOT EXISTS routerroutes( "
+                   "destination VARCHAR(64) NOT NULL,"
+                   "nexthop VARCHAR(64) NOT NULL,"
+                   "router_id VARCHAR(36) NOT NULL,"
+                   "PRIMARY KEY (destination, nexthop, router_id),"
+                   "FOREIGN KEY (router_id) REFERENCES routers (id) "
+                   "ON DELETE CASCADE ON UPDATE CASCADE)")
 
 
 def downgrade(active_plugins=None, options=None):
-    if not migration.should_run(active_plugins, migration_for_plugins):
-        return
-
-    op.drop_table('routerroutes')
-    op.drop_table('routerroutes_mapping')
+    # The routerroutes table should not be dropped
+    op.execute('DROP TABLE IF EXISTS routerroutes_mapping')
index 92b132643c9ca9c8608d3fcaad311cdb98f584d9..bcc22813422ad8cc0e62449d21c4743c1f7afbf3 100644 (file)
@@ -25,20 +25,12 @@ Create Date: 2014-02-13 09:35:19.147619
 revision = '1b837a7125a9'
 down_revision = '6be312499f9'
 
-migration_for_plugins = [
-    'neutron.plugins.ml2.plugin.Ml2Plugin'
-]
 
 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(
         'cisco_ml2_apic_epgs',
         sa.Column('network_id', sa.String(length=255), nullable=False),
@@ -66,9 +58,6 @@ def upgrade(active_plugins=None, options=None):
 
 
 def downgrade(active_plugins=None, options=None):
-    if not migration.should_run(active_plugins, migration_for_plugins):
-        return
-
     op.drop_table('cisco_ml2_apic_contracts')
     op.drop_table('cisco_ml2_apic_port_profiles')
     op.drop_table('cisco_ml2_apic_epgs')
index 6d4d3adee49cce3f9db9944765d62c6cfff3edea..daa007a09583c0aee81cfb2250bcb143d48bc13c 100644 (file)
@@ -27,40 +27,51 @@ down_revision = '54f7549a0e5f'
 
 # Change to ['*'] if this migration applies to all plugins
 
-migration_for_plugins = [
-    'neutron.services.loadbalancer.plugin.LoadBalancerPlugin'
-]
+# This migration will be executed only if the neutron DB schema
+# contains the tables for load balancing service plugin.
+# This migration will be skipped when executed in offline mode.
 
-from alembic import op
-import sqlalchemy as sa
 
+import sqlalchemy as sa
 
 from neutron.db import migration
 
 
+@migration.skip_if_offline
 def upgrade(active_plugins=None, options=None):
-    if not migration.should_run(active_plugins, migration_for_plugins):
-        return
-
-    op.alter_column('poolstatisticss', 'bytes_in', nullable=False,
-                    existing_type=sa.BigInteger())
-    op.alter_column('poolstatisticss', 'bytes_out', nullable=False,
-                    existing_type=sa.BigInteger())
-    op.alter_column('poolstatisticss', 'active_connections', nullable=False,
-                    existing_type=sa.BigInteger())
-    op.alter_column('poolstatisticss', 'total_connections', nullable=False,
-                    existing_type=sa.BigInteger())
+    migration.alter_column_if_exists(
+        'poolstatisticss', 'bytes_in',
+        nullable=False,
+        existing_type=sa.BigInteger())
+    migration.alter_column_if_exists(
+        'poolstatisticss', 'bytes_out',
+        nullable=False,
+        existing_type=sa.BigInteger())
+    migration.alter_column_if_exists(
+        'poolstatisticss', 'active_connections',
+        nullable=False,
+        existing_type=sa.BigInteger())
+    migration.alter_column_if_exists(
+        'poolstatisticss', 'total_connections',
+        nullable=False,
+        existing_type=sa.BigInteger())
 
 
+@migration.skip_if_offline
 def downgrade(active_plugins=None, options=None):
-    if not migration.should_run(active_plugins, migration_for_plugins):
-        return
-
-    op.alter_column('poolstatisticss', 'bytes_in', nullable=True,
-                    existing_type=sa.BigInteger())
-    op.alter_column('poolstatisticss', 'bytes_out', nullable=True,
-                    existing_type=sa.BigInteger())
-    op.alter_column('poolstatisticss', 'active_connections', nullable=True,
-                    existing_type=sa.BigInteger())
-    op.alter_column('poolstatisticss', 'total_connections', nullable=True,
-                    existing_type=sa.BigInteger())
+    migration.alter_column_if_exists(
+        'poolstatisticss', 'bytes_in',
+        nullable=True,
+        existing_type=sa.BigInteger())
+    migration.alter_column_if_exists(
+        'poolstatisticss', 'bytes_out',
+        nullable=True,
+        existing_type=sa.BigInteger())
+    migration.alter_column_if_exists(
+        'poolstatisticss', 'active_connections',
+        nullable=True,
+        existing_type=sa.BigInteger())
+    migration.alter_column_if_exists(
+        'poolstatisticss', 'total_connections',
+        nullable=True,
+        existing_type=sa.BigInteger())
index 57876d0968a5af37ab1302027fdeaf5788cad9e3..950caf4896ad78de9ed98430fc7e48b3a67b28b7 100644 (file)
@@ -25,20 +25,21 @@ Create Date: 2014-05-19 16:39:42.048125
 revision = '2db5203cb7a9'
 down_revision = '10cd28e692e9'
 
-migration_for_plugins = [
-    'neutron.plugins.nuage.plugin.NuagePlugin'
-]
 
 from alembic import op
 import sqlalchemy as sa
 
 from neutron.db import migration
 
+# This migration will be executed only if the neutron DB schema contains
+# the tables for the nuage plugin.
+# This migration will be skipped when executed in offline mode.
 
-def upgrade(active_plugins=None, options=None):
-    if not migration.should_run(active_plugins, migration_for_plugins):
-        return
 
+@migration.skip_if_offline
+def upgrade(active_plugins=None, options=None):
+    # These tables will be created even if the nuage plugin is not enabled.
+    # This is fine as they would be created anyway by the healing migration.
     op.create_table(
         'nuage_floatingip_pool_mapping',
         sa.Column('fip_pool_id', sa.String(length=36), nullable=False),
@@ -59,25 +60,31 @@ def upgrade(active_plugins=None, options=None):
                                 ondelete='CASCADE'),
         sa.PrimaryKeyConstraint('fip_id'),
     )
-    op.rename_table('net_partitions', 'nuage_net_partitions')
-    op.rename_table('net_partition_router_mapping',
-                    'nuage_net_partition_router_mapping')
-    op.rename_table('router_zone_mapping', 'nuage_router_zone_mapping')
-    op.rename_table('subnet_l2dom_mapping', 'nuage_subnet_l2dom_mapping')
-    op.rename_table('port_mapping', 'nuage_port_mapping')
-    op.rename_table('routerroutes_mapping', 'nuage_routerroutes_mapping')
+    migration.rename_table_if_exists('net_partitions',
+                                     'nuage_net_partitions')
+    migration.rename_table_if_exists('net_partition_router_mapping',
+                                     'nuage_net_partition_router_mapping')
+    migration.rename_table_if_exists('router_zone_mapping',
+                                     'nuage_router_zone_mapping')
+    migration.rename_table_if_exists('subnet_l2dom_mapping',
+                                     'nuage_subnet_l2dom_mapping')
+    migration.rename_table_if_exists('port_mapping',
+                                     'nuage_port_mapping')
+    migration.rename_table_if_exists('routerroutes_mapping',
+                                     'nuage_routerroutes_mapping')
 
 
+@migration.skip_if_offline
 def downgrade(active_plugins=None, options=None):
-    if not migration.should_run(active_plugins, migration_for_plugins):
-        return
-
-    op.drop_table('nuage_floatingip_mapping')
-    op.drop_table('nuage_floatingip_pool_mapping')
-    op.rename_table('nuage_net_partitions', 'net_partitions')
-    op.rename_table('nuage_net_partition_router_mapping',
-                    'net_partition_router_mapping')
-    op.rename_table('nuage_router_zone_mapping', 'router_zone_mapping')
-    op.rename_table('nuage_subnet_l2dom_mapping', 'subnet_l2dom_mapping')
-    op.rename_table('nuage_port_mapping', 'port_mapping')
-    op.rename_table('nuage_routerroutes_mapping', 'routerroutes_mapping')
+    migration.drop_table_if_exists('nuage_floatingip_mapping')
+    migration.drop_table_if_exists('nuage_floatingip_pool_mapping')
+    migration.rename_table_if_exists('nuage_net_partitions', 'net_partitions')
+    migration.rename_table_if_exists('nuage_net_partition_router_mapping',
+                                     'net_partition_router_mapping')
+    migration.rename_table_if_exists('nuage_router_zone_mapping',
+                                     'router_zone_mapping')
+    migration.rename_table_if_exists('nuage_subnet_l2dom_mapping',
+                                     'subnet_l2dom_mapping')
+    migration.rename_table_if_exists('nuage_port_mapping', 'port_mapping')
+    migration.rename_table_if_exists('nuage_routerroutes_mapping',
+                                     'routerroutes_mapping')
index 28dd886d738496313ab1f4b84a0a60681fea70ed..67dcbc2365bd6884b66b9342c64c8baf8af741c1 100644 (file)
@@ -25,12 +25,6 @@ Create Date: 2014-03-25 11:04:27.341830
 revision = '33c3db036fe4'
 down_revision = 'b65aa907aec'
 
-# Change to ['*'] if this migration applies to all plugins
-
-migration_for_plugins = [
-    'neutron.services.metering.metering_plugin.MeteringPlugin'
-]
-
 from alembic import op
 import sqlalchemy as sa
 
@@ -38,9 +32,6 @@ from neutron.db import migration
 
 
 def upgrade(active_plugins=None, options=None):
-    if not migration.should_run(active_plugins, migration_for_plugins):
-        return
-
     if op.get_bind().engine.dialect.name == 'postgresql':
         migration.create_table_if_not_exist_psql(
             'meteringlabels',
@@ -60,7 +51,4 @@ def upgrade(active_plugins=None, options=None):
 
 
 def downgrade(active_plugins=None, options=None):
-    if not migration.should_run(active_plugins, migration_for_plugins):
-        return
-
     pass
index 10bc6fee825d4b4e9b06d35a6270a288ee8e1c08..465441995f072ed8320118b27954430d056d3947 100644 (file)
@@ -25,35 +25,31 @@ Create Date: 2014-04-10 19:32:46.697189
 revision = '4eca4a84f08a'
 down_revision = '33c3db036fe4'
 
-# Change to ['*'] if this migration applies to all plugins
-
-migration_for_plugins = [
-    'neutron.plugins.ml2.plugin.Ml2Plugin'
-]
 
 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.drop_table('cisco_ml2_credentials')
+    op.execute('DROP TABLE IF EXISTS cisco_ml2_credentials')
 
 
 def downgrade(active_plugins=None, options=None):
-    if not migration.should_run(active_plugins, migration_for_plugins):
-        return
-
-    op.create_table(
-        'cisco_ml2_credentials',
-        sa.Column('credential_id', sa.String(length=255), nullable=True),
-        sa.Column('tenant_id', sa.String(length=255), nullable=False),
-        sa.Column('credential_name', sa.String(length=255), nullable=False),
-        sa.Column('user_name', sa.String(length=255), nullable=True),
-        sa.Column('password', sa.String(length=255), nullable=True),
-        sa.PrimaryKeyConstraint('tenant_id', 'credential_name')
-    )
+    if op.get_bind().engine.dialect.name == 'postgresql':
+        migration.create_table_if_not_exist_psql(
+            'cisco_ml2_credentials',
+            ("(credential_id VARCHAR(255) NULL,"
+             "tenant_id VARCHAR(255) NOT NULL,"
+             "credential_name VARCHAR(255) NOT NULL,"
+             "user_name VARCHAR(255) NULL,"
+             "password VARCHAR(255) NULL,"
+             "PRIMARY KEY (tenant_id, credential_name))"))
+    else:
+        op.execute('CREATE TABLE IF NOT EXISTS cisco_ml2_credentials( '
+                   'credential_id VARCHAR(255) NULL,'
+                   'tenant_id VARCHAR(255) NOT NULL,'
+                   'credential_name VARCHAR(255) NOT NULL,'
+                   'user_name VARCHAR(255) NULL,'
+                   'password VARCHAR(255) NULL,'
+                   'PRIMARY KEY (tenant_id, credential_name))')
index f98e2c5cdb0841b542653c5e314c4c06032a9126..9c9f94c2e91b159d426528b7f019ed2b5b9e1f2c 100644 (file)
@@ -26,33 +26,28 @@ revision = '5446f2a45467'
 down_revision = '2db5203cb7a9'
 
 
-from alembic import op
 import sqlalchemy as sa
 import sqlalchemy.sql
 
 
+from neutron.db import migration
 from neutron.plugins.cisco.common import cisco_constants
 
-PLUGINS = {
-    'brocade': 'neutron.plugins.brocade.NeutronPlugin.BrocadePluginV2',
-    'cisco': 'neutron.plugins.cisco.network_plugin.PluginV2',
-    'ml2': 'neutron.plugins.ml2.plugin.Ml2Plugin',
-    'mlnx': 'neutron.plugins.mlnx.mlnx_plugin.MellanoxEswitchPlugin',
-    'vmware': [
-        'neutron.plugins.nicira.NeutronPlugin.NvpPluginV2',
-        'neutron.plugins.nicira.NeutronServicePlugin.NvpAdvancedPlugin',
-        'neutron.plugins.vmware.plugin.NsxPlugin',
-        'neutron.plugins.vmware.plugin.NsxServicePlugin',
-    ],
-    'agents': [
-        'neutron.plugins.linuxbridge.lb_neutron_plugin.LinuxBridgePluginV2',
-        'neutron.plugins.nec.nec_plugin.NECPluginV2',
-        'neutron.plugins.oneconvergence.plugin.OneConvergencePluginV2',
-        'neutron.plugins.openvswitch.ovs_neutron_plugin.OVSNeutronPluginV2',
-        'neutron.plugins.ibm.sdnve_neutron_plugin.SdnvePluginV2',
-        'neutron.services.loadbalancer.plugin.LoadBalancerPlugin',
-    ],
-}
+# This migration will be executed only if then Neutron db contains tables for
+# selected plugins and agents.
+# required tables and columns are:
+# brocade_ports.port_id
+# segmentation_id_llocation.allocated
+# cisco_n1kv_profile_bindings.tenant_id
+# cisco_network_profiles.multicast_ip_index
+# cisco_n1kv_vlan_allocations.allocated
+# nsxrouterextattributess.service_router
+# nsxrouterextattributess.distributed
+# qosqueues.default
+# agents.admin_state_up
+# ml2_gre_allocations.allocated
+# ml2_vxlan_allocations.allocated
+# This migration will be skipped when executed offline mode.
 
 
 def upgrade(active_plugins=None, options=None):
@@ -63,37 +58,33 @@ def downgrade(active_plugins=None, options=None):
     run(active_plugins, None)
 
 
+@migration.skip_if_offline
 def run(active_plugins, default):
-    if PLUGINS['ml2'] in active_plugins:
-        set_default_ml2(default)
-    if PLUGINS['mlnx'] in active_plugins:
-        set_default_agents(default)
-        set_default_mlnx(default)
-    if PLUGINS['brocade'] in active_plugins:
-        set_default_agents(default)
-        set_default_brocade(default)
-    if PLUGINS['cisco'] in active_plugins:
-        set_default_cisco(default)
-    if set(PLUGINS['vmware']) & set(active_plugins):
-            set_default_vmware(default)
-            set_default_agents(default)
-    if set(PLUGINS['agents']) & set(active_plugins):
-        set_default_agents(default)
+    set_default_ml2(default)
+    set_default_mlnx(default)
+    set_default_brocade(default)
+    set_default_cisco(default)
+    set_default_vmware(default)
+    set_default_agents(default)
 
 
 def set_default_brocade(default):
     if default:
         default = ''
-    op.alter_column('brocadeports', 'port_id',
-                    server_default=default, existing_type=sa.String(36))
+    migration.alter_column_if_exists(
+        'brocadeports', 'port_id',
+        server_default=default,
+        existing_type=sa.String(36))
 
 
 def set_default_mlnx(default):
     if default:
         default = sqlalchemy.sql.false()
-    op.alter_column('segmentation_id_allocation', 'allocated',
-                    server_default=default, existing_nullable=False,
-                    existing_type=sa.Boolean)
+    migration.alter_column_if_exists(
+        'segmentation_id_allocation', 'allocated',
+        server_default=default,
+        existing_nullable=False,
+        existing_type=sa.Boolean)
 
 
 def set_default_cisco(default):
@@ -102,47 +93,61 @@ def set_default_cisco(default):
     profile_default = '0' if default else None
     if default:
         default = sqlalchemy.sql.false()
-    op.alter_column('cisco_n1kv_profile_bindings', 'tenant_id',
-                    existing_type=sa.String(length=36),
-                    server_default=profile_binding_default,
-                    existing_nullable=False)
-    op.alter_column('cisco_network_profiles', 'multicast_ip_index',
-                    server_default=profile_default, existing_type=sa.Integer)
-    op.alter_column('cisco_n1kv_vlan_allocations', 'allocated',
-                    existing_type=sa.Boolean,
-                    server_default=default, existing_nullable=False)
-    op.alter_column('cisco_n1kv_vxlan_allocations', 'allocated',
-                    existing_type=sa.Boolean,
-                    server_default=default, existing_nullable=False)
+    migration.alter_column_if_exists(
+        'cisco_n1kv_profile_bindings', 'tenant_id',
+        existing_type=sa.String(length=36),
+        server_default=profile_binding_default,
+        existing_nullable=False)
+    migration.alter_column_if_exists(
+        'cisco_network_profiles', 'multicast_ip_index',
+        server_default=profile_default,
+        existing_type=sa.Integer)
+    migration.alter_column_if_exists(
+        'cisco_n1kv_vlan_allocations', 'allocated',
+        existing_type=sa.Boolean,
+        server_default=default,
+        existing_nullable=False)
 
 
 def set_default_vmware(default=None):
     if default:
         default = sqlalchemy.sql.false()
-    op.alter_column('nsxrouterextattributess', 'service_router',
-                    server_default=default, existing_nullable=False,
-                    existing_type=sa.Boolean)
-    op.alter_column('nsxrouterextattributess', 'distributed',
-                    server_default=default, existing_nullable=False,
-                    existing_type=sa.Boolean)
-    op.alter_column('qosqueues', 'default',
-                    server_default=default, existing_type=sa.Boolean)
+    migration.alter_column_if_exists(
+        'nsxrouterextattributess', 'service_router',
+        server_default=default,
+        existing_nullable=False,
+        existing_type=sa.Boolean)
+    migration.alter_column_if_exists(
+        'nsxrouterextattributess', 'distributed',
+        server_default=default,
+        existing_nullable=False,
+        existing_type=sa.Boolean)
+    migration.alter_column_if_exists(
+        'qosqueues', 'default',
+        server_default=default,
+        existing_type=sa.Boolean)
 
 
 def set_default_agents(default=None):
     if default:
         default = sqlalchemy.sql.true()
-    op.alter_column('agents', 'admin_state_up',
-                    server_default=default, existing_nullable=False,
-                    existing_type=sa.Boolean)
+    migration.alter_column_if_exists(
+        'agents', 'admin_state_up',
+        server_default=default,
+        existing_nullable=False,
+        existing_type=sa.Boolean)
 
 
 def set_default_ml2(default=None):
     if default:
         default = sqlalchemy.sql.false()
-    op.alter_column('ml2_gre_allocations', 'allocated',
-                    server_default=default, existing_nullable=False,
-                    existing_type=sa.Boolean)
-    op.alter_column('ml2_vxlan_allocations', 'allocated',
-                    server_default=default, existing_nullable=False,
-                    existing_type=sa.Boolean)
+    migration.alter_column_if_exists(
+        'ml2_gre_allocations', 'allocated',
+        server_default=default,
+        existing_nullable=False,
+        existing_type=sa.Boolean)
+    migration.alter_column_if_exists(
+        'ml2_vxlan_allocations', 'allocated',
+        server_default=default,
+        existing_nullable=False,
+        existing_type=sa.Boolean)
index 626c26fb706a8339df021769392f8318c2f38bff..c3ce00b033b4dc3570aebc91c128b0156045132c 100644 (file)
@@ -25,30 +25,27 @@ Create Date: 2014-03-17 11:00:17.539028
 revision = '54f7549a0e5f'
 down_revision = 'icehouse'
 
-# Change to ['*'] if this migration applies to all plugins
+# This migration will be executed only if the neutron DB schema
+# contains the tables for VPN service plugin.
+# This migration will be skipped when executed in offline mode.
 
-migration_for_plugins = [
-    'neutron.services.vpn.plugin.VPNDriverPlugin'
-]
 
-from alembic import op
 import sqlalchemy as sa
 
-
 from neutron.db import migration
 
 
+@migration.skip_if_offline
 def upgrade(active_plugins=None, options=None):
-    if not migration.should_run(active_plugins, migration_for_plugins):
-        return
-
-    op.alter_column('ipsec_site_connections', 'peer_address',
-                    existing_type=sa.String(255), nullable=False)
+    migration.alter_column_if_exists(
+        'ipsec_site_connections', 'peer_address',
+        existing_type=sa.String(255),
+        nullable=False)
 
 
+@migration.skip_if_offline
 def downgrade(active_plugins=None, options=None):
-    if not migration.should_run(active_plugins, migration_for_plugins):
-        return
-
-    op.alter_column('ipsec_site_connections', 'peer_address', nullable=True,
-                    existing_type=sa.String(255))
+    migration.alter_column_if_exists(
+        'ipsec_site_connections', 'peer_address',
+        nullable=True,
+        existing_type=sa.String(255))
index e304fdc24a0aa0b00aa60be54ecb38c28c362c4a..72bf07d01574164e8cf3deef25b0a5dd38d0e37d 100644 (file)
@@ -25,30 +25,27 @@ Create Date: 2014-03-27 14:38:12.571173
 revision = '6be312499f9'
 down_revision = 'd06e871c0d5'
 
-# Change to ['*'] if this migration applies to all plugins
+# This migration will be executed only if the neutron DB schema
+# contains the tables for the cisco plugin.
+# This migration will be skipped when executed in offline mode.
 
-migration_for_plugins = [
-    'neutron.plugins.cisco.network_plugin.PluginV2'
-]
-
-from alembic import op
 import sqlalchemy as sa
 
 
 from neutron.db import migration
 
 
+@migration.skip_if_offline
 def upgrade(active_plugins=None, options=None):
-    if not migration.should_run(active_plugins, migration_for_plugins):
-        return
-
-    op.alter_column('cisco_nexusport_bindings', 'vlan_id', nullable=False,
-                    existing_type=sa.Integer)
+    migration.alter_column_if_exists(
+        'cisco_nexusport_bindings', 'vlan_id',
+        nullable=False,
+        existing_type=sa.Integer)
 
 
+@migration.skip_if_offline
 def downgrade(active_plugins=None, options=None):
-    if not migration.should_run(active_plugins, migration_for_plugins):
-        return
-
-    op.alter_column('cisco_nexusport_bindings', 'vlan_id', nullable=True,
-                    existing_type=sa.Integer)
+    migration.alter_column_if_exists(
+        'cisco_nexusport_bindings', 'vlan_id',
+        nullable=True,
+        existing_type=sa.Integer)
index de82ce50582db28fb00d9a1a3820f75d03751a8d..df55b8410763cd9554f19be4e3909b0bbd1a1010 100644 (file)
@@ -16,7 +16,7 @@
 """set_length_of_protocol_field
 
 Revision ID: b65aa907aec
-Revises: 2447ad0e9585
+Revises: 1e5dd1d09b22
 Create Date: 2014-03-21 16:30:10.626649
 
 """
@@ -25,28 +25,22 @@ Create Date: 2014-03-21 16:30:10.626649
 revision = 'b65aa907aec'
 down_revision = '1e5dd1d09b22'
 
-# Change to ['*'] if this migration applies to all plugins
+# This migration will be executed only if then Neutron db contains tables for
+# the firewall service plugin
+# This migration will not be executed in offline mode
 
-migration_for_plugins = [
-    'neutron.services.firewall.fwaas_plugin.FirewallPlugin'
-]
-
-from alembic import op
 import sqlalchemy as sa
 
 from neutron.db import migration
 
 
+@migration.skip_if_offline
 def upgrade(active_plugins=None, options=None):
-    if not migration.should_run(active_plugins, migration_for_plugins):
-        return
-
-    op.alter_column('firewall_rules', 'protocol', type_=sa.String(40),
-                    existing_nullable=True)
+    migration.alter_column_if_exists(
+        'firewall_rules', 'protocol',
+        type_=sa.String(40),
+        existing_nullable=True)
 
 
 def downgrade(active_plugins=None, options=None):
-    if not migration.should_run(active_plugins, migration_for_plugins):
-        return
-
     pass
index be99747c57eeb0366541f31bb80aacd87c75f301..b8e8eb4d60edb4fd6cfab8ae6bcf630ae4af9d24 100644 (file)
@@ -25,30 +25,28 @@ Create Date: 2014-03-21 17:22:20.545186
 revision = 'd06e871c0d5'
 down_revision = '4eca4a84f08a'
 
-# Change to ['*'] if this migration applies to all plugins
+# This migration will be executed only if the neutron DB schema
+# contains the tables for the ML2 plugin brocade driver.
+# This migration will be skipped when executed in offline mode.
 
-migration_for_plugins = [
-    'neutron.plugins.ml2.plugin.Ml2Plugin'
-]
 
-from alembic import op
 import sqlalchemy as sa
 
 
 from neutron.db import migration
 
 
+@migration.skip_if_offline
 def upgrade(active_plugins=None, options=None):
-    if not migration.should_run(active_plugins, migration_for_plugins):
-        return
-
-    op.alter_column('ml2_brocadeports', 'admin_state_up', nullable=False,
-                    existing_type=sa.Boolean)
+    migration.alter_column_if_exists(
+        'ml2_brocadeports', 'admin_state_up',
+        nullable=False,
+        existing_type=sa.Boolean)
 
 
+@migration.skip_if_offline
 def downgrade(active_plugins=None, options=None):
-    if not migration.should_run(active_plugins, migration_for_plugins):
-        return
-
-    op.alter_column('ml2_brocadeports', 'admin_state_up', nullable=True,
-                    existing_type=sa.Boolean)
+    migration.alter_column_if_exists(
+        'ml2_brocadeports', 'admin_state_up',
+        nullable=True,
+        existing_type=sa.Boolean)
index 7dd0dfcc4f0a66ec125b3752e68b009d21546431..ca505944cc89eacd1738083c894ca1a7b33fcc38 100644 (file)
@@ -25,6 +25,16 @@ from neutron.tests import base
 
 
 class TestDbMigration(base.BaseTestCase):
+
+    def setUp(self):
+        super(TestDbMigration, self).setUp()
+        mock.patch('alembic.op.get_bind').start()
+        self.mock_alembic_is_offline = mock.patch(
+            'alembic.context.is_offline_mode', return_value=False).start()
+        self.mock_alembic_is_offline.return_value = False
+        self.mock_sa_inspector = mock.patch(
+            'sqlalchemy.engine.reflection.Inspector').start()
+
     def test_should_run_plugin_in_list(self):
         self.assertTrue(migration.should_run(['foo'], ['foo', 'bar']))
         self.assertFalse(migration.should_run(['foo'], ['bar']))
@@ -32,6 +42,39 @@ class TestDbMigration(base.BaseTestCase):
     def test_should_run_plugin_wildcard(self):
         self.assertTrue(migration.should_run(['foo'], ['*']))
 
+    def _prepare_mocked_sqlalchemy_inspector(self):
+        mock_inspector = mock.MagicMock()
+        mock_inspector.get_table_names.return_value = ['foo', 'bar']
+        mock_inspector.get_columns.return_value = [{'name': 'foo_column'},
+                                                   {'name': 'bar_column'}]
+        self.mock_sa_inspector.from_engine.return_value = mock_inspector
+
+    def test_schema_has_table(self):
+        self._prepare_mocked_sqlalchemy_inspector()
+        self.assertTrue(migration.schema_has_table('foo'))
+
+    def test_schema_has_table_raises_if_offline(self):
+        self.mock_alembic_is_offline.return_value = True
+        self.assertRaises(RuntimeError, migration.schema_has_table, 'foo')
+
+    def test_schema_has_column_missing_table(self):
+        self._prepare_mocked_sqlalchemy_inspector()
+        self.assertFalse(migration.schema_has_column('meh', 'meh'))
+
+    def test_schema_has_column(self):
+        self._prepare_mocked_sqlalchemy_inspector()
+        self.assertTrue(migration.schema_has_column('foo', 'foo_column'))
+
+    def test_schema_has_column_raises_if_offline(self):
+        self.mock_alembic_is_offline.return_value = True
+        self.assertRaises(RuntimeError, migration.schema_has_column,
+                          'foo', 'foo_col')
+
+    def test_schema_has_column_missing_column(self):
+        self._prepare_mocked_sqlalchemy_inspector()
+        self.assertFalse(migration.schema_has_column(
+            'foo', column_name='meh'))
+
 
 class TestCli(base.BaseTestCase):
     def setUp(self):