mcollective-client 2.2.4 → 2.4.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of mcollective-client might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/lib/mcollective/application.rb +25 -34
- data/lib/mcollective/client.rb +91 -33
- data/lib/mcollective/config.rb +42 -43
- data/lib/mcollective/data/base.rb +1 -1
- data/lib/mcollective/data/result.rb +6 -2
- data/lib/mcollective/ddl/agentddl.rb +28 -1
- data/lib/mcollective/ddl/base.rb +8 -6
- data/lib/mcollective/log.rb +11 -3
- data/lib/mcollective/logger/file_logger.rb +4 -4
- data/lib/mcollective/matcher.rb +9 -1
- data/lib/mcollective/message.rb +14 -23
- data/lib/mcollective/optionparser.rb +9 -1
- data/lib/mcollective/pluginpackager.rb +24 -3
- data/lib/mcollective/pluginpackager/agent_definition.rb +12 -12
- data/lib/mcollective/pluginpackager/standard_definition.rb +12 -12
- data/lib/mcollective/rpc/agent.rb +15 -12
- data/lib/mcollective/rpc/client.rb +67 -31
- data/lib/mcollective/rpc/helpers.rb +7 -1
- data/lib/mcollective/rpc/reply.rb +3 -1
- data/lib/mcollective/shell.rb +45 -8
- data/lib/mcollective/util.rb +37 -1
- data/lib/mcollective/windows_daemon.rb +14 -3
- data/spec/spec_helper.rb +2 -0
- data/spec/unit/application_spec.rb +45 -26
- data/spec/unit/cache_spec.rb +3 -3
- data/spec/unit/client_spec.rb +269 -24
- data/spec/unit/config_spec.rb +89 -26
- data/spec/unit/data/base_spec.rb +1 -0
- data/spec/unit/data/result_spec.rb +19 -1
- data/spec/unit/data_spec.rb +3 -0
- data/spec/unit/ddl/agentddl_spec.rb +32 -0
- data/spec/unit/ddl/base_spec.rb +4 -0
- data/spec/unit/ddl/dataddl_spec.rb +1 -1
- data/spec/unit/log_spec.rb +44 -27
- data/spec/unit/logger/base_spec.rb +1 -1
- data/spec/unit/matcher_spec.rb +14 -0
- data/spec/unit/message_spec.rb +24 -0
- data/spec/unit/optionparser_spec.rb +99 -0
- data/spec/unit/pluginpackager/agent_definition_spec.rb +48 -17
- data/spec/unit/pluginpackager/standard_definition_spec.rb +44 -20
- data/spec/unit/pluginpackager_spec.rb +31 -7
- data/spec/unit/plugins/mcollective/agent/rpcutil_spec.rb +176 -0
- data/spec/unit/plugins/mcollective/application/plugin_spec.rb +81 -0
- data/spec/unit/plugins/mcollective/audit/logfile_spec.rb +44 -0
- data/spec/unit/plugins/mcollective/connector/activemq_spec.rb +118 -27
- data/spec/unit/plugins/mcollective/connector/rabbitmq_spec.rb +168 -34
- data/spec/unit/plugins/mcollective/data/agent_data_spec.rb +1 -0
- data/spec/unit/plugins/mcollective/data/fstat_data_spec.rb +1 -0
- data/spec/unit/plugins/mcollective/discovery/flatfile_spec.rb +10 -0
- data/spec/unit/plugins/mcollective/discovery/stdin_spec.rb +65 -0
- data/spec/unit/plugins/mcollective/facts/yaml_facts_spec.rb +65 -0
- data/spec/unit/plugins/mcollective/packagers/debpackage_packager_spec.rb +240 -219
- data/spec/unit/plugins/mcollective/packagers/modulepackage_packager_spec.rb +209 -0
- data/spec/unit/plugins/mcollective/packagers/rpmpackage_packager_spec.rb +223 -109
- data/spec/unit/rpc/actionrunner_spec.rb +2 -1
- data/spec/unit/rpc/agent_spec.rb +130 -1
- data/spec/unit/rpc/client_spec.rb +169 -3
- data/spec/unit/security/base_spec.rb +0 -1
- data/spec/unit/shell_spec.rb +76 -3
- data/spec/unit/util_spec.rb +69 -1
- data/spec/unit/windows_daemon_spec.rb +30 -9
- metadata +104 -90
- data/spec/unit/plugins/mcollective/connector/stomp/eventlogger_spec.rb +0 -34
- data/spec/unit/plugins/mcollective/connector/stomp_spec.rb +0 -424
- data/spec/unit/plugins/mcollective/validator/any_validator_spec.rb +0 -15
@@ -65,7 +65,7 @@ module MCollective
|
|
65
65
|
|
66
66
|
begin
|
67
67
|
# Incoming requests need to be validated against the DDL thus reusing
|
68
|
-
# all the work users put into creating DDLs and creating a
|
68
|
+
# all the work users put into creating DDLs and creating a consistent
|
69
69
|
# quality of input validation everywhere with the a simple once off
|
70
70
|
# investment of writing a DDL
|
71
71
|
@request.validate!
|
@@ -85,7 +85,7 @@ module MCollective
|
|
85
85
|
if respond_to?("#{@request.action}_action")
|
86
86
|
send("#{@request.action}_action")
|
87
87
|
else
|
88
|
-
raise UnknownRPCAction, "Unknown action
|
88
|
+
raise UnknownRPCAction, "Unknown action '#{@request.action}' for agent '#{@request.agent}'"
|
89
89
|
end
|
90
90
|
rescue RPCAborted => e
|
91
91
|
@reply.fail e.to_s, 1
|
@@ -96,13 +96,17 @@ module MCollective
|
|
96
96
|
rescue MissingRPCData => e
|
97
97
|
@reply.fail e.to_s, 3
|
98
98
|
|
99
|
-
rescue InvalidRPCData => e
|
99
|
+
rescue InvalidRPCData, DDLValidationError => e
|
100
100
|
@reply.fail e.to_s, 4
|
101
101
|
|
102
102
|
rescue UnknownRPCError => e
|
103
|
+
Log.error("%s#%s failed: %s: %s" % [@agent_name, @request.action, e.class, e.to_s])
|
104
|
+
Log.error(e.backtrace.join("\n\t"))
|
103
105
|
@reply.fail e.to_s, 5
|
104
106
|
|
105
107
|
rescue Exception => e
|
108
|
+
Log.error("%s#%s failed: %s: %s" % [@agent_name, @request.action, e.class, e.to_s])
|
109
|
+
Log.error(e.backtrace.join("\n\t"))
|
106
110
|
@reply.fail e.to_s, 5
|
107
111
|
|
108
112
|
end
|
@@ -137,16 +141,13 @@ module MCollective
|
|
137
141
|
|
138
142
|
Log.debug("Starting default activation checks for #{agent_name}")
|
139
143
|
|
140
|
-
should_activate = Config.instance.pluginconf
|
144
|
+
should_activate = Util.str_to_bool(Config.instance.pluginconf.fetch("#{agent_name}.activate_agent", true))
|
141
145
|
|
142
|
-
|
143
|
-
Log.debug("Found plugin
|
144
|
-
unless should_activate =~ /^1|y|true$/
|
145
|
-
return false
|
146
|
-
end
|
146
|
+
unless should_activate
|
147
|
+
Log.debug("Found plugin configuration '#{agent_name}.activate_agent' with value '#{should_activate}'")
|
147
148
|
end
|
148
149
|
|
149
|
-
return
|
150
|
+
return should_activate
|
150
151
|
end
|
151
152
|
|
152
153
|
# Returns an array of actions this agent support
|
@@ -211,7 +212,7 @@ module MCollective
|
|
211
212
|
end
|
212
213
|
end
|
213
214
|
|
214
|
-
[:stdin, :cwd, :environment].each do |k|
|
215
|
+
[:stdin, :cwd, :environment, :timeout].each do |k|
|
215
216
|
if options.include?(k)
|
216
217
|
shellopts[k] = options[k]
|
217
218
|
end
|
@@ -231,7 +232,9 @@ module MCollective
|
|
231
232
|
|
232
233
|
# Registers meta data for the introspection hash
|
233
234
|
def self.metadata(data)
|
234
|
-
|
235
|
+
agent = File.basename(caller.first).split(":").first
|
236
|
+
|
237
|
+
Log.warn("Setting metadata in agents has been deprecated, DDL files are now being used for this information. Please update the '#{agent}' agent")
|
235
238
|
end
|
236
239
|
|
237
240
|
# Creates the needed activate? class in a manner similar to the other
|
@@ -5,7 +5,7 @@ module MCollective
|
|
5
5
|
class Client
|
6
6
|
attr_accessor :timeout, :verbose, :filter, :config, :progress, :ttl, :reply_to
|
7
7
|
attr_reader :client, :stats, :ddl, :agent, :limit_targets, :limit_method, :output_format, :batch_size, :batch_sleep_time, :batch_mode
|
8
|
-
attr_reader :discovery_options, :discovery_method, :limit_seed
|
8
|
+
attr_reader :discovery_options, :discovery_method, :default_discovery_method, :limit_seed
|
9
9
|
|
10
10
|
@@initial_options = nil
|
11
11
|
|
@@ -57,7 +57,13 @@ module MCollective
|
|
57
57
|
@output_format = initial_options[:output_format] || :console
|
58
58
|
@force_direct_request = false
|
59
59
|
@reply_to = initial_options[:reply_to]
|
60
|
-
@discovery_method = initial_options[:discovery_method]
|
60
|
+
@discovery_method = initial_options[:discovery_method]
|
61
|
+
if !@discovery_method
|
62
|
+
@discovery_method = Config.instance.default_discovery_method
|
63
|
+
@default_discovery_method = true
|
64
|
+
else
|
65
|
+
@default_discovery_method = false
|
66
|
+
end
|
61
67
|
@discovery_options = initial_options[:discovery_options] || []
|
62
68
|
@force_display_mode = initial_options[:force_display_mode] || false
|
63
69
|
|
@@ -71,6 +77,8 @@ module MCollective
|
|
71
77
|
|
72
78
|
@collective = @client.collective
|
73
79
|
@ttl = initial_options[:ttl] || Config.instance.ttl
|
80
|
+
@publish_timeout = initial_options[:publish_timeout] || Config.instance.publish_timeout
|
81
|
+
@threaded = initial_options[:threaded] || Config.instance.threaded
|
74
82
|
|
75
83
|
# if we can find a DDL for the service override
|
76
84
|
# the timeout of the client so we always magically
|
@@ -149,6 +157,18 @@ module MCollective
|
|
149
157
|
:data => data}
|
150
158
|
end
|
151
159
|
|
160
|
+
# For the provided arguments and action the input arguments get
|
161
|
+
# modified by supplying any defaults provided in the DDL for arguments
|
162
|
+
# that were not supplied in the request
|
163
|
+
#
|
164
|
+
# We then pass the modified arguments to the DDL for validation
|
165
|
+
def validate_request(action, args)
|
166
|
+
raise "No DDL found for agent %s cannot validate inputs" % @agent unless @ddl
|
167
|
+
|
168
|
+
@ddl.set_default_input_arguments(action, args)
|
169
|
+
@ddl.validate_rpc_request(action, args)
|
170
|
+
end
|
171
|
+
|
152
172
|
# Magic handler to invoke remote methods
|
153
173
|
#
|
154
174
|
# Once the stub is created using the constructor or the RPC#rpcclient helper you can
|
@@ -217,7 +237,7 @@ module MCollective
|
|
217
237
|
|
218
238
|
@stats.reset
|
219
239
|
|
220
|
-
|
240
|
+
validate_request(action, args)
|
221
241
|
|
222
242
|
# if a global batch size is set just use that else set it
|
223
243
|
# in the case that it was passed as an argument
|
@@ -274,7 +294,7 @@ module MCollective
|
|
274
294
|
# request which technically does not need filters. If you try to use this
|
275
295
|
# mode with direct addressing disabled an exception will be raise
|
276
296
|
def custom_request(action, args, expected_agents, filter = {}, &block)
|
277
|
-
|
297
|
+
validate_request(action, args)
|
278
298
|
|
279
299
|
if filter == {} && !Config.instance.direct_addressing
|
280
300
|
raise "Attempted to do a filterless custom_request without direct_addressing enabled, preventing unexpected call to all nodes"
|
@@ -355,6 +375,7 @@ module MCollective
|
|
355
375
|
# since that is the user supplied timeout either via initial options
|
356
376
|
# or via specifically setting it on the client.
|
357
377
|
def discovery_method=(method)
|
378
|
+
@default_discovery_method = false
|
358
379
|
@discovery_method = method
|
359
380
|
|
360
381
|
if @initial_options[:discovery_options]
|
@@ -375,7 +396,7 @@ module MCollective
|
|
375
396
|
|
376
397
|
# Sets the class filter
|
377
398
|
def class_filter(klass)
|
378
|
-
@filter["cf_class"]
|
399
|
+
@filter["cf_class"] = @filter["cf_class"] | [klass]
|
379
400
|
@filter["cf_class"].compact!
|
380
401
|
reset
|
381
402
|
end
|
@@ -387,10 +408,10 @@ module MCollective
|
|
387
408
|
|
388
409
|
if value.nil?
|
389
410
|
parsed = Util.parse_fact_string(fact)
|
390
|
-
@filter["fact"]
|
411
|
+
@filter["fact"] = @filter["fact"] | [parsed] unless parsed == false
|
391
412
|
else
|
392
413
|
parsed = Util.parse_fact_string("#{fact}#{operator}#{value}")
|
393
|
-
@filter["fact"]
|
414
|
+
@filter["fact"] = @filter["fact"] | [parsed] unless parsed == false
|
394
415
|
end
|
395
416
|
|
396
417
|
@filter["fact"].compact!
|
@@ -399,21 +420,21 @@ module MCollective
|
|
399
420
|
|
400
421
|
# Sets the agent filter
|
401
422
|
def agent_filter(agent)
|
402
|
-
@filter["agent"]
|
423
|
+
@filter["agent"] = @filter["agent"] | [agent]
|
403
424
|
@filter["agent"].compact!
|
404
425
|
reset
|
405
426
|
end
|
406
427
|
|
407
428
|
# Sets the identity filter
|
408
429
|
def identity_filter(identity)
|
409
|
-
@filter["identity"]
|
430
|
+
@filter["identity"] = @filter["identity"] | [identity]
|
410
431
|
@filter["identity"].compact!
|
411
432
|
reset
|
412
433
|
end
|
413
434
|
|
414
435
|
# Set a compound filter
|
415
436
|
def compound_filter(filter)
|
416
|
-
@filter["compound"]
|
437
|
+
@filter["compound"] = @filter["compound"] | [Matcher.create_compound_callstack(filter)]
|
417
438
|
reset
|
418
439
|
end
|
419
440
|
|
@@ -479,24 +500,8 @@ module MCollective
|
|
479
500
|
@discovered_agents = hosts
|
480
501
|
@force_direct_request = true
|
481
502
|
|
482
|
-
|
483
|
-
|
484
|
-
# in direct addressing mode and we could empty it out but this use case should
|
485
|
-
# only really be for a few -I's on the CLI
|
486
|
-
#
|
487
|
-
# For safety we leave the filter in place for now, that way we can support this
|
488
|
-
# enhancement also in broadcast mode.
|
489
|
-
#
|
490
|
-
# This is only needed for the 'mc' discovery method, other methods might change
|
491
|
-
# the concept of identity to mean something else so we should pass the full
|
492
|
-
# identity filter to them
|
493
|
-
elsif options[:filter]["identity"].size > 0 && @discovery_method == "mc"
|
494
|
-
regex_filters = options[:filter]["identity"].select{|i| i.match("^\/")}.size
|
495
|
-
|
496
|
-
if regex_filters == 0
|
497
|
-
@discovered_agents = options[:filter]["identity"].clone
|
498
|
-
@force_direct_request = true if Config.instance.direct_addressing
|
499
|
-
end
|
503
|
+
else
|
504
|
+
identity_filter_discovery_optimization
|
500
505
|
end
|
501
506
|
end
|
502
507
|
|
@@ -559,7 +564,9 @@ module MCollective
|
|
559
564
|
:discovery_method => @discovery_method,
|
560
565
|
:discovery_options => @discovery_options,
|
561
566
|
:force_display_mode => @force_display_mode,
|
562
|
-
:config => @config
|
567
|
+
:config => @config,
|
568
|
+
:publish_timeout => @publish_timeout,
|
569
|
+
:threaded => @threaded}
|
563
570
|
end
|
564
571
|
|
565
572
|
# Sets the collective we are communicating with
|
@@ -701,7 +708,9 @@ module MCollective
|
|
701
708
|
#
|
702
709
|
# Should only be called via method_missing
|
703
710
|
def fire_and_forget_request(action, args, filter=nil)
|
704
|
-
|
711
|
+
validate_request(action, args)
|
712
|
+
|
713
|
+
identity_filter_discovery_optimization
|
705
714
|
|
706
715
|
req = new_request(action.to_s, args)
|
707
716
|
|
@@ -710,7 +719,34 @@ module MCollective
|
|
710
719
|
message = Message.new(req, nil, {:agent => @agent, :type => :request, :collective => @collective, :filter => filter, :options => options})
|
711
720
|
message.reply_to = @reply_to if @reply_to
|
712
721
|
|
713
|
-
|
722
|
+
if @force_direct_request || @client.discoverer.force_direct_mode?
|
723
|
+
message.discovered_hosts = discover.clone
|
724
|
+
message.type = :direct_request
|
725
|
+
end
|
726
|
+
|
727
|
+
client.sendreq(message, nil)
|
728
|
+
end
|
729
|
+
|
730
|
+
# if an identity filter is supplied and it is all strings no regex we can use that
|
731
|
+
# as discovery data, technically the identity filter is then redundant if we are
|
732
|
+
# in direct addressing mode and we could empty it out but this use case should
|
733
|
+
# only really be for a few -I's on the CLI
|
734
|
+
#
|
735
|
+
# For safety we leave the filter in place for now, that way we can support this
|
736
|
+
# enhancement also in broadcast mode.
|
737
|
+
#
|
738
|
+
# This is only needed for the 'mc' discovery method, other methods might change
|
739
|
+
# the concept of identity to mean something else so we should pass the full
|
740
|
+
# identity filter to them
|
741
|
+
def identity_filter_discovery_optimization
|
742
|
+
if options[:filter]["identity"].size > 0 && @discovery_method == "mc"
|
743
|
+
regex_filters = options[:filter]["identity"].select{|i| i.match("^\/")}.size
|
744
|
+
|
745
|
+
if regex_filters == 0
|
746
|
+
@discovered_agents = options[:filter]["identity"].clone
|
747
|
+
@force_direct_request = true if Config.instance.direct_addressing
|
748
|
+
end
|
749
|
+
end
|
714
750
|
end
|
715
751
|
|
716
752
|
# Calls an agent in a way very similar to call_agent but it supports batching
|
@@ -100,6 +100,7 @@ module MCollective
|
|
100
100
|
result_text << text_for_result(sender, status, message, result, ddl)
|
101
101
|
|
102
102
|
when :flatten
|
103
|
+
Log.warn("The display option :flatten is being deprecated and will be removed in the next minor release")
|
103
104
|
result_text << text_for_flattened_result(status, result)
|
104
105
|
|
105
106
|
end
|
@@ -128,7 +129,7 @@ module MCollective
|
|
128
129
|
result_text << " %s\n" % [Util.colorize(:yellow, msg)] unless msg == "OK"
|
129
130
|
|
130
131
|
# only print good data, ignore data that results from failure
|
131
|
-
if
|
132
|
+
if status == 0
|
132
133
|
if result.is_a?(Hash)
|
133
134
|
# figure out the lengths of the display as strings, we'll use
|
134
135
|
# it later to correctly justify the output
|
@@ -172,6 +173,10 @@ module MCollective
|
|
172
173
|
result_text << " " << result[k].pretty_inspect.split("\n").join("\n" << padding) << "\n"
|
173
174
|
end
|
174
175
|
end
|
176
|
+
elsif status == 1
|
177
|
+
# for status 1 we dont want to show half baked
|
178
|
+
# data by default since the DDL will supply all the defaults
|
179
|
+
# it just doesnt look right
|
175
180
|
else
|
176
181
|
result_text << "\n\t" + result.pretty_inspect.split("\n").join("\n\t")
|
177
182
|
end
|
@@ -249,6 +254,7 @@ module MCollective
|
|
249
254
|
# Add SimpleRPC common options
|
250
255
|
def self.add_simplerpc_options(parser, options)
|
251
256
|
parser.separator ""
|
257
|
+
parser.separator "RPC Options"
|
252
258
|
|
253
259
|
# add SimpleRPC specific options to all clients that use our library
|
254
260
|
parser.on('--np', '--no-progress', 'Do not show the progress bar') do |v|
|
@@ -26,7 +26,9 @@ module MCollective
|
|
26
26
|
interface = @ddl.action_interface(@action)
|
27
27
|
|
28
28
|
interface[:output].keys.each do |output|
|
29
|
-
|
29
|
+
# must deep clone this data to avoid accidental updates of the DDL in cases where the
|
30
|
+
# default is for example a string and someone does << on it
|
31
|
+
@data[output] = Marshal.load(Marshal.dump(interface[:output][output].fetch(:default, nil)))
|
30
32
|
end
|
31
33
|
end
|
32
34
|
|
data/lib/mcollective/shell.rb
CHANGED
@@ -17,9 +17,12 @@ module MCollective
|
|
17
17
|
# stderr - a variable that will receive stdin, must support <<
|
18
18
|
# environment - the shell environment, defaults to include LC_ALL=C
|
19
19
|
# set to nil to clear the environment even of LC_ALL
|
20
|
+
# timeout - a timeout in seconds after which the subprocess is killed,
|
21
|
+
# the special value :on_thread_exit kills the subprocess
|
22
|
+
# when the invoking thread (typically the agent) has ended
|
20
23
|
#
|
21
24
|
class Shell
|
22
|
-
attr_reader :environment, :command, :status, :stdout, :stderr, :stdin, :cwd
|
25
|
+
attr_reader :environment, :command, :status, :stdout, :stderr, :stdin, :cwd, :timeout
|
23
26
|
|
24
27
|
def initialize(command, options={})
|
25
28
|
@environment = {"LC_ALL" => "C"}
|
@@ -29,6 +32,7 @@ module MCollective
|
|
29
32
|
@stderr = ""
|
30
33
|
@stdin = nil
|
31
34
|
@cwd = Dir.tmpdir
|
35
|
+
@timeout = nil
|
32
36
|
|
33
37
|
options.each do |opt, val|
|
34
38
|
case opt.to_s
|
@@ -54,6 +58,9 @@ module MCollective
|
|
54
58
|
else
|
55
59
|
@environment.merge!(val.dup)
|
56
60
|
end
|
61
|
+
when "timeout"
|
62
|
+
raise "timeout should be a positive integer or the symbol :on_thread_exit symbol" unless val.eql?(:on_thread_exit) || ( val.is_a?(Fixnum) && val>0 )
|
63
|
+
@timeout = val
|
57
64
|
end
|
58
65
|
end
|
59
66
|
end
|
@@ -67,21 +74,51 @@ module MCollective
|
|
67
74
|
|
68
75
|
opts["stdin"] = @stdin if @stdin
|
69
76
|
|
70
|
-
|
71
|
-
|
72
|
-
#
|
73
|
-
# timeout
|
74
|
-
#
|
77
|
+
|
78
|
+
thread = Thread.current
|
79
|
+
# Start a double fork and exec with systemu which implies a guard thread.
|
80
|
+
# If a valid timeout is configured the guard thread will terminate the
|
81
|
+
# executing process and reap the pid.
|
82
|
+
# If no timeout is specified the process will run to completion with the
|
83
|
+
# guard thread reaping the pid on completion.
|
75
84
|
@status = systemu(@command, opts) do |cid|
|
76
85
|
begin
|
77
|
-
|
78
|
-
|
86
|
+
if timeout.is_a?(Fixnum)
|
87
|
+
# wait for the specified timeout
|
88
|
+
sleep timeout
|
89
|
+
else
|
90
|
+
# sleep while the agent thread is still alive
|
91
|
+
while(thread.alive?)
|
92
|
+
sleep 0.1
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# if the process is still running
|
97
|
+
if (Process.kill(0, cid))
|
98
|
+
# and a timeout was specified
|
99
|
+
if timeout
|
100
|
+
if Util.windows?
|
101
|
+
Process.kill('KILL', cid)
|
102
|
+
else
|
103
|
+
# Kill the process
|
104
|
+
Process.kill('TERM', cid)
|
105
|
+
sleep 2
|
106
|
+
Process.kill('KILL', cid) if (Process.kill(0, cid))
|
107
|
+
end
|
108
|
+
end
|
109
|
+
# only wait if the parent thread is dead
|
110
|
+
Process.waitpid(cid) unless thread.alive?
|
111
|
+
end
|
79
112
|
rescue SystemExit
|
113
|
+
rescue Errno::ESRCH
|
80
114
|
rescue Errno::ECHILD
|
115
|
+
Log.warn("Could not reap process '#{cid}'.")
|
81
116
|
rescue Exception => e
|
82
117
|
Log.info("Unexpected exception received while waiting for child process: #{e.class}: #{e}")
|
83
118
|
end
|
84
119
|
end
|
120
|
+
@status.thread.kill
|
121
|
+
@status
|
85
122
|
end
|
86
123
|
end
|
87
124
|
end
|
data/lib/mcollective/util.rb
CHANGED
@@ -430,7 +430,7 @@ module MCollective
|
|
430
430
|
# Code originally from Puppet
|
431
431
|
def self.versioncmp(version_a, version_b)
|
432
432
|
vre = /[-.]|\d+|[^-.\d]+/
|
433
|
-
|
433
|
+
ax = version_a.scan(vre)
|
434
434
|
bx = version_b.scan(vre)
|
435
435
|
|
436
436
|
while (ax.length>0 && bx.length>0)
|
@@ -456,5 +456,41 @@ module MCollective
|
|
456
456
|
|
457
457
|
version_a <=> version_b;
|
458
458
|
end
|
459
|
+
|
460
|
+
# we should really use Pathname#absolute? but it's not in all the
|
461
|
+
# ruby versions we support and it comes down to roughly this
|
462
|
+
def self.absolute_path?(path, separator=File::SEPARATOR, alt_separator=File::ALT_SEPARATOR)
|
463
|
+
if alt_separator
|
464
|
+
path_matcher = /^([a-zA-Z]:){0,1}[#{Regexp.quote alt_separator}#{Regexp.quote separator}]/
|
465
|
+
else
|
466
|
+
path_matcher = /^#{Regexp.quote separator}/
|
467
|
+
end
|
468
|
+
|
469
|
+
!!path.match(path_matcher)
|
470
|
+
end
|
471
|
+
|
472
|
+
# Converts a string into a boolean value
|
473
|
+
# Strings matching 1,y,yes,true or t will return TrueClass
|
474
|
+
# Any other value will return FalseClass
|
475
|
+
def self.str_to_bool(val)
|
476
|
+
clean_val = val.to_s.strip
|
477
|
+
if clean_val =~ /^(1|yes|true|y|t)$/i
|
478
|
+
return true
|
479
|
+
elsif clean_val =~ /^(0|no|false|n|f)$/i
|
480
|
+
return false
|
481
|
+
else
|
482
|
+
raise("Cannot convert string value '#{clean_val}' into a boolean.")
|
483
|
+
end
|
484
|
+
end
|
485
|
+
|
486
|
+
# Looks up the template directory and returns its full path
|
487
|
+
def self.templatepath(template_file)
|
488
|
+
config_dir = File.dirname(Config.instance.configfile)
|
489
|
+
template_path = File.join(config_dir, template_file)
|
490
|
+
return template_path if File.exists?(template_path)
|
491
|
+
|
492
|
+
template_path = File.join("/etc/mcollective", template_file)
|
493
|
+
return template_path
|
494
|
+
end
|
459
495
|
end
|
460
496
|
end
|