Package ZenReports :: Module ReportMail
[hide private]
[frames] | no frames]

Source Code for Module ZenReports.ReportMail

  1  ########################################################################### 
  2  # 
  3  # This program is part of Zenoss Core, an open source monitoring platform. 
  4  # Copyright (C) 2007, Zenoss Inc. 
  5  # 
  6  # This program is free software; you can redistribute it and/or modify it 
  7  # under the terms of the GNU General Public License version 2 as published by 
  8  # the Free Software Foundation. 
  9  # 
 10  # For complete information please visit: http://www.zenoss.com/oss/ 
 11  # 
 12  ########################################################################### 
 13   
 14  import sys 
 15  import base64 
 16  import urllib2 
 17  from HTMLParser import HTMLParser 
 18  from urlparse import urlparse, urlunparse 
 19  import mimetypes 
 20  from email.MIMEText import MIMEText 
 21  from email.MIMEMultipart import MIMEMultipart 
 22  from email.MIMEImage import MIMEImage 
 23   
 24  import Globals 
 25  from Products.ZenUtils.ZenScriptBase import ZenScriptBase 
 26  from Products.ZenUtils import Utils 
 27  import md5 
 28   
29 -def sibling(url, path):
30 parts = list(urlparse(url)) 31 parts[2] = '/'.join(parts[2].split('/')[:-1] + [path]) 32 return urlunparse(parts[0:3] + ['', '',''])
33
34 -class Page(HTMLParser):
35 """Turn an html page into a mime-encoded multi-part email. 36 Turn the <title> into the subject and keep only the text of the 37 content pane. Url references are turned into absolute references, 38 and images are sent with the page.""" 39
40 - def __init__(self, user, passwd):
41 HTMLParser.__init__(self) 42 self.user = user 43 self.passwd = passwd 44 self.html = [] 45 self.images = {} 46 self.contentPane = 0 47 self.inTitle = False 48 self.title = ''
49
50 - def fetchImage(self, url):
51 return self.slurp(url).read()
52
53 - def absolute(self, url):
54 url = url.strip() 55 if url.startswith('http:'): 56 return url 57 if url.startswith('/'): 58 base = list(urlparse(self.base)) 59 base[2] = url 60 return urlunparse(base[0:3] + ['', '','']) 61 return sibling(self.base, url)
62
63 - def alter(self, attrs, name, function):
64 result = [] 65 for a, v in attrs: 66 if a.lower() == name: 67 v = function(v) 68 result.append( (a, v) ) 69 return result
70
71 - def updateSrc(self, attrs):
72 def cache(v): 73 if v not in self.images: 74 v = self.absolute(v) 75 name = 'img%s.png' % md5.md5(v).hexdigest() 76 self.images[v] = (name, self.fetchImage(v)) 77 v, _ = self.images[v] 78 return 'cid:%s' % v
79 return self.alter(attrs, 'src', cache)
80
81 - def updateHref(self, attrs):
82 return self.alter(attrs, 'href', self.absolute)
83
84 - def handle_starttag(self, tag, attrs):
85 tag = tag.upper() 86 if tag == 'TITLE': 87 self.inTitle = True 88 if tag == 'IMG': 89 attrs = self.updateSrc(attrs) 90 if tag == 'A': 91 attrs = self.updateHref(attrs) 92 if tag == 'DIV': 93 if ('id','contentPane') in attrs: 94 self.contentPane = 1 95 elif self.contentPane: 96 self.contentPane += 1 97 if self.contentPane: 98 attrs = ' '.join([("%s=%s" % (a, repr(v))) for a, v in attrs]) 99 if attrs: attrs = ' ' + attrs 100 self.html.append('<%s%s>' % (tag, attrs))
101
102 - def handle_endtag(self, tag):
103 tag = tag.upper() 104 if tag == 'TITLE': 105 self.inTitle = False 106 if self.contentPane: 107 self.html.append('</%s>' % tag.upper()) 108 if tag == 'DIV': 109 if self.contentPane: 110 self.contentPane -= 1
111
112 - def handle_data(self, data):
113 if self.contentPane: 114 self.html.append(data) 115 if self.inTitle: 116 self.title += data
117
118 - def slurp(self, url):
119 self.base = url.strip() 120 req = urllib2.Request(url) 121 encoded = base64.encodestring('%s:%s' % (self.user, self.passwd))[:-1] 122 req.add_header("Authorization", "Basic %s" % encoded) 123 return urllib2.urlopen(req)
124
125 - def fetch(self, url):
126 self.feed(self.slurp(url).read())
127
128 - def mail(self):
129 msg = MIMEMultipart('related') 130 msg.preamble = 'This is a multi-part message in MIME format' 131 txt = MIMEText(''.join(self.html), 'html') 132 msg.attach(txt) 133 for url, (name, img) in self.images.items(): 134 ctype, encoding = mimetypes.guess_type(url) 135 if ctype == None: 136 ctype = 'image/png' 137 maintype, subtype = ctype.split('/', 1) 138 img = MIMEImage(img, subtype) 139 fname = url.rsplit('/', 1)[1] 140 img.add_header('Content-ID', '<%s>' % name) 141 msg.attach(img) 142 msg['Subject'] = self.title 143 return msg
144
145 -class NoDestinationAddressForUser(Exception): pass
146 -class UnknownUser(Exception): pass
147
148 -class ReportMail(ZenScriptBase):
149
150 - def run(self):
151 'Fetch a report by URL and post as a mime encoded email' 152 self.connect() 153 o = self.options 154 if not o.passwd and not o.url: 155 self.log.error("No zenoss password or url provided") 156 sys.exit(1) 157 try: 158 user = self.dmd.ZenUsers._getOb(o.user) 159 except AttributeError: 160 self.log.error("Unknown user %s" % o.user) 161 sys.exit(1) 162 163 if not o.addresses and user.email: 164 o.addresses = [user.email] 165 if not o.addresses: 166 self.log.error("No address for user %s" % o.user) 167 sys.exit(1) 168 page = Page(o.user, o.passwd) 169 page.fetch(o.url) 170 msg = page.mail() 171 if o.subject: 172 msg['Subject'] = o.subject 173 msg['From'] = o.fromAddress 174 msg['To'] = ', '.join(o.addresses) 175 result, errorMsg = Utils.sendEmail(msg, 176 self.dmd.smtpHost, 177 self.dmd.smtpPort, 178 self.dmd.smtpUseTLS, 179 self.dmd.smtpUser, 180 self.dmd.smtpPass) 181 if result: 182 self.log.debug("sent email: %s to:%s", msg.as_string(), o.addresses) 183 else: 184 self.log.info("failed to send email to %s: %s %s", 185 o.addresses, msg.as_string(), errorMsg) 186 sys.exit(1) 187 sys.exit(0)
188
189 - def buildOptions(self):
190 ZenScriptBase.buildOptions(self) 191 self.parser.add_option('--url', '-u', 192 dest='url', 193 default=None, 194 help='URL of report to send') 195 self.parser.add_option('--user', '-U', 196 dest='user', 197 default='admin', 198 help="User to log into Zenoss") 199 self.parser.add_option('--passwd', '-p', 200 dest='passwd', 201 help="Password to log into Zenoss") 202 self.parser.add_option('--address', '-a', 203 dest='addresses', 204 default=[], 205 action='append', 206 help='Email address destination ' 207 '(may be given more than once). Default value' 208 "comes from the user's profile.") 209 self.parser.add_option('--subject', '-s', 210 dest='subject', 211 default='', 212 help='Subject line for email message.' 213 'Default value is the title of the html page.') 214 self.parser.add_option('--from', '-f', 215 dest='fromAddress', 216 default='zenoss@localhost', 217 help='Origination address')
218 219 220 if __name__ == '__main__': 221 ReportMail().run() 222