get rid of vm_config.vname

This commit is contained in:
Hannes Mehnert 2018-10-23 00:54:05 +02:00
parent 183d1c9e58
commit c399501a18
12 changed files with 53 additions and 79 deletions

View File

@ -94,10 +94,7 @@ let create _ opt_socket force name image cpuid requested_memory boot_params bloc
(* TODO we could do the compression btw *) (* TODO we could do the compression btw *)
and vmimage = `Hvt_amd64, Cstruct.of_string image' and vmimage = `Hvt_amd64, Cstruct.of_string image'
in in
let vm_config = { let vm_config = { cpuid ; requested_memory ; block_device ; network ; vmimage ; argv } in
vname = name ; cpuid ; requested_memory ; block_device ; network ;
vmimage ; argv
} in
let cmd = let cmd =
if force then if force then
`Vm_force_create vm_config `Vm_force_create vm_config

View File

@ -47,18 +47,18 @@ let create c_fd process cont =
| Error (`Msg msg) -> | Error (`Msg msg) ->
Logs.err (fun m -> m "create continuation failed %s" msg) ; Logs.err (fun m -> m "create continuation failed %s" msg) ;
Lwt.return_unit Lwt.return_unit
| Ok (state'', out, vm) -> | Ok (state'', out, name, vm) ->
state := state'' ; state := state'' ;
s := { !s with vm_created = succ !s.vm_created } ; s := { !s with vm_created = succ !s.vm_created } ;
Lwt.async (fun () -> Lwt.async (fun () ->
Vmm_lwt.wait_and_clear vm.Vmm_core.pid vm.Vmm_core.stdout >>= fun r -> Vmm_lwt.wait_and_clear vm.Vmm_core.pid vm.Vmm_core.stdout >>= fun r ->
let state', out' = Vmm_engine.handle_shutdown !state vm r in let state', out' = Vmm_engine.handle_shutdown !state name vm r in
s := { !s with vm_destroyed = succ !s.vm_destroyed } ; s := { !s with vm_destroyed = succ !s.vm_destroyed } ;
state := state' ; state := state' ;
process out' >|= fun () -> process out' >|= fun () ->
Lwt.wakeup wakeme ()) ; Lwt.wakeup wakeme ()) ;
process out >>= fun () -> process out >>= fun () ->
let state', out = Vmm_engine.setup_stats !state vm in let state', out = Vmm_engine.setup_stats !state name vm in
state := state' ; state := state' ;
process out (* TODO: need to read from stats socket! *) process out (* TODO: need to read from stats socket! *)

View File

@ -248,9 +248,8 @@ let vm_of_cert prefix cert =
opt cert Oid.network strings_of_cstruct >>= fun network -> opt cert Oid.network strings_of_cstruct >>= fun network ->
req "vmimage" cert Oid.vmimage image_of_cstruct >>= fun vmimage -> req "vmimage" cert Oid.vmimage image_of_cstruct >>= fun vmimage ->
opt cert Oid.argv strings_of_cstruct >>= fun argv -> opt cert Oid.argv strings_of_cstruct >>= fun argv ->
let vname = prefix @ [ id cert ] in
let network = match network with None -> [] | Some x -> x in let network = match network with None -> [] | Some x -> x in
Ok { vname ; cpuid ; requested_memory ; block_device ; network ; vmimage ; argv } Ok { cpuid ; requested_memory ; block_device ; network ; vmimage ; argv }
let command_of_cert version cert = let command_of_cert version cert =
version_of_cert version cert >>= fun () -> version_of_cert version cert >>= fun () ->
@ -508,7 +507,7 @@ let pp_vm_cmd ppf = function
let vm_config = let vm_config =
let f (cpuid, requested_memory, block_device, network, vmimage, argv) = let f (cpuid, requested_memory, block_device, network, vmimage, argv) =
let network = match network with None -> [] | Some xs -> xs in let network = match network with None -> [] | Some xs -> xs in
{ vname = [] ; cpuid ; requested_memory ; block_device ; network ; vmimage ; argv } { cpuid ; requested_memory ; block_device ; network ; vmimage ; argv }
and g vm = and g vm =
let network = match vm.network with [] -> None | xs -> Some xs in let network = match vm.network with [] -> None | xs -> Some xs in
(vm.cpuid, vm.requested_memory, vm.block_device, network, vm.vmimage, vm.argv) (vm.cpuid, vm.requested_memory, vm.block_device, network, vm.vmimage, vm.argv)

