]> xenbits.xensource.com Git - xcp/xen-api.git/commitdiff
Rewrite the pool database restore processing to use the type-safe Db.* API, now that...
authorDavid Scott <dave.scott@eu.citrix.com>
Wed, 26 Jan 2011 17:39:06 +0000 (17:39 +0000)
committerDavid Scott <dave.scott@eu.citrix.com>
Wed, 26 Jan 2011 17:39:06 +0000 (17:39 +0000)
Signed-off-by: David Scott <dave.scott@eu.citrix.com>
ocaml/database/db_backend.ml
ocaml/database/db_cache_impl.ml
ocaml/xapi/pool_db_backup.ml
ocaml/xapi/xapi.ml

index 60caad0449ad721390805ce8b208f881932fe9a7..242be357efa773148ac33d8582968c2d7b882229 100644 (file)
@@ -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
-
index 46c54ae13c9440eda9896f4c872494f2b25e307e..44f7a515798cc00b8235d6ec9282b4bcfa801320 100644 (file)
@@ -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
 
index abe505b2edc7231cd99cf43d3c032b2f3db28105..9e5c567844a59b09ed83e889bd0d5b1e38b610c1 100644 (file)
@@ -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 =
index 46338e538c10f9876b12556df5ac13e56586dc02..dfd539c761a78a9f5e74cb0a807fad2ffe580fad 100644 (file)
@@ -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);