blob: a830dbf1274e8833c8baa0d6b6b9364f8d827729 [file] [log] [blame]
#!/usr/bin/env python
import dbus
import BaseHTTPServer
import SocketServer
import json
import socket
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)
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)
class HTTPServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
def __init__(self, address, handler):
bind = True
if os.environ.get('LISTEN_PID', None) == str(os.getpid()):
bind = False
BaseHTTPServer.HTTPServer.__init__(
self, address, handler, bind_and_activate=bind)
if bind is True:
self.server_bind()
else:
FIRST_SYSTEMD_SOCKET_FD = 3
self.socket = socket.fromfd(
FIRST_SYSTEMD_SOCKET_FD,
socket.AF_INET,
socket.SOCK_STREAM)
self.server_activate()
if __name__ == '__main__':
server = HTTPServer(('', 3000), DBusRestHandler)
server.serve_forever()