Package flumotion :: Package admin :: Package command :: Module commands
[hide private]

Source Code for Module flumotion.admin.command.commands

  1  # -*- Mode: Python -*- 
  2  # vi:si:et:sw=4:sts=4:ts=4 
  3  # 
  4  # Flumotion - a streaming media server 
  5  # Copyright (C) 2004,2005,2006,2007 Fluendo, S.L. (www.fluendo.com). 
  6  # All rights reserved. 
  7   
  8  # This file may be distributed and/or modified under the terms of 
  9  # the GNU General Public License version 2 as published by 
 10  # the Free Software Foundation. 
 11  # This file is distributed without any warranty; without even the implied 
 12  # warranty of merchantability or fitness for a particular purpose. 
 13  # See "LICENSE.GPL" in the source distribution for more information. 
 14   
 15  # Licensees having purchased or holding a valid Flumotion Advanced 
 16  # Streaming Server license may use this file in accordance with the 
 17  # Flumotion Advanced Streaming Server Commercial License Agreement. 
 18  # See "LICENSE.Flumotion" in the source distribution for more information. 
 19   
 20  # Headers in this file shall remain intact. 
 21  import os 
 22   
 23  from twisted.internet import defer 
 24  from zope.interface import implements 
 25   
 26  from flumotion.twisted.defer import defer_generator 
 27  from flumotion.admin.command import utils 
 28  from flumotion.common.planet import moods 
 29  from flumotion.common import errors, log, componentui, common 
 30  from flumotion.twisted import flavors 
 31   
 32  __all__ = ['commands'] 
 33   
 34  # copied from flumotion/twisted/integration.py 
