Add 'cmdline' template option.
[mclu.git] / template.ml
1 (* mclu: Mini Cloud
2  * Copyright (C) 2014-2015 Red Hat Inc.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  *)
18
19 (* Templates. *)
20
21 open Utils
22
23 open Printf
24
25 let template_dir =
26   try Sys.getenv "MCLU_PATH"
27   with Not_found -> Config.pkgdatadir // "templates"
28
29 let templates () =
30   let files = Sys.readdir template_dir in
31   let files = Array.to_list files in
32   let files = List.map ((//) template_dir) files in
33   let files =
34     List.filter (fun name -> Filename.check_suffix name ".template") files in
35   let templates =
36     List.map (
37       fun filename ->
38         let name = Filename.basename filename in
39         let name = Filename.chop_suffix name ".template" in
40         (name, filename)
41     ) files in
42   List.sort compare templates
43
44 let template_names () = List.map fst (templates ())
45
46 let run_template ~verbose filename subcmd args =
47   let cmd =
48     sprintf "%s %s %s" (quote filename) (quote subcmd)
49       (String.concat " " (List.map quote args)) in
50   if verbose then printf "%s\n%!" cmd;
51   let chan = Unix.open_process_in cmd in
52   let lines = ref [] in
53   (try while true do lines := input_line chan :: !lines done
54    with End_of_file -> ());
55   let lines = List.rev !lines in
56   let stat = Unix.close_process_in chan in
57   (match stat with
58   | Unix.WEXITED 0 -> Some lines
59   | Unix.WEXITED 2 -> None
60   | Unix.WEXITED i ->
61     eprintf "mclu: template '%s' subcmd '%s' exited with error %d\n"
62       filename subcmd i;
63     exit 1
64   | Unix.WSIGNALED i ->
65     eprintf "mclu: template '%s' subcmd '%s' killed by signal %d\n"
66       filename subcmd i;
67     exit 1
68   | Unix.WSTOPPED i ->
69     eprintf "mclu: template '%s' subcmd '%s' stopped by signal %d\n"
70       filename subcmd i;
71     exit 1
72   )
73
74 type template_info = {
75   base_image : string;
76   guest_arch : string option;
77   minimum_memory : int64 option;
78   recommended_memory : int64 option;
79   minimum_size : int64 option;
80   disk_bus : string option;
81   network_model : string option;
82   has_xml_target : bool;
83   needs_external_kernel : bool;
84   cmdline : string option;
85 }
86
87 let probe ?(verbose = false) filename =
88   (* Check the template is a template. *)
89   (match run_template ~verbose filename "probe" [] with
90   | Some ["hello"] -> ()
91   | _ ->
92     eprintf "mclu: file %s is not an mclu template\n" filename;
93     exit 1
94   );
95
96   (* Probe for various properties. *)
97   let base_image =
98     match run_template ~verbose filename "base-image" [] with
99     | Some [answer] -> answer
100     | _ ->
101       eprintf "mclu: cannot parse '%s base-image'\n" filename;
102       exit 1 in
103   let guest_arch =
104     match run_template ~verbose filename "guest-arch" [] with
105     | Some [arch] -> Some arch
106     | _ -> None in
107   let minimum_memory =
108     match run_template ~verbose filename "minimum-memory" [] with
109     | Some [memory] ->
110       (try Some (bytes_of_human_size memory)
111        with Not_found ->
112          eprintf "mclu: cannot parse output of '%s minimum-memory'\n"
113            filename;
114          exit 1
115       );
116     | _ -> None in
117   let recommended_memory =
118     match run_template ~verbose filename "recommended-memory" [] with
119     | Some [memory] ->
120       (try Some (bytes_of_human_size memory)
121        with Not_found ->
122          eprintf "mclu: cannot parse output of '%s recommended-memory'\n"
123            filename;
124          exit 1
125       );
126     | _ -> None in
127   let minimum_size =
128     match run_template ~verbose filename "minimum-size" [] with
129     | Some [size] ->
130       (try Some (bytes_of_human_size size)
131        with Not_found ->
132          eprintf "mclu: cannot parse output of '%s minimum-size'\n"
133            filename;
134          exit 1
135       );
136     | _ -> None in
137   let disk_bus =
138     match run_template ~verbose filename "disk-bus" [] with
139     | Some [answer] -> Some answer
140     | _ -> None in
141   let network_model =
142     match run_template ~verbose filename "network-model" [] with
143     | Some [answer] -> Some answer
144     | _ -> None in
145
146   let has_xml_target = run_template ~verbose filename "xml" [] <> None in
147
148   let needs_external_kernel =
149     match run_template ~verbose filename "needs-external-kernel" [] with
150     | None -> false
151     | Some ["1"|"yes"] -> true
152     | Some _ -> false in
153
154   let cmdline =
155     match run_template ~verbose filename "cmdline" [] with
156     | Some [cmdline] -> Some cmdline
157     | _ -> None in
158
159   if not needs_external_kernel && cmdline <> None then (
160     eprintf "mclu: template cannot set 'cmdline' unless 'needs-external-kernel' is 'yes'.\n";
161     exit 1
162   );
163
164   { base_image = base_image;
165     guest_arch = guest_arch;
166     minimum_memory = minimum_memory;
167     recommended_memory = recommended_memory;
168     minimum_size = minimum_size;
169     disk_bus = disk_bus;
170     network_model = network_model;
171     has_xml_target = has_xml_target;
172     needs_external_kernel = needs_external_kernel;
173     cmdline = cmdline }