24394524 MySQL Cluster support for Cinder must be ported to Mitaka
authorOctave Orgeron <octave.orgeron@oracle.com>
Thu, 08 Sep 2016 13:16:06 -0600
changeset 6866 4c1935f5ec9a
parent 6865 1cc50ab79b8c
child 6867 87f7fd05f888
24394524 MySQL Cluster support for Cinder must be ported to Mitaka 24394534 MySQL Cluster support for Glance must be ported to Mitaka 24394543 MySQL Cluster support for Heat must be ported to Mitaka 24394552 MySQL Cluster support for Ironic must be ported to Mitaka 24394567 MySQL Cluster support for Keystone must be ported to Mitaka 24394574 MySQL Cluster support for Neutron must be ported to Mitaka 24394587 MySQL Cluster support for Nova must be ported to Mitaka 24409419 MySQL Cluster support for oslo.db must be ported to Mitaka
components/openstack/cinder/files/cinder.conf
components/openstack/cinder/patches/15-mysql_cluster_support.patch
components/openstack/glance/files/glance-api.conf
components/openstack/glance/files/glance-glare.conf
components/openstack/glance/files/glance-manage.conf
components/openstack/glance/files/glance-registry.conf
components/openstack/glance/files/glance-scrubber.conf
components/openstack/glance/patches/15-mysql_cluster_support.patch
components/openstack/heat/files/heat.conf
components/openstack/heat/patches/08-mysql_cluster_support.patch
components/openstack/ironic/files/ironic.conf
components/openstack/ironic/patches/05-mysql_cluster_support.patch
components/openstack/keystone/files/keystone.conf
components/openstack/keystone/patches/mysql_cluster_support.patch
components/openstack/neutron/files/neutron.conf
components/openstack/neutron/patches/11-mysql_cluster_support.patch
components/openstack/nova/files/nova.conf
components/openstack/nova/patches/13-mysql_cluster_support.patch
components/python/oslo.db/patches/mysql_cluster_support.patch
--- a/components/openstack/cinder/files/cinder.conf	Thu Sep 08 12:41:54 2016 -0700
+++ b/components/openstack/cinder/files/cinder.conf	Thu Sep 08 13:16:06 2016 -0600
@@ -2885,6 +2885,12 @@
 # (string value)
 #mysql_sql_mode = TRADITIONAL
 
+# This configures the MySQL storage engine. This allows for OpenStack to
+# support different storage engines such as InnoDB, NDB, etc. By Default,
+# this value will be set to InnoDB. For MySQL Cluster, set to NDBCLUSTER.
+# Example: mysql_storage_engine=(string value)
+#mysql_storage_engine = InnoDB
+
 # Timeout before idle SQL connections are reaped. (integer value)
 # Deprecated group/name - [DEFAULT]/sql_idle_timeout
 # Deprecated group/name - [DATABASE]/sql_idle_timeout
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openstack/cinder/patches/15-mysql_cluster_support.patch	Thu Sep 08 13:16:06 2016 -0600
@@ -0,0 +1,715 @@
+This patchset is for bug:
+
+24394524 Mitaka Cinder should support MySQL Cluster
+
+This fixes the following aspects of Cinder:
+1. Implementation of an oslo.db configuration parameter to specify the MySQL
+   storage engine (mysql_storage_engine).
+2. Replacement of hardcoded SQL statements that set the engine to "InnoDB"
+   to the above configuration value.
+3. Logic to handle SQL differences between MySQL InnoDB and MySQL Cluster (NDB).
+   This includes column lengths, constraints, foreign keys, and indexes.
+
+This has not been committed upstream, but has been filed in launchpad:
+
+https://bugs.launchpad.net/cinder/+bug/1564110
+
+
+--- cinder-8.0.0/cinder/db/sqlalchemy/migrate_repo/versions/001_cinder_init.py.orig	2016-08-25 08:57:00.626583510 -0700
++++ cinder-8.0.0/cinder/db/sqlalchemy/migrate_repo/versions/001_cinder_init.py	2016-08-25 09:53:31.239234646 -0700
[email protected]@ -12,8 +12,17 @@
+ #    License for the specific language governing permissions and limitations
+ #    under the License.
+ 
++from oslo_config import cfg
+ from sqlalchemy import Boolean, Column, DateTime, ForeignKey
++from sqlalchemy import dialects
+ from sqlalchemy import Integer, MetaData, String, Table
++from sqlalchemy import Text
++
++CONF = cfg.CONF
++
++
++def TinyText():
++    return Text().with_variant(dialects.mysql.TINYTEXT(), 'mysql')
+ 
+ 
+ def define_tables(meta):
[email protected]@ -31,7 +40,7 @@ def define_tables(meta):
+         Column('instance_uuid', String(length=255)),
+         Column('old_instance_type_id', Integer),
+         Column('new_instance_type_id', Integer),
+-        mysql_engine='InnoDB'
++        mysql_engine=CONF.database.mysql_storage_engine
+     )
+ 
+     services = Table(
[email protected]@ -47,7 +56,7 @@ def define_tables(meta):
+         Column('report_count', Integer, nullable=False),
+         Column('disabled', Boolean),
+         Column('availability_zone', String(length=255)),
+-        mysql_engine='InnoDB'
++        mysql_engine=CONF.database.mysql_storage_engine
+     )
+ 
+     sm_flavors = Table(
[email protected]@ -59,7 +68,7 @@ def define_tables(meta):
+         Column('id', Integer, primary_key=True, nullable=False),
+         Column('label', String(length=255)),
+         Column('description', String(length=255)),
+-        mysql_engine='InnoDB'
++        mysql_engine=CONF.database.mysql_storage_engine
+     )
+ 
+     sm_backend_config = Table(
[email protected]@ -74,7 +83,7 @@ def define_tables(meta):
+         Column('sr_uuid', String(length=255)),
+         Column('sr_type', String(length=255)),
+         Column('config_params', String(length=2047)),
+-        mysql_engine='InnoDB'
++        mysql_engine=CONF.database.mysql_storage_engine
+     )
+ 
+     sm_volume = Table(
[email protected]@ -90,7 +99,7 @@ def define_tables(meta):
+         Column('backend_id', Integer, ForeignKey('sm_backend_config.id'),
+                nullable=False),
+         Column('vdi_uuid', String(length=255)),
+-        mysql_engine='InnoDB'
++        mysql_engine=CONF.database.mysql_storage_engine
+     )
+ 
+     snapshots = Table(
[email protected]@ -109,7 +118,7 @@ def define_tables(meta):
+         Column('scheduled_at', DateTime),
+         Column('display_name', String(length=255)),
+         Column('display_description', String(length=255)),
+-        mysql_engine='InnoDB'
++        mysql_engine=CONF.database.mysql_storage_engine
+     )
+ 
+     volume_types = Table(
[email protected]@ -120,7 +129,7 @@ def define_tables(meta):
+         Column('deleted', Boolean),
+         Column('id', Integer, primary_key=True, nullable=False),
+         Column('name', String(length=255)),
+-        mysql_engine='InnoDB'
++        mysql_engine=CONF.database.mysql_storage_engine
+     )
+ 
+     volume_metadata = Table(
[email protected]@ -134,7 +143,7 @@ def define_tables(meta):
+                nullable=False),
+         Column('key', String(length=255)),
+         Column('value', String(length=255)),
+-        mysql_engine='InnoDB'
++        mysql_engine=CONF.database.mysql_storage_engine
+     )
+ 
+     volume_type_extra_specs = Table(
[email protected]@ -148,38 +157,69 @@ def define_tables(meta):
+                nullable=False),
+         Column('key', String(length=255)),
+         Column('value', String(length=255)),
+-        mysql_engine='InnoDB'
++        mysql_engine=CONF.database.mysql_storage_engine
+     )
+ 
+-    volumes = Table(
+-        'volumes', meta,
+-        Column('created_at', DateTime),
+-        Column('updated_at', DateTime),
+-        Column('deleted_at', DateTime),
+-        Column('deleted', Boolean),
+-        Column('id', String(length=36), primary_key=True, nullable=False),
+-        Column('ec2_id', String(length=255)),
+-        Column('user_id', String(length=255)),
+-        Column('project_id', String(length=255)),
+-        Column('host', String(length=255)),
+-        Column('size', Integer),
+-        Column('availability_zone', String(length=255)),
+-        Column('instance_uuid', String(length=36)),
+-        Column('mountpoint', String(length=255)),
+-        Column('attach_time', String(length=255)),
+-        Column('status', String(length=255)),
+-        Column('attach_status', String(length=255)),
+-        Column('scheduled_at', DateTime),
+-        Column('launched_at', DateTime),
+-        Column('terminated_at', DateTime),
+-        Column('display_name', String(length=255)),
+-        Column('display_description', String(length=255)),
+-        Column('provider_location', String(length=256)),
+-        Column('provider_auth', String(length=256)),
+-        Column('snapshot_id', String(length=36)),
+-        Column('volume_type_id', Integer),
+-        mysql_engine='InnoDB'
+-    )
++    if CONF.database.mysql_storage_engine == 'NDBCLUSTER':
++        volumes = Table(
++            'volumes', meta,
++            Column('created_at', DateTime),
++            Column('updated_at', DateTime),
++            Column('deleted_at', DateTime),
++            Column('deleted', Boolean),
++            Column('id', String(length=36), primary_key=True, nullable=False),
++            Column('ec2_id', String(length=255)),
++            Column('user_id', String(length=255)),
++            Column('project_id', String(length=255)),
++            Column('host', String(length=255)),
++            Column('size', Integer),
++            Column('availability_zone', TinyText()),
++            Column('instance_uuid', String(length=36)),
++            Column('mountpoint', TinyText()),
++            Column('attach_time', TinyText()),
++            Column('status', String(length=64)),
++            Column('attach_status', String(length=64)),
++            Column('scheduled_at', DateTime),
++            Column('launched_at', DateTime),
++            Column('terminated_at', DateTime),
++            Column('display_name', String(length=255)),
++            Column('display_description', TinyText()),
++            Column('provider_location', String(length=256)),
++            Column('provider_auth', String(length=256)),
++            Column('snapshot_id', String(length=36)),
++            Column('volume_type_id', Integer),
++            mysql_engine=CONF.database.mysql_storage_engine
++        )
++    else:
++        volumes = Table(
++            'volumes', meta,
++            Column('created_at', DateTime),
++            Column('updated_at', DateTime),
++            Column('deleted_at', DateTime),
++            Column('deleted', Boolean),
++            Column('id', String(length=36), primary_key=True, nullable=False),
++            Column('ec2_id', String(length=255)),
++            Column('user_id', String(length=255)),
++            Column('project_id', String(length=255)),
++            Column('host', String(length=255)),
++            Column('size', Integer),
++            Column('availability_zone', String(length=255)),
++            Column('instance_uuid', String(length=36)),
++            Column('mountpoint', String(length=255)),
++            Column('attach_time', String(length=255)),
++            Column('status', String(length=255)),
++            Column('attach_status', String(length=255)),
++            Column('scheduled_at', DateTime),
++            Column('launched_at', DateTime),
++            Column('terminated_at', DateTime),
++            Column('display_name', String(length=255)),
++            Column('display_description', String(length=255)),
++            Column('provider_location', String(length=256)),
++            Column('provider_auth', String(length=256)),
++            Column('snapshot_id', String(length=36)),
++            Column('volume_type_id', Integer),
++            mysql_engine=CONF.database.mysql_storage_engine
++        )
+ 
+     quotas = Table(
+         'quotas', meta,
[email protected]@ -191,7 +231,7 @@ def define_tables(meta):
+         Column('project_id', String(length=255)),
+         Column('resource', String(length=255), nullable=False),
+         Column('hard_limit', Integer),
+-        mysql_engine='InnoDB'
++        mysql_engine=CONF.database.mysql_storage_engine
+     )
+ 
+     iscsi_targets = Table(
[email protected]@ -205,7 +245,7 @@ def define_tables(meta):
+         Column('host', String(length=255)),
+         Column('volume_id', String(length=36), ForeignKey('volumes.id'),
+                nullable=True),
+-        mysql_engine='InnoDB'
++        mysql_engine=CONF.database.mysql_storage_engine
+     )
+     return [sm_flavors,
+             sm_backend_config,
[email protected]@ -255,4 +295,5 @@ def upgrade(migrate_engine):
+         migrate_engine.execute(
+             "ALTER DATABASE %s DEFAULT CHARACTER SET utf8" %
+             migrate_engine.url.database)
+-        migrate_engine.execute("ALTER TABLE %s Engine=InnoDB" % table)
++        migrate_engine.execute("ALTER TABLE %(db_table)s Engine=%(mysql_storage_engine)s" %
++                               dict(db_table=table, mysql_storage_engine=CONF.database.mysql_storage_engine))
+--- cinder-8.0.0/cinder/db/sqlalchemy/migrate_repo/versions/002_quota_class.py.orig	2016-08-25 08:57:08.652633578 -0700
++++ cinder-8.0.0/cinder/db/sqlalchemy/migrate_repo/versions/002_quota_class.py	2016-08-25 08:56:32.039341151 -0700
[email protected]@ -12,9 +12,12 @@
+ #    License for the specific language governing permissions and limitations
+ #    under the License.
+ 
++from oslo_config import cfg
+ from sqlalchemy import Boolean, Column, DateTime
+ from sqlalchemy import MetaData, Integer, String, Table, ForeignKey
+ 
++CONF = cfg.CONF
++
+ 
+ def upgrade(migrate_engine):
+     meta = MetaData()
[email protected]@ -34,7 +37,7 @@ def upgrade(migrate_engine):
+                           Column('resource',
+                                  String(length=255)),
+                           Column('hard_limit', Integer(), nullable=True),
+-                          mysql_engine='InnoDB',
++                          mysql_engine=CONF.database.mysql_storage_engine,
+                           mysql_charset='utf8',
+                           )
+ 
[email protected]@ -55,7 +58,7 @@ def upgrade(migrate_engine):
+                          Column('in_use', Integer(), nullable=False),
+                          Column('reserved', Integer(), nullable=False),
+                          Column('until_refresh', Integer(), nullable=True),
+-                         mysql_engine='InnoDB',
++                         mysql_engine=CONF.database.mysql_storage_engine,
+                          mysql_charset='utf8',
+                          )
+ 
[email protected]@ -82,7 +85,7 @@ def upgrade(migrate_engine):
+                                 String(length=255)),
+                          Column('delta', Integer(), nullable=False),
+                          Column('expire', DateTime(timezone=False)),
+-                         mysql_engine='InnoDB',
++                         mysql_engine=CONF.database.mysql_storage_engine,
+                          mysql_charset='utf8',
+                          )
+ 
+--- cinder-8.0.0/cinder/db/sqlalchemy/migrate_repo/versions/003_glance_metadata.py.orig	2016-08-25 08:57:16.312610448 -0700
++++ cinder-8.0.0/cinder/db/sqlalchemy/migrate_repo/versions/003_glance_metadata.py	2016-08-25 08:56:32.040239306 -0700
[email protected]@ -12,9 +12,12 @@
+ #    License for the specific language governing permissions and limitations
+ #    under the License.
+ 
++from oslo_config import cfg
+ from sqlalchemy import Column, DateTime, Text, Boolean
+ from sqlalchemy import MetaData, Integer, String, Table, ForeignKey
+ 
++CONF = cfg.CONF
++
+ 
+ def upgrade(migrate_engine):
+     meta = MetaData()
[email protected]@ -26,11 +29,11 @@ def upgrade(migrate_engine):
+     Table('volumes',
+           meta,
+           Column('id', Integer(), primary_key=True, nullable=False),
+-          mysql_engine='InnoDB')
++          mysql_engine=CONF.database.mysql_storage_engine)
+     Table('snapshots',
+           meta,
+           Column('id', Integer(), primary_key=True, nullable=False),
+-          mysql_engine='InnoDB')
++          mysql_engine=CONF.database.mysql_storage_engine)
+     # Create new table
+     volume_glance_metadata = Table(
+         'volume_glance_metadata',
[email protected]@ -45,7 +48,7 @@ def upgrade(migrate_engine):
+                ForeignKey('snapshots.id')),
+         Column('key', String(255)),
+         Column('value', Text),
+-        mysql_engine='InnoDB'
++        mysql_engine=CONF.database.mysql_storage_engine
+     )
+ 
+     try:
+--- cinder-8.0.0/cinder/db/sqlalchemy/migrate_repo/versions/008_add_backup.py.orig	2016-08-25 08:57:23.692681043 -0700
++++ cinder-8.0.0/cinder/db/sqlalchemy/migrate_repo/versions/008_add_backup.py	2016-08-25 08:56:32.041089963 -0700
[email protected]@ -13,9 +13,12 @@
+ #    License for the specific language governing permissions and limitations
+ #    under the License.
+ 
++from oslo_config import cfg
+ from sqlalchemy import Boolean, Column, DateTime
+ from sqlalchemy import MetaData, Integer, String, Table
+ 
++CONF = cfg.CONF
++
+ 
+ def upgrade(migrate_engine):
+     meta = MetaData()
[email protected]@ -43,7 +46,7 @@ def upgrade(migrate_engine):
+         Column('service', String(length=255)),
+         Column('size', Integer()),
+         Column('object_count', Integer()),
+-        mysql_engine='InnoDB'
++        mysql_engine=CONF.database.mysql_storage_engine
+     )
+ 
+     backups.create()
+--- cinder-8.0.0/cinder/db/sqlalchemy/migrate_repo/versions/009_add_snapshot_metadata_table.py.orig	2016-08-25 08:57:30.092571835 -0700
++++ cinder-8.0.0/cinder/db/sqlalchemy/migrate_repo/versions/009_add_snapshot_metadata_table.py	2016-08-25 08:56:32.041930428 -0700
[email protected]@ -10,9 +10,12 @@
+ #    License for the specific language governing permissions and limitations
+ #    under the License.
+ 
++from oslo_config import cfg
+ from sqlalchemy import Boolean, Column, DateTime
+ from sqlalchemy import Integer, MetaData, String, Table, ForeignKey
+ 
++CONF = cfg.CONF
++
+ 
+ def upgrade(migrate_engine):
+     meta = MetaData()
[email protected]@ -32,7 +35,7 @@ def upgrade(migrate_engine):
+                nullable=False),
+         Column('key', String(length=255)),
+         Column('value', String(length=255)),
+-        mysql_engine='InnoDB'
++        mysql_engine=CONF.database.mysql_storage_engine
+     )
+ 
+     snapshot_metadata.create()
+--- cinder-8.0.0/cinder/db/sqlalchemy/migrate_repo/versions/010_add_transfers_table.py.orig	2016-08-25 08:57:36.677754626 -0700
++++ cinder-8.0.0/cinder/db/sqlalchemy/migrate_repo/versions/010_add_transfers_table.py	2016-08-25 08:56:32.042766768 -0700
[email protected]@ -10,9 +10,12 @@
+ #    License for the specific language governing permissions and limitations
+ #    under the License.
+ 
++from oslo_config import cfg
+ from sqlalchemy import Boolean, Column, DateTime
+ from sqlalchemy import MetaData, String, Table, ForeignKey
+ 
++CONF = cfg.CONF
++
+ 
+ def upgrade(migrate_engine):
+     meta = MetaData()
[email protected]@ -34,7 +37,7 @@ def upgrade(migrate_engine):
+         Column('salt', String(length=255)),
+         Column('crypt_hash', String(length=255)),
+         Column('expires_at', DateTime(timezone=False)),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
+--- cinder-8.0.0/cinder/db/sqlalchemy/migrate_repo/versions/017_add_encryption_information.py.orig	2016-08-25 08:57:43.340435246 -0700
++++ cinder-8.0.0/cinder/db/sqlalchemy/migrate_repo/versions/017_add_encryption_information.py	2016-08-25 08:56:32.043630671 -0700
[email protected]@ -13,9 +13,12 @@
+ #    License for the specific language governing permissions and limitations
+ #    under the License.
+ 
++from oslo_config import cfg
+ from sqlalchemy import Column, ForeignKey, MetaData, Table
+ from sqlalchemy import Boolean, DateTime, Integer, String
+ 
++CONF = cfg.CONF
++
+ 
+ def upgrade(migrate_engine):
+     meta = MetaData(bind=migrate_engine)
[email protected]@ -51,9 +54,9 @@ def upgrade(migrate_engine):
+         # scheme unless each volume type is associated with at most one
+         # encryption scheme.
+         Column('volume_type_id', String(length=36),
+-               ForeignKey(volume_types.c.id),
++               ForeignKey(volume_types.c.id, name='encryption_ibfk_1'),
+                primary_key=True, nullable=False),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
+--- cinder-8.0.0/cinder/db/sqlalchemy/migrate_repo/versions/018_add_qos_specs.py.orig	2016-08-25 08:57:51.329501203 -0700
++++ cinder-8.0.0/cinder/db/sqlalchemy/migrate_repo/versions/018_add_qos_specs.py	2016-08-25 08:56:32.044466643 -0700
[email protected]@ -14,9 +14,12 @@
+ #    License for the specific language governing permissions and limitations
+ #    under the License.
+ 
++from oslo_config import cfg
+ from sqlalchemy import Boolean, Column, DateTime
+ from sqlalchemy import ForeignKey, MetaData, String, Table
+ 
++CONF = cfg.CONF
++
+ 
+ def upgrade(migrate_engine):
+     """Add volume_type_rate_limit table."""
[email protected]@ -34,7 +37,7 @@ def upgrade(migrate_engine):
+                ForeignKey('quality_of_service_specs.id')),
+         Column('key', String(255)),
+         Column('value', String(255)),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
+--- cinder-8.0.0/cinder/db/sqlalchemy/migrate_repo/versions/020_add_volume_admin_metadata_table.py.orig	2016-08-25 08:58:01.270351421 -0700
++++ cinder-8.0.0/cinder/db/sqlalchemy/migrate_repo/versions/020_add_volume_admin_metadata_table.py	2016-08-25 08:56:32.045303898 -0700
[email protected]@ -10,9 +10,12 @@
+ #    License for the specific language governing permissions and limitations
+ #    under the License.
+ 
++from oslo_config import cfg
+ from sqlalchemy import Boolean, Column, DateTime
+ from sqlalchemy import Integer, MetaData, String, Table, ForeignKey
+ 
++CONF = cfg.CONF
++
+ 
+ def upgrade(migrate_engine):
+     meta = MetaData()
[email protected]@ -32,7 +35,7 @@ def upgrade(migrate_engine):
+                nullable=False),
+         Column('key', String(length=255)),
+         Column('value', String(length=255)),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
+--- cinder-8.0.0/cinder/db/sqlalchemy/migrate_repo/versions/025_add_consistencygroup.py.orig	2016-08-25 08:58:07.982189985 -0700
++++ cinder-8.0.0/cinder/db/sqlalchemy/migrate_repo/versions/025_add_consistencygroup.py	2016-08-25 08:56:32.046312221 -0700
[email protected]@ -13,9 +13,12 @@
+ #    License for the specific language governing permissions and limitations
+ #    under the License.
+ 
++from oslo_config import cfg
+ from sqlalchemy import Boolean, Column, DateTime
+ from sqlalchemy import ForeignKey, MetaData, String, Table
+ 
++CONF = cfg.CONF
++
+ 
+ def upgrade(migrate_engine):
+     meta = MetaData()
[email protected]@ -37,7 +40,7 @@ def upgrade(migrate_engine):
+         Column('description', String(length=255)),
+         Column('volume_type_id', String(length=255)),
+         Column('status', String(length=255)),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8',
+     )
+ 
[email protected]@ -59,7 +62,7 @@ def upgrade(migrate_engine):
+         Column('name', String(length=255)),
+         Column('description', String(length=255)),
+         Column('status', String(length=255)),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8',
+     )
+ 
+--- cinder-8.0.0/cinder/db/sqlalchemy/migrate_repo/versions/032_add_volume_type_projects.py.orig	2016-08-25 08:58:14.732428526 -0700
++++ cinder-8.0.0/cinder/db/sqlalchemy/migrate_repo/versions/032_add_volume_type_projects.py	2016-08-25 08:56:32.047162211 -0700
[email protected]@ -10,9 +10,12 @@
+ #    License for the specific language governing permissions and limitations
+ #    under the License.
+ 
++from oslo_config import cfg
+ from sqlalchemy import Boolean, Column, DateTime, UniqueConstraint
+ from sqlalchemy import Integer, MetaData, String, Table, ForeignKey
+ 
++CONF = cfg.CONF
++
+ 
+ def upgrade(migrate_engine):
+     meta = MetaData()
[email protected]@ -35,7 +38,7 @@ def upgrade(migrate_engine):
+         Column('project_id', String(length=255)),
+         Column('deleted', Boolean(create_constraint=True, name=None)),
+         UniqueConstraint('volume_type_id', 'project_id', 'deleted'),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+     )
+ 
+     volume_type_projects.create()
+--- cinder-8.0.0/cinder/db/sqlalchemy/migrate_repo/versions/038_add_driver_initiator_data_table.py.orig	2016-08-25 08:58:22.055008365 -0700
++++ cinder-8.0.0/cinder/db/sqlalchemy/migrate_repo/versions/038_add_driver_initiator_data_table.py	2016-08-25 08:56:32.048168233 -0700
[email protected]@ -10,9 +10,12 @@
+ #    License for the specific language governing permissions and limitations
+ #    under the License.
+ 
++from oslo_config import cfg
+ from sqlalchemy import Column, DateTime, Integer
+ from sqlalchemy import MetaData, String, Table, UniqueConstraint
+ 
++CONF = cfg.CONF
++
+ 
+ def upgrade(migrate_engine):
+     meta = MetaData()
[email protected]@ -29,7 +32,7 @@ def upgrade(migrate_engine):
+         Column('key', String(length=255), nullable=False),
+         Column('value', String(length=255)),
+         UniqueConstraint('initiator', 'namespace', 'key'),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
+--- cinder-8.0.0/cinder/db/sqlalchemy/migrate_repo/versions/040_add_volume_attachment.py.orig	2016-08-25 08:58:30.054762283 -0700
++++ cinder-8.0.0/cinder/db/sqlalchemy/migrate_repo/versions/040_add_volume_attachment.py	2016-08-25 08:56:32.049123043 -0700
[email protected]@ -16,11 +16,13 @@
+ import datetime
+ import uuid
+ 
++from oslo_config import cfg
+ import six
+ from sqlalchemy import Boolean, Column, DateTime
+ from sqlalchemy import ForeignKey, MetaData, String, Table
+ 
+ CREATED_AT = datetime.datetime.now()  # noqa
++CONF = cfg.CONF
+ 
+ 
+ def upgrade(migrate_engine):
[email protected]@ -51,7 +53,7 @@ def upgrade(migrate_engine):
+         Column('detach_time', DateTime),
+         Column('attach_mode', String(length=36)),
+         Column('attach_status', String(length=255)),
+-        mysql_engine='InnoDB'
++        mysql_engine=CONF.database.mysql_storage_engine
+     )
+ 
+     volume_attachment.create()
+--- cinder-8.0.0/cinder/db/sqlalchemy/migrate_repo/versions/055_add_image_volume_cache_table.py.orig	2016-08-25 08:58:37.835313620 -0700
++++ cinder-8.0.0/cinder/db/sqlalchemy/migrate_repo/versions/055_add_image_volume_cache_table.py	2016-08-25 08:56:32.049959998 -0700
[email protected]@ -12,9 +12,12 @@
+ #    License for the specific language governing permissions and limitations
+ #    under the License.
+ 
++from oslo_config import cfg
+ from sqlalchemy import Column, DateTime, Integer
+ from sqlalchemy import MetaData, String, Table
+ 
++CONF = cfg.CONF
++
+ 
+ def upgrade(migrate_engine):
+     meta = MetaData()
[email protected]@ -30,7 +33,7 @@ def upgrade(migrate_engine):
+         Column('volume_id', String(length=36), nullable=False),
+         Column('size', Integer, nullable=False),
+         Column('last_used', DateTime, nullable=False),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
+--- cinder-8.0.0/cinder/db/sqlalchemy/migrate_repo/versions/067_readd_iscsi_targets_table.py.orig	2016-08-25 08:58:44.335850305 -0700
++++ cinder-8.0.0/cinder/db/sqlalchemy/migrate_repo/versions/067_readd_iscsi_targets_table.py	2016-08-25 08:56:32.050821951 -0700
[email protected]@ -10,9 +10,12 @@
+ #    License for the specific language governing permissions and limitations
+ #    under the License.
+ 
++from oslo_config import cfg
+ from sqlalchemy import Boolean, Column, DateTime, ForeignKey
+ from sqlalchemy import Integer, MetaData, String, Table
+ 
++CONF = cfg.CONF
++
+ 
+ def upgrade(migrate_engine):
+     meta = MetaData()
[email protected]@ -30,7 +33,7 @@ def upgrade(migrate_engine):
+         Column('host', String(length=255)),
+         Column('volume_id', String(length=36), ForeignKey('volumes.id'),
+                nullable=True),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
+--- cinder-8.0.0/cinder/db/sqlalchemy/models.py.orig	2016-08-25 08:58:53.030663246 -0700
++++ cinder-8.0.0/cinder/db/sqlalchemy/models.py	2016-08-25 10:02:19.406765223 -0700
[email protected]@ -24,6 +24,7 @@ from oslo_config import cfg
+ from oslo_db.sqlalchemy import models
+ from oslo_utils import timeutils
+ from sqlalchemy import Column, Integer, String, Text, schema
++from sqlalchemy import dialects
+ from sqlalchemy.ext.declarative import declarative_base
+ from sqlalchemy import ForeignKey, DateTime, Boolean
+ from sqlalchemy.orm import relationship, backref, validates
[email protected]@ -33,11 +34,15 @@ CONF = cfg.CONF
+ BASE = declarative_base()
+ 
+ 
++def TinyText():
++            return Text().with_variant(dialects.mysql.TINYTEXT(), 'mysql')
++
++
+ class CinderBase(models.TimestampMixin,
+                  models.ModelBase):
+     """Base class for Cinder Models."""
+ 
+-    __table_args__ = {'mysql_engine': 'InnoDB'}
++    __table_args__ = {'mysql_engine': CONF.database.mysql_storage_engine}
+ 
+     # TODO(rpodolyaka): reuse models.SoftDeleteMixin in the next stage
+     #                   of implementing of BP db-cleanup
[email protected]@ -150,9 +155,16 @@ class Volume(BASE, CinderBase):
+ 
+     host = Column(String(255))  # , ForeignKey('hosts.id'))
+     size = Column(Integer)
+-    availability_zone = Column(String(255))  # TODO(vish): foreign key?
+-    status = Column(String(255))  # TODO(vish): enum?
+-    attach_status = Column(String(255))  # TODO(vish): enum
++
++    if CONF.database.mysql_storage_engine == 'NDBCLUSTER':
++        availability_zone = Column(TinyText())
++        status = Column(String(64))
++        attach_status = Column(String(64))
++    else:
++        availability_zone = Column(String(255))  # TODO(vish): foreign key?
++        status = Column(String(255))  # TODO(vish): enum?
++        attach_status = Column(String(255))  # TODO(vish): enum
++
+     migration_status = Column(String(255))
+ 
+     scheduled_at = Column(DateTime)
[email protected]@ -160,7 +172,11 @@ class Volume(BASE, CinderBase):
+     terminated_at = Column(DateTime)
+ 
+     display_name = Column(String(255))
+-    display_description = Column(String(255))
++
++    if CONF.database.mysql_storage_engine == 'NDBCLUSTER':
++        display_description = Column(TinyText())
++    else:
++        display_description = Column(String(255))
+ 
+     provider_location = Column(String(255))
+     provider_auth = Column(String(255))
[email protected]@ -592,7 +608,7 @@ class DriverInitiatorData(BASE, models.T
+     __tablename__ = 'driver_initiator_data'
+     __table_args__ = (
+         schema.UniqueConstraint("initiator", "namespace", "key"),
+-        {'mysql_engine': 'InnoDB'}
++        {'mysql_engine': CONF.database.mysql_storage_engine}
+     )
+     id = Column(Integer, primary_key=True, nullable=False)
+     initiator = Column(String(255), index=True, nullable=False)
+--- cinder-8.0.0/cinder/tests/unit/test_migrations.py.orig	2016-08-25 08:59:01.630728551 -0700
++++ cinder-8.0.0/cinder/tests/unit/test_migrations.py	2016-08-25 08:56:32.054462196 -0700
[email protected]@ -25,6 +25,7 @@ import uuid
+ import fixtures
+ from migrate.versioning import api as migration_api
+ from migrate.versioning import repository
++from oslo_config import cfg
+ from oslo_db.sqlalchemy import test_base
+ from oslo_db.sqlalchemy import test_migrations
+ from oslo_db.sqlalchemy import utils as db_utils
[email protected]@ -33,6 +34,8 @@ import sqlalchemy
+ from cinder.db import migration
+ import cinder.db.sqlalchemy.migrate_repo
+ 
++CONF = cfg.CONF
++
+ 
+ class MigrationsMixin(test_migrations.WalkVersionsMixin):
+     """Test sqlalchemy-migrate migrations."""
[email protected]@ -842,8 +845,9 @@ class TestMysqlMigrations(test_base.MySQ
+             "SELECT count(*) "
+             "from information_schema.TABLES "
+             "where TABLE_SCHEMA='openstack_citest' "
+-            "and ENGINE!='InnoDB' "
+-            "and TABLE_NAME!='migrate_version'")
++            "and ENGINE!='%(mysql_storage_engine)s' "
++            "and TABLE_NAME!='migrate_version'" %
++            dict(mysql_storage_engine=CONF.database.mysql_storage_engine))
+         count = noninnodb.scalar()
+         self.assertEqual(count, 0, "%d non InnoDB tables created" % count)
+ 
--- a/components/openstack/glance/files/glance-api.conf	Thu Sep 08 12:41:54 2016 -0700
+++ b/components/openstack/glance/files/glance-api.conf	Thu Sep 08 13:16:06 2016 -0600
@@ -650,6 +650,12 @@
 # Example: mysql_sql_mode= (string value)
 #mysql_sql_mode = TRADITIONAL
 
