Skip to content

Commit ccaf4d6

Browse files
authored
CP-53721 Enable XAPI to configure SSH auto mode (#6442)
This PR introduces support for Dom0 SSH control, providing the capability to configure the auto mode for a specific host or all hosts in the pool. New Host Object Fields and Host/Pool API: - `ssh_auto_mode`: Indicates whether SSH auto mode is enabled. configure to true means startup SSH when the XAPI service is down; stop SSH when the XAPI service is up. configure to false means SSH service status is independent and not effected by the XAPI service status. - `set_ssh_auto_mode`: Allows setting auto mode for specific host or all hosts in the pool. (Note: Default value for XS8/XS9, pool join/eject operations, and XAPI startup scenarios will be addressed in the next PR, this PR only include the change of data_mode/API/cli )
2 parents 779f8f7 + 4db3c2f commit ccaf4d6

18 files changed

+161
-15
lines changed

ocaml/idl/datamodel_common.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ open Datamodel_roles
1010
to leave a gap for potential hotfixes needing to increment the schema version.*)
1111
let schema_major_vsn = 5
1212

13-
let schema_minor_vsn = 788
13+
let schema_minor_vsn = 789
1414

1515
(* Historical schema versions just in case this is useful later *)
1616
let rio_schema_major_vsn = 5

ocaml/idl/datamodel_errors.ml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2046,6 +2046,9 @@ let _ =
20462046
error Api_errors.set_console_timeout_partially_failed ["hosts"]
20472047
~doc:"Some hosts failed to set console timeout." () ;
20482048

2049+
error Api_errors.set_ssh_auto_mode_partially_failed ["hosts"]
2050+
~doc:"Some hosts failed to set SSH auto mode." () ;
2051+
20492052
error Api_errors.host_driver_no_hardware ["driver variant"]
20502053
~doc:"No hardware present for this host driver variant" () ;
20512054

ocaml/idl/datamodel_host.ml

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1335,6 +1335,13 @@ let create_params =
13351335
; param_release= numbered_release "25.14.0-next"
13361336
; param_default= Some (VInt Constants.default_console_idle_timeout)
13371337
}
1338+
; {
1339+
param_type= Bool
1340+
; param_name= "ssh_auto_mode"
1341+
; param_doc= "True if SSH auto mode is enabled for the host"
1342+
; param_release= numbered_release "25.14.0-next"
1343+
; param_default= Some (VBool Constants.default_ssh_auto_mode)
1344+
}
13381345
]
13391346

13401347
let create =
@@ -1350,8 +1357,8 @@ let create =
13501357
; ( Changed
13511358
, "25.14.0-next"
13521359
, "Added --ssh_enabled --ssh_enabled_timeout --ssh_expiry \
1353-
--console_idle_timeout options to allow them to be configured for \
1354-
new host"
1360+
--console_idle_timeout --ssh_auto_mode options to allow them to be \
1361+
configured for new host"
13551362
)
13561363
]
13571364
~versioned_params:create_params ~doc:"Create a new host record"
@@ -2440,6 +2447,21 @@ let set_console_idle_timeout =
24402447
]
24412448
~allowed_roles:_R_POOL_ADMIN ()
24422449

2450+
let set_ssh_auto_mode =
2451+
call ~name:"set_ssh_auto_mode" ~lifecycle:[]
2452+
~doc:"Set the SSH auto mode for the host"
2453+
~params:
2454+
[
2455+
(Ref _host, "self", "The host")
2456+
; ( Bool
2457+
, "value"
2458+
, "The SSH auto mode for the host,when set to true, SSH to normally be \
2459+
disabled and SSH to be enabled only in case of emergency e.g., xapi \
2460+
is down"
2461+
)
2462+
]
2463+
~allowed_roles:_R_POOL_ADMIN ()
2464+
24432465
let latest_synced_updates_applied_state =
24442466
Enum
24452467
( "latest_synced_updates_applied_state"
@@ -2601,6 +2623,7 @@ let t =
26012623
; disable_ssh
26022624
; set_ssh_enabled_timeout
26032625
; set_console_idle_timeout
2626+
; set_ssh_auto_mode
26042627
]
26052628
~contents:
26062629
([
@@ -3056,6 +3079,10 @@ let t =
30563079
"console_idle_timeout"
30573080
"The timeout in seconds after which idle console will be \
30583081
automatically terminated (0 means never)"
3082+
; field ~qualifier:DynamicRO ~lifecycle:[] ~ty:Bool
3083+
~default_value:(Some (VBool Constants.default_ssh_auto_mode))
3084+
"ssh_auto_mode"
3085+
"Reflects whether SSH auto mode is enabled for the host"
30593086
]
30603087
)
30613088
()

