Implement Runtime wrapper
[deliverable/lttng-ivc.git] / lttng_ivc / utils / runtime.py
1 import os
2 import shlex
3 import subprocess
4 import uuid
5 import logging
6
7 _logger = logging.getLogger("Runtime")
8
9
10 class Runtime(object):
11 def __init__(self, runtime_dir):
12 """
13 A dictionary of popen object eg. lttng-sessiond, relayd,
14 anything really. Key is a uuid.
15 """
16 self.__subprocess = {}
17 self.__stdout_stderr = {}
18 self.__projects = []
19
20 self.__runtime_log = os.path.join(runtime_dir, "log")
21 self.__runtime_log_sub = os.path.join(self.__runtime_log, "subprocess")
22
23 self._runtime_log_aggregation = os.path.join(self.__runtime_log, "runtime.log")
24
25 self._run_command_count = 0
26
27 self.lttng_home = os.path.join(runtime_dir, "lttng_home")
28
29 # TODO move exist_ok to false !!!! ONLY for testing
30 os.makedirs(self.__runtime_log, exist_ok=True)
31 os.makedirs(self.__runtime_log_sub, exist_ok=True)
32 os.makedirs(self.lttng_home, exist_ok=True)
33
34 def add_project(self, project):
35 self.__projects.append(project)
36
37 def subprocess_signal(self, subprocess_uuid, signal):
38 self.__subproces[subprocess_uuid].send_signal(signal)
39
40 def subprocess_terminate(self, subprocess_uuid, timeout=60):
41 process = self.__subprocess[subprocess_uuid]
42 process.terminate()
43 process.wait(timeout)
44 stdout, stderr = self.__stdout_stderr[subprocess_uuid]
45 stdout.close()
46 stderr.close()
47
48 def subprocess_kill(self, subprocess_uuid):
49 process = self.__subprocess[subprocess_uuid]
50 process.kill()
51 process.wait()
52 stdout, stderr = self.__stdout_stderr[subprocess_uuid]
53 stdout.close()
54 stderr.close()
55
56 def get_subprocess_stdout_path(self, subprocess_uuid):
57 stdout, stderr = self.__stdout_stderr[subprocess_uuid]
58 return stdout.name
59
60 def get_subprocess_stderr_path(self, subprocess_uuid):
61 stdout, stderr = self.__stdout_stderr[subprocess_uuid]
62 return stderr.name
63
64 def spawn_subprocess(self, command_line):
65 args = shlex.split(command_line)
66 env = self.get_env()
67
68 tmp_id = uuid.uuid1()
69 out_path = os.path.join(self.__runtime_log_sub, str(tmp_id) + ".out")
70 err_path = os.path.join(self.__runtime_log_sub, str(tmp_id) + ".err")
71 stdout = open(out_path, "w")
72 stderr = open(err_path, "w")
73
74 p = subprocess.Popen(args, stdout=stdout, stderr=stderr, env=env)
75 self.__subprocess[tmp_id] = p
76 self.__stdout_stderr[tmp_id] = (stdout, stderr)
77 _logger.debug("Spawned sub pid: {} args: {} stdout: {} stderr{}".format(p.pid, p.args, out_path, err_path))
78
79 def run(self, command_line):
80 """
81 Run the command and return a tuple of a (CompletedProcess, stdout_path,
82 stderr_path). The subprocess is already executed and returned. The
83 callecaller is responsible for checking for errors.
84 """
85 args = shlex.split(command_line)
86 env = self.get_env()
87
88 tmp_id = self._run_command_count
89 self._run_command_count += 1
90
91 out_path = os.path.join(self.__runtime_log, str(tmp_id) + ".out")
92 err_path = os.path.join(self.__runtime_log, str(tmp_id) + ".err")
93 stdout = open(out_path, "w")
94 stderr = open(err_path, "w")
95
96 stdout.write("Output for command #{} {}\n".format(tmp_id, command_line))
97 stdout.write("Start >>>>>>>>>>>>>>>>\n")
98 stdout.flush()
99
100 stderr.write("Output for command #{} {}\n".format(tmp_id, command_line))
101 stderr.write("Start >>>>>>>>>>>>>>>>\n")
102 stderr.flush()
103
104 cp = subprocess.run(args, stdout=stdout, stderr=stderr, env=env)
105 _logger.debug("Command #{} args: {} stdout: {} stderr{}".format(tmp_id, cp.args, out_path, err_path))
106
107 stdout.write("End <<<<<<<<<<<<<<<<\n")
108 stdout.close()
109
110 stderr.write("End <<<<<<<<<<<<<<<<\n")
111 stderr.close()
112
113 # Add to the global log file. This can help a little. Leave the other
114 # file available for per-run analysis
115 with open(self._runtime_log_aggregation, "a") as log:
116 with open(out_path, "r") as out:
117 log.write(out.read())
118 with open(err_path, "r") as out:
119 log.write(out.read())
120
121 return (cp, out_path, err_path)
122
123 def get_cppflags(self):
124 cppflags = []
125 for project in self.__projects:
126 cppflags.append(project.get_cppflags())
127 return " ".join(cppflags)
128
129 def get_ldflags(self):
130 ldflags = []
131 for project in self.__projects:
132 ldflags.append(project.get_ldflags())
133 return " ".join(ldflags)
134
135 def get_ld_library_path(self):
136 library_path = []
137 for project in self.__projects:
138 library_path.append(project.get_ld_library_path())
139 return " ".join(library_path)
140
141 def get_bin_path(self):
142 path = []
143 for project in self.__projects:
144 path.append(project.get_bin_path())
145 return ":".join(path)
146
147 def get_env(self):
148 env = os.environ.copy()
149
150 env["LTTNG_HOME"] = self.lttng_home
151
152 env_fetch = {"CPPFLAGS": (self.get_cppflags(), " "),
153 "LDFLAGS": (self.get_ldflags(), " "),
154 "LD_LIRABRY_PATH": (self.get_ld_library_path(), ":"),
155 "PATH": (self.get_bin_path(), ":"),
156 }
157 for key, (value, delimiter) in env_fetch.items():
158 tmp_var = ""
159 if key in env:
160 tmp_var = env[key]
161 env[key] = delimiter.join([value, tmp_var])
162
163 for project in self.__projects:
164 for var, value in project.special_env_variables.items():
165 if var in env:
166 # Raise for now since no special cases is known
167 _logger.warning("% Special var % is already defined",
168 self.label, var)
169 raise Exception("Multiple definition of a special environment variable")
170 else:
171 env[var] = value
172 return env
173
174 def close(self):
175 for key, subp in self.__subprocess.items():
176 subp.terminate()
177 for key, subp in self.__subprocess.items():
178 try:
179 # TODO move timeout to settings
180 subp.wait(timeout=60)
181 except subprocess.TimeoutExpired as e:
182 # Force a little bit
183 subp.kill()
184 subp.wait()
185 for key, (stdout, stderr) in self.__stdout_stderr.items():
186 stdout.close()
187 stderr.close()
This page took 0.034261 seconds and 5 git commands to generate.