DBMS: Fix db_sync between N and N+1 releases

If we start cinderlib from the N release with the DBMS persistence
plugin on a DB where we have already run it with release N+1 then we'll
get an `oslo_db.exception.DBMigrationError` exception.

This is because cinderlib automatically does a `db_sync` on each
initialization, but on N this will fail because there will be unknown
(as in newer) migrations applied to the DB.

This patch takes this possibility and checks the exception raised by
`db_sync` and ignores it if the `migrate` package complains with a
VersionNotFoundError.

Closes-Bug: #1868145
Change-Id: I539919e01f603d19cde8750ad92e3d2b4ac2fbc7
This commit is contained in:
Gorka Eguileor
2020-03-19 14:54:31 +01:00
parent 18f85d8b25
commit 01dfcb172e
3 changed files with 43 additions and 1 deletions

View File

@@ -23,6 +23,7 @@ from cinder.db.sqlalchemy import api as sqla_api
from cinder.db.sqlalchemy import models from cinder.db.sqlalchemy import models
from cinder import exception as cinder_exception from cinder import exception as cinder_exception
from cinder import objects as cinder_objs from cinder import objects as cinder_objs
import migrate
from oslo_config import cfg from oslo_config import cfg
from oslo_db import exception from oslo_db import exception
from oslo_log import log from oslo_log import log
@@ -72,7 +73,15 @@ class DBPersistence(persistence_base.PersistenceDriverBase):
self.original_get_by_id = self.db_instance.get_by_id self.original_get_by_id = self.db_instance.get_by_id
self.db_instance.get_by_id = self.get_by_id self.db_instance.get_by_id = self.get_by_id
migration.db_sync() try:
migration.db_sync()
except exception.DBMigrationError as exc:
# We can be running 2 Cinder versions at the same time on the same
# DB while we upgrade, so we must ignore the fact that the DB is
# now on a newer version.
if not isinstance(getattr(exc, 'inner_exception', None),
migrate.exceptions.VersionNotFoundError):
raise
self._create_key_value_table() self._create_key_value_table()
# NOTE : At this point, the persistence isn't ready so we need to use # NOTE : At this point, the persistence isn't ready so we need to use

View File

@@ -14,6 +14,7 @@
# under the License. # under the License.
import tempfile import tempfile
from unittest import mock
from cinder.db.sqlalchemy import api as sqla_api from cinder.db.sqlalchemy import api as sqla_api
from cinder import objects as cinder_ovos from cinder import objects as cinder_ovos
@@ -121,5 +122,31 @@ class TestDBPersistence(base.BasePersistenceTest):
self.assertEqual('__DEFAULT__', self.persistence.DEFAULT_TYPE.name) self.assertEqual('__DEFAULT__', self.persistence.DEFAULT_TYPE.name)
class TestDBPersistenceNewerSchema(base.helper.TestHelper):
"""Test DBMS plugin can start when the DB has a newer schema."""
CONNECTION = 'sqlite:///' + tempfile.NamedTemporaryFile().name
PERSISTENCE_CFG = {'storage': 'db',
'connection': CONNECTION}
@classmethod
def setUpClass(cls):
pass
def _raise_exc(self):
inner_exc = dbms.migrate.exceptions.VersionNotFoundError()
exc = dbms.exception.DBMigrationError(inner_exc)
self.original_db_sync()
raise(exc)
def test_newer_db_schema(self):
self.original_db_sync = dbms.migration.db_sync
with mock.patch.object(dbms.migration, 'db_sync',
side_effect=self._raise_exc) as db_sync_mock:
super(TestDBPersistenceNewerSchema, self).setUpClass()
db_sync_mock.assert_called_once()
self.assertIsInstance(cinderlib.Backend.persistence,
dbms.DBPersistence)
class TestMemoryDBPersistence(TestDBPersistence): class TestMemoryDBPersistence(TestDBPersistence):
PERSISTENCE_CFG = {'storage': 'memory_db'} PERSISTENCE_CFG = {'storage': 'memory_db'}

View File

@@ -0,0 +1,6 @@
---
fixes:
- |
Bug #1868145: Support rolling upgrades with the DBMS persistence plugin.
Can run an N release version even if we have already run once an N+1
release.