SheepDog 0.1.0.20110705 → 0.2.0.20120314

Sign up to get free protection for your applications and to get access to all the features.
data/AUTHORS CHANGED
@@ -1,2 +1,3 @@
1
- = Muriel Salvan (murielsalvan@users.sourceforge.net)
1
+ = Muriel Salvan (muriel@x-aeon.com)
2
2
  * 0.1.0.20110705
3
+ * 0.2.0.20120314
data/ChangeLog CHANGED
@@ -1,4 +1,25 @@
1
- = StatsCollect Release History
1
+ = SheepDog Release History
2
+
3
+ == 0.2.0.20120314 (Alpha)
4
+
5
+ * Added rUtilAnts dependency in the release file.
6
+ * Adapted release info to last version of Ruby Packager.
7
+ * Removed rdoc warning in documentation.
8
+ * Adapted to Ruby's standard conventions.
9
+ * Updated Copyright information.
10
+ * Updated email address of Muriel Salvan.
11
+ * Adapted comments to match a better RDoc syntax.
12
+ * Adapted to new version of rUtilAnts.
13
+ * Adding possibility to add conditions to monitors.
14
+ * Added a common file that can define common methods.
15
+ * Added Monitors/Top to monitor global values returned by top command (configuration example file modified).
16
+ * Monitors/LogFile: Increased robustness in case of a previous SheepDog crash.
17
+ * Monitors/LogFile: Added possibility to not specify a filter in logs parsing.
18
+ * Monitors/Process: Added possibility to monitor resident memory per process (example configuration file updated).
19
+ * Bug correction: Notifiers/SendMail: Multi reports were sending only the last report.
20
+ * Bug correction: Notifiers/StdOut: Multi reports were sending only the last report.
21
+ * Bug correction: Notifiers/SendMail: Notifications could not be sent when grouped.
22
+ * Bug correction: Notifiers/StdOut: Notifications could not be sent when grouped.
2
23
 
3
24
  == 0.1.0.20110705 (Alpha)
4
25
 
data/LICENSE CHANGED
@@ -6,7 +6,7 @@ This list is found in the file named AUTHORS.
6
6
  The AUTHORS and LICENSE files have to be included in any release of software
7
7
  embedding source code of this package, or using it as a derivative software.
8
8
 
9
- Copyright (c) 2011 Muriel Salvan (murielsalvan@users.sourceforge.net)
9
+ Copyright (c) 2011 - 2012 Muriel Salvan (muriel@x-aeon.com)
10
10
 
11
11
  Redistribution and use in source and binary forms, with or without
12
12
  modification, are permitted provided that the following conditions are met:
data/README CHANGED
@@ -1,6 +1,3 @@
1
- -- This file is best viewed when processed by rdoc.
2
- ++
3
-
4
1
  = Sheep Dog
5
2
 
6
3
  Simple command line tool that monitors files and processes and sends notifications or take corrective actions when problems arise. Monitor log files for errors, processes CPU and memory consumption (can kill if exceeding), respawn dead processes.
@@ -11,8 +8,8 @@ Check the website at http://sheepdogsys.sourceforge.net
11
8
 
12
9
  == Who wrote it ?
13
10
 
14
- Check the AUTHORS[link:files/AUTHORS.html] file.
11
+ Check the AUTHORS[link:AUTHORS.html] file.
15
12
 
16
13
  == What is the license ?
17
14
 
18
- You can find out in the LICENSE[link:files/LICENSE.html] file.
15
+ You can find out in the LICENSE[link:LICENSE.html] file.
data/ReleaseInfo CHANGED
@@ -2,7 +2,7 @@
2
2
  # This file has been generated by RubyPackager during a delivery.
3
3
  # More info about RubyPackager: http://rubypackager.sourceforge.net
4
4
  {
5
- :Version => '0.1.0.20110705',
6
- :Tags => [ 'Alpha' ],
7
- :DevStatus => 'Alpha'
5
+ :version => '0.2.0.20120314',
6
+ :tags => [ 'Alpha' ],
7
+ :dev_status => 'Alpha'
8
8
  }
data/bin/sheepdog.rb CHANGED
@@ -1,24 +1,24 @@
1
1
  #!/bin/env ruby
2
2
  #--
3
- # Copyright (c) 2011 Muriel Salvan (murielsalvan@users.sourceforge.net)
3
+ # Copyright (c) 2011 - 2012 Muriel Salvan (muriel@x-aeon.com)
4
4
  # Licensed under the terms specified in LICENSE file. No warranty is provided.
5
5
  #++
6
6
 
7
7
  require 'rUtilAnts/Logging'
8
- RUtilAnts::Logging::initializeLogging('','')
8
+ RUtilAnts::Logging::install_logger_on_object
9
9
  require 'tmpdir'
10
10
  lLogFile = "#{Dir.tmpdir}/SheepDog_#{Process.pid}.log"
11
- setLogFile(lLogFile)
12
- logInfo 'Starting SheepDog'
11
+ set_log_file(lLogFile)
12
+ log_info 'Starting SheepDog'
13
13
  require 'sheepdog/Executor'
14
14
 
15
15
  lConfFileName = ARGV[0]
16
16
  if (lConfFileName == nil)
17
- logErr "Usage: sheepdog.rb <ConfigFileName>"
17
+ log_err "Usage: sheepdog.rb <ConfigFileName>"
18
18
  elsif (File.exists?(lConfFileName))
19
19
  SheepDog::Executor.new.execute(eval(File.read(lConfFileName)))
20
20
  else
21
- logErr "Missing file: #{lConfFileName}"
21
+ log_err "Missing file: #{lConfFileName}"
22
22
  end
23
23
 
24
24
  File.unlink(lLogFile)
