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