class Proxy::Reports::AnsibleProcessor

Constants

KEYS_TO_COPY

Public Class Methods

new(data, json_body: true) click to toggle source
Calls superclass method
# File lib/smart_proxy_reports/ansible_processor.rb, line 7
def initialize(data, json_body: true)
  super(data, json_body: json_body)
  measure :parse do
    @data = JSON.parse(data)
  end
  @body = {}
  logger.debug "Processing report #{report_id}"
  debug_payload("Input", @data)
end

Public Instance Methods

build_report() click to toggle source
# File lib/smart_proxy_reports/ansible_processor.rb, line 51
def build_report
  process
  if debug_payload?
    logger.debug { JSON.pretty_generate(@body) }
  end
  build_report_root(
    format: "ansible",
    version: 1,
    host: @body["host"],
    reported_at: @body["reported_at"],
    statuses: process_statuses,
    proxy: @body["proxy"],
    body: @body,
    keywords: @body["keywords"],
  )
end
process() click to toggle source
# File lib/smart_proxy_reports/ansible_processor.rb, line 34
def process
  measure :process do
    @body["format"] = "ansible"
    @body["id"] = report_id
    @body["host"] = hostname_from_config || @data["host"]
    @body["proxy"] = Proxy::Reports::Plugin.settings.reported_proxy_hostname
    @body["reported_at"] = @data["reported_at"]
    @body["results"] = process_results
    @body["keywords"] = keywords
    @body["telemetry"] = telemetry
    @body["errors"] = errors if errors?
    KEYS_TO_COPY.each do |key|
      @body[key] = @data[key]
    end
  end
end
process_results() click to toggle source
# File lib/smart_proxy_reports/ansible_processor.rb, line 21
def process_results
  @data["results"]&.each do |result|
    process_facts(result)
    process_level(result)
    process_message(result)
    process_keywords(result)
  end
  @data["results"]
rescue StandardError => e
  logger.error "Unable to parse results", e
  @data["results"]
end
report_id() click to toggle source
# File lib/smart_proxy_reports/ansible_processor.rb, line 17
def report_id
  @data["uuid"] || generated_report_id
end
spool_report() click to toggle source
# File lib/smart_proxy_reports/ansible_processor.rb, line 68
def spool_report
  report_hash = build_report
  debug_payload("Output", report_hash)
  payload = measure :format do
    report_hash.to_json
  end
  SpooledHttpClient.instance.spool(report_id, payload)
end

Private Instance Methods

process_facts(result) click to toggle source
# File lib/smart_proxy_reports/ansible_processor.rb, line 79
def process_facts(result)
  # TODO: add fact processing and sending to the fact endpoint
  result["result"]["ansible_facts"] = {}
end
process_keywords(result) click to toggle source
# File lib/smart_proxy_reports/ansible_processor.rb, line 84
def process_keywords(result)
  if result["failed"]
    add_keywords("HasFailure", "AnsibleTaskFailed:#{result["task"]["action"]}")
  elsif result["result"]["changed"]
    add_keywords("HasChange")
  end
end
process_level(result) click to toggle source
# File lib/smart_proxy_reports/ansible_processor.rb, line 92
def process_level(result)
  if result["failed"]
    result["level"] = "err"
  elsif result["result"]["changed"]
    result["level"] = "notice"
  else
    result["level"] = "info"
  end
end
process_message(result) click to toggle source
# File lib/smart_proxy_reports/ansible_processor.rb, line 102
def process_message(result)
  msg = "N/A"
  return result["friendly_message"] = msg if result["task"].nil? || result["task"]["action"].nil?
  return result["friendly_message"] = result["result"]["msg"] if result["failed"]
  result_tree = result["result"]
  task_tree = result["task"]
  raise("Report do not contain required 'results/result' element") unless result_tree
  raise("Report do not contain required 'results/task' element") unless task_tree
  module_args_tree = result_tree.dig("invocation", "module_args")

  case task_tree["action"]
  when "ansible.builtin.package", "package"
    detail = result_tree["results"] || result_tree["msg"] || "No details"
    msg = "Package(s) #{module_args_tree["name"].join(",")} are #{module_args_tree["state"]}: #{detail}"
  when "ansible.builtin.template", "template"
    msg = "Render template #{module_args_tree["_original_basename"]} to #{result_tree["dest"]}"
  when "ansible.builtin.service", "service"
    msg = "Service #{result_tree["name"]} is #{result_tree["state"]} and enabled: #{result_tree["enabled"]}"
  when "ansible.builtin.group", "group"
    msg = "User group #{result_tree["name"]} is #{result_tree["state"]} with gid: #{result_tree["gid"]}"
  when "ansible.builtin.user", "user"
    msg = "User #{result_tree["name"]} is #{result_tree["state"]} with uid: #{result_tree["uid"]}"
  when "ansible.builtin.cron", "cron"
    msg = "Cron job: #{module_args_tree["minute"]} #{module_args_tree["hour"]} #{module_args_tree["day"]} #{module_args_tree["month"]} #{module_args_tree["weekday"]} #{module_args_tree["job"]} and disabled: #{module_args_tree["disabled"]}"
  when "ansible.builtin.copy", "copy"
    msg = "Copy #{module_args_tree["_original_basename"]} to #{result_tree["dest"]}"
  when "ansible.builtin.command", "ansible.builtin.shell", "command", "shell"
    msg = result_tree["stdout_lines"]
  end
rescue StandardError => e
  logger.debug "Unable to parse result (#{e.message}): #{result.inspect}"
ensure
  result["friendly_message"] = msg
end
process_statuses() click to toggle source
# File lib/smart_proxy_reports/ansible_processor.rb, line 137
def process_statuses
  {
    "applied" => @body["status"]["applied"],
    "failed" => @body["status"]["failed"],
    "pending" => @body["status"]["pending"] || 0, # It's only present in check mode
    "other" => @body["status"]["skipped"],
  }
rescue StandardError => e
  logger.error "Unable to process statuses", e
  { "applied" => 0, "failed" => 0, "pending" => 0, "other" => 0 }
end