public
nobgit
read
NobMail
Based on mailcow: dockerized
Languages
Repository composition by tracked source files.
PHP
49%
JavaScript
35%
HTML
9%
CSS
4%
Shell
2%
Python
1%
Lua
0%
Perl
0%
Ruby
0%
SCSS
0%
Create file
Wiki Documentation
Clone
https://nobgit.com/orgs/nobgit/nobmail.git
ssh://[email protected]:2222/orgs/nobgit/nobmail.git
Trace
data/Dockerfiles/dockerapi/modules/DockerApi.py
Trace helps you understand code history line by line. See who changed each line, when it changed, and which commit introduced it.
Author
Date
Commit
Line
Code
1
import psutil
2
import sys
3
import os
4
import re
5
import time
6
import json
7
import asyncio
8
import platform
9
from datetime import datetime
10
from fastapi import FastAPI, Response, Request
12
class DockerApi:
13
def __init__(self, redis_client, sync_docker_client, async_docker_client, logger):
14
self.redis_client = redis_client
15
self.sync_docker_client = sync_docker_client
16
self.async_docker_client = async_docker_client
17
self.logger = logger
19
self.host_stats_isUpdating = False
20
self.containerIds_to_update = []
22
# api call: container_post - post_action: stop
23
def container_post__stop(self, request_json, **kwargs):
24
if 'container_id' in kwargs:
25
filters = {"id": kwargs['container_id']}
26
elif 'container_name' in kwargs:
27
filters = {"name": kwargs['container_name']}
29
for container in self.sync_docker_client.containers.list(all=True, filters=filters):
30
container.stop()
32
res = { 'type': 'success', 'msg': 'command completed successfully'}
33
return Response(content=json.dumps(res, indent=4), media_type="application/json")
34
# api call: container_post - post_action: start
35
def container_post__start(self, request_json, **kwargs):
36
if 'container_id' in kwargs:
37
filters = {"id": kwargs['container_id']}
38
elif 'container_name' in kwargs:
39
filters = {"name": kwargs['container_name']}
41
for container in self.sync_docker_client.containers.list(all=True, filters=filters):
42
container.start()
44
res = { 'type': 'success', 'msg': 'command completed successfully'}
45
return Response(content=json.dumps(res, indent=4), media_type="application/json")
46
# api call: container_post - post_action: restart
47
def container_post__restart(self, request_json, **kwargs):
48
if 'container_id' in kwargs:
49
filters = {"id": kwargs['container_id']}
50
elif 'container_name' in kwargs:
51
filters = {"name": kwargs['container_name']}
53
for container in self.sync_docker_client.containers.list(all=True, filters=filters):
54
container.restart()
56
res = { 'type': 'success', 'msg': 'command completed successfully'}
57
return Response(content=json.dumps(res, indent=4), media_type="application/json")
58
# api call: container_post - post_action: top
59
def container_post__top(self, request_json, **kwargs):
60
if 'container_id' in kwargs:
61
filters = {"id": kwargs['container_id']}
62
elif 'container_name' in kwargs:
63
filters = {"name": kwargs['container_name']}
65
for container in self.sync_docker_client.containers.list(all=True, filters=filters):
66
res = { 'type': 'success', 'msg': container.top()}
67
return Response(content=json.dumps(res, indent=4), media_type="application/json")
68
# api call: container_post - post_action: stats
69
def container_post__stats(self, request_json, **kwargs):
70
if 'container_id' in kwargs:
71
filters = {"id": kwargs['container_id']}
72
elif 'container_name' in kwargs:
73
filters = {"name": kwargs['container_name']}
75
for container in self.sync_docker_client.containers.list(all=True, filters=filters):
76
for stat in container.stats(decode=True, stream=True):
77
res = { 'type': 'success', 'msg': stat}
78
return Response(content=json.dumps(res, indent=4), media_type="application/json")
79
# api call: container_post - post_action: exec - cmd: mailq - task: delete
80
def container_post__exec__mailq__delete(self, request_json, **kwargs):
81
if 'container_id' in kwargs:
82
filters = {"id": kwargs['container_id']}
83
elif 'container_name' in kwargs:
84
filters = {"name": kwargs['container_name']}
86
if 'items' in request_json:
87
r = re.compile("^[0-9a-fA-F]+$")
88
filtered_qids = filter(r.match, request_json['items'])
89
if filtered_qids:
90
flagged_qids = ['-d %s' % i for i in filtered_qids]
91
sanitized_string = str(' '.join(flagged_qids))
92
for container in self.sync_docker_client.containers.list(filters=filters):
93
postsuper_r = container.exec_run(["/bin/bash", "-c", "/usr/sbin/postsuper " + sanitized_string])
94
return self.exec_run_handler('generic', postsuper_r)
95
# api call: container_post - post_action: exec - cmd: mailq - task: hold
96
def container_post__exec__mailq__hold(self, request_json, **kwargs):
97
if 'container_id' in kwargs:
98
filters = {"id": kwargs['container_id']}
99
elif 'container_name' in kwargs:
100
filters = {"name": kwargs['container_name']}
102
if 'items' in request_json:
103
r = re.compile("^[0-9a-fA-F]+$")
104
filtered_qids = filter(r.match, request_json['items'])
105
if filtered_qids:
106
flagged_qids = ['-h %s' % i for i in filtered_qids]
107
sanitized_string = str(' '.join(flagged_qids))
108
for container in self.sync_docker_client.containers.list(filters=filters):
109
postsuper_r = container.exec_run(["/bin/bash", "-c", "/usr/sbin/postsuper " + sanitized_string])
110
return self.exec_run_handler('generic', postsuper_r)
111
# api call: container_post - post_action: exec - cmd: mailq - task: cat
112
def container_post__exec__mailq__cat(self, request_json, **kwargs):
113
if 'container_id' in kwargs:
114
filters = {"id": kwargs['container_id']}
115
elif 'container_name' in kwargs:
116
filters = {"name": kwargs['container_name']}
118
if 'items' in request_json:
119
r = re.compile("^[0-9a-fA-F]+$")
120
filtered_qids = filter(r.match, request_json['items'])
121
if filtered_qids:
122
sanitized_string = str(' '.join(filtered_qids))
124
for container in self.sync_docker_client.containers.list(filters=filters):
125
postcat_return = container.exec_run(["/bin/bash", "-c", "/usr/sbin/postcat -q " + sanitized_string], user='postfix')
126
if not postcat_return:
127
postcat_return = 'err: invalid'
128
return self.exec_run_handler('utf8_text_only', postcat_return)
129
# api call: container_post - post_action: exec - cmd: mailq - task: unhold
130
def container_post__exec__mailq__unhold(self, request_json, **kwargs):
131
if 'container_id' in kwargs:
132
filters = {"id": kwargs['container_id']}
133
elif 'container_name' in kwargs:
134
filters = {"name": kwargs['container_name']}
136
if 'items' in request_json:
137
r = re.compile("^[0-9a-fA-F]+$")
138
filtered_qids = filter(r.match, request_json['items'])
139
if filtered_qids:
140
flagged_qids = ['-H %s' % i for i in filtered_qids]
141
sanitized_string = str(' '.join(flagged_qids))
142
for container in self.sync_docker_client.containers.list(filters=filters):
143
postsuper_r = container.exec_run(["/bin/bash", "-c", "/usr/sbin/postsuper " + sanitized_string])
144
return self.exec_run_handler('generic', postsuper_r)
145
# api call: container_post - post_action: exec - cmd: mailq - task: deliver
146
def container_post__exec__mailq__deliver(self, request_json, **kwargs):
147
if 'container_id' in kwargs:
148
filters = {"id": kwargs['container_id']}
149
elif 'container_name' in kwargs:
150
filters = {"name": kwargs['container_name']}
152
if 'items' in request_json:
153
r = re.compile("^[0-9a-fA-F]+$")
154
filtered_qids = filter(r.match, request_json['items'])
155
if filtered_qids:
156
flagged_qids = ['-i %s' % i for i in filtered_qids]
157
for container in self.sync_docker_client.containers.list(filters=filters):
158
for i in flagged_qids:
159
postqueue_r = container.exec_run(["/bin/bash", "-c", "/usr/sbin/postqueue " + i], user='postfix')
160
# todo: check each exit code
161
res = { 'type': 'success', 'msg': 'Scheduled immediate delivery'}
162
return Response(content=json.dumps(res, indent=4), media_type="application/json")
163
# api call: container_post - post_action: exec - cmd: mailq - task: list
164
def container_post__exec__mailq__list(self, request_json, **kwargs):
165
if 'container_id' in kwargs:
166
filters = {"id": kwargs['container_id']}
167
elif 'container_name' in kwargs:
168
filters = {"name": kwargs['container_name']}
170
for container in self.sync_docker_client.containers.list(filters=filters):
171
mailq_return = container.exec_run(["/usr/sbin/postqueue", "-j"], user='postfix')
172
return self.exec_run_handler('utf8_text_only', mailq_return)
173
# api call: container_post - post_action: exec - cmd: mailq - task: flush
174
def container_post__exec__mailq__flush(self, request_json, **kwargs):
175
if 'container_id' in kwargs:
176
filters = {"id": kwargs['container_id']}
177
elif 'container_name' in kwargs:
178
filters = {"name": kwargs['container_name']}
180
for container in self.sync_docker_client.containers.list(filters=filters):
181
postqueue_r = container.exec_run(["/usr/sbin/postqueue", "-f"], user='postfix')
182
return self.exec_run_handler('generic', postqueue_r)
183
# api call: container_post - post_action: exec - cmd: mailq - task: super_delete
184
def container_post__exec__mailq__super_delete(self, request_json, **kwargs):
185
if 'container_id' in kwargs:
186
filters = {"id": kwargs['container_id']}
187
elif 'container_name' in kwargs:
188
filters = {"name": kwargs['container_name']}
190
for container in self.sync_docker_client.containers.list(filters=filters):
191
postsuper_r = container.exec_run(["/usr/sbin/postsuper", "-d", "ALL"])
192
return self.exec_run_handler('generic', postsuper_r)
193
# api call: container_post - post_action: exec - cmd: system - task: fts_rescan
194
def container_post__exec__system__fts_rescan(self, request_json, **kwargs):
195
if 'container_id' in kwargs:
196
filters = {"id": kwargs['container_id']}
197
elif 'container_name' in kwargs:
198
filters = {"name": kwargs['container_name']}
200
if 'username' in request_json:
201
for container in self.sync_docker_client.containers.list(filters=filters):
202
rescan_return = container.exec_run(["/bin/bash", "-c", "/usr/bin/doveadm fts rescan -u '" + request_json['username'].replace("'", "'\\''") + "'"], user='vmail')
203
if rescan_return.exit_code == 0:
204
res = { 'type': 'success', 'msg': 'fts_rescan: rescan triggered'}
205
return Response(content=json.dumps(res, indent=4), media_type="application/json")
206
else:
207
res = { 'type': 'warning', 'msg': 'fts_rescan error'}
208
return Response(content=json.dumps(res, indent=4), media_type="application/json")
209
if 'all' in request_json:
210
for container in self.sync_docker_client.containers.list(filters=filters):
211
rescan_return = container.exec_run(["/bin/bash", "-c", "/usr/bin/doveadm fts rescan -A"], user='vmail')
212
if rescan_return.exit_code == 0:
213
res = { 'type': 'success', 'msg': 'fts_rescan: rescan triggered'}
214
return Response(content=json.dumps(res, indent=4), media_type="application/json")
215
else:
216
res = { 'type': 'warning', 'msg': 'fts_rescan error'}
217
return Response(content=json.dumps(res, indent=4), media_type="application/json")
218
# api call: container_post - post_action: exec - cmd: system - task: df
219
def container_post__exec__system__df(self, request_json, **kwargs):
220
if 'container_id' in kwargs:
221
filters = {"id": kwargs['container_id']}
222
elif 'container_name' in kwargs:
223
filters = {"name": kwargs['container_name']}
225
if 'dir' in request_json:
226
for container in self.sync_docker_client.containers.list(filters=filters):
227
df_return = container.exec_run(["/bin/bash", "-c", "/bin/df -H '" + request_json['dir'].replace("'", "'\\''") + "' | /usr/bin/tail -n1 | /usr/bin/tr -s [:blank:] | /usr/bin/tr ' ' ','"], user='nobody')
228
if df_return.exit_code == 0:
229
return df_return.output.decode('utf-8').rstrip()
230
else:
231
return "0,0,0,0,0,0"
232
# api call: container_post - post_action: exec - cmd: system - task: mysql_upgrade
233
def container_post__exec__system__mysql_upgrade(self, request_json, **kwargs):
234
if 'container_id' in kwargs:
235
filters = {"id": kwargs['container_id']}
236
elif 'container_name' in kwargs:
237
filters = {"name": kwargs['container_name']}
239
for container in self.sync_docker_client.containers.list(filters=filters):
240
sql_return = container.exec_run(["/bin/bash", "-c", "/usr/bin/mysql_upgrade -uroot -p'" + os.environ['DBROOT'].replace("'", "'\\''") + "'\n"], user='mysql')
241
if sql_return.exit_code == 0:
242
matched = False
243
for line in sql_return.output.decode('utf-8').split("\n"):
244
if 'is already upgraded to' in line:
245
matched = True
246
if matched:
247
res = { 'type': 'success', 'msg':'mysql_upgrade: already upgraded', 'text': sql_return.output.decode('utf-8')}
248
return Response(content=json.dumps(res, indent=4), media_type="application/json")
249
else:
250
container.restart()
251
res = { 'type': 'warning', 'msg':'mysql_upgrade: upgrade was applied', 'text': sql_return.output.decode('utf-8')}
252
return Response(content=json.dumps(res, indent=4), media_type="application/json")
253
else:
254
res = { 'type': 'error', 'msg': 'mysql_upgrade: error running command', 'text': sql_return.output.decode('utf-8')}
255
return Response(content=json.dumps(res, indent=4), media_type="application/json")
256
# api call: container_post - post_action: exec - cmd: system - task: mysql_tzinfo_to_sql
257
def container_post__exec__system__mysql_tzinfo_to_sql(self, request_json, **kwargs):
258
if 'container_id' in kwargs:
259
filters = {"id": kwargs['container_id']}
260
elif 'container_name' in kwargs:
261
filters = {"name": kwargs['container_name']}
263
for container in self.sync_docker_client.containers.list(filters=filters):
264
sql_return = container.exec_run(["/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')
265
if sql_return.exit_code == 0:
266
res = { 'type': 'info', 'msg': 'mysql_tzinfo_to_sql: command completed successfully', 'text': sql_return.output.decode('utf-8')}
267
return Response(content=json.dumps(res, indent=4), media_type="application/json")
268
else:
269
res = { 'type': 'error', 'msg': 'mysql_tzinfo_to_sql: error running command', 'text': sql_return.output.decode('utf-8')}
270
return Response(content=json.dumps(res, indent=4), media_type="application/json")
271
# api call: container_post - post_action: exec - cmd: reload - task: dovecot
272
def container_post__exec__reload__dovecot(self, request_json, **kwargs):
273
if 'container_id' in kwargs:
274
filters = {"id": kwargs['container_id']}
275
elif 'container_name' in kwargs:
276
filters = {"name": kwargs['container_name']}
278
for container in self.sync_docker_client.containers.list(filters=filters):
279
reload_return = container.exec_run(["/bin/bash", "-c", "/usr/sbin/dovecot reload"])
280
return self.exec_run_handler('generic', reload_return)
281
# api call: container_post - post_action: exec - cmd: reload - task: postfix
282
def container_post__exec__reload__postfix(self, request_json, **kwargs):
283
if 'container_id' in kwargs:
284
filters = {"id": kwargs['container_id']}
285
elif 'container_name' in kwargs:
286
filters = {"name": kwargs['container_name']}
288
for container in self.sync_docker_client.containers.list(filters=filters):
289
reload_return = container.exec_run(["/bin/bash", "-c", "/usr/sbin/postfix reload"])
290
return self.exec_run_handler('generic', reload_return)
291
# api call: container_post - post_action: exec - cmd: reload - task: nginx
292
def container_post__exec__reload__nginx(self, request_json, **kwargs):
293
if 'container_id' in kwargs:
294
filters = {"id": kwargs['container_id']}
295
elif 'container_name' in kwargs:
296
filters = {"name": kwargs['container_name']}
298
for container in self.sync_docker_client.containers.list(filters=filters):
299
reload_return = container.exec_run(["/bin/sh", "-c", "/usr/sbin/nginx -s reload"])
300
return self.exec_run_handler('generic', reload_return)
301
# api call: container_post - post_action: exec - cmd: sieve - task: list
302
def container_post__exec__sieve__list(self, request_json, **kwargs):
303
if 'container_id' in kwargs:
304
filters = {"id": kwargs['container_id']}
305
elif 'container_name' in kwargs:
306
filters = {"name": kwargs['container_name']}
308
if 'username' in request_json:
309
for container in self.sync_docker_client.containers.list(filters=filters):
310
sieve_return = container.exec_run(["/bin/bash", "-c", "/usr/bin/doveadm sieve list -u '" + request_json['username'].replace("'", "'\\''") + "'"])
311
return self.exec_run_handler('utf8_text_only', sieve_return)
312
# api call: container_post - post_action: exec - cmd: sieve - task: print
313
def container_post__exec__sieve__print(self, request_json, **kwargs):
314
if 'container_id' in kwargs:
315
filters = {"id": kwargs['container_id']}
316
elif 'container_name' in kwargs:
317
filters = {"name": kwargs['container_name']}
319
if 'username' in request_json and 'script_name' in request_json:
320
for container in self.sync_docker_client.containers.list(filters=filters):
321
cmd = ["/bin/bash", "-c", "/usr/bin/doveadm sieve get -u '" + request_json['username'].replace("'", "'\\''") + "' '" + request_json['script_name'].replace("'", "'\\''") + "'"]
322
sieve_return = container.exec_run(cmd)
323
return self.exec_run_handler('utf8_text_only', sieve_return)
324
# api call: container_post - post_action: exec - cmd: maildir - task: cleanup
325
def container_post__exec__maildir__cleanup(self, request_json, **kwargs):
326
if 'container_id' in kwargs:
327
filters = {"id": kwargs['container_id']}
328
elif 'container_name' in kwargs:
329
filters = {"name": kwargs['container_name']}
331
if 'maildir' in request_json:
332
for container in self.sync_docker_client.containers.list(filters=filters):
333
sane_name = re.sub(r'\W+', '', request_json['maildir'])
334
vmail_name = request_json['maildir'].replace("'", "'\\''")
335
cmd_vmail = "if [[ -d '/var/vmail/" + vmail_name + "' ]]; then /bin/mv '/var/vmail/" + vmail_name + "' '/var/vmail/_garbage/" + str(int(time.time())) + "_" + sane_name + "'; fi"
336
index_name = request_json['maildir'].split("/")
337
if len(index_name) > 1:
338
index_name = index_name[1].replace("'", "'\\''") + "@" + index_name[0].replace("'", "'\\''")
339
cmd_vmail_index = "if [[ -d '/var/vmail_index/" + index_name + "' ]]; then /bin/mv '/var/vmail_index/" + index_name + "' '/var/vmail/_garbage/" + str(int(time.time())) + "_" + sane_name + "_index'; fi"
340
cmd = ["/bin/bash", "-c", cmd_vmail + " && " + cmd_vmail_index]
341
else:
342
cmd = ["/bin/bash", "-c", cmd_vmail]
343
maildir_cleanup = container.exec_run(cmd, user='vmail')
344
return self.exec_run_handler('generic', maildir_cleanup)
345
# api call: container_post - post_action: exec - cmd: maildir - task: move
346
def container_post__exec__maildir__move(self, request_json, **kwargs):
347
if 'container_id' in kwargs:
348
filters = {"id": kwargs['container_id']}
349
elif 'container_name' in kwargs:
350
filters = {"name": kwargs['container_name']}
352
if 'old_maildir' in request_json and 'new_maildir' in request_json:
353
for container in self.sync_docker_client.containers.list(filters=filters):
354
vmail_name = request_json['old_maildir'].replace("'", "'\\''")
355
new_vmail_name = request_json['new_maildir'].replace("'", "'\\''")
356
cmd_vmail = f"if [[ -d '/var/vmail/{vmail_name}' ]]; then /bin/mv '/var/vmail/{vmail_name}' '/var/vmail/{new_vmail_name}'; fi"
358
index_name = request_json['old_maildir'].split("/")
359
new_index_name = request_json['new_maildir'].split("/")
360
if len(index_name) > 1 and len(new_index_name) > 1:
361
index_name = index_name[1].replace("'", "'\\''") + "@" + index_name[0].replace("'", "'\\''")
362
new_index_name = new_index_name[1].replace("'", "'\\''") + "@" + new_index_name[0].replace("'", "'\\''")
363
cmd_vmail_index = f"if [[ -d '/var/vmail_index/{index_name}' ]]; then /bin/mv '/var/vmail_index/{index_name}' '/var/vmail_index/{new_index_name}_index'; fi"
364
cmd = ["/bin/bash", "-c", cmd_vmail + " && " + cmd_vmail_index]
365
else:
366
cmd = ["/bin/bash", "-c", cmd_vmail]
367
maildir_move = container.exec_run(cmd, user='vmail')
368
return self.exec_run_handler('generic', maildir_move)
369
# api call: container_post - post_action: exec - cmd: rspamd - task: worker_password
370
def container_post__exec__rspamd__worker_password(self, request_json, **kwargs):
371
if 'container_id' in kwargs:
372
filters = {"id": kwargs['container_id']}
373
elif 'container_name' in kwargs:
374
filters = {"name": kwargs['container_name']}
376
if 'raw' in request_json:
377
for container in self.sync_docker_client.containers.list(filters=filters):
378
cmd = "/usr/bin/rspamadm pw -e -p '" + request_json['raw'].replace("'", "'\\''") + "' 2> /dev/null"
379
cmd_response = self.exec_cmd_container(container, cmd, user="_rspamd")
381
matched = False
382
for line in cmd_response.split("\n"):
383
if '$2$' in line:
384
hash = line.strip()
385
hash_out = re.search(r'\$2\$.+$', hash).group(0)
386
rspamd_passphrase_hash = re.sub(r'[^0-9a-zA-Z\$]+', '', hash_out.rstrip())
387
rspamd_password_filename = "/etc/rspamd/override.d/worker-controller-password.inc"
388
cmd = '''/bin/echo 'enable_password = "%s";' > %s && cat %s''' % (rspamd_passphrase_hash, rspamd_password_filename, rspamd_password_filename)
389
cmd_response = self.exec_cmd_container(container, cmd, user="_rspamd")
390
if rspamd_passphrase_hash.startswith("$2$") and rspamd_passphrase_hash in cmd_response:
391
container.restart()
392
matched = True
393
if matched:
394
res = { 'type': 'success', 'msg': 'command completed successfully' }
395
self.logger.info('success changing Rspamd password')
396
return Response(content=json.dumps(res, indent=4), media_type="application/json")
397
else:
398
self.logger.error('failed changing Rspamd password')
399
res = { 'type': 'danger', 'msg': 'command did not complete' }
400
return Response(content=json.dumps(res, indent=4), media_type="application/json")
401
# api call: container_post - post_action: exec - cmd: sogo - task: rename
402
def container_post__exec__sogo__rename_user(self, request_json, **kwargs):
403
if 'container_id' in kwargs:
404
filters = {"id": kwargs['container_id']}
405
elif 'container_name' in kwargs:
406
filters = {"name": kwargs['container_name']}
408
if 'old_username' in request_json and 'new_username' in request_json:
409
for container in self.sync_docker_client.containers.list(filters=filters):
410
old_username = request_json['old_username'].replace("'", "'\\''")
411
new_username = request_json['new_username'].replace("'", "'\\''")
413
sogo_return = container.exec_run(["/bin/bash", "-c", f"sogo-tool rename-user '{old_username}' '{new_username}'"], user='sogo')
414
return self.exec_run_handler('generic', sogo_return)
415
# api call: container_post - post_action: exec - cmd: doveadm - task: get_acl
416
def container_post__exec__doveadm__get_acl(self, request_json, **kwargs):
417
if 'container_id' in kwargs:
418
filters = {"id": kwargs['container_id']}
419
elif 'container_name' in kwargs:
420
filters = {"name": kwargs['container_name']}
422
for container in self.sync_docker_client.containers.list(filters=filters):
423
id = request_json['id'].replace("'", "'\\''")
425
shared_folders = container.exec_run(["/bin/bash", "-c", f"doveadm mailbox list -u '{id}'"])
426
shared_folders = shared_folders.output.decode('utf-8')
427
shared_folders = shared_folders.splitlines()
429
formatted_acls = []
430
mailbox_seen = []
431
for shared_folder in shared_folders:
432
if "Shared" not in shared_folder:
433
mailbox = shared_folder.replace("'", "'\\''")
434
if mailbox in mailbox_seen:
435
continue
437
acls = container.exec_run(["/bin/bash", "-c", f"doveadm acl get -u '{id}' '{mailbox}'"])
438
acls = acls.output.decode('utf-8').strip().splitlines()
439
if len(acls) >= 2:
440
for acl in acls[1:]:
441
user_id, rights = acl.split(maxsplit=1)
442
user_id = user_id.split('=')[1]
443
mailbox_seen.append(mailbox)
444
formatted_acls.append({ 'user': id, 'id': user_id, 'mailbox': mailbox, 'rights': rights.split() })
445
elif "Shared" in shared_folder and "/" in shared_folder:
446
shared_folder = shared_folder.split("/")
447
if len(shared_folder) < 3:
448
continue
450
user = shared_folder[1].replace("'", "'\\''")
451
mailbox = '/'.join(shared_folder[2:]).replace("'", "'\\''")
452
if mailbox in mailbox_seen:
453
continue
455
acls = container.exec_run(["/bin/bash", "-c", f"doveadm acl get -u '{user}' '{mailbox}'"])
456
acls = acls.output.decode('utf-8').strip().splitlines()
457
if len(acls) >= 2:
458
for acl in acls[1:]:
459
user_id, rights = acl.split(maxsplit=1)
460
user_id = user_id.split('=')[1].replace("'", "'\\''")
461
if user_id == id and mailbox not in mailbox_seen:
462
mailbox_seen.append(mailbox)
463
formatted_acls.append({ 'user': user, 'id': id, 'mailbox': mailbox, 'rights': rights.split() })
465
return Response(content=json.dumps(formatted_acls, indent=4), media_type="application/json")
466
# api call: container_post - post_action: exec - cmd: doveadm - task: delete_acl
467
def container_post__exec__doveadm__delete_acl(self, request_json, **kwargs):
468
if 'container_id' in kwargs:
469
filters = {"id": kwargs['container_id']}
470
elif 'container_name' in kwargs:
471
filters = {"name": kwargs['container_name']}
473
for container in self.sync_docker_client.containers.list(filters=filters):
474
user = request_json['user'].replace("'", "'\\''")
475
mailbox = request_json['mailbox'].replace("'", "'\\''")
476
id = request_json['id'].replace("'", "'\\''")
478
if user and mailbox and id:
479
acl_delete_return = container.exec_run(["/bin/bash", "-c", f"doveadm acl delete -u '{user}' '{mailbox}' 'user={id}'"])
480
return self.exec_run_handler('generic', acl_delete_return)
481
# api call: container_post - post_action: exec - cmd: doveadm - task: set_acl
482
def container_post__exec__doveadm__set_acl(self, request_json, **kwargs):
483
if 'container_id' in kwargs:
484
filters = {"id": kwargs['container_id']}
485
elif 'container_name' in kwargs:
486
filters = {"name": kwargs['container_name']}
488
for container in self.sync_docker_client.containers.list(filters=filters):
489
user = request_json['user'].replace("'", "'\\''")
490
mailbox = request_json['mailbox'].replace("'", "'\\''")
491
id = request_json['id'].replace("'", "'\\''")
492
rights = ""
494
available_rights = [
495
"admin",
496
"create",
497
"delete",
498
"expunge",
499
"insert",
500
"lookup",
501
"post",
502
"read",
503
"write",
504
"write-deleted",
505
"write-seen"
506
]
507
for right in request_json['rights']:
508
right = right.replace("'", "'\\''").lower()
509
if right in available_rights:
510
rights += right + " "
512
if user and mailbox and id and rights:
513
acl_set_return = container.exec_run(["/bin/bash", "-c", f"doveadm acl set -u '{user}' '{mailbox}' 'user={id}' {rights}"])
514
return self.exec_run_handler('generic', acl_set_return)
517
# Collect host stats
518
async def get_host_stats(self, wait=5):
519
try:
520
system_time = datetime.now()
521
host_stats = {
522
"cpu": {
523
"cores": psutil.cpu_count(),
524
"usage": psutil.cpu_percent()
525
},
526
"memory": {
527
"total": psutil.virtual_memory().total,
528
"usage": psutil.virtual_memory().percent,
529
"swap": psutil.swap_memory()
530
},
531
"uptime": time.time() - psutil.boot_time(),
532
"system_time": system_time.strftime("%d.%m.%Y %H:%M:%S"),
533
"architecture": platform.machine()
534
}
536
await self.redis_client.set('host_stats', json.dumps(host_stats), ex=10)
537
except Exception as e:
538
res = {
539
"type": "danger",
540
"msg": str(e)
541
}
543
await asyncio.sleep(wait)
544
self.host_stats_isUpdating = False
545
# Collect container stats
546
async def get_container_stats(self, container_id, wait=5, stop=False):
547
if container_id and container_id.isalnum():
548
try:
549
for container in (await self.async_docker_client.containers.list()):
550
if container._id == container_id:
551
res = await container.stats(stream=False)
553
if await self.redis_client.exists(container_id + '_stats'):
554
stats = json.loads(await self.redis_client.get(container_id + '_stats'))
555
else:
556
stats = []
557
stats.append(res[0])
558
if len(stats) > 3:
559
del stats[0]
560
await self.redis_client.set(container_id + '_stats', json.dumps(stats), ex=60)
561
except Exception as e:
562
res = {
563
"type": "danger",
564
"msg": str(e)
565
}
566
else:
567
res = {
568
"type": "danger",
569
"msg": "no or invalid id defined"
570
}
572
await asyncio.sleep(wait)
573
if stop == True:
574
# update task was called second time, stop
575
self.containerIds_to_update.remove(container_id)
576
else:
577
# call update task a second time
578
await self.get_container_stats(container_id, wait=0, stop=True)
580
def exec_cmd_container(self, container, cmd, user, timeout=2, shell_cmd="/bin/bash"):
581
def recv_socket_data(c_socket, timeout):
582
c_socket.setblocking(0)
583
total_data=[]
584
data=''
585
begin=time.time()
586
while True:
587
if total_data and time.time()-begin > timeout:
588
break
589
elif time.time()-begin > timeout*2:
590
break
591
try:
592
data = c_socket.recv(8192)
593
if data:
594
total_data.append(data.decode('utf-8'))
595
#change the beginning time for measurement
596
begin=time.time()
597
else:
598
#sleep for sometime to indicate a gap
599
time.sleep(0.1)
600
break
601
except:
602
pass
603
return ''.join(total_data)
605
try :
606
socket = container.exec_run([shell_cmd], stdin=True, socket=True, user=user).output._sock
607
if not cmd.endswith("\n"):
608
cmd = cmd + "\n"
609
socket.send(cmd.encode('utf-8'))
610
data = recv_socket_data(socket, timeout)
611
socket.close()
612
return data
613
except Exception as e:
614
self.logger.error("error - exec_cmd_container: %s" % str(e))
615
traceback.print_exc(file=sys.stdout)
617
def exec_run_handler(self, type, output):
618
if type == 'generic':
619
if output.exit_code == 0:
620
res = { 'type': 'success', 'msg': 'command completed successfully' }
621
return Response(content=json.dumps(res, indent=4), media_type="application/json")
622
else:
623
res = { 'type': 'danger', 'msg': 'command failed: ' + output.output.decode('utf-8') }
624
return Response(content=json.dumps(res, indent=4), media_type="application/json")
625
if type == 'utf8_text_only':
626
return Response(content=output.output.decode('utf-8'), media_type="text/plain")