~epylog/epylog/main

« back to all changes in this revision

Viewing changes to py/epylog/publishers.py

  • Committer: Frode Egeland
  • Date: 2009-03-28 01:37:12 UTC
  • Revision ID: egeland@gmail.com-20090328013712-m3kzz0zjzxhirhxk
Imported from http://archive.ubuntu.com/ubuntu/pool/universe/e/epylog/epylog_1.0.3-6.tar.gz source

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"""
 
2
This module is used to publish the report into a set of predefined
 
3
publisher classes. You can write your own, as long as they contain the
 
4
__init__ and publish methods.
 
5
"""
 
6
##
 
7
# Copyright (C) 2003 by Duke University
 
8
#
 
9
# This program is free software; you can redistribute it and/or
 
10
# modify it under the terms of the GNU General Public License
 
11
# as published by the Free Software Foundation; either version 2
 
12
# of the License, or (at your option) any later version.
 
13
#
 
14
# This program is distributed in the hope that it will be useful,
 
15
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
16
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
17
# GNU General Public License for more details.
 
18
#
 
19
# You should have received a copy of the GNU General Public License
 
20
# along with this program; if not, write to the Free Software
 
21
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 
22
# 02111-1307, USA.
 
23
#
 
24
# $Id: publishers.py,v 1.19.2.1 2004/02/09 20:31:42 icon Exp $
 
25
#
 
26
# @Author Konstantin Ryabitsev <icon@linux.duke.edu>
 
27
# @version $Date: 2004/02/09 20:31:42 $
 
28
#
 
29
 
 
30
import epylog
 
31
import os
 
32
import re
 
33
import socket
 
34
import time
 
35
import shutil
 
36
import gzip
 
37
import tempfile
 
38
 
 
39
if 'mkdtemp' not in dir(tempfile):
 
40
    ##
 
41
    # Must be python < 2.3
 
42
    #
 
43
    del tempfile
 
44
    import mytempfile as tempfile
 
45
 
 
46
def make_html_page(template, starttime, endtime, title, module_reports,
 
47
                   unparsed, logger):
 
48
    """
 
49
    Make a html page out of a set of parameters, which include
 
50
    module reports. Used by most, if not all, publishers.
 
51
    """
 
52
    logger.put(5, '>make_html_page')
 
53
    logger.put(3, 'Making a standard report page')
 
54
    fmtstr = re.sub(re.compile('%'), '%%', template)
 
55
    fmtstr = re.sub(re.compile('@@STARTTIME@@'), '%(starttime)s', fmtstr)
 
56
    fmtstr = re.sub(re.compile('@@ENDTIME@@'), '%(endtime)s', fmtstr)
 
57
    fmtstr = re.sub(re.compile('@@TITLE@@'), '%(title)s', fmtstr)
 
58
    fmtstr = re.sub(re.compile('@@HOSTNAME@@'), '%(hostname)s', fmtstr)
 
59
    fmtstr = re.sub(re.compile('@@MODULE_REPORTS@@'), '%(allrep)s', fmtstr)
 
60
    fmtstr = re.sub(re.compile('@@UNPARSED_STRINGS@@'), '%(unparsed)s', fmtstr)
 
61
    fmtstr = re.sub(re.compile('@@VERSION@@'), '%(version)s', fmtstr)
 
62
    logger.put(5, 'fmtstr=%s' % fmtstr)
 
63
 
 
64
    valumap = {}
 
65
    valumap['starttime'] = starttime
 
66
    valumap['endtime'] = endtime
 
67
    valumap['title'] = title
 
68
    valumap['hostname'] = socket.gethostname()
 
69
    
 
70
    logger.put(3, 'Concatenating the module reports together')
 
71
    allrep = ''
 
72
    for modrep in module_reports:
 
73
        logger.puthang(3, 'Processing report for "%s"' % modrep.name)
 
74
        allrep = '%s\n<h2>%s</h2>\n%s' % (allrep, modrep.name,
 
75
                                          modrep.htmlreport)
 
76
        logger.endhang(3)
 
77
    if allrep == '':
 