ocaml/idl/datamodel_pool.ml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1606,6 +1606,21 @@ let set_console_idle_timeout =
16061606
]
16071607
~allowed_roles:_R_POOL_ADMIN ()
16081608

1609+
let set_ssh_auto_mode =
1610+
call ~name:"set_ssh_auto_mode" ~lifecycle:[]
1611+
~doc:"Set the SSH auto mode for all hosts in the pool"
1612+
~params:
1613+
[
1614+
(Ref _pool, "self", "The pool")
1615+
; ( Bool
1616+
, "value"
1617+
, "The SSH auto mode for all hosts in the pool,when set to true, SSH \
1618+
to normally be disabled and SSH to be enabled only in case of \
1619+
emergency e.g., xapi is down"
1620+
)
1621+
]
1622+
~allowed_roles:_R_POOL_ADMIN ()
1623+
16091624
(** A pool class *)
16101625
let t =
16111626
create_obj ~in_db:true
@@ -1704,6 +1719,7 @@ let t =
17041719
; disable_ssh
17051720
; set_ssh_enabled_timeout
17061721
; set_console_idle_timeout
1722+
; set_ssh_auto_mode
17071723
]
17081724
~contents:
17091725
([

ocaml/idl/schematest.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ let hash x = Digest.string x |> Digest.to_hex
33
(* BEWARE: if this changes, check that schema has been bumped accordingly in
44
ocaml/idl/datamodel_common.ml, usually schema_minor_vsn *)
55

6-
let last_known_schema_hash = "8bf2b9ab509301baf138820cf34608d3"
6+
let last_known_schema_hash = "7c52d11789dea3ab3167c5d0e3e7fa89"
77

88
let current_schema_hash : string =
99
let open Datamodel_types in

ocaml/tests/common/test_common.ml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -172,14 +172,14 @@ let make_host ~__context ?(uuid = make_uuid ()) ?(name_label = "host")
172172
?(local_cache_sr = Ref.null) ?(chipset_info = []) ?(ssl_legacy = false)
173173
?(last_software_update = Date.epoch) ?(last_update_hash = "")
174174
?(ssh_enabled = true) ?(ssh_enabled_timeout = 0L) ?(ssh_expiry = Date.epoch)
175-
?(console_idle_timeout = 0L) () =
175+
?(console_idle_timeout = 0L) ?(ssh_auto_mode = false) () =
176176
let host =
177177
Xapi_host.create ~__context ~uuid ~name_label ~name_description ~hostname
178178
~address ~external_auth_type ~external_auth_service_name
179179
~external_auth_configuration ~license_params ~edition ~license_server
180180
~local_cache_sr ~chipset_info ~ssl_legacy ~last_software_update
181181
~last_update_hash ~ssh_enabled ~ssh_enabled_timeout ~ssh_expiry
182-
~console_idle_timeout
182+
~console_idle_timeout ~ssh_auto_mode
183183
in
184184
Db.Host.set_cpu_info ~__context ~self:host ~value:default_cpu_info ;
185185
host
@@ -219,7 +219,7 @@ let make_host2 ~__context ?(ref = Ref.make ()) ?(uuid = make_uuid ())
219219
~recommended_guidances:[] ~latest_synced_updates_applied:`unknown
220220
~pending_guidances_recommended:[] ~pending_guidances_full:[]
221221
~last_update_hash:"" ~ssh_enabled:true ~ssh_enabled_timeout:0L
222-
~ssh_expiry:Date.epoch ~console_idle_timeout:0L ;
222+
~ssh_expiry:Date.epoch ~console_idle_timeout:0L ~ssh_auto_mode:false ;
223223
ref
224224

225225
let make_pif ~__context ~network ~host ?(device = "eth0")

ocaml/tests/test_host.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ let add_host __context name =
2525
~local_cache_sr:Ref.null ~chipset_info:[] ~ssl_legacy:false
2626
~last_software_update:Clock.Date.epoch ~last_update_hash:""
2727
~ssh_enabled:true ~ssh_enabled_timeout:0L ~ssh_expiry:Clock.Date.epoch
28-
~console_idle_timeout:0L
28+
~console_idle_timeout:0L ~ssh_auto_mode:false
2929
)
3030

3131
(* Creates an unlicensed pool with the maximum number of hosts *)

ocaml/xapi-cli-server/records.ml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1584,6 +1584,17 @@ let pool_record rpc session_id pool =
15841584
~value:(safe_i64_of_string "console-idle-timeout" value)
15851585
)
15861586
()
1587+
; make_field ~name:"ssh-auto-mode"
1588+
~get:(fun () ->
1589+
get_consistent_field_or_default ~rpc ~session_id
1590+
~getter:Client.Host.get_ssh_auto_mode ~transform:string_of_bool
1591+
~default:inconsistent
1592+
)
1593+
~set:(fun value ->
1594+
Client.Pool.set_ssh_auto_mode ~rpc ~session_id ~self:pool
1595+
~value:(safe_bool_of_string "ssh-auto-mode" value)
1596+
)
1597+
()
15871598
]
15881599
}
15891600

@@ -3375,6 +3386,13 @@ let host_record rpc session_id host =
33753386
~value:(safe_i64_of_string "console-idle-timeout" value)
33763387
)
33773388
()
3389+
; make_field ~name:"ssh-auto-mode"
3390+
~get:(fun () -> string_of_bool (x ()).API.host_ssh_auto_mode)
3391+
~set:(fun value ->
3392+
Client.Host.set_ssh_auto_mode ~rpc ~session_id ~self:host
3393+
~value:(safe_bool_of_string "ssh-auto-mode" value)
3394+
)
3395+
()
33783396
]
33793397
}
33803398

ocaml/xapi-consts/api_errors.ml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1426,6 +1426,9 @@ let set_ssh_timeout_partially_failed =
14261426
let set_console_timeout_partially_failed =
14271427
add_error "SET_CONSOLE_TIMEOUT_PARTIALLY_FAILED"
14281428

1429+
let set_ssh_auto_mode_partially_failed =
1430+
add_error "SET_SSH_AUTO_MODE_PARTIALLY_FAILED"
1431+
14291432
let host_driver_no_hardware = add_error "HOST_DRIVER_NO_HARDWARE"
14301433

14311434
let tls_verification_not_enabled_in_pool =

ocaml/xapi-consts/constants.ml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,3 +428,5 @@ let default_ssh_enabled = true
428428
let default_ssh_enabled_timeout = 0L
429429

430430
let default_console_idle_timeout = 0L
431+
432+
let default_ssh_auto_mode = false

ocaml/xapi/dbsync_slave.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ let create_localhost ~__context info =
6464
~ssh_enabled_timeout:Constants.default_ssh_enabled_timeout
6565
~ssh_expiry:Date.epoch
6666
~console_idle_timeout:Constants.default_console_idle_timeout
67+
~ssh_auto_mode:Constants.default_ssh_auto_mode
6768
in
6869
()
6970

ocaml/xapi/message_forwarding.ml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1197,6 +1197,12 @@ functor
11971197
(pool_uuid ~__context self)
11981198
value ;
11991199
Local.Pool.set_console_idle_timeout ~__context ~self ~value
1200+
1201+
let set_ssh_auto_mode ~__context ~self ~value =
1202+
info "Pool.set_ssh_auto_mode: pool='%s' value='%b'"
1203+
(pool_uuid ~__context self)
1204+
value ;
1205+
Local.Pool.set_ssh_auto_mode ~__context ~self ~value
12001206
end
12011207

12021208
module VM = struct
@@ -4063,6 +4069,14 @@ functor
40634069
let local_fn = Local.Host.set_console_idle_timeout ~self ~value in
40644070
let remote_fn = Client.Host.set_console_idle_timeout ~self ~value in
40654071
do_op_on ~local_fn ~__context ~host:self ~remote_fn
4072+
4073+
let set_ssh_auto_mode ~__context ~self ~value =
4074+
info "Host.set_ssh_auto_mode: host='%s' value='%b'"
4075+
(host_uuid ~__context self)
4076+
value ;
4077+
let local_fn = Local.Host.set_ssh_auto_mode ~self ~value in
4078+
let remote_fn = Client.Host.set_ssh_auto_mode ~self ~value in
4079+
do_op_on ~local_fn ~__context ~host:self ~remote_fn
40664080
end
40674081

40684082
module Host_crashdump = struct

ocaml/xapi/xapi_globs.ml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1297,6 +1297,8 @@ let job_for_disable_ssh = ref "Disable SSH"
12971297

12981298
let ssh_service = ref "sshd"
12991299

1300+
let ssh_monitor_service = ref "xapi-ssh-monitor"
1301+
13001302
(* Fingerprint of default patch key *)
13011303
let citrix_patch_key =
13021304
"NERDNTUzMDMwRUMwNDFFNDI4N0M4OEVCRUFEMzlGOTJEOEE5REUyNg=="

ocaml/xapi/xapi_host.ml

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -979,7 +979,7 @@ let create ~__context ~uuid ~name_label ~name_description:_ ~hostname ~address
979979
~external_auth_type ~external_auth_service_name ~external_auth_configuration
980980
~license_params ~edition ~license_server ~local_cache_sr ~chipset_info
981981
~ssl_legacy:_ ~last_software_update ~last_update_hash ~ssh_enabled
982-
~ssh_enabled_timeout ~ssh_expiry ~console_idle_timeout =
982+
~ssh_enabled_timeout ~ssh_expiry ~console_idle_timeout ~ssh_auto_mode =
983983
(* fail-safe. We already test this on the joining host, but it's racy, so multiple concurrent
984984
pool-join might succeed. Note: we do it in this order to avoid a problem checking restrictions during
985985
the initial setup of the database *)
@@ -1044,7 +1044,7 @@ let create ~__context ~uuid ~name_label ~name_description:_ ~hostname ~address
10441044
~tls_verification_enabled ~last_software_update ~last_update_hash
10451045
~recommended_guidances:[] ~latest_synced_updates_applied:`unknown
10461046
~pending_guidances_recommended:[] ~pending_guidances_full:[] ~ssh_enabled
1047-
~ssh_enabled_timeout ~ssh_expiry ~console_idle_timeout ;
1047+
~ssh_enabled_timeout ~ssh_expiry ~console_idle_timeout ~ssh_auto_mode ;
10481048
(* If the host we're creating is us, make sure its set to live *)
10491049
Db.Host_metrics.set_last_updated ~__context ~self:metrics ~value:(Date.now ()) ;
10501050
Db.Host_metrics.set_live ~__context ~self:metrics ~value:host_is_us ;
@@ -3112,18 +3112,47 @@ let emergency_clear_mandatory_guidance ~__context =
31123112
) ;
31133113
Db.Host.set_pending_guidances ~__context ~self ~value:[]
31143114

3115+
let set_ssh_auto_mode ~__context ~self ~value =
3116+
debug "Setting SSH auto mode for host %s to %B"
3117+
(Helpers.get_localhost_uuid ())
3118+
value ;
3119+
3120+
Db.Host.set_ssh_auto_mode ~__context ~self ~value ;
3121+
3122+
try
3123+
(* When enabled, the ssh_monitor_service regularly checks XAPI status to manage SSH availability.
3124+
During normal operation when XAPI is running properly, SSH is automatically disabled.
3125+
SSH is only enabled during emergency scenarios
3126+
(e.g., when XAPI is down) to allow administrative access for troubleshooting. *)
3127+
if value then (
3128+
Xapi_systemctl.enable ~wait_until_success:false
3129+
!Xapi_globs.ssh_monitor_service ;
3130+
Xapi_systemctl.start ~wait_until_success:false
3131+
!Xapi_globs.ssh_monitor_service
3132+
) else (
3133+
Xapi_systemctl.stop ~wait_until_success:false
3134+
!Xapi_globs.ssh_monitor_service ;
3135+
Xapi_systemctl.disable ~wait_until_success:false
3136+
!Xapi_globs.ssh_monitor_service
3137+
)
3138+
with e ->
3139+
error "Failed to configure SSH auto mode: %s" (Printexc.to_string e) ;
3140+
Helpers.internal_error "Failed to configure SSH auto mode: %s"
3141+
(Printexc.to_string e)
3142+
31153143
let disable_ssh_internal ~__context ~self =
31163144
try
31173145
debug "Disabling SSH for host %s" (Helpers.get_localhost_uuid ()) ;
3118-
Xapi_systemctl.disable ~wait_until_success:false !Xapi_globs.ssh_service ;
3146+
if not (Db.Host.get_ssh_auto_mode ~__context ~self) then
3147+
Xapi_systemctl.disable ~wait_until_success:false !Xapi_globs.ssh_service ;
31193148
Xapi_systemctl.stop ~wait_until_success:false !Xapi_globs.ssh_service ;
31203149
Db.Host.set_ssh_enabled ~__context ~self ~value:false
31213150
with e ->
31223151
error "Failed to disable SSH for host %s: %s" (Ref.string_of self)
31233152
(Printexc.to_string e) ;
31243153
Helpers.internal_error "Failed to disable SSH: %s" (Printexc.to_string e)
31253154

3126-
let schedule_disable_ssh_job ~__context ~self ~timeout =
3155+
let schedule_disable_ssh_job ~__context ~self ~timeout ~auto_mode =
31273156
let host_uuid = Helpers.get_localhost_uuid () in
31283157
let expiry_time =
31293158
match
@@ -3152,7 +3181,11 @@ let schedule_disable_ssh_job ~__context ~self ~timeout =
31523181
Xapi_stdext_threads_scheduler.Scheduler.add_to_queue
31533182
!Xapi_globs.job_for_disable_ssh
31543183
Xapi_stdext_threads_scheduler.Scheduler.OneShot (Int64.to_float timeout)
3155-
(fun () -> disable_ssh_internal ~__context ~self
3184+
(fun () ->
3185+
disable_ssh_internal ~__context ~self ;
3186+
(* re-enable SSH auto mode if it was enabled before calling host.enable_ssh *)
3187+
if auto_mode then
3188+
set_ssh_auto_mode ~__context ~self ~value:true
31563189
) ;
31573190

31583191
Db.Host.set_ssh_expiry ~__context ~self ~value:expiry_time
@@ -3161,6 +3194,10 @@ let enable_ssh ~__context ~self =
31613194
try
31623195
debug "Enabling SSH for host %s" (Helpers.get_localhost_uuid ()) ;
31633196

3197+
let cached_ssh_auto_mode = Db.Host.get_ssh_auto_mode ~__context ~self in
3198+
(* Disable SSH auto mode when SSH is enabled manually *)
3199+
set_ssh_auto_mode ~__context ~self ~value:false ;
3200+
31643201
Xapi_systemctl.enable ~wait_until_success:false !Xapi_globs.ssh_service ;
31653202
Xapi_systemctl.start ~wait_until_success:false !Xapi_globs.ssh_service ;
31663203

@@ -3171,6 +3208,7 @@ let enable_ssh ~__context ~self =
31713208
!Xapi_globs.job_for_disable_ssh
31723209
| t ->
31733210
schedule_disable_ssh_job ~__context ~self ~timeout:t
3211+
~auto_mode:cached_ssh_auto_mode
31743212
) ;
31753213

31763214
Db.Host.set_ssh_enabled ~__context ~self ~value:true
@@ -3208,7 +3246,7 @@ let set_ssh_enabled_timeout ~__context ~self ~value =
32083246
!Xapi_globs.job_for_disable_ssh ;
32093247
Db.Host.set_ssh_expiry ~__context ~self ~value:Date.epoch
32103248
| t ->
3211-
schedule_disable_ssh_job ~__context ~self ~timeout:t
3249+
schedule_disable_ssh_job ~__context ~self ~timeout:t ~auto_mode:false
32123250

32133251
let set_console_idle_timeout ~__context ~self ~value =
32143252
let assert_timeout_valid timeout =

ocaml/xapi/xapi_host.mli

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ val create :
134134
-> ssh_enabled_timeout:int64
135135
-> ssh_expiry:API.datetime
136136
-> console_idle_timeout:int64
137+
-> ssh_auto_mode:bool
137138
-> [`host] Ref.t
138139

139140
val destroy : __context:Context.t -> self:API.ref_host -> unit
@@ -579,4 +580,11 @@ val set_console_idle_timeout :
579580
__context:Context.t -> self:API.ref_host -> value:int64 -> unit
580581

581582
val schedule_disable_ssh_job :
582-
__context:Context.t -> self:API.ref_host -> timeout:int64 -> unit
583+
__context:Context.t
584+
-> self:API.ref_host
585+
-> timeout:int64
586+
-> auto_mode:bool
587+
-> unit
588+
589+
val set_ssh_auto_mode :
590+
__context:Context.t -> self:API.ref_host -> value:bool -> unit

ocaml/xapi/xapi_periodic_scheduler_init.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ let register ~__context =
9090
if Int64.compare expiry_time current_time > 0 then
9191
let remaining = Int64.sub expiry_time current_time in
9292
Xapi_host.schedule_disable_ssh_job ~__context ~self ~timeout:remaining
93+
~auto_mode:true
9394
(* handle the case where XAPI is not active when the SSH timeout expires *)
9495
else if Fe_systemctl.is_active ~service:!Xapi_globs.ssh_service then
9596
Xapi_host.disable_ssh ~__context ~self

0 commit comments

Comments
 (0)