Merge ~sylvain-pineau/checkbox-support:run_watcher_systemd into checkbox-support:master
- Git
- lp:~sylvain-pineau/checkbox-support
- run_watcher_systemd
- Merge into master
Status: | Merged |
---|---|
Approved by: | Sylvain Pineau |
Approved revision: | 139e4e61c6ec113cb8cabcf28f01bfca350e4eb7 |
Merged at revision: | cff67a7d24f257fcfece504ee05b1658df1c9cba |
Proposed branch: | ~sylvain-pineau/checkbox-support:run_watcher_systemd |
Merge into: | checkbox-support:master |
Diff against target: |
683 lines (+158/-480) 2 files modified
checkbox_support/scripts/run_watcher.py (+158/-204) dev/null (+0/-276) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jonathan Cave (community) | Approve | ||
Sylvain Pineau (community) | Needs Resubmitting | ||
Maciej Kisielewski | Approve | ||
Review via email: mp+353572@code.launchpad.net |
Commit message
accessing syslog is not possible on UC18 and the same events can be found in systemd journal.
This MR updates the run watcher usb tool to rely on python3-systemd to still get the insert/remove events.
as part of this transition, the script now uses a class instead of globals.
log_watcher was only needed by this script so removed.
Tested on Desktop classic 18.04
Description of the change
Jonathan Cave (jocave) wrote : | # |
I'm testing this on a gen2c board and the device insertion is not being detected. Would like to check the appropriate messages and being generated and searched for.
Sylvain Pineau (sylvain-pineau) wrote : | # |
Fixed Maciej's nitpicks
For info, the parts update is here: https:/
Jonathan Cave (jocave) wrote : | # |
My findings are that the code works perfectly *if* the systemd libraries used by the checkbox snap match the version of systemd that is running the system journal.
i.e. if the device is an Ubuntu Core 18 device then the checkbox snap must have been built on bionic and target core18. If the device is a Ubuntu Core 16 device the snap must have been built on xenial
Therefore I see no problem with the code, but I think we need firm plan on how we are going to test devices before landing this.
Jonathan Cave (jocave) wrote : | # |
We are now in a position to build snaps for different series so I think we can land this.
Preview Diff
1 | diff --git a/checkbox_support/log_watcher.py b/checkbox_support/log_watcher.py |
2 | deleted file mode 100644 |
3 | index 4ae2da5..0000000 |
4 | --- a/checkbox_support/log_watcher.py |
5 | +++ /dev/null |
6 | @@ -1,276 +0,0 @@ |
7 | -#!/usr/bin/env python3 |
8 | -""" |
9 | -Real-time log files watcher supporting log rotation. |
10 | - |
11 | -Works with Python >= 2.6 and >= 3.2, on both POSIX and Windows. |
12 | - |
13 | - |
14 | -License: MIT |
15 | - |
16 | -Original work Copyright (c) Giampaolo Rodola' <g.rodola [AT] gmail [DOT] com> |
17 | -Modified work Copyright (c) 2015-2016: Taihsiang Ho <tai271828@gmail.com> |
18 | - |
19 | -Permission is hereby granted, free of charge, to any person obtaining a copy of |
20 | -this software and associated documentation files (the "Software"), to deal in |
21 | -the Software without restriction, including without limitation the rights to |
22 | -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies |
23 | -of the Software, and to permit persons to whom the Software is furnished to do |
24 | -so, subject to the following conditions: |
25 | - |
26 | -The above copyright notice and this permission notice shall be included in all |
27 | -copies or substantial portions of the Software. |
28 | - |
29 | -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
30 | -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
31 | -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
32 | -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
33 | -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
34 | -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
35 | -SOFTWARE. |
36 | -""" |
37 | -import os |
38 | -import time |
39 | -import errno |
40 | -import stat |
41 | - |
42 | - |
43 | -class LogWatcher(object): |
44 | - |
45 | - """ |
46 | - Looks for changes in all files of a directory. |
47 | - |
48 | - This is useful for watching log file changes in real-time. |
49 | - It also supports files rotation. |
50 | - |
51 | - Example: |
52 | - |
53 | - >>> def callback(filename, lines): |
54 | - ... print(filename, lines) |
55 | - ... |
56 | - >>> lw = LogWatcher("/var/log/", callback) |
57 | - >>> lw.loop() |
58 | - """ |
59 | - |
60 | - def __init__(self, folder: str, callback: 'Callable[[str], List[str]]', |
61 | - extensions: "List[str]"=None, logfile: str=None, |
62 | - tail_lines: int=0, |
63 | - sizehint: int=1048576): |
64 | - """ |
65 | - Initialize a new log watcher. |
66 | - |
67 | - :param folder: str |
68 | - the folder to watch |
69 | - |
70 | - :param callback: callback |
71 | - a function which is called every time one of the file being |
72 | - watched is updated; |
73 | - this is called with "filename" and "lines" arguments. |
74 | - |
75 | - :param extensions: list |
76 | - only watch files with these extensions |
77 | - |
78 | - :param logfile: str |
79 | - only watch this file. if this var exists, |
80 | - it will override extention list above. |
81 | - |
82 | - :param tail_lines: int |
83 | - read last N lines from files being watched before starting |
84 | - |
85 | - :param sizehint: int |
86 | - passed to file.readlines(), represents an |
87 | - approximation of the maximum number of bytes to read from |
88 | - a file on every ieration (as opposed to load the entire |
89 | - file in memory until EOF is reached). Defaults to 1MB. |
90 | - """ |
91 | - self.folder = os.path.realpath(folder) |
92 | - self.extensions = extensions |
93 | - self.logfile = logfile |
94 | - self._files_map = {} |
95 | - self._callback = callback |
96 | - self._sizehint = sizehint |
97 | - assert os.path.isdir(self.folder), self.folder |
98 | - assert callable(callback), repr(callback) |
99 | - self.update_files() |
100 | - for id, file in self._files_map.items(): |
101 | - file.seek(os.path.getsize(file.name)) # EOF |
102 | - if tail_lines: |
103 | - try: |
104 | - lines = self.tail(file.name, tail_lines) |
105 | - except IOError as err: |
106 | - if err.errno != errno.ENOENT: |
107 | - raise |
108 | - else: |
109 | - if lines: |
110 | - self._callback(file.name, lines) |
111 | - |
112 | - def __enter__(self): |
113 | - return self |
114 | - |
115 | - def __exit__(self, *args): |
116 | - self.close() |
117 | - |
118 | - def __del__(self): |
119 | - self.close() |
120 | - |
121 | - def loop(self, interval=0.1, blocking=True): |
122 | - """Start a busy loop checking for file changes every *interval* |
123 | - seconds. If *blocking* is False make one loop then return. |
124 | - """ |
125 | - # May be overridden in order to use pyinotify lib and block |
126 | - # until the directory being watched is updated. |
127 | - # Note that directly calling readlines() as we do is faster |
128 | - # than first checking file's last modification times. |
129 | - while True: |
130 | - self.update_files() |
131 | - for fid, file in list(self._files_map.items()): |
132 | - self.readlines(file) |
133 | - if not blocking: |
134 | - return |
135 | - time.sleep(interval) |
136 | - |
137 | - def log(self, line): |
138 | - """Log when a file is un/watched""" |
139 | - print(line) |
140 | - |
141 | - def listdir(self): |
142 | - """List directory and filter files by extension. |
143 | - You may want to override this to add extra logic or globbing |
144 | - support. |
145 | - """ |
146 | - ls = os.listdir(self.folder) |
147 | - if self.extensions: |
148 | - ls = [x for x in ls if os.path.splitext(x)[1][1:] |
149 | - in self.extensions] |
150 | - if self.logfile in ls: |
151 | - ls = [self.logfile] |
152 | - |
153 | - return ls |
154 | - |
155 | - @classmethod |
156 | - def open(cls, file): |
157 | - """Wrapper around open(). |
158 | - By default files are opened in binary mode and readlines() |
159 | - will return bytes on both Python 2 and 3. |
160 | - This means callback() will deal with a list of bytes. |
161 | - Can be overridden in order to deal with unicode strings |
162 | - instead, like this: |
163 | - |
164 | - import codecs, locale |
165 | - return codecs.open(file, 'r', encoding=locale.getpreferredencoding(), |
166 | - errors='ignore') |
167 | - """ |
168 | - return open(file, 'rb') |
169 | - |
170 | - @classmethod |
171 | - def tail(cls, fname, window): |
172 | - """Read last N lines from file fname.""" |
173 | - if window <= 0: |
174 | - raise ValueError('invalid window value %r' % window) |
175 | - with cls.open(fname) as f: |
176 | - buffer_size = 1024 |
177 | - # True if open() was overridden and file was opened in text |
178 | - # mode. In that case readlines() will return unicode strings |
179 | - # instead of bytes. |
180 | - encoded = getattr(f, 'encoding', False) |
181 | - CR = '\n' if encoded else b'\n' |
182 | - data = '' if encoded else b'' |
183 | - f.seek(0, os.SEEK_END) |
184 | - fsize = f.tell() |
185 | - block = -1 |
186 | - exit = False |
187 | - while not exit: |
188 | - step = (block * buffer_size) |
189 | - if abs(step) >= fsize: |
190 | - f.seek(0) |
191 | - newdata = f.read(buffer_size - (abs(step) - fsize)) |
192 | - exit = True |
193 | - else: |
194 | - f.seek(step, os.SEEK_END) |
195 | - newdata = f.read(buffer_size) |
196 | - data = newdata + data |
197 | - if data.count(CR) >= window: |
198 | - break |
199 | - else: |
200 | - block -= 1 |
201 | - return data.splitlines()[-window:] |
202 | - |
203 | - def update_files(self): |
204 | - ls = [] |
205 | - for name in self.listdir(): |
206 | - absname = os.path.realpath(os.path.join(self.folder, name)) |
207 | - try: |
208 | - st = os.stat(absname) |
209 | - except EnvironmentError as err: |
210 | - if err.errno != errno.ENOENT: |
211 | - raise |
212 | - else: |
213 | - if not stat.S_ISREG(st.st_mode): |
214 | - continue |
215 | - fid = self.get_file_id(st) |
216 | - ls.append((fid, absname)) |
217 | - |
218 | - # check existent files |
219 | - for fid, file in list(self._files_map.items()): |
220 | - try: |
221 | - st = os.stat(file.name) |
222 | - except EnvironmentError as err: |
223 | - if err.errno == errno.ENOENT: |
224 | - self.unwatch(file, fid) |
225 | - else: |
226 | - raise |
227 | - else: |
228 | - if fid != self.get_file_id(st): |
229 | - # same name but different file (rotation); reload it. |
230 | - self.unwatch(file, fid) |
231 | - self.watch(file.name) |
232 | - |
233 | - # add new ones |
234 | - for fid, fname in ls: |
235 | - if fid not in self._files_map: |
236 | - self.watch(fname) |
237 | - |
238 | - def readlines(self, file): |
239 | - """ |
240 | - Read file lines. |
241 | - |
242 | - Since last access until EOF is reached and invoke callback. |
243 | - """ |
244 | - while True: |
245 | - lines = file.readlines(self._sizehint) |
246 | - if not lines: |
247 | - break |
248 | - self._callback(file.name, lines) |
249 | - |
250 | - def watch(self, fname): |
251 | - try: |
252 | - file = self.open(fname) |
253 | - fid = self.get_file_id(os.stat(fname)) |
254 | - except EnvironmentError as err: |
255 | - if err.errno != errno.ENOENT: |
256 | - raise |
257 | - else: |
258 | - self.log("watching logfile %s" % fname) |
259 | - self._files_map[fid] = file |
260 | - |
261 | - def unwatch(self, file, fid): |
262 | - # File no longer exists. If it has been renamed try to read it |
263 | - # for the last time in case we're dealing with a rotating log |
264 | - # file. |
265 | - self.log("un-watching logfile %s" % file.name) |
266 | - del self._files_map[fid] |
267 | - with file: |
268 | - lines = self.readlines(file) |
269 | - if lines: |
270 | - self._callback(file.name, lines) |
271 | - |
272 | - @staticmethod |
273 | - def get_file_id(st): |
274 | - if os.name == 'posix': |
275 | - return "%xg%x" % (st.st_dev, st.st_ino) |
276 | - else: |
277 | - return "%f" % st.st_ctime |
278 | - |
279 | - def close(self): |
280 | - for id, file in self._files_map.items(): |
281 | - file.close() |
282 | - self._files_map.clear() |
283 | diff --git a/checkbox_support/scripts/run_watcher.py b/checkbox_support/scripts/run_watcher.py |
284 | index 9283db6..24653a5 100644 |
285 | --- a/checkbox_support/scripts/run_watcher.py |
286 | +++ b/checkbox_support/scripts/run_watcher.py |
287 | @@ -1,219 +1,182 @@ |
288 | #!/usr/bin/env python3 |
289 | -# Copyright 2015-2016 Canonical Ltd. |
290 | +# Copyright 2015-2018 Canonical Ltd. |
291 | # All rights reserved. |
292 | # |
293 | # Written by: |
294 | # Taihsiang Ho <taihsiang.ho@canonical.com> |
295 | +# Sylvain Pineau <sylvain.pineau@canonical.com> |
296 | """ |
297 | -application to use LogWatcher. |
298 | - |
299 | -this script use LogWatcher to define the actual behavior to watch log files by |
300 | -a customized callback. |
301 | +this script monitors the systemd journal to catch insert/removal USB events |
302 | """ |
303 | import argparse |
304 | import contextlib |
305 | -import sys |
306 | +import logging |
307 | import os |
308 | import re |
309 | +import select |
310 | import signal |
311 | -import logging |
312 | -from checkbox_support.log_watcher import LogWatcher |
313 | - |
314 | -global ARGS |
315 | -USB_INSERT_TIMEOUT = 30 # sec |
316 | - |
317 | -# {log_string_1:status_1, log_string_2:status_2 ...} |
318 | -FLAG_DETECTION = {"device": { |
319 | - "new high-speed USB device number": False, |
320 | - "new SuperSpeed USB device number": False |
321 | - }, |
322 | - "driver": { |
323 | - "using ehci_hcd": False, |
324 | - "using xhci_hcd": False |
325 | - }, |
326 | - "insertion": { |
327 | - "USB Mass Storage device detected": False |
328 | +import sys |
329 | +from systemd import journal |
330 | + |
331 | + |
332 | +logger = logging.getLogger(__file__) |
333 | +logger.setLevel(logging.INFO) |
334 | +logger.addHandler(logging.StreamHandler(sys.stdout)) |
335 | + |
336 | + |
337 | +class USBWatcher: |
338 | + |
339 | + PART_RE = re.compile("sd\w+:.*(?P<part_name>sd\w+)") |
340 | + USB_ACTION_TIMEOUT = 30 # sec |
341 | + FLAG_DETECTION = {"device": { |
342 | + "new high-speed USB device number": False, |
343 | + "new SuperSpeed USB device number": False |
344 | }, |
345 | - "removal": { |
346 | - "USB disconnect, device number": False |
347 | + "driver": { |
348 | + "using ehci_hcd": False, |
349 | + "using xhci_hcd": False |
350 | + }, |
351 | + "insertion": { |
352 | + "USB Mass Storage device detected": False |
353 | + }, |
354 | + "removal": { |
355 | + "USB disconnect, device number": False |
356 | + } |
357 | } |
358 | - } |
359 | - |
360 | - |
361 | -MOUNTED_PARTITION_CANDIDATES = None |
362 | -MOUNTED_PARTITION = None |
363 | - |
364 | -logging.basicConfig(level=logging.WARNING) |
365 | - |
366 | - |
367 | -###################################################### |
368 | -# run the log watcher |
369 | -###################################################### |
370 | - |
371 | - |
372 | -def callback(filename, lines): |
373 | - """ |
374 | - a callback function for LogWatcher. |
375 | - |
376 | - customized callback to define the actual behavior about how to watch and |
377 | - what to watch of the log files. |
378 | - |
379 | - :param filename: str, a text filename. usually be a log file. |
380 | - :param lines: list, contents the elements as string to tell what to watch. |
381 | - """ |
382 | - for line in lines: |
383 | - line_str = str(line) |
384 | - refresh_detection(line_str) |
385 | - get_partition_info(line_str) |
386 | - report_detection() |
387 | - |
388 | - |
389 | -def detect_str(line, str_2_detect): |
390 | - """detect the string in the line.""" |
391 | - if str_2_detect in line: |
392 | - return True |
393 | - return False |
394 | - |
395 | - |
396 | -def detect_partition(line): |
397 | - """ |
398 | - detect device and partition info from lines. |
399 | - |
400 | - :param line: |
401 | - str, line string from log file |
402 | - |
403 | - :return : |
404 | - a list denoting [device, partition1, partition2 ...] |
405 | - from syslog |
406 | - """ |
407 | - # looking for string like |
408 | - # sdb: sdb1 |
409 | - pattern = "sd.+sd.+" |
410 | - match = re.search(pattern, line) |
411 | - if match: |
412 | - # remove the trailing \n and quote |
413 | - match_string = match.group()[:-3] |
414 | - # will looks like |
415 | - # ['sdb', ' sdb1'] |
416 | - match_list = match_string.split(":") |
417 | - return match_list |
418 | - |
419 | - |
420 | -def get_partition_info(line_str): |
421 | - """get partition info.""" |
422 | - global MOUNTED_PARTITION_CANDIDATES, MOUNTED_PARTITION |
423 | - MOUNTED_PARTITION_CANDIDIATES = detect_partition(line_str) |
424 | - if (MOUNTED_PARTITION_CANDIDIATES and |
425 | - len(MOUNTED_PARTITION_CANDIDIATES) == 2): |
426 | - # hard code because I expect |
427 | - # FLAG_MOUNT_DEVICE_CANDIDIATES is something like ['sdb', ' sdb1'] |
428 | - # This should be smarter if the device has multiple partitions. |
429 | - MOUNTED_PARTITION = MOUNTED_PARTITION_CANDIDIATES[1].strip() |
430 | - |
431 | - |
432 | -def refresh_detection(line_str): |
433 | - """ |
434 | - refresh values of the dictionary FLAG_DETECTION. |
435 | - |
436 | - :param line_str: str of the scanned log lines. |
437 | - """ |
438 | - global FLAG_DETECTION |
439 | - for key in FLAG_DETECTION.keys(): |
440 | - for sub_key in FLAG_DETECTION[key].keys(): |
441 | - if detect_str(line_str, sub_key): |
442 | - FLAG_DETECTION[key][sub_key] = True |
443 | - |
444 | - |
445 | -def report_detection(): |
446 | - """report detection status.""" |
447 | - # insertion detection |
448 | - if ( |
449 | - ARGS.testcase == "insertion" and |
450 | - FLAG_DETECTION["insertion"]["USB Mass Storage device detected"] and |
451 | - MOUNTED_PARTITION |
452 | - ): |
453 | - device = "" |
454 | - driver = "" |
455 | - for key in FLAG_DETECTION["device"]: |
456 | - if FLAG_DETECTION["device"][key]: |
457 | - device = key |
458 | - for key in FLAG_DETECTION["driver"]: |
459 | - if FLAG_DETECTION["driver"][key]: |
460 | - driver = key |
461 | - logging.info("%s was inserted %s controller" % (device, driver)) |
462 | - logging.info("usable partition: %s" % MOUNTED_PARTITION) |
463 | - |
464 | - # judge the detection by the expection |
465 | + |
466 | + def __init__(self, args): |
467 | + self.args = args |
468 | + self.MOUNTED_PARTITION = None |
469 | + signal.signal(signal.SIGALRM, self._no_usb_timeout) |
470 | + signal.alarm(self.USB_ACTION_TIMEOUT) |
471 | + |
472 | + def run(self): |
473 | + j = journal.Reader() |
474 | + j.seek_tail() |
475 | + p = select.poll() |
476 | + p.register(j, j.get_events()) |
477 | + if self.args.testcase == "insertion": |
478 | + print("\n\nINSERT NOW\n\n", flush=True) |
479 | + elif self.args.testcase == "removal": |
480 | + print("\n\nREMOVE NOW\n\n", flush=True) |
481 | + while p.poll(): |
482 | + if j.process() != journal.APPEND: |
483 | + continue |
484 | + self._callback([e['MESSAGE'] for e in j if e]) |
485 | + |
486 | + def _callback(self, lines): |
487 | + for line in lines: |
488 | + line_str = str(line) |
489 | + self._refresh_detection(line_str) |
490 | + self._get_partition_info(line_str) |
491 | + self._report_detection() |
492 | + |
493 | + def _get_partition_info(self, line_str): |
494 | + """get partition info.""" |
495 | + # looking for string like "sdb: sdb1" |
496 | + match = re.search(self.PART_RE, line_str) |
497 | + if match: |
498 | + self.MOUNTED_PARTITION = match.group('part_name') |
499 | + |
500 | + def _refresh_detection(self, line_str): |
501 | + """ |
502 | + refresh values of the dictionary FLAG_DETECTION. |
503 | + |
504 | + :param line_str: str of the scanned log lines. |
505 | + """ |
506 | + for key in self.FLAG_DETECTION.keys(): |
507 | + for sub_key in self.FLAG_DETECTION[key].keys(): |
508 | + if sub_key in line_str: |
509 | + self.FLAG_DETECTION[key][sub_key] = True |
510 | + |
511 | + def _report_detection(self): |
512 | + """report detection status.""" |
513 | + # insertion detection |
514 | if ( |
515 | - ARGS.usb_type == 'usb2' and |
516 | - device == "new high-speed USB device number" |
517 | + self.args.testcase == "insertion" and |
518 | + self.FLAG_DETECTION["insertion"]["USB Mass Storage device detected"] and |
519 | + self.MOUNTED_PARTITION |
520 | ): |
521 | - print("USB2 insertion test passed.") |
522 | - write_usb_info() |
523 | - sys.exit() |
524 | + device = "" |
525 | + driver = "" |
526 | + for key in self.FLAG_DETECTION["device"]: |
527 | + if self.FLAG_DETECTION["device"][key]: |
528 | + device = key |
529 | + for key in self.FLAG_DETECTION["driver"]: |
530 | + if self.FLAG_DETECTION["driver"][key]: |
531 | + driver = key |
532 | + logger.info("%s was inserted %s controller" % (device, driver)) |
533 | + logger.info("usable partition: %s" % self.MOUNTED_PARTITION) |
534 | + # judge the detection by the expection |
535 | + if ( |
536 | + self.args.usb_type == 'usb2' and |
537 | + device == "new high-speed USB device number" |
538 | + ): |
539 | + logger.info("USB2 insertion test passed.") |
540 | + self._write_usb_info() |
541 | + sys.exit() |
542 | + if ( |
543 | + self.args.usb_type == 'usb3' and |
544 | + device == "new SuperSpeed USB device number" |
545 | + ): |
546 | + logger.info("USB3 insertion test passed.") |
547 | + self._write_usb_info() |
548 | + sys.exit() |
549 | + # removal detection |
550 | if ( |
551 | - ARGS.usb_type == 'usb3' and |
552 | - device == "new SuperSpeed USB device number" |
553 | + self.args.testcase == "removal" and |
554 | + self.FLAG_DETECTION["removal"]["USB disconnect, device number"] |
555 | ): |
556 | - print("USB3 insertion test passed.") |
557 | - write_usb_info() |
558 | + logger.info("Removal test passed.") |
559 | + self._remove_usb_info() |
560 | sys.exit() |
561 | |
562 | - # removal detection |
563 | - if ( |
564 | - ARGS.testcase == "removal" and |
565 | - FLAG_DETECTION["removal"]["USB disconnect, device number"] |
566 | - ): |
567 | - logging.info("An USB mass storage was removed.") |
568 | - remove_usb_info() |
569 | - sys.exit() |
570 | - |
571 | - |
572 | -def write_usb_info(): |
573 | - """ |
574 | - reserve detected usb storage info. |
575 | - |
576 | - write the info we got in this script to $PLAINBOX_SESSION_SHARE |
577 | - so the other jobs, e.g. read/write test, could know more information, |
578 | - for example the partition it want to try to mount. |
579 | - """ |
580 | - plainbox_session_share = os.environ.get('PLAINBOX_SESSION_SHARE') |
581 | - if not plainbox_session_share: |
582 | - logging.warning("no env var PLAINBOX_SESSION_SHARE") |
583 | + def _write_usb_info(self): |
584 | + """ |
585 | + reserve detected usb storage info. |
586 | + |
587 | + write the info we got in this script to $PLAINBOX_SESSION_SHARE |
588 | + so the other jobs, e.g. read/write test, could know more information, |
589 | + for example the partition it want to try to mount. |
590 | + """ |
591 | + plainbox_session_share = os.environ.get('PLAINBOX_SESSION_SHARE') |
592 | + if not plainbox_session_share: |
593 | + logger.error("no env var PLAINBOX_SESSION_SHARE") |
594 | + sys.exit(1) |
595 | + if self.MOUNTED_PARTITION: |
596 | + logger.info( |
597 | + "cache file usb_insert_info is at: %s" |
598 | + % plainbox_session_share) |
599 | + file_to_share = open( |
600 | + os.path.join(plainbox_session_share, "usb_insert_info"), "w") |
601 | + file_to_share.write(self.MOUNTED_PARTITION + "\n") |
602 | + file_to_share.close() |
603 | + |
604 | + def _remove_usb_info(self): |
605 | + """remove usb strage info from $PLAINBOX_SESSION_SHARE.""" |
606 | + plainbox_session_share = os.environ.get('PLAINBOX_SESSION_SHARE') |
607 | + if not plainbox_session_share: |
608 | + logger.error("no env var PLAINBOX_SESSION_SHARE") |
609 | + sys.exit(1) |
610 | + file_to_share = os.path.join( |
611 | + plainbox_session_share, "usb_insert_info") |
612 | + with contextlib.suppress(FileNotFoundError): |
613 | + os.remove(file_to_share) |
614 | + |
615 | + def _no_usb_timeout(self, signum, frame): |
616 | + """ |
617 | + define timeout feature. |
618 | + |
619 | + timeout and return failure if there is no usb insertion/removal |
620 | + detected after USB_ACTION_TIMEOUT secs |
621 | + """ |
622 | + logger.error( |
623 | + "no USB storage %s was reported in systemd journal" |
624 | + % self.args.testcase) |
625 | sys.exit(1) |
626 | |
627 | - if MOUNTED_PARTITION: |
628 | - logging.info( |
629 | - "cache file usb_insert_info is at: %s" % plainbox_session_share) |
630 | - file_to_share = open( |
631 | - os.path.join(plainbox_session_share, "usb_insert_info"), "w") |
632 | - file_to_share.write(MOUNTED_PARTITION + "\n") |
633 | - file_to_share.close() |
634 | - |
635 | - |
636 | -def remove_usb_info(): |
637 | - """remove usb strage info from $PLAINBOX_SESSION_SHARE.""" |
638 | - plainbox_session_share = os.environ.get('PLAINBOX_SESSION_SHARE') |
639 | - if not plainbox_session_share: |
640 | - logging.warning("no env var PLAINBOX_SESSION_SHARE") |
641 | - sys.exit(1) |
642 | - file_to_share = os.path.join(plainbox_session_share, "usb_insert_info") |
643 | - with contextlib.suppress(FileNotFoundError): |
644 | - os.remove(file_to_share) |
645 | - |
646 | - |
647 | -def no_usb_timeout(signum, frame): |
648 | - """ |
649 | - define timeout feature. |
650 | - |
651 | - timeout and return failure if there is no usb insertion is detected |
652 | - after USB_INSERT_TIMEOUT secs |
653 | - """ |
654 | - logging.info("no USB storage insertion was detected from /var/log/syslog") |
655 | - sys.exit(1) |
656 | |
657 | def main(): |
658 | - # access the parser |
659 | parser = argparse.ArgumentParser() |
660 | parser.add_argument('testcase', |
661 | choices=['insertion', 'removal'], |
662 | @@ -221,18 +184,9 @@ def main(): |
663 | parser.add_argument('usb_type', |
664 | choices=['usb2', 'usb3'], |
665 | help=("usb2 or usb3")) |
666 | - global ARGS |
667 | - ARGS = parser.parse_args() |
668 | - |
669 | - # set up the log watcher |
670 | - watcher = LogWatcher("/var/log", callback, logfile="syslog") |
671 | - signal.signal(signal.SIGALRM, no_usb_timeout) |
672 | - signal.alarm(USB_INSERT_TIMEOUT) |
673 | - if ARGS.testcase == "insertion": |
674 | - print("\n\nINSERT NOW\n\n", flush=True) |
675 | - elif ARGS.testcase == "removal": |
676 | - print("\n\nREMOVE NOW\n\n", flush=True) |
677 | - watcher.loop() |
678 | + args = parser.parse_args() |
679 | + watcher = USBWatcher(args) |
680 | + watcher.run() |
681 | |
682 | |
683 | if __name__ == "__main__": |
Code looks good. +1
And I love (+158/-480).
Total nitpick would be that some not perfect code got copied. Like string interpolation in logger calls and using .warning() when stuff is an error.