summaryrefslogtreecommitdiffstats
path: root/src/osmo_gsm_tester/ms_driver.py
blob: f1334fc28766889fc1502fb23f74794c5c98a8fd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# ms_driver: Launch OsmocomBB mobile's virtually connected to a BTS
#
# Copyright (C) 2018 by Holger Hans Peter Freyther
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

from datetime import timedelta
from . import log, util
from osmo_ms_driver.cdf import cdfs
from osmo_ms_driver.event_server import EventServer
from osmo_ms_driver.simple_loop import SimpleLoop
from osmo_ms_driver.location_update_test import MassUpdateLocationTest
from osmo_ms_driver.starter import BinaryOptions, MobileTestStarter
from osmo_ms_driver.test_support import TestExecutor

import os.path
import shutil
import tempfile

class MsDriver(log.Origin):

    def __init__(self, suite_run):
        super().__init__(log.C_RUN, 'ms-driver')
        self._suite_run = suite_run

        # TODO: take config out of the test scenario
        self._time_start = timedelta(seconds=60)
        self._time_step = timedelta(milliseconds=100)
        self._test_duration = timedelta(seconds=120)
        self._cdf = cdfs["ease_in_out"](self._time_start, self._time_step)
        self._loop = SimpleLoop()
        self._executor = TestExecutor()
        self.event_server_sk_tmp_dir = None
        self._subscribers = []
        self._configured = False
        self._results = {}

        # Set-up and start the event server
        event_server_path = self.event_server_path()
        if len(event_server_path.encode()) > 107:
            raise log.Error('Path for event_server socket is longer than max allowed len for unix socket path (107):', self.event_server_path())

        self._ev_server = EventServer("ev_server", event_server_path)
        self._ev_server.listen(self._loop)

    def event_server_path(self):
        if self.event_server_sk_tmp_dir is None:
            self.event_server_sk_tmp_dir = tempfile.mkdtemp('', 'ogteventserversk')
        return os.path.join(self.event_server_sk_tmp_dir, 'osmo_ms_driver.unix')

    def build_binary_options(self):
        """Builds an instance of BinaryOptions.

        Populates the BinaryOptions by searching the virtphy and mobile
        application within the trial directory.
        """

        # Get the base directory for the virtphy/mobile application
        inst = util.Dir(os.path.abspath(self._suite_run.trial.get_inst('osmocom-bb')))

        # Assume these are dynamically linked and verify there is a lib dir.
        lib = inst.child('lib')
        if not os.path.isdir(lib):
            raise RuntimeError('No lib/ in %r' % inst)
        env = { 'LD_LIBRARY_PATH': util.prepend_library_path(lib) }

        def check_and_return_binary(name):
            """Checks the binary exists and returns the path."""
            binary = inst.child('bin', name)
            if not os.path.isfile(binary):
                raise RuntimeError('Binary missing: %r' % binary)
            return binary

        virtphy = check_and_return_binary('virtphy')
        mobile = check_and_return_binary('mobile')
        return BinaryOptions(virtphy, mobile, env)

    def subscriber_add(self, subscriber):
        """Adds a subscriber to the list of subscribers."""
        self._subscribers.append(subscriber)

    def add_test(self, test_name, **kwargs):
        """
        Instantiates and returns a test for the given name.

        The instance created and added will be returned.
        """
        if test_name == 'ul_test':
            test = MassUpdateLocationTest("mass",
                                          self._ev_server, self._results)

        # Verify that a test was instantiated.
        if test_name is None:
            raise log.Error("Unknown test_name: " + test_name)

        # Add it to the executor and return it.
        self._executor.add_test(test)
        return test

    def configure(self):
        """
        Configures the subscribers, tests and registration server. Needs to be
        called after the complete configuration of this driver.
        """
        options = self.build_binary_options()
        self._starter = MobileTestStarter("mass", options, self._cdf,
                                          self._ev_server,
                                          util.Dir(self.event_server_sk_tmp_dir),
                                          self._results, suite_run=self._suite_run)

        for sub in self._subscribers:
            self._starter.subscriber_add(sub)

        self._starter.configure_tasks()
        self._executor.configure(self._subscribers, self._starter.mobiles())
        self._configured = True

    def run_test(self):
        """
        Runs the configured tests by starting the configured amount of mobile
        devices according to their schedule. Returns once all tests succeeded
        or the configured timeout has passed.
        """
        if not self._configured:
            self.configure()
        self._executor.before_start()
        deadline = self._starter.start_all(self._loop, self._test_duration)
        self._executor.after_start()
        self._executor.wait_for_test(self._loop, deadline)

    def print_stats(self):
        """
        Prints statistics about the test run.
        """
        self._executor.print_stats()

    def cleanup(self):
        """
        Cleans up the driver (e.g. AF_UNIX files).
        """

        # Clean-up the temporary directory.
        if self.event_server_sk_tmp_dir:
            shutil.rmtree(path=self.event_server_sk_tmp_dir)

# vim: expandtab tabstop=4 shiftwidth=4