-
Notifications
You must be signed in to change notification settings - Fork 899
/
Copy pathevm_database.rb
236 lines (196 loc) · 6.82 KB
/
evm_database.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
class EvmDatabase
include Vmdb::Logging
# A ordered list of classes that are seeded before server initialization.
PRIMORDIAL_SEEDABLE_CLASSES = %w[
MiqDatabase
MiqRegion
MiqEnterprise
Zone
MiqServer
ServerRole
MiqWorkerType
Tenant
MiqProductFeature
MiqUserRole
MiqGroup
User
MiqReport
].freeze
# An ordered list of classes that will complete the seeding, but occuring
# after server initialization.
OTHER_SEEDABLE_CLASSES = %w[
MiqWidget
MiqAction
MiqEventDefinitionSet
MiqEventDefinition
MiqPolicySet
ChargebackRateDetailMeasure
ChargeableField
Currency
ChargebackRate
ServiceTemplateCatalog
BlacklistedEvent
Classification
CustomizationTemplate
Dialog
ManageIQ::Providers::EmbeddedAnsible
MiqAlert
MiqAlertSet
MiqDialog
MiqSearch
MiqShortcut
MiqWidgetSet
NotificationType
PxeImageType
ScanItem
TimeProfile
MiqAeDatastore
].freeze
def self.seedable_classes
PRIMORDIAL_SEEDABLE_CLASSES + OTHER_SEEDABLE_CLASSES + seedable_plugin_classes
end
def self.seedable_plugin_classes
Vmdb::Plugins.flat_map { |p| p.try(:seedable_classes) }.compact
end
class << self
attr_accessor :seeding
alias seeding? seeding
def with_seed
self.seeding = true
yield
ensure
self.seeding = false
end
end
self.seeding = false
def self.seed(classes = nil, exclude_list = [])
with_seed do
classes ||= seedable_classes
classes -= exclude_list
classes = classes.collect(&:constantize)
invalid = classes.reject { |c| c.respond_to?(:seed) }
raise ArgumentError, "class(es) #{invalid.join(", ")} do not respond to seed" if invalid.any?
seed_classes(classes)
end
end
def self.seed_primordial
if skip_seeding?
puts "** Seeding is skipped on startup. Unset SKIP_SEEDING to re-enable" # rubocop:disable Rails/Output
return
end
seed(PRIMORDIAL_SEEDABLE_CLASSES)
end
def self.seed_rest
return if skip_seeding?
seed(OTHER_SEEDABLE_CLASSES + seedable_plugin_classes)
end
# Returns whether or not a primordial seed has completed.
def self.seeded_primordially?
# While not technically accurate, as someone could just insert a record
# directly, this is the simplest check at the moment to guess whether or not
# a primordial seed has completed.
MiqDatabase.any? && MiqRegion.in_my_region.any? && MiqServer.in_my_region.any?
end
# Returns whether or not a full seed has completed.
def self.seeded?
# While not technically accurate, as someone could just insert a record
# directly, this is the simplest check at the moment to guess whether or not
# a full seed has completed.
#
# MiqAction was chosen because it cannot be added by a user directly.
seeded_primordially? && MiqAction.in_my_region.any?
end
def self.skip_seeding?
ENV['SKIP_SEEDING'] && seeded_primordially?
end
private_class_method :skip_seeding?
def self.seed_classes(classes)
_log.info("Seeding...")
lock_timeout = (ENV["SEEDING_LOCK_TIMEOUT"].presence || 10.minutes).to_i
total = Benchmark.ms do
# Only 1 machine can go through this at a time
MiqDatabase.with_lock(lock_timeout) do
classes.each do |c|
_log.info("Seeding #{c}...")
ms = Benchmark.ms { c.seed }
_log.info("Seeding #{c}... Complete in #{ms}ms")
end
end
end
_log.info("Seeding... Complete in #{total}ms")
rescue Timeout::Error
_log.error("Seeding... Timed out after #{lock_timeout} seconds")
raise
rescue StandardError => err
_log.log_backtrace(err)
raise
end
private_class_method :seed_classes
def self.host
ActiveRecord::Base.configurations.find_db_config(Rails.env).host
end
def self.local?
host.blank? || ["localhost", "localhost.localdomain", "127.0.0.1", "0.0.0.0"].include?(host)
end
# Determines the average time to the database in milliseconds
def self.ping(connection = ActiveRecord::Base.connection)
query = "SELECT 1"
Benchmark.realtime { 10.times { connection.select_value(query) } } / 10 * 1000
end
def self.raise_server_event(event)
msg = "Server IP: #{MiqServer.my_server.ipaddress}, Server Host Name: #{MiqServer.my_server.hostname}"
MiqEvent.raise_evm_event_queue(MiqServer.my_server, event, :event_details => msg)
end
def self.restart_failover_monitor_service
service = LinuxAdmin::Service.new("evm-failover-monitor")
service.restart if service.running?
end
def self.restart_failover_monitor_service_queue
MiqQueue.put(
:class_name => name,
:method_name => 'restart_failover_monitor_service',
:role => 'database_operations',
:zone => nil
)
end
def self.run_failover_monitor(monitor = nil)
require 'manageiq-postgres_ha_admin'
ManageIQ::PostgresHaAdmin.logger = Vmdb.logger
monitor ||= ManageIQ::PostgresHaAdmin::FailoverMonitor.new(Rails.root.join("config/ha_admin.yml"))
configure_rails_handler(monitor)
configure_logical_replication_handlers(monitor)
_log.info("Starting database failover monitor")
monitor.monitor_loop
end
def self.configure_rails_handler(monitor)
file_path = Rails.root.join("config/database.yml")
rails_handler = ManageIQ::PostgresHaAdmin::RailsConfigHandler.new(:file_path => file_path, :environment => Rails.env)
_log.info("Configuring database failover for #{file_path}'s #{Rails.env} environment")
rails_handler.after_failover do |_new_conn_info|
# refresh the rails connection info after the config handler changed database.yml
begin
ActiveRecord::Base.remove_connection
rescue PG::Error
# We expect this to fail because it cannot access the database in the cached config
end
ActiveRecord::Base.establish_connection(Rails.application.config.database_configuration[Rails.env])
raise_server_event("db_failover_executed")
end
monitor.add_handler(rails_handler)
end
private_class_method :configure_rails_handler
def self.configure_logical_replication_handlers(monitor)
return unless MiqServer.my_server.has_active_role?("database_operations")
local_db_conninfo = ActiveRecord::Base.connection.raw_connection.conninfo_hash.delete_blanks
PglogicalSubscription.all.each do |s|
handler = ManageIQ::PostgresHaAdmin::LogicalReplicationConfigHandler.new(:subscription => s.id, :conn_info => local_db_conninfo)
_log.info("Configuring database failover for replication subscription #{s.id} ")
handler.after_failover do |new_conn_info|
s.delete
PglogicalSubscription.new(new_conn_info.slice(:dbname, :host, :user, :password, :port)).save
end
monitor.add_handler(handler)
end
end
private_class_method :configure_logical_replication_handlers
end