@@ -0,0 +1,33 @@
1
+ #--
2
+ # Copyright (c) 2011 - 2012 Muriel Salvan (muriel@x-aeon.com)
3
+ # Licensed under the terms specified in LICENSE file. No warranty is provided.
4
+ #++
5
+
6
+ module SheepDog
7
+
8
+ module Common
9
+
10
+ # Convert a string representing a memory quantity to its integer value.
11
+ # Useful to decode top output, that uses k and m for its quantities.
12
+ #
13
+ # Parameters::
14
+ # * *iStrValue* (_String_): The value as a string
15
+ # Return::
16
+ # * _Integer_: Corresponding value
17
+ def quantity2Int(iStrValue)
18
+ rResult = nil
19
+
20
+ if (iStrValue[-1..-1] == 'k')
21
+ rResult = iStrValue[0..-2].to_i * 1024
22
+ elsif (iStrValue[-1..-1] == 'm')
23
+ rResult = iStrValue[0..-2].to_i * 1024 * 1024
24
+ else
25
+ rResult = iStrValue.to_i
26
+ end
27
+
28
+ return rResult
29
+ end
30
+
31
+ end
32
+
33
+ end
@@ -1,10 +1,11 @@
1
1
  #--
2
- # Copyright (c) 2011 Muriel Salvan (murielsalvan@users.sourceforge.net)
2
+ # Copyright (c) 2011 - 2012 Muriel Salvan (muriel@x-aeon.com)
3
3
  # Licensed under the terms specified in LICENSE file. No warranty is provided.
4
4
  #++
5
5
 
6
6
  require 'time'
7
7
  require 'fileutils'
8
+ require 'sheepdog/Common'
8
9
  require 'sheepdog/Report'
9
10
 
10
11
  module SheepDog
@@ -15,14 +16,14 @@ module SheepDog
15
16
  def initialize
16
17
  # Parse plugins
17
18
  require 'rUtilAnts/Plugins'
18
- RUtilAnts::Plugins::initializePlugins
19
- parsePluginsFromDir('Notifiers', "#{File.expand_path(File.dirname(__FILE__))}/Notifiers", 'SheepDog::Notifiers')
20
- parsePluginsFromDir('Monitors', "#{File.expand_path(File.dirname(__FILE__))}/Monitors", 'SheepDog::Monitors')
19
+ RUtilAnts::Plugins::install_plugins_on_object
20
+ parse_plugins_from_dir('Notifiers', "#{File.expand_path(File.dirname(__FILE__))}/Notifiers", 'SheepDog::Notifiers')
21
+ parse_plugins_from_dir('Monitors', "#{File.expand_path(File.dirname(__FILE__))}/Monitors", 'SheepDog::Monitors')
21
22
  end
22
23
 
23
24
  # Execute a given configuration
24
25
  #
25
- # Parameters:
26
+ # Parameters::
26
27
  # * *iConf* (<em>map<Symbol,Object></em>): The sheep dog configuration
27
28
  def execute(iConf)
28
29
  # Get the local database, storing dates of last reports sent...
@@ -49,125 +50,73 @@ module SheepDog
49
50
  lDelayedReports = {}
50
51
  # Loop through the objects to monitor
51
52
  iConf[:Monitors].each do |iMonitorName, iMonitorInfo|
52
- # Check that it is a known monitor, by accessing the plugin
53
- lMonitorPluginInstance, lError = getPluginInstance('Monitors', iMonitorInfo[:Type])
54
- if (lMonitorPluginInstance == nil)
55
- # Unknown monitor
56
- logErr "Unknown Monitor #{iMonitorInfo[:Type]}: #{lError}. Ignoring corresponding monitoring process. Please check configuration."
57
- else
58
- # Create the report to be filled by this process
59
- lReport = Report.new
60
- # Create the monitor configuration dir
61
- lMonitorDir = "#{iConf[:WorkingDir]}/#{iMonitorName}"
62
- FileUtils::mkdir_p(lMonitorDir)
63
- # Set instance variables and methods for this monitor
64
- lMonitorPluginInstance.instance_variable_set(:@SheepDogConf, iConf)
65
- lMonitorPluginInstance.instance_variable_set(:@Report, lReport)
66
- lMonitorPluginInstance.instance_variable_set(:@MonitorDir, lMonitorDir)
67
- if (!lMonitorPluginInstance.respond_to?(:report))
68
- # Report an entry
69
- #
70
- # Parameters:
71
- # * *iEntry* (_String_): Entry to be reported
72
- def lMonitorPluginInstance.report(iEntry)
73
- @Report.addEntry(iEntry)
74
- logInfo "Report: #{iEntry}"
53
+ # First check if conditions are met
54
+ if ((iMonitorInfo[:Conditions] == nil) or
55
+ (checkConditions(iMonitorInfo[:Conditions])))
56
+ # Check that it is a known monitor, by accessing the plugin
57
+ lMonitorPluginInstance, lError = get_plugin_instance('Monitors', iMonitorInfo[:Type])
58
+ if (lMonitorPluginInstance == nil)
59
+ # Unknown monitor
60
+ log_err "Unknown Monitor #{iMonitorInfo[:Type]}: #{lError}. Ignoring corresponding monitoring process. Please check configuration."
61
+ else
62
+ # Create the report to be filled by this process
63
+ lReport = Report.new
64
+ # Create the monitor configuration dir
65
+ lMonitorDir = "#{iConf[:WorkingDir]}/#{iMonitorName}"
66
+ FileUtils::mkdir_p(lMonitorDir)
67
+ # Set instance variables and methods for this monitor
68
+ lMonitorPluginInstance.instance_variable_set(:@SheepDogConf, iConf)
69
+ lMonitorPluginInstance.instance_variable_set(:@Report, lReport)
70
+ lMonitorPluginInstance.instance_variable_set(:@MonitorDir, lMonitorDir)
71
+ if (!lMonitorPluginInstance.respond_to?(:report))
72
+ # Report an entry
73
+ #
74
+ # Parameters::
75
+ # * *iEntry* (_String_): Entry to be reported
76
+ def lMonitorPluginInstance.report(iEntry)
77
+ @Report.addEntry(iEntry)
78
+ log_info "Report: #{iEntry}"
79
+ end
75
80
  end
