# HG changeset patch # User Jon Ludlam # Date 1285345650 -3600 # Node ID 243ef81d48f72655dc409b127eb39785e4a5f5c0 # Parent e8da99c62c3e9ae692285a3b5d016547bc503192 Local disk caching API work. Added pool-level API calls to enable/disable caching, host-level API calls for same, per VDI on_boot and allow_caching parameters, and CLI calls for all. Signed-off-by: Jon Ludlam diff -r e8da99c62c3e -r 243ef81d48f7 ocaml/client_records/record_util.ml --- a/ocaml/client_records/record_util.ml +++ b/ocaml/client_records/record_util.ml @@ -324,6 +324,11 @@ let i642sm_to_string sep x = String.concat sep (List.map (fun (a,b) -> Printf.sprintf "%Ld %s" a b) x) +let on_boot_to_string onboot = + match onboot with + | `reset -> "reset" + | `persist -> "persist" + (** Parse a string which might have a units suffix on the end *) let bytes_of_string field x = let isdigit c = c >= '0' && c <= '9' in diff -r e8da99c62c3e -r 243ef81d48f7 ocaml/client_records/records.ml --- a/ocaml/client_records/records.ml +++ b/ocaml/client_records/records.ml @@ -1057,6 +1057,7 @@ make_field ~name:"power-on-mode" ~get:(fun () -> (x ()).API.host_power_on_mode) (); make_field ~name:"power-on-config" ~get:(fun () -> Record_util.s2sm_to_string "; " (x ()).API.host_power_on_config) ~get_map:(fun () -> (x ()).API.host_power_on_config) (); + make_field ~name:"local-cache-sr" ~get:(fun () -> get_uuid_from_ref (x ()).API.host_local_cache_sr) (); ]} let vdi_record rpc session_id vdi = @@ -1113,6 +1114,10 @@ ~get_map:(fun () -> (x ()).API.vDI_xenstore_data) (); make_field ~name:"sm-config" ~get:(fun () -> Record_util.s2sm_to_string "; " (x ()).API.vDI_sm_config) ~get_map:(fun () -> (x ()).API.vDI_sm_config) (); + make_field ~name:"on-boot" ~get:(fun () -> Record_util.on_boot_to_string (x ()).API.vDI_on_boot) + ~set:(fun onboot -> Client.VDI.set_on_boot rpc session_id vdi (match onboot with "persist" -> `persist | "reset" -> `reset)) (); + make_field ~name:"allow-caching" ~get:(fun () -> string_of_bool (x ()).API.vDI_allow_caching) + ~set:(fun b -> Client.VDI.set_allow_caching rpc session_id vdi (bool_of_string b)) (); ]} let vbd_record rpc session_id vbd = @@ -1272,6 +1277,7 @@ make_field ~name:"sm-config" ~get:(fun () -> Record_util.s2sm_to_string "; " (x ()).API.sR_sm_config) ~get_map:(fun () -> (x ()).API.sR_sm_config) (); make_field ~name:"blobs" ~get:(fun () -> Record_util.s2brm_to_string get_uuid_from_ref "; " (x ()).API.sR_blobs) (); + make_field ~name:"local-cache-enabled" ~get:(fun () -> string_of_bool (x ()).API.sR_local_cache_enabled) (); ]} let pbd_record rpc session_id pbd = diff -r e8da99c62c3e -r 243ef81d48f7 ocaml/idl/api_errors.ml --- a/ocaml/idl/api_errors.ml +++ b/ocaml/idl/api_errors.ml @@ -62,6 +62,9 @@ let host_is_slave = "HOST_IS_SLAVE" let host_name_invalid = "HOST_NAME_INVALID" let host_has_resident_vms = "HOST_HAS_RESIDENT_VMS" +let hosts_failed_to_enable_caching = "HOSTS_FAILED_TO_ENABLE_CACHING" +let hosts_failed_to_disable_caching = "HOSTS_FAILED_TO_DISABLE_CACHING" +let host_cannot_see_SR = "HOST_CANNOT_SEE_SR" (* Host errors which explain why the host is in emergency mode *) let host_its_own_slave = "HOST_ITS_OWN_SLAVE" @@ -181,6 +184,7 @@ let vdi_incompatible_type = "VDI_INCOMPATIBLE_TYPE" let vdi_not_managed = "VDI_NOT_MANAGED" let vdi_io_error = "VDI_IO_ERROR" +let vdi_on_boot_mode_incompatable_with_operation = "VDI_ON_BOOT_MODE_INCOMPATABLE_WITH_OPERATION" let cannot_create_state_file = "CANNOT_CREATE_STATE_FILE" diff -r e8da99c62c3e -r 243ef81d48f7 ocaml/idl/datamodel.ml --- a/ocaml/idl/datamodel.ml +++ b/ocaml/idl/datamodel.ml @@ -3653,6 +3653,28 @@ ~hide_from_docs:true () +let host_enable_local_storage_caching = call ~flags:[`Session] + ~name:"enable_local_storage_caching" + ~in_product_since:rel_cowley + ~doc:"Enable the use of a local SR for caching purposes" + ~params:[ + Ref _host, "host", "The host"; + Ref _sr, "sr", "The SR to use as a local cache" + ] + ~allowed_roles:_R_POOL_OP + () + +let host_disable_local_storage_caching = call ~flags:[`Session] + ~name:"disable_local_storage_caching" + ~in_product_since:rel_cowley + ~doc:"Disable the use of a local SR for caching purposes" + ~params:[ + Ref _host, "host", "The host" + ] + ~allowed_roles:_R_POOL_OP + () + + (** Hosts *) let host = create_obj ~in_db:true ~in_product_since:rel_rio ~in_oss_since:oss_since_303 ~internal_deprecated_since:None ~persist:PersistEverything ~gen_constructor_destructor:false ~name:_host ~descr:"A physical host" ~gen_events:true @@ -3726,6 +3748,8 @@ host_set_cpu_features; host_reset_cpu_features; host_reset_networking; + host_enable_local_storage_caching; + host_disable_local_storage_caching; ] ~contents: ([ uid _host; @@ -3767,7 +3791,7 @@ field ~qualifier:DynamicRO ~in_product_since:rel_midnight_ride ~default_value:(Some (VMap [])) ~ty:(Map (String,String)) "bios_strings" "BIOS strings"; field ~qualifier:DynamicRO ~in_product_since:rel_midnight_ride ~default_value:(Some (VString "")) ~ty:String "power_on_mode" "The power on mode"; field ~qualifier:DynamicRO ~in_product_since:rel_midnight_ride ~default_value:(Some (VMap [])) ~ty:(Map(String, String)) "power_on_config" "The power on config"; - + field ~qualifier:DynamicRO ~in_product_since:rel_cowley ~default_value:(Some (VRef (Ref.string_of Ref.null))) ~ty:(Ref _sr) "local_cache_sr" "The SR that is used as a local cache"; ]) () @@ -4430,6 +4454,7 @@ field ~ty:Bool ~qualifier:DynamicRO ~in_oss_since:None ~internal_only:true "default_vdi_visibility" ""; field ~in_oss_since:None ~ty:(Map(String, String)) ~in_product_since:rel_miami ~qualifier:RW "sm_config" "SM dependent data" ~default_value:(Some (VMap [])); field ~qualifier:DynamicRO ~in_product_since:rel_orlando ~ty:(Map(String, Ref _blob)) ~default_value:(Some (VMap [])) "blobs" "Binary blobs associated with this SR"; + field ~qualifier:DynamicRO ~in_product_since:rel_cowley ~ty:Bool ~default_value:(Some (VBool false)) "local_cache_enabled" "True if this SR is assigned to be the local cache for its host"; ]) () @@ -4665,6 +4690,31 @@ ~allowed_roles:_R_VM_ADMIN () +let on_boot = Enum ("on_boot", [ "reset", "The VDI will be reset to the state it was in at the last clone"; +"persist", "The VDIs contents are persistent" ]) + + let vdi_set_on_boot = call + ~name:"set_on_boot" + ~in_oss_since:None + ~in_product_since:rel_cowley + ~params:[Ref _vdi, "self", "The VDI to modify"; + on_boot, "value", "The value to set"] + ~doc:"Set the value of the on_boot parameter" + ~hide_from_docs:true + ~allowed_roles:_R_VM_ADMIN + () + +let vdi_set_allow_caching = call + ~name:"set_allow_caching" + ~in_oss_since:None + ~in_product_since:rel_cowley + ~params:[Ref _vdi, "self", "The VDI to modify"; + Bool, "value", "The value to set"] + ~doc:"Set the value of the allow_caching parameter" + ~hide_from_docs:true + ~allowed_roles:_R_VM_ADMIN + () + (** A virtual disk *) let vdi = create_obj ~in_db:true ~in_product_since:rel_rio ~in_oss_since:oss_since_303 ~internal_deprecated_since:None ~persist:PersistEverything ~gen_constructor_destructor:true ~name:_vdi ~descr:"A virtual disk image" @@ -4685,6 +4735,8 @@ vdi_set_virtual_size; vdi_set_physical_utilisation; vdi_generate_config; + vdi_set_on_boot; + vdi_set_allow_caching; ] ~contents: ([ uid _vdi; @@ -4713,6 +4765,9 @@ field ~in_product_since:rel_orlando ~qualifier:DynamicRO ~ty:(Set (Ref _vdi)) "snapshots" "List pointing to all the VDIs snapshots."; field ~in_product_since:rel_orlando ~default_value:(Some (VDateTime Date.never)) ~qualifier:DynamicRO ~ty:DateTime "snapshot_time" "Date/time when this snapshot was created."; field ~writer_roles:_R_VM_OP ~in_product_since:rel_orlando ~default_value:(Some (VSet [])) ~ty:(Set String) "tags" "user-specified tags for categorization purposes"; + field ~in_product_since:rel_cowley ~qualifier:DynamicRO ~ty:Bool ~default_value:(Some (VBool false)) "allow_caching" "true if this VDI is to be cached in the local cache SR"; + field ~in_product_since:rel_cowley ~qualifier:DynamicRO ~ty:on_boot ~default_value:(Some (VEnum "persist")) "on_boot" "The behaviour of this VDI on a VM boot"; + ]) () @@ -5294,6 +5349,24 @@ ~result:(String, "An XMLRPC result") () +let pool_enable_local_storage_caching = call + ~name:"enable_local_storage_caching" + ~in_oss_since:None + ~in_product_since:rel_cowley + ~params:[Ref _pool, "self", "Reference to the pool"] + ~doc:"This call attempts to enable pool-wide local storage caching" + ~allowed_roles:_R_POOL_OP + () + +let pool_disable_local_storage_caching = call + ~name:"disable_local_storage_caching" + ~in_oss_since:None + ~in_product_since:rel_cowley + ~params:[Ref _pool, "self", "Reference to the pool"] + ~doc:"This call disables pool-wide local storage caching" + ~allowed_roles:_R_POOL_OP + () + (** A pool class *) let pool = create_obj @@ -5356,6 +5429,8 @@ ; pool_audit_log_append ; pool_set_vswitch_controller ; pool_test_archive_target + ; pool_enable_local_storage_caching + ; pool_disable_local_storage_caching ] ~contents: [uid ~in_oss_since:None _pool diff -r e8da99c62c3e -r 243ef81d48f7 ocaml/xapi/cli_frontend.ml --- a/ocaml/xapi/cli_frontend.ml +++ b/ocaml/xapi/cli_frontend.ml @@ -450,6 +450,42 @@ flags=[Host_selectors]; }; + "host-enable-local-storage-caching", + { + reqd=["sr-uuid"]; + optn=[]; + help="Enable local storage caching on the specified host"; + implementation=No_fd Cli_operations.host_enable_local_storage_caching; + flags=[Host_selectors]; + }; + + "host-disable-local-storage-caching", + { + reqd=[]; + optn=[]; + help="Disable local storage caching on the specified host"; + implementation=No_fd Cli_operations.host_disable_local_storage_caching; + flags=[Host_selectors]; + }; + + "pool-enable-local-storage-caching", + { + reqd=["uuid"]; + optn=[]; + help="Enable local storage caching across the pool"; + implementation=No_fd Cli_operations.pool_enable_local_storage_caching; + flags=[]; + }; + + "pool-disable-local-storage-caching", + { + reqd=["uuid"]; + optn=[]; + help="Disable local storage caching across the pool"; + implementation=No_fd Cli_operations.pool_disable_local_storage_caching; + flags=[]; + }; + "host-shutdown", { reqd=[]; diff -r e8da99c62c3e -r 243ef81d48f7 ocaml/xapi/cli_operations.ml --- a/ocaml/xapi/cli_operations.ml +++ b/ocaml/xapi/cli_operations.ml @@ -3338,6 +3338,23 @@ in ignore(do_host_op rpc session_id op params []) +let host_enable_local_storage_caching printer rpc session_id params = + ignore(do_host_op rpc session_id (fun _ host -> + let sr_uuid = List.assoc "sr-uuid" params in + let sr = Client.SR.get_by_uuid rpc session_id sr_uuid in + Client.Host.enable_local_storage_caching rpc session_id (host.getref ()) sr + ) params ["sr-uuid"]) + +let host_disable_local_storage_caching printer rpc session_id params = + ignore(do_host_op rpc session_id (fun _ host -> Client.Host.disable_local_storage_caching rpc session_id (host.getref ())) params []) + +let pool_enable_local_storage_caching printer rpc session_id params = + let pool = List.hd (Client.Pool.get_all rpc session_id) in + Client.Pool.enable_local_storage_caching rpc session_id pool + +let pool_disable_local_storage_caching printer rpc session_id params = + let pool = List.hd (Client.Pool.get_all rpc session_id) in + Client.Pool.disable_local_storage_caching rpc session_id pool let host_set_power_on_mode printer rpc session_id params = let power_on_mode = List.assoc "power-on-mode" params in diff -r e8da99c62c3e -r 243ef81d48f7 ocaml/xapi/message_forwarding.ml --- a/ocaml/xapi/message_forwarding.ml +++ b/ocaml/xapi/message_forwarding.ml @@ -2184,6 +2184,14 @@ let reset_networking ~__context ~host = info "Host.reset_networking: host = '%s'" (host_uuid ~__context host); Local.Host.reset_networking ~__context ~host + + let enable_local_storage_caching ~__context ~host ~sr = + let local_fn = Local.Host.enable_local_storage_caching ~host ~sr in + do_op_on ~local_fn ~__context ~host (fun session_id rpc -> Client.Host.enable_local_storage_caching rpc session_id host sr) + + let disable_local_storage_caching ~__context ~host = + let local_fn = Local.Host.disable_local_storage_caching ~host in + do_op_on ~local_fn ~__context ~host (fun session_id rpc -> Client.Host.disable_local_storage_caching rpc session_id host) end module Host_crashdump = struct @@ -2793,6 +2801,24 @@ Sm.assert_session_has_internal_sr_access ~__context ~sr; Local.VDI.set_physical_utilisation ~__context ~self ~value + let ensure_vdi_not_on_running_vm ~__context ~self = + let vbds = Db.VDI.get_VBDs ~__context ~self in + List.iter (fun vbd -> + let vm = Db.VBD.get_VM ~__context ~self:vbd in + let state = Db.VM.get_power_state ~__context ~self:vm in + match state with + | `Halted -> () + | _ -> raise (Api_errors.Server_error(Api_errors.vm_bad_power_state, + [Ref.string_of vm; "halted"; Record_util.power_to_string state]))) vbds + + let set_on_boot ~__context ~self ~value = + ensure_vdi_not_on_running_vm ~__context ~self; + Local.VDI.set_on_boot ~__context ~self ~value + + let set_allow_caching ~__context ~self ~value = + ensure_vdi_not_on_running_vm ~__context ~self; + Local.VDI.set_allow_caching ~__context ~self ~value + (* know sr so just use SR forwarding policy direct here *) let create ~__context ~name_label ~name_description ~sR ~virtual_size ~_type ~sharable ~read_only ~other_config ~xenstore_data ~sm_config ~tags = info "VDI.create: SR = '%s'; name label = '%s'" (sr_uuid ~__context sR) name_label; @@ -2912,7 +2938,7 @@ (fun () -> forward_vdi_op ~local_fn ~__context ~self:vdi (fun session_id rpc -> Client.VDI.force_unlock rpc session_id vdi)) - + end module VBD = struct diff -r e8da99c62c3e -r 243ef81d48f7 ocaml/xapi/sm_exec.ml --- a/ocaml/xapi/sm_exec.ml +++ b/ocaml/xapi/sm_exec.ml @@ -57,10 +57,14 @@ vdi_ref: API.ref_VDI option; vdi_location: string option; vdi_uuid: string option; + vdi_on_boot: string option; + vdi_allow_caching : string option; (* Reference to the task which performs the call *) subtask_of: API.ref_task option; + local_cache_sr: string option; + cmd: string; args: string list; } @@ -73,6 +77,10 @@ then vdi_location else may (fun self -> Db.VDI.get_location ~__context ~self) vdi_ref in let vdi_uuid = may (fun self -> Db.VDI.get_uuid ~__context ~self) vdi_ref in + let vdi_on_boot = may (fun self -> + match Db.VDI.get_on_boot ~__context ~self with `persist -> "persist" | `reset -> "reset") vdi_ref in + let vdi_allow_caching = may (fun self -> string_of_bool (Db.VDI.get_allow_caching ~__context ~self)) vdi_ref in + let local_cache_sr = try Some (Db.SR.get_uuid ~__context ~self:(Db.Host.get_local_cache_sr ~__context ~self:(Helpers.get_localhost __context))) with _ -> None in let sr_uuid = may (fun self -> Db.SR.get_uuid ~__context ~self) sr_ref in { host_ref = !Xapi_globs.localhost_ref; session_ref = None; (* filled in at the last minute *) @@ -85,8 +93,11 @@ vdi_ref = vdi_ref; vdi_location = vdi_location; vdi_uuid = vdi_uuid; + vdi_on_boot = vdi_on_boot; + vdi_allow_caching = vdi_allow_caching; new_uuid = new_uuid; subtask_of = subtask_of; + local_cache_sr = local_cache_sr; cmd = cmd; args = args }) @@ -108,13 +119,15 @@ let vdi_ref = default [] (may (fun x -> [ "vdi_ref", XMLRPC.To.string (Ref.string_of x) ]) call.vdi_ref) in let vdi_location = default [] (may (fun x -> [ "vdi_location", XMLRPC.To.string x ]) call.vdi_location) in let vdi_uuid = default [] (may (fun x -> [ "vdi_uuid", XMLRPC.To.string x ]) call.vdi_uuid) in + let vdi_on_boot = default [] (may (fun x -> [ "vdi_on_boot", XMLRPC.To.string x ]) call.vdi_on_boot) in + let vdi_allow_caching = default [] (may (fun x -> [ "vdi_allow_caching", XMLRPC.To.string x ]) call.vdi_allow_caching) in let new_uuid = default [] (may (fun x -> [ "new_uuid", XMLRPC.To.string x ]) call.new_uuid) in let driver_params = default [] (may (fun x -> [ "driver_params", kvpairs x ]) call.driver_params) in let vdi_sm_config = default [] (may (fun x -> [ "vdi_sm_config", kvpairs x ]) call.vdi_sm_config) in let subtask_of = default [] (may (fun x -> [ "subtask_of", XMLRPC.To.string (Ref.string_of x) ]) call.subtask_of) in - - let all = common @ dc @ session_ref @ sr_sm_config @ sr_ref @ sr_uuid @ vdi_ref @ vdi_location @ vdi_uuid @ driver_params @ vdi_sm_config @ new_uuid @ subtask_of in + let local_cache_sr = default [] (may (fun x -> ["local_cache_sr", XMLRPC.To.string x]) call.local_cache_sr) in + let all = common @ dc @ session_ref @ sr_sm_config @ sr_ref @ sr_uuid @ vdi_ref @ vdi_location @ vdi_uuid @ driver_params @ vdi_sm_config @ new_uuid @ subtask_of @ vdi_on_boot @ vdi_allow_caching @ local_cache_sr in XMLRPC.To.methodCall call.cmd [ XMLRPC.To.structure all ] let methodResponse xml = @@ -260,6 +273,7 @@ let lookup_table = [ "SR_PROBE", Sr_probe; "SR_UPDATE", Sr_update; + "SR_SUPPORTS_LOCAL_CACHING", Sr_supports_local_caching; "VDI_CREATE", Vdi_create; "VDI_DELETE", Vdi_delete; "VDI_ATTACH", Vdi_attach; @@ -273,6 +287,7 @@ "VDI_UPDATE", Vdi_update; "VDI_INTRODUCE", Vdi_introduce; "VDI_GENERATE_CONFIG", Vdi_generate_config; + "VDI_RESET_ON_BOOT", Vdi_reset_on_boot; ] in let strings = XMLRPC.From.array XMLRPC.From.string (safe_assoc "capabilities" info) in List.iter (fun s -> diff -r e8da99c62c3e -r 243ef81d48f7 ocaml/xapi/smint.ml --- a/ocaml/xapi/smint.ml +++ b/ocaml/xapi/smint.ml @@ -32,15 +32,18 @@ (** Very primitive first attempt at a set of backend capabilities *) type capability = - | Sr_create | Sr_delete | Sr_attach | Sr_detach | Sr_scan | Sr_probe | Sr_update + | Sr_create | Sr_delete | Sr_attach | Sr_detach | Sr_scan | Sr_probe | Sr_update + | Sr_supports_local_caching | Vdi_create | Vdi_delete | Vdi_attach | Vdi_detach | Vdi_clone | Vdi_snapshot | Vdi_resize | Vdi_activate | Vdi_deactivate | Vdi_update | Vdi_introduce | Vdi_resize_online | Vdi_generate_config + | Vdi_reset_on_boot let all_capabilities = [ Sr_create; Sr_delete; Sr_attach; Sr_detach; Sr_scan; Sr_probe; Sr_update; + Sr_supports_local_caching; Vdi_create; Vdi_delete; Vdi_attach; Vdi_detach; Vdi_clone; Vdi_resize; Vdi_activate; Vdi_deactivate; Vdi_update; Vdi_introduce; diff -r e8da99c62c3e -r 243ef81d48f7 ocaml/xapi/xapi_host.ml --- a/ocaml/xapi/xapi_host.ml +++ b/ocaml/xapi/xapi_host.ml @@ -96,8 +96,14 @@ end (** Check that a) there are no running VMs present on the host, b) there are no VBDs currently - attached to dom0, and c) that there are no tasks running *) -let assert_can_shutdown ~__context ~host = + attached to dom0, c) host is disabled. + + This is approximately maintainance mode as defined by the gui. However, since + we haven't agreed on an exact definition of this mode, we'll not call this maintainance mode here, but we'll + use a synonym. According to http://thesaurus.com/browse/maintenance, bacon is a synonym + for maintainance, hence the name of the following function. +*) +let assert_bacon_mode ~__context ~host = if Db.Host.get_enabled ~__context ~self:host then raise (Api_errors.Server_error (Api_errors.host_not_disabled, [])); @@ -107,14 +113,14 @@ (* We always expect a control domain to be resident on a host *) if List.length vms > 1 then raise (Api_errors.Server_error (Api_errors.host_in_use, [ selfref; "vm"; List.hd (List.map Ref.string_of vms) ])); - debug "Shutdown test: VMs OK - %d running VMs" (List.length vms); + debug "Bacon test: VMs OK - %d running VMs" (List.length vms); let controldomain = List.find (fun vm -> Db.VM.get_resident_on ~__context ~self:vm = host && Db.VM.get_is_control_domain ~__context ~self:vm) (Db.VM.get_all ~__context) in let vbds = List.filter (fun vbd -> Db.VBD.get_VM ~__context ~self:vbd = controldomain && Db.VBD.get_currently_attached ~__context ~self:vbd) (Db.VBD.get_all ~__context) in if List.length vbds > 0 then raise (Api_errors.Server_error (Api_errors.host_in_use, [ selfref; "vbd"; List.hd (List.map Ref.string_of vbds) ])); - debug "Shutdown test: VBDs OK" + debug "Bacon test: VBDs OK" let pif_update_dhcp_address ~__context ~self = let network = Db.PIF.get_network ~__context ~self in @@ -463,7 +469,7 @@ end let shutdown_and_reboot_common ~__context ~host label description operation cmd = - assert_can_shutdown ~__context ~host; + assert_bacon_mode ~__context ~host; Xapi_ha.before_clean_shutdown_or_reboot ~__context ~host; Remote_requests.stop_request_thread(); @@ -648,6 +654,7 @@ ~bios_strings:[] ~power_on_mode:"" ~power_on_config:[] + ~local_cache_sr:Ref.null ; (* If the host we're creating is us, make sure its set to live *) Db.Host_metrics.set_last_updated ~__context ~self:metrics ~value:(Date.of_float (Unix.gettimeofday ())); @@ -1354,3 +1361,31 @@ let physical_features = List.assoc "physical_features" cpu_info in let cpu_info = List.replace_assoc "features_after_reboot" physical_features cpu_info in Db.Host.set_cpu_info ~__context ~self:host ~value:cpu_info + +let enable_local_storage_caching ~__context ~host ~sr = + assert_bacon_mode ~__context ~host; + let ty = Db.SR.get_type ~__context ~self:sr in + let pbds = Db.SR.get_PBDs ~__context ~self:sr in + let shared = Db.SR.get_shared ~__context ~self:sr in + let has_required_capability = + let caps = Sm.capabilities_of_driver ty in + List.mem Smint.Sr_supports_local_caching caps + in + debug "shared: %b. List.length pbds: %d. has_required_capability: %b" shared (List.length pbds) has_required_capability; + if (shared=false) && (List.length pbds = 1) && has_required_capability then begin + let pbd_host = Db.PBD.get_host ~__context ~self:(List.hd pbds) in + if pbd_host <> host then raise (Api_errors.Server_error (Api_errors.host_cannot_see_SR,[Ref.string_of host; Ref.string_of sr])); + Db.Host.set_local_cache_sr ~__context ~self:host ~value:sr; + Db.SR.set_local_cache_enabled ~__context ~self:sr ~value:true + end else begin + raise (Api_errors.Server_error (Api_errors.sr_operation_not_supported,[])) + end + +let disable_local_storage_caching ~__context ~host = + assert_bacon_mode ~__context ~host; + let sr = Db.Host.get_local_cache_sr ~__context ~self:host in + Db.Host.set_local_cache_sr ~__context ~self:host ~value:Ref.null; + try Db.SR.set_local_cache_enabled ~__context ~self:sr ~value:false with _ -> () + + + diff -r e8da99c62c3e -r 243ef81d48f7 ocaml/xapi/xapi_host.mli --- a/ocaml/xapi/xapi_host.mli +++ b/ocaml/xapi/xapi_host.mli @@ -252,6 +252,10 @@ (** Remove the feature mask, such that after a reboot all features of the CPU are enabled. *) val reset_cpu_features : __context:Context.t -> host:API.ref_host -> unit +(** Control the local caching behaviour of the host *) +val enable_local_storage_caching : __context:Context.t -> host:API.ref_host -> sr:API.ref_SR -> unit +val disable_local_storage_caching : __context:Context.t -> host:API.ref_host -> unit + (** Purge all network-related metadata associated with the given host. *) val reset_networking : __context:Context.t -> host:API.ref_host -> unit diff -r e8da99c62c3e -r 243ef81d48f7 ocaml/xapi/xapi_pool.ml --- a/ocaml/xapi/xapi_pool.ml +++ b/ocaml/xapi/xapi_pool.ml @@ -16,6 +16,7 @@ open Pervasiveext open Threadext open Stringext +open Listext module L = Debug.Debugger(struct let name="license" end) module D=Debug.Debugger(struct let name="xapi" end) @@ -1464,4 +1465,50 @@ "test_archive_target" config - +let enable_local_storage_caching ~__context ~self = + let srs = Db.SR.get_all_records ~__context in + let pbds = Db.PBD.get_all_records ~__context in + let hosts = Db.Host.get_all ~__context in + + (* Exception handler is to cope with transient PBDs with invalid references *) + let hosts_and_srs = List.filter_map (fun (pbdref,pbdrec) -> + try Some (pbdrec.API.pBD_host, pbdrec.API.pBD_SR, List.assoc pbdrec.API.pBD_SR srs) with _ -> None) pbds + in + + let acceptable = List.filter (fun (href,srref,srrec) -> + (not srrec.API.sR_shared) && + (List.length srrec.API.sR_PBDs = 1) && + (List.mem Smint.Sr_supports_local_caching (Sm.capabilities_of_driver srrec.API.sR_type)) + ) hosts_and_srs in + + let failed_hosts = + Helpers.call_api_functions ~__context + (fun rpc session_id -> + let failed = List.filter_map (fun host -> + let result = ref (Some host) in + let acceptable_srs = List.filter (fun (href,srref,srrec) -> href=host) acceptable in + List.iter (fun (href,ref,sr) -> + try Client.Host.enable_local_storage_caching rpc session_id host ref; result := None with _ -> ()) acceptable_srs; + !result + ) hosts in + failed) + in + if List.length failed_hosts > 0 then + raise (Api_errors.Server_error (Api_errors.hosts_failed_to_enable_caching, List.map Ref.string_of failed_hosts)) + else () + + +let disable_local_storage_caching ~__context ~self = + let hosts = Db.Host.get_all ~__context in + let failed_hosts = Helpers.call_api_functions ~__context + (fun rpc session_id -> + List.filter_map (fun host -> + try + Client.Host.disable_local_storage_caching ~rpc ~session_id ~host; + None + with _ -> + Some host) hosts) + in + if List.length failed_hosts > 0 then + raise (Api_errors.Server_error (Api_errors.hosts_failed_to_disable_caching, List.map Ref.string_of failed_hosts)) + else () diff -r e8da99c62c3e -r 243ef81d48f7 ocaml/xapi/xapi_pool.mli --- a/ocaml/xapi/xapi_pool.mli +++ b/ocaml/xapi/xapi_pool.mli @@ -194,3 +194,5 @@ val audit_log_append : __context:Context.t -> line:string -> unit val test_archive_target : __context:Context.t -> self:API.ref_pool -> config:API.string_to_string_map -> string +val enable_local_storage_caching : __context:Context.t -> self:API.ref_pool -> unit +val disable_local_storage_caching : __context:Context.t -> self:API.ref_pool -> unit diff -r e8da99c62c3e -r 243ef81d48f7 ocaml/xapi/xapi_sr.ml --- a/ocaml/xapi/xapi_sr.ml +++ b/ocaml/xapi/xapi_sr.ml @@ -232,7 +232,7 @@ ~physical_size: (-1L) ~content_type ~_type ~shared ~other_config:[] ~default_vdi_visibility:true - ~sm_config ~blobs:[] ~tags:[] in + ~sm_config ~blobs:[] ~tags:[] ~local_cache_enabled:false in update_allowed_operations ~__context ~self:sr_ref; (* Return ref of newly created sr *) diff -r e8da99c62c3e -r 243ef81d48f7 ocaml/xapi/xapi_vdi.ml --- a/ocaml/xapi/xapi_vdi.ml +++ b/ocaml/xapi/xapi_vdi.ml @@ -30,6 +30,9 @@ let _ref = Ref.string_of _ref' in let current_ops = record.Db_actions.vDI_current_operations in let vdi_is_sharable = record.Db_actions.vDI_sharable in + + let reset_on_boot = record.Db_actions.vDI_on_boot = `reset in + (* Policy: 1. any current_operation implies exclusivity; fail everything else 2. if doing a VM start then assume the sharing check is done elsewhere @@ -111,6 +114,8 @@ else None | `snapshot when record.Db_actions.vDI_sharable -> Some (Api_errors.vdi_is_sharable, [ _ref ]) + | `snapshot when reset_on_boot -> + Some (Api_errors.vdi_on_boot_mode_incompatable_with_operation, []) | _ -> None ) @@ -236,7 +241,8 @@ ~physical_utilisation:(-1L) ~_type ~sharable ~read_only ~xenstore_data ~sm_config - ~other_config ~storage_lock:false ~location ~managed:true ~missing:false ~parent:Ref.null ~tags:[]; + ~other_config ~storage_lock:false ~location ~managed:true ~missing:false ~parent:Ref.null ~tags:[] + ~on_boot:`persist ~allow_caching:false; ref let internal_db_introduce ~__context ~uuid ~name_label ~name_description ~sR ~_type ~sharable ~read_only ~other_config ~location ~xenstore_data ~sm_config = @@ -330,6 +336,9 @@ Db.VDI.set_sharable ~__context ~self:newvdi ~value:a.Db_actions.vDI_sharable; Db.VDI.set_other_config ~__context ~self:newvdi ~value:a.Db_actions.vDI_other_config; Db.VDI.set_xenstore_data ~__context ~self:newvdi ~value:a.Db_actions.vDI_xenstore_data; + Db.VDI.set_on_boot ~__context ~self:newvdi ~value:a.Db_actions.vDI_on_boot; + Db.VDI.set_allow_caching ~__context ~self:newvdi ~value:a.Db_actions.vDI_allow_caching; + (* Record the fact this is a snapshot *) (*(try Db.VDI.remove_from_other_config ~__context ~self:newvdi ~key:Xapi_globs.snapshot_of with _ -> ()); @@ -421,6 +430,8 @@ Db.VDI.set_sharable ~__context ~self:newvdi ~value:a.Db_actions.vDI_sharable; Db.VDI.set_other_config ~__context ~self:newvdi ~value:a.Db_actions.vDI_other_config; Db.VDI.set_xenstore_data ~__context ~self:newvdi ~value:a.Db_actions.vDI_xenstore_data; + Db.VDI.set_on_boot ~__context ~self:newvdi ~value:a.Db_actions.vDI_on_boot; + Db.VDI.set_allow_caching ~__context ~self:newvdi ~value:a.Db_actions.vDI_allow_caching; update_allowed_operations ~__context ~self:newvdi; newvdi @@ -467,7 +478,7 @@ let dst = Helpers.call_api_functions ~__context (fun rpc session_id -> - Client.VDI.create ~rpc ~session_id + let result = Client.VDI.create ~rpc ~session_id ~name_label:src.API.vDI_name_label ~name_description:src.API.vDI_name_description ~sR:sr @@ -477,9 +488,15 @@ ~read_only:src.API.vDI_read_only ~other_config:src.API.vDI_other_config ~xenstore_data:src.API.vDI_xenstore_data - ~sm_config:src.API.vDI_sm_config ~tags:[] + ~sm_config:src.API.vDI_sm_config ~tags:[] in + if src.API.vDI_on_boot = `reset then begin + try Client.VDI.set_on_boot ~rpc ~session_id ~self:result ~value:(`reset) with _ -> () + end; + result ) in try + Db.VDI.set_allow_caching ~__context ~self:dst ~value:src.API.vDI_allow_caching; + Sm_fs_ops.copy_vdi ~__context vdi dst; Db.VDI.remove_from_current_operations ~__context ~self:dst ~key:task_id; @@ -514,3 +531,24 @@ let set_physical_utilisation ~__context ~self ~value = Db.VDI.set_physical_utilisation ~__context ~self ~value + +let set_on_boot ~__context ~self ~value = + let sr = Db.VDI.get_SR ~__context ~self in + let ty = Db.SR.get_type ~__context ~self:sr in + let caps = Sm.capabilities_of_driver ty in + if not (List.mem Smint.Vdi_reset_on_boot caps) then + raise (Api_errors.Server_error(Api_errors.sr_operation_not_supported,[Ref.string_of sr])); + Sm.assert_pbd_is_plugged ~__context ~sr; + Sm.call_sm_vdi_functions ~__context ~vdi:self + (fun srconf srtype sr -> + let vdi_info = Sm.vdi_clone srconf srtype [] __context sr self in + let uuid = require_uuid vdi_info in + let ref = Db.VDI.get_by_uuid ~__context ~uuid in + Sm.vdi_delete srconf srtype sr ref); + Db.VDI.set_on_boot ~__context ~self ~value + +let set_allow_caching ~__context ~self ~value = + Db.VDI.set_allow_caching ~__context ~self ~value + + + diff -r e8da99c62c3e -r 243ef81d48f7 ocaml/xapi/xapi_vm_lifecycle.ml --- a/ocaml/xapi/xapi_vm_lifecycle.ml +++ b/ocaml/xapi/xapi_vm_lifecycle.ml @@ -16,6 +16,7 @@ *) open Xapi_pv_driver_version +open Listext module D = Debug.Debugger(struct let name="xapi" end) open D @@ -202,7 +203,7 @@ (** Take an internal VM record and a proposed operation, return true if the operation would be acceptable *) -let check_operation_error ~vmr ~vmgmr ~ref ~clone_suspended_vm_enabled ~op = +let check_operation_error ~vmr ~vmgmr ~ref ~clone_suspended_vm_enabled vdis_reset_and_caching ~op = let ref_str = Ref.string_of ref in let power_state = vmr.Db_actions.vM_power_state in let current_ops = vmr.Db_actions.vM_current_operations in @@ -245,6 +246,18 @@ && op <> `changing_dynamic_range then Some (Api_errors.operation_not_allowed, ["Operations on domain 0 are not allowed"]) + (* Check for an error due to VDI caching/reset behaviour *) + else if op = `checkpoint || op = `snapshot || op = `suspend || op = `snapshot_with_quiesce + then (* If any vdi exists with on_boot=reset, then disallow checkpoint, snapshot, suspend *) + if List.exists fst vdis_reset_and_caching + then Some (Api_errors.vdi_on_boot_mode_incompatable_with_operation,[]) + else None + else if op = `pool_migrate then + (* If any vdi exists with on_boot=reset and caching is enabled, disallow migrate *) + if List.exists (fun (reset,caching) -> reset && caching) vdis_reset_and_caching + then Some (Api_errors.vdi_on_boot_mode_incompatable_with_operation,[]) + else None + (* check PV drivers constraints if needed *) else if need_pv_drivers_check ~power_state ~op then check_drivers ~vmr ~vmgmr ~op ~ref @@ -271,24 +284,32 @@ let all = Db.VM.get_record_internal ~__context ~self in let gm = maybe_get_guest_metrics ~__context ~ref:(all.Db_actions.vM_guest_metrics) in let clone_suspended_vm_enabled = Helpers.clone_suspended_vm_enabled ~__context in - all, gm, clone_suspended_vm_enabled + let vdis_reset_and_caching = List.filter_map (fun vbd -> + try + let vdi = Db.VBD.get_VDI ~__context ~self:vbd in + let sm_config = Db.VDI.get_sm_config ~__context ~self:vdi in + Some + ((try List.assoc "on_boot" sm_config = "reset" with _ -> false), + (try String.lowercase (List.assoc "caching" sm_config) = "true" with _ -> false)) + with _ -> None) all.Db_actions.vM_VBDs in + all, gm, clone_suspended_vm_enabled, vdis_reset_and_caching let is_operation_valid ~__context ~self ~op = - let all, gm, clone_suspended_vm_enabled = get_info ~__context ~self in - match check_operation_error all gm self clone_suspended_vm_enabled op with + let all, gm, clone_suspended_vm_enabled, vdis_reset_and_caching = get_info ~__context ~self in + match check_operation_error all gm self clone_suspended_vm_enabled vdis_reset_and_caching op with | None -> true | Some _ -> false let assert_operation_valid ~__context ~self ~op = - let all, gm, clone_suspended_vm_enabled = get_info ~__context ~self in - match check_operation_error all gm self clone_suspended_vm_enabled op with + let all, gm, clone_suspended_vm_enabled, vdis_reset_and_caching = get_info ~__context ~self in + match check_operation_error all gm self clone_suspended_vm_enabled vdis_reset_and_caching op with | None -> () | Some (a,b) -> raise (Api_errors.Server_error (a,b)) let update_allowed_operations ~__context ~self = - let all, gm, clone_suspended_vm_enabled = get_info ~__context ~self in + let all, gm, clone_suspended_vm_enabled, vdis_reset_and_caching = get_info ~__context ~self in let check accu op = - match check_operation_error all gm self clone_suspended_vm_enabled op with + match check_operation_error all gm self clone_suspended_vm_enabled vdis_reset_and_caching op with | None -> op :: accu | _ -> accu in