InPanel 1.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- inpanel/__init__.py +29 -0
- inpanel/acme.py +289 -0
- inpanel/aliyuncs.py +269 -0
- inpanel/app.py +514 -0
- inpanel/apt.py +223 -0
- inpanel/base.py +200 -0
- inpanel/config.py +139 -0
- inpanel/data/config.ini +29 -0
- inpanel/data/history +5 -0
- inpanel/data/runlogs.ini +12 -0
- inpanel/data/update_info.ini +4 -0
- inpanel/dnf.py +225 -0
- inpanel/lib/__init__.py +9 -0
- inpanel/lib/async_process.py +98 -0
- inpanel/lib/filelock.py +82 -0
- inpanel/lib/magic.py +233 -0
- inpanel/lib/pxssh.py +310 -0
- inpanel/lib/pyDes.py +867 -0
- inpanel/mod/__init__.py +0 -0
- inpanel/mod/apt.py +316 -0
- inpanel/mod/certificate.py +268 -0
- inpanel/mod/config.py +372 -0
- inpanel/mod/cron.py +315 -0
- inpanel/mod/disk.py +322 -0
- inpanel/mod/dnf.py +239 -0
- inpanel/mod/file.py +722 -0
- inpanel/mod/firewall.py +199 -0
- inpanel/mod/firewall_base.py +137 -0
- inpanel/mod/firewall_firewalld.py +170 -0
- inpanel/mod/firewall_iptables.py +176 -0
- inpanel/mod/firewall_macos.py +213 -0
- inpanel/mod/firewall_ufw.py +128 -0
- inpanel/mod/ftp.py +231 -0
- inpanel/mod/httpd.py +1205 -0
- inpanel/mod/lighttpd.py +26 -0
- inpanel/mod/login.py +98 -0
- inpanel/mod/mysql.py +577 -0
- inpanel/mod/named.py +26 -0
- inpanel/mod/nginx.py +2327 -0
- inpanel/mod/package.py +33 -0
- inpanel/mod/package_apt.py +62 -0
- inpanel/mod/package_base.py +77 -0
- inpanel/mod/package_dnf.py +62 -0
- inpanel/mod/package_map.py +366 -0
- inpanel/mod/package_yum.py +64 -0
- inpanel/mod/php.py +289 -0
- inpanel/mod/process.py +291 -0
- inpanel/mod/proftpd.py +26 -0
- inpanel/mod/pureftpd.py +26 -0
- inpanel/mod/query.py +108 -0
- inpanel/mod/server.py +1319 -0
- inpanel/mod/service.py +262 -0
- inpanel/mod/setting.py +228 -0
- inpanel/mod/shell.py +78 -0
- inpanel/mod/ssh.py +280 -0
- inpanel/mod/system.py +220 -0
- inpanel/mod/task.py +1337 -0
- inpanel/mod/ufw.py +14 -0
- inpanel/mod/user.py +306 -0
- inpanel/mod/vsftpd.py +67 -0
- inpanel/mod/yum.py +243 -0
- inpanel/plugins/__init__.py +10 -0
- inpanel/plugins/__pycache__/__init__.cpython-313.pyc +0 -0
- inpanel/plugins/acme/static/index.html +10 -0
- inpanel/plugins/acme/static/index.js +13 -0
- inpanel/plugins/shadowsocks-libev/__pycache__/info.cpython-313.pyc +0 -0
- inpanel/plugins/shadowsocks-libev/info.py +73 -0
- inpanel/plugins/shadowsocks-libev/static/index.html +70 -0
- inpanel/plugins/shadowsocks-libev/static/index.js +18 -0
- inpanel/public/css/global.css +138 -0
- inpanel/public/favicon.ico +0 -0
- inpanel/public/images/alipay.jpg +0 -0
- inpanel/public/images/loading.gif +0 -0
- inpanel/public/images/weixin.jpg +0 -0
- inpanel/public/index.html +94 -0
- inpanel/public/js/controllers/controllers.js +566 -0
- inpanel/public/js/controllers/database.js +590 -0
- inpanel/public/js/controllers/ecs.js +624 -0
- inpanel/public/js/controllers/file.js +940 -0
- inpanel/public/js/controllers/plugins.js +105 -0
- inpanel/public/js/controllers/service.js +1460 -0
- inpanel/public/js/controllers/site.js +1241 -0
- inpanel/public/js/controllers/utils.js +2319 -0
- inpanel/public/js/core.js +221 -0
- inpanel/public/js/directives.js +784 -0
- inpanel/public/js/filters.js +395 -0
- inpanel/public/js/services.js +257 -0
- inpanel/public/lib/angular/1.0.2/angular-resource.min.js +10 -0
- inpanel/public/lib/angular/1.0.2/angular.min.js +158 -0
- inpanel/public/lib/bootstrap/css/bootstrap-theme.css +587 -0
- inpanel/public/lib/bootstrap/css/bootstrap-theme.css.map +1 -0
- inpanel/public/lib/bootstrap/css/bootstrap-theme.min.css +6 -0
- inpanel/public/lib/bootstrap/css/bootstrap-theme.min.css.map +1 -0
- inpanel/public/lib/bootstrap/css/bootstrap.css +6757 -0
- inpanel/public/lib/bootstrap/css/bootstrap.css.map +1 -0
- inpanel/public/lib/bootstrap/css/bootstrap.min.css +6 -0
- inpanel/public/lib/bootstrap/css/bootstrap.min.css.map +1 -0
- inpanel/public/lib/bootstrap/fonts/glyphicons-halflings-regular.eot +0 -0
- inpanel/public/lib/bootstrap/fonts/glyphicons-halflings-regular.svg +288 -0
- inpanel/public/lib/bootstrap/fonts/glyphicons-halflings-regular.ttf +0 -0
- inpanel/public/lib/bootstrap/fonts/glyphicons-halflings-regular.woff +0 -0
- inpanel/public/lib/bootstrap/fonts/glyphicons-halflings-regular.woff2 +0 -0
- inpanel/public/lib/bootstrap/img/glyphicons-halflings-white.png +0 -0
- inpanel/public/lib/bootstrap/img/glyphicons-halflings.png +0 -0
- inpanel/public/lib/bootstrap/js/bootstrap.js +2377 -0
- inpanel/public/lib/bootstrap/js/bootstrap.min.js +7 -0
- inpanel/public/lib/bootstrap/js/npm.js +13 -0
- inpanel/public/lib/codemirror/codemirror-compressed.js +1 -0
- inpanel/public/lib/codemirror/codemirror.css +173 -0
- inpanel/public/lib/jquery/jquery-1.12.4.min.js +5 -0
- inpanel/public/lib/md5/md5.js +379 -0
- inpanel/public/lib/md5/md5.min.js +1 -0
- inpanel/public/partials/database/index.html +172 -0
- inpanel/public/partials/database/mysql/dbedit.html +462 -0
- inpanel/public/partials/database/mysql/dbnew.html +348 -0
- inpanel/public/partials/database/mysql/useredit.html +310 -0
- inpanel/public/partials/database/mysql/usernew.html +97 -0
- inpanel/public/partials/directives/message.html +20 -0
- inpanel/public/partials/directives/navbar.html +25 -0
- inpanel/public/partials/directives/selector.html +57 -0
- inpanel/public/partials/directives/srvbase.html +72 -0
- inpanel/public/partials/directives/srvext.html +34 -0
- inpanel/public/partials/directives/srvfile.html +23 -0
- inpanel/public/partials/directives/srvinstall.html +34 -0
- inpanel/public/partials/directives/srvlog.html +23 -0
- inpanel/public/partials/directives/srvminiop.html +28 -0
- inpanel/public/partials/directives/srvuninstall.html +38 -0
- inpanel/public/partials/directives/srvupdate.html +30 -0
- inpanel/public/partials/ecs/account.html +156 -0
- inpanel/public/partials/ecs/ecs.html +198 -0
- inpanel/public/partials/ecs/index.html +40 -0
- inpanel/public/partials/ecs/setings.html +459 -0
- inpanel/public/partials/file/file.html +501 -0
- inpanel/public/partials/file/trash.html +95 -0
- inpanel/public/partials/ftp.html +36 -0
- inpanel/public/partials/log.html +83 -0
- inpanel/public/partials/login.html +79 -0
- inpanel/public/partials/logout.html +22 -0
- inpanel/public/partials/main.html +534 -0
- inpanel/public/partials/plugins/index.html +55 -0
- inpanel/public/partials/plugins/plugins.html +13 -0
- inpanel/public/partials/secure.html +13 -0
- inpanel/public/partials/service/apache.html +147 -0
- inpanel/public/partials/service/cron.html +71 -0
- inpanel/public/partials/service/index.html +293 -0
- inpanel/public/partials/service/iptables.html +40 -0
- inpanel/public/partials/service/lighttpd.html +63 -0
- inpanel/public/partials/service/mariadb.html +113 -0
- inpanel/public/partials/service/memcache.html +36 -0
- inpanel/public/partials/service/minio.html +44 -0
- inpanel/public/partials/service/mongodb.html +44 -0
- inpanel/public/partials/service/mysql.html +113 -0
- inpanel/public/partials/service/named.html +61 -0
- inpanel/public/partials/service/nginx.html +296 -0
- inpanel/public/partials/service/ntp.html +40 -0
- inpanel/public/partials/service/php.html +250 -0
- inpanel/public/partials/service/proftpd.html +59 -0
- inpanel/public/partials/service/pureftpd.html +59 -0
- inpanel/public/partials/service/redis.html +40 -0
- inpanel/public/partials/service/samba.html +58 -0
- inpanel/public/partials/service/sendmail.html +40 -0
- inpanel/public/partials/service/ssh.html +186 -0
- inpanel/public/partials/service/tomcat.html +65 -0
- inpanel/public/partials/service/vsftpd.html +195 -0
- inpanel/public/partials/setting.html +299 -0
- inpanel/public/partials/site/apache/site.html +277 -0
- inpanel/public/partials/site/index.html +344 -0
- inpanel/public/partials/site/nginx/site.html +558 -0
- inpanel/public/partials/sorry.html +19 -0
- inpanel/public/partials/storage/autofm.html +103 -0
- inpanel/public/partials/storage/backup.html +42 -0
- inpanel/public/partials/storage/index.html +360 -0
- inpanel/public/partials/storage/migrate.html +93 -0
- inpanel/public/partials/storage/movedata.html +98 -0
- inpanel/public/partials/storage/remote.html +77 -0
- inpanel/public/partials/utils/cron.html +486 -0
- inpanel/public/partials/utils/firewall.html +266 -0
- inpanel/public/partials/utils/index.html +193 -0
- inpanel/public/partials/utils/network.html +118 -0
- inpanel/public/partials/utils/process.html +239 -0
- inpanel/public/partials/utils/repository.html +373 -0
- inpanel/public/partials/utils/shell.html +28 -0
- inpanel/public/partials/utils/ssl.html +319 -0
- inpanel/public/partials/utils/time.html +109 -0
- inpanel/public/partials/utils/user.html +359 -0
- inpanel/public/robots.txt +2 -0
- inpanel/remote.py +109 -0
- inpanel/templates/file/preview.html +34 -0
- inpanel/templates/index.html +93 -0
- inpanel/utils.py +282 -0
- inpanel/web.py +1811 -0
- inpanel/yum.py +756 -0
- inpanel-1.2.1.dist-info/METADATA +399 -0
- inpanel-1.2.1.dist-info/RECORD +198 -0
- inpanel-1.2.1.dist-info/WHEEL +5 -0
- inpanel-1.2.1.dist-info/entry_points.txt +2 -0
- inpanel-1.2.1.dist-info/licenses/LICENSE +30 -0
- inpanel-1.2.1.dist-info/top_level.txt +1 -0
inpanel/mod/config.py
ADDED
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# Copyright (c) 2017-2026 Jackson Dou
|
|
4
|
+
# All rights reserved.
|
|
5
|
+
#
|
|
6
|
+
# InPanel is distributed under the terms of the New BSD License.
|
|
7
|
+
# The full license can be found in 'LICENSE'.
|
|
8
|
+
|
|
9
|
+
'''Module for Configurations Management.'''
|
|
10
|
+
|
|
11
|
+
from configparser import RawConfigParser
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
from ..base import config_file, runlogs_path, update_info_path, config_path, run_type
|
|
15
|
+
from ..lib.filelock import FileLock
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def load_config(inifile=None, configs=None):
|
|
19
|
+
'''the configurations for InPanel'''
|
|
20
|
+
if run_type == 'source':
|
|
21
|
+
ssl_cert_path = str(Path(config_path) / 'certificate')
|
|
22
|
+
else:
|
|
23
|
+
ssl_cert_path = '/etc/inpanel/certificate'
|
|
24
|
+
|
|
25
|
+
default_configs = {
|
|
26
|
+
'server': {
|
|
27
|
+
'ip': '*',
|
|
28
|
+
'port': '14433',
|
|
29
|
+
'forcehttps': 'off', # force use https
|
|
30
|
+
'sslkey': str(Path(ssl_cert_path) / 'inpanel.key'),
|
|
31
|
+
'sslcrt': str(Path(ssl_cert_path) / 'inpanel.crt')
|
|
32
|
+
},
|
|
33
|
+
'auth': {
|
|
34
|
+
'username': 'admin',
|
|
35
|
+
'password': 'a17351f4e092ba7d87a7a90c170a5cf6:1bdf574a782e8f006676ee1225743a6b', # admin
|
|
36
|
+
'passwordcheck': 'on',
|
|
37
|
+
'accesskey': '', # empty access key never validated
|
|
38
|
+
'accesskeyenable': 'off',
|
|
39
|
+
},
|
|
40
|
+
'runtime': {
|
|
41
|
+
'mode': 'prod', # format: demo | prod
|
|
42
|
+
'loginlock': 'off',
|
|
43
|
+
'loginfails': '0',
|
|
44
|
+
'loginlockexpire': '0',
|
|
45
|
+
},
|
|
46
|
+
'time': {
|
|
47
|
+
'timezone': '' # format: timezone = Asia/Shanghai
|
|
48
|
+
},
|
|
49
|
+
'ecs': {
|
|
50
|
+
'accounts': ''
|
|
51
|
+
},
|
|
52
|
+
'inpanel': {
|
|
53
|
+
'Instance Name': 'Access key'
|
|
54
|
+
}
|
|
55
|
+
} if configs is None else configs
|
|
56
|
+
|
|
57
|
+
return Config(config_file if inifile is None else inifile, default_configs)
|
|
58
|
+
|
|
59
|
+
def runlogs_config():
|
|
60
|
+
# to recode running data logs
|
|
61
|
+
return load_config(runlogs_path, {
|
|
62
|
+
'file': {
|
|
63
|
+
'lastdir': str(Path.home()), # user Home path
|
|
64
|
+
'lastfile': '',
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def update_info_config():
|
|
70
|
+
# to record version update information separately from config
|
|
71
|
+
return load_config(update_info_path, {
|
|
72
|
+
'update': {
|
|
73
|
+
'lastcheck': '0',
|
|
74
|
+
'updateinfo': '',
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
class Config(object):
|
|
79
|
+
def __init__(self, inifile=None, configs=None):
|
|
80
|
+
if inifile is None:
|
|
81
|
+
return None
|
|
82
|
+
|
|
83
|
+
inifile_path = Path(inifile)
|
|
84
|
+
if not inifile_path.parent.exists():
|
|
85
|
+
inifile_path.parent.mkdir(parents=True, exist_ok=True)
|
|
86
|
+
|
|
87
|
+
self.inifile = inifile
|
|
88
|
+
self.cfg = RawConfigParser()
|
|
89
|
+
|
|
90
|
+
with FileLock(self.inifile):
|
|
91
|
+
if inifile_path.exists():
|
|
92
|
+
self.cfg.read(self.inifile)
|
|
93
|
+
|
|
94
|
+
# initialize configurations
|
|
95
|
+
default_configs = {} if configs is None else configs
|
|
96
|
+
needupdate = False
|
|
97
|
+
for sec, secdata in default_configs.items():
|
|
98
|
+
if not self.cfg.has_section(sec):
|
|
99
|
+
self.cfg.add_section(sec)
|
|
100
|
+
needupdate = True
|
|
101
|
+
for opt, val in secdata.items():
|
|
102
|
+
if not self.cfg.has_option(sec, opt):
|
|
103
|
+
self.cfg.set(sec, opt, str(val))
|
|
104
|
+
needupdate = True
|
|
105
|
+
|
|
106
|
+
# update ini file
|
|
107
|
+
if needupdate:
|
|
108
|
+
self.update(False)
|
|
109
|
+
|
|
110
|
+
def update(self, lock=True):
|
|
111
|
+
if lock:
|
|
112
|
+
flock = FileLock(self.inifile)
|
|
113
|
+
flock.acquire()
|
|
114
|
+
|
|
115
|
+
try:
|
|
116
|
+
inifp = open(self.inifile, 'w', encoding='utf-8')
|
|
117
|
+
self.cfg.write(inifp)
|
|
118
|
+
inifp.close()
|
|
119
|
+
if lock:
|
|
120
|
+
flock.release()
|
|
121
|
+
return True
|
|
122
|
+
except:
|
|
123
|
+
if lock:
|
|
124
|
+
flock.release()
|
|
125
|
+
return False
|
|
126
|
+
|
|
127
|
+
def has_option(self, section, option):
|
|
128
|
+
return self.cfg.has_option(section, option)
|
|
129
|
+
|
|
130
|
+
def remove_option(self, section, option):
|
|
131
|
+
return self.cfg.remove_option(section, option)
|
|
132
|
+
|
|
133
|
+
def get(self, section, option):
|
|
134
|
+
if self.cfg.has_option(section, option):
|
|
135
|
+
return self.cfg.get(section, option)
|
|
136
|
+
else:
|
|
137
|
+
return None
|
|
138
|
+
|
|
139
|
+
def getboolean(self, section, option):
|
|
140
|
+
return self.cfg.getboolean(section, option)
|
|
141
|
+
|
|
142
|
+
def getint(self, section, option):
|
|
143
|
+
return self.cfg.getint(section, option)
|
|
144
|
+
|
|
145
|
+
def has_section(self, section):
|
|
146
|
+
return self.cfg.has_section(section)
|
|
147
|
+
|
|
148
|
+
def add_section(self, section):
|
|
149
|
+
return self.cfg.add_section(section)
|
|
150
|
+
|
|
151
|
+
def remove_section(self, section=None):
|
|
152
|
+
if section is None:
|
|
153
|
+
return False
|
|
154
|
+
else:
|
|
155
|
+
return self.cfg.remove_section(section)
|
|
156
|
+
|
|
157
|
+
def set(self, section, option, value):
|
|
158
|
+
try:
|
|
159
|
+
self.cfg.set(section, option, value)
|
|
160
|
+
except:
|
|
161
|
+
return False
|
|
162
|
+
return self.update()
|
|
163
|
+
|
|
164
|
+
def get_section_list(self):
|
|
165
|
+
'''Return a list of section names, excluding [DEFAULT]'''
|
|
166
|
+
return self.cfg.sections()
|
|
167
|
+
|
|
168
|
+
def get_option_list(self, section):
|
|
169
|
+
'''Return a list of option names for the given section name.'''
|
|
170
|
+
return self.cfg.options(section)
|
|
171
|
+
|
|
172
|
+
def get_config_list(self):
|
|
173
|
+
'''Return a list of all config for the given config file.'''
|
|
174
|
+
config_list = []
|
|
175
|
+
sections = self.cfg.sections()
|
|
176
|
+
for section in sections:
|
|
177
|
+
sec = {'section': section, 'option': {}}
|
|
178
|
+
options = self.cfg.options(section)
|
|
179
|
+
for key in options:
|
|
180
|
+
sec['option'][key] = self.cfg.get(section, key)
|
|
181
|
+
config_list.append(sec)
|
|
182
|
+
return config_list
|
|
183
|
+
|
|
184
|
+
def get_config(self):
|
|
185
|
+
'''Return a dict of all config for the given config file.'''
|
|
186
|
+
config = {}
|
|
187
|
+
for section in self.cfg.sections():
|
|
188
|
+
config[section] = {}
|
|
189
|
+
for item in self.cfg.options(section):
|
|
190
|
+
config[section][item] = self.cfg.get(section, item)
|
|
191
|
+
return config
|
|
192
|
+
|
|
193
|
+
def addsection(self, section, data):
|
|
194
|
+
'''add one section'''
|
|
195
|
+
try:
|
|
196
|
+
if not self.cfg.has_section(section):
|
|
197
|
+
self.cfg.add_section(section)
|
|
198
|
+
for option, value in data.items():
|
|
199
|
+
self.cfg.set(section, option, value)
|
|
200
|
+
return self.update(False)
|
|
201
|
+
except:
|
|
202
|
+
return False
|
|
203
|
+
|
|
204
|
+
def addsections(self, section):
|
|
205
|
+
'''add some sections'''
|
|
206
|
+
try:
|
|
207
|
+
for sec, data in section.items():
|
|
208
|
+
if not self.cfg.has_section(sec):
|
|
209
|
+
self.cfg.add_section(sec)
|
|
210
|
+
for option, value in data.items():
|
|
211
|
+
self.cfg.set(sec, option, value)
|
|
212
|
+
return self.update(False)
|
|
213
|
+
except:
|
|
214
|
+
return False
|
|
215
|
+
|
|
216
|
+
def raw_loadconfig(filepath, return_sort=False, delimiter='=', quoter=' "\'', overwrite=True):
|
|
217
|
+
"""Read config from file.
|
|
218
|
+
"""
|
|
219
|
+
filepath = Path(filepath)
|
|
220
|
+
if not filepath.exists():
|
|
221
|
+
return None
|
|
222
|
+
config = {}
|
|
223
|
+
if return_sort:
|
|
224
|
+
sortlist = []
|
|
225
|
+
with open(filepath, encoding='utf-8') as f:
|
|
226
|
+
for line in f:
|
|
227
|
+
pair = line.strip().split(delimiter)
|
|
228
|
+
if len(pair) != 2:
|
|
229
|
+
continue
|
|
230
|
+
k, v = [x.strip(quoter) for x in pair]
|
|
231
|
+
if return_sort:
|
|
232
|
+
sortlist.append(k)
|
|
233
|
+
if overwrite:
|
|
234
|
+
config[k] = v
|
|
235
|
+
else:
|
|
236
|
+
if not k in config:
|
|
237
|
+
config[k] = []
|
|
238
|
+
config[k].append(v)
|
|
239
|
+
if return_sort:
|
|
240
|
+
return (config, sortlist)
|
|
241
|
+
else:
|
|
242
|
+
return config
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def raw_saveconfig(filepath, config, sortlist=[], delimiter='=', quoter='"'):
|
|
246
|
+
"""Write config to file.
|
|
247
|
+
"""
|
|
248
|
+
filepath = Path(filepath)
|
|
249
|
+
if not filepath.exists():
|
|
250
|
+
return False
|
|
251
|
+
lines = []
|
|
252
|
+
|
|
253
|
+
# write the item in sortlist first
|
|
254
|
+
if len(sortlist) > 0:
|
|
255
|
+
for k in sortlist:
|
|
256
|
+
if k in config:
|
|
257
|
+
line = '%s%s%s%s%s\n' % (k, delimiter, quoter, config[k], quoter)
|
|
258
|
+
del config[k]
|
|
259
|
+
lines.append(line)
|
|
260
|
+
|
|
261
|
+
# then write the rest items
|
|
262
|
+
for k, v in config.items():
|
|
263
|
+
if isinstance(v, list):
|
|
264
|
+
for vv in v:
|
|
265
|
+
line = '%s%s%s%s%s\n' % (k, delimiter, quoter, vv, quoter)
|
|
266
|
+
lines.append(line)
|
|
267
|
+
else:
|
|
268
|
+
line = '%s%s%s%s%s\n' % (k, delimiter, quoter, v, quoter)
|
|
269
|
+
lines.append(line)
|
|
270
|
+
|
|
271
|
+
with open(filepath, 'w', encoding='utf-8') as f:
|
|
272
|
+
f.writelines(lines)
|
|
273
|
+
return True
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def loadconfig(filepath, keymap=None, delimiter='=', quoter=' "\''):
|
|
277
|
+
"""Load config from file and parse it to dict.
|
|
278
|
+
"""
|
|
279
|
+
raw_config = raw_loadconfig(filepath)
|
|
280
|
+
if raw_config is None:
|
|
281
|
+
return None
|
|
282
|
+
if keymap is None:
|
|
283
|
+
config = dict((k, v) for k, v in raw_config.items())
|
|
284
|
+
else:
|
|
285
|
+
config = dict((keymap[k], v) for k, v in raw_config.items() if k in keymap)
|
|
286
|
+
return config
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def saveconfig(filepath, config, keymap=None, delimiter='=', read_quoter=' "\'', write_quoter='"'):
|
|
290
|
+
"""Save config to file."""
|
|
291
|
+
raw_config, sortlist = raw_loadconfig(filepath, return_sort=True, delimiter=delimiter, quoter=read_quoter)
|
|
292
|
+
if raw_config is None:
|
|
293
|
+
return False
|
|
294
|
+
for k, v in config.items():
|
|
295
|
+
if keymap is None:
|
|
296
|
+
raw_config[k] = v
|
|
297
|
+
else:
|
|
298
|
+
if k in keymap:
|
|
299
|
+
raw_config[keymap[k]] = v
|
|
300
|
+
else:
|
|
301
|
+
return False
|
|
302
|
+
return raw_saveconfig(filepath, raw_config, sortlist, delimiter=delimiter, quoter=write_quoter)
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
def readconfig(filepath, readfunc, **params):
|
|
306
|
+
"""Read config from file.
|
|
307
|
+
"""
|
|
308
|
+
filepath = Path(filepath)
|
|
309
|
+
with open(filepath, encoding='utf-8') as f:
|
|
310
|
+
for line in f:
|
|
311
|
+
rt = readfunc(line.strip(), **params)
|
|
312
|
+
if rt is not None:
|
|
313
|
+
return rt
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
def writeconfig(filepath, readfunc, writefunc, **params):
|
|
317
|
+
"""Write config to file.
|
|
318
|
+
"""
|
|
319
|
+
filepath = Path(filepath)
|
|
320
|
+
lines = []
|
|
321
|
+
linemeet = False
|
|
322
|
+
with open(filepath, encoding='utf-8') as f:
|
|
323
|
+
for line in f:
|
|
324
|
+
rt = readfunc(line.strip(), **params)
|
|
325
|
+
if rt is not None:
|
|
326
|
+
linemeet = True
|
|
327
|
+
line = writefunc(line, **params)
|
|
328
|
+
if line is not None:
|
|
329
|
+
lines.append(line+'\n')
|
|
330
|
+
else:
|
|
331
|
+
lines.append(line)
|
|
332
|
+
|
|
333
|
+
# generate a new line if no line meet
|
|
334
|
+
if not linemeet:
|
|
335
|
+
line = writefunc(None, **params)
|
|
336
|
+
if line is not None:
|
|
337
|
+
lines.append(line+'\n')
|
|
338
|
+
|
|
339
|
+
with open(filepath, 'w', encoding='utf-8') as f:
|
|
340
|
+
f.writelines(lines)
|
|
341
|
+
return True
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
__all__ = ['load_config', 'runlogs_config', 'update_info_config', 'Config', 'raw_loadconfig', 'raw_saveconfig', 'loadconfig', 'saveconfig', 'readconfig', 'writeconfig']
|
|
345
|
+
|
|
346
|
+
if __name__ == '__main__':
|
|
347
|
+
import json
|
|
348
|
+
default_config = {
|
|
349
|
+
'1.1.1.1': {
|
|
350
|
+
'server': '1.1.1.1',
|
|
351
|
+
'port': 8023,
|
|
352
|
+
'accesskey': 'O64Td9EWEj8THu+RlHuoO8tjJzUmdsx37pTluP+aVc0='
|
|
353
|
+
},
|
|
354
|
+
'1.1.1.2': {
|
|
355
|
+
'server': '1.1.1.1',
|
|
356
|
+
'port': 8023,
|
|
357
|
+
'accesskey': 'O64Td9EWEj8THu+RlHuoO8tjJzUmdsx37pTluP+aVc0='
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
config = Config('data/clients.ini', {})
|
|
361
|
+
print(config)
|
|
362
|
+
print(config.get_section_list())
|
|
363
|
+
config.update()
|
|
364
|
+
config.addsection('accccc', {'abc': 2345, 'ccc': 34567654345})
|
|
365
|
+
config.addsections(default_config)
|
|
366
|
+
print(config.get_section_list())
|
|
367
|
+
# print(config.update())
|
|
368
|
+
print('abc')
|
|
369
|
+
config_list = config.get_config_list()
|
|
370
|
+
print(json.dumps(config_list))
|
|
371
|
+
config_dict = config.get_config()
|
|
372
|
+
print(json.dumps(config_dict))
|
inpanel/mod/cron.py
ADDED
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# Copyright (c) 2017-2026 Jackson Dou
|
|
4
|
+
# All rights reserved.
|
|
5
|
+
#
|
|
6
|
+
# InPanel is distributed under the terms of the (new) BSD License.
|
|
7
|
+
# The full license can be found in 'LICENSE'.
|
|
8
|
+
|
|
9
|
+
"""Module for Cron Jobs Management."""
|
|
10
|
+
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
import re
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
crontab = '/etc/crontab'
|
|
16
|
+
cronspool = '/var/spool/cron/'
|
|
17
|
+
cfg_map = {
|
|
18
|
+
'SHELL': 'shell',
|
|
19
|
+
'MAILTO': 'mailto',
|
|
20
|
+
'HOME': 'home',
|
|
21
|
+
'PATH': 'path'
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def load_config():
|
|
26
|
+
try:
|
|
27
|
+
if not Path(crontab).exists():
|
|
28
|
+
return {}
|
|
29
|
+
except OSError:
|
|
30
|
+
return {}
|
|
31
|
+
|
|
32
|
+
config = {}
|
|
33
|
+
with open(crontab, 'r', encoding='utf-8') as f:
|
|
34
|
+
lines = f.readlines()
|
|
35
|
+
|
|
36
|
+
while len(lines) > 0:
|
|
37
|
+
line = lines.pop(0)
|
|
38
|
+
out = line.strip()
|
|
39
|
+
if not out or out.startswith('#'):
|
|
40
|
+
continue
|
|
41
|
+
|
|
42
|
+
k = out.strip().split('=')[0]
|
|
43
|
+
if k and k in cfg_map:
|
|
44
|
+
config[cfg_map[k]] = out.split('=')[1]
|
|
45
|
+
|
|
46
|
+
return config
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def update_config(configs):
|
|
50
|
+
cmap_reverse = dict((v, k) for k, v in cfg_map.items())
|
|
51
|
+
new_config = {}
|
|
52
|
+
for k, v in configs.items():
|
|
53
|
+
if k in cmap_reverse:
|
|
54
|
+
new_config[cmap_reverse[k]] = v
|
|
55
|
+
return save_config(crontab, new_config)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def save_config(filepath, config):
|
|
59
|
+
try:
|
|
60
|
+
if not Path(filepath).exists():
|
|
61
|
+
return False
|
|
62
|
+
except OSError:
|
|
63
|
+
return False
|
|
64
|
+
|
|
65
|
+
with open(filepath, 'r', encoding='utf-8') as f:
|
|
66
|
+
lines = f.readlines()
|
|
67
|
+
|
|
68
|
+
output = []
|
|
69
|
+
while len(lines) > 0:
|
|
70
|
+
line = lines.pop(0)
|
|
71
|
+
out = line.strip()
|
|
72
|
+
if not out or out.startswith('#'):
|
|
73
|
+
output.append('%s' % (line))
|
|
74
|
+
continue
|
|
75
|
+
|
|
76
|
+
k = out.split('=')[0]
|
|
77
|
+
if k:
|
|
78
|
+
if k in config:
|
|
79
|
+
output.append('%s=%s\n' % (k, config[k]))
|
|
80
|
+
else:
|
|
81
|
+
output.append('%s' % (line))
|
|
82
|
+
|
|
83
|
+
with open(filepath, 'w', encoding='utf-8') as f:
|
|
84
|
+
f.writelines(output)
|
|
85
|
+
return True
|
|
86
|
+
|
|
87
|
+
return False
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def cron_list(level='normal', user=None):
|
|
91
|
+
'''
|
|
92
|
+
parser Cron config to python object (array)
|
|
93
|
+
return a list of cron jobs
|
|
94
|
+
'''
|
|
95
|
+
if level == 'normal':
|
|
96
|
+
if user is None:
|
|
97
|
+
return None
|
|
98
|
+
spool = str(Path(cronspool) / user)
|
|
99
|
+
elif level == 'system':
|
|
100
|
+
spool = crontab
|
|
101
|
+
|
|
102
|
+
try:
|
|
103
|
+
if not Path(spool).exists():
|
|
104
|
+
return None
|
|
105
|
+
except OSError:
|
|
106
|
+
return None
|
|
107
|
+
|
|
108
|
+
crons = []
|
|
109
|
+
with open(spool, encoding='utf-8') as f:
|
|
110
|
+
i = 0
|
|
111
|
+
for line in f:
|
|
112
|
+
line = line.strip()
|
|
113
|
+
line_user = ''
|
|
114
|
+
if re.findall(r"^\d|^\*|^\-", line):
|
|
115
|
+
if level == 'normal':
|
|
116
|
+
text = re.split(r'\s+', line, 5)
|
|
117
|
+
command = text[5]
|
|
118
|
+
line_user = user
|
|
119
|
+
elif level == 'system':
|
|
120
|
+
# this user's list
|
|
121
|
+
text = re.split(r'\s+', line, 6)
|
|
122
|
+
if user and user != text[5]:
|
|
123
|
+
continue
|
|
124
|
+
else:
|
|
125
|
+
line_user = text[5]
|
|
126
|
+
command = text[6]
|
|
127
|
+
else:
|
|
128
|
+
continue
|
|
129
|
+
i += 1
|
|
130
|
+
crons.append({
|
|
131
|
+
'id': i,
|
|
132
|
+
'minute': text[0],
|
|
133
|
+
'hour': text[1],
|
|
134
|
+
'day': text[2],
|
|
135
|
+
'month': text[3],
|
|
136
|
+
'weekday': text[4],
|
|
137
|
+
'command': command,
|
|
138
|
+
'user': line_user
|
|
139
|
+
})
|
|
140
|
+
return crons
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def cron_add(user, minute, hour, day, month, weekday, command, level):
|
|
144
|
+
'''add normal or system cron
|
|
145
|
+
'''
|
|
146
|
+
if level == 'system':
|
|
147
|
+
if user is None or user == '' or len(user) == 0:
|
|
148
|
+
return False
|
|
149
|
+
spool = crontab
|
|
150
|
+
line = "%s %s %s %s %s %s %s\n" % (minute, hour, day, month, weekday, user, command)
|
|
151
|
+
else:
|
|
152
|
+
user = user or 'root'
|
|
153
|
+
spool = str(Path(cronspool) / user)
|
|
154
|
+
line = "%s %s %s %s %s %s\n" % (minute, hour, day, month, weekday, command)
|
|
155
|
+
|
|
156
|
+
with open(spool, 'a+', encoding='utf-8') as f:
|
|
157
|
+
f.write(line)
|
|
158
|
+
return True
|
|
159
|
+
|
|
160
|
+
return False
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def cron_mod(user, id, minute, hour, day, month, weekday, command, level, currlist=''):
|
|
164
|
+
'''modify normal or system cron
|
|
165
|
+
'''
|
|
166
|
+
if user is None or id is None:
|
|
167
|
+
return False
|
|
168
|
+
if level == 'system':
|
|
169
|
+
spool = crontab
|
|
170
|
+
cron_line = "%s %s %s %s %s %s %s\n" % (minute, hour, day, month, weekday, user, command)
|
|
171
|
+
else:
|
|
172
|
+
spool = str(Path(cronspool) / user)
|
|
173
|
+
cron_line = "%s %s %s %s %s %s\n" % (minute, hour, day, month, weekday, command)
|
|
174
|
+
|
|
175
|
+
with open(spool, 'r', encoding='utf-8') as f:
|
|
176
|
+
lines = f.readlines()
|
|
177
|
+
|
|
178
|
+
i, j = 0, 0
|
|
179
|
+
for line in lines:
|
|
180
|
+
j += 1
|
|
181
|
+
if re.findall(r"^\d|^\*|^\-", line):
|
|
182
|
+
if level == 'normal':
|
|
183
|
+
i += 1
|
|
184
|
+
elif level == 'system':
|
|
185
|
+
# if currlist is this user's list
|
|
186
|
+
if currlist and currlist == user:
|
|
187
|
+
text = re.split(r'\s+', line, 6)
|
|
188
|
+
if user == text[5]:
|
|
189
|
+
i += 1
|
|
190
|
+
else:
|
|
191
|
+
i += 1
|
|
192
|
+
else:
|
|
193
|
+
continue
|
|
194
|
+
if str(i) == str(id):
|
|
195
|
+
lines[j-1] = cron_line
|
|
196
|
+
break
|
|
197
|
+
|
|
198
|
+
with open(spool, 'w+', encoding='utf-8') as f:
|
|
199
|
+
f.writelines(lines)
|
|
200
|
+
return True
|
|
201
|
+
|
|
202
|
+
return False
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def cron_del(user, id, level, currlist=''):
|
|
206
|
+
if user is None or id is None:
|
|
207
|
+
return False
|
|
208
|
+
spool = crontab if level == 'system' else str(Path(cronspool) / user)
|
|
209
|
+
|
|
210
|
+
with open(spool, 'r', encoding='utf-8') as f:
|
|
211
|
+
lines = f.readlines()
|
|
212
|
+
|
|
213
|
+
i, j = 0, 0
|
|
214
|
+
for line in lines:
|
|
215
|
+
j += 1
|
|
216
|
+
if re.findall(r"^\d|^\*|^\-", line):
|
|
217
|
+
if level == 'normal':
|
|
218
|
+
i += 1
|
|
219
|
+
elif level == 'system':
|
|
220
|
+
if currlist and currlist == user:
|
|
221
|
+
text = re.split(r'\s+', line, 6)
|
|
222
|
+
if user == text[5]:
|
|
223
|
+
i += 1
|
|
224
|
+
else:
|
|
225
|
+
i += 1
|
|
226
|
+
else:
|
|
227
|
+
continue
|
|
228
|
+
if str(i) == str(id):
|
|
229
|
+
del lines[j-1]
|
|
230
|
+
break
|
|
231
|
+
|
|
232
|
+
with open(spool, 'w+', encoding='utf-8') as f:
|
|
233
|
+
f.writelines(lines)
|
|
234
|
+
return True
|
|
235
|
+
|
|
236
|
+
return False
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def web_handler(context):
|
|
240
|
+
action = context.get_argument('action', '')
|
|
241
|
+
|
|
242
|
+
if action == 'get_settings':
|
|
243
|
+
context.write({'code': 0, 'msg': '获取 Cron 服务配置信息成功!', 'data': load_config()})
|
|
244
|
+
|
|
245
|
+
if action == 'save_settings':
|
|
246
|
+
mailto = context.get_argument('mailto', '')
|
|
247
|
+
rt = update_config({'mailto': mailto})
|
|
248
|
+
if rt:
|
|
249
|
+
context.write({'code': 0, 'msg': '设置保存成功!'})
|
|
250
|
+
else:
|
|
251
|
+
context.write({'code': -1, 'msg': '设置保存失败!'})
|
|
252
|
+
|
|
253
|
+
user = context.get_argument('user', '')
|
|
254
|
+
level = context.get_argument('level', 'normal')
|
|
255
|
+
|
|
256
|
+
if action == 'cron_list':
|
|
257
|
+
context.write({'code': 0, 'msg': '获取定时任务成功!', 'data': cron_list(user=user, level=level)})
|
|
258
|
+
|
|
259
|
+
elif action in ('cron_add', 'cron_mod'):
|
|
260
|
+
command = context.get_argument('command', '')
|
|
261
|
+
if command == '':
|
|
262
|
+
context.write({'code': -1, 'msg': '请输入命令!'})
|
|
263
|
+
return
|
|
264
|
+
|
|
265
|
+
if level == 'system' and user == '':
|
|
266
|
+
context.write({'code': -1, 'msg': '请输入用户'})
|
|
267
|
+
return
|
|
268
|
+
|
|
269
|
+
minute = context.get_argument('minute', '')
|
|
270
|
+
hour = context.get_argument('hour', '')
|
|
271
|
+
day = context.get_argument('day', '')
|
|
272
|
+
month = context.get_argument('month', '')
|
|
273
|
+
weekday = context.get_argument('weekday', '')
|
|
274
|
+
weekday = context.get_argument('weekday', '')
|
|
275
|
+
|
|
276
|
+
if action == 'cron_add':
|
|
277
|
+
if cron_add(user, minute, hour, day, month, weekday, command, level):
|
|
278
|
+
context.write({'code': 0, 'msg': '定时任务添加成功!'})
|
|
279
|
+
else:
|
|
280
|
+
context.write({'code': -1, 'msg': '定时任务添加失败!'})
|
|
281
|
+
|
|
282
|
+
elif action == 'cron_mod':
|
|
283
|
+
cronid = context.get_argument('cronid', '')
|
|
284
|
+
currlist = context.get_argument('currlist', '')
|
|
285
|
+
if cron_mod(user, cronid, minute, hour, day, month, weekday, command, level, currlist):
|
|
286
|
+
context.write({'code': 0, 'msg': '定时任务修改成功!'})
|
|
287
|
+
else:
|
|
288
|
+
context.write({'code': -1, 'msg': '定时任务修改失败!'})
|
|
289
|
+
|
|
290
|
+
elif action == 'cron_del':
|
|
291
|
+
cronid = context.get_argument('cronid', '')
|
|
292
|
+
currlist = context.get_argument('currlist', '')
|
|
293
|
+
if cron_del(user, cronid, level, currlist):
|
|
294
|
+
context.write({'code': 0, 'msg': '定时任务删除成功!'})
|
|
295
|
+
else:
|
|
296
|
+
context.write({'code': -1, 'msg': '定时任务删除失败!'})
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
if __name__ == "__main__":
|
|
300
|
+
import json
|
|
301
|
+
crontab = '/Users/douzhenjiang/test/inpanel/test/crontab'
|
|
302
|
+
cronspool = '/Users/douzhenjiang/test/inpanel/test/var_spool_cron'
|
|
303
|
+
# print crontab
|
|
304
|
+
# os.system("top")
|
|
305
|
+
# print loadconfig(crontab, cfg_map)
|
|
306
|
+
# print raw_loadconfig(crontab)
|
|
307
|
+
# print(load_config())
|
|
308
|
+
# print update_config({'shell': 'shelshelshel', 'home': 'homehomehome', 'path':'abc'})
|
|
309
|
+
# print dict((v, k) for k, v in cfg_map.items())
|
|
310
|
+
|
|
311
|
+
config = cron_list(level='system', user='root')
|
|
312
|
+
# config = cron_list(level='system', user='apache')
|
|
313
|
+
# config = cron_list(level='system')
|
|
314
|
+
print(json.dumps(config))
|
|
315
|
+
# print(cron_add('root', '*','*','*','*','*', 'command'))
|