Wednesday, May 18, 2016

Python Pecan Web Framework Tutorial (2) -- adding hook and oslo-config

pecan2

Introduction

這段代碼我放到我的github裡

https://github.com/jonahwu/lab/tree/master/pecan/pecantest/testproject_config

這篇文章我將會講到兩件事情
* Pecan Hook
* config file的使用

Pecan Hook

Hook as a middleware, one can use Paste middleware and also combine with hook. The example show as followed. Paste middleware can be used as auth or to check http header for securiy that is indepedently with our core appliction, and hook can be used as database access, that deeply glue with our application.

Firstly, modify *setup_app function in app.py setup_app.

def setup_app(pecan_config=None, extra_hooks=None):
    #print 'setup_app pecan_config %s'%pecan_config
    app_hooks = [hooks.ConfigHook(),
                 hooks.SecondHook(),
                ]


    app = pecan.make_app(
        pecan_config.app.root,
        debug=True,
        hooks=app_hooks,
        #force_canonical=getattr(pecan_config.app, 'force_canonical', True),
        #guess_content_type_from_ext=False
    )
    #print 'return setup_app'
    return app

Adding hooks.py in test_project directory.

from pecan import hooks

class ConfigHook(hooks.PecanHook):

    @staticmethod
    def before(state):
        print '---------i am in the hook---------------'


class SecondHook(hooks.PecanHook):

    @staticmethod
    def before(state):
        print '---------i am in the second hook---------------'

    @staticmethod
    def after(state):
        print '---------i am in the second hook in after state---------------'

    @staticmethod
    def on_route(state):
        print '---------i am in the second hook in on-route state---------------'

Run the application

root@ubuntu:~/pecan_test/test_project# python api.py

start to run server
start to build paste server
api paste file /root/pecan_test/test_project/api_paste.ini
into app_factory
to get Query Controller
into the root controller
port 8080
 * Running on http://0.0.0.0:8080/ (Press CTRL+C to quit)

The hook lib contains before, after, on_route function.

The characteritic is where the return query is in the controller.

-----------go away from paste middleware--------
---------i am in the second hook in on-route state---------------
---------i am in the hook---------------
---------i am in the second hook---------------
return query
---------i am in the second hook in after state---------------

That means, before() will be executed before into controller, and after() will be executed after into controller. on-route() will be executed before any hook stated. And the Hook function will be execute after Paste middleware.

This example use hook to all application, not in a specific function, of course it supported. But we don't like this kind of writing skill that will be complicate to trace code.

Configuration File

這部分內容可以參考我之前寫的oslo-config的部分

http://gogosatellite.blogspot.tw/2016/05/openstack-oslo-config-tutorial.html

寫任何application都會遇到config file與logging的問題,我們先解決config file的問題。 相信,如果看懂了範例,要將oslo-config放到pecan裡就不是難事了。 此外,python原生也提供config的功能,但,遠遠不及oslo-config的強大,因此對於config的部分,我還是選OpenStack的oslo-config做為使用的基準。

root@ubuntu:~/pecan_test/test_project# ls -l
total 112
-rw-r--r-- 1 root root  363 May 16 23:56 api.conf
-rw-r--r-- 1 root root  585 May 16 23:30 api_paste.ini
-rwxr-xr-x 1 root root  880 May 16 23:39 api.py
-rw-r--r-- 1 root root 5306 May 15 10:17 app_eventlet.py
-rw-r--r-- 1 root root 4645 May 17 00:00 app.py
-rw-r--r-- 1 root root  932 May 14 18:55 app.wsgi
drwxr-xr-x 4 root root 4096 May 14 14:14 build
-rw-r--r-- 1 root root 1020 May 16 18:42 config.py
drwxr-xr-x 2 root root 4096 May 14 14:14 dist
-rw-r--r-- 1 root root 2981 May 16 14:18 healthcheck.py
-rw-r--r-- 1 root root  550 May 16 14:24 hooks.py
-rw-r--r-- 1 root root    0 May 16 23:37 __init__.py
-rw-r--r-- 1 root root  128 May 14 19:14 __init__.pyc
-rw-r--r-- 1 root root   27 May 14 14:04 MANIFEST.in
drwxr-xr-x 4 root root 4096 May 14 14:04 public
-rw-r--r-- 1 root root   14 May 15 11:02 readme
-rw-r--r-- 1 root root  186 May 16 23:35 service.py
-rw-r--r-- 1 root root   96 May 14 14:04 setup.cfg
-rw-r--r-- 1 root root  494 May 14 14:04 setup.py
drwxr-xr-x 6 root root 4096 May 14 19:28 test_project