View File

@ -233,4 +233,3 @@ type log_entry = header * Ptime.t * Log.event
val log_entry_to_cstruct : log_entry -> Cstruct.t val log_entry_to_cstruct : log_entry -> Cstruct.t
val log_entry_of_cstruct : Cstruct.t -> (log_entry, [> `Msg of string ]) result val log_entry_of_cstruct : Cstruct.t -> (log_entry, [> `Msg of string ]) result

View File

@ -160,7 +160,6 @@ let is_sub ~super ~sub =
sub_bridges super.bridges sub.bridges && sub_block super.block sub.block sub_bridges super.bridges sub.bridges && sub_block super.block sub.block
type vm_config = { type vm_config = {
vname : id ;
cpuid : int ; cpuid : int ;
requested_memory : int ; requested_memory : int ;
block_device : string option ; block_device : string option ;
@ -169,18 +168,13 @@ type vm_config = {
argv : string list option ; argv : string list option ;
} }
(* used for block devices *)
let location vm = match vm.vname with
| tld::rest -> tld, String.concat ~sep:"." rest
| [] -> invalid_arg "dunno how this happened"
let pp_image ppf (typ, blob) = let pp_image ppf (typ, blob) =
let l = Cstruct.len blob in let l = Cstruct.len blob in
Fmt.pf ppf "%a: %d bytes" pp_vmtype typ l Fmt.pf ppf "%a: %d bytes" pp_vmtype typ l
let pp_vm_config ppf (vm : vm_config) = let pp_vm_config ppf (vm : vm_config) =
Fmt.pf ppf "%a cpu %d, %d MB memory, block device %a@ bridge %a, image %a, argv %a" Fmt.pf ppf "cpu %d, %d MB memory, block device %a@ bridge %a, image %a, argv %a"
pp_id vm.vname vm.cpuid vm.requested_memory vm.cpuid vm.requested_memory
Fmt.(option ~none:(unit "no") string) vm.block_device Fmt.(option ~none:(unit "no") string) vm.block_device
Fmt.(list ~sep:(unit ", ") string) vm.network Fmt.(list ~sep:(unit ", ") string) vm.network
pp_image vm.vmimage pp_image vm.vmimage

View File

@ -204,7 +204,6 @@ val sub_block : 'a option -> 'a option -> bool
val sub_cpu : IS.t -> IS.t -> bool val sub_cpu : IS.t -> IS.t -> bool
val is_sub : super:policy -> sub:policy -> bool val is_sub : super:policy -> sub:policy -> bool
type vm_config = { type vm_config = {
vname : id;
cpuid : int; cpuid : int;
requested_memory : int; requested_memory : int;
block_device : string option; block_device : string option;
@ -212,7 +211,6 @@ type vm_config = {
vmimage : vmtype * Cstruct.t; vmimage : vmtype * Cstruct.t;
argv : string list option; argv : string list option;
} }
val location : vm_config -> string * string
val pp_image : val pp_image :
Format.formatter -> Format.formatter ->
[< `Hvt_amd64 | `Hvt_amd64_compressed | `Hvt_arm64 ] * Cstruct.t -> unit [< `Hvt_amd64 | `Hvt_amd64_compressed | `Hvt_arm64 ] * Cstruct.t -> unit

View File

@ -47,28 +47,28 @@ let log t id event =
let handle_create t hdr vm_config = let handle_create t hdr vm_config =
(* TODO fix (remove field?) *) (* TODO fix (remove field?) *)
let vm_config = { vm_config with vname = hdr.Vmm_asn.id } in let name = hdr.Vmm_asn.id in
(match Vmm_resources.find_vm t.resources vm_config.vname with (match Vmm_resources.find_vm t.resources name with
| Some _ -> Error (`Msg "VM with same name is already running") | Some _ -> Error (`Msg "VM with same name is already running")
| None -> Ok ()) >>= fun () -> | None -> Ok ()) >>= fun () ->
Logs.debug (fun m -> m "now checking resource policies") ; Logs.debug (fun m -> m "now checking resource policies") ;
(if Vmm_resources.check_vm_policy t.resources vm_config then (if Vmm_resources.check_vm_policy t.resources name vm_config then
Ok () Ok ()
else else
Error (`Msg "resource policies don't allow this")) >>= fun () -> Error (`Msg "resource policies don't allow this")) >>= fun () ->
(* prepare VM: save VM image to disk, create fifo, ... *) (* prepare VM: save VM image to disk, create fifo, ... *)
Vmm_unix.prepare vm_config >>= fun taps -> Vmm_unix.prepare name vm_config >>= fun taps ->
Logs.debug (fun m -> m "prepared vm with taps %a" Fmt.(list ~sep:(unit ",@ ") string) taps) ; Logs.debug (fun m -> m "prepared vm with taps %a" Fmt.(list ~sep:(unit ",@ ") string) taps) ;
(* TODO should we pre-reserve sth in t? *) (* TODO should we pre-reserve sth in t? *)
let cons = `Console_add in let cons = `Console_add in
let header = Vmm_asn.{ version = t.wire_version ; sequence = t.console_counter ; id = vm_config.vname } in let header = Vmm_asn.{ version = t.wire_version ; sequence = t.console_counter ; id = name } in
Ok ({ t with console_counter = Int64.succ t.console_counter }, [ `Cons (header, `Command (`Console_cmd cons)) ], Ok ({ t with console_counter = Int64.succ t.console_counter }, [ `Cons (header, `Command (`Console_cmd cons)) ],
`Create (fun t task -> `Create (fun t task ->
(* actually execute the vm *) (* actually execute the vm *)
Vmm_unix.exec vm_config taps >>= fun vm -> Vmm_unix.exec name vm_config taps >>= fun vm ->
Logs.debug (fun m -> m "exec()ed vm") ; Logs.debug (fun m -> m "exec()ed vm") ;
Vmm_resources.insert_vm t.resources vm >>= fun resources -> Vmm_resources.insert_vm t.resources name vm >>= fun resources ->
let tasks = String.Map.add (string_of_id vm_config.vname) task t.tasks in let tasks = String.Map.add (string_of_id name) task t.tasks in
let used_bridges = let used_bridges =
List.fold_left2 (fun b br ta -> List.fold_left2 (fun b br ta ->
let old = match String.Map.find br b with let old = match String.Map.find br b with
@ -79,21 +79,21 @@ let handle_create t hdr vm_config =
t.used_bridges vm_config.network taps t.used_bridges vm_config.network taps
in in
let t = { t with resources ; tasks ; used_bridges } in let t = { t with resources ; tasks ; used_bridges } in
let t, out = log t vm_config.vname (`VM_start (vm.pid, vm.taps, None)) in let t, out = log t name (`VM_start (vm.pid, vm.taps, None)) in
let data = `Success (`String "created VM") in let data = `Success (`String "created VM") in
Ok (t, [ `Data (hdr, data) ; out ], vm))) Ok (t, [ `Data (hdr, data) ; out ], name, vm)))
let setup_stats t vm = let setup_stats t name vm =
let stat_out = `Stats_add (vm.pid, vm.taps) in let stat_out = `Stats_add (vm.pid, vm.taps) in
let header = Vmm_asn.{ version = t.wire_version ; sequence = t.stats_counter ; id = vm.config.vname } in let header = Vmm_asn.{ version = t.wire_version ; sequence = t.stats_counter ; id = name } in
let t = { t with stats_counter = Int64.succ t.stats_counter } in let t = { t with stats_counter = Int64.succ t.stats_counter } in
t, [ `Stat (header, `Command (`Stats_cmd stat_out)) ] t, [ `Stat (header, `Command (`Stats_cmd stat_out)) ]
let handle_shutdown t vm r = let handle_shutdown t name vm r =
(match Vmm_unix.shutdown vm with (match Vmm_unix.shutdown name vm with
| Ok () -> () | Ok () -> ()
| Error (`Msg e) -> Logs.warn (fun m -> m "%s while shutdown vm %a" e pp_vm vm)) ; | Error (`Msg e) -> Logs.warn (fun m -> m "%s while shutdown vm %a" e pp_vm vm)) ;
let resources = Vmm_resources.remove t.resources vm.config.vname in let resources = Vmm_resources.remove t.resources name in
let used_bridges = let used_bridges =
List.fold_left2 (fun b br ta -> List.fold_left2 (fun b br ta ->
let old = match String.Map.find br b with let old = match String.Map.find br b with
@ -104,10 +104,10 @@ let handle_shutdown t vm r =
t.used_bridges vm.config.network vm.taps t.used_bridges vm.config.network vm.taps
in in
let stat_out = `Stats_remove in let stat_out = `Stats_remove in
let header = Vmm_asn.{ version = t.wire_version ; sequence = t.stats_counter ; id = vm.config.vname } in let header = Vmm_asn.{ version = t.wire_version ; sequence = t.stats_counter ; id = name } in
let tasks = String.Map.remove (string_of_id vm.config.vname) t.tasks in let tasks = String.Map.remove (string_of_id name) t.tasks in
let t = { t with stats_counter = Int64.succ t.stats_counter ; resources ; used_bridges ; tasks } in let t = { t with stats_counter = Int64.succ t.stats_counter ; resources ; used_bridges ; tasks } in
let t, logout = log t vm.config.vname (`VM_stop (vm.pid, r)) let t, logout = log t name (`VM_stop (vm.pid, r))
in in
(t, [ `Stat (header, `Command (`Stats_cmd stat_out)) ; logout ]) (t, [ `Stat (header, `Command (`Stats_cmd stat_out)) ; logout ])
@ -172,8 +172,8 @@ let handle_command t (header, payload) =
| `Command (`Vm_cmd (`Vm_create vm_config)) -> | `Command (`Vm_cmd (`Vm_create vm_config)) ->
handle_create t header vm_config handle_create t header vm_config
| `Command (`Vm_cmd (`Vm_force_create vm_config)) -> | `Command (`Vm_cmd (`Vm_force_create vm_config)) ->
let resources = Vmm_resources.remove t.resources vm_config.vname in let resources = Vmm_resources.remove t.resources id in
if Vmm_resources.check_vm_policy resources vm_config then if Vmm_resources.check_vm_policy resources id vm_config then
begin match Vmm_resources.find_vm t.resources id with begin match Vmm_resources.find_vm t.resources id with
| None -> handle_create t header vm_config | None -> handle_create t header vm_config
| Some vm -> | Some vm ->

View File

@ -11,16 +11,16 @@ type service_out = [
type out = [ service_out | `Data of Vmm_asn.wire ] type out = [ service_out | `Data of Vmm_asn.wire ]
val handle_shutdown : 'a t -> Vmm_core.vm -> val handle_shutdown : 'a t -> Vmm_core.id -> Vmm_core.vm ->
[ `Exit of int | `Signal of int | `Stop of int ] -> 'a t * out list [ `Exit of int | `Signal of int | `Stop of int ] -> 'a t * out list
val handle_command : 'a t -> Vmm_asn.wire -> val handle_command : 'a t -> Vmm_asn.wire ->
'a t * out list * 'a t * out list *
[ `Create of 'c t -> 'c -> ('c t * out list * Vmm_core.vm, [> Rresult.R.msg ]) result [ `Create of 'c t -> 'c -> ('c t * out list * Vmm_core.id * Vmm_core.vm, [> Rresult.R.msg ]) result
| `End | `End
| `Wait of 'a * out list | `Wait of 'a * out list
| `Wait_and_create of 'a * ('a t -> 'a t * out list * | `Wait_and_create of 'a * ('a t -> 'a t * out list *
[ `Create of 'd t -> 'd -> ('d t * out list * Vmm_core.vm, [> Rresult.R.msg ]) result [ `Create of 'd t -> 'd -> ('d t * out list * Vmm_core.id * Vmm_core.vm, [> Rresult.R.msg ]) result
| `End ]) ] | `End ]) ]
val setup_stats : 'a t -> Vmm_core.vm -> 'a t * out list val setup_stats : 'a t -> Vmm_core.id -> Vmm_core.vm -> 'a t * out list

View File

@ -50,17 +50,17 @@ let find_vm t name = match Vmm_trie.find name t with
| Some (Vm vm) -> Some vm | Some (Vm vm) -> Some vm
| _ -> None | _ -> None
let check_vm_policy t vm = let check_vm_policy t name vm =
let dom = domain vm.vname in let dom = domain name in
let res = resource_usage t dom in let res = resource_usage t dom in
match Vmm_trie.find dom t with match Vmm_trie.find dom t with
| None -> true | None -> true
| Some (Vm _) -> assert false | Some (Vm _) -> assert false
| Some (Policy p) -> check_resource p vm res | Some (Policy p) -> check_resource p vm res
let insert_vm t vm = let insert_vm t name vm =
if check_vm_policy t vm.config then if check_vm_policy t name vm.config then
match Vmm_trie.insert vm.config.vname (Vm vm) t with match Vmm_trie.insert name (Vm vm) t with
| t', None -> Ok t' | t', None -> Ok t'
| _, Some _ -> Error (`Msg "vm already exists") | _, Some _ -> Error (`Msg "vm already exists")
else else

View File

@ -22,11 +22,11 @@ val find_vm : t -> Vmm_core.id -> Vmm_core.vm option
(** [check_vm_policy t vm] checks whether [vm] under [id] in [t] would be (** [check_vm_policy t vm] checks whether [vm] under [id] in [t] would be
allowed under the current policies. *) allowed under the current policies. *)
val check_vm_policy : t -> Vmm_core.vm_config -> bool val check_vm_policy : t -> Vmm_core.id -> Vmm_core.vm_config -> bool
(** [insert_vm t vm] inserts [vm] under [id] in [t], and returns the new [t] or (** [insert_vm t vm] inserts [vm] under [id] in [t], and returns the new [t] or
an error. *) an error. *)
val insert_vm : t -> Vmm_core.vm -> (t, [> `Msg of string]) result val insert_vm : t -> Vmm_core.id -> Vmm_core.vm -> (t, [> `Msg of string]) result
(** [insert_policy t id policy] inserts [policy] under [id] in [t], and returns (** [insert_policy t id policy] inserts [policy] under [id] in [t], and returns
the new [t] or an error. *) the new [t] or an error. *)

View File

@ -58,8 +58,8 @@ let rec mkfifo name =
| Unix.Unix_error (Unix.EINTR, _, _) -> mkfifo name | Unix.Unix_error (Unix.EINTR, _, _) -> mkfifo name
let image_file, fifo_file = let image_file, fifo_file =
((fun vm -> Fpath.(tmpdir / (string_of_id vm.vname) + "img")), ((fun name -> Fpath.(tmpdir / (string_of_id name) + "img")),
(fun vm -> Fpath.(tmpdir / "fifo" / (string_of_id vm.vname)))) (fun name -> Fpath.(tmpdir / "fifo" / (string_of_id name))))
let rec fifo_exists file = let rec fifo_exists file =
try Ok (Unix.((stat @@ Fpath.to_string file).st_kind = S_FIFO)) with try Ok (Unix.((stat @@ Fpath.to_string file).st_kind = S_FIFO)) with
@ -103,18 +103,7 @@ let destroy_tap tapname =
Bos.OS.Cmd.run Bos.Cmd.(v "ip" % "tuntap" % "del" % "dev" % tapname % "mode" % "tap") Bos.OS.Cmd.run Bos.Cmd.(v "ip" % "tuntap" % "del" % "dev" % tapname % "mode" % "tap")
| x -> Error (`Msg ("unsupported operating system " ^ x)) | x -> Error (`Msg ("unsupported operating system " ^ x))
let create_bridge bname = let prepare name vm =
Lazy.force (uname ()) >>= fun (sys, _) ->
match sys with
| x when x = "FreeBSD" ->
let cmd = Bos.Cmd.(v "ifconfig" % "bridge" % "create") in
Bos.OS.Cmd.run_out cmd |> Bos.OS.Cmd.out_string >>= fun (name, _) ->
Bos.OS.Cmd.run Bos.Cmd.(v "ifconfig" % name % "name" % bname)
| x when x = "Linux" ->
Bos.OS.Cmd.run Bos.Cmd.(v "brctl" % "addbr" % bname)
| x -> Error (`Msg ("unsupported operating system " ^ x))
let prepare vm =
(match vm.vmimage with (match vm.vmimage with
| `Hvt_amd64, blob -> Ok blob | `Hvt_amd64, blob -> Ok blob
| `Hvt_amd64_compressed, blob -> | `Hvt_amd64_compressed, blob ->
@ -123,7 +112,7 @@ let prepare vm =
| Error () -> Error (`Msg "failed to uncompress") | Error () -> Error (`Msg "failed to uncompress")
end end
| `Hvt_arm64, _ -> Error (`Msg "no amd64 hvt image found")) >>= fun image -> | `Hvt_arm64, _ -> Error (`Msg "no amd64 hvt image found")) >>= fun image ->
let fifo = fifo_file vm in let fifo = fifo_file name in
(match fifo_exists fifo with (match fifo_exists fifo with
| Ok true -> Ok () | Ok true -> Ok ()
| Ok false -> Error (`Msg ("file " ^ Fpath.to_string fifo ^ " exists and is not a fifo")) | Ok false -> Error (`Msg ("file " ^ Fpath.to_string fifo ^ " exists and is not a fifo"))
@ -137,13 +126,13 @@ let prepare vm =
create_tap b >>= fun tap -> create_tap b >>= fun tap ->
Ok (tap :: acc)) Ok (tap :: acc))
(Ok []) vm.network >>= fun taps -> (Ok []) vm.network >>= fun taps ->
Bos.OS.File.write (image_file vm) (Cstruct.to_string image) >>= fun () -> Bos.OS.File.write (image_file name) (Cstruct.to_string image) >>= fun () ->
Ok (List.rev taps) Ok (List.rev taps)
let shutdown vm = let shutdown name vm =
(* same order as prepare! *) (* same order as prepare! *)
Bos.OS.File.delete (image_file vm.config) >>= fun () -> Bos.OS.File.delete (image_file name) >>= fun () ->
Bos.OS.File.delete (fifo_file vm.config) >>= fun () -> Bos.OS.File.delete (fifo_file name) >>= fun () ->
List.fold_left (fun r n -> r >>= fun () -> destroy_tap n) (Ok ()) vm.taps List.fold_left (fun r n -> r >>= fun () -> destroy_tap n) (Ok ()) vm.taps
let cpuset cpu = let cpuset cpu =
@ -156,7 +145,7 @@ let cpuset cpu =
Ok ([ "taskset" ; "-c" ; cpustring ]) Ok ([ "taskset" ; "-c" ; cpustring ])
| x -> Error (`Msg ("unsupported operating system " ^ x)) | x -> Error (`Msg ("unsupported operating system " ^ x))
let exec vm taps = let exec name vm taps =
(* TODO: --net-mac=xx *) (* TODO: --net-mac=xx *)
let net = List.map (fun t -> "--net=" ^ t) taps in let net = List.map (fun t -> "--net=" ^ t) taps in
let argv = match vm.argv with None -> [] | Some xs -> xs in let argv = match vm.argv with None -> [] | Some xs -> xs in
@ -168,12 +157,12 @@ let exec vm taps =
let mem = "--mem=" ^ string_of_int vm.requested_memory in let mem = "--mem=" ^ string_of_int vm.requested_memory in
let cmd = let cmd =
Bos.Cmd.(of_list cpuset % p bin % mem %% of_list net % Bos.Cmd.(of_list cpuset % p bin % mem %% of_list net %
"--" % p (image_file vm) %% of_list argv) "--" % p (image_file name) %% of_list argv)
in in
let line = Bos.Cmd.to_list cmd in let line = Bos.Cmd.to_list cmd in
let prog = try List.hd line with Failure _ -> failwith err_empty_line in let prog = try List.hd line with Failure _ -> failwith err_empty_line in
let line = Array.of_list line in let line = Array.of_list line in
let fifo = fifo_file vm in let fifo = fifo_file name in
Logs.debug (fun m -> m "write fd for fifo %a" Fpath.pp fifo); Logs.debug (fun m -> m "write fd for fifo %a" Fpath.pp fifo);
write_fd_for_file fifo >>= fun stdout -> write_fd_for_file fifo >>= fun stdout ->
Logs.debug (fun m -> m "opened file descriptor!"); Logs.debug (fun m -> m "opened file descriptor!");

View File

@ -4,16 +4,14 @@ open Rresult
open Vmm_core open Vmm_core
val prepare : vm_config -> (string list, [> R.msg ]) result val prepare : id -> vm_config -> (string list, [> R.msg ]) result
val shutdown : vm -> (unit, [> R.msg ]) result val shutdown : id -> vm -> (unit, [> R.msg ]) result
val exec : vm_config -> string list -> (vm, [> R.msg ]) result val exec : id -> vm_config -> string list -> (vm, [> R.msg ]) result
val destroy : vm -> unit val destroy : vm -> unit
val close_no_err : Unix.file_descr -> unit val close_no_err : Unix.file_descr -> unit
val create_tap : string -> (string, [> R.msg ]) result val create_tap : string -> (string, [> R.msg ]) result
val create_bridge : string -> (unit, [> R.msg ]) result