From e517caab5bf081504c0e1a8ab2df2cf757688879 Mon Sep 17 00:00:00 2001 From: Jon Ludlam Date: Wed, 26 Jan 2011 17:39:04 +0000 Subject: [PATCH] CP-1981: Track the vdi_activations in the local database The 'locks' for the VDIs, which are maintained by the SM backends now, are stored in xapi's database. In the event of a master failover, the pool database may revert to a previous backup, which is then resynced with reality. Unfortunately there's no logic to resync the SM backends' data, nor any hook point to do this. Ideally, the SM backends would store their critical data internally somehow, but until this happens xapi will have to contain the important logic to resynchronise these locks. This patch maintains a list of the VDIs that have been activatedi (including RW/RO mode) in the local database. Signed-off-by: Jon Ludlam --- ocaml/xapi/OMakefile | 1 + ocaml/xapi/sm.ml | 12 ++++- ocaml/xapi/xapi.ml | 5 +- ocaml/xapi/xapi_local_vdi_state.ml | 84 +++++++++++++++++++++++++++++ ocaml/xapi/xapi_local_vdi_state.mli | 5 ++ 5 files changed, 104 insertions(+), 3 deletions(-) create mode 100644 ocaml/xapi/xapi_local_vdi_state.ml create mode 100644 ocaml/xapi/xapi_local_vdi_state.mli diff --git a/ocaml/xapi/OMakefile b/ocaml/xapi/OMakefile index 800036cb..9416156b 100644 --- a/ocaml/xapi/OMakefile +++ b/ocaml/xapi/OMakefile @@ -151,6 +151,7 @@ XAPI_MODULES = $(COMMON) \ binpack \ xapi_local_session \ xapi_local_pbd_state \ + xapi_local_vdi_state \ xha_interface \ xha_statefile \ xha_metadata_vdi \ diff --git a/ocaml/xapi/sm.ml b/ocaml/xapi/sm.ml index 99de187a..25c8365a 100644 --- a/ocaml/xapi/sm.ml +++ b/ocaml/xapi/sm.ml @@ -159,12 +159,20 @@ let vdi_detach dconf driver sr vdi = let vdi_activate dconf driver sr vdi writable = debug "vdi_activate" driver (sprintf "sr=%s vdi=%s" (Ref.string_of sr) (Ref.string_of vdi)); let call = Sm_exec.make_call ~sr_ref:sr ~vdi_ref:vdi dconf "vdi_activate" [ sprintf "%b" writable ] in - Sm_exec.parse_unit (Sm_exec.exec_xmlrpc (driver_type driver) (driver_filename driver) call) + let Some loc = call.Sm_exec.vdi_location in + Xapi_local_vdi_state.activate loc writable; + try + Sm_exec.parse_unit (Sm_exec.exec_xmlrpc (driver_type driver) (driver_filename driver) call) + with e -> + Xapi_local_vdi_state.deactivate loc; + raise e let vdi_deactivate dconf driver sr vdi = debug "vdi_deactivate" driver (sprintf "sr=%s vdi=%s" (Ref.string_of sr) (Ref.string_of vdi)); let call = Sm_exec.make_call ~sr_ref:sr ~vdi_ref:vdi dconf "vdi_deactivate" [] in - Sm_exec.parse_unit (Sm_exec.exec_xmlrpc (driver_type driver) (driver_filename driver) call) + let Some loc = call.Sm_exec.vdi_location in + Sm_exec.parse_unit (Sm_exec.exec_xmlrpc (driver_type driver) (driver_filename driver) call); + Xapi_local_vdi_state.deactivate loc let vdi_snapshot dconf driver driver_params sr vdi = debug "vdi_snapshot" driver (sprintf "sr=%s vdi=%s driver_params=[%s]" (Ref.string_of sr) (Ref.string_of vdi) (String.concat "; " (List.map (fun (k, v) -> k ^ "=" ^ v) driver_params))); diff --git a/ocaml/xapi/xapi.ml b/ocaml/xapi/xapi.ml index 0f6ff995..d5add7ba 100644 --- a/ocaml/xapi/xapi.ml +++ b/ocaml/xapi/xapi.ml @@ -343,7 +343,10 @@ let init_local_database () = (* We've just rebooted, so we clear the flag that stops the host being disabled during the reboot *) if !Xapi_globs.on_system_boot then Localdb.put Constants.host_disabled_until_reboot "false"; (* After a reboot we assume all PBDs have currently_attached = false *) - if !Xapi_globs.on_system_boot then Xapi_local_pbd_state.clear () + if !Xapi_globs.on_system_boot then Xapi_local_pbd_state.clear (); + + (* Ditto for VDIs *) + if !Xapi_globs.on_system_boot then Xapi_local_vdi_state.clear () let bring_up_management_if ~__context () = diff --git a/ocaml/xapi/xapi_local_vdi_state.ml b/ocaml/xapi/xapi_local_vdi_state.ml new file mode 100644 index 00000000..7f0c7935 --- /dev/null +++ b/ocaml/xapi/xapi_local_vdi_state.ml @@ -0,0 +1,84 @@ +(* Some functions to record vdi_activates in the local database such that, in the + event of a pool-master change (and possible subsequent reversion of the database) + we can resynchronise the VDI.sm_config fields relating to attachment *) + +(* Note that when the SM backends can manage this themselves, this code should be + removed *) + +(* We could augment this to include attachments as well as activations. This could then + be used as input to the refcounting code, to help prevent a leaked attachment *) + +open Stringext +open Threadext + +module StringSet = Set.Make(String) + +module D=Debug.Debugger(struct let name="xapi_local_vdi_state" end) +open D + +(* The local database keys to use *) +let key_ro = "vdi_activations_ro" +let key_rw = "vdi_activations_rw" + +(* Perform all mutations holding this lock *) +let lock = Mutex.create () + +(* Marshalling/unmarshalling *) +let set_of_string str = + let t x = match String_unmarshall_helper.set (fun x -> x) x with + | [ r ] -> r + | _ -> failwith (Printf.sprintf "Failed to parse VDI location info from local database: %s" x) in + try + let list = String_unmarshall_helper.set t str in + List.fold_left (fun set elt -> StringSet.add elt set) StringSet.empty list + with e -> + error "Unexpected error in Xapi_local_vdi_state.set_of_string: %s" (Printexc.to_string e); + StringSet.empty + +let string_of_set set = + let list = StringSet.fold (fun elt list -> elt::list) set [] in + let t x = String_marshall_helper.set (fun x -> x) [ x ] in + String_marshall_helper.set t list + +(* Helper function *) +let mutate rw f = + let key = if rw then key_rw else key_ro in + Mutex.execute lock (fun () -> + let cur = Localdb.get_with_default key "()" in + let set = set_of_string cur in + let newset = f set in + let value = string_of_set newset in + Localdb.put key value) + +(* Exposed functions *) +let activate location rw = + mutate rw (fun set -> StringSet.add location set) + +let deactivate location = + mutate true (fun set -> StringSet.remove location set); + mutate false (fun set -> StringSet.remove location set) + +let clear () = + mutate true (fun _ -> StringSet.empty); + mutate false (fun _ -> StringSet.empty) + +let iter f = + let (set_ro, set_rw) = Mutex.execute lock (fun () -> + let get key = + let cur = Localdb.get_with_default key "()" in + set_of_string cur + in + (get key_ro, get key_rw) + ) in + StringSet.iter (f false) set_ro; + StringSet.iter (f true) set_rw + +let fold f x = + let (set_ro, set_rw) = Mutex.execute lock (fun () -> + let get key = + let cur = Localdb.get_with_default key "()" in + set_of_string cur + in + (get key_ro, get key_rw) + ) in + StringSet.fold (f true) set_rw (StringSet.fold (f false) set_ro x) diff --git a/ocaml/xapi/xapi_local_vdi_state.mli b/ocaml/xapi/xapi_local_vdi_state.mli new file mode 100644 index 00000000..8dc4b8ec --- /dev/null +++ b/ocaml/xapi/xapi_local_vdi_state.mli @@ -0,0 +1,5 @@ +val activate : string -> bool -> unit +val deactivate : string -> unit +val clear : unit -> unit +val iter : (bool -> string -> unit) -> unit +val fold : (bool -> string -> 'a -> 'a) -> 'a -> 'a -- 2.39.5