diff options
author | Dan Mick <dan.mick@inktank.com> | 2013-07-26 17:47:32 -0700 |
---|---|---|
committer | Dan Mick <dan.mick@inktank.com> | 2013-07-26 21:38:12 -0700 |
commit | 8af47755af26826f205402db9fef9632e0f1462e (patch) | |
tree | 6e0add26c95b624a572cc3236e3f1b900ccff4e5 | |
parent | 629326aa92f82a0bc66df30e572223743a55ccf4 (diff) | |
download | ceph-8af47755af26826f205402db9fef9632e0f1462e.tar.gz |
ceph-rest-api: clean up options/environment
ceph-rest-api:
* create app from wrapper by calling generate_app()
* pass args to generate_app() (early parsed in wrapper)
* parse -i/--id here as well
* set addr:port on returned app object
* handle only EnvironmentError exceptions; let others spew traceback
* turn off debug when running singlethreaded server
ceph_rest_api.py:
* put glob.* on app.ceph_* instead; pass around app in init code
* drop conf parsing (let librados do its job)
Documentation updated to match.
Signed-off-by: Dan Mick <dan.mick@inktank.com>
Reviewed-by: Sage Weil <sage@inktank.com>
-rw-r--r-- | doc/man/8/ceph-rest-api.rst | 75 | ||||
-rw-r--r-- | man/ceph-rest-api.8 | 76 | ||||
-rwxr-xr-x | src/ceph-rest-api | 31 | ||||
-rwxr-xr-x | src/pybind/ceph_rest_api.py | 226 |
4 files changed, 188 insertions, 220 deletions
diff --git a/doc/man/8/ceph-rest-api.rst b/doc/man/8/ceph-rest-api.rst index 8a87f97ce19..a000d09c676 100644 --- a/doc/man/8/ceph-rest-api.rst +++ b/doc/man/8/ceph-rest-api.rst @@ -7,7 +7,7 @@ Synopsis ======== -| **ceph-rest-api** [ -c *conffile* ] [ -n *name* ... ] +| **ceph-rest-api** [ -c *conffile* ] [--cluster *clustername* ] [ -n *name* ] [-i *id* ] Description @@ -21,7 +21,7 @@ command-line tool through an HTTP-accessible interface. Options ======= -.. option:: -c/--conf *conffile* +.. option:: -c/--conf conffile names the ceph.conf file to use for configuration. If -c is not specified, the default depends on the state of the --cluster option @@ -35,21 +35,26 @@ Options so you can also pass this option in the environment as CEPH_CONF. -.. option:: --cluster *clustername* +.. option:: --cluster clustername set *clustername* for use in the $cluster metavariable, for locating the ceph.conf file. The default is 'ceph'. - You can also pass this option in the environment as - CEPH_CLUSTER_NAME. -.. option:: -n/--name *name* +.. option:: -n/--name name specifies the client 'name', which is used to find the client-specific configuration options in the config file, and also is the name used for authentication when connecting to the cluster (the entity name appearing in ceph auth list output, - for example). The default is 'client.restapi'. You can also - pass this option in the environment as CEPH_NAME. + for example). The default is 'client.restapi'. + +.. option:: -i/--id id + + specifies the client 'id', which will form the clientname + as 'client.<id>' if clientname is not set. If -n/-name is + set, that takes precedence. + + Also, global Ceph options are supported. Configuration parameters @@ -57,16 +62,22 @@ Configuration parameters Supported configuration parameters include: -* **restapi client name** the 'clientname' used for auth and ceph.conf -* **restapi keyring** the keyring file holding the key for 'clientname' -* **restapi public addr** ip:port to listen on (default 0.0.0.0:5000) +* **keyring** the keyring file holding the key for 'clientname' +* **public addr** ip:port to listen on (default 0.0.0.0:5000) +* **log file** (usual Ceph default) * **restapi base url** the base URL to answer requests on (default /api/v0.1) -* **restapi log level** critical, error, warning, info, debug -* **restapi log file** (default /var/local/ceph/<clientname>.log) +* **restapi log level** critical, error, warning, info, debug (default warning) + +Configuration parameters are searched in the standard order: +first in the section named '<clientname>', then 'client', then 'global'. -A server will run on **restapi public addr** if the ceph-rest-api -executed directly; otherwise, configuration is specified by the -enclosing WSGI web server. +<clientname> is either supplied by -n/--name, "client.<id>" where +<id> is supplied by -i/--id, or 'client.restapi' if neither option +is present. + +A single-threaded server will run on **public addr** if the ceph-rest-api +executed directly; otherwise, configuration is specified by the enclosing +WSGI web server. Commands ======== @@ -92,7 +103,9 @@ with a small description of each command, is provided when the requested path is incomplete/partially matching. Requesting / will redirect to the value of **restapi base url**, and that path will give a full list of all known commands. The command set is very similar to the commands -supported by the **ceph** tool. +supported by the **ceph** tool. One notable exception is that the +``ceph pg <pgid> <command>`` style of commands is supported here +as ``tell/<pgid>/command?args``. Deployment as WSGI application ============================== @@ -101,18 +114,22 @@ When deploying as WSGI application (say, with Apache/mod_wsgi, or nginx/uwsgi, or gunicorn, etc.), use the ``ceph_rest_api.py`` module (``ceph-rest-api`` is a thin layer around this module). The standalone web server is of course not used, so address/port configuration is done in -the WSGI server. Also, configuration switches are not passed; rather, -environment variables are used: - -* CEPH_CONF holds -c/--conf -* CEPH_CLUSTER_NAME holds --cluster -* CEPH_NAME holds -n/--name - -Any errors reading configuration or connecting to the cluster cause -ImportError to be raised with a descriptive message on import; see -your WSGI server documentation for how to see those messages in case -of problem. - +the WSGI server. Use a python .wsgi module or the equivalent to call +``app = generate_app(conf, cluster, clientname, clientid, args)`` where: + +* conf is as -c/--conf above +* cluster is as --cluster above +* clientname, -n/--name +* clientid, -i/--id, and +* args are any other generic Ceph arguments + +When app is returned, it will have attributes 'ceph_addr' and 'ceph_port' +set to what the address and port are in the Ceph configuration; +those may be used for the server, or ignored. + +Any errors reading configuration or connecting to the cluster cause an +exception to be raised; see your WSGI server documentation for how to +see those messages in case of problem. Availability ============ diff --git a/man/ceph-rest-api.8 b/man/ceph-rest-api.8 index 33425fecc00..5170b7f37cf 100644 --- a/man/ceph-rest-api.8 +++ b/man/ceph-rest-api.8 @@ -1,4 +1,4 @@ -.TH "CEPH-REST-API" "8" "July 12, 2013" "dev" "Ceph" +.TH "CEPH-REST-API" "8" "July 26, 2013" "dev" "Ceph" .SH NAME ceph-rest-api \- ceph RESTlike administration server . @@ -32,7 +32,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] . .SH SYNOPSIS .nf -\fBceph\-rest\-api\fP [ \-c \fIconffile\fP ] [ \-n \fIname\fP ... ] +\fBceph\-rest\-api\fP [ \-c \fIconffile\fP ] [\-\-cluster \fIclustername\fP ] [ \-n \fIname\fP ] [\-i \fIid\fP ] .fi .sp .SH DESCRIPTION @@ -44,7 +44,7 @@ command\-line tool through an HTTP\-accessible interface. .SH OPTIONS .INDENT 0.0 .TP -.B \-c/\-\-conf *conffile* +.B \-c/\-\-conf conffile names the ceph.conf file to use for configuration. If \-c is not specified, the default depends on the state of the \-\-cluster option (default \(aqceph\(aq; see below). The configuration file is searched @@ -64,43 +64,54 @@ so you can also pass this option in the environment as CEPH_CONF. .UNINDENT .INDENT 0.0 .TP -.B \-\-cluster *clustername* +.B \-\-cluster clustername set \fIclustername\fP for use in the $cluster metavariable, for locating the ceph.conf file. The default is \(aqceph\(aq. -You can also pass this option in the environment as -CEPH_CLUSTER_NAME. .UNINDENT .INDENT 0.0 .TP -.B \-n/\-\-name *name* +.B \-n/\-\-name name specifies the client \(aqname\(aq, which is used to find the client\-specific configuration options in the config file, and also is the name used for authentication when connecting to the cluster (the entity name appearing in ceph auth list output, -for example). The default is \(aqclient.restapi\(aq. You can also -pass this option in the environment as CEPH_NAME. +for example). The default is \(aqclient.restapi\(aq. +.UNINDENT +.INDENT 0.0 +.TP +.B \-i/\-\-id id +specifies the client \(aqid\(aq, which will form the clientname +as \(aqclient.<id>\(aq if clientname is not set. If \-n/\-name is +set, that takes precedence. +.sp +Also, global Ceph options are supported. .UNINDENT .SH CONFIGURATION PARAMETERS .sp Supported configuration parameters include: .INDENT 0.0 .IP \(bu 2 -\fBrestapi client name\fP the \(aqclientname\(aq used for auth and ceph.conf +\fBkeyring\fP the keyring file holding the key for \(aqclientname\(aq .IP \(bu 2 -\fBrestapi keyring\fP the keyring file holding the key for \(aqclientname\(aq +\fBpublic addr\fP ip:port to listen on (default 0.0.0.0:5000) .IP \(bu 2 -\fBrestapi public addr\fP ip:port to listen on (default 0.0.0.0:5000) +\fBlog file\fP (usual Ceph default) .IP \(bu 2 \fBrestapi base url\fP the base URL to answer requests on (default /api/v0.1) .IP \(bu 2 -\fBrestapi log level\fP critical, error, warning, info, debug -.IP \(bu 2 -\fBrestapi log file\fP (default /var/local/ceph/<clientname>.log) +\fBrestapi log level\fP critical, error, warning, info, debug (default warning) .UNINDENT .sp -A server will run on \fBrestapi public addr\fP if the ceph\-rest\-api -executed directly; otherwise, configuration is specified by the -enclosing WSGI web server. +Configuration parameters are searched in the standard order: +first in the section named \(aq<clientname>\(aq, then \(aqclient\(aq, then \(aqglobal\(aq. +.sp +<clientname> is either supplied by \-n/\-\-name, "client.<id>" where +<id> is supplied by \-i/\-\-id, or \(aqclient.restapi\(aq if neither option +is present. +.sp +A single\-threaded server will run on \fBpublic addr\fP if the ceph\-rest\-api +executed directly; otherwise, configuration is specified by the enclosing +WSGI web server. .SH COMMANDS .sp Commands are submitted with HTTP GET requests (for commands that @@ -122,28 +133,37 @@ with a small description of each command, is provided when the requested path is incomplete/partially matching. Requesting / will redirect to the value of \fBrestapi base url\fP, and that path will give a full list of all known commands. The command set is very similar to the commands -supported by the \fBceph\fP tool. +supported by the \fBceph\fP tool. One notable exception is that the +\fBceph pg <pgid> <command>\fP style of commands is supported here +as \fBtell/<pgid>/command?args\fP. .SH DEPLOYMENT AS WSGI APPLICATION .sp When deploying as WSGI application (say, with Apache/mod_wsgi, or nginx/uwsgi, or gunicorn, etc.), use the \fBceph_rest_api.py\fP module (\fBceph\-rest\-api\fP is a thin layer around this module). The standalone web server is of course not used, so address/port configuration is done in -the WSGI server. Also, configuration switches are not passed; rather, -environment variables are used: +the WSGI server. Use a python .wsgi module or the equivalent to call +\fBapp = generate_app(conf, cluster, clientname, clientid, args)\fP where: .INDENT 0.0 .IP \(bu 2 -CEPH_CONF holds \-c/\-\-conf +conf is as \-c/\-\-conf above +.IP \(bu 2 +cluster is as \-\-cluster above .IP \(bu 2 -CEPH_CLUSTER_NAME holds \-\-cluster +clientname, \-n/\-\-name .IP \(bu 2 -CEPH_NAME holds \-n/\-\-name +clientid, \-i/\-\-id, and +.IP \(bu 2 +args are any other generic Ceph arguments .UNINDENT .sp -Any errors reading configuration or connecting to the cluster cause -ImportError to be raised with a descriptive message on import; see -your WSGI server documentation for how to see those messages in case -of problem. +When app is returned, it will have attributes \(aqceph_addr\(aq and \(aqceph_port\(aq +set to what the address and port are in the Ceph configuration; +those may be used for the server, or ignored. +.sp +Any errors reading configuration or connecting to the cluster cause an +exception to be raised; see your WSGI server documentation for how to +see those messages in case of problem. .SH AVAILABILITY .sp \fBceph\-rest\-api\fP is part of the Ceph distributed file system. Please refer to the Ceph documentation at diff --git a/src/ceph-rest-api b/src/ceph-rest-api index a44919acd6f..ae5245b4f76 100755 --- a/src/ceph-rest-api +++ b/src/ceph-rest-api @@ -33,34 +33,35 @@ def parse_args(): parser.add_argument('-c', '--conf', help='Ceph configuration file') parser.add_argument('--cluster', help='Ceph cluster name') parser.add_argument('-n', '--name', help='Ceph client name') + parser.add_argument('-i', '--id', help='Ceph client id') - return parser.parse_args() - + return parser.parse_known_args() # main -parsed_args = parse_args() -if parsed_args.conf: - os.environ['CEPH_CONF'] = parsed_args.conf -if parsed_args.cluster: - os.environ['CEPH_CLUSTER_NAME'] = parsed_args.cluster -if parsed_args.name: - os.environ['CEPH_NAME'] = parsed_args.name +parsed_args, rest = parse_args() # import now that env vars are available to imported module try: import ceph_rest_api -except Exception as e: +except EnvironmentError as e: print >> sys.stderr, "Error importing ceph_rest_api: ", str(e) sys.exit(1) -# importing ceph_rest_api has set module globals 'app', 'addr', and 'port' +# let other exceptions generate traceback + +app = ceph_rest_api.generate_app( + parsed_args.conf, + parsed_args.cluster, + parsed_args.name, + parsed_args.id, + rest, +) files = [os.path.split(fr[1])[-1] for fr in inspect.stack()] if 'pdb.py' in files: - ceph_rest_api.app.run(host=ceph_rest_api.addr, port=ceph_rest_api.port, - debug=True, use_reloader=False, use_debugger=False) + app.run(host=app.ceph_addr, port=app.ceph_port, + debug=True, use_reloader=False, use_debugger=False) else: - ceph_rest_api.app.run(host=ceph_rest_api.addr, port=ceph_rest_api.port, - debug=True) + app.run(host=app.ceph_addr, port=app.ceph_port) diff --git a/src/pybind/ceph_rest_api.py b/src/pybind/ceph_rest_api.py index 830fb2249ea..9e53eba05fd 100755 --- a/src/pybind/ceph_rest_api.py +++ b/src/pybind/ceph_rest_api.py @@ -2,7 +2,6 @@ # vim: ts=4 sw=4 smarttab expandtab import collections -import ConfigParser import contextlib import errno import json @@ -18,15 +17,18 @@ import flask from ceph_argparse import * # -# Globals +# Globals and defaults # +DEFAULT_ADDR = '0.0.0.0' +DEFAULT_PORT = '5000' +DEFAULT_ID = 'restapi' + DEFAULT_BASEURL = '/api/v0.1' -DEFAULT_ADDR = '0.0.0.0:5000' DEFAULT_LOG_LEVEL = 'warning' -DEFAULT_CLIENTNAME = 'client.restapi' -DEFAULT_LOG_FILE = '/var/log/ceph/' + DEFAULT_CLIENTNAME + '.log' +# default client name will be 'client.<DEFAULT_ID>' +# 'app' must be global for decorators, etc. APPNAME = '__main__' app = flask.Flask(APPNAME) @@ -38,88 +40,12 @@ LOGLEVELS = { 'debug':logging.DEBUG, } -# my globals, in a named tuple for usage clarity. I promise -# these are never written once initialized, and are global -# to every thread. - -glob = collections.namedtuple('gvars', 'cluster urls sigdict baseurl') -glob.cluster = None -glob.urls = {} -glob.sigdict = {} -glob.baseurl = '' - -def load_conf(clustername='ceph', conffile=None): - ''' - Load the ceph conf file using ConfigParser. Use the standard - fallback order: - - 1) the passed in arg (from CEPH_CONF) - 2) /etc/ceph/{cluster}.conf - 3) ~/.ceph/{cluster}.conf - 4) {cluster}.conf - ''' - - class _TrimIndentFile(object): - def __init__(self, fp): - self.fp = fp - - def readline(self): - line = self.fp.readline() - return line.lstrip(' \t') - - - def _optionxform(s): - s = s.replace('_', ' ') - s = '_'.join(s.split()) - return s - - - def parse(fp): - cfg = ConfigParser.RawConfigParser() - cfg.optionxform = _optionxform - ifp = _TrimIndentFile(fp) - cfg.readfp(ifp) - return cfg - - - def load(path): - f = file(path) - with contextlib.closing(f): - return parse(f) - - if conffile: - # from CEPH_CONF - return load(conffile) - else: - for path in [ - '/etc/ceph/{0}.conf'.format(clustername), - os.path.expanduser('~/.ceph/{0}.conf'.format(clustername)), - '{0}.conf'.format(clustername), - ]: - if os.path.exists(path): - return load(path) - - raise EnvironmentError('No conf file found for "{0}"'.format(clustername)) - -def get_conf(cfg, clientname, key): - ''' - Get config entry from conf file, first in [clientname], then [client], - then [global]. - ''' - fullkey = 'restapi_' + key - for sectionname in clientname, 'client', 'global': - try: - return cfg.get(sectionname, fullkey) - except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): - pass - return None - -def find_up_osd(): +def find_up_osd(app): ''' Find an up OSD. Return the last one that's up. Returns id as an int. ''' - ret, outbuf, outs = json_command(glob.cluster, prefix="osd dump", + ret, outbuf, outs = json_command(app.ceph_cluster, prefix="osd dump", argdict=dict(format='json')) if ret: raise EnvironmentError(ret, 'Can\'t get osd dump output') @@ -135,18 +61,18 @@ def find_up_osd(): METHOD_DICT = {'r':['GET'], 'w':['PUT', 'DELETE']} -def api_setup(): +def api_setup(app, conf, cluster, clientname, clientid, args): ''' This is done globally, and cluster connection kept open for the lifetime of the daemon. librados should assure that even if the cluster goes away and comes back, our connection remains. Initialize the running instance. Open the cluster, get the command - signatures, module, perms, and help; stuff them away in the glob.urls - dict. Also save glob.sigdict for help() handling. + signatures, module, perms, and help; stuff them away in the app.ceph_urls + dict. Also save app.ceph_sigdict for help() handling. ''' - def get_command_descriptions(target=('mon','')): - ret, outbuf, outs = json_command(glob.cluster, target, + def get_command_descriptions(cluster, target=('mon','')): + ret, outbuf, outs = json_command(cluster, target, prefix='get_command_descriptions', timeout=30) if ret: @@ -162,62 +88,61 @@ def api_setup(): raise EnvironmentError(err) return sigdict - conffile = os.environ.get('CEPH_CONF', '') - clustername = os.environ.get('CEPH_CLUSTER_NAME', 'ceph') - clientname = os.environ.get('CEPH_NAME', DEFAULT_CLIENTNAME) - try: - err = '' - cfg = load_conf(clustername, conffile) - except Exception as e: - err = "Can't load Ceph conf file: " + str(e) - app.logger.critical(err) - app.logger.critical("CEPH_CONF: %s", conffile) - app.logger.critical("CEPH_CLUSTER_NAME: %s", clustername) - raise EnvironmentError(err) - - client_logfile = '/var/log/ceph' + clientname + '.log' - - glob.cluster = rados.Rados(name=clientname, conffile=conffile) - glob.cluster.connect() - - glob.baseurl = get_conf(cfg, clientname, 'base_url') or DEFAULT_BASEURL - if glob.baseurl.endswith('/'): - glob.baseurl = glob.baseurl[:-1] - addr = get_conf(cfg, clientname, 'public_addr') or DEFAULT_ADDR - addrport = addr.rsplit(':', 1) - addr = addrport[0] - if len(addrport) > 1: - port = addrport[1] - else: - port = DEFAULT_ADDR.rsplit(':', 1) + app.ceph_cluster = cluster or 'ceph' + app.ceph_urls = {} + app.ceph_sigdict = {} + app.ceph_baseurl = '' + + conf = conf or '' + cluster = cluster or 'ceph' + clientid = clientid or DEFAULT_ID + clientname = clientname or 'client.' + clientid + + app.ceph_cluster = rados.Rados(name=clientname, conffile=conf) + app.ceph_cluster.conf_parse_argv(args) + app.ceph_cluster.connect() + + app.ceph_baseurl = app.ceph_cluster.conf_get('restapi_base_url') \ + or DEFAULT_BASEURL + if app.ceph_baseurl.endswith('/'): + app.ceph_baseurl = app.ceph_baseurl[:-1] + addr = app.ceph_cluster.conf_get('public_addr') or DEFAULT_ADDR + + # remove any nonce from the conf value + addr = addr.split('/')[0] + addr, port = addr.rsplit(':', 1) + addr = addr or DEFAULT_ADDR + port = port or DEFAULT_PORT port = int(port) - loglevel = get_conf(cfg, clientname, 'log_level') or DEFAULT_LOG_LEVEL - logfile = get_conf(cfg, clientname, 'log_file') or client_logfile + loglevel = app.ceph_cluster.conf_get('restapi_log_level') \ + or DEFAULT_LOG_LEVEL + logfile = app.ceph_cluster.conf_get('log_file') app.logger.addHandler(logging.handlers.WatchedFileHandler(logfile)) app.logger.setLevel(LOGLEVELS[loglevel.lower()]) for h in app.logger.handlers: h.setFormatter(logging.Formatter( '%(asctime)s %(name)s %(levelname)s: %(message)s')) - glob.sigdict = get_command_descriptions() + app.ceph_sigdict = get_command_descriptions(app.ceph_cluster) - osdid = find_up_osd() + osdid = find_up_osd(app) if osdid: - osd_sigdict = get_command_descriptions(target=('osd', int(osdid))) + osd_sigdict = get_command_descriptions(app.ceph_cluster, + target=('osd', int(osdid))) - # shift osd_sigdict keys up to fit at the end of the mon's glob.sigdict - maxkey = sorted(glob.sigdict.keys())[-1] + # shift osd_sigdict keys up to fit at the end of the mon's app.ceph_sigdict + maxkey = sorted(app.ceph_sigdict.keys())[-1] maxkey = int(maxkey.replace('cmd', '')) osdkey = maxkey + 1 for k, v in osd_sigdict.iteritems(): newv = v newv['flavor'] = 'tell' globk = 'cmd' + str(osdkey) - glob.sigdict[globk] = newv + app.ceph_sigdict[globk] = newv osdkey += 1 - # glob.sigdict maps "cmdNNN" to a dict containing: + # app.ceph_sigdict maps "cmdNNN" to a dict containing: # 'sig', an array of argdescs # 'help', the helptext # 'module', the Ceph module this command relates to @@ -225,11 +150,11 @@ def api_setup(): # a hint as to whether this is a GET or POST/PUT operation # 'avail', a comma-separated list of strings of consumers that should # display this command (filtered by parse_json_funcsigs() above) - glob.urls = {} - for cmdnum, cmddict in glob.sigdict.iteritems(): + app.ceph_urls = {} + for cmdnum, cmddict in app.ceph_sigdict.iteritems(): cmdsig = cmddict['sig'] flavor = cmddict.get('flavor', 'mon') - url, params = generate_url_and_params(cmdsig, flavor) + url, params = generate_url_and_params(app, cmdsig, flavor) perm = cmddict['perm'] for k in METHOD_DICT.iterkeys(): if k in perm: @@ -242,34 +167,34 @@ def api_setup(): 'methods':methods, } - # glob.urls contains a list of urldicts (usually only one long) - if url not in glob.urls: - glob.urls[url] = [urldict] + # app.ceph_urls contains a list of urldicts (usually only one long) + if url not in app.ceph_urls: + app.ceph_urls[url] = [urldict] else: # If more than one, need to make union of methods of all. # Method must be checked in handler methodset = set(methods) - for old_urldict in glob.urls[url]: + for old_urldict in app.ceph_urls[url]: methodset |= set(old_urldict['methods']) methods = list(methodset) - glob.urls[url].append(urldict) + app.ceph_urls[url].append(urldict) # add, or re-add, rule with all methods and urldicts app.add_url_rule(url, url, handler, methods=methods) url += '.<fmt>' app.add_url_rule(url, url, handler, methods=methods) - app.logger.debug("urls added: %d", len(glob.urls)) + app.logger.debug("urls added: %d", len(app.ceph_urls)) app.add_url_rule('/<path:catchall_path>', '/<path:catchall_path>', handler, methods=['GET', 'PUT']) return addr, port -def generate_url_and_params(sig, flavor): +def generate_url_and_params(app, sig, flavor): ''' Digest command signature from cluster; generate an absolute - (including glob.baseurl) endpoint from all the prefix words, + (including app.ceph_baseurl) endpoint from all the prefix words, and a list of non-prefix param descs ''' @@ -304,7 +229,7 @@ def generate_url_and_params(sig, flavor): else: params.append(desc) - return glob.baseurl + url, params + return app.ceph_baseurl + url, params # @@ -339,7 +264,7 @@ def show_human_help(prefix): permmap = {'r':'GET', 'rw':'PUT'} line = '' - for cmdsig in sorted(glob.sigdict.itervalues(), cmp=descsort): + for cmdsig in sorted(app.ceph_sigdict.itervalues(), cmp=descsort): concise = concise_sig(cmdsig['sig']) flavor = cmdsig.get('flavor', 'mon') if flavor == 'tell': @@ -374,7 +299,7 @@ def log_request(): @app.route('/') def root_redir(): - return flask.redirect(glob.baseurl) + return flask.redirect(app.ceph_baseurl) def make_response(fmt, output, statusmsg, errorcode): ''' @@ -433,11 +358,11 @@ def handler(catchall_path=None, fmt=None, target=None): if ep[0] != '/': ep = '/' + ep - # demand that endpoint begin with glob.baseurl - if not ep.startswith(glob.baseurl): + # demand that endpoint begin with app.ceph_baseurl + if not ep.startswith(app.ceph_baseurl): return make_response(fmt, '', 'Page not found', 404) - rel_ep = ep[len(glob.baseurl)+1:] + rel_ep = ep[len(app.ceph_baseurl)+1:] # Extensions override Accept: headers override defaults if not fmt: @@ -477,7 +402,7 @@ def handler(catchall_path=None, fmt=None, target=None): prefix = ' '.join(rel_ep.split('/')).strip() # show "match as much as you gave me" help for unknown endpoints - if not ep in glob.urls: + if not ep in app.ceph_urls: helptext = show_human_help(prefix) if helptext: resp = flask.make_response(helptext, 400) @@ -488,7 +413,7 @@ def handler(catchall_path=None, fmt=None, target=None): found = None exc = '' - for urldict in glob.urls[ep]: + for urldict in app.ceph_urls[ep]: if flask.request.method not in urldict['methods']: continue paramsig = urldict['paramsig'] @@ -537,7 +462,7 @@ def handler(catchall_path=None, fmt=None, target=None): cmdtarget = ('mon', '') app.logger.debug('sending command prefix %s argdict %s', prefix, argdict) - ret, outbuf, outs = json_command(glob.cluster, prefix=prefix, + ret, outbuf, outs = json_command(app.ceph_cluster, prefix=prefix, target=cmdtarget, inbuf=flask.request.data, argdict=argdict) if ret: @@ -552,6 +477,11 @@ def handler(catchall_path=None, fmt=None, target=None): return response # -# Last module-level (import-time) ask: set up the cluster connection -# -addr, port = api_setup() +# Main entry point from wrapper/WSGI server: call with cmdline args, +# get back the WSGI app entry point +# +def generate_app(conf, cluster, clientname, clientid, args): + addr, port = api_setup(app, conf, cluster, clientname, clientid, args) + app.ceph_addr = addr + app.ceph_port = port + return app |