78
        allrep = 'No module reports'
 
79
    valumap['allrep'] = allrep
 
80
    
 
81
    if unparsed is not None:
 
82
        logger.put(3, 'Regexing <, > and &')
 
83
        unparsed = re.sub(re.compile('&'), '&amp;', unparsed)
 
84
        unparsed = re.sub(re.compile('<'), '&lt;',  unparsed)
 
85
        unparsed = re.sub(re.compile('>'), '&gt;',  unparsed)
 
86
        logger.put(3, 'Wrapping unparsed strings into <pre>')
 
87
        unparsed = '<pre>\n%s</pre>' % unparsed
 
88
    else:
 
89
        unparsed = 'No unparsed strings'
 
90
    valumap['unparsed'] = unparsed
 
91
    valumap['version'] = epylog.VERSION
 
92
    
 
93
    endpage = fmtstr % valumap
 
94
    logger.put(5, 'htmlreport follows:')
 
95
    logger.put(5, endpage)
 
96
    logger.put(5, '<make_html_page')
 
97
    return endpage
 
98
 
 
99
def do_chunked_gzip(infh, outfh, filename, logger):
 
100
    """
 
101
    A memory-friendly way of compressing the data.
 
102
    """
 
103
    gzfh = gzip.GzipFile('rawlogs', fileobj=outfh)
 
104
    bartotal = infh.tell()
 
105
    bardone = 0
 
106
    bartitle = 'Gzipping %s' % filename
 
107
    infh.seek(0)
 
108
    logger.put(3, 'Doing chunked read from infh into gzfh')
 
109
    while 1:
 
110
        chunk = infh.read(epylog.CHUNK_SIZE)
 
111
        if not chunk:
 
112
            logger.put(5, 'Reached EOF')
 
113
            break
 
114
        gzfh.write(chunk)
 
115
        bardone += len(chunk)
 
116
        logger.progressbar(1, bartitle, bardone, bartotal)
 
117
        logger.put(3, 'Wrote %d bytes' % len(chunk))
 
118
    gzfh.close()
 
119
    logger.endbar(1, bartitle, 'gzipped down to %d bytes' % outfh.tell())
 
120
 
 
121
def mail_smtp(smtpserv, fromaddr, toaddr, msg, logger):
 
122
    """
 
123
    Send mail using smtp.
 
124
    """
 
125
    logger.put(5, '>publishers.mail_smtp')
 
126
    import smtplib
 
127
    logger.puthang(3, 'Mailing it via the SMTP server %s' % smtpserv)
 
128
    server = smtplib.SMTP(smtpserv)
 
129
    server.sendmail(fromaddr, toaddr, msg)
 
130
    server.quit()
 
131
    logger.endhang(3)
 
132
    logger.put(5, '<publishers.mail_smtp')
 
133
 
 
134
def mail_sendmail(sendmail, msg, logger):
 
135
    """
 
136
    Send mail using sendmail.
 
137
    """
 
138
    logger.put(5, '>publishers.mail_sendmail')
 
139
    logger.puthang(3, 'Mailing the message via sendmail')
 
140
    p = os.popen(sendmail, 'w')
 
141
    p.write(msg)
 
142
    p.close()
 
143
    logger.endhang(3)
 
144
    logger.put(5, '<publishers.mail_sendmail')
 
145
    
 
146
class MailPublisher:
 
147
    """
 
148
    This publisher sends the results of an epylog run as an email message.
 
149
    """
 
150
    
 
151
    name = 'Mail Publisher'
 
152
    
 
153
    def __init__(self, sec, config, logger):
 
154
        logger.put(5, '>MailPublisher.__init__')
 
155
        self.logger = logger
 
156
        self.tmpprefix = config.tmpprefix
 
157
        self.section = sec
 
158
        logger.put(3, 'Looking for required elements in mail method config')
 
159
        try:
 
160
            mailto = config.get(self.section, 'mailto')
 
161
            addrs = mailto.split(',')
 
162
            self.mailto = []
 
163
            for addr in addrs:
 
164
                addr = addr.strip()
 
