manageiq-appliance_console 5.4.0 → 6.1.1

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.
@@ -0,0 +1,325 @@
1
+ require 'awesome_spawn'
2
+ require 'pathname'
3
+ require 'linux_admin'
4
+
5
+ module ManageIQ
6
+ module ApplianceConsole
7
+ class PostgresAdmin
8
+ def self.data_directory
9
+ Pathname.new(ENV.fetch("APPLIANCE_PG_DATA"))
10
+ end
11
+
12
+ def self.mount_point
13
+ Pathname.new(ENV.fetch("APPLIANCE_PG_MOUNT_POINT"))
14
+ end
15
+
16
+ def self.template_directory
17
+ Pathname.new(ENV.fetch("APPLIANCE_TEMPLATE_DIRECTORY"))
18
+ end
19
+
20
+ def self.service_name
21
+ ENV.fetch("APPLIANCE_PG_SERVICE")
22
+ end
23
+
24
+ def self.package_name
25
+ ENV.fetch('APPLIANCE_PG_PACKAGE_NAME')
26
+ end
27
+
28
+ # Unprivileged user to run postgresql
29
+ def self.user
30
+ "postgres".freeze
31
+ end
32
+
33
+ def self.group
34
+ user
35
+ end
36
+
37
+ def self.logical_volume_name
38
+ "lv_pg".freeze
39
+ end
40
+
41
+ def self.volume_group_name
42
+ "vg_data".freeze
43
+ end
44
+
45
+ def self.database_disk_filesystem
46
+ "xfs".freeze
47
+ end
48
+
49
+ def self.initialized?
50
+ !Dir[data_directory.join("*")].empty?
51
+ end
52
+
53
+ def self.service_running?
54
+ LinuxAdmin::Service.new(service_name).running?
55
+ end
56
+
57
+ def self.local_server_in_recovery?
58
+ data_directory.join("recovery.conf").exist?
59
+ end
60
+
61
+ def self.local_server_status
62
+ if service_running?
63
+ "running (#{local_server_in_recovery? ? "standby" : "primary"})"
64
+ elsif initialized?
65
+ "initialized and stopped"
66
+ else
67
+ "not initialized"
68
+ end
69
+ end
70
+
71
+ def self.logical_volume_path
72
+ Pathname.new("/dev").join(volume_group_name, logical_volume_name)
73
+ end
74
+
75
+ def self.database_size(opts)
76
+ result = run_command("psql", opts, :command => "SELECT pg_database_size('#{opts[:dbname]}');")
77
+ result.match(/^\s+([0-9]+)\n/)[1].to_i
78
+ end
79
+
80
+ def self.prep_data_directory
81
+ # initdb will fail if the database directory is not empty or not owned by the PostgresAdmin.user
82
+ FileUtils.mkdir(PostgresAdmin.data_directory) unless Dir.exist?(PostgresAdmin.data_directory)
83
+ FileUtils.chown_R(PostgresAdmin.user, PostgresAdmin.group, PostgresAdmin.data_directory)
84
+ FileUtils.rm_rf(PostgresAdmin.data_directory.children.map(&:to_s))
85
+ end
86
+
87
+ PG_DUMP_MAGIC = "PGDMP".force_encoding(Encoding::BINARY).freeze
88
+ def self.pg_dump_file?(file)
89
+ File.open(file, "rb") { |f| f.readpartial(5) } == PG_DUMP_MAGIC
90
+ end
91
+
92
+ BASE_BACKUP_MAGIC = "\037\213".force_encoding(Encoding::BINARY).freeze # just the first 2 bits of gzip magic
93
+ def self.base_backup_file?(file)
94
+ File.open(file, "rb") { |f| f.readpartial(2) } == BASE_BACKUP_MAGIC
95
+ end
96
+
97
+ def self.backup(opts)
98
+ backup_pg_compress(opts)
99
+ end
100
+
101
+ def self.restore(opts)
102
+ file = opts[:local_file]
103
+ backup_type = opts.delete(:backup_type)
104
+
105
+ case
106
+ when backup_type == :pgdump then restore_pg_dump(opts)
107
+ when backup_type == :basebackup then restore_pg_basebackup(file)
108
+ when pg_dump_file?(file) then restore_pg_dump(opts)
109
+ when base_backup_file?(file) then restore_pg_basebackup(file)
110
+ else
111
+ raise "#{file} is not a database backup"
112
+ end
113
+ end
114
+
115
+ def self.restore_pg_basebackup(file)
116
+ pg_service = LinuxAdmin::Service.new(service_name)
117
+
118
+ pg_service.stop
119
+ prep_data_directory
120
+
121
+ require 'rubygems/package'
122
+
123
+ # Using a Gem::Package instance for the #extract_tar_gz method, so we don't
124
+ # have to re-write all of that logic. Mostly making use of
125
+ # `Gem::Package::TarReader` + `Zlib::GzipReader` that is already part of
126
+ # rubygems/stdlib and integrated there.
127
+ unpacker = Gem::Package.new("obviously_not_a_gem")
128
+ File.open(file, IO::RDONLY | IO::NONBLOCK) do |backup_file|
129
+ unpacker.extract_tar_gz(backup_file, data_directory.to_s)
130
+ end
131
+
132
+ FileUtils.chown_R(PostgresAdmin.user, PostgresAdmin.group, PostgresAdmin.data_directory)
133
+
134
+ pg_service.start
135
+ file
136
+ end
137
+
138
+ def self.backup_pg_dump(opts)
139
+ opts = opts.dup
140
+ dbname = opts.delete(:dbname)
141
+
142
+ args = combine_command_args(opts, :format => "c", :file => opts[:local_file], nil => dbname)
143
+ args = handle_multi_value_pg_dump_args!(opts, args)
144
+
145
+ run_command_with_logging("pg_dump", opts, args)
146
+ opts[:local_file]
147
+ end
148
+
149
+ def self.backup_pg_compress(opts)
150
+ opts = opts.dup
151
+
152
+ # discard dbname as pg_basebackup does not connect to a specific database
153
+ opts.delete(:dbname)
154
+
155
+ path = Pathname.new(opts.delete(:local_file))
156
+ FileUtils.mkdir_p(path.dirname)
157
+
158
+ # Build commandline from AwesomeSpawn
159
+ args = {:z => nil, :format => "t", :wal_method => "fetch", :pgdata => "-"}
160
+ cmd = AwesomeSpawn.build_command_line("pg_basebackup", combine_command_args(opts, args))
161
+ logger.info("MIQ(#{name}.#{__method__}) Running command... #{cmd}")
162
+
163
+ # Run command in a separate thread
164
+ read, write = IO.pipe
165
+ error_path = Dir::Tmpname.create("") { |tmpname| tmpname }
166
+ process_thread = Process.detach(Kernel.spawn(pg_env(opts), cmd, :out => write, :err => error_path))
167
+ stream_reader = Thread.new { IO.copy_stream(read, path) } # Copy output to path
168
+ write.close
169
+
170
+ # Wait for them to finish
171
+ process_status = process_thread.value
172
+ stream_reader.join
173
+ read.close
174
+
175
+ handle_error(cmd, process_status.exitstatus, error_path)
176
+ path.to_s
177
+ end
178
+
179
+ def self.recreate_db(opts)
180
+ dbname = opts[:dbname]
181
+ opts = opts.merge(:dbname => 'postgres')
182
+ run_command("psql", opts, :command => "DROP DATABASE IF EXISTS #{dbname}")
183
+ run_command("psql", opts, :command => "CREATE DATABASE #{dbname} WITH OWNER = #{opts[:username] || 'root'} ENCODING = 'UTF8'")
184
+ end
185
+
186
+ def self.restore_pg_dump(opts)
187
+ recreate_db(opts)
188
+ args = { :verbose => nil, :exit_on_error => nil }
189
+
190
+ if File.pipe?(opts[:local_file])
191
+ cmd_args = combine_command_args(opts, args)
192
+ cmd = AwesomeSpawn.build_command_line("pg_restore", cmd_args)
193
+ error_path = Dir::Tmpname.create("") { |tmpname| tmpname }
194
+ spawn_args = { :err => error_path, :in => [opts[:local_file].to_s, "rb"] }
195
+
196
+ logger.info("MIQ(#{name}.#{__method__}) Running command... #{cmd}")
197
+ process_thread = Process.detach(Kernel.spawn(pg_env(opts), cmd, spawn_args))
198
+ process_status = process_thread.value
199
+
200
+ handle_error(cmd, process_status.exitstatus, error_path)
201
+ else
202
+ args[nil] = opts[:local_file]
203
+ run_command("pg_restore", opts, args)
204
+ end
205
+ opts[:local_file]
206
+ end
207
+
208
+ GC_DEFAULTS = {
209
+ :analyze => false,
210
+ :full => false,
211
+ :verbose => false,
212
+ :table => nil,
213
+ :dbname => nil,
214
+ :username => nil,
215
+ :reindex => false
216
+ }
217
+
218
+ GC_AGGRESSIVE_DEFAULTS = {
219
+ :analyze => true,
220
+ :full => true,
221
+ :verbose => false,
222
+ :table => nil,
223
+ :dbname => nil,
224
+ :username => nil,
225
+ :reindex => true
226
+ }
227
+
228
+ def self.gc(options = {})
229
+ options = (options[:aggressive] ? GC_AGGRESSIVE_DEFAULTS : GC_DEFAULTS).merge(options)
230
+
231
+ result = vacuum(options)
232
+ logger.info("MIQ(#{name}.#{__method__}) Output... #{result}") if result.to_s.length > 0
233
+
234
+ if options[:reindex]
235
+ result = reindex(options)
236
+ logger.info("MIQ(#{name}.#{__method__}) Output... #{result}") if result.to_s.length > 0
237
+ end
238
+ end
239
+
240
+ def self.vacuum(opts)
241
+ # TODO: Add a real exception here
242
+ raise "Vacuum requires database" unless opts[:dbname]
243
+
244
+ args = {}
245
+ args[:analyze] = nil if opts[:analyze]
246
+ args[:full] = nil if opts[:full]
247
+ args[:verbose] = nil if opts[:verbose]
248
+ args[:table] = opts[:table] if opts[:table]
249
+ run_command("vacuumdb", opts, args)
250
+ end
251
+
252
+ def self.reindex(opts)
253
+ args = {}
254
+ args[:table] = opts[:table] if opts[:table]
255
+ run_command("reindexdb", opts, args)
256
+ end
257
+
258
+ def self.run_command(cmd_str, opts, args)
259
+ run_command_with_logging(cmd_str, opts, combine_command_args(opts, args))
260
+ end
261
+
262
+ def self.run_command_with_logging(cmd_str, opts, params = {})
263
+ logger.info("MIQ(#{name}.#{__method__}) Running command... #{AwesomeSpawn.build_command_line(cmd_str, params)}")
264
+ AwesomeSpawn.run!(cmd_str, :params => params, :env => pg_env(opts)).output
265
+ end
266
+
267
+ class << self
268
+ # Temporary alias due to manageiq core stubbing this method
269
+ alias runcmd_with_logging run_command_with_logging
270
+ end
271
+
272
+ private_class_method def self.combine_command_args(opts, args)
273
+ default_args = {:no_password => nil}
274
+ default_args[:dbname] = opts[:dbname] if opts[:dbname]
275
+ default_args[:username] = opts[:username] if opts[:username]
276
+ default_args[:host] = opts[:hostname] if opts[:hostname]
277
+ default_args[:port] = opts[:port] if opts[:port]
278
+ default_args.merge(args)
279
+ end
280
+
281
+ private_class_method def self.logger
282
+ ManageIQ::ApplianceConsole.logger
283
+ end
284
+
285
+ private_class_method def self.pg_env(opts)
286
+ {
287
+ "PGUSER" => opts[:username],
288
+ "PGPASSWORD" => opts[:password]
289
+ }.delete_blanks
290
+ end
291
+ # rubocop:disable Style/SymbolArray
292
+ PG_DUMP_MULTI_VALUE_ARGS = [
293
+ :t, :table, :T, :exclude_table, :"exclude-table", :exclude_table_data, :"exclude-table-data",
294
+ :n, :schema, :N, :exclude_schema, :"exclude-schema"
295
+ ].freeze
296
+ # rubocop:enable Style/SymbolArray
297
+ #
298
+ # NOTE: Potentially mutates opts hash (args becomes new array and not
299
+ # mutated by this method)
300
+ private_class_method def self.handle_multi_value_pg_dump_args!(opts, args)
301
+ if opts.keys.any? { |key| PG_DUMP_MULTI_VALUE_ARGS.include?(key) }
302
+ args = args.to_a
303
+ PG_DUMP_MULTI_VALUE_ARGS.each do |table_key|
304
+ next unless opts.key?(table_key)
305
+ table_val = opts.delete(table_key)
306
+ args += Array.wrap(table_val).map! { |v| [table_key, v] }
307
+ end
308
+ end
309
+ args
310
+ end
311
+
312
+ private_class_method def self.handle_error(cmd, exit_status, error_path)
313
+ if exit_status != 0
314
+ result = AwesomeSpawn::CommandResult.new(cmd, "", File.read(error_path), exit_status)
315
+ message = AwesomeSpawn::CommandResultError.default_message(cmd, exit_status)
316
+ logger.error("AwesomeSpawn: #{message}")
317
+ logger.error("AwesomeSpawn: #{result.error}")
318
+ raise AwesomeSpawn::CommandResultError.new(message, result)
319
+ end
320
+ ensure
321
+ File.delete(error_path) if File.exist?(error_path)
322
+ end
323
+ end
324
+ end
325
+ end
@@ -1,6 +1,6 @@
1
1
  # TODO: add appropriate requires instead of depending on appliance_console.rb.
