Skip to content

Commit 96882d1

Browse files
committed
CA-390277: Stop using records on CLI cross-pool migrations
Using records in cross-pool migration code is dangerous, as the code interacts with potentially newer hosts. This means that fields in the record might be different from what's expected. In particular adding an enum field can break the deserialization, and removing a field as well. The tradeoff here is that there are more remote roundtrips to get the data needed. Signed-off-by: Pau Ruiz Safont <[email protected]>
1 parent 2e39039 commit 96882d1

File tree

1 file changed

+86
-70
lines changed

1 file changed

+86
-70
lines changed

ocaml/xapi-cli-server/cli_operations.ml

Lines changed: 86 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -4310,56 +4310,65 @@ let vm_migrate printer rpc session_id params =
43104310
Client.Session.login_with_password remote_rpc username password "1.3"
43114311
Constants.xapi_user_agent
43124312
in
4313+
let remote f = f ~rpc:remote_rpc ~session_id:remote_session in
43134314
finally
43144315
(fun () ->
4315-
let host, host_record =
4316-
let all = Client.Host.get_all_records remote_rpc remote_session in
4317-
if List.mem_assoc "host" params then
4318-
let x = List.assoc "host" params in
4319-
try
4320-
List.find
4321-
(fun (_, h) ->
4322-
h.API.host_hostname = x
4323-
|| h.API.host_name_label = x
4324-
|| h.API.host_uuid = x
4316+
let host =
4317+
let host_matches x self =
4318+
let hostname () = remote (Client.Host.get_hostname ~self) in
4319+
let uuid () = remote (Client.Host.get_uuid ~self) in
4320+
let name_label () = remote (Client.Host.get_name_label ~self) in
4321+
hostname () = x || uuid () = x || name_label () = x
4322+
in
4323+
let matches, fail_msg =
4324+
match List.assoc_opt "host" params with
4325+
| Some x ->
4326+
(host_matches x, Printf.sprintf "Failed to find host: %s" x)
4327+
| None ->
4328+
( (fun _ -> true)
4329+
, Printf.sprintf "Failed to find a suitable host"
43254330
)
4326-
all
4327-
with Not_found ->
4328-
failwith (Printf.sprintf "Failed to find host: %s" x)
4329-
else
4330-
List.hd all
4331+
in
4332+
let all_hosts = remote Client.Host.get_all in
4333+
match List.find_opt matches all_hosts with
4334+
| Some host ->
4335+
host
4336+
| None ->
4337+
failwith fail_msg
43314338
in
4332-
let network, network_record =
4333-
let all = Client.Network.get_all_records remote_rpc remote_session in
4334-
if List.mem_assoc "remote-network" params then
4335-
let x = List.assoc "remote-network" params in
4336-
try
4337-
List.find
4338-
(fun (_, net) ->
4339-
net.API.network_bridge = x
4340-
|| net.API.network_name_label = x
4341-
|| net.API.network_uuid = x
4342-
)
4343-
all
4344-
with Not_found ->
4345-
failwith (Printf.sprintf "Failed to find network: %s" x)
4346-
else
4347-
let pifs = host_record.API.host_PIFs in
4348-
let management_pifs =
4349-
List.filter
4350-
(fun pif ->
4351-
Client.PIF.get_management remote_rpc remote_session pif
4352-
)
4353-
pifs
4354-
in
4355-
if List.length management_pifs = 0 then
4356-
failwith
4357-
(Printf.sprintf "Could not find management PIF on host %s"
4358-
host_record.API.host_uuid
4359-
) ;
4360-
let pif = List.hd management_pifs in
4361-
let net = Client.PIF.get_network remote_rpc remote_session pif in
4362-
(net, Client.Network.get_record remote_rpc remote_session net)
4339+
let network =
4340+
let network_matches x self =
4341+
let bridge () = remote (Client.Network.get_bridge ~self) in
4342+
let uuid () = remote (Client.Network.get_uuid ~self) in
4343+
let name_label () = remote (Client.Network.get_name_label ~self) in
4344+
bridge () = x || uuid () = x || name_label () = x
4345+
in
4346+
match List.assoc_opt "remote-network" params with
4347+
| Some x -> (
4348+
let all_networks = remote Client.Network.get_all in
4349+
match List.find_opt (network_matches x) all_networks with
4350+
| Some network ->
4351+
network
4352+
| None ->
4353+
failwith (Printf.sprintf "Failed to find network: %s" x)
4354+
)
4355+
| None -> (
4356+
let pifs = remote (Client.Host.get_PIFs ~self:host) in
4357+
let management_pif =
4358+
List.find_opt
4359+
(fun self -> remote (Client.PIF.get_management ~self))
4360+
pifs
4361+
in
4362+
match management_pif with
4363+
| None ->
4364+
let host_uuid = remote (Client.Host.get_uuid ~self:host) in
4365+
failwith
4366+
(Printf.sprintf "Could not find management PIF on host %s"
4367+
host_uuid
4368+
)
4369+
| Some pif ->
4370+
remote (Client.PIF.get_network ~self:pif)
4371+
)
43634372
in
43644373
let vif_map =
43654374
List.map
@@ -4400,43 +4409,47 @@ let vm_migrate printer rpc session_id params =
44004409
and among the choices of that the shared is preferred first(as it is recommended to have shared storage
44014410
in pool to host VMs), and then the one with the maximum available space *)
44024411
try
4403-
let query =
4404-
Printf.sprintf
4405-
{|(field "host"="%s") and (field "currently_attached"="true")|}
4406-
(Ref.string_of host)
4407-
in
4408-
let host_pbds =
4409-
Client.PBD.get_all_records_where remote_rpc remote_session query
4412+
let pbd_in_host self =
4413+
let host_of () = remote (Client.PBD.get_host ~self) in
4414+
let attached () =
4415+
remote (Client.PBD.get_currently_attached ~self)
4416+
in
4417+
host_of () = host && attached ()
44104418
in
44114419
let srs =
4412-
List.map
4413-
(fun (pbd_ref, pbd_rec) ->
4414-
( pbd_rec.API.pBD_SR
4415-
, Client.SR.get_record remote_rpc remote_session
4416-
pbd_rec.API.pBD_SR
4417-
)
4418-
)
4419-
host_pbds
4420+
remote Client.PBD.get_all
4421+
|> List.filter pbd_in_host
4422+
|> List.map (fun self -> remote (Client.PBD.get_SR ~self))
44204423
in
44214424
(* In the following loop, the current SR:sr' will be compared with previous checked ones,
44224425
first if it is an ISO type, then pass this one for selection, then the only shared one from this and
44234426
previous one will be valued, and if not that case (both shared or none shared), choose the one with
44244427
more space available *)
4428+
let is_iso self =
4429+
let typ = remote (Client.SR.get_content_type ~self) in
4430+
typ = "iso"
4431+
in
4432+
let physical_size self =
4433+
remote (Client.SR.get_physical_size ~self)
4434+
in
4435+
let physical_utilisation self =
4436+
remote (Client.SR.get_physical_utilisation ~self)
4437+
in
4438+
let shared self = remote (Client.SR.get_shared ~self) in
44254439
let sr, _ =
44264440
List.fold_left
4427-
(fun (sr, free_space) ((_, sr_rec') as sr') ->
4428-
if sr_rec'.API.sR_content_type = "iso" then
4441+
(fun (sr, free_space) sr' ->
4442+
if is_iso sr' then
44294443
(sr, free_space)
44304444
else
44314445
let free_space' =
4432-
Int64.sub sr_rec'.API.sR_physical_size
4433-
sr_rec'.API.sR_physical_utilisation
4446+
Int64.sub (physical_size sr') (physical_utilisation sr')
44344447
in
44354448
match sr with
44364449
| None ->
44374450
(Some sr', free_space')
4438-
| Some ((_, sr_rec) as sr) -> (
4439-
match (sr_rec.API.sR_shared, sr_rec'.API.sR_shared) with
4451+
| Some sr -> (
4452+
match (shared sr, shared sr') with
44404453
| true, false ->
44414454
(Some sr, free_space)
44424455
| false, true ->
@@ -4450,7 +4463,7 @@ let vm_migrate printer rpc session_id params =
44504463
)
44514464
(None, Int64.zero) srs
44524465
in
4453-
match sr with Some (sr_ref, _) -> Some sr_ref | _ -> None
4466+
sr
44544467
with _ -> None
44554468
in
44564469
let vdi_map =
@@ -4509,13 +4522,16 @@ let vm_migrate printer rpc session_id params =
45094522
)
45104523
params
45114524
in
4525+
let host_name_label = remote (Client.Host.get_name_label ~self:host) in
4526+
let network_name_label =
4527+
remote (Client.Network.get_name_label ~self:network)
4528+
in
45124529
printer
45134530
(Cli_printer.PMsg
45144531
(Printf.sprintf
45154532
"Will migrate to remote host: %s, using remote network: %s. \
45164533
Here is the VDI mapping:"
4517-
host_record.API.host_name_label
4518-
network_record.API.network_name_label
4534+
host_name_label network_name_label
45194535
)
45204536
) ;
45214537
List.iter

0 commit comments

Comments
 (0)