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')
91
92 security.declareProtected(ZEN_DEFINE_COMMANDS_EDIT,
93 'manage_editUserCommand')
112
113
114 security.declareProtected(ZEN_RUN_COMMANDS, 'manage_doUserCommand')
116 ''' Execute a UserCommand. If REQUEST then
117 wrap output in proper zenoss html page.
118 '''
119
120
121
122 command = self.getUserCommands(asDict=True).get(commandId,None)
123 if not command:
124 if REQUEST:
125 return self.redirectToUserCommands(REQUEST)
126 if REQUEST:
127 REQUEST['cmd'] = command
128 header, footer = self.commandOutputTemplate().split('OUTPUT_TOKEN')
129 REQUEST.RESPONSE.write(str(header))
130 out = REQUEST.RESPONSE
131 else:
132 out = None
133
134 startTime = time.time()
135 numTargets = 0
136 for target in self.getUserCommandTargets():
137 numTargets += 1
138 try:
139 self.write(out, '')
140 self.write(out, '==== %s ====' % target.id)
141 self.doCommandForTarget(command, target, out)
142 except:
143 self.write(out,
144 'exception while performing command for %s' % target.id)
145 self.write(
146 out, 'type: %s value: %s' % tuple(sys.exc_info()[:2]))
147 self.write(out, '')
148 self.write(out, '')
149 self.write(out, 'DONE in %s seconds on %s targets' %
150 (long(time.time() - startTime), numTargets))
151 REQUEST.RESPONSE.write(footer)
152
153
155 ''' Execute the given UserCommand on the given target
156 '''
157 compiled = self.compile(cmd, target)
158 child = popen2.Popen4(compiled)
159 flags = fcntl.fcntl(child.fromchild, fcntl.F_GETFL)
160 fcntl.fcntl(child.fromchild, fcntl.F_SETFL, flags | os.O_NDELAY)
161 timeout = getattr(target, 'zCommandCommandTimeout', self.defaultTimeout)
162 timeout = max(timeout, 1)
163 endtime = time.time() + timeout
164 self.write(out, '%s' % compiled)
165 self.write(out, '')
166 pollPeriod = 1
167 firstPass = True
168 while time.time() < endtime and (firstPass or child.poll() == -1):
169 firstPass = False
170 r, w, e = select.select([child.fromchild], [], [], pollPeriod)
171 if r:
172 t = child.fromchild.read()
173
174
175
176 if t:
177 self.write(out, t)
178
179 if child.poll() == -1:
180 self.write(out, 'Command timed out for %s' % target.id +
181 ' (timeout is %s seconds)' % timeout)
182 os.kill(child.pid, signal.SIGKILL)
183
184
195
196
197 security.declareProtected(ZEN_VIEW, 'getUserCommandIds')
199 ''' Get the user command ids available in this context
200 '''
201 commandIds = []
202 mychain = self.getAqChainForUserCommands()
203 mychain.reverse()
204 for obj in mychain:
205 if getattr(aq_base(obj), 'userCommands', None):
206 for c in obj.userCommands():
207 commandIds.append(c.id)
208 return commandIds
209
210 security.declareProtected(ZEN_DEFINE_COMMANDS_VIEW, 'getUserCommands')
212 ''' Get the user commands available in this context
213 '''
214 commands = {}
215 mychain = self.getAqChainForUserCommands()
216 mychain.reverse()
217 for obj in mychain:
218 if getattr(aq_base(obj), 'userCommands', None):
219 for c in obj.userCommands():
220 commands[c.id] = c
221 def cmpCommands(a, b):
222 return cmp(a.getId(), b.getId())
223 if not asDict:
224 commands = commands.values()
225 commands.sort(cmpCommands)
226 return commands
227
228
231
232
242
243
245 ''' Return url for page which manages user commands
246 '''
247
248 return self.getPrimaryUrlPath()
249
250 security.declareProtected(ZEN_DEFINE_COMMANDS_VIEW, 'getUserCommand')
252 ''' Returns the command from the current context if it exists
253 '''
254 return self.getUserCommands(asDict=True).get(commandId, None)
255
256
258 ''' Get the environment that provides context for the tales
259 evaluation of a UserCommand.
260 '''
261
262 return {
263 'target': self,
264 'here': self,
265 'nothing': None,
266 'now': DateTime()
267 }
268
269
271 ''' Called by Commandable.doCommand() to ascertain objects on which
272 a UserCommand should be executed.
273 '''
274 raise NotImplemented
275
276
277 - def write(self, out, lines):
278 ''' Output (maybe partial) result text from a UserCommand.
279 '''
280
281
282
283
284
285
286
287
288 startLine = '<tr><td class="commandoutput">'
289 endLine = '\n</td></tr>\n'
290 if out:
291 if not isinstance(lines, list):
292 lines = [lines]
293 for l in lines:
294 if not isinstance(l, str):
295 l = str(l)
296 l = l.strip()
297 l = cgi.escape(l)
298 l = l.replace('\n', endLine + startLine)
299 out.write(startLine + l + endLine)
300
301
302
303 InitializeClass(Commandable)
304