hieracles 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/Gemfile +2 -0
- data/README.md +54 -8
- data/bin/hc +12 -6
- data/bin/ppdb +42 -0
- data/hc.1 +50 -8
- data/lib/hieracles.rb +2 -2
- data/lib/hieracles/config.rb +31 -19
- data/lib/hieracles/format.rb +4 -0
- data/lib/hieracles/formats/console.rb +49 -16
- data/lib/hieracles/formats/csv.rb +12 -8
- data/lib/hieracles/formats/json.rb +19 -6
- data/lib/hieracles/formats/plain.rb +24 -3
- data/lib/hieracles/formats/rawyaml.rb +4 -0
- data/lib/hieracles/formats/yaml.rb +4 -0
- data/lib/hieracles/node.rb +55 -10
- data/lib/hieracles/notification.rb +31 -0
- data/lib/hieracles/options/hc.rb +109 -0
- data/lib/hieracles/options/ppdb.rb +32 -0
- data/lib/hieracles/optparse.rb +4 -43
- data/lib/hieracles/puppetdb.rb +12 -0
- data/lib/hieracles/puppetdb/apierror.rb +10 -0
- data/lib/hieracles/puppetdb/client.rb +63 -0
- data/lib/hieracles/puppetdb/filter.rb +15 -0
- data/lib/hieracles/puppetdb/fixsslconnectionadapter.rb +25 -0
- data/lib/hieracles/puppetdb/query.rb +79 -0
- data/lib/hieracles/puppetdb/request.rb +44 -0
- data/lib/hieracles/puppetdb/response.rb +14 -0
- data/ppdb.1 +158 -0
- data/spec/files/config.yml +2 -0
- data/spec/files/config_withdb.yml +9 -0
- data/spec/files/facts.json +110 -0
- data/spec/files/facts.yaml +103 -0
- data/spec/files/hiera_columns.yaml +16 -0
- data/spec/files/hiera_deep.yaml +17 -0
- data/spec/files/hiera_deeper.yaml +17 -0
- data/spec/files/ssl/bad-ca.crt +1 -0
- data/spec/files/ssl/bad-cert.crt +1 -0
- data/spec/files/ssl/bad-key.pem +1 -0
- data/spec/files/ssl/ca.crt +16 -0
- data/spec/files/ssl/cert.crt +16 -0
- data/spec/files/ssl/key-pass.pem +18 -0
- data/spec/files/ssl/key.pem +15 -0
- data/spec/lib/config_spec.rb +51 -11
- data/spec/lib/format_spec.rb +5 -0
- data/spec/lib/formats/console_spec.rb +24 -3
- data/spec/lib/formats/csv_spec.rb +15 -0
- data/spec/lib/formats/json_spec.rb +22 -2
- data/spec/lib/formats/plain_spec.rb +23 -3
- data/spec/lib/formats/rawyaml_spec.rb +13 -0
- data/spec/lib/formats/yaml_spec.rb +138 -48
- data/spec/lib/hiera_spec.rb +0 -1
- data/spec/lib/hieracles_spec.rb +8 -0
- data/spec/lib/interpolate_spec.rb +49 -0
- data/spec/lib/node_spec.rb +50 -9
- data/spec/lib/notification_spec.rb +29 -0
- data/spec/lib/options/hc_spec.rb +82 -0
- data/spec/lib/optparse_spec.rb +7 -7
- data/spec/lib/puppetdb/apierror_spec.rb +11 -0
- data/spec/lib/puppetdb/client_spec.rb +64 -0
- data/spec/lib/puppetdb/fixsslconnectionadapter_spec.rb +107 -0
- data/spec/lib/puppetdb/query_spec.rb +39 -0
- data/spec/lib/puppetdb/request_spec.rb +61 -0
- data/spec/lib/puppetdb/response_spec.rb +13 -0
- data/spec/spec_helper.rb +4 -4
- metadata +133 -30
- data/Rakefile +0 -14
- data/hieracles.gemspec +0 -90
- data/lib/hieracles/help.rb +0 -38
- data/spec/lib/help_spec.rb +0 -8
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'hieracles/optparse'
|
2
|
+
|
3
|
+
module Hieracles
|
4
|
+
module Options
|
5
|
+
class Ppdb < Hieracles::Optparse
|
6
|
+
|
7
|
+
def available_options
|
8
|
+
{
|
9
|
+
version: {
|
10
|
+
has_arg: false,
|
11
|
+
aliases: ['v', 'version']
|
12
|
+
}
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.usage
|
17
|
+
return <<-END
|
18
|
+
|
19
|
+
Usage: ppdb <endpoint> <command> [extra_args]
|
20
|
+
|
21
|
+
Available commands:
|
22
|
+
node info <fqdn>
|
23
|
+
node facts <fqdn>
|
24
|
+
node resources <fqdn>
|
25
|
+
|
26
|
+
END
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
data/lib/hieracles/optparse.rb
CHANGED
@@ -4,48 +4,9 @@ module Hieracles
|
|
4
4
|
|
5
5
|
attr_reader :options, :payload
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
aliases: ['c', 'conf', 'config']
|
11
|
-
},
|
12
|
-
format: {
|
13
|
-
has_arg: true,
|
14
|
-
aliases: ['f', 'format']
|
15
|
-
},
|
16
|
-
params: {
|
17
|
-
has_arg: true,
|
18
|
-
aliases: ['p', 'params']
|
19
|
-
},
|
20
|
-
hierafile: {
|
21
|
-
has_arg: true,
|
22
|
-
aliases: ['h', 'hierafile']
|
23
|
-
},
|
24
|
-
basepath: {
|
25
|
-
has_arg: true,
|
26
|
-
aliases: ['b', 'basepath']
|
27
|
-
},
|
28
|
-
encpath: {
|
29
|
-
has_arg: true,
|
30
|
-
aliases: ['e', 'encpath']
|
31
|
-
},
|
32
|
-
version: {
|
33
|
-
has_arg: false,
|
34
|
-
aliases: ['v', 'version']
|
35
|
-
},
|
36
|
-
yaml_facts: {
|
37
|
-
has_arg: true,
|
38
|
-
aliases: ['y', 'yaml']
|
39
|
-
},
|
40
|
-
json_facts: {
|
41
|
-
has_arg: true,
|
42
|
-
aliases: ['j', 'json']
|
43
|
-
},
|
44
|
-
interactive: {
|
45
|
-
has_arg: false,
|
46
|
-
aliases: ['i', 'interactive']
|
47
|
-
}
|
48
|
-
}
|
7
|
+
def available_options
|
8
|
+
{}
|
9
|
+
end
|
49
10
|
|
50
11
|
def initialize(array)
|
51
12
|
@options = {}
|
@@ -71,7 +32,7 @@ module Hieracles
|
|
71
32
|
|
72
33
|
def optionkeys
|
73
34
|
back = {}
|
74
|
-
|
35
|
+
available_options.each do |k, v|
|
75
36
|
v[:aliases].each do |a|
|
76
37
|
back[a] = { var: k, has_args: v[:has_arg] }
|
77
38
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'hieracles/puppetdb/fixsslconnectionadapter'
|
2
|
+
require 'hieracles/puppetdb/apierror'
|
3
|
+
require 'hieracles/puppetdb/query'
|
4
|
+
require 'hieracles/puppetdb/filter'
|
5
|
+
require 'hieracles/puppetdb/client'
|
6
|
+
require 'hieracles/puppetdb/request'
|
7
|
+
require 'hieracles/puppetdb/response'
|
8
|
+
|
9
|
+
module Hieracles
|
10
|
+
module Puppetdb
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
|
3
|
+
module Hieracles
|
4
|
+
module Puppetdb
|
5
|
+
class Client
|
6
|
+
include HTTParty
|
7
|
+
|
8
|
+
def initialize(options, version = 3)
|
9
|
+
@version = version
|
10
|
+
setup_if_ssl(options)
|
11
|
+
options['port'] ||= 8080
|
12
|
+
options['host'] ||= 'localhost'
|
13
|
+
scheme = options['usessl'] ? "https://" : "http://"
|
14
|
+
self.class.base_uri(scheme +
|
15
|
+
options['host'] +
|
16
|
+
':' + options['port'].to_s +
|
17
|
+
'/v' + @version.to_s())
|
18
|
+
end
|
19
|
+
|
20
|
+
def setup_if_ssl(options)
|
21
|
+
if options['usessl']
|
22
|
+
self.class.default_options = {:options => options}
|
23
|
+
self.class.connection_adapter(FixSSLConnectionAdapter)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def request(endpoint, method = 'get', query = nil, opts = {})
|
28
|
+
ret = send("#{method}_request".to_sym, endpoint, query, opts)
|
29
|
+
if ret.code.to_s() =~ /^[4|5]/ or ret.parsed_response.length < 1
|
30
|
+
notifications = [ Hieracles::Notification.new('puppetdb', 'No match.', 'error') ]
|
31
|
+
Response.new({}, 0, notifications)
|
32
|
+
else
|
33
|
+
total = ret.headers['X-Records']
|
34
|
+
if total.nil?
|
35
|
+
total = ret.parsed_response.length
|
36
|
+
end
|
37
|
+
|
38
|
+
Response.new(ret.parsed_response, total)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def get_request(endpoint, query, opts)
|
43
|
+
path = "/" + endpoint
|
44
|
+
if query
|
45
|
+
json_query = JSON.dump(query)
|
46
|
+
filtered_opts = {'query' => json_query}
|
47
|
+
opts.each do |k,v|
|
48
|
+
if k == :counts_filter
|
49
|
+
filtered_opts['counts-filter'] = JSON.dump(v)
|
50
|
+
else
|
51
|
+
filtered_opts[k.to_s.sub("_", "-")] = v
|
52
|
+
end
|
53
|
+
end
|
54
|
+
puts path ; puts filtered_opts ; exit(0)
|
55
|
+
self.class.get(path, query: filtered_opts)
|
56
|
+
else
|
57
|
+
self.class.get(path)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
|
3
|
+
module Hieracles
|
4
|
+
module Puppetdb
|
5
|
+
class FixSSLConnectionAdapter < HTTParty::ConnectionAdapter
|
6
|
+
def attach_ssl_certificates(http, options)
|
7
|
+
raise(IOError, "Cert file #{options['cert']} not found.") unless File.exist? options['cert'].to_s
|
8
|
+
raise(IOError, "Key file #{options['key']} not found.") unless File.exist? options['key']
|
9
|
+
raise(IOError, "CA file #{options['ca_file']} not found.") unless File.exist? options['ca_file']
|
10
|
+
http.cert = OpenSSL::X509::Certificate.new(File.read(options['cert']))
|
11
|
+
if options['key_password']
|
12
|
+
http.key = OpenSSL::PKey::RSA.new(File.read(options['key']), options['key_password'])
|
13
|
+
else
|
14
|
+
http.key = OpenSSL::PKey::RSA.new(File.read(options['key']))
|
15
|
+
end
|
16
|
+
http.ca_file = options['ca_file']
|
17
|
+
if options['verify_peer']
|
18
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
19
|
+
else
|
20
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Hieracles
|
2
|
+
module Puppetdb
|
3
|
+
class Query
|
4
|
+
|
5
|
+
def initialize(elements)
|
6
|
+
@elements = parse(elements)
|
7
|
+
end
|
8
|
+
|
9
|
+
def parse(elements)
|
10
|
+
items = []
|
11
|
+
index = 0
|
12
|
+
if elements.length > 1
|
13
|
+
elements.each do |e|
|
14
|
+
puts items.inspect
|
15
|
+
if e == 'or'
|
16
|
+
index++
|
17
|
+
continue
|
18
|
+
end
|
19
|
+
if /(.*)(>|<|=|!=|~)(.*)/.match e
|
20
|
+
items[index] ||= []
|
21
|
+
items[index] << [$2, $1, $3]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
else
|
25
|
+
if /(.*)(>|<|=|!=|~)(.*)/.match elements[0]
|
26
|
+
items = [$2, $1, $3]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
items
|
30
|
+
end
|
31
|
+
|
32
|
+
def output
|
33
|
+
back = []
|
34
|
+
if @elements.length > 1
|
35
|
+
back << 'or'
|
36
|
+
@elements.each do |e|
|
37
|
+
back << build_and(e)
|
38
|
+
end
|
39
|
+
else
|
40
|
+
@elements.each do |e|
|
41
|
+
back << build_and(e)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
back
|
45
|
+
end
|
46
|
+
|
47
|
+
def build_and(arrays)
|
48
|
+
if arrays.length > 1
|
49
|
+
['and', arrays]
|
50
|
+
else
|
51
|
+
arrays
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def defined?
|
56
|
+
@elements.count > 0
|
57
|
+
end
|
58
|
+
|
59
|
+
def match(item)
|
60
|
+
@elements.each do |e|
|
61
|
+
matched = false
|
62
|
+
e.each do |a|
|
63
|
+
case a[0]
|
64
|
+
when '='
|
65
|
+
|
66
|
+
when '!='
|
67
|
+
when '<'
|
68
|
+
when '>'
|
69
|
+
when '!'
|
70
|
+
else
|
71
|
+
end
|
72
|
+
end
|
73
|
+
return true if matched
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Hieracles
|
2
|
+
module Puppetdb
|
3
|
+
class Request
|
4
|
+
|
5
|
+
def initialize(options)
|
6
|
+
@client = Hieracles::Puppetdb::Client.new options
|
7
|
+
end
|
8
|
+
|
9
|
+
def node_info(fqdn)
|
10
|
+
@client.request("nodes/#{fqdn}")
|
11
|
+
end
|
12
|
+
alias_method :node_infos, :node_info
|
13
|
+
|
14
|
+
def node_facts(fqdn, regex = //)
|
15
|
+
resp = @client.request("nodes/#{fqdn}/facts")
|
16
|
+
filter = Filter.new regex
|
17
|
+
resp.data = resp.data.reduce({}) do |a, d|
|
18
|
+
if filter.match(d['name'])
|
19
|
+
a[d['name'].to_sym] = d['value']
|
20
|
+
end
|
21
|
+
a
|
22
|
+
end
|
23
|
+
resp
|
24
|
+
end
|
25
|
+
alias_method :node_fact, :node_facts
|
26
|
+
|
27
|
+
def node_resources(fqdn, *args)
|
28
|
+
resp = @client.request("nodes/#{fqdn}/resources")
|
29
|
+
field = args.shift
|
30
|
+
resp.data = resp.data.reduce({}) do |a, d|
|
31
|
+
if !field || Regexp.new(field).match(d['title'])
|
32
|
+
a[d['title']] = d
|
33
|
+
end
|
34
|
+
a
|
35
|
+
end
|
36
|
+
resp
|
37
|
+
end
|
38
|
+
alias_method :node_res, :node_resources
|
39
|
+
|
40
|
+
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Hieracles
|
2
|
+
module Puppetdb
|
3
|
+
class Response
|
4
|
+
attr_reader :data, :total_records, :notifications
|
5
|
+
attr_writer :data
|
6
|
+
|
7
|
+
def initialize(data, total_records = nil, notifications = nil)
|
8
|
+
@data = data
|
9
|
+
@total_records = total_records
|
10
|
+
@notifications = notifications
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/ppdb.1
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
.TH ppdb 1 "2015-12-05" "version 0.2.1" "Hieracles command manual"
|
2
|
+
|
3
|
+
.SH NAME
|
4
|
+
ppdb \- Command line tool to query puppetdb
|
5
|
+
.SH SYNOPSIS
|
6
|
+
.B ppdb
|
7
|
+
.I object
|
8
|
+
.I command
|
9
|
+
.B [
|
10
|
+
.I options
|
11
|
+
.B ]
|
12
|
+
|
13
|
+
.SH DESCRIPTION
|
14
|
+
.PP
|
15
|
+
This tool is part of the Hieracles tools suite.
|
16
|
+
.PP
|
17
|
+
PuppetDB stores information gathered by Puppet when nodes run
|
18
|
+
the puppet client. It exposes a very neat REST interface, but
|
19
|
+
its query system can be a little complicated.
|
20
|
+
.PP
|
21
|
+
The purpose of
|
22
|
+
.B ppdb
|
23
|
+
is to overlay the calls to the REST API and propose a more
|
24
|
+
user-friendly way to query the Puppet Database (puppetdb).
|
25
|
+
.PP
|
26
|
+
Note that this tool is designed to work fine with the version 3
|
27
|
+
of the PuppetDB API.
|
28
|
+
.PP
|
29
|
+
Ref http://docs.puppetlabs.com/puppetdb/2.3/api/
|
30
|
+
.PP
|
31
|
+
.B ppdb
|
32
|
+
takes at minimum 2 arguments, an object (or endpoint)
|
33
|
+
and a subcommand, plus extra parameters depending the subcommand.
|
34
|
+
.PP
|
35
|
+
The object is like the endpoint in PuppetDB API, but we extend it
|
36
|
+
to a different list of possible objects:
|
37
|
+
|
38
|
+
.IP node 10
|
39
|
+
Uses the nodes endpoint. It accepts the subcommands:
|
40
|
+
.RS
|
41
|
+
|
42
|
+
.IP info 10
|
43
|
+
will display the equivalent of
|
44
|
+
.I "GET /v3/nodes/<NODE>"
|
45
|
+
and takes the node
|
46
|
+
.B certname
|
47
|
+
as an argument.
|
48
|
+
.RS 10
|
49
|
+
Optionaly, you can add a filter to limit what fields are displayed.
|
50
|
+
.TP
|
51
|
+
eg. ppdb node info <fqdn>
|
52
|
+
.RS
|
53
|
+
will display all the fields from the API response
|
54
|
+
.RE
|
55
|
+
eg. ppdb node info <fqdn> time
|
56
|
+
.RS
|
57
|
+
will only display the fields containing
|
58
|
+
.I time
|
59
|
+
in their label.
|
60
|
+
.RE
|
61
|
+
.RE
|
62
|
+
|
63
|
+
.IP facts 10
|
64
|
+
will display the equivalent of
|
65
|
+
.I "GET /v3/nodes/<NODE>/facts"
|
66
|
+
and takes the node
|
67
|
+
.B certname
|
68
|
+
as an argument.
|
69
|
+
.RS 10
|
70
|
+
As the result can be verbose, you can add a filter to limit what
|
71
|
+
fields are displayed.
|
72
|
+
.TP
|
73
|
+
eg. ppdb node facts <fqdn>
|
74
|
+
.RS
|
75
|
+
will display all the fields from the API response
|
76
|
+
.RE
|
77
|
+
eg. ppdb node facts <fqdn> memory
|
78
|
+
.RS
|
79
|
+
will only display the fields containing
|
80
|
+
.I memory
|
81
|
+
in their label.
|
82
|
+
.RE
|
83
|
+
.RE
|
84
|
+
|
85
|
+
.IP resources 10
|
86
|
+
will display the equivalent of
|
87
|
+
.I "GET /v3/nodes/<NODE>/resources"
|
88
|
+
and takes the node
|
89
|
+
.B certname
|
90
|
+
as an argument. You can use
|
91
|
+
.I res
|
92
|
+
instead of
|
93
|
+
.I resources
|
94
|
+
for shorter.
|
95
|
+
.RS 10
|
96
|
+
As the result can be verbose, you can add a filter to limit what
|
97
|
+
resources are displayed.
|
98
|
+
.TP
|
99
|
+
eg. ppdb node res <fqdn>
|
100
|
+
.RS
|
101
|
+
will display all the fields from the API response
|
102
|
+
.RE
|
103
|
+
eg. ppdb node res <fqdn> nagios
|
104
|
+
.RS
|
105
|
+
will only display the fields containing
|
106
|
+
.I nagios
|
107
|
+
in their label.
|
108
|
+
.RE
|
109
|
+
.RE
|
110
|
+
|
111
|
+
.SH OPTIONS
|
112
|
+
|
113
|
+
.TP
|
114
|
+
.PD 0
|
115
|
+
.B \-v
|
116
|
+
.TP
|
117
|
+
.PD
|
118
|
+
.B \-\-version
|
119
|
+
outputs version.
|
120
|
+
|
121
|
+
|
122
|
+
.SH FILES
|
123
|
+
.I ~/.config/hieracles/config.yaml
|
124
|
+
.RS
|
125
|
+
ppdb uses the same configuration file as hieracles, and relies on
|
126
|
+
its information to find the url of the PuppetDB
|
127
|
+
|
128
|
+
.SH EXAMPLES
|
129
|
+
A typical config file would contain, at minimum:
|
130
|
+
.PP
|
131
|
+
.RS
|
132
|
+
---
|
133
|
+
.RE
|
134
|
+
.RS
|
135
|
+
puppetdb:
|
136
|
+
.RS
|
137
|
+
usessl: false
|
138
|
+
.RE
|
139
|
+
.RS
|
140
|
+
host: localhost
|
141
|
+
.RE
|
142
|
+
.RS
|
143
|
+
port: 8080
|
144
|
+
.RE
|
145
|
+
.RE
|
146
|
+
|
147
|
+
.SH SEE ALSO
|
148
|
+
hiera(1), puppet(8), hc(1)
|
149
|
+
|
150
|
+
.SH BUGS
|
151
|
+
Please report any bug to https://github.com/Gandi/hieracles/issues
|
152
|
+
|
153
|
+
.SH AUTHORS
|
154
|
+
Copyright (c) 2015 gandi.net https://gandi.net
|
155
|
+
.LP
|
156
|
+
Hieracles is written by mose@gandi.net
|
157
|
+
.LP
|
158
|
+
https://github.com/Gandi/hieracles
|