This package provides an OCCI compatible front-end which can easily be integrated with any Resource Management Framework (e.g. a Hypervisor - but not strictly bound to that).
The package here provides an OCCI compatible service and takes care of:
- A WSGI application which can run on most web frameworks for Python,
- Easy to extend OCCI interface - you can add your own OCCI extension, hook it up to different Resource Management Frameworks or even define own renderings,
- An OCCI compatible interface (Support text/occi, text/plain and text/uri-list),
- A dynamically updated capabilities query interface,
- Support for collections, mixins, templating and actions as defined by OCCI,
- Renderings which are defined by OCCI,
- A simple HTML interface for simple browsing using any Web browser.
Creating a OCCI compatible service is simple. Write a Backend and associate a Kind, Mixin or Action with it. The Backend is the glue between the OCCI front-end and you Resource Management Framework. Every OCCI type definition needs and corresponding backend which will handle the operations.
This can either mean to implement one backend which can handle several OCCI types; or to implement a backend for each OCCI type:
from occi.backend import ActionBackend, KindBackend, MixinBackend
from occi.core_model import Action, Kind, Mixin, Resource
from occi.wsgi import Application
from wsgiref.simple_server import make_server
class MyBackend(KindBackend, ActionBackend):
'''
TODO: This needs to be implemented!
Will implement create, retrieve, update, delete, replace
Will also implement: action (from ActionBackend).
'''
pass
class MyMixinBackend(MixinBackend):
'''
TODO: This needs to be implemented!
Will implement create, retrieve, update, delete, replace
'''
pass
if __name__ == '__main__':
mykind = Kind('http://example.com/occi#',
'mykind',
title='A OCCI kind...',
attributes={'occi.example.attr': '',
'occi.compute.state': 'immutable',
'occi.compute.mandatory': 'required'},
related=[Resource.kind],
actions=[])
mymixin = Mixin('http://example.com/occi#', 'mykind', '/my_stuff/')
kind_backend = MyBackend()
mixin_backend = MyMixinBackend()
app = Application()
app.register_backend(mykind, kind_backend)
app.register_backend(mymixin, mixin_backend)
httpd = make_server('', 8888, app)
httpd.serve_forever()
The OCCI service itself is an WSGI application which can run on most Web servers our there. And now that the WSGI application is defined (as app) we only need to run it. This can be done with the wsgiref implementation which comes with the standard Python distribution:
httpd = make_server('', 8888, app)
httpd.serve_forever()
Or if you would like to use other frameworks such as Tornado (Or any other which supports the WSGI framework):
container = tornado.wsgi.WSGIContainer(app)
http_server = tornado.httpserver.HTTPServer(container)
http_server.listen(8888)
tornado.ioloop.IOLoop.instance().start()
After running this example the service will be up and running on port 8888. You can visit it by going to localhost:8888.
For a more detailed example see the file run_iaas_wsgi_service.py in the misc folder.
(Optional) The implementation of a registry is optional. The registry is a container for the current resources, categories and renderings which are available within the service.
By default the service will use a non persistent registry which uses dictionaries. This might be sub-optimal since their are many lookups needed. The use of a simple Database (like Python’s build-in sqlite) might be a faster and better solution for large deployments (Or hook it up to an existing DB of your application)
Also by implementing a registry it is possible to do security checks on ownership of resource, categories as well as backends. Also if the the core.id should be set to a specific value this can be realized in the registry.
To defined an own registry implement the routines from the abstract class Registry. When instantiating the service provide the registry:
app = Application(registry=MyRegistry())
(Optional) You can add your own set of Renderings to your Service if you like. For example to add support for OVF or JSON feel free to add those rendering on your own:
my_registry = NonePersistentRegistry()
renderings = {'text/json': JsonRendering(registry)}
app = Application(renderings=renderings)
Please note that your rendering implementation should derive from the Rendering class. You do not necessarily need to implement all functions defined in that class but you are encouraged to do so. For more details review the OCCI and HTML rendering which come along within this package.
Note
The HTMLRendering can be customized with an own CSS to adapt your look and feel when using Web browsers. Simply provide a CSS as a string when calling the HTMLRenderings’s constructor.
Note
Please also check the API documentation.
In general backends handle types of resources - whenever a resource (of the type which the backend is registered for) gets modified, it is called with the resource as parameter.
Note
Backends always have the last word - if a resource cannot be created, retrieved, updated, replace or delete it will not be done by the service. Simply throw an AttributeError. (Same for Action and Mixin Backends)
3 Types of Backends exists (KindBackend, MixinBackend and ActionBackend). Those who are capable of handling resource of type OCCI Kind. Those who handle OCCI Mixins and those who can handle Actions.
Thanks to the multiple inheritance features of python you can create a Backend which handles the resources of a type as well as the actions which can be performed on the resources. To do so simple derive from the KindBackend and ActionBackend class.
A Backend is always presented the current state of the resource. The implementor does not need to modify the attributes ‘links’ and ‘mixins’ of the entity. Those are handled by the service. Only the ‘attributes’ dictionary itself should be modified accordingly. It is recommend to add necessary attributes to this dictionary during create. Also do not forget to set the currently applicable actions in the ‘actions’ list of an entity.
The following listing shows when and which function is called on a KindBackend or MixinBackend:
Routine | Description |
---|---|
create | Called when a resource or link is create. It is recommended to set all necessary attributes (like the state) and applicable actions in this routine. |
retrieve | Called when a resource or link is to be retrieved. Implementors can request up to date information from the Resource Management Framework and assign it to the resource (like updating the state or reviewing the applicable actions). |
update | Called when a resource or link needs to updated. New information is provided in the new entity. Note that the the new entity does not need to be a complete representation of the resource. |
replace | Called when a resource or link get replaced. Also see ‘update’ - but this time the new entity is a complety representation. |
delete | Called when a resource or link get deleted. |
The following routines are called on Backends deriving from Action Backend:
Routine | Description |
---|---|
action | Called when an action is performed on the resource. Implementors might want to check the and update the attributes (like the state) of the resource. |
The OCCI implementation eventually calls all the backends associated to an resource. If a resource has 2 mixins associated to it, the implementation will call the backend which defines the kind, and the backends which define the mixins. So in total 3 calls are made.
To give another example: When a resource is created which has links assigned the ‘create’ routines for the backend defining the kind of the resource is called; the backends for the assigned mixins of the resource are called; as well as backends defining the links kinds get called called. So if a resource has 1 kind, 2 mixins and 2 links –> 5 calls on backends are performed.
To pass information to the backends and the registry it is possible to override the __call__ function in the WSGI OCCI application. This is useful to pass on security information to the backends and registry for example:
def __call__(self, environ, response):
sec_obj = {'username': 'password'}
return self._call_occi(environ, response, security=sec_obj)
Now the sec_obj will be passed within the extras parameter of the functions in the backends/registry (to retrieve it: extras[security]).
Now it is possible to do security assertions in the registry (check ownership of an category or resource listing) or backend (check ownership of resources). This will allow that two users have the same category (term, scheme and location are equal) but that different resources are returned due to the ownership. Thus this creates Service provider client specific namespaces.
Next to this within the OCCI core model Entity (and therefore also Resource and Link) and Category have an attribute called extras which will not be rendered, but can be used to store any kind of object (such as ownership information).
Note
The OCCI Infrastructure extension is also implemented and ready to be used. Simply write the backends for the Kinds, Mixins and Actions defined in the module occi.extension.infrastructure.
Note
A Backend must handle all the kinds it defines. That means that if it defines a compute kind and a network resource link kind all the operations should have an ‘if statement’ to check which type of entity is provided to the routine.
Note
There are some routines for your convenience which will help you implement backends. They are located in the backend module. Please refer to the API documentation for more details.
Note
If a backend handles mixins it should verify that mixins can be applied to the provided resources.
Note
It is recommended to throw either AttributeError or HTTPErrors on any exceptions in the Backends. HTTPErrors can be used if you want to define a return code (defined in the wsgi module) or AttribtueErrors on more generic errors which will result in a Bad Request.
Note
It is possible to influence the behaviour of which backends are called by overwriting the get_all_backends() routine in the registry. By default (in the NonePersitentRegistry) the backends which define the kinds and mixins are called. This could be altered to also call the backends from related kinds for example.
Note
By implementing switch cases in the set_backend() and remove_mixin() routine in the registry will allow you to set backends for user defined Mixins which actually do something.