Compare commits

..

4 commits

Author SHA1 Message Date
Reynir Björnsson 353284bd49 Reword bridge detection error message 2020-11-30 11:54:42 +01:00
Reynir Björnsson b4a4a28634 Use ip link show to detect bridge
ip tuntap show lists all tuntap devices and ignores the rest of the
arguments, annoyingly. It will always return with exit code 0.

We do not detect if the interface is a bridge.
2020-11-30 11:37:34 +01:00
Hannes Mehnert bc71e26756 check that bridges with the provided names exist before creating tap devices 2020-11-27 22:40:15 +01:00
Hannes Mehnert 466e2d52b8 check manifest with provided device arguments 2020-11-27 22:24:52 +01:00
4 changed files with 48 additions and 26 deletions

View file

@ -102,7 +102,9 @@ let setup_log style_renderer level =
let create_vm force image cpuid memory argv block_devices bridges compression restart_on_fail exit_codes = let create_vm force image cpuid memory argv block_devices bridges compression restart_on_fail exit_codes =
let open Rresult.R.Infix in let open Rresult.R.Infix in
Bos.OS.File.read (Fpath.v image) >>| fun image -> let img_file = Fpath.v image in
Bos.OS.File.read img_file >>= fun image ->
Vmm_unix.manifest_devices_match ~bridges ~block_devices img_file >>| fun () ->
let image, compressed = match compression with let image, compressed = match compression with
| 0 -> Cstruct.of_string image, false | 0 -> Cstruct.of_string image, false
| level -> | level ->

View file

@ -16,16 +16,12 @@ let find_string_value k = function
| Some (_, `String value) -> Ok value | Some (_, `String value) -> Ok value
| _ -> Rresult.R.error_msgf "couldn't find %s in json dictionary" k | _ -> Rresult.R.error_msgf "couldn't find %s in json dictionary" k
let find_devices x = let find_devices x =
let open Rresult in let open Rresult in
let device dev = let device dev =
find_string_value "name" dev >>= fun name -> find_string_value "name" dev >>= fun name ->
find_string_value "type" dev >>= fun typ -> find_string_value "type" dev >>| fun typ ->
match typ with name, typ
| "BLOCK_BASIC" -> Ok (`Block name)
| "NET_BASIC" -> Ok (`Net name)
| _ -> Rresult.R.error_msgf "unknown device type %s in json" typ
in in
match x with match x with
| `Null | `Bool _ | `Float _ | `String _ | `A _ -> | `Null | `Bool _ | `Float _ | `String _ | `A _ ->
@ -36,9 +32,11 @@ let find_devices x =
List.fold_left List.fold_left
(fun acc dev -> (fun acc dev ->
acc >>= fun (block_devices, networks) -> acc >>= fun (block_devices, networks) ->
device dev >>= function device dev >>= fun (name, typ) ->
| `Block block -> Ok (block :: block_devices, networks) match typ with
| `Net net -> Ok (block_devices, (net, None) :: networks)) | "BLOCK_BASIC" -> Ok (name :: block_devices, networks)
| "NET_BASIC" -> Ok (block_devices, name :: networks)
| _ -> Rresult.R.error_msgf "unknown device type %s in json" typ)
(Ok ([], [])) devices (Ok ([], [])) devices
| _ -> Rresult.R.error_msg "devices field is not array in json" | _ -> Rresult.R.error_msg "devices field is not array in json"

View file