2
2
  # TODO: Further refactor these unrelated methods.
3
- require "util/postgres_admin"
3
+ require "manageiq/appliance_console/postgres_admin"
4
4
  require "awesome_spawn"
5
5
 
6
6
  module ManageIQ
@@ -62,6 +62,50 @@ module ApplianceConsole
62
62
  say(" " + h + ': ' + (Net::Ping::External.new(h).ping ? 'Success!' : 'Failure, Check network settings and IP address or hostname provided.'))
63
63
  end
64
64
  end
65
+
66
+ def self.disk_usage(file = nil)
67
+ file_arg = file
68
+ file_arg = "-l" if file.nil? || file == ""
69
+
70
+ unless file_arg == "-l" || File.exist?(file)
71
+ raise "file #{file} does not exist"
72
+ end
73
+
74
+ # Collect bytes
75
+ result = AwesomeSpawn.run!("df", :params => ["-T", "-P", file_arg]).output.lines.each_with_object([]) do |line, array|
76
+ lArray = line.strip.split(" ")
77
+ next if lArray.length != 7
78
+ fsname, type, total, used, free, used_percentage, mount_point = lArray
79
+ next unless total =~ /[0-9]+/
80
+ next if array.detect { |hh| hh[:filesystem] == fsname }
81
+
82
+ array << {
83
+ :filesystem => fsname,
84
+ :type => type,
85
+ :total_bytes => total.to_i * 1024,
86
+ :used_bytes => used.to_i * 1024,
87
+ :available_bytes => free.to_i * 1024,
88
+ :used_bytes_percent => used_percentage.chomp("%").to_i,
89
+ :mount_point => mount_point,
90
+ }
91
+ end
92
+
93
+ # Collect inodes
94
+ AwesomeSpawn.run!("df", :params => ["-T", "-P", "-i", file_arg]).output.lines.each do |line|
95
+ lArray = line.strip.split(" ")
96
+ next if lArray.length != 7
97
+ fsname, _type, total, used, free, used_percentage, _mount_point = lArray
98
+ next unless total =~ /[0-9]+/
99
+ h = result.detect { |hh| hh[:filesystem] == fsname }
100
+ next if h.nil?
101
+
102
+ h[:total_inodes] = total.to_i
103
+ h[:used_inodes] = used.to_i
104
+ h[:available_inodes] = free.to_i
105
+ h[:used_inodes_percent] = used_percentage.chomp("%").to_i
106
+ end
107
+ result
108
+ end
65
109
  end
