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, div):
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 self.div = div
50
51 - def fetchImage(self, url):
52 return self.slurp(url).read()
53
54 - def absolute(self, url):
55 url = url.strip() 56 if url.startswith('http:'): 57 return url 58 if url.startswith('/'): 59 base = list(urlparse(self.base)) 60 base[2] = url 61 return urlunparse(base[0:3] + ['', '','']) 62 return sibling(self.base, url)
63
64 - def alter(self, attrs, name, function):
65 result = [] 66 for a, v in attrs: 67 if a.lower() == name: 68 v = function(v) 69 result.append( (a, v) ) 70 return result
71
72 - def updateSrc(self, attrs):
73 def cache(v): 74 if v not in self.images: 75 v = self.absolute(v) 76 name = 'img%s.png' % md5.md5(v).hexdigest() 77 self.images[v] = (name, self.fetchImage(v)) 78 v, _ = self.images[v] 79 return 'cid:%s' % v
80 return self.alter(attrs, 'src', cache)
81
82 - def updateHref(self, attrs):
83 return self.alter(attrs, 'href', self.absolute)
84
85 - def handle_starttag(self, tag, attrs):
86 tag = tag.upper() 87 if tag == 'TITLE': 88 self.inTitle = True 89 if tag == 'IMG': 90 attrs = self.updateSrc(attrs) 91 if tag == 'A': 92 attrs = self.updateHref(attrs) 93 if tag == 'DIV': 94 if ('id',self.div) in attrs: 95 self.contentPane = 1 96 elif self.contentPane: 97 self.contentPane += 1 98 if self.contentPane: 99 attrs = ' '.join([("%s=%s" % (a, repr(v))) for a, v in attrs]) 100 if attrs: attrs = ' ' + attrs 101 self.html.append('<%s%s>' % (tag, attrs))
102
103 - def handle_endtag(self, tag):
104 tag = tag.upper() 105 if tag == 'TITLE': 106 self.inTitle = False 107 if self.contentPane: 108 self.html.append('</%s>' % tag.upper()) 109 if tag == 'DIV': 110 if self.contentPane: 111 self.contentPane -= 1
112
113 - def handle_data(self, data):
114 if self.contentPane: 115 self.html.append(data) 116 if self.inTitle: 117 self.title += data
118
119 - def slurp(self, url):
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 try: 124 result = urllib2.urlopen(req) 125 except urllib2.HTTPError: 126 import StringIO 127 result = StringIO.StringIO('') 128 return result
129
130 - def fetch(self, url):
131 self.base = url.strip() 132 self.feed(self.slurp(url).read())
133
134 - def mail(self):
135 msg = MIMEMultipart('related') 136 msg.preamble = 'This is a multi-part message in MIME format' 137 txt = MIMEText(''.join(self.html), 'html') 138 msg.attach(txt) 139 for url, (name, img) in self.images.items(): 140 ctype, encoding = mimetypes.guess_type(url) 141 if ctype == None: 142 ctype = 'image/png' 143 maintype, subtype = ctype.split('/', 1) 144 img = MIMEImage(img, subtype) 145 img.add_header('Content-ID', '<%s>' % name) 146 msg.attach(img) 147 msg['Subject'] = self.title 148 return msg
149
150 -class NoDestinationAddressForUser(Exception): pass
151 -class UnknownUser(Exception): pass
152
153 -class ReportMail(ZenScriptBase):
154
155 - def run(self):
156 'Fetch a report by URL and post as a mime encoded email' 157 self.connect() 158 o = self.options 159 if not o.passwd and not o.url: 160 self.log.error("No zenoss password or url provided") 161 sys.exit(1) 162 try: 163 user = self.dmd.ZenUsers._getOb(o.user) 164 except AttributeError: 165 self.log.error("Unknown user %s" % o.user) 166 sys.exit(1) 167 168 if not o.addresses and user.email: 169 o.addresses = [user.email] 170 if not o.addresses: 171 self.log.error("No address for user %s" % o.user) 172 sys.exit(1) 173 page = Page(o.user, o.passwd, o.div) 174 page.fetch(o.url) 175 msg = page.mail() 176 if o.subject: 177 msg['Subject'] = o.subject 178 msg['From'] = o.fromAddress 179 msg['To'] = ', '.join(o.addresses) 180 result, errorMsg = Utils.sendEmail(msg, 181 self.dmd.smtpHost, 182 self.dmd.smtpPort, 183 self.dmd.smtpUseTLS, 184 self.dmd.smtpUser, 185 self.dmd.smtpPass) 186 if result: 187 self.log.debug("sent email: %s to:%s", msg.as_string(), o.addresses) 188 else: 189 self.log.info("failed to send email to %s: %s %s", 190 o.addresses, msg.as_string(), errorMsg) 191 sys.exit(1) 192 sys.exit(0)
193
194 - def buildOptions(self):
195 ZenScriptBase.buildOptions(self) 196 self.parser.add_option('--url', '-u', 197 dest='url', 198 default=None, 199 help='URL of report to send') 200 self.parser.add_option('--user', '-U', 201 dest='user', 202 default='admin', 203 help="User to log into Zenoss") 204 self.parser.add_option('--passwd', '-p', 205 dest='passwd', 206 help="Password to log into Zenoss") 207 self.parser.add_option('--address', '-a', 208 dest='addresses', 209 default=[], 210 action='append', 211 help='Email address destination ' 212 '(may be given more than once). Default value' 213 "comes from the user's profile.") 214 self.parser.add_option('--subject', '-s', 215 dest='subject', 216 default='', 217 help='Subject line for email message.' 218 'Default value is the title of the html page.') 219 self.parser.add_option('--from', '-f', 220 dest='fromAddress', 221 default='zenoss@localhost', 222 help='Origination address') 223 self.parser.add_option('--div', '-d', 224 dest='div', 225 default='contentPane', 226 help='DIV to extract from URL')
227 228 229 if __name__ == '__main__': 230 ReportMail().run() 231