Skip to content

Commit ed9b76a

Browse files
committed
Merge pull request #2332 from thomassa/xenprep-start
CP-13301 full implementation of xenprep_start
2 parents 919a4d4 + 6a4be8d commit ed9b76a

File tree

6 files changed

+103
-7
lines changed

6 files changed

+103
-7
lines changed

ocaml/idl/api_errors.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ let vbd_not_empty = "VBD_NOT_EMPTY"
138138
let vbd_is_empty = "VBD_IS_EMPTY"
139139
let vbd_tray_locked = "VBD_TRAY_LOCKED"
140140
let vbd_missing = "VBD_MISSING"
141+
let vm_no_empty_cd_vbd = "VM_NO_EMPTY_CD_VBD"
141142
let vm_snapshot_failed = "VM_SNAPSHOT_FAILED"
142143
let vm_snapshot_with_quiesce_failed = "VM_SNAPSHOT_WITH_QUIESCE_FAILED"
143144
let vm_snapshot_with_quiesce_timeout = "VM_SNAPSHOT_WITH_QUIESCE_TIMEOUT"

ocaml/idl/datamodel.ml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,8 @@ let _ =
652652
(* CA-83260 *)
653653
error Api_errors.disk_vbd_must_be_readwrite_for_hvm ["vbd"]
654654
~doc:"All VBDs of type 'disk' must be read/write for HVM guests" ();
655+
error Api_errors.vm_no_empty_cd_vbd ["vm"]
656+
~doc:"The VM has no empty CD drive (VBD)." ();
655657
error Api_errors.vm_hvm_required ["vm"]
656658
~doc:"HVM is required for this operation" ();
657659
error Api_errors.vm_no_vcpus ["vm"]

