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   
 22  from flumotion.common import errors, planet, log 
 23  from flumotion.monitor.nagios import util 
 24  from flumotion.common.planet import moods 
 25   
 26  from flumotion.admin.command import common 
 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 Mood(common.AdminCommand):
201 description = "Check the mood of a component." 202
203 - def doCallback(self, args):
204 if not self.parentCommand.componentId: 205 common.errorRaise("Please specify a component id " 206 "with 'component -i [component-id]'") 207 208 p = self.parentCommand 209 moodValue = p.componentState.get('mood') 210 moodName = planet.moods.get(moodValue).name 211 self.stdout.write("Component '%s' is %s.\n" % (p.componentId, 212 moodName))
213 214
215 -class PropertyGet(common.AdminCommand):
216 description = "Get a property of a component." 217 name = 'get' 218
219 - def do(self, args):
220 if not args: 221 return common.errorReturn('Please specify a property to get.') 222 223 self._propertyName = args[0] 224 225 return common.AdminCommand.do(self, args)
226
227 - def doCallback(self, args):
228 u = self.parentCommand.uiState 229 name = self._propertyName 230 231 if not u.hasKey(name): 232 common.errorRaise("Component '%s' does not have property '%s'." % ( 233 self.parentCommand.parentCommand.componentId, name)) 234 235 self.stdout.write("Property '%s' is '%r'.\n" % ( 236 name, u.get(name)))
237 238
239 -class PropertyList(common.AdminCommand):
240 description = "List properties of a component." 241 name = 'list' 242
243 - def doCallback(self, args):
244 l = self.parentCommand.uiState.keys() 245 l.sort() 246 self.stdout.write('Properties:\n') 247 for p in l: 248 self.stdout.write('- %s\n' % p)
249 250 # FIXME: why is this called property when it really is about ui state ? 251 252
253 -class Property(util.LogCommand):
254 """ 255 @param uiState: the ui state of the component; set after logging in. 256 """ 257 258 description = "Act on properties of a component." 259 260 subCommandClasses = [PropertyGet, PropertyList] 261
262 - def handleOptions(self, options):
263 if not self.parentCommand.componentId: 264 common.errorRaise("Please specify a component id " 265 "with 'component -i [component-id]'") 266 267 # call our callback after connecting 268 d = self.getRootCommand().loginDeferred 269 d.addCallback(self._callback) 270 d.addErrback(self._errback)
271
272 - def _callback(self, result):
273 274 def getUIStateCb(uiState): 275 self.uiState = uiState
276 277 componentCommand = self.parentCommand 278 model = componentCommand.parentCommand.medium 279 d = model.componentCallRemote( 280 componentCommand.componentState, 'getUIState') 281 d.addCallback(getUIStateCb) 282 return d
283
284 - def _errback(self, failure):
285 failure.trap(errors.SleepingComponentError) 286 common.errorRaise("Component '%s' is sleeping." % 287 self.parentCommand.componentId)
288 289
290 -class Start(common.AdminCommand):
291 description = "Start a component." 292
293 - def doCallback(self, args):
294 if not self.parentCommand.componentId: 295 common.errorRaise("Please specify a component id " 296 "with 'component -i [component-id]'") 297 298 p = self.parentCommand 299 moodValue = p.componentState.get('mood') 300 if moodValue == moods.happy.value: 301 self.stdout.write("Component is already happy.\n") 302 return 0 303 304 d = self.getRootCommand().medium.callRemote('componentStart', 305 self.parentCommand.componentState) 306 307 def cb(result): 308 self.stdout.write("Started component.\n")
309 310 def eb(failure): 311 if failure.trap(errors.ComponentMoodError): 312 common.errorRaise("Component '%s' is in the wrong mood." % 313 self.parentCommand.componentId) 314 else: 315 common.errorRaise(log.getFailureMessage(failure))
316 317 d.addCallback(cb) 318 d.addErrback(eb) 319 320 return d 321 322
323 -class Stop(common.AdminCommand):
324 description = "Stop a component." 325
326 - def doCallback(self, args):
327 if not self.parentCommand.componentId: 328 common.errorRaise("Please specify a component id " 329 "with 'component -i [component-id]'") 330 331 p = self.parentCommand 332 moodValue = p.componentState.get('mood') 333 if moodValue == moods.sleeping.value: 334 self.stdout.write("Component is already sleeping.\n") 335 return 0 336 337 d = self.getRootCommand().medium.callRemote('componentStop', 338 self.parentCommand.componentState) 339 340 def cb(result): 341 self.stdout.write("Stopped component.\n")
342 343 def eb(failure): 344 if failure.trap(errors.ComponentMoodError): 345 common.errorRaise("Component '%s' is in the wrong mood." % 346 self.parentCommand.componentId) 347 else: 348 common.errorRaise(log.getFailureMessage(failure))
349 350 d.addCallback(cb) 351 d.addErrback(eb) 352 353 return d 354 355
356 -class Component(util.LogCommand):
357 """ 358 @ivar componentId: the component id, passed as an argument 359 @ivar componentState: the component state; set when logged in to manager. 360 @type componentState: L{flumotion.common.state.AdminComponentState} 361 @ivar planetState: the planet state; set when logged in to manager. 362 @type planetState: L{flumotion.common.state.AdminPlanetState} 363 """ 364 description = "Act on a component." 365 usage = "-i [component id]" 366 367 subCommandClasses = [Delete, Invoke, List, DetailedList, 368 UpstreamList, Mood, Property, Start, Stop] 369 370 componentId = None 371 componentState = None 372 planetState = None 373 workerHeavenState = None 374
375 - def addOptions(self):
376 self.parser.add_option('-i', '--component-id', 377 action="store", dest="componentId", 378 help="component id of the component")
379
380 - def handleOptions(self, options):
381 self.componentId = options.componentId 382 # call our callback after connecting 383 self.getRootCommand().loginDeferred.addCallback(self._callback)
384
385 - def _callback(self, result):
386 d = self.parentCommand.medium.callRemote('getPlanetState') 387 388 def gotPlanetStateCb(result): 389 self.planetState = result 390 self.debug('gotPlanetStateCb') 391 392 # only get componentState if we got passed an argument for it 393 if not self.componentId: 394 return 395 396 try: 397 self.componentState = util.findComponent(result, 398 self.componentId) 399 except Exception, e: 400 self.debug(log.getExceptionMessage(e)) 401 common.errorRaise("Invalid component id '%s'" % 402 self.componentId) 403 self.debug('gotPlanetStateCb') 404 if not self.componentState: 405 common.errorRaise('Could not find component %s' % 406 self.componentId)
407 408 def getWorkerHeavenStateCb(result): 409 d = self.parentCommand.medium.callRemote('getWorkerHeavenState') 410 return d
411 412 def gotWorkerHeavenStateCb(result): 413 self.workerHeavenState = result 414 415 d.addCallback(gotPlanetStateCb) 416 d.addCallback(getWorkerHeavenStateCb) 417 d.addCallback(gotWorkerHeavenStateCb) 418 return d 419
420 - def pprint(self, comps):
421 tab = 4 422 cols = [[c[i] for c in comps] for i in xrange(len(comps[0]))] 423 max_widths = [max(map(len, c)) for c in cols] 424 for c in comps: 425 s = " " 426 for i in xrange(len(c)): 427 width = "%d" % (max_widths[i] + tab) 428 s += ('%-' + width + "s") % c[i] 429 self.stdout.write(s + "\n")
430
431 - def print_components(self, components, workers):
432 comps = [] 433 for c in components: 434 workerName = c.get('workerName') 435 host = "unknown" 436 for w in workers: 437 if workerName == w.get('name'): 438 host = w.get('host') 439 break 440 moodName = planet.moods.get(c.get('mood')).name 441 comps.append((c.get('name'), c.get('type'), host, moodName)) 442 self.pprint(comps)
443