76
- end
77
- # Call this monitor
78
- begin
79
- logInfo "Executing monitoring process #{iMonitorName} ..."
80
- lMonitorPluginInstance.execute(iMonitorInfo)
81
- rescue Exception
82
- logErr "Exception while executing monitor #{iMonitorName}: #{$!}.\n#{$!.backtrace.join("\n")}"
83
- report "!!! Exception while executing monitor #{iMonitorName}: #{$!}.\n#{$!.backtrace.join("\n")}"
84
- end
85
- # If this report is not empty, save it in a file
86
- lCurrentReportFileName = nil
87
- lCurrentReportTime = nil
88
- if (!lReport.empty?)
89
- lCurrentReportTime = Time.now.utc
90
- lCurrentReportFileName = "#{lMonitorDir}/Report_#{lCurrentReportTime.strftime('%Y-%m-%d-%H-%M-%S')}"
91
- File.open(lCurrentReportFileName, 'w') do |oFile|
92
- oFile.write(Marshal.dump(lReport))
81
+ # Call this monitor
82
+ begin
83
+ log_info "Executing monitoring process #{iMonitorName} ..."
84
+ lMonitorPluginInstance.execute(iMonitorInfo)
85
+ rescue Exception
86
+ log_err "Exception while executing monitor #{iMonitorName}: #{$!}.\n#{$!.backtrace.join("\n")}"
87
+ report "!!! Exception while executing monitor #{iMonitorName}: #{$!}.\n#{$!.backtrace.join("\n")}"
93
88
  end
94
- end
95
- # Get the list of reports to send, per time
96
- # map< Time, FileName >
97
- lReportFiles = {}
98
- Dir.glob("#{lMonitorDir}/Report_*").each do |iReportFile|
99
- lMatch = File.basename(iReportFile).match(/^Report_(\d\d\d\d)-(\d\d)-(\d\d)-(\d\d)-(\d\d)-(\d\d)$/)
100
- if (lMatch == nil)
101
- logErr "Invalid file report name: #{iReportFile}. Ignoring it."
102
- else
103
- lReportFiles[Time.parse("#{lMatch[1]}-#{lMatch[2]}-#{lMatch[3]} #{lMatch[4]}:#{lMatch[5]}:#{lMatch[6]} UTC")] = iReportFile
89
+ # If this report is not empty, save it in a file
90
+ lCurrentReportFileName = nil
91
+ lCurrentReportTime = nil
92
+ if (!lReport.empty?)
93
+ lCurrentReportTime = Time.now.utc
94
+ lCurrentReportFileName = "#{lMonitorDir}/Report_#{lCurrentReportTime.strftime('%Y-%m-%d-%H-%M-%S')}"
95
+ File.open(lCurrentReportFileName, 'w') do |oFile|
96
+ oFile.write(Marshal.dump(lReport))
97
+ end
104
98
  end
105
- end
106
- # For each report file, compute the list of notifiers that will send it
107
- if (!lReportFiles.empty?)
108
- # There are some report files to be (maybe) sent.
109
- # Loop through the notifiers.
110
- iMonitorInfo[:Notifiers].each do |iNotifierName, iNotifierConf|
111
- lNotifierID = iNotifierConf[:Type]
112
- if (iNotifierConf[:GroupReports] == nil)
113
- # Send the report now if it exists
114
- if (lCurrentReportFileName != nil)
115
- # Send [iMonitorInfo, [lCurrentReportFileName]] to iNotifierName
116
- if (iNotifierConf[:GroupWithOtherMonitors] == true)
117
- if (lGroupedMonitorReports[lNotifierID] == nil)
118
- lGroupedMonitorReports[lNotifierID] = {}
119
- end
120
- if (lGroupedMonitorReports[lNotifierID][iMonitorName] == nil)
121
- lGroupedMonitorReports[lNotifierID][iMonitorName] = []
122
- end
123
- lGroupedMonitorReports[lNotifierID][iMonitorName] << [ iNotifierConf, [ lCurrentReportFileName ] ]
124
- else
125
- notify(iConf, {lNotifierID => {iMonitorName => [ [ iNotifierConf, [lCurrentReportFileName] ] ] }}, lDelayedReports)
126
- end
127
- lSentReports[lCurrentReportFileName] = nil
128
- # Remember last report sent
129
- if (lDatabase[:LastReportsSent][iMonitorName] == nil)
130
- lDatabase[:LastReportsSent][iMonitorName] = {}
131
- end
132
- lDatabase[:LastReportsSent][iMonitorName][iNotifierName] = lCurrentReportTime
133
- end
99
+ # Get the list of reports to send, per time
100
+ # map< Time, FileName >
101
+ lReportFiles = {}
102
+ Dir.glob("#{lMonitorDir}/Report_*").each do |iReportFile|
103
+ lMatch = File.basename(iReportFile).match(/^Report_(\d\d\d\d)-(\d\d)-(\d\d)-(\d\d)-(\d\d)-(\d\d)$/)
104
+ if (lMatch == nil)
105
+ log_err "Invalid file report name: #{iReportFile}. Ignoring it."
134
106
  else
135
- # Get the interval in seconds
136
- lSecsInterval = getSecsInterval(iNotifierConf[:GroupReports])
137
- # Maybe we don't want to send reports now
138
- # Get the last time we sent reports for this one
139
- if ((lDatabase[:LastReportsSent][iMonitorName] != nil) and
140
- (lDatabase[:LastReportsSent][iMonitorName][iNotifierName] != nil) and
141
- ((Time.now.utc - lDatabase[:LastReportsSent][iMonitorName][iNotifierName]) < lSecsInterval))
142
- # Reports from last one sent to the most recent one are marked to be sent later
143
- lReportFiles.each do |iReportTime, iReportFileName|
144
- if (iReportTime > lDatabase[:LastReportsSent][iMonitorName][iNotifierName])
145
- # This report will be sent another time
146
- lDelayedReports[iReportFileName] = nil
147
- end
148
- end
149
- else
150
- # Send all corresponding reports now
151
- lLastReportSentDate = nil
152
- if ((lDatabase[:LastReportsSent][iMonitorName] != nil) and
153
- (lDatabase[:LastReportsSent][iMonitorName][iNotifierName] != nil))
154
- lLastReportSentDate = lDatabase[:LastReportsSent][iMonitorName][iNotifierName]
155
- else
156
- lLastReportSentDate = Time.parse('1970-01-01 00:00:00 UTC')
157
- end
158
- lReportFilesToSend = []
159
- lLastReportTime = Time.parse('1970-01-01 00:00:00 UTC')
160
- lReportFiles.each do |iReportTime, iReportFileName|
161
- if (iReportTime > lLastReportSentDate)
162
- lReportFilesToSend << iReportFileName
163
- lSentReports[iReportFileName] = nil
164
- if (iReportTime > lLastReportTime)
165
- lLastReportTime = iReportTime
166
- end
167
- end
168
- end
169
- if (!lReportFilesToSend.empty?)
170
- # Send [iMonitorInfo, lReportFilesToSend] to iNotifierName
107
+ lReportFiles[Time.parse("#{lMatch[1]}-#{lMatch[2]}-#{lMatch[3]} #{lMatch[4]}:#{lMatch[5]}:#{lMatch[6]} UTC")] = iReportFile
108
+ end
109
+ end
110
+ # For each report file, compute the list of notifiers that will send it
111
+ if (!lReportFiles.empty?)
112
+ # There are some report files to be (maybe) sent.
113
+ # Loop through the notifiers.
114
+ iMonitorInfo[:Notifiers].each do |iNotifierName, iNotifierConf|
115
+ lNotifierID = iNotifierConf[:Type]
116
+ if (iNotifierConf[:GroupReports] == nil)
117
+ # Send the report now if it exists
118
+ if (lCurrentReportFileName != nil)
119
+ # Send [iMonitorInfo, [lCurrentReportFileName]] to iNotifierName
171
120
  if (iNotifierConf[:GroupWithOtherMonitors] == true)
