| 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 password = REQUEST.form.get('password', '')
98 userManager = self.acl_users.userManager
99 if not userManager.authenticateCredentials({
100 'login':self.dmd.ZenUsers.getUser().getId(),
101 'password':password
102 }):
103 messaging.IMessageSender(self).sendToBrowser(
104 'Password Error',
105 'Invalid or empty password.',
106 priority=messaging.WARNING
107 )
108 return REQUEST.RESPONSE.redirect(command.absolute_url_path())
109 del REQUEST.form['password']
110 command.manage_changeProperties(**REQUEST.form)
111 return self.redirectToUserCommands(REQUEST)
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 # This could be changed so that output is sent through a
120 # logger so that non web-based code can produce output.
121 # Not necessary for now.
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 # We are sometimes getting to this point without any data
174 # from child.fromchild. I don't think that should happen
175 # but the conditional below seems to be necessary.
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
186 ''' Evaluate command as a tales expression
187 '''
188 exp = "string:"+ cmd.command
189 compiled = talesCompile(exp)
190 environ = target.getUserCommandEnvironment()
191 res = compiled(getEngine().getContext(environ))
192 if isinstance(res, Exception):
193 raise res
194 return res
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
230 return aq_chain(self.primaryAq())
231
232
234 ''' Redirect to the page which lists UserCommands
235 for this Commandable object.
236 '''
237 unused(commandId)
238 url = self.getUrlForUserCommands()
239 if url:
240 return REQUEST.RESPONSE.redirect(url)
241 return self.callZenScreen(REQUEST)
242
243
245 ''' Return url for page which manages user commands
246 '''
247 # This should be overridden by subclasses of Commandable
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 # Overridden by Service and Device
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
278 ''' Output (maybe partial) result text from a UserCommand.
279 '''
280 # Looks like firefox renders progressive output more smoothly
281 # if each line is stuck into a table row.
282
283 # I doubt the above statement, as tested on Firefox 3
284 # this only generates a larger DOM object and does nothing
285 # for smoothness. It actually slows things down for large command
286 # output. Should maybe retested especially now that we are
287 # using CSS pre
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
| Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0beta1 on Mon Oct 19 14:42:08 2009 | http://epydoc.sourceforge.net |