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.
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/PKG-INFO +1 -2
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/base.py +8 -1
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/db_mysql.py +8 -6
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/disk.py +39 -2
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/distro.py +16 -11
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/dmidecode.py +2 -2
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/endoflifedate.py +28 -16
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/lftest.py +24 -13
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/linuxfabrik_lib.egg-info/PKG-INFO +1 -2
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/linuxfabrik_lib.egg-info/requires.txt +0 -1
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/net.py +151 -43
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/nextcloud.py +7 -5
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/pyproject.toml +1 -2
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/redfish.py +651 -20
- linuxfabrik_lib-5.0.0/shell.py +204 -0
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/ssh.py +74 -62
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/time.py +26 -7
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/url.py +41 -31
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/version.py +18 -11
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/winrm.py +0 -0
- linuxfabrik_lib-4.3.0/shell.py +0 -235
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/LICENSE +0 -0
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/README.md +0 -0
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/__init__.py +0 -0
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/args.py +0 -0
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/cache.py +0 -0
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/db_sqlite.py +0 -0
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/feedparser.py +0 -0
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/globals.py +0 -0
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/grassfish.py +0 -0
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/huawei.py +0 -0
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/human.py +0 -0
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/icinga.py +0 -0
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/infomaniak.py +0 -0
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/jitsi.py +0 -0
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/keycloak.py +0 -0
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/librenms.py +0 -0
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/linuxfabrik_lib.egg-info/SOURCES.txt +0 -0
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/linuxfabrik_lib.egg-info/dependency_links.txt +0 -0
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/linuxfabrik_lib.egg-info/top_level.txt +0 -0
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/nodebb.py +0 -0
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/powershell.py +0 -0
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/psutil.py +0 -0
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/qts.py +0 -0
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/rocket.py +0 -0
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/setup.cfg +0 -0
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/smb.py +0 -0
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/txt.py +0 -0
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/uptimerobot.py +0 -0
- {linuxfabrik_lib-4.3.0 → linuxfabrik_lib-5.0.0}/veeam.py +0 -0
- {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:
|
|
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__ = '
|
|
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__ = '
|
|
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 (
|
|
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__ = '
|
|
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
|
-
|
|
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__ = '
|
|
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
|
-
#
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
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
|
-
|
|
440
|
-
facts['os_info'] = (
|
|
441
|
-
|
|
442
|
-
)
|
|
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__ = '
|
|
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__ = '
|
|
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.
|
|
507
|
-
'latestReleaseDate': '2026-
|
|
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.
|
|
514
|
-
'latestReleaseDate': '2026-
|
|
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.
|
|
521
|
-
'latestReleaseDate': '2026-
|
|
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-
|
|
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.
|
|
1931
|
-
'latestReleaseDate': '2026-
|
|
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.
|
|
2942
|
-
'latestReleaseDate': '2026-
|
|
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.
|
|
2950
|
-
'latestReleaseDate': '2026-
|
|
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/
|
|
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__ = '
|
|
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 =
|
|
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
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
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,
|
|
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),
|
|
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:
|
|
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
|