mcollective-client 2.5.3 → 2.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/mcollective.rb +1 -1
- data/lib/mcollective/application.rb +21 -6
- data/lib/mcollective/client.rb +7 -0
- data/lib/mcollective/config.rb +13 -1
- data/lib/mcollective/connector/base.rb +2 -0
- data/lib/mcollective/facts/base.rb +18 -5
- data/lib/mcollective/log.rb +7 -0
- data/lib/mcollective/logger/base.rb +12 -8
- data/lib/mcollective/logger/file_logger.rb +7 -0
- data/lib/mcollective/message.rb +1 -1
- data/lib/mcollective/optionparser.rb +4 -0
- data/lib/mcollective/registration/base.rb +24 -10
- data/lib/mcollective/rpc/agent.rb +7 -1
- data/lib/mcollective/rpc/client.rb +89 -35
- data/lib/mcollective/rpc/helpers.rb +8 -3
- data/lib/mcollective/rpc/result.rb +4 -0
- data/lib/mcollective/rpc/stats.rb +6 -2
- data/lib/mcollective/shell.rb +2 -0
- data/lib/mcollective/ssl.rb +5 -0
- data/lib/mcollective/util.rb +29 -1
- data/lib/mcollective/validator.rb +9 -4
- data/spec/spec_helper.rb +6 -0
- data/spec/unit/config_spec.rb +10 -0
- data/spec/unit/connector/base_spec.rb +28 -0
- data/spec/unit/facts/base_spec.rb +35 -0
- data/spec/unit/log_spec.rb +9 -0
- data/spec/unit/logger/base_spec.rb +12 -2
- data/spec/unit/logger/file_logger_spec.rb +82 -0
- data/spec/unit/plugins/mcollective/application/plugin_spec.rb +1 -0
- data/spec/unit/plugins/mcollective/connector/activemq_spec.rb +44 -17
- data/spec/unit/plugins/mcollective/connector/rabbitmq_spec.rb +20 -19
- data/spec/unit/plugins/mcollective/data/fact_data_spec.rb +92 -0
- data/spec/unit/registration/base_spec.rb +46 -0
- data/spec/unit/rpc/agent_spec.rb +37 -0
- data/spec/unit/rpc/client_spec.rb +68 -15
- data/spec/unit/rpc/result_spec.rb +21 -0
- data/spec/unit/runner_spec.rb +97 -19
- data/spec/unit/shell_spec.rb +5 -0
- data/spec/unit/ssl_spec.rb +5 -0
- data/spec/unit/util_spec.rb +163 -1
- metadata +215 -209
data/lib/mcollective.rb
CHANGED
@@ -314,12 +314,27 @@ module MCollective
|
|
314
314
|
:okcount => 0,
|
315
315
|
:failcount => 0}.merge(stats.to_hash)
|
316
316
|
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
317
|
+
if (request_stats[:discoverytime] == 0 && request_stats[:responses] == 0)
|
318
|
+
return 4
|
319
|
+
end
|
320
|
+
|
321
|
+
if (request_stats[:discovered] > 0)
|
322
|
+
if (request_stats[:responses] == 0)
|
323
|
+
return 3
|
324
|
+
elsif (request_stats[:failcount] > 0)
|
325
|
+
return 2
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
if (request_stats[:discovered] == 0)
|
330
|
+
if (request_stats[:responses] && request_stats[:responses] > 0)
|
331
|
+
return 0
|
332
|
+
else
|
333
|
+
return 1
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
return 0
|
323
338
|
end
|
324
339
|
|
325
340
|
# A helper that creates a consistent exit code for applications by looking at an
|
data/lib/mcollective/client.rb
CHANGED
@@ -18,6 +18,11 @@ module MCollective
|
|
18
18
|
@connection.connect
|
19
19
|
end
|
20
20
|
|
21
|
+
@@request_sequence = 0
|
22
|
+
def self.request_sequence
|
23
|
+
@@request_sequence
|
24
|
+
end
|
25
|
+
|
21
26
|
# Returns the configured main collective if no
|
22
27
|
# specific collective is specified as options
|
23
28
|
def collective
|
@@ -55,6 +60,8 @@ module MCollective
|
|
55
60
|
request.reply_to = @options[:reply_to] if @options[:reply_to]
|
56
61
|
end
|
57
62
|
|
63
|
+
@@request_sequence += 1
|
64
|
+
|
58
65
|
request.encode!
|
59
66
|
subscribe(agent, :reply) unless request.reply_to
|
60
67
|
request
|
data/lib/mcollective/config.rb
CHANGED
@@ -15,7 +15,8 @@ module MCollective
|
|
15
15
|
attr_reader :main_collective, :ssl_cipher, :registration_collective
|
16
16
|
attr_reader :direct_addressing, :direct_addressing_threshold, :ttl
|
17
17
|
attr_reader :default_discovery_method, :default_discovery_options
|
18
|
-
attr_reader :publish_timeout, :threaded, :soft_shutdown
|
18
|
+
attr_reader :publish_timeout, :threaded, :soft_shutdown, :activate_agents
|
19
|
+
attr_reader :registration_splay, :discovery_timeout, :soft_shutdown_timeout
|
19
20
|
|
20
21
|
def initialize
|
21
22
|
@configured = false
|
@@ -43,6 +44,8 @@ module MCollective
|
|
43
44
|
@registration_collective = val
|
44
45
|
when "registerinterval"
|
45
46
|
@registerinterval = Integer(val)
|
47
|
+
when "registration_splay"
|
48
|
+
@registration_splay = Util.str_to_bool(val)
|
46
49
|
when "collectives"
|
47
50
|
@collectives = val.split(",").map {|c| c.strip}
|
48
51
|
when "main_collective"
|
@@ -87,6 +90,8 @@ module MCollective
|
|
87
90
|
@classesfile = val
|
88
91
|
when /^plugin.(.+)$/
|
89
92
|
@pluginconf[$1] = val
|
93
|
+
when "discovery_timeout"
|
94
|
+
@discovery_timeout = Integer(val)
|
90
95
|
when "publish_timeout"
|
91
96
|
@publish_timeout = Integer(val)
|
92
97
|
when "rpcaudit"
|
@@ -115,6 +120,10 @@ module MCollective
|
|
115
120
|
@default_discovery_method = val
|
116
121
|
when "soft_shutdown"
|
117
122
|
@soft_shutdown = Util.str_to_bool(val)
|
123
|
+
when "soft_shutdown_timeout"
|
124
|
+
@soft_shutdown_timeout = Integer(val)
|
125
|
+
when "activate_agents"
|
126
|
+
@activate_agents = Util.str_to_bool(val)
|
118
127
|
when "topicprefix", "topicsep", "queueprefix", "rpchelptemplate", "helptemplatedir"
|
119
128
|
Log.warn("Use of deprecated '#{key}' option. This option is ignored and should be removed from '#{configfile}'")
|
120
129
|
else
|
@@ -165,6 +174,7 @@ module MCollective
|
|
165
174
|
@registration = "Agentlist"
|
166
175
|
@registerinterval = 0
|
167
176
|
@registration_collective = nil
|
177
|
+
@registration_splay = false
|
168
178
|
@classesfile = "/var/lib/puppet/state/classes.txt"
|
169
179
|
@rpcaudit = false
|
170
180
|
@rpcauditprovider = ""
|
@@ -193,6 +203,8 @@ module MCollective
|
|
193
203
|
@publish_timeout = 2
|
194
204
|
@threaded = false
|
195
205
|
@soft_shutdown = false
|
206
|
+
@soft_shutdown_timeout = nil
|
207
|
+
@activate_agents = true
|
196
208
|
end
|
197
209
|
|
198
210
|
def read_plugin_config_dir(dir)
|
@@ -17,6 +17,8 @@ module MCollective
|
|
17
17
|
module Connector
|
18
18
|
class Base
|
19
19
|
def self.inherited(klass)
|
20
|
+
plugin_name = klass.to_s.split("::").last.downcase
|
21
|
+
ddl = DDL.new(plugin_name, :connector)
|
20
22
|
PluginManager << {:type => "connector_plugin", :class => klass.to_s}
|
21
23
|
end
|
22
24
|
end
|
@@ -34,11 +34,7 @@ module MCollective
|
|
34
34
|
# Force reset to last known good state on empty facts
|
35
35
|
raise "Got empty facts" if tfacts.empty?
|
36
36
|
|
37
|
-
@facts
|
38
|
-
|
39
|
-
tfacts.each_pair do |key,value|
|
40
|
-
@facts[key.to_s] = value.to_s
|
41
|
-
end
|
37
|
+
@facts = normalize_facts(tfacts)
|
42
38
|
|
43
39
|
@last_good_facts = @facts.clone
|
44
40
|
@last_facts_load = Time.now.to_i
|
@@ -81,6 +77,23 @@ module MCollective
|
|
81
77
|
def force_reload?
|
82
78
|
false
|
83
79
|
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def normalize_facts(value)
|
84
|
+
case value
|
85
|
+
when Array
|
86
|
+
return value.map { |v| normalize_facts(v) }
|
87
|
+
when Hash
|
88
|
+
new_hash = {}
|
89
|
+
value.each do |k,v|
|
90
|
+
new_hash[k.to_s] = normalize_facts(v)
|
91
|
+
end
|
92
|
+
return new_hash
|
93
|
+
else
|
94
|
+
return value.to_s
|
95
|
+
end
|
96
|
+
end
|
84
97
|
end
|
85
98
|
end
|
86
99
|
end
|
data/lib/mcollective/log.rb
CHANGED
@@ -44,6 +44,13 @@ module MCollective
|
|
44
44
|
@logger.cycle_level if @configured
|
45
45
|
end
|
46
46
|
|
47
|
+
# reopen log files
|
48
|
+
def reopen
|
49
|
+
if @configured
|
50
|
+
@logger.reopen
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
47
54
|
# logs a message at a certain level
|
48
55
|
def log(level, msg)
|
49
56
|
configure unless @configured
|
@@ -34,6 +34,18 @@ module MCollective
|
|
34
34
|
@active_level = level.to_sym
|
35
35
|
end
|
36
36
|
|
37
|
+
def start
|
38
|
+
raise "The logging class did not supply a start method"
|
39
|
+
end
|
40
|
+
|
41
|
+
def log(level, from, msg)
|
42
|
+
raise "The logging class did not supply a log method"
|
43
|
+
end
|
44
|
+
|
45
|
+
def reopen
|
46
|
+
# reopen may not make sense to all Loggers, but we expect it of the API
|
47
|
+
end
|
48
|
+
|
37
49
|
private
|
38
50
|
def map_level(level)
|
39
51
|
raise "Logger class do not know how to handle #{level} messages" unless valid_levels.include?(level.to_sym)
|
@@ -60,14 +72,6 @@ module MCollective
|
|
60
72
|
def valid_levels
|
61
73
|
raise "The logging class did not supply a valid_levels method"
|
62
74
|
end
|
63
|
-
|
64
|
-
def log(level, from, msg)
|
65
|
-
raise "The logging class did not supply a log method"
|
66
|
-
end
|
67
|
-
|
68
|
-
def start
|
69
|
-
raise "The logging class did not supply a start method"
|
70
|
-
end
|
71
75
|
end
|
72
76
|
end
|
73
77
|
end
|
data/lib/mcollective/message.rb
CHANGED
@@ -200,7 +200,7 @@ module MCollective
|
|
200
200
|
if msg_age > ttl
|
201
201
|
PluginManager["global_stats"].ttlexpired
|
202
202
|
|
203
|
-
raise(MsgTTLExpired, "message #{requestid} from #{cid} created at #{msgtime} is #{msg_age} seconds old, TTL is #{ttl}")
|
203
|
+
raise(MsgTTLExpired, "message #{requestid} from #{cid} created at #{msgtime} is #{msg_age} seconds old, TTL is #{ttl}. Rejecting message.")
|
204
204
|
end
|
205
205
|
end
|
206
206
|
|
@@ -177,6 +177,10 @@ module MCollective
|
|
177
177
|
@parser.on("--threaded", "Start publishing requests and receiving responses in threaded mode.") do |v|
|
178
178
|
@options[:threaded] = true
|
179
179
|
end
|
180
|
+
|
181
|
+
@parser.on("--sort", "Sort the output of an RPC call before processing.") do |v|
|
182
|
+
@options[:sort] = true
|
183
|
+
end
|
180
184
|
end
|
181
185
|
|
182
186
|
private
|
@@ -20,16 +20,7 @@ module MCollective
|
|
20
20
|
return false if interval == 0
|
21
21
|
|
22
22
|
Thread.new do
|
23
|
-
|
24
|
-
begin
|
25
|
-
publish(body)
|
26
|
-
|
27
|
-
sleep interval
|
28
|
-
rescue Exception => e
|
29
|
-
Log.error("Sending registration message failed: #{e}")
|
30
|
-
sleep interval
|
31
|
-
end
|
32
|
-
end
|
23
|
+
publish_thread(connection)
|
33
24
|
end
|
34
25
|
end
|
35
26
|
|
@@ -72,6 +63,29 @@ module MCollective
|
|
72
63
|
req.publish
|
73
64
|
end
|
74
65
|
end
|
66
|
+
|
67
|
+
def body
|
68
|
+
raise "Registration Plugins must implement the #body method"
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
def publish_thread(connnection)
|
73
|
+
if config.registration_splay
|
74
|
+
splay_delay = rand(interval)
|
75
|
+
Log.debug("registration_splay enabled. Registration will start in #{splay_delay} seconds")
|
76
|
+
sleep splay_delay
|
77
|
+
end
|
78
|
+
|
79
|
+
loop do
|
80
|
+
begin
|
81
|
+
publish(body)
|
82
|
+
sleep interval
|
83
|
+
rescue Exception => e
|
84
|
+
Log.error("Sending registration message failed: #{e}")
|
85
|
+
sleep interval
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
75
89
|
end
|
76
90
|
end
|
77
91
|
end
|
@@ -138,10 +138,16 @@ module MCollective
|
|
138
138
|
# end
|
139
139
|
def self.activate?
|
140
140
|
agent_name = self.to_s.split("::").last.downcase
|
141
|
+
config = Config.instance
|
141
142
|
|
142
143
|
Log.debug("Starting default activation checks for #{agent_name}")
|
143
144
|
|
144
|
-
|
145
|
+
# Check global state to determine if agent should be loaded
|
146
|
+
should_activate = config.activate_agents
|
147
|
+
|
148
|
+
# Check agent specific state to determine if agent should be loaded
|
149
|
+
should_activate = Util.str_to_bool(config.pluginconf.fetch("#{agent_name}.activate_agent",
|
150
|
+
should_activate))
|
145
151
|
|
146
152
|
unless should_activate
|
147
153
|
Log.debug("Found plugin configuration '#{agent_name}.activate_agent' with value '#{should_activate}'")
|
@@ -25,7 +25,12 @@ module MCollective
|
|
25
25
|
initial_options = Marshal.load(@@initial_options)
|
26
26
|
|
27
27
|
else
|
28
|
-
oparser = MCollective::Optionparser.new({:verbose => false,
|
28
|
+
oparser = MCollective::Optionparser.new({ :verbose => false,
|
29
|
+
:progress_bar => true,
|
30
|
+
:mcollective_limit_targets => false,
|
31
|
+
:batch_size => nil,
|
32
|
+
:batch_sleep_time => 1 },
|
33
|
+
"filter")
|
29
34
|
|
30
35
|
initial_options = oparser.parse do |parser, opts|
|
31
36
|
if block_given?
|
@@ -67,13 +72,13 @@ module MCollective
|
|
67
72
|
@discovery_options = initial_options[:discovery_options] || []
|
68
73
|
@force_display_mode = initial_options[:force_display_mode] || false
|
69
74
|
|
70
|
-
@batch_size =
|
75
|
+
@batch_size = initial_options[:batch_size] || 0
|
71
76
|
@batch_sleep_time = Float(initial_options[:batch_sleep_time] || 1)
|
72
|
-
@batch_mode = @batch_size
|
77
|
+
@batch_mode = determine_batch_mode(@batch_size)
|
73
78
|
|
74
79
|
agent_filter agent
|
75
80
|
|
76
|
-
@discovery_timeout = @initial_options.fetch(:disctimeout, nil)
|
81
|
+
@discovery_timeout = @initial_options.fetch(:disctimeout, nil) || Config.instance.discovery_timeout
|
77
82
|
|
78
83
|
@collective = @client.collective
|
79
84
|
@ttl = initial_options[:ttl] || Config.instance.ttl
|
@@ -239,6 +244,11 @@ module MCollective
|
|
239
244
|
|
240
245
|
validate_request(action, args)
|
241
246
|
|
247
|
+
# TODO(ploubser): The logic here seems poor. It implies that it is valid to
|
248
|
+
# pass arguments where batch_mode is set to false and batch_mode > 0.
|
249
|
+
# If this is the case we completely ignore the supplied value of batch_mode
|
250
|
+
# and do our own thing.
|
251
|
+
|
242
252
|
# if a global batch size is set just use that else set it
|
243
253
|
# in the case that it was passed as an argument
|
244
254
|
batch_mode = args.include?(:batch_size) || @batch_mode
|
@@ -248,7 +258,7 @@ module MCollective
|
|
248
258
|
# if we were given a batch_size argument thats 0 and batch_mode was
|
249
259
|
# determined to be on via global options etc this will allow a batch_size
|
250
260
|
# of 0 to disable or batch_mode for this call only
|
251
|
-
batch_mode = (
|
261
|
+
batch_mode = determine_batch_mode(batch_size)
|
252
262
|
|
253
263
|
# Handle single target requests by doing discovery and picking
|
254
264
|
# a random node. Then do a custom request specifying a filter
|
@@ -580,7 +590,13 @@ module MCollective
|
|
580
590
|
|
581
591
|
# Sets and sanity checks the limit_targets variable
|
582
592
|
# used to restrict how many nodes we'll target
|
593
|
+
# Limit targets can be reset by passing nil or false
|
583
594
|
def limit_targets=(limit)
|
595
|
+
if !limit
|
596
|
+
@limit_targets = nil
|
597
|
+
return
|
598
|
+
end
|
599
|
+
|
584
600
|
if limit.is_a?(String)
|
585
601
|
raise "Invalid limit specified: #{limit} valid limits are /^\d+%*$/" unless limit =~ /^\d+%*$/
|
586
602
|
|
@@ -606,10 +622,14 @@ module MCollective
|
|
606
622
|
|
607
623
|
# Sets the batch size, if the size is set to 0 that will disable batch mode
|
608
624
|
def batch_size=(limit)
|
609
|
-
|
625
|
+
unless Config.instance.direct_addressing
|
626
|
+
raise "Can only set batch size if direct addressing is supported"
|
627
|
+
end
|
628
|
+
|
629
|
+
validate_batch_size(limit)
|
610
630
|
|
611
|
-
@batch_size =
|
612
|
-
@batch_mode = @batch_size
|
631
|
+
@batch_size = limit
|
632
|
+
@batch_mode = determine_batch_mode(@batch_size)
|
613
633
|
end
|
614
634
|
|
615
635
|
def batch_sleep_time=(time)
|
@@ -759,8 +779,8 @@ module MCollective
|
|
759
779
|
def call_agent_batched(action, args, opts, batch_size, sleep_time, &block)
|
760
780
|
raise "Batched requests requires direct addressing" unless Config.instance.direct_addressing
|
761
781
|
raise "Cannot bypass result processing for batched requests" if args[:process_results] == false
|
782
|
+
validate_batch_size(batch_size)
|
762
783
|
|
763
|
-
batch_size = Integer(batch_size)
|
764
784
|
sleep_time = Float(sleep_time)
|
765
785
|
|
766
786
|
Log.debug("Calling #{agent}##{action} in batches of #{batch_size} with sleep time of #{sleep_time}")
|
@@ -782,10 +802,22 @@ module MCollective
|
|
782
802
|
@stdout.print twirl.twirl(respcount, discovered.size)
|
783
803
|
end
|
784
804
|
|
805
|
+
if (batch_size =~ /^(\d+)%$/)
|
806
|
+
# determine batch_size as a percentage of the discovered array's size
|
807
|
+
batch_size = (discovered.size / 100.0 * Integer($1)).ceil
|
808
|
+
else
|
809
|
+
batch_size = Integer(batch_size)
|
810
|
+
end
|
811
|
+
|
785
812
|
@stats.requestid = nil
|
813
|
+
processed_nodes = 0
|
786
814
|
|
787
|
-
discovered.in_groups_of(batch_size) do |hosts
|
788
|
-
message = Message.new(req, nil, {:agent => @agent,
|
815
|
+
discovered.in_groups_of(batch_size) do |hosts|
|
816
|
+
message = Message.new(req, nil, {:agent => @agent,
|
817
|
+
:type => :direct_request,
|
818
|
+
:collective => @collective,
|
819
|
+
:filter => opts[:filter],
|
820
|
+
:options => opts})
|
789
821
|
|
790
822
|
# first time round we let the Message object create a request id
|
791
823
|
# we then re-use it for future requests to keep auditing sane etc
|
@@ -808,13 +840,20 @@ module MCollective
|
|
808
840
|
end
|
809
841
|
end
|
810
842
|
|
843
|
+
if @initial_options[:sort]
|
844
|
+
results.sort!
|
845
|
+
end
|
846
|
+
|
811
847
|
@stats.noresponsefrom.concat @client.stats[:noresponsefrom]
|
812
848
|
@stats.responses += @client.stats[:responses]
|
813
849
|
@stats.blocktime += @client.stats[:blocktime] + sleep_time
|
814
850
|
@stats.totaltime += @client.stats[:totaltime]
|
815
851
|
@stats.discoverytime += @client.stats[:discoverytime]
|
816
852
|
|
817
|
-
|
853
|
+
processed_nodes += hosts.length
|
854
|
+
if (discovered.length > processed_nodes)
|
855
|
+
sleep sleep_time
|
856
|
+
end
|
818
857
|
end
|
819
858
|
|
820
859
|
@stats.aggregate_summary = aggregate.summarize if aggregate
|
@@ -898,6 +937,10 @@ module MCollective
|
|
898
937
|
end
|
899
938
|
end
|
900
939
|
|
940
|
+
if @initial_options[:sort]
|
941
|
+
results.sort!
|
942
|
+
end
|
943
|
+
|
901
944
|
@stats.aggregate_summary = aggregate.summarize if aggregate
|
902
945
|
@stats.aggregate_failures = aggregate.failed if aggregate
|
903
946
|
@stats.client_stats = @client.stats
|
@@ -947,35 +990,46 @@ module MCollective
|
|
947
990
|
result = rpc_result_from_reply(@agent, action, resp)
|
948
991
|
aggregate = aggregate_reply(result, aggregate) if aggregate
|
949
992
|
|
950
|
-
if resp[:body][:statuscode] == 0
|
951
|
-
|
952
|
-
|
953
|
-
@stats.time_block_execution :start
|
993
|
+
@stats.ok if resp[:body][:statuscode] == 0
|
994
|
+
@stats.fail if resp[:body][:statuscode] != 0
|
995
|
+
@stats.time_block_execution :start
|
954
996
|
|
955
|
-
|
956
|
-
|
957
|
-
|
958
|
-
|
959
|
-
|
960
|
-
|
997
|
+
case block.arity
|
998
|
+
when 1
|
999
|
+
block.call(resp)
|
1000
|
+
when 2
|
1001
|
+
block.call(resp, result)
|
1002
|
+
end
|
961
1003
|
|
962
|
-
|
963
|
-
else
|
964
|
-
@stats.fail
|
1004
|
+
@stats.time_block_execution :end
|
965
1005
|
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
|
970
|
-
|
971
|
-
|
972
|
-
|
973
|
-
|
974
|
-
|
1006
|
+
return aggregate
|
1007
|
+
end
|
1008
|
+
|
1009
|
+
private
|
1010
|
+
|
1011
|
+
def determine_batch_mode(batch_size)
|
1012
|
+
if (batch_size != 0 && batch_size != "0")
|
1013
|
+
return true
|
1014
|
+
end
|
1015
|
+
|
1016
|
+
return false
|
1017
|
+
end
|
1018
|
+
|
1019
|
+
# Validate the bach_size based on the following criteria
|
1020
|
+
# batch_size is percentage string and it's more than 0 percent
|
1021
|
+
# batch_size is a string of digits
|
1022
|
+
# batch_size is of type Integer
|
1023
|
+
def validate_batch_size(batch_size)
|
1024
|
+
if (batch_size.is_a?(Integer))
|
1025
|
+
return
|
1026
|
+
elsif (batch_size.is_a?(String))
|
1027
|
+
if ((batch_size =~ /^(\d+)%$/ && Integer($1) != 0) || batch_size =~ /^(\d+)$/)
|
1028
|
+
return
|
975
1029
|
end
|
976
1030
|
end
|
977
1031
|
|
978
|
-
|
1032
|
+
raise("batch_size must be an integer or match a percentage string (e.g. '24%'")
|
979
1033
|
end
|
980
1034
|
end
|
981
1035
|
end
|