66
110
  end
67
111
  end
@@ -1,5 +1,5 @@
1
1
  module ManageIQ
2
2
  module ApplianceConsole
3
- VERSION = '5.4.0'.freeze
3
+ VERSION = '6.1.1'.freeze
4
4
  end
5
5
  end
@@ -11,10 +11,6 @@ module ManageIQ
11
11
  def self.logger
12
12
  @logger ||= ManageIQ::ApplianceConsole::Logger.instance
13
13
  end
14
-
15
- def self.logger=(logger)
16
- @logger = logger
17
- end
18
14
  end
19
15
  end
20
16
 
@@ -24,7 +20,6 @@ require 'manageiq/appliance_console/logger'
24
20
  require 'manageiq/appliance_console/logging'
25
21
  require 'manageiq/appliance_console/prompts'
26
22
 
27
- require 'manageiq-gems-pending'
28
23
  require 'highline'
29
24
 
30
25
  require 'manageiq/appliance_console/auth_utilities'
@@ -43,7 +38,8 @@ require 'manageiq/appliance_console/internal_database_configuration'
43
38
  require 'manageiq/appliance_console/key_configuration'
44
39
  require 'manageiq/appliance_console/logfile_configuration'
45
40
  require 'manageiq/appliance_console/logical_volume_management'
