1 (* virsh-like command line tool.
2 (C) Copyright 2007 Richard W.M. Jones, Red Hat Inc.
9 module C = Libvirt.Connect
10 module D = Libvirt.Domain
11 module N = Libvirt.Network
14 let program_name = Filename.basename Sys.executable_name
16 (* Parse arguments. *)
18 let readonly = ref false
20 let argspec = Arg.align [
21 "-c", Arg.Set_string name, "URI Hypervisor connection URI";
22 "-r", Arg.Set readonly, " Read-only connection";
27 " ^ program_name ^ " [options] [command]
30 " ^ program_name ^ " help
32 Full description of a single command:
33 " ^ program_name ^ " help command
37 let add_extra_arg, get_extra_args =
38 let extra_args = ref [] in
39 let add_extra_arg s = extra_args := s :: !extra_args in
40 let get_extra_args () = List.rev !extra_args in
41 add_extra_arg, get_extra_args
43 let () = Arg.parse argspec add_extra_arg usage_msg
45 let name = match !name with "" -> None | name -> Some name
46 let readonly = !readonly
47 let extra_args = get_extra_args ()
49 (* Read a whole file into memory and return it (as a string). *)
50 let rec input_file filename =
51 let chan = open_in_bin filename in
52 let data = input_all chan in
56 let buf = Buffer.create 16384 in
57 let tmpsize = 16384 in
58 let tmp = String.create tmpsize in
60 while n := input chan tmp 0 tmpsize; !n > 0 do
61 Buffer.add_substring buf tmp 0 !n;
65 (* Hypervisor connection. *)
66 type conn_t = No_connection | RO of Libvirt.ro C.t | RW of Libvirt.rw C.t
67 let conn = ref No_connection
69 let close_connection () =
80 (* Command helper functions.
82 * Each cmd<n> is a function that constructs a command.
83 * string string string ... <--- user types on the command line
85 * arg1 arg2 arg3 ... <--- conversion functions
88 * function f <--- work function
91 * print result <--- printing function
93 * (Note that cmd<n> function constructs and returns the above
94 * function, it isn't the function itself.)
96 * Example: If the function takes one parameter (an int) and
97 * returns a string to be printed, you would use:
99 * cmd1 print_endline f int_of_string
101 let cmd0 print fn = function (* Command with no args. *)
102 | [] -> print (fn ())
103 | _ -> failwith "incorrect number of arguments for function"
105 let cmd1 print fn arg1 = function (* Command with one arg. *)
106 | [str1] -> print (fn (arg1 str1))
107 | _ -> failwith "incorrect number of arguments for function"
109 let cmd2 print fn arg1 arg2 = function (* Command with 2 args. *)
110 | [str1; str2] -> print (fn (arg1 str1) (arg2 str2))
111 | _ -> failwith "incorrect number of arguments for function"
113 let cmd3 print fn arg1 arg2 arg3 = function (* Command with 3 args. *)
114 | [str1; str2; str3] -> print (fn (arg1 str1) (arg2 str2) (arg3 str3))
115 | _ -> failwith "incorrect number of arguments for function"
117 let cmd01 print fn arg1 = function (* Command with 0 or 1 arg. *)
118 | [] -> print (fn None)
119 | [str1] -> print (fn (Some (arg1 str1)))
120 | _ -> failwith "incorrect number of arguments for function"
122 let cmd12 print fn arg1 arg2 = function (* Command with 1 or 2 args. *)
123 | [str1] -> print (fn (arg1 str1) None)
124 | [str1; str2] -> print (fn (arg1 str1) (Some (arg2 str2)))
125 | _ -> failwith "incorrect number of arguments for function"
127 let cmdN print fn = (* Command with any number of args. *)
128 fun args -> print (fn args)
131 (* Get the connection or fail if we don't have one. *)
132 let rec get_full_connection () =
134 | No_connection -> failwith "not connected to the hypervisor"
135 | RO _ -> failwith "tried to do read-write operation on read-only hypervisor connection"
137 and get_readonly_connection () =
139 | No_connection -> failwith "not connected to the hypervisor"
141 | RW conn -> C.const conn
143 and with_full_connection fn =
144 fun () -> fn (get_full_connection ())
146 and with_readonly_connection fn =
147 fun () -> fn (get_readonly_connection ())
148 and arg_full_connection fn =
149 fun str -> fn (get_full_connection ()) str
150 and arg_readonly_connection fn =
151 fun str -> fn (get_readonly_connection ()) str
154 (* Parsing of command arguments. *)
155 let string_of_readonly = function
156 | "readonly" | "read-only" | "ro" -> true
157 | _ -> failwith "flag should be 'readonly'"
159 let string_of_string (str : string) = str in
160 let boolean_of_string = function
161 | "enable" | "enabled" | "on" | "1" | "true" -> true
162 | "disable" | "disabled" | "off" | "0" | "false" -> false
163 | _ -> failwith "setting should be 'on' or 'off'"
165 let domain_of_string conn str =
168 let id = int_of_string str in
169 D.lookup_by_id conn id
171 Failure "int_of_string" ->
172 if String.length str = Libvirt.uuid_string_length then
173 D.lookup_by_uuid_string conn str
175 D.lookup_by_name conn str
178 Libvirt.Virterror err ->
179 failwith ("domain " ^ str ^ ": not found. Additional info: " ^
180 Libvirt.Virterror.to_string err);
182 let network_of_string conn str =
184 if String.length str = Libvirt.uuid_string_length then
185 N.lookup_by_uuid_string conn str
187 N.lookup_by_name conn str
189 Libvirt.Virterror err ->
190 failwith ("network " ^ str ^ ": not found. Additional info: " ^
191 Libvirt.Virterror.to_string err);
193 let rec parse_sched_params = function
195 | [_] -> failwith "expected field value pairs, but got an odd number of arguments"
196 | field :: value :: rest ->
197 (* XXX We only support the UINT type at the moment. *)
198 (field, D.SchedFieldUInt32 (Int32.of_string value))
199 :: parse_sched_params rest
201 let cpumap_of_string str =
202 let c = get_readonly_connection () in
203 let info = C.get_node_info c in
205 String.make (C.cpumaplen (C.maxcpus_of_node_info info)) '\000' in
206 List.iter (C.use_cpu cpumap)
207 (List.map int_of_string (String.nsplit str ","));
211 (* Printing of command results. *)
212 let no_return _ = () in
213 let print_int i = print_endline (string_of_int i) in
214 let print_int64 i = print_endline (Int64.to_string i) in
215 let print_bool b = print_endline (string_of_bool b) in
216 let print_version v =
217 let major = v / 1000000 in
218 let minor = (v - major * 1000000) / 1000 in
219 let release = (v - major * 1000000 - minor * 1000) in
220 printf "%d.%d.%d\n" major minor release
222 let string_of_domain_state = function
223 | D.InfoNoState -> "unknown"
224 | D.InfoRunning -> "running"
225 | D.InfoBlocked -> "blocked"
226 | D.InfoPaused -> "paused"
227 | D.InfoShutdown -> "shutdown"
228 | D.InfoShutoff -> "shutoff"
229 | D.InfoCrashed -> "crashed"
231 let string_of_vcpu_state = function
232 | D.VcpuOffline -> "offline"
233 | D.VcpuRunning -> "running"
234 | D.VcpuBlocked -> "blocked"
236 let print_domain_array doms =
240 try sprintf "%d" (D.get_id dom)
241 with Libvirt.Virterror _ -> "" in
243 try sprintf "%s" (D.get_name dom)
244 with Libvirt.Virterror _ -> "" in
247 let { D.state = state } = D.get_info dom in
248 string_of_domain_state state
249 with Libvirt.Virterror _ -> "" in
250 printf "%5s %-30s %s\n" id name state
253 let print_network_array nets =
256 printf "%s\n" (N.get_name net)
259 let print_node_info info =
260 printf "model: %s\n" info.C.model;
261 printf "memory: %Ld K\n" info.C.memory;
262 printf "cpus: %d\n" info.C.cpus;
263 printf "mhz: %d\n" info.C.mhz;
264 printf "nodes: %d\n" info.C.nodes;
265 printf "sockets: %d\n" info.C.sockets;
266 printf "cores: %d\n" info.C.cores;
267 printf "threads: %d\n" info.C.threads;
269 let print_domain_state { D.state = state } =
270 print_endline (string_of_domain_state state)
272 let print_domain_info info =
273 printf "state: %s\n" (string_of_domain_state info.D.state);
274 printf "max_mem: %Ld K\n" info.D.max_mem;
275 printf "memory: %Ld K\n" info.D.memory;
276 printf "nr_virt_cpu: %d\n" info.D.nr_virt_cpu;
277 printf "cpu_time: %Ld ns\n" info.D.cpu_time;
279 let print_sched_param_array params =
284 | D.SchedFieldInt32 i -> printf " %ld\n" i
285 | D.SchedFieldUInt32 i -> printf " %lu\n" i
286 | D.SchedFieldInt64 i -> printf " %Ld\n" i
287 | D.SchedFieldUInt64 i -> printf " %Lu\n" i
288 | D.SchedFieldFloat f -> printf " %g\n" f
289 | D.SchedFieldBool b -> printf " %b\n" b
292 let print_vcpu_info (ncpus, vcpu_infos, cpumaps, maplen, maxcpus) =
293 for n = 0 to ncpus-1 do
294 printf "virtual CPU: %d\n" n;
295 printf " on physical CPU: %d\n" vcpu_infos.(n).D.cpu;
296 printf " current state: %s\n"
297 (string_of_vcpu_state vcpu_infos.(n).D.vcpu_state);
298 printf " CPU time: %Ld ns\n" vcpu_infos.(n).D.vcpu_time;
299 printf " CPU affinity: ";
300 for m = 0 to maxcpus-1 do
301 print_char (if C.cpu_usable cpumaps maplen n m then 'y' else '-')
306 let print_block_stats { D.rd_req = rd_req; rd_bytes = rd_bytes;
307 wr_req = wr_req; wr_bytes = wr_bytes;
309 if rd_req >= 0L then printf "read requests: %Ld\n" rd_req;
310 if rd_bytes >= 0L then printf "read bytes: %Ld\n" rd_bytes;
311 if wr_req >= 0L then printf "write requests: %Ld\n" wr_req;
312 if wr_bytes >= 0L then printf "write bytes: %Ld\n" wr_bytes;
313 if errs >= 0L then printf "errors: %Ld\n" errs;
314 and print_interface_stats { D.rx_bytes = rx_bytes; rx_packets = rx_packets;
315 rx_errs = rx_errs; rx_drop = rx_drop;
316 tx_bytes = tx_bytes; tx_packets = tx_packets;
317 tx_errs = tx_errs; tx_drop = tx_drop } =
318 if rx_bytes >= 0L then printf "rx bytes: %Ld\n" rx_bytes;
319 if rx_packets >= 0L then printf "rx packets: %Ld\n" rx_packets;
320 if rx_errs >= 0L then printf "rx errs: %Ld\n" rx_errs;
321 if rx_drop >= 0L then printf "rx dropped: %Ld\n" rx_drop;
322 if tx_bytes >= 0L then printf "tx bytes: %Ld\n" tx_bytes;
323 if tx_packets >= 0L then printf "tx packets: %Ld\n" tx_packets;
324 if tx_errs >= 0L then printf "tx errs: %Ld\n" tx_errs;
325 if tx_drop >= 0L then printf "tx dropped: %Ld\n" tx_drop;
328 (* List of commands. *)
331 cmd2 no_return D.attach_device
332 (arg_full_connection domain_of_string) input_file,
333 "Attach device to domain.";
335 cmd2 no_return D.set_autostart
336 (arg_full_connection domain_of_string) boolean_of_string,
337 "Set whether a domain autostarts at boot.";
339 cmd0 print_endline (with_readonly_connection C.get_capabilities),
340 "Returns capabilities of hypervisor/driver.";
342 cmd0 no_return close_connection,
343 "Close an existing hypervisor connection.";
346 (fun name readonly ->
349 | None | Some false -> conn := RW (C.connect ~name ())
350 | Some true -> conn := RO (C.connect_readonly ~name ())
351 ) string_of_string string_of_readonly,
352 "Open a new hypervisor connection.";
355 (fun xml -> D.create_linux (get_full_connection ()) xml) input_file,
356 "Create a domain from an XML file.";
359 (fun xml -> D.define_xml (get_full_connection ()) xml) input_file,
360 "Define (but don't start) a domain from an XML file.";
362 cmd2 no_return D.detach_device
363 (arg_full_connection domain_of_string) input_file,
364 "Detach device from domain.";
366 cmd1 no_return D.destroy (arg_full_connection domain_of_string),
369 cmd2 print_block_stats D.block_stats
370 (arg_readonly_connection domain_of_string) string_of_string,
371 "Display the block device statistics for a domain.";
373 cmd1 print_int D.get_id (arg_readonly_connection domain_of_string),
374 "Print the ID of a domain.";
376 cmd2 print_interface_stats D.interface_stats
377 (arg_readonly_connection domain_of_string) string_of_string,
378 "Display the network interface statistics for a domain.";
380 cmd1 print_domain_info D.get_info
381 (arg_readonly_connection domain_of_string),
382 "Print the domain info.";
384 cmd1 print_int64 D.get_max_memory
385 (arg_readonly_connection domain_of_string),
386 "Print the max memory (in kilobytes) of a domain.";
388 cmd1 print_int D.get_max_vcpus
389 (arg_readonly_connection domain_of_string),
390 "Print the max VCPUs of a domain.";
392 cmd1 print_endline D.get_name
393 (arg_readonly_connection domain_of_string),
394 "Print the name of a domain.";
396 cmd1 print_endline D.get_os_type
397 (arg_readonly_connection domain_of_string),
398 "Print the OS type of a domain.";
400 cmd1 print_domain_state D.get_info
401 (arg_readonly_connection domain_of_string),
402 "Print the domain state.";
404 cmd1 print_endline D.get_uuid_string
405 (arg_readonly_connection domain_of_string),
406 "Print the UUID of a domain.";
408 cmd2 no_return D.core_dump
409 (arg_full_connection domain_of_string) string_of_string,
410 "Core dump a domain to a file for analysis.";
412 cmd1 print_endline D.get_xml_desc
413 (arg_full_connection domain_of_string),
414 "Print the XML description of a domain.";
416 cmd1 print_bool D.get_autostart
417 (arg_readonly_connection domain_of_string),
418 "Print whether a domain autostarts at boot.";
420 cmd0 print_endline (with_readonly_connection C.get_hostname),
421 "Print the hostname.";
423 cmd0 print_domain_array
425 let c = get_readonly_connection () in
426 let n = C.num_of_domains c in
427 let domids = C.list_domains c n in
428 Array.map (D.lookup_by_id c) domids),
429 "List the running domains.";
431 cmd0 print_domain_array
433 let c = get_readonly_connection () in
434 let n = C.num_of_defined_domains c in
435 let domnames = C.list_defined_domains c n in
436 Array.map (D.lookup_by_name c) domnames),
437 "List the defined but not running domains.";
439 cmd0 no_return (fun () -> exit 0),
440 "Quit the interactive terminal.";
442 cmd0 print_int (fun () -> C.get_max_vcpus (get_readonly_connection ()) ()),
443 "Print the max VCPUs available.";
445 cmd2 no_return N.set_autostart
446 (arg_full_connection network_of_string) boolean_of_string,
447 "Set whether a network autostarts at boot.";
449 cmd1 print_endline N.get_bridge_name
450 (arg_readonly_connection network_of_string),
451 "Print the bridge name of a network.";
454 (fun xml -> N.create_xml (get_full_connection ()) xml) input_file,
455 "Create a network from an XML file.";
458 (fun xml -> N.define_xml (get_full_connection ()) xml) input_file,
459 "Define (but don't start) a network from an XML file.";
461 cmd1 no_return N.destroy (arg_full_connection network_of_string),
462 "Destroy a network.";
464 cmd1 print_endline N.get_xml_desc
465 (arg_full_connection network_of_string),
466 "Print the XML description of a network.";
468 cmd1 print_bool N.get_autostart
469 (arg_full_connection network_of_string),
470 "Print whether a network autostarts at boot.";
472 cmd0 print_network_array
474 let c = get_readonly_connection () in
475 let n = C.num_of_networks c in
476 let nets = C.list_networks c n in
477 Array.map (N.lookup_by_name c) nets),
478 "List the active networks.";
480 cmd0 print_network_array
482 let c = get_readonly_connection () in
483 let n = C.num_of_defined_networks c in
484 let nets = C.list_defined_networks c n in
485 Array.map (N.lookup_by_name c) nets),
486 "List the defined but inactive networks.";
488 cmd1 print_endline N.get_name
489 (arg_readonly_connection network_of_string),
490 "Print the name of a network.";
492 cmd1 no_return N.create
493 (arg_full_connection network_of_string),
494 "Start a previously defined inactive network.";
496 cmd1 no_return N.undefine
497 (arg_full_connection network_of_string),
498 "Undefine an inactive network.";
500 cmd1 print_endline N.get_uuid_string
501 (arg_readonly_connection network_of_string),
502 "Print the UUID of a network.";
504 cmd0 print_node_info (with_readonly_connection C.get_node_info),
505 "Print node information.";
507 cmd1 no_return D.reboot (arg_full_connection domain_of_string),
511 fun path -> D.restore (get_full_connection ()) path
513 "Restore a domain from the named file.";
515 cmd1 no_return D.resume (arg_full_connection domain_of_string),
518 cmd2 no_return D.save
519 (arg_full_connection domain_of_string) string_of_string,
520 "Save a domain to a file.";
522 cmd1 print_sched_param_array (
524 let n = snd (D.get_scheduler_type dom) in
525 D.get_scheduler_parameters dom n
526 ) (arg_readonly_connection domain_of_string),
527 "Get the current scheduler parameters for a domain.";
531 | [] -> failwith "expecting domain followed by field value pairs"
533 let conn = get_full_connection () in
534 let dom = domain_of_string conn dom in
535 let params = parse_sched_params pairs in
536 let params = Array.of_list params in
537 D.set_scheduler_parameters dom params
539 "Set the scheduler parameters for a domain.";
542 (fun dom -> fst (D.get_scheduler_type dom))
543 (arg_readonly_connection domain_of_string),
544 "Get the scheduler type.";
546 cmd2 no_return D.set_memory
547 (arg_full_connection domain_of_string) Int64.of_string,
548 "Set the memory used by the domain (in kilobytes).";
550 cmd2 no_return D.set_max_memory
551 (arg_full_connection domain_of_string) Int64.of_string,
552 "Set the maximum memory used by the domain (in kilobytes).";
554 cmd1 no_return D.shutdown
555 (arg_full_connection domain_of_string),
556 "Gracefully shutdown a domain.";
558 cmd1 no_return D.create
559 (arg_full_connection domain_of_string),
560 "Start a previously defined inactive domain.";
562 cmd1 no_return D.suspend
563 (arg_full_connection domain_of_string),
566 cmd0 print_endline (with_readonly_connection C.get_type),
567 "Print the driver name";
569 cmd1 no_return D.undefine
570 (arg_full_connection domain_of_string),
571 "Undefine an inactive domain.";
573 cmd0 print_endline (with_readonly_connection C.get_uri),
574 "Print the canonical URI.";
576 cmd1 print_vcpu_info (
578 let c = get_readonly_connection () in
579 let info = C.get_node_info c in
580 let dominfo = D.get_info dom in
581 let maxcpus = C.maxcpus_of_node_info info in
582 let maplen = C.cpumaplen maxcpus in
583 let maxinfo = dominfo.D.nr_virt_cpu in
584 let ncpus, vcpu_infos, cpumaps = D.get_vcpus dom maxinfo maplen in
585 ncpus, vcpu_infos, cpumaps, maplen, maxcpus
586 ) (arg_readonly_connection domain_of_string),
587 "Pin domain VCPU to a list of physical CPUs.";
589 cmd3 no_return D.pin_vcpu
590 (arg_full_connection domain_of_string) int_of_string cpumap_of_string,
591 "Pin domain VCPU to a list of physical CPUs.";
593 cmd2 no_return D.set_vcpus
594 (arg_full_connection domain_of_string) int_of_string,
595 "Set the number of virtual CPUs assigned to a domain.";
597 cmd0 print_version (with_readonly_connection C.get_version),
598 "Print the driver version";
603 | None -> (* List of commands. *)
606 fun (cmd, _, description) ->
607 sprintf "%-12s %s" cmd description
610 "\n\nUse '" ^ program_name ^ " help command' for help on a command."
612 | Some command -> (* Full description of one command. *)
614 let (command, _, description) =
615 List.find (fun (c, _, _) -> c = command) commands in
616 sprintf "%s %s\n\n%s" program_name command description
619 failwith ("help: " ^ command ^ ": command not found");
624 cmd01 print_endline help string_of_string,
625 "Print list of commands or full description of one command.";
628 (* Execute a command. *)
629 let do_command command args =
631 let (_, cmd, _) = List.find (fun (c, _, _) -> c = command) commands in
635 failwith (command ^ ": command not found");
640 (* Interactive mode. *)
641 let rec interactive_mode () =
644 | No_connection -> "mlvirsh(no connection)$ "
645 | RO _ -> "mlvirsh(ro)$ "
646 | RW _ -> "mlvirsh# " in
648 let command = read_line () in
649 (match String.nsplit command " " with
652 do_command command args
654 Gc.full_major (); (* Free up all unreachable domain and network objects. *)
657 (* Connect to hypervisor. Allow the connection to fail. *)
661 if readonly then RO (C.connect_readonly ?name ())
662 else RW (C.connect ?name ())
664 Libvirt.Virterror err ->
665 eprintf "%s: %s\n" program_name (Libvirt.Virterror.to_string err);
670 (* Execute the command on the command line, if there was one.
671 * Otherwise go into interactive mode.
673 (match extra_args with
675 do_command command args
677 try interactive_mode () with End_of_file -> ()
680 (* If we are connected to a hypervisor, close the connection. *)
683 (* A good way to find heap bugs: *)
686 | Libvirt.Virterror err ->
687 eprintf "%s: %s\n" program_name (Libvirt.Virterror.to_string err)
689 eprintf "%s: %s\n" program_name msg