Skip to content

Commit

Permalink
CP-46179 create backup VDI with determinstic UUID
Browse files Browse the repository at this point in the history
When creating a backup VDI on an SR we want to derive the VDI's UUID
from the SR. This way we can't be later tricked into accepting a
different VDI as a backup.

Implement a hash that derives the VDI UUID and pass this UUID to SM to
be used.

Currently the creation of this VDI in Xapi is detected based on its
name-label: "Pool Metadata Backup". The SM stack usually creates a new
random UUID but will use a given UUID passed as "vdi_uuid" for its
"vdi_create" command. The difficulty in the implementation is that
"vdi_uuid" is used in other commands as well and we have to make sure to
set it only in the intended context. It is not straight forward to pass
the UUID from Xapi_vdi.create down to Sm.

Signed-off-by: Christian Lindig <[email protected]>
  • Loading branch information
Christian Lindig authored and lindig committed Mar 25, 2024
1 parent d5d6da8 commit bbc5a1f
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 13 deletions.
12 changes: 12 additions & 0 deletions ocaml/libs/uuid/uuidx.ml
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,15 @@ let string_of_uuid = to_string
let uuid_of_int_array = of_int_array

let int_array_of_uuid = to_int_array

module Hash = struct
(** Derive a deterministic UUID from a string: the same
string maps to the same UUID. We are using our own namespace; the
namespace is not a secret *)

let namespace =
let ns = "e93e0639-2bdb-4a59-8b46-352b3f408c19" in
Uuidm.(of_string ns |> Option.get)

let string str = Uuidm.v5 namespace str
end
8 changes: 8 additions & 0 deletions ocaml/libs/uuid/uuidx.mli
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,11 @@ val make_cookie : unit -> cookie
val cookie_of_string : string -> cookie

val string_of_cookie : cookie -> string

module Hash : sig
(** hash a string (deterministically) into a UUID. This uses
namespace UUID e93e0639-2bdb-4a59-8b46-352b3f408c19. *)

(* UUID Version 5 derived from argument string and namespace UUID *)
val string : string -> 'a t
end
6 changes: 3 additions & 3 deletions ocaml/xapi/sm.ml
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ let sr_update ~dbg dconf driver sr =
let call = Sm_exec.make_call ~sr_ref:sr dconf "sr_update" [] in
Sm_exec.parse_unit (Sm_exec.exec_xmlrpc ~dbg (driver_filename driver) call)

