Custom Search

Saturday, March 29, 2014

How to OpenStack API and WSGI api-paste.ini Work

1)
Method-1
=========

http://docs.repoze.org/moonshining/pep333.html

a)
Open a file named "myapplication.py" and add following code.

##Our Application
def green(environ, start_response):
    """
        Simplest possible application object
    """
    status = '200 OK'
    response_headers = [('Content-Type', 'text/plain')]
    start_response(status, response_headers)
    return ['%s\n' % environ.get('GREETING', 'Hello world!')]

##Middleware-1/Filter-1
##Class as a middleware component
class Caseless:
    def __init__(self, app):
        self.app = app
    def __call__(self, environ, start_response):
        for chunk in self.app(environ, start_response):
            yield chunk.lower()          
          
##Middleware-2/Filter-2
##closure or nested function or decorator as a middleware component
def greeting_setter(app):
    def _curried(environ, start_response):
        environ['GREETING'] = 'Bonjour, le monde!'
        return app(environ, start_response)
    return _curried

##Run your application "green" under the "Paste" webserver.
if __name__ == '__main__':
    from paste import httpserver
    from caseless import Caseless
    httpserver.serve(Caseless(greeting_setter(green)),
                     host='127.0.0.1', port='8080')

b)
#easy_install Paste

c)
##Run the application
#python myapplication.py


2)

Method-2 (OpenStack Using this method for API service)

=================================

http://docs.repoze.org/moonshining/tools/paste.html

a)

Open a file named "configuration.ini" and add following code

[app:green]
paste.app_factory = myapplication:app_factory

[filter:caseless]
paste.filter_app_factory = myapplication:filter_caseless_factory

[filter:greetingsetter]
paste.filter_app_factory = myapplication:filter_greeting_setter_factory

[pipeline:main]
pipeline = caseless greetingsetter green

[server:main]
use = egg:Paste#http
host = 127.0.0.1
port = 8080

b)
Open a file named "myapplication.py" and add following code.

##Our Application
def green(environ, start_response):
    """
        Simplest possible application object
    """
    status = '200 OK'
    response_headers = [('Content-Type', 'text/plain')]
    start_response(status, response_headers)
    return ['%s\n' % environ.get('GREETING', 'Hello world!')]

##Middleware-1/Filter-1
##Class as a middleware component
class Caseless:
    def __init__(self, app):
        self.app = app
    def __call__(self, environ, start_response):
        for chunk in self.app(environ, start_response):
            yield chunk.lower()
          
##Middleware-2/Filter-2
##closure or nested function or decorator as a middleware component
def greeting_setter(app):
    def _curried(environ, start_response):
        environ['GREETING'] = 'Bonjour, le monde!'
        return app(environ, start_response)
    return _curried

##Factory to get middleware "Caseless" warapped application instance
def filter_caseless_factory(app, global_config):
    return Caseless(app)

##Factory to get middleware "greeting_setter" warapped application instance
def filter_greeting_setter_factory(app, global_config):
    return greeting_setter(app)

c)
#easy_install Paste
#easy_install PasteDeploy
#easy_install PasteScript

d)
##Run the application
#paster serve configuration.ini


3)

Example from OpenStack

=======================

a)
heat-api Service:
heat/heat/bin/heat-api
https://github.com/openstack/heat/blob/master/bin/heat-api

from heat.common import config
from heat.common import wsgi

if __name__ == '__main__':
    ##This line will return all middleware/filter wrapped apllication instance based on pipeine in the .ini file.
    app = config.load_paste_app() <=== IMP
    ##Following line run your application using "eventlet green thread"
    ##See https://github.com/openstack/heat/blob/master/heat/common/wsgi.py
    server = wsgi.Server()
    server.start(app, cfg.CONF.heat_api, default_port=port)
    server.wait()

b)
heat/heat/common/config.py
https://github.com/openstack/heat/blob/master/heat/common/config.py

from heat.common import wsgi

def load_paste_app(app_name=None):
    """
        Builds and returns a WSGI app from a paste config file.
        conf_file = /etc/heat/api-paste.ini
        app_name = heat-api
    """
    app = wsgi.paste_deploy_app(conf_file, app_name, cfg.CONF)
    return app

c)
heat/heat/common/wsgi.py
https://github.com/openstack/heat/blob/master/heat/common/wsgi.py

