dockerapi.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623
  1. from fastapi import FastAPI, Response, Request
  2. import aiodocker
  3. import psutil
  4. import sys
  5. import re
  6. import time
  7. import os
  8. import json
  9. import asyncio
  10. import redis
  11. from datetime import datetime
  12. containerIds_to_update = []
  13. host_stats_isUpdating = False
  14. app = FastAPI()
  15. @app.get("/host/stats")
  16. async def get_host_update_stats():
  17. global host_stats_isUpdating
  18. if host_stats_isUpdating == False:
  19. print("start host stats task")
  20. asyncio.create_task(get_host_stats())
  21. host_stats_isUpdating = True
  22. while True:
  23. if redis_client.exists('host_stats'):
  24. break
  25. print("wait for host_stats results")
  26. await asyncio.sleep(1.5)
  27. print("host stats pulled")
  28. stats = json.loads(redis_client.get('host_stats'))
  29. return Response(content=json.dumps(stats, indent=4), media_type="application/json")
  30. @app.get("/containers/{container_id}/json")
  31. async def get_container(container_id : str):
  32. if container_id and container_id.isalnum():
  33. try:
  34. for container in (await async_docker_client.containers.list()):
  35. if container._id == container_id:
  36. container_info = await container.show()
  37. return Response(content=json.dumps(container_info, indent=4), media_type="application/json")
  38. res = {
  39. "type": "danger",
  40. "msg": "no container found"
  41. }
  42. return Response(content=json.dumps(res, indent=4), media_type="application/json")
  43. except Exception as e:
  44. res = {
  45. "type": "danger",
  46. "msg": str(e)
  47. }
  48. return Response(content=json.dumps(res, indent=4), media_type="application/json")
  49. else:
  50. res = {
  51. "type": "danger",
  52. "msg": "no or invalid id defined"
  53. }
  54. return Response(content=json.dumps(res, indent=4), media_type="application/json")
  55. @app.get("/containers/json")
  56. async def get_containers():
  57. containers = {}
  58. try:
  59. for container in (await async_docker_client.containers.list()):
  60. container_info = await container.show()
  61. containers.update({container_info['Id']: container_info})
  62. return Response(content=json.dumps(containers, indent=4), media_type="application/json")
  63. except Exception as e:
  64. res = {
  65. "type": "danger",
  66. "msg": str(e)
  67. }
  68. return Response(content=json.dumps(res, indent=4), media_type="application/json")
  69. @app.post("/containers/{container_id}/{post_action}")
  70. async def post_containers(container_id : str, post_action : str, request: Request):
  71. try :
  72. request_json = await request.json()
  73. except Exception as err:
  74. request_json = {}
  75. if container_id and container_id.isalnum() and post_action:
  76. try:
  77. """Dispatch container_post api call"""
  78. if post_action == 'exec':
  79. if not request_json or not 'cmd' in request_json:
  80. res = {
  81. "type": "danger",
  82. "msg": "cmd is missing"
  83. }
  84. return Response(content=json.dumps(res, indent=4), media_type="application/json")
  85. if not request_json or not 'task' in request_json:
  86. res = {
  87. "type": "danger",
  88. "msg": "task is missing"
  89. }
  90. return Response(content=json.dumps(res, indent=4), media_type="application/json")
  91. api_call_method_name = '__'.join(['container_post', str(post_action), str(request_json['cmd']), str(request_json['task']) ])
  92. else:
  93. api_call_method_name = '__'.join(['container_post', str(post_action) ])
  94. docker_utils = DockerUtils(async_docker_client)
  95. 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"))
  96. print("api call: %s, container_id: %s" % (api_call_method_name, container_id))
  97. return await api_call_method(container_id, request_json)
  98. except Exception as e:
  99. print("error - container_post: %s" % str(e))
  100. res = {
  101. "type": "danger",
  102. "msg": str(e)
  103. }
  104. return Response(content=json.dumps(res, indent=4), media_type="application/json")
  105. else:
  106. res = {
  107. "type": "danger",
  108. "msg": "invalid container id or missing action"
  109. }
  110. return Response(content=json.dumps(res, indent=4), media_type="application/json")
  111. @app.post("/container/{container_id}/stats/update")
  112. async def post_container_update_stats(container_id : str):
  113. global containerIds_to_update
  114. # start update task for container if no task is running
  115. if container_id not in containerIds_to_update:
  116. asyncio.create_task(get_container_stats(container_id))
  117. containerIds_to_update.append(container_id)
  118. while True:
  119. if redis_client.exists(container_id + '_stats'):
  120. break
  121. await asyncio.sleep(1.5)
  122. stats = json.loads(redis_client.get(container_id + '_stats'))
  123. return Response(content=json.dumps(stats, indent=4), media_type="application/json")
  124. class DockerUtils:
  125. def __init__(self, docker_client):
  126. self.docker_client = docker_client
  127. # api call: container_post - post_action: stop
  128. async def container_post__stop(self, container_id, request_json):
  129. for container in (await self.docker_client.containers.list()):
  130. if container._id == container_id:
  131. await container.stop()
  132. res = {
  133. 'type': 'success',
  134. 'msg': 'command completed successfully'
  135. }
  136. return Response(content=json.dumps(res, indent=4), media_type="application/json")
  137. # api call: container_post - post_action: start
  138. async def container_post__start(self, container_id, request_json):
  139. for container in (await self.docker_client.containers.list()):
  140. if container._id == container_id:
  141. await container.start()
  142. res = {
  143. 'type': 'success',
  144. 'msg': 'command completed successfully'
  145. }
  146. return Response(content=json.dumps(res, indent=4), media_type="application/json")
  147. # api call: container_post - post_action: restart
  148. async def container_post__restart(self, container_id, request_json):
  149. for container in (await self.docker_client.containers.list()):
  150. if container._id == container_id:
  151. await container.restart()
  152. res = {
  153. 'type': 'success',
  154. 'msg': 'command completed successfully'
  155. }
  156. return Response(content=json.dumps(res, indent=4), media_type="application/json")
  157. # api call: container_post - post_action: top
  158. async def container_post__top(self, container_id, request_json):
  159. for container in (await self.docker_client.containers.list()):
  160. if container._id == container_id:
  161. ps_exec = await container.exec("ps")
  162. async with ps_exec.start(detach=False) as stream:
  163. ps_return = await stream.read_out()
  164. exec_details = await ps_exec.inspect()
  165. if exec_details["ExitCode"] == None or exec_details["ExitCode"] == 0:
  166. res = {
  167. 'type': 'success',
  168. 'msg': ps_return.data.decode('utf-8')
  169. }
  170. return Response(content=json.dumps(res, indent=4), media_type="application/json")
  171. else:
  172. res = {
  173. 'type': 'danger',
  174. 'msg': ''
  175. }
  176. return Response(content=json.dumps(res, indent=4), media_type="application/json")
  177. # api call: container_post - post_action: exec - cmd: mailq - task: delete
  178. async def container_post__exec__mailq__delete(self, container_id, request_json):
  179. if 'items' in request_json:
  180. r = re.compile("^[0-9a-fA-F]+$")
  181. filtered_qids = filter(r.match, request_json['items'])
  182. if filtered_qids:
  183. flagged_qids = ['-d %s' % i for i in filtered_qids]
  184. sanitized_string = str(' '.join(flagged_qids))
  185. for container in (await self.docker_client.containers.list()):
  186. if container._id == container_id:
  187. postsuper_r_exec = await container.exec(["/bin/bash", "-c", "/usr/sbin/postsuper " + sanitized_string])
  188. return await exec_run_handler('generic', postsuper_r_exec)
  189. # api call: container_post - post_action: exec - cmd: mailq - task: hold
  190. async def container_post__exec__mailq__hold(self, container_id, request_json):
  191. if 'items' in request_json:
  192. r = re.compile("^[0-9a-fA-F]+$")
  193. filtered_qids = filter(r.match, request_json['items'])
  194. if filtered_qids:
  195. flagged_qids = ['-h %s' % i for i in filtered_qids]
  196. sanitized_string = str(' '.join(flagged_qids))
  197. for container in (await self.docker_client.containers.list()):
  198. if container._id == container_id:
  199. postsuper_r_exec = await container.exec(["/bin/bash", "-c", "/usr/sbin/postsuper " + sanitized_string])
  200. return await exec_run_handler('generic', postsuper_r_exec)
  201. # api call: container_post - post_action: exec - cmd: mailq - task: cat
  202. async def container_post__exec__mailq__cat(self, container_id, request_json):
  203. if 'items' in request_json:
  204. r = re.compile("^[0-9a-fA-F]+$")
  205. filtered_qids = filter(r.match, request_json['items'])
  206. if filtered_qids:
  207. sanitized_string = str(' '.join(filtered_qids))
  208. for container in (await self.docker_client.containers.list()):
  209. if container._id == container_id:
  210. postcat_exec = await container.exec(["/bin/bash", "-c", "/usr/sbin/postcat -q " + sanitized_string], user='postfix')
  211. return await exec_run_handler('utf8_text_only', postcat_exec)
  212. # api call: container_post - post_action: exec - cmd: mailq - task: unhold
  213. async def container_post__exec__mailq__unhold(self, container_id, request_json):
  214. if 'items' in request_json:
  215. r = re.compile("^[0-9a-fA-F]+$")
  216. filtered_qids = filter(r.match, request_json['items'])
  217. if filtered_qids:
  218. flagged_qids = ['-H %s' % i for i in filtered_qids]
  219. sanitized_string = str(' '.join(flagged_qids))
  220. for container in (await self.docker_client.containers.list()):
  221. if container._id == container_id:
  222. postsuper_r_exec = await container.exec(["/bin/bash", "-c", "/usr/sbin/postsuper " + sanitized_string])
  223. return await exec_run_handler('generic', postsuper_r_exec)
  224. # api call: container_post - post_action: exec - cmd: mailq - task: deliver
  225. async def container_post__exec__mailq__deliver(self, container_id, request_json):
  226. if 'items' in request_json:
  227. r = re.compile("^[0-9a-fA-F]+$")
  228. filtered_qids = filter(r.match, request_json['items'])
  229. if filtered_qids:
  230. flagged_qids = ['-i %s' % i for i in filtered_qids]
  231. for container in (await self.docker_client.containers.list()):
  232. if container._id == container_id:
  233. for i in flagged_qids:
  234. postsuper_r_exec = await container.exec(["/bin/bash", "-c", "/usr/sbin/postqueue " + i], user='postfix')
  235. async with postsuper_r_exec.start(detach=False) as stream:
  236. postsuper_r_return = await stream.read_out()
  237. # todo: check each exit code
  238. res = {
  239. 'type': 'success',
  240. 'msg': 'Scheduled immediate delivery'
  241. }
  242. return Response(content=json.dumps(res, indent=4), media_type="application/json")
  243. # api call: container_post - post_action: exec - cmd: mailq - task: list
  244. async def container_post__exec__mailq__list(self, container_id, request_json):
  245. for container in (await self.docker_client.containers.list()):
  246. if container._id == container_id:
  247. mailq_exec = await container.exec(["/usr/sbin/postqueue", "-j"], user='postfix')
  248. return await exec_run_handler('utf8_text_only', mailq_exec)
  249. # api call: container_post - post_action: exec - cmd: mailq - task: flush
  250. async def container_post__exec__mailq__flush(self, container_id, request_json):
  251. for container in (await self.docker_client.containers.list()):
  252. if container._id == container_id:
  253. postsuper_r_exec = await container.exec(["/usr/sbin/postqueue", "-f"], user='postfix')
  254. return await exec_run_handler('generic', postsuper_r_exec)
  255. # api call: container_post - post_action: exec - cmd: mailq - task: super_delete
  256. async def container_post__exec__mailq__super_delete(self, container_id, request_json):
  257. for container in (await self.docker_client.containers.list()):
  258. if container._id == container_id:
  259. postsuper_r_exec = await container.exec(["/usr/sbin/postsuper", "-d", "ALL"])
  260. return await exec_run_handler('generic', postsuper_r_exec)
  261. # api call: container_post - post_action: exec - cmd: system - task: fts_rescan
  262. async def container_post__exec__system__fts_rescan(self, container_id, request_json):
  263. if 'username' in request_json:
  264. for container in (await self.docker_client.containers.list()):
  265. if container._id == container_id:
  266. rescan_exec = await container.exec(["/bin/bash", "-c", "/usr/bin/doveadm fts rescan -u '" + request_json['username'].replace("'", "'\\''") + "'"], user='vmail')
  267. async with rescan_exec.start(detach=False) as stream:
  268. rescan_return = await stream.read_out()
  269. exec_details = await rescan_exec.inspect()
  270. if exec_details["ExitCode"] == None or exec_details["ExitCode"] == 0:
  271. res = {
  272. 'type': 'success',
  273. 'msg': 'fts_rescan: rescan triggered'
  274. }
  275. return Response(content=json.dumps(res, indent=4), media_type="application/json")
  276. else:
  277. res = {
  278. 'type': 'warning',
  279. 'msg': 'fts_rescan error'
  280. }
  281. return Response(content=json.dumps(res, indent=4), media_type="application/json")
  282. if 'all' in request_json:
  283. for container in (await self.docker_client.containers.list()):
  284. if container._id == container_id:
  285. rescan_exec = await container.exec(["/bin/bash", "-c", "/usr/bin/doveadm fts rescan -A"], user='vmail')
  286. async with rescan_exec.start(detach=False) as stream:
  287. rescan_return = await stream.read_out()
  288. exec_details = await rescan_exec.inspect()
  289. if exec_details["ExitCode"] == None or exec_details["ExitCode"] == 0:
  290. res = {
  291. 'type': 'success',
  292. 'msg': 'fts_rescan: rescan triggered'
  293. }
  294. return Response(content=json.dumps(res, indent=4), media_type="application/json")
  295. else:
  296. res = {
  297. 'type': 'warning',
  298. 'msg': 'fts_rescan error'
  299. }
  300. return Response(content=json.dumps(res, indent=4), media_type="application/json")
  301. # api call: container_post - post_action: exec - cmd: system - task: df
  302. async def container_post__exec__system__df(self, container_id, request_json):
  303. if 'dir' in request_json:
  304. for container in (await self.docker_client.containers.list()):
  305. if container._id == container_id:
  306. 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')
  307. async with df_exec.start(detach=False) as stream:
  308. df_return = await stream.read_out()
  309. print(df_return)
  310. print(await df_exec.inspect())
  311. exec_details = await df_exec.inspect()
  312. if exec_details["ExitCode"] == None or exec_details["ExitCode"] == 0:
  313. return df_return.data.decode('utf-8').rstrip()
  314. else:
  315. return "0,0,0,0,0,0"
  316. # api call: container_post - post_action: exec - cmd: system - task: mysql_upgrade
  317. async def container_post__exec__system__mysql_upgrade(self, container_id, request_json):
  318. for container in (await self.docker_client.containers.list()):
  319. if container._id == container_id:
  320. sql_exec = await container.exec(["/bin/bash", "-c", "/usr/bin/mysql_upgrade -uroot -p'" + os.environ['DBROOT'].replace("'", "'\\''") + "'\n"], user='mysql')
  321. async with sql_exec.start(detach=False) as stream:
  322. sql_return = await stream.read_out()
  323. exec_details = await sql_exec.inspect()
  324. if exec_details["ExitCode"] == None or exec_details["ExitCode"] == 0:
  325. matched = False
  326. for line in sql_return.data.decode('utf-8').split("\n"):
  327. if 'is already upgraded to' in line:
  328. matched = True
  329. if matched:
  330. res = {
  331. 'type': 'success',
  332. 'msg': 'mysql_upgrade: already upgraded',
  333. 'text': sql_return.data.decode('utf-8')
  334. }
  335. return Response(content=json.dumps(res, indent=4), media_type="application/json")
  336. else:
  337. await container.restart()
  338. res = {
  339. 'type': 'warning',
  340. 'msg': 'mysql_upgrade: upgrade was applied',
  341. 'text': sql_return.data.decode('utf-8')
  342. }
  343. return Response(content=json.dumps(res, indent=4), media_type="application/json")
  344. else:
  345. res = {
  346. 'type': 'error',
  347. 'msg': 'mysql_upgrade: error running command',
  348. 'text': sql_return.data.decode('utf-8')
  349. }
  350. return Response(content=json.dumps(res, indent=4), media_type="application/json")
  351. # api call: container_post - post_action: exec - cmd: system - task: mysql_tzinfo_to_sql
  352. async def container_post__exec__system__mysql_tzinfo_to_sql(self, container_id, request_json):
  353. for container in (await self.docker_client.containers.list()):
  354. if container._id == container_id:
  355. sql_exec = await container.exec(["/bin/bash", "-c", "/usr/bin/mysql_tzinfo_to_sql /usr/share/zoneinfo | /bin/sed 's/Local time zone must be set--see zic manual page/FCTY/' | /usr/bin/mysql -uroot -p'" + os.environ['DBROOT'].replace("'", "'\\''") + "' mysql \n"], user='mysql')
  356. async with sql_exec.start(detach=False) as stream:
  357. sql_return = await stream.read_out()
  358. exec_details = await sql_exec.inspect()
  359. if exec_details["ExitCode"] == None or exec_details["ExitCode"] == 0:
  360. res = {
  361. 'type': 'info',
  362. 'msg': 'mysql_tzinfo_to_sql: command completed successfully',
  363. 'text': sql_return.data.decode('utf-8')
  364. }
  365. return Response(content=json.dumps(res, indent=4), media_type="application/json")
  366. else:
  367. res = {
  368. 'type': 'error',
  369. 'msg': 'mysql_tzinfo_to_sql: error running command',
  370. 'text': sql_return.data.decode('utf-8')
  371. }
  372. return Response(content=json.dumps(res, indent=4), media_type="application/json")
  373. # api call: container_post - post_action: exec - cmd: reload - task: dovecot
  374. async def container_post__exec__reload__dovecot(self, container_id, request_json):
  375. for container in (await self.docker_client.containers.list()):
  376. if container._id == container_id:
  377. reload_exec = await container.exec(["/bin/bash", "-c", "/usr/sbin/dovecot reload"])
  378. return await exec_run_handler('generic', reload_exec)
  379. # api call: container_post - post_action: exec - cmd: reload - task: postfix
  380. async def container_post__exec__reload__postfix(self, container_id, request_json):
  381. for container in (await self.docker_client.containers.list()):
  382. if container._id == container_id:
  383. reload_exec = await container.exec(["/bin/bash", "-c", "/usr/sbin/postfix reload"])
  384. return await exec_run_handler('generic', reload_exec)
  385. # api call: container_post - post_action: exec - cmd: reload - task: nginx
  386. async def container_post__exec__reload__nginx(self, container_id, request_json):
  387. for container in (await self.docker_client.containers.list()):
  388. if container._id == container_id:
  389. reload_exec = await container.exec(["/bin/sh", "-c", "/usr/sbin/nginx -s reload"])
  390. return await exec_run_handler('generic', reload_exec)
  391. # api call: container_post - post_action: exec - cmd: sieve - task: list
  392. async def container_post__exec__sieve__list(self, container_id, request_json):
  393. if 'username' in request_json:
  394. for container in (await self.docker_client.containers.list()):
  395. if container._id == container_id:
  396. sieve_exec = await container.exec(["/bin/bash", "-c", "/usr/bin/doveadm sieve list -u '" + request_json['username'].replace("'", "'\\''") + "'"])
  397. return await exec_run_handler('utf8_text_only', sieve_exec)
  398. # api call: container_post - post_action: exec - cmd: sieve - task: print
  399. async def container_post__exec__sieve__print(self, container_id, request_json):
  400. if 'username' in request_json and 'script_name' in request_json:
  401. for container in (await self.docker_client.containers.list()):
  402. if container._id == container_id:
  403. cmd = ["/bin/bash", "-c", "/usr/bin/doveadm sieve get -u '" + request_json['username'].replace("'", "'\\''") + "' '" + request_json['script_name'].replace("'", "'\\''") + "'"]
  404. sieve_exec = await container.exec(cmd)
  405. return await exec_run_handler('utf8_text_only', sieve_exec)
  406. # api call: container_post - post_action: exec - cmd: maildir - task: cleanup
  407. async def container_post__exec__maildir__cleanup(self, container_id, request_json):
  408. if 'maildir' in request_json:
  409. for container in (await self.docker_client.containers.list()):
  410. if container._id == container_id:
  411. sane_name = re.sub(r'\W+', '', request_json['maildir'])
  412. 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"]
  413. maildir_cleanup_exec = await container.exec(cmd, user='vmail')
  414. return await exec_run_handler('generic', maildir_cleanup_exec)
  415. # api call: container_post - post_action: exec - cmd: rspamd - task: worker_password
  416. async def container_post__exec__rspamd__worker_password(self, container_id, request_json):
  417. if 'raw' in request_json:
  418. for container in (await self.docker_client.containers.list()):
  419. if container._id == container_id:
  420. cmd = "./set_worker_password.sh '" + request_json['raw'].replace("'", "'\\''") + "' 2> /dev/null"
  421. rspamd_password_exec = await container.exec(cmd, user='_rspamd')
  422. async with rspamd_password_exec.start(detach=False) as stream:
  423. rspamd_password_return = await stream.read_out()
  424. matched = False
  425. if "OK" in rspamd_password_return.data.decode('utf-8'):
  426. matched = True
  427. await container.restart()
  428. if matched:
  429. res = {
  430. 'type': 'success',
  431. 'msg': 'command completed successfully'
  432. }
  433. return Response(content=json.dumps(res, indent=4), media_type="application/json")
  434. else:
  435. res = {
  436. 'type': 'danger',
  437. 'msg': 'command did not complete'
  438. }
  439. return Response(content=json.dumps(res, indent=4), media_type="application/json")
  440. async def exec_run_handler(type, exec_obj):
  441. async with exec_obj.start(detach=False) as stream:
  442. exec_return = await stream.read_out()
  443. if exec_return == None:
  444. exec_return = ""
  445. else:
  446. exec_return = exec_return.data.decode('utf-8')
  447. if type == 'generic':
  448. exec_details = await exec_obj.inspect()
  449. if exec_details["ExitCode"] == None or exec_details["ExitCode"] == 0:
  450. res = {
  451. "type": "success",
  452. "msg": "command completed successfully"
  453. }
  454. return Response(content=json.dumps(res, indent=4), media_type="application/json")
  455. else:
  456. res = {
  457. "type": "success",
  458. "msg": "'command failed: " + exec_return
  459. }
  460. return Response(content=json.dumps(res, indent=4), media_type="application/json")
  461. if type == 'utf8_text_only':
  462. return Response(content=exec_return, media_type="text/plain")
  463. async def get_host_stats(wait=5):
  464. global host_stats_isUpdating
  465. try:
  466. system_time = datetime.now()
  467. host_stats = {
  468. "cpu": {
  469. "cores": psutil.cpu_count(),
  470. "usage": psutil.cpu_percent()
  471. },
  472. "memory": {
  473. "total": psutil.virtual_memory().total,
  474. "usage": psutil.virtual_memory().percent,
  475. "swap": psutil.swap_memory()
  476. },
  477. "uptime": time.time() - psutil.boot_time(),
  478. "system_time": system_time.strftime("%d.%m.%Y %H:%M:%S")
  479. }
  480. redis_client.set('host_stats', json.dumps(host_stats), ex=10)
  481. except Exception as e:
  482. res = {
  483. "type": "danger",
  484. "msg": str(e)
  485. }
  486. print(json.dumps(res, indent=4))
  487. await asyncio.sleep(wait)
  488. host_stats_isUpdating = False
  489. async def get_container_stats(container_id, wait=5, stop=False):
  490. global containerIds_to_update
  491. if container_id and container_id.isalnum():
  492. try:
  493. for container in (await async_docker_client.containers.list()):
  494. if container._id == container_id:
  495. res = await container.stats(stream=False)
  496. if redis_client.exists(container_id + '_stats'):
  497. stats = json.loads(redis_client.get(container_id + '_stats'))
  498. else:
  499. stats = []
  500. stats.append(res[0])
  501. if len(stats) > 3:
  502. del stats[0]
  503. redis_client.set(container_id + '_stats', json.dumps(stats), ex=60)
  504. except Exception as e:
  505. res = {
  506. "type": "danger",
  507. "msg": str(e)
  508. }
  509. print(json.dumps(res, indent=4))
  510. else:
  511. res = {
  512. "type": "danger",
  513. "msg": "no or invalid id defined"
  514. }
  515. print(json.dumps(res, indent=4))
  516. await asyncio.sleep(wait)
  517. if stop == True:
  518. # update task was called second time, stop
  519. containerIds_to_update.remove(container_id)
  520. else:
  521. # call update task a second time
  522. await get_container_stats(container_id, wait=0, stop=True)
  523. if os.environ['REDIS_SLAVEOF_IP'] != "":
  524. redis_client = redis.Redis(host=os.environ['REDIS_SLAVEOF_IP'], port=os.environ['REDIS_SLAVEOF_PORT'], db=0)
  525. else:
  526. redis_client = redis.Redis(host='redis-mailcow', port=6379, db=0)
  527. async_docker_client = aiodocker.Docker(url='unix:///var/run/docker.sock')