1
2
3
4
5
6
7
8
9
10
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
45
46 security = ClassSecurityInfo()
47
48 security.declareProtected(ZEN_DEFINE_COMMANDS_EDIT, 'manage_addUserCommand')
72
73
74 security.declareProtected(ZEN_DEFINE_COMMANDS_EDIT,
75 'manage_deleteUserCommand')
90
91 security.declareProtected(ZEN_DEFINE_COMMANDS_EDIT,
92 'manage_editUserCommand')
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
118
119
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
173
174
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
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
230
231
241
242
244 ''' Return url for page which manages user commands
245 '''
246
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
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
276 - def write(self, out, lines):
277 ''' Output (maybe partial) result text from a UserCommand.
278 '''
279
280
281
282
283
284
285
286
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