scout_agent 3.0.7 → 3.1.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.
- data/CHANGELOG +13 -0
- data/README +44 -2
- data/Rakefile +1 -1
- data/TODO +7 -1
- data/lib/scout_agent.rb +26 -1
- data/lib/scout_agent/agent/communication_agent.rb +75 -2
- data/lib/scout_agent/agent/master_agent.rb +132 -31
- data/lib/scout_agent/assignment/queue.rb +3 -0
- data/lib/scout_agent/assignment/snapshot.rb +4 -1
- data/lib/scout_agent/assignment/start.rb +3 -4
- data/lib/scout_agent/assignment/stop.rb +64 -30
- data/lib/scout_agent/id_card.rb +23 -12
- data/lib/scout_agent/lifeline.rb +129 -19
- data/lib/scout_agent/mission.rb +221 -68
- data/lib/scout_agent/order.rb +2 -2
- data/lib/scout_agent/plan.rb +38 -18
- metadata +2 -2
data/lib/scout_agent/mission.rb
CHANGED
@@ -2,20 +2,35 @@
|
|
2
2
|
# encoding: UTF-8
|
3
3
|
|
4
4
|
module ScoutAgent
|
5
|
+
#
|
6
|
+
# These objects are the data gathers periodically run by the agent. To gather
|
7
|
+
# a new type of data, you inherit from this Class and provide a build_report()
|
8
|
+
# method that calls some combination of report(), alert(), and error() to send
|
9
|
+
# data to the Scout server.
|
10
|
+
#
|
5
11
|
class Mission
|
6
|
-
|
7
|
-
|
12
|
+
#
|
13
|
+
# This method returns the last Mission subclass that was loaded. The
|
14
|
+
# Mission runner uses this after compiling Mission code to get the newly
|
15
|
+
# loaded object.
|
16
|
+
#
|
17
|
+
def self.prepared
|
18
|
+
@prepared ||= nil
|
8
19
|
end
|
9
|
-
|
10
|
-
|
11
|
-
|
20
|
+
|
21
|
+
#
|
22
|
+
# Hooks into Ruby's Class loading process so we can keep track of the last
|
23
|
+
# loaded subclass.
|
24
|
+
#
|
25
|
+
def self.inherited(new_mission)
|
26
|
+
@prepared = new_mission
|
12
27
|
end
|
13
28
|
|
14
29
|
#
|
15
|
-
# You can use this method to indicate one or more libraries your
|
30
|
+
# You can use this method to indicate one or more libraries your Mission
|
16
31
|
# requires:
|
17
32
|
#
|
18
|
-
# class MyNeedyPlugin <
|
33
|
+
# class MyNeedyPlugin < ScoutAgent::Mission
|
19
34
|
# needs "faster_csv", "elif"
|
20
35
|
# # ...
|
21
36
|
# end
|
@@ -25,13 +40,18 @@ module ScoutAgent
|
|
25
40
|
#
|
26
41
|
def self.needs(*libraries)
|
27
42
|
if libraries.empty?
|
28
|
-
@needs ||=
|
43
|
+
@needs ||= Array.new
|
29
44
|
else
|
30
45
|
needs.push(*libraries.flatten)
|
31
46
|
end
|
32
47
|
end
|
33
48
|
|
34
|
-
#
|
49
|
+
#
|
50
|
+
# Creates a new Scout Mission to run. It requires the +id+ and +name+ for
|
51
|
+
# the Mission as well as the +last_run+ Time, +memory+, and +options+ from
|
52
|
+
# the Mission database. You may optionally provide a +log+ for the Mission
|
53
|
+
# to write messages to.
|
54
|
+
#
|
35
55
|
def initialize(id, name, last_run, memory, options, log = WireTap.new(nil))
|
36
56
|
@id = id
|
37
57
|
@name = name
|
@@ -43,77 +63,145 @@ module ScoutAgent
|
|
43
63
|
@queue_db = !testing? && Database.load(:queue, @log)
|
44
64
|
end
|
45
65
|
|
66
|
+
#
|
67
|
+
# The numerical ID this Mission is known by to the Scout server. This is
|
68
|
+
# <tt>:testing</tt> for a Mission running in the agent's test mode.
|
69
|
+
#
|
70
|
+
attr_reader :id
|
71
|
+
# The human readable name of this Mission.
|
72
|
+
attr_reader :name
|
73
|
+
# The last run Time for this Mission. Can be +nil+ for new Missions.
|
46
74
|
attr_reader :last_run
|
75
|
+
# The log file this Mission is allowed to append messages to.
|
47
76
|
attr_reader :log
|
77
|
+
# A common alias used in Rails code.
|
48
78
|
alias_method :logger, :log
|
49
79
|
|
80
|
+
#
|
81
|
+
# Returns +true+ if this Mission is currently being run in the agent's test
|
82
|
+
# mode.
|
83
|
+
#
|
50
84
|
def testing?
|
51
85
|
@id == :testing
|
52
86
|
end
|
53
87
|
|
88
|
+
#
|
89
|
+
# Fetches an option passed to this Mission from the Web interface by +name+.
|
90
|
+
# If a direct lookup fails, it will be attempted again after converting a
|
91
|
+
# String +name+ to a Symbol or any other +name+ to a String. This makes the
|
92
|
+
# lookups feel familiar to HashWithIndifferentAccess fans.
|
93
|
+
#
|
54
94
|
def option(name)
|
55
95
|
@options[name] ||
|
56
96
|
@options[name.is_a?(String) ? name.to_sym : String(name)]
|
57
97
|
end
|
58
|
-
|
59
|
-
# Builds the data to send to the server.
|
98
|
+
|
60
99
|
#
|
61
|
-
#
|
100
|
+
# This method returns an Array of the reports that will be saved to the
|
101
|
+
# database at the end of this execution. It can be used to edit reports
|
102
|
+
# you have already entered.
|
62
103
|
#
|
63
|
-
|
104
|
+
def reports
|
105
|
+
data_for_server[:reports]
|
106
|
+
end
|
107
|
+
|
108
|
+
#
|
109
|
+
# This method adds a +new_report+ that will be saved to the database at the
|
110
|
+
# end of the current Mission run. The passed +new_report+ should be a Hash
|
111
|
+
# of the data values you wish to track.
|
64
112
|
#
|
65
|
-
|
66
|
-
|
67
|
-
|
113
|
+
def report(new_report)
|
114
|
+
reports << new_report
|
115
|
+
end
|
116
|
+
# Another way to add reports.
|
117
|
+
alias_method :add_report, :report
|
118
|
+
|
119
|
+
#
|
120
|
+
# This method returns an Array of the hints that will be saved to the
|
121
|
+
# database at the end of this execution. It can be used to edit hints you
|
122
|
+
# have already entered.
|
68
123
|
#
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
#
|
124
|
+
def hints
|
125
|
+
data_for_server[:hints]
|
126
|
+
end
|
127
|
+
|
128
|
+
#
|
129
|
+
# This method adds a +new_hint+ that will be saved to the database at the
|
130
|
+
# end of the current Mission run. The passed +new_hint+ should be a Hash of
|
131
|
+
# the data values you wish to track.
|
74
132
|
#
|
75
|
-
|
76
|
-
|
77
|
-
# error(:subject => "subject", :body => "body")
|
78
|
-
# add_error("subject", "body")
|
79
|
-
# add_error(:subject => "subject", :body => "body")
|
80
|
-
#
|
81
|
-
def data_for_server
|
82
|
-
@data_for_server ||= { :reports => [ ],
|
83
|
-
:hints => [ ],
|
84
|
-
:alerts => [ ],
|
85
|
-
:errors => [ ],
|
86
|
-
:memory => { } }
|
133
|
+
def hint(new_hint)
|
134
|
+
hints << new_hint
|
87
135
|
end
|
136
|
+
# Another way to add hints.
|
137
|
+
alias_method :add_hint, :hint
|
88
138
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
def #{kind}(new_entry)
|
97
|
-
#{kind}s << new_entry
|
98
|
-
end
|
99
|
-
else
|
100
|
-
def #{kind}(*fields)
|
101
|
-
#{kind}s << ( fields.first.is_a?(Hash) ?
|
102
|
-
fields.first :
|
103
|
-
{:subject => fields.first, :body => fields.last} )
|
104
|
-
end
|
105
|
-
end
|
106
|
-
alias_method :add_#{kind}, :#{kind}
|
107
|
-
END
|
139
|
+
#
|
140
|
+
# This method returns an Array of the alerts that will be saved to the
|
141
|
+
# database at the end of this execution. It can be used to edit alerts you
|
142
|
+
# have already entered.
|
143
|
+
#
|
144
|
+
def alerts
|
145
|
+
data_for_server[:alerts]
|
108
146
|
end
|
109
147
|
|
110
148
|
#
|
111
|
-
#
|
149
|
+
# :call-seq:
|
150
|
+
# alert(subject, body)
|
151
|
+
# alert(new_alert)
|
112
152
|
#
|
113
|
-
#
|
114
|
-
#
|
153
|
+
# This method adds an alert that will be saved to the database at the end of
|
154
|
+
# the current Mission run. You can pass the +subject+ and +body+ of the
|
155
|
+
# generated alert separately, or a Hash containing <tt>:subject</tt> and
|
156
|
+
# <tt>:body</tt> values for the +new_alert+.
|
157
|
+
#
|
158
|
+
def alert(*new_alert)
|
159
|
+
alerts << build_alert_or_error(new_alert)
|
160
|
+
end
|
161
|
+
# Another way to add alerts.
|
162
|
+
alias_method :add_alert, :alert
|
163
|
+
|
164
|
+
#
|
165
|
+
# This method returns an Array of the errors that will be saved to the
|
166
|
+
# database at the end of this execution. It can be used to edit errors you
|
167
|
+
# have already entered.
|
168
|
+
#
|
169
|
+
def errors
|
170
|
+
data_for_server[:errors]
|
171
|
+
end
|
172
|
+
|
173
|
+
#
|
174
|
+
# :call-seq:
|
175
|
+
# error(subject, body)
|
176
|
+
# error(new_error)
|
177
|
+
#
|
178
|
+
# This method adds an error that will be saved to the database at the end of
|
179
|
+
# the current Mission run. You can pass the +subject+ and +body+ of the
|
180
|
+
# generated error separately, or a Hash containing <tt>:subject</tt> and
|
181
|
+
# <tt>:body</tt> values for the +new_error+.
|
182
|
+
#
|
183
|
+
def error(*new_error)
|
184
|
+
errors << build_alert_or_error(new_error)
|
185
|
+
end
|
186
|
+
# Another way to add errors.
|
187
|
+
alias_method :add_error, :error
|
188
|
+
|
189
|
+
#
|
190
|
+
# :call-seq:
|
191
|
+
# memory(name)
|
192
|
+
# memory.delete(name)
|
115
193
|
# memory.clear
|
116
194
|
#
|
195
|
+
# The primary usage of this method is to fetch values saved into the Mission
|
196
|
+
# memory during a previous execution by +name+. The +name+ lookup rules are
|
197
|
+
# The same as those decribed in the option() method, so you can use String
|
198
|
+
# and Symbol keys interchangeably.
|
199
|
+
#
|
200
|
+
# When called without a +name+, this method returns a Hash of <b>the
|
201
|
+
# current</b> Mission memory (not the remembered values from the previous
|
202
|
+
# execution). This is mainly helpful if you want to edit the values you
|
203
|
+
# have added to be saved during this run.
|
204
|
+
#
|
117
205
|
def memory(name = nil)
|
118
206
|
if name.nil?
|
119
207
|
data_for_server[:memory]
|
@@ -124,20 +212,46 @@ module ScoutAgent
|
|
124
212
|
end
|
125
213
|
|
126
214
|
#
|
127
|
-
#
|
215
|
+
# :call-seq:
|
216
|
+
# remember(name, value)
|
217
|
+
# remember(name1, value1, name2, value2, ...)
|
218
|
+
# remember(name => value)
|
219
|
+
# remember(name1 => value1, name2 => value2, ...)
|
220
|
+
# remember(name1, value1, ..., name2 => value2, ...)
|
128
221
|
#
|
129
|
-
#
|
130
|
-
#
|
131
|
-
#
|
132
|
-
#
|
133
|
-
#
|
222
|
+
# Adds all +name+ and +value+ pairs into the Mission memory that will be
|
223
|
+
# saved from the current execution.
|
224
|
+
#
|
225
|
+
# It's important to note that Mission memory is not automatically copied
|
226
|
+
# from the previous executions, so you must call this method during each
|
227
|
+
# execution if you need to keep saving a value.
|
134
228
|
#
|
135
229
|
def remember(*args)
|
230
|
+
# divide Hash and non-Hash arguments
|
136
231
|
hashes, other = args.partition { |value| value.is_a? Hash }
|
137
|
-
|
138
|
-
|
232
|
+
# merge all Hash arguments into the Mission memory
|
233
|
+
hashes.each do |hash|
|
234
|
+
memory.merge!(hash)
|
235
|
+
end
|
236
|
+
# merge all non-Hash arguments into the Mission memory
|
237
|
+
(0...other.size).step(2) do |i|
|
238
|
+
memory.merge!(other[i] => other[i + 1])
|
239
|
+
end
|
139
240
|
end
|
140
241
|
|
242
|
+
#
|
243
|
+
# This method iterates over messages that have been queued from an external
|
244
|
+
# source for your Mission. Each message (the restored Ruby representation
|
245
|
+
# of the JSON data passed to the queue command) will be passed into your
|
246
|
+
# +block+ one by one. If your block accepts more than one argument, you
|
247
|
+
# will also be passed queue Times with the messages. The act of reading
|
248
|
+
# these messages removes them from the database so the current Mission
|
249
|
+
# should be used to pass them to the server as meaningful data or save them
|
250
|
+
# to Mission memory for later use.
|
251
|
+
#
|
252
|
+
# When a Mission is running in test mode, queued messages are read from
|
253
|
+
# <tt>options(:\_\_queue\_\_)</tt> and queue Times are simply <tt>Time.now</tt>.
|
254
|
+
#
|
141
255
|
def each_queued_message(&block)
|
142
256
|
if testing?
|
143
257
|
Array(option(:__queue__)).each do |message|
|
@@ -160,6 +274,10 @@ module ScoutAgent
|
|
160
274
|
end
|
161
275
|
|
162
276
|
#
|
277
|
+
# This method is the wrapper over Scout's older interface for Mission (then
|
278
|
+
# called a Plugin) execution. It hands off to the newer build_report(),
|
279
|
+
# then records the results of that run.
|
280
|
+
#
|
163
281
|
# Old plugins will work because they override this method. New plugins can
|
164
282
|
# now leave this method in place, add a build_report() method instead, and
|
165
283
|
# use the new helper methods to build up content inside which will
|
@@ -192,13 +310,15 @@ module ScoutAgent
|
|
192
310
|
write_reports_and_update_memory
|
193
311
|
end
|
194
312
|
|
313
|
+
#######
|
195
314
|
private
|
315
|
+
#######
|
196
316
|
|
197
317
|
#
|
198
|
-
# Returns true
|
199
|
-
# first attempt. If that fails, RubyGems is loaded and another attempt
|
200
|
-
# made. If the library cannot be loaded either way, an error() is
|
201
|
-
# and build_report() will not be called.
|
318
|
+
# Returns +true+ if a library can be loaded. A bare require() is made as
|
319
|
+
# the first attempt. If that fails, RubyGems is loaded and another attempt
|
320
|
+
# is made. If the library cannot be loaded either way, an error() is
|
321
|
+
# generated and build_report() will not be called.
|
202
322
|
#
|
203
323
|
def library_available?(library)
|
204
324
|
begin
|
@@ -214,7 +334,40 @@ module ScoutAgent
|
|
214
334
|
end
|
215
335
|
true
|
216
336
|
end
|
337
|
+
|
338
|
+
#
|
339
|
+
# Builds the data to send to the server. The public interface to this data
|
340
|
+
# lives in the helper methods: reports(), report(), add_report(), hints(),
|
341
|
+
# hint(), add_hint(), alerts(), alert(), add_alert(), errors(), error(), and
|
342
|
+
# add_error().
|
343
|
+
#
|
344
|
+
def data_for_server
|
345
|
+
@data_for_server ||= { :reports => Array.new,
|
346
|
+
:hints => Array.new,
|
347
|
+
:alerts => Array.new,
|
348
|
+
:errors => Array.new,
|
349
|
+
:memory => Hash.new }
|
350
|
+
end
|
217
351
|
|
352
|
+
#
|
353
|
+
# Builds a new alert or error Hash either by passing a Hash directly through
|
354
|
+
# or by grabbing a subject and body out of an Array of arguments.
|
355
|
+
#
|
356
|
+
def build_alert_or_error(new_alert_or_error)
|
357
|
+
if new_alert_or_error.first.is_a? Hash
|
358
|
+
new_alert_or_error
|
359
|
+
else
|
360
|
+
{:subject => new_alert_or_error.first, :body => new_alert_or_error.last}
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
364
|
+
#
|
365
|
+
# Writes generated reports and such to both the Mission database and the
|
366
|
+
# log(). The Mission memory will also be updated with the values stored
|
367
|
+
# from the execution that has just finished.
|
368
|
+
#
|
369
|
+
# Both database writes are skipped when running in the agent's test mode.
|
370
|
+
#
|
218
371
|
def write_reports_and_update_memory
|
219
372
|
%w[report hint alert error].each do |type|
|
220
373
|
Array(send("#{type}s")).each do |fields|
|
@@ -228,6 +381,6 @@ module ScoutAgent
|
|
228
381
|
end
|
229
382
|
end
|
230
383
|
|
231
|
-
# An
|
384
|
+
# An alias to support older plugins.
|
232
385
|
Plugin = Mission
|
233
386
|
end
|
data/lib/scout_agent/order.rb
CHANGED
@@ -32,8 +32,8 @@ module ScoutAgent
|
|
32
32
|
end
|
33
33
|
|
34
34
|
#
|
35
|
-
# Hooks into Ruby's
|
36
|
-
# are loaded.
|
35
|
+
# Hooks into Ruby's Class loading process so we can notice subclasses as
|
36
|
+
# they are loaded.
|
37
37
|
#
|
38
38
|
def self.inherited(order)
|
39
39
|
subclasses << order
|
data/lib/scout_agent/plan.rb
CHANGED
@@ -3,14 +3,17 @@
|
|
3
3
|
|
4
4
|
module ScoutAgent
|
5
5
|
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
6
|
+
# These extra methods are mixed into the OpenStruct stored in
|
7
|
+
# ScoutAgent::Plan. They add support for defaults, some interface
|
8
|
+
# improvements, support for configuration files and command-line switches,
|
9
|
+
# plus validation.
|
9
10
|
#
|
10
|
-
#
|
11
|
-
#
|
11
|
+
# To see the options stored in the plan and their current values, use the
|
12
|
+
# following command:
|
12
13
|
#
|
13
|
-
|
14
|
+
# scout_agent config
|
15
|
+
#
|
16
|
+
module Planned
|
14
17
|
################
|
15
18
|
### Defaults ###
|
16
19
|
################
|
@@ -39,31 +42,44 @@ module ScoutAgent
|
|
39
42
|
[:log_dir, nil] ]
|
40
43
|
end
|
41
44
|
|
42
|
-
#
|
45
|
+
#
|
46
|
+
# This method is used to set or reset the default configuration. It returns
|
47
|
+
# +self+ to support method chaining.
|
48
|
+
#
|
43
49
|
def set_defaults
|
44
50
|
defaults.each do |name, value|
|
45
51
|
send("#{name}=", value)
|
46
52
|
end
|
53
|
+
self # for chaining
|
47
54
|
end
|
48
55
|
alias_method :reset_defaults, :set_defaults
|
49
56
|
|
50
|
-
#
|
51
|
-
|
57
|
+
#
|
58
|
+
# --
|
59
|
+
#
|
60
|
+
# The following interface enhancements cannot use alias_method() because the
|
61
|
+
# underlying method are not defined in OpenStruct until their first use.
|
62
|
+
#
|
63
|
+
# ++
|
64
|
+
#
|
65
|
+
|
66
|
+
# Provide a more natural interface for ScoutAgent::Plan#run_as_daemon.
|
67
|
+
def run_as_daemon?
|
52
68
|
run_as_daemon
|
53
69
|
end
|
54
70
|
|
55
|
-
# Provide a more natural interface for
|
56
|
-
def test_mode?
|
71
|
+
# Provide a more natural interface for ScoutAgent::Plan#test_mode.
|
72
|
+
def test_mode?
|
57
73
|
test_mode
|
58
74
|
end
|
59
75
|
|
60
|
-
# Provide a more natural interface for
|
61
|
-
def enable_xmpp?
|
76
|
+
# Provide a more natural interface for ScoutAgent::Plan#enable_xmpp.
|
77
|
+
def enable_xmpp?
|
62
78
|
enable_xmpp
|
63
79
|
end
|
64
80
|
|
65
|
-
# Provide a more natural interface for
|
66
|
-
def periodic_snapshots?
|
81
|
+
# Provide a more natural interface for ScoutAgent::Plan#periodic_snapshots.
|
82
|
+
def periodic_snapshots?
|
67
83
|
periodic_snapshots
|
68
84
|
end
|
69
85
|
|
@@ -386,9 +402,13 @@ module ScoutAgent
|
|
386
402
|
end
|
387
403
|
end
|
388
404
|
|
405
|
+
#
|
406
|
+
# This constant holds all global configuration for the agent. There's only
|
407
|
+
# this one instance of this data and all systems share it. This data is
|
408
|
+
# configured at startup and should never again change without a restart.
|
389
409
|
#
|
390
|
-
#
|
391
|
-
#
|
410
|
+
# Users are free to add to this configuration as need (via their configuration
|
411
|
+
# file) and make use of those new values in their plugins.
|
392
412
|
#
|
393
|
-
Plan.set_defaults
|
413
|
+
Plan = OpenStruct.new.extend(Planned).set_defaults
|
394
414
|
end
|