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/jobs.py
""" A convenience system to manage jobs, both active and already run """ import fnmatch import logging import os import salt.client import salt.minion import salt.payload import salt.returners import salt.utils.args import salt.utils.files import salt.utils.jid import salt.utils.master from salt.exceptions import SaltClientError try: import dateutil.parser as dateutil_parser DATEUTIL_SUPPORT = True except ImportError: DATEUTIL_SUPPORT = False log = logging.getLogger(__name__) def master(): """ Return the actively executing runners for the master CLI Example: .. code-block:: bash salt-run jobs.master """ return salt.utils.master.get_running_jobs(__opts__) def active(display_progress=False): """ Return a report on all actively running jobs from a job id centric perspective CLI Example: .. code-block:: bash salt-run jobs.active """ ret = {} with salt.client.get_local_client(__opts__["conf_file"]) as client: try: active_ = client.cmd("*", "saltutil.running", timeout=__opts__["timeout"]) except SaltClientError as client_error: print(client_error) return ret if display_progress: __jid_event__.fire_event( { "message": "Attempting to contact minions: {}".format( list(active_.keys()) ) }, "progress", ) for minion, data in active_.items(): if display_progress: __jid_event__.fire_event( {"message": f"Received reply from minion {minion}"}, "progress" ) if not isinstance(data, list): continue for job in data: if not job["jid"] in ret: ret[job["jid"]] = _format_jid_instance(job["jid"], job) ret[job["jid"]].update( {"Running": [{minion: job.get("pid", None)}], "Returned": []} ) else: ret[job["jid"]]["Running"].append({minion: job["pid"]}) mminion = salt.minion.MasterMinion(__opts__) for jid in ret: returner = _get_returner( (__opts__["ext_job_cache"], __opts__["master_job_cache"]) ) data = mminion.returners[f"{returner}.get_jid"](jid) if data: for minion in data: if minion not in ret[jid]["Returned"]: ret[jid]["Returned"].append(minion) return ret def lookup_jid( jid, ext_source=None, returned=True, missing=False, display_progress=False ): """ Return the printout from a previously executed job jid The jid to look up. ext_source The external job cache to use. Default: `None`. returned : True If ``True``, include the minions that did return from the command. .. versionadded:: 2015.8.0 missing : False If ``True``, include the minions that did *not* return from the command. display_progress : False If ``True``, fire progress events. .. versionadded:: 2015.5.0 CLI Example: .. code-block:: bash salt-run jobs.lookup_jid 20130916125524463507 salt-run jobs.lookup_jid 20130916125524463507 --out=highstate """ ret = {} mminion = salt.minion.MasterMinion(__opts__) returner = _get_returner( (__opts__["ext_job_cache"], ext_source, __opts__["master_job_cache"]) ) try: data = list_job(jid, ext_source=ext_source, display_progress=display_progress) except TypeError: return "Requested returner could not be loaded. No JIDs could be retrieved." targeted_minions = data.get("Minions", []) returns = data.get("Result", {}) if returns: for minion in returns: if display_progress: __jid_event__.fire_event({"message": minion}, "progress") if "return" in returns[minion]: if returned: ret[minion] = returns[minion].get("return") else: if returned: ret[minion] = returns[minion].get("return") if missing: for minion_id in (x for x in targeted_minions if x not in returns): ret[minion_id] = "Minion did not return" # We need to check to see if the 'out' key is present and use it to specify # the correct outputter, so we get highstate output for highstate runs. try: # Check if the return data has an 'out' key. We'll use that as the # outputter in the absence of one being passed on the CLI. outputter = returns[next(iter(returns))].get("out") except (StopIteration, AttributeError): outputter = None if outputter: return {"outputter": outputter, "data": ret} else: return ret def list_job(jid, ext_source=None, display_progress=False): """ List a specific job given by its jid ext_source If provided, specifies which external job cache to use. display_progress : False If ``True``, fire progress events. .. versionadded:: 2015.8.8 CLI Example: .. code-block:: bash salt-run jobs.list_job 20130916125524463507 salt-run jobs.list_job 20130916125524463507 --out=pprint """ ret = {"jid": jid} mminion = salt.minion.MasterMinion(__opts__) returner = _get_returner( (__opts__["ext_job_cache"], ext_source, __opts__["master_job_cache"]) ) if display_progress: __jid_event__.fire_event( {"message": f"Querying returner: {returner}"}, "progress" ) job = mminion.returners[f"{returner}.get_load"](jid) ret.update(_format_jid_instance(jid, job)) ret["Result"] = mminion.returners[f"{returner}.get_jid"](jid) fstr = "{}.get_endtime".format(__opts__["master_job_cache"]) if __opts__.get("job_cache_store_endtime") and fstr in mminion.returners: endtime = mminion.returners[fstr](jid) if endtime: ret["EndTime"] = endtime return ret def list_jobs( ext_source=None, outputter=None, search_metadata=None, search_function=None, search_target=None, start_time=None, end_time=None, display_progress=False, ): """ List all detectable jobs and associated functions ext_source If provided, specifies which external job cache to use. **FILTER OPTIONS** .. note:: If more than one of the below options are used, only jobs which match *all* of the filters will be returned. search_metadata Specify a dictionary to match to the job's metadata. If any of the key-value pairs in this dictionary match, the job will be returned. Example: .. code-block:: bash salt-run jobs.list_jobs search_metadata='{"foo": "bar", "baz": "qux"}' search_function Can be passed as a string or a list. Returns jobs which match the specified function. Globbing is allowed. Example: .. code-block:: bash salt-run jobs.list_jobs search_function='test.*' salt-run jobs.list_jobs search_function='["test.*", "pkg.install"]' .. versionchanged:: 2015.8.8 Multiple targets can now also be passed as a comma-separated list. For example: .. code-block:: bash salt-run jobs.list_jobs search_function='test.*,pkg.install' search_target Can be passed as a string or a list. Returns jobs which match the specified minion name. Globbing is allowed. Example: .. code-block:: bash salt-run jobs.list_jobs search_target='*.mydomain.tld' salt-run jobs.list_jobs search_target='["db*", "myminion"]' .. versionchanged:: 2015.8.8 Multiple targets can now also be passed as a comma-separated list. For example: .. code-block:: bash salt-run jobs.list_jobs search_target='db*,myminion' start_time Accepts any timestamp supported by the dateutil_ Python module (if this module is not installed, this argument will be ignored). Returns jobs which started after this timestamp. end_time Accepts any timestamp supported by the dateutil_ Python module (if this module is not installed, this argument will be ignored). Returns jobs which started before this timestamp. .. _dateutil: https://pypi.python.org/pypi/python-dateutil CLI Example: .. code-block:: bash salt-run jobs.list_jobs salt-run jobs.list_jobs search_function='test.*' search_target='localhost' search_metadata='{"bar": "foo"}' salt-run jobs.list_jobs start_time='2015, Mar 16 19:00' end_time='2015, Mar 18 22:00' """ returner = _get_returner( (__opts__["ext_job_cache"], ext_source, __opts__["master_job_cache"]) ) if display_progress: __jid_event__.fire_event( {"message": f"Querying returner {returner} for jobs."}, "progress" ) mminion = salt.minion.MasterMinion(__opts__) ret = mminion.returners[f"{returner}.get_jids"]() mret = {} for item in ret: _match = True if search_metadata: _match = False if "Metadata" in ret[item]: if isinstance(search_metadata, dict): for key in search_metadata: if key in ret[item]["Metadata"]: if ret[item]["Metadata"][key] == search_metadata[key]: _match = True else: log.info( "The search_metadata parameter must be specified" " as a dictionary. Ignoring." ) if search_target and _match: _match = False if "Target" in ret[item]: targets = ret[item]["Target"] if isinstance(targets, str): targets = [targets] for target in targets: for key in salt.utils.args.split_input(search_target): if fnmatch.fnmatch(target, key): _match = True if search_function and _match: _match = False if "Function" in ret[item]: for key in salt.utils.args.split_input(search_function): if fnmatch.fnmatch(ret[item]["Function"], key): _match = True if start_time and _match: _match = False if DATEUTIL_SUPPORT: parsed_start_time = dateutil_parser.parse(start_time) _start_time = dateutil_parser.parse(ret[item]["StartTime"]) if _start_time >= parsed_start_time: _match = True else: log.error( "'dateutil' library not available, skipping start_time comparison." ) if end_time and _match: _match = False if DATEUTIL_SUPPORT: parsed_end_time = dateutil_parser.parse(end_time) _start_time = dateutil_parser.parse(ret[item]["StartTime"]) if _start_time <= parsed_end_time: _match = True else: log.error( "'dateutil' library not available, skipping end_time comparison." ) if _match: mret[item] = ret[item] if outputter: return {"outputter": outputter, "data": mret} else: return mret def list_jobs_filter( count, filter_find_job=True, ext_source=None, outputter=None, display_progress=False ): """ List all detectable jobs and associated functions ext_source The external job cache to use. Default: `None`. CLI Example: .. code-block:: bash salt-run jobs.list_jobs_filter 50 salt-run jobs.list_jobs_filter 100 filter_find_job=False """ returner = _get_returner( (__opts__["ext_job_cache"], ext_source, __opts__["master_job_cache"]) ) if display_progress: __jid_event__.fire_event( {"message": f"Querying returner {returner} for jobs."}, "progress" ) mminion = salt.minion.MasterMinion(__opts__) fun = f"{returner}.get_jids_filter" if fun not in mminion.returners: raise NotImplementedError(f"'{fun}' returner function not implemented yet.") ret = mminion.returners[fun](count, filter_find_job) if outputter: return {"outputter": outputter, "data": ret} else: return ret def print_job(jid, ext_source=None): """ Print a specific job's detail given by its jid, including the return data. CLI Example: .. code-block:: bash salt-run jobs.print_job 20130916125524463507 """ ret = {} returner = _get_returner( (__opts__["ext_job_cache"], ext_source, __opts__["master_job_cache"]) ) mminion = salt.minion.MasterMinion(__opts__) try: job = mminion.returners[f"{returner}.get_load"](jid) ret[jid] = _format_jid_instance(jid, job) except TypeError: ret[jid]["Result"] = ( "Requested returner {} is not available. Jobs cannot be " "retrieved. Check master log for details.".format(returner) ) return ret ret[jid]["Result"] = mminion.returners[f"{returner}.get_jid"](jid) fstr = "{}.get_endtime".format(__opts__["master_job_cache"]) if __opts__.get("job_cache_store_endtime") and fstr in mminion.returners: endtime = mminion.returners[fstr](jid) if endtime: ret[jid]["EndTime"] = endtime return ret def exit_success(jid, ext_source=None): """ Check if a job has been executed and exit successfully jid The jid to look up. ext_source The external job cache to use. Default: `None`. CLI Example: .. code-block:: bash salt-run jobs.exit_success 20160520145827701627 """ ret = dict() data = list_job(jid, ext_source=ext_source) minions = data.get("Minions", []) result = data.get("Result", {}) for minion in minions: if minion in result and "return" in result[minion]: ret[minion] = True if result[minion]["return"] else False else: ret[minion] = False for minion in result: if "return" in result[minion] and result[minion]["return"]: ret[minion] = True return ret def last_run( ext_source=None, outputter=None, metadata=None, function=None, target=None, display_progress=False, ): """ .. versionadded:: 2015.8.0 List all detectable jobs and associated functions CLI Example: .. code-block:: bash salt-run jobs.last_run salt-run jobs.last_run target=nodename salt-run jobs.last_run function='cmd.run' salt-run jobs.last_run metadata="{'foo': 'bar'}" """ if metadata: if not isinstance(metadata, dict): log.info("The metadata parameter must be specified as a dictionary") return False _all_jobs = list_jobs( ext_source=ext_source, outputter=outputter, search_metadata=metadata, search_function=function, search_target=target, display_progress=display_progress, ) if _all_jobs: last_job = sorted(_all_jobs)[-1] return print_job(last_job, ext_source) else: return False def _get_returner(returner_types): """ Helper to iterate over returner_types and pick the first one """ for returner in returner_types: if returner and returner is not None: return returner def _format_job_instance(job): """ Helper to format a job instance """ if not job: ret = {"Error": "Cannot contact returner or no job with this jid"} return ret ret = { "Function": job.get("fun", "unknown-function"), "Arguments": list(job.get("arg", [])), # unlikely but safeguard from invalid returns "Target": job.get("tgt", "unknown-target"), "Target-type": job.get("tgt_type", "list"), "User": job.get("user", "root"), } if "metadata" in job: ret["Metadata"] = job.get("metadata", {}) else: if "kwargs" in job: if "metadata" in job["kwargs"]: ret["Metadata"] = job["kwargs"].get("metadata", {}) if "Minions" in job: ret["Minions"] = job["Minions"] return ret def _format_jid_instance(jid, job): """ Helper to format jid instance """ ret = _format_job_instance(job) ret.update({"StartTime": salt.utils.jid.jid_to_time(jid)}) return ret def _walk_through(job_dir, display_progress=False): """ Walk through the job dir and return jobs """ for top in os.listdir(job_dir): t_path = os.path.join(job_dir, top) for final in os.listdir(t_path): load_path = os.path.join(t_path, final, ".load.p") with salt.utils.files.fopen(load_path, "rb") as rfh: job = salt.payload.load(rfh) if not os.path.isfile(load_path): continue with salt.utils.files.fopen(load_path, "rb") as rfh: job = salt.payload.load(rfh) jid = job["jid"] if display_progress: __jid_event__.fire_event({"message": f"Found JID {jid}"}, "progress") yield jid, job, t_path, final