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/imh-python/lib/python3.9/site-packages/pyroute2/ndb
Viewing File: /opt/imh-python/lib/python3.9/site-packages/pyroute2/ndb/view.py
''' Accessing objects ================= NDB objects are grouped into "views": * interfaces * addresses * routes * neighbours * rules * netns * ... Views are dictionary-like objects that accept strings or dict selectors:: # access eth0 ndb.interfaces["eth0"] # access eth0 in the netns test01 ndb.sources.add(netns="test01") ndb.interfaces[{"target": "test01", "ifname": "eth0"}] # access a route to 10.4.0.0/24 ndb.routes["10.4.0.0/24"] # same with a dict selector ndb.routes[{"dst": "10.4.0.0", "dst_len": 24}] Objects cache ============= NDB create objects on demand, it doesn't create thousands of route objects for thousands of routes by default. The object is being created only when accessed for the first time, and stays in the cache as long as it has any not committed changes. To inspect cached objects, use views' `.cache`:: >>> ndb.interfaces.cache.keys() [(('target', u'localhost'), ('tflags', 0), ('index', 1)), # lo (('target', u'localhost'), ('tflags', 0), ('index', 5))] # eth3 There is no asynchronous cache invalidation, the cache is being cleaned up every time when an object is accessed. API === ''' import errno import gc import json import queue import threading import time from collections import OrderedDict from functools import partial from pyroute2 import cli, config from pyroute2.common import basestring ## # NDB stuff from .auth_manager import check_auth from .objects import RSLV_DELETE from .objects.address import Address from .objects.interface import Interface, Vlan from .objects.neighbour import FDBRecord, Neighbour from .objects.netns import NetNS from .objects.route import Route from .objects.rule import Rule from .report import Record, RecordSet from .source import Source, SourceProxy class TmpHandler: def __init__(self, ndb, event, handler): self.ndb = ndb self.event = event self.handler = handler def __enter__(self): self.ndb.task_manager.register_handler( self.ndb.schema.classes[self.event], self.handler ) return self def __exit__(self, exc_type, exc_value, traceback): self.ndb.task_manager.unregister_handler( self.ndb.schema.classes[self.event], self.handler ) class View(dict): ''' The View() object returns RTNL objects on demand:: ifobj1 = ndb.interfaces['eth0'] ifobj2 = ndb.interfaces['eth0'] # ifobj1 != ifobj2 ''' def __init__(self, ndb, table, chain=None, auth_managers=None): self.ndb = ndb self.log = ndb.log.channel('view.%s' % table) self.table = table self.event = table # FIXME self.chain = chain self.cache = {} if auth_managers is None: auth_managers = [] if chain: auth_managers += chain.auth_managers self.auth_managers = auth_managers self.constraints = {} self.classes = OrderedDict() self.classes['interfaces'] = Interface self.classes['addresses'] = Address self.classes['neighbours'] = Neighbour self.classes['af_bridge_fdb'] = FDBRecord self.classes['routes'] = Route self.classes['rules'] = Rule self.classes['netns'] = NetNS self.classes['af_bridge_vlans'] = Vlan def __enter__(self): return self def __exit__(self, exc_type, exc_value, traceback): pass @property def default_target(self): if self.table == 'netns': return self.ndb.nsmanager else: return self.ndb.localhost @property def context(self): if self.chain is not None: return self.chain.context else: return {} def getmany(self, spec, table=None): return self.ndb.task_manager.db_get(table or self.table, spec) def getone(self, spec, table=None): for obj in self.getmany(spec, table): return obj @cli.change_pointer @check_auth('obj:read') def get(self, spec=None, table=None, **kwarg): spec = spec or kwarg try: return self.__getitem__(spec, table) except KeyError: return None def template(self, key, table=None): if self.chain: context = self.chain.context else: context = {} iclass = self.classes[table or self.table] spec = iclass.new_spec(key, context, self.default_target) return iclass( self, spec, load=False, master=self.chain, auth_managers=self.auth_managers, ) @cli.change_pointer @check_auth('obj:modify') def create(self, *argspec, **kwspec): iclass = self.classes[self.table] if self.chain: context = self.chain.context else: context = {} spec = iclass.new_spec( kwspec or argspec[0], context, self.default_target ) if self.chain: spec['ndb_chain'] = self.chain spec['create'] = True return self[spec] @cli.change_pointer @check_auth('obj:modify') def ensure(self, *argspec, **kwspec): try: obj = self.locate(**kwspec) except KeyError: obj = self.create(**kwspec) for key, value in kwspec.items(): obj[key] = value return obj @cli.change_pointer @check_auth('obj:modify') def add(self, *argspec, **kwspec): self.log.warning( '''\n The name add() will be removed in future releases, use create() instead. If you believe that the idea to rename is wrong, please file your opinion to the project's bugtracker. The reason behind the rename is not to confuse interfaces.add() with bridge and bond port operations, that don't create any new interfaces but work on existing ones. ''' ) return self.create(*argspec, **kwspec) @check_auth('obj:read') def wait(self, **spec): ret = None timeout = spec.pop('timeout', -1) action = spec.pop('action', 'add') ctime = time.time() # install a limited events queue -- for a possible immediate reaction evq = queue.Queue(maxsize=100) def handler(evq, target, event): # ignore the "queue full" exception # # if we miss some events here, nothing bad happens: we just # load them from the DB after a timeout, falling back to # the DB polling # # the most important here is not to allocate too much memory try: evq.put_nowait((target, event)) except queue.Full: pass with TmpHandler(self.ndb, self.event, partial(handler, evq)): while True: ret = self.get(spec) if (ret and action == 'add') or ( ret is None and action == 'remove' ): return ret try: target, msg = evq.get(timeout=1) except queue.Empty: pass if timeout > -1: if ctime + timeout < time.time(): raise TimeoutError() @check_auth('obj:read') def locate(self, spec=None, table=None, **kwarg): ''' This method works like `__getitem__()`, but the important difference is that it uses only key fields to locate the object in the DB, ignoring all other keys. It is useful to locate objects that may change attributes during request, like an interface may come up/down, or an address may become primary/secondary, so plain `__getitem__()` will not match while the object still exists. ''' if isinstance(spec, Record): spec = spec._as_dict() spec = spec or kwarg if not spec: raise TypeError('got an empty spec') table = table or self.table iclass = self.classes[table] spec = iclass.new_spec(spec) kspec = self.ndb.schema.compiled[table]['norm_idx'] lookup_fallbacks = self.ndb.schema.compiled[table]['lookup_fallbacks'] request = {} for name in kspec: name = iclass.nla2name(name) if name in spec: request[name] = spec[name] elif name in lookup_fallbacks: fallback = lookup_fallbacks[name] if fallback in spec: request[fallback] = spec[fallback] if not request: raise KeyError('got an empty key') return self[request] @check_auth('obj:read') def __getitem__(self, key, table=None): ret = self.template(key, table) # rtnl_object.key() returns a dictionary that can not # be used as a cache key. Create here a tuple from it. # The key order guaranteed by the dictionary. cache_key = tuple(ret.key.items()) rtime = time.time() # Iterate all the cache to remove unused and clean # (without any started transaction) objects. for ckey in tuple(self.cache): # Skip the current cache_key to avoid extra # cache del/add records in the logs if ckey == cache_key: continue # 1. Remove only expired items # 2. The number of changed rtnl_object fields must # be 0 which means that no transaction is started # 3. The number of referrers must be > 1, the first # one is the cache itself <- this op is expensive! if ( rtime - self.cache[ckey].atime > config.cache_expire and self.cache[ckey].clean and gc.get_referrers(self.cache[ckey]) ): self.log.debug('cache del %s' % (ckey,)) self.cache.pop(ckey, None) if cache_key in self.cache: self.log.debug('cache hit %s' % (cache_key,)) # Explicitly get rid of the created object del ret # The object from the cache has already # registered callbacks, simply return it ret = self.cache[cache_key] ret.atime = rtime return ret else: # Cache only existing objects if self.exists(key): ret.load_sql() self.log.debug('cache add %s' % (cache_key,)) self.cache[cache_key] = ret ret.register() return ret def exists(self, key, table=None): ''' Check if the specified object exists in the database:: ndb.interfaces.exists('eth0') ndb.interfaces.exists({'ifname': 'eth0', 'target': 'localhost'}) ndb.addresses.exists('127.0.0.1/8') ''' if self.chain: context = self.chain.context else: context = {} iclass = self.classes[self.table] key = iclass.new_spec(key, context, self.default_target) iclass.resolve( view=self, spec=key, fields=iclass.resolve_fields, policy=RSLV_DELETE, ) table = table or self.table schema = self.ndb.schema task_manager = self.ndb.task_manager names = schema.compiled[self.table]['all_names'] self.log.debug('check if the key %s exists in table %s' % (key, table)) keys = [] values = [] for name, value in key.items(): nla_name = iclass.name2nla(name) if nla_name in names: name = nla_name if value is not None and name in names: keys.append('f_%s = %s' % (name, schema.plch)) if isinstance(value, (dict, list, tuple, set)): value = json.dumps(value) values.append(value) spec = task_manager.db_fetchone( 'SELECT * FROM %s WHERE %s' % (self.table, ' AND '.join(keys)), values, ) if spec is not None: self.log.debug('exists') return True else: self.log.debug('not exists') return False def __setitem__(self, key, value): raise NotImplementedError() def __delitem__(self, key): raise NotImplementedError() def __iter__(self): return self.keys() def __contains__(self, key): return key in self.dump() @check_auth('obj:list') def keys(self): for record in self.dump(): yield record @check_auth('obj:list') def values(self): for key in self.keys(): yield self[key] @check_auth('obj:list') def items(self): for key in self.keys(): yield (key, self[key]) @cli.show_result def count(self): return self.classes[self.table]._count(self)[0] def __len__(self): return self.count() def _keys(self, iclass): return ['target', 'tflags'] + self.ndb.schema.compiled[ iclass.view or iclass.table ]['names'] def _native(self, dump): fnames = next(dump) for record in dump: yield Record(fnames, record, self.classes[self.table]) @cli.show_result @check_auth('obj:list') def dump(self): iclass = self.classes[self.table] return RecordSet( self._native(iclass.dump(self)), config={ 'recordset_pipe': self.ndb.config.get( 'recordset_pipe', 'false' ) }, ) @cli.show_result @check_auth('obj:list') def summary(self): iclass = self.classes[self.table] return RecordSet( self._native(iclass.summary(self)), config={ 'recordset_pipe': self.ndb.config.get( 'recordset_pipe', 'false' ) }, ) def __repr__(self): if self.chain and 'ifname' in self.chain: parent = f'{self.chain["ifname"]}/' else: parent = '' return f''' NDB view for {parent}{self.table} Number of objects: {self.count()} to list objects use .summary() or .dump() -> RecordSet (generator) -> Record key: Union[Record, dict, spec] to get objects use ...[key] / .__getitem__(key) -> RTNL_Object ''' class SourcesView(View): def __init__(self, ndb, auth_managers=None): super(SourcesView, self).__init__(ndb, 'sources') self.classes['sources'] = Source self.cache = {} self.proxy = {} self.lock = threading.Lock() if auth_managers is None: auth_managers = [] self.auth_managers = auth_managers def async_add(self, **spec): spec = dict(Source.defaults(spec)) self.cache[spec['target']] = Source(self.ndb, **spec).start() return self.cache[spec['target']] def add(self, **spec): spec = dict(Source.defaults(spec)) target = spec['target'] if target in self: raise KeyError(f'source {target} exists') if 'event' not in spec: sync = True spec['event'] = threading.Event() else: sync = False self.cache[spec['target']] = Source(self.ndb, **spec).start() if sync: self.cache[spec['target']].event.wait() return self.cache[spec['target']] def remove(self, target, code=errno.ECONNRESET, sync=True): if target not in self: raise KeyError(f'source {target} does not exist') with self.lock: if target in self.cache: source = self.cache[target] source.close(code=code, sync=sync) return self.cache.pop(target) @check_auth('obj:list') def keys(self): for key in self.cache: yield key def _keys(self, iclass): return ['target', 'kind'] def wait(self, **spec): raise NotImplementedError() def _summary(self, *argv, **kwarg): return self._dump(*argv, **kwarg) def __getitem__(self, key, table=None): if isinstance(key, basestring): target = key elif isinstance(key, dict) and 'target' in key.keys(): target = key['target'] else: raise KeyError() if target in self.cache: return self.cache[target] elif target in self.proxy: return self.proxy[target] else: proxy = SourceProxy(self.ndb, target) self.proxy[target] = proxy return proxy