blob: 67a45670116a372b2cc6223d237fb05e8728999c [file] [log] [blame]
Alexb78191f2021-11-02 16:35:46 -05001# author: Alex Savatieiev (osavatieiev@mirantis.com)
2from gevent import monkey, pywsgi
3monkey.patch_all()
4import falcon # noqa E402
5import os # noqa E402
6import json # noqa E402
7
8from copy import deepcopy # noqa E402
9from platform import system, release, node # noqa E402
10
11from cfg_checker.common.settings import pkg_dir # noqa E402
12from cfg_checker.helpers.falcon_jinja2 import FalconTemplate # noqa E402
13from .fio_runner import FioProcessShellRun, get_time # noqa E402
14
15template = FalconTemplate(
16 path=os.path.join(pkg_dir, "templates")
17)
18
19_module_status = {
20 "status": "unknown",
21 "healthcheck": {},
22 "actions": [],
23 "options": {},
24 "uri": "<agent_uri>/api/<modile_name>",
25}
26
27_action = {
28 "module": "<name>",
29 "action": "<action>",
30 "options": "<options_dict>"
31}
32
33_modules = {
34 "fio": deepcopy(_module_status)
35}
36
37_status = {
38 "agent": {
39 "started": get_time()
40 },
41 "modules": list(_modules.keys()),
42 "help": {
43 ".../api": {
44 "GET": "<this_status>",
45 "POST": json.dumps(_action)
46 },
47 ".../api/<module_name>": {
48 "GET": "returns healthcheck and module help"
49 }
50 }
51}
52
53# Populate modules
54_fio = FioProcessShellRun()
55
56
57def _init_status(mod):
58 _modules[mod]["uri"] = "<agent_uri>/api/fio"
59 _modules[mod]["actions"] = list(_fio.actions.keys())
60
61
62def _update_status(mod):
63 _modules[mod]["healthcheck"] = _fio.hchk
64 _modules[mod]["options"] = _fio.get_options()
65 _modules[mod].update(_fio.status())
66
67
68class FioStatus:
69 _name = "fio"
70
71 def on_get(self, req, resp):
72 # Hacky way to handle empty request
73 _m = req.get_media(default_when_empty={})
74 if "fast" in _m and _m["fast"]:
75 resp.status = falcon.HTTP_200
76 resp.content_type = "application/json"
77 resp.text = json.dumps(_fio.status())
78 else:
79 _update_status(self._name)
80
81 resp.status = falcon.HTTP_200
82 resp.content_type = "application/json"
83 resp.text = json.dumps(_modules[self._name])
84
85
86class Api:
87 def on_get(self, req, resp):
88 # return api status
89 resp.status = falcon.HTTP_200
90 resp.content_type = "application/json"
91 resp.text = json.dumps(_status)
92
93 def on_post(self, req, resp):
94 def _resp(resp, code, msg):
95 resp.status = code
96 resp.content_type = "application/json"
97 resp.text = json.dumps({"error": msg})
98 # Handle actions
99 _m = req.get_media(default_when_empty={})
100 if _m:
101 # Validate action structure
102 _module = _m.get('module', "")
103 _action = _m.get('action', "")
104 _options = _m.get('options', {})
105
106 if not _module or _module not in list(_modules.keys()):
107 _resp(
108 resp,
109 falcon.HTTP_400,
110 "Invalid module '{}'".format(_module)
111 )
112 return
113 elif not _action or _action not in _modules[_module]['actions']:
114 _resp(
115 resp,
116 falcon.HTTP_400,
117 "Invalid action '{}'".format(_action)
118 )
119 return
120 else:
121 # Handle command
122 _a = _fio.actions[_action]
123 if _action == 'get_options':
124 resp.status = falcon.HTTP_200
125 resp.content_type = "application/json"
126 resp.text = json.dumps({"options": _a()})
127 elif _action == 'get_resultlist':
128 resp.status = falcon.HTTP_200
129 resp.content_type = "application/json"
130 resp.text = json.dumps({"resultlist": _a()})
131 elif _action == 'get_result':
132 if 'time' not in _options:
133 _resp(
134 resp,
135 falcon.HTTP_400,
136 "No 'time' found for '{}'".format(_action)
137 )
138 return
139 _time = _options['time']
140 resp.status = falcon.HTTP_200
141 resp.content_type = "application/json"
142 resp.text = json.dumps({_time: _a(_time)})
143 elif _action == 'do_singlerun':
144 _a(_options)
145 resp.status = falcon.HTTP_200
146 resp.content_type = "application/json"
147 resp.text = json.dumps({"ok": True})
148 elif _action == 'do_scheduledrun':
149 # prepare scheduled run
150
151 # Run it
152 _a(_options)
153 resp.status = falcon.HTTP_200
154 resp.content_type = "application/json"
155 resp.text = json.dumps({"ok": True})
156 else:
157 _resp(
158 resp,
159 falcon.HTTP_500,
160 "Unknown error happened for '{}/{}/{}'".format(
161 _module,
162 _action,
163 _options
164 )
165 )
166 return
167 else:
Alex5cace3b2021-11-10 16:40:37 -0600168 _resp(resp, falcon.HTTP_400, "Empty request body")
Alexb78191f2021-11-02 16:35:46 -0500169
170
171class Index:
172 @template.render("agent_index_html.j2")
173 def on_get(self, request, response):
174 # prepare template context
175 _context = {
176 "gen_date": get_time(),
177 "system": system(),
178 "release": release(),
179 "hostname": node()
180 }
181 _context.update(_status)
182 # creating response
183 response.status = falcon.HTTP_200
184 response.content_type = "text/html"
185 response.context = _context
186
187
188def agent_server(host="0.0.0.0", port=8765):
189 # init api
190 api = falcon.API()
191 # populate pages
192 api.add_route("/", Index())
193 api.add_route("/api/", Api())
194
195 # Populate modules list
196 _active_modules = [FioStatus]
197 # init modules
198 for mod in _active_modules:
199 _init_status(mod._name)
200 _update_status(mod._name)
201
202 api.add_route("/api/"+mod._name, mod())
203
204 # init and start server
205 server = pywsgi.WSGIServer((host, port), api)
206 server.serve_forever()