linuxfabrik-lib 4.0.0__tar.gz → 4.0.2__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 (49) hide show
  1. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/PKG-INFO +1 -1
  2. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/db_mysql.py +48 -2
  3. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/endoflifedate.py +8 -8
  4. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/linuxfabrik_lib.egg-info/PKG-INFO +1 -1
  5. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/pyproject.toml +1 -1
  6. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/url.py +21 -8
  7. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/LICENSE +0 -0
  8. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/README.md +0 -0
  9. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/__init__.py +0 -0
  10. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/args.py +0 -0
  11. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/base.py +0 -0
  12. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/cache.py +0 -0
  13. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/db_sqlite.py +0 -0
  14. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/disk.py +0 -0
  15. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/distro.py +0 -0
  16. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/dmidecode.py +0 -0
  17. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/feedparser.py +0 -0
  18. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/globals.py +0 -0
  19. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/grassfish.py +0 -0
  20. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/huawei.py +0 -0
  21. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/human.py +0 -0
  22. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/icinga.py +0 -0
  23. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/infomaniak.py +0 -0
  24. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/jitsi.py +0 -0
  25. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/keycloak.py +0 -0
  26. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/lftest.py +0 -0
  27. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/librenms.py +0 -0
  28. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/linuxfabrik_lib.egg-info/SOURCES.txt +0 -0
  29. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/linuxfabrik_lib.egg-info/dependency_links.txt +0 -0
  30. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/linuxfabrik_lib.egg-info/requires.txt +0 -0
  31. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/linuxfabrik_lib.egg-info/top_level.txt +0 -0
  32. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/net.py +0 -0
  33. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/nextcloud.py +0 -0
  34. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/nodebb.py +0 -0
  35. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/powershell.py +0 -0
  36. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/psutil.py +0 -0
  37. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/qts.py +0 -0
  38. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/redfish.py +0 -0
  39. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/rocket.py +0 -0
  40. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/setup.cfg +0 -0
  41. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/shell.py +0 -0
  42. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/smb.py +0 -0
  43. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/time.py +0 -0
  44. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/txt.py +0 -0
  45. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/uptimerobot.py +0 -0
  46. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/veeam.py +0 -0
  47. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/version.py +0 -0
  48. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/wildfly.py +0 -0
  49. {linuxfabrik_lib-4.0.0 → linuxfabrik_lib-4.0.2}/winrm.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: linuxfabrik-lib
3
- Version: 4.0.0
3
+ Version: 4.0.2
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.
@@ -15,7 +15,7 @@ import warnings
15
15
  warnings.filterwarnings('ignore', category=UserWarning, module='pymysql')
16
16
 
17
17
  __author__ = 'Linuxfabrik GmbH, Zurich/Switzerland'
18
- __version__ = '2026051101'
18
+ __version__ = '2026051801'
19
19
 
20
20
  import re
21
21
  import sys
@@ -238,6 +238,14 @@ def connect(mysql_connection, **kwargs):
238
238
  - If connection fails, the error message contains the reason for failure.
239
239
  - `pymysql`'s `read_default_file` and `read_default_group` allow connection settings from a
240
240
  `.cnf` file.
241
+ - After the connection is up, the function aligns the session's character set and collation
242
+ with the `mysql` system schema (`SET NAMES ... COLLATE ...`). This stops queries against
243
+ system tables like `mysql.user` and `mysql.global_priv` from aborting with ER 1267
244
+ ("Illegal mix of collations") when the server's connection-collation default differs from
245
+ the column collations. On MariaDB 10.4+, `mysql.user` is a view over `mysql.global_priv`
246
+ whose JSON-derived columns return COERCIBLE results, so a plain `col = 'literal'` compare
247
+ breaks the moment `collation_connection` and the column collation differ. The alignment
248
+ is best-effort: on any error the connection stays usable with the server's defaults.
241
249
 
242
250
  ### Example
