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])
...