diff options
| author | John Estabrook <jestabro@vyos.io> | 2026-03-04 08:47:29 -0600 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-03-04 08:47:29 -0600 |
| commit | e25b13ae3040d02326f01bf9bedd097795fb3a62 (patch) | |
| tree | 6e7ab4f1a6f8ea98f16503a07a34f138f295e193 /src | |
| parent | 2c16a5ad7171362d71ff241adab873fa3752b784 (diff) | |
| parent | 3147fb1498070eccc733e3e3ff70281e5d8a8064 (diff) | |
| download | vyconf-e25b13ae3040d02326f01bf9bedd097795fb3a62.tar.gz vyconf-e25b13ae3040d02326f01bf9bedd097795fb3a62.zip | |
Merge pull request #43 from jestabro/copy-rename-requests
T8313: add vyconf support for copy/rename
Diffstat (limited to 'src')
| -rw-r--r-- | src/session.ml | 50 | ||||
| -rw-r--r-- | src/session.mli | 4 | ||||
| -rw-r--r-- | src/vyconf_cli.ml | 8 | ||||
| -rw-r--r-- | src/vyconf_client.ml | 22 | ||||
| -rw-r--r-- | src/vyconf_client.mli | 4 | ||||
| -rw-r--r-- | src/vyconf_pbt.ml | 154 | ||||
| -rw-r--r-- | src/vyconf_pbt.mli | 52 | ||||
| -rw-r--r-- | src/vyconfd.ml | 18 |
8 files changed, 191 insertions, 121 deletions
diff --git a/src/session.ml b/src/session.ml index 28ccdad..7658028 100644 --- a/src/session.ml +++ b/src/session.ml @@ -267,6 +267,53 @@ let aux_delete w s path name tagval = let discard _w s = { s with changeset = []; } +let copy w s p1 p2 = + if Vyos1x.Util.is_empty p1 + then raise (Session_error "Cannot copy an empty path") + else + if Vyos1x.Util.is_empty p2 + then raise (Session_error "Cannot copy to an empty path") + else + let p1_total = s.edit_level @ p1 in + let p2_total = s.edit_level @ p2 in + let ct = get_proposed_config w s in + if not ((VT.exists[@alert "-exn"]) ct p1_total) + then + let p1_str = Vyos1x.Util.string_of_list p1_total in + let out = Printf.sprintf "Configuration path \"%s\" does not exist" p1_str in + raise (Session_error out) + else + let _ = validate w s p2_total in + let ct' = (VT.copy[@alert "-exn"]) ct p1_total p2_total in + let changeset' = get_changeset w s ct ct' in + { s with changeset = changeset' @ s.changeset } + +let rename w s p1 p2 = + if Vyos1x.Util.is_empty p1 + then raise (Session_error "Cannot rename an empty path") + else + let p2_last = + match Vyos1x.Util.get_last p2 with + | None -> raise (Session_error "Cannot rename to an empty value") + | Some v -> v + in + if (Vyos1x.Util.drop_last p1) <> (Vyos1x.Util.drop_last p2) + then raise (Session_error "Cannot rename a node: paths are inconsistent") + else + let p1_total = s.edit_level @ p1 in + let p2_total = s.edit_level @ p2 in + let ct = get_proposed_config w s in + if not ((VT.exists[@alert "-exn"]) ct p1_total) + then + let p1_str = Vyos1x.Util.string_of_list p1_total in + let out = Printf.sprintf "Configuration path \"%s\" does not exist" p1_str in + raise (Session_error out) + else + let _ = validate w s p2_total in + let ct' = (VT.rename[@alert "-exn"]) ct p1_total p2_last in + let changeset' = get_changeset w s ct ct' in + { s with changeset = changeset' @ s.changeset } + let edit_env_str s = (* To maintain consistency with classic CLI, we return env variable for PS1 on changes to edit level. @@ -405,8 +452,9 @@ let get_completion_env ?(legacy_format=false) w s path = | h :: tl -> (h, tl) in let config = get_proposed_config w s in + let path_total = s.edit_level @ path in let res = - Vyos1x.Completion.get_completion_env_str ~legacy_format w.reference_tree config op path + Vyos1x.Completion.get_completion_env_str ~legacy_format w.reference_tree config op path_total in match res with | Ok env -> env diff --git a/src/session.mli b/src/session.mli index 08df1db..cb51075 100644 --- a/src/session.mli +++ b/src/session.mli @@ -100,3 +100,7 @@ val reference_path_exists : world -> session_data -> string list -> bool val get_path_type : ?legacy_format:bool -> world -> session_data -> string list -> string val get_completion_env : ?legacy_format:bool -> world -> session_data -> string list -> string + +val copy : world -> session_data -> string list -> string list -> session_data + +val rename : world -> session_data -> string list -> string list -> session_data diff --git a/src/vyconf_cli.ml b/src/vyconf_cli.ml index 8563572..67aaa7a 100644 --- a/src/vyconf_cli.ml +++ b/src/vyconf_cli.ml @@ -5,12 +5,16 @@ type op_t = | OpSet | OpDelete | OpDiscard + | OpCopy + | OpRename let op_of_string s = match s with | "vy_set" -> OpSet | "vy_delete" -> OpDelete | "vy_discard" -> OpDiscard + | "vy_copy" -> OpCopy + | "vy_rename" -> OpRename | _ -> failwith (Printf.sprintf "Unknown operation %s" s) let config_format_of_string s = @@ -76,6 +80,8 @@ let main op path = | OpSet -> set c path | OpDelete -> delete c path | OpDiscard -> discard c + | OpCopy -> copy c path + | OpRename -> rename c path end | Error e -> Error e |> Lwt.return in @@ -109,5 +115,7 @@ let () = match op, path_list with | OpSet, [] | OpDelete, [] -> let () = print_endline "Must specify config path" in exit 1 + | OpCopy, [] | OpRename, [] -> + let () = print_endline "Copy and rename operations require configuration paths" in exit 1 | _, _ -> let result = Lwt_main.run (main op path_list) in exit result diff --git a/src/vyconf_client.ml b/src/vyconf_client.ml index 6b6c6f1..d866ad4 100644 --- a/src/vyconf_client.ml +++ b/src/vyconf_client.ml @@ -254,3 +254,25 @@ let get_completion_env client path = (* legacy getCompletionEnv is silent on error *) | Fail -> Error "" |> Lwt.return | _ -> Error (Option.value resp.error ~default:"") |> Lwt.return + +let copy client path = + let path_arr = Array.of_list path in + let path1 = Array.to_list (Array.sub path_arr 0 2) in + let path2 = Array.to_list (Array.sub path_arr 3 2) in + let req = Copy {source=path1; destination=path2;} in + let%lwt resp = do_request client req in + match resp.status with + | Success -> Lwt.return (Ok "") + | Fail -> Error (Option.value resp.error ~default:"") |> Lwt.return + | _ -> Error (Option.value resp.error ~default:"") |> Lwt.return + +let rename client path = + let path_arr = Array.of_list path in + let path1 = Array.to_list (Array.sub path_arr 0 2) in + let path2 = Array.to_list (Array.sub path_arr 3 2) in + let req = Rename {source=path1; destination=path2;} in + let%lwt resp = do_request client req in + match resp.status with + | Success -> Lwt.return (Ok "") + | Fail -> Error (Option.value resp.error ~default:"") |> Lwt.return + | _ -> Error (Option.value resp.error ~default:"") |> Lwt.return diff --git a/src/vyconf_client.mli b/src/vyconf_client.mli index 3ee1083..9c5a453 100644 --- a/src/vyconf_client.mli +++ b/src/vyconf_client.mli @@ -55,3 +55,7 @@ val reference_path_exists : t -> string list -> (string, string) result Lwt.t val get_path_type : t -> string list -> (string, string) result Lwt.t val get_completion_env : t -> string list -> (string, string) result Lwt.t + +val copy : t -> string list -> (string, string) result Lwt.t + +val rename : t -> string list -> (string, string) result Lwt.t diff --git a/src/vyconf_pbt.ml b/src/vyconf_pbt.ml index 939af0c..ce2fb2f 100644 --- a/src/vyconf_pbt.ml +++ b/src/vyconf_pbt.ml @@ -67,16 +67,14 @@ type request_session_changed = { dummy : int32 option; } -type request_rename = { - edit_level : string list; - source : string; - destination : string; +type request_copy = { + source : string list; + destination : string list; } -type request_copy = { - edit_level : string list; - source : string; - destination : string; +type request_rename = { + source : string list; + destination : string list; } type request_comment = { @@ -360,22 +358,18 @@ let rec default_request_session_changed dummy; } -let rec default_request_rename - ?edit_level:((edit_level:string list) = []) - ?source:((source:string) = "") - ?destination:((destination:string) = "") - () : request_rename = { - edit_level; +let rec default_request_copy + ?source:((source:string list) = []) + ?destination:((destination:string list) = []) + () : request_copy = { source; destination; } -let rec default_request_copy - ?edit_level:((edit_level:string list) = []) - ?source:((source:string) = "") - ?destination:((destination:string) = "") - () : request_copy = { - edit_level; +let rec default_request_rename + ?source:((source:string list) = []) + ?destination:((destination:string list) = []) + () : request_rename = { source; destination; } @@ -702,28 +696,24 @@ let default_request_session_changed_mutable () : request_session_changed_mutable dummy = None; } -type request_rename_mutable = { - mutable edit_level : string list; - mutable source : string; - mutable destination : string; +type request_copy_mutable = { + mutable source : string list; + mutable destination : string list; } -let default_request_rename_mutable () : request_rename_mutable = { - edit_level = []; - source = ""; - destination = ""; +let default_request_copy_mutable () : request_copy_mutable = { + source = []; + destination = []; } -type request_copy_mutable = { - mutable edit_level : string list; - mutable source : string; - mutable destination : string; +type request_rename_mutable = { + mutable source : string list; + mutable destination : string list; } -let default_request_copy_mutable () : request_copy_mutable = { - edit_level = []; - source = ""; - destination = ""; +let default_request_rename_mutable () : request_rename_mutable = { + source = []; + destination = []; } type request_comment_mutable = { @@ -1079,19 +1069,17 @@ let rec pp_request_session_changed fmt (v:request_session_changed) = in Pbrt.Pp.pp_brk pp_i fmt () -let rec pp_request_rename fmt (v:request_rename) = +let rec pp_request_copy fmt (v:request_copy) = let pp_i fmt () = - Pbrt.Pp.pp_record_field ~first:true "edit_level" (Pbrt.Pp.pp_list Pbrt.Pp.pp_string) fmt v.edit_level; - Pbrt.Pp.pp_record_field ~first:false "source" Pbrt.Pp.pp_string fmt v.source; - Pbrt.Pp.pp_record_field ~first:false "destination" Pbrt.Pp.pp_string fmt v.destination; + Pbrt.Pp.pp_record_field ~first:true "source" (Pbrt.Pp.pp_list Pbrt.Pp.pp_string) fmt v.source; + Pbrt.Pp.pp_record_field ~first:false "destination" (Pbrt.Pp.pp_list Pbrt.Pp.pp_string) fmt v.destination; in Pbrt.Pp.pp_brk pp_i fmt () -let rec pp_request_copy fmt (v:request_copy) = +let rec pp_request_rename fmt (v:request_rename) = let pp_i fmt () = - Pbrt.Pp.pp_record_field ~first:true "edit_level" (Pbrt.Pp.pp_list Pbrt.Pp.pp_string) fmt v.edit_level; - Pbrt.Pp.pp_record_field ~first:false "source" Pbrt.Pp.pp_string fmt v.source; - Pbrt.Pp.pp_record_field ~first:false "destination" Pbrt.Pp.pp_string fmt v.destination; + Pbrt.Pp.pp_record_field ~first:true "source" (Pbrt.Pp.pp_list Pbrt.Pp.pp_string) fmt v.source; + Pbrt.Pp.pp_record_field ~first:false "destination" (Pbrt.Pp.pp_list Pbrt.Pp.pp_string) fmt v.destination; in Pbrt.Pp.pp_brk pp_i fmt () @@ -1495,26 +1483,26 @@ let rec encode_pb_request_session_changed (v:request_session_changed) encoder = end; () -let rec encode_pb_request_rename (v:request_rename) encoder = +let rec encode_pb_request_copy (v:request_copy) encoder = Pbrt.List_util.rev_iter_with (fun x encoder -> Pbrt.Encoder.string x encoder; Pbrt.Encoder.key 1 Pbrt.Bytes encoder; - ) v.edit_level encoder; - Pbrt.Encoder.string v.source encoder; - Pbrt.Encoder.key 2 Pbrt.Bytes encoder; - Pbrt.Encoder.string v.destination encoder; - Pbrt.Encoder.key 3 Pbrt.Bytes encoder; + ) v.source encoder; + Pbrt.List_util.rev_iter_with (fun x encoder -> + Pbrt.Encoder.string x encoder; + Pbrt.Encoder.key 2 Pbrt.Bytes encoder; + ) v.destination encoder; () -let rec encode_pb_request_copy (v:request_copy) encoder = +let rec encode_pb_request_rename (v:request_rename) encoder = Pbrt.List_util.rev_iter_with (fun x encoder -> Pbrt.Encoder.string x encoder; Pbrt.Encoder.key 1 Pbrt.Bytes encoder; - ) v.edit_level encoder; - Pbrt.Encoder.string v.source encoder; - Pbrt.Encoder.key 2 Pbrt.Bytes encoder; - Pbrt.Encoder.string v.destination encoder; - Pbrt.Encoder.key 3 Pbrt.Bytes encoder; + ) v.source encoder; + Pbrt.List_util.rev_iter_with (fun x encoder -> + Pbrt.Encoder.string x encoder; + Pbrt.Encoder.key 2 Pbrt.Bytes encoder; + ) v.destination encoder; () let rec encode_pb_request_comment (v:request_comment) encoder = @@ -2252,75 +2240,57 @@ let rec decode_pb_request_session_changed d = dummy = v.dummy; } : request_session_changed) -let rec decode_pb_request_rename d = - let v = default_request_rename_mutable () in +let rec decode_pb_request_copy d = + let v = default_request_copy_mutable () in let continue__= ref true in - let destination_is_set = ref false in - let source_is_set = ref false in while !continue__ do match Pbrt.Decoder.key d with | None -> ( - v.edit_level <- List.rev v.edit_level; + v.destination <- List.rev v.destination; + v.source <- List.rev v.source; ); continue__ := false | Some (1, Pbrt.Bytes) -> begin - v.edit_level <- (Pbrt.Decoder.string d) :: v.edit_level; + v.source <- (Pbrt.Decoder.string d) :: v.source; end | Some (1, pk) -> - Pbrt.Decoder.unexpected_payload "Message(request_rename), field(1)" pk + Pbrt.Decoder.unexpected_payload "Message(request_copy), field(1)" pk | Some (2, Pbrt.Bytes) -> begin - v.source <- Pbrt.Decoder.string d; source_is_set := true; + v.destination <- (Pbrt.Decoder.string d) :: v.destination; end | Some (2, pk) -> - Pbrt.Decoder.unexpected_payload "Message(request_rename), field(2)" pk - | Some (3, Pbrt.Bytes) -> begin - v.destination <- Pbrt.Decoder.string d; destination_is_set := true; - end - | Some (3, pk) -> - Pbrt.Decoder.unexpected_payload "Message(request_rename), field(3)" pk + Pbrt.Decoder.unexpected_payload "Message(request_copy), field(2)" pk | Some (_, payload_kind) -> Pbrt.Decoder.skip d payload_kind done; - begin if not !destination_is_set then Pbrt.Decoder.missing_field "destination" end; - begin if not !source_is_set then Pbrt.Decoder.missing_field "source" end; ({ - edit_level = v.edit_level; source = v.source; destination = v.destination; - } : request_rename) + } : request_copy) -let rec decode_pb_request_copy d = - let v = default_request_copy_mutable () in +let rec decode_pb_request_rename d = + let v = default_request_rename_mutable () in let continue__= ref true in - let destination_is_set = ref false in - let source_is_set = ref false in while !continue__ do match Pbrt.Decoder.key d with | None -> ( - v.edit_level <- List.rev v.edit_level; + v.destination <- List.rev v.destination; + v.source <- List.rev v.source; ); continue__ := false | Some (1, Pbrt.Bytes) -> begin - v.edit_level <- (Pbrt.Decoder.string d) :: v.edit_level; + v.source <- (Pbrt.Decoder.string d) :: v.source; end | Some (1, pk) -> - Pbrt.Decoder.unexpected_payload "Message(request_copy), field(1)" pk + Pbrt.Decoder.unexpected_payload "Message(request_rename), field(1)" pk | Some (2, Pbrt.Bytes) -> begin - v.source <- Pbrt.Decoder.string d; source_is_set := true; + v.destination <- (Pbrt.Decoder.string d) :: v.destination; end | Some (2, pk) -> - Pbrt.Decoder.unexpected_payload "Message(request_copy), field(2)" pk - | Some (3, Pbrt.Bytes) -> begin - v.destination <- Pbrt.Decoder.string d; destination_is_set := true; - end - | Some (3, pk) -> - Pbrt.Decoder.unexpected_payload "Message(request_copy), field(3)" pk + Pbrt.Decoder.unexpected_payload "Message(request_rename), field(2)" pk | Some (_, payload_kind) -> Pbrt.Decoder.skip d payload_kind done; - begin if not !destination_is_set then Pbrt.Decoder.missing_field "destination" end; - begin if not !source_is_set then Pbrt.Decoder.missing_field "source" end; ({ - edit_level = v.edit_level; source = v.source; destination = v.destination; - } : request_copy) + } : request_rename) let rec decode_pb_request_comment d = let v = default_request_comment_mutable () in diff --git a/src/vyconf_pbt.mli b/src/vyconf_pbt.mli index 2558dc0..f78ad8c 100644 --- a/src/vyconf_pbt.mli +++ b/src/vyconf_pbt.mli @@ -74,16 +74,14 @@ type request_session_changed = { dummy : int32 option; } -type request_rename = { - edit_level : string list; - source : string; - destination : string; +type request_copy = { + source : string list; + destination : string list; } -type request_copy = { - edit_level : string list; - source : string; - destination : string; +type request_rename = { + source : string list; + destination : string list; } type request_comment = { @@ -364,22 +362,20 @@ val default_request_session_changed : request_session_changed (** [default_request_session_changed ()] is the default value for type [request_session_changed] *) -val default_request_rename : - ?edit_level:string list -> - ?source:string -> - ?destination:string -> - unit -> - request_rename -(** [default_request_rename ()] is the default value for type [request_rename] *) - val default_request_copy : - ?edit_level:string list -> - ?source:string -> - ?destination:string -> + ?source:string list -> + ?destination:string list -> unit -> request_copy (** [default_request_copy ()] is the default value for type [request_copy] *) +val default_request_rename : + ?source:string list -> + ?destination:string list -> + unit -> + request_rename +(** [default_request_rename ()] is the default value for type [request_rename] *) + val default_request_comment : ?path:string list -> ?comment:string -> @@ -618,12 +614,12 @@ val pp_request_discard : Format.formatter -> request_discard -> unit val pp_request_session_changed : Format.formatter -> request_session_changed -> unit (** [pp_request_session_changed v] formats v *) -val pp_request_rename : Format.formatter -> request_rename -> unit -(** [pp_request_rename v] formats v *) - val pp_request_copy : Format.formatter -> request_copy -> unit (** [pp_request_copy v] formats v *) +val pp_request_rename : Format.formatter -> request_rename -> unit +(** [pp_request_rename v] formats v *) + val pp_request_comment : Format.formatter -> request_comment -> unit (** [pp_request_comment v] formats v *) @@ -762,12 +758,12 @@ val encode_pb_request_discard : request_discard -> Pbrt.Encoder.t -> unit val encode_pb_request_session_changed : request_session_changed -> Pbrt.Encoder.t -> unit (** [encode_pb_request_session_changed v encoder] encodes [v] with the given [encoder] *) -val encode_pb_request_rename : request_rename -> Pbrt.Encoder.t -> unit -(** [encode_pb_request_rename v encoder] encodes [v] with the given [encoder] *) - val encode_pb_request_copy : request_copy -> Pbrt.Encoder.t -> unit (** [encode_pb_request_copy v encoder] encodes [v] with the given [encoder] *) +val encode_pb_request_rename : request_rename -> Pbrt.Encoder.t -> unit +(** [encode_pb_request_rename v encoder] encodes [v] with the given [encoder] *) + val encode_pb_request_comment : request_comment -> Pbrt.Encoder.t -> unit (** [encode_pb_request_comment v encoder] encodes [v] with the given [encoder] *) @@ -906,12 +902,12 @@ val decode_pb_request_discard : Pbrt.Decoder.t -> request_discard val decode_pb_request_session_changed : Pbrt.Decoder.t -> request_session_changed (** [decode_pb_request_session_changed decoder] decodes a [request_session_changed] binary value from [decoder] *) -val decode_pb_request_rename : Pbrt.Decoder.t -> request_rename -(** [decode_pb_request_rename decoder] decodes a [request_rename] binary value from [decoder] *) - val decode_pb_request_copy : Pbrt.Decoder.t -> request_copy (** [decode_pb_request_copy decoder] decodes a [request_copy] binary value from [decoder] *) +val decode_pb_request_rename : Pbrt.Decoder.t -> request_rename +(** [decode_pb_request_rename decoder] decodes a [request_rename] binary value from [decoder] *) + val decode_pb_request_comment : Pbrt.Decoder.t -> request_comment (** [decode_pb_request_comment decoder] decodes a [request_comment] binary value from [decoder] *) diff --git a/src/vyconfd.ml b/src/vyconfd.ml index 6029f95..45f0aad 100644 --- a/src/vyconfd.ml +++ b/src/vyconfd.ml @@ -263,6 +263,22 @@ let discard world token (_req: request_discard) = response_tmpl with Session.Session_error msg -> {response_tmpl with status=Fail; error=(Some msg)} +let copy world token (req: request_copy) = + try + let session = Session.copy world (find_session token) req.source req.destination + in + Hashtbl.replace sessions token session; + response_tmpl + with Session.Session_error msg -> {response_tmpl with status=Fail; error=(Some msg)} + +let rename world token (req: request_rename) = + try + let session = Session.rename world (find_session token) req.source req.destination + in + Hashtbl.replace sessions token session; + response_tmpl + with Session.Session_error msg -> {response_tmpl with status=Fail; error=(Some msg)} + let load world token (req: request_load) = try let session = Session.load world (find_session token) req.location req.cached @@ -505,6 +521,8 @@ let rec handle_connection world ic oc () = | Some t, Reference_path_exists r -> reference_path_exists world t r | Some t, Get_path_type r -> get_path_type world t r | Some t, Get_completion_env r -> get_completion_env world t r + | Some t, Copy r -> copy world t r + | Some t, Rename r -> rename world t r | _ -> failwith "Unimplemented" ) |> Lwt.return end |
