Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add evm:db:dump:local and evm:db:dump:remote tasks #17483

Merged
merged 6 commits into from
May 31, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 62 additions & 43 deletions lib/evm_database_ops.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@

class EvmDatabaseOps
include Vmdb::Logging
BACKUP_TMP_FILE = "/tmp/miq_backup"
BACKUP_TMP_FILE = "/tmp/miq_backup".freeze
DUMP_TMP_FILE = "/tmp/miq_pg_dump".freeze

DEFAULT_OPTS = {:dbname => 'vmdb_production'}

Expand All @@ -27,6 +28,19 @@ def self.database_size(opts)
PostgresAdmin.database_size(opts)
end

def self.validate_free_space(database_opts)
free_space = backup_destination_free_space(database_opts[:local_file])
db_size = database_size(database_opts)
if free_space > db_size
_log.info("[#{database_opts[:dbname]}] with database size: [#{db_size} bytes], free space at [#{database_opts[:local_file]}]: [#{free_space} bytes]")
else
msg = "Destination location: [#{database_opts[:local_file]}], does not have enough free disk space: [#{free_space} bytes] for database of size: [#{db_size} bytes]"
_log.warn(msg)
MiqEvent.raise_evm_event_queue(MiqServer.my_server, "evm_server_db_backup_low_space", :event_details => msg)
raise MiqException::MiqDatabaseBackupInsufficientSpace, msg
end
end

def self.backup(db_opts, connect_opts = {})
# db_opts:
# :dbname => 'vmdb_production',
Expand All @@ -39,35 +53,26 @@ def self.backup(db_opts, connect_opts = {})
# :password => 'Zug-drep5s',
# :remote_file_name => "backup_1", - Provide a base file name for the uploaded file

db_opts = DEFAULT_OPTS.merge(db_opts)

begin
if db_opts[:local_file].nil?
connect_opts[:remote_file_name] ||= File.basename(backup_file_name)

session = MiqGenericMountSession.new_session(connect_opts)
uri = with_mount_session(:backup, db_opts, connect_opts) do |database_opts|
validate_free_space(database_opts)
PostgresAdmin.backup(database_opts)
end
_log.info("[#{merged_db_opts(db_opts)[:dbname]}] database has been backed up to file: [#{uri}]")
uri
end

uri = File.join(connect_opts[:uri], "db_backup", connect_opts[:remote_file_name])
db_opts[:local_file] = session.uri_to_local_path(uri)
end
def self.dump(db_opts, connect_opts = {})
# db_opts and connect_opts similar to .backup

free_space = backup_destination_free_space(db_opts[:local_file])
db_size = database_size(db_opts)
if free_space > db_size
_log.info("[#{db_opts[:dbname]}] with database size: [#{db_size} bytes], free space at [#{db_opts[:local_file]}]: [#{free_space} bytes]")
else
msg = "Destination location: [#{db_opts[:local_file]}], does not have enough free disk space: [#{free_space} bytes] for database of size: [#{db_size} bytes]"
_log.warn(msg)
MiqEvent.raise_evm_event_queue(MiqServer.my_server, "evm_server_db_backup_low_space", :event_details => msg)
raise MiqException::MiqDatabaseBackupInsufficientSpace, msg
end
backup = PostgresAdmin.backup(db_opts)
ensure
session.disconnect if session
uri = with_mount_session(:dump, db_opts, connect_opts) do |database_opts|
# For database dumps, this isn't going to be as accurate (since the dump
# size will probably be larger than the calculated BD size), but it still
# won't hurt to do as a generic way to get a rough idea if we have enough
# disk space or the appliance for the task.
validate_free_space(database_opts)
PostgresAdmin.backup(database_opts)
end

uri ||= backup
_log.info("[#{db_opts[:dbname]}] database has been backed up to file: [#{uri}]")
_log.info("[#{merged_db_opts(db_opts)[:dbname]}] database has been dumped up to file: [#{uri}]")
uri
end

Expand All @@ -82,28 +87,42 @@ def self.restore(db_opts, connect_opts = {})
# :username => 'samba_one',
# :password => 'Zug-drep5s',

uri = with_mount_session(:restore, db_opts, connect_opts) do |database_opts|
prepare_for_restore(database_opts[:local_file])

# remove all the connections before we restore; AR will reconnect on the next query
ActiveRecord::Base.connection_pool.disconnect!
PostgresAdmin.restore(database_opts)
end
_log.info("[#{merged_db_opts(db_opts)[:dbname]}] database has been restored from file: [#{uri}]")
uri
end

private_class_method def self.merged_db_opts(db_opts)
DEFAULT_OPTS.merge(db_opts)
end