165
                logger.put(5, 'adding mailto=%s' % addr)
 
166
                self.mailto.append(addr)
 
167
        except: self.mailto = ['root']
 
168
 
 
169
        try: format = config.get(self.section, 'format')
 
170
        except: format = 'both'
 
171
 
 
172
        if (format != 'plain') and (format != 'html') and (format != 'both'):
 
173
            msg = ('Format for Mail Publisher must be either "html", "plain",'
 
174
                   + ' or "both." Format "%s" is unknown') % format
 
175
            raise epylog.ConfigError(msg, logger)
 
176
        self.format = format
 
177
 
 
178
        if format != 'html':
 
179
            logger.put(3, 'Plaintext version requested. Checking for lynx')
 
180
            try: lynx = config.get(self.section, 'lynx')
 
181
            except:
 
182
                lynx = '/usr/bin/lynx'
 
183
                if not os.access(lynx, os.X_OK):
 
184
                    msg = 'Could not find "%s"' % lynx
 
185
                    raise epylog.ConfigError(msg, logger)
 
186
            self.lynx = lynx
 
187
            logger.put(3, 'Lynx found in "%s" and is executable' % self.lynx)
 
188
 
 
189
        try:
 
190
            include_rawlogs = config.getboolean(self.section,'include_rawlogs')
 
191
        except: include_rawlogs = 1
 
192
 
 
193
        if include_rawlogs:
 
194
            try: rawlogs = int(config.get(self.section, 'rawlogs_limit'))
 
195
            except: rawlogs = 200
 
196
            self.rawlogs = rawlogs * 1024
 
197
        else: self.rawlogs = 0
 
198
        
 
199
        try: self.smtpserv = config.get(self.section, 'smtpserv')
 
200
        except: self.smtpserv = 'localhost'
 
201
 
 
202
        logger.put(5, 'format=%s' % self.format)
 
203
        logger.put(5, 'rawlogs=%d' % self.rawlogs)
 
204
        logger.put(5, 'smtpserv=%s' % self.smtpserv)
 
205
        
 
206
        logger.put(5, '<MailPublisher.__init__')
 
207
        
 
208
    def publish(self, template, starttime, endtime, title, module_reports,
 
209
                unparsed_strings, rawfh):
 
210
        logger = self.logger
 
211
        logger.put(5, '>MailPublisher.publish')
 
212
        logger.puthang(3, 'Creating a standard html page report')
 
213
        html_report = make_html_page(template, starttime, endtime, title,
 
214
                                     module_reports, unparsed_strings, logger)
 
215
        self.htmlrep = html_report
 
216
        logger.endhang(3)
 
217
 
 
218
        self.plainrep = None
 
219
        if self.format != 'html':
 
220
            tempfile.tempdir = self.tmpprefix
 
221
            logger.puthang(3, 'Creating a plaintext format of the report')
 
222
            htmlfile = tempfile.mktemp('.html')
 
223
            tfh = open(htmlfile, 'w')
 
224
            tfh.write(html_report)
 
225
            tfh.close()
 
226
            logger.put(3, 'HTML report is in "%s"' % htmlfile)
 
227
            plainfile = tempfile.mktemp('PLAIN')
 
228
            logger.put(3, 'PLAIN report will go into "%s"' % plainfile)
 
229
            logger.put(3, 'Making a syscall to "%s"' % self.lynx)
 
230
            exitcode = os.system('%s -dump %s > %s 2>/dev/null'
 
231
                                 % (self.lynx, htmlfile, plainfile))
 
232
            if exitcode or not os.access(plainfile, os.R_OK):
 
233
                msg = 'Error making a call to "%s"' % self.lynx
 
234
                raise epylog.SysCallError(msg, logger)
 
235
            logger.puthang(3, 'Reading in the plain version')
 
236
            tfh = open(plainfile)
 
237
            self.plainrep = tfh.read()
 
238
            tfh.close()
 
239
            logger.put(5, 'plainrep follows:')
 
240
            logger.put(5, self.plainrep)
 
241
            logger.endhang(3)
 
242
            logger.endhang(3)
 
