Skip to content

Commit 777b134

Browse files
committed
Add support for CloudLinux VM missing os-release and /etc/redhat-release
> closes #240
1 parent 20d8b9d commit 777b134

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
)
@@ -1241,14 +1244,32 @@ def _armbian_version(self) -> str:
12411244
except FileNotFoundError:
12421245
return ""
12431246

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

12531274
# This is to prevent the Linux kernel version from
12541275
# appearing as the 'best' version on otherwise
@@ -1257,9 +1278,17 @@ def _parse_uname_content(lines: Sequence[str]) -> Dict[str, str]:
12571278
return {}
12581279
props["id"] = name.lower()
12591280
props["name"] = name
1260-
props["release"] = version
1281+
props["release"] = release.split("-")[0] # only keep version part
12611282
return props
12621283

1284+
@cached_property
1285+
def _kernel_modules(self) -> List[str]:
1286+
try:
1287+
with open(os.path.join(self.proc_dir, "modules"), encoding="ascii") as fp:
1288+
return [line.split()[0] for line in fp]
1289+
except OSError:
1290+
return []
1291+
12631292
@staticmethod
12641293
def _to_str(bytestring: bytes) -> str:
12651294
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)