Skip to content

Commit

Permalink
CP-52074: Add API for start/stop systemd service sshd (#6198)
Browse files Browse the repository at this point in the history
This PR is to add API and CLI which is used to enable and disable SSH on
all hosts in a pool or on specified hosts within a pool.
  • Loading branch information
BengangY authored Jan 14, 2025
2 parents 9eeb1f3 + 944a91d commit 90e9602
Show file tree
Hide file tree
Showing 16 changed files with 271 additions and 6 deletions.
8 changes: 6 additions & 2 deletions ocaml/forkexecd/lib/fe_systemctl.ml
Original file line number Diff line number Diff line change
Expand Up @@ -121,15 +121,19 @@ let stop ~service =
Xapi_stdext_unix.Unixext.unlink_safe destination ;
status

let is_active ~service =
let status ~command ~service =
let status =
Forkhelpers.safe_close_and_exec None None None [] systemctl
["is-active"; "--quiet"; service]
[command; "--quiet"; service]
|> Forkhelpers.waitpid
|> snd
in
Unix.WEXITED 0 = status

let is_active ~service = status ~command:"is-active" ~service

let is_enabled ~service = status ~command:"is-enabled" ~service

(** path to service file *)
let path service = Filename.concat run_path (service ^ ".service")

Expand Down
3 changes: 3 additions & 0 deletions ocaml/forkexecd/lib/fe_systemctl.mli
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ val start_transient :
val is_active : service:string -> bool
(** [is_active ~service] checks whether the [service] is still running *)

val is_enabled : service:string -> bool
(** [is_enabled ~service] checks whether the [service] is enabled *)

val show : service:string -> status
(** [shows ~service] retrieves the exitcodes and PIDs of the specified [service] *)

Expand Down
12 changes: 12 additions & 0 deletions ocaml/idl/datamodel_errors.ml
Original file line number Diff line number Diff line change
Expand Up @@ -2010,6 +2010,18 @@ let _ =

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

error Api_errors.enable_ssh_failed ["host"]
~doc:"Failed to enable SSH access." () ;

error Api_errors.disable_ssh_failed ["host"]
~doc:"Failed to disable SSH access." () ;

error Api_errors.enable_ssh_partially_failed ["hosts"]
~doc:"Some of hosts failed to enable SSH access." () ;

error Api_errors.disable_ssh_partially_failed ["hosts"]
~doc:"Some of hosts failed to disable SSH access." () ;

message
(fst Api_messages.ha_pool_overcommitted)
~doc:
Expand Down
24 changes: 24 additions & 0 deletions ocaml/idl/datamodel_host.ml
Original file line number Diff line number Diff line change
Expand Up @@ -2338,6 +2338,28 @@ let emergency_clear_mandatory_guidance =
~doc:"Clear the pending mandatory guidance on this host"
~allowed_roles:_R_LOCAL_ROOT_ONLY ()

let enable_ssh =
call ~name:"enable_ssh"
~doc:
"Enable SSH access on the host. It will start the service sshd only if \
it is not running. It will also enable the service sshd only if it is \
not enabled. A newly joined host in the pool or an ejected host from \
the pool would keep the original status."
~lifecycle:[]
~params:[(Ref _host, "self", "The host")]
~allowed_roles:_R_POOL_ADMIN ()

let disable_ssh =
call ~name:"disable_ssh"
~doc:
"Disable SSH access on the host. It will stop the service sshd only if \
it is running. It will also disable the service sshd only if it is \
enabled. A newly joined host in the pool or an ejected host from the \
pool would keep the original status."
~lifecycle:[]
~params:[(Ref _host, "self", "The host")]
~allowed_roles:_R_POOL_ADMIN ()

let latest_synced_updates_applied_state =
Enum
( "latest_synced_updates_applied_state"
Expand Down Expand Up @@ -2494,6 +2516,8 @@ let t =
; set_https_only
; apply_recommended_guidances
; emergency_clear_mandatory_guidance
; enable_ssh
; disable_ssh
]
~contents:
([
Expand Down
20 changes: 20 additions & 0 deletions ocaml/idl/datamodel_pool.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1539,6 +1539,24 @@ let get_guest_secureboot_readiness =
~result:(pool_guest_secureboot_readiness, "The readiness of the pool")
~allowed_roles:_R_POOL_OP ()

let enable_ssh =
call ~name:"enable_ssh"
~doc:
"Enable SSH access on all hosts in the pool. It's a helper which calls \
host.enable_ssh for all the hosts in the pool."
~lifecycle:[]
~params:[(Ref _pool, "self", "The pool")]
~allowed_roles:_R_POOL_ADMIN ()

let disable_ssh =
call ~name:"disable_ssh"
~doc:
"Disable SSH access on all hosts in the pool. It's a helper which calls \
host.disable_ssh for all the hosts in the pool."
~lifecycle:[]
~params:[(Ref _pool, "self", "The pool")]
~allowed_roles:_R_POOL_ADMIN ()

(** A pool class *)
let t =
create_obj ~in_db:true
Expand Down Expand Up @@ -1633,6 +1651,8 @@ let t =
; set_ext_auth_cache_size
; set_ext_auth_cache_expiry
; get_guest_secureboot_readiness
; enable_ssh
; disable_ssh
]
~contents:
([
Expand Down
1 change: 1 addition & 0 deletions ocaml/sdk-gen/go/gen_go_helper.ml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ let acronyms =
; "db"
; "xml"
; "eof"
; "ssh"
]
|> StringSet.of_list

Expand Down
48 changes: 48 additions & 0 deletions ocaml/xapi-cli-server/cli_frontend.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1048,6 +1048,32 @@ let rec cmdtable_data : (string * cmd_spec) list =
; flags= [Host_selectors]
}
)
; ( "host-enable-ssh"
, {
reqd= []
; optn= []
; help=
"Enable SSH access on the host. It will start the service sshd only \
if it is not running. It will also enable the service sshd only if \
it is not enabled. A newly joined host in the pool or an ejected \
host from the pool would keep the original status."
; implementation= No_fd Cli_operations.host_enable_ssh
; flags= [Host_selectors]
}
)
; ( "host-disable-ssh"
, {
reqd= []
; optn= []
; help=
"Disable SSH access on the host. It will stop the service sshd only \
if it is running. It will also disable the service sshd only if it \
is enabled. A newly joined host in the pool or an ejected host from \
the pool would keep the original status."
; implementation= No_fd Cli_operations.host_disable_ssh
; flags= [Host_selectors]
}
)
; ( "host-emergency-clear-mandatory-guidance"
, {
reqd= []
Expand Down Expand Up @@ -3105,6 +3131,28 @@ let rec cmdtable_data : (string * cmd_spec) list =
; flags= []
}
)
; ( "pool-enable-ssh"
, {
reqd= []
; optn= []
; help=
"Enable SSH access on all hosts in the pool. It's a helper which \
calls host.enable_ssh for all the hosts in the pool."
; implementation= No_fd Cli_operations.pool_enable_ssh
; flags= []
}
)
; ( "pool-disable-ssh"
, {
reqd= []
; optn= []
; help=
"Disable SSH access on all hosts in the pool. It's a helper which \
calls host.disable_ssh for all the hosts in the pool."
; implementation= No_fd Cli_operations.pool_disable_ssh
; flags= []
}
)
; ( "host-ha-xapi-healthcheck"
, {
reqd= []
Expand Down
28 changes: 28 additions & 0 deletions ocaml/xapi-cli-server/cli_operations.ml
Original file line number Diff line number Diff line change
Expand Up @@ -6779,6 +6779,14 @@ let pool_sync_bundle fd _printer rpc session_id params =
| None ->
failwith "Required parameter not found: filename"

let pool_enable_ssh _printer rpc session_id params =
let pool = get_pool_with_default rpc session_id params "uuid" in
Client.Pool.enable_ssh ~rpc ~session_id ~self:pool

let pool_disable_ssh _printer rpc session_id params =
let pool = get_pool_with_default rpc session_id params "uuid" in
Client.Pool.disable_ssh ~rpc ~session_id ~self:pool

let host_restore fd _printer rpc session_id params =
let filename = List.assoc "file-name" params in
let op _ host =
Expand Down Expand Up @@ -7729,6 +7737,26 @@ let host_apply_updates _printer rpc session_id params =
params ["hash"]
)

let host_enable_ssh _printer rpc session_id params =
ignore
(do_host_op rpc session_id
(fun _ host ->
let host = host.getref () in
Client.Host.enable_ssh ~rpc ~session_id ~self:host
)
params []
)

let host_disable_ssh _printer rpc session_id params =
ignore
(do_host_op rpc session_id
(fun _ host ->
let host = host.getref () in
Client.Host.disable_ssh ~rpc ~session_id ~self:host
)
params []
)

module SDN_controller = struct
let introduce printer rpc session_id params =
let port =
Expand Down
8 changes: 8 additions & 0 deletions ocaml/xapi-consts/api_errors.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1403,3 +1403,11 @@ let telemetry_next_collection_too_late =
let illegal_in_fips_mode = add_error "ILLEGAL_IN_FIPS_MODE"

let too_many_groups = add_error "TOO_MANY_GROUPS"

let enable_ssh_failed = add_error "ENABLE_SSH_FAILED"

let disable_ssh_failed = add_error "DISABLE_SSH_FAILED"

let enable_ssh_partially_failed = add_error "ENABLE_SSH_PARTIALLY_FAILED"

let disable_ssh_partially_failed = add_error "DISABLE_SSH_PARTIALLY_FAILED"
22 changes: 22 additions & 0 deletions ocaml/xapi/message_forwarding.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1185,6 +1185,14 @@ functor
let get_guest_secureboot_readiness ~__context ~self =
info "%s: pool='%s'" __FUNCTION__ (pool_uuid ~__context self) ;
Local.Pool.get_guest_secureboot_readiness ~__context ~self

let enable_ssh ~__context ~self =
info "%s: pool = '%s'" __FUNCTION__ (pool_uuid ~__context self) ;
Local.Pool.enable_ssh ~__context ~self

let disable_ssh ~__context ~self =
info "%s: pool = '%s'" __FUNCTION__ (pool_uuid ~__context self) ;
Local.Pool.disable_ssh ~__context ~self
end

module VM = struct
Expand Down Expand Up @@ -4154,6 +4162,20 @@ functor
let emergency_clear_mandatory_guidance ~__context =
info "Host.emergency_clear_mandatory_guidance" ;
Local.Host.emergency_clear_mandatory_guidance ~__context

let enable_ssh ~__context ~self =
info "%s: host = '%s'" __FUNCTION__ (host_uuid ~__context self) ;
let local_fn = Local.Host.enable_ssh ~self in
do_op_on ~local_fn ~__context ~host:self (fun session_id rpc ->
Client.Host.enable_ssh ~rpc ~session_id ~self
)

let disable_ssh ~__context ~self =
info "%s: host = '%s'" __FUNCTION__ (host_uuid ~__context self) ;
let local_fn = Local.Host.disable_ssh ~self in
do_op_on ~local_fn ~__context ~host:self (fun session_id rpc ->
Client.Host.disable_ssh ~rpc ~session_id ~self
)
end

module Host_crashdump = struct
Expand Down
20 changes: 20 additions & 0 deletions ocaml/xapi/xapi_host.ml
Original file line number Diff line number Diff line change
Expand Up @@ -3127,3 +3127,23 @@ let emergency_clear_mandatory_guidance ~__context =
info "%s: %s is cleared" __FUNCTION__ s
) ;
Db.Host.set_pending_guidances ~__context ~self ~value:[]

let enable_ssh ~__context ~self =
try
Xapi_systemctl.enable ~wait_until_success:false "sshd" ;
Xapi_systemctl.start ~wait_until_success:false "sshd"
with _ ->
raise
(Api_errors.Server_error
(Api_errors.enable_ssh_failed, [Ref.string_of self])
)

let disable_ssh ~__context ~self =
try
Xapi_systemctl.disable ~wait_until_success:false "sshd" ;
Xapi_systemctl.stop ~wait_until_success:false "sshd"
with _ ->
raise
(Api_errors.Server_error
(Api_errors.disable_ssh_failed, [Ref.string_of self])
)
4 changes: 4 additions & 0 deletions ocaml/xapi/xapi_host.mli
Original file line number Diff line number Diff line change
Expand Up @@ -561,3 +561,7 @@ val set_https_only :
__context:Context.t -> self:API.ref_host -> value:bool -> unit

val emergency_clear_mandatory_guidance : __context:Context.t -> unit

val enable_ssh : __context:Context.t -> self:API.ref_host -> unit

val disable_ssh : __context:Context.t -> self:API.ref_host -> unit
34 changes: 34 additions & 0 deletions ocaml/xapi/xapi_pool.ml
Original file line number Diff line number Diff line change
Expand Up @@ -3952,3 +3952,37 @@ let put_bundle_handler (req : Request.t) s _ =
| None ->
()
)

