taskr 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,22 @@
1
+ === 0.4.0 :: 2008-12-23
2
+
3
+ * Log entries in task view can now be filtered by date. By default only entries
4
+ from the last 24 hours are shown.
5
+ * Added ability to reload and edit existing tasks [dvdplm]
6
+ * Added experimental 'dont_wait' parameter to the Taskr4Rails action that
7
+ forces the remote code to be forked so that it does not hold up the entire
8
+ server. Currently this will only work if the remote Rails server is running
9
+ on Mongrel.
10
+ * Taskr can be configured to send SNMP traps whenever task events are logged.
11
+ See the config.example.yml file's "LOGGING" section for more info.
12
+ * Task actions and parameters are now again included in the XML views for
13
+ the task list and for individual tasks.
14
+ * Restr 0.5.0 is now required, due to a bug in Restr 0.4.0 that caused
15
+ some actions to log their activities incorrectly.
16
+ * Picnic 0.7.0 is now required, due to changes in environment configuration.
17
+ * Fixed dependency loading problems introduced by upstream changes in RubyGems
18
+ 1.3.1.
19
+
1
20
  === 0.3.0 :: 2008-06-19
2
21
 
3
22
  * Added "Run Now" function allowing a task to be immediately triggered.
@@ -38,4 +57,4 @@
38
57
 
39
58
  === 0.1.0 :: 2007-12-21
40
59
 
41
- * First public release.
60
+ * First public release.
data/Rakefile CHANGED
@@ -11,8 +11,8 @@ require 'hoe'
11
11
  include FileUtils
12
12
  require File.join(File.dirname(__FILE__), 'lib', 'taskr', 'version')
13
13
 
14
- AUTHOR = "Matt Zukowski" # can also be an array of Authors
15
- EMAIL = "matt at roughest dot net"
14
+ AUTHOR = ["Matt Zukowski", "David Palm"] # can also be an array of Authors
15
+ EMAIL = ["matt at roughest dot net", "dvd plm on googles free email service"]
16
16
  DESCRIPTION = "cron-like scheduler service with a RESTful interface"
17
17
  GEM_NAME = "taskr" # what ppl will type to install your gem
18
18
  RUBYFORGE_PROJECT = "taskr" # The unix name for your project
@@ -21,7 +21,7 @@ HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
21
21
 
22
22
  NAME = "taskr"
23
23
  REV = nil
24
- #REV = `svn info`[/Revision: (\d+)/, 1] rescue nil
24
+ #REV = YAML.load(`svn info`)['Revision']
25
25
  VERS = ENV['VERSION'] || (Taskr::VERSION::STRING + (REV ? ".#{REV}" : ""))
26
26
  CLEAN.include ['**/.*.sw?', '*.gem', '.config']
