Statistics
| Branch: | Tag: | Revision:

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 :