172
121
  if (lGroupedMonitorReports[lNotifierID] == nil)
173
122
  lGroupedMonitorReports[lNotifierID] = {}
@@ -175,15 +124,71 @@ module SheepDog
175
124
  if (lGroupedMonitorReports[lNotifierID][iMonitorName] == nil)
176
125
  lGroupedMonitorReports[lNotifierID][iMonitorName] = []
177
126
  end
178
- lGroupedMonitorReports[lNotifierID][iMonitorName] << [ iNotifierConf, lReportFilesToSend ]
127
+ lGroupedMonitorReports[lNotifierID][iMonitorName] << [ iNotifierConf, [ lCurrentReportFileName ] ]
179
128
  else
180
- notify(iConf, {lNotifierID => {iMonitorName => [ [ iNotifierConf, lReportFilesToSend ] ]}}, lDelayedReports)
129
+ notify(iConf, {lNotifierID => {iMonitorName => [ [ iNotifierConf, [lCurrentReportFileName] ] ] }}, lDelayedReports)
181
130
  end
131
+ lSentReports[lCurrentReportFileName] = nil
182
132
  # Remember last report sent
183
133
  if (lDatabase[:LastReportsSent][iMonitorName] == nil)
184
134
  lDatabase[:LastReportsSent][iMonitorName] = {}
185
135
  end
186
- lDatabase[:LastReportsSent][iMonitorName][iNotifierName] = lLastReportTime
136
+ lDatabase[:LastReportsSent][iMonitorName][iNotifierName] = lCurrentReportTime
137
+ end
138
+ else
139
+ # Get the interval in seconds
140
+ lSecsInterval = getSecsInterval(iNotifierConf[:GroupReports])
141
+ # Maybe we don't want to send reports now
142
+ # Get the last time we sent reports for this one
143
+ if ((lDatabase[:LastReportsSent][iMonitorName] != nil) and
144
+ (lDatabase[:LastReportsSent][iMonitorName][iNotifierName] != nil) and
145
+ ((Time.now.utc - lDatabase[:LastReportsSent][iMonitorName][iNotifierName]) < lSecsInterval))
146
+ # Reports from last one sent to the most recent one are marked to be sent later
147
+ lReportFiles.each do |iReportTime, iReportFileName|
148
+ if (iReportTime > lDatabase[:LastReportsSent][iMonitorName][iNotifierName])
149
+ # This report will be sent another time
150
+ lDelayedReports[iReportFileName] = nil
151
+ end
152
+ end
153
+ else
154
+ # Send all corresponding reports now
155
+ lLastReportSentDate = nil
156
+ if ((lDatabase[:LastReportsSent][iMonitorName] != nil) and
157
+ (lDatabase[:LastReportsSent][iMonitorName][iNotifierName] != nil))
158
+ lLastReportSentDate = lDatabase[:LastReportsSent][iMonitorName][iNotifierName]
159
+ else
160
+ lLastReportSentDate = Time.parse('1970-01-01 00:00:00 UTC')
161
+ end
162
+ lReportFilesToSend = []
163
+ lLastReportTime = Time.parse('1970-01-01 00:00:00 UTC')
164
+ lReportFiles.each do |iReportTime, iReportFileName|
165
+ if (iReportTime > lLastReportSentDate)
166
+ lReportFilesToSend << iReportFileName
167
+ lSentReports[iReportFileName] = nil
168
+ if (iReportTime > lLastReportTime)
169
+ lLastReportTime = iReportTime
170
+ end
171
+ end
172
+ end
173
+ if (!lReportFilesToSend.empty?)
174
+ # Send [iMonitorInfo, lReportFilesToSend] to iNotifierName
175
+ if (iNotifierConf[:GroupWithOtherMonitors] == true)
176
+ if (lGroupedMonitorReports[lNotifierID] == nil)
177
+ lGroupedMonitorReports[lNotifierID] = {}
178
+ end
179
+ if (lGroupedMonitorReports[lNotifierID][iMonitorName] == nil)
180
+ lGroupedMonitorReports[lNotifierID][iMonitorName] = []
181
+ end
182
+ lGroupedMonitorReports[lNotifierID][iMonitorName] << [ iNotifierConf, lReportFilesToSend ]
183
+ else
184
+ notify(iConf, {lNotifierID => {iMonitorName => [ [ iNotifierConf, lReportFilesToSend ] ]}}, lDelayedReports)
185
+ end
186
+ # Remember last report sent
187
+ if (lDatabase[:LastReportsSent][iMonitorName] == nil)
188
+ lDatabase[:LastReportsSent][iMonitorName] = {}
189
+ end
190
+ lDatabase[:LastReportsSent][iMonitorName][iNotifierName] = lLastReportTime
191
+ end
187
192
  end
188
193
  end
189
194
  end
@@ -203,7 +208,7 @@ module SheepDog
203
208
  end
204
209
  # Log reports to be sent delayed
