class ModuleInstaller

Public Instance Methods

install_module(module_name_or_path, module_version = nil) click to toggle source
# File lib/module_installer.rb, line 7
def install_module(module_name_or_path, module_version = nil)
  brpm_content_spec = nil

  if true #TODO: support no-gem-install mode
    module_spec = install_gem(module_name_or_path, module_version)

    brpm_content_spec = module_spec if module_spec.name == "brpm_content_framework"

    is_local = is_module_path?(module_name_or_path)
    install_bundle(module_spec, is_local)
    pull_docker_image(module_spec)
  else
    module_name = module_name_or_path
    module_spec = Gem::Specification.find_by_name(module_name)
  end

  if BrpmAuto.brpm_installed?
    BrpmAuto.log "A BRPM instance is installed locally"

    if brpm_content_spec
      if brpm_content_spec.version > Gem::Version.new(BrpmAuto.version) or ! File.exist?(get_symlink_path)
        BrpmAuto.log "Updating the symlink to brpm_content_framework-latest..."
        update_symlink_to_brpm_content(brpm_content_spec.gem_dir)
      end

      if brpm_content_spec.version > Gem::Version.new(BrpmAuto.version) or ! File.exist?("#{ENV["BRPM_HOME"]}/automation_results/log.html")
        BrpmAuto.log "Copying the log.html file to te automation_results directory..."
        FileUtils.cp("#{brpm_content_spec.gem_dir}/infrastructure/log.html", "#{ENV["BRPM_HOME"]}/automation_results")
      end
    end

    BrpmAuto.log "Preparing the connectivity to BRPM..."
    if prepare_brpm_connection
      module_friendly_name = get_module_friendly_name(module_spec)
      BrpmAuto.log "Creating an automation category for #{module_friendly_name} if one doesn't exist yet..."
      create_automation_category_if_not_exists(module_friendly_name)

      BrpmAuto.log "Retrieving the integration servers..."
      integration_servers = @brpm_rest_client.get_project_servers

      BrpmAuto.log "Installing the automation script wrappers in the local BRPM instance..."
      failed_scripts = each_auto_script_wrapper(module_spec.gem_dir) do |auto_script_path, automation_type|
        BrpmAuto.log "Installing automation script wrapper for script #{auto_script_path}..."
        install_auto_script_wrapper(auto_script_path, automation_type, module_spec.name, module_friendly_name, integration_servers)
      end

      if failed_scripts.size > 0
        return false
      end
    end
  end

  BrpmAuto.log "Module #{module_spec.name} #{module_spec.version} is now installed."
end
uninstall_module(module_name, module_version) click to toggle source
# File lib/module_installer.rb, line 62
def uninstall_module(module_name, module_version)
  if BrpmAuto.brpm_installed?
    BrpmAuto.log "A BRPM instance is installed locally"

    BrpmAuto.log "Preparing the connectivity to BRPM..."
    if prepare_brpm_connection
      version_req = Gem::Requirement.create(Gem::Version.new(module_version))
      module_spec = Gem::Specification.find_by_name(module_name, version_req)

      module_friendly_name = get_module_friendly_name(module_spec)

      BrpmAuto.log "Uninstalling the automation script wrappers in the local BRPM instance..."
      failed_scripts = each_auto_script_wrapper(module_spec.gem_dir) do |auto_script_path, _|
        BrpmAuto.log "Uninstalling automation script wrapper for script #{auto_script_path}..."
        uninstall_auto_script_wrapper(auto_script_path, module_friendly_name)
      end

      if failed_scripts.size > 0
        BrpmAuto.log "Aborting the uninstall."
        return false
      end

      BrpmAuto.log "Deleting the automation category for #{module_friendly_name}..."
      delete_automation_category(module_friendly_name)
    end
  end

  remove_docker_image(module_name, module_version)

  BrpmAuto.log "Uninstalling gem #{module_name} #{module_version}..."
  _, stderr, _, status = BrpmAuto.execute_command("gem uninstall #{module_name} -v #{module_version} -x") do |stdout_err|
    BrpmAuto.log "    #{stdout_err.chomp}"
  end
  raise "The process failed with status #{status.exitstatus}.\n#{stderr}" unless status.success?

  true
end

Private Instance Methods

