Skip to content

Commit bb672a9

Browse files
committed
CP-48676: Reuse pool sessions on slave logins.
Prevent this reusable pool session from being destroyed so that it remains valid. This feature can be toggled with the flag reuse-pool-sessions. This commit has been reworked to use Atomic instead of a mutex to prevent a deadlock as shown in CA-400339. Signed-off-by: Steven Woods <[email protected]>
1 parent 46dec4b commit bb672a9

File tree

2 files changed

+75
-13
lines changed

2 files changed

+75
-13
lines changed

ocaml/xapi/xapi_globs.ml

+8
Original file line numberDiff line numberDiff line change
@@ -1075,6 +1075,9 @@ let pool_recommendations_dir = ref "/etc/xapi.pool-recommendations.d"
10751075

10761076
let disable_webserver = ref false
10771077

1078+
let reuse_pool_sessions = ref true
1079+
(* Enables the reuse of pool sessions, speeding up intrapool communication *)
1080+
10781081
let test_open = ref 0
10791082

10801083
let tgroups_enabled = ref false
@@ -1709,6 +1712,11 @@ let other_options =
17091712
, (fun () -> !driver_tool)
17101713
, "Path to drivertool for selecting host driver variants"
17111714
)
1715+
; ( "reuse-pool-sessions"
1716+
, Arg.Set reuse_pool_sessions
1717+
, (fun () -> string_of_bool !reuse_pool_sessions)
1718+
, "Enable the reuse of pool sessions"
1719+
)
17121720
]
17131721

17141722
(* The options can be set with the variable xapiflags in /etc/sysconfig/xapi.

ocaml/xapi/xapi_session.ml

+67-13
Original file line numberDiff line numberDiff line change
@@ -398,19 +398,24 @@ let is_subject_suspended ~__context ~cache subject_identifier =
398398
debug "Subject identifier %s is suspended" subject_identifier ;
399399
(is_suspended, subject_name)
400400

401+
let reusable_pool_session = Atomic.make Ref.null
402+
401403
let destroy_db_session ~__context ~self =
402404
Context.with_tracing ~__context __FUNCTION__ @@ fun __context ->
403-
Xapi_event.on_session_deleted self ;
404-
(* unregister from the event system *)
405-
(* This info line is important for tracking, auditability and client accountability purposes on XenServer *)
406-
(* Never print the session id nor uuid: they are secret values that should be known only to the user that *)
407-
(* logged in. Instead, we print a non-invertible hash as the tracking id for the session id *)
408-
(* see also task creation in context.ml *)
409-
(* CP-982: create tracking id in log files to link username to actions *)
410-
info "Session.destroy %s" (trackid self) ;
411-
Rbac_audit.session_destroy ~__context ~session_id:self ;
412-
(try Db.Session.destroy ~__context ~self with _ -> ()) ;
413-
Rbac.destroy_session_permissions_tbl ~session_id:self
405+
if self <> Atomic.get reusable_pool_session then (
406+
Xapi_event.on_session_deleted self ;
407+
(* unregister from the event system *)
408+
(* This info line is important for tracking, auditability and client accountability purposes on XenServer *)
409+
(* Never print the session id nor uuid: they are secret values that should be known only to the user that *)
410+
(* logged in. Instead, we print a non-invertible hash as the tracking id for the session id *)
411+
(* see also task creation in context.ml *)
412+
(* CP-982: create tracking id in log files to link username to actions *)
413+
info "Session.destroy %s" (trackid self) ;
414+
Rbac_audit.session_destroy ~__context ~session_id:self ;
415+
(try Db.Session.destroy ~__context ~self with _ -> ()) ;
416+
Rbac.destroy_session_permissions_tbl ~session_id:self
417+
) else
418+
info "Skipping Session.destroy for reusable pool session %s" (trackid self)
414419

415420
(* CP-703: ensure that activate sessions are invalidated in a bounded time *)
416421
(* in response to external authentication/directory services updates, such as *)
@@ -610,8 +615,8 @@ let revalidate_all_sessions ~__context =
610615
debug "Unexpected exception while revalidating external sessions: %s"
611616
(ExnHelper.string_of_exn e)
612617

613-
let login_no_password_common ~__context ~uname ~originator ~host ~pool
614-
~is_local_superuser ~subject ~auth_user_sid ~auth_user_name
618+
let login_no_password_common_create_session ~__context ~uname ~originator ~host
619+
~pool ~is_local_superuser ~subject ~auth_user_sid ~auth_user_name
615620
~rbac_permissions ~db_ref ~client_certificate =
616621
Context.with_tracing ~originator ~__context __FUNCTION__ @@ fun __context ->
617622
let create_session () =
@@ -661,6 +666,55 @@ let login_no_password_common ~__context ~uname ~originator ~host ~pool
661666
ignore (Client.Pool.get_all ~rpc ~session_id) ;
662667
session_id
663668

669+
let login_no_password_common ~__context ~uname ~originator ~host ~pool
670+
~is_local_superuser ~subject ~auth_user_sid ~auth_user_name
671+
~rbac_permissions ~db_ref ~client_certificate =
672+
Context.with_tracing ~originator ~__context __FUNCTION__ @@ fun __context ->
673+
let is_valid_session session_id =
674+
if session_id = Ref.null then
675+
false
676+
else
677+
try
678+
(* Call an API function to check the session is still valid *)
679+
let rpc = Helpers.make_rpc ~__context in
680+
ignore (Client.Pool.get_all ~rpc ~session_id) ;
681+
true
682+
with Api_errors.Server_error (err, _) ->
683+
debug "%s: Invalid session: %s" __FUNCTION__ err ;
684+
false
685+
in
686+
let create_session () =
687+
login_no_password_common_create_session ~__context ~uname ~originator ~host
688+
~pool ~is_local_superuser ~subject ~auth_user_sid ~auth_user_name
689+
~rbac_permissions ~db_ref ~client_certificate
690+
in
691+
let rec get_session () =
692+
let session = Atomic.get reusable_pool_session in
693+
if is_valid_session session then (
694+
(* Check if the session changed during validation.
695+
Use our version regardless to avoid being stuck in a loop of session creation *)
696+
if Atomic.get reusable_pool_session <> session then
697+
debug "reusable_pool_session has changed, using the original anyway" ;
698+
session
699+
) else
700+
let new_session = create_session () in
701+
if Atomic.compare_and_set reusable_pool_session session new_session then
702+
new_session
703+
else (
704+
(* someone else raced with us and created a session, destroy ours and attempt to use theirs *)
705+
destroy_db_session ~__context ~self:new_session ;
706+
(get_session [@tailcall]) ()
707+
)
708+
in
709+
if
710+
(originator, pool, is_local_superuser, uname)
711+
= (xapi_internal_originator, true, true, None)
712+
&& !Xapi_globs.reuse_pool_sessions
713+
then
714+
get_session ()
715+
else
716+
create_session ()
717+
664718
(* XXX: only used internally by the code which grants the guest access to the API.
665719
Needs to be protected by a proper access control system *)
666720
let login_no_password ~__context ~uname ~host ~pool ~is_local_superuser ~subject

0 commit comments

Comments
 (0)