choria-mcorpc-support 2.23.1 → 2.24.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/lib/mcollective.rb +3 -2
- data/lib/mcollective/agent/choria_util.ddl +206 -107
- data/lib/mcollective/agent/choria_util.json +101 -1
- data/lib/mcollective/agent/rpcutil.json +2 -3
- data/lib/mcollective/agent/scout.json +107 -135
- data/lib/mcollective/application/facts.rb +2 -67
- data/lib/mcollective/application/find.rb +1 -1
- data/lib/mcollective/application/plugin.rb +2 -15
- data/lib/mcollective/client.rb +1 -1
- data/lib/mcollective/config.rb +135 -103
- data/lib/mcollective/ddl.rb +0 -1
- data/lib/mcollective/discovery.rb +11 -63
- data/lib/mcollective/discovery/broadcast.ddl +11 -0
- data/lib/mcollective/discovery/choria.ddl +6 -4
- data/lib/mcollective/discovery/delegate.ddl +13 -0
- data/lib/mcollective/discovery/delegate.rb +73 -0
- data/lib/mcollective/discovery/external.ddl +13 -0
- data/lib/mcollective/discovery/file.ddl +13 -0
- data/lib/mcollective/discovery/flatfile.ddl +7 -5
- data/lib/mcollective/discovery/inventory.ddl +13 -0
- data/lib/mcollective/discovery/mc.ddl +3 -3
- data/lib/mcollective/generators.rb +0 -1
- data/lib/mcollective/optionparser.rb +1 -1
- data/lib/mcollective/pluginpackager/forge_packager.rb +1 -1
- data/lib/mcollective/rpc/client.rb +4 -2
- data/lib/mcollective/util.rb +25 -35
- data/lib/mcollective/util/tasks_support.rb +15 -2
- metadata +9 -23
- data/lib/mcollective/data.rb +0 -96
- data/lib/mcollective/data/agent_data.ddl +0 -22
- data/lib/mcollective/data/agent_data.rb +0 -17
- data/lib/mcollective/data/base.rb +0 -68
- data/lib/mcollective/data/bolt_task_data.ddl +0 -90
- data/lib/mcollective/data/bolt_task_data.rb +0 -32
- data/lib/mcollective/data/collective_data.ddl +0 -20
- data/lib/mcollective/data/collective_data.rb +0 -9
- data/lib/mcollective/data/fact_data.ddl +0 -28
- data/lib/mcollective/data/fact_data.rb +0 -55
- data/lib/mcollective/data/fstat_data.ddl +0 -89
- data/lib/mcollective/data/fstat_data.rb +0 -54
- data/lib/mcollective/data/result.rb +0 -50
- data/lib/mcollective/ddl/dataddl.rb +0 -56
- data/lib/mcollective/discovery/choria.rb +0 -223
- data/lib/mcollective/discovery/flatfile.rb +0 -47
- data/lib/mcollective/discovery/stdin.ddl +0 -11
- data/lib/mcollective/discovery/stdin.rb +0 -67
- data/lib/mcollective/generators/data_generator.rb +0 -50
- data/lib/mcollective/generators/templates/data_input_snippet.erb +0 -7
@@ -1,54 +0,0 @@
|
|
1
|
-
module MCollective
|
2
|
-
module Data
|
3
|
-
class Fstat_data < Base
|
4
|
-
query do |file|
|
5
|
-
result[:name] = file
|
6
|
-
result[:output] = "not present"
|
7
|
-
result[:type] = "unknown"
|
8
|
-
result[:mode] = "0000"
|
9
|
-
result[:present] = 0
|
10
|
-
result[:size] = 0
|
11
|
-
result[:mtime] = 0
|
12
|
-
result[:ctime] = 0
|
13
|
-
result[:atime] = 0
|
14
|
-
result[:mtime_seconds] = 0
|
15
|
-
result[:ctime_seconds] = 0
|
16
|
-
result[:atime_seconds] = 0
|
17
|
-
result[:md5] = 0
|
18
|
-
result[:uid] = 0
|
19
|
-
result[:gid] = 0
|
20
|
-
|
21
|
-
if File.exist?(file)
|
22
|
-
result[:output] = "present"
|
23
|
-
result[:present] = 1
|
24
|
-
|
25
|
-
if File.symlink?(file)
|
26
|
-
stat = File.lstat(file)
|
27
|
-
else
|
28
|
-
stat = File.stat(file)
|
29
|
-
end
|
30
|
-
|
31
|
-
[:size, :uid, :gid].each do |item|
|
32
|
-
result[item] = stat.send(item)
|
33
|
-
end
|
34
|
-
|
35
|
-
[:mtime, :ctime, :atime].each do |item|
|
36
|
-
result[item] = stat.send(item).strftime("%F %T")
|
37
|
-
result["#{item}_seconds".to_sym] = stat.send(item).to_i
|
38
|
-
result["#{item}_age".to_sym] = Time.now.to_i - stat.send(item).to_i
|
39
|
-
end
|
40
|
-
|
41
|
-
result[:mode] = "%o" % [stat.mode]
|
42
|
-
result[:md5] = Digest::MD5.hexdigest(File.read(file)) if stat.file?
|
43
|
-
|
44
|
-
result[:type] = "directory" if stat.directory?
|
45
|
-
result[:type] = "file" if stat.file?
|
46
|
-
result[:type] = "symlink" if stat.symlink?
|
47
|
-
result[:type] = "socket" if stat.socket?
|
48
|
-
result[:type] = "chardev" if stat.chardev?
|
49
|
-
result[:type] = "blockdev" if stat.blockdev?
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
@@ -1,50 +0,0 @@
|
|
1
|
-
module MCollective
|
2
|
-
module Data
|
3
|
-
class Result
|
4
|
-
# remove some methods that might clash with commonly
|
5
|
-
# used return data to improve the effectiveness of the
|
6
|
-
# method_missing lookup strategy
|
7
|
-
undef :type if method_defined?(:type)
|
8
|
-
|
9
|
-
def initialize(outputs)
|
10
|
-
@data = {}
|
11
|
-
|
12
|
-
outputs.each_key do |output|
|
13
|
-
@data[output] = Marshal.load(Marshal.dump(outputs[output].fetch(:default, nil)))
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
def include?(key)
|
18
|
-
@data.include?(key.to_sym)
|
19
|
-
end
|
20
|
-
|
21
|
-
def [](key)
|
22
|
-
@data[key.to_sym]
|
23
|
-
end
|
24
|
-
|
25
|
-
def []=(key, val)
|
26
|
-
# checks using the string representation of the class name to avoid deprecations on Bignum and Fixnum
|
27
|
-
raise "Can only store String, Integer, Float or Boolean data but got #{val.class} for key #{key}" unless ["String", "Integer", "Bignum", "Fixnum", "Float", "TrueClass",
|
28
|
-
"FalseClass"].include?(val.class.to_s)
|
29
|
-
|
30
|
-
@data[key.to_sym] = val
|
31
|
-
end
|
32
|
-
|
33
|
-
def keys
|
34
|
-
@data.keys
|
35
|
-
end
|
36
|
-
|
37
|
-
def respond_to_missing?(method, *)
|
38
|
-
include?(method)
|
39
|
-
end
|
40
|
-
|
41
|
-
def method_missing(method, *args)
|
42
|
-
key = method.to_sym
|
43
|
-
|
44
|
-
raise NoMethodError, "undefined local variable or method `%s'" % key unless include?(key)
|
45
|
-
|
46
|
-
@data[key]
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
@@ -1,56 +0,0 @@
|
|
1
|
-
module MCollective
|
2
|
-
module DDL
|
3
|
-
# A DDL file for the data query plugins.
|
4
|
-
#
|
5
|
-
# Query plugins can today take only one input by convention in the DDL that
|
6
|
-
# is called :query, otherwise the input is identical to the standard input.
|
7
|
-
#
|
8
|
-
# metadata :name => "Agent",
|
9
|
-
# :description => "Meta data about installed MColletive Agents",
|
10
|
-
# :author => "R.I.Pienaar <rip@devco.net>",
|
11
|
-
# :license => "ASL 2.0",
|
12
|
-
# :version => "1.0",
|
13
|
-
# :url => "https://docs.puppetlabs.com/mcollective/",
|
14
|
-
# :timeout => 1
|
15
|
-
#
|
16
|
-
# dataquery :description => "Agent Meta Data" do
|
17
|
-
# input :query,
|
18
|
-
# :prompt => "Agent Name",
|
19
|
-
# :description => "Valid agent name",
|
20
|
-
# :type => :string,
|
21
|
-
# :validation => /^[\w\_]+$/,
|
22
|
-
# :maxlength => 20
|
23
|
-
#
|
24
|
-
# [:license, :timeout, :description, :url, :version, :author].each do |item|
|
25
|
-
# output item,
|
26
|
-
# :description => "Agent #{item}",
|
27
|
-
# :display_as => item.to_s.capitalize
|
28
|
-
# end
|
29
|
-
# end
|
30
|
-
class DataDDL < Base
|
31
|
-
def dataquery(input, &block)
|
32
|
-
raise "Data queries need a :description" unless input.include?(:description)
|
33
|
-
raise "Data queries can only have one definition" if @entities[:data]
|
34
|
-
|
35
|
-
@entities[:data] = {:description => input[:description],
|
36
|
-
:input => {},
|
37
|
-
:output => {}}
|
38
|
-
|
39
|
-
@current_entity = :data
|
40
|
-
yield if block_given?
|
41
|
-
@current_entity = nil
|
42
|
-
end
|
43
|
-
|
44
|
-
def input(argument, properties)
|
45
|
-
raise "The only valid input name for a data query is 'query'" if argument != :query
|
46
|
-
|
47
|
-
super
|
48
|
-
end
|
49
|
-
|
50
|
-
# Returns the interface for the data query
|
51
|
-
def dataquery_interface
|
52
|
-
@entities[:data] || {}
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
@@ -1,223 +0,0 @@
|
|
1
|
-
require "net/http"
|
2
|
-
require_relative "../util/choria"
|
3
|
-
|
4
|
-
module MCollective
|
5
|
-
class Discovery
|
6
|
-
class Choria
|
7
|
-
def self.discover(filter, timeout, limit=0, client=nil)
|
8
|
-
Choria.new(filter, timeout, limit, client).discover
|
9
|
-
end
|
10
|
-
|
11
|
-
attr_reader :timeout, :limit, :client, :config
|
12
|
-
attr_accessor :filter
|
13
|
-
|
14
|
-
def initialize(filter, timeout, limit, client)
|
15
|
-
@filter = filter
|
16
|
-
@timeout = timeout
|
17
|
-
@limit = limit
|
18
|
-
@client = client
|
19
|
-
@config = Config.instance
|
20
|
-
end
|
21
|
-
|
22
|
-
# Search for nodes
|
23
|
-
#
|
24
|
-
# @return [Array<String>] list of certnames found
|
25
|
-
def discover
|
26
|
-
queries = []
|
27
|
-
|
28
|
-
if choria.proxied_discovery?
|
29
|
-
Log.debug("Performing discovery against a PuppetDB Proxy")
|
30
|
-
|
31
|
-
choria.proxy_discovery_query(proxy_request)
|
32
|
-
else
|
33
|
-
Log.debug("Performing direct discovery against PuppetDB")
|
34
|
-
queries << discover_collective(filter["collective"]) if filter["collective"]
|
35
|
-
queries << discover_nodes(filter["identity"]) unless filter["identity"].empty?
|
36
|
-
queries << discover_classes(filter["cf_class"]) unless filter["cf_class"].empty?
|
37
|
-
queries << discover_facts(filter["fact"]) unless filter["fact"].empty?
|
38
|
-
queries << discover_agents(filter["agent"]) unless filter["agent"].empty?
|
39
|
-
|
40
|
-
choria.pql_query(node_search_string(queries.compact), true)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
# Creates a request hash for the discovery proxy
|
45
|
-
#
|
46
|
-
# @return [Hash]
|
47
|
-
def proxy_request
|
48
|
-
request = {}
|
49
|
-
|
50
|
-
request["collective"] = filter["collective"] if filter["collective"]
|
51
|
-
request["identities"] = filter["identity"] unless filter["identity"].empty?
|
52
|
-
request["classes"] = filter["cf_class"] unless filter["cf_class"].empty?
|
53
|
-
request["facts"] = filter["fact"] unless filter["fact"].empty?
|
54
|
-
request["agents"] = filter["agent"] unless filter["agent"].empty?
|
55
|
-
|
56
|
-
request
|
57
|
-
end
|
58
|
-
|
59
|
-
# Discovers nodes in a specific collective
|
60
|
-
#
|
61
|
-
# @param filter [String] a collective name
|
62
|
-
# @return [String] a query string
|
63
|
-
def discover_collective(filter)
|
64
|
-
'certname in inventory[certname] { facts.mcollective.server.collectives.match("\d+") = "%s" }' % filter
|
65
|
-
end
|
66
|
-
|
67
|
-
# Searches for facts
|
68
|
-
#
|
69
|
-
# Nodes are searched using an `and` operator via the discover_classes method
|
70
|
-
#
|
71
|
-
# When the `rpcutil` or `scout` agent is required it will look for `Mcollective` class
|
72
|
-
# otherwise `Mcollective_avent_agentname` thus it will only find plugins
|
73
|
-
# installed using the `choria/mcollective` AIO plugin packager
|
74
|
-
#
|
75
|
-
# @param filter [Array<String>] agent names
|
76
|
-
# @return [Array<String>] list of nodes found
|
77
|
-
def discover_agents(filter)
|
78
|
-
pql = filter.map do |agent|
|
79
|
-
if ["rpcutil", "scout"].include?(agent)
|
80
|
-
"(%s or %s)" % [discover_classes(["mcollective::service"]), discover_classes(["choria::service"])]
|
81
|
-
elsif agent =~ /^\/(.+)\/$/
|
82
|
-
'resources {type = "File" and tag ~ "mcollective_agent_.*?%s.*?_server"}' % [string_regexi($1)]
|
83
|
-
else
|
84
|
-
'resources {type = "File" and tag = "mcollective_agent_%s_server"}' % [agent]
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
pql.join(" and ") unless pql.empty?
|
89
|
-
end
|
90
|
-
|
91
|
-
# Turns a string into a case insensitive regex string
|
92
|
-
#
|
93
|
-
# @param value [String]
|
94
|
-
# @return [String]
|
95
|
-
def string_regexi(value)
|
96
|
-
value =~ /^\/(.+)\/$/ ? derived_value = $1 : derived_value = value.dup
|
97
|
-
|
98
|
-
derived_value.each_char.map do |char|
|
99
|
-
if char =~ /[[:alpha:]]/
|
100
|
-
"[%s%s]" % [char.downcase, char.upcase]
|
101
|
-
else
|
102
|
-
char
|
103
|
-
end
|
104
|
-
end.join
|
105
|
-
end
|
106
|
-
|
107
|
-
# Capitalize a Puppet Resource
|
108
|
-
#
|
109
|
-
# foo::bar => Foo::Bar
|
110
|
-
#
|
111
|
-
# @param resource [String] a resource title
|
112
|
-
# @return [String]
|
113
|
-
def capitalize_resource(resource)
|
114
|
-
resource.split("::").map(&:capitalize).join("::")
|
115
|
-
end
|
116
|
-
|
117
|
-
# Searches for facts
|
118
|
-
#
|
119
|
-
# Nodes are searched using an `and` operator
|
120
|
-
#
|
121
|
-
# @param filter [Array<Hash>] hashes with :fact, :operator and :value
|
122
|
-
# @return [Array<String>] list of nodes found
|
123
|
-
def discover_facts(filter)
|
124
|
-
pql = filter.map do |q|
|
125
|
-
fact = q[:fact]
|
126
|
-
operator = q[:operator]
|
127
|
-
value = q[:value]
|
128
|
-
|
129
|
-
case operator
|
130
|
-
when "=~"
|
131
|
-
regex = string_regexi(value)
|
132
|
-
|
133
|
-
'inventory {facts.%s ~ "%s"}' % [fact, regex]
|
134
|
-
when "=="
|
135
|
-
if ["true", "false"].include?(value) || numeric?(value)
|
136
|
-
'inventory {facts.%s = %s or facts.%s = "%s"}' % [fact, value, fact, value]
|
137
|
-
else
|
138
|
-
'inventory {facts.%s = "%s"}' % [fact, value]
|
139
|
-
end
|
140
|
-
when "!="
|
141
|
-
if ["true", "false"].include?(value) || numeric?(value)
|
142
|
-
'inventory {!(facts.%s = %s or facts.%s = "%s")}' % [fact, value, fact, value]
|
143
|
-
else
|
144
|
-
'inventory {!(facts.%s = "%s")}' % [fact, value]
|
145
|
-
end
|
146
|
-
when ">=", ">", "<=", "<"
|
147
|
-
raise("Do not know how to do string fact comparisons using the '%s' operator with PuppetDB" % operator) unless numeric?(value)
|
148
|
-
|
149
|
-
"inventory {facts.%s %s %s}" % [fact, operator, value]
|
150
|
-
else
|
151
|
-
raise("Do not know how to do fact comparisons using the '%s' operator with PuppetDB" % operator)
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
pql.join(" and ") unless pql.empty?
|
156
|
-
end
|
157
|
-
|
158
|
-
# Searches for classes
|
159
|
-
#
|
160
|
-
# Nodes are searched using an `and` operator
|
161
|
-
#
|
162
|
-
# @return [Array<String>] list of nodes found
|
163
|
-
def discover_classes(filter)
|
164
|
-
pql = filter.map do |klass|
|
165
|
-
if klass =~ /^\/(.+)\/$/
|
166
|
-
'resources {type = "Class" and title ~ "%s"}' % [string_regexi($1)]
|
167
|
-
else
|
168
|
-
'resources {type = "Class" and title = "%s"}' % [capitalize_resource(klass)]
|
169
|
-
end
|
170
|
-
end
|
171
|
-
|
172
|
-
pql.join(" and ") unless pql.empty?
|
173
|
-
end
|
174
|
-
|
175
|
-
# Searches for nodes
|
176
|
-
#
|
177
|
-
# Nodes are searched using an `or` operator
|
178
|
-
#
|
179
|
-
# @return [Array<String>] list of nodes found
|
180
|
-
def discover_nodes(filter)
|
181
|
-
if filter.empty?
|
182
|
-
Log.debug("Empty node filter found, discovering all nodes")
|
183
|
-
nil
|
184
|
-
else
|
185
|
-
pql = filter.map do |ident|
|
186
|
-
case ident
|
187
|
-
when /^pql:\s*(.+)$/
|
188
|
-
"certname in %s" % $1
|
189
|
-
when /^\/(.+)\/$/
|
190
|
-
'certname ~ "%s"' % string_regexi($1)
|
191
|
-
else
|
192
|
-
'certname = "%s"' % ident
|
193
|
-
end
|
194
|
-
end
|
195
|
-
|
196
|
-
pql.join(" or ") unless pql.empty?
|
197
|
-
end
|
198
|
-
end
|
199
|
-
|
200
|
-
# Produce a nodes query with the supplied sub query included
|
201
|
-
#
|
202
|
-
# @param queries [Array<String>] PQL queries to be used as a sub query
|
203
|
-
# @return [String] nodes search string
|
204
|
-
def node_search_string(queries)
|
205
|
-
filter_queries = queries.map {|q| "(%s)" % q}.join(" and ")
|
206
|
-
|
207
|
-
"nodes[certname, deactivated] { %s }" % [filter_queries]
|
208
|
-
end
|
209
|
-
|
210
|
-
# Determines if a string is a number, either float or integer
|
211
|
-
#
|
212
|
-
# @param string [String]
|
213
|
-
# @return [boolean]
|
214
|
-
def numeric?(string)
|
215
|
-
true if Float(string) rescue false
|
216
|
-
end
|
217
|
-
|
218
|
-
def choria
|
219
|
-
@_choria ||= Util::Choria.new(false)
|
220
|
-
end
|
221
|
-
end
|
222
|
-
end
|
223
|
-
end
|
@@ -1,47 +0,0 @@
|
|
1
|
-
# discovers against a flatfile instead of the traditional network discovery
|
2
|
-
# the flat file must have a node name per line which should match identities
|
3
|
-
# as configured
|
4
|
-
module MCollective
|
5
|
-
class Discovery
|
6
|
-
class Flatfile
|
7
|
-
def self.discover(filter, timeout, limit=0, client=nil)
|
8
|
-
if client.options[:discovery_options].empty?
|
9
|
-
raise "The flatfile discovery method needs a path to a text file"
|
10
|
-
else
|
11
|
-
file = client.options[:discovery_options].first
|
12
|
-
end
|
13
|
-
|
14
|
-
raise "Cannot read the file %s specified as discovery source" % file unless File.readable?(file)
|
15
|
-
|
16
|
-
discovered = []
|
17
|
-
hosts = []
|
18
|
-
|
19
|
-
File.readlines(file).each do |host|
|
20
|
-
host = host.chomp.strip
|
21
|
-
next if host.empty? || host.match(/^#/)
|
22
|
-
raise 'Identities can only match /^[\w\.\-]+$/' unless host.match(/^[\w.\-]+$/)
|
23
|
-
|
24
|
-
hosts << host
|
25
|
-
end
|
26
|
-
|
27
|
-
# this plugin only supports identity filters, do regex matches etc against
|
28
|
-
# the list found in the flatfile
|
29
|
-
if !filter["identity"].empty?
|
30
|
-
filter["identity"].each do |identity|
|
31
|
-
identity = Regexp.new(identity.gsub("\/", "")) if identity.match("^/")
|
32
|
-
|
33
|
-
if identity.is_a?(Regexp)
|
34
|
-
discovered = hosts.grep(identity)
|
35
|
-
elsif hosts.include?(identity)
|
36
|
-
discovered << identity
|
37
|
-
end
|
38
|
-
end
|
39
|
-
else
|
40
|
-
discovered = hosts
|
41
|
-
end
|
42
|
-
|
43
|
-
discovered
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
@@ -1,11 +0,0 @@
|
|
1
|
-
metadata :name => "stdin",
|
2
|
-
:description => "STDIN based discovery for node identities",
|
3
|
-
:author => "Tomas Doran <bobtfish@bobtfish.net.net>",
|
4
|
-
:license => "ASL 2.0",
|
5
|
-
:version => "0.1",
|
6
|
-
:url => "https://docs.puppetlabs.com/mcollective/",
|
7
|
-
:timeout => 0
|
8
|
-
|
9
|
-
discovery do
|
10
|
-
capabilities :identity
|
11
|
-
end
|