Skip to content

Commit bcf37f0

Browse files
Merge pull request #47 from tkishel/refactor_code_and_scripts_v2
(refactor) reorganize into service and system classes
2 parents 7cae4c4 + 806efb7 commit bcf37f0

31 files changed

+884
-1056
lines changed

README.md

Lines changed: 127 additions & 95 deletions
Large diffs are not rendered by default.

files/amq_metrics

Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
#!/opt/puppetlabs/puppet/bin/ruby
22

3-
require "net/https"
4-
require "json"
5-
require "uri"
3+
require 'net/https'
4+
require 'json'
5+
require 'uri'
66
require 'time'
77
require 'optparse'
88
require 'yaml'
99

1010
options = {}
1111
OptionParser.new do |opts|
1212
opts.banner = "Usage: amq_metrics [options]"
13-
1413
opts.on('-p', '--[no-]print', 'Print to stdout') { |p| options[:print] = p }
1514
opts.on('-m [TYPE]', '--metrics_type [TYPE]', 'Type of metric to collect') { |v| options[:metrics_type] = v }
16-
opts.on('-o [DIR]', '--output-dir [DIR]', 'Directory to save output to') { |o| options[:output_dir] = o }
15+
opts.on('-o [DIR]', '--output_dir [DIR]', 'Directory to save output to') { |o| options[:output_dir] = o }
1716
end.parse!
1817

1918
if options[:metrics_type].nil? then
@@ -22,16 +21,30 @@ if options[:metrics_type].nil? then
2221
end
2322

2423
METRICS_TYPE = options[:metrics_type]
25-
config = YAML.load_file(File.join(File.dirname(File.expand_path(__FILE__)),"#{METRICS_TYPE}_config.yaml"))
2624

27-
OUTPUT_DIR = options[:output_dir]
28-
HOSTS = config['hosts']
29-
PORT = config['metrics_port']
30-
METRICS = config['additional_metrics']
31-
CLIENTCERT = config['clientcert']
32-
PE_VERSION = config['pe_version']
25+
config_file = File.expand_path("../../config/#{METRICS_TYPE}.yaml", __FILE__)
26+
config = YAML.load_file(config_file)
27+
28+
OUTPUT_DIR = options[:output_dir]
29+
PE_VERSION = config['pe_version']
30+
CERTNAME = config['clientcert']
31+
HOSTS = config['hosts']
32+
PORT = config['metrics_port']
33+
ADDITIONAL_METRICS = config['additional_metrics']
3334

34-
POST_DATA = METRICS.to_json
35+
POST_DATA = ADDITIONAL_METRICS.to_json
36+
37+
def get_endpoint(url, post_data)
38+
uri = URI.parse(url)
39+
http = Net::HTTP.new(uri.host, uri.port)
40+
request = Net::HTTP::Post.new(uri.request_uri)
41+
request.basic_auth("admin", "admin")
42+
request.body = post_data
43+
data = http.request(request)
44+
rescue Exception => e
45+
$error_array << "#{e}"
46+
data = {}
47+
end
3548

3649
def recurse_merge!(a,b)
3750
a.merge!(b) do |_,aa,bb|
@@ -56,26 +69,14 @@ end
5669

5770
$error_array = []
5871

59-
def get_endpoint(url, post_data)
60-
uri = URI.parse(url)
61-
http = Net::HTTP.new(uri.host, uri.port)
62-
request = Net::HTTP::Post.new(uri.request_uri)
63-
request.basic_auth("admin", "admin")
64-
request.body = post_data
65-
data = http.request(request)
66-
rescue Exception => e
67-
$error_array << "#{e}"
68-
data = {}
69-
end
70-
71-
filename = Time.now.utc.strftime('%Y%m%dT%H%M%SZ') + '.json'
72+
output_file = Time.now.utc.strftime('%Y%m%dT%H%M%SZ') + '.json'
7273

7374
HOSTS.each do |host|
7475
begin
7576
timestamp = Time.now
7677
dataset = {'timestamp' => timestamp.utc.iso8601, 'servers' => {}}
7778
hostkey = host.gsub('.', '-')
78-
dataset['servers'][hostkey] = {METRICS_TYPE => {}}
79+
dataset['servers'][hostkey] = { METRICS_TYPE => {} }
7980

8081
host_url = "https://#{host}:#{PORT}/api/jolokia"
8182
response = get_endpoint(host_url, POST_DATA)
@@ -104,7 +105,7 @@ HOSTS.each do |host|
104105
unless OUTPUT_DIR.nil? then
105106
Dir.chdir(OUTPUT_DIR) do
106107
Dir.mkdir(host) unless File.exist?(host)
107-
File.open(File.join(host, filename), 'w') do |file|
108+
File.open(File.join(host, output_file), 'w') do |file|
108109
file.write(json_dataset)
109110
end
110111
end