add_version_params(auto_script_params) click to toggle source
# File lib/module_installer.rb, line 524
def add_version_params(auto_script_params)
  include_position_attribute = false
  if auto_script_params.find { |_, param| param.has_key?("position") }
    include_position_attribute = true
  end

  input_params = auto_script_params.select do |_, param|
    type = param["type"] || "in"
    ! type.start_with?("out")
  end

  module_version_param = {}
  module_version_param["name"] = "module_version"
  module_version_param["required"] = false
  module_version_param["position"] = "A#{input_params.size + 1}:C#{input_params.size + 1}" if include_position_attribute
  auto_script_params["module_version"] = module_version_param
end
create_automation_category_if_not_exists(module_friendly_name) click to toggle source
# File lib/module_installer.rb, line 248
def create_automation_category_if_not_exists(module_friendly_name)
  list_item = @brpm_rest_client.get_list_item_by_name("AutomationCategory", module_friendly_name)

  unless list_item
    BrpmAuto.log "Automation category #{module_friendly_name} doesn't exist yet, so creating it now..."
    list_item = {}
    list_item["list_id"] = @brpm_rest_client.get_list_by_name("AutomationCategory")["id"]
    list_item["value_text"] = module_friendly_name
    @brpm_rest_client.create_list_item_from_hash(list_item)
  end
end
create_automation_error_if_not_exists(automation_error) click to toggle source
# File lib/module_installer.rb, line 236
def create_automation_error_if_not_exists(automation_error)
  list_item = @brpm_rest_client.get_list_item_by_name("AutomationErrors", automation_error)

  unless list_item
    BrpmAuto.log "Automation error #{automation_error} doesn't exist yet, so creating it now..."
    list_item = {}
    list_item["list_id"] = @brpm_rest_client.get_list_by_name("AutomationErrors")["id"]
    list_item["value_text"] = automation_error
    @brpm_rest_client.create_list_item_from_hash(list_item)
  end
end
delete_automation_category(module_friendly_name) click to toggle source
# File lib/module_installer.rb, line 260
def delete_automation_category(module_friendly_name)
  #TODO: first check if there are any manually created automation scripts added to this automation category. If yes then dont delete it
  list_item = @brpm_rest_client.get_list_item_by_name("AutomationCategory", module_friendly_name)

  if list_item
    @brpm_rest_client.archive_list_item(list_item["id"])
    @brpm_rest_client.delete_list_item(list_item["id"])
  end
end
each_auto_script_wrapper(module_path) { |auto_script_path, "ResourceAutomation"| ... } click to toggle source
# File lib/module_installer.rb, line 270
def each_auto_script_wrapper(module_path)
  # For resource automations
  resource_automation_dir = "#{module_path}/resource_automations"
  resource_automation_script_paths = Dir.glob("#{resource_automation_dir}/*.rb")

  failed_scripts = []
  if resource_automation_script_paths.size > 0
    resource_automation_script_paths.each do |auto_script_path|
      begin
        yield auto_script_path, "ResourceAutomation"
      rescue Exception => e
        failed_scripts << auto_script_path
        BrpmAuto.log_error(e)
        BrpmAuto.log "\n\t" + e.backtrace.join("\n\t")
      end
    end
  end

  # For automations
  automation_dir = "#{module_path}/automations"
  automation_script_paths = Dir.glob("#{automation_dir}/*.rb")

  if automation_script_paths.size > 0
    automation_script_paths.each do |auto_script_path|
      begin
        yield auto_script_path, "Automation"
      rescue Exception => e
        failed_scripts << auto_script_path
        BrpmAuto.log_error(e)
        BrpmAuto.log "\n\t" + e.backtrace.join("\n\t")
      end
    end
  end

  if Gem::Version.new(BrpmAuto.brpm_version) >= Gem::Version.new("4.8")
    # For Local Shell
    automation_dir = "#{module_path}/automations"
    automation_script_paths = Dir.glob("#{automation_dir}/*.sh")

    if automation_script_paths.size > 0
      automation_script_paths.each do |auto_script_path|
        begin
          yield auto_script_path, "Local Shell"
        rescue Exception => e
          failed_scripts << auto_script_path
          BrpmAuto.log_error(e)
          BrpmAuto.log "\n\t" + e.backtrace.join("\n\t")
        end
      end
    end
  end

  if Gem::Version.new(BrpmAuto.brpm_version) >= Gem::Version.new("4.8")
    # For Remote Shell
    automation_dir = "#{module_path}/remote_automations"
    automation_script_paths = Dir.glob("#{automation_dir}/*.sh")

    if automation_script_paths.size > 0
      automation_script_paths.each do |auto_script_path|
        begin
          yield auto_script_path, "RemoteAutomation"
        rescue Exception => e
          failed_scripts << auto_script_path
          BrpmAuto.log_error(e)
          BrpmAuto.log "\n\t" + e.backtrace.join("\n\t")
        end
      end
    end
  end

  if failed_scripts.size > 0
    BrpmAuto.log "The following wrapper scripts generated errors:"
    failed_scripts.each do |failed_script|
      BrpmAuto.log "  - #{failed_script}"
    end
  end

  failed_scripts
