From 3af9b0f8c925631bb8b9b923b18a218da13819a2 Mon Sep 17 00:00:00 2001 From: David Scott Date: Wed, 26 Jan 2011 17:39:06 +0000 Subject: [PATCH] Rewrite the pool database restore processing to use the type-safe Db.* API, now that it's possible to have multiple active databases. Fold the 'post_restore_hook' logic into a single 'prepare' step as there was no special reason it had to be done so late (and in the wrong layer of the code) Signed-off-by: David Scott --- ocaml/database/db_backend.ml | 37 --------- ocaml/database/db_cache_impl.ml | 14 +--- ocaml/xapi/pool_db_backup.ml | 134 ++++++++++++++++---------------- ocaml/xapi/xapi.ml | 6 +- 4 files changed, 76 insertions(+), 115 deletions(-) diff --git a/ocaml/database/db_backend.ml b/ocaml/database/db_backend.ml index 60caad04..242be357 100644 --- a/ocaml/database/db_backend.ml +++ b/ocaml/database/db_backend.ml @@ -64,40 +64,3 @@ let blow_away_non_persistent_fields (schema: Schema.t) db = TableSet.add tblname tbl' acc) ts TableSet.empty) db -(* after restoring from backup, we take the master's host record and make it reflect us *) -let post_restore_hook db = - debug "Executing post_restore_hook"; - let my_installation_uuid = Xapi_inventory.lookup Xapi_inventory._installation_uuid in - let my_control_uuid = Xapi_inventory.lookup Xapi_inventory._control_domain_uuid in - - let not_found = "" in - - (* Look up the pool master: *) - let pools = TableSet.find Db_names.pool (Database.tableset db) in - let master = Table.fold (fun _ref r acc -> Row.find Db_names.master r) pools not_found in - - let update_master_host db : Database.t = - if master = not_found then begin - debug "No master record to update"; - db - end else begin - set_field_in_row Db_names.host master Db_names.uuid my_installation_uuid db - end in - - let update_master_dom0 db : Database.t = - (* Look up the pool master's control domain: *) - let vms = TableSet.find Db_names.vm (Database.tableset db) in - let master_dom0 = Table.fold (fun _ref r acc -> - if - Row.find Db_names.resident_on r = master && - (Row.find Db_names.is_control_domain r = "true") - then _ref else acc) vms not_found in - if master_dom0 = not_found then begin - debug "No master control domain record to update"; - db - end else begin - set_field_in_row Db_names.vm master_dom0 Db_names.uuid my_control_uuid db - end in - - (update_master_host ++ update_master_dom0) db - diff --git a/ocaml/database/db_cache_impl.ml b/ocaml/database/db_cache_impl.ml index 46c54ae1..44f7a515 100644 --- a/ocaml/database/db_cache_impl.ml +++ b/ocaml/database/db_cache_impl.ml @@ -270,7 +270,7 @@ let process_structured_field t (key,value) tblname fld objref proc_fn_selector = process_structured_field_locked t (key,value) tblname fld objref proc_fn_selector) (* -------------------------------------------------------------------- *) - + let load connections default_schema = (* We also consider populating from the HA metadata LUN and the general metadata LUN *) @@ -280,15 +280,9 @@ let load connections default_schema = (* If we have a temporary_restore_path (backup uploaded in previous run of xapi process) then restore from that *) let populate db = - Printf.printf "populate\n%!"; - let backup = Parse_db_conf.make Xapi_globs.db_temporary_restore_path in - match Db_connections.choose [ backup ] with - | Some c -> Db_backend.post_restore_hook (Backend_xml.populate default_schema c) - | None -> - begin match Db_connections.choose connections with - | Some c -> Backend_xml.populate default_schema c - | None -> db (* empty *) - end in + match Db_connections.choose connections with + | Some c -> Backend_xml.populate default_schema c + | None -> db in (* empty *) let empty = Database.update_manifest (Manifest.update_schema (fun _ -> Some (default_schema.Schema.major_vsn, default_schema.Schema.minor_vsn))) (Database.make default_schema) in diff --git a/ocaml/xapi/pool_db_backup.ml b/ocaml/xapi/pool_db_backup.ml index abe505b2..9e5c5678 100644 --- a/ocaml/xapi/pool_db_backup.ml +++ b/ocaml/xapi/pool_db_backup.ml @@ -50,52 +50,55 @@ let version_check db = raise (Api_errors.Server_error(Api_errors.restore_incompatible_version, [])) end -(** Restore all of our state from an XML backup. This includes the pool config, token etc *) -let restore_from_xml __context dry_run (xml_filename: string) = - debug "attempting to restore database from %s" xml_filename; - let db = Db_xml.From.file (Schema.of_datamodel ()) xml_filename in +(** Makes a new database suitable for xapi by rewriting some configuration from the current + database. *) +let prepare_database_for_restore ~old_context ~new_context = - (* Do not write the pool_conf: it contains only the master/slave configuration which is - managed separately *) - version_check db; (* To prevent duplicate installation_uuids or duplicate IP address confusing the "no other masters" check we remove all hosts from the backup except the master. *) - let ts = Database.tableset db in - let hosts = TableSet.find Db_names.host ts in - let uuid_to_ref = Table.fold - (fun _ref r acc -> (Row.find "uuid" r, _ref)::acc) hosts [] in (* Look up the pool master: *) - let pools = TableSet.find Db_names.pool ts in - let master = Table.fold - (fun _ref r acc -> Row.find Db_names.master r) pools "" in + let pool = Helpers.get_pool ~__context:new_context in + let master = Db.Pool.get_master ~__context:new_context ~self:pool in (* Remove all slaves from the database *) - let master_row = Table.find master hosts in - let hosts' = Table.add master master_row Table.empty in + List.iter (fun self -> + if self <> master then begin + List.iter (fun self -> Db.PIF.destroy ~__context:new_context ~self) + (Db.Host.get_PIFs ~__context:new_context ~self); + Db.Host.destroy ~__context:new_context ~self + end) + (Db.Host.get_all ~__context:new_context); - let db = set_table Db_names.host hosts' db in + (* Set the master's uuid to ours *) + let my_installation_uuid = Xapi_inventory.lookup Xapi_inventory._installation_uuid in + Db.Host.set_uuid ~__context:new_context ~self:master ~value:my_installation_uuid; - debug "All hosts: [ %s ]" (String.concat "; " (List.map fst uuid_to_ref)); - debug "Previous master: %s" master; - + (* Set the master's dom0 to ours *) + let my_control_uuid = Xapi_inventory.lookup Xapi_inventory._control_domain_uuid in + begin match List.filter (fun self -> Db.VM.get_is_control_domain ~__context:new_context ~self) + (Db.Host.get_resident_VMs ~__context:new_context ~self:master) with + | [ dom0 ] -> + Db.VM.set_uuid ~__context:new_context ~self:dom0 ~value:my_control_uuid + | _ -> error "Failed to set master control domain's uuid" + end; + (* Rewrite this host's PIFs' MAC addresses based on device name. *) (* First inspect the current machine's configuration and build up a table of device name -> PIF reference. *) - let localhost = Helpers.get_localhost ~__context in - let all_pifs = Db.Host.get_PIFs ~__context ~self:localhost in + let all_pifs = Db.Host.get_PIFs ~__context:old_context ~self:(Helpers.get_localhost ~__context:old_context) in let device_to_ref = - let physical = List.filter (fun self -> Db.PIF.get_physical ~__context ~self) all_pifs in - List.map (fun self -> Db.PIF.get_device ~__context ~self, self) physical in + let physical = List.filter (fun self -> Db.PIF.get_physical ~__context:old_context ~self) all_pifs in + List.map (fun self -> Db.PIF.get_device ~__context:old_context ~self, self) physical in (* Since it's difficult for us to change the /etc/xensource-inventory and the ifcfg- files, we /preserve/ the current management PIF across the restore. NB this interface might be a bond or a vlan. *) let mgmt_dev = - match List.filter (fun self -> Db.PIF.get_management ~__context ~self) all_pifs with - | [ dev ] -> Some (Db.PIF.get_device ~__context ~self:dev) + match List.filter (fun self -> Db.PIF.get_management ~__context:old_context ~self) all_pifs with + | [ dev ] -> Some (Db.PIF.get_device ~__context:old_context ~self:dev) | _ -> None (* no management interface configured *) in (* The PIFs of the master host in the backup need their MAC addresses adjusting @@ -111,56 +114,53 @@ let restore_from_xml __context dry_run (xml_filename: string) = PIFs whose device name are not recognised or those belonging to (now dead) slaves are forgotten. *) - let pifs = TableSet.find "PIF" ts in - - let pifs' = ref Table.empty in + let found_mgmt_if = ref false in let ifs_in_backup = ref [] in - Table.iter - (fun _ref r -> - if Row.find "host" r = master then begin - let device = Row.find "device" r in - ifs_in_backup := device :: !ifs_in_backup; - - let uuid = Row.find "uuid" r in - let physical = bool_of_string (Row.find "physical" r) in + List.iter + (fun self -> + let device = Db.PIF.get_device ~__context:new_context ~self in + ifs_in_backup := device :: !ifs_in_backup; - let is_mgmt = Some device = mgmt_dev in - let pif = Row.add "management" (string_of_bool is_mgmt) r in - if is_mgmt then found_mgmt_if := true; + let uuid = Db.PIF.get_uuid ~__context:new_context ~self in + let physical = Db.PIF.get_physical ~__context:new_context ~self in + let is_mgmt = Some device = mgmt_dev in + Db.PIF.set_management ~__context:new_context ~self ~value:is_mgmt; + if is_mgmt then found_mgmt_if := true; - (* We only need to rewrite the MAC addresses of physical PIFs *) - let pif = - if not physical then pif - else begin - (* If this is a physical PIF but we can't find the device name - on the restore target, bail out. *) - if not(List.mem_assoc device device_to_ref) - then raise (Api_errors.Server_error(Api_errors.restore_target_missing_device, [ device ])); - (* Otherwise rewrite the MAC address to match the current machine - and set the management flag accordingly *) - let existing_pif = List.assoc device device_to_ref in - Row.add "MAC" (Db.PIF.get_MAC ~__context ~self:existing_pif) pif - end in - - debug "Rewriting PIF uuid %s device %s (management %s -> %s) MAC %s -> %s" - uuid device (Row.find "management" r) (Row.find "management" pif) - (Row.find "MAC" r) (Row.find "MAC" pif); - pifs' := Table.add _ref pif !pifs' - end else begin - (* don't bother copying forgotten slave PIFs *) - debug "Forgetting slave PIF uuid %s" (Row.find "uuid" r) - end - ) pifs; - let db = set_table "PIF" !pifs' db in + (* We only need to rewrite the MAC addresses of physical PIFs *) + if physical then begin + (* If this is a physical PIF but we can't find the device name + on the restore target, bail out. *) + if not(List.mem_assoc device device_to_ref) + then raise (Api_errors.Server_error(Api_errors.restore_target_missing_device, [ device ])); + (* Otherwise rewrite the MAC address to match the current machine + and set the management flag accordingly *) + let existing_pif = List.assoc device device_to_ref in + Db.PIF.set_MAC ~__context:new_context ~self ~value:(Db.PIF.get_MAC ~__context:old_context ~self:existing_pif) + end; + debug "Rewriting PIF uuid %s device %s management %b MAC %s" + uuid device is_mgmt (Db.PIF.get_MAC ~__context:new_context ~self); + ) (Db.Host.get_PIFs ~__context:new_context ~self:master); (* Check that management interface was synced up *) if not(!found_mgmt_if) && mgmt_dev <> None - then raise (Api_errors.Server_error(Api_errors.restore_target_mgmt_if_not_in_backup, !ifs_in_backup)); - + then raise (Api_errors.Server_error(Api_errors.restore_target_mgmt_if_not_in_backup, !ifs_in_backup)) + + +(** Restore all of our state from an XML backup. This includes the pool config, token etc *) +let restore_from_xml __context dry_run (xml_filename: string) = + debug "attempting to restore database from %s" xml_filename; + let db = Db_upgrade.generic_database_upgrade (Db_xml.From.file (Schema.of_datamodel ()) xml_filename) in + version_check db; + + let db_ref = Db_ref.in_memory (ref (ref db)) in + let new_context = Context.make ~database:db_ref "restore_db" in + + prepare_database_for_restore ~old_context:__context ~new_context; (* write manifest and unmarshalled db directly to db_temporary_restore_path, so its ready for us on restart *) if not(dry_run) - then Db_xml.To.file Xapi_globs.db_temporary_restore_path db + then Db_xml.To.file Xapi_globs.db_temporary_restore_path (Db_ref.get_database (Context.database_of new_context)) (** Called when a CLI user downloads a backup of the database *) let pull_database_backup_handler (req: Http.request) s = diff --git a/ocaml/xapi/xapi.ml b/ocaml/xapi/xapi.ml index 46338e53..dfd539c7 100644 --- a/ocaml/xapi/xapi.ml +++ b/ocaml/xapi/xapi.ml @@ -70,7 +70,11 @@ open Db_cache_types let start_database_engine () = let schema = Schema.of_datamodel () in - let connections = Db_conn_store.read_db_connections () in + (* If the temporary restore file is present then we must populate from that *) + let connections = + if Sys.file_exists Xapi_globs.db_temporary_restore_path + then [ Parse_db_conf.make Xapi_globs.db_temporary_restore_path ] + else Db_conn_store.read_db_connections () in let t = Db_backend.make () in Db_cache_impl.make t connections schema; Db_cache_impl.sync connections (Db_ref.get_database t); -- 2.39.5