blob: 600b3d58af65c23eb449f7b8b9b57562204e0c2b [file] [log] [blame]
Brad Bishopaa65f6e2015-10-27 16:28:51 -04001#!/usr/bin/env python
2
3# Contributors Listed Below - COPYRIGHT 2015
4# [+] International Business Machines Corp.
5#
6#
7# Licensed under the Apache License, Version 2.0 (the "License");
8# you may not use this file except in compliance with the License.
9# You may obtain a copy of the License at
10#
11# http://www.apache.org/licenses/LICENSE-2.0
12#
13# Unless required by applicable law or agreed to in writing, software
14# distributed under the License is distributed on an "AS IS" BASIS,
15# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
16# implied. See the License for the specific language governing
17# permissions and limitations under the License.
18
19import BaseHTTPServer
20import SocketServer
21import json
22import dbus
23from OpenBMCMapper import Path
24import OpenBMCMapper
25
26class RestException(Exception):
27 def __init__(self, msg, http_status=403):
28 self.status = http_status
29 super(RestException, self).__init__(msg)
30
31class Response(object):
32 def render(self, handler):
33 raise NotImplemented()
34
35class ErrorResponse(Response):
36 def __init__(self, ex):
37 self.ex = ex
38
39 def render(self, handler):
40 err = {'status': 'error', 'error': self.ex.message,}
41 handler.send_response(self.ex.status)
42 handler.send_header('Content-Type', 'application/json')
43 handler.end_headers()
44 handler.wfile.write(json.dumps(err, indent=2, sort_keys=True))
45
46class JSONResponse(Response):
47 def __init__(self, data):
48 self.data = data
49
50 def render(self, handler):
51 handler.send_response(200)
52 handler.send_header('Content-Type', 'application/json')
53 handler.end_headers()
54 handler.wfile.write(json.dumps(self.data, indent=2, sort_keys=True))
55
56class RequestHandler(object):
57 def __init__(self, req, path, data):
58 self.req = req
59 self.path = path
60 self.bus = req.server.bus
61 self.mapper = req.server.mapper
62 self.data = data
63
64 def do_command(self):
65 f = getattr(self, 'do_' + self.req.command)
66 return f()
67
68 def do_GET(self):
69 raise RestException("Not Implemented", 501)
70
71 def do_PUT(self):
72 raise RestException("Not Implemented", 501)
73
74 def do_POST(self):
75 raise RestException("Not Implemented", 501)
76
77 def do_PATCH(self):
78 raise RestException("Not Implemented", 501)
79
80 def do_DELETE(self):
81 raise RestException("Not Implemented", 501)
82
83class MethodHandler(RequestHandler):
84 def __init__(self, req, path, data):
85 super(MethodHandler, self).__init__(req, path, data)
86 self.method = Path(self.req.path).rel(first = -1)
87
88 def find_method_in_interface(self, obj, interface):
89 try:
90 iface = dbus.Interface(obj, interface)
91 return getattr(iface, self.method)
92 except dbus.DBusException:
93 return None
94
95 def find_method_on_bus(self, bus, interfaces):
96 obj = self.bus.get_object(bus, self.path)
97 for i in interfaces:
98 m = self.find_method_in_interface(obj, i)
99 if not m:
100 continue
101 return m
102
103 def find_method(self):
104 busses = self.mapper.GetTree(
105 self.path, 0, 'exact')[self.path]
106 for items in busses.iteritems():
107 m = self.find_method_on_bus(*items)
108 if not m:
109 continue
110
111 return m
112
113 def do_POST(self):
114 try:
115 method = self.find_method()
116 except:
117 raise RestException("Not Found", 404)
118 try:
119 d = { 'result': method(*self.data),
120 'status': 'OK'}
121 except Exception, e:
122 d = { 'error': str(e),
123 'status': 'error'}
124 return d
125
126class InstanceHandler(RequestHandler):
127 def __init__(self, req, path, data, busses):
128 super(InstanceHandler, self).__init__(req, path, data)
129 self.busses = busses
130
131 def get_one_iface(self, properties_iface, iface):
132 try:
133 return properties_iface.GetAll(iface)
134 except:
135 # interface doesn't have any properties
136 return {}
137
138 def get_one_bus(self, bus, interfaces):
139 properties = {}
140 obj = self.bus.get_object(bus, self.path)
141 properties_iface = dbus.Interface(
142 obj, dbus_interface=dbus.PROPERTIES_IFACE)
143 for i in interfaces:
144 properties.update(self.get_one_iface(properties_iface, i))
145
146 return properties
147
148 def do_GET(self):
149 properties = {}
150 for item in self.busses.iteritems():
151 properties.update(self.get_one_bus(*item))
152
153 return properties
154
155 def try_set_one_interface(self, prop, value, properties_iface, interface):
156 try:
157 properties_iface.Set(interface, prop, value)
158 return True
159 except:
160 # property doesn't live on this interface/bus
161 return False
162
163 def try_set_one_bus(self, prop, value, bus, interfaces):
164 obj = self.bus.get_object(bus, self.path)
165 properties_iface = dbus.Interface(
166 obj, dbus_interface=dbus.PROPERTIES_IFACE)
167
168 for iface in interfaces:
169 if self.try_set_one_interface(prop, value,
170 properties_iface, iface):
171 return True
172
173 return False
174
175 def set_one_property(self, prop, value):
176 for item in self.busses.iteritems():
177 if not self.try_set_one_bus(prop, value, *item):
178 raise RestException("Not Found", 404)
179
180 def validate_json(self):
181 if type(self.data) != dict:
182 raise RestException("Bad Request", 400)
183
184 obj = self.do_GET()
185 if len(self.data) != len(obj):
186 raise RestException("Bad Request", 400)
187 for x in obj.iterkeys():
188 if x not in self.data:
189 raise RestException("Bad Request", 400)
190
191 def do_PUT(self):
192 try:
193 self.validate_json()
194 for p in self.data.iteritems():
195 self.set_one_property(*p)
196
197 d = { 'status': 'OK'}
198 except Exception, e:
199 d = { 'error': str(e),
200 'status': 'error'}
201 return d
202
203 def do_POST(self):
204 for p in self.data.iteritems():
205 self.set_one_property(*p)
206
207class AttrHandler(RequestHandler):
208 def __init__(self, req, path, data):
209 super(AttrHandler, self).__init__(req, path, data)
210 try:
211 self.inst = InstanceHandler(req, path, data,
212 self.mapper.GetTree(path, 0, 'exact')[path])
213 except KeyError:
214 raise RestException("Not Found", 404)
215 self.attr = Path(self.req.path).rel(first = -1)
216
217 def do_GET(self):
218 obj = self.inst.do_GET()
219 try:
220 return obj[self.attr]
221 except KeyError:
222 raise RestException("Not Found", 404)
223
224 def do_PUT(self):
225 self.inst.set_one_property(self.attr, self.data)
226
227class TypesHandler(RequestHandler):
228 def __init__(self, req, path, data):
229 super(TypesHandler, self).__init__(req, path, data)
230
231 def do_GET(self):
232 types = self.mapper.GetTreePaths(self.path, 1, 'exact')
233 if not types:
234 raise RestException("Not Found", 404)
235
236 return types
237
238class ListHandler(RequestHandler):
239 def __init__(self, req, path, data):
240 super(ListHandler, self).__init__(req, path, data)
241
242 def do_GET(self):
243 objs = self.mapper.GetTree(self.path, -1, 'fuzzy')
244 if self.path in objs:
245 del objs[self.path]
246 if not objs:
247 raise RestException("Not Found", 404)
248
249 return objs.keys()
250
251class EnumerateHandler(RequestHandler):
252 def __init__(self, req, path, data):
253 super(EnumerateHandler, self).__init__(req, path, data)
254
255 def do_GET(self):
256 objs = {}
257 mapper_data = self.mapper.GetTree(self.path, -1, 'fuzzy')
258 if self.path in mapper_data:
259 del mapper_data[self.path]
260
261 for x,y in mapper_data.iteritems():
262 objs[x] = InstanceHandler(self.req, x, self.data, y).do_GET()
263
264 if not objs:
265 raise RestException("Not Found", 404)
266
267 return objs
268
269class DBusRestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
270 def get_real_handler(self, data):
271 path = Path(self.path)
272
273 if self.path[-1] == '/':
274 return TypesHandler(self, path.fq(), data)
275
276 if path.parts[-1] == 'list':
277 return ListHandler(self, path.fq(last = -1), data)
278
279 if path.parts[-1] == 'enumerate':
280 return EnumerateHandler(self, path.fq(last = -1), data)
281
282 if path.parts[-2] == 'attr':
283 return AttrHandler(self, path.fq(last = -2), data)
284
285 if path.parts[-2] == 'action':
286 return MethodHandler(self, path.fq(last = -2), data)
287
288 # have to do an objectmapper query at this point
289 mapper_entry = self.server.mapper.GetTree(
290 path.fq(), 0, 'exact')
291 if mapper_entry:
292 return InstanceHandler(self, path.fq(), data,
293 mapper_entry[path.fq()])
294
295 raise RestException("Not Found", 404)
296
297 def do_command(self):
298 data = None
299 try:
300 if self.command in ['POST', 'PUT', 'PATCH']:
301 length = int(self.headers.getheader(
302 'content-length'))
303 data = json.loads(self.rfile.read(length))
304
305 resp = self.get_real_handler(data).do_command()
306 if not resp:
307 resp = {'status': 'OK' }
308 response = JSONResponse(resp)
309 except RestException, ex:
310 response = ErrorResponse(ex)
311
312 response.render(self)
313 self.wfile.close()
314
315 def do_GET(self):
316 return self.do_command()
317
318 def do_POST(self):
319 return self.do_command()
320
321 def do_PATCH(self):
322 return self.do_command()
323
324 def do_PUT(self):
325 return self.do_command()
326
327 def do_DELETE(self):
328 return self.do_command()
329
330class HTTPServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
331 def __init__(self, bind, handler):
332 BaseHTTPServer.HTTPServer.__init__(self, bind, handler)
333 self.bus = dbus.SystemBus()
334 mapper = self.bus.get_object(OpenBMCMapper.MAPPER_NAME,
335 OpenBMCMapper.MAPPER_PATH)
336 self.mapper = dbus.Interface(mapper,
337 dbus_interface = OpenBMCMapper.MAPPER_IFACE)
338
339if __name__ == '__main__':
340 bus = dbus.SystemBus()
341 server = HTTPServer(('', 80), DBusRestHandler)
342 server.serve_forever()