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