right_chimp 1.1.3 → 2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/CHANGES +4 -0
- data/Gemfile +0 -1
- data/Gemfile.lock +2 -22
- data/README +25 -47
- data/chimp.gemspec +4 -4
- data/lib/right_chimp/Chimp.rb +498 -317
- data/lib/right_chimp/Log.rb +7 -4
- data/lib/right_chimp/daemon/ChimpDaemon.rb +75 -10
- data/lib/right_chimp/daemon/ChimpDaemonClient.rb +16 -8
- data/lib/right_chimp/exec/ExecReport.rb +14 -20
- data/lib/right_chimp/exec/ExecRightScript.rb +7 -6
- data/lib/right_chimp/exec/ExecSSH.rb +4 -9
- data/lib/right_chimp/exec/Executor.rb +7 -5
- data/lib/right_chimp/objects/ChimpObjects.rb +393 -0
- data/lib/right_chimp/queue/ChimpQueue.rb +32 -32
- data/lib/right_chimp/queue/ExecutionGroup.rb +3 -8
- data/lib/right_chimp/queue/QueueWorker.rb +0 -4
- data/lib/right_chimp/templates/all_jobs.erb +4 -4
- data/lib/right_chimp/version.rb +1 -1
- data/lib/right_chimp.rb +11 -2
- metadata +9 -38
data/lib/right_chimp/Chimp.rb
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
#
|
2
2
|
# The Chimp class encapsulates the command-line program logic
|
3
3
|
#
|
4
|
-
|
5
4
|
module Chimp
|
6
5
|
class Chimp
|
6
|
+
|
7
7
|
attr_accessor :concurrency, :delay, :retry_count, :hold, :progress, :prompt,
|
8
8
|
:quiet, :use_chimpd, :chimpd_host, :chimpd_port, :tags, :array_names,
|
9
|
-
:deployment_names, :script, :servers, :ssh, :report, :interactive, :action,
|
10
|
-
:limit_start, :limit_end, :dry_run, :group, :job_id, :verify
|
11
|
-
|
9
|
+
:deployment_names, :script, :all_scripts, :servers, :ssh, :report, :interactive, :action,
|
10
|
+
:limit_start, :limit_end, :dry_run, :group, :job_id, :job_uuid, :verify, :cli_args
|
12
11
|
#
|
13
12
|
# These class variables control verbosity
|
14
13
|
#
|
@@ -53,7 +52,10 @@ module Chimp
|
|
53
52
|
#
|
54
53
|
@current = true
|
55
54
|
@match_all = true
|
55
|
+
|
56
|
+
# This is an array of json data for each instance
|
56
57
|
@servers = []
|
58
|
+
|
57
59
|
@arrays = []
|
58
60
|
@tags = []
|
59
61
|
@array_names = []
|
@@ -78,7 +80,15 @@ module Chimp
|
|
78
80
|
@chimpd_port = 9055
|
79
81
|
@chimpd_wait_until_done = false
|
80
82
|
|
81
|
-
|
83
|
+
#
|
84
|
+
# Will contain the operational scripts we have found
|
85
|
+
# In the form: [name, href]
|
86
|
+
@op_scripts = []
|
87
|
+
|
88
|
+
#
|
89
|
+
# This will contain the href and the name of the script to be run
|
90
|
+
# in the form: [name, href]
|
91
|
+
@script_to_run = nil
|
82
92
|
end
|
83
93
|
|
84
94
|
#
|
@@ -87,10 +97,23 @@ module Chimp
|
|
87
97
|
def run
|
88
98
|
queue = ChimpQueue.instance
|
89
99
|
|
100
|
+
arguments = []
|
101
|
+
|
102
|
+
ARGV.each { |arg| arguments << arg.clone }
|
103
|
+
|
104
|
+
self.cli_args=arguments.collect {|param|
|
105
|
+
param.gsub(/(?<==).*/) do |match|
|
106
|
+
match='"'+match+'"'
|
107
|
+
end
|
108
|
+
}.join(" ")
|
109
|
+
|
110
|
+
|
90
111
|
parse_command_line if @interactive
|
112
|
+
|
91
113
|
check_option_validity if @interactive
|
92
114
|
disable_logging unless @@verbose
|
93
115
|
|
116
|
+
|
94
117
|
puts "chimp #{VERSION} executing..." if (@interactive and not @use_chimpd) and not @@quiet
|
95
118
|
|
96
119
|
#
|
@@ -105,20 +128,50 @@ module Chimp
|
|
105
128
|
# Send the command to chimpd for execution
|
106
129
|
#
|
107
130
|
if @use_chimpd
|
108
|
-
|
131
|
+
timestamp=Time.now.to_i
|
132
|
+
length=6
|
133
|
+
self.job_uuid = (36**(length-1) + rand(36**length - 36**(length-1))).to_s(36)
|
134
|
+
ChimpDaemonClient.submit(@chimpd_host, @chimpd_port, self,self.job_uuid)
|
109
135
|
exit
|
136
|
+
else
|
137
|
+
#Connect to the Api
|
138
|
+
Connection.instance
|
139
|
+
if @interactive
|
140
|
+
Connection.connect
|
141
|
+
else
|
142
|
+
Connection.connect_and_cache
|
143
|
+
end
|
110
144
|
end
|
111
145
|
|
112
|
-
#
|
113
146
|
# If we're processing the command ourselves, then go
|
114
147
|
# ahead and start making API calls to select the objects
|
115
148
|
# to operate upon
|
116
149
|
#
|
150
|
+
|
151
|
+
# Get elements if --array has been passed
|
117
152
|
get_array_info
|
153
|
+
|
154
|
+
# Get elements if we are searching by tags
|
118
155
|
get_server_info
|
119
|
-
get_template_info
|
120
|
-
get_executable_info
|
121
156
|
|
157
|
+
# At this stage @servers should be populated with our findings
|
158
|
+
# Get ST info for all elements
|
159
|
+
if not @servers.empty?
|
160
|
+
get_template_info unless @servers.empty?
|
161
|
+
|
162
|
+
puts "Looking for the rightscripts (This might take some time)" if (@interactive and not @use_chimpd) and not @@quiet
|
163
|
+
get_executable_info
|
164
|
+
end
|
165
|
+
|
166
|
+
if Chimp.failure
|
167
|
+
#This is the failure point when executing standalone
|
168
|
+
Log.error "##################################################"
|
169
|
+
Log.error " API CALL FAILED FOR:"
|
170
|
+
Log.error " chimp #{@cli_args} "
|
171
|
+
Log.error " Run manually!"
|
172
|
+
Log.error "##################################################"
|
173
|
+
exit 1
|
174
|
+
end
|
122
175
|
#
|
123
176
|
# Optionally display the list of objects to operate on
|
124
177
|
# and prompt the user
|
@@ -127,48 +180,70 @@ module Chimp
|
|
127
180
|
list_of_objects = make_human_readable_list_of_objects
|
128
181
|
confirm = (list_of_objects.size > 0 and @action != :action_none) or @action == :action_none
|
129
182
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
183
|
+
if @script_to_run.nil?
|
184
|
+
verify("Your command will be executed on the following:", list_of_objects, confirm)
|
185
|
+
else
|
186
|
+
verify("Your command \""+@script_to_run.params['right_script']['name']+"\" will be executed on the following:", list_of_objects, confirm)
|
134
187
|
end
|
135
188
|
end
|
136
|
-
|
137
189
|
#
|
138
190
|
# Load the queue with work
|
139
191
|
#
|
140
|
-
|
141
|
-
|
192
|
+
if not @servers.first.nil? and ( not @executable.nil? or @action == :action_ssh or @action == :action_report)
|
193
|
+
jobs = generate_jobs(@servers, @server_template, @executable)
|
194
|
+
add_to_queue(jobs)
|
195
|
+
end
|
142
196
|
|
143
197
|
#
|
144
198
|
# Exit early if there is nothing to do
|
145
199
|
#
|
146
200
|
if @action == :action_none or queue.group[@group].size == 0
|
147
|
-
puts "No actions to perform." unless
|
201
|
+
puts "No actions to perform." unless self.quiet
|
148
202
|
else
|
149
203
|
do_work
|
150
204
|
end
|
151
205
|
end
|
152
206
|
|
153
207
|
#
|
154
|
-
#
|
155
|
-
# Used by chimpd
|
208
|
+
# Load up @array with server arrays to operate on
|
156
209
|
#
|
157
|
-
def
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
210
|
+
def get_array_info
|
211
|
+
return if @array_names.empty?
|
212
|
+
|
213
|
+
# The first thing to do here is make an api1.5 call to get the array hrefs.
|
214
|
+
arrays_hrefs=get_hrefs_for_arrays(@array_names)
|
215
|
+
# Then we filter on all the instances by this href
|
216
|
+
all_instances = Connection.all_instances() unless arrays_hrefs.empty?
|
217
|
+
if all_instances.nil?
|
218
|
+
Log.debug "No results from API query"
|
219
|
+
else
|
220
|
+
arrays_hrefs.each { |href|
|
221
|
+
@servers += all_instances.select {|s|
|
222
|
+
s['links']['incarnator']['href'] == href
|
223
|
+
}
|
224
|
+
}
|
225
|
+
end
|
226
|
+
# The result will be stored (not returned) into @servers
|
227
|
+
end
|
228
|
+
|
229
|
+
#
|
230
|
+
# Go through each of the various ways to specify servers via
|
231
|
+
# the command line (tags, deployments, etc.) and get all the info
|
232
|
+
# needed from the RightScale API.
|
233
|
+
#
|
234
|
+
def get_server_info
|
235
|
+
@servers += get_servers_by_tag(@tags) unless tags.empty?
|
236
|
+
# Perhaps allow searchign by deployment
|
237
|
+
@servers += get_servers_by_deployment(@deployment_names) unless @deployment_names.empty?
|
164
238
|
end
|
165
239
|
|
166
240
|
#
|
167
241
|
# Get the ServerTemplate info from the API
|
168
242
|
#
|
169
243
|
def get_template_info
|
170
|
-
|
171
|
-
|
244
|
+
# If we have a server or an array
|
245
|
+
if not (@servers.first.nil? and @array_names.empty?)
|
246
|
+
@server_template = detect_server_template(@servers)
|
172
247
|
end
|
173
248
|
end
|
174
249
|
|
@@ -176,9 +251,38 @@ module Chimp
|
|
176
251
|
# Get the Executable (RightScript) info from the API
|
177
252
|
#
|
178
253
|
def get_executable_info
|
179
|
-
if not (@servers.empty?
|
180
|
-
|
181
|
-
|
254
|
+
if not (@servers.empty? )
|
255
|
+
if (@script != nil)
|
256
|
+
# If script is an uri/url no need to "detect it"
|
257
|
+
# https://my.rightscale.com/acct/9202/right_scripts/205347
|
258
|
+
if @script =~ /\A#{URI::regexp}\z/
|
259
|
+
if not @use_chimpd
|
260
|
+
puts "=================================================="
|
261
|
+
puts "WARNING! You will be running this script on all "
|
262
|
+
puts "server matches! (Press enter to continue)"
|
263
|
+
puts "=================================================="
|
264
|
+
gets
|
265
|
+
end
|
266
|
+
|
267
|
+
script_number = File.basename(@script)
|
268
|
+
|
269
|
+
s=Executable.new
|
270
|
+
s.params['right_script']['href']="right_script_href=/api/right_scripts/"+script_number
|
271
|
+
#Make an 1.5 call to extract name, by loading resource.
|
272
|
+
Log.debug "Making API 1.5 call : client.resource(#{s.params['right_script']['href'].scan(/=(.*)/).last.last})"
|
273
|
+
the_name = Connection.client.resource(s.params['right_script']['href'].scan(/=(.*)/).last.last).name
|
274
|
+
s.params['right_script']['name'] = the_name
|
275
|
+
@executable=s
|
276
|
+
else
|
277
|
+
#If its not an url, go ahead try to locate it in the ST"
|
278
|
+
@executable = detect_right_script(@server_template, @script)
|
279
|
+
end
|
280
|
+
else
|
281
|
+
# @script could be nil because we want to run ssh
|
282
|
+
if @action == :action_ssh
|
283
|
+
puts "Using SSH command: \"#{@ssh}\"" if @action == :action_ssh
|
284
|
+
end
|
285
|
+
end
|
182
286
|
end
|
183
287
|
end
|
184
288
|
|
@@ -292,6 +396,7 @@ module Chimp
|
|
292
396
|
@limit_start, @limit_end = arg.split(',')
|
293
397
|
when '--verbose', '-v'
|
294
398
|
@@verbose = true
|
399
|
+
Log.threshold = Logger::DEBUG
|
295
400
|
when '--quiet', '-q'
|
296
401
|
@@quiet = true
|
297
402
|
when '--dont-check-templates', '-0'
|
@@ -323,6 +428,17 @@ module Chimp
|
|
323
428
|
@verify = false
|
324
429
|
end
|
325
430
|
end
|
431
|
+
|
432
|
+
if @use_chimpd && ( @script.nil? || @script.empty? )
|
433
|
+
if @chimpd_wait_until_done == false
|
434
|
+
puts "#######################################################"
|
435
|
+
puts "ERROR: --script cannot be empty when sending to chimpd"
|
436
|
+
puts "#######################################################"
|
437
|
+
exit 1
|
438
|
+
end
|
439
|
+
end
|
440
|
+
|
441
|
+
|
326
442
|
rescue GetoptLong::InvalidOption => ex
|
327
443
|
help
|
328
444
|
exit 1
|
@@ -335,7 +451,6 @@ module Chimp
|
|
335
451
|
if @group_concurrency > @concurrency
|
336
452
|
@concurrency = @group_concurrency
|
337
453
|
end
|
338
|
-
|
339
454
|
end
|
340
455
|
|
341
456
|
#
|
@@ -361,269 +476,327 @@ module Chimp
|
|
361
476
|
end
|
362
477
|
|
363
478
|
#
|
364
|
-
#
|
365
|
-
# the command line (tags, deployments, etc.) and get all the info
|
366
|
-
# needed from the RightScale API.
|
367
|
-
#
|
368
|
-
def get_server_info
|
369
|
-
@servers += get_servers_by_tag(@tags)
|
370
|
-
@servers += get_servers_by_deployment(@deployment_names)
|
371
|
-
@servers = filter_out_non_operational_servers(@servers)
|
372
|
-
end
|
373
|
-
|
479
|
+
# Api1.6 equivalent
|
374
480
|
#
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
Log.debug "Breaking array into instances..."
|
387
|
-
@servers += get_servers_by_array(@array_names)
|
388
|
-
@array_names = []
|
481
|
+
def get_servers_by_tag(tags)
|
482
|
+
# Take tags and collapse it,
|
483
|
+
# Default case, tag is AND
|
484
|
+
if @match_all
|
485
|
+
t = tags.join("&tag=")
|
486
|
+
filter = "tag=#{t}"
|
487
|
+
servers = Connection.instances(filter)
|
488
|
+
else
|
489
|
+
t = tags.join(",")
|
490
|
+
filter = "tag=#{t}"
|
491
|
+
servers = Connection.instances(filter)
|
389
492
|
end
|
390
493
|
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
if not a.nil?
|
395
|
-
@arrays << a
|
494
|
+
if servers.nil?
|
495
|
+
if @ignore_errors
|
496
|
+
Log.warn "[#{Chimp.get_job_uuid}]Tag query returned no results: #{tags.join(" ")}"
|
396
497
|
else
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
498
|
+
raise "[#{Chimp.get_job_uuid}]Tag query returned no results: #{tags.join(" ")}\n"
|
499
|
+
end
|
500
|
+
elsif servers.empty?
|
501
|
+
if @ignore_errors
|
502
|
+
Log.warn "[#{Chimp.get_job_uuid}]Tag query returned no results: #{tags.join(" ")}"
|
503
|
+
else
|
504
|
+
raise "[#{Chimp.get_job_uuid}]Tag query returned no results: #{tags.join(" ")}\n"
|
402
505
|
end
|
403
506
|
end
|
507
|
+
|
508
|
+
servers = verify_tagged_instances(servers,tags)
|
509
|
+
|
510
|
+
return(servers)
|
404
511
|
end
|
405
512
|
|
406
513
|
#
|
407
|
-
#
|
514
|
+
# Verify that all returned instances from the API match our tag request
|
408
515
|
#
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
516
|
+
def verify_tagged_instances(servers,tags)
|
517
|
+
array_list = servers
|
518
|
+
# servers is an array of hashes
|
519
|
+
# verify that each object contains the tags.
|
520
|
+
if @match_all
|
521
|
+
# has to contain BOTH
|
522
|
+
matching_servers = array_list.select { |instance| (tags - instance['tags']).empty? }
|
523
|
+
|
524
|
+
else
|
525
|
+
# has to contain ANY
|
526
|
+
matching_servers = array_list.select { |instance| tags.any? {|tag| instance['tags'].include?(tag) }}
|
527
|
+
end
|
414
528
|
|
415
|
-
|
529
|
+
# Shall there be a discrepancy, we need to raise an error and end the run.
|
530
|
+
if matching_servers.size != servers.size
|
416
531
|
if @ignore_errors
|
417
|
-
Log.
|
532
|
+
Log.error "[#{Chimp.get_job_uuid}] #{servers.size - matching_servers.size} instances didnt match tag selection."
|
533
|
+
Log.error "[#{Chimp.get_job_uuid}] #{tags.join(" ")}"
|
534
|
+
Chimp.set_failure(true)
|
535
|
+
servers = []
|
418
536
|
else
|
419
|
-
|
537
|
+
raise "[#{Chimp.get_job_uuid}] #{servers.size - matching_servers.size} instances didnt match tag selection"
|
420
538
|
end
|
421
539
|
end
|
422
540
|
|
423
|
-
return
|
541
|
+
return servers
|
424
542
|
end
|
425
|
-
|
426
543
|
#
|
427
544
|
# Parse deployment names and get Server objects
|
428
545
|
#
|
429
|
-
# Returns: array of RestConnection::Server objects
|
430
|
-
#
|
431
546
|
def get_servers_by_deployment(names)
|
432
547
|
servers = []
|
548
|
+
all_instances = Connection.all_instances
|
433
549
|
|
434
|
-
|
435
|
-
|
436
|
-
d = ::Deployment.find_by_nickname(deployment).first
|
437
|
-
|
438
|
-
if d == nil
|
439
|
-
if @ignore_errors
|
440
|
-
Log.warn "cannot find deployment #{deployment}"
|
441
|
-
else
|
442
|
-
raise "cannot find deployment #{deployment}"
|
443
|
-
end
|
444
|
-
else
|
445
|
-
d.servers_no_reload.each do |s|
|
446
|
-
servers << s
|
447
|
-
end
|
448
|
-
end
|
449
|
-
end
|
450
|
-
end
|
550
|
+
result = all_instances.select {|i| names.any? {|n| i['links']['deployment']['name'] =~ /#{n}/ }}
|
551
|
+
servers = result
|
451
552
|
|
452
553
|
return(servers)
|
453
554
|
end
|
454
555
|
|
455
556
|
#
|
456
|
-
#
|
457
|
-
#
|
458
|
-
# Returns: array of RestConnection::Server objects
|
557
|
+
# Given some array names, return the arrays hrefs
|
558
|
+
# Api1.5
|
459
559
|
#
|
460
|
-
def
|
461
|
-
|
560
|
+
def get_hrefs_for_arrays(names)
|
561
|
+
result = []
|
562
|
+
arrays_hrefs = []
|
462
563
|
if names.size > 0
|
463
564
|
names.each do |array_name|
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
565
|
+
# Find if arrays exist, if not raise warning.
|
566
|
+
# One API call per array
|
567
|
+
Log.debug "Making API 1.5 call: client.server_arrays.index(:filter => [#{array_name}])"
|
568
|
+
result = Connection.client.server_arrays.index(:filter => ["name==#{array_name}"])
|
569
|
+
# Result is an array with all the server arrays
|
570
|
+
if result.size != 0
|
571
|
+
arrays_hrefs += result.collect(&:href)
|
572
|
+
else
|
573
|
+
if @ignore_errors
|
574
|
+
Log.debug "[#{Chimp.get_job_uuid}] Could not find array \"#{array_name}\""
|
575
|
+
else
|
576
|
+
Log.error "[#{Chimp.get_job_uuid}] Could not find array \"#{array_name}\""
|
469
577
|
end
|
470
578
|
end
|
471
579
|
end
|
472
|
-
|
580
|
+
if ( arrays_hrefs.empty? )
|
581
|
+
Log.debug "[#{Chimp.get_job_uuid}] Did not find any arrays that matched!" unless names.size == 1
|
582
|
+
end
|
583
|
+
|
584
|
+
return(arrays_hrefs)
|
473
585
|
|
474
|
-
|
586
|
+
end
|
475
587
|
end
|
476
588
|
|
477
589
|
#
|
478
|
-
#
|
479
|
-
#
|
480
|
-
# Returns: RestConnection::ServerTemplate
|
590
|
+
# Given a list of servers
|
481
591
|
#
|
482
|
-
def detect_server_template(
|
483
|
-
st = nil
|
592
|
+
def detect_server_template(servers)
|
484
593
|
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
if script and template == nil
|
490
|
-
Log.debug "Getting template URI..."
|
491
|
-
|
492
|
-
if not servers.empty?
|
493
|
-
for i in (0..servers.size - 1)
|
494
|
-
|
495
|
-
template = servers[i]['server_template_href'] if not servers[i].empty?
|
496
|
-
break if template
|
497
|
-
end
|
498
|
-
|
499
|
-
elsif not array_names_to_detect.empty?
|
500
|
-
array_names_to_detect.each do |array_name|
|
501
|
-
a = Ec2ServerArray.find_by(:nickname) { |n| n =~ /^#{array_name}/i }.first
|
502
|
-
next unless a
|
503
|
-
template = a['server_template_href']
|
504
|
-
break if template
|
505
|
-
end
|
506
|
-
end
|
507
|
-
|
508
|
-
raise "Unable to locate ServerTemplate!" unless template
|
509
|
-
Log.debug "Template: #{template}"
|
594
|
+
Log.debug "Looking for server template"
|
595
|
+
st = []
|
596
|
+
if servers[0].nil?
|
597
|
+
return (st)
|
510
598
|
end
|
511
599
|
|
600
|
+
st += servers.collect { |s|
|
601
|
+
[s['href'],s['server_template']]
|
602
|
+
}.uniq {|a| a[0]}
|
603
|
+
|
512
604
|
#
|
513
|
-
#
|
605
|
+
# We return an array of server_template resources
|
606
|
+
# of the type [ st_href, st object ]
|
514
607
|
#
|
515
|
-
|
516
|
-
Log.debug "Looking up template..."
|
517
|
-
|
518
|
-
if template =~ /^http/
|
519
|
-
st = ::ServerTemplate.find(template)
|
520
|
-
else
|
521
|
-
st = ::ServerTemplate.find_by_nickname(template).first
|
522
|
-
end
|
523
|
-
|
524
|
-
if st == nil
|
525
|
-
raise "No matching ServerTemplate found!"
|
526
|
-
else
|
527
|
-
Log.debug "ServerTemplate: \"#{st['nickname']}\""
|
528
|
-
end
|
529
|
-
end
|
608
|
+
Log.debug "Found server templates"
|
530
609
|
|
531
610
|
return(st)
|
532
611
|
end
|
533
612
|
|
534
613
|
#
|
535
|
-
#
|
536
|
-
#
|
537
|
-
# Returns: RestConnection::Executable
|
614
|
+
# This function returns @script_to_run which is extracted from matching
|
615
|
+
# the desired script against all server templates or the script URL
|
538
616
|
#
|
539
617
|
def detect_right_script(st, script)
|
618
|
+
Log.debug "Looking for rightscript"
|
540
619
|
executable = nil
|
620
|
+
# In the event that chimpd find @op_scripts as nil, set it as an array.
|
621
|
+
if @op_scripts.nil?
|
622
|
+
@op_scripts = []
|
623
|
+
end
|
624
|
+
if st.nil?
|
625
|
+
return executable
|
626
|
+
end
|
541
627
|
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
628
|
+
# Take the sts and extract all operational scripts
|
629
|
+
@op_scripts = extract_operational_scripts(st)
|
630
|
+
|
631
|
+
# if script is empty, we will list all common scripts
|
632
|
+
# if not empty, we will list the first matching one
|
633
|
+
if @script == "" and @script != nil
|
634
|
+
# list all operational scripts
|
635
|
+
reduce_to_common_scripts(st.size)
|
636
|
+
|
637
|
+
script_id = list_and_select_op_script
|
638
|
+
|
639
|
+
# Provide the name + href
|
640
|
+
s = Executable.new
|
641
|
+
s.params['right_script']['href'] = @op_scripts[script_id][1].right_script.href
|
642
|
+
s.params['right_script']['name'] = @op_scripts[script_id][0]
|
643
|
+
@script_to_run = s
|
644
|
+
else
|
645
|
+
# Try to find the rightscript in our list of common operational scripts
|
646
|
+
@op_scripts.each do |rb|
|
647
|
+
script_name = rb[0]
|
648
|
+
if script_name.downcase.include?(script.downcase)
|
649
|
+
# We only need the name and the href
|
650
|
+
s = Executable.new
|
651
|
+
s.params['right_script']['href'] = rb[1].right_script.href
|
652
|
+
s.params['right_script']['name'] = script_name
|
653
|
+
@script_to_run = s
|
654
|
+
|
655
|
+
Log.debug "Found rightscript"
|
656
|
+
return @script_to_run
|
564
657
|
end
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
658
|
+
end
|
659
|
+
#
|
660
|
+
# If we reach here it means we didnt find the script in the operationals
|
661
|
+
#
|
662
|
+
if @script_to_run == nil
|
663
|
+
# Search outside common op scripts
|
664
|
+
search_for_script_in_sts(script, st)
|
665
|
+
if @script_to_run.nil?
|
666
|
+
if @interactive
|
667
|
+
puts "ERROR: Sorry, didnt find that ( "+script+" ), provide an URI instead"
|
668
|
+
puts "I searched in:"
|
669
|
+
st.each { |s|
|
670
|
+
puts " * "+s[1]['name']+" [Rev"+s[1]['version'].to_s+"]"
|
671
|
+
}
|
672
|
+
if not @ignore_errors
|
673
|
+
exit 1
|
674
|
+
end
|
572
675
|
else
|
573
|
-
|
676
|
+
Log.error "["+self.job_uuid+"] Sorry, didnt find the script: ( "+script+" )!"
|
677
|
+
return nil
|
574
678
|
end
|
679
|
+
else
|
680
|
+
if self.job_uuid.nil?
|
681
|
+
self.job_uuid = ""
|
682
|
+
end
|
683
|
+
Log.warn "["+self.job_uuid+"]\"#{@script_to_run.params['right_script']['name']}\" is not a common operational script!"
|
684
|
+
return @script_to_run
|
575
685
|
end
|
576
|
-
# Provide the href as the input for the block that will do the lookup
|
577
|
-
script = op_script_hrefs[ op_script_id ]
|
578
686
|
end
|
579
687
|
end
|
688
|
+
end
|
580
689
|
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
script_data = {}
|
596
|
-
script_data[ 'name' ] = script_obj.params['name']
|
597
|
-
script = ::RightScript.new({ :href => script_URI, :right_script => script_data })
|
690
|
+
def search_for_script_in_sts(script, st)
|
691
|
+
# Loop and look inside every st
|
692
|
+
st.each do |s|
|
693
|
+
Log.debug "Making API 1.5 call: client.resource(#{s[1]['href']})"
|
694
|
+
temp=Connection.client.resource(s[1]['href'])
|
695
|
+
temp.runnable_bindings.index.each do |x|
|
696
|
+
# Look for first match
|
697
|
+
if x.raw['right_script']['name'].downcase.include?(script.downcase)
|
698
|
+
Log.debug "Found requested righscript: #{script}"
|
699
|
+
# Provide the name + href
|
700
|
+
s = Executable.new
|
701
|
+
s.params['right_script']['href'] = x.raw['links'].find{|i| i['rel'] == 'right_script'}['href']
|
702
|
+
s.params['right_script']['name'] = x.raw['right_script']['name']
|
703
|
+
@script_to_run = s
|
598
704
|
end
|
705
|
+
end
|
706
|
+
end
|
707
|
+
# If we hit here, we never found the desired script
|
708
|
+
return
|
709
|
+
end
|
710
|
+
|
711
|
+
#
|
712
|
+
# Presents the user with a list of scripts contained in @op_scripts
|
713
|
+
# and Returns an integer indicating the selection
|
714
|
+
#
|
715
|
+
#
|
716
|
+
def list_and_select_op_script
|
717
|
+
puts "List of available operational scripts:"
|
718
|
+
puts "------------------------------------------------------------"
|
719
|
+
for i in 0..@op_scripts.length - 1
|
720
|
+
puts " %3d. #{@op_scripts[i][0]}" % i
|
721
|
+
end
|
722
|
+
puts "------------------------------------------------------------"
|
723
|
+
while true
|
724
|
+
printf "Type the number of the script to run and press Enter (Ctrl-C to quit): "
|
725
|
+
script_id = Integer(gets.chomp) rescue -1
|
726
|
+
if script_id >= 0 && script_id < @op_scripts.length
|
727
|
+
puts "Script choice: #{script_id}. #{@op_scripts[ script_id ][0]}"
|
728
|
+
break
|
599
729
|
else
|
600
|
-
|
601
|
-
|
730
|
+
puts "#{script_id < 0 ? 'Invalid input' : 'Input out of range'}."
|
731
|
+
end
|
732
|
+
end
|
733
|
+
|
734
|
+
return script_id
|
735
|
+
end
|
736
|
+
|
737
|
+
#
|
738
|
+
# Takes the number of st's to search in,
|
739
|
+
# and reduces @op_scripts to only those who are
|
740
|
+
# repeated enough times.
|
741
|
+
#
|
742
|
+
def reduce_to_common_scripts(number_of_st)
|
743
|
+
counts = Hash.new 0
|
744
|
+
@op_scripts.each { |s| counts[s[0]] +=1 }
|
745
|
+
|
746
|
+
b = @op_scripts.inject({}) do |res, row|
|
747
|
+
res[row[0]] ||= []
|
748
|
+
res[row[0]] << row[1]
|
749
|
+
res
|
602
750
|
end
|
603
751
|
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
end
|
752
|
+
b.inject([]) do |res, (key, values)|
|
753
|
+
res << [key, values.first] if values.size >= number_of_st
|
754
|
+
@op_scripts = res
|
755
|
+
end
|
756
|
+
end
|
610
757
|
|
611
|
-
|
758
|
+
#
|
759
|
+
# Returns all matching operational scripts in the st list passed
|
760
|
+
#
|
761
|
+
def extract_operational_scripts(st)
|
762
|
+
op_scripts = []
|
763
|
+
size = st.size
|
764
|
+
st.each do |s|
|
765
|
+
# Example of s structure
|
766
|
+
# ["/api/server_templates/351930003",
|
767
|
+
# {"id"=>351930003,
|
768
|
+
# "name"=>"RightScale Right_Site - 2015q1",
|
769
|
+
# "kind"=>"cm#server_template",
|
770
|
+
# "version"=>5,
|
771
|
+
# "href"=>"/api/server_templates/351930003"} ]
|
772
|
+
Log.debug "Making API 1.5 call: client.resource"
|
773
|
+
temp=Connection.client.resource(s[1]['href'])
|
774
|
+
temp.runnable_bindings.index.each do |x|
|
775
|
+
# only add the operational ones
|
776
|
+
if x.sequence == "operational"
|
777
|
+
name = x.raw['right_script']['name']
|
778
|
+
op_scripts.push([name, x])
|
779
|
+
end
|
780
|
+
end
|
612
781
|
end
|
613
782
|
|
614
|
-
|
783
|
+
#We now only have operational runnable_bindings under the script_objects array
|
784
|
+
if op_scripts.length < 1
|
785
|
+
raise "ERROR: No operational scripts found on the server(s). "
|
786
|
+
st.each {|s|
|
787
|
+
puts " (Search performed on server template '#{s[1]['name']}')"
|
788
|
+
}
|
789
|
+
end
|
790
|
+
return op_scripts
|
615
791
|
end
|
616
792
|
|
617
793
|
#
|
618
794
|
# Load up the queue with work
|
619
795
|
#
|
620
|
-
|
621
|
-
#
|
622
|
-
def generate_jobs(queue_servers, queue_arrays, queue_template, queue_executable)
|
796
|
+
def generate_jobs(queue_servers, queue_template, queue_executable)
|
623
797
|
counter = 0
|
624
798
|
tasks = []
|
625
799
|
Log.debug "Loading queue..."
|
626
|
-
|
627
800
|
#
|
628
801
|
# Configure group
|
629
802
|
#
|
@@ -631,50 +804,13 @@ module Chimp
|
|
631
804
|
ChimpQueue.instance.create_group(@group, @group_type, @group_concurrency)
|
632
805
|
end
|
633
806
|
|
634
|
-
#
|
635
|
-
# Process ServerArray selection
|
636
|
-
#
|
637
|
-
Log.debug("processing queue selection")
|
638
|
-
if not queue_arrays.empty?
|
639
|
-
queue_arrays.each do |array|
|
640
|
-
instances = filter_out_non_operational_servers(array.instances)
|
641
|
-
|
642
|
-
if not instances
|
643
|
-
Log.error("no instances in array!")
|
644
|
-
break
|
645
|
-
end
|
646
|
-
|
647
|
-
instances.each do |array_instance|
|
648
|
-
#
|
649
|
-
# Handle limiting options
|
650
|
-
#
|
651
|
-
counter += 1
|
652
|
-
next if @limit_start.to_i > 0 and counter < @limit_start.to_i
|
653
|
-
break if @limit_end.to_i > 0 and counter > @limit_end.to_i
|
654
|
-
a = ExecArray.new(
|
655
|
-
:array => array,
|
656
|
-
:server => array_instance,
|
657
|
-
:exec => queue_executable,
|
658
|
-
:inputs => @inputs,
|
659
|
-
:template => queue_template,
|
660
|
-
:timeout => @timeout,
|
661
|
-
:verbose => @@verbose,
|
662
|
-
:quiet => @@quiet
|
663
|
-
)
|
664
|
-
a.dry_run = @dry_run
|
665
|
-
ChimpQueue.instance.push(@group, a)
|
666
|
-
end
|
667
|
-
end
|
668
|
-
end
|
669
|
-
|
670
807
|
#
|
671
808
|
# Process Server selection
|
672
809
|
#
|
673
810
|
Log.debug("Processing server selection")
|
674
811
|
|
675
|
-
queue_servers.sort! { |a,b| a['
|
812
|
+
queue_servers.sort! { |a,b| a['name'] <=> b['name'] }
|
676
813
|
queue_servers.each do |server|
|
677
|
-
|
678
814
|
#
|
679
815
|
# Handle limiting options
|
680
816
|
#
|
@@ -685,18 +821,49 @@ module Chimp
|
|
685
821
|
#
|
686
822
|
# Construct the Server object
|
687
823
|
#
|
688
|
-
s =
|
689
|
-
|
690
|
-
s.
|
691
|
-
|
692
|
-
s.
|
693
|
-
s.
|
824
|
+
s = Server.new
|
825
|
+
|
826
|
+
s.params['href'] = server['href']
|
827
|
+
|
828
|
+
s.params['current_instance_href'] = s.params['href']
|
829
|
+
s.params['current-instance-href'] = s.params['href']
|
830
|
+
|
831
|
+
s.params['name'] = server['name']
|
832
|
+
s.params['nickname'] = s.params['name']
|
833
|
+
|
834
|
+
s.params['ip_address'] = server['public_ip_addresses'].first
|
835
|
+
s.params['ip-address'] = s.params['ip_address']
|
836
|
+
|
837
|
+
s.params['private-ip-address'] = server['private_ip_addresses'].first
|
838
|
+
s.params['private_ip_address'] = s.params['private-ip-address']
|
839
|
+
|
840
|
+
s.params['resource_uid'] = server['resource_uid']
|
841
|
+
s.params['resource-uid'] = s.params['resource_uid']
|
842
|
+
|
843
|
+
s.params['instance-type'] = server['links']['instance_type']['name']
|
844
|
+
s.params['instance_type'] = s.params['instance-type']
|
845
|
+
s.params['ec2_instance_type'] = s.params['instance-type']
|
846
|
+
s.params['ec2-instance-type'] = s.params['instance-type']
|
847
|
+
|
848
|
+
s.params['dns-name'] = server['public_dns_names'].first
|
849
|
+
s.params['dns_name'] = s.params['dns-name']
|
850
|
+
|
851
|
+
s.params['locked'] = server['locked']
|
852
|
+
s.params['state'] = server['state']
|
853
|
+
s.params['datacenter'] = server['links']['datacenter']['name']
|
854
|
+
|
855
|
+
# This will be useful for later on when we need to run scripts
|
856
|
+
Log.debug "Making API 1.5 call: client.resource"
|
857
|
+
s.object = Connection.client.resource(server['href'])
|
858
|
+
|
694
859
|
e = nil
|
695
860
|
|
861
|
+
# If @script has been passed
|
696
862
|
if queue_executable
|
697
863
|
e = ExecRightScript.new(
|
698
864
|
:server => s,
|
699
865
|
:exec => queue_executable,
|
866
|
+
:job_uuid => @job_uuid,
|
700
867
|
:inputs => @inputs,
|
701
868
|
:timeout => @timeout,
|
702
869
|
:verbose => @@verbose,
|
@@ -710,16 +877,8 @@ module Chimp
|
|
710
877
|
:verbose => @@verbose,
|
711
878
|
:quiet => @@quiet
|
712
879
|
)
|
713
|
-
elsif queue_template and not clone
|
714
|
-
e = ExecSetTemplate.new(
|
715
|
-
:server => s,
|
716
|
-
:template => queue_template,
|
717
|
-
:verbose => @@verbose,
|
718
|
-
:quiet => @@quiet
|
719
|
-
)
|
720
880
|
elsif @report
|
721
881
|
if s.href
|
722
|
-
s.href = s.href.sub("/current","")
|
723
882
|
e = ExecReport.new(:server => s, :verbose => @@verbose, :quiet => @@quiet)
|
724
883
|
e.fields = @report
|
725
884
|
end
|
@@ -735,9 +894,7 @@ module Chimp
|
|
735
894
|
|
736
895
|
tasks.push(e)
|
737
896
|
end
|
738
|
-
|
739
897
|
end
|
740
|
-
|
741
898
|
return(tasks)
|
742
899
|
end
|
743
900
|
|
@@ -846,16 +1003,6 @@ module Chimp
|
|
846
1003
|
return get_results(@group)
|
847
1004
|
end
|
848
1005
|
|
849
|
-
#
|
850
|
-
# Filter out non-operational servers
|
851
|
-
# Then add operational servers to the list of objects to display
|
852
|
-
#
|
853
|
-
def filter_out_non_operational_servers(servers)
|
854
|
-
Log.debug "Filtering out non-operational servers..."
|
855
|
-
servers.reject! { |s| s == nil || s['state'] != "operational" }
|
856
|
-
return(servers)
|
857
|
-
end
|
858
|
-
|
859
1006
|
#
|
860
1007
|
# Do work: either by submitting to chimpd
|
861
1008
|
# or running it ourselves.
|
@@ -881,22 +1028,61 @@ module Chimp
|
|
881
1028
|
puts "chimp run complete"
|
882
1029
|
end
|
883
1030
|
|
1031
|
+
#
|
1032
|
+
# Allow the set/retrieval of job_uuid from outside
|
1033
|
+
#
|
1034
|
+
def self.get_job_uuid
|
1035
|
+
@job_uuid
|
1036
|
+
end
|
1037
|
+
|
1038
|
+
def self.set_job_uuid(value)
|
1039
|
+
@job_uuid = value
|
1040
|
+
end
|
1041
|
+
|
884
1042
|
#
|
885
1043
|
# Completely process a non-interactive chimp object command
|
1044
|
+
# This is used by chimpd, when processing a task.
|
886
1045
|
#
|
887
1046
|
def process
|
888
|
-
|
889
|
-
|
890
|
-
|
891
|
-
|
892
|
-
|
1047
|
+
Chimp.set_failure(false)
|
1048
|
+
Chimp.set_job_uuid(self.job_uuid)
|
1049
|
+
|
1050
|
+
Log.debug "[#{Chimp.get_job_uuid}] Processing task"
|
1051
|
+
|
1052
|
+
get_array_info unless Chimp.failure
|
1053
|
+
get_server_info unless Chimp.failure
|
1054
|
+
get_template_info unless Chimp.failure
|
1055
|
+
get_executable_info unless Chimp.failure
|
1056
|
+
|
1057
|
+
if Chimp.failure
|
1058
|
+
Log.error "##################################################"
|
1059
|
+
Log.error "["+self.job_uuid+"] API CALL FAILED FOR:"
|
1060
|
+
Log.error "["+self.job_uuid+"] chimp #{@cli_args} "
|
1061
|
+
Log.error "["+self.job_uuid+"] Run manually!"
|
1062
|
+
Log.error "##################################################"
|
1063
|
+
return []
|
1064
|
+
else
|
1065
|
+
if @servers.first.nil? or @executable.nil?
|
1066
|
+
Log.warn "["+self.job_uuid+"] Nothing to do for \"chimp #{@cli_args}\"."
|
1067
|
+
return []
|
1068
|
+
else
|
1069
|
+
return generate_jobs(@servers, @server_template, @executable)
|
1070
|
+
end
|
1071
|
+
end
|
893
1072
|
end
|
894
1073
|
|
895
1074
|
#
|
896
|
-
#
|
1075
|
+
# Asks for confirmation before continuing
|
897
1076
|
#
|
898
|
-
def
|
899
|
-
|
1077
|
+
def ask_confirmation(prompt = 'Continue?', default = false)
|
1078
|
+
a = ''
|
1079
|
+
s = default ? '[Y/n]' : '[y/N]'
|
1080
|
+
d = default ? 'y' : 'n'
|
1081
|
+
until %w[y n].include? a
|
1082
|
+
a = ask("#{prompt} #{s} ") { |q| q.limit = 1; q.case = :downcase }
|
1083
|
+
a = d if a.length == 0
|
1084
|
+
end
|
1085
|
+
a == 'y'
|
900
1086
|
end
|
901
1087
|
|
902
1088
|
#
|
@@ -967,13 +1153,7 @@ module Chimp
|
|
967
1153
|
STDOUT.sync = true
|
968
1154
|
STDERR.sync = true
|
969
1155
|
|
970
|
-
if @@verbose
|
971
|
-
Log.threshold = Logger::DEBUG
|
972
|
-
elsif @@quiet == true
|
973
|
-
Log.threshold = Logger::WARN
|
974
|
-
else
|
975
|
-
Log.threshold = Logger::INFO
|
976
|
-
end
|
1156
|
+
Log.threshold= Logger::DEBUG if @@verbose
|
977
1157
|
end
|
978
1158
|
|
979
1159
|
def self.verbose?
|
@@ -987,8 +1167,20 @@ module Chimp
|
|
987
1167
|
return 0
|
988
1168
|
end
|
989
1169
|
|
1170
|
+
def self.get_job_uuid
|
1171
|
+
@job_uuid
|
1172
|
+
end
|
1173
|
+
|
1174
|
+
def self.failure
|
1175
|
+
return @failure
|
1176
|
+
end
|
1177
|
+
|
1178
|
+
def self.set_failure(status)
|
1179
|
+
@failure = status
|
1180
|
+
end
|
1181
|
+
|
990
1182
|
####################################################
|
991
|
-
private
|
1183
|
+
#private
|
992
1184
|
####################################################
|
993
1185
|
|
994
1186
|
#
|
@@ -1105,7 +1297,6 @@ module Chimp
|
|
1105
1297
|
puts "Script OK. All the servers share the same template and the script is included in it."
|
1106
1298
|
end
|
1107
1299
|
end
|
1108
|
-
puts
|
1109
1300
|
end
|
1110
1301
|
|
1111
1302
|
#
|
@@ -1114,16 +1305,10 @@ module Chimp
|
|
1114
1305
|
def make_human_readable_list_of_objects
|
1115
1306
|
list_of_objects = []
|
1116
1307
|
|
1117
|
-
if @servers
|
1118
|
-
list_of_objects += @servers.map { |s| s['
|
1308
|
+
if @servers and not @servers.first.nil?
|
1309
|
+
list_of_objects += @servers.map { |s| s['name'] }
|
1119
1310
|
end
|
1120
1311
|
|
1121
|
-
if @arrays
|
1122
|
-
@arrays.each do |a|
|
1123
|
-
i = filter_out_non_operational_servers(a.instances)
|
1124
|
-
list_of_objects += i.map { |j| j['nickname'] }
|
1125
|
-
end
|
1126
|
-
end
|
1127
1312
|
return(list_of_objects)
|
1128
1313
|
end
|
1129
1314
|
|
@@ -1168,7 +1353,7 @@ module Chimp
|
|
1168
1353
|
puts " --progress toggle progress indicator"
|
1169
1354
|
puts " --noprompt don't prompt with list of objects to run against"
|
1170
1355
|
puts " --noverify disable interactive verification of errors"
|
1171
|
-
puts " --verbose
|
1356
|
+
puts " --verbose be more verbose"
|
1172
1357
|
puts " --dont-check-templates don't check for script even if servers have diff. templates"
|
1173
1358
|
puts " --quiet suppress non-essential output"
|
1174
1359
|
puts " --version display version and exit"
|
@@ -1180,13 +1365,9 @@ module Chimp
|
|
1180
1365
|
puts
|
1181
1366
|
puts "Misc Notes:"
|
1182
1367
|
puts " * If you leave the name of a --script or --ssh command blank, chimp will prompt you"
|
1183
|
-
puts " *
|
1184
|
-
puts " *
|
1185
|
-
puts " *
|
1186
|
-
puts " server_template_href, deployment_href, created_at, updated_at"
|
1187
|
-
puts
|
1368
|
+
puts " * URIs must be API URIs in the format https://us-3.rightscale.com/acct/<acct>/right_scripts/<script_id>"
|
1369
|
+
puts " * The following reporting keywords can be used: ip-address,name,href,private-ip-address,resource_uid,"
|
1370
|
+
puts " * ec2-instance-type,datacenter,dns-name,locked,tag=foo"
|
1188
1371
|
end
|
1189
|
-
|
1190
1372
|
end
|
1191
1373
|
end
|
1192
|
-
|