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.
Files changed (198) hide show
  1. inpanel/__init__.py +29 -0
  2. inpanel/acme.py +289 -0
  3. inpanel/aliyuncs.py +269 -0
  4. inpanel/app.py +514 -0
  5. inpanel/apt.py +223 -0
  6. inpanel/base.py +200 -0
  7. inpanel/config.py +139 -0
  8. inpanel/data/config.ini +29 -0
  9. inpanel/data/history +5 -0
  10. inpanel/data/runlogs.ini +12 -0
  11. inpanel/data/update_info.ini +4 -0
  12. inpanel/dnf.py +225 -0
  13. inpanel/lib/__init__.py +9 -0
  14. inpanel/lib/async_process.py +98 -0
  15. inpanel/lib/filelock.py +82 -0
  16. inpanel/lib/magic.py +233 -0
  17. inpanel/lib/pxssh.py +310 -0
  18. inpanel/lib/pyDes.py +867 -0
  19. inpanel/mod/__init__.py +0 -0
  20. inpanel/mod/apt.py +316 -0
  21. inpanel/mod/certificate.py +268 -0
  22. inpanel/mod/config.py +372 -0
  23. inpanel/mod/cron.py +315 -0
  24. inpanel/mod/disk.py +322 -0
  25. inpanel/mod/dnf.py +239 -0
  26. inpanel/mod/file.py +722 -0
  27. inpanel/mod/firewall.py +199 -0
  28. inpanel/mod/firewall_base.py +137 -0
  29. inpanel/mod/firewall_firewalld.py +170 -0
  30. inpanel/mod/firewall_iptables.py +176 -0
  31. inpanel/mod/firewall_macos.py +213 -0
  32. inpanel/mod/firewall_ufw.py +128 -0
  33. inpanel/mod/ftp.py +231 -0
  34. inpanel/mod/httpd.py +1205 -0
  35. inpanel/mod/lighttpd.py +26 -0
  36. inpanel/mod/login.py +98 -0
  37. inpanel/mod/mysql.py +577 -0
  38. inpanel/mod/named.py +26 -0
  39. inpanel/mod/nginx.py +2327 -0
  40. inpanel/mod/package.py +33 -0
  41. inpanel/mod/package_apt.py +62 -0
  42. inpanel/mod/package_base.py +77 -0
  43. inpanel/mod/package_dnf.py +62 -0
  44. inpanel/mod/package_map.py +366 -0
  45. inpanel/mod/package_yum.py +64 -0
  46. inpanel/mod/php.py +289 -0
  47. inpanel/mod/process.py +291 -0
  48. inpanel/mod/proftpd.py +26 -0
  49. inpanel/mod/pureftpd.py +26 -0
  50. inpanel/mod/query.py +108 -0
  51. inpanel/mod/server.py +1319 -0
  52. inpanel/mod/service.py +262 -0
  53. inpanel/mod/setting.py +228 -0
  54. inpanel/mod/shell.py +78 -0
  55. inpanel/mod/ssh.py +280 -0
  56. inpanel/mod/system.py +220 -0
  57. inpanel/mod/task.py +1337 -0
  58. inpanel/mod/ufw.py +14 -0
  59. inpanel/mod/user.py +306 -0
  60. inpanel/mod/vsftpd.py +67 -0
  61. inpanel/mod/yum.py +243 -0
  62. inpanel/plugins/__init__.py +10 -0
  63. inpanel/plugins/__pycache__/__init__.cpython-313.pyc +0 -0
  64. inpanel/plugins/acme/static/index.html +10 -0
  65. inpanel/plugins/acme/static/index.js +13 -0
  66. inpanel/plugins/shadowsocks-libev/__pycache__/info.cpython-313.pyc +0 -0
  67. inpanel/plugins/shadowsocks-libev/info.py +73 -0
  68. inpanel/plugins/shadowsocks-libev/static/index.html +70 -0
  69. inpanel/plugins/shadowsocks-libev/static/index.js +18 -0
  70. inpanel/public/css/global.css +138 -0
  71. inpanel/public/favicon.ico +0 -0
  72. inpanel/public/images/alipay.jpg +0 -0
  73. inpanel/public/images/loading.gif +0 -0
  74. inpanel/public/images/weixin.jpg +0 -0
  75. inpanel/public/index.html +94 -0
  76. inpanel/public/js/controllers/controllers.js +566 -0
  77. inpanel/public/js/controllers/database.js +590 -0
  78. inpanel/public/js/controllers/ecs.js +624 -0
  79. inpanel/public/js/controllers/file.js +940 -0
  80. inpanel/public/js/controllers/plugins.js +105 -0
  81. inpanel/public/js/controllers/service.js +1460 -0
  82. inpanel/public/js/controllers/site.js +1241 -0
  83. inpanel/public/js/controllers/utils.js +2319 -0
  84. inpanel/public/js/core.js +221 -0
  85. inpanel/public/js/directives.js +784 -0
  86. inpanel/public/js/filters.js +395 -0
  87. inpanel/public/js/services.js +257 -0
  88. inpanel/public/lib/angular/1.0.2/angular-resource.min.js +10 -0
  89. inpanel/public/lib/angular/1.0.2/angular.min.js +158 -0
  90. inpanel/public/lib/bootstrap/css/bootstrap-theme.css +587 -0
  91. inpanel/public/lib/bootstrap/css/bootstrap-theme.css.map +1 -0
  92. inpanel/public/lib/bootstrap/css/bootstrap-theme.min.css +6 -0
  93. inpanel/public/lib/bootstrap/css/bootstrap-theme.min.css.map +1 -0
  94. inpanel/public/lib/bootstrap/css/bootstrap.css +6757 -0
  95. inpanel/public/lib/bootstrap/css/bootstrap.css.map +1 -0
  96. inpanel/public/lib/bootstrap/css/bootstrap.min.css +6 -0
  97. inpanel/public/lib/bootstrap/css/bootstrap.min.css.map +1 -0
  98. inpanel/public/lib/bootstrap/fonts/glyphicons-halflings-regular.eot +0 -0
  99. inpanel/public/lib/bootstrap/fonts/glyphicons-halflings-regular.svg +288 -0
  100. inpanel/public/lib/bootstrap/fonts/glyphicons-halflings-regular.ttf +0 -0
  101. inpanel/public/lib/bootstrap/fonts/glyphicons-halflings-regular.woff +0 -0
  102. inpanel/public/lib/bootstrap/fonts/glyphicons-halflings-regular.woff2 +0 -0
  103. inpanel/public/lib/bootstrap/img/glyphicons-halflings-white.png +0 -0
  104. inpanel/public/lib/bootstrap/img/glyphicons-halflings.png +0 -0
  105. inpanel/public/lib/bootstrap/js/bootstrap.js +2377 -0
  106. inpanel/public/lib/bootstrap/js/bootstrap.min.js +7 -0
  107. inpanel/public/lib/bootstrap/js/npm.js +13 -0
  108. inpanel/public/lib/codemirror/codemirror-compressed.js +1 -0
  109. inpanel/public/lib/codemirror/codemirror.css +173 -0
  110. inpanel/public/lib/jquery/jquery-1.12.4.min.js +5 -0
  111. inpanel/public/lib/md5/md5.js +379 -0
  112. inpanel/public/lib/md5/md5.min.js +1 -0
  113. inpanel/public/partials/database/index.html +172 -0
  114. inpanel/public/partials/database/mysql/dbedit.html +462 -0
  115. inpanel/public/partials/database/mysql/dbnew.html +348 -0
  116. inpanel/public/partials/database/mysql/useredit.html +310 -0
  117. inpanel/public/partials/database/mysql/usernew.html +97 -0
  118. inpanel/public/partials/directives/message.html +20 -0
  119. inpanel/public/partials/directives/navbar.html +25 -0
  120. inpanel/public/partials/directives/selector.html +57 -0
  121. inpanel/public/partials/directives/srvbase.html +72 -0
  122. inpanel/public/partials/directives/srvext.html +34 -0
  123. inpanel/public/partials/directives/srvfile.html +23 -0
  124. inpanel/public/partials/directives/srvinstall.html +34 -0
  125. inpanel/public/partials/directives/srvlog.html +23 -0
  126. inpanel/public/partials/directives/srvminiop.html +28 -0
  127. inpanel/public/partials/directives/srvuninstall.html +38 -0
  128. inpanel/public/partials/directives/srvupdate.html +30 -0
  129. inpanel/public/partials/ecs/account.html +156 -0
  130. inpanel/public/partials/ecs/ecs.html +198 -0
  131. inpanel/public/partials/ecs/index.html +40 -0
  132. inpanel/public/partials/ecs/setings.html +459 -0
  133. inpanel/public/partials/file/file.html +501 -0
  134. inpanel/public/partials/file/trash.html +95 -0
  135. inpanel/public/partials/ftp.html +36 -0
  136. inpanel/public/partials/log.html +83 -0
  137. inpanel/public/partials/login.html +79 -0
  138. inpanel/public/partials/logout.html +22 -0
  139. inpanel/public/partials/main.html +534 -0
  140. inpanel/public/partials/plugins/index.html +55 -0
  141. inpanel/public/partials/plugins/plugins.html +13 -0
  142. inpanel/public/partials/secure.html +13 -0
  143. inpanel/public/partials/service/apache.html +147 -0
  144. inpanel/public/partials/service/cron.html +71 -0
  145. inpanel/public/partials/service/index.html +293 -0
  146. inpanel/public/partials/service/iptables.html +40 -0
  147. inpanel/public/partials/service/lighttpd.html +63 -0
  148. inpanel/public/partials/service/mariadb.html +113 -0
  149. inpanel/public/partials/service/memcache.html +36 -0
  150. inpanel/public/partials/service/minio.html +44 -0
  151. inpanel/public/partials/service/mongodb.html +44 -0
  152. inpanel/public/partials/service/mysql.html +113 -0
  153. inpanel/public/partials/service/named.html +61 -0
  154. inpanel/public/partials/service/nginx.html +296 -0
  155. inpanel/public/partials/service/ntp.html +40 -0
  156. inpanel/public/partials/service/php.html +250 -0
  157. inpanel/public/partials/service/proftpd.html +59 -0
  158. inpanel/public/partials/service/pureftpd.html +59 -0
  159. inpanel/public/partials/service/redis.html +40 -0
  160. inpanel/public/partials/service/samba.html +58 -0
  161. inpanel/public/partials/service/sendmail.html +40 -0
  162. inpanel/public/partials/service/ssh.html +186 -0
  163. inpanel/public/partials/service/tomcat.html +65 -0
  164. inpanel/public/partials/service/vsftpd.html +195 -0
  165. inpanel/public/partials/setting.html +299 -0
  166. inpanel/public/partials/site/apache/site.html +277 -0
  167. inpanel/public/partials/site/index.html +344 -0
  168. inpanel/public/partials/site/nginx/site.html +558 -0
  169. inpanel/public/partials/sorry.html +19 -0
  170. inpanel/public/partials/storage/autofm.html +103 -0
  171. inpanel/public/partials/storage/backup.html +42 -0
  172. inpanel/public/partials/storage/index.html +360 -0
  173. inpanel/public/partials/storage/migrate.html +93 -0
  174. inpanel/public/partials/storage/movedata.html +98 -0
  175. inpanel/public/partials/storage/remote.html +77 -0
  176. inpanel/public/partials/utils/cron.html +486 -0
  177. inpanel/public/partials/utils/firewall.html +266 -0
  178. inpanel/public/partials/utils/index.html +193 -0
  179. inpanel/public/partials/utils/network.html +118 -0
  180. inpanel/public/partials/utils/process.html +239 -0
  181. inpanel/public/partials/utils/repository.html +373 -0
  182. inpanel/public/partials/utils/shell.html +28 -0
  183. inpanel/public/partials/utils/ssl.html +319 -0
  184. inpanel/public/partials/utils/time.html +109 -0
  185. inpanel/public/partials/utils/user.html +359 -0
  186. inpanel/public/robots.txt +2 -0
  187. inpanel/remote.py +109 -0
  188. inpanel/templates/file/preview.html +34 -0
  189. inpanel/templates/index.html +93 -0
  190. inpanel/utils.py +282 -0
  191. inpanel/web.py +1811 -0
  192. inpanel/yum.py +756 -0
  193. inpanel-1.2.1.dist-info/METADATA +399 -0
  194. inpanel-1.2.1.dist-info/RECORD +198 -0
  195. inpanel-1.2.1.dist-info/WHEEL +5 -0
  196. inpanel-1.2.1.dist-info/entry_points.txt +2 -0
  197. inpanel-1.2.1.dist-info/licenses/LICENSE +30 -0
  198. inpanel-1.2.1.dist-info/top_level.txt +1 -0
