1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 '''
20 Actual mime-type renderings.
21
22 Created on Jun 28, 2011
23
24 @author: tmetsch
25 '''
26
27 from occi.core_model import Resource, Link
28 from occi.handlers import CATEGORY, ATTRIBUTE, LOCATION, LINK, CONTENT_TYPE
29 from occi.protocol.rendering import Rendering
30 import occi.protocol.occi_parser as parser
31 import shlex
32
33
35 '''
36 Simple class which functions as an adapter between the OCCI model and the
37 HTTP rendering. Holds all information in the way an entity is rendered.
38 '''
39
40
41
42
44 self.categories = []
45 self.links = []
46 self.attributes = []
47 self.locations = []
48
49
50
51
52
53
55 '''
56 Extract an entity from the HTTP data object.
57
58 data -- The http data object
59 def_kind -- A given kind definition.
60 registry -- The registry.
61 extras -- Passed on extra object.
62 '''
63
64
65
66
67
68 kind = None
69 mixins = []
70
71
72 kind_found = False
73 for category_string in data.categories:
74 category = parser.get_category(category_string.strip(), registry,
75 extras)
76 if repr(category) == 'kind' and not kind_found:
77 kind = category
78 kind_found = True
79 else:
80 mixins.append(category)
81
82
83 attributes = {}
84 for attr_string in data.attributes:
85 key, value = parser.get_attributes(attr_string)
86 attributes[key] = value
87
88
89 if kind_found is False and def_kind is None:
90 raise AttributeError('Could not find a valid kind.')
91 elif def_kind is not None:
92 kind = def_kind
93
94 if Resource.kind in kind.related:
95
96 entity = Resource(None, kind, mixins, [])
97 for link_string in data.links:
98 entity.links.append(parser.get_link(link_string.strip(),
99 entity,
100 registry, extras))
101 elif Link.kind in kind.related:
102 try:
103 source_attr = attributes['occi.core.source']
104 target_attr = attributes['occi.core.target']
105
106 if not source_attr.find(registry.get_hostname()):
107 source_attr = source_attr.replace(registry.get_hostname(), '')
108 if not target_attr.find(registry.get_hostname()):
109 target_attr = target_attr.replace(registry.get_hostname(), '')
110
111 source = registry.get_resource(source_attr, extras)
112
113 target = registry.get_resource(target_attr, extras)
114 except KeyError:
115 raise AttributeError('Both occi.core.[source, target]'
116 + ' attributes need to be resources.')
117 entity = Link(None, kind, mixins, source, target)
118 else:
119 raise AttributeError('This kind seems not to be related to either'
120 + ' resource or link.')
121
122 entity.attributes = attributes
123 return entity
124
125
127 '''
128 Create a HTTP data object from an entity.
129
130 entity -- The entity to render.
131 registry -- Registry.
132 '''
133 data = HTTPData()
134
135
136 cat_str_list = [parser.get_category_str(entity.kind, registry)]
137
138 for category in entity.mixins:
139 cat_str_list.append(parser.get_category_str(category, registry))
140
141 data.categories = cat_str_list
142
143 attributes = []
144 if 'occi.core.id' not in entity.attributes:
145 entity.attributes['occi.core.id'] = entity.identifier
146
147 link_str_list = []
148
149 for action in entity.actions:
150 act = '<' + entity.identifier + '?action=' + action.term + '>'
151 act = act + '; rel="' + str(action) + '"'
152 link_str_list.append(act)
153
154
155 if isinstance(entity, Resource):
156
157 for link in entity.links:
158 link_str_list.append(parser.get_link_str(link))
159
160 elif isinstance(entity, Link):
161 entity.attributes['occi.core.source'] = entity.source.identifier
162
163 entity.attributes['occi.core.target'] = entity.target.identifier
164
165 data.links = link_str_list
166
167
168 for attr in entity.attributes:
169 attributes.append(attr + '="'
170 + entity.attributes[attr] + '"')
171
172 data.attributes = attributes
173
174 return data
175
176
178 '''
179 Extract a set of (in the service existing) entities from a request.
180
181 data -- the HTTP data.
182 registry -- The registry used for this call.
183 extras -- Passed on extra object.
184 '''
185 result = []
186 for item in data.locations:
187 try:
188 if not item.find(registry.get_hostname()):
189 item = item.replace(registry.get_hostname(), '')
190
191 result.append(registry.get_resource(item.strip(), extras))
192 except KeyError:
193 raise AttributeError('Could not find the resource with id: '
194 + str(item))
195
196 return result
197
198
200 '''
201 Return a list of entities using the X-OCCI-Location attribute.
202
203 entity_list -- list of entities.
204 registry -- The registry used for this call.registry
205 '''
206 data = HTTPData()
207 for entity in entity_list:
208 data.locations.append(registry.get_hostname() + entity.identifier)
209
210 return data
211
212
214 '''
215 Create a HTTP data object from a set of categories.
216
217 categories -- list of categories.
218 registry -- needed to retrieve hostname info.
219 '''
220 data = HTTPData()
221
222 for cat in categories:
223 data.categories.append(parser.get_category_str(cat, registry))
224
225 return data
226
227
229 '''
230 Create an action from an HTTP data object.
231
232 data -- the HTTP data.
233 registry -- The registry used for this call.
234 extras -- Passed on extra object.
235 '''
236 action = parser.get_category(data.categories[0].strip(), registry, extras)
237
238 attributes = {}
239 for attr in data.attributes:
240 key, val = parser.get_attributes(attr)
241 attributes[key] = val
242
243 return action, attributes
244
245
247 '''
248 Create a Mixin from an HTTP data object.
249
250 data -- the HTTP data.
251 registry -- The registry used for this call.
252 extras -- Passed on extra object.
253 '''
254 result = []
255 for cat_str in data.categories:
256 result.append(parser.get_category(cat_str, registry, extras,
257 is_mixin=True))
258 return result
259
260
262 '''
263 Parse categories and attributes from the request.
264
265 data -- the HTTP data.
266 registry -- The registry used for this call.
267 extras -- Passed on extra object.
268 '''
269 categories = []
270 attributes = {}
271
272 for cat in data.categories:
273 categories.append(parser.get_category(cat, registry, extras))
274
275 for attr in data.attributes:
276 key, value = parser.get_attributes(attr)
277 attributes[key] = value
278
279 return categories, attributes
280
281
283 '''
284 Simple method to split out the information from the HTTP headers.
285
286 headers -- The HTTP headers.
287 '''
288
289 data = HTTPData()
290 if CATEGORY in headers.keys():
291 data.categories = headers[CATEGORY].split(',')
292 if ATTRIBUTE in headers.keys():
293 split = shlex.shlex(headers[ATTRIBUTE], posix=True)
294 split.whitespace = ','
295 split.whitespace_split = True
296 data.attributes = list(split)
297 if LOCATION in headers.keys():
298 data.locations = headers[LOCATION].split(',')
299 if LINK in headers.keys():
300 data.links = headers[LINK].split(',')
301 return data
302
303
305 '''
306 Simple method to set all information in the HTTP header.
307
308 data -- The data to set.
309 mime_type -- The content type to set.
310 '''
311 headers = {}
312 body = 'OK'
313
314
315
316
317 if len(data.categories) > 0:
318 headers[CATEGORY] = ', '.join(data.categories)
319 if len(data.links) > 0:
320 headers[LINK] = ', '.join(data.links)
321 if len(data.locations) > 0:
322 headers[LOCATION] = ', '.join(data.locations)
323 if len(data.attributes) > 0:
324 headers[ATTRIBUTE] = ', '.join(data.attributes)
325 headers[CONTENT_TYPE] = mime_type
326
327 return headers, body
328
329
330 -class TextOcciRendering(Rendering):
331 '''
332 This is a rendering which will use the HTTP header to place the information
333 in an syntax and semantics as defined in the OCCI specification.
334 '''
335
336 mime_type = 'text/occi'
337
338
339
340
341
342 - def get_data(self, headers, body):
343 '''
344 Mainly here so TextPlainRendering can reuse.
345
346 headers -- The headers of the request.
347 body -- The body of the request.
348 '''
349 return _extract_data_from_headers(headers)
350
351 - def set_data(self, data):
352 '''
353 Mainly here so TextPlainRendering can reuse.
354
355 data -- An HTTPData object.
356 '''
357 return _set_data_to_headers(data, self.mime_type)
358
359 - def to_entity(self, headers, body, def_kind, extras):
360 data = self.get_data(headers, body)
361 entity = _to_entity(data, def_kind, self.registry, extras)
362 return entity
363
364 - def from_entity(self, entity):
365 data = _from_entity(entity, self.registry)
366 headers, body = self.set_data(data)
367 return headers, body
368
369 - def to_entities(self, headers, body, extras):
370 data = self.get_data(headers, body)
371 entities = _to_entities(data, self.registry, extras)
372 return entities
373
374 - def from_entities(self, entities, key):
375 data = _from_entities(entities, self.registry)
376 headers, body = self.set_data(data)
377 return headers, body
378
379 - def from_categories(self, categories):
380 data = _from_categories(categories, self.registry)
381 headers, body = self.set_data(data)
382 return headers, body
383
384 - def to_action(self, headers, body, extras):
385 data = self.get_data(headers, body)
386 action = _to_action(data, self.registry, extras)
387 return action
388
389 - def to_mixins(self, headers, body, extras):
390 data = self.get_data(headers, body)
391 mixin = _to_mixins(data, self.registry, extras)
392 return mixin
393
394 - def get_filters(self, headers, body, extras):
395 data = self.get_data(headers, body)
396 categories, attributes = _get_filter(data, self.registry, extras)
397 return categories, attributes
398
399
401 '''
402 Simple method to split out the information from the HTTP body.
403
404 body -- The HTTP body.
405 '''
406 data = HTTPData()
407 for entry in body.split('\n'):
408 if entry.find(CATEGORY + ':') > -1:
409 data.categories.extend(_extract_values(entry, CATEGORY + ':'))
410 if entry.find(ATTRIBUTE + ':') > -1:
411 data.attributes.extend(_extract_values(entry, ATTRIBUTE + ':'))
412 if entry.find(LINK + ':') > -1:
413 data.links.extend(_extract_values(entry, LINK + ':'))
414 if entry.find(LOCATION + ':') > -1:
415 data.locations.extend(_extract_values(entry, LOCATION + ':'))
416 return data
417
418
420 '''
421 In HTTP body OCCI renderings can either be in new lines or separated by ,.
422
423 entry -- The text line to look into.
424 key -- The key to look for and strip away.
425 '''
426 items = []
427 tmp = entry[entry.find(key) + len(key) + 1:]
428 if tmp.find(',') == -1:
429 items.append(tmp)
430 else:
431 split = shlex.shlex(tmp, posix=True)
432 split.whitespace = ','
433 split.whitespace_split = True
434 items.extend(list(split))
435 return items
436
437
438 -def _set_data_to_body(data, mime_type):
439 '''
440 Simple method to set all information in the HTTP body.
441
442 data -- The data to set.
443 mime_type -- The content type to set.
444 '''
445 body = ''
446 if len(data.categories) > 0:
447 for cat in data.categories:
448 body += '\n' + CATEGORY + ': ' + cat
449
450 if len(data.links) > 0:
451 for link in data.links:
452 body += '\n' + LINK + ': ' + link
453
454 if len(data.attributes) > 0:
455 for attr in data.attributes:
456 body += '\n' + ATTRIBUTE + ': ' + attr
457
458 if len(data.locations) > 0:
459 for loc in data.locations:
460 body += '\n' + LOCATION + ': ' + loc
461
462 return {CONTENT_TYPE: mime_type}, body
463
464
465 -class TextPlainRendering(TextOcciRendering):
466 '''
467 This is a rendering which will use the HTTP body to place the information
468 in an syntax and semantics as defined in the OCCI specification.
469 '''
470
471 mime_type = 'text/plain'
472
473 - def set_data(self, data):
475
476 - def get_data(self, headers, body):
478
479
480 -class TextUriListRendering(Rendering):
481 '''
482 This is a rendering which can handle URI lists.
483 '''
484
485 mime_type = 'text/uri-list'
486 error = 'Unable to handle this request with the text/uri-list' \
487 ' rendering.'
488
489 - def to_entity(self, headers, body, def_kind, extras):
490 raise AttributeError(self.error)
491
492 - def from_entity(self, entity):
493 raise AttributeError(self.error)
494
495 - def to_entities(self, headers, body, extras):
496 raise AttributeError(self.error)
497
498 - def from_entities(self, entities, key):
499 body = '# uri:' + str(key)
500 for entity in entities:
501 body += '\n' + self.registry.get_hostname() + entity.identifier
502 return {CONTENT_TYPE: self.mime_type}, body
503
504 - def from_categories(self, categories):
505 raise AttributeError(self.error)
506
507 - def to_action(self, headers, body, extras):
508 raise AttributeError(self.error)
509
510 - def to_mixins(self, headers, body, extras):
511 raise AttributeError(self.error)
512
513 - def get_filters(self, headers, body, extras):
514 raise AttributeError(self.error)
515