243
251
  >>> mysql_connection = {
@@ -260,10 +268,48 @@ def connect(mysql_connection, **kwargs):
260
268
  connect_timeout=mysql_connection.get('timeout', 3),
261
269
  **kwargs,
262
270
  )
263
- return True, conn
264
271
  except Exception as e:
265
272
  return False, f'Connecting to DB failed: {e}'
266
273
 
274
+ _align_session_collation(conn)
275
+ return True, conn
276
+
277
+
278
+ def _align_session_collation(conn):
279
+ """Match `collation_connection` to the `mysql` system schema's default collation.
280
+
281
+ Without this, queries that compare a JSON-derived column from the `mysql.user`
282
+ view (e.g. `IS_ROLE = 'N'`, `plugin = 'mysql_native_password'`) abort with
283
+ ER 1267 ("Illegal mix of collations") when the server's connection collation
284
+ default doesn't match the schema's column collation. Looking up the schema's
285
+ own defaults from `information_schema.schemata` keeps this working across
286
+ MySQL/MariaDB versions and locale-specific installs (utf8mb3 on older
287
+ servers, utf8mb4_general_ci on most current ones, utf8mb4_uca1400_ai_ci on
288
+ MariaDB 10.10+). Best-effort: on any error the connection stays usable.
289
+ """
290
+ try:
291
+ with conn.cursor(pymysql.cursors.DictCursor) as cursor:
292
+ cursor.execute(
293
+ 'select default_character_set_name as cs, '
294
+ 'default_collation_name as coll '
295
+ 'from information_schema.schemata '
296
+ "where schema_name = 'mysql'"
297
+ )
298
+ row = cursor.fetchone()
299
+ if not row:
300
+ return
301
+ cs = (row.get('cs') or '').strip()
302
+ coll = (row.get('coll') or '').strip()
303
+ # Whitelist identifiers to keep the formatted `SET NAMES` safe from
304
+ # any caller-controlled value (the row comes from a system view, but
305
+ # defence in depth doesn't cost anything).
306
+ if not (re.match(r'^[A-Za-z0-9_]+$', cs) and re.match(r'^[A-Za-z0-9_]+$', coll)):
307
+ return
308
+ with conn.cursor() as cursor:
309
+ cursor.execute(f'set names {cs} collate {coll}')
310
+ except Exception:
311
+ pass
312
+
267
313
 
268
314
  def get_all_status(conn):
