blob: 235ba1ad095a1732c0f077e756e82582870b242f [file] [log] [blame]
# author: Alex Savatieiev (osavatieiev@mirantis.com)
from gevent import monkey, pywsgi
monkey.patch_all()
import falcon # noqa E402
import os # noqa E402
import json # noqa E402
from copy import deepcopy # noqa E402
from platform import system, release, node # noqa E402
from cfg_checker.common.log import logger # noqa E402
from cfg_checker.common.settings import pkg_dir # noqa E402
from cfg_checker.common.exception import CheckerException # noqa E402
from cfg_checker.helpers.falcon_jinja2 import FalconTemplate # noqa E402
from .fio_runner import FioProcessShellRun, get_time # noqa E402
template = FalconTemplate(
path=os.path.join(pkg_dir, "templates")
)
_module_status = {
"status": "unknown",
"healthcheck": {},
"actions": [],
"options": {},
"uri": "<agent_uri>/api/<modile_name>",
}
_action = {
"module": "<name>",
"action": "<action>",
"options": "<options_dict>"
}
_modules = {
"fio": deepcopy(_module_status)
}
_status = {
"agent": {
"started": get_time()
},
"modules": list(_modules.keys()),
"help": {
".../api": {
"GET": "<this_status>",
"POST": json.dumps(_action)
},
".../api/<module_name>": {
"GET": "returns healthcheck and module help"
}
}
}
# Populate modules
_fio = FioProcessShellRun()
def _init_status(mod):
_modules[mod]["uri"] = "<agent_uri>/api/fio"
_modules[mod]["actions"] = list(_fio.actions.keys())
def _update_status(mod):
_modules[mod]["healthcheck"] = _fio.hchk
_modules[mod]["options"] = _fio.get_options()
_modules[mod].update(_fio.status())
class FioStatus:
_name = "fio"
def on_get(self, req, resp):
# Hacky way to handle empty request
_m = req.get_media(default_when_empty={})
if "fast" in _m and _m["fast"]:
resp.status = falcon.HTTP_200
resp.content_type = "application/json"
resp.text = json.dumps(_fio.status())
else:
_update_status(self._name)
resp.status = falcon.HTTP_200
resp.content_type = "application/json"
resp.text = json.dumps(_modules[self._name])
class Api:
def on_get(self, req, resp):
# return api status
resp.status = falcon.HTTP_200
resp.content_type = "application/json"
resp.text = json.dumps(_status)
def on_post(self, req, resp):
def _resp(resp, code, msg, opt={}):
resp.status = code
resp.content_type = "application/json"
resp.text = json.dumps({"error": msg, "options": opt})
# Handle actions
logger.info("Getting media")
try:
_m = req.stream.read()
_m = json.loads(_m)
except json.JSONDecodeError:
_msg = "Incorrect input data"
logger.error(_msg)
_resp(resp, falcon.HTTP_400, _msg)
return
if _m:
logger.debug("got media object:\n{}".format(json.dumps(_m)))
# Validate action structure
_module = _m.get('module', "")
_action = _m.get('action', "")
_options = _m.get('options', {})
if not _module or _module not in list(_modules.keys()):
logger.error("invalid module '{}'".format(_module))
_resp(
resp,
falcon.HTTP_400,
"Invalid module '{}'".format(_module)
)
return
elif not _action or _action not in _modules[_module]['actions']:
logger.error("invalid action '{}'".format(_action))
_resp(
resp,
falcon.HTTP_400,
"Invalid action '{}'".format(_action)
)
return
else:
# Handle command
_a = _fio.actions[_action]
try:
if _action == 'get_options':
logger.info("returning options")
resp.status = falcon.HTTP_200
resp.content_type = "application/json"
resp.text = json.dumps({"options": _a()})
elif _action == 'get_resultlist':
logger.info("getting results")
resp.status = falcon.HTTP_200
resp.content_type = "application/json"
resp.text = json.dumps({"resultlist": _a()})
elif _action == 'get_result':
if 'time' not in _options:
_msg = "No 'time' found for '{}'".format(_action)
logger.error(_msg)
_resp(
resp,
falcon.HTTP_400,
_msg
)
return
_time = _options['time']
logger.info("getting results for '{}'".format(_time))
resp.status = falcon.HTTP_200
resp.content_type = "application/json"
resp.text = json.dumps({_time: _a(_time)})
elif _action == 'do_singlerun':
logger.info("executing single run")
_a(_options)
resp.status = falcon.HTTP_200
resp.content_type = "application/json"
resp.text = json.dumps({"ok": True})
elif _action == 'do_scheduledrun':
logger.info("executing scheduled run")
# prepare scheduled run
# Run it
_a(_options)
resp.status = falcon.HTTP_200
resp.content_type = "application/json"
resp.text = json.dumps({"ok": True})
else:
_msg = "Unknown error happened for '{}/{}/{}'".format(
_module,
_action,
_options
)
logger.error(_msg)
_resp(resp, falcon.HTTP_500, _msg)
except CheckerException as e:
_msg = "Error for '{}/{}':\n{}".format(
_module,
_action,
e
)
logger.error(_msg)
_resp(resp, falcon.HTTP_500, _msg, opt=_options)
return
else:
_msg = "Empty request body"
logger.error(_msg)
_resp(resp, falcon.HTTP_400, _msg)
class Index:
@template.render("agent_index_html.j2")
def on_get(self, request, response):
# prepare template context
_context = {
"gen_date": get_time(),
"system": system(),
"release": release(),
"hostname": node()
}
_context.update(_status)
# creating response
response.status = falcon.HTTP_200
response.content_type = "text/html"
response.context = _context
def agent_server(host="0.0.0.0", port=8765):
# init api
api = falcon.API()
# populate pages
api.add_route("/", Index())
api.add_route("/api/", Api())
# Populate modules list
_active_modules = [FioStatus]
# init modules
for mod in _active_modules:
_init_status(mod._name)
_update_status(mod._name)
api.add_route("/api/"+mod._name, mod())
# init and start server
server = pywsgi.WSGIServer((host, port), api)
server.serve_forever()