blob: 4a17e0c59a47a95993e4b7b0dfe64d21d0561057 [file] [log] [blame]
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001from fastapi import FastAPI, Response, Request
2import aiodocker
3import psutil
4import sys
5import re
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01006import time
7import os
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01008import json
9import asyncio
10import redis
11from datetime import datetime
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +010012
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +010013
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +010014containerIds_to_update = []
15host_stats_isUpdating = False
16app = FastAPI()
17
18
19@app.get("/host/stats")
20async def get_host_update_stats():
21 global host_stats_isUpdating
22
23 if host_stats_isUpdating == False:
24 print("start host stats task")
25 asyncio.create_task(get_host_stats())
26 host_stats_isUpdating = True
27
28 while True:
29 if redis_client.exists('host_stats'):
30 break
31 print("wait for host_stats results")
32 await asyncio.sleep(1.5)
33
34
35 print("host stats pulled")
36 stats = json.loads(redis_client.get('host_stats'))
37 return Response(content=json.dumps(stats, indent=4), media_type="application/json")
38
39@app.get("/containers/{container_id}/json")
40async def get_container(container_id : str):
41 if container_id and container_id.isalnum():
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +010042 try:
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +010043 for container in (await async_docker_client.containers.list()):
44 if container._id == container_id:
45 container_info = await container.show()
46 return Response(content=json.dumps(container_info, indent=4), media_type="application/json")
47
48 res = {
49 "type": "danger",
50 "msg": "no container found"
51 }
52 return Response(content=json.dumps(res, indent=4), media_type="application/json")
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +010053 except Exception as e:
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +010054 res = {
55 "type": "danger",
56 "msg": str(e)
57 }
58 return Response(content=json.dumps(res, indent=4), media_type="application/json")
59 else:
60 res = {
61 "type": "danger",
62 "msg": "no or invalid id defined"
63 }
64 return Response(content=json.dumps(res, indent=4), media_type="application/json")
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +010065
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +010066@app.get("/containers/json")
67async def get_containers():
68 containers = {}
69 try:
70 for container in (await async_docker_client.containers.list()):
71 container_info = await container.show()
72 containers.update({container_info['Id']: container_info})
73 return Response(content=json.dumps(containers, indent=4), media_type="application/json")
74 except Exception as e:
75 res = {
76 "type": "danger",
77 "msg": str(e)
78 }
79 return Response(content=json.dumps(res, indent=4), media_type="application/json")
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +010080
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +010081@app.post("/containers/{container_id}/{post_action}")
82async def post_containers(container_id : str, post_action : str, request: Request):
83 try :
84 request_json = await request.json()
85 except Exception as err:
86 request_json = {}
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +010087
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +010088 if container_id and container_id.isalnum() and post_action:
89 try:
90 """Dispatch container_post api call"""
91 if post_action == 'exec':
92 if not request_json or not 'cmd' in request_json:
93 res = {
94 "type": "danger",
95 "msg": "cmd is missing"
96 }
97 return Response(content=json.dumps(res, indent=4), media_type="application/json")
98 if not request_json or not 'task' in request_json:
99 res = {
100 "type": "danger",
101 "msg": "task is missing"
102 }
103 return Response(content=json.dumps(res, indent=4), media_type="application/json")
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100104
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100105 api_call_method_name = '__'.join(['container_post', str(post_action), str(request_json['cmd']), str(request_json['task']) ])
106 else:
107 api_call_method_name = '__'.join(['container_post', str(post_action) ])
108
109 docker_utils = DockerUtils(async_docker_client)
110 api_call_method = getattr(docker_utils, api_call_method_name, lambda container_id: Response(content=json.dumps({'type': 'danger', 'msg':'container_post - unknown api call' }, indent=4), media_type="application/json"))
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100111
112
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100113 print("api call: %s, container_id: %s" % (api_call_method_name, container_id))
114 return await api_call_method(container_id, request_json)
115 except Exception as e:
116 print("error - container_post: %s" % str(e))
117 res = {
118 "type": "danger",
119 "msg": str(e)
120 }
121 return Response(content=json.dumps(res, indent=4), media_type="application/json")
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100122
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100123 else:
124 res = {
125 "type": "danger",
126 "msg": "invalid container id or missing action"
127 }
128 return Response(content=json.dumps(res, indent=4), media_type="application/json")
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100129
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100130@app.post("/container/{container_id}/stats/update")
131async def post_container_update_stats(container_id : str):
132 global containerIds_to_update
133
134 # start update task for container if no task is running
135 if container_id not in containerIds_to_update:
136 asyncio.create_task(get_container_stats(container_id))
137 containerIds_to_update.append(container_id)
138
139 while True:
140 if redis_client.exists(container_id + '_stats'):
141 break
142 await asyncio.sleep(1.5)
143
144 stats = json.loads(redis_client.get(container_id + '_stats'))
145 return Response(content=json.dumps(stats, indent=4), media_type="application/json")
146
147
148
149
150class DockerUtils:
151 def __init__(self, docker_client):
152 self.docker_client = docker_client
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100153
154 # api call: container_post - post_action: stop
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100155 async def container_post__stop(self, container_id, request_json):
156 for container in (await self.docker_client.containers.list()):
157 if container._id == container_id:
158 await container.stop()
159 res = {
160 'type': 'success',
161 'msg': 'command completed successfully'
162 }
163 return Response(content=json.dumps(res, indent=4), media_type="application/json")
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100164
165 # api call: container_post - post_action: start
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100166 async def container_post__start(self, container_id, request_json):
167 for container in (await self.docker_client.containers.list()):
168 if container._id == container_id:
169 await container.start()
170 res = {
171 'type': 'success',
172 'msg': 'command completed successfully'
173 }
174 return Response(content=json.dumps(res, indent=4), media_type="application/json")
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100175
176
177 # api call: container_post - post_action: restart
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100178 async def container_post__restart(self, container_id, request_json):
179 for container in (await self.docker_client.containers.list()):
180 if container._id == container_id:
181 await container.restart()
182 res = {
183 'type': 'success',
184 'msg': 'command completed successfully'
185 }
186 return Response(content=json.dumps(res, indent=4), media_type="application/json")
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100187
188
189 # api call: container_post - post_action: top
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100190 async def container_post__top(self, container_id, request_json):
191 for container in (await self.docker_client.containers.list()):
192 if container._id == container_id:
193 ps_exec = await container.exec("ps")
194 async with ps_exec.start(detach=False) as stream:
195 ps_return = await stream.read_out()
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100196
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100197 exec_details = await ps_exec.inspect()
198 if exec_details["ExitCode"] == None or exec_details["ExitCode"] == 0:
199 res = {
200 'type': 'success',
201 'msg': ps_return.data.decode('utf-8')
202 }
203 return Response(content=json.dumps(res, indent=4), media_type="application/json")
204 else:
205 res = {
206 'type': 'danger',
207 'msg': ''
208 }
209 return Response(content=json.dumps(res, indent=4), media_type="application/json")
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100210
211
212 # api call: container_post - post_action: exec - cmd: mailq - task: delete
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100213 async def container_post__exec__mailq__delete(self, container_id, request_json):
214 if 'items' in request_json:
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100215 r = re.compile("^[0-9a-fA-F]+$")
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100216 filtered_qids = filter(r.match, request_json['items'])
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100217 if filtered_qids:
218 flagged_qids = ['-d %s' % i for i in filtered_qids]
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100219 sanitized_string = str(' '.join(flagged_qids))
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100220
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100221 for container in (await self.docker_client.containers.list()):
222 if container._id == container_id:
223 postsuper_r_exec = await container.exec(["/bin/bash", "-c", "/usr/sbin/postsuper " + sanitized_string])
224 return await exec_run_handler('generic', postsuper_r_exec)
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100225
226 # api call: container_post - post_action: exec - cmd: mailq - task: hold
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100227 async def container_post__exec__mailq__hold(self, container_id, request_json):
228 if 'items' in request_json:
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100229 r = re.compile("^[0-9a-fA-F]+$")
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100230 filtered_qids = filter(r.match, request_json['items'])
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100231 if filtered_qids:
232 flagged_qids = ['-h %s' % i for i in filtered_qids]
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100233 sanitized_string = str(' '.join(flagged_qids))
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100234
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100235 for container in (await self.docker_client.containers.list()):
236 if container._id == container_id:
237 postsuper_r_exec = await container.exec(["/bin/bash", "-c", "/usr/sbin/postsuper " + sanitized_string])
238 return await exec_run_handler('generic', postsuper_r_exec)
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100239
240 # api call: container_post - post_action: exec - cmd: mailq - task: cat
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100241 async def container_post__exec__mailq__cat(self, container_id, request_json):
242 if 'items' in request_json:
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100243 r = re.compile("^[0-9a-fA-F]+$")
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100244 filtered_qids = filter(r.match, request_json['items'])
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100245 if filtered_qids:
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100246 sanitized_string = str(' '.join(filtered_qids))
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100247
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100248 for container in (await self.docker_client.containers.list()):
249 if container._id == container_id:
250 postcat_exec = await container.exec(["/bin/bash", "-c", "/usr/sbin/postcat -q " + sanitized_string], user='postfix')
251 return await exec_run_handler('utf8_text_only', postcat_exec)
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100252
253 # api call: container_post - post_action: exec - cmd: mailq - task: unhold
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100254 async def container_post__exec__mailq__unhold(self, container_id, request_json):
255 if 'items' in request_json:
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100256 r = re.compile("^[0-9a-fA-F]+$")
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100257 filtered_qids = filter(r.match, request_json['items'])
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100258 if filtered_qids:
259 flagged_qids = ['-H %s' % i for i in filtered_qids]
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100260 sanitized_string = str(' '.join(flagged_qids))
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100261
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100262 for container in (await self.docker_client.containers.list()):
263 if container._id == container_id:
264 postsuper_r_exec = await container.exec(["/bin/bash", "-c", "/usr/sbin/postsuper " + sanitized_string])
265 return await exec_run_handler('generic', postsuper_r_exec)
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100266
267
268 # api call: container_post - post_action: exec - cmd: mailq - task: deliver
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100269 async def container_post__exec__mailq__deliver(self, container_id, request_json):
270 if 'items' in request_json:
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100271 r = re.compile("^[0-9a-fA-F]+$")
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100272 filtered_qids = filter(r.match, request_json['items'])
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100273 if filtered_qids:
274 flagged_qids = ['-i %s' % i for i in filtered_qids]
275
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100276 for container in (await self.docker_client.containers.list()):
277 if container._id == container_id:
278 for i in flagged_qids:
279 postsuper_r_exec = await container.exec(["/bin/bash", "-c", "/usr/sbin/postqueue " + i], user='postfix')
280 async with postsuper_r_exec.start(detach=False) as stream:
281 postsuper_r_return = await stream.read_out()
282 # todo: check each exit code
283 res = {
284 'type': 'success',
285 'msg': 'Scheduled immediate delivery'
286 }
287 return Response(content=json.dumps(res, indent=4), media_type="application/json")
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100288
289
290 # api call: container_post - post_action: exec - cmd: mailq - task: list
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100291 async def container_post__exec__mailq__list(self, container_id, request_json):
292 for container in (await self.docker_client.containers.list()):
293 if container._id == container_id:
294 mailq_exec = await container.exec(["/usr/sbin/postqueue", "-j"], user='postfix')
295 return await exec_run_handler('utf8_text_only', mailq_exec)
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100296
297
298 # api call: container_post - post_action: exec - cmd: mailq - task: flush
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100299 async def container_post__exec__mailq__flush(self, container_id, request_json):
300 for container in (await self.docker_client.containers.list()):
301 if container._id == container_id:
302 postsuper_r_exec = await container.exec(["/usr/sbin/postqueue", "-f"], user='postfix')
303 return await exec_run_handler('generic', postsuper_r_exec)
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100304
305
306 # api call: container_post - post_action: exec - cmd: mailq - task: super_delete
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100307 async def container_post__exec__mailq__super_delete(self, container_id, request_json):
308 for container in (await self.docker_client.containers.list()):
309 if container._id == container_id:
310 postsuper_r_exec = await container.exec(["/usr/sbin/postsuper", "-d", "ALL"])
311 return await exec_run_handler('generic', postsuper_r_exec)
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100312
313
314 # api call: container_post - post_action: exec - cmd: system - task: fts_rescan
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100315 async def container_post__exec__system__fts_rescan(self, container_id, request_json):
316 if 'username' in request_json:
317 for container in (await self.docker_client.containers.list()):
318 if container._id == container_id:
319 rescan_exec = await container.exec(["/bin/bash", "-c", "/usr/bin/doveadm fts rescan -u '" + request_json['username'].replace("'", "'\\''") + "'"], user='vmail')
320 async with rescan_exec.start(detach=False) as stream:
321 rescan_return = await stream.read_out()
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100322
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100323 exec_details = await rescan_exec.inspect()
324 if exec_details["ExitCode"] == None or exec_details["ExitCode"] == 0:
325 res = {
326 'type': 'success',
327 'msg': 'fts_rescan: rescan triggered'
328 }
329 return Response(content=json.dumps(res, indent=4), media_type="application/json")
330 else:
331 res = {
332 'type': 'warning',
333 'msg': 'fts_rescan error'
334 }
335 return Response(content=json.dumps(res, indent=4), media_type="application/json")
336
337 if 'all' in request_json:
338 for container in (await self.docker_client.containers.list()):
339 if container._id == container_id:
340 rescan_exec = await container.exec(["/bin/bash", "-c", "/usr/bin/doveadm fts rescan -A"], user='vmail')
341 async with rescan_exec.start(detach=False) as stream:
342 rescan_return = await stream.read_out()
343
344 exec_details = await rescan_exec.inspect()
345 if exec_details["ExitCode"] == None or exec_details["ExitCode"] == 0:
346 res = {
347 'type': 'success',
348 'msg': 'fts_rescan: rescan triggered'
349 }
350 return Response(content=json.dumps(res, indent=4), media_type="application/json")
351 else:
352 res = {
353 'type': 'warning',
354 'msg': 'fts_rescan error'
355 }
356 return Response(content=json.dumps(res, indent=4), media_type="application/json")
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100357
358
359 # api call: container_post - post_action: exec - cmd: system - task: df
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100360 async def container_post__exec__system__df(self, container_id, request_json):
361 if 'dir' in request_json:
362 for container in (await self.docker_client.containers.list()):
363 if container._id == container_id:
364 df_exec = await container.exec(["/bin/bash", "-c", "/bin/df -H '" + request_json['dir'].replace("'", "'\\''") + "' | /usr/bin/tail -n1 | /usr/bin/tr -s [:blank:] | /usr/bin/tr ' ' ','"], user='nobody')
365 async with df_exec.start(detach=False) as stream:
366 df_return = await stream.read_out()
367
368 print(df_return)
369 print(await df_exec.inspect())
370 exec_details = await df_exec.inspect()
371 if exec_details["ExitCode"] == None or exec_details["ExitCode"] == 0:
372 return df_return.data.decode('utf-8').rstrip()
373 else:
374 return "0,0,0,0,0,0"
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100375
376
377 # api call: container_post - post_action: exec - cmd: system - task: mysql_upgrade
Matthias Andreas Benkardf3d740c2022-12-28 21:27:39 +0100378 async def container_post__exec__system__mysql_upgrade(self, container_id, request_json):
379 return Response(content=json.dumps(dict(type='success', msg='mysql_upgrade: not touching fake MySQL', text=''), indent=4), media_type="application/json")
380
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100381
382 # api call: container_post - post_action: exec - cmd: system - task: mysql_tzinfo_to_sql
Matthias Andreas Benkardf3d740c2022-12-28 21:27:39 +0100383 async def container_post__exec__system__mysql_tzinfo_to_sql(self, container_id, request_json):
384 return Response(content=json.dumps(dict(type='success', msg='mysql_tzinfo_to_sql: not touching fake MySQL', text=''), indent=4), media_type="application/json")
385
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100386
387 # api call: container_post - post_action: exec - cmd: reload - task: dovecot
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100388 async def container_post__exec__reload__dovecot(self, container_id, request_json):
389 for container in (await self.docker_client.containers.list()):
390 if container._id == container_id:
391 reload_exec = await container.exec(["/bin/bash", "-c", "/usr/sbin/dovecot reload"])
392 return await exec_run_handler('generic', reload_exec)
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100393
394
395 # api call: container_post - post_action: exec - cmd: reload - task: postfix
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100396 async def container_post__exec__reload__postfix(self, container_id, request_json):
397 for container in (await self.docker_client.containers.list()):
398 if container._id == container_id:
399 reload_exec = await container.exec(["/bin/bash", "-c", "/usr/sbin/postfix reload"])
400 return await exec_run_handler('generic', reload_exec)
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100401
402
403 # api call: container_post - post_action: exec - cmd: reload - task: nginx
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100404 async def container_post__exec__reload__nginx(self, container_id, request_json):
405 for container in (await self.docker_client.containers.list()):
406 if container._id == container_id:
407 reload_exec = await container.exec(["/bin/sh", "-c", "/usr/sbin/nginx -s reload"])
408 return await exec_run_handler('generic', reload_exec)
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100409
410
411 # api call: container_post - post_action: exec - cmd: sieve - task: list
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100412 async def container_post__exec__sieve__list(self, container_id, request_json):
413 if 'username' in request_json:
414 for container in (await self.docker_client.containers.list()):
415 if container._id == container_id:
416 sieve_exec = await container.exec(["/bin/bash", "-c", "/usr/bin/doveadm sieve list -u '" + request_json['username'].replace("'", "'\\''") + "'"])
417 return await exec_run_handler('utf8_text_only', sieve_exec)
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100418
419
420 # api call: container_post - post_action: exec - cmd: sieve - task: print
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100421 async def container_post__exec__sieve__print(self, container_id, request_json):
422 if 'username' in request_json and 'script_name' in request_json:
423 for container in (await self.docker_client.containers.list()):
424 if container._id == container_id:
425 cmd = ["/bin/bash", "-c", "/usr/bin/doveadm sieve get -u '" + request_json['username'].replace("'", "'\\''") + "' '" + request_json['script_name'].replace("'", "'\\''") + "'"]
426 sieve_exec = await container.exec(cmd)
427 return await exec_run_handler('utf8_text_only', sieve_exec)
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100428
429
430 # api call: container_post - post_action: exec - cmd: maildir - task: cleanup
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100431 async def container_post__exec__maildir__cleanup(self, container_id, request_json):
432 if 'maildir' in request_json:
433 for container in (await self.docker_client.containers.list()):
434 if container._id == container_id:
435 sane_name = re.sub(r'\W+', '', request_json['maildir'])
436 cmd = ["/bin/bash", "-c", "if [[ -d '/var/vmail/" + request_json['maildir'].replace("'", "'\\''") + "' ]]; then /bin/mv '/var/vmail/" + request_json['maildir'].replace("'", "'\\''") + "' '/var/vmail/_garbage/" + str(int(time.time())) + "_" + sane_name + "'; fi"]
437 maildir_cleanup_exec = await container.exec(cmd, user='vmail')
438 return await exec_run_handler('generic', maildir_cleanup_exec)
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100439
440 # api call: container_post - post_action: exec - cmd: rspamd - task: worker_password
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100441 async def container_post__exec__rspamd__worker_password(self, container_id, request_json):
442 if 'raw' in request_json:
443 for container in (await self.docker_client.containers.list()):
444 if container._id == container_id:
445
446 cmd = "./set_worker_password.sh '" + request_json['raw'].replace("'", "'\\''") + "' 2> /dev/null"
447 rspamd_password_exec = await container.exec(cmd, user='_rspamd')
448 async with rspamd_password_exec.start(detach=False) as stream:
449 rspamd_password_return = await stream.read_out()
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100450
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100451 matched = False
452 if "OK" in rspamd_password_return.data.decode('utf-8'):
453 matched = True
454 await container.restart()
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100455
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100456 if matched:
457 res = {
458 'type': 'success',
459 'msg': 'command completed successfully'
460 }
461 return Response(content=json.dumps(res, indent=4), media_type="application/json")
462 else:
463 res = {
464 'type': 'danger',
465 'msg': 'command did not complete'
466 }
467 return Response(content=json.dumps(res, indent=4), media_type="application/json")
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100468
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100469
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100470
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100471async def exec_run_handler(type, exec_obj):
472 async with exec_obj.start(detach=False) as stream:
473 exec_return = await stream.read_out()
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100474
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100475 if exec_return == None:
476 exec_return = ""
477 else:
478 exec_return = exec_return.data.decode('utf-8')
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100479
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100480 if type == 'generic':
481 exec_details = await exec_obj.inspect()
482 if exec_details["ExitCode"] == None or exec_details["ExitCode"] == 0:
483 res = {
484 "type": "success",
485 "msg": "command completed successfully"
486 }
487 return Response(content=json.dumps(res, indent=4), media_type="application/json")
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100488 else:
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100489 res = {
490 "type": "success",
491 "msg": "'command failed: " + exec_return
492 }
493 return Response(content=json.dumps(res, indent=4), media_type="application/json")
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100494 if type == 'utf8_text_only':
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100495 return Response(content=exec_return, media_type="text/plain")
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100496
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100497async def get_host_stats(wait=5):
498 global host_stats_isUpdating
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100499
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100500 try:
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100501 system_time = datetime.now()
502 host_stats = {
503 "cpu": {
504 "cores": psutil.cpu_count(),
505 "usage": psutil.cpu_percent()
506 },
507 "memory": {
508 "total": psutil.virtual_memory().total,
509 "usage": psutil.virtual_memory().percent,
510 "swap": psutil.swap_memory()
511 },
512 "uptime": time.time() - psutil.boot_time(),
513 "system_time": system_time.strftime("%d.%m.%Y %H:%M:%S")
514 }
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100515
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100516 redis_client.set('host_stats', json.dumps(host_stats), ex=10)
517 except Exception as e:
518 res = {
519 "type": "danger",
520 "msg": str(e)
521 }
522 print(json.dumps(res, indent=4))
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100523
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100524 await asyncio.sleep(wait)
525 host_stats_isUpdating = False
526
527
528async def get_container_stats(container_id, wait=5, stop=False):
529 global containerIds_to_update
530
531 if container_id and container_id.isalnum():
532 try:
533 for container in (await async_docker_client.containers.list()):
534 if container._id == container_id:
535 res = await container.stats(stream=False)
536
537 if redis_client.exists(container_id + '_stats'):
538 stats = json.loads(redis_client.get(container_id + '_stats'))
539 else:
540 stats = []
541 stats.append(res[0])
542 if len(stats) > 3:
543 del stats[0]
544 redis_client.set(container_id + '_stats', json.dumps(stats), ex=60)
545 except Exception as e:
546 res = {
547 "type": "danger",
548 "msg": str(e)
549 }
550 print(json.dumps(res, indent=4))
551 else:
552 res = {
553 "type": "danger",
554 "msg": "no or invalid id defined"
555 }
556 print(json.dumps(res, indent=4))
557
558 await asyncio.sleep(wait)
559 if stop == True:
560 # update task was called second time, stop
561 containerIds_to_update.remove(container_id)
562 else:
563 # call update task a second time
564 await get_container_stats(container_id, wait=0, stop=True)
565
566
567if os.environ['REDIS_SLAVEOF_IP'] != "":
568 redis_client = redis.Redis(host=os.environ['REDIS_SLAVEOF_IP'], port=os.environ['REDIS_SLAVEOF_PORT'], db=0)
569else:
570 redis_client = redis.Redis(host='redis-mailcow', port=6379, db=0)
571
572async_docker_client = aiodocker.Docker(url='unix:///var/run/docker.sock')