Coverage for nova/db/migration.py: 98%

43 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2025-04-17 15:08 +0000

1# Copyright 2010 United States Government as represented by the 

2# Administrator of the National Aeronautics and Space Administration. 

3# All Rights Reserved. 

4# 

5# Licensed under the Apache License, Version 2.0 (the "License"); you may 

6# not use this file except in compliance with the License. You may obtain 

7# a copy of the License at 

8# 

9# http://www.apache.org/licenses/LICENSE-2.0 

10# 

11# Unless required by applicable law or agreed to in writing, software 

12# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 

13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 

14# License for the specific language governing permissions and limitations 

15# under the License. 

16 

17import os 

18 

19from alembic import command as alembic_api 

20from alembic import config as alembic_config 

21from alembic.runtime import migration as alembic_migration 

22from oslo_log import log as logging 

23 

24from nova.db.api import api as api_db_api 

25from nova.db.main import api as main_db_api 

26from nova import exception 

27 

28LOG = logging.getLogger(__name__) 

29 

30 

31def _get_engine(database='main', context=None): 

32 if database == 'main': 

33 return main_db_api.get_engine(context=context) 

34 

35 if database == 'api': 35 ↛ exitline 35 didn't return from function '_get_engine' because the condition on line 35 was always true

36 return api_db_api.get_engine() 

37 

38 

39def _find_alembic_conf(database='main'): 

40 """Get the path for the alembic repository.""" 

41 

42 path = os.path.join( 

43 os.path.abspath(os.path.dirname(__file__)), 

44 database, 'alembic.ini') 

45 

46 config = alembic_config.Config(path) 

47 # we don't want to use the logger configuration from the file, which is 

48 # only really intended for the CLI 

49 # https://stackoverflow.com/a/42691781/613428 

50 config.attributes['configure_logger'] = False 

51 return config 

52 

53 

54def _upgrade_alembic(engine, config, version): 

55 # reuse the connection rather than creating a new one 

56 with engine.begin() as connection: 

57 config.attributes['connection'] = connection 

58 alembic_api.upgrade(config, version or 'head') 

59 

60 

61def db_sync(version=None, database='main', context=None): 

62 """Migrate the database to `version` or the most recent version.""" 

63 

64 if database not in ('main', 'api'): 

65 raise exception.Invalid('%s is not a valid database' % database) 

66 

67 # if the user requested a specific version, check if it's an integer: 

68 # if so, we're almost certainly in sqlalchemy-migrate land and won't 

69 # support that 

70 if version is not None and version.isdigit(): 

71 raise exception.Invalid( 

72 'You requested an sqlalchemy-migrate database version; this is ' 

73 'no longer supported' 

74 ) 

75 

76 engine = _get_engine(database, context=context) 

77 

78 config = _find_alembic_conf(database) 

79 # discard the URL stored in alembic.ini in favour of the URL configured 

80 # for the engine, casting from 'sqlalchemy.engine.url.URL' to str in the 

81 # process 

82 # NOTE(sean-k-mooney): the engine has already url encoded the connection 

83 # string using a mix of url encode styles for different parts of the url. 

84 # since we are updating the alembic config parser instance we need to 

85 # escape '%' to '%%' to account for ConfigParser's string interpolation. 

86 url = engine.url.render_as_string(hide_password=False).replace('%', '%%') 

87 config.set_main_option('sqlalchemy.url', url) 

88 

89 # apply anything later 

90 LOG.info('Applying migration(s)') 

91 

92 _upgrade_alembic(engine, config, version) 

93 

94 LOG.info('Migration(s) applied') 

95 

96 

97def db_version(database='main', context=None): 

98 """Display the current database version.""" 

99 if database not in ('main', 'api'): 

100 raise exception.Invalid('%s is not a valid database' % database) 

101 

102 engine = _get_engine(database, context=context) 

103 

104 with engine.connect() as conn: 

105 m_context = alembic_migration.MigrationContext.configure(conn) 

106 version = m_context.get_current_revision() 

107 

108 return version