blob: 171061d43ce146c8728e5b4fbc5930ce3e419823 [file] [log] [blame]
Neels Hofmeyr3531a192017-03-28 14:30:28 +02001# osmo_gsm_tester: trial: directory of binaries to be tested
2#
3# Copyright (C) 2016-2017 by sysmocom - s.f.m.c. GmbH
4#
5# Author: Neels Hofmeyr <neels@hofmeyr.de>
6#
7# This program is free software: you can redistribute it and/or modify
8# it under the terms of the GNU Affero General Public License as
9# published by the Free Software Foundation, either version 3 of the
10# License, or (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU Affero General Public License for more details.
16#
17# You should have received a copy of the GNU Affero General Public License
18# along with this program. If not, see <http://www.gnu.org/licenses/>.
19
20import os
21import time
22import shutil
23import tarfile
24
25from . import log, util
26
27FILE_MARK_TAKEN = 'taken'
28FILE_CHECKSUMS = 'checksums.md5'
29TIMESTAMP_FMT = '%Y-%m-%d_%H-%M-%S'
30FILE_LAST_RUN = 'last_run'
Neels Hofmeyrfd7b9d02017-05-05 19:51:40 +020031FILE_LOG = 'log'
Neels Hofmeyr4f33dcc2017-05-07 00:01:09 +020032FILE_LOG_BRIEF = 'log_brief'
Neels Hofmeyr3531a192017-03-28 14:30:28 +020033
34class Trial(log.Origin):
35 path = None
36 dir = None
37 _run_dir = None
38 bin_tars = None
Neels Hofmeyrfd7b9d02017-05-05 19:51:40 +020039 log_targets = None
Neels Hofmeyr3531a192017-03-28 14:30:28 +020040
41 @staticmethod
42 def next(trials_dir):
43
44 with trials_dir.lock('Trial.next'):
45 trials = [e for e in trials_dir.children()
46 if trials_dir.isdir(e) and not trials_dir.exists(e, FILE_MARK_TAKEN)]
47 if not trials:
48 return None
49 # sort by time to get the one that waited longest
50 trials.sort(key=lambda e: os.path.getmtime(trials_dir.child(e)))
51 next_trial = trials[0]
52 return Trial(trials_dir.child(next_trial)).take()
53
54 def __init__(self, trial_dir):
Neels Hofmeyre44a0cb2017-05-14 03:25:45 +020055 self.path = os.path.abspath(trial_dir)
Your Name3c6673a2017-04-08 18:52:39 +020056 self.set_name(os.path.basename(self.path))
Neels Hofmeyr3531a192017-03-28 14:30:28 +020057 self.set_log_category(log.C_TST)
58 self.dir = util.Dir(self.path)
59 self.inst_dir = util.Dir(self.dir.child('inst'))
60 self.bin_tars = []
61
62 def __repr__(self):
63 return self.name()
64
65 def __enter__(self):
Neels Hofmeyrfd7b9d02017-05-05 19:51:40 +020066 # add a log target to log to the run dir
67 run_dir = self.get_run_dir()
Neels Hofmeyr39b0b892017-05-14 16:16:31 +020068 detailed_log = run_dir.new_child(FILE_LOG)
Neels Hofmeyrfd7b9d02017-05-05 19:51:40 +020069 self.log_targets = [
Neels Hofmeyr39b0b892017-05-14 16:16:31 +020070 log.FileLogTarget(detailed_log)
Neels Hofmeyrfd7b9d02017-05-05 19:51:40 +020071 .set_all_levels(log.L_DBG)
72 .style_change(trace=True),
Neels Hofmeyr4f33dcc2017-05-07 00:01:09 +020073 log.FileLogTarget(run_dir.new_child(FILE_LOG_BRIEF))
74 .style_change(src=False, all_origins=False)
Neels Hofmeyrfd7b9d02017-05-05 19:51:40 +020075 ]
Neels Hofmeyr3531a192017-03-28 14:30:28 +020076 self.log('Trial start')
Neels Hofmeyr39b0b892017-05-14 16:16:31 +020077 self.log('Detailed log at', detailed_log)
Neels Hofmeyr506edbc2017-05-06 21:56:27 +020078 self.take()
Neels Hofmeyr3531a192017-03-28 14:30:28 +020079 super().__enter__()
80
81 def __exit__(self, *exc_info):
82 super().__exit__(*exc_info)
83 self.log('Trial end')
84
Neels Hofmeyrfd7b9d02017-05-05 19:51:40 +020085 for lt in self.log_targets:
86 lt.remove()
87 self.log_targets = None
88
Neels Hofmeyr3531a192017-03-28 14:30:28 +020089 def take(self):
90 self.dir.touch(FILE_MARK_TAKEN)
91 return self
92
93 def get_run_dir(self):
94 if self._run_dir is not None:
95 return self._run_dir
96 self._run_dir = util.Dir(self.dir.new_child('run.%s' % time.strftime(TIMESTAMP_FMT)))
97 self._run_dir.mkdir()
98
99 last_run = self.dir.child(FILE_LAST_RUN)
100 if os.path.islink(last_run):
101 os.remove(last_run)
102 if not os.path.exists(last_run):
103 os.symlink(self.dir.rel_path(self._run_dir.path), last_run)
104 return self._run_dir
105
106 def verify(self):
107 "verify checksums"
108
109 if not self.dir.exists():
110 raise RuntimeError('Trial dir does not exist: %r' % self.dir)
111 if not self.dir.isdir():
112 raise RuntimeError('Trial dir is not a dir: %r' % self.dir)
113
114 checksums = self.dir.child(FILE_CHECKSUMS)
115 if not self.dir.isfile(FILE_CHECKSUMS):
116 raise RuntimeError('No checksums file in trial dir: %r', checksums)
117
118 with open(checksums, 'r') as f:
119 line_nr = 0
120 for line in [l.strip() for l in f.readlines()]:
121 line_nr += 1
122 if not line:
123 continue
124 md5, filename = line.split(' ')
125 file_path = self.dir.child(filename)
126
127 if not self.dir.isfile(filename):
128 raise RuntimeError('File listed in checksums file but missing in trials dir:'
129 ' %r vs. %r line %d' % (file_path, checksums, line_nr))
130
131 if md5 != util.md5_of_file(file_path):
132 raise RuntimeError('Checksum mismatch for %r vs. %r line %d'
133 % (file_path, checksums, line_nr))
134
135 if filename.endswith('.tgz'):
136 self.bin_tars.append(filename)
137
138 def has_bin_tar(self, bin_name):
139 bin_tar_start = '%s.' % bin_name
140 matches = [t for t in self.bin_tars if t.startswith(bin_tar_start)]
141 self.dbg(bin_name=bin_name, matches=matches)
142 if not matches:
143 return None
144 if len(matches) > 1:
145 raise RuntimeError('More than one match for bin name %r: %r' % (bin_name, matches))
146 bin_tar = matches[0]
147 bin_tar_path = self.dir.child(bin_tar)
148 if not os.path.isfile(bin_tar_path):
149 raise RuntimeError('Not a file or missing: %r' % bin_tar_path)
150 return bin_tar_path
151
152 def get_inst(self, bin_name):
153 bin_tar = self.has_bin_tar(bin_name)
154 if not bin_tar:
Your Name3c6673a2017-04-08 18:52:39 +0200155 raise RuntimeError('No such binary available: %r' % bin_name)
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200156 inst_dir = self.inst_dir.child(bin_name)
157
158 if os.path.isdir(inst_dir):
159 # already unpacked
160 return inst_dir
161
162 t = None
163 try:
164 os.makedirs(inst_dir)
165 t = tarfile.open(bin_tar)
166 t.extractall(inst_dir)
167 return inst_dir
168
169 except:
170 shutil.rmtree(inst_dir)
171 raise
172 finally:
173 if t:
174 try:
175 t.close()
176 except:
177 pass
178
179# vim: expandtab tabstop=4 shiftwidth=4