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
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 =
~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 =
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 =
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"
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"
~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
host_set_cpu_features;
host_reset_cpu_features;
host_reset_networking;
+ host_enable_local_storage_caching;
+ host_disable_local_storage_caching;
]
~contents:
([ uid _host;
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";
])
()
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";
])
()
~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"
vdi_set_virtual_size;
vdi_set_physical_utilisation;
vdi_generate_config;
+ vdi_set_on_boot;
+ vdi_set_allow_caching;
]
~contents:
([ uid _vdi;
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";
+
])
()
~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
; 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
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=[];
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
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
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;
(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
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;
}
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 *)
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
})
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 =
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;
"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 ->
(** 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;
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, []));
(* 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
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();
~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 ()));
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 _ -> ()
+
+
+
(** 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
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)
"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 ()
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
~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 *)
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
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
)
~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 =
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 _ -> ());
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
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
~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;
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
+
+
+
*)
open Xapi_pv_driver_version
+open Listext
module D = Debug.Debugger(struct let name="xapi" end)
open D
(** 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
&& 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
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