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/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())
|