+# This configures the MySQL storage engine. This allows for OpenStack to
+# support different storage engines such as InnoDB, NDB, etc. By Default,
+# this value will be set to InnoDB. For MySQL Cluster, set to NDBCLUSTER.
+# Example: mysql_storage_engine=(string value)
+#mysql_storage_engine = InnoDB
+
 # Timeout before idle SQL connections are reaped. (integer value)
 # Deprecated group/name - [DEFAULT]/sql_idle_timeout
 # Deprecated group/name - [DATABASE]/sql_idle_timeout
--- a/components/openstack/glance/files/glance-glare.conf	Thu Sep 08 12:41:54 2016 -0700
+++ b/components/openstack/glance/files/glance-glare.conf	Thu Sep 08 13:16:06 2016 -0600
@@ -384,6 +384,12 @@
 # Example: mysql_sql_mode= (string value)
 #mysql_sql_mode = TRADITIONAL
 
+# This configures the MySQL storage engine. This allows for OpenStack to
+# support different storage engines such as InnoDB, NDB, etc. By Default,
+# this value will be set to InnoDB. For MySQL Cluster, set to NDBCLUSTER.
+# Example: mysql_storage_engine=(string value)
+#mysql_storage_engine = InnoDB
+
 # Timeout before idle SQL connections are reaped. (integer value)
 # Deprecated group/name - [DEFAULT]/sql_idle_timeout
 # Deprecated group/name - [DATABASE]/sql_idle_timeout
--- a/components/openstack/glance/files/glance-manage.conf	Thu Sep 08 12:41:54 2016 -0700
+++ b/components/openstack/glance/files/glance-manage.conf	Thu Sep 08 13:16:06 2016 -0600
@@ -140,6 +140,12 @@
 # Example: mysql_sql_mode= (string value)
 #mysql_sql_mode = TRADITIONAL
 
+# This configures the MySQL storage engine. This allows for OpenStack to
+# support different storage engines such as InnoDB, NDB, etc. By Default,
+# this value will be set to InnoDB. For MySQL Cluster, set to NDBCLUSTER.
+# Example: mysql_storage_engine=(string value)
+#mysql_storage_engine = InnoDB
+
 # Timeout before idle SQL connections are reaped. (integer value)
 # Deprecated group/name - [DEFAULT]/sql_idle_timeout
 # Deprecated group/name - [DATABASE]/sql_idle_timeout
--- a/components/openstack/glance/files/glance-registry.conf	Thu Sep 08 12:41:54 2016 -0700
+++ b/components/openstack/glance/files/glance-registry.conf	Thu Sep 08 13:16:06 2016 -0600
@@ -451,6 +451,12 @@
 # Example: mysql_sql_mode= (string value)
 #mysql_sql_mode = TRADITIONAL
 
+# This configures the MySQL storage engine. This allows for OpenStack to
+# support different storage engines such as InnoDB, NDB, etc. By Default,
+# this value will be set to InnoDB. For MySQL Cluster, set to NDBCLUSTER.
+# Example: mysql_storage_engine=(string value)
+#mysql_storage_engine = InnoDB
+
 # Timeout before idle SQL connections are reaped. (integer value)
 # Deprecated group/name - [DEFAULT]/sql_idle_timeout
 # Deprecated group/name - [DATABASE]/sql_idle_timeout
--- a/components/openstack/glance/files/glance-scrubber.conf	Thu Sep 08 12:41:54 2016 -0700
+++ b/components/openstack/glance/files/glance-scrubber.conf	Thu Sep 08 13:16:06 2016 -0600
@@ -384,6 +384,12 @@
 # Example: mysql_sql_mode= (string value)
 #mysql_sql_mode = TRADITIONAL
 
