Package occi :: Module handlers
[hide private]
[frames] | no frames]

Source Code for Module occi.handlers

  1  # coding=utf-8 
  2  # 
  3  # Copyright (C) 2010-2012 Platform Computing 
  4  # 
  5  # This library is free software; you can redistribute it and/or 
  6  # modify it under the terms of the GNU Lesser General Public 
  7  # License as published by the Free Software Foundation; either 
  8  # version 2.1 of the License, or (at your option) any later version. 
  9  # 
 10  # This library is distributed in the hope that it will be useful, 
 11  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 12  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
 13  # Lesser General Public License for more details. 
 14  # 
 15  # You should have received a copy of the GNU Lesser General Public 
 16  # License along with this library; if not, write to the Free Software 
 17  # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA 
 18  # 
 19  ''' 
 20  The web handling part of the OCCI service. 
 21   
 22  Created on Jun 27, 2011 
 23   
 24  @author: tmetsch 
 25  ''' 
 26   
 27  from occi import workflow 
 28  from occi.exceptions import HTTPError 
 29   
 30  #============================================================================== 
 31  # Set of HTTP Header field names - naming is defined by WSGI 
 32  #============================================================================== 
 33   
 34  CONTENT_TYPE = 'Content-Type' 
 35  ACCEPT = 'Accept' 
 36  LINK = 'Link' 
 37  LOCATION = 'X-OCCI-Location' 
 38  ATTRIBUTE = 'X-OCCI-Attribute' 
 39  CATEGORY = 'Category' 
 40  QUERY_STRING = 'Query_String' 
 41   
 42   