269
315
  """
@@ -1614,8 +1614,8 @@ ENDOFLIFE_DATE = {
1614
1614
  { 'cycle': '11.8',
1615
1615
  'eol': '2028-06-04',
1616
1616
  'extendedSupport': '2033-10-22',
1617
- 'latest': '11.8.6',
1618
- 'latestReleaseDate': '2026-02-04',
1617
+ 'latest': '11.8.7',
1618
+ 'latestReleaseDate': '2026-05-14',
1619
1619
  'lts': True,
1620
1620
  'releaseDate': '2025-06-04'},
1621
1621
  { 'cycle': '11.7',
@@ -1645,8 +1645,8 @@ ENDOFLIFE_DATE = {
1645
1645
  { 'cycle': '11.4',
1646
1646
  'eol': '2029-05-29',
1647
1647
  'extendedSupport': '2033-01-16',
1648
- 'latest': '11.4.10',
1649
- 'latestReleaseDate': '2026-02-04',
1648
+ 'latest': '11.4.11',
1649
+ 'latestReleaseDate': '2026-05-14',
1650
1650
  'lts': True,
1651
1651
  'releaseDate': '2024-05-29'},
1652
1652
  { 'cycle': '11.3',
@@ -1684,8 +1684,8 @@ ENDOFLIFE_DATE = {
1684
1684
  { 'cycle': '10.11',
1685
1685
  'eol': '2028-02-16',
1686
1686
  'extendedSupport': '2028-02-16',
1687
- 'latest': '10.11.16',
1688
- 'latestReleaseDate': '2026-02-04',
1687
+ 'latest': '10.11.17',
1688
+ 'latestReleaseDate': '2026-05-14',
1689
1689
  'lts': True,
1690
1690
  'releaseDate': '2023-02-16'},
1691
1691
  { 'cycle': '10.10',
@@ -1723,8 +1723,8 @@ ENDOFLIFE_DATE = {
1723
1723
  { 'cycle': '10.6',
1724
1724
  'eol': '2026-07-06',
1725
1725
  'extendedSupport': '2029-08-23',
1726
- 'latest': '10.6.25',
1727
- 'latestReleaseDate': '2026-02-04',
1726
+ 'latest': '10.6.26',
1727
+ 'latestReleaseDate': '2026-05-14',
1728
1728
  'lts': True,
1729
1729
  'releaseDate': '2021-07-06'},
1730
1730
  { 'cycle': '10.5',
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: linuxfabrik-lib
3
- Version: 4.0.0
3
+ Version: 4.0.2
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.
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
6
6
 
7
7
  [project]
8
8
  name = "linuxfabrik-lib"
9
- version = "4.0.0"
9
+ version = "4.0.2"
10
10
  description = "Python libraries used in various Linuxfabrik projects, including the 'Linuxfabrik Monitoring Plugins' project."
11
11
  readme = "README.md"
12
12
  authors = [
@@ -11,7 +11,7 @@
11
11
  """Get for example HTML or JSON from an URL."""
12
12
 
13
13
  __author__ = 'Linuxfabrik GmbH, Zurich/Switzerland'
14
- __version__ = '2026051003'
14
+ __version__ = '2026051802'
15
15
 
16
16
  import base64
17
17
  import json
@@ -35,13 +35,20 @@ except ImportError:
35
35
  from . import txt
36
36
 
37
37
 
38
- # stdlib ssl version names; '1.0' first because it is the most permissive minimum
39
- _TLS_VERSIONS = {
40
- '1.0': ssl.TLSVersion.TLSv1,
41
- '1.1': ssl.TLSVersion.TLSv1_1,
42
- '1.2': ssl.TLSVersion.TLSv1_2,
43
- '1.3': ssl.TLSVersion.TLSv1_3,
44
- }
38
+ # stdlib ssl version names; '1.0' first because it is the most permissive minimum.
39
+ # `ssl.TLSVersion` was added in Python 3.7. Build the dict only when available so
40
+ # `import lib.url` still works on older interpreters (e.g. RHEL 8's default `python3`
41
+ # = 3.6) - any plugin that doesn't actually use TLS version pinning then continues
42
+ # to work. Callers that pass `tls_min` / `tls_max` get a clearer RuntimeError in
43
+ # `_build_ssl_context()` instead of an AttributeError at import time.
44
+ _TLS_VERSIONS = {}
45
+ if hasattr(ssl, 'TLSVersion'):
46
+ _TLS_VERSIONS = {
47
+ '1.0': ssl.TLSVersion.TLSv1,
48
+ '1.1': ssl.TLSVersion.TLSv1_1,
49
+ '1.2': ssl.TLSVersion.TLSv1_2,
50
+ '1.3': ssl.TLSVersion.TLSv1_3,
51
+ }
45
52
 
46
53
 
47
54
  def _redact_url(url):
@@ -59,6 +66,12 @@ def _build_ssl_context(insecure, tls_min, tls_max):
59
66
  if insecure:
60
67
  ctx.check_hostname = False
61
68
  ctx.verify_mode = ssl.CERT_NONE
69
+ if tls_min is not None or tls_max is not None:
70
+ if not _TLS_VERSIONS:
71
+ raise RuntimeError(
72
+ 'TLS version pinning (`tls_min` / `tls_max`) requires Python 3.7+ '
73
+ '(`ssl.TLSVersion`); this interpreter is too old.'
74
+ )
62
75
  if tls_min is not None:
63
76
  if tls_min not in _TLS_VERSIONS:
64
77
  raise ValueError(
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes