Skip to content

Commit 532a3dd

Browse files
committed
Add support for CloudLinux VM missing os-release and /etc/redhat-release
> closes #240
1 parent d9ae439 commit 532a3dd

File tree

6 files changed

+102
-6
lines changed

6 files changed

+102
-6
lines changed

src/distro/distro.py

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
Callable,
4343
Dict,
4444
Iterable,
45+
List,
4546
Optional,
4647
Sequence,
4748
TextIO,
@@ -73,6 +74,7 @@ class InfoDict(TypedDict):
7374

7475

7576
_UNIXCONFDIR = os.environ.get("UNIXCONFDIR", "/etc")
77+
_UNIXPROCDIR = os.environ.get("UNIXPROCDIR", "/proc")
7678
_UNIXUSRLIBDIR = os.environ.get("UNIXUSRLIBDIR", "/usr/lib")
7779
_OS_RELEASE_BASENAME = "os-release"
7880

@@ -783,6 +785,7 @@ def __init__(
783785
"""
784786
self.root_dir = root_dir
785787
self.etc_dir = os.path.join(root_dir, "etc") if root_dir else _UNIXCONFDIR
788+
self.proc_dir = os.path.join(root_dir, "proc") if root_dir else _UNIXPROCDIR
786789
self.usr_lib_dir = (
787790
os.path.join(root_dir, "usr/lib") if root_dir else _UNIXUSRLIBDIR
788791
)
@@ -1302,25 +1305,55 @@ def _armbian_version(self) -> str:
13021305
except FileNotFoundError:
13031306
return ""
13041307

1305-
@staticmethod
1306-
def _parse_uname_content(lines: Sequence[str]) -> Dict[str, str]:
1308+
def _parse_uname_content(self, lines: Sequence[str]) -> Dict[str, str]:
13071309
if not lines:
13081310
return {}
13091311
props = {}
1310-
match = re.search(r"^([^\s]+)\s+([\d\.]+)", lines[0].strip())
1312+
match = re.search(r"^([^\s]+)\s+([^\s]+)", lines[0].strip())
13111313
if match:
1312-
name, version = match.groups()
1314+
name, release = match.groups()
13131315

13141316
# This is to prevent the Linux kernel version from
13151317
# appearing as the 'best' version on otherwise
13161318
# identifiable distributions.
13171319
if name == "Linux":
1318-
return {}
1320+
# Attempt various OS detection based on uname release information
1321+
return self._cloudlinux_detection(release.split("."))
1322+
13191323
props["id"] = name.lower()
13201324
props["name"] = name
1321-
props["release"] = version
1325+
props["release"] = release.split("-")[0] # only keep version part
13221326
return props
13231327

1328+
def _cloudlinux_detection(self, release_parts: List[str]) -> Dict[str, str]:
1329+
if (
1330+
# check penultimate release component contains an "el*" version
1331+
len(release_parts) > 1
1332+
and release_parts[-2].startswith("el")
1333+
and (
1334+
# CloudLinux < 9 : "lve*" is set in kernel release
1335+
any(rc.startswith("lve") for rc in release_parts)
1336+
# CloudLinux >= 9 : check whether "kmodlve" is loaded
1337+
or "kmodlve" in self._kernel_modules
1338+
)
1339+
):
1340+
return {
1341+
"id": "cloudlinux",
1342+
"name": "CloudLinux",
1343+
# strip "el" prefix and replace underscores by dots
1344+
"release": release_parts[-2][2:].replace("_", "."),
1345+
}
1346+
1347+
return {}
1348+
1349+
@cached_property
1350+
def _kernel_modules(self) -> List[str]:
1351+
try:
1352+
with open(os.path.join(self.proc_dir, "modules"), encoding="ascii") as fp:
1353+
return [line.split()[0] for line in fp]
1354+
except OSError:
1355+
return []
1356+
13241357
@staticmethod
13251358
def _to_str(bytestring: bytes) -> str:
13261359
encoding = sys.getfilesystemencoding()
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/bin/sh
2+
3+
echo "Linux 3.10.0-962.3.2.lve1.5.24.9.el7.x86_64"
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/bin/sh
2+
3+
echo "Linux 4.18.0-513.18.1.lve.2.el8.x86_64"
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/bin/sh
2+
3+
echo "Linux 5.14.0-427.20.1.el9_4.x86_64"
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
autofs4 23643 2 - Live 0x0000000000000000
2+
btrfs 21980 0 - Live 0x0000000000000000
3+
cdrom 3656 2 hfsplus,hfs, Live 0x0000000000000000
4+
ext4 19980 5 - Live 0x0000000000000000
5+
fat 31261 2 msdos,vfat, Live 0x0000000000000000
6+
fuse 20211 9 - Live 0x0000000000000000
7+
hfs 28747 0 - Live 0x0000000000000000
8+
hfsplus 4168 0 - Live 0x0000000000000000
9+
kmodlve 17657856 2 - Live 0x0000000000000000
10+
msdos 7785 0 - Live 0x0000000000000000
11+
vfat 7785 1 - Live 0x0000000000000000
12+
xor 1770 1 btrfs, Live 0x0000000000000000

tests/test_distro.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
from distro import distro
3737

3838
RELATIVE_UNIXCONFDIR = distro._UNIXCONFDIR[1:]
39+
RELATIVE_UNIXPROCDIR = distro._UNIXPROCDIR[1:]
3940
RELATIVE_UNIXUSRLIBDIR = distro._UNIXUSRLIBDIR[1:]
4041
MODULE_DISTRO = distro._distro
4142

@@ -118,11 +119,13 @@ def setup_method(self, test_method: FunctionType) -> None:
118119
# changes it:
119120
self._saved_path = os.environ["PATH"]
120121
self._saved_UNIXCONFDIR = distro._UNIXCONFDIR
122+
self._saved_UNIXPROCDIR = distro._UNIXPROCDIR
121123
self._saved_UNIXUSRLIBDIR = distro._UNIXUSRLIBDIR
122124

123125
def teardown_method(self, test_method: FunctionType) -> None:
124126
os.environ["PATH"] = self._saved_path
125127
distro._UNIXCONFDIR = self._saved_UNIXCONFDIR
128+
distro._UNIXPROCDIR = self._saved_UNIXPROCDIR
126129
distro._UNIXUSRLIBDIR = self._saved_UNIXUSRLIBDIR
127130

128131
def _setup_for_distro(self, distro_root: str) -> None:
@@ -131,6 +134,7 @@ def _setup_for_distro(self, distro_root: str) -> None:
131134
# distro that runs this test, so we use a PATH with only one entry:
132135
os.environ["PATH"] = distro_bin
133136
distro._UNIXCONFDIR = os.path.join(distro_root, RELATIVE_UNIXCONFDIR)
137+
distro._UNIXPROCDIR = os.path.join(distro_root, RELATIVE_UNIXPROCDIR)
134138
distro._UNIXUSRLIBDIR = os.path.join(distro_root, RELATIVE_UNIXUSRLIBDIR)
135139

136140

@@ -628,6 +632,42 @@ def test_manjaro1512_lsb_release(self) -> None:
628632
# }
629633
# self._test_outcome(desired_outcome)
630634

635+
def test_cloudlinuxvm7_uname(self) -> None:
636+
self._test_outcome(
637+
{
638+
"id": "cloudlinux",
639+
"name": "CloudLinux",
640+
"version": "7",
641+
"pretty_name": "CloudLinux 7",
642+
"pretty_version": "7",
643+
"best_version": "7",
644+
}
645+
)
646+
647+
def test_cloudlinuxvm8_uname(self) -> None:
648+
self._test_outcome(
649+
{
650+
"id": "cloudlinux",
651+
"name": "CloudLinux",
652+
"version": "8",
653+
"pretty_name": "CloudLinux 8",
654+
"pretty_version": "8",
655+
"best_version": "8",
656+
}
657+
)
658+
659+
def test_cloudlinuxvm9_uname(self) -> None:
660+
self._test_outcome(
661+
{
662+
"id": "cloudlinux",
663+
"name": "CloudLinux",
664+
"version": "9.4",
665+
"pretty_name": "CloudLinux 9.4",
666+
"pretty_version": "9.4",
667+
"best_version": "9.4",
668+
}
669+
)
670+
631671
def test_openbsd62_uname(self) -> None:
632672
self._test_outcome(
633673
{
@@ -2406,9 +2446,11 @@ def test_repr(self) -> None:
24062446
if attr in (
24072447
"root_dir",
24082448
"etc_dir",
2449+
"proc_dir",
24092450
"usr_lib_dir",
24102451
"_debian_version",
24112452
"_armbian_version",
2453+
"_kernel_modules",
24122454
):
24132455
continue
24142456
assert f"{attr}=" in repr_str

0 commit comments

Comments
 (0)