diff --git a/ocaml/idl/datamodel_host.ml b/ocaml/idl/datamodel_host.ml index 2a272c7b5db..3d208bef45f 100644 --- a/ocaml/idl/datamodel_host.ml +++ b/ocaml/idl/datamodel_host.ml @@ -1235,6 +1235,28 @@ let host_query_ha = call ~flags:[`Session] ~allowed_roles:_R_VM_OP () + let set_iscsi_iqn = call + ~name:"set_iscsi_iqn" + ~lifecycle:[Published, rel_kolkata, ""] + ~doc:"Sets the initiator IQN for the host" + ~params:[ + Ref _host, "host", "The host"; + String, "value", "The value to which the IQN should be set" + ] + ~allowed_roles:_R_POOL_OP + () + + let set_multipathing = call + ~name:"set_multipathing" + ~lifecycle:[Published, rel_kolkata, ""] + ~doc:"Specifies whether multipathing is enabled" + ~params:[ + Ref _host, "host", "The host"; + Bool, "value", "Whether multipathing should be enabled" + ] + ~allowed_roles:_R_POOL_OP + () + (** Hosts *) let t = 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 @@ -1346,6 +1368,8 @@ let host_query_ha = call ~flags:[`Session] apply_guest_agent_config; mxgpu_vf_setup; allocate_resources_for_vm; + set_iscsi_iqn; + set_multipathing; ] ~contents: ([ uid _host; @@ -1400,6 +1424,8 @@ let host_query_ha = call ~flags:[`Session] field ~qualifier:DynamicRO ~in_product_since:rel_cream ~default_value:(Some (VSet [VInt 0L])) ~ty:(Set (Int)) "virtual_hardware_platform_versions" "The set of versions of the virtual hardware platform that the host can offer to its guests"; field ~qualifier:DynamicRO ~default_value:(Some (VRef null_ref)) ~in_product_since:rel_ely ~ty:(Ref _vm) "control_domain" "The control domain (domain 0)"; field ~qualifier:DynamicRO ~lifecycle:[Published, rel_ely, ""] ~ty:(Set (Ref _pool_update)) ~ignore_foreign_key:true "updates_requiring_reboot" "List of updates which require reboot"; - field ~qualifier:DynamicRO ~lifecycle:[Published, rel_falcon, ""] ~ty:(Set (Ref _feature)) "features" "List of features available on this host" + field ~qualifier:DynamicRO ~lifecycle:[Published, rel_falcon, ""] ~ty:(Set (Ref _feature)) "features" "List of features available on this host"; + field ~qualifier:StaticRO ~lifecycle:[Published, rel_kolkata, ""] ~default_value:(Some (VString "")) ~ty:String "iscsi_iqn" "The initiator IQN for the host"; + field ~qualifier:StaticRO ~lifecycle:[Published, rel_kolkata, ""] ~default_value:(Some (VBool false)) ~ty:Bool "multipathing" "Specifies whether multipathing is enabled"; ]) () diff --git a/ocaml/tests/suite.ml b/ocaml/tests/suite.ml index f087a1b42b5..4e5ef8ac159 100644 --- a/ocaml/tests/suite.ml +++ b/ocaml/tests/suite.ml @@ -62,6 +62,7 @@ let base_suite = Test_network_event_loop.test; Test_network.test; Test_pusb.test; + Test_host_helpers.test; ] let () = diff --git a/ocaml/tests/test_common.ml b/ocaml/tests/test_common.ml index 75d9a291d03..2a418e39e88 100644 --- a/ocaml/tests/test_common.ml +++ b/ocaml/tests/test_common.ml @@ -151,7 +151,9 @@ let make_host2 ~__context ?(ref=Ref.make ()) ?(uuid=make_uuid ()) ?(name_label=" ~display:`enabled ~virtual_hardware_platform_versions:[] ~control_domain:Ref.null - ~updates_requiring_reboot:[]; + ~updates_requiring_reboot:[] + ~iscsi_iqn:"" + ~multipathing:false; ref let make_pif ~__context ~network ~host ?(device="eth0") ?(mAC="C0:FF:EE:C0:FF:EE") ?(mTU=1500L) diff --git a/ocaml/tests/test_host_helpers.ml b/ocaml/tests/test_host_helpers.ml new file mode 100644 index 00000000000..49573690b51 --- /dev/null +++ b/ocaml/tests/test_host_helpers.ml @@ -0,0 +1,126 @@ +(* + * Copyright (C) Citrix Systems Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; version 2.1 only. with the special + * exception on linking described in file LICENSE. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + *) + +open OUnit + +let setup_test_oc_watcher () = + let __context, _ = Test_event_common.event_setup_common () in + let calls = ref [] in + let test_rpc call = + match call.Rpc.name, call.Rpc.params with + | "host.set_iscsi_iqn", [_session_id_rpc;host_rpc;value_rpc] -> + let host = API.ref_host_of_rpc host_rpc in + let v = API.string_of_rpc value_rpc in + Db.Host.set_iscsi_iqn ~__context ~self:host ~value:v; + calls := (host,`set_iscsi_iqn v) :: !calls; + Rpc.{success=true; contents=Rpc.String ""} + | "host.set_multipathing", [_session_id_rpc;host_rpc;value_rpc] -> + let host = API.ref_host_of_rpc host_rpc in + let v = API.bool_of_rpc value_rpc in + Db.Host.set_multipathing ~__context ~self:host ~value:v; + calls := (host,`set_multipathing v) :: !calls; + Rpc.{success=true; contents=Rpc.String ""} + | _ -> Mock_rpc.rpc __context call + in + Context.set_test_rpc __context test_rpc; + let host1 = !Xapi_globs.localhost_ref in + let host2 = Test_common.make_host ~__context () in + let watcher = Xapi_host_helpers.Configuration.watch_other_configs ~__context 0.0 in + let token = watcher "" in + (__context, calls, host1, host2, watcher, token) + +let test_host1 () = + (* Test1: update other_config:iscsi_iqn,multipathing on host1, check they appear in the calls list *) + let (__context, calls, host1, host2, watcher, token) = setup_test_oc_watcher () in + Db.Host.set_multipathing ~__context ~self:host1 ~value:false; + + Db.Host.add_to_other_config ~__context ~self:host1 ~key:"iscsi_iqn" ~value:"test1"; + let token = watcher token in + assert_equal !calls [host1, `set_iscsi_iqn "test1"]; + + calls := []; + Db.Host.add_to_other_config ~__context ~self:host1 ~key:"multipathing" ~value:"true"; + let _token = watcher token in + assert_equal !calls [host1, `set_multipathing true] + +let test_host2 () = + (* Test2: update other_config:iscsi_iqn,multipathing on host2, check they appear in the calls list *) + let (__context, calls, host1, host2, watcher, token) = setup_test_oc_watcher () in + Db.Host.set_multipathing ~__context ~self:host2 ~value:true; + + Db.Host.add_to_other_config ~__context ~self:host2 ~key:"iscsi_iqn" ~value:"test2"; + let token = watcher token in + assert_equal !calls [host2, `set_iscsi_iqn "test2"]; + + calls := []; + Db.Host.add_to_other_config ~__context ~self:host2 ~key:"multipathing" ~value:"false"; + let _token = watcher token in + assert_equal !calls [host2, `set_multipathing false] + +let test_different_keys () = + (* Test3: verify that setting other other-config keys causes no set *) + let (__context, calls, host1, host2, watcher, token) = setup_test_oc_watcher () in + Db.Host.add_to_other_config ~__context ~self:host1 ~key:"other_key" ~value:"test1"; + let _token = watcher token in + assert_equal !calls [] + +let test_host_set_iscsi_iqn () = + (* Test3: verify that sequence of DB calls in Host.set_iscsi_iqn don't cause the + watcher to invoke further calls *) + let (__context, calls, host1, host2, watcher, token) = setup_test_oc_watcher () in + Db.Host.add_to_other_config ~__context ~self:host1 ~key:"iscsi_iqn" ~value:"test1"; + let token = watcher token in + assert_equal !calls [host1, `set_iscsi_iqn "test1"]; + calls := []; + Db.Host.remove_from_other_config ~__context ~self:host1 ~key:"iscsi_iqn"; + let token = watcher token in + assert_equal !calls []; + Db.Host.set_iscsi_iqn ~__context ~self:host1 ~value:"test2"; + let token = watcher token in + assert_equal !calls []; + Db.Host.add_to_other_config ~__context ~self:host1 ~key:"iscsi_iqn" ~value:"test2"; + let _token = watcher token in + assert_equal !calls [] + +let test_host_set_multipathing () = + (* Test3: verify that sequence of DB calls in Host.set_multipathing don't cause the + watcher to invoke further calls *) + let (__context, calls, host1, host2, watcher, token) = setup_test_oc_watcher () in + Db.Host.set_multipathing ~__context ~self:host2 ~value:false; + + Db.Host.add_to_other_config ~__context ~self:host1 ~key:"multipathing" ~value:"true"; + let token = watcher token in + assert_equal !calls [host1, `set_multipathing true]; + calls := []; + + Db.Host.remove_from_other_config ~__context ~self:host1 ~key:"multipathing"; + let token = watcher token in + assert_equal !calls []; + Db.Host.set_multipathing ~__context ~self:host1 ~value:false; + let token = watcher token in + assert_equal !calls []; + Db.Host.add_to_other_config ~__context ~self:host1 ~key:"multipathing" ~value:"false"; + let _token = watcher token in + assert_equal !calls [] + + +let test = + "other_config_watcher" >::: + [ + "test_host1" >:: test_host1; + "test_host2" >:: test_host2; + "test_different_keys" >:: test_different_keys; + "test_host_set_iscsi_iqn" >:: test_host_set_iscsi_iqn; + "test_host_set_multipathing" >:: test_host_set_multipathing; + ] diff --git a/ocaml/tests/test_sm_features.ml b/ocaml/tests/test_sm_features.ml index 8c526b93b08..847673d6d92 100644 --- a/ocaml/tests/test_sm_features.ml +++ b/ocaml/tests/test_sm_features.ml @@ -157,6 +157,16 @@ let test_sequences = features = ["VDI_RESIZE", 1L]; }; }; + (* Test SMAPIV3 SR_MULTIPATH feature *) + { + raw = ["SR_MULTIPATH"]; + smapiv1_features = [Sr_multipath, 1L]; + smapiv2_features = ["SR_MULTIPATH/1"]; + sm = { + capabilities = ["SR_MULTIPATH"]; + features = ["SR_MULTIPATH", 1L]; + }; + } ] module ParseSMAPIv1Features = Generic.Make(struct diff --git a/ocaml/xapi/message_forwarding.ml b/ocaml/xapi/message_forwarding.ml index cb1128dad7d..ef9201a9aff 100644 --- a/ocaml/xapi/message_forwarding.ml +++ b/ocaml/xapi/message_forwarding.ml @@ -2654,6 +2654,20 @@ module Forward = functor(Local: Custom_actions.CUSTOM_ACTIONS) -> struct (host_uuid ~__context self) (vm_uuid ~__context vm); let snapshot = Db.VM.get_record ~__context ~self:vm in VM.allocate_vm_to_host ~__context ~vm ~host:self ~snapshot () + + let set_iscsi_iqn ~__context ~host ~value = + info "Host.set_iscsi_iqn: host='%s' iqn='%s'" (host_uuid ~__context host) value; + let local_fn = Local.Host.set_iscsi_iqn ~host ~value in + do_op_on ~local_fn ~__context ~host + (fun session_id rpc -> + Client.Host.set_iscsi_iqn rpc session_id host value) + + let set_multipathing ~__context ~host ~value = + info "Host.set_multipathing: host='%s' value='%s'" (host_uuid ~__context host) (string_of_bool value); + let local_fn = Local.Host.set_multipathing ~host ~value in + do_op_on ~local_fn ~__context ~host + (fun session_id rpc -> + Client.Host.set_multipathing rpc session_id host value) end module Host_crashdump = struct diff --git a/ocaml/xapi/records.ml b/ocaml/xapi/records.ml index 9c9bf0d9ad2..61c66f89b75 100644 --- a/ocaml/xapi/records.ml +++ b/ocaml/xapi/records.ml @@ -1249,6 +1249,10 @@ let host_record rpc session_id host = make_field ~name:"features" ~get:(fun () -> String.concat "; " (List.map get_uuid_from_ref (x ()).API.host_features)) ~get_set:(fun () -> List.map get_uuid_from_ref (x ()).API.host_features) (); + make_field ~name:"iscsi_iqn" + ~get:(fun () -> (x ()).API.host_iscsi_iqn) ~set:(fun s -> Client.Host.set_iscsi_iqn rpc session_id host s) (); + make_field ~name:"multipathing" + ~get:(fun () -> string_of_bool (x ()).API.host_multipathing) ~set:(fun s -> Client.Host.set_multipathing rpc session_id host (safe_bool_of_string "multipathing" s)) (); ]} let vdi_record rpc session_id vdi = diff --git a/ocaml/xapi/smint.ml b/ocaml/xapi/smint.ml index 3385ac1cac8..e930d39745e 100644 --- a/ocaml/xapi/smint.ml +++ b/ocaml/xapi/smint.ml @@ -30,6 +30,7 @@ type capability = | Sr_stats | Sr_metadata | Sr_trim + | Sr_multipath | Vdi_create | Vdi_delete | Vdi_attach | Vdi_detach | Vdi_mirror | Vdi_clone | Vdi_snapshot | Vdi_resize | Vdi_activate | Vdi_deactivate | Vdi_update | Vdi_introduce @@ -47,6 +48,7 @@ let string_to_capability_table = [ "SR_SUPPORTS_LOCAL_CACHING", Sr_supports_local_caching; "SR_METADATA", Sr_metadata; "SR_TRIM", Sr_trim; + "SR_MULTIPATH", Sr_multipath; "VDI_CREATE", Vdi_create; "VDI_DELETE", Vdi_delete; "VDI_ATTACH", Vdi_attach; diff --git a/ocaml/xapi/xapi.ml b/ocaml/xapi/xapi.ml index ba16dc7e51e..133af27c447 100644 --- a/ocaml/xapi/xapi.ml +++ b/ocaml/xapi/xapi.ml @@ -818,6 +818,8 @@ let server_init() = "hi-level database upgrade", [ Startup.OnlyMaster ], Xapi_db_upgrade.hi_level_db_upgrade_rules ~__context; "bringing up management interface", [], bring_up_management_if ~__context; "Starting periodic scheduler", [Startup.OnThread], Xapi_periodic_scheduler.loop; + "Synchronising host configuration files", [], (fun () -> Xapi_host_helpers.Configuration.sync_config_files ~__context); + "Starting Host other-config watcher", [Startup.OnlyMaster], (fun () -> Xapi_host_helpers.Configuration.start_watcher_thread ~__context); "Remote requests", [Startup.OnThread], Remote_requests.handle_requests; ]; begin match Pool_role.get_role () with diff --git a/ocaml/xapi/xapi_globs.ml b/ocaml/xapi/xapi_globs.ml index fc7015edc9a..180273bd79f 100644 --- a/ocaml/xapi/xapi_globs.ml +++ b/ocaml/xapi/xapi_globs.ml @@ -761,6 +761,9 @@ let udhcpd_skel = ref (Filename.concat "/etc/xensource" "udhcpd.skel") let udhcpd_leases_db = ref "/var/lib/xcp/dhcp-leases.db" let udhcpd_pidfile = ref "/var/run/udhcpd.pid" +let iscsi_initiator_config_file = ref "/etc/iscsi/initiatorname.iscsi" +let multipathing_config_file = ref "/var/run/nonpersistent/multipath_enabled" + let busybox = ref "busybox" let xe_path = ref "xe" @@ -1112,6 +1115,7 @@ module Resources = struct "logconfig", log_config_file, "Configure the logging policy"; "cpu-info-file", cpu_info_file, "Where to cache boot-time CPU info"; "server-cert-path", server_cert_path, "Path to server ssl certificate"; + "iscsi_initiatorname", iscsi_initiator_config_file, "Path to the initiatorname.iscsi file" ] let essential_dirs = [ "sm-dir", sm_dir, "Directory containing SM plugins"; diff --git a/ocaml/xapi/xapi_host.ml b/ocaml/xapi/xapi_host.ml index f684b915c67..2ae1d3ca42b 100644 --- a/ocaml/xapi/xapi_host.ml +++ b/ocaml/xapi/xapi_host.ml @@ -645,6 +645,8 @@ let create ~__context ~uuid ~name_label ~name_description ~hostname ~address ~ex ~virtual_hardware_platform_versions:(if host_is_us then Xapi_globs.host_virtual_hardware_platform_versions else [0L]) ~control_domain:Ref.null ~updates_requiring_reboot:[] + ~iscsi_iqn:"" + ~multipathing:false ; (* 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 ())); @@ -1766,3 +1768,31 @@ let mxgpu_vf_setup ~__context ~host = let allocate_resources_for_vm ~__context ~self ~vm ~live = (* Implemented entirely in Message_forwarding *) () + +let set_iscsi_iqn ~__context ~host ~value = + if value = "" then raise Api_errors.(Server_error (invalid_value, ["value"; value])); + (* Note, the following sequence is carefully written - see the + other-config watcher thread in xapi_host_helpers.ml *) + Db.Host.remove_from_other_config ~__context ~self:host ~key:"iscsi_iqn"; + (* we need to first set the iscsi_iqn field and then the other-config field: + * setting the other-config field triggers and update on the iscsi_iqn + * field if they are different + * + * we want to keep the legacy `other_config:iscsi_iqn` and the new `iscsi_iqn` + * fields in sync: + * when you update the `iscsi_iqn` field we want to update `other_config`, + * but when updating `other_config` we want to update `iscsi_iqn` too. + * we have to be careful not to introduce an infinite loop of updates. + * *) + Db.Host.set_iscsi_iqn ~__context ~self:host ~value; + Db.Host.add_to_other_config ~__context ~self:host ~key:"iscsi_iqn" ~value; + Xapi_host_helpers.Configuration.set_initiator_name value + +let set_multipathing ~__context ~host ~value = + (* Note, the following sequence is carefully written - see the + other-config watcher thread in xapi_host_helpers.ml *) + Db.Host.remove_from_other_config ~__context ~self:host ~key:"multipathing"; + Db.Host.set_multipathing ~__context ~self:host ~value; + Db.Host.add_to_other_config ~__context ~self:host ~key:"multipathing" ~value:(string_of_bool value); + Xapi_host_helpers.Configuration.set_multipathing value + diff --git a/ocaml/xapi/xapi_host.mli b/ocaml/xapi/xapi_host.mli index 3bd3c54bbcc..309ebca18cf 100644 --- a/ocaml/xapi/xapi_host.mli +++ b/ocaml/xapi/xapi_host.mli @@ -310,3 +310,5 @@ val apply_guest_agent_config : __context:Context.t -> host:API.ref_host -> unit val mxgpu_vf_setup : __context:Context.t -> host:API.ref_host -> unit val allocate_resources_for_vm : __context:Context.t -> self:API.ref_host -> vm:API.ref_VM -> live:bool -> unit +val set_iscsi_iqn : __context:Context.t -> host:API.ref_host -> value:string -> unit +val set_multipathing : __context:Context.t -> host:API.ref_host -> value:bool -> unit diff --git a/ocaml/xapi/xapi_host_helpers.ml b/ocaml/xapi/xapi_host_helpers.ml index 203f1653718..362cf1c7161 100644 --- a/ocaml/xapi/xapi_host_helpers.ml +++ b/ocaml/xapi/xapi_host_helpers.ml @@ -266,3 +266,86 @@ module Host_requires_reboot = struct Unixext.touch_file Xapi_globs.requires_reboot_file ) end + +module Configuration = struct + + let make_initiatorname_config iqn hostname = + (* CA-18000: there is a 30 character limit to the initiator when talking to + Dell MD3000i filers, so we limit the size of the initiator name in all cases *) + let hostname_chopped = + if String.length hostname > 30 + then String.sub hostname 0 30 + else hostname + in + Printf.sprintf + "InitiatorName=%s\nInitiatorAlias=%s\n" + iqn hostname_chopped + + let set_initiator_name iqn = + let hostname = Unix.gethostname () in + let config_file = make_initiatorname_config iqn hostname in + Unixext.write_string_to_file !Xapi_globs.iscsi_initiator_config_file config_file + + let set_multipathing enabled = + let flag = !Xapi_globs.multipathing_config_file in + if enabled then Unixext.touch_file flag + else begin + Unixext.unlink_safe flag + end + + let sync_config_files ~__context = + (* If the host fields are not in sync with the values in other_config, + the other_config watcher thread will make sure that these functions will + be called again with the up to date values. *) + let self = Helpers.get_localhost ~__context in + set_initiator_name (Db.Host.get_iscsi_iqn ~__context ~self); + set_multipathing (Db.Host.get_multipathing ~__context ~self) + + let watch_other_configs ~__context delay = + let loop token = + Helpers.call_api_functions ~__context (fun rpc session_id -> + let events = + Client.Client.Event.from rpc session_id ["host"] token delay |> + Event_types.event_from_of_rpc + in + List.iter (fun ev -> + match Event_helper.record_of_event ev with + | Event_helper.Host (host_ref, Some host_rec) -> begin + let oc = host_rec.API.host_other_config in + let iscsi_iqn = try Some (List.assoc "iscsi_iqn" oc) with _ -> None in + begin match iscsi_iqn with + | None -> () + | Some "" -> () + | Some iqn when iqn <> host_rec.API.host_iscsi_iqn -> + Client.Client.Host.set_iscsi_iqn rpc session_id host_ref iqn + | _ -> () + end; + (* Accepted values are "true" and "false" *) + (* If someone deletes the multipathing other_config key, we don't do anything *) + let multipathing = try Some (List.assoc "multipathing" oc |> Pervasives.bool_of_string) with _ -> None in + begin match multipathing with + | None -> () + | Some multipathing when multipathing <> host_rec.API.host_multipathing -> + Client.Client.Host.set_multipathing rpc session_id host_ref multipathing + | _ -> () + end + end + | _ -> ()) + events.Event_types.events; + events.Event_types.token) + in + loop + + let start_watcher_thread ~__context = + Thread.create (fun () -> + let loop = watch_other_configs ~__context 30.0 in + while true do + begin + try + let rec inner token = inner (loop token) in inner "" + with e -> + error "Caught exception in Configuration.start_watcher_thread: %s" (Printexc.to_string e); + Thread.delay 5.0; + end; + done) () |> ignore +end diff --git a/ocaml/xapi/xapi_host_helpers.mli b/ocaml/xapi/xapi_host_helpers.mli new file mode 100644 index 00000000000..98aa6fa1d07 --- /dev/null +++ b/ocaml/xapi/xapi_host_helpers.mli @@ -0,0 +1,134 @@ +(* + * Copyright (C) Citrix Systems Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; version 2.1 only. with the special + * exception on linking described in file LICENSE. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + *) + +(** Common code for dealing with Hosts. + * @group Host Management +*) + +val assert_operation_valid : + __context:Context.t -> + self:API.ref_host -> + op:API.host_allowed_operations -> + unit +(** [assert_operation_valid ~__context ~self ~op] checks the operation [op] is + currently valid on [host]. There are various checks that are performed, + for example: + {ul + {- Ony one 'provisioning-type' operation is allowed at once} + {- Reboot / Shutdown cannot run concurrently} + {- Shutdown and Reboot are only allowed if the host is disabled} + }*) + +val update_allowed_operations : + __context:Context.t -> + self:API.ref_host -> + unit +(** [update_allowed_operations ~__context ~self] updates the + allowed_operations database field with all the operations that are + currently allowed given the current state of the host. *) + +val update_allowed_operations_all_hosts : + __context:Context.t -> + unit +(** [update_allowd_operations_all_hosts ~__context] runs + [update_alloed_operations] for each host *) + +val cancel_tasks : + __context:Context.t -> + self:API.ref_host -> + all_tasks_in_db:API.ref_task list -> + task_ids:string list -> unit +(** [cancel_tasks ~__context ~self ~all_tasks_in_db ~task_ids] is a helper + utility for batch cancelling tasks associated with a previously dead host. + See the file `cancel_tasks.ml` and the function [Helpers.cancel_tasks] for + more context. *) + +val mark_host_as_dead : + __context:Context.t -> + host:API.ref_host -> + reason:string -> + unit +(** [mark_host_as_dead ~__context ~host ~reason] is called on the master when a + host is declaring it's going to be dead soon, via the [tickle_heartbeat] + code. The host will be added to the Xapi_globs table of + [hosts_which_are_shutting_down], the host_metrics live field will be set to + false and any pre and post declare_dead scripts will be executed. *) + +val consider_enabling_host : __context:Context.t -> unit +(** [consider_enabling_host ~__context] is called at the end of the xapi + startup sequence. It will enable the host unless: + {ul + {- the user asked the host to be disabled and there was a problem} + {- HA is enabled and one-or-more PBDs failed to plug} + {- `disabled_until_next_reboot` is set in the local DB}} +*) + +val consider_enabling_host_request : __context:Context.t -> unit +(** [consider_enabling_host_request ~__context] will ensure that + [consider_enabling_host] is called soon. It will coalesce multiple requests + that are made. *) + +val user_requested_host_disable : bool ref +(** [user_requested_host_disable] : true if so. Persistent until xapi is + restarted *) + +val assert_startup_complete : unit -> unit +(** [assert_startup_complete ()] will raise `host_still_booting` if the startup + sequence is not yet complete. *) + +module Host_requires_reboot : sig + val set : unit -> unit + (** [set ()] is used to signal the host needs a reboot. This could be, for + example, that the dom0 memory settings have changed and a reboot is + required for them to take effect. *) + + val get : unit -> bool + (** [get ()] returns [true] if the host needs to be rebooted *) +end + +module Configuration : sig + val set_initiator_name : string -> unit + (** [set_initiator_name iqn] will write the iscsi initiator configuration to + the file specified in Xapi_globs (usually /etc/iscsi/initiatorname.iscsi) + *) + + val set_multipathing : bool -> unit + (** [set_multipathing enabled] will touch the file specified in Xapi_globs + (usually /var/run/nonpersistent/multipath_enabled) if [enabled] is true, + otherwise it will remove the file. *) + + val sync_config_files : __context:Context.t -> unit + (** [sync_config_files ~__context] ensures that the iscsi iqn and + multipathing configuration files reflect the values of the corresponding + fields in xapi's database. It should be called at startup on every host + BEFORE the other_config watcher [start_watcher_thread] is started *) + + val watch_other_configs : __context:Context.t -> float -> string -> string + (** [watch_other_configs ~__context timeout] returns a function that performs + one iteration of watching Host.other_config. If an update occurs this + will check whether the iscsi_iqn field in other-config is correctly + reflected in the field Host.iscsi_iqn, and if not it will call + Host.set_iscsi_iqn with the value specified in other-config. This is + intended to be run on the master. The returned function has type + [token -> token], where [token] is a string. The initial value should be + the empty string, and the returned value should be used for further + invocations. This function is exposed only for unit testing, and should + not be invoked directly.*) + + val start_watcher_thread : __context:Context.t -> unit + (** [start_watcher_thread ~__context] will start a thread that watches the + other-config field of all hosts and keeps the iscsi_iqn value in sync + with the first-class field Host.iscsi_iqn. As with watch_other_configs, + this function must only be run on the master. *) +end diff --git a/scripts/xe-set-iscsi-iqn b/scripts/xe-set-iscsi-iqn index c0a2c11a128..5c7da9cbb9a 100755 --- a/scripts/xe-set-iscsi-iqn +++ b/scripts/xe-set-iscsi-iqn @@ -8,7 +8,6 @@ set -e XE="@BINDIR@/xe" -configmap="other-config" configkey="iscsi_iqn" if [ ! -f @INVENTORY@ ]; then @@ -30,4 +29,4 @@ fi . @INVENTORY@ -${XE} host-param-set uuid=${INSTALLATION_UUID} ${configmap}:${configkey}=${iqn} +${XE} host-param-set uuid=${INSTALLATION_UUID} ${configkey}=${iqn}