Skip to content

Commit c95cf87

Browse files
committed
feat(zypper.py): add zypper.py by translate to python zypper.sh
Signed-off-by: Wabri <[email protected]>
1 parent d96f938 commit c95cf87

File tree

4 files changed

+298
-1
lines changed

4 files changed

+298
-1
lines changed

testlp.txt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
Repository | Name | Category | Severity | Interactive | Status | Summary
2+
-------------------------------------------------------------+-----------------------------+-------------+-----------+-------------+--------+-------------------------------------------------
3+
Update repository of openSUSE Backports | openSUSE-2024-254 | security | important | --- | needed | Security update for chromium, gn, rust-bindgen
4+
Main Update Repository | openSUSE-2024-256 | recommended | moderate | --- | needed | Recommended update for yast2-theme
5+
Update repository of openSUSE Backports | openSUSE-2024-258 | security | important | --- | needed | Security update for chromium
6+
Update repository of openSUSE Backports | openSUSE-2024-267 | security | important | --- | needed | Security update for chromium
7+
Update repository of openSUSE Backports | openSUSE-2024-278 | security | important | --- | needed | Security update for chromium
8+
Update repository of openSUSE Backports | openSUSE-2024-287 | recommended | moderate | reboot | needed | Recommended update for cockpit, cockpit-machines
9+
Update repository of openSUSE Backports | openSUSE-2024-302 | security | important | --- | needed | Security update for chromium
10+
Update repository of openSUSE Backports | openSUSE-2024-311 | security | important | --- | needed | Security update for chromium
11+
Update repository of openSUSE Backports | openSUSE-2024-314 | security | important | --- | needed | Security update for chromium
12+
Update repository of openSUSE Backports | openSUSE-2024-327 | security | important | --- | needed | Security update for chromium
13+
Update repository of openSUSE Backports | openSUSE-2024-335 | security | important | --- | needed | Security update for chromium
14+
Update repository of openSUSE Backports | openSUSE-2024-337 | security | no | --- | needed | Security update for chromium

testlu.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
S | Repository | Name | Current Version | Available Version | Arch
2+
--+---------------------------------------------------------------------------------------+----------------------------------+---------------------------------------+-----------------------------------------+-------
3+
v | Update repository with updates from SUSE Linux Enterprise 15 | bash | 4.4-150400.25.22 | 4.4-150400.27.3.2 | x86_64
4+
v | Update repository with updates from SUSE Linux Enterprise 15 | bash-doc | 4.4-150400.25.22 | 4.4-150400.27.3.2 | noarch
5+
v | Update repository with updates from SUSE Linux Enterprise 15 | bash-lang | 4.4-150400.25.22 | 4.4-150400.27.3.2 | noarch
6+
v | Update repository with updates from SUSE Linux Enterprise 15 | bash-sh | 4.4-150400.25.22 | 4.4-150400.27.3.2 | x86_64
7+
v | Update repository with updates from SUSE Linux Enterprise 15 | binutils | 2.41-150100.7.46.1 | 2.43-150100.7.49.1 | x86_64
8+
v | Update repository with updates from SUSE Linux Enterprise 15 | bubblewrap | 0.8.0-150500.3.3.1 | 0.8.0-150500.3.6.1 | x86_64

