heat/heat/db/sqlalchemy/migrate_repo/versions/035_event_uuid_to_id.py

239 lines
10 KiB
Python

#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import itertools
import uuid
import migrate.changeset.constraint as constraint
from oslo.utils import timeutils
import sqlalchemy
def upgrade(migrate_engine):
if migrate_engine.name == 'sqlite':
upgrade_sqlite(migrate_engine)
return
meta = sqlalchemy.MetaData(bind=migrate_engine)
event_table = sqlalchemy.Table('event', meta, autoload=True)
event_uuid_column_kwargs = {}
if migrate_engine.name == 'ibm_db_sa':
# NOTE(mriedem): DB2 10.5 doesn't support unique constraints over
# nullable columns, it creates a unique index instead, so we have
# to make the uuid column non-nullable in the DB2 case.
event_uuid_column_kwargs['nullable'] = False
event_uuid = sqlalchemy.Column('uuid', sqlalchemy.String(length=36),
default=lambda: str(uuid.uuid4),
**event_uuid_column_kwargs)
event_table.create_column(event_uuid)
if migrate_engine.name == 'postgresql':
sequence = sqlalchemy.Sequence('evt')
sqlalchemy.schema.CreateSequence(sequence,
bind=migrate_engine).execute()
event_id = sqlalchemy.Column('tmp_id', sqlalchemy.Integer,
server_default=sqlalchemy.text(
"nextval('evt')"))
else:
event_id_column_kwargs = {}
if migrate_engine.name == 'ibm_db_sa':
# NOTE(mriedem): This is turned into a primary key constraint
# later so it must be non-nullable.
event_id_column_kwargs['nullable'] = False
event_id = sqlalchemy.Column('tmp_id', sqlalchemy.Integer,
**event_id_column_kwargs)
event_table.create_column(event_id)
fake_autoincrement = itertools.count(1)
event_list = event_table.select().order_by(
sqlalchemy.sql.expression.asc(
event_table.c.created_at)).execute().fetchall()
for event in event_list:
values = {'tmp_id': fake_autoincrement.next(), 'uuid': event.id}
update = event_table.update().where(
event_table.c.id == event.id).values(values)
migrate_engine.execute(update)
constraint_kwargs = {'table': event_table}
if migrate_engine.name == 'ibm_db_sa':
# NOTE(mriedem): DB2 gives a random name to the unique constraint
# if one is not provided so let's set the standard name ourselves.
constraint_kwargs['name'] = 'uniq_event0uuid0'
cons = constraint.UniqueConstraint('uuid', **constraint_kwargs)
cons.create()
event_table.c.id.drop()
alter_kwargs = {}
if migrate_engine.name == 'ibm_db_sa':
alter_kwargs['nullable'] = False
event_table.c.tmp_id.alter('id', sqlalchemy.Integer, **alter_kwargs)
cons = constraint.PrimaryKeyConstraint('tmp_id', table=event_table)
cons.create()
if migrate_engine.name == 'ibm_db_sa':
# NOTE(chenxiao): For DB2, setting "ID" column "autoincrement=True"
# can't make sense after above "tmp_id=>id" transformation,
# so should work around it.
sql = ("ALTER TABLE EVENT ALTER COLUMN ID SET GENERATED BY "
"DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1)")
migrate_engine.execute(sql)
else:
event_table.c.tmp_id.alter(sqlalchemy.Integer, autoincrement=True)
def upgrade_sqlite(migrate_engine):
meta = sqlalchemy.MetaData(bind=migrate_engine)
# (pafuent) Here it isn't recommended to import the table from the models,
# because in future migrations the model could change and this migration
# could fail.
# I know it is ugly but it's the only way that I found to 'freeze'
# the model state for this migration.
stack_table = sqlalchemy.Table('stack', meta, autoload=True)
event_table = sqlalchemy.Table(
'new_event', meta,
sqlalchemy.Column('id', sqlalchemy.Integer, primary_key=True),
sqlalchemy.Column('stack_id', sqlalchemy.String(36),
sqlalchemy.ForeignKey(stack_table.c.id),
nullable=False),
sqlalchemy.Column('uuid', sqlalchemy.String(36),
default=lambda: str(uuid.uuid4()),
unique=True),
sqlalchemy.Column('resource_action', sqlalchemy.String(255)),
sqlalchemy.Column('resource_status', sqlalchemy.String(255)),
sqlalchemy.Column('resource_name', sqlalchemy.String(255)),
sqlalchemy.Column('physical_resource_id', sqlalchemy.String(255)),
sqlalchemy.Column('resource_status_reason', sqlalchemy.String(255)),
sqlalchemy.Column('resource_type', sqlalchemy.String(255)),
sqlalchemy.Column('resource_properties', sqlalchemy.PickleType),
sqlalchemy.Column('created_at', sqlalchemy.DateTime,
default=timeutils.utcnow),
sqlalchemy.Column('updated_at', sqlalchemy.DateTime,
onupdate=timeutils.utcnow))
event_table.create()
prev_event_table = sqlalchemy.Table('event', meta, autoload=True)
event_list = list(prev_event_table.select().order_by(
sqlalchemy.sql.expression.asc(prev_event_table.c.created_at))
.execute())
for event in event_list:
values = {
'stack_id': event.stack_id,
'uuid': event.id,
'resource_action': event.resource_action,
'resource_status': event.resource_status,
'resource_name': event.resource_name,
'physical_resource_id': event.physical_resource_id,
'resource_status_reason': event.resource_status_reason,
'resource_type': event.resource_type,
'resource_properties': event.resource_properties}
migrate_engine.execute(event_table.insert(values))
prev_event_table.drop()
event_table.rename('event')
def downgrade(migrate_engine):
if migrate_engine.name == 'sqlite':
downgrade_sqlite(migrate_engine)
return
meta = sqlalchemy.MetaData(bind=migrate_engine)
event_table = sqlalchemy.Table('event', meta, autoload=True)
event_id_column_kwargs = {}
if migrate_engine.name == 'ibm_db_sa':
event_id_column_kwargs['nullable'] = False
event_id = sqlalchemy.Column('tmp_id', sqlalchemy.String(length=36),
default=lambda: str(uuid.uuid4),
**event_id_column_kwargs)
event_id.create(event_table)
event_list = event_table.select().execute()
for event in event_list:
values = {'tmp_id': event.uuid}
update = event_table.update().where(
event_table.c.uuid == event.uuid).values(values)
migrate_engine.execute(update)
event_table.c.id.drop()
event_table.c.uuid.drop()
cons = constraint.PrimaryKeyConstraint('tmp_id', table=event_table)
cons.create()
alter_kwargs = {}
# NOTE(mriedem): DB2 won't allow a primary key on a nullable column so
# we have to make it non-nullable.
if migrate_engine.name == 'ibm_db_sa':
alter_kwargs['nullable'] = False
event_table.c.tmp_id.alter('id', default=lambda: str(uuid.uuid4),
**alter_kwargs)
if migrate_engine.name == 'postgresql':
sequence = sqlalchemy.Sequence('evt')
sqlalchemy.schema.DropSequence(sequence, bind=migrate_engine).execute()
def downgrade_sqlite(migrate_engine):
meta = sqlalchemy.MetaData(bind=migrate_engine)
# (pafuent) Here it isn't recommended to import the table from the models,
# because in future migrations the model could change and this migration
# could fail.
# I know it is ugly but it's the only way that I found to 'freeze'
# the model state for this migration.
stack_table = sqlalchemy.Table('stack', meta, autoload=True)
event_table = sqlalchemy.Table(
'new_event', meta,
sqlalchemy.Column('id', sqlalchemy.String(36),
default=lambda: str(uuid.uuid4())),
sqlalchemy.Column('stack_id', sqlalchemy.String(36),
sqlalchemy.ForeignKey(stack_table.c.id),
nullable=False),
sqlalchemy.Column('resource_action', sqlalchemy.String(255)),
sqlalchemy.Column('resource_status', sqlalchemy.String(255)),
sqlalchemy.Column('resource_name', sqlalchemy.String(255)),
sqlalchemy.Column('physical_resource_id', sqlalchemy.String(255)),
sqlalchemy.Column('resource_status_reason', sqlalchemy.String(255)),
sqlalchemy.Column('resource_type', sqlalchemy.String(255)),
sqlalchemy.Column('resource_properties', sqlalchemy.PickleType),
sqlalchemy.Column('created_at', sqlalchemy.DateTime,
default=timeutils.utcnow),
sqlalchemy.Column('updated_at', sqlalchemy.DateTime,
onupdate=timeutils.utcnow))
event_table.create()
prev_event_table = sqlalchemy.Table('event', meta, autoload=True)
event_list = prev_event_table.select().execute().fetchall()
for event in event_list:
values = {
'id': event.uuid,
'stack_id': event.stack_id,
'resource_action': event.resource_action,
'resource_status': event.resource_status,
'resource_name': event.resource_name,
'physical_resource_id': event.physical_resource_id,
'resource_status_reason': event.resource_status_reason,
'resource_type': event.resource_type,
'resource_properties': event.resource_properties}
migrate_engine.execute(event_table.insert(values))
prev_event_table.drop()
event_table.rename('event')