oflow 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +20 -0
- data/README.md +182 -0
- data/lib/oflow/actor.rb +76 -0
- data/lib/oflow/actors/errorhandler.rb +32 -0
- data/lib/oflow/actors/ignore.rb +22 -0
- data/lib/oflow/actors/log.rb +175 -0
- data/lib/oflow/actors/relay.rb +23 -0
- data/lib/oflow/actors/timer.rb +126 -0
- data/lib/oflow/actors.rb +11 -0
- data/lib/oflow/box.rb +195 -0
- data/lib/oflow/env.rb +52 -0
- data/lib/oflow/errors.rb +74 -0
- data/lib/oflow/flow.rb +75 -0
- data/lib/oflow/haserrorhandler.rb +48 -0
- data/lib/oflow/haslinks.rb +64 -0
- data/lib/oflow/haslog.rb +72 -0
- data/lib/oflow/hasname.rb +31 -0
- data/lib/oflow/hastasks.rb +209 -0
- data/lib/oflow/inspector.rb +501 -0
- data/lib/oflow/link.rb +43 -0
- data/lib/oflow/pattern.rb +8 -0
- data/lib/oflow/stamp.rb +39 -0
- data/lib/oflow/task.rb +415 -0
- data/lib/oflow/test/action.rb +21 -0
- data/lib/oflow/test/actorwrap.rb +62 -0
- data/lib/oflow/test.rb +8 -0
- data/lib/oflow/tracker.rb +109 -0
- data/lib/oflow/version.rb +5 -0
- data/lib/oflow.rb +23 -0
- data/test/actors/log_test.rb +57 -0
- data/test/actors/timer_test.rb +56 -0
- data/test/actorwrap_test.rb +48 -0
- data/test/all_tests.rb +27 -0
- data/test/box_test.rb +127 -0
- data/test/collector.rb +23 -0
- data/test/flow_basic_test.rb +93 -0
- data/test/flow_cfg_error_test.rb +94 -0
- data/test/flow_log_test.rb +87 -0
- data/test/flow_nest_test.rb +215 -0
- data/test/flow_rescue_test.rb +133 -0
- data/test/flow_tracker_test.rb +82 -0
- data/test/stutter.rb +21 -0
- data/test/task_test.rb +98 -0
- data/test/tracker_test.rb +59 -0
- metadata +93 -0
@@ -0,0 +1,209 @@
|
|
1
|
+
|
2
|
+
module OFlow
|
3
|
+
|
4
|
+
# Provides the ability to have Tasks and Flows.
|
5
|
+
module HasTasks
|
6
|
+
|
7
|
+
# Initializes the tasks attribute.
|
8
|
+
def init_tasks()
|
9
|
+
@tasks = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
# Creates a Flow and yield to a block with the newly create Flow. Used to
|
13
|
+
# contruct Flows.
|
14
|
+
# @param name [Symbol|String] base name for the Flow
|
15
|
+
# @param options [Hash] optional parameters
|
16
|
+
# @param block [Proc] block to yield to with the new Flow instance
|
17
|
+
# @return [Flow] new Flow
|
18
|
+
def flow(name, options={}, &block)
|
19
|
+
f = Flow.new(self, name, options)
|
20
|
+
@tasks[f.name] = f
|
21
|
+
yield(f) if block_given?
|
22
|
+
f.resolve_all_links()
|
23
|
+
# Wait to validate until at the top so up-links don't fail validation.
|
24
|
+
f.validate() if Env == self
|
25
|
+
f
|
26
|
+
end
|
27
|
+
|
28
|
+
# Creates a Task and yield to a block with the newly create Task. Used to
|
29
|
+
# configure Tasks.
|
30
|
+
# @param name [Symbol|String] base name for the Task
|
31
|
+
# @param actor_class [Class] Class to create an Actor instance of
|
32
|
+
# @param options [Hash] optional parameters
|
33
|
+
# @param block [Proc] block to yield to with the new Task instance
|
34
|
+
# @return [Task] new Task
|
35
|
+
def task(name, actor_class, options={}, &block)
|
36
|
+
t = Task.new(self, name, actor_class, options)
|
37
|
+
@tasks[t.name] = t
|
38
|
+
yield(t) if block_given?
|
39
|
+
t
|
40
|
+
end
|
41
|
+
|
42
|
+
# Validates the container by verifying all links on a task have been set to
|
43
|
+
# a valid destination and that destination has been resolved.
|
44
|
+
# @raise [ValidateError] if there is an error in validation
|
45
|
+
def validate()
|
46
|
+
# collects errors and raises all errors at once if there are any
|
47
|
+
errors = _validation_errors()
|
48
|
+
raise ValidateError.new(errors) unless errors.empty?
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns an Array of validation errors.
|
52
|
+
def _validation_errors()
|
53
|
+
errors = []
|
54
|
+
@tasks.each_value { |t| errors += t._validation_errors() }
|
55
|
+
errors
|
56
|
+
end
|
57
|
+
|
58
|
+
# Resolves all the Links on all the Tasks and Flows being managed as well as
|
59
|
+
# any Links in the instance itself.
|
60
|
+
def resolve_all_links()
|
61
|
+
@links.each_value { |lnk|
|
62
|
+
set_link_target(lnk) if lnk.target.nil?
|
63
|
+
}
|
64
|
+
@tasks.each_value { |t|
|
65
|
+
t.resolve_all_links()
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
# Iterates over each Task and yields to the provided block with each Task.
|
70
|
+
# @param blk [Proc] Proc to call on each iteration
|
71
|
+
def each_task(&blk)
|
72
|
+
@tasks.each { |name,task| blk.yield(task) }
|
73
|
+
end
|
74
|
+
|
75
|
+
# Performs a recursive walk over all Tasks and yields to the provided block
|
76
|
+
# for each. Flows are followed recusively.
|
77
|
+
# @param tasks_only [Boolean] indicates on Tasks and not Flows are yielded to
|
78
|
+
# @param blk [Proc] Proc to call on each iteration
|
79
|
+
def walk_tasks(tasks_only=true, &blk)
|
80
|
+
@tasks.each_value do |t|
|
81
|
+
if t.is_a?(Task)
|
82
|
+
blk.yield(t)
|
83
|
+
else
|
84
|
+
blk.yield(t) unless tasks_only
|
85
|
+
t.walk_tasks(tasks_only, &blk)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Locates and return a Task with the specified name.
|
91
|
+
# @param name [String] name of the Task
|
92
|
+
# @return [Task|nil] the Task with the name specified or nil
|
93
|
+
def find_task(name)
|
94
|
+
name = name.to_sym unless name.nil?
|
95
|
+
return self if :flow == name
|
96
|
+
@tasks[name]
|
97
|
+
end
|
98
|
+
|
99
|
+
# Locates and return a Task with the specified full name.
|
100
|
+
# @param name [String] full name of the Task
|
101
|
+
# @return [Task|nil] the Task with the name specified or nil
|
102
|
+
def locate(name)
|
103
|
+
name = name[1..-1] if name.start_with?(':')
|
104
|
+
name = name[0..-2] if name.end_with?(':')
|
105
|
+
path = name.split(':')
|
106
|
+
_locate(path)
|
107
|
+
end
|
108
|
+
|
109
|
+
def _locate(path)
|
110
|
+
t = @tasks[path[0].to_sym]
|
111
|
+
return t if t.nil? || 1 == path.size
|
112
|
+
t._locate(path[1..-1])
|
113
|
+
end
|
114
|
+
|
115
|
+
# Returns the number of active Tasks.
|
116
|
+
def task_count()
|
117
|
+
@tasks.size
|
118
|
+
end
|
119
|
+
|
120
|
+
# Returns the sum of all the requests in all the Tasks's queues.
|
121
|
+
# @return [Fixnum] total number of items waiting to be processed
|
122
|
+
def queue_count()
|
123
|
+
cnt = 0
|
124
|
+
@tasks.each_value { |task| cnt += task.queue_count() }
|
125
|
+
cnt
|
126
|
+
end
|
127
|
+
|
128
|
+
# Returns true of one or more Tasks is either processing a request or has a
|
129
|
+
# request waiting to be processed on it's input queue.
|
130
|
+
# @return [true|false] the busy state across all Tasks
|
131
|
+
def busy?
|
132
|
+
@tasks.each_value { |task| return true if task.busy? }
|
133
|
+
false
|
134
|
+
end
|
135
|
+
|
136
|
+
# Calls the stop() method on all Tasks.
|
137
|
+
def stop()
|
138
|
+
@tasks.each_value { |task| task.stop() }
|
139
|
+
end
|
140
|
+
|
141
|
+
# Calls the step() method one Task that is stopped and has an item in the
|
142
|
+
# queue. The Tasks with the highest backed_up() value is selected.
|
143
|
+
def step()
|
144
|
+
max = 0.0
|
145
|
+
best = nil
|
146
|
+
walk_tasks() do |t|
|
147
|
+
if Task::STOPPED == t.state
|
148
|
+
bu = t.backed_up()
|
149
|
+
if max < bu
|
150
|
+
best = t
|
151
|
+
max = bu
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
best.step() unless best.nil?
|
156
|
+
best
|
157
|
+
end
|
158
|
+
|
159
|
+
# Calls the start() method on all Tasks.
|
160
|
+
def start()
|
161
|
+
@tasks.each_value { |task| task.start() }
|
162
|
+
end
|
163
|
+
|
164
|
+
# Wakes up all the Tasks in the Flow.
|
165
|
+
def wakeup()
|
166
|
+
@tasks.each_value { |t| t.wakeup() }
|
167
|
+
end
|
168
|
+
|
169
|
+
# Wakes up all the Tasks in the Flow and waits for the system to become idle
|
170
|
+
# before returning.
|
171
|
+
def flush()
|
172
|
+
wakeup()
|
173
|
+
@tasks.each_value { |t| t.flush() }
|
174
|
+
while busy?
|
175
|
+
sleep(0.2)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
# Sets the state of all Tasks recursively. This should not be called
|
180
|
+
# directly.
|
181
|
+
def state=(s)
|
182
|
+
@tasks.each_value do |task|
|
183
|
+
task.state = s
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
# Shuts down all Tasks.
|
188
|
+
# @param flush_first [Boolean] flag indicating shutdown should occur after the system becomes idle
|
189
|
+
def shutdown(flush_first=false)
|
190
|
+
# block all tasks first so threads can empty queues
|
191
|
+
@tasks.each_value do |task|
|
192
|
+
task.state = Task::BLOCKED
|
193
|
+
end
|
194
|
+
# shutdown and wait for queues to empty if necessary
|
195
|
+
@tasks.each_value do |task|
|
196
|
+
task.shutdown(flush_first)
|
197
|
+
end
|
198
|
+
@tasks = {}
|
199
|
+
end
|
200
|
+
|
201
|
+
# Clears out all Tasks and Flows and resets the object back to a empty state.
|
202
|
+
def clear()
|
203
|
+
shutdown()
|
204
|
+
@tasks = {}
|
205
|
+
_clear()
|
206
|
+
end
|
207
|
+
|
208
|
+
end # HasTasks
|
209
|
+
end # OFlow
|
@@ -0,0 +1,501 @@
|
|
1
|
+
|
2
|
+
require 'oterm'
|
3
|
+
|
4
|
+
module OFlow
|
5
|
+
|
6
|
+
class Inspector < ::OTerm::Executor
|
7
|
+
attr_reader :running
|
8
|
+
|
9
|
+
def initialize(port=6060)
|
10
|
+
super()
|
11
|
+
@running = true
|
12
|
+
|
13
|
+
register('busy', self, :busy, 'returns the busy state of the system.', nil)
|
14
|
+
register('list', self, :list, '[-r] [<id>] lists Flows and Tasks.',
|
15
|
+
%|Shows a list of Flow and Task full names that fall under the id if one is
|
16
|
+
provided, otherwise the top leve is assumed. If the -r option is specified then
|
17
|
+
the names of all the Flows and Tasks under the named item are displayed.|)
|
18
|
+
register('show', self, :show, '[-v] <id> displays a description of a Flow or Task.',
|
19
|
+
%|Shows a description of the identified Flow or Task. If the -v option is
|
20
|
+
specified then a detailed description of the Tasks is displayed which included
|
21
|
+
the number of requests queued and the status of the Task. More -v arguments
|
22
|
+
will give increasing more detail.|)
|
23
|
+
register('start', self, :start, '[<task id>] start or restart a Task.', nil)
|
24
|
+
register('step', self, :step, '[<task id>] step once.',
|
25
|
+
%|Step once for the Task specfified or once for some Task that is
|
26
|
+
waiting if no Task is identified.|)
|
27
|
+
register('stop', self, :stop, '[<task id>] stops a Task.', nil)
|
28
|
+
register('verbosity', self, :verbosity, '[<level>] show or set the verbosity or log level.', nil)
|
29
|
+
register('watch', self, :watch, '[<task id> displays status of Tasks.',
|
30
|
+
%|Displays the Task name, activity indicator, queued count, and number of
|
31
|
+
requests processed. If the terminal supports real time updates the displays
|
32
|
+
stays active until the X character is pressed. While running options are
|
33
|
+
available for sorting on name, activity, or queue size.|)
|
34
|
+
|
35
|
+
# register('debug', self, :debug, 'toggles debug mode.', nil)
|
36
|
+
|
37
|
+
@server = ::OTerm::Server.new(self, port, false)
|
38
|
+
end
|
39
|
+
|
40
|
+
def join()
|
41
|
+
@server.join()
|
42
|
+
end
|
43
|
+
|
44
|
+
def shutdown(listener, args)
|
45
|
+
super
|
46
|
+
@running = false
|
47
|
+
Env.shutdown()
|
48
|
+
end
|
49
|
+
|
50
|
+
def greeting()
|
51
|
+
"Welcome to the Operations Flow Inspector."
|
52
|
+
end
|
53
|
+
|
54
|
+
def debug(listener, args)
|
55
|
+
@server.debug = !@server.debug
|
56
|
+
end
|
57
|
+
|
58
|
+
def busy(listener, args)
|
59
|
+
if Env.busy?()
|
60
|
+
listener.out.pl("One or more Tasks is busy.")
|
61
|
+
else
|
62
|
+
listener.out.pl("All Tasks are idle.")
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def flows(listener, args)
|
67
|
+
Env.each_task() do |t|
|
68
|
+
listener.out.pl(t.full_name)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def list(listener, args)
|
73
|
+
if nil == args
|
74
|
+
Env.each_task() do |task|
|
75
|
+
listener.out.pl(task.full_name)
|
76
|
+
end
|
77
|
+
return
|
78
|
+
end
|
79
|
+
recurse, id, ok = _parse_opt_id_args(args, 'r', listener)
|
80
|
+
return unless ok
|
81
|
+
if id.nil?
|
82
|
+
flow = Env
|
83
|
+
else
|
84
|
+
flow = Env.locate(id)
|
85
|
+
end
|
86
|
+
if flow.nil?
|
87
|
+
listener.out.pl("--- No Flow or Task found for #{id}")
|
88
|
+
return
|
89
|
+
end
|
90
|
+
_walk(flow, recurse, listener) do |task|
|
91
|
+
listener.out.pl(task.full_name) unless Env == task
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def show(listener, args)
|
96
|
+
if nil == args
|
97
|
+
listener.out.pl("--- No Flow or Task specified")
|
98
|
+
return
|
99
|
+
end
|
100
|
+
detail, id, ok = _parse_opt_id_args(args, 'v', listener)
|
101
|
+
return unless ok
|
102
|
+
|
103
|
+
task = Env.locate(id)
|
104
|
+
if task.nil?
|
105
|
+
listener.out.pl("--- Failed to find '#{id}'")
|
106
|
+
return
|
107
|
+
end
|
108
|
+
listener.out.pl(task.describe(detail))
|
109
|
+
end
|
110
|
+
|
111
|
+
def start(listener, args)
|
112
|
+
if nil == args || 0 == args.size()
|
113
|
+
Env.start()
|
114
|
+
listener.out.pl("All Tasks restarted")
|
115
|
+
else
|
116
|
+
args.strip!
|
117
|
+
task = Env.locate(args)
|
118
|
+
if task.nil?
|
119
|
+
listener.out.pl("--- Failed to find '#{args}'")
|
120
|
+
else
|
121
|
+
task.start()
|
122
|
+
listener.out.pl("#{task.full_name} restarted")
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def step(listener, args)
|
128
|
+
lg = Env.log()
|
129
|
+
stop_after = false
|
130
|
+
if !lg.nil? && Task::STOPPED == lg.state
|
131
|
+
lg.start()
|
132
|
+
stop_after = true
|
133
|
+
end
|
134
|
+
|
135
|
+
if nil == args || 0 == args.size()
|
136
|
+
task = Env
|
137
|
+
else
|
138
|
+
args.strip!
|
139
|
+
task = Env.locate(args)
|
140
|
+
end
|
141
|
+
if task.nil?
|
142
|
+
listener.out.pl("--- Failed to find '#{args}'")
|
143
|
+
else
|
144
|
+
task = task.step()
|
145
|
+
if task.nil?
|
146
|
+
listener.out.pl("--- No tasks in '#{args}' are stopped or have have queued requests")
|
147
|
+
else
|
148
|
+
listener.out.pl("#{task.full_name} stepped")
|
149
|
+
end
|
150
|
+
end
|
151
|
+
lg.stop() if stop_after
|
152
|
+
end
|
153
|
+
|
154
|
+
def stop(listener, args)
|
155
|
+
if nil == args || 0 == args.size()
|
156
|
+
Env.stop()
|
157
|
+
listener.out.pl("All Tasks stopped(paused)")
|
158
|
+
else
|
159
|
+
args.strip!
|
160
|
+
task = Env.locate(args)
|
161
|
+
if task.nil?
|
162
|
+
listener.out.pl("--- Failed to find '#{args}'")
|
163
|
+
else
|
164
|
+
task.stop()
|
165
|
+
listener.out.pl("#{task.full_name} stopped(paused)")
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def verbosity(listener, args)
|
171
|
+
lg = Env.log
|
172
|
+
if lg.nil?
|
173
|
+
listener.out.pl("--- No logger")
|
174
|
+
return
|
175
|
+
end
|
176
|
+
lga = lg.actor
|
177
|
+
if nil != args && 0 < args.size()
|
178
|
+
args.strip!
|
179
|
+
lg.receive(:severity, Box.new(args))
|
180
|
+
listener.out.pl("verbosity change pending")
|
181
|
+
elsif lga.respond_to?(:severity)
|
182
|
+
listener.out.pl("verbosity: #{lga.severity()}")
|
183
|
+
else
|
184
|
+
listener.out.pl("--- Logger does support requests for verbosity level")
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def watch(listener, args)
|
189
|
+
tasks = []
|
190
|
+
if args.nil? || 0 == args.size()
|
191
|
+
Env.walk_tasks() { |t| tasks << t }
|
192
|
+
else
|
193
|
+
args.strip!
|
194
|
+
task = Env.locate(args)
|
195
|
+
if task.nil?
|
196
|
+
listener.out.pl("--- Failed to find '#{args}'")
|
197
|
+
return
|
198
|
+
elsif task.kind_of?(HasTasks)
|
199
|
+
task.walk_tasks() { |t| tasks << t }
|
200
|
+
else
|
201
|
+
tasks << task
|
202
|
+
end
|
203
|
+
end
|
204
|
+
if listener.out.is_vt100?
|
205
|
+
_dynamic_watch(listener, tasks)
|
206
|
+
else
|
207
|
+
max_len = 10
|
208
|
+
tasks.each do |t|
|
209
|
+
len = t.full_name.size
|
210
|
+
max_len = len if max_len < len
|
211
|
+
end
|
212
|
+
listener.out.pl(" %#{max_len}s %-11s %5s %9s" % ['Task Name', 'Q-cnt/max', 'busy?', 'processed'])
|
213
|
+
tasks.each do |t|
|
214
|
+
listener.out.pl(" %#{max_len}s %5d/%-5d %5s %9d" % [t.full_name, t.queue_count(), t.max_queue_count().to_i, t.busy?(), t.proc_count()])
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
# sort by values
|
220
|
+
BY_NAME = 'name'
|
221
|
+
BY_ACTIVITY = 'activity'
|
222
|
+
BY_QUEUE = 'queued'
|
223
|
+
BY_COUNT = 'count'
|
224
|
+
BY_STATE = 'state'
|
225
|
+
|
226
|
+
def _dynamic_watch(listener, tasks)
|
227
|
+
o = listener.out
|
228
|
+
sort_by = BY_NAME
|
229
|
+
rev = false
|
230
|
+
delay = 0.4
|
231
|
+
tasks.map! { |t| TaskStat.new(t) }
|
232
|
+
lines = tasks.size + 3
|
233
|
+
h, w = o.screen_size()
|
234
|
+
lines = h - 1 if lines > h - 1
|
235
|
+
o.clear_screen()
|
236
|
+
done = false
|
237
|
+
until done
|
238
|
+
tasks.each { |ts| ts.refresh() }
|
239
|
+
max = 6
|
240
|
+
max_n = 1
|
241
|
+
tasks.each do |ts|
|
242
|
+
max = ts.name.size if max < ts.name.size
|
243
|
+
max_n = ts.count.size if max_n < ts.count.size
|
244
|
+
end
|
245
|
+
# 5 for space between after, 3 for state, max_n for number
|
246
|
+
max_q = w - max - 8 - max_n
|
247
|
+
|
248
|
+
case sort_by
|
249
|
+
when BY_NAME
|
250
|
+
if rev
|
251
|
+
tasks.sort! { |a,b| b.name <=> a.name }
|
252
|
+
else
|
253
|
+
tasks.sort! { |a,b| a.name <=> b.name }
|
254
|
+
end
|
255
|
+
when BY_ACTIVITY
|
256
|
+
if rev
|
257
|
+
tasks.sort! { |a,b| a.activity <=> b.activity }
|
258
|
+
else
|
259
|
+
tasks.sort! { |a,b| b.activity <=> a.activity }
|
260
|
+
end
|
261
|
+
when BY_QUEUE
|
262
|
+
if rev
|
263
|
+
tasks.sort! { |a,b| a.queued <=> b.queued }
|
264
|
+
else
|
265
|
+
tasks.sort! { |a,b| b.queued <=> a.queued }
|
266
|
+
end
|
267
|
+
when BY_COUNT
|
268
|
+
if rev
|
269
|
+
tasks.sort! { |a,b| a.proc_cnt <=> b.proc_cnt }
|
270
|
+
else
|
271
|
+
tasks.sort! { |a,b| b.proc_cnt <=> a.proc_cnt }
|
272
|
+
end
|
273
|
+
when BY_STATE
|
274
|
+
if rev
|
275
|
+
tasks.sort! { |a,b| a.state <=> b.state }
|
276
|
+
else
|
277
|
+
tasks.sort! { |a,b| b.state <=> a.state }
|
278
|
+
end
|
279
|
+
end
|
280
|
+
o.set_cursor(1, 1)
|
281
|
+
o.bold()
|
282
|
+
o.underline()
|
283
|
+
o.p("%1$*2$s ? %3$*4$s @ Queued %5$*6$s" % ['#', -max_n, 'Task', -max, ' ', max_q])
|
284
|
+
o.attrs_off()
|
285
|
+
i = 2
|
286
|
+
tasks[0..lines].each do |ts|
|
287
|
+
o.set_cursor(i, 1)
|
288
|
+
o.p("%1$*2$s %5$c %3$*4$s " % [ts.count, max_n, ts.name, -max, ts.state])
|
289
|
+
o.set_cursor(i, max + max_n + 5)
|
290
|
+
case ts.activity
|
291
|
+
when 0
|
292
|
+
o.p(' ')
|
293
|
+
when 1
|
294
|
+
o.p('.')
|
295
|
+
when 2, 3
|
296
|
+
o.p('o')
|
297
|
+
else
|
298
|
+
o.p('O')
|
299
|
+
end
|
300
|
+
o.p(' ')
|
301
|
+
qlen = ts.queued
|
302
|
+
qlen = max_q if max_q < qlen
|
303
|
+
if 0 < qlen
|
304
|
+
o.reverse()
|
305
|
+
o.p("%1$*2$d" % [ts.queued, -qlen])
|
306
|
+
o.attrs_off()
|
307
|
+
end
|
308
|
+
o.clear_to_end()
|
309
|
+
i += 1
|
310
|
+
end
|
311
|
+
o.bold()
|
312
|
+
o.set_cursor(i, 1)
|
313
|
+
if rev
|
314
|
+
o.p("E) exit R) ")
|
315
|
+
o.reverse()
|
316
|
+
o.p("reverse")
|
317
|
+
o.attrs_off()
|
318
|
+
o.bold()
|
319
|
+
o.p(" +) faster -) slower [%0.1f]" % [delay])
|
320
|
+
else
|
321
|
+
o.p("E) exit R) reverse +) faster -) slower [%0.1f]" % [delay])
|
322
|
+
end
|
323
|
+
i += 1
|
324
|
+
o.set_cursor(i, 1)
|
325
|
+
o.p('sort by')
|
326
|
+
{ '#' => BY_COUNT, '?' => BY_STATE, 'N' => BY_NAME, 'A' => BY_ACTIVITY, 'Q' => BY_QUEUE }.each do |c,by|
|
327
|
+
if by == sort_by
|
328
|
+
o.p(" #{c}) ")
|
329
|
+
o.reverse()
|
330
|
+
o.p(by)
|
331
|
+
o.attrs_off()
|
332
|
+
o.bold()
|
333
|
+
else
|
334
|
+
o.p(" #{c}) #{by}")
|
335
|
+
end
|
336
|
+
end
|
337
|
+
o.attrs_off()
|
338
|
+
|
339
|
+
c = o.recv_wait(1, delay, /./)
|
340
|
+
unless c.nil?
|
341
|
+
case c[0]
|
342
|
+
when 'e', 'E'
|
343
|
+
done = true
|
344
|
+
when 'n', 'N'
|
345
|
+
sort_by = BY_NAME
|
346
|
+
rev = false
|
347
|
+
when 'a', 'A'
|
348
|
+
sort_by = BY_ACTIVITY
|
349
|
+
rev = false
|
350
|
+
when 'q', 'Q'
|
351
|
+
sort_by = BY_QUEUE
|
352
|
+
rev = false
|
353
|
+
when '#', 'c', 'C'
|
354
|
+
sort_by = BY_COUNT
|
355
|
+
rev = false
|
356
|
+
when '?', 's', 'S'
|
357
|
+
sort_by = BY_STATE
|
358
|
+
rev = false
|
359
|
+
when 'r', 'R'
|
360
|
+
rev = !rev
|
361
|
+
when '+'
|
362
|
+
delay /= 2.0 unless delay <= 0.1
|
363
|
+
when '-'
|
364
|
+
delay *= 2.0 unless 3.0 <= delay
|
365
|
+
end
|
366
|
+
end
|
367
|
+
end
|
368
|
+
o.pl()
|
369
|
+
end
|
370
|
+
|
371
|
+
def tab(cmd, listener)
|
372
|
+
start = cmd.index(' ')
|
373
|
+
if start.nil?
|
374
|
+
super
|
375
|
+
return
|
376
|
+
end
|
377
|
+
op = cmd[0...start]
|
378
|
+
start = cmd.rindex(' ')
|
379
|
+
pre = cmd[0...start]
|
380
|
+
last = cmd[start + 1..-1]
|
381
|
+
|
382
|
+
return if '-' == last[0]
|
383
|
+
|
384
|
+
# Tab completion is different depending on the command.
|
385
|
+
names = []
|
386
|
+
case op.downcase()
|
387
|
+
when 'verbosity'
|
388
|
+
names = ['fatal', 'error', 'warn', 'info', 'debug'].select { |s| s.start_with?(last.downcase()) }
|
389
|
+
else # expect id or options
|
390
|
+
with_colon = ':' == last[0]
|
391
|
+
Env.walk_tasks(false) do |t|
|
392
|
+
fn = t.full_name
|
393
|
+
fn = fn[1..-1] unless with_colon
|
394
|
+
names << fn if fn.start_with?(last)
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
return if 0 == names.size
|
399
|
+
if 1 == names.size
|
400
|
+
listener.move_col(1000)
|
401
|
+
listener.insert(names[0][last.size..-1])
|
402
|
+
listener.out.prompt()
|
403
|
+
listener.out.p(listener.buf)
|
404
|
+
else
|
405
|
+
listener.out.pl()
|
406
|
+
names.each do |name|
|
407
|
+
listener.out.pl("#{pre} #{name}")
|
408
|
+
end
|
409
|
+
best = best_completion(last, names)
|
410
|
+
if best == last
|
411
|
+
listener.update_cmd(0)
|
412
|
+
else
|
413
|
+
listener.move_col(1000)
|
414
|
+
listener.insert(best[last.size..-1])
|
415
|
+
listener.out.prompt()
|
416
|
+
listener.out.p(listener.buf)
|
417
|
+
end
|
418
|
+
end
|
419
|
+
end
|
420
|
+
|
421
|
+
def _parse_opt_id_args(args, opt, listener)
|
422
|
+
opt_cnt = 0
|
423
|
+
id = nil
|
424
|
+
args.strip!
|
425
|
+
args.split(' ').each do |a|
|
426
|
+
if '-' == a[0]
|
427
|
+
a[1..-1].each_char do |c|
|
428
|
+
if c == opt
|
429
|
+
opt_cnt += 1
|
430
|
+
else
|
431
|
+
listener.out.pl("--- -#{c} is not a valid option")
|
432
|
+
return [0, nil, false]
|
433
|
+
end
|
434
|
+
end
|
435
|
+
elsif !id.nil?
|
436
|
+
listener.out.pl("--- Multiple Ids specified")
|
437
|
+
return [0, nil, false]
|
438
|
+
else
|
439
|
+
id = a
|
440
|
+
end
|
441
|
+
end
|
442
|
+
[opt_cnt, id, true]
|
443
|
+
end
|
444
|
+
|
445
|
+
def _walk(flow, recurse, listener, &block)
|
446
|
+
if flow.respond_to?(:each_task)
|
447
|
+
if recurse
|
448
|
+
block.yield(flow)
|
449
|
+
flow.each_task() do |task|
|
450
|
+
_walk(task, true, listener, &block)
|
451
|
+
end
|
452
|
+
else
|
453
|
+
flow.each_task() do |task|
|
454
|
+
block.yield(task)
|
455
|
+
end
|
456
|
+
end
|
457
|
+
else
|
458
|
+
block.yield(flow)
|
459
|
+
end
|
460
|
+
end
|
461
|
+
|
462
|
+
class TaskStat
|
463
|
+
attr_reader :task
|
464
|
+
attr_reader :queued
|
465
|
+
attr_reader :activity
|
466
|
+
attr_reader :name
|
467
|
+
attr_reader :proc_cnt
|
468
|
+
attr_reader :count
|
469
|
+
attr_reader :state
|
470
|
+
|
471
|
+
STATE_MAP = {
|
472
|
+
Task::STARTING => '^',
|
473
|
+
Task::STOPPED => '*',
|
474
|
+
Task::RUNNING => ' ',
|
475
|
+
Task::CLOSING => 'X',
|
476
|
+
Task::BLOCKED => '-',
|
477
|
+
Task::STEP => 's',
|
478
|
+
}
|
479
|
+
def initialize(t)
|
480
|
+
@task = t
|
481
|
+
@proc_cnt = t.proc_count()
|
482
|
+
@activity = 0
|
483
|
+
@queued = t.queue_count()
|
484
|
+
@name = t.full_name
|
485
|
+
@count = @proc_cnt.to_s
|
486
|
+
@state = STATE_MAP.fetch(t.state, '?')
|
487
|
+
end
|
488
|
+
|
489
|
+
def refresh()
|
490
|
+
cnt = @task.proc_count()
|
491
|
+
@activity = cnt - @proc_cnt
|
492
|
+
@proc_cnt = cnt
|
493
|
+
@queued = @task.queue_count()
|
494
|
+
@count = cnt.to_s
|
495
|
+
@state = STATE_MAP.fetch(@task.state, '?')
|
496
|
+
end
|
497
|
+
|
498
|
+
end # TaskStat
|
499
|
+
|
500
|
+
end # Inspector
|
501
|
+
end # OFlow
|