zypper.py

Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
#!/usr/bin/env python3
2+
3+
"""
4+
Description: #TODO: short version
5+
6+
#TODO: long version
7+
8+
#TODO: example
9+
10+
#TODO: code of the example
11+
12+
#TODO: additional information
13+
14+
Dependencies: #TODO: add if needed
15+
16+
Authors: Gabriele Puliti <[email protected]>
17+
Bernd Shubert <[email protected]>
18+
"""
19+
20+
import argparse
21+
import subprocess
22+
import os
23+
24+
from collections.abc import Sequence
25+
26+
def __print_package_info(package, fields, prefix, filters=None):
27+
filters = filters or {}
28+
check = all(package.get(k) == v for k, v in filters.items())
29+
30+
if check:
31+
field_str = ",".join([f'{name}="{package[field]}"' for field, name in fields])
32+
print(f"{prefix}{{{field_str}}} 1")
33+
34+
def __print_pending_data(data, all_info, fields_more, fields_less, prefix, filters=None):
35+
if all_info:
36+
fields = fields_more
37+
else:
38+
fields = fields_less
39+
40+
if len(data) == 0:
41+
field_str = ",".join([f'{name}=""' for _, name in fields])
42+
print(f"{prefix}{{{field_str}}} 0")
43+
else:
44+
for package in data:
45+
__print_package_info(package, fields, prefix, filters)
46+
47+
def print_pending_updates(data, all_info, filters=None):
48+
fields_more = [("Repository", "repository"), ("Name", "package-name"),
49+
("Available Version", "available-version")]
50+
fields_less = [("Repository", "repository"), ("Name", "package-name")]
51+
prefix = "zypper_update_pending"
52+
53+
__print_pending_data(data, all_info, fields_more, fields_less, prefix, filters)
54+
55+
def print_pending_patches(data, all_info, filters=None):
56+
fields_more = [
57+
("Repository", "repository"), ("Name", "patch-name"), ("Category", "category"),
58+
("Severity", "severity"), ("Interactive", "interactive"), ("Status", "status")
59+
]
60+
fields_less = [
61+
("Repository", "repository"), ("Name", "patch-name"),
62+
("Interactive", "interactive"), ("Status", "status")
63+
]
64+
prefix = "zypper_patch_pending"
65+
66+
__print_pending_data(data, all_info, fields_more, fields_less, prefix, filters)
67+
68+
def print_orphaned_packages(data):
69+
fields = [
70+
("Package", "package"), ("Installed Version", "installed-version")
71+
]
72+
prefix = "zypper_package_orphan"
73+
74+
__print_pending_data(data, True, fields, None, prefix, None)
75+
76+
def __print_data_sum(data, prefix, filters=None):
77+
filters = filters or {}
78+
if len(data) == 0:
79+
print(prefix + "{total} 0")
80+
else:
81+
gauge = 0
82+
for package in data:
83+
check = all(package.get(k) == v for k, v in filters.items())
84+
if check:
85+
gauge += 1
86+
print(prefix + "{total} " + str(gauge))
87+
88+
def print_updates_sum(data, filters=None):
89+
prefix = "zypper_updates_pending_total"
90+
91+
__print_data_sum(data, prefix, filters)
92+
93+
def print_patches_sum(data, prefix="zypper_patches_pending_total", filters=None):
94+
__print_data_sum(data, prefix, filters)
95+
96+
def print_reboot_required():
97+
needs_restarting_path = '/usr/bin/needs-restarting'
98+
is_path_ok = os.path.isfile(needs_restarting_path) and os.access(needs_restarting_path, os.X_OK)
99+
100+
if is_path_ok:
101+
result = subprocess.run(
102+
[needs_restarting_path, '-r'],
103+
stdout=subprocess.DEVNULL,
104+
stderr=subprocess.DEVNULL,
105+
check=False)
106+
107+
print('# HELP node_reboot_required Node require reboot to activate installed updates or patches. (0 = not needed, 1 = needed)')
108+
print('# TYPE node_reboot_required gauge')
109+
if result.returncode == 0:
110+
print('node_reboot_required 0')
111+
else:
112+
print('node_reboot_required 1')
113+
114+
def print_zypper_version():
115+
result = subprocess.run(
116+
['/usr/bin/zypper', '-V'],
117+
stdout=subprocess.PIPE,
118+
check=False).stdout.decode('utf-8')
119+
120+
print("zypper_version " + result.split()[1])
121+
122+
123+
def __extract_lu_data(raw: str):
124+
raw_lines = raw.splitlines()[2:]
125+
extracted_data = []
126+
127+
for line in raw_lines:
128+
parts = [part.strip() for part in line.split('|')]
129+
if len(parts) >= 5:
130+
extracted_data.append({
131+
"Repository": parts[1],
132+
"Name": parts[2],
133+
"Current Version": parts[3],
134+
"Available Version": parts[4],
135+
"Arch": parts[5]
136+
})
137+
138+
return extracted_data
139+
140+
def __extract_lp_data(raw: str):
141+
raw_lines = raw.splitlines()[2:]
142+
extracted_data = []
143+
144+
for line in raw_lines:
145+
parts = [part.strip() for part in line.split('|')]
146+
if len(parts) >= 5:
147+
extracted_data.append({
148+
"Repository": parts[0],
149+
"Name": parts[1],
150+
"Category": parts[2],
151+
"Severity": parts[3],
152+
"Interactive": parts[4],
153+
"Status": parts[5]
154+
})
155+
156+
return extracted_data
157+
158+
def __extract_orphaned_data(raw: str):
159+
raw_lines = raw.splitlines()[2:]
160+
extracted_data = []
161+
162+
for line in raw_lines:
163+
parts = [part.strip() for part in line.split('|')]
164+
if len(parts) >= 5:
165+
extracted_data.append({
166+
"Package": parts[3],
167+
"Installed Version": parts[5]
168+
})
169+
170+
return extracted_data
171+
172+
def __parse_arguments(argv):
173+
parser = argparse.ArgumentParser()
174+
parser.add_mutually_exclusive_group(required=False)
175+
parser.add_argument(
176+
"-m",
177+
"--more",
178+
dest="all_info",
179+
action='store_true',
180+
help="Print all the package infos",
181+
)
182+
parser.add_argument(
183+
"-l",
184+
"--less",
185+
dest="all_info",
186+
action='store_false',
187+
help="Print less package infos",
188+
)
189+
parser.set_defaults(all_info=True)
190+
return parser.parse_args(argv)
191+
192+
def main(argv: Sequence[str] | None = None) -> int:
193+
args = __parse_arguments(argv)
194+
195+
raw_zypper_lu = subprocess.run(
196+
['cat', 'testlu.txt'],
197+
#['/usr/bin/zypper', '--quiet', 'lu'],
198+
stdout=subprocess.PIPE,
199+
check=False
200+
).stdout.decode('utf-8')
201+
data_zypper_lu = __extract_lu_data(raw_zypper_lu)
202+
203+
raw_zypper_lp = subprocess.run(
204+
['cat', 'testlp.txt'],
205+
#['/usr/bin/zypper', '--quiet', 'lp'],
206+
stdout=subprocess.PIPE,
207+
check=False
208+
).stdout.decode('utf-8')
209+
data_zypper_lp = __extract_lp_data(raw_zypper_lp)
210+
211+
raw_zypper_lp = subprocess.run(
212+
['cat', 'testlp.txt'],
213+
#['/usr/bin/zypper', '--quiet', 'lp'],
214+
stdout=subprocess.PIPE,
215+
check=False
216+
).stdout.decode('utf-8')
217+
data_zypper_lp = __extract_lp_data(raw_zypper_lp)
218+
219+
raw_zypper_orphaned = subprocess.run(
220+
['cat', 'testlp.txt'],
221+
#['/usr/bin/zypper', '--quiet', 'pa', --orphaned'],
222+
stdout=subprocess.PIPE,
223+
check=False
224+
).stdout.decode('utf-8')
225+
data_zypper_orphaned = __extract_orphaned_data(raw_zypper_orphaned)
226+
227+
print('# HELP zypper_update_pending zypper package update available from repository. (0 = not available, 1 = available)')
228+
print('# TYPE zypper_update_pending gauge')
229+
print_pending_updates(data_zypper_lu, args.all_info)
230+
231+
print('# HELP zypper_updates_pending_total zypper packages updates available in total')
232+
print('# TYPE zypper_updates_pending_total counter')
233+
print_updates_sum(data_zypper_lu)
234+
235+
print('# HELP zypper_patch_pending zypper patch available from repository. (0 = not available, 1 = available)')
236+
print('# TYPE zypper_patch_pending gauge')
237+
print_pending_patches(data_zypper_lp, args.all_info)
238+
239+
print('# HELP zypper_patches_pending_total zypper patches available total')
240+
print('# TYPE zypper_patches_pending_total counter')
241+
print_patches_sum(data_zypper_lp)
242+
243+
print('# HELP zypper_patches_pending_security_total zypper patches available with category security total')
244+
print('# TYPE zypper_patches_pending_security_total counter')
245+
print_patches_sum(data_zypper_lp,
246+
prefix="zypper_patches_pending_security_total",
247+
filters={'Category':'security'})
248+
249+
print('# HELP zypper_patches_pending_security_important_total zypper patches available with category security severity important total')
250+
print('# TYPE zypper_patches_pending_security_important_total counter')
251+
print_patches_sum(data_zypper_lp,
252+
prefix="zypper_patches_pending_security_important_total",
253+
filters={'Category':'security', 'Severity': 'important'})
254+
255+
print('# HELP zypper_patches_pending_reboot_total zypper patches available which require reboot total')
256+
print('# TYPE zypper_patches_pending_reboot_total counter')
257+
print_patches_sum(data_zypper_lp,
258+
prefix="zypper_patches_pending_reboot_total",
259+
filters={'Interactive': 'reboot'})
260+
261+
print_reboot_required()
262+
263+
print('# HELP zypper_version zypper installed package version')
264+
print('# TYPE zypper_version gauges')
265+
print_zypper_version()
266+
267+
print('# HELP zypper_package_orphan zypper packages with no update source (orphaned)')
268+
print('# TYPE zypper_package_orphan gauges')
269+
print_orphaned_packages(data_zypper_orphaned)
270+
271+
return 0
272+
273+
274+
if __name__ == "__main__":
275+
raise SystemExit(main())

zypper.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ get_updates_sum() {
128128

129129
get_pending_patches() {
130130
if [ -z "$1" ]; then
131-
echo 'zypper_patch_pending{repository="",patch-name="",category="",severity="",interactive="",status""} 0'
131+
echo 'zypper_patch_pending{repository="",patch-name="",category="",severity="",interactive="",status=""} 0'
132132
else
133133
echo "$1" |
134134
awk -v output_format=$2 "$filter_pending_patches"

0 commit comments

Comments
 (0)