blob: 026eb7426c106b55ada600e3ef2be169fb392713 [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
Brad Bishop936f5fe2015-11-03 15:10:11 -050023from OpenBMCMapper import Path, Mapper, PathTree
Brad Bishopaa65f6e2015-10-27 16:28:51 -040024import OpenBMCMapper
25
26class RestException(Exception):
27 def __init__(self, msg, http_status=403):
28 self.status = http_status
29 super(RestException, self).__init__(msg)
Brad Bishop9ee57c42015-11-03 14:59:29 -050030
Brad Bishopaa65f6e2015-10-27 16:28:51 -040031class 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):
Brad Bishop9ee57c42015-11-03 14:59:29 -050058 self.req = req
Brad Bishopaa65f6e2015-10-27 16:28:51 -040059 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):
Brad Bishopade8a8b2015-11-03 15:05:15 -0500104 busses = self.mapper.get_object(
105 self.path)
Brad Bishopaa65f6e2015-10-27 16:28:51 -0400106 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:
Brad Bishop9ee57c42015-11-03 14:59:29 -0500169 if self.try_set_one_interface(prop, value,
Brad Bishopaa65f6e2015-10-27 16:28:51 -0400170 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,
Brad Bishopade8a8b2015-11-03 15:05:15 -0500212 self.mapper.get_object(path))
Brad Bishopaa65f6e2015-10-27 16:28:51 -0400213 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):
Brad Bishopade8a8b2015-11-03 15:05:15 -0500232 types = self.mapper.get_subtree_paths(self.path, 1)
Brad Bishopaa65f6e2015-10-27 16:28:51 -0400233 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):
Brad Bishopade8a8b2015-11-03 15:05:15 -0500243 objs = self.mapper.get_subtree(self.path)
Brad Bishopaa65f6e2015-10-27 16:28:51 -0400244 if not objs:
245 raise RestException("Not Found", 404)
246
Brad Bishop9ee57c42015-11-03 14:59:29 -0500247 return objs.keys()
Brad Bishopaa65f6e2015-10-27 16:28:51 -0400248
249class EnumerateHandler(RequestHandler):
250 def __init__(self, req, path, data):
251 super(EnumerateHandler, self).__init__(req, path, data)
252
Brad Bishop936f5fe2015-11-03 15:10:11 -0500253 def get_enumerate(self, path, data):
254 busses = []
255 for s, i in data.iteritems():
256 if OpenBMCMapper.ENUMERATE_IFACE in i:
257 busses.append(s)
258 return busses
259
260 def call_enumerate(self, path, busses):
261 objs = {}
262 for b in busses:
263 obj = self.bus.get_object(b, path)
264 iface = dbus.Interface(obj, OpenBMCMapper.ENUMERATE_IFACE)
265 objs.update(iface.enumerate())
266 return objs
267
Brad Bishopaa65f6e2015-10-27 16:28:51 -0400268 def do_GET(self):
269 objs = {}
Brad Bishopade8a8b2015-11-03 15:05:15 -0500270 mapper_data = self.mapper.get_subtree(self.path)
Brad Bishop936f5fe2015-11-03 15:10:11 -0500271 tree = PathTree()
Brad Bishopaa65f6e2015-10-27 16:28:51 -0400272 for x,y in mapper_data.iteritems():
Brad Bishop936f5fe2015-11-03 15:10:11 -0500273 tree[x] = y
274
275 try:
276 # Check to see if the root path implements
277 # enumerate in addition to any sub tree
278 # objects.
279 root = self.mapper.get_object(self.path)
280 mapper_data[self.path] = root
281 except:
282 pass
283
284 have_enumerate = [ (x[0], self.get_enumerate(*x)) for x in mapper_data.iteritems() \
285 if self.get_enumerate(*x) ]
286
287 for x,y in have_enumerate:
288 objs.update(self.call_enumerate(x, y))
289 tmp = tree[x]
290 del tree[x]
291 tree[x] = tmp
292
293 for x,y in tree.dataitems():
Brad Bishopaa65f6e2015-10-27 16:28:51 -0400294 objs[x] = InstanceHandler(self.req, x, self.data, y).do_GET()
295
296 if not objs:
297 raise RestException("Not Found", 404)
298
299 return objs
300
301class DBusRestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
302 def get_real_handler(self, data):
303 path = Path(self.path)
304
305 if self.path[-1] == '/':
306 return TypesHandler(self, path.fq(), data)
307
308 if path.parts[-1] == 'list':
309 return ListHandler(self, path.fq(last = -1), data)
310
311 if path.parts[-1] == 'enumerate':
312 return EnumerateHandler(self, path.fq(last = -1), data)
313
Brad Bishop12452ed2015-11-03 15:06:44 -0500314 if path.depth() > 1 and path.parts[-2] == 'attr':
Brad Bishopaa65f6e2015-10-27 16:28:51 -0400315 return AttrHandler(self, path.fq(last = -2), data)
316
Brad Bishop12452ed2015-11-03 15:06:44 -0500317 if path.depth() > 1 and path.parts[-2] == 'action':
Brad Bishopaa65f6e2015-10-27 16:28:51 -0400318 return MethodHandler(self, path.fq(last = -2), data)
319
320 # have to do an objectmapper query at this point
Brad Bishopade8a8b2015-11-03 15:05:15 -0500321 mapper_entry = self.server.mapper.get_object(path.fq())
Brad Bishopaa65f6e2015-10-27 16:28:51 -0400322 if mapper_entry:
Brad Bishop9ee57c42015-11-03 14:59:29 -0500323 return InstanceHandler(self, path.fq(), data,
Brad Bishopade8a8b2015-11-03 15:05:15 -0500324 mapper_entry)
Brad Bishopaa65f6e2015-10-27 16:28:51 -0400325
326 raise RestException("Not Found", 404)
327
328 def do_command(self):
329 data = None
330 try:
331 if self.command in ['POST', 'PUT', 'PATCH']:
332 length = int(self.headers.getheader(
333 'content-length'))
334 data = json.loads(self.rfile.read(length))
335
336 resp = self.get_real_handler(data).do_command()
337 if not resp:
338 resp = {'status': 'OK' }
339 response = JSONResponse(resp)
340 except RestException, ex:
341 response = ErrorResponse(ex)
342
343 response.render(self)
344 self.wfile.close()
345
346 def do_GET(self):
347 return self.do_command()
348
349 def do_POST(self):
350 return self.do_command()
351
352 def do_PATCH(self):
353 return self.do_command()
354
355 def do_PUT(self):
356 return self.do_command()
357
358 def do_DELETE(self):
359 return self.do_command()
360
361class HTTPServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
Brad Bishop53fd4932015-10-30 09:22:30 -0400362 def __init__(self, bind, handler, bus):
Brad Bishopaa65f6e2015-10-27 16:28:51 -0400363 BaseHTTPServer.HTTPServer.__init__(self, bind, handler)
Brad Bishop53fd4932015-10-30 09:22:30 -0400364 self.bus = bus
Brad Bishopade8a8b2015-11-03 15:05:15 -0500365 self.mapper = Mapper(self.bus)
Brad Bishopaa65f6e2015-10-27 16:28:51 -0400366
367if __name__ == '__main__':
368 bus = dbus.SystemBus()
Brad Bishop53fd4932015-10-30 09:22:30 -0400369 server = HTTPServer(('', 80), DBusRestHandler, bus)
Brad Bishopaa65f6e2015-10-27 16:28:51 -0400370 server.serve_forever()