| 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 as published by
8 # 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 REQUEST['RESPONSE'].redirect(uc.getPrimaryUrlPath())
68 return self.callZenScreen(REQUEST)
69 return uc
70
71
72 security.declareProtected(ZEN_DEFINE_COMMANDS_EDIT,
73 'manage_deleteUserCommand')
75 "Delete User Command(s) to this device"
76 import types
77 if type(ids) in types.StringTypes:
78 ids = [ids]
79 for id in ids:
80 self.userCommands._delObject(id)
81 if self.meta_type == 'Device':
82 self.setLastChange()
83 if REQUEST:
84 messaging.IMessageSender(self).sendToBrowser(
85 'Commands Deleted',
86 'User commands %s have been deleted.' % " ".join(ids)
87 )
88 return self.callZenScreen(REQUEST)
89
90 security.declareProtected(ZEN_DEFINE_COMMANDS_EDIT,
91 'manage_editUserCommand')
93 ''' Want to redirect back to management tab after a save
94 '''
95 command = self.getUserCommand(commandId)
96 if command:
97 command.manage_changeProperties(**REQUEST.form)
98 return self.redirectToUserCommands(REQUEST)
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 # This could be changed so that output is sent through a
107 # logger so that non web-based code can produce output.
108 # Not necessary for now.
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 # We are sometimes getting to this point without any data
161 # from child.fromchild. I don't think that should happen
162 # but the conditional below seems to be necessary.
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
173 ''' Evaluate command as a tales expression
174 '''
175 exp = "string:"+ cmd.command
176 compiled = talesCompile(exp)
177 environ = target.getUserCommandEnvironment()
178 res = compiled(getEngine().getContext(environ))
179 if isinstance(res, Exception):
180 raise res
181 return res
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
217 return aq_chain(self.primaryAq())
218
219
221 ''' Redirect to the page which lists UserCommands
222 for this Commandable object.
223 '''
224 unused(commandId)
225 url = self.getUrlForUserCommands()
226 if url:
227 return REQUEST.RESPONSE.redirect(url)
228 return self.callZenScreen(REQUEST)
229
230
232 ''' Return url for page which manages user commands
233 '''
234 # This should be overridden by subclasses of Commandable
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 # Overridden by Service and Device
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
265 ''' Output (maybe partial) result text from a UserCommand.
266 '''
267 # Looks like firefox renders progressive output more smoothly
268 # if each line is stuck into a table row.
269
270 # I doubt the above statement, as tested on Firefox 3
271 # this only generates a larger DOM object and does nothing
272 # for smoothness. It actually slows things down for large command
273 # output. Should maybe retested especially now that we are
274 # using CSS pre
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
| Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0beta1 on Thu May 7 11:46:43 2009 | http://epydoc.sourceforge.net |