Skip to content

Commit f0243e4

Browse files
committed
Implement VDI.data_destroy XenAPI call
This call is idempotent, therefore it is allowed on a VDI of type cbt_metadata. Repeat the same checks done for VDI.destroy in check_operation_error, in addition to the checks specific to data_destroy. However, some of the checks performed by VDI.destroy are irrelevant to VDI.data_destroy, because CBT can only be enabled on VDIs of type user or system, therefore we can never get into a state where CBT is enabled for a ha_statefile, redo_log, or metadata VDI. But I still decided to extract the VDI.destroy checks and call these for both operations, to avoid the various checks getting out of sync - it is safe to still perform these irrelevant checks for VDI.data_destroy. I have also reused the implementation of VDI.destroy, and this means that VDI.data_destroy suffers from the same race as VDI.destroy: we delete the VBDs referring to the VDI shortly after (data_)destroying the VDI. Signed-off-by: Gabor Igloi <[email protected]>
1 parent 9bba7fa commit f0243e4

File tree

6 files changed

+111
-63
lines changed

6 files changed

+111
-63
lines changed

ocaml/idl/datamodel.ml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6007,6 +6007,7 @@ let storage_operations =
60076007
"vdi_mirror", "Mirroring a VDI";
60086008
"vdi_enable_cbt", "Enabling changed block tracking for a VDI";
60096009
"vdi_disable_cbt", "Disabling changed block tracking for a VDI";
6010+
"vdi_data_destroy", "Deleting the data of the VDI";
60106011
"vdi_set_on_boot", "Setting the on_boot field of the VDI";
60116012
"pbd_create", "Creating a PBD for this SR";
60126013
"pbd_destroy", "Destroying one of this SR's PBDs"; ])
@@ -6331,6 +6332,7 @@ let vdi_operations =
63316332
"generate_config", "Generating static configuration";
63326333
"enable_cbt", "Enabling changed block tracking for a VDI";
63336334
"disable_cbt", "Disabling changed block tracking for a VDI";
6335+
"data_destroy", "Deleting the data of the VDI";
63346336
"set_on_boot", "Setting the on_boot field of the VDI";
63356337
"blocked", "Operations on this VDI are temporarily blocked";
63366338
])
@@ -6573,6 +6575,15 @@ let vdi_disable_cbt = call
65736575
~allowed_roles:_R_VM_ADMIN
65746576
()
65756577

