Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion ocaml/idl/datamodel_vm.ml
Original file line number Diff line number Diff line change
Expand Up @@ -2376,8 +2376,12 @@ let sysprep =
[
(Ref _vm, "self", "The VM")
; (String, "unattend", "XML content passed to sysprep")
; (Float, "timeout", "timeout in seconds for expected reboot")
]
~doc:"Pass unattend.xml to Windows sysprep" ~allowed_roles:_R_VM_ADMIN ()
~doc:
"Pass unattend.xml to Windows sysprep and wait for the VM to shut down \
as part of a reboot."
~allowed_roles:_R_VM_ADMIN ()

let vm_uefi_mode =
Enum
Expand Down
2 changes: 1 addition & 1 deletion ocaml/xapi-cli-server/cli_frontend.ml
Original file line number Diff line number Diff line change
Expand Up @@ -2767,7 +2767,7 @@ let rec cmdtable_data : (string * cmd_spec) list =
; ( "vm-sysprep"
, {
reqd= ["filename"]
; optn= []
; optn= ["timeout"]
; help= "Pass and execute sysprep configuration file"
; implementation= With_fd Cli_operations.vm_sysprep
; flags= [Vm_selectors]
Expand Down
12 changes: 11 additions & 1 deletion ocaml/xapi-cli-server/cli_operations.ml
Original file line number Diff line number Diff line change
Expand Up @@ -3590,6 +3590,15 @@ let vm_data_source_forget printer rpc session_id params =

let vm_sysprep fd printer rpc session_id params =
let filename = List.assoc "filename" params in
let timeout =
match List.assoc "timeout" params |> float_of_string with
| exception _ ->
3.0 *. 60.0 (* default in the CLI, no default in the API *)
| s when s < 0.0 ->
0.0
| s ->
s
in
let unattend =
match get_client_file fd filename with
| Some xml ->
Expand All @@ -3602,8 +3611,9 @@ let vm_sysprep fd printer rpc session_id params =
(do_vm_op printer rpc session_id
(fun vm ->
Client.VM.sysprep ~rpc ~session_id ~self:(vm.getref ()) ~unattend
~timeout
)
params ["filename"]
params ["filename"; "timeout"]
)

(* APIs to collect SR level RRDs *)
Expand Down
6 changes: 3 additions & 3 deletions ocaml/xapi/message_forwarding.ml
Original file line number Diff line number Diff line change
Expand Up @@ -3116,10 +3116,10 @@ functor
Local.VM.remove_from_blocked_operations ~__context ~self ~key ;
Xapi_vm_lifecycle.update_allowed_operations ~__context ~self

let sysprep ~__context ~self ~unattend =
let sysprep ~__context ~self ~unattend ~timeout =
info "VM.sysprep: self = '%s'" (vm_uuid ~__context self) ;
let local_fn = Local.VM.sysprep ~self ~unattend in
let remote_fn = Client.VM.sysprep ~self ~unattend in
let local_fn = Local.VM.sysprep ~self ~unattend ~timeout in
let remote_fn = Client.VM.sysprep ~self ~unattend ~timeout in
let policy = Helpers.Policy.fail_immediately in
with_vm_operation ~__context ~self ~doc:"VM.sysprep" ~op:`sysprep
~policy (fun () ->
Expand Down
28 changes: 18 additions & 10 deletions ocaml/xapi/vm_sysprep.ml
Original file line number Diff line number Diff line change
Expand Up @@ -207,27 +207,36 @@ let find_vdi ~__context ~label =

(** notify the VM with [domid] to run sysprep and where to find the
file. *)
let trigger ~domid ~uuid =
let trigger ~domid ~uuid ~timeout =
let open Ezxenstore_core.Xenstore in
let module Watch = Ezxenstore_core.Watch in
let control = Printf.sprintf "/local/domain/%Ld/control/sysprep" domid in
let domain = Printf.sprintf "/local/domain/%Ld" domid in
with_xs (fun xs ->
xs.Xs.write (control // "filename") "D://unattend.xml" ;
xs.Xs.write (control // "vdi-uuid") uuid ;
xs.Xs.write (control // "action") "sysprep" ;
debug "%s: notified domain %Ld" __FUNCTION__ domid ;
try
(* wait for sysprep to start, then domain to dissapear *)
Watch.(
wait_for ~xs ~timeout:5.0
(value_to_become (control // "action") "running")
) ;
"running"
with Watch.Timeout _ -> xs.Xs.read (control // "action")
debug "%s: sysprep is runnung; waiting for sysprep to finish"
__FUNCTION__ ;
Watch.(wait_for ~xs ~timeout (key_to_disappear (control // "action"))) ;
debug "%s sysprep is finished" __FUNCTION__ ;
Watch.(wait_for ~xs ~timeout (key_to_disappear domain)) ;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible that the xapi sees the last change of disappear directly without seeing the changes above? In other words, can the xenstore ensure the individual consecutive changes will never be combined?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would rule out sysprep terminating and the domain disappearing together. But that is an argument based on how the Windows domain works. not how xapi and xenstore work.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the domain disappears then all keys underneath it disappear too, so the 'action' will always disappear on shutdown/reboot.

true
with Watch.Timeout _ ->
debug "%s: sysprep timeout" __FUNCTION__ ;
false
)

(* This function is executed on the host where [vm] is running *)
let sysprep ~__context ~vm ~unattend =
debug "%s" __FUNCTION__ ;
let sysprep ~__context ~vm ~unattend ~timeout =
debug "%s (timeout %f)" __FUNCTION__ timeout ;
if not !Xapi_globs.vm_sysprep_enabled then
fail API_not_enabled ;
let vm_uuid = Db.VM.get_uuid ~__context ~self:vm in
Expand Down Expand Up @@ -259,14 +268,13 @@ let sysprep ~__context ~vm ~unattend =
call ~__context @@ fun rpc session_id ->
Client.VBD.insert ~rpc ~session_id ~vdi ~vbd ;
Thread.delay !Xapi_globs.vm_sysprep_wait ;
match trigger ~domid ~uuid with
| "running" ->
match trigger ~domid ~uuid ~timeout with
| true ->
debug "%s: sysprep running, ejecting CD" __FUNCTION__ ;
Thread.delay 1.0 ;
Client.VBD.eject ~rpc ~session_id ~vbd ;
Sys.remove iso
| status ->
debug "%s: sysprep %S, ejecting CD" __FUNCTION__ status ;
| false ->
debug "%s: sysprep timeout, ejecting CD" __FUNCTION__ ;
Client.VBD.eject ~rpc ~session_id ~vbd ;
Sys.remove iso ;
fail VM_sysprep_timeout
7 changes: 6 additions & 1 deletion ocaml/xapi/vm_sysprep.mli
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,12 @@ exception Sysprep of error
val on_startup : __context:Context.t -> unit
(** clean up on toolstart start up *)

val sysprep : __context:Context.t -> vm:API.ref_VM -> unattend:string -> unit
val sysprep :
__context:Context.t
-> vm:API.ref_VM
-> unattend:string
-> timeout:float
-> unit
(** Execute sysprep on [vm] using script [unattend]. This requires
driver support from the VM and is checked. [unattend:string] must
not exceed 32kb. Raised [Failure] that must be handled, *)
11 changes: 8 additions & 3 deletions ocaml/xapi/xapi_vm.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1702,10 +1702,15 @@ let get_secureboot_readiness ~__context ~self =
)
)

let sysprep ~__context ~self ~unattend =
let sysprep ~__context ~self ~unattend ~timeout =
let uuid = Db.VM.get_uuid ~__context ~self in
debug "%s %S" __FUNCTION__ uuid ;
match Vm_sysprep.sysprep ~__context ~vm:self ~unattend with
debug "%s %S (timeout %f)" __FUNCTION__ uuid timeout ;
if timeout < 0.0 then
raise
Api_errors.(
Server_error (invalid_value, ["timeout"; string_of_float timeout])
) ;
match Vm_sysprep.sysprep ~__context ~vm:self ~unattend ~timeout with
| () ->
debug "%s %S success" __FUNCTION__ uuid ;
()
Expand Down
7 changes: 6 additions & 1 deletion ocaml/xapi/xapi_vm.mli
Original file line number Diff line number Diff line change
Expand Up @@ -451,4 +451,9 @@ val add_to_blocked_operations :
val remove_from_blocked_operations :
__context:Context.t -> self:API.ref_VM -> key:API.vm_operations -> unit

val sysprep : __context:Context.t -> self:API.ref_VM -> unattend:string -> unit
val sysprep :
__context:Context.t
-> self:API.ref_VM
-> unattend:string
-> timeout:float
-> unit
Loading