-
Notifications
You must be signed in to change notification settings - Fork 260
/
Copy pathdatadog_reports.rb
157 lines (138 loc) · 5.45 KB
/
datadog_reports.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
require 'puppet'
require 'yaml'
begin
require 'dogapi'
rescue LoadError
Puppet.info 'You need the `dogapi` gem to use the Datadog report (run puppet with puppet_run_reports on your master)'
end
Puppet::Reports.register_report(:datadog_reports) do
configfile = '/etc/datadog-agent/datadog-reports.yaml'
raise(Puppet::ParseError, "Datadog report config file #{configfile} not readable") unless File.readable?(configfile)
config = YAML.load_file(configfile)
API_KEY = config[:datadog_api_key]
API_URL = config[:api_url]
REPORT_FACT_TAGS = config[:report_fact_tags] || []
REPORT_TRUSTED_FACT_TAGS = config[:report_trusted_fact_tags] || []
if ENV['DD_PROXY_HTTP'].nil?
ENV['DD_PROXY_HTTP'] = config[:proxy_http]
end
if ENV['DD_PROXY_HTTPS'].nil?
ENV['DD_PROXY_HTTPS'] = config[:proxy_https]
end
# if need be initialize the regex
if !config[:hostname_extraction_regex].nil?
begin
HOSTNAME_EXTRACTION_REGEX = Regexp.new config[:hostname_extraction_regex]
rescue
raise(Puppet::ParseError, "Invalid hostname_extraction_regex #{HOSTNAME_REGEX}")
end
unless HOSTNAME_EXTRACTION_REGEX.named_captures.key? 'hostname'
raise(Puppet::ParseError, "hostname_extraction_regex doesn't include a match group named 'hostname': #{HOSTNAME_REGEX}")
end
else
HOSTNAME_EXTRACTION_REGEX = nil
end
desc <<-DESC
Send notification of metrics to Datadog
DESC
def pluralize(number, noun)
if number == 0
"no #{noun}"
elsif number < 1
"less than 1 #{noun}"
elsif number == 1
"1 #{noun}"
else
"#{number.round} #{noun}s"
end
rescue
"#{number} #{noun}(s)"
end
def process
# Here we have access to methods in Puppet::Transaction::Report
# https://puppet.com/docs/puppet/latest/format_report.html#puppet::transaction::report
@summary = summary
@msg_host = host
unless HOSTNAME_EXTRACTION_REGEX.nil?
m = @msg_host.match(HOSTNAME_EXTRACTION_REGEX)
if !m.nil? && !m[:hostname].nil?
@msg_host = m[:hostname]
end
end
Puppet.info "Processing reports for #{@msg_host}"
event_title = ''
alert_type = ''
event_priority = 'low'
event_data = ''
if defined?(status)
# for puppet log format 2 and above
@status = status
if @status == 'failed'
event_title = "Puppet failed on #{@msg_host}"
alert_type = 'error'
event_priority = 'normal'
elsif @status == 'changed'
event_title = "Puppet changed resources on #{@msg_host}"
alert_type = 'success'
event_priority = 'normal'
elsif @status == 'unchanged'
event_title = "Puppet ran on, and left #{@msg_host} unchanged"
alert_type = 'success'
else
event_title = "Puppet ran on #{@msg_host}"
alert_type = 'success'
end
else
# for puppet log format 1
event_title = "Puppet ran on #{@msg_host}"
end
# Extract statuses
total_resource_count = resource_statuses.length
changed_resources = resource_statuses.values.select { |s| s.changed }
failed_resources = resource_statuses.values.select { |s| s.failed }
# Little insert if we know the config
config_version_blurb = defined?(configuration_version) ? "applied version #{configuration_version} and" : ''
event_data << "Puppet #{config_version_blurb} changed #{pluralize(changed_resources.length, 'resource')} out of #{total_resource_count}."
# List changed resources
unless changed_resources.empty?
event_data << "\nThe resources that changed are:\n@@@\n"
changed_resources.each { |s| event_data << "#{s.title} in #{s.file}:#{s.line}\n" }
event_data << "\n@@@\n"
end
# List failed resources
unless failed_resources.empty?
event_data << "\nThe resources that failed are:\n@@@\n"
failed_resources.each { |s| event_data << "#{s.title} in #{s.file}:#{s.line}\n" }
event_data << "\n@@@\n"
end
# instantiate DogAPI client
@dog = Dogapi::Client.new(API_KEY, nil, @msg_host, nil, nil, nil, API_URL)
Puppet.debug "Sending metrics for #{@msg_host} to Datadog"
@dog.batch_metrics do
metrics.each do |metric, data|
data.each_value do |val|
name = "puppet.#{val[1].tr(' ', '_')}.#{metric}".downcase
value = val[2]
@dog.emit_point(name.to_s, value, host: @msg_host.to_s)
end
end
end
facts = Puppet::Node::Facts.indirection.find(host).values
facts_tags = REPORT_FACT_TAGS.map { |name| "#{name}:#{facts.dig(*name.split('.'))}" }
trusted_facts = (Puppet.lookup(:trusted_information) { {} }).to_h
trusted_fact_tags = REPORT_TRUSTED_FACT_TAGS.map { |name| "#{name}:#{trusted_facts.dig(*name.split('.'))}" }
dog_tags = facts_tags + trusted_fact_tags
# Uncomment below line for debug logging of tags
# Puppet.debug "Sending events for #{@msg_host} to Datadog with tags #{dog_tags.to_s}"
@dog.emit_event(Dogapi::Event.new(event_data,
msg_title: event_title,
event_type: 'config_management.run',
event_object: @msg_host,
alert_type: alert_type,
priority: event_priority,
source_type_name: 'puppet',
tags: dog_tags),
host: @msg_host)
Puppet.info "Event sent for #{@msg_host} to Datadog"
end
end