ocaml/xapi/cli_operations.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2820,7 +2820,7 @@ let vm_cd_insert printer rpc session_id params =
28202820
let vm_record = vm.record () in
28212821
let vbds = vm_record.API.vM_VBDs in
28222822
let cdvbds = List.filter (fun vbd -> (Client.VBD.get_type rpc session_id vbd = `CD) && (Client.VBD.get_empty rpc session_id vbd)) vbds in
2823-
if List.length cdvbds = 0 then (failwith "No empty CD devices found");
2823+
if List.length cdvbds = 0 then raise (Api_errors.Server_error(Api_errors.vm_no_empty_cd_vbd, [ Ref.string_of (vm.getref ()) ]));
28242824
if List.length cdvbds > 1 then (failwith "Two or more empty CD devices found. Please use vbd-insert");
28252825
let cd = List.hd cdvbds in
28262826
Client.VBD.insert rpc session_id cd (List.hd vdis)

ocaml/xapi/helpers.ml

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -822,32 +822,66 @@ let cancel_tasks ~__context ~ops ~all_tasks_in_db (* all tasks in database *) ~t
822822
let is_removable ~__context ~vbd = Db.VBD.get_type ~__context ~self:vbd = `CD
823823

824824
let is_tools_sr_cache = ref []
825+
let tools_sr_memoised = ref None
825826
let is_tools_sr_cache_m = Mutex.create ()
826827

827828
let clear_tools_sr_cache () =
828829
Mutex.execute is_tools_sr_cache_m
829-
(fun () -> is_tools_sr_cache := [])
830+
(fun () -> is_tools_sr_cache := []; tools_sr_memoised := None)
830831

831832
(** Returns true if this SR is the XenSource Tools SR *)
832833
let is_tools_sr ~__context ~sr =
833834
try
834835
Mutex.execute is_tools_sr_cache_m
835-
(fun () -> List.assoc sr !is_tools_sr_cache)
836+
(fun () ->
837+
match !tools_sr_memoised with
838+
| Some s when s = sr -> true
839+
| Some _ (* We could return false except for nervousness about adding an assumption of only one Tools SR *)
840+
| None -> List.assoc sr !is_tools_sr_cache
841+
)
836842
with Not_found ->
837843
let other_config = Db.SR.get_other_config ~__context ~self:sr in
838844
(* Miami GA *)
839-
let result =
845+
let result = (
840846
List.mem_assoc Xapi_globs.tools_sr_tag other_config
841847
(* Miami beta2 and earlier: *)
842848
|| (List.mem_assoc Xapi_globs.xensource_internal other_config)
843-
in
849+
) && (
850+
"iso" = Db.SR.get_content_type ~__context ~self:sr
851+
) in
844852
Mutex.execute is_tools_sr_cache_m
845853
(fun () ->
846854
let cache = !is_tools_sr_cache in
847855
if not (List.mem_assoc sr cache) then
848856
is_tools_sr_cache := (sr, result) :: !is_tools_sr_cache);
849857
result
850858

859+
let get_tools_sr ~__context =
860+
let seek_tools_sr () =
861+
let candidates = Db.SR.get_refs_where ~__context ~expr:(
862+
And (
863+
Or (
864+
Eq (Field "name__label", Literal Xapi_globs.miami_tools_sr_name),
865+
Eq (Field "name__label", Literal Xapi_globs.rio_tools_sr_name) (* In case of ancient hosts that have been upgraded repeatedly? *)
866+
),
867+
Eq (Field "content_type", Literal "iso")
868+
)
869+
) in
870+
let srs = List.filter (fun sr ->
871+
is_tools_sr ~__context ~sr
872+
) candidates in
873+
if List.length srs <> 1 then failwith
874+
("Expected exactly one Tools SR; found " ^ string_of_int (List.length srs));
875+
List.hd srs
876+
in
877+
match Mutex.execute is_tools_sr_cache_m (fun () -> !tools_sr_memoised)
878+
with
879+
| Some sr -> sr
880+
| None ->
881+
let sr = seek_tools_sr () in
882+
Mutex.execute is_tools_sr_cache_m (fun () -> tools_sr_memoised := Some sr);
883+
sr
884+
851885
(** Return true if the MAC is in the right format XX:XX:XX:XX:XX:XX *)
852886
let is_valid_MAC mac =
853887
let l = String.split ':' mac in

ocaml/xapi/xapi_globs.ml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,9 @@ let miami_tools_sr_name = "XenServer Tools"
205205

206206
let tools_sr_dir = ref "/opt/xensource/packages/iso"
207207

208+
(* let xenprep_iso_name_label = "xenprep.iso" *) (* TODO use this once the xenprep ISO is available *)
209+
let xenprep_iso_name_label = "xs-tools.iso" (* Temporary stopgap value for dev-testing *)
210+
208211
let default_template_key = "default_template"
209212
let linux_template_key = "linux_template"
210213
let base_template_name_key = "base_template_name"

ocaml/xapi/xapi_vm.ml

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,13 @@ exception InvalidOperation of string
3636

3737
let assert_operation_valid = Xapi_vm_lifecycle.assert_operation_valid
3838

39+
(* Convenience function currently not exposed in .mli but could be. *)
40+
(* Not thread-safe. *)
41+
let db_set_in_other_config ~__context ~self ~key ~value =
42+
if List.mem_assoc key (Db.VM.get_other_config ~__context ~self) then
43+
Db.VM.remove_from_other_config ~__context ~self ~key;
44+
Db.VM.add_to_other_config ~__context ~self ~key ~value
45+
3946
let update_allowed_operations ~__context ~self =
4047
Helpers.log_exn_continue "updating allowed operations of VBDs/VIFs/VDIs in VM.update_allowed_operations"
4148
(fun () ->
@@ -1019,6 +1026,55 @@ let set_auto_update_drivers ~__context ~self ~value=
10191026
assert_can_set_auto_update_drivers ~__context ~self ~value;
10201027
Db.VM.set_auto_update_drivers ~__context ~self ~value
10211028

1029+
let xenprep_mutex = Mutex.create ()
1030+
10221031
let xenprep_start ~__context ~self =
1023-
info "Xapi_vm.xenprep_start: VM=%s" (Db.VM.get_uuid ~__context ~self);
1024-
Db.VM.add_to_other_config ~__context ~self ~key:"xenprep_progress" ~value:"dummyvalue_should_be_cd_inserted"
1032+
let key = "xenprep_progress" in
1033+
let vm_uuid = Db.VM.get_uuid ~__context ~self in (* Just for log-msgs *)
1034+
info "Xapi_vm.xenprep_start: VM=%s" vm_uuid;
1035+
let started_already = Mutex.execute xenprep_mutex (fun () ->
1036+
if List.mem_assoc key (Db.VM.get_other_config ~__context ~self) then true
1037+
else (Db.VM.add_to_other_config ~__context ~self ~key ~value:"about_to_insert_iso";
1038+
false)
1039+
) in
1040+
if started_already then (
1041+
info "Xapi_vm.xenprep_start: VM is in xenprep already: VM=%s" vm_uuid
1042+
) else (
1043+
( try
1044+
let cd_name = Xapi_globs.xenprep_iso_name_label in
1045+
let sr = Helpers.get_tools_sr ~__context in
1046+
let vdis =
1047+
let open Db_filter_types in
1048+
Db.VDI.get_refs_where ~__context ~expr:(
1049+
And (
1050+
Eq (Field "SR", Literal (Ref.string_of sr)),
1051+
Eq (Field "name__label", Literal cd_name)
1052+
)
1053+
) in
1054+
if List.length vdis <> 1 then failwith
1055+
("xenprep_start failed: found "^(string_of_int (List.length vdis))^" ISOs with name_label="^cd_name^" in tools SR");
1056+
let vdi = List.hd vdis in
1057+
(* Find any empty CD-drive VBD for the VM. *)
1058+
(* We don't care if currently_attached is false: VBD.insert will take care of things. *)
1059+
let vbds = Db.VM.get_VBDs ~__context ~self in
1060+
let empty_cd_vbds = List.filter (fun vbd -> (Db.VBD.get_type ~__context ~self:vbd = `CD) && (Db.VBD.get_empty ~__context ~self:vbd)) vbds in
1061+
if [] = empty_cd_vbds then raise (Api_errors.Server_error(Api_errors.vm_no_empty_cd_vbd, [ Ref.string_of self ]));
1062+
let cd_vbd = List.hd empty_cd_vbds in
1063+
(* We found a suitable drive, so insert the CD. *)
1064+
(* We use the API to be on the safe side, because calling
1065+
* Xapi_vbd.insert directly would bypass the "operations"
1066+
* handling in Message_forwarding *)
1067+
Helpers.call_api_functions ~__context
1068+
(fun rpc session_id ->
1069+
Client.VBD.insert ~rpc ~session_id ~vbd:cd_vbd ~vdi
1070+
)
1071+
with e ->
1072+
Mutex.execute xenprep_mutex (fun () ->
1073+
Db.VM.remove_from_other_config ~__context ~self ~key
1074+
);
1075+
raise e
1076+
);
1077+
Mutex.execute xenprep_mutex (fun () ->
1078+
db_set_in_other_config ~__context ~self ~key ~value:"ISO_inserted"
1079+
)
1080+
)

0 commit comments

Comments
 (0)