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

Source Code for Module flumotion.admin.command.component

  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,2008,2009 Fluendo, S.L. 
  6  # Copyright (C) 2010,2011 Flumotion Services, S.A. 
  7  # All rights reserved. 
  8  # 
  9  # This file may be distributed and/or modified under the terms of 
 10  # the GNU Lesser General Public License version 2.1 as published by 
 11  # the Free Software Foundation. 
 12  # This file is distributed without any warranty; without even the implied 
 13  # warranty of merchantability or fitness for a particular purpose. 
 14  # See "LICENSE.LGPL" in the source distribution for more information. 
 15  # 
 16  # Headers in this file shall remain intact. 
 17   
 18  """ 
 19  component commands 
 20  """ 
 21  from twisted.internet import defer 
 22   
 23  from flumotion.admin.command import common 
 24  from flumotion.common import errors, planet, log 
 25  from flumotion.monitor.nagios import util 
 26  from flumotion.common.planet import moods 
 27   
 28  __version__ = "$Rev: 6562 $" 
 29   
 30   
31 -class Delete(common.AdminCommand):
32 description = "Delete a component." 33
34 - def doCallback(self, args):
35 if not self.parentCommand.componentId: 36 common.errorRaise("Please specify a component id " 37 "with 'component -i [component-id]'") 38 39 d = self.getRootCommand().medium.callRemote('deleteComponent', 40 self.parentCommand.componentState) 41 42 def cb(result): 43 self.stdout.write("Deleted component.\n")
44 45 def eb(failure): 46 if failure.check(errors.ComponentMoodError, 47 errors.BusyComponentError): 48 common.errorRaise("Component '%s' is in the wrong mood." % 49 self.parentCommand.componentId) 50 else: 51 common.errorRaise(log.getFailureMessage(failure))
52 53 d.addCallback(cb) 54 d.addErrback(eb) 55 56 return d 57 58
59 -class Invoke(common.AdminCommand):
60 usage = "[method-name] [arguments]" 61 summary = "invoke a method on a component" 62 description = """Invoke a method on a component. 63 %s 64 For a list of methods that can be invoked, see the component's medium class 65 and its remote_* methods. 66 67 Examples: getConfig, setFluDebug""" % common.ARGUMENTS_DESCRIPTION 68
69 - def addOptions(self):
70 self.parser.add_option('-r', '--raw-output', 71 action="store_true", dest="rawOutput", 72 help="do not pretty print the output")
73
74 - def handleOptions(self, options):
75 self.rawOutput = options.rawOutput
76
77 - def doCallback(self, args):
78 if not self.parentCommand.componentId: 79 common.errorRaise("Please specify a component id " 80 "with 'component -i [component-id]'") 81 82 try: 83 methodName = args[0] 84 except IndexError: 85 common.errorRaise('Please specify a method name to invoke.') 86 if len(args) > 1: 87 args = common.parseTypedArgs(args[1], args[2:]) 88 if args is None: 89 common.errorRaise('Could not parse arguments.') 90 else: 91 args = [] 92 93 p = self.parentCommand 94 d = self.getRootCommand().medium.componentCallRemote( 95 self.parentCommand.componentState, methodName, *args) 96 97 def cb(result): 98 if self.rawOutput: 99 self.stdout.write(str(result)) 100 else: 101 import pprint 102 self.stdout.write("Invoking '%s' on '%s' returned:\n%s\n" % ( 103 methodName, p.componentId, pprint.pformat(result)))
104 105 def eb(failure): 106 if failure.check(errors.NoMethodError): 107 common.errorRaise("No method '%s' on component '%s'." % ( 108 methodName, p.componentId)) 109 elif failure.check(errors.SleepingComponentError): 110 common.errorRaise( 111 "Component '%s' is sleeping." % p.componentId) 112 else: 113 common.errorRaise(log.getFailureMessage(failure))
114 115 d.addCallback(cb) 116 d.addErrback(eb) 117 118 return d 119 120
121 -class List(common.AdminCommand):
122 description = "List components." 123
124 - def doCallback(self, args):
125 p = self.parentCommand 126 a = p.planetState.get('atmosphere') 127 if a.get('components'): 128 self.stdout.write('atmosphere:\n') 129 for c in a.get('components'): 130 self.stdout.write(' ' + c.get('name') + '\n') 131 132 for f in p.planetState.get('flows'): 133 if f.get('components'): 134 self.stdout.write('%s flow:\n' % f.get('name')) 135 for c in f.get('components'): 136 self.stdout.write(' ' + c.get('name') + '\n')
137 138
139 -class DetailedList(common.AdminCommand):
140 description = "List components with types and worker hosts." 141
142 - def doCallback(self, args):
143 p = self.parentCommand 144 a = p.planetState.get('atmosphere') 145 s = p.workerHeavenState 146 workers = s.get('workers') 147 a_comps = a.get('components') 148 if a_comps: 149 self.stdout.write('atmosphere:\n') 150 self.parentCommand.print_components(a_comps, workers) 151 152 for f in p.planetState.get('flows'): 153 f_comps = f.get('components') 154 if f_comps: 155 self.stdout.write('%s flow:\n' % f.get('name')) 156 self.parentCommand.print_components(f_comps, workers)
157 158
159 -class UpstreamList(common.AdminCommand):
160 description = """List a component and its upstream components along 161 with types and worker hosts.""" 162
163 - def get_eaters_ids(self, eaters_dic):
164 avatars = [] 165 for flow in eaters_dic.keys(): 166 comps = eaters_dic[flow] 167 for c in comps: 168 (name, what) = c[0].split(':') 169 avatars.append('/%s/%s' % (flow, name)) 170 return avatars
171
172 - def doCallback(self, args):
173 p = self.parentCommand 174 s = p.workerHeavenState 175 workers = s.get('workers') 176 177 if not p.componentId: 178 common.errorRaise("Please specify a component id " 179 "with 'component -i [component-id]'") 180 181 eaters = p.componentState.get('config').get('eater', {}) 182 eaters_id = self.get_eaters_ids(eaters) 183 comps = [p.componentState] 184 while len(eaters_id) > 0: 185 eaters = {} 186 for i in eaters_id: 187 try: 188 compState = util.findComponent(p.planetState, i) 189 comps.append(compState) 190 eaters.update(compState.get('config').get('eater', {})) 191 except Exception, e: 192 self.debug(log.getExceptionMessage(e)) 193 common.errorRaise("Error retrieving component '%s'" % i) 194 eaters_id = self.get_eaters_ids(eaters) 195 196 self.stdout.write('Upstream Components:\n') 197 self.parentCommand.print_components(comps, workers)
198 199
200 -class DownstreamList(common.AdminCommand):
201 description = """List a component and its downstream components along 202 with types and worker hosts.""" 203 204 components = [] 205
206 - def doCallback(self, args):
207 p = self.parentCommand 208 s = p.workerHeavenState 209 workers = s.get('workers') 210 211 if not p.componentId: 212 common.errorRaise("Please specify a component id " 213 "with 'component -i [component-id]'") 214 215 self.stdout.write('Downstream Components:\n') 216 217 d = defer.maybeDeferred(self.getUIState, p.componentState) 218 d.addCallback(self.parentCommand.print_components, workers) 219 return d
220
221 - def getUIState(self, state):
222 p = self.parentCommand 223 admin = p.parentCommand.medium 224 225 self.components.append(state) 226 d = admin.componentCallRemote(state, 'getUIState') 227 d.addCallback(self.gotUIState) 228 return d
229
230 - def gotUIState(self, state):
231 p = self.parentCommand 232 233 dList = [] 234 for f in state.get('feeders'): 235 for c in f['clients']: 236 feeder_id = c['client-id'].split(':')[0] 237 compState = util.findComponent(p.planetState, feeder_id) 238 if compState is None or compState in self.components: 239 continue 240 d = defer.maybeDeferred(self.getUIState, compState) 241 dList.append(d) 242 d = defer.DeferredList(dList) 243 d.addCallback(lambda result: self.components) 244 return d
245 246
247 -class Mood(common.AdminCommand):
248 description = "Check the mood of a component." 249
250 - def doCallback(self, args):
251 if not self.parentCommand.componentId: 252 common.errorRaise("Please specify a component id " 253 "with 'component -i [component-id]'") 254 255 p = self.parentCommand 256 moodValue = p.componentState.get('mood') 257 moodName = planet.moods.get(moodValue).name 258 self.stdout.write("Component '%s' is %s.\n" % (p.componentId, 259 moodName))
260 261
262 -class PropertyGet(common.AdminCommand):
263 description = "Get a property of a component." 264 name = 'get' 265
266 - def do(self, args):
267 if not args: 268 return common.errorReturn('Please specify a property to get.') 269 270 self._propertyName = args[0] 271 272 return common.AdminCommand.do(self, args)
273
274 - def doCallback(self, args):
275 u = self.parentCommand.uiState 276 name = self._propertyName 277 278 if not u.hasKey(name): 279 common.errorRaise("Component '%s' does not have property '%s'." % ( 280 self.parentCommand.parentCommand.componentId, name)) 281 282 self.stdout.write("Property '%s' is '%r'.\n" % ( 283 name, u.get(name)))
284 285
286 -class PropertyList(common.AdminCommand):
287 description = "List properties of a component." 288 name = 'list' 289
290 - def doCallback(self, args):
291 l = self.parentCommand.uiState.keys() 292 l.sort() 293 self.stdout.write('Properties:\n') 294 for p in l: 295 self.stdout.write('- %s\n' % p)
296 297 # FIXME: why is this called property when it really is about ui state ? 298 299
300 -class Property(util.LogCommand):
301 """ 302 @param uiState: the ui state of the component; set after logging in. 303 """ 304 305 description = "Act on properties of a component." 306 307 subCommandClasses = [PropertyGet, PropertyList] 308
309 - def handleOptions(self, options):
310 if not self.parentCommand.componentId: 311 common.errorRaise("Please specify a component id " 312 "with 'component -i [component-id]'") 313 314 # call our callback after connecting 315 d = self.getRootCommand().loginDeferred 316 d.addCallback(self._callback) 317 d.addErrback(self._errback)
318
319 - def _callback(self, result):
320 321 def getUIStateCb(uiState): 322 self.uiState = uiState
323 324 componentCommand = self.parentCommand 325 model = componentCommand.parentCommand.medium 326 d = model.componentCallRemote( 327 componentCommand.componentState, 'getUIState') 328 d.addCallback(getUIStateCb) 329 return d
330
331 - def _errback(self, failure):
332 failure.trap(errors.SleepingComponentError) 333 common.errorRaise("Component '%s' is sleeping." % 334 self.parentCommand.componentId)
335 336
337 -class Start(common.AdminCommand):
338 description = "Start a component." 339
340 - def doCallback(self, args):
341 if not self.parentCommand.componentId: 342 common.errorRaise("Please specify a component id " 343 "with 'component -i [component-id]'") 344 345 p = self.parentCommand 346 moodValue = p.componentState.get('mood') 347 if moodValue == moods.happy.value: 348 self.stdout.write("Component is already happy.\n") 349 return 0 350 351 d = self.getRootCommand().medium.callRemote('componentStart', 352 self.parentCommand.componentState) 353 354 def cb(result): 355 self.stdout.write("Started component.\n")
356 357 def eb(failure): 358 if failure.trap(errors.ComponentMoodError): 359 common.errorRaise("Component '%s' is in the wrong mood." % 360 self.parentCommand.componentId) 361 else: 362 common.errorRaise(log.getFailureMessage(failure))
363 364 d.addCallback(cb) 365 d.addErrback(eb) 366 367 return d 368 369
370 -class Stop(common.AdminCommand):
371 description = "Stop a component." 372
373 - def doCallback(self, args):
374 if not self.parentCommand.componentId: 375 common.errorRaise("Please specify a component id " 376 "with 'component -i [component-id]'") 377 378 p = self.parentCommand 379 moodValue = p.componentState.get('mood') 380 if moodValue == moods.sleeping.value: 381 self.stdout.write("Component is already sleeping.\n") 382 return 0 383 384 d = self.getRootCommand().medium.callRemote('componentStop', 385 self.parentCommand.componentState) 386 387 def cb(result): 388 self.stdout.write("Stopped component.\n")
389 390 def eb(failure): 391 if failure.trap(errors.ComponentMoodError): 392 common.errorRaise("Component '%s' is in the wrong mood." % 393 self.parentCommand.componentId) 394 else: 395 common.errorRaise(log.getFailureMessage(failure))
396 397 d.addCallback(cb) 398 d.addErrback(eb) 399 400 return d 401 402
403 -class Component(util.LogCommand):
404 """ 405 @ivar componentId: the component id, passed as an argument 406 @ivar componentState: the component state; set when logged in to manager. 407 @type componentState: L{flumotion.common.state.AdminComponentState} 408 @ivar planetState: the planet state; set when logged in to manager. 409 @type planetState: L{flumotion.common.state.AdminPlanetState} 410 """ 411 description = "Act on a component." 412 usage = "-i [component id]" 413 414 subCommandClasses = [Delete, Invoke, List, DetailedList, UpstreamList, 415 DownstreamList, Mood, Property, Start, Stop] 416 417 componentId = None 418 componentState = None 419 planetState = None 420 workerHeavenState = None 421
422 - def addOptions(self):
423 self.parser.add_option('-i', '--component-id', 424 action="store", dest="componentId", 425 help="component id of the component") 426 self.parser.add_option('-e', '--extra-attrs', 427 action="store", dest="extraAttributes", default="", 428 help="comma-separated extra attributes for the component list")
429
430 - def handleOptions(self, options):
431 self.componentId = options.componentId 432 self.extraAttributes = options.extraAttributes.split(",") 433 # call our callback after connecting 434 self.getRootCommand().loginDeferred.addCallback(self._callback)
435
436 - def _callback(self, result):
437 d = self.parentCommand.medium.callRemote('getPlanetState') 438 439 def gotPlanetStateCb(result): 440 self.planetState = result 441 self.debug('gotPlanetStateCb') 442 443 # only get componentState if we got passed an argument for it 444 if not self.componentId: 445 return 446 447 try: 448 self.componentState = util.findComponent(result, 449 self.componentId) 450 except Exception, e: 451 self.debug(log.getExceptionMessage(e)) 452 common.errorRaise("Invalid component id '%s'" % 453 self.componentId) 454 self.debug('gotPlanetStateCb') 455 if not self.componentState: 456 common.errorRaise('Could not find component %s' % 457 self.componentId)
458 459 def getWorkerHeavenStateCb(result): 460 d = self.parentCommand.medium.callRemote('getWorkerHeavenState') 461 return d
462 463 def gotWorkerHeavenStateCb(result): 464 self.workerHeavenState = result 465 466 d.addCallback(gotPlanetStateCb) 467 d.addCallback(getWorkerHeavenStateCb) 468 d.addCallback(gotWorkerHeavenStateCb) 469 return d 470
471 - def pprint(self, comps):
472 tab = 4 473 cols = [[c[i] for c in comps] for i in xrange(len(comps[0]))] 474 max_widths = [max(map(len, c)) for c in cols] 475 for c in comps: 476 s = " " 477 for i in xrange(len(c)): 478 width = "%d" % (max_widths[i] + tab) 479 s += ('%-' + width + "s") % c[i] 480 self.stdout.write(s + "\n")
481
482 - def print_components(self, components, workers):
483 comps = [] 484 for c in components: 485 workerName = c.get('workerName') 486 host = "unknown" 487 for w in workers: 488 if workerName == w.get('name'): 489 host = w.get('host') 490 break 491 moodName = planet.moods.get(c.get('mood')).name 492 componentAttrs = [c.get('name'), c.get('type'), host, moodName] 493 for attr in self.extraAttributes: 494 if c.hasKey(attr): 495 componentAttrs.append(str(c.get(attr))) 496 comps.append(componentAttrs) 497 self.pprint(comps)
498