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')
70
71
72 security.declareProtected(ZEN_DEFINE_COMMANDS_EDIT,
73 'manage_deleteUserCommand')
89
90 security.declareProtected(ZEN_DEFINE_COMMANDS_EDIT,
91 'manage_editUserCommand')
99
100
101 security.declareProtected(ZEN_RUN_COMMANDS, 'manage_doUserCommand')
103 ''' Execute a UserCommand. If REQUEST then
104 wrap output in proper zenoss html page.
105 '''
106
107
108
109 command = self.getUserCommands(asDict=True).get(commandId,None)
110 if not command:
111 if REQUEST:
112 return self.redirectToUserCommands(REQUEST)
113 if REQUEST:
114 REQUEST['cmd'] = command
115 header, footer = self.commandOutputTemplate().split('OUTPUT_TOKEN')
116 REQUEST.RESPONSE.write(str(header))
117 out = REQUEST.RESPONSE
118 else:
119 out = None
120
121 startTime = time.time()
122 numTargets = 0
123 for target in self.getUserCommandTargets():
124 numTargets += 1
125 try:
126 self.write(out, '')
127 self.write(out, '==== %s ====' % target.id)
128 self.doCommandForTarget(command, target, out)
129 except:
130 self.write(out,
131 'exception while performing command for %s' % target.id)
132 self.write(
133 out, 'type: %s value: %s' % tuple(sys.exc_info()[:2]))
134 self.write(out, '')
135 self.write(out, '')
136 self.write(out, 'DONE in %s seconds on %s targets' %
137 (long(time.time() - startTime), numTargets))
138 REQUEST.RESPONSE.write(footer)
139
140
142 ''' Execute the given UserCommand on the given target
143 '''
144 compiled = self.compile(cmd, target)
145 child = popen2.Popen4(compiled)
146 flags = fcntl.fcntl(child.fromchild, fcntl.F_GETFL)
147 fcntl.fcntl(child.fromchild, fcntl.F_SETFL, flags | os.O_NDELAY)
148 timeout = getattr(target, 'zCommandCommandTimeout', self.defaultTimeout)
149 timeout = max(timeout, 1)
150 endtime = time.time() + timeout
151 self.write(out, '%s' % compiled)
152 self.write(out, '')
153 pollPeriod = 1
154 firstPass = True
155 while time.time() < endtime and (firstPass or child.poll() == -1):
156 firstPass = False
157 r, w, e = select.select([child.fromchild], [], [], pollPeriod)
158 if r:
159 t = child.fromchild.read()
160
161
162
163 if t:
164 self.write(out, t)
165
166 if child.poll() == -1:
167 self.write(out, 'Command timed out for %s' % target.id +
168 ' (timeout is %s seconds)' % timeout)
169 os.kill(child.pid, signal.SIGKILL)
170
171
182
183
184 security.declareProtected(ZEN_VIEW, 'getUserCommandIds')
186 ''' Get the user command ids available in this context
187 '''
188 commandIds = []
189 mychain = self.getAqChainForUserCommands()
190 mychain.reverse()
191 for obj in mychain:
192 if getattr(aq_base(obj), 'userCommands', None):
193 for c in obj.userCommands():
194 commandIds.append(c.id)
195 return commandIds
196
197 security.declareProtected(ZEN_DEFINE_COMMANDS_VIEW, 'getUserCommands')
199 ''' Get the user commands available in this context
200 '''
201 commands = {}
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 commands[c.id] = c
208 def cmpCommands(a, b):
209 return cmp(a.getId(), b.getId())
210 if not asDict:
211 commands = commands.values()
212 commands.sort(cmpCommands)
213 return commands
214
215
218
219
229
230
232 ''' Return url for page which manages user commands
233 '''
234
235 return self.getPrimaryUrlPath()
236
237 security.declareProtected(ZEN_DEFINE_COMMANDS_VIEW, 'getUserCommand')
239 ''' Returns the command from the current context if it exists
240 '''
241 return self.getUserCommands(asDict=True).get(commandId, None)
242
243
245 ''' Get the environment that provides context for the tales
246 evaluation of a UserCommand.
247 '''
248
249 return {
250 'target': self,
251 'here': self,
252 'nothing': None,
253 'now': DateTime()
254 }
255
256
258 ''' Called by Commandable.doCommand() to ascertain objects on which
259 a UserCommand should be executed.
260 '''
261 raise NotImplemented
262
263
264 - def write(self, out, lines):
265 ''' Output (maybe partial) result text from a UserCommand.
266 '''
267
268
269
270
271
272
273
274
275 startLine = '<tr><td class="commandoutput">'
276 endLine = '\n</td></tr>\n'
277 if out:
278 if not isinstance(lines, list):
279 lines = [lines]
280 for l in lines:
281 if not isinstance(l, str):
282 l = str(l)
283 l = l.strip()
284 l = cgi.escape(l)
285 l = l.replace('\n', endLine + startLine)
286 out.write(startLine + l + endLine)
287
288
289
290 InitializeClass(Commandable)
291