blob: 2d661ca6335818bd956a4d7247174b56f9399cfa [file] [log] [blame]
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +00001# osmo_ms_driver: Starter for processes
2# Help to start processes over time.
3#
4# Copyright (C) 2018 by Holger Hans Peter Freyther
5#
6# This program is free software: you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as
8# published by the Free Software Foundation, either version 3 of the
9# License, or (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program. If not, see <http://www.gnu.org/licenses/>.
18
19
20from osmo_gsm_tester import log
21from .starter import OsmoVirtPhy, OsmoMobile
22
23from datetime import timedelta
24
25import time
26
27def imsi_ki_gen():
28 """
29 Generate IMSIs and KIs to be used by test.
30 """
31 n = 1010000000000
32 while True:
33 yield ("%.15d" % n, "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00")
34 n += 1
35
36class Results(log.Origin):
37
38 def __init__(self, name):
39 super().__init__(log.C_RUN, name)
40 self._time_of_registration = None
41 self._time_of_launch = None
42 self._time_of_lu = None
43
44 def set_start_time(self, time):
45 assert self._time_of_registration is None
46 self._time_of_registration = time
47
48 def set_launch_time(self, time):
49 assert self._time_of_launch is None
50 self._time_of_launch = time
51
52 def set_lu_time(self, time):
53 assert self._time_of_lu is None
54 self._time_of_lu = time
55
56 def start_time(self):
57 return self._time_of_registration or 0
58
59 def launch_time(self):
60 return self._time_of_launch or 0
61
62 def lu_time(self):
63 return self._time_of_lu or 0
64
65 def lu_delay(self):
66 return self.lu_time() - self.start_time()
67
68class MassUpdateLocationTest(log.Origin):
69 """
70 A test to launch a configurable amount of MS and make them
71 execute a Location Updating Procedure.
72
73 Configure the number of MS to be tested and a function that
74 decides how quickly to start them and a timeout.
75 """
76
77 TEMPLATE_LUA = "osmo-mobile-lu.lua"
78 TEMPLATE_CFG = "osmo-mobile.cfg"
79 TEST_TIME = timedelta(seconds=120)
80
81 def __init__(self, name, number_of_ms, cdf_function, event_server, tmp_dir):
82 super().__init__(log.C_RUN, name)
83 self._number_of_ms = number_of_ms
84 self._cdf = cdf_function
85 self._cdf.set_target(number_of_ms)
86 self._unstarted = []
87 self._phys = []
88 self._results = {}
89 imsi_gen = imsi_ki_gen()
90
91 for i in range(0, number_of_ms):
92 ms_name = "%.5d" % i
93
94 phy = OsmoVirtPhy(ms_name, tmp_dir)
95 self._phys.append(phy)
96
97 launcher = OsmoMobile(ms_name, tmp_dir, self.TEMPLATE_LUA,
98 self.TEMPLATE_CFG, imsi_gen,
99 phy.phy_filename(),
100 event_server.server_path())
101 self._results[ms_name] = Results(ms_name)
102 self._unstarted.append(launcher)
103 self._event_server = event_server
104 self._event_server.register(self.handle_msg)
105
106 def pre_launch(self, loop):
107 """
108 We need the virtphy's be ready when the lua script in the
109 mobile comes and kicks-off the test. In lua we don't seem to
110 be able to just stat/check if a file/socket exists so we need
111 to do this from here.
112 """
113 self.log("Pre-launching all virtphy's")
114 for phy in self._phys:
115 phy.start(loop)
116
117 self.log("Checking if sockets are in the filesystem")
118 for phy in self._phys:
119 phy.verify_ready()
120
121 def launch(self, loop):
122 self.log("Starting testcase")
123
124 self.pre_launch(loop)
125
126 self._start_time = time.clock_gettime(time.CLOCK_MONOTONIC)
127 self._end_time = self._start_time + \
128 self._cdf.duration().total_seconds() + \
129 timedelta(seconds=120).total_seconds()
130
131 current_time = self._start_time
132 step_size = self._cdf.step_size().total_seconds()
133 self._started = []
134 too_slow = 0
135
136 # Start
137 self._cdf.step_once()
138
139 while len(self._unstarted) > 0:
140 # Check for timeout
141 # start pending MS
142 while len(self._started) < self._cdf.current_scaled_value() and len(self._unstarted) > 0:
143 ms = self._unstarted.pop(0)
144 ms.start(loop)
145 launch_time = time.clock_gettime(time.CLOCK_MONOTONIC)
146 self._results[ms.name_number()].set_launch_time(launch_time)
147 self._started.append(ms)
148
149 # Progress and sleep
150 self._cdf.step_once()
151
152 now_time = time.clock_gettime(time.CLOCK_MONOTONIC)
153 sleep_time = (current_time + step_size) - now_time
154 if sleep_time <= 0:
155 self.log("Starting too slowly. Moving on",
156 target=(current_time + step_size), now=now_time, sleep=sleep_time)
157 too_slow += 1
158 else:
159 loop.schedule_timeout(sleep_time)
160 loop.select()
161 current_time += step_size
162
163 end_time = time.clock_gettime(time.CLOCK_MONOTONIC)
164 self.log("All started...", too_slow=too_slow, duration=end_time - self._start_time)
165
166 def stop_all(self):
167 for launcher in self._started:
168 launcher.kill()
169
170 def handle_msg(self, _data, time):
171 import json
172 data = json.loads(_data.decode())
173
174 if data['type'] == 'register':
175 ms = self._results[data['ms']]
176 ms.set_start_time(time)
177 launch_delay = ms.start_time() - ms.launch_time()
178 self.log("MS start registered ", ms=ms, at=time, delay=launch_delay)
179 elif data['type'] == 'event':
180 if data['data']['lu_done'] == 1:
181 ms = self._results[data['ms']]
182 ms.set_lu_time(time)
183 self.log("MS performed LU ", ms=ms, at=time, lu_delay=ms.lu_delay())
184 else:
185 print(time, data)
186 raise Exception("Unknown event type..:" + _data.decode())
187
188
189 def wait_for_result(self, loop):
190 to_complete_time = self._start_time + self.TEST_TIME.total_seconds()
191
192 while True:
193 now_time = time.clock_gettime(time.CLOCK_MONOTONIC)
194 sleep_time = to_complete_time - now_time
195 if sleep_time < 0:
196 break
197 loop.schedule_timeout(sleep_time)
198 loop.select()
199
200 def print_stats(self):
201 from functools import reduce
202 all_completed = reduce(lambda b, ms: b and ms.lu_time() is not None, self._results.values(), True)
203 min_value = min(self._results.values(), key=lambda x: x.lu_delay())
204 max_value = max(self._results.values(), key=lambda x: x.lu_delay())
205
206 self.log("Tests done", all_completed=all_completed,
207 min=min_value.lu_delay(), max=max_value.lu_delay())