ManageIQ/manageiq-appliance_console

View on GitHub
bin/appliance_console

Summary

Maintainability
Test Coverage
#!/usr/bin/env ruby
# description: ManageIQ appliance console
#

require 'bundler'
Bundler.setup

require 'manageiq/appliance_console'

require 'fileutils'
# defines globally /via kernal: agree(), ask(), choose() and say()
require 'highline/import'
require 'rubygems'
require 'bcrypt'
require 'linux_admin'
require 'awesome_spawn'

require 'manageiq/appliance_console/i18n'

HighLine.default_instance.tap do |highline|
  highline.wrap_at = 80
  highline.page_at = 35
end

def summary_entry(field, value)
  dfield = "#{field}:"
  "#{dfield.ljust(24)} #{value}"
end

def ensure_key_configured
  key_config = ManageIQ::ApplianceConsole::KeyConfiguration.new
  unless key_config.key_exist?
    say "No encryption key found.\n"
    say "For migrations, copy encryption key from a hardened appliance."
    say "For worker and multi-region setups, copy key from another appliance.\n"
    say "If this is your first appliance, just generate one now.\n\n"

    if key_config.ask_question_loop
      say("\nEncryption key now configured.\n\n")
    else
      say("\nEncryption key not configured.")
      press_any_key
      raise ManageIQ::ApplianceConsole::MiqSignalError
    end
  end
end

[:INT, :TERM, :ABRT, :TSTP].each { |s| trap(s) { raise ManageIQ::ApplianceConsole::MiqSignalError } }

VERSION_FILE  = ManageIQ::ApplianceConsole::RAILS_ROOT.join("VERSION")
LOGFILE       = ManageIQ::ApplianceConsole::RAILS_ROOT.join("log", "appliance_console.log")
DB_RESTORE_FILE = "/tmp/evm_db.backup".freeze

AS_OPTIONS = I18n.t("advanced_settings.menu_order").collect do |item|
  I18n.t("advanced_settings.#{item}")
end

require 'manageiq-password'
ManageIQ::Password.key_root = ManageIQ::ApplianceConsole::RAILS_ROOT.join("certs").to_s

# Load appliance_console libraries
include ManageIQ::ApplianceConsole::Prompts

# Restore database choices
RESTORE_LOCAL   = "Local file".freeze
RESTORE_NFS     = "Network File System (NFS)".freeze
RESTORE_SMB     = "Samba (SMB)".freeze
RESTORE_OPTIONS = [RESTORE_LOCAL, RESTORE_NFS, RESTORE_SMB, ManageIQ::ApplianceConsole::CANCEL].freeze

# Restart choices
RE_RESTART  = "Restart".freeze
RE_DELLOGS  = "Restart and Clean Logs".freeze
RE_OPTIONS  = [RE_RESTART, RE_DELLOGS, ManageIQ::ApplianceConsole::CANCEL].freeze

NETWORK_INTERFACE = "eth0".freeze
CLOUD_INIT_NETWORK_CONFIG_FILE = "/etc/cloud/cloud.cfg.d/99_miq_disable_network_config.cfg".freeze
CLOUD_INIT_DISABLE_NETWORK_CONFIG = "network: {config: disabled}\n".freeze