private_class_method def self.with_mount_session(action, db_opts, connect_opts)
db_opts = DEFAULT_OPTS.merge(db_opts)

begin
if db_opts[:local_file].nil?
if db_opts[:local_file].nil?
if action == :restore
uri = connect_opts[:uri]
connect_opts[:uri] = File.dirname(connect_opts[:uri])
session = MiqGenericMountSession.new_session(connect_opts)
db_opts[:local_file] = session.uri_to_local_path(uri)
else
connect_opts[:remote_file_name] ||= File.basename(backup_file_name(action))
backup_folder = action == :dump ? "db_dump" : "db_backup"
uri = File.join(connect_opts[:uri], backup_folder, connect_opts[:remote_file_name])
end

prepare_for_restore(db_opts[:local_file])

# remove all the connections before we restore; AR will reconnect on the next query
ActiveRecord::Base.connection_pool.disconnect!
backup_file = PostgresAdmin.restore(db_opts)
ensure
session.disconnect if session
session = MiqGenericMountSession.new_session(connect_opts)
db_opts[:local_file] = session.uri_to_local_path(uri)
end

uri ||= backup_file
_log.info("[#{db_opts[:dbname]}] database has been restored from file: [#{uri}]")
uri
block_result = yield db_opts if block_given?
uri || block_result
ensure
session.disconnect if session
end

private_class_method def self.prepare_for_restore(filename)
Expand Down Expand Up @@ -166,9 +185,9 @@ def self.download(connect_opts, local_file)
local_file
end

def self.backup_file_name
def self.backup_file_name(action = :backup)
time_suffix = Time.now.utc.strftime("%Y%m%d_%H%M%S")
"#{BACKUP_TMP_FILE}_#{time_suffix}"
"#{action == :backup ? BACKUP_TMP_FILE : DUMP_TMP_FILE}_#{time_suffix}"
end
private_class_method :backup_file_name
end
49 changes: 49 additions & 0 deletions lib/tasks/evm_dba.rake
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,55 @@ namespace :evm do
end
end

namespace :dump do
require Rails.root.join("lib", "evm_database_ops").expand_path.to_s
desc 'Dump the local ManageIQ EVM Database (VMDB) to a local file'
task :local do
require 'trollop'
opts = Trollop.options(EvmRakeHelper.extract_command_options) do
opt :local_file, "Destination file", :type => :string, :required => true
opt :username, "Username", :type => :string
opt :password, "Password", :type => :string
opt :hostname, "Hostname", :type => :string
opt :dbname, "Database name", :type => :string
opt :"exclude-table-data", "Tables to exclude data", :type => :strings
end

opts.delete_if { |_, v| v.nil? }
EvmDatabaseOps.dump(opts)

exit # exit so that parameters to the first rake task are not run as rake tasks
end

desc 'Dump the local ManageIQ EVM Database (VMDB) to a remote file'
task :remote do
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need the restore for the dump side of things?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lol sorry, I read this as task :restore do
🤦‍♂️

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be more clear, I was suggesting removing this task because I thought it was restoring a pg_dump which I don't really want to do unless it was an old backup (from before we removed it as a backup option).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is fair. Yeah, I really don't plan to support pg_dump restores myself. I have a fun set of directions for importing DBs when working on performance issues and such, so me personally, I only really care about getting the pg_dump stuff working from a "backup" perspective.

The fact that it "can work" in the :restore tasks is just a nice plus.

require 'trollop'
opts = Trollop.options(EvmRakeHelper.extract_command_options) do
opt :uri, "Destination depot URI", :type => :string, :required => true
opt :uri_username, "Destination depot username", :type => :string
opt :uri_password, "Destination depot password", :type => :string
opt :remote_file_name, "Destination depot filename", :type => :string
opt :username, "Username", :type => :string
opt :password, "Password", :type => :string
opt :hostname, "Hostname", :type => :string
opt :dbname, "Database name", :type => :string
opt :"exclude-table-data", "Tables to exclude data", :type => :strings
end

db_opts = {}
[:dbname, :username, :password, :hostname, :"exclude-table-data"].each { |k| db_opts[k] = opts[k] if opts[k] }

connect_opts = {}
[:uri, :uri_username, :uri_password, :remote_file_name].each { |k| connect_opts[k] = opts[k] if opts[k] }
connect_opts[:username] = connect_opts.delete(:uri_username) if connect_opts[:uri_username]
connect_opts[:password] = connect_opts.delete(:uri_password) if connect_opts[:uri_password]

EvmDatabaseOps.dump(db_opts, connect_opts)

exit # exit so that parameters to the first rake task are not run as rake tasks
end
end

namespace :restore do
desc 'Restore the local ManageIQ EVM Database (VMDB) from a local backup file'
task :local => :environment do
Expand Down
Loading