Skip to content

Commit d2d21f1

Browse files
authored
CA-388210: use unique datapaths for concurrent VDI copies (#5920)
Testing on a box shows that the new unique arguments for 'domain' work, but opening as a draft PR, because this needs wider testing in XenRT.
2 parents 7c20ec7 + 2686c6f commit d2d21f1

File tree

4 files changed

+84
-44
lines changed

4 files changed

+84
-44
lines changed

ocaml/xapi-storage-script/lib.ml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ module Process = struct
131131

132132
type t = {
133133
exit_status: (unit, exit_or_signal) Result.t
134+
; pid: int
134135
; stdout: string
135136
; stderr: string
136137
}
@@ -176,6 +177,7 @@ module Process = struct
176177
let run ~env ~prog ~args ~input =
177178
let ( let@ ) f x = f x in
178179
let@ p = with_process ~env ~prog ~args in
180+
let pid = p#pid in
179181
let sender = send p#stdin input in
180182
let receiver_out = receive p#stdout in
181183
let receiver_err = receive p#stderr in
@@ -185,7 +187,7 @@ module Process = struct
185187
Lwt.both sender receiver >>= fun ((), (stdout, stderr)) ->
186188
p#status >>= fun status ->
187189
let exit_status = Output.exit_or_signal_of_unix status in
188-
Lwt.return {Output.exit_status; stdout; stderr}
190+
Lwt.return {Output.exit_status; pid; stdout; stderr}
189191
)
190192
(function
191193
| Lwt.Canceled as exn ->

ocaml/xapi-storage-script/lib.mli

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ module Process : sig
6565

6666
type t = {
6767
exit_status: (unit, exit_or_signal) result
68+
; pid: int
6869
; stdout: string
6970
; stderr: string
7071
}
@@ -78,7 +79,7 @@ module Process : sig
7879
-> Output.t Lwt.t
7980
(** Runs a cli program, writes [input] into its stdin, then closing the fd,
8081
and finally waits for the program to finish and returns the exit status,
81-
its stdout and stderr. *)
82+
the pid, and its stdout and stderr. *)
8283
end
8384

8485
module DirWatcher : sig

ocaml/xapi-storage-script/main.ml

Lines changed: 58 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,23 @@ let backend_backtrace_error name args backtrace =
6666
let missing_uri () =
6767
backend_error "MISSING_URI" ["Please include a URI in the device-config"]
6868

69+
(** return a unique 'domain' string for Dom0, so that we can plug disks
70+
multiple times (e.g. for copy).
71+
72+
XAPI should give us a unique 'dp' (datapath) string, e.g. a UUID for storage migration,
73+
or vbd/domid/device.
74+
For regular guests keep the domain as passed by XAPI (an integer).
75+
*)
76+
let domain_of ~dp ~vm =
77+
let vm = Storage_interface.Vm.string_of vm in
78+
match vm with
79+
| "0" ->
80+
(* SM tries to use this in filesystem paths, so cannot have /,
81+
and systemd might be a bit unhappy with - *)
82+
"u0-" ^ dp |> String.map (function '/' | '-' -> '_' | c -> c)
83+
| _ ->
84+
vm
85+
6986
(** Functions to wrap calls to the above client modules and convert their
7087
exceptions and errors into SMAPIv2 errors of type
7188
[Storage_interface.Exception.exnty]. The above client modules should only
@@ -460,6 +477,8 @@ let fork_exec_rpc :
460477
)
461478
>>>= fun input ->
462479
let input = compat_in input |> Jsonrpc.to_string in
480+
debug (fun m -> m "Running %s" @@ Filename.quote_command script_name args)
481+
>>= fun () ->
463482
Process.run ~env ~prog:script_name ~args ~input >>= fun output ->
464483
let fail_because ~cause description =
465484
fail
@@ -483,12 +502,13 @@ let fork_exec_rpc :
483502
with
484503
| Error _ ->
485504
error (fun m ->
486-
m "%s failed and printed bad error json: %s" script_name
487-
output.Process.Output.stdout
505+
m "%s[%d] failed and printed bad error json: %s" script_name
506+
output.pid output.Process.Output.stdout
488507
)
489508
>>= fun () ->
490509
error (fun m ->
491-
m "%s failed, stderr: %s" script_name output.Process.Output.stderr
510+
m "%s[%d] failed, stderr: %s" script_name output.pid
511+
output.Process.Output.stderr
492512
)
493513
>>= fun () ->
494514
fail_because "non-zero exit and bad json on stdout"
@@ -499,12 +519,12 @@ let fork_exec_rpc :
499519
with
500520
| Error _ ->
501521
error (fun m ->
502-
m "%s failed and printed bad error json: %s" script_name
503-
output.Process.Output.stdout
522+
m "%s[%d] failed and printed bad error json: %s" script_name
523+
output.pid output.Process.Output.stdout
504524
)
505525
>>= fun () ->
506526
error (fun m ->
507-
m "%s failed, stderr: %s" script_name
527+
m "%s[%d] failed, stderr: %s" script_name output.pid
508528
output.Process.Output.stderr
509529
)
510530
>>= fun () ->
@@ -515,7 +535,9 @@ let fork_exec_rpc :
515535
)
516536
)
517537
| Error (Signal signal) ->
518-
error (fun m -> m "%s caught a signal and failed" script_name)
538+
error (fun m ->
539+
m "%s[%d] caught a signal and failed" script_name output.pid
540+
)
519541
>>= fun () -> fail_because "signalled" ~cause:(Signal.to_string signal)
520542
| Ok () -> (
521543
(* Parse the json on stdout. We get back a JSON-RPC
@@ -527,8 +549,8 @@ let fork_exec_rpc :
527549
with
528550
| Error _ ->
529551
error (fun m ->
530-
m "%s succeeded but printed bad json: %s" script_name
531-
output.Process.Output.stdout
552+
m "%s[%d] succeeded but printed bad json: %s" script_name
553+
output.pid output.Process.Output.stdout
532554
)
533555
>>= fun () ->
534556
fail
@@ -537,7 +559,8 @@ let fork_exec_rpc :
537559
)
538560
| Ok response ->
539561
info (fun m ->
540-
m "%s succeeded: %s" script_name output.Process.Output.stdout
562+
m "%s[%d] succeeded: %s" script_name output.pid
563+
output.Process.Output.stdout
541564
)
542565
>>= fun () ->
543566
let response = compat_out response in
@@ -744,7 +767,7 @@ let vdi_of_volume x =
744767
; persistent= true
745768
}
746769