243
 
 
244
        if self.rawlogs:
 
245
            ##
 
246
            # GzipFile doesn't work with StringIO. :/ Bleh.
 
247
            #
 
248
            tempfile.tempdir = self.tmpprefix
 
249
            outfh = open(tempfile.mktemp('GZIP'), 'w+')
 
250
            do_chunked_gzip(rawfh, outfh, 'rawlogs', logger)
 
251
            size = outfh.tell()
 
252
            if size > self.rawlogs:
 
253
                logger.put(1, '%d is over the defined max of "%d"'
 
254
                           % (size, self.rawlogs))
 
255
                logger.put(1, 'Not attaching the raw logs')
 
256
                self.rawlogs = 0
 
257
            else:
 
258
                logger.put(5, 'Reading in the gzipped logs')
 
259
                outfh.seek(0)
 
260
                self.gzlogs = outfh.read()
 
261
            outfh.close()
 
262
            
 
263
        ##
 
264
        # Using MimeWriter, since package 'email' doesn't come with rhl-7.3
 
265
        # Suck-o.
 
266
        #
 
267
        logger.puthang(3, 'Creating an email message')
 
268
        import StringIO, MimeWriter
 
269
        fh = StringIO.StringIO()
 
270
        logger.put(5, 'Creating a main header')
 
271
        mw = MimeWriter.MimeWriter(fh)
 
272
        mw.addheader('Subject', title)
 
273
        if len(self.mailto) > 1:
 
274
            import string
 
275
            tostr = string.join(self.mailto, ', ')
 
276
        else:
 
277
            tostr = self.mailto[0]
 
278
        mw.addheader('To', tostr)
 
279
        mw.addheader('X-Mailer', epylog.VERSION)
 
280
        self.mw = mw
 
281
        
 
282
        if self.rawlogs > 0 and self.format == 'both':
 
283
            logger.put(5, 'Making a html + plain + gzip message')
 
284
            self._mk_both_rawlogs()
 
285
        elif self.rawlogs > 0 and self.format == 'html':
 
286
            logger.put(5, 'Making a html + gzip message')
 
287
            self._mk_html_rawlogs()
 
288
        elif self.rawlogs > 0 and self.format == 'plain':
 
289
            logger.put(5, 'Making a plain + gzip message')
 
290
            self._mk_plain_rawlogs()
 
291
        elif self.rawlogs == 0 and self.format == 'both':
 
292
            logger.put(5, 'Making a html + plain message')
 
293
            self._mk_both_nologs()
 
294
        elif self.rawlogs == 0 and self.format == 'html':
 
295
            logger.put(5, 'Making a html message')
 
296
            self._mk_html_nologs()
 
297
        elif self.rawlogs == 0 and self.format == 'plain':
 
298
            logger.put(5, 'Making a plain message')
 
299
            self._mk_plain_nologs()
 
300
        logger.endhang(3)
 
301
 
 
302
        fh.seek(0)
 
303
        msg = fh.read()
 
304
        fh.close()
 
305
        logger.put(5, 'Message follows')
 
306
        logger.put(5, msg)
 
307
        logger.put(5, 'End of message')
 
308
 
 
309
        logger.put(3, 'Figuring out if we are using sendmail or smtplib')
 
310
        if re.compile('^/').search(self.smtpserv):
 
311
            mail_sendmail(self.smtpserv, msg, logger)
 
312
        else:
 
313
            fromaddr = 'root@%s' % socket.gethostname()
 
314
            mail_smtp(self.smtpserv, fromaddr, self.mailto, msg, logger)
 
315
        logger.put(1, 'Mailed the report to: %s' % tostr)
 
316
        logger.put(5, '<MailPublisher.publish')
 
317
 
 
318
    def _mk_both_rawlogs(self):
 
319
        """
 
320
        Make an email message that includes html, plaintext, and gzipped raw
 
321
        logs sections. Most painful.
 
322
        """
 
323
        self.logger.put(5, '>MailPublisher._mk_both_rawlogs')
 
324
        import base64
 
325
        logger = self.logger
 
