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/pillar
Viewing File: /opt/saltstack/salt/lib/python3.10/site-packages/salt/pillar/file_tree.py
""" The ``file_tree`` external pillar allows values from all files in a directory tree to be imported as Pillar data. .. note:: This is an external pillar and is subject to the :ref:`rules and constraints <external-pillars>` governing external pillars. .. versionadded:: 2015.5.0 In this pillar, data is organized by either Minion ID or Nodegroup name. To setup pillar data for a specific Minion, place it in ``<root_dir>/hosts/<minion_id>``. To setup pillar data for an entire Nodegroup, place it in ``<root_dir>/nodegroups/<node_group>`` where ``<node_group>`` is the Nodegroup's name. Example ``file_tree`` Pillar ============================ Master Configuration -------------------- .. code-block:: yaml ext_pillar: - file_tree: root_dir: /srv/ext_pillar follow_dir_links: False keep_newline: True The ``root_dir`` parameter is required and points to the directory where files for each host are stored. The ``follow_dir_links`` parameter is optional and defaults to False. If ``follow_dir_links`` is set to True, this external pillar will follow symbolic links to other directories. .. warning:: Be careful when using ``follow_dir_links``, as a recursive symlink chain will result in unexpected results. .. versionchanged:: 2018.3.0 If ``root_dir`` is a relative path, it will be treated as relative to the :conf_master:`pillar_roots` of the environment specified by :conf_minion:`pillarenv`. If an environment specifies multiple roots, this module will search for files relative to all of them, in order, merging the results. If ``keep_newline`` is set to ``True``, then the pillar values for files ending in newlines will keep that newline. The default behavior is to remove the end-of-file newline. ``keep_newline`` should be turned on if the pillar data is intended to be used to deploy a file using ``contents_pillar`` with a :py:func:`file.managed <salt.states.file.managed>` state. .. versionchanged:: 2015.8.4 The ``raw_data`` parameter has been renamed to ``keep_newline``. In earlier releases, ``raw_data`` must be used. Also, this parameter can now be a list of globs, allowing for more granular control over which pillar values keep their end-of-file newline. The globs match paths relative to the directories named for minion IDs and nodegroups underneath the ``root_dir`` (see the layout examples in the below sections). .. code-block:: yaml ext_pillar: - file_tree: root_dir: /path/to/root/directory keep_newline: - files/testdir/* .. note:: In earlier releases, this documentation incorrectly stated that binary files would not affected by the ``keep_newline`` configuration. However, this module does not actually distinguish between binary and text files. .. versionchanged:: 2017.7.0 Templating/rendering has been added. You can now specify a default render pipeline and a black- and whitelist of (dis)allowed renderers. ``template`` must be set to ``True`` for templating to happen. .. code-block:: yaml ext_pillar: - file_tree: root_dir: /path/to/root/directory render_default: jinja|yaml renderer_blacklist: - gpg renderer_whitelist: - jinja - yaml template: True Assigning Pillar Data to Individual Hosts ----------------------------------------- To configure pillar data for each host, this external pillar will recursively iterate over ``root_dir``/hosts/``id`` (where ``id`` is a minion ID), and compile pillar data with each subdirectory as a dictionary key and each file as a value. For example, the following ``root_dir`` tree: .. code-block:: text ./hosts/ ./hosts/test-host/ ./hosts/test-host/files/ ./hosts/test-host/files/testdir/ ./hosts/test-host/files/testdir/file1.txt ./hosts/test-host/files/testdir/file2.txt ./hosts/test-host/files/another-testdir/ ./hosts/test-host/files/another-testdir/symlink-to-file1.txt will result in the following pillar tree for minion with ID ``test-host``: .. code-block:: text test-host: ---------- apache: ---------- config.d: ---------- 00_important.conf: <important_config important_setting="yes" /> 20_bob_extra.conf: <bob_specific_cfg has_freeze_ray="yes" /> corporate_app: ---------- settings: ---------- common_settings: // This is the main settings file for the corporate // internal web app main_setting: probably bob_settings: role: bob .. note:: The leaf data in the example shown is the contents of the pillar files. """ import fnmatch import logging import os import salt.loader import salt.template import salt.utils.dictupdate import salt.utils.files import salt.utils.minions import salt.utils.path import salt.utils.stringio import salt.utils.stringutils # Set up logging log = logging.getLogger(__name__) def _on_walk_error(err): """ Log salt.utils.path.os_walk() error. """ log.error("%s: %s", err.filename, err.strerror) def _check_newline(prefix, file_name, keep_newline): """ Return a boolean stating whether or not a file's trailing newline should be removed. To figure this out, first check if keep_newline is a boolean and if so, return its opposite. Otherwise, iterate over keep_newline and check if any of the patterns match the file path. If a match is found, return False, otherwise return True. """ if isinstance(keep_newline, bool): return not keep_newline full_path = os.path.join(prefix, file_name) for pattern in keep_newline: try: if fnmatch.fnmatch(full_path, pattern): return False except TypeError: if fnmatch.fnmatch(full_path, str(pattern)): return False return True def _construct_pillar( top_dir, follow_dir_links, keep_newline=False, render_default=None, renderer_blacklist=None, renderer_whitelist=None, template=False, ): """ Construct pillar from file tree. """ pillar = {} renderers = salt.loader.render(__opts__, __salt__) norm_top_dir = os.path.normpath(top_dir) for dir_path, dir_names, file_names in salt.utils.path.os_walk( top_dir, topdown=True, onerror=_on_walk_error, followlinks=follow_dir_links ): # Find current path in pillar tree pillar_node = pillar norm_dir_path = os.path.normpath(dir_path) prefix = os.path.relpath(norm_dir_path, norm_top_dir) if norm_dir_path != norm_top_dir: path_parts = [] head = prefix while head: head, tail = os.path.split(head) path_parts.insert(0, tail) while path_parts: pillar_node = pillar_node[path_parts.pop(0)] # Create dicts for subdirectories for dir_name in dir_names: pillar_node[dir_name] = {} # Add files for file_name in file_names: file_path = os.path.join(dir_path, file_name) if not os.path.isfile(file_path): log.error("file_tree: %s: not a regular file", file_path) continue contents = b"" try: with salt.utils.files.fopen(file_path, "rb") as fhr: buf = fhr.read(__opts__["file_buffer_size"]) while buf: contents += buf buf = fhr.read(__opts__["file_buffer_size"]) if contents.endswith(b"\n") and _check_newline( prefix, file_name, keep_newline ): contents = contents[:-1] except OSError as exc: log.error("file_tree: Error reading %s: %s", file_path, exc.strerror) else: data = contents if template is True: data = salt.template.compile_template_str( template=salt.utils.stringutils.to_unicode(contents), renderers=renderers, default=render_default, blacklist=renderer_blacklist, whitelist=renderer_whitelist, ) if salt.utils.stringio.is_readable(data): pillar_node[file_name] = data.getvalue() else: pillar_node[file_name] = data return pillar def ext_pillar( minion_id, pillar, root_dir=None, follow_dir_links=False, debug=False, keep_newline=False, render_default=None, renderer_blacklist=None, renderer_whitelist=None, template=False, ): """ Compile pillar data from the given ``root_dir`` specific to Nodegroup names and Minion IDs. If a Minion's ID is not found at ``<root_dir>/host/<minion_id>`` or if it is not included in any Nodegroups named at ``<root_dir>/nodegroups/<node_group>``, no pillar data provided by this pillar module will be available for that Minion. .. versionchanged:: 2017.7.0 Templating/rendering has been added. You can now specify a default render pipeline and a black- and whitelist of (dis)allowed renderers. ``template`` must be set to ``True`` for templating to happen. .. code-block:: yaml ext_pillar: - file_tree: root_dir: /path/to/root/directory render_default: jinja|yaml renderer_blacklist: - gpg renderer_whitelist: - jinja - yaml template: True :param minion_id: The ID of the Minion whose pillar data is to be collected :param pillar: Unused by the ``file_tree`` pillar module :param root_dir: Filesystem directory used as the root for pillar data (e.g. ``/srv/ext_pillar``) .. versionchanged:: 2018.3.0 If ``root_dir`` is a relative path, it will be treated as relative to the :conf_master:`pillar_roots` of the environment specified by :conf_minion:`pillarenv`. If an environment specifies multiple roots, this module will search for files relative to all of them, in order, merging the results. :param follow_dir_links: Follow symbolic links to directories while collecting pillar files. Defaults to ``False``. .. warning:: Care should be exercised when enabling this option as it will follow links that point outside of ``root_dir``. .. warning:: Symbolic links that lead to infinite recursion are not filtered. :param debug: Enable debug information at log level ``debug``. Defaults to ``False``. This option may be useful to help debug errors when setting up the ``file_tree`` pillar module. :param keep_newline: Preserve the end-of-file newline in files. Defaults to ``False``. This option may either be a boolean or a list of file globs (as defined by the `Python fnmatch package <https://docs.python.org/library/fnmatch.html>`_) for which end-of-file newlines are to be kept. ``keep_newline`` should be turned on if the pillar data is intended to be used to deploy a file using ``contents_pillar`` with a :py:func:`file.managed <salt.states.file.managed>` state. .. versionchanged:: 2015.8.4 The ``raw_data`` parameter has been renamed to ``keep_newline``. In earlier releases, ``raw_data`` must be used. Also, this parameter can now be a list of globs, allowing for more granular control over which pillar values keep their end-of-file newline. The globs match paths relative to the directories named for Minion IDs and Nodegroup namess underneath the ``root_dir``. .. code-block:: yaml ext_pillar: - file_tree: root_dir: /srv/ext_pillar keep_newline: - apache/config.d/* - corporate_app/settings/* .. note:: In earlier releases, this documentation incorrectly stated that binary files would not affected by the ``keep_newline``. However, this module does not actually distinguish between binary and text files. :param render_default: Override Salt's :conf_master:`default global renderer <renderer>` for the ``file_tree`` pillar. .. code-block:: yaml render_default: jinja :param renderer_blacklist: Disallow renderers for pillar files. .. code-block:: yaml renderer_blacklist: - json :param renderer_whitelist: Allow renderers for pillar files. .. code-block:: yaml renderer_whitelist: - yaml - jinja :param template: Enable templating of pillar files. Defaults to ``False``. """ # Not used del pillar if not root_dir: log.error("file_tree: no root_dir specified") return {} if not os.path.isabs(root_dir): pillarenv = __opts__["pillarenv"] if pillarenv is None: log.error("file_tree: root_dir is relative but pillarenv is not set") return {} log.debug("file_tree: pillarenv = %s", pillarenv) env_roots = __opts__["pillar_roots"].get(pillarenv, None) if env_roots is None: log.error( "file_tree: root_dir is relative but no pillar_roots are specified " " for pillarenv %s", pillarenv, ) return {} env_dirs = [] for env_root in env_roots: env_dir = os.path.normpath(os.path.join(env_root, root_dir)) # don't redundantly load consecutively, but preserve any expected precedence if env_dir not in env_dirs or env_dir != env_dirs[-1]: env_dirs.append(env_dir) dirs = env_dirs else: dirs = [root_dir] result_pillar = {} for root in dirs: dir_pillar = _ext_pillar( minion_id, root, follow_dir_links, debug, keep_newline, render_default, renderer_blacklist, renderer_whitelist, template, ) result_pillar = salt.utils.dictupdate.merge( result_pillar, dir_pillar, strategy="recurse" ) return result_pillar def _ext_pillar( minion_id, root_dir, follow_dir_links, debug, keep_newline, render_default, renderer_blacklist, renderer_whitelist, template, ): """ Compile pillar data for a single root_dir for the specified minion ID """ log.debug("file_tree: reading %s", root_dir) if not os.path.isdir(root_dir): log.error( "file_tree: root_dir %s does not exist or is not a directory", root_dir ) return {} if not isinstance(keep_newline, (bool, list)): log.error( "file_tree: keep_newline must be either True/False or a list " "of file globs. Skipping this ext_pillar for root_dir %s", root_dir, ) return {} ngroup_pillar = {} nodegroups_dir = os.path.join(root_dir, "nodegroups") if os.path.exists(nodegroups_dir) and len(__opts__.get("nodegroups", ())) > 0: master_ngroups = __opts__["nodegroups"] ext_pillar_dirs = os.listdir(nodegroups_dir) if len(ext_pillar_dirs) > 0: for nodegroup in ext_pillar_dirs: if os.path.isdir(nodegroups_dir) and nodegroup in master_ngroups: ckminions = salt.utils.minions.CkMinions(__opts__) _res = ckminions.check_minions( master_ngroups[nodegroup], "compound" ) match = _res["minions"] if minion_id in match: ngroup_dir = os.path.join(nodegroups_dir, str(nodegroup)) ngroup_pillar = salt.utils.dictupdate.merge( ngroup_pillar, _construct_pillar( ngroup_dir, follow_dir_links, keep_newline, render_default, renderer_blacklist, renderer_whitelist, template, ), strategy="recurse", ) else: if debug is True: log.debug( "file_tree: no nodegroups found in file tree directory %s," " skipping...", ext_pillar_dirs, ) else: if debug is True: log.debug("file_tree: no nodegroups found in master configuration") host_dir = os.path.join(root_dir, "hosts", minion_id) if not os.path.exists(host_dir): if debug is True: log.debug( "file_tree: no pillar data for minion %s found in file tree" " directory %s", minion_id, host_dir, ) return ngroup_pillar if not os.path.isdir(host_dir): log.error("file_tree: %s exists, but is not a directory", host_dir) return ngroup_pillar host_pillar = _construct_pillar( host_dir, follow_dir_links, keep_newline, render_default, renderer_blacklist, renderer_whitelist, template, ) return salt.utils.dictupdate.merge(ngroup_pillar, host_pillar, strategy="recurse")