1
2
3
4
5
6
7
8
9
10
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 from email.MIMEBase import MIMEBase
24
25 import Globals
26 from Products.ZenUtils.ZenScriptBase import ZenScriptBase
27 from Products.ZenUtils import Utils
28 import md5
29
31 parts = list(urlparse(url))
32 parts[2] = '/'.join(parts[2].split('/')[:-1] + [path])
33 return urlunparse(parts[0:3] + ['', '',''])
34
35 -class Page(HTMLParser):
36 """Turn an html page into a mime-encoded multi-part email.
37 Turn the <title> into the subject and keep only the text of the
38 content pane. Url references are turned into absolute references,
39 and images are sent with the page."""
40
41 - def __init__(self, user, passwd, div, comment):
42 HTMLParser.__init__(self)
43 self.user = user
44 self.passwd = passwd
45 self.html = []
46 self.images = {}
47 self.contentPane = 0
48 self.inTitle = False
49 self.title = ''
50 self.div = div
51 self.comment = comment
52 self.csv = None
53
54 - def fetchImage(self, url):
55 return self.slurp(url).read()
56
57 - def absolute(self, url):
58 url = url.strip()
59 if url.startswith('http'):
60 return url
61 if url.startswith('/'):
62 base = list(urlparse(self.base))
63 base[2] = url
64 return urlunparse(base[0:3] + ['', '',''])
65 return sibling(self.base, url)
66
67 - def alter(self, attrs, name, function):
68 result = []
69 for a, v in attrs:
70 if a.lower() == name:
71 v = function(v)
72 result.append( (a, v) )
73 return result
74
75 - def updateSrc(self, attrs):
76 def cache(v):
77 if v not in self.images:
78 v = self.absolute(v)
79 name = 'img%s.png' % md5.md5(v).hexdigest()
80 self.images[v] = (name, self.fetchImage(v))
81 v, _ = self.images[v]
82 return 'cid:%s' % v
83 return self.alter(attrs, 'src', cache)
84
85 - def updateHref(self, attrs):
86 return self.alter(attrs, 'href', self.absolute)
87
88 - def handle_starttag(self, tag, attrs):
89 tag = tag.upper()
90 if tag == 'TITLE':
91 self.inTitle = True
92 if tag == 'IMG':
93 attrs = self.updateSrc(attrs)
94 if tag == 'A':
95 attrs = self.updateHref(attrs)
96 if tag == 'DIV':
97 if ('id',self.div) in attrs:
98 self.contentPane = 1
99 elif self.contentPane:
100 self.contentPane += 1
101 if self.contentPane:
102 attrs = ' '.join(("%s=%s" % (a, repr(v))) for a, v in attrs)
103 if attrs: attrs = ' ' + attrs
104 self.html.append('<%s%s>' % (tag, attrs))
105
106 - def handle_endtag(self, tag):
107 tag = tag.upper()
108 if tag == 'TITLE':
109 self.inTitle = False
110 if self.contentPane:
111 self.html.append('</%s>' % tag.upper())
112 if tag == 'DIV':
113 if self.contentPane:
114 self.contentPane -= 1
115
116 - def handle_data(self, data):
117 if self.contentPane:
118 self.html.append(data)
119 if self.inTitle:
120 self.title += data
121
122 - def handleCSV(self, data):
124
125 - def slurp(self, url):
126 req = urllib2.Request(url)
127 encoded = base64.encodestring('%s:%s' % (self.user, self.passwd))[:-1]
128 req.add_header("Authorization", "Basic %s" % encoded)
129 try:
130 result = urllib2.urlopen(req)
131 except urllib2.HTTPError:
132 import StringIO
133 result = StringIO.StringIO('')
134 return result
135
136 - def fetch(self, url):
137 url = url.replace(' ', '%20')
138 self.base = url.strip()
139 response = self.slurp(url)
140
141
142 if hasattr(response, 'headers') and \
143 response.headers.get('Content-Type') == 'application/vnd.ms-excel':
144 self.handleCSV(response.read())
145 else:
146
147 self.feed(response.read())
148
150 msg = MIMEMultipart('related')
151 msg.preamble = 'This is a multi-part message in MIME format'
152 if self.csv is not None:
153 txt = MIMEText(self.comment, 'plain')
154 msg.attach(txt)
155 csv = MIMEBase('application', 'vnd.ms-excel')
156 csv.add_header('Content-ID', '<Zenoss Report>')
157 csv.add_header('Content-Disposition', 'attachment',
158 filename='zenoss_report.csv')
159 csv.set_payload(self.csv)
160 msg.attach(csv)
161 else:
162 txt = MIMEText(''.join(self.html), 'html')
163 msg.attach(txt)
164 for url, (name, img) in self.images.items():
165 ctype, encoding = mimetypes.guess_type(url)
166 if ctype == None:
167 ctype = 'image/png'
168 maintype, subtype = ctype.split('/', 1)
169 img = MIMEImage(img, subtype)
170 img.add_header('Content-ID', '<%s>' % name)
171 msg.attach(img)
172 return msg
173
176
178
180 'Fetch a report by URL and post as a mime encoded email'
181 self.connect()
182 o = self.options
183 if not o.passwd and not o.url:
184 self.log.error("No zenoss password or url provided")
185 sys.exit(1)
186 try:
187 user = self.dmd.ZenUsers._getOb(o.user)
188 except AttributeError:
189 self.log.error("Unknown user %s" % o.user)
190 sys.exit(1)
191
192 if not o.addresses and user.email:
193 o.addresses = [user.email]
194 if not o.addresses:
195 self.log.error("No address for user %s" % o.user)
196 sys.exit(1)
197 page = Page(o.user, o.passwd, o.div, o.comment)
198 url = self.mangleUrl(o.url)
199 page.fetch(url)
200 msg = page.mail()
201 if o.subject:
202 msg['Subject'] = o.subject
203 elif page.title:
204 msg['Subject'] = page.title
205 else:
206 msg['Subject'] = 'Zenoss Report'
207 msg['From'] = o.fromAddress
208 msg['To'] = ', '.join(o.addresses)
209 result, errorMsg = Utils.sendEmail(msg,
210 self.dmd.smtpHost,
211 self.dmd.smtpPort,
212 self.dmd.smtpUseTLS,
213 self.dmd.smtpUser,
214 self.dmd.smtpPass)
215 if result:
216 self.log.debug("sent email: %s to:%s", msg.as_string(), o.addresses)
217 else:
218 self.log.info("failed to send email to %s: %s %s",
219 o.addresses, msg.as_string(), errorMsg)
220 sys.exit(1)
221 sys.exit(0)
222
224 if url.find('/zport/dmd/reports#reporttree:') != -1 :
225 urlSplit = url.split('/zport/dmd/reports#reporttree:')
226 url = urlSplit[0] + urlSplit[1].replace('.', '/')
227 if url.find('adapt=false') == -1 :
228 url += '?adapt=false' if url.find('?') == -1 else '&adapt=false'
229 return url
230
232 ZenScriptBase.buildOptions(self)
233 self.parser.add_option('--url', '-u',
234 dest='url',
235 default=None,
236 help='URL of report to send')
237 self.parser.add_option('--user', '-U',
238 dest='user',
239 default='admin',
240 help="User to log into Zenoss")
241 self.parser.add_option('--passwd', '-p',
242 dest='passwd',
243 help="Password to log into Zenoss")
244 self.parser.add_option('--address', '-a',
245 dest='addresses',
246 default=[],
247 action='append',
248 help='Email address destination '
249 '(may be given more than once). Default value'
250 "comes from the user's profile.")
251 self.parser.add_option('--subject', '-s',
252 dest='subject',
253 default='',
254 help='Subject line for email message.'
255 'Default value is the title of the html page.')
256 self.parser.add_option('--from', '-f',
257 dest='fromAddress',
258 default='zenoss@localhost',
259 help='Origination address')
260 self.parser.add_option('--div', '-d',
261 dest='div',
262 default='contentPane',
263 help='DIV to extract from URL')
264 self.parser.add_option('--comment', '-c',
265 dest='comment',
266 default='Report CSV attached.',
267 help='Comment to include in body of CSV reports')
268
269
270 if __name__ == '__main__':
271 ReportMail().run()
272