source: rtems-tools/tester/rt/tftp.py

Last change on this file was a22b396, checked in by Chris Johns <chrisj@…>, on 10/07/23 at 03:41:05

tester/tftp: Add a session timeout

  • Fix listener done state
  • Finish open with the state as finished

Closes #4959

  • Property mode set to 100644
File size: 10.5 KB
Line 
1#
2# RTEMS Tools Project (http://www.rtems.org/)
3# Copyright 2013, 2020 Chris Johns (chrisj@rtems.org)
4# All rights reserved.
5#
6# This file is part of the RTEMS Tools package in 'rtems-tools'.
7#
8# Redistribution and use in source and binary forms, with or without
9# modification, are permitted provided that the following conditions are met:
10#
11# 1. Redistributions of source code must retain the above copyright notice,
12# this list of conditions and the following disclaimer.
13#
14# 2. Redistributions in binary form must reproduce the above copyright notice,
15# this list of conditions and the following disclaimer in the documentation
16# and/or other materials provided with the distribution.
17#
18# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28# POSSIBILITY OF SUCH DAMAGE.
29#
30
31#
32# RTEMS Testing TFTP Interface
33#
34
35from __future__ import print_function
36
37import errno
38import logging
39import threading
40import time
41import sys
42
43from rtemstoolkit import error
44from rtemstoolkit import log
45from rtemstoolkit import reraise
46
47import tester.rt.tftpserver
48
49class tftp(object):
50    '''RTEMS Testing TFTP base.'''
51
52    def __init__(self, bsp_arch, bsp, session_timeout, trace = False):
53        self.session_timeout = session_timeout
54        self.trace = trace
55        self.lock_trace = False
56        self.lock = threading.RLock()
57        self.bsp = bsp
58        self.bsp_arch = bsp_arch
59        self._init()
60
61    def __del__(self):
62        self.kill()
63
64    def _init(self, state = 'reset'):
65        self.output_length = None
66        self.console = None
67        self.server = None
68        self.port = 0
69        self.exe = None
70        self.timeout = None
71        self.test_too_long = None
72        self.timer = None
73        self.opened = False
74        self.running = False
75        self.finished = False
76        self.caught = None
77        self.target_state = state
78
79    def _lock(self, msg):
80        if self.lock_trace:
81            log.trace('|[   LOCK:%s ]|' % (msg))
82        self.lock.acquire()
83        if self.lock_trace:
84            log.trace('|[   LOCK:%s done ]|' % (msg))
85
86    def _unlock(self, msg):
87        if self.lock_trace:
88            log.trace('|] UNLOCK:%s [|' % (msg))
89        self.lock.release()
90        if self.lock_trace:
91            log.trace('|[ UNLOCK:%s done ]|' % (msg))
92
93    def _trace(self, text):
94        if log.tracing:
95            log.trace('] tftp: ' + text)
96
97    def _console(self, text):
98        if self.console:
99            self.console(text)
100
101    def _set_target_state(self, state):
102        self._trace('set state: ' + state)
103        self.target_state = state
104
105    def _finished(self):
106        self.server = None
107        self.exe = None
108
109    def _stop(self, finished = True):
110        try:
111            if self.server is not None:
112                self.server.stop()
113            self.finished = finished
114            self._set_target_state('finished')
115        except:
116            pass
117
118    def _run_finished(self):
119        while self.opened and (self.running or not self.finished):
120            self._unlock('_run_finished')
121            time.sleep(0.2)
122            self._lock('_run_finished')
123
124    def _kill(self):
125        self._stop()
126        self._run_finished()
127
128    def _timeout(self):
129        self._stop()
130        self._run_finished()
131        if self.timeout is not None:
132            self.timeout()
133
134    def _test_too_long(self):
135        self._stop()
136        self._run_finished()
137        if self.test_too_long is not None:
138            self.test_too_long()
139
140    def _exe_handle(self, req_file, raddress, rport):
141        self._lock('_exe_handle')
142        exe = self.exe
143        self.exe = None
144        self._unlock('_exe_handle')
145        if exe is not None:
146            self._console('tftp: %s' % (exe))
147            return open(exe, "rb")
148        self._console('tftp: re-requesting exe; target must have reset')
149        self._stop()
150        return None
151
152    def _listener(self, exe):
153        self.server = tester.rt.tftpserver.tftp_server(host = 'all',
154                                                       port = self.port,
155                                                       session_timeout = self.session_timeout,
156                                                       timeout = 10,
157                                                       forced_file = exe,
158                                                       sessions = 1)
159        try:
160            if False and log.tracing:
161                self.server.trace_packets()
162            self.server.start()
163            return self.server.run() == 1
164        except:
165            self.server.stop()
166            raise
167
168    def _runner(self):
169        self._trace('session: start')
170        self._lock('_runner')
171        exe = self.exe
172        self.exe = None
173        self._unlock('_runner')
174        caught = None
175        retry = 0
176        target_loaded = False
177        try:
178            self._lock('_runner')
179            state = self.target_state
180            self._unlock('_runner')
181            self._trace('runner: ' + state)
182            while state not in ['shutdown', 'finished', 'timeout']:
183                if state in ['booting', 'running']:
184                    time.sleep(0.25)
185                else:
186                    self._trace('listening: begin: ' + state)
187                    target_loaded = self._listener(exe)
188                    self._lock('_runner')
189                    if target_loaded:
190                        self._set_target_state('booting')
191                    else:
192                        retry += 1
193                        if retry > 1:
194                            self._set_target_state('timeout')
195                            self._timeout()
196                    state = self.target_state
197                    self._unlock('_runner')
198                    self._trace('listening: end: ' + state)
199                self._lock('_runner')
200                state = self.target_state
201                self._unlock('_runner')
202        except:
203            caught = sys.exc_info()
204            self._trace('session: exception')
205        self._lock('_runner')
206        self._trace('runner: ' + self.target_state)
207        self.caught = caught
208        self.finished = True
209        self.running = False
210        self._unlock('_runner')
211        self._trace('session: end')
212
213    def open(self, executable, port, output_length, console, timeout):
214        self._trace('open: start')
215        self._lock('_open')
216        if self.exe is not None:
217            self._unlock('_open')
218            raise error.general('tftp: already running')
219        self._init()
220        self.output_length = output_length
221        self.console = console
222        self.port = port
223        self.exe = executable
224        self.timeout = timeout[2]
225        self.test_too_long = timeout[3]
226        self.opened = True
227        self.running = True
228        self._console('tftp: exe: %s' % (executable))
229        self.listener = threading.Thread(target = self._runner,
230                                         name = 'tftp-listener')
231        self.listener.daemon = True
232        self._unlock('_open: start listner')
233        self.listener.start()
234        self._lock('_open: start listner')
235        step = 0.25
236        period = timeout[0]
237        seconds = timeout[1]
238        output_length = self.output_length()
239        while not self.finished and period > 0 and seconds > 0:
240            if not self.running and self.caught:
241                break
242            self._unlock('_open: loop')
243            current_length = self.output_length()
244            if output_length != current_length:
245                period = timeout[0]
246            output_length = current_length
247            if seconds < step:
248                seconds = 0
249            else:
250                seconds -= step
251            if period < step:
252                step = period
253                period = 0
254            else:
255                period -= step
256            time.sleep(step)
257            self._lock('_open: loop')
258        self._trace('open: finished')
259        if not self.finished:
260            if period == 0:
261                self._timeout()
262            elif seconds == 0:
263                self._test_too_long()
264        caught = self.caught
265        self._init('finished')
266        self._unlock('_open')
267        if caught is not None:
268            reraise.reraise(*caught)
269
270    def kill(self):
271        self._trace('kill')
272        self._lock('kill')
273        self._set_target_state('shutdown')
274        self._kill()
275        self._unlock('kill')
276
277    def target_restart(self, started):
278        self._trace('target: restart: %d' % (started))
279        self._lock('target_restart')
280        try:
281            if not started:
282                if self.server is not None:
283                    self._trace('server: stop')
284                    self._stop(finished = False)
285                    self._trace('server: stop done')
286                state = 'booting'
287            else:
288                state = 'shutdown'
289            self._set_target_state(state)
290        finally:
291            self._unlock('target_restart')
292
293    def target_reset(self, started):
294        self._trace('target: reset: %d' % (started))
295        self._lock('target_reset')
296        try:
297            if not started:
298                if self.server is not None:
299                    self._trace('server: stop')
300                    self.server.stop()
301                state = 'booting'
302            else:
303                state = 'shutdown'
304            self._set_target_state(state)
305        finally:
306            self._unlock('target_reset')
307
308    def target_start(self):
309        self._trace('target: start')
310        self._lock('target_start')
311        self._set_target_state('running')
312        self._unlock('target_start')
313
314    def target_end(self):
315        self._trace('target: end')
316        self._lock('target_end')
317        self._set_target_state('finished')
318        self._unlock('target_end')
319
320if __name__ == "__main__":
321    import sys
322    if len(sys.argv) > 1:
323        executable = sys.argv[1]
324    else:
325        executable = None
326    t = tftp('arm', 'beagleboneblack')
327    t.open(executable)
Note: See TracBrowser for help on using the repository browser.