manageiq-postgres_ha_admin 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2ea7e89193add948ecd827491eae6c7e055c20a6
4
- data.tar.gz: d416a3b5fd696ca582e13e360b6ea00af762aa58
3
+ metadata.gz: 00a1ceacb4bfb538a4cdeb7b7b1bc9d60e7ca986
4
+ data.tar.gz: 7ace66668500095929cb06389dd98ed77ec9c2ce
5
5
  SHA512:
6
- metadata.gz: a76ffe81cb4c0f218a5374a333f2458da83c59ff14cbc285ba9787c58e4362568c7c0303d794f587098ae3aa4b6fe144fc44c6ef1270e990e1581e5c83521394
7
- data.tar.gz: 2aaeb1824891fad95440f473ccd864a048538ddcdf37832a9d3b8c492d29d81e1a1d353ee070aca4861136a5cea8a2152bd26bf90c94c989df29e2f1bd97e088
6
+ metadata.gz: ee0f5eb9fb4d357e9d413166c9251cb67191bc7110d2b275eb6d22055a923a1e85f9313d023f13ff156690a82d76aa05fca79dc60d257b79c246db80d0338fa8
7
+ data.tar.gz: 6444e2e66980ee5c3f498ee292e47f9cf2a88f4169106690fa41a0706f14b1a8e802a2020d06180577920025cd7212dc3801e2a37bb577f0eee35ab165352a85
@@ -5,6 +5,16 @@ This project adheres to [Semantic Versioning](http://semver.org/).
5
5
 
6
6
  ## [Unreleased]
7
7
 
8
+ ## [3.0.0] - 2018-09-05
9
+
10
+ ### Added
11
+ - Allow users of the gem to specify a logger object [#7](https://github.com/ManageIQ/manageiq-postgres_ha_admin/pull/7)
12
+
13
+ ### Changed
14
+ - Make sources of database connection info pluggable [#8](https://github.com/ManageIQ/manageiq-postgres_ha_admin/pull/8)
15
+ - Improve FailoverDatabases/ServerStore class [#9](https://github.com/ManageIQ/manageiq-postgres_ha_admin/pull/9)
16
+ - Make failover monitor generic [#10](https://github.com/ManageIQ/manageiq-postgres_ha_admin/pull/10)
17
+
8
18
  ## [2.0.0] - 2018-08-01
9
19
 
10
20
  ### Added
@@ -14,5 +24,6 @@ This project adheres to [Semantic Versioning](http://semver.org/).
14
24
  ### Changed
15
25
  - Make changes for upgrading repmgr to version 4 [#4](https://github.com/ManageIQ/manageiq-postgres_ha_admin/pull/4)
16
26
 
17
- [Unreleased]: https://github.com/ManageIQ/manageiq-postgres_ha_admin/compare/v2.0.0...master
27
+ [Unreleased]: https://github.com/ManageIQ/manageiq-postgres_ha_admin/compare/v3.0.0...master
28
+ [3.0.0]: https://github.com/ManageIQ/manageiq-postgres_ha_admin/compare/v2.0.0...v3.0.0
18
29
  [2.0.0]: https://github.com/ManageIQ/manageiq-postgres_ha_admin/compare/v1.0.0...v2.0.0
@@ -1,4 +1,23 @@
1
1
  require 'manageiq/postgres_ha_admin/version'
2
- require 'manageiq/postgres_ha_admin/database_yml'
3
- require 'manageiq/postgres_ha_admin/failover_databases'
2
+
3
+ require 'manageiq/postgres_ha_admin/logging'
4
+ require 'manageiq/postgres_ha_admin/null_logger'
5
+
6
+ require 'manageiq/postgres_ha_admin/server_store'
4
7
  require 'manageiq/postgres_ha_admin/failover_monitor'
8
+
9
+ require 'manageiq/postgres_ha_admin/config_handler'
10
+ require 'manageiq/postgres_ha_admin/config_handler/rails_config_handler'
11
+ require 'manageiq/postgres_ha_admin/config_handler/pglogical_config_handler'
12
+
13
+ module ManageIQ
14
+ module PostgresHaAdmin
15
+ class << self
16
+ attr_writer :logger
17
+ end
18
+
19
+ def self.logger
20
+ @logger ||= NullLogger.new
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,35 @@
1
+ module ManageIQ
2
+ module PostgresHaAdmin
3
+ class ConfigHandler
4
+ def name
5
+ "Config Handler"
6
+ end
7
+
8
+ def read
9
+ raise NotImplementedError
10
+ end
11
+
12
+ def write(_conninfo)
13
+ raise NotImplementedError
14
+ end
15
+
16
+ def before_failover(&block)
17
+ raise ArgumentError, "A block is required to set the before failover callback" unless block_given?
18
+ @before_failover_cb = block
19
+ end
20
+
21
+ def after_failover(&block)
22
+ raise ArgumentError, "A block is required to set the after failover callback" unless block_given?
23
+ @after_failover_cb = block
24
+ end
25
+
26
+ def do_before_failover
27
+ @before_failover_cb&.call
28
+ end
29
+
30
+ def do_after_failover(new_primary_conn_info)
31
+ @after_failover_cb&.call(new_primary_conn_info)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,36 @@
1
+ require 'pg'
2
+ require 'pg/dsn_parser'
3
+
4
+ module ManageIQ
5
+ module PostgresHaAdmin
6
+ class PglogicalConfigHandler < ConfigHandler
7
+ attr_reader :subscription, :conn_info
8
+
9
+ def initialize(options = {})
10
+ @subscription = options[:subscription]
11
+ @conn_info = options[:conn_info]
12
+ end
13
+
14
+ def name
15
+ "pglogical subscription #{subscription} Config Handler"
16
+ end
17
+
18
+ def read
19
+ conn = PG::Connection.open(@conn_info)
20
+ dsn = conn.exec_params(<<~SQL, [@subscription]).first["if_dsn"]
21
+ SELECT if_dsn
22
+ FROM pglogical.subscription s
23
+ JOIN pglogical.node_interface i
24
+ ON s.sub_origin_if = i.if_id
25
+ WHERE s.sub_name = $1
26
+ SQL
27
+ PG::DSNParser.new.parse(dsn)
28
+ end
29
+
30
+ def write(_params)
31
+ # Nothing to do here as the expectation is that the user will
32
+ # remove and re-add the subscription in the after failover callback
33
+ end
34
+ end
35
+ end
36
+ end
@@ -4,29 +4,33 @@ require 'fileutils'
4
4
 
5
5
  module ManageIQ
6
6
  module PostgresHaAdmin
7
- class DatabaseYml
8
- attr_reader :db_yml_file, :environment
7
+ class RailsConfigHandler < ConfigHandler
8
+ attr_reader :environment, :file_path
9
9
 
10
- def initialize(db_yml_file, environment)
11
- @db_yml_file = db_yml_file
12
- @environment = environment
10
+ def initialize(options = {})
11
+ @file_path = options[:file_path]
12
+ @environment = options[:environment]
13
13
  end
14
14
 
15
- def pg_params_from_database_yml
16
- rails_params_to_pg(YAML.load_file(db_yml_file)[environment])
15
+ def name
16
+ "Rails #{environment} Config Handler"
17
17
  end
18
18
 
19
- def update_database_yml(params)
20
- db_yml = YAML.load_file(db_yml_file)
19
+ def read
20
+ rails_params_to_pg(YAML.load_file(file_path)[environment])
21
+ end
22
+
23
+ def write(params)
24
+ db_yml = YAML.load_file(file_path)
21
25
  db_yml[environment].merge!(pg_parameters_to_rails(params))
22
26
  remove_empty(db_yml[environment])
23
27
 
24
- new_name = "#{db_yml_file}_#{Time.current.strftime("%d-%B-%Y_%H.%M.%S")}"
25
- FileUtils.copy(db_yml_file, new_name)
28
+ new_name = "#{file_path}_#{Time.current.strftime("%d-%B-%Y_%H.%M.%S")}"
29
+ FileUtils.copy(file_path, new_name)
26
30
  begin
27
- File.write(db_yml_file, db_yml.to_yaml)
31
+ File.write(file_path, db_yml.to_yaml)
28
32
  rescue
29
- FileUtils.mv(new_name, db_yml_file)
33
+ FileUtils.mv(new_name, file_path)
30
34
  raise
31
35
  end
32
36
  new_name
@@ -5,48 +5,46 @@ require 'linux_admin'
5
5
  module ManageIQ
6
6
  module PostgresHaAdmin
7
7
  class FailoverMonitor
8
- RAILS_ROOT = [
9
- Pathname.new("/var/www/miq/vmdb"),
10
- Pathname.new(File.expand_path(File.join(__dir__, "../..")))
11
- ].detect { |f| File.exist?(f) }
8
+ include Logging
9
+
12
10
  FAILOVER_ATTEMPTS = 10
13
11
  DB_CHECK_FREQUENCY = 300
14
12
  FAILOVER_CHECK_FREQUENCY = 60
15
- LOG_FILE = '/var/www/miq/vmdb/log/ha_admin.log'.freeze
16
13
  attr_accessor :failover_attempts, :db_check_frequency, :failover_check_frequency
14
+ attr_reader :config_handlers
17
15
 
18
- def initialize(db_yml_file: '/var/www/miq/vmdb/config/database.yml',
19
- failover_yml_file: '/var/www/miq/vmdb/config/failover_databases.yml',
20
- ha_admin_yml_file: '/var/www/miq/vmdb/config/ha_admin.yml',
21
- logger: nil,
22
- environment: 'production')
23
- if logger.respond_to?(:error, :info)
24
- @logger = logger
25
- else
26
- @logger = Logger.new(LOG_FILE)
27
- @logger.level = Logger::INFO
28
- end
29
- @database_yml = DatabaseYml.new(db_yml_file, environment)
30
- @failover_db = FailoverDatabases.new(failover_yml_file, @logger)
31
- initialize_settings(ha_admin_yml_file)
16
+ def initialize(config_path = "")
17
+ initialize_settings(config_path)
18
+ @config_handlers = []
32
19
  end
33
20
 
34
- def monitor
35
- connection = pg_connection(@database_yml.pg_params_from_database_yml)
36
- if connection
37
- @failover_db.update_failover_yml(connection)
38
- connection.finish
39
- return
40
- end
41
-
42
- @logger.error("Primary Database is not available. EVM server stop initiated. Starting to execute failover...")
43
- stop_evmserverd
21
+ def add_handler(handler)
22
+ @config_handlers << [handler, ServerStore.new]
23
+ end
44
24
 
45
- if execute_failover
46
- start_evmserverd
47
- raise_failover_event
48
- else
49
- @logger.error("Failover failed")
25
+ def monitor
26
+ config_handlers.each do |handler, server_store|
27
+ begin
28
+ connection = pg_connection(handler.read)
29
+ if connection
30
+ server_store.update_servers(connection)
31
+ connection.finish
32
+ next
33
+ end
34
+
35
+ logger.error("Primary Database is not available for #{handler.name}. Starting to execute failover...")
36
+ handler.do_before_failover
37
+
38
+ new_conn_info = execute_failover(handler, server_store)
39
+ if new_conn_info
40
+ handler.do_after_failover(new_conn_info)
41
+ else
42
+ logger.error("Failover failed")
43
+ end
44
+ rescue => e
45
+ logger.error("Received #{e.class} error while monitoring #{handler.name}: #{e.message}")
46
+ logger.error(e.backtrace)
47
+ end
50
48
  end
51
49
  end
52
50
 
@@ -55,59 +53,52 @@ module PostgresHaAdmin
55
53
  begin
56
54
  monitor
57
55
  rescue => err
58
- @logger.error("#{err.class}: #{err}")
59
- @logger.error(err.backtrace.join("\n"))
56
+ logger.error("#{err.class}: #{err}")
57
+ logger.error(err.backtrace.join("\n"))
60
58
  end
61
59
  sleep(db_check_frequency)
62
60
  end
63
61
  end
64
62
 
65
- def active_servers_conninfo
66
- servers = @failover_db.active_databases_conninfo_hash
67
- db_yml_params = @database_yml.pg_params_from_database_yml
68
- servers.map! { |info| db_yml_params.merge(info) }
69
- end
70
-
71
- def raise_failover_event
72
- require "awesome_spawn"
73
- AwesomeSpawn.run("rake evm:raise_server_event",
74
- :chdir => RAILS_ROOT,
75
- :params => ["--", {:event => "db_failover_executed"}])
63
+ def active_servers_conninfo(handler, server_store)
64
+ servers = server_store.connection_info_list
65
+ current_params = handler.read
66
+ servers.map! { |info| current_params.merge(info) }
76
67
  end
77
68
 
78
69
  private
79
70
 
80
71
  def initialize_settings(ha_admin_yml_file)
72
+ ha_admin_yml = {}
81
73
  begin
82
- ha_admin_yml = YAML.load_file(ha_admin_yml_file)
74
+ ha_admin_yml = YAML.load_file(ha_admin_yml_file) if File.exist?(ha_admin_yml_file)
83
75
  rescue SystemCallError, IOError => err
84
- ha_admin_yml = {}
85
- @logger.error("#{err.class}: #{err}")
86
- @logger.info("File not loaded: #{ha_admin_yml_file}. Default settings for failover will be used.")
76
+ logger.error("#{err.class}: #{err}")
77
+ logger.info("File not loaded: #{ha_admin_yml_file}. Default settings for failover will be used.")
87
78
  end
88
79
  @failover_attempts = ha_admin_yml['failover_attempts'] || FAILOVER_ATTEMPTS
89
80
  @db_check_frequency = ha_admin_yml['db_check_frequency'] || DB_CHECK_FREQUENCY
90
81
  @failover_check_frequency = ha_admin_yml['failover_check_frequency'] || FAILOVER_CHECK_FREQUENCY
91
- @logger.info("FAILOVER_ATTEMPTS=#{@failover_attempts} DB_CHECK_FREQUENCY=#{@db_check_frequency} FAILOVER_CHECK_FREQUENCY=#{@failover_check_frequency}")
82
+ logger.info("FAILOVER_ATTEMPTS=#{@failover_attempts} DB_CHECK_FREQUENCY=#{@db_check_frequency} FAILOVER_CHECK_FREQUENCY=#{@failover_check_frequency}")
92
83
  end
93
84
 
94
- def execute_failover
85
+ def execute_failover(handler, server_store)
95
86
  failover_attempts.times do
96
- with_each_standby_connection do |connection, params|
87
+ with_each_standby_connection(handler, server_store) do |connection, params|
97
88
  next if database_in_recovery?(connection)
98
- next unless @failover_db.host_is_repmgr_primary?(params[:host], connection)
99
- @logger.info("Failing over to server using conninfo: #{params.reject { |k, _v| k == :password }}")
100
- @failover_db.update_failover_yml(connection)
101
- @database_yml.update_database_yml(params)
102
- return true
89
+ next unless server_store.host_is_primary?(params[:host], connection)
90
+ logger.info("Failing over to server using conninfo: #{params.reject { |k, _v| k == :password }}")
91
+ server_store.update_servers(connection)
92
+ handler.write(params)
93
+ return params
103
94
  end
104
95
  sleep(failover_check_frequency)
105
96
  end
106
97
  false
107
98
  end
108
99
 
109
- def with_each_standby_connection
110
- active_servers_conninfo.each do |params|
100
+ def with_each_standby_connection(handler, server_store)
101
+ active_servers_conninfo(handler, server_store).each do |params|
111
102
  connection = pg_connection(params)
112
103
  next if connection.nil?
113
104
  begin
@@ -121,19 +112,10 @@ module PostgresHaAdmin
121
112
  def pg_connection(params)
122
113
  PG::Connection.open(params)
123
114
  rescue PG::Error => e
124
- @logger.error("Failed to establish PG connection: #{e.message}")
115
+ logger.error("Failed to establish PG connection: #{e.message}")
125
116
  nil
126
117
  end
127
118
 
128
- def start_evmserverd
129
- LinuxAdmin::Service.new("evmserverd").restart
130
- @logger.info("Starting EVM server from failover monitor")
131
- end
132
-
133
- def stop_evmserverd
134
- LinuxAdmin::Service.new("evmserverd").stop
135
- end
136
-
137
119
  # Checks if postgres database is in recovery mode
138
120
  #
139
121
  # @param pg_connection [PG::Connection] established pg connection
@@ -0,0 +1,9 @@
1
+ module ManageIQ
2
+ module PostgresHaAdmin
3
+ module Logging
4
+ def logger
5
+ ManageIQ::PostgresHaAdmin.logger
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,13 @@
1
+ require 'logger'
2
+
3
+ module ManageIQ
4
+ module PostgresHaAdmin
5
+ class NullLogger < Logger
6
+ def initialize(*_args)
7
+ end
8
+
9
+ def add(*_args, &_block)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -4,38 +4,38 @@ require 'pg/dsn_parser'
4
4
 
5
5
  module ManageIQ
6
6
  module PostgresHaAdmin
7
- class FailoverDatabases
7
+ class ServerStore
8
+ include Logging
9
+
8
10
  TABLE_NAME = "repmgr.nodes".freeze
9
11
 
10
- attr_reader :yml_file
12
+ attr_reader :servers
11
13
 
12
- def initialize(yml_file, logger)
13
- @yml_file = yml_file
14
- @logger = logger
14
+ def initialize
15
+ @servers = []
15
16
  end
16
17
 
17
- def active_databases_conninfo_hash
18
+ def connection_info_list
18
19
  valid_keys = PG::Connection.conndefaults_hash.keys + [:requiressl]
19
- active_databases.map! do |db_info|
20
+ servers.map! do |db_info|
20
21
  db_info.keep_if { |k, _v| valid_keys.include?(k) }
21
22
  end
22
23
  end
23
24
 
24
- def active_databases
25
- all_databases.select { |record| record[:active] }
26
- end
27
-
28
- def update_failover_yml(connection)
29
- arr_yml = query_repmgr(connection)
30
- File.write(yml_file, arr_yml.to_yaml) unless arr_yml.empty?
25
+ def update_servers(connection)
26
+ new_servers = query_repmgr(connection)
27
+ if servers_changed?(new_servers)
28
+ logger.info("Updating servers cache to #{new_servers}")
29
+ @servers = new_servers
30
+ end
31
31
  rescue IOError => err
32
- @logger.error("#{err.class}: #{err}")
33
- @logger.error(err.backtrace.join("\n"))
32
+ logger.error("#{err.class}: #{err}")
33
+ logger.error(err.backtrace.join("\n"))
34
34
  end
35
35
 
36
- def host_is_repmgr_primary?(host, connection)
36
+ def host_is_primary?(host, connection)
37
37
  query_repmgr(connection).each do |record|
38
- if record[:host] == host && entry_is_active_master?(record)
38
+ if record[:host] == host && record[:type] == 'primary'
39
39
  return true
40
40
  end
41
41
  end
@@ -44,10 +44,14 @@ module PostgresHaAdmin
44
44
 
45
45
  private
46
46
 
47
+ def servers_changed?(new_servers)
48
+ ((servers - new_servers) + (new_servers - servers)).any?
49
+ end
50
+
47
51
  def query_repmgr(connection)
48
52
  return [] unless table_exists?(connection, TABLE_NAME)
49
53
  result = []
50
- db_result = connection.exec("SELECT type, conninfo, active FROM #{TABLE_NAME}")
54
+ db_result = connection.exec("SELECT type, conninfo, active FROM #{TABLE_NAME} WHERE active")
51
55
  db_result.map_types!(PG::BasicTypeMapForResults.new(connection)).each do |record|
52
56
  dsn = PG::DSNParser.parse(record.delete("conninfo"))
53
57
  result << record.symbolize_keys.merge(dsn)
@@ -55,20 +59,11 @@ module PostgresHaAdmin
55
59
  db_result.clear
56
60
  result
57
61
  rescue PG::Error => err
58
- @logger.error("#{err.class}: #{err}")
59
- @logger.error(err.backtrace.join("\n"))
62
+ logger.error("#{err.class}: #{err}")
63
+ logger.error(err.backtrace.join("\n"))
60
64
  result
61
65
  end
62
66
 
63
- def entry_is_active_master?(record)
64
- record[:type] == 'primary' && record[:active]
65
- end
66
-
67
- def all_databases
68
- return [] unless File.exist?(yml_file)
69
- YAML.load_file(yml_file)
70
- end
71
-
72
67
  def table_exists?(connection, table_name)
73
68
  result = connection.exec("SELECT to_regclass('#{table_name}')").first
74
69
  !result['to_regclass'].nil?
@@ -1,5 +1,5 @@
1
1
  module ManageIQ
2
2
  module PostgresHaAdmin
3
- VERSION = '2.0.0'.freeze
3
+ VERSION = '3.0.0'.freeze
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: manageiq-postgres_ha_admin
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ManageIQ Developers
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-08-01 00:00:00.000000000 Z
11
+ date: 2018-09-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: awesome_spawn
@@ -152,8 +152,7 @@ dependencies:
152
152
  version: '0'
153
153
  description: ManageIQ Postgres H.A. Admin
154
154
  email:
155
- executables:
156
- - postgres_ha_admin
155
+ executables: []
157
156
  extensions: []
158
157
  extra_rdoc_files: []
159
158
  files:
@@ -170,11 +169,14 @@ files:
170
169
  - LICENSE.txt
171
170
  - README.md
172
171
  - Rakefile
173
- - bin/postgres_ha_admin
174
172
  - lib/manageiq-postgres_ha_admin.rb
175
- - lib/manageiq/postgres_ha_admin/database_yml.rb
176
- - lib/manageiq/postgres_ha_admin/failover_databases.rb
173
+ - lib/manageiq/postgres_ha_admin/config_handler.rb
174
+ - lib/manageiq/postgres_ha_admin/config_handler/pglogical_config_handler.rb
175
+ - lib/manageiq/postgres_ha_admin/config_handler/rails_config_handler.rb
177
176
  - lib/manageiq/postgres_ha_admin/failover_monitor.rb
177
+ - lib/manageiq/postgres_ha_admin/logging.rb
178
+ - lib/manageiq/postgres_ha_admin/null_logger.rb
179
+ - lib/manageiq/postgres_ha_admin/server_store.rb
178
180
  - lib/manageiq/postgres_ha_admin/version.rb
179
181
  - manageiq-postgres_ha_admin.gemspec
180
182
  homepage: https://github.com/ManageIQ/manageiq-postgres_ha_admin
@@ -1,7 +0,0 @@
1
- #!/bin/env ruby
2
-
3
- require 'bundler/setup'
4
- require 'manageiq-postgres_ha_admin'
5
-
6
- monitor = ManageIQ::PostgresHaAdmin::FailoverMonitor.new
7
- monitor.monitor_loop