46
- require 'manageiq/appliance_console/messaging_configuration'
41
+ require 'manageiq/appliance_console/message_configuration_client'
42
+ require 'manageiq/appliance_console/message_configuration_server'
47
43
  require 'manageiq/appliance_console/oidc_authentication'
48
44
  require 'manageiq/appliance_console/principal'
49
45
  require 'manageiq/appliance_console/saml_authentication'
@@ -28,7 +28,7 @@ en:
28
28
  app_config: Configure Application
29
29
  db_replication: Configure Database Replication
30
30
  log_config: Logfile Configuration
31
- failover_monitor: Configure Application Database Failover Monitor
31
+ failover_monitor: Control Application Database Failover Monitor
32
32
  httpdauth: Configure External Authentication (httpd)
33
33
  extauth_opts: Update External Authentication Options
34
34
  evmstop: Stop EVM Server Processes
@@ -17,7 +17,7 @@ en:
17
17
  dbrestore: Restore Database From Backup
18
18
  app_config: Configure Application
19
19
  db_replication: Configure Database Replication
20
- failover_monitor: Configure Application Database Failover Monitor
20
+ failover_monitor: Control Application Database Failover Monitor
21
21
  evmstop: Stop EVM Server Processes
22
22
  key_gen: Generate Custom Encryption Key
