Skip to content
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

Add ignore_vdis to VM.snapshot method #4563

Merged
merged 1 commit into from
Dec 7, 2021
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
7 changes: 4 additions & 3 deletions ocaml/idl/datamodel_vm.ml
Original file line number Diff line number Diff line change
Expand Up @@ -247,9 +247,10 @@ let power_behaviour =
~in_product_since: rel_orlando
~doc:"Snapshots the specified VM, making a new VM. Snapshot automatically exploits the capabilities of the underlying storage repository in which the VM's disk images are stored (e.g. Copy on Write)."
~result: (Ref _vm, "The reference of the newly created VM.")
~params:[
Ref _vm, "vm", "The VM to be snapshotted";
String, "new_name", "The name of the snapshotted VM"
~versioned_params:
[{param_type=Ref _vm; param_name="vm"; param_doc="The VM to be snapshotted"; param_release=orlando_release; param_default=None};
{param_type=String; param_name="new_name"; param_doc="The name of the snapshotted VM"; param_release=orlando_release; param_default=None};
{param_type=Set(Ref _vdi); param_name="ignore_vdis"; param_doc="A list of VDIs to ignore for the snapshot"; param_release=next_release; param_default=Some (VSet [])};
]
~errs:[Api_errors.vm_bad_power_state; Api_errors.sr_full; Api_errors.operation_not_allowed]
~allowed_roles:_R_VM_POWER_ADMIN
Expand Down
1 change: 1 addition & 0 deletions ocaml/quicktest/quicktest.ml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ let () =
; ("Quicktest_async_calls", Quicktest_async_calls.tests ())
; ("Quicktest_vm_import_export", Quicktest_vm_import_export.tests ())
; ("Quicktest_vm_lifecycle", Quicktest_vm_lifecycle.tests ())
; ("Quicktest_vm_snapshot", Quicktest_vm_snapshot.tests ())
; ( "Quicktest_vdi_ops_data_integrity"
, Quicktest_vdi_ops_data_integrity.tests ()
)
Expand Down
112 changes: 112 additions & 0 deletions ocaml/quicktest/quicktest_vm_snapshot.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
(** Set up snapshot test: create a small VM with a selection of VBDs *)
let with_setup rpc session_id sr vm_template f =
print_endline "Setting up test VM" ;
let uuid = Client.Client.VM.get_uuid rpc session_id vm_template in
print_endline (Printf.sprintf "Template has uuid: %s%!" uuid) ;
let vdi =
Client.Client.VDI.create rpc session_id "small" __LOC__ sr 4194304L `user
false false [] [] [] []
in
let vdi2 =
Client.Client.VDI.create rpc session_id "small2" __LOC__ sr 4194304L `user
false false [] [] [] []
in
Qt.VM.with_new rpc session_id ~template:vm_template (fun vm ->
print_endline (Printf.sprintf "Installed new VM") ;
print_endline
(Printf.sprintf "Using SR: %s"
(Client.Client.SR.get_name_label ~rpc ~session_id ~self:sr)
) ;
ignore
(Client.Client.VBD.create ~rpc ~session_id ~vM:vm ~vDI:vdi
~userdevice:"0" ~bootable:false ~mode:`RW ~_type:`Disk
~unpluggable:true ~empty:false ~other_config:[]
~qos_algorithm_type:"" ~qos_algorithm_params:[] ~device:""
~currently_attached:true
) ;
ignore
(Client.Client.VBD.create ~rpc ~session_id ~vM:vm ~vDI:vdi2
~userdevice:"1" ~bootable:false ~mode:`RW ~_type:`Disk
~unpluggable:true ~empty:false ~other_config:[]
~qos_algorithm_type:"" ~qos_algorithm_params:[] ~device:""
~currently_attached:true
) ;
f rpc session_id vm vdi vdi2 ;
Client.Client.VDI.destroy rpc session_id vdi ;
Client.Client.VDI.destroy rpc session_id vdi2
)

let test_snapshot rpc session_id vm vdi vdi2 =
let snapshot = Client.Client.VM.snapshot rpc session_id vm "Snapshot" [] in
let vbds = Client.Client.VM.get_VBDs rpc session_id snapshot in
let snap_vbd =
match
List.find_opt
(fun vbd -> Client.Client.VBD.get_userdevice rpc session_id vbd = "0")
vbds
with
| None ->
Alcotest.fail "Couldn't find VBD on snapshot"
| Some vbd ->
vbd
in
let snap_vbd2 =
match
List.find_opt
(fun vbd -> Client.Client.VBD.get_userdevice rpc session_id vbd = "1")
vbds
with
| None ->
Alcotest.fail "Couldn't find VBD on snapshot"
| Some vbd ->
vbd
in
let snap_vdi = Client.Client.VBD.get_VDI rpc session_id snap_vbd in
let snap_vdi2 = Client.Client.VBD.get_VDI rpc session_id snap_vbd2 in
let orig_vdi = Client.Client.VDI.get_snapshot_of rpc session_id snap_vdi in
let orig_vdi2 = Client.Client.VDI.get_snapshot_of rpc session_id snap_vdi2 in
assert (orig_vdi = vdi) ;
assert (orig_vdi2 = vdi2)

let test_snapshot_ignore_vdi rpc session_id vm vdi vdi2 =
let snapshot =
Client.Client.VM.snapshot rpc session_id vm "Snapshot" [vdi2]
in
let vbds = Client.Client.VM.get_VBDs rpc session_id snapshot in
let snap_vbd =
match
List.find_opt
(fun vbd -> Client.Client.VBD.get_userdevice rpc session_id vbd = "0")
vbds
with
| None ->
Alcotest.fail "Couldn't find VBD on snapshot"
| Some vbd ->
vbd
in
assert (
not
(List.exists
(fun vbd -> Client.Client.VBD.get_userdevice rpc session_id vbd = "1")
vbds
)
) ;
let snap_vdi = Client.Client.VBD.get_VDI rpc session_id snap_vbd in
let orig_vdi = Client.Client.VDI.get_snapshot_of rpc session_id snap_vdi in
assert (orig_vdi = vdi)

let test rpc session_id sr_info vm_template () =
let sr = sr_info.Qt.sr in
List.iter
(with_setup rpc session_id sr vm_template)
[test_snapshot; test_snapshot_ignore_vdi]

let tests () =
let open Qt_filter in
[
[("VM snapshot tests", `Slow, test)]
|> conn
|> sr SR.(all |> allowed_operations [`vdi_create])
|> vm_template Qt.VM.Template.other
]
|> List.concat
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 @@ -1310,7 +1310,7 @@ let rec cmdtable_data : (string * cmd_spec) list =
; ( "vm-snapshot"
, {
reqd= ["new-name-label"]
; optn= ["new-name-description"]
; optn= ["new-name-description"; "ignore-vdi-uuids"]
; help=
"Snapshot an existing VM, using storage-level fast disk snapshot \
operation where available."
Expand Down
20 changes: 17 additions & 3 deletions ocaml/xapi-cli-server/cli_operations.ml
Original file line number Diff line number Diff line change
Expand Up @@ -4079,7 +4079,7 @@ let vm_clone_aux clone_op cloned_string printer include_template_vms rpc
do_vm_op printer ~include_template_vms rpc session_id
(fun vm -> clone_op ~rpc ~session_id ~vm:(vm.getref ()) ~new_name)
params
["new-name-label"; "new-name-description"]
["new-name-label"; "new-name-description"; "ignore-vdi-uuids"]
in
Option.iter
(fun desc ->
Expand All @@ -4093,8 +4093,22 @@ let vm_clone_aux clone_op cloned_string printer include_template_vms rpc

let vm_clone printer = vm_clone_aux Client.VM.clone "Cloned " printer true

let vm_snapshot printer =
vm_clone_aux Client.VM.snapshot "Snapshotted " printer false
let vm_snapshot printer rpc session_id params =
let ignore_vdis_uuids =
match List.assoc_opt "ignore-vdi-uuids" params with
| None ->
[]
| Some x ->
String.split_on_char ',' x
in
let ignore_vdis =
List.map
(fun vdi_uuid -> Client.VDI.get_by_uuid rpc session_id vdi_uuid)
benjamreis marked this conversation as resolved.
Show resolved Hide resolved
ignore_vdis_uuids
in
vm_clone_aux
(Client.VM.snapshot ~ignore_vdis)
"Snapshotted " printer false rpc session_id params

let vm_snapshot_with_quiesce printer =
vm_clone_aux Client.VM.snapshot_with_quiesce "Snapshotted" printer false
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 @@ -1577,10 +1577,10 @@ functor
~value:transportable_snapshot_id

(* almost a copy of the clone function *)
let snapshot ~__context ~vm ~new_name =
let snapshot ~__context ~vm ~new_name ~ignore_vdis =
info "VM.snapshot: VM = '%s'; new_name = '%s'" (vm_uuid ~__context vm)
new_name ;
let local_fn = Local.VM.snapshot ~vm ~new_name in
let local_fn = Local.VM.snapshot ~vm ~new_name ~ignore_vdis in
(* We mark the VM as snapshoting. We don't mark the disks; the implementation of the snapshot uses the API *)
(* to snapshot and lock the individual VDIs. We don't give any atomicity guarantees here but we do prevent *)
(* disk corruption. *)
Expand All @@ -1589,7 +1589,7 @@ functor
(fun () ->
forward_to_access_srs ~local_fn ~__context ~vm
(fun session_id rpc ->
Client.VM.snapshot rpc session_id vm new_name
Client.VM.snapshot rpc session_id vm new_name ignore_vdis
)
)
in
Expand Down
4 changes: 2 additions & 2 deletions ocaml/xapi/xapi_vm.ml
Original file line number Diff line number Diff line change
Expand Up @@ -757,9 +757,9 @@ let clone ~__context ~vm ~new_name =

(* We do call wait_in_line for snapshot and snapshot_with_quiesce because the locks are taken at *)
(* the VBD level (with pause/unpause mechanism *)
let snapshot ~__context ~vm ~new_name =
let snapshot ~__context ~vm ~new_name ~ignore_vdis =
TaskHelper.set_cancellable ~__context ;
Xapi_vm_snapshot.snapshot ~__context ~vm ~new_name
Xapi_vm_snapshot.snapshot ~__context ~vm ~new_name ~ignore_vdis

(* As we will destroy the domain ourself, we grab the vm_lock here in order to tell the event thread to *)
(* do not look at this domain. The message forwarding layer already checked that the VM reference we *)
Expand Down
6 changes: 5 additions & 1 deletion ocaml/xapi/xapi_vm.mli
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,11 @@ val clone :
__context:Context.t -> vm:API.ref_VM -> new_name:string -> [`VM] Ref.t

val snapshot :
__context:Context.t -> vm:API.ref_VM -> new_name:string -> [`VM] Ref.t
__context:Context.t
-> vm:API.ref_VM
-> new_name:string
-> ignore_vdis:[`VDI] API.Ref.t list
-> [`VM] Ref.t

val revert : __context:Context.t -> snapshot:[`VM] Ref.t -> unit

Expand Down
15 changes: 14 additions & 1 deletion ocaml/xapi/xapi_vm_clone.ml
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,8 @@ let make_driver_params () =
[(Constants._sm_epoch_hint, Uuid.to_string (Uuid.make_uuid ()))]

(* NB this function may be called when the VM is suspended for copy/clone operations. Snapshot can be done in live.*)
let clone ?snapshot_info_record disk_op ~__context ~vm ~new_name =
let clone ?snapshot_info_record ?(ignore_vdis = []) disk_op ~__context ~vm
~new_name =
Helpers.call_api_functions ~__context (fun rpc session_id ->
let task_id = Ref.string_of (Context.get_task_id __context) in
let vbds = Db.VM.get_VBDs ~__context ~self:vm in
Expand All @@ -429,6 +430,18 @@ let clone ?snapshot_info_record disk_op ~__context ~vm ~new_name =
let is_a_snapshot =
disk_op = Disk_op_snapshot || disk_op = Disk_op_checkpoint
in

let vbds =
if is_a_snapshot then
List.filter
(fun x ->
not (List.mem (Db.VBD.get_VDI ~__context ~self:x) ignore_vdis)
)
vbds
else
vbds
in

(* Check licence permission before copying disks, since the copy can take a long time.
* We always allow snapshotting a VM, but check before clone/copy of an existing snapshot or template. *)
if Db.VM.get_has_vendor_device ~__context ~self:vm && not is_a_snapshot
Expand Down
1 change: 1 addition & 0 deletions ocaml/xapi/xapi_vm_clone.mli
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ val clone_single_vdi :
(* NB this function may be called when the VM is suspended for copy/clone operations. Snapshot can be done in live.*)
val clone :
?snapshot_info_record:(string * string) list
-> ?ignore_vdis:[`VDI] API.Ref.t list
-> disk_op_t
-> __context:Context.t
-> vm:[`VM] API.Ref.t
Expand Down
3 changes: 2 additions & 1 deletion ocaml/xapi/xapi_vm_snapshot.ml
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,13 @@ open D
(*************************************************************************************************)
(* Crash-consistant snapshot *)
(*************************************************************************************************)
let snapshot ~__context ~vm ~new_name =
let snapshot ~__context ~vm ~new_name ~ignore_vdis =
debug "Snapshot: begin" ;
TaskHelper.set_cancellable ~__context ;
Xapi_vmss.show_task_in_xencenter ~__context ~vm ;
let res =
Xapi_vm_clone.clone Xapi_vm_clone.Disk_op_snapshot ~__context ~vm ~new_name
~ignore_vdis
in
debug "Snapshot: end" ; res

Expand Down
7 changes: 7 additions & 0 deletions ocaml/xe-cli/bash-completion
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,13 @@ _xe()
return 0
;;

ignore-vdi-uuids) # for vm-snapshot
val=$(final_comma_separated_param "$value")
vdis=$(${xe} vdi-list --minimal 2>/dev/null)
IFS=$'\n'
set_completions "$vdis" "$val"
return 0
;;
*)
snd=`echo ${param} | gawk -F- '{print $NF}'`
fst=`echo ${param} | gawk -F- '{printf "%s", $1; for (i=2; i<NF; i++) printf "-%s", $i}'`
Expand Down