summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJohn Estabrook <jestabro@vyos.io>2026-03-04 08:47:29 -0600
committerGitHub <noreply@github.com>2026-03-04 08:47:29 -0600
commite25b13ae3040d02326f01bf9bedd097795fb3a62 (patch)
tree6e7ab4f1a6f8ea98f16503a07a34f138f295e193 /src
parent2c16a5ad7171362d71ff241adab873fa3752b784 (diff)
parent3147fb1498070eccc733e3e3ff70281e5d8a8064 (diff)
downloadvyconf-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.ml50
-rw-r--r--src/session.mli4
-rw-r--r--src/vyconf_cli.ml8
-rw-r--r--src/vyconf_client.ml22
-rw-r--r--src/vyconf_client.mli4
-rw-r--r--src/vyconf_pbt.ml154
-rw-r--r--src/vyconf_pbt.mli52
-rw-r--r--src/vyconfd.ml18
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