@@ -55,6 +55,7 @@ def host_data(hostname_or_ip: str) -> dict[str, str]:
5555class 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 )]
0 commit comments