from paste import deploy

def paste_deploy_app(paste_config_file, app_name, conf):
    setup_paste_factories(conf) <===
    try:
        return deploy.loadapp("config:%s" % paste_config_file, name=app_name)
    finally:
        teardown_paste_factories()

def setup_paste_factories(conf):
    global app_factory, filter_factory <===
    app_factory = AppFactory(conf) <===
    filter_factory = FilterFactory(conf) <===

class AppFactory(BasePasteFactory):
    KEY = 'heat.app_factory' <===
    def __call__(self, global_conf, **local_conf):
        """
            The actual paste.app_factory protocol method.
        """
        factory = self._import_factory(local_conf) <===
        ##Example of factory (app factory) : "heat.api.openstack.v1.API"
        return factory(self.conf, **local_conf)

class FilterFactory(AppFactory):
    KEY = 'heat.filter_factory'
    def __call__(self, global_conf, **local_conf):
        """
            The actual paste.filter_factory protocol method.
        """
        factory = self._import_factory(local_conf) <===
        ##Example of factory (filter factory) : "heat.api.openstack.faultwrap_filter"
        def filter(app): <===
            return factory(app, self.conf, **local_conf)
        return filter
      
class BasePasteFactory(object):
        """
            Import an app/filter class.
            Lookup the KEY from the PasteDeploy local conf and import the
            class named there. This class can then be used as an app or
            filter factory.
        """      
    KEY = None

    def __init__(self, conf):
        self.conf = conf

    def __call__(self, global_conf, **local_conf):
        raise NotImplementedError      

    def _import_factory(self, local_conf):
        class_name = local_conf[self.KEY].replace(':', '.').strip()
        ##Example of class_name : "heat.api.openstack.v1.API", "heat.api.openstack.faultwrap_filter"
        return importutils.import_class(class_name)

4)
api-paste.ini
===========

https://github.com/openstack/heat/blob/master/etc/heat/api-paste.ini

a)
[server:main]
--------------------
* In /etc/heat/api-paste.ini you can not find the section [server:main], because OpenStack explicitly using "paste.deploy.loadapp" to get the Middlewares/Filters wrapped WSGI application instance.

* You can check the following lines in the file https://github.com/openstack/heat/blob/master/heat/common/wsgi.py

def paste_deploy_app(paste_config_file, app_name, conf):
    """
        paste_config_file = /etc/heat/api-paste.ini
        app_name = heat-api
    """
    setup_paste_factories(conf)
    try:
        return deploy.loadapp("config:%s" % paste_config_file, name=app_name) <===
    finally:
        teardown_paste_factories()

b)
[pipeline:main]
-----------------------
* In /etc/heat/api-paste.ini you can not find the section [pipeline:main], because we are passinf the name of the pipeline to the method "paste.deploy.loadapp" as "name=app_name". Here value of the variable "app_name" is "heat-api", so that will load the pipeline "[pipeline:heat-api]" defined in the https://github.com/openstack/heat/blob/master/etc/heat/api-paste.ini.

* Also note that, same paste config file https://github.com/openstack/heat/blob/master/etc/heat/api-paste.ini is used for "heat-api", "heat-api-cfn" and "heat-api-cloudwatch". So having one 'main' wouldn't work.

def paste_deploy_app(paste_config_file, app_name, conf):
    """
        paste_config_file = /etc/heat/api-paste.ini
        app_name = heat-api
    """
    setup_paste_factories(conf)
    try:
        return deploy.loadapp("config:%s" % paste_config_file, name=app_name) <===
    finally:
        teardown_paste_factories()



3 comments:

  1. OpenStack APIs and WSGI
    http://www.slideshare.net/lhrc_mikeyp/openstack-apis-and-wsgi

    ReplyDelete
  2. what is api-paste.ini file in openstack
    http://stackoverflow.com/questions/18952315/what-is-api-paste-ini-file-in-openstack

    ReplyDelete
  3. proposed replacing the WSGI framework in Oslo with a combination of Pecan and WSME
    https://etherpad.openstack.org/p/havana-common-wsgi
    https://etherpad.openstack.org/p/grizzly-common-wsgi-frameworks

    PECAN
    http://pecan.readthedocs.org/en/latest/

    WSME
    http://pythonhosted.org/WSME/


    ReplyDelete