module Ssh = struct
let operate ~__context ~action ~error =
let hosts = Db.Host.get_all ~__context in
Helpers.call_api_functions ~__context (fun rpc session_id ->
let failed_hosts =
List.fold_left
(fun failed_hosts host ->
try
action ~rpc ~session_id ~self:host ;
failed_hosts
with _ -> Ref.string_of host :: failed_hosts
)
[] hosts
in
match failed_hosts with
| [] ->
()
| _ ->
raise (Api_errors.Server_error (error, failed_hosts))
)

let enable ~__context ~self:_ =
operate ~__context ~action:Client.Host.enable_ssh
~error:Api_errors.enable_ssh_partially_failed

let disable ~__context ~self:_ =
operate ~__context ~action:Client.Host.disable_ssh
~error:Api_errors.disable_ssh_partially_failed
end

let enable_ssh = Ssh.enable

let disable_ssh = Ssh.disable
4 changes: 4 additions & 0 deletions ocaml/xapi/xapi_pool.mli
Original file line number Diff line number Diff line change
Expand Up @@ -434,3 +434,7 @@ val get_guest_secureboot_readiness :
-> API.pool_guest_secureboot_readiness

val put_bundle_handler : Http.Request.t -> Unix.file_descr -> 'a -> unit

val enable_ssh : __context:Context.t -> self:API.ref_pool -> unit

val disable_ssh : __context:Context.t -> self:API.ref_pool -> unit
Loading

0 comments on commit 90e9602

Please sign in to comment.