inpanel/mod/server.py ADDED
@@ -0,0 +1,1319 @@
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
+ '''Module for reading and writing Server Information'''
9
+
10
+ import datetime
11
+ import fcntl
12
+ import glob
13
+ import hashlib
14
+ import multiprocessing
15
+ import os
16
+ from pathlib import Path
17
+ import re
18
+ import shlex
19
+ import shutil
20
+ import socket
21
+ import struct
22
+ import pwd
23
+ import psutil
24
+ from subprocess import PIPE, Popen
25
+ import time
26
+ from xml.dom.minidom import parseString
27
+
28
+ from ..base import (
29
+ hostname, kernel_name, os_name, os_title, os_versint, server_info,
30
+ run_type, version_info, config_file, root_path, pidfile, logfile, logerror, execfile
31
+ )
32
+ from .. import utils
33
+ from ..utils import b2h
34
+
35
+ from .config import load_config, loadconfig, raw_loadconfig, raw_saveconfig, saveconfig
36
+ from .shell import run
37
+
38
+ def strfdelta(tdelta, fmt):
39
+ d = {'days': tdelta.days}
40
+ d['hours'], rem = divmod(tdelta.seconds, 3600)
41
+ d['minutes'], d['seconds'] = divmod(rem, 60)
42
+ return fmt.format(**d)
43
+
44
+
45
+ def div_percent(a, b):
46
+ if b == 0:
47
+ return '0%'
48
+ return '%.2f%%' % (round(float(a) / b, 4) * 100)
49
+
50
+
51
+ class ServerInfo(object):
52
+
53
+ # item : realtime update
54
+ server_items = {
55
+ 'hostname' : False,
56
+ 'datetime' : True,
57
+ 'uptime' : True,
58
+ 'loadavg' : True,
59
+ 'cpustat' : True,
60
+ 'meminfo' : True,
61
+ 'mounts' : True,
62
+ 'netifaces' : True,
63
+ 'nameservers' : True,
64
+ 'distribution' : False,
65
+ 'uname' : False,
66
+ 'cpuinfo' : False,
67
+ 'diskinfo' : False,
68
+ 'virt' : False,
69
+ 'panelinfo' : True,
70
+ }
71
+
72
+ @classmethod
73
+ def hostname(self):
74
+ return hostname
75
+
76
+ @classmethod
77
+ def datetime(self, asstruct=False):
78
+ if not asstruct:
79
+ return time.strftime('%Y-%m-%d %X %Z')
80
+ else:
81
+ d = time.localtime()
82
+ return {
83
+ 'year': d.tm_year,
84
+ 'mon': d.tm_mon,
85
+ 'mday': d.tm_mday,
86
+ 'hour': d.tm_hour,
87
+ 'min': d.tm_min,
88
+ 'sec': d.tm_sec,
89
+ 'tz': time.strftime('%Z', d),
90
+ 'str': time.strftime('%Y-%m-%d %X', d),
91
+ }
92
+
93
+ @classmethod
94
+ def uptime(self):
95
+ if kernel_name == 'Darwin':
96
+ return {
97
+ 'up': 0,
98
+ 'idle': 0,
99
+ 'idle_rate': 0,
100
+ }
101
+ with open('/proc/uptime', 'r', encoding='utf-8') as f:
102
+ uptime, idletime = f.readline().split()
103
+ up_seconds = int(float(uptime))
104
+ idle_seconds = int(float(idletime))
105
+ # in some machine like Linode VPS, idle time may bigger than up time
106
+ if idle_seconds > up_seconds:
107
+ cpu_count = multiprocessing.cpu_count()
108
+ idle_seconds = idle_seconds / cpu_count
109
+ # in some VPS, this value may still bigger than up time
110
+ # may be the domain 0 machine has more cores
111
+ # we calclate approximately for it
112
+ if idle_seconds > up_seconds:
113
+ for n in range(2, 10):
114
+ if idle_seconds / n < up_seconds:
115
+ idle_seconds = idle_seconds / n
116
+ break
117
+ fmt = '{days} 天 {hours} 小时 {minutes} 分 {seconds} 秒'
118
+ uptime_string = strfdelta(datetime.timedelta(seconds=up_seconds), fmt)
119
+ idletime_string = strfdelta(datetime.timedelta(seconds=idle_seconds), fmt)
120
+ return {
121
+ 'up': uptime_string,
122
+ 'idle': idletime_string,
123
+ 'idle_rate': div_percent(idle_seconds, up_seconds),
124
+ }
125
+
126
+ @classmethod
127
+ def loadavg(self):
128
+ loadavg = {
129
+ '1min': 0,
130
+ '5min': 0,
131
+ '15min': 0
132
+ }
133
+ if kernel_name == 'Linux':
134
+ with open('/proc/loadavg', 'r', encoding='utf-8') as f:
135
+ load_1min, load_5min, load_15min = f.readline().split()[0:3]
136
+ loadavg['1min'] = load_1min
137
+ loadavg['5min'] = load_5min
138
+ loadavg['15min'] = load_15min
139
+ elif kernel_name == 'Darwin': pass
140
+ elif kernel_name == 'Windows': pass
141
+
142
+ return loadavg
143
+
144
+ @classmethod
145
+ def cpustat(self, fullstat=False):
146
+ cpustat = {
147
+ 'cpus': [],
148
+ 'total': 0,
149
+ 'btime': 0,
150
+ }
151
+ if kernel_name == 'Linux':
152
+ # REF: http://www.kernel.org/doc/Documentation/filesystems/proc.txt
153
+ fname = ('used', 'idle')
154
+ full_fname = ('user', 'nice', 'system', 'idle', 'iowait', 'irq',
155
+ 'softirq', 'steal', 'guest', 'guest_nice')
156
+ cpustat['cpus'] = []
157
+ with open('/proc/stat', 'r', encoding='utf-8') as f:
158
+ for line in f:
159
+ if line.startswith('cpu'):
160
+ fields = line.strip().split()
161
+ name = fields[0]
162
+ if not fullstat and name != 'cpu':
163
+ continue
164
+ stat = fields[1:]
165
+ stat = [int(i) for i in stat]
166
+ statall = sum(stat)
167
+ if fullstat:
168
+ while len(stat) < 10:
169
+ stat.append(0)
170
+ stat = dict(zip(full_fname, stat))
171
+ else:
172
+ stat = [statall - stat[3], stat[3]]
173
+ stat = dict(zip(fname, stat))
174
+ stat['all'] = statall
175
+ if name == 'cpu':
176
+ cpustat['total'] = stat
177
+ else:
178
+ cpustat['cpus'].append(stat)
179
+ elif line.startswith('btime'):
180
+ btime = int(line.strip().split()[1])
181
+ cpustat['btime'] = time.strftime('%Y-%m-%d %X %Z', time.localtime(btime))
182
+ elif kernel_name == 'Darwin': pass
183
+ elif kernel_name == 'Windows': pass
184
+
185
+ return cpustat
186
+
187
+ @classmethod
188
+ def meminfo(self):
189
+ # OpenVZ may not have some varirables
190
+ # so init them first
191
+ mem_total = 0
192
+ mem_free = 0
193
+ mem_available = 0
194
+ mem_buffers = 0
195
+ mem_cached = 0
196
+ mem_slab = 0
197
+ swap_total = 0
198
+ swap_free = 0
199
+ swap_swappiness = 0
200
+ mem_available_computed = 0
201
+
202
+ if kernel_name == 'Linux':
203
+ with open('/proc/meminfo', 'r', encoding='utf-8') as f:
204
+ for line in f:
205
+ if ':' not in line:
206
+ continue
207
+ item, value = line.split(':')
208
+ value = int(value.split()[0]) * 1024
209
+ if item == 'MemTotal':
210
+ mem_total = value
211
+ elif item == 'MemFree':
212
+ mem_free = value
213
+ elif item == 'MemAvailable':
214
+ mem_available = value
215
+ elif item == 'Buffers':
216
+ mem_buffers = value
217
+ elif item == 'Cached':
218
+ mem_cached = value
219
+ elif item == 'Slab':
220
+ mem_slab = value
221
+ elif item == 'SwapTotal':
222
+ swap_total = value
223
+ elif item == 'SwapFree':
224
+ swap_free = value
225
+ with open('/proc/sys/vm/swappiness', 'r', encoding='utf-8') as f:
226
+ swap_swappiness = f.readline()
227
+ elif kernel_name == 'Darwin': pass
228
+ elif kernel_name == 'Windows': pass
229
+
230
+ mem_used = mem_total - mem_free
231
+ swap_used = swap_total - swap_free
232
+ if not mem_available:
233
+ # MemAvailable ≈ MemFree + Buffers + Cached
234
+ mem_available_computed = mem_free + mem_buffers + mem_cached
235
+
236
+ return {
237
+ 'mem_total': b2h(mem_total),
238
+ 'mem_used': b2h(mem_used),
239
+ 'mem_free': b2h(mem_free),
240
+ 'mem_available': b2h(mem_available),
241
+ 'mem_available_computed': b2h(mem_available_computed),
242
+ 'mem_buffers': b2h(mem_buffers),
243
+ 'mem_cached': b2h(mem_cached),
244
+ 'mem_slab': b2h(mem_slab),
245
+ 'swap_total': b2h(swap_total),
246
+ 'swap_used': b2h(swap_used),
247
+ 'swap_free': b2h(swap_free),
248
+ 'swap_swappiness': swap_swappiness,
249
+ 'mem_used_rate': div_percent(mem_used, mem_total),
250
+ 'mem_free_rate': div_percent(mem_free, mem_total),
251
+ 'mem_available_rate': div_percent(mem_available, mem_total),
252
+ 'mem_available_computed_rate': div_percent(mem_available_computed, mem_total),
253
+ 'swap_used_rate': div_percent(swap_used, swap_total),
254
+ 'swap_free_rate': div_percent(swap_free, swap_total),
255
+ }
256
+
257
+ @classmethod
258
+ def mounts(self, detectdev=False):
259
+ _mounts = []
260
+ if kernel_name == 'Linux':
261
+ with open('/proc/mounts', 'r', encoding='utf-8') as f:
262
+ for line in f:
263
+ dev, path, fstype = line.split()[0:3]
264
+ # simfs: filesystem in OpenVZ
265
+ if fstype in ('ext2', 'ext3', 'ext4', 'xfs', 'jfs', 'reiserfs',
266
+ 'btrfs', 'simfs'):
267
+ if not Path(path).is_dir():
268
+ continue
269
+ _mounts.append({'dev': dev, 'path': path, 'fstype': fstype})
270
+ for mount in _mounts:
271
+ stat = os.statvfs(mount['path'])
272
+ total = stat.f_blocks * stat.f_bsize
273
+ free = stat.f_bfree * stat.f_bsize
274
+ used = (stat.f_blocks - stat.f_bfree) * stat.f_bsize
275
+ mount['total'] = b2h(total)
276
+ mount['free'] = b2h(free)
277
+ mount['used'] = b2h(used)
278
+ mount['used_rate'] = div_percent(used, total)
279
+ if detectdev:
280
+ dev = os.stat(mount['path']).st_dev
281
+ mount['major'], mount['minor'] = os.major(dev), os.minor(dev)
282
+ elif kernel_name == 'Darwin':
283
+ # TODO
284
+ for mnt in glob.glob('/Volumes/*'):
285
+ # t = os.stat(mnt)
286
+ # if t.st_rdev == s.st_dev:
287
+ # print(t)
288
+ _mounts.append({'dev': mnt, 'path': mnt, 'fstype': 'APFS'})
289
+
290
+ # for mount in _mounts:
291
+ # stat = os.statvfs(mount['path'])
292
+ # total = stat.f_blocks * stat.f_bsize
293
+ # free = stat.f_bfree * stat.f_bsize
294
+ # used = (stat.f_blocks - stat.f_bfree) * stat.f_bsize
295
+ # mount['total'] = b2h(total)
296
+ # mount['free'] = b2h(free)
297
+ # mount['used'] = b2h(used)
298
+ # mount['used_rate'] = div_percent(used, total)
299
+ elif kernel_name == 'Windows':
300
+ pass
301
+
302
+ return _mounts
303
+
304
+ @classmethod
305
+ def netifaces(self):
306
+ result = []
307
+ if kernel_name == 'Linux':
308
+ with open('/proc/net/dev', 'r', encoding='utf-8') as f:
309
+ for line in f:
310
+ if not ':' in line:
311
+ continue
312
+ name, data = line.split(':')
313
+ name = name.strip()
314
+ data = data.split()
315
+ rx = int(data[0])
316
+ tx = int(data[8])
317
+ result.append({
318
+ 'name': name,
319
+ 'rx': b2h(rx),
320
+ 'tx': b2h(tx),
321
+ 'timestamp': int(time.time()),
322
+ 'rx_bytes': rx,
323
+ 'tx_bytes': tx,
324
+ })
325
+ with open('/proc/net/route', encoding='utf-8') as f:
326
+ for line in f:
327
+ fields = line.strip().split()
328
+ if fields[1] != '00000000' or not int(fields[3], 16) & 2:
329
+ continue
330
+ gw = socket.inet_ntoa(struct.pack('<L', int(fields[2], 16)))
331
+ for netiface in result:
332
+ if netiface['name'] == fields[0]:
333
+ netiface['gw'] = gw
334
+ break
335
+
336
+ elif kernel_name == 'Darwin':
337
+ pass
338
+ elif kernel_name == 'Windows':
339
+ pass
340
+
341
+ # REF: http://linux.about.com/library/cmd/blcmdl7_netdevice.htm
342
+ for i, netiface in enumerate(result):
343
+ guess_iface = False
344
+ while True:
345
+ try:
346
+ ifname = netiface['name'][:15]
347
+ ifnamepack = struct.pack('256s', ifname.encode('utf-8'))
348
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
349
+ sfd = s.fileno()
350
+ flags, = struct.unpack(
351
+ 'H',
352
+ fcntl.ioctl(
353
+ sfd,
354
+ 0x8913, # SIOCGIFFLAGS
355
+ ifnamepack)[16:18])
356
+ netiface['status'] = ('down', 'up')[flags & 0x1]
357
+ netiface['ip'] = socket.inet_ntoa(
358
+ fcntl.ioctl(
359
+ sfd,
360
+ 0x8915, # SIOCGIFADDR
361
+ ifnamepack)[20:24])
362
+ netiface['bcast'] = socket.inet_ntoa(
363
+ fcntl.ioctl(
364
+ sfd,
365
+ 0x8919, # SIOCGIFBRDADDR
366
+ ifnamepack)[20:24])
367
+ netiface['mask'] = socket.inet_ntoa(
368
+ fcntl.ioctl(
369
+ sfd,
370
+ 0x891b, # SIOCGIFNETMASK
371
+ ifnamepack)[20:24])
372
+ hwinfo = fcntl.ioctl(
373
+ sfd,
374
+ 0x8927, # SIOCSIFHWADDR
375
+ ifnamepack)
376
+ # REF: networking/interface.c, /usr/include/linux/if.h, /usr/include/linux/if_arp.h
377
+ encaps = {
378
+ '\xff\xff': 'UNSPEC', # -1
379
+ '\x01\x00': 'Ethernet', # 1
380
+ '\x00\x02': 'Point-to-Point Protocol', # 512
381
+ '\x04\x03': 'Local Loopback', # 772
382
+ '\x08\x03': 'IPv6-in-IPv4', # 776
383
+ '\x20\x00': 'InfiniBand', # 32
384
+ }
385
+ hwtype = hwinfo[16:18]
386
+ netiface['encap'] = encaps[bytes.decode(hwtype)]
387
+ # netiface['mac'] = ':'.join(['%02X' % ord(str(char)) for char in hwinfo[18:24]])
388
+ netiface['mac'] = ':'.join(['%02X' % i for i in hwinfo[18:24]])
389
+
390
+ if not netiface['name'].startswith('venet'):
391
+ break
392
+
393
+ # detect interface like venet0:0, venet0:1, etc.
394
+ if not guess_iface:
395
+ guess_iface = True
396
+ guess_iface_name = netiface['name']
397
+ guess_iface_i = 0
398
+ else:
399
+ result.append(netiface)
400
+ guest_iface_i += 1
401
+
402
+ netiface = {
403
+ 'name': '%s:%d' % (guess_iface_name, guess_iface_i),
404
+ 'rx': '0B',
405
+ 'tx': '0B',
406
+ 'timestamp': 0,
407
+ 'rx_bytes': 0,
408
+ 'tx_bytes': 0,
409
+ }
410
+ except:
411
+ #result[i] = None
412
+ break
413
+
414
+ result = [iface for iface in result if 'mac' in iface]
415
+ return result
416
+
417
+ @classmethod
418
+ def nameservers(self):
419
+ nameservers = []
420
+ if kernel_name in ('Linux', 'Darwin'):
421
+ with open('/etc/resolv.conf', 'r', encoding='utf-8') as f:
422
+ for line in f:
423
+ if not 'nameserver' in line:
424
+ continue
425
+ ns, = line.strip().split()[1:2]
426
+ nameservers.append(ns)
427
+ elif kernel_name == 'Windows':
428
+ pass
429
+
430
+ return nameservers
431
+
432
+ @classmethod
433
+ def distribution(self):
434
+ return os_title
435
+
436
+ @classmethod
437
+ def dist(self):
438
+ return {
439
+ 'name': os_name.lower(),
440
+ 'version': os_versint,
441
+ }
442
+
443
+ @classmethod
444
+ def uname(self):
445
+ return {
446
+ 'dist': server_info['os_name'],
447
+ 'kernel_name': server_info['kernel_name'],
448
+ 'hostname': server_info['hostname'],
449
+ 'kernel_release': server_info['kernel_release'],
450
+ 'kernel_version': server_info['kernel_version'],
451
+ 'machine': server_info['machine'],
452
+ 'processor': server_info['processor'],
453
+ 'platform': server_info['os_platform']
454
+ }
455
+
456
+ @classmethod
457
+ def cpuinfo(self):
458
+ models = []
459
+ bitss = []
460
+ cpuids = []
461
+ if kernel_name == 'Linux':
462
+ with open('/proc/cpuinfo', 'r', encoding='utf-8') as f:
463
+ for line in f:
464
+ if 'model name' in line or 'physical id' in line or 'flags' in line:
465
+ item, value = line.strip().split(':')
466
+ item = item.strip()
467
+ value = value.strip()
468
+ if item == 'model name':
469
+ models.append(re.sub(r'\s+', ' ', value))
470
+ elif item == 'physical id':
471
+ cpuids.append(value)
472
+ elif item == 'flags':
473
+ if ' lm ' in value:
474
+ bitss.append('64bit')
475
+ else:
476
+ bitss.append('32bit')
477
+ elif kernel_name == 'Darwin':
478
+ pass
479
+ elif kernel_name == 'Windows':
480
+ pass
481
+
482
+ cores = [{'model': x, 'bits': y} for x, y in zip(models, bitss)]
483
+ cpu_count = len(set(cpuids))
484
+ if cpu_count == 0:
485
+ cpu_count = 1
486
+ return {
487
+ 'cores': cores,
488
+ 'cpu_count': cpu_count,
489
+ 'core_count': len(cores),
490
+ }
491
+
492
+ @classmethod
493
+ def partinfo(self, uuid=None, devname=None):
494
+ """Read partition info including uuid and filesystem.
495
+
496
+ You can specify uuid or devname to get the identified partition info.
497
+ If no argument provided, all partitions will return.
498
+
499
+ We read info from /etc/blkid/blkid.tab instead of call blkid command.
500
+ REF: http://linuxconfig.org/how-to-retrieve-and-change-partitions-universally-unique-identifier-uuid-on-linux
501
+ """
502
+ blks = {}
503
+ if kernel_name == 'Linux':
504
+ p = Popen(shlex.split('/sbin/blkid'), stdout=PIPE, close_fds=True)
505
+ p.stdout.read()
506
+ p.wait()
507
+
508
+ # OpenVZ may not have this file
509
+ if not Path('/etc/blkid/blkid.tab').exists():
510
+ return None
511
+
512
+ with open('/etc/blkid/blkid.tab', encoding='utf-8') as f:
513
+ for line in f:
514
+ dom = parseString(line).documentElement
515
+ _fstype = dom.getAttribute('TYPE')
516
+ _uuid = dom.getAttribute('UUID')
517
+ _devname = dom.firstChild.nodeValue.replace('/dev/', '')
518
+ partinfo = {
519
+ 'name': _devname,
520
+ 'fstype': _fstype,
521
+ 'uuid': _uuid,
522
+ }
523
+ if uuid and uuid == _uuid:
524
+ return partinfo
525
+ elif devname and devname == _devname:
526
+ return partinfo
527
+ else:
528
+ blks[_devname] = partinfo
529
+ if uuid or devname:
530
+ return None
531
+ elif kernel_name == 'Darwin':
532
+ pass
533
+ elif kernel_name == 'Windows':
534
+ pass
535
+
536
+ return blks
537
+
538
+ @classmethod
539
+ def diskinfo(self):
540
+ """Return a dictionary contain info of all disks and partitions.
541
+ """
542
+ disks = {
543
+ 'count': 0,
544
+ 'totalsize': 0,
545
+ 'partitions': [],
546
+ 'lvm': {
547
+ 'partcount': 0,
548
+ 'unpartition': 0,
549
+ 'partitions': []
550
+ }
551
+ }
552
+
553
+ # read mount points
554
+ mounts = ServerInfo.mounts(True)
555
+
556
+ # scan for uuid and filesystem of partitions
557
+ blks = ServerInfo.partinfo()
558
+
559
+ # OpenVZ may not have blk info
560
+ if not blks:
561
+ return disks
562
+
563
+ for devname, blkinfo in blks.items():
564
+ dev = os.stat('/dev/%s' % devname).st_rdev
565
+ major, minor = os.major(dev), os.minor(dev)
566
+ blks[devname]['major'] = major
567
+ blks[devname]['minor'] = minor
568
+
569
+ parts = []
570
+ with open('/proc/partitions', 'r', encoding='utf-8') as f:
571
+ for line in f:
572
+ fields = line.split()
573
+ if len(fields) == 0:
574
+ continue
575
+ if not fields[0].isdigit():
576
+ continue
577
+ major, minor, blocks, name = fields
578
+ major, minor, blocks = int(major), int(minor), int(blocks)
579
+ parts.append({
580
+ 'name': name,
581
+ 'major': major,
582
+ 'minor': minor,
583
+ 'blocks': blocks,
584
+ })
585
+
586
+ # check if some unmounted partition is busy
587
+ has_busy_part = False
588
+ for i, part in enumerate(parts):
589
+ # check if it appears in blkid list
590
+ if not part['name'] in blks:
591
+ # don't check the part with child partition
592
+ if i + 1 < len(parts) and parts[i + 1]['name'].startswith(
593
+ part['name']):
594
+ continue
595
+
596
+ # if dev name doesn't match, check the major and minor of the dev
597
+ devfound = False
598
+ for devname, blkinfo in blks.items():
599
+ if blkinfo['major'] == part['major'] and blkinfo[
600
+ 'minor'] == part['minor']:
601
+ devfound = True
602
+ break
603
+ if devfound:
604
+ continue
605
+
606
+ # means that it is busy
607
+ has_busy_part = True
608
+ break
609
+
610
+ # scan for lvm logical volume
611
+ lvmlvs = []
612
+ lvmlvs_vname = {}
613
+ if not has_busy_part and Path('/sbin/lvm').exists():
614
+ p = Popen(shlex.split('/sbin/lvm lvdisplay'), stdout=PIPE, close_fds=True)
615
+ lvs = p.stdout
616
+ while True:
617
+ line = lvs.readline()
618
+ if not line:
619
+ break
620
+ if 'LV Name' in line or 'LV Path' in line:
621
+ devlink = line.replace('LV Name',
622
+ '').replace('LV Path', '').strip()
623
+ if not Path(devlink).exists():
624
+ continue
625
+ dev = os.readlink(devlink)
626
+ dev = str(Path(devlink).parent.joinpath(dev).resolve())
627
+ dev = dev.replace('/dev/', '')
628
+ lvmlvs_vname[dev] = devlink.replace('/dev/', '')
629
+ lvmlvs.append(dev)
630
+ p.wait()
631
+
632
+ # scan for the 'on' status swap partition
633
+ swapptns = []
634
+ with open('/proc/swaps', 'r', encoding='utf-8') as f:
635
+ for line in f:
636
+ if not line.startswith('/dev/'):
637
+ continue
638
+ fields = line.split()
639
+ swapptns.append(fields[0].replace('/dev/', ''))
640
+
641
+ for part in parts:
642
+ name = part['name']
643
+ major = part['major']
644
+ minor = part['minor']
645
+ blocks = part['blocks']
646
+
647
+ # check if the partition is a hardware disk
648
+ # we treat name with no digit as a hardware disk
649
+ is_hw = True
650
+ partcount = 0
651
+ unpartition = 0
652
+ if len([x for x in name if x.isdigit()]) > 0 or name in lvmlvs:
653
+ is_hw = False
654
+
655
+ # determine which disk this partition belong to
656
+ # and calcular the unpartition disk space
657
+ parent_part = disks
658
+ parent_part_found = False
659
+ for i, ptn in enumerate(disks['partitions']):
660
+ if name.startswith(ptn['name']):
661
+ parent_part_found = True
662
+ parent_part = disks['partitions'][i]
663
+ parent_part['partcount'] += 1
664
+ parent_part['unpartition'] -= blocks * 1024
665
+ break
666
+ if not is_hw and not parent_part_found:
667
+ parent_part = disks['lvm']
668
+
669
+ if name in blks and blks[name]['fstype'].startswith('LVM'):
670
+ is_pv = True
671
+ else:
672
+ is_pv = False
673
+
674
+ partition = {
675
+ 'major': major,
676
+ 'minor': minor,
677
+ 'name': name,
678
+ 'size': b2h(blocks * 1024),
679
+ 'is_hw': is_hw,
680
+ 'is_pv': is_pv,
681
+ 'partcount': partcount,
682
+ }
683
+
684
+ if name in blks:
685
+ partition['fstype'] = blks[name]['fstype']
686
+ partition['uuid'] = blks[name]['uuid']
687
+
688
+ if name in lvmlvs:
689
+ partition['is_lv'] = True
690
+ partition['vname'] = lvmlvs_vname[name]
691
+ else:
692
+ partition['is_lv'] = False
693
+
694
+ for mount in mounts:
695
+ if mount['major'] == major and mount['minor'] == minor:
696
+ partition['mount'] = mount['path']
697
+ # read filesystem type from blkid
698
+ #partition['fstype'] = mount['fstype']
699
+ break
700
+ if name in swapptns:
701
+ partition['fstype'] = 'swap'
702
+ partition['mount'] = 'swap'
703
+
704
+ if is_hw:
705
+ partition['partitions'] = []
706
+ partition['unpartition'] = blocks * 1024
707
+ disks['count'] += 1
708
+ disks['totalsize'] += blocks * 1024
709
+
710
+ parent_part['partitions'].append(partition)
711
+
712
+ disks['totalsize'] = b2h(disks['totalsize'])
713
+ disks['lvscount'] = len(lvmlvs)
714
+ for i, part in enumerate(disks['partitions']):
715
+ unpartition = part['unpartition']
716
+ if unpartition <= 10 * 1024**2: # ignore size < 10MB
717
+ unpartition = '0'
718
+ else:
719
+ unpartition = b2h(unpartition)
720
+ disks['partitions'][i]['unpartition'] = unpartition
721
+ return disks
722
+
723
+ @classmethod
724
+ def virt(self):
725
+ """ Detect the virtual tech of system.
726
+ REF: http://www.dmo.ca/blog/detecting-virtualization-on-linux/
727
+ """
728
+ if kernel_name == 'Linux':
729
+ # detect from dmesg first
730
+ if Path('/var/log/dmesg').exists():
731
+ with open('/var/log/dmesg', encoding='utf-8') as f:
732
+ for line in f:
733
+ if 'VMware Virtual' in line:
734
+ return 'VMware'
735
+ if any([
736
+ 'QEMU Virtual CPU' in line,
737
+ 'Booting paravirtualized kernel on KVM' in line
738
+ ]):
739
+ return 'KVM'
740
+ if 'Booting paravirtualized kernel on Xen' in line:
741
+ return 'Xen PV'
742
+ if 'Xen HVM' in line:
743
+ return 'Xen HVM'
744
+ if 'Xen version' in line:
745
+ return 'Xen'
746
+ if Path('/proc/xen/').exists():
747
+ return 'Xen'
748
+ if Path('/proc/vz/').exists():
749
+ return 'Virtuozzo/OpenVZ'
750
+ elif kernel_name == 'Darwin':
751
+ return 'Darwin'
752
+ elif kernel_name == 'Windows':
753
+ return 'Windows'
754
+
755
+ return ''
756
+
757
+ @classmethod
758
+ def panelinfo(self):
759
+ pid = os.getpid()
760
+ proc = psutil.Process(pid)
761
+
762
+ start_time = proc.create_time()
763
+ start_time_str = time.strftime('%Y-%m-%d %X %Z', time.localtime(start_time))
764
+
765
+ uptime_seconds = int(time.time() - start_time)
766
+ fmt = '{days} 天 {hours} 小时 {minutes} 分 {seconds} 秒'
767
+ uptime_string = strfdelta(datetime.timedelta(seconds=uptime_seconds), fmt)
768
+
769
+ try:
770
+ uid = proc.uids().real
771
+ pwd_info = pwd.getpwuid(uid)
772
+ username = pwd_info.pw_name
773
+ except:
774
+ username = 'unknown'
775
+
776
+ try:
777
+ mem_info = proc.memory_info()
778
+ mem_rss = b2h(mem_info.rss)
779
+ mem_vms = b2h(mem_info.vms)
780
+ except:
781
+ mem_rss = '0B'
782
+ mem_vms = '0B'
783
+
784
+ try:
785
+ cpu_percent = proc.cpu_percent(interval=0)
786
+ except:
787
+ cpu_percent = '0%'
788
+
789
+ try:
790
+ cmdline = ' '.join(proc.cmdline())
791
+ except:
792
+ cmdline = 'unknown'
793
+
794
+ config = load_config(config_file)
795
+ server_ip = config.get('server', 'ip') or '*'
796
+ server_port = config.get('server', 'port') or '8080'
797
+ try:
798
+ force_https = config.getboolean('server', 'forcehttps')
799
+ except:
800
+ force_https = False
801
+
802
+ bind_addr = f"{server_ip}:{server_port}"
803
+ display_ip = server_info['hostname'] if server_ip == '*' else server_ip
804
+ access_url = f"http{'s' if force_https else ''}://{display_ip}:{server_port}"
805
+
806
+ if run_type == 'source':
807
+ run_mode = '源码运行'
808
+ elif run_type == 'system':
809
+ run_mode = '系统模式'
810
+ elif run_type == 'binary':
811
+ run_mode = '二进制模式'
812
+ else:
813
+ run_mode = '未知模式'
814
+
815
+ return {
816
+ 'pid': pid,
817
+ 'start_time': start_time_str,
818
+ 'uptime': uptime_string,
819
+ 'run_user': username,
820
+ 'mem_rss': mem_rss,
821
+ 'mem_vms': mem_vms,
822
+ 'cpu_percent': str(cpu_percent) + '%' if isinstance(cpu_percent, (int, float)) else cpu_percent,
823
+ 'run_mode': run_mode,
824
+ 'run_command': cmdline,
825
+ 'bind_addr': bind_addr,
826
+ 'access_url': access_url,
827
+ 'version': version_info['version'],
828
+ 'releasetime': version_info['releasetime'],
829
+ 'config_file': config_file,
830
+ 'root_path': root_path,
831
+ 'pid_file': pidfile,
832
+ 'log_file': logfile,
833
+ 'error_log_file': logerror,
834
+ 'exec_file': execfile,
835
+ }
836
+
837
+
838
+ @classmethod
839
+ def set_hostname(cls, hostname=None):
840
+ '''Change Hostname'''
841
+ if hostname is None:
842
+ return False
843
+
844
+ hostname = hostname.strip().replace(' ', '').replace('\n', '')
845
+ if hostname == '':
846
+ return False
847
+
848
+ if os_name == 'CentOS':
849
+ if saveconfig('/etc/sysconfig/network', {'HOSTNAME': hostname}) and \
850
+ raw_saveconfig('/etc/hosts', { '127.0.0.1': hostname, '::1': hostname }, delimiter=' ', quoter='') and \
851
+ run(str('hostname %s' % hostname)) == 0:
852
+ return True
853
+ else:
854
+ return False
855
+ elif os_name == 'Ubuntu':
856
+ try:
857
+ with open('/etc/hostname', 'w', encoding='utf-8') as f:
858
+ f.write(hostname)
859
+ newdata = {
860
+ '127.0.0.1': ['localhost', hostname],
861
+ '::1': ['localhost', hostname]
862
+ }
863
+ if run('hostnamectl set-hostname %s' % hostname) == 0 and \
864
+ raw_saveconfig('/etc/hosts', newdata, delimiter=' ', quoter=''):
865
+ return True
866
+ else:
867
+ return False
868
+ except:
869
+ return False
870
+ else:
871
+ return False
872
+
873
+ @classmethod
874
+ def set_nameservers(cls, nameservers=None):
875
+ """Write nameservers to config file.
876
+ """
877
+ if nameservers is None:
878
+ return False
879
+
880
+ nspath = '/etc/resolv.conf'
881
+ data = { 'nameserver': nameservers }
882
+ return raw_saveconfig(nspath, data, delimiter=' ', quoter='')
883
+
884
+ @classmethod
885
+ def ifconfig(cls, ifname, config=None):
886
+ """Read or write single interface's config.
887
+ """
888
+ dist = cls.dist()
889
+ if dist['name'] in ('centos', 'redhat'):
890
+ cfile = '/etc/sysconfig/network-scripts/ifcfg-%s' % ifname
891
+ cmap = {
892
+ 'DEVICE': 'name',
893
+ 'HWADDR': 'mac',
894
+ 'IPADDR': 'ip',
895
+ 'NETMASK': 'mask',
896
+ 'GATEWAY': 'gw',
897
+ }
898
+ if config is None:
899
+ return loadconfig(cfile, cmap)
900
+ else:
901
+ cmap_reverse = dict((v, k) for k, v in cmap.items())
902
+ return saveconfig(cfile, config, cmap_reverse)
903
+ else:
904
+ return None
905
+
906
+ @classmethod
907
+ def ifconfigs(cls):
908
+ """Read config of all interfaces.
909
+ """
910
+ configs = {}
911
+ ifaces = cls.netifaces()
912
+ for iface in ifaces:
913
+ ifname = iface['name']
914
+ config = cls.ifconfig(ifname)
915
+ if config:
916
+ configs[ifname] = config
917
+ return configs
918
+
919
+
920
+ @classmethod
921
+ def network_handle_get(cls, sec, ifname=None):
922
+ """Handle network GET requests."""
923
+ if sec == 'hostname':
924
+ return {'hostname': cls.hostname()}
925
+ elif sec == 'ifnames':
926
+ ifconfigs = cls.ifconfigs()
927
+ return {'ifnames': sorted(ifconfigs.keys())}
928
+ elif sec == 'ifconfig':
929
+ ifconfig = cls.ifconfig(ifname)
930
+ if ifconfig is not None:
931
+ return ifconfig
932
+ return None
933
+ elif sec == 'nameservers':
934
+ return {'nameservers': cls.nameservers()}
935
+ return None
936
+
937
+ @classmethod
938
+ def network_handle_post(cls, sec, ifname=None, args=None):
939
+ """Handle network POST requests."""
940
+ if args is None:
941
+ args = {}
942
+
943
+ if sec == 'hostname':
944
+ hostname = args.get('hostname', '')
945
+ if hostname != '':
946
+ if cls.set_hostname(hostname):
947
+ return {'code': 0, 'msg': '主机名保存成功!'}
948
+ else:
949
+ return {'code': -1, 'msg': '主机名保存失败!'}
950
+ else:
951
+ return {'code': -1, 'msg': '主机名不能为空!'}
952
+
953
+ elif sec == 'ifconfig':
954
+ ip = args.get('ip', '')
955
+ mask = args.get('mask', '')
956
+ gw = args.get('gw', '')
957
+
958
+ if not utils.is_valid_ip(ip):
959
+ return {'code': -1, 'msg': '%s 不是有效的IP地址!' % ip}
960
+ if not utils.is_valid_netmask(mask):
961
+ return {'code': -1, 'msg': '%s 不是有效的子网掩码!' % mask}
962
+ if gw != '' and not utils.is_valid_ip(gw):
963
+ return {'code': -1, 'msg': '网关IP %s 不是有效的IP地址!' % gw}
964
+
965
+ if cls.ifconfig(ifname, {'ip': ip, 'mask': mask, 'gw': gw}):
966
+ return {'code': 0, 'msg': 'IP设置保存成功!'}
967
+ else:
968
+ return {'code': -1, 'msg': 'IP设置保存失败!'}
969
+
970
+ elif sec == 'nameservers':
971
+ nameservers = args.get('nameservers', '')
972
+ nameservers = nameservers.split(',')
973
+
974
+ for i, nameserver in enumerate(nameservers):
975
+ if nameserver == '':
976
+ del nameservers[i]
977
+ continue
978
+ if not utils.is_valid_ip(nameserver):
979
+ return {'code': -1, 'msg': '%s 不是有效的IP地址!' % nameserver}
980
+
981
+ if cls.set_nameservers(nameservers):
982
+ return {'code': 0, 'msg': 'DNS设置保存成功!'}
983
+ else:
984
+ return {'code': -1, 'msg': 'DNS设置保存失败!'}
985
+
986
+ return None
987
+
988
+
989
+ @classmethod
990
+ def reboot(cls):
991
+ """Reboot the server."""
992
+ p = Popen('reboot', stdout=PIPE, stderr=PIPE, close_fds=True)
993
+ info = p.stdout.read()
994
+ p.stderr.read()
995
+ if p.wait() == 0:
996
+ return {'code': 0, 'msg': '已向系统发送重启指令,系统即将重启!'}
997
+ else:
998
+ return {'code': -1, 'msg': '向系统发送重启指令失败!'}
999
+
1000
+
1001
+
1002
+ @classmethod
1003
+ def get_timezone_regions(cls):
1004
+ """Return all the timezone regions."""
1005
+ return ('Africa', 'America', 'Antarctica', 'Arctic', 'Asia',
1006
+ 'Atlantic', 'Australia', 'Europe', 'Indian', 'Pacific', 'Etc')
1007
+
1008
+ @classmethod
1009
+ def get_timezone_list(cls, region=None):
1010
+ """Return timezone list.
1011
+
1012
+ Pass None to parameter region to get the full timezone name, such as:
1013
+ Asia/Shanghai
1014
+ Asia/Chongqing
1015
+ Or else only the city name would be returned.
1016
+ """
1017
+ zonepath = Path('/usr/share/zoneinfo')
1018
+ timezones = []
1019
+ if region is None:
1020
+ regions = cls.get_timezone_regions()
1021
+ for region in regions:
1022
+ regionpath = zonepath / region
1023
+ for zonefile in regionpath.iterdir():
1024
+ if not zonefile.is_file():
1025
+ continue
1026
+ timezones.append('%s/%s' % (region, zonefile.name))
1027
+ else:
1028
+ regionpath = zonepath / region
1029
+ if not regionpath.exists():
1030
+ return []
1031
+ for zonefile in regionpath.iterdir():
1032
+ if not zonefile.is_file():
1033
+ continue
1034
+ timezones.append(zonefile.name)
1035
+
1036
+ return sorted(timezones)
1037
+
1038
+ @classmethod
1039
+ def get_timezone(cls, config):
1040
+ """Get system timezone."""
1041
+ tzpath = Path('/etc/localtime')
1042
+ zonepath = Path('/usr/share/zoneinfo')
1043
+
1044
+ # firstly read from config file
1045
+ timezone = ''
1046
+ if not config.has_section('time'):
1047
+ config.add_section('time')
1048
+
1049
+ if config.has_option('time', 'timezone'):
1050
+ timezone = config.get('time', 'timezone')
1051
+ if timezone:
1052
+ return timezone
1053
+
1054
+ # or else check the system config file
1055
+ dist = cls.dist()
1056
+ if dist['name'] in ('centos', 'redhat'):
1057
+ clockinfo = raw_loadconfig('/etc/sysconfig/clock')
1058
+ if clockinfo and 'ZONE' in clockinfo:
1059
+ timezone = clockinfo['ZONE']
1060
+ return timezone
1061
+ else:
1062
+ pass
1063
+
1064
+ # or else find the file match /etc/localtime
1065
+ with open(tzpath, 'rb') as f:
1066
+ tzdata = hashlib.md5(f.read()).hexdigest()
1067
+ regions = cls.get_timezone_regions()
1068
+ for region in regions:
1069
+ regionpath = zonepath / region
1070
+ for zonefile in regionpath.iterdir():
1071
+ if not zonefile.is_file():
1072
+ continue
1073
+ with open(zonefile, 'rb') as f:
1074
+ if hashlib.md5(f.read()).hexdigest() == tzdata:
1075
+ return '%s/%s' % (region, zonefile.name)
1076
+ return ''
1077
+
1078
+ @classmethod
1079
+ def set_timezone(cls, config, timezone=None):
1080
+ """Set system timezone.
1081
+
1082
+ Pass timezone full name like 'Asia/Shanghai' to set timezone.
1083
+ """
1084
+ if timezone is None:
1085
+ return False
1086
+
1087
+ tzpath = Path('/etc/localtime')
1088
+ zonepath = Path('/usr/share/zoneinfo')
1089
+
1090
+ timezonefile = zonepath / timezone
1091
+ if not timezonefile.exists():
1092
+ return False
1093
+ try:
1094
+ shutil.copyfile(timezonefile, tzpath)
1095
+ except:
1096
+ return False
1097
+
1098
+ # write timezone setting to config file
1099
+ return config.set('time', 'timezone', timezone)
1100
+ class ServerTool(object):
1101
+ @classmethod
1102
+ def supportfs(self):
1103
+ """Return a list of file system that system support.
1104
+ """
1105
+ support_list = []
1106
+ for fstype in ('ext2', 'ext3', 'ext4', 'xfs', 'jfs', 'reiserfs',
1107
+ 'btrfs'):
1108
+ if Path('/sbin/mkfs.%s' % fstype).exists():
1109
+ support_list.append(fstype)
1110
+ support_list.append('swap')
1111
+ return support_list
1112
+
1113
+
1114
+
1115
+
1116
+ @classmethod
1117
+ def handle_time_get(cls, sec, region=None):
1118
+ """Handle time-related GET requests.
1119
+
1120
+ Args:
1121
+ sec: section ('datetime', 'timezone', or 'timezone_list')
1122
+ region: optional region for timezone list
1123
+
1124
+ Returns:
1125
+ dict with requested data
1126
+ """
1127
+ if sec == 'datetime':
1128
+ return cls.datetime(asstruct=True)
1129
+ elif sec == 'timezone':
1130
+ return {'timezone': cls.get_timezone(cls.config)}
1131
+ elif sec == 'timezone_list':
1132
+ if region is None:
1133
+ return {'regions': cls.get_timezone_regions()}
1134
+ else:
1135
+ return {'cities': cls.get_timezone_list(region)}
1136
+ return None
1137
+
1138
+ @classmethod
1139
+ def handle_time_post(cls, config, sec, timezone=''):
1140
+ """Handle time-related POST requests.
1141
+
1142
+ Args:
1143
+ config: config object
1144
+ sec: section ('timezone')
1145
+ timezone: timezone string
1146
+
1147
+ Returns:
1148
+ dict with 'code' and 'msg' keys
1149
+ """
1150
+ if sec == 'timezone':
1151
+ if cls.set_timezone(config, timezone):
1152
+ return {'code': 0, 'msg': '时区设置保存成功!'}
1153
+ else:
1154
+ return {'code': -1, 'msg': '时区设置保存失败!'}
1155
+ return None
1156
+
1157
+ if __name__ == '__main__':
1158
+ print('')
1159
+ print('* Hostname: %s' % ServerInfo.hostname())
1160
+ print('')
1161
+
1162
+ print('* Server time: %s' % ServerInfo.datetime())
1163
+ print('')
1164
+
1165
+ uptime = ServerInfo.uptime()
1166
+ print('* Uptime: %s' % uptime['up'])
1167
+ print('* Idletime: %s' % uptime['idle'])
1168
+ print('* Idlerate: %s' % uptime['idle_rate'])
1169
+ print('')
1170
+
1171
+ loadavg = ServerInfo.loadavg()
1172
+ print('* Last 1 min processes: %s' % loadavg['1min'])
1173
+ print('* Last 15 min processes: %s' % loadavg['5min'])
1174
+ print('* Last 15 min processes: %s' % loadavg['15min'])
1175
+ print('')
1176
+
1177
+ cpustat = ServerInfo.cpustat()
1178
+ tstat = cpustat['total']
1179
+ print('* Total CPU stats:')
1180
+ for k, v in tstat.items():
1181
+ print(' %s: %d' % (k, v))
1182
+ for i, tstat in enumerate(cpustat['cpus']):
1183
+ print('* CPU-%d stats:' % i)
1184
+ for k, v in tstat.items():
1185
+ print(' %s: %d' % (k, v))
1186
+ print('')
1187
+
1188
+ meminfo = ServerInfo.meminfo()
1189
+ print('* Memory total: %s' % meminfo['mem_total'])
1190
+ print('* Memory used: %s (%s)' % (meminfo['mem_used'], meminfo['mem_used_rate']))
1191
+ print('* Memory free: %s (%s)' % (meminfo['mem_free'], meminfo['mem_free_rate']))
1192
+ print('* Memory available: %s (%s)' % (meminfo['mem_available'], meminfo['mem_available_rate']))
1193
+ print('* Memory buffers: %s' % meminfo['mem_buffers'])
1194
+ print('* Memory cached: %s' % meminfo['mem_cached'])
1195
+ print('* Memory slab: %s' % meminfo['mem_slab'])
1196
+ print('* Swap total: %s' % meminfo['swap_total'])
1197
+ print('* Swap used: %s (%s)' % (meminfo['swap_used'], meminfo['swap_used_rate']))
1198
+ print('* Swap free: %s (%s)' % (meminfo['swap_free'], meminfo['swap_free_rate']))
1199
+ print('* Swappiness: %s' % meminfo['swap_swappiness'])
1200
+ print()
1201
+ print('')
1202
+ mounts = ServerInfo.mounts(True)
1203
+ for mount in mounts:
1204
+ print('* Mount device: %s' % mount['dev'])
1205
+ if 'major' in mount:
1206
+ print('* Dev node: (%d, %d)' % (mount['major'], mount['minor']))
1207
+ print('* Mount point: %s' % mount['path'])
1208
+ print('* Total space: %s' % mount['total'])
1209
+ print('* Free space: %s' % mount['free'])
1210
+ print('* Used space: %s (%s)' % (mount['used'], mount['used_rate']))
1211
+ print('')
1212
+
1213
+ netifaces = ServerInfo.netifaces()
1214
+ for netiface in netifaces:
1215
+ print('* Interface name: %s' % netiface['name'])
1216
+ print('* Interface status: %s' % netiface['status'])
1217
+ print('* Link encap: %s' % netiface['encap'])
1218
+ print('* IP address: %s' % netiface['ip'])
1219
+ print('* Broadcast: %s' % netiface['bcast'])
1220
+ print('* Network mask: %s' % netiface['mask'])
1221
+ if 'gw' in netiface:
1222
+ print('* Default gateway: %s' % netiface['gw'])
1223
+ print('* MAC address: %s' % netiface['mac'])
1224
+ print('* Data receive: %s' % netiface['rx'])
1225
+ print('* Data transmit: %s' % netiface['tx'])
1226
+ print('')
1227
+
1228
+ nameservers = ServerInfo.nameservers()
1229
+ print('* Name servers:')
1230
+ for nameserver in nameservers:
1231
+ print(' %s' % nameserver)
1232
+ print('')
1233
+
1234
+ uname = ServerInfo.uname()
1235
+ print('* Kernel name: %s' % uname['kernel_name'])
1236
+ print('* Kernel release: %s' % uname['kernel_release'])
1237
+ print('* Kernel version: %s' % uname['kernel_version'])
1238
+ print('* Machine: %s' % uname['machine'])
1239
+ print('')
1240
+
1241
+ cpuinfo = ServerInfo.cpuinfo()
1242
+ print('* CPU count: %d' % cpuinfo['cpu_count'])
1243
+ print('* CPU cores: %d' % cpuinfo['core_count'])
1244
+ for c in cpuinfo['cores']:
1245
+ print('* CPU core: %s (%s)' % (c['model'], c['bits']))
1246
+ print('')
1247
+
1248
+ diskinfo = ServerInfo.diskinfo()
1249
+ count = diskinfo['count']
1250
+ totalsize = diskinfo['totalsize']
1251
+ partitions = diskinfo['partitions']
1252
+ print('* %d disks detected, total size: %s' % (count, totalsize))
1253
+ print('')
1254
+ for partition in partitions:
1255
+ print('* Partition name: %s (%d, %d)' %
1256
+ (partition['name'], partition['major'], partition['minor']))
1257
+ if 'vname' in partition:
1258
+ print(' Volumn name: %s' % partition['vname'])
1259
+ print(' Partition size: %s (%s free)' %
1260
+ (partition['size'], partition['unpartition']))
1261
+ if 'uuid' in partition:
1262
+ print(' Partition UUID: %s' % partition['uuid'])
1263
+ if 'fstype' in partition:
1264
+ print(' Partition fstype: %s' % partition['fstype'])
1265
+ print(' Partition is PV: %s' % partition['is_pv'])
1266
+ print(' Partition is LV: %s' % partition['is_lv'])
1267
+ print(' Partition is HW: %s' % partition['is_hw'])
1268
+ if 'mount' in partition:
1269
+ print(' Mount point: %s' % partition['mount'])
1270
+ if partition['is_hw']:
1271
+ print(' Partition count: %d' % partition['partcount'])
1272
+ print()
1273
+ for subpartition in partition['partitions']:
1274
+ print(' - Subpartition name: %s (%d, %d)' %
1275
+ (subpartition['name'], subpartition['major'],
1276
+ subpartition['minor']))
1277
+ if 'vname' in subpartition:
1278
+ print(' - Volumn name: %s' % subpartition['vname'])
1279
+ print(' - Subpartition size: %s' % subpartition['size'])
1280
+ if 'uuid' in subpartition:
1281
+ print(' - Subpartition UUID: %s' % subpartition['uuid'])
1282
+ if 'fstype' in subpartition:
1283
+ print(' - Subpartition fstype: %s' % subpartition['fstype'])
1284
+ print(' - Subpartition is PV: %s' % subpartition['is_pv'])
1285
+ print(' - Subpartition is LV: %s' % subpartition['is_lv'])
1286
+ print(' - Subpartition is HW: %s' % subpartition['is_hw'])
1287
+ if 'mount' in subpartition:
1288
+ print(' - Mount point: %s' % subpartition['mount'])
1289
+ if subpartition['is_hw']:
1290
+ print(' - Subpartition count: %d' % subpartition['partcount'])
1291
+ print()
1292
+ print()
1293
+
1294
+ print('* LVM partitions:')
1295
+ for partition in diskinfo['lvm']['partitions']:
1296
+ print(' - Partition name: %s (%d, %d)' %
1297
+ (partition['name'], partition['major'], partition['minor']))
1298
+ if 'vname' in partition:
1299
+ print(' - Volumn name: %s' % partition['vname'])
1300
+ print(' - Partition size: %s' % partition['size'])
1301
+ if 'uuid' in partition:
1302
+ print(' - Partition UUID: %s' % partition['uuid'])
1303
+ if 'fstype' in partition:
1304
+ print(' - Partition fstype: %s' % partition['fstype'])
1305
+ print(' - Partition is PV: %s' % partition['is_pv'])
1306
+ print(' - Partition is LV: %s' % partition['is_lv'])
1307
+ print(' - Partition is HW: %s' % partition['is_hw'])
1308
+ if 'mount' in partition:
1309
+ print(' - Mount point: %s' % partition['mount'])
1310
+ if partition['is_hw']:
1311
+ print(' - Partition count: %d' % partition['partcount'])
1312
+ print()
1313
+
1314
+ print('* Support file systems:')
1315
+ for fstype in ServerTool.supportfs():
1316
+ print(' - %s' % fstype)
1317
+ print()
1318
+
1319
+ print('* Virtual Tech: %s' % ServerInfo.virt())