end
get_auto_script_config(auto_script_path, module_friendly_name) click to toggle source
# File lib/module_installer.rb, line 480
def get_auto_script_config(auto_script_path, module_friendly_name)
  auto_script_name = File.basename(auto_script_path, ".*")
  auto_script_config_path = "#{File.dirname(auto_script_path)}/#{auto_script_name}.meta"

  auto_script_config_content = File.exists?(auto_script_config_path) ? File.read(auto_script_config_path) : ""
  auto_script_config = YAML.load(auto_script_config_content) || {}
  auto_script_config["name"] = auto_script_name
  auto_script_config["params"] = auto_script_config["params"] || {}
  auto_script_config["friendly_name"] = auto_script_config["friendly_name"] || "#{module_friendly_name} - #{auto_script_config["name"].gsub("_", " ").capitalize}"

  auto_script_config
end
get_automation_language(automation_type) click to toggle source
# File lib/module_installer.rb, line 515
def get_automation_language(automation_type)
  case automation_type
  when "Automation", "ResourceAutomation"
    "ruby"
  else
    "shell"
  end
end
get_integration_server_template(integration_server_id, integration_server_name, integration_server_type, automation_language) click to toggle source
# File lib/module_installer.rb, line 542
  def get_integration_server_template(integration_server_id, integration_server_name, integration_server_type, automation_language)
    case automation_language
    when "ruby"
      <<EOR
#=== #{integration_server_type} Integration Server: #{integration_server_name} ===#
# [integration_id=#{integration_server_id}]
#=== End ===#

params["SS_integration_dns"] = SS_integration_dns
params["SS_integration_username"] = SS_integration_username
params["SS_integration_password_enc"] = SS_integration_password_enc
params["SS_integration_details"] = YAML.load(SS_integration_details)
EOR
    when "shell"
      <<EOR
#=== #{integration_server_type} Integration Server: #{integration_server_name} ===#
# [integration_id=#{integration_server_id}]
#=== End ===#
EOR
    else
      BrpmAuto.log "WARNING - automation language #{automation_language} is not supported"
      ""
    end
  end
get_module_friendly_name(module_spec) click to toggle source
# File lib/module_installer.rb, line 230
def get_module_friendly_name(module_spec)
  module_config = YAML.load_file("#{module_spec.gem_dir}/config.yml")

  module_config["friendly_name"] || "#{module_spec.name.sub("brpm_module_", "").capitalize}"
end
get_os_type_ids(os_types) click to toggle source
# File lib/module_installer.rb, line 493
def get_os_type_ids(os_types)
  @os_type_map ||= { "200" => "Any", "201" => "Linux", "202" => "Windows" }
  os_type_ids = []

  os_types ||= ["Any"]

  os_types.each do |os_type|
      os_type_ids << @os_type_map.key(os_type)
  end

  os_type_ids
end
get_script_executor_template(automation_type, module_name, auto_script_name) click to toggle source
# File lib/module_installer.rb, line 567
  def get_script_executor_template(automation_type, module_name, auto_script_name)
    template = <<EOR
params["direct_execute"] = "true"

params["module_version"] = nil if params["module_version"].empty?

EOR

    if automation_type == "Automation"

      template += <<EOR
require "\#{ENV["BRPM_CONTENT_HOME"] || "\#{ENV["BRPM_HOME"]}/modules"}/gems/brpm_content_framework-latest/lib/brpm_script_executor.rb"

