e53df64c45
tl;dr This report processor consumes a puppet ruby report object and writes a json wire-format report to disk in the puppet reportdir. The report format produced by puppet does not match the wire format for reports that are accepted by puppetdb so this report processor turns the ruby report object produced by puppet into a json object in the format that puppetdb expects to receive. The code for doing this already exists in the puppetdb-terminus report processor, except that code then expects to directly communicate with the puppetdb service to submit the report and is not resuable. (I submitted https://tickets.puppetlabs.com/browse/PDB-2150 to document this feature request). The masterless nodes in openstack-infra are not allowed to directly communicate with the puppetdb service and so must have a custom report processor that writes the json report to disk. To enable this report processor, puppet must be configured with both `report = true` (the default) and `reports = puppetdb_file` Change-Id: I3b80a8012f2d6dc231c4d7701633d428a28b0006
131 lines
4.3 KiB
Ruby
131 lines
4.3 KiB
Ruby
# File-writing code is from the store report processor in puppet's master branch.
|
|
# The rest of the code is from the puppetdb report processor in puppetdb's 2.3.x branch.
|
|
require 'puppet'
|
|
|
|
Puppet::Reports.register_report(:puppetdb_file) do
|
|
desc <<-DESC
|
|
Save report information to a file for sending to PuppetDB via the REST API
|
|
later. Reports are serialized to JSON format and may then submitted to puppetdb.
|
|
DESC
|
|
|
|
# Process the report by formatting it into a PuppetDB 'store report'
|
|
# written to disk to be submitted to PuppetDB later.
|
|
#
|
|
# @return [void]
|
|
def process
|
|
dir = File.join(Puppet[:reportdir], host)
|
|
if ! Puppet::FileSystem.exist?(dir)
|
|
FileUtils.mkdir_p(dir)
|
|
FileUtils.chmod_R(0750, dir)
|
|
end
|
|
now = Time.now.gmtime
|
|
name = %w{year month day hour min}.collect do |method|
|
|
"%02d" % now.send(method).to_s
|
|
end.join("") + "_puppetdb.json"
|
|
file = File.join(dir, name)
|
|
begin
|
|
Puppet::Util.replace_file(file, 0640) do |fh|
|
|
fh.print({ "command" => "store report", "version" => 3, "payload" => report_to_hash }.to_json)
|
|
end
|
|
rescue => detail
|
|
Puppet.log_exception(detail, "Could not write report for #{host} at #{file}: #{detail}")
|
|
end
|
|
|
|
nil
|
|
end
|
|
|
|
# Convert `self` (an instance of `Puppet::Transaction::Report`) to a hash
|
|
# suitable for sending over the wire to PuppetDB
|
|
#
|
|
# @return Hash[<String, Object>]
|
|
# @api private
|
|
def report_to_hash
|
|
if environment.nil?
|
|
raise Puppet::Error, "Environment is nil, unable to submit report. This may be due a bug with Puppet. Ensure you are running the latest revision, see PUP-2508 for more details."
|
|
end
|
|
|
|
{
|
|
"certname" => host,
|
|
"puppet-version" => puppet_version,
|
|
"report-format" => report_format,
|
|
"configuration-version" => configuration_version.to_s,
|
|
"start-time" => time.iso8601(9),
|
|
"end-time" => (time + run_duration).iso8601(9),
|
|
"resource-events" => build_events_list,
|
|
"environment" => environment,
|
|
"transaction-uuid" => transaction_uuid,
|
|
"status" => status,
|
|
}
|
|
end
|
|
|
|
# Build a resource-events array from both evaluated and skipped resources.
|
|
#
|
|
# @return Array[Hash]
|
|
# @api private
|
|
def build_events_list
|
|
resource_statuses.inject([]) do |events, status_entry|
|
|
_, status = *status_entry
|
|
if ! (status.events.empty?)
|
|
events.concat(status.events.map { |event| event_to_hash(status, event) })
|
|
elsif status.skipped
|
|
events.concat([fabricate_event(status, "skipped")])
|
|
end
|
|
events
|
|
end
|
|
end
|
|
|
|
# Calculate run duration.
|
|
#
|
|
# @return Number
|
|
# @api private
|
|
def run_duration
|
|
if metrics["time"] and metrics["time"]["total"]
|
|
metrics["time"]["total"]
|
|
else
|
|
0
|
|
end
|
|
end
|
|
|
|
# Convert an instance of `Puppet::Transaction::Event` to a hash
|
|
# suitable for sending over the wire to PuppetDB
|
|
#
|
|
# @return Hash[<String, Object>]
|
|
# @api private
|
|
def event_to_hash(resource_status, event)
|
|
{
|
|
"status" => event.status,
|
|
"timestamp" => event.time.iso8601(9),
|
|
"resource-type" => resource_status.resource_type,
|
|
"resource-title" => resource_status.title.to_s,
|
|
"property" => event.property,
|
|
"new-value" => event.desired_value,
|
|
"old-value" => event.previous_value,
|
|
"message" => event.message,
|
|
"file" => resource_status.file,
|
|
"line" => resource_status.line,
|
|
"containment-path" => resource_status.containment_path,
|
|
}
|
|
end
|
|
|
|
# Given an instance of `Puppet::Resource::Status` and a status
|
|
# string, this method fabricates a PuppetDB event object with the
|
|
# provided `"status"`.
|
|
#
|
|
# @api private
|
|
def fabricate_event(resource_status, event_status)
|
|
{
|
|
"status" => event_status,
|
|
"timestamp" => resource_status.time.iso8601(9),
|
|
"resource-type" => resource_status.resource_type,
|
|
"resource-title" => resource_status.title.to_s,
|
|
"property" => nil,
|
|
"new-value" => nil,
|
|
"old-value" => nil,
|
|
"message" => nil,
|
|
"file" => resource_status.file,
|
|
"line" => resource_status.line,
|
|
"containment-path" => resource_status.containment_path,
|
|
}
|
|
end
|
|
end
|