205
210
  lDelayedReports.keys.each do |iReportFileName|
206
- logInfo "Report to be sent later: #{iReportFileName}"
211
+ log_info "Report to be sent later: #{iReportFileName}"
207
212
  end
208
213
 
209
214
  # Write back database
@@ -214,19 +219,46 @@ module SheepDog
214
219
 
215
220
  private
216
221
 
222
+ # Check conditions
223
+ #
224
+ # Parameters::
225
+ # * *iConditions* (<em>map<Symbol,Object></em>): The conditions to check
226
+ # Return::
227
+ # * _Boolean_: Are the conditions respected ?
228
+ def checkConditions(iConditions)
229
+ rPassed = true
230
+
231
+ if (iConditions[:TimeRanges] != nil)
232
+ require 'time'
233
+ # If current time falls into at least 1 range of the list, it's ok.
234
+ lNow = Time.parse(Time.now.utc.strftime('2001-01-01 %H:%m:%S UTC')).utc
235
+ rPassed = false
236
+ iConditions[:TimeRanges].each do |iTimeRangeInfo|
237
+ iBeginTime, iEndTime = iTimeRangeInfo.map { |iStrTime| Time.parse("2001-01-01 #{iStrTime}").utc }
238
+ rPassed = ((lNow >= iBeginTime) and
239
+ (lNow <= iEndTime))
240
+ if (rPassed)
241
+ break
242
+ end
243
+ end
244
+ end
245
+
246
+ return rPassed
247
+ end
248
+
217
249
  # Process notifications to be sent.
218
250
  #
219
- # Parameters:
251
+ # Parameters::
220
252
  # * *iConf* (<em>map<Symbol,Object></em>): SheepDog config
221
- # * *iNotificationsInfo* (<em>map<NotifierName,map<MonitorName,list<[NotifierConf,list<ReportFileName>]>>></em>): The list of report files to send along with their notifier config, per monitor name, per notifier name
253
+ # * *iNotificationsInfo* (<em>map<NotifierName,map<MonitorName,list< [NotifierConf,list<ReportFileName>] >>></em>): The list of report files to send along with their notifier config, per monitor name, per notifier name
222
254
  # * *ioErrorReports* (<em>map<ReportFileName,nil></em>): The set of report file names that could not be sent through notifications
223
255
  def notify(iConf, iNotificationsInfo, ioErrorReports)
224
256
  iNotificationsInfo.each do |iNotifierName, iNotifierNotificationsInfo|
225
257
  # Find this notifier
226
258
  if (iConf[:Notifiers][iNotifierName] == nil)
227
- logErr "Unknown notifier named #{iNotifierName}. Ignoring notifications to be sent there. Please check configuration."
259
+ log_err "Unknown notifier named #{iNotifierName}. Ignoring notifications to be sent there. Please check configuration."
228
260
  else
229
- accessPlugin('Notifiers', iConf[:Notifiers][iNotifierName][:Type]) do |iNotifierPlugin|
261
+ access_plugin('Notifiers', iConf[:Notifiers][iNotifierName][:Type]) do |iNotifierPlugin|
230
262
  # List of reports to send through this notifier
231
263
  # list< Report >
232
264
  lLstReports = []
@@ -241,7 +273,7 @@ module SheepDog
241
273
  begin
242
274
  lReport = Marshal.load(File.read(iReportFileName))
243
275
  rescue Exception
244
- logErr "Invalid report stored in file #{iReportFileName}: #{$!}.\n#{$!.backtrace.join("\n")}"
276
+ log_err "Invalid report stored in file #{iReportFileName}: #{$!}.\n#{$!.backtrace.join("\n")}"
245
277
  ioErrorReports[iReportFileName] = nil
246
278
  lReport = nil
247
279
  end
@@ -257,10 +289,10 @@ module SheepDog
257
289
  end
258
290
  end
259
291
  begin
260
- logInfo "===== Send notification to #{iNotifierName} of #{lLstReports.size} reports..."
261
- iNotifierPlugin.sendNotification(iConf[:Notifiers][iNotifierName], lLstReports)
292
+ log_info "===== Send notification to #{iNotifierName} of #{lLstReports.size} reports..."
293
+ iNotifierPlugin.send_notification(iConf[:Notifiers][iNotifierName], lLstReports)
262
294
  rescue Exception
263
- logErr "Exception while sending notification from #{iNotifierName} for reports #{lReportFilesSet.keys.join(', ')}: #{$!}.\n#{$!.backtrace.join("\n")}"
295
+ log_err "Exception while sending notification from #{iNotifierName} for reports #{lReportFilesSet.keys.join(', ')}: #{$!}.\n#{$!.backtrace.join("\n")}"
264
296
  ioErrorReports.merge!(lReportFilesSet)
265
297
  end
266
298
  end
@@ -270,15 +302,15 @@ module SheepDog
270
302
 
271
303
  # Get the number of seconds defined in a configuration
272
304
  #
273
- # Parameters:
305
+ # Parameters::
274
306
  # * *iConf* (<em>map<Symbol,Object></em>): The configuration
275
- # Return:
307
+ # Return::
276
308
  # * _Fixnum_: The number of seconds
277
309
  def getSecsInterval(iConf)
278
310
  if (iConf[:Interval_Secs] != nil)
279
311
  return iConf[:Interval_Secs]
280
312
  else
281
- logErr "Unable to decode interval from #{iConf.inspect}"
313
+ log_err "Unable to decode interval from #{iConf.inspect}"
282
314
  return 0
283
315
  end
284
316
  end
@@ -1,5 +1,5 @@
1
1
  #--
2
- # Copyright (c) 2011 Muriel Salvan (murielsalvan@users.sourceforge.net)
2
+ # Copyright (c) 2011 - 2012 Muriel Salvan (muriel@x-aeon.com)
3
3
  # Licensed under the terms specified in LICENSE file. No warranty is provided.
4
4
  #++
5
5
 
@@ -11,14 +11,19 @@ module SheepDog
11
11
 
12
12
  # Execute the monitoring process for a given configuration
13
13
  #
14
- # Parameters:
14
+ # Parameters::
15
15
  # * *iConf* (<em>map<Symbol,Object></em>): The monitor configuration
16
16
  def execute(iConf)
