albatross/src/vmm_vmmd.ml

256 lines
9.6 KiB
OCaml
Raw Normal View History

(* (c) 2017, 2018 Hannes Mehnert, all rights reserved *)
2017-05-26 14:30:34 +00:00
open Astring
open Vmm_core
open Rresult
open R.Infix
type 'a t = {
2018-10-23 22:03:36 +00:00
wire_version : Vmm_commands.version ;
console_counter : int64 ;
stats_counter : int64 ;
log_counter : int64 ;
2017-05-26 14:30:34 +00:00
resources : Vmm_resources.t ;
tasks : 'a String.Map.t ;
2017-05-26 14:30:34 +00:00
}
let kill t =
List.iter Vmm_unix.destroy
(List.map snd (Vmm_trie.all t.resources.Vmm_resources.unikernels))
2018-11-10 00:02:07 +00:00
let init wire_version =
let t = {
wire_version ;
console_counter = 1L ;
stats_counter = 1L ;
log_counter = 1L ;
resources = Vmm_resources.empty ;
tasks = String.Map.empty ;
} in
match Vmm_unix.find_block_devices () with
| Error (`Msg msg) ->
Logs.warn (fun m -> m "couldn't find block devices %s" msg) ;
t
| Ok devs ->
let resources =
List.fold_left (fun r (id, size) ->
match Vmm_resources.insert_block r id size with
| Error (`Msg msg) ->
2018-11-13 00:02:05 +00:00
Logs.err (fun m -> m "couldn't insert block device %a (%dMB): %s" Name.pp id size msg) ;
2018-11-10 00:02:07 +00:00
r
| Ok r -> r)
t.resources devs
in
{ t with resources }
2017-05-26 14:30:34 +00:00
2018-10-22 21:20:00 +00:00
type service_out = [
2018-10-23 22:03:36 +00:00
| `Stat of Vmm_commands.wire
| `Log of Vmm_commands.wire
| `Cons of Vmm_commands.wire
2018-10-22 21:20:00 +00:00
]
2018-10-23 22:03:36 +00:00
type out = [ service_out | `Data of Vmm_commands.wire ]
2018-10-22 21:20:00 +00:00
let log t name event =
2018-10-23 22:03:36 +00:00
let data = (Ptime_clock.now (), event) in
let header = Vmm_commands.{ version = t.wire_version ; sequence = t.log_counter ; name } in
2018-10-22 21:20:00 +00:00
let log_counter = Int64.succ t.log_counter in
2018-10-23 22:03:36 +00:00
Logs.debug (fun m -> m "log %a" Log.pp data) ;
({ t with log_counter }, `Log (header, `Data (`Log_data data)))
2017-05-26 14:30:34 +00:00
2018-11-10 00:02:07 +00:00
let handle_create t reply name vm_config =
2018-10-22 22:54:05 +00:00
(match Vmm_resources.find_vm t.resources name with
| Some _ -> Error (`Msg "VM with same name is already running")
| None -> Ok ()) >>= fun () ->
Logs.debug (fun m -> m "now checking resource policies") ;
Vmm_resources.check_vm t.resources name vm_config >>= fun () ->
2017-05-26 14:30:34 +00:00
(* prepare VM: save VM image to disk, create fifo, ... *)
2018-10-22 22:54:05 +00:00
Vmm_unix.prepare name vm_config >>= fun taps ->
Logs.debug (fun m -> m "prepared vm with taps %a" Fmt.(list ~sep:(unit ",@ ") string) taps) ;
2018-10-22 23:36:44 +00:00
let cons_out =
let header = Vmm_commands.{ version = t.wire_version ; sequence = t.console_counter ; name } in
2018-10-22 23:36:44 +00:00
(header, `Command (`Console_cmd `Console_add))
in
Ok ({ t with console_counter = Int64.succ t.console_counter },
[ `Cons cons_out ],
`Create (fun t task ->
(* actually execute the vm *)
2018-11-13 00:02:05 +00:00
let block_device = match vm_config.Unikernel.block_device with
| None -> None
| Some block -> Some (Name.block_name name block)
in
2018-11-10 00:02:07 +00:00
Vmm_unix.exec name vm_config taps block_device >>= fun vm ->
Logs.debug (fun m -> m "exec()ed vm") ;
2018-10-22 22:54:05 +00:00
Vmm_resources.insert_vm t.resources name vm >>= fun resources ->
let tasks = String.Map.add (Name.to_string name) task t.tasks in
2018-10-23 20:14:28 +00:00
let t = { t with resources ; tasks } in
2018-11-13 00:02:05 +00:00
let t, out = log t name (`Unikernel_start (name, vm.Unikernel.pid, vm.Unikernel.taps, None)) in
2018-11-10 00:02:07 +00:00
Ok (t, [ reply (`String "created VM") ; out ], name, vm)))
2018-10-22 22:54:05 +00:00
let setup_stats t name vm =
let stat_out =
let pid = vm.Unikernel.pid in
let name = "solo5-" ^ string_of_int pid
and ifs = Unikernel.(List.combine vm.config.network_interfaces vm.taps)
in
`Stats_add (name, pid, ifs)
in
let header = Vmm_commands.{ version = t.wire_version ; sequence = t.stats_counter ; name } in
let t = { t with stats_counter = Int64.succ t.stats_counter } in
t, `Stat (header, `Command (`Stats_cmd stat_out))
2017-05-26 14:30:34 +00:00
2018-10-22 22:54:05 +00:00
let handle_shutdown t name vm r =
(match Vmm_unix.shutdown name vm with
2017-05-26 14:30:34 +00:00
| Ok () -> ()
2018-11-13 00:02:05 +00:00
| Error (`Msg e) -> Logs.warn (fun m -> m "%s while shutdown vm %a" e Unikernel.pp vm)) ;
let resources = match Vmm_resources.remove_vm t.resources name with
| Error (`Msg e) ->
2018-11-13 00:02:05 +00:00
Logs.warn (fun m -> m "%s while removing vm %a from resources" e Unikernel.pp vm) ;
t.resources
| Ok resources -> resources
in
let header = Vmm_commands.{ version = t.wire_version ; sequence = t.stats_counter ; name } in
let tasks = String.Map.remove (Name.to_string name) t.tasks in
2018-10-23 20:14:28 +00:00
let t = { t with stats_counter = Int64.succ t.stats_counter ; resources ; tasks } in
2018-11-13 00:02:05 +00:00
let t, logout = log t name (`Unikernel_stop (name, vm.Unikernel.pid, r))
in
2018-10-23 21:11:22 +00:00
(t, [ `Stat (header, `Command (`Stats_cmd `Stats_remove)) ; logout ])
2018-11-10 00:02:07 +00:00
let handle_policy_cmd t reply id = function
| `Policy_remove ->
Logs.debug (fun m -> m "remove policy %a" Name.pp id) ;
2018-11-10 00:02:07 +00:00
Vmm_resources.remove_policy t.resources id >>= fun resources ->
Ok ({ t with resources }, [ reply (`String "removed policy") ], `End)
| `Policy_add policy ->
Logs.debug (fun m -> m "insert policy %a" Name.pp id) ;
2018-11-10 00:02:07 +00:00
let same_policy = match Vmm_resources.find_policy t.resources id with
| None -> false
2018-11-11 02:09:37 +00:00
| Some p' -> Policy.equal policy p'
2018-11-10 00:02:07 +00:00
in
if same_policy then
Ok (t, [ reply (`String "no modification of policy") ], `Loop)
else
Vmm_resources.insert_policy t.resources id policy >>= fun resources ->
Ok ({ t with resources }, [ reply (`String "added policy") ], `Loop)
| `Policy_info ->
Logs.debug (fun m -> m "policy %a" Name.pp id) ;
2018-11-10 00:02:07 +00:00
let policies =
Vmm_trie.fold id t.resources.Vmm_resources.policies
2018-11-10 00:02:07 +00:00
(fun prefix policy policies-> (prefix, policy) :: policies)
[]
in
match policies with
| [] ->
Logs.debug (fun m -> m "policies: couldn't find %a" Name.pp id) ;
2018-11-10 00:02:07 +00:00
Error (`Msg "policy: not found")
| _ ->
Ok (t, [ reply (`Policies policies) ], `End)
2018-11-13 00:02:05 +00:00
let handle_unikernel_cmd t reply id msg_to_err = function
| `Unikernel_info ->
Logs.debug (fun m -> m "info %a" Name.pp id) ;
2018-11-10 00:02:07 +00:00
let vms =
Vmm_trie.fold id t.resources.Vmm_resources.unikernels
2018-11-13 00:02:05 +00:00
(fun id vm vms -> (id, vm.Unikernel.config) :: vms)
2018-11-10 00:02:07 +00:00
[]
in
begin match vms with
| [] ->
Logs.debug (fun m -> m "info: couldn't find %a" Name.pp id) ;
2018-11-13 00:02:05 +00:00
Error (`Msg "info: no unikernel found")
2018-11-10 00:02:07 +00:00
| _ ->
2018-11-13 00:02:05 +00:00
Ok (t, [ reply (`Unikernels vms) ], `End)
2018-11-10 00:02:07 +00:00
end
2018-11-13 00:02:05 +00:00
| `Unikernel_create vm_config -> handle_create t reply id vm_config
| `Unikernel_force_create vm_config ->
2018-11-10 00:02:07 +00:00
begin
let resources =
match Vmm_resources.remove_vm t.resources id with
| Error _ -> t.resources
| Ok r -> r
in
Vmm_resources.check_vm resources id vm_config >>= fun () ->
match Vmm_resources.find_vm t.resources id with
| None -> handle_create t reply id vm_config
| Some vm ->
Vmm_unix.destroy vm ;
let id_str = Name.to_string id in
match String.Map.find_opt id_str t.tasks with
2018-11-10 00:02:07 +00:00
| None -> handle_create t reply id vm_config
| Some task ->
let tasks = String.Map.remove id_str t.tasks in
let t = { t with tasks } in
Ok (t, [], `Wait_and_create
(task, fun t -> msg_to_err @@ handle_create t reply id vm_config))
2018-11-10 00:02:07 +00:00
end
2018-11-13 00:02:05 +00:00
| `Unikernel_destroy ->
2018-11-10 00:02:07 +00:00
match Vmm_resources.find_vm t.resources id with
| Some vm ->
Vmm_unix.destroy vm ;
let id_str = Name.to_string id in
2018-11-10 00:02:07 +00:00
let out, next =
2018-11-13 00:02:05 +00:00
let s = reply (`String "destroyed unikernel") in
2018-11-10 00:02:07 +00:00
match String.Map.find_opt id_str t.tasks with
| None -> [ s ], `End
| Some t -> [], `Wait (t, s)
in
let tasks = String.Map.remove id_str t.tasks in
Ok ({ t with tasks }, out, next)
| None -> Error (`Msg "destroy: not found")
let handle_block_cmd t reply id = function
| `Block_remove ->
Logs.debug (fun m -> m "removing block %a" Name.pp id) ;
2018-11-10 00:02:07 +00:00
begin match Vmm_resources.find_block t.resources id with
| None -> Error (`Msg "remove block: not found")
| Some (_, true) -> Error (`Msg "remove block: is in use")
| Some (_, false) ->
Vmm_unix.destroy_block id >>= fun () ->
Vmm_resources.remove_block t.resources id >>= fun resources ->
Ok ({ t with resources }, [ reply (`String "removed block") ], `End)
end
| `Block_add size ->
begin
Logs.debug (fun m -> m "insert block %a: %dMB" Name.pp id size) ;
2018-11-10 00:02:07 +00:00
match Vmm_resources.find_block t.resources id with
| Some _ -> Error (`Msg "block device with same name already exists")
| None ->
Vmm_resources.check_block t.resources id size >>= fun () ->
Vmm_unix.create_block id size >>= fun () ->
Vmm_resources.insert_block t.resources id size >>= fun resources ->
Ok ({ t with resources }, [ reply (`String "added block device") ], `Loop)
2018-11-10 00:02:07 +00:00
end
| `Block_info ->
Logs.debug (fun m -> m "block %a" Name.pp id) ;
2018-11-10 00:02:07 +00:00
let blocks =
Vmm_trie.fold id t.resources.Vmm_resources.block_devices
(fun prefix (size, active) blocks -> (prefix, size, active) :: blocks)
2018-11-10 00:02:07 +00:00
[]
in
match blocks with
| [] ->
Logs.debug (fun m -> m "block: couldn't find %a" Name.pp id) ;
2018-11-10 00:02:07 +00:00
Error (`Msg "block: not found")
| _ ->
2018-11-13 00:02:05 +00:00
Ok (t, [ reply (`Block_devices blocks) ], `End)
2018-11-10 00:02:07 +00:00
2018-10-22 21:20:00 +00:00
let handle_command t (header, payload) =
let msg_to_err = function
| Ok x -> x
| Error (`Msg msg) ->
Logs.err (fun m -> m "error while processing command: %s" msg) ;
(t, [ `Data (header, `Failure msg) ], `End)
2018-11-10 00:02:07 +00:00
and reply x = `Data (header, `Success x)
and id = header.Vmm_commands.name
in
msg_to_err (
2018-10-22 21:20:00 +00:00
match payload with
2018-11-10 00:02:07 +00:00
| `Command (`Policy_cmd pc) -> handle_policy_cmd t reply id pc
2018-11-13 00:02:05 +00:00
| `Command (`Unikernel_cmd vc) -> handle_unikernel_cmd t reply id msg_to_err vc
2018-11-10 00:02:07 +00:00
| `Command (`Block_cmd bc) -> handle_block_cmd t reply id bc
2018-10-22 23:02:14 +00:00
| _ ->
2018-10-23 22:03:36 +00:00
Logs.err (fun m -> m "ignoring %a" Vmm_commands.pp_wire (header, payload)) ;
2018-10-22 23:02:14 +00:00
Error (`Msg "unknown command"))