bacuwatch 1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/bacuwatch/README.bacuwatch +37 -0
- data/bacuwatch/bacuwatch +214 -0
- data/bacuwatch/bacuwatch.conf.template +53 -0
- data/bin/bacuwatch +214 -0
- metadata +48 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
The Bacuwatch program can be used to keep a watch on a group of Bacula
|
|
2
|
+
jobs. Typically, it is run periodically from a cron job, and is
|
|
3
|
+
generally configured to send an email message with a single line
|
|
4
|
+
status report on each backup job to the Bacula administrator and
|
|
5
|
+
optionally to send a more detailed report on each job to the user of
|
|
6
|
+
the machine which that job backs up.
|
|
7
|
+
|
|
8
|
+
A typical summary report would consist of a series of single-line
|
|
9
|
+
reports, such as:
|
|
10
|
+
|
|
11
|
+
Okay for delta-force; (Full: 24 days; Last: 5 hours)
|
|
12
|
+
|
|
13
|
+
A typical job report contains the same information, but additionally
|
|
14
|
+
(and at no extra charge) shows the storage volume and number of files
|
|
15
|
+
backed up, for example:
|
|
16
|
+
|
|
17
|
+
Hello Most Honorable Sensai Norris,
|
|
18
|
+
|
|
19
|
+
This is the Bacula Watcher program, reporting on the state of
|
|
20
|
+
the backup job, delta-force, for your computer.
|
|
21
|
+
|
|
22
|
+
- The last full backup ran 24 days ago,
|
|
23
|
+
on Saturday, October 21, 2006.
|
|
24
|
+
It contained 5 gigabytes of data in 172,198 files.
|
|
25
|
+
|
|
26
|
+
- The last incremental backup ran 5 hours ago,
|
|
27
|
+
on Tuesday, November 14, 2006.
|
|
28
|
+
It contained 28 megabytes of data in 81 files.
|
|
29
|
+
|
|
30
|
+
All seems well with the 'delta-force' backup job.
|
|
31
|
+
|
|
32
|
+
When run with no options, the bacuwatch program prints out a job
|
|
33
|
+
summary. To have it periodically email summary and status reports,
|
|
34
|
+
for example at 10:00 every Tuesday morning, the crontab entry would
|
|
35
|
+
be:
|
|
36
|
+
|
|
37
|
+
0 10 * * 2 cd $HOME/bacuview/bacuwatch && ./bacuwatch -summary -notify
|
data/bacuwatch/bacuwatch
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
#! /usr/bin/ruby -sw
|
|
2
|
+
|
|
3
|
+
#require "rubygems"
|
|
4
|
+
#require "pp"
|
|
5
|
+
require "mysql"
|
|
6
|
+
require "postgres"
|
|
7
|
+
require "time"
|
|
8
|
+
|
|
9
|
+
#
|
|
10
|
+
# Define the configuration language, and read in the configuration.
|
|
11
|
+
#
|
|
12
|
+
|
|
13
|
+
@db_args = {
|
|
14
|
+
"dbms" => "postgres", "host" => "localhost",
|
|
15
|
+
"database" => "bacula", "user" => "bacuview", "password" => "bacuview"
|
|
16
|
+
}
|
|
17
|
+
def db(*args)
|
|
18
|
+
@db_args.merge!(args[0])
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
@mail_suffix = nil
|
|
22
|
+
def mailhost(hostname)
|
|
23
|
+
@mail_suffix = hostname
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def mail_fixup(addr)
|
|
27
|
+
return nil unless addr
|
|
28
|
+
addr.gsub(/@$/, "@#{@mail_suffix}")
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
@summaries_to = []
|
|
32
|
+
def summary(*addrs)
|
|
33
|
+
@summaries_to += addrs.map{ |addr| mail_fixup(addr) }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
@watched = []
|
|
37
|
+
def watch(job, addr=nil, name=nil)
|
|
38
|
+
@watched << [ job, mail_fixup(addr), name ]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
config_path =
|
|
42
|
+
[
|
|
43
|
+
defined?($config) && $config,
|
|
44
|
+
File.join(File.dirname(__FILE__), 'bacuwatch.conf'),
|
|
45
|
+
File.join(ENV["HOME"], '.bacuwatch.conf'),
|
|
46
|
+
'/etc/bacuwatch.conf'
|
|
47
|
+
].compact
|
|
48
|
+
config = config_path.find{|fn| File.exist? fn}
|
|
49
|
+
if not config
|
|
50
|
+
puts [ "No config file found among:", config_path ].join("\n ")
|
|
51
|
+
exit
|
|
52
|
+
end
|
|
53
|
+
load config
|
|
54
|
+
|
|
55
|
+
class Numeric
|
|
56
|
+
def days
|
|
57
|
+
self * 86400
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def ago
|
|
61
|
+
d = self
|
|
62
|
+
return "never" if d <= 0
|
|
63
|
+
return (d).to_s + " seconds" if d < 2*60
|
|
64
|
+
return (d/60).to_s + " minutes" if d < 2*60*60
|
|
65
|
+
return (d/(60*60)).to_s + " hours" if d < 2*60*60*24
|
|
66
|
+
return (d/(60*60*24)).to_s + " days" if d < 2*60*60*24*30
|
|
67
|
+
return (d/(60*60*24*30)).to_s + " months" if d < 2*60*60*24*365
|
|
68
|
+
return (d/(60*60*24*365)).to_s + " years"
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
class String
|
|
73
|
+
def commatize
|
|
74
|
+
return self if size < 5
|
|
75
|
+
self.reverse.gsub(/(\d{3}\B)/, '\1,').reverse
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def suffixize
|
|
79
|
+
n = self.to_i
|
|
80
|
+
return self + " " if n < 1000*2
|
|
81
|
+
return (n/1000).to_s + " kilo" if n < 1000*1000*2
|
|
82
|
+
return (n/(1000*1000)).to_s + " mega" if n < 1000*1000*1000*2
|
|
83
|
+
return (n/(1000*1000*1000)).to_s + " giga" if n < 1000*1000*1000*1000*2
|
|
84
|
+
return (n/(1000*1000*1000*1000)).to_s + " tera"
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
class Watcher
|
|
89
|
+
attr :stat
|
|
90
|
+
attr :report
|
|
91
|
+
|
|
92
|
+
@@summary_msg = []
|
|
93
|
+
def self.summary
|
|
94
|
+
@@summary_msg.map{ |s| s.gsub!(/^(\S)/, ' \1') }
|
|
95
|
+
@@summary_msg.sort!
|
|
96
|
+
@@summary_msg
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def initialize(db, dbms, job, name)
|
|
100
|
+
@db = db
|
|
101
|
+
@dbms = dbms
|
|
102
|
+
@report = []
|
|
103
|
+
|
|
104
|
+
report << "Hello" + (name ? " #{name}" : "") + ","
|
|
105
|
+
report << ""
|
|
106
|
+
report << "This is the Bacula Watcher program, reporting on the state of"
|
|
107
|
+
report << "the backup job, #{job}, for your computer."
|
|
108
|
+
report << ""
|
|
109
|
+
full = summarize(job, "F")
|
|
110
|
+
incr = summarize(job, "I")
|
|
111
|
+
status(job, full, incr)
|
|
112
|
+
report.join("\n")
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def query_job(job, level=nil)
|
|
116
|
+
query = "select starttime, jobfiles, jobbytes, level " +
|
|
117
|
+
"from Job where type='B' and jobstatus='T' and " +
|
|
118
|
+
"name='#{job}' and level='#{level}' order by starttime desc limit 1;"
|
|
119
|
+
if @dbms == "mysql"
|
|
120
|
+
@db.query(query).fetch_row
|
|
121
|
+
else
|
|
122
|
+
@db.exec(query).result[0]
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def summarize(job, level)
|
|
127
|
+
l = { "I" => "incremental", "F" => "full" }[level] || level
|
|
128
|
+
r = query_job(job, level)
|
|
129
|
+
if r == nil
|
|
130
|
+
report << " - There has never been a #{l} backup performed."
|
|
131
|
+
d = nil
|
|
132
|
+
else
|
|
133
|
+
t = Time.parse(r[0])
|
|
134
|
+
d = (Time.now - t).to_i
|
|
135
|
+
report <<
|
|
136
|
+
" - The last #{l} backup ran #{d.ago} ago,\n" +
|
|
137
|
+
" on #{t.strftime('%A, %B %d, %Y')}.\n" +
|
|
138
|
+
" It contained #{r[2].suffixize}bytes of data " +
|
|
139
|
+
"in #{r[1].commatize} files."
|
|
140
|
+
end
|
|
141
|
+
report << ""
|
|
142
|
+
return d
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def status(job, full, incr)
|
|
146
|
+
last = incr ? (full && full < incr ? full : incr) : full
|
|
147
|
+
err = []
|
|
148
|
+
if !full
|
|
149
|
+
err << " - The #{job} backup job has never run successfully."
|
|
150
|
+
else
|
|
151
|
+
err << " - The last full backup is too old." if full > 45.days
|
|
152
|
+
err << " - The most recent backup is too old." if last > 15.days
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
if err.empty?
|
|
156
|
+
@stat = "Okay"
|
|
157
|
+
report << "All seems well with the '#{job}' backup job."
|
|
158
|
+
else
|
|
159
|
+
@stat = "ERROR"
|
|
160
|
+
report << "There seems to be a PROBLEM with the '#{job}' backup job."
|
|
161
|
+
[ "", err, "" ].flatten.each do |e| report << e end
|
|
162
|
+
end
|
|
163
|
+
@@summary_msg << "#{stat} for #{job}; " +
|
|
164
|
+
"(Full: #{full.to_i.ago}; Last: #{last.to_i.ago})"
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def mail_to(addr, subject, msg)
|
|
169
|
+
mail = open("|mail -s '#{subject}' #{addr}", "w")
|
|
170
|
+
mail.puts msg
|
|
171
|
+
mail.close
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
if defined?($help) and $help
|
|
175
|
+
print <<-EOF
|
|
176
|
+
bacuwatch [-help] [-quiet] [-summary] [-notify] [-config <config-file>]
|
|
177
|
+
|
|
178
|
+
-help: Prints this help information.
|
|
179
|
+
-quiet: Disables printing the summary report to standard output.
|
|
180
|
+
-config: Specifies a configuration file.
|
|
181
|
+
-summary: Emails a summary report to each of the addresses
|
|
182
|
+
listed in a "summary" statement.
|
|
183
|
+
-notify: Emails a job report to the email address
|
|
184
|
+
listed in the "watch" statement for that job.
|
|
185
|
+
EOF
|
|
186
|
+
exit
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
if @db_args["dbms"] == "mysql"
|
|
190
|
+
db = Mysql.new(@db_args["host"],
|
|
191
|
+
@db_args["user"], @db_args["password"], @db_args["database"])
|
|
192
|
+
else
|
|
193
|
+
db = PGconn.new(@db_args["host"], 5432, "", "", @db_args["database"],
|
|
194
|
+
@db_args["user"], @db_args["password"])
|
|
195
|
+
end
|
|
196
|
+
@watched.each { |watch_info|
|
|
197
|
+
job, addr, name = watch_info
|
|
198
|
+
w = Watcher.new(db, @db_args["dbms"], job, name)
|
|
199
|
+
subject = "Bacula Watcher: #{w.stat} for #{job}."
|
|
200
|
+
if addr and defined?($notify) and $notify
|
|
201
|
+
mail_to(addr, subject, w.report)
|
|
202
|
+
end
|
|
203
|
+
}
|
|
204
|
+
db.close
|
|
205
|
+
|
|
206
|
+
if defined?($summary) and $summary
|
|
207
|
+
@summaries_to.each { |addr|
|
|
208
|
+
mail_to(addr, "Bacula Watch Summary Report", Watcher::summary)
|
|
209
|
+
}
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
if !defined?($quiet) or !$quiet
|
|
213
|
+
puts Watcher::summary
|
|
214
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#
|
|
2
|
+
# bacuwatch.conf -- the configuration file for the bacuwatch program.
|
|
3
|
+
#
|
|
4
|
+
#----
|
|
5
|
+
#
|
|
6
|
+
# A 'db' statement is required to define the connection to the bacula
|
|
7
|
+
# database. Only those parameters that differ from the default values
|
|
8
|
+
# below need to be supplied, so a statement along the lines of:
|
|
9
|
+
#
|
|
10
|
+
# db "host" => "db-server", "password" => "abracadabara"
|
|
11
|
+
#
|
|
12
|
+
# will likely suffice. The possible arguments and their defaults are:
|
|
13
|
+
#
|
|
14
|
+
# "dbms" => "postgres", "host" => "localhost",
|
|
15
|
+
# "database" => "bacula", "user" => "bacuview", "password" => "bacuview"
|
|
16
|
+
#
|
|
17
|
+
#
|
|
18
|
+
#----
|
|
19
|
+
#
|
|
20
|
+
# A 'mailhost' statement can be used to abbreviate email addresses in a
|
|
21
|
+
# common domain. Email addresses ending in an at-sign (@) will be sent
|
|
22
|
+
# to the domain listed in the mailhost statement, so a mailhost
|
|
23
|
+
# statement of:
|
|
24
|
+
#
|
|
25
|
+
# mailhost "example.org"
|
|
26
|
+
#
|
|
27
|
+
# will cause "carol@" in a 'watch' or 'summary' statement to be
|
|
28
|
+
# expanded to "carol@example.org", while email addresses not ending in
|
|
29
|
+
# an at-sign are used as is. This statement goes into effect when
|
|
30
|
+
# seen, so must preceed its first use, but can be changed in mid-file.
|
|
31
|
+
#
|
|
32
|
+
#----
|
|
33
|
+
#
|
|
34
|
+
# A 'watch' statement is used to place a watch on a particular job.
|
|
35
|
+
# The watch statement takes three parameters: a mandatory job name,
|
|
36
|
+
# an optional email address, and an optional name or title. So, watch
|
|
37
|
+
# statements of:
|
|
38
|
+
#
|
|
39
|
+
# watch "delta-force"
|
|
40
|
+
# watch "delta-force", "chuck@norris.org"
|
|
41
|
+
# watch "delta-force", "chuck@norris.org", "Most Honorable Sensai Norris"
|
|
42
|
+
#
|
|
43
|
+
# would watch the 'delta-force' backup job; watch the 'delta-force'
|
|
44
|
+
# backup job and send a report to chuck@norris.org; and watch the
|
|
45
|
+
# 'delta-force' backup job, sending a report to chuck@norris.org using
|
|
46
|
+
# the properly honorific title.
|
|
47
|
+
#
|
|
48
|
+
#----
|
|
49
|
+
#
|
|
50
|
+
# A 'summary' statement is used to email a single-line summary of the
|
|
51
|
+
# status of each job to each of the addresses supplied, for example:
|
|
52
|
+
#
|
|
53
|
+
# summary "alice@example.org", "bob@example.net"
|
data/bin/bacuwatch
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
#! /usr/bin/ruby -sw
|
|
2
|
+
|
|
3
|
+
#require "rubygems"
|
|
4
|
+
#require "pp"
|
|
5
|
+
require "mysql"
|
|
6
|
+
require "postgres"
|
|
7
|
+
require "time"
|
|
8
|
+
|
|
9
|
+
#
|
|
10
|
+
# Define the configuration language, and read in the configuration.
|
|
11
|
+
#
|
|
12
|
+
|
|
13
|
+
@db_args = {
|
|
14
|
+
"dbms" => "postgres", "host" => "localhost",
|
|
15
|
+
"database" => "bacula", "user" => "bacuview", "password" => "bacuview"
|
|
16
|
+
}
|
|
17
|
+
def db(*args)
|
|
18
|
+
@db_args.merge!(args[0])
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
@mail_suffix = nil
|
|
22
|
+
def mailhost(hostname)
|
|
23
|
+
@mail_suffix = hostname
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def mail_fixup(addr)
|
|
27
|
+
return nil unless addr
|
|
28
|
+
addr.gsub(/@$/, "@#{@mail_suffix}")
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
@summaries_to = []
|
|
32
|
+
def summary(*addrs)
|
|
33
|
+
@summaries_to += addrs.map{ |addr| mail_fixup(addr) }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
@watched = []
|
|
37
|
+
def watch(job, addr=nil, name=nil)
|
|
38
|
+
@watched << [ job, mail_fixup(addr), name ]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
config_path =
|
|
42
|
+
[
|
|
43
|
+
defined?($config) && $config,
|
|
44
|
+
File.join(File.dirname(__FILE__), 'bacuwatch.conf'),
|
|
45
|
+
File.join(ENV["HOME"], '.bacuwatch.conf'),
|
|
46
|
+
'/etc/bacuwatch.conf'
|
|
47
|
+
].compact
|
|
48
|
+
config = config_path.find{|fn| File.exist? fn}
|
|
49
|
+
if not config
|
|
50
|
+
puts [ "No config file found among:", config_path ].join("\n ")
|
|
51
|
+
exit
|
|
52
|
+
end
|
|
53
|
+
load config
|
|
54
|
+
|
|
55
|
+
class Numeric
|
|
56
|
+
def days
|
|
57
|
+
self * 86400
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def ago
|
|
61
|
+
d = self
|
|
62
|
+
return "never" if d <= 0
|
|
63
|
+
return (d).to_s + " seconds" if d < 2*60
|
|
64
|
+
return (d/60).to_s + " minutes" if d < 2*60*60
|
|
65
|
+
return (d/(60*60)).to_s + " hours" if d < 2*60*60*24
|
|
66
|
+
return (d/(60*60*24)).to_s + " days" if d < 2*60*60*24*30
|
|
67
|
+
return (d/(60*60*24*30)).to_s + " months" if d < 2*60*60*24*365
|
|
68
|
+
return (d/(60*60*24*365)).to_s + " years"
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
class String
|
|
73
|
+
def commatize
|
|
74
|
+
return self if size < 5
|
|
75
|
+
self.reverse.gsub(/(\d{3}\B)/, '\1,').reverse
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def suffixize
|
|
79
|
+
n = self.to_i
|
|
80
|
+
return self + " " if n < 1000*2
|
|
81
|
+
return (n/1000).to_s + " kilo" if n < 1000*1000*2
|
|
82
|
+
return (n/(1000*1000)).to_s + " mega" if n < 1000*1000*1000*2
|
|
83
|
+
return (n/(1000*1000*1000)).to_s + " giga" if n < 1000*1000*1000*1000*2
|
|
84
|
+
return (n/(1000*1000*1000*1000)).to_s + " tera"
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
class Watcher
|
|
89
|
+
attr :stat
|
|
90
|
+
attr :report
|
|
91
|
+
|
|
92
|
+
@@summary_msg = []
|
|
93
|
+
def self.summary
|
|
94
|
+
@@summary_msg.map{ |s| s.gsub!(/^(\S)/, ' \1') }
|
|
95
|
+
@@summary_msg.sort!
|
|
96
|
+
@@summary_msg
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def initialize(db, dbms, job, name)
|
|
100
|
+
@db = db
|
|
101
|
+
@dbms = dbms
|
|
102
|
+
@report = []
|
|
103
|
+
|
|
104
|
+
report << "Hello" + (name ? " #{name}" : "") + ","
|
|
105
|
+
report << ""
|
|
106
|
+
report << "This is the Bacula Watcher program, reporting on the state of"
|
|
107
|
+
report << "the backup job, #{job}, for your computer."
|
|
108
|
+
report << ""
|
|
109
|
+
full = summarize(job, "F")
|
|
110
|
+
incr = summarize(job, "I")
|
|
111
|
+
status(job, full, incr)
|
|
112
|
+
report.join("\n")
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def query_job(job, level=nil)
|
|
116
|
+
query = "select starttime, jobfiles, jobbytes, level " +
|
|
117
|
+
"from Job where type='B' and jobstatus='T' and " +
|
|
118
|
+
"name='#{job}' and level='#{level}' order by starttime desc limit 1;"
|
|
119
|
+
if @dbms == "mysql"
|
|
120
|
+
@db.query(query).fetch_row
|
|
121
|
+
else
|
|
122
|
+
@db.exec(query).result[0]
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def summarize(job, level)
|
|
127
|
+
l = { "I" => "incremental", "F" => "full" }[level] || level
|
|
128
|
+
r = query_job(job, level)
|
|
129
|
+
if r == nil
|
|
130
|
+
report << " - There has never been a #{l} backup performed."
|
|
131
|
+
d = nil
|
|
132
|
+
else
|
|
133
|
+
t = Time.parse(r[0])
|
|
134
|
+
d = (Time.now - t).to_i
|
|
135
|
+
report <<
|
|
136
|
+
" - The last #{l} backup ran #{d.ago} ago,\n" +
|
|
137
|
+
" on #{t.strftime('%A, %B %d, %Y')}.\n" +
|
|
138
|
+
" It contained #{r[2].suffixize}bytes of data " +
|
|
139
|
+
"in #{r[1].commatize} files."
|
|
140
|
+
end
|
|
141
|
+
report << ""
|
|
142
|
+
return d
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def status(job, full, incr)
|
|
146
|
+
last = incr ? (full && full < incr ? full : incr) : full
|
|
147
|
+
err = []
|
|
148
|
+
if !full
|
|
149
|
+
err << " - The #{job} backup job has never run successfully."
|
|
150
|
+
else
|
|
151
|
+
err << " - The last full backup is too old." if full > 45.days
|
|
152
|
+
err << " - The most recent backup is too old." if last > 15.days
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
if err.empty?
|
|
156
|
+
@stat = "Okay"
|
|
157
|
+
report << "All seems well with the '#{job}' backup job."
|
|
158
|
+
else
|
|
159
|
+
@stat = "ERROR"
|
|
160
|
+
report << "There seems to be a PROBLEM with the '#{job}' backup job."
|
|
161
|
+
[ "", err, "" ].flatten.each do |e| report << e end
|
|
162
|
+
end
|
|
163
|
+
@@summary_msg << "#{stat} for #{job}; " +
|
|
164
|
+
"(Full: #{full.to_i.ago}; Last: #{last.to_i.ago})"
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def mail_to(addr, subject, msg)
|
|
169
|
+
mail = open("|mail -s '#{subject}' #{addr}", "w")
|
|
170
|
+
mail.puts msg
|
|
171
|
+
mail.close
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
if defined?($help) and $help
|
|
175
|
+
print <<-EOF
|
|
176
|
+
bacuwatch [-help] [-quiet] [-summary] [-notify] [-config <config-file>]
|
|
177
|
+
|
|
178
|
+
-help: Prints this help information.
|
|
179
|
+
-quiet: Disables printing the summary report to standard output.
|
|
180
|
+
-config: Specifies a configuration file.
|
|
181
|
+
-summary: Emails a summary report to each of the addresses
|
|
182
|
+
listed in a "summary" statement.
|
|
183
|
+
-notify: Emails a job report to the email address
|
|
184
|
+
listed in the "watch" statement for that job.
|
|
185
|
+
EOF
|
|
186
|
+
exit
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
if @db_args["dbms"] == "mysql"
|
|
190
|
+
db = Mysql.new(@db_args["host"],
|
|
191
|
+
@db_args["user"], @db_args["password"], @db_args["database"])
|
|
192
|
+
else
|
|
193
|
+
db = PGconn.new(@db_args["host"], 5432, "", "", @db_args["database"],
|
|
194
|
+
@db_args["user"], @db_args["password"])
|
|
195
|
+
end
|
|
196
|
+
@watched.each { |watch_info|
|
|
197
|
+
job, addr, name = watch_info
|
|
198
|
+
w = Watcher.new(db, @db_args["dbms"], job, name)
|
|
199
|
+
subject = "Bacula Watcher: #{w.stat} for #{job}."
|
|
200
|
+
if addr and defined?($notify) and $notify
|
|
201
|
+
mail_to(addr, subject, w.report)
|
|
202
|
+
end
|
|
203
|
+
}
|
|
204
|
+
db.close
|
|
205
|
+
|
|
206
|
+
if defined?($summary) and $summary
|
|
207
|
+
@summaries_to.each { |addr|
|
|
208
|
+
mail_to(addr, "Bacula Watch Summary Report", Watcher::summary)
|
|
209
|
+
}
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
if !defined?($quiet) or !$quiet
|
|
213
|
+
puts Watcher::summary
|
|
214
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
rubygems_version: 0.9.0
|
|
3
|
+
specification_version: 1
|
|
4
|
+
name: bacuwatch
|
|
5
|
+
version: !ruby/object:Gem::Version
|
|
6
|
+
version: "1.5"
|
|
7
|
+
date: 2006-12-03 00:00:00 -05:00
|
|
8
|
+
summary: An app to periodically report on a Bacula backup system.
|
|
9
|
+
require_paths:
|
|
10
|
+
- lib
|
|
11
|
+
email: john@kodis.org
|
|
12
|
+
homepage:
|
|
13
|
+
rubyforge_project:
|
|
14
|
+
description: Bacuwatch is an application normally run from a cron job to email out reports on the status of a series of bacula jobs.
|
|
15
|
+
autorequire:
|
|
16
|
+
default_executable:
|
|
17
|
+
bindir: bin
|
|
18
|
+
has_rdoc: false
|
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
|
20
|
+
requirements:
|
|
21
|
+
- - ">"
|
|
22
|
+
- !ruby/object:Gem::Version
|
|
23
|
+
version: 0.0.0
|
|
24
|
+
version:
|
|
25
|
+
platform: ruby
|
|
26
|
+
signing_key:
|
|
27
|
+
cert_chain:
|
|
28
|
+
post_install_message:
|
|
29
|
+
authors:
|
|
30
|
+
- John Kodis
|
|
31
|
+
files:
|
|
32
|
+
- bacuwatch/bacuwatch
|
|
33
|
+
- bacuwatch/bacuwatch.conf.template
|
|
34
|
+
- bacuwatch/README.bacuwatch
|
|
35
|
+
test_files: []
|
|
36
|
+
|
|
37
|
+
rdoc_options: []
|
|
38
|
+
|
|
39
|
+
extra_rdoc_files: []
|
|
40
|
+
|
|
41
|
+
executables:
|
|
42
|
+
- bacuwatch
|
|
43
|
+
extensions: []
|
|
44
|
+
|
|
45
|
+
requirements: []
|
|
46
|
+
|
|
47
|
+
dependencies: []
|
|
48
|
+
|