001ffbd156
* created on_success method which upgrade script runs if upgrade succeed, don't fail upgrade in case of errors * remove saved version files for all upgrades from working directories It solves several problems: 1. user runs upgrade 5.0 -> 5.1 which fails upgrade system saves version which we upgrade from in file working_dir/5.1/version.yaml. Then user runs upgrade 5.0 -> 5.0.1 which successfully upgraded. Then user runs again upgrade 5.0.1 -> 5.1, but there is saved file working_dir/5.1/version.yaml which contains 5.0 version, and upgrade system thinks that it's upgrading from 5.0 version, as result it tries to make database dump from wrong version of container. 2. without this hack user can run upgrade second time and loose his data, this hack prevents this case because before upgrade checker will use current version instead of saved version to determine version which we run upgrade from. Change-Id: I5e6ae6ba2ae2e60b9812e131d2a7c533f4a38ab6 Related-bug: #1349833
91 lines
3.0 KiB
Python
91 lines
3.0 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
# Copyright 2014 Mirantis, Inc.
|
|
#
|
|
# 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 logging
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class UpgradeManager(object):
|
|
"""Upgrade manager is used to orchestrate upgrading process.
|
|
|
|
:param upgraders: a list with upgrader classes to use; each upgrader
|
|
must inherit the :class:`BaseUpgrader`
|
|
:param no_rollback: call :meth:`BaseUpgrader.rollback` method
|
|
in case of exception during execution
|
|
"""
|
|
|
|
def __init__(self, upgraders, no_rollback=True):
|
|
#: a list of upgraders to use
|
|
self._upgraders = upgraders
|
|
#: a list of used upgraders (needs by rollback feature)
|
|
self._used_upgraders = []
|
|
#: should we make rollback in case of error?
|
|
self._rollback = not no_rollback
|
|
|
|
def run(self):
|
|
"""Runs consequentially all registered upgraders.
|
|
|
|
.. note:: in case of exception the `rollback` method will be called
|
|
"""
|
|
logger.info('*** START UPGRADING')
|
|
|
|
for upgrader in self._upgraders:
|
|
|
|
try:
|
|
logger.debug('%s: upgrading...', upgrader.__class__.__name__)
|
|
self._used_upgraders.append(upgrader)
|
|
upgrader.upgrade()
|
|
except Exception as exc:
|
|
logger.exception(
|
|
'%s: failed to upgrade: "%s"',
|
|
upgrader.__class__.__name__, exc)
|
|
|
|
if self._rollback:
|
|
self.rollback()
|
|
|
|
logger.error('*** UPGRADE FAILED')
|
|
raise
|
|
|
|
self._on_success()
|
|
logger.info('*** UPGRADE DONE SUCCESSFULLY')
|
|
|
|
def _on_success(self):
|
|
"""Run on_success method for engines,
|
|
skip method call if there were some errors,
|
|
because if upgrade succeed we shouldn't
|
|
fail it.
|
|
"""
|
|
for upgrader in self._upgraders:
|
|
try:
|
|
logger.debug(
|
|
'%s: run on_success method',
|
|
upgrader.__class__.__name__)
|
|
upgrader.on_success()
|
|
except Exception as exc:
|
|
logger.exception(
|
|
'%s: skip the engine because failed to '
|
|
'execute on_success method: "%s"',
|
|
upgrader.__class__.__name__, exc)
|
|
|
|
def rollback(self):
|
|
logger.debug('Run rollback')
|
|
|
|
while self._used_upgraders:
|
|
upgrader = self._used_upgraders.pop()
|
|
logger.debug('%s: rollbacking...', upgrader.__class__.__name__)
|
|
upgrader.rollback()
|