與之前比起來,我們增加了service.py 我們看一下service.py

from oslo_config import cfg
import sys

def prepare_service(argv=None):
    if argv is None:
        argv = sys.argv
    cfg.CONF(argv[1:], project='app', validate_default_values=True)

cfg的config file位置,是透過argv來得到的,因此在執行pecan service時,要帶config-file如下:

./api.py  --config-file /root/pecan_test/test_project/api.conf

此外api.py為入口,config的驅動由此開始。

import app
import service
def main():
    service.prepare_service()
    print ' start to run server'
    app.build_server()

main()

其中,service.prepare_service()驅動了config module的啟動。

當然在app.py或者controller的部分,針對config都必須做一些default宣告,並且register到group(section)裡,如下:

from oslo_config import cfg
.
.
CONF = cfg.CONF
OPTS = [
    cfg.StrOpt('api_paste_config',
               default="api_paste.ini",
               help="Configuration file for WSGI definition of API."
               ),
    cfg.IntOpt('api_workers', default=1,
               help='Number of workers for Ceilometer API server.'),
    cfg.StrOpt('host',
               default='0.0.0.0',
               help='The listen IP for the ceilometer API server.',
               ),
    cfg.IntOpt('port',
               default=8777,
               deprecated_name='metering_api_port',
               deprecated_group='DEFAULT',
               help='The port for the ceilometer API server.',
               ),
    cfg.IntOpt('workers', default=1,
               help='Number of workers for Ceilometer API server.'),
    cfg.IntOpt('threaded', default=100,
               help='Number of threaded for Ceilometer API server.'),
]

API_OPTS = [
    cfg.BoolOpt('pecan_debug',
                default=False,
                help='Toggle Pecan Debug Middleware.'),
]

CONF.register_opts(OPTS)
CONF.register_opts(OPTS, group='api')

接下你就可以透過CONF來獲取所要的config資訊。比如

host = CONF.api.host
port = CONF.api.port
workers=CONF.api.workers
threaded=CONF.api.threaded

之前範例load_app()部分也可透過config的完成而自動化,程式如下

cfg_file = None
cfg_path = cfg.CONF.api_paste_config
if not os.path.isabs(cfg_path):
    cfg_file = CONF.find_file(cfg_path)
elif os.path.exists(cfg_path):
    cfg_file = cfg_path

if not cfg_file:
    raise cfg.ConfigFilesNotFoundError([cfg.CONF.api_paste_config])
return deploy.loadapp("config:" + cfg_file)

接下來就是controller的部分,在不同檔案不同Class下,在我的Blog也介紹過,因此我也不再贅述方法。 於 test_project/controllers/v2/query.py下進行修改,方法與進行app.py時是相同的方式。

QUERY_API_OPTS = [
    cfg.IntOpt('threaded', default=100,
               help='Number of threaded for Ceilometer API server.'),
]
CONF = cfg.CONF
CONF.register_opts(QUERY_API_OPTS, group='api')

接下來在GET query裡我們將config讀出,改變程式如下

@pecan.expose()
def get(self):
    print 'show threaded we used %s'%CONF.api.threaded
    print 'return query'
    logger.error('return query from logger')
    #print self.user_id
    return "query get"

api.conf中的threaded參數可以透過CONF.api.threaded可輕鬆獲得。 因此,透過此方法,你所寫的任何一段程式,在任何檔案,目錄,任何Class都可以獲得。

注意

我們程式部分用的是testproject,但git的部分已經改成testproject_config了,因此,內容與你看到的有些許不同。

No comments:

Post a Comment