Skip to content

Commit 18e93ff

Browse files
authored
Merge feature/configure-ssh to master (#6361)
1. Merge `feature/configure-ssh` to `master`. 2. Update `datamodel_lifecycle.ml` to `25.12.0-next`.
2 parents 17514dc + 18ba7e7 commit 18e93ff

17 files changed

+277
-6
lines changed

ocaml/forkexecd/lib/fe_systemctl.ml

+6-2
Original file line numberDiff line numberDiff line change
@@ -121,15 +121,19 @@ let stop ~service =
121121
Xapi_stdext_unix.Unixext.unlink_safe destination ;
122122
status
123123

124-
let is_active ~service =
124+
let status ~command ~service =
125125
let status =
126126
Forkhelpers.safe_close_and_exec None None None [] systemctl
127-
["is-active"; "--quiet"; service]
127+
[command; "--quiet"; service]
128128
|> Forkhelpers.waitpid
129129
|> snd
130130
in
131131
Unix.WEXITED 0 = status
132132

133+
let is_active ~service = status ~command:"is-active" ~service
134+
135+
let is_enabled ~service = status ~command:"is-enabled" ~service
136+
133137
(** path to service file *)
134138
let path service = Filename.concat run_path (service ^ ".service")
135139

ocaml/forkexecd/lib/fe_systemctl.mli

+3
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ val start_transient :
4545
val is_active : service:string -> bool
4646
(** [is_active ~service] checks whether the [service] is still running *)
4747

48+
val is_enabled : service:string -> bool
49+
(** [is_enabled ~service] checks whether the [service] is enabled *)
50+
4851
val show : service:string -> status
4952
(** [shows ~service] retrieves the exitcodes and PIDs of the specified [service] *)
5053

ocaml/idl/datamodel_errors.ml

+12
Original file line numberDiff line numberDiff line change
@@ -2028,6 +2028,18 @@ let _ =
20282028

20292029
error Api_errors.too_many_groups [] ~doc:"VM can only belong to one group." () ;
20302030

2031+
error Api_errors.enable_ssh_failed ["host"]
2032+
~doc:"Failed to enable SSH access." () ;
2033+
2034+
error Api_errors.disable_ssh_failed ["host"]
2035+
~doc:"Failed to disable SSH access." () ;
2036+
2037+
error Api_errors.enable_ssh_partially_failed ["hosts"]
2038+
~doc:"Some of hosts failed to enable SSH access." () ;
2039+
2040+
error Api_errors.disable_ssh_partially_failed ["hosts"]
2041+
~doc:"Some of hosts failed to disable SSH access." () ;
2042+
20312043
error Api_errors.host_driver_no_hardware ["driver variant"]
20322044
~doc:"No hardware present for this host driver variant" () ;
20332045

ocaml/idl/datamodel_host.ml

+24
Original file line numberDiff line numberDiff line change
@@ -2346,6 +2346,28 @@ let emergency_clear_mandatory_guidance =
23462346
~doc:"Clear the pending mandatory guidance on this host"
23472347
~allowed_roles:_R_LOCAL_ROOT_ONLY ()
23482348

2349+
let enable_ssh =
2350+
call ~name:"enable_ssh"
2351+
~doc:
2352+
"Enable SSH access on the host. It will start the service sshd only if \
2353+
it is not running. It will also enable the service sshd only if it is \
2354+
not enabled. A newly joined host in the pool or an ejected host from \
2355+
the pool would keep the original status."
2356+
~lifecycle:[]
2357+
~params:[(Ref _host, "self", "The host")]
2358+
~allowed_roles:_R_POOL_ADMIN ()
2359+
2360+
let disable_ssh =
2361+
call ~name:"disable_ssh"
2362+
~doc:
2363+
"Disable SSH access on the host. It will stop the service sshd only if \
2364+
it is running. It will also disable the service sshd only if it is \
2365+
enabled. A newly joined host in the pool or an ejected host from the \
2366+
pool would keep the original status."
2367+
~lifecycle:[]
2368+
~params:[(Ref _host, "self", "The host")]
2369+
~allowed_roles:_R_POOL_ADMIN ()
2370+
23492371
let latest_synced_updates_applied_state =
23502372
Enum
23512373
( "latest_synced_updates_applied_state"
@@ -2503,6 +2525,8 @@ let t =
25032525
; set_https_only
25042526
; apply_recommended_guidances
25052527
; emergency_clear_mandatory_guidance
2528+
; enable_ssh
2529+
; disable_ssh
25062530
]
25072531
~contents:
25082532
([

ocaml/idl/datamodel_lifecycle.ml

+8
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,10 @@ let prototyped_of_message = function
205205
Some "22.26.0"
206206
| "VTPM", "create" ->
207207
Some "22.26.0"
208+
| "host", "disable_ssh" ->
209+
Some "25.12.0-next"
210+
| "host", "enable_ssh" ->
211+
Some "25.12.0-next"
208212
| "host", "emergency_clear_mandatory_guidance" ->
209213
Some "24.10.0"
210214
| "host", "apply_recommended_guidances" ->
@@ -223,6 +227,10 @@ let prototyped_of_message = function
223227
Some "23.30.0"
224228
| "VM", "set_groups" ->
225229
Some "24.19.1"
230+
| "pool", "disable_ssh" ->
231+
Some "25.12.0-next"
232+
| "pool", "enable_ssh" ->
233+
Some "25.12.0-next"
226234
| "pool", "get_guest_secureboot_readiness" ->
227235
Some "24.17.0"
228236
| "pool", "set_ext_auth_cache_expiry" ->

ocaml/idl/datamodel_pool.ml

+20
Original file line numberDiff line numberDiff line change
@@ -1553,6 +1553,24 @@ let get_guest_secureboot_readiness =
15531553
~result:(pool_guest_secureboot_readiness, "The readiness of the pool")
15541554
~allowed_roles:_R_POOL_OP ()
15551555

1556+
let enable_ssh =
1557+
call ~name:"enable_ssh"
1558+
~doc:
1559+
"Enable SSH access on all hosts in the pool. It's a helper which calls \
1560+
host.enable_ssh for all the hosts in the pool."
1561+
~lifecycle:[]
1562+
~params:[(Ref _pool, "self", "The pool")]
1563+
~allowed_roles:_R_POOL_ADMIN ()
1564+
1565+
let disable_ssh =
1566+
call ~name:"disable_ssh"
1567+
~doc:
1568+
"Disable SSH access on all hosts in the pool. It's a helper which calls \
1569+
host.disable_ssh for all the hosts in the pool."
1570+
~lifecycle:[]
1571+
~params:[(Ref _pool, "self", "The pool")]
1572+
~allowed_roles:_R_POOL_ADMIN ()
1573+
15561574
(** A pool class *)
15571575
let t =
15581576
create_obj ~in_db:true
@@ -1647,6 +1665,8 @@ let t =
16471665
; set_ext_auth_cache_size
16481666
; set_ext_auth_cache_expiry
16491667
; get_guest_secureboot_readiness
1668+
; enable_ssh
1669+
; disable_ssh
16501670
]
16511671
~contents:
16521672
([

ocaml/sdk-gen/go/gen_go_helper.ml

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ let acronyms =
3838
; "db"
3939
; "xml"
4040
; "eof"
41+
; "ssh"
4142
]
4243
|> StringSet.of_list
4344

ocaml/xapi-cli-server/cli_frontend.ml

+48
Original file line numberDiff line numberDiff line change
@@ -1050,6 +1050,32 @@ let rec cmdtable_data : (string * cmd_spec) list =
10501050
; flags= [Host_selectors]
10511051
}
10521052
)
1053+
; ( "host-enable-ssh"
1054+
, {
1055+
reqd= []
1056+
; optn= []
1057+
; help=
1058+
"Enable SSH access on the host. It will start the service sshd only \
1059+
if it is not running. It will also enable the service sshd only if \
1060+
it is not enabled. A newly joined host in the pool or an ejected \
1061+
host from the pool would keep the original status."
1062+
; implementation= No_fd Cli_operations.host_enable_ssh
1063+
; flags= [Host_selectors]
1064+
}
1065+
)
1066+
; ( "host-disable-ssh"
1067+
, {
1068+
reqd= []
1069+
; optn= []
1070+
; help=
1071+
"Disable SSH access on the host. It will stop the service sshd only \
1072+
if it is running. It will also disable the service sshd only if it \
1073+
is enabled. A newly joined host in the pool or an ejected host from \
1074+
the pool would keep the original status."
1075+
; implementation= No_fd Cli_operations.host_disable_ssh
1076+
; flags= [Host_selectors]
1077+
}
1078+
)
10531079
; ( "host-emergency-clear-mandatory-guidance"
10541080
, {
10551081
reqd= []
@@ -3107,6 +3133,28 @@ let rec cmdtable_data : (string * cmd_spec) list =
31073133
; flags= []
31083134
}
31093135
)
3136+
; ( "pool-enable-ssh"
3137+
, {
3138+
reqd= []
3139+
; optn= []
3140+
; help=
3141+
"Enable SSH access on all hosts in the pool. It's a helper which \
3142+
calls host.enable_ssh for all the hosts in the pool."
3143+
; implementation= No_fd Cli_operations.pool_enable_ssh
3144+
; flags= []
3145+
}
3146+
)
3147+
; ( "pool-disable-ssh"
3148+
, {
3149+
reqd= []
3150+
; optn= []
3151+
; help=
3152+
"Disable SSH access on all hosts in the pool. It's a helper which \
3153+
calls host.disable_ssh for all the hosts in the pool."
3154+
; implementation= No_fd Cli_operations.pool_disable_ssh
3155+
; flags= []
3156+
}
3157+
)
31103158
; ( "host-ha-xapi-healthcheck"
31113159
, {
31123160
reqd= []

ocaml/xapi-cli-server/cli_operations.ml

+28
Original file line numberDiff line numberDiff line change
@@ -6828,6 +6828,14 @@ let pool_sync_bundle fd _printer rpc session_id params =
68286828
| None ->
68296829
failwith "Required parameter not found: filename"
68306830

6831+
let pool_enable_ssh _printer rpc session_id params =
6832+
let pool = get_pool_with_default rpc session_id params "uuid" in
6833+
Client.Pool.enable_ssh ~rpc ~session_id ~self:pool
6834+
6835+
let pool_disable_ssh _printer rpc session_id params =
6836+
let pool = get_pool_with_default rpc session_id params "uuid" in
6837+
Client.Pool.disable_ssh ~rpc ~session_id ~self:pool
6838+
68316839
let host_restore fd _printer rpc session_id params =
68326840
let filename = List.assoc "file-name" params in
68336841
let op _ host =
@@ -7778,6 +7786,26 @@ let host_apply_updates _printer rpc session_id params =
77787786
params ["hash"]
77797787
)
77807788

7789+
let host_enable_ssh _printer rpc session_id params =
7790+
ignore
7791+
(do_host_op rpc session_id
7792+
(fun _ host ->
7793+
let host = host.getref () in
7794+
Client.Host.enable_ssh ~rpc ~session_id ~self:host
7795+
)
7796+
params []
7797+
)
7798+
7799+
let host_disable_ssh _printer rpc session_id params =
7800+
ignore
7801+
(do_host_op rpc session_id
7802+
(fun _ host ->
7803+
let host = host.getref () in
7804+
Client.Host.disable_ssh ~rpc ~session_id ~self:host
7805+
)
7806+
params []
7807+
)
7808+
77817809
module SDN_controller = struct
77827810
let introduce printer rpc session_id params =
77837811
let port =

ocaml/xapi-consts/api_errors.ml

+8
Original file line numberDiff line numberDiff line change
@@ -1412,6 +1412,14 @@ let illegal_in_fips_mode = add_error "ILLEGAL_IN_FIPS_MODE"
14121412

14131413
let too_many_groups = add_error "TOO_MANY_GROUPS"
14141414

1415+
let enable_ssh_failed = add_error "ENABLE_SSH_FAILED"
1416+
1417+
let disable_ssh_failed = add_error "DISABLE_SSH_FAILED"
1418+
1419+
let enable_ssh_partially_failed = add_error "ENABLE_SSH_PARTIALLY_FAILED"
1420+
1421+
let disable_ssh_partially_failed = add_error "DISABLE_SSH_PARTIALLY_FAILED"
1422+
14151423
let host_driver_no_hardware = add_error "HOST_DRIVER_NO_HARDWARE"
14161424

14171425
let tls_verification_not_enabled_in_pool =

ocaml/xapi/message_forwarding.ml

+20
Original file line numberDiff line numberDiff line change
@@ -1177,6 +1177,14 @@ functor
11771177
let get_guest_secureboot_readiness ~__context ~self =
11781178
info "%s: pool='%s'" __FUNCTION__ (pool_uuid ~__context self) ;
11791179
Local.Pool.get_guest_secureboot_readiness ~__context ~self
1180+
1181+
let enable_ssh ~__context ~self =
1182+
info "%s: pool = '%s'" __FUNCTION__ (pool_uuid ~__context self) ;
1183+
Local.Pool.enable_ssh ~__context ~self
1184+
1185+
let disable_ssh ~__context ~self =
1186+
info "%s: pool = '%s'" __FUNCTION__ (pool_uuid ~__context self) ;
1187+
Local.Pool.disable_ssh ~__context ~self
11801188
end
11811189

11821190
module VM = struct
@@ -4015,6 +4023,18 @@ functor
40154023
let emergency_clear_mandatory_guidance ~__context =
40164024
info "Host.emergency_clear_mandatory_guidance" ;
40174025
Local.Host.emergency_clear_mandatory_guidance ~__context
4026+
4027+
let enable_ssh ~__context ~self =
4028+
info "%s: host = '%s'" __FUNCTION__ (host_uuid ~__context self) ;
4029+
let local_fn = Local.Host.enable_ssh ~self in
4030+
let remote_fn = Client.Host.enable_ssh ~self in
4031+
do_op_on ~local_fn ~__context ~host:self ~remote_fn
4032+
4033+
let disable_ssh ~__context ~self =
4034+
info "%s: host = '%s'" __FUNCTION__ (host_uuid ~__context self) ;
4035+
let local_fn = Local.Host.disable_ssh ~self in
4036+
let remote_fn = Client.Host.disable_ssh ~self in
4037+
do_op_on ~local_fn ~__context ~host:self ~remote_fn
40184038
end
40194039

40204040
module Host_crashdump = struct

ocaml/xapi/xapi_host.ml

+20
Original file line numberDiff line numberDiff line change
@@ -3111,3 +3111,23 @@ let emergency_clear_mandatory_guidance ~__context =
31113111
info "%s: %s is cleared" __FUNCTION__ s
31123112
) ;
31133113
Db.Host.set_pending_guidances ~__context ~self ~value:[]
3114+
3115+
let enable_ssh ~__context ~self =
3116+
try
3117+
Xapi_systemctl.enable ~wait_until_success:false "sshd" ;
3118+
Xapi_systemctl.start ~wait_until_success:false "sshd"
3119+
with _ ->
3120+
raise
3121+
(Api_errors.Server_error
3122+
(Api_errors.enable_ssh_failed, [Ref.string_of self])
3123+
)
3124+
3125+
let disable_ssh ~__context ~self =
3126+
try
3127+
Xapi_systemctl.disable ~wait_until_success:false "sshd" ;
3128+
Xapi_systemctl.stop ~wait_until_success:false "sshd"
3129+
with _ ->
3130+
raise
3131+
(Api_errors.Server_error
3132+
(Api_errors.disable_ssh_failed, [Ref.string_of self])
3133+
)

ocaml/xapi/xapi_host.mli

+4
Original file line numberDiff line numberDiff line change
@@ -563,3 +563,7 @@ val set_https_only :
563563
__context:Context.t -> self:API.ref_host -> value:bool -> unit
564564

565565
val emergency_clear_mandatory_guidance : __context:Context.t -> unit
566+
567+
val enable_ssh : __context:Context.t -> self:API.ref_host -> unit
568+
569+
val disable_ssh : __context:Context.t -> self:API.ref_host -> unit

ocaml/xapi/xapi_pool.ml

+34
Original file line numberDiff line numberDiff line change
@@ -3974,3 +3974,37 @@ let put_bundle_handler (req : Request.t) s _ =
39743974
| None ->
39753975
()
39763976
)
3977+
3978+
module Ssh = struct
3979+
let operate ~__context ~action ~error =
3980+
let hosts = Db.Host.get_all ~__context in
3981+
Helpers.call_api_functions ~__context (fun rpc session_id ->
3982+
let failed_hosts =
3983+
List.fold_left
3984+
(fun failed_hosts host ->
3985+
try
3986+
action ~rpc ~session_id ~self:host ;
3987+
failed_hosts
3988+
with _ -> Ref.string_of host :: failed_hosts
3989+
)
3990+
[] hosts
3991+
in
3992+
match failed_hosts with
3993+
| [] ->
3994+
()
3995+
| _ ->
3996+
raise (Api_errors.Server_error (error, failed_hosts))
3997+
)
3998+
3999+
let enable ~__context ~self:_ =
4000+
operate ~__context ~action:Client.Host.enable_ssh
4001+
~error:Api_errors.enable_ssh_partially_failed
4002+
4003+
let disable ~__context ~self:_ =
4004+
operate ~__context ~action:Client.Host.disable_ssh
4005+
~error:Api_errors.disable_ssh_partially_failed
4006+
end
4007+
4008+
let enable_ssh = Ssh.enable
4009+
4010+
let disable_ssh = Ssh.disable

ocaml/xapi/xapi_pool.mli

+4
Original file line numberDiff line numberDiff line change
@@ -433,3 +433,7 @@ val get_guest_secureboot_readiness :
433433
-> API.pool_guest_secureboot_readiness
434434

435435
val put_bundle_handler : Http.Request.t -> Unix.file_descr -> 'a -> unit
436+
437+
val enable_ssh : __context:Context.t -> self:API.ref_pool -> unit
438+
439+
val disable_ssh : __context:Context.t -> self:API.ref_pool -> unit

0 commit comments

Comments
 (0)