blob: 29abf73f7e2c592502bfa36d15a803f53a75d4c0 [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 Freyther61f28772019-04-27 14:25:22 +010022from .test_support import ResultStore
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +000023
24from datetime import timedelta
25
Holger Hans Peter Freyther337141f2019-02-23 09:58:59 +000026import collections
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +000027import time
28
Holger Hans Peter Freyther61f28772019-04-27 14:25:22 +010029# Key used for the result dictionary
30LU_RESULT_NAME = 'lu_time'
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +000031
Holger Hans Peter Freyther61f28772019-04-27 14:25:22 +010032def has_lu_time(result):
33 """
34 Returns true if a LU occurred.
35 """
36 return result.has_result(LU_RESULT_NAME)
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +000037
Holger Hans Peter Freyther61f28772019-04-27 14:25:22 +010038def lu_time(result):
39 """
40 Returns the time of the LU occurrence.
41 """
42 return result.get_result(LU_RESULT_NAME, default=0)
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +000043
Holger Hans Peter Freyther61f28772019-04-27 14:25:22 +010044def lu_delay(result):
45 """
46 Returns the delay from LU success to MS start time.
47 """
48 return lu_time(result) - result.start_time()
Holger Hans Peter Freyther0f6e4102018-06-23 15:52:25 +010049
Holger Hans Peter Freyther61f28772019-04-27 14:25:22 +010050def set_lu_time(result, time):
51 """
52 Sets/Overrides the time of the LU success for this MS.
53 """
54 result.set_result(LU_RESULT_NAME, time)
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +000055
Holger Hans Peter Freyther337141f2019-02-23 09:58:59 +000056
57LUStats = collections.namedtuple("LUStats", ["num_attempted", "num_completed",
58 "min_latency", "max_latency"])
59
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +000060class MassUpdateLocationTest(log.Origin):
61 """
62 A test to launch a configurable amount of MS and make them
63 execute a Location Updating Procedure.
64
65 Configure the number of MS to be tested and a function that
66 decides how quickly to start them and a timeout.
67 """
68
Holger Hans Peter Freythere524f2c2019-04-27 15:44:44 +010069 TEMPLATE_LUA = "osmo-mobile.lua"
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +000070 TEMPLATE_CFG = "osmo-mobile.cfg"
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +000071
Holger Hans Peter Freyther5e67ed42019-02-25 09:48:50 +000072 def __init__(self, name, options, cdf_function,
Holger Hans Peter Freytherf743afb2018-11-05 06:07:57 +000073 event_server, tmp_dir, suite_run=None):
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +000074 super().__init__(log.C_RUN, name)
Holger Hans Peter Freytherf658b832018-11-05 05:05:43 +000075 self._binary_options = options
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +000076 self._cdf = cdf_function
Holger Hans Peter Freytherf743afb2018-11-05 06:07:57 +000077 self._suite_run = suite_run
Holger Hans Peter Freyther5e67ed42019-02-25 09:48:50 +000078 self._tmp_dir = tmp_dir
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +000079 self._unstarted = []
Holger Hans Peter Freyther99a6a412018-08-29 04:24:38 +010080 self._mobiles = []
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +000081 self._phys = []
82 self._results = {}
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +000083
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +000084 self._event_server = event_server
85 self._event_server.register(self.handle_msg)
Holger Hans Peter Freytherb697b3b2018-08-29 04:26:20 +010086 self._started = []
Holger Hans Peter Freyther5e67ed42019-02-25 09:48:50 +000087 self._subscribers = []
Holger Hans Peter Freyther99a6a412018-08-29 04:24:38 +010088
Holger Hans Peter Freyther5e67ed42019-02-25 09:48:50 +000089 def subscriber_add(self, subscriber):
90 """
91 Adds a subscriber to the list of subscribers.
92
93 Must be called before starting the testcase.
94 """
95 self._subscribers.append(subscriber)
96
97 def configure_tasks(self):
98 """Sets up the test run."""
99
100 self._cdf.set_target(len(self._subscribers))
101 self._outstanding = len(self._subscribers)
102 for i in range(0, self._outstanding):
103 ms_name = "%.5d" % i
104
105 phy = OsmoVirtPhy(self._binary_options.virtphy,
106 self._binary_options.env,
107 ms_name, self._tmp_dir)
108 self._phys.append(phy)
109
110 launcher = OsmoMobile(self._binary_options.mobile,
111 self._binary_options.env,
112 ms_name, self._tmp_dir, self.TEMPLATE_LUA,
113 self.TEMPLATE_CFG, self._subscribers[i],
114 phy.phy_filename(),
115 self._event_server.server_path())
Holger Hans Peter Freyther61f28772019-04-27 14:25:22 +0100116 self._results[ms_name] = ResultStore(ms_name)
Holger Hans Peter Freyther5e67ed42019-02-25 09:48:50 +0000117 self._mobiles.append(launcher)
118 self._unstarted = copy(self._mobiles)
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +0000119
120 def pre_launch(self, loop):
121 """
122 We need the virtphy's be ready when the lua script in the
123 mobile comes and kicks-off the test. In lua we don't seem to
124 be able to just stat/check if a file/socket exists so we need
125 to do this from here.
126 """
127 self.log("Pre-launching all virtphy's")
128 for phy in self._phys:
Holger Hans Peter Freytherf743afb2018-11-05 06:07:57 +0000129 phy.start(loop, self._suite_run)
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +0000130
131 self.log("Checking if sockets are in the filesystem")
132 for phy in self._phys:
133 phy.verify_ready()
134
Holger Hans Peter Freyther6926c572018-06-23 19:58:38 +0100135 def prepare(self, loop):
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +0000136 self.log("Starting testcase")
137
Holger Hans Peter Freyther5e67ed42019-02-25 09:48:50 +0000138 self.configure_tasks()
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +0000139 self.pre_launch(loop)
140
141 self._start_time = time.clock_gettime(time.CLOCK_MONOTONIC)
142 self._end_time = self._start_time + \
143 self._cdf.duration().total_seconds() + \
144 timedelta(seconds=120).total_seconds()
145
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +0000146 self._started = []
Holger Hans Peter Freyther6926c572018-06-23 19:58:38 +0100147 self._too_slow = 0
148
149 def step_once(self, loop, current_time):
150 if len(self._unstarted) <= 0:
151 return current_time, None
152
153 step_size = self._cdf.step_size().total_seconds()
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +0000154
155 # Start
156 self._cdf.step_once()
157
Holger Hans Peter Freyther6926c572018-06-23 19:58:38 +0100158 # Check for timeout
159 # start pending MS
160 while len(self._started) < self._cdf.current_scaled_value() and len(self._unstarted) > 0:
161 ms = self._unstarted.pop(0)
Holger Hans Peter Freytherf743afb2018-11-05 06:07:57 +0000162 ms.start(loop, self._suite_run)
Holger Hans Peter Freyther6926c572018-06-23 19:58:38 +0100163 launch_time = time.clock_gettime(time.CLOCK_MONOTONIC)
164 self._results[ms.name_number()].set_launch_time(launch_time)
165 self._started.append(ms)
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +0000166
Holger Hans Peter Freyther6926c572018-06-23 19:58:38 +0100167 now_time = time.clock_gettime(time.CLOCK_MONOTONIC)
168 sleep_time = (current_time + step_size) - now_time
169 if sleep_time <= 0:
170 self.log("Starting too slowly. Moving on",
171 target=(current_time + step_size), now=now_time, sleep=sleep_time)
172 self._too_slow += 1
173 sleep_time = 0
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +0000174
Holger Hans Peter Freyther6926c572018-06-23 19:58:38 +0100175 if len(self._unstarted) == 0:
176 end_time = time.clock_gettime(time.CLOCK_MONOTONIC)
177 self.log("All started...", too_slow=self._too_slow, duration=end_time - self._start_time)
178 return current_time, None
179
180 return current_time + step_size, sleep_time
181
Holger Hans Peter Freyther1dc9a042018-06-23 22:27:23 +0100182 def run_test(self, loop, test_duration):
Holger Hans Peter Freyther6926c572018-06-23 19:58:38 +0100183 self.prepare(loop)
184
Holger Hans Peter Freyther1dc9a042018-06-23 22:27:23 +0100185 to_complete_time = self._start_time + test_duration.total_seconds()
Holger Hans Peter Freyther6926c572018-06-23 19:58:38 +0100186 tick_time = self._start_time
187
188 while not self.all_completed():
189 tick_time, sleep_time = self.step_once(loop, tick_time)
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +0000190 now_time = time.clock_gettime(time.CLOCK_MONOTONIC)
Holger Hans Peter Freyther6926c572018-06-23 19:58:38 +0100191 if sleep_time is None:
192 sleep_time = to_complete_time - now_time
193 if sleep_time < 0:
194 break
195 loop.schedule_timeout(sleep_time)
196 loop.select()
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +0000197
198 def stop_all(self):
199 for launcher in self._started:
Holger Hans Peter Freytherf743afb2018-11-05 06:07:57 +0000200 launcher.terminate()
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +0000201
Holger Hans Peter Freyther05895a92018-06-17 19:31:46 +0100202 def handle_msg(self, _data, addr, time):
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +0000203 import json
204 data = json.loads(_data.decode())
205
206 if data['type'] == 'register':
207 ms = self._results[data['ms']]
208 ms.set_start_time(time)
209 launch_delay = ms.start_time() - ms.launch_time()
210 self.log("MS start registered ", ms=ms, at=time, delay=launch_delay)
211 elif data['type'] == 'event':
212 if data['data']['lu_done'] == 1:
213 ms = self._results[data['ms']]
Holger Hans Peter Freyther61f28772019-04-27 14:25:22 +0100214 if not has_lu_time(ms):
Holger Hans Peter Freyther0f6e4102018-06-23 15:52:25 +0100215 self._outstanding = self._outstanding - 1
Holger Hans Peter Freyther61f28772019-04-27 14:25:22 +0100216 set_lu_time(ms, time)
217 self.log("MS performed LU ", ms=ms, at=time, lu_delay=lu_delay(ms))
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +0000218 else:
219 print(time, data)
220 raise Exception("Unknown event type..:" + _data.decode())
221
222
Holger Hans Peter Freyther0f6e4102018-06-23 15:52:25 +0100223 def all_completed(self):
224 return self._outstanding == 0
225
Holger Hans Peter Freyther77d6d552019-02-23 04:43:08 +0000226 def find_min_max(self, results):
Holger Hans Peter Freytheraf522fd2018-06-23 21:35:30 +0100227 min_value = max_value = None
228 for result in results:
Holger Hans Peter Freyther61f28772019-04-27 14:25:22 +0100229 if min_value is None or lu_delay(result) < min_value:
230 min_value = lu_delay(result)
231 if max_value is None or lu_delay(result) > max_value:
232 max_value = lu_delay(result)
Holger Hans Peter Freytheraf522fd2018-06-23 21:35:30 +0100233 return min_value, max_value
234
Holger Hans Peter Freyther337141f2019-02-23 09:58:59 +0000235 def get_result_values(self):
236 """
237 Returns the raw result values of the test run in any order.
238 """
239 return self._results.values()
240
241 def get_stats(self):
242 """
243 Returns a statistical summary of the test.
244 """
245 attempted = len(self._subscribers)
246 completed = attempted - self._outstanding
Holger Hans Peter Freyther61f28772019-04-27 14:25:22 +0100247 min_latency, max_latency = self.find_min_max(filter(lambda x: has_lu_time(x), self._results.values()))
Holger Hans Peter Freyther337141f2019-02-23 09:58:59 +0000248 return LUStats(attempted, completed, min_latency, max_latency)
249
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +0000250 def print_stats(self):
Holger Hans Peter Freyther337141f2019-02-23 09:58:59 +0000251 stats = self.get_stats()
252 all_completed = stats.num_attempted == stats.num_completed
Holger Hans Peter Freyther30cc0212018-02-25 21:34:35 +0000253
254 self.log("Tests done", all_completed=all_completed,
Holger Hans Peter Freyther337141f2019-02-23 09:58:59 +0000255 min=stats.min_latency, max=stats.max_latency)
Holger Hans Peter Freyther61f28772019-04-27 14:25:22 +0100256
257 def lus_less_than(self, acceptable_delay):
258 """
259 Returns LUs that completed within the acceptable delay.
260 """
261 res = []
262 for result in self._results.values():
263 if not has_lu_time(result):
264 continue
265 if timedelta(seconds=lu_delay(result)) >= acceptable_delay:
266 continue
267 res.append(result)
268 return res