summaryrefslogtreecommitdiff
path: root/src/commit.ml
blob: a2b9f6e0d9fc7e19e2451bb87ace6fee297c5c3c (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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
module VT = Vyos1x.Vytree
module CT = Vyos1x.Config_tree
module CD = Vyos1x.Config_diff
module RT = Vyos1x.Reference_tree
module FP = FilePath

type tree_source = DELETE | ADD

let tree_source_to_yojson = function
    | DELETE -> `String "DELETE"
    | ADD -> `String "ADD"

type status = {
  success : bool;
  out : string;
} [@@deriving to_yojson]

type node_data = {
    script_name: string;
    priority: int;
    tag_value: string option;
    arg_value: string option;
    path: string list;
    source: tree_source;
    reply: status option;
} [@@deriving to_yojson]


let default_node_data = {
    script_name = "";
    priority = 0;
    tag_value = None;
    arg_value = None;
    path = [];
    source = ADD;
    reply = Some { success = false; out = ""; };
}

type commit_data = {
    session_id: string;
    named_active : string option;
    named_proposed : string option;
    dry_run: bool;
    atomic: bool;
    background: bool;
    init: status option;
    node_list: node_data list;
} [@@deriving to_yojson]

let default_commit_data = {
    session_id = "";
    named_active = None;
    named_proposed = None;
    dry_run = false;
    atomic = false;
    background = false;
    init = Some { success = false; out = ""; };
    node_list = [];
}

let lex_order c1 c2 =
    let c = Vyos1x.Util.lex_order c1.path c2.path in
    match c with
    | 0 ->
        begin
            match c1.tag_value, c2.tag_value with
            | Some t1, Some t2 -> Vyos1x.Util.lexical_numeric_compare t1 t2
            | _ -> 0
        end
    | _ as a -> a

module CI = struct
    type t = node_data
    let compare a b =
        match compare a.priority b.priority with
        | 0 -> lex_order a b
        | _ as c -> c
end
module CS = Set.Make(CI)

let owner_args_from_data p o =
    let oa = Pcre.split o in
    let owner = FilePath.basename (List.nth oa 0) in
    if List.length oa < 2 then owner, None
    else
    let var = List.nth oa 1 in
    let res = Pcre.extract_all ~pat:"\\.\\./" var in
    let var_pos = Array.length res in
    let arg_value = Vyos1x.Util.get_last_n p var_pos
    in owner, arg_value

let add_tag_instance cd cs tv =
    CS.add { cd with tag_value = Some tv; } cs

let get_node_data rt ct src (path, cs') t =
    if Vyos1x.Util.is_empty path then
        (path, cs')
    else
    if (VT.name_of_node t) = "" then
        (path, cs')
    else
    let rpath = List.rev path in
    (* the following is critical to avoid redundant calculations for owner
       of a tag node, quadratic in the number of tag node values *)
    if CT.is_tag_value ct rpath then
        (path, cs')
    else
    let rt_path = RT.refpath rt rpath in
    let priority =
        match RT.get_priority rt rt_path with
        | None -> 0
        | Some s -> int_of_string s
    in
    let owner = RT.get_owner rt rt_path in
    match owner with
    | None -> (path, cs')
    | Some owner_str ->
    let (own, arg) = owner_args_from_data rpath owner_str in
    let c_data = { default_node_data with
                   script_name = own;
                   priority = priority;
                   arg_value = arg;
                   path = rpath;
                   source = src; }
    in
    let tag_values =
        match RT.is_tag rt rt_path with
        | false -> []
        | true -> VT.list_children t
    in
    let cs =
        match tag_values with
        | [] -> CS.add c_data cs'
        | _ -> List.fold_left (add_tag_instance c_data) cs' tag_values
    in (path, cs)

let get_commit_set rt ct src =
    snd (VT.fold_tree_with_path (get_node_data rt ct src) ([], CS.empty) ct)

(* for initial consistency with the legacy ordering of delete and add
   queues, enforce the following subtlety: if a path in the delete tree is
   an owner node, or the tag value thereof, insert in the delete queue; if
   the path is in a subtree, however, insert in the add queue - cf. T5492
*)
let legacy_order del_t a b =
    let shift c_data (c_del, c_add) =
        let path =
            match c_data.tag_value with
            | None -> c_data.path
            | Some v -> c_data.path @ [v]
        in
        match VT.is_terminal_path del_t path with
        | false -> CS.remove c_data c_del, CS.add c_data c_add
        | true -> c_del, c_add
    in
    CS.fold shift a (a, b)

let calculate_priority_lists rt at wt =
    let diff = CD.diff_tree [] at wt in
    let del_tree = CD.get_tagged_delete_tree diff in
    let add_tree = CT.get_subtree diff ["add"] in
    let cs_del' = get_commit_set rt del_tree DELETE in
    let cs_add' = get_commit_set rt add_tree ADD in
    let cs_del, cs_add = legacy_order del_tree cs_del' cs_add' in
    List.rev (CS.elements cs_del), CS.elements cs_add

let commit_store c_data =
    let out =
        let func acc nd =
            match nd.reply with
            | None -> acc ^ "\n"
            | Some r ->
                match r.success with
                | true -> acc ^ "\n"
                | false -> acc ^ "\n" ^ r.out
        in List.fold_left func "" c_data.node_list
    in print_endline out

let show_commit_data at wt =
    let vc =
        Startup.load_daemon_config Defaults.defaults.config_file in
    let rt_opt =
        Startup.read_reference_tree (FP.concat vc.reftree_dir vc.reference_tree)
    in
    match rt_opt with
    | Error msg -> msg
    | Ok rt ->
        let del_list, add_list =
            calculate_priority_lists rt at wt
        in
        let sprint_node_data acc s =
            acc ^ "\n" ^ (node_data_to_yojson s |> Yojson.Safe.to_string)
        in
        let del_out = List.fold_left sprint_node_data "" del_list in
        let add_out = List.fold_left sprint_node_data "" add_list in
        del_out ^ "\n" ^ add_out