From a0a14ea1ee38ce78f7433b3a93f7eea61c8163fc Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Wed, 10 Sep 2025 12:50:52 -0500 Subject: T7737: add user and sudo_user fields to session for use in commit Add optional protobuf fields to convey environment variables USER and SUDO_USER, as needed for some config_mode scripts. Add non-option fields to the session record type, defaulting to the empty string. --- src/session.mli | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src/session.mli') diff --git a/src/session.mli b/src/session.mli index abe5165..5dbbaf0 100644 --- a/src/session.mli +++ b/src/session.mli @@ -15,13 +15,14 @@ type session_data = { conf_mode: bool; changeset: cfg_op list; client_app: string; - user: string; - client_pid: int32 + client_pid: int32; + client_user: string; + client_sudo_user: string; } exception Session_error of string -val make : world -> string -> string -> int32 -> session_data +val make : world -> string -> string -> string -> int32 -> session_data val set_modified : session_data -> session_data -- cgit v1.2.3 From 4a10cd92e8e5d3f7bf53c1a80c408c68073535c8 Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Wed, 10 Sep 2025 14:36:49 -0500 Subject: T7737: provide session pid, user, sudo_user to vyos-commitd Send vyconfd session pid and client env vars to vyos-commitd for use in config mode scripts. --- data/vycall.proto | 14 +++++----- src/commit.ml | 54 +++++++++++++++++++++------------------ src/commit.mli | 7 ++--- src/session.ml | 4 +-- src/session.mli | 2 +- src/vycall_client.ml | 3 +++ src/vycall_pbt.ml | 72 +++++++++++++++++++++++++++++++++++++++++++--------- src/vycall_pbt.mli | 6 +++++ src/vyconfd.ml | 11 ++++++-- 9 files changed, 122 insertions(+), 51 deletions(-) (limited to 'src/session.mli') diff --git a/data/vycall.proto b/data/vycall.proto index 0037aa7..9517217 100644 --- a/data/vycall.proto +++ b/data/vycall.proto @@ -12,10 +12,12 @@ message Call { message Commit { required string session_id = 1; - required bool dry_run = 4; - required bool atomic = 5; - required bool background = 6; - - optional Status init = 7; - repeated Call calls = 8; + required int32 session_pid = 2; + required string sudo_user = 3; + required string user = 4; + required bool dry_run = 5; + required bool atomic = 6; + required bool background = 7; + optional Status init = 8; + repeated Call calls = 9; } diff --git a/src/commit.ml b/src/commit.ml index 8913ceb..7b04c36 100644 --- a/src/commit.ml +++ b/src/commit.ml @@ -12,10 +12,20 @@ let tree_source_to_yojson = function | ADD -> `String "ADD" type status = { - success : bool; - out : string; + success : bool; + out : string; } [@@deriving to_yojson] +let default_status = { + success = true; + out = ""; +} + +let fail_status msg = + { success = false; + out = msg; + } + type node_data = { script_name: string; priority: int; @@ -39,6 +49,9 @@ let default_node_data = { type commit_data = { session_id: string; + session_pid: int32; + sudo_user: string; + user: string; dry_run: bool; atomic: bool; background: bool; @@ -49,21 +62,6 @@ type commit_data = { result : status; } [@@deriving to_yojson] -let default_commit_data = { - session_id = ""; - dry_run = false; - atomic = false; - background = false; - init = None; - node_list = []; - config_diff = CT.default; - config_result = CT.default; - result = { success = true; out = ""; }; -} - -let fail_status msg = - { success=false; out=msg } - let lex_order c1 c2 = let c = Vyos1x.Util.lex_order c1.path c2.path in match c with @@ -224,25 +222,31 @@ let config_result_update c_data n_data = let commit_update c_data = match c_data.init with | None -> - { default_commit_data with - init=Some (fail_status "commitd failure: no init status provided") + { c_data with + init = Some (fail_status "commitd failure: no init status provided") } | Some _ -> let func acc_data nd = match nd.reply with | None -> - { default_commit_data with - init=Some (fail_status"commitd failure: no reply status provided") + { c_data with + init = Some (fail_status "commitd failure: no reply status provided") } | Some _ -> config_result_update acc_data nd in List.fold_left func c_data c_data.node_list -let make_commit_data ?(dry_run=false) rt at wt id = +let make_commit_data ?(dry_run=false) rt at wt id pid sudo_user user = let diff = CD.diff_tree [] at wt in let del_list, add_list = calculate_priority_lists rt diff in - { default_commit_data with - session_id = id; + { session_id = id; + session_pid = pid; + sudo_user = sudo_user; + user = user; dry_run = dry_run; + atomic = false; + background = false; + init = None; config_diff = diff; config_result = CT.get_subtree diff ["inter"]; - node_list = del_list @ add_list; } + node_list = del_list @ add_list; + result = default_status; } diff --git a/src/commit.mli b/src/commit.mli index 01022ec..ee38768 100644 --- a/src/commit.mli +++ b/src/commit.mli @@ -17,6 +17,9 @@ type node_data = { type commit_data = { session_id: string; + session_pid: int32; + sudo_user: string; + user: string; dry_run: bool; atomic: bool; background: bool; @@ -33,9 +36,7 @@ val tree_source_to_yojson : tree_source -> [> `String of string ] val default_node_data : node_data -val default_commit_data : commit_data - -val make_commit_data : ?dry_run:bool -> Vyos1x.Reference_tree.t -> Vyos1x.Config_tree.t -> Vyos1x.Config_tree.t -> string -> commit_data +val make_commit_data : ?dry_run:bool -> Vyos1x.Reference_tree.t -> Vyos1x.Config_tree.t -> Vyos1x.Config_tree.t -> string -> int32 -> string -> string -> commit_data val calculate_priority_lists : Vyos1x.Reference_tree.t -> Vyos1x.Config_tree.t -> node_data list * node_data list diff --git a/src/session.ml b/src/session.ml index 23c3d19..5fd20b5 100644 --- a/src/session.ml +++ b/src/session.ml @@ -191,7 +191,7 @@ let save w s file = | Error e -> raise (Session_error (Printf.sprintf "Error saving config: %s" e)) | Ok () -> s -let prepare_commit ?(dry_run=false) w config id = +let prepare_commit ?(dry_run=false) w config id pid sudo_user user = let at = w.running_config in let rt = w.reference_tree in let vc = w.vyconf_config in @@ -207,7 +207,7 @@ let prepare_commit ?(dry_run=false) w config id = with Vyos1x.Internal.Write_error msg -> raise (Session_error msg) in - CC.make_commit_data ~dry_run:dry_run rt at config id + CC.make_commit_data ~dry_run:dry_run rt at config id pid sudo_user user let get_config w s id = let at = w.running_config in diff --git a/src/session.mli b/src/session.mli index 5dbbaf0..fc2acf4 100644 --- a/src/session.mli +++ b/src/session.mli @@ -56,7 +56,7 @@ val list_children : world -> session_data -> string list -> string list val string_of_op : cfg_op -> string -val prepare_commit : ?dry_run:bool -> world -> Vyos1x.Config_tree.t -> string -> Commitd_client.Commit.commit_data +val prepare_commit : ?dry_run:bool -> world -> Vyos1x.Config_tree.t -> string -> int32 -> string -> string -> Commitd_client.Commit.commit_data val get_config : world -> session_data -> string -> string diff --git a/src/vycall_client.ml b/src/vycall_client.ml index 11bbe3f..515f5df 100644 --- a/src/vycall_client.ml +++ b/src/vycall_client.ml @@ -31,6 +31,9 @@ let call_to_node_data ((c: call), (nd: node_data)) = let commit_data_to_commit_proto cd = { session_id = cd.session_id; + session_pid = cd.session_pid; + sudo_user = cd.sudo_user; + user = cd.user; dry_run = cd.dry_run; atomic = cd.atomic; background = cd.background; diff --git a/src/vycall_pbt.ml b/src/vycall_pbt.ml index 715c453..89d2c67 100644 --- a/src/vycall_pbt.ml +++ b/src/vycall_pbt.ml @@ -14,6 +14,9 @@ type call = { type commit = { session_id : string; + session_pid : int32; + sudo_user : string; + user : string; dry_run : bool; atomic : bool; background : bool; @@ -43,6 +46,9 @@ let rec default_call let rec default_commit ?session_id:((session_id:string) = "") + ?session_pid:((session_pid:int32) = 0l) + ?sudo_user:((sudo_user:string) = "") + ?user:((user:string) = "") ?dry_run:((dry_run:bool) = false) ?atomic:((atomic:bool) = false) ?background:((background:bool) = false) @@ -50,6 +56,9 @@ let rec default_commit ?calls:((calls:call list) = []) () : commit = { session_id; + session_pid; + sudo_user; + user; dry_run; atomic; background; @@ -83,6 +92,9 @@ let default_call_mutable () : call_mutable = { type commit_mutable = { mutable session_id : string; + mutable session_pid : int32; + mutable sudo_user : string; + mutable user : string; mutable dry_run : bool; mutable atomic : bool; mutable background : bool; @@ -92,6 +104,9 @@ type commit_mutable = { let default_commit_mutable () : commit_mutable = { session_id = ""; + session_pid = 0l; + sudo_user = ""; + user = ""; dry_run = false; atomic = false; background = false; @@ -122,6 +137,9 @@ let rec pp_call fmt (v:call) = let rec pp_commit fmt (v:commit) = let pp_i fmt () = Pbrt.Pp.pp_record_field ~first:true "session_id" Pbrt.Pp.pp_string fmt v.session_id; + Pbrt.Pp.pp_record_field ~first:false "session_pid" Pbrt.Pp.pp_int32 fmt v.session_pid; + Pbrt.Pp.pp_record_field ~first:false "sudo_user" Pbrt.Pp.pp_string fmt v.sudo_user; + Pbrt.Pp.pp_record_field ~first:false "user" Pbrt.Pp.pp_string fmt v.user; Pbrt.Pp.pp_record_field ~first:false "dry_run" Pbrt.Pp.pp_bool fmt v.dry_run; Pbrt.Pp.pp_record_field ~first:false "atomic" Pbrt.Pp.pp_bool fmt v.atomic; Pbrt.Pp.pp_record_field ~first:false "background" Pbrt.Pp.pp_bool fmt v.background; @@ -167,21 +185,27 @@ let rec encode_pb_call (v:call) encoder = let rec encode_pb_commit (v:commit) encoder = Pbrt.Encoder.string v.session_id encoder; Pbrt.Encoder.key 1 Pbrt.Bytes encoder; + Pbrt.Encoder.int32_as_varint v.session_pid encoder; + Pbrt.Encoder.key 2 Pbrt.Varint encoder; + Pbrt.Encoder.string v.sudo_user encoder; + Pbrt.Encoder.key 3 Pbrt.Bytes encoder; + Pbrt.Encoder.string v.user encoder; + Pbrt.Encoder.key 4 Pbrt.Bytes encoder; Pbrt.Encoder.bool v.dry_run encoder; - Pbrt.Encoder.key 4 Pbrt.Varint encoder; - Pbrt.Encoder.bool v.atomic encoder; Pbrt.Encoder.key 5 Pbrt.Varint encoder; - Pbrt.Encoder.bool v.background encoder; + Pbrt.Encoder.bool v.atomic encoder; Pbrt.Encoder.key 6 Pbrt.Varint encoder; + Pbrt.Encoder.bool v.background encoder; + Pbrt.Encoder.key 7 Pbrt.Varint encoder; begin match v.init with | Some x -> Pbrt.Encoder.nested encode_pb_status x encoder; - Pbrt.Encoder.key 7 Pbrt.Bytes encoder; + Pbrt.Encoder.key 8 Pbrt.Bytes encoder; | None -> (); end; Pbrt.List_util.rev_iter_with (fun x encoder -> Pbrt.Encoder.nested encode_pb_call x encoder; - Pbrt.Encoder.key 8 Pbrt.Bytes encoder; + Pbrt.Encoder.key 9 Pbrt.Bytes encoder; ) v.calls encoder; () @@ -261,6 +285,9 @@ let rec decode_pb_commit d = let background_is_set = ref false in let atomic_is_set = ref false in let dry_run_is_set = ref false in + let user_is_set = ref false in + let sudo_user_is_set = ref false in + let session_pid_is_set = ref false in let session_id_is_set = ref false in while !continue__ do match Pbrt.Decoder.key d with @@ -272,39 +299,60 @@ let rec decode_pb_commit d = end | Some (1, pk) -> Pbrt.Decoder.unexpected_payload "Message(commit), field(1)" pk - | Some (4, Pbrt.Varint) -> begin - v.dry_run <- Pbrt.Decoder.bool d; dry_run_is_set := true; + | Some (2, Pbrt.Varint) -> begin + v.session_pid <- Pbrt.Decoder.int32_as_varint d; session_pid_is_set := true; + end + | Some (2, pk) -> + Pbrt.Decoder.unexpected_payload "Message(commit), field(2)" pk + | Some (3, Pbrt.Bytes) -> begin + v.sudo_user <- Pbrt.Decoder.string d; sudo_user_is_set := true; + end + | Some (3, pk) -> + Pbrt.Decoder.unexpected_payload "Message(commit), field(3)" pk + | Some (4, Pbrt.Bytes) -> begin + v.user <- Pbrt.Decoder.string d; user_is_set := true; end | Some (4, pk) -> Pbrt.Decoder.unexpected_payload "Message(commit), field(4)" pk | Some (5, Pbrt.Varint) -> begin - v.atomic <- Pbrt.Decoder.bool d; atomic_is_set := true; + v.dry_run <- Pbrt.Decoder.bool d; dry_run_is_set := true; end | Some (5, pk) -> Pbrt.Decoder.unexpected_payload "Message(commit), field(5)" pk | Some (6, Pbrt.Varint) -> begin - v.background <- Pbrt.Decoder.bool d; background_is_set := true; + v.atomic <- Pbrt.Decoder.bool d; atomic_is_set := true; end | Some (6, pk) -> Pbrt.Decoder.unexpected_payload "Message(commit), field(6)" pk - | Some (7, Pbrt.Bytes) -> begin - v.init <- Some (decode_pb_status (Pbrt.Decoder.nested d)); + | Some (7, Pbrt.Varint) -> begin + v.background <- Pbrt.Decoder.bool d; background_is_set := true; end | Some (7, pk) -> Pbrt.Decoder.unexpected_payload "Message(commit), field(7)" pk | Some (8, Pbrt.Bytes) -> begin - v.calls <- (decode_pb_call (Pbrt.Decoder.nested d)) :: v.calls; + v.init <- Some (decode_pb_status (Pbrt.Decoder.nested d)); end | Some (8, pk) -> Pbrt.Decoder.unexpected_payload "Message(commit), field(8)" pk + | Some (9, Pbrt.Bytes) -> begin + v.calls <- (decode_pb_call (Pbrt.Decoder.nested d)) :: v.calls; + end + | Some (9, pk) -> + Pbrt.Decoder.unexpected_payload "Message(commit), field(9)" pk | Some (_, payload_kind) -> Pbrt.Decoder.skip d payload_kind done; begin if not !background_is_set then Pbrt.Decoder.missing_field "background" end; begin if not !atomic_is_set then Pbrt.Decoder.missing_field "atomic" end; begin if not !dry_run_is_set then Pbrt.Decoder.missing_field "dry_run" end; + begin if not !user_is_set then Pbrt.Decoder.missing_field "user" end; + begin if not !sudo_user_is_set then Pbrt.Decoder.missing_field "sudo_user" end; + begin if not !session_pid_is_set then Pbrt.Decoder.missing_field "session_pid" end; begin if not !session_id_is_set then Pbrt.Decoder.missing_field "session_id" end; ({ session_id = v.session_id; + session_pid = v.session_pid; + sudo_user = v.sudo_user; + user = v.user; dry_run = v.dry_run; atomic = v.atomic; background = v.background; diff --git a/src/vycall_pbt.mli b/src/vycall_pbt.mli index f389302..fd39a0c 100644 --- a/src/vycall_pbt.mli +++ b/src/vycall_pbt.mli @@ -21,6 +21,9 @@ type call = { type commit = { session_id : string; + session_pid : int32; + sudo_user : string; + user : string; dry_run : bool; atomic : bool; background : bool; @@ -49,6 +52,9 @@ val default_call : val default_commit : ?session_id:string -> + ?session_pid:int32 -> + ?sudo_user:string -> + ?user:string -> ?dry_run:bool -> ?atomic:bool -> ?background:bool -> diff --git a/src/vyconfd.ml b/src/vyconfd.ml index 0a0d246..707ce4b 100644 --- a/src/vyconfd.ml +++ b/src/vyconfd.ml @@ -245,8 +245,15 @@ let commit world token (req: request_commit) = let s = find_session token in let proposed_config = Session.get_proposed_config world s in let req_dry_run = Option.value req.dry_run ~default:false in - - let commit_data = Session.prepare_commit ~dry_run:req_dry_run world proposed_config token + let commit_data = + Session.prepare_commit + ~dry_run:req_dry_run + world + proposed_config + token + s.client_pid + s.client_sudo_user + s.client_user in let%lwt received_commit_data = VC.do_commit commit_data in let%lwt result_commit_data = -- cgit v1.2.3 From 6577d7ac1b10557b5087bc959dad3c21971a539b Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Wed, 10 Sep 2025 15:45:56 -0500 Subject: T7737: add requests to provide auxiliary set/delete operations on config Occasionally, it is useful to update the config tree _during a commit_ with non-actionable data (meaning data not required for the actions of currently invoked config_mode scripts): the classic example is that of removing the entries of plaintext passwords and adding the encrypted entry. Add the relevent requests and session record field. --- data/vyconf.proto | 14 +++++ src/session.ml | 60 ++++++++++++++++++ src/session.mli | 13 ++++ src/vyconf_pbt.ml | 180 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/vyconf_pbt.mli | 48 ++++++++++++++ src/vyconfd.ml | 33 +++++++++- 6 files changed, 347 insertions(+), 1 deletion(-) (limited to 'src/session.mli') diff --git a/data/vyconf.proto b/data/vyconf.proto index 5323856..0cd2883 100644 --- a/data/vyconf.proto +++ b/data/vyconf.proto @@ -49,6 +49,18 @@ message Request { repeated string path = 1; } + message AuxSet { + repeated string path = 1; + required string script_name = 2; + optional string tag_value = 3; + } + + message AuxDelete { + repeated string path = 1; + required string script_name = 2; + optional string tag_value = 3; + } + message Discard { optional int32 dummy = 1; } @@ -179,6 +191,8 @@ message Request { SessionOfPid session_of_pid = 27; SessionExists session_exists = 28; GetConfig get_config = 29; + AuxSet aux_set = 30; + AuxDelete aux_delete = 31; } } diff --git a/src/session.ml b/src/session.ml index 5fd20b5..4829d76 100644 --- a/src/session.ml +++ b/src/session.ml @@ -3,6 +3,7 @@ module IC = Vyos1x.Internal.Make(CT) module CC = Commitd_client.Commit module CD = Vyos1x.Config_diff module VT = Vyos1x.Vytree +module VL = Vyos1x.Vylist module RT = Vyos1x.Reference_tree module D = Directories module FP = FilePath @@ -12,6 +13,7 @@ exception Session_error of string type cfg_op = | CfgSet of string list * string option * CT.value_behaviour | CfgDelete of string list * string option + [@@deriving yojson] type world = { mutable running_config: CT.t; @@ -20,11 +22,19 @@ type world = { dirs: Directories.t } +type aux_op = { + script_name: string; + tag_value: string option; + changeset: cfg_op list; +} [@@deriving yojson] + + type session_data = { proposed_config : CT.t; modified: bool; conf_mode: bool; changeset: cfg_op list; + mutable aux_changeset: aux_op list; client_app: string; client_pid: int32; client_user: string; @@ -36,6 +46,7 @@ let make world client_app sudo_user user pid = { modified = false; conf_mode = false; changeset = []; + aux_changeset = []; client_app = client_app; client_user = user; client_sudo_user = sudo_user; @@ -55,6 +66,10 @@ let string_of_op op = | None -> Printf.sprintf "delete %s" path_str | Some v -> Printf.sprintf "delete %s \"%s\"" path_str v) +let sprint_changeset ss = + let ss = List.map (fun x -> aux_op_to_yojson x) ss in + Yojson.Safe.to_string (`List ss) + let set_modified s = if s.modified = true then s else {s with modified = true} @@ -144,6 +159,51 @@ let delete w s path = let changeset' = update_delete w s.changeset path in { s with changeset = changeset' } +let aux_set w s path name tagval = + let _ = validate w s path in + let aux = s.aux_changeset in + let ident y = + if (y.script_name <> name || y.tag_value <> tagval) then false + else true + in + let op' = VL.find ident aux in + let changeset' = + match op' with + | None -> + update_set w [] path + | Some o -> + update_set w o.changeset path + in + let op = + { script_name = name; tag_value = tagval; changeset = changeset' } + in + let aux_changeset' = + VL.replace ~force:true ident op aux + in + s.aux_changeset <- aux_changeset' + +let aux_delete w s path name tagval = + let aux = s.aux_changeset in + let ident y = + if (y.script_name <> name || y.tag_value <> tagval) then false + else true + in + let op' = VL.find ident aux in + let changeset' = + match op' with + | None -> + update_delete w [] path + | Some o -> + update_delete w o.changeset path + in + let op = + { script_name = name; tag_value = tagval; changeset = changeset' } + in + let aux_changeset' = + VL.replace ~force:true ident op aux + in + s.aux_changeset <- aux_changeset' + let discard _w s = { s with changeset = []; } diff --git a/src/session.mli b/src/session.mli index fc2acf4..96fa4d1 100644 --- a/src/session.mli +++ b/src/session.mli @@ -9,11 +9,18 @@ type world = { dirs: Directories.t } +type aux_op = { + script_name: string; + tag_value: string option; + changeset: cfg_op list; +} [@@deriving yojson] + type session_data = { proposed_config : Vyos1x.Config_tree.t; modified: bool; conf_mode: bool; changeset: cfg_op list; + mutable aux_changeset: aux_op list; client_app: string; client_pid: int32; client_user: string; @@ -22,6 +29,8 @@ type session_data = { exception Session_error of string +val sprint_changeset : aux_op list -> string + val make : world -> string -> string -> string -> int32 -> session_data val set_modified : session_data -> session_data @@ -34,6 +43,10 @@ val set : world -> session_data -> string list -> session_data val delete : world -> session_data -> string list -> session_data +val aux_set : world -> session_data -> string list -> string -> string option -> unit + +val aux_delete : world -> session_data -> string list -> string -> string option -> unit + val get_proposed_config : world -> session_data -> Vyos1x.Config_tree.t val discard : world -> session_data -> session_data diff --git a/src/vyconf_pbt.ml b/src/vyconf_pbt.ml index a89f9a8..1fbbf75 100644 --- a/src/vyconf_pbt.ml +++ b/src/vyconf_pbt.ml @@ -47,6 +47,18 @@ type request_delete = { path : string list; } +type request_aux_set = { + path : string list; + script_name : string; + tag_value : string option; +} + +type request_aux_delete = { + path : string list; + script_name : string; + tag_value : string option; +} + type request_discard = { dummy : int32 option; } @@ -172,6 +184,8 @@ type request = | Session_of_pid of request_session_of_pid | Session_exists of request_session_exists | Get_config of request_get_config + | Aux_set of request_aux_set + | Aux_delete of request_aux_delete type request_envelope = { token : string option; @@ -261,6 +275,26 @@ let rec default_request_delete path; } +let rec default_request_aux_set + ?path:((path:string list) = []) + ?script_name:((script_name:string) = "") + ?tag_value:((tag_value:string option) = None) + () : request_aux_set = { + path; + script_name; + tag_value; +} + +let rec default_request_aux_delete + ?path:((path:string list) = []) + ?script_name:((script_name:string) = "") + ?tag_value:((tag_value:string option) = None) + () : request_aux_delete = { + path; + script_name; + tag_value; +} + let rec default_request_discard ?dummy:((dummy:int32 option) = None) () : request_discard = { @@ -509,6 +543,30 @@ let default_request_delete_mutable () : request_delete_mutable = { path = []; } +type request_aux_set_mutable = { + mutable path : string list; + mutable script_name : string; + mutable tag_value : string option; +} + +let default_request_aux_set_mutable () : request_aux_set_mutable = { + path = []; + script_name = ""; + tag_value = None; +} + +type request_aux_delete_mutable = { + mutable path : string list; + mutable script_name : string; + mutable tag_value : string option; +} + +let default_request_aux_delete_mutable () : request_aux_delete_mutable = { + path = []; + script_name = ""; + tag_value = None; +} + type request_discard_mutable = { mutable dummy : int32 option; } @@ -788,6 +846,22 @@ let rec pp_request_delete fmt (v:request_delete) = in Pbrt.Pp.pp_brk pp_i fmt () +let rec pp_request_aux_set fmt (v:request_aux_set) = + let pp_i fmt () = + Pbrt.Pp.pp_record_field ~first:true "path" (Pbrt.Pp.pp_list Pbrt.Pp.pp_string) fmt v.path; + Pbrt.Pp.pp_record_field ~first:false "script_name" Pbrt.Pp.pp_string fmt v.script_name; + Pbrt.Pp.pp_record_field ~first:false "tag_value" (Pbrt.Pp.pp_option Pbrt.Pp.pp_string) fmt v.tag_value; + in + Pbrt.Pp.pp_brk pp_i fmt () + +let rec pp_request_aux_delete fmt (v:request_aux_delete) = + let pp_i fmt () = + Pbrt.Pp.pp_record_field ~first:true "path" (Pbrt.Pp.pp_list Pbrt.Pp.pp_string) fmt v.path; + Pbrt.Pp.pp_record_field ~first:false "script_name" Pbrt.Pp.pp_string fmt v.script_name; + Pbrt.Pp.pp_record_field ~first:false "tag_value" (Pbrt.Pp.pp_option Pbrt.Pp.pp_string) fmt v.tag_value; + in + Pbrt.Pp.pp_brk pp_i fmt () + let rec pp_request_discard fmt (v:request_discard) = let pp_i fmt () = Pbrt.Pp.pp_record_field ~first:true "dummy" (Pbrt.Pp.pp_option Pbrt.Pp.pp_int32) fmt v.dummy; @@ -958,6 +1032,8 @@ let rec pp_request fmt (v:request) = | Session_of_pid x -> Format.fprintf fmt "@[Session_of_pid(@,%a)@]" pp_request_session_of_pid x | Session_exists x -> Format.fprintf fmt "@[Session_exists(@,%a)@]" pp_request_session_exists x | Get_config x -> Format.fprintf fmt "@[Get_config(@,%a)@]" pp_request_get_config x + | Aux_set x -> Format.fprintf fmt "@[Aux_set(@,%a)@]" pp_request_aux_set x + | Aux_delete x -> Format.fprintf fmt "@[Aux_delete(@,%a)@]" pp_request_aux_delete x let rec pp_request_envelope fmt (v:request_envelope) = let pp_i fmt () = @@ -1093,6 +1169,36 @@ let rec encode_pb_request_delete (v:request_delete) encoder = ) v.path encoder; () +let rec encode_pb_request_aux_set (v:request_aux_set) encoder = + Pbrt.List_util.rev_iter_with (fun x encoder -> + Pbrt.Encoder.string x encoder; + Pbrt.Encoder.key 1 Pbrt.Bytes encoder; + ) v.path encoder; + Pbrt.Encoder.string v.script_name encoder; + Pbrt.Encoder.key 2 Pbrt.Bytes encoder; + begin match v.tag_value with + | Some x -> + Pbrt.Encoder.string x encoder; + Pbrt.Encoder.key 3 Pbrt.Bytes encoder; + | None -> (); + end; + () + +let rec encode_pb_request_aux_delete (v:request_aux_delete) encoder = + Pbrt.List_util.rev_iter_with (fun x encoder -> + Pbrt.Encoder.string x encoder; + Pbrt.Encoder.key 1 Pbrt.Bytes encoder; + ) v.path encoder; + Pbrt.Encoder.string v.script_name encoder; + Pbrt.Encoder.key 2 Pbrt.Bytes encoder; + begin match v.tag_value with + | Some x -> + Pbrt.Encoder.string x encoder; + Pbrt.Encoder.key 3 Pbrt.Bytes encoder; + | None -> (); + end; + () + let rec encode_pb_request_discard (v:request_discard) encoder = begin match v.dummy with | Some x -> @@ -1394,6 +1500,12 @@ let rec encode_pb_request (v:request) encoder = | Get_config x -> Pbrt.Encoder.nested encode_pb_request_get_config x encoder; Pbrt.Encoder.key 29 Pbrt.Bytes encoder; + | Aux_set x -> + Pbrt.Encoder.nested encode_pb_request_aux_set x encoder; + Pbrt.Encoder.key 30 Pbrt.Bytes encoder; + | Aux_delete x -> + Pbrt.Encoder.nested encode_pb_request_aux_delete x encoder; + Pbrt.Encoder.key 31 Pbrt.Bytes encoder; end let rec encode_pb_request_envelope (v:request_envelope) encoder = @@ -1646,6 +1758,72 @@ let rec decode_pb_request_delete d = path = v.path; } : request_delete) +let rec decode_pb_request_aux_set d = + let v = default_request_aux_set_mutable () in + let continue__= ref true in + let script_name_is_set = ref false in + while !continue__ do + match Pbrt.Decoder.key d with + | None -> ( + v.path <- List.rev v.path; + ); continue__ := false + | Some (1, Pbrt.Bytes) -> begin + v.path <- (Pbrt.Decoder.string d) :: v.path; + end + | Some (1, pk) -> + Pbrt.Decoder.unexpected_payload "Message(request_aux_set), field(1)" pk + | Some (2, Pbrt.Bytes) -> begin + v.script_name <- Pbrt.Decoder.string d; script_name_is_set := true; + end + | Some (2, pk) -> + Pbrt.Decoder.unexpected_payload "Message(request_aux_set), field(2)" pk + | Some (3, Pbrt.Bytes) -> begin + v.tag_value <- Some (Pbrt.Decoder.string d); + end + | Some (3, pk) -> + Pbrt.Decoder.unexpected_payload "Message(request_aux_set), field(3)" pk + | Some (_, payload_kind) -> Pbrt.Decoder.skip d payload_kind + done; + begin if not !script_name_is_set then Pbrt.Decoder.missing_field "script_name" end; + ({ + path = v.path; + script_name = v.script_name; + tag_value = v.tag_value; + } : request_aux_set) + +let rec decode_pb_request_aux_delete d = + let v = default_request_aux_delete_mutable () in + let continue__= ref true in + let script_name_is_set = ref false in + while !continue__ do + match Pbrt.Decoder.key d with + | None -> ( + v.path <- List.rev v.path; + ); continue__ := false + | Some (1, Pbrt.Bytes) -> begin + v.path <- (Pbrt.Decoder.string d) :: v.path; + end + | Some (1, pk) -> + Pbrt.Decoder.unexpected_payload "Message(request_aux_delete), field(1)" pk + | Some (2, Pbrt.Bytes) -> begin + v.script_name <- Pbrt.Decoder.string d; script_name_is_set := true; + end + | Some (2, pk) -> + Pbrt.Decoder.unexpected_payload "Message(request_aux_delete), field(2)" pk + | Some (3, Pbrt.Bytes) -> begin + v.tag_value <- Some (Pbrt.Decoder.string d); + end + | Some (3, pk) -> + Pbrt.Decoder.unexpected_payload "Message(request_aux_delete), field(3)" pk + | Some (_, payload_kind) -> Pbrt.Decoder.skip d payload_kind + done; + begin if not !script_name_is_set then Pbrt.Decoder.missing_field "script_name" end; + ({ + path = v.path; + script_name = v.script_name; + tag_value = v.tag_value; + } : request_aux_delete) + let rec decode_pb_request_discard d = let v = default_request_discard_mutable () in let continue__= ref true in @@ -2173,6 +2351,8 @@ let rec decode_pb_request d = | Some (27, _) -> (Session_of_pid (decode_pb_request_session_of_pid (Pbrt.Decoder.nested d)) : request) | Some (28, _) -> (Session_exists (decode_pb_request_session_exists (Pbrt.Decoder.nested d)) : request) | Some (29, _) -> (Get_config (decode_pb_request_get_config (Pbrt.Decoder.nested d)) : request) + | Some (30, _) -> (Aux_set (decode_pb_request_aux_set (Pbrt.Decoder.nested d)) : request) + | Some (31, _) -> (Aux_delete (decode_pb_request_aux_delete (Pbrt.Decoder.nested d)) : request) | Some (n, payload_kind) -> ( Pbrt.Decoder.skip d payload_kind; loop () diff --git a/src/vyconf_pbt.mli b/src/vyconf_pbt.mli index 2755f61..d63962e 100644 --- a/src/vyconf_pbt.mli +++ b/src/vyconf_pbt.mli @@ -54,6 +54,18 @@ type request_delete = { path : string list; } +type request_aux_set = { + path : string list; + script_name : string; + tag_value : string option; +} + +type request_aux_delete = { + path : string list; + script_name : string; + tag_value : string option; +} + type request_discard = { dummy : int32 option; } @@ -179,6 +191,8 @@ type request = | Session_of_pid of request_session_of_pid | Session_exists of request_session_exists | Get_config of request_get_config + | Aux_set of request_aux_set + | Aux_delete of request_aux_delete type request_envelope = { token : string option; @@ -269,6 +283,22 @@ val default_request_delete : request_delete (** [default_request_delete ()] is the default value for type [request_delete] *) +val default_request_aux_set : + ?path:string list -> + ?script_name:string -> + ?tag_value:string option -> + unit -> + request_aux_set +(** [default_request_aux_set ()] is the default value for type [request_aux_set] *) + +val default_request_aux_delete : + ?path:string list -> + ?script_name:string -> + ?tag_value:string option -> + unit -> + request_aux_delete +(** [default_request_aux_delete ()] is the default value for type [request_aux_delete] *) + val default_request_discard : ?dummy:int32 option -> unit -> @@ -460,6 +490,12 @@ val pp_request_set : Format.formatter -> request_set -> unit val pp_request_delete : Format.formatter -> request_delete -> unit (** [pp_request_delete v] formats v *) +val pp_request_aux_set : Format.formatter -> request_aux_set -> unit +(** [pp_request_aux_set v] formats v *) + +val pp_request_aux_delete : Format.formatter -> request_aux_delete -> unit +(** [pp_request_aux_delete v] formats v *) + val pp_request_discard : Format.formatter -> request_discard -> unit (** [pp_request_discard v] formats v *) @@ -568,6 +604,12 @@ val encode_pb_request_set : request_set -> Pbrt.Encoder.t -> unit val encode_pb_request_delete : request_delete -> Pbrt.Encoder.t -> unit (** [encode_pb_request_delete v encoder] encodes [v] with the given [encoder] *) +val encode_pb_request_aux_set : request_aux_set -> Pbrt.Encoder.t -> unit +(** [encode_pb_request_aux_set v encoder] encodes [v] with the given [encoder] *) + +val encode_pb_request_aux_delete : request_aux_delete -> Pbrt.Encoder.t -> unit +(** [encode_pb_request_aux_delete v encoder] encodes [v] with the given [encoder] *) + val encode_pb_request_discard : request_discard -> Pbrt.Encoder.t -> unit (** [encode_pb_request_discard v encoder] encodes [v] with the given [encoder] *) @@ -676,6 +718,12 @@ val decode_pb_request_set : Pbrt.Decoder.t -> request_set val decode_pb_request_delete : Pbrt.Decoder.t -> request_delete (** [decode_pb_request_delete decoder] decodes a [request_delete] binary value from [decoder] *) +val decode_pb_request_aux_set : Pbrt.Decoder.t -> request_aux_set +(** [decode_pb_request_aux_set decoder] decodes a [request_aux_set] binary value from [decoder] *) + +val decode_pb_request_aux_delete : Pbrt.Decoder.t -> request_aux_delete +(** [decode_pb_request_aux_delete decoder] decodes a [request_aux_delete] binary value from [decoder] *) + val decode_pb_request_discard : Pbrt.Decoder.t -> request_discard (** [decode_pb_request_discard decoder] decodes a [request_discard] binary value from [decoder] *) diff --git a/src/vyconfd.ml b/src/vyconfd.ml index 707ce4b..9e8c57e 100644 --- a/src/vyconfd.ml +++ b/src/vyconfd.ml @@ -210,6 +210,34 @@ let delete world token (req: request_delete) = response_tmpl with Session.Session_error msg -> {response_tmpl with status=Fail; error=(Some msg)} +let aux_set world token (req: request_aux_set) = + try + let () = (Lwt_log.debug @@ Printf.sprintf "[%s]\n" (Vyos1x.Util.string_of_list req.path)) |> Lwt.ignore_result in + let () = + Session.aux_set + world + (find_session token) + req.path + req.script_name + req.tag_value + in + response_tmpl + with Session.Session_error msg -> {response_tmpl with status=Fail; error=(Some msg)} + +let aux_delete world token (req: request_aux_delete) = + try + let () = (Lwt_log.debug @@ Printf.sprintf "[%s]\n" (Vyos1x.Util.string_of_list req.path)) |> Lwt.ignore_result in + let () = + Session.aux_delete + world + (find_session token) + req.path + req.script_name + req.tag_value + in + response_tmpl + with Session.Session_error msg -> {response_tmpl with status=Fail; error=(Some msg)} + let discard world token (_req: request_discard) = try let session = Session.discard world (find_session token) @@ -279,7 +307,8 @@ let commit world token (req: request_commit) = Session.get_changeset world world.Session.running_config - proposed_config } + proposed_config; + aux_changeset = []; } in Hashtbl.replace sessions token session; let success, msg_str = @@ -342,6 +371,8 @@ let rec handle_connection world ic oc () = | Some t, Validate r -> validate world t r | Some t, Set r -> set world t r | Some t, Delete r -> delete world t r + | Some t, Aux_set r -> aux_set world t r + | Some t, Aux_delete r -> aux_delete world t r | Some t, Discard r -> discard world t r | Some t, Session_changed r -> session_changed world t r | Some t, Get_config r -> get_config world t r -- cgit v1.2.3 From bca62a7bd1da4e78497fdce3882421609ce28c00 Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Wed, 10 Sep 2025 19:13:21 -0500 Subject: T7737: apply auxiliary set/delete operations to config post-commit --- src/session.ml | 26 ++++++++++++++++++++++++++ src/session.mli | 2 ++ src/vyconfd.ml | 14 ++++++++++---- 3 files changed, 38 insertions(+), 4 deletions(-) (limited to 'src/session.mli') diff --git a/src/session.ml b/src/session.ml index 4829d76..5f70feb 100644 --- a/src/session.ml +++ b/src/session.ml @@ -269,6 +269,32 @@ let prepare_commit ?(dry_run=false) w config id pid sudo_user user = in CC.make_commit_data ~dry_run:dry_run rt at config id pid sudo_user user +let post_process_commit w s ((c_data: CC.commit_data), proposed_config) = + let ident n v y = + if (y.script_name <> n || y.tag_value <> v) then false + else true + in + let func (running, proposed) (n_data: CC.node_data) = + match n_data.reply with + | None -> (running, proposed) + | Some reply -> + match reply.success with + | false -> (running, proposed) + | true -> + begin + let post = + VL.find + (ident n_data.script_name n_data.tag_value) + s.aux_changeset + in + match post with + | None -> (running, proposed) + | Some p -> + (apply_changes w p.changeset running, apply_changes w p.changeset proposed) + end + in + List.fold_left func (c_data.config_result, proposed_config) c_data.node_list + let get_config w s id = let at = w.running_config in let wt = get_proposed_config w s in diff --git a/src/session.mli b/src/session.mli index 96fa4d1..a08f873 100644 --- a/src/session.mli +++ b/src/session.mli @@ -71,6 +71,8 @@ val string_of_op : cfg_op -> string val prepare_commit : ?dry_run:bool -> world -> Vyos1x.Config_tree.t -> string -> int32 -> string -> string -> Commitd_client.Commit.commit_data +val post_process_commit : world -> session_data -> Commitd_client.Commit.commit_data * Vyos1x.Config_tree.t -> Vyos1x.Config_tree.t * Vyos1x.Config_tree.t + val get_config : world -> session_data -> string -> string val cleanup_config : world -> string -> unit diff --git a/src/vyconfd.ml b/src/vyconfd.ml index 9e8c57e..05cd693 100644 --- a/src/vyconfd.ml +++ b/src/vyconfd.ml @@ -295,21 +295,27 @@ let commit world token (req: request_commit) = let res, out = init_data.success, init_data.out in + let () = + (Lwt_log.debug @@ Printf.sprintf "aux_changeset: %s" (Session.sprint_changeset s.aux_changeset)) |> Lwt.ignore_result + in match res with | false -> Lwt.return {response_tmpl with status=Internal_error; error=(Some out)} | true -> - (* partial commit *) if not req_dry_run then - world.Session.running_config <- result_commit_data.config_result; + let post_running, post_proposed = + Session.post_process_commit world s (result_commit_data, proposed_config) + in + world.Session.running_config <- post_running; let session = { s with changeset = Session.get_changeset world world.Session.running_config - proposed_config; + post_proposed; aux_changeset = []; } - in Hashtbl.replace sessions token session; + in Hashtbl.replace sessions token session + else (); let success, msg_str = result_commit_data.result.success, result_commit_data.result.out -- cgit v1.2.3