326
        mixed_mw = self.mw
 
327
        mixed_mw.addheader('Mime-Version', '1.0')
 
328
        logger.put(5, 'Creating a multipart/mixed part')
 
329
        mixed_mw.startmultipartbody('mixed')
 
330
 
 
331
        logger.put(5, 'Creating a multipart/alternative part')
 
332
        alt_mw = mixed_mw.nextpart()
 
333
        alt_mw.startmultipartbody('alternative')
 
334
 
 
335
        logger.put(5, 'Creating a text/plain part')
 
336
        plain_mw = alt_mw.nextpart()
 
337
        plain_mw.addheader('Content-Transfer-Encoding', '8bit')
 
338
        plain_fh = plain_mw.startbody('text/plain; charset=iso-8859-1')
 
339
        plain_fh.write(self.plainrep)
 
340
 
 
341
        logger.put(5, 'Creating a text/html part')
 
342
        html_mw = alt_mw.nextpart()
 
343
        html_mw.addheader('Content-Transfer-Encoding', '8bit')
 
344
        html_fh = html_mw.startbody('text/html; charset=iso-8859-1')
 
345
        html_fh.write(self.htmlrep)
 
346
 
 
347
        alt_mw.lastpart()
 
348
        logger.put(5, 'Creating an application/gzip part')
 
349
        gzip_mw = mixed_mw.nextpart()
 
350
        gzip_mw.addheader('Content-Transfer-Encoding', 'base64')
 
351
        gzip_mw.addheader('Content-Disposition',
 
352
                          'attachment; filename=rawlogs.gz')
 
353
        gzip_fh = gzip_mw.startbody('application/gzip; NAME=rawlogs.gz')
 
354
        gzip_fh.write(base64.encodestring(self.gzlogs))
 
355
        mixed_mw.lastpart()
 
356
        self.logger.put(5, '<MailPublisher._mk_both_rawlogs')
 
357
 
 
358
    def _mk_html_rawlogs(self):
 
359
        """
 
360
        Make an email message that includes html and gzipped raw logs sections.
 
361
        """
 
362
        self.logger.put(5, '>MailPublisher._mk_html_rawlogs')
 
363
        import base64
 
364
        logger = self.logger
 
365
        mixed_mw = self.mw
 
366
        mixed_mw.addheader('Mime-Version', '1.0')
 
367
        logger.put(5, 'Creating a multipart/mixed part')
 
368
        mixed_mw.startmultipartbody('mixed')
 
369
 
 
370
        logger.put(5, 'Creating a text/html part')
 
371
        html_mw = mixed_mw.nextpart()
 
372
        html_mw.addheader('Content-Transfer-Encoding', '8bit')
 
373
        html_fh = html_mw.startbody('text/html; charset=iso-8859-1')
 
374
        html_fh.write(self.htmlrep)
 
375
 
 
376
        logger.put(5, 'Creating an application/gzip part')
 
377
        gzip_mw = mixed_mw.nextpart()
 
378
        gzip_mw.addheader('Content-Transfer-Encoding', 'base64')
 
379
        gzip_mw.addheader('Content-Disposition',
 
380
                          'attachment; filename=rawlogs.gz')
 
381
        gzip_fh = gzip_mw.startbody('application/gzip; NAME=rawlogs.gz')
 
382
        gzip_fh.write(base64.encodestring(self.gzlogs))
 
383
        mixed_mw.lastpart()
 
384
        self.logger.put(5, '<MailPublisher._mk_html_rawlogs')
 
385
 
 
386
    def _mk_plain_rawlogs(self):
 
387
        """
 
388
        Make an email message that includes plaintext and gzipped raw logs
 
389
        sections.
 
390
        """
 
391
        self.logger.put(5, '>MailPublisher._mk_plain_rawlogs')
 
392
        import base64
 
393
        logger = self.logger
 
394
        mixed_mw = self.mw
 
395
        mixed_mw.addheader('Mime-Version', '1.0')
 
396
        logger.put(5, 'Creating a multipart/mixed part')
 
397
        mixed_mw.startmultipartbody('mixed')
 
