2012-10-01 18:57:46 +00:00
|
|
|
'''
|
|
|
|
A REST API for Salt
|
2012-09-26 21:58:55 +00:00
|
|
|
'''
|
|
|
|
from flask import Flask
|
|
|
|
from flask import jsonify
|
|
|
|
from flask import request
|
|
|
|
from flask.views import MethodView
|
2012-10-01 21:59:41 +00:00
|
|
|
from werkzeug import exceptions
|
2012-09-26 21:58:55 +00:00
|
|
|
|
|
|
|
import salt.client
|
2012-10-01 17:05:17 +00:00
|
|
|
import salt.runner
|
2012-10-01 18:57:46 +00:00
|
|
|
import saltapi.loader
|
|
|
|
|
|
|
|
def __virtual__():
|
|
|
|
'''
|
|
|
|
Verify enough infos to actually start server.
|
|
|
|
'''
|
|
|
|
# if not 'port' in __opts__ or not __opts__['port']:
|
|
|
|
# return False
|
|
|
|
|
|
|
|
return 'rest'
|
|
|
|
|
2012-09-26 21:58:55 +00:00
|
|
|
|
|
|
|
def make_json_error(ex):
|
|
|
|
'''
|
|
|
|
Creates a JSON-oriented Flask app.
|
|
|
|
|
|
|
|
All error responses that you don't specifically
|
|
|
|
manage yourself will have application/json content
|
|
|
|
type, and will contain JSON like this (just an example)::
|
|
|
|
|
|
|
|
{ "message": "405: Method Not Allowed" }
|
|
|
|
|
|
|
|
http://flask.pocoo.org/snippets/83/
|
|
|
|
|
|
|
|
'''
|
|
|
|
response = jsonify(message=str(ex))
|
2012-10-01 21:59:41 +00:00
|
|
|
response.status_code = (ex.code
|
|
|
|
if isinstance(ex, exceptions.HTTPException) else 500)
|
2012-09-26 21:58:55 +00:00
|
|
|
return response
|
|
|
|
|
2012-10-01 19:41:58 +00:00
|
|
|
class SaltAPI(MethodView):
|
2012-10-01 17:05:17 +00:00
|
|
|
'''
|
2012-10-01 19:41:58 +00:00
|
|
|
Base class for salt objects
|
2012-10-01 17:05:17 +00:00
|
|
|
'''
|
2012-10-01 20:22:10 +00:00
|
|
|
def __init__(self, *args, **kwargs):
|
2012-10-01 18:57:46 +00:00
|
|
|
self.runners = saltapi.loader.runner(__opts__)
|
|
|
|
self.local = salt.client.LocalClient(__opts__['conf_file'])
|
|
|
|
|
2012-10-01 19:41:58 +00:00
|
|
|
|
|
|
|
class JobsView(SaltAPI):
|
|
|
|
'''
|
|
|
|
View Salt jobs or create new jobs (run commands)
|
|
|
|
'''
|
2012-10-01 17:05:17 +00:00
|
|
|
def get_job_by_jid(self, jid):
|
|
|
|
'''
|
|
|
|
Return information on a previously run job
|
|
|
|
'''
|
2012-10-01 19:23:36 +00:00
|
|
|
ret = self.runners['jobs.lookup_jid'](jid)
|
2012-09-26 21:58:55 +00:00
|
|
|
return jsonify(ret)
|
|
|
|
|
2012-10-01 17:05:17 +00:00
|
|
|
def get_jobs_list(self):
|
|
|
|
'''
|
|
|
|
Return a previously run jobs
|
|
|
|
'''
|
2012-10-01 19:23:36 +00:00
|
|
|
ret = self.runners['jobs.list_jobs']()
|
|
|
|
return jsonify(ret)
|
2012-10-01 17:05:17 +00:00
|
|
|
|
|
|
|
def get(self, jid=None):
|
|
|
|
'''
|
|
|
|
View a list of previously run jobs, or fetch a single job
|
|
|
|
'''
|
|
|
|
if jid:
|
|
|
|
return self.get_job_by_jid(jid)
|
|
|
|
|
|
|
|
return self.get_jobs_list()
|
|
|
|
|
2012-09-26 21:58:55 +00:00
|
|
|
def post(self):
|
|
|
|
'''
|
2012-10-01 17:05:17 +00:00
|
|
|
Run minion commands
|
2012-09-26 21:58:55 +00:00
|
|
|
'''
|
2012-10-01 18:57:46 +00:00
|
|
|
ret = self.local.cmd(
|
2012-09-26 21:58:55 +00:00
|
|
|
request.form['tgt'],
|
|
|
|
request.form['cmd'])
|
|
|
|
return jsonify(ret)
|
|
|
|
|
2012-10-01 18:56:39 +00:00
|
|
|
|
2012-09-26 21:58:55 +00:00
|
|
|
def build_app():
|
|
|
|
'''
|
|
|
|
Build the Flask app
|
|
|
|
'''
|
2012-10-01 18:57:46 +00:00
|
|
|
app = Flask('rest_flask')
|
2012-10-01 17:05:17 +00:00
|
|
|
jobs = JobsView.as_view('jobs')
|
2012-09-26 21:58:55 +00:00
|
|
|
|
2012-10-01 21:59:41 +00:00
|
|
|
for code in exceptions.default_exceptions.iterkeys():
|
2012-09-26 21:58:55 +00:00
|
|
|
app.error_handler_spec[None][code] = make_json_error
|
|
|
|
|
2012-10-01 17:05:17 +00:00
|
|
|
app.add_url_rule('/jobs', view_func=jobs, methods=['GET', 'POST'])
|
|
|
|
app.add_url_rule('/jobs/<int:jid>', view_func=jobs, methods=['GET'])
|
2012-09-26 21:58:55 +00:00
|
|
|
|
|
|
|
return app
|
|
|
|
|
2012-10-01 18:56:39 +00:00
|
|
|
|
2012-09-26 21:58:55 +00:00
|
|
|
def bind():
|
|
|
|
'''
|
|
|
|
Server loop here. Started in a multiprocess.
|
|
|
|
'''
|
|
|
|
app = build_app()
|
|
|
|
# port = __opts__['port']
|
|
|
|
app.run(host='0.0.0.0', port=8000, debug=True)
|