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/runners
Viewing File: /opt/saltstack/salt/lib/python3.10/site-packages/salt/runners/manage.py
""" General management functions for salt, tools like seeing what hosts are up and what hosts are down """ import logging import operator import os import re import subprocess import tempfile import time import urllib.request import uuid import salt.client import salt.client.ssh import salt.key import salt.utils.compat import salt.utils.files import salt.utils.minions import salt.utils.path import salt.utils.versions import salt.version import salt.wheel from salt.exceptions import SaltClientError, SaltSystemExit FINGERPRINT_REGEX = re.compile(r"^([a-f0-9]{2}:){15}([a-f0-9]{2})$") log = logging.getLogger(__name__) def _ping(tgt, tgt_type, timeout, gather_job_timeout): with salt.client.get_local_client(__opts__["conf_file"]) as client: pub_data = client.run_job( tgt, "test.ping", (), tgt_type, "", timeout, "", listen=True ) if not pub_data: return pub_data log.debug( "manage runner will ping the following minion(s): %s", ", ".join(sorted(pub_data["minions"])), ) returned = set() for fn_ret in client.get_cli_event_returns( pub_data["jid"], pub_data["minions"], client._get_timeout(timeout), tgt, tgt_type, gather_job_timeout=gather_job_timeout, ): if fn_ret: for mid, _ in fn_ret.items(): log.debug("minion '%s' returned from ping", mid) returned.add(mid) not_returned = sorted(set(pub_data["minions"]) - returned) returned = sorted(returned) return returned, not_returned def status( output=True, tgt="*", tgt_type="glob", timeout=None, gather_job_timeout=None ): """ .. versionchanged:: 2017.7.0 The ``expr_form`` argument has been renamed to ``tgt_type``, earlier releases must use ``expr_form``. Print the status of all known salt minions CLI Example: .. code-block:: bash salt-run manage.status salt-run manage.status tgt="webservers" tgt_type="nodegroup" salt-run manage.status timeout=5 gather_job_timeout=10 """ ret = {} if not timeout: timeout = __opts__["timeout"] if not gather_job_timeout: gather_job_timeout = __opts__["gather_job_timeout"] res = _ping(tgt, tgt_type, timeout, gather_job_timeout) ret["up"], ret["down"] = ([], []) if not res else res return ret def key_regen(): """ This routine is used to regenerate all keys in an environment. This is invasive! ALL KEYS IN THE SALT ENVIRONMENT WILL BE REGENERATED!! The key_regen routine sends a command out to minions to revoke the master key and remove all minion keys, it then removes all keys from the master and prompts the user to restart the master. The minions will all reconnect and keys will be placed in pending. After the master is restarted and minion keys are in the pending directory execute a salt-key -A command to accept the regenerated minion keys. The master *must* be restarted within 60 seconds of running this command or the minions will think there is something wrong with the keys and abort. Only Execute this runner after upgrading minions and master to 0.15.1 or higher! CLI Example: .. code-block:: bash salt-run manage.key_regen """ client = salt.client.get_local_client(__opts__["conf_file"]) try: client.cmd("*", "saltutil.regen_keys") except SaltClientError as client_error: print(client_error) return False for root, _, files in salt.utils.path.os_walk(__opts__["pki_dir"]): for fn_ in files: path = os.path.join(root, fn_) try: os.remove(path) except OSError: pass msg = ( "The minion and master keys have been deleted. Restart the Salt\n" "Master within the next 60 seconds!!!\n\n" "Wait for the minions to reconnect. Once the minions reconnect\n" "the new keys will appear in pending and will need to be re-\n" "accepted by running:\n" " salt-key -A\n\n" "Be advised that minions not currently connected to the master\n" "will not be able to reconnect and may require manual\n" "regeneration via a local call to\n" " salt-call saltutil.regen_keys" ) return msg def down( removekeys=False, tgt="*", tgt_type="glob", timeout=None, gather_job_timeout=None ): """ .. versionchanged:: 2017.7.0 The ``expr_form`` argument has been renamed to ``tgt_type``, earlier releases must use ``expr_form``. Print a list of all the down or unresponsive salt minions Optionally remove keys of down minions CLI Example: .. code-block:: bash salt-run manage.down salt-run manage.down removekeys=True salt-run manage.down tgt="webservers" tgt_type="nodegroup" """ ret = status( output=False, tgt=tgt, tgt_type=tgt_type, timeout=timeout, gather_job_timeout=gather_job_timeout, ).get("down", []) for minion in ret: if removekeys: wheel = salt.wheel.Wheel(__opts__) wheel.call_func("key.delete", match=minion) return ret def up( tgt="*", tgt_type="glob", timeout=None, gather_job_timeout=None ): # pylint: disable=C0103 """ .. versionchanged:: 2017.7.0 The ``expr_form`` argument has been renamed to ``tgt_type``, earlier releases must use ``expr_form``. Print a list of all of the minions that are up CLI Example: .. code-block:: bash salt-run manage.up salt-run manage.up tgt="webservers" tgt_type="nodegroup" salt-run manage.up timeout=5 gather_job_timeout=10 """ ret = status( output=False, tgt=tgt, tgt_type=tgt_type, timeout=timeout, gather_job_timeout=gather_job_timeout, ).get("up", []) return ret def list_state(subset=None, show_ip=False): """ .. versionadded:: 2015.8.0 .. versionchanged:: 2019.2.0 Print a list of all minions that are up according to Salt's presence detection (no commands will be sent to minions) subset : None Pass in a list of minion ids. show_ip : False Also show the IP address each minion is connecting from. CLI Example: .. code-block:: bash salt-run manage.list_state """ # Always return 'present' for 0MQ for now # TODO: implement other states support for 0MQ ckminions = salt.utils.minions.CkMinions(__opts__) minions = ckminions.connected_ids(show_ip=show_ip, subset=subset) connected = dict(minions) if show_ip else sorted(minions) return connected def list_not_state(subset=None, show_ip=False): """ .. versionadded:: 2015.8.0 .. versionchanged:: 2019.2.0 Print a list of all minions that are NOT up according to Salt's presence detection (no commands will be sent to minions) subset : None Pass in a list of minion ids. show_ip : False Also show the IP address each minion is connecting from. CLI Example: .. code-block:: bash salt-run manage.list_not_state """ connected = list_state(subset=None, show_ip=show_ip) with salt.key.get_key(__opts__) as key: keys = key.list_keys() not_connected = [] for minion in keys[key.ACC]: if minion not in connected and (subset is None or minion in subset): not_connected.append(minion) return not_connected def present(subset=None, show_ip=False): """ .. versionchanged:: 2019.2.0 Print a list of all minions that are up according to Salt's presence detection (no commands will be sent to minions) subset : None Pass in a list of minion ids. show_ip : False Also show the IP address each minion is connecting from. CLI Example: .. code-block:: bash salt-run manage.present """ return list_state(subset=subset, show_ip=show_ip) def not_present(subset=None, show_ip=False): """ .. versionadded:: 2015.5.0 .. versionchanged:: 2019.2.0 Print a list of all minions that are NOT up according to Salt's presence detection (no commands will be sent) subset : None Pass in a list of minion ids. show_ip : False Also show the IP address each minion is connecting from. CLI Example: .. code-block:: bash salt-run manage.not_present """ return list_not_state(subset=subset, show_ip=show_ip) def joined(subset=None, show_ip=False): """ .. versionadded:: 2015.8.0 .. versionchanged:: 2019.2.0 Print a list of all minions that are up according to Salt's presence detection (no commands will be sent to minions) subset : None Pass in a list of minion ids. show_ip : False Also show the IP address each minion is connecting from. CLI Example: .. code-block:: bash salt-run manage.joined """ return list_state(subset=subset, show_ip=show_ip) def not_joined(subset=None, show_ip=False): """ .. versionadded:: 2015.8.0 .. versionchanged:: 2019.2.0 Print a list of all minions that are NOT up according to Salt's presence detection (no commands will be sent) subset : None Pass in a list of minion ids. show_ip : False Also show the IP address each minion is connecting from. CLI Example: .. code-block:: bash salt-run manage.not_joined """ return list_not_state(subset=subset, show_ip=show_ip) def allowed(subset=None, show_ip=False): """ .. versionadded:: 2015.8.0 .. versionchanged:: 2019.2.0 Print a list of all minions that are up according to Salt's presence detection (no commands will be sent to minions) subset : None Pass in a list of minion ids. show_ip : False Also show the IP address each minion is connecting from. CLI Example: .. code-block:: bash salt-run manage.allowed """ return list_state(subset=subset, show_ip=show_ip) def not_allowed(subset=None, show_ip=False): """ .. versionadded:: 2015.8.0 .. versionchanged:: 2019.2.0 Print a list of all minions that are NOT up according to Salt's presence detection (no commands will be sent) subset : None Pass in a list of minion ids. show_ip : False Also show the IP address each minion is connecting from. CLI Example: .. code-block:: bash salt-run manage.not_allowed """ return list_not_state(subset=subset, show_ip=show_ip) def alived(subset=None, show_ip=False): """ .. versionadded:: 2015.8.0 .. versionchanged:: 2019.2.0 Print a list of all minions that are up according to Salt's presence detection (no commands will be sent to minions) subset : None Pass in a list of minion ids. show_ip : False Also show the IP address each minion is connecting from. CLI Example: .. code-block:: bash salt-run manage.alived """ return list_state(subset=subset, show_ip=show_ip) def not_alived(subset=None, show_ip=False): """ .. versionadded:: 2015.8.0 .. versionchanged:: 2019.2.0 Print a list of all minions that are NOT up according to Salt's presence detection (no commands will be sent) subset : None Pass in a list of minion ids. show_ip : False Also show the IP address each minion is connecting from. CLI Example: .. code-block:: bash salt-run manage.not_alived """ return list_not_state(subset=subset, show_ip=show_ip) def reaped(subset=None, show_ip=False): """ .. versionadded:: 2015.8.0 .. versionchanged:: 2019.2.0 Print a list of all minions that are up according to Salt's presence detection (no commands will be sent to minions) subset : None Pass in a list of minion ids. show_ip : False Also show the IP address each minion is connecting from. CLI Example: .. code-block:: bash salt-run manage.reaped """ return list_state(subset=subset, show_ip=show_ip) def not_reaped(subset=None, show_ip=False): """ .. versionadded:: 2015.8.0 .. versionchanged:: 2019.2.0 Print a list of all minions that are NOT up according to Salt's presence detection (no commands will be sent) subset : None Pass in a list of minion ids. show_ip : False Also show the IP address each minion is connecting from. CLI Example: .. code-block:: bash salt-run manage.not_reaped """ return list_not_state(subset=subset, show_ip=show_ip) def safe_accept(target, tgt_type="glob"): """ .. versionchanged:: 2017.7.0 The ``expr_form`` argument has been renamed to ``tgt_type``, earlier releases must use ``expr_form``. Accept a minion's public key after checking the fingerprint over salt-ssh CLI Example: .. code-block:: bash salt-run manage.safe_accept my_minion salt-run manage.safe_accept minion1,minion2 tgt_type=list """ ssh_client = salt.client.ssh.client.SSHClient() ret = ssh_client.cmd(target, "key.finger", tgt_type=tgt_type) failures = {} for minion, finger in ret.items(): if not FINGERPRINT_REGEX.match(finger): failures[minion] = finger else: with salt.key.Key(__opts__) as salt_key: fingerprints = salt_key.finger(minion) accepted = fingerprints.get("minions", {}) pending = fingerprints.get("minions_pre", {}) if minion in accepted: del ret[minion] continue elif minion not in pending: failures[minion] = f"Minion key {minion} not found by salt-key" elif pending[minion] != finger: failures[minion] = ( "Minion key {} does not match the key in salt-key: {}".format( finger, pending[minion] ) ) else: subprocess.call(["salt-key", "-qya", minion]) if minion in failures: del ret[minion] if failures: print("safe_accept failed on the following minions:") for minion, message in failures.items(): print(minion) print("-" * len(minion)) print(message) print("") __jid_event__.fire_event({"message": f"Accepted {len(ret):d} keys"}, "progress") return ret, failures def versions(): """ Check the version of active minions CLI Example: .. code-block:: bash salt-run manage.versions """ ret = {} client = salt.client.get_local_client(__opts__["conf_file"]) try: minions = client.cmd( "*", "test.version", full_return=True, timeout=__opts__["timeout"] ) except SaltClientError as client_error: print(client_error) return ret labels = { -2: "Minion offline", -1: "Minion requires update", 0: "Up to date", 1: "Minion newer than master", 2: "Master", } version_status = {} master_version = salt.version.__saltstack_version__ for minion in minions: if not minions[minion] or minions[minion]["retcode"]: minion_version = False ver_diff = -2 else: minion_version = salt.version.SaltStackVersion.parse(minions[minion]["ret"]) ver_diff = salt.utils.compat.cmp(minion_version, master_version) if ver_diff not in version_status: version_status[ver_diff] = {} if minion_version: version_status[ver_diff][minion] = minion_version.string else: version_status[ver_diff][minion] = minion_version # Add version of Master to output version_status[2] = master_version.string for key in version_status: if key == 2: ret[labels[key]] = version_status[2] else: for minion in sorted(version_status[key]): ret.setdefault(labels[key], {})[minion] = version_status[key][minion] return ret def bootstrap( version="develop", script="https://github.com/saltstack/salt-bootstrap/releases/latest/download/bootstrap-salt.sh", hosts="", script_args="", roster="flat", ssh_user=None, ssh_password=None, ssh_priv_key=None, tmp_dir="/tmp/.bootstrap", http_backend="tornado", ): """ Bootstrap minions with salt-bootstrap version : develop Git tag of version to install script : https://github.com/saltstack/salt-bootstrap/releases/latest/download/bootstrap-salt.sh URL containing the script to execute hosts Comma-separated hosts [example: hosts='host1.local,host2.local']. These hosts need to exist in the specified roster. script_args Any additional arguments that you want to pass to the script. .. versionadded:: 2016.11.0 roster : flat The roster to use for Salt SSH. More information about roster files can be found in :ref:`Salt's Roster Documentation <ssh-roster>`. A full list of roster types, see the :ref:`builtin roster modules <all-salt.roster>` documentation. .. versionadded:: 2016.11.0 ssh_user If ``user`` isn't found in the ``roster``, a default SSH user can be set here. Keep in mind that ``ssh_user`` will not override the roster ``user`` value if it is already defined. .. versionadded:: 2016.11.0 ssh_password If ``passwd`` isn't found in the ``roster``, a default SSH password can be set here. Keep in mind that ``ssh_password`` will not override the roster ``passwd`` value if it is already defined. .. versionadded:: 2016.11.0 ssh_privkey If ``priv`` isn't found in the ``roster``, a default SSH private key can be set here. Keep in mind that ``ssh_password`` will not override the roster ``passwd`` value if it is already defined. .. versionadded:: 2016.11.0 tmp_dir : /tmp/.bootstrap The temporary directory to download the bootstrap script in. This directory will have ``-<uuid4>`` appended to it. For example: ``/tmp/.bootstrap-a19a728e-d40a-4801-aba9-d00655c143a7/`` .. versionadded:: 2016.11.0 http_backend : tornado The backend library to use to download the script. If you need to use a ``file:///`` URL, then you should set this to ``urllib2``. .. versionadded:: 2016.11.0 CLI Example: .. code-block:: bash salt-run manage.bootstrap hosts='host1,host2' salt-run manage.bootstrap hosts='host1,host2' version='v3006.2' salt-run manage.bootstrap hosts='host1,host2' version='v3006.2' script='https://github.com/saltstack/salt-bootstrap/develop' """ client_opts = __opts__.copy() if roster is not None: client_opts["roster"] = roster if ssh_user is not None: client_opts["ssh_user"] = ssh_user if ssh_password is not None: client_opts["ssh_passwd"] = ssh_password if ssh_priv_key is not None: client_opts["ssh_priv"] = ssh_priv_key for host in hosts.split(","): client_opts["tgt"] = host client_opts["selected_target_option"] = "glob" tmp_dir = "{}-{}/".format(tmp_dir.rstrip("/"), uuid.uuid4()) deploy_command = os.path.join(tmp_dir, "deploy.sh") try: client_opts["argv"] = ["file.makedirs", tmp_dir, "mode=0700"] salt.client.ssh.SSH(client_opts).run() client_opts["argv"] = [ "http.query", script, f"backend={http_backend}", f"text_out={deploy_command}", ] salt.client.ssh.SSH(client_opts).run() client_opts["argv"] = [ "cmd.run", " ".join(["sh", deploy_command, script_args]), "python_shell=False", ] salt.client.ssh.SSH(client_opts).run() client_opts["argv"] = ["file.remove", tmp_dir] salt.client.ssh.SSH(client_opts).run() except SaltSystemExit as exc: log.error(str(exc)) def bootstrap_psexec( hosts="", master=None, version=None, arch="win32", installer_url=None, username=None, password=None, ): """ Bootstrap Windows minions via PsExec. hosts Comma separated list of hosts to deploy the Windows Salt minion. master Address of the Salt master passed as an argument to the installer. version Point release of installer to download. Defaults to the most recent. arch Architecture of installer to download. Defaults to win32. installer_url URL of minion installer executable. Defaults to the latest version from https://packages.broadcom.com/artifactory/saltproject-generic/windows/ username Optional user name for login on remote computer. password Password for optional username. If omitted, PsExec will prompt for one to be entered for each host. CLI Example: .. code-block:: bash salt-run manage.bootstrap_psexec hosts='host1,host2' salt-run manage.bootstrap_psexec hosts='host1,host2' version='0.17' username='DOMAIN\\Administrator' salt-run manage.bootstrap_psexec hosts='host1,host2' installer_url='http://exampledomain/salt-installer.exe' """ # TODO: Need to make this gets the latest version from the new repo location # TODO: Similar to tests/support/win_installer.py # TODO: Maybe need to move that ^^^^ to a salt util if not installer_url: base_url = "https://repo.saltproject.io/windows/" source = urllib.request.urlopen(base_url).read() salty_rx = re.compile( '>(Salt-Minion-(.+?)-(.+)-Setup.exe)</a></td><td align="right">(.*?)\\s*<' ) source_list = sorted( ( [path, ver, plat, time.strptime(date, "%d-%b-%Y %H:%M")] for path, ver, plat, date in salty_rx.findall(source) ), key=operator.itemgetter(3), reverse=True, ) if version: source_list = [s for s in source_list if s[1] == version] if arch: source_list = [s for s in source_list if s[2] == arch] if not source_list: return -1 version = source_list[0][1] arch = source_list[0][2] installer_url = base_url + source_list[0][0] # It's no secret that Windows is notoriously command-line hostile. # Win 7 and newer can use PowerShell out of the box, but to reach # all those XP and 2K3 machines we must suppress our gag-reflex # and use VB! # The following script was borrowed from an informative article about # downloading exploit payloads for malware. Nope, no irony here. # http://www.greyhathacker.net/?p=500 vb_script = """strFileURL = "{0}" strHDLocation = "{1}" Set objXMLHTTP = CreateObject("MSXML2.XMLHTTP") objXMLHTTP.open "GET", strFileURL, false objXMLHTTP.send() If objXMLHTTP.Status = 200 Then Set objADOStream = CreateObject("ADODB.Stream") objADOStream.Open objADOStream.Type = 1 objADOStream.Write objXMLHTTP.ResponseBody objADOStream.Position = 0 objADOStream.SaveToFile strHDLocation objADOStream.Close Set objADOStream = Nothing End if Set objXMLHTTP = Nothing Set objShell = CreateObject("WScript.Shell") objShell.Exec("{1}{2}")""" vb_saltexec = "saltinstall.exe" vb_saltexec_args = " /S /minion-name=%COMPUTERNAME%" if master: vb_saltexec_args += f" /master={master}" # One further thing we need to do; the Windows Salt minion is pretty # self-contained, except for the Microsoft Visual C++ 2008 runtime. # It's tiny, so the bootstrap will attempt a silent install. vb_vcrunexec = "vcredist.exe" if arch == "AMD64": vb_vcrun = vb_script.format( "http://download.microsoft.com/download/d/2/4/d242c3fb-da5a-4542-ad66-f9661d0a8d19/vcredist_x64.exe", vb_vcrunexec, " /q", ) else: vb_vcrun = vb_script.format( "http://download.microsoft.com/download/d/d/9/dd9a82d0-52ef-40db-8dab-795376989c03/vcredist_x86.exe", vb_vcrunexec, " /q", ) vb_salt = vb_script.format(installer_url, vb_saltexec, vb_saltexec_args) # PsExec doesn't like extra long arguments; save the instructions as a batch # file so we can fire it over for execution. # First off, change to the local temp directory, stop salt-minion (if # running), and remove the master's public key. # This is to accommodate for reinstalling Salt over an old or broken build, # e.g. if the master address is changed, the salt-minion process will fail # to authenticate and quit; which means infinite restarts under Windows. batch = ( "cd /d %TEMP%\nnet stop salt-minion\ndel" " c:\\salt\\conf\\pki\\minion\\minion_master.pub\n" ) # Speaking of command-line hostile, cscript only supports reading a script # from a file. Glue it together line by line. for x, y in ((vb_vcrunexec, vb_vcrun), (vb_saltexec, vb_salt)): vb_lines = y.split("\n") batch += ( "\ndel " + x + "\n@echo " + vb_lines[0] + " >" + x + ".vbs\n@echo " + (" >>" + x + ".vbs\n@echo ").join(vb_lines[1:]) + " >>" + x + ".vbs\ncscript.exe /NoLogo " + x + ".vbs" ) batch_path = tempfile.mkstemp(suffix=".bat")[1] with salt.utils.files.fopen(batch_path, "wb") as batch_file: batch_file.write(batch) for host in hosts.split(","): argv = ["psexec", "\\\\" + host] if username: argv += ["-u", username] if password: argv += ["-p", password] argv += ["-h", "-c", batch_path] subprocess.call(argv)