module ManageIQ
module ApplianceConsole
  eth0 = LinuxAdmin::NetworkInterface.new(NETWORK_INTERFACE)
  # Because it takes a few seconds, get the region once in the outside loop
  region = ManageIQ::ApplianceConsole::DatabaseConfiguration.region

  # Calling stty to provide the equivalent line settings when the console is run via an ssh session or
  # over the virtual machine console.
  system("stty -echoprt ixany iexten echoe echok")

  loop do
    begin
      dns = LinuxAdmin::Dns.new
      eth0.reload
      eth0.parse_conf if eth0.respond_to?(:parse_conf)

      host             = LinuxAdmin::Hosts.new.hostname
      ip               = eth0.address
      mac              = eth0.mac_address
      mask             = eth0.netmask
      gw               = eth0.gateway
      dns1, dns2       = dns.nameservers
      order            = dns.search_order.join(' ')
      timezone         = LinuxAdmin::TimeDate.system_timezone
      version          = File.read(VERSION_FILE).chomp if File.exist?(VERSION_FILE)
      dbhost           = ManageIQ::ApplianceConsole::DatabaseConfiguration.database_host
      database         = ManageIQ::ApplianceConsole::DatabaseConfiguration.database_name
      messaging        = ManageIQ::ApplianceConsole::MessageConfiguration.configured?
      messaging_broker = ManageIQ::ApplianceConsole::MessageServerConfiguration.configured?
      evm_status       = if ManageIQ::ApplianceConsole::EvmServer.running?
        "running"
      elsif ManageIQ::ApplianceConsole::EvmServer.runnable?
        "not running"
      else
        "not configured"
      end

      summary_attributes = [
        summary_entry("Hostname", host),
        summary_entry("IPv4 Address", "#{ip}/#{mask}"),
        summary_entry("IPv4 Gateway", gw),
        summary_entry("IPv6 Address", eth0.address6 ? "#{eth0.address6}/#{eth0.prefix6}" : ''),
        summary_entry("IPV6 Gateway", eth0.gateway6),
        summary_entry("Primary DNS", dns1),
        summary_entry("Secondary DNS", dns2),
        summary_entry("Search Order", order),
        summary_entry("MAC Address", mac),
        summary_entry("Timezone", timezone),
        summary_entry("Local Database Server", PostgresAdmin.local_server_status),
        summary_entry("#{I18n.t("product.name")} Server", evm_status),
        summary_entry("#{I18n.t("product.name")} Database", dbhost || "not configured"),
        summary_entry("Database/Region", database ? "#{database} / #{region.to_i}" : "not configured"),
        summary_entry("Messaging", messaging ? "configured" : "not configured"),
        summary_entry("Local Messaging Broker", messaging_broker ? "configured" : "not configured"),
        summary_entry("External Auth", ExternalHttpdAuthentication.config_status),
        summary_entry("#{I18n.t("product.name")} Version", version),
      ]

      clear_screen

      say(<<-EOL)
Welcome to the #{I18n.t("product.name")} Virtual Appliance.

To modify the configuration, use a web browser to access the management page.