27
27
  RDOC_OPTS = ['--quiet', '--title', "taskr documentation",
@@ -54,10 +54,33 @@ hoe = Hoe.new(GEM_NAME, VERS) do |p|
54
54
  #p.spec_extras - A hash of extra values to set in the gemspec.
55
55
 
56
56
  p.extra_deps = [
57
- ['picnic', '~> 0.6.4'],
57
+ ['picnic', '~> 0.7.0'],
58
58
  ['reststop', '~> 0.3.0'],
59
- ['restr', '~> 0.4.0'],
59
+ ['restr', '~> 0.5.0'],
60
60
  ['rufus-scheduler', '~> 1.0.7']
61
61
  ]
62
62
  p.spec_extras = {:executables => ['taskr', 'taskr-ctl']}
63
63
  end
64
+
65
+ desc "Generate gemspec"
66
+ task :gemspec do |x|
67
+ # Check the manifest before generating the gemspec
68
+ manifest = %x[rake check_manifest]
69
+ manifest.gsub!(/\(in .{1,}\)\n/, "")
70
+
71
+ unless manifest.empty?
72
+ print "\n", "#"*68, "\n"
73
+ print <<-EOS
74
+ Manifest.txt is not up-to-date. Please review the changes below.
75
+ If the changes are correct, run 'rake check_manifest | patch'
76
+ and then run this command again.
77
+ EOS
78
+ print "#"*68, "\n\n"
79
+ puts manifest
80
+ else
81
+ gemspec = %x[rake debug_gem]
82
+ gemspec.gsub!(/\(in .{1,}\)\n/, "")
83
+ File.open("#{GEM_NAME}.gemspec",'w'){|f| f<<gemspec}
84
+ %x[rake debug_gem > #{GEM_NAME}.gemspec]
85
+ end
86
+ end
data/bin/taskr CHANGED
File without changes
File without changes
@@ -139,6 +139,19 @@ task_log:
139
139
  # ERROR, WARN, INFO, or DEBUG
140
140
  level: DEBUG
141
141
 
142
+ ### SNMP Traps
143
+ # Taskr can be configured to send out SNMP traps whenever task events occur.
144
+ # That is, in addition to logging task events to the Task Log (see above),
145
+ # Taskr can send out SNMP traps notifying an SNMP listener of the event.
146
+ # This functionality relies on the Net-SNMP package's `snmptrap` command-line
147
+ # utility, so make sure you have the PERL Net-SNMP installed on your system
148
+ # before enabling the following configuration options.
149
+
150
+ #snmp:
151
+ # send_traps: true
152
+ # to_host: snmp.example.net
153
+ # community: public
154
+ # enterprise_oid: 1.3.6.1.4.1.55555.7007
142
155
 
143
156
  ##### MISC #####################################################################
144
157
 
@@ -58,13 +58,13 @@ module Taskr
58
58
 
59
59
  def trigger(trigger_args = {})
60
60
  begin
61
- $LOG.info("Executing task #{self.task.name}")
61
+ $LOG.info("Executing task #{self.task.name.inspect}")
62
62
  execute
63
63
  task.update_attribute(:last_triggered, Time.now)
64
64
  task.update_attribute(:last_triggered_error, nil)
65
65
  rescue => e
66
66
  puts
67
- $LOG.error(e)
67
+ $LOG.error("Error while executing task #{self.task.name.inspect}! The error was: #{e} (see Taskr log for debugging details)")
68
68
  $LOG.debug(e.backtrace.join($/))
69
69
  details = e.message
70
70
  details += "\n\n#{$LAST_ERROR_BODY}" if $LAST_ERROR_BODY # dumb way of reading Restr errors... Restr needs to be fixed
@@ -95,7 +95,7 @@ module Taskr
95
95
 
96
96
  def trigger(trigger_args = {})
97
97
  begin
98
- $LOG.info("Executing task #{self.task.name}")
98
+ $LOG.info("Executing task #{self.task.name.inspect}")
99
99
  actions.each do |a|
100
100
  a.execute
101
101
  LogEntry.info(a, "Action #{a} executed.")
@@ -104,7 +104,7 @@ module Taskr
104
104
  task.update_attribute(:last_triggered, Time.now)
105
105
  task.update_attribute(:last_triggered_error, nil)
106
106
  rescue => e
107
- $LOG.error(e)
107
+ $LOG.error("Error while executing task #{self.task.name.inspect}! The error was: #{e} (see Taskr log for debugging details)")
108
108
  $LOG.debug("#{e.backtrace}")
109
109
  task.update_attribute(:last_triggered, Time.now)
110
110
  task.update_attribute(:last_triggered_error, {:type => e.class.to_s, :details => "#{e.message}"})
@@ -268,8 +268,12 @@ module Taskr
268
268
  parameters['params'] = params2
269
269
  end
270
270
 
271
- Restr.logger = LogEntry.logger_for_action(task_action)
272
- Restr.do(parameters['method'], parameters['url'], (parameters['params'] unless parameters['params'].blank?), auth)
271
+ options = {:logger => LogEntry.logger_for_action(task_action)}
272
+ options.merge!(auth) if auth
273
+ Restr.do(parameters['method'],
274
+ parameters['url'],
275
+ (parameters['params'] unless parameters['params'].blank?),
276
+ options)
273
277
  end
274
278
  end
275
279
 
@@ -296,19 +300,21 @@ module Taskr
296
300
  end
297
301
 
298
302
  class Taskr4rails < Base
299
- self.parameters = ['url', 'auth', 'ruby_code']#, 'shell_command']
303
+ self.parameters = ['url', 'auth', 'ruby_code', 'dont_wait']#, 'shell_command']
300
304
  self.description = "Executes code on a remote Rails server via the taskr4rails plugin."
301
305
 
302
306
  def execute
303
307
  data = {
308
+ :task_name => task.name,
309
+ :task_id => task.id,
304
310
  :auth => parameters['auth'],
311
+ :dont_wait => parameters['dont_wait'],
305
312
  :ruby_code => parameters['ruby_code']#,
306
313
  #:shell_command => parameters['shell_command']
307
314
  }
308
-
309
-
310
- Restr.logger = LogEntry.logger_for_action(task_action)
311
- Restr.post(parameters['url'], data)
315
+
316
+ options = {:logger => LogEntry.logger_for_action(task_action)}
317
+ Restr.post(parameters['url'], data, options)
312
318
  end
313
319
  end
314
320
  end
@@ -66,9 +66,117 @@ module Taskr::Controllers
66
66
  render :view_task
67
67
  end
68
68
 
69
+ def edit(task_id)
70
+ @task = Task.find(task_id, :include => [:task_actions])
71
+ @actions = Taskr::Actions.list
72
+ render :edit_task
73
+ end
74
+
75
+ def update(task_id)
76
+ $LOG.debug "Update Input params: #{@input.inspect}"
77
+ @task = Task.find(task_id, :include => [:task_actions])
78
+ params = normalize_input(@input)
79
+ @task.attributes= {
80
+ :name => params[:name],
81
+ :schedule_method => params[:schedule_method],
82
+ :schedule_when => params[:schedule_when],
83
+ :memo => params[:memo]
84
+ }
85
+
86
+ @task.task_actions.each do |action|
87
+ $LOG.debug("Updating parameters for #{action.inspect}")
88
+ action_params = params[:action].delete("action_id_#{action.id}")
89
+ $LOG.debug("Using values #{action_params.inspect}")
90
+ next unless action_params
91
+ action_params.each do |param_name, value|
92
+ $LOG.debug("Looking up \"#{param_name}\". Setting value to \"#{value}\"")
93
+ action.parameters.find_by_name(param_name).update_attribute(:value, value)
94
+ end
95
+ end
96
+
97
+ # Create new actions/action_parameters for the remaining params
98
+ unless params[:action].empty?
99
+ params[:action].map do |num, params|
100
+ $LOG.debug "Looping remaining action_parameters: #{params.inspect} (num: #{num})"
101
+ action_class = get_action_class(params[:action_class_name])
102
+ action = TaskAction.new(:order => params[:order] || (@task.task_actions.maximum(:order)+1) || num, :action_class_name => action_class.to_s)
103
+
104
+ action_class.parameters.each do |p|
105
+ value = params[p]
106
+ value = nil if value.blank?
107
+ action.action_parameters << TaskActionParameter.new(:name => p, :value => value)
108
+ end
109
+ @task.task_actions << action
110
+ end
111
+ end
112
+
113
+ unless @task.valid?
114
+ @status = 500
115
+ @actions = Taskr::Actions.list
116
+ else
117
+ @task.save!
118
+ @status = 200
119
+ end
120
+
121
+ @task.reload # Ensure any updates to the record goes in
122
+
123
+ Taskr.scheduler.unschedule(@task.scheduler_job_id)
124
+ @task.schedule! Taskr.scheduler
125
+
126
+ $LOG.info "Task \"#{@task.name}\" (ID: #{@task.id}) updated sucessfully."
127
+ redirect R(@task)
128
+ end
129
+
130
+ def normalize_input(hsh)
131
+ hsh[:task] || hsh["0"] || hsh
132
+ end
133
+
134
+ def normalize_actions_params(input_params)
135
+ $LOG.debug "normalize_actions_params Normalizing: #{input_params.inspect}"
136
+ # some gymnastics here to provide compatibility for the way various
137
+ # REST client libraries submit data
138
+ actions_data = input_params[:actions] || input_params[:action]
139
+
140
+ raise ArgumentError, "Missing action(s) parameter." if actions_data.blank?
141
+ actions = case actions_data
142
+ when Array
143
+ $LOG.debug "normalize_actions_params Plain Array. Returning as-is."
144
+ actions_data
145
+ when Hash
146
+ $LOG.debug "normalize_actions_params Some weird Hash. Injecting."
147
+ actions_data.inject([]) do |acc,(i,a)|
148
+ $LOG.debug "normalize_actions_params acc: #{acc.inspect} index: #{i.inspect} array: #{a.inspect}."
149
+ acc << a
150
+ acc
151
+ end
152
+ else
153
+ $LOG.debug "normalize_actions_params Not a weird hash.\n\tactions_data[:action]: #{actions_data[:action].inspect}\n\tactions_data[:actions]: #{actions_data[:actions].inspect}\n\tactions_data: #{actions_data.inspect}\n\n"
154
+ actions_data[:action] || actions_data[:actions] || actions_data
155
+ end
156
+ actions = [actions] unless actions.kind_of? Array
157
+ $LOG.debug "normalize_actions_params DONE. Returning: #{actions.inspect}"
158
+ actions
159
+ end
160
+
161
+ def get_action_class(class_name)
162
+ action_class_name = "Taskr::Actions::#{class_name}" unless class_name =~ /^Taskr::Actions::/
163
+
164
+ begin
165
+ action_class = action_class_name.constantize
166
+ unless action_class.include? Rufus::Schedulable
167
+ raise ArgumentError,
168
+ "#{a[:action_class_name].inspect} cannot be used as an action because it does not include the Rufus::Schedulable module."
169
+ end
170
+ rescue NameError
171
+ raise ArgumentError,
172
+ "#{a[:action_class_name].inspect} is not defined (i.e. there is no such action class)."
173
+ end
174
+ action_class
175
+ end
176
+
69
177
  # Create and schedule a new task.
70
178
  def create
71
- puts @input.inspect
179
+ $LOG.debug @input.inspect
72
180
  begin
73
181
  # the "0" is for compatibility with PHP's Zend_Rest_Client
74
182
  task_data = @input[:task] || @input["0"] || @input
@@ -125,7 +233,7 @@ module Taskr::Controllers
125
233
  end
126
234
 
127
235
  action = TaskAction.new(:order => a[:order] || i, :action_class_name => action_class_name)
128
-
236
+ $LOG.debug "Action should be initialized and ready for creation: #{action.inspect}"
129
237
 
130
238
  action_class.parameters.each do |p|
131
239
  value = a[p]
@@ -147,7 +255,7 @@ module Taskr::Controllers
147
255
 
148
256
  @task.schedule! Taskr.scheduler
149
257
 
150
- if @task.save
258
+ if @task.save!
151
259
  location = "/tasks/#{@task.id}?format=#{@format}"
152
260
  $LOG.debug "#{@task} saved successfuly. Setting Location header to #{location.inspect}."
153
261
  @headers['Location'] = location
@@ -174,7 +282,7 @@ module Taskr::Controllers
174
282
  # ok to catch exception silently. it should have gotten logged by the action
175
283
  end
176
284
 
177
- render :view_task
285
+ redirect R(@task)
178
286
  end
179
287
 
180
288
  # Unschedule and delete an existing task.
@@ -197,12 +305,31 @@ module Taskr::Controllers
197
305
  _error("Task #{id} was not destroyed.", 500)
198
306
  end
199
307
  end
308
+
309
+ # Reload a task
310
+ def reload(id)
311
+ @task = Task.find(id)
312
+ $LOG.debug "Re-scheduling task #{@task}..."
313
+ if @task.scheduler_job_id
314
+ Taskr.scheduler.unschedule(@task.scheduler_job_id)
315
+ $LOG.debug "\t...unscheduled task #{@task}..."
316
+ end
317
+ @task.schedule! Taskr.scheduler
318
+ $LOG.debug "\t...scheduled task #{@task}...\n"
319
+ redirect R(@task)
320
+ end
200
321
  end
201
322
 
202
323
  class LogEntries < REST 'log_entries'
203
324
  def list
325
+ @since = @input[:since]
326
+
327
+ @level = ['DEBUG', 'INFO', 'WARN', 'ERROR']
328
+ @level.index(@input[:level]).times {@level.shift} if @input[:level]
329
+
204
330
  @log_entries = LogEntry.find(:all,
205
- :conditions => ['task_id = ?', @input[:task_id]],
331
+ :conditions => ['task_id = ? AND IF(?,timestamp > ?,1) AND level IN (?)',
332
+ @input[:task_id], !@since.blank?, @since, @level],
206
333
  :order => 'timestamp DESC, id DESC')
207
334
 
208
335
  render :log_entries_list
@@ -15,34 +15,39 @@
15
15
 
16
16
  $: << File.dirname(File.expand_path(__FILE__))
17
17
 
18
- # Try to load local versions of Picnic and Reststop if possible...
19
- $: << File.dirname(File.expand_path(__FILE__))+"/../../../picnic/lib"
20
- $: << File.dirname(File.expand_path(__FILE__))+"/../../vendor/picnic/lib"
21
- $: << File.dirname(File.expand_path(__FILE__))+"/../../../reststop/lib"
22
- $: << File.dirname(File.expand_path(__FILE__))+"/../../vendor/reststop/lib"
23
-
24
- # active_resource needs newer versions of active_support, but this conflicts
25
- # with active_record, so we need a newer version of that as well (yes, it's a mess)
26
- #$: << File.dirname(File.expand_path(__FILE__))+"/../../vendor/activeresource/lib"
27
- #$: << File.dirname(File.expand_path(__FILE__))+"/../../vendor/activesupport/lib"
28
- #$: << File.dirname(File.expand_path(__FILE__))+"/../../vendor/activerecord/lib"
18
+ # Try to load local version of Picnic if possible (for development purposes)
19
+ alt_picnic_paths = [
20
+ File.dirname(File.expand_path(__FILE__))+"/../../../picnic/lib",
21
+ File.dirname(File.expand_path(__FILE__))+"/../../vendor/picnic/lib"
22
+ ]
23
+ # Try to load local version of Reststop if possible (for development purposes)
24
+ alt_reststop_paths = [
25
+ File.dirname(File.expand_path(__FILE__))+"/../../../reststop/lib",
26
+ File.dirname(File.expand_path(__FILE__))+"/../../vendor/reststop/lib"
27
+ ]
29
28
 
30
29
  require 'rubygems'
31
30
 
32
- require 'active_support'
33
- #require 'active_resource'
34
- require 'active_record'
35
-
36
-
37
31
  # make things backwards-compatible for rubygems < 0.9.0
38
- unless Object.method_defined? :gem
32
+ if Object.method_defined?(:require_gem)
39
33
  alias gem require_gem
40
34
  end
41
35
 
36
+ if alt_picnic_paths.any?{|path| File.exists? "#{path}/picnic.rb" }
37
+ alt_picnic_paths.each{|path| $: << path}
38
+ end
42
39
  require 'picnic'
43
- require 'camping/db'
40
+
41
+ if alt_reststop_paths.any?{|path| File.exists? "#{path}/reststop.rb" }
42
+ alt_reststop_paths.each{|path| $: << path}
43
+ end
44
44
 
45
45
  require 'reststop'
46
46
 
47
- gem 'rufus-scheduler', '~> 1.0.7'
47
+ require 'active_record'
48
+ require 'active_support'
49
+
50
+ require 'camping/db'
51
+
52
+ gem 'rufus-scheduler', '>=1.0.7'
48
53
  require 'rufus/scheduler'
@@ -39,7 +39,7 @@ module Taskr::Models
39
39
  validates_presence_of :task_actions
40
40
  validates_associated :task_actions
41
41
 
42
- def schedule!(scheduler)
42
+ def schedule!(scheduler = Taskr.scheduler)
43
43
  case schedule_method
44
44
  when 'cron'
45
45
  method = :schedule
@@ -62,11 +62,20 @@ module Taskr::Models
62
62
 
63
63
  $LOG.debug "Scheduling task #{name.inspect}: #{self.inspect}"
64
64
 
65
- if task_actions.length > 0
66
- action = prepare_action
65
+ if self.new_record? # Need to distinguish between the edit/create cases. "Edit" needs to reload the task_actions or nothing works; "Create" needs NOT to relaod the actions, or the validations kick in and nothing works. FIXME!!!!!
66
+ if task_actions.length > 0
67
+ action = prepare_action
68
+ else
69
+ $LOG.warn "Task #{name.inspect} has no actions and as a result will not be scheduled!"
70
+ return false
71
+ end
67
72
  else
68
- $LOG.warn "Task #{name.inspect} has no actions and as a result will not be scheduled!"
69
- return false
73
+ if task_actions(true).length > 0
74
+ action = prepare_action
75
+ else
76
+ $LOG.warn "Task #{name.inspect} has no actions and as a result will not be scheduled!"
77
+ return false
78
+ end
70
79
  end
71
80
 
72
81
  job_id = scheduler.send(method, t || schedule_when, :schedulable => action)
@@ -149,6 +158,7 @@ module Taskr::Models
149
158
  :class_name => 'TaskActionParameter',
150
159
  :foreign_key => :task_action_id,
151
160
  :dependent => :destroy
161
+ alias_method :parameters, :action_parameters
152
162
 
153
163
  has_many :log_entries
154
164
  has_one :last_log_entry,
@@ -170,6 +180,10 @@ module Taskr::Models
170
180
  self[:action_class_name].constantize
171
181
  end
172
182
 
183
+ def description
184
+ action_class.description
185
+ end
186
+
173
187
  def to_xml(options = {})
174
188
  options[:indent] ||= 2
175
189
  xml = options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent])