398
 
 
399
        logger.put(5, 'Creating a text/plain part')
 
400
        plain_mw = mixed_mw.nextpart()
 
401
        plain_mw.addheader('Content-Transfer-Encoding', '8bit')
 
402
        plain_fh = plain_mw.startbody('text/plain; charset=iso-8859-1')
 
403
        plain_fh.write(self.plainrep)
 
404
 
 
405
        logger.put(5, 'Creating an application/gzip part')
 
406
        gzip_mw = mixed_mw.nextpart()
 
407
        gzip_mw.addheader('Content-Transfer-Encoding', 'base64')
 
408
        gzip_mw.addheader('Content-Disposition',
 
409
                          'attachment; filename=rawlogs.gz')
 
410
        gzip_fh = gzip_mw.startbody('application/gzip; NAME=rawlogs.gz')
 
411
        gzip_fh.write(base64.encodestring(self.gzlogs))
 
412
        mixed_mw.lastpart()
 
413
        self.logger.put(5, '<MailPublisher._mk_plain_rawlogs')
 
414
 
 
415
    def _mk_both_nologs(self):
 
416
        """
 
417
        Make a message that just includes html and plaintext sections.
 
418
        """
 
419
        self.logger.put(5, '>MailPublisher._mk_both_nologs')
 
420
        logger = self.logger
 
421
        alt_mw = self.mw
 
422
        alt_mw.addheader('Mime-Version', '1.0')
 
423
        logger.put(5, 'Creating a multipart/alternative part')
 
424
        alt_mw.startmultipartbody('alternative')
 
425
 
 
426
        logger.put(5, 'Creating a text/plain part')
 
427
        plain_mw = alt_mw.nextpart()
 
428
        plain_mw.addheader('Content-Transfer-Encoding', '8bit')
 
429
        plain_fh = plain_mw.startbody('text/plain; charset=iso-8859-1')
 
430
        plain_fh.write(self.plainrep)
 
431
 
 
432
        logger.put(5, 'Creating a text/html part')
 
433
        html_mw = alt_mw.nextpart()
 
434
        html_mw.addheader('Content-Transfer-Encoding', '8bit')
 
435
        html_fh = html_mw.startbody('text/html; charset=iso-8859-1')
 
436
        html_fh.write(self.htmlrep)
 
437
 
 
438
        alt_mw.lastpart()
 
439
        self.logger.put(5, '<MailPublisher._mk_both_nologs')
 
440
 
 
441
    def _mk_html_nologs(self):
 
442
        """
 
443
        Make a message that just includes HTML-formatted section.
 
444
        """
 
445
        self.logger.put(5, '>MailPublisher._mk_html_nologs')
 
446
        logger = self.logger
 
447
        alt_mw = self.mw
 
448
        alt_mw.addheader('Mime-Version', '1.0')
 
449
        logger.put(5, 'Creating a multipart/alternative part')
 
450
        alt_mw.startmultipartbody('alternative')
 
451
        logger.put(5, 'Creating a text/html part')
 
452
        html_mw = alt_mw.nextpart()
 
453
        html_mw.addheader('Content-Transfer-Encoding', '8bit')
 
454
        html_fh = html_mw.startbody('text/html; charset=iso-8859-1')
 
455
        html_fh.write(self.htmlrep)
 
456
        alt_mw.lastpart()
 
457
        self.logger.put(5, '<MailPublisher._mk_html_nologs')
 
458
 
 
459
    def _mk_plain_nologs(self):
 
460
        """
 
461
        Make a message that just includes a plaintext-formatted section.
 
462
        """
 
463
        self.logger.put(5, '>MailPublisher._mk_plain_nologs')
 
464
        logger = self.logger
 
465
        plain_mw = self.mw
 
466
        logger.put(5, 'Creating a text/plain part')
 
467
        plain_mw.addheader('Content-Transfer-Encoding', '8bit')
 
468
        plain_fh = plain_mw.startbody('text/plain; charset=iso-8859-1')
 
469
        plain_fh.write(self.plainrep)
 
470
        self.logger.put(5, '<MailPublisher._mk_plain_nologs')
 
