[PATCH phosphor-rest-server] REST server work in progress

OpenBMC Patches patches at stwcx.xyz
Wed Oct 28 07:59:28 AEDT 2015


From: Brad Bishop <bradleyb at us.ibm.com>

Supports:
 list, enumerate, attr, instance GET operations
 method, instance POST
 attr, instance PUT
---
 phosphor-rest | 342 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 setup.cfg     |   2 +
 setup.py      |   5 +
 3 files changed, 349 insertions(+)
 create mode 100644 phosphor-rest
 create mode 100644 setup.cfg
 create mode 100644 setup.py

diff --git a/phosphor-rest b/phosphor-rest
new file mode 100644
index 0000000..600b3d5
--- /dev/null
+++ b/phosphor-rest
@@ -0,0 +1,342 @@
+#!/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()
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..ed3bf6e
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,2 @@
+[install]
+install_scripts=/usr/sbin
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..2ddb129
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,5 @@
+from distutils.core import setup
+setup(name='Phosphor REST',
+      version='1.0',
+      scripts=['phosphor-rest']
+      )
-- 
2.6.0




More information about the openbmc mailing list