let vdi_create ~dbg dconf driver sr sm_config vdi_type size name_label
let vdi_create ~dbg ?vdi_uuid dconf driver sr sm_config vdi_type size name_label
name_description metadata_of_pool is_a_snapshot snapshot_time snapshot_of
read_only =
with_dbg ~dbg ~name:"vdi_create" @@ fun di ->
Expand All @@ -164,8 +164,8 @@ let vdi_create ~dbg dconf driver sr sm_config vdi_type size name_label
) ;
srmaster_only dconf ;
let call =
Sm_exec.make_call ~sr_ref:sr ~vdi_sm_config:sm_config ~vdi_type dconf
"vdi_create"
Sm_exec.make_call ?vdi_uuid ~sr_ref:sr ~vdi_sm_config:sm_config ~vdi_type
dconf "vdi_create"
[
sprintf "%Lu" size
; name_label
Expand Down
21 changes: 18 additions & 3 deletions ocaml/xapi/sm_exec.ml
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ type call = {
}

let make_call ?driver_params ?sr_sm_config ?vdi_sm_config ?vdi_type
?vdi_location ?new_uuid ?sr_ref ?vdi_ref (subtask_of, device_config) cmd
args =
?vdi_location ?new_uuid ?sr_ref ?vdi_ref ?vdi_uuid
(subtask_of, device_config) cmd args =
Server_helpers.exec_with_new_task "sm_exec" (fun __context ->
(* Only allow a subset of calls if the SR has been introduced by a DR task. *)
Option.iter
Expand Down Expand Up @@ -117,7 +117,22 @@ let make_call ?driver_params ?sr_sm_config ?vdi_sm_config ?vdi_type
Option.map (fun self -> Db.VDI.get_location ~__context ~self) vdi_ref
in
let vdi_uuid =
Option.map (fun self -> Db.VDI.get_uuid ~__context ~self) vdi_ref
match (cmd, vdi_ref, vdi_uuid) with
| "vdi_create", None, (Some x as uuid) ->
debug "%s: cmd=%s vdi_uuid=%s" __FUNCTION__ cmd x ;
uuid
(* when creating a VDI we sometimes want to provide the UUID
rather than letting the backend pick one. This is to
support backup VDIs CP-46179. So in that case, use the
provided UUID but not for other commands *)
| _, None, Some uuid ->
warn "%s: cmd=%s vdi_uuid=%s - should not happen" __FUNCTION__ cmd
uuid ;
None
| _, Some self, _ ->
Db.VDI.get_uuid ~__context ~self |> Option.some
| _, None, None ->
None
in
let vdi_on_boot =
Option.map
Expand Down
26 changes: 20 additions & 6 deletions ocaml/xapi/storage_smapiv1.ml
Original file line number Diff line number Diff line change
Expand Up @@ -691,19 +691,33 @@ module SMAPIv1 : Server_impl = struct
let uuid = require_uuid vi in
vdi_info_from_db ~__context (Db.VDI.get_by_uuid ~__context ~uuid)

let create _context ~dbg ~sr ~vdi_info =
let create _context ~dbg ~sr ~(vdi_info : Storage_interface.vdi_info) =
with_dbg ~name:"VDI.create" ~dbg @@ fun di ->
let dbg = Debuginfo.to_string di in
try
Server_helpers.exec_with_new_task "VDI.create"
~subtask_of:(Ref.of_string dbg) (fun __context ->
let sr = Db.SR.get_by_uuid ~__context ~uuid:(s_of_sr sr) in
let sr_uuid = s_of_sr sr in
let sr = Db.SR.get_by_uuid ~__context ~uuid:sr_uuid in
let vi =
(* we want to set vdi_uuid when creating a backup VDI with
a specific UUID. SM picks up vdi_uuid instead of creating
a new random UUID; Cf. Xapi_vdi.create *)
let vdi_uuid =
match vdi_info.uuid with
| Some uuid when uuid = Uuidx.(Hash.string sr_uuid |> to_string)
->
info "%s: creating a backup VDI %s" __FUNCTION__ uuid ;
vdi_info.uuid
| _ ->
None
in
Sm.call_sm_functions ~__context ~sR:sr (fun device_config _type ->
Sm.vdi_create ~dbg device_config _type sr vdi_info.sm_config
vdi_info.ty vdi_info.virtual_size vdi_info.name_label
vdi_info.name_description vdi_info.metadata_of_pool
vdi_info.is_a_snapshot vdi_info.snapshot_time
Sm.vdi_create ~dbg ?vdi_uuid device_config _type sr
vdi_info.sm_config vdi_info.ty vdi_info.virtual_size
vdi_info.name_label vdi_info.name_description
vdi_info.metadata_of_pool vdi_info.is_a_snapshot
vdi_info.snapshot_time
(s_of_vdi vdi_info.snapshot_of)
vdi_info.read_only
)
Expand Down
16 changes: 15 additions & 1 deletion ocaml/xapi/xapi_vdi.ml
Original file line number Diff line number Diff line change
Expand Up @@ -625,13 +625,27 @@ let create ~__context ~name_label ~name_description ~sR ~virtual_size ~_type
| `cbt_metadata ->
"cbt_metadata"
in
(* special case: we want to use a specific UUID for Pool Meta Data
Backup *)
let uuid_ =
match (_type, name_label) with
| `user, "Pool Metadata Backup" ->
let sr = Db.SR.get_uuid ~__context ~self:sR in
let uuid = Uuidx.(Hash.string sr |> to_string) in
info "%s: using deterministic UUID for '%s' VDI: %s" __FUNCTION__
name_label uuid ;
Some uuid
| _ ->
None
in
let open Storage_access in
let task = Context.get_task_id __context in
let open Storage_interface in
let vdi_info =
{
Storage_interface.default_vdi_info with
name_label
uuid= uuid_
; name_label
; name_description
; ty= vdi_type
; read_only
Expand Down

0 comments on commit bbc5a1f

Please sign in to comment.