choria-mcorpc-support 2.20.3 → 2.20.4
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 +4 -4
- data/lib/mcollective.rb +18 -19
- data/lib/mcollective/agent.rb +0 -1
- data/lib/mcollective/agents.rb +17 -18
- data/lib/mcollective/aggregate.rb +11 -11
- data/lib/mcollective/application.rb +30 -41
- data/lib/mcollective/applications.rb +13 -14
- data/lib/mcollective/cache.rb +1 -1
- data/lib/mcollective/client.rb +25 -25
- data/lib/mcollective/config.rb +114 -120
- data/lib/mcollective/data.rb +9 -9
- data/lib/mcollective/ddl.rb +1 -1
- data/lib/mcollective/discovery.rb +13 -13
- data/lib/mcollective/exceptions.rb +17 -17
- data/lib/mcollective/facts.rb +2 -2
- data/lib/mcollective/log.rb +7 -9
- data/lib/mcollective/matcher.rb +28 -32
- data/lib/mcollective/message.rb +31 -29
- data/lib/mcollective/monkey_patches.rb +92 -83
- data/lib/mcollective/optionparser.rb +23 -23
- data/lib/mcollective/pluginmanager.rb +8 -11
- data/lib/mcollective/pluginpackager.rb +13 -17
- data/lib/mcollective/rpc.rb +16 -18
- data/lib/mcollective/runnerstats.rb +10 -6
- data/lib/mcollective/shell.rb +30 -33
- data/lib/mcollective/ssl.rb +9 -12
- data/lib/mcollective/util.rb +160 -135
- data/lib/mcollective/validator.rb +18 -20
- metadata +4 -4
@@ -10,7 +10,7 @@ module MCollective
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def self.[](klass)
|
13
|
-
const_get(
|
13
|
+
const_get(klass.to_s)
|
14
14
|
end
|
15
15
|
|
16
16
|
# Fetch and return metadata from plugin DDL
|
@@ -19,12 +19,12 @@ module MCollective
|
|
19
19
|
|
20
20
|
begin
|
21
21
|
ddl_file = File.read(Dir.glob(File.join(path, type, "*.ddl")).first)
|
22
|
-
rescue Exception
|
22
|
+
rescue Exception # rubocop:disable Lint/RescueException
|
23
23
|
raise "failed to load ddl file in plugin directory : #{File.join(path, type)}"
|
24
24
|
end
|
25
25
|
ddl.instance_eval ddl_file
|
26
26
|
|
27
|
-
|
27
|
+
[ddl.meta, ddl.requirements[:mcollective]]
|
28
28
|
end
|
29
29
|
|
30
30
|
# Checks if a directory is present and not empty
|
@@ -34,19 +34,19 @@ module MCollective
|
|
34
34
|
|
35
35
|
# Quietly calls a block if verbose parameter is false
|
36
36
|
def self.execute_verbosely(verbose, &block)
|
37
|
-
|
37
|
+
if verbose
|
38
|
+
block.call
|
39
|
+
else
|
38
40
|
old_stdout = $stdout.clone
|
39
41
|
$stdout.reopen(File.new("/dev/null", "w"))
|
40
42
|
begin
|
41
43
|
block.call
|
42
|
-
rescue Exception
|
44
|
+
rescue Exception # rubocop:disable Lint/RescueException
|
43
45
|
$stdout.reopen old_stdout
|
44
|
-
raise
|
46
|
+
raise
|
45
47
|
ensure
|
46
48
|
$stdout.reopen old_stdout
|
47
49
|
end
|
48
|
-
else
|
49
|
-
block.call
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
@@ -54,15 +54,13 @@ module MCollective
|
|
54
54
|
def self.command_available?(build_tool)
|
55
55
|
ENV["PATH"].split(File::PATH_SEPARATOR).each do |path|
|
56
56
|
builder = File.join(path, build_tool)
|
57
|
-
if File.
|
58
|
-
return true
|
59
|
-
end
|
57
|
+
return true if File.exist?(builder)
|
60
58
|
end
|
61
59
|
false
|
62
60
|
end
|
63
61
|
|
64
62
|
def self.safe_system(*args)
|
65
|
-
raise(
|
63
|
+
raise("Failed: #{args.join(' ')}") unless system(*args)
|
66
64
|
end
|
67
65
|
|
68
66
|
# Filter out platform specific dependencies
|
@@ -77,22 +75,20 @@ module MCollective
|
|
77
75
|
if prefix == $1
|
78
76
|
dependency[:name] = $2
|
79
77
|
dependency
|
80
|
-
else
|
81
|
-
nil
|
82
78
|
end
|
83
79
|
else
|
84
80
|
dependency
|
85
81
|
end
|
86
|
-
end.reject
|
82
|
+
end.reject(&:nil?)
|
87
83
|
end
|
88
84
|
|
89
85
|
# Return the path to a plugin's core directories
|
90
86
|
def self.get_plugin_path(target)
|
91
|
-
if
|
87
|
+
if File.exist?(File.join(target, "lib", "mcollective"))
|
92
88
|
return File.join(target, "lib", "mcollective")
|
93
89
|
end
|
94
90
|
|
95
|
-
|
91
|
+
target
|
96
92
|
end
|
97
93
|
end
|
98
94
|
end
|
data/lib/mcollective/rpc.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "pp"
|
2
2
|
|
3
3
|
module MCollective
|
4
4
|
# Toolset to create a standard interface of client and agent using
|
@@ -21,15 +21,13 @@ module MCollective
|
|
21
21
|
def rpcoptions
|
22
22
|
oparser = MCollective::Optionparser.new({:verbose => false, :progress_bar => true}, "filter")
|
23
23
|
|
24
|
-
options = oparser.parse do |parser,
|
25
|
-
if block_given?
|
26
|
-
yield(parser, options)
|
27
|
-
end
|
24
|
+
options = oparser.parse do |parser, opts|
|
25
|
+
yield(parser, opts) if block_given?
|
28
26
|
|
29
|
-
Helpers.add_simplerpc_options(parser,
|
27
|
+
Helpers.add_simplerpc_options(parser, opts)
|
30
28
|
end
|
31
29
|
|
32
|
-
|
30
|
+
options
|
33
31
|
end
|
34
32
|
|
35
33
|
# Wrapper to create clients, supposed to be used as
|
@@ -57,7 +55,7 @@ module MCollective
|
|
57
55
|
# :exit_on_failure is true by default, and causes the application to
|
58
56
|
# exit if there is a failure constructing the RPC client. Set this flag
|
59
57
|
# to false to cause an Exception to be raised instead.
|
60
|
-
def rpcclient(agent, flags
|
58
|
+
def rpcclient(agent, flags={})
|
61
59
|
configfile = flags[:configfile] || Util.config_file_for_user
|
62
60
|
options = flags[:options] || nil
|
63
61
|
|
@@ -71,12 +69,12 @@ module MCollective
|
|
71
69
|
begin
|
72
70
|
if options
|
73
71
|
rpc = Client.new(agent, :configfile => options[:config], :options => options)
|
74
|
-
@options = rpc.options
|
75
72
|
else
|
76
73
|
rpc = Client.new(agent, :configfile => configfile)
|
77
|
-
@options = rpc.options
|
78
74
|
end
|
79
|
-
|
75
|
+
|
76
|
+
@options = rpc.options
|
77
|
+
rescue Exception => e # rubocop:disable Lint/RescueException:
|
80
78
|
if exit_on_failure
|
81
79
|
puts("Could not create RPC client: #{e}")
|
82
80
|
exit!
|
@@ -97,7 +95,7 @@ module MCollective
|
|
97
95
|
# printrpcstats can easily get access to it without
|
98
96
|
# users having to pass it around in params.
|
99
97
|
def self.stats(stats)
|
100
|
-
@@stats = stats
|
98
|
+
@@stats = stats # rubocop:disable Style/ClassVars
|
101
99
|
end
|
102
100
|
|
103
101
|
# means for other classes to drop discovered hosts into this module
|
@@ -105,7 +103,7 @@ module MCollective
|
|
105
103
|
# printrpcstats can easily get access to it without
|
106
104
|
# users having to pass it around in params.
|
107
105
|
def self.discovered(discovered)
|
108
|
-
@@discovered = discovered
|
106
|
+
@@discovered = discovered # rubocop:disable Style/ClassVars
|
109
107
|
end
|
110
108
|
|
111
109
|
# Prints stats, requires stats to be saved from elsewhere
|
@@ -125,7 +123,7 @@ module MCollective
|
|
125
123
|
|
126
124
|
flags = {:summarize => false, :caption => "rpc stats"}.merge(flags)
|
127
125
|
|
128
|
-
verbose =
|
126
|
+
verbose = !!@options[:verbose]
|
129
127
|
|
130
128
|
begin
|
131
129
|
stats = @@stats
|
@@ -143,17 +141,17 @@ module MCollective
|
|
143
141
|
# that produce an error will be printed
|
144
142
|
#
|
145
143
|
# To get details of each result run with the -v command line option.
|
146
|
-
def printrpc(result, flags
|
147
|
-
verbose =
|
144
|
+
def printrpc(result, flags={})
|
145
|
+
verbose = !!@options[:verbose]
|
148
146
|
verbose = flags[:verbose] || verbose
|
149
147
|
flatten = flags[:flatten] || false
|
150
148
|
format = @options[:output_format]
|
151
149
|
forced_mode = @options[:force_display_mode] || false
|
152
150
|
|
153
|
-
result_text =
|
151
|
+
result_text = Helpers.rpcresults(result, :verbose => verbose, :flatten => flatten, :format => format, :force_display_mode => forced_mode)
|
154
152
|
|
155
153
|
if result.is_a?(Array) && format == :console
|
156
|
-
puts "\n%s\n" % [
|
154
|
+
puts "\n%s\n" % [result_text]
|
157
155
|
else
|
158
156
|
# when we get just one result to print dont pad them all with
|
159
157
|
# blank spaces etc, just print the individual result with no
|
@@ -60,27 +60,31 @@ module MCollective
|
|
60
60
|
|
61
61
|
# Returns a hash with all stats
|
62
62
|
def to_hash
|
63
|
-
stats = {
|
63
|
+
stats = {
|
64
|
+
:validated => @validated,
|
64
65
|
:unvalidated => @unvalidated,
|
65
66
|
:passed => @passed,
|
66
67
|
:filtered => @filtered,
|
67
68
|
:starttime => @starttime,
|
68
69
|
:total => @total,
|
69
70
|
:ttlexpired => @ttlexpired,
|
70
|
-
:replies => @replies
|
71
|
+
:replies => @replies
|
72
|
+
}
|
71
73
|
|
72
|
-
reply = {
|
74
|
+
reply = {
|
75
|
+
:stats => stats,
|
73
76
|
:threads => [],
|
74
77
|
:pid => Process.pid,
|
75
|
-
:times => {}
|
78
|
+
:times => {}
|
79
|
+
}
|
76
80
|
|
77
|
-
::Process.times.each_pair{|k,v|
|
81
|
+
::Process.times.each_pair {|k, v|
|
78
82
|
k = k.to_sym
|
79
83
|
reply[:times][k] = v
|
80
84
|
}
|
81
85
|
|
82
86
|
Thread.list.each do |t|
|
83
|
-
reply[:threads] <<
|
87
|
+
reply[:threads] << t.inspect.to_s
|
84
88
|
end
|
85
89
|
|
86
90
|
reply[:agents] = Agents.agentlist
|
data/lib/mcollective/shell.rb
CHANGED
@@ -36,33 +36,33 @@ module MCollective
|
|
36
36
|
|
37
37
|
options.each do |opt, val|
|
38
38
|
case opt.to_s
|
39
|
-
|
40
|
-
|
41
|
-
|
39
|
+
when "stdout"
|
40
|
+
raise "stdout should support <<" unless val.respond_to?("<<")
|
41
|
+
@stdout = val
|
42
42
|
|
43
|
-
|
44
|
-
|
45
|
-
|
43
|
+
when "stderr"
|
44
|
+
raise "stderr should support <<" unless val.respond_to?("<<")
|
45
|
+
@stderr = val
|
46
46
|
|
47
|
-
|
48
|
-
|
49
|
-
|
47
|
+
when "stdin"
|
48
|
+
raise "stdin should be a String" unless val.is_a?(String)
|
49
|
+
@stdin = val
|
50
50
|
|
51
|
-
|
52
|
-
|
53
|
-
|
51
|
+
when "cwd"
|
52
|
+
raise "Directory #{val} does not exist" unless File.directory?(val)
|
53
|
+
@cwd = val
|
54
54
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
55
|
+
when "environment"
|
56
|
+
if val.nil?
|
57
|
+
@environment = {}
|
58
|
+
else
|
59
|
+
@environment.merge!(val.dup)
|
60
|
+
@environment = @environment.delete_if { |_k, v| v.nil? }
|
61
|
+
end
|
62
62
|
|
63
|
-
|
64
|
-
|
65
|
-
|
63
|
+
when "timeout"
|
64
|
+
raise "timeout should be a positive integer or the symbol :on_thread_exit symbol" unless val.eql?(:on_thread_exit) || (val.is_a?(Integer) && val > 0)
|
65
|
+
@timeout = val
|
66
66
|
end
|
67
67
|
end
|
68
68
|
end
|
@@ -76,7 +76,6 @@ module MCollective
|
|
76
76
|
|
77
77
|
opts["stdin"] = @stdin if @stdin
|
78
78
|
|
79
|
-
|
80
79
|
thread = Thread.current
|
81
80
|
# Start a double fork and exec with systemu which implies a guard thread.
|
82
81
|
# If a valid timeout is configured the guard thread will terminate the
|
@@ -90,32 +89,30 @@ module MCollective
|
|
90
89
|
sleep timeout
|
91
90
|
else
|
92
91
|
# sleep while the agent thread is still alive
|
93
|
-
while
|
94
|
-
sleep 0.1
|
95
|
-
end
|
92
|
+
sleep 0.1 while thread.alive?
|
96
93
|
end
|
97
94
|
|
98
95
|
# if the process is still running
|
99
|
-
if
|
96
|
+
if Process.kill(0, cid)
|
100
97
|
# and a timeout was specified
|
101
98
|
if timeout
|
102
99
|
if Util.windows?
|
103
|
-
Process.kill(
|
100
|
+
Process.kill("KILL", cid)
|
104
101
|
else
|
105
102
|
# Kill the process
|
106
|
-
Process.kill(
|
103
|
+
Process.kill("TERM", cid)
|
107
104
|
sleep 2
|
108
|
-
Process.kill(
|
105
|
+
Process.kill("KILL", cid) if Process.kill(0, cid) # rubocop:disable Metrics/BlockNesting
|
109
106
|
end
|
110
107
|
end
|
111
108
|
# only wait if the parent thread is dead
|
112
109
|
Process.waitpid(cid) unless thread.alive?
|
113
110
|
end
|
114
|
-
rescue SystemExit
|
115
|
-
rescue Errno::ESRCH
|
111
|
+
rescue SystemExit # rubocop:disable Lint/HandleExceptions
|
112
|
+
rescue Errno::ESRCH # rubocop:disable Lint/HandleExceptions
|
116
113
|
rescue Errno::ECHILD
|
117
114
|
Log.warn("Could not reap process '#{cid}'.")
|
118
|
-
rescue Exception => e
|
115
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
119
116
|
Log.info("Unexpected exception received while waiting for child process: #{e.class}: #{e}")
|
120
117
|
end
|
121
118
|
end
|
data/lib/mcollective/ssl.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
1
|
+
require "openssl"
|
2
|
+
require "base64"
|
3
|
+
require "digest/sha1"
|
4
4
|
|
5
5
|
module MCollective
|
6
6
|
# A class that assists in encrypting and decrypting data using a
|
@@ -161,7 +161,7 @@ module MCollective
|
|
161
161
|
cipher.decrypt
|
162
162
|
cipher.key = key
|
163
163
|
cipher.pkcs5_keyivgen(key)
|
164
|
-
|
164
|
+
cipher.update(crypt_string) + cipher.final
|
165
165
|
end
|
166
166
|
|
167
167
|
# Signs a string using the private key
|
@@ -195,9 +195,7 @@ module MCollective
|
|
195
195
|
def self.base64_decode(string)
|
196
196
|
# The Base 64 character set is A-Z a-z 0-9 + / =
|
197
197
|
# Also allow for whitespace, but raise if we get anything else
|
198
|
-
if string !~ /^[A-Za-z0-9+\/=\s]+$/
|
199
|
-
raise ArgumentError, 'invalid base64'
|
200
|
-
end
|
198
|
+
raise(ArgumentError, "invalid base64") if string !~ /^[A-Za-z0-9+\/=\s]+$/
|
201
199
|
Base64.decode64(string)
|
202
200
|
end
|
203
201
|
|
@@ -216,9 +214,9 @@ module MCollective
|
|
216
214
|
# https://github.com/kwilczynski/puppet-functions/blob/master/lib/puppet/parser/functions/uuid.rb
|
217
215
|
#
|
218
216
|
def self.uuid(string=nil)
|
219
|
-
string ||= OpenSSL::Random.random_bytes(16).unpack(
|
217
|
+
string ||= OpenSSL::Random.random_bytes(16).unpack("H*").shift
|
220
218
|
|
221
|
-
uuid_name_space_dns = [0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8].map
|
219
|
+
uuid_name_space_dns = [0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8].map(&:chr).join
|
222
220
|
|
223
221
|
sha1 = Digest::SHA1.new
|
224
222
|
sha1.update(uuid_name_space_dns)
|
@@ -236,10 +234,10 @@ module MCollective
|
|
236
234
|
bytes[8] |= 0x80
|
237
235
|
|
238
236
|
bytes = [4, 2, 2, 2, 6].collect do |i|
|
239
|
-
bytes.slice!(0, i).pack(
|
237
|
+
bytes.slice!(0, i).pack("C*").unpack("H*")
|
240
238
|
end
|
241
239
|
|
242
|
-
bytes.join(
|
240
|
+
bytes.join("-")
|
243
241
|
end
|
244
242
|
|
245
243
|
# Reads either a :public or :private key from disk, uses an
|
@@ -280,6 +278,5 @@ module MCollective
|
|
280
278
|
raise "Can only load :public or :private keys"
|
281
279
|
end
|
282
280
|
end
|
283
|
-
|
284
281
|
end
|
285
282
|
end
|
data/lib/mcollective/util.rb
CHANGED
@@ -6,10 +6,10 @@ module MCollective
|
|
6
6
|
# If the passed name starts with a / it's assumed to be regex
|
7
7
|
# and will use regex to match
|
8
8
|
def self.has_agent?(agent)
|
9
|
-
agent = Regexp.new(agent.gsub("\/", "")) if agent.
|
9
|
+
agent = Regexp.new(agent.gsub("\/", "")) if agent.start_with?("/")
|
10
10
|
|
11
11
|
if agent.is_a?(Regexp)
|
12
|
-
if Agents.agentlist.grep(agent).
|
12
|
+
if !Agents.agentlist.grep(agent).empty?
|
13
13
|
return true
|
14
14
|
else
|
15
15
|
return false
|
@@ -17,8 +17,6 @@ module MCollective
|
|
17
17
|
else
|
18
18
|
return Agents.agentlist.include?(agent)
|
19
19
|
end
|
20
|
-
|
21
|
-
false
|
22
20
|
end
|
23
21
|
|
24
22
|
# On windows ^c can't interrupt the VM if its blocking on
|
@@ -36,7 +34,7 @@ module MCollective
|
|
36
34
|
# If the passed name starts with a / it's assumed to be regex
|
37
35
|
# and will use regex to match
|
38
36
|
def self.has_cf_class?(klass)
|
39
|
-
klass = Regexp.new(klass.gsub("\/", "")) if klass.
|
37
|
+
klass = Regexp.new(klass.gsub("\/", "")) if klass.start_with?("/")
|
40
38
|
cfile = Config.instance.classesfile
|
41
39
|
|
42
40
|
Log.debug("Looking for configuration management classes in #{cfile}")
|
@@ -45,11 +43,11 @@ module MCollective
|
|
45
43
|
File.readlines(cfile).each do |k|
|
46
44
|
if klass.is_a?(Regexp)
|
47
45
|
return true if k.chomp.match(klass)
|
48
|
-
|
49
|
-
return true
|
46
|
+
elsif k.chomp == klass
|
47
|
+
return true
|
50
48
|
end
|
51
49
|
end
|
52
|
-
rescue Exception => e
|
50
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
53
51
|
Log.warn("Parsing classes file '#{cfile}' failed: #{e.class}: #{e}")
|
54
52
|
end
|
55
53
|
|
@@ -67,7 +65,6 @@ module MCollective
|
|
67
65
|
# If the passed value starts with a / it's assumed to be regex
|
68
66
|
# and will use regex to match
|
69
67
|
def self.has_fact?(fact, value, operator)
|
70
|
-
|
71
68
|
Log.debug("Comparing #{fact} #{operator} #{value}")
|
72
69
|
Log.debug("where :fact = '#{fact}', :operator = '#{operator}', :value = '#{value}'")
|
73
70
|
|
@@ -86,30 +83,28 @@ module MCollective
|
|
86
83
|
end
|
87
84
|
|
88
85
|
def self.test_fact_value(fact, value, operator)
|
89
|
-
if operator ==
|
86
|
+
if operator == "=~"
|
90
87
|
# to maintain backward compat we send the value
|
91
88
|
# as /.../ which is what 1.0.x needed. this strips
|
92
89
|
# off the /'s which is what we need here
|
93
|
-
if value =~ /^\/(.+)\/$/
|
94
|
-
value = $1
|
95
|
-
end
|
90
|
+
value = $1 if value =~ /^\/(.+)\/$/
|
96
91
|
|
97
92
|
return true if fact.match(Regexp.new(value))
|
98
93
|
|
99
94
|
elsif operator == "=="
|
100
95
|
return true if fact == value
|
101
96
|
|
102
|
-
elsif [
|
97
|
+
elsif ["<=", ">=", "<", ">", "!="].include?(operator)
|
103
98
|
# Yuk - need to type cast, but to_i and to_f are overzealous
|
104
99
|
if value =~ /^[0-9]+$/ && fact =~ /^[0-9]+$/
|
105
|
-
fact = Integer(fact)
|
106
|
-
value = Integer(value)
|
100
|
+
fact = Integer(fact) # rubocop:disable Lint/UselessAssignment
|
101
|
+
value = Integer(value) # rubocop:disable Lint/UselessAssignment
|
107
102
|
elsif value =~ /^[0-9]+.[0-9]+$/ && fact =~ /^[0-9]+.[0-9]+$/
|
108
|
-
fact = Float(fact)
|
109
|
-
value = Float(value)
|
103
|
+
fact = Float(fact) # rubocop:disable Lint/UselessAssignment
|
104
|
+
value = Float(value) # rubocop:disable Lint/UselessAssignment
|
110
105
|
end
|
111
106
|
|
112
|
-
return true if eval("fact #{operator} value")
|
107
|
+
return true if eval("fact #{operator} value") # rubocop:disable Security/Eval
|
113
108
|
end
|
114
109
|
|
115
110
|
false
|
@@ -121,12 +116,12 @@ module MCollective
|
|
121
116
|
# If the passed name starts with a / it's assumed to be regex
|
122
117
|
# and will use regex to match
|
123
118
|
def self.has_identity?(identity)
|
124
|
-
identity = Regexp.new(identity.gsub("\/", "")) if identity.
|
119
|
+
identity = Regexp.new(identity.gsub("\/", "")) if identity.start_with?("/")
|
125
120
|
|
126
121
|
if identity.is_a?(Regexp)
|
127
122
|
return Config.instance.identity.match(identity)
|
128
|
-
|
129
|
-
return true
|
123
|
+
elsif Config.instance.identity == identity
|
124
|
+
return true
|
130
125
|
end
|
131
126
|
|
132
127
|
false
|
@@ -139,56 +134,94 @@ module MCollective
|
|
139
134
|
|
140
135
|
# Creates an empty filter
|
141
136
|
def self.empty_filter
|
142
|
-
{
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
137
|
+
{
|
138
|
+
"fact" => [],
|
139
|
+
"cf_class" => [],
|
140
|
+
"agent" => [],
|
141
|
+
"identity" => [],
|
142
|
+
"compound" => []
|
143
|
+
}
|
147
144
|
end
|
148
145
|
|
149
146
|
# Returns the PuppetLabs mcollective path for windows
|
150
147
|
def self.windows_prefix
|
151
|
-
require
|
152
|
-
|
148
|
+
require "win32/dir"
|
149
|
+
File.join(Dir::COMMON_APPDATA, "PuppetLabs", "mcollective")
|
153
150
|
end
|
154
151
|
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
152
|
+
def self.choria_windows_prefix
|
153
|
+
require "win32/dir"
|
154
|
+
File.join(Dir::COMMON_APPDATA, "ChoriaIO", "choria")
|
155
|
+
end
|
156
|
+
|
157
|
+
def self.mcollective_config_paths_for_user
|
159
158
|
config_paths = []
|
160
159
|
|
161
|
-
# user dotfile
|
162
160
|
begin
|
163
161
|
# File.expand_path will raise if HOME isn't set, catch it
|
164
162
|
user_path = File.expand_path("~/.mcollective")
|
165
163
|
config_paths << user_path
|
166
|
-
rescue Exception
|
164
|
+
rescue Exception # rubocop:disable Lint/RescueException, Lint/HandleExceptions
|
167
165
|
end
|
168
166
|
|
169
|
-
|
170
|
-
|
171
|
-
config_paths << File.join(self.windows_prefix, 'etc', 'client.cfg')
|
167
|
+
if windows?
|
168
|
+
config_paths << File.join(windows_prefix, "etc", "client.cfg")
|
172
169
|
else
|
173
|
-
config_paths <<
|
174
|
-
config_paths <<
|
170
|
+
config_paths << "/etc/puppetlabs/mcollective/client.cfg"
|
171
|
+
config_paths << "/etc/mcollective/client.cfg"
|
175
172
|
end
|
176
173
|
|
177
|
-
|
174
|
+
config_paths
|
175
|
+
end
|
176
|
+
|
177
|
+
def self.choria_config_paths_for_user
|
178
|
+
config_paths = []
|
179
|
+
|
180
|
+
begin
|
181
|
+
# File.expand_path will raise if HOME isn't set, catch it
|
182
|
+
user_path = File.expand_path("~/.choriarc")
|
183
|
+
config_paths << user_path
|
184
|
+
rescue Exception # rubocop:disable Lint/RescueException, Lint/HandleExceptions
|
185
|
+
end
|
186
|
+
|
187
|
+
if windows?
|
188
|
+
config_paths << File.join(choria_windows_prefix, "etc", "client.conf")
|
189
|
+
else
|
190
|
+
config_paths << "/etc/choria/client.conf"
|
191
|
+
end
|
192
|
+
|
193
|
+
config_paths
|
194
|
+
end
|
195
|
+
|
196
|
+
# Picks the default user config file, pririties are first Choria ones then old MCollective ones
|
197
|
+
#
|
198
|
+
# In roughly this order, first to exist is used:
|
199
|
+
#
|
200
|
+
# - ~/.choriarc
|
201
|
+
# - APPData/ChoriaIO/choria/etc/client.conf on windows
|
202
|
+
# - /etc/choria/client.conf on unix
|
203
|
+
# - ~/.mcollective
|
204
|
+
# - APPData/PuppetLabs/mcollective/etc/client.cfg on windows
|
205
|
+
# - /etc/puppetlabs/mcollective/client.cfg
|
206
|
+
# - /etc/mcollective/client.cfg
|
207
|
+
def self.config_file_for_user
|
208
|
+
config_paths = choria_config_paths_for_user + mcollective_config_paths_for_user
|
178
209
|
found = config_paths.find_index { |file| File.readable?(file) } || 0
|
179
|
-
|
210
|
+
config_paths[found]
|
180
211
|
end
|
181
212
|
|
182
213
|
# Creates a standard options hash
|
183
214
|
def self.default_options
|
184
|
-
{
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
215
|
+
{
|
216
|
+
:verbose => false,
|
217
|
+
:disctimeout => nil,
|
218
|
+
:timeout => 5,
|
219
|
+
:config => config_file_for_user,
|
220
|
+
:collective => nil,
|
221
|
+
:discovery_method => nil,
|
222
|
+
:discovery_options => Config.instance.default_discovery_options,
|
223
|
+
:filter => empty_filter
|
224
|
+
}
|
192
225
|
end
|
193
226
|
|
194
227
|
def self.make_subscriptions(agent, type, collective=nil)
|
@@ -237,15 +270,15 @@ module MCollective
|
|
237
270
|
# Parse a fact filter string like foo=bar into the tuple hash thats needed
|
238
271
|
def self.parse_fact_string(fact)
|
239
272
|
if fact =~ /^([^ ]+?)[ ]*=>[ ]*(.+)/
|
240
|
-
|
273
|
+
{:fact => $1, :value => $2, :operator => ">="}
|
241
274
|
elsif fact =~ /^([^ ]+?)[ ]*=<[ ]*(.+)/
|
242
|
-
|
275
|
+
{:fact => $1, :value => $2, :operator => "<="}
|
243
276
|
elsif fact =~ /^([^ ]+?)[ ]*(<=|>=|<|>|!=|==|=~)[ ]*(.+)/
|
244
|
-
|
277
|
+
{:fact => $1, :value => $3, :operator => $2}
|
245
278
|
elsif fact =~ /^(.+?)[ ]*=[ ]*\/(.+)\/$/
|
246
|
-
|
279
|
+
{:fact => $1, :value => "/#{$2}/", :operator => "=~"}
|
247
280
|
elsif fact =~ /^([^= ]+?)[ ]*=[ ]*(.+)/
|
248
|
-
|
281
|
+
{:fact => $1, :value => $2, :operator => "=="}
|
249
282
|
else
|
250
283
|
raise "Could not parse fact #{fact} it does not appear to be in a valid format"
|
251
284
|
end
|
@@ -267,11 +300,11 @@ module MCollective
|
|
267
300
|
# combo is regarded as line continuation and simply ignored.
|
268
301
|
str.gsub!(/\n/, "'\n'")
|
269
302
|
|
270
|
-
|
303
|
+
str
|
271
304
|
end
|
272
305
|
|
273
306
|
def self.windows?
|
274
|
-
!!(RbConfig::CONFIG[
|
307
|
+
!!(RbConfig::CONFIG["host_os"] =~ /mswin|win32|dos|mingw|cygwin/i)
|
275
308
|
end
|
276
309
|
|
277
310
|
# Return color codes, if the config color= option is false
|
@@ -279,12 +312,14 @@ module MCollective
|
|
279
312
|
def self.color(code)
|
280
313
|
colorize = Config.instance.color
|
281
314
|
|
282
|
-
colors = {
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
315
|
+
colors = {
|
316
|
+
:red => "[31m",
|
317
|
+
:green => "[32m",
|
318
|
+
:yellow => "[33m",
|
319
|
+
:cyan => "[36m",
|
320
|
+
:bold => "[1m",
|
321
|
+
:reset => "[0m"
|
322
|
+
}
|
288
323
|
|
289
324
|
if colorize
|
290
325
|
return colors[code] || ""
|
@@ -295,7 +330,7 @@ module MCollective
|
|
295
330
|
|
296
331
|
# Helper to return a string in specific color
|
297
332
|
def self.colorize(code, msg)
|
298
|
-
"%s%s%s" % [
|
333
|
+
"%s%s%s" % [color(code), msg, color(:reset)]
|
299
334
|
end
|
300
335
|
|
301
336
|
# Returns the current ruby version as per RUBY_VERSION, mostly
|
@@ -316,7 +351,7 @@ module MCollective
|
|
316
351
|
# The terminal size is detected by default, but custom line widths can
|
317
352
|
# passed. All strings will also be left aligned with 5 whitespace characters
|
318
353
|
# by default.
|
319
|
-
def self.align_text(text, console_cols
|
354
|
+
def self.align_text(text, console_cols=nil, preamble=5)
|
320
355
|
unless console_cols
|
321
356
|
console_cols = terminal_dimensions[0]
|
322
357
|
|
@@ -334,40 +369,36 @@ module MCollective
|
|
334
369
|
console_cols = 80 if console_cols <= 0
|
335
370
|
|
336
371
|
text = text.split("\n")
|
337
|
-
piece =
|
372
|
+
piece = ""
|
338
373
|
whitespace = 0
|
339
374
|
|
340
375
|
text.each_with_index do |line, i|
|
341
376
|
whitespace = 0
|
342
377
|
|
343
|
-
while whitespace < line.length && line[whitespace].chr ==
|
378
|
+
while whitespace < line.length && line[whitespace].chr == " "
|
344
379
|
whitespace += 1
|
345
380
|
end
|
346
381
|
|
347
382
|
# If the current line is empty, indent it so that a snippet
|
348
383
|
# from the previous line is aligned correctly.
|
349
|
-
if line == ""
|
350
|
-
line = (" " * whitespace)
|
351
|
-
end
|
384
|
+
line = (" " * whitespace) if line == ""
|
352
385
|
|
353
386
|
# If text was snipped from the previous line, prepend it to the
|
354
387
|
# current line after any current indentation.
|
355
|
-
if piece !=
|
388
|
+
if piece != ""
|
356
389
|
# Reset whitespaces to 0 if there are more whitespaces than there are
|
357
390
|
# console columns
|
358
391
|
whitespace = 0 if whitespace >= console_cols
|
359
392
|
|
360
393
|
# If the current line is empty and being prepended to, create a new
|
361
394
|
# empty line in the text so that formatting is preserved.
|
362
|
-
if text[i + 1] && line == (" " * whitespace)
|
363
|
-
text.insert(i + 1, "")
|
364
|
-
end
|
395
|
+
text.insert(i + 1, "") if text[i + 1] && line == (" " * whitespace)
|
365
396
|
|
366
397
|
# Add the snipped text to the current line
|
367
398
|
line.insert(whitespace, "#{piece} ")
|
368
399
|
end
|
369
400
|
|
370
|
-
piece =
|
401
|
+
piece = ""
|
371
402
|
|
372
403
|
# Compare the line length to the allowed line length.
|
373
404
|
# If it exceeds it, snip the offending text from the line
|
@@ -375,9 +406,7 @@ module MCollective
|
|
375
406
|
if line.length > (console_cols + preamble)
|
376
407
|
reverse = console_cols
|
377
408
|
|
378
|
-
while line[reverse].chr !=
|
379
|
-
reverse -= 1
|
380
|
-
end
|
409
|
+
reverse -= 1 while line[reverse].chr != " "
|
381
410
|
|
382
411
|
piece = line.slice!(reverse, (line.length - 1)).lstrip
|
383
412
|
end
|
@@ -385,13 +414,13 @@ module MCollective
|
|
385
414
|
# If a snippet exists when all the columns in the text have been
|
386
415
|
# updated, create a new line and append the snippet to it, using
|
387
416
|
# the same left alignment as the last line in the text.
|
388
|
-
if piece !=
|
389
|
-
text[i+1] = "#{' ' *
|
390
|
-
piece =
|
417
|
+
if piece != "" && text[i + 1].nil?
|
418
|
+
text[i + 1] = "#{' ' * whitespace}#{piece}"
|
419
|
+
piece = ""
|
391
420
|
end
|
392
421
|
|
393
422
|
# Add the preamble to the line and add it to the text
|
394
|
-
line = ((
|
423
|
+
line = ((" " * preamble) + line)
|
395
424
|
text[i] = line
|
396
425
|
end
|
397
426
|
|
@@ -402,7 +431,7 @@ module MCollective
|
|
402
431
|
#
|
403
432
|
# Returns [0, 0] if it can't figure it out or if you're
|
404
433
|
# not running on a tty
|
405
|
-
def self.terminal_dimensions(stdout
|
434
|
+
def self.terminal_dimensions(stdout=STDOUT, environment=ENV)
|
406
435
|
return [0, 0] unless stdout.tty?
|
407
436
|
|
408
437
|
return [80, 40] if Util.windows?
|
@@ -413,8 +442,8 @@ module MCollective
|
|
413
442
|
elsif environment["TERM"] && command_in_path?("tput")
|
414
443
|
return [`tput cols`.to_i, `tput lines`.to_i]
|
415
444
|
|
416
|
-
elsif command_in_path?(
|
417
|
-
return `stty size`.scan(/\d+/).map
|
445
|
+
elsif command_in_path?("stty")
|
446
|
+
return `stty size`.scan(/\d+/).map(&:to_i)
|
418
447
|
else
|
419
448
|
return [0, 0]
|
420
449
|
end
|
@@ -444,28 +473,26 @@ module MCollective
|
|
444
473
|
ax = version_a.scan(vre)
|
445
474
|
bx = version_b.scan(vre)
|
446
475
|
|
447
|
-
while
|
476
|
+
while !ax.empty? && !bx.empty?
|
448
477
|
a = ax.shift
|
449
478
|
b = bx.shift
|
450
479
|
|
451
|
-
if
|
452
|
-
elsif
|
453
|
-
elsif
|
454
|
-
elsif
|
455
|
-
elsif
|
456
|
-
elsif
|
457
|
-
elsif
|
458
|
-
elsif
|
459
|
-
if
|
460
|
-
return a.to_s.upcase <=> b.to_s.upcase
|
461
|
-
end
|
480
|
+
if a == b then next
|
481
|
+
elsif a == "-" && b == "-" then next
|
482
|
+
elsif a == "-" then return -1
|
483
|
+
elsif b == "-" then return 1
|
484
|
+
elsif a == "." && b == "." then next
|
485
|
+
elsif a == "." then return -1
|
486
|
+
elsif b == "." then return 1
|
487
|
+
elsif a =~ /^\d+$/ && b =~ /^\d+$/
|
488
|
+
return a.to_s.upcase <=> b.to_s.upcase if a =~ /^0/ || b =~ /^0/
|
462
489
|
return a.to_i <=> b.to_i
|
463
490
|
else
|
464
491
|
return a.upcase <=> b.upcase
|
465
492
|
end
|
466
493
|
end
|
467
494
|
|
468
|
-
version_a <=> version_b
|
495
|
+
version_a <=> version_b
|
469
496
|
end
|
470
497
|
|
471
498
|
# we should really use Pathname#absolute? but it's not in all the
|
@@ -486,7 +513,7 @@ module MCollective
|
|
486
513
|
def self.str_to_bool(val)
|
487
514
|
clean_val = val.to_s.strip
|
488
515
|
if clean_val =~ /^(1|yes|true|y|t)$/i
|
489
|
-
return
|
516
|
+
return true
|
490
517
|
elsif clean_val =~ /^(0|no|false|n|f)$/i
|
491
518
|
return false
|
492
519
|
else
|
@@ -498,10 +525,10 @@ module MCollective
|
|
498
525
|
def self.templatepath(template_file)
|
499
526
|
config_dir = File.dirname(Config.instance.configfile)
|
500
527
|
template_path = File.join(config_dir, template_file)
|
501
|
-
return template_path if File.
|
528
|
+
return template_path if File.exist?(template_path)
|
502
529
|
|
503
530
|
template_path = File.join("/etc/mcollective", template_file)
|
504
|
-
|
531
|
+
template_path
|
505
532
|
end
|
506
533
|
|
507
534
|
# subscribe to the direct addressing queue
|
@@ -511,68 +538,66 @@ module MCollective
|
|
511
538
|
|
512
539
|
# Get field size for printing
|
513
540
|
def self.field_size(elements, min_size=40)
|
514
|
-
max_length = elements.max_by
|
541
|
+
max_length = elements.max_by(&:length).length
|
515
542
|
max_length > min_size ? max_length : min_size
|
516
543
|
end
|
517
544
|
|
518
545
|
# Calculate number of fields for printing
|
519
546
|
def self.field_number(field_size, max_size=90)
|
520
|
-
number = (max_size/field_size).to_i
|
521
|
-
|
547
|
+
number = (max_size / field_size).to_i
|
548
|
+
number == 0 ? 1 : number
|
522
549
|
end
|
523
|
-
|
524
|
-
def self.get_hidden_input_on_windows
|
525
|
-
require
|
550
|
+
|
551
|
+
def self.get_hidden_input_on_windows # rubocop:disable Naming/AccessorMethodName
|
552
|
+
require "Win32API"
|
553
|
+
|
526
554
|
# Hook into getch from crtdll. Keep reading all keys till return
|
527
555
|
# or newline is hit.
|
528
556
|
# If key is backspace or delete, then delete the character and update
|
529
557
|
# the buffer.
|
530
|
-
input =
|
531
|
-
|
532
|
-
|
533
|
-
if
|
534
|
-
|
535
|
-
|
536
|
-
end
|
558
|
+
input = ""
|
559
|
+
|
560
|
+
while char = Win32API.new("crtdll", "_getch", [], "I").Call
|
561
|
+
break if [10, 13].include?(char) # return or newline
|
562
|
+
if [127, 8].include?(char) # backspace and delete
|
563
|
+
input.slice!(-1, 1) unless input.empty?
|
537
564
|
else
|
538
565
|
input << char.chr
|
539
566
|
end
|
540
567
|
end
|
541
|
-
|
568
|
+
|
542
569
|
input
|
543
570
|
end
|
544
571
|
|
545
|
-
def self.get_hidden_input_on_unix
|
572
|
+
def self.get_hidden_input_on_unix # rubocop:disable Naming/AccessorMethodName
|
546
573
|
unless $stdin.tty?
|
547
|
-
raise
|
574
|
+
raise "Could not hook to stdin to hide input. If using SSH, try using -t flag while connecting to server."
|
548
575
|
end
|
549
|
-
unless system
|
550
|
-
raise
|
576
|
+
unless system "stty -echo -icanon"
|
577
|
+
raise "Could not hide input using stty command."
|
551
578
|
end
|
552
579
|
input = $stdin.gets
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
580
|
+
ensure
|
581
|
+
unless system "stty echo icanon"
|
582
|
+
raise "Could not enable echoing of input. Try executing `stty echo icanon` to debug."
|
583
|
+
end
|
557
584
|
input
|
558
585
|
end
|
559
586
|
|
560
|
-
def self.get_hidden_input(message=
|
561
|
-
unless message.nil?
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
require 'io/console'
|
587
|
+
def self.get_hidden_input(message="Please enter data: ")
|
588
|
+
print message unless message.nil?
|
589
|
+
|
590
|
+
if versioncmp(ruby_version, "1.9.3") >= 0
|
591
|
+
require "io/console"
|
566
592
|
input = $stdin.noecho(&:gets)
|
593
|
+
elsif windows? # Use hacks to get hidden input on Ruby <1.9.3
|
594
|
+
input = get_hidden_input_on_windows
|
567
595
|
else
|
568
|
-
|
569
|
-
if self.windows?
|
570
|
-
input = self.get_hidden_input_on_windows()
|
571
|
-
else
|
572
|
-
input = self.get_hidden_input_on_unix()
|
573
|
-
end
|
596
|
+
input = get_hidden_input_on_unix
|
574
597
|
end
|
598
|
+
|
575
599
|
input.chomp! if input
|
600
|
+
|
576
601
|
input
|
577
602
|
end
|
578
603
|
end
|