PNG  IHDRX cHRMz&u0`:pQ<bKGD pHYsodtIME MeqIDATxw]Wug^Qd˶ 6`!N:!@xI~)%7%@Bh&`lnjVF29gΨ4E$|>cɚ{gk= %,a KX%,a KX%,a KX%,a KX%,a KX%,a KX%, b` ǟzeאfp]<!SJmɤY޲ڿ,%c ~ع9VH.!Ͳz&QynֺTkRR.BLHi٪:l;@(!MԴ=žI,:o&N'Kù\vRmJ雵֫AWic H@" !: Cé||]k-Ha oݜ:y F())u]aG7*JV@J415p=sZH!=!DRʯvɱh~V\}v/GKY$n]"X"}t@ xS76^[bw4dsce)2dU0 CkMa-U5tvLƀ~mlMwfGE/-]7XAƟ`׮g ewxwC4\[~7@O-Q( a*XGƒ{ ՟}$_y3tĐƤatgvێi|K=uVyrŲlLӪuܿzwk$m87k( `múcE)"@rK( z4$D; 2kW=Xb$V[Ru819קR~qloѱDyįݎ*mxw]y5e4K@ЃI0A D@"BDk_)N\8͜9dz"fK0zɿvM /.:2O{ Nb=M=7>??Zuo32 DLD@D| &+֎C #B8ַ`bOb $D#ͮҪtx]%`ES`Ru[=¾!@Od37LJ0!OIR4m]GZRJu$‡c=%~s@6SKy?CeIh:[vR@Lh | (BhAMy=݃  G"'wzn޺~8ԽSh ~T*A:xR[ܹ?X[uKL_=fDȊ؂p0}7=D$Ekq!/t.*2ʼnDbŞ}DijYaȲ(""6HA;:LzxQ‘(SQQ}*PL*fc\s `/d'QXW, e`#kPGZuŞuO{{wm[&NBTiiI0bukcA9<4@SӊH*؎4U/'2U5.(9JuDfrޱtycU%j(:RUbArLֺN)udA':uGQN"-"Is.*+k@ `Ojs@yU/ H:l;@yyTn}_yw!VkRJ4P)~y#)r,D =ě"Q]ci'%HI4ZL0"MJy 8A{ aN<8D"1#IJi >XjX֔#@>-{vN!8tRݻ^)N_╗FJEk]CT՟ YP:_|H1@ CBk]yKYp|og?*dGvzنzӴzjֺNkC~AbZƷ`.H)=!QͷVTT(| u78y֮}|[8-Vjp%2JPk[}ԉaH8Wpqhwr:vWª<}l77_~{s۴V+RCģ%WRZ\AqHifɤL36: #F:p]Bq/z{0CU6ݳEv_^k7'>sq*+kH%a`0ԣisqにtү04gVgW΂iJiS'3w.w}l6MC2uԯ|>JF5`fV5m`Y**Db1FKNttu]4ccsQNnex/87+}xaUW9y>ͯ骵G{䩓Գ3+vU}~jJ.NFRD7<aJDB1#ҳgSb,+CS?/ VG J?|?,2#M9}B)MiE+G`-wo߫V`fio(}S^4e~V4bHOYb"b#E)dda:'?}׮4繏`{7Z"uny-?ǹ;0MKx{:_pÚmFמ:F " .LFQLG)Q8qN q¯¯3wOvxDb\. BKD9_NN &L:4D{mm o^tֽ:q!ƥ}K+<"m78N< ywsard5+вz~mnG)=}lYݧNj'QJS{S :UYS-952?&O-:W}(!6Mk4+>A>j+i|<<|;ر^߉=HE|V#F)Emm#}/"y GII웻Jі94+v뾧xu~5C95~ūH>c@덉pʃ1/4-A2G%7>m;–Y,cyyaln" ?ƻ!ʪ<{~h~i y.zZB̃/,雋SiC/JFMmBH&&FAbϓO^tubbb_hZ{_QZ-sύodFgO(6]TJA˯#`۶ɟ( %$&+V'~hiYy>922 Wp74Zkq+Ovn錄c>8~GqܲcWꂎz@"1A.}T)uiW4="jJ2W7mU/N0gcqܗOO}?9/wìXžΏ0 >֩(V^Rh32!Hj5`;O28؇2#ݕf3 ?sJd8NJ@7O0 b־?lldщ̡&|9C.8RTWwxWy46ah嘦mh٤&l zCy!PY?: CJyв]dm4ǜҐR޻RլhX{FƯanшQI@x' ao(kUUuxW_Ñ줮[w8 FRJ(8˼)_mQ _!RJhm=!cVmm ?sFOnll6Qk}alY}; "baӌ~M0w,Ggw2W:G/k2%R,_=u`WU R.9T"v,<\Ik޽/2110Ӿxc0gyC&Ny޽JҢrV6N ``یeA16"J³+Rj*;BϜkZPJaÍ<Jyw:NP8/D$ 011z֊Ⱳ3ι֘k1V_"h!JPIΣ'ɜ* aEAd:ݺ>y<}Lp&PlRfTb1]o .2EW\ͮ]38؋rTJsǏP@芎sF\> P^+dYJLbJ C-xϐn> ι$nj,;Ǖa FU *择|h ~izť3ᤓ`K'-f tL7JK+vf2)V'-sFuB4i+m+@My=O҈0"|Yxoj,3]:cо3 $#uŘ%Y"y죯LebqtҢVzq¼X)~>4L׶m~[1_k?kxֺQ`\ |ٛY4Ѯr!)N9{56(iNq}O()Em]=F&u?$HypWUeB\k]JɩSع9 Zqg4ZĊo oMcjZBU]B\TUd34ݝ~:7ڶSUsB0Z3srx 7`:5xcx !qZA!;%͚7&P H<WL!džOb5kF)xor^aujƍ7 Ǡ8/p^(L>ὴ-B,{ۇWzֺ^k]3\EE@7>lYBȝR.oHnXO/}sB|.i@ɥDB4tcm,@ӣgdtJ!lH$_vN166L__'Z)y&kH;:,Y7=J 9cG) V\hjiE;gya~%ks_nC~Er er)muuMg2;֫R)Md) ,¶ 2-wr#F7<-BBn~_(o=KO㭇[Xv eN_SMgSҐ BS헃D%g_N:/pe -wkG*9yYSZS.9cREL !k}<4_Xs#FmҶ:7R$i,fi!~' # !6/S6y@kZkZcX)%5V4P]VGYq%H1!;e1MV<!ϐHO021Dp= HMs~~a)ަu7G^];git!Frl]H/L$=AeUvZE4P\.,xi {-~p?2b#amXAHq)MWǾI_r`S Hz&|{ +ʖ_= (YS(_g0a03M`I&'9vl?MM+m~}*xT۲(fY*V4x@29s{DaY"toGNTO+xCAO~4Ϳ;p`Ѫ:>Ҵ7K 3}+0 387x\)a"/E>qpWB=1 ¨"MP(\xp߫́A3+J] n[ʼnӼaTbZUWb={~2ooKױӰp(CS\S筐R*JغV&&"FA}J>G֐p1ٸbk7 ŘH$JoN <8s^yk_[;gy-;߉DV{c B yce% aJhDȶ 2IdйIB/^n0tNtџdcKj4϶v~- CBcgqx9= PJ) dMsjpYB] GD4RDWX +h{y`,3ꊕ$`zj*N^TP4L:Iz9~6s) Ga:?y*J~?OrMwP\](21sZUD ?ܟQ5Q%ggW6QdO+\@ ̪X'GxN @'4=ˋ+*VwN ne_|(/BDfj5(Dq<*tNt1х!MV.C0 32b#?n0pzj#!38}޴o1KovCJ`8ŗ_"]] rDUy޲@ Ȗ-;xџ'^Y`zEd?0„ DAL18IS]VGq\4o !swV7ˣι%4FѮ~}6)OgS[~Q vcYbL!wG3 7띸*E Pql8=jT\꘿I(z<[6OrR8ºC~ډ]=rNl[g|v TMTղb-o}OrP^Q]<98S¤!k)G(Vkwyqyr޽Nv`N/e p/~NAOk \I:G6]4+K;j$R:Mi #*[AȚT,ʰ,;N{HZTGMoּy) ]%dHء9Պ䠬|<45,\=[bƟ8QXeB3- &dҩ^{>/86bXmZ]]yޚN[(WAHL$YAgDKp=5GHjU&99v簪C0vygln*P)9^͞}lMuiH!̍#DoRBn9l@ xA/_v=ȺT{7Yt2N"4!YN`ae >Q<XMydEB`VU}u]嫇.%e^ánE87Mu\t`cP=AD/G)sI"@MP;)]%fH9'FNsj1pVhY&9=0pfuJ&gޤx+k:!r˭wkl03׼Ku C &ѓYt{.O.zҏ z}/tf_wEp2gvX)GN#I ݭ߽v/ .& и(ZF{e"=V!{zW`, ]+LGz"(UJp|j( #V4, 8B 0 9OkRrlɱl94)'VH9=9W|>PS['G(*I1==C<5"Pg+x'K5EMd؞Af8lG ?D FtoB[je?{k3zQ vZ;%Ɠ,]E>KZ+T/ EJxOZ1i #T<@ I}q9/t'zi(EMqw`mYkU6;[t4DPeckeM;H}_g pMww}k6#H㶏+b8雡Sxp)&C $@'b,fPߑt$RbJ'vznuS ~8='72_`{q纶|Q)Xk}cPz9p7O:'|G~8wx(a 0QCko|0ASD>Ip=4Q, d|F8RcU"/KM opKle M3#i0c%<7׿p&pZq[TR"BpqauIp$ 8~Ĩ!8Սx\ւdT>>Z40ks7 z2IQ}ItԀ<-%S⍤};zIb$I 5K}Q͙D8UguWE$Jh )cu4N tZl+[]M4k8֦Zeq֮M7uIqG 1==tLtR,ƜSrHYt&QP윯Lg' I,3@P'}'R˪e/%-Auv·ñ\> vDJzlӾNv5:|K/Jb6KI9)Zh*ZAi`?S {aiVDԲuy5W7pWeQJk֤#5&V<̺@/GH?^τZL|IJNvI:'P=Ϛt"¨=cud S Q.Ki0 !cJy;LJR;G{BJy޺[^8fK6)=yʊ+(k|&xQ2`L?Ȓ2@Mf 0C`6-%pKpm')c$׻K5[J*U[/#hH!6acB JA _|uMvDyk y)6OPYjœ50VT K}cǻP[ $:]4MEA.y)|B)cf-A?(e|lɉ#P9V)[9t.EiQPDѠ3ϴ;E:+Օ t ȥ~|_N2,ZJLt4! %ա]u {+=p.GhNcŞQI?Nd'yeh n7zi1DB)1S | S#ًZs2|Ɛy$F SxeX{7Vl.Src3E℃Q>b6G ўYCmtկ~=K0f(=LrAS GN'ɹ9<\!a`)֕y[uՍ[09` 9 +57ts6}b4{oqd+J5fa/,97J#6yν99mRWxJyѡyu_TJc`~W>l^q#Ts#2"nD1%fS)FU w{ܯ R{ ˎ󅃏џDsZSQS;LV;7 Od1&1n$ N /.q3~eNɪ]E#oM~}v֯FڦwyZ=<<>Xo稯lfMFV6p02|*=tV!c~]fa5Y^Q_WN|Vs 0ҘދU97OI'N2'8N֭fgg-}V%y]U4 峧p*91#9U kCac_AFңĪy뚇Y_AiuYyTTYЗ-(!JFLt›17uTozc. S;7A&&<ԋ5y;Ro+:' *eYJkWR[@F %SHWP 72k4 qLd'J "zB6{AC0ƁA6U.'F3:Ȅ(9ΜL;D]m8ڥ9}dU "v!;*13Rg^fJyShyy5auA?ɩGHRjo^]׽S)Fm\toy 4WQS@mE#%5ʈfFYDX ~D5Ϡ9tE9So_aU4?Ѽm%&c{n>.KW1Tlb}:j uGi(JgcYj0qn+>) %\!4{LaJso d||u//P_y7iRJ߬nHOy) l+@$($VFIQ9%EeKʈU. ia&FY̒mZ=)+qqoQn >L!qCiDB;Y<%} OgBxB!ØuG)WG9y(Ą{_yesuZmZZey'Wg#C~1Cev@0D $a@˲(.._GimA:uyw֬%;@!JkQVM_Ow:P.s\)ot- ˹"`B,e CRtaEUP<0'}r3[>?G8xU~Nqu;Wm8\RIkբ^5@k+5(By'L&'gBJ3ݶ!/㮻w҅ yqPWUg<e"Qy*167΃sJ\oz]T*UQ<\FԎ`HaNmڜ6DysCask8wP8y9``GJ9lF\G g's Nn͵MLN֪u$| /|7=]O)6s !ĴAKh]q_ap $HH'\1jB^s\|- W1:=6lJBqjY^LsPk""`]w)󭃈,(HC ?䔨Y$Sʣ{4Z+0NvQkhol6C.婧/u]FwiVjZka&%6\F*Ny#8O,22+|Db~d ~Çwc N:FuuCe&oZ(l;@ee-+Wn`44AMK➝2BRՈt7g*1gph9N) *"TF*R(#'88pm=}X]u[i7bEc|\~EMn}P瘊J)K.0i1M6=7'_\kaZ(Th{K*GJyytw"IO-PWJk)..axӝ47"89Cc7ĐBiZx 7m!fy|ϿF9CbȩV 9V-՛^pV̌ɄS#Bv4-@]Vxt-Z, &ֺ*diؠ2^VXbs֔Ìl.jQ]Y[47gj=幽ex)A0ip׳ W2[ᎇhuE^~q흙L} #-b۸oFJ_QP3r6jr+"nfzRJTUqoaۍ /$d8Mx'ݓ= OՃ| )$2mcM*cЙj}f };n YG w0Ia!1Q.oYfr]DyISaP}"dIӗթO67jqR ҊƐƈaɤGG|h;t]䗖oSv|iZqX)oalv;۩meEJ\!8=$4QU4Xo&VEĊ YS^E#d,yX_> ۘ-e\ "Wa6uLĜZi`aD9.% w~mB(02G[6y.773a7 /=o7D)$Z 66 $bY^\CuP. (x'"J60׿Y:Oi;F{w佩b+\Yi`TDWa~|VH)8q/=9!g߆2Y)?ND)%?Ǐ`k/sn:;O299yB=a[Ng 3˲N}vLNy;*?x?~L&=xyӴ~}q{qE*IQ^^ͧvü{Huu=R|>JyUlZV, B~/YF!Y\u_ݼF{_C)LD]m {H 0ihhadd nUkf3oٺCvE\)QJi+֥@tDJkB$1!Đr0XQ|q?d2) Ӣ_}qv-< FŊ߫%roppVBwü~JidY4:}L6M7f٬F "?71<2#?Jyy4뷢<_a7_=Q E=S1И/9{+93֮E{ǂw{))?maÆm(uLE#lïZ  ~d];+]h j?!|$F}*"4(v'8s<ŏUkm7^7no1w2ؗ}TrͿEk>p'8OB7d7R(A 9.*Mi^ͳ; eeUwS+C)uO@ =Sy]` }l8^ZzRXj[^iUɺ$tj))<sbDJfg=Pk_{xaKo1:-uyG0M ԃ\0Lvuy'ȱc2Ji AdyVgVh!{]/&}}ċJ#%d !+87<;qN޼Nفl|1N:8ya  8}k¾+-$4FiZYÔXk*I&'@iI99)HSh4+2G:tGhS^繿 Kتm0 вDk}֚+QT4;sC}rՅE,8CX-e~>G&'9xpW,%Fh,Ry56Y–hW-(v_,? ; qrBk4-V7HQ;ˇ^Gv1JVV%,ik;D_W!))+BoS4QsTM;gt+ndS-~:11Sgv!0qRVh!"Ȋ(̦Yl.]PQWgٳE'`%W1{ndΗBk|Ž7ʒR~,lnoa&:ü$ 3<a[CBݮwt"o\ePJ=Hz"_c^Z.#ˆ*x z̝grY]tdkP*:97YľXyBkD4N.C_[;F9`8& !AMO c `@BA& Ost\-\NX+Xp < !bj3C&QL+*&kAQ=04}cC!9~820G'PC9xa!w&bo_1 Sw"ܱ V )Yl3+ס2KoXOx]"`^WOy :3GO0g;%Yv㐫(R/r (s } u B &FeYZh0y> =2<Ϟc/ -u= c&׭,.0"g"7 6T!vl#sc>{u/Oh Bᾈ)۴74]x7 gMӒ"d]U)}" v4co[ ɡs 5Gg=XR14?5A}D "b{0$L .\4y{_fe:kVS\\O]c^W52LSBDM! C3Dhr̦RtArx4&agaN3Cf<Ԉp4~ B'"1@.b_/xQ} _߃҉/gٓ2Qkqp0շpZ2fԫYz< 4L.Cyυι1t@鎫Fe sYfsF}^ V}N<_`p)alٶ "(XEAVZ<)2},:Ir*#m_YӼ R%a||EƼIJ,,+f"96r/}0jE/)s)cjW#w'Sʯ5<66lj$a~3Kʛy 2:cZ:Yh))+a߭K::N,Q F'qB]={.]h85C9cr=}*rk?vwV렵ٸW Rs%}rNAkDv|uFLBkWY YkX מ|)1!$#3%y?pF<@<Rr0}: }\J [5FRxY<9"SQdE(Q*Qʻ)q1E0B_O24[U'],lOb ]~WjHޏTQ5Syu wq)xnw8~)c 쫬gٲߠ H% k5dƝk> kEj,0% b"vi2Wس_CuK)K{n|>t{P1򨾜j>'kEkƗBg*H%'_aY6Bn!TL&ɌOb{c`'d^{t\i^[uɐ[}q0lM˕G:‚4kb祔c^:?bpg… +37stH:0}en6x˟%/<]BL&* 5&fK9Mq)/iyqtA%kUe[ڛKN]Ě^,"`/ s[EQQm?|XJ߅92m]G.E΃ח U*Cn.j_)Tѧj̿30ڇ!A0=͜ar I3$C^-9#|pk!)?7.x9 @OO;WƝZBFU keZ75F6Tc6"ZȚs2y/1 ʵ:u4xa`C>6Rb/Yм)^=+~uRd`/|_8xbB0?Ft||Z\##|K 0>>zxv8۴吅q 8ĥ)"6>~\8:qM}#͚'ĉ#p\׶ l#bA?)|g g9|8jP(cr,BwV (WliVxxᡁ@0Okn;ɥh$_ckCgriv}>=wGzβ KkBɛ[˪ !J)h&k2%07δt}!d<9;I&0wV/ v 0<H}L&8ob%Hi|޶o&h1L|u֦y~󛱢8fٲUsւ)0oiFx2}X[zVYr_;N(w]_4B@OanC?gĦx>мgx>ΛToZoOMp>40>V Oy V9iq!4 LN,ˢu{jsz]|"R޻&'ƚ{53ўFu(<٪9:΋]B;)B>1::8;~)Yt|0(pw2N%&X,URBK)3\zz&}ax4;ǟ(tLNg{N|Ǽ\G#C9g$^\}p?556]/RP.90 k,U8/u776s ʪ_01چ|\N 0VV*3H鴃J7iI!wG_^ypl}r*jɤSR 5QN@ iZ#1ٰy;_\3\BQQ x:WJv츟ٯ$"@6 S#qe딇(/P( Dy~TOϻ<4:-+F`0||;Xl-"uw$Цi󼕝mKʩorz"mϺ$F:~E'ҐvD\y?Rr8_He@ e~O,T.(ފR*cY^m|cVR[8 JҡSm!ΆԨb)RHG{?MpqrmN>߶Y)\p,d#xۆWY*,l6]v0h15M˙MS8+EdI='LBJIH7_9{Caз*Lq,dt >+~ّeʏ?xԕ4bBAŚjﵫ!'\Ը$WNvKO}ӽmSşذqsOy?\[,d@'73'j%kOe`1.g2"e =YIzS2|zŐƄa\U,dP;jhhhaxǶ?КZ՚.q SE+XrbOu%\GتX(H,N^~]JyEZQKceTQ]VGYqnah;y$cQahT&QPZ*iZ8UQQM.qo/T\7X"u?Mttl2Xq(IoW{R^ ux*SYJ! 4S.Jy~ BROS[V|žKNɛP(L6V^|cR7i7nZW1Fd@ Ara{詑|(T*dN]Ko?s=@ |_EvF]׍kR)eBJc" MUUbY6`~V޴dJKß&~'d3i WWWWWW
Current Directory: /opt/saltstack/salt/lib/python3.10/site-packages/salt
Viewing File: /opt/saltstack/salt/lib/python3.10/site-packages/salt/scripts.py
""" This module contains the function calls to execute command line scripts """ import contextlib import functools import logging import os import signal import subprocess import sys import threading import time import traceback from random import randint import salt.defaults.exitcodes from salt.exceptions import SaltClientError, SaltReqTimeoutError, SaltSystemExit log = logging.getLogger(__name__) def _handle_signals(client, signum, sigframe): try: hardcrash = client.options.hard_crash except (AttributeError, KeyError): hardcrash = False if signum == signal.SIGINT: exit_msg = "\nExiting gracefully on Ctrl-c" try: jid = client.local_client.pub_data["jid"] exit_msg += ( "\n" "This job's jid is: {0}\n" "The minions may not have all finished running and any remaining " "minions will return upon completion. To look up the return data " "for this job later, run the following command:\n\n" "salt-run jobs.lookup_jid {0}".format(jid) ) except (AttributeError, KeyError): pass else: exit_msg = None if exit_msg is None and hardcrash: exit_msg = "\nExiting with hard crash on Ctrl-c" if exit_msg: print(exit_msg, file=sys.stderr, flush=True) if hardcrash: try: # This raises AttributeError on Python 3.4 and 3.5 if there is no current exception. # Ref: https://bugs.python.org/issue23003 trace = traceback.format_exc() log.error(trace) except AttributeError: pass sys.exit(salt.defaults.exitcodes.EX_GENERIC) sys.exit(salt.defaults.exitcodes.EX_OK) def _install_signal_handlers(client): # Install the SIGINT/SIGTERM handlers if not done so far if signal.getsignal(signal.SIGINT) in (signal.SIG_DFL, signal.default_int_handler): # No custom signal handling was added, install our own signal.signal(signal.SIGINT, functools.partial(_handle_signals, client)) if signal.getsignal(signal.SIGTERM) is signal.SIG_DFL: # No custom signal handling was added, install our own signal.signal(signal.SIGTERM, functools.partial(_handle_signals, client)) def salt_master(): """ Start the salt master. """ import salt.cli.daemons # Fix for setuptools generated scripts, so that it will # work with multiprocessing fork emulation. # (see multiprocessing.forking.get_preparation_data()) if __name__ != "__main__": sys.modules["__main__"] = sys.modules[__name__] master = salt.cli.daemons.Master() master.start() def minion_process(): """ Start a minion process """ # Because the minion is going to start on a separate process, # salt._logging.in_mainprocess() will return False. # We'll just force it to return True for this particular case so # that proper logging can be set up. import salt._logging salt._logging.in_mainprocess.__pid__ = os.getpid() # Now the remaining required imports import salt.cli.daemons import salt.utils.platform # salt_minion spawns this function in a new process def handle_hup(manager, sig, frame): manager.minion.reload() lock = threading.RLock() def suicide_when_without_parent(parent_pid): """ Have the minion suicide if the parent process is gone NOTE: small race issue where the parent PID could be replace with another process with same PID! """ while lock.acquire(blocking=False): lock.release() time.sleep(5) try: # check pid alive (Unix only trick!) if os.getuid() == 0 and not salt.utils.platform.is_windows(): os.kill(parent_pid, 0) except OSError as exc: # forcibly exit, regular sys.exit raises an exception-- which # isn't sufficient in a thread log.error("Minion process encountered exception: %s", exc) os._exit(salt.defaults.exitcodes.EX_GENERIC) try: if not salt.utils.platform.is_windows(): thread = threading.Thread( target=suicide_when_without_parent, args=(os.getppid(),) ) thread.start() minion = salt.cli.daemons.Minion() signal.signal(signal.SIGHUP, functools.partial(handle_hup, minion)) minion.start() except (SaltClientError, SaltReqTimeoutError, SaltSystemExit) as exc: lock.acquire(blocking=True) log.warning( "Fatal functionality error caught by minion handler:\n", exc_info=True ) log.warning("** Restarting minion **") delay = 60 if minion is not None and hasattr(minion, "config"): delay = minion.config.get("random_reauth_delay", 60) delay = randint(1, delay) log.info("waiting random_reauth_delay %ss", delay) time.sleep(delay) sys.exit(salt.defaults.exitcodes.SALT_KEEPALIVE) finally: lock.acquire(blocking=True) def salt_minion(): """ Start the salt minion in a subprocess. Auto restart minion on error. """ import signal import salt.utils.debug import salt.utils.platform import salt.utils.process salt.utils.debug.enable_sigusr1_handler() salt.utils.process.notify_systemd() import multiprocessing import salt.cli.daemons # Fix for setuptools generated scripts, so that it will # work with multiprocessing fork emulation. # (see multiprocessing.forking.get_preparation_data()) if __name__ != "__main__": sys.modules["__main__"] = sys.modules[__name__] if "" in sys.path: sys.path.remove("") if salt.utils.platform.is_windows(): minion = salt.cli.daemons.Minion() minion.start() return if "--disable-keepalive" in sys.argv: sys.argv.remove("--disable-keepalive") minion = salt.cli.daemons.Minion() minion.start() return def escalate_signal_to_process( pid, signum, sigframe ): # pylint: disable=unused-argument """ Escalate the signal received to the multiprocessing process that is actually running the minion """ # escalate signal os.kill(pid, signum) # keep one minion subprocess running prev_sigint_handler = signal.getsignal(signal.SIGINT) prev_sigterm_handler = signal.getsignal(signal.SIGTERM) while True: try: process = multiprocessing.Process( target=minion_process, name="MinionKeepAlive" ) process.start() signal.signal( signal.SIGTERM, functools.partial(escalate_signal_to_process, process.pid), ) signal.signal( signal.SIGINT, functools.partial(escalate_signal_to_process, process.pid), ) signal.signal( signal.SIGHUP, functools.partial(escalate_signal_to_process, process.pid), ) except Exception: # pylint: disable=broad-except # if multiprocessing does not work minion = salt.cli.daemons.Minion() minion.start() break process.join() # Process exited or was terminated. Since we're going to try to restart # it, we MUST, reset signal handling to the previous handlers signal.signal(signal.SIGINT, prev_sigint_handler) signal.signal(signal.SIGTERM, prev_sigterm_handler) if not process.exitcode == salt.defaults.exitcodes.SALT_KEEPALIVE: sys.exit(process.exitcode) # ontop of the random_reauth_delay already preformed # delay extra to reduce flooding and free resources # NOTE: values are static but should be fine. time.sleep(2 + randint(1, 10)) # need to reset logging because new minion objects # cause extra log handlers to accumulate rlogger = logging.getLogger() for handler in rlogger.handlers: rlogger.removeHandler(handler) logging.basicConfig() def proxy_minion_process(queue): """ Start a proxy minion process """ import salt.cli.daemons import salt.utils.platform # salt_minion spawns this function in a new process lock = threading.RLock() def suicide_when_without_parent(parent_pid): """ Have the minion suicide if the parent process is gone NOTE: there is a small race issue where the parent PID could be replace with another process with the same PID! """ while lock.acquire(blocking=False): lock.release() time.sleep(5) try: # check pid alive (Unix only trick!) os.kill(parent_pid, 0) except OSError: # forcibly exit, regular sys.exit raises an exception-- which # isn't sufficient in a thread os._exit(999) try: if not salt.utils.platform.is_windows(): thread = threading.Thread( target=suicide_when_without_parent, args=(os.getppid(),) ) thread.start() restart = False proxyminion = None status = salt.defaults.exitcodes.EX_OK proxyminion = salt.cli.daemons.ProxyMinion() proxyminion.start() # pylint: disable=broad-except except ( Exception, SaltClientError, SaltReqTimeoutError, SaltSystemExit, ) as exc: # pylint: enable=broad-except log.error("Proxy Minion failed to start: ", exc_info=True) restart = True # status is superfluous since the process will be restarted status = salt.defaults.exitcodes.SALT_KEEPALIVE except SystemExit as exc: restart = False status = exc.code finally: lock.acquire(blocking=True) if restart is True: log.warning("** Restarting proxy minion **") delay = 60 if proxyminion is not None: if hasattr(proxyminion, "config"): delay = proxyminion.config.get("random_reauth_delay", 60) random_delay = randint(1, delay) log.info("Sleeping random_reauth_delay of %s seconds", random_delay) # preform delay after minion resources have been cleaned queue.put(random_delay) else: queue.put(0) sys.exit(status) def salt_proxy(): """ Start a proxy minion. """ import multiprocessing import salt.cli.daemons import salt.utils.platform if "" in sys.path: sys.path.remove("") if salt.utils.platform.is_windows(): proxyminion = salt.cli.daemons.ProxyMinion() proxyminion.start() return if "--disable-keepalive" in sys.argv: sys.argv.remove("--disable-keepalive") proxyminion = salt.cli.daemons.ProxyMinion() proxyminion.start() return # keep one minion subprocess running while True: try: queue = multiprocessing.Queue() except Exception: # pylint: disable=broad-except # This breaks in containers proxyminion = salt.cli.daemons.ProxyMinion() proxyminion.start() return process = multiprocessing.Process( target=proxy_minion_process, args=(queue,), name="ProxyMinion" ) process.start() try: process.join() try: restart_delay = queue.get(block=False) except Exception: # pylint: disable=broad-except if process.exitcode == 0: # Minion process ended naturally, Ctrl+C or --version break restart_delay = 60 if restart_delay == 0: # Minion process ended naturally, Ctrl+C, --version, etc. sys.exit(process.exitcode) # delay restart to reduce flooding and allow network resources to close time.sleep(restart_delay) except KeyboardInterrupt: break # need to reset logging because new minion objects # cause extra log handlers to accumulate rlogger = logging.getLogger() for handler in rlogger.handlers: rlogger.removeHandler(handler) logging.basicConfig() def salt_syndic(): """ Start the salt syndic. """ import salt.utils.process salt.utils.process.notify_systemd() import salt.cli.daemons pid = os.getpid() try: syndic = salt.cli.daemons.Syndic() syndic.start() except KeyboardInterrupt: os.kill(pid, 15) def salt_key(): """ Manage the authentication keys with salt-key. """ import salt.cli.key try: client = salt.cli.key.SaltKey() _install_signal_handlers(client) client.run() except Exception as err: # pylint: disable=broad-except sys.stderr.write(f"Error: {err}\n") def salt_cp(): """ Publish commands to the salt system from the command line on the master. """ import salt.cli.cp client = salt.cli.cp.SaltCPCli() _install_signal_handlers(client) client.run() def salt_call(): """ Directly call a salt command in the modules, does not require a running salt minion to run. """ import salt.cli.call if "" in sys.path: sys.path.remove("") client = salt.cli.call.SaltCall() _install_signal_handlers(client) client.run() def salt_run(): """ Execute a salt convenience routine. """ import salt.cli.run if "" in sys.path: sys.path.remove("") client = salt.cli.run.SaltRun() _install_signal_handlers(client) client.run() def salt_ssh(): """ Execute the salt-ssh system """ import salt.cli.ssh if "" in sys.path: sys.path.remove("") try: client = salt.cli.ssh.SaltSSH() _install_signal_handlers(client) client.run() except SaltClientError as err: print(str(err), file=sys.stderr, flush=True) try: if client.options.hard_crash: trace = traceback.format_exc() log.error(trace) except (AttributeError, KeyError): pass sys.exit(salt.defaults.exitcodes.EX_GENERIC) def salt_cloud(): """ The main function for salt-cloud """ try: # Late-imports for CLI performance import salt.cloud import salt.cloud.cli except ImportError as e: # No salt cloud on Windows import salt.defaults.exitcodes log.error("Error importing salt cloud: %s", e) print("salt-cloud is not available in this system") sys.exit(salt.defaults.exitcodes.EX_UNAVAILABLE) if "" in sys.path: sys.path.remove("") client = salt.cloud.cli.SaltCloud() _install_signal_handlers(client) client.run() def salt_api(): """ The main function for salt-api """ import salt.utils.process salt.utils.process.notify_systemd() import salt.cli.api sapi = salt.cli.api.SaltAPI() # pylint: disable=E1120 sapi.start() def salt_main(): """ Publish commands to the salt system from the command line on the master. """ import salt.cli.salt if "" in sys.path: sys.path.remove("") client = salt.cli.salt.SaltCMD() _install_signal_handlers(client) client.run() def salt_spm(): """ The main function for spm, the Salt Package Manager .. versionadded:: 2015.8.0 """ import salt.cli.spm spm = salt.cli.spm.SPM() # pylint: disable=E1120 spm.run() def salt_extend(extension, name, description, salt_dir, merge): """ Quickstart for developing on the saltstack installation .. versionadded:: 2016.11.0 """ import salt.utils.extend salt.utils.extend.run( extension=extension, name=name, description=description, salt_dir=salt_dir, merge=merge, ) def salt_unity(): """ Change the args and redirect to another salt script """ avail = [] for fun in dir(sys.modules[__name__]): if fun.startswith("salt"): avail.append(fun[5:]) if len(sys.argv) < 2: msg = "Must pass in a salt command, available commands are:" for cmd in avail: msg += f"\n{cmd}" print(msg) sys.exit(1) cmd = sys.argv[1] if cmd not in avail: # Fall back to the salt command sys.argv[0] = "salt" s_fun = salt_main else: sys.argv[0] = f"salt-{cmd}" sys.argv.pop(1) s_fun = getattr(sys.modules[__name__], f"salt_{cmd}") s_fun() def _pip_args(args, target): new_args = args[:] target_in_args = False for arg in args: if "--target" in arg: target_in_args = True if "install" in args and not target_in_args: new_args.append(f"--target={target}") return new_args def _pip_environment(env, extras): new_env = env.copy() if "PYTHONPATH" in env: new_env["PYTHONPATH"] = f"{extras}{os.pathsep}{env['PYTHONPATH']}" else: new_env["PYTHONPATH"] = extras return new_env def _get_onedir_env_path(): # This function only exists to simplify testing. with contextlib.suppress(AttributeError): return sys.RELENV return None def salt_pip(): """ Proxy to current python's pip """ relenv_path = _get_onedir_env_path() if relenv_path is None: print( "'salt-pip' is only meant to be used from a Salt onedir. You probably " "want to use the system 'pip` binary.", file=sys.stderr, flush=True, ) sys.exit(salt.defaults.exitcodes.EX_GENERIC) else: extras = str(relenv_path / "extras-{}.{}".format(*sys.version_info)) env = _pip_environment(os.environ.copy(), extras) args = _pip_args(sys.argv[1:], extras) command = [ sys.executable, "-m", "pip", ] + _pip_args(sys.argv[1:], extras) ret = subprocess.run(command, shell=False, check=False, env=env) sys.exit(ret.returncode)