Package Products :: Package ZenModel :: Module Commandable
[hide private]
[frames] | no frames]

Source Code for Module Products.ZenModel.Commandable

  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") 
41 42 -class Commandable:
43 44 defaultTimeout = 60 # seconds 45 46 security = ClassSecurityInfo() 47 48 security.declareProtected(ZEN_DEFINE_COMMANDS_EDIT, 'manage_addUserCommand')
49 - def manage_addUserCommand(self, newId=None, desc='', cmd='', REQUEST=None):
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')
77 - def manage_deleteUserCommand(self, ids=(), REQUEST=None):
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')
96 - def manage_editUserCommand(self, commandId, REQUEST):
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
118 - def manage_doUserCommand(self, commandId=None, REQUEST=None):
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
160 - def doCommandForTarget(self, cmd, target, out):
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
191 - def compile(self, cmd, target):
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')
211 - def getUserCommandIds(self):
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')
224 - def getUserCommands(self, asDict=False):
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
243 - def redirectToUserCommands(self, REQUEST, commandId=None):
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
254 - def getUrlForUserCommands(self):
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')
261 - def getUserCommand(self, commandId):
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
280 - def getUserCommandTargets(self):
281 ''' Called by Commandable.doCommand() to ascertain objects on which 282 a UserCommand should be executed. 283 ''' 284 raise NotImplemented
285 286
287 - def write(self, out, lines):
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