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/renderers
Viewing File: /opt/saltstack/salt/lib/python3.10/site-packages/salt/renderers/stateconf.py
""" A flexible renderer that takes a templating engine and a data format :maintainer: Jack Kuan <kjkuan@gmail.com> :maturity: new :platform: all """ # See http://docs.saltproject.io/en/latest/ref/renderers/all/salt.renderers.stateconf.html # for a guide to using this module. # # FIXME: I really need to review and simplify this renderer, it's getting out of hand! # # TODO: # - sls meta/info state: E.g., # # sls_info: # stateconf.set: # - author: Jack Kuan # - description: what the salt file does... # - version: 0.1.0 # # - version constraint for 'include'. E.g., # # include: # - apache: >= 0.1.0 # import copy import getopt import io import logging import os import re from itertools import chain import salt.utils.files import salt.utils.stringutils from salt.exceptions import SaltRenderError __all__ = ["render"] log = logging.getLogger(__name__) __opts__ = { "stateconf_end_marker": r"#\s*-+\s*end of state config\s*-+", # e.g., something like "# --- end of state config --" works by default. "stateconf_start_state": ".start", # name of the state id for the generated start state. "stateconf_goal_state": ".goal", # name of the state id for the generated goal state. "stateconf_state_func": "stateconf.set", # names the state and the state function to be recognized as a special # state from which to gather sls file context variables. It should be # specified in the 'state.func' notation, and both the state module and # the function must actually exist and the function should be a dummy, # no-op state function that simply returns a # dict(name=name, result=True, changes={}, comment='') } STATE_FUNC = STATE_NAME = "" def __init__(opts): global STATE_NAME, STATE_FUNC STATE_FUNC = __opts__["stateconf_state_func"] STATE_NAME = STATE_FUNC.split(".", maxsplit=1)[0] MOD_BASENAME = os.path.basename(__file__) INVALID_USAGE_ERROR = SaltRenderError( "Invalid use of {0} renderer!\n" """Usage: #!{0} [-GoSp] [<data_renderer> [options] . <template_renderer> [options]] where an example <data_renderer> would be yaml and a <template_renderer> might be jinja. Each renderer can be passed its renderer specific options. Options(for this renderer): -G Do not generate the goal state that requires all other states in the sls. -o Indirectly order the states by adding requires such that they will be executed in the order they are defined in the sls. Implies using yaml -o. -s Generate the start state that gets inserted as the first state in the sls. This only makes sense if your high state data dict is ordered. -p Assume high state input. This option allows you to pipe high state data through this renderer. With this option, the use of stateconf.set state in the sls will have no effect, but other features of the renderer still apply. """.format( MOD_BASENAME ) ) def render(input, saltenv="base", sls="", argline="", **kws): gen_start_state = False no_goal_state = False implicit_require = False def process_sls_data(data, context=None, extract=False): sls_dir = os.path.dirname(sls.replace(".", os.path.sep)) if "." in sls else sls ctx = dict(sls_dir=sls_dir if sls_dir else ".") if context: ctx.update(context) tmplout = render_template( io.StringIO(data), saltenv, sls, context=ctx, argline=rt_argline.strip(), **kws, ) high = render_data(tmplout, saltenv, sls, argline=rd_argline.strip()) return process_high_data(high, extract) def process_high_data(high, extract): # make a copy so that the original, un-preprocessed highstate data # structure can be used later for error checking if anything goes # wrong during the preprocessing. data = copy.deepcopy(high) try: rewrite_single_shorthand_state_decl(data) rewrite_sls_includes_excludes(data, sls, saltenv) if not extract and implicit_require: sid = has_names_decls(data) if sid: raise SaltRenderError( "'names' declaration(found in state id: {}) is " "not supported with implicitly ordered states! You " "should generate the states in a template for-loop " "instead.".format(sid) ) add_implicit_requires(data) if gen_start_state: add_start_state(data, sls) if not extract and not no_goal_state: add_goal_state(data) rename_state_ids(data, sls) # We must extract no matter what so extending a stateconf sls file # works! extract_state_confs(data) except SaltRenderError: raise except Exception as err: # pylint: disable=broad-except log.exception( "Error found while pre-processing the salt file %s:\n%s", sls, err ) from salt.state import State state = State(__opts__) errors = state.verify_high(high) if errors: raise SaltRenderError("\n".join(errors)) raise SaltRenderError("sls preprocessing/rendering failed!") return data # ---------------------- renderers = kws["renderers"] opts, args = getopt.getopt(argline.split(), "Gosp") argline = " ".join(args) if args else "yaml . jinja" if ("-G", "") in opts: no_goal_state = True if ("-o", "") in opts: implicit_require = True if ("-s", "") in opts: gen_start_state = True if ("-p", "") in opts: data = process_high_data(input, extract=False) else: # Split on the first dot surrounded by spaces but not preceded by a # backslash. A backslash preceded dot will be replaced with just dot. args = [ arg.strip().replace("\\.", ".") for arg in re.split(r"\s+(?<!\\)\.\s+", argline, 1) ] try: name, rd_argline = (args[0] + " ").split(" ", 1) render_data = renderers[name] # e.g., the yaml renderer if implicit_require: if name == "yaml": rd_argline = "-o " + rd_argline else: raise SaltRenderError( "Implicit ordering is only supported if the yaml renderer " "is used!" ) name, rt_argline = (args[1] + " ").split(" ", 1) render_template = renderers[name] # e.g., the mako renderer except KeyError as err: raise SaltRenderError(f"Renderer: {err} is not available!") except IndexError: raise INVALID_USAGE_ERROR if isinstance(input, str): with salt.utils.files.fopen(input, "r") as ifile: sls_templ = salt.utils.stringutils.to_unicode(ifile.read()) else: # assume file-like sls_templ = salt.utils.stringutils.to_unicode(input.read()) # first pass to extract the state configuration match = re.search(__opts__["stateconf_end_marker"], sls_templ) if match: process_sls_data(sls_templ[: match.start()], extract=True) # if some config has been extracted then remove the sls-name prefix # of the keys in the extracted stateconf.set context to make them easier # to use in the salt file. if STATE_CONF: tmplctx = STATE_CONF.copy() if tmplctx: prefix = sls + "::" tmplctx = { k[len(prefix) :] if k.startswith(prefix) else k: v for k, v in tmplctx.items() } else: tmplctx = {} # do a second pass that provides the extracted conf as template context data = process_sls_data(sls_templ, tmplctx) if log.isEnabledFor(logging.DEBUG): import pprint # FIXME: pprint OrderedDict log.debug("Rendered sls: %s", pprint.pformat(data)) return data def has_names_decls(data): for sid, _, _, args in statelist(data): if sid == "extend": continue for _ in nvlist(args, ["names"]): return sid def rewrite_single_shorthand_state_decl(data): # pylint: disable=C0103 """ Rewrite all state declarations that look like this:: state_id_decl: state.func into:: state_id_decl: state.func: [] """ for sid, states in data.items(): if isinstance(states, str): data[sid] = {states: []} def rewrite_sls_includes_excludes(data, sls, saltenv): # if the path of the included/excluded sls starts with a leading dot(.) # then it's taken to be relative to the including/excluding sls. for sid in data: if sid == "include": includes = data[sid] for i, each in enumerate(includes): if isinstance(each, dict): slsenv, incl = each.popitem() else: slsenv = saltenv incl = each if incl.startswith("."): includes[i] = {slsenv: _relative_to_abs_sls(incl, sls)} elif sid == "exclude": for sdata in data[sid]: if "sls" in sdata and sdata["sls"].startswith("."): sdata["sls"] = _relative_to_abs_sls(sdata["sls"], sls) def _local_to_abs_sid(sid, sls): # id must starts with '.' if "::" in sid: return _relative_to_abs_sls(sid, sls) else: abs_sls = _relative_to_abs_sls(sid, sls + ".") return "::".join(abs_sls.rsplit(".", 1)) def _relative_to_abs_sls(relative, sls): """ Convert ``relative`` sls reference into absolute, relative to ``sls``. """ levels, suffix = re.match(r"^(\.+)(.*)$", relative).groups() level_count = len(levels) p_comps = sls.split(".") if level_count > len(p_comps): raise SaltRenderError( "Attempted relative include goes beyond top level package" ) return ".".join(p_comps[:-level_count] + [suffix]) def nvlist(thelist, names=None): """ Given a list of items:: - whatever - name1: value1 - name2: - key: value - key: value return a generator that yields each (item, key, value) tuple, skipping items that are not name-value's(dictionaries) or those not in the list of matching names. The item in the returned tuple is the single-key dictionary. """ # iterate over the list under the state dict. for nvitem in thelist: if isinstance(nvitem, dict): # then nvitem is a name-value item(a dict) of the list. name, value = next(iter(nvitem.items())) if names is None or name in names: yield nvitem, name, value def nvlist2(thelist, names=None): """ Like nvlist but applied one more time to each returned value. So, given a list, args, of arguments to a state like this:: - name: echo test - cwd: / - require: - file: test.sh nvlist2(args, ['require']) would yield the tuple, (dict_item, 'file', 'test.sh') where dict_item is the single-key dictionary of {'file': 'test.sh'}. """ for _, _, value in nvlist(thelist, names): yield from nvlist(value) def statelist(states_dict, sid_excludes=frozenset(["include", "exclude"])): for sid, states in states_dict.items(): if sid.startswith("__"): continue if sid in sid_excludes: continue for sname, args in states.items(): if sname.startswith("__"): continue yield sid, states, sname, args REQUISITES = ( "require", "require_in", "watch", "watch_in", "use", "use_in", "listen", "listen_in", "onchanges", "onchanges_in", "onfail", "onfail_in", ) def rename_state_ids(data, sls, is_extend=False): # if the .sls file is salt://my/salt/file.sls # then rename all state ids defined in it that start with a dot(.) with # "my.salt.file::" + the_state_id_without_the_first_dot. # update "local" references to the renamed states. if "extend" in data and not is_extend: rename_state_ids(data["extend"], sls, True) for sid, _, _, args in statelist(data): for req, sname, sid in nvlist2(args, REQUISITES): if sid.startswith("."): req[sname] = _local_to_abs_sid(sid, sls) for sid in list(data): if sid.startswith("."): newsid = _local_to_abs_sid(sid, sls) if newsid in data: raise SaltRenderError( "Can't rename state id({}) into {} because the later " "already exists!".format(sid, newsid) ) # add a '- name: sid' to those states without '- name'. for sname, args in data[sid].items(): if state_name(sname) == STATE_NAME: continue for arg in args: if isinstance(arg, dict) and next(iter(arg)) == "name": break else: # then no '- name: ...' is defined in the state args # add the sid without the leading dot as the name. args.insert(0, dict(name=sid[1:])) data[newsid] = data[sid] del data[sid] REQUIRE = ("require", "watch", "listen", "onchanges", "onfail") REQUIRE_IN = ("require_in", "watch_in", "listen_in", "onchanges_in", "onfail_in") EXTENDED_REQUIRE = {} EXTENDED_REQUIRE_IN = {} # To avoid cycles among states when each state requires the one before it: # explicit require/watch/listen/onchanges/onfail can only contain states before it # explicit require_in/watch_in/listen_in/onchanges_in/onfail_in can only contain states after it def add_implicit_requires(data): def T(sid, state): # pylint: disable=C0103 return f"{sid}:{state_name(state)}" states_before = set() states_after = set() for sid in data: for state in data[sid]: states_after.add(T(sid, state)) prev_state = (None, None) # (state_name, sid) for sid, states, sname, args in statelist(data): if sid == "extend": for esid, _, _, eargs in statelist(states): for _, rstate, rsid in nvlist2(eargs, REQUIRE): EXTENDED_REQUIRE.setdefault(T(esid, rstate), []).append( (None, rstate, rsid) ) for _, rstate, rsid in nvlist2(eargs, REQUIRE_IN): EXTENDED_REQUIRE_IN.setdefault(T(esid, rstate), []).append( (None, rstate, rsid) ) continue tag = T(sid, sname) states_after.remove(tag) reqs = nvlist2(args, REQUIRE) if tag in EXTENDED_REQUIRE: reqs = chain(reqs, EXTENDED_REQUIRE[tag]) for _, rstate, rsid in reqs: if T(rsid, rstate) in states_after: raise SaltRenderError( "State({}) can't require/watch/listen/onchanges/onfail a state({})" " defined after it!".format(tag, T(rsid, rstate)) ) reqs = nvlist2(args, REQUIRE_IN) if tag in EXTENDED_REQUIRE_IN: reqs = chain(reqs, EXTENDED_REQUIRE_IN[tag]) for _, rstate, rsid in reqs: if T(rsid, rstate) in states_before: raise SaltRenderError( "State({}) can't" " require_in/watch_in/listen_in/onchanges_in/onfail_in a state({})" " defined before it!".format(tag, T(rsid, rstate)) ) # add a (- state: sid) item, at the beginning of the require of this # state if there's a state before this one. if prev_state[0] is not None: try: next(nvlist(args, ["require"]))[2].insert(0, dict([prev_state])) except StopIteration: # i.e., there's no require args.append(dict(require=[dict([prev_state])])) states_before.add(tag) prev_state = (state_name(sname), sid) def add_start_state(data, sls): start_sid = __opts__["stateconf_start_state"] if start_sid in data: raise SaltRenderError( "Can't generate start state({})! The same state id already exists!".format( start_sid ) ) if not data: return # the start state is either the first state whose id declaration has # no __sls__, or it's the first state whose id declaration has a # __sls__ == sls. non_sids = ("include", "exclude", "extend") for sid, states in data.items(): if sid in non_sids or sid.startswith("__"): continue if "__sls__" not in states or states["__sls__"] == sls: break else: raise SaltRenderError("Can't determine the first state in the sls file!") reqin = {state_name(next(iter(data[sid].keys()))): sid} data[start_sid] = {STATE_FUNC: [{"require_in": [reqin]}]} def add_goal_state(data): goal_sid = __opts__["stateconf_goal_state"] if goal_sid in data: raise SaltRenderError( "Can't generate goal state({})! The same state id already exists!".format( goal_sid ) ) else: reqlist = [] for sid, states, state, _ in statelist(data, ("include", "exclude", "extend")): if "__sls__" in states: # Then id declaration must have been included from a # rendered sls. Currently, this is only possible with # pydsl's high state output. continue reqlist.append({state_name(state): sid}) data[goal_sid] = {STATE_FUNC: [dict(require=reqlist)]} def state_name(sname): """ Return the name of the state regardless if sname is just the state name or a state.func name. """ return sname.split(".", 1)[0] # Quick and dirty way to get attribute access for dictionary keys. # So, we can do: ${apache.port} instead of ${apache['port']} when possible. class Bunch(dict): def __getattr__(self, name): return self[name] # With sls: # # state_id: # stateconf.set: # - name1: value1 # # STATE_CONF is: # { state_id => {name1: value1} } # STATE_CONF = {} # stateconf.set STATE_CONF_EXT = {} # stateconf.set under extend: ... def extract_state_confs(data, is_extend=False): for state_id, state_dict in data.items(): if state_id == "extend" and not is_extend: extract_state_confs(state_dict, True) continue if STATE_NAME in state_dict: key = STATE_NAME elif STATE_FUNC in state_dict: key = STATE_FUNC else: continue to_dict = STATE_CONF_EXT if is_extend else STATE_CONF conf = to_dict.setdefault(state_id, Bunch()) for sdk in state_dict[key]: if not isinstance(sdk, dict): continue key, val = next(iter(sdk.items())) conf[key] = val if not is_extend and state_id in STATE_CONF_EXT: extend = STATE_CONF_EXT[state_id] for requisite in "require", "watch", "listen", "onchanges", "onfail": if requisite in extend: extend[requisite] += to_dict[state_id].get(requisite, []) to_dict[state_id].update(STATE_CONF_EXT[state_id])