Ceilometer RESTful框架
ceilometer-api使用pecan和flask来构建restful api,这里简单介绍一下pecan和flask的使用。
ceilomter-api服务启动流程
/usr/bin/ceilometer-api
... from ceilometer.cli import api if __name__ == "__main__": sys.exit(api())
...
/usr/lib/python2.6/site-packages/ceilometer/cli.py
...
def api(): service.prepare_service() srv = app.build_server() srv.serve_forever()
...
/usr/lib/python2.6/site-packages/ceilometer/api/app.py
...
def build_server(): # Build the WSGI app root = VersionSelectorApplication() # Create the WSGI server and start it host, port = cfg.CONF.api.host, cfg.CONF.api.port server_cls = get_server_cls(host) srv = simple_server.make_server(host, port, root, server_cls)
...
...
class VersionSelectorApplication(object): def __init__(self): pc = get_pecan_config() pc.app.debug = CONF.debug pc.app.enable_acl = (CONF.auth_strategy == 'keystone') if cfg.CONF.enable_v1_api: from ceilometer.api.v1 import app as v1app self.v1 = v1app.make_app(cfg.CONF, enable_acl=pc.app.enable_acl) else: def not_found(environ, start_response): start_response('404 Not Found', []) return [] self.v1 = not_found self.v2 = setup_app(pecan_config=pc) def __call__(self, environ, start_response): if environ['PATH_INFO'].startswith('/v1/'): return self.v1(environ, start_response) return self.v2(environ, start_response)
...
如上图所示,ceilometer-api有两种版本即V1和V2,分别使用Flask和Pecan来构建RESTful api,其中V1在Havana已deprecated。
Pecan 是轻量的web框架,主页http://pecan.readthedocs.org/ ,其中URL Mapping如下图:
:
ceilometer使用pecan代码如下:
/usr/lib/python2.6/site-packages/ceilometer/api/app.py
...
def setup_app(pecan_config=None, extra_hooks=None): # FIXME: Replace DBHook with a hooks.TransactionHook app_hooks = [hooks.ConfigHook(), hooks.DBHook( storage.get_connection(cfg.CONF, 'metric'), storage.get_connection(cfg.CONF, 'event'), ), hooks.PipelineHook(), hooks.TranslationHook()] if extra_hooks: app_hooks.extend(extra_hooks) if not pecan_config: pecan_config = get_pecan_config() pecan.configuration.set_config(dict(pecan_config), overwrite=True) app = pecan.make_app( pecan_config.app.root, static_root=pecan_config.app.static_root, template_path=pecan_config.app.template_path, debug=CONF.debug, force_canonical=getattr(pecan_config.app, 'force_canonical', True), hooks=app_hooks, wrap_app=middleware.ParsableErrorMiddleware, guess_content_type_from_ext=False ) if getattr(pecan_config.app, 'enable_acl', True): return acl.install(app, cfg.CONF) return app
...
ceilometer中pecan的配置 /usr/lib/python2.6/site-packages/ceilometer/api/config.py
...
app = { 'root': 'ceilometer.api.controllers.root.RootController', 'modules': ['ceilometer.api'], 'static_root': '%(confdir)s/public', 'template_path': '%(confdir)s/ceilometer/api/templates', }
...
RootController /usr/lib/python2.6/site-packages/ceilometer/api/controllers/root.py
...
class RootController(object): v2 = v2.V2Controller() @pecan.expose(generic=True, template='index.html') def index(self): # FIXME: Return version information return dict()
...
V2Controller包含多个Resource的Controller, /usr/lib/python2.6/site-packages/ceilometer/api/controllers/v2.py
class V2Controller(object): """Version 2 API controller root.""" resources = ResourcesController() meters = MetersController() samples = SamplesController() alarms = AlarmsController() event_types = EventTypesController() events = EventsController() status = StatusController() query = QueryController() capabilities = CapabilitiesController()
下面以MetersController进行分析:/usr/lib/python2.6/site-packages/ceilometer/api/controllers/v2.py
class MetersController(rest.RestController): """Works on meters.""" @pecan.expose() def _lookup(self, meter_name, *remainder): return MeterController(meter_name), remainder
#wsme_pecan是来自wsmeext.pecan @wsme_pecan.wsexpose([Meter], [Query]) def get_all(self, q=[]): """Return all known meters, based on the data recorded so far. :param q: Filter rules for the meters to be returned. """ #Timestamp field is not supported for Meter queries kwargs = _query_to_kwargs(q, pecan.request.metric_conn.get_meters, allow_timestamps=False) return [Meter.from_db_model(m) for m in pecan.request.metric_conn.get_meters(**kwargs)]
这里用到了wsme和wsmeext.pecan。wsme即Web Services Made Easy,简化了REST Web服务的编写。可以运行在另一个框架的顶层,如(Cornice、Flask、Pecan)等,这里就是运行在了pecan之上来更加简单的构建restapi,用法:
wsmeext.pecan.wsexpose(return_type, *arg_types, **options)
Flask与Pecan类似,是基于Python的开源微RESTful框架,ceilometer使用Flask代码如下: /usr/lib/python2.6/site-packages/ceilometer/api/v1/app.py
...
def make_app(conf, enable_acl=True, attach_storage=True, sources_file='sources.json'): app = flask.Flask('ceilometer.api') app.register_blueprint(v1_blueprint.blueprint, url_prefix='/v1') app.json_encoder = JSONEncoder try: with open(sources_file, "r") as f: sources = jsonutils.load(f) except IOError: sources = {} @app.before_request def attach_config(): flask.request.cfg = conf flask.request.sources = sources if attach_storage: @app.before_request def attach_storage(): flask.request.storage_conn = \ storage.get_connection(conf) # Install the middleware wrapper if enable_acl: app.wsgi_app = acl.install(app.wsgi_app, conf) return app
...
app.register_blueprint 注册RESTful方法,如下图所示
...
blueprint = flask.Blueprint('v1', __name__, template_folder='templates', static_folder='static') @blueprint.route('/meters') def list_meters_all(): """Return a list of meters. :param metadata.<key>: match on the metadata within the resource. (optional) """ rq = flask.request meters = rq.storage_conn.get_meters( project=acl.get_limited_to_project(rq.headers), metaquery=_get_metaquery(rq.args)) return flask.jsonify(meters=[m.as_dict() for m in meters])
...