+# This configures the MySQL storage engine. This allows for OpenStack to
+# support different storage engines such as InnoDB, NDB, etc. By Default,
+# this value will be set to InnoDB. For MySQL Cluster, set to NDBCLUSTER.
+# Example: mysql_storage_engine=(string value)
+#mysql_storage_engine = InnoDB
+
 # Timeout before idle SQL connections are reaped. (integer value)
 # Deprecated group/name - [DEFAULT]/sql_idle_timeout
 # Deprecated group/name - [DATABASE]/sql_idle_timeout
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openstack/glance/patches/15-mysql_cluster_support.patch	Thu Sep 08 13:16:06 2016 -0600
@@ -0,0 +1,1033 @@
+This patchset is for bug:
+
+24394534 Mitaka Glance should support MySQL Cluster
+
+This fixes the following aspects of Glance:
+1. Implementation of an oslo.db configuration parameter to specify the MySQL
+   storage engine (mysql_storage_engine).
+2. Replacement of hardcoded SQL statements that set the engine to "InnoDB"
+   to the above configuration value.
+3. Logic to handle SQL differences between MySQL InnoDB and MySQL Cluster (NDB).
+   This includes column lengths, constraints, foreign keys, and indexes.
+
+This has not been committed upstream, but has been filed in launchpad:
+
+https://bugs.launchpad.net/glance/+bug/1564110
+
+
+--- glance-12.0.0/glance/db/sqlalchemy/migrate_repo/versions/001_add_images_table.py.orig	2016-08-10 09:36:22.622664972 -0700
++++ glance-12.0.0/glance/db/sqlalchemy/migrate_repo/versions/001_add_images_table.py	2016-08-10 09:34:27.421171908 -0700
[email protected]@ -13,11 +13,14 @@
+ #    License for the specific language governing permissions and limitations
+ #    under the License.
+ 
++from oslo_config import cfg
+ from sqlalchemy.schema import (Column, MetaData, Table)
+ 
+ from glance.db.sqlalchemy.migrate_repo.schema import (
+     Boolean, DateTime, Integer, String, Text, create_tables, drop_tables)  # noqa
+ 
++CONF = cfg.CONF
++
+ 
+ def define_images_table(meta):
+     images = Table('images',
[email protected]@ -41,7 +44,7 @@ def define_images_table(meta):
+                           nullable=False,
+                           default=False,
+                           index=True),
+-                   mysql_engine='InnoDB',
++                   mysql_engine=CONF.database.mysql_storage_engine,
+                    mysql_charset='utf8',
+                    extend_existing=True)
+ 
+--- glance-12.0.0/glance/db/sqlalchemy/migrate_repo/versions/002_add_image_properties_table.py.orig	2016-08-10 09:36:22.630165670 -0700
++++ glance-12.0.0/glance/db/sqlalchemy/migrate_repo/versions/002_add_image_properties_table.py	2016-08-10 09:34:27.422389893 -0700
[email protected]@ -13,6 +13,7 @@
+ #    License for the specific language governing permissions and limitations
+ #    under the License.
+ 
++from oslo_config import cfg
+ from sqlalchemy.schema import (
+     Column, ForeignKey, Index, MetaData, Table, UniqueConstraint)
+ 
[email protected]@ -20,6 +21,8 @@ from glance.db.sqlalchemy.migrate_repo.s
+     Boolean, DateTime, Integer, String, Text, create_tables, drop_tables,
+     from_migration_import)  # noqa
+ 
++CONF = cfg.CONF
++
+ 
+ def define_image_properties_table(meta):
+     (define_images_table,) = from_migration_import(
[email protected]@ -44,7 +47,8 @@ def define_image_properties_table(meta):
+                                     nullable=False),
+                              Column('image_id',
+                                     Integer(),
+-                                    ForeignKey('images.id'),
++                                    ForeignKey('images.id',
++                                               name='image_properties_ibfk_1'),
+                                     nullable=False,
+                                     index=True),
+                              Column('key', String(255), nullable=False),
[email protected]@ -59,7 +63,7 @@ def define_image_properties_table(meta):
+                                     index=True),
+                              UniqueConstraint('image_id', 'key',
+                                               **constr_kwargs),
+-                             mysql_engine='InnoDB',
++                             mysql_engine=CONF.database.mysql_storage_engine,
+                              mysql_charset='utf8',
+                              extend_existing=True)
+ 
+--- glance-12.0.0/glance/db/sqlalchemy/migrate_repo/versions/003_add_disk_format.py.orig	2016-08-10 09:36:22.637028552 -0700
++++ glance-12.0.0/glance/db/sqlalchemy/migrate_repo/versions/003_add_disk_format.py	2016-08-10 09:34:27.423147663 -0700
[email protected]@ -14,11 +14,14 @@
+ #    under the License.
+ 
+ from migrate.changeset import *  # noqa
++from oslo_config import cfg
+ from sqlalchemy import *  # noqa
+ 
+ from glance.db.sqlalchemy.migrate_repo.schema import (
+     Boolean, DateTime, Integer, String, Text, from_migration_import)  # noqa
+ 
++CONF = cfg.CONF
++
+ 
+ def get_images_table(meta):
+     """
[email protected]@ -47,7 +50,7 @@ def get_images_table(meta):
+                           nullable=False,
+                           default=False,
+                           index=True),
+-                   mysql_engine='InnoDB',
++                   mysql_engine=CONF.database.mysql_storage_engine,
+                    extend_existing=True)
+ 
+     return images
+--- glance-12.0.0/glance/db/sqlalchemy/migrate_repo/versions/004_add_checksum.py.orig	2016-08-10 09:36:22.643321775 -0700
++++ glance-12.0.0/glance/db/sqlalchemy/migrate_repo/versions/004_add_checksum.py	2016-08-10 09:34:27.423839815 -0700
[email protected]@ -14,11 +14,14 @@
+ #    under the License.
+ 
+ from migrate.changeset import *  # noqa
++from oslo_config import cfg
+ from sqlalchemy import *  # noqa
+ 
+ from glance.db.sqlalchemy.migrate_repo.schema import (
+     Boolean, DateTime, Integer, String, Text, from_migration_import)  # noqa
+ 
++CONF = cfg.CONF
++
+ 
+ def get_images_table(meta):
+     """
[email protected]@ -48,7 +51,7 @@ def get_images_table(meta):
+                           default=False,
+                           index=True),
+                    Column('checksum', String(32)),
+-                   mysql_engine='InnoDB',
++                   mysql_engine=CONF.database.mysql_storage_engine,
+                    extend_existing=True)
+ 
+     return images
+--- glance-12.0.0/glance/db/sqlalchemy/migrate_repo/versions/005_size_big_integer.py.orig	2016-08-10 09:36:22.650608190 -0700
++++ glance-12.0.0/glance/db/sqlalchemy/migrate_repo/versions/005_size_big_integer.py	2016-08-10 09:34:27.424528158 -0700
[email protected]@ -14,12 +14,15 @@
+ #    under the License.
+ 
+ from migrate.changeset import *  # noqa
++from oslo_config import cfg
+ from sqlalchemy import *  # noqa
+ 
+ from glance.db.sqlalchemy.migrate_repo.schema import (
+     Boolean, DateTime, BigInteger, Integer, String,
+     Text, from_migration_import)  # noqa
+ 
++CONF = cfg.CONF
++
+ 
+ def get_images_table(meta):
+     """
[email protected]@ -48,7 +51,7 @@ def get_images_table(meta):
+                           nullable=False,
+                           default=False,
+                           index=True),
+-                   mysql_engine='InnoDB',
++                   mysql_engine=CONF.database.mysql_storage_engine,
+                    extend_existing=True)
+ 
+     return images
+--- glance-12.0.0/glance/db/sqlalchemy/migrate_repo/versions/006_key_to_name.py.orig	2016-08-10 09:36:22.657538722 -0700
++++ glance-12.0.0/glance/db/sqlalchemy/migrate_repo/versions/006_key_to_name.py	2016-08-10 09:34:27.425360155 -0700
[email protected]@ -14,11 +14,14 @@
+ #    under the License.
+ 
+ from migrate.changeset import *  # noqa
++from oslo_config import cfg
+ from sqlalchemy import *  # noqa
+ 
+ from glance.db.sqlalchemy.migrate_repo.schema import (
+     Boolean, DateTime, Integer, String, Text, from_migration_import)  # noqa
+ 
++CONF = cfg.CONF
++
+ 
+ def get_images_table(meta):
+     """
[email protected]@ -49,7 +52,8 @@ def get_image_properties_table(meta):
+                                     nullable=False),
+                              Column('image_id',
+                                     Integer(),
+-                                    ForeignKey('images.id'),
++                                    ForeignKey('images.id',
++                                               name='image_properties_ibfk_1'),
+                                     nullable=False,
+                                     index=True),
+                              Column('name', String(255), nullable=False),
[email protected]@ -63,7 +67,7 @@ def get_image_properties_table(meta):
+                                     default=False,
+                                     index=True),
+                              UniqueConstraint('image_id', 'name'),
+-                             mysql_engine='InnoDB',
++                             mysql_engine=CONF.database.mysql_storage_engine,
+                              extend_existing=True)
+ 
+     return image_properties
+--- glance-12.0.0/glance/db/sqlalchemy/migrate_repo/versions/007_add_owner.py.orig	2016-08-10 09:36:22.664818540 -0700
++++ glance-12.0.0/glance/db/sqlalchemy/migrate_repo/versions/007_add_owner.py	2016-08-10 09:34:27.427318330 -0700
[email protected]@ -14,12 +14,15 @@
+ #    under the License.
+ 
+ from migrate.changeset import *  # noqa
++from oslo_config import cfg
+ from sqlalchemy import *  # noqa
+ 
+ from glance.db.sqlalchemy.migrate_repo.schema import (
+     Boolean, DateTime, BigInteger, Integer, String,
+     Text, from_migration_import)  # noqa
+ 
++CONF = cfg.CONF
++
+ 
+ def get_images_table(meta):
+     """
[email protected]@ -50,7 +53,7 @@ def get_images_table(meta):
+                           index=True),
+                    Column('checksum', String(32)),
+                    Column('owner', String(255)),
+-                   mysql_engine='InnoDB',
++                   mysql_engine=CONF.database.mysql_storage_engine,
+                    extend_existing=True)
+ 
+     return images
+--- glance-12.0.0/glance/db/sqlalchemy/migrate_repo/versions/008_add_image_members_table.py.orig	2016-08-10 09:36:22.672154268 -0700
++++ glance-12.0.0/glance/db/sqlalchemy/migrate_repo/versions/008_add_image_members_table.py	2016-08-10 09:34:27.428348883 -0700
[email protected]@ -14,12 +14,15 @@
+ #    under the License.
+ 
+ from migrate.changeset import *  # noqa
++from oslo_config import cfg
+ from sqlalchemy import *  # noqa
+ 
+ from glance.db.sqlalchemy.migrate_repo.schema import (
+     Boolean, DateTime, Integer, String, create_tables,
+     drop_tables, from_migration_import)  # noqa
+ 
++CONF = cfg.CONF
++
+ 
+ def get_images_table(meta):
+     """
[email protected]@ -54,7 +57,8 @@ def get_image_members_table(meta):
+                                  nullable=False),
+                           Column('image_id',
+                                  Integer(),
+-                                 ForeignKey('images.id'),
++                                 ForeignKey('images.id',
++                                            name='image_members_image_id_fkey'),
+                                  nullable=False,
+                                  index=True),
+                           Column('member', String(255), nullable=False),
[email protected]@ -72,7 +76,7 @@ def get_image_members_table(meta):
+                                  index=True),
+                           UniqueConstraint('image_id', 'member'),
+                           mysql_charset='utf8',
+-                          mysql_engine='InnoDB',
++                          mysql_engine=CONF.database.mysql_storage_engine,
+                           extend_existing=True)
+ 
+     # DB2: an index has already been created for the UniqueConstraint option
+--- glance-12.0.0/glance/db/sqlalchemy/migrate_repo/versions/009_add_mindisk_and_minram.py.orig	2016-08-10 09:36:22.678770092 -0700
++++ glance-12.0.0/glance/db/sqlalchemy/migrate_repo/versions/009_add_mindisk_and_minram.py	2016-08-10 09:34:27.454363865 -0700
[email protected]@ -14,11 +14,14 @@
+ #    under the License.
+ 
+ from migrate.changeset import *  # noqa
++from oslo_config import cfg
+ from sqlalchemy import *  # noqa
+ 
+ from glance.db.sqlalchemy.migrate_repo.schema import (
+     Boolean, DateTime, Integer, String, Text, from_migration_import)  # noqa
+ 
++CONF = cfg.CONF
++
+ 
+ def get_images_table(meta):
+     """
[email protected]@ -51,7 +54,7 @@ def get_images_table(meta):
+                    Column('owner', String(255)),
+                    Column('min_disk', Integer(), default=0),
+                    Column('min_ram', Integer(), default=0),
+-                   mysql_engine='InnoDB',
++                   mysql_engine=CONF.database.mysql_storage_engine,
+                    extend_existing=True)
+ 
+     return images
+--- glance-12.0.0/glance/db/sqlalchemy/migrate_repo/versions/014_add_image_tags_table.py.orig	2016-08-10 09:36:22.685172827 -0700
++++ glance-12.0.0/glance/db/sqlalchemy/migrate_repo/versions/014_add_image_tags_table.py	2016-08-10 09:34:27.456082973 -0700
[email protected]@ -13,10 +13,13 @@
+ #    License for the specific language governing permissions and limitations
+ #    under the License.
+ 
++from oslo_config import cfg
+ from sqlalchemy import schema
+ 
+ from glance.db.sqlalchemy.migrate_repo import schema as glance_schema
+ 
++CONF = cfg.CONF
++
+ 
+ def define_image_tags_table(meta):
+     # Load the images table so the foreign key can be set up properly
[email protected]@ -30,7 +33,8 @@ def define_image_tags_table(meta):
+                                             nullable=False),
+                               schema.Column('image_id',
+                                             glance_schema.String(36),
+-                                            schema.ForeignKey('images.id'),
++                                            schema.ForeignKey('images.id',
++                                                              name='image_tags_ibfk_1'),
+                                             nullable=False),
+                               schema.Column('value',
+                                             glance_schema.String(255),
[email protected]@ -46,7 +50,7 @@ def define_image_tags_table(meta):
+                                             glance_schema.Boolean(),
+                                             nullable=False,
+                                             default=False),
+-                              mysql_engine='InnoDB',
++                              mysql_engine=CONF.database.mysql_storage_engine,
+                               mysql_charset='utf8')
+ 
+     schema.Index('ix_image_tags_image_id',
+--- glance-12.0.0/glance/db/sqlalchemy/migrate_repo/versions/018_add_image_locations_table.py.orig	2016-08-10 09:36:22.691935092 -0700
++++ glance-12.0.0/glance/db/sqlalchemy/migrate_repo/versions/018_add_image_locations_table.py	2016-08-10 09:34:27.457534530 -0700
[email protected]@ -13,10 +13,13 @@
+ #    License for the specific language governing permissions and limitations
+ #    under the License.
+ 
++from oslo_config import cfg
+ import sqlalchemy
+ 
+ from glance.db.sqlalchemy.migrate_repo import schema
+ 
++CONF = cfg.CONF
++
+ 
+ def upgrade(migrate_engine):
+     meta = sqlalchemy.schema.MetaData(migrate_engine)
[email protected]@ -32,7 +35,8 @@ def upgrade(migrate_engine):
+                           nullable=False),
+         sqlalchemy.Column('image_id',
+                           schema.String(36),
+-                          sqlalchemy.ForeignKey('images.id'),
++                          sqlalchemy.ForeignKey('images.id',
++                                                name='image_locations_ibfk_1'),
+                           nullable=False,
+                           index=True),
+         sqlalchemy.Column('value',
[email protected]@ -50,7 +54,7 @@ def upgrade(migrate_engine):
+                           nullable=False,
+                           default=False,
+                           index=True),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8',
+     )
+ 
+--- glance-12.0.0/glance/db/sqlalchemy/migrate_repo/versions/021_set_engine_mysql_innodb.py.orig	2016-08-10 09:36:22.698690410 -0700
++++ glance-12.0.0/glance/db/sqlalchemy/migrate_repo/versions/021_set_engine_mysql_innodb.py	2016-08-10 09:34:27.458291445 -0700
[email protected]@ -14,21 +14,24 @@
+ #    License for the specific language governing permissions and limitations
+ #    under the License.
+ 
++from oslo_config import cfg
+ from sqlalchemy import MetaData
+ 
+ tables = ['image_locations']
++CONF = cfg.CONF
+ 
+ 
+ def upgrade(migrate_engine):
+     meta = MetaData()
+     meta.bind = migrate_engine
+     if migrate_engine.name == "mysql":
+-        d = migrate_engine.execute("SHOW TABLE STATUS WHERE Engine!='InnoDB';")
++        d = migrate_engine.execute("SHOW TABLE STATUS WHERE Engine!='%s';" %
++                                   CONF.database.mysql_storage_engine)
+         for row in d.fetchall():
+             table_name = row[0]
+             if table_name in tables:
+-                migrate_engine.execute("ALTER TABLE %s Engine=InnoDB" %
+-                                       table_name)
++                migrate_engine.execute("ALTER TABLE %(db_table)s Engine=%(mysql_storage_engine)s" %
++                                       dict(db_table=table_name, mysql_storage_engine=CONF.database.mysql_storage_engine))
+ 
+ 
+ def downgrade(migrate_engine):
+--- glance-12.0.0/glance/db/sqlalchemy/migrate_repo/versions/022_image_member_index.py.orig	2016-08-10 09:36:22.705922240 -0700
++++ glance-12.0.0/glance/db/sqlalchemy/migrate_repo/versions/022_image_member_index.py	2016-08-10 09:34:27.459101595 -0700
[email protected]@ -15,6 +15,7 @@
+ 
+ import re
+ 
++from migrate.changeset.constraint import ForeignKeyConstraint
+ from migrate.changeset import UniqueConstraint
+ from oslo_db import exception as db_exception
+ from sqlalchemy import and_, func, orm
[email protected]@ -28,6 +29,12 @@ ORIGINAL_KEYNAME_RE = re.compile('image_
+ 
+ def upgrade(migrate_engine):
+     image_members = _get_image_members_table(migrate_engine)
++    images = _get_images_table(migrate_engine)
++    # Drop foreign key first
++    fkc = ForeignKeyConstraint([image_members.c.image_id],
++                               [images.c.id],
++                               name='image_members_image_id_fkey')
++    fkc.drop()
+ 
+     if migrate_engine.name in ('mysql', 'postgresql'):
+         try:
[email protected]@ -44,6 +51,9 @@ def upgrade(migrate_engine):
+                          name=NEW_KEYNAME,
+                          table=image_members).create()
+ 
++    # Add foreign key back
++    fkc.create()
++
+ 
+ def downgrade(migrate_engine):
+     image_members = _get_image_members_table(migrate_engine)
[email protected]@ -65,6 +75,12 @@ def _get_image_members_table(migrate_eng
+     return Table('image_members', meta, autoload=True)
+ 
+ 
++def _get_images_table(migrate_engine):
++    meta = MetaData()
++    meta.bind = migrate_engine
++    return Table('images', meta, autoload=True)
++
++
+ def _get_original_keyname(db):
+     return {'mysql': 'image_id',
+             'postgresql': 'image_members_image_id_member_key'}[db]
+--- glance-12.0.0/glance/db/sqlalchemy/migrate_repo/versions/030_add_tasks_table.py.orig	2016-08-10 09:36:22.712553022 -0700
++++ glance-12.0.0/glance/db/sqlalchemy/migrate_repo/versions/030_add_tasks_table.py	2016-08-10 09:34:27.460669890 -0700
[email protected]@ -14,11 +14,14 @@
+ #    License for the specific language governing permissions and limitations
+ #    under the License.
+ 
++from oslo_config import cfg
+ from sqlalchemy.schema import (Column, MetaData, Table, Index)
+ 
+ from glance.db.sqlalchemy.migrate_repo.schema import (
+     Boolean, DateTime, String, Text, create_tables, drop_tables)  # noqa
+ 
++CONF = cfg.CONF
++
+ 
+ def define_tasks_table(meta):
+     tasks = Table('tasks',
[email protected]@ -38,7 +41,7 @@ def define_tasks_table(meta):
+                          Boolean(),
+                          nullable=False,
+                          default=False),
+-                  mysql_engine='InnoDB',
++                  mysql_engine=CONF.database.mysql_storage_engine,
+                   mysql_charset='utf8',
+                   extend_existing=True)
+ 
+--- glance-12.0.0/glance/db/sqlalchemy/migrate_repo/versions/032_add_task_info_table.py.orig	2016-08-10 09:36:22.719716648 -0700
++++ glance-12.0.0/glance/db/sqlalchemy/migrate_repo/versions/032_add_task_info_table.py	2016-08-10 09:34:27.471154455 -0700
[email protected]@ -13,6 +13,7 @@
+ #    License for the specific language governing permissions and limitations
+ #    under the License.
+ 
++from oslo_config import cfg
+ from sqlalchemy.schema import (Column, ForeignKey, MetaData, Table)
+ 
+ from glance.db.sqlalchemy.migrate_repo.schema import (String,
[email protected]@ -21,6 +22,7 @@ from glance.db.sqlalchemy.migrate_repo.s
+                                                       drop_tables)  # noqa
+ 
+ TASKS_MIGRATE_COLUMNS = ['input', 'message', 'result']
++CONF = cfg.CONF
+ 
+ 
+ def define_task_info_table(meta):
[email protected]@ -31,13 +33,14 @@ def define_task_info_table(meta):
+     task_info = Table('task_info',
+                       meta,
+                       Column('task_id', String(36),
+-                             ForeignKey('tasks.id'),
++                             ForeignKey('tasks.id',
++                             name='task_info_ibfk_1'),
+                              primary_key=True,
+                              nullable=False),
+                       Column('input', Text()),
+                       Column('result', Text()),
+                       Column('message', Text()),
+-                      mysql_engine='InnoDB',
++                      mysql_engine=CONF.database.mysql_storage_engine,
+                       mysql_charset='utf8')
+ 
+     return task_info
+--- glance-12.0.0/glance/db/sqlalchemy/migrate_repo/versions/035_add_metadef_tables.py.orig	2016-08-10 09:36:22.726515825 -0700
++++ glance-12.0.0/glance/db/sqlalchemy/migrate_repo/versions/035_add_metadef_tables.py	2016-08-10 09:34:27.472707375 -0700
[email protected]@ -12,6 +12,7 @@
+ #    License for the specific language governing permissions and limitations
+ #    under the License.
+ 
++from oslo_config import cfg
+ import sqlalchemy
+ from sqlalchemy.schema import (
+     Column, ForeignKey, Index, MetaData, Table, UniqueConstraint)  # noqa
[email protected]@ -25,6 +26,7 @@ from glance.db.sqlalchemy.migrate_repo.s
+ RESOURCE_TYPES = [u'OS::Glance::Image', u'OS::Cinder::Volume',
+                   u'OS::Nova::Flavor', u'OS::Nova::Aggregate',
+                   u'OS::Nova::Server']
++CONF = cfg.CONF
+ 
+ 
+ def _get_metadef_resource_types_table(meta):
[email protected]@ -65,7 +67,7 @@ def define_metadef_namespaces_table(meta
+                        Column('created_at', DateTime(), nullable=False),
+                        Column('updated_at', DateTime()),
+                        UniqueConstraint('namespace', **_constr_kwargs),
+-                       mysql_engine='InnoDB',
++                       mysql_engine=CONF.database.mysql_storage_engine,
+                        mysql_charset='utf8',
+                        extend_existing=True)
+ 
[email protected]@ -85,7 +87,8 @@ def define_metadef_objects_table(meta):
+                     meta,
+                     Column('id', Integer(), primary_key=True, nullable=False),
+                     Column('namespace_id', Integer(),
+-                           ForeignKey('metadef_namespaces.id'),
++                           ForeignKey('metadef_namespaces.id',
++                           name='metadef_objects_ibfk_1'),
+                            nullable=False),
+                     Column('name', String(80), nullable=False),
+                     Column('description', Text()),
[email protected]@ -95,7 +98,7 @@ def define_metadef_objects_table(meta):
+                     Column('updated_at', DateTime()),
+                     UniqueConstraint('namespace_id', 'name',
+                                      **_constr_kwargs),
+-                    mysql_engine='InnoDB',
++                    mysql_engine=CONF.database.mysql_storage_engine,
+                     mysql_charset='utf8',
+                     extend_existing=True)
+ 
[email protected]@ -117,14 +120,15 @@ def define_metadef_properties_table(meta
+         'metadef_properties',
+         meta,
+         Column('id', Integer(), primary_key=True, nullable=False),
+-        Column('namespace_id', Integer(), ForeignKey('metadef_namespaces.id'),
++        Column('namespace_id', Integer(), ForeignKey('metadef_namespaces.id',
++               name='metadef_properties_ibfk_1'),
+                nullable=False),
+         Column('name', String(80), nullable=False),
+         Column('schema', Text(), nullable=False),
+         Column('created_at', DateTime(), nullable=False),
+         Column('updated_at', DateTime()),
+         UniqueConstraint('namespace_id', 'name', **_constr_kwargs),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8',
+         extend_existing=True)
+ 
[email protected]@ -151,7 +155,7 @@ def define_metadef_resource_types_table(
+         Column('created_at', DateTime(), nullable=False),
+         Column('updated_at', DateTime()),
+         UniqueConstraint('name', **_constr_kwargs),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8',
+         extend_existing=True)
+ 
[email protected]@ -172,10 +176,12 @@ def define_metadef_namespace_resource_ty
+         'metadef_namespace_resource_types',
+         meta,
+         Column('resource_type_id', Integer(),
+-               ForeignKey('metadef_resource_types.id'),
++               ForeignKey('metadef_resource_types.id',
++               name='metadef_namespace_resource_types_ibfk_1'),
+                primary_key=True, nullable=False),
+         Column('namespace_id', Integer(),
+-               ForeignKey('metadef_namespaces.id'),
++               ForeignKey('metadef_namespaces.id',
++               name='metadef_namespace_resource_types_ibfk_2'),
+                primary_key=True, nullable=False),
+         Column('properties_target', String(80)),
+         Column('prefix', String(80)),
[email protected]@ -183,7 +189,7 @@ def define_metadef_namespace_resource_ty
+         Column('updated_at', DateTime()),
+         UniqueConstraint('resource_type_id', 'namespace_id',
+                          **_constr_kwargs),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8',
+         extend_existing=True)
+ 
+--- glance-12.0.0/glance/db/sqlalchemy/migrate_repo/versions/037_add_changes_to_satisfy_models.py.orig	2016-08-10 09:36:22.732852195 -0700
++++ glance-12.0.0/glance/db/sqlalchemy/migrate_repo/versions/037_add_changes_to_satisfy_models.py	2016-08-10 09:34:27.473457502 -0700
[email protected]@ -81,7 +81,8 @@ def upgrade(migrate_engine):
+         image_locations = Table('image_locations', meta, autoload=True)
+         if len(image_locations.foreign_keys) == 0:
+             migrate_engine.execute(AddConstraint(ForeignKeyConstraint(
+-                [image_locations.c.image_id], [images.c.id])))
++                [image_locations.c.image_id], [images.c.id],
++                name='image_locations_ibfk_1')))
+ 
+ 
+ def downgrade(migrate_engine):
+--- glance-12.0.0/glance/db/sqlalchemy/migrate_repo/versions/038_add_metadef_tags_table.py.orig	2016-08-10 09:36:22.739351950 -0700
++++ glance-12.0.0/glance/db/sqlalchemy/migrate_repo/versions/038_add_metadef_tags_table.py	2016-08-10 09:34:27.474322102 -0700
[email protected]@ -12,12 +12,15 @@
+ #    License for the specific language governing permissions and limitations
+ #    under the License.
+ 
++from oslo_config import cfg
+ from sqlalchemy.schema import (
+     Column, Index, MetaData, Table, UniqueConstraint)  # noqa
+ 
+ from glance.db.sqlalchemy.migrate_repo.schema import (
+     DateTime, Integer, String, create_tables, drop_tables)  # noqa
+ 
++CONF = cfg.CONF
++
+ 
+ def define_metadef_tags_table(meta):
+     _constr_kwargs = {}
[email protected]@ -32,7 +35,7 @@ def define_metadef_tags_table(meta):
+                          Column('updated_at', DateTime()),
+                          UniqueConstraint('namespace_id', 'name',
+                                           **_constr_kwargs),
+-                         mysql_engine='InnoDB',
++                         mysql_engine=CONF.database.mysql_storage_engine,
+                          mysql_charset='utf8',
+                          extend_existing=False)
+ 
+--- glance-12.0.0/glance/db/sqlalchemy/migrate_repo/versions/039_add_changes_to_satisfy_models_metadef.py.orig	2016-08-10 09:36:22.745773757 -0700
++++ glance-12.0.0/glance/db/sqlalchemy/migrate_repo/versions/039_add_changes_to_satisfy_models_metadef.py	2016-08-10 09:41:20.008006522 -0700
[email protected]@ -63,15 +63,25 @@ def upgrade(migrate_engine):
+     else:
+         Index('ix_namespaces_namespace', metadef_namespaces.c.namespace).drop()
+ 
++        # Drop foreign key first
++        fkc = migrate.ForeignKeyConstraint([metadef_objects.c.namespace_id],
++                                           [metadef_namespaces.c.id],
++                                           name='metadef_objects_ibfk_1')
++        fkc.drop()
++
+         Index('ix_objects_namespace_id_name', metadef_objects.c.namespace_id,
+               metadef_objects.c.name).drop()
+ 
++        # Add foreign key back
++        fkc.create()
++
+         Index('ix_metadef_properties_namespace_id_name',
+               metadef_properties.c.namespace_id,
+               metadef_properties.c.name).drop()
+ 
+     fkc = migrate.ForeignKeyConstraint([metadef_tags.c.namespace_id],
+-                                       [metadef_namespaces.c.id])
++                                       [metadef_namespaces.c.id],
++                                       name='metadef_tags_ibfk_1')
+     fkc.create()
+ 
+     # `migrate` module removes unique constraint after adding
[email protected]@ -88,8 +98,13 @@ def upgrade(migrate_engine):
+         uc.create()
+ 
+     if meta.bind.name != "ibm_db_sa":
++        fkc = migrate.ForeignKeyConstraint([metadef_tags.c.namespace_id],
++                                           [metadef_namespaces.c.id],
++                                           name='metadef_tags_ibfk_1')
++        fkc.drop()
+         Index('ix_tags_namespace_id_name', metadef_tags.c.namespace_id,
+               metadef_tags.c.name).drop()
++        fkc.create()
+ 
+     Index('ix_metadef_tags_name', metadef_tags.c.name).create()
+ 
[email protected]@ -120,10 +135,15 @@ def upgrade(migrate_engine):
+         migrate_engine.execute(DropConstraint(constraint))
+         fkc.create()
+ 
++        fkc = migrate.ForeignKeyConstraint([metadef_ns_res_types.c.resource_type_id],
++                                           [metadef_resource_types.c.id],
++                                           name='metadef_namespace_resource_types_ibfk_1')
++        fkc.drop()
+         constraint = UniqueConstraint(metadef_ns_res_types.c.resource_type_id,
+                                       metadef_ns_res_types.c.namespace_id,
+                                       name='resource_type_id')
+         migrate_engine.execute(DropConstraint(constraint))
++        fkc.create()
+ 
+         constraint = UniqueConstraint(metadef_namespaces.c.namespace,
+                                       name='namespace')
[email protected]@ -251,7 +271,8 @@ def downgrade(migrate_engine):
+ 
+     if migrate_engine.name != 'sqlite':
+         fkc = migrate.ForeignKeyConstraint([metadef_tags.c.namespace_id],
+-                                           [metadef_namespaces.c.id])
++                                           [metadef_namespaces.c.id],
++                                           name='metadef_tags_ibfk_1')
+         fkc.drop()
+ 
+         if meta.bind.name != "ibm_db_sa":
[email protected]@ -332,8 +353,8 @@ def downgrade(migrate_engine):
+     if migrate_engine.name == 'mysql':
+         fkc = migrate.ForeignKeyConstraint(
+             [metadef_ns_res_types.c.resource_type_id],
+-            [metadef_namespaces.c.id],
+-            name='metadef_namespace_resource_types_ibfk_2')
++            [metadef_resource_types.c.id],
++            name='metadef_namespace_resource_types_ibfk_1')
+         fkc.drop()
+ 
+         Index('ix_metadef_ns_res_types_namespace_id',
+--- glance-12.0.0/glance/db/sqlalchemy/migrate_repo/versions/040_add_changes_to_satisfy_metadefs_tags.py.orig	2016-08-10 09:42:23.016042269 -0700
++++ glance-12.0.0/glance/db/sqlalchemy/migrate_repo/versions/040_add_changes_to_satisfy_metadefs_tags.py	2016-08-10 09:41:29.896233507 -0700
[email protected]@ -10,7 +10,7 @@
+ #    License for the specific language governing permissions and limitations
+ #    under the License.
+ 
+-
++import migrate
+ import sqlalchemy
+ from sqlalchemy import (Table, Index)
+ 
[email protected]@ -20,5 +20,11 @@ def upgrade(migrate_engine):
+         meta = sqlalchemy.MetaData()
+         meta.bind = migrate_engine
+         metadef_tags = Table('metadef_tags', meta, autoload=True)
++        metadef_namespaces = Table('metadef_namespaces', meta, autoload=True)
++        fkc = migrate.ForeignKeyConstraint([metadef_tags.c.namespace_id],
++                                           [metadef_namespaces.c.id],
++                                           name='metadef_tags_ibfk_1')
++        fkc.drop()
+         Index('namespace_id', metadef_tags.c.namespace_id,
+               metadef_tags.c.name).drop()
++        fkc.create()
+--- glance-12.0.0/glance/db/sqlalchemy/migrate_repo/versions/041_add_artifact_tables.py.orig	2016-08-10 09:36:22.752236945 -0700
++++ glance-12.0.0/glance/db/sqlalchemy/migrate_repo/versions/041_add_artifact_tables.py	2016-08-10 09:34:27.477711872 -0700
[email protected]@ -12,6 +12,7 @@
+ # License for the specific language governing permissions and limitations
+ # under the License.
+ 
++from oslo_config import cfg
+ from sqlalchemy.schema import (Column, ForeignKey, Index, MetaData, Table)
+ 
+ 
[email protected]@ -19,6 +20,8 @@ from glance.db.sqlalchemy.migrate_repo.s
+     BigInteger, Boolean, DateTime, Integer, Numeric, String, Text,
+     create_tables)  # noqa
+ 
++CONF = cfg.CONF
++
+ 
+ def define_artifacts_table(meta):
+     artifacts = Table('artifacts',
[email protected]@ -43,7 +46,7 @@ def define_artifacts_table(meta):
+                              nullable=False),
+                       Column('deleted_at', DateTime()),
+                       Column('published_at', DateTime()),
+-                      mysql_engine='InnoDB',
++                      mysql_engine=CONF.database.mysql_storage_engine,
+                       mysql_charset='utf8',
+                       extend_existing=True)
+ 
[email protected]@ -64,12 +67,14 @@ def define_artifact_tags_table(meta):
+                           Column('id', String(36), primary_key=True,
+                                  nullable=False),
+                           Column('artifact_id', String(36),
+-                                 ForeignKey('artifacts.id'), nullable=False),
++                                 ForeignKey('artifacts.id',
++                                 name='artifact_tags_ibfk_1'),
++                                 nullable=False),
+                           Column('value', String(255), nullable=False),
+                           Column('created_at', DateTime(), nullable=False),
+                           Column('updated_at', DateTime(),
+                                  nullable=False),
+-                          mysql_engine='InnoDB',
++                          mysql_engine=CONF.database.mysql_storage_engine,
+                           mysql_charset='utf8',
+                           extend_existing=True)
+ 
[email protected]@ -86,13 +91,16 @@ def define_artifact_dependencies_table(m
+                                   Column('id', String(36), primary_key=True,
+                                          nullable=False),
+                                   Column('artifact_source', String(36),
+-                                         ForeignKey('artifacts.id'),
++                                         ForeignKey('artifacts.id',
++                                         name='artifact_dependencies_ibfk_1'),
+                                          nullable=False),
+                                   Column('artifact_dest', String(36),
+-                                         ForeignKey('artifacts.id'),
++                                         ForeignKey('artifacts.id',
++                                         name='artifact_dependencies_ibfk_2'),
+                                          nullable=False),
+                                   Column('artifact_origin', String(36),
+-                                         ForeignKey('artifacts.id'),
++                                         ForeignKey('artifacts.id',
++                                         name='artifact_dependencies_ibfk_3'),
+                                          nullable=False),
+                                   Column('is_direct', Boolean(),
+                                          nullable=False),
[email protected]@ -102,7 +110,7 @@ def define_artifact_dependencies_table(m
+                                          nullable=False),
+                                   Column('updated_at', DateTime(),
+                                          nullable=False),
+-                                  mysql_engine='InnoDB',
++                                  mysql_engine=CONF.database.mysql_storage_engine,
+                                   mysql_charset='utf8',
+                                   extend_existing=True)
+ 
[email protected]@ -124,7 +132,8 @@ def define_artifact_blobs_table(meta):
+                            Column('id', String(36), primary_key=True,
+                                   nullable=False),
+                            Column('artifact_id', String(36),
+-                                  ForeignKey('artifacts.id'),
++                                  ForeignKey('artifacts.id',
++                                  name='artifact_blobs_ibfk_1'),
+                                   nullable=False),
+                            Column('size', BigInteger(), nullable=False),
+                            Column('checksum', String(32)),
[email protected]@ -134,7 +143,7 @@ def define_artifact_blobs_table(meta):
+                            Column('created_at', DateTime(), nullable=False),
+                            Column('updated_at', DateTime(),
+                                   nullable=False),
+-                           mysql_engine='InnoDB',
++                           mysql_engine=CONF.database.mysql_storage_engine,
+                            mysql_charset='utf8',
+                            extend_existing=True)
+     Index('ix_artifact_blobs_artifact_id',
[email protected]@ -151,7 +160,8 @@ def define_artifact_properties_table(met
+                                        primary_key=True,
+                                        nullable=False),
+                                 Column('artifact_id', String(36),
+-                                       ForeignKey('artifacts.id'),
++                                       ForeignKey('artifacts.id',
++                                       name='artifact_properties_ibfk_1'),
+                                        nullable=False),
+                                 Column('name', String(255),
+                                        nullable=False),
[email protected]@ -165,7 +175,7 @@ def define_artifact_properties_table(met
+                                 Column('updated_at', DateTime(),
+                                        nullable=False),
+                                 Column('position', Integer()),
+-                                mysql_engine='InnoDB',
++                                mysql_engine=CONF.database.mysql_storage_engine,
+                                 mysql_charset='utf8',
+                                 extend_existing=True)
+     Index('ix_artifact_properties_artifact_id',
[email protected]@ -181,7 +191,8 @@ def define_artifact_blob_locations_table
+                                            primary_key=True,
+                                            nullable=False),
+                                     Column('blob_id', String(36),
+-                                           ForeignKey('artifact_blobs.id'),
++                                           ForeignKey('artifact_blobs.id',
++                                                      name='artifact_blob_locations_ibfk_1'),
+                                            nullable=False),
+                                     Column('value', Text(), nullable=False),
+                                     Column('created_at', DateTime(),
[email protected]@ -191,7 +202,7 @@ def define_artifact_blob_locations_table
+                                     Column('position', Integer()),
+                                     Column('status', String(36),
+                                            nullable=True),
+-                                    mysql_engine='InnoDB',
++                                    mysql_engine=CONF.database.mysql_storage_engine,
+                                     mysql_charset='utf8',
+                                     extend_existing=True)
+     Index('ix_artifact_blob_locations_blob_id',
+--- glance-12.0.0/glance/db/sqlalchemy/models_glare.py.orig	2016-08-10 09:36:22.759461763 -0700
++++ glance-12.0.0/glance/db/sqlalchemy/models_glare.py	2016-08-10 09:34:27.479493197 -0700
[email protected]@ -14,6 +14,8 @@
+ 
+ import uuid
+ 
++from oslo_config import cfg
++from oslo_db import options
+ from oslo_db.sqlalchemy import models
+ from sqlalchemy import BigInteger
+ from sqlalchemy import Boolean
[email protected]@ -35,12 +37,15 @@ from glance.common import timeutils
+ import glance.glare as ga
+ 
+ BASE = declarative.declarative_base()
++CONF = cfg.CONF
++options.set_defaults(cfg.CONF)
+ 
+ 
+ class ArtifactBase(models.ModelBase, models.TimestampMixin):
+     """Base class for Artifact Models."""
+ 
+-    __table_args__ = {'mysql_engine': 'InnoDB', 'mysql_charset': 'utf8'}
++    __table_args__ = {'mysql_engine': CONF.database.mysql_storage_engine,
++                      'mysql_charset': 'utf8'}
+     __table_initialized__ = False
+     __protected_attributes__ = set([
+         "created_at", "updated_at"])
[email protected]@ -98,7 +103,8 @@ class Artifact(BASE, ArtifactBase):
+         Index('ix_artifact_state', 'state'),
+         Index('ix_artifact_owner', 'owner'),
+         Index('ix_artifact_visibility', 'visibility'),
+-        {'mysql_engine': 'InnoDB', 'mysql_charset': 'utf8'})
++        {'mysql_engine': CONF.database.mysql_storage_engine,
++         'mysql_charset': 'utf8'})
+ 
+     __protected_attributes__ = ArtifactBase.__protected_attributes__.union(
+         set(['published_at', 'deleted_at']))
[email protected]@ -219,7 +225,8 @@ class ArtifactDependency(BASE, ArtifactB
+                             'artifact_dest'),
+                       Index('ix_artifact_dependencies_direct_dependencies',
+                             'artifact_source', 'is_direct'),
+-                      {'mysql_engine': 'InnoDB', 'mysql_charset': 'utf8'})
++                      {'mysql_engine': CONF.database.mysql_storage_engine,
++                       'mysql_charset': 'utf8'})
+ 
+     id = Column(String(36), primary_key=True, nullable=False,
+                 default=lambda: str(uuid.uuid4()))
[email protected]@ -248,7 +255,8 @@ class ArtifactTag(BASE, ArtifactBase):
+     __table_args__ = (Index('ix_artifact_tags_artifact_id', 'artifact_id'),
+                       Index('ix_artifact_tags_artifact_id_tag_value',
+                             'artifact_id', 'value'),
+-                      {'mysql_engine': 'InnoDB', 'mysql_charset': 'utf8'},)
++                      {'mysql_engine': CONF.database.mysql_storage_engine,
++                       'mysql_charset': 'utf8'},)
+ 
+     id = Column(String(36), primary_key=True, nullable=False,
+                 default=lambda: str(uuid.uuid4()))
[email protected]@ -265,7 +273,8 @@ class ArtifactProperty(BASE, ArtifactBas
+     __table_args__ = (
+         Index('ix_artifact_properties_artifact_id', 'artifact_id'),
+         Index('ix_artifact_properties_name', 'name'),
+-        {'mysql_engine': 'InnoDB', 'mysql_charset': 'utf8'},)
++        {'mysql_engine': CONF.database.mysql_storage_engine,
++         'mysql_charset': 'utf8'},)
+     id = Column(String(36), primary_key=True, nullable=False,
+                 default=lambda: str(uuid.uuid4()))
+     artifact_id = Column(String(36), ForeignKey('artifacts.id'),
[email protected]@ -287,7 +296,8 @@ class ArtifactBlob(BASE, ArtifactBase):
+     __table_args__ = (
+         Index('ix_artifact_blobs_artifact_id', 'artifact_id'),
+         Index('ix_artifact_blobs_name', 'name'),
+-        {'mysql_engine': 'InnoDB', 'mysql_charset': 'utf8'},)
++        {'mysql_engine': CONF.database.mysql_storage_engine,
++         'mysql_charset': 'utf8'},)
+     id = Column(String(36), primary_key=True, nullable=False,
+                 default=lambda: str(uuid.uuid4()))
+     artifact_id = Column(String(36), ForeignKey('artifacts.id'),
[email protected]@ -307,7 +317,8 @@ class ArtifactBlobLocation(BASE, Artifac
+     __tablename__ = 'artifact_blob_locations'
+     __table_args__ = (Index('ix_artifact_blob_locations_blob_id',
+                             'blob_id'),
+-                      {'mysql_engine': 'InnoDB', 'mysql_charset': 'utf8'})
++                      {'mysql_engine': CONF.database.mysql_storage_engine,
++                       'mysql_charset': 'utf8'})
+ 
+     id = Column(String(36), primary_key=True, nullable=False,
+                 default=lambda: str(uuid.uuid4()))
+--- glance-12.0.0/glance/db/sqlalchemy/models_metadef.py.orig	2016-08-10 09:36:22.766310320 -0700
++++ glance-12.0.0/glance/db/sqlalchemy/models_metadef.py	2016-08-10 09:34:27.480397292 -0700
[email protected]@ -16,6 +16,7 @@
+ SQLAlchemy models for glance metadata schema
+ """
+ 
++from oslo_config import cfg
+ from oslo_db.sqlalchemy import models
+ from sqlalchemy import Boolean
+ from sqlalchemy import Column
[email protected]@ -32,6 +33,8 @@ from sqlalchemy import UniqueConstraint
+ from glance.common import timeutils
+ from glance.db.sqlalchemy.models import JSONEncodedDict
+ 
++CONF = cfg.CONF
++
+ 
+ class DictionaryBase(models.ModelBase):
+     metadata = None
[email protected]@ -49,7 +52,8 @@ BASE_DICT = declarative_base(cls=Diction
+ class GlanceMetadefBase(models.TimestampMixin):
+     """Base class for Glance Metadef Models."""
+ 
+-    __table_args__ = {'mysql_engine': 'InnoDB', 'mysql_charset': 'utf8'}
++    __table_args__ = {'mysql_engine': CONF.database.mysql_storage_engine,
++                      'mysql_charset': 'utf8'}
+     __table_initialized__ = False
+     __protected_attributes__ = set(["created_at", "updated_at"])
+ 
+--- glance-12.0.0/glance/db/sqlalchemy/models.py.orig	2016-08-10 09:36:22.773221505 -0700
++++ glance-12.0.0/glance/db/sqlalchemy/models.py	2016-08-10 09:34:27.481259927 -0700
[email protected]@ -20,6 +20,7 @@ SQLAlchemy models for glance data
+ 
+ import uuid
+ 
++from oslo_config import cfg
+ from oslo_db.sqlalchemy import models
+ from oslo_serialization import jsonutils
+ from sqlalchemy import BigInteger
[email protected]@ -41,6 +42,7 @@ from glance.common import timeutils
+ 
+ 
+ BASE = declarative_base()
++CONF = cfg.CONF
+ 
+ 
+ class JSONEncodedDict(TypeDecorator):
[email protected]@ -62,7 +64,7 @@ class JSONEncodedDict(TypeDecorator):
+ class GlanceBase(models.ModelBase, models.TimestampMixin):
+     """Base class for Glance Models."""
+ 
+-    __table_args__ = {'mysql_engine': 'InnoDB', 'mysql_charset': 'utf8'}
++    __table_args__ = {'mysql_engine': CONF.database.mysql_storage_engine, 'mysql_charset': 'utf8'}
+     __table_initialized__ = False
+     __protected_attributes__ = set([
+         "created_at", "updated_at", "deleted_at", "deleted"])
+--- glance-12.0.0/glance/tests/unit/test_migrations.py.orig	2016-08-10 09:36:22.780385325 -0700
++++ glance-12.0.0/glance/tests/unit/test_migrations.py	2016-08-10 09:34:27.483366399 -0700
[email protected]@ -122,7 +122,7 @@ class MigrationsMixin(test_migrations.Wa
+                                                         sqlalchemy.Boolean(),
+                                                         nullable=False,
+                                                         default=False),
+-                                      mysql_engine='InnoDB',
++                                      mysql_engine=CONF.database.mysql_storage_engine,
+                                       mysql_charset='utf8')
+         images_001.create()
+ 
[email protected]@ -1846,10 +1846,11 @@ class TestMysqlMigrations(test_base.MySQ
+         noninnodb = self.migrate_engine.execute(
+             "SELECT count(*) "
+             "FROM information_schema.TABLES "
+-            "WHERE TABLE_SCHEMA='%s' "
+-            "AND ENGINE!='InnoDB' "
++            "WHERE TABLE_SCHEMA='%(table_schema)s' "
++            "AND ENGINE!='%(mysql_storage_engine)s' "
+             "AND TABLE_NAME!='migrate_version'"
+-            % self.migrate_engine.url.database)
++            % dict(table_schema=self.migrate_engine.url.database,
++                   mysql_storage_engine=CONF.database.mysql_storage_engine))
+         count = noninnodb.scalar()
+         self.assertEqual(0, count, "%d non InnoDB tables created" % count)
+ 
--- a/components/openstack/heat/files/heat.conf	Thu Sep 08 12:41:54 2016 -0700
+++ b/components/openstack/heat/files/heat.conf	Thu Sep 08 13:16:06 2016 -0600
@@ -900,6 +900,12 @@
 # (string value)
 #mysql_sql_mode = TRADITIONAL
 
