diff --git a/ocaml/tests/dune b/ocaml/tests/dune index 93bf4b66ddf..27a684614d2 100644 --- a/ocaml/tests/dune +++ b/ocaml/tests/dune @@ -8,7 +8,7 @@ test_vm_placement test_vm_helpers test_repository test_repository_helpers test_ref test_livepatch test_rpm test_updateinfo test_storage_smapiv1_wrapper test_storage_quicktest test_observer - test_pool_periodic_update_sync)) + test_pool_periodic_update_sync test_pkg_mgr)) (libraries alcotest angstrom @@ -60,13 +60,13 @@ (tests (names test_vm_helpers test_vm_placement test_network_sriov test_vdi_cbt test_clustering test_pusb test_daemon_manager test_repository test_repository_helpers - test_livepatch test_rpm test_updateinfo test_pool_periodic_update_sync) + test_livepatch test_rpm test_updateinfo test_pool_periodic_update_sync test_pkg_mgr) (package xapi) (modes exe) (modules test_vm_helpers test_vm_placement test_network_sriov test_vdi_cbt test_event test_clustering test_cluster_host test_cluster test_pusb test_daemon_manager test_repository test_repository_helpers test_livepatch test_rpm - test_updateinfo test_pool_periodic_update_sync) + test_updateinfo test_pool_periodic_update_sync test_pkg_mgr) (libraries alcotest fmt diff --git a/ocaml/tests/test_pkg_mgr.ml b/ocaml/tests/test_pkg_mgr.ml new file mode 100644 index 00000000000..17e7eb5c28a --- /dev/null +++ b/ocaml/tests/test_pkg_mgr.ml @@ -0,0 +1,330 @@ +open Pkg_mgr + +let format_cmd cmd_line' = + let {cmd; params} = cmd_line' in + Printf.sprintf "%s:%s" cmd (String.concat " " params) + +let check exp ac () = + let exp_str = format_cmd exp in + let ac_str = ac |> format_cmd in + let msg = Printf.sprintf "%s -- %s" exp_str ac_str in + Alcotest.(check string) msg exp_str ac_str + +let test_dnf_repo_query_installed = + [ + ( "" + , `Quick + , check + { + cmd= !Xapi_globs.dnf_cmd + ; params= + [ + "repoquery" + ; "-a" + ; "--qf" + ; "%{name}:|%{epoch}:|%{version}:|%{release}:|%{arch}:|%{repoid}" + ; "--installed" + ] + } + (Pkg_mgr.Dnf_cmd.repoquery_installed ()) + ) + ] + +let test_dnf_repo_query_updates = + [ + ( "" + , `Quick + , check + { + cmd= !Xapi_globs.dnf_cmd + ; params= + [ + "repoquery" + ; "-a" + ; "--disablerepo=*" + ; "--enablerepo=testrepo1,testrepo2" + ; "--qf" + ; "%{name}:|%{epoch}:|%{version}:|%{release}:|%{arch}:|%{repoid}" + ; "--upgrades" + ] + } + (Pkg_mgr.Dnf_cmd.repoquery_updates + ~repositories:["testrepo1"; "testrepo2"] + ) + ) + ] + +let test_dnf_clean_all_cache = + [ + ( "" + , `Quick + , check + {cmd= !Xapi_globs.dnf_cmd; params= ["clean"; "all"]} + (Pkg_mgr.Dnf_cmd.clean_cache ~repo_name:"*") + ) + ] + +let test_dnf_clean_repo_cache = + [ + ( "" + , `Quick + , check + { + cmd= !Xapi_globs.dnf_cmd + ; params= ["--disablerepo=*"; "--enablerepo=test_repo"; "clean"; "all"] + } + (Pkg_mgr.Dnf_cmd.clean_cache ~repo_name:"test_repo") + ) + ] + +let test_dnf_get_pkgs_from_updateinfo = + let sub_command = "upgrades" in + let repositories = ["testrepo1"; "testrepo2"] in + [ + ( "" + , `Quick + , check + { + cmd= !Xapi_globs.dnf_cmd + ; params= + [ + "-q" + ; "--disablerepo=*" + ; "--enablerepo=testrepo1,testrepo2" + ; "updateinfo" + ; "list" + ; "upgrades" + ] + } + (Pkg_mgr.Dnf_cmd.get_pkgs_from_updateinfo ~sub_command ~repositories) + ) + ] + +let test_dnf_config_repo = + let config = ["--setopt=testrepo.accesstoken=file:///some/path"] in + [ + ( "" + , `Quick + , check + { + cmd= !Xapi_globs.dnf_cmd + ; params= + [ + "config-manager" + ; "--setopt=testrepo.accesstoken=file:///some/path" + ; "testrepo" + ] + } + (Pkg_mgr.Dnf_cmd.config_repo ~repo_name:"testrepo" ~config) + ) + ] + +let test_dnf_sync_repo = + [ + ( "" + , `Quick + , check + { + cmd= !Xapi_globs.dnf_cmd + ; params= + [ + "reposync" + ; "-p" + ; !Xapi_globs.local_pool_repo_dir + ; "--downloadcomps" + ; "--download-metadata" + ; "--delete" + ; "--newest-only" + ; "--repoid=testrepo" + ] + } + (Pkg_mgr.Dnf_cmd.sync_repo ~repo_name:"testrepo") + ) + ] + +let test_dnf_apply_upgrades = + [ + ( "" + , `Quick + , check + { + cmd= !Xapi_globs.dnf_cmd + ; params= + [ + "-y" + ; "--disablerepo=*" + ; "--enablerepo=testrepo1,testrepo2" + ; "upgrade" + ] + } + (Pkg_mgr.Dnf_cmd.apply_upgrade ~repositories:["testrepo1"; "testrepo2"]) + ) + ] + +let test_yum_repo_query_installed = + [ + ( "" + , `Quick + , check + { + cmd= !Xapi_globs.repoquery_cmd + ; params= + [ + "-a" + ; "--pkgnarrow=installed" + ; "--qf" + ; "%{name}:|%{epoch}:|%{version}:|%{release}:|%{arch}:|%{repoid}" + ] + } + (Pkg_mgr.Yum_cmd.repoquery_installed ()) + ) + ] + +let test_yum_clean_all_cache = + [ + ( "" + , `Quick + , check + {cmd= !Xapi_globs.yum_cmd; params= ["clean"; "all"]} + (Pkg_mgr.Yum_cmd.clean_cache ~repo_name:"*") + ) + ] + +let test_yum_clean_repo_cache = + [ + ( "" + , `Quick + , check + { + cmd= !Xapi_globs.yum_cmd + ; params= ["--disablerepo=*"; "--enablerepo=test_repo"; "clean"; "all"] + } + (Pkg_mgr.Yum_cmd.clean_cache ~repo_name:"test_repo") + ) + ] + +let test_yum_get_pkgs_from_updateinfo = + let sub_command = "updates" in + let repositories = ["testrepo1"; "testrepo2"] in + [ + ( "" + , `Quick + , check + { + cmd= !Xapi_globs.yum_cmd + ; params= + [ + "-q" + ; "--disablerepo=*" + ; "--enablerepo=testrepo1,testrepo2" + ; "updateinfo" + ; "list" + ; "updates" + ] + } + (Pkg_mgr.Yum_cmd.get_pkgs_from_updateinfo ~sub_command ~repositories) + ) + ] + +let test_yum_config_repo = + let config = ["--setopt=testrepo.accesstoken=file:///some/path"] in + [ + ( "" + , `Quick + , check + { + cmd= !Xapi_globs.yum_config_manager_cmd + ; params= + ["--setopt=testrepo.accesstoken=file:///some/path"; "testrepo"] + } + (Pkg_mgr.Yum_cmd.config_repo ~repo_name:"testrepo" ~config) + ) + ] + +let test_yum_sync_repo = + [ + ( "" + , `Quick + , check + { + cmd= !Xapi_globs.reposync_cmd + ; params= + [ + "-p" + ; !Xapi_globs.local_pool_repo_dir + ; "--downloadcomps" + ; "--download-metadata" + ; "--delete" + ; "--newest-only" + ; "--repoid=testrepo" + ; "--plugins" + ] + } + (Pkg_mgr.Yum_cmd.sync_repo ~repo_name:"testrepo") + ) + ] + +let test_yum_apply_upgrades = + [ + ( "" + , `Quick + , check + { + cmd= !Xapi_globs.yum_cmd + ; params= + [ + "-y" + ; "--disablerepo=*" + ; "--enablerepo=testrepo1,testrepo2" + ; "upgrade" + ] + } + (Pkg_mgr.Yum_cmd.apply_upgrade ~repositories:["testrepo1"; "testrepo2"]) + ) + ] + +let test_yum_repo_query_updates = + [ + ( "" + , `Quick + , check + { + cmd= !Xapi_globs.repoquery_cmd + ; params= + [ + "-a" + ; "--disablerepo=*" + ; "--enablerepo=testrepo1,testrepo2" + ; "--qf" + ; "%{name}:|%{epoch}:|%{version}:|%{release}:|%{arch}:|%{repoid}" + ; "--pkgnarrow updates" + ; "--plugins" + ] + } + (Pkg_mgr.Yum_cmd.repoquery_updates + ~repositories:["testrepo1"; "testrepo2"] + ) + ) + ] + +let tests = + [ + ("test_dnf_repo_query_installed", test_dnf_repo_query_installed) + ; ("test_dnf_repo_query_updates", test_dnf_repo_query_updates) + ; ("test_dnf_clean_all_cache", test_dnf_clean_all_cache) + ; ("test_dnf_clean_repo_cache", test_dnf_clean_repo_cache) + ; ("test_dnf_get_pkgs_from_updateinfo", test_dnf_get_pkgs_from_updateinfo) + ; ("test_dnf_cofig_repo", test_dnf_config_repo) + ; ("test_dnf_sync_repo", test_dnf_sync_repo) + ; ("test_dnf_apply_upgrades", test_dnf_apply_upgrades) + ; ("test_yum_repo_query_installed", test_yum_repo_query_installed) + ; ("test_yum_clean_all_cache", test_yum_clean_all_cache) + ; ("test_yum_clean_repo_cache", test_yum_clean_repo_cache) + ; ("test_yum_get_pkgs_from_updateinfo", test_yum_get_pkgs_from_updateinfo) + ; ("test_yum_cofig_repo", test_yum_config_repo) + ; ("test_yum_sync_repo", test_yum_sync_repo) + ; ("test_yum_apply_upgrades", test_yum_apply_upgrades) + ; ("test_yum_repo_query_updates", test_yum_repo_query_updates) + ] + +let () = Alcotest.run "Pkg_mgr suite" tests diff --git a/ocaml/xapi/pkg_mgr.ml b/ocaml/xapi/pkg_mgr.ml new file mode 100644 index 00000000000..e8fa455e627 --- /dev/null +++ b/ocaml/xapi/pkg_mgr.ml @@ -0,0 +1,296 @@ +(* + * Copyright (c) Cloud Software Group, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; version 2.1 only. with the special + * exception on linking described in file LICENSE. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + *) + +type mgr = Yum | Dnf + +type cmd_line = {cmd: string; params: string list} + +let active () = + match Sys.file_exists !Xapi_globs.dnf_cmd with true -> Dnf | false -> Yum + +module type S = sig + val manager : mgr + + val repoquery_installed : unit -> cmd_line + + val clean_cache : repo_name:string -> cmd_line + + val get_pkgs_from_updateinfo : + sub_command:string -> repositories:string list -> cmd_line + + val get_updates_from_upgrade_dry_run : repositories:string list -> cmd_line + + val is_obsoleted : pkg_name:string -> repositories:string list -> cmd_line + + val repoquery_updates : repositories:string list -> cmd_line + + val repoquery_available : repositories:string list -> cmd_line + + val config_repo : repo_name:string -> config:string list -> cmd_line + + val get_repo_config : repo_name:string -> cmd_line + + val make_cache : repo_name:string -> cmd_line + + val sync_repo : repo_name:string -> cmd_line + + val apply_upgrade : repositories:string list -> cmd_line +end + +module type Args = sig + val pkg_cmd : string + + val repoquery_cmd : string + + val repoconfig_cmd : string + + val reposync_cmd : string + + val repoquery_installed : unit -> string list + + val clean_cache : string -> string list + + val get_pkgs_from_updateinfo : string -> string list -> string list + + val get_updates_from_upgrade_dry_run : string list -> string list + + val is_obsoleted : string -> string list -> string list + + val repoquery_updates : string list -> string list + + val repoquery_available : string list -> string list + + val config_repo : string -> string list -> string list + + val get_repo_config : string -> string list + + val make_cache : string -> string list + + val sync_repo : string -> string list + + val apply_upgrade : string list -> string list +end + +let repoquery_sep = ":|" + +let fmt = + ["name"; "epoch"; "version"; "release"; "arch"; "repoid"] + |> List.map (fun field -> Printf.sprintf "%%{%s}" field) (* %{field} *) + |> String.concat repoquery_sep + +module Common_args = struct + let clean_cache = function + | "*" -> + ["clean"; "all"] + | name -> + [ + "--disablerepo=*" + ; Printf.sprintf "--enablerepo=%s" name + ; "clean" + ; "all" + ] + + let get_pkgs_from_updateinfo sub_command repositories = + [ + "-q" + ; "--disablerepo=*" + ; Printf.sprintf "--enablerepo=%s" (String.concat "," repositories) + ; "updateinfo" + ; "list" + ; sub_command + ] + + let is_obsoleted pkg_name repositories = + [ + "-a" + ; "--disablerepo=*" + ; Printf.sprintf "--enablerepo=%s" (String.concat "," repositories) + ; "--whatobsoletes" + ; pkg_name + ; "--qf" + ; "%{name}" + ] + + let get_updates_from_upgrade_dry_run repositories = + [ + "--disablerepo=*" + ; Printf.sprintf "--enablerepo=%s" (String.concat "," repositories) + ; "--assumeno" + ; "upgrade" + ] + + let repoquery repositories = + [ + "-a" + ; "--disablerepo=*" + ; Printf.sprintf "--enablerepo=%s" (String.concat "," repositories) + ; "--qf" + ; fmt + ] + + let config_repo repo_name config = config @ [repo_name] + + let make_cache repo_name = + [ + "--disablerepo=*" + ; Printf.sprintf "--enablerepo=%s" repo_name + ; "-y" + ; "makecache" + ] + + let sync_repo repo_name = + [ + "-p" + ; !Xapi_globs.local_pool_repo_dir + ; "--downloadcomps" + ; "--download-metadata" + ; "--delete" + ; "--newest-only" + ; Printf.sprintf "--repoid=%s" repo_name + ] + + let apply_upgrade repositories = + [ + "-y" + ; "--disablerepo=*" + ; Printf.sprintf "--enablerepo=%s" (String.concat "," repositories) + ; "upgrade" + ] +end + +module Yum_args : Args = struct + include Common_args + + let repoquery_installed () = ["-a"; "--pkgnarrow=installed"; "--qf"; fmt] + + let pkg_cmd = !Xapi_globs.yum_cmd + + let repoquery_cmd = !Xapi_globs.repoquery_cmd + + let repoconfig_cmd = !Xapi_globs.yum_config_manager_cmd + + let reposync_cmd = !Xapi_globs.reposync_cmd + + let get_updates_from_upgrade_dry_run repositories = + ["--quiet"] @ Common_args.get_updates_from_upgrade_dry_run repositories + + let is_obsoleted pkg_name repositories = + Common_args.is_obsoleted pkg_name repositories @ ["--plugins"] + + let repoquery_available repositories = + Common_args.repoquery repositories + @ ["--pkgnarrow"; "available"; "--plugins"] + + let repoquery_updates repositories = + Common_args.repoquery repositories @ ["--pkgnarrow"; "updates"; "--plugins"] + + let sync_repo repo_name = Common_args.sync_repo repo_name @ ["--plugins"] + + let get_repo_config repo_name = [repo_name] +end + +module Dnf_args : Args = struct + let pkg_cmd = !Xapi_globs.dnf_cmd + + let repoquery_cmd = !Xapi_globs.dnf_cmd + + let repoconfig_cmd = !Xapi_globs.dnf_cmd + + let reposync_cmd = !Xapi_globs.dnf_cmd + + type sub_cmd = Repoquery | Repoconfig | Reposync + + let sub_cmd_to_string = function + | Reposync -> + "reposync" + | Repoconfig -> + "config-manager" + | Repoquery -> + "repoquery" + + let add_sub_cmd sub_cmd params = [sub_cmd_to_string sub_cmd] @ params + + include Common_args + + let repoquery_installed () = + ["-a"; "--qf"; fmt; "--installed"] |> add_sub_cmd Repoquery + + let is_obsoleted pkg_name repositories = + Common_args.is_obsoleted pkg_name repositories |> add_sub_cmd Repoquery + + let repoquery_available repositories = + Common_args.repoquery repositories @ ["--available"] + |> add_sub_cmd Repoquery + + let repoquery_updates repositories = + Common_args.repoquery repositories @ ["--upgrades"] |> add_sub_cmd Repoquery + + let config_repo repo_name config = + Common_args.config_repo repo_name config |> add_sub_cmd Repoconfig + + let sync_repo repo_name = + Common_args.sync_repo repo_name |> add_sub_cmd Reposync + + let get_repo_config repo_name = + ["--dump"; repo_name] |> add_sub_cmd Repoconfig +end + +module Cmd_line (M : Args) : S = struct + let manager = + match M.pkg_cmd with cmd when cmd = !Xapi_globs.dnf_cmd -> Dnf | _ -> Yum + + (* functor to construct comand line and arguments *) + let repoquery_installed () = + {cmd= M.repoquery_cmd; params= M.repoquery_installed ()} + + let clean_cache ~repo_name = {cmd= M.pkg_cmd; params= M.clean_cache repo_name} + + let get_pkgs_from_updateinfo ~sub_command ~repositories = + { + cmd= M.pkg_cmd + ; params= M.get_pkgs_from_updateinfo sub_command repositories + } + + let get_updates_from_upgrade_dry_run ~repositories = + {cmd= M.pkg_cmd; params= M.get_updates_from_upgrade_dry_run repositories} + + let is_obsoleted ~pkg_name ~repositories = + {cmd= M.repoquery_cmd; params= M.is_obsoleted pkg_name repositories} + + let repoquery_updates ~repositories = + {cmd= M.repoquery_cmd; params= M.repoquery_updates repositories} + + let repoquery_available ~repositories = + {cmd= M.repoquery_cmd; params= M.repoquery_available repositories} + + let config_repo ~repo_name ~config = + {cmd= M.repoconfig_cmd; params= M.config_repo repo_name config} + + let get_repo_config ~repo_name = + {cmd= M.repoconfig_cmd; params= M.get_repo_config repo_name} + + let make_cache ~repo_name = {cmd= M.pkg_cmd; params= M.make_cache repo_name} + + let sync_repo ~repo_name = + {cmd= M.reposync_cmd; params= M.sync_repo repo_name} + + let apply_upgrade ~repositories = + {cmd= M.pkg_cmd; params= M.apply_upgrade repositories} +end + +module Yum_cmd = Cmd_line (Yum_args) +module Dnf_cmd = Cmd_line (Dnf_args) + +let get_pkg_mgr : (module S) = + match active () with Dnf -> (module Dnf_cmd) | Yum -> (module Yum_cmd) diff --git a/ocaml/xapi/pkg_mgr.mli b/ocaml/xapi/pkg_mgr.mli new file mode 100644 index 00000000000..b2678690ec0 --- /dev/null +++ b/ocaml/xapi/pkg_mgr.mli @@ -0,0 +1,73 @@ +(* + * Copyright (c) Cloud Software Group, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; version 2.1 only. with the special + * exception on linking described in file LICENSE. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + *) + +val repoquery_sep : string + +type mgr = Yum | Dnf + +type cmd_line = {cmd: string; params: string list} + +(** Interfaces to build command line and params for the package manager *) +module type S = sig + val manager : mgr + + val repoquery_installed : unit -> cmd_line + (** Command line and arguments to perform repoquery installed packages *) + + val clean_cache : repo_name:string -> cmd_line + (** Command line and arguments to clean a repo cache *) + + val get_pkgs_from_updateinfo : + sub_command:string -> repositories:string list -> cmd_line + (** Command line and arguments to get packages from updateinfo *) + + val get_updates_from_upgrade_dry_run : repositories:string list -> cmd_line + (** Command line and arguments to dry run an upgrade, with repositories enabled *) + + val is_obsoleted : pkg_name:string -> repositories:string list -> cmd_line + (** Command line and arguments to check whether a package is obsoleted by any other + * package in given repositories *) + + val repoquery_updates : repositories:string list -> cmd_line + (** Command line and arguments to perform repoquery in repositories + * query pacakges that are upgrable *) + + val repoquery_available : repositories:string list -> cmd_line + (** Command line and arguments to perform repoquery in repositories + * query pacakges that are available *) + + val config_repo : repo_name:string -> config:string list -> cmd_line + (** Command line and arguments to perform repo configuration *) + + val get_repo_config : repo_name:string -> cmd_line + (** Command line and arguments to query repo configuration *) + + val make_cache : repo_name:string -> cmd_line + (** Command line and arguments to make cache for repo *) + + val sync_repo : repo_name:string -> cmd_line + (** Command line and arguments to sync with remote repo *) + + val apply_upgrade : repositories:string list -> cmd_line + (** Command line and arguments to apply upgrades from repos *) +end + +(** Exposed only for unittest, do not use the modules directly + * Instead, use get_pkg_mgr to detect active package manager *) +module Yum_cmd : S + +module Dnf_cmd : S + +val get_pkg_mgr : (module S) +(** Get active package manager module to provide command line and arguments *) diff --git a/ocaml/xapi/repository.ml b/ocaml/xapi/repository.ml index bd63984c0a1..a0f7d0ec403 100644 --- a/ocaml/xapi/repository.ml +++ b/ocaml/xapi/repository.ml @@ -21,6 +21,8 @@ open Updateinfo open Repository_helpers module UpdateIdSet = Set.Make (String) +module Pkgs = (val Pkg_mgr.get_pkg_mgr) + let capacity_in_parallel = 16 (* The cache below is protected by pool's current_operations locking mechanism *) @@ -128,7 +130,8 @@ let sync ~__context ~self ~token ~token_id = write_initial_yum_config () ; clean_yum_cache repo_name ; (* Remove imported YUM repository GPG key *) - Xapi_stdext_unix.Unixext.rm_rec (get_repo_config repo_name "gpgdir") ; + if Pkgs.manager = Yum then + Xapi_stdext_unix.Unixext.rm_rec (get_repo_config repo_name "gpgdir") ; Xapi_stdext_pervasives.Pervasiveext.finally (fun () -> with_access_token ~token ~token_id @@ fun token_path -> @@ -143,49 +146,27 @@ let sync ~__context ~self ~token ~token_id = let proxy_url_param, proxy_username_param, proxy_password_param = get_proxy_params ~__context repo_name in - let config_params = + let Pkg_mgr.{cmd; params} = [ "--save" ; proxy_url_param ; proxy_username_param ; proxy_password_param ; token_param - ; repo_name ] + |> fun config -> Pkgs.config_repo ~repo_name ~config in - ignore - (Helpers.call_script ~log_output:Helpers.On_failure - !Xapi_globs.yum_config_manager_cmd - config_params - ) ; + ignore (Helpers.call_script ~log_output:Helpers.On_failure cmd params) ; (* Import YUM repository GPG key to check metadata in reposync *) - let makecache_params = - [ - "--disablerepo=*" - ; Printf.sprintf "--enablerepo=%s" repo_name - ; "-y" - ; "makecache" - ] - in - ignore (Helpers.call_script !Xapi_globs.yum_cmd makecache_params) ; + let Pkg_mgr.{cmd; params} = Pkgs.make_cache ~repo_name in + ignore (Helpers.call_script cmd params) ; (* Sync with remote repository *) - let sync_params = - [ - "-p" - ; !Xapi_globs.local_pool_repo_dir - ; "--downloadcomps" - ; "--download-metadata" - ; "--delete" - ; "--plugins" - ; "--newest-only" - ; Printf.sprintf "--repoid=%s" repo_name - ] - in + let Pkg_mgr.{cmd; params} = Pkgs.sync_repo ~repo_name in Unixext.mkdir_rec !Xapi_globs.local_pool_repo_dir 0o700 ; clean_yum_cache repo_name ; - ignore (Helpers.call_script !Xapi_globs.reposync_cmd sync_params) + ignore (Helpers.call_script cmd params) ) (fun () -> (* Rewrite repo conf file as initial content to remove credential related info, @@ -587,15 +568,8 @@ let get_pool_updates_in_json ~__context ~hosts = let apply ~__context ~host = (* This function runs on member host *) with_local_repositories ~__context (fun repositories -> - let params = - [ - "-y" - ; "--disablerepo=*" - ; Printf.sprintf "--enablerepo=%s" (String.concat "," repositories) - ; "upgrade" - ] - in - try ignore (Helpers.call_script !Xapi_globs.yum_cmd params) + let Pkg_mgr.{cmd; params} = Pkgs.apply_upgrade ~repositories in + try ignore (Helpers.call_script cmd params) with e -> let host' = Ref.string_of host in error "Failed to apply updates on host ref='%s': %s" host' diff --git a/ocaml/xapi/repository_helpers.ml b/ocaml/xapi/repository_helpers.ml index f67e8647822..6d7683f654b 100644 --- a/ocaml/xapi/repository_helpers.ml +++ b/ocaml/xapi/repository_helpers.ml @@ -24,6 +24,8 @@ module RpmFullNameSet = Set.Make (String) let exposing_pool_repo_mutex = Mutex.create () +module Pkgs = (val Pkg_mgr.get_pkg_mgr) + module Update = struct type t = { name: string @@ -273,10 +275,8 @@ let with_updateinfo_xml gz_path f = let clean_yum_cache name = try - let params = - ["--disablerepo=*"; Printf.sprintf "--enablerepo=%s" name; "clean"; "all"] - in - ignore (Helpers.call_script !Xapi_globs.yum_cmd params) + let Pkg_mgr.{cmd; params} = Pkgs.clean_cache ~repo_name:name in + ignore (Helpers.call_script cmd params) with e -> warn "Unable to clean YUM cache for %s: %s" name (ExnHelper.string_of_exn e) @@ -349,8 +349,8 @@ let write_yum_config ~source_url ~binary_url ~repo_gpgcheck ~gpgkey_path ) let get_repo_config repo_name config_name = - let config_params = [repo_name] in - Helpers.call_script !Xapi_globs.yum_config_manager_cmd config_params + let Pkg_mgr.{cmd; params} = Pkgs.get_repo_config ~repo_name in + Helpers.call_script cmd params |> Astring.String.cuts ~sep:"\n" |> List.filter_map (fun kv -> let prefix = Printf.sprintf "%s = " config_name in @@ -424,7 +424,7 @@ let with_local_repositories ~__context f = write_yum_config ~source_url:None ~binary_url ~repo_gpgcheck:false ~gpgkey_path ~repo_name ; clean_yum_cache repo_name ; - let config_params = + let Pkg_mgr.{cmd; params} = [ "--save" ; Printf.sprintf "--setopt=%s.sslverify=false" repo_name @@ -432,14 +432,10 @@ let with_local_repositories ~__context f = ; Printf.sprintf "--setopt=%s.ptoken=true" repo_name (* makes yum include the pool secret as a cookie in all requests (only to access the repo mirror in the coordinator!) *) - ; repo_name ] + |> fun config -> Pkgs.config_repo ~repo_name ~config in - ignore - (Helpers.call_script - !Xapi_globs.yum_config_manager_cmd - config_params - ) ; + ignore (Helpers.call_script cmd params) ; repo_name ) enabled @@ -484,21 +480,9 @@ let parse_updateinfo_list acc line = acc let is_obsoleted pkg_name repositories = - let params = - [ - "-a" - ; "--plugins" - ; "--disablerepo=*" - ; Printf.sprintf "--enablerepo=%s" (String.concat "," repositories) - ; "--whatobsoletes" - ; pkg_name - ; "--qf" - ; "%{name}" - ] - in + let Pkg_mgr.{cmd; params} = Pkgs.is_obsoleted ~pkg_name ~repositories in match - Helpers.call_script !Xapi_globs.repoquery_cmd params - |> Astring.String.cuts ~sep:"\n" ~empty:false + Helpers.call_script cmd params |> Astring.String.cuts ~sep:"\n" ~empty:false with | [] -> false @@ -513,17 +497,10 @@ let is_obsoleted pkg_name repositories = false let get_pkgs_from_yum_updateinfo_list sub_command repositories = - let params = - [ - "-q" - ; "--disablerepo=*" - ; Printf.sprintf "--enablerepo=%s" (String.concat "," repositories) - ; "updateinfo" - ; "list" - ; sub_command - ] + let Pkg_mgr.{cmd; params} = + Pkgs.get_pkgs_from_updateinfo ~sub_command ~repositories in - Helpers.call_script !Xapi_globs.yum_cmd params + Helpers.call_script cmd params |> assert_yum_error |> Astring.String.cuts ~sep:"\n" |> List.map (fun x -> @@ -674,15 +651,8 @@ let eval_guidances ~updates_info ~updates ~kind ~livepatches = ) |> GuidanceSet.resort -let repoquery_sep = ":|" - -let get_repoquery_fmt () = - ["name"; "epoch"; "version"; "release"; "arch"; "repoid"] - |> List.map (fun field -> "%{" ^ field ^ "}") - |> String.concat repoquery_sep - let parse_line_of_repoquery acc line = - match Astring.String.cuts ~sep:repoquery_sep line with + match Astring.String.cuts ~sep:Pkg_mgr.repoquery_sep line with | [name; epoch'; version; release; arch; repo] -> ( try let epoch = Epoch.of_string epoch' in @@ -698,9 +668,8 @@ let parse_line_of_repoquery acc line = acc let get_installed_pkgs () = - let fmt = get_repoquery_fmt () in - let params = ["-a"; "--pkgnarrow=installed"; "--qf"; fmt] in - Helpers.call_script !Xapi_globs.repoquery_cmd params + let Pkg_mgr.{cmd; params} = Pkgs.repoquery_installed () in + Helpers.call_script cmd params |> Astring.String.cuts ~sep:"\n" |> List.map (fun x -> debug "repoquery installed: %s" x ; @@ -709,20 +678,8 @@ let get_installed_pkgs () = |> List.fold_left parse_line_of_repoquery [] |> List.map (fun (pkg, _) -> (Pkg.to_name_arch_string pkg, pkg)) -let get_pkgs_from_repoquery pkg_narrow repositories = - let fmt = get_repoquery_fmt () in - let params = - [ - "-a" - ; "--plugins" - ; "--disablerepo=*" - ; Printf.sprintf "--enablerepo=%s" (String.concat "," repositories) - ; Printf.sprintf "--pkgnarrow=%s" pkg_narrow - ; "--qf" - ; fmt - ] - in - Helpers.call_script !Xapi_globs.repoquery_cmd params +let get_pkgs_from_repoquery cmd params = + Helpers.call_script cmd params |> Astring.String.cuts ~sep:"\n" |> List.map (fun x -> debug "repoquery available: %s" x ; @@ -733,10 +690,12 @@ let get_pkgs_from_repoquery pkg_narrow repositories = let get_updates_from_repoquery repositories = List.iter (fun r -> clean_yum_cache r) repositories ; (* Use 'updates' to decrease the number of packages to apply 'is_obsoleted' *) - let updates = get_pkgs_from_repoquery "updates" repositories in + let Pkg_mgr.{cmd; params} = Pkgs.repoquery_updates ~repositories in + let updates = get_pkgs_from_repoquery cmd params in (* 'new_updates' are a list of RPM packages to be installed, rather than updated *) + let Pkg_mgr.{cmd; params} = Pkgs.repoquery_available ~repositories in let new_updates = - get_pkgs_from_repoquery "available" repositories + get_pkgs_from_repoquery cmd params |> List.filter (fun x -> not (List.mem x updates)) |> List.filter (fun (pkg, _) -> not (is_obsoleted pkg.Pkg.name repositories)) in @@ -936,20 +895,18 @@ module YumUpgradeOutput = struct end let get_updates_from_yum_upgrade_dry_run repositories = - let params = - [ - "--disablerepo=*" - ; Printf.sprintf "--enablerepo=%s" (String.concat "," repositories) - ; "--assumeno" - ; "--quiet" - ; "upgrade" - ] + let Pkg_mgr.{cmd; params} = + Pkgs.get_updates_from_upgrade_dry_run ~repositories in - match Forkhelpers.execute_command_get_output !Xapi_globs.yum_cmd params with + match Forkhelpers.execute_command_get_output cmd params with | _, _ -> Some [] - | exception Forkhelpers.Spawn_internal_error (stderr, _, Unix.WEXITED 1) -> ( - stderr |> YumUpgradeOutput.parse_output_of_dry_run |> function + | exception Forkhelpers.Spawn_internal_error (stderr, stdout, Unix.WEXITED 1) + -> ( + (*Yum put the details to stderr while dnf to stdout*) + (match Pkgs.manager with Yum -> stderr | Dnf -> stdout) + |> YumUpgradeOutput.parse_output_of_dry_run + |> function | Ok (pkgs, Some txn_file) -> Unixext.unlink_safe txn_file ; Some pkgs diff --git a/ocaml/xapi/xapi_globs.ml b/ocaml/xapi/xapi_globs.ml index 37e9f561537..ee8d03df389 100644 --- a/ocaml/xapi/xapi_globs.ml +++ b/ocaml/xapi/xapi_globs.ml @@ -909,6 +909,8 @@ let repository_domain_name_allowlist = ref [] let yum_cmd = ref "/usr/bin/yum" +let dnf_cmd = ref "/usr/bin/dnf" + let kpatch_cmd = ref "/usr/sbin/kpatch" let xen_livepatch_cmd = ref "/usr/sbin/xen-livepatch" @@ -1714,6 +1716,7 @@ module Resources = struct , "Executed to manage SQlite Database, like PBIS database" ) ; ("yum-cmd", yum_cmd, "Path to yum command") + ; ("dnf-cmd", dnf_cmd, "Path to dnf command") ; ("reposync-cmd", reposync_cmd, "Path to reposync command") ; ( "yum-config-manager-cmd" , yum_config_manager_cmd diff --git a/ocaml/xapi/xapi_pool.ml b/ocaml/xapi/xapi_pool.ml index f4736a1a61f..314d77f0b15 100644 --- a/ocaml/xapi/xapi_pool.ml +++ b/ocaml/xapi/xapi_pool.ml @@ -18,6 +18,8 @@ module Listext = Xapi_stdext_std.Listext module Unixext = Xapi_stdext_unix.Unixext module Xstringext = Xapi_stdext_std.Xstringext +module Pkgs = (val Pkg_mgr.get_pkg_mgr) + let finally = Xapi_stdext_pervasives.Pervasiveext.finally let with_lock = Xapi_stdext_threads.Threadext.Mutex.execute @@ -3406,7 +3408,10 @@ let sync_updates ~__context ~self ~force ~token ~token_id = |> List.iter (fun repo -> if force then cleanup_pool_repo ~__context ~self:repo ; sync ~__context ~self:repo ~token ~token_id ; - create_pool_repository ~__context ~self:repo + (* Dnf sync all the metadata including updateinfo, + * Thus no need to re-create pool repository *) + if Pkgs.manager = Yum then + create_pool_repository ~__context ~self:repo ) ; let checksum = set_available_updates ~__context in Db.Pool.set_last_update_sync ~__context ~self ~value:(Date.now ()) ;