471
 
 
472
 
 
473
class FilePublisher:
 
474
    """
 
475
    FilePublisher publishes the results of an Epylog run into a set of files
 
476
    and directories on the hard drive.
 
477
    """
 
478
    name = 'File Publisher'
 
479
    def __init__(self, sec, config, logger):
 
480
        logger.put(5, '>FilePublisher.__init__')
 
481
        self.logger = logger
 
482
        self.tmpprefix = config.tmpprefix
 
483
        logger.put(3, 'Looking for required elements in file method config')
 
484
        msg = 'Required attribute "%s" not found'
 
485
        try: expire = int(config.get(sec, 'expire_in'))
 
486
        except: epylog.ConfigError(msg % 'expire_in', logger)
 
487
        
 
488
        try: dirmask = config.get(sec, 'dirmask')
 
489
        except: epylog.ConfigError(msg % 'dirmask', logger)
 
490
        try: filemask = config.get(sec, 'filemask')
 
491
        except: epylog.ConfigError(msg % 'filemask', logger)
 
492
 
 
493
        logger.put(3, 'Verifying dirmask and filemask')
 
494
        msg = 'Invalid mask for %s: %s'
 
495
        try: self.dirname = time.strftime(dirmask, time.localtime())
 
496
        except: epylog.ConfigError(msg % ('dirmask', dirmask), logger)
 
497
        try: path = config.get(sec, 'path')
 
498
        except: epylog.ConfigError(msg % 'path', logger)
 
499
        try: self.filename = time.strftime(filemask, time.localtime())
 
500
        except: epylog.ConfigError(msg % ('filemask', filemask), logger)
 
501
        self._prune_old(path, dirmask, expire)
 
502
        self.path = os.path.join(path, self.dirname)
 
503
 
 
504
        logger.put(3, 'Checking if notify is set')
 
505
        self.notify = []
 
506
        try:
 
507
            notify = config.get(sec, 'notify')
 
508
            for addy in notify.split(','):
 
509
                addy = addy.strip()
 
510
                logger.put(3, 'Will notify: %s' % addy)
 
511
                self.notify.append(addy)
 
512
        except: pass
 
513
        try: self.smtpserv = config.get(sec, 'smtpserv')
 
514
        except: self.smtpserv = '/usr/sbin/sendmail -t'
 
515
        if self.notify:
 
516
            try:
 
517
                self.pubroot = config.get(sec, 'pubroot')
 
518
                logger.put(5, 'pubroot=%s' % self.pubroot)
 
519
            except:
 
520
                msg = 'File publisher requires a pubroot when notify is set'
 
521
                raise epylog.ConfigError(msg, logger)
 
522
        
 
523
        logger.put(5, 'path=%s' % self.path)
 
524
        logger.put(5, 'filename=%s' % self.filename)
 
525
        logger.put(5, '<FilePublisher.__init__')
 
526
 
 
527
    def _prune_old(self, path, dirmask, expire):
 
528
        """
 
529
        Removes the directories that are older than a certain date.
 
530
        """
 
531
        logger = self.logger
 
532
        logger.put(5, '>FilePublisher._prune_old')
 
533
        logger.put(3, 'Pruning directories older than %d days' % expire)
 
534
        expire_limit = int(time.time()) - (86400 * expire)
 
535
        logger.put(5, 'expire_limit=%d' % expire_limit)
 
536
        if not os.path.isdir(path):
 
537
            logger.put(3, 'Dir %s not found -- skipping pruning' % path)
 
538
            logger.put(5, '<FilePublisher._prune_old')
 
539
            return
 
540
        for entry in os.listdir(path):
 
541
            logger.put(5, 'Found: %s' % entry)
 
542
            if os.path.isdir(os.path.join(path, entry)):
 
543
                logger.put(3, 'Found directory %s' % entry)
 
544
                logger.put(4, 'Trying to strptime it into a timestamp')
 
545
                try: stamp = time.mktime(time.strptime(entry, dirmask))
 
546
                except ValueError, e:
 