files/create-metrics-archive

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#!/bin/bash
2+
3+
while [[ $1 ]]; do
4+
case "$1" in
5+
'-d'|'--directory')
6+
metrics_directory="$2"
7+
;;
8+
'-r'|'--retention-days')
9+
retention_days="$2"
10+
esac
11+
shift 2
12+
done
13+
14+
# Arguments and defaults.
15+
16+
metrics_directory="${metrics_directory:-/opt/puppetlabs/puppet-metrics-collector}"
17+
retention_days="${retention_days:-30}"
18+
timestamp="$(date +%Y.%m.%d.%H.%M.%S)"
19+
output_file="puppet-metrics-${timestamp}.tar.gz"
20+
21+
find "$metrics_directory" -type f -ctime -"$retention_days" -and \( -name "*json" -or -name "*gz" \) | \
22+
tar --create --gzip --file "$output_file" --files-from - --transform "s,^${metrics_directory#/},puppet-metrics-${timestamp}," || {
23+
echo "Error: could not create metrics archive: $output_file from $metrics_directory"
24+
exit 1
25+
}
26+
27+
echo "Created metrics archive: $output_file"

files/json2timeseriesdb

100755100644
Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
#!/opt/puppetlabs/puppet/bin/ruby
22

3-
# Keep these two scripts in sync.
4-
# puppetlabs-puppet-metrics-viewer/json2graphite.rb
5-
# puppetlabs-puppet_metrics_collector/files/json2timeseriesdb
6-
73
require 'fcntl'
84
require 'json'
95
require 'time'

files/metrics_tidy

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#!/bin/bash
2+
3+
while [[ $1 ]]; do
4+
case "$1" in
5+
'-d'|'--directory')
6+
metrics_directory="$2"
7+
;;
8+
'-r'|'--retention-days')
9+
retention_days="$2"
10+
esac
11+
shift 2
12+
done
13+
14+
# Arguments and defaults.
15+
16+
# Guard against deleting or archiving files outside of a Puppet service metrics directory.
17+
valid_paths=(puppetserver puppetdb orchestrator ace bolt activemq)
18+
19+
metrics_directory="${metrics_directory:-/opt/puppetlabs/puppet-metrics-collector/puppetserver}"
20+
retention_days="${retention_days:-90}"
21+
22+
# Parameter expansion to strip everything before the last '/', giving us the basename.
23+
metrics_type="${metrics_directory##*/}"
24+
25+
# Check that $metrics_directory ends in a Puppet service.
26+
paths_regex="$(IFS='|'; echo "${valid_paths[*]}")"
27+
[[ $metrics_directory =~ ${paths_regex}$ ]] || {
28+
echo "Error: Invalid metrics directory. Must end in one of: $(echo -n "${valid_paths[@]}")."
29+
exit 1
30+
}
31+
32+
# Delete files in a Puppet service metrics directory older than the retention period, in days.
33+
find "$metrics_directory" -type f -ctime +"$retention_days" -delete
34+
35+
# Compress the remaining files in a Puppet service metrics directory.
36+
find "$metrics_directory" -type f -name "*json" | \
37+
tar --create --gzip --file "${metrics_directory}/${metrics_type}-$(date +%Y.%m.%d.%H.%M.%S).tar.gz" --files-from - 2>/dev/null

