linuxfabrik-lib 4.2.0__tar.gz → 4.3.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.2.0 → linuxfabrik_lib-4.3.0}/PKG-INFO +1 -1
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/db_mysql.py +9 -8
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/db_sqlite.py +4 -7
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/disk.py +224 -2
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/dmidecode.py +4 -6
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/endoflifedate.py +68 -55
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/huawei.py +3 -2
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/human.py +2 -5
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/icinga.py +2 -2
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/lftest.py +63 -12
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/linuxfabrik_lib.egg-info/PKG-INFO +1 -1
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/linuxfabrik_lib.egg-info/SOURCES.txt +2 -0
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/net.py +117 -4
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/pyproject.toml +6 -1
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/redfish.py +10 -5
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/rocket.py +7 -7
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/shell.py +23 -1
- linuxfabrik_lib-4.3.0/ssh.py +248 -0
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/time.py +2 -5
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/txt.py +15 -4
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/uptimerobot.py +2 -2
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/url.py +62 -25
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/veeam.py +8 -5
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/LICENSE +0 -0
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/README.md +0 -0
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/__init__.py +0 -0
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/args.py +0 -0
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/base.py +0 -0
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/cache.py +0 -0
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/distro.py +0 -0
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/feedparser.py +0 -0
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/globals.py +0 -0
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/grassfish.py +0 -0
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/infomaniak.py +0 -0
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/jitsi.py +0 -0
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/keycloak.py +0 -0
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/librenms.py +0 -0
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/linuxfabrik_lib.egg-info/dependency_links.txt +0 -0
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/linuxfabrik_lib.egg-info/requires.txt +0 -0
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/linuxfabrik_lib.egg-info/top_level.txt +0 -0
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/nextcloud.py +0 -0
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/nodebb.py +0 -0
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/powershell.py +0 -0
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/psutil.py +0 -0
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/qts.py +0 -0
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/setup.cfg +0 -0
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/smb.py +0 -0
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/version.py +0 -0
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/wildfly.py +0 -0
- {linuxfabrik_lib-4.2.0 → linuxfabrik_lib-4.3.0}/winrm.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: linuxfabrik-lib
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.3.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.
|
|
@@ -10,19 +10,18 @@
|
|
|
10
10
|
|
|
11
11
|
"""Library for accessing MySQL/MariaDB servers."""
|
|
12
12
|
|
|
13
|
-
import warnings
|
|
14
|
-
|
|
15
|
-
warnings.filterwarnings('ignore', category=UserWarning, module='pymysql')
|
|
16
|
-
|
|
17
|
-
__author__ = 'Linuxfabrik GmbH, Zurich/Switzerland'
|
|
18
|
-
__version__ = '2026051803'
|
|
19
|
-
|
|
20
13
|
import re
|
|
21
14
|
import sys
|
|
15
|
+
import warnings
|
|
22
16
|
|
|
23
17
|
from . import base
|
|
24
18
|
from .globals import STATE_UNKNOWN
|
|
25
19
|
|
|
20
|
+
warnings.filterwarnings('ignore', category=UserWarning, module='pymysql')
|
|
21
|
+
|
|
22
|
+
__author__ = 'Linuxfabrik GmbH, Zurich/Switzerland'
|
|
23
|
+
__version__ = '2026060201'
|
|
24
|
+
|
|
26
25
|
try:
|
|
27
26
|
import pymysql.cursors
|
|
28
27
|
except ImportError:
|
|
@@ -538,7 +537,9 @@ def get_server_info(banner=None):
|
|
|
538
537
|
return {'flavor': flavor, 'version': version}
|
|
539
538
|
return None
|
|
540
539
|
|
|
541
|
-
|
|
540
|
+
# local import to keep db_mysql usable without shell at module load
|
|
541
|
+
from . import shell
|
|
542
|
+
|
|
542
543
|
for command in (
|
|
543
544
|
'mysqld --version',
|
|
544
545
|
'mariadbd --version',
|
|
@@ -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__ = '2026060201'
|
|
32
32
|
|
|
33
33
|
import csv
|
|
34
34
|
import hashlib
|
|
@@ -557,10 +557,7 @@ def delete(conn, sql, data=None, delete_db_on_operational_error=True):
|
|
|
557
557
|
|
|
558
558
|
c = conn.cursor()
|
|
559
559
|
try:
|
|
560
|
-
if data
|
|
561
|
-
rowcount = c.execute(sql, data).rowcount
|
|
562
|
-
else:
|
|
563
|
-
rowcount = c.execute(sql).rowcount
|
|
560
|
+
rowcount = c.execute(sql, data).rowcount if data else c.execute(sql).rowcount
|
|
564
561
|
return True, rowcount
|
|
565
562
|
except sqlite3.OperationalError as e:
|
|
566
563
|
if delete_db_on_operational_error:
|
|
@@ -938,7 +935,7 @@ def insert(conn, data, table='perfdata', delete_db_on_operational_error=True):
|
|
|
938
935
|
# table is sanitized via __filter_str() above; keys come from the caller's
|
|
939
936
|
# data dict (column names, not values); VALUES use parameterized binds
|
|
940
937
|
keys = ','.join(data.keys())
|
|
941
|
-
binds = ','.join(f':{key}' for key in data
|
|
938
|
+
binds = ','.join(f':{key}' for key in data)
|
|
942
939
|
sql = f'INSERT INTO "{table}" ({keys}) VALUES ({binds});' # nosec B608
|
|
943
940
|
|
|
944
941
|
c = conn.cursor()
|
|
@@ -1161,7 +1158,7 @@ def replace(conn, data, table='perfdata', delete_db_on_operational_error=True):
|
|
|
1161
1158
|
table = __filter_str(table)
|
|
1162
1159
|
|
|
1163
1160
|
keys = ','.join(data.keys())
|
|
1164
|
-
binds = ','.join(f':{key}' for key in data
|
|
1161
|
+
binds = ','.join(f':{key}' for key in data)
|
|
1165
1162
|
sql = f'REPLACE INTO "{table}" ({keys}) VALUES ({binds});'
|
|
1166
1163
|
|
|
1167
1164
|
c = conn.cursor()
|
|
@@ -13,11 +13,12 @@ partitions, grepping a file, etc.
|
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
15
|
__author__ = 'Linuxfabrik GmbH, Zurich/Switzerland'
|
|
16
|
-
__version__ = '
|
|
16
|
+
__version__ = '2026060601'
|
|
17
17
|
|
|
18
18
|
import csv
|
|
19
19
|
import os
|
|
20
20
|
import re
|
|
21
|
+
import shutil
|
|
21
22
|
import tempfile
|
|
22
23
|
|
|
23
24
|
from . import shell
|
|
@@ -55,6 +56,65 @@ def bd2dmd(device):
|
|
|
55
56
|
return mapped_device if os.path.islink(mapped_device) else ''
|
|
56
57
|
|
|
57
58
|
|
|
59
|
+
def copy_dir(src, dst):
|
|
60
|
+
"""
|
|
61
|
+
Recursively copy a directory tree.
|
|
62
|
+
|
|
63
|
+
Wraps `shutil.copytree()` and reports the outcome in the same
|
|
64
|
+
`(success, error)` style as the other disk helpers, so callers do not have to
|
|
65
|
+
handle exceptions themselves.
|
|
66
|
+
|
|
67
|
+
### Parameters
|
|
68
|
+
- **src** (`str`): Source directory.
|
|
69
|
+
- **dst** (`str`): Destination directory (must not exist yet).
|
|
70
|
+
|
|
71
|
+
### Returns
|
|
72
|
+
- **tuple**:
|
|
73
|
+
- tuple[0] (**bool**): True if the copy succeeded, otherwise False.
|
|
74
|
+
- tuple[1] (**None or str**): None on success, otherwise an error message.
|
|
75
|
+
|
|
76
|
+
### Example
|
|
77
|
+
>>> copy_dir('/usr/share/lynis', '/tmp/lynis')
|
|
78
|
+
(True, None)
|
|
79
|
+
"""
|
|
80
|
+
try:
|
|
81
|
+
shutil.copytree(src, dst)
|
|
82
|
+
return True, None
|
|
83
|
+
except (OSError, shutil.Error) as e:
|
|
84
|
+
return False, f'Error copying directory {src} to {dst}: {e}'
|
|
85
|
+
except Exception as e:
|
|
86
|
+
return False, f'Unknown error copying directory {src} to {dst}: {e}'
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def copy_file(src, dst):
|
|
90
|
+
"""
|
|
91
|
+
Copy a single file, preserving its metadata.
|
|
92
|
+
|
|
93
|
+
Wraps `shutil.copy2()` and reports the outcome in the same `(success, error)`
|
|
94
|
+
style as the other disk helpers.
|
|
95
|
+
|
|
96
|
+
### Parameters
|
|
97
|
+
- **src** (`str`): Source file.
|
|
98
|
+
- **dst** (`str`): Destination file or directory.
|
|
99
|
+
|
|
100
|
+
### Returns
|
|
101
|
+
- **tuple**:
|
|
102
|
+
- tuple[0] (**bool**): True if the copy succeeded, otherwise False.
|
|
103
|
+
- tuple[1] (**None or str**): None on success, otherwise an error message.
|
|
104
|
+
|
|
105
|
+
### Example
|
|
106
|
+
>>> copy_file('/etc/lynis/default.prf', '/tmp/lynis/default.prf')
|
|
107
|
+
(True, None)
|
|
108
|
+
"""
|
|
109
|
+
try:
|
|
110
|
+
shutil.copy2(src, dst)
|
|
111
|
+
return True, None
|
|
112
|
+
except OSError as e:
|
|
113
|
+
return False, f'OS error "{e.strerror}" while copying {src} to {dst}'
|
|
114
|
+
except Exception as e:
|
|
115
|
+
return False, f'Unknown error copying {src} to {dst}: {e}'
|
|
116
|
+
|
|
117
|
+
|
|
58
118
|
def dir_exists(path):
|
|
59
119
|
"""
|
|
60
120
|
Check if a directory exists at the given path.
|
|
@@ -120,6 +180,79 @@ def file_exists(path, allow_empty=False):
|
|
|
120
180
|
return os.path.getsize(path) > 0
|
|
121
181
|
|
|
122
182
|
|
|
183
|
+
# Block-device name prefixes that never carry meaningful I/O for monitoring
|
|
184
|
+
# (loopback, RAM disks, compressed RAM, floppy and optical devices). They are
|
|
185
|
+
# skipped by get_block_devices().
|
|
186
|
+
_PSEUDO_DEVICE_PREFIXES = ('fd', 'loop', 'ram', 'sr', 'zram')
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def get_block_devices():
|
|
190
|
+
"""
|
|
191
|
+
Return all local block devices that expose I/O counters, mounted or not.
|
|
192
|
+
|
|
193
|
+
Unlike `get_real_disks()`, which is limited to block devices that currently have a mounted
|
|
194
|
+
filesystem, this also includes devices without a mounted filesystem, for example raw devices
|
|
195
|
+
backing a database or storage layer, or unmounted multipath/SAN volumes. Devices are
|
|
196
|
+
enumerated from `/proc/diskstats`, so their names line up with the per-device I/O counters
|
|
197
|
+
exposed there.
|
|
198
|
+
|
|
199
|
+
Each device is represented as a dictionary with:
|
|
200
|
+
- 'bd' : Block device path (e.g. '/dev/sda' or '/dev/dm-7').
|
|
201
|
+
- 'dmd': Device-mapper path if the device is a device-mapper target (e.g.
|
|
202
|
+
'/dev/mapper/data'), otherwise an empty string.
|
|
203
|
+
- 'mp' : Mount point(s), space-separated if mounted in several places, or an empty string if
|
|
204
|
+
the device is not mounted.
|
|
205
|
+
|
|
206
|
+
Pseudo devices that never carry meaningful I/O are skipped by name prefix: loopback (`loop`),
|
|
207
|
+
RAM disks (`ram`), compressed RAM (`zram`), floppy (`fd`) and optical (`sr`) devices.
|
|
208
|
+
|
|
209
|
+
### Parameters
|
|
210
|
+
- None
|
|
211
|
+
|
|
212
|
+
### Returns
|
|
213
|
+
- **list of dict**: One entry per block device, including unmounted ones. Empty list on
|
|
214
|
+
systems without `/proc/diskstats` (e.g. non-Linux).
|
|
215
|
+
|
|
216
|
+
### Example
|
|
217
|
+
>>> get_block_devices()
|
|
218
|
+
[{'bd': '/dev/dm-7', 'dmd': '/dev/mapper/data', 'mp': ''},
|
|
219
|
+
{'bd': '/dev/sda1', 'dmd': '', 'mp': '/boot'}]
|
|
220
|
+
"""
|
|
221
|
+
success, diskstats = read_file('/proc/diskstats')
|
|
222
|
+
if not success:
|
|
223
|
+
return []
|
|
224
|
+
|
|
225
|
+
# map every mounted block-device path to its mount point(s)
|
|
226
|
+
mountpoints = {}
|
|
227
|
+
mounts_ok, mounts_content = read_file('/proc/mounts')
|
|
228
|
+
if mounts_ok:
|
|
229
|
+
for line in mounts_content.splitlines():
|
|
230
|
+
if not line.startswith('/dev/'):
|
|
231
|
+
continue
|
|
232
|
+
parts = line.split()
|
|
233
|
+
device_path, mount_point = parts[0], parts[1]
|
|
234
|
+
if device_path in mountpoints:
|
|
235
|
+
mountpoints[device_path] += f' {mount_point}'
|
|
236
|
+
else:
|
|
237
|
+
mountpoints[device_path] = mount_point
|
|
238
|
+
|
|
239
|
+
disks = []
|
|
240
|
+
for line in diskstats.splitlines():
|
|
241
|
+
parts = line.split()
|
|
242
|
+
if len(parts) < 3:
|
|
243
|
+
continue
|
|
244
|
+
name = parts[2]
|
|
245
|
+
if name.startswith(_PSEUDO_DEVICE_PREFIXES):
|
|
246
|
+
continue
|
|
247
|
+
bd = f'/dev/{name}'
|
|
248
|
+
dmd = bd2dmd(name)
|
|
249
|
+
# a device can be mounted under its block-device path or its device-mapper path
|
|
250
|
+
mp = mountpoints.get(bd, '') or (mountpoints.get(dmd, '') if dmd else '')
|
|
251
|
+
disks.append({'bd': bd, 'dmd': dmd, 'mp': mp})
|
|
252
|
+
|
|
253
|
+
return disks
|
|
254
|
+
|
|
255
|
+
|
|
123
256
|
def get_cwd():
|
|
124
257
|
"""
|
|
125
258
|
Get the current working directory.
|
|
@@ -168,7 +301,7 @@ def get_owner(file):
|
|
|
168
301
|
"""
|
|
169
302
|
try:
|
|
170
303
|
return os.stat(file).st_uid
|
|
171
|
-
except:
|
|
304
|
+
except OSError:
|
|
172
305
|
return -1
|
|
173
306
|
|
|
174
307
|
|
|
@@ -302,6 +435,67 @@ def grep_file(filename, pattern):
|
|
|
302
435
|
return True, ''
|
|
303
436
|
|
|
304
437
|
|
|
438
|
+
def make_temp_dir(prefix=''):
|
|
439
|
+
"""
|
|
440
|
+
Create a unique temporary directory and return its path.
|
|
441
|
+
|
|
442
|
+
Wraps `tempfile.mkdtemp()` and reports the outcome in the same
|
|
443
|
+
`(success, result)` style as the other disk helpers.
|
|
444
|
+
|
|
445
|
+
### Parameters
|
|
446
|
+
- **prefix** (`str`, optional): Prefix for the directory name. Defaults to ''.
|
|
447
|
+
|
|
448
|
+
### Returns
|
|
449
|
+
- **tuple**:
|
|
450
|
+
- tuple[0] (**bool**): True on success, otherwise False.
|
|
451
|
+
- tuple[1] (**str**): The created directory path on success, otherwise an
|
|
452
|
+
error message.
|
|
453
|
+
|
|
454
|
+
### Example
|
|
455
|
+
>>> make_temp_dir(prefix='myapp-')
|
|
456
|
+
(True, '/tmp/myapp-abcd1234')
|
|
457
|
+
"""
|
|
458
|
+
try:
|
|
459
|
+
return True, tempfile.mkdtemp(prefix=prefix)
|
|
460
|
+
except OSError as e:
|
|
461
|
+
return False, f'OS error "{e.strerror}" while creating a temporary directory'
|
|
462
|
+
except Exception as e:
|
|
463
|
+
return False, f'Unknown error creating a temporary directory: {e}'
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
def mkdir(path, mode=0o755, exist_ok=True):
|
|
467
|
+
"""
|
|
468
|
+
Create a directory, including any missing parent directories.
|
|
469
|
+
|
|
470
|
+
Wraps `os.makedirs()` and reports the outcome in the same `(success, error)`
|
|
471
|
+
style as the other disk helpers, so callers do not have to handle exceptions
|
|
472
|
+
themselves.
|
|
473
|
+
|
|
474
|
+
### Parameters
|
|
475
|
+
- **path** (`str`): Directory path to create.
|
|
476
|
+
- **mode** (`int`, optional): Permission bits for newly created directories.
|
|
477
|
+
Defaults to `0o755`.
|
|
478
|
+
- **exist_ok** (`bool`, optional): If `True`, an already existing directory is
|
|
479
|
+
not an error. Defaults to `True`.
|
|
480
|
+
|
|
481
|
+
### Returns
|
|
482
|
+
- **tuple**:
|
|
483
|
+
- tuple[0] (**bool**): True if the directory exists afterwards, else False.
|
|
484
|
+
- tuple[1] (**None or str**): None on success, otherwise an error message.
|
|
485
|
+
|
|
486
|
+
### Example
|
|
487
|
+
>>> mkdir('/tmp/example/sub')
|
|
488
|
+
(True, None)
|
|
489
|
+
"""
|
|
490
|
+
try:
|
|
491
|
+
os.makedirs(path, mode=mode, exist_ok=exist_ok)
|
|
492
|
+
return True, None
|
|
493
|
+
except OSError as e:
|
|
494
|
+
return False, f'OS error "{e.strerror}" while creating {path}'
|
|
495
|
+
except Exception as e:
|
|
496
|
+
return False, f'Unknown error creating {path}: {e}'
|
|
497
|
+
|
|
498
|
+
|
|
305
499
|
def read_csv(
|
|
306
500
|
filename,
|
|
307
501
|
delimiter=',',
|
|
@@ -444,6 +638,34 @@ def read_file(filename):
|
|
|
444
638
|
return False, f'Unknown error opening or reading {filename}: {e}'
|
|
445
639
|
|
|
446
640
|
|
|
641
|
+
def rm_dir(path):
|
|
642
|
+
"""
|
|
643
|
+
Recursively delete a directory tree.
|
|
644
|
+
|
|
645
|
+
Wraps `shutil.rmtree()` and reports the outcome in the same `(success, error)`
|
|
646
|
+
style as `rm_file()`.
|
|
647
|
+
|
|
648
|
+
### Parameters
|
|
649
|
+
- **path** (`str`): Directory tree to delete.
|
|
650
|
+
|
|
651
|
+
### Returns
|
|
652
|
+
- **tuple**:
|
|
653
|
+
- tuple[0] (**bool**): True if deletion succeeded, otherwise False.
|
|
654
|
+
- tuple[1] (**None or str**): None on success, otherwise an error message.
|
|
655
|
+
|
|
656
|
+
### Example
|
|
657
|
+
>>> rm_dir('/tmp/lynis')
|
|
658
|
+
(True, None)
|
|
659
|
+
"""
|
|
660
|
+
try:
|
|
661
|
+
shutil.rmtree(path)
|
|
662
|
+
return True, None
|
|
663
|
+
except OSError as e:
|
|
664
|
+
return False, f'OS error "{e.strerror}" while deleting {path}'
|
|
665
|
+
except Exception as e:
|
|
666
|
+
return False, f'Unknown error deleting {path}: {e}'
|
|
667
|
+
|
|
668
|
+
|
|
447
669
|
def rm_file(filename):
|
|
448
670
|
"""
|
|
449
671
|
Delete or remove a file.
|
|
@@ -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__ = '2026060201'
|
|
18
18
|
|
|
19
19
|
import re
|
|
20
20
|
|
|
@@ -260,9 +260,7 @@ def dmidecode_parse(output):
|
|
|
260
260
|
if 'no module installed' in size:
|
|
261
261
|
return True
|
|
262
262
|
# Sometimes vendors encode 0-sized entries
|
|
263
|
-
|
|
264
|
-
return True
|
|
265
|
-
return False
|
|
263
|
+
return bool(size.startswith('0 ') or size == '0')
|
|
266
264
|
|
|
267
265
|
# Fields to ignore by DMI type when constructing fingerprints (order-independent)
|
|
268
266
|
IGNORE_BY_TYPE = {
|
|
@@ -382,7 +380,7 @@ def dmidecode_parse(output):
|
|
|
382
380
|
seen[fp] = (dmi_handle, rep)
|
|
383
381
|
data[dmi_handle] = rep
|
|
384
382
|
else:
|
|
385
|
-
|
|
383
|
+
_, rep = seen[fp]
|
|
386
384
|
rep['dedup_count'] = int(rep.get('dedup_count', 1)) + 1
|
|
387
385
|
rep['dedup_handles'].append(dmi_handle[0])
|
|
388
386
|
# enrich socket/slot lists
|
|
@@ -517,7 +515,7 @@ def get_data():
|
|
|
517
515
|
if not success:
|
|
518
516
|
return False
|
|
519
517
|
|
|
520
|
-
stdout,
|
|
518
|
+
stdout, _, retc = result
|
|
521
519
|
if retc != 0:
|
|
522
520
|
return False
|
|
523
521
|
|