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/modules
Viewing File: /opt/saltstack/salt/lib/python3.10/site-packages/salt/modules/ansiblegate.py
# # Author: Bo Maryniuk <bo@suse.de> # """ Ansible Support =============== This module can have an optional minion-level configuration in /etc/salt/minion.d/ as follows: ansible_timeout: 1200 The timeout is how many seconds Salt should wait for any Ansible module to respond. """ import fnmatch import json import logging import os import subprocess import sys from tempfile import NamedTemporaryFile import salt.utils.ansible import salt.utils.decorators.path import salt.utils.json import salt.utils.path import salt.utils.platform import salt.utils.stringutils import salt.utils.timed_subprocess import salt.utils.yaml from salt.exceptions import CommandExecutionError # Function alias to make sure not to shadow built-in's __func_alias__ = {"list_": "list"} __virtualname__ = "ansible" log = logging.getLogger(__name__) INVENTORY = """ hosts: vars: ansible_connection: local """ DEFAULT_TIMEOUT = 1200 # seconds (20 minutes) __non_ansible_functions__ = [] __load__ = __non_ansible_functions__[:] = [ "help", "list_", "call", "playbooks", "discover_playbooks", "targets", ] def _set_callables(modules): """ Set all Ansible modules callables :return: """ def _set_function(real_cmd_name, doc): """ Create a Salt function for the Ansible module. """ def _cmd(*args, **kwargs): """ Call an Ansible module as a function from the Salt. """ return call(real_cmd_name, *args, **kwargs) _cmd.__doc__ = doc return _cmd for mod, (real_mod, doc) in modules.items(): __load__.append(mod) setattr(sys.modules[__name__], mod, _set_function(real_mod, doc)) def __virtual__(): if salt.utils.platform.is_windows(): return False, "The ansiblegate module isn't supported on Windows" ansible_bin = salt.utils.path.which("ansible") if not ansible_bin: return False, "The 'ansible' binary was not found." ansible_doc_bin = salt.utils.path.which("ansible-doc") if not ansible_doc_bin: return False, "The 'ansible-doc' binary was not found." ansible_playbook_bin = salt.utils.path.which("ansible-playbook") if not ansible_playbook_bin: return False, "The 'ansible-playbook' binary was not found." env = os.environ.copy() env["ANSIBLE_DEPRECATION_WARNINGS"] = "0" proc = subprocess.run( [ansible_doc_bin, "--list", "--json", "--type=module"], capture_output=True, check=False, shell=False, text=True, env=env, ) if proc.returncode != 0: return ( False, f"Failed to get the listing of ansible modules:\n{proc.stderr}", ) module_funcs = dir(sys.modules[__name__]) ansible_module_listing = salt.utils.json.loads(proc.stdout) salt_ansible_modules_mapping = {} for key in list(ansible_module_listing): if not key.startswith("ansible."): salt_ansible_modules_mapping[key] = (key, ansible_module_listing[key]) continue # Strip 'ansible.' from the module # Fyi, str.partition() is faster than str.replace() _, _, alias = key.partition(".") if alias in salt_ansible_modules_mapping: continue if alias in module_funcs: continue salt_ansible_modules_mapping[alias] = (key, ansible_module_listing[key]) if alias.startswith(("builtin.", "system.")): # Strip "builtin." or "system." so that we can do something like # "salt-call ansible.ping" instead of "salt-call ansible.builtin.ping", # although both formats can be used _, _, alias = alias.partition(".") if alias in salt_ansible_modules_mapping: continue if alias in module_funcs: continue salt_ansible_modules_mapping[alias] = (key, ansible_module_listing[key]) _set_callables(salt_ansible_modules_mapping) return __virtualname__ def help(module=None, *args): """ Display help on Ansible standard module. :param module: The module to get the help CLI Example: .. code-block:: bash salt * ansible.help ping """ if not module: raise CommandExecutionError( "Please tell me what module you want to have helped with. " 'Or call "ansible.list" to know what is available.' ) ansible_doc_bin = salt.utils.path.which("ansible-doc") env = os.environ.copy() env["ANSIBLE_DEPRECATION_WARNINGS"] = "0" proc = subprocess.run( [ansible_doc_bin, "--json", "--type=module", module], capture_output=True, check=True, shell=False, text=True, env=env, ) data = salt.utils.json.loads(proc.stdout) doc = data[next(iter(data))] if not args: ret = doc["doc"] for section in ("examples", "return", "metadata"): section_data = doc.get(section) if section_data: ret[section] = section_data else: ret = {} for arg in args: info = doc.get(arg) if info is not None: ret[arg] = info return ret def list_(pattern=None): """ Lists available modules. CLI Example: .. code-block:: bash salt * ansible.list salt * ansible.list '*win*' # To get all modules matching 'win' on it's name """ if pattern is None: module_list = set(__load__) module_list.discard(set(__non_ansible_functions__)) return sorted(module_list) return sorted(fnmatch.filter(__load__, pattern)) def call(module, *args, **kwargs): """ Call an Ansible module by invoking it. :param module: the name of the module. :param args: Arguments to pass to the module :param kwargs: keywords to pass to the module CLI Example: .. code-block:: bash salt * ansible.call ping data=foobar """ module_args = [] for arg in args: module_args.append(salt.utils.json.dumps(arg)) _kwargs = {} for _kw in kwargs.get("__pub_arg", []): if isinstance(_kw, dict): _kwargs = _kw break else: _kwargs = {k: v for (k, v) in kwargs.items() if not k.startswith("__pub")} for key, value in _kwargs.items(): module_args.append(f"{key}={salt.utils.json.dumps(value)}") with NamedTemporaryFile(mode="w") as inventory: ansible_binary_path = salt.utils.path.which("ansible") log.debug("Calling ansible module %r", module) try: env = os.environ.copy() env["ANSIBLE_DEPRECATION_WARNINGS"] = "0" proc_exc = subprocess.run( [ ansible_binary_path, "localhost", "--limit", "127.0.0.1", "-m", module, "-a", " ".join(module_args), "-i", inventory.name, ], capture_output=True, timeout=__opts__.get("ansible_timeout", DEFAULT_TIMEOUT), text=True, check=True, shell=False, env=env, ) original_output = proc_exc.stdout proc_out = original_output.splitlines() if proc_out[0].endswith("{"): proc_out[0] = "{" try: out = salt.utils.json.loads("\n".join(proc_out)) except ValueError as exc: out = { "Error": proc_exc.stderr or str(exc), "Output": original_output, } return out elif proc_out[0].endswith(">>"): out = {"output": "\n".join(proc_out[1:])} else: out = {"output": original_output} except subprocess.CalledProcessError as exc: out = {"Exitcode": exc.returncode, "Error": exc.stderr or str(exc)} if exc.stdout: out["Given JSON output"] = exc.stdout return out for key in ("invocation", "changed"): out.pop(key, None) return out @salt.utils.decorators.path.which("ansible-playbook") def playbooks( playbook, rundir=None, check=False, diff=False, extra_vars=None, flush_cache=False, forks=5, inventory=None, limit=None, list_hosts=False, list_tags=False, list_tasks=False, module_path=None, skip_tags=None, start_at_task=None, syntax_check=False, tags=None, playbook_kwargs=None, ): """ Run Ansible Playbooks :param playbook: Which playbook to run. :param rundir: Directory to run `ansible-playbook` in. (Default: None) :param check: don't make any changes; instead, try to predict some of the changes that may occur (Default: False) :param diff: when changing (small) files and templates, show the differences in those files; works great with --check (default: False) :param extra_vars: set additional variables as key=value or YAML/JSON, if filename prepend with @, (default: None) :param flush_cache: clear the fact cache for every host in inventory (default: False) :param forks: specify number of parallel processes to use (Default: 5) :param inventory: specify inventory host path or comma separated host list. (Default: None) (Ansible's default is /etc/ansible/hosts) :param limit: further limit selected hosts to an additional pattern (Default: None) :param list_hosts: outputs a list of matching hosts; does not execute anything else (Default: False) :param list_tags: list all available tags (Default: False) :param list_tasks: list all tasks that would be executed (Default: False) :param module_path: prepend colon-separated path(s) to module library. (Default: None) :param skip_tags: only run plays and tasks whose tags do not match these values (Default: False) :param start_at_task: start the playbook at the task matching this name (Default: None) :param: syntax_check: perform a syntax check on the playbook, but do not execute it (Default: False) :param tags: only run plays and tasks tagged with these values (Default: None) :return: Playbook return CLI Example: .. code-block:: bash salt 'ansiblehost' ansible.playbooks playbook=/srv/playbooks/play.yml """ command = ["ansible-playbook", playbook] if check: command.append("--check") if diff: command.append("--diff") if isinstance(extra_vars, dict): command.append(f"--extra-vars='{json.dumps(extra_vars)}'") elif isinstance(extra_vars, str) and extra_vars.startswith("@"): command.append(f"--extra-vars={extra_vars}") if flush_cache: command.append("--flush-cache") if inventory: command.append(f"--inventory={inventory}") if limit: command.append(f"--limit={limit}") if list_hosts: command.append("--list-hosts") if list_tags: command.append("--list-tags") if list_tasks: command.append("--list-tasks") if module_path: command.append(f"--module-path={module_path}") if skip_tags: command.append(f"--skip-tags={skip_tags}") if start_at_task: command.append(f"--start-at-task={start_at_task}") if syntax_check: command.append("--syntax-check") if tags: command.append(f"--tags={tags}") if playbook_kwargs: for key, value in playbook_kwargs.items(): key = key.replace("_", "-") if value is True: command.append(f"--{key}") elif isinstance(value, str): command.append(f"--{key}={value}") elif isinstance(value, dict): command.append(f"--{key}={json.dumps(value)}") command.append(f"--forks={forks}") cmd_kwargs = { "env": { "ANSIBLE_STDOUT_CALLBACK": "json", "ANSIBLE_RETRY_FILES_ENABLED": "0", "ANSIBLE_DEPRECATION_WARNINGS": "0", }, "cwd": rundir, "cmd": " ".join(command), "reset_system_locale": False, } ret = __salt__["cmd.run_all"](**cmd_kwargs) log.debug("Ansible Playbook Return: %s", ret) try: retdata = json.loads(ret["stdout"]) except ValueError: retdata = ret if "retcode" in ret: __context__["retcode"] = retdata["retcode"] = ret["retcode"] return retdata def targets(inventory="/etc/ansible/hosts", yaml=False, export=False): """ .. versionadded:: 3005 Return the inventory from an Ansible inventory_file :param inventory: The inventory file to read the inventory from. Default: "/etc/ansible/hosts" :param yaml: Return the inventory as yaml output. Default: False :param export: Return inventory as export format. Default: False CLI Example: .. code-block:: bash salt 'ansiblehost' ansible.targets salt 'ansiblehost' ansible.targets inventory=my_custom_inventory """ return salt.utils.ansible.targets(inventory=inventory, yaml=yaml, export=export) def discover_playbooks( path=None, locations=None, playbook_extension=None, hosts_filename=None, syntax_check=False, ): """ .. versionadded:: 3005 Discover Ansible playbooks stored under the given path or from multiple paths (locations) This will search for files matching with the playbook file extension under the given root path and will also look for files inside the first level of directories in this path. The return of this function would be a dict like this: .. code-block:: python { "/home/foobar/": { "my_ansible_playbook.yml": { "fullpath": "/home/foobar/playbooks/my_ansible_playbook.yml", "custom_inventory": "/home/foobar/playbooks/hosts" }, "another_playbook.yml": { "fullpath": "/home/foobar/playbooks/another_playbook.yml", "custom_inventory": "/home/foobar/playbooks/hosts" }, "lamp_simple/site.yml": { "fullpath": "/home/foobar/playbooks/lamp_simple/site.yml", "custom_inventory": "/home/foobar/playbooks/lamp_simple/hosts" }, "lamp_proxy/site.yml": { "fullpath": "/home/foobar/playbooks/lamp_proxy/site.yml", "custom_inventory": "/home/foobar/playbooks/lamp_proxy/hosts" } }, "/srv/playbooks/": { "example_playbook/example.yml": { "fullpath": "/srv/playbooks/example_playbook/example.yml", "custom_inventory": "/srv/playbooks/example_playbook/hosts" } } } :param path: Path to discover playbooks from. :param locations: List of paths to discover playbooks from. :param playbook_extension: File extension of playbooks file to search for. Default: "yml" :param hosts_filename: Filename of custom playbook inventory to search for. Default: "hosts" :param syntax_check: Skip playbooks that do not pass "ansible-playbook --syntax-check" validation. Default: False :return: The discovered playbooks under the given paths CLI Example: .. code-block:: bash salt 'ansiblehost' ansible.discover_playbooks path=/srv/playbooks/ salt 'ansiblehost' ansible.discover_playbooks locations='["/srv/playbooks/", "/srv/foobar"]' """ if not path and not locations: raise CommandExecutionError( "You have to specify either 'path' or 'locations' arguments" ) if path and locations: raise CommandExecutionError( "You cannot specify 'path' and 'locations' at the same time" ) if not playbook_extension: playbook_extension = "yml" if not hosts_filename: hosts_filename = "hosts" if path: if not os.path.isabs(path): raise CommandExecutionError( f"The given path is not an absolute path: {path}" ) if not os.path.isdir(path): raise CommandExecutionError(f"The given path is not a directory: {path}") return { path: _explore_path(path, playbook_extension, hosts_filename, syntax_check) } if locations: all_ret = {} for location in locations: all_ret[location] = _explore_path( location, playbook_extension, hosts_filename, syntax_check ) return all_ret def _explore_path(path, playbook_extension, hosts_filename, syntax_check): ret = {} if not os.path.isabs(path): log.error("The given path is not an absolute path: %s", path) return ret if not os.path.isdir(path): log.error("The given path is not a directory: %s", path) return ret try: # Check files in the given path for _f in os.listdir(path): _path = os.path.join(path, _f) if os.path.isfile(_path) and _path.endswith("." + playbook_extension): ret[_f] = {"fullpath": _path} # Check for custom inventory file if os.path.isfile(os.path.join(path, hosts_filename)): ret[_f].update( {"custom_inventory": os.path.join(path, hosts_filename)} ) elif os.path.isdir(_path): # Check files in the 1st level of subdirectories for _f2 in os.listdir(_path): _path2 = os.path.join(_path, _f2) if os.path.isfile(_path2) and _path2.endswith( "." + playbook_extension ): ret[os.path.join(_f, _f2)] = {"fullpath": _path2} # Check for custom inventory file if os.path.isfile(os.path.join(_path, hosts_filename)): ret[os.path.join(_f, _f2)].update( { "custom_inventory": os.path.join( _path, hosts_filename ) } ) except Exception as exc: raise CommandExecutionError( f"There was an exception while discovering playbooks: {exc}" ) # Run syntax check validation if syntax_check: check_command = ["ansible-playbook", "--syntax-check"] try: for pb in list(ret): if __salt__["cmd.retcode"]( check_command + [ret[pb]], reset_system_locale=False ): del ret[pb] except Exception as exc: raise CommandExecutionError( "There was an exception while checking syntax of playbooks: {}".format( exc ) ) return ret