-
Notifications
You must be signed in to change notification settings - Fork 292
Add VM.sysprep API call #6547
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Add VM.sysprep API call #6547
Conversation
Did you mean to assign Rob and Edwin as reviewers, not assignees? |
I can guess the big-picture intentions behind this, but as someone not terribly familiar with Windows setup, it could be worth spelling out why this is wanted and what this simplifies and improves |
It's coming from a customer requirement: supporting a way to customise a Windows VM using Windows tools. The challenge is how to get a file to the VM such that a tool in the VM can read it. My preferred method would have been providing this via the network but this does not work when the VM has no network yet set up. So we end up with a somewhat convoluted approach to create an ISO that we provide via CD ROM to the VM. This is currently disabled via a flag. |
5c8c133
to
7ecd772
Compare
, { | ||
reqd= ["filename"] | ||
; optn= [] | ||
; help= "Pass and execure sysprep configuration file" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo
ocaml/xapi/vm_sysprep.ml
Outdated
|
||
let finally = Xapi_stdext_pervasives.Pervasiveext.finally | ||
|
||
let genisoimage = "/usr/bin/genisoimage" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the usual way to refer to these is by declaring it in Xapi_globs.Resources, perhaps as an optional runtime dependency (if the file is missing we won't be able to sysprep, but we should still be able to perform other ops).
ocaml/xapi/vm_sysprep.ml
Outdated
let genisoimage = "/usr/bin/genisoimage" | ||
|
||
(** This will be shown to the user to explain a failure *) | ||
exception Failure of string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this can be confusing, because failwith
also raises a builtin Failure
exception, can we use a different name that doesn't clash with stdlib builtins?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had that thought; can rename it.
ocaml/xapi/vm_sysprep.ml
Outdated
(** create a name with a random infix. We need random names for | ||
temporay directories to avoid collition *) | ||
let temp_name prefix suffix = | ||
let rnd = Random.State.bits (Lazy.force prng) land 0xFFFFFF in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lazy doesn't play nicely with multithreaded code (in OCaml 4.14.2 it can actually segfault, although that is fixed on the 4.14 branch, and we should also really apply that patch to our internal compiler), also sharing a Random.State between threads is probably worse than using the default random state (which in OCaml 5 has some code to make it domain-local).
To avoid issues with lazy can you initialize this fully on startup like this:
let prng = Random.State.make_self_init ()
Then even if we have race conditions on this state, it'll only affect this prng, and even if we end up with same random number in 2 threads that just means we'll fail to generate the temp name and retry.
We have multiple PRNGs in XAPI already though, see e.g. xapi_session.ml where we use Mirage_crypto_rng.generate
, although in this case we don't necessarily need cryptographic quality random numbers.
2d29a74
to
ffd8aed
Compare
ffd8aed
to
782a6d6
Compare
Add a new API call for VM sysprep and the corresponding XE implementation. This is mostly scaffolding. Signed-off-by: Christian Lindig <[email protected]>
We want to create a temporary directory that will be used to hold files for creating an ISO. There is no existing function that creates all necessary directories in a predicatble way. Signed-off-by: Christian Lindig <[email protected]>
Implement creating an ISO from a temporary directory Signed-off-by: Christian Lindig <[email protected]>
Signed-off-by: Christian Lindig <[email protected]>
Create a local SR unless it exists. Signed-off-by: Christian Lindig <[email protected]>
We need to locate the CD drive of the VM. Signed-off-by: Christian Lindig <[email protected]>
The VM is notified to perform a sysprep by writing to XenStore. The VM picks this up via its guest agent. Signed-off-by: Christian Lindig <[email protected]>
Signed-off-by: Christian Lindig <[email protected]>
Introduce on_startup(), called on xapi startup, to clean up the local SR to avoid accumulating ISO files. Signed-off-by: Christian Lindig <[email protected]>
For simplicity, add vm-sysprep-enabled = true/false to xapi.conf rather than using a full V6D feature flag. Signed-off-by: Christian Lindig <[email protected]>
Signed-off-by: Christian Lindig <[email protected]>
Signed-off-by: Christian Lindig <[email protected]>
Add the paths to genisoimage so xapi_globs and list it as non-essential binary. Signed-off-by: Christian Lindig <[email protected]>
782a6d6
to
6e37536
Compare
~policy (fun () -> | ||
forward_vm_op ~local_fn ~__context ~vm:self ~remote_fn | ||
) ; | ||
Xapi_vm_lifecycle.update_allowed_operations ~__context ~self |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
with_vm_operation
already does this.
@@ -239,6 +239,8 @@ let prototyped_of_message = function | |||
Some "25.2.0" | |||
| "host", "set_numa_affinity_policy" -> | |||
Some "24.0.0" | |||
| "VM", "sysprep" -> | |||
Some "25.22.0" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs a refresh
|
||
(* VM.sysprep *) | ||
|
||
(* Using a single error during development, might want to expand this |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove this now that the code is stable
Db.SR.get_VDIs ~__context ~self:sr | ||
|> List.iter @@ fun self -> | ||
match Db.VDI.get_record ~__context ~self with | ||
| API.{vDI_VBDs= []; vDI_location= _location; _} -> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is vDI_location= _location
doing? I don't see _location
defined anywhere?
) | ||
|
||
(** create a name with a random infix. We need random names for | ||
temporay directories to avoid collition *) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"temporay"... "collition"... random mutations? :)
let device_config = [("location", SR.dir); ("legacy_mode", "true")] in | ||
Xapi_sr.create ~__context ~host ~name_label:label ~device_config | ||
~content_type:"iso" ~_type:"iso" ~name_description:"Sysprep ISOs" | ||
~shared:false ~sm_config:[] ~physical_size:(mib 512) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why 512MiB if the XML file is limited to just 32KiB?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The SR holds multiple ISOs, each is about 300k; we remove them after each API call. I am not sure the 512MB is enforced by SM.
match xs.Xs.read (control // "feature-sysprep") with | ||
| "1" -> | ||
debug "%s: VM %s supports sysprep" __FUNCTION__ vm_uuid | ||
| _ -> | ||
fail "VM %s does not support sysprep" vm_uuid | ||
| exception _ -> | ||
fail "VM %s does not support sysprep" vm_uuid | ||
) ; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could eventually be replaced by allowed_operations checking (see check_op_for_feature
in xapi_vm_lifecycle.ml
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this test is good because it checks against the driver directly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is, and so does the xapi_vm_lifecycle.ml check, which is based on xapi_guest_agent.ml putting all those control keys (including all feature- keys) into VM_guest_metrics.other
(which is a bad name).
E.g. on a VM on my machine:
let vdi = find_vdi ~__context ~label in | ||
debug "%s: inserting Sysprep VDI for VM %s" __FUNCTION__ vm_uuid ; | ||
Xapi_vbd.insert ~__context ~vdi ~vbd ; | ||
Thread.delay 5.0 ; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice if the guest agent can watch for the device to appear rather than adding an arbitrary delay here...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand the desire; but we also need to be sure it has seen the ISO as otherwise we can't tell it to read from it. So this would require a back channel via xenstore that we watch
Xapi_vbd.eject ~__context ~vbd ; | ||
Sys.remove iso ; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Two duplicated lines could be factored out.
; ( "Remove local ISO SR" | ||
, [Startup.OnThread] | ||
, fun () -> Vm_sysprep.on_startup ~__context | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be moved after the block below, where the master connection is established if running on a pool member, and dbsync has run.
A new API call that works in collaboration with a guest agent on a Windows VM to pass a
unattend.xml
to Windows sysprep.unattended.xml
and inserts the ISO into the VMs CD-ROM, then notifies the VM via xenstore.vm_sysprep.ml
.Xapi_vm.sysprep()