root / snf-image-host / helper-monitor.py @ master
History | View | Annotate | Download (6 kB)
1 | f32664b2 | Nikos Skalkotos | #!/usr/bin/env python
|
---|---|---|---|
2 | f32664b2 | Nikos Skalkotos | |
3 | f32664b2 | Nikos Skalkotos | # Copyright (C) 2012 GRNET S.A.
|
4 | f32664b2 | Nikos Skalkotos | #
|
5 | f32664b2 | Nikos Skalkotos | # This program is free software; you can redistribute it and/or modify
|
6 | f32664b2 | Nikos Skalkotos | # it under the terms of the GNU General Public License as published by
|
7 | f32664b2 | Nikos Skalkotos | # the Free Software Foundation; either version 2 of the License, or
|
8 | f32664b2 | Nikos Skalkotos | # (at your option) any later version.
|
9 | f32664b2 | Nikos Skalkotos | #
|
10 | f32664b2 | Nikos Skalkotos | # This program is distributed in the hope that it will be useful, but
|
11 | f32664b2 | Nikos Skalkotos | # WITHOUT ANY WARRANTY; without even the implied warranty of
|
12 | f32664b2 | Nikos Skalkotos | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
13 | f32664b2 | Nikos Skalkotos | # General Public License for more details.
|
14 | f32664b2 | Nikos Skalkotos | #
|
15 | f32664b2 | Nikos Skalkotos | # You should have received a copy of the GNU General Public License
|
16 | f32664b2 | Nikos Skalkotos | # along with this program; if not, write to the Free Software
|
17 | f32664b2 | Nikos Skalkotos | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
18 | f32664b2 | Nikos Skalkotos | # 02110-1301, USA.
|
19 | f32664b2 | Nikos Skalkotos | |
20 | f32664b2 | Nikos Skalkotos | import sys |
21 | f32664b2 | Nikos Skalkotos | import os |
22 | f32664b2 | Nikos Skalkotos | import time |
23 | f32664b2 | Nikos Skalkotos | import json |
24 | f32664b2 | Nikos Skalkotos | import re |
25 | b2592d38 | Nikos Skalkotos | import optparse |
26 | b2592d38 | Nikos Skalkotos | import socket |
27 | b2592d38 | Nikos Skalkotos | from scapy.all import sniff |
28 | f32664b2 | Nikos Skalkotos | |
29 | d509231b | Nikos Skalkotos | LINESIZE = 512
|
30 | d509231b | Nikos Skalkotos | BUFSIZE = 512
|
31 | f32664b2 | Nikos Skalkotos | PROGNAME = os.path.basename(sys.argv[0])
|
32 | f32664b2 | Nikos Skalkotos | STDERR_MAXLINES = 10
|
33 | f32664b2 | Nikos Skalkotos | MAXLINES = 100
|
34 | 0b51f509 | Nikos Skalkotos | MSG_TYPE = 'image-helper'
|
35 | f32664b2 | Nikos Skalkotos | |
36 | f32664b2 | Nikos Skalkotos | PROTOCOL = { |
37 | d509231b | Nikos Skalkotos | 'TASK_START': ('task-start', 'task'), |
38 | d509231b | Nikos Skalkotos | 'TASK_END': ('task-end', 'task'), |
39 | d509231b | Nikos Skalkotos | 'WARNING': ('warning', 'messages'), |
40 | d509231b | Nikos Skalkotos | 'STDERR': ('error', 'stderr'), |
41 | d509231b | Nikos Skalkotos | 'ERROR': ('error', 'messages')} |
42 | f32664b2 | Nikos Skalkotos | |
43 | f32664b2 | Nikos Skalkotos | |
44 | b2592d38 | Nikos Skalkotos | def parse_options(input_args): |
45 | b2592d38 | Nikos Skalkotos | usage = "Usage: %prog [options] <file-sescriptor>"
|
46 | b2592d38 | Nikos Skalkotos | parser = optparse.OptionParser(usage=usage) |
47 | f32664b2 | Nikos Skalkotos | |
48 | b2592d38 | Nikos Skalkotos | parser.add_option("-i", "--interface", type="string", dest="ifname", |
49 | b2592d38 | Nikos Skalkotos | default=None, metavar="IFNAME", |
50 | b2592d38 | Nikos Skalkotos | help="listen on interface IFNAME for monitoring data")
|
51 | f32664b2 | Nikos Skalkotos | |
52 | f58bbc57 | Nikos Skalkotos | parser.add_option( |
53 | f58bbc57 | Nikos Skalkotos | "-f", "--filter", type="string", dest="filter", |
54 | b2592d38 | Nikos Skalkotos | help="add FILTER to incomint traffice when working on an interface",
|
55 | b2592d38 | Nikos Skalkotos | default=None, metavar="FILTER") |
56 | f32664b2 | Nikos Skalkotos | |
57 | b2592d38 | Nikos Skalkotos | options, args = parser.parse_args(input_args) |
58 | f32664b2 | Nikos Skalkotos | |
59 | b2592d38 | Nikos Skalkotos | if len(args) != 1: |
60 | b2592d38 | Nikos Skalkotos | parser.error('Wrong number of argumets')
|
61 | f32664b2 | Nikos Skalkotos | |
62 | b2592d38 | Nikos Skalkotos | options.fd = args[0]
|
63 | f32664b2 | Nikos Skalkotos | |
64 | b2592d38 | Nikos Skalkotos | if options.filter is not None and options.ifname is None: |
65 | f58bbc57 | Nikos Skalkotos | parser.error('You need to define an interface since filters are'
|
66 | b2592d38 | Nikos Skalkotos | 'defined')
|
67 | f32664b2 | Nikos Skalkotos | |
68 | b2592d38 | Nikos Skalkotos | return options
|
69 | f32664b2 | Nikos Skalkotos | |
70 | f32664b2 | Nikos Skalkotos | |
71 | b2592d38 | Nikos Skalkotos | def error(msg): |
72 | b2592d38 | Nikos Skalkotos | sys.stderr.write("HELPER-MONITOR ERROR: %s\n" % msg)
|
73 | b2592d38 | Nikos Skalkotos | sys.exit(1)
|
74 | 13965151 | Nikos Skalkotos | |
75 | b2592d38 | Nikos Skalkotos | |
76 | b2592d38 | Nikos Skalkotos | class HelperMonitor(object): |
77 | b2592d38 | Nikos Skalkotos | def __init__(self, fd): |
78 | b2592d38 | Nikos Skalkotos | self.fd = fd
|
79 | b2592d38 | Nikos Skalkotos | self.lines_left = 0 |
80 | b2592d38 | Nikos Skalkotos | self.line_count = 0 |
81 | b2592d38 | Nikos Skalkotos | self.stderr = "" |
82 | b2592d38 | Nikos Skalkotos | self.line = "" |
83 | b2592d38 | Nikos Skalkotos | |
84 | b2592d38 | Nikos Skalkotos | def process(self, data): |
85 | b2592d38 | Nikos Skalkotos | if not data: |
86 | b2592d38 | Nikos Skalkotos | if not self.line: |
87 | b2592d38 | Nikos Skalkotos | return
|
88 | 13965151 | Nikos Skalkotos | else:
|
89 | b2592d38 | Nikos Skalkotos | data = '\n'
|
90 | 13965151 | Nikos Skalkotos | |
91 | 13965151 | Nikos Skalkotos | while True: |
92 | b2592d38 | Nikos Skalkotos | split = data.split('\n', 1) |
93 | b2592d38 | Nikos Skalkotos | self.line += split[0] |
94 | 13965151 | Nikos Skalkotos | if len(split) == 1: |
95 | b2592d38 | Nikos Skalkotos | if len(self.line) > LINESIZE: |
96 | 13965151 | Nikos Skalkotos | error("Line size exceeded the maximum allowed size")
|
97 | 13965151 | Nikos Skalkotos | break
|
98 | 13965151 | Nikos Skalkotos | |
99 | b2592d38 | Nikos Skalkotos | data = split[1]
|
100 | f32664b2 | Nikos Skalkotos | |
101 | b2592d38 | Nikos Skalkotos | self.line_count += 1 |
102 | b2592d38 | Nikos Skalkotos | if self.line_count >= MAXLINES + 1: |
103 | 13965151 | Nikos Skalkotos | error("Exceeded maximum allowed number of lines: %d." %
|
104 | 13965151 | Nikos Skalkotos | MAXLINES) |
105 | f32664b2 | Nikos Skalkotos | |
106 | b2592d38 | Nikos Skalkotos | if self.lines_left > 0: |
107 | c730d77c | Nikos Skalkotos | self.stderr += "%s\n" % self.line |
108 | b2592d38 | Nikos Skalkotos | self.lines_left -= 1 |
109 | b2592d38 | Nikos Skalkotos | if self.lines_left == 0: |
110 | c730d77c | Nikos Skalkotos | self.send("STDERR", self.stderr) |
111 | b2592d38 | Nikos Skalkotos | self.stderr = "" |
112 | b2592d38 | Nikos Skalkotos | self.line = "" |
113 | 13965151 | Nikos Skalkotos | continue
|
114 | 13965151 | Nikos Skalkotos | |
115 | b2592d38 | Nikos Skalkotos | self.line = self.line.strip() |
116 | b2592d38 | Nikos Skalkotos | if len(self.line) == 0: |
117 | 13965151 | Nikos Skalkotos | continue
|
118 | 13965151 | Nikos Skalkotos | |
119 | b2592d38 | Nikos Skalkotos | if self.line.startswith("STDERR:"): |
120 | c730d77c | Nikos Skalkotos | m = re.match("STDERR:(\d+):(.*)", self.line) |
121 | 13965151 | Nikos Skalkotos | if not m: |
122 | 13965151 | Nikos Skalkotos | error("Invalid syntax for STDERR line")
|
123 | 13965151 | Nikos Skalkotos | try:
|
124 | b2592d38 | Nikos Skalkotos | self.lines_left = int(m.group(1)) |
125 | 13965151 | Nikos Skalkotos | except ValueError: |
126 | 13965151 | Nikos Skalkotos | error("Second field in STDERR line must be an integer")
|
127 | 13965151 | Nikos Skalkotos | |
128 | b2592d38 | Nikos Skalkotos | if self.lines_left > STDERR_MAXLINES: |
129 | 13965151 | Nikos Skalkotos | error("Too many lines in the STDERR output")
|
130 | b2592d38 | Nikos Skalkotos | elif self.lines_left < 0: |
131 | f58bbc57 | Nikos Skalkotos | error("Second field of STDERR: %d is invalid" %
|
132 | f58bbc57 | Nikos Skalkotos | self.lines_left)
|
133 | 13965151 | Nikos Skalkotos | |
134 | b2592d38 | Nikos Skalkotos | if self.lines_left > 0: |
135 | b2592d38 | Nikos Skalkotos | self.stderr = m.group(2) + "\n" |
136 | b2592d38 | Nikos Skalkotos | self.lines_left -= 1 |
137 | b2592d38 | Nikos Skalkotos | |
138 | b2592d38 | Nikos Skalkotos | if self.lines_left == 0: |
139 | c730d77c | Nikos Skalkotos | self.send("STDERR", self.stderr) |
140 | b2592d38 | Nikos Skalkotos | self.stderr = "" |
141 | b2592d38 | Nikos Skalkotos | elif self.line.startswith("TASK_START:") \ |
142 | f58bbc57 | Nikos Skalkotos | or self.line.startswith("TASK_END:") \ |
143 | f58bbc57 | Nikos Skalkotos | or self.line.startswith("WARNING:") \ |
144 | f58bbc57 | Nikos Skalkotos | or self.line.startswith("ERROR:"): |
145 | b2592d38 | Nikos Skalkotos | (msg_type, _, value) = self.line.partition(':') |
146 | b2592d38 | Nikos Skalkotos | |
147 | b2592d38 | Nikos Skalkotos | if self.line.startswith("WARNING:") or \ |
148 | f58bbc57 | Nikos Skalkotos | self.line.startswith("ERROR:"): |
149 | 654f87c0 | Nikos Skalkotos | value = [value] |
150 | b2592d38 | Nikos Skalkotos | self.send(msg_type, value)
|
151 | 13965151 | Nikos Skalkotos | else:
|
152 | 13965151 | Nikos Skalkotos | error("Unknown command!")
|
153 | 13965151 | Nikos Skalkotos | |
154 | 13965151 | Nikos Skalkotos | # Remove the processed line
|
155 | b2592d38 | Nikos Skalkotos | self.line = "" |
156 | b2592d38 | Nikos Skalkotos | |
157 | b2592d38 | Nikos Skalkotos | def send(self, msg_type, value): |
158 | b2592d38 | Nikos Skalkotos | subtype, value_name = PROTOCOL[msg_type] |
159 | b2592d38 | Nikos Skalkotos | |
160 | b2592d38 | Nikos Skalkotos | msg = {} |
161 | b2592d38 | Nikos Skalkotos | msg['type'] = MSG_TYPE
|
162 | b2592d38 | Nikos Skalkotos | msg['subtype'] = subtype
|
163 | b2592d38 | Nikos Skalkotos | msg[value_name] = value |
164 | b2592d38 | Nikos Skalkotos | msg['timestamp'] = time.time()
|
165 | b2592d38 | Nikos Skalkotos | os.write(self.fd, "%s\n" % json.dumps(msg)) |
166 | b2592d38 | Nikos Skalkotos | |
167 | b2592d38 | Nikos Skalkotos | |
168 | b2592d38 | Nikos Skalkotos | if __name__ == "__main__": |
169 | b2592d38 | Nikos Skalkotos | options = parse_options(sys.argv[1:])
|
170 | b2592d38 | Nikos Skalkotos | |
171 | b2592d38 | Nikos Skalkotos | try:
|
172 | b2592d38 | Nikos Skalkotos | fd = int(options.fd)
|
173 | b2592d38 | Nikos Skalkotos | except ValueError: |
174 | b2592d38 | Nikos Skalkotos | error("File descriptor is not an integer")
|
175 | b2592d38 | Nikos Skalkotos | |
176 | b2592d38 | Nikos Skalkotos | try:
|
177 | b2592d38 | Nikos Skalkotos | os.fstat(fd) |
178 | b2592d38 | Nikos Skalkotos | except OSError: |
179 | b2592d38 | Nikos Skalkotos | error("File descriptor is not valid")
|
180 | b2592d38 | Nikos Skalkotos | |
181 | b2592d38 | Nikos Skalkotos | monitor = HelperMonitor(fd) |
182 | b2592d38 | Nikos Skalkotos | |
183 | b2592d38 | Nikos Skalkotos | if options.ifname is not None: |
184 | b2592d38 | Nikos Skalkotos | try:
|
185 | b2592d38 | Nikos Skalkotos | sniff(filter=options.filter, iface=options.ifname, |
186 | f58bbc57 | Nikos Skalkotos | prn=lambda x: monitor.process(x.payload.getfieldval("load"))) |
187 | b2592d38 | Nikos Skalkotos | except socket.error as e: |
188 | b2592d38 | Nikos Skalkotos | # Network is down
|
189 | b2592d38 | Nikos Skalkotos | if e.errno == 100: |
190 | b2592d38 | Nikos Skalkotos | monitor.process(None)
|
191 | b2592d38 | Nikos Skalkotos | else:
|
192 | b2592d38 | Nikos Skalkotos | raise
|
193 | b2592d38 | Nikos Skalkotos | else:
|
194 | a205f1e3 | Nikos Skalkotos | while True: |
195 | a205f1e3 | Nikos Skalkotos | data = os.read(sys.stdin.fileno(), BUFSIZE) |
196 | b2592d38 | Nikos Skalkotos | monitor.process(data) |
197 | a205f1e3 | Nikos Skalkotos | if not data: |
198 | a205f1e3 | Nikos Skalkotos | break
|
199 | f32664b2 | Nikos Skalkotos | |
200 | f32664b2 | Nikos Skalkotos | # vim: set sta sts=4 shiftwidth=4 sw=4 et ai : |