6578+
let vdi_data_destroy = call
6579+
~name:"data_destroy"
6580+
~in_oss_since:None
6581+
~in_product_since:rel_inverness
6582+
~params:[Ref _vdi, "self", "The VDI whose data should be deleted."]
6583+
~doc:"Delete the data of the snapshot VDI, but keep its changed block tracking metadata. When successful, this call changes the type of the VDI to cbt_metadata. This operation is idempotent: calling it on a VDI of type cbt_metadata results in a no-op, and no error will be thrown."
6584+
~allowed_roles:_R_VM_ADMIN
6585+
()
6586+
65766587
(** A virtual disk *)
65776588
let vdi =
65786589
create_obj ~in_db:true ~in_product_since:rel_rio ~in_oss_since:oss_since_303 ~internal_deprecated_since:None ~persist:PersistEverything ~gen_constructor_destructor:true ~name:_vdi ~descr:"A virtual disk image"
@@ -6607,6 +6618,7 @@ let vdi =
66076618
vdi_pool_migrate;
66086619
vdi_enable_cbt;
66096620
vdi_disable_cbt;
6621+
vdi_data_destroy;
66106622
]
66116623
~contents:
66126624
([ uid _vdi;

ocaml/xapi/message_forwarding.ml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3596,6 +3596,15 @@ module Forward = functor(Local: Custom_actions.CUSTOM_ACTIONS) -> struct
35963596
forward_vdi_op ~local_fn ~__context ~self
35973597
(fun session_id rpc -> Client.VDI.disable_cbt rpc session_id self))
35983598

3599+
let data_destroy ~__context ~self =
3600+
info "VDI.data_destroy: VDI = '%s'" (vdi_uuid ~__context self);
3601+
let local_fn = Local.VDI.data_destroy ~self in
3602+
let sR = Db.VDI.get_SR ~__context ~self in
3603+
with_sr_andor_vdi ~__context ~sr:(sR, `vdi_data_destroy) ~vdi:(self, `data_destroy) ~doc:"VDI.data_destroy"
3604+
(fun () ->
3605+
forward_vdi_op ~local_fn ~__context ~self
3606+
(fun session_id rpc -> Client.VDI.data_destroy rpc session_id self))
3607+
35993608
end
36003609
module VBD = struct
36013610

ocaml/xapi/record_util.ml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ let vdi_operation_to_string: API.vdi_operations -> string = function
111111
| `generate_config -> "generate_config"
112112
| `enable_cbt -> "enable_cbt"
113113
| `disable_cbt -> "disable_cbt"
114+
| `data_destroy -> "data_destroy"
114115
| `set_on_boot -> "set_on_boot"
115116
| `blocked -> "blocked"
116117

@@ -131,6 +132,7 @@ let sr_operation_to_string: API.storage_operations -> string = function
131132
| `vdi_enable_cbt -> "VDI.enable_cbt"
132133
| `vdi_disable_cbt -> "VDI.disable_cbt"
133134
| `vdi_set_on_boot -> "VDI.set_on_boot"
135+
| `vdi_data_destroy -> "VDI.data_destroy"
134136
| `pbd_create -> "PBD.create"
135137
| `pbd_destroy -> "PBD.destroy"
136138

ocaml/xapi/xapi_sr_operations.ml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ open Record_util
3737

3838
let all_ops : API.storage_operations_set =
3939
[ `scan; `destroy; `forget; `plug; `unplug; `vdi_create; `vdi_destroy; `vdi_resize; `vdi_clone; `vdi_snapshot; `vdi_mirror;
40-
`vdi_enable_cbt; `vdi_disable_cbt; `vdi_set_on_boot; `vdi_introduce; `update; `pbd_create; `pbd_destroy ]
40+
`vdi_enable_cbt; `vdi_disable_cbt; `vdi_data_destroy; `vdi_set_on_boot; `vdi_introduce; `update; `pbd_create; `pbd_destroy ]
4141

