+
    j                     |    R t ^ RIt^ RIt^ RIt^ RIt^ RIHt ^RIHt Rt	Rt
^t^
t ! R R]P                  4      tR# )u,  sshj.scan — one-shot + periodic bandwidth scan (B14).

B2 step 3e: moved verbatim from sshj_cli.py (single-file era) — no
behavior change. Owns the SPEED_* constants (SpeedScan is their
only consumer). Checker arrives duck-typed via the constructor, so
the only package edge is tailnet.ssh_host.
N)ThreadPoolExecutor)ssh_hosti g      @c                      a a ] tR t^t oRtV 3R lt]R 4       t]]R3R l4       t	RR lt
R tR tR	 tR
 tR tRtVtV ;t# )	SpeedScanuU  One-shot bandwidth per UP host (B14): after reachability is known,
measure a small transfer each way — DOWN = remote streams N bytes
(`head -c N /dev/zero`), UP = local streams N bytes into the remote
(`cat >/dev/null`). Bounded by SPEED_TRANSFER_TIMEOUT per leg so a
slow network can never stall the UI; results are per-leg None on
failure (the band simply shows what was measured). Throttled:
SPEED_INTERVAL between passes, first pass after SPEED_FIRST_DELAY.
SSHJ_SPEED_SCAN=0 disables; the dry-run hook (SSHJ_SSH_CMD) also
disables it — an `echo` fake ssh would only measure the echo pipe.c                .  < \         SV `  R R7       Wn        RV n        \        P
                  P                  RR4      R8g  ;'       d%    \        P
                  P                  R4      '       * V n        V P                  \        P
                  P                  RR4      \        R	R
4      V n
        V P                  \        P
                  P                  RR4      \        ^R4      V n        \        V n        / V n        \         P"                  ! 4       V n        R# )T)daemonFSSHJ_SPEED_SCAN10SSHJ_SSH_CMDSSHJ_SPEED_BYTES i   i SSHJ_SPEED_INTERVALiX  N)super__init__checkerdoneosenvirongetenabled_clamp
SPEED_SIZEsizeSPEED_INTERVALintervalSPEED_FIRST_DELAYfirst_delayspeed	threadingLocklock)selfr   	__class__s   &&src/sshj/scan.pyr   SpeedScan.__init__$   s    %	

'8#>#E ? ? "

~ >> 	KK

/A2 F *FJ@	BJJNN3H"$M$2As<,
NN$	    c           	     t     \        V\        V\        V 4      4      4      #   \        \        3 d    Tu # i ; iN)maxminint
ValueError	TypeError)envdfltlohis   &&&&r$   r   SpeedScan._clamp2   s8    	r3r3s8,--I& 	K	s   ! 77Nc                	  aaaa \        V 4      pV P                  R4      R9  d   RV R,          .M. p. ROV,           p\        p/ pRp \        P                  ! 4       p	\        P
                  ! VRVRV R2.,           \        P                  \        P                  R7      p\        P                  ! 4       S,           oVP                  4       fN   Se   SP                  4       '       g   \        P                  ! 4       S8  d   M\        P                  ! R4       K_  VP                  4       f   VP                  4        VP                  4        VP                  ^ 8X  d/   \        P                  ! 4       V	,
          p
V
^ 8  d   Wj,          VR	&   Te*   TP                  e    TP                  P!                  4        Rp \        P                  ! 4       p	\        P
                  ! TRTR
.,           \        P                  \        P                  R7      p\        P                  ! 4       S,           o^ .oTTTT3R lp\"        P$                  ! YP&                  3RR7      pTP)                  4        TP                  4       fd   Se   SP                  4       '       g1   \        P                  ! 4       S8  g   TP+                  4       '       g   M\        P                  ! R4       Ku  TP                  4       f   TP                  4        TP                  4        TP&                  e    TP&                  P!                  4        TP-                  RR7       TP                  ^ 8X  dE   \        P                  ! 4       T	,
          p
T
^ 8  d"   S^ ,          ^ 8  d   S^ ,          T
,          TR&   TeB   TP&                  TP                  TP.                  3 F  pTf   K	   TP!                  4        K  	  T#   \         d     ELki ; i  \         d     ELPi ; i  Te=   TP                  e.    TP                  P!                  4        i   \         d     i i ; ii i ; i  \         d     EL%i ; i  \         d     Li ; i  \         d     K  i ; i  TeU   TP&                  TP                  TP.                  3 F,  pTf   K	   TP!                  4        K    \         d     K*  i ; i	  i i ; i)aP  Transfer `SPEED_SIZE` bytes each way over one ssh connection.
Returns {"up": bps|None, "down": bps|None}; None = leg failed or
the remote lacks the required coreutils (degrade to latency-only).
`stop_event`: on quit, an in-flight leg is killed so the pool's
worker finishes instantly (concurrent.futures' atexit otherwise
waits for it).portz-pNz--zhead -c z
 /dev/zero)stdoutstderrg?downzcat >/dev/null)stdinr6   c                 "  < S'       d8   SP                  4       '       g^   \        P                  ! 4       SS,           8:  d;    V P                  R R,          4      pT'       g   R# S^ ;;,          T,          uu&   Ky  R# R#   \         d     R# i ; i)    i   N)is_settimewrite	Exception)pipekendfed
stop_eventtos   & r$   _feed$SpeedScan.speed_probe.<locals>._feedq   sq    %Z->->-@-@99;#(2 JJuu}5 FaKF 3 .A % s   A? ?BBT)targetargsr   g      ?)timeoutup)r   22)	ssh-ozBatchMode=yesrM   zStrictHostKeyChecking=norM   zLogLevel=ERRORrM   zServerAliveInterval=1)r   r   r   r<   
subprocessPopenPIPEDEVNULLpollr;   sleepkillwait
returncoder>   r5   closer   Threadr8   startis_alivejoinr6   )hrD   rC   tgtr4   basenoutpt0dtrE   ftsrA   rB   s   &ff           @@r$   speed_probeSpeedScan.speed_probe9   s    qk$%EE&M$Cai GIMN	B  shqc4L(M!M(2(2(:(:<A ))+"C&&("*z/@/@/B/B99;#-

3vvxFFH||q YY[2%6"#&CK }!5HHNN$ 6	!B  s4D(E!E'1(2(:(:<A ))+"C
 #C	  	  !!ggZ)-/BHHJ&&("*z/@/@/B/B99;#-!{{}}

3vvxFFHww"GGMMO GGCG ||q YY[2%6c!fqj #ACI }''188QXX6A}!GGI 7 
C  		 !  }!5HHNN$   "6}Z !   		  ) ! ! }''188QXX6A}!GGI( ! !	 7 s   	BO* )B$O*  O< =CQ% /Q% AQ% "Q <A'Q% Q6*O95P 8O99P <P
PQ"P=<Q=QQ
QQQ"Q% !Q""Q% %Q30R 2Q33R 6RR,S":S
S"S	S"S	S"c                   T;'       g    V P                   pVP                  ;_uu_ 4        VP                   Uu. uFK  pVP                  P	                  V4      R8X  g   K%  W2P
                  9   g   K7  W0P                  9  g   KI  VNKM  	  ppV Uu. uF  q2P
                  V,          NK  	  ppRRR4       X F  pV P                  V4       K  	  \        V4      # u upi u upi   + '       g   i     L?; i)zScan every UP host once, concurrently (bounded). Pure-ish:
uses checker.status/by_alias/order; returns the number scanned.
Calls the class-level speed_probe (tests replace that symbol).rJ   N)	r   r!   orderstatusr   by_aliasr   	_scan_onelen)r"   r   catodohostsr\   s   &&     r$   speed_scan_targetsSpeedScan.speed_scan_targets   s     ##t||VVV ww -w!xx||A$. 34

? + AwD - -11DqZZ]]DE1	 
 ANN1 5z- 2	 Vs:   C%"C!C3CCC%C ,C%
C%%C5	c           	     d   VR ,          p\        V P                  RR4      p\        V P                  RR4      pVeO   \        V\        R4      4      '       g4   VP	                  V P
                  V\        P                  WR7      4       R# V P                  V\        P                  WR7      4       R# )aliaspoolNstop_ev)rC   )getattrr   
isinstancetypesubmit_storer   rf   )r"   r\   ru   rv   rw   s   &&   r$   rl   SpeedScan._scan_one   s    '
t||VT2$,,	48JtT$Z$@$@KKUI,A,A!,A,XYKKy44Q4KLr&   c                    V P                   ;_uu_ 4        W P                  V&   R R R 4       R #   + '       g   i     R # ; ir(   )r!   r   )r"   ru   ress   &&&r$   r|   SpeedScan._store   s#    YYY #JJu YYYs   .?	c                    V P                   ;_uu_ 4        V P                  P                  V4      uuR R R 4       #   + '       g   i     R # ; ir(   )r!   r   r   )r"   ru   s   &&r$   r   SpeedScan.get   s'    YYY::>>%( YYYs	   ;A	c                    R V n         R# )TN)r   r"   s   &r$   stopSpeedScan.stop   s	    	r&   c                    V P                   '       g   R # \        P                  ! V P                  4       V P                  '       g3   V P                  4        \        P                  ! V P                  4       KD  R # r(   )r   r<   rS   r   r   rr   r   r   s   &r$   runSpeedScan.run   sJ    |||

4##$)))##%JJt}}% r&   )r   r   r   r   r   r!   r   r   r(   )__name__
__module____qualname____firstlineno____doc__r   staticmethodr   SPEED_TRANSFER_TIMEOUTrf   rr   rl   r|   r   r   r   __static_attributes____classdictcell____classcell__)r#   __classdict__s   @@r$   r   r      sd     L%   0T b bHM$)& &r&   r   )r   r   rN   r   r<   concurrent.futuresr   tailnetr   r   r   r   r   rX   r    r&   r$   <module>r      sJ    
    1  
  m&	   m&r&   