Skip to content

Commit bd7b920

Browse files
committed
Detect dnf/yum, and use the right package manager
This is required for XCP-ng 9, which uses dnf. Signed-off-by: Gaëtan Lehmann <gaetan.lehmann@vates.tech>
1 parent 418f713 commit bd7b920

2 files changed

Lines changed: 48 additions & 27 deletions

File tree

lib/host.py

Lines changed: 48 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ def host_data(hostname_or_ip: str) -> dict[str, str]:
5555
class Host:
5656
xe_prefix = "host"
5757
pool: "Pool"
58+
package_manager: str
5859

5960
# Data extraction is automatic, no conversion from str is done.
6061
@dataclass
@@ -90,6 +91,13 @@ def __init__(self, pool: Pool, hostname_or_ip: str):
9091

9192
self.rescan_block_devices_info()
9293

94+
if self.file_exists('/usr/bin/yum'):
95+
self.package_manager = 'yum'
96+
elif self.file_exists('/usr/bin/dnf'):
97+
self.package_manager = 'dnf'
98+
else:
99+
raise Exception("No yum or dnf on the host")
100+
93101
def __str__(self) -> str:
94102
return self.hostname_or_ip
95103

@@ -509,7 +517,7 @@ def yum_clean_metadata(self) -> str:
509517
yum clean metadata -q
510518
"""
511519
logging.info(f"[{self}] Removing cache metadata...")
512-
return self.ssh("yum clean metadata -q")
520+
return self.ssh(f'{self.package_manager} clean metadata -q')
513521

514522
def yum_update(self, enablerepos: list[str] = []) -> str:
515523
"""Updates packages on target.
@@ -522,7 +530,7 @@ def yum_update(self, enablerepos: list[str] = []) -> str:
522530
523531
:param enablerepos: Enable one or more repositories (default: []).
524532
"""
525-
base_command = "yum update -y"
533+
base_command = f'{self.package_manager} update -y'
526534

527535
logging.info(f"[{self}] Updating packages...")
528536
if enablerepos:
@@ -601,7 +609,7 @@ def is_enabled(self) -> bool:
601609
def has_updates(self) -> bool:
602610
try:
603611
# yum check-update returns 100 if there are updates, 1 if there's an error, 0 if no updates
604-
self.ssh('yum check-update')
612+
self.ssh(f'{self.package_manager} check-update')
605613
# returned 0, else there would have been a SSHCommandFailed
606614
return False
607615
except commands.SSHCommandFailed as e:
@@ -625,41 +633,52 @@ def get_last_yum_history_tid(self) -> int:
625633
[...]
626634
"""
627635
try:
628-
history_str = self.ssh('yum history list --noplugins')
636+
history_str = self.ssh(f'{self.package_manager} history list --noplugins')
629637
except commands.SSHCommandFailed:
630-
# yum history list fails if the list is empty, and it's also not possible to rollback
631-
# to before the first transaction, so "0" would not be appropriate as last transaction.
632-
# To workaround this, create transactions: install and remove a small package.
633-
logging.info(f"[{self}] Install and remove a small package to workaround empty yum history.")
634-
self.yum_install(['gpm-libs'])
635-
self.yum_remove(['gpm-libs'])
636-
history_str = self.ssh('yum history list --noplugins')
637-
638-
history = history_str.splitlines()
639-
line_index = None
640-
for i in range(len(history)):
641-
if history[i].startswith('--------'):
642-
line_index = i
643-
break
638+
# yum history list fails on xcp-ng 8 if the list is empty, but dnf history list works properly on xcp-ng 9.
639+
# Just use a fake empty value to deal with both versions in the same way.
640+
if self.package_manager == 'yum':
641+
history_str = '''ID | Command line | Date and time | Action(s) | Altered
642+
-------------------------------------------------------------------------------'''
643+
else:
644+
raise
644645

645-
if line_index is None:
646-
raise Exception('Unable to get yum transactions')
646+
def split_history(history_str: str) -> list[str]:
647+
history = history_str.splitlines()
648+
line_index = None
649+
for i in range(len(history)):
650+
if history[i].startswith('--------'):
651+
line_index = i
652+
break
653+
if line_index is None:
654+
raise Exception('Unable to get yum transactions')
655+
return history[line_index + 1:]
656+
657+
history = split_history(history_str)
658+
if not history:
659+
# we need a transaction to already exist. Install a small package with no deps, and remove it immediately.
660+
logging.info(f"[{self}] Install and remove a small package to workaround empty yum history.")
661+
small_pkg = 'gpm-libs' if self.xcp_version.major == 8 else 'bubblewrap'
662+
self.yum_install([small_pkg])
663+
self.yum_remove([small_pkg])
664+
history = split_history(self.ssh(f'{self.package_manager} history list --noplugins'))
647665

648666
try:
649-
return int(history[line_index + 1].split()[0])
667+
return int(history[0].split()[0])
650668
except ValueError:
651669
raise Exception('Unable to parse correctly last yum history tid. Output:\n' + history_str)
652670

653671
def yum_install(self, packages: list[str], enablerepo: str | None = None) -> str:
654672
logging.info(f"[{self}] Install packages: {' '.join(packages)} on host")
655-
cmd = 'yum install --setopt=skip_missing_names_on_install=False -y'
673+
opts = '--setopt=skip_missing_names_on_install=False' if self.package_manager == 'yum' else ''
674+
cmd = f'{self.package_manager} install {opts} -y'
656675
if enablerepo is not None:
657676
cmd = f'{cmd} --enablerepo={enablerepo}'
658677
return self.ssh(f'{cmd} {" ".join(packages)}')
659678

660679
def yum_remove(self, packages: list[str]) -> str:
661680
logging.info(f"[{self}] Remove packages: {' '.join(packages)} from host")
662-
return self.ssh(f'yum remove -y {" ".join(packages)}')
681+
return self.ssh(f'{self.package_manager} remove -y {" ".join(packages)}')
663682

664683
def packages(self) -> list[str]:
665684
""" Returns the list of installed RPMs - with version, release, arch and epoch. """
@@ -692,9 +711,12 @@ def yum_restore_saved_state(self) -> None:
692711

693712
assert isinstance(self.saved_rollback_id, int)
694713

695-
self.ssh(
696-
f'yum history rollback --enablerepo=xcp-ng-base,xcp-ng-testing,xcp-ng-updates {self.saved_rollback_id} -y'
697-
)
714+
repositories = ['xcp-ng-base']
715+
if self.xcp_version.major == 8:
716+
# TODO: activate those repositories in xcp-ng 9. For now they are not available.
717+
repositories += ['xcp-ng-testing', 'xcp-ng-updates']
718+
self.ssh(f'{self.package_manager} history rollback'
719+
f' --enablerepo={",".join(repositories)} {self.saved_rollback_id} -y')
698720
pkgs = self.packages()
699721
if self.saved_packages_list != pkgs:
700722
missing = [x for x in self.saved_packages_list if x not in set(pkgs)]

tests/guest_tools/unix/test_guest_tools_unix.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import pytest
44

55
import logging
6-
import time
76

87
from lib.common import PackageManagerEnum, wait_for
98
from lib.host import Host

0 commit comments

Comments
 (0)