files/pe_metrics.rb

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
#!/opt/puppetlabs/puppet/bin/ruby
2+
3+
require 'net/https'
4+
require 'json'
5+
require 'uri'
6+
require 'time'
7+
require 'optparse'
8+
require 'yaml'
9+
10+
options = {}
11+
12+
OptionParser.new do |opts|
13+
opts.banner = "Usage: #{File.basename(__FILE__)} [options]"
14+
opts.on('-p', '--[no-]print', 'Print to STDOUT') { |p| options[:print] = p }
15+
opts.on('-m [TYPE]', '--metrics_type [TYPE]', 'Type of metrics to collect') { |v| options[:metrics_type] = v }
16+
opts.on('-o [DIR]', '--output_dir [DIR]', 'Directory to save output to') { |o| options[:output_dir] = o }
17+
opts.on('--metrics_port [PORT]', 'The port the metrics service runs on') { |port| options[:metrics_port] = port }
18+
opts.on('--[no-]ssl', 'Use SSL when collecting metrics') { |ssl| options[:ssl] = ssl }
19+
end.parse!
20+
21+
if options[:metrics_type].nil? then
22+
STDERR.puts '--metrics_type (-m) is a required argument'
23+
exit 1
24+
end
25+
26+
# Allow scripts that require this script to access the options hash.
27+
OPTIONS = options
28+
29+
config_file = File.expand_path("../../config/#{options[:metrics_type]}.yaml", __FILE__)
30+
config = YAML.load_file(config_file)
31+
32+
def coalesce(higher_precedence, lower_precedence, default = nil)
33+
[higher_precedence, lower_precedence].find{|x|!x.nil?} || default
34+
end
35+
36+
METRICS_TYPE = options[:metrics_type]
37+
OUTPUT_DIR = options[:output_dir]
38+
PE_VERSION = config['pe_version']
39+
CERTNAME = config['clientcert']
40+
HOSTS = config['hosts']
41+
PORT = coalesce(options[:metrics_port], config['metrics_port'])
42+
USE_SSL = coalesce(options[:ssl], config['ssl'], true)
43+
EXCLUDES = config['excludes']
44+
ADDITIONAL_METRICS = config['additional_metrics']
45+
46+
# Metrics endpoints for our Puma services require a client certificate with SSL.
47+
# Metrics endpoints for our Trapper Keeper services do not require a client certificate.
48+
49+
if USE_CLIENTCERT
50+
SSLDIR = `/opt/puppetlabs/bin/puppet config print ssldir`.chomp
51+
end
52+
53+
$error_array = []
54+
55+
def generate_host_url(host, port, use_ssl)
56+
protocol = use_ssl ? 'https' : 'http'
57+
58+
host_url = "#{protocol}://#{host}:#{port}"
59+
end
60+
61+
def setup_connection(url, use_ssl)
62+
uri = URI.parse(url)
63+
http = Net::HTTP.new(uri.host, uri.port)
64+
65+
if use_ssl then
66+
http.use_ssl = true
67+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
68+
69+
if USE_SSL && USE_CLIENTCERT
70+
# PE Puma services serve metrics from endpoints requiring a client certificate.
71+
# If https://github.com/puma/puma/pull/2098 is merged into the Puma used by PE,
72+
# we can collect metrics from /stats and /gc-stats without a client certificate.
73+
http.ca_path = "#{SSLDIR}/ca"
74+
http.ca_file = "#{SSLDIR}/certs/ca.pem"
75+
http.cert = OpenSSL::X509::Certificate.new(File.read("#{SSLDIR}/certs/#{CERTNAME}.pem"))
76+
http.key = OpenSSL::PKey::RSA.new(File.read("#{SSLDIR}/private_keys/#{CERTNAME}.pem"))
77+
end
78+
end
79+
80+
return http, uri
81+
end
82+
83+
def get_endpoint(url, use_ssl)
84+
http, uri = setup_connection(url, use_ssl)
85+
86+
data = JSON.parse(http.get(uri.request_uri).body)
87+
rescue Exception => e
88+
$error_array << "#{e}"
89+
data = {}
90+
end
91+
92+
def post_endpoint(url, use_ssl, post_data)
93+
http, uri = setup_connection(url, use_ssl)
94+
95+
request = Net::HTTP::Post.new(uri.request_uri)
96+
request.content_type = 'application/json'
97+
request.body = post_data
98+
99+
data = JSON.parse(http.request(request).body)
100+
rescue Exception => e
101+
$error_array << "#{e}"
102+
data = {}
103+
end
104+
105+
def individually_retrieve_additional_metrics(host, port, use_ssl, metrics)
106+
host_url = generate_host_url(host, port, use_ssl)
107+
108+
metrics_array = []
109+
metrics.each do |metric|
110+
endpoint = URI.escape("#{host_url}/metrics/v1/mbeans/#{metric['url']}")
111+
metrics_array << { 'name' => metric['name'], 'data' => get_endpoint(endpoint, use_ssl) }
112+
end
113+
114+
return metrics_array
115+
end
116+
117+
def bulk_retrieve_additional_metrics(host, port, use_ssl, metrics)
118+
host_url = generate_host_url(host, port, use_ssl)
119+
120+
post_data = []
121+
metrics.each do |metric|
122+
post_data << metric['url']
123+
end
124+
125+
endpoint = "#{host_url}/metrics/v1/mbeans"
126+
metrics_output = post_endpoint(endpoint, use_ssl, post_data.to_json)
127+
metrics_array = []
128+
129+
metrics.each_index do |index|
130+
metric_name = metrics[index]['name']
131+
metric_data = metrics_output[index]
132+
metrics_array << { 'name' => metric_name, 'data' => metric_data }
133+
end
134+
135+
return metrics_array
136+
end
137+
138+
def retrieve_additional_metrics(host, port, use_ssl, pe_version, metrics)
139+
if Gem::Version.new(pe_version) < Gem::Version.new('2016.2.0') then
140+
metrics_array = individually_retrieve_additional_metrics(host, port, use_ssl, metrics)
141+
else
142+
metrics_array = bulk_retrieve_additional_metrics(host, port, use_ssl, metrics)
143+
end
144+
145+
return metrics_array
146+
end
147+
148+
def filter_metrics(dataset, filters)
149+
return dataset if filters.empty?
150+
151+
case dataset
152+
when Hash
153+
dataset = dataset.inject({}) {|m, (k, v)| m[k] = filter_metrics(v,filters) unless filters.include? k ; m }
154+
when Array
155+
dataset.map! {|e| filter_metrics(e,filters)}
156+
end
157+
158+
return dataset
159+
end

0 commit comments

Comments
 (0)