17
17
  # Get past values
18
18
  lReadValuesFileName = "#{@MonitorDir}/ReadValues"
19
19
  lReadValues = nil
20
20
  if (File.exists?(lReadValuesFileName))
21
- lReadValues = Marshal.load(File.read(lReadValuesFileName))
21
+ begin
22
+ lReadValues = Marshal.load(File.read(lReadValuesFileName))
23
+ rescue Exception
24
+ report "Error while reading previously read values: #{$!}"
25
+ lReadValues = nil
26
+ end
22
27
  else
23
28
  lReadValues = {
24
29
  :LastPos => 0,
@@ -41,16 +46,20 @@ module SheepDog
41
46
  iFile.seek(lStartPos)
42
47
  iFile.read.split("\n").each do |iLine|
43
48
  # Match the line against filters
44
- lMatch = false
45
- iConf[:Filters].each do |iFilter|
46
- if (iLine.match(iFilter) != nil)
47
- lMatch = true
48
- break
49
- end
50
- end
51
- if (lMatch)
52
- # Report this line
49
+ if (iConf[:Filters] == nil)
53
50
  report iLine
51
+ else
52
+ lMatch = false
53
+ iConf[:Filters].each do |iFilter|
54
+ if (iLine.match(iFilter) != nil)
55
+ lMatch = true
56
+ break
57
+ end
58
+ end
59
+ if (lMatch)
60
+ # Report this line
61
+ report iLine
62
+ end
54
63
  end
55
64
  end
56
65
  lReadValues[:LastPos] = iFile.pos
@@ -1,10 +1,10 @@
1
1
  #--
2
- # Copyright (c) 2011 Muriel Salvan (murielsalvan@users.sourceforge.net)
2
+ # Copyright (c) 2011 - 2012 Muriel Salvan (muriel@x-aeon.com)
3
3
  # Licensed under the terms specified in LICENSE file. No warranty is provided.
4
4
  #++
5
5
 
6
6
  require 'rUtilAnts/Misc'
7
- RUtilAnts::Misc::initializeMisc
7
+ RUtilAnts::Misc::install_misc_on_object
8
8
 
9
9
  module SheepDog
10
10
 
@@ -14,7 +14,7 @@ module SheepDog
14
14
 
15
15
  # Execute the monitoring process for a given configuration
16
16
  #
17
- # Parameters:
17
+ # Parameters::
18
18
  # * *iConf* (<em>map<Symbol,Object></em>): The monitor configuration
19
19
  def execute(iConf)
20
20
  # Get the list of processes
@@ -50,7 +50,7 @@ module SheepDog
50
50
  # Maybe we want to execute something
51
51
  if (iConf.has_key?(:ExecuteIfMissing))
52
52
  report "Missing process. Executing \"#{iConf[:ExecuteIfMissing][:CmdLine]}\" from \"#{iConf[:ExecuteIfMissing][:Pwd]}\":"
53
- changeDir(iConf[:ExecuteIfMissing][:Pwd]) do
53
+ change_dir(iConf[:ExecuteIfMissing][:Pwd]) do
54
54
  report `#{iConf[:ExecuteIfMissing][:CmdLine]}`
55
55
  end
56
56
  end
@@ -60,7 +60,7 @@ module SheepDog
60
60
  # map< Integer, nil >
61
61
  lAboveLimitsPIDs = {}
62
62
  lLstPIDs.each do |iPID|
63
- lCPUPercent, lMemPercent, lVirtualSize = getPIDMetrics(iPID)
63
+ lCPUPercent, lMemPercent, lVirtualSize, lResidentSize = getPIDMetrics(iPID)
64
64
  # Challenge metrics against limits
65
65
  if ((iConf[:Limits].has_key?(:CPUPercent)) and
66
66
  (lCPUPercent > iConf[:Limits][:CPUPercent]))
@@ -77,6 +77,11 @@ module SheepDog
77
77
  report "PID #{iPID} exceeds virtual mem size limit: #{lVirtualSize} > #{iConf[:Limits][:VirtualMemSize]}"
78
78
  lAboveLimitsPIDs[iPID] = nil
79
79
  end
80
+ if ((iConf[:Limits].has_key?(:ResidentMemSize)) and
81
+ (lResidentSize > iConf[:Limits][:ResidentMemSize]))
82
+ report "PID #{iPID} exceeds resident mem size limit: #{lResidentSize} > #{iConf[:Limits][:ResidentMemSize]}"
83
+ lAboveLimitsPIDs[iPID] = nil
84
+ end
80
85
  end
81
86
  if (!lAboveLimitsPIDs.empty?)
82
87
  # What to do with PIDs exceeding limits ?
@@ -91,14 +96,17 @@ module SheepDog
91
96
 
92
97
  private
93
98
 
99
+ include SheepDog::Common
100
+
94
101
  # Get metrics of a PID
95
102
  #
96
- # Parameters:
103
+ # Parameters::
97
104
  # * *iPID* (_Integer_): The PID to get metrics from
98
- # Return:
105
+ # Return::
99
106
  # * _Float_: The CPU percentage
100
107
  # * _Float_: The mem percentage
101
108
  # * _Integer_: The total virtual memory size
109
+ # * _Integer_: The total resident memory size
102
110
  def getPIDMetrics(iPID)
103
111
  rCPUPercent = nil
104
112
  rMemPercent = nil
@@ -106,11 +114,13 @@ module SheepDog
106
114
 
107
115
  # From top
108
116
  lTopOutput = `top -n1 -p#{iPID} -b | tail -2 | head -1`.strip
109
- lMatch = lTopOutput.match(/^\d+\s+\S+\s+\d+\s+\d+\s+\S+\s+\S+\s+\d+\s+\S+\s+(\S+)\s+(\S+)\s+\S+\s+.+$/)
117
+ lMatch = lTopOutput.match(/^\d+\s+\S+\s+\d+\s+\d+\s+\S+\s+(\S+)\s+\d+\s+\S+\s+(\S+)\s+(\S+)\s+\S+\s+.+$/)
110
118
  if (lMatch == nil)
111
119
  report "Unable to decode top output for PID #{iPID}: \"#{lTopOutput}\""
112
120
  else
113
- rCPUPercent, rMemPercent = lMatch[1..2].map { |iStrValue| iStrValue.to_f }
121
+ # Convert RES column to integer
122
+ rRESValue = quantity2Int(lMatch[1])
123
+ rCPUPercent, rMemPercent = lMatch[2..3].map { |iStrValue| iStrValue.to_f }
114
124
  end
115
125
  # From proc/<PID>/stat
116
126
  lStatOutput = `cat /proc/#{iPID}/stat`.strip
@@ -121,7 +131,7 @@ module SheepDog
121
131
  rVS = lMatch[1].to_i
122
132
  end
123
133
 
124
- return rCPUPercent, rMemPercent, rVS
134
+ return rCPUPercent, rMemPercent, rVS, rRESValue
125
135
  end
126
136
 
127
137
  end
@@ -0,0 +1,63 @@
1
+ #--
2
+ # Copyright (c) 2011 - 2012 Muriel Salvan (muriel@x-aeon.com)
3
+ # Licensed under the terms specified in LICENSE file. No warranty is provided.
4
+ #++
5
+
6
+ require 'rUtilAnts/Misc'
7
+ RUtilAnts::Misc::install_misc_on_object
8
+
9
+ module SheepDog
10
+
11
+ module Monitors
12
+
13
+ class Top
14
+
15
+ include SheepDog::Common
16
+
17
+ # Execute the monitoring process for a given configuration
18
+ #
19
+ # Parameters::
20
+ # * *iConf* (<em>map<Symbol,Object></em>): The monitor configuration
21
+ def execute(iConf)
22
+ lLstTopOutput = `top -b -p0 -n1 | head -4`.split("\n").map { |iLine| iLine.strip }
23
+ # The loads
24
+ if (iConf[:Limits][:Loads] != nil)
25
+ lLine = lLstTopOutput[0]
26
+ lMatch = lLine.match(/load average: ([^,]+), ([^,]+), ([^,]+)$/)
27
+ if (lMatch == nil)
28
+ report "Unable to decode top output for loads: \"#{lLine}\"."
29
+ else
30
+ lTopValues = lMatch[1..3].map { |iStrValue| iStrValue.to_f }
31
+ 3.times do |iIdx|
32
+ if ((iConf[:Limits][:Loads][iIdx] != nil) and
33
+ (lTopValues[iIdx] > iConf[:Limits][:Loads][iIdx]))
34
+ report "Value ##{iIdx} of load exceeds limit: #{lTopValues[iIdx]} > #{iConf[:Limits][:Loads][iIdx]}"
35
+ end
36
+ end
37
+ end
38
+ end
39
+ # The memory
40
+ if (iConf[:Limits][:Memory] != nil)
41
+ lLine = lLstTopOutput[3]
42
+ lMatch = lLine.match(/\s+(\S+) used,\s+(\S+) free/)
43
+ if (lMatch == nil)
44
+ report "Unable to decode top output for memory: \"#{lLine}\"."
45
+ else
46
+ lMemUsed, lMemFree = lMatch[1..2].map { |iStrValue| quantity2Int(iStrValue) }
47
+ if ((iConf[:Limits][:Memory][:MaxUsed] != nil) and
48
+ (lMemUsed > iConf[:Limits][:Memory][:MaxUsed]))
49
+ report "Used memory exceed maximal limit: #{lMemUsed} > #{iConf[:Limits][:Memory][:MaxUsed]}"
50
+ end
51
+ if ((iConf[:Limits][:Memory][:MinFree] != nil) and
52
+ (lMemFree < iConf[:Limits][:Memory][:MinFree]))
53
+ report "Free memory below minimal limit: #{lMemFree} < #{iConf[:Limits][:Memory][:MinFree]}"
54
+ end
55
+ end
56
+ end
57
+ end
58
+
59
+ end
60
+
61
+ end
62
+
63
+ end
@@ -1,5 +1,5 @@
1
1
  #--
2
- # Copyright (c) 2010 - 2011 Muriel Salvan (murielsalvan@users.sourceforge.net)
2
+ # Copyright (c) 2010 - 2012 Muriel Salvan (muriel@x-aeon.com)
3
3
  # Licensed under the terms specified in LICENSE file. No warranty is provided.
4
4
  #++
5
5
 
@@ -11,19 +11,20 @@ module SheepDog
11
11
 
12
12
  # Send notifications for a given list of reports.
13
13
  #
14
- # Parameters:
14
+ # Parameters::
15
15
  # * *iConf* (<em>map<Symbol,Object></em>): The notifier config
16
16
  # * *iLstReports* (<em>list<Report></em>): List of reports to notify
17
- def sendNotification(iConf, iLstReports)
17
+ def send_notification(iConf, iLstReports)
18
18
  lTitle = nil
19
19
  lMessage = nil
20
20
  if (iLstReports.size > 1)
21
21
  lTitle = "#{iLstReports.size} reports"
22
+ lMessage = ''
22
23
  iLstReports.each_with_index do |iReport, iIdx|
23
24
  lMessage << "===============================================\n"
24
25
  lMessage << "========== Report #{iIdx+1} (#{iReport.CreationTime.utc.strftime('%Y-%m-%d %H:%M:%S')} UTC from #{iReport.ReportFileName}): #{iReport.Title}\n"
25
26
  lMessage << iReport.getSimpleText
26
- lMessage << "===============================================\n\n"
27
+ lMessage << "\n===============================================\n\n"
27
28
  end
28
29
  else
29
30
  lReport = iLstReports.first
@@ -1,5 +1,5 @@
1
1
  #--
2
- # Copyright (c) 2011 Muriel Salvan (murielsalvan@users.sourceforge.net)
2
+ # Copyright (c) 2011 - 2012 Muriel Salvan (muriel@x-aeon.com)
3
3
  # Licensed under the terms specified in LICENSE file. No warranty is provided.
4
4
  #++
5
5
 
@@ -11,19 +11,20 @@ module SheepDog
11
11
 
12
12
  # Send notifications for a given list of reports.
13
13
  #
14
- # Parameters:
14
+ # Parameters::
15
15
  # * *iConf* (<em>map<Symbol,Object></em>): The notifier config
16
16
  # * *iLstReports* (<em>list<Report></em>): List of reports to notify
17
- def sendNotification(iConf, iLstReports)
17
+ def send_notification(iConf, iLstReports)
18
18
  lTitle = nil
19
19
  lMessage = nil
20
20
  if (iLstReports.size > 1)
21
21
  lTitle = "#{iLstReports.size} reports"
22
+ lMessage = ''
22
23
  iLstReports.each_with_index do |iReport, iIdx|
23
24
  lMessage << "===============================================\n"
24
25
  lMessage << "========== Report #{iIdx+1} (#{iReport.CreationTime.utc.strftime('%Y-%m-%d %H:%M:%S')} UTC from #{iReport.ReportFileName}): #{iReport.Title}\n"
25
26
  lMessage << iReport.getSimpleText
26
- lMessage << "===============================================\n\n"
27
+ lMessage << "\n===============================================\n\n"
27
28
  end
28
29
  else
29
30
  lReport = iLstReports.first
@@ -1,5 +1,5 @@
1
1
  #--
2
- # Copyright (c) 2011 Muriel Salvan (murielsalvan@users.sourceforge.net)
2
+ # Copyright (c) 2011 - 2012 Muriel Salvan (muriel@x-aeon.com)
3
3
  # Licensed under the terms specified in LICENSE file. No warranty is provided.
4
4
  #++
5
5
 
@@ -30,7 +30,7 @@ module SheepDog
30
30
 
31
31
  # Add an entry to the report
32
32
  #
33
- # Parameters:
33
+ # Parameters::
34
34
  # * *iEntry* (_String_): Entry to be added
35
35
  def addEntry(iEntry)
36
36
  @Entries << iEntry
@@ -38,7 +38,7 @@ module SheepDog
38
38
 
39
39
  # Set the report's title
40
40
  #
41
- # Parameters:
41
+ # Parameters::
42
42
  # * *iTitle* (_String_): Report's title
43
43
  def setTitle(iTitle)
44
44
  @Title = iTitle
@@ -46,7 +46,7 @@ module SheepDog
46
46
 
47
47
  # Set the report's file name
48
48
  #
49
- # Parameters:
49
+ # Parameters::
50
50
  # * *iFileName* (_String_): Report's file name
51
51
  def setReportFileName(iFileName)
52
52
  @ReportFileName = iFileName
@@ -54,7 +54,7 @@ module SheepDog
54
54
 
55
55
  # Get the report as simple text
56
56
  #
57
- # Return:
57
+ # Return::
58
58
  # * _String_: The report as simple text
59
59
  def getSimpleText
60
60
  return @Entries.join("\n")
@@ -62,7 +62,7 @@ module SheepDog
62
62
 
63
63
  # Is this report empty ?
64
64
  #
65
- # Return:
65
+ # Return::
66
66
  # * _Boolean_: Is this report empty ?
67
67
  def empty?
68
68
  return @Entries.empty?
@@ -56,6 +56,9 @@
56
56
  :GroupWithOtherMonitors => true
57
57
  }
