Fix 033 add encryption unique key migration

SQLite doesn't support 'drop constraint' statament. So it notifies:
SAWarning: Table 'encryption' specifies columns 'volume_type_id'
as primary_key=True, not matching locally specified columns
'encryption_id'; setting the current primary key columns to
'encryption_id'. This warning may become an exception in a future
release.

Add new strategy for sqlite upgrade: table is renamed to temporary
name, new table is created followed by INSERT statements, and
renamed to original one.

Change-Id: I723fdadb946f6944dde03954409899a9ddc3ee0e
Closes-Bug: #1431374
This commit is contained in:
Anton Arefiev 2015-03-18 14:59:59 +02:00
parent d84c501a60
commit 5f8a616b15
2 changed files with 67 additions and 34 deletions

View File

@ -13,8 +13,8 @@
import uuid import uuid
from migrate import PrimaryKeyConstraint, ForeignKeyConstraint from migrate import PrimaryKeyConstraint, ForeignKeyConstraint
from sqlalchemy import Column from sqlalchemy import Column, MetaData, Table
from sqlalchemy import MetaData, String, Table from sqlalchemy import String, Integer, Boolean, DateTime
def upgrade(migrate_engine): def upgrade(migrate_engine):
@ -24,45 +24,42 @@ def upgrade(migrate_engine):
encryptions = Table('encryption', meta, autoload=True) encryptions = Table('encryption', meta, autoload=True)
encryption_id_column_kwargs = {} # NOTE: SQLite doesn't support 'drop constraint' statament
if migrate_engine.name == 'ibm_db_sa': if migrate_engine.name == 'sqlite':
# NOTE(junxiebj): DB2 10.5 doesn't support primary key _upgrade_sqlite(meta, encryptions)
# constraints over nullable columns, so we have to else:
# make the column non-nullable in the DB2 case. encryption_id_column_kwargs = {}
encryption_id_column_kwargs['nullable'] = False if migrate_engine.name == 'ibm_db_sa':
encryption_id = Column('encryption_id', String(36), # NOTE(junxiebj): DB2 10.5 doesn't support primary key
**encryption_id_column_kwargs) # constraints over nullable columns, so we have to
encryptions.create_column(encryption_id) # make the column non-nullable in the DB2 case.
encryption_id_column_kwargs['nullable'] = False
encryption_id = Column('encryption_id', String(36),
**encryption_id_column_kwargs)
encryptions.create_column(encryption_id)
encryption_items = list(encryptions.select().execute()) encryption_items = list(encryptions.select().execute())
for item in encryption_items: for item in encryption_items:
encryptions.update().\ encryptions.update().\
where(encryptions.c.volume_type_id == item['volume_type_id']).\ where(encryptions.c.volume_type_id == item['volume_type_id']).\
values(encryption_id=str(uuid.uuid4())).execute() values(encryption_id=str(uuid.uuid4())).execute()
# NOTE (e0ne): need to drop FK first for MySQL # NOTE (e0ne): need to drop FK first for MySQL
if migrate_engine.name == 'mysql': if migrate_engine.name == 'mysql':
ref_table = Table('volume_types', meta, autoload=True) ref_table = Table('volume_types', meta, autoload=True)
params = {'columns': [encryptions.c['volume_type_id']], params = {'columns': [encryptions.c['volume_type_id']],
'refcolumns': [ref_table.c['id']], 'refcolumns': [ref_table.c['id']],
'name': 'encryption_ibfk_1'} 'name': 'encryption_ibfk_1'}
volume_type_fk = ForeignKeyConstraint(**params) volume_type_fk = ForeignKeyConstraint(**params)
volume_type_fk.drop() volume_type_fk.drop()
try:
volume_type_pk = PrimaryKeyConstraint('volume_type_id', volume_type_pk = PrimaryKeyConstraint('volume_type_id',
table=encryptions) table=encryptions)
volume_type_pk.drop() volume_type_pk.drop()
except Exception:
# NOTE (e0ne): SQLite doesn't support 'drop constraint' statament
if migrate_engine.url.get_dialect().name.startswith('sqlite'):
pass
else:
raise
pkey = PrimaryKeyConstraint(encryptions.columns.encryption_id) pkey = PrimaryKeyConstraint(encryptions.columns.encryption_id)
pkey.create() pkey.create()
def downgrade(migrate_engine): def downgrade(migrate_engine):
@ -84,3 +81,36 @@ def downgrade(migrate_engine):
'name': 'encryption_ibfk_1'} 'name': 'encryption_ibfk_1'}
volume_type_fk = ForeignKeyConstraint(**params) volume_type_fk = ForeignKeyConstraint(**params)
volume_type_fk.create() volume_type_fk.create()
def _upgrade_sqlite(meta, encryptions):
new_encryptions = Table(
'encryption_33', meta,
Column('created_at', DateTime(timezone=False)),
Column('updated_at', DateTime(timezone=False)),
Column('deleted_at', DateTime(timezone=False)),
Column('deleted', Boolean(create_constraint=True, name=None)),
Column('cipher', String(255)),
Column('key_size', Integer),
Column('provider', String(255)),
Column('control_location', String(255)),
Column('encryption_id', String(36), primary_key=True),
Column('volume_type_id', String(36))
)
new_encryptions.create()
encryption_items = list(encryptions.select().execute())
for item in encryption_items:
new_encryptions.insert().\
values(created_at=item['created_at'],
updated_at=item['updated_at'],
deleted_at=item['deleted_at'],
deleted=item['deleted'],
cipher=item['cipher'],
key_size=item['key_size'],
provider=item['provider'],
control_location=item['control_location'],
encryption_id=str(uuid.uuid4()),
volume_type_id=item['volume_type_id']).execute()
encryptions.drop()
new_encryptions.rename('encryption')

View File

@ -37,11 +37,14 @@ ignore_codes = ["E1103"]
# Note(xyang): the fourth and fifth error messages are for the code [E1101]. # Note(xyang): the fourth and fifth error messages are for the code [E1101].
# They should be ignored because 'sha256' and 'sha224' are functions in # They should be ignored because 'sha256' and 'sha224' are functions in
# 'hashlib'. # 'hashlib'.
# Note(aarefiev): the sixth error message is for SQLAlchemy rename calls in
# DB migration(033_add_encryption_unique_key).
ignore_messages = ["An attribute affected in cinder.tests", ignore_messages = ["An attribute affected in cinder.tests",
"No name 'urllib' in module '_MovedItems'", "No name 'urllib' in module '_MovedItems'",
"No value passed for parameter 'dml'", "No value passed for parameter 'dml'",
"Module 'hashlib' has no 'sha256' member", "Module 'hashlib' has no 'sha256' member",
"Module 'hashlib' has no 'sha224' member"] "Module 'hashlib' has no 'sha224' member",
"Instance of 'Table' has no 'rename' member"]
# Note(maoy): we ignore all errors in openstack.common because it should be # Note(maoy): we ignore all errors in openstack.common because it should be
# checked elsewhere. We also ignore cinder.tests for now due to high false # checked elsewhere. We also ignore cinder.tests for now due to high false
# positive rate. # positive rate.