4242
let sm_cap_table : (API.storage_operations * _) list =
4343
[ `vdi_create, Smint.Vdi_create;
@@ -47,6 +47,7 @@ let sm_cap_table : (API.storage_operations * _) list =
4747
`vdi_mirror, Smint.Vdi_mirror;
4848
`vdi_enable_cbt, Smint.Vdi_configure_cbt;
4949
`vdi_disable_cbt, Smint.Vdi_configure_cbt;
50+
`vdi_data_destroy, Smint.Vdi_configure_cbt;
5051
`vdi_set_on_boot, Smint.Vdi_reset_on_boot;
5152
`update, Smint.Sr_update;
5253
(* We fake clone ourselves *)

ocaml/xapi/xapi_vdi.ml

Lines changed: 85 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ let check_sm_feature_error (op:API.vdi_operations) sm_features sr =
4040
| `generate_config -> Some Vdi_generate_config
4141
| `clone -> Some Vdi_clone
4242
| `mirror -> Some Vdi_mirror
43-
| `enable_cbt | `disable_cbt -> Some Vdi_configure_cbt
43+
| `enable_cbt | `disable_cbt | `data_destroy -> Some Vdi_configure_cbt
4444
| `set_on_boot -> Some Vdi_reset_on_boot
4545
) in
4646
match required_sm_feature with
@@ -157,76 +157,88 @@ let check_operation_error ~__context ?(sr_records=[]) ?(pbd_records=[]) ?(vbd_re
157157
else if my_has_current_operation_vbd_records <> []
158158
then Some (Api_errors.other_operation_in_progress, [ "VDI"; _ref ])
159159
else
160+
160161
let sm_features = Xapi_sr_operations.features_of_sr_internal ~__context ~_type:sr_type in
161162
let sm_feature_error = check_sm_feature_error op sm_features sr in
162163
if sm_feature_error <> None
163164
then sm_feature_error
165+
164166
else
165167
let allowed_for_cbt_metadata_vdi = match op with
166168
| `clone | `copy | `disable_cbt | `enable_cbt | `mirror | `resize | `resize_online | `snapshot | `set_on_boot -> false
167-
| `blocked | `destroy | `force_unlock | `forget | `generate_config | `update -> true in
169+
| `blocked |`data_destroy | `destroy | `force_unlock | `forget | `generate_config | `update -> true in
168170
if not allowed_for_cbt_metadata_vdi && record.Db_actions.vDI_type = `cbt_metadata
169171
then Some (Api_errors.vdi_incompatible_type, [ _ref; Record_util.vdi_type_to_string `cbt_metadata ])
170172
else
171173
let allowed_when_cbt_enabled = match op with
172174
| `mirror | `set_on_boot -> false
173-
| `blocked | `clone | `copy | `destroy | `disable_cbt | `enable_cbt | `force_unlock | `forget | `generate_config | `resize | `resize_online | `snapshot | `update -> true in
175+
| `blocked | `clone | `copy | `data_destroy | `destroy | `disable_cbt | `enable_cbt | `force_unlock | `forget | `generate_config | `resize | `resize_online | `snapshot | `update -> true in
174176
if not allowed_when_cbt_enabled && record.Db_actions.vDI_cbt_enabled
175177
then Some (Api_errors.vdi_cbt_enabled, [_ref])
176-
else (
177-
match op with
178-
| `forget ->
179-
if ha_enabled && List.mem record.Db_actions.vDI_type [ `ha_statefile; `redo_log ]
180-
then Some (Api_errors.ha_is_enabled, [])
181-
else if List.mem record.Db_actions.vDI_type [ `rrd ]
182-
then Some (Api_errors.vdi_has_rrds, [_ref])
183-
else None
184-
| `destroy ->
185-
if sr_type = "udev"
186-
then Some (Api_errors.vdi_is_a_physical_device, [_ref])
187-
else
188-
if is_tools_sr
189-
then Some (Api_errors.sr_operation_not_supported, [Ref.string_of sr])
190-
else if List.mem record.Db_actions.vDI_type [ `rrd ]
191-
then Some (Api_errors.vdi_has_rrds, [_ref])
192-
else
193-
if ha_enabled && List.mem record.Db_actions.vDI_type [ `ha_statefile; `redo_log ]
194-
then Some (Api_errors.ha_is_enabled, [])
195-
else if List.mem record.Db_actions.vDI_type [`ha_statefile; `metadata ] && Xapi_pool_helpers.ha_enable_in_progress ~__context
196-
then Some (Api_errors.ha_enable_in_progress, [])
197-
else if List.mem record.Db_actions.vDI_type [`ha_statefile; `metadata ] && Xapi_pool_helpers.ha_disable_in_progress ~__context
198-
then Some (Api_errors.ha_disable_in_progress, [])
199-
else None
200-
| `resize ->
201-
if ha_enabled && List.mem record.Db_actions.vDI_type [ `ha_statefile; `redo_log ]
202-
then Some (Api_errors.ha_is_enabled, [])
203-
else None
204-
| `resize_online ->
205-
if ha_enabled && List.mem record.Db_actions.vDI_type [ `ha_statefile; `redo_log ]
206-
then Some (Api_errors.ha_is_enabled, [])
207-
else None
208-
| `snapshot when record.Db_actions.vDI_sharable ->
209-
Some (Api_errors.vdi_is_sharable, [ _ref ])
210-
| `snapshot when reset_on_boot ->
211-
Some (Api_errors.vdi_on_boot_mode_incompatible_with_operation, [])
212-
| `snapshot ->
213-
if List.exists (fun (_, op) -> op = `copy) current_ops
214-
then Some (Api_errors.operation_not_allowed, ["Snapshot operation not allowed during copy."])
215-
else None
216-
| `copy ->
217-
if List.mem record.Db_actions.vDI_type [ `ha_statefile; `redo_log ]
218-
then Some (Api_errors.operation_not_allowed, ["VDI containing HA statefile or redo log cannot be copied (check the VDI's allowed operations)."])
219-
else None
220-
| (`enable_cbt | `disable_cbt) ->
221-
if record.Db_actions.vDI_is_a_snapshot
222-
then Some (Api_errors.operation_not_allowed, ["VDI is a snapshot: " ^ _ref])
223-
else if not (List.mem record.Db_actions.vDI_type [ `user; `system ])
224-
then Some (Api_errors.vdi_incompatible_type, [ _ref; Record_util.vdi_type_to_string record.Db_actions.vDI_type ])
225-
else if record.Db_actions.vDI_on_boot = `reset
226-
then Some (Api_errors.vdi_on_boot_mode_incompatible_with_operation, [])
227-
else None
228-
| `mirror | `clone | `generate_config | `force_unlock | `set_on_boot | `blocked | `update -> None
229-
)
178+
else
179+
180+
let check_destroy () =
181+
if sr_type = "udev"
182+
then Some (Api_errors.vdi_is_a_physical_device, [_ref])
183+
else
184+
if is_tools_sr
185+
then Some (Api_errors.sr_operation_not_supported, [Ref.string_of sr])
186+
else if List.mem record.Db_actions.vDI_type [ `rrd ]
187+
then Some (Api_errors.vdi_has_rrds, [_ref])
188+
else
189+
if ha_enabled && List.mem record.Db_actions.vDI_type [ `ha_statefile; `redo_log ]
190+
then Some (Api_errors.ha_is_enabled, [])
191+
else if List.mem record.Db_actions.vDI_type [`ha_statefile; `metadata ] && Xapi_pool_helpers.ha_enable_in_progress ~__context
192+
then Some (Api_errors.ha_enable_in_progress, [])
193+
else if List.mem record.Db_actions.vDI_type [`ha_statefile; `metadata ] && Xapi_pool_helpers.ha_disable_in_progress ~__context
194+
then Some (Api_errors.ha_disable_in_progress, [])
195+
else None
196+
in
197+
198+
begin match op with
199+
| `forget ->
200+
if ha_enabled && List.mem record.Db_actions.vDI_type [ `ha_statefile; `redo_log ]
201+
then Some (Api_errors.ha_is_enabled, [])
202+
else if List.mem record.Db_actions.vDI_type [ `rrd ]
203+
then Some (Api_errors.vdi_has_rrds, [_ref])
204+
else None
205+
| `destroy -> check_destroy ()
206+
| `data_destroy ->
207+
if not record.Db_actions.vDI_is_a_snapshot
208+
then Some (Api_errors.operation_not_allowed, ["VDI is not a snapshot: " ^ _ref])
209+
else if not record.Db_actions.vDI_cbt_enabled
210+
then Some (Api_errors.vdi_no_cbt_metadata, [_ref])
211+
else check_destroy ()
212+
| `resize ->
213+
if ha_enabled && List.mem record.Db_actions.vDI_type [ `ha_statefile; `redo_log ]
214+
then Some (Api_errors.ha_is_enabled, [])
215+
else None
216+
| `resize_online ->
217+
if ha_enabled && List.mem record.Db_actions.vDI_type [ `ha_statefile; `redo_log ]
218+
then Some (Api_errors.ha_is_enabled, [])
219+
else None
220+
| `snapshot when record.Db_actions.vDI_sharable ->
221+
Some (Api_errors.vdi_is_sharable, [ _ref ])
222+
| `snapshot when reset_on_boot ->
223+
Some (Api_errors.vdi_on_boot_mode_incompatible_with_operation, [])
224+
| `snapshot ->
225+
if List.exists (fun (_, op) -> op = `copy) current_ops
226+
then Some (Api_errors.operation_not_allowed, ["Snapshot operation not allowed during copy."])
227+
else None
228+
| `copy ->
229+
if List.mem record.Db_actions.vDI_type [ `ha_statefile; `redo_log ]
230+
then Some (Api_errors.operation_not_allowed, ["VDI containing HA statefile or redo log cannot be copied (check the VDI's allowed operations)."])
231+
else None
232+
| (`enable_cbt | `disable_cbt) ->
233+
if record.Db_actions.vDI_is_a_snapshot
234+
then Some (Api_errors.operation_not_allowed, ["VDI is a snapshot: " ^ _ref])
235+
else if not (List.mem record.Db_actions.vDI_type [ `user; `system ])
236+
then Some (Api_errors.vdi_incompatible_type, [ _ref; Record_util.vdi_type_to_string record.Db_actions.vDI_type ])
237+
else if record.Db_actions.vDI_on_boot = `reset
238+
then Some (Api_errors.vdi_on_boot_mode_incompatible_with_operation, [])
239+
else None
240+
| `mirror | `clone | `generate_config | `force_unlock | `set_on_boot | `blocked | `update -> None
241+
end
230242

231243
let assert_operation_valid ~__context ~self ~(op:API.vdi_operations) =
232244
let pool = Helpers.get_pool ~__context in
@@ -520,9 +532,8 @@ let snapshot ~__context ~vdi ~driver_params =
520532
update_allowed_operations ~__context ~self:newvdi;
521533
newvdi
522534

523-
let destroy ~__context ~self =
535+
let destroy_and_data_destroy_common ~__context ~self ~(operation:[`destroy | `data_destroy]) =
524536
let sr = Db.VDI.get_SR ~__context ~self in
525-
let location = Db.VDI.get_location ~__context ~self in
526537
Sm.assert_pbd_is_plugged ~__context ~sr;
527538
Xapi_vdi_helpers.assert_managed ~__context ~vdi:self;
528539

@@ -532,18 +543,21 @@ let destroy ~__context ~self =
532543
let r = Db.VBD.get_record_internal ~__context ~self:vbd in
533544
r.Db_actions.vBD_currently_attached || r.Db_actions.vBD_reserved) vbds in
534545
if attached_vbds<>[] then
535-
raise (Api_errors.Server_error (Api_errors.vdi_in_use, [(Ref.string_of self); "destroy" ]))
546+
let caller_name = match operation with `destroy -> "destroy" | `data_destroy -> "data_destroy" in
547+
raise (Api_errors.Server_error (Api_errors.vdi_in_use, [(Ref.string_of self); caller_name]))
536548
else
537549
begin
538550
let open Storage_access in
539551
let open Storage_interface in
540552
let task = Context.get_task_id __context in
553+
let location = Db.VDI.get_location ~__context ~self in
541554
let module C = Client(struct let rpc = rpc end) in
555+
let call_f = match operation with `destroy -> C.VDI.destroy | `data_destroy -> C.VDI.data_destroy in
542556
transform_storage_exn
543557
(fun () ->
544-
C.VDI.destroy ~dbg:(Ref.string_of task) ~sr:(Db.SR.get_uuid ~__context ~self:sr) ~vdi:location
558+
call_f ~dbg:(Ref.string_of task) ~sr:(Db.SR.get_uuid ~__context ~self:sr) ~vdi:location
545559
);
546-
if Db.is_valid_ref __context self
560+
if operation = `destroy && Db.is_valid_ref __context self
547561
then Db.VDI.destroy ~__context ~self;
548562

549563
(* destroy all the VBDs now rather than wait for the GC thread. This helps
@@ -558,6 +572,15 @@ let destroy ~__context ~self =
558572
|> List.iter (fun self -> Db.VM.set_suspend_VDI ~__context ~self ~value:Ref.null);
559573
end
560574

575+
let destroy = destroy_and_data_destroy_common ~operation:`destroy
576+
577+
let data_destroy ~__context ~self =
578+
if Db.VDI.get_type ~__context ~self <> `cbt_metadata then begin
579+
destroy_and_data_destroy_common ~__context ~self ~operation:`data_destroy;
580+
Db.VDI.set_type ~__context ~self ~value:`cbt_metadata;
581+
update_allowed_operations ~__context ~self
582+
end
583+
561584
let resize_online ~__context ~vdi ~size =
562585
Sm.assert_pbd_is_plugged ~__context ~sr:(Db.VDI.get_SR ~__context ~self:vdi);
563586
Xapi_vdi_helpers.assert_managed ~__context ~vdi;

ocaml/xapi/xapi_vdi.mli

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ val snapshot :
148148
vdi:[ `VDI ] API.Ref.t ->
149149
driver_params:(string * string) list -> [ `VDI ] API.Ref.t
150150
val destroy : __context:Context.t -> self:[ `VDI ] API.Ref.t -> unit
151+
val data_destroy : __context:Context.t -> self:[ `VDI ] API.Ref.t -> unit
151152
val resize_online :
152153
__context:Context.t -> vdi:[ `VDI ] API.Ref.t -> size:int64 -> unit
153154
val resize :

0 commit comments

Comments
 (0)