Coverage for nova/db/main/migrations/env.py: 44%

36 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2025-04-24 11:16 +0000

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

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

3# a copy of the License at 

4# 

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

6# 

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

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

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

10# License for the specific language governing permissions and limitations 

11# under the License. 

12 

13from logging.config import fileConfig 

14 

15from alembic import context 

16from sqlalchemy import engine_from_config 

17from sqlalchemy import pool 

18 

19from nova.db.main import models 

20 

21# this is the Alembic Config object, which provides 

22# access to the values within the .ini file in use. 

23config = context.config 

24 

25# Interpret the config file for Python logging unless we're told not to. 

26# This line sets up loggers basically. 

27if config.attributes.get('configure_logger', True): 27 ↛ 28line 27 didn't jump to line 28 because the condition on line 27 was never true

28 fileConfig(config.config_file_name) 

29 

30# this is the MetaData object for the various models in the main database 

31target_metadata = models.BASE.metadata 

32 

33 

34def include_name(name, type_, parent_names): 

35 """Determine which tables or columns to skip. 

36 

37 This is used when we decide to "delete" a table or column. In this 

38 instance, we will remove the SQLAlchemy model or field but leave the 

39 underlying database table or column in place for a number of releases 

40 after. Once we're sure that there is no code running that contains 

41 references to the old models, we can then remove the underlying table. In 

42 the interim, we must track the discrepancy between models and actual 

43 database data here. 

44 """ 

45 if type_ == 'table': 

46 # NOTE(stephenfin): We don't have models corresponding to the various 

47 # shadow tables. Alembic doesn't like this. Tell Alembic to look the 

48 # other way. Good Alembic. 

49 if name.startswith('shadow_'): 

50 return False 

51 

52 return name not in models.REMOVED_TABLES 

53 

54 if type_ == 'column': 

55 return (parent_names['table_name'], name) not in models.REMOVED_COLUMNS 

56 

57 return True 

58 

59 

60def run_migrations_offline(): 

61 """Run migrations in 'offline' mode. 

62 

63 This configures the context with just a URL and not an Engine, though an 

64 Engine is acceptable here as well. By skipping the Engine creation we 

65 don't even need a DBAPI to be available. 

66 

67 Calls to context.execute() here emit the given string to the script output. 

68 """ 

69 url = config.get_main_option("sqlalchemy.url") 

70 context.configure( 

71 url=url, 

72 target_metadata=target_metadata, 

73 render_as_batch=True, 

74 include_name=include_name, 

75 literal_binds=True, 

76 dialect_opts={"paramstyle": "named"}, 

77 ) 

78 

79 with context.begin_transaction(): 

80 context.run_migrations() 

81 

82 

83def run_migrations_online(): 

84 """Run migrations in 'online' mode. 

85 

86 In this scenario we need to create an Engine and associate a connection 

87 with the context. 

88 

89 This is modified from the default based on the below, since we want to 

90 share an engine when unit testing so in-memory database testing actually 

91 works. 

92 

93 https://alembic.sqlalchemy.org/en/latest/cookbook.html#connection-sharing 

94 """ 

95 connectable = config.attributes.get('connection', None) 

96 

97 if connectable is None: 97 ↛ 99line 97 didn't jump to line 99 because the condition on line 97 was never true

98 # only create Engine if we don't have a Connection from the outside 

99 connectable = engine_from_config( 

100 config.get_section(config.config_ini_section), 

101 prefix="sqlalchemy.", 

102 poolclass=pool.NullPool, 

103 ) 

104 with connectable.connect() as connection: 

105 context.configure( 

106 connection=connection, 

107 target_metadata=target_metadata, 

108 render_as_batch=True, 

109 include_name=include_name, 

110 ) 

111 

112 with context.begin_transaction(): 

113 context.run_migrations() 

114 else: 

115 context.configure( 

116 connection=connectable, 

117 target_metadata=target_metadata, 

118 render_as_batch=True, 

119 include_name=include_name, 

120 ) 

121 

122 with context.begin_transaction(): 

123 context.run_migrations() 

124 

125 

126if context.is_offline_mode(): 126 ↛ 127line 126 didn't jump to line 127 because the condition on line 126 was never true

127 run_migrations_offline() 

128else: 

129 run_migrations_online()