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: /usr/lib/python2.7/site-packages/yum
Viewing File: /usr/lib/python2.7/site-packages/yum/config.py
#!/usr/bin/python -t # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Library General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # Copyright 2002 Duke University """ Configuration parser and default values for yum. """ _use_iniparse = True import os import sys import warnings import rpm import copy import urlparse import shlex from parser import ConfigPreProcessor, varReplace try: from iniparse import INIConfig from iniparse.compat import NoSectionError, NoOptionError, ParsingError from iniparse.compat import RawConfigParser as ConfigParser except ImportError: _use_iniparse = False if not _use_iniparse: from ConfigParser import NoSectionError, NoOptionError, ParsingError from ConfigParser import ConfigParser import rpmUtils.transaction import rpmUtils.miscutils import Errors import types from misc import get_uuid, read_in_items_from_dot_dir import fnmatch # Alter/patch these to change the default checking... __pkgs_gpgcheck_default__ = False __repo_gpgcheck_default__ = False __payload_gpgcheck_default__ = False __main_multilib_policy_default__ = 'best' __main_failovermethod_default__ = 'priority' __main_installonly_limit_default__ = 3 __group_command_default__ = 'objects' __exactarchlist_default__ = [] class Option(object): """ This class handles a single Yum configuration file option. Create subclasses for each type of supported configuration option. Python descriptor foo (__get__ and __set__) is used to make option definition easy and concise. """ def __init__(self, default=None, parse_default=False): self._setattrname() self.inherit = False if parse_default: default = self.parse(default) self.default = default def _setattrname(self): """Calculate the internal attribute name used to store option state in configuration instances. """ self._attrname = '__opt%d' % id(self) def __get__(self, obj, objtype): """Called when the option is read (via the descriptor protocol). :param obj: The configuration instance to modify. :param objtype: The type of the config instance (not used). :return: The parsed option value or the default value if the value wasn't set in the configuration file. """ # xemacs highlighting hack: ' if obj is None: return self return getattr(obj, self._attrname, None) def __set__(self, obj, value): """Called when the option is set (via the descriptor protocol). :param obj: The configuration instance to modify. :param value: The value to set the option to. """ # Only try to parse if it's a string if isinstance(value, basestring): try: value = self.parse(value) except ValueError, e: # Add the field name onto the error raise ValueError('Error parsing "%s = %r": %s' % (self._optname, value, str(e))) setattr(obj, self._attrname, value) def setup(self, obj, name): """Initialise the option for a config instance. This must be called before the option can be set or retrieved. :param obj: :class:`BaseConfig` (or subclass) instance. :param name: Name of the option. """ self._optname = name setattr(obj, self._attrname, copy.copy(self.default)) def clone(self): """Return a safe copy of this :class:`Option` instance. :return: a safe copy of this :class:`Option` instance """ new = copy.copy(self) new._setattrname() return new def parse(self, s): """Parse the string value to the :class:`Option`'s native value. :param s: raw string value to parse :return: validated native value :raise: ValueError if there was a problem parsing the string. Subclasses should override this """ return s def tostring(self, value): """Convert the :class:`Option`'s native value to a string value. This does the opposite of the :func:`parse` method above. Subclasses should override this. :param value: native option value :return: string representation of input """ return str(value) def Inherit(option_obj): """Clone an :class:`Option` instance for the purposes of inheritance. The returned instance has all the same properties as the input :class:`Option` and shares items such as the default value. Use this to avoid redefinition of reused options. :param option_obj: :class:`Option` instance to inherit :return: New :class:`Option` instance inherited from the input """ new_option = option_obj.clone() new_option.inherit = True return new_option class ListOption(Option): """An option containing a list of strings.""" def __init__(self, default=None, parse_default=False): if default is None: default = [] super(ListOption, self).__init__(default, parse_default) def parse(self, s): """Convert a string from the config file to a workable list, parses globdir: paths as foo.d-style dirs. :param s: The string to be converted to a list. Commas and whitespace are used as separators for the list :return: *s* converted to a list """ # we need to allow for the '\n[whitespace]' continuation - easier # to sub the \n with a space and then read the lines s = s.replace('\n', ' ') s = s.replace(',', ' ') results = [] for item in s.split(): if item.startswith('glob:'): thisglob = item.replace('glob:', '') results.extend(read_in_items_from_dot_dir(thisglob)) continue results.append(item) return results def tostring(self, value): """Convert a list of to a string value. This does the opposite of the :func:`parse` method above. :param value: a list of values :return: string representation of input """ return '\n '.join(value) class UrlOption(Option): """This option handles lists of URLs with validation of the URL scheme. """ def __init__(self, default=None, schemes=('http', 'ftp', 'file', 'https'), allow_none=False): super(UrlOption, self).__init__(default) self.schemes = schemes self.allow_none = allow_none def parse(self, url): """Parse a url to make sure that it is valid, and in a scheme that can be used. :param url: a string containing the url to parse :return: *url* if it is valid :raises: :class:`ValueError` if there is an error parsing the url """ url = url.strip() # Handle the "_none_" special case if url.lower() == '_none_': if self.allow_none: return '_none_' else: raise ValueError('"_none_" is not a valid value') # Check that scheme is valid (s,b,p,q,f,o) = urlparse.urlparse(url) if s not in self.schemes: raise ValueError('URL must be %s not "%s"' % (self._schemelist(), s)) return url def _schemelist(self): '''Return a user friendly list of the allowed schemes ''' if len(self.schemes) < 1: return 'empty' elif len(self.schemes) == 1: return self.schemes[0] else: return '%s or %s' % (', '.join(self.schemes[:-1]), self.schemes[-1]) class ProxyOption(UrlOption): """ Just like URLOption but accept "libproxy" too. """ def parse(self, proxy): if proxy.strip().lower() == 'libproxy': return 'libproxy' return UrlOption.parse(self, proxy) class UrlListOption(ListOption): """Option for handling lists of URLs with validation of the URL scheme. """ def __init__(self, default=None, schemes=('http', 'ftp', 'file', 'https'), parse_default=False): super(UrlListOption, self).__init__(default, parse_default) # Hold a UrlOption instance to assist with parsing self._urloption = UrlOption(schemes=schemes) def parse(self, s): """Parse a string containing multiple urls into a list, and ensure that they are in a scheme that can be used. :param s: the string to parse :return: a list of strings containing the urls in *s* :raises: :class:`ValueError` if there is an error parsing the urls """ out = [] s = s.replace('\n', ' ') s = s.replace(',', ' ') items = [ item.replace(' ', '%20') for item in shlex.split(s) ] tmp = [] for item in items: if item.startswith('glob:'): thisglob = item.replace('glob:', '') tmp.extend(read_in_items_from_dot_dir(thisglob)) continue tmp.append(item) for url in super(UrlListOption, self).parse(' '.join(tmp)): out.append(self._urloption.parse(url)) return out class WildListOption(ListOption): """An option containing a list of strings that supports shell-style wildcard matching in membership test operations.""" def parse(self, s): class WildList(list): def __contains__(self, item): if not isinstance(item, basestring): return False return any(fnmatch.fnmatch(item, p) for p in self) patterns = super(WildListOption, self).parse(s) return WildList(patterns) class IntOption(Option): """An option representing an integer value.""" def __init__(self, default=None, range_min=None, range_max=None): super(IntOption, self).__init__(default) self._range_min = range_min self._range_max = range_max def parse(self, s): """Parse a string containing an integer. :param s: the string to parse :return: the integer in *s* :raises: :class:`ValueError` if there is an error parsing the integer """ try: val = int(s) except (ValueError, TypeError), e: raise ValueError('invalid integer value') if self._range_max is not None and val > self._range_max: raise ValueError('out of range integer value') if self._range_min is not None and val < self._range_min: raise ValueError('out of range integer value') return val class PositiveIntOption(IntOption): """An option representing a positive integer value, where 0 can have a special representation. """ def __init__(self, default=None, range_min=0, range_max=None, names_of_0=None): super(PositiveIntOption, self).__init__(default, range_min, range_max) self._names0 = names_of_0 def parse(self, s): """Parse a string containing a positive integer, where 0 can have a special representation. :param s: the string to parse :return: the integer in *s* :raises: :class:`ValueError` if there is an error parsing the integer """ if s in self._names0: return 0 return super(PositiveIntOption, self).parse(s) class SecondsOption(Option): """An option representing an integer value of seconds, or a human readable variation specifying days, hours, minutes or seconds until something happens. Works like :class:`BytesOption`. Note that due to historical president -1 means "never", so this accepts that and allows the word never, too. Valid inputs: 100, 1.5m, 90s, 1.2d, 1d, 0xF, 0.1, -1, never. Invalid inputs: -10, -0.1, 45.6Z, 1d6h, 1day, 1y. Return value will always be an integer """ MULTS = {'d': 60 * 60 * 24, 'h' : 60 * 60, 'm' : 60, 's': 1} def parse(self, s): """Parse a string containing an integer value of seconds, or a human readable variation specifying days, hours, minutes or seconds until something happens. Works like :class:`BytesOption`. Note that due to historical president -1 means "never", so this accepts that and allows the word never, too. Valid inputs: 100, 1.5m, 90s, 1.2d, 1d, 0xF, 0.1, -1, never. Invalid inputs: -10, -0.1, 45.6Z, 1d6h, 1day, 1y. :param s: the string to parse :return: an integer representing the number of seconds specified by *s* :raises: :class:`ValueError` if there is an error parsing the string """ if len(s) < 1: raise ValueError("no value specified") if s == "-1" or s == "never": # Special cache timeout, meaning never return -1 if s[-1].isalpha(): n = s[:-1] unit = s[-1].lower() mult = self.MULTS.get(unit, None) if not mult: raise ValueError("unknown unit '%s'" % unit) else: n = s mult = 1 try: n = float(n) except (ValueError, TypeError), e: raise ValueError('invalid value') if n < 0: raise ValueError("seconds value may not be negative") return int(n * mult) class BoolOption(Option): """An option representing a boolean value. The value can be one of 0, 1, yes, no, true, or false. """ def parse(self, s): """Parse a string containing a boolean value. 1, yes, and true will evaluate to True; and 0, no, and false will evaluate to False. Case is ignored. :param s: the string containing the boolean value :return: the boolean value contained in *s* :raises: :class:`ValueError` if there is an error in parsing the boolean value """ s = s.lower() if s in ('0', 'no', 'false'): return False elif s in ('1', 'yes', 'true'): return True else: raise ValueError('invalid boolean value') def tostring(self, value): """Convert a boolean value to a string value. This does the opposite of the :func:`parse` method above. :param value: the boolean value to convert :return: a string representation of *value* """ if value: return "1" else: return "0" class FloatOption(Option): """An option representing a numeric float value.""" def parse(self, s): """Parse a string containing a numeric float value. :param s: a string containing a numeric float value to parse :return: the numeric float value contained in *s* :raises: :class:`ValueError` if there is an error parsing float value """ try: return float(s.strip()) except (ValueError, TypeError): raise ValueError('invalid float value') class SelectionOption(Option): """Handles string values where only specific values are allowed. """ def __init__(self, default=None, allowed=(), mapper={}): super(SelectionOption, self).__init__(default) self._allowed = allowed self._mapper = mapper def parse(self, s): """Parse a string for specific values. :param s: the string to parse :return: *s* if it contains a valid value :raises: :class:`ValueError` if there is an error parsing the values """ if s in self._mapper: s = self._mapper[s] if s not in self._allowed: raise ValueError('"%s" is not an allowed value' % s) return s class CaselessSelectionOption(SelectionOption): """Mainly for compatibility with :class:`BoolOption`, works like :class:`SelectionOption` but lowers input case. """ def parse(self, s): """Parse a string for specific values. :param s: the string to parse :return: *s* if it contains a valid value :raises: :class:`ValueError` if there is an error parsing the values """ return super(CaselessSelectionOption, self).parse(s.lower()) class BytesOption(Option): """An option representing a value in bytes. The value may be given in bytes, kilobytes, megabytes, or gigabytes. """ # Multipliers for unit symbols MULTS = { 'k': 1024, 'm': 1024*1024, 'g': 1024*1024*1024, } def parse(self, s): """Parse a friendly bandwidth option to bytes. The input should be a string containing a (possibly floating point) number followed by an optional single character unit. Valid units are 'k', 'M', 'G'. Case is ignored. The convention that 1k = 1024 bytes is used. Valid inputs: 100, 123M, 45.6k, 12.4G, 100K, 786.3, 0. Invalid inputs: -10, -0.1, 45.6L, 123Mb. :param s: the string to parse :return: the number of bytes represented by *s* :raises: :class:`ValueError` if the option can't be parsed """ if len(s) < 1: raise ValueError("no value specified") if s[-1].isalpha(): n = s[:-1] unit = s[-1].lower() mult = self.MULTS.get(unit, None) if not mult: raise ValueError("unknown unit '%s'" % unit) else: n = s mult = 1 try: n = float(n) except ValueError: raise ValueError("couldn't convert '%s' to number" % n) if n < 0: raise ValueError("bytes value may not be negative") return int(n * mult) class ThrottleOption(BytesOption): """An option representing a bandwidth throttle value. See :func:`parse` for acceptable input values. """ def parse(self, s): """Get a throttle option. Input may either be a percentage or a "friendly bandwidth value" as accepted by the :class:`BytesOption`. Valid inputs: 100, 50%, 80.5%, 123M, 45.6k, 12.4G, 100K, 786.0, 0. Invalid inputs: 100.1%, -4%, -500. :param s: the string to parse :return: the bandwidth represented by *s*. The return value will be an int if a bandwidth value was specified, and a float if a percentage was given :raises: :class:`ValueError` if input can't be parsed """ if len(s) < 1: raise ValueError("no value specified") if s[-1] == '%': n = s[:-1] try: n = float(n) except ValueError: raise ValueError("couldn't convert '%s' to number" % n) if n < 0 or n > 100: raise ValueError("percentage is out of range") return n / 100.0 else: return BytesOption.parse(self, s) class BaseConfig(object): """Base class for storing configuration definitions. Subclass when creating your own definitions. """ def __init__(self): self._section = None for name in self.iterkeys(): option = self.optionobj(name) option.setup(self, name) def __str__(self): out = [] out.append('[%s]' % self._section) for name, value in self.iteritems(): out.append('%s: %r' % (name, value)) return '\n'.join(out) def populate(self, parser, section, parent=None): """Set option values from an INI file section. :param parser: :class:`ConfigParser` instance (or subclass) :param section: INI file section to read use :param parent: Optional parent :class:`BaseConfig` (or subclass) instance to use when doing option value inheritance """ self.cfg = parser self._section = section if parser.has_section(section): opts = set(parser.options(section)) else: opts = set() for name in self.iterkeys(): option = self.optionobj(name) value = None if name in opts: value = parser.get(section, name) else: # No matching option in this section, try inheriting if parent and option.inherit: value = getattr(parent, name) if value is not None: setattr(self, name, value) def optionobj(cls, name, exceptions=True): """Return the :class:`Option` instance for the given name. :param cls: the class to return the :class:`Option` instance from :param name: the name of the :class:`Option` instance to return :param exceptions: defines what action to take if the specified :class:`Option` instance does not exist. If *exceptions* is True, a :class:`KeyError` will be raised. If *exceptions* is False, None will be returned :return: the :class:`Option` instance specified by *name*, or None if it does not exist and *exceptions* is False :raises: :class:`KeyError` if the specified :class:`Option` does not exist, and *exceptions* is True """ obj = getattr(cls, name, None) if isinstance(obj, Option): return obj elif exceptions: raise KeyError else: return None optionobj = classmethod(optionobj) def isoption(cls, name): """Return True if the given name refers to a defined option. :param cls: the class to find the option in :param name: the name of the option to search for :return: whether *name* specifies a defined option """ return cls.optionobj(name, exceptions=False) is not None isoption = classmethod(isoption) def iterkeys(self): """Yield the names of all defined options in the instance.""" for name in dir(self): if self.isoption(name): yield name def iteritems(self): """Yield (name, value) pairs for every option in the instance. The value returned is the parsed, validated option value. """ # Use dir() so that we see inherited options too for name in self.iterkeys(): yield (name, getattr(self, name)) def write(self, fileobj, section=None, always=()): """Write out the configuration to a file-like object. :param fileobj: File-like object to write to :param section: Section name to use. If not specified, the section name used during parsing will be used :param always: A sequence of option names to always write out. Options not listed here will only be written out if they are at non-default values. Set to None to dump out all options """ # Write section heading if section is None: if self._section is None: raise ValueError("not populated, don't know section") section = self._section # Updated the ConfigParser with the changed values cfgOptions = self.cfg.options(section) for name,value in self.iteritems(): option = self.optionobj(name) if always is None or name in always or option.default != value or name in cfgOptions : self.cfg.set(section,name, option.tostring(value)) # write the updated ConfigParser to the fileobj. self.cfg.write(fileobj) def getConfigOption(self, option, default=None): """Return the current value of the given option. :param option: string specifying the option to return the value of :param default: the value to return if the option does not exist :return: the value of the option specified by *option*, or *default* if it does not exist """ warnings.warn('getConfigOption() will go away in a future version of Yum.\n' 'Please access option values as attributes or using getattr().', DeprecationWarning) if hasattr(self, option): return getattr(self, option) return default def setConfigOption(self, option, value): """Set the value of the given option to the given value. :param option: string specifying the option to set the value of :param value: the value to set the option to """ warnings.warn('setConfigOption() will go away in a future version of Yum.\n' 'Please set option values as attributes or using setattr().', DeprecationWarning) if hasattr(self, option): setattr(self, option, value) else: raise Errors.ConfigError, 'No such option %s' % option class StartupConf(BaseConfig): """Configuration option definitions for yum.conf's [main] section that are required early in the initialisation process or before the other [main] options can be parsed. """ # xemacs highlighting hack: ' debuglevel = IntOption(2, -4, 10) errorlevel = IntOption(2, 0, 10) distroverpkg = ListOption(['system-release(releasever)', 'redhat-release']) installroot = Option('/') config_file_path = Option('/etc/yum/yum.conf') plugins = BoolOption(False) pluginpath = ListOption(['/usr/share/yum-plugins', '/usr/lib/yum-plugins']) pluginconfpath = ListOption(['/etc/yum/pluginconf.d']) gaftonmode = BoolOption(False) syslog_ident = Option() syslog_facility = Option('LOG_USER') syslog_device = Option('/dev/log') persistdir = Option('/var/lib/yum') skip_missing_names_on_install = BoolOption(True) skip_missing_names_on_update = BoolOption(True) class YumConf(StartupConf): """Configuration option definitions for yum.conf's [main] section. Note: see also options inherited from :class:`StartupConf` """ retries = PositiveIntOption(10, names_of_0=["<forever>"]) recent = IntOption(7, range_min=0) reset_nice = BoolOption(True) cachedir = Option('/var/cache/yum') keepcache = BoolOption(True) usercache = BoolOption(True) logfile = Option('/var/log/yum.log') reposdir = ListOption(['/etc/yum/repos.d', '/etc/yum.repos.d']) commands = ListOption() exclude = ListOption() failovermethod = Option(__main_failovermethod_default__) proxy = ProxyOption(default=False, schemes=('http', 'ftp', 'https', 'socks4', 'socks4a', 'socks5', 'socks5h'), allow_none=True) proxy_username = Option() proxy_password = Option() username = Option() password = Option() installonlypkgs = ListOption(['kernel', 'kernel-bigmem', 'installonlypkg(kernel)', 'installonlypkg(kernel-module)', 'installonlypkg(vm)', 'kernel-enterprise','kernel-smp', 'kernel-debug', 'kernel-unsupported', 'kernel-source', 'kernel-devel', 'kernel-PAE', 'kernel-PAE-debug']) # NOTE: If you set this to 2, then because it keeps the current kernel it # means if you ever install an "old" kernel it'll get rid of the newest one # so you probably want to use 3 as a minimum ... if you turn it on. installonly_limit = PositiveIntOption(__main_installonly_limit_default__, range_min=2, names_of_0=["0", "<off>"]) kernelpkgnames = ListOption(['kernel','kernel-smp', 'kernel-enterprise', 'kernel-bigmem', 'kernel-BOOT', 'kernel-PAE', 'kernel-PAE-debug']) exactarchlist = WildListOption(__exactarchlist_default__) tsflags = ListOption() override_install_langs = Option() assumeyes = BoolOption(False) assumeno = BoolOption(False) alwaysprompt = BoolOption(True) exactarch = BoolOption(True) tolerant = BoolOption(True) diskspacecheck = BoolOption(True) overwrite_groups = BoolOption(False) keepalive = BoolOption(True) # FIXME: rename gpgcheck to pkgs_gpgcheck gpgcheck = BoolOption(__pkgs_gpgcheck_default__) repo_gpgcheck = BoolOption(__repo_gpgcheck_default__) localpkg_gpgcheck = BoolOption(__pkgs_gpgcheck_default__) payload_gpgcheck = BoolOption(__payload_gpgcheck_default__) obsoletes = BoolOption(True) showdupesfromrepos = BoolOption(False) enabled = BoolOption(True) remove_leaf_only = BoolOption(False) repopkgsremove_leaf_only = BoolOption(False) enablegroups = BoolOption(True) enable_group_conditionals = BoolOption(True) groupremove_leaf_only = BoolOption(False) group_package_types = ListOption(['mandatory', 'default']) group_command = SelectionOption(__group_command_default__, ('compat', 'objects', 'simple')) upgrade_group_objects_upgrade = BoolOption(True) timeout = FloatOption(30.0) # FIXME: Should use variation of SecondsOption minrate = IntOption(0) bandwidth = BytesOption(0) throttle = ThrottleOption(0) ip_resolve = CaselessSelectionOption( allowed = ('ipv4', 'ipv6', 'whatever'), mapper = {'4': 'ipv4', '6': 'ipv6'}) max_connections = IntOption(0, range_min=0) ftp_disable_epsv = BoolOption(False) deltarpm = IntOption(2, range_min=-16, range_max=128) deltarpm_percentage = IntOption(75, range_min=0, range_max=100) deltarpm_metadata_percentage = IntOption(100, range_min=0) http_caching = SelectionOption('all', ('none', 'packages', 'all', 'lazy:packages')) metadata_expire = SecondsOption(60 * 60 * 6) # Time in seconds (6h). metadata_expire_filter = SelectionOption('read-only:present', ('never', 'read-only:future', 'read-only:present', 'read-only:past')) # Time in seconds (1 day). NOTE: This isn't used when using metalinks mirrorlist_expire = SecondsOption(60 * 60 * 24) # XXX rpm_check_debug is unused, left around for API compatibility for now rpm_check_debug = BoolOption(True) disable_excludes = ListOption() query_install_excludes = BoolOption(False) skip_broken = BoolOption(False) # Note that "instant" is the old behaviour, but group:primary is very # similar but better :). mdpolicy = ListOption(['group:small']) mddownloadpolicy = SelectionOption('sqlite', ('sqlite', 'xml')) # ('instant', 'group:all', 'group:main', 'group:small', 'group:primary')) multilib_policy = SelectionOption(__main_multilib_policy_default__, ('best', 'all')) # all == install any/all arches you can # best == use the 'best arch' for the system bugtracker_url = Option('https://bugzilla.redhat.com/enter_bug.cgi?product=Fedora&version=rawhide&component=yum') color = SelectionOption('auto', ('auto', 'never', 'always'), mapper={'on' : 'always', 'yes' : 'always', '1' : 'always', 'true' : 'always', 'off' : 'never', 'no' : 'never', '0' : 'never', 'false' : 'never', 'tty' : 'auto', 'if-tty' : 'auto'}) color_list_installed_older = Option('bold') color_list_installed_newer = Option('bold,yellow') color_list_installed_reinstall = Option('normal') color_list_installed_extra = Option('bold,red') color_list_installed_running_kernel = Option('bold,underline') color_list_available_upgrade = Option('bold,blue') color_list_available_downgrade = Option('dim,cyan') color_list_available_reinstall = Option('bold,underline,green') color_list_available_install = Option('normal') color_list_available_running_kernel = Option('bold,underline') color_update_installed = Option('normal') color_update_local = Option('bold') color_update_remote = Option('normal') color_search_match = Option('bold') ui_repoid_vars = ListOption(['releasever', 'basearch']) sslcacert = Option() sslverify = BoolOption(True) sslclientcert = Option() sslclientkey = Option() ssl_check_cert_permissions = BoolOption(True) history_record = BoolOption(True) history_record_packages = ListOption(['yum', 'rpm']) rpmverbosity = Option('info') protected_packages = ListOption("yum, glob:/etc/yum/protected.d/*.conf", parse_default=True) protected_multilib = BoolOption(True) exit_on_lock = BoolOption(False) loadts_ignoremissing = BoolOption(False) loadts_ignorerpm = BoolOption(False) loadts_ignorenewrpm = BoolOption(False) autosavets = BoolOption(True) clean_requirements_on_remove = BoolOption(False) upgrade_requirements_on_install = BoolOption(False) history_list_view = SelectionOption('single-user-commands', ('single-user-commands', 'users', 'commands'), mapper={'cmds' : 'commands', 'default' :'single-user-commands'}) recheck_installed_requires = BoolOption(False) fssnap_automatic_pre = BoolOption(False) fssnap_automatic_post = BoolOption(False) fssnap_automatic_keep = IntOption(1) fssnap_percentage = IntOption(100, range_min=1, range_max=100) fssnap_devices = ListOption("!*/swap !*/lv_swap " "glob:/etc/yum/fssnap.d/*.conf", parse_default=True) fssnap_abort_on_errors = SelectionOption('any', ('broken-setup', 'snapshot-failure', 'any', 'none')) depsolve_loop_limit = PositiveIntOption(100, names_of_0=["<forever>"]) autocheck_running_kernel = BoolOption(True) check_config_file_age = BoolOption(True) usr_w_check = BoolOption(True) shell_exit_status = SelectionOption('0', ('0', '?')) _reposlist = [] # cachedir before variable substitutions _pristine_cachedir = None def dump(self): """Return a string representing the values of all the configuration options. :return: a string representing the values of all the configuration options """ output = '[main]\n' # we exclude all vars which start with _ or are in this list: excluded_vars = ('cfg', 'uid', 'yumvar', 'progress_obj', 'failure_obj', 'disable_excludes', 'config_file_age', 'config_file_path', ) for attr in dir(self): if attr.startswith('_'): continue if attr in excluded_vars: continue if isinstance(getattr(self, attr), types.MethodType): continue res = getattr(self, attr) if not res and type(res) not in (type(False), type(0)): res = '' if type(res) == types.ListType: res = ',\n '.join(res) output = output + '%s = %s\n' % (attr, res) return output class RepoConf(BaseConfig): """Option definitions for repository INI file sections.""" __cached_keys = set() def iterkeys(self): """Yield the names of all defined options in the instance.""" ck = self.__cached_keys if not isinstance(self, RepoConf): ck = set() if not ck: ck.update(list(BaseConfig.iterkeys(self))) for name in self.__cached_keys: yield name name = Option() enabled = Inherit(YumConf.enabled) keepcache = Inherit(YumConf.keepcache) baseurl = UrlListOption() mirrorlist = UrlOption() metalink = UrlOption() mediaid = Option() gpgkey = UrlListOption() gpgcakey = UrlListOption() exclude = ListOption() includepkgs = ListOption() proxy = Inherit(YumConf.proxy) proxy_username = Inherit(YumConf.proxy_username) proxy_password = Inherit(YumConf.proxy_password) retries = Inherit(YumConf.retries) failovermethod = Inherit(YumConf.failovermethod) username = Inherit(YumConf.username) password = Inherit(YumConf.password) # FIXME: rename gpgcheck to pkgs_gpgcheck gpgcheck = Inherit(YumConf.gpgcheck) repo_gpgcheck = Inherit(YumConf.repo_gpgcheck) keepalive = Inherit(YumConf.keepalive) enablegroups = Inherit(YumConf.enablegroups) minrate = Inherit(YumConf.minrate) bandwidth = Inherit(YumConf.bandwidth) throttle = Inherit(YumConf.throttle) timeout = Inherit(YumConf.timeout) ip_resolve = Inherit(YumConf.ip_resolve) # This isn't inherited so that we can automatically disable file:// _only_ # repos. if they haven't set an explicit deltarpm_percentage for the repo. deltarpm_percentage = IntOption(None, range_min=0, range_max=100) # Rely on the above config. to do automatic disabling, and thus. no hack # needed here. deltarpm_metadata_percentage = Inherit(YumConf.deltarpm_metadata_percentage) ftp_disable_epsv = Inherit(YumConf.ftp_disable_epsv) http_caching = Inherit(YumConf.http_caching) metadata_expire = Inherit(YumConf.metadata_expire) metadata_expire_filter = Inherit(YumConf.metadata_expire_filter) mirrorlist_expire = Inherit(YumConf.mirrorlist_expire) # NOTE: metalink expire _must_ be the same as metadata_expire, due to the # checksumming of the repomd.xml. mdpolicy = Inherit(YumConf.mdpolicy) mddownloadpolicy = Inherit(YumConf.mddownloadpolicy) cost = IntOption(1000) sslcacert = Inherit(YumConf.sslcacert) sslverify = Inherit(YumConf.sslverify) sslclientcert = Inherit(YumConf.sslclientcert) sslclientkey = Inherit(YumConf.sslclientkey) ssl_check_cert_permissions = Inherit(YumConf.ssl_check_cert_permissions) skip_if_unavailable = BoolOption(False) async = BoolOption(True) ui_repoid_vars = Inherit(YumConf.ui_repoid_vars) check_config_file_age = Inherit(YumConf.check_config_file_age) compare_providers_priority = IntOption(80, range_min=1, range_max=99) class VersionGroupConf(BaseConfig): """Option definitions for version groups.""" pkglist = ListOption() run_with_packages = BoolOption(False) def _read_yumvars(yumvars, root): # Read the FS yumvars try: dir_fsvars = root + "/etc/yum/vars/" fsvars = os.listdir(dir_fsvars) except OSError: fsvars = [] for fsvar in fsvars: if os.path.islink(dir_fsvars + fsvar): continue try: val = open(dir_fsvars + fsvar).readline() if val and val[-1] == '\n': val = val[:-1] except (OSError, IOError): continue yumvars[fsvar] = val def readStartupConfig(configfile, root, releasever=None): """Parse Yum's main configuration file and return a :class:`StartupConf` instance. This is required in order to access configuration settings required as Yum starts up. :param configfile: the path to yum.conf :param root: the base path to use for installation (typically '/') :return: A :class:`StartupConf` instance :raises: :class:`Errors.ConfigError` if a problem is detected with while parsing. """ # ' xemacs syntax hack StartupConf.installroot.default = root startupconf = StartupConf() startupconf.config_file_path = configfile parser = ConfigParser() confpp_obj = ConfigPreProcessor(configfile) yumvars = _getEnvVar() _read_yumvars(yumvars, startupconf.installroot) confpp_obj._vars = yumvars startupconf.yumvars = yumvars try: parser.readfp(confpp_obj) except ParsingError, e: raise Errors.ConfigError("Parsing file failed: %s" % e) startupconf.populate(parser, 'main') # Check that plugin paths are all absolute for path in startupconf.pluginpath: if not path[0] == '/': raise Errors.ConfigError("All plugin search paths must be absolute") # Stuff this here to avoid later re-parsing startupconf._parser = parser # setup the release ver here if releasever is None: releasever = _getsysver(startupconf.installroot, startupconf.distroverpkg) startupconf.releasever = releasever uuidfile = '%s/%s/uuid' % (startupconf.installroot, startupconf.persistdir) startupconf.uuid = get_uuid(uuidfile) return startupconf def readMainConfig(startupconf): """Parse Yum's main configuration file :param startupconf: :class:`StartupConf` instance as returned by readStartupConfig() :return: Populated :class:`YumConf` instance """ # ' xemacs syntax hack # Set up substitution vars but make sure we always prefer FS yumvars yumvars = startupconf.yumvars yumvars.setdefault('basearch', startupconf.basearch) yumvars.setdefault('arch', startupconf.arch) yumvars.setdefault('releasever', startupconf.releasever) yumvars.setdefault('uuid', startupconf.uuid) # Read [main] section yumconf = YumConf() yumconf.populate(startupconf._parser, 'main') # Store the original cachedir (for later reference in clean commands) yumconf._pristine_cachedir = yumconf.cachedir # Apply the installroot to directory options def _apply_installroot(yumconf, option): path = getattr(yumconf, option) ir_path = yumconf.installroot + path ir_path = ir_path.replace('//', '/') # os.path.normpath won't fix this and # it annoys me ir_path = varReplace(ir_path, yumvars) setattr(yumconf, option, ir_path) if StartupConf.installroot.default != yumconf.installroot: # Note that this isn't perfect, in that if the initial installroot has # X=Y, and X doesn't exist in the new installroot ... then we'll still # have X afterwards (but if the new installroot has X=Z, that will be # the value after this). _read_yumvars(yumvars, yumconf.installroot) # These can use the above FS yumvars for option in ('cachedir', 'logfile', 'persistdir'): _apply_installroot(yumconf, option) # Add in some extra attributes which aren't actually configuration values yumconf.yumvar = yumvars yumconf.uid = 0 yumconf.cache = 0 yumconf.progess_obj = None # items related to the originating config file yumconf.config_file_path = startupconf.config_file_path if os.path.exists(startupconf.config_file_path): yumconf.config_file_age = os.stat(startupconf.config_file_path)[8] else: yumconf.config_file_age = 0 # propagate the debuglevel and errorlevel values: yumconf.debuglevel = startupconf.debuglevel yumconf.errorlevel = startupconf.errorlevel return yumconf def readVersionGroupsConfig(configfile="/etc/yum/version-groups.conf"): """Parse the configuration file for version groups. :param configfile: the configuration file to read :return: a dictionary containing the parsed options """ parser = ConfigParser() confpp_obj = ConfigPreProcessor(configfile) try: parser.readfp(confpp_obj) except ParsingError, e: raise Errors.ConfigError("Parsing file failed: %s" % e) ret = {} for section in parser.sections(): ret[section] = VersionGroupConf() ret[section].populate(parser, section) return ret def getOption(conf, section, name, option): """Convenience function to retrieve a parsed and converted value from a :class:`ConfigParser`. :param conf: ConfigParser instance or similar :param section: Section name :param name: :class:`Option` name :param option: :class:`Option` instance to use for conversion :return: The parsed value or default if value was not present :raises: :class:`ValueError` if the option could not be parsed """ try: val = conf.get(section, name) except (NoSectionError, NoOptionError): return option.default return option.parse(val) def _getEnvVar(): '''Return variable replacements from the environment variables YUM0 to YUM9 The result is intended to be used with parser.varReplace() ''' yumvar = {} for num in range(0, 10): env = 'YUM%d' % num val = os.environ.get(env, '') if val: yumvar[env.lower()] = val return yumvar def _getsysver(installroot, distroverpkg): '''Calculate the release version for the system. @param installroot: The value of the installroot option. @param distroverpkg: The value of the distroverpkg option. @return: The release version as a string (eg. '4' for FC4) ''' ts = rpmUtils.transaction.initReadOnlyTransaction(root=installroot) ts.pushVSFlags(~(rpm._RPMVSF_NOSIGNATURES|rpm._RPMVSF_NODIGESTS)) try: for distroverpkg_prov in distroverpkg: idx = ts.dbMatch('provides', distroverpkg_prov) if idx.count(): break except TypeError, e: # This is code for "cannot open rpmdb" # this is for pep 352 compliance on python 2.6 and above :( if sys.hexversion < 0x02050000: if hasattr(e,'message'): raise Errors.YumBaseError("Error: " + str(e.message)) else: raise Errors.YumBaseError("Error: " + str(e)) raise Errors.YumBaseError("Error: " + str(e)) except rpm.error, e: # This is the "new" code for "cannot open rpmdb", 4.8.0 ish raise Errors.YumBaseError("Error: " + str(e)) # we're going to take the first one - if there is more than one of these # then the user needs a beating if idx.count() == 0: releasever = '$releasever' else: try: hdr = idx.next() except StopIteration: raise Errors.YumBaseError("Error: rpmdb failed release provides. Try: rpm --rebuilddb") releasever = hdr['version'] off = hdr[getattr(rpm, 'RPMTAG_PROVIDENAME')].index(distroverpkg_prov) flag = hdr[getattr(rpm, 'RPMTAG_PROVIDEFLAGS')][off] flag = rpmUtils.miscutils.flagToString(flag) ver = hdr[getattr(rpm, 'RPMTAG_PROVIDEVERSION')][off] if flag == 'EQ' and ver: if hdr['name'] != distroverpkg_prov: # override the package version releasever = ver del hdr del idx del ts return releasever def _readRawRepoFile(repo): if not _use_iniparse: return None if not hasattr(repo, 'repofile') or not repo.repofile: return None try: ini = INIConfig(open(repo.repofile)) except: return None # b/c repoids can have $values in them we need to map both ways to figure # out which one is which section_id = repo.id if repo.id not in ini._sections: for sect in ini._sections.keys(): if varReplace(sect, repo.yumvar) == repo.id: section_id = sect break else: return None return ini, section_id def writeRawRepoFile(repo,only=None): """Write changes in a repo object back to a .repo file. :param repo: the Repo Object to write back out :param only: list of attributes to work on. If *only* is None, all options will be written out """ if not _use_iniparse: return ini, section_id = _readRawRepoFile(repo) # Updated the ConfigParser with the changed values cfgOptions = repo.cfg.options(repo.id) for name,value in repo.iteritems(): if value is None: # Proxy continue if only is not None and name not in only: continue option = repo.optionobj(name) ovalue = option.tostring(value) # If the value is the same, but just interpreted ... when we don't want # to keep the interpreted values. if (name in ini[section_id] and ovalue == varReplace(ini[section_id][name], repo.yumvar)): ovalue = ini[section_id][name] if name not in cfgOptions and option.default == value: continue ini[section_id][name] = ovalue fp =file(repo.repofile,"w") fp.write(str(ini)) fp.close() # Copied from yum-config-manager ... how we alter yu.conf ... used in "yum fs" def _writeRawConfigFile(filename, section_id, yumvar, cfgoptions, items, optionobj, only=None): """ From writeRawRepoFile, but so we can alter [main] too. """ ini = INIConfig(open(filename)) osection_id = section_id # b/c repoids can have $values in them we need to map both ways to figure # out which one is which if section_id not in ini._sections: for sect in ini._sections.keys(): if varReplace(sect, yumvar) == section_id: section_id = sect # Updated the ConfigParser with the changed values cfgOptions = cfgoptions(osection_id) for name,value in items(): if value is None: # Proxy continue if only is not None and name not in only: continue option = optionobj(name) ovalue = option.tostring(value) # If the value is the same, but just interpreted ... when we don't want # to keep the interpreted values. if (name in ini[section_id] and ovalue == varReplace(ini[section_id][name], yumvar)): ovalue = ini[section_id][name] if name not in cfgOptions and option.default == value: continue ini[section_id][name] = ovalue fp =file(filename, "w") fp.write(str(ini)) fp.close() #def main(): # mainconf = readMainConfig(readStartupConfig('/etc/yum/yum.conf', '/')) # print mainconf.cachedir # #if __name__ == '__main__': # main()