Skip to content

Commit 6e26983

Browse files
authored
Add distro detection and enhanced error handling (#3)
1 parent b29ef6f commit 6e26983

File tree

7 files changed

+552
-17
lines changed

7 files changed

+552
-17
lines changed

.github/workflows/test.yml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: Tests
2+
3+
on:
4+
push:
5+
branches: [ master, main ]
6+
pull_request:
7+
branches: [ master, main ]
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-latest
12+
strategy:
13+
matrix:
14+
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
15+
fail-fast: false
16+
17+
steps:
18+
- name: Checkout code
19+
uses: actions/checkout@v4
20+
21+
- name: Set up Python ${{ matrix.python-version }}
22+
uses: actions/setup-python@v5
23+
with:
24+
python-version: ${{ matrix.python-version }}
25+
26+
- name: Install uv
27+
uses: astral-sh/setup-uv@v3
28+
with:
29+
enable-cache: true
30+
31+
- name: Set up project
32+
run: uv sync --dev
33+
34+
- name: Run tests
35+
run: uv run pytest test_kc_compat.py -v

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
__pycache__

README.md

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# KernelChecker
22

3-
_kernelcheker.py_
3+
## kernelcheker.py
44
KernelChecker was created to allow control panels to easily detect & advise
55
users on updating running kernel. It can be used to promote KernelCare
66
through the control panel. One of the goals behind creating this script was to to make it easier for control
@@ -67,25 +67,39 @@ else if needs_update == False {
6767
```
6868

6969

70-
_kc-compat.py_
70+
## kc-compat.py
7171
Checks if server is running kernel compatible with KernelCare.
7272
Usage:
7373
```bash
7474
python kc-compat.py [--silent|-q]
7575
```
7676

77-
Outputs COMPATIBLE if kernel supported, UNSUPPORTED and UNSUPPORTED; INSIDE CONTAINER
78-
if --silent flag is provided -- doesn't print anything
79-
Produces exit code 0 if compatible; 1 unsupported; 2 unsupported, inside container
77+
Outputs:
78+
- `COMPATIBLE` if kernel supported
79+
- `NEEDS REVIEW` manual validation is required
80+
- `UNSUPPORTED; INSIDE CONTAINER` if running inside a container
81+
- `CONNECTION ERROR; HTTP <code>` or `CONNECTION ERROR; <reason>` for network issues
82+
- `SYSTEM ERROR; <error>` for file system issues
83+
- `UNEXPECTED ERROR; <error>` for other errors
84+
85+
If --silent flag is provided -- doesn't print anything
86+
87+
Exit codes:
88+
- 0: compatible
89+
- 1: needs review
90+
- 2: unsupported, inside container
91+
- 3: connection error
92+
- 4: system error
93+
- 5: unexpected error
8094

8195
Alternatively you can use:
8296
```bash
83-
curl -s https://raw.githubusercontent.com/iseletsk/kernelchecker/master/py/kc-compat.py|python
97+
curl -s https://raw.githubusercontent.com/cloudlinux/kcare-scripts/master/kc-compat.py | python
8498
```
8599

86100
or
87101
```bash
88-
wget -qq -O - https://raw.githubusercontent.com/iseletsk/kernelchecker/master/py/kc-compat.py|python
102+
wget -qq -O - https://raw.githubusercontent.com/cloudlinux/kcare-scripts/master/kc-compat.py | python
89103
```
90104

91105
_Note: You cannot use exit code in this case, only output_

kc-compat.py

Lines changed: 83 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55

66
try:
77
from urllib.request import urlopen
8+
from urllib.error import HTTPError, URLError
89
except ImportError:
9-
from urllib2 import urlopen
10+
from urllib2 import urlopen, HTTPError, URLError
1011

1112
__author__ = 'Igor Seletskiy'
1213
__copyright__ = "Copyright (c) Cloud Linux GmbH & Cloud Linux Software, Inc"
@@ -18,6 +19,21 @@
1819
__version__ = '1.0'
1920

2021

22+
SUPPORTED_DISTROS = {
23+
"almalinux",
24+
"amzn",
25+
"centos",
26+
"cloudlinux",
27+
"debian",
28+
"ol",
29+
"raspbian",
30+
"rhel",
31+
"rocky",
32+
"ubuntu",
33+
"proxmox",
34+
}
35+
36+
2137
def get_kernel_hash():
2238
try:
2339
# noinspection PyCompatibility
@@ -43,13 +59,48 @@ def inside_lxc_container():
4359
return '/lxc/' in open('/proc/1/cgroup').read()
4460

4561

62+
def get_distro_info():
63+
"""
64+
Get current distribution name and version
65+
:return: distro name or None if detection fails
66+
"""
67+
68+
def parse_value(line):
69+
return line.split('=', 1)[1].strip().strip('"\'')
70+
71+
os_release_path = '/etc/os-release'
72+
if not os.path.exists(os_release_path):
73+
return None
74+
75+
try:
76+
with open(os_release_path, 'r') as f:
77+
for line in f:
78+
line = line.strip()
79+
if line.startswith('ID='):
80+
return parse_value(line)
81+
except (IOError, OSError):
82+
return None
83+
84+
85+
def is_distro_supported(distro_name):
86+
"""
87+
Check if the given distro name is supported
88+
"""
89+
return distro_name in SUPPORTED_DISTROS
90+
91+
4692
def is_compat():
4793
url = 'http://patches.kernelcare.com/' + get_kernel_hash() + '/version'
4894
try:
4995
urlopen(url)
5096
return True
51-
except Exception:
52-
return False
97+
except HTTPError as e:
98+
if e.code == 404:
99+
return False
100+
else:
101+
raise
102+
except URLError:
103+
raise
53104

54105

55106
def myprint(silent, message):
@@ -60,19 +111,41 @@ def myprint(silent, message):
60111
def main():
61112
"""
62113
if --silent or -q argument provided, don't print anything, just use exit code
63-
otherwise print results (COMPATIBLE or UNSUPPORTED)
114+
otherwise print results (COMPATIBLE or support contact messages)
64115
else exit with 0 if COMPATIBLE, 1 or more otherwise
65116
"""
66117
silent = len(sys.argv) > 1 and (sys.argv[1] == '--silent' or sys.argv[1] == '-q')
67118
if inside_vz_container() or inside_lxc_container():
68119
myprint(silent, "UNSUPPORTED; INSIDE CONTAINER")
69120
return 2
70-
if is_compat():
71-
myprint(silent, "COMPATIBLE")
72-
return 0
73-
else:
74-
myprint(silent, "UNSUPPORTED")
75-
return 1
121+
122+
try:
123+
if is_compat():
124+
myprint(silent, "COMPATIBLE")
125+
return 0
126+
else:
127+
# Handle 404 case - check if distro is supported
128+
distro_name = get_distro_info()
129+
if distro_name and is_distro_supported(distro_name):
130+
myprint(silent, "NEEDS REVIEW")
131+
myprint(silent, "We support your distribution, but we're having trouble detecting your precise kernel configuration. Please, contact CloudLinux Inc. support by email at [email protected] or by request form at https://www.cloudlinux.com/index.php/support")
132+
return 1
133+
else:
134+
myprint(silent, "NEEDS REVIEW")
135+
myprint(silent, "Please contact CloudLinux Inc. support by email at [email protected] or by request form at https://www.cloudlinux.com/index.php/support")
136+
return 1
137+
except HTTPError as e:
138+
myprint(silent, "CONNECTION ERROR; HTTP %d" % e.code)
139+
return 3
140+
except URLError as e:
141+
myprint(silent, "CONNECTION ERROR; %s" % str(e.reason))
142+
return 3
143+
except (IOError, OSError) as e:
144+
myprint(silent, "SYSTEM ERROR; %s" % str(e))
145+
return 4
146+
except Exception as e:
147+
myprint(silent, "UNEXPECTED ERROR; %s" % str(e))
148+
return 5
76149

77150

78151
if __name__ == "__main__":

pyproject.toml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
[project]
2+
name = "kcare-scripts"
3+
version = "1.0"
4+
description = "KernelCare compatibility checking and kernel analysis scripts"
5+
readme = "README.md"
6+
requires-python = ">=3.9"
7+
dependencies = []
8+
authors = [
9+
{name = "Igor Seletskiy", email = "[email protected]"}
10+
]
11+
maintainers = [
12+
{name = "Igor Seletskiy", email = "[email protected]"},
13+
{name = "Rinat Sabitov", email = "[email protected]"}
14+
]
15+
license = {text = "Apache-2.0"}
16+
keywords = ["kernel", "kernelcare", "compatibility", "linux", "security"]
17+
classifiers = [
18+
"Development Status :: 5 - Production/Stable",
19+
"Intended Audience :: System Administrators",
20+
"License :: OSI Approved :: Apache Software License",
21+
"Operating System :: POSIX :: Linux",
22+
"Programming Language :: Python :: 3",
23+
"Topic :: System :: Operating System Kernels :: Linux",
24+
"Topic :: System :: Systems Administration",
25+
]
26+
27+
[dependency-groups]
28+
dev = [
29+
"pytest>=8.4.1",
30+
]

0 commit comments

Comments
 (0)