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/disk.py
ADDED
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
#
|
|
4
|
+
# Copyright (c) 2017-2026 Jackson Dou
|
|
5
|
+
# All rights reserved.
|
|
6
|
+
#
|
|
7
|
+
# InPanel is distributed under the terms of the (new) BSD License.
|
|
8
|
+
# The full license can be found in 'LICENSE'.
|
|
9
|
+
|
|
10
|
+
'''Module for disk Management'''
|
|
11
|
+
|
|
12
|
+
import os
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from shlex import split
|
|
15
|
+
|
|
16
|
+
import pexpect
|
|
17
|
+
|
|
18
|
+
from .config import readconfig, writeconfig
|
|
19
|
+
from . import server
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def add(disk, size=''):
|
|
23
|
+
"""Add a new partition on a disk.
|
|
24
|
+
|
|
25
|
+
If the size exceed the max available space the disk left, then the
|
|
26
|
+
new partition will be created with the left space.
|
|
27
|
+
|
|
28
|
+
A disk can have 4 partitions at max.
|
|
29
|
+
|
|
30
|
+
True will return if create successfully, or else False will return.
|
|
31
|
+
|
|
32
|
+
Example:
|
|
33
|
+
fdisk.add('/dev/sdb') # use all of the space
|
|
34
|
+
fdisk.add('/dev/sdb', '5G') # create a partition with at most 5G space
|
|
35
|
+
"""
|
|
36
|
+
try:
|
|
37
|
+
cmd = split('fdisk \'%s\'' % disk)
|
|
38
|
+
except:
|
|
39
|
+
return False
|
|
40
|
+
|
|
41
|
+
child = pexpect.spawn(cmd[0], cmd[1:])
|
|
42
|
+
i = child.expect(['(m for help)', 'Unable to open'])
|
|
43
|
+
if i == 1:
|
|
44
|
+
if child.isalive():
|
|
45
|
+
child.wait()
|
|
46
|
+
return False
|
|
47
|
+
|
|
48
|
+
rt = True
|
|
49
|
+
partno_found = False
|
|
50
|
+
partno = 1
|
|
51
|
+
while not partno_found:
|
|
52
|
+
child.sendline('n')
|
|
53
|
+
i = child.expect(
|
|
54
|
+
['primary partition', 'You must delete some partition'])
|
|
55
|
+
if i == 1:
|
|
56
|
+
break
|
|
57
|
+
child.sendline('p')
|
|
58
|
+
|
|
59
|
+
i = child.expect(['Partition number', 'Selected partition'])
|
|
60
|
+
if i == 0:
|
|
61
|
+
child.sendline('%d' % partno)
|
|
62
|
+
|
|
63
|
+
i = child.expect(['First cylinder', '(m for help)'])
|
|
64
|
+
if i == 0:
|
|
65
|
+
partno_found = True
|
|
66
|
+
partno += 1
|
|
67
|
+
if partno > 4:
|
|
68
|
+
break
|
|
69
|
+
|
|
70
|
+
if not partno_found:
|
|
71
|
+
rt = False
|
|
72
|
+
|
|
73
|
+
if rt:
|
|
74
|
+
child.sendline('')
|
|
75
|
+
child.expect('Last cylinder')
|
|
76
|
+
child.sendline('+%s' % size)
|
|
77
|
+
i = child.expect(
|
|
78
|
+
['(m for help)', 'Value out of range', 'Last cylinder'])
|
|
79
|
+
if i == 1:
|
|
80
|
+
child.sendline('')
|
|
81
|
+
child.expect('(m for help)')
|
|
82
|
+
elif i == 2: # wrong size input
|
|
83
|
+
child.sendline('')
|
|
84
|
+
child.expect('(m for help)')
|
|
85
|
+
rt = False
|
|
86
|
+
|
|
87
|
+
if rt:
|
|
88
|
+
child.sendline('w')
|
|
89
|
+
else:
|
|
90
|
+
child.sendline('q')
|
|
91
|
+
|
|
92
|
+
if child.isalive():
|
|
93
|
+
child.wait()
|
|
94
|
+
return rt
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def delete(partition):
|
|
98
|
+
"""Delete a partition.
|
|
99
|
+
|
|
100
|
+
True will return if delete successfully, or else False will return.
|
|
101
|
+
|
|
102
|
+
Example:
|
|
103
|
+
fdisk.delete('/dev/sdb1')
|
|
104
|
+
"""
|
|
105
|
+
disk = partition[:-1]
|
|
106
|
+
partno = partition[-1:]
|
|
107
|
+
try:
|
|
108
|
+
cmd = split('fdisk \'%s\'' % disk)
|
|
109
|
+
except:
|
|
110
|
+
return False
|
|
111
|
+
|
|
112
|
+
child = pexpect.spawn(cmd[0], cmd[1:])
|
|
113
|
+
i = child.expect(['(m for help)', 'Unable to open'])
|
|
114
|
+
if i == 1:
|
|
115
|
+
if child.isalive():
|
|
116
|
+
child.wait()
|
|
117
|
+
return False
|
|
118
|
+
|
|
119
|
+
child.sendline('d')
|
|
120
|
+
|
|
121
|
+
rt = True
|
|
122
|
+
i = child.expect([
|
|
123
|
+
'Partition number',
|
|
124
|
+
'Selected partition %s' % partno,
|
|
125
|
+
'No partition is defined yet',
|
|
126
|
+
pexpect.TIMEOUT
|
|
127
|
+
], timeout=1)
|
|
128
|
+
if i == 0:
|
|
129
|
+
child.sendline(partno)
|
|
130
|
+
elif i == 2 or i == 3:
|
|
131
|
+
rt = False
|
|
132
|
+
|
|
133
|
+
if rt:
|
|
134
|
+
i = child.expect(['(m for help)', 'has empty type'])
|
|
135
|
+
if i == 0:
|
|
136
|
+
child.sendline('w')
|
|
137
|
+
elif i == 1:
|
|
138
|
+
rt = False
|
|
139
|
+
|
|
140
|
+
if not rt:
|
|
141
|
+
child.sendline('q')
|
|
142
|
+
|
|
143
|
+
if child.isalive():
|
|
144
|
+
child.wait()
|
|
145
|
+
return rt
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def scan(disk, size=''):
|
|
149
|
+
"""Rescan partitions on a disk.
|
|
150
|
+
|
|
151
|
+
True will return if scan successfully, or else False will return.
|
|
152
|
+
|
|
153
|
+
Example:
|
|
154
|
+
fdisk.scan('/dev/sdb')
|
|
155
|
+
"""
|
|
156
|
+
try:
|
|
157
|
+
cmd = split('fdisk \'%s\'' % disk)
|
|
158
|
+
except:
|
|
159
|
+
return False
|
|
160
|
+
|
|
161
|
+
child = pexpect.spawn(cmd[0], cmd[1:])
|
|
162
|
+
i = child.expect(['(m for help)', 'Unable to open'])
|
|
163
|
+
if i == 1:
|
|
164
|
+
child.wait()
|
|
165
|
+
return False
|
|
166
|
+
|
|
167
|
+
child.sendline('w')
|
|
168
|
+
i = child.expect([
|
|
169
|
+
'The kernel still uses the old table',
|
|
170
|
+
pexpect.TIMEOUT,
|
|
171
|
+
pexpect.EOF
|
|
172
|
+
], timeout=1)
|
|
173
|
+
if i == 0:
|
|
174
|
+
rt = False
|
|
175
|
+
else:
|
|
176
|
+
rt = True
|
|
177
|
+
|
|
178
|
+
if child.isalive():
|
|
179
|
+
child.wait()
|
|
180
|
+
return rt
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def handle_fdisk(action, devname, size='', unit=''):
|
|
184
|
+
"""Handle fdisk operations.
|
|
185
|
+
|
|
186
|
+
Args:
|
|
187
|
+
action: 'add', 'delete', or 'scan'
|
|
188
|
+
devname: device name (e.g., 'sdb')
|
|
189
|
+
size: partition size (e.g., '5')
|
|
190
|
+
unit: 'M' or 'G'
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
dict with 'code' and 'msg' keys
|
|
194
|
+
"""
|
|
195
|
+
if action == 'add':
|
|
196
|
+
if unit not in ('M', 'G'):
|
|
197
|
+
return {'code': -1, 'msg': '错误的分区大小!'}
|
|
198
|
+
|
|
199
|
+
if size == '':
|
|
200
|
+
size = None # use whole left space
|
|
201
|
+
else:
|
|
202
|
+
try:
|
|
203
|
+
size = float(size)
|
|
204
|
+
except:
|
|
205
|
+
return {'code': -1, 'msg': '错误的分区大小!'}
|
|
206
|
+
|
|
207
|
+
if unit == 'G' and size - int(size) > 0:
|
|
208
|
+
size *= 1024
|
|
209
|
+
unit = 'M'
|
|
210
|
+
size = '%d%s' % (round(size), unit)
|
|
211
|
+
|
|
212
|
+
if add('/dev/%s' % devname, size):
|
|
213
|
+
return {'code': 0, 'msg': '在 %s 设备上创建分区成功!' % devname}
|
|
214
|
+
else:
|
|
215
|
+
return {'code': -1, 'msg': '在 %s 设备上创建分区失败!' % devname}
|
|
216
|
+
|
|
217
|
+
elif action == 'delete':
|
|
218
|
+
if delete('/dev/%s' % devname):
|
|
219
|
+
fstab(devname, {'devname': devname, 'mount': None})
|
|
220
|
+
return {'code': 0, 'msg': '分区 %s 删除成功!' % devname}
|
|
221
|
+
else:
|
|
222
|
+
return {'code': -1, 'msg': '分区 %s 删除失败!' % devname}
|
|
223
|
+
|
|
224
|
+
elif action == 'scan':
|
|
225
|
+
if scan('/dev/%s' % devname):
|
|
226
|
+
return {'code': 0, 'msg': '扫描设备 %s 的分区成功!' % devname}
|
|
227
|
+
else:
|
|
228
|
+
return {'code': -1, 'msg': '扫描设备 %s 的分区失败!' % devname}
|
|
229
|
+
|
|
230
|
+
else:
|
|
231
|
+
return {'code': -1, 'msg': '未定义的操作!'}
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def _read_fstab(line, **params):
|
|
235
|
+
"""Read fstab config for a specific device.
|
|
236
|
+
|
|
237
|
+
Args:
|
|
238
|
+
line: a line from /etc/fstab
|
|
239
|
+
params: contains 'devname' parameter
|
|
240
|
+
|
|
241
|
+
Returns:
|
|
242
|
+
dict with config or None
|
|
243
|
+
"""
|
|
244
|
+
if not line or line.startswith('#'):
|
|
245
|
+
return
|
|
246
|
+
fields = line.split()
|
|
247
|
+
dev = fields[0]
|
|
248
|
+
config = {
|
|
249
|
+
'dev': fields[0],
|
|
250
|
+
'mount': fields[1],
|
|
251
|
+
'fstype': fields[2],
|
|
252
|
+
}
|
|
253
|
+
if dev.startswith('/dev/'):
|
|
254
|
+
try:
|
|
255
|
+
devlink = os.readlink(dev)
|
|
256
|
+
dev = str(Path(devlink).parent.joinpath(dev).resolve())
|
|
257
|
+
except:
|
|
258
|
+
pass
|
|
259
|
+
dev = dev.replace('/dev/', '')
|
|
260
|
+
if dev == params['devname']:
|
|
261
|
+
return config
|
|
262
|
+
elif dev.startswith('UUID='):
|
|
263
|
+
uuid = dev.replace('UUID=', '')
|
|
264
|
+
partinfo = server.ServerInfo.partinfo(devname=params['devname'])
|
|
265
|
+
if partinfo['uuid'] == uuid:
|
|
266
|
+
return config
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def _write_fstab(line, **params):
|
|
270
|
+
"""Write fstab config for a specific device.
|
|
271
|
+
|
|
272
|
+
Args:
|
|
273
|
+
line: a line from /etc/fstab (None for new line)
|
|
274
|
+
params: contains 'devname' and 'config' parameters
|
|
275
|
+
|
|
276
|
+
Returns:
|
|
277
|
+
formatted line string or None to remove
|
|
278
|
+
"""
|
|
279
|
+
config = params['config']
|
|
280
|
+
if not 'mount' in config or config['mount'] is None:
|
|
281
|
+
return None # remove line
|
|
282
|
+
if line is None: # new line
|
|
283
|
+
return '/dev/%s %s %s defaults 1 2' % \
|
|
284
|
+
(params['devname'], config['mount'], config['fstype'])
|
|
285
|
+
else: # update existing line
|
|
286
|
+
fields = line.split()
|
|
287
|
+
return '%s %s %s %s %s %s' % \
|
|
288
|
+
(fields[0], config['mount'],
|
|
289
|
+
'fstype' in config and config['fstype'] or fields[2],
|
|
290
|
+
fields[3], fields[4], fields[5])
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def fstab(devname, config=None):
|
|
294
|
+
"""Read or write config from /etc/fstab.
|
|
295
|
+
|
|
296
|
+
Args:
|
|
297
|
+
devname: device name (e.g., 'sda1')
|
|
298
|
+
config: None for read, dict for write/remove
|
|
299
|
+
|
|
300
|
+
Returns:
|
|
301
|
+
config dict on read, boolean on write
|
|
302
|
+
"""
|
|
303
|
+
cfgfile = '/etc/fstab'
|
|
304
|
+
if config is None:
|
|
305
|
+
# read config
|
|
306
|
+
return readconfig(cfgfile, _read_fstab, devname=devname)
|
|
307
|
+
else:
|
|
308
|
+
# write or remove config
|
|
309
|
+
return writeconfig(cfgfile, _read_fstab, _write_fstab,
|
|
310
|
+
devname=devname, config=config)
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
if __name__ == '__main__':
|
|
314
|
+
# !!!!!!!!!!! DANGEROUS TESTING !!!!!!!!!!!
|
|
315
|
+
# print('* Add partition to sdb with 5G:',)
|
|
316
|
+
# print(add('/dev/sdb', '5G'))
|
|
317
|
+
|
|
318
|
+
# print('* Delete partition /dev/sdb1:')
|
|
319
|
+
# print(delete('/dev/sdb1'))
|
|
320
|
+
|
|
321
|
+
print('* Rescan partitions of /dev/sdb:')
|
|
322
|
+
print(scan('/dev/sdb'))
|
inpanel/mod/dnf.py
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# Copyright (c) 2020-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
|
+
'''Module for DNF Repository Management'''
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
from .. import base
|
|
14
|
+
from . import config
|
|
15
|
+
from . import file
|
|
16
|
+
from ..dnf import dnf_reporpms
|
|
17
|
+
|
|
18
|
+
config_path = '/etc/yum.repos.d'
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def get_list():
|
|
22
|
+
'''get repo list'''
|
|
23
|
+
res = []
|
|
24
|
+
if base.kernel_name in ('Linux'):
|
|
25
|
+
d = str(Path(config_path))
|
|
26
|
+
if not Path(d).exists() or not Path(d).is_dir():
|
|
27
|
+
return None
|
|
28
|
+
items = sorted(os.listdir(d))
|
|
29
|
+
return items if len(items) > 0 else []
|
|
30
|
+
elif base.kernel_name in ('Darwin'):
|
|
31
|
+
return None
|
|
32
|
+
else:
|
|
33
|
+
return None
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def item_exists(repo):
|
|
37
|
+
'''exists'''
|
|
38
|
+
return Path(str(Path(config_path) / repo)).exists()
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def get_item(repo):
|
|
42
|
+
'''get repo config'''
|
|
43
|
+
if not repo:
|
|
44
|
+
return None
|
|
45
|
+
repo_file = str(Path(config_path) / repo)
|
|
46
|
+
if Path(repo_file).exists():
|
|
47
|
+
_config = config.Config(repo_file)
|
|
48
|
+
return _config.get_config()
|
|
49
|
+
return None
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def set_item(repo, data):
|
|
53
|
+
'''set repo config'''
|
|
54
|
+
if not repo:
|
|
55
|
+
return None
|
|
56
|
+
if not data:
|
|
57
|
+
return None
|
|
58
|
+
repo_file = str(Path(config_path) / repo)
|
|
59
|
+
if Path(repo_file).exists():
|
|
60
|
+
_config = config.Config(repo_file, data)
|
|
61
|
+
return True
|
|
62
|
+
else:
|
|
63
|
+
return False
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def add_item(repo, data):
|
|
67
|
+
'''add repo config'''
|
|
68
|
+
if not repo:
|
|
69
|
+
return None
|
|
70
|
+
if not data:
|
|
71
|
+
return None
|
|
72
|
+
repo_file = str(Path(config_path) / repo)
|
|
73
|
+
if Path(repo_file).exists():
|
|
74
|
+
return False
|
|
75
|
+
else:
|
|
76
|
+
_config = config.Config(repo_file, data)
|
|
77
|
+
return True
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def del_item(repo):
|
|
81
|
+
'''delete repo file'''
|
|
82
|
+
if not repo:
|
|
83
|
+
return None
|
|
84
|
+
repo_file = str(Path(config_path) / repo)
|
|
85
|
+
return file.delete(repo_file)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def get_repo_release(os_versint, os_name, arch):
|
|
89
|
+
'''install release'''
|
|
90
|
+
cmds = []
|
|
91
|
+
if os_versint >= 8:
|
|
92
|
+
if os_name in ('centos', 'almalinux', 'rocky', 'rhel', 'fedora'):
|
|
93
|
+
cmds.append('dnf install -y %s-release' % os_name)
|
|
94
|
+
else:
|
|
95
|
+
for rpm in dnf_reporpms['base'][os_versint][arch]:
|
|
96
|
+
cmds.append('rpm -U %s' % rpm)
|
|
97
|
+
return cmds
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def get_repo_epel(os_versint, os_name, arch):
|
|
101
|
+
'''install epel'''
|
|
102
|
+
cmds = []
|
|
103
|
+
if os_versint >= 8:
|
|
104
|
+
cmds.append('dnf install -y epel-release')
|
|
105
|
+
else:
|
|
106
|
+
for rpm in dnf_reporpms['epel'][os_versint][arch]:
|
|
107
|
+
cmds.append('rpm -U %s' % rpm)
|
|
108
|
+
return cmds
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def get_repo_powertools(os_versint, os_name, arch):
|
|
112
|
+
'''install powertools/crb'''
|
|
113
|
+
cmds = []
|
|
114
|
+
if os_versint == 8:
|
|
115
|
+
cmds.append('dnf install -y dnf-plugins-core')
|
|
116
|
+
cmds.append('dnf config-manager --set-enabled powertools')
|
|
117
|
+
elif os_versint >= 9:
|
|
118
|
+
cmds.append('dnf install -y dnf-plugins-core')
|
|
119
|
+
cmds.append('dnf config-manager --set-enabled crb')
|
|
120
|
+
return cmds
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def get_repo_remi(os_versint, os_name, arch):
|
|
124
|
+
'''install remi repository'''
|
|
125
|
+
cmds = []
|
|
126
|
+
if os_versint >= 8:
|
|
127
|
+
cmds.append('dnf install -y https://rpms.remirepo.net/enterprise/remi-release-%d.rpm' % os_versint)
|
|
128
|
+
return cmds
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def web_handler(context):
|
|
132
|
+
'''Handle web requests for DNF repository management'''
|
|
133
|
+
action = context.get('action', '')
|
|
134
|
+
repo = context.get('repo', '')
|
|
135
|
+
|
|
136
|
+
if action == 'list':
|
|
137
|
+
items = get_list()
|
|
138
|
+
if items is None:
|
|
139
|
+
return {'code': -1, 'msg': '获取配置失败!'}
|
|
140
|
+
else:
|
|
141
|
+
return {'code': 0, 'msg': '', 'data': items}
|
|
142
|
+
|
|
143
|
+
elif action == 'item':
|
|
144
|
+
if not repo:
|
|
145
|
+
return {'code': -1, 'msg': '配置文件不能为空!'}
|
|
146
|
+
data = get_item(repo)
|
|
147
|
+
if data is None:
|
|
148
|
+
return {'code': -1, 'msg': '配置文件不存在!'}
|
|
149
|
+
else:
|
|
150
|
+
return {'code': 0, 'msg': '', 'data': data}
|
|
151
|
+
|
|
152
|
+
elif action == 'add':
|
|
153
|
+
if not repo:
|
|
154
|
+
return {'code': -1, 'msg': '配置文件不能为空!'}
|
|
155
|
+
serverid = context.get('serverid', '')
|
|
156
|
+
if not serverid:
|
|
157
|
+
return {'code': -1, 'msg': '仓库标识ID不能为空!'}
|
|
158
|
+
name = context.get('name', '')
|
|
159
|
+
if not name:
|
|
160
|
+
return {'code': -1, 'msg': '仓库名称不能为空!'}
|
|
161
|
+
baseurl = context.get('baseurl', '')
|
|
162
|
+
if not baseurl:
|
|
163
|
+
return {'code': -1, 'msg': '仓库路径不能为空!'}
|
|
164
|
+
|
|
165
|
+
enabled = context.get('enabled', True)
|
|
166
|
+
gpgcheck = context.get('gpgcheck', False)
|
|
167
|
+
|
|
168
|
+
data = {
|
|
169
|
+
serverid: {
|
|
170
|
+
'name': name,
|
|
171
|
+
'enabled': 0 if not enabled else 1,
|
|
172
|
+
'baseurl': baseurl,
|
|
173
|
+
'gpgcheck': 0 if not gpgcheck else 1,
|
|
174
|
+
'gpgkey': ''
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if item_exists(repo):
|
|
179
|
+
return {'code': -1, 'msg': '配置文件已存在!'}
|
|
180
|
+
if add_item(repo, data) is True:
|
|
181
|
+
return {'code': 0, 'msg': '配置添加成功!'}
|
|
182
|
+
else:
|
|
183
|
+
return {'code': -1, 'msg': '配置添加失败!'}
|
|
184
|
+
|
|
185
|
+
elif action == 'edit':
|
|
186
|
+
if not repo:
|
|
187
|
+
return {'code': -1, 'msg': '配置文件不能为空!'}
|
|
188
|
+
serverid = context.get('serverid', '')
|
|
189
|
+
if not serverid:
|
|
190
|
+
return {'code': -1, 'msg': '仓库标识ID不能为空!'}
|
|
191
|
+
name = context.get('name', '')
|
|
192
|
+
if not name:
|
|
193
|
+
return {'code': -1, 'msg': '仓库名称不能为空!'}
|
|
194
|
+
baseurl = context.get('baseurl', '')
|
|
195
|
+
if not baseurl:
|
|
196
|
+
return {'code': -1, 'msg': '仓库路径不能为空!'}
|
|
197
|
+
|
|
198
|
+
enabled = context.get('enabled', True)
|
|
199
|
+
gpgcheck = context.get('gpgcheck', False)
|
|
200
|
+
|
|
201
|
+
data = {
|
|
202
|
+
serverid: {
|
|
203
|
+
'name': name,
|
|
204
|
+
'enabled': 0 if not enabled else 1,
|
|
205
|
+
'baseurl': baseurl,
|
|
206
|
+
'gpgcheck': 0 if not gpgcheck else 1,
|
|
207
|
+
'gpgkey': ''
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if not item_exists(repo):
|
|
212
|
+
return {'code': -1, 'msg': '配置文件不存在!'}
|
|
213
|
+
if set_item(repo, data) is True:
|
|
214
|
+
return {'code': 0, 'msg': '配置修改成功!'}
|
|
215
|
+
else:
|
|
216
|
+
return {'code': -1, 'msg': '配置修改失败!'}
|
|
217
|
+
|
|
218
|
+
elif action == 'del':
|
|
219
|
+
if not repo:
|
|
220
|
+
return {'code': -1, 'msg': '配置文件不能为空!'}
|
|
221
|
+
if not item_exists(repo):
|
|
222
|
+
return {'code': -1, 'msg': '配置文件不存在!'}
|
|
223
|
+
if del_item(repo) is True:
|
|
224
|
+
return {'code': 0, 'msg': '配置文件已移入回收站!'}
|
|
225
|
+
else:
|
|
226
|
+
return {'code': -1, 'msg': '删除失败!'}
|
|
227
|
+
|
|
228
|
+
else:
|
|
229
|
+
return {'code': -1, 'msg': '未定义的操作!'}
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
if __name__ == '__main__':
|
|
233
|
+
l = get_list()
|
|
234
|
+
print(l)
|
|
235
|
+
if l:
|
|
236
|
+
i = l[0]
|
|
237
|
+
print(i)
|
|
238
|
+
c = get_item(i)
|
|
239
|
+
print(c)
|