23
23
  evmstart: Start EVM Server Processes
@@ -20,8 +20,8 @@ Gem::Specification.new do |spec|
20
20
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
21
21
  spec.require_paths = ["lib"]
22
22
 
23
- spec.add_runtime_dependency "activerecord", ">= 4.2.2"
24
- spec.add_runtime_dependency "activesupport", ">= 4.2.2"
23
+ spec.add_runtime_dependency "activerecord", "~> 6.0.4", ">= 6.0.4.1"
24
+ spec.add_runtime_dependency "activesupport", "~> 6.0.4", ">= 6.0.4.1"
25
25
  spec.add_runtime_dependency "awesome_spawn", "~> 1.4"
26
26
  spec.add_runtime_dependency "bcrypt", "~> 3.1.10"
27
27
  spec.add_runtime_dependency "bcrypt_pbkdf", ">= 1.0", "< 2.0"
@@ -33,10 +33,10 @@ Gem::Specification.new do |spec|
33
33
  spec.add_runtime_dependency "optimist", "~> 3.0"
34
34
  spec.add_runtime_dependency "pg"
35
35
  spec.add_runtime_dependency "rbnacl", ">= 3.2", "< 5.0"
36
- spec.add_runtime_dependency "rbnacl-libsodium"
37
36
 
38
37
  spec.add_development_dependency "bundler"
39
38
  spec.add_development_dependency "codeclimate-test-reporter", "~> 1.0.0"
39
+ spec.add_development_dependency "manageiq-style"
40
40
  spec.add_development_dependency "rake"
41
41
  spec.add_development_dependency "rspec", "~> 3.0"
42
42
  spec.add_development_dependency "rubocop"