ManageIQ/manageiq-appliance_console

View on GitHub
lib/manageiq/appliance_console/database_admin.rb

Summary

Maintainability
A
2 hrs
Test Coverage
C
76%
require 'manageiq/appliance_console/errors'
require 'uri'

module ManageIQ
  module ApplianceConsole
    class DatabaseAdmin < HighLine
      include ManageIQ::ApplianceConsole::Prompts

      DB_RESTORE_FILE      = "/tmp/evm_db.backup".freeze
      DB_DEFAULT_DUMP_FILE = "/tmp/evm_db.dump".freeze
      LOCAL_FILE_VALIDATOR = ->(a) { File.exist?(a) }.freeze

      USER_PROMPT = <<-PROMPT.strip_heredoc.chomp
        username with access to this file.
        Example: 'mydomain.com/user'
      PROMPT

      attr_reader :action, :backup_type, :database_opts, :delete_agree, :filename

      def initialize(action = :restore, input = $stdin, output = $stdout)
        super(input, output)

        @action        = action
        @database_opts = {:dbname => DatabaseConfiguration.database_name}
      end

      def uri
        @database_opts[:local_file]
      end

      def ask_questions
        setting_header
        if action == :restore && EvmServer.running?
          say("\nDatabase restore failed. Please execute the \“Stop EVM Server Processes\” command and try again.")
          press_any_key
          raise MiqSignalError
        end
        ask_file_location
        ask_for_tables_to_exclude_in_dump
      end

      def activate
        clear_screen
        setting_header

        ask_to_delete_backup_after_restore
        confirm_and_execute
      end

      def ask_file_location
        @database_opts[:local_file] = just_ask(*filename_prompt_args)
      end

      def ask_to_delete_backup_after_restore
        if action == :restore
          say("The local database restore file is located at: '#{uri}'.\n")
          @delete_agree = agree("Should this file be deleted after completing the restore? (Y/N): ")
        end
      end

      def ask_for_tables_to_exclude_in_dump
        if action == :dump && should_exclude_tables?
          say(<<-PROMPT.strip_heredoc)

            To exclude tables from the dump, enter them in a space separated
            list.  For example:

                > metrics_* vim_performance_states event_streams

          PROMPT
          table_excludes = ask_for_many("table",
                                        "tables to exclude",
                                        "metrics_* vim_performance_states event_streams",
                                        255,
                                        Float::INFINITY)

          @database_opts[:exclude_table_data] = table_excludes
        end || true
      end

      def confirm_and_execute
        if allowed_to_execute?
          processing_message
          run_action
        end
        press_any_key
      end

      def allowed_to_execute?
        return true unless action == :restore

        say("\nNote: A database restore cannot be undone.  The restore will use the file: #{uri}.\n")
        agree("Are you sure you would like to restore the database? (Y/N): ")
      end

      def file_options
        @file_options ||= I18n.t("database_admin.menu_order").each_with_object({}) do |file_option, h|
          # special anonymous ftp sites are defined by uri
          uri = URI(file_option)
          if uri.scheme
            h["#{uri.scheme} to #{uri.host}"] = file_option unless skip_file_location?(uri.host)
          else
            h[I18n.t("database_admin.#{file_option}")] = file_option
          end
        end
      end

      def setting_header
        say("#{I18n.t("advanced_settings.db#{action}")}\n\n")
      end

      private

      def skip_file_location?(hostname)
        config = custom_endpoint_config_for(hostname)
        return false unless config && config[:enabled_for].present?
        !Array(config[:enabled_for]).include?(action.to_s)
      end

      def should_exclude_tables?
        ask_yn?("Would you like to exclude tables in the dump") do |q|
          q.readline = true
        end
      end

      def should_split_output?
        ask_yn?("Would you like to split the #{action} output into multiple parts") do |q|
          q.readline = true
        end
      end

      def filename_prompt_args
        return restore_prompt_args if action == :restore
        default = action == :dump ? DB_DEFAULT_DUMP_FILE : DB_RESTORE_FILE
        prompt  = "location to save the #{action} file to"
        [prompt, default, nil, "file that exists"]
      end

      def restore_prompt_args
        default   = DB_RESTORE_FILE
        validator = LOCAL_FILE_VALIDATOR
        prompt    = "location of the local restore file"
        [prompt, default, validator, "file that exists"]
      end

      def remote_file_prompt_args_for(remote_type)
        prompt  = if action == :restore
                    "location of the remote backup file"
                  else
                    "location to save the remote #{action} file to"
                  end
        prompt += "\nExample: #{sample_url}"
        [prompt, remote_type]
      end

      def sample_url
        I18n.t("database_admin.sample_url.nfs")
      end

      def processing_message
        msg = if action == :restore
                "\nRestoring the database..."
              else
                "\nRunning Database #{action} to #{uri}..."
              end
        say(msg)
      end

      def run_action
        success = send(action)
        if success && action == :restore && delete_agree
          say("\nRemoving the database restore file #{uri}...")
          File.delete(uri)
        elsif !success
          say("\nDatabase #{action} failed. Check the logs for more information")
        end
      end

      def backup
        result = PostgresAdmin.backup(database_opts)
        ManageIQ::ApplianceConsole.logger.info("[#{@database_opts[:dbname]}] database has been backed up to file: [#{uri}]")
        result
      end

      def dump
        result = PostgresAdmin.backup_pg_dump(database_opts)
        ManageIQ::ApplianceConsole.logger.info("[#{@database_opts[:dbname]}] database has been dumped up to file: [#{uri}]")
        result
      end

      def restore
        result = PostgresAdmin.restore(database_opts.merge(:backup_type => backup_type))
        ManageIQ::ApplianceConsole.logger.info("[#{@database_opts[:dbname]}] database has been restored from file: [#{uri}]")
        result
      end
    end
  end
end