blob: f14bd28df681d427352412da8ed1e58f5e6abe6a [file] [log] [blame]
Holger Hans Peter Freyther799fe622018-11-07 03:48:58 +00001# osmo_ms_driver: Location Update Test
Holger Hans Peter Freyther574e62f2018-06-20 09:15:15 +01002# Create MS's and wait for the Location Update to succeed.
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +00003#
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
Holger Hans Peter Freyther99a6a412018-08-29 04:24:38 +010019from copy import copy
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +000020from osmo_gsm_tester import log
21from .starter import OsmoVirtPhy, OsmoMobile
Holger Hans Peter Freyther574e62f2018-06-20 09:15:15 +010022from .test_support import imsi_ki_gen, Results
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +000023
24from datetime import timedelta
25
Holger Hans Peter Freytherb4ad8d72019-02-23 09:58:59 +000026import collections
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +000027import time
28
Holger Hans Peter Freyther574e62f2018-06-20 09:15:15 +010029class LUResult(Results):
Holger Hans Peter Freytherb4ad8d72019-02-23 09:58:59 +000030 """Representation of a Location Updating Result."""
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +000031
32 def __init__(self, name):
Holger Hans Peter Freyther574e62f2018-06-20 09:15:15 +010033 super().__init__(name)
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +000034 self._time_of_lu = None
35
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +000036 def set_lu_time(self, time):
37 assert self._time_of_lu is None
38 self._time_of_lu = time
39
Holger Hans Peter Freyther0f6e4102018-06-23 15:52:25 +010040 def has_lu_time(self):
41 return self._time_of_lu is not None
42
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +000043 def lu_time(self):
44 return self._time_of_lu or 0
45
46 def lu_delay(self):
47 return self.lu_time() - self.start_time()
48
Holger Hans Peter Freytherb4ad8d72019-02-23 09:58:59 +000049
50LUStats = collections.namedtuple("LUStats", ["num_attempted", "num_completed",
51 "min_latency", "max_latency"])
52
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +000053class MassUpdateLocationTest(log.Origin):
54 """
55 A test to launch a configurable amount of MS and make them
56 execute a Location Updating Procedure.
57
58 Configure the number of MS to be tested and a function that
59 decides how quickly to start them and a timeout.
60 """
61
62 TEMPLATE_LUA = "osmo-mobile-lu.lua"
63 TEMPLATE_CFG = "osmo-mobile.cfg"
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +000064
Holger Hans Peter Freytherf743afb2018-11-05 06:07:57 +000065 def __init__(self, name, options, number_of_ms, cdf_function,
66 event_server, tmp_dir, suite_run=None):
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +000067 super().__init__(log.C_RUN, name)
Holger Hans Peter Freytherf658b832018-11-05 05:05:43 +000068 self._binary_options = options
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +000069 self._number_of_ms = number_of_ms
70 self._cdf = cdf_function
71 self._cdf.set_target(number_of_ms)
Holger Hans Peter Freytherf743afb2018-11-05 06:07:57 +000072 self._suite_run = suite_run
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +000073 self._unstarted = []
Holger Hans Peter Freyther99a6a412018-08-29 04:24:38 +010074 self._mobiles = []
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +000075 self._phys = []
76 self._results = {}
77 imsi_gen = imsi_ki_gen()
78
Holger Hans Peter Freyther0f6e4102018-06-23 15:52:25 +010079 self._outstanding = number_of_ms
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +000080 for i in range(0, number_of_ms):
81 ms_name = "%.5d" % i
82
Holger Hans Peter Freytherf658b832018-11-05 05:05:43 +000083 phy = OsmoVirtPhy(options.virtphy, options.env,
84 ms_name, tmp_dir)
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +000085 self._phys.append(phy)
86
Holger Hans Peter Freytherf658b832018-11-05 05:05:43 +000087 launcher = OsmoMobile(options.mobile, options.env,
88 ms_name, tmp_dir, self.TEMPLATE_LUA,
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +000089 self.TEMPLATE_CFG, imsi_gen,
90 phy.phy_filename(),
91 event_server.server_path())
Holger Hans Peter Freyther574e62f2018-06-20 09:15:15 +010092 self._results[ms_name] = LUResult(ms_name)
Holger Hans Peter Freyther99a6a412018-08-29 04:24:38 +010093 self._mobiles.append(launcher)
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +000094 self._event_server = event_server
95 self._event_server.register(self.handle_msg)
Holger Hans Peter Freyther99a6a412018-08-29 04:24:38 +010096 self._unstarted = copy(self._mobiles)
Holger Hans Peter Freytherb697b3b2018-08-29 04:26:20 +010097 self._started = []
Holger Hans Peter Freyther99a6a412018-08-29 04:24:38 +010098
99 def mobiles(self):
100 return self._mobiles
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +0000101
102 def pre_launch(self, loop):
103 """
104 We need the virtphy's be ready when the lua script in the
105 mobile comes and kicks-off the test. In lua we don't seem to
106 be able to just stat/check if a file/socket exists so we need
107 to do this from here.
108 """
109 self.log("Pre-launching all virtphy's")
110 for phy in self._phys:
Holger Hans Peter Freytherf743afb2018-11-05 06:07:57 +0000111 phy.start(loop, self._suite_run)
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +0000112
113 self.log("Checking if sockets are in the filesystem")
114 for phy in self._phys:
115 phy.verify_ready()
116
Holger Hans Peter Freyther6926c572018-06-23 19:58:38 +0100117 def prepare(self, loop):
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +0000118 self.log("Starting testcase")
119
120 self.pre_launch(loop)
121
122 self._start_time = time.clock_gettime(time.CLOCK_MONOTONIC)
123 self._end_time = self._start_time + \
124 self._cdf.duration().total_seconds() + \
125 timedelta(seconds=120).total_seconds()
126
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +0000127 self._started = []
Holger Hans Peter Freyther6926c572018-06-23 19:58:38 +0100128 self._too_slow = 0
129
130 def step_once(self, loop, current_time):
131 if len(self._unstarted) <= 0:
132 return current_time, None
133
134 step_size = self._cdf.step_size().total_seconds()
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +0000135
136 # Start
137 self._cdf.step_once()
138
Holger Hans Peter Freyther6926c572018-06-23 19:58:38 +0100139 # Check for timeout
140 # start pending MS
141 while len(self._started) < self._cdf.current_scaled_value() and len(self._unstarted) > 0:
142 ms = self._unstarted.pop(0)
Holger Hans Peter Freytherf743afb2018-11-05 06:07:57 +0000143 ms.start(loop, self._suite_run)
Holger Hans Peter Freyther6926c572018-06-23 19:58:38 +0100144 launch_time = time.clock_gettime(time.CLOCK_MONOTONIC)
145 self._results[ms.name_number()].set_launch_time(launch_time)
146 self._started.append(ms)
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +0000147
Holger Hans Peter Freyther6926c572018-06-23 19:58:38 +0100148 now_time = time.clock_gettime(time.CLOCK_MONOTONIC)
149 sleep_time = (current_time + step_size) - now_time
150 if sleep_time <= 0:
151 self.log("Starting too slowly. Moving on",
152 target=(current_time + step_size), now=now_time, sleep=sleep_time)
153 self._too_slow += 1
154 sleep_time = 0
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +0000155
Holger Hans Peter Freyther6926c572018-06-23 19:58:38 +0100156 if len(self._unstarted) == 0:
157 end_time = time.clock_gettime(time.CLOCK_MONOTONIC)
158 self.log("All started...", too_slow=self._too_slow, duration=end_time - self._start_time)
159 return current_time, None
160
161 return current_time + step_size, sleep_time
162
Holger Hans Peter Freyther1dc9a042018-06-23 22:27:23 +0100163 def run_test(self, loop, test_duration):
Holger Hans Peter Freyther6926c572018-06-23 19:58:38 +0100164 self.prepare(loop)
165
Holger Hans Peter Freyther1dc9a042018-06-23 22:27:23 +0100166 to_complete_time = self._start_time + test_duration.total_seconds()
Holger Hans Peter Freyther6926c572018-06-23 19:58:38 +0100167 tick_time = self._start_time
168
169 while not self.all_completed():
170 tick_time, sleep_time = self.step_once(loop, tick_time)
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +0000171 now_time = time.clock_gettime(time.CLOCK_MONOTONIC)
Holger Hans Peter Freyther6926c572018-06-23 19:58:38 +0100172 if sleep_time is None:
173 sleep_time = to_complete_time - now_time
174 if sleep_time < 0:
175 break
176 loop.schedule_timeout(sleep_time)
177 loop.select()
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +0000178
179 def stop_all(self):
180 for launcher in self._started:
Holger Hans Peter Freytherf743afb2018-11-05 06:07:57 +0000181 launcher.terminate()
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +0000182
Holger Hans Peter Freyther05895a92018-06-17 19:31:46 +0100183 def handle_msg(self, _data, addr, time):
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +0000184 import json
185 data = json.loads(_data.decode())
186
187 if data['type'] == 'register':
188 ms = self._results[data['ms']]
189 ms.set_start_time(time)
190 launch_delay = ms.start_time() - ms.launch_time()
191 self.log("MS start registered ", ms=ms, at=time, delay=launch_delay)
192 elif data['type'] == 'event':
193 if data['data']['lu_done'] == 1:
194 ms = self._results[data['ms']]
Holger Hans Peter Freyther0f6e4102018-06-23 15:52:25 +0100195 if not ms.has_lu_time():
196 self._outstanding = self._outstanding - 1
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +0000197 ms.set_lu_time(time)
198 self.log("MS performed LU ", ms=ms, at=time, lu_delay=ms.lu_delay())
199 else:
200 print(time, data)
201 raise Exception("Unknown event type..:" + _data.decode())
202
203
Holger Hans Peter Freyther0f6e4102018-06-23 15:52:25 +0100204 def all_completed(self):
205 return self._outstanding == 0
206
Holger Hans Peter Freyther77d6d552019-02-23 04:43:08 +0000207 def find_min_max(self, results):
Holger Hans Peter Freytheraf522fd2018-06-23 21:35:30 +0100208 min_value = max_value = None
209 for result in results:
210 if min_value is None or result.lu_delay() < min_value:
211 min_value = result.lu_delay()
212 if max_value is None or result.lu_delay() > max_value:
213 max_value = result.lu_delay()
214 return min_value, max_value
215
Holger Hans Peter Freytherb4ad8d72019-02-23 09:58:59 +0000216 def get_result_values(self):
217 """
218 Returns the raw result values of the test run in any order.
219 """
220 return self._results.values()
221
222 def get_stats(self):
223 """
224 Returns a statistical summary of the test.
225 """
226 attempted = self._number_of_ms
227 completed = attempted - self._outstanding
228 min_latency, max_latency = self.find_min_max(filter(lambda x: x.has_lu_time(), self._results.values()))
229 return LUStats(attempted, completed, min_latency, max_latency)
230
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +0000231 def print_stats(self):
Holger Hans Peter Freytherb4ad8d72019-02-23 09:58:59 +0000232 stats = self.get_stats()
233 all_completed = stats.num_attempted == stats.num_completed
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +0000234
235 self.log("Tests done", all_completed=all_completed,
Holger Hans Peter Freytherb4ad8d72019-02-23 09:58:59 +0000236 min=stats.min_latency, max=stats.max_latency)