linuxfabrik-lib 4.4.0__tar.gz → 5.1.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.4.0 → linuxfabrik_lib-5.1.0}/PKG-INFO +1 -1
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/base.py +8 -1
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/db_mysql.py +8 -6
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/db_sqlite.py +38 -8
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/disk.py +9 -2
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/distro.py +16 -11
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/dmidecode.py +2 -2
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/endoflifedate.py +139 -46
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/lftest.py +38 -23
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/linuxfabrik_lib.egg-info/PKG-INFO +1 -1
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/nextcloud.py +7 -5
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/pyproject.toml +1 -1
- linuxfabrik_lib-5.1.0/shell.py +204 -0
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/ssh.py +74 -62
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/time.py +13 -3
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/txt.py +3 -2
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/url.py +3 -4
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/version.py +18 -11
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/winrm.py +0 -0
- linuxfabrik_lib-4.4.0/shell.py +0 -235
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/LICENSE +0 -0
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/README.md +0 -0
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/__init__.py +0 -0
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/args.py +0 -0
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/cache.py +0 -0
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/feedparser.py +0 -0
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/globals.py +0 -0
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/grassfish.py +0 -0
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/huawei.py +0 -0
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/human.py +0 -0
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/icinga.py +0 -0
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/infomaniak.py +0 -0
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/jitsi.py +0 -0
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/keycloak.py +0 -0
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/librenms.py +0 -0
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/linuxfabrik_lib.egg-info/SOURCES.txt +0 -0
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/linuxfabrik_lib.egg-info/dependency_links.txt +0 -0
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/linuxfabrik_lib.egg-info/requires.txt +0 -0
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/linuxfabrik_lib.egg-info/top_level.txt +0 -0
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/net.py +0 -0
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/nodebb.py +0 -0
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/powershell.py +0 -0
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/psutil.py +0 -0
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/qts.py +0 -0
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/redfish.py +0 -0
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/rocket.py +0 -0
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/setup.cfg +0 -0
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/smb.py +0 -0
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/uptimerobot.py +0 -0
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.0}/veeam.py +0 -0
- {linuxfabrik_lib-4.4.0 → linuxfabrik_lib-5.1.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.1.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.
|
|
@@ -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:
|
|
@@ -28,7 +28,7 @@ This is one typical use case of this library (taken from `disk-io`):
|
|
|
28
28
|
"""
|
|
29
29
|
|
|
30
30
|
__author__ = 'Linuxfabrik GmbH, Zurich/Switzerland'
|
|
31
|
-
__version__ = '
|
|
31
|
+
__version__ = '2026061701'
|
|
32
32
|
|
|
33
33
|
import csv
|
|
34
34
|
import hashlib
|
|
@@ -355,7 +355,8 @@ def create_index(
|
|
|
355
355
|
If `True`, creates a unique index.
|
|
356
356
|
If `False`, creates a standard (non-unique) index. Defaults to `False`.
|
|
357
357
|
- **delete_db_on_operational_error** (`bool`, optional):
|
|
358
|
-
If `True`, deletes the database file when
|
|
358
|
+
If `True`, deletes the database file when the on-disk database turns out
|
|
359
|
+
to be unusable (e.g. a schema mismatch between releases).
|
|
359
360
|
Defaults to `True`.
|
|
360
361
|
|
|
361
362
|
### Returns
|
|
@@ -467,7 +468,8 @@ def cut(conn, table='perfdata', _max=5, delete_db_on_operational_error=True):
|
|
|
467
468
|
- **_max** (`int`, optional):
|
|
468
469
|
Number of most recent records to keep. Defaults to `5`.
|
|
469
470
|
- **delete_db_on_operational_error** (`bool`, optional):
|
|
470
|
-
If `True`, deletes the database file when
|
|
471
|
+
If `True`, deletes the database file when the on-disk database turns out
|
|
472
|
+
to be unusable (e.g. a schema mismatch between releases).
|
|
471
473
|
Defaults to `True`.
|
|
472
474
|
|
|
473
475
|
### Returns
|
|
@@ -530,7 +532,8 @@ def delete(conn, sql, data=None, delete_db_on_operational_error=True):
|
|
|
530
532
|
Dictionary of parameters to bind to the SQL statement.
|
|
531
533
|
Defaults to an empty dict (no parameters).
|
|
532
534
|
- **delete_db_on_operational_error** (`bool`, optional):
|
|
533
|
-
If `True`, deletes the database file when
|
|
535
|
+
If `True`, deletes the database file when the on-disk database turns out
|
|
536
|
+
to be unusable (e.g. a schema mismatch between releases).
|
|
534
537
|
Defaults to `True`.
|
|
535
538
|
|
|
536
539
|
### Returns
|
|
@@ -563,6 +566,14 @@ def delete(conn, sql, data=None, delete_db_on_operational_error=True):
|
|
|
563
566
|
if delete_db_on_operational_error:
|
|
564
567
|
rm_db(conn)
|
|
565
568
|
return False, f'Operational Error: {e}, Query: {sql}, Data: {data}'
|
|
569
|
+
except (sqlite3.IntegrityError, sqlite3.DataError) as e:
|
|
570
|
+
# A constraint or data error (e.g. a NOT NULL column that a newer or
|
|
571
|
+
# older release no longer writes) means the on-disk schema no
|
|
572
|
+
# longer matches the data being written. Deleting the database lets the
|
|
573
|
+
# next run rebuild a valid cache from scratch.
|
|
574
|
+
if delete_db_on_operational_error:
|
|
575
|
+
rm_db(conn)
|
|
576
|
+
return False, f'Integrity Error: {e}, Query: {sql}, Data: {data}'
|
|
566
577
|
except Exception as e:
|
|
567
578
|
return False, f'Query failed: {sql}, Error: {e}, Data: {data}'
|
|
568
579
|
|
|
@@ -906,7 +917,8 @@ def insert(conn, data, table='perfdata', delete_db_on_operational_error=True):
|
|
|
906
917
|
Name of the table to insert into.
|
|
907
918
|
Defaults to `'perfdata'`.
|
|
908
919
|
- **delete_db_on_operational_error** (`bool`, optional):
|
|
909
|
-
If `True`, deletes the database file when
|
|
920
|
+
If `True`, deletes the database file when the on-disk database turns out
|
|
921
|
+
to be unusable (e.g. a schema mismatch between releases).
|
|
910
922
|
Defaults to `True`.
|
|
911
923
|
|
|
912
924
|
### Returns
|
|
@@ -946,6 +958,14 @@ def insert(conn, data, table='perfdata', delete_db_on_operational_error=True):
|
|
|
946
958
|
if delete_db_on_operational_error:
|
|
947
959
|
rm_db(conn)
|
|
948
960
|
return False, f'Operational Error: {e}, Query: {sql}, Data: {data}'
|
|
961
|
+
except (sqlite3.IntegrityError, sqlite3.DataError) as e:
|
|
962
|
+
# A constraint or data error (e.g. a NOT NULL column that a newer or
|
|
963
|
+
# older release no longer writes) means the on-disk schema no
|
|
964
|
+
# longer matches the data being written. Deleting the database lets the
|
|
965
|
+
# next run rebuild a valid cache from scratch.
|
|
966
|
+
if delete_db_on_operational_error:
|
|
967
|
+
rm_db(conn)
|
|
968
|
+
return False, f'Integrity Error: {e}, Query: {sql}, Data: {data}'
|
|
949
969
|
except Exception as e:
|
|
950
970
|
return False, f'Query failed: {sql}, Error: {e}, Data: {data}'
|
|
951
971
|
|
|
@@ -1030,7 +1050,7 @@ def per_second_deltas(filename, name, counters):
|
|
|
1030
1050
|
# subsequent drop_table() with "Cannot operate on a closed database".
|
|
1031
1051
|
ok, _ = insert(conn, row, delete_db_on_operational_error=False)
|
|
1032
1052
|
if not ok:
|
|
1033
|
-
# Schema mismatch from a previous
|
|
1053
|
+
# Schema mismatch from a previous release (different counter
|
|
1034
1054
|
# columns or NOT NULL constraints). Rebuild the table from the
|
|
1035
1055
|
# current schema; we lose the previous baseline but auto-recover on
|
|
1036
1056
|
# the next run.
|
|
@@ -1130,7 +1150,8 @@ def replace(conn, data, table='perfdata', delete_db_on_operational_error=True):
|
|
|
1130
1150
|
Name of the table to operate on.
|
|
1131
1151
|
Defaults to `'perfdata'`.
|
|
1132
1152
|
- **delete_db_on_operational_error** (`bool`, optional):
|
|
1133
|
-
If `True`, deletes the database file when
|
|
1153
|
+
If `True`, deletes the database file when the on-disk database turns out
|
|
1154
|
+
to be unusable (e.g. a schema mismatch between releases).
|
|
1134
1155
|
Defaults to `True`.
|
|
1135
1156
|
|
|
1136
1157
|
### Returns
|
|
@@ -1169,6 +1190,14 @@ def replace(conn, data, table='perfdata', delete_db_on_operational_error=True):
|
|
|
1169
1190
|
if delete_db_on_operational_error:
|
|
1170
1191
|
rm_db(conn)
|
|
1171
1192
|
return False, f'Operational Error: {e}, Query: {sql}, Data: {data}'
|
|
1193
|
+
except (sqlite3.IntegrityError, sqlite3.DataError) as e:
|
|
1194
|
+
# A constraint or data error (e.g. a NOT NULL column that a newer or
|
|
1195
|
+
# older release no longer writes) means the on-disk schema no
|
|
1196
|
+
# longer matches the data being written. Deleting the database lets the
|
|
1197
|
+
# next run rebuild a valid cache from scratch.
|
|
1198
|
+
if delete_db_on_operational_error:
|
|
1199
|
+
rm_db(conn)
|
|
1200
|
+
return False, f'Integrity Error: {e}, Query: {sql}, Data: {data}'
|
|
1172
1201
|
except Exception as e:
|
|
1173
1202
|
return False, f'Query failed: {sql}, Error: {e}, Data: {data}'
|
|
1174
1203
|
|
|
@@ -1237,7 +1266,8 @@ def select(
|
|
|
1237
1266
|
If `True`, return results as a list of dictionaries.
|
|
1238
1267
|
If `False`, return raw SQLite row objects. Defaults to `True`.
|
|
1239
1268
|
- **delete_db_on_operational_error** (`bool`, optional):
|
|
1240
|
-
If `True`, deletes the database file when
|
|
1269
|
+
If `True`, deletes the database file when the on-disk database turns out
|
|
1270
|
+
to be unusable (e.g. a schema mismatch between releases).
|
|
1241
1271
|
Defaults to `True`.
|
|
1242
1272
|
|
|
1243
1273
|
### Returns
|
|
@@ -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
|
|
@@ -747,7 +747,14 @@ def udevadm(device, _property):
|
|
|
747
747
|
>>> udevadm('/dev/linuxfabrik', 'DEVNAME')
|
|
748
748
|
''
|
|
749
749
|
"""
|
|
750
|
-
|
|
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
|
+
)
|
|
751
758
|
if not success:
|
|
752
759
|
return ''
|
|
753
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,15 +15,15 @@ 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
|
-
'latest': '2.4.
|
|
26
|
-
'latestReleaseDate': '2026-
|
|
25
|
+
'latest': '2.4.68',
|
|
26
|
+
'latestReleaseDate': '2026-06-08',
|
|
27
27
|
'link': 'https://downloads.apache.org/httpd/CHANGES_2.4',
|
|
28
28
|
'lts': False,
|
|
29
29
|
'releaseDate': '2012-02-21'},
|
|
@@ -501,24 +501,31 @@ ENDOFLIFE_DATE = {
|
|
|
501
501
|
|
|
502
502
|
|
|
503
503
|
'https://endoflife.date/api/gitlab.json': [
|
|
504
|
+
{ 'cycle': '19.1',
|
|
505
|
+
'eol': '2026-09-17',
|
|
506
|
+
'latest': '19.1.0',
|
|
507
|
+
'latestReleaseDate': '2026-06-17',
|
|
508
|
+
'lts': False,
|
|
509
|
+
'releaseDate': '2026-06-18',
|
|
510
|
+
'support': '2026-07-16'},
|
|
504
511
|
{ 'cycle': '19.0',
|
|
505
512
|
'eol': '2026-08-20',
|
|
506
|
-
'latest': '19.0.
|
|
507
|
-
'latestReleaseDate': '2026-
|
|
513
|
+
'latest': '19.0.2',
|
|
514
|
+
'latestReleaseDate': '2026-06-10',
|
|
508
515
|
'lts': False,
|
|
509
516
|
'releaseDate': '2026-05-21',
|
|
510
517
|
'support': '2026-06-18'},
|
|
511
518
|
{ 'cycle': '18.11',
|
|
512
519
|
'eol': '2026-07-16',
|
|
513
|
-
'latest': '18.11.
|
|
514
|
-
'latestReleaseDate': '2026-
|
|
520
|
+
'latest': '18.11.5',
|
|
521
|
+
'latestReleaseDate': '2026-06-10',
|
|
515
522
|
'lts': False,
|
|
516
523
|
'releaseDate': '2026-04-16',
|
|
517
524
|
'support': '2026-05-21'},
|
|
518
525
|
{ 'cycle': '18.10',
|
|
519
526
|
'eol': '2026-06-18',
|
|
520
|
-
'latest': '18.10.
|
|
521
|
-
'latestReleaseDate': '2026-
|
|
527
|
+
'latest': '18.10.8',
|
|
528
|
+
'latestReleaseDate': '2026-06-10',
|
|
522
529
|
'lts': False,
|
|
523
530
|
'releaseDate': '2026-03-19',
|
|
524
531
|
'support': '2026-04-16'},
|
|
@@ -956,7 +963,7 @@ ENDOFLIFE_DATE = {
|
|
|
956
963
|
{ 'cycle': '12.4',
|
|
957
964
|
'eol': '2027-05-24',
|
|
958
965
|
'latest': '12.4.4',
|
|
959
|
-
'latestReleaseDate': '2026-06-
|
|
966
|
+
'latestReleaseDate': '2026-06-09',
|
|
960
967
|
'lts': False,
|
|
961
968
|
'releaseDate': '2026-02-24',
|
|
962
969
|
'support': '2026-04-17'},
|
|
@@ -1140,7 +1147,7 @@ ENDOFLIFE_DATE = {
|
|
|
1140
1147
|
|
|
1141
1148
|
'https://endoflife.date/api/graylog.json': [
|
|
1142
1149
|
{ 'cycle': '7.1',
|
|
1143
|
-
'eol':
|
|
1150
|
+
'eol': '2027-05-04',
|
|
1144
1151
|
'latest': '7.1.3',
|
|
1145
1152
|
'latestReleaseDate': '2026-06-03',
|
|
1146
1153
|
'lts': False,
|
|
@@ -1837,6 +1844,12 @@ ENDOFLIFE_DATE = {
|
|
|
1837
1844
|
|
|
1838
1845
|
|
|
1839
1846
|
'https://endoflife.date/api/mastodon.json': [
|
|
1847
|
+
{ 'cycle': '4.6',
|
|
1848
|
+
'eol': False,
|
|
1849
|
+
'latest': '4.6.0',
|
|
1850
|
+
'latestReleaseDate': '2026-06-17',
|
|
1851
|
+
'lts': False,
|
|
1852
|
+
'releaseDate': '2026-06-17'},
|
|
1840
1853
|
{ 'cycle': '4.5',
|
|
1841
1854
|
'eol': False,
|
|
1842
1855
|
'latest': '4.5.11',
|
|
@@ -1844,7 +1857,7 @@ ENDOFLIFE_DATE = {
|
|
|
1844
1857
|
'lts': False,
|
|
1845
1858
|
'releaseDate': '2025-11-06'},
|
|
1846
1859
|
{ 'cycle': '4.4',
|
|
1847
|
-
'eol':
|
|
1860
|
+
'eol': '2026-12-17',
|
|
1848
1861
|
'latest': '4.4.18',
|
|
1849
1862
|
'latestReleaseDate': '2026-06-03',
|
|
1850
1863
|
'lts': False,
|
|
@@ -1927,8 +1940,8 @@ ENDOFLIFE_DATE = {
|
|
|
1927
1940
|
'https://endoflife.date/api/matomo.json': [
|
|
1928
1941
|
{ 'cycle': '5',
|
|
1929
1942
|
'eol': False,
|
|
1930
|
-
'latest': '5.11.
|
|
1931
|
-
'latestReleaseDate': '2026-06-
|
|
1943
|
+
'latest': '5.11.2',
|
|
1944
|
+
'latestReleaseDate': '2026-06-17',
|
|
1932
1945
|
'lts': False,
|
|
1933
1946
|
'releaseDate': '2023-12-18',
|
|
1934
1947
|
'support': True},
|
|
@@ -2174,8 +2187,8 @@ ENDOFLIFE_DATE = {
|
|
|
2174
2187
|
'https://endoflife.date/api/mysql.json': [
|
|
2175
2188
|
{ 'cycle': '9.7',
|
|
2176
2189
|
'eol': '2034-04-21',
|
|
2177
|
-
'latest': '9.7.
|
|
2178
|
-
'latestReleaseDate': '2026-
|
|
2190
|
+
'latest': '9.7.1',
|
|
2191
|
+
'latestReleaseDate': '2026-06-03',
|
|
2179
2192
|
'link': 'https://dev.mysql.com/doc/relnotes/mysql/9.7/en/news-9-7-0.html',
|
|
2180
2193
|
'lts': True,
|
|
2181
2194
|
'releaseDate': '2026-04-21',
|
|
@@ -2237,8 +2250,8 @@ ENDOFLIFE_DATE = {
|
|
|
2237
2250
|
'support': '2024-10-15'},
|
|
2238
2251
|
{ 'cycle': '8.4',
|
|
2239
2252
|
'eol': '2032-04-30',
|
|
2240
|
-
'latest': '8.4.
|
|
2241
|
-
'latestReleaseDate': '2026-
|
|
2253
|
+
'latest': '8.4.10',
|
|
2254
|
+
'latestReleaseDate': '2026-06-03',
|
|
2242
2255
|
'lts': True,
|
|
2243
2256
|
'releaseDate': '2024-04-10',
|
|
2244
2257
|
'support': '2029-04-30'},
|
|
@@ -2297,6 +2310,12 @@ ENDOFLIFE_DATE = {
|
|
|
2297
2310
|
|
|
2298
2311
|
|
|
2299
2312
|
'https://endoflife.date/api/nextcloud.json': [
|
|
2313
|
+
{ 'cycle': '34',
|
|
2314
|
+
'eol': '2027-06-30',
|
|
2315
|
+
'latest': '34.0.0',
|
|
2316
|
+
'latestReleaseDate': '2026-06-08',
|
|
2317
|
+
'lts': False,
|
|
2318
|
+
'releaseDate': '2026-06-09'},
|
|
2300
2319
|
{ 'cycle': '33',
|
|
2301
2320
|
'eol': '2027-02-28',
|
|
2302
2321
|
'latest': '33.0.5',
|
|
@@ -2636,29 +2655,29 @@ ENDOFLIFE_DATE = {
|
|
|
2636
2655
|
'https://endoflife.date/api/postfix.json': [
|
|
2637
2656
|
{ 'cycle': '3.11',
|
|
2638
2657
|
'eol': False,
|
|
2639
|
-
'latest': '3.11.
|
|
2640
|
-
'latestReleaseDate': '2026-
|
|
2658
|
+
'latest': '3.11.4',
|
|
2659
|
+
'latestReleaseDate': '2026-06-18',
|
|
2641
2660
|
'link': 'https://www.postfix.org/announcements/postfix-3.11.0.html',
|
|
2642
2661
|
'lts': False,
|
|
2643
2662
|
'releaseDate': '2026-03-06'},
|
|
2644
2663
|
{ 'cycle': '3.10',
|
|
2645
2664
|
'eol': False,
|
|
2646
|
-
'latest': '3.10.
|
|
2647
|
-
'latestReleaseDate': '2026-
|
|
2665
|
+
'latest': '3.10.11',
|
|
2666
|
+
'latestReleaseDate': '2026-06-18',
|
|
2648
2667
|
'link': 'https://www.postfix.org/announcements/postfix-3.10.0.html',
|
|
2649
2668
|
'lts': False,
|
|
2650
2669
|
'releaseDate': '2025-02-16'},
|
|
2651
2670
|
{ 'cycle': '3.9',
|
|
2652
2671
|
'eol': False,
|
|
2653
|
-
'latest': '3.9.
|
|
2654
|
-
'latestReleaseDate': '2026-
|
|
2672
|
+
'latest': '3.9.12',
|
|
2673
|
+
'latestReleaseDate': '2026-06-18',
|
|
2655
2674
|
'link': 'https://www.postfix.org/announcements/postfix-3.9.2.html',
|
|
2656
2675
|
'lts': False,
|
|
2657
2676
|
'releaseDate': '2024-03-06'},
|
|
2658
2677
|
{ 'cycle': '3.8',
|
|
2659
2678
|
'eol': False,
|
|
2660
|
-
'latest': '3.8.
|
|
2661
|
-
'latestReleaseDate': '2026-
|
|
2679
|
+
'latest': '3.8.18',
|
|
2680
|
+
'latestReleaseDate': '2026-06-18',
|
|
2662
2681
|
'link': 'https://www.postfix.org/announcements/postfix-3.9.2.html',
|
|
2663
2682
|
'lts': False,
|
|
2664
2683
|
'releaseDate': '2023-04-17'},
|
|
@@ -2944,16 +2963,16 @@ ENDOFLIFE_DATE = {
|
|
|
2944
2963
|
'https://endoflife.date/api/python.json': [
|
|
2945
2964
|
{ 'cycle': '3.14',
|
|
2946
2965
|
'eol': '2030-10-31',
|
|
2947
|
-
'latest': '3.14.
|
|
2948
|
-
'latestReleaseDate': '2026-
|
|
2966
|
+
'latest': '3.14.6',
|
|
2967
|
+
'latestReleaseDate': '2026-06-10',
|
|
2949
2968
|
'lts': False,
|
|
2950
2969
|
'pep': 'PEP-0745',
|
|
2951
2970
|
'releaseDate': '2025-10-07',
|
|
2952
2971
|
'support': '2027-10-01'},
|
|
2953
2972
|
{ 'cycle': '3.13',
|
|
2954
2973
|
'eol': '2029-10-31',
|
|
2955
|
-
'latest': '3.13.
|
|
2956
|
-
'latestReleaseDate': '2026-
|
|
2974
|
+
'latest': '3.13.14',
|
|
2975
|
+
'latestReleaseDate': '2026-06-10',
|
|
2957
2976
|
'lts': False,
|
|
2958
2977
|
'pep': 'PEP-0719',
|
|
2959
2978
|
'releaseDate': '2024-10-07',
|
|
@@ -3273,46 +3292,53 @@ ENDOFLIFE_DATE = {
|
|
|
3273
3292
|
],
|
|
3274
3293
|
|
|
3275
3294
|
|
|
3276
|
-
'https://endoflife.date/api/
|
|
3295
|
+
'https://endoflife.date/api/rocket-chat.json': [
|
|
3296
|
+
{ 'cycle': '8.5',
|
|
3297
|
+
'eol': '2027-06-10',
|
|
3298
|
+
'latest': '8.5.1',
|
|
3299
|
+
'latestReleaseDate': '2026-06-15',
|
|
3300
|
+
'lts': True,
|
|
3301
|
+
'releaseDate': '2026-06-10',
|
|
3302
|
+
'support': '2026-06-10'},
|
|
3277
3303
|
{ 'cycle': '8.4',
|
|
3278
3304
|
'eol': '2026-10-31',
|
|
3279
|
-
'latest': '8.4.
|
|
3280
|
-
'latestReleaseDate': '2026-06-
|
|
3305
|
+
'latest': '8.4.4',
|
|
3306
|
+
'latestReleaseDate': '2026-06-15',
|
|
3281
3307
|
'lts': False,
|
|
3282
3308
|
'releaseDate': '2026-04-30',
|
|
3283
3309
|
'support': '2026-04-30'},
|
|
3284
3310
|
{ 'cycle': '8.3',
|
|
3285
3311
|
'eol': '2026-10-31',
|
|
3286
|
-
'latest': '8.3.
|
|
3287
|
-
'latestReleaseDate': '2026-06-
|
|
3312
|
+
'latest': '8.3.6',
|
|
3313
|
+
'latestReleaseDate': '2026-06-15',
|
|
3288
3314
|
'lts': False,
|
|
3289
3315
|
'releaseDate': '2026-04-07',
|
|
3290
3316
|
'support': '2026-04-07'},
|
|
3291
3317
|
{ 'cycle': '8.2',
|
|
3292
3318
|
'eol': '2026-09-30',
|
|
3293
|
-
'latest': '8.2.
|
|
3294
|
-
'latestReleaseDate': '2026-06-
|
|
3319
|
+
'latest': '8.2.6',
|
|
3320
|
+
'latestReleaseDate': '2026-06-15',
|
|
3295
3321
|
'lts': False,
|
|
3296
3322
|
'releaseDate': '2026-03-02',
|
|
3297
3323
|
'support': '2026-03-02'},
|
|
3298
3324
|
{ 'cycle': '8.1',
|
|
3299
3325
|
'eol': '2026-08-31',
|
|
3300
|
-
'latest': '8.1.
|
|
3301
|
-
'latestReleaseDate': '2026-
|
|
3326
|
+
'latest': '8.1.6',
|
|
3327
|
+
'latestReleaseDate': '2026-06-15',
|
|
3302
3328
|
'lts': False,
|
|
3303
3329
|
'releaseDate': '2026-02-10',
|
|
3304
3330
|
'support': '2026-02-10'},
|
|
3305
3331
|
{ 'cycle': '8.0',
|
|
3306
3332
|
'eol': '2026-06-30',
|
|
3307
|
-
'latest': '8.0.
|
|
3308
|
-
'latestReleaseDate': '2026-
|
|
3333
|
+
'latest': '8.0.7',
|
|
3334
|
+
'latestReleaseDate': '2026-06-15',
|
|
3309
3335
|
'lts': False,
|
|
3310
3336
|
'releaseDate': '2026-01-12',
|
|
3311
3337
|
'support': '2026-01-12'},
|
|
3312
3338
|
{ 'cycle': '7.13',
|
|
3313
3339
|
'eol': '2026-05-31',
|
|
3314
|
-
'latest': '7.13.
|
|
3315
|
-
'latestReleaseDate': '2026-
|
|
3340
|
+
'latest': '7.13.9',
|
|
3341
|
+
'latestReleaseDate': '2026-06-15',
|
|
3316
3342
|
'lts': False,
|
|
3317
3343
|
'releaseDate': '2025-12-05',
|
|
3318
3344
|
'support': '2025-12-05'},
|
|
@@ -3332,8 +3358,8 @@ ENDOFLIFE_DATE = {
|
|
|
3332
3358
|
'support': '2025-10-17'},
|
|
3333
3359
|
{ 'cycle': '7.10',
|
|
3334
3360
|
'eol': '2026-06-30',
|
|
3335
|
-
'latest': '7.10.
|
|
3336
|
-
'latestReleaseDate': '2026-
|
|
3361
|
+
'latest': '7.10.13',
|
|
3362
|
+
'latestReleaseDate': '2026-06-15',
|
|
3337
3363
|
'lts': True,
|
|
3338
3364
|
'releaseDate': '2025-09-04',
|
|
3339
3365
|
'support': '2025-09-04'},
|
|
@@ -3862,6 +3888,73 @@ ENDOFLIFE_DATE = {
|
|
|
3862
3888
|
],
|
|
3863
3889
|
|
|
3864
3890
|
|
|
3891
|
+
'https://endoflife.date/api/tomcat.json': [
|
|
3892
|
+
{ 'cycle': '11.0',
|
|
3893
|
+
'eol': False,
|
|
3894
|
+
'latest': '11.0.23',
|
|
3895
|
+
'latestReleaseDate': '2026-06-17',
|
|
3896
|
+
'lts': False,
|
|
3897
|
+
'minJavaVersion': '17',
|
|
3898
|
+
'releaseDate': '2024-10-03'},
|
|
3899
|
+
{ 'cycle': '10.1',
|
|
3900
|
+
'eol': False,
|
|
3901
|
+
'latest': '10.1.56',
|
|
3902
|
+
'latestReleaseDate': '2026-06-17',
|
|
3903
|
+
'lts': False,
|
|
3904
|
+
'minJavaVersion': '11',
|
|
3905
|
+
'releaseDate': '2022-09-23'},
|
|
3906
|
+
{ 'cycle': '10.0',
|
|
3907
|
+
'eol': '2022-10-31',
|
|
3908
|
+
'latest': '10.0.27',
|
|
3909
|
+
'latestReleaseDate': '2022-10-03',
|
|
3910
|
+
'lts': False,
|
|
3911
|
+
'minJavaVersion': '8',
|
|
3912
|
+
'releaseDate': '2020-12-03'},
|
|
3913
|
+
{ 'cycle': '9.0',
|
|
3914
|
+
'eol': '2027-03-31',
|
|
3915
|
+
'latest': '9.0.119',
|
|
3916
|
+
'latestReleaseDate': '2026-06-18',
|
|
3917
|
+
'lts': False,
|
|
3918
|
+
'minJavaVersion': '8',
|
|
3919
|
+
'releaseDate': '2017-09-27'},
|
|
3920
|
+
{ 'cycle': '8.5',
|
|
3921
|
+
'eol': '2024-03-31',
|
|
3922
|
+
'latest': '8.5.100',
|
|
3923
|
+
'latestReleaseDate': '2024-03-19',
|
|
3924
|
+
'lts': False,
|
|
3925
|
+
'minJavaVersion': '7',
|
|
3926
|
+
'releaseDate': '2016-03-17'},
|
|
3927
|
+
{ 'cycle': '8.0',
|
|
3928
|
+
'eol': '2018-06-30',
|
|
3929
|
+
'latest': '8.0.53',
|
|
3930
|
+
'latestReleaseDate': '2018-06-29',
|
|
3931
|
+
'lts': False,
|
|
3932
|
+
'minJavaVersion': '7',
|
|
3933
|
+
'releaseDate': '2014-01-29'},
|
|
3934
|
+
{ 'cycle': '7',
|
|
3935
|
+
'eol': '2021-03-31',
|
|
3936
|
+
'latest': '7.0.109',
|
|
3937
|
+
'latestReleaseDate': '2021-04-22',
|
|
3938
|
+
'lts': False,
|
|
3939
|
+
'minJavaVersion': '6',
|
|
3940
|
+
'releaseDate': '2013-01-10'},
|
|
3941
|
+
{ 'cycle': '6',
|
|
3942
|
+
'eol': '2016-12-31',
|
|
3943
|
+
'latest': '6.0.53',
|
|
3944
|
+
'latestReleaseDate': '2017-04-02',
|
|
3945
|
+
'lts': False,
|
|
3946
|
+
'minJavaVersion': '5',
|
|
3947
|
+
'releaseDate': '2006-10-21'},
|
|
3948
|
+
{ 'cycle': '5',
|
|
3949
|
+
'eol': '2012-09-30',
|
|
3950
|
+
'latest': '5.5.36',
|
|
3951
|
+
'latestReleaseDate': '2012-10-10',
|
|
3952
|
+
'lts': False,
|
|
3953
|
+
'minJavaVersion': '1.4',
|
|
3954
|
+
'releaseDate': '2003-09-06'},
|
|
3955
|
+
],
|
|
3956
|
+
|
|
3957
|
+
|
|
3865
3958
|
'https://endoflife.date/api/valkey.json': [
|
|
3866
3959
|
{ 'cycle': '9.1',
|
|
3867
3960
|
'eol': False,
|