@@ -186,7 +200,7 @@ module Taskr::Models
186
200
  end
187
201
 
188
202
  def to_s
189
- "#{self.class.name.demodulize}(#{task_action})"
203
+ "#{self.class.name.demodulize}(#{action_class})"
190
204
  end
191
205
  end
192
206
 
@@ -202,7 +216,7 @@ module Taskr::Models
202
216
  xml.tag!('id', {:type => 'integer'}, id)
203
217
  xml.tag!('name', name)
204
218
  xml.tag!('value') do
205
- xml.cdata!(value)
219
+ xml.cdata!(value.to_s)
206
220
  end
207
221
  end
208
222
  end
@@ -247,6 +261,11 @@ module Taskr::Models
247
261
  :data => data
248
262
  )
249
263
  end
264
+
265
+ # Send SNMP traps through net-snmp if Taskr is configured to send them.
266
+ if Taskr::Conf[:snmp] && Taskr::Conf[:snmp][:send_traps]
267
+ send_snmp_trap(level, task, action, data)
268
+ end
250
269
  end
251
270
 
252
271
  # Produces a Logger-like class that will create log entries for the given
@@ -261,6 +280,57 @@ module Taskr::Models
261
280
  log(level, action, data)
262
281
  end
263
282
  end
283
+
284
+ def send_snmp_trap(level, task, action, data)
285
+ snmp_community = Taskr::Conf[:snmp][:community]
286
+ enterprise_oid = Taskr::Conf[:snmp][:enterprise_oid] || '1.3.6.1.4.1.55555.7007'
287
+ to_host = Taskr::Conf[:snmp][:to_host]
288
+ snmp_persistent_dir = Taskr::Conf[:snmp][:snmp_persistent_dir] || '/tmp'
289
+ my_host = ENV['HOSTNAME'] || `hostname`.strip || 'taskr'
290
+
291
+
292
+ task_oid = '1.3.6.1.4.1.55555.7007.1'
293
+ task_typ = 's'
294
+ task_val = task.to_s.gsub(/"/, '\"')
295
+
296
+
297
+ # see http://www.oid-info.com/get/1.3.6.1.4.1.9.5.1.14.4.1.2
298
+ level_oid = '1.3.6.1.4.1.9.5.1.14.4.1.2'
299
+ level_typ = 'i'
300
+ case level
301
+ when 'DEBUG' then level_val = 8
302
+ when 'INFO' then level_val = 7
303
+ when 'WARN' then level_val = 5
304
+ when 'ERROR' then level_val = 4
305
+ end
306
+
307
+ # see http://www.oid-info.com/get/1.3.6.1.4.1.9.9.41.1.2.3.1.4
308
+ sevr_oid = '1.3.6.1.4.1.9.9.41.1.2.3.1.4'
309
+ sevr_typ = 's'
310
+ sevr_val = level
311
+
312
+ # TODO: make msg format configurable
313
+ msg = ("Taskr #{level} on task #{task}[#{action}]: #{data}").gsub(/"/, '\"')
314
+
315
+ # see http://www.oid-info.com/get/1.3.6.1.4.1.9.9.41.1.2.3.1.5
316
+ msg_oid = '1.3.6.1.4.1.9.9.41.1.2.3.1.5'
317
+ msg_typ = 's'
318
+ msg_val = msg
319
+
320
+ cmd = %{snmptrap -v 1 -c #{snmp_community} #{to_host} #{enterprise_oid} #{my_host} 6 #{level_val} '' \
321
+ #{level_oid} #{level_typ} "#{level_val}" \
322
+ #{sevr_oid} #{sevr_typ} "#{sevr_val}" \
323
+ #{task_oid} #{task_typ} "#{task_val}" \
324
+ #{msg_oid} #{msg_typ} "#{msg_val}"}
325
+
326
+ # Band-aid fix for bug in Net-SNMP.
327
+ # See http://sourceforge.net/tracker/index.php?func=detail&aid=1588455&group_id=12694&atid=112694
328
+ ENV['SNMP_PERSISTENT_DIR'] ||= snmp_persistent_dir
329
+
330
+ $LOG.debug "SENDING SNMP TRAP: #{cmd}"
331
+
332
+ `#{cmd}`
333
+ end
264
334
  end
265
335
 
266
336
  # Exposes a Logger-like interface for logging entries for some particular
@@ -1,7 +1,7 @@
1
1
  module Taskr #:nodoc:
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
- MINOR = 3
4
+ MINOR = 4
5
5
  TINY = 0
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
@@ -24,11 +24,11 @@ module Taskr::Views
24
24
  CONTENT_TYPE = 'text/xml'
25
25
 
26
26
  def tasks_list
27
- @tasks.to_xml(:root => 'tasks')#, :include => [:task_actions])
27
+ @tasks.to_xml(:root => 'tasks', :include => [:task_actions])
28
28
  end
29
29
 
30
30
  def view_task
31
- @task.to_xml(:root => 'task')#, :include => [:task_actions])
31
+ @task.to_xml(:root => 'task', :include => [:task_actions])
32
32
  end
33
33
  end
34
34
 
@@ -52,6 +52,7 @@ module Taskr::Views
52
52
  th "Job ID"
53
53
  th "Created On"
54
54
  th "Created By"
55
+ th ""
55
56
  end
56
57
  end
57
58
  tbody do
@@ -73,6 +74,7 @@ module Taskr::Views
73
74
  td(:class => "job-id") {t.scheduler_job_id}
74
75
  td t.created_on
75
76
  td t.created_by
77
+ td {a(:href => R(t, 'edit')) {"Edit"}}
76
78
  end
77
79
  end
78
80
  end
@@ -83,6 +85,96 @@ module Taskr::Views
83
85
  end
84
86
  end
85
87
 
88
+ def edit_task
89
+ html_scaffold do
90
+ script(:type => 'text/javascript') do
91
+ %{
92
+ function show_action_parameters(num) {
93
+ new Ajax.Updater('parameters_'+num, '#{R(Actions)}', {
94
+ method: 'get',
95
+ parameters: {
96
+ id: $F('action_class_name_'+num),
97
+ action: 'parameters_form',
98
+ num: num
99
+ }
100
+ });
101
+ }
102
+ }
103
+ end
104
+
105
+ a(:href => R(Tasks, :list)) {"Back to Task List"}
106
+
107
+ form :method => 'put', :action => R(@task, 'update') do
108
+ h1 "Edit Task \"#{@task.name}\""
109
+
110
+ p do
111
+ label 'name'
112
+ br
113
+ input :type => 'text', :name => 'name', :value => @task.name, :size => 40
114
+ end
115
+
116
+ p do
117
+ label 'schedule'
118
+ br
119
+ select(:name => 'schedule_method') do
120
+ ['every','at','in','cron'].each do |method|
121
+ if @task.schedule_method == method
122
+ option(:value => method, :selected => 'selected') {method}
123
+ else
124
+ option(:value => method) {method}
125
+ end
126
+ end
127
+ end
128
+ input :type => 'text', :name => 'schedule_when', :value => @task.schedule_when, :size => 30
129
+ end
130
+
131
+ p do
132
+ label 'description/memo'
133
+ br
134
+ textarea(:name => 'memo', :cols => '60', :rows => '4'){@task.memo}
135
+ end
136
+
137
+ p do
138
+ label "Actions:"
139
+ @task.task_actions.each do |action|
140
+ div {action_parameters_form(action)}
141
+ end
142
+ # div do
143
+ # if @task.task_actions.length > 1
144
+ # ol(:style => 'padding-left: 20px') do
145
+ # @task.task_actions.each do |ta|
146
+ # html_task_action_li(ta)
147
+ # end
148
+ # end
149
+ # else
150
+ # html_task_action_li(@task.task_actions.first)
151
+ # end
152
+ # end
153
+ end
154
+
155
+
156
+ p do
157
+ a(:id => 'add_action', :href => '#'){'Add another action'}
158
+ end
159
+
160
+ script(:type => 'text/javascript') do
161
+ %{
162
+ Event.observe('add_action', 'click', function() {
163
+ new Ajax.Updater('add_action', '#{R(Actions, :new)}', {
164
+ method: 'get',
165
+ parameters: { num: $$('select.action_class_name').size() },
166
+ insertion: Insertion.Before
167
+ });
168
+ return false;
169
+ })
170
+ }
171
+ end
172
+
173
+ button(:type => 'submit') {"Save"}
174
+ end
175
+ end
176
+ end
177
+
86
178
  def new_task
87
179
  html_scaffold do
88
180
  script(:type => 'text/javascript') do
@@ -158,6 +250,9 @@ module Taskr::Views
158
250
  form(:method => 'put', :style => 'display: inline', :action => R(@task, 'run')) do
159
251
  button(:type => 'submit', :value => 'run') {"Run Now!"}
160
252
  end
253
+ form(:method => 'put', :style => 'display: inline', :action => R(@task, 'reload')) do
254
+ button(:type => 'submit', :value => 'reload') {"Reload!"}
255
+ end
161
256
  br
162
257
  a(:href => R(Tasks, :list)) {"Back to Task List"}
163
258
 
@@ -222,7 +317,40 @@ module Taskr::Views
222
317
  end
223
318
  end
224
319
 
225
- iframe(:src => R(LogEntries, :list, :task_id => @task.id), :style => 'width: 100%; margin-top: 20px;')
320
+ script %{
321
+ function clickbold(el) {
322
+ $$('#logfilter a').each(function(a){a.style.fontWeight = 'normal'})
323
+ el.style.fontWeight = 'bold'
324
+ }
325
+ }
326
+
327
+ p(:style => "margin-top: 20px; border-top: 1px dotted black; padding-top: 10px", :id => 'logfilter') do
328
+ strong "Show: "
329
+ a(:href => R(LogEntries, :list, :task_id => @task.id, :since => (Time.now - 1.day).to_formatted_s(:db)),
330
+ :target => 'log', :onclick => "clickbold(this)", :style => 'font-weight: bold') {"Last 24 Hours"}
331
+ text "|"
332
+ a(:href => R(LogEntries, :list, :task_id => @task.id, :since => (Time.now - 2.days).to_formatted_s(:db)),
333
+ :target => 'log', :onclick => "clickbold(this)") {"48 Hours"}
334
+ text "|"
335
+ a(:href => R(LogEntries, :list, :task_id => @task.id),
336
+ :target => 'log', :onclick => "clickbold(this)") {"All"}
337
+ br
338
+
339
+ strong "Level: "
340
+ a(:href => R(LogEntries, :list, :task_id => @task.id, :level => 'DEBUG'),
341
+ :target => 'log', :onclick => "clickbold(this)") {"DEBUG"}
342
+ text "|"
343
+ a(:href => R(LogEntries, :list, :task_id => @task.id, :level => 'INFO'),
344
+ :target => 'log', :onclick => "clickbold(this)") {"INFO"}
345
+ text "|"
346
+ a(:href => R(LogEntries, :list, :task_id => @task.id, :level => 'WARN'),
347
+ :target => 'log', :onclick => "clickbold(this)") {"WARN"}
348
+ text "|"
349
+ a(:href => R(LogEntries, :list, :task_id => @task.id, :level => 'ERROR'),
350
+ :target => 'log', :onclick => "clickbold(this)") {"ERROR"}
351
+ end
352
+ iframe(:src => R(LogEntries, :list, :task_id => @task.id, :since => (Time.now - 1.day).to_formatted_s(:db)),
353
+ :style => 'width: 100%; height: 300px', :name => 'log')
226
354
  end
227
355
  end
228
356
 
@@ -248,16 +376,20 @@ module Taskr::Views
248
376
  end
249
377
  end
250
378
 
251
- def action_parameters_form
379
+ def action_parameters_form(action = @action)
252
380
  @num ||= 0
253
381
 
254
- p {em @action.description}
382
+ p {em action.description}
255
383
 
256
- @action.parameters.each do |param|
384
+ action.parameters.each do |param|
257
385
  p do
258
386
  label param
259
387
  br
260
- input :type => 'text', :name => "action[#{@num}][#{param}]", :size => 50
388
+ unless action.is_a?(Taskr::Models::TaskAction) # Setting up a new task
389
+ input :type => 'text', :name => "action[#{@num}][#{param}]", :size => 50
390
+ else # Editing an existing task
391
+ input :type => 'text', :name => "action[action_id_#{action.id}][#{param.name}]", :size => 50, :value => param.value
392
+ end
261
393
  end
262
394
  end
263
395
  end
@@ -286,6 +418,12 @@ module Taskr::Views
286
418
 
287
419
  def log_entries_list
288
420
  h2 "Log"
421
+
422
+ p(:style => 'margin: 0px') do
423
+ em(:style => "font-weight: normal; font-size: 9pt"){"Entries since #{@since}<br />"} unless @since.blank?
424
+ em(:style => "font-weight: normal; font-size: 9pt"){"With levels #{@level.join(", ")}<br />"} unless @level.blank?
425
+ end
426
+
289
427
  table do
290
428
  @log_entries.each do |entry|
291
429
  case entry.level.downcase.intern
@@ -37,22 +37,40 @@ class Taskr4railsController < ActionController::Base
37
37
 
38
38
  io = StringIO.new
39
39
  prev_stdout, prev_stderr = $stdout, $stderr
40
- output = ""
41
40
  $stdout = io
42
41
  $stderr = io
42
+ err = false # start off assuming there's no error
43
43
  begin
44
- eval(params[:ruby_code]) if params[:ruby_code]
45
- #output << (`cd #{RAILS_ROOT}; #{params[:shell_command]}` || "") if params[:shell_command]
46
- err = false
44
+ if !params[:dont_wait].blank? && params[:dont_wait] != 0 && params[:dont_wait] != '0'
45
+ puts "Task #{params[:task_name].inspect} will be forked to its own process because the 'dont_wait' parameter was set to true."
46
+
47
+ # Monkey-patch Mongrel to not remove its pid file in the child
48
+ # See: http://geekblog.vodpod.com/?p=26
49
+ require 'mongrel'
50
+ Mongrel::Configurator.class_eval("def remove_pid_file; puts 'child no-op'; end")
51
+
52
+ pid = fork do
53
+ RAILS_DEFAULT_LOGGER.debug("*** Taskr4Rails -- Executing task #{params[:task_name].inspect} with Ruby code: #{params[:ruby_code]}")
54
+ eval(params[:ruby_code])
55
+ end
56
+
57
+ RAILS_DEFAULT_LOGGER.debug("*** Taskr4Rails -- Task #{params[:task_name].inspect} is being forked into its own thread.")
58
+
59
+ Process.detach(pid)
60
+ else
61
+ RAILS_DEFAULT_LOGGER.debug("*** Taskr4Rails -- Executing task #{params[:task_name].inspect} with Ruby code: #{params[:ruby_code]}")
62
+ RAILS_DEFAULT_LOGGER.debug("*** Taskr4Rails -- Waiting for task #{params[:task_name].inspect} to complete.")
63
+ eval(params[:ruby_code])
64
+ end
47
65
  rescue => e
48
- output << "#{e.class}: #{e}\n\nBACKTRACE:\n#{e.backtrace.join("\n")}"
66
+ puts "#{e.class}: #{e}\n\nBACKTRACE:\n#{e.backtrace.join("\n")}"
49
67
  err = true
50
68
  end
51
69
  $stdout = prev_stdout
52
70
  $stderr = prev_stderr
53
- output = io.read(nil)
71
+ output = io.string
54
72
 
55
73
  render :text => output, :status => (err ? 500 : 200)
56
74
  end
57
75
 
58
- end
76
+ end
metadata CHANGED
@@ -1,28 +1,31 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: taskr
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Zukowski
8
+ - David Palm
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
12
 
12
- date: 2008-06-19 00:00:00 -04:00
13
+ date: 2008-12-23 00:00:00 -05:00
13
14
  default_executable:
14
15
  dependencies:
15
16
  - !ruby/object:Gem::Dependency
16
17
  name: picnic
18
+ type: :runtime
17
19
  version_requirement:
18
20
  version_requirements: !ruby/object:Gem::Requirement
19
21
  requirements:
20
22
  - - ~>
21
23
  - !ruby/object:Gem::Version
22
- version: 0.6.4
24
+ version: 0.7.0
23
25
  version:
24
26
  - !ruby/object:Gem::Dependency
25
27
  name: reststop
28
+ type: :runtime
26
29
  version_requirement:
27
30
  version_requirements: !ruby/object:Gem::Requirement
28
31
  requirements:
@@ -32,15 +35,17 @@ dependencies:
32
35
  version:
33
36
  - !ruby/object:Gem::Dependency
34
37
  name: restr
38
+ type: :runtime
35
39
  version_requirement:
36
40
  version_requirements: !ruby/object:Gem::Requirement
37
41
  requirements:
38
42
  - - ~>
39
43
  - !ruby/object:Gem::Version
40
- version: 0.4.0
44
+ version: 0.5.0
41
45
  version:
42
46
  - !ruby/object:Gem::Dependency
43
47
  name: rufus-scheduler
48
+ type: :runtime
44
49
  version_requirement:
45
50
  version_requirements: !ruby/object:Gem::Requirement
46
51
  requirements:
@@ -48,8 +53,20 @@ dependencies:
48
53
  - !ruby/object:Gem::Version
49
54
  version: 1.0.7
50
55
  version:
56
+ - !ruby/object:Gem::Dependency
57
+ name: hoe
58
+ type: :development
59
+ version_requirement:
60
+ version_requirements: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: 1.8.2
65
+ version:
51
66
  description: cron-like scheduler service with a RESTful interface
52
- email: matt at roughest dot net
67
+ email:
68
+ - matt at roughest dot net
69
+ - dvd plm on googles free email service
53
70
  executables:
54
71
  - taskr
55
72
  - taskr-ctl
@@ -120,7 +137,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
120
137
  requirements: []
121
138
 
122
139
  rubyforge_project: taskr
123
- rubygems_version: 1.0.1
140
+ rubygems_version: 1.3.1
124
141
  signing_key:
125
142
  specification_version: 2
126
143
  summary: cron-like scheduler service with a RESTful interface