#{HighLine.default_instance.list(summary_attributes)}
        EOL

      press_any_key

      clear_screen
      selection = ask_with_menu("Advanced Setting", AS_OPTIONS, nil, true)
      case selection
      when I18n.t("advanced_settings.httpdauth")
        say("#{selection}\n\n")

        httpd_auth = ExternalHttpdAuthentication.new(host)
        if httpd_auth.ask_questions && httpd_auth.activate
          httpd_auth.post_activation
          say("\nExternal Authentication configured successfully.\n")
          press_any_key
        else
          say("\nExternal Authentication configuration failed!\n")
          press_any_key
          raise MiqSignalError
        end

      when I18n.t("advanced_settings.extauth_opts")
        say("#{selection}\n\n")

        extauth_options = ExternalAuthOptions.new
        if extauth_options.ask_questions && extauth_options.any_updates?
          extauth_options.update_configuration
          say("\nExternal Authentication Options updated successfully.\n")
        else
          say("\nExternal Authentication Options not updated.\n")
        end
        press_any_key

      when I18n.t("advanced_settings.evmstop")
        say("#{selection}\n\n")
        if ManageIQ::ApplianceConsole::EvmServer.running?
          if ask_yn? "\nNote: It may take up to a few minutes for all #{I18n.t("product.name")} server processes to exit gracefully. Stop #{I18n.t("product.name")}"
            say("\nStopping #{I18n.t("product.name")} Server...")
            logger.info("EVM server stop initiated by appliance console.")
            ManageIQ::ApplianceConsole::EvmServer.stop
          end
        else
          say("\n#{I18n.t("product.name")} Server is not running...")
        end
        press_any_key

      when I18n.t("advanced_settings.evmstart")
        say("#{selection}\n\n")
        if ask_yn?("\nStart #{I18n.t("product.name")}")
          say("\nStarting #{I18n.t("product.name")} Server...")
          logger.info("EVM server start initiated by appliance console.")
          ManageIQ::ApplianceConsole::EvmServer.start
          press_any_key
        end

      when I18n.t("advanced_settings.dbbackup")
        db_admin = ManageIQ::ApplianceConsole::DatabaseAdmin.new(:backup)
        db_admin.ask_questions && db_admin.activate

      when I18n.t("advanced_settings.dbdump")
        db_admin = ManageIQ::ApplianceConsole::DatabaseAdmin.new(:dump)
        db_admin.ask_questions && db_admin.activate

      when I18n.t("advanced_settings.dbrestore")
        db_admin = ManageIQ::ApplianceConsole::DatabaseAdmin.new(:restore)
        db_admin.ask_questions && db_admin.activate

      when I18n.t("advanced_settings.key_gen")
        say("#{selection}\n\n")

        key_config = ManageIQ::ApplianceConsole::KeyConfiguration.new
        if key_config.ask_question_loop
          say("\nEncryption key now configured.")
          press_any_key
        else
          say("\nEncryption key not configured.")
          press_any_key
          raise MiqSignalError
        end

      when I18n.t("advanced_settings.app_config")
        say("#{selection}\n\n")

        ensure_key_configured

        options = {
          "Create Internal Database"           => "create_internal",
          "Create Region in External Database" => "create_external",
          "Join Region in External Database"   => "join_external",
          "Reset Configured Database"          => "reset_region",
          "Make No Database Changes"           => "no_changes"
        }
        database_action = ask_with_menu("Database Operation", options)

        messaging_options = {
          "Configure this appliance as a messaging server" => "message_server",
          "Connect to an external messaging system"        => "message_client",
          "Make No messaging changes"                      => "no_changes"
        }

        messaging_action = ask_with_menu("Configure Messaging", messaging_options)

        changes_requested = database_action != "no_changes" || messaging_action != "no_changes"

        # Stop evmserver while we make changes to the database and/or messaging configuration
        if changes_requested
          say("\nStopping #{I18n.t("product.name")} Server...")
          ManageIQ::ApplianceConsole::EvmServer.stop
        end

        database_configuration =
          case database_action
          when "create_internal"
            ManageIQ::ApplianceConsole::InternalDatabaseConfiguration.new
          when /_external/
            ManageIQ::ApplianceConsole::ExternalDatabaseConfiguration.new(:action => database_action.split("_").first.to_sym)
          else
            ManageIQ::ApplianceConsole::DatabaseConfiguration.new
          end

        case database_action
        when "reset_region"
          if database_configuration.reset_region
            say("Database reset successfully")
            say("Start the server processes via '#{I18n.t("advanced_settings.evmstart")}'.")
          else
            say("Failed to reset database")
          end
        when "create_internal", /_external/
          database_configuration.run_interactive
        end
        # Get the region again because it may have changed
        region = ManageIQ::ApplianceConsole::DatabaseConfiguration.region

        case messaging_action
        when "message_server"
          say("#{selection}\n\n")

          message_server = MessageServerConfiguration.new
          if !MessageServerConfiguration.available?
            say("\nMessage Server configuration is unavailable!\n")
            press_any_key
            raise MiqSignalError
          elsif message_server.ask_questions && message_server.configure
            say("\nMessage Server configured successfully.\n")
          else
            say("\nMessage Server configuration failed!\n")
            press_any_key
            raise MiqSignalError
          end
        when "message_client"
          say("#{selection}\n\n")

          message_client = MessageClientConfiguration.new
          if !MessageClientConfiguration.available?
            say("\nMessage Client configuration is unavailable!\n")
            press_any_key
            raise MiqSignalError
          elsif message_client.ask_questions && message_client.configure
            say("\nMessage Client configured successfully.\n")
          else
            say("\nMessage Client configuration failed!\n")
            press_any_key
            raise MiqSignalError
          end
        end

        # Start evmserverd if database and/or messaging were set up and we are supposed to run as an evmserver
        if changes_requested && database_configuration.run_as_evm_server
          say("\nStarting #{I18n.t("product.name")} Server...")
          ManageIQ::ApplianceConsole::EvmServer.start(:enable => true)
        end

        press_any_key

      when I18n.t("advanced_settings.db_replication")
        say("#{selection}\n\n")

        options = {
          "Configure Server as Primary" => "primary",
          "Configure Server as Standby" => "standby"
        }

        action = ask_with_menu("Database replication Operation", options)

        case action
        when "primary"
          db_replication = ManageIQ::ApplianceConsole::DatabaseReplicationPrimary.new
          logger.info("Configuring Server as Primary")
        when "standby"
          db_replication = ManageIQ::ApplianceConsole::DatabaseReplicationStandby.new
          logger.info("Configuring Server as Standby")
          ensure_key_configured
        end

        if db_replication.ask_questions && db_replication.activate
          say("Database Replication configured")
          logger.info("Database Replication configured")
          press_any_key
        else
          say("Database Replication not configured")
          logger.info("Database Replication not configured")
          press_any_key
          raise MiqSignalError
        end
      when I18n.t("advanced_settings.failover_monitor")
        say("#{selection}\n\n")

        options = {
          "Start Database Failover Monitor" => "start",
          "Stop Database Failover Monitor"  => "stop"
        }

        action = ask_with_menu("Failover Monitor Configuration", options)
        failover_service = LinuxAdmin::Service.new("evm-failover-monitor")

        begin
          case action
          when "start"
            logger.info("Starting and enabling evm-failover-monitor service")
            failover_service.enable.start
          when "stop"
            logger.info("Stopping and disabling evm-failover-monitor service")
            failover_service.disable.stop
          end
        rescue AwesomeSpawn::CommandResultError => e
          say("Failed to configure failover monitor")
          logger.error("Failed to configure evm-failover-monitor service")
          say(e.result.output)
          say(e.result.error)
          say("")
          press_any_key
          raise MiqSignalError
        end

        say("Failover Monitor Service configured successfully")
        press_any_key

      when I18n.t("advanced_settings.log_config")
        say("#{selection}\n\n")
        log_config = ManageIQ::ApplianceConsole::LogfileConfiguration.new
        if log_config.ask_questions && log_config.activate
          say("Log file configuration updated.")
          say("The appliance may take a few minutes to fully restart.")
          press_any_key
        else
          say("Log file configuration unchanged")
          press_any_key
          raise MiqSignalError
        end

      when I18n.t("advanced_settings.tmp_config")
        say("#{selection}\n\n")
        tmp_config = ManageIQ::ApplianceConsole::TempStorageConfiguration.new
        if tmp_config.ask_questions && tmp_config.activate
          say("Temp storage disk configured")
          press_any_key
        else
          say("Temp storage disk not configured")
          press_any_key
          raise MiqSignalError
        end

      when I18n.t("advanced_settings.restart")
        case ask_with_menu("Restart Option", RE_OPTIONS, nil, false)
        when ManageIQ::ApplianceConsole::CANCEL
          # don't do anything
        when RE_RESTART
          if are_you_sure?("restart the appliance now")
            logger.info("Appliance restart initiated by appliance console.")
            ManageIQ::ApplianceConsole::EvmServer.stop
            LinuxAdmin::System.reboot!
          end
        when RE_DELLOGS
          if are_you_sure?("restart the appliance now")
            logger.info("Appliance restart with clean logs initiated by appliance console.")
            ManageIQ::ApplianceConsole::EvmServer.stop
            LinuxAdmin::Service.new("miqtop").stop
            LinuxAdmin::Service.new("miqvmstat").stop
            LinuxAdmin::Service.new("httpd").stop
            FileUtils.rm_rf(Dir.glob("/var/www/miq/vmdb/log/*.log*"))
            FileUtils.rm_rf(Dir.glob("/var/www/miq/vmdb/log/apache/*.log*"))
            logger.info("Logs cleaned and appliance rebooted by appliance console.")
            LinuxAdmin::System.reboot!
          end
        end

      when I18n.t("advanced_settings.shutdown")
        say("#{selection}\n\n")
        if are_you_sure?("shut down the appliance now")
          say("\nShutting down appliance...  This process may take a few minutes.\n\n")
          logger.info("Appliance shutdown initiated by appliance console")
          ManageIQ::ApplianceConsole::EvmServer.stop
          LinuxAdmin::System.shutdown!
        end

      when I18n.t("advanced_settings.scap")
        say("#{selection}\n\n")
        ManageIQ::ApplianceConsole::Scap.new.lockdown
        press_any_key

      when I18n.t("advanced_settings.summary")
        # Do nothing

      when I18n.t("advanced_settings.quit")
        break
      end
    rescue MiqSignalError
      # If a signal is caught anywhere in the inner (after login) loop, go back to the summary screen
      next
    end
  end
end
end