| Trees | Indices | Help |
|
|---|
|
|
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 or (at your
8 # option) any later version as published by the Free Software Foundation.
9 #
10 # For complete information please visit: http://www.zenoss.com/oss/
11 #
12 ###########################################################################
13
14 __doc__="""Commandable
15
16 Mixin class for classes that need a relationship back from UserCommand.
17
18 """
19
20 from Globals import InitializeClass
21 from AccessControl import ClassSecurityInfo
22 from ZenossSecurity import *
23 from UserCommand import UserCommand
24 from Acquisition import aq_base, aq_chain
25 from Products.PageTemplates.Expressions import getEngine
26 from Products.ZenUtils.ZenTales import talesCompile
27 from Products.ZenUtils.Utils import unused
28 from Products.ZenWidgets import messaging
29 from DateTime import DateTime
30 import os
31 import popen2
32 import fcntl
33 import select
34 import signal
35 import time
36 import cgi
37 import sys
38
39 import logging
40 log = logging.getLogger("zen.Device")
41
43
44 defaultTimeout = 60 # seconds
45
46 security = ClassSecurityInfo()
47
48 security.declareProtected(ZEN_DEFINE_COMMANDS_EDIT, 'manage_addUserCommand')
50 "Add a UserCommand to this device"
51 unused(desc, cmd)
52 uc = None
53 if newId:
54 uc = UserCommand(newId)
55 self.userCommands._setObject(newId, uc)
56 uc = self.userCommands._getOb(newId)
57 if self.meta_type == 'Device':
58 self.setLastChange()
59 uc.description = desc
60 uc.command = cmd
61 if REQUEST:
62 if uc:
63 messaging.IMessageSender(self).sendToBrowser(
64 'Command Added',
65 'User command %s has been created.' % newId
66 )
67 screenName = REQUEST.get("editScreenName", "")
68 return REQUEST.RESPONSE.redirect(uc.getPrimaryUrlPath() +
69 '/%s' % screenName if screenName else '')
70 return self.callZenScreen(REQUEST, True)
71 return uc
72
73
74 security.declareProtected(ZEN_DEFINE_COMMANDS_EDIT,
75 'manage_deleteUserCommand')
77 "Delete User Command(s) to this device"
78 if isinstance(ids, basestring):
79 ids = [ids]
80 for id in ids:
81 self.userCommands._delObject(id)
82 if self.meta_type == 'Device':
83 self.setLastChange()
84 if REQUEST:
85 messaging.IMessageSender(self).sendToBrowser(
86 'Commands Deleted',
87 'User commands %s have been deleted.' % " ".join(ids)
88 )
89 return self.redirectToUserCommands(REQUEST)
90
91 security.declareProtected(ZEN_DEFINE_COMMANDS_EDIT,
92 'manage_editUserCommand')
94 ''' Want to redirect back to management tab after a save
95 '''
96 command = self.getUserCommand(commandId)
97 if command:
98 password = REQUEST.form.get('password', '')
99 if not self.dmd.ZenUsers.authenticateCredentials(
100 self.dmd.ZenUsers.getUser().getId(), password):
101 messaging.IMessageSender(self).sendToBrowser(
102 'Password Error',
103 'Invalid or empty password.',
104 priority=messaging.WARNING
105 )
106 return REQUEST.RESPONSE.redirect(command.absolute_url_path())
107 del REQUEST.form['password']
108 command.manage_changeProperties(**REQUEST.form)
109 return self.redirectToUserCommands(REQUEST)
110
111
112 security.declareProtected(ZEN_RUN_COMMANDS, 'manage_doUserCommand')
114 ''' Execute a UserCommand. If REQUEST then
115 wrap output in proper zenoss html page.
116 '''
117 # This could be changed so that output is sent through a
118 # logger so that non web-based code can produce output.
119 # Not necessary for now.
120
121 command = self.getUserCommands(asDict=True).get(commandId,None)
122 if not command:
123 if REQUEST:
124 return self.redirectToUserCommands(REQUEST)
125 if REQUEST:
126 REQUEST['cmd'] = command
127 header, footer = self.commandOutputTemplate().split('OUTPUT_TOKEN')
128 REQUEST.RESPONSE.write(str(header))
129 out = REQUEST.RESPONSE
130 else:
131 out = None
132
133 startTime = time.time()
134 numTargets = 0
135 for target in self.getUserCommandTargets():
136 numTargets += 1
137 try:
138 self.write(out, '')
139 self.write(out, '==== %s ====' % target.id)
140 self.doCommandForTarget(command, target, out)
141 except:
142 self.write(out,
143 'exception while performing command for %s' % target.id)
144 self.write(
145 out, 'type: %s value: %s' % tuple(sys.exc_info()[:2]))
146 self.write(out, '')
147 self.write(out, '')
148 self.write(out, 'DONE in %s seconds on %s targets' %
149 (long(time.time() - startTime), numTargets))
150 REQUEST.RESPONSE.write(str(footer))
151
152
154 ''' Execute the given UserCommand on the given target
155 '''
156 compiled = self.compile(cmd, target)
157 child = popen2.Popen4(compiled)
158 flags = fcntl.fcntl(child.fromchild, fcntl.F_GETFL)
159 fcntl.fcntl(child.fromchild, fcntl.F_SETFL, flags | os.O_NDELAY)
160 timeout = getattr(target, 'zCommandCommandTimeout', self.defaultTimeout)
161 timeout = max(timeout, 1)
162 endtime = time.time() + timeout
163 self.write(out, '%s' % compiled)
164 self.write(out, '')
165 pollPeriod = 1
166 firstPass = True
167 while time.time() < endtime and (firstPass or child.poll() == -1):
168 firstPass = False
169 r, w, e = select.select([child.fromchild], [], [], pollPeriod)
170 if r:
171 t = child.fromchild.read()
172 # We are sometimes getting to this point without any data
173 # from child.fromchild. I don't think that should happen
174 # but the conditional below seems to be necessary.
175 if t:
176 self.write(out, t)
177
178 if child.poll() == -1:
179 self.write(out, 'Command timed out for %s' % target.id +
180 ' (timeout is %s seconds)' % timeout)
181 os.kill(child.pid, signal.SIGKILL)
182
183
185 ''' Evaluate command as a tales expression
186 '''
187 exp = "string:"+ cmd.command
188 compiled = talesCompile(exp)
189 environ = target.getUserCommandEnvironment()
190 res = compiled(getEngine().getContext(environ))
191 if isinstance(res, Exception):
192 raise res
193 return res
194
195
196 security.declareProtected(ZEN_VIEW, 'getUserCommandIds')
198 ''' Get the user command ids available in this context
199 '''
200 commandIds = []
201 mychain = self.getAqChainForUserCommands()
202 mychain.reverse()
203 for obj in mychain:
204 if getattr(aq_base(obj), 'userCommands', None):
205 for c in obj.userCommands():
206 commandIds.append(c.id)
207 return commandIds
208
209 security.declareProtected(ZEN_DEFINE_COMMANDS_VIEW, 'getUserCommands')
211 ''' Get the user commands available in this context
212 '''
213 commands = {}
214 mychain = self.getAqChainForUserCommands()
215 mychain.reverse()
216 for obj in mychain:
217 if getattr(aq_base(obj), 'userCommands', None):
218 for c in obj.userCommands():
219 commands[c.id] = c
220 def cmpCommands(a, b):
221 return cmp(a.getId(), b.getId())
222 if not asDict:
223 commands = commands.values()
224 commands.sort(cmpCommands)
225 return commands
226
227
229 return aq_chain(self.primaryAq())
230
231
233 ''' Redirect to the page which lists UserCommands
234 for this Commandable object.
235 '''
236 unused(commandId)
237 url = self.getUrlForUserCommands()
238 if url:
239 return REQUEST.RESPONSE.redirect(url)
240 return self.callZenScreen(REQUEST)
241
242
244 ''' Return url for page which manages user commands
245 '''
246 # This should be overridden by subclasses of Commandable
247 return self.getPrimaryUrlPath()
248
249 security.declareProtected(ZEN_DEFINE_COMMANDS_VIEW, 'getUserCommand')
251 ''' Returns the command from the current context if it exists
252 '''
253 return self.getUserCommands(asDict=True).get(commandId, None)
254
255
257 ''' Get the environment that provides context for the tales
258 evaluation of a UserCommand.
259 '''
260 # Overridden by Service and Device
261 return {
262 'target': self,
263 'here': self,
264 'nothing': None,
265 'now': DateTime()
266 }
267
268
270 ''' Called by Commandable.doCommand() to ascertain objects on which
271 a UserCommand should be executed.
272 '''
273 raise NotImplemented
274
275
277 ''' Output (maybe partial) result text from a UserCommand.
278 '''
279 # Looks like firefox renders progressive output more smoothly
280 # if each line is stuck into a table row.
281
282 # I doubt the above statement, as tested on Firefox 3
283 # this only generates a larger DOM object and does nothing
284 # for smoothness. It actually slows things down for large command
285 # output. Should maybe retested especially now that we are
286 # using CSS pre
287 startLine = '<tr><td class="commandoutput">'
288 endLine = '\n</td></tr>\n'
289 if out:
290 if not isinstance(lines, list):
291 lines = [lines]
292 for l in lines:
293 if not isinstance(l, str):
294 l = str(l)
295 l = l.strip()
296 l = cgi.escape(l)
297 l = l.replace('\n', endLine + startLine)
298 out.write(startLine + l + endLine)
299
300
301
302 InitializeClass(Commandable)
303
| Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1.1812 on Tue Oct 11 12:51:43 2011 | http://epydoc.sourceforge.net |