From b2c50e21ad0c9b8af778f59432ac0c5c29d91cb2 Mon Sep 17 00:00:00 2001 From: Lin Liu Date: Thu, 28 Mar 2024 03:35:53 +0000 Subject: [PATCH 1/4] CP-45921: Use dnf as package manager for XS9 Given XS9 has updated to dnf and no yum is available, xapi will choose package manager basing on following - If dnf exists, use dnf - otherwise, fallback to yum xapi just presume dnf or yum is available in the system. Because xapi decides to use dnf or yum according to the running environment, this commit is compatible with yum/xs8 and dnf/xs9 Signed-off-by: Lin Liu --- ocaml/xapi/pkg_mgr.ml | 261 +++++++++++++++++++++++++++++++ ocaml/xapi/pkg_mgr.mli | 62 ++++++++ ocaml/xapi/repository.ml | 52 ++---- ocaml/xapi/repository_helpers.ml | 107 ++++--------- ocaml/xapi/xapi_globs.ml | 3 + ocaml/xapi/xapi_pool.ml | 5 +- 6 files changed, 375 insertions(+), 115 deletions(-) create mode 100644 ocaml/xapi/pkg_mgr.ml create mode 100644 ocaml/xapi/pkg_mgr.mli diff --git a/ocaml/xapi/pkg_mgr.ml b/ocaml/xapi/pkg_mgr.ml new file mode 100644 index 00000000000..1977b9ac950 --- /dev/null +++ b/ocaml/xapi/pkg_mgr.ml @@ -0,0 +1,261 @@ +type mgr = Yum | Dnf + +let active () = + match Sys.file_exists !Xapi_globs.dnf_cmd with true -> Dnf | false -> Yum + +module type S = sig + val repoquery_installed : unit -> string * string list + + val clean_cache : repo_name:string -> string * string list + + val get_pkgs_from_updateinfo : + sub_command:string -> repositories:string list -> string * string list + + val get_updates_from_upgrade_dry_run : + repositories:string list -> string * string list + + val is_obsoleted : + pkg_name:string -> repositories:string list -> string * string list + + val get_pkgs_from_repoquery : + pkg_narrow:string -> repositories:string list -> string * string list + + val config_repo : + repo_name:string -> config:string list -> string * string list + + val get_repo_config : repo_name:string -> string * string list + + val make_cache : repo_name:string -> string * string list + + val sync_repo : repo_name:string -> string * string list + + val apply_upgrade : repositories:string list -> string * string list +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 get_pkgs_from_repoquery : string -> 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 -> "%{" ^ 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 get_pkgs_from_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 get_pkgs_from_repoquery pkg_narrow repositories = + Common_args.get_pkgs_from_repoquery repositories + @ [Printf.sprintf "--pkgnarrow=%s" pkg_narrow; "--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 get_pkgs_from_repoquery pkg_narrow repositories = + Common_args.get_pkgs_from_repoquery repositories + @ [Printf.sprintf "--%s" pkg_narrow] + |> 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 + (* functor to construct comand line and arguments *) + let repoquery_installed () = (M.repoquery_cmd, M.repoquery_installed ()) + + let clean_cache ~repo_name = (M.pkg_cmd, M.clean_cache repo_name) + + let get_pkgs_from_updateinfo ~sub_command ~repositories = + (M.pkg_cmd, M.get_pkgs_from_updateinfo sub_command repositories) + + let get_updates_from_upgrade_dry_run ~repositories = + (M.pkg_cmd, M.get_updates_from_upgrade_dry_run repositories) + + let is_obsoleted ~pkg_name ~repositories = + (M.repoquery_cmd, M.is_obsoleted pkg_name repositories) + + let get_pkgs_from_repoquery ~pkg_narrow ~repositories = + (M.repoquery_cmd, M.get_pkgs_from_repoquery pkg_narrow repositories) + + let config_repo ~repo_name ~config = + (M.repoconfig_cmd, M.config_repo repo_name config) + + let get_repo_config ~repo_name = + (M.repoconfig_cmd, M.get_repo_config repo_name) + + let make_cache ~repo_name = (M.pkg_cmd, M.make_cache repo_name) + + let sync_repo ~repo_name = (M.reposync_cmd, M.sync_repo repo_name) + + let apply_upgrade ~repositories = (M.pkg_cmd, 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..fdfe92a60be --- /dev/null +++ b/ocaml/xapi/pkg_mgr.mli @@ -0,0 +1,62 @@ +val repoquery_sep : string + +(* Type of package manager supported *) +type mgr = Yum | Dnf + +(*Which pakcage manager is actively using*) +val active : unit -> mgr + +(* Interfaces to build command line and params for the package manager + * Each interface has following return result + * (comandline * commandline params) with type (string, string list) + * *) +module type S = sig + (* Command line and arguments to perform repoquery installed packages*) + val repoquery_installed : unit -> string * string list + + (* Command line and arguments to clean a repo cache *) + val clean_cache : repo_name:string -> string * string list + + (*Command line and arguments to get packages from updateinfo*) + val get_pkgs_from_updateinfo : + sub_command:string -> repositories:string list -> string * string list + + (*Command line and arguments to dry run an upgrade, with repositories enabled*) + val get_updates_from_upgrade_dry_run : + repositories:string list -> string * string list + + (*Command line and arguments to check whether a package is obsoleted by any other + * package in given repositories*) + val is_obsoleted : + pkg_name:string -> repositories:string list -> string * string list + + (*Command line and arguments to perform repoquery in repositories + * Filter the output by pkg_narrow*) + val get_pkgs_from_repoquery : + pkg_narrow:string -> repositories:string list -> string * string list + + (*Command line and arguments to perform repo configuration*) + val config_repo : + repo_name:string -> config:string list -> string * string list + + (*Command line and arguments to query repo configuration*) + val get_repo_config : repo_name:string -> string * string list + + (* Command line and arguments to make cache for repo*) + val make_cache : repo_name:string -> string * string list + + (* Command line and arguments to sync with remote repo*) + val sync_repo : repo_name:string -> string * string list + + (* Command line and arguments to apply upgrades from repos*) + val apply_upgrade : repositories:string list -> string * string list +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 + +(* Get active package manager module to provide command line and arguments*) +val get_pkg_mgr : (module S) diff --git a/ocaml/xapi/repository.ml b/ocaml/xapi/repository.ml index bd63984c0a1..21e33ded9bd 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 Pkg_mgr.(active () = 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 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 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 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 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..f71938301ac 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 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 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 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 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,8 @@ 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 - ] - in - Helpers.call_script !Xapi_globs.yum_cmd params + let cmd, params = Pkgs.get_pkgs_from_updateinfo ~sub_command ~repositories in + Helpers.call_script cmd params |> assert_yum_error |> Astring.String.cuts ~sep:"\n" |> List.map (fun x -> @@ -674,15 +649,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 +666,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 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 ; @@ -710,19 +677,8 @@ let get_installed_pkgs () = |> 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 cmd, params = Pkgs.get_pkgs_from_repoquery ~pkg_narrow ~repositories in + Helpers.call_script cmd params |> Astring.String.cuts ~sep:"\n" |> List.map (fun x -> debug "repoquery available: %s" x ; @@ -733,7 +689,11 @@ 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 open Pkg_mgr in + let updates_arg = + match active () with Yum -> "updates" | Dnf -> "upgrades" + in + let updates = get_pkgs_from_repoquery updates_arg repositories in (* 'new_updates' are a list of RPM packages to be installed, rather than updated *) let new_updates = get_pkgs_from_repoquery "available" repositories @@ -936,20 +896,17 @@ 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" - ] - in - match Forkhelpers.execute_command_get_output !Xapi_globs.yum_cmd params with + let cmd, params = Pkgs.get_updates_from_upgrade_dry_run ~repositories in + 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) + -> ( + let open Pkg_mgr in + (*Yum put the details to stderr while dnf to stdout*) + (match active () 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..20e32cdebd4 100644 --- a/ocaml/xapi/xapi_pool.ml +++ b/ocaml/xapi/xapi_pool.ml @@ -3406,7 +3406,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 Pkg_mgr.(active () = 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 ()) ; From 3267e995376312ca513f5203e3de7afbe84aac47 Mon Sep 17 00:00:00 2001 From: Lin Liu Date: Fri, 29 Mar 2024 05:49:48 +0000 Subject: [PATCH 2/4] CP-45921: Add unittest Signed-off-by: Lin Liu --- ocaml/tests/suite_alcotest.ml | 1 + ocaml/tests/test_pkg_mgr.ml | 254 ++++++++++++++++++++++++++++++++++ 2 files changed, 255 insertions(+) create mode 100644 ocaml/tests/test_pkg_mgr.ml diff --git a/ocaml/tests/suite_alcotest.ml b/ocaml/tests/suite_alcotest.ml index 21a637d5ea7..c57b707e9a8 100644 --- a/ocaml/tests/suite_alcotest.ml +++ b/ocaml/tests/suite_alcotest.ml @@ -71,4 +71,5 @@ let () = @ Test_session.tests @ Test_xapi_cmd_result.tests @ Test_extauth_plugin_ADwinbind.tests + @ Test_pkg_mgr.tests ) diff --git a/ocaml/tests/test_pkg_mgr.ml b/ocaml/tests/test_pkg_mgr.ml new file mode 100644 index 00000000000..4f665e7a158 --- /dev/null +++ b/ocaml/tests/test_pkg_mgr.ml @@ -0,0 +1,254 @@ +let format_cmd cmd_lines = + let cmd, args = cmd_lines in + Printf.sprintf "%s:%s" cmd (String.concat " " args) + +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 + ( !Xapi_globs.dnf_cmd + , [ + "repoquery" + ; "-a" + ; "--qf" + ; "%{name}:|%{epoch}:|%{version}:|%{release}:|%{arch}:|%{repoid}" + ; "--installed" + ] + ) + (Pkg_mgr.Dnf_cmd.repoquery_installed ()) + ) + ] + +let test_dnf_clean_all_cache = + [ + ( "" + , `Quick + , check + (!Xapi_globs.dnf_cmd, ["clean"; "all"]) + (Pkg_mgr.Dnf_cmd.clean_cache ~repo_name:"*") + ) + ] + +let test_dnf_clean_repo_cache = + [ + ( "" + , `Quick + , check + ( !Xapi_globs.dnf_cmd + , ["--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 + ( !Xapi_globs.dnf_cmd + , [ + "-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 + ( !Xapi_globs.dnf_cmd + , [ + "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 + ( !Xapi_globs.dnf_cmd + , [ + "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 + ( !Xapi_globs.dnf_cmd + , [ + "-y" + ; "--disablerepo=*" + ; "--enablerepo=testrepo1,testrepo2" + ; "upgrade" + ] + ) + (Pkg_mgr.Dnf_cmd.apply_upgrade ~repositories:["testrepo1"; "testrepo2"]) + ) + ] + +let test_yum_repo_query_installed = + [ + ( "" + , `Quick + , check + ( !Xapi_globs.repoquery_cmd + , [ + "-a" + ; "--pkgnarrow=installed" + ; "--qf" + ; "%{name}:|%{epoch}:|%{version}:|%{release}:|%{arch}:|%{repoid}" + ] + ) + (Pkg_mgr.Yum_cmd.repoquery_installed ()) + ) + ] + +let test_yum_clean_all_cache = + [ + ( "" + , `Quick + , check + (!Xapi_globs.yum_cmd, ["clean"; "all"]) + (Pkg_mgr.Yum_cmd.clean_cache ~repo_name:"*") + ) + ] + +let test_yum_clean_repo_cache = + [ + ( "" + , `Quick + , check + ( !Xapi_globs.yum_cmd + , ["--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 + ( !Xapi_globs.yum_cmd + , [ + "-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 + ( !Xapi_globs.yum_config_manager_cmd + , ["--setopt=testrepo.accesstoken=file:///some/path"; "testrepo"] + ) + (Pkg_mgr.Yum_cmd.config_repo ~repo_name:"testrepo" ~config) + ) + ] + +let test_yum_sync_repo = + [ + ( "" + , `Quick + , check + ( !Xapi_globs.reposync_cmd + , [ + "-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 + ( !Xapi_globs.yum_cmd + , [ + "-y" + ; "--disablerepo=*" + ; "--enablerepo=testrepo1,testrepo2" + ; "upgrade" + ] + ) + (Pkg_mgr.Yum_cmd.apply_upgrade ~repositories:["testrepo1"; "testrepo2"]) + ) + ] + +let tests = + [ + ("test_dnf_repo_query_installed", test_dnf_repo_query_installed) + ; ("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) + ] From d91592415424d24f86f6a92dafabc8e050e4cd15 Mon Sep 17 00:00:00 2001 From: Lin Liu Date: Tue, 2 Apr 2024 03:27:09 +0000 Subject: [PATCH 3/4] CP-45921: Add license header Signed-off-by: Lin Liu --- ocaml/xapi/pkg_mgr.ml | 14 ++++++++++++++ ocaml/xapi/pkg_mgr.mli | 14 ++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/ocaml/xapi/pkg_mgr.ml b/ocaml/xapi/pkg_mgr.ml index 1977b9ac950..733648b6c14 100644 --- a/ocaml/xapi/pkg_mgr.ml +++ b/ocaml/xapi/pkg_mgr.ml @@ -1,3 +1,17 @@ +(* + * Copyright (C) Citrix Systems 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 let active () = diff --git a/ocaml/xapi/pkg_mgr.mli b/ocaml/xapi/pkg_mgr.mli index fdfe92a60be..fed5a911b48 100644 --- a/ocaml/xapi/pkg_mgr.mli +++ b/ocaml/xapi/pkg_mgr.mli @@ -1,3 +1,17 @@ +(* + * Copyright (C) Citrix Systems 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 of package manager supported *) From 1ad52448de2de3ce8e571d7f78a8b2ab826804c1 Mon Sep 17 00:00:00 2001 From: Lin Liu Date: Wed, 3 Apr 2024 02:47:17 +0000 Subject: [PATCH 4/4] CP-45921: Code refine for - Move test code to its own suite - Replace active with manager, which is static - Add cmd_line record type to abstract cmd line type - Refine doc comment and license Signed-off-by: Lin Liu --- ocaml/tests/dune | 6 +- ocaml/tests/suite_alcotest.ml | 1 - ocaml/tests/test_pkg_mgr.ml | 272 ++++++++++++++++++++----------- ocaml/xapi/pkg_mgr.ml | 95 ++++++----- ocaml/xapi/pkg_mgr.mli | 75 ++++----- ocaml/xapi/repository.ml | 10 +- ocaml/xapi/repository_helpers.ml | 34 ++-- ocaml/xapi/xapi_pool.ml | 4 +- 8 files changed, 296 insertions(+), 201 deletions(-) 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/suite_alcotest.ml b/ocaml/tests/suite_alcotest.ml index c57b707e9a8..21a637d5ea7 100644 --- a/ocaml/tests/suite_alcotest.ml +++ b/ocaml/tests/suite_alcotest.ml @@ -71,5 +71,4 @@ let () = @ Test_session.tests @ Test_xapi_cmd_result.tests @ Test_extauth_plugin_ADwinbind.tests - @ Test_pkg_mgr.tests ) diff --git a/ocaml/tests/test_pkg_mgr.ml b/ocaml/tests/test_pkg_mgr.ml index 4f665e7a158..17e7eb5c28a 100644 --- a/ocaml/tests/test_pkg_mgr.ml +++ b/ocaml/tests/test_pkg_mgr.ml @@ -1,6 +1,8 @@ -let format_cmd cmd_lines = - let cmd, args = cmd_lines in - Printf.sprintf "%s:%s" cmd (String.concat " " args) +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 @@ -13,25 +15,51 @@ let test_dnf_repo_query_installed = ( "" , `Quick , check - ( !Xapi_globs.dnf_cmd - , [ - "repoquery" - ; "-a" - ; "--qf" - ; "%{name}:|%{epoch}:|%{version}:|%{release}:|%{arch}:|%{repoid}" - ; "--installed" - ] - ) + { + 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 - (!Xapi_globs.dnf_cmd, ["clean"; "all"]) + {cmd= !Xapi_globs.dnf_cmd; params= ["clean"; "all"]} (Pkg_mgr.Dnf_cmd.clean_cache ~repo_name:"*") ) ] @@ -41,9 +69,10 @@ let test_dnf_clean_repo_cache = ( "" , `Quick , check - ( !Xapi_globs.dnf_cmd - , ["--disablerepo=*"; "--enablerepo=test_repo"; "clean"; "all"] - ) + { + cmd= !Xapi_globs.dnf_cmd + ; params= ["--disablerepo=*"; "--enablerepo=test_repo"; "clean"; "all"] + } (Pkg_mgr.Dnf_cmd.clean_cache ~repo_name:"test_repo") ) ] @@ -55,16 +84,18 @@ let test_dnf_get_pkgs_from_updateinfo = ( "" , `Quick , check - ( !Xapi_globs.dnf_cmd - , [ - "-q" - ; "--disablerepo=*" - ; "--enablerepo=testrepo1,testrepo2" - ; "updateinfo" - ; "list" - ; "upgrades" - ] - ) + { + 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) ) ] @@ -75,13 +106,15 @@ let test_dnf_config_repo = ( "" , `Quick , check - ( !Xapi_globs.dnf_cmd - , [ - "config-manager" - ; "--setopt=testrepo.accesstoken=file:///some/path" - ; "testrepo" - ] - ) + { + 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) ) ] @@ -91,18 +124,20 @@ let test_dnf_sync_repo = ( "" , `Quick , check - ( !Xapi_globs.dnf_cmd - , [ - "reposync" - ; "-p" - ; !Xapi_globs.local_pool_repo_dir - ; "--downloadcomps" - ; "--download-metadata" - ; "--delete" - ; "--newest-only" - ; "--repoid=testrepo" - ] - ) + { + 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") ) ] @@ -112,14 +147,16 @@ let test_dnf_apply_upgrades = ( "" , `Quick , check - ( !Xapi_globs.dnf_cmd - , [ - "-y" - ; "--disablerepo=*" - ; "--enablerepo=testrepo1,testrepo2" - ; "upgrade" - ] - ) + { + cmd= !Xapi_globs.dnf_cmd + ; params= + [ + "-y" + ; "--disablerepo=*" + ; "--enablerepo=testrepo1,testrepo2" + ; "upgrade" + ] + } (Pkg_mgr.Dnf_cmd.apply_upgrade ~repositories:["testrepo1"; "testrepo2"]) ) ] @@ -129,14 +166,16 @@ let test_yum_repo_query_installed = ( "" , `Quick , check - ( !Xapi_globs.repoquery_cmd - , [ - "-a" - ; "--pkgnarrow=installed" - ; "--qf" - ; "%{name}:|%{epoch}:|%{version}:|%{release}:|%{arch}:|%{repoid}" - ] - ) + { + cmd= !Xapi_globs.repoquery_cmd + ; params= + [ + "-a" + ; "--pkgnarrow=installed" + ; "--qf" + ; "%{name}:|%{epoch}:|%{version}:|%{release}:|%{arch}:|%{repoid}" + ] + } (Pkg_mgr.Yum_cmd.repoquery_installed ()) ) ] @@ -146,7 +185,7 @@ let test_yum_clean_all_cache = ( "" , `Quick , check - (!Xapi_globs.yum_cmd, ["clean"; "all"]) + {cmd= !Xapi_globs.yum_cmd; params= ["clean"; "all"]} (Pkg_mgr.Yum_cmd.clean_cache ~repo_name:"*") ) ] @@ -156,9 +195,10 @@ let test_yum_clean_repo_cache = ( "" , `Quick , check - ( !Xapi_globs.yum_cmd - , ["--disablerepo=*"; "--enablerepo=test_repo"; "clean"; "all"] - ) + { + cmd= !Xapi_globs.yum_cmd + ; params= ["--disablerepo=*"; "--enablerepo=test_repo"; "clean"; "all"] + } (Pkg_mgr.Yum_cmd.clean_cache ~repo_name:"test_repo") ) ] @@ -170,16 +210,18 @@ let test_yum_get_pkgs_from_updateinfo = ( "" , `Quick , check - ( !Xapi_globs.yum_cmd - , [ - "-q" - ; "--disablerepo=*" - ; "--enablerepo=testrepo1,testrepo2" - ; "updateinfo" - ; "list" - ; "updates" - ] - ) + { + 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) ) ] @@ -190,9 +232,11 @@ let test_yum_config_repo = ( "" , `Quick , check - ( !Xapi_globs.yum_config_manager_cmd - , ["--setopt=testrepo.accesstoken=file:///some/path"; "testrepo"] - ) + { + 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) ) ] @@ -202,18 +246,20 @@ let test_yum_sync_repo = ( "" , `Quick , check - ( !Xapi_globs.reposync_cmd - , [ - "-p" - ; !Xapi_globs.local_pool_repo_dir - ; "--downloadcomps" - ; "--download-metadata" - ; "--delete" - ; "--newest-only" - ; "--repoid=testrepo" - ; "--plugins" - ] - ) + { + 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") ) ] @@ -223,21 +269,48 @@ let test_yum_apply_upgrades = ( "" , `Quick , check - ( !Xapi_globs.yum_cmd - , [ - "-y" - ; "--disablerepo=*" - ; "--enablerepo=testrepo1,testrepo2" - ; "upgrade" - ] - ) + { + 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) @@ -251,4 +324,7 @@ let tests = ; ("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 index 733648b6c14..e8fa455e627 100644 --- a/ocaml/xapi/pkg_mgr.ml +++ b/ocaml/xapi/pkg_mgr.ml @@ -1,5 +1,5 @@ (* - * Copyright (C) Citrix Systems Inc. + * 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 @@ -14,36 +14,38 @@ 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 repoquery_installed : unit -> string * string list + val manager : mgr + + val repoquery_installed : unit -> cmd_line - val clean_cache : repo_name:string -> string * string list + val clean_cache : repo_name:string -> cmd_line val get_pkgs_from_updateinfo : - sub_command:string -> repositories:string list -> string * string list + sub_command:string -> repositories:string list -> cmd_line + + val get_updates_from_upgrade_dry_run : repositories:string list -> cmd_line - val get_updates_from_upgrade_dry_run : - repositories:string list -> string * string list + val is_obsoleted : pkg_name:string -> repositories:string list -> cmd_line - val is_obsoleted : - pkg_name:string -> repositories:string list -> string * string list + val repoquery_updates : repositories:string list -> cmd_line - val get_pkgs_from_repoquery : - pkg_narrow:string -> repositories:string list -> string * string list + val repoquery_available : repositories:string list -> cmd_line - val config_repo : - repo_name:string -> config:string list -> string * string list + val config_repo : repo_name:string -> config:string list -> cmd_line - val get_repo_config : repo_name:string -> string * string list + val get_repo_config : repo_name:string -> cmd_line - val make_cache : repo_name:string -> string * string list + val make_cache : repo_name:string -> cmd_line - val sync_repo : repo_name:string -> string * string list + val sync_repo : repo_name:string -> cmd_line - val apply_upgrade : repositories:string list -> string * string list + val apply_upgrade : repositories:string list -> cmd_line end module type Args = sig @@ -65,7 +67,9 @@ module type Args = sig val is_obsoleted : string -> string list -> string list - val get_pkgs_from_repoquery : 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 @@ -82,7 +86,7 @@ let repoquery_sep = ":|" let fmt = ["name"; "epoch"; "version"; "release"; "arch"; "repoid"] - |> List.map (fun field -> "%{" ^ field ^ "}") + |> List.map (fun field -> Printf.sprintf "%%{%s}" field) (* %{field} *) |> String.concat repoquery_sep module Common_args = struct @@ -126,7 +130,7 @@ module Common_args = struct ; "upgrade" ] - let get_pkgs_from_repoquery repositories = + let repoquery repositories = [ "-a" ; "--disablerepo=*" @@ -184,9 +188,12 @@ module Yum_args : Args = struct let is_obsoleted pkg_name repositories = Common_args.is_obsoleted pkg_name repositories @ ["--plugins"] - let get_pkgs_from_repoquery pkg_narrow repositories = - Common_args.get_pkgs_from_repoquery repositories - @ [Printf.sprintf "--pkgnarrow=%s" pkg_narrow; "--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"] @@ -222,11 +229,13 @@ module Dnf_args : Args = struct let is_obsoleted pkg_name repositories = Common_args.is_obsoleted pkg_name repositories |> add_sub_cmd Repoquery - let get_pkgs_from_repoquery pkg_narrow repositories = - Common_args.get_pkgs_from_repoquery repositories - @ [Printf.sprintf "--%s" pkg_narrow] + 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 @@ -238,34 +247,46 @@ module Dnf_args : Args = struct 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 () = (M.repoquery_cmd, M.repoquery_installed ()) + let repoquery_installed () = + {cmd= M.repoquery_cmd; params= M.repoquery_installed ()} - let clean_cache ~repo_name = (M.pkg_cmd, M.clean_cache repo_name) + let clean_cache ~repo_name = {cmd= M.pkg_cmd; params= M.clean_cache repo_name} let get_pkgs_from_updateinfo ~sub_command ~repositories = - (M.pkg_cmd, M.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 = - (M.pkg_cmd, M.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 = - (M.repoquery_cmd, M.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 get_pkgs_from_repoquery ~pkg_narrow ~repositories = - (M.repoquery_cmd, M.get_pkgs_from_repoquery pkg_narrow repositories) + let repoquery_available ~repositories = + {cmd= M.repoquery_cmd; params= M.repoquery_available repositories} let config_repo ~repo_name ~config = - (M.repoconfig_cmd, M.config_repo repo_name config) + {cmd= M.repoconfig_cmd; params= M.config_repo repo_name config} let get_repo_config ~repo_name = - (M.repoconfig_cmd, M.get_repo_config repo_name) + {cmd= M.repoconfig_cmd; params= M.get_repo_config repo_name} - let make_cache ~repo_name = (M.pkg_cmd, M.make_cache repo_name) + let make_cache ~repo_name = {cmd= M.pkg_cmd; params= M.make_cache repo_name} - let sync_repo ~repo_name = (M.reposync_cmd, M.sync_repo repo_name) + let sync_repo ~repo_name = + {cmd= M.reposync_cmd; params= M.sync_repo repo_name} - let apply_upgrade ~repositories = (M.pkg_cmd, M.apply_upgrade repositories) + let apply_upgrade ~repositories = + {cmd= M.pkg_cmd; params= M.apply_upgrade repositories} end module Yum_cmd = Cmd_line (Yum_args) diff --git a/ocaml/xapi/pkg_mgr.mli b/ocaml/xapi/pkg_mgr.mli index fed5a911b48..b2678690ec0 100644 --- a/ocaml/xapi/pkg_mgr.mli +++ b/ocaml/xapi/pkg_mgr.mli @@ -1,5 +1,5 @@ (* - * Copyright (C) Citrix Systems Inc. + * 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 @@ -14,63 +14,60 @@ val repoquery_sep : string -(* Type of package manager supported *) type mgr = Yum | Dnf -(*Which pakcage manager is actively using*) -val active : unit -> mgr +type cmd_line = {cmd: string; params: string list} -(* Interfaces to build command line and params for the package manager - * Each interface has following return result - * (comandline * commandline params) with type (string, string list) - * *) +(** Interfaces to build command line and params for the package manager *) module type S = sig - (* Command line and arguments to perform repoquery installed packages*) - val repoquery_installed : unit -> string * string list + val manager : mgr - (* Command line and arguments to clean a repo cache *) - val clean_cache : repo_name:string -> string * string list + 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 *) - (*Command line and arguments to get packages from updateinfo*) val get_pkgs_from_updateinfo : - sub_command:string -> repositories:string list -> string * string list + 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 *) - (*Command line and arguments to dry run an upgrade, with repositories enabled*) - val get_updates_from_upgrade_dry_run : - repositories:string list -> string * string list + 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 *) - (*Command line and arguments to check whether a package is obsoleted by any other - * package in given repositories*) - val is_obsoleted : - pkg_name:string -> repositories:string list -> string * string list + val repoquery_updates : repositories:string list -> cmd_line + (** Command line and arguments to perform repoquery in repositories + * query pacakges that are upgrable *) - (*Command line and arguments to perform repoquery in repositories - * Filter the output by pkg_narrow*) - val get_pkgs_from_repoquery : - pkg_narrow:string -> repositories:string list -> string * string list + val repoquery_available : repositories:string list -> cmd_line + (** Command line and arguments to perform repoquery in repositories + * query pacakges that are available *) - (*Command line and arguments to perform repo configuration*) - val config_repo : - repo_name:string -> config:string list -> string * string list + val config_repo : repo_name:string -> config:string list -> cmd_line + (** Command line and arguments to perform repo configuration *) - (*Command line and arguments to query repo configuration*) - val get_repo_config : repo_name:string -> string * string list + val get_repo_config : repo_name:string -> cmd_line + (** Command line and arguments to query repo configuration *) - (* Command line and arguments to make cache for repo*) - val make_cache : repo_name:string -> string * string list + val make_cache : repo_name:string -> cmd_line + (** Command line and arguments to make cache for repo *) - (* Command line and arguments to sync with remote repo*) - val sync_repo : repo_name:string -> string * string list + val sync_repo : repo_name:string -> cmd_line + (** Command line and arguments to sync with remote repo *) - (* Command line and arguments to apply upgrades from repos*) - val apply_upgrade : repositories:string list -> string * string list + 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*) +(** 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 -(* Get active package manager module to provide command line and arguments*) 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 21e33ded9bd..a0f7d0ec403 100644 --- a/ocaml/xapi/repository.ml +++ b/ocaml/xapi/repository.ml @@ -130,7 +130,7 @@ let sync ~__context ~self ~token ~token_id = write_initial_yum_config () ; clean_yum_cache repo_name ; (* Remove imported YUM repository GPG key *) - if Pkg_mgr.(active () = Yum) then + if Pkgs.manager = Yum then Xapi_stdext_unix.Unixext.rm_rec (get_repo_config repo_name "gpgdir") ; Xapi_stdext_pervasives.Pervasiveext.finally (fun () -> @@ -146,7 +146,7 @@ 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 cmd, params = + let Pkg_mgr.{cmd; params} = [ "--save" ; proxy_url_param @@ -159,11 +159,11 @@ let sync ~__context ~self ~token ~token_id = ignore (Helpers.call_script ~log_output:Helpers.On_failure cmd params) ; (* Import YUM repository GPG key to check metadata in reposync *) - let cmd, params = Pkgs.make_cache ~repo_name in + let Pkg_mgr.{cmd; params} = Pkgs.make_cache ~repo_name in ignore (Helpers.call_script cmd params) ; (* Sync with remote repository *) - let cmd, params = Pkgs.sync_repo ~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 cmd params) @@ -568,7 +568,7 @@ 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 cmd, params = Pkgs.apply_upgrade ~repositories in + 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 diff --git a/ocaml/xapi/repository_helpers.ml b/ocaml/xapi/repository_helpers.ml index f71938301ac..6d7683f654b 100644 --- a/ocaml/xapi/repository_helpers.ml +++ b/ocaml/xapi/repository_helpers.ml @@ -275,7 +275,7 @@ let with_updateinfo_xml gz_path f = let clean_yum_cache name = try - let cmd, params = Pkgs.clean_cache ~repo_name:name in + 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,7 +349,7 @@ let write_yum_config ~source_url ~binary_url ~repo_gpgcheck ~gpgkey_path ) let get_repo_config repo_name config_name = - let cmd, params = Pkgs.get_repo_config ~repo_name in + 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 -> @@ -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 cmd, params = + let Pkg_mgr.{cmd; params} = [ "--save" ; Printf.sprintf "--setopt=%s.sslverify=false" repo_name @@ -480,7 +480,7 @@ let parse_updateinfo_list acc line = acc let is_obsoleted pkg_name repositories = - let cmd, params = Pkgs.is_obsoleted ~pkg_name ~repositories in + let Pkg_mgr.{cmd; params} = Pkgs.is_obsoleted ~pkg_name ~repositories in match Helpers.call_script cmd params |> Astring.String.cuts ~sep:"\n" ~empty:false with @@ -497,7 +497,9 @@ let is_obsoleted pkg_name repositories = false let get_pkgs_from_yum_updateinfo_list sub_command repositories = - let cmd, params = Pkgs.get_pkgs_from_updateinfo ~sub_command ~repositories in + let Pkg_mgr.{cmd; params} = + Pkgs.get_pkgs_from_updateinfo ~sub_command ~repositories + in Helpers.call_script cmd params |> assert_yum_error |> Astring.String.cuts ~sep:"\n" @@ -666,7 +668,7 @@ let parse_line_of_repoquery acc line = acc let get_installed_pkgs () = - let cmd, params = Pkgs.repoquery_installed () in + let Pkg_mgr.{cmd; params} = Pkgs.repoquery_installed () in Helpers.call_script cmd params |> Astring.String.cuts ~sep:"\n" |> List.map (fun x -> @@ -676,8 +678,7 @@ 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 cmd, params = Pkgs.get_pkgs_from_repoquery ~pkg_narrow ~repositories in +let get_pkgs_from_repoquery cmd params = Helpers.call_script cmd params |> Astring.String.cuts ~sep:"\n" |> List.map (fun x -> @@ -689,14 +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 open Pkg_mgr in - let updates_arg = - match active () with Yum -> "updates" | Dnf -> "upgrades" - in - let updates = get_pkgs_from_repoquery updates_arg 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 @@ -896,15 +895,16 @@ module YumUpgradeOutput = struct end let get_updates_from_yum_upgrade_dry_run repositories = - let cmd, params = Pkgs.get_updates_from_upgrade_dry_run ~repositories in + let Pkg_mgr.{cmd; params} = + Pkgs.get_updates_from_upgrade_dry_run ~repositories + in match Forkhelpers.execute_command_get_output cmd params with | _, _ -> Some [] | exception Forkhelpers.Spawn_internal_error (stderr, stdout, Unix.WEXITED 1) -> ( - let open Pkg_mgr in (*Yum put the details to stderr while dnf to stdout*) - (match active () with Yum -> stderr | Dnf -> stdout) + (match Pkgs.manager with Yum -> stderr | Dnf -> stdout) |> YumUpgradeOutput.parse_output_of_dry_run |> function | Ok (pkgs, Some txn_file) -> diff --git a/ocaml/xapi/xapi_pool.ml b/ocaml/xapi/xapi_pool.ml index 20e32cdebd4..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 @@ -3408,7 +3410,7 @@ let sync_updates ~__context ~self ~force ~token ~token_id = sync ~__context ~self:repo ~token ~token_id ; (* Dnf sync all the metadata including updateinfo, * Thus no need to re-create pool repository *) - if Pkg_mgr.(active () = Yum) then + if Pkgs.manager = Yum then create_pool_repository ~__context ~self:repo ) ; let checksum = set_available_updates ~__context in