547
                    logger.put(3, 'Dir %s did not match dirmask %s: %s'
 
548
                               % (entry, dirmask, e))
 
549
                    logger.put(3, 'Skipping %s' % entry)
 
550
                    continue
 
551
                if stamp < expire_limit:
 
552
                    logger.put(3, '%s is older than expire limit')
 
553
                    shutil.rmtree(os.path.join(path, entry))
 
554
                    logger.put(1, 'File Publisher: Pruned old directory: %s'
 
555
                               % entry)
 
556
                else:
 
557
                    logger.put(3, '%s is still active' % entry)
 
558
            else:
 
559
                logger.put(3, '%s is not a directory. Skipping.' % entry)
 
560
        logger.put(3, 'Finished with pruning')
 
561
        logger.put(5, '<FilePublisher._prune_old')
 
562
        
 
563
    def publish(self, template, starttime, endtime, title, module_reports,
 
564
                unparsed_strings, rawfh):
 
565
        logger = self.logger
 
566
        logger.put(5, '>FilePublisher.publish')
 
567
        logger.put(3, 'Checking and creating the report directories')
 
568
        if not os.path.isdir(self.path):
 
569
            try: os.makedirs(self.path)
 
570
            except OSError, e:
 
571
                logger.put(0, 'Error creating directory "%s": %s' %
 
572
                           (self.path, e))
 
573
                logger.put(0, 'File publisher exiting.')
 
574
                return
 
575
        logger.puthang(3, 'Creating a standard html page report')
 
576
        html_report = make_html_page(template, starttime, endtime, title,
 
577
                                     module_reports, unparsed_strings, logger)
 
578
        logger.endhang(3)
 
579
        filename = '%s.html' % self.filename
 
580
        repfile = os.path.join(self.path, filename)
 
581
        logger.put(3, 'Dumping the report into %s' % repfile)
 
582
        fh = open(repfile, 'w')
 
583
        fh.write(html_report)
 
584
        fh.close()
 
585
        logger.put(1, 'Report saved in: %s' % self.path)
 
586
        if self.notify:
 
587
            logger.puthang(3, 'Creating an email message')
 
588
            publoc = '%s/%s/%s' % (self.pubroot, self.dirname, filename)
 
589
            msg = 'New Epylog report is available at:\r\n%s' % publoc
 
590
            import StringIO, MimeWriter
 
591
            fh = StringIO.StringIO()
 
592
            logger.put(3, 'Creating a main header')
 
593
            mw = MimeWriter.MimeWriter(fh)
 
594
            mw.addheader('Subject', '%s (report notification)' % title)
 
595
            tostr = ', '.join(self.notify)
 
596
            mw.addheader('To', tostr)
 
597
            mw.addheader('X-Mailer', epylog.VERSION)
 
598
            mw.addheader('Content-Transfer-Encoding', '8bit')
 
599
            bfh = mw.startbody('text/plain; charset=iso-8859-1')
 
600
            bfh.write(msg)
 
601
            fh.seek(0)
 
602
            msg = fh.read()
 
603
            fh.close()
 
604
            logger.put(3, 'Figuring out if we are using sendmail or smtplib')
 
605
            if re.compile('^/').search(self.smtpserv):
 
606
                mail_sendmail(self.smtpserv, msg, logger)
 
607
            else:
 
608
                fromaddr = 'root@%s' % socket.gethostname()
 
609
                mail_smtp(self.smtpserv, fromaddr, self.notify, msg, logger)
 
610
            logger.put(1, 'Notification mailed to: %s' % tostr)
 
611
 
 
612
        logfilen = '%s.log' % self.filename
 
613
        logfile = os.path.join(self.path, '%s.gz' % logfilen)
 
614
        logger.put(3, 'Gzipping logs and writing them to %s' % logfilen)
 
615
        outfh = open(logfile, 'w+')
 
616
        do_chunked_gzip(rawfh, outfh, logfilen, logger)
 
617
        outfh.close()
 
618
        logger.put(1, 'Gzipped logs saved in: %s' % self.path)
 
619
        logger.put(5, '<FilePublisher.publish')