58
58
  },
59
+ :Conditions => {
60
+ :TimeRanges => [ [ '02:00:00 UTC', '03:00:00 UTC' ], [ '07:00:00 UTC', '15:00:00 UTC' ] ]
61
+ },
59
62
 
60
63
  :Processes => [
61
64
  {
@@ -66,7 +69,8 @@
66
69
  :Limits => {
67
70
  :CPUPercent => 5,
68
71
  :MemPercent => 5,
69
- :VirtualMemSize => 16777216
72
+ :VirtualMemSize => 16777216,
73
+ :ResidentMemSize => 8192
70
74
  },
71
75
  :ActionAboveLimits => :Kill
72
76
  },
@@ -92,6 +96,25 @@
92
96
  :CmdLine => '/usr/bin/ruby /usr/bin/mongrel_rails start -p 12004 -d -e production -P log/mongrel.pid',
93
97
  :Pwd => '/my/home/rails'
94
98
  }
99
+ },
100
+
101
+ # Monitor global top values
102
+ 'TopValues' => {
103
+ :Type => 'Top',
104
+ :Notifiers => {
105
+ 'Mail' => {
106
+ :Type => 'Mail',
107
+ :Title => 'top values',
108
+ :GroupWithOtherMonitors => true
109
+ }
110
+ },
111
+ :Limits => {
112
+ :Loads => [ 2.00, 1.50, nil ],
113
+ :Memory => {
114
+ :MaxUsed => 4000000000,
115
+ :MinFree => 400000000
116
+ }
117
+ }
95
118
  }