747-
let choose_datapath ?(persistent = true) domain response =
770+
let choose_datapath ?(persistent = true) response =
748771
(* We can only use a URI with a valid scheme, since we use the scheme
749772
to name the datapath plugin. *)
750773
let possible =
@@ -789,7 +812,7 @@ let choose_datapath ?(persistent = true) domain response =
789812
| [] ->
790813
fail (missing_uri ())
791814
| (script_dir, scheme, u) :: _us ->
792-
return (fork_exec_rpc ~script_dir, scheme, u, domain)
815+
return (fork_exec_rpc ~script_dir, scheme, u)
793816

794817
(* Bind the implementations *)
795818
let bind ~volume_script_dir =
@@ -863,7 +886,7 @@ let bind ~volume_script_dir =
863886
stat ~dbg ~sr ~vdi:temporary
864887
)
865888
>>>= fun response ->
866-
choose_datapath domain response >>>= fun (rpc, _datapath, uri, domain) ->
889+
choose_datapath response >>>= fun (rpc, _datapath, uri) ->
867890
return_data_rpc (fun () -> Datapath_client.attach (rpc ~dbg) dbg uri domain)
868891
in
869892
let wrap th = Rpc_lwt.T.put th in
@@ -1432,9 +1455,9 @@ let bind ~volume_script_dir =
14321455
|> wrap
14331456
in
14341457
S.VDI.introduce vdi_introduce_impl ;
1435-
let vdi_attach3_impl dbg _dp sr vdi' vm' _readwrite =
1458+
let vdi_attach3_impl dbg dp sr vdi' vm _readwrite =
14361459
(let vdi = Storage_interface.Vdi.string_of vdi' in
1437-
let domain = Storage_interface.Vm.string_of vm' in
1460+
let domain = domain_of ~dp ~vm in
14381461
vdi_attach_common dbg sr vdi domain >>>= fun response ->
14391462
let convert_implementation = function
14401463
| Xapi_storage.Data.XenDisk {params; extra; backend_type} ->
@@ -1456,9 +1479,9 @@ let bind ~volume_script_dir =
14561479
|> wrap
14571480
in
14581481
S.VDI.attach3 vdi_attach3_impl ;
1459-
let vdi_activate_common dbg sr vdi' vm' readonly =
1482+
let vdi_activate_common dbg dp sr vdi' vm readonly =
14601483
(let vdi = Storage_interface.Vdi.string_of vdi' in
1461-
let domain = Storage_interface.Vm.string_of vm' in
1484+
let domain = domain_of ~dp ~vm in
14621485
Attached_SRs.find sr >>>= fun sr ->
14631486
(* Discover the URIs using Volume.stat *)
14641487
stat ~dbg ~sr ~vdi >>>= fun response ->
@@ -1472,7 +1495,7 @@ let bind ~volume_script_dir =
14721495
stat ~dbg ~sr ~vdi:temporary
14731496
)
14741497
>>>= fun response ->
1475-
choose_datapath domain response >>>= fun (rpc, _datapath, uri, domain) ->
1498+
choose_datapath response >>>= fun (rpc, _datapath, uri) ->
14761499
return_data_rpc (fun () ->
14771500
let rpc = rpc ~dbg in
14781501
if readonly then
@@ -1483,17 +1506,17 @@ let bind ~volume_script_dir =
14831506
)
14841507
|> wrap
14851508
in
1486-
let vdi_activate3_impl dbg _dp sr vdi' vm' =
1487-
vdi_activate_common dbg sr vdi' vm' false
1509+
let vdi_activate3_impl dbg dp sr vdi' vm =
1510+
vdi_activate_common dbg dp sr vdi' vm false
14881511
in
14891512
S.VDI.activate3 vdi_activate3_impl ;
1490-
let vdi_activate_readonly_impl dbg _dp sr vdi' vm' =
1491-
vdi_activate_common dbg sr vdi' vm' true
1513+
let vdi_activate_readonly_impl dbg dp sr vdi' vm =
1514+
vdi_activate_common dbg dp sr vdi' vm true
14921515
in
14931516
S.VDI.activate_readonly vdi_activate_readonly_impl ;
1494-
let vdi_deactivate_impl dbg _dp sr vdi' vm' =
1517+
let vdi_deactivate_impl dbg dp sr vdi' vm =
14951518
(let vdi = Storage_interface.Vdi.string_of vdi' in
1496-
let domain = Storage_interface.Vm.string_of vm' in
1519+
let domain = domain_of ~dp ~vm in
14971520
Attached_SRs.find sr >>>= fun sr ->
14981521
(* Discover the URIs using Volume.stat *)
14991522
stat ~dbg ~sr ~vdi >>>= fun response ->
@@ -1506,17 +1529,17 @@ let bind ~volume_script_dir =
15061529
stat ~dbg ~sr ~vdi:temporary
15071530
)
15081531
>>>= fun response ->
1509-
choose_datapath domain response >>>= fun (rpc, _datapath, uri, domain) ->
1532+
choose_datapath response >>>= fun (rpc, _datapath, uri) ->
15101533
return_data_rpc (fun () ->
15111534
Datapath_client.deactivate (rpc ~dbg) dbg uri domain
15121535
)
15131536
)
15141537
|> wrap
15151538
in
15161539
S.VDI.deactivate vdi_deactivate_impl ;
1517-
let vdi_detach_impl dbg _dp sr vdi' vm' =
1540+
let vdi_detach_impl dbg dp sr vdi' vm =
15181541
(let vdi = Storage_interface.Vdi.string_of vdi' in
1519-
let domain = Storage_interface.Vm.string_of vm' in
1542+
let domain = domain_of ~dp ~vm in
15201543
Attached_SRs.find sr >>>= fun sr ->
15211544
(* Discover the URIs using Volume.stat *)
15221545
stat ~dbg ~sr ~vdi >>>= fun response ->
@@ -1529,7 +1552,7 @@ let bind ~volume_script_dir =
15291552
stat ~dbg ~sr ~vdi:temporary
15301553
)
15311554
>>>= fun response ->
1532-
choose_datapath domain response >>>= fun (rpc, _datapath, uri, domain) ->
1555+
choose_datapath response >>>= fun (rpc, _datapath, uri) ->
15331556
return_data_rpc (fun () -> Datapath_client.detach (rpc ~dbg) dbg uri domain)
15341557
)
15351558
|> wrap
@@ -1564,14 +1587,12 @@ let bind ~volume_script_dir =
15641587
|> wrap
15651588
in
15661589
S.SR.stat sr_stat_impl ;
1567-
let vdi_epoch_begin_impl dbg sr vdi' vm' persistent =
1590+
let vdi_epoch_begin_impl dbg sr vdi' _vm persistent =
15681591
(let vdi = Storage_interface.Vdi.string_of vdi' in
1569-
let domain = Storage_interface.Vm.string_of vm' in
15701592
Attached_SRs.find sr >>>= fun sr ->
15711593
(* Discover the URIs using Volume.stat *)
15721594
stat ~dbg ~sr ~vdi >>>= fun response ->
1573-
choose_datapath ~persistent domain response
1574-
>>>= fun (rpc, datapath, uri, _domain) ->
1595+
choose_datapath ~persistent response >>>= fun (rpc, datapath, uri) ->
15751596
(* If non-persistent and the datapath plugin supports NONPERSISTENT
15761597
then we delegate this to the datapath plugin. Otherwise we will
15771598
make a temporary clone now and attach/detach etc this file. *)
@@ -1602,13 +1623,12 @@ let bind ~volume_script_dir =
16021623
|> wrap
16031624
in
16041625
S.VDI.epoch_begin vdi_epoch_begin_impl ;
1605-
let vdi_epoch_end_impl dbg sr vdi' vm' =
1626+
let vdi_epoch_end_impl dbg sr vdi' _vm =
16061627
(let vdi = Storage_interface.Vdi.string_of vdi' in
1607-
let domain = Storage_interface.Vm.string_of vm' in
16081628
Attached_SRs.find sr >>>= fun sr ->
16091629
(* Discover the URIs using Volume.stat *)
16101630
stat ~dbg ~sr ~vdi >>>= fun response ->
1611-
choose_datapath domain response >>>= fun (rpc, datapath, uri, _domain) ->
1631+
choose_datapath response >>>= fun (rpc, datapath, uri) ->
16121632
if Datapath_plugins.supports_feature datapath _nonpersistent then
16131633
return_data_rpc (fun () -> Datapath_client.close (rpc ~dbg) dbg uri)
16141634
else
@@ -1627,9 +1647,9 @@ let bind ~volume_script_dir =
16271647
S.VDI.epoch_end vdi_epoch_end_impl ;
16281648
let vdi_set_persistent_impl _dbg _sr _vdi _persistent = return () |> wrap in
16291649
S.VDI.set_persistent vdi_set_persistent_impl ;
1630-
let dp_destroy2 dbg _dp sr vdi' vm' _allow_leak =
1650+
let dp_destroy2 dbg dp sr vdi' vm _allow_leak =
16311651
(let vdi = Storage_interface.Vdi.string_of vdi' in
1632-
let domain = Storage_interface.Vm.string_of vm' in
1652+
let domain = domain_of ~dp ~vm in
16331653
Attached_SRs.find sr >>>= fun sr ->
16341654
(* Discover the URIs using Volume.stat *)
16351655
stat ~dbg ~sr ~vdi >>>= fun response ->
@@ -1642,7 +1662,7 @@ let bind ~volume_script_dir =
16421662
stat ~dbg ~sr ~vdi:temporary
16431663
)
16441664
>>>= fun response ->
1645-
choose_datapath domain response >>>= fun (rpc, _datapath, uri, domain) ->
1665+
choose_datapath response >>>= fun (rpc, _datapath, uri) ->
16461666
return_data_rpc (fun () ->
16471667
Datapath_client.deactivate (rpc ~dbg) dbg uri domain
16481668
)

ocaml/xapi-storage-script/test_lib.ml

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,12 +103,20 @@ let test_run_status =
103103
let module P = Process in
104104
let test () =
105105
let* output = P.run ~prog:"true" ~args:[] ~input:"" ~env:[] in
106-
let expected = P.Output.{exit_status= Ok (); stdout= ""; stderr= ""} in
106+
let expected =
107+
P.Output.{exit_status= Ok (); pid= output.pid; stdout= ""; stderr= ""}
108+
in
107109
Alcotest.(check output_c) "Exit status is correct" expected output ;
108110

109111
let* output = P.run ~prog:"false" ~args:[] ~input:"" ~env:[] in
110112
let expected =
111-
P.Output.{exit_status= Error (Exit_non_zero 1); stdout= ""; stderr= ""}
113+
P.Output.
114+
{
115+
exit_status= Error (Exit_non_zero 1)
116+
; pid= output.pid
117+
; stdout= ""
118+
; stderr= ""
119+
}
112120
in
113121
Alcotest.(check output_c) "Exit status is correct" expected output ;
114122

@@ -121,15 +129,24 @@ let test_run_output =
121129
let test () =
122130
let content = "@@@@@@" in
123131
let* output = P.run ~prog:"cat" ~args:["-"] ~input:content ~env:[] in
124-
let expected = P.Output.{exit_status= Ok (); stdout= content; stderr= ""} in
132+
let expected =
133+
P.Output.
134+
{exit_status= Ok (); pid= output.pid; stdout= content; stderr= ""}
135+
in
125136
Alcotest.(check output_c) "Stdout is correct" expected output ;
126137

127138
let* output = P.run ~prog:"cat" ~args:[content] ~input:content ~env:[] in
128139
let stderr =
129140
Printf.sprintf "cat: %s: No such file or directory\n" content
130141
in
131142
let expected =
132-
P.Output.{exit_status= Error (Exit_non_zero 1); stdout= ""; stderr}
143+
P.Output.
144+
{
145+
exit_status= Error (Exit_non_zero 1)
146+
; pid= output.pid
147+
; stdout= ""
148+
; stderr
149+
}
133150
in
134151
Alcotest.(check output_c) "Stderr is correct" expected output ;
135152
Lwt.return ()

0 commit comments

Comments
 (0)