linuxfabrik-lib 4.3.0__tar.gz → 5.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/PKG-INFO +1 -2
  2. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/base.py +8 -1
  3. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/db_mysql.py +8 -6
  4. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/disk.py +39 -2
  5. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/distro.py +16 -11
  6. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/dmidecode.py +2 -2
  7. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/endoflifedate.py +28 -16
  8. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/lftest.py +24 -13
  9. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/linuxfabrik_lib.egg-info/PKG-INFO +1 -2
  10. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/linuxfabrik_lib.egg-info/requires.txt +0 -1
  11. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/net.py +151 -43
  12. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/nextcloud.py +7 -5
  13. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/pyproject.toml +1 -2
  14. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/redfish.py +651 -20
  15. linuxfabrik_lib-5.0.0/shell.py +204 -0
  16. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/ssh.py +74 -62
  17. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/time.py +26 -7
  18. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/url.py +41 -31
  19. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/version.py +18 -11
  20. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/winrm.py +0 -0
  21. linuxfabrik_lib-4.3.0/shell.py +0 -235
  22. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/LICENSE +0 -0
  23. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/README.md +0 -0
  24. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/__init__.py +0 -0
  25. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/args.py +0 -0
  26. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/cache.py +0 -0
  27. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/db_sqlite.py +0 -0
  28. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/feedparser.py +0 -0
  29. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/globals.py +0 -0
  30. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/grassfish.py +0 -0
  31. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/huawei.py +0 -0
  32. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/human.py +0 -0
  33. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/icinga.py +0 -0
  34. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/infomaniak.py +0 -0
  35. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/jitsi.py +0 -0
  36. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/keycloak.py +0 -0
  37. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/librenms.py +0 -0
  38. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/linuxfabrik_lib.egg-info/SOURCES.txt +0 -0
  39. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/linuxfabrik_lib.egg-info/dependency_links.txt +0 -0
  40. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/linuxfabrik_lib.egg-info/top_level.txt +0 -0
  41. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/nodebb.py +0 -0
  42. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/powershell.py +0 -0
  43. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/psutil.py +0 -0
  44. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/qts.py +0 -0
  45. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/rocket.py +0 -0
  46. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/setup.cfg +0 -0
  47. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/smb.py +0 -0
  48. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/txt.py +0 -0
  49. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/uptimerobot.py +0 -0
  50. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/veeam.py +0 -0
  51. {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/wildfly.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: linuxfabrik-lib
3
- Version: 4.3.0
3
+ Version: 5.0.0
4
4
  Summary: Python libraries used in various Linuxfabrik projects, including the 'Linuxfabrik Monitoring Plugins' project.
5
5
  Author-email: "Linuxfabrik GmbH, Zurich, Switzerland" <info@linuxfabrik.ch>
6
6
  License: This is free and unencumbered software released into the public domain.
@@ -59,7 +59,6 @@ License-File: LICENSE
59
59
  Requires-Dist: beautifulsoup4
60
60
  Requires-Dist: httpx[http2]
61
61
  Requires-Dist: lxml
62
- Requires-Dist: netifaces
63
62
  Requires-Dist: psutil
64
63
  Requires-Dist: pymysql
65
64
  Requires-Dist: pypsrp
@@ -11,7 +11,7 @@
11
11
  """Provides very common every-day functions."""
12
12
 
13
13
  __author__ = 'Linuxfabrik GmbH, Zurich/Switzerland'
14
- __version__ = '2026051002'
14
+ __version__ = '2026061201'
15
15
 
16
16
  import html
17
17
  import numbers
@@ -119,6 +119,9 @@ def cu(msg=None):
119
119
  tb = format_exc() if has_traceback else None
120
120
 
121
121
  if msg is not None:
122
+ # Normalize line endings to LF (see oao); error output may also carry
123
+ # CRLF, for example when a Windows command prints its error to stdout.
124
+ msg = msg.replace('\r\n', '\n').replace('\r', '\n')
122
125
  msg = (
123
126
  txt.sanitize_sensitive_data(msg).strip().replace('<', "'").replace('>', "'")
124
127
  )
@@ -628,6 +631,10 @@ def oao(msg, state=STATE_OK, perfdata='', always_ok=False):
628
631
  (and exits with code 2)
629
632
 
630
633
  """
634
+ # Normalize line endings to LF. Output captured on Windows (or read from a
635
+ # file or HTTP response) can carry CRLF or stray CR, which a web UI showing
636
+ # the output with `white-space: pre-wrap` would render as an extra line break.
637
+ msg = msg.replace('\r\n', '\n').replace('\r', '\n')
631
638
  msg = html.escape(
632
639
  txt.sanitize_sensitive_data(msg.strip()),
633
640
  quote=False,
@@ -20,7 +20,7 @@ from .globals import STATE_UNKNOWN
20
20
  warnings.filterwarnings('ignore', category=UserWarning, module='pymysql')
21
21
 
22
22
  __author__ = 'Linuxfabrik GmbH, Zurich/Switzerland'
23
- __version__ = '2026060201'
23
+ __version__ = '2026061201'
24
24
 
25
25
  try:
26
26
  import pymysql.cursors
@@ -302,7 +302,9 @@ def _align_session_collation(conn):
302
302
  # Whitelist identifiers to keep the formatted `SET NAMES` safe from
303
303
  # any caller-controlled value (the row comes from a system view, but
304
304
  # defence in depth doesn't cost anything).
305
- if not (re.match(r'^[A-Za-z0-9_]+$', cs) and re.match(r'^[A-Za-z0-9_]+$', coll)):
305
+ if not (
306
+ re.match(r'^[A-Za-z0-9_]+$', cs) and re.match(r'^[A-Za-z0-9_]+$', coll)
307
+ ):
306
308
  return
307
309
  with conn.cursor() as cursor:
308
310
  cursor.execute(f'set names {cs} collate {coll}')
@@ -541,10 +543,10 @@ def get_server_info(banner=None):
541
543
  from . import shell
542
544
 
543
545
  for command in (
544
- 'mysqld --version',
545
- 'mariadbd --version',
546
- 'mariadb --version',
547
- 'mysql --version',
546
+ ['mysqld', '--version'],
547
+ ['mariadbd', '--version'],
548
+ ['mariadb', '--version'],
549
+ ['mysql', '--version'],
548
550
  ):
549
551
  success, result = shell.shell_exec(command)
550
552
  if not success:
@@ -13,7 +13,7 @@ partitions, grepping a file, etc.
13
13
  """
14
14
 
15
15
  __author__ = 'Linuxfabrik GmbH, Zurich/Switzerland'
16
- __version__ = '2026060601'
16
+ __version__ = '2026061201'
17
17
 
18
18
  import csv
19
19
  import os
@@ -693,6 +693,36 @@ def rm_file(filename):
693
693
  return False, f'Unknown error deleting {filename}: {e}'
694
694
 
695
695
 
696
+ def shorten_path(path):
697
+ """
698
+ Shorten a path for display.
699
+
700
+ Abbreviate a slash-separated path by reducing every component except the last one to its
701
+ first character, the way the zsh prompt shortens a long working directory. The last
702
+ component is kept in full because it usually carries the identifying information. This is
703
+ a pure string transformation; the path is not resolved and the filesystem is not touched.
704
+
705
+ ### Parameters
706
+ - **path** (`str`): A slash-separated path, for example `/etc/pki/tls/certs/002c0b4f.0`.
707
+
708
+ ### Returns
709
+ - **str**: The abbreviated path, for example `/e/p/t/c/002c0b4f.0`. A value without a
710
+ slash (a bare basename, an empty string, a sentinel like `-`) is returned unchanged.
711
+
712
+ ### Example
713
+ >>> shorten_path('/etc/pki/tls/certs/002c0b4f.0')
714
+ '/e/p/t/c/002c0b4f.0'
715
+
716
+ >>> shorten_path('index.php')
717
+ 'index.php'
718
+ """
719
+ if not path or '/' not in path:
720
+ return path
721
+ head, _, tail = path.rpartition('/')
722
+ abbrev = '/'.join(part[:1] for part in head.split('/'))
723
+ return f'{abbrev}/{tail}'
724
+
725
+
696
726
  def udevadm(device, _property):
697
727
  """
698
728
  Run `udevadm info` and extract a specific property manually.
@@ -717,7 +747,14 @@ def udevadm(device, _property):
717
747
  >>> udevadm('/dev/linuxfabrik', 'DEVNAME')
718
748
  ''
719
749
  """
720
- success, result = shell.shell_exec(f'udevadm info --query=property --name={device}')
750
+ # Only query real device nodes under /dev. Resolving the path first defends
751
+ # against a crafted `device` that points elsewhere or smuggles arguments.
752
+ device = os.path.realpath(device)
753
+ if not device.startswith('/dev/'):
754
+ return ''
755
+ success, result = shell.shell_exec(
756
+ ['udevadm', 'info', '--query=property', f'--name={device}']
757
+ )
721
758
  if not success:
722
759
  return ''
723
760
  stdout, _, _ = result
@@ -17,15 +17,13 @@ Source Code is taken, converted, shortened and modified from:
17
17
  """
18
18
 
19
19
  __author__ = 'Linuxfabrik GmbH, Zurich/Switzerland'
20
- __version__ = '2025061001'
20
+ __version__ = '2026061201'
21
21
 
22
22
 
23
23
  import os
24
24
  import platform
25
25
  import re
26
26
 
27
- from . import shell
28
-
29
27
  # --- Static mappings ---
30
28
 
31
29
  OSDIST_LIST = (
@@ -430,15 +428,22 @@ def get_distribution_facts():
430
428
  distro = facts.get('distribution_file_variety', 'distribution')
431
429
  facts['os_family'] = _map_os_family(distro) # returns 'RedHat', for example
432
430
 
433
- # hardcoded command, no user input; shell=True needed to source the file
434
- cmd = '. /etc/os-release && echo "$NAME $VERSION"'
435
- success, result = shell.shell_exec(cmd, shell=True) # nosec B604
436
- if not success:
431
+ # read NAME and VERSION from /etc/os-release
432
+ values = {}
433
+ try:
434
+ with open('/etc/os-release', encoding='utf-8') as os_release:
435
+ for line in os_release:
436
+ line = line.strip()
437
+ if not line or line.startswith('#') or '=' not in line:
438
+ continue
439
+ key, value = line.split('=', 1)
440
+ values[key] = value.strip().strip('"').strip("'")
441
+ except OSError:
437
442
  return facts
438
443
 
439
- stdout, _, _ = result
440
- facts['os_info'] = (
441
- stdout.strip()
442
- ) # returns 'Fedora Linux 41 (Workstation Edition)', for example
444
+ # returns 'Fedora Linux 41 (Workstation Edition)', for example
445
+ facts['os_info'] = ' '.join(
446
+ part for part in (values.get('NAME'), values.get('VERSION')) if part
447
+ )
443
448
 
444
449
  return facts
@@ -14,7 +14,7 @@ Copied and refactored from py-dmidecode (https://github.com/zaibon/py-dmidecode)
14
14
  """
15
15
 
16
16
  __author__ = 'Linuxfabrik GmbH, Zurich/Switzerland'
17
- __version__ = '2026060201'
17
+ __version__ = '2026061201'
18
18
 
19
19
  import re
20
20
 
@@ -511,7 +511,7 @@ def get_data():
511
511
  ...
512
512
  }
