mcollective-client 2.2.4 → 2.4.0
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.
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
|