|  | #!/usr/bin/env python | 
|  |  | 
|  | import dbus | 
|  | import BaseHTTPServer | 
|  | import SocketServer | 
|  | import json | 
|  | import os | 
|  | import sys | 
|  | from xml.etree import ElementTree | 
|  | from obmc.dbuslib.introspection import IntrospectionParser | 
|  |  | 
|  | busses = { | 
|  | 'system': dbus.SystemBus, | 
|  | } | 
|  |  | 
|  | class DBusRestException(Exception): | 
|  | def __init__(self, message, code=403): | 
|  | self.code = code | 
|  | super(DBusRestException, self).__init__(message) | 
|  |  | 
|  | class DBusRestResponse(object): | 
|  |  | 
|  | def render(self, handler): | 
|  | raise NotImplemented() | 
|  |  | 
|  | class DBusRestJSONResponse(DBusRestResponse): | 
|  | def __init__(self, data): | 
|  | self.data = data | 
|  |  | 
|  | def render(self, handler): | 
|  | handler.send_response(200) | 
|  | handler.send_header('Content-Type', 'application/json') | 
|  | handler.end_headers() | 
|  | handler.wfile.write(json.dumps(self.data, indent=2, sort_keys=True)) | 
|  |  | 
|  | class DBusRestResourceResponse(DBusRestResponse): | 
|  | mime_map = { | 
|  | 'html': 'text/html', | 
|  | 'js': 'text/javascript', | 
|  | 'css': 'text/css', | 
|  | 'png': 'image/png', | 
|  | 'gif': 'image/gif', | 
|  | } | 
|  | resource_base = os.path.join(sys.prefix, 'share', | 
|  | os.path.basename(__file__), 'resources') | 
|  |  | 
|  | def __init__(self, name): | 
|  | (_, ext) = os.path.splitext(name) | 
|  | self.content_type = self.mime_map.get(ext[1:], | 
|  | 'application/octet-stream') | 
|  | try: | 
|  | self.data = open(os.path.join(self.resource_base, name)).read() | 
|  | except IOError: | 
|  | raise DBusRestException("Not Found", 404) | 
|  |  | 
|  | def render(self, handler): | 
|  | handler.send_response(200) | 
|  | handler.send_header('Content-Type', | 
|  | self.content_type + '; charset=utf-8') | 
|  | handler.end_headers() | 
|  | handler.wfile.write(self.data) | 
|  |  | 
|  | class DBusRestErrorResonse(DBusRestResponse): | 
|  | def __init__(self, ex): | 
|  | self.ex = ex | 
|  |  | 
|  | def render(self, handler): | 
|  | err = { | 
|  | 'status': 'error', | 
|  | 'error': self.ex.message, | 
|  | } | 
|  | handler.send_response(self.ex.code) | 
|  | handler.send_header('Content-Type', 'application/json') | 
|  | handler.end_headers() | 
|  | handler.wfile.write(json.dumps(err, indent=2, sort_keys=True)) | 
|  |  | 
|  |  | 
|  | class DBusRestHandler(BaseHTTPServer.BaseHTTPRequestHandler): | 
|  |  | 
|  | def handle_root(self): | 
|  | return DBusRestJSONResponse({ | 
|  | 'status': 'ok', | 
|  | 'busses': [ {'name': x} for x in busses.keys() ], | 
|  | }) | 
|  |  | 
|  | def handle_bus(self): | 
|  | objects = self.bus.list_names() | 
|  | objects.sort() | 
|  | return DBusRestJSONResponse({ | 
|  | 'status': 'ok', | 
|  | 'objects': [ {'name': x} for x in objects ], | 
|  | }) | 
|  |  | 
|  | def get_object_or_404(self, bus_name, obj_path): | 
|  | try: | 
|  | obj = self.bus.get_object(bus_name, obj_path) | 
|  | except dbus.DBusException: | 
|  | raise DBusRestException("Object Not Found", 404) | 
|  | return obj | 
|  |  | 
|  | def handle_service(self, bus_name): | 
|  | data = IntrospectionParser(bus_name, self.bus).introspect() | 
|  | objects = [ { 'path': x } for x in data.iterkeys() ] | 
|  |  | 
|  | return DBusRestJSONResponse({ | 
|  | 'status': 'ok', | 
|  | 'bus_name': bus_name, | 
|  | 'objects': objects, | 
|  | }) | 
|  |  | 
|  | def handle_object(self, obj): | 
|  | iface = dbus.Interface(obj, 'org.freedesktop.DBus.Introspectable') | 
|  |  | 
|  | interfaces = [] | 
|  | tree = ElementTree.fromstring(iface.Introspect()) | 
|  | for node in tree: | 
|  | if node.tag == 'interface': | 
|  | interfaces.append({'name': node.attrib['name']}) | 
|  |  | 
|  | return DBusRestJSONResponse({ | 
|  | 'status': 'ok', | 
|  | 'bus_name': obj.requested_bus_name, | 
|  | 'object_path': obj.object_path, | 
|  | 'interfaces': interfaces, | 
|  | }) | 
|  |  | 
|  | def handle_interface(self, obj, interface_name): | 
|  | iface = dbus.Interface(obj, 'org.freedesktop.DBus.Introspectable') | 
|  |  | 
|  | interface_node = None | 
|  | tree = ElementTree.fromstring(iface.Introspect()) | 
|  | for node in tree: | 
|  | if (node.tag == 'interface' and | 
|  | node.attrib['name'] == interface_name): | 
|  | interface_node = node | 
|  | break | 
|  |  | 
|  | if interface_node is None: | 
|  | raise DBusRestException("Interface Not Found", 404) | 
|  |  | 
|  | methods = [] | 
|  | signals = [] | 
|  | base_uri = ('/bus/' + self.bus_name + '/' + obj.requested_bus_name + | 
|  | obj.object_path + '/' + interface_name); | 
|  |  | 
|  |  | 
|  | def parse_method(node): | 
|  | args = [] | 
|  | for child in node: | 
|  | if child.tag == 'arg': | 
|  | args.append(child.attrib) | 
|  |  | 
|  | return { | 
|  | 'name': node.attrib['name'], | 
|  | 'args': args, | 
|  | 'uri': '/'.join([base_uri, node.attrib['name']]), | 
|  | } | 
|  |  | 
|  | def parse_signal(node): | 
|  | args = [] | 
|  | for child in node: | 
|  | if child.tag == 'arg': | 
|  | args.append(child.attrib) | 
|  |  | 
|  | return { | 
|  | 'name': node.attrib['name'], | 
|  | 'args': args | 
|  | } | 
|  |  | 
|  | for node in interface_node: | 
|  | if node.tag == 'method': | 
|  | methods.append(parse_method(node)) | 
|  | elif node.tag == 'signal': | 
|  | signals.append(parse_signal(node)) | 
|  |  | 
|  | iface = dbus.Interface(obj, 'org.freedesktop.DBus.Properties') | 
|  | try: | 
|  | properties = iface.GetAll(interface_name) | 
|  | except dbus.DBusException: | 
|  | properties = [] | 
|  |  | 
|  | return DBusRestJSONResponse({ | 
|  | 'status': 'ok', | 
|  | 'bus_name': obj.requested_bus_name, | 
|  | 'object_path': obj.object_path, | 
|  | 'interface': interface_name, | 
|  | 'methods': methods, | 
|  | 'signals': signals, | 
|  | 'properties': properties, | 
|  | }) | 
|  |  | 
|  | def handle_property(self, obj, interface_name, property_name): | 
|  | iface = dbus.Interface(obj, 'org.freedesktop.DBus.Properties') | 
|  | try: | 
|  | value = iface.Get(interface_name, property_name) | 
|  | except dbus.DBusException: | 
|  | raise DBusRestException("Property Not Found", 404) | 
|  |  | 
|  | return DBusRestJSONResponse({ | 
|  | 'status': 'ok', | 
|  | 'bus_name': obj.requested_bus_name, | 
|  | 'object_path': obj.object_path, | 
|  | 'interface': interface_name, | 
|  | 'property': property_name, | 
|  | 'value': value, | 
|  | }) | 
|  |  | 
|  | def dispatch_bus(self, parts): | 
|  |  | 
|  | if not parts: | 
|  | return self.handle_root() | 
|  |  | 
|  | self.bus_name = parts.pop(0) | 
|  | busfn = busses.get(self.bus_name, None) | 
|  | if busfn is None: | 
|  | raise DBusRestException("Bus Not Found", 404) | 
|  |  | 
|  | self.bus = busfn() | 
|  |  | 
|  | if not parts: | 
|  | return self.handle_bus() | 
|  |  | 
|  | bus_name = parts.pop(0) | 
|  |  | 
|  | if not parts: | 
|  | return self.handle_service(bus_name) | 
|  |  | 
|  | # construct our object path, stopping when we get to an | 
|  | # interface name | 
|  | object_path = '' | 
|  | while parts: | 
|  | if '.' in parts[0]: | 
|  | break | 
|  | object_path += '/' + parts.pop(0) | 
|  |  | 
|  | obj = self.get_object_or_404(bus_name, object_path) | 
|  |  | 
|  | if not parts: | 
|  | return self.handle_object(obj) | 
|  |  | 
|  | interface_name = parts.pop(0) | 
|  | if not parts: | 
|  | return self.handle_interface(obj, interface_name) | 
|  |  | 
|  | property_name = parts.pop(0) | 
|  | if not parts: | 
|  | return self.handle_property(obj, interface_name, property_name) | 
|  |  | 
|  | raise DBusRestException("Not Found", 404) | 
|  |  | 
|  | def dispatch_resource(self, parts): | 
|  | return DBusRestResourceResponse(parts[0]) | 
|  |  | 
|  | def dispatch(self): | 
|  | parts = filter(bool, self.path.split('/')) | 
|  |  | 
|  | if parts: | 
|  | action = parts.pop(0) | 
|  |  | 
|  | if action == 'bus': | 
|  | return self.dispatch_bus(parts) | 
|  |  | 
|  | elif action == 'res': | 
|  | return self.dispatch_resource(parts) | 
|  |  | 
|  | raise DBusRestException("Not Found", 404) | 
|  |  | 
|  | return self.dispatch_resource(['index.html']) | 
|  |  | 
|  | def handle_call(self, obj, interface_name, call_name, data): | 
|  | iface = dbus.Interface(obj, interface_name) | 
|  | call = getattr(iface, call_name) | 
|  | d = { | 
|  | 'obj': obj.object_path, | 
|  | 'interface': interface_name, | 
|  | 'call': call_name, | 
|  | } | 
|  | try: | 
|  | d.update({ | 
|  | 'result': call(*data), | 
|  | 'status': 'ok', | 
|  | }) | 
|  | except Exception, e: | 
|  | d.update({ | 
|  | 'error': str(e), | 
|  | 'status': 'error', | 
|  | }) | 
|  | return DBusRestJSONResponse(d) | 
|  |  | 
|  | def dispatch_call(self, data): | 
|  | parts = filter(bool, self.path.split('/')) | 
|  | if not parts: | 
|  | raise DBusRestException("Not Found", 404) | 
|  |  | 
|  | action = parts.pop(0) | 
|  | if action != 'bus': | 
|  | raise DBusRestException("Not Found", 404) | 
|  |  | 
|  | busfn = busses.get(parts.pop(0), None) | 
|  | if busfn is None: | 
|  | raise DBusRestException("Bus Not Found", 404) | 
|  |  | 
|  | self.bus = busfn() | 
|  |  | 
|  | if not parts: | 
|  | raise DBusRestException("Not Found", 404) | 
|  |  | 
|  | bus_name = parts.pop(0) | 
|  |  | 
|  | if not parts: | 
|  | raise DBusRestException("Not Found", 404) | 
|  |  | 
|  | # construct our object path, stopping when we get to an | 
|  | # interface name | 
|  | object_path = '' | 
|  | while parts: | 
|  | if '.' in parts[0]: | 
|  | break | 
|  | object_path += '/' + parts.pop(0) | 
|  |  | 
|  | obj = self.get_object_or_404(bus_name, object_path) | 
|  |  | 
|  | if not parts: | 
|  | raise DBusRestException("Not Found", 404) | 
|  |  | 
|  | interface_name = parts.pop(0) | 
|  | if not parts: | 
|  | raise DBusRestException("Not Found", 404) | 
|  |  | 
|  | call_name = parts.pop(0) | 
|  |  | 
|  | return self.handle_call(obj, interface_name, call_name, data) | 
|  |  | 
|  | def do_GET(self): | 
|  | try: | 
|  | response = self.dispatch() | 
|  | except DBusRestException, ex: | 
|  | response = DBusRestErrorResonse(ex) | 
|  |  | 
|  | response.render(self) | 
|  | self.wfile.close() | 
|  |  | 
|  | def do_POST(self): | 
|  | length = int(self.headers.getheader('content-length')) | 
|  | data = json.loads(self.rfile.read(length)) | 
|  | try: | 
|  | response = self.dispatch_call(data) | 
|  | except DBusRestException, ex: | 
|  | response = DBusRestErrorResonse(ex) | 
|  |  | 
|  | response.render(self) | 
|  | self.wfile.close() | 
|  |  | 
|  |  | 
|  | class HTTPServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer): | 
|  | pass | 
|  |  | 
|  | if __name__ == '__main__': | 
|  | server = HTTPServer(('', 3000), DBusRestHandler) | 
|  | server.serve_forever() |