@ -209,14 +209,39 @@ let solo5_image_devices image =
(Vmm_json.json_of_string s) >>= fun data -> (Vmm_json.json_of_string s) >>= fun data ->
Vmm_json.find_devices data Vmm_json.find_devices data
let equal_blocks b1 b2 = let equal_string_lists b1 b2 err =
let open Astring in let open Astring in
String.Set.(equal (of_list b1) (of_list b2)) if String.Set.(equal (of_list b1) (of_list b2)) then
Ok ()
else
R.error_msg err
let equal_networks n1 n2 = let devices_match ~bridges ~block_devices (manifest_block, manifest_net) =
let open Astring in equal_string_lists manifest_block block_devices
let n1 = List.map fst n1 and n2 = List.map fst n2 in "specified block device(s) does not match with manifest" >>= fun () ->
String.Set.(equal (of_list n1) (of_list n2)) equal_string_lists manifest_net bridges
"specified bridge(s) does not match with the manifest"
let manifest_devices_match ~bridges ~block_devices image_file =
solo5_image_devices image_file >>=
let bridges = List.map fst bridges in
devices_match ~bridges ~block_devices
let bridge_name (service, b) = match b with None -> service | Some b -> b
let bridge_exists bridge_name =
let cmd =
match Lazy.force uname with
| FreeBSD -> Bos.Cmd.(v "ifconfig" % bridge_name)
| Linux -> Bos.Cmd.(v "ip" % "link" % "show" % bridge_name)
in
Bos.OS.Cmd.(run_out ~err:err_null cmd |> out_null |> success)
|> R.reword_error (fun _e -> R.msgf "interface %s does not exist" bridge_name)
let bridges_exist bridges =
List.fold_left
(fun acc b -> acc >>= fun () -> bridge_exists (bridge_name b))
(Ok ()) bridges
let prepare name vm = let prepare name vm =
(match vm.Unikernel.typ with (match vm.Unikernel.typ with
@ -231,11 +256,8 @@ let prepare name vm =
Bos.OS.File.write filename (Cstruct.to_string image) >>= fun () -> Bos.OS.File.write filename (Cstruct.to_string image) >>= fun () ->
solo5_image_target filename >>= fun target -> solo5_image_target filename >>= fun target ->
check_solo5_cmd (solo5_tender target) >>= fun _ -> check_solo5_cmd (solo5_tender target) >>= fun _ ->
solo5_image_devices filename >>= fun (block_devices, networks) -> manifest_devices_match ~bridges:vm.Unikernel.bridges ~block_devices:vm.Unikernel.block_devices filename >>= fun () ->
(if equal_blocks vm.Unikernel.block_devices block_devices then Ok () bridges_exist vm.Unikernel.bridges >>= fun () ->
else R.error_msg "specified block device(s) does not match with manifest") >>= fun () ->
(if equal_networks vm.Unikernel.bridges networks then Ok ()
else R.error_msg "specified bridge(s) does not match with the manifest") >>= fun () ->
let fifo = Name.fifo_file name in let fifo = Name.fifo_file name in
begin match fifo_exists fifo with begin match fifo_exists fifo with
| Ok true -> Ok () | Ok true -> Ok ()
@ -252,11 +274,11 @@ let prepare name vm =
let _ = Unix.umask old_umask in let _ = Unix.umask old_umask in
R.error_msgf "file %a error in %s: %a" Fpath.pp fifo f pp_unix_err e R.error_msgf "file %a error in %s: %a" Fpath.pp fifo f pp_unix_err e
end >>= fun () -> end >>= fun () ->
List.fold_left (fun acc (net, bri) -> List.fold_left (fun acc arg ->
acc >>= fun acc -> acc >>= fun acc ->
let bridge = match bri with None -> net | Some b -> b in let bridge = bridge_name arg in
create_tap bridge >>= fun tap -> create_tap bridge >>= fun tap ->
Ok ((net, tap) :: acc)) Ok ((fst arg, tap) :: acc))
(Ok []) vm.Unikernel.bridges >>= fun taps -> (Ok []) vm.Unikernel.bridges >>= fun taps ->
Ok (List.rev taps) Ok (List.rev taps)

View file

@ -38,5 +38,5 @@ val restore : ?name:string -> unit -> (Cstruct.t, [> R.msg | `NoFile ]) result
val vm_device : Unikernel.t -> (string, [> R.msg ]) result val vm_device : Unikernel.t -> (string, [> R.msg ]) result
(* XXX: remove? *) val manifest_devices_match : bridges:(string * string option) list ->
val solo5_image_devices : Fpath.t -> (string list * (string * string option) list , [> R.msg]) result block_devices:string list -> Fpath.t -> (unit, [> R.msg]) result