summaryrefslogtreecommitdiff
path: root/src/vycall_client.ml
blob: c7fc8115989f50c7aea95f4ed0559b8cf462340e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
(* send commit data to Python commit daemon *)

open Vycall_message.Vycall_pbt
open Vyconfd_config.Commit

module CT = Vyos1x.Config_tree
module IC = Vyos1x.Internal.Make(CT)
module ST = Vyconfd_config.Startup
module DF = Vyconfd_config.Defaults
module FP = FilePath

type t = {
    ic: Lwt_io.input Lwt_io.channel;
    oc: Lwt_io.output Lwt_io.channel;
}

(* explicit translation between commit data and commit protobuf
 * to keep the commit data opaque to protobuf message definition.
 * The commit daemon updates the (subset of) commit data with
 * results of script execution in init/reply fields.
 *)
let node_data_to_call nd =
    { script_name = nd.script_name;
      tag_value = nd.tag_value;
      arg_value = nd.arg_value;
      reply = None
    }

let call_to_node_data ((c: call), (nd: node_data)) =
    match c.reply with
    | None -> nd
    | Some r -> { nd with reply = Some { success = r.success; out = r.out }}

let commit_data_to_commit_proto cd =
    { session_id = cd.session_id;
      named_active = cd.named_active;
      named_proposed = cd.named_proposed;
      dry_run = cd.dry_run;
      atomic = cd.atomic;
      background = cd.background;
      init = None;
      calls = List.map node_data_to_call cd.node_list;
    }

let commit_proto_to_commit_data (c: commit) (cd: commit_data) =
    match c.init with
    | None -> cd
    | Some i ->
        { cd with init = Some { success = i.success; out = i.out };
          node_list =
              List.map call_to_node_data (List.combine c.calls cd.node_list);
        }

(* read/write message from/to socket *)
let call_write oc msg =
    let length = Bytes.length msg in
    let length' = Int32.of_int length in
    if length' < 0l then failwith (Printf.sprintf "Bad message length: %d" length) else
    let header = Bytes.create 4 in
    let () = EndianBytes.BigEndian.set_int32 header 0 length' in
    let%lwt () = Lwt_io.write_from_exactly oc header 0 4 in
    Lwt_io.write_from_exactly oc msg 0 length

let call_read ic =
    let header = Bytes.create 4 in
    let%lwt () = Lwt_io.read_into_exactly ic header 0 4 in
    let length = EndianBytes.BigEndian.get_int32 header 0 |> Int32.to_int in
    if length < 0 then failwith (Printf.sprintf "Bad message length: %d" length) else
    let buffer = Bytes.create length in
    let%lwt () = Lwt_io.read_into_exactly ic buffer 0 length in
    Lwt.return buffer

(* encode/decode commit data *)
let do_call client request =
    let enc = Pbrt.Encoder.create () in
    let () = encode_pb_commit request enc in
    let msg = Pbrt.Encoder.to_bytes enc in
    let%lwt () = call_write client.oc msg in
    let%lwt resp = call_read client.ic in
    decode_pb_commit (Pbrt.Decoder.of_bytes resp) |> Lwt.return

(* socket management and commit callback *)
let create sockfile =
    let open Lwt_unix in
    let sock = socket PF_UNIX SOCK_STREAM 0 in
    let%lwt () = connect sock (ADDR_UNIX sockfile) in
    let ic = Lwt_io.of_fd ~mode:Lwt_io.Input sock in
    let oc = Lwt_io.of_fd ~mode:Lwt_io.Output sock in
    Lwt.return { ic=ic; oc=oc; }

let update session_data =
    Lwt.return (commit_store session_data)

let do_commit session_data =
    let session = commit_data_to_commit_proto session_data in
    let run () =
        let sockfile = "/run/vyos-commitd.sock" in
        let%lwt client = create sockfile in
        let%lwt resp = do_call client session in
        let%lwt () = Lwt_io.close client.oc in
        update (commit_proto_to_commit_data resp session_data)
    in Lwt_main.run @@ run ()

(* test function *)
let test_commit at wt =
    let vc =
        ST.load_daemon_config DF.defaults.config_file in
    let () =
        IC.write_internal at (FP.concat vc.session_dir vc.running_cache) in
    let () =
        IC.write_internal wt (FP.concat vc.session_dir vc.session_cache) in
    let rt_opt =
        ST.read_reference_tree (FP.concat vc.reftree_dir vc.reference_tree)
    in
    match rt_opt with
    | Error msg -> print_endline msg
    | Ok rt ->
        let del_list, add_list =
            calculate_priority_lists rt at wt
        in
        let commit_session =
            { default_commit_data with node_list = del_list @ add_list }
        in
        do_commit commit_session