43 -class BaseHandler(object):
44 ''' 45 General request handler. 46 ''' 47 48 # disabling 'Too many arguments' pylint check (only inst. within module) 49 # pylint: disable=R0913 50
51 - def __init__(self, registry, headers, body, query, extras=None):
52 self.registry = registry 53 self.headers = headers 54 self.body = body 55 self.query = query 56 57 self.extras = extras
58
59 - def handle(self, method, key):
60 ''' 61 Call a HTTP method function on this handler. E.g. when method is HTTP 62 GET the function get(key) will be called. If the function is not 63 defined a 405 is returned. 64 65 method -- The HTTP method name. 66 key -- The key of the resource. 67 ''' 68 try: 69 return getattr(self, str.lower(method))(key) 70 except AttributeError: 71 return 405, {'Content-type': 'text/plain'}, 'Method not supported.'
72
73 - def get_renderer(self, content_type):
74 ''' 75 Returns the proper rendering parser. 76 77 content_type -- String with either either Content-Type or Accept. 78 ''' 79 try: 80 return self.registry.get_renderer(self.headers[content_type]) 81 except KeyError: 82 # In case no Accept is defined in the request 83 return self.registry.get_renderer(self.registry.get_default_type())
84
85 - def response(self, status, headers=None, body='OK'):
86 ''' 87 Will try to figure out what rendering the client wants and return back 88 the given status, header and boy. 89 90 status -- The status code. 91 headers -- The HTTP headers (default: empty). 92 body -- The text for the body (default: 'ok'). 93 ''' 94 rendering = self.get_renderer(ACCEPT) 95 96 if headers is None: 97 headers = {} 98 99 headers['Content-Type'] = rendering.mime_type 100 101 return status, headers, body
102
103 - def parse_action(self):
104 ''' 105 Retrieves the Action which was given in the request. 106 ''' 107 rendering = self.get_renderer(CONTENT_TYPE) 108 109 action, attr = rendering.to_action(self.headers, self.body, 110 self.extras) 111 112 return action, attr
113
114 - def parse_filter(self):
115 ''' 116 Retrieve any attributes or categories which where provided in the 117 request for filtering. 118 ''' 119 attr = ATTRIBUTE 120 cat = CATEGORY 121 if attr not in self.headers: 122 # stupid pep8 - have to break in two lines :-/ 123 if cat not in self.headers and self.body == '': 124 return [], {} 125 126 rendering = self.get_renderer(CONTENT_TYPE) 127 128 categories, attributes = rendering.get_filters(self.headers, self.body, 129 self.extras) 130 131 return categories, attributes
132
133 - def parse_entity(self, def_kind=None):
134 ''' 135 Retrieves the entity which was rendered within the request. 136 137 def_kind -- Indicates if the request can be incomplete (False). 138 ''' 139 rendering = self.get_renderer(CONTENT_TYPE) 140 141 entity = rendering.to_entity(self.headers, self.body, def_kind, 142 self.extras) 143 return entity
144
145 - def parse_entities(self):
146 ''' 147 Retrieves a set of entities which was rendered within the request. 148 ''' 149 rendering = self.get_renderer(CONTENT_TYPE) 150 151 entities = rendering.to_entities(self.headers, self.body, self.extras) 152 153 return entities
154
155 - def parse_mixins(self):
156 ''' 157 Retrieves a mixin from a request. 158 ''' 159 rendering = self.get_renderer(CONTENT_TYPE) 160 161 mixin = rendering.to_mixins(self.headers, self.body, self.extras) 162 163 return mixin
164
165 - def render_entity(self, entity):
166 ''' 167 Renders a single entity to the client. 168 169 entity -- The entity which should be rendered. 170 ''' 171 rendering = self.get_renderer(ACCEPT) 172 173 headers, body = rendering.from_entity(entity) 174 175 return 200, headers, body
176
177 - def render_entities(self, entities, key):
178 ''' 179 Renders a list of entities to the client. 180 181 entities -- The entities which should be rendered. 182 ''' 183 rendering = self.get_renderer(ACCEPT) 184 185 headers, body = rendering.from_entities(entities, key) 186 187 return 200, headers, body
188
189 - def render_categories(self, categories):
190 ''' 191 Renders a list of categories to the client. 192 193 categories -- The categories which should be rendered. 194 ''' 195 rendering = self.get_renderer(ACCEPT) 196 197 headers, body = rendering.from_categories(categories) 198 199 return 200, headers, body
200 201
202 -class ResourceHandler(BaseHandler):
203 ''' 204 Handles the request on single resource instances. 205 ''' 206
207 - def get(self, key):
208 ''' 209 Do a HTTP GET on a resource. 210 211 key -- The resource id. 212 ''' 213 try: 214 entity = self.registry.get_resource(key, self.extras) 215 216 workflow.retrieve_entity(entity, self.registry, self.extras) 217 218 return self.render_entity(entity) 219 except KeyError as key_error: 220 raise HTTPError(404, 'Resource not found: ' + str(key_error))
221
222 - def post(self, key):
223 ''' 224 Do a HTTP POST on a resource. 225 226 key -- The resource id. 227 ''' 228 if self.query is not (): 229 # action 230 try: 231 entity = self.registry.get_resource(key, self.extras) 232 action, attr = self.parse_action() 233 234 workflow.action_entity(entity, action, self.registry, attr, 235 self.extras) 236 237 return self.render_entity(entity) 238 except AttributeError as attr: 239 raise HTTPError(400, str(attr)) 240 except KeyError as key_error: 241 raise HTTPError(404, str(key_error)) 242 else: 243 # update 244 try: 245 old = self.registry.get_resource(key, self.extras) 246 new = self.parse_entity(def_kind=old.kind) 247 248 workflow.update_entity(old, new, self.registry, self.extras) 249 250 return self.render_entity(old) 251 except AttributeError as attr: 252 raise HTTPError(400, str(attr)) 253 except KeyError as key_error: 254 raise HTTPError(404, str(key_error))
255
256 - def put(self, key):
257 ''' 258 Do a HTTP PUT on a resource. 259 260 key -- The resource id. 261 ''' 262 if key in self.registry.get_resource_keys(self.extras): 263 # replace... 264 try: 265 old = self.registry.get_resource(key, self.extras) 266 new = self.parse_entity() 267 268 workflow.replace_entity(old, new, self.registry, self.extras) 269 270 return self.render_entity(old) 271 except AttributeError as attr: 272 raise HTTPError(400, str(attr)) 273 else: 274 # create... 275 try: 276 entity = self.parse_entity() 277 278 workflow.create_entity(key, entity, self.registry, self.extras) 279 280 heads = {'Location': self.registry.get_hostname() 281 + entity.identifier} 282 return self.response(201, heads) 283 except AttributeError as attr: 284 raise HTTPError(400, str(attr))
285
286 - def delete(self, key):
287 ''' 288 Do a HTTP DELETE on a resource. 289 290 key -- The resource id. 291 ''' 292 # delete 293 try: 294 entity = self.registry.get_resource(key, self.extras) 295 296 workflow.delete_entity(entity, self.registry, self.extras) 297 298 return self.response(200) 299 except AttributeError as attr: 300 raise HTTPError(400, str(attr)) 301 except KeyError as key_error: 302 raise HTTPError(404, str(key_error))
303 304
305 -class CollectionHandler(BaseHandler):
306 ''' 307 Handles all operations on collections. 308 ''' 309
310 - def get(self, key):
311 ''' 312 Do a HTTP GET on a collection. 313 314 key -- The resource id. 315 ''' 316 # retrieve (filter) 317 try: 318 categories, attributes = self.parse_filter() 319 entities = workflow.get_entities_under_path(key, self.registry, 320 self.extras) 321 result = workflow.filter_entities(entities, categories, attributes) 322 323 return self.render_entities(result, key) 324 except AttributeError as attr: 325 raise HTTPError(400, str(attr))
326
327 - def post(self, key):
328 ''' 329 Do a HTTP POST on a collection. 330 331 key -- The resource id. 332 ''' 333 if self.query is not (): 334 # action 335 try: 336 action, attr = self.parse_action() 337 entities = workflow.get_entities_under_path(key, self.registry, 338 self.extras) 339 for entity in entities: 340 workflow.action_entity(entity, action, self.registry, attr, 341 self.extras) 342 343 return self.response(200) 344 except AttributeError as attr: 345 raise HTTPError(400, str(attr)) 346 elif not len(self.parse_entities()): 347 # create resource (&links) 348 try: 349 entity = self.parse_entity() 350 workflow.create_entity(workflow.create_id(entity.kind), 351 entity, self.registry, self.extras) 352 353 heads = {'Location': self.registry.get_hostname() 354 + entity.identifier} 355 return self.response(201, heads) 356 except AttributeError as attr: 357 raise HTTPError(400, str(attr)) 358 elif len(self.parse_entities()) > 0: 359 # update 360 try: 361 mixin = self.registry.get_category(key, self.extras) 362 new_entities = self.parse_entities() 363 old_entities = workflow.get_entities_under_path(key, 364 self.registry, 365 self.extras) 366 workflow.update_collection(mixin, old_entities, 367 new_entities, self.registry, 368 self.extras) 369 370 return self.response(200) 371 except AttributeError as attr: 372 raise HTTPError(400, str(attr))
373
374 - def put(self, key):
375 ''' 376 Do a HTTP PUT on a collection. 377 378 key -- The resource id. 379 ''' 380 # replace 381 try: 382 mixin = self.registry.get_category(key, self.extras) 383 new_entities = self.parse_entities() 384 old_entities = workflow.get_entities_under_path(key, self.registry, 385 self.extras) 386 workflow.replace_collection(mixin, old_entities, new_entities, 387 self.registry, self.extras) 388 389 return self.response(200) 390 except AttributeError as attr: 391 raise HTTPError(400, str(attr))
392
393 - def delete(self, key):
394 ''' 395 Do a HTTP DELETE on a collection. 396 397 key -- The resource id. 398 ''' 399 if not len(self.parse_entities()): 400 # delete entities 401 entities = workflow.get_entities_under_path(key, self.registry, 402 self.extras) 403 for entity in entities: 404 workflow.delete_entity(entity, self.registry, self.extras) 405 406 return self.response(200) 407 elif len(self.parse_entities()) > 0: 408 # remove from collection 409 try: 410 mixin = self.registry.get_category(key, self.extras) 411 entities = self.parse_entities() 412 workflow.delete_from_collection(mixin, entities, self.registry, 413 self.extras) 414 415 return self.response(200) 416 except AttributeError as attr: 417 raise HTTPError(400, str(attr))
418 419
420 -class QueryHandler(BaseHandler):
421 ''' 422 Handles the Query interface. 423 ''' 424 425 # disabling 'Unused attr' pylint check (not needed here) 426 # disabling 'Unused argument' pylint check (only here to have one sig) 427 # pylint: disable=W0612,W0613 428
429 - def get(self, key=None):
430 ''' 431 Do a HTTP GET on the query interface. 432 433 key -- The resource id. 434 ''' 435 # retrieve (filter) 436 try: 437 categories, attributes = self.parse_filter() 438 439 result = workflow.filter_categories(categories, self.registry, 440 self.extras) 441 442 return self.render_categories(result) 443 except AttributeError as attr: 444 raise HTTPError(400, str(attr))
445
446 - def post(self, key=None):
447 ''' 448 Do a HTTP POST on the query interface. 449 450 key -- The resource id. 451 ''' 452 # add user-defined mixin 453 try: 454 mixins = self.parse_mixins() 455 456 workflow.append_mixins(mixins, self.registry, self.extras) 457 458 return self.render_categories(mixins) 459 except AttributeError as attr: 460 raise HTTPError(400, str(attr))
461
462 - def delete(self, key=None):
463 ''' 464 Do a HTTP DELETE on the query interface. 465 466 key -- The resource id. 467 ''' 468 # delete user defined mixin 469 try: 470 categories, attributes = self.parse_filter() 471 472 workflow.remove_mixins(categories, self.registry, self.extras) 473 474 return self.response(200) 475 except AttributeError as attr: 476 raise HTTPError(400, str(attr))
477