ambctl: Fix "list" command
[profile/ivi/automotive-message-broker.git] / tools / ambctl.py
1 #!/usr/bin/python
2
3 import argparse
4 import dbus
5 import sys
6 import json
7 import gobject
8 import fileinput
9 import termios, fcntl, os
10 import glib
11 import curses.ascii
12 import traceback
13
14 from dbus.mainloop.glib import DBusGMainLoop
15
16 class bcolors:
17                 HEADER = '\x1b[95m'
18                 OKBLUE = '\x1b[94m'
19                 OKGREEN = '\x1b[92m'
20                 WARNING = '\x1b[93m'
21                 FAIL = '\x1b[91m'
22                 ENDC = '\x1b[0m'
23                 GREEN = '\x1b[32m'
24                 WHITE = '\x1b[37m'
25                 BLUE = '\x1b[34m'
26
27 class Autocomplete:
28         class Cmd:
29                 name = ""
30                 description = ""
31                 def __init__(self, n, d):
32                         self.name = n
33                         self.description = d
34
35         commands = []
36         properties = []
37
38         def __init__(self):
39                 self.commands = []
40                 self.commands.append(Autocomplete.Cmd('help', 'Prints help data (also see COMMAND help)'))
41                 self.commands.append(Autocomplete.Cmd('list', 'List supported ObjectNames'))
42                 self.commands.append(Autocomplete.Cmd('get', 'Get properties from an ObjectName'))
43                 self.commands.append(Autocomplete.Cmd('listen', 'Listen for changes on an ObjectName'))
44                 self.commands.append(Autocomplete.Cmd('set', 'Set a property for an ObjectName'))
45                 self.commands.append(Autocomplete.Cmd('getHistory', 'Get logged data within a time range'))
46                 self.commands.append(Autocomplete.Cmd('plugin', 'enable, disable and get info on a plugin'))
47                 self.commands.append(Autocomplete.Cmd('quit', 'Exit ambctl'))
48
49                 try:
50                         bus = dbus.SystemBus()
51                         managerObject = bus.get_object("org.automotive.message.broker", "/");
52                         managerInterface = dbus.Interface(managerObject, "org.automotive.Manager")
53                         self.properties = managerInterface.List()
54                 except dbus.exceptions.DBusException, error:
55                         print error
56
57         def complete(self, partialString, commandsOnly = False):
58                 results = []
59
60                 sameString = ""
61
62                 for cmd in self.commands:
63                         if not (len(partialString)) or cmd.name.startswith(partialString):
64                                 results.append(cmd.name)
65
66                 if not commandsOnly:
67                         for property in self.properties:
68                                 if not(len(partialString)) or property.startswith(partialString):
69                                         results.append(str(property))
70
71                 if len(results) > 1 and len(results[0]) > 0:
72                         for i in range(len(results[0])):
73                                 for j in range(len(results[0])-i+1):
74                                         if j > len(sameString) and all(results[0][i:i+j] in x for x in results):
75                                                 sameString = results[0][i:i+j]
76                 elif len(results) == 1:
77                         sameString = results[0]
78
79                 return results, sameString
80
81
82 def help():
83                 help = ("Available commands:\n")
84                 autocomplete = Autocomplete()
85                 for cmd in autocomplete.commands:
86                         help += bcolors.HEADER + cmd.name + bcolors.WHITE
87                         for i in range(1, 15 - len(cmd.name)):
88                                 help += " "
89                         help += cmd.description + "\n"
90
91                 return help
92
93 def changed(interface, properties, invalidated):
94         print json.dumps(properties, indent=2)
95
96 def listPlugins():
97         list = []
98         for root, dirs, files in os.walk('@PLUGIN_SEGMENT_INSTALL_PATH@'):
99                 for file in files:
100                         fullpath = root + "/" + file;
101                         pluginFile = open(fullpath)
102                         try:
103                                 data = json.load(pluginFile)
104
105                                 data['segmentPath'] = fullpath
106                                 list.append(data)
107                         except ValueError, e:
108                                 print "error parsing json file", file, ":", e
109                                 traceback.print_stack()
110                         finally: pluginFile.close()
111         return list
112
113 def enablePlugin(pluginName, enabled):
114         return setPluginProperty(pluginName, "enabled", enabled);
115
116 def setPluginProperty(pluginName, key, value):
117         list = listPlugins()
118
119         for plugin in list:
120                 if plugin["name"] == pluginName:
121                         try :
122                                 if key not in plugin:
123                                         print "Key not found: ", key
124                                         return False
125                                 type = plugin[key].__class__
126                                 if type == bool:
127                                         value = value.lower() == "true"
128                                 value = type(value)
129                                 plugin[key] = value
130                                 file = open(plugin["segmentPath"], 'rw+')
131                                 plugin.pop('segmentPath', None)
132                                 fixedData = json.dumps(plugin, separators=(', ', ' : '), indent=4)
133                                 fixedData = fixedData.replace('    ','\t');
134                                 file.truncate()
135                                 file.write(fixedData)
136                                 file.close()
137                                 return True
138                         except IOError, error:
139                                 print error
140                                 return False
141         return False
142 class Subscribe:
143         subscriptions = {}
144 def processCommand(command, commandArgs, noMain=True):
145
146         if command == 'help':
147                 print help()
148                 return 1
149
150
151         def getManager():
152                 try:
153                         bus = dbus.SystemBus()
154                         managerObject = bus.get_object("org.automotive.message.broker", "/");
155                         managerInterface = dbus.Interface(managerObject, "org.automotive.Manager")
156                         return managerInterface, bus
157                 except:
158                         print "Error connecting to AMB.  is AMB running?"
159                         return None
160
161         if command == "list" :
162                 managerInterface, bus = getManager()
163                 if managerInterface == None:
164                         return 0
165                 supportedList = managerInterface.List()
166                 for objectName in supportedList:
167                         print objectName
168                 return 1
169         elif command == "get":
170                 if len(commandArgs) == 0:
171                         commandArgs = ['help']
172                 if commandArgs[0] == "help":
173                         print "ObjectName [ObjectName...]"
174                         return 1
175                 managerInterface, bus = getManager()
176                 if managerInterface == None:
177                         return 0
178                 for objectName in commandArgs:
179                         objects = managerInterface.FindObject(objectName)
180                         print objectName
181                         for o in objects:
182                                 propertiesInterface = dbus.Interface(bus.get_object("org.automotive.message.broker", o),"org.freedesktop.DBus.Properties")
183                                 print json.dumps(propertiesInterface.GetAll("org.automotive."+objectName), indent=2)
184                 return 1
185         elif command == "listen":
186                 off = False
187                 if len(commandArgs) == 0:
188                         commandArgs = ['help']
189                 if commandArgs[0] == "help":
190                         print "[help] [off] ObjectName [ObjectName...]"
191                         return 1
192                 elif commandArgs[0] == "off":
193                         off=True
194                         commandArgs=commandArgs[1:]
195                 managerInterface, bus = getManager()
196                 if managerInterface == None:
197                         return 1
198                 for objectName in commandArgs:
199                         objects = managerInterface.FindObject(objectName)
200                         for o in objects:
201                                 if off == False:
202                                         signalMatch = bus.add_signal_receiver(changed, dbus_interface="org.freedesktop.DBus.Properties", signal_name="PropertiesChanged", path=o)
203                                         Subscribe.subscriptions[o] = signalMatch
204                                 else:
205                                         try:
206                                                 signalMatch = Subscribe.subscriptions.get(o)
207                                                 signalMatch.remove()
208                                                 del Subscribe.subscriptions[o]
209                                         except KeyError:
210                                                 print "not lisenting to object at: ", o
211                                                 pass
212                 if not noMain == True:
213                         try:
214                                 main_loop = gobject.MainLoop(None, False)
215                                 main_loop.run()
216                         except KeyboardInterrupt:
217                                 return 1
218                         except:
219                                 traceback.print_stack()
220         elif command == "set":
221                 if len(commandArgs) == 0:
222                         commandArgs = ['help']
223                 if len(commandArgs) and commandArgs[0] == "help":
224                         print "ObjectName PropertyName VALUE [ZONE]"
225                         return 1
226                 if len(commandArgs) < 3:
227                         print "set requires more arguments (see set help)"
228                         return 1
229                 objectName = commandArgs[0]
230                 propertyName = commandArgs[1]
231                 value = commandArgs[2]
232                 zone = 0
233                 if len(commandArgs) == 4:
234                         zone = int(commandArgs[3])
235                 managerInterface, bus = getManager()
236                 if managerInterface == None:
237                         return 1
238                 object = managerInterface.FindObjectForZone(objectName, zone)
239                 propertiesInterface = dbus.Interface(bus.get_object("org.automotive.message.broker", object),"org.freedesktop.DBus.Properties")
240                 property = propertiesInterface.Get("org.automotive."+objectName, propertyName)
241                 if property.__class__ == dbus.Boolean:
242                         value = value.lower() == "true"
243                 realValue = property.__class__(value)
244                 propertiesInterface.Set("org.automotive."+objectName, propertyName, realValue)
245                 property = propertiesInterface.Get("org.automotive."+objectName, propertyName)
246                 if property == realValue:
247                         print propertyName + " = ", property
248                 else:
249                         print "Error setting property.  Expected value: ", realValue, " Received: ", property
250                 return 1
251         elif command == "getHistory":
252                 if len(commandArgs) == 0:
253                         commandArgs = ['help']
254                 if commandArgs[0] == "help":
255                         print "ObjectName [ZONE] [STARTTIME] [ENDTIME] "
256                         return 1
257                 if len(commandArgs) < 1:
258                         print "getHistory requires more arguments (see getHistory help)"
259                         return 1
260                 objectName = commandArgs[0]
261                 start = 1
262                 if len(commandArgs) >= 3:
263                         start = float(commandArgs[2])
264                 end = 9999999999
265                 if len(commandArgs) >= 4:
266                         end = float(commandArgs[3])
267                 zone = 0
268                 if len(commandArgs) >= 2:
269                         zone = int(commandArgs[1])
270                 managerInterface, bus = getManager()
271                 if managerInterface == None:
272                         return 1
273                 object = managerInterface.FindObjectForZone(objectName, zone);
274                 propertiesInterface = dbus.Interface(bus.get_object("org.automotive.message.broker", object),"org.automotive."+objectName)
275                 print json.dumps(propertiesInterface.GetHistory(start, end), indent=2)
276         elif command == "plugin":
277                 if len(commandArgs) == 0:
278                         commandArgs = ['help']
279                 if commandArgs[0] == 'help':
280                         print "[list] [pluginName] [key value]"
281                         return 1
282                 elif commandArgs[0] == 'list':
283                         for plugin in listPlugins():
284                                 print plugin['name']
285                         return 1
286                 elif len(commandArgs) == 1:
287                         for plugin in listPlugins():
288                                 if plugin['name'] == commandArgs[0]:
289                                         print json.dumps(plugin, indent=4)
290                                         return 1
291                                 else:
292                                         print "name: " + plugin['name'] + "==?" + commandArgs[0]
293                         print "plugin not found: ", commandArgs[0]
294                         return 0
295                 else:
296                         if len(commandArgs) < 3:
297                                 return 1
298                         plugin = commandArgs[0]
299                         key = commandArgs[1]
300                         value = commandArgs[2]
301
302                         if not setPluginProperty(plugin, key, value):
303                                 print "Could not set property"
304                         print plugin, key, ":", value
305                         return 1
306
307         else:
308                 print "Invalid command"
309         return 1
310
311
312
313 parser = argparse.ArgumentParser(prog="ambctl", description='Automotive Message Broker DBus client tool', add_help=False)
314 parser.add_argument('command', metavar='COMMAND [help]', nargs='?', default='stdin', help='amb dbus command')
315 parser.add_argument('commandArgs', metavar='ARG', nargs='*',
316                         help='amb dbus command arguments')
317 parser.add_argument('-h', '--help', help='print help', action='store_true')
318
319 args = parser.parse_args()
320
321 if args.help:
322                 parser.print_help()
323                 print
324                 print help()
325                 sys.exit()
326
327 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
328
329 if args.command == "stdin":
330                 class Data:
331                                 history = []
332                                 line = ""
333                                 templine = ""
334                                 promptAmbctl = "[ambctl]"
335                                 promptEnd = "# "
336                                 fullprompt = promptAmbctl + promptEnd
337                                 curpos = 0
338                                 historypos = -1
339                                 autocomplete = Autocomplete()
340                                 def full_line_len(self):
341                                                 return len(self.fullprompt) + len(self.line)
342                                 def insert(self, str):
343                                                 if self.curpos == len(self.line):
344                                                                 self.line+=str
345                                                                 self.curpos = len(self.line)
346                                                 else:
347                                                                 self.line = self.line[:self.curpos] + str + self.line[self.curpos:]
348                                                                 self.curpos+=1
349                                 def arrow_back(self):
350                                                 if self.curpos > 0:
351                                                                 self.curpos-=1
352                                                                 return True
353                                                 return False
354
355                                 def arrow_forward(self):
356                                                 if self.curpos < len(self.line):
357                                                                 self.curpos+=1
358                                                                 return True
359                                                 return False
360
361                                 def back_space(self):
362                                                 if self.curpos > 0:
363                                                                 self.curpos-=1
364                                                                 self.line = self.line[:self.curpos] + self.line[self.curpos+1:]
365                                                                 return True
366                                                 return False
367                                 def delete(self):
368                                                 if self.curpos < len(self.line):
369                                                                 self.line = self.line[:self.curpos] + self.line[self.curpos+2:]
370                                                                 return True
371                                                 return False
372
373                                 def save_temp(self):
374                                                 if len(self.history)-1 == 0 or len(self.history)-1 != self.historypos:
375                                                                 return
376                                                 self.templine = self.line
377
378                                 def push(self):
379                                                 self.history.append(self.line)
380                                                 self.historypos = len(self.history)-1
381                                                 self.clear()
382
383                                 def set(self, str):
384                                                 self.line = str
385                                                 self.curpos = len(self.line)
386
387                                 def history_up(self):
388                                                 if self.historypos >= 0:
389                                                                 self.line = self.history[self.historypos]
390                                                                 if self.historypos != 0:
391                                                                                 self.historypos-=1
392                                                                 return True
393                                                 return False
394
395                                 def history_down(self):
396                                                 if self.historypos >= 0 and self.historypos < len(self.history)-1:
397                                                                 self.historypos+=1
398                                                                 self.line = self.history[self.historypos]
399
400                                                 else:
401                                                                 self.historypos = len(self.history)-1
402                                                                 self.set(self.templine)
403
404                                                 return True
405
406                                 def clear(self):
407                                                 self.set("")
408                                                 templist = ""
409
410                 def erase_line():
411                                 sys.stdout.write('\x1b[2K\x1b[80D')
412
413                 def cursor_left():
414                                 sys.stdout.write('\x1b[1D')
415
416                 def cursor_right():
417                                 sys.stdout.write('\x1b[1C')
418
419                 def display_prompt():
420                                 sys.stdout.write(bcolors.OKBLUE+Data.promptAmbctl+bcolors.WHITE+Data.promptEnd);
421
422                 def redraw(data):
423                                 erase_line()
424                                 display_prompt()
425                                 sys.stdout.write(data.line)
426                                 cursorpos = len(data.line) - data.curpos
427                                 for x in xrange(cursorpos):
428                                                 cursor_left()
429                                 sys.stdout.flush()
430
431                 def handle_keyboard(source, cond, data):
432                                                 str = source.read()
433                                                 #print "char: ", ord(str)
434
435                                                 if len(str) > 1:
436                                                         if ord(str[0]) == 27 and ord(str[1]) == 91 and ord(str[2]) == 68:
437                                                                 #left arrow
438                                                                 if data.arrow_back():
439                                                                         cursor_left()
440                                                                         sys.stdout.flush()
441                                                         elif ord(str[0]) == 27 and ord(str[1]) == 91 and ord(str[2]) == 67:
442                                                                 #right arrow
443                                                                 if data.arrow_forward():
444                                                                         cursor_right()
445                                                                         sys.stdout.flush()
446                                                         elif ord(str[0]) == 27 and ord(str[1]) == 91 and ord(str[2]) == 70:
447                                                                 #end
448                                                                 while data.arrow_forward():
449                                                                         cursor_right()
450                                                                 sys.stdout.flush()
451                                                         elif ord(str[0]) == 27 and ord(str[1]) == 91 and ord(str[2]) == 72: #home
452                                                                 while data.arrow_back():
453                                                                         cursor_left()
454                                                                 sys.stdout.flush()
455                                                         elif len(str) == 4 and ord(str[0]) == 27 and ord(str[1]) == 91 and ord(str[2]) == 51 and ord(str[3]) == 126:
456                                                                 #del
457                                                                 data.delete()
458                                                                 redraw(data)
459                                                         elif ord(str[0]) == 27 and ord(str[1]) == 91 and ord(str[2]) == 65:
460                                                                 #up arrow
461                                                                 data.save_temp()
462                                                                 data.history_up()
463                                                                 while data.arrow_forward():
464                                                                         cursor_right()
465                                                                 redraw(data)
466                                                         elif ord(str[0]) == 27 and ord(str[1]) == 91 and ord(str[2]) == 66:
467                                                                 #down arrow
468                                                                 data.history_down()
469                                                                 while data.arrow_forward():
470                                                                         cursor_right()
471                                                                 redraw(data)
472                                                 elif ord(str) == 10:
473                                                         #enter
474                                                         if data.line == "":
475                                                                 return True
476                                                         print ""
477                                                         words = data.line.split(' ')
478                                                         if words[0] == "quit":
479                                                                 termios.tcsetattr(fd, termios.TCSAFLUSH, old)
480                                                                 fcntl.fcntl(fd, fcntl.F_SETFL, oldflags)
481                                                                 sys.exit()
482                                                         try:
483                                                                 if len(words) > 1:
484                                                                         processCommand(words[0], words[1:])
485                                                                 else:
486                                                                         processCommand(words[0], [])
487                                                         except dbus.exceptions.DBusException, error:
488                                                                 print error
489                                                         except:
490                                                                 print "Error running command ", sys.exc_info()[0]
491                                                                 traceback.print_stack()
492                                                         data.push();
493                                                         data.clear()
494                                                         redraw(data)
495                                                 elif ord(str) == 127: #backspace
496                                                         data.back_space()
497                                                         redraw(data)
498                                                 elif ord(str) == 9:
499                                                         #tab
500                                                         #get last string:
501                                                         wordsList = data.line.split(' ')
502                                                         toComplete = wordsList[-1]
503                                                         results, samestring = data.autocomplete.complete(toComplete)
504                                                         if len(samestring) and len(samestring) > len(toComplete) and not (samestring == toComplete):
505                                                                 if len(wordsList) > 1:
506                                                                         data.line = ' '.join(wordsList[0:-1]) + ' ' + samestring
507                                                                 else:
508                                                                         data.line = samestring
509                                                                 while data.arrow_forward():
510                                                                         cursor_right()
511
512                                                         elif len(results) and not results[0] == toComplete:
513                                                                 print ''
514                                                                 if len(results) <= 3:
515                                                                         print ' '.join(results)
516                                                                 else:
517                                                                         longestLen = 0
518                                                                         for r in results:
519                                                                                 if len(r) > longestLen:
520                                                                                         longestLen = len(r)
521                                                                         i=0
522                                                                         while i < len(results):
523                                                                                 row = ""
524                                                                                 numCols = 3
525                                                                                 if len(results) < i+3:
526                                                                                         numCols = len(results) - i
527                                                                                 for n in xrange(numCols):
528                                                                                         row += results[i]
529                                                                                         for n in xrange((longestLen + 5) - len(results[i])):
530                                                                                                 row += ' '
531                                                                                         i = i + 1
532                                                                                 print row
533
534                                                         redraw(data)
535                                                 elif curses.ascii.isprint(ord(str)) or ord(str) == curses.ascii.SP: #regular text
536                                                         data.insert(str)
537                                                         redraw(data)
538
539                                                 return True
540                 print "@PROJECT_PRETTY_NAME@ @PROJECT_VERSION@"
541
542                 data = Data()
543                 fd = sys.stdin.fileno()
544                 old = termios.tcgetattr(fd)
545                 new = termios.tcgetattr(fd)
546                 new[3] = new[3] & ~termios.ICANON & ~termios.ECHO
547                 termios.tcsetattr(fd, termios.TCSANOW, new)
548
549                 oldflags = fcntl.fcntl(fd, fcntl.F_GETFL)
550                 fcntl.fcntl(fd, fcntl.F_SETFL, oldflags | os.O_NONBLOCK)
551
552                 io_stdin = glib.IOChannel(fd)
553                 io_stdin.add_watch(glib.IO_IN, handle_keyboard, data)
554
555                 try:
556                         erase_line()
557                         display_prompt()
558                         sys.stdout.flush()
559                         main_loop = gobject.MainLoop(None, False)
560                         main_loop.run()
561                 except KeyboardInterrupt:
562                         sys.exit()
563                 except:
564                         traceback.print_stack()
565                 finally:
566                         termios.tcsetattr(fd, termios.TCSAFLUSH, old)
567                         fcntl.fcntl(fd, fcntl.F_SETFL, oldflags)
568                         print ""
569                         sys.exit()
570
571 else:
572         try:
573                 processCommand(args.command, args.commandArgs, False)
574         except dbus.exceptions.DBusException, error:
575                 print error