| Trees | Indices | Help |
|
|---|
|
|
1 ##############################################################################
2 #
3 # Copyright (C) Zenoss, Inc. 2007, all rights reserved.
4 #
5 # This content is made available according to terms specified in
6 # License.zenoss under the directory where your Zenoss product is installed.
7 #
8 ##############################################################################
9
10
11 from Products.ZenMessaging.audit import audit
12 from Products.ZenUtils.deprecated import deprecated
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")
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 audit('UI.Command.Add', newId, action=uc.command, description=uc.description)
64 messaging.IMessageSender(self).sendToBrowser(
65 'Command Added',
66 'User command %s has been created.' % newId
67 )
68 screenName = REQUEST.get("editScreenName", "")
69 return REQUEST.RESPONSE.redirect(uc.getPrimaryUrlPath() +
70 '/%s' % screenName if screenName else '')
71 return self.callZenScreen(REQUEST, True)
72 return uc
73
74
75 security.declareProtected(ZEN_DEFINE_COMMANDS_EDIT,
76 'manage_deleteUserCommand')
78 "Delete User Command(s) to this device"
79 if isinstance(ids, basestring):
80 ids = [ids]
81 for id in ids:
82 self.userCommands._delObject(id)
83 if self.meta_type == 'Device':
84 self.setLastChange()
85 if REQUEST:
86 for id in ids:
87 audit('UI.Command.Delete', id)
88 messaging.IMessageSender(self).sendToBrowser(
89 'Commands Deleted',
90 'User commands %s have been deleted.' % " ".join(ids)
91 )
92 return self.redirectToUserCommands(REQUEST)
93
94 security.declareProtected(ZEN_DEFINE_COMMANDS_EDIT,
95 'manage_editUserCommand')
97 ''' Want to redirect back to management tab after a save
98 '''
99 command = self.getUserCommand(commandId)
100 if command:
101 password = REQUEST.form.get('password', '')
102 if not self.dmd.ZenUsers.authenticateCredentials(
103 self.dmd.ZenUsers.getUser().getId(), password):
104 messaging.IMessageSender(self).sendToBrowser(
105 'Password Error',
106 'Invalid or empty password.',
107 priority=messaging.WARNING
108 )
109 return REQUEST.RESPONSE.redirect(command.absolute_url_path())
110 del REQUEST.form['password']
111 command.manage_changeProperties(**REQUEST.form)
112 audit('UI.Command.Edit', commandId, action=REQUEST.form.get('command',''))
113 return self.redirectToUserCommands(REQUEST)
114
115
116 security.declareProtected(ZEN_RUN_COMMANDS, 'manage_doUserCommand')
117 @deprecated
119 ''' Execute a UserCommand. If REQUEST then
120 wrap output in proper zenoss html page.
121 '''
122 # This could be changed so that output is sent through a
123 # logger so that non web-based code can produce output.
124 # Not necessary for now.
125
126 command = self.getUserCommands(asDict=True).get(commandId,None)
127 if not command:
128 if REQUEST:
129 return self.redirectToUserCommands(REQUEST)
130 if REQUEST:
131 REQUEST['cmd'] = command
132 header, footer = self.commandOutputTemplate().split('OUTPUT_TOKEN')
133 REQUEST.RESPONSE.write(str(header))
134 out = REQUEST.RESPONSE
135 else:
136 out = None
137
138 startTime = time.time()
139 numTargets = 0
140 for target in self.getUserCommandTargets():
141 numTargets += 1
142 try:
143 self.write(out, '')
144 self.write(out, '==== %s ====' % target.id)
145 self.doCommandForTarget(command, target, out)
146 #untested, method call cannot be found
147 audit('UI.Command.Invoke', commandId, target=target)
148 except:
149 self.write(out,
150 'exception while performing command for %s' % target.id)
151 self.write(
152 out, 'type: %s value: %s' % tuple(sys.exc_info()[:2]))
153 self.write(out, '')
154 self.write(out, '')
155 self.write(out, 'DONE in %s seconds on %s targets' %
156 (long(time.time() - startTime), numTargets))
157 REQUEST.RESPONSE.write(str(footer))
158
159
161 ''' Execute the given UserCommand on the given target
162 '''
163 compiled = self.compile(cmd, target)
164 child = popen2.Popen4(compiled)
165 flags = fcntl.fcntl(child.fromchild, fcntl.F_GETFL)
166 fcntl.fcntl(child.fromchild, fcntl.F_SETFL, flags | os.O_NDELAY)
167 timeout = getattr(target, 'zCommandCommandTimeout', self.defaultTimeout)
168 timeout = max(timeout, 1)
169 endtime = time.time() + timeout
170 self.write(out, '%s' % compiled)
171 self.write(out, '')
172 pollPeriod = 1
173 firstPass = True
174 while time.time() < endtime and (firstPass or child.poll() == -1):
175 firstPass = False
176 r, w, e = select.select([child.fromchild], [], [], pollPeriod)
177 if r:
178 t = child.fromchild.read()
179 # We are sometimes getting to this point without any data
180 # from child.fromchild. I don't think that should happen
181 # but the conditional below seems to be necessary.
182 if t:
183 self.write(out, t)
184
185 if child.poll() == -1:
186 self.write(out, 'Command timed out for %s' % target.id +
187 ' (timeout is %s seconds)' % timeout)
188 os.kill(child.pid, signal.SIGKILL)
189
190
192 ''' Evaluate command as a tales expression
193 '''
194 command = cmd.command
195 # make sure we are targeting the right collector
196 if not command.startswith("dcsh") and hasattr(target, "getPerformanceServerName"):
197 collector = target.getPerformanceServer()
198 # if there isn't a collector just run it locally
199 if collector and hasattr(collector, 'isLocalHost') and not collector.isLocalHost():
200 command = 'dcsh --collector=${device/getPerformanceServerName} -n "%s"' % (command)
201 exp = "string:"+ command
202 compiled = talesCompile(exp)
203 environ = target.getUserCommandEnvironment()
204 res = compiled(getEngine().getContext(environ))
205 if isinstance(res, Exception):
206 raise res
207 return res
208
209
210 security.declareProtected(ZEN_VIEW, 'getUserCommandIds')
212 ''' Get the user command ids available in this context
213 '''
214 commandIds = []
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 commandIds.append(c.id)
221 return commandIds
222
223 security.declareProtected(ZEN_DEFINE_COMMANDS_VIEW, 'getUserCommands')
225 ''' Get the user commands available in this context
226 '''
227 commands = {}
228 mychain = self.getAqChainForUserCommands()
229 mychain.reverse()
230 for obj in mychain:
231 if getattr(aq_base(obj), 'userCommands', None):
232 for c in obj.userCommands():
233 commands[c.id] = c
234 if not asDict:
235 commands = sorted(commands.itervalues(), key=lambda cmd: cmd.getId())
236 return commands
237
238
240 return aq_chain(self.primaryAq())
241
242
244 ''' Redirect to the page which lists UserCommands
245 for this Commandable object.
246 '''
247 unused(commandId)
248 url = self.getUrlForUserCommands()
249 if url:
250 return REQUEST.RESPONSE.redirect(url)
251 return self.callZenScreen(REQUEST)
252
253
255 ''' Return url for page which manages user commands
256 '''
257 # This should be overridden by subclasses of Commandable
258 return self.getPrimaryUrlPath()
259
260 security.declareProtected(ZEN_DEFINE_COMMANDS_VIEW, 'getUserCommand')
262 ''' Returns the command from the current context if it exists
263 '''
264 return self.getUserCommands(asDict=True).get(commandId, None)
265
266
268 ''' Get the environment that provides context for the tales
269 evaluation of a UserCommand.
270 '''
271 # Overridden by Service and Device
272 return {
273 'target': self,
274 'here': self,
275 'nothing': None,
276 'now': DateTime()
277 }
278
279
281 ''' Called by Commandable.doCommand() to ascertain objects on which
282 a UserCommand should be executed.
283 '''
284 raise NotImplemented
285
286
288 ''' Output (maybe partial) result text from a UserCommand.
289 '''
290 # Looks like firefox renders progressive output more smoothly
291 # if each line is stuck into a table row.
292
293 # I doubt the above statement, as tested on Firefox 3
294 # this only generates a larger DOM object and does nothing
295 # for smoothness. It actually slows things down for large command
296 # output. Should maybe retested especially now that we are
297 # using CSS pre
298 startLine = '<tr><td class="commandoutput">'
299 endLine = '\n</td></tr>\n'
300 if out:
301 if not isinstance(lines, list):
302 lines = [lines]
303 for l in lines:
304 if not isinstance(l, str):
305 l = str(l)
306 l = l.strip()
307 l = cgi.escape(l)
308 l = l.replace('\n', endLine + startLine)
309 out.write(startLine + l + endLine)
310
311
312
313 InitializeClass(Commandable)
314
| Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1.1812 on Mon Jul 30 17:11:30 2012 | http://epydoc.sourceforge.net |