diff --git a/ocaml/database/redo_log.ml b/ocaml/database/redo_log.ml index 6185ea5bcc8..663b06bbf3e 100644 --- a/ocaml/database/redo_log.ml +++ b/ocaml/database/redo_log.ml @@ -48,8 +48,17 @@ let mib megabytes = let ( ** ) = Int64.mul in Int64.of_int megabytes ** 1024L ** 1024L -(* Make sure we have plenty of room for the database *) -let minimum_vdi_size, recommended_vdi_size = (mib 256, mib 4096) +(* Make sure we have plenty of room for the database + There is also a 4MiB statefile, so make the sum be 4GB, which is easier to document. +*) +let minimum_vdi_size = + let ( // ) = Int64.div and ( ** ) = Int64.mul and ( -- ) = Int64.sub in + let align = mib 4 in + ((4_000_000_000L // align) + -- 2L + (* -2 because we also need room for a statefile, and an 'empty' SR seems to have a utilization of 4MiB *) + ) + ** align let redo_log_sm_config = [("type", "raw")] diff --git a/ocaml/database/redo_log.mli b/ocaml/database/redo_log.mli index 800922c7b0a..710612fe9fa 100644 --- a/ocaml/database/redo_log.mli +++ b/ocaml/database/redo_log.mli @@ -21,9 +21,6 @@ val get_static_device : string -> string option val minimum_vdi_size : int64 (** Minimum size for redo log VDI *) -val recommended_vdi_size : int64 -(** Recommended size for redo log VDI *) - val redo_log_sm_config : (string * string) list (** SM config for redo log VDI *) diff --git a/ocaml/xapi/xapi_pool.ml b/ocaml/xapi/xapi_pool.ml index 240075c9edb..11a9dd76e47 100644 --- a/ocaml/xapi/xapi_pool.ml +++ b/ocaml/xapi/xapi_pool.ml @@ -2859,7 +2859,7 @@ let create_redo_log_vdi ~__context ~sr = Client.VDI.create ~rpc ~session_id ~name_label:"Metadata redo-log" ~name_description: "Used when HA is disabled, while extra security is still desired" - ~sR:sr ~virtual_size:Redo_log.recommended_vdi_size ~_type:`redo_log + ~sR:sr ~virtual_size:Redo_log.minimum_vdi_size ~_type:`redo_log ~sharable:true ~read_only:false ~other_config:[] ~xenstore_data:[] ~sm_config:Redo_log.redo_log_sm_config ~tags:[] ) diff --git a/ocaml/xapi/xapi_sr.ml b/ocaml/xapi/xapi_sr.ml index ec895852553..0508f5384c5 100644 --- a/ocaml/xapi/xapi_sr.ml +++ b/ocaml/xapi/xapi_sr.ml @@ -946,7 +946,7 @@ let find_or_create_metadata_vdi ~__context ~sr = Helpers.call_api_functions ~__context (fun rpc session_id -> Client.VDI.create ~rpc ~session_id ~name_label:"Metadata for DR" ~name_description:"Used for disaster recovery" ~sR:sr - ~virtual_size:Redo_log.recommended_vdi_size ~_type:`metadata + ~virtual_size:Redo_log.minimum_vdi_size ~_type:`metadata ~sharable:false ~read_only:false ~other_config:[] ~xenstore_data:[] ~sm_config:Redo_log.redo_log_sm_config ~tags:[] ) diff --git a/ocaml/xapi/xha_metadata_vdi.ml b/ocaml/xapi/xha_metadata_vdi.ml index 311edc71394..d824bf6a493 100644 --- a/ocaml/xapi/xha_metadata_vdi.ml +++ b/ocaml/xapi/xha_metadata_vdi.ml @@ -24,8 +24,8 @@ let create ~__context ~sr = Helpers.call_api_functions ~__context (fun rpc session_id -> Client.VDI.create ~rpc ~session_id ~name_label:"Metadata for HA" ~name_description:"Used for master failover" ~sR:sr - ~virtual_size:Redo_log.recommended_vdi_size ~_type:`redo_log - ~sharable:true ~read_only:false ~other_config:[] ~xenstore_data:[] + ~virtual_size:Redo_log.minimum_vdi_size ~_type:`redo_log ~sharable:true + ~read_only:false ~other_config:[] ~xenstore_data:[] ~sm_config:Redo_log.redo_log_sm_config ~tags:[] ) diff --git a/ocaml/xapi/xha_statefile.ml b/ocaml/xapi/xha_statefile.ml index 7d2e082f19e..357ad1bd6b2 100644 --- a/ocaml/xapi/xha_statefile.ml +++ b/ocaml/xapi/xha_statefile.ml @@ -29,13 +29,49 @@ open Client (** Return the minimum size of an HA statefile, as of XenServer HA state-file description vsn 1.3 *) -let minimum_size = +let minimum_statefile_size = let ( ** ) = Int64.mul and ( ++ ) = Int64.add in let global_section_size = 4096L and host_section_size = 4096L and maximum_number_of_hosts = 64L in global_section_size ++ (maximum_number_of_hosts ** host_section_size) +let round_to ~align n = Int64.(div (add n @@ sub align 1L) align |> mul align) + +(* SM doesn't actually allow us to create VDIs smaller than 4MiB, so we need to round up *) +let minimum_sr_size = + [minimum_statefile_size; Redo_log.minimum_vdi_size] + |> List.map @@ round_to ~align:Int64.(shift_left 1L 22) + |> List.fold_left Int64.add Int64.zero + +let ha_fits_sr ~__context ~what ~sr ~typ ~minimum_size = + let ha_fits self = + Db.VDI.get_type ~__context ~self = typ + && Db.VDI.get_virtual_size ~__context ~self >= minimum_size + in + match List.filter ha_fits (Db.SR.get_VDIs ~__context ~self:sr) with + | x :: _ -> + debug "Would re-use existing %s: %s" what + (Db.VDI.get_uuid ~__context ~self:x) ; + Some x + | [] -> + debug "no suitable existing %s found; would have to create a fresh one" + what ; + let self = sr in + let size = Db.SR.get_physical_size ~__context ~self in + let utilisation = Db.SR.get_physical_utilisation ~__context ~self in + let free_space = Int64.sub size utilisation in + if free_space < minimum_sr_size then ( + let sr = Ref.string_of sr in + info "%s: SR %s size=%Ld utilisation=%Ld free=%Ld needed=%Ld" + __FUNCTION__ sr size utilisation free_space minimum_sr_size ; + raise + (Api_errors.Server_error + (Api_errors.sr_source_space_insufficient, [sr]) + ) + ) else + None + let check_sr_can_host_statefile ~__context ~sr ~cluster_stack = (* Check that each host has a PBD to this SR *) let pbds = Db.SR.get_PBDs ~__context ~self:sr in @@ -89,7 +125,7 @@ let check_sr_can_host_statefile ~__context ~sr ~cluster_stack = ] ) ) - | (_, sm) :: _ -> ( + | (_, sm) :: _ -> if (not (List.mem_assoc "VDI_GENERATE_CONFIG" sm.Db_actions.sM_features)) && not (List.mem_assoc "VDI_ATTACH_OFFLINE" sm.Db_actions.sM_features) @@ -98,40 +134,17 @@ let check_sr_can_host_statefile ~__context ~sr ~cluster_stack = (Api_errors.Server_error (Api_errors.sr_operation_not_supported, [Ref.string_of sr]) ) ; - let size = minimum_size in - let ha_fits self = - Db.VDI.get_type ~__context ~self = `ha_statefile - && Db.VDI.get_virtual_size ~__context ~self >= size - in - match List.filter ha_fits (Db.SR.get_VDIs ~__context ~self:sr) with - | x :: _ -> - debug "Would re-use existing statefile: %s" - (Db.VDI.get_uuid ~__context ~self:x) ; - Some x - | [] -> - debug - "no suitable existing statefile found; would have to create a \ - fresh one" ; - let self = sr in - let size = Db.SR.get_physical_size ~__context ~self in - let utilisation = Db.SR.get_physical_utilisation ~__context ~self in - let free_space = Int64.sub size utilisation in - if free_space < minimum_size then ( - let sr = Ref.string_of sr in - info "%s: SR %s size=%Ld utilisation=%Ld free=%Ld needed=%Ld" - __FUNCTION__ sr size utilisation free_space minimum_size ; - raise - (Api_errors.Server_error - (Api_errors.sr_source_space_insufficient, [sr]) - ) - ) else - None - ) + ha_fits_sr ~__context ~what:"statefile" ~sr + ~minimum_size:minimum_statefile_size ~typ:`ha_statefile let assert_sr_can_host_statefile ~__context ~sr ~cluster_stack = let (_ : 'a option) = check_sr_can_host_statefile ~__context ~sr ~cluster_stack in + let (_ : _ option) = + ha_fits_sr ~__context ~what:"redo-log" ~sr + ~minimum_size:Redo_log.minimum_vdi_size ~typ:`redo_log + in () let list_srs_which_can_host_statefile ~__context ~cluster_stack = @@ -146,7 +159,7 @@ let list_srs_which_can_host_statefile ~__context ~cluster_stack = let create ~__context ~sr ~cluster_stack = assert_sr_can_host_statefile ~__context ~sr ~cluster_stack ; - let size = minimum_size in + let size = minimum_statefile_size in Helpers.call_api_functions ~__context (fun rpc session_id -> Client.VDI.create ~rpc ~session_id ~name_label:"Statefile for HA" ~name_description:"Used for storage heartbeating" ~sR:sr