35 -class CommandNotFoundException(Exception):
36 - def __init__(self, command):
37 Exception.__init__(self) 38 self.command = command
39 - def __str__(self):
40 return 'Command %r not found in the PATH.' % self.command
41
42 -def _which(executable):
43 if os.sep in executable: 44 if os.access(os.path.abspath(executable), os.X_OK): 45 return os.path.abspath(executable) 46 elif os.getenv('PATH'): 47 for path in os.getenv('PATH').split(os.pathsep): 48 if os.access(os.path.join(path, executable), os.X_OK): 49 return os.path.join(path, executable) 50 raise CommandNotFoundException(executable)
51 52 53 # it's probably time to move this stuff into classes... 54 55 # command-list := (command-spec, command-spec...) 56 # command-spec := (command-name, command-desc, arguments, command-proc) 57 # command-name := str 58 # command-desc := str 59 # command-proc := f(model, quit, *args) -> None 60 # arguments := (arg-spec, arg-spec...) 61 # arg-spec := (arg-name, arg-parser, arg-default?) 62 # arg-name := str 63 # arg-parser := f(x) -> Python value or exception 64 # arg-default := any python value 65 66
67 -def do_getprop(model, quit, avatarId, propname):
68 d = utils.get_component_uistate(model, avatarId) 69 yield d 70 uistate = d.value() 71 if uistate: 72 if uistate.hasKey(propname): 73 print uistate.get(propname) 74 else: 75 print ('Component %s in flow %s has no property called %s' 76 % (avatarId[1], avatarId[0], propname)) 77 quit()
78 do_getprop = defer_generator(do_getprop) 79
80 -def do_listprops(model, quit, avatarId):
81 d = utils.get_component_uistate(model, avatarId) 82 yield d 83 uistate = d.value() 84 if uistate: 85 for k in uistate.keys(): 86 print k 87 quit()
88 do_listprops = defer_generator(do_listprops) 89
90 -def do_showplanet(model, quit):
91 d = model.callRemote('getPlanetState') 92 yield d 93 planet = d.value() 94 95 for f in planet.get('flows'): 96 print 'flow: %s' % f.get('name') 97 for c in f.get('components'): 98 print ' %s' % c.get('name') 99 100 a = planet.get('atmosphere') 101 print 'atmosphere: %s' % a.get('name') 102 for c in a.get('components'): 103 print ' %s' % c.get('name') 104 105 quit()
106 do_showplanet = defer_generator(do_showplanet) 107
108 -def do_getmood(model, quit, avatarId):
109 d = model.callRemote('getPlanetState') 110 yield d 111 planet = d.value() 112 c = utils.find_component(planet, avatarId) 113 if c: 114 mood = c.get('mood') 115 try: 116 _which('cowsay') 117 os.spawnlp(os.P_WAIT, 'cowsay', 'cowsay', 118 "%s is %s" % (c.get('name'), moods[mood].name)) 119 except CommandNotFoundException: 120 print "%s is %s" % (c.get('name'), moods[mood].name) 121 122 quit()
123 do_getmood = defer_generator(do_getmood) 124
125 -def do_showcomponent(model, quit, avatarId):
126 def show_uistate(k, v, indent=0): 127 if isinstance(v, list): 128 show_uistate(k, '<list>', indent) 129 for x in v: 130 show_uistate(None, x, indent+4) 131 elif isinstance(v, dict): 132 show_uistate(k, '<dict>', indent) 133 keys = v.keys() 134 keys.sort() 135 for k in keys: 136 show_uistate(k, v[k], indent+4) 137 elif isinstance(v, componentui.AdminComponentUIState): 138 show_uistate(k, '<uistate>', indent) 139 keys = v.keys() 140 keys.sort() 141 for k in keys: 142 show_uistate(k, v.get(k), indent+4) 143 else: 144 print '%s%s%s' % (' '*indent, k and k+': ' or '', v)
145 146 d = model.callRemote('getPlanetState') 147 yield d 148 planet = d.value() 149 c = utils.find_component(planet, avatarId) 150 if c: 151 print 'Component state:' 152 keys = c.keys() 153 keys.sort() 154 for k in keys: 155 print ' %s: %r' % (k, c.get(k)) 156 d = utils.get_component_uistate(model, avatarId, c, quiet=True) 157 yield d 158 try: 159 ui = d.value() 160 if ui: 161 print 162 show_uistate('UI state', ui) 163 except Exception, e: 164 print 'Error while retrieving UI state:', \ 165 log.getExceptionMessage(e) 166 quit() 167 do_showcomponent = defer_generator(do_showcomponent) 168
169 -class ParseException(Exception):
170 pass
171
172 -def _parse_typed_args(spec, args):
173 def _readFile(filename): 174 try: 175 f = open(filename) 176 contents = f.read() 177 f.close() 178 return contents 179 except: 180 raise ParseException("Failed to read file %s" % (filename,))
181 182 def _do_parse_typed_args(spec, args): 183 accum = [] 184 while spec: 185 argtype = spec.pop(0) 186 parsers = {'i': int, 's': str, 'b': common.strToBool, 187 'F': _readFile} 188 if argtype == ')': 189 return tuple(accum) 190 elif argtype == '(': 191 accum.append(_do_parse_typed_args(spec, args)) 192 elif argtype == '}': 193 return dict(accum) 194 elif argtype == '{': 195 accum.append(_do_parse_typed_args(spec, args)) 196 elif argtype not in parsers: 197 raise ParseException('Unknown argument type: %r' 198 % argtype) 199 else: 200 parser = parsers[argtype] 201 try: 202 arg = args.pop(0) 203 except IndexError: 204 raise ParseException('Missing argument of type %r' 205 % parser) 206 try: 207 accum.append(parser(arg)) 208 except Exception, e: 209 raise ParseException('Failed to parse %s as %r: %s' 210 % (arg, parser, e)) 211 212 spec = list(spec) + [')'] 213 args = list(args) 214 215 try: 216 res = _do_parse_typed_args(spec, args) 217 except ParseException, e: 218 print e.args[0] 219 return None 220 221 if args: 222 print 'Left over arguments:', args 223 return None 224 else: 225 return res 226
227 -def do_invoke(model, quit, avatarId, methodName, *args):
228 d = model.callRemote('getPlanetState') 229 yield d 230 planet = d.value() 231 c = utils.find_component(planet, avatarId) 232 if not c: 233 print "Could not find component %r" % avatarId 234 quit() 235 yield None 236 237 if args: 238 args = _parse_typed_args(args[0], args[1:]) 239 if args is None: 240 quit() 241 yield None 242 243 d = model.componentCallRemote(c, methodName, *args) 244 yield d 245 246 try: 247 v = d.value() 248 print "Invoke of %s on %s was successful." % (methodName, 249 avatarId[1]) 250 print v 251 except errors.NoMethodError: 252 print "No method '%s' on component '%s'" % (methodName, avatarId) 253 except errors.SleepingComponentError: 254 print "Component %s not running." % avatarId[1] 255 except Exception, e: 256 raise 257 258 quit()
259 do_invoke = defer_generator(do_invoke) 260
261 -def do_workerinvoke(model, quit, workerName, moduleName, methodName, *args):
262 if args: 263 args = _parse_typed_args(args[0], args[1:]) 264 if args is None: 265 quit() 266 yield None 267 268 d = model.callRemote('workerCallRemote', workerName, 'runFunction', 269 moduleName, methodName, *args) 270 yield d 271 272 try: 273 v = d.value() 274 print "Invoke of %s on %s was successful." % (methodName, workerName) 275 print v 276 except errors.NoMethodError: 277 print "No method '%s' on component '%s'" % (methodName, workerName) 278 except Exception, e: 279 raise 280 281 quit()
282 do_workerinvoke = defer_generator(do_workerinvoke) 283
284 -def do_managerinvoke(model, quit, methodName, *args):
285 if args: 286 args = _parse_typed_args(args[0], args[1:]) 287 if args is None: 288 quit() 289 yield None 290 291 d = model.callRemote(methodName, *args) 292 yield d 293 294 try: 295 v = d.value() 296 print "Invoke of %s was successful." % (methodName,) 297 print v 298 except errors.NoMethodError: 299 print "No method '%s' on manager" % (methodName,) 300 except Exception, e: 301 raise 302 303 quit()
304 do_managerinvoke = defer_generator(do_managerinvoke) 305
306 -def do_loadconfiguration(model, quit, confFile, saveAs):
307 print 'Loading configuration from file: %s' % confFile 308 309 f = open(confFile, 'r') 310 configurationXML = f.read() 311 f.close() 312 313 d = model.callRemote('loadConfiguration', configurationXML, 314 saveAs=saveAs) 315 yield d 316 d.value() 317 print 'Configuration loaded successfully.' 318 if saveAs: 319 print 'Additionally, the configuration XML was saved on the manager.' 320 321 quit()
322 do_loadconfiguration = defer_generator(do_loadconfiguration) 323
324 -def do_showworkers(model, quit):
325 d = model.callRemote('getWorkerHeavenState') 326 yield d 327 whs = d.value() 328 329 for worker in whs.get('workers'): 330 print "%s: %s" % (worker.get('name'), worker.get('host')) 331 quit()
332 do_showworkers = defer_generator(do_showworkers) 333
334 -class MoodListener(defer.Deferred):
335 - def __init__(self, moods, state):
336 defer.Deferred.__init__(self) 337 self._moodsFinal = moods 338 state.addListener(self, self.stateSet)
339
340 - def stateSet(self, object, key, value):
341 if key == 'mood' and moods[value] in self._moodsFinal: 342 self.callback(moods[value])
343 344 # FIXME: nicer to rewrite do_stop, do_start and do_delete to run some common 345 # code
346 -def do_avatar_action(model, quit, avatarPath, action):
347 """ 348 @type action: a tuple of (actionName, remoteCall, moods, checkMoodFunc) 349 """ 350 d = model.callRemote('getPlanetState') 351 yield d 352 planet = d.value() 353 components = [] 354 if avatarPath[0] == 'flow': 355 flows = planet.get('flows') 356 flow_to_act = None 357 for f in flows: 358 if avatarPath[1] == f.get('name'): 359 flow_to_act = f 360 if flow_to_act == None: 361 print "The flow %s is not found." % avatarPath[1] 362 quit() 363 else: 364 components = flow_to_act.get('components') 365 elif avatarPath[0] == 'atmosphere': 366 components = planet.get('atmosphere').get('components') 367 elif avatarPath[0] == 'root': 368 flows = planet.get('flows') 369 for f in flows: 370 components = components + f.get('components') 371 components = components + planet.get('atmosphere').get('components') 372 else: 373 c = utils.find_component(planet, avatarPath[1:]) 374 if c: 375 components.append(c) 376 # else: message already printed in find_component() 377 378 if len(components) > 0: 379 def actionComponent(c): 380 if action[3](moods[c.get('mood')]): 381 return model.callRemote(action[1], c) 382 else: 383 print "Cannot %s component /%s/%s, it is in mood: %s." % ( 384 action[0], 385 c.get("parent").get("name"), c.get("name"), 386 moods[c.get("mood")].name) 387 return None
388 dl = [] 389 for comp in components: 390 actD = actionComponent(comp) 391 # maybeDeferred won't work here due to python lexicals 392 if actD: 393 dl.append(actD) 394 if action[2]: 395 # wait for component to be in certain moods 396 dl.append(MoodListener(action[2], comp)) 397 d = defer.DeferredList(dl) 398 yield d 399 d.value() 400 if avatarPath[0] == 'flow': 401 print "Components in flow now completed action %s." % action[0] 402 elif avatarPath[0] == 'atmosphere': 403 print "Components in atmosphere now completed action %s." % ( 404 action[0],) 405 elif avatarPath[0] == 'root': 406 print "Components in / now completed action %s." % action[0] 407 else: 408 print "Component now completed action %s." % action[0] 409 quit() 410 do_avatar_action = defer_generator(do_avatar_action) 411
412 -def do_stop(model, quit, avatarPath):
413 return do_avatar_action(model, quit, avatarPath, ('stop', 'componentStop', 414 (moods.sleeping,), moods.can_stop))
415
416 -def do_start(model, quit, avatarPath):
417 return do_avatar_action(model, quit, avatarPath, ('start', 'componentStart', 418 (moods.happy, moods.sad), moods.can_start))
419
420 -def do_delete(model, quit, avatarPath):
421 return do_avatar_action(model, quit, avatarPath, ('delete', 422 'deleteComponent', None, lambda m: not moods.can_stop(m)))
423 424 commands = (('getprop', 425 'gets a property on a component', 426 (('component-path', utils.avatarId), 427 ('property-name', str)), 428 do_getprop), 429 ('listprops', 430 'lists the properties a component has', 431 (('component-path', utils.avatarId), 432 ), 433 do_listprops), 434 ('showplanet', 435 'shows the flows, atmosphere, and components in the planet', 436 (), 437 do_showplanet), 438 ('getmood', 439 'gets the mood of a component', 440 (('component-path', utils.avatarId), 441 ), 442 do_getmood), 443 ('showcomponent', 444 'shows everything we know about a component', 445 (('component-path', utils.avatarId), 446 ), 447 do_showcomponent), 448 ('showworkers', 449 'shows all the workers that are logged into the manager', 450 (), 451 do_showworkers), 452 ('invoke', 453 'invoke a component method', 454 (('component-path', utils.avatarId), 455 ('method-name', str), 456 ('args', str, None, True)), 457 do_invoke), 458 ('workerinvoke', 459 'invoke a function on a worker', 460 (('worker-name', str), 461 ('module-name', str), 462 ('method-name', str), 463 ('args', str, None, True)), 464 do_workerinvoke), 465 ('managerinvoke', 466 'invoke a function on a manager', 467 (('method-name', str), 468 ('args', str, None, True)), 469 do_managerinvoke), 470 ('loadconfiguration', 471 'load configuration into the manager', 472 (('conf-file', str), 473 ('save-as', str, None), 474 ), 475 do_loadconfiguration), 476 ('stop', 477 'stops a component, flow or all flows', 478 (('path', utils.avatarPath), 479 ), 480 do_stop), 481 ('start', 482 'starts a componment, all components in a flow or all flows', 483 (('path', utils.avatarPath), 484 ), 485 do_start), 486 ('delete', 487 'deletes a component, all components in a flow or all flows', 488 (('path', utils.avatarPath), 489 ), 490 do_delete) 491 ) 492