Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
Cloud:OpenStack:Pike:Staging
openstack-cinder
ocata_live_migrations_to_pike.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File ocata_live_migrations_to_pike.patch of Package openstack-cinder
diff --git a/cinder/cmd/manage.py b/cinder/cmd/manage.py index f4ec90ae3..1d830554f 100644 --- a/cinder/cmd/manage.py +++ b/cinder/cmd/manage.py @@ -205,7 +205,8 @@ class HostCommands(object): class DbCommands(object): """Class for managing the database.""" - online_migrations = () + online_migrations = (db.migrate_consistencygroups_to_groups, + db.migrate_add_message_prefix) def __init__(self): pass diff --git a/cinder/db/api.py b/cinder/db/api.py index 25c406595..013ac50d7 100644 --- a/cinder/db/api.py +++ b/cinder/db/api.py @@ -1888,3 +1888,24 @@ def conditional_update(context, model, values, expected_values, filters=(), return IMPL.conditional_update(context, model, values, expected_values, filters, include_deleted, project_only, order) + +########################## +# Ocata live migrations + + +def migrate_consistencygroups_to_groups(context, max_count, force=False): + """Migrage CGs to generic volume groups""" + return IMPL.migrate_consistencygroups_to_groups(context, max_count, force) + + +def migrate_add_message_prefix(context, max_count, force=False): + """Change Message event ids to start with the VOLUME_ prefix. + + :param max_count: The maximum number of messages to consider in + this run. + :param force: Ignored in this migration + :returns: number of messages needing migration, number of + messages migrated (both will always be less than + max_count). + """ + return IMPL.migrate_add_message_prefix(context, max_count, force) diff --git a/cinder/db/sqlalchemy/api.py b/cinder/db/sqlalchemy/api.py index d80fbfd82..7840512dd 100644 --- a/cinder/db/sqlalchemy/api.py +++ b/cinder/db/sqlalchemy/api.py @@ -61,6 +61,7 @@ from cinder.i18n import _ from cinder.objects import fields from cinder import utils from cinder.volume import utils as vol_utils +from cinder.volume import group_types CONF = cfg.CONF @@ -6932,3 +6933,171 @@ def conditional_update(context, model, values, expected_values, filters=(), # Return True if we were able to change any DB entry, False otherwise result = query.update(values, **update_args) return 0 != result + +########################## +# Ocata live migrations + + +@require_admin_context +def migrate_consistencygroups_to_groups(context, max_count, force=False): + now = timeutils.utcnow() + grps = model_query(context, models.Group) + ids = [grp.id for grp in grps] if grps else [] + # NOTE(xyang): We are using the same IDs in the CG and Group tables. + # This is because we are deleting the entry from the CG table after + # migrating it to the Group table. Also when the user queries a CG id, + # we will display it whether it is in the CG table or the Group table. + # Without using the same IDs, we'll have to add a consistencygroup_id + # column in the Group group to correlate it with the CG entry so we + # know whether it has been migrated or not. It makes things more + # complicated especially because the CG entry will be removed after + # migration. + query = (model_query(context, models.ConsistencyGroup). + filter(models.ConsistencyGroup.id.notin_(ids))) + cgs = query.limit(max_count) + + # Check if default group_type for migrating cgsnapshots exists + result = (model_query(context, models.GroupTypes, + project_only=True). + filter_by(name=group_types.DEFAULT_CGSNAPSHOT_TYPE). + first()) + if not result: + msg = (_('Group type %s not found. Rerun migration script to create ' + 'the default cgsnapshot type.') % + group_types.DEFAULT_CGSNAPSHOT_TYPE) + raise exception.NotFound(msg) + grp_type_id = result['id'] + + count_all = 0 + count_hit = 0 + for cg in cgs.all(): + cg_ids = [] + cgsnapshot_ids = [] + volume_ids = [] + snapshot_ids = [] + session = get_session() + with session.begin(): + count_all += 1 + cgsnapshot_list = [] + vol_list = [] + + # NOTE(dulek): We should avoid modifying consistency groups that + # are in the middle of some operation. + if not force: + if cg.status not in (fields.ConsistencyGroupStatus.AVAILABLE, + fields.ConsistencyGroupStatus.ERROR, + fields.ConsistencyGroupStatus.DELETING): + continue + + # Migrate CG to group + grp = model_query(context, models.Group, + session=session).filter_by(id=cg.id).first() + if grp: + # NOTE(xyang): This CG is already migrated to group. + continue + + values = {'id': cg.id, + 'created_at': now, + 'updated_at': now, + 'deleted': False, + 'user_id': cg.user_id, + 'project_id': cg.project_id, + 'host': cg.host, + 'cluster_name': cg.cluster_name, + 'availability_zone': cg.availability_zone, + 'name': cg.name, + 'description': cg.description, + 'group_type_id': grp_type_id, + 'status': cg.status, + 'group_snapshot_id': cg.cgsnapshot_id, + 'source_group_id': cg.source_cgid, + } + + mappings = [] + for item in cg.volume_type_id.rstrip(',').split(','): + mapping = models.GroupVolumeTypeMapping() + mapping['volume_type_id'] = item + mapping['group_id'] = cg.id + mappings.append(mapping) + + values['volume_types'] = mappings + + grp = models.Group() + grp.update(values) + session.add(grp) + cg_ids.append(cg.id) + + # Update group_id in volumes + vol_list = (model_query(context, models.Volume, + session=session). + filter_by(consistencygroup_id=cg.id).all()) + for vol in vol_list: + vol.group_id = cg.id + volume_ids.append(vol.id) + + # Migrate data from cgsnapshots to group_snapshots + cgsnapshot_list = (model_query(context, models.Cgsnapshot, + session=session). + filter_by(consistencygroup_id=cg.id).all()) + + for cgsnap in cgsnapshot_list: + grp_snap = (model_query(context, models.GroupSnapshot, + session=session). + filter_by(id=cgsnap.id).first()) + if grp_snap: + # NOTE(xyang): This CGSnapshot is already migrated to + # group snapshot. + continue + + grp_snap = models.GroupSnapshot() + values = {'id': cgsnap.id, + 'created_at': now, + 'updated_at': now, + 'deleted': False, + 'user_id': cgsnap.user_id, + 'project_id': cgsnap.project_id, + 'group_id': cg.id, + 'name': cgsnap.name, + 'description': cgsnap.description, + 'group_type_id': grp_type_id, + 'status': cgsnap.status, } + grp_snap.update(values) + session.add(grp_snap) + cgsnapshot_ids.append(cgsnap.id) + + # Update group_snapshot_id in snapshots + snap_list = (model_query(context, models.Snapshot, + session=session). + filter_by(cgsnapshot_id=cgsnap.id).all()) + for snap in snap_list: + snap.group_snapshot_id = cgsnap.id + snapshot_ids.append(snap.id) + + # Delete entries in CG and CGSnapshot tables + cg_cgsnapshot_destroy_all_by_ids(context, cg_ids, cgsnapshot_ids, + volume_ids, snapshot_ids, + session=session) + + count_hit += 1 + + return count_all, count_hit + + +@require_admin_context +def migrate_add_message_prefix(context, max_count, force=False): + prefix = "VOLUME_" + session = get_session() + with session.begin(): + messages = (model_query(context, models.Message.id, session=session). + filter(~models.Message.event_id.like(prefix + '%')). + limit(max_count)) + ids = [msg[0] for msg in messages.all()] + count_all = messages.count() + count_hit = 0 + if ids: + count_hit = (model_query(context, models.Message, session=session). + filter(models.Message.id.in_(ids)). + update({'event_id': prefix + models.Message.event_id}, + synchronize_session=False)) + + return count_all, count_hit diff --git a/cinder/db/sqlalchemy/migrate_repo/versions/096_add_replication_status_to_groups_table.py b/cinder/db/sqlalchemy/migrate_repo/versions/096_add_replication_status_to_groups_table.py new file mode 100644 index 000000000..d639a0cd0 --- /dev/null +++ b/cinder/db/sqlalchemy/migrate_repo/versions/096_add_replication_status_to_groups_table.py @@ -0,0 +1,31 @@ +# Copyright (C) 2017 Dell Inc. or its subsidiaries. +# All Rights Reserved. +# +# 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. + +from sqlalchemy import Column +from sqlalchemy import MetaData, String, Table + +# This was moved back from 102 to 96 (replacing a placeholder) for the +# SUSE cloud newton -> pike skipped upgrade. This column is needed before the +# live_migrations can take place. This is not included upstream. + +def upgrade(migrate_engine): + meta = MetaData() + meta.bind = migrate_engine + + # Add replication_status column to groups table + table = Table('groups', meta, autoload=True) + if not hasattr(table.c, 'replication_status'): + new_column = Column('replication_status', String(255), nullable=True) + table.create_column(new_column) diff --git a/cinder/db/sqlalchemy/migrate_repo/versions/096_placeholder.py b/cinder/db/sqlalchemy/migrate_repo/versions/096_placeholder.py deleted file mode 100644 index 7f0c9af0d..000000000 --- a/cinder/db/sqlalchemy/migrate_repo/versions/096_placeholder.py +++ /dev/null @@ -1,22 +0,0 @@ -# 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. - -# This is a placeholder for Mitaka backports. -# Do not use this number for new Newton work. New work starts after -# all the placeholders. -# -# See this for more information: -# http://lists.openstack.org/pipermail/openstack-dev/2013-March/006827.html - - -def upgrade(migrate_engine): - pass
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor