forked from GambitResearch/suponoff
-
Notifications
You must be signed in to change notification settings - Fork 0
/
suponoff-monhelper.py
203 lines (166 loc) · 6.78 KB
/
suponoff-monhelper.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
#! /usr/bin/env python3
"""
A supervisor-like XML-RPC server that offers an interface to retrieve resource
usage and limits of processes, and access to application log files (in addition
to the stdout and stderr access that supervisord already provides).
To use this, copy this file to each server you want to manage and run it,
perhaps from supervisor itself (it may need to be run as root).
By default, it listens on 0.0.0.0:9002, so that the suponoff web app can
connect to it. This may pose a security threat: use option "-a 127.0.0.0.1"
when the suponoff app is on the same machine or when the traffic can be
channelled through a secure connection (e.g., SSH or https).
It requires the python module `psutil' to be installed.
"""
import argparse
import time
import os
import os.path
import logging.handlers
import shlex
import psutil
from datetime import date
from xmlrpc.server import SimpleXMLRPCServer
LOG = logging.getLogger()
def get_application_logfile(argv):
for argi in range(len(argv)):
if argi < (len(argv) - 1) and argv[argi] in ["--logfile", "--log-file"]:
#print(argv[argi + 1])
return argv[argi + 1]
if argv[argi:argi+2] == ['bash', '-c']:
argv1 = shlex.split(argv[argi + 2])
return get_application_logfile(argv1)
class MonHelperRPCInterface: # pylint: disable=R0923
def __init__(self):
pass
def getProcessResourceUsage(self, pid_list):
try:
results = {}
for pid in pid_list:
if pid == 0:
continue
result = {}
results[str(pid)] = result
try:
proc = psutil.Process(pid)
except psutil.NoSuchProcess as e:
LOG.info("No such process: %s", e.msg)
continue
except:
LOG.exception("Process %s:", pid)
continue
result["fileno"] = proc.num_fds()
try:
proc.rlimit
except AttributeError:
max_fileno = -1
max_vmsize = -1
else:
max_fileno = proc.rlimit(psutil.RLIMIT_NOFILE)[0]
max_vmsize = proc.rlimit(psutil.RLIMIT_AS)[0]
if max_fileno != -1:
result["max_fileno"] = max_fileno
result["numconnections"] = len(proc.connections('all'))
result["numfiles"] = len(proc.open_files())
if max_vmsize != -1:
result["max_vmsize"] = str(max_vmsize)
result["vmsize"] = str(proc.memory_info()[1])
result["numchildren"] = len(proc.children())
result["numthreads"] = proc.num_threads()
result["cpu"] = ",".join(str(x) for x in
[time.time()] +
list(proc.cpu_times()))
result["diskio"] = ",".join(str(x) for x in
[time.time()] +
list(proc.io_counters()))
return results
except:
LOG.exception("Error")
return {}
def tailApplicationLog(self, pid, offset, length):
try:
cmdline = psutil.Process(pid).cmdline()
logfile = get_application_logfile(cmdline)
if logfile is None:
return ['', 0, False]
if not os.path.exists(logfile):
# logbook rotated logfiles have a date even for today
base, ext = os.path.splitext(logfile)
logfile2 = "{}-{}{}".format(base,
date.today().strftime("%Y-%m-%d"),
ext)
if os.path.exists(logfile2):
logfile = logfile2
if not os.path.exists(logfile):
# rotatelogs rotated logfiles...
logfile = "{}.{}".format(logfile,
date.today().strftime("%Y-%m-%d"))
if logfile is None or not os.path.exists(logfile):
return ['', 0, False]
return tailFile(logfile, int(offset), int(length))
except:
LOG.exception("Error")
return ['', 0, False]
# copied from supervisor.options
def tailFile(filename, offset, length):
"""
Read length bytes from the file named by filename starting at
offset, automatically increasing offset and setting overflow
flag if log size has grown beyond (offset + length). If length
bytes are not available, as many bytes as are available are returned.
"""
overflow = False
try:
f = open(filename, 'rb')
f.seek(0, 2)
sz = f.tell()
if sz > (offset + length):
overflow = True
offset = sz - 1
if (offset + length) > sz:
if offset > (sz - 1):
length = 0
offset = sz - length
if offset < 0:
offset = 0
if length < 0:
length = 0
if length == 0:
data = ''
else:
f.seek(offset)
data = f.read(length)
offset = sz
return [data, offset, overflow]
except (OSError, IOError):
return ['', offset, False]
# copied from supervisor.options
def split_namespec(namespec):
names = namespec.split(':', 1)
if len(names) == 2:
# group and and process name differ
group_name, process_name = names
if not process_name or process_name == '*':
process_name = None
else:
# group name is same as process name
group_name, process_name = namespec, namespec
return group_name, process_name
def main(args=None):
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--address', '-a', default='0.0.0.0', help='the '
'address on which the server is listening (e.g., '
'"127.0.0.1", default "0.0.0.0").')
parser.add_argument('--port', '-p', type=int, default=9002, help='the port'
' on which the server is listening (default "9002").')
args = parser.parse_args(args)
logging.basicConfig(format="%(asctime)s %(levelname)s "
"%(filename)s:%(lineno)d %(message)s")
LOG.setLevel(logging.INFO)
LOG.info("psutil version %s at %s", psutil.__version__, psutil.__file__)
server = SimpleXMLRPCServer((args.address, args.port))
LOG.info("serving on %s:%d", args.address, args.port)
server.register_introspection_functions()
server.register_instance(MonHelperRPCInterface())
server.serve_forever()
if __name__ == '__main__':
main()