BrpmScriptExecutor.execute_automation_script_in_separate_process("#{module_name}", "#{auto_script_name}", params)
EOR

    elsif automation_type == "ResourceAutomation"

      template += <<EOR
load "\#{ENV["BRPM_CONTENT_HOME"] || "\#{ENV["BRPM_HOME"]}/modules"}/gems/brpm_content_framework-latest/lib/brpm_script_executor.rb"

def execute(script_params, parent_id, offset, max_records)
  BrpmScriptExecutor.execute_resource_automation_script_in_separate_process("#{module_name}", "#{auto_script_name}", script_params, parent_id, offset, max_records)
end
EOR
    end

    template
  end
install_auto_script_wrapper(auto_script_path, automation_type, module_name, module_friendly_name, integration_servers) click to toggle source
# File lib/module_installer.rb, line 350
def install_auto_script_wrapper(auto_script_path, automation_type, module_name, module_friendly_name, integration_servers)
  auto_script_config = get_auto_script_config(auto_script_path, module_friendly_name)

  if automation_type == "RemoteAutomation"
    automation_type = auto_script_config["automation_type"] || "Remote Shell"
  end
  automation_type == "ResourceAutomation" if automation_type == "Data Retriever"

  BrpmAuto.log "Installing the wrapper script for automation script #{auto_script_config["name"]} (friendly name: #{auto_script_config["friendly_name"]}, automation type: #{automation_type})..."

  if automation_type != "ResourceAutomation"
    add_version_params(auto_script_config["params"])
  end

  automation_language = get_automation_language(automation_type)

  wrapper_script_content = ""
  if auto_script_config["params"].size > 0
    params_content = auto_script_config["params"].to_yaml
    params_content.sub!("---\n", "") # Remove the yaml document separator line
    params_content.gsub!(/(^)+/, "# ") # Prepend "# " to each line
    wrapper_script_content = "###\n#{params_content}###\n"
  end

  integration_server = nil
  if is_local_automation(automation_type)
    if auto_script_config["integration_server_type"]
      server_type_id = @brpm_rest_client.get_id_for_project_server_type(auto_script_config["integration_server_type"])
      if server_type_id
        integration_server = integration_servers.find { |integr_server| integr_server["server_name_id"] == server_type_id } #TODO: support multiple integration servers of same type (user should pick one)
      else
        integration_server = integration_servers.find { |integr_server| integr_server["name"].include?(auto_script_config["integration_server_type"]) } #TODO: support multiple integration servers of same type (user should pick one)
      end

      if integration_server
        wrapper_script_content += "\n"
        wrapper_script_content += get_integration_server_template(integration_server["id"], integration_server["name"], auto_script_config["integration_server_type"], automation_language)
      else
        BrpmAuto.log "WARNING - An integration server of type #{auto_script_config["integration_server_type"]} (or that has #{auto_script_config["integration_server_type"]} in its name if the integration server type is not supported) doesn't exist so not setting the integration server in the wrapper script."
      end
    end
  end

  wrapper_script_content += "\n"
  case automation_type
  when "Automation", "ResourceAutomation"
    wrapper_script_content += get_script_executor_template(automation_type, module_name, auto_script_config["name"])
  when "Local Shell"
    wrapper_script_content += "#{auto_script_path}\n"
  when "Remote Shell", "Remote Dispatcher"
    wrapper_script_content += File.read(auto_script_path)
  else
    raise "Unsupported automation type: #{automation_type}"
  end

  script = {}
  script["name"] = auto_script_config["friendly_name"]
  script["description"] = auto_script_config["description"] || ""
  script["automation_type"] = automation_type
  script["automation_category"] = module_friendly_name
  script["agent_type"] = auto_script_config["agent_type"] if automation_type == "Remote Dispatcher" and auto_script_config["agent_type"]
  script["os_type_ids"] = get_os_type_ids(auto_script_config["os_types"]) if automation_type == "Remote Shell" and auto_script_config["os_types"]
  script["content"] = wrapper_script_content
  script["integration_id"] = integration_server["id"] if auto_script_config["integration_server_type"] and integration_server
  if automation_type == "ResourceAutomation"
    script["unique_identifier"] = auto_script_config["resource_id"] || auto_script_config["name"]
    script["render_as"] = auto_script_config["render_as"] || "List"
  end

  script = @brpm_rest_client.create_or_update_script(script)

  if script["aasm_state"] == "draft"
    BrpmAuto.log "Updating the aasm_state of the wrapper script to 'pending'..."
    script_to_update = {}
    script_to_update["id"] = script["id"]
    script_to_update["aasm_state"] = "pending"
    script = @brpm_rest_client.update_script_from_hash(script_to_update)
  end

  if script["aasm_state"] == "pending"
    BrpmAuto.log "Updating the aasm_state of the wrapper script to 'released'..."
    script_to_update = {}
    script_to_update["id"] = script["id"]
    script_to_update["aasm_state"] = "released"
    script = @brpm_rest_client.update_script_from_hash(script_to_update)
  end

  if script["aasm_state"] == "retired"
    BrpmAuto.log "Updating the aasm_state of the wrapper script to 'released'..."
    script_to_update = {}
    script_to_update["id"] = script["id"]
    script_to_update["aasm_state"] = "released"
    script = @brpm_rest_client.update_script_from_hash(script_to_update)
  end
