blob: 600b3d58af65c23eb449f7b8b9b57562204e0c2b [file] [log] [blame]
#!/usr/bin/env python
# Contributors Listed Below - COPYRIGHT 2015
# [+] International Business Machines Corp.
#
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied. See the License for the specific language governing
# permissions and limitations under the License.
import BaseHTTPServer
import SocketServer
import json
import dbus
from OpenBMCMapper import Path
import OpenBMCMapper
class RestException(Exception):
def __init__(self, msg, http_status=403):
self.status = http_status
super(RestException, self).__init__(msg)
class Response(object):
def render(self, handler):
raise NotImplemented()
class ErrorResponse(Response):
def __init__(self, ex):
self.ex = ex
def render(self, handler):
err = {'status': 'error', 'error': self.ex.message,}
handler.send_response(self.ex.status)
handler.send_header('Content-Type', 'application/json')
handler.end_headers()
handler.wfile.write(json.dumps(err, indent=2, sort_keys=True))
class JSONResponse(Response):
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 RequestHandler(object):
def __init__(self, req, path, data):
self.req = req
self.path = path
self.bus = req.server.bus
self.mapper = req.server.mapper
self.data = data
def do_command(self):
f = getattr(self, 'do_' + self.req.command)
return f()
def do_GET(self):
raise RestException("Not Implemented", 501)
def do_PUT(self):
raise RestException("Not Implemented", 501)
def do_POST(self):
raise RestException("Not Implemented", 501)
def do_PATCH(self):
raise RestException("Not Implemented", 501)
def do_DELETE(self):
raise RestException("Not Implemented", 501)
class MethodHandler(RequestHandler):
def __init__(self, req, path, data):
super(MethodHandler, self).__init__(req, path, data)
self.method = Path(self.req.path).rel(first = -1)
def find_method_in_interface(self, obj, interface):
try:
iface = dbus.Interface(obj, interface)
return getattr(iface, self.method)
except dbus.DBusException:
return None
def find_method_on_bus(self, bus, interfaces):
obj = self.bus.get_object(bus, self.path)
for i in interfaces:
m = self.find_method_in_interface(obj, i)
if not m:
continue
return m
def find_method(self):
busses = self.mapper.GetTree(
self.path, 0, 'exact')[self.path]
for items in busses.iteritems():
m = self.find_method_on_bus(*items)
if not m:
continue
return m
def do_POST(self):
try:
method = self.find_method()
except:
raise RestException("Not Found", 404)
try:
d = { 'result': method(*self.data),
'status': 'OK'}
except Exception, e:
d = { 'error': str(e),
'status': 'error'}
return d
class InstanceHandler(RequestHandler):
def __init__(self, req, path, data, busses):
super(InstanceHandler, self).__init__(req, path, data)
self.busses = busses
def get_one_iface(self, properties_iface, iface):
try:
return properties_iface.GetAll(iface)
except:
# interface doesn't have any properties
return {}
def get_one_bus(self, bus, interfaces):
properties = {}
obj = self.bus.get_object(bus, self.path)
properties_iface = dbus.Interface(
obj, dbus_interface=dbus.PROPERTIES_IFACE)
for i in interfaces:
properties.update(self.get_one_iface(properties_iface, i))
return properties
def do_GET(self):
properties = {}
for item in self.busses.iteritems():
properties.update(self.get_one_bus(*item))
return properties
def try_set_one_interface(self, prop, value, properties_iface, interface):
try:
properties_iface.Set(interface, prop, value)
return True
except:
# property doesn't live on this interface/bus
return False
def try_set_one_bus(self, prop, value, bus, interfaces):
obj = self.bus.get_object(bus, self.path)
properties_iface = dbus.Interface(
obj, dbus_interface=dbus.PROPERTIES_IFACE)
for iface in interfaces:
if self.try_set_one_interface(prop, value,
properties_iface, iface):
return True
return False
def set_one_property(self, prop, value):
for item in self.busses.iteritems():
if not self.try_set_one_bus(prop, value, *item):
raise RestException("Not Found", 404)
def validate_json(self):
if type(self.data) != dict:
raise RestException("Bad Request", 400)
obj = self.do_GET()
if len(self.data) != len(obj):
raise RestException("Bad Request", 400)
for x in obj.iterkeys():
if x not in self.data:
raise RestException("Bad Request", 400)
def do_PUT(self):
try:
self.validate_json()
for p in self.data.iteritems():
self.set_one_property(*p)
d = { 'status': 'OK'}
except Exception, e:
d = { 'error': str(e),
'status': 'error'}
return d
def do_POST(self):
for p in self.data.iteritems():
self.set_one_property(*p)
class AttrHandler(RequestHandler):
def __init__(self, req, path, data):
super(AttrHandler, self).__init__(req, path, data)
try:
self.inst = InstanceHandler(req, path, data,
self.mapper.GetTree(path, 0, 'exact')[path])
except KeyError:
raise RestException("Not Found", 404)
self.attr = Path(self.req.path).rel(first = -1)
def do_GET(self):
obj = self.inst.do_GET()
try:
return obj[self.attr]
except KeyError:
raise RestException("Not Found", 404)
def do_PUT(self):
self.inst.set_one_property(self.attr, self.data)
class TypesHandler(RequestHandler):
def __init__(self, req, path, data):
super(TypesHandler, self).__init__(req, path, data)
def do_GET(self):
types = self.mapper.GetTreePaths(self.path, 1, 'exact')
if not types:
raise RestException("Not Found", 404)
return types
class ListHandler(RequestHandler):
def __init__(self, req, path, data):
super(ListHandler, self).__init__(req, path, data)
def do_GET(self):
objs = self.mapper.GetTree(self.path, -1, 'fuzzy')
if self.path in objs:
del objs[self.path]
if not objs:
raise RestException("Not Found", 404)
return objs.keys()
class EnumerateHandler(RequestHandler):
def __init__(self, req, path, data):
super(EnumerateHandler, self).__init__(req, path, data)
def do_GET(self):
objs = {}
mapper_data = self.mapper.GetTree(self.path, -1, 'fuzzy')
if self.path in mapper_data:
del mapper_data[self.path]
for x,y in mapper_data.iteritems():
objs[x] = InstanceHandler(self.req, x, self.data, y).do_GET()
if not objs:
raise RestException("Not Found", 404)
return objs
class DBusRestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def get_real_handler(self, data):
path = Path(self.path)
if self.path[-1] == '/':
return TypesHandler(self, path.fq(), data)
if path.parts[-1] == 'list':
return ListHandler(self, path.fq(last = -1), data)
if path.parts[-1] == 'enumerate':
return EnumerateHandler(self, path.fq(last = -1), data)
if path.parts[-2] == 'attr':
return AttrHandler(self, path.fq(last = -2), data)
if path.parts[-2] == 'action':
return MethodHandler(self, path.fq(last = -2), data)
# have to do an objectmapper query at this point
mapper_entry = self.server.mapper.GetTree(
path.fq(), 0, 'exact')
if mapper_entry:
return InstanceHandler(self, path.fq(), data,
mapper_entry[path.fq()])
raise RestException("Not Found", 404)
def do_command(self):
data = None
try:
if self.command in ['POST', 'PUT', 'PATCH']:
length = int(self.headers.getheader(
'content-length'))
data = json.loads(self.rfile.read(length))
resp = self.get_real_handler(data).do_command()
if not resp:
resp = {'status': 'OK' }
response = JSONResponse(resp)
except RestException, ex:
response = ErrorResponse(ex)
response.render(self)
self.wfile.close()
def do_GET(self):
return self.do_command()
def do_POST(self):
return self.do_command()
def do_PATCH(self):
return self.do_command()
def do_PUT(self):
return self.do_command()
def do_DELETE(self):
return self.do_command()
class HTTPServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
def __init__(self, bind, handler):
BaseHTTPServer.HTTPServer.__init__(self, bind, handler)
self.bus = dbus.SystemBus()
mapper = self.bus.get_object(OpenBMCMapper.MAPPER_NAME,
OpenBMCMapper.MAPPER_PATH)
self.mapper = dbus.Interface(mapper,
dbus_interface = OpenBMCMapper.MAPPER_IFACE)
if __name__ == '__main__':
bus = dbus.SystemBus()
server = HTTPServer(('', 80), DBusRestHandler)
server.serve_forever()