+# This configures the MySQL storage engine. This allows for OpenStack to
+# support different storage engines such as InnoDB, NDB, etc. By Default,
+# this value will be set to InnoDB. For MySQL Cluster, set to NDBCLUSTER.
+# Example: mysql_storage_engine=(string value)
+#mysql_storage_engine = InnoDB
+
 # Timeout before idle SQL connections are reaped. (integer value)
 # Deprecated group/name - [DEFAULT]/sql_idle_timeout
 # Deprecated group/name - [DATABASE]/sql_idle_timeout
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openstack/heat/patches/08-mysql_cluster_support.patch	Thu Sep 08 13:16:06 2016 -0600
@@ -0,0 +1,304 @@
+This patchset is for bug:
+
+24394543 Mitaka Heat should support MySQL Cluster
+
+This fixes the following aspects of Heat:
+1. Implementation of an oslo.db configuration parameter to specify the MySQL
+   storage engine (mysql_storage_engine).
+2. Replacement of hardcoded SQL statements that set the engine to "InnoDB"
+   to the above configuration value.
+3. Logic to handle SQL differences between MySQL InnoDB and MySQL Cluster (NDB).
+   This includes column lengths, constraints, foreign keys, and indexes.
+
+This has not been committed upstream, but has been filed in launchpad:
+
+https://bugs.launchpad.net/heat/+bug/1564110
+
+
+--- heat-6.0.0/heat/db/sqlalchemy/migrate_repo/versions/028_havana.py.orig	2016-08-08 13:53:36.196603218 -0700
++++ heat-6.0.0/heat/db/sqlalchemy/migrate_repo/versions/028_havana.py	2016-08-08 13:53:14.078159110 -0700
[email protected]@ -11,12 +11,15 @@
+ #    License for the specific language governing permissions and limitations
+ #    under the License.
+ 
++from oslo_config import cfg
+ import uuid
+ 
+ import sqlalchemy
+ 
+ from heat.db.sqlalchemy import types
+ 
++CONF = cfg.CONF
++
+ 
+ def upgrade(migrate_engine):
+     meta = sqlalchemy.MetaData()
[email protected]@ -29,7 +32,7 @@ def upgrade(migrate_engine):
+         sqlalchemy.Column('created_at', sqlalchemy.DateTime),
+         sqlalchemy.Column('updated_at', sqlalchemy.DateTime),
+         sqlalchemy.Column('template', types.LongText),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -46,7 +49,7 @@ def upgrade(migrate_engine):
+         sqlalchemy.Column('tenant_id', sqlalchemy.String(256)),
+         sqlalchemy.Column('trust_id', sqlalchemy.String(255)),
+         sqlalchemy.Column('trustor_user_id', sqlalchemy.String(64)),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -75,7 +78,7 @@ def upgrade(migrate_engine):
+         sqlalchemy.Column('tenant', sqlalchemy.String(256)),
+         sqlalchemy.Column('disable_rollback', sqlalchemy.Boolean,
+                           nullable=False),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -93,7 +96,7 @@ def upgrade(migrate_engine):
+         sqlalchemy.Column('stack_id', sqlalchemy.String(36),
+                           sqlalchemy.ForeignKey('stack.id'), nullable=False),
+         sqlalchemy.Column('rsrc_metadata', types.LongText),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -110,7 +113,7 @@ def upgrade(migrate_engine):
+                           sqlalchemy.String(36),
+                           sqlalchemy.ForeignKey('resource.id'),
+                           nullable=False),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -129,7 +132,7 @@ def upgrade(migrate_engine):
+         sqlalchemy.Column('resource_status_reason', sqlalchemy.String(255)),
+         sqlalchemy.Column('resource_type', sqlalchemy.String(255)),
+         sqlalchemy.Column('resource_properties', sqlalchemy.PickleType),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -145,7 +148,7 @@ def upgrade(migrate_engine):
+         sqlalchemy.Column('last_evaluated', sqlalchemy.DateTime),
+         sqlalchemy.Column('stack_id', sqlalchemy.String(36),
+                           sqlalchemy.ForeignKey('stack.id'), nullable=False),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -159,7 +162,7 @@ def upgrade(migrate_engine):
+         sqlalchemy.Column('watch_rule_id', sqlalchemy.Integer,
+                           sqlalchemy.ForeignKey('watch_rule.id'),
+                           nullable=False),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
+--- heat-6.0.0/heat/db/sqlalchemy/migrate_repo/versions/031_stack_lock.py.orig	2016-08-08 13:53:44.254365335 -0700
++++ heat-6.0.0/heat/db/sqlalchemy/migrate_repo/versions/031_stack_lock.py	2016-08-08 13:53:14.078870800 -0700
[email protected]@ -11,8 +11,11 @@
+ #    License for the specific language governing permissions and limitations
+ #    under the License.
+ 
++from oslo_config import cfg
+ import sqlalchemy
+ 
++CONF = cfg.CONF
++
+ 
+ def upgrade(migrate_engine):
+     meta = sqlalchemy.MetaData()
[email protected]@ -27,7 +30,7 @@ def upgrade(migrate_engine):
+         sqlalchemy.Column('created_at', sqlalchemy.DateTime),
+         sqlalchemy.Column('updated_at', sqlalchemy.DateTime),
+         sqlalchemy.Column('engine_id', sqlalchemy.String(length=36)),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+     sqlalchemy.Table('stack', meta, autoload=True)
+--- heat-6.0.0/heat/db/sqlalchemy/migrate_repo/versions/033_software_config.py.orig	2016-08-08 13:53:52.462462615 -0700
++++ heat-6.0.0/heat/db/sqlalchemy/migrate_repo/versions/033_software_config.py	2016-08-08 13:53:14.079526502 -0700
[email protected]@ -11,10 +11,13 @@
+ #    License for the specific language governing permissions and limitations
+ #    under the License.
+ 
++from oslo_config import cfg
+ import sqlalchemy
+ 
+ from heat.db.sqlalchemy import types
+ 
++CONF = cfg.CONF
++
+ 
+ def upgrade(migrate_engine):
+     meta = sqlalchemy.MetaData()
[email protected]@ -34,7 +37,7 @@ def upgrade(migrate_engine):
+         sqlalchemy.Column('tenant', sqlalchemy.String(64),
+                           nullable=False,
+                           index=True),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+     software_config.create()
[email protected]@ -63,7 +66,7 @@ def upgrade(migrate_engine):
+         sqlalchemy.Column('tenant', sqlalchemy.String(64),
+                           nullable=False,
+                           index=True),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+     software_deployment.create()
+--- heat-6.0.0/heat/db/sqlalchemy/migrate_repo/versions/044_snapshots.py.orig	2016-08-08 13:54:01.375663605 -0700
++++ heat-6.0.0/heat/db/sqlalchemy/migrate_repo/versions/044_snapshots.py	2016-08-08 13:53:14.080116985 -0700
[email protected]@ -11,10 +11,13 @@
+ #    License for the specific language governing permissions and limitations
+ #    under the License.
+ 
++from oslo_config import cfg
+ import sqlalchemy
+ 
+ from heat.db.sqlalchemy import types
+ 
++CONF = cfg.CONF
++
+ 
+ def upgrade(migrate_engine):
+     meta = sqlalchemy.MetaData()
[email protected]@ -38,7 +41,7 @@ def upgrade(migrate_engine):
+         sqlalchemy.Column('tenant', sqlalchemy.String(64),
+                           nullable=False,
+                           index=True),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+     sqlalchemy.Table('stack', meta, autoload=True)
+--- heat-6.0.0/heat/db/sqlalchemy/migrate_repo/versions/051_service.py.orig	2016-08-08 13:54:08.527359970 -0700
++++ heat-6.0.0/heat/db/sqlalchemy/migrate_repo/versions/051_service.py	2016-08-08 13:53:14.080700065 -0700
[email protected]@ -13,10 +13,13 @@
+ # See the License for the specific language governing permissions and
+ # limitations under the License.
+ 
++from oslo_config import cfg
+ import uuid
+ 
+ import sqlalchemy
+ 
++CONF = cfg.CONF
++
+ 
+ def upgrade(migrate_engine):
+     meta = sqlalchemy.MetaData()
[email protected]@ -36,7 +39,7 @@ def upgrade(migrate_engine):
+         sqlalchemy.Column('created_at', sqlalchemy.DateTime),
+         sqlalchemy.Column('updated_at', sqlalchemy.DateTime),
+         sqlalchemy.Column('deleted_at', sqlalchemy.DateTime),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+     service.create()
+--- heat-6.0.0/heat/db/sqlalchemy/migrate_repo/versions/054_stack_tags_table.py.orig	2016-08-08 13:54:17.198831162 -0700
++++ heat-6.0.0/heat/db/sqlalchemy/migrate_repo/versions/054_stack_tags_table.py	2016-08-08 13:53:14.081281862 -0700
[email protected]@ -11,8 +11,11 @@
+ #    License for the specific language governing permissions and limitations
+ #    under the License.
+ 
++from oslo_config import cfg
+ import sqlalchemy
+ 
++CONF = cfg.CONF
++
+ 
+ def upgrade(migrate_engine):
+     meta = sqlalchemy.MetaData(bind=migrate_engine)
[email protected]@ -33,7 +36,7 @@ def upgrade(migrate_engine):
+                           sqlalchemy.String(36),
+                           sqlalchemy.ForeignKey('stack.id'),
+                           nullable=False),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+     stack_tag.create()
+--- heat-6.0.0/heat/db/sqlalchemy/migrate_repo/versions/057_resource_uuid_to_id.py.orig	2016-08-08 13:54:24.119188110 -0700
++++ heat-6.0.0/heat/db/sqlalchemy/migrate_repo/versions/057_resource_uuid_to_id.py	2016-08-08 13:53:14.081963807 -0700
[email protected]@ -189,10 +189,11 @@ def upgrade_resource_data_post(migrate_e
+         name = inspector.get_indexes('resource_data')[0]['name']
+         sqlalchemy.Index(name, rd_table.c.resource_id).drop()
+ 
++    # Change column before it becomes a foreign key
++    rd_table.c.resource_id.alter(nullable=False)
+     cons = migrate.ForeignKeyConstraint(columns=[rd_table.c.resource_id],
+                                         refcolumns=[res_table.c.id])
+     cons.create()
+-    rd_table.c.resource_id.alter(nullable=False)
+ 
+     rd_table.c.tmp_res_uuid.drop()
+ 
+--- heat-6.0.0/heat/db/sqlalchemy/migrate_repo/versions/059_sync_point.py.orig	2016-08-08 13:54:31.078965167 -0700
++++ heat-6.0.0/heat/db/sqlalchemy/migrate_repo/versions/059_sync_point.py	2016-08-08 13:53:14.082544202 -0700
[email protected]@ -11,10 +11,13 @@
+ #    License for the specific language governing permissions and limitations
+ #    under the License.
+ 
++from oslo_config import cfg
+ import sqlalchemy
+ 
+ from heat.db.sqlalchemy import types as heat_db_types
+ 
++CONF = cfg.CONF
++
+ 
+ def upgrade(migrate_engine):
+     meta = sqlalchemy.MetaData()
[email protected]@ -41,7 +44,7 @@ def upgrade(migrate_engine):
+         sqlalchemy.ForeignKeyConstraint(['stack_id'], ['stack.id'],
+                                         name='fk_stack_id'),
+ 
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+     sync_point.create()
+--- heat-6.0.0/heat/db/sqlalchemy/models.py.orig	2016-08-08 13:54:38.888846543 -0700
++++ heat-6.0.0/heat/db/sqlalchemy/models.py	2016-08-08 13:55:40.851731167 -0700
[email protected]@ -15,6 +15,7 @@
+ 
+ import uuid
+ 
++from oslo_config import cfg
+ from oslo_db.sqlalchemy import models
+ from oslo_utils import timeutils
+ import six
[email protected]@ -27,6 +28,7 @@ from sqlalchemy.orm import session as or
+ from heat.db.sqlalchemy import types
+ 
+ BASE = declarative.declarative_base()
++CONF = cfg.CONF
+ 
+ 
+ def get_session():
[email protected]@ -36,7 +38,7 @@ def get_session():
+ 
+ class HeatBase(models.ModelBase, models.TimestampMixin):
+     """Base class for Heat Models."""
+-    __table_args__ = {'mysql_engine': 'InnoDB'}
++    __table_args__ = {'mysql_engine': CONF.database.mysql_storage_engine}
+ 
+     def expire(self, session=None, attrs=None):
+         """Expire this object ()."""
--- a/components/openstack/ironic/files/ironic.conf	Thu Sep 08 12:41:54 2016 -0700
+++ b/components/openstack/ironic/files/ironic.conf	Thu Sep 08 13:16:06 2016 -0600
@@ -894,6 +894,12 @@
 # value)
 #mysql_sql_mode=TRADITIONAL
 
+# This configures the MySQL storage engine. This allows for OpenStack to
+# support different storage engines such as InnoDB, NDB, etc. By Default,
+# this value will be set to InnoDB. For MySQL Cluster, set to NDBCLUSTER.
+# Example: mysql_storage_engine=(string value)
+#mysql_storage_engine = InnoDB
+
 # Timeout before idle SQL connections are reaped. (integer
 # value)
 # Deprecated group/name - [DEFAULT]/sql_idle_timeout
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openstack/ironic/patches/05-mysql_cluster_support.patch	Thu Sep 08 13:16:06 2016 -0600
@@ -0,0 +1,149 @@
+This patchset is for bug:
+
+24394552 Mitaka Ironic should support MySQL Cluster
+
+This fixes the following aspects of Ironic:
+1. Implementation of an oslo.db configuration parameter to specify the MySQL
+   storage engine (mysql_storage_engine).
+2. Replacement of hardcoded SQL statements that set the engine to "InnoDB"
+   to the above configuration value.
+3. Logic to handle SQL differences between MySQL InnoDB and MySQL Cluster (NDB).
+   This includes column lengths, constraints, foreign keys, and indexes.
+4. Changes the conductor table column "online" to be quoted because "online"
+   is a reserved word in MySQL and other databases.
+
+This has not been committed upstream, but has been filed in launchpad:
+
+https://bugs.launchpad.net/ironic/+bug/1564110
+
+
+--- ironic-5.1.2/ironic/db/sqlalchemy/alembic/versions/2581ebaf0cb2_initial_migration.py.orig	2016-08-05 15:02:32.138604246 -0700
++++ ironic-5.1.2/ironic/db/sqlalchemy/alembic/versions/2581ebaf0cb2_initial_migration.py	2016-08-05 15:04:47.376598798 -0700
[email protected]@ -24,8 +24,10 @@ revision = '2581ebaf0cb2'
+ down_revision = None
+ 
+ from alembic import op
++from oslo_config import cfg
+ import sqlalchemy as sa
+ 
++CONF = cfg.CONF
+ 
+ def upgrade():
+     # commands auto generated by Alembic - please adjust!
[email protected]@ -38,7 +40,7 @@ def upgrade():
+         sa.Column('drivers', sa.Text(), nullable=True),
+         sa.PrimaryKeyConstraint('id'),
+         sa.UniqueConstraint('hostname', name='uniq_conductors0hostname'),
+-        mysql_ENGINE='InnoDB',
++        mysql_ENGINE=CONF.database.mysql_storage_engine,
+         mysql_DEFAULT_CHARSET='UTF8'
+     )
+     op.create_table(
[email protected]@ -51,7 +53,7 @@ def upgrade():
+         sa.Column('description', sa.String(length=255), nullable=True),
+         sa.PrimaryKeyConstraint('id'),
+         sa.UniqueConstraint('uuid', name='uniq_chassis0uuid'),
+-        mysql_ENGINE='InnoDB',
++        mysql_ENGINE=CONF.database.mysql_storage_engine,
+         mysql_DEFAULT_CHARSET='UTF8'
+     )
+     op.create_table(
[email protected]@ -77,7 +79,7 @@ def upgrade():
+         sa.ForeignKeyConstraint(['chassis_id'], ['chassis.id'], ),
+         sa.PrimaryKeyConstraint('id'),
+         sa.UniqueConstraint('uuid', name='uniq_nodes0uuid'),
+-        mysql_ENGINE='InnoDB',
++        mysql_ENGINE=CONF.database.mysql_storage_engine,
+         mysql_DEFAULT_CHARSET='UTF8'
+     )
+     op.create_index('node_instance_uuid', 'nodes', ['instance_uuid'],
[email protected]@ -95,7 +97,7 @@ def upgrade():
+         sa.PrimaryKeyConstraint('id'),
+         sa.UniqueConstraint('address', name='uniq_ports0address'),
+         sa.UniqueConstraint('uuid', name='uniq_ports0uuid'),
+-        mysql_ENGINE='InnoDB',
++        mysql_ENGINE=CONF.database.mysql_storage_engine,
+         mysql_DEFAULT_CHARSET='UTF8'
+     )
+     # end Alembic commands
+--- ironic-5.1.2/ironic/db/sqlalchemy/alembic/versions/487deb87cc9d_add_conductor_affinity_and_online.py.orig	2016-08-05 15:01:05.339248664 -0700
++++ ironic-5.1.2/ironic/db/sqlalchemy/alembic/versions/487deb87cc9d_add_conductor_affinity_and_online.py	2016-08-05 15:01:53.896426955 -0700
[email protected]@ -29,7 +29,7 @@ import sqlalchemy as sa
+ def upgrade():
+     op.add_column(
+         'conductors',
+-        sa.Column('online', sa.Boolean(), default=True))
++        sa.Column('online', sa.Boolean(), default=True, quote=True))
+     op.add_column(
+         'nodes',
+         sa.Column('conductor_affinity', sa.Integer(),
+--- ironic-5.1.2/ironic/db/sqlalchemy/alembic/versions/48d6c242bb9b_add_node_tags.py.orig	2016-08-05 15:01:12.489520146 -0700
++++ ironic-5.1.2/ironic/db/sqlalchemy/alembic/versions/48d6c242bb9b_add_node_tags.py	2016-08-05 15:01:53.897903465 -0700
[email protected]@ -23,8 +23,10 @@ revision = '48d6c242bb9b'
+ down_revision = '516faf1bb9b1'
+ 
+ from alembic import op
++from oslo_config import cfg
+ import sqlalchemy as sa
+ 
++CONF = cfg.CONF
+ 
+ def upgrade():
+     op.create_table(
[email protected]@ -36,7 +38,7 @@ def upgrade():
+         sa.Column('tag', sa.String(length=255), nullable=False),
+         sa.ForeignKeyConstraint(['node_id'], ['nodes.id'], ),
+         sa.PrimaryKeyConstraint('node_id', 'tag'),
+-        mysql_ENGINE='InnoDB',
++        mysql_ENGINE=CONF.database.mysql_storage_engine,
+         mysql_DEFAULT_CHARSET='UTF8'
+     )
+     op.create_index('node_tags_idx', 'node_tags', ['tag'], unique=False)
+--- ironic-5.1.2/ironic/db/sqlalchemy/alembic/versions/5ea1b0d310e_added_port_group_table_and_altered_ports.py.orig	2016-08-05 15:01:20.133426869 -0700
++++ ironic-5.1.2/ironic/db/sqlalchemy/alembic/versions/5ea1b0d310e_added_port_group_table_and_altered_ports.py	2016-08-05 15:01:53.898548390 -0700
[email protected]@ -23,8 +23,10 @@ revision = '5ea1b0d310e'
+ down_revision = '48d6c242bb9b'
+ 
+ from alembic import op
++from oslo_config import cfg
+ import sqlalchemy as sa
+ 
++CONF = cfg.CONF
+ 
+ def upgrade():
+     op.create_table('portgroups',
[email protected]@ -42,7 +44,7 @@ def upgrade():
+                     sa.UniqueConstraint('address',
+                                         name='uniq_portgroups0address'),
+                     sa.UniqueConstraint('name', name='uniq_portgroups0name'),
+-                    mysql_ENGINE='InnoDB',
++                    mysql_ENGINE=CONF.database.mysql_storage_engine,
+                     mysql_DEFAULT_CHARSET='UTF8')
+     op.add_column(u'ports', sa.Column('local_link_connection', sa.Text(),
+                                       nullable=True))
+--- ironic-5.1.2/ironic/db/sqlalchemy/models.py.orig	2016-08-05 15:01:28.309661466 -0700
++++ ironic-5.1.2/ironic/db/sqlalchemy/models.py	2016-08-05 15:01:53.899243145 -0700
[email protected]@ -48,8 +48,12 @@ db_options.set_defaults(cfg.CONF, _DEFAU
+ def table_args():
+     engine_name = urlparse.urlparse(cfg.CONF.database.connection).scheme
+     if engine_name == 'mysql':
+-        return {'mysql_engine': cfg.CONF.database.mysql_engine,
+-                'mysql_charset': "utf8"}
++        if cfg.CONF.database.mysql_storage_engine == "NDBCLUSTER":
++            return {'mysql_engine': cfg.CONF.database.mysql_storage_engine,
++                    'mysql_charset': "utf8"}
++        else:
++            return {'mysql_engine': cfg.CONF.database.mysql_engine,
++                    'mysql_charset': "utf8"}
+     return None
+ 
+ 
[email protected]@ -93,7 +97,7 @@ class Conductor(Base):
+     id = Column(Integer, primary_key=True)
+     hostname = Column(String(255), nullable=False)
+     drivers = Column(db_types.JsonEncodedList)
+-    online = Column(Boolean, default=True)
++    online = Column('online', Boolean, default=True, quote=True)
+ 
+ 
+ class Node(Base):
--- a/components/openstack/keystone/files/keystone.conf	Thu Sep 08 12:41:54 2016 -0700
+++ b/components/openstack/keystone/files/keystone.conf	Thu Sep 08 13:16:06 2016 -0600
@@ -558,6 +558,12 @@
 # (string value)
 #mysql_sql_mode = TRADITIONAL
 
+# This configures the MySQL storage engine. This allows for OpenStack to
+# support different storage engines such as InnoDB, NDB, etc. By Default,
+# this value will be set to InnoDB. For MySQL Cluster, set to NDBCLUSTER.
+# Example: mysql_storage_engine=(string value)
+#mysql_storage_engine = InnoDB
+
 # Timeout before idle SQL connections are reaped. (integer value)
 # Deprecated group/name - [DEFAULT]/sql_idle_timeout
 # Deprecated group/name - [DATABASE]/sql_idle_timeout
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openstack/keystone/patches/mysql_cluster_support.patch	Thu Sep 08 13:16:06 2016 -0600
@@ -0,0 +1,337 @@
+This patchset is for bug:
+
+24394567 Mitaka Keystone should support MySQL Cluster
+
+This fixes the following aspects of Keystone:
+1. Implementation of an oslo.db configuration parameter to specify the MySQL
+   storage engine (mysql_storage_engine).
+2. Replacement of hardcoded SQL statements that set the engine to "InnoDB"
+   to the above configuration value.
+3. Logic to handle SQL differences between MySQL InnoDB and MySQL Cluster (NDB).
+   This includes column lengths, constraints, foreign keys, and indexes.
+
+This has not been committed upstream, but has been filed in launchpad:
+
+https://bugs.launchpad.net/keystone/+bug/1564110
+
+
+--- keystone-9.1.0/keystone/common/sql/migrate_repo/versions/067_kilo.py.orig	2016-08-04 10:10:34.947867201 -0700
++++ keystone-9.1.0/keystone/common/sql/migrate_repo/versions/067_kilo.py	2016-08-04 10:16:37.481333376 -0700
[email protected]@ -12,6 +12,7 @@
+ 
+ 
+ import migrate
++from oslo_config import cfg
+ from oslo_log import log
+ import sqlalchemy as sql
+ 
[email protected]@ -21,6 +22,7 @@ from keystone.identity.mapping_backends
+ 
+ 
+ LOG = log.getLogger(__name__)
++CONF = cfg.CONF
+ 
+ 
+ def upgrade(migrate_engine):
[email protected]@ -44,7 +46,7 @@ def upgrade(migrate_engine):
+         sql.Column('blob', ks_sql.JsonBlob, nullable=False),
+         sql.Column('type', sql.String(length=255), nullable=False),
+         sql.Column('extra', ks_sql.JsonBlob.impl),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8')
+ 
+     domain = sql.Table(
[email protected]@ -53,7 +55,7 @@ def upgrade(migrate_engine):
+         sql.Column('name', sql.String(length=64), nullable=False),
+         sql.Column('enabled', sql.Boolean, default=True, nullable=False),
+         sql.Column('extra', ks_sql.JsonBlob.impl),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8')
+ 
+     endpoint = sql.Table(
[email protected]@ -67,7 +69,7 @@ def upgrade(migrate_engine):
+         sql.Column('enabled', sql.Boolean, nullable=False, default=True,
+                    server_default='1'),
+         sql.Column('region_id', sql.String(length=255), nullable=True),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8')
+ 
+     group = sql.Table(
[email protected]@ -77,7 +79,7 @@ def upgrade(migrate_engine):
+         sql.Column('name', sql.String(length=64), nullable=False),
+         sql.Column('description', sql.Text),
+         sql.Column('extra', ks_sql.JsonBlob.impl),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8')
+ 
+     policy = sql.Table(
[email protected]@ -86,7 +88,7 @@ def upgrade(migrate_engine):
+         sql.Column('type', sql.String(length=255), nullable=False),
+         sql.Column('blob', ks_sql.JsonBlob, nullable=False),
+         sql.Column('extra', ks_sql.JsonBlob.impl),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8')
+ 
+     project = sql.Table(
[email protected]@ -98,7 +100,7 @@ def upgrade(migrate_engine):
+         sql.Column('enabled', sql.Boolean),
+         sql.Column('domain_id', sql.String(length=64), nullable=False),
+         sql.Column('parent_id', sql.String(64), nullable=True),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8')
+ 
+     role = sql.Table(
[email protected]@ -106,7 +108,7 @@ def upgrade(migrate_engine):
+         sql.Column('id', sql.String(length=64), primary_key=True),
+         sql.Column('name', sql.String(length=255), nullable=False),
+         sql.Column('extra', ks_sql.JsonBlob.impl),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8')
+ 
+     service = sql.Table(
[email protected]@ -116,7 +118,7 @@ def upgrade(migrate_engine):
+         sql.Column('enabled', sql.Boolean, nullable=False, default=True,
+                    server_default='1'),
+         sql.Column('extra', ks_sql.JsonBlob.impl),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8')
+ 
+     token = sql.Table(
[email protected]@ -127,7 +129,7 @@ def upgrade(migrate_engine):
+         sql.Column('valid', sql.Boolean, default=True, nullable=False),
+         sql.Column('trust_id', sql.String(length=64)),
+         sql.Column('user_id', sql.String(length=64)),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8')
+ 
+     trust = sql.Table(
[email protected]@ -141,7 +143,7 @@ def upgrade(migrate_engine):
+         sql.Column('expires_at', sql.DateTime),
+         sql.Column('remaining_uses', sql.Integer, nullable=True),
+         sql.Column('extra', ks_sql.JsonBlob.impl),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8')
+ 
+     trust_role = sql.Table(
[email protected]@ -150,7 +152,7 @@ def upgrade(migrate_engine):
+                    nullable=False),
+         sql.Column('role_id', sql.String(length=64), primary_key=True,
+                    nullable=False),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8')
+ 
+     user = sql.Table(
[email protected]@ -162,14 +164,14 @@ def upgrade(migrate_engine):
+         sql.Column('enabled', sql.Boolean),
+         sql.Column('domain_id', sql.String(length=64), nullable=False),
+         sql.Column('default_project_id', sql.String(length=64)),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8')
+ 
+     user_group_membership = sql.Table(
+         'user_group_membership', meta,
+         sql.Column('user_id', sql.String(length=64), primary_key=True),
+         sql.Column('group_id', sql.String(length=64), primary_key=True),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8')
+ 
+     region = sql.Table(
[email protected]@ -179,7 +181,7 @@ def upgrade(migrate_engine):
+         sql.Column('description', sql.String(255), nullable=False),
+         sql.Column('parent_region_id', sql.String(255), nullable=True),
+         sql.Column('extra', sql.Text()),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8')
+ 
+     assignment = sql.Table(
[email protected]@ -197,7 +199,7 @@ def upgrade(migrate_engine):
+         sql.Column('role_id', sql.String(64), nullable=False),
+         sql.Column('inherited', sql.Boolean, default=False, nullable=False),
+         sql.PrimaryKeyConstraint('type', 'actor_id', 'target_id', 'role_id'),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8')
+ 
+     mapping = sql.Table(
[email protected]@ -211,7 +213,7 @@ def upgrade(migrate_engine):
+             mapping_backend.EntityType.GROUP,
+             name='entity_type'),
+             nullable=False),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8')
+ 
+     domain_config_whitelist = sql.Table(
[email protected]@ -221,7 +223,7 @@ def upgrade(migrate_engine):
+         sql.Column('group', sql.String(255), primary_key=True),
+         sql.Column('option', sql.String(255), primary_key=True),
+         sql.Column('value', ks_sql.JsonBlob.impl, nullable=False),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8')
+ 
+     domain_config_sensitive = sql.Table(
[email protected]@ -231,7 +233,7 @@ def upgrade(migrate_engine):
+         sql.Column('group', sql.String(255), primary_key=True),
+         sql.Column('option', sql.String(255), primary_key=True),
+         sql.Column('value', ks_sql.JsonBlob.impl, nullable=False),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8')
+ 
+     # create all tables
+--- keystone-9.1.0/keystone/common/sql/migrate_repo/versions/073_insert_assignment_inherited_pk.py.orig	2016-08-04 10:10:40.619013471 -0700
++++ keystone-9.1.0/keystone/common/sql/migrate_repo/versions/073_insert_assignment_inherited_pk.py	2016-08-04 10:18:06.349087340 -0700
[email protected]@ -11,11 +11,13 @@
+ # under the License.
+ 
+ import migrate
++from oslo_config import cfg
+ import sqlalchemy as sql
+ from sqlalchemy.orm import sessionmaker
+ 
+ from keystone.assignment.backends import sql as assignment_sql
+ 
++CONF = cfg.CONF
+ 
+ def upgrade(migrate_engine):
+     """Inserts inherited column to assignment table PK constraints.
[email protected]@ -57,7 +59,7 @@ def upgrade(migrate_engine):
+                        nullable=False),
+             sql.PrimaryKeyConstraint('type', 'actor_id', 'target_id',
+                                      'role_id', 'inherited'),
+-            mysql_engine='InnoDB',
++            mysql_engine=CONF.database.mysql_storage_engine,
+             mysql_charset='utf8')
+ 
+         # Create the new assignment table
+--- keystone-9.1.0/keystone/common/sql/migrate_repo/versions/075_confirm_config_registration.py.orig	2016-08-04 10:10:47.746776861 -0700
++++ keystone-9.1.0/keystone/common/sql/migrate_repo/versions/075_confirm_config_registration.py	2016-08-04 10:19:03.802564813 -0700
[email protected]@ -10,9 +10,11 @@
+ #    License for the specific language governing permissions and limitations
+ #    under the License.
+ 
++from oslo_config import cfg
+ import sqlalchemy as sql
+ 
+ REGISTRATION_TABLE = 'config_register'
++CONF = cfg.CONF
+ 
+ 
+ def upgrade(migrate_engine):
[email protected]@ -24,6 +26,6 @@ def upgrade(migrate_engine):
+         meta,
+         sql.Column('type', sql.String(64), primary_key=True),
+         sql.Column('domain_id', sql.String(64), nullable=False),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8')
+     registration_table.create(migrate_engine, checkfirst=True)
+--- keystone-9.1.0/keystone/common/sql/migrate_repo/versions/081_add_endpoint_policy_table.py.orig	2016-08-04 10:10:53.258335033 -0700
++++ keystone-9.1.0/keystone/common/sql/migrate_repo/versions/081_add_endpoint_policy_table.py	2016-08-04 10:20:06.575865756 -0700
[email protected]@ -12,10 +12,12 @@
+ # License for the specific language governing permissions and limitations
+ # under the License.
+ 
++from oslo_config import cfg
+ import sqlalchemy as sql
+ 
+ from keystone.common.sql import migration_helpers
+ 
++CONF = cfg.CONF
+ 
+ def upgrade(migrate_engine):
+     try:
[email protected]@ -48,7 +50,7 @@ def upgrade(migrate_engine):
+         sql.Column('region_id', sql.String(64),
+                    nullable=True),
+         sql.UniqueConstraint('endpoint_id', 'service_id', 'region_id'),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8')
+ 
+     endpoint_policy_table.create(migrate_engine, checkfirst=True)
+--- keystone-9.1.0/keystone/common/sql/migrate_repo/versions/082_add_federation_tables.py.orig	2016-08-04 10:10:59.098720956 -0700
++++ keystone-9.1.0/keystone/common/sql/migrate_repo/versions/082_add_federation_tables.py	2016-08-04 10:22:41.994100588 -0700
[email protected]@ -45,7 +45,7 @@ def upgrade(migrate_engine):
+         sql.Column('id', sql.String(64), primary_key=True),
+         sql.Column('enabled', sql.Boolean, nullable=False),
+         sql.Column('description', sql.Text(), nullable=True),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8')
+     idp_table.create(migrate_engine, checkfirst=True)
+ 
[email protected]@ -57,7 +57,7 @@ def upgrade(migrate_engine):
+                    sql.ForeignKey('identity_provider.id', ondelete='CASCADE'),
+                    primary_key=True),
+         sql.Column('mapping_id', sql.String(64), nullable=False),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8')
+     federation_protocol_table.create(migrate_engine, checkfirst=True)
+ 
[email protected]@ -66,7 +66,7 @@ def upgrade(migrate_engine):
+         meta,
+         sql.Column('id', sql.String(64), primary_key=True),
+         sql.Column('rules', sql.Text(), nullable=False),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8')
+     mapping_table.create(migrate_engine, checkfirst=True)
+ 
[email protected]@ -81,7 +81,7 @@ def upgrade(migrate_engine):
+         sql.Column('sp_url', sql.String(256), nullable=False),
+         sql.Column(_RELAY_STATE_PREFIX, sql.String(256), nullable=False,
+                    server_default=relay_state_prefix_default),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8')
+     sp_table.create(migrate_engine, checkfirst=True)
+ 
[email protected]@ -92,6 +92,6 @@ def upgrade(migrate_engine):
+         sql.Column('idp_id', sql.String(64),
+                    sql.ForeignKey('identity_provider.id', ondelete='CASCADE')),
+         sql.Column('remote_id', sql.String(255), primary_key=True),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8')
+     remote_id_table.create(migrate_engine, checkfirst=True)
+--- keystone-9.1.0/keystone/common/sql/migrate_repo/versions/087_implied_roles.py.orig	2016-08-04 10:11:04.322293468 -0700
++++ keystone-9.1.0/keystone/common/sql/migrate_repo/versions/087_implied_roles.py	2016-08-04 10:23:34.919749416 -0700
[email protected]@ -12,10 +12,12 @@
+ 
+ 
+ import migrate
++from oslo_config import cfg
+ import sqlalchemy as sql
+ 
+ 
+ ROLE_TABLE = 'role'
++CONF = cfg.CONF
+ 
+ 
+ def upgrade(migrate_engine):
[email protected]@ -27,7 +29,7 @@ def upgrade(migrate_engine):
+         sql.Column('prior_role_id', sql.String(length=64), primary_key=True),
+         sql.Column(
+             'implied_role_id', sql.String(length=64), primary_key=True),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8')
+     implied_role.create()
+     role = sql.Table(ROLE_TABLE, meta, autoload=True)
--- a/components/openstack/neutron/files/neutron.conf	Thu Sep 08 12:41:54 2016 -0700
+++ b/components/openstack/neutron/files/neutron.conf	Thu Sep 08 13:16:06 2016 -0600
@@ -708,6 +708,12 @@
 # (string value)
 #mysql_sql_mode = TRADITIONAL
 
+# This configures the MySQL storage engine. This allows for OpenStack to
+# support different storage engines such as InnoDB, NDB, etc. By Default,
+# this value will be set to InnoDB. For MySQL Cluster, set to NDBCLUSTER.
+# Example: mysql_storage_engine=(string value)
+#mysql_storage_engine = InnoDB
+
 # Timeout before idle SQL connections are reaped. (integer value)
 # Deprecated group/name - [DEFAULT]/sql_idle_timeout
 # Deprecated group/name - [DATABASE]/sql_idle_timeout
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openstack/neutron/patches/11-mysql_cluster_support.patch	Thu Sep 08 13:16:06 2016 -0600
@@ -0,0 +1,630 @@
+This patchset is for bug:
+
+24394574 Mitaka Neutron should support MySQL Cluster
+
+This fixes the following aspects of Neutron:
+1. Implementation of an oslo.db configuration parameter to specify the MySQL
+   storage engine (mysql_storage_engine).
+2. Replacement of hardcoded SQL statements that set the engine to "InnoDB"
+   to the above configuration value.
+3. Logic to handle SQL differences between MySQL InnoDB and MySQL Cluster (NDB).
+
+This has not been committed upstream, but has been filed in launchpad:
+
+https://bugs.launchpad.net/neutron/+bug/1564110
+
+
+--- neutron-8.1.2/neutron/db/agents_db.py.orig	2016-08-25 14:48:36.647874143 -0700
++++ neutron-8.1.2/neutron/db/agents_db.py	2016-08-25 15:25:50.307210666 -0700
[email protected]@ -73,6 +73,7 @@ AGENT_OPTS = [
+                        "agent until admin changes admin_state_up to True.")),
+ ]
+ cfg.CONF.register_opts(AGENT_OPTS)
++CONF = cfg.CONF
+ 
+ # this is the ratio from agent_down_time to the time we use to consider
+ # the agents down for considering their resource versions in the
[email protected]@ -90,10 +91,17 @@ class Agent(model_base.BASEV2, model_bas
+     )
+ 
+     # L3 agent, DHCP agent, OVS agent, LinuxBridge
+-    agent_type = sa.Column(sa.String(255), nullable=False)
+-    binary = sa.Column(sa.String(255), nullable=False)
+-    # TOPIC is a fanout exchange topic
+-    topic = sa.Column(sa.String(255), nullable=False)
++    if CONF.database.mysql_storage_engine == 'NDBCLUSTER':
++        agent_type = sa.Column(sa.String(128), nullable=False)
++        binary = sa.Column(sa.String(128), nullable=False)
++        # TOPIC is a fanout exchange topic
++        topic = sa.Column(sa.String(128), nullable=False)
++    else:
++        agent_type = sa.Column(sa.String(255), nullable=False)
++        binary = sa.Column(sa.String(255), nullable=False)
++        # TOPIC is a fanout exchange topic
++        topic = sa.Column(sa.String(255), nullable=False)
++
+     # TOPIC.host is a target topic
+     host = sa.Column(sa.String(255), nullable=False)
+     availability_zone = sa.Column(sa.String(255))
[email protected]@ -108,11 +116,17 @@ class Agent(model_base.BASEV2, model_bas
+     # description is note for admin user
+     description = sa.Column(sa.String(attributes.DESCRIPTION_MAX_LEN))
+     # configurations: a json dict string, I think 4095 is enough
+-    configurations = sa.Column(sa.String(4095), nullable=False)
++    if CONF.database.mysql_storage_engine == 'NDBCLUSTER':
++        configurations = sa.Column(sa.Text(), nullable=False)
++    else:
++        configurations = sa.Column(sa.String(4095), nullable=False)
+     # resource_versions: json dict, 8191 allows for ~256 resource versions
+     #                    assuming ~32byte length "'name': 'ver',"
+     #                    the whole row limit is 65535 bytes in mysql
+-    resource_versions = sa.Column(sa.String(8191))
++    if CONF.database.mysql_storage_engine == 'NDBCLUSTER':
++        resource_versions = sa.Column(sa.Text())
++    else:
++        resource_versions = sa.Column(sa.String(8191))
+     # load - number of resources hosted by the agent
+     load = sa.Column(sa.Integer, server_default='0', nullable=False)
+ 
+--- neutron-8.1.2/neutron/db/api.py.orig	2016-08-25 14:46:46.198992228 -0700
++++ neutron-8.1.2/neutron/db/api.py	2016-08-25 14:43:31.067614686 -0700
[email protected]@ -37,6 +37,8 @@ retry_db_errors = oslo_db_api.wrap_db_re
+     exception_checker=is_deadlock
+ )
+ 
++CONF = cfg.CONF
++
+ 
+ @contextlib.contextmanager
+ def exc_to_retry(exceptions):
[email protected]@ -81,12 +83,22 @@ def get_session(autocommit=True, expire_
+ @contextlib.contextmanager
+ def autonested_transaction(sess):
+     """This is a convenience method to not bother with 'nested' parameter."""
+-    if sess.is_active:
+-        session_context = sess.begin(nested=True)
++    # MySQL Cluster does not support nested operations.
++    if CONF.database.mysql_storage_engine == "NDBCLUSTER":
++        try:
++            session_context = sess.begin(subtransactions=True, nested=False)
++        except exc.InvalidRequestError:
++            session_context = sess.begin(subtransactions=False, nested=False)
++        finally:
++            with session_context as tx:
++                yield tx
+     else:
+-        session_context = sess.begin(subtransactions=True)
+-    with session_context as tx:
+-        yield tx
++        if sess.is_active:
++            session_context = sess.begin(nested=True)
++        else:
++            session_context = sess.begin(subtransactions=True)
++        with session_context as tx:
++            yield tx
+ 
+ 
+ # Common database operation implementations
+--- neutron-8.1.2/neutron/db/flavors_db.py.orig	2016-08-25 15:01:44.265969231 -0700
++++ neutron-8.1.2/neutron/db/flavors_db.py	2016-08-25 15:03:44.220882633 -0700
[email protected]@ -12,6 +12,7 @@
+ #    License for the specific language governing permissions and limitations
+ #    under the License.
+ 
++from oslo_config import cfg
+ from oslo_log import log as logging
+ from oslo_utils import uuidutils
+ import sqlalchemy as sa
[email protected]@ -26,6 +27,8 @@ from neutron.extensions import flavors a
+ 
+ LOG = logging.getLogger(__name__)
+ 
++CONF = cfg.CONF
++
+ 
+ class Flavor(model_base.BASEV2, model_base.HasId):
+     name = sa.Column(sa.String(attr.NAME_MAX_LEN))
[email protected]@ -43,7 +46,10 @@ class ServiceProfile(model_base.BASEV2,
+     driver = sa.Column(sa.String(1024), nullable=False)
+     enabled = sa.Column(sa.Boolean, nullable=False, default=True,
+                         server_default=sa.sql.true())
+-    metainfo = sa.Column(sa.String(4096))
++    if CONF.database.mysql_storage_engine == 'NDBCLUSTER':
++        metainfo = sa.Column(sa.Text())
++    else:
++        metainfo = sa.Column(sa.String(4096))
+     flavors = orm.relationship("FlavorServiceProfileBinding")
+ 
+ 
+--- neutron-8.1.2/neutron/db/migration/alembic_migrations/agent_init_ops.py.orig	2016-08-25 14:46:52.869757151 -0700
++++ neutron-8.1.2/neutron/db/migration/alembic_migrations/agent_init_ops.py	2016-08-25 14:43:31.069947973 -0700
[email protected]@ -18,26 +18,53 @@
+ # in the modules for relevant resources
+ 
+ 
++from alembic import context
+ from alembic import op
+ import sqlalchemy as sa
+ 
++config = context.config
++CONF = config.neutron_config
++
+ 
+ def upgrade():
+-    op.create_table(
+-        'agents',
+-        sa.Column('id', sa.String(length=36), nullable=False),
+-        sa.Column('agent_type', sa.String(length=255), nullable=False),
+-        sa.Column('binary', sa.String(length=255), nullable=False),
+-        sa.Column('topic', sa.String(length=255), nullable=False),
+-        sa.Column('host', sa.String(length=255), nullable=False),
+-        sa.Column('admin_state_up', sa.Boolean(), nullable=False,
+-                  server_default=sa.sql.true()),
+-        sa.Column('created_at', sa.DateTime(), nullable=False),
+-        sa.Column('started_at', sa.DateTime(), nullable=False),
+-        sa.Column('heartbeat_timestamp', sa.DateTime(), nullable=False),
+-        sa.Column('description', sa.String(length=255), nullable=True),
+-        sa.Column('configurations', sa.String(length=4095), nullable=False),
+-        sa.Column('load', sa.Integer(), server_default='0', nullable=False),
+-        sa.PrimaryKeyConstraint('id'),
+-        sa.UniqueConstraint('agent_type', 'host',
+-                            name='uniq_agents0agent_type0host'))
++    # MySQL Cluster limits rows to 14,000 characters. This adjusts the
++    # columns to fit within that limit. Excessively large columns are
++    # converted to TEXT.
++    if CONF.database.mysql_storage_engine == "NDBCLUSTER":
++        op.create_table(
++            'agents',
++            sa.Column('id', sa.String(length=36), nullable=False),
++            sa.Column('agent_type', sa.String(length=128), nullable=False),
++            sa.Column('binary', sa.String(length=128), nullable=False),
++            sa.Column('topic', sa.String(length=128), nullable=False),
++            sa.Column('host', sa.String(length=255), nullable=False),
++            sa.Column('admin_state_up', sa.Boolean(), nullable=False,
++                      server_default=sa.sql.true()),
++            sa.Column('created_at', sa.DateTime(), nullable=False),
++            sa.Column('started_at', sa.DateTime(), nullable=False),
++            sa.Column('heartbeat_timestamp', sa.DateTime(), nullable=False),
++            sa.Column('description', sa.String(length=255), nullable=True),
++            sa.Column('configurations', sa.Text(), nullable=False),
++            sa.Column('load', sa.Integer(), server_default='0', nullable=False),
++            sa.PrimaryKeyConstraint('id'),
++            sa.UniqueConstraint('agent_type', 'host',
++                                name='uniq_agents0agent_type0host'))
++    else:
++        op.create_table(
++            'agents',
++            sa.Column('id', sa.String(length=36), nullable=False),
++            sa.Column('agent_type', sa.String(length=255), nullable=False),
++            sa.Column('binary', sa.String(length=255), nullable=False),
++            sa.Column('topic', sa.String(length=255), nullable=False),
++            sa.Column('host', sa.String(length=255), nullable=False),
++            sa.Column('admin_state_up', sa.Boolean(), nullable=False,
++                      server_default=sa.sql.true()),
++            sa.Column('created_at', sa.DateTime(), nullable=False),
++            sa.Column('started_at', sa.DateTime(), nullable=False),
++            sa.Column('heartbeat_timestamp', sa.DateTime(), nullable=False),
++            sa.Column('description', sa.String(length=255), nullable=True),
++            sa.Column('configurations', sa.String(length=4095), nullable=False),
++            sa.Column('load', sa.Integer(), server_default='0', nullable=False),
++            sa.PrimaryKeyConstraint('id'),
++            sa.UniqueConstraint('agent_type', 'host',
++                                name='uniq_agents0agent_type0host'))
+--- neutron-8.1.2/neutron/db/migration/alembic_migrations/dvr_init_opts.py.orig	2016-08-25 14:46:59.635718601 -0700
++++ neutron-8.1.2/neutron/db/migration/alembic_migrations/dvr_init_opts.py	2016-08-25 14:43:31.071309141 -0700
[email protected]@ -15,9 +15,13 @@
+ 
+ # Initial operations for dvr
+ 
++from alembic import context
+ from alembic import op
+ import sqlalchemy as sa
+ 
++config = context.config
++CONF = config.neutron_config
++
+ 
+ def upgrade():
+     op.create_table(
[email protected]@ -27,23 +31,43 @@ def upgrade():
+                   nullable=False, unique=True),
+         sa.PrimaryKeyConstraint('host')
+     )
+-    op.create_table(
+-        'ml2_dvr_port_bindings',
+-        sa.Column('port_id', sa.String(length=36), nullable=False),
+-        sa.Column('host', sa.String(length=255), nullable=False),
+-        sa.Column('router_id', sa.String(length=36), nullable=True),
+-        sa.Column('vif_type', sa.String(length=64), nullable=False),
+-        sa.Column('vif_details', sa.String(length=4095),
+-                  nullable=False, server_default=''),
+-        sa.Column('vnic_type', sa.String(length=64),
+-                  nullable=False, server_default='normal'),
+-        sa.Column('profile', sa.String(length=4095),
+-                  nullable=False, server_default=''),
+-        sa.Column(u'status', sa.String(16), nullable=False),
+-        sa.ForeignKeyConstraint(['port_id'], ['ports.id'],
+-                                ondelete='CASCADE'),
+-        sa.PrimaryKeyConstraint('port_id', 'host')
+-    )
++    # MySQL Cluster limits rows to 14,000 characters. This adjusts the
++    # columns to fit within that limit. Excessively large columns are
++    # converted to TEXT.
++    if CONF.database.mysql_storage_engine == "NDBCLUSTER":
++        op.create_table(
++            'ml2_dvr_port_bindings',
++            sa.Column('port_id', sa.String(length=36), nullable=False),
++            sa.Column('host', sa.String(length=255), nullable=False),
++            sa.Column('router_id', sa.String(length=36), nullable=True),
++            sa.Column('vif_type', sa.String(length=64), nullable=False),
++            sa.Column('vif_details', sa.Text(), nullable=False),
++            sa.Column('vnic_type', sa.String(length=64),
++                      nullable=False, server_default='normal'),
++            sa.Column('profile', sa.Text(), nullable=False),
++            sa.Column(u'status', sa.String(16), nullable=False),
++            sa.ForeignKeyConstraint(['port_id'], ['ports.id'],
++                                    ondelete='CASCADE'),
++            sa.PrimaryKeyConstraint('port_id', 'host')
++        )
++    else:
++        op.create_table(
++            'ml2_dvr_port_bindings',
++            sa.Column('port_id', sa.String(length=36), nullable=False),
++            sa.Column('host', sa.String(length=255), nullable=False),
++            sa.Column('router_id', sa.String(length=36), nullable=True),
++            sa.Column('vif_type', sa.String(length=64), nullable=False),
++            sa.Column('vif_details', sa.String(length=4095),
++                      nullable=False, server_default=''),
++            sa.Column('vnic_type', sa.String(length=64),
++                      nullable=False, server_default='normal'),
++            sa.Column('profile', sa.String(length=4095),
++                      nullable=False, server_default=''),
++            sa.Column(u'status', sa.String(16), nullable=False),
++            sa.ForeignKeyConstraint(['port_id'], ['ports.id'],
++                                    ondelete='CASCADE'),
++            sa.PrimaryKeyConstraint('port_id', 'host')
++        )
+     op.create_table(
+         'csnat_l3_agent_bindings',
+         sa.Column('router_id', sa.String(length=36), nullable=False),
+--- neutron-8.1.2/neutron/db/migration/alembic_migrations/env.py.orig	2016-08-25 14:47:07.700975613 -0700
++++ neutron-8.1.2/neutron/db/migration/alembic_migrations/env.py	2016-08-25 14:43:31.072607136 -0700
[email protected]@ -34,6 +34,7 @@ except ImportError:
+ 
+ 
+ MYSQL_ENGINE = None
++MYSQL_STORAGE_ENGINE = None
+ 
+ # this is the Alembic Config object, which provides
+ # access to the values within the .ini file in use.
[email protected]@ -59,6 +60,16 @@ def set_mysql_engine():
+                     model_base.BASEV2.__table_args__['mysql_engine'])
+ 
+ 
++def set_mysql_storage_engine():
++    try:
++        mysql_storage_engine = neutron_config.database.mysql_storage_engine
++    except cfg.NoSuchOptError:
++        mysql_storage_engine = InnoDB
++
++    global MYSQL_STORAGE_ENGINE
++    MYSQL_STORAGE_ENGINE = mysql_storage_engine
++
++
+ def include_object(object_, name, type_, reflected, compare_to):
+     if type_ == 'table' and name in external.TABLES:
+         return False
[email protected]@ -81,6 +92,7 @@ def run_migrations_offline():
+ 
+     """
+     set_mysql_engine()
++    set_mysql_storage_engine()
+ 
+     kwargs = dict()
+     if neutron_config.database.connection:
[email protected]@ -108,6 +120,7 @@ def run_migrations_online():
+ 
+     """
+     set_mysql_engine()
++    set_mysql_storage_engine()
+     connection = config.attributes.get('connection')
+     with DBConnection(neutron_config.database.connection, connection) as conn:
+         context.configure(
+--- neutron-8.1.2/neutron/db/migration/alembic_migrations/ml2_init_ops.py.orig	2016-08-25 14:47:14.579387371 -0700
++++ neutron-8.1.2/neutron/db/migration/alembic_migrations/ml2_init_ops.py	2016-08-25 14:43:31.074019618 -0700
[email protected]@ -16,9 +16,13 @@
+ # Initial operations for ML2 plugin and drivers
+ 
+ 
++from alembic import context
+ from alembic import op
+ import sqlalchemy as sa
+ 
++config = context.config
++CONF = config.neutron_config
++
+ 
+ def upgrade():
+     op.create_table(
[email protected]@ -83,21 +87,39 @@ def upgrade():
+                                 ondelete='CASCADE'),
+         sa.PrimaryKeyConstraint('id'))
+ 
+-    op.create_table(
+-        'ml2_port_bindings',
+-        sa.Column('port_id', sa.String(length=36), nullable=False),
+-        sa.Column('host', sa.String(length=255), nullable=False,
+-                  server_default=''),
+-        sa.Column('vif_type', sa.String(length=64), nullable=False),
+-        sa.Column('vnic_type', sa.String(length=64), nullable=False,
+-                  server_default='normal'),
+-        sa.Column('profile', sa.String(length=4095), nullable=False,
+-                  server_default=''),
+-        sa.Column('vif_details', sa.String(length=4095), nullable=False,
+-                  server_default=''),
+-        sa.ForeignKeyConstraint(['port_id'], ['ports.id'],
+-                                ondelete='CASCADE'),
+-        sa.PrimaryKeyConstraint('port_id'))
++    # MySQL Cluster limits rows to 14,000 characters. This adjusts the
++    # columns to fit within that limit. Excessively large columns are
++    # converted to TEXT.
++    if CONF.database.mysql_storage_engine == "NDBCLUSTER":
++        op.create_table(
++            'ml2_port_bindings',
++            sa.Column('port_id', sa.String(length=36), nullable=False),
++            sa.Column('host', sa.String(length=255), nullable=False,
++                      server_default=''),
++            sa.Column('vif_type', sa.String(length=64), nullable=False),
++            sa.Column('vnic_type', sa.String(length=64), nullable=False,
++                      server_default='normal'),
++            sa.Column('profile', sa.Text(), nullable=False),
++            sa.Column('vif_details', sa.Text(), nullable=False),
++            sa.ForeignKeyConstraint(['port_id'], ['ports.id'],
++                                    ondelete='CASCADE'),
++            sa.PrimaryKeyConstraint('port_id'))
++    else:
++        op.create_table(
++            'ml2_port_bindings',
++            sa.Column('port_id', sa.String(length=36), nullable=False),
++            sa.Column('host', sa.String(length=255), nullable=False,
++                      server_default=''),
++            sa.Column('vif_type', sa.String(length=64), nullable=False),
++            sa.Column('vnic_type', sa.String(length=64), nullable=False,
++                      server_default='normal'),
++            sa.Column('profile', sa.String(length=4095), nullable=False,
++                      server_default=''),
++            sa.Column('vif_details', sa.String(length=4095), nullable=False,
++                      server_default=''),
++            sa.ForeignKeyConstraint(['port_id'], ['ports.id'],
++                                    ondelete='CASCADE'),
++            sa.PrimaryKeyConstraint('port_id'))
+ 
+     op.create_table(
+         'ml2_port_binding_levels',
+--- neutron-8.1.2/neutron/db/migration/alembic_migrations/versions/liberty/expand/31337ec0ffee_flavors.py.orig	2016-08-25 14:47:21.341890981 -0700
++++ neutron-8.1.2/neutron/db/migration/alembic_migrations/versions/liberty/expand/31337ec0ffee_flavors.py	2016-08-25 14:43:31.076633173 -0700
[email protected]@ -24,9 +24,13 @@ Create Date: 2014-07-17 03:00:00.00
+ revision = '313373c0ffee'
+ down_revision = '52c5312f6baf'
+ 
++from alembic import context
+ from alembic import op
+ import sqlalchemy as sa
+ 
++config = context.config
++CONF = config.neutron_config
++
+ 
+ def upgrade():
+     op.create_table(
[email protected]@ -40,16 +44,31 @@ def upgrade():
+         sa.PrimaryKeyConstraint('id')
+     )
+ 
+-    op.create_table(
+-        'serviceprofiles',
+-        sa.Column('id', sa.String(36)),
+-        sa.Column('description', sa.String(1024)),
+-        sa.Column('driver', sa.String(1024), nullable=False),
+-        sa.Column('enabled', sa.Boolean, nullable=False,
+-                  server_default=sa.sql.true()),
+-        sa.Column('metainfo', sa.String(4096)),
+-        sa.PrimaryKeyConstraint('id')
+-    )
++    # MySQL Cluster limits rows to 14,000 characters. This adjusts the
++    # columns to fit within that limit. Excessively large columns are
++    # converted to TEXT.
++    if CONF.database.mysql_storage_engine == "NDBCLUSTER":
++        op.create_table(
++            'serviceprofiles',
++            sa.Column('id', sa.String(36)),
++            sa.Column('description', sa.String(1024)),
++            sa.Column('driver', sa.String(1024), nullable=False),
++            sa.Column('enabled', sa.Boolean, nullable=False,
++                      server_default=sa.sql.true()),
++            sa.Column('metainfo', sa.Text()),
++            sa.PrimaryKeyConstraint('id')
++        )
++    else:
++        op.create_table(
++            'serviceprofiles',
++            sa.Column('id', sa.String(36)),
++            sa.Column('description', sa.String(1024)),
++            sa.Column('driver', sa.String(1024), nullable=False),
++            sa.Column('enabled', sa.Boolean, nullable=False,
++                      server_default=sa.sql.true()),
++            sa.Column('metainfo', sa.String(4096)),
++            sa.PrimaryKeyConstraint('id')
++        )
+ 
+     op.create_table(
+         'flavorserviceprofilebindings',
+--- neutron-8.1.2/neutron/db/migration/alembic_migrations/versions/mitaka/expand/31ed664953e6_add_resource_versions_row_to_agent_table.py.orig	2016-08-25 14:47:28.407786618 -0700
++++ neutron-8.1.2/neutron/db/migration/alembic_migrations/versions/mitaka/expand/31ed664953e6_add_resource_versions_row_to_agent_table.py	2016-08-25 14:43:31.079354015 -0700
[email protected]@ -25,10 +25,21 @@ Create Date: 2016-01-15 13:41:30.016915
+ revision = '31ed664953e6'
+ down_revision = '15e43b934f81'
+ 
++from alembic import context
+ from alembic import op
+ import sqlalchemy as sa
+ 
++config = context.config
++CONF = config.neutron_config
++
+ 
+ def upgrade():
+-    op.add_column('agents',
+-                  sa.Column('resource_versions', sa.String(length=8191)))
++    # MySQL Cluster limits rows to 14,000 characters. This adjusts the
++    # columns to fit within that limit. Excessively large columns are
++    # converted to TEXT.
++    if CONF.database.mysql_storage_engine == "NDBCLUSTER":
++        op.add_column('agents',
++                      sa.Column('resource_versions', sa.Text()))
++    else:
++        op.add_column('agents',
++                      sa.Column('resource_versions', sa.String(length=8191)))
+--- neutron-8.1.2/neutron/db/migration/cli.py.orig	2016-08-25 14:47:38.390828076 -0700
++++ neutron-8.1.2/neutron/db/migration/cli.py	2016-08-25 14:43:31.081266845 -0700
[email protected]@ -103,6 +103,9 @@ _db_opts = [
+                default='',
+                help=_('Database engine for which script will be generated '
+                       'when using offline migration.')),
++    cfg.StrOpt('mysql_storage_engine',
++               default='InnoDB',
++               help=_('MySQL Storage Engine')),
+ ]
+ 
+ CONF = cfg.ConfigOpts()
+--- neutron-8.1.2/neutron/db/model_base.py.orig	2016-08-25 14:47:46.388723853 -0700
++++ neutron-8.1.2/neutron/db/model_base.py	2016-08-25 14:43:31.082371798 -0700
[email protected]@ -13,6 +13,8 @@
+ # See the License for the specific language governing permissions and
+ # limitations under the License.
+ 
++from alembic import context
++from oslo_config import cfg
+ from oslo_db.sqlalchemy import models
+ from oslo_utils import uuidutils
+ import sqlalchemy as sa
[email protected]@ -22,6 +24,15 @@ from sqlalchemy import orm
+ 
+ from neutron.api.v2 import attributes as attr
+ 
++# Attempt to determine the context that this module is being used.
++# If via neutron-db-manage and alembic, use alembic context. If not,
++# use oslo_config.
++try:
++    config = context.config
++    CONF = config.neutron_config
++except AttributeError:
++    CONF = cfg.CONF
++
+ 
+ class HasTenant(object):
+     """Tenant mixin, add to subclasses that have a tenant."""
[email protected]@ -48,7 +59,7 @@ class HasStatusDescription(object):
+ class NeutronBase(models.ModelBase):
+     """Base class for Neutron Models."""
+ 
+-    __table_args__ = {'mysql_engine': 'InnoDB'}
++    __table_args__ = {'mysql_engine': CONF.database.mysql_storage_engine}
+ 
+     def __iter__(self):
+         self._i = iter(orm.object_mapper(self).columns)
+--- neutron-8.1.2/neutron/plugins/ml2/models.py.orig	2016-08-25 15:03:58.107501050 -0700
++++ neutron-8.1.2/neutron/plugins/ml2/models.py	2016-08-25 15:22:24.556984578 -0700
[email protected]@ -13,6 +13,7 @@
+ #    License for the specific language governing permissions and limitations
+ #    under the License.
+ 
++from oslo_config import cfg
+ import sqlalchemy as sa
+ from sqlalchemy import orm
+ 
[email protected]@ -22,6 +23,8 @@ from neutron.extensions import portbindi
+ 
+ BINDING_PROFILE_LEN = 4095
+ 
++CONF = cfg.CONF
++
+ 
+ class NetworkSegment(model_base.BASEV2, model_base.HasId):
+     """Represent persistent state of a network segment.
[email protected]@ -63,11 +66,22 @@ class PortBinding(model_base.BASEV2):
+     vnic_type = sa.Column(sa.String(64), nullable=False,
+                           default=portbindings.VNIC_NORMAL,
+                           server_default=portbindings.VNIC_NORMAL)
+-    profile = sa.Column(sa.String(BINDING_PROFILE_LEN), nullable=False,
+-                        default='', server_default='')
++
++    if CONF.database.mysql_storage_engine == 'NDBCLUSTER':
++        profile = sa.Column(sa.Text(), nullable=False,
++                            default='', server_default='')
++    else:
++        profile = sa.Column(sa.String(BINDING_PROFILE_LEN), nullable=False,
++                            default='', server_default='')
++
+     vif_type = sa.Column(sa.String(64), nullable=False)
+-    vif_details = sa.Column(sa.String(4095), nullable=False, default='',
+-                            server_default='')
++
++    if CONF.database.mysql_storage_engine == 'NDBCLUSTER':
++        vif_details = sa.Column(sa.Text(), nullable=False, default='',
++                                server_default='')
++    else:
++        vif_details = sa.Column(sa.String(4095), nullable=False, default='',
++                                server_default='')
+ 
+     # Add a relationship to the Port model in order to instruct SQLAlchemy to
+     # eagerly load port bindings
[email protected]@ -113,13 +127,25 @@ class DVRPortBinding(model_base.BASEV2):
+     host = sa.Column(sa.String(255), nullable=False, primary_key=True)
+     router_id = sa.Column(sa.String(36), nullable=True)
+     vif_type = sa.Column(sa.String(64), nullable=False)
+-    vif_details = sa.Column(sa.String(4095), nullable=False, default='',
+-                            server_default='')
++
++    if CONF.database.mysql_storage_engine == 'NDBCLUSTER':
++        vif_details = sa.Column(sa.Text(), nullable=False, default='',
++                                server_default='')
++    else:
++        vif_details = sa.Column(sa.String(4095), nullable=False, default='',
++                                server_default='')
++
+     vnic_type = sa.Column(sa.String(64), nullable=False,
+                           default=portbindings.VNIC_NORMAL,
+                           server_default=portbindings.VNIC_NORMAL)
+-    profile = sa.Column(sa.String(BINDING_PROFILE_LEN), nullable=False,
+-                        default='', server_default='')
++
++    if CONF.database.mysql_storage_engine == 'NDBCLUSTER':
++        profile = sa.Column(sa.Text(), nullable=False,
++                            default='', server_default='')
++    else:
++        profile = sa.Column(sa.String(BINDING_PROFILE_LEN), nullable=False,
++                            default='', server_default='')
++
+     status = sa.Column(sa.String(16), nullable=False)
+ 
+     # Add a relationship to the Port model in order to instruct SQLAlchemy to
+--- neutron-8.1.2/neutron/tests/functional/db/test_migrations.py.orig	2016-08-25 14:47:53.221640215 -0700
++++ neutron-8.1.2/neutron/tests/functional/db/test_migrations.py	2016-08-25 14:43:31.084056418 -0700
[email protected]@ -30,6 +30,7 @@ from neutron.db.migration.models import
+ from neutron.tests.common import base
+ 
+ cfg.CONF.import_opt('core_plugin', 'neutron.common.config')
++CONF = cfg.CONF
+ 
+ CORE_PLUGIN = 'neutron.plugins.ml2.plugin.Ml2Plugin'
+ 
[email protected]@ -273,7 +274,7 @@ class TestModelsMigrationsMysql(_TestMod
+             self.assertTrue(len(tables) > 0,
+                             "No tables found. Wrong schema?")
+             res = [table for table in tables if
+-                   insp.get_table_options(table)['mysql_engine'] != 'InnoDB'
++                   insp.get_table_options(table)['mysql_engine'] != CONF.database.mysql_storage_engine
+                    and table != 'alembic_version']
+             self.assertEqual(0, len(res), "%s non InnoDB tables created" % res)
+ 
--- a/components/openstack/nova/files/nova.conf	Thu Sep 08 12:41:54 2016 -0700
+++ b/components/openstack/nova/files/nova.conf	Thu Sep 08 13:16:06 2016 -0600
@@ -2181,6 +2181,12 @@
 # (string value)
 #mysql_sql_mode = TRADITIONAL
 
+# This configures the MySQL storage engine. This allows for OpenStack to
+# support different storage engines such as InnoDB, NDB, etc. By Default,
+# this value will be set to InnoDB. For MySQL Cluster, set to NDBCLUSTER.
+# Example: mysql_storage_engine=(string value)
+#mysql_storage_engine = InnoDB
+
 # Timeout before idle SQL connections are reaped. (integer value)
 #idle_timeout = 3600
 
@@ -3159,6 +3165,12 @@
 # (string value)
 #mysql_sql_mode = TRADITIONAL
 
+# This configures the MySQL storage engine. This allows for OpenStack to
+# support different storage engines such as InnoDB, NDB, etc. By Default,
+# this value will be set to InnoDB. For MySQL Cluster, set to NDBCLUSTER.
+# Example: mysql_storage_engine=(string value)
+#mysql_storage_engine = InnoDB
+
 # Timeout before idle SQL connections are reaped. (integer value)
 # Deprecated group/name - [DEFAULT]/sql_idle_timeout
 # Deprecated group/name - [DATABASE]/sql_idle_timeout
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openstack/nova/patches/13-mysql_cluster_support.patch	Thu Sep 08 13:16:06 2016 -0600
@@ -0,0 +1,1300 @@
+This patchset is for bug:
+
+24394587 Mitaka Nova should support MySQL Cluster
+
+This fixes the following aspects of Nova:
+1. Implementation of an oslo.db configuration parameter to specify the MySQL
+   storage engine (mysql_storage_engine).
+2. Replacement of hardcoded SQL statements that set the engine to "InnoDB"
+   to the above configuration value.
+3. Logic to handle SQL differences between MySQL InnoDB and MySQL Cluster (NDB).
+   This includes column lengths, constraints, foreign keys, and indexes.
+
+This has not been committed upstream, but has been filed in launchpad:
+
+https://bugs.launchpad.net/nova/+bug/1564110
+
+
+--- nova-13.1.0/nova/db/sqlalchemy/api_migrations/migrate_repo/versions/001_cell_mapping.py.orig	2016-08-24 14:22:51.920016201 -0700
++++ nova-13.1.0/nova/db/sqlalchemy/api_migrations/migrate_repo/versions/001_cell_mapping.py	2016-08-24 14:22:34.974838825 -0700
[email protected]@ -11,6 +11,7 @@
+ #    under the License.
+ 
+ from migrate import UniqueConstraint
++from oslo_config import cfg
+ from sqlalchemy import Column
+ from sqlalchemy import DateTime
+ from sqlalchemy import Index
[email protected]@ -20,6 +21,8 @@ from sqlalchemy import String
+ from sqlalchemy import Table
+ from sqlalchemy import Text
+ 
++CONF = cfg.CONF
++
+ 
+ def upgrade(migrate_engine):
+     meta = MetaData()
[email protected]@ -34,7 +37,7 @@ def upgrade(migrate_engine):
+         Column('transport_url', Text()),
+         Column('database_connection', Text()),
+         UniqueConstraint('uuid', name='uniq_cell_mappings0uuid'),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
+--- nova-13.1.0/nova/db/sqlalchemy/api_migrations/migrate_repo/versions/002_instance_mapping.py.orig	2016-08-24 14:22:59.979100128 -0700
++++ nova-13.1.0/nova/db/sqlalchemy/api_migrations/migrate_repo/versions/002_instance_mapping.py	2016-08-24 14:22:34.976482225 -0700
[email protected]@ -12,6 +12,7 @@
+ 
+ from migrate.changeset.constraint import ForeignKeyConstraint
+ from migrate import UniqueConstraint
++from oslo_config import cfg
+ from sqlalchemy import Column
+ from sqlalchemy import DateTime
+ from sqlalchemy import Index
[email protected]@ -20,6 +21,8 @@ from sqlalchemy import MetaData
+ from sqlalchemy import String
+ from sqlalchemy import Table
+ 
++CONF = cfg.CONF
++
+ 
+ def upgrade(migrate_engine):
+     meta = MetaData()
[email protected]@ -38,7 +41,7 @@ def upgrade(migrate_engine):
+         Index('project_id_idx', 'project_id'),
+         ForeignKeyConstraint(columns=['cell_id'],
+             refcolumns=[cell_mappings.c.id]),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
+--- nova-13.1.0/nova/db/sqlalchemy/api_migrations/migrate_repo/versions/003_host_mapping.py.orig	2016-08-24 14:23:07.246721338 -0700
++++ nova-13.1.0/nova/db/sqlalchemy/api_migrations/migrate_repo/versions/003_host_mapping.py	2016-08-24 14:22:34.977720063 -0700
[email protected]@ -12,6 +12,7 @@
+ 
+ from migrate.changeset.constraint import ForeignKeyConstraint
+ from migrate import UniqueConstraint
++from oslo_config import cfg
+ from sqlalchemy import Column
+ from sqlalchemy import DateTime
+ from sqlalchemy import Index
[email protected]@ -20,6 +21,8 @@ from sqlalchemy import MetaData
+ from sqlalchemy import String
+ from sqlalchemy import Table
+ 
++CONF = cfg.CONF
++
+ 
+ def upgrade(migrate_engine):
+     meta = MetaData()
[email protected]@ -36,7 +39,7 @@ def upgrade(migrate_engine):
+             name='uniq_host_mappings0host'),
+         ForeignKeyConstraint(columns=['cell_id'],
+             refcolumns=[cell_mappings.c.id]),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
+--- nova-13.1.0/nova/db/sqlalchemy/api_migrations/migrate_repo/versions/004_add_request_spec.py.orig	2016-08-24 14:23:13.631755716 -0700
++++ nova-13.1.0/nova/db/sqlalchemy/api_migrations/migrate_repo/versions/004_add_request_spec.py	2016-08-24 14:22:34.978932430 -0700
[email protected]@ -11,6 +11,7 @@
+ #    under the License.
+ 
+ from migrate import UniqueConstraint
++from oslo_config import cfg
+ from sqlalchemy import Column
+ from sqlalchemy import DateTime
+ from sqlalchemy import Index
[email protected]@ -20,6 +21,8 @@ from sqlalchemy import String
+ from sqlalchemy import Table
+ from sqlalchemy import Text
+ 
++CONF = cfg.CONF
++
+ 
+ def upgrade(migrate_engine):
+     meta = MetaData()
[email protected]@ -33,7 +36,7 @@ def upgrade(migrate_engine):
+         Column('spec', Text, nullable=False),
+         UniqueConstraint('instance_uuid',
+             name='uniq_request_specs0instance_uuid'),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
+--- nova-13.1.0/nova/db/sqlalchemy/api_migrations/migrate_repo/versions/005_flavors.py.orig	2016-08-24 14:23:20.047706518 -0700
++++ nova-13.1.0/nova/db/sqlalchemy/api_migrations/migrate_repo/versions/005_flavors.py	2016-08-24 14:22:34.980360970 -0700
[email protected]@ -12,6 +12,7 @@
+ 
+ from migrate.changeset.constraint import ForeignKeyConstraint
+ from migrate import UniqueConstraint
++from oslo_config import cfg
+ from sqlalchemy import Boolean
+ from sqlalchemy import Column
+ from sqlalchemy import DateTime
[email protected]@ -22,6 +23,8 @@ from sqlalchemy import MetaData
+ from sqlalchemy import String
+ from sqlalchemy import Table
+ 
++CONF = cfg.CONF
++
+ 
+ def upgrade(migrate_engine):
+     meta = MetaData()
[email protected]@ -44,7 +47,7 @@ def upgrade(migrate_engine):
+         Column('is_public', Boolean),
+         UniqueConstraint("flavorid", name="uniq_flavors0flavorid"),
+         UniqueConstraint("name", name="uniq_flavors0name"),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+     flavors.create(checkfirst=True)
[email protected]@ -59,7 +62,7 @@ def upgrade(migrate_engine):
+         UniqueConstraint('flavor_id', 'key',
+             name='uniq_flavor_extra_specs0flavor_id0key'),
+         ForeignKeyConstraint(columns=['flavor_id'], refcolumns=[flavors.c.id]),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -82,7 +85,7 @@ def upgrade(migrate_engine):
+             name='uniq_flavor_projects0flavor_id0project_id'),
+         ForeignKeyConstraint(columns=['flavor_id'],
+             refcolumns=[flavors.c.id]),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+     flavor_projects.create(checkfirst=True)
+--- nova-13.1.0/nova/db/sqlalchemy/api_migrations/migrate_repo/versions/006_build_request.py.orig	2016-08-24 14:23:28.058484501 -0700
++++ nova-13.1.0/nova/db/sqlalchemy/api_migrations/migrate_repo/versions/006_build_request.py	2016-08-24 14:22:34.981684038 -0700
[email protected]@ -12,6 +12,7 @@
+ 
+ from migrate.changeset.constraint import ForeignKeyConstraint
+ from migrate import UniqueConstraint
++from oslo_config import cfg
+ from sqlalchemy import Boolean
+ from sqlalchemy import Column
+ from sqlalchemy import DateTime
[email protected]@ -24,6 +25,8 @@ from sqlalchemy import String
+ from sqlalchemy import Table
+ from sqlalchemy import Text
+ 
++CONF = cfg.CONF
++
+ 
+ def InetSmall():
+     return String(length=39).with_variant(dialects.postgresql.INET(),
[email protected]@ -61,7 +64,7 @@ def upgrade(migrate_engine):
+         Index('build_requests_project_id_idx', 'project_id'),
+         ForeignKeyConstraint(columns=['request_spec_id'],
+             refcolumns=[request_specs.c.id]),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
+--- nova-13.1.0/nova/db/sqlalchemy/migrate_repo/versions/216_havana.py.orig	2016-08-24 14:23:37.472011893 -0700
++++ nova-13.1.0/nova/db/sqlalchemy/migrate_repo/versions/216_havana.py	2016-08-24 14:22:34.987060556 -0700
[email protected]@ -14,6 +14,7 @@
+ 
+ from migrate.changeset import UniqueConstraint
+ from migrate import ForeignKeyConstraint
++from oslo_config import cfg
+ from oslo_log import log as logging
+ from sqlalchemy import Boolean, BigInteger, Column, DateTime, Enum, Float
+ from sqlalchemy import dialects
[email protected]@ -25,6 +26,8 @@ from nova.i18n import _LE
+ 
+ LOG = logging.getLogger(__name__)
+ 
++CONF = cfg.CONF
++
+ 
+ # Note on the autoincrement flag: this is defaulted for primary key columns
+ # of integral type, so is no longer set explicitly in such cases.
[email protected]@ -37,6 +40,10 @@ def MediumText():
+     return Text().with_variant(dialects.mysql.MEDIUMTEXT(), 'mysql')
+ 
+ 
++def TinyText():
++    return Text().with_variant(dialects.mysql.TINYTEXT(), 'mysql')
++
++
+ def Inet():
+     return String(length=43).with_variant(dialects.postgresql.INET(),
+                   'postgresql')
[email protected]@ -76,7 +83,7 @@ def _create_shadow_tables(migrate_engine
+ 
+         shadow_table_name = 'shadow_' + table_name
+         shadow_table = Table(shadow_table_name, meta, *columns,
+-                             mysql_engine='InnoDB')
++                             mysql_engine=CONF.database.mysql_storage_engine)
+         try:
+             shadow_table.create()
+         except Exception:
[email protected]@ -167,7 +174,7 @@ def _create_dump_tables(migrate_engine):
+             else:
+                 columns.append(column.copy())
+         table_dump = Table(dump_table_name, meta, *columns,
+-                           mysql_engine='InnoDB')
++                           mysql_engine=CONF.database.mysql_storage_engine)
+         table_dump.create()
+ 
+ 
[email protected]@ -187,7 +194,7 @@ def upgrade(migrate_engine):
+         Column('url', String(length=255)),
+         Column('md5hash', String(length=255)),
+         Column('deleted', Integer),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -200,7 +207,7 @@ def upgrade(migrate_engine):
+         Column('aggregate_id', Integer, ForeignKey('aggregates.id'),
+               nullable=False),
+         Column('deleted', Integer),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -214,7 +221,7 @@ def upgrade(migrate_engine):
+         Column('key', String(length=255), nullable=False),
+         Column('value', String(length=255), nullable=False),
+         Column('deleted', Integer),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -225,7 +232,7 @@ def upgrade(migrate_engine):
+         Column('id', Integer, primary_key=True, nullable=False),
+         Column('name', String(length=255)),
+         Column('deleted', Integer),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -250,7 +257,7 @@ def upgrade(migrate_engine):
+         Column('disk_bus', String(length=255), nullable=True),
+         Column('boot_index', Integer),
+         Column('image_id', String(length=36), nullable=True),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -268,7 +275,7 @@ def upgrade(migrate_engine):
+         Column('last_ctr_in', BigInteger()),
+         Column('last_ctr_out', BigInteger()),
+         Column('deleted', Integer),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -284,7 +291,7 @@ def upgrade(migrate_engine):
+         Column('is_parent', Boolean),
+         Column('deleted', Integer),
+         Column('transport_url', String(length=255), nullable=False),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -297,7 +304,7 @@ def upgrade(migrate_engine):
+         Column('project_id', String(length=255)),
+         Column('file_name', String(length=255)),
+         Column('deleted', Integer),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -310,7 +317,7 @@ def upgrade(migrate_engine):
+         Column('key', String(length=255), nullable=False),
+         Column('value', String(length=255)),
+         Column('deleted', Integer),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -339,7 +346,7 @@ def upgrade(migrate_engine):
+         Column('host_ip', InetSmall()),
+         Column('supported_instances', Text),
+         Column('pci_stats', Text, nullable=True),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -356,7 +363,7 @@ def upgrade(migrate_engine):
+         Column('host', String(length=255)),
+         Column('compute_host', String(length=255)),
+         Column('deleted', Integer),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -379,7 +386,7 @@ def upgrade(migrate_engine):
+         Column('pool_id', Integer, ForeignKey('console_pools.id')),
+         Column(*consoles_instance_uuid_column_args),
+         Column('deleted', Integer),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -392,7 +399,7 @@ def upgrade(migrate_engine):
+         Column('scope', String(length=255)),
+         Column('availability_zone', String(length=255)),
+         Column('project_id', String(length=255)),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -410,7 +417,7 @@ def upgrade(migrate_engine):
+         Column('host', String(length=255)),
+         Column('instance_uuid', String(length=36)),
+         Column('deleted', Integer),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -427,7 +434,7 @@ def upgrade(migrate_engine):
+         Column('pool', String(length=255)),
+         Column('interface', String(length=255)),
+         Column('deleted', Integer),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -442,7 +449,7 @@ def upgrade(migrate_engine):
+         Column('details', MediumText()),
+         Column('host', String(length=255)),
+         Column('deleted', Integer),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -453,7 +460,7 @@ def upgrade(migrate_engine):
+         Column('id', Integer, primary_key=True, nullable=False),
+         Column('uuid', String(36), nullable=False),
+         Column('deleted', Integer),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -465,7 +472,7 @@ def upgrade(migrate_engine):
+         Column('network_info', MediumText()),
+         Column('instance_uuid', String(length=36), nullable=False),
+         Column('deleted', Integer),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -481,7 +488,7 @@ def upgrade(migrate_engine):
+         Column('name', String(length=255)),
+         UniqueConstraint('uuid', 'deleted',
+                          name='uniq_instance_groups0uuid0deleted'),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8',
+     )
+ 
[email protected]@ -495,7 +502,7 @@ def upgrade(migrate_engine):
+         Column('value', String(length=255)),
+         Column('group_id', Integer, ForeignKey('instance_groups.id'),
+                nullable=False),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8',
+     )
+ 
[email protected]@ -508,7 +515,7 @@ def upgrade(migrate_engine):
+         Column('policy', String(length=255)),
+         Column('group_id', Integer, ForeignKey('instance_groups.id'),
+                nullable=False),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8',
+     )
+ 
[email protected]@ -521,7 +528,7 @@ def upgrade(migrate_engine):
+         Column('instance_id', String(length=255)),
+         Column('group_id', Integer, ForeignKey('instance_groups.id'),
+                nullable=False),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8',
+     )
+ 
[email protected]@ -534,7 +541,7 @@ def upgrade(migrate_engine):
+         Column('value', String(length=255)),
+         Column('instance_uuid', String(length=36), nullable=True),
+         Column('deleted', Integer),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -547,7 +554,7 @@ def upgrade(migrate_engine):
+         Column('key', String(length=255), nullable=False),
+         Column('value', String(length=255)),
+         Column('deleted', Integer),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -561,7 +568,7 @@ def upgrade(migrate_engine):
+         Column('key', String(length=255)),
+         Column('value', String(length=255)),
+         Column('deleted', Integer),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -573,7 +580,7 @@ def upgrade(migrate_engine):
+         Column('instance_type_id', Integer, nullable=False),
+         Column('project_id', String(length=255)),
+         Column('deleted', Integer),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -594,67 +601,131 @@ def upgrade(migrate_engine):
+         Column('disabled', Boolean),
+         Column('is_public', Boolean),
+         Column('deleted', Integer),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
+-    inst_lock_enum = Enum('owner', 'admin', name='instances0locked_by')
+-    instances = Table('instances', meta,
+-        Column('created_at', DateTime),
+-        Column('updated_at', DateTime),
+-        Column('deleted_at', DateTime),
+-        Column('id', Integer, primary_key=True, nullable=False),
+-        Column('internal_id', Integer),
+-        Column('user_id', String(length=255)),
+-        Column('project_id', String(length=255)),
+-        Column('image_ref', String(length=255)),
+-        Column('kernel_id', String(length=255)),
+-        Column('ramdisk_id', String(length=255)),
+-        Column('launch_index', Integer),
+-        Column('key_name', String(length=255)),
+-        Column('key_data', MediumText()),
+-        Column('power_state', Integer),
+-        Column('vm_state', String(length=255)),
+-        Column('memory_mb', Integer),
+-        Column('vcpus', Integer),
+-        Column('hostname', String(length=255)),
+-        Column('host', String(length=255)),
+-        Column('user_data', MediumText()),
+-        Column('reservation_id', String(length=255)),
+-        Column('scheduled_at', DateTime),
+-        Column('launched_at', DateTime),
+-        Column('terminated_at', DateTime),
+-        Column('display_name', String(length=255)),
+-        Column('display_description', String(length=255)),
+-        Column('availability_zone', String(length=255)),
+-        Column('locked', Boolean),
+-        Column('os_type', String(length=255)),
+-        Column('launched_on', MediumText()),
+-        Column('instance_type_id', Integer),
+-        Column('vm_mode', String(length=255)),
+-        Column('uuid', String(length=36)),
+-        Column('architecture', String(length=255)),
+-        Column('root_device_name', String(length=255)),
+-        Column('access_ip_v4', InetSmall()),
+-        Column('access_ip_v6', InetSmall()),
+-        Column('config_drive', String(length=255)),
+-        Column('task_state', String(length=255)),
+-        Column('default_ephemeral_device', String(length=255)),
+-        Column('default_swap_device', String(length=255)),
+-        Column('progress', Integer),
+-        Column('auto_disk_config', Boolean),
+-        Column('shutdown_terminate', Boolean),
+-        Column('disable_terminate', Boolean),
+-        Column('root_gb', Integer),
+-        Column('ephemeral_gb', Integer),
+-        Column('cell_name', String(length=255)),
+-        Column('node', String(length=255)),
+-        Column('deleted', Integer),
+-        Column('locked_by', inst_lock_enum),
+-        Column('cleaned', Integer, default=0),
+-        mysql_engine='InnoDB',
+-        mysql_charset='utf8'
+-    )
++    # MySQL Cluster limits row lengths to 14,000 characters. The logic
++    # below modifies the 'instances' table so that it can fit within these
++    # constraints if NDBCLUSTER is set as the mysql_storage_engine. Columns
++    # that can be shrunk are, while others are converted to TinyText blobs
++    # which can hold 256 characters.
++    if CONF.database.mysql_storage_engine == "NDBCLUSTER":
++        inst_lock_enum = Enum('owner', 'admin', name='instances0locked_by')
++        instances = Table('instances', meta,
++            Column('created_at', DateTime),
++            Column('updated_at', DateTime),
++            Column('deleted_at', DateTime),
++            Column('id', Integer, primary_key=True, nullable=False),
++            Column('internal_id', Integer),
++            Column('user_id', String(length=255)),
++            Column('project_id', String(length=255)),
++            Column('image_ref', String(length=255)),
++            Column('kernel_id', TinyText()),
++            Column('ramdisk_id', TinyText()),
++            Column('launch_index', Integer),
++            Column('key_name', TinyText()),
++            Column('key_data', MediumText()),
++            Column('power_state', Integer),
++            Column('vm_state', String(length=64)),
++            Column('memory_mb', Integer),
++            Column('vcpus', Integer),
++            Column('hostname', String(length=255)),
++            Column('host', String(length=255)),
++            Column('user_data', MediumText()),
++            Column('reservation_id', String(length=255)),
++            Column('scheduled_at', DateTime),
++            Column('launched_at', DateTime),
++            Column('terminated_at', DateTime),
++            Column('display_name', String(length=255)),
++            Column('display_description', TinyText()),
++            Column('availability_zone', TinyText()),
++            Column('locked', Boolean),
++            Column('os_type', String(length=64)),
++            Column('launched_on', MediumText()),
++            Column('instance_type_id', Integer),
++            Column('vm_mode', String(length=64)),
++            Column('uuid', String(length=36)),
++            Column('architecture', String(length=64)),
++            Column('root_device_name', String(length=64)),
++            Column('access_ip_v4', InetSmall()),
++            Column('access_ip_v6', InetSmall()),
++            Column('config_drive', TinyText()),
++            Column('task_state', String(length=64)),
++            Column('default_ephemeral_device', TinyText()),
++            Column('default_swap_device', TinyText()),
++            Column('progress', Integer),
++            Column('auto_disk_config', Boolean),
++            Column('shutdown_terminate', Boolean),
++            Column('disable_terminate', Boolean),
++            Column('root_gb', Integer),
++            Column('ephemeral_gb', Integer),
++            Column('cell_name', String(length=255)),
++            Column('node', String(length=255)),
++            Column('deleted', Integer),
++            Column('locked_by', inst_lock_enum),
++            Column('cleaned', Integer, default=0),
++            mysql_engine=CONF.database.mysql_storage_engine,
++            mysql_charset='utf8'
++        )
++    else:
++        inst_lock_enum = Enum('owner', 'admin', name='instances0locked_by')
++        instances = Table('instances', meta,
++            Column('created_at', DateTime),
++            Column('updated_at', DateTime),
++            Column('deleted_at', DateTime),
++            Column('id', Integer, primary_key=True, nullable=False),
++            Column('internal_id', Integer),
++            Column('user_id', String(length=255)),
++            Column('project_id', String(length=255)),
++            Column('image_ref', String(length=255)),
++            Column('kernel_id', String(length=255)),
++            Column('ramdisk_id', String(length=255)),
++            Column('launch_index', Integer),
++            Column('key_name', String(length=255)),
++            Column('key_data', MediumText()),
++            Column('power_state', Integer),
++            Column('vm_state', String(length=255)),
++            Column('memory_mb', Integer),
++            Column('vcpus', Integer),
++            Column('hostname', String(length=255)),
++            Column('host', String(length=255)),
++            Column('user_data', MediumText()),
++            Column('reservation_id', String(length=255)),
++            Column('scheduled_at', DateTime),
++            Column('launched_at', DateTime),
++            Column('terminated_at', DateTime),
++            Column('display_name', String(length=255)),
++            Column('display_description', String(length=255)),
++            Column('availability_zone', String(length=255)),
++            Column('locked', Boolean),
++            Column('os_type', String(length=255)),
++            Column('launched_on', MediumText()),
++            Column('instance_type_id', Integer),
++            Column('vm_mode', String(length=255)),
++            Column('uuid', String(length=36)),
++            Column('architecture', String(length=255)),
++            Column('root_device_name', String(length=255)),
++            Column('access_ip_v4', InetSmall()),
++            Column('access_ip_v6', InetSmall()),
++            Column('config_drive', String(length=255)),
++            Column('task_state', String(length=255)),
++            Column('default_ephemeral_device', String(length=255)),
++            Column('default_swap_device', String(length=255)),
++            Column('progress', Integer),
++            Column('auto_disk_config', Boolean),
++            Column('shutdown_terminate', Boolean),
++            Column('disable_terminate', Boolean),
++            Column('root_gb', Integer),
++            Column('ephemeral_gb', Integer),
++            Column('cell_name', String(length=255)),
++            Column('node', String(length=255)),
++            Column('deleted', Integer),
++            Column('locked_by', inst_lock_enum),
++            Column('cleaned', Integer, default=0),
++            mysql_engine=CONF.database.mysql_storage_engine,
++            mysql_charset='utf8'
++        )
+ 
+     instance_actions = Table('instance_actions', meta,
+         Column('created_at', DateTime),
[email protected]@ -670,7 +741,7 @@ def upgrade(migrate_engine):
+         Column('finish_time', DateTime),
+         Column('message', String(length=255)),
+         Column('deleted', Integer),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8',
+     )
+ 
[email protected]@ -686,7 +757,7 @@ def upgrade(migrate_engine):
+         Column('result', String(length=255)),
+         Column('traceback', Text),
+         Column('deleted', Integer),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8',
+     )
+ 
[email protected]@ -699,7 +770,7 @@ def upgrade(migrate_engine):
+         Column('host', String(length=255)),
+         Column('volume_id', String(length=36), nullable=True),
+         Column('deleted', Integer),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -713,7 +784,7 @@ def upgrade(migrate_engine):
+         Column('fingerprint', String(length=255)),
+         Column('public_key', MediumText()),
+         Column('deleted', Integer),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -732,7 +803,7 @@ def upgrade(migrate_engine):
+         Column('source_node', String(length=255)),
+         Column('dest_node', String(length=255)),
+         Column('deleted', Integer),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -766,7 +837,7 @@ def upgrade(migrate_engine):
+         Column('priority', Integer),
+         Column('rxtx_base', Integer),
+         Column('deleted', Integer),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -794,7 +865,7 @@ def upgrade(migrate_engine):
+         UniqueConstraint('compute_node_id',
+                         'address', 'deleted',
+                         name=pci_devices_uc_name),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8')
+ 
+     provider_fw_rules = Table('provider_fw_rules', meta,
[email protected]@ -807,7 +878,7 @@ def upgrade(migrate_engine):
+         Column('to_port', Integer),
+         Column('cidr', Inet()),
+         Column('deleted', Integer),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -820,7 +891,7 @@ def upgrade(migrate_engine):
+         Column('resource', String(length=255)),
+         Column('hard_limit', Integer),
+         Column('deleted', Integer),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -836,7 +907,7 @@ def upgrade(migrate_engine):
+         Column('until_refresh', Integer),
+         Column('deleted', Integer),
+         Column('user_id', String(length=255)),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -849,7 +920,7 @@ def upgrade(migrate_engine):
+         Column('resource', String(length=255), nullable=False),
+         Column('hard_limit', Integer),
+         Column('deleted', Integer),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -873,7 +944,7 @@ def upgrade(migrate_engine):
+                         Column('hard_limit', Integer, nullable=True),
+                         UniqueConstraint('user_id', 'project_id', 'resource',
+                                          'deleted', name=uniq_name),
+-                        mysql_engine='InnoDB',
++                        mysql_engine=CONF.database.mysql_storage_engine,
+                         mysql_charset='utf8',
+                         )
+ 
[email protected]@ -890,7 +961,7 @@ def upgrade(migrate_engine):
+         Column('expire', DateTime),
+         Column('deleted', Integer),
+         Column('user_id', String(length=255)),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -901,7 +972,7 @@ def upgrade(migrate_engine):
+         Column('id', Integer, primary_key=True, nullable=False),
+         Column('uuid', String(length=36), nullable=False),
+         Column('deleted', Integer),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -914,7 +985,7 @@ def upgrade(migrate_engine):
+         Column('security_group_id', Integer),
+         Column('instance_uuid', String(length=36)),
+         Column('deleted', Integer),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -930,7 +1001,7 @@ def upgrade(migrate_engine):
+         Column('cidr', Inet()),
+         Column('group_id', Integer, ForeignKey('security_groups.id')),
+         Column('deleted', Integer),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -944,7 +1015,7 @@ def upgrade(migrate_engine):
+         Column('user_id', String(length=255)),
+         Column('project_id', String(length=255)),
+         Column('deleted', Integer),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -958,7 +1029,7 @@ def upgrade(migrate_engine):
+         Column('from_port', Integer),
+         Column('to_port', Integer),
+         Column('cidr', Inet()),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8',
+     )
+ 
[email protected]@ -974,7 +1045,7 @@ def upgrade(migrate_engine):
+         Column('disabled', Boolean),
+         Column('deleted', Integer),
+         Column('disabled_reason', String(length=255)),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -985,7 +1056,7 @@ def upgrade(migrate_engine):
+         Column('id', Integer, primary_key=True, nullable=False),
+         Column('uuid', String(length=36), nullable=False),
+         Column('deleted', Integer),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -1004,7 +1075,7 @@ def upgrade(migrate_engine):
+         Column('display_name', String(length=255)),
+         Column('display_description', String(length=255)),
+         Column('deleted', String(length=36)),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -1022,7 +1093,7 @@ def upgrade(migrate_engine):
+         Column('task_items', Integer),
+         Column('errors', Integer),
+         Column('deleted', Integer),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -1036,7 +1107,7 @@ def upgrade(migrate_engine):
+         Column('uuid', String(length=36)),
+         Column('instance_uuid', String(length=36), nullable=True),
+         Column('deleted', Integer),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -1047,7 +1118,7 @@ def upgrade(migrate_engine):
+         Column('id', Integer, primary_key=True, nullable=False),
+         Column('uuid', String(length=36), nullable=False),
+         Column('deleted', Integer),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -1077,7 +1148,7 @@ def upgrade(migrate_engine):
+         Column('instance_uuid', String(length=36)),
+         Column('attach_time', DateTime),
+         Column('deleted', String(length=36)),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='utf8'
+     )
+ 
[email protected]@ -1102,7 +1173,7 @@ def upgrade(migrate_engine):
+             Column("project_id", String(length=36)),
+             Column("user_id", String(length=36)),
+             Column("availability_zone", String(length=255)),
+-            mysql_engine='InnoDB',
++            mysql_engine=CONF.database.mysql_storage_engine,
+             mysql_charset='utf8'
+     )
+ 
[email protected]@ -1475,10 +1546,12 @@ def upgrade(migrate_engine):
+ 
+         # mysql-specific index by leftmost 100 chars.  (mysql gets angry if the
+         # index key length is too long.)
+-        sql = ("create index migrations_by_host_nodes_and_status_idx ON "
+-               "migrations (deleted, source_compute(100), dest_compute(100), "
+-               "source_node(100), dest_node(100), status)")
+-        migrate_engine.execute(sql)
++        # MySQL Cluster does not support index prefixes.
++        if CONF.database.mysql_storage_engine != "NDBCLUSTER":
++            sql = ("create index migrations_by_host_nodes_and_status_idx ON "
++                   "migrations (deleted, source_compute(100), dest_compute(100), "
++                   "source_node(100), dest_node(100), status)")
++            migrate_engine.execute(sql)
+ 
+     # PostgreSQL specific indexes
+     if migrate_engine.name == 'postgresql':
+--- nova-13.1.0/nova/db/sqlalchemy/migrate_repo/versions/249_remove_duplicate_index.py.orig	2016-08-24 14:23:44.593220901 -0700
++++ nova-13.1.0/nova/db/sqlalchemy/migrate_repo/versions/249_remove_duplicate_index.py	2016-08-24 14:22:34.988854556 -0700
[email protected]@ -13,7 +13,7 @@
+ #    License for the specific language governing permissions and limitations
+ #    under the License.
+ 
+-
++from migrate.changeset.constraint import ForeignKeyConstraint
+ from sqlalchemy import MetaData, Table
+ 
+ 
[email protected]@ -25,7 +25,17 @@ def upgrade(migrate_engine):
+ 
+     meta = MetaData(bind=migrate_engine)
+ 
++    # Remove the foreign key so that the index can be removed correctly
++    block_device_mapping = Table('block_device_mapping', meta, autoload=True)
++    instances = Table('instances', meta, autoload=True)
++    fkey = ForeignKeyConstraint(columns=[block_device_mapping.c.instance_uuid],
++                                refcolumns=[instances.c.uuid])
++    fkey.drop()
++
+     bdm = Table('block_device_mapping', meta, autoload=True)
+     for index in bdm.indexes:
+         if index.name == INDEX_NAME:
+             index.drop()
++
++    # Recreate foreign key
++    fkey.create()
+--- nova-13.1.0/nova/db/sqlalchemy/migrate_repo/versions/252_add_instance_extra_table.py.orig	2016-08-24 14:23:51.489446458 -0700
++++ nova-13.1.0/nova/db/sqlalchemy/migrate_repo/versions/252_add_instance_extra_table.py	2016-08-24 14:22:34.990323618 -0700
[email protected]@ -12,6 +12,7 @@
+ 
+ 
+ from migrate import ForeignKeyConstraint
++from oslo_config import cfg
+ from sqlalchemy import Column
+ from sqlalchemy import DateTime
+ from sqlalchemy import Index
[email protected]@ -21,6 +22,8 @@ from sqlalchemy import String
+ from sqlalchemy import Table
+ from sqlalchemy import Text
+ 
++CONF = cfg.CONF
++
+ 
+ def upgrade(migrate_engine):
+     meta = MetaData()
[email protected]@ -42,7 +45,8 @@ def upgrade(migrate_engine):
+             continue
+         _columns = tuple([Column(*args, **kwargs)
+                           for args, kwargs in columns])
+-        table = Table(basename, meta, *_columns, mysql_engine='InnoDB',
++        table = Table(basename, meta, *_columns,
++                      mysql_engine=CONF.database.mysql_storage_engine,
+                       mysql_charset='utf8')
+         table.create()
+ 
+--- nova-13.1.0/nova/db/sqlalchemy/migrate_repo/versions/266_add_instance_tags.py.orig	2016-08-24 14:23:58.465133071 -0700
++++ nova-13.1.0/nova/db/sqlalchemy/migrate_repo/versions/266_add_instance_tags.py	2016-08-24 14:22:34.991752728 -0700
[email protected]@ -10,8 +10,11 @@
+ #    License for the specific language governing permissions and limitations
+ #    under the License.
+ 
++from oslo_config import cfg
+ import sqlalchemy as sa
+ 
++CONF = cfg.CONF
++
+ 
+ def upgrade(migrate_engine):
+     meta = sa.MetaData(bind=migrate_engine)
[email protected]@ -22,6 +25,6 @@ def upgrade(migrate_engine):
+                     sa.Column('tag', sa.Unicode(80), primary_key=True,
+                               nullable=False),
+                     sa.Index('tags_tag_idx', 'tag'),
+-                    mysql_engine='InnoDB',
++                    mysql_engine=CONF.database.mysql_storage_engine,
+                     mysql_charset='utf8')
+     tags.create()
+--- nova-13.1.0/nova/db/sqlalchemy/migrate_repo/versions/267_instance_uuid_non_nullable.py.orig	2016-08-24 14:24:04.907962418 -0700
++++ nova-13.1.0/nova/db/sqlalchemy/migrate_repo/versions/267_instance_uuid_non_nullable.py	2016-08-24 14:22:34.993331103 -0700
[email protected]@ -13,6 +13,7 @@
+ #    under the License.
+ 
+ from migrate import UniqueConstraint
++from oslo_config import cfg
+ from oslo_log import log as logging
+ from sqlalchemy import MetaData
+ from sqlalchemy.sql import null
[email protected]@ -24,6 +25,8 @@ LOG = logging.getLogger(__name__)
+ 
+ UC_NAME = 'uniq_instances0uuid'
+ 
++CONF = cfg.CONF
++
+ 
+ def scan_for_null_records(table, col_name, check_fkeys):
+     """Queries the table looking for NULL instances of the given column.
[email protected]@ -96,10 +99,15 @@ def upgrade(migrate_engine):
+     # defined with cascading deletes.
+     meta = MetaData(migrate_engine)
+     meta.reflect(migrate_engine)
+-    # Scan the database first and fail if any NULL records found.
+-    process_null_records(meta, scan=True)
+-    # Now run the alter statements.
+-    process_null_records(meta, scan=False)
++
++    # MySQL Cluster creates non-NULL records by default. We can skip
++    # the checks and just configure the constraint.
++    if CONF.database.mysql_storage_engine != "NDBCLUSTER":
++        # Scan the database first and fail if any NULL records found.
++        process_null_records(meta, scan=True)
++        # Now run the alter statements.
++        process_null_records(meta, scan=False)
++
+     # Create a unique constraint on instances.uuid for foreign keys.
+     instances = meta.tables['instances']
+     UniqueConstraint('uuid', table=instances, name=UC_NAME).create()
+--- nova-13.1.0/nova/db/sqlalchemy/migrate_repo/versions/314_add_resource_provider_tables.py.orig	2016-08-24 14:24:12.780710808 -0700
++++ nova-13.1.0/nova/db/sqlalchemy/migrate_repo/versions/314_add_resource_provider_tables.py	2016-08-24 14:22:34.995184176 -0700
[email protected]@ -12,6 +12,7 @@
+ """Database migrations for resource-providers."""
+ 
+ from migrate import UniqueConstraint
++from oslo_config import cfg
+ from sqlalchemy import Column
+ from sqlalchemy import Float
+ from sqlalchemy import Index
[email protected]@ -20,6 +21,8 @@ from sqlalchemy import MetaData
+ from sqlalchemy import String
+ from sqlalchemy import Table
+ 
++CONF = cfg.CONF
++
+ 
+ def upgrade(migrate_engine):
+     meta = MetaData()
[email protected]@ -30,7 +33,7 @@ def upgrade(migrate_engine):
+         Column('id', Integer, primary_key=True, nullable=False),
+         Column('uuid', String(36), nullable=False),
+         UniqueConstraint('uuid', name='uniq_resource_providers0uuid'),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='latin1'
+     )
+     # NOTE(mriedem): DB2 creates an index when a unique constraint is created
[email protected]@ -50,7 +53,7 @@ def upgrade(migrate_engine):
+         Column('max_unit', Integer, nullable=False),
+         Column('step_size', Integer, nullable=False),
+         Column('allocation_ratio', Float, nullable=False),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='latin1'
+     )
+     Index('inventories_resource_provider_id_idx',
[email protected]@ -65,7 +68,7 @@ def upgrade(migrate_engine):
+         Column('consumer_id', String(36), nullable=False),
+         Column('resource_class_id', Integer, nullable=False),
+         Column('used', Integer, nullable=False),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='latin1'
+     )
+     Index('allocations_resource_provider_class_id_idx',
+--- nova-13.1.0/nova/db/sqlalchemy/migrate_repo/versions/318_resource_provider_name_aggregates.py.orig	2016-08-24 14:24:19.484566325 -0700
++++ nova-13.1.0/nova/db/sqlalchemy/migrate_repo/versions/318_resource_provider_name_aggregates.py	2016-08-24 14:22:34.997184351 -0700
[email protected]@ -11,6 +11,7 @@
+ #    under the License.
+ 
+ from migrate import UniqueConstraint
++from oslo_config import cfg
+ from oslo_db.sqlalchemy import utils
+ from sqlalchemy import Column
+ from sqlalchemy import DDL
[email protected]@ -20,6 +21,8 @@ from sqlalchemy import MetaData
+ from sqlalchemy import Table
+ from sqlalchemy import Unicode
+ 
++CONF = cfg.CONF
++
+ 
+ def upgrade(migrate_engine):
+     meta = MetaData(bind=migrate_engine)
[email protected]@ -66,7 +69,7 @@ def upgrade(migrate_engine):
+         Column('resource_provider_id', Integer, primary_key=True,
+                nullable=False),
+         Column('aggregate_id', Integer, primary_key=True, nullable=False),
+-        mysql_engine='InnoDB',
++        mysql_engine=CONF.database.mysql_storage_engine,
+         mysql_charset='latin1'
+     )
+     Index('resource_provider_aggregates_aggregate_id_idx',
+--- nova-13.1.0/nova/db/sqlalchemy/models.py.orig	2016-08-24 14:24:27.031251043 -0700
++++ nova-13.1.0/nova/db/sqlalchemy/models.py	2016-08-24 14:25:54.013809653 -0700
[email protected]@ -20,6 +20,7 @@ SQLAlchemy models for nova data.
+ """
+ 
+ from oslo_config import cfg
++from oslo_db import options
+ from oslo_db.sqlalchemy import models
+ from oslo_utils import timeutils
+ from sqlalchemy import (Column, Index, Integer, BigInteger, Enum, String,
[email protected]@ -33,12 +34,17 @@ from nova.db.sqlalchemy import types
+ 
+ CONF = cfg.CONF
+ BASE = declarative_base()
++options.set_defaults(cfg.CONF)
+ 
+ 
+ def MediumText():
+     return Text().with_variant(MEDIUMTEXT(), 'mysql')
+ 
+ 
++def TinyText():
++    return Text().with_variant(TINYTEXT(), 'mysql')
++
++
+ class NovaBase(models.TimestampMixin,
+                models.ModelBase):
+     metadata = None
[email protected]@ -250,17 +256,32 @@ class Instance(BASE, NovaBase, models.So
+     project_id = Column(String(255))
+ 
+     image_ref = Column(String(255))
+-    kernel_id = Column(String(255))
+-    ramdisk_id = Column(String(255))
++    if CONF.database.mysql_storage_engine == "NDBCLUSTER":
++        kernel_id = Column(TinyText())
++        ramdisk_id = Column(TinyText())
++    else:
++        kernel_id = Column(String(255))
++        ramdisk_id = Column(String(255))
++
+     hostname = Column(String(255))
+ 
+     launch_index = Column(Integer)
+-    key_name = Column(String(255))
++
++    if CONF.database.mysql_storage_engine == "NDBCLUSTER":
++        key_name = Column(TinyText())
++    else:
++        key_name = Column(String(255))
++
+     key_data = Column(MediumText())
+ 
+     power_state = Column(Integer)
+-    vm_state = Column(String(255))
+-    task_state = Column(String(255))
++
++    if CONF.database.mysql_storage_engine == "NDBCLUSTER":
++        vm_state = Column(String(64))
++        task_state = Column(String(64))
++    else:
++        vm_state = Column(String(255))
++        task_state = Column(String(255))
+ 
+     memory_mb = Column(Integer)
+     vcpus = Column(Integer)
[email protected]@ -291,11 +312,17 @@ class Instance(BASE, NovaBase, models.So
+     # This always refers to the availability_zone kwarg passed in /servers and
+     # provided as an API option, not at all related to the host AZ the instance
+     # belongs to.
+-    availability_zone = Column(String(255))
++    if CONF.database.mysql_storage_engine == "NDBCLUSTER":
++        availability_zone = Column(TinyText())
++    else:
++        availability_zone = Column(String(255))
+ 
+     # User editable field for display in user-facing UIs
+     display_name = Column(String(255))
+-    display_description = Column(String(255))
++    if CONF.database.mysql_storage_engine == "NDBCLUSTER":
++        display_description = Column(TinyText())
++    else:
++        display_description = Column(String(255))
+ 
+     # To remember on which host an instance booted.
+     # An instance may have moved to another host by live migration.
[email protected]@ -306,15 +333,27 @@ class Instance(BASE, NovaBase, models.So
+     locked = Column(Boolean)
+     locked_by = Column(Enum('owner', 'admin'))
+ 
+-    os_type = Column(String(255))
+-    architecture = Column(String(255))
+-    vm_mode = Column(String(255))
++    if CONF.database.mysql_storage_engine == "NDBCLUSTER":
++        os_type = Column(String(64))
++        architecture = Column(String(64))
++        vm_mode = Column(String(64))
++    else:
++        os_type = Column(String(255))
++        architecture = Column(String(255))
++        vm_mode = Column(String(255))
++
+     uuid = Column(String(36), nullable=False)
+ 
+-    root_device_name = Column(String(255))
+-    default_ephemeral_device = Column(String(255))
+-    default_swap_device = Column(String(255))
+-    config_drive = Column(String(255))
++    if CONF.database.mysql_storage_engine == "NDBCLUSTER":
++        root_device_name = Column(String(64))
++        default_ephemeral_device = Column(TinyText())
++        default_swap_device = Column(TinyText())
++        config_drive = Column(TinyText())
++    else:
++        root_device_name = Column(String(255))
++        default_ephemeral_device = Column(String(255))
++        default_swap_device = Column(String(255))
++        config_drive = Column(String(255))
+ 
+     # User editable field meant to represent what ip should be used
+     # to connect to the instance
+--- nova-13.1.0/nova/db/sqlalchemy/utils.py.orig	2016-08-24 14:24:33.499513020 -0700
++++ nova-13.1.0/nova/db/sqlalchemy/utils.py	2016-08-24 14:22:35.001433371 -0700
[email protected]@ -13,6 +13,7 @@
+ #    License for the specific language governing permissions and limitations
+ #    under the License.
+ 
++from oslo_config import cfg
+ from oslo_db import exception as db_exc
+ from oslo_db.sqlalchemy import utils as oslodbutils
+ from oslo_log import log as logging
[email protected]@ -30,6 +31,8 @@ from nova.i18n import _, _LE
+ 
+ LOG = logging.getLogger(__name__)
+ 
++CONF = cfg.CONF
++
+ 
+ class DeleteFromSelect(UpdateBase):
+     def __init__(self, table, select, column):
[email protected]@ -120,7 +123,7 @@ def create_shadow_table(migrate_engine,
+ 
+     shadow_table_name = db._SHADOW_TABLE_PREFIX + table.name
+     shadow_table = Table(shadow_table_name, meta, *columns,
+-                         mysql_engine='InnoDB')
++                         mysql_engine=CONF.database.mysql_storage_engine)
+     try:
+         shadow_table.create()
+         return shadow_table
+--- nova-13.1.0/nova/tests/unit/db/test_migrations.py.orig	2016-08-24 14:24:39.811171818 -0700
++++ nova-13.1.0/nova/tests/unit/db/test_migrations.py	2016-08-24 14:22:35.003780248 -0700
[email protected]@ -42,6 +42,7 @@ import os
+ from migrate import UniqueConstraint
+ from migrate.versioning import repository
+ import mock
++from oslo_config import cfg
+ from oslo_db.sqlalchemy import test_base
+ from oslo_db.sqlalchemy import test_migrations
+ from oslo_db.sqlalchemy import utils as oslodbutils
[email protected]@ -59,6 +60,8 @@ from nova import exception
+ from nova import test
+ from nova.tests import fixtures as nova_fixtures
+ 
++CONF = cfg.CONF
++
+ 
+ class NovaMigrationsCheckers(test_migrations.ModelsMigrationsSync,
+                              test_migrations.WalkVersionsMixin):
[email protected]@ -911,9 +914,10 @@ class TestNovaMigrationsMySQL(NovaMigrat
+             "SELECT count(*) "
+             "FROM information_schema.TABLES "
+             "WHERE TABLE_SCHEMA='%(database)s' "
+-            "AND ENGINE != 'InnoDB' "
++            "AND ENGINE != '%(mysql_storage_engine)s' "
+             "AND TABLE_NAME != 'migrate_version'" %
+-            {'database': self.migrate_engine.url.database})
++            {'database': self.migrate_engine.url.database,
++             'mysql_storage_engine': CONF.database.mysql_storage_engine})
+         count = noninnodb.scalar()
+         self.assertEqual(count, 0, "%d non InnoDB tables created" % count)
+ 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/python/oslo.db/patches/mysql_cluster_support.patch	Thu Sep 08 13:16:06 2016 -0600
@@ -0,0 +1,29 @@
+This patchset is for bug:
+
+24409419 Mitaka oslo.db should support MySQL Cluster
+
+This fixes the following aspects of Oslo.db:
+1. Implementation of an oslo.db configuration parameter to specify the MySQL
+   storage engine (mysql_storage_engine).
+
+This has not been committed upstream, but has been filed in launchpad:
+
+https://bugs.launchpad.net/oslo.db/+bug/1564110
+
+
+--- oslo.db-4.7.0/oslo_db/options.py.orig	2016-08-04 14:14:42.609731178 -0700
++++ oslo.db-4.7.0/oslo_db/options.py	2016-08-04 14:42:24.071847993 -0700
[email protected]@ -48,6 +48,13 @@ database_opts = [
+                     'server-set SQL mode. To use whatever SQL mode '
+                     'is set by the server configuration, '
+                     'set this to no value. Example: mysql_sql_mode='),
++    cfg.StrOpt('mysql_storage_engine',
++               default='InnoDB',
++               help='This configures the MySQL storage engine. '
++                    'This allows for OpenStack to support different storage '
++                    'engine such as InnoDB, NDB, etc. For MySQL Cluster, '
++                    'such as InnoDB, NDB, etc. For MySQL Cluster, set to '
++                    'NDBCLUSTER. Example: mysql_storage_engine=NDBCLUSTER'),
+     cfg.IntOpt('idle_timeout',
+                default=3600,
+                deprecated_opts=[cfg.DeprecatedOpt('sql_idle_timeout',