513
513
  """
514
- success, result = shell.shell_exec('sudo dmidecode')
514
+ success, result = shell.shell_exec(['sudo', 'dmidecode'])
515
515
  if not success:
516
516
  return False
517
517
 
@@ -15,11 +15,11 @@ Internet."""
15
15
  # pylint: disable=C0302
16
16
 
17
17
  __author__ = 'Linuxfabrik GmbH, Zurich/Switzerland'
18
- __version__ = '2026060201' # adapt this if something in this script changed fundamentally
18
+ __version__ = '2026061001' # adapt this if something in this script changed fundamentally
19
19
 
20
20
  ENDOFLIFE_DATE = {
21
21
 
22
- 'https://endoflife.date/api/apache.json': [
22
+ 'https://endoflife.date/api/apache-http-server.json': [
23
23
  { 'cycle': '2.4',
24
24
  'eol': False,
25
25
  'latest': '2.4.67',
@@ -503,22 +503,22 @@ ENDOFLIFE_DATE = {
503
503
  'https://endoflife.date/api/gitlab.json': [
504
504
  { 'cycle': '19.0',
505
505
  'eol': '2026-08-20',
506
- 'latest': '19.0.1',
507
- 'latestReleaseDate': '2026-05-26',
506
+ 'latest': '19.0.2',
507
+ 'latestReleaseDate': '2026-06-10',
508
508
  'lts': False,
509
509
  'releaseDate': '2026-05-21',
510
510
  'support': '2026-06-18'},
511
511
  { 'cycle': '18.11',
512
512
  'eol': '2026-07-16',
513
- 'latest': '18.11.4',
514
- 'latestReleaseDate': '2026-05-26',
513
+ 'latest': '18.11.5',
514
+ 'latestReleaseDate': '2026-06-10',
515
515
  'lts': False,
516
516
  'releaseDate': '2026-04-16',
517
517
  'support': '2026-05-21'},
518
518
  { 'cycle': '18.10',
519
519
  'eol': '2026-06-18',
520
- 'latest': '18.10.7',
521
- 'latestReleaseDate': '2026-05-26',
520
+ 'latest': '18.10.8',
521
+ 'latestReleaseDate': '2026-06-10',
522
522
  'lts': False,
523
523
  'releaseDate': '2026-03-19',
524
524
  'support': '2026-04-16'},
@@ -956,7 +956,7 @@ ENDOFLIFE_DATE = {
956
956
  { 'cycle': '12.4',
957
957
  'eol': '2027-05-24',
958
958
  'latest': '12.4.4',
959
- 'latestReleaseDate': '2026-06-04',
959
+ 'latestReleaseDate': '2026-06-09',
960
960
  'lts': False,
961
961
  'releaseDate': '2026-02-24',
962
962
  'support': '2026-04-17'},
@@ -1927,8 +1927,8 @@ ENDOFLIFE_DATE = {
1927
1927
  'https://endoflife.date/api/matomo.json': [
1928
1928
  { 'cycle': '5',
1929
1929
  'eol': False,
1930
- 'latest': '5.10.1',
1931
- 'latestReleaseDate': '2026-05-29',
1930
+ 'latest': '5.11.1',
1931
+ 'latestReleaseDate': '2026-06-12',
1932
1932
  'lts': False,
1933
1933
  'releaseDate': '2023-12-18',
1934
1934
  'support': True},
@@ -1964,6 +1964,12 @@ ENDOFLIFE_DATE = {
1964
1964
 
1965
1965
 
1966
1966
  'https://endoflife.date/api/mediawiki.json': [
1967
+ { 'cycle': '1.46',
1968
+ 'eol': '2027-05-31',
1969
+ 'latest': '1.46.0',
1970
+ 'latestReleaseDate': '2026-05-31',
1971
+ 'lts': False,
1972
+ 'releaseDate': '2026-05-31'},
1967
1973
  { 'cycle': '1.45',
1968
1974
  'eol': '2026-12-31',
1969
1975
  'latest': '1.45.3',
@@ -2291,6 +2297,12 @@ ENDOFLIFE_DATE = {
2291
2297
 
2292
2298
 
2293
2299
  'https://endoflife.date/api/nextcloud.json': [
2300
+ { 'cycle': '34',
2301
+ 'eol': '2027-06-30',
2302
+ 'latest': '34.0.0',
2303
+ 'latestReleaseDate': '2026-06-08',
2304
+ 'lts': False,
2305
+ 'releaseDate': '2026-06-09'},
2294
2306
  { 'cycle': '33',
2295
2307
  'eol': '2027-02-28',
2296
2308
  'latest': '33.0.5',
@@ -2938,16 +2950,16 @@ ENDOFLIFE_DATE = {
2938
2950
  'https://endoflife.date/api/python.json': [
2939
2951
  { 'cycle': '3.14',
2940
2952
  'eol': '2030-10-31',
2941
- 'latest': '3.14.5',
2942
- 'latestReleaseDate': '2026-05-10',
2953
+ 'latest': '3.14.6',
2954
+ 'latestReleaseDate': '2026-06-10',
2943
2955
  'lts': False,
2944
2956
  'pep': 'PEP-0745',
2945
2957
  'releaseDate': '2025-10-07',
2946
2958
  'support': '2027-10-01'},
2947
2959
  { 'cycle': '3.13',
2948
2960
  'eol': '2029-10-31',
2949
- 'latest': '3.13.13',
2950
- 'latestReleaseDate': '2026-04-07',
2961
+ 'latest': '3.13.14',
2962
+ 'latestReleaseDate': '2026-06-10',
2951
2963
  'lts': False,
2952
2964
  'pep': 'PEP-0719',
2953
2965
  'releaseDate': '2024-10-07',
@@ -3267,7 +3279,7 @@ ENDOFLIFE_DATE = {
3267
3279
  ],
3268
3280
 
3269
3281
 
3270
- 'https://endoflife.date/api/rocketchat.json': [
3282
+ 'https://endoflife.date/api/rocket-chat.json': [
3271
3283
  { 'cycle': '8.4',
3272
3284
  'eol': '2026-10-31',
3273
3285
  'latest': '8.4.3',
@@ -13,12 +13,13 @@
13
13
  import contextlib
14
14
  import os
15
15
  import re
16
+ import shlex
16
17
  import tempfile
17
18
 
18
19
  from . import base, disk, shell
19
20
 
20
21
  __author__ = 'Linuxfabrik GmbH, Zurich/Switzerland'
21
- __version__ = '2026060202'
22
+ __version__ = '2026061201'
22
23
 
23
24
 
24
25
  def run(test_instance, plugin, testcase):
@@ -73,8 +74,10 @@ def run(test_instance, plugin, testcase):
73
74
  ... with self.subTest(id=t['id']):
74
75
  ... lib.lftest.run(self, self.check, t)
75
76
  """
77
+ # params come from the trusted testcase definition; tokenize them the way a
78
+ # shell would so quoted multi-word values stay intact, then build the argv.
76
79
  params = testcase.get('params', '')
77
- cmd = f'{plugin} {params} --test={testcase["test"]}'.strip()
80
+ cmd = [plugin, *shlex.split(params), f'--test={testcase["test"]}']
78
81
  stdout, stderr, retc = base.coe(shell.shell_exec(cmd))
79
82
 
80
83
  test_instance.assertEqual(
@@ -133,7 +136,6 @@ def attach_tests(test_class, tests, plugin_attr='check'):
133
136
  ### Example
134
137
  >>> class TestCheck(unittest.TestCase):
135
138
  ... check = '../my-plugin'
136
- ...
137
139
  >>> attach_tests(TestCheck, TESTS)
138
140
  >>>
139
141
  >>> if __name__ == '__main__':
@@ -157,6 +159,7 @@ def attach_tests(test_class, tests, plugin_attr='check'):
157
159
  def _make(captured_testcase):
158
160
  def _method(self):
159
161
  run(self, getattr(self, plugin_attr), captured_testcase)
162
+
160
163
  return _method
161
164
 
162
165
  setattr(test_class, method_name, _make(testcase))
@@ -222,6 +225,7 @@ def attach_each(test_class, items, action, id_func=str):
222
225
  def _make(captured_item):
223
226
  def _method(self):
224
227
  action(self, captured_item)
228
+
225
229
  return _method
226
230
 
227
231
  setattr(test_class, method_name, _make(item))
@@ -339,8 +343,7 @@ def run_container(
339
343
  from testcontainers.core.wait_strategies import LogMessageWaitStrategy
340
344
  except ImportError as e:
341
345
  raise RuntimeError(
342
- 'testcontainers is not installed; run '
343
- "`pip install testcontainers`"
346
+ 'testcontainers is not installed; run `pip install testcontainers`'
344
347
  ) from e
345
348
 
346
349
  c = DockerContainer(image)
@@ -456,12 +459,15 @@ def _run_mysql_compatible_resolved(image_ref, command, seed):
456
459
  # in favour of `mariadb`; sclorg c10s ships both; MySQL
457
460
  # ships `mysql` only. Prefer `mariadb`, fall back to
458
461
  # `mysql` so all flavours work.
459
- container.exec([
460
- 'sh', '-c',
461
- 'if command -v mariadb >/dev/null 2>&1; then CLIENT=mariadb; '
462
- 'else CLIENT=mysql; fi; '
463
- f'"$CLIENT" -utest -ptest test -e "{seed}"',
464
- ])
462
+ container.exec(
463
+ [
464
+ 'sh',
465
+ '-c',
466
+ 'if command -v mariadb >/dev/null 2>&1; then CLIENT=mariadb; '
467
+ 'else CLIENT=mysql; fi; '
468
+ f'"$CLIENT" -utest -ptest test -e "{seed}"',
469
+ ]
470
+ )
465
471
 
466
472
  host = container.get_container_host_ip()
467
473
  port = container.get_exposed_port(3306)
@@ -574,7 +580,8 @@ def run_mysql_compatible_from_containerfile(
574
580
  ... ) as (container, defaults_file):
575
581
  ... result = subprocess.run(
576
582
  ... ['python3', '../mysql-traffic', f'--defaults-file={defaults_file}'],
577
- ... capture_output=True, text=True,
583
+ ... capture_output=True,
584
+ ... text=True,
578
585
  ... )
579
586
  """
580
587
  try:
@@ -592,6 +599,8 @@ def run_mysql_compatible_from_containerfile(
592
599
  dockerfile_name = os.path.basename(abspath)
593
600
  tag = f'lfmp-mysql-{dockerfile_name}'.lower().replace('_', '-')
594
601
 
602
+ # No parenthesized context managers: they are Python 3.10+ syntax and break
603
+ # parsing on RHEL 8's default Python 3.6.
595
604
  with (
596
605
  DockerImage(
597
606
  path=build_dir,
@@ -600,7 +609,9 @@ def run_mysql_compatible_from_containerfile(
600
609
  clean_up=False,
601
610
  ) as image,
602
611
  _run_mysql_compatible_resolved(
603
- str(image.tag), command, seed,
612
+ str(image.tag),
613
+ command,
614
+ seed,
604
615
  ) as result,
605
616
  ):
606
617
  yield result
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: linuxfabrik-lib
3
- Version: 4.3.0
3
+ Version: 5.0.0
4
4
  Summary: Python libraries used in various Linuxfabrik projects, including the 'Linuxfabrik Monitoring Plugins' project.
5
5
  Author-email: "Linuxfabrik GmbH, Zurich, Switzerland" <info@linuxfabrik.ch>
6
6
  License: This is free and unencumbered software released into the public domain.
@@ -59,7 +59,6 @@ License-File: LICENSE
59
59
  Requires-Dist: beautifulsoup4
60
60
  Requires-Dist: httpx[http2]
61
61
  Requires-Dist: lxml
62
- Requires-Dist: netifaces
63
62
  Requires-Dist: psutil
64
63
  Requires-Dist: pymysql
65
64
  Requires-Dist: pypsrp
@@ -1,7 +1,6 @@
1
1
  beautifulsoup4
2
2
  httpx[http2]
3
3
  lxml
4
- netifaces
5
4
  psutil
6
5
  pymysql
7
6
  pypsrp