Class: ManageIQ::ApplianceConsole::PostgresAdmin
- Inherits:
-
Object
- Object
- ManageIQ::ApplianceConsole::PostgresAdmin
- Defined in:
- lib/manageiq/appliance_console/postgres_admin.rb
Constant Summary collapse
- PG_DUMP_MAGIC =
"PGDMP".force_encoding(Encoding::BINARY).freeze
- BASE_BACKUP_MAGIC =
just the first 2 bits of gzip magic
"\037\213".force_encoding(Encoding::BINARY).freeze
- GC_DEFAULTS =
{ :analyze => false, :full => false, :verbose => false, :table => nil, :dbname => nil, :username => nil, :reindex => false }
- GC_AGGRESSIVE_DEFAULTS =
{ :analyze => true, :full => true, :verbose => false, :table => nil, :dbname => nil, :username => nil, :reindex => true }
- PG_DUMP_MULTI_VALUE_ARGS =
rubocop:disable Style/SymbolArray
[ :t, :table, :T, :exclude_table, :"exclude-table", :exclude_table_data, :"exclude-table-data", :n, :schema, :N, :exclude_schema, :"exclude-schema" ].freeze
Class Method Summary collapse
- .backup(opts) ⇒ Object
- .backup_pg_compress(opts) ⇒ Object
- .backup_pg_dump(opts) ⇒ Object
- .base_backup_file?(file) ⇒ Boolean
- .data_directory ⇒ Object
- .database_disk_filesystem ⇒ Object
- .database_size(opts) ⇒ Object
- .gc(options = {}) ⇒ Object
- .group ⇒ Object
- .initialized? ⇒ Boolean
- .local_server_in_recovery? ⇒ Boolean
- .local_server_status ⇒ Object
- .logical_volume_name ⇒ Object
- .logical_volume_path ⇒ Object
- .mount_point ⇒ Object
- .package_name ⇒ Object
- .pg_dump_file?(file) ⇒ Boolean
- .prep_data_directory ⇒ Object
- .recreate_db(opts) ⇒ Object
- .reindex(opts) ⇒ Object
- .restore(opts) ⇒ Object
- .restore_pg_basebackup(file) ⇒ Object
- .restore_pg_dump(opts) ⇒ Object
- .run_command(cmd_str, opts, args) ⇒ Object
- .run_command_with_logging(cmd_str, opts, params = {}) ⇒ Object (also: runcmd_with_logging)
- .service_name ⇒ Object
- .service_running? ⇒ Boolean
- .template_directory ⇒ Object
-
.user ⇒ Object
Unprivileged user to run postgresql.
- .vacuum(opts) ⇒ Object
- .volume_group_name ⇒ Object
Class Method Details
.backup(opts) ⇒ Object
97 98 99 |
# File 'lib/manageiq/appliance_console/postgres_admin.rb', line 97 def self.backup(opts) backup_pg_compress(opts) end |
.backup_pg_compress(opts) ⇒ Object
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 |
# File 'lib/manageiq/appliance_console/postgres_admin.rb', line 149 def self.backup_pg_compress(opts) opts = opts.dup # discard dbname as pg_basebackup does not connect to a specific database opts.delete(:dbname) path = Pathname.new(opts.delete(:local_file)) FileUtils.mkdir_p(path.dirname) # Build commandline from AwesomeSpawn args = {:z => nil, :format => "t", :wal_method => "fetch", :pgdata => "-"} cmd = AwesomeSpawn.build_command_line("pg_basebackup", combine_command_args(opts, args)) logger.info("MIQ(#{name}.#{__method__}) Running command... #{cmd}") # Run command in a separate thread read, write = IO.pipe error_path = Dir::Tmpname.create("") { |tmpname| tmpname } process_thread = Process.detach(Kernel.spawn(pg_env(opts), cmd, :out => write, :err => error_path)) stream_reader = Thread.new { IO.copy_stream(read, path) } # Copy output to path write.close # Wait for them to finish process_status = process_thread.value stream_reader.join read.close handle_error(cmd, process_status.exitstatus, error_path) path.to_s end |
.backup_pg_dump(opts) ⇒ Object
138 139 140 141 142 143 144 145 146 147 |
# File 'lib/manageiq/appliance_console/postgres_admin.rb', line 138 def self.backup_pg_dump(opts) opts = opts.dup dbname = opts.delete(:dbname) args = combine_command_args(opts, :format => "c", :file => opts[:local_file], nil => dbname) args = handle_multi_value_pg_dump_args!(opts, args) run_command_with_logging("pg_dump", opts, args) opts[:local_file] end |
.base_backup_file?(file) ⇒ Boolean
93 94 95 |
# File 'lib/manageiq/appliance_console/postgres_admin.rb', line 93 def self.base_backup_file?(file) File.open(file, "rb") { |f| f.readpartial(2) } == BASE_BACKUP_MAGIC end |
.data_directory ⇒ Object
8 9 10 |
# File 'lib/manageiq/appliance_console/postgres_admin.rb', line 8 def self.data_directory Pathname.new(ENV.fetch("APPLIANCE_PG_DATA")) end |
.database_disk_filesystem ⇒ Object
45 46 47 |
# File 'lib/manageiq/appliance_console/postgres_admin.rb', line 45 def self.database_disk_filesystem "xfs".freeze end |
.database_size(opts) ⇒ Object
75 76 77 78 |
# File 'lib/manageiq/appliance_console/postgres_admin.rb', line 75 def self.database_size(opts) result = run_command("psql", opts, :command => "SELECT pg_database_size('#{opts[:dbname]}');") result.match(/^\s+([0-9]+)\n/)[1].to_i end |
.gc(options = {}) ⇒ Object
228 229 230 231 232 233 234 235 236 237 238 |
# File 'lib/manageiq/appliance_console/postgres_admin.rb', line 228 def self.gc( = {}) = ([:aggressive] ? GC_AGGRESSIVE_DEFAULTS : GC_DEFAULTS).merge() result = vacuum() logger.info("MIQ(#{name}.#{__method__}) Output... #{result}") if result.to_s.length > 0 if [:reindex] result = reindex() logger.info("MIQ(#{name}.#{__method__}) Output... #{result}") if result.to_s.length > 0 end end |
.group ⇒ Object
33 34 35 |
# File 'lib/manageiq/appliance_console/postgres_admin.rb', line 33 def self.group user end |
.initialized? ⇒ Boolean
49 50 51 |
# File 'lib/manageiq/appliance_console/postgres_admin.rb', line 49 def self.initialized? !Dir[data_directory.join("*")].empty? end |
.local_server_in_recovery? ⇒ Boolean
57 58 59 |
# File 'lib/manageiq/appliance_console/postgres_admin.rb', line 57 def self.local_server_in_recovery? data_directory.join("recovery.conf").exist? end |
.local_server_status ⇒ Object
61 62 63 64 65 66 67 68 69 |
# File 'lib/manageiq/appliance_console/postgres_admin.rb', line 61 def self.local_server_status if service_running? "running (#{local_server_in_recovery? ? "standby" : "primary"})" elsif initialized? "initialized and stopped" else "not initialized" end end |
.logical_volume_name ⇒ Object
37 38 39 |
# File 'lib/manageiq/appliance_console/postgres_admin.rb', line 37 def self.logical_volume_name "lv_pg".freeze end |
.logical_volume_path ⇒ Object
71 72 73 |
# File 'lib/manageiq/appliance_console/postgres_admin.rb', line 71 def self.logical_volume_path Pathname.new("/dev").join(volume_group_name, logical_volume_name) end |
.mount_point ⇒ Object
12 13 14 |
# File 'lib/manageiq/appliance_console/postgres_admin.rb', line 12 def self.mount_point Pathname.new(ENV.fetch("APPLIANCE_PG_MOUNT_POINT")) end |
.package_name ⇒ Object
24 25 26 |
# File 'lib/manageiq/appliance_console/postgres_admin.rb', line 24 def self.package_name ENV.fetch('APPLIANCE_PG_PACKAGE_NAME') end |
.pg_dump_file?(file) ⇒ Boolean
88 89 90 |
# File 'lib/manageiq/appliance_console/postgres_admin.rb', line 88 def self.pg_dump_file?(file) File.open(file, "rb") { |f| f.readpartial(5) } == PG_DUMP_MAGIC end |
.prep_data_directory ⇒ Object
80 81 82 83 84 85 |
# File 'lib/manageiq/appliance_console/postgres_admin.rb', line 80 def self.prep_data_directory # initdb will fail if the database directory is not empty or not owned by the PostgresAdmin.user FileUtils.mkdir(PostgresAdmin.data_directory) unless Dir.exist?(PostgresAdmin.data_directory) FileUtils.chown_R(PostgresAdmin.user, PostgresAdmin.group, PostgresAdmin.data_directory) FileUtils.rm_rf(PostgresAdmin.data_directory.children.map(&:to_s)) end |
.recreate_db(opts) ⇒ Object
179 180 181 182 183 184 |
# File 'lib/manageiq/appliance_console/postgres_admin.rb', line 179 def self.recreate_db(opts) dbname = opts[:dbname] opts = opts.merge(:dbname => 'postgres') run_command("psql", opts, :command => "DROP DATABASE IF EXISTS #{dbname}") run_command("psql", opts, :command => "CREATE DATABASE #{dbname} WITH OWNER = #{opts[:username] || 'root'} ENCODING = 'UTF8'") end |
.reindex(opts) ⇒ Object
252 253 254 255 256 |
# File 'lib/manageiq/appliance_console/postgres_admin.rb', line 252 def self.reindex(opts) args = {} args[:table] = opts[:table] if opts[:table] run_command("reindexdb", opts, args) end |
.restore(opts) ⇒ Object
101 102 103 104 105 106 107 108 109 110 111 112 113 |
# File 'lib/manageiq/appliance_console/postgres_admin.rb', line 101 def self.restore(opts) file = opts[:local_file] backup_type = opts.delete(:backup_type) case when backup_type == :pgdump then restore_pg_dump(opts) when backup_type == :basebackup then restore_pg_basebackup(file) when pg_dump_file?(file) then restore_pg_dump(opts) when base_backup_file?(file) then restore_pg_basebackup(file) else raise "#{file} is not a database backup" end end |
.restore_pg_basebackup(file) ⇒ Object
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'lib/manageiq/appliance_console/postgres_admin.rb', line 115 def self.restore_pg_basebackup(file) pg_service = LinuxAdmin::Service.new(service_name) pg_service.stop prep_data_directory require 'rubygems/package' # Using a Gem::Package instance for the #extract_tar_gz method, so we don't # have to re-write all of that logic. Mostly making use of # `Gem::Package::TarReader` + `Zlib::GzipReader` that is already part of # rubygems/stdlib and integrated there. unpacker = Gem::Package.new("obviously_not_a_gem") File.open(file, IO::RDONLY | IO::NONBLOCK) do |backup_file| unpacker.extract_tar_gz(backup_file, data_directory.to_s) end FileUtils.chown_R(PostgresAdmin.user, PostgresAdmin.group, PostgresAdmin.data_directory) pg_service.start file end |
.restore_pg_dump(opts) ⇒ Object
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
# File 'lib/manageiq/appliance_console/postgres_admin.rb', line 186 def self.restore_pg_dump(opts) recreate_db(opts) args = { :verbose => nil, :exit_on_error => nil } if File.pipe?(opts[:local_file]) cmd_args = combine_command_args(opts, args) cmd = AwesomeSpawn.build_command_line("pg_restore", cmd_args) error_path = Dir::Tmpname.create("") { |tmpname| tmpname } spawn_args = { :err => error_path, :in => [opts[:local_file].to_s, "rb"] } logger.info("MIQ(#{name}.#{__method__}) Running command... #{cmd}") process_thread = Process.detach(Kernel.spawn(pg_env(opts), cmd, spawn_args)) process_status = process_thread.value handle_error(cmd, process_status.exitstatus, error_path) else args[nil] = opts[:local_file] run_command("pg_restore", opts, args) end opts[:local_file] end |
.run_command(cmd_str, opts, args) ⇒ Object
258 259 260 |
# File 'lib/manageiq/appliance_console/postgres_admin.rb', line 258 def self.run_command(cmd_str, opts, args) run_command_with_logging(cmd_str, opts, combine_command_args(opts, args)) end |
.run_command_with_logging(cmd_str, opts, params = {}) ⇒ Object Also known as: runcmd_with_logging
262 263 264 265 |
# File 'lib/manageiq/appliance_console/postgres_admin.rb', line 262 def self.run_command_with_logging(cmd_str, opts, params = {}) logger.info("MIQ(#{name}.#{__method__}) Running command... #{AwesomeSpawn.build_command_line(cmd_str, params)}") AwesomeSpawn.run!(cmd_str, :params => params, :env => pg_env(opts)).output end |
.service_name ⇒ Object
20 21 22 |
# File 'lib/manageiq/appliance_console/postgres_admin.rb', line 20 def self.service_name ENV.fetch("APPLIANCE_PG_SERVICE") end |
.service_running? ⇒ Boolean
53 54 55 |
# File 'lib/manageiq/appliance_console/postgres_admin.rb', line 53 def self.service_running? LinuxAdmin::Service.new(service_name).running? end |
.template_directory ⇒ Object
16 17 18 |
# File 'lib/manageiq/appliance_console/postgres_admin.rb', line 16 def self.template_directory Pathname.new(ENV.fetch("APPLIANCE_TEMPLATE_DIRECTORY")) end |
.user ⇒ Object
Unprivileged user to run postgresql
29 30 31 |
# File 'lib/manageiq/appliance_console/postgres_admin.rb', line 29 def self.user "postgres".freeze end |
.vacuum(opts) ⇒ Object
240 241 242 243 244 245 246 247 248 249 250 |
# File 'lib/manageiq/appliance_console/postgres_admin.rb', line 240 def self.vacuum(opts) # TODO: Add a real exception here raise "Vacuum requires database" unless opts[:dbname] args = {} args[:analyze] = nil if opts[:analyze] args[:full] = nil if opts[:full] args[:verbose] = nil if opts[:verbose] args[:table] = opts[:table] if opts[:table] run_command("vacuumdb", opts, args) end |
.volume_group_name ⇒ Object
41 42 43 |
# File 'lib/manageiq/appliance_console/postgres_admin.rb', line 41 def self.volume_group_name "vg_data".freeze end |