96
119
 
97
120
  }
metadata CHANGED
@@ -4,10 +4,10 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 1
7
+ - 2
8
8
  - 0
9
- - 20110705
10
- version: 0.1.0.20110705
9
+ - 20120314
10
+ version: 0.2.0.20120314
11
11
  platform: ruby
12
12
  authors:
13
13
  - Muriel Salvan
@@ -15,12 +15,25 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-07-05 00:00:00 +02:00
18
+ date: 2012-03-14 00:00:00 +01:00
19
19
  default_executable:
20
- dependencies: []
21
-
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rUtilAnts
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ segments:
30
+ - 1
31
+ - 0
32
+ version: "1.0"
33
+ type: :runtime
34
+ version_requirements: *id001
22
35
  description: Simple command line tool that monitors files and processes and sends notifications or take corrective actions when problems arise. Monitor log files for errors, processes CPU and memory consumption (can kill if exceeding), respawn dead processes.
23
- email: murielsalvan@users.sourceforge.net
36
+ email: muriel@x-aeon.com
24
37
  executables:
25
38
  - sheepdog.rb
26
39
  extensions: []
@@ -32,9 +45,11 @@ files:
32
45
  - bin/sheepdog.rb
33
46
  - ChangeLog
34
47
  - Credits
48
+ - lib/sheepdog/Common.rb
35
49
  - lib/sheepdog/Executor.rb
36
50
  - lib/sheepdog/Monitors/LogFile.rb
37
51
  - lib/sheepdog/Monitors/Process.rb
52
+ - lib/sheepdog/Monitors/Top.rb
38
53
  - lib/sheepdog/Notifiers/SendMail.rb
39
54
  - lib/sheepdog/Notifiers/StdOut.rb
40
55
  - lib/sheepdog/Report.rb