end
install_bundle(spec, is_local = false) click to toggle source
# File lib/module_installer.rb, line 161
def install_bundle(spec, is_local = false)
  gemfile_path = File.join(spec.gem_dir, "Gemfile")

  if File.exists?(gemfile_path)
    BrpmAuto.log "Found a Gemfile so doing a 'bundle install'..."
    command = "export BRPM_CONTENT_FRAMEWORK_DEPLOYMENT=true; cd #{spec.gem_dir}; bundle install --without development test"
    command += " --local" if is_local
    BrpmAuto.log command
    _, stderr, _, status = BrpmAuto.execute_command(command) do |stdout_err|
      BrpmAuto.log "    #{stdout_err.chomp}"
    end
    raise "The process failed with status #{status.exitstatus}.\n#{stderr}" unless status.success?
  else
    BrpmAuto.log "This module doesn't have a Gemfile."
  end
end
install_gem(module_name_or_path, module_version) click to toggle source
# File lib/module_installer.rb, line 136
def install_gem(module_name_or_path, module_version)
  BrpmAuto.log "Installing gem #{module_name_or_path}#{module_version.nil? ? "" : " " + module_version}..."
  command = "gem install #{module_name_or_path}#{module_version.nil? ? "" : " -v " + module_version} --ignore-dependencies"
  BrpmAuto.log command
  _, stderr, _, status = BrpmAuto.execute_command(command) do |stdout_err|
    BrpmAuto.log "    #{stdout_err.chomp}"
  end
  raise "The process failed with status #{status.exitstatus}.\n#{stderr}" unless status.success?

  if is_module_path?(module_name_or_path)
    require 'rubygems/name_tuple'
    source = Gem::Source::SpecificFile.new module_name_or_path
    module_name = source.spec.name
    module_version = source.spec.version
  else
    module_name = module_name_or_path
  end

  Gem.refresh
  version_req = module_version ? Gem::Requirement.create(Gem::Version.new(module_version)) : Gem::Requirement.default
  module_spec = Gem::Specification.find_by_name(module_name, version_req)

  module_spec
end
is_local_automation(automation_type) click to toggle source
# File lib/module_installer.rb, line 506
def is_local_automation(automation_type)
  case automation_type
  when "Automation", "Local Shell", "ResourceAutomation"
    true
  else
    false
  end
end
is_module_path?(module_name_or_path) click to toggle source
# File lib/module_installer.rb, line 132
def is_module_path?(module_name_or_path)
  module_name_or_path =~ /\.gem$/ and File.file? module_name_or_path
end
prepare_brpm_connection() click to toggle source
# File lib/module_installer.rb, line 102
def prepare_brpm_connection
  brpm_file = File.expand_path("~/.brpm")

  brpm_config = nil
  if File.exists?(brpm_file)
    brpm_config = YAML.load_file(brpm_file)
  end

  if brpm_config
    unless brpm_config["brpm_url"] and brpm_config["brpm_api_token"]
      BrpmAuto.log "WARNING - Properties brpm_url and/or brpm_api_token don't exist in file ~/.brpm so not installing the automation script wrappers in BRPM. If you want to install them the next time you should add brpm_url and brpm_api_token properties in yaml format in file ~/.brpm."
      return false
    end
  else
    BrpmAuto.log "WARNING - File ~/.brpm doesn't exist so not installing the automation script wrappers in BRPM. If you want to install them the next time you should create this file and add brpm_url and brpm_api_token properties in yaml format."
    return false
  end

  begin
    BrpmAuto.log "Loading brpm_module_brpm..."
    BrpmAuto.require_module "brpm_module_brpm"
  rescue Gem::GemNotFoundException, Gem::LoadError
    BrpmAuto.log "WARNING - Module brpm_module_brpm is not installed so not installing the automation script wrappers in BRPM."
    return false
  end

  @brpm_rest_client = BrpmRestClient.new(brpm_config["brpm_url"], brpm_config["brpm_api_token"])
  true
end
pull_docker_image(spec) click to toggle source
# File lib/module_installer.rb, line 178
def pull_docker_image(spec)
  case BrpmAuto.global_params["execute_automation_scripts_in_docker"]
  when "always", "if_docker_image_exists"
    BrpmAuto.log "Pulling the docker image from the Docker Hub..."
    stdout, stderr, _, status = BrpmAuto.execute_command("docker pull bmcrlm/#{spec.name}:#{spec.version}") do |stdout_err|
      BrpmAuto.log "    #{stdout_err.chomp}"
    end

    if stdout =~ /Image is up to date for/ or stdout =~ /Downloaded newer image for/
      #all went fine
    else
      if stderr =~ /not found/
        if BrpmAuto.global_params["execute_automation_scripts_in_docker"] == "always"
          raise stderr.chomp
        elsif BrpmAuto.global_params["execute_automation_scripts_in_docker"] == "if_docker_image_exists"
          BrpmAuto.log stderr.chomp
          BrpmAuto.log "docker is only optional so ignoring the error."
        end
      else
        raise "The process failed with status #{status.exitstatus}.\n#{stderr}" unless status.success?
      end
    end
  end
end
remove_docker_image(module_name, module_version) click to toggle source
# File lib/module_installer.rb, line 203
def remove_docker_image(module_name, module_version)
  case BrpmAuto.global_params["execute_automation_scripts_in_docker"]
  when "always", "if_docker_image_exists"
    BrpmAuto.log "Removing the docker image..."
    _, stderr, _, status = BrpmAuto.execute_command("docker images | grep bmcrlm/#{module_name} | grep #{module_version} | awk {'print $3'} | xargs -r docker rmi") do |stdout_err|
      BrpmAuto.log "    #{stdout_err.chomp}"
    end
    raise "The process failed with status #{status.exitstatus}.\n#{stderr}" unless status.success?
  end
end
uninstall_auto_script_wrapper(auto_script_path, module_friendly_name) click to toggle source
# File lib/module_installer.rb, line 446
def uninstall_auto_script_wrapper(auto_script_path, module_friendly_name)
  auto_script_config = get_auto_script_config(auto_script_path, module_friendly_name)

  script = @brpm_rest_client.get_script_by_name(auto_script_config["friendly_name"])

  unless script
    BrpmAuto.log "Script #{auto_script_config["friendly_name"]} was not found, probably already deleted. Continuing."
    return
  end

  if script["aasm_state"] == "released"
    BrpmAuto.log "Updating the aasm_state of the wrapper script to 'retired'..."
    script_to_update = {}
    script_to_update["id"] = script["id"]
    script_to_update["aasm_state"] = "retired"
    script = @brpm_rest_client.update_script_from_hash(script_to_update)
  end

  if script["aasm_state"] == "retired"
    BrpmAuto.log "Updating the aasm_state of the wrapper script to 'archived'..."
    script_to_update = {}
    script_to_update["id"] = script["id"]
    script_to_update["aasm_state"] = "archived"
    script = @brpm_rest_client.update_script_from_hash(script_to_update)
  end

  if script["aasm_state"] == "archived_state"
    BrpmAuto.log "Deleting the wrapper script..."
    @brpm_rest_client.delete_script(script["id"])
  else
    raise "Script #{auto_script_config["friendly_name"]} is not in aasm_state 'archived' so unable to delete it."
  end
end