Skip to content

Commit ec93f04

Browse files
committed
Add support for CloudLinux VM missing os-release and /etc/redhat-release
> closes #240
1 parent 3bd19e6 commit ec93f04

File tree

6 files changed

+97
-5
lines changed

6 files changed

+97
-5
lines changed

src/distro/distro.py

Lines changed: 34 additions & 5 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

@@ -759,6 +761,7 @@ def __init__(
759761
"""
760762
self.root_dir = root_dir
761763
self.etc_dir = os.path.join(root_dir, "etc") if root_dir else _UNIXCONFDIR
764+
self.proc_dir = os.path.join(root_dir, "proc") if root_dir else _UNIXPROCDIR
762765
self.usr_lib_dir = (
763766
os.path.join(root_dir, "usr/lib") if root_dir else _UNIXUSRLIBDIR
764767
)
@@ -1239,14 +1242,32 @@ def _armbian_version(self) -> str:
12391242
except FileNotFoundError:
12401243
return ""
12411244

1242-
@staticmethod
1243-
def _parse_uname_content(lines: Sequence[str]) -> Dict[str, str]:
1245+
def _parse_uname_content(self, lines: Sequence[str]) -> Dict[str, str]:
12441246
if not lines:
12451247
return {}
12461248
props = {}
1247-
match = re.search(r"^([^\s]+)\s+([\d\.]+)", lines[0].strip())
1249+
match = re.search(r"^([^\s]+)\s+([^\s]+)", lines[0].strip())
12481250
if match:
1249-
name, version = match.groups()
1251+
name, release = match.groups()
1252+
1253+
# CloudLinux detection relies on uname release information
1254+
release_parts = release.split(".")
1255+
if (
1256+
# check penultimate release component contains an "el*" version
1257+
len(release_parts) > 1
1258+
and release_parts[-2].startswith("el")
1259+
and (
1260+
# CloudLinux < 9 : "lve*" is set in kernel release
1261+
any(rc.startswith("lve") for rc in release_parts)
1262+
# CloudLinux >= 9 : check whether "kmodlve" is loaded
1263+
or "kmodlve" in self._loaded_modules
1264+
)
1265+
):
1266+
props["id"] = "cloudlinux"
1267+
props["name"] = "CloudLinux"
1268+
# strip "el" prefix and replace underscores by dots
1269+
props["release"] = release_parts[-2][2:].replace("_", ".")
1270+
return props
12501271

12511272
# This is to prevent the Linux kernel version from
12521273
# appearing as the 'best' version on otherwise
@@ -1255,9 +1276,17 @@ def _parse_uname_content(lines: Sequence[str]) -> Dict[str, str]:
12551276
return {}
12561277
props["id"] = name.lower()
12571278
props["name"] = name
1258-
props["release"] = version
1279+
props["release"] = release.split("-")[0] # only keep version part
12591280
return props
12601281

1282+
@cached_property
1283+
def _loaded_modules(self) -> List[str]:
1284+
try:
1285+
with open(os.path.join(self.proc_dir, "modules"), encoding="ascii") as fp:
1286+
return [line.split()[0] for line in fp]
1287+
except OSError:
1288+
return []
1289+
12611290
@staticmethod
12621291
def _to_str(bytestring: bytes) -> str:
12631292
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
{
@@ -2387,9 +2427,11 @@ def test_repr(self) -> None:
23872427
if attr in (
23882428
"root_dir",
23892429
"etc_dir",
2430+
"proc_dir",
23902431
"usr_lib_dir",
23912432
"_debian_version",
23922433
"_armbian_version",
2434+
"_loaded_modules",
23932435
):
23942436
continue
23952437
assert f"{attr}=" in repr_str

0 commit comments

Comments
 (0)