]> sourceware.org Git - systemtap.git/blame - stap-exporter/exporter.py
Clean up the exporter.py formatting with autopep8
[systemtap.git] / stap-exporter / exporter.py
CommitLineData
ca70fe93
AM
1#!/usr/bin/python3
2
1b5ebe34
AM
3import os
4import sys
5import configparser
6import subprocess
7import shlex
ca70fe93
AM
8from http.server import BaseHTTPRequestHandler, HTTPServer
9from urllib.parse import urlparse
10from time import sleep, time
11from pathlib import Path
ca70fe93 12
1b5ebe34 13script_dir = os.path.abspath(__file__ + "/../") + "/scripts/"
ca70fe93 14proc_path = "/proc/systemtap/__systemtap_exporter"
ca70fe93 15
664aa00b 16
ca70fe93 17class Session:
664aa00b 18
1b5ebe34
AM
19 def __init__(self, name, sess_id):
20 self.name = name
ca70fe93 21 self.id = sess_id
1b5ebe34
AM
22 self.cmd = self.get_cmd(name)
23 self.timeout = None
24 self.process = None
25 self.start_time = None
26
27 def begin(self):
ca70fe93
AM
28 self.process = subprocess.Popen(shlex.split(self.cmd))
29
30 def get_proc_path(self):
1b5ebe34 31 return proc_path + str(self.id) + "/" + self.name
ca70fe93
AM
32
33 def get_cmd(self, script):
b12df72d
AM
34 return "stap -m __systemtap_exporter%d %s%s" % (self.id,
35 script_dir,
36 script)
ca70fe93 37
664aa00b 38
ca70fe93 39class SessionMgr:
664aa00b 40
1b5ebe34 41 def __init__(self):
664aa00b
WC
42 self.counter = 0
43 self.sessions = {}
44 self.parse_conf()
ca70fe93 45
1b5ebe34 46 def create_sess(self, script_name):
664aa00b
WC
47 sess = Session(script_name, self.get_new_id())
48 self.sessions[script_name] = sess
49 return sess
1b5ebe34
AM
50
51 def start_sess(self, sess):
52 """ Begin execution of script and set session's start time """
53 sess.begin()
54 if self.wait_for_sess_init(sess) != 0:
55 # init failed
56 self.terminate_sess(sess.name, sess)
57 print("Failed to launch " + sess.name)
58 return 1
59 sess.start_time = time()
60 print("Successfully launched " + sess.name)
61 return 0
62
63 def start_sess_from_name(self, script_name):
64 sess = self.sessions[script_name]
65 return self.start_sess(sess)
66
67 def parse_conf(self):
664aa00b
WC
68 print("Reading config file")
69 config = configparser.ConfigParser()
1b5ebe34 70
664aa00b
WC
71 try:
72 config.read_file(open(script_dir + '/../exporter.conf'))
73 except Exception as e:
74 print("Unable to read exporter.conf: " + str(e))
75 sys.exit(-1)
1b5ebe34 76
664aa00b
WC
77 for sec in config.sections():
78 sess = self.create_sess(sec)
1b5ebe34 79
664aa00b
WC
80 if 'timeout' in config[sec]:
81 try:
82 sess.timeout = int(config[sec]['timeout'])
83 except:
84 print("Unable to parse option 'timeout' of section " + sec)
85 sys.exit(-1)
1b5ebe34 86
664aa00b
WC
87 if 'startup' in config[sec] and config[sec]['startup'] == 'True':
88 self.start_sess(sess)
1b5ebe34
AM
89
90 def sess_exists(self, name):
664aa00b 91 return name in self.sessions
1b5ebe34
AM
92
93 def sess_started(self, name):
664aa00b 94 return self.sessions[name].process is not None
1b5ebe34
AM
95
96 def get_new_id(self):
664aa00b
WC
97 ret = self.counter
98 self.counter += 1
99 return ret
1b5ebe34 100
ca70fe93 101 def wait_for_sess_init(self, sess):
664aa00b
WC
102 """ Return 0 if init ok within 30 seconds, else 1.
103 Init is considered ok when the session's procfs probe file exists.
104 """
105 max_wait = 30
106 pause_duration = 3
107 path = Path(sess.get_proc_path())
108 t0 = time()
109
110 while time() - t0 < max_wait:
111 if path.exists():
112 return 0
113 sleep(pause_duration)
114 return 1
ca70fe93 115
1b5ebe34
AM
116 def terminate_sess(self, name, sess):
117 print("Terminating " + name)
118 sess.process.terminate()
119 sess.process = None
120 sess.start_time = None
121
122 def check_timeouts(self):
123 for (name, sess) in self.sessions.items():
124 if (sess.start_time is not None
125 and sess.timeout is not None
126 and time() - sess.start_time >= sess.timeout):
127 self.terminate_sess(name, sess)
128
ca70fe93
AM
129
130class HTTPHandler(BaseHTTPRequestHandler):
1b5ebe34 131 sessmgr = SessionMgr()
ca70fe93
AM
132
133 def set_headers(self, code, content_type, transfer_encoding=None):
134 self.send_response(code)
135 self.send_header('Content-type', content_type)
136
137 if transfer_encoding:
138 self.send_header('Transfer-Encoding', transfer_encoding)
139
140 self.end_headers()
141
142 def send_metrics(self, sess):
143 metrics_path = sess.get_proc_path()
144 self.set_headers(200, 'text/plain', 'chunked')
145 try:
146 with open(metrics_path) as metrics:
147 for line in metrics:
b12df72d 148 self.wfile.write(b'%lx\r\n%b\r\n'
664aa00b 149 % (len(line), bytes(line, 'utf-8')))
ca70fe93
AM
150 except Exception as e:
151 msg = bytes(str(e), 'utf-8')
152 self.wfile.write(b'%lx\r\n%b\r\n' % (len(msg), msg))
153
154 self.wfile.write(b'0\r\n\r\n')
155
156 def send_msg(self, code, msg):
b12df72d
AM
157 self.set_headers(code, 'text/plain')
158 self.wfile.write(bytes(msg, 'utf-8'))
ca70fe93
AM
159
160 def do_GET(self):
1b5ebe34
AM
161 # remove the preceeding '/' from url
162 name = urlparse(self.path).path[1:]
163 mgr = self.sessmgr
164 if not mgr.sess_exists(name):
165 # exporter doesn't recognize the url
664aa00b 166 self.send_msg(404, "Error 404: file not found")
1b5ebe34
AM
167 elif mgr.sess_started(name):
168 # session is already running, send metrics
169 self.send_metrics(mgr.sessions[name])
170 elif mgr.start_sess_from_name(name) == 0:
171 # session successfully launched
b12df72d
AM
172 self.send_msg(200, ("Script successfully started. "
173 "Refresh page to access metrics."))
1b5ebe34
AM
174 else:
175 # session failed to start
176 self.send_msg(500, "Unable to start stap session")
ca70fe93
AM
177
178if __name__ == "__main__":
179 server_address = ('', 9900)
1b5ebe34 180 sessmgr = HTTPHandler.sessmgr
ca70fe93 181 httpd = HTTPServer(server_address, HTTPHandler)
1b5ebe34
AM
182 httpd.timeout = 5
183 print("Exporter initialization complete")
184
185 while 1:
186 httpd.handle_request()
187 sessmgr.check_timeouts()
This page took 0.043343 seconds and 5 git commands to generate.