bacuwatch 1.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|