./0000755000175000017500000000000011757171516007342 5ustar plbplb./oar-2.5.2/0000755000175000017500000000000011757171206010561 5ustar plbplb./oar-2.5.2/third_party/0000755000175000017500000000000011757171206013112 5ustar plbplb./oar-2.5.2/third_party/tarball/0000755000175000017500000000000011757171206014533 5ustar plbplb./oar-2.5.2/third_party/tarball/taktuk-3.7.3.tar.gz0000644000175000017500000227240611757171206017650 0ustar plbplbrK[w6__l/!&W+H}$u-/Ʀ/73 fk+/i4iF3@ n[l@<_s{gO2iQklV@a (Kru3XUfޯ}n}^5g㜍Yp{cDSaǨxnlfٜ}Is$hzcqm-,/PIN"]x4 E9 |ai`_al׹g9."IϽ; J$|$uՏ,䟻K/z=Cwͦ>6 >fjQ]C@-@G[<}Ym L;#sg=lG᠏;^{4b!S@;:d?j{5+@m}Q/SVQ&P;.MX0g+AwzddخsCYHg lDZc3c@yY(Xm[ ZP?_04uc .2mMEȪ`̺ d1+Rp\'`nŀQ[vLm ՞z?#2i#]PgtIL'HEUxM( U!TMI4|[R+Hax>Ws1G"E hyg.M2&B0qQ],gqa}%8nV:H*9 51 nZ`ce[>)8 ВvL4Dw5eX$A!g}8*M@6@ ;Z{hMLiH3|{`Apzxvpp_ CD麰T&,+ P.yjr&OZ!lLN1`SO.pn-DJ9cNIkHVl0gfltmr n͡;lZ19:Ui2LJOmL=>RlDOkY]Qw23hzӞyG@<(=l6$Yk9Z`&BJ~L; s.(7Q{0j fZ^yb}?cGX(NnJԨW`UK"bt~8dH쁇 ahb#\Q :} 5`Ƈu; -^oGJHSmmtGW>DqZn&]K{N3$n@"DPuvUOI{dFMA#un}KY^Zcvo_1_6{1ؽZ[_*dxЅ[ɻv^h#Wt*Bɠ3h')ϣ0==pgظ?tYKv봯B0ꪣSE1tXrzB SֹX q$PS/|aL]yЈMBէP7ŧ\ жaX&&۝N |w m@ln8-~Fͼ`EhtezS?Xfǃ6uDPunіq ,^2hL@2GR_lC\1b z.k t xy`S@@VTGGI.1ph`f,L:dY| E~/! [*l[20 d{{QaH% Ug55qۈKL}sK$V YY5`Rā4d0bs׌<-Lf ԥFd$FB V(>W+@,;Un-8Cm<=DFcY/r3~vw#氲@8U)2U &2Um׸M@8pNJjմ|U]MDtƟt 2y˖b|t@&iȀȨqVJ2]UP&S^1Tiw24w{w i0"w*!*y:%9t׊7e3;Dg$o@d @q66IWA~)HscDȝtfM[)K{rC%#SIebJ$_rQY!=Z5E_Q>o #ט'F2&Vc^7:E3"fpzE{7!G^ EEstwMum lMaאn A&Θ/U3롶D1-C\28*ȮeW5~u*1 HRU,B>HгfY7nFeLJbn]L;y])YU۪zUyO"궥W׊<<tq'M8@ڙX.0XB&`H q9xW-i6!*RGk+H,! fK}iH䖾M5vX l&l/ۺN0EVToY'U,&qS44ļ4 ]Z)@ȏJeH]NTVJU"GtEa)oSAQߦ_ $e-) B/c,rPi<_j[BQ˖o{}:`uȼS+pMne1}UEj2cYCcGǧOYkKF<:θDY\u=wZK qXNKe"^'J$ cy\ |I{ˣ0hU.#"أQ`S!wup_=؃ƍN'MkX eBtK+pk qh?/#. (jui\щ!_wKw|6p}%1rѕqZg-/9ro^ F]!^CV+!<)\v xD-aؒzNn_T[8ƌ8}Ɏ,߀A3"ҁ@3٧%хJ9ظme% ts:^yqڼIuq]pxItMPݽ ֢}!y\jK6({Cc`y|jv 4 Y]O:[iēG?Vٖ/(L6u'✝֟ѱ(i\)̭#}ԔRY(-')H֓JQQ&Ͻpp̣cD9RW+-^ 0$N  FIPJ,E7(SCT"̚q ëAx=~nc{OV)+ ı:e0I"ӣqf m1p;mQ™YBg5lFhj|L8\G#,>Ⱥ`AN/7XtGLዉ* p>]ZD֋KO[& nE.b^`|{Nb@h~V@OƛaD83\5Zcbdps<ɁfpҨ=P?+ m x%`;Iغoȑ<6 Wಜ0ߕUō,u\>gZY$ <ʾXLA@,0 kiƕ}P8ZYS3#gZ^.;a< }Ym-bDY.  `0l͂shRDɚ"PYa=ot saSZ'QTERg(Yx3MXN^Q(n{_:E%r!((;]UɽƉf [p of̂Ŗ(HS9^ lenN4ɧkL<5ZX?]y+:I?itz-+׹o%V ~gg,O.r ps`V GiylH@ɭ汖ɦXǧlzM¼D5T}"(sBaU?8<'^KˁTRբa4);T1|(P/bxEj~h6#/mh~EЌK2=sFjAnE-w޴N<3ɽcZݵשSg?tEn|AO( ?N[r `px!s* $!h`%lxj,qD>$Jst=F"ƏqaZeD^l6SE9>YC~!J DFJ2 hG.ڻnY/s?-.`~z0qZD.$EeJ ַ͟"d",i(gİ)% }ۙm/nn=YY,o߾on?\+>YшX+?MTk):hh:X:4* .)6h=V1ºi Q8LY'+1 gFj=yr~rwmʬu~hLOӽɮEdN3=:<4Gn}ij gSZ~~> 0V:Av/C%M@G=y*#+8uOV>iwfabvpfV*d6A-VbɹtC턖?cV{v&,MͰܓonVrLNꞯ{jaUP;a eZP Gσ <6W*+ncf=/],&auZU|!ZMzķ//!*Q^=hɣ/~p5V7Ud cRKmoTD_ZjmqUP-#/đ/ZKjyѲBEǙ!EsDs2Dg f]<<ӛc9k^\{"9¯,ѿ?3#!U%.!&YpuIK*Hf5:1 b- qK \cyxz~Z?BNIyV ss{o^A5cko ky`q>68Em'Ofz'&sXCt%.$F ށqT! _W>Z4^* 3T+@D't̫-?tZ t]"u4p# +`5c #iь5“-l[x;$՘E#ܒAwxZ T96Pb Lh .U #̶hJF)KCg1 njjoU@e:>E.>T2JZ xRrf]5hIoMA F^7_mTvVi4d O wµ EBY"sjt'+[2 CʢKbQ: jA vgwl :X^@,ܶnйEr=|q@u#XSe]1ϳŎ[˓pR/ eǷR)KfFbtSD$?iToL9ظA)mibƥ?#8ޑ7GC1̿!KOyW0flr]Nq#*Ф<ѝz䲚Ɗ>CN1O ħ-^_VX 'w*/[ ;p#Lb,,>q"1Њ42pQCI$ІlUJa"=9h Wol);N {dI==sÊBލw.Q+OjLFK|Bg_w|oa17p\M7\(|ѻ% 5 HZi Ss>fAM1h7j2@׫:KK#Хewa cq{}8CpcLđm6?)h# x UIV"J !rp1]T<}تtB thϮzZ_]kE>kg wnUK3\ju%ƌ {4h!]#rf/ӥ7ϿM-NTr k2EI.q\HC #KKѷ›;QHin*p;R.{Dq6,,MŐیHbBFX,..M&%&zXU"Мm+Em4*oB^ZQ;/#6i'ho=H-"aZ҈Vq}]jq[ΐڍG,wduHWo:=avC@!ߘ^=Cw %jW y }=Surce^y`Bviô 'Yz#LYͨ ZFolol._j)> \V~o{Z1+mvoРIȂFdYxn^  h"֪BoQ(|>(i#U2NMZB}I~A]P,o3nJ}$JtZ@.; 8nUzQZ\8FUH'["27¤4E3߯ˁ *xfo"䠱Ltaagp&ޠ}x͌z_b7pM_L}&SC1O ƊX%{چ9ZԸb2vd'6!RB63-.R3lԫ֋PSeXY8nwIuC}QqeX1qln1x %Z9mX卉M37LġN6c,ZF:s-e2xOj8oXMcEkF 0 R=%l^jVt&i@b ,*)HTSu-K2izOu%Du[8>ZMBEaT<9dPZRC5($5xh5CNs%" ˄}ath32G!V8[C*7Yˋ_- gz0JO.&(аaW6~mfYf685mO@!+wu9{b]۴ &˶=Yط'{N3SfrΚѣkUGf&Ya^oŴl.'Yw8 k.a QT['!׼GC5o=.cM[H$2}~ pFa sLFv,] :O '5M1Np&{$6^84nh鹦>d/8T< p-ZDa+5CL鰎n52֑h9$ ]? <na0j]ZU7gmܵ︒}?6>FųaJF9"HTgD$?Pc<ш@ˡq6K-Da}g+0gg\yH=g ?.W.ilj}A)iau({g+uh5#y\"VvS~T"c"z=Q,/J0`ڋ[ѫZ=]c *VY%F;Pr/bN!첡iUM8e;/&ӘfmJY_Z&]7sdeN¶,JH?&ঘ[-q{6!k0o VC5'QP}c@uu Mr}0̤DfMi}JDßug u!7C .Rh Yit;暏@h]3$۾VAyL2im4vmLmۤhJE̾d2LkIkxLA8FlJ) /KKE~Y][.U/gL 9u/e 1:DrR1!ȦB* G=x[b!K.PQ%|:oUqXQi>6F@l%S?6|K%VajP0bIx:ʨ)  @}tq_QG\hB65;e|d/C{|+e(3 @V#c- 3XXxR[e wMuIn> b+No2`p !81D,Zb]ۺӶeb9#Cb/Q~`gఐ `V=TYّ ((?[>AJx\4Ɖ(iM&gQ#]kW{+f2:@grUčiLq,6qv>?}pq>̞zR\/Ft#*B@֏/ U PQ"C 4xT?(R,kkeywwT&ɀ* e` qF㳪!,,A%}+ F(DOn'INt2uF:瘉ڢǕ{,ʼZWڱP@twhXbP$0h"[ń2H +tLjFq5(B=Ps a_!zq?g"GRwL ,Ь]a4-_Wj?^/|<JCdW ;_bRAy5 . kf][f%|[9 Zc=dUɻ3>y`, i¤W蕍99:Zzc1ެ$ 'r_K' ǿ,.TN6K=Y+ٻuЋ=\jGeIF<,E񅚹t2lX~A1+q8&1cSu>OKȦ?-(ٜ{axBL*E(I 8EIQjKy@p,F&j> V{>S]͏j!< 7@s*UPAxBVR;:Chjژ.K2-)j!m-iwL ȼjCh,ˌ.-Fjc{;/-0UuBIa{%%Ts:#F'ٿiw90 oEH9 > RK3DQmr;X:o:h`;]㒮uI[`K"M@,WB|WbnR_qQM`>qLXZH prو]2(+b,LCl)@X̃%hr|i\OOj(*}4(s63)r^ ;+xwњ id}`|DU)o"V=n~ktu&Bi1L!Ej%yE$ n !uzjq $T]t0LH, FsBtTڅ?Y|ԗZqn.y]=}J]tF}in9h}TR7kST/yXOGŠ@2Em?n,].29HYtL6PhaP&+jQIqH*ʻe,?` s/S]w2VlS9 42&SӁi7@.(VۜodbUOx+a[*Zlxr`\x'+[WxМ|HwNvQYO)tϟ[_ơB80msJH\3v>fX D4yj1 FUg>L|rq&U}e|qQ :6vUo+tB3 2/,8V ,z(Il-O;ī&HnrXT}2xayS+uH||p%T RT6ͨA$;װxj5B5ʸ=8myQR9G pيS|*(l@垂MmlMl"~@3˛I [{7Fa3 ֳo:×jqQԈhl!ghsp :09{S?Ms~iˇYF6Mõ[I.,r{-a''&mE-ENc!Z %R(F|~ԁL*- 1{Wt;  *MwQ4m<k1,Jۚ\vbSHyxs9gRad~m!1ވA3ubkM%?;\[)y'x:<x& d->xQ-A$Pit 5Me EEcAnbz U߶%[w.9L-H! 1ZVLm2T3_+[arᆾESKӸ~tt𶧍8惷&BNM*I` M,m9"d.@9Ò-溾R/ g C}9f}VFc';3%"F,r©S)r&˞V Q@OӹqwAt*Yw؄.p\櫭JgbS>{9PN {^g@HڮKKw[~\2?ާЫҴVCR"~k?Vi|5oV`*3,^XՄ[!L'`1ٕ-qk C:VWV*1ż\ hIR7P0lU4[R>3QR"A " V\+cVM4n|`*DŽ/ [?h=mYuFâ!--J'$ϙ*WF/7y 0^9^@&Q^/R1._Cor:aUO~L\+' Iedy,%NKι((!# Ws)1ӜIPM\j(\8o˭0uHMb뺍"^Ixԫp\?Gozs[msqC_ptu}sʓϞwK4J/+pWw``XKPUe־yW]%VfS fJEVA:22="ubIQ9݁omݎ3A>G`s6q|S<ޱcB t"׍-G^V^0/J]P #{veGP@7gzu]Tt9e_뻯P)ףZ* k"X^}Mrr*rs/׍&W~~ 4d BTr Z6J0هϲ }'1t) 1Bڜ$qUry8#9#:1F#OJ"9l,xb錞 :1(hUGYZo Y4Nl}{(oSk"?Q5Zx̗3 Hf&ķ/7_aBj,tf;Obv0>8nlxΦK졛~l}4N͍cqXIykE_VBRU ب2ڤKꨠ hS9YRDpq ioM=hPZR0t\I} <|'6I41mc Nzgk_o֤gKA|"2Hrؚp+Ka-ΉQo#?!ruxt5((h^36^8ӷ`.5S'w.IYP Rߺ-S E3i(Xt$a&sٜW}7H۱1#&i?@2HGRz*Q'0V!0 '.ҘY1 ~2fb`2f !1 ,1YYC,d. }M!~;%bN\Їb̈1EbEZ?*@ϤkWL7g p<|h**ڟi(zN˸˻iħQɱk|:+I驑Ndkke,(CohkdۗG <;VT&"v\̆mBQaoI}qdĝx1V6d6-6O;QZ -i}{AQUH4wyw"Z>"Zkx ey)2k+)1SPQwP ]]GXCqVWzncA۪ 'M` 8ch4voe `a n΄QӔ/ sFox.鉬VtUMhmtzi+Ȟz2K嘼:(LbH<2 !bnװIRf^:lUVE+e57p/ܛ3؉Z^d4RTnU'6d kbE,e,Z=L5-@eM2D74bVz0JD+V E`(UCQ%ѤK44.nkUČ#:tNey[&x!Q `Ur#U/t$sA[]fBvPcYň <$3/9[h}x`\pEpj_hjjhgVKIᨶ<2.L= AsqQ{Ү@7 pʹF.Hy>k4÷_ ~7E4.܇OcŋOG^?1)h -I_r!9KgU93))IansbtN#pX& !Tk6^2<4w6m^afg riø~ ;6wP&Κ~/zya@Yqz@H D&` w\.JژB!1Bˑ.Ka흅,*l0"aӆ+kcl\ 54;c¢= qX1v;h694TGklJan&|fpԱ^񫖹6.rEBl1Kr4s`Ko*gUk6_)L2g»×jAs| }>& jۙ%P UxlO&='᥋8LEH#zx9Jrs&Bzx:?ѻ&xy@9pA+M76ARٌEϟ4dd(A-&|Y2/ $b{"j6*|Y{"^s>1w+Y,\* qUV`Ŭ3"@FuWPI'IQ"Or!lS{;U5 fKF8bt$GzϞ ``d4LB'xaZ xem\$õޖc}'< FeW&n Yf(BTbBNRgK$&M5 ݳ0'ZUEP5ݚ<<„ HdݱP,~<ǡEϜ!S<,2nJxυzrccᦆiŊ W[JiSa| xʛQ% tRw{-g(6}iG 'dT)YK(sT2ST \:nԧ0ӷf t <(z; r% |ism:4<~7S;옉Dd\?"Lzn&|3'ю8Cr9~~L$jD+/&^JtaD֠2%Bǁr Ʋ9Y ˥*V0テ+28k4ʼnBCcʹ%*%;~NdƔ^)/Kum(^gIpmT:ެFLC!$}@_Lcݝ<}B^7BFNѲKޫՂ;kT0Cו+N0Zȳ9³2J!8S8Jɇa%Hs/ktk3uQ49E; a`p}^*<eӵA@C ~ #x“ Š#&3r<nUtM%U6?𜡋d>فML&qzɸt׹i19G2l g 4I$W[3Bou=5^,jd(+Y> ʋQ?ݶMqDcXQlco86;PikZ-+Wn<ŐmٟeL-ѻI~7DLz쐡o8d+Ƣ (YrͬViH^9?6uB]sp<@@_ iɰj${y׏6r.ؑ֕wGG[ {O874^P@<1M^}즱7d-j )~!vWIZ 5#r0tl'/ xb&|.c0Umc+QJd{D2٘ȌgW[(Ģ8IPLhﴀgC q~zeб˖=`+kT>X߅ l/tKHyuR2v웺Ni,RNYd^4ZZ i(ބecCGyK k/Eq, Y~ ;l.1b =69"J7O2BMCWbH1:ѽ_,يIr"NA ə[L.IZxp KESѓ)iKy+0ad5ˈ8ZÜM)γ;m>M|v|1RqQ%|/hn.IEb˥6\ nZ5x54͌mc<#N}D1mO @irYAByǶ\2{IX[?wtRx<Q'OˤofJj]Y>Q̋K%0^h.i2녨$"t?S/ sL|d/c,Xl4 X+mo3S͗X )cáeS/Z77`?b%iji̒Bt` <3]uDPʞץL5ґ Gx[?4Z}r;/7D.sJwX43w5 \UDV"@ 1| !۽þ(Y%y.M Woc^,ɉų5N A>gGJ.5#"$gN-`X6.2D3]#B$~3O?L](&MpΦJݒƞԏGv@/p}wൈЦ B$2pP;SY: M-g I5Z)RX^.XQ:( D,wrtgiB\< ,ޜ\hst>uXL;4ݗ6ȗE-UXF)~ j-w8H]ckY+!]<j\V(ȸXgSD<հ\]L9Šh?'zR.[9۸Dy)rLE1Hr{PcQvVo5b!ZYӒkzx7i iO͐aG9[\Ǒ{Wd%\5[WLpM`m8f,GI͘¬zN7 ^ۮ)SGTW; ɪg>tD"wM}qdXZhs+ONO?YOsYG1-Fᆆnr2/eC dgQNl~@ˉTL}eGGv XJ P@qȜN[H쀷:r2r[ hp]ghj=<0hET0=KAq.=Zh{ܿ\p`tq^L7v*w[{ܯza4VHAUl(#?!4g!E-%"vLSH@pV7yuy" PST-LeD= $Y3P7YMufUs*ऄ/EC ՓdF#B]6@QvVH17-|ku m]r_1,o.}S77%TiDX}sSY(EojpRyy#d>g" B:Hraﱢڲ-.~QNy]ˑZ-DђfH1=G_s8Q9ߥu@GMf}w0 mZg9Eh8քO)DE,Qy5 (`E@iu i/Fg%P-8+NR} =1d;&-UgOl;6@X"$t{'(v xZ1W'-ӌ'ăQf[&~6<]SW& d. D5IM۷6﹎OnaXպ5R Zg;~NguPNiff3Z8~ԧwV[QLùpkު^T x6w5{4Nw:[`_ ˘#F lG!p|(G0^V/nB3ƒ{b4g)nW5s\`U\Nڋ%Gd41MSt-O9pwySNl`bd}jaYKos׼/ rԠ5t&zV'Zi4;|Oa~=]H\hIb{ƒШ,:mACk_3ьhTsU1բ9ߜ,'jo4)">᳦oa[j#o$9/22x=nق U^u|\;=.m\+_n7r 'Zl"KDr~+xGQѷrS:тrJV`̌]ů$v89'+/q;%~>[2$]PTmViMR{ȈbV;/rC(|x>YYY[2y?e]\.+<^Z{Œ4MYN-tv#?,̨Fc{$9}m0hj0 ʼnȍ1"7 ÏJkqyA*>w1[[%!OZxD."GOzD O=5ԕKye#BZ+f6nT8M&lxЉQr ȝu*ӎkdOI-ߴ)΄èi80i?jDI 0hᡆnZq}פSeޥ6oRL@-WXZFEbI޾XnW*5Xd{+ D|{ b$bBRDTsU\NY<}^?ϧ'oy  n$"R(]~>`7'p/P`~i_ZpiJϾZ_`: D⠇q=EHsaٗʛI­ig&^UJDt˝F u#u;A {\s_5kM#_+ֶ7owh31 '[3~hxf;6-<5j=Y1wFϦ;gЎX`>(跊SߝBL_")sLIQyO /!UXDہ>EV.WO_N"thր`Uk u' yǃ]=)ǟr?&Qw |b|k<<XBn_9Ơ]h\`ƇByKw('}''%ܛo@gܔ__g1:4YF<3"(fG''~1z+Rp7Pj\cVjIf/#MaUE3EEYCE2k2k,mE8tܑO>|)Qkg[ﲾ+U~Cgb#c8eouju>V/$ej3E78wEeQYpXf+wW E@anº0.މ0Q_b}O:`"m^1J5g &=;=NM. :V 1{t 8TbL~21DH:![֭) y6"nuMnI炚q1&Yyr..u}i_(fi $Κi14._8hkR֬u&^XA3C4C3Rz-0+gWħOB(Sl2.I<Ǐ1٨&Xp pWțjRߪ:}ȇ3]U !;ІNgwW^ͪ/L\??)}VoQcvxf]juz}5|hFa{YK ^ y\MDM꿪TMPkPv)q9H1\%u6nXU?CcYhKb1b*!+D.k?~Eˋ"F׼A&P ^aQ%/sZ{ 0%(PN|05\R 9VȯwF~t]9sz^}ͤ G0\DcTdtB wT14pICcCy礡t.G49 .E+~ ̲p"YZxlB};SussR1vtC+lwEN. 5c˥r=Ha5tk3Ųlj&E]"tPwd$&ΰ` y:S˰&0x@ki'e{4 " X& skp ǹD#S Ku08&_f4V}LPi,%l5N ѡ'%ܩFIϔPQ4J_\3MȹEj6L8mnLQ 5<-RP8NY2 [)p?K]G,hTĖ\6'ȷ">=E*y`,?I#&Jkŋֿ4WЮj.ӽ=Z W |mWhmD`b[u4[s#k+5=;؜11ۨ"AœAwYUb>,4/,A#CR 7H <:QſŪZ~0x,uïd8hԊp&<>ՑEFjVj*dmVGIiH8%3G~ 8!:Aky%Q9:BS 3!Hp:I#6 ROtLFT-6.(9(|y ('d; UUKQ0|K/^UE'q2b-E$ H>1W`#M w2IcDG!ev:549GBNW7EÎHX;R' [Nb"w#sіB Zz{e#-j@gk(9YsMOU:-``|# 3 0 u.H"W3w=( :a7o~Q+R\v a+&[],Ƞ)-H"HM~|}DPtruImQWVEϧWWgMj VƱ9Nz JзXA2lǙXq| 444|i>}tѧ)F) >#> &)Ɛ)xx^m#jL̶mDڡ6&R'qxrvc>?)81Pm68)oP,C:MMo -26 |ƐKT%E\,Nށ=:劦="ob'l ^.e~X*%Lc&a[ $=G8M{[((7TEV vS aeѥm56M̍upZIvL(UDϰfssRb`_Te4a _/.3US!\Y-¬"bT\y$~Wn+sP܏[UBZ3Ə7UI>n`Y6WU/0̨òk%*NP{aq$e(pByV]FGցe;C^M L1EA_}Ȱ┶a]E-tz毃qy\/?.Q=oXwۣh@ϣ>a2^ Uw<2"!xEŭa\9Y3 {c8uuᥖ,aymt݅WUX8:Zp Ԭg$zpTRE `ِ8 t`Vlzaa䣸V&j.GUߔ]@A lL<[qe9s'a]%neK_xXi.80FhF&V>z !%'Hd٭Xv׳UZZ_XD'"uϿ 8}i+B_n˹[Hkq%ts7i5EB:WAH 3Ҕ]e|]k-"&~CgKʔL%5,+{xX{@is)̊/!Hdzs`31\Ӷ{iE~yE^Ȼ?$dyu״l^X?-1nJ*tQ`Z;KZu9n.8ƥ1hle+[+0%L+ښRVt`Sb@!Г^.IРcZM"$-bpLun5&!"-Jc+MY/xzyQb];TvwG.ENDH!Jb~+po0k }U1jIZńNǻQ ~0 T cS*eM5Sb5a &? NP7@mn"M'o؆{r@Kt#M'Zz/VX맨ZlJр3;z΍T/ :\T/Gt>U(L^csHV&CwQU@/"y力tFAx`\ "z~lF}QP6&\+%^$r &7iD~8'[lC7ە^Ӓ$wm1C.[Xf> vQmFIzLO}0ūGpZ/$ԱuiY{ȌInfc5cW"|5Y9K ٪ьS k_:|{U崑m߭E_V'1JP]3venY+J=C7-V#1B $*eѫoiϤuю0zуM j1EʒLu:OPcy>Xe٦*N!܏DۮɈI~,$u` ,d)zևB)V>&jS󥏟brL{m0`S]? &&VN7.rZhH f➧;C;;fRZI]r,tB 'BW 29sVIes!VW/xmq9%E!żEʎ/t@%bclڨɐBiq2h'sA14Qh2[c9ƮC4Ŗw#䱚.&#Ѣz%5N F!J)\12Ig ZcJfvɯ[jxY-=fE=cO=5mo0ȵ6 (ۗxQܩ7e?q 6襏jtojv!&^SU.n k|+|XԶGZ ,oT䯍M}OXkN)=H#Oᶸ]xE7rN \" lG1krOZ1LsfɊKD8qE( uGw9vPFdI`kRǢ}uׄlȊHMͼX>.B!,fq֫XпgoG`E9I3]aލPhWPv8hK[DE꾎wӔ*%& pyaٵ7&;m#o1eC1gF vSm?N؞w葇#2gBHN)ް5 .O<}3鴇F_:QK=z!ߑCH!Dxmn:վ:6vV4L4m}ڨE#m4 p̡`,ۅw%(Π U gb̪x.kf㨉¡2"=UVF)z7r./},.YSx*`Pv)*h)FqAp1a3G;GśQI.x<%q0R&LL/aÁQg}2[Rk@n|4[(N(\ߓy6D_n!xN'r; ;.b/_m1B{0G[h{krlWFE͢("`8fQY齪lTjo$e7_֊''˪u{rEhpk.9w u(c5wzz9'.Z#*K # o`%F0e&J(;l-J~ ^FI.JԶ^U" S 8'XXAY}m "ӕVKke" ]2)7dX@)Qf$:r:JcùJA$7٢ [Kz'Ejέ3iHӢ&ym v>-?BdRC;s1gW`Pz1plL =rCNWAH_GuIikNe֒ 8-I4I&V"Gar=Y-4GfL@w}&ޙ h9"LK H/FZH#eYZ$HO"4ZҢ g;uqNVI+z|MgX<-G?)b~<<\ƴv97oMk,Th7WP~ԲiW\U"kUriz⇿=ӜGF@&)]4Xv c6PU+u>p[ϟ|07_'R:K<]8Ah{̙lL tWp]8Εnig/cL24o),8V@VM[*[ytZp/KOVT519O9waed(8zEH1bPwЌҿ JL@lL0Lqh@M乖xܒ$ k*\ѳl#{|Y vK؇u!JNLYt|3^_Qv47~0DV<~R!!6 Syix3$w,)9%&Q Pw֡Dpxyj\=Dвܭcwqqgߌm4 KpY~D _ K-{r1OkpFE u)J׷vw?]1'âs3 lՖإY1Sgm4+&!3/;ds9Wێ݌ ;3SsPi|kٗt9+g ^sRVإ׺i]>D_PPƃƈҡ C,jM3G؂N[pıܮsNKjk )yQv6F4RI}sb'a=t\xlb'TMejıb.y ^9G \GF&lfC,F2ȞmD)s,߽eB60nP%(*G>(->߽w׏uԭyN@z^Kt4)`-)@Yxp'9, &?'ZS*.+ɎjF#1~ZE[ /7*Q^59˿tkeػv?R'P>)åZZG$ #x(̶HƄ5vE 6ô`Z(:4Pk=DD961}+5dʐ;Q_H:DJa~ɀ#YTE_䮼s#caatٞG l޼,V(t1Nۓr0_@ |T?olodmM9_a&&\O;:%AS*pzb1.LxT--,J5~\ v9y+mx}oa0az$ (,HN¹Ec*&b"}7= y1r$s>K >_ҏ/t-*Nm.>.pt~tf(sM!4'k3-Nt6M4&j|k?xڙ%Ӗ啘 9B:ǼJİp>JwZn-J`O^3@D$JDZF$Rs%$.IuXQ3P&YTF(2;ܽ| x<~sn]M&3yS}]_,sg2X3Gzھ{4dyR<5j(ALS,889.`FSl4/7IsWld\1.-C6#\/Z-k}֑CfaZΕBt4pV4/f٦MF>/\ˠ.a'*KLZ~dAӭPД.J9I\Jm$rѬVڴnި -L>J(zb!`h~-MXpѹ&DV E:&Q<>F}\XU;5jD,Q)T_",R~ ,imb F tb\i( d"Shykjt$ENp;A Lir^x|S緦1Um\͝;ab9*ˈy؞ Qc6dyq:sf C N65{'o]y\转9]ӧXsIM]x_dMK,OktBgsp?׳S+>{Ԋ:YT+W>U+|$5-EEE1RmVi? \TKMue;p=׀VwNM!eiU1T:Xĝg׿<(vRlG=Wse?(G}s?,p۵'NQHc'hV>RnWfys"=V͖\a9bJ;h#0tC Sğ umBʛNJ̛).Ҹ5Vd4%TALAYeJts6I[_A|uM$b2.)sbo򊪽wV63-2{4]eYr @U@ KXU2Q&8V\rɲEm>!QF0 e$Y(>ÃDki$0Ak]hc%DiK[eRNtjϟVmpJeYQcf/ /x.ȥsDG:>5m(@iibFD/^2p"Xa{R=#l^s.9 )Md>LS02,ӆ։Ea W~닟tOEvY]H" ,]q;ҟubȑ9_Y|r?Q {;Ce  oKh d"q5b.MIn(lvF.4C8ȺX}ҀVA?= m  h[R)hA0PR@zp zPa"Oѣ%4!%lg #FȰX!r^*? ߓ\^͉ŕlY#I{/D'~9<; MQc~!`_- CTV${ z c`zBt$JϪFدUЙW>(ԫFIq"'p+@m*"w"lнhRHQhÇS\^d7t^㈴ x6J.Y]#M>&O4G¥ ˞pIVI48msa[,9)NHA\*h;$S '%3VP b%ZI$ćguc&|&X}Ps*éV_y;b')mM::Ewۡ"3(gBg` xg0-O ︍rk?ћ;us_5 SiWP[Dup*Kyy#>rs?"h E҆ n.iILOEn+F(G9CӋ%H 7  E3]c). |&-9IYw1ZIu'W#ry`NOsr*63/L@A,u٩\T/~~(Rp&&sq@OK>753}e7v᫊X̅x]# iSRIZd pIպ;35V~1"n>-} #{nNBKɁ.뮀 V0xrVgXoij +3P;@μILbNGA =EӜt** C1Ma>1Gt$3b yY- HUv{{?lbPכGU z‹!I\ OM; +c㌵=g 3(ѷ1֨?avCH5-?Yw_DӻM2̪5Μ|W<`xg2 ;N7S3KzkGsN4b{S*k 7K?'턛> Yr]sVp*y,|=k3}ɺƦMdНLT~} }o[*b8e ??|yxxO3Z-3bZw._8fa 4rl&;qE5ekE=! J8.va[xò(kA#2ΰΖԷ _G7e -;$بP]D<:$ 8B0q-qv5OTj\/8sc34M٦{Z#h]tk6U8hF[6V`6q-؝}&=sC'; w9/fSVsja&}\LA; •IӤƒ x 2vKX/לҹ٩c|&Zxԟ\ gtqg?u/仭 ^?> {k s]"Æ76BWhtB0ɕ_wU}SS;73B}TmTC[_\M1HdhfO/=Vk޹qw40gDwq@Bi0Z $vʌ:q-ǿ5!1ybs`b yk]J~"8Zz#I[N6SHlTՓnARwO-gDc/y `mVdem&qdO$r XwlJ\?B9xs)| _2Μ ?3,RZ;oǦ*S'4D7v0JhA4_x2F(B/ClHP)MyQq.˼Zhp׫6ƞYb1l LV#H#'F$ss!&R7ƧEz%y(<9VYʂu}R-z $%Qx\΄\"(t(Ghʋu)Q~viߒ=: (FيOA7v_/yķ:Wۄ=D=>m[wZ݌C\W'lb&KTzj"%]?77Z^RV~0 >Κ.aA|8i-6W~tf^\wגg6Q9m9F_^yve :B{SwylfC.Z5djoCrߖՉ hI!Ae'y$̂2`N,qadsس+w.CzE?O+ˣ&ćoa07qc{ـ%3F)i: jB(ck+Tyc݅O_Zv-nU;/.7ns}wULu|M6N8D#֠l6~~ 5M|~ {DQ4BF)o{|[lW`IWEy?ZK= Smt;Y{፶V[@Vƻ T"gs%0חO{c~.rz2ѨK(Cw>/hbw1TuTY=mCmw8?:xBVĂGzxu0c7JJixD:%V6"f# FPIc{YW] 3dwb/J:Z?j$kˬZ[ΰVjX*Rld( /+: 0 Ha:-2˞Ĺ1Z#~x$`]AݿnZjDA6q %Z] arBJ_(tE>D.aXP|޾<{ͷ+ ~׷1>,LY,93VXRo wByE $ٴחmS X!Ϗ=GSTwwٍ]{pq!:L.9*h g ̠Wta U]5Vx^-? 6b{J 8ŵ/9,E. YԮ HgMH-,s;kD=ytBV0/M4 Zyx>ѿEs)z`NȼV"#8SNH!9U(iM@ LF~/X̅q!_6z/x!^btm  )(GlZ4¾fK @W=YJNk '1 Xb~pϘAJwL,1PSCj%m;JioFzlC<;ˆb*0{ۭ#zytq`&]mbDXtq ]8 4Ffo!ƒR{A#D\ C4Znխsàmf do: u7s('(JI[iv޿ ѥ8ln}9ۯ-3|ɬǶaVM ћUi8p[ǖ x.ωO+"_bwZ`A0XCKR:ލK[O5s(5 )g0ǧӀl1dؠ<Ҽ@(($==4ZC{uAWcP3=<|KΰPZ^7>3LhidbPYu:F@MpMs"1׸P}h44DezCE,*Hc[GsUt3SJNNyoSCq$(dhs?MQr(=^P64 )%¼$a6Hꅷ| _MFT*@ -E@2V$`pxKޕ?wYC.[JV8DWG);f5e(fѧKțA,ጪL dgGY=IWplpxEl[t(no]5r4<7:]{vӺdQa?P9dHI3t,]ggBcw)hOyp1 ? v7q`bDJ8O6h8+F18^okoFH5=GG-_hiVgeFpVہbêi(ŨQ4A`heE {%0Q3‚EHjW jlh{')YJjwbG쇒K<>͂cnȻhv `i9T-Ո.Yl<>8od:*ބB G;cbGpWM4X=}sQ MaCk)gGUPH Wpœcz׾;]܃`HCoBW4{24'$V$$9g祩wl#ĥW@TuCJ[iiU76{Lb]Oa YAS)!D&>׷%{J%s[B.D=Us ,/2Ha2hV:uK^x./YFmyِRZ :{d"Y&Y_G@`??i^`9GE'cDNKNge={a/$^t鷄j\un״BKtBmFJ&j6?P`5ucyZ&OFdwYnErxVk kՂ2Q|U턗p$^6pqQ:t4+G>p*YG֊ZWй)7\ g2rk):kfO Zߖ>|C{+Ц~#f*HeT"@vID<1w/ t)2!K-2B˯>l|CԐH^TP0YHy@#)Q_#-ya8K N1CP4.n`A@U=}&k.^?:hꋸ4T1RڍXz6 w}jjҸT0};{$p6XpZKʫ $Ym9)Wh/Rbx!-- D/Ԡɝ^\MiugX0MJgҖ@"ɖloolnn%:FTv0 ueN i൸,D鍁2 k{d8Q(cl h X<0:p"U! | 9NI0lqkZߥ 2o'eI$JfL?`*ZbԩлRxZPq*vBO,=_t:QCT ע CCaǀ"WWDVGKj2EACgL^!j},g(Ă؊EYdOBjԵȈXءs"E%4]l5"dnN@NTp  5e(y^"x2_a XX6q4 O\1l"bJ:h]Pc/+~˒*33,CI)AaqQh֎DO_ 0|}1R`D<*wõC@[A02R26̭b,4ÄS8@1dsFIsFޅ.~TR=Dhg1'/.RLxYÊ5hתU,e_]zѪ"Co?l +{b E+@|+f.f lֵ""V:UF-sk)ͼ*-rVD Y}8]q#|q LEHnQpHI0@k |5ȫWNwJ|ql.>+bX%h;C+K,BS>^`3ԥE|һ3Kǃ΍T#/#R\*3'%II%O+av~D;}`IP̋(w. Đ'LRo+Vڻ_q]$Zm9_#mXER,c3hyfb)L6cPM"/-@}YFtA0u,&Li6ֺE&}oZB$uBt"#4Z:EYfu< Y 24h%WTB;ܻb.~%2U%2,lr<<`0d5Z'R H,[x eBڪVI/)"zq7bVmq/#\|,ZhXl'DֺVC#t}Q! nX `Π1}"zN8cGDF%/\YsK@bk gFOlt-ȓMwIc ~Pc_mȴx=β.cLh1 #c+}En2C~ڱ?s=t7yh=3m5{ TcC$،QVFRq(U ,fxVG'kk`\-&%%1 b^E5nt4ߢ*"ĀdʄABU(ca|Z Yܐb(k"㋣F8{XDz'k{lKꔢ5^<˸mF/4z u#)6"dDq.1Vu1usxM2٫%EQ11I)rgKjǧɖsȸʳ( PiМ33j'n"in-J^֊'ǥD-,~ؕ^%g[g6T}$K*EإR8U&,2 hÄ9mjsX:6 P!Z`k+~MCleFDvl<.jgPZ<8]5CV;FY/A:B6k Xܘ؀8|8vZ J mġ Ncx?莡ۦfoU, n9%a5$S,-L mhy\{nK7d&~8_,DZiaG! b*ГCV rB]SݟeDOLY 6K.km׹GM6 cA 2 "a)[BGJks_p&e6\mЀ'AU$T*^`b%r?&>)eGHjTSMm "1ާ@+Uy!Y^An_Z6])wX {F"=1or ϜebukOE9 *-ŴZV#X(d|?ҪRSXq2qDhqdrЫէ33}oز)"b,v}2hU owaKkF~.(IW@\GҮo.֮-NX 3fjryL-a",׿?76쳖x*5.\/>1IOs°\3m;ui6bk"رD"d„;ft32EhŌv }U~Ԅe\L2aV; VpSm)ѿl̀PC (.7GX<^ݺeg!ijSz$:>>9Y*q&쿯/QaQ[Cg N"k+z)4 •̐81筨?wJ(q JefOmj6r $ՑO\3wۊ ;'"nLucSY`X{+K$FOX~a]aU  ؗz`/\ +(| 9 Ap;r0N}(Z5\GGR8K3FIUG0";Ď!oX?j\G=Bߣ oMlX7%bcE,hM SS24ev\s*iF43ytt& -(DU5ð:(H 6u%&bh8.ԭ `R1uL6]VxvcBuLKb;!o0iJs#YzQ6 GԺA_BR Yb23@)2Ħ4;eyecP^@#9@bKT.42H>, 8 %F.T,;ɱa|Ᏸhl錱:U0E{ ,[-H%,-Jg@ U 8 :a@y`RHU QHMR `Xh!Zn UtA3U3B\t5VV h~CS8 KNL{&vh_`AȻd'zcR ei4!ދ+(IǡJxLSsRbj117 C!LЇ$ -'k/BZqS/#dO\vO&χjxhgaAW(WFa;.Q~7W`2ʋ̽ƢA ^MtQa6ss74k>9O1~)ϚԤ>g/?Kf1F̥GO^Ś0jɾԾ_ ]ؠF}U[ړJQkܯʕ'ߪ!eɇϰ>)dW'}ed]]Z׼N aȃpe5:OTu0#<[\HT/fiQ7jz }ֵ΃DlQ^|k P?;(e{L1dfFxa<|e&ub;^cG'2p<.]zT sN#%=?;3Bmx^"R.0/&O|T7~g0<_g蛾0HKs)qٳ#jaQq94%7hTnur-au^_/aϮz)0Eh̅@b%bWgrZW裾 :\^ 1\8 IrBKYjިFb1A.%4 CHyŊLJl w Mh0ux/(lT%!m)':e9K`aP]giwhU>R6lƐjq8ElϪz*Ve Z8z{Ӏ(qW<93Q"*[GxT̑b%윞M!?6!~*˱lِvl%XQ@$0:)} ~(ST1Z?}h!R#n[%l8OIm!8v@=i/ӑMHhɸC54>AF xvG1mQě6iG[ D7l7HgrYSቍC{{砚7ds8\PyLD-:gC[<_M VWޤmH>dž~ ;^4?"Ѝ<[(6kFMI(;p;v<[9%b(Owx4q h5xLMeNtࣦF]+= m=\.'i{رolA޹C|>mDڱ\6t޷/FzQTP7o<Ԟšt͍-}sA I%-ȁiI)?a䚙aCN=Q~R)`L__ZA\zށ~Ⱦtl3rp)g;ˆy$(8ё`8=Kltv d-ѰhP|{DiɏV؝\Y]Ѕ+/#/#e%($LhA EBP*b-P*84[-h]ZWu Q\&X.xu#BJX#MPp,QR3rSRctPFMC?񢥅ǷY "nf5 k(D94 o8*yOVQg2T5 ?O]?D-R,kC H>z6ED'm"cS1] OatM[2af҄.CJą'fG0x!ahrH,YOEsz՞WCt^M}+5 qӝ  /AS'E @k]:XF`%HZ0Xm'$u3P}G;U&{=e>H<$VZ9 qa ja3'7Ys[QqJJ~:Fa/y0ޖֆ_͒hHtb(f=#LI8SGBE SߥDE~Sgz1{9G~,0$ϵ^v._+4serpn> %&꫖x TUαt(gVIcCW&tJ춖ɐOTH'hh[.\E=R{ayngh@Ɂɭ z 5G 3#sm]K@S 0=Dy'US5=1 +&.u? IL5pA3=hmQk Eq 5N-8iY[/)Vp"yJ~ 1˴4䵯ۙ^_iit\ب77+ˍo뿿#a>}bNVBofGiϥ/? kr.}uYOc^Uu2~O xh61%ۼH .* Qn²Á:C:S~YWo_NO[Gݷya`bcc*s21] *ćW@?E[~hzK?֩~rvzrAde smV` -t &}j8&~@;j_}rVQ-.Zhë 9GvXQ5\n[(H(b]ܸ,1#qca@NCwcN|L; ]0JBM=ǖ+<M;PQt!ŵG"FV 2PnjcKң/( :Cu>cI-e"#5 j~i!ESc$"ʼnScr814eH⺒ gUhƛ} @{HPH)ޗDNlhH55mrHg3hffVN儝ͧ 2'~t}5#&T^ TQ7K-a&&ur2  < ,JM*~ (yW0qn'CQm! B'H4PXoks5]Q%*9ӿgoIŶ YhmP Ս[ܘ( $3|#Bo lEGach63-N4 &Pd$Wbbo@ 3GṆ)B6 OF)ܴk9{wl3pwx) A MOWGx'(⍑Ԡ(WǝE7(v. `&Ihp0">obK؞D!PP`[ECy,/i~WF<p&s<$z@ (9_E2Z2Հ Cz?tOpϥne:2kh`uFaRXiJ7ɮƋz]ҞO)3PwlFŅIamSCbrAys5ϣ̀ 6~0dv*c&t %aAodkj0l&I)yU з#mvBP>J7;X >2 jGݩk M+Gip#bYJ8j˫d -ATR$ćm.~rޡ8JLO2П:12z2IyF~6<9$^W b;tr ~J&(`yX )lCwFkk₊1yIC9ʧINmY򘗅0 q4%f ǦH4HO5ĺmK?/~1JF).i]tߞ[Gg{vtӣ݃iHxyYU-!n4k/(j -#L Núz,7>%x:'lxf7'|FK Mg|enw*%/$kFy7FӢo,S>2_l>i'gqn}?cʛbs؟ wӔCvh{ lT!G?Uh7`%Dc %h2)Za\o`ޅq&Np"w75ZPgewT|BrI@aN^.ЌzkjA&_̽kM\R*M$ˋ^v }Z)7VN ˆj&76?ݒ%P! n6F]hZ՗P]tV%չUOcõC9:z`(oKIy3w" duqyA"Fi0w%ŞRR!>to13->DYiwMl]ʕ+ '^Ri ; J1}R*EBXإF Ƭ wrD+|*^ɛa>luv vu9T|c9}k ܾ3elu>z45HoViU (ѝ4` :_㎨ Z[$hIШL<'wު@UuѼƶ:nio\ DCB~!HDӛKx= >QݞA"csۯ,XW&ab^+N\3e閽F=M9VA"{W쑜% 0c90GhΝ.}@4SbF-fMy.(֍4XfO Ԭ>Hl6"]R!^GKE_9{fj[$:w|gވp0oT&6 5lU k>/;QWQGuy-UOP=MaEw-^L0ēˍWWӺzrl⮢tszVFK'MZ3q^*ސ:BħVYp'';_e™43fUAX)ct:`6~Hp U*=p,\yMxSԐ>–s ';_>O9h֙ffQ,~YBY`;É";'eN y/8,E+7͑pHbd@UY#]C7Mgz>p5z. Ì4`w0b})X8AMfuDqgjeKW˹Ǥ)>DTpK>g&67Y5C,pŪFRQ3c"IQExJâHnX-hD36H-bSe3uGIxC%CoGXU4/ K Ô;qTJ{zVUaw5'LԿ8]vO}vli/8@)ϞYKU0f7 [^N{AcaMʈ)H<,;r&W2&*o|[{*{!Qk99L +f'/XZdprNOlF.KA0>[>6Eiy~RUǾ肐)qM˞;:@ B<"UbE( _1G|5 (] SI؈$p%]sF5FjL#m!:H[7Z6͋-\v%O; TD(NN],*B}Ҿ;0\Ǒ C7;׽h./"?*BK\g9uK:虍:kybC^jӖ.̍ {7F7waNϥ#WѢJrnGߗ8a vB/Nm2뽈ƙŸu\O3 T=H cPdDc_̡' @_::~7O4q4?ïmdY\b ] q.ב`Fï3MT+6|@οyM}HTH>l/kW0H5h:ܓ U(좓Db2 mLB#w}Y6+sS@4-jߚiF֍'G27c#Tsz`_ݟK+nMQW/v 'SFk .O[hCWRn>l%KD ~p1o;~Oȫۡk<1iu3vR]EU*;ֱնѯgZ[چe+|'o;pHUIX1[l VLc삑J2eiG<=;"5>Z-o U|l}ˡݍ4m/G(&VioR͇e%4O2iok r@P!(!Vi{QBPI2li> TbXQVPz- Fדya1%>-\Mˆyc,Th3M:*֛LdthǫwVZȃJċU"M_>[Yu=L%A ICi pV 2lF lI4pّnH@>ȅ6A,.ǀ˫Fg$*$κgxf JdF8'TYAfbVlYK/$' QFz]\H1 pwu~\{苳QlHoDNÁ+d8:$Y~iR5&%gsP<^:8j2) PPa(Gjcb7DRЅA-l/U_nװІwy(J5O6I`Bu$w;hkGFR1(q`ٟNRB&oiE|ײQyA` cz4ʙgbHVɬ,`22EՈt-2tsaqvLm`:R&ˑ;GKdb7*x"88ˊ-1AF!aM+8*V[܊t1$Pb!W_tI.<1/C40AH=7ե_f.i$Hd+CZ#sP V҂L_YTrwz;#z# 48Ft"3@])AQ[CKIVQ]>di M{w[,Z ]1G.ZD9c2/z,;Zݬ vV5}]zy;ȅO>/m\C_̣{?m}tY*_|ئxzH~ B;O扝%b:Eܽ]`vK<)OOo Q9)ڳdgk\;̆~IM2;bfdio]wlo\|,*Ps]-NRSG`5#H\VAƉ@{B4F#IE@|!2 7:|Z:ZJKy=SRʹN7J 3R!G͟@+ޟx-ȉ&lc=+[e5r J\)w)sLi2Pgy ^~C'>" zUJya`:OlhEfsY)^W)%K3QW7MDhݐ\ZU3*OoqzlJ'E݇tX0_@\wWzGٖxRĒU Sp)/#&1۫waگ xBş5 /ݛ`Q)HŁet{"\dXÄAgysJ"3m-L`uGfyJXxb|SO˲M)o' .&#u |dIB5R}4?gK D[m2 f(NU=@X@!g Mﴩ@cb;i-mKhQ@OxX#ʾÞy Z51:`IQNJ58m2}zZʈӱ,@TU#x2=GhRRG^VWrطqP*so0荃ޟnL,( GszO>ld~}z4GQ>K=|*eb}P5 ےIxRSӶ 7⓱aX<6+Ls2SJ8;cR6FuY؜ )~Ms i;߽yy[6e"v:ňϽv)MYxiXZA>mZG\=껈?d]SJSg;sl*CR%]asaU|0pg1'lDk4sY |w}IQ?ڜAn~qQ7T*E^k}rZ+sx>?,Tqs-/>Y;/w_)ZJQ4U_3)Cާ%ǀ`A96Wi)BxvIz4z<'f[1OIŧ}33<:IRS-O"D>t=;'9Q}?u L= ]moof;/Gx=xP!>~BG!c;:Ї?֕#;/.|wl tqRL9b*&d ̞bOs`V'?:\L>@DdYm]^-BlO0xIm{MЖdLL&%w !j5۹}u⼦oL(hƚEx)V̱&]xk w<"3m# v6>y0ˣ %pFayeb@͍K*U+ۿ<;87S.ylM"/~j(ς]|(^6$tg-p2S AƎT3{_K`!ۨddcDoݴH%Tv5O%Aֲ[*8EO cUtJػNwD&h-oH=(vWe{TSeh˷,%  H{*4 Lf//ٰ`~FHXfxRQV7q[ p315s7 zW\ILk>բ%ZB47$BkN4!D],!5X $gB51|TBm=BR~>29:A=ޏ悪-΢wI5Eq?-Dׂ7'Qq͞u`R/ߋR>k8ͧ=e: sq"4wk$V̂DT"FUW۽:`>NzG(D;S(:)._rbb0MqcE/NBp._TC{Nxƺ~)WFkwڭ:'x!NsM!ten* V4j')ioVQ60 %3(F)y2Z6t wE> JO@J-J TwpI_U#zZ_1j짢Dc6 s -,MFOmM,^Q.IPa(d+U>G>~q Dr$:O\YmB,F!k6aĻVa4uxqqy~#|W#K_!~J;3丅Դu7 PT]1(CEʌ!9ʦcJ?0coIΛVd*&\0(z>||ytqxܻuċ%PO,\L4(Ǘa9|r,JW!{78}4jgy*g~b-2X΂KMVh'g'7# &I,Ԇā3˹)V>4a57īWޔ(}lVC _]]!RZb.S2/|]NM ufbCC-):]U[\8˗Fw9 yr3?<`rk'kP~K])r]bϵSOѯv@zn 0 Ֆf5 :hQ -c>)sێY2w>YV{ɱREVĊ ,RUa8R(k30.Q)Fkula%=6IG,MV&bNHuE:=LIsڝH@6KSs TjzpoSRxNԦ8 dr̂MiZ:2"&B7<_u<IAl#zGGleKQ780@8}>. l2%?=>(@@oHBH0udhv=GY"*SnZ$|Oljx Ы̡Э0fy?+DQX;r0gwđx62p?`E#WVfS!m UIR&Q# 7Z yly@Wx{Vx3N0o ;U;?\=z'H8f@bgPup|pr/~s'FZYpw..N0}WȿooitPKIͿpc1 ʈfOM'Z~2noe06EǂDvp?N類uֿoG!^@iY jᚾ]=)DCGNj9XiVז P0xB0W밀)_#4EojV؞er1r'&Y]'I\2R 3EPg3b1VZᬚ ƸecJ]r{+p0=ڧCDk= ˀTbk.Tˇ:"$BEKz5)(;ĉI|}%DcjI #SňVM^i-vx!N]5BT%>KӒe /|gmd;hjDs:j% ȇ9ĂmC+ Ze:Y4Y$6^"]8҅IAҰ3Ќv$68'r& O2?D)D$rF((-J ahhC(TdOIlJLd"8jsJ Q *ߤx#˃V><\Yչyzvp޻8A:~3QE.ڀ4LhJK&r0qu3!>4lPE|pb\3ۧ5ALYϩ%M `#QSF @zp][]JА5KI Oz_f*2}*\єJ䮜O~5a%'ҕr6v}YȦ+BFB!A4I3;ԵJƀ+䆭J^ְ!9%! V@)&ӊl2@'Y;PeZ a2<4 %cz,K!x^a-ApAr!nJ,˞,//˭>{>mNz^*dI G}`bܛm  ;cR;x@Ͳn};>2^a*v$^\ӼoMTyI5gř٨E&"`w<b${iE^^('tv`8ߛA8>1QN|磜ar@SR8ο`O\h$ ݟ;e*0t`xMHGdy " DՒJBIyZ×w@sy3[V!_<`O≞5bXT u|YTtL6vƦ#/gD%F٢pĦlT~Y+i59~_QvѠ@K\o\?Ι'uվQKΘ>~f9 *14Jy`><~0`C7C,H$+xYTA3V +tM`HH3h Zޜv^FZ&2|N;]oH+;/gg|%X"@hKd?(V/fjD@t{SheGY)J?̴!', "EI^12촓)7WkkPݘbH"$BCjaUg Rܔ"eG t in1> jV΁\J2kk]`v b&tpz;د7n[d A[X]7n>?"~q1^F7S$`: ip0jrڏhGVc qn#L&}|q S~Qvw:"[>,2!_Fn2[ omcTK~䟣=FBB(Dq#g|~H4ʌ@U ~ <*0a^EB"?1 9r޶u^+IZ$jN_+I^,&Z1.| p壞* %$u/zRS`5|gAr8A2Hv'6RԵ?sz&k Ñ3--;G:"߾`}cPV?#d8/-Og4/EE[-<4P 4 iha>L_桁L00ĚKI}FFiP") Y؃ bGTPx1(I͙s_mpY;459tTjf1M %);-kj֟_d;PD |m%; s J9TBZl!nȿ N|O2:&R kQ&=MAVŋǹBw_0.%t!;r8.yxB&+. tSc?ycF7#%ql'Ѯr&5"! c`RGlgh>; _{5?⿷mysix54 R)~eLiNv%+I].ÿlxd6|eh 5yl(n2Ȣ,FY(3I2km(kޤonޱ!\k^\7oI3B',ߙǡoKH]<;'{2Paf1Pۅd,}]Lu1u&ȀBi93" R>apB-eOQ$']CJV\4$0 BMP6\%TPyVAqCBzB@q<$r[J^1'J(=`07|l|uAˏҨ+kYTi)$KST Zx<1O{cr- z Iš%=": gy$1UY4߮$T4fF^()7(|xYbBD#/#.dO`k8s;, D&w:jM:E~'fA(*f-y;C;}8 m.P\D({'$Fi|%¹{yD ;N3L &xnn"U!L#;_U)AW+@.QrxmFeBQG{[V ΆPo:%ySRVZ0_GY4*BT˕N.n+'>:,قәeePg7I9  A22I]mxC)^qvveusy[lf*l0L@T  #Uɨm41wCu)d+r` {1#}̦˓[niYST ? 5] ˨KӋ҇$8Xc%F( qO^0U[ÁѪ^f!u6e+?e@6NPѹi.9;91ЉHDž 0ɰa F3B^M5kvPZ~^+6~)6+T&;[49ND4&'/ O_C}0@)RlWd۾0#Nk,ejx@ʬB FQWf*GlD>9v,fWnnW·wh_ >{upzN:22qBLCCT7GBG3OjO$/MdY&m&wMP)hWY~+4W-l2IX ˫n`KJ&Y@%j%+vO_fIt\' :]mTVYHTGȄfo;+/A0@/o.Ғ9`IfgCRC7t<()[?y޵e4闁tM+`Ҁ\zˎWdVՠ}`miʕ?RO]uj&dqH0۱ѵɤ\*IBh|Ek^ <$~hJ9Lʭ,XPaE|@ܪ;-*rJ?.C2w GdHRk&:94>.{pcny^`W\i ?.7&_O})/_z_^ei |"K9a1OV ebTȤi6*hPZbJ>q(7 hҒK0}خ1jOV5:ɲ撔T8^Z5ӨUYPͪ,V&d*˭+-e3$RSyMՒi$ Ӭ7!ME⑔u_kk?'w!Nprs{u2{:皁jePծ&*䫞wn`n] |oi#<.˷+Q>]]$cQNYK.l.d݈ȎZe0-D ~cE"ģ\R02W&@_|S)LdI6N\e.Zp*(lNXe!U-ʳ{:|֣N P tt6!-1C#7jbw ;i=3MgTըx$l@8s 4<0HOQUeQ2#9' h S88wȷ$_{c U ]H BA-ř bUsX˸1B]ƱrҢ2$w7j%䌨O8E$v&f:q! {E8HTn:M6qFhBw9F>{-~䜜qDVk{w~)N-]6WqҐR A^ DXΒ)'__D<DM5yeޠx9LjQ# `6!!&$FvC,j\ČZ/״9#RkGm%W7f=ࢿ'w,>_K͆y00}R'N %]RK$g;L3ΟPA\^6L\G3KoR`>FIo]-*y&3E((p90AdeuAP4ب!g;NjAO8Ibg?à<9b<%3RtȈ& CJm5h^PhLDž4WRD *5GdT'9HVýzMI uOLiQ[?bۙ7H'IL4`7LA+v̘"fŃKHZpl. ϥIo8X\=HŁg)B)KbĹ5 x+$7HB$ҍzq_?_2)ހOW1zyA ]pG '%ov ]Em;bƅ2ϲ:K7-8n:/ﮐrw}F e{^X(y;ꫩ?'$zI x~*\~d2<⺐ 4ڑLYhK7;Pⱀg$|pkfziWQ)|{FiTZ-澳B;[&12)>?fyg gZ;O-,m!T?] (:b*!P]l|{BY{ -|7*ڏlgkzxRP o&zM;Ho`6e-Hgbi]t jnons\U呸84'J[V&i咤paz/FUi[je ԭ%ϩ%7=8e~XiNOpb$ujDo!PG;L-p"lVq ~fz SXZH0Z'j)#@yy 2 ؤQ>& CRLX-!|vN=~B8Ej\Q_$il8 !֘bpC̦WVx<1/oP-7%> "pV,#X $z2pHh6rIO#Lgrt&̀9}͓Qjw.( G}dM %*5ٚ5Cg,_pfYIpwt A$|s|{-@{ I"mRbU)H4&;H nӏq50˳A#6?n?UŤh}hh\ >'EDNW}z[N{zlO6X6|JGf8t/"X s1{&Eb$Yo5{V|94ηݵU+)݆`m}7 E`l [DDpqjieI lg;$a2bqϔj\4\|u̻hg9aiؼ>udyʅti#hnbf~Qi߳`HR0Q.rQ 6:7i#&$B mLg8} \Q9wh&i ef)rX=ѹ}TH5)PzA9jiѤk]8)@Y;靽lzmA`H A1)HI }WRU^WioѰwU `'X4zz*#FWmUGKZb;ʻޱ˖#.%6mK8ߒuU2#2zq=zDa(p#RG `ueoGnaQzG %Ng{QD!|w|{w #>ڟ}{3ilپuD}JC//" v=y&` bIh2ĀF ЮQtȱe!4Q^ɬs`DDiOaW'W˧sIk!#xUC nF52یn@pɡ(aM7|߃ۻs#rxzuO42TwfSY !)hhcoGfuG Sm 1DHbP'hPw.3f >Jh<SH1s1F KX@Oy/&?63>~k4U׷Fދd"sx%хYU=)ZgCwR[+5>J~={G=u|>wʄqf7Xpa4SI:@ԍZUBY 1D%46-p^60KƁ'? BAY27gsA +{ϘζTr2qp6/$6ݜcPV,`ϲlH5zwUofY6Il!w6Blh\hWmq?P@2q󖧷'0KZJGB:xVR^>?pe\vx7mgi1]˳]9ݯ.D~ݫ:w[www6mSwg| ÿGkFT*c{㖹~AqjQ:@ `Npr ǐ}b-,*'ɔ{90R%hGB#LnPSVP\9 F䍮V9 wH^\$UnPzT#՛v7) pxي80wy6~t61dLY8JD8F 9ƋMOq8DǖK"`5^~rLH]vj0$dL唒q^HK"E4Ns`+Yp$yF)a4) /{}pë83uzWCöm}#,-Z3[`a ''P#!/c&T?>_}-6tx$冃vl A>]tA;d8K0o2IzS$u.yyK h^jȕoZ<\!Hΰ&? *:糜'LO2PDWj7QF'c U>|1n^m/('Zb ƀ@Д{S׶ePl].u|ߣm^ tXݨ0/  @o!yEo/zW5DҠܐir6rZgW D KY|eyЀue=LڒbTNP~v4 5B"Fܿ@W&Itx|7~MĨR 3gB;IF E(;M]>|p{$ Abk&)΁ހ}cG$s?9^=7\/96XOk/w l{v4a˥FVF9mHBnE?Td(?5̓MӉo$X_?y{~◿o;ӳoq6} Y IQr9C$<=l峫vm. ĵ,o~{mE[#Q&7^ut^Ge'I0'sèwb0q4HEČ{ <,B.PYy$r 93D[n:TѦH Y ZuAjFC ʜ&FeU 5LCAi½1?|>{Y:skEpå4l.[kG'i#S¢o9^-SXvDSW)eEO1pΠuqE!9`ҟ2nnu A;ϳ>ڱ~6@!T(BjBl>Mbs_)0,JHԅ| nlkl%1n;Nei22D:-86Qux%xRP <6(.!5IItwT]w諝w'%iH~RwkpW\S,P>CO'.?$KA|Mv,!Zoox ""1%רld :ܗBg;ũVl*$Vε{mOE/fIQ!ďVGxӎ@ cn>ˉLʈUQ} | xY2N1 vMi9$xK'\wÁ!vbC: A\S !ƵVy ݃%Ĭednjȑ=5Uf?+'&4h]M9Wк@=vRGE^3gSRTwFv(i#9Su-=~oB7L^JKֶ67' ڠO-6)e]=T@@3$$ˀح}hK,s!'Վx)Ƨ$JXIC^q8sz+<&{ OjaҕMn%DLNB!ajKY&șyX#P]XV" H f^ysG_Y=(5zaT!S~F!|Z=e(1_*l7J'~u1M"fmNNWYPݦݝb WferOW\39vmtŸN,2M~}^6w-~Q܌6Es.)$\|n}%tҩz 8]+gb#tw9n&πaxQn { OtbG,S`tNt ź ha9ȺwpzgUx*A,%jZ]&ߑӭM99ϡ  q%Φ;H Ӓi>Vu7zRmuȆB1eʐ$#Fٮ N+S " 5 8JX[hN)kG4js_vh*6YθmqA6휎aiOk|r=F2y`&@}&\$uZ,esgNor+en -V%zP!d~*SV૞'Dq{ %b/h),H咐BBHCK5],Ef  fcbzM+&^ ~֢m76Kcd.XG̊y g"sFD!y-gm󊗛iQ+ )~<܇մ], AZйi6qpzD;;:Bz7D 9YRRZ{D" / Two7bu8|WWEQ-D+LtO@;ejYd v>2PU ADQM{z_ƸZ@ߟ7.  -6GȳVJ`MEX ép8;?Ν"ϝۻs,Lz2O= -+LY +~nwn=/wvߎn|\I5՘݃gJ5Xĝe=s cVO>SiCD"GY2XB[q T3szE eç?!bQ|~ǛIa?LuE]5u6G0 -W7GߖEE)̷A7j"xH0x{"$2#F:䚇w[cIo:Ds< ]dl?պZ^$7g}&-%dgl;γ!B9X&ma33,${U(+9!fΡ' 6+O7|Mpe??|tjR2#_OGc0ћA:5Ϲ>&Y^vO<ӧ}KS\t"'vt}(]׸`n{cDun6a8tx߯ N /;κ|5~^9Vߎ^=} 5zjx>__TVdRRIu0b[E&q3V%vlkRY1-(mk1ۺݜ$1rhL[3dġӉ*&ANɠYkY#9yCt3b:~zgz5] " ҏ"X0#N/TJ ysI. +SAtnVR19$sXbp6e5?X \>"f K& c6M~3v["ã"Î"fhaJd8O ؇{Nۢ@oѸG$y*(J1hFgyb.vڌ BMy%,5UUH<v%šT&Zl5(DBcMk=mțj^}4TBn9 Eޭ`'35ϱ~>xfd]`3k,K_4'̤Lvհ<ǭGrFs<|G 3#adi) K!c|(sWDG'v~E)5PMHY.F{bm-F>Cʼn($\v$Q٦}ݍĖşӠNIromI8D+3R!){.kGT|lGވ˨%<ގFT7eo?i/ -DJ"$)rCgl:5>ݎ 1ȝ5c `xXho[[~KKYB6`f8N.Zos6!wKgj I7"8%0R}TXl4UN <20*k5LqOnVٴǁ?D}3|1eȨJv|sؘ1}/pؤ=d i.ZdZSj|JmLoXw/n褴ړ&I w 9ŷ 0 Sb[,tgylS\c^o)ϑKv-)> )fM {7c-x: '׸ #Ռv$[9KExz=Fuޙ~N-IN-y83Yl4q x:B\~(3f▷7VNbeE[u7]9vOAn,$ ?C ^VluQa~m6evG=fYGYf8dD/h#J>@[dDLKL1"50ov_EWG}sgMݽuY);_\[7d7_m]|3 fE Q7+/]IxomC nw")r#nFkYœqy502EI.b$ ּ4]FT i%k:N`hH|}![&m!6^޼ӌN pا/5P1b>l9<.ճ>dkҚ8w@I_^fgGrlF/;Rܲ#~Z;LClښg-iޤcĎܞIjҊf zx{b*پ)ק7$ R ü;teΏyq"i7)8m;ݔd3EU6{sӵBn߶3-%}I5Ȗ`yȁZNeO#c{'_B*;%N p5ud\'Hn qtmm`t̞K" `;_'iop?-wurm,/pXʥKvq=l]7V/my;'?Ecx?fLU. DQ Wx*ʖnNwuG~-@QܤJQ(ZʼnSO+׆%Q'AV[s b,F!G@ïe$ T8+)#uS:)T#iikmW4 (1IBPD]OTLYi,Bï8¨7Z4tQlɓLnFIZ*̳sǝhӂ&Ng5wJB2O n #JH;y*KD?'̉ޘ 2Yq\1N;8J>ғ!{%gήلu)*6"#zߦ# 9O0b,[;]muHX }5=wsfep>;Yn81T1)Ѽb4ْE`Bq?f;.6 #9:/ R'(xJ8AB&Y\ZV)*]n2$aKQFnR8f{X n.Tk( _k-)\T8[b,}<JbbI{)#'哙SIj|$4¥T& &q-@f$G dxBbi 7BsUUJQ׹I&xL@A-Ā'LC@an|(m)ׄcEn>7 Ԯ`?]W ܎r1~S9C[ȺfHr V,#p>1'`l9WPw! phY4Xug]߯6wkA%pt*)1~Gz&N)Ƚ=:̿X7q WCa?>=P?/=,Ƣ_5@0C_CFk9%_!lcwAЪ<#jic:G"Qi40;e/^t6_?Q=}ÃOw`vwk/?<|q8no޹M[Wέ;M睐Ei`@G6Ui(44L%:FziU$;=syD]DIMpsot }=Ja0P?-Ը [_IrBsDe5Zt.c MtG@Um VTpޤcB/Q?i:ΈG'|?~5<ݠEάlaōکo J<r3)2O5hM\>ԅeREiX R?Gbpǯv ~0_>^W LECo>VߨuU*,bNU92+S7vuC5 tp߬^x!r1Ag4x<'h?&U573tXmNPh:y6ތ79T.ZV[ +ZX V>C jXz/Qs l'V_KMݧf:N'rṫ?c!pLxf/ͦKٝkbC?h:c$j2 lG_E[|5op>}hvom?;h 3ysw_hf]{QhS(uƅ"`k6pxtiN'$#8%GD.fwٯHfmވrA|!S\=輂j"QcYlCT& HËO16+)h9,0̧ +YaL˽.|>*n`/pB2HO%K Y s&vxVN]uoBY&8I`Z^AŃ=sOIBXxyr(P98%) 8JzPicTw¢">u"Nl5tz'H58\\ac%>\t_AIzUw`f jN}]\[q-G -d[#FB')-rF9Q>("j"fvGo ^1 ~]3n%é,/P>r~>y1Tf5`r9ܷ ov1(Fg,tv$R16JBon4komOܸxw6>:̓~Zϓ|:v-#kNbfIH`zB Q"b0hz=D9$ X֣̘*$+]^<QJ^#Lέpg UNh{zEmfq>V "xֈl>t{/kk|LJ7`_$ r,Lr6:ƨ#x4)Ysۺxi3o)@3e4X(]J^z(YҿH7wq#ⲝ2]#3hv5& AJ' iiO3֐5觶=xRQD't\ Lt͏=rD)PǶuI 9 F^Wa Ϋ.r?3P`l>bG-5FoډV<ttXI֍gKͺ$ A՗[ j{cIMBHԟ~y o c%@՝w?g7hқ_ݹޠ,_6k/ގG4ܪ'Adq:q+ ۭ zo76w'wƑgTbm8 4smy),V fknx b*h^m+@PN+sEމ=pYHqQ -z+*;pY|ɴ; #:>+BJǔ-P0iWt(K_2GOTwƃ5{U}ۨ4nfH筸 =w1?~>]yfyz_:fm}]nη:NY{ndqu;15ۈn#j+~ozð7~c̿tQMEzc%/@.A2E½ؘݿVC8܄\q.b ߿$o";&IP.дf86KO%k4;dcU$['\`2bmcm A'Q4Dojt$ztËOE/r?s%6 MGQ5MZK-b wX$9IO5Nlֶ|U9vξ_C8vYB":Ơ6\cxI*qlfFwBwvwʵ : F(:K6y vZj>&6BʎWM;GZs Us RhǚQhNJP:tCͺqWJiE?Ŧ+=4M!0hlSl(yu,44X s\4mX\؟c+)O1iT7MrHBť57ᬎVhaEwS\,)3= iyhDA[('PlpSFmUwWZA M΀MM.GPzKY;@}矛óq;R&Ugq,,ͺ4άq μz/]=&OCgI1e5MXtFfLR̾K|M&4\zhCEaGD= .W'Ȫ_. Yok˜O+K^!AY˫$#f I1.+DtQ'mD% Th|?CĎ4٣HDW ԟNBV{ p#o)$)!||қ@;Pވ<t V$5˭HoYMjW=1CV9nuͧt:/tdF=}C.cݙwu،N٬r|5=D0gƻ4jO`0hOxˡ}]ܷ /Kĭ!PxY8IG SCn+(Η6PHamR9(w~'p,Ű>r8 at0n1 rU2.\0JD$(׮uC]LϣRdͨ.ucilnCZGKb7ii3*؞WV+GݙeB ץ*#?9] 3JvrIFCZ I2.v:*Hn vI8T9l86u)Uٲ#Eoc\06vJbjv~7*`9~?Κ!>y .| 0'-f9Y.bcqv.u4Gā׹Hg0jȡvM %K@4qEBcIvvN,Phו'bI>WpG!dޥS~#A鍯H?. e( vrxLFko&{|:e@`ڂs9!ˋ q?k#1A*T6IXt^&ƚ^S/OGKN\F07r`|VuoO ּBǤI1jp|r i60aN삾?y7:2ȧBK45}KhL3F#w9z_k&F YǵGiOR\.r.UD+/% a ɶš@%BQ ]EoW.֟F͏đD~ru|HXb?%3ǚN-ד+EC$ñd@>߬&,P$DHa@b'g%KRsur?v F$;;'4}{CoeK/|?v#&,"r݊-]Ӑ9X42Ѱ8^"srUN5KwȾ^b{'HZYWn*hǂM_Y]kɁ^ `d5Iyy19,9ZJX#qR ^ۉf#AFiFDV[ Y, 1bkH,l틜)\#z xDt%l霂CϞ~v_ڹ|Ao#w X&ή ~+z.ª88рB"'o8oLFM}O}v&Sܶ8άy7hYo~o0JL"b߶T\fQS*b梳q//Lkjkn9aCqNw@05wo6OKwA.+i{8YF+57;Kf^D%w⃗k̸x\`!t(9-f3qz)ȬלݓIoLeS.mf~Q/ ;1漺ljPde >;f[ jnVZvΌAUQf:w(\FԊ;kb{:`1X6y|L^H$:V 'Zh`\OL@AfpP٘9w#>/}6Mm:iM6/ҼOϦ=кy޹ |.qVMӮS)qT[0)ì-/Q Mx!vd_Fµ]o)9z;ӑI d*ⵀ9xr eQiz{1y4e)ۀD vw>dV&#ϰɔyK>]vjkQ{4^6޾ͷ h/70!C[*#/OAEpXx߁< gZW"c&ɷ(`p.5gn*}$l['߿ǒD.8Cףр>[E-?=H0&'3H޾/ñH}2_/LL# ۠y-Zv|70,1ljtݿ:`7i@\(F88zvpT"X.Qhhӛ|߄nf\T:r ?]廆ƥXSa~;;lu1`El@b;pgTB=Y2P5AGNZl ~o؟KCЪL'v2+剞5m$"]q4cx,qE눅~[[ca#S[C! ?"'=..F3} -gU[&{aeru0q2DiJ'eDX,/Ͽ&qX[Ƨ!,Ïߐ ,S& |_b¼&8l@ ǞYbl'*/qh䂆C>]>D n,4o‘OfWȱSûXrvtZC s*{#i|(:EAǬI0rnw(p͂T 5RzW元^v%fv%{kNYvJ!AJIz[JdנP:Vx!.ldwȈjD6:3JlQ DvI`BH [#D?Ȳ%gsbՁxəjz|=K L7?OgڰH#G ^UAQP462>Pc~2 in'HX#2Ǎw:l'B"Vnqp ^%72#3-c (ҁ<.KXtm`u[NO` HJ4Z88W ,P*[M YQ@>j"7kVo_H8(Hp$݋i1ڛ)=1@q_3!݅nyksM_Y><@Aa kZM&㌃ۣ lR$NO\ pQ1B*PJ |:t."̀EL/:Pt8q*x91GQ4VUsL^wD1Coz<!fƬBذ>Zj%p}ŬjՊvףC"}E# R,T[^͸6O{+Vf+mz]z&Ѫ}z&x\VzR^rߋ߸6Dwg/m9CjrU?ebʠ[9L9Wf1\碋nNtPAۏ/?ꁢf1ىmE 5 v3Qa 59͔2I25CG0wW zyw|`L vVM!hfmk<њԩdqdfsXAlÈ4'f'.-`4נ8S~ #$е⤛Bp>{ d3D,4)_Rum ɕH1`7@y=r(@EY lK·! Ѣ2q5)G^ǥ[!49M.XrigvP·1κeol.72/g<8."oE$-)4GbFrK]^6-}fӁ!b+ay$ӑK(~{{'fѭj>vq25s +{"@bbɠrT}}ȱ1w\Uřc D(:i= w>?.+3:@/ݱ0BtGc$۱|b<a'.C2"Hfw*{z;#m ~vSZ"?bbP}Y̘~q/(ʷ.ߺ h|MS/̄A\;6dQF_d_ 9T\F`tJNL/|M:F DZUDuQCJY>@txvA|5fhM)J7Oy `I(3R#dG~g7d,J]x5ʄQv3Pꀵ{+*-эq"c=/v]PD7:DM3:q6&h੒ I("X&`"EzJ PkA5G J?hZxAZaX[ӝƙw%AYbnE*NDvlod\n'wO8)}d/MNgnKaW@~>Z8 43;]+bl=_'cM"f'شӿ34Zѳib4^K"۱ Arn55JtGZ[Ѻ-UzzsۦɄ)arNHg|le]9 F~v'$q+B kt@(o/FOǣ y*G([T5L,C[[헇m= ߩI-REI%"'d]Bl)|o:(\&*R8LWzͣ*HP99`|{a)4aɴ%=  Mxtƿb0!R,U0tnEkI{ʓl,:~ VRJ~K7*¶b77؝9ַl%AWq Z}ԥO MbH!OXH:*~yYͿXG4҃ .3y6DD58 '-:  mm'F=snc2nܭ4tauEi\劅_j闋_rȼ SQP _2Mʹɦ.5ʽ-c]vKs1Vꔛ<O߳Dq'F-r@#La=ٻ.ji K5.d{eUEsYSm^dXU녬65A[VY\pGxrP~eNz >1<X]n32 0/e%)ޓ66Y(=ya)D]h(z2eܔmԆǏP}Mlmlъ{oSµq64?̝,<ڗ ~k=Ⱀ O ?]P NܜrɍN ɴОU ,xpn hF& r,< ĊL,#59ZL뾱*To"K(4allAsR{un0=,c/vN}jMVE6[^PÍ*aQyFdȌֵ,Cso.lNSx>zInF/Gc2V@_Q5Fʴ9N1rm"N|@h0O]'n}~wMz<\.~ڴ湤AF ~jqQ=dmIJ6YSL}(nK82@ڻ Cƴ`>N/1~ 3˓N o2<2PܣBKK#wV w;|]g+*hDSٵNO* wQpn7HeBY ܊p#~0^A8vS%ä.>z/L<䛼ɂr:T-8ޫԼ蹢{ 9DOM Qk4{czzOZ$jS M~+Oh&ەuӣtsu~|GO@bO)ံ 1²]X%!G!p7b>.Ń{Y7;FmHC$hD y6bGğw ^ݺ.~oR]+:鿧 xM|sƝS' wZyNw85pfXCVMA<;w/ Ԃî@^J³ Àd2dQܟQgP0kU|v~7\\DifM 5,g6j< |( 5Z'@TY>vCg)zyN/gj E‰"9SvGzEQt{IΑKRxR](9.(@wzI_5,99tZ-;yD?MUW/1>1E'(+G~V%WNJd k˅VT<(s>7|[Rx25({UR,!=s‹:99;~o[."ZaM2Za@0(`7 Ŕ qȖ$KAKM49 b#$A7ltI?HOSXG;xٮ(_uڪvmJD 7sR,Sښ ?F8Oeğ52DF'C E&l2t}m.nْ-WJX"@:d ?v2W'MдϚ l1d0fV%\(-oI!R$N_l4}& h_Y;Tc,+ql70 $>=+a\ G=Ky;rE&Y#&Z$mE/拵Y^ۨuV'fgWh5]a_Ө\L\ËS:M,uP;_^wD2Ʒ鹮kA:G,qF/)t "qT1[ckJNTb/U$땎!4U K`vp3Zq-w p ਖaC=~-Q_]"\_zq2o|c&ӥma:kNw> Qu .\Åp(B_[aERk\z%lglaĪՊaԼUY9U63[1Ol'6' \{ #bC*.o<{մpқd|e@p*vo(̌\xH3$5.WKIHYU2?9:qCn7OGmvP7 x d²5jF*"d Eek4qD _?.D4Q3mZ^ğe/;~8tȷSXGELrn !*Z}-vi:Vd>hʬ 28_ Azi`H."^K=?2Ě$soydM"b*~}zSzеSqCV[7$k|B[ŊXRv?ZR#" 2o)ONIxw'Uw^c\;BI DΎZŜN{v!V41'dH`R(;3(Oa Ѡi>^N6"&ؿ'WaHWd#ҳ` 9|'kgqMwev wH 6)ua._ [163jOk@*pX}BA~1܏Vڛ !lsђFoR}|Uck%_[5Ru`b$RǮr`L^֡~ *W>٣?=];*QوGQ6ʍ?-%Uς*lfűFC?A&}%B@⁀ևl]k ]P Aw^oU hNhwnO..ATU-Qٺm%pWhx8d;E081}ۭQ6e㨝gQgs0N5(r AFt7Fܣ:L>]-:"7 e)a;ڭB!ҿN* IkZc o^ >m)S, b pcbR5!{Y%rAؘb|kn@UjQNAFXFn ;k୊Xbkµaj2g{uR2 ¯7GF:] f( -|DŰ8Xv68 m[ކxJd2^̾Mp6[?xDS̐6!B)e)Tcz 7:ZRfie A"'[nK0+`*U`?Rzk")FhR9KGKQ:Uj~ZFQ08!)"^.fcճg%,,=d⍦Yn dv cԛ1т3&#O<y- Qr9Z/gݨsiƂMxz(:2VA͑@vw_=g^͉o#S_ŧ @>eɵrbcPT;/⍍W]/Lt[J%cNWQ;uUd ћa:2qIíWo>F=tV*( ZXv0A}ғCIIHh[ )TBLP,wSb@yb3:K9]3rzRn &p $\2Co{M@а#Gv`[J DL&@{;)J9[ Uu5-Z3rM1~"!0Y*Y̦-#h}8W3UXLFrV{, qm4l^WWH0΍>CuQ+ukAkIv1SactȐBgqiSE xLWb hJk ƛ.VͭاUiu897&)2=DwE^70r NW˔o# Q>H]Mmpi;zlҵ_&"؋dSَek%Q@(뤬-TY5 ΁0b ZduR%Z9$aoѥ>jAݍĝXêWCg衧`^^jP4c"VK+dOSfYFPxα9u{'䀃nt3"\]`]lXS%*/ &Ϧ[, :}ja~>EWmkbđRneL$eJ,_"]x T~֠veh'kPPa%V\s''hs-KP|h6̑$?4#[оW"@ "3q⍋8F#^5 +{fIQ˼ o52{nxΖZ#)aVy0T}=4+ uF F4)ΚAoO_ DL&@\TGәh=+‘̰g7a#{ ;Xk=&Y:YseTh^=j4:ְE'oYYo6.jT6l\+ WQ9x:% ,cX k؆xP U\n޽EҡXb$[ :1GmBI1"ɗu6Ǎ%b#jJu!&=>ݰ@m QOn m9T*ytBlTe%[|ᙷ6 u*\#^mxϱkR\p5=nlRwݣ{S,#,iN^oj=6FPN!Xq `\H{E89S0/8tm7X_OPkz>;*ER֕!=Mlqj.FaN㬈maE}怫1 7A6څb#Auۖ'4wOLÎڞm,k; cm^^v{V@bd4.OWP=R9k ?!ttRJF.N1y 08,0ҼAWR{oy=JŁ;wB]vvy%J%B pepmYކB gF1Zn1*!030$d\r)4YM%Eg.$RsN)5^pF"'oVYgɮEĢReR Ӽ6CJYXR:RV߬#*QpnN Eo eM[VoOkv*:Γ^m1_"MT$p&lO5((#k1D+K.21}FXk? N:E4cIa!\5|(ϴYIcm ܻVع]Wb:wS!aD3L|rOҎ[\jG@˩O7{,+w*!dC蔋lkfHu!6DLCME.W(2taҨﴋ0ek!I4p:w4`9D*k&FޠyczIդT<=+SonM 7RZ6t(-_-a?GhFn!nӕh{¬+^Y%ƧEG~_Q&AAA fE|m%A1Q+榫cKLDKʵ( Xf C]wz_w'{{$~׾up)ݭ]gkQjν+ Eg2\~jA.h츀Fouv& 20pjZ҃cM. VmjZʯ\.mi n7WP_q9*WL& [/zTYi%ߔ(_CW,I8`Tu2csvv;%ĉeF84T?/$Xo%C:^ *+bX#Xf|zCC~\ݽ^d򲳗(Dث{Zlyo=%a7X!1TD*>hR3[-"7봆W C2I@ WݵѴR$> ̃qb%E#,S]qYDgQ=dqU*$s+o׬mqhJ/H! :LQ*8CTZ nq HqŰVr4,]S12|No92,q RZ(Z\XYyo eI׎jR9A0U,;3JBq@|ډ;uLF02bi={ѳd]frAñI)˦YV) ѱigW$ˀk^M2udw:-6RCOè]E'͆)5Ǫ +be_uS.$xt e mf.{S<ēo:KfSgpDI! 4Iı4\ئ@lapc1͇t2trf}v蓇M&eb}rwnw s j6_.co`}cˋnL<7M/j76JJ u+IF!,ɕsHwc2ŴG2&x-Z{kuSq1LQA0af'|>r%z6Mor#9`»pUD3O|I8La(0 48+-R^kjvU[ZtY["ݙ HqVQwjhqV]: /4[G !Z@88 ?ۢ7[:@TW8BRNu!IC39}T{l8^ D<:=XwI t2MZA&|,SblUA<^p\ެL{TXU'C>`=^DxmXYMu1dxR.n JM(GvyTUd+$7-̲e~dq뫎/ Q |:$u3cO#UA鍀÷W82^S^-@[sA90 J,3+:O=~XfDkvB#.$ڔB%!+0*yS X=O4{_l#lPhĽ/ FwC|JaJ:NNrΑRgi[O.VLb'#^"'.##f,{{Q5z7k@ 0չMZ%8&z, >5 棥#P=MU4p1NBLjYr k'NLe{J:ڵGYBk9qx)oQR9_-v xn\+0ZH˗i/ <9!jTbz8zJtHhj=6C ߌ%"!lX]ٔyDGl-V5&bb%%k`thSU^3 G-fy-I*8`1[RJZVcU gު&vn:b3Fk46_ PW zcD‹h$|=άϧhtFg]#?U.$&11~ĄpM3irq(`Q M3\9YiH&:%i_uEke:7ݗ<[l]peeZxȢ7xjAm9b%+F(Pa׭ŀ0zv0 gZ NKNgg71\' .ޝ]M4H)较]QIǝ .0)<;bu6Q؍w2M||?`|Gf^*+rAa}D>& Yғun| .L8kv֣ԅ5~l+OpsŊq% ق0-OlHзWb$עЄvS6^)n8WSnѻ."4;Ԣ7;0:?M ;wJIv䭲,eS$Wrsa(ddߔo@D'@1Ţu6ÇEr% YwXhR{o6Vt4!4͈2;e<پvV,'Xe&V< tVJTwH}JB?H}REz ]!V*Pfiq hW3Bj%4a! 㞪֣_4Zh8ofiV|Q3?xz}վɧon/aoEx*j>fvH(;E>Y3~z@0&,#h0h~.~?6GrH Cr-hyV`11ħi(v!l K%'Z16do HkU u0'Sb XDS0%jKWȩ&"ΒnoO+Yd\LwhD7&SXPzVâdQ4<}QpF'JDUs5icH qv5iJ ʔ] Ml_w- {2.C.AFXcp|([B/ۚ ,RCmC>À&C5̚[`~=N (6}'4vG^.6 q>ˤ.3nEˈ;r]Tu!V>m\wLhp?yFQȤe\/|=:]@*JqP>O 渰4 GЈ4 ]ĉ޴hל,~\krdxfM;n2kgOy[vzߨo L@l7,O>; a \Z .5Y^ӉK``wWS\b ŌfL"<ťE9փ+ w43vŪİBņԌ@[~ M{.P0,tX-Ө P#|]XS( q5HA='{ߵ6%O,Xv^Oiu-ѻX3G뱢Th0xǚ]m0X`9ޟ6>crqsW<'lqPXgse< qgށJ&y6ZD軯Mm%UtW2?d8lGpږI!Ybc8Np+(9=Ghj^ m8v 2qa?QN{p^"p>HN9/suNK5*g T^vx- ]Dwoso+0J#3Ϧ7E$;hWo\_hv݁.a]~jU-#AWI/L i}Xws4]F0!F͖]DٯsP cHIHv˹kQ–3<1s7D~IXOt[FNGMQs0s,EQB/~0qߙ!Fz!O ,7i6$tݠg[^T˦{̵jS q6o;FUIWt6dz5mOU(c9P\%㷄,eڞZ^L5fOqOpN?M>S`.)(8a^n (D0Z!sU.̌F,73{HtmTH}QEױjC!YEzTasG P\׼7:3T'S{Q8=.OeKG{7zsq(8X%!$!bgb:aln_IzA$嵕͚kH=]:Ƒ^@ #xDKȡl(eqCT.J+D-I„JX?"`Ղ!`*d5Ĩge Fy.aKnp 'i3Hv>+2Q-Ւq)|!8KUkP$"Cs3Dv*$fמz46EH`CRb 66@ 4tq2M Tʗ`Ɇ9ɽiu){,E'ϬcS:Ghb hB?a*RLC? cZQ#G _EX4-,,UAK "xP8 uwjyc'pušIc_Yp1kvDy$)"@;߃i;J@ ]TҟT+*A^||\ԋ^1 ZA, zFPoÎӆV;`F6&'y(#S2ӻG?PKv#\<8j:0 eP9ŝwQTXYa2Wpm 6q1bܞypx_Rt=9SJuf4ݞ ZLR8MN(I?Ay`"',$xH`deBAC +Ă8F,aa &[`Ka q75S`yzEed$H+Oݵ{)=U #pȳhEЍ#8p䲟$]{mwsj{ch8 PL9^)epat~$e˔D >Odo(@M?L>۪rb@\ {o(\ pA!qCA)|_owløVw+@*;7(H|fWSOq[$#I賦h1.w]QЉKjw@Xmm w/UȐǶL6]&gۂ7erM9KD+eFWر\)C^!T r6zĚ7Vs8Gdl`ޘ`UVo0cQ7&B uוKGڴ w'|s# &:<C v;lk;jT d250linTv2 9mؽ\VbhfE>| :;k;b&ΊFUگ|% sbFִq݆Ig{Yuzu{/W˸2 {X\Zcw:[֙Dڸ>,'2Fza낰cFZ&ew4ţ'Cwc6>FaCQ4'QK\ 0 zbӘϰ1 [fO#@yo q&Pgk,њ,YDI6=p0pbiJL"vSփJ& CE56SQxPJgC s 5Ve_ABAzrPH`|xvp GS[XuaΞv>%*ԠYFu#v!0Ppz@B*HEC< o {b~Ub\K!w洇ҳ\zS~r1µ.K f/o;ZC ry7$w^wӼt Unј$&E G{Iۣz &PpJ=cѬYp7/$lzt/"D>1i^+:zڏ||M'1vo/[xͺD5{q'd_-o #omӝ _^d'!gEԮ&JܥZB򆼵xN8>`:ّxQϪ}]LQxQz)Ykwe>\.-^uSkAcbE)h!$9*CXyy?*3TŞq*Ul~ ,]m5*}eU01YjE1y0DOdv`TS{sVVa Iz:CX"uWt@U gYVwѼ8i7)c"^rlըvmG|za}oC¹πt1'DnMM  J#bRx1㏋:SGC09H襭eSvCԸ'Wd."*󊄢zbYQMt3ƻrB_~$wfJb>ôWfet Ob)lu( ۓ*~*Jd'O<BptQ['M7wMdGm=r4гpn?zS2=4{8]`t%IQG]="~Ҹ@[{q/JT-dbK6~ԑj#t|Ck&Km\E%~Xvݑ6cEsR(ߟ0gg(;6β;Pbn;8c$e$= r׼VlwYW~ؚziw}y/k ~ۮcaǘ ep4&挫 y`")\E``36Y-E;$W Sݡ=}v(b|>wѠ4xNa˒9Z'SǥŽYiM.mQKCu+}] C_ȅ4'`ElN4]!Z]zprjaFV6fW[57fyoq_RLkW'NYoɒ2{''.t2fS!&cUNlp 8^0N`Uo+s`DŽMAPEzڷG v VtDe ̷ SY.[o]qq 8MƱ]$vWP;~426Ee])s!u.oyC]7x:"!Ćh IrG &NNdJʵ1k;1Cx!@JkD&ytq2zymy4'5𚘏_}@|UHVW^y"!ׯq3 Gt4;m:e_v86R*g߯Rʪ A&veDɬ`eqQ Cm01(u`tfǧ* bLeSO%m*A˂:uatEnY" ~TNhD j"W\q{Rh"= lFV~fM,5 wgci-l dø T3V RT&qm]FAϾ_w#DP^ᬘj][Ʒiٍg^}|eZ=zŷ2үhSd7'l{I{5![ϻ{~6:jl5'jmБXeRǬყhW~?={&Hp>ׇ],{3t"{CP!رntoW@SԸht)}?kF7}?[4B+B kaoTm6Cun˶w4ʅ^(tE\~G޿y3GM;|%sxC_7d'å%i @-4A߽` af_KtMN[<ᗟXN Jqf:Ɗ-]),ͯ={]zͨn7 4xL~vz{O(}fS.ƬvRv``O~yn#a;i[%p|pcJr\Íݦ-쥔8tTF̖ M~ћ0M܌ !5uy8`v<&<0:ІVn`k`oJJx>YCkJ~1N.hS+i1*suE7f$<e6KHKi&,4NuGW~\׏|~&k!OLM6 c.5->++?NFpxޝh0OcRs,U>'W$vo1Mɘbn܌nv-ڱ;o1݂]-7yY"(H\N{eÚk&41(gpp5%5B,Ul;S xRXkQn@(e;e޵#̅{6uVD((Ѵ}3pǟ?GKC0lEޡBg<{3\* XT~֢U&>k0E{^;I>$"%[͞h4w4eET L.^F'=o gw ,k -+}CdD n>D<x`WEw\D9oS7OYbZ3M^9XEUbOa$ ,Bܵ&\YҾSˆn+}ϲ/攋@x ΐY&aBz`[wA9' [ Du7:]̽/n5(4en4q7 Vﶻv}M$7"zkW^a"ǥA^tC3A£ޱu ~9JZf;eAߍ~1]VlNg2Z̺56EuY$8uoH)oja'Rzt[6mi?l)&KjeeQ  =nyK0$pHF$:%Rz_w{VC/U=ܠ`a>FU|mgtM}&UM%8UzOio#+ȨslATtT y))ǔ_. W|֫.QVP.@x71l ;?{/=ܠ :u2WnC#_q}ZWS; 4q٤/rϳNbihxM'>hpԯytGFbll,9mt$[rf3T >69Ȍyl\ΟenlIَgv2p+jALyD"?XX!*X"kpװD{x&Ll*,IY4͎KTᵣCg C1dC^pQ!C(Gos9ba/lQ@Ԝy&w uorkQ+XA›FM&g_^Fo kÃ/ub4^0 4:|qzvemʄGz7AV-;qMgp4#1$ @.9r1 n ]: u)F:VaM*,z$@6퉄&zM&b:KFP# HǼ1yoppCm-!I1w -9 t 4VIA9克OF v5/;Ǘq tC"37ˆ .f,:!M +Cb#jE%|(T g5*8zQЏ)WJ + #RDӔ(%tˤ9F wh<귭?otRʂ4ZoKhg:EBjh]S<~ L4s6FhkkmdE5p^4fN;2 8Њ;R U>>M. M ҶաW~bcŒ̴ىPW`Z#L!Α͙S[*H #s*^=APjdj\ ,i p,Xhplc-)3#Z'93DTցLJgu~g0oO{Ӵ{让&rPQr:mdaGoP1%(tsSqjj&68WK6ImzU+CI`kO i]q!P1;IO8e< ] H@( # H?$O no ,Ap,SaB=f&&Iz)mTWBPFjqcw8+3kHuHz0kw,>8ޒf9uђ]bh$Vi{k&ȶ_]bŏUچ$&\1oc▎'xm䧛E=25 fí\b-R֜%fܜ@Z8Ꝁrћ(i7lւbNX4=w쵉.^15w 6由5Er41#_#fplf@6YAE4;჊@(x;˟ּ_ 7j+0b kaNqB6v\ EdXuJօy;ڏ6| $|CK![ pgVlmuI'~ *qIE1| +bG܌%ǹcTUّ Om&BU'zץ:]U{ BV^7b3 W>Üm؛ 0i8ˀfcthOq_[pv$"8"$}>dpl7ph۝t'iO[7م*x~ɣ_ LA5up_r0.vh?&;-Xwr/?xxp+KTM&*4\e6. 0eLJhN!ed4κ3+]1$H Y&3KkŅDKpig`XJaGޒpr?^7aF9/WΎO& O䏆˒:n_:q s(t*-w[ xD7TCH(s?綁aGxᅥ[G^{7.0Z)zs,tL1ZԜD8\ᣧ>/q>3AhGj{jg%!QWE!Qsј#pfgU8<(r07] )-\REoE3xbJsG ӷƆ ^v}3Wq!H,C__WNx*Q|4r?w/Lm)i5XUb0&Q+]8`|aߧsu)dעHqj Up`l";9XnXgc ꎃL`Âi5Y8A%_Js%Co46]BlQNmSX#zͽHf*}f&#Ѩ˘ "w'WBzWC{kԝ_]AnO},]/nEe5ހo.kr_ZHM3 K$ Z匍{)Hw5j/ye"BW8SxN{;j1XS,˂ԛ#-ZT8>̮5^ʦzVteloPވpw[?dG^|oDnt`4kycUy8sLIu7-geDA8t#T~#-7>]፲ʵX` r.Dp*1B?Yg;+rLR'd=2aY8iH89MExDHݐ15REO^4}x3XXbF&lf a)4 mV8S Ǒ9#lIF?^0<m:KRHK&#ڲWP. ?ot6:7:λ7o;uz/Њǰ^/-ҲM>%bq}'8j/Oz[PvPt,[Ya?訁VmCVҨ_{̝^ƅwRu~\~?ЂCef%1n6zO zpaV })*oAl~$_,vz*MVTcVD` {qǫ!ZkJέf$c=|罁>1u7FQja-Ȧ5BRI:(ֱ#4p\nА4^i!~D>P4N'h"0ܬ}n9&V]a</ W物 w>% zSxvGveyQ]UΖoijN[爒6$jlاm{rG"m>C3~lT[ ʴyE<7(8^ͦ=8`Y "[쪔YQU'͗qP:IP"r5<uB -^>d[ޒRȢTɗtθy/Pfkb1 D'4'  L`nH2{|4KC1΢j'}>sqzi^m)ۭ 35S9Qq0k. -*L>WT8mܡ9L+̒!D3{Cꨩ c A,3/,"憯h4ʼnbzJٮ(6cB3Rd^^A9 FZu {\nl,XX L-Ec[jo%`{C~q sL}bWU_J8!5(]9Ԡ=+tB2u:^v޲/xY0&8B든JX$ tB{Xnz)bT邱%hU;> T:y“Wmy\ں.H:i!Uo[[Fٌ0=c.NXSTr^eJ0)PNPG`azf ;eLj R?L\Q"߁i> rBpQ4:Kt)Uʍ!jg!.RG>9=9JrYP &P?Hr{@3q06jKI@t(y7Iri\p'vUn:/eŰ)O|c&j+ =גrɪ\\ȶ@2-l_/XrEOVxi~jE +psHI!S8 SfBP8j' Vuo@X|k(~!h\>l;TΗñ}&\*)(upR)2Jpm~bF\=;H:1-rsO\H)[p̼27glی˵[ZP1 e,xZ go'x,0__ YïUX໫P x )" J@]j5n*ixeWNXV@Wi OԎEqCŵ\ ~'*D7'4i^Ğ=(ضVx =Uѐ0zH. mC$bGK^0<'SUMYGIrA8я`AI 8^Nc*Zp0KM2t \(d|1+wMce aGB7k@qC 쾍ﱞ3t8a.{02~hcA9|At 9h}}:!(IQ{\uqdd|{FSs $cSC8u}WU˲*k?+̎)k/!tjD"k.QAȘrXTWʣr{xtb*(7-K 9칧u#go,lR6XK@&WjDf۱\L;8I@EPu<`UhʇW$#ޭJ( CZv@ N8e~.\l“"j({_Nyy s IrŎ_*v FR[^sk^{Eg쀃@;`u}7p˘l.>DXK[ÄY7tn<փ/ҠZyxI]O]i mȄö]'l9cms>V͎-#*CsڣR~}PzoLĢT+.k /U}ebLrxD1@{^N̲L'&#+IAne>1a2pi1 I(l{N.TH ?ɨ>8f'ϫBtÖR~pNQ`y&kWetM'bٝS5 ; %љF^1usJTSYAl.E<q싕s{z ! _s3wYz,Q9wl, 63NcVD,eN3ִl7ww=9pU/J!on~OHAō 2<p-HLaGxm4$JRό]|:.KkseDL &Q;Gx^u>пޤa]5孌'r) u[.d*ޤb=0XQW >cPF] Iq=inG|g!K\n2I)qqI"" SAv m'trBrf BWr.'7V40 t&VćȓuZ76 U./^ `VDk}k^.IaMv07E, C*d۸FvF=D?o|@!I0)u[XYL㏹Yi,HĈ%XY f{"0[_MU{ρW5w~*'@7{ uaLϰS.-C$di2:d\봕2p GM;uGĐcr֬\2đ98Si~D q1;_^;vV9DQ569'y9qaݵLA2=#TdK>v"t.#P3 6c 3t۰ur.K+nj_˼a,-595Cx.F>Qlև<H;0[1tJ%sv9v'ӻ UW?S7" w\Ր~T6(zmzdyq >h*|"T0jGh4>(x2\ES.i)TۿS@3 cLhػNQϕL;J!-'4wc 5hib(AB5PA'^$ nBt;d=}OƃB]7EM9A,(sK*BQ\=hNAl\cu,dksZ?&UqE7uEa%lP^ϝP̓C,uprM#ۼ+J9;" &c|+exOXvVV,Gq&^nb!Pl@\P{ nI w'>9%8=]JExgRQE=GWw w^b| Ha$o$Sf_"s1` #h _ >G3 J'F2-b~Pn?&F-DϾXa3ڎiT(~hpʐiԁM ]q[$&52-w%, 2:tc-ЪOtM22TMjhF>gX % dnܳ0{v g0Zm>۸c%;P `^'qlbF$ އ+O!oLʁE$rr'H+:6WB]tBUmZa$AU~븹kI`G$QLL(3 Ǹl7-'Wx`vF,3\V Ņ:PWep6i,[[e=.+C!E=ฝ2"ŵ |}l() JLF%,g~mK: €$ͅ13K%_)ۇ E*}89s8b!5efTݍsrm*ΤмBpʑ], (7M" [b`ߩ2mс"eԶC6a~?,aK|W} wJvFӺVō rI]cDK B[Tjߖc'HGz -lڒ p\UQElk;ZϓStsH` 0 _8=aJ]*xODK+9W;ߝߚGG׵&̀B`V"<{GD;ۭi:͓ͣsxrtxB; g .gzQrܙ;(A62Fp F<#O A$.:uB>*#= /:ρ{M(_[kr蜴ԫ&9 aH҂Y a 3u;]pTbu}& 4ţC%^@?zzn uVeOdf#9+%Wz󏜲=kƽ6c׶u2OngmF!VJ_:粃>+i:*P?e*wLOٔɔEt^H19{x qbAx%gxYQU܎ v;f垒h ` a ְbx6V̙h˪+Sә9+AxϩHmکb-3ڲ3\OE6T,H Ls _&NX;gاт` !fcGNL:<ͨpI=E`,d2#3N0d (P+/a_Lq*37sbO勏eϣ?};bbOsLˣm󉯸:}"͆n0ӂh2IuX6m~:.5 ԅ(@4@`-ʹѦaYZE/ahY89u &_;z!p Z,r:v$p {R٧+8O/rH ,l *`E 9cfM[ɖl"yi{H m{懱Ũ;}[ܳŻ¢y Ȳ,݋F,}IAms 84CB&Ϫjq\·lxKFf[a{t/\ˣ **apNLJ_+|2 RzYȾXw1{ca O-) gVqfL  5q#8 f7Ghjs&TQ)1%.Y]u]@bKDacPЉ] z_%ri _"G7-:4-/b"dB`bt~v29VoS/b1l<1zr SY0xg~4&>aϓM3kMȕhc(}bp⥏de=Wf΍ZA;SqN[mj]kC$]RԡT,Xi !0]'U-d#Io&S 98cCo/r: g4gkFwxK]|bq'[l^Eq 9"b$1QCaʹ1ѭJ sT9(+}G)cW(gh[o7޵Y/~>Z@iҲDw(P$r U1Amé9T$w 1R7ޜ{9P]i'qkԨV ЀnU MOV+G*>LՔKsxOJ~&d1ś┣Ā2zKx?#UɄ;UJzqԿ8=" DxXQ s4m ^Af&k{+_2-B{dLje-;I]h( Mq/&)+0 N|B|FqVIqJ֎v\>͒r,ax(%M1D:cSUȦiA)yg~g5N v~qTSU }}R;,=ŕrXc+nxrPCƱz=eaKwpvlG~/,|@us:mG/GtkIg'[q:DK%j{X)ep+_UGl' +{akK. 9 uBtPt>( `mTP:%+{@xM+ÿ P{~KQ_!8 1JB%1/>{-1K2P ʟ%MvoYeTolFhՍ!OM,F9&c4ُt1.*"<5 D i >G( hh:Ytvv+wǪ Ƀ!RKɣS1>@:}>'9A;JGnV[fDӟ^u'9[g)ͣr!%(ha0?sﳻl4*|INzІ3@?($xsqA _\\߷Tޕ4׼ ;5uY5X}/*8.S=qԃ&_B9[לּEcHG&4WBApI{ +UX)};Oy<sLEC2tIyfE$r6@7u(<1@)B,ժpAܒ8GU{˪^|^mGCȍ&b)Χ>ýMmHh$VU#f9PcKaO{jbH㨀am70Hl\8r|.P/0; rMT5;G`w}tm<ơFGdسdN7^0}k&GPui{`oBU_=#e7& }ȅ||/̑پwL7;׌&2`W5UU{JoPx.H#}ft~kgO!"CDg? lx0l0Ée:Fin[~\3ox,/;_y?5ڛ/v/[ΆS eols_v>ȮClu0 tu=y~C垵{_W鞾鞾鞾鞾鞾鞾鞾鞒+cgg9^N6y.VIH)?&J_|t!6b#??[c~h5d9*/D?)A^ ]q<'H9Jg+n i*Υc<|&:M1k̴>K$y4N1r{,ó\|L1Mg!sئ,?RJ7r: iʱ3 ݦag9)yYN LҢ<_8E1{qS $)#i$cALG4 o[K8E9uY\eL'Ţ>ƴXL4%#;><NIhomΩKԬ2uJK!)bk-XKn)u_S%U>8~{[F!YΘ|=!6ryJ|s6q}?3^oJ]9&˚iq.F9:N]Q=TJ@<=C鴻AI lWzt#iJ/rgtD}zz:CgPM/@s_yn0|Umsk# 2j R|BO(+ul#pCw=~/q,A9P9N-q,giÞodz|f8Y=i3~k*o5J".'HY.fgQ' Ry֏q/P"T@Cƨ*\xfȡiZmߔ>ԃLc oK` 16Y =Alf4eT8æo, Y~E0(nlA.Wixf0X~Tgee(m=w17 "D4r2"cH1S뺤A=sb>Wt&;N'eW YU7wN`tzh8NIQ3 {g%%r51X& b7;8ghXX ;ژv uH:iG;r~^}0F!]4N+%<MZ@o'Fً/okԈcg= N^T<ձ|׋U')UӀ5)7&'ٜQ}&^̩M,epSsnSg7F|0=enFs s >~A/qӼ,#1ZXH)#d}~>b^i/p>=6FQ!N'T_`-7DũucYs>>:~f)LeSx')St*A#GI(PGa~{[!‰!ϗ*ǣ uЙ)3QJ8 =hK?$1WPP!?kp>ӑ_p 0JM89h=t(Xf mKF],iJ4-L 5Y=Y?Ś fFy{={?klAs?94i`\V.I`M5&)s9kºT-x?r+fM3`yC] vy/5- iqԓU i7E)/vN0%QjFsF2b#o⋁,S~|39vfzʓs˽ V!n1K{ŀ>AB>Se>40?5EXq^O=K-ȊCu 1ToU1?zZzW=?T$LFG!r:ŦKBG*71Vp[H',(cw1Db`1S[L`ϴz7 ӱgwuhT)JX0 c{P-(pO9攮C*Han uVYYTH{Ϙ{"<]P[ap|.nfR(]>\RŎu{1ݹ;9O"0Ր]A:U$GewO('?35cWNO1dUnIمRHq-_=I̯h793x?qC?!QgMF|3HTzjV(gev/*VoC^XƵ_oP^L؈ ߫vYb8"o(9~6f)ӷl=rwB&Iϸ?t[y xj.J.!)GzPPgtRsamRsԓJS۳뎥t aDzÊU3@,I{ԔxA+|nlZpzALg+K,xeے3^U. 7`lgYpTs73Q+(4)"ƒ↓{OnFIIik҇A8gdP$ Vr4ljKƉ~qrVX2:]:aLrz5_`T'ﱒIx'^M]b{:PBHO@Zzj.K1+(. 8lܴ4WYLu"{c҄f,K{adq0Иs;ʭ5[T- t(A%W?~iobo\Fmu8ZrS}E\+k|p胋}E\+[ |p胋}E\+K+"x_>>Lo\Ž焜p{Â}p_0__ju+C|;U F2H|'}0#-x$݆ՉHYIU6.04!&%Qa'/#IS_|a#请2 ni8yr^t=j_7.O‹h,bEͬ ar) %]qkXo(՘gq nCq9F$]LCJqE">t`m`$^TA?l܏B" sc,%Vx}䬭=xk6ф;Nbeb 8j Ɨϭ9)m8z37m3GwD'4^-eTJ=w k7A:.e / 갚}t"#=}! ZcPԿ` InK"-iv[kj?Sqsg0 {oN!$a2k=S`ff+ $ ;/nGvCtt UnkFлh]ANI}%c5=YECIG̩-Pi'ЙՋ(mOܬR>@4H 3f:, ^PhDd?$( 6|kPd8V8S^tQv}nvAW2[.h'@$!N)'BBFo{˕0@n(m@(>q7}Kwh8L@S"))6TMBbR1 qSG@Hٹ<]- -H'DySXTIQ@Є^fQԹ 2*RcҵZ1]`bf=SfQV:5́Sq8St!1TxNS~3eWX/j D1dISAu6چMQH),"㆓&1%^KRh&dh>%л?X bm3NFf #z#tx6ퟠmj.Ҽ3 9m:c*>pŝhFFhc gI4zQknZ[#KIVr[9:1q6ɉ)L;Ƅ쨹k~6 T&a͢E[]rWi!c4FR-9[lQ']ฝN0ޠ&m #cEVKA %m ɐ}e% @&agp+~0 ԐlAVRX5dh$ӨiƸ~xLܻHc1?ۇs&>gP' W`@Jv4Fxk0hZ{1g! lBMCF5l`UECAB)Fȕ3f$B;38ЈzZx{Pzd?X{ܯ.]&)PTj'аxvOf<& \hD/q"V,f65G+ VS:S;MJf2U)/ s]y 2!q-)5o[I.jEч G _${WTpL@JBGMm~wl"|+ qȦ.l .0Ќ]SSөr`b}љ*a&Ma0qu.;Uʮq;89><9 2D@qBz=pƀ!1 I~QiZ$ٱLqѽɰhlhK*[xN'>-aRtPt㔟ٽ-PPCdx9m f6xwH^xEn+M  nߨ;O+HAB4PǦ@_d⚩0ɍZ+J#&!?k$7{Y/;Y/rq* e>Qn=L2Kٟqp'gƨ|z54@PKAIzk<:Գ J¥@za+l?q[RRunk¶w3:0(lzZ//;[NVp>oh ڽxe5̲1P8۶lNWc۰r+kD${Žy&c1&f~c Oc.?P,g,dYTtf{rf,\Rv\/H$i!KbԊwPag uZU|@g9s1Vb,FJ^G99>Ţ {ch!8Y4iӅL[I6 q>=4;62H'9)<#NTSӃ>rW^)"WadN?R;ҎE:5m.ˀ>Uÿ q &gdK=l ORx&64Hz [Ъ'AmфIgź(K e)LlAcU:Em#~u!K^M_'9x.1%gK"d~.`]I`Vɬ=@ئ}.`UU `g,v5(f\&88CS*vӻ^S xw *Iw M̑*fK N[4N^-d!kTeiWgܰ 9a)=Zt3ukRVm5'aaCb GJ5bxs31G`s'ss3.6Xw5܏-QS9h'M0UhTh hce4޶zZC:<':7]w-t$'bq»W"zX} YGr*=tiJE*ѻYcjl5]>#E"4FdՏr6省dib|ת[3OԒ;*5DikUu_EE+bCc[RӋ8+aUIÝ68.>Vk ]9[!v yfZ_jLZ,)LYא]M˾@@2;@o|q'f 6nQ~Z4"}gJizC{pTZgt];2Vze%4(EKf]\riif3Me5Y_# NeG,=Pb3F*cA)4H*5{g?T_[cϫW7?7?7v[xgj&YѧYvjn5TWrixUO:˩g%+J'J:vg7w'#i^iki@x6zty©/g}uS_e0SN=GP;Pif{n7`,S_+VߵoZ]+||%*ηn;OOSCsV9Bq^~K}ןO)DO@)mSx Rx}9oom;{hODŐK:Z vЙhdz:⡳^8 3^W'ZȮvYVD!aaX{;=v3m!^YL[X[Kb\) }%-UgRHS3R|qNttcq M N>p{ώ܈_2#spF7ԇy zCt׮(TY&=Q4^DgD8b0Q Fҷa@(DR_9t{Om 1 jO6GDcUgb jBI'}̼xC~߬{ Y1>MN'&ΡS4%V7_ e^z2RPxg*c%D <}@H%XGsOHm2"z d< G&O'O[EzDmα=-'!sć(1q vD\#߹0&2~ p]Kdrq16NU`M-&7HIBRiHs#H-xa G5xȱ=i,W 520Br9"P#.x"jP؁1jYVŊedsIA3p&,YſTM|3f~! I fu&p0ڿCi8Hi栝N-T[D {Sapչ^:wqxȶpuS138P,gNP'v۠c\@+J"gm\WeG$88TD͢lٔL-kA !ud).$SҋD RJ.xw(eHޫݫi{R{3{7o F?Wk*ֿ}f7o_쿾}f쿞Ma7ðoa þ}3 f0a7ðoa þ}3 f0a7ðoa þ}3 famuϽFMBUZ[~ 42GIr($9,b}\=M 1 s)лuQ-¶ ;;@a?8m(5Q a3 &C:oU鵋<-t<(cOâ QinAqܚJryɓW.&b8@!Ha0TS qX}h3,1ŭj fk&vDy E?W>^}%ת#[E&ad9ffz(|VIF37IY.w7hA$]76%ZF>R$)}]QJF-F^ELP[Z8=qPsi̔ jG25؁a<@;x[+3w'caDh.|$uPoe'/" ,܊ /F Мgf}B((w<~{?xPqMݭQZ(kSF!>Wst0}@ht h}tO0'&tzA*s|2z=OP]21('(COP&s 0B˿,`@ )x(ɡjビ`S,"vλ PqqYȜrǾ{ , #gV{og_4vr[YGL<%KQO j4@א!7 أ[LƩy ,GPVPwv))D[E+ q4bVDIu a6a(1b(Ћ."f)ʈL%l!K;i'B*\&r18KBAECk3!oWؠBD(K@xd'$TPNwPdD<kLǧij61U`8ue2NFM֎$` 2~sq#?7$VBo'X^fG/bNb47ɮ i ԙ1 զΠvDY¡i(z2ui\YyPWD PA[Ғ-຋wuR8t 43|ac0K&(x34F2E "ɒ)5JYT%#b1y $@:te0umGmw1Oz%(#0pǗM=Ĵ<#byx%TҒ8FXof%ri([K Ⱥv.>6Ow[{52#îxRh@< z뢸N&"[^$;\ _(.Ha4z]XCB?Bg T񀶀P ķH OC$yD[P ^7B ^s O ,0TE,+sx~pr';,'Rj)B@|Wbjwnc>y6fvBYkpJ3̈́쀎P=m7QTW,uE`]#[7ю54KFK cay.Gn)0S.L2+500^!ľ< $ P%+\~Miǰ/PG 8fa ,ӂXǧ -)Kōj:@X1U+o|v\D r'bffB0G6q,hC\v\J1HGsw1ܓIIV e!ƀ" C 4@֓ ]ĚзHq%Juiik)??NqVr N=r+#JD@PY89sUt3U/G%aQߏ")ߖoK þPǺl(!uIZIK4_*Цe)u_u)7G_18'nl@+6~ƦJ19YӸd-};2pC$\*9bvhęHbp>8f4 ?}| KgQJ*L/V*lSmf奌몏 {;ƈ g83*һ %'qQT͝ՈYtK_uI\|tKFH!`qZ{rsE0ó t厍7(oFINyZKTh^8>H?(diy[Dۻ,K_c5B]^v㏐by xh3\^-[>SIlˎhw° 7TqL'zѷo埭ڨq~Ig5F(/\,!fYM(N#̦3/?iRh額[6,k[+?8w?慆"vc)fPMbgw{)TX tgêtɅ)SÙ.oHUOXgZ La QUs%&x:OXU,Jf󙏥&a$HJ$Pnw?I`\Q՚^r)稚 Rx m֊v7|"~ 8 o8 J익QlDoZT&=2#{}gA϶Q0 5} &Qo[_}5Mcq9S CH A{n>Ln+/ָ|) McX..1tphɌo%m[A1uqq"&RjQ^<7od"5tb9Ik&vbONIšXH0bM IK ij1v2V{7~ У8Eߛ}B&S==;TIl \7uNf?[1X_HwQ! '3yd QӣtGzKd"La"vƉN}7DvcSjKNQqp %cNuȢz"geŜ=Bo SA/+"$(m1\nJxҨLT4Zك4B%gӇ¥E |Md-E{ -^`EҋQhsKO8}$䟡O8t*> n")n61 _({!4Y G8ˈZaK@UsuIj{Wr&W"hO隨 Ӫ|Dܿ cSy#A5j0$60ҕb9oW;ZfM']Ȩf4UTC+%ZW#Wy*+!7,?tѰr>+O=/.=Gxux\9w?:یwa0F|*HHԟ>]ZĿw5>O  "%LW@3Z#Ck0r/חeta[h]~n ?0_:`|qq(9G_rϏO @C}0 @b@)O / JލxRmޝEl4 0; %{Ij\!>u](A"u*N&oNs-ҊJ2A5n uc߰d WRP|MBq:"]s3~3*ީ t}͐O$=;N*gD*o48US׋\]'g[c-ʸ>zgWq1:WsP 'gr"cz'#SǮ+x m7"5tܑc]|хHW;d#=hm nVt|zvm:Zpe)Ruqz7|h8510rv,# 5}K m'x ?2~/N|G4N+4x?i?`ZqkUr_܃Uv"^Viz㏉E?ȩml\a`G6-S!Lgyoa.8,\(?r I8밺Jd+מ}>ENQ"ӓ`$ oP*UN>KBJ/ƋTIZd1Hy\[RAT)w3/ua89!{O1 IEsR-Qv[í*I ^:Ǐ9KD [\9CEXNy )N骔u#Uluǹ q5eߒM#Т *_]GVo^5@u/BKܨ Q ud<H'g; n7mUzɗSRC^_a|sKjq^"$^X((7GװN N4vsyw9nw2?HStb$;Hduzg =;|nGr:w6rels ,쑟CZX*&ۀCJ&pp5Ig=X51 ^8z9QAo#+Rdq&o0TƓ+^wB\YR$'/S n ïyPEdڸN{櫝m ԇ ?~tr#叶w-xZy.u..:մUNU7]7trH,;CQ~P(}jns.c'H>Xj{I(s=0< EQRݷ;B 7xǥ7T##xgEg^$x}]g*YyR9‹eg /Y,F 40J,Nf 7C[>OJra4i飿d`atz_u>^7@ȋ|rovn7o"@̹?ϏBgț ߢpD>{T*̓/&c!.Ŋv,&E;7H ~%Ans1.( )[ޙ}yRe/5e>NCƂIH|o,%]"*[D~}pan,{鍇es2c_.ى/"{FNnʨYJ`qqdP6 XXՁɽV Vߖ*%Yx(p6Oǿn'/'GG_wG6ڋ@W[Ej@I§$!JsT?H?%6$АakgQi$5*5DF܀Nû vdAb;AFƞ?N. =4}Gn. Y8V*5oy{}>jw~j$,) ď&r2.{(!n9gzR0T0W'k+"nkPË"%8!.ʐuoEΗ br"w -4Dȳԉd] VR1 ّ$Aik{VK?kA&C!(pTutR:-*Ud0FKzuTR51'cga^[[)cQN*k6WVR`w)U{VCgU]%*kR9ڔRӋ:n{{lږfˮikZ_kz*O6K͙$5w|#TiSU>>Ҹ,K{TWJ~vsHN|skW;uwoZ'{{z _090&N)l [^cH ռ:;ȿq.GýAaGd111$\{ʁ54]l`؉37V$q騶/aV8iwj\|\acqnqB\uv@aJs$gC0owo[{(.`b;'Xa l(XA3@ZGKW;7սC, u}ȋȭW @`4ኢ$@vZa{kizrbmr=r{gW}gaaoi^zEwm@#VogwfͲE5EZ9,;7~qE,x$hAP]-$_mNZ(~yx6/u\KckP+v$T^3T]IU8> cnC8j&CCv_rw%nb% V({5C/^FB5%TɨDֆ흼!c.ecq $Wר<ڞ_zlM iŔMs4 HqZgP Je/1d|B(_e2K[+ V$#wQdw1L^f xޯT?w } Ύ^/۽k y.Y'ֿ͂GS؄NՋ:Au92Dxa~u\T hnljBʚSmxԴs=.*"{W? CJ b<~: /FfpqgYweL|ߏ>Q`6Qj^c![Xnxf En-ʣYxyY@d@q]x%9yԁMhFDl|o=ͷZ5`UYbU/POOT?>h坮7( hUtcWz990j}ј90NZ* [fxqE͘اcYIXNXt^9՘b{n0֮6N4*B_ъ8QJ2Ti3j,ۅ}êX%S =vu3+ K5D0x^'I,PJ>z4 &|0jTʝ,9郠p@F(@ܛ{X- ]0k,꓇2$ܵF$5Nv[$;|ʱ1*~r^8?+[#zNa InAAgǩh+#@( u -|rEJ;8+g EG[S1 =(6rtgsGt2b"3`~H'鈂;, ? ߇(,t|(rB }edE}r'x>)@CK W*7Z !ۻx LwAAD0=dMMIzG%;7`h|ef("EG3J%vk?ot'7z {d$:V(xDG\ãVh#C{B꧷Ughy^9pOQd}8n>n?o?>zt`^wOi]eo6hjhXe^1O 13Y'.@ 2*Zc'OJ?GPmSTPGbt[^0B%Ѵlyu/ԁ/B/bA2hS7! >huJeeVpҟIWG-:g݋+#5͑$!"_Zo?}ʐ>( ]Y}X#;[J?V8M$UL26aVV9ʕC@YE`pC\%5ӈÃ߶ Se܌{h-!J|Gקj=AKP8ntTl(ͰMTI^ɂP,oR1b֢d !e8e_fN' $v ]U*;|)êJ/ZyҪT%T,c~ 0oW.uCWJ(^`֓DJ XKS`fL]{:,޿:̞2Ml3-"K"E%()iIݴQAwܳLdlL^NWR 9<8 VTG{%ʎzC@RĐU $z"ª.K E-cC(h]NǩXNUSPW<3!ƃ:R=i(PWt>&u:)lާ3]:ZVѐzN` 323_9f=1tSk]Jle:›ڌ-79|[>4"l.@$y}hm=<`o.]tx¾փ4c!S-|76چh 9{+[%gCHhҝm-f13g Js0)dS&SrǍ?Pԥܽ4)CB~ֆ䗷3?3$x)j2Kw]@i@ip#/awԽ?^IOtZF?Qu)čXηnCer2j.SG3ՖSjdش}h۬ʻ#'Ʃ%益;Mr ېo0v;x(:Hehy&a+wvs=>>=jf5/^;lXHq;;cϰ8V VT Ao}Uf\vUinάVJ rV_NfUܣo-[Wmi0:ZvUkt\meWVG[ŋZ]n/M?A=!m?yje)1k/nGd}0v谝cR`ʧro/? '8z)~ȟdetr|Z64CU]_Fk ;ް{l<<,P):;^qs]2o(Vn@Y0r.&HIlmgܞ_TٽQtϯRe^>sHyRJ;TEW\j]^Y}0;cO+46d՝ߧ'hC9a5jz #2+WD|w+߼ zNcF hKLn쟤ptVC ҶLmbe,D1X{KQt/Xj?xp} Ơߖ@inprWR(Enq@ȩaYԳu]tn2.xGy~͕tGߺ+=g{> &]l(4L-7gFaf,SxpW{IŽ,/ ֞| (xÂc8-~A[/^ߺnMvۺČ4IAd<gLLBoTAezw,) ^eJy=ggW1[".KONF !բ2)Aړ CE9J#kmo ^17ޟnhS s{xWżr}2o({(^`uA`gNM (|+$_Na tRYww@Td~ o`Xe$O(ǎ/NΒ`B' 6f5 4C!7=-i c6%%PMOĭE#Ƃ]{C`!C~Ϡ-)i \*{v9nefNF,Q4KOebC4)YǏ<ߥa&okr倂@> |5|%>wVM39}}hN{WO+dۧ'/; ˒d_L\I/]qdqfÜ j×7Ffk#F?s[>YZyV*"$×8"e\M,},mz`D5+ftt.DCV;g*=_*b X%Of8܎Ǭv-X%?l\R;Vh.6ƃ) * ? ,gW ) ;=wN#>zwt]alڬ@Qc<Җ3\Т唂=dFG}M8Pfd-,W1vApV'nC`-L&eRk -!# /IC:|y疸` x!($ h@],u|zA}䒉 *=KUFą5q*T ~p atSbB*B8U`];ͬRVjN]aF“U;ks%Z^L$<^Ս-" c ].H`<$S+9W\џsĐ`SI%[Zw¦dRv"F,V}2Y5uF(VʜxKlȇ|q06_o-[_>;+Д w&fާ9"x4wD)%>Q4Mh} )]/'\E#Tq5̜,yPd_Y%P|:D;J'-2)?㝓=^Av^: 3Hyr{uН+/g0H#W:9)H,Kf/ǸC:,5k ƒ epG !i,)|QG+-ud0ou z؁[%}.1'zY{1%4gƬ*Hrj̽0MCm)4Z'Ge›߅seu AYePhAvzAx-w|8HU!ɭTuCWLֺ1̢7r0MS錪]]}F 0 Ji`'@z0Mt8Ɠ|M0zL{Pu/5Û=C_I]MnmiS1lzvAy^ήvuta [Q0J,DD^,!T$Mcp[ Cxr{`j2VyA/{%ԴztwO^W 1 :wFr.7%*a!8P [,䆋:xTe.gé.ʨ!qKrZGnc&>RŶ( ڂZ_wSI}L6h)Ѡ7Vg*M/bIqkz?J;ɛd\ <IH{.o?^zqo9.׉Menun*ʤ"X$J7$!~Ioxy{-1S@bE8gܽ}K3&6d'콀{o3γv7OmYa|V0feb@; aL+1fC} jg/~YbJ5 VuoS]n+FI'r6#EiZa2TF!3 QZtƆShJs}E7Kvׯt$o޼Au]YBצV[^2"c Kw96q0.Lvw)#]Yb$1n|c?}Z͆5Ad.hg>l r<3pq^"]t;A7Bm'+TD:$ox參!HQ%aT8:D.>YujGGcF_!IK0\rޮװw9MG14{mwS|ٗ"dv-+\%=Eη23h s?, O>_kD}-Q60#;&$?>Z>"6\-xkEBi2];YNw|dG GCK9. -8!buU\q]DѨQ;ϫsWwݹ ΅׹ɩ$cq$NprpUop`ma1A>5:Ȁ/Q=\8e'Cx;S ,v]:Lf͐Oܤ I~FSTpw=͙u!9 xך9Fg\Nm8Z0({Ufl߯Lh W,suG\*N&T)'RfjDNK?,>eW#yv SX",Y,)Bj42&2U2_蒩Ol39K/bdzm1 E2]w>OEjE[KwU9ŋ`2F-:; 1eR^/ )#f.tp(Cu|`if>^J\OCulϲNϡn4Z{$0*GTdC\0ߦ1ɎGBg-&1J[v. 7u }pr;ή_(q` 6# }e`.*"P~;IlU)vS4-7qJۄ$Cv.PJȉ1ʏF*a7E#;>Cꄒ٩5 ? 'Y|Mc8WkGn]mXZAzࡣES} oh^%'=R5l$gDB8҅  qxq4#N|b\$!12R.ʽ~#(e=<٢%cLT0pGzPQ< &!)rq꒟!QdH zc<QXkhYQpyR+ CeX\`+u mz=]ll%ĸܳ{8Nr~7-+R i|y{px ; -C;V.9@σ+ ]xe|\̨jB_1]ړaXRA 5aG-W,QJbWf`yއ@s9uL +6XT*j`3 >/X œlc>_%F2xє׊fO>K#x^{EZJGnJ[]{>mNHz_*I G-bݻ-" 0;;x⢰@@Ͳkr^QU~$˧ o(+iqzV|<<#1sF,nH'qI?;^G7;UK_ C/2ēܹp >ey\0&e=J9sC|ЀU(BI.p\~^ǠHV)tET?'s&+rЁ!d#&Gx]X$/DḄ#IdP"Fk^z:@<Z͹Ɨ{*WOIϥaZ ^ jCj2]m:3"ϕɰuqS>bfIĶ((ԔʡQvϣ1ݠg Qguy3 ۏ;Wa/j^?r?΅'÷K+3Oe9!*ԏx`<uH/A3Ay[_Z6ɩj++};ǿhEv9qGm\9-l@-q.VB}ahItqo`_{&$>\{g2LТzR|uK$Qx b0 g@RȖ0G]XSyj-)A^Zc#p mՀ\YZBx+ x+_`TޮAh{0ޫGY?|⌯+SH|\j-%fnKMRWǥ@6+r>vGF_|t=Oe33*CO\:>YF&5q(N:ؾ9)P9He(z7p1-教E09GaAr>}@@L7  wy0IQK LvH5<\j w'5k.='|TqZYX*cr\QLaP;ϼsr:.y!(E9藀:P1~tor!"Ti/;ìBC+fݪ75]U+%%nʥtq̄5XJ]6v B7qG3O;SOW%]jӡ AcJgFv{@wU?ڍ!%s\ez]Y)JEf׿ }hʔ@FKt&W(._J_1Lfq%mb`fv4Gy1JfL'gV.)TڤR_ԣF\k ׋\?Rs*vQ;ط7AuٟG9K@ς<_*r2rVZ"~lOٞ&[0Nd3c(`* T&`\!|AyCy ]d nT|8F(OKg"!%?#Fb u1T2GMHaPI 9J:2 ƒb )Q1M z&S^⪐W>Qz.9bO,~yv#jGnJzgzj> HŒOLxBL*%}oתVUbgk)1j4$VWiNa<#9q-bQ\oD)oĄ*#/ͺb]Ȟ,xd+Y~vv @aNG]&)1KA*DQYRGyKͨ 1 =CsgB3SIחCt➹IQB μ,qF<=SX; eσ-BIv#/k52+҆&vI[l2 QHt1 gJf1 \rujj$)?f39.JR/Ff J E.F2O3ާ`l˥$"Xŗ.M09*O/=!c0k-OOtG΋7{ gIRvR{}:A5!N1'J~O9iq}yJ)>'K&ʴD[62a0eby#4PFE"9xr?k,vʓ]^h=-2D= ˕_*O1m4Q܊ a E*lTc/GU|s?`FU9tYNagsM,ePMŁ镬>ڹ_n?!k~pڧVw&FUn|8qrmJOFE޸(Ȼ(ϯ>$[\cMxb+h)RJ=X~r<}ehDbrpIˀ`胴0s]rREědƅLUSb^îQy 媃l U8VSWag\d%V=[+6NhSeL)cS]%Ɇ OOӬ>hG)ױ&kSՖk3 <}F\=Ψ1HT5A/DL+~&(96ቜnKiδE˵a x/ޮ.dV9ۧkY(TTSYcpBQUBCLo [ 8\xcM2`Y{&j1ZS!~> ?fAAKU •R1&6Q M&I0u66UoN`C&E v :EWaKv2X#>x"_g:#^UTN1*"Q]ҟ57 绝YZ2 ` BklF}RC;KVƮϥ,pd- Cqʹ5ũV6hYse2o[Ug d<` - IEvbitn4$#4~J^yEl~(Գ[[4&J+9"{<5AS2Y~t;d,.=]j4 MmgY.e<PzZet~9ila%Vиkv|,t;ȰAiDia;mb]u#^-0FNOE#}t\$(H$._()tdQ0NWټ\j]@θV'#w2lXAk,2j`ûʲM=PHc?"|x˱ 9cG){hm>fq|غAL@\3ugKa@IW/l%FsH\N ,ctM:exÞ&Q5r;ؽ؀F fʦuC9[*!DY12=Xɦ )9Dª(b% %hl`-fzJ1XV~f3dĪZ7 ̣˺|^2ҜgHFf0,9ɏ*"]Lxb\&+J)Q#lZ4il=se*G>ލK1[*W!9}Zɵq>Twvtڿb>5nR h^ AP&X#o~u!\BH–9y$\u!{s}֎p/*Ў=yd3$`&б.彺Os澣4Q\%@+|и $T#㈧*_ƁϕS{IG#O]]^r"/3 Y6 N;z#t1 3߆ bxat\(A#k;y R#!pP#~c^ - [6qAx)MH0Z/%jSFAk',H7`;x -X-!|iFHQp&ss.N*/] Eþ G! c .s(h|y+O˅0yE9+$x M8$p4l]V9"<Հ,~ݴcYp(]~& ʎT9L(wQ(ña8& >]xI0LxPbb9} (ʡ1>}T ;)2Y>EŬsCgTL0 qf`Ew$^#T̋AEExR3YB1y/ PMJ*E<Xn^K @e}W%e@M'٠#i,?Us>>/.-:sŊv'\@s"EگbgKW3Mbļvi/6á"rkS3 ^{Jɯ?Ũ|[2]B6$Jxdz(72Q(fzpqhe2رE퐘}d_*#1a2vaEA?zusЭ LBZbk'1if$z~Q`VQ.3Ar(znFqcCR.΁Hc[AymSژ/5)px# @3;TȒc6fCN3/М*aNJ mQ:Ǻ@nw;[隲wԙO6:)s;i>*{+Ъѱoְ*>B<1rLQ4jtJJZ[J{bê<ìߛgEQǁ9f1"# ,n".G.cQJB%~ÿ(y;+,7nזTts[Ƚ&rUs_`mkWWwkK+Q/!|_sgJ}_N0dyYVhhE[n8Dhhށ4/͠Uv*kuM8qPj3*X=N^br3WzUO PC5"DOTMEX7 X:a8f SPJW͸81#y^?шC:mj9hs}?&c9>ҹfQǠ4g*aϨ{%WC'?[گZ;?^) &xbًծwUN}g4nh灗ͽ "_rd~aԆn? $R ٙ+5AWm!GԀ S>w b=0Ƥ헻ۭcVI "Q=Fka<~ =:G޼ "WoaxXn}/pu^e0 Fhru}sd>>(4onmxj矿Qoo~_U[[p$+Y4A9}u N Į 1"mh|djG#6p`c(1;?Zg'OZZϝ XΏ7-X7UbPSs_MsDZQXGI}@a<3SKbOMר|4T ^sgwc.y|H1k7&o_:b#gE+G5[QQ,rhk LVC*LX$$9e t$Q9-ITI+CuHvzNj(1 RCky(Xt]!HPPqI lFf[9+EDa+F)VQzJѰz\h^ 2]L4S `,kB@ޞVK/t na8{M"bb^yml߫N9dscF%C  ^e23[E]=c`oUaDhqs&MAtf^#l87ѣKAj( Fh{ȟ֝]!Eڳ0#c̪`C⺊C|U\A9';!tUp8>pw9kțr,yKae#n>n~H;[;/v60vurk`y;>7&>6WP^vy h;ՐlljJ%|$f07ϞX8p:NtqAP"X'l ]~).B|l 7);Fu,R).T*(H6f?CiS젪)# Tf*ۆz0߬mnwu&dKd}?oĺH?~Kh&ѐT1xW:-VK甂;oT7 iqH &F$FjHA?`JO6 œğO,a;ÕW+u{-2\˙Dx`vM=hy6OrcA[/46 h .U>Ĺ9]snr&!b,i|SYII9{G3;g II0WTgx QáO4WgC^W[X׭eV1NKn,i9^SZi =Zjm $f i4Q$Dnq8ŜFy*?ݐud&sy)Idԥ6E1g#9˙7t_(?_kf~yÚ"U+idz7$9UbZ"\.έTߙ2yz!Od~V(18V߼bSQ&GfWa`kO$fޅ7!YvXGL~eGYccþlZ! ΢#)Q ͛g\a6g!moP `>˳߄oL2`KeȧeMŠ*$]fxv>T:n2*; ʢ6՗c[Wb.Ws6HbD7(WxyDX"n# R/2ow;l0BV/y:tKnBu H%=,`޲\Ӣr|3n$ #` tq"!nz%_SHZ[Л a+Uw!cBt'c 29 #?0`l 쐉J]8r:G+b89?cYTo=l'o|{#<! $ $,A?pCWaQ1튳s/Br\i`` @Fc4HbڥFvwSdV[60_)U(W%Gg6}4@T ˅B]t:<-.TPutZ?]24O"Xp~WMma>oơ<0dN9Q\]Q GQ`D6iX<,hдQ3cUPW%z,TEdH\HT?tʡ)Xa;(P)`hύ}EׁsD0!kE`~oIxϵRuи.LS elTL H% 1vcA ~ȷo^?%ʒjNxh͏ߏF$ .pv-6\NaI)—ehA,O%'' LQYA?ɽ7ᄽ)0K3sYF]$u V8t?'^`25Ld!^zĆ t钧+) b}.me9b7p1à<m4?S;qFwY@YN:~/bʘnQ#C@AgBq"ʁ{u}Օ7?<'Q>s o 1=]h.D3 8GtCg:&<[ O.*^;qzP~B-|)?T:51Ca ْxS,.tl Q/q?B V3yQ%`demcMCTh $^ڎ:yRJ:T,\G :yg'M^_z~#謙J D6]Ð5K-:uZ_rn#Yk /b^H t2ރF/&8}z>mA |2?[Cw(ufUAJh{π5i-";dZt;omKUdhC/ g pC.4{M?PXKʘYDP/4);VWωw`QT^x'9orDH CBm?΋V>^Wi퐅ʤsW<2èB'λd\ּ1Vwc34bXXLiXJ]X[E1fT?#vV.k0/j ik;)&~?"ui~Z @:s * }M@a9ݛ)U$j"i{tloG;/wme jf,4] f1KZsf ME/czz0){g uE#!&ZDWv3'K_fef q?gc}6Fe!I輪$SpdOߘ'Y홧%^hrs&3Iv>׷_L`N06'='\y,Ǧ6s-UJFQ 4Q |hq)O>>1|iA0 SN?cQߋ-1 Bg^) <8:(LqQLk#Y Wey~ ב/`})CAʀ%f/T?=D0AgHAU5V/iگj{V9G/Tq8] 12]u~Bf AD(@F-x #f[stn6 5q*mm UF 5!9TbHg03x %{';P. eyBY2Ap<]Ba.J.rJBgrtFhCfjөB|EYAgv"a wuɑ,DBd41wn/A|u (hTgkEK g>r(owt [Y,/GrvFyb]piyAs*ؘK%AΝ}XãG=<9<֯[f&NnBpN!@ss`k U˒v91ms׎oMrkk}&xؚ4 \sRs;fnnfQ2l~s= v =BG'ے0fT {[̞ mnr9;m6Qs&ksRV]oޤ+4 "r8/Lx8Zk玛~u Cp :Md.%t| HT;LWj7-!p)%?<>H&og c*}WG8|8>:nL|'{,cS;3oD% A7BC눨^L))DShY`_79sݤY٭76Mn7 8 ʭ1x|21|Lr2(6c xgs!<^,0h>pOq93I/fo /BSbm@)h!$h> >qQ 4D=>NY;O :csx/aRrd+.!ݳKQ򨟱 a2L,*aO9S,ŎT3%`f/T?xm@n&c$"K Kf@- +Eac6 |BxǐO*|*f*(? n,CPHpOg 67e~,ܕIR 6ǵQ 9Sd,*$Ww~*(F- }J0+g%1[g'fvϐ"#iHm-X?v:!^N#8kSs2 m ' }\.#8w+¯ZXߛW% vT4h[FnVfޅʿQh[Xn1x ,r6oχӇa G܎ t5nE5zbM.M=' EkDuQ@l x? Ι' j$Z~͊eRLŦZo&H0Gt5ntgzB}l|1G0LGzˌq7S8GsH1shC z3_ToTtW "eۯ֛I0tT=qZJ%L0AY7#Uޛ?q$?Wh#Dt>@] 5$>#if[GsHpdtWU'F*hȌn/P2/*bT/cXKeX"ccw* %dXTF5<-<˭@FrDM3&)nnXͷnLm~HVv 1G!Ԅrjgg 'Ι $pԣ~RoKVeUz6Iוe,q,?YddH tyP7rk_9ֳytםvxZ>kyc1m2(OI- q8(?DR?X@&I˖\i/6՝l4,dK]L'ld5TmEJd\GY?gU(&Rt~?o2P(j Okl*&WXϩꬲZyNv:ttւ+[&t~t~?眞zCk6Wy}e~Nt~9+c2R~R/qEUz6I=ٚBYM4M% Y,#[TXz~P\!scISd-vTveͣ~3LV;&BWӼs:ֳKD?Sҭ,La5ۨwWR5oRݟί49yOAebҏI=ȿM䜂?gԃj@F]՞*F2n}ͪ[oWBkKk٤7KaHѫ̛Le3eynʛ՜A|My~AYfq[ZR5t>~}qܷEs'ͼqTgp R f>gg-8+[A,$N'ŗdU2Y tDRI=ȿQ\r}բgͣ~u|9uu' [Ջ ͣ~oLi >e+z~O z,Y..Q?ع_ٚIz2.vMM8ۜW$mE3Ca=GdgS^kPμvZI3o/r(I_%?Avۓ3ց!{EY`䴷p1tߤCSGYm PJnܶ픖uH|U7:R=(si}(5M\eK[Ejpsmk.h1w-ԿmAC(HQ7~YrLm4d':|L aGc~1 y$z燐 &QHEPDI~FvJx}Zȯ&rgf4)4tGBLu%U2f&>7d r՘eٗLuPuo?_sߋ>E[!b (| )ԧ^$LR,/XHPHel1$/>@VUU,Ϭ$c>,|<\+@ \?2Q:% #9rv@~IdHQ'S4[ γ)BmrJtR1[ѻQiZU{V pLp³jaoS(c^o+Rg* c°Uqql#/Fp9ӊC_yrYlH֡l5C5Ҽ0")H}N5j+"rdg"AKOdlMpd e$RY/+%bN8fk1V,ߪ4mB]CiwTn[s▊y٠4\4 } FA\U'L8%Oik qq~Et> 3ӱx޸gSL7z1rUSu^j\D#+*<#lJbBgA $rZGJ{Mҙ YR@&Ga̙$؟#-BT;ąIr H |R H$9!P#SIZP(b(u#и8zk8b;EKVy%W2 Ɛ W.c%n\T +8yo_xiO{T:7 @0B Šƌ#Cwgdtзd&[wq0V5̑S]kPw_LW:7@舷R.IS'm4utI Mc02nMjI6PزY yˑxch) P#OzʀB}\!5/5 A(M:yQ5֩T+m$4 hp@m$pViU&|M$۔x}~gGo%LF76#襳E#"hC.I$ ybVWʖYbgy5׬IRxDScNR/S#Wg٬ g#+x2 ;*b# ՝FF1 5mN E +ek4:$L **7mhXn ={CAh=PKGW N"9S 7dgL@zLkC⊲Hԧ@Յh|;9 g};o3!1ЇDcn$4!,uɑd4)NǬeuTEs+̯PQnx!3ݡ u2Jf{5E)xl]Q{lR\'k$:fp$QCŸF|3ZS"/*PƲ7FPQQ`+rWZ'S'><iDQ5'A_F83`xVj+"P) i'4B% (=$-꿤FtzF M%ŌhW̪M&oNWRFqRB)ᡞ#E0r?WҜ.7*‘d?)=m |ZwfMVEXgÚ[ua-TuBKՕR!e4ZdŶ(7e͢j{CV^kf&[eJ*VY?ۂt-إI|W.׮>k^)-Щ ;i[on1)_AN4f)U ^ 8s5#LҿwuFK=?ҿǀh s]쇽fn'M>`b\HtM4™:F&kcb*ȟP!AJ88@ر?atf?t. PS(=k8Ll:ݔѺX]=45\OȬ/9K˩ f@U{ӴS/[- xgK}#O&{bRkzOMU O}ʩ`0렜~Q<ü?*~Nԧ f'%FК?wPlN-69[]%>$6JV9bO?*1a%5i浃NB.;$3?| ,ڐfhuMLڊIQsm"pߙ$ qKm4&ߋ$.lFepTMȼYl۶OFC/.X oj` Xa7}=FB17MEPKng_[witAwwC2r\0(ݐd{}D2yyFj䌘j|%) !5DjF<?AjGW'I*\vKQVˊUXHbW2W2*^nD3vs!M'qz,2HG( @_ NCi/J᛬w2=2kl3mTSa-Z6&ʫ](Kz-Iw%4RCJ)sP wNAV8ͰIEW2%̭N%| 8ů/RɷBZ;d:Ɠj\T͝픥n^*30-p#jBUs! i \''( 蝘 p'zzW񎇛 . E9GFAIKZb ɑACwp8HNvܣ蓣]񰹆fT%xY]{܀ Qh!ryK`H0ġ!kVѩ%-]k0\G C ]w&<0Av (qٸ^Z14?YWrGG1nYy5[KNUtc“-9Jf\% n8ʴ;LI<{^(/-ӚQ!OGll}Rڶg4 0KPV[k0!!;v,dZc۴F-3䍊z ] %X]/8b_;0\m̧9*tjNg7~[.RLۦ{OTC.ծ>d˻p}g2LeE~wH&Q:rrYr(y9MwD'y&4QD,0 v۹i #+? zdNdʗE# k4ö"!Y@,@gޞoAjXq|ޗ}kr΋zi9uW6SJ!ɍgʛA* ZbX$wZqtvմxz7] MRD6ᑫCHaߗx)˞3:y9z=6,i~*C˜Wa' Xᅚ&P$3@$`ҺUQ]9Au,Nec-cn@ԍLVBRm.o~s(bE"גC 奜`&?-+tLKL^E"0<%#/'_hU5Uf {k0r&Ҥm[D"\O]^0$ $0:6އ5ԕ I<7:yF7&VbWTxy T"&yqgsH5 xvjn[ MlGu$X9;MrYNWt4K/f+-MY (>):u~R[[لkPez#?an'Hu!'0o> NX#MuF#u-Z4Ȃm{ZJ/ԸVq--s4w83XUqNzo. aVf#«~;b^9 Sn#s VPA@۞gt|j78LjZ5OUJ+CnK,ܛȹ.([ eb{I@0&Lf]$HHꞞx(Uc t8LƏ3 10o&|NUmh/H >ZϨJN9ʰ? /6c8AAE9]Tƛێh+;T۬0=l,eE/ҩ8鲤*K%CB$h랅Ӷ ܷؼulϡݝ8㽧d?=}0qg"epGGsr?8dܛ^J2Ѐ٨I!/m<WۇЀSn|G&$c5٫v ;*p9zݮP!$[27b-H-5fݻ+aVFWh; 7D]tK!UO]Oc:F(L.8'$;'Iw5 `{sI09YC[i89hY:ݹK0ݞVReN&@ke50- x^LgS*).޽q4ilC W^9=2oA,Dbތ/*$M6V*{;?v_rqjĺd;oB wV%:\Q-f$~͡h:6ZudKA*Ɍd>EvvG0H{?!3oVX2+h:d0sꦉџ%ߔ{BQKv*~iz)V>#/BXmD6s@1e0s3d7KC[-KUtQ-6I̊xu=l<dKpJBG͸Xz+Ơp|~-\+I8aSh(ě8Ǫ68kI  &/K \^/q1mn|m sa|6cʐ,LƐ5%8"f=]h%sdaޢr.Z왃ϋ2J܂Fzm 9,߷(2ڢ<%|!8 :`Z.؋ ݲ,ɘ6#']2,j71w2ELͬDd:ovd3NcXɦ~34MMy1LmQ|DU0ufΧYN=ުiNX!+n-ҩ](8Ď)O8FQrXb1 x|X|(t'hNHo! m*m}@ kp+Yi45QS &p6cQ A(6PS;izڛd=0A $ '4<]~=7OvfJ 2ΒHx@32;Wj[~sg]^4 #ՊvG R[ [Z#!#?ֿ̒'Y6;Vo?hPJըHGOI-~s(w1 18Utmb`)e]5,g6 AbJG.ƩFnw{>*b[@栘kxQlkK0?7;<YrŢG3`$eJ jv1r6 -e(:24 )pKhdTuH'垜)!ʽCu]mkPޛ4P^G |ۧ!^Qcvu\mm]L(]|>UK9yv^F35]z1/Cxmq*Rj/IC;Fi#ϊ8ŊÆut gZؿ8(}ܩ|  i-\MgR܆C"FM !;Jj]N'{t3L5"DDZ*P mEP%Ach7!Ocr,/\,ga;G?Tc$hn5K)iWn!vy2r8S *hc0T[Pa\UVi}P}\_)4+FYֹFCb[Ih+6TVu2IScrj l3y2^^f;(= yqŇQ#+>hmR-?]k+R+ITVlDZKQ)kEZ).ށPyԲ0?6$#i$sVI$ѕVuna-"^/TdJ֥?Fnf^9eiWa*؉A/hnP vwLb޷J5 +z )\̮`alCŭSx4/@x+"Fc&RűaEXX"?xK(Um!cqE, MPuHQ|;:y8S DsgR b8 mamYUe~'z$,XE_Y)ɱDAJO3X OxZ۪ bu9>OFjK?1 N{uq&uXt/h︋}ؾl-ӿ( ud 4%A5'7GIim],UJZif#ý"=G ~ 'q` wYglӄWchH]8I-F2  %Z,˖@] 5"$ʚ{C<^骨2GsJb |ը$P'a 󕰼dwm@i{Wgr;|Fb/E5Gǚ{֤UjL '+J<8Hԥ<Ҁ-V9HV>U Q|ͺI,#tF4ĀRi{pgX|j(>y= ܠߧ2kJ?kS!HOEr.X+;w^I:"XqcN&,a{1j\`~4FvQ6Z~t㽯c¦.Kmw+Ow:6\ fSB8]7:++?3ZE ;΋}I]UyF|?{`q/5uDP:*cPxF;{Z@< .2fb?>Q/HKwuѸ#.Q=җJqнAWugt2_S%]Ҭ2+KX G`>8N9*17f)?b5NE7؝,,N~Iv, O<)r=IlO>8l:8ӕ6sMB,]f4ZyH_PJ 8HGB+l#&LC99fWJ$?PHYc*,6 VOT35@ڌŔߣFŷڜ,SCԱF\0|,ě!ks٫)IQeCri6x* SK ۱ٿkܐN"NƵB2a모~ݸQ !vm.Sa}IܝaGF]^Z0C­qӐxQJu2f1^ͭ&fJwwߕ*6Bsw"菉CTEX9ΉtFgUy#laܜs ͣ7I !LF<2) EjUZ:Qp{- U̻-;nr\\`qrҥR2j.(p8a%o!v̤jcf+g\I"a]+^CGVRҩ ]E-rpfaϽu/W?W%׏Xi֪0>cMDPNar'!Cr9+ R& q6pQm>`OC!E^e2FNBxM>C8gw:\Yb$XވSGƉvDL?IE O p\'? ͈;>S1aF:$w?t&9VJYbg'`qvEC4 >Ke$Mr@8 uXg|@)e D{^ tRp 8N$>`Z:`(EV #51J &=`\Z"4{BOf*'1cڨi_jaz㝾%2!،tȽ]"CEV9{Y_8nHo/Z)muuLVYv"\Z"d-Uo'mr<|ST|Y4\IMCḢi$rBoɧrk(V39~;i^2uMY}{I!E[!b#?wMզ}{aJ<6n 7acCC F{(Jm'3'mm]2L}BH =*m`\t~m}jV{fdZ)eR+qE4|T|cS>*mgȵ#afн)[BO* ^ ڍv~t/"1šV!UHY*/c>6e uC?#] ސFC4J˲CP:7I,ݞ(mX,U-bN^݋AX)D$K=O}%MGYvzN-5)9s5  sh6D/R@@i1 E@V7^{F^S_ZmQQu%dEZcgswrʓr-v"kkTU*9lAf)ʼnazMҔ:*V.| =v>}z#EVS+߈\]D0)Ћ9"ñ?x%hMT`J<:k*'2 Jɨ9:$;2z#y;=, wft9yG }N G'U@Htc {NmaM {;\&u2N <fvy lg[N3\:|F&5x]LN?Εo{7~;o0r9<0fgEw;K-/f iv(Ab`_/0k0)>Vq+s߯}1#ds()&b7`gB_tp/-`RsO~Z(o㽨*V$vI)N驡Vdeney.GxYCE4b/I 8;6:~_XJFg#Obo8(~*t ~W)1=ih7; {Ax>!LfXv?dݬ>kDّh,r,nunK6י/ɛD'TզjĚ`!`?AgOl{&NYV~ܯ?\Mfg {;Z_Q+~7Zl bCY'QJw!i^? a 2,ghs8p]-J1\ ץDј?:;~7zFkz~w+ 1enMΚ?(*n%Wc?ξFo;D?g4F{q8~vģ7ÌQf,k'i/Su{bUNzq4ÚXR OY*ᯠϛU_4wc-؜<~ŭ١qjrVS?dyuM.`*ֈ^w^#vZ]y_M `\ 8+kc5ZƐ3%xC4Znl8G?i6d'0lH :h7N[A*ڹǪ8/m^a Bmd!ޣ)w&KULlea2 {oE*ͼ B6Rd@7 &} 1O[[8j(AtD&U҆dA]~ﺈzwU'9JNY;̤5YUyM@Zh5 pVF!. XbS4a,\le!wy0=ƑכDb '4`d'7<yļzxrY_{uGUǎ+iK:ܙ[%a^~\Y8Wd>o؟evN^0AwZWVVk 8 U`r*IRVJЗ LPk߬x|^7c  W4=³a؉#Ni gƶ'_{tӫ2|×ݽ{/0Q[fB!5NrV ?ܟX0$ܢ$SMz .G叄tk,w;;?u>;(5uI1J~]g,V41%db+&rZPy=4%Ђ/Ø0v(O}l T`ʁ} mJTHq$Dun ѱi4_x⟛B}-AZP:88o6ٲt(:ceN./QK]xƵ>2 { р&]t™hr޻!3iVQ7iœQ9@ӕPB3z3`2ɯؒͪԍBc .Y_gEE-Pp$fB#U܃E Ω TKa-5pWI=.Lfv cqb[Xh˿te;o/ 'GniLTexզe5?f8 ?6:-wfhbI@sɚVlI\Flp$/T?pUuCv8r@qR=.tH̟,_bo??~lW;Z*nVi=>8kWJp[7z +ؓ( ʣwa";g Nc)cSm"JߦF0P8Jq# sj8~jT9阨 $F[f'}R<|e `0hvd_"I$pP X_6~zMu;$dIL._z79i<H ]EVFVQ4z+Aw1j:GQ"O0RJ Ȱm`}U`ٽ;rjيKi0K;Y֫S\:>![&A(z8D4x2} 2gDr3C4H7Xh ]BzZ567e#Cb̹눂3(ĚIo,Ҟ-X "Cto ө$r/XrQ[X X_jj`*^A> 7BcT=>Whs]IBV3<[..MNޔt B%_UJ#9,%RLIԨZԔi0n-2pDņx GK%-;R]'JPmlKX8CYl}]o"rCSC$?N7e$BJo}^0Z? I'/#uRBaLM& xgu%z^#c"+M Ϊ\UCMTh@{ xT,oXT\.1$BJ偂HWDTU QgI'O}(-rߋGq'8!o=N'PG$;_mA)pU* AMaM.RYЁrlz;JwݷHш>RN6ƹ\9qe)7RHk3>N噮}^&̖&dkf:s:g]Z[OJ x*&A՛QuRp`790-ޕRhRbT%He >E(J9AF/Լok(ijz$U(0&H5q%EKE6>}MG%z#nH_jmsmvܑVy/n 7\Xu?Z18CM^s4JZDSSKiT q-1nW>~dI=:{@ g1!_;*i#@%P4IEF  %(Jib ߀jzT [S*~C] );6M'xs[t˥%=+ccK"Qp'e% CۑX^SfHΙ9l4{PEg%!E3?fgNtwǐ=qC5H/7s`ʮL$n*ٔ &!gWleϼyq.==¶&W45~s/N1G}/9ؽ2NٹFdq c5g1$72 |$7|T:l'H/ΊshIET"'5r\q_y\9]s|E0i#v/(!kpc\ rgУ;gF&"5aFJGsrDNXX&f`ÞM~^*N?e|Y"n~a ^-5b&ƭ1,[qDH!}f !o%Fϗhgc*gij2S{aK% FiY5|{V VB2d:&!Yb} #kUFAW!bM9}L45QD,*v`=w"1ԍk~}fV:tYvq؛9qCSɬlO֐(,?\+3qVyƂ]i̷1Zb^+^^9#" -GAC1sQD44teЈYX%x:]6A?|uwTg_mB۽M$o:ߣ?0^:oNC+m}0wJgvcSܓt MO=+yN͙h ؝S[ަ(ˢqm{[. ȆC+dI>9yJ5BZH]yD {0"8E*uMdOt39w 5jOˌ' ͰH I]B|Xh3iM~7gTNEgo2J +ծ-1 %&G*cMSvW(IJ+ۓntŊu0 8/`NJZ0uZSIG~_d,v"kD-(,̑(R]@R{p =Q]>֪iĞ%UvYeof m^_GVۻKamMZPPۻ7[Tۻw;am/WZۻA\mBb[Hd]é+dlVJZlS 3²WX{HM`)e> "e"֡y#n:Z(s%'[:|k,XIW(p+;\)iXX|cЧ>MYDCQ$ _rt[;K0 aƱGuBrmվx%y\^ʟxK g" 3p&%z3k7&OH&ޯoͩf崻|k<%-d UXKy/***. ;C?ܙ#lz U"ʉof.=)=Fj Sa vPpy(0x 0 6C0kþj;hΓzLd8hWͬtf P!^޵1Pz.WVTnW^$Y]CNO9tdHd4 ֟M;DyךKa)4w9+A.vlW5=kt)*;7%ϊd6hQO7a);zɗQ?nWaD 6Vt0cq#Ls at۔Z1ᨛD]jjo3(vQ. .?AǤwS-r'_^e׶-}! _R(B+sm6EbDNѲ-4H/ irʡt% eZ/C'+5.&fU ,r_w>Jt$Q$QpuAM"͔(?)+7ALPJOBns~3${o8;JiII[O.l -~iU~_%| եL&LIh#: FQ{ěsE,C>E7,r; z(4|NwnIA_*W*3߽X|Hς nt)9%_Bğpw +4REg_-rPmR>D ƨTbz^?ꑳe J{q܅Dk픊_uzp"NZRI[˒cJ,az]y3˷oݜ)ôPcӾ f!`¨UwЕ!&H^ESq'j}{G!&*N彩! 2*:p SJ\x!WV,e^pw[:6NKU.K~_7K<|v^6{7AD6NDy~X,|_rZp}B$ܔfsׂT zZL`]` lcTJa}巏^5 xW0U4]=߭/lF4jNCp,{ "UԀ;˿(Y|mI,6\=ql4$qܸ;O/ךk8ǺEaNM=~(uJXl2j =zmm8Utplڃ .?ߨ/vxjҿ7`mu?-f.o\uY+RuH%3yZ(^5.IH^g~;]l<7ԯ_5-xHagCNпw%t$4]pAa5pc^05ZEsПUfYz Յ,amc]q9&Ivho=SfOD=sx F.{L3Q,nF>A"TlD(ZNU⹕G,/}%))H{ {5'+F0 E^}[w]$jUjap7*/V-{{U57q,1':mq/zϏӾ> s|O/_:ظŕy$giKթ3*yۈ-s¿J3z7LxAtGIHnW>AMW pLjx)"2Dfb9(@yP(*NQLȴUoj)nTrdt^6~Y%Sv1傐Uv hAdRBch-§=%=8{ p@f!9DYv%Oƹ1 #M ~/mhVTU;Y^L\[F\`eHf76F/}^vx4AK) ,ʠ s $=W9pw2*tisVVڃG=dl`1?<8G8=/ )Ç9?<͖s|VumS"'Kt.22>%{T̋wk֯71y`< Gڙsf*1Aue+1sܳra6 ?, srjb":8Q.e%fdrN]g!^kt^}naqC: Ec 4NтMQL/`UP9y+dBثHDӎ}dXXY8B^t1 0ie2Ce*qSF_ !ծDgF4Tp΅1s2'2v 2U>r~ekX17 TG6wFa^+Jz\_#8~ ޷]#.P-9ݒ#+?9_[\ ,[&.We9?!*_h Xi2f72d$Odα$MG}ӟ'AiYS[NC:,@Hq\g8;0Z1!rc5D;I ;h'Ngά ^2(/`9ך舁 DN}RH8GW'/Dt"\~1Yv.勚^zgݿĝ(fx e,fA^O2@j]|Nw^ C8l ctZ< cXJmn8ot2- q;/?4/zދnu/zދb=zN$NY2C\Arts"\sUII@0}s>p1Gx>.'B1T8p~ǐo$YtwaѮx{'a5ȷY/"\قnaaG'C?4 PȄ!jpȷ{桘%/"]">?2w>UE鮕Uq!BZsC(~"ԫH  z۟0Xbp Pv6j سJ&/{Vz5Rg`~USLnenWPh THuf:-[)(7$}WK6$ ZH3Y- ^iYf~}jT_V_Rbl%RWsk["ߋG _q|WHjuuVZBoh ,kQ]!42<6j -\U0Bc2P\ڊZiޖ[jS QRS'}9o0.ʪWdAwΦkNr]ɏ{ۻ{ĺ*jZg~ 7+ l~4ɒYNہj qQ lZiL;U. PX< @s(G*QBT^ !;٭Cם,o垇ѯ*>`4ބdup` *4(tR5R!Ӎ?k 5Bs(! @`w~U c|B0}ebaKd^UޒZ$}%r.[OR{9A/-K.4خEfj۵P1`$W3G!/CXA-~ ]L/٦FZ|ZtIw_-A9hɗҜᖓ!Ѽ(RLh&77a9g8y%8,{k0I}_)rM_>;Ert_ܬGkv36%|Q~~b3e"#~u%b%OwC$8 ),SV< ~ajs@F8V7)'Q穮MMGFNdhJSALzPAo W4oTo冣=M"d:2n Wu>jsHN'0t;NhĬM\^R#QRy@.Q+ )NA4 bvx.}t#{2*- hmU,O޹8MrsBDuLfU],sNTO^C%}p۰O}yISOfꖼ*խ_EY 4~yse!c*9ko˨򥝬-.SZ8VwO:o}}zZiYv=fj}1V|%.#3mR'!ЪI0ISp ѱH߁!7!e]@Fl6ғ;hr֜yC OxRPoV ->ȮӁb^٨6!=G!+^[r@{d"!?εr뫟p}€6B-1g/~rxBs,$5!>_ޔr0 l6 ?˽ľfguӵI..l{<,ߋĢ8vb\0$oHOHR+#2L@&@=-ΰI3͗h-ĞSyW`ǜAjujsDIoS)yb8Xfx >nI~L)ktt hs?9Y-qu۝}am!XczMo) v9 2=7z!EpùI;THjaƦF0jCD\ lBdgD;%P߮&7b56r{/ǯ~ڳe-_SyF̄~/əhV{^dBtLTit6/*&]] , 1.EhBd{>) 焬=1<BOnFg٪xҀ`/NvбNXTeenG_}'_ ٣xc&ON`|5hׅ_(=hK ܱ;rjيsNa_ [%aOAlt.OU[1Y+ aC{A>!4(${<>~=:&+^$}V#1^V (Z/?ң֪L1`N' 7ͽ8ǹAsH3wQp'ty,!16_!FH2jgv߽]jVg5E%gˉ פ,KҴctғFA#/rg9]jIc;䋉7CorL!܋Q(]+TLV)"ϣ)`fQ-0`Qo+IC%dUa^ь8w% yߐSa>tAښ?:az:}U)qLS|<ݺm8Lou!xeBճvw,S8Vj\VQLM9q>hK#8éqpI-`LDST n%”UXsOer4(Y*f!0Bt3Mν0G11g%:pPmXg4H=@e0nDLvm"s?4wEǽ×x#1AdLDgi?<ӵA3?M-M?(~CJL t0,:ih(1JR3AON~Y$zbhBL%790%l#?3%A]8&+AnpOq}Ѹ$ijW9Aq`/Լok(ijz$Lզ(0&:H5qEV/Rm}Me;C /,32}\]2wU˪d WG:V]ǏfYTrnZ>V4:dtKi%f6^/ qai: ZXY0'y!;5^K0PƩs> 2_U'lQ0p1'O5:l 'ޖ*@ʯfW|hqYOYܹ|V1sV Cktл7;K;D L>kf9M 1lAXfxc\'u>B}g\QpfRUaL`.B6$]h{^8N{^8Np Rle?KSu9CE6w7P. Jߊo tUa>3|h 8wk@8vϾ{D) ߞIxwW3Nq^."o{[PqWJi1W9B$O3"?|v<*gb^f'44gi3)ޝ@n{iߖ͕ y7sa!%shqqR{zѸ{p.qݻݻKn`\8gQZzg CF1] C:'&"J|HG"mxf\K4v,-p9樁9: Ziu A58QP V(~ib.*{ X}OR>k`X}nu [c&6J|힯-D3s26lׅ塒Tf. {F*hFd AjȻ Fӑ2 DCm6yBsa_Ano;54[mɃt,Ӎ$(Gq_gjM^b={.t1.t$^?%ӡ?"\i&VЍWC/pT37zI,)DSc<`8Un驭]I芇 Ull'xdMi&O4Q^q>iFzWgAž=w{v3q8{ i1aQPGHu0FJxLL(pxZK4}i@-Gcii;$^.3wy0ebPPIs2Tc/ |V`E`LLr)CeFqw5Ռ0N|Ox./g "hS+Q/C/A%r)]nCDxqGڬy 3/WGcs{ ( 0atSi瘒Vǰ>שo'q',hMO3͆Wmna ݦ,{'Gu L xsl0"n0/E+& ؋v? ! _ mxrRy Jnd?zbI .Ta&B/% \DG*oÖRPOXpB(:-ƙWŃk .3.%8xcC6o&} 8Ԃfm*=~U(w'M#{ciGc 8t8jRP+4׻ۈZu ^[YGU0!5w,/=.>F ``GKt(A8J&ẍ́ z^䙹?~C ),wQ˝%@BI*hbzt‰V|_C*T& INw=hn:s.m<\[2]ŐZGzܘ^lHJxO̿YkAzf'Sn$W*/6X.9dm8 7W߈VuA`fJ#(:%>b:1"H0jWj -VrvPpJv*9 **hPծ|ʇOuMAPa|O$Te D0S#{AD%ԡeXT \-K8)*NKֿA鸵!?1R5 M.D8s ' [Μ_Z\-ga0`œ"A ,cN o1Jȇܲ9m7@39qEu8+1`(z)ڒ$=wӼVqbVq"R[X+ V9uNIg:Zaz({;峡wo={ut^gGʹd(9ta`Y;DjgMw^?oՃzB' ) |@t4bKӱ:McCRш&Qe6IcUYiBOe)ָZNUT 5Y.fʔ*nB*D;oD;N&5(2OEU?1rgA$ͻp-b#=+N0IXcfbRaC3{pMDa|`#qQSJ{C,w YH; @ځ%1jU7&v=~h pnjKqsp.Զrg5'}|&f  є +5u?Y簾`h$ ,/#Љ^~Zup*<7*4]o|Gc%'*{-#*s8cv֣7Nc_I2H1pė۰oH/ aoSO2[6vիnt8ZmS +Dؒ6XX> o-Ck>:Iك!6Bin;a->t>'DJvϭI~^T\X9x<)&BV{ ԭ3`kbֈzs@G'IO<8 =G"0q!z4*Pޭ2*USuRUfE5;b9bQAKuI3?'2ބ 4&YD2IeQ̻5ۖ((Ќj8,h#N X&ݪ7J&j>PZ%7S ?04[*Dɴ@ &) ,[MqRĈn#ewoފvޮ([-V6YlD N@qkNIkJJ3g!#ZEqNhZ.4mD}K&Zh.iO~#͝}9k*GF )0b>`ʴඇoÚm=GPA%␺*v8|cB '7xЂ% [2[ZDIӑ 3J7׀RM jtMҧ!^҅3Gu3}]kmȺus57YtF +yr5uѻ\bZ+rDs9BqNz[;ap۷u%9}ǵS+զOĎGƧë #x&? BeЏfhBOFu[ئ8ǤޗUp>ҵM):&qѡv}Ƥ!-7둱0+a+:#?<89~w$}0B2WT`,eTcAG;a8:آ.g_=τ[D+7/L)4PU?= &U7Gx›x5 tSW)r&Nփ#bgCA/_B*|VիKvN*mւ?392|ﳍ`>Qc"ahQa9yd S +cdJ7d{ǑG9SrU )vUfJK-[x)z@6BOW\P   Ƈ%0U**؉(coPDnEm,KnDlQ k50)Nd q*#VHN^fT'hÂxW6xy8D {VGUkAsz (7$h ~׌%nT\avSb]lnOF 2Y2+6LjG쮝&B!'ЈL38/UBO Q@q11ZDV)8B_l;JQ>TMi$-Vf $c˕H򢑈X9s#9$/._A>@6;R{NB7N#q|5Ʉ.`p˟:[ቄ $Q@ OȺ'#덞%"_W{oxq-x!B Rf+lW둟lVvh.h$RL4#,襜7T-`c@Hwsy{yWW 597oa86iPvXFM6UIF'k%@-*Hަ#]LXAJC?ԠN *BlBơJ.[%ׇ>X-K{X e:wV;4|ך] kղT6E~49ruWpZDWˢiԁݨ**vr'pӼfȞKg^A59/r|)1r3R;쐸b*~J2,=5 l#_' q(ZTGDڃLI0$7 #X:W*DCXO{>IN_jTHʐ&@~tTH>/|,W%*0UP`I$rE*@^&~Od|F'?˃A &Rb!$85dATKLˌ|%qrYGj l908޼_xnù9^fpg=,U/3oڍ6{ov٦&K'Tn)/.p=R N1+6S)2tڕ,[T{I@naDjه/!c':[eHV!FW"ORkZDLZ&-'WOC BI)]h JEOۭ *cPǔ n@-3ٵ:W>T$X F┄3 DCޘAipjoɄVFx.t޶0׶:ayv C?IǗea{ujI2hHHQHCX;|? \rs#xJ!mDhtč.P .F| ̎K@\N#C|r@=81@ ۼ{ t߀4EW+e1pр_+haVp}L{1tRe2n4wnMM*!X3rR".'Q(@[ˈ51$IO! 3פعi0 =.P t5),B-J˄Ʊ\%ȗ2Vk '>C^]@jD&QS.A8e]2+jBBJ1YX*,-T}j/$nLI ބBxGH=Y JU7j^BU^nXd;JbOJiy8۩+!%T ԞtW T01Y3ݽ/_لd{8_4>CqDQi<g M"ffios~+!f V d5z:Ll\*F;͗#1$_|Ç5QF3ny…=b$[QZp1V̙~wAs-5byUm@rL0/(~< s:U5mv厼闥\=>||nM=+>[!f21zgprN/yP^ߢŇC]D2ksƦfnB1uܬ9SBEsnJVd.k#+\>8Ʊb,\5ouD "f2r;-enl;;>n>ζUmAo&}la}, ,WIe2z5sq^ MHgEh&FgMsګM0AwZ]"ytp7̝:[Q\9sٞvYnf9ckR@tScH' s}C<:70JNs1 чwbYP}%ٺv T 'Z9s9OuD|'ky3NT|w8*MbE΍:l0۽7yt}VLMy;_d(oMM:B'?;_md 1\Q|^zsnn"o$:X}'#AaBbUuD&q~k;i~]܎Y&f>yq 3+&YH-TCQ8#5,lyDtSx?S<39n}ЇzAJ ^pW\쐋<[Frܣ֌yVtNahv\<ڛ=E$Bvu6:MӸisgɐ?ﲫ,-3?Nєڊ }Uڣ;'hJ ykj;7;5#c kFMrΜ!\`m?&* ; |<{HSWubcZaFItq9sWz#b_.~8:)9ͦw?Fa/#|7`/s>}_pwRos&+(+1A=p&D))̈x:a5ۼ)FMwMZO"ʹVr\pFalt8ՐqƩq6TF쓒/ >^Jz D^OGRMrQJ*?<$瀮1.tK`Tui)#UsF7JNht`^%2R|XivIHe92C^7E%&Z9a $(`Nف.wkG \q?>=@П ʖ4DevhvAKEr$-o$)E62UN"}y>a |W?R\ /!vcy‰LP$S9AC&{ 6$ AVj4Hk1 4y1v|g,a桽ɉ0pT᎐SReJ]PX!uV0zcVj{(xbWW;&*xhYk|g\_kcNT!Kuݐ_4ךd(@Տ4f&Dj67rڳMJ1ƀzA8+>;t"iP}ICkj|  *k>Wp_\O) av,ͅ(4%֍Otxb]sUd$8S y@TeVD%k`/RP tf*e a@!ڬ2Fܰ1|_C2Cϐ;¦N/rfdĿS &#Qa"]#F\MRnY惶x|NT(5?y]ٯWG/De+܄«[U|ߕ^?ET =xQSwN8pv8|䍦Pl7So>wGɿ<(Hϒr\P{#@KVN#g:bz!-$5!3aZ9cҤ75StW D2vB=<{>ե"oul3h4&p ī:؋B@ָ,+xKG|郿bNC?Qbd4 cv#bjd{_ @{؀$tƀ*Q È3]={Y#: R2eݟ{F8݃jpi/X`Gx0@. `@C1\guP'׈TṶS~}dg٫E: ]/wjκ>V:T_TEpWj-5e3%?qֺ*ޙ@=h}ٱ跼H|RnĨ:bU&^YRav.G5Ɔ I^XτRrj;='d#2սq:aAwaX\6.e7[aNAҒ3օ4.Cj~?MGz;w?޻M)(rPr5Rw(h 'N41MpUàԥ3-T4GtĘD}1xgJ:SW9C}0zD#mYax /%*R3BtzYBʬxQ擻vz3ệ'WpǢ%6DgUl.eܠyז࿌'EH~jѨA կEX@k zR!iZ\*r"P1R!unO T||:QT)VH//~~8. YZy49YbX<e'68p)%-N..p~b.8JHwtO4F~S'ERbpn_9-1ol?7M!RJ(5S[x%"dvN4ݷ@RhuXOf STWi>XT/ p zğ$`z `g'@c;d ׊"Dؐd!z;"#sNbg{p%dņanOZ?A^G𻖿QY_Y*O 77[o5Y/.M7S-qܺ[s4'VX'G<;=oVsRewSו*8) E=G"s#qkl?^Lro$ɖd%IF9bQZ-h3G{s\;ˡFe˸Ad,gy[5e!h>IdG+aUC$ۿXL3X?LrG#{|^AnF{N|'.{N D lB?-N9;M2sbh8h2KH[  ƅ?Sl_H_* 0ҸTr-^HC@cYZJYN-Wۏe@Hv]}Wy@6>â#s[#À N]5ԕG L3c2V O;{adr%ݲߡ[ yi酣:1bҚ Un$܍*GけXwDFh/FK"U<7~v\ ^^7 owt~WV: nqonr܁WiLz}fp&~O}ݘ^A2N)r=#L$3Kh2 =Nskt {_AF2 R^GZn0{ 曍o?GWa; )eaQeVhdХT2A/:M{Ird^d +/XHH3ftFzk !m0Qޔ]B1{('_*zFlǣ&CM;e gͅ}Z"gI($Y[)0MbT+)^`j@(c^dj$Q)kׯi[2v*_NX=:,t?0riGus4ħ&h+W3}M bdzLg1*ZkjEch. /}Pas[Aa$0͐Fi{;Uk>@Yn|M&2tI4O5p24Jt"O*=^Btȕ,`I,ga, kR,X} tX̮C Tx(W.p='Hor=4f>;|AEY2Ɛ(j+E.-n24UsEs%M$SKf=r ʦSXDYޕ>ޡ,^Uv$pYzQmY>j>CIdІʜO)(uI@&дe<*0YνG-+ XD~fJ?Y)} 'fݑ%x|bP_4D)qtVRk.z)D;j㽨"ܷZ6N1P|" r@"*i{A˃"AW H5Q?F}*Kxj%drOtޏyd7Q!>xj#uZuDxu gf\x&OGѪf@( DUw3v+cۮNE FnɰtybS/5V޲6ʦ6nyZv+p^KZLdX:&?1y;'X,_PPCH  (7NLUSf'cn2eP I;tCU.qkiV/e-ޱt/f܋bƽq/f|^1c.$ R-r0ٓqX֚9KCF f+L,.L k<%=QZN[7HU[@1˘=R%e EGvJ&ПxG~1"F%tȾWV٫#}o)]VfMjT3o3(%X 4I,4>F<[&,xX=x@<|VϣB{Dfcu`AO~Vߋk 6 I*rLg63z#S2g6 m#N|>:qb겦'QԵ nd b0 `zfƈ0}rdaŽ'i_]vՊ­-chؠf3AvշRTaUC[djINbHh,hitn,WSDß7+ ~3n\Ee7]'h6.;I& j90&soGT=_hUinv8Y hmWʇ;G{/|rzwIò̮<'t[K.bۡ[LͨUHL~ʦ;Y`Z R{Fg3NAd\Y3 4PP`)gwH0{ϗ2ʢL4EʿiQ'9x &ugh0@튍 ahJU דd V"C5LOE3O r((9af$0ھ.Fcddoسb )CVSh/b0Kr"|K5΂{ oo&-2䯊nQI̟@b9 pݮێFS{A5M3򚞠ށA!O;9znS\m,a"GEnEj3UC/O]QPB{E1‹br LXrXŨNu> hJ6$'V (5;1N%B ׃9Ń.[ 0&m!4LgJukۿhdUY$yA{oZe̩~MC1Ksm"W`in9:-̋GQ,?h r0׉![p!jsjF,Mz>zVnuKs7oU*PvlIGo ~Kml4kO^ta$טAh))Һ)|w B RFB[ց;v$夋-g6).@ 4qu *LK$A8%MCqLA4\LUdc2N1 ߰GgiP.Uzw;C^y R%:"/7[Ԓum*!r;>p-P>e{ꇉA@ xj,bdHB ®ly׌9Ա";!B- l"P1O6s7go_g뛚CH90L4rawW{s y=dd( OoG1d.qbA_ʗ0)G&Q?'fqs;9hvUgRt/k/"C~#\/$BWVk: @vnO/x[4l>F(֨ >ᙍ>~oƅ Gs-=@XytcngqC? *pIOap 4*[;7N-r_' ƲxJ?nj(zNgY>w E/ U Ύs K/ZT֤T`)\ 7vW+O1 ˁ:gPZ1J-3>Xa%|*` *%Wα# ^Uq限x^Pœ d6D+ЈSEO/ Ws ˾m^pOY66++.-A3GNmG];8o?KfuvUÞT¹rk7&nȗD^ SOmt~|.:VSU:('ʙ`7_=[P6? w0Q59YHunSX[CyuQJe$z2h)szğ1 dʳ1byBd*S@)B!6cb2c\,8*"%HzMot|"R%2'pVz4|FaIw4KzaJ†LAC-!<7nREp\q"%P9!#ZX5Վej)vުOJп\)*\nU-kjYF eE VYG;#; RcU4N`_(-GjݣeBJ^ AA[R@a- daeĀNlY(_DJ/c‰ʡzҬ֕4eC:ia4&8΁0"͵fi} PܴoФU]nًvMlP\݆7_}R<q ӵw7ɐZ~  xZq*-$Lx3寝Üo KtfZa*W+HbߑzHL/a'M dQs*lԈH{t)ŊL y!0b)ŰS csOBQ8+w+mh 7-/[WcDƹ`?w,yunE-ږ6^'A8PIp/0|1?'/ӢM\uT#VU~^ߌO1T56=hĆI )b l ySM\rVΊLRHՏ.RAD9]ݕ:+FL^ Q{2  ͯ|DğM+({F3UE . ܩ|7N+NU~܃7~j]4zP! _[l[IoՐDew?l.L'Tbak׌;T#tzɢUJ[蝏"8g~}NPݕRU!pXt TjH8W6F>}*4*\AN:sC/( UHQ<~֪I% }5v- ٪tz7ͷj*4C#䋚_aqP/%|+w٢goӢڐ) iRiV5b)c<_Ӣ=n~| sNzp+5L{:S)zLAꢊ[HJ[F&k@ Xbi"Ijr510%-Z>Eh!cmzRئM2u='xA8mZlqF 7ՀL\`nssxg)Rs^V8se;)O_c@S7K;ʏ/zEk=<~+I[8? o?~Gòs\|(& șC͘sf@=s1P* _) ~Tf,=L[E&X˲KFΛ|~&BS@M@Fb?C,[Ȁ%kbQIVE<.4i1At=z?{O7uYf~Sq}"4/XyW6By4.sF>$" dCXI˄\r&%A'MGKLksKo:X=!0=# iGs2 }RA;'X7l|\-ÿq1i< );]1|7΂^F4HjL=t9y-ޙB^*~B?DR"?<!!?46f78DA*rj pHfDT:0i'E& A),9,%P\]qVI#~@Vο=8ntj%.jHx&9^rNe"tSN;6]p9R<СȌ L҂^;D s*4&TEҌTD&5ӥOC},a.M9hrւJҖUxAR9(/N{Se! g˚!@?:s.o3Ǽr(":⹷Kn,eo&*28BC#;KÏ3(Gb+By&ގD锲ޠW:=+;̅!`ys~3hJD)jd2ސՅ:/\:<>L֚+J-u} $e.,y( &z\޲y=Y+Ίq*Wgw \Ywsz. idů$P JSBc奨)wf>wM55r+ıtIPoJbrKG\" s..9 s.G%^*op&&'arb,([u'pgf9<(_s(9$>| +oX1!Mk?cliJ(py`$H I!¤gF;4}|(c VpH"r/9:11,6 7 d,$Ďg9KHL *Z4axL6DtpJ %w%.Q,YmGi~>dͬ0KBxmI d9qa;qǂCdrզ9 2ZJ?vʸW;'ZSݑɥ|"J1Q2u'*VL$S͚Pg=${QU~]C 'qL/xkr@PZ$\ t&JшE'SJ`X`aCe)eN:RrXq|Fv ;Gq4d&=(BE>V5 w^5Yo:EYe%ÔsɅ:9)#t>cHl㗷Jk %u7pEc.Warv}z"$^m0Mi:0Aa0}y!Lcӧ8^OQ)p!FK]z1ѽAS{Z?U]= c}<E. G 6m0S< ȮDp㶩+#lU'/'JZME!w1b2Uc++iIb  Aڄ\*zg%DgC6~> ObJ&(-Sd,]{bǨhb''X J@a|NHׇ{OMGi8VkQ 2rG[00bjٯW *Sfz狑gEht۽px/~6k?pń?³LMqM.rSMvMfw>E;vȿ q,wQN(v۳I3gGb˧`ϪG6W;O 5]X!s?uwX.vUbą] XY @n CAIA&EF܅|DS/r=L.Wus 9>?G8PVY1"\A< bߴſè;B68jl xAH9?/o@Bt(.F)}'I=MnZ~'%:>>;8|}PZۥ%' {ʛIr']$TeUs~8Wx8%i`G(Q_B1d DVG A㡗`<ڦ?]i8 ~ɶJ/N3׽gH 3DͦNiӲ**NR+ηfeЏl)3 IZ)$a> h_ax σÍO%4-*2`JVt{HFBQγW%Wt+i0zX4CP3%Ȋ`"]M%߰)EWF@뷅ӟG{wOh }U ;[p@6F EqXK^}@ ^3r-}4[*5|KP'ЮaH<.S[w(c23f#TTnZ7PgaQm$|ǹbrfQqy)S72A!{qN2o"kP|&4奵{i^Z{iMKf)w#jw.I tVj @QIخQI֩ ͬ‘Fcrێb]1j 'zʧ~lϯ DFU>_G^d1 (gʻoN4BqKGe=7w^t[جI09Qþ?ۑa{1dBD*JSa\b/mxM \¦#*:rIL`m]! FNΛ$7繁sʝ7ߗs)F (S{; =wVOiZgg$ܳnE;yoH >04pO(US$8oW_ESE9Ua1(^7sNTf"` xS.g~`s8,#m9;9T/M͒`}ق>偐[sI' =}8x2V[-j Ѧ),@t`yc_LJ;ޫRav(Z8s'Gja i0#ltVGdxh[?f at? ]LinpN}mxS)@G`LIrPLfMq}NwvkǶ_wy٫Br̓#8Ajx./1zя{v;*@ (r/NX1qlI@֨0VFYôv&L&1O,-0#6b⭭z2%S` W Sohccu{;&>A F̌1#]OkΡ'pGJjq+$yuW8I,NiVt%؃{?<ĄWgR g9Q} d1F -%MIMz295Sx%9uJ :w^dژ8gwɯnv{9b,}¶$h#[[]&ۏFyV%Jz%QɊ<9};>͆3ǜF,nDž&kEsc޼8~,܏3¤_XJ>=ӮDS{;'|ݩNUy&w )f 8D$UW+r_!c|[qB"کFc#t l?;LOO>*/GW:_J+ezV(g(eaC)?gUSfMX/u!Q%ZaSGhVcP4IxSIu}}wQ.r/BHT8ɤ6W[kD4IMY1qt#@x& }+땮Dgğ=Qzx5xoS{l#OQ Q6a0,M7zGqdD w=пҿ+&pp9 FO#ؽ<>y=z+A<I,jo Bg:hg8+?W? M/h?P2ԥ/ՊeW 5ӡmp.h6;\!J"p?:OP}y%` CXosS$:fKe6I:yFC):g3妁}u눠6| =p Ǭ@:/)~~Ǘn# %5 0f7xarUGޫ!񋽣#FW/Uut`N06S*u|_a jvn)-<ٳ..&A"Qj4qڧ&z]"U(w&W>mW?) =hUU+t@W[U7=e-YUg\*r~ yWڶ*Ϩ(4o4 Rsa9Lp'Y_>ۇmV㎱s41R6) uJr48 tʥj?؏;701 iwN}};HC,T5"3l.$Nh@Cяjen{ {<Fe?34r'՝іy.#!ًB|)Ixg:dz;g08x+TCix\N*Mh;p_<܏ppBG{C9E"&8^X |U9+.o1A;+M}F0&&w& +&cʥb-%IW /ViIo@Ѳs K-t/Zu~հ˰3t`w#hIَ֒uf' %]LEK,XU]󼒻E-XЂ,2+jp DerZq=w%{qv,McsS"3^3xcY {8AvN6KEX=9JhaLX:@,L\K@t"ZuMzŝ;O{~|P mccLBCްό̗qmݼ{T bjw eQW LLN``G}"pz Op=xqp  %/E{?X`3 %[?10R?yKZ4% O8]L\6u9~Ѹ_c/{ku9h>~˗kFu ]bbh[^n<4m{:Rat! Z;W.k% !"fK=qZ :n2!'"K ^Rlt`YD#tH.;|=)q.o9]-^5˒=(I"hnwtQKV5hYLjzSy^K[,9K4WZ@@N X/_%ǘpv2M&dW'vl~%MiדqլXW'R)C];[P<׉WX衃(Z< / A]BD><投m]8`Kf88yƓHwq4$E]&[`TLOOFc m>QDfmKvhe*ڥ<Û!aTov]+ Υ > ӗ/1ppJ@٠<]ӝ:04(OТ}1T|@`e!gne)z DI5ʡ*Ȩo2ܓVI"/1WD `"bZ#E 5$$tlZI;*kgUK32㼩@^|r?7Ɛ;0KΥ)X@$rlb J, /r G*L u}|Ah4@CH C>hØamwH:SpJaos,?0Cj <'vO*ڌ&f;T*k?:V PAAs>^ W%|b!ҧ$\wQn*MMƊv6n kiu'"v<3I u #/38Y E;> ߒ{Nd" ŽZ ;՟Hwb~>|馶w-yRe^ P0cEktr"Y`1xFwr{ h*o>@ޮAjdrks|+[cvtZA’HiIw?5ak.5{r3 }}=t |{{r}Ş^JOl5i7f<QmHtg7Z6h*4&w׫D~P7 SY"U$ ;wcM*grϳ׫sbHPQRik^^X`O $lm7./SRJ9mhmn]u|ொmk+S'1c0k1fM?thRMLRڮ3t: ;-I:{.8ÚA>O :5?kQ?^6o8i8Z)@/gգp1QZ$XC=ޥoQOI̐t,d,C$`b {@VTOH'O"e]v\GuaKIe*utd~DHBFҰ6hEj }oCP"kT6Alpp^g#2rQWn1b r 4J^1PO?$rVݜ 5Pbx:AKN]][Mׯ>ar=нqO:%3}An; a{2Aބ|٪$vdTf^l mw{] rb)ZV/_zKiNf́S9ơ]qpN@6.\Rv^BT'$R#soSzc8(T)1E3tz/ergd@ vJޯ1912\i.E6 LN #OUهXc#k|j0j4ׂpM_5:xĪp W,aڡ-udjJV&NFhP`p#9:j~]a2XJg( ?imVHX0Gt9)&l@{ #LQ29ɞen![H 2ſ#x Lg$룅IMw0ƳFZ4֚y6EU׍1cyf,GlE[x ' mY3Vn*۠yuV8-)+%kiêe&Q&z8^m٤tЂ\CwS*5q\_qNzr;_J~"w]u^R=5_~Kbw2=ۖup֪}^YƐOЦ{2P~@k_?1⯵VHPX;6>Fto}SL+߉$:i@'{;7=ER7Eo2qJ/Y bXRό >xz7qmW\|1qEFqcp٢\Kq$$SFxyPlCDU6sNWoY YKKKKKKs*C㛫K5C)K.!& ) .eO$",ݳ 2qd̳ qod99.:5: ",#\_Lq$`Nٓk͔$Q ^AKgB2Tl~2-O(Zf8 Ʀsś> bgBsmڂQc$蜫{|rNp$~b<#8.ZbV†Rh/jSC/0cΊ_s/~Kz< g']wwܷK M -4ୋ{枻} ?n2:" :)mA m5C)Nkw$c~BXO3_8{;V0ѣd}^GF'q43!kq-h4=O-[4hk?I*x H"ʇ+ j8ktQ9Zhs= ϰ Ԭ~Lr*ŠQKF;8G e.3R|M~ Hj˺n0.6>FqԽ2͹Kw\:VoK)x6SF~5[$jjR=URhe϶w^n !&f戆Yޯ h~0$.ʱ @q#25TCڻдÉO. )rV&DZ.N חzݓ_qV|O_.#d$~Ԓϭ$aGfy{.O8v=-ӬP.̛n>W1.w\1lu6>C_AVי$7|$ ;=Z>Ӊϔ?c󞿘Mʴ}k!H}\wg#hlz9vܹ53RgK8 ^V:K5I03h^e6|=w01p f|ћt(:bɌ)iO5, 5 =o߈mqp OW6 <>U8!HħiK%6˜ycHX9Ҫ%u z!-y0杓rw͹4I3 {?H+K?M]yя:GtRlI4L.qM+"BSuX2D1O}WbNwR b&&^)un]u4ֆDER ;HNZA,p]&̋ǘP-.8j`{ބPے,oGHcd$³w xrCjLXVuY3MEm'3cWMύ7#Z-D˺8TQ+EUJg7#f^⏜x<"Gʂj}i6H̵J͜0E,. r<}S1. AB&'0Mw>k}@_ZfBTaXde"w ٫aXZabbRT-+BBf tߖͲRvePwuC &cr L!1$\S]<[ k>rP䢥DKe2;HZtcg)-,}yAUDNR/sfK'ƒ{dILiŲP2[j>A?s쏄`S4GtcoCD-N4P_gNĶe}yI@=suϿ4`hU'DW6":TYXi0Toj.?uQW8;e> =;%q؋s` 2*P6ia cfy]Xяz>ovxP{g{$\=l'Ov'(e\g@NG|8S!vLz*>]XUs4Yu$?EQEκՏpgjBݿCT6n߿{7ֿjsB@v ,7Xzi>T1/# =GNf2mđh ``߆˥K؂(!١$·-31zqx1OfbBtط%C21sw>pzW$&X?-)$ 0D^Djd3ʇ+!k"M&܄d| 7~:7l/mu=Y,%0,YW|('&Υ(!+E`K}ΤcC}2#/\s1ųN8:Lv|&} Gqo%Zv#ʉ 5]y~yj41NqgM)bD+Tتd3@~9L f AI۪aޕYYfo0/oyE1Ꝙh5xPG4kMuuQ3$}D=yfcKSn\31ŠY% NwcyK%/H:eLn7ǰ4cH4lı_qG迄Zu$4K8쿣M!i&p.~h\4(WGls$7X3.@ď 6qx]JMvv˖eiTaAd5kAМ;Ϲ8-ebKױ ^/MS!HlFgK@t`S[s~Ƒjt&9?𝤋54Z3m9GQN@<It<둭1v5bփN>Z [Ԧ/ZA hXooړѹ~{uIp?sh0wUjHXH{IQvnDmyLO$ntE,I3b[ܫn[<Om0$0ƺXOAUjjKלS.>9Ws@=CXdHCj-D3Ng7uZpaK6S \YOuc:F/w}n~CmrZ,d(˴;;k6slraƆ_5HXx%_`j3"t[1{~+>'׾KoTGP~n^[=SkžGF>J,͘f{,#)SLʑ9"d=R&/ƒ! 绍o`>|8XP7k+I)^I@)0Ƨ৿}TL!TM/[Νٹ^iR:aفqΤ)/5s nR.J_Jΰݥ Nq0'G&yW x@IV;Y9\Dc¶0p%E`)wqwtyY|g~f~&\[0P q D;/Ճ]螿6kLN:Jt g{麡-5'6'h!H͝Sbf1oL YfHnl>($%JD6U|C-r<SmKZ; mF\_E]G-8M +ɢPDtzolr0Q/%g þb2QE4f~L08 ~"8,`7CdhXxu@Ġ/fjZ!n1@iU vj4`YɲIuusv}GK  };Q4 kȹ)ēބ'K=X,/%MZ@iS1NtDLd/T>!5}#NFNȰk?~DaηUDɢ7즁axiS-n8Ty|5:mUGSFTb[;3d%lfWH|N{War7`A^%_Ľ=cqë' ;`1N05Rn`=6gei$lؽ&Pe%i|$#ce*S34=}xX L'-4<;;}%y ̓L)wfl2KgT|7h浬^j:_GxìYks[Ɂ%o 0Eg)v X"?KSB-ɾv'LiC9T>Άzq*ԊN?q:g3/˰"9.M h,:IsWj%*h^B`02E#x6, ^D&_bI@r.pv]S$j }c " /p R&Fݹ!}uJĝVb^O@q20qj5Θv] gQEStR\5}?R }COkK\[͉xԠs09 W\'3%&Ofvӡ  L~dyY)1٪6qj}^ 1PCWOHU˘HN~Iw*cρE/Q*evzZ](?&35] ;jE5Ghlu2.l@hېIP3%]lz>}ͅno?e.K};E1h:4zwyks d:15z5<аuF̵qf\de}kgkҞj=PF#yT޹: u'13FK';I-fKd$څ@7/lUi/.&]p#!X~/vNXP@uZ#ÉdR'}!O/R7A T>\AKT ҙ" Zcvw{,Mt7 σQuʧM*7E"q0A*F =<σ|0jtt>F#K6ewIQfl,*)4\*¸:OKpniDž{wjLOΔ1^ iĸjNqsv4*f(i\go!bL怇'@ Ʉpq_4'kB[F:S&(s3kKtk:t a19Y}б5ܙuR?Kko6@o'u+Qͅ>:_߶^wگ}R:[j$j[YdjAOsB䟝/ڻ4<?Pg?>3wvs2*.Υğ6Sh^^_.WajN-܇yLby玎E(?x̅gYFyhju9 ~ m p ]}|X;!Ty⛱9gid"AoiU>w%uiO&6<C2貺dv s7SsF_tq/yv~SnX1?ëhv ;\Ռꗬ&OOyY ;e1M6N@A)jF@l$0d%d&rjk`ifVzjԀg& h \ynbYint*8l5@>W,k]ZGF 57<ޭt`w5ш<.N;Nұp{bg3iCøx Vx;$ 纻,YDO|zx ePA -;v=,FhKY: Ucu.=fdT/mHɕ:.صa.#~Vih2qLi;=qH\DSFO˫tm{&.^ s^'裸韶? Veݹ[$nk&88 nGP|}%bN=3)ZG.:Ťnjⴗx)ߤ $T /…cq 7 )5l⟄G=slVuN9\WA(0]h~% vn8Ά9zcc5%o"S 7334~L@ՠDHk"Yq,7 :xKs䨕VuCmm) M~쭒Bcx~^vO}TjLW*Π9lXPlw6ʫm_~~jfÔml*~|A eS}^W?Ʈ OUl 껉GI)jQ*^~@G?oFi Wy"q3j9 zӱ8YBg{-7 O1n(+jkH^NB}V 6 1ñz~ȈI"P2⺈Pf ^~.' i)""K]MZPc2n*RoFNrES?}hq`ᨒ{Hߤ^zn[wN!䨲Iݑ0Eȏ6qN4)1 Fq-L;i9 !Lɾ6'9)?%TxYe]Qһ0`yy}Yi f\=%AÂISǴ֪M(#U]ʿj?kfbR(>?آИ x܋ϓ?p`S#m2ݚ ä"h$aEâ lyFswpxUQѐ< jõ5L-+NlM_ν%7ͦ[z~ &SԸ7Y{Ts̹9ujJ'+yFUf0)D/ԞӡqjrϺ6U_Bl4Q3C_y% ` 5Ab'ݣS)VDщΞhStނ(pVcq~S`wp6l]52!邦wa?T0c<"J}Ͽ`OHܿ}EĨ2n?}N *$]x[[/ug_b-WՐ} ;f|G؃KNX,tk^L<++ ,"`x 3w|ׅ\UL 5 |7aDz e@SD9K,5%l=8atؼ~% (چ2m\;Yۑ٪6yppcU;[?LE=A25 l2%qZ'fGU*.[nWuzu&vMYlQVrъp!PC>aسXi'h&[/GPfPMy% gז%#IH- rtdsVQ-fr\jkRs=73d􇳄n#Wc$ly-R>6 Z]^V&l D7OSu1C9\V5z9Dd9)C3H_.>+7ҡk8rlU`3hլ؋j ` H:DX2}&Juɵt}/vs4s5]=)^}_?ùD-},݃RٖWcզ7O?JP86aa5y{mV=>xƪꓱz4O1T_U5𙩍{8q'2W[%ī]1GLAĆ/ x ; 7F.ZCCr~IӺqCe@LWlWb~TQDRר~w~}Z:@}~;Ҳ)&bY(/gq,ɜ4e9Wbׄ*c]NҤgM{?l|+lP0 gӟLƁidvEO>OgE-d~Kϝo;l֊#Z":g}*P?1E{ exQ'9-^Q 5=` 3)i5RqǝJ7؏Urq:A"EDw^dNzpŻ?zH N]!AIS,| 9H\/[GKym7#dsgP}n|n[x )b鰽Lwf~[}9yR]ꜙL>2`[`^)0)ڽ>K|:S(°pP^^ZJίjUX:_;bΩdZNBDzw8±{ 9Ma/ODy!JN.G~XHv`3g*#?Tnb/A:+ڛb3^b;8פ񸒂sY*?Wue_k/U٥ojeԴMD}%?IJ&f+`5q~/w=رPU?f2NjBX]E]XjuZKaBN8g.sW3'LMv5"ː.Xm8 `@Jr^r%o1P 4?l8[x& {oVp0Dy^9N> ZU󙱙Rh@SSmB60oz 7>i7k+PF ׳ iQG7,< ikM1ejD̳*Of;@۰9c}6-6TNXD?)K2z9E3A9EC ؤKNӦ`qO7̜QghcN;אO_ü#THrYe.fLj9aO0 JDɹ,L6B7Le.HRDj̣+s(n,M3<2%'B4% АLٜ_c8N~%o5@e4eZqZwuZyW{OV?.MRS@tt<pҘ-BxtwV`WY9 身Gނ pbM.+ ;Cf7]t2>ޠ%&N#M:$]^ƽkWy6NAoOsTt 8um`>M\!>LVɋăl6Gt;4>Vng+[9|`+n?hM4As4J$"~a̖FR/틙Njfq2Dfs+.p$w1x-d։NO ]wO'`W_ xJsuqv.7u*2 '[: -&N'_yE8{:7. FDVHJe#)Hꗟ^"#-c}ܐ(n$b= 놈\^?,+l>oU,4N&E涴6ALao5wx( )pm /'kQ$=7mkjCk=f'M.쀚=yjB\,KPw22.}ku/s19_ N?Aj 'kFΜp{{G1O1֫Oпsо ]Ms8~ >NY㫁vux8ha,i: Dc}3@5 91PZ4%0GsIvz/z^R|x;F|2+ Meצ9Ў[4Rn[B)B-B Hٍ0sFmn/b1LһK>; I8¡]%tDM~-"|ߖB|4Z1!vۧѸ=&-as,Z{v/V;/?;d?wҽr\ 8VfEtzM O :Έ˧VaP4|,FNﵭ1NαGFWC;3 a#QB#**R|+&DWY LM-uP8|Dhu&i"8琥mL]$j [zW?nH fd5=%w% {ro?A5E^?2Pa =P9x#40C$D#- &aT@ Wsj9baN$мLv|X9u$)LrvX=s&( %X O{6/N͞)m4a *:$U['%5u}ǽã%/+8hZ=~zqG nX)fɄ1m*2trA8 7 )fcd:!yrX'pTjK0yb fo'Ʃq0͟EJ㇬,h({ aa@ +D '?qt{zRn?qDd+( ϟ95Q^=G vETE-pj jn9?M.XֵRdnh\ ) ^M;Zu|8E/AtNFXtc4UJUřMBXV.<* J$6igQ8˙Ww2#h"Q֑$V }S}d2MPHAcޕ,*IkesnzT(]A+y[,+PO%c{(O$>yIo}.a.PDYfևP>5&3hľ9>$sC@L,PƯ?q·%'ŪڍrDynWȠ*sj 9V嵎[ |^3-~65Ц-Ob/a?jkQGĞ1^>䶢P)6W5UO{d֪U)Jo+7*tI,8n95U9@d뜞W_;Q{Xy /xs46z@v^<Ipb 1"F,-Nd)PhU+1\MdeX_7£OH @$TR" ?I$+r5r` (B0ד_iutgJ**_N0b7}C,﨤8*L|8eyrER'I|u'WIMZ-TBqPJBZ&ZcBWA@T`K"jduy/^ySkJy(^ij}lXj >B!>h%L~jVίBFEڬ<օJ ϦYؐST~L(^NU)2c{{~/ KO^1g8vTSxRqD؏N'y+bNK8{1+ϟQϸ$|F VHeabDԒaKfL'Wbl>3 67 қo: O yy߲KHiN(W\EǨ -%?W$[a*IHCGyw~W }GԶBA 4@'`pۯHǢDP 5%w+I[<ٴ4/e|0c%!W9_" |":jX˞E,:^>Ao$IVz]x`[ɻe[޲Uӏȟ=H,`(zcHC;sU}*VGG2ӎҺ~L6ߔW:I~Dj)EJξ9j ؔx› {1K8kNƝ- m )9+)_80?"2{wgʈMb>8$hr zU8dWx*v l8f f5%EI?prG.܇!KDqO"kysvymF<4)#tR:Gg4ڪUw^TPsޜn/|nJO6W> ^Ow)ŅBʋz[Pglg%'wv* hz?Gʼn\^n1;[@#QI'kpM=<͖FMy{Mѷk䫯mހGpaMiH(QɓhǓW_\3 ?>g9Fkަ^XQH~G䖰r~sZ1M޸l"rM3b >af^VOv,‘y Ϧh:PQ84@-L1#ϐx$PX7q;3^7×XkSN>SpsMm|$z2x"ik7˲sMZL.[chGI a/8; Ɖ58*9EO m{8:t5Sd~xuxsf' ҕq]?B+ݦz&Ϡ깨5oU?m^|:nDrGq'xpaUɵ8z3#QM {mW-8}ekH5QkwHVǕ{ fhq'kk -I"=0uXd%0&6txY xhy[čGXj;D[jw| tks4Nsi>k5VZyr@WIBZ/j&__xǕ9zV&gN)H<3 OgG0Cp~uޠ%;i(A .]I!4̆ɣ>gt8Zc&8֦_ñ tPu >0VfwW1K8Ex[R-^nRm}&l3&a3 (HJU(ކY2.%Q.4caѤ*ΣLk{l$tLJ/ͲLأ' wPڹBYl1zbiOgŠ<:oI늬i3A#f= H\Oc/Сg6C=~: α /_Ju7σQu'{0e;SJBN!֕}'N0ǧ㜠tI wu*j6NxSKBBSjvq|!j}42wrf*NPsAvpx>M jڅz?bܤ5SK4($-;#CK AլpA@qs(d$?8?H2>@6ӓY99t?Hg`w6/^{- fUM^t ٞ!A2H P8+&ט0u9?He}''CՍMO ֚w0-x"LlZ ~ow8)_Qλ x@'{Jٝ03Wdwt I`x vv+Kw3w_1dău\ Z=.7u9Z=u&͠8B\Q7eb`/N,tr)"9OK34"93o?$1l>plv>;fV%逄C]AQ66.Afbhm̙Nes2nQv O]\1ל ;Is=]n’OJ6ЭP-l )?AV+gʉ),['GZmϞd OҘr>%ǜG\ lQ{j7n&fBϝ(aYԤFRq3m.E4iѣ4ƉH@g%H=Bɔ*w /;ٻ&ޖ3̸'\2`AfԍS \V,`fe}Չ٤(5CӎΕlסwp=QW* !%&D;>ROmȌ=vHb#ICQdΆ(qwW#X:+!h҉ƌ!ze'i xɽKCǑrD)˝פωc"o<[[M ΚfMsg&G՜R Bn$_+M~{ZUE[ُxJjbڥ8RIv-Z`sVM.yj.z1Ol;JZOt2ݓ8lhNPa7WTN-tbtqv0SǠEGSQD;s|9r}|ڨ$ᔹ1RDni5%|;"S>E(of5M27-u5G0<\XOyM LDbcL/&c[&\c4Րͨx]5vzw&Ǜe޹w9䌳8h Pb*-Oe\XDHLnY-(jf[TΚE,YD<vg=-~ihq,㆙+ѡD1ǡ{Beǔ\|C*y$q/јnFB̷ c~]̹;?Tػ45yi;a̋UP|L&h%|'hsy>G_e͒uylt0DG'[y3k8,Z8%RclɅ׃ߒMf lISp/\?~޿JBK8*h`z "2TNz#35ggv~SOtt[Ζ,G Ya)˹اw.4rO^j'L IE~z7 "I&I l †s]|,"\'>O]~=%hn XlL -;vݤ贬Rb?.k]A.=oF,erASz rS8~yIH` *F7K(4O?2qLi?O3`H\DSPU}m=W6vzBSI${dH:韶? qugfzmh@XSuLƿkCP-p5o8|p&e;Ll' S:حSwv`9]o[<.1P)|GhЄ0&FyyADRw_N&F94X>2ɿ4 sFV]PM 3 s)ADOeu# :4!yy1pu6G6ߡ'm`Y*9 Z ʴ)4C5{Q+ mCmӸ߹]k﮿w8?l):JJom5h![.FSS+ـJ01_9޳ >2C0˳ZNfS!b]4T힠:$Fh|S7 E? В@%#ْ(d3xښV6{:bdD„~كLTF৆ڃ WIaE4`>~ *SΓ~^O>_ES`"fcϡ$.] xK`tN#hBBsLH`R獠Ϩ5dUwͩFRgnwiYWLZJTUf(*tԧ6V5'q5'Z^RG`նwWy0w?ݨ?: }x$'[S6F'wbTl4w7!8Jl\[Od2ݧ͋hp(T QE1g_L768BII$@r:C9U&]gto.š31NPy@68Eb@pF 7z Zs8>}H{sbМ;Ga FV1MLSkdPO(b}6:s9.Y͑,q0"MUlDȉr_ O,Wp;WO>\)ܞu;%Ágs,E'2M+sWzn*>vy2gjBݿCT6n߿{7ֿji1؋u/h -y╊> Tp{3.A1"^O,/`(3ibv(wraע{>1ݶ-Gu) G⋖-"fMTUZ*76x6¼F"uA(c ϧ06[{O?ԭjb>55dd;&V ϩ6]8Jzͫ:xmzj!6qiTŪ^Fc f2/[$-^l$sȆ;إY2bb0ػ;:4hV'6j8=Ї~mְ%G<T}9gv$Ɗ<K*L.ֹn!˳RJuwhi.y?~6Z׹ C׆5H 1b#$ YN^PMA+LA<:^=jN@Dث'q3pjcc{ȶLtosl֑q2Fnޅ:*Ao70go=D"d0qR_83pZ7ѼZ?#tjWh*6߬w{P|7hOm4Uփ pnSs̡) Gʹȥ9k :qŷ{{2ezK=_/%K]-'YZ>@^ b #Mac\ J:3KҌ/!FuM{cwnbG2I6EIݝg*xuxr7}$xgAx%C Uy)$2uk{޸_ `C4|] VؘRG=\쫟u~j%Bys|JPWdח,PISvlS樠P>{"a(}+_VEuTC otRpCy"əPFr&մh6dlr?rt.GXAՋ?QüF1ԘDkZd=swDF%-Yp2R2(8-IW Ub}ndm`,:ޑ%i6 CO/hыP(woI-|HRL.!^짎qH$({w-EHA|Ѡбh6Ż9)-Y U1]DN& MҠhML3BT?+EdZ<$%`N1Es CsutO6LSK8 9%-0:jwP8Ҽ-5;Pa9 WԐKvgvL\xU>i~g9p6[ u(%lΝ;u?Ў:CcPh=m[J:hzJP[zC k4 -,<_"HR u/ 84| Fjj!wg(I: 36Üg7̤ h 6uNχ4,#EWJ.`~s2A p>2_v=/Ʈq5  (w#<,T cx7:I0;Fx^1݊BвSx1 jff(Soeʯ֎ ,@bI]0 $@| kT^?yGIڠvM& t)AYR¢2[&i{)E GGiɀ.2e_{tHM"*m֒mՁÀP[.Z%pSMl}S]IKXyHv@Pޱ튫o2v$6ιJO~b5I)<<= !|{eNXJQ|%+9g3"-KIIIIERm dtGTY]Ο߂U"ZZɲS)-Eޥ/a)YzCmMtȑ;ǾsOSsk: $.e=Df.Gda~ƍ"j)uF9R.yw^'l>==hR5F ;#EnZJ`y 7J=jcyp AB` #}'M"5".ϕ &I <~d [Oy51ܭ&cQ<,vN_\"VKgDqf\;œTT~!7 O[5' Wۀਟb#&ɤ'q^'ҕ_r\^3v,|ë*>e;wp4mrc[ؓDÍ Zy׮z7V8^rl<ȻOulK>>qaی6e3SCj44dZ}l{˃|Bkfw~Oռv3I,Y/+fv8=٦k ՘>s05t4I} RwʬYȂR̃$\Y_*nt(StHvI$w$%}6 Dvyy.o; or&$UYw)+M)C^bnFKv8r>jJen2+A]^c`1; 3@P@h}_=IkGSA%<{N HYN@ |u}8ACԼ]Uc"4?q SM'962xq|yNP~:zc IDe$=0+>+SO⟐w?fL+2;MUP=s0^ BȞƹJE@2vmqc0<3'vՆb:ܹ3#H, )FHe6tu Dv?Mhly^4#0r67"'q1%B}dYN_5u`% `Uߝ0%_Y A|53cΉ@,(T7/PWz̈W hD]D̊Kx4f-+ xI)t:g|C9!q~lQ[pjKQ<$\KRh]%Ecޥu异"HyfYQ,.Pq?&ʹb? E.2!1ծ|4~l1%!i\Z[,1a/ܘ2b)qR&DFv7ގO"c٩cO=q5jGdCځwwhp+myD+EcJzhӃ=vl¤)/.1!Yu# 2'|nDub |CJMmvi4|8 Knu2RֿI F_:CQ8#h| ]%/qи-}upZ\*~;LO0R;7f_R{ {~ıx 1̫@&CR]ׁkp4$}r>>v t'@:NKS:dE\Ñ)?T+9<׿ᙥCT v}:-yKI2Cj#-wh ЙӂPCgG.i8X<p N"(ߔbu7! P^~P$reorutmL~ŏ9y)x#׶B}jCLs?'IlWt}}K]t)+wqaL=ZV]$<$)}y`!~>Gqa-0Ptt:UTv_/ "'~Fj4 688Q+b1>T^MJQ;m𼍗ݵ뒩8Ɨtft~ mtI{vf\L^sgx>+,[< %^<e”[&~ðw_t=7nfiKr^]iHՒu.&ݛF 5 I=rHBVSYX~WÀE͜ Fzg"a:5-*TeEFӑ!"W`a2#q%RMJ4t&2&02dQsN s9K#蔋PMr~znj@3?I>D ̭ 0#:*.3 oNv[ߕ?Ib=g'ä)sI:25/'*)A 5:/گ>fU5 st,~,0.Zʏtq5' .֡K][J>+=bFcq@]4U`)ZDR)ȸ"^tJ\yר}~ǐ@|s檋qCQ^qs=iO[gKRgy8&{ ( q .an}\t+ Քe\/*Kڧ,ȈL=3bessh;nݑv8U-2+3a*lJ3ַ/UKB[ rAmL̈́hDSzJ_.Kls@a9Ewm`b$1Fc4@(~`5JM uT; иgc7ͫr&>{}!DdbA JR~ LP6v4|i6QŻM-*’OHw1I4r2F"[\O&ݣ\)ӹJ$N0 ˊR9fPR`x,8=E-렌j}If ^=R+yu`4 1M"iL 3TѢݥj|U2ɗs%$ $fsӳJ`rX,_=Rkְ⴦?QeԆn3lgI^j@? aQ| Ole;TS~> 7 6=1L>~m!=htASŻ<#`Ɂ0"{]RJO߸mQe~JT<ڂ'_~K<moZei 5;ໜԉ97SqYO斸m Vg W~%;S1h0Hɬdp_ 4EdĢi:}̃XO$鴏BL-sѤ|HWoQA8pRg4fYN&QԧlEp7LCqI Ux;>'`}o|jtLmA;$W}!sUANmnl!-a3]Ԡ!9pi]R_tw.jfRf.RQ&7zi+k3聹ۡ0)&bZs9|Rп Dw%9Wbׄ*c]NҤgM{?l|+lP0?G୷$->Έ'Af\tVTf.Kϝo;lֲ7}x@Ĝe(xm(ɕ9-'£@k {R@JgRNk$uq" #k~S 5 ǾC>.N]jY":aY|)eJ! SWHY࣐a8䝇Z ^"N#.z#^nGi/d [v3wU›g@H%7ß]zyGl7EޗOOpk ɪsfJ7җPm ax'^+hxJl4[ MFpvt4WLҒW2w~ԅU;’Y[vN%rR"ŧԯr|q|"4v0cѨs\v3g[ͤFd5у~d@mHRu̖+E\l 7p7Ö-z`/ؒgu=UPĜݦܨru3ƴȢ##?<Q8D gS%H ׺OLetGѰK){%G|Ӧ`)10K^p6 {tF~z ~a5+%59KX2I~&S'L pA}8®_9_7߿u^F6HD=)?sV~^{bMd9iT}+!(х6i\Xx=D|;Tg <3 65t:Kֲ03H"I.?a{8uړ,k7h E^6~xX1ʚhHS& x=%k81K][kiuD/~]ܫ߆iuiZ@rOJ3 VsT3NXRa,_bvtKt#oA8g @Ƕ~O! ׇJC3\FPr@7霓Pvy6>y_}Xk P8>u~*Qgٺ8M4q5\0Y=T'/M3sX~Ol~wǰZ>!o4s4J$"~a̖FR/틙Njfq2`q+.JBVWwzWͬ;'_X.40p]];5 ͢aY '[: -Qhzt:# >Ȍ X($W7"*BP N\Y$=x28n{裇aC&HoeqT5Ii?tN8m`8xj_t*z/+1g,T"WjU7VW--eA+G|;2ǚ{Wɻe|7ެ/A{_ެU9ެ)z]5)dzTXՙ O04Ŝ&쉫J ۋI Ȉd[VE":={+frtl!Rk{G=! +uD`r2ޢ;~ABשv4ŨҼm,]tnw9pA%*8ѐ)p:$Mj"•"wcb9X#K8~|y+ɿ^לk71j km%4 ^$D|zB9VޟobW(/OEYA::4I,sRy fkrBchhS'.jL(uDp +9d*&d,6O'I8 t;WO>\ PW0#)IOM& {ro?A5E^?2Pa = !٥׀"!!8.n7^0 R\0Sȱn S5)thls9u3䂂+9[-%l{Y= E]d* GL{%ΕC3:,aȬhm ~]>ݫ>=u zEx2F#ЮX.L.#@W8K"{#՟ Џ+Fˌiwg!5~E gĘH&|;x< {}"LQ"isO8egLnK '9a>ED N0J2k:3{G/߁>z."3</~x|Jˀu~F EyᨀOMcB Y;NH\/ 뒠?%Q`D#r85FHItB ŝqo4!; !s䤝#}=={82V(EG_Sޞ#"'&Tm0"8◴˺1OIH..Qp_>u/ZGQXN7gԁhNA;FSeT*QQJ4$Ĕ|aO`~©JQ譤(zhJ #a8&-%E}qp/ɱq6igQ8˙Ww٦2#h"Q֑$V }S}d2MPHA h9Mzp7zA* y1/I+]q2 X"]LB=m<͟w' ,m%O^fbt § C1A?Qև`b>O͇ sCP/2{?EìC av)Cx^'2tz@rH[Vkt׭*>n!LˠB*٦$<ભEڃVS{xeۊB\@[T=푵ZzW5B' l7W[@$ 7[͜Mq Akz{/[B2(?Gcӫ/(j'OA+g(s b$:tLZuPU7Y`= >!RXTzw1bhJJ]D?I4dE&s_,8EfzR+NluWTE PyG[BgRl1OH*U22 8I+*ޠ[h"_IhZkDkWj \l[]3/9?<+opmۢC5T40ŋx6Qo~ KM@'Y_>ۇoAͪ#TȨA54gҺP)[48 q*]?ڏKw?e]BflpOQ0O%5Az+l]nt OmC|9-xa|pү<Fe?,#If)X!uuQPK/1g^;g0@-mn237Aet"3@e%B!'ó&QlA#O{18H=Q[:K~ԯH*U~- _~ˇN8m hb#+NR_,EkJV8VJyJyr˳i%i_e2/aK9BrD6EPu~հ=Yu# }߶ H&yW91 bU&m.{@S 1V]#0m0 ilnmH[*F6<28)t/hW'2iO┳RI"VM=HUx Ide>m~ؖ'L9_GBŒ ѯΝ]3y8ݎKB;DKEQ[-"ӻ5~>q@J_t:H)X~9܆Xl臗Ȉx(9Sb~m$*\A A<2zq/1ܕZ-[5R;Dы9'Sd&'K:ByLn6lrnEW sئcݵ. ">BD}#dN^H$c-'lonj1s>יFEIY(xO[#\Wj?+i3U6t&%F9'Q>e2e.E%3B\f ۢ#e]xyd#-3iKݼKi;1>& ܅,r,nȔxzEkE\jNwt k4Z8Rj/e9d-nPݮ2<^Ǚu*7) :bGZÍTV ą$ZOߡ7ND{N;s~Cwie.5A$\%$VFK-Dž 2;TLHR(0ʘ{lY3yk1AH>"S8IX }X,tF_Jޅd@"ɴCiNŹwt܏/RH!g &'J8~_n@ ϜMz19 jdcs48#l)t] DtM;}6hkM') gQ w`8=Y,#"\%^h8ƈvtb!`JBi{enyzxAtrҦ]w ۃq됸0:ltC @32W`"aWJvs" 3NmUsUQQ̴ckM7,N-Z`ms/hδZ3<(6%f^.9΄뚴qg4CGBJNJ*񏫈2jSذ C@wN0^jJ *)!d(;;sIQxѽ=aȒ%Q\Z~|^m!x]e^~%D =Hj՝/<j7' 8R䓄UWSb]dJqeY?25pE@iI>!F#wo(h$-Ch Edz; NS(Mb {VgJb+8G\O`@clQl zMUJ6ͅ*+y._rD?AA*t㑂 ll*HktggaxG)0,!-Hp\wiMigG;}o/+!w}?B+ݦ{A&ϵ;Hg=@dZ붪舄FDcx17O"|9#_[Z^0z\$h:!rbW`/"O}޶v-q"o"Aj b J۵9::)z\ZCKlH`o!-LnՒo{` o&ux+۽ xyk\[ |Xj;Pj_tksZy.?gڪTw[K^C\OߺKSn_k/q>/~q_|*Ao맰<t7 :0O2eOfГa<~:`:NQCV;\x2_ IS`]yva5m[/nAaf*ٹj%-c7Rl4NYW< UaU6Xh*Ts%+-}q-=QBWZ5s!-pO@ (rnMOa! פ=FC{1`KeCZj:NB a(6C>+ʁƘmXn} O`Y0(4V4n89Bdވ3i@Y^ȹ_cgpMU3d|B@",$Mט  uue=8h~UzXx|{U>qpx(w).WzHr*lz}{A{,C5;HS(TN3PcPQq,?ZRS\ME`nw}69_5)g.Ȕ*Ԩ€ѫ] jMQS-^&:qDBBH#iWö.tpB5,\?Pr'E\ܦ+ٵy9-&N)?xoekVNVͧ˨^BygU}~vBPuFnWI{1XxGgsC''CՍMD +꼃h8adԧ iHӟ2u%y+΀Kdy?W3ӑd|baHvg@[MÔ]]\;Ey6#ȖpH?tњiu9⊺)r{u:8eaKyZ q?Fg_}%|sW Ɏaq|p*lD;l-vkm-15ξ~Lbpy]Rso][uoA{{2xW%,p&ɻ.$sGm 8,ax?:J;^ + .^L+2!Fn<몱+338DpRΥ4Y%g}&O%J_FzEL(P[ޢNnQ!K*trVɅWB$/ZӼzX9WC @=Cʆ{d)T H$g_V# Co1Y8@"љooARݙs/w݃OiHkpP㕽 vØLдK5Ot'}f%LEZu`JNvz\/N" _b~Kb{7 Dӛ)Vԥ7ܥK-aoP \Ht TM,PC|~5)9HLYԓ<4]閳%֬0;Ǩ'/L}s$Lc?{=B̊_c4zpc#M|$]HTOa.>K..G%qŝXl -;v4DR֢3:.k]a.=XF elASz/qS8~yIH F7K(4OD>2qLi?O0`H\DSPU}m=W~vzBSi!{d:H:韶? qugfzmh@XSuLƿk‡؛-p5ot>k]ƍIPJZk{4g* Z"3H eYz%e0bzb7Í0{ƒx4qOgѡmӷף0G=fF %W0'Mh!ڸ׽Ivu ǚ<b5Ayqxq`Șp\W0A>ᔴA;r lW|Czv#jO$ӜPsC>;#.:7:X6KN~:1b1+KEg8K?::&TqJqʨ+H4d  <M-pEOVd(d%-i'qw-[L:d܃SkRtFyb+0:ygӅ޹_F%Γh`r! 84w M aznԞ8lIid"aM# V.L#@y>yA.gcu~Vj;1z[ߵ|sO̖2!vVow!H#UxW=~ d}ZsjS=;#3<i68|2,JC/q/w|rnR~,:x%ѡ%lKF%9$P${+7*5O7Sm\tX+ _(X b#F #h>} U Zb/'}"'T E"ƞCI%\ 9Ca5:&,FrN#аtM(',A0F 9y#3*v -YUjs2fѻԹ$]deVb2օ^?-wպiǽãUu;_ýqoZUU0U#D~_tSΩO5mjN+(蛻kT0\5](tG=}In0US <.P娩an"i6TX}NĹTk^թī݆hslT KF.V5[6P|Ҥ]fkg5Q3$NzCS=R i&D6$ .͒;NѡAn@^ b #MacI\ J:3KҌ/!Fu~ء+~xϏ}Gv-bЪqKd~Əɳ^Oǣ›-݌xꕕVuCmm)ncoW0d 뫔|Ǘ@Q}toӼ[v{[>᠜TLH*pp)2Hr# v(jO)-'閩C_{ƽ7_KS ҘgT] :d_ԯUs/ ʛKT"/N"{`"N n;̉vLv,.kjP1|H22P~ X0(W+ioW^@!݀bq|7S1BI8wc#$3y̾VG֬$ȯԷdTuygRoj=R&}d:*u+Tw8"L{An4J_ylHYky|'qVhUy%4ξbWV|@`h#Ĭ\dгmS5DzJ@-'1>5q\_t>HVvhD -cm}־- i&ԏ0րOBg γLBvqw:>ZG{k4qԬ#PZO "5_FZo>R#AX;6`x:GI(!ی qH wt@$JWaר$A~,5A5kTB*y5$Ŗvo|6%1 3-VȔ{~QJ"'<)u;>R w'E#>a<" O{Ƕ+&8;g38*=$٩<e^Rrh~z_____ǧ=nK zҞO|%bq }Fj1#KMw np ܀nUs: L=p0_ϯ9,L9nm2:>[zN \]4-5ckǂ?ڬ{^_qG&wA0=I4ܸqzwly0U*xʷuI_keӺDؼMmS z?5IH϶w^n冝eLBLdDbyOXrطjjk[d!ia?{C{ N! V.ǸIRuԟ>4NE^b+YFl$d畓+GcTTΖz6*%><@߀>3֗zݓ_qV|4_³g2{?j__߇=4AUEo>(O305tbc#˗ E[7>DXLqd{a<>N0֎ m-ϔ)f>FF#b1/S0+Lۉط & QGC*}/ÚH!!"L$`^n PN >za˚۟o{\lViQ4@ix>g) "]͕:hzMN ZQ.fZq謽s"gn2zA/_o:|Z͂))#ɠsC2ū.gAW3bi!u@x,i0f~Mbm3KLK#7kXJܸ 7QݍSZvSO~jѫ;{eШv;9 }w[.Ji:`O=ý90s KL=nfVH}Bƿ: 1?(6".Qa]9MyhnSt)yfOn7RS?t+M*_q>v tN+*;NKa:dE\Ñ))h?T+W<׿ᙥCT `}:/yK2Clq#Y-wh ЙӂCgG.i8Xp N"(Fbu7! ,T^~P$reo9utmL`~ŏ9y(b#׶B}jCLs?'IlWt}}K])+wqaL=ZV]$<$)}y`!~>Gqa-0tt:UTv_/ "'~Fj4 688Q+b1>^MQ;m𼍗ݵ뒩8Ɨtfڳ貓0̸*|pWY ;y$=KxӋ #L0a'޿zoTc_Җ伬%\L7wHU+5j@H1dϑ'O=R⭦޼#[~( %BD `;@]s-G4R=ԊZ2߳j d1ԩ^S&QSs݋/0Gc8M*4eIja@ U8 1pDO gA=ٸȹР$up>DB\Fzx$m: StU)<}Q0Ld1Foeo Nx/k[pCiɧewD nJމl _DOՙ@wthjvJL񲪲90ɻtunCqzN=DEp(ȏE:w9G'O5_x-8UqB0W7W(pI:kP4ť ӓM,$-\,_ɹ%pd=ރ] /sm(`:]n "#WN .6@]Wۦb]J.ܝk=Fm/q Z&7 Ka\wZYR\H(xQ>~o%`ۜ|(%{3њQ6t NsKĢ` ؆yqmL&=A MYNG2wDE{yԍ;)'GN6ӈְNdv"/USB:B9@q`jDq pf9nKq~XN^)z桞[ogE?' znE8hVnڰ=q5g"UpuF a"ǂe$&8iR "T)7&CDz-:ԝ c1'@ZAۏVa ᦥAMI:]4BbJB0"A3 ?Vڶ 2Z5Ηz]zi+y$MZpKV^&CUX+4|z8V +,P8^U *z7B\j X9-}D*1T@".,5 )zXC 6$1b5MR8xF3:Q5-w)4Ut⦭\ ׏'}jW>%0C ڠ`8ÊaQ Ķpd [>"X&P Wad!d$ɺXZ2A[l׍FFx8  {CWR9dcJtlxXp|2r>^/}be(cU:s8`6 T+1!\ɢMJyV/`"td7(oM,0p zUZ[ Lck!}gCf/7[:89?NԾ}X\"h3) Nyz"(2{q*Hcc#(p^=U+Q8dXD e=*Z֍ieXCMon2i?bQ!\T f!2RWs UV޳ JT+tڼh4͗_{X/ 1Y 9;9W3~"Icqe[І W~'St0Db1XɤRpiEY`ѰM6aW'l llCL_vILL"<&y m>= A.+WH 7 5\9T ,5 xt$]VWzt @嬜'BNrSmLnGhȻOǡe 2NFyLA I!/.0Z  gPT}B2U-$'&Fu߼;?w:]NVB5;|Ar%"W$ ċX3SP Q^S~hF<.ZԿuE/>lYخźiKSg)QTpgI>Y9DDI1+`XkqD6͕^e]Nt9K}9ګz%AŷˡfEW8C ΂^IZܮAVg6an$I}ٮQWd}ea sƴHcˏCNu&NBWĂΠ\SOmnAHJ5+~M^ 5@ŧ5me6a]\jI"ƣa$+G Aᅣ^i+Cd|ZK0%cCY-`PmHs; ăe5~qa5^;%yLr3頽X/* .?!Bئge̒hd,*bOVI HWH|e!g߲R&n&g瘒j*7ӹv YʏL vM]%S[LD>ODdz!ŦV jbDiMgXb}0F%9.hD'Iˑ HLkkLHGCU@oBSܦ?:%i72^6)-f^+3j/,\kJZ)i WGE&Wt{\qm?"hXȋoNԂBuuk}}Zx 9~ Q播f1]fW5YXF1{` q*CNb1`@25?q2YC |LWe5&Kh5cAZ-jFf M[#?jg)y=q'f7F=w٤ێRtdNWHR>i. ^ywVNv_6m&-9Am)on@MQAkS,l:"2A c!eKdZqJIx.xbPI~RұoҒ&`mU씘4}Ėf&Q-~{8(]) M>]{p&M羌2)W_A~8QMt5͋Ӄg/Zh B^Ov ,O-dwlyਫ਼sS@)AgWz 3AirE=R? #.3WZz}Y[yog/w "2[=koXXfqKd9>{T-rEvK F6(NÇzJgpc gsYuDݯ]~Y-lU`# _cyiڗ[22Ng  ;sT3[\`̱] ̝<>t {O8gf  ?h10Rߚvz8\&Hr@2/(V;+7Wn=VN^FY>5>CAW]ǽh(G\. c SFݳϕ>tDgNZ? ?MsTӕfݜ}.Kar % BnC6_BtJ;"ВL-)W #Vn>Xv,SۀN8WǷ~?Min4sDDfkmv1:c/d,י)atZNscvLqAzf}4Q6{if}rIۚ˞Yt>_~s!K?|+OȽi 5?3:sSXѲb|u#M ?sU + iM`d@G|N{yёݘաIbq7#E6wZ2Bkch`S'gs#p4+]`Fݟi&B+sv>Jw2L`vǿdbw.ǖvK]N}*~eٯ3C|Tb$Ri%֐5 dmWwKPYM][í/![Noaa$hŬk$bfg H(M hV68*mkKzu9g|@Od` !`.tp}<E6k-C>%|͘Æd6< ~z1/]ǞGm_w lmx'+IO_0§}s'Y7͔47w[`~Ϲnƻwn 9{ofif acUm&cd:?D3O+d*-ZCh4Bӿhu4M[(R6[A+fAb:Q7niRGL=#M! by:#fqIUgC@lWST,87u1 }vN5 x#w>!~d+? '{(l:Q્O+H #d$˲nq<$IB :Y?~N/q%,o'Eԥ?&!?OL QI +?i@ ]M⾜,">79'KI)*ߔN0d7U)8jLM|8eOyqR7Ȓɐ~u',T"(X#qֵ㠋9l j !>k~򠡵]iW^r~ywօiDWЁy_ȳsvClj 1Fk!}\? 4&u6 EW !"@94gҺP)ۘu8<v]+m~2GwCz6@%dŶ9a_RCgs\v6yY)agN" lI?r%0f+1V]؅ G&[J) 2f8TȫLl*Sn%L;%X?G[EFZqۓ]BGkd+,_l)$y ["㖊N Fm5FRZ Y%zД8VZB& d`JdϩbMX1@+:&A^,󫄭lHSmDWM@x91Ŀ('>!6WqoSݨ>e"3!8|lyPȺ4a?JƩuzp(n&ZRCr\zӸ1gwIQ$Aw pA a3d(fў.KLQDbEЄD~}_.tYZf_ eKޘ;_NΌBqJKQ, o)_I{{Buw. Fo0^+f_ki:[Wn'tD}ҨAHvd#G7BDijGbe03oM@#lOQ3wf`%- D9ab!Qt@i_m zMM6#H0S #"hcTHݭZ^Sl浦P)[AM'>;0C=gsY(mȆV$#na"PO[5]w ] XAMcxѷ<1X'1'Rbs'`71v9,,MMLdBdPl]c.T;0W?[;Nl^+.=S6xN2^Z1.5O7a=]GRJe$-;4T.#r_:~x_[$:tŘʃn؎lY}n І'9rSHezHɘsbz4[TFy`&1Hk_l@dLL84/QjJ^*}7&euF(0GeL}p';7E4p+N `a"} y 5F{S4m~mE2Q9 r+R!g )%/!W&>Ř ]1=Sl^)Q`I2EH.5/g 6tƣ~( ;c<}0o.4/p8|~Sv=9Z/ C=B&"AtsRڨaˇ[JEBȼ iR-pZ\[Z˴H" 5%"LYo2u0,YnxZNxtrfMwy|q2vH;tY4śMN>#ۍcEAGR7z2tƔ8δ.CaWZ0+/w>VsL؞!OnpeuH@Ca1t 3OC3 lFXg>D 9}g[a@¦lExQ=ȳ6< BLFӆA])u ֑Z0m$>TF;l d91t~g4hUIW%hN@:kTL &N榆1FK ; Qw %8y[Ô%K{ȚX}9ʱo|63K`C{)6>FqpP({>mrE1>7& =t^h^bJqM4Uٜfȴlq z"F#vomlΐPаH 1B@+nw(zY>%qJ%\"YU_Oיl@tӉ<7H!|'t"aER -28Sz[x6JiH5vgxkWy TQO6~TtK~ʈ ?BAH;!ή{i5e')ܢ-YuzǬWƝ1XWP f:F?~v!BمȅKAh1 pW1^ keJN5^2%-iHmM /)xetdIT)?< #9OiN!IIB;O-eeߓE|s-:/sz:e1C| @7? Lـt73W殐t3$>~ /xwt 6^9^>Or]?. ~=?G ,C CgNxw)|bo)΀uI|ϮYrj6j?@h}w 0><pVj #x?dg翿߄\O$?{8M͟7}_ޠݼx4Y>;],+=q~>u\oմT\@'U&B4b,y9W\Y{#yh5wdlLnڏ.M\x]BКzf0([Qs\!:nYZYţ9Zq: kffyYTSL-zw,FOeV!wޚ{Fkw}Q ) ty ~0H:+p* $1Ł:^s`FqH/̼nĠ6a[FDXoſnUt-_5o^<0=XS3PW@Pi ]`vo!:*?kīm$d1y?Ra@_@ެnX| JJn;aZ<7K؄Ε?,@WjZ*ϪFչx=RJl6 蟉u2CA=ĵNLoMi +/m55ؒ}J`rMrm>W6[u)aiPmj5y̳~D^i3|~$f"R*ĭQp|̀37 {"J7|co8Փ7o {c@[n lo _( ǞRNCn%?zxjNkjQ!kru [4z܏F <)}q6 ҋ$e̚FiUmk НQ1?a qX`lꔊoK^w ñ=y) D7* wExw]ȫb> 7EwvQۧ?'cQӳFE},'>Y z(,aϋFG.P,Wr#-gimDl:L>L6?9,1Q`ߗCP0%#ȼ ڍ.pOUS/Gpi>l``èng!WeTP] rA0\YxOF{.x'TS|Dh= qvtW,{ & {boo%N 9xiu){wr"'ͯd\\*ObHQ&{3E}|L;IVYJes<;ǚ&lIbCcǔnk֝>]|4"1ixUӈ>;{[[(Fga߯OvSi?hby6qn0z0E>Q'C4g qϚG/yUj~ F."0B8v~{yl(޲,'ӌ4.$j?[Ȣ)!.]V3j2ؿ/6嫦,K!la&مR[; OԲ',O`t,T&inPѶڨ4aCѭP74/(׀wqAdPb/vVնp)PJQ*A8֘|P[)ò$8ԷUE][o+= Kw bGm twǨz ׅc`t|MͿXPtO}"؇g,U+$-){'A8?ZK4P d`@g{jrv ;¡{QjWk<{YY0_4Zp:Zh.u* H<3la?u r(fdJA|'B6Z\N*#RsuI#"` Yu+AWxCtwCc@,e~3½utݎ\< f!`HFp .~98l8vO>=}6zlgFO>=}6zlgFO>== F0Ea7>Sy4[>Lz'5[\'}?V7d{i4-@2 ?c H mUM HonnonaY`>l?j=RZX}0OdZ([:~K̝L6da- YYa`?`K(KW"~pP:;g2(|Bf{Ae,-p#[pL꼗kҟwUQ, yZ꾋! ftq/J(8)} sNeYQRО:QNd<te|U"P00gz4~3{)[?M:8>8n(іJ +le:oX6idn4P0̀(h:lSPэ.檛IzÌLPo$1H#c@):$D'%} iDD76سyCA?%WjH}OaGc&U{jgJ.k%D/@)%߾&9ˮ\(y:ȬΘN{2RKQJ%Pީ,7:AXY刽nqJ1, LT"VI$)ZC@Dc/BP_hQ4]Fp& =6>QdߕRI _Vi]fXal4[sxxW$zFhLIi\ou *^C"ĜxjǂO]Ls (pWZ= H0z~&;xQԇ(IJ&B,zeROnvWd̈́p攳9jA#WH;8~tɄ HU i$Ӹ7) =eSSpױr"PccaqH19ԈFIjDC\gLˢV$_ Tc|~N㑖90w1֥^] ,0Fʂ/,:+@˰z[ambܨ-֛ADbs؂¥VټZ-7&~bYtBNS"=qdnVm=͔ý_3+kH`h^rZ&Sk^X졔c'Ȁ5JLѓ 1e T/N$cX*4mNU@fE`cɗN. iT/aR~".Yι=l`' EkPo6J>(H7vC&@g&u1?~AIy,JfwkxSPĕqOQFr*ժ̇m\o߽)Z|\vT(x[5L-bFsX % q%XąFe2[z7YR:oWiRrRayM"٥iLRni#"D3BHN|<^`]^]NbNPïH"h-~pJJC 8WK'Bm:/R-?+*npl#ZL @ĔQNy-=LlSLaq gÅ@qH6Z@O%bKq#p PQ8 C%9}$.44̓ª?ui9^-ӿ̠#D<0^j,GD &y ]n`U7 Oo뗋 ͛\\_BŴ|izOOUl6;u| wA)@jh_4Wp : z_Bx5 Zg: x.c6]C*8˗Q嫵 1eVcb,\>㜟5kZ} sC%ź< K\8ֶ2Q9>0; lnShR\ZJ:\[^% O )béȀ_EL:5e4`"ܦY|k"] SS=^PIQZ;`cͭ '`7OP<@)g8%,P#Jv}CyIR cJH\/DFkS$9n{=+gПm%/hkBp~\{pq|^xqf@O/^M{G~-wepc|ޘlv[l7'4qpc^@?gG 40wvALQQn:r+ꌢȈXQSEZ8)nƉ6jÈ̍56zh0q@gƍJU%t9 wK$&ȗĒTAiӖh2w)?W4Fn8B;1O]M1hc P f%a?rlǂg;G[5IZ+5O+ӑJ/oV\BwqӋr %J {X%FarkW*>h2󵤃n<"Lp*,(WP I5ixqH`TO\uc8sK-Ls FjodgQ٣c/[ .T5) O$i|XV\DOkm`ʄRjo#I.6nh0쟣h/F)kmln=~t?ãg??j#E;yӠ9:xd`t` [ʒ46 EetޅsI̮^56ZWሜ9A(U a\i?0'T| [*5TjP x!,;+Uc s'{Jbۯ.Nwaə(0tB8Iѩ} E+Z:*'Z,T ]Es^o|]5HOG|W&Z>8if7pw-[,֡D~+PM2B(lɇ~Zn]q:[ejERխR2j@4 `TV.FDEB=Pl\a9Jh72C`<17F[_.hX6$QQ|At}Le">FePbpL2JKz6Zlȉ" Oޒv:Nb 8iSaəM \8‹Ĺ }&)-4s%/FfÆbWD{`^]ԥ'9S.iL&^+O6ퟣYƬR24Fr-@J~Hwzbʕ#hq~$.#װO\[U͖Wl:`4gupyG:V(Q-)BNotdHqj#@ '+u+— :RV|rpS+ kZI0CH?fJۘI{֘8-=g#puaxsٻ}Ҽ=k\PˀÞ, 14U>X5hjա, 8s$Kt7.wfTK[;-h]:}Zn1PնB1ic\(.F?kcɍ=ϱide$怦ւ9BuRIDN~ᇗիb{mڣ)k ܃͡M2Ш HRM62[k^=$Xe_//^BTxߑ3~w8Ef4_}13 Jk(?:\`9JbٽI C4jFu62;J!؀KnL^LcD;x{gHEvl#d y]S&Q3*U"#LI2׾u ]!Kg2=$m&υd*HyFIl W ?eZWH!*Xc$r#"3-/,>o'A!MyhE8p @Q<߮kOI~yJUkAQL7'+E(NEFp [_%~RW%+ WCL ͺ1c&rJ\q|mMiB8`ׯPTi߰1UO<3QoG)9>T;Xtк2%⮇pbm|?2לaGJxư:b \[(l}Q~]ȝ:p+ -o@T;x~Yibr_ 0/Mj^=@  c׍}4OA3XgfqyI?_,lHAH#yi0 loA)^ԫO\j.i)+uQqVUߒ7/fO?߰0_s7OARQѻy{.XrѮrCI7^-T<ǿQfb98%YD5UL9U(X@rD#wг]4'N n !7 bsZY}53VjZ+~1{q@tÀEfTq I]MwJy,HIU 0SX3-"Gx2Tǽ`/=Chbs,v̽XKz~>?Ы u5?__]J;:l㡬(@^ӾCq0H6? $ԛ;\(n#3^95:р9r'pv.hN[=bD@[Z,o#Npl;56Q)D;:Y+l~;=jθ3sbݹANl;9Q)hNأ;W/Ӵ7Nfd\S&tCiPO ɔ˂n@Vr }mkʇrk? nJW+g g N߯b[|> BNހ3rXgaЀǵ(QBZ,Y#fKGUa[dE7l8gkJa >4'fI` q #VH= h A$Y$zQDſC'W~MC`hfm/n6j)PnMLi7B;6J}ZWZg\&ǔRFUl<)(X ͊l)В)510.7$߄h(AŰ"NjĢ**ހbI/=Cj:E1P*vu+pظ(A*ȑ2wP 310Nv^Qgcp{iQ~? IzS Gg%S+Ղ(nIwmfJxop(IqZqޑx(/$.`u Jjt{KjYja.0w![q07P20:YZI5 ^׎$'qԳ>]hC8 +7*n*K]rγf?0&JgtSG ;Ƣ ʻtt#2nnBg;xA~#X6q^Z2*sZl`u^ŽֽyT3FKbpdڿ/Ď,+eSb1G_ԝ_Ž[n'[dX2~1y1YR|lIy1Yp{-ȿ'=zp`5li'=7-?NeBԄz"S!~Ƨ82hNtF/a^7:6w Pq3Ƥ hy?m)OEt;n3ׂ%4=/^ȃ֛ۢvxM>ivL"ɓmw-O7lc5۫^ ;Jp⟜דvg?wO~&N2ms^8E ->׃˸oѣ 6upӦ|#ONmZ؇~r}`sq/@:D/A;=;&G#8-G;GO5%G~.nZy_K[ {E~kVÿw3sDg/^z0By޼xp$Opụj5L:!bE}PPFR L7 +{Ū?4CtF6}nj`yR-*N2$93AK_q_.U}XuU_JU=+nfsf?U*91Bt":B:+KkAlMS a_V/%J&NXfu^kfZO+?Uս)X#Շ% R^9#x_6qaetu֬)ZYO<[yrKd?Sn1yG,Grjzͽ"8}Qy;DCo#K~X{Lc`iZ&[Uaf/Έq4D{N1ӒnZyk[MH7H,e^M6!q =\kcwly~j=[wO4_} DDMy?q~Ea,ww|Zs9η=L|9vmuMȩkLO@%/& &(K/9``3_.]5~lIԍCJ%" ^DRH;e$o;R2?~d#7oQ%X*;6P"nz,tџ d@Jҕ (~\R +~^= @l_"\QM•F7^X t#w#)8R=o Χ!S|uiguZwz;PNPXÌ`b 8ͻҬb5?MY'Vnc/ac7f;t8'|]3uSNjquLWw0}o& :]4peYFuj_/n=fnl3W1Օcvi yxKL/+,KCJ퀍9dڠw&glƟd.YZ^FxdThqV|8̄18h-/[Ia J9"Bz>Q5]w 5m-x*ZC-NVK5(G茯317qO6. aH9^0TCdp\qylr[LB Ҷ'B7hacFhHgP&U'6x. q-U$ 5NS`Q2XHarp0SR c,=sl: ܝ(JtNDT!Zz*us0H5^-}E]%hPWU~b@0X%l:B).=! aWVzP~}h)P* Tg"F kEB,,Sca[lbv3wn3gn$ָ-S _p44 578$z'儫rOYi͕t4 gƧc,:jI8 x'7-Mrr%o<@8GɜN2uTFXXQ)2yݏW!$L@?J(Z!W-gզU;c8y6FC}3FE̪yA@ Dr ^|e`V:>|tDf'SZ{S)o5[J+6kR)qu`=@zfEYI5143)Y+Cki/BKRׯ f>jW![?חcxtdK6PZȀk@N]&H,EW3o~+);Eab<FjbZ#h^CY6YR^s2w$_w"՝A+f2}zZ 35ĶkoC4&-o^y:1}vmپ'::L8cHZC)zbwkUD_BX,PE: :J% ڍ5yFQ!ʴu0oXX g1%>TS^NcR[L7{|ԽIt+ݚt;FCsGND N陙ZKNJ8,ۗ,skXz=]/(kky3w__ n{x44DzQ uR+QzvfTpr$T<0ΙȝeaıbK]~uMP_EWGU`i3BX̛/F.GRb9׬)*2r*擽3uVw?dk/⾙p;KQ>s҂9gYkO -찘&%L[ 39jDeAwUD}a4(QtE{LFd|~5HQu0LQqΉw;HPb pNvPX'r$Ÿԫj}[\P&Bb d³S}X ")2!jJ:D  jl~C6KJ:ixI}2!dlB,|io8SƮI`Oq1BkO[PQ$n 2#AQ:!A&UW@/vKEH8נ~VNi~ۜV!/i3kOPޗN0ag^or{kx9g":?(aRR_KK'Dq,u`:y$9`.cvz<J0B&tL$&vgReO5 4| 뿺6՝aGm\jԁ54CE.R8>~xFNZhc83YU"|hnrM}9Sw^pt坟t3p 6li^uƽ5ԅR|P\6 E߂ف^rJnXVk Kj IKuӾ12,ta<6.hŚcLiCi!+%3Pq66 }Xu#Άq0Rk7h`y $Ѐ⟢=#38݈I514!4 vR8mGbFNX-4po0qlmW9:Ph%QJb>?ZHvң$Ĩ,2IW:2IdXJ<žreC%IJP jcqwxD*؄ڑUS*A:tA}Q|}1S>a7.g)fب/)CJHQ/ͦazfLuM*摴|!sLXRI4 YIp<\FӲTuW Dr_?x@!]ZKKWCտBd?Q{kU/te!BxQvTdݨK #42H{hM1eM=HT%iI[g81 N$<å]x\"dHNt#dRN(|# {V3`廙i7n1- ߤ,_vڛeM&=ъK͟B8I]Vj~~eh 8fvӄǑ'0q{/?t|q]`GǧO|U ~@[럔N&):vDGX5ííY@2Ƅ LVts.8LF6n(a0&?I4:Zbj"#X@";Ѹ~W2 EkspU1?imlG6N㗭>J#(M$@PYIѓ-܌ <7H.4R2$m3}daۘnr}ga"zOEP+`B"For&zYR)̑tIb,;M)Mq O8]1N0pHrK0pԧ[?TAXѨv*zu0hAV$go-mRxE"ǿlwu;kej`&vv7<LB>,Gz7XNm2Qc2Ql: Nj 44l[1 T~h4 \7L~*T77[=e 8zxabem2 ׻R `) :h q߶J^\MӋ5f C%kV3 ]ey]ˀ^ػ`l)+/i̇MK~%b['ycd>$"2tBu6%P.Nu;%'Ҙ&ӿLt?xadxk_f4hrn_g8|[^zC|QgefY:xhg{ ޷86+c䗿vЉz|[O%>~$P[p̯Уk*I(ԓ#/0~1P":I96#jX!Z@SFFZJEh_'pF /k(.0QI? z9 4 tT" u({9@֐uVW-cVԙh5]c*`yMrd8 ,H/aJ1eL- 9W ;BFOX/"br -f>QjKtq5grJ;1L%l/  hcM&3G<⽰CZkȄà)UrV-aV4ߜ\9b*y0E_C[ZxpHm*#cdUWc=OTpws:/XC* 8b L3h ĥB"PXstrIk!mQp0|B:x|#XJ@4>] p⸤ 4ӐO3[ zs~;p7OZ wU>q*NhVBeeٴ۟۲$Cr$H`M ~wA&(h,@ p69.SPR1C"KHg|zgTr9I pwPfL $HXM;$uR k FN BTб-9 #5uR%pur`8?);F݈jAq"D2RSB\0Ϝks+@6G b/1DfBW5&b0 դR1.qd39^c , 8]<鴶I tK Ia܃UAM  2p1!s~#K>6`""-yV# 1a.1Cī;Npt =1w-OBȡҽҜ& rV,}G-1`1ӽNEZp}D(>H&pns@^1,X"*. ˖ 0->Hx ȱЈ]B,Iq5 %ǥj<ӉqlfB-4,am%SZ4ihɐc: )ec GdwrVpRBwu%UWx1 M/!d˚t7Kouѕ75a8y( 2y +?;K ޒWyHfBmY-frr*c61J}Ah#-EU@v014hV^(I!Qi#&gSzoēA1.Q Ͳj;63|:JD0tBT2@Cim(F"ޥd/vN J-Xl!1yEpkҍ&2t[8^FE6$h.+!lP ȊO0e2BȑWtd QT PnM.]`"eՂ3q nb; T2re32FԶ,8#a)QĔM;/=oZ^פ @.C1;W"XH-37'BeHj}Ό]"HDžT|xk<y4CԺmgqNp} E@ mYQ.&)0@OofZt GN Vu%~8]BrBI\gԂ:X @ma|An PT $VRHjQf>Mn1Ja? \a9Ǣ1g!goLcT49ZX 5ݸC*B/Y JzR(`ѣhKO'IW!Fr-FzzϜaFb-V )4G&hor!Z30::M .~]h;2faC:޹&`Z9M#l(&GT">ecP4JA7\ِti,1 89ng(N!׬#yG`g "`*d/pm5(B]oGD%}{pgB )".R %]eDT_q/%4Z; %>ErU13nADn0X vf,3SX֬#)EzF:m':웏4Kc`&O,ipCiQaZb&+FX|O,N q]KB ҚQ%#z'te)ZcŤ|aEF2H6?1&dD|DgN +^(Z8] x pMV|I#2iZVNQ ƊS|8#[)Škrtі%BHWc NQ@}aiн):F/p..`DlRapWX'"@`Q8(0vSb 8"I ;Cs1D&\q5j+/J*[F&\{1%tHqAn;=C2[b`!Нh86x68.%9k U7a҈u NnC5oLҜ33e' FAܴD^ؙBrqtek +AĴP*j+?؎rabVp%ٵaI4 o?< 6]ZiQ۠:h cZNpfH#@lɿpB[#]4w @B!K15⦁ ߱cA$馳7|c$Y!((+pKI/1<;zmC\# j]M"zhK?RfUz|ᡂ: @}xkXlnCk~Q-Vsztk/ox/zSm{OA4=~X=A|4­CT&q7I֏`I'ќ~۬mjh?wa[q{oc&?a`f0}Z]Vl'C'ZBhp'~ϳu.K!m`._. 6omo %g[GǴ@;;jokƻ~::y{!8:X߆GCleqJ 7dgg{;8íyɁlc @.mw(`va_<`rۇ q xiX0\ܢ'[Gu-ut@zWN<]҈Zĩ!ʖDX0}g;;GlG[Xpk֋ƳC8ZXkhaޣM pt7yuV׷wN7I5!Ȏx*Sz [h o ,m˚K Gs *<#5Q`t<( ؇AW"\#1 nsCk #U&};8,5<ԊƏڭ$ąni@PS>+{'} 0mo+Ygyvx ]t/.ݼF0!@ѣ ^jXd{2k=P IeG![sk܁pzliͷ~oEN>?#ŊBl8Ԉ(@郁K#;4db9=(%d #xMb^ÂEZELNt:Yk<ŇD&KyLE0sr ߻1FڽhXV= Rd໬=;эΊNh#> (SSъbЅKlLB%N:HqOMGld@'$nЉ%T ƔzS%v*hH :'F= ۯ!ؐ]J%+jhaܥ$H Hc3@u]#ep Kq558tQQ*aHg3"@[&}H#l8GG@"$F HT;uw39.X?u?7<OJ{~E0dUU\{hLjCcFNTe~ 8^zrbQfE} 4Zӿn|Vg0:][<9\G4$?3'e?cUHELKh ܿ1Pedd #C3Zk>kr{>^ki{VHgwqIl+:Cv[ݐ婏9Yz>6dZwhʆWAFKӭw+8 MKϏv6({h&.lThr9niU%G7.͓⒪V/da+k+Bzn"w9ѿyҞyjPK>)~ed/ q_C!#fg5ŝLCA&ƗtjA=Jfm|NX{PqG =Mp_NS)>?sRqf+I&a%%r܊xrfZbQo`QM8бM˩BvY 9w8 ,\Gia#4zN2ࡪpP$ RoH1j0*zh!_CsSFMЈoys3/y &زYCFE7 TiϮ4'RǀϩZR +] OAR dGŷTչPe “x7/m67[*Tf-i֚ /<721̈́YEXǐʚݲ<vb`r^==A(*=oq]@1N^襣 ~`I)$i<]z!#qB8Á {yoRn*q)!WL -{ӎ-P3'-t zw G NDT} [8>;CqQ.,cTX?fQLf|o";L#R]N?7~16qiɩDxeexMq9~ *Z_\zb̕pþ؀-6 PtVhUmRP-eӂOeThQ :T,S D$%A)ƴC8j-<ۅ~J'a0+Otx@w###xy(Zཌྷ#} MN "C`F!-vw9PM\NFu]>PԘS~ %J$֥Ձwt 갗M6+ȟ*ƟֺS̻ժj6  hcsigwo/;E^?6sw9 hT#rf}~t+"?v/~Hޕ\u⪪zI5% T*ҐKVuwC7y}8+5Aƶ'}Y;nj6KoJċЭc )Oc>D|MU|1lRFdU{q]'\ᦜ9`QG{DbEyI]ә hL)xTUIh ti9DiU /ܚGT)E6@s 񶬖Po#1=."kA\p,dU\x#7, \\Y"rD,T$,BFۜ@GC]G"~Ao8S:_ ɤ:~2+t7&&Q,}6ύ][*߮ N5D-+f}Jah =I+8gJG“M4Y.i&C-ua-E^$vjݤ8M9~5!IWkGjaq#k.>$c}o }V\q.hStpehnȉՒ=#>\5q&4HoeDYG`'[}%a:s 4&DF\YnIV n6.T}ޮ!{d/ۑwLLHSYW*4sA'n))ȹ N% {.Ğin$H$llN B$KQrlZuQZ1l0ɠG?ŰX4s4߅cER (',hz ")y-4L)) xf1767%&et1#t!3ߣa?ak崘"=Ū3e_"fi}„iթ j59i6~Vg s,ρ[ ӄ?+Y" ϯk }6CLh+>#M''R"I+k^#zMI9ώ7=~P >>M(3=a8&`u.,ɉFW %<%K1:|YͫuȦuS-W S ~p,C,\~{>`؏.ˈYy#ᇉL!0K~)S4ڏ_IWa†"ԣW֗_RcX"ts9PCiֺ8;~tn ,4/ GR3U_zHNa@ \8Aj+BySىdF. Z+ҭ3IdY)T ^Bo;\Uۮ~@SHvL ˚ EHi/e-h!T¦;j[(v4dUȓC6:lXDX>h/E4)Yd%bǁ'uI6n+(onh=YHZC唠y;ݸ} ޱk:˘T_&QZC݉E|#W|BxNإ[|QN:D!1=C7x%դ<џ)[4Rg} EBuփ̻"iLdҒZ yXbW"+:/W+Z4 d^#RF֬Gˋ6B0B?'% $l8Rtw.)Cn__*@@WH \}% Rjap+d8'_(sM*3ҕ }gfƏ]4Z%aj;/GzLր{ '4arJs0|f兲 Wu38fGkRT~"vn<0'7o۳ѝtJ&iV&{h3X<6Cd @5#Q]ɠ;̜\Lft|v_5 8iScDH$A=Lŷ?miPN\NͤϲY?~8B.WV dљpv\ "397"ǘWr%`}yr+gQЪVͅb]A%fWJl-l>A(S:Q,S3Ұ|Z=Y&&aZM gOzB>6>ÿ`UM`Xˇ=$ n'Au\N p>&#KP_4si"ΙC6n&‡w밚ч(ݍ k1vxXwEqbǖ`^$%g4!50k[e{3x^TcjnEoZ;EV%:hUr*aD>`-*Y*Z ebJ@@"݂{shcg}{wFK҃+g?6SGǘ\܈_MW'P؝.JwPQ %kNbCĸ\ώ:]j#&r z'cB0t r''pmw>n{+3;Dz='8w}w~|[؏i#"@co㦭ZV#c kgmb<e;OTUmm:#]4AW[ڃe#O^%b.~~|(NEwvO++3gg1- δLL3逓$k'i 6i0<[~Bgh!T/%mv{A# OH SIḯj$A@mMkvT 0P'DEMI.#"Q,nXm֐b=RyTìTϤ-d Z2jHq?Ie$uL~5QJq4gcI2<ň6ņkA79/C7roa\h_.dNX2=,) Nop#iFD}qZO$lx) IW7Jn/ HFatsMj[&H3ɐa#dÔ(00jtBKda;Dnd1k))LS߀(q2`"heS:bN9QW!Xɍ$@"SP?^cu^v%7qa^{ V9()sN"8V5a<ݰOx%Ʃ8SY3F4h0&(&=6PxFxE4( Ab&ajaD?Wq@)6 %)h@֘$87:CA XҢ{9Jqt5fhQt1^ :R%l1<՝rxoR 14Q'0+FSEY~Y"3By#an3̝pbDaaynmlKvu(:Ĉ"FcmmV̈ p+= |NI)E9u^_څv#P27FK^BW$)ǚm;M8y=S@hP̰/ots6G cnA81kLp Ji|ix~nC:j9gҤvܰS䂎܆eos:ҡE /Zƃ \\ 9lo-.ґٖ ' 14٠Kb,tItMrW5g9ۡ> M M yi?i8伹b`PwunFY2㧉RtSA1jg3alܼKBH@ H`~pRzMA04y qƁ7Ѩ]$pKa["*p nxGnA[Ui!E*kH+YTmxѰE ,L`r'+)7~ ~SZLظH#w%zu2#jWB)S8%gOX[bV߶ wkT -w1VO):[tK29םM_ &C51ppC4xE -Wц4%&ڠw{ R7dgÙ/P5cl3z+Cb ީ+F[_(GGc<ԕLku,taTo?;*k tE.)91 '+1ґ=F&7\OOt)?:9akuUR B~cγ$y`! ɱ !idTzH|T|`ʮ,gHp kLFs6tnwz:0t66i6;gaGEU#B1q{-Y$,YJ CXES#œLo^FBqWZ-$2[ޕNn72.`4VqsɳBLi*h Q%8"|I#y#n c}h a㋗8Ɵc78T%r36U7w~rt| h*͟I l"mvG|ƓӒeuNwt8RnS<,91\rpr-"2- h KZ#b0j[3O|1 VYLm.LJAHK2D:cMj.2MqV`m:M(83]9s;'$L9ע (/ɜaӖH,HVe i^.Rp{2oIR ɊXGq) 5ګM<]QFa=lNV7$mof'LDAvEZgT . Jvh'\3F{^sU [_&nb4XGC<>7=H`qcU`OwNpְ_&I!3LGJibZxR(  @1ayRi#YCJkpQ:QnfEO>ْ[}>ZE*et*eb+W CO *E,(1IǬ+i"ASKUQX wPt578FXbzML8M%-Q wѐ8(GH6^U .8Y;d](l`F;N)%mڑ:;wNƎSAMK,O*5Z"yGg!Iژ+;ҫ2D[)xoi'jz--g?'MUD',9WԖ5 N6ˇB/ǀB5i૥K / b BD6Z҄ 74`S;HD䩝J@a>&qX3D<>G lW=ڂ?rZiEm?!}wHxH-z3Z>>ᱩ|hpCPcz{[Ul (mmn@{G؀[wsy" O܆b2kxpъ@,S y ~gPg"ƐQKJNîht)BUNj̛@]ݱvGZ%q挰rXlD7ZjFNMFŊn,΀|DSd dyűD5Kqm@ñ1!9YBG }Q۸di'&Sw,v7&6MrD)*\ZzeeQ606틚1p%>]~ty@mrxH&RjZ0¸#}7 )_:r24$jUr<ǙT,&IW'GB2J4MhqR$&ɘ )J4D#5tzopnI t=pKOw7v3C'2^>ngz =p> { 3W9k\-Zׁf Ie-~uFOn1ۍh~~bmB #(H>z~Y=~7<:ƿm%ÉRa[R:E܊ӊwLm.l3Yf.`kJCv0 7#2uV|Qm'xS`?Eh^nUst d@1#KA'gDrc)(=w(`RP<5)-4؉c9Z=6 9.PlhB#gYyi-Ut: -_`nW~6YNadٷ)N eЉbaWe0GP@% ^Upx:Zj셊RDnqWD1 Ǒ)xK2Yq0[M*9R`Ra9{MA? # FŕlteiV@nVz)1:e9TO*QO˖E.2n `N73YIG v1{CM E^' {̢Ϋ}Mr] >MELUQL8Ƙ$^‘BURk?HهrIՋ6Gk2P#ЪPH,AܼN8o?8/7[ PZ[DAv 49a$6 _^ kC6TQ>`VVCݱ\ Q!Ν<&h 6MI3I1:#AT[A& i(A <^ɉC챪jNjk#EC-Jvr<㑎yLPI ?A8Qب7( LboeN9zc_Ǒ`{`SgҢQkvA=:]Gy+ia1uՁX?s smx,HvAM˒@5B= )wTgX֒HJI+:Y{qkx}$cdhS PxȽs!Ԫk<,~pD89EdrOj8ꍻ${M.qЮ]ZZt$_Z*.d*56U *Nk1+");V`v*n8{f;0Z%NXFoOh T ~.?:1\R˩:z6|ʹ_;J9kIZ?K~K:KRшp\b%μo}i1zkN,3b0PaiklS PD`NJDU$lZV+jZ~hŘMZކEY\pZFH;/-U)i`q ߐ8j&J^ԄcYOE~.#m/U;uD4Zn=sU T-ilQY% axK[Gp۩ XK.b'6D};ʚi$,+i/6-1|n lPj8-+Cng8s 2Y;#PҊv\>:'( ݎIt6I##N N e(5+=鿖YhOV=.նn_?*htrrjsIu&DR(t"#[IPC56cKi`7.j鸍d!y3i,6#}~ 4}<l E l9Ř}Y;wd['qȡ{C9o P]CžiƫE(aF8a~)4ΕN^1Y^}-tʝ-Ζw67H՟[uG~_/_kzs?OMWU]Amd/~U6dh 6khkUmU N,A9`g܇GySbާпReJAT86DI2صpwƵ0JQg=Gf"u>A`sCwƝ; % ?.5,q84l'4KWձg$\.SCNʳ.il[؂*b]ţMU{q`ޕG?&>qbulc@c>^yvX8\m;/G ~:ӾوydINhB.|^k_ .IzO$ 0#̹uTZ:UCIKx%bs5Qxtkg; .hcN7C'f\nkqP\ N('T4H-ʱOp9Jg( wN:yi."K%ZjD4.jD{pQ7tz'Pgap"<H:]R5$B| R̿`݅d%JP4Z-Fd?X1Ѷt"7(2(n>G4z/2{BԽd*0UPdL@6~i#g΋]e4CCIqTɌT C#ː⿵D!scB&6la[|Css<"#-_Z z螚xn감ZM!J vd4lˤ|I<Λ8~1[X|||B||׼i5k~YjܫśE_S+lQhRJf6(& E8{(-ѝWaDt4g/#k@ (dĶ E^@Ԥ.*,*'u"q"&(^fdk·2X,PX -T ŮH٘FX*-55#RKXКA"; [h +`jԣ'{xG?Q]nU/w{eh E&ᒋ3ġX1-髤~JKzEۜQlPўs9Ħ#"^+6a҆|ѥ<WAM99vp8gtcjYA9$9U߄mȻj:3'Ln'`FwڍFo/mG7VNA`K6ﮨ]0ݟ*x&sb]eIy^E[k.O,5TJ_ՄJ,hRsfʚL^Y˝,98#f 7%:t" C~Kӕ&Q"T8L66Z 3H%D9*,, Q\?TWn7JNbͩ-4n&x8d&7 :n_V۷uxnSGjv5yCZ8M]KQ]?JH \!יcͤ `iPoJ@d~&p!v{7TE3$$[aTeM~UG~"; nMHےH;f*{P2mWq0FQN`eY_]@r2 BzʅTǾ@/0̳qR'-,+{'v}Gܣ̾;|W7ݩ}àӖYv!H9dd &{=R Ж{0pc QH6>[:~@T-rQ"xG|j^[]T0{F O7$JqC '4y|\KL1!*h^ix-7gH*ӫe[2]UQXN+ @jxU,RUe;4u|8} 30L}Q̫Bٚ.ľ:OFr0=ղ(Ä:(F݋vgpLi.`9q5/퍺" mg N&mLI-x8D2 .nՂP_zr&ց&i O~57%|+T"1e^4s(7F>2]'+F Tvi. j=hZJ9'@D$q{~ewCaH|D9:[,^&!'ȁfgO0d _d΃ xt͜c!-YKٜ1OpD @} pЯq&u4!mq|;X-DXk68kPs.ڐb]4[mwBsuS~,2یR*m_SNi5=rvq)ذc#d'z>3;%T`.%*E(c 00 Kg"H$yv+!-In? v|$oQ;Q{)>sMU &O*ȻUgۤXmW2:7:eHْN;WQT6 S:K~ydb-Vo]0r0IL@6U0_m (0.GF 6ntmFoz3Tִ2kYA@X*Pe'g*б7CN-9nmDn;!FEV=bn2[gBma?֮hyYrm)SevQ=6,̰˜ iyD}֮۟lExb?>8I\ ~Xly 9E::q-uȡRٺV(q_⋃.K;|L'Uu%(ZRk+4c52LoQK?r3*RD^xeዋNGVP,m=#bNm`cǔ]:rqNP$>fykvpaJůGUο \uۆ00|oȇFQ6ި64i¤ISSU Lh8NSo`2%M{p{UO ÞWh/V-Y2O3 }T1 $o:&x2[i'Et@mJ7 I]P˄;x1D3m1{S ۳&6=[n'GH&#Fw9tt7dJ4V5s{D^ޏzc@ TH>q9L ^{.z—j D_JTrN¶옥Sjk0eH_wme<74HI'Wǧ(/$9֬ wҾs-Dq8 D4aeu D @ 瑜-$p4o_91`ir/N4VCѶ nE"W-(P4J'QQac#D;a8Dβ[vs:Z$V2)y}^] 2˓Ϟ U+zr A! 余g GpRJ[+a|DYW.=T< ֏t%`qNjw1b+0(ۺTH39\S`E}vB[ui6ή}7{hYnx^XH" m&-tX͍bdǒڻ洝Js tGOBgP0\й5OGwF.'ѿi,"̸SY=e_ݣ#3f CF\0 ѸD|zt( U͂_vdSP;ΞUfX$8o:=Tw|v_E:ضV͓ S ,P\ @KEߺL_!+(lo<>ziVja',Kx4 OS}jߧgާ=W=<:u۾c6W| ̗yue_]g.O=yY | Xѐ_ZZ'M&eu{E NQ8Ƀ&Цyק?F½ٳ sPIZk}"r.wP eR1ud4,ǧ 9o>$|%G<%3ϫc!kf0>!cW7wk}C\(ߜ_*0ɱ24Hhh ,|@gF],b&뜼p"1]CW4pVI/ZU=vqSo;l\Md.9<3O=:͑yyn{OQ7q|Te&G#iUe_ ~F]!Oh3+`0az]צ(}^Y0;j2%J\uAY!,Y.ƾhӛoB+1.mj/apy&qXSvwt wĀjp /$Bk-Mr!ę0б$:rԙ5b paML<|;`YBD@ld;$L2U+ڭ›wzP~5k/ v4[_ {j{B)Džѕ>3˙[Fp{agٸebaL2LXl/ҙG8K[20 M4rҙK*W;ȲӼة<9Qiuw"Vz:7rx|CG !>!3*"h$ld|y4u{{Hx7bߍ/XGFexFs$'g;g,b'˴Ffdfv-Eb(&WE)PԚu)ݺmNl/ 80 ՜ >@ ew#CH{ E'J3q7"N|.ʏg$Pň~!4V_߸N dEMjݦ^:HM 9^"@4{('+~38 \_]f@lLO:L(:K&4x94\O}r(3aQ8\cB̒M!6l?bNtъ3{7ڝWGEG(Gh߉5a3Ys.Ov?_6WG]wo;Z ==flZyʽ]j[bs m8?YRh#&b9 Xބw9鄬ؼ#|>̰υ ?buj5Kh0ᳫe2! ͉?ӀBM%r6 \HRk3Ay +Y /AzI% 4ZphP8;6H+E8ڬ2k{d<\0RTݭUhj4X4,`zNks;L[Z!HSgr4}\3}gXLGW0gpnnE}lA3kkּdN$;{!nEsѰ9+e#/"[oAvMܥ̭%T T|vlr`t7f(tʮ09R${6:'lmiQ)Ƀ=ƹJe0Xp _v/}$d5^oOT7fA-χ~rƤfI΋(`=Jz-Pr5pǾïvpG(˩v(4G<-bRGyx"e7{x&v~1n~\\\4 &As-mO#VLh ͋47 P]zgXq}hWfcKpPSj/n$ᛜ;M?Bސ:J2nɲtҙ%)<).tM(wgt`Jܠ w$Bk 9s$'וUEgx=ϲǾ)*!ƏrLfMfFI}+ʒ}$ՑDzrE)l.vf9ǿ2y㗼%w*S_RW%-3' iQ肀<1!ksQKwh0`"s QnG."*Xl9pC23hAbu) Nv~3Y@.{G$3r{V |~uh+cۑ&i4&ghpS= \yg^lve=9J3,!22HaH6RRZ >*m6A?0is|5(Y"V/{Cx:,c3̝"ap vy綠LvV6j]ĦYYf 9LC[\&n9&Kh;_^wD2Ʒ幭kA:Gq S$HeF֘?xm6ZI 4KS0zQ? =RGX4E o*Gg4V\Qq™y,-u"(?D\.G/0m".3t׉wbQy*tbXՁ Қqkp! \br_ZER/7}"kPBrc $V]V K9FRDe0[ڽcZЀz: Ҧًx,yXG8'pI~)Ck:̌\xf>Kz77_kd]_5(!eMwe$ )qVI>VvTFv'^ u l[s&6;VcHJG(GKuXDPkW&:Z^ƟY_YEPnsVW6V%aLbn!&oFs-vqëT>ꉟ,H] tbHn$_s=:Ji5)>x&W#Kf>s%еQq/.xMIZo%iQ'쭎sK$Rkߔi-R)t S*uטCX!yzKB ׭ V$hcN`m ȐfKY`kw ;#h-NMg>$yRăMNy_׭"NalX@k£jmd@=6ۃf!ρ{!rrE=U[~1ԏvڛ1lֳAh _rDx4p_F*O[l0DS 䱫)wu赟5xe'PzGG{ߟ|IgOGU$PٌG;B~X?-U HG9gֈzg=6(E`P?SU г4eړO $<}Av't;7v'WDTWDeR&w<@ Ohxg;>EPUG*6F$`Wrݴ1jpXjǩA*R2ۨ ;;߇Jxɩ2gb\'#xq"%,GVeQOj :w\&8T>b'~/~7i \6Sd)~ ݺ{Jb)VڽowDc~k!KЦ L"k~M>Ɋ>bXrL|W̻ s]ն#`wo}9Bofߦ6ۚ?xDS̐V^]t`IKRz!nu2" VZ:$RR.⎺s!D壨J1,7hHtʄ#LZ&WǾ(HUKZZ(Iۏ#Y"*ْ}͖hh$qAx?a<ŶQZ]{z%˥jgݿjfJ\dC[Vr!1JxlxG=etxSCJO_Uk~A,ʠ)FihS?7+l,{wQ֘Cr"o4]Enw0}M]N' 37zU%hlx5+jݳzExfzg7ejZ[}xKq8@f҅Kts׭NKH!QTa N= B 4lQKwrmEVqdhqFNe7 l JI`w3E/f}sdkq/ښtyfbT9Lmc3EʚB>Z1z%2{[ebz_!rn\mC1s$r8ħOLBc^CFr V'N[^:'H8}d|Kf1%q\RqYS%ZP%%>guS ݻJ(ʮ/Ȳ% b<-_*RwT?q$:K5H+g#k%vwWzIJjpլg:'j>$ހ,CE&7) meՍ6P9Safژ\Įa)n0j̉ ҽ3;$4VYh ?mBɹ{zDj!ORa0|S1v gFM-7ڋgU$}w*03>\) SH'EވѥY t)[gc_7H1_]|A+*V *Qjۯ嚦IlsusEgunv/M z+Jeގep& +"?'`l 9K/ |R`FTj.pbEMPlD YٸxWXR_~:{)}i`a#r̯S@51[^d _j=q.ܕZܙK ~H011E/M"}Ebb4рJmb@|pY-R7Ga0sz^"% |Ak+5+Q,*˻@:RIoPZ4n3bLsS 퐳y]·6c7!k?`ގdwHhn0=BS̐)b"Ȇ#p zI[0cE|%툤Z,SQhsq,KXQ^P PfX+|؂:Y*Pu}hV G$ :Rh#<,z2}zpBcX- *Ţ=DSEΠٍ!"˧m(J3,'E>gƙ0t ٦XSv$5 /(->0/ۣ=l1otLh5_#sc1`Bj)ycD2H)˿UΉP絶;ACU9r*i.nhs+|;4ˉHRPBueZչG2B'HL\x'jN3Q^5 {fI*e&C-[|`372yVL>T"v3bg +R _mЎ/&ΞO3 {$Ȏ7m!{x;fC2CĢH/hj!I#4)I]FN I䝮΍/%KDo+B]#H&i>(|uXn݌,Lf e=L?kOzD<R矋HA/z$F${-z$itua%iE'ee6 56d\+v(Cd}c%C%vsK[2_&TV'9GڤubF`o1U2)7not²]UwBz,0CmBʟܰ ֝4HvAʬ}4X*׹AalTMJV3HsnW;A%.ʸml Χh\qMG\#hqAuE=X|"- 4LfcL kJS`f˅7dX3vG ƫĮ ck 4o;+CfhCVqMQYf01% 4~/\8A!Na²L#@n&(Z A3!0 ƶ%7;+'a6`2l9A`}vY`/N Z X@ xAJF; $R$ @@2g3ô:"y@D6omxF]»wWij;⍼FR%Fpy"c? Q2]x)7s J>K9L ,:]/MUP)$heљ~*K64\5^ #a7ȽIgΩabT{v[d*nvAfyƎ7e wtQ,P4( 5xyU1nYYI|{3mhVAr92^x۳)f 'r) @:`I}FgS>I)0Q,*wJEP/;#,r{Tiw1;HZqkEAux!|ipU-enWs- rbɴdN_.ĭC8J {KBSmxbdnz~C(w "k'H&"%=1S& NobSG'qo-p׸ZD۟|[1%<)zM{; X)dΓTHhL¥|`)-TJr@ǚP{, -w*$?J-S ǯ.sr4&4 Ԅ_ue@)|iy.[0~˷GL\*8g? d֕ӑRz6$0d/dPE&$fh5h9uX /| hc=(u2qrYVF: ֟:QU)0!y&w^*-+ZѫD*+aFE6D[V9=t#BԀwO^jwa좵Z̐$U sfhhG`6'j^zh7ha^`%/6)Xp%S/ܒn8덥sNي.L{"h=b+OvzpLgģŞx!6 1>]/:*]ZJUDrh8+B^)&F1ˑ+BscKDLD ӗܕK  bU4ut- Mޙ~:g]do/D/q[Gˏn:[F Ws] P/(>ExEYOq?A_j8dulmeT]FxU F{*ഠrv֢r⽚+K[rv#}z+h*+zI &R[=,7򗃘38vQy&N0:Y偑]odv=rrb٢ʉ3N( 駼4$JU*uCa0#e}Lqw:p&1/;{ JYBDcq,Nv*&>W/ K9"DBXl"H9gYhT^a"qPC B)1t@wm4-2)7'#`u=U(J2].x裏(qױ&rAsRY: Rc '7`2-'w0p{y Xn1s$CW upizv.77㧞Fd6p/BRx}Q!|8 ]52d6sJsi+|)ޢۛXZplk ^sQs 98N֐@)rX5%aH= 0348 mRޙKjt˛ϩY3Q@B-՟3VjNSNڙA5d9KIFBOm8u) XPJa]Hz1řH,=N/,In;ߤUiF!cqDŦJpg!i8RbxxI9s9z=h~JcnVI<8WFx Ί4x/o?vt2ޒBi~Rooo'up潉UV`z}? c}QrOꑽ>GIظjSvh1|idדxcPz`[];{i1{GIԍ(~l=YԲWZb!ϊ,ptHҰ3ao;ʠ%~,A `'H WZ[ؤd[H@<ȧ.#ґ&To)yGx<1}Jg\I=LY<"2' H^)u":Щ[םj^NC2F6BpP9_-MJ$bs ^/3< j4ǝŞpKhN'zXeXEh%'.&Ze'߆ =>E;*dW֠),Kv* CµSww#qB{xq:9Ŏh+9,+wo֐oUE%iV`* ]` 16 ֜6XWψ0-)5 jQemoS(_s]$ y,)!MF$;h˴oD'@MkcvYwXh RoG}6V$P4LNEk-grZhboSaߺIg[uAZV` 4HLpdZZ t6 6B p:V>.JCL3TuG=Rk8ppZ{iVf?:[hG PGC2Q*6[B`mMI,RmxB>gѡKC! N&H->Gg&(q6hoI1͍Gy/K@8lU.nE˰ᝃs^]H܍|{'?T вf;& N6AQȤJqɱ='ʧ{4ڧ&Xr\ݟ2 E0J!kV.g6Y,mʱ\rG3cH+$۹\lHD@^J.)Nq=H 瞏o[ς'p6Xv^Ou-ѻ,왳XֿM_ &M/g-.Nڝ󌩖69[S̰$@O8G<[׿yalgg :Zig2[XD7xڹ ,*%5v,ԎiPYe~.mRfM$33; Y풶0aKq^aon+l76*e8qvJLE^qߢ7k=.؛XׯTXϴk ma:Fw#^?031A lrxrt&.%9qH(4Z]@/a_ N6Yٮb[F R̤MP ""WZHWA&oPQ<,2([UN d gעN#vMGd`1׮M{t "<or}]5q86q7Nh0q7%!FTbaq鎇QE p]?򒨤bUx1`ȎyI.wM^tuM&}iGmOhPӼ-$a$*+stbzA@c`A%׀(6& ΐ)T =}N X/HbGP8jh2]RDib ;SBstj>n=kmk1lS'SF|]\WK{RPRSUçD=pީ=|L #kr]C˭x|wۢӝnYb U/2KmZyi#h^!8FPfÌtS)#ٍLsc"k qTdꈊ#xQY`\檍KWY,Ԓu+(!ˋa`]ҡ& Qx-&ߛ VоN'yt'F nU׽`2ж `HukoBqI \{{l[N'qטת\N?ӏ q&= nSl2yru)F3!j5r 1SJYK'+B#N.6*HX:U ,_h[͍a39x.Pe0ukJ=lC5sqb"%K@?xAƷ=ϲEŏ{#~x-<Ӗym?^k59MqGuY]r!I96 U뒌} gEk&z8.-Vf/0pZT2^-V]nmHEἣt#^0@͕J7׶Yq;`K꽄Uʽ7\r$ECCFF@*CGNT0Ghs1Kk @tr*;co4&+9mRu2% NSoPMNRw` ^mf ,> I@;uGV6#;ZÁKG{978>tQ{i0A8X%HBż9cL~mWJVCR^FR^Z,fԓj=DvGzqTd."i0 C*Í3`:%'0gI?/7YGeG2&(x'l}&No]j1bƸLA[bM ]6^ $M:ZV#5Y4Mr,B#u VTg9&/`PnξJ'jL$l>,Ix,y]bWfkfp{1ncCf [_Ж=!XCŶ9Q8DlJIzE) m?O?OuB*?FvNwbvVѱ2CڟH~.4ڡmgVU>?~%d{;sG<i"jlDbI: ^hT$'CZ2Cy{`; X" ɤ)I!T* E%7Ȧ uʄ͚Ƅ9\[$ ,NJ fk1m~3r6 dHU}0m0U`ֺZӳk&EY:XKO D:Y}|:mUhg8x] 4&CbRg0T:'4&=H!ʾO?>sh-Jt^0꼼e]a f?KB+'$ $kdNDKduQ@1ϨKg83 FМj8%dB8׃\zE9وv=;6-Ի7iU #*qt  ޞi%kV}ZGF!ei?/u^ (96͐:,ِ7G@!.I-PwK+NKd,?1|&~aӜq=27DqKP)ـ1.X&~FƗy(Q?Ç$ ݊vd-,A y~7M0P8=6ޏ'p[:a]I羢窭u`,"yD$6qшn*ַԪeSlMm3(-k]>XʋZ#1b h#_|l EK Sl+֗,vvƕ mpB%z`mhY(ʖW?>'|қLk4ql6^mҾ ](uN):],PE g Sx8kXlh$ӈh4-3 F]FPv挧9{ݪq8fgɑQ*m7=ӂ+";ǀyYI> ̻ۆ<]$0 D#^(K>ٺM77rًܺ@YGͯzMEP ğhh )qJEfsF|1-NUa$ZD68~|R' A6n 4k^G# )xLr ?DF/+ ă9av1?ɪZFPp,*ˣE4d?i[.~B9S(5 chٰK5K24 I&qeU=,:sl--~b_yiEBY$镍ƒ͵0n}9o`Bh&Ǯ-bHPa"u>6xbz-$D%#A[x1^0HV2]RX:AE'Oժuh q i7$_7=ܸ^y3DPdn=U} hLWoq옑B+xSgjg346N*Vdƴ,끊4_~]2ؠcb=Th3*kI (Hw8j-~ :6{yFǍrXhTq/ ~SB Yaɏe(?x!> oU&J}?ru!os6,'n%WDyz(=6Z9EG# J!Ruv^MGCB d&"q0~[tm8q:i3z0zz;d^ #S2? w C?SK,Nv#܎=8k:!3s fwMQbiS /s=_q,<&Q#[+kƝw\9at?g:\gਫ|n Z:҆qh" d /i3=L*<!vMf8B7:LJҎl ["f{[c?<$ڞbegd$V(krc򲖽9Z&4GB5etq2%،z g“' #V5y[C6ՇB@^#ɦKXԥkARQjšpU ֬,w!/HP$ p=5o.-2 /y-J:1 vKQU^|yC^ 1W.k3.@޵):F qTtO/\ )k:qb-^ykgp9/>,R4I)ϜJܡ+eۘ8cұ -CNgnxIp:n=~qWf9.&:$jx'am2rcw9nEzfMB^1uR'Lt,&VbuD(gkm=];抚9ZRMNUp\r+HET0vU 7t\B|dr. Os2n$R d(ϖ}>&*R֜K)/ÎU4E DM/dܠ= EJ{F^3 ,e75 lfF:tDtvvLD7 Z˴_|! ةрZ1[ݸ>d-NgW؛Kg(yXT@.Zcw9[3'ɳݓׇy ǡ X}ecEj-S2v;cLob}⮒X/OnEs?wRYPc2@4 Z휐ei0bxy:K(DGENo9 L"H`%"c/mm^_9IE*Ke錸W&nB`]8mj|R~|ٽQOmz;8b@dXP K߲C6D%ro oq 2"@X~N:@kg6u0( rL4Pi 噅5,Y|$0n=S]jQyvaf%=qQn[P4ҙ|PF*DC~QAVn2@fQhogjsaf){FXKTʟf&9G=*BP ?Jh-UEx^ @ oER9",/u/?/_Zv⅁WaϹڱ |rcĐ5]. T^՚4WEUyWԝ;whio Unj~`N$vjrWtYޗJ(0ĥJ$dara ΂ ,*ٻCV-u$Zƽw^c^ޱ$S]Y2@RLR +*, 2CE$6ʔRt:č:T$r2:-.b^}Tŋr ]1\&3FߒN&;/FY'D=LaLtYaCɭEqz ?i KvK\7ru71$W]Dݚ{tF+{UͼQߍKT%m^|=RRDR>i}YtSHb8egqtw{ߟtnKm#G8#Q,Qƒ/{hHl|/H۵=c@ݭ'0ͧOe )|0(II1$d}Ֆ'4;=nݛyom6}b/HzjN}3؟NÝσ:$挾IOr=Q.嚸ϖ7;-iϚ}U:fCG⹺` [ҧ t^4 [ȿ;>d/ws?{w`{3qngs6ق&Ci,L-׵アIy\]W[r5~>8 3 ifÛW*u1;[eU=T?"NBc@Y!XS*j&G@5cH4O` Ƒ:[5Q}M\7XJ16qSq~)a(hY 9|Y4RALMm3d}tLf^Q̐2>z8jE]N%ҡ)z;EP +,,]h<ོcR&cx)\Wܑ5@gnpb~:Pѫ[u~jt̞cM"Nǝt r8rX[x#xё;^MUΒMmN4![H1`1MVwW'rFUo?1ih{OvU'{ӽ'Ovrw.T9aЮwɝ ;:zؑ0:+9U~i 4;-!Eu*ekSyE=X &]P1vrT^MbEC{qwmC=?T5Z~FKcuSljl`AwBB#E ,b$N $(Blv|rvj H~ľT857LTԼ,BI,Պ%vaNk!#:e|Ρ.Y,g?Cr 7`_M%j/_r2J;p~ؕs C[sA;"X@:$'evT"?VAM=[~/g3 +jEj$l!4%Kh^̭[xlg3wO#NyXP ҿ:}W1.Άd{sx n?("$ɧA:YbFLJ/xqSv@t(9>3^8 NNo8f;  !Kgh#i ޶{qg-|l+'9hMq7,jR8(g'>6tN#eEY?l>M2Bbt _+KR`8ڬp1[HP(M 1G;v9YQ>+J={Fs~MpJhby6y(2< Bp<>V=t:̏){%й@*)#ElƨPAIyZtC V.Fy6@e  U%6xBxb.ȩf!aj+ywfVtQжo{{@w îɇMF{2~Fk<G:f , ?>݅}|Q"\G|dhk6ē)1!0vHYRqNqmؾL*d)k|=Q2Pf=cRNp[`]Cv5ԃ+Kow$;NSȎ nÜϙ|fo)zk QX[kѩz<}؞[Ҟ(Xa}y9^F#Fl)nl(Һ^OZ3.тCSH28!n{YoiVشDr zw:FGibOaJHŲ^ :5,'gd'YorGnP L5 _ۢ_<łTHg (r-ԩ46W,@ -o=ԟ48!+JR3+ ^w}'&6"`s5eZG8}(ӤR5)c~Ūٹ_`2K~\ςWBc [E.) TSouQ&hObM4눆q-j1ά6<$A xb^]9EQbC{yG^\lLʵ X${1C<8RFtiaz\&`ެ ylh=ymyu kb=~aϝ`tt#'m9.EB_Cx+JK86Kvczkz;.^6 *~+9w*++EOen1_Nls8Bb[Ȯ?Ҙv56:afh6]W &&|O3V RIK:F>t ?m[xW)s*{fhNA =;nmojkU[O4DFm1#˱:YӓG'hW~O5)9OdhENݧP&6:|Njgg87k jNKH+{'mr窮-ZT^0xš♡%#Lo3DoL Px0 gvOOwq77`z]װ7oeo]ʆ-i2|V߽Sh0y\]MXUؠNZϞV1FFzf:-EbKW*mKzѮ2ICZ\xPcڔ>)6cvk;)'0q00ՃgI Fж' WTov#6 9/rrrhiۻSXJt0]eWe5`JGf 4}ª<C2NNCJ_7wx/O=@]FV0u Fmױ ^2v_WI՗٠Ž^uq%Ցc4ك<u(D 4MP]rMa! f\gC/re+\B*}P  !+_4ɏ"ωIA!93 .:mF=ڦhYW_10tWSJ EBP)- i5Pe=EP)CC-cy7zi"6`epn]':;Eon|&B'&>.uY>x}1wZ<ͮAx]S h0Ocђ8JtF7vm1ـژ1Pi7ᾝY'Sw- 8rθa^J&ė)x[#X{y]0 ]bO8:C)寶adў ](ē45^CvV-3 4f;m |71d>9̦2hK%6D06AlӣӟbWp,4^ :4e,P'uB`l;Xۧ.Nw^O?` H9,IC@p- tx ϸz MAϺ)m*Tߺƞ'+.-VO4B>dd}rtB4S AȲnwuȸ=G" ^ۉ0p>4ly}M\;E/408ד9{Ckp{W&Ѹi]ml=˶|6EdG1̲4jيhi[gIӭ\AwSVgN;cn1iMr54eѦJ͟(0]uPKԐHx[inl,WqP Hx4;6 wN0g鯖Vlv_5hM] utF[hWdҥ_】@&~+1NneiA-}(JpG׋`[ $hK뾴8Tě0f,k=[fq\a*?i@o$ ldą:-R~w`4g+w\;H^4'y$mv]p }_9ӝ}e"JY7%";cгVʼEFcR7X|S ӑ1Bdᕎq|u=J )Yja &Q8k+?ٱi_CAuB"?TYac ?~uه~Ex.Ax_i2ؘGqf_M~AiR:.>  $8u&tĐ2ʗ9r6<=h9*ILS̆W+z C ~37`q$MbtUV"rt"VܣٌkDkrJ1$ #nlIَgvCp UoITy8*Ͼ=b _昃ТrBJ1I1Y2;mq'?u?d7s @Mq)f&(ݡKTᵔ0W$`ˆ&)Ғ3 F ] ^sx #)M2늚3o%hrU_~ZY% Y~R@=}txNt 3JZ˜&tvdem= ʄ"[ IγВ701$ ^0+'Ű.xlm+vڙTM96ЮzoPCdq;C*j= =4:I%¶ߡ<8PBi#}81o|,voҝ4cfEtz@ВI.))窼p (FK+q0*?U?53cB'p^ tJQ4MXԄy Ǿ^. @jgZʠKE'tsND *7@=rfcn7/2)4tϪ־*xѣ;&C {wW"SYa2ȏB\B@&6Z(牕翬[\ R0oM} F-dxE3NTMF jlRh|ه4 +jeecd`kkyИ6?.?/E+b v䥀 hFh2sn Eo̷v(Ö616fю\,J{^ *-km-3q[YS"ˑuA@KM.x;^7]ڂ0\f=E[k,myVҬlc-%•f8 ^+gb΅ZwRZ}jyji׳?E;#GUN90;`T_b+tdPel~ %MuPR$?$oP+V' NTfs1#Mrf"Qs-Vv`|a8h iދgav;!&)81END߇踽#? W66L3Sfľ轕m]TW IdphlXl)Ϊyֆ١ -ěO|Q86 2JPAL(Jw, GKP79gf#0Zw$㞸_T~hE]ߨ8nyf D<6ɃJ:6{Gɤ[J6Qԣ\6+̲fP gNΩ#/ e#iPX0^m@?Xh^^P]jzH(6Fou(X("u@W_ U~Vq"QODЋy*!^.!3:,P@\[(l4Vt M]IԂŖÒ`QR!(p3 SH;!,Mjf%g3癟mʱnvً$AMoJ纀K$T5Z=iR>r%+Ęky4FBK 1vvDbJ[K_3A(~f464 1|p@OCI6 cMc:uS@XSڽ5M87'&V-oq7oujpa ֗9 `dԼW x4x<8&7\S`klT~Xًc7R2TQJgs0P( l2Nqjvor[H95i8]Fl2FZJ1[l07"â[V޶nwcf/Tc7VՂhK>wV`fjKp[U;'6Zӑ4j9Jx^ :q+RWvе MLT;]78R΃Cz O&|9Cѭ00i? @gaGx_[Mm%HIYh15j6 e@w*h 5SQOg.da+ ܍&W8@{5ǦMM9w v=oha[6cAL1ZprjN!/8\S8La!:ZTYIeuY)shLdđ>:]Eù- >.ޞsp'D - {Ӏ\D-3:cll(6<x8.v:w^0UJIܩo0 ܈giv`ȪQFm Ebzk(N$G5+kg#sO bVABw"Xn߰*f' Ɇ%._3 M*++gA5/l|J/9h LjAk:p T'[􂔙KpT2ʖ $aI C$1-܊mI+L.VŢg}w@h:Xܙ+&\^^ 8Yx:u`К]p½0 yU ͈LmJhܕjNrٹ!^,_4g'# RҡPu4{Bpb yR36UFkTM~~k w[]Xou3VBf$sfp㒪sSmKTp3P1#A)9c?ؘv qG"FV&sX!SD Q L.:2zBrBzWCĕjUj9tAÃQ?qN 8h2kHK@I+g{XyaC1r/$I{b^mY% 4(G2,eU?{š* XdK66XЎ_ @0%;(bZ\ *79jS^AwAYwyG/S]ɴ :0q5?j}}39j1%C(ɸi)+m#Jy>Q@"ɏUV cM6tU(=H7[.k3HsȈ̂ V7FUJ< 0"iA#).Zݐ15RӺoYqu9:sa?bF&lf i(_!@j7V8S73sE8~<0<ۆ1]e@qiV%P=\ 䰶 |,gFN;hܠ! ;Q4JkG/wtrFSZ);0<}j09&Nae7XɅOAC-1ٍBV]oe/.K*Z%Ycs>d[wD>r'R4DZKj IY1 sq5d&g m٪vgWOBJ4_B!g, 0Իȅ0^>f[% -q!Wϙq+}Z]%n?@tRcM\8QN c2()ɬb/݁r2\K˽ i[bj"O3 F8K[a}f2n%ʫ3Y(qڴٹcS_0g#FL+ d~ Jڬ^^<$.imkCܻ})bT1tW;X[j3-nIu]`J8-\j ˘BXBEo;^`aff/&jLSWCw (8M'yK*>{;"KMq5:_Hv1Uʍ!fOhC܀dH?R@Smc,]0?}Jފ{! }w&FC0` N"s7fuFp Rݠp'vֆUnz]a>SjyPMV2p_J*j8fwȇmV9T7 :53o:Hе`je4M0"_VWiT5#379Ę1ZVF[ybmL1=6 f"ę,0kZlZ签A2މuv/|I3j~ЯqGT"9첽SCIo)N]Q_Aҗ Vq}^b`犞,0@oRݣ72tlPgP#$H+Zo!SAi^_X0<%V  hE7%^aL N}8r3ťf g.E"D!5 O ^i_I w|O}GL9ѹz'NHV e3\;3Y<+-fN#,}'Hϰ2QO0WS,[gox0_*_Y/X໻] ix`Sy^h}C0 ,? ]ZE$9mM}]v=[95|_D9\=ʳiw4,dkm&F1,*Q@qBPk=ֳ>ץJ)UNx-7`u|)؎)(u˽V07uG^-%*hQIP,߾Iױ ppDVF6*z,AGYw)^Ű9ڨtVGDV%.N9T6PeN3=SDhr r4=',ZcXД :9Ν;4|ڛҀ>X7+Bgh4{Cpvl?r 2vzUOoJ0(c ?kD:5t5蚋¨]x2ƃVuYyo\+Ukj$߫#vMW:ݢa`=;}e֐0$Aإyk[,,Z Wf#Mg((3h]*]|iNTш,t{Xkvop˳ew_~œ&-*3̣¢@Ny^L1iR h:14J7Gpɻ (-Im 6\)aˇ,/lJֵKAMyMyP"Ϧ IDEP&:='o3ϐڹ?ήGFKNm˯kqԒ;Y c[6bNQ` 8qUj#삖jowNjܨQ.j\XܦٳzsȪSuBcq+ءх@"A:2EH-b!f_r(:>+xޕ!o\I7vq06Nx˴(h xѵپ"yage7wy4 u] Mmp &̉܅Dcq3oYsmۺ[m\I核GT*vi")iHtuH☵vxy˄ISY]Pvlwܰ~"YZ)/ m?y"tAܸes{yN1yuDt^!,M4ssRx3tC#IM֢I*9'j@֥{#Լ2q#۸m\a%N,43~:hQŒ5(f!SAڡ7)r05s}i5Z&~[cel@ Д]$#Vq'~!m0QCCS8\Sawɹ#0J+^fyS䐉ҏ3')bǯ ;؊TxtKR>}}pQ`p ]_g^:7p˸|.v紥LIsZvAug1yD)Q5'xCza'72o-@.VZ7+_`kѡʽ Q]V?ñApϽ^,eN,ƇztRc^V CY q`&<"wELm63ciIyaV:3QK)3&Ҿ]YHBhsqSd"qPO¬618g'Tu!a+(?y@i:UYtCy i YV2ah!0 m%r'C82w;@.}FΤ+w=>:Z/R@B :Wtyar=0xYW >PeF} )p=,Yz^W qfu2.Z+5DDhsP!?_y;JQfPD.-eXKi0 tƪ%RFxu\3\PYx3І{:׊ !IaOv0sv=('6o6Gm7lҁ¡M,0rX5{<Z'5aa)wl?B.y,돢G#0)X81*տI)}|gMR]3Fo h!y-jP~ 0\?sԢKCUm'uVm.dɸike` MG{;Nbȹ%V9[k !l& /zzx!jH5S6ޮZ33t߰ur.c7E;h Pŧt>&m}/Z" }0a1g D \qM]I`: p';vP)83CYOnM@$n;ƇV.ZkV?CAۗcʳR b(4_)1'4q]e!az馾 "m߄ۢr۪t>u4eJ(:JӾh)w-{ /'wSwQ:p$Np"DCԋ])n_nʥ 8J>rhpϒʳ]d#[s*0wGHIzݍ͋,Ey!:e7[!ݪ2'Њ&kO˭'Ѣ K>=u1Puf (kOe+SW?f) ( /N8#xORA]+ 3D\v^QGq^6_X!]ӝJa%_;`+J1'Ior05GSNcd إUtxA)usl*n͍n-ƾv!iM"`$̒h$f_ʼnmXAd:Uw4:|uN|B , ;fķBp}i>'ϳOi1f[Q1rQpܕւ.LH(:qXtSKr tc-ЪOtM22ԷM]97y` #)dtBv6~3˭A/w+90Z܊ xb(U0F|jZl!2r `QX@y@gyOWLn๙M]ہۭ1G(cb"pGb4s9:}oNX;捆ZF|ɤ~wHLlϭ% hJRȊrJ¦}O=?Kך%&8ϓ޵4%tk=y$ Sr){m{l7'"蚴.7 lafٻ}(|8%s8b% 5ftB1лxO5z7mShQ|&mDUbPltK]ThrÿXN3eDlnM#v{9pavsEOZJFӾQAOąZ Ba|MlKͱS 8hL=jNL`?t0Cbx"X%z4}}Cݏ+Էb 'EB4) a 2ꅇE_X+|Q%{*8_fU&Myv|{p|x{B/dkk陻84GM$ KgchǣZzvHA:MZFc A;|L&#k$D6}I2wUd`GX< R;"/€ :0­Ā89>5iѺ ~9\ڡJlyQQ'磜Nго;hzPfwx*K>Vw.F \ST+I Btqkg{H)>~YBs:>܀bi@yN&:02Ѐ iS(4%\N zB /:H25!#S\.)!@f;k@ F#٥x&xM66#",_"u 9XFyHvr-\)t SG>42j{'MƜCB1@'QAT$x1nC2)i&m*ZԔJ fFjK\ w;^7jkT%85abJ3"*F~6jޓ`V7ӿ~5ϫk~{_˿̪ϝsNWWN^HgR7f*†($}'i0N:}< =Ӡ#cL浼SW뀻7>_Nmi8Ջq^Lp⛤CF;ǻρ1n{=(F'^YzOe_MρPpr wt崇5NdL1MzKˍ/iDˬRcd[rҭnz=,؍/p&8) bo?>Y}|}p1jʺte&tSh{{?#잜LJj[m<>VGώOАBT,7+ SvyXai @dzM9q|KC8Kp\ո7ɴnhAP_AtzVhq:{ =XRkk͵{kJ=;mසcɆj-j{eb|̟}o0Fޙ=QM`Nn$ qS|H`}L'c:: >v|޽U-ΞT0öuẗ́W%hG\5O2C@ d{gpg{BN͒hxѳݓ-9;:=riou9=ۓ᨝;e#}!+DHفM@Is5ꑅu1`y.z4 ow4RH|.lT]~ݺjbyL76mq&pQm[D5/7 vuXChIvv֚qvpТ+v_nXO\RҰEi +oқ`(_zzPэyygwvsLyzY%&w0GpT3,euwer3_@]~;*_(f `_U$Θ pA:CbUaLv _/u[k~F4#&*ͣVc^edĩY:Kso[d6gOo|wEfQ{tA8'fy}ޚ ŀ|=']"9d#>:~U;o'& ol ֦Dyz'j%n ~7ocVYEEPqgWZk ɇ61k!R=W>_7oo@*F2{_7X_xfU["95ޥPwCP#'m6Rnrֵ{ x5)*IR^5R|z/m%zqt D{AkinY~蠡ZyJgQ;'Ó62+=yӟwO=yӟwOSϣ@~zчtW#,s%${{};']>vӅ;p[j;cy>}zx\眍]7mWwNjvTZD<:>|r4R-Ţ}~{L%+R~$E iu?oKUZχ' z+riS lL/&/} Z{~0ɧ-%Li`:jdCAUDrŤ\5Ѫ|Gfʅʼ5;ͫ$*&3ݫ3ks7̽:h!{F5[iu9u{. :%MB~O0c yI"OCS+ݺ&ԷI}s>! n]ߧm.B"[J 56kNRDE12D#P}݃]vuj 6ա~L?uΕcZDّÐ ~My\S5&2O8b v+G'e؆̊w!_E*H s-h)ltvWMﻝm?/!5SC dt veS I~鋽^s~ϧ?<>ʬNKfuYjR[F~NaO`;u Z)9*TUEtMK7U۬C]5AHmm'޸[.<{G݃f[G.Wr[<薌]a9ЋVe0~ 8Fo<zvzG7=o~kĊ2Wg?a?wv00ȏ^'k@zI=o]~۟~?DuEoܮ}ox~ah1=Lvw~8g;#ơysjޜ›'ǻ4x?vaofe:׶$4(}_0xe)q ϱGf"i}r]I ~"/~?4@/_x:u[95 "7,w,?&P^։=L+D:_K[b\8E~E |7l ye<2 ˸ .yH'7{_χ@_񯇉wR]_ψ8?&bOLh<}w[,k~/VG+u4QiɧHjkGO#K'%}9_ 5vO:ӟ uV>?1R|{8oV]~nhak`_EߴQhuw߆}%:Τͧmf{9CI@_#bÉ2Q~ ~ ?t༥ 㤴_YhX{inMxN):tcY '~(?x88a=H8zHlaG\Z%1 |vh uI;uQ,ypai'r14CG̯h/zso Jwi=oÄh Z G>LQԝa|_,^Kľ@\7SE\c<@ C 4or[ۮ^[4gU>/CְxΧn>esڧTzjK}Vw N<'x}xڧv4#s[QW k?E]qͭqϯ!?_иY1RFRNX*á᫷u㽟vO6i—q_qmC\!ΒwL'f-Z*O+X»u4vNFeAT6mE ZuҋMyCJS٥,Yg qJz i'B%fFͤutwdþ~eew1S_n`d5nA+b@[]&ᱮZ~[e%4$ M`a2 ͨCA9޼9vA0k˓(4w$b=;dL%i Mo88wiFmpzbWrEWLuיOi`o1CZd#z웙;rEVCcߪE:͆{Y or9]%v6)5b+Y/)m|+M2굗8o@ITCZp+($A`Lsًp5=]^ {& aMnSVBa\Q^[f!S˖amk 1cBEIf0 A_nE\zw*.ZZadQTq[/vwCc)sgn 1NtϽ^t=;kS*(MjՊM%A_WKzA[<F@隿Z4 WHFnɰvkHmgZDh.E笳 #C99hwO"t TDnߋn{OOC-NͦۡFT:~{63H[.bU)Ƥ22 ԙ'bj).-UOw<Dp \,{vMR7s00^~I`A FmspЛJ% QP Uѯ|5?gr!̔@E(DA&}E;y&JQm.sb$\dU2'garrz^JV) Ćt){A_SHCL2ԌKq6L86RNӌH+&U+Yŏ&VuC9X^&R3 :)z95.Kڷ -Lg&B}.MzfiPΕ1Rϗ}e:u zur税wPrƷ5,û@f xd@C޺ _W/:NMpγl5n# hpL^Ya^To EGc8 XDjU,t WZz&hZ2^{uICGF—7|_[]~6U: 9wC ٖu͐KlkBW1=vYprdMhlUts?\a$(HQG*ܦᐡlK>l;iBp#h\H-Ģq F=Bn}HT+'gĶS;R0oNi\Ժm+4 r&78?UlY{^A` (;2 M ?U {m`$7%*/yEpo 0t_gLNhqD^Mmhu`^`]snsӯGEVIe)1F`X<^-BnE'IMZE0xˈ }GKX΀$zbrLJ TU7F$@pa ?:sL7928S;BuNBH'z=[wbqC(.f>?qĵt!3f.DL:1sSB<9۰5Hx|O^ 9&[b #o%o7ur~vt'hbo d.N&T0֊{d~9*Mjѣ _T}n/chK&dh'иWLd&.ɶ$ ?yO3Mt(\xVKoDkOE``1.^ i([|L3oE5_Ѱ]A)ZHt0 |WK#4~8e8isRfYy:(`Va kiFg0shQԔ{M{>[Xy 45eOd;3  ²en,e+^]}LE[]9F8^n8Ӯ' X5[ZuF9:.ַE;wFV/(?Ь"2Xu{X+?& PWcog .e}P/9c#pEZr֠KJ^"sOKEza>gUL4k&Z4obW7o1٦j |Qv}8{WZ /P;dX%*K 4[n7Ft9U NVWvEPϡXoZ ֎1m7/*\(',Q_3jI.߿3)R 1aѡJQ~\;RXd0M i2 %Ѐ(* Y\e: { h FF*>ZԌpB]V.Bh:@ t h-l-SЌ&n`:p>DOo7-PNCv0>R1dtF?k7^GMLx+> YÁASS~yʋwc=94ߝ>Ÿ͕JzvUR|Vqo6EjoC'?uK?mCZy߻{spyt-֨M7w"Ř)]ZvA].m .sZDfVLZv!A͘ n}d?q:ka*K9zV|`6x-g0U=an{qoS30?K33%JAt;qeZ'fّ՝Hnl;"*s|L8DB@X@^z%`$gZ߈78pgnP3Ъx.%)7/vјe3̪.97R/$Eg {_H!GX߀̯|KŬCyڼ :t9NSLE tݬȄ`w~bv|uzi_a?Έ*&^!OXq<7x6z}p{xQ^4MaI|,#ȗR&tmʎ dT!  $H~zG7 ~ BnK- C_A8{)H\w.UtqGS$I@aX9q/(pvЊ'^4byX7v 5-7^r^VLSm^poJYq&̄O2xTDB.2 ڈg a""@ ~k#fe/24r*w;gϔx@Z;C He~*曤$ @G?|N8; A/(dJNPJ.8\҈T5$dYzjt꼕Q+-VFGk6wA5hOC9Hd:4L35*+RU= Kѝ> A_٩!u?za$%a,yGW5wt[ ꭀx"$(QĺEQa"<B➱!(Π+ OM5 i[MCqF&F #{9/}%Qf-/Rh17iqs,l`6>r*3-L.͒KBCbjT6 0{cX߷)} M:]{9v~QZC?K7Q_) *i͑PuTo͘B⭻c]XE*<;}V_|(coFC-G4wZ%weZ(:j]Gwp & @lqx֌Fd  =F5TK8*zP}A(W~|ob4퇽/bC}iuqq|ޤMPi]AumMGzN,y>Sa/A+wX8WpVEcո򋺻rա_Na]nvy(TSg|K= '5D1b/j"N1li7yoЌk;G3$*#Cڏ4Wo'&~4IhdJOGdjmEr0rz;4fuTl^5WG'Њ#C6,NE8FC*UM>Bn f:3heݬJZ?Fl{o[t*o/޽~kV׾ڽEOfoh`~gp`O랪NNX/ɒrg{JDߚ߶V#G7cSwjoK*NߣS,S'Ëx8tIhA64Hn84:CD師ѿ{ak 3oZ0L2K6H5n;C0QVJa]ܰUڂ5C{Bq|hzvzN>9#|_AYoE'C7T %0ʱb:nMhtvB]Įo>`>(4@HPVwS}3 ?oXL[]=[T jlw3ɉz|xγcudIF Xp&)4arX juy=D6NcK 7z3MH(,2LNd ܻa>OZh#g'4mOWQxfТ'@"ӄ=,IĆM❯l*؄K5h#6-"hc6j uuXx7ZQ0w\D k*(;yG5)_r!  c3c"C4Ԃ Rd wy-Bx?A7-퓑L+q|EQ] =Md6cJrK$;P^7_[M& ̂TD"Ul@p`|Pn6濦!KAvvj[O~ջ$-wgg8=u RYnh`ٹ" Pt4 T9tz(|{IU8P唱FS{ڐG w7i¸rN>!9 I2Uba,0M ;" ]mT387-&s%z.b*4:&w}>='Vv??<:;uƚ;-zzxoa;%P_p$Ci _qXh)򳲡[3F=:NZSN@}l. A at(Ck–SH1s`Y& @zd=@n6MK(+ 8h]IפxbmlbiF*k@:,scݶ:x1W9B6\_4i.D@؆"X[CgDML̨0cc>, %ی $]k (7s/Gb[>[~Kݳzuwlm(K0uWG,0s/ g$E䭚 Lz!mܭ{MTΓP=nЮ NlR01LJJT/+D8i?NH~k$9q1Zf2P|Zj:HxTM>1;3P]}FU ^ 7KIR EY0 緘k p4zy?BnIk`a*Y\4kBmhm6YyEPH4Ag4:DSҚjI6a (!Oޓez宾ըWj.RNHQ#Bjc*xVUU}hYk_KZ~,=H~Ey}Rw8 m3=dPQza]V^z<2,v+I;Ԛ6S2=P`ݪUU|N:MyAPSNtd01r3:JŊ2^}zWnlKdR6LC{¿N'SC::o#:EGBH?k(?> qی`!)\UK@Dԓ< &jgg/dvMnCP-T5.4i}ux:i(!LA"u"*ŖƵ$CE{EW׳gc6n@dt%_?}i OQC)ϐh11ZόҷS$*nOv9Eo‚򿸍!R;z0GOwpHL>Mzc-;z.!cRS_:tP(p mǘ&o!yxbw +?rj9v!FVXMUmһΚ0'{#0^%]3(;?xxa~Xod).Ys4؜ #dɤG!dCt·`/ta(_f -'E:hO`od(Pl2ޕu'C2 ѩ{0 S+-?s~[&o}OC?n]_vo:*oz[kկ^G٠D^gI>7,;x]{K=>;<@+xwwd$_ʗJCZ=^0jY%Wzw ~}KxiMJ';O|9}e}3R{rP~1~:rrw_)|y|xm ~~[aG?{B3]Li->=׵edl˽]3m*NuX@_GGKʸG=x%^ .HF|Jϝ>ٔ9 bw{F: aGeBȽωb? # hz=2;DJ6_ZCi E:OtV{$vM^oі+8Ó0}mU_XKپ)(aoF[39 /t$hK i~\@| _a*%滩Ӏ夡Q]{,K%Žu~iF8jDa7f^6 )ǴۍioyÙ襣v[f|I96tj;oTP!&+Uw?/1Z[d#>p>C[Ư mWj!^;ځ/>l˨٧g&]MoH<:\UvÇ3댩wfzΥo-^pc.$kb}Ao{&%db4TcߴGz݇эW3AWn_`n&DΧV*N 㱂%t)I*Q#ZAלۤA68v`D7fu,Oߩ:U/4/6^vfgHn\{>x/I X5[\Pe[.^p!Io`fRB&FܷI&jE4N*ѮYڹbp11]x)wpݍ3sn?4*|;z/Xp8A !`yb!,ĪaQmGSڋ5{(,;Ē]`#I:HYR:yoO $찬zF%O"9|RBtD欨~Q)Yb(,c F廿VW9|vڨxx (n=@R0ҎخA~\MN'.MJFc</a~pdO8eNUUB6 4#NfŁqO<+|?7_ b)"O!cH PzÇėe@-: ,ahE-d44{/ ᶛes="m>;Z.-@KL.ߞHYto: 'P3#vH:xkhXdwQqcdGJA Rd&^Poe7ŸO4lt-U 8^JfP/V\lň^+f IsYvįmQ,3iCbNc,PKTZ)3H9h!&ᡵU/G|NG*/7nQ7w&\Lߒc^uW= N5O( fXwˡe8Ierli~/uƫ{~CMǶ7;9grH"~*:xqһ2="'|T U8 BM^:Xő//g*.d-UkU% j[g*zYNت8TsҕI ziK74LUZze\[Qpj@q}Vƒ.A2ܲIg}ڃg'Vu0 pk`nxj?4|duy6BG{[9Lã\;\Cr7R26K(w&] z5=HY̘K-b0:A=Hiy }^i!^Ǽ w,j!9~h,("@ny 𿆽r~7r<=9ޣ ;SN-b8&+FՠhX'I14ra@L F-7I(XDZ :V_;$ib.Ioǰ u]N,9<89=~szx 0CW|wu`8W&@3t2>(VðJ@O-*&ЌN :6FI r)$NO?9y 1(;0SB'I`Rb]U瘸SՠCх]_80^ɕN6J2K,yn Ozb37MډeG-u%5˅UCBgƶJ&mQ*0xRq)4A:j$qZعM$E%.w`f,C &3"DSPX,M.ޠt$R@sG^ ڴĐKɁ[hmƑ&5i1L"#hRPh4^l6fO Ɏ )SJ(Tt>eGXxrڛ!o%f%j,xy57(;G: w͟t"z mkj7$%K%л׫{[R3Ia<-$RvrJG*~xh%mqkH ˳ 0;J'K $yH2tcȜT^<9"h2qz-1>AlڮU\P{f-ik:+5G*%Bra>%lcjW*6OG@SCL>À f3gW4fn:ݓq^h1 !au>d[jy|JtZP^Dj f@XO=ƥi-N(XFvtl򈓘!, #iI܇#d,tC>8W+qhVz7xbP54ba`f 7M%۳L7`[.[<#e}!^hZ~ KX:y /26 CC )),bE$D7"M,t[iRbF7W0ɑl k1'tqqz͗4GU֡Q<~EMU'%A5L.i;4j4CWLPw}L*t ;q)}@&ۣⱻW{YPE+FXJ?SPVV7)PIXE_!rg#[F=‘-ZAbvlT"S7JgNhv}סܱL:Lm'!Dlpee2%"[dͅ!cƅM/x:ˆN\٧ZVU,'I#B0L( jn)bN|a"a-j08˘)(q9Iᴻi(\3} v˃G H >!µf7Ӡ MUޢ}M;?`P{tW$ԜnOl^쐥$8-ׇB> nfQ]n92LLAW[A_b twADL?9;8o^F\u Zo)|2շ x ǽ˞$ ٻ[189$;7c1OPZϣGD{#D㗔?r~k /)CH7I x6&zNnxP O9tJѴ3[u$<@K%&If|SwS'06+G1_C+p Y=\}u9vFHz:ImJ㿚G#47~o fkwws狕i>dyo;OrG}?uzY;rF g_Y]c۳ɑZ| lLdyy),o|}WL1^Lp⛔r`L&dPnmTQͧp4Q"%=I9OdLp4t*~r_0w r rNɠ4o]'\/\ GYE4.F=dE&탟Տ?o|);P3 9'EB{Oww~;Rj잜('ն:>>y}@O2VۛaNJf9:Y5^Ɲr}L#v76H>gwCC :z W}@hq:{a>OZqzvo-6,UkU{ػN/[۶gK.fj݃k88)>t6=/k?3 x&N߽woTi߾ǰmwv?y=>~܉ Oaލ d?_#nEe4z + %ۏ{n5Fk̿Nw `/k߬59pZAr:O_U\j޶?Ϲ޻o}=Uz0 i̎$9v"m˸\BC6t"!9B\dMl3z:\do9P}J޿$!YE$R1d7NzS#9ѬAP3d)>!pm{Jcxuzfa0DO!8uH0c6e4npfѠ?RlMjC7x81Ȯu8)"L s(6A{-'A/(,%eɳzEL&{wG[5Œ՘ 吂.ڐci?^kBN {;u$Փ'Jb<яAӽ''bRNI-az| ;2IBf~Q´';=̬ʚA9=G#Ӂt,\M ;fErh ]95?H.^NvޱݓN>n@O=dA<2IXuاυ&m=vE\^3PzoS?WdZ&KSL#a{҇K٤!Wƿ@2|Mdzq`>q b en1G6lF|^MO(<%;~g.$#%Ԭ\05 hlmH8UКLHŽ9Vl)ю-XJ2[f?P@lϞ=/ݱ q) u(f!d11* d'z Ƿg>MkvOlw!! @k ~*<"ű)1KL(KB16Sr (J ;;C}oI"LUagdBF 2T/鋬c2&^Cz=A*rFh5#zgtO$abQeu _\X0yn9D\AovsCmmtp.4LF$=4c[e?!ة@UcVr\͐ +E&C}>O :F |bZQIg{^ C"#t.CkLF>ۚO0Rě&؜ͷI;v飩57z..+\7]#LrIY;C`LcU 1y&KŜ;kO1L;69JеXF` 6 s5G3a WtŃ׃q.57< 8V<"M @$fW,!!)ϴs]}(6g&'b7l!0d6~?4z+hmrCyff /,#g(͞Of[[OA7&x*9i>7,";3ŗo&.>J'01}"#֘1^g/q _r>5ԓ()Wz;IZ4{-1o{?Dx8 w`aWE\5<o!aW#"gOVMCP"F=).h>zę`kBH5 @C{}Ei(p"iAnNAfh P^5@[ "{`x, ȨAXinm?AU'N5vL1Ӵg64}9IN3,3#CMGc B@VnOh{M "gY``)KFL?=ylj],2]ڊEf:bb.$ pwБOG b!͠8Nbu_ʶּ1(* @B 2*pa$(ƅ]8[eK"*5rÁ@Y@"s]T! lÃǻ_.$sŜP]/>9<Bʬ./˜bQ Cv`b݋OM^޿׀x.|Wnm6^ !|j63`-KU=rEΖ)@7\NDK5ĭWVͨM'R(?@2ggYURrDDϒt<ʦv9I9;H53Q?W! 'JaJr?0O+a0-n̙<wo+kn!8QZ129Ƒzq, >7CʪжmQO DJF\D1ڵMwJ#}[Pn;}͇Ms% Bdi*<^hӜd``H5tg[V +s2ZlUBN4g087˧Pߕզ UU6̤'ᵪŜ`\r@)&sc+u)'YWd; + &k>ɇ1YK|4V6G) KȜu1oׇ4ś[xmxq;dFȝ7=``O:qs'- C10,#_n)mqQ;P {ӧ>]{TMI:g8E]m܍@v*Dg?iQz Ɓ_)y]LppƉA^y|H"( eLS@x@EJkޖΦ|b9ŗ%%MY wtXv( \/u5dS!1˪*'DBTeچ2=n:e)z{#(bCgyq: h0kas\,霹# RXu֧Rt  5_YBȱ̀3)ԧr>䷾-zI$a<Ѭ.:;TPM 7 *M'\S/ky18:9l?_r_}/sKi*`24m?`B.wɴ)5 Ql)elJ+gSΓ%!C  <,cJ[ߤe*Dbs~*wh&A턳WolqvN "/L:/]es_Sc$3w8ÌR9wS@,7^$WI {= g4qD|yECӺ}Z+ ଺M@ɬmw!>, p+ՄU*tl-BZ@7Z#ei>F=~@@ ndY3;|z]}in e$/"eG_eۮU4 L3ͲIfAEaWw0'ሒLR u/+ /#cC;J @pt:WG$%V^>]>%Y#(?1}M<Otzfك:r}n<ȝ^Y$<1yw[ ҩͬ 0Щ*v`iO2 e'aU|`%[ PN$IqJDHznN{H(aJ{iB6:M:5Cq@> &2^X tT,J%4Q]o4PWegӔS9VbLƨ.}TF}xy+?[Ŭ#ǁ (?'qr|$cd;|]HᆸkO$;:3UgF\nP0:N36J[%wT/9}ؒ[;x6 fY㽧s" K&d<é*IxکS>/Ij>g-<]PCkN>ȳ~UY6+cXAoJvK+5\_2PM;-"źlJfm榵;$wڳ_B@pMulƵ|5r{{P N *OMje^u Czהmn+{˥ϬM>lⅴŮlhe*ISV]<19~0H"^f lF?GZ mM7\n%f$e5Im3 (nܾU;AKvOO5tgl3wQ#v4,)Qsʒuliec >\[]1 M/b4wupϮAk0n˸(ZS*?1Q"kDW9CIUаPs+<99$dWх]B2T]X9PCDV|xI,:nNPr rt^sKKcnpx7BFݢn``bVcpEglJ1ޓ&.˖4:SRyL'O`렯GE z)21 +C3Ff|SlFsMSleR ; \Z&]NΓ 졣:Fm`Il!R[i,6 |@aYRssv &v36YaZy '}N?=U$v [eD*;,4^ g%T0 Haok .-ٯ⅍Aާ> Ɋ^i ɋ-1$xeGX /no\}!tiks"I 4Owg@Dl@E>m .>u>Tw霦OLDGȞrgvc, Rsw?E5Sx3254JDtGנ-h\wPb,`:أS .@`!-s[oW%,&)ux-ZI.CFə)ޤ13ŪI ~j.r^CVfQh2Zr!AIĀ0͙>hW-3+5!ڹ :̥C@ՁBSR>>w;-S@o=i3VF'!O)~ef͐FpN3ʛ%fjvǶ31hJv!@:-&"-3K#ܑhl>Mk "2б [MRixN)V>(hEGY*9+E4R/!ԥriN4_iۆ6lۇH0Q/SQXfS)oAqO6 =pF~DD odD6{JC7BO%R3E9Eq+̳݃UЫ\ +|z>r%K}1b[AFuEsKf7 vEaanև `ج]u:cOvq$?( GfIF%fJǬ<7|3Ƀ+@i>8}H{ WvڱY Uy"iO=9 NḨD<Tn><\h jS ZAB6-.㞳܋ߊ%49:\SϹBRn//$!dوѠ4cB0hc-<КX@xsk"ԧįqn螿XeO( gtnYj6 QSC4 1uA"2?k,)UǼ堰[?U( OQe17z'9gҘ;ź$/lPiad^Kб1^M>v ٷb7HeT= Əx?u;/ZGcUgka{\xd}[̏l Q/qƾ`_<;cb,,$ot-b3'Y}̼9QDVFY-*v ϣt&e㫰W Le24| `QEeuUqrCnm]KEI!Iذ Q,Wc("7\lHjzpÔ|wtJ$"-^mjl"c{6HRa=w= 8Y)ƒ;~]UmtT[Fop $q{q_eY+-Gy_2&Vuj6bP[ ꨄַe5I3OY}4Θ;;&_Dh?> !X ?NvPN}'w>1)aYB\bA m Op"u]{9^wqt;IB 0qWY( gpsAxKɵ\O笳rR({;JD]&R/ uJL9tΑcVDEڒMn!4,HA'],ނ`B :y68. ET^6#WwS 5R!eStO25()MqxLWI4SZj; v1TzC{%Y]0Y+|!qIԟDߘ%nhX`K1 #?qM5` }<-B#vp)afg +-YI֠G4~ fK^vP24>%~:8L>6  {p{I>iwm_~<}|=n6\34n*$ߛ7mll4)՚1ꥑ,w & ӻA[{J<%;L^i=G ͝ܟ`ȶ L?(8HuBLJhF]ASܳ;]0NG94ˏ,?i>_ʦܓ컿ܷO2O&@?'gN@~:W4=|#iUMcyhFsy2_/giN< kN(5NXz/~NJz~~s]Nof*ϬEny"|l9KDf&OliӌopiDvc;P=iv#c9G@|熜 LTyfYB3;Ӝ ͧowW34? 'Mw-x@~ ;O'd`ς>`_a( -] /?ُ{/7Vx峃c*to;Xc@:p݃T~uÃݧA%.&yxN}WQO%9(ˋ'j/AH;+on x6 xUsKn~[_X==j˟87G8?0'{O~<;:j2_w5}^l =xw {b%L~yۤ-6=[a0,& QA8'a|*fuu >fKB0|m 0kTciDe'@w VDp4 gENРZ|#Q'߅ LPLDҾ"́* bcմ 8x"@ =E*v+Bśn[PD`,ާ p~^ŏgozoAe 7?/ˮoU: /@?f#k)"tC kU@tN%ݶu{EDw&\fy)p"b|KC /΃O]d!ɟGbx>y!+Q %O46qp!;dYGa>WЇ nF`SPx[Ey[ Chx;layh4_sp`ґi`PkSLIfp=9ʐ7&҄.dh彧hg2<^"80_뙗:*+np?|~hKQ+yU̦)F~w &AP?_Ėlkyd9 }}' `֙3V~]Ġsr?4>-U)LtVиduy6xji}8381, 3_;!"v8O22`<O|(O| d)5;FG#N:s(B>`oRO+8~!MN=m{%'&r;a{f'˟6Lg5mJFtiCf gZ8ɺN ­d[,5MK (`4l}UWN6˃THu.Xa2dN="Qnx*T5YfNc3CyS7-gޮ*ljW`?nPfwe)wW D-h6% 6# A-XΎ9Qfֆr)x4Uzn ~b>~{d).ETH6[ϖ-j۲Ela0.[60\/[m([tO.[[%O+W n|RpyXPv"Il>w}9NO5u͌yZ\Ѻkg;kT#Iʒ/yJSWMOdfȫԎ$?˻^7w!p I t[f^ ?(*+(۸m1v?6pÃ;#!!I Au=:kVptWj 7sp|~gva;ug8E3 P '|_;Zcr! CO Xo9̔5 )}-/_cel`IUZL\=eŎvgY#KP^훀عͦ%c?@Bt%0x} s]P:y1h֥&U&>qٍ69; #x5$oLkdo8) b73 C&Ub7ǘz[_EF袄%5ݷ >'9mg8%N6cB%5>zuSOH՜ __8#_v_ P %1^=EAW!;;A땋ہRw/%}VQ8p3^*)#fBZD8V?$2x ؛;q vxݭVӯπ_v7W~]mZ,0, lP)+諑L)իԱ=P]7viIƋ{i3l&;b.$m谔5\jγ`Bc㍴~=/ݤ𪋉R nTw]罧/̊W3W6~Bi4(`JƾW$Tv$6M*%y0O\/)PP:S Cd"PE³FYP &CzCH im L/˶2@xQۙ7:.gA>WT ׁTg2= s%Jb0g!Azy[W[N#>a!Vcm\gNc AMW'q1 aVyV|J501aq6ժRbbfxd8  9( tz^:C^hHҫݐɋRNW7Q"CFFD5Y'.Ļյud)A0:s*'DdxtD6Ãß99tlI9swGeme?rF̐:TD+iӵWCL*8J9,M'*6V{I]:C_BŲH" P48fXL!%+|׏#Dcs"C61uu<"Al~Ӓ!ՏIk\Lnd )+SQPñQ;e4" `t Y*Zd;kk, T! jJb``zĶ )>ڳR&3"k3-,4C!sH D4ZfPZe('3t }ZcGG۽TWIM肼<'ƺnxǭus.lFNp1i\ah`0"3jLF1LA7ϣW3f~GFc 0̶̆kthKӛtuSAIe[|LnPAOehb. t ֮TưQ"g%5-St^ Sfvb=v;4Rk#1CJyJ+$47e)CڢweZ̿(_˧vBBntj(,]tx?^,zcM>neaBd^d#?;oGQFfJO`R$|aPlL}ӂ*aYC\ !V )DӢay`妡qpZ{ծ8ٍy68ˇ> w(t/H;Ew( 02VR-on ycfJM|gA;^ڤ r@WfRM8)>&)M sc5<ҝL_xM1l cc##^f]ƒ-{˓ JG6=mEݵ[C~m2dbf鎡Gnr4۴bu`0V;wg2@eoyKzw)B46]#328(@B{m<dBPg ="êDot7 D+7%/TjAW 7\7AEo@k p 7 #6()I60tyI4tlXfFgّ3L#m0XEV.fLDAwPVl@s,}Eks}-VԦ)nŵOX3:)տf4 ѸH$[nN:j0v3MITblӖ=݌u/.C^TNЋ'G Yۣ̆֞ԭM{װ+|^jT-s}{}nl4Ŀl9uFH9ݯ#6qIS!9gmZ_Z?lǫΛ_Ǜ7_~ur!*=E~*&#s1zIt,`7$5]C/SwxOٝq^wamdWg0op\Cvb?fCfDghK%PX;[f~%) y!/ Z[UIH6AU):1I,sLG<F6Sm2Cwi4s*e_sg'J氜3vW8!pl=/TaJǡ`V$n+k,~|Um )D1~{O7WGat/%/zkQs75ݴ ݐXxNѡ_mry]DQ$,޿7lrC Nl_AL펔 TM,;Mxo Hiy=x>+f*zo|m AB#ndo1@ݒ7;X3ᅦ&f!%Y'+?"Xƛ75,i]WbDFi[;\,@mz!w\SJevvyZ='|E>2( _pjARsn$o"8>;)9}6#w%N~D@DnF}[)HSqzյ ۱2\)1}%[7nbVߴ;Ϥ u{֙aBKZ k1J7CZpX#O*$`e3>M֩Ev.:Вގ:-w ,E ZsZК555NE#FmZPU>1E ʰSF1J -a'ꈑBqSAU 4ھԦ-'- #9\3U᚞)в@(UO\.[П<–ڽq3raڇS,Wwl" tV8|~E*I|d-usgeuw_߽+O;V'Da?1[ @[ߴ87h\=dt VUͳ Qc&j7N4㏟lPkǙ7*.LX@m O<xf ([i{Z)Mڰnv!48uÚ[4xeU4~s`P74n;Ŏ[ݨJ@WJ# j6ſ{ sO,6{1nK#M0, )##uY+E08TB<ñplgbFDqּBf}9nQ_eUP X $lfIJݿ CeEyDgNЃd6h A^?V'dxo$}(SPm‹GBtQ^iz7aRmڻG/-X"pg|/3$2"mYp"  xݣ&WPrՈ*5DēۑQ-y^43/!'ѹKO 1s}%1|(K1LJ\t+,A4mW^8k;l#<~do=\ٯt9O_͗_x/xdau )>>pKO P>B{f1@SBⷫ $לMxEhFXLw7xivN%hms;n\[$ Eɻv3 67\)Kt8C@u.:b6ΥZ@KP0У+[49l4fR{׏|+k~:-bdHgK iaˌ^p?7Mp6 9"?\dvOWSA GePdתvMxhnI9a 1ILe'{{xVڔ\O){fdK_X懒=JgHܼK epXZNiy(9)梱@a0[nRxsǗ*׭Y wzJee&DiDhxV8cwrXBjb{- }J+ݩlr|\򰉋t+iJkw7v`D3*S$բV C-#M,s,Q+&9TSieTݳAW xɮd @(٤U{?W5ԅb @1MO;(#N3BEjR՝x_l41d>>5Vw~xxE.96v-O hYС*K$!H]%BAO\;,-trSR~hȁ5_.˕/!1A E6MnU2ƲYz5/ކ7tU)aeVLM/38b2A'E ^XSVrAV0#I"!ÚyfMVSw3J?v7HHSV־H[[G{O!p#s$gUvF3$Cޓ;Ş`Q?D?.LQD|2 WsC^C[н#OXlE eq:qh‡ҁ7;84\$+~$5/cN!t;'vؗhtK˟~ywx|%]lapLN>Φ[[/ͫKefrdRXLH.Oc {?ބ"9hy(L;3L(j]th] %lu"*;DGּ+8xAE}-"taѧ`>5Yfai`la:<-aL!z?r١D}AtM5}N/ \Enj;֝_KD,^V y)k,'!Uu Gcv-i_" l8!@yI{:PVY%+$#\+ҒhG1HHXW&!fm&VmJTXGaXOv%)pNCKB)XEc*eRR;e&iT?ϛm{#b_u.}M],p[cK#Sl.b&#/ G* 4 #,]9&Hޓ}\EsW6u7glqeyi\i!*M#7'p4t|G/*V% $׍Pݧ/t#r5!r^IFmAYZ[pwaDgUS.a,mM˶|'r7#|OJ:{XμpnemG/EȼVd"ڨIEKS>+i`֜!fDY6o%FK^9VS)YqW5γPA1BpFCSr5-mr;+i߽%lweMF 3ݨuEk>w"'])YH3\v0b%mXRCwyft*nOٔJ~w\|Nld'd7+,$:\Czqw_,M #WX܅*RD3EYǾQFyAsc{--C 7_4.+OlղL|aß42]։+*vd)|Dh-<"f*S2bf–wl׾y< Mq5"Q[\vbΞ^*lrێZBǃ R_PQ @Yt,5"p bdCri"aM֋ihgq0Cߩf{X3SФTaK~x#\d+(unM[?||u}OTiYKE׸D<Q{`W-i˥m/`?p$`I.~@q:SZ1%#h= W&%m*Tkwf_J$+OLRg^w/՚k"%X.SI?K <ܼSί0ڗ}Z弹TI\}d b%bl3i"DP(E4ԫG/d97%ʪ⋷I70Fu:ͻ56,㺶2Q0'OcWAV&c|XqV} ǿGrUYݭ4.D_x ., Xܰ/4 _ƯEw6sUӣeh3EXm7NU~BLH$XQ!x÷:“Hgo#0'94mml̟S3SGW6+e$Y;|dۻT bEl_LO/ 4Lʏ{ɇZeDPқU/:c{r+Loo@M۹=5*4^80N)vN|ؓ nfjZ_mR|\z=q4uM +}͂R)VF7ߧ 龢@7d Q~ ;[4%|+M;^>VmQ_P? +#]~h/1m|P%vO9;‘0E^r9S3'*G^UKܼ@dV@M/T3Vļj'9 YkSS(U"&0}\ PE2FNllZ4CapNA"?}#X0_ H@=zT zBtF'O vrA ?ДYY~fq,ΦY;jpD|N7&j WEWJ.J;_٩L׊_*3X'Yź8E$PV1Z@T9Y `(U{B]V LҵЁ 4|/~Ĺ[TIxVu5/&?*]M.ݻ G- ;;SV]kO>pt|tnrsԌUE,7I4فNƾ5AFtE㒩l~"ŇiLq[##_^Ai6 Ͼxtoh%֧Љfޫe@G}ژfd泠n*#l7vo>?ߋwOq?{pb^7pE߂.7M}sw_>xwǗ~; d^Ó~+;hO´;^hJgS_4Motw 6q}&~K}yûk_^/}>v~csoiOӍ.n{7wollnNaQx3~?û?A gwBh}YwsYST췒Fqy:yy̸iq63]o"y25i1_~ݷ[G:3[39.wߚҿ}7%j@m^?InzcDfs'b4_w]wpIڱDCl6Tf 74+uO2#yxZ:-ѲA[ jv9uu>|o{ӎCμOd%wM!::x]1ǦǬ:wT#\Ŝ@|Ylw%4 kZʗp`kr=3Di̓ОgєF tP#7 ?WV]Y'S%RVV90'tlifE(WvKS՞0<衼WT|n*i5ou_ d"r_*뿄##YZ$y&ɛ4\I}viA\^v`G0D7'7Gwh(]f! ˯\Xz~Kg]yE3llU7|;Mp`2}W&yb>rb~uiPʚxbאA4""l_kLh6sx#dzk&fpmJSޯggIr ~΀ic򞽭:xht:Kx~yA govy߾lm)&w,@KP7/avDcHw[ᠽE !9憮Pelr _TJeHRŷ c'=xzgق6{iHa $U :/fbm}ۋLԞǣP0GߖϏ&%~u rwb ;{iY>ہb@Z+5wlsUzhmn{CI 6`rL˛:vR[z=,X\b"г~ wW]-c2~vOl=&,ZW^EU3 {̌h dIOgβёIv 2d`N:eٵSsNJ3t:/fu\ĭ5#~uD˄s-#^'\2P2>B፠<}n9EI9i,E(A=, ھtZj!?2BR2jP7gjvnl޻翤'>8׿O?_>;|<:hn/$Q(bVxznٳ;RVakIZa`NYl Y!y̛Q .Ssh~0%^m!T>:gC \1%)mN⚏FsQU%B%a'l4XÉTٞʏdENG%RNs/G{`03q*ujb|/?AprD+w˕ǫ4}yxpEKG/>51b/ؑ۟vCgHC[ltꟍ m @g ǗfSe@p6ؑ L^Si,1Kߧ.Fֈhm'M|m;ܲER|ۡNl:)>f>)M8*ń5b*.CT Yy8Erݨ$|Gc⟽"XM-SiL=\Rf?v|0^ >2 а6#Link=`v|\ao3Tլ. %l (憯 f@t9&7eei=M)Ky Zvj;\vudosoн&(oOQM֝`Ly}PJQ ]Ni: v04p'PV o<1 <1H[8F a{~v[mc_lF`&G'f2fsΊ{-зd6N Q'̴`)35,E8dVb̦QXD6Β+ዴu7F~hy5(JTOQz ~Ȥz^/sz-撚&3ت00u8 l(9I2#4?Kg-Y|Y߁z3`h 5>ҍW0A=@ղ7%X@p@p%R,*m1B(gULS?Us(rI]r t\dkv)t׫s%ɒpX'}BO* E^Z]#_<$hd9蘈ΩNXcA/T:kf%rCb ,pB_R26i .9djYwJDcxI| ZP`Վ s!R'ej?df"d 1O?|nD؝a#bX~֛r\[™mf"Ft1b9deoQ.Z,\>nM`/]`HZ advL)J XIqN#i7Ɣn5#!@ž7펷Z"[k^WTM{W~ aG?*g 9x;_o4ש Տ<[;mRJ S,U.2Imz.׬Rּ,qL=Y `T!~{+T +Wտ]Tujimo!:V֜ 7[(Ŕj٧̡g"y,-h\pH$S> c*,ET0H7`XpkNk굮2<l0DJn,cyKB0t.oN(nM뚎WW&5󬩫Z~VjX+PV\Hiq,%fzv*b/֥ ]@Wsy)M7jƸ. p(z[^:e ?EnMK{0}bo:sΊN3I8= pI+Ο6ÉQ V8݁ 쟎g#qvʮOљ#H9"Dn6xw 2F+Cʍ y.'̔-]\'8\WD&'#C-T,Tr0l'0AFJA' ~"p$m-k\~HyQV z__Qq==r/G{wbe 8S'_Iѱ9PM_@e&߈zzݬP_*LܠJSFUd&Iчו:fz#]9= @+]o¡ø wcZRN3?[4`cpi2|m(=5^-WCF~Ȧ#/?e@MzTXy9u1 .^A`U1kR>g(D)nYK"{3C#,8F8m<ί8 `̍DiL[g)c#|%EVzEcyX'?A)wىw<i>,U&䱉i/R-,[= n[Ś{-A`>eXE=w NpMZ:Δ{^m-߉wCÔtNr$7Vd;sḵw?bqzC$ u8p>E\WC{m{)OnR$W`1d`XEL@ʈYͭNq1 =ޔGHK3z B֜:h\I0:َĠp$gc#W仲 rsn:߇DsW@_;p>Q;̍ b^m@{f_yҡ b'+ж3s R{βF֘jGG_U_nfWV!k/)t(/etکv f W㡕2h*30H D<,m,7TsIJ? B$ jvDGܭūMq2 rdWW4  `FA+. C#gYc(\S1q87pw5kA^3K N##t|(HqVNw f0<8~ :s j FDh-R2,T%UW3QiwvS$^47L_k~iIe^HךBaFbvo p9^uĐAYJL9-*NdjR7FÖ3K0k+xiTsPT*W?v7wsFN:*?QaApp05C݌`@I\F#\J@t ^h{fjog~ɱݮ JO\k~O*_~Y.V{VwK@"!샣ݝwW-1p&e v,[63m{r ueL;Xʌ pNx+}f7|Ij)+`Z2i1eLA,S4%@L9=5h7;k}vx֣~rx < }á6a_ʺ`{z\*'yIs>V;^ߔX7;v99iEٷԍ/Ku5/Y-$ĺizåg:svyoӛd)⦸Y y&H6#qeGez4,LyܙV.'keNDFJG D6 BǶh`7 l\Zmh[ ,ZbepUv8,(IMqi{Ds-@JJƫUdEG%Lx+cH" 7gƤkks*y\ b!wP>\|(k7zUre@9[c{5uB96c>oSHDJ4{NgB/v Gt Fչ!JeYۤ $*}QOTli Bꨤk;G"CbĮ5)xݒa)O9p)f)U2#/MM.=h )6!}E]gUj#ǭI>eqp#$(h}{Fؒ]ۍ<;Y\(H28mOPjq,J*ުR> J;T7\iMi1yJ 'LI0O>bi:.n1³nn'Z&_8¨vz!*'Ws#[w;"| }nL[>)g`kAZuBn W̺TO#v֨Ӂ2;0|^|oUPZ}nL@E `Eq5WzkMQ>]E. ':7(?$MY_v":E!YaD*ˍZ@OpQǯ'mOlw^0^2حuemVa݁ v!_9CuzW`HN^JB74l;DiA?D wYg>66_h_~uMU'v$V8LTE%*x4DEXeʩX AܤE蝼 \P1*1Q,}ig͔cr #Xj/$r[~J}lڭ"/O[?XfsUɍu Vtwk|RZ#=*bn`%Rn\|i-ˍ~  XqvX'/q:[kBf)Xf2iz=&ۏte#ohN]Tdcٮl:]D [%ݼ$$ǔv<|̈/ dFt8Hlllu/U,$!$v䶲Qc4N2 ~# l_zf8gTrIAfmdgl?f~ 4ؖ!bx`kM hq2 (e'X+ 趼{٭[a72 !g ;525d"{O{x;ѯ(BIsnSI HJt7}NFO2tlt^ ߏxJCK.Qmu(#5%\J$IDx.s!:u¬c)ϝ.2' H(t90ʼnm쉟b96wAy=X1ACHh P4ȮSkW@`ߪZBf-&RiHMtl8Ph|j\uF]$fm7J]WMtlyKю$/$4 :Hg`w<$'F\@HXSʷ%%PveQ=F@pWWXjn@CEy/;25I+uϒ{}Q(?R&@.|pu)9݇0{-é 1s\S> 'IѨBv(0ߓ)KkJU[<>\K^7B̓>C0T[BY-=Ta l̍҉ܓ@"j(")> 7x=HFHܦL :ߑ_aV4٧wy}jww-ąnCڂ: teK7r3%]l \&Wpԫ7d-@ mq%K\mr"k&IL'lػӲĔL6ղ{iuuQPk'W/psIyDJЉ$g6i5NP{׭ -X3<4hZLpٛ.[N:+Bc;J"%(xV| =#KyR&uGy>7EXb6|Qu=\e#JdY;1 5(/I~z: I_şd* qHff,+l>Ō13φ#~Gs#éHt4|I'ҭG5 /Tt7ܩ>O[#!PW1l-;؆ lv[HeZۍp0# fޝ @v="]YWkVlV:IGPz)W'@'N{8J4WQ%V7,pfR4Ol*yB:<q&A~fŌ YO 8nI`VMi2*Lb@`?ƥ_s ^1.ǶޏOofS/D%Ŕ7m{^kf1 ͓tz2;\uHI r@LZ7BwR\G!vC&'^4DB -aT޽W ?%U{f_,M,@ d)&Cy[>]1IX$  ḷKH,EvVB+C KppQ,8nv{S;v)H/ɜ7#i Wȹk+E`_)IaQICK< "ra'QqJ9w*}*~4"H&H ([>Ym&M{Z@kW켏|;RgVe*/D]A,L7Slv˚:/)`.FS4р,XB23MfOP, *Pz ep˘\I-ÖmswZWB'mͩqؒ !nE6R?4+ovn׿XTfi >% kŁ^{so3oY1Ua.lds3XM;.< AIOpFcW}XϐSoP]snّnNsΈCCq'Ѓ%UEy氳"be+QrQ'qWeY%ݡHīvm*$U6)sSIQ25dj|œl$0)]>(n jf !NdzZ$J)c;nV*$"xQz?2 C.casyS!* ko,܏a]a%җr~^љ炚Dqՙ%ƹ] su 9cy[nZTPʛW6 *^33ç)@ WVM~_*ީwgQ)֖tb 1oB*yI:S;Bu^eNu kxQ~G905o_eƺau!q?F`r;Bjf "(bNѻ)5sk4扛9P:ɅR =$|_8qf&8Lg. 0V#oa&1@ΣJXo-LS%f;e]n6'3dVVbkC3)V/5L7-YCc]̟݇zkdjv:2SCɢSļL[f{v@!|L+M $SL-[:f"+q͖Nζp7A69ܩ( .& mc#r*FL3SvW76|.Hv,J'TQd$]dp֐7y:RETdUΘzL l5>/=rJaA<{Ҋ+K_VzBwq߱t82± s[& wOs4ٞ+{"0Llv2\nr7E>N]:4*nP_Y|mvr`/ʟͶ~d\V}CV0S`>$ O)2 cREU$Kc*GP&DXHbv_, b| `외I>>aKBG+cd$Av yMG|߶KQFA`Y',o 9!/'E-0KcyAl/^nAfGܾ篴}}ޛU(EAm#~l*xuYF(m|2hi!1s ]zv#_uG]t:_1/%S[_/S=SLUqNTn)0[Ȳ$E[~X/Y' `SIJbD5mGyz&WNi?|3UHW}u vڕgvԎ ưMxğ+՛m?Yl`#뙯Wˬ0;돩;ҭtDO8W3yyћ%~};Yޗn𬍳3;u~ Gn *Q 2诘Ś {: ɍ8ج:@+JDҢo+>O)5%@:?KhԌ$<âCZ8ȀFJE Mf[A8; SJ`&:S^>jN0W@ͳbF˫| GWYן] 3[Ox˝"Qi[I}1aI|0|4hx=s\ /d:64Oh4o9ܠSa}ƜmXcwm+ -n鳅ґY~TU'k7g 87M{rO]@nV 1't4JU&ʡ6#w\0KrҎ_l>nm'$4;3qdrBu)C>EFKCƲ߅2P* BL0߷/yx+5n/[z7{^'UψǂG7o08sQNr|pdb|:RC;{W5BMgpʅ!:&e^Km8ǣ`'ugJ\!.}߻#՟.eqVInDp4t L x땲LPφtxlK fñ08CΦ$J)sCD3c#"-5j͎Ֆ\BIZX䣹K?O6uR,W>%|dxnf&%pA@r=z!3e /T$15qp0ǏBj;qެEz2八 ٨^l׷ L #(erq},h;u4+!>՟ޝx~ 3!-s8 Si~Ֆv.=k8$y2'Oty3Aܙ/dDNeT { OUY+E ;UeHY:{+;WRv"2SoS|a"+$ Ud ^M)VO33=8!3? X'QqNʞ8ljt7YkvQy ңb^ɝ9k}+AD 袢>W%[&G#Ooq. Ndt^?3SJʙzD –FUkzGBP*ۏ13?ϦuS >j}0=OWaLC>WroMF9pH3xšUn[5ރ|:fa6ͤݵcuuzSw,.rCrԴr iD#d$W e'iI=Pizˍh }±+FJ[ΓEÔu7p%|A@GMIy@G嚕Wx&ytڧ]"#n%jL""Lo~S)Wu`uiS{Z/'j8wۅ+*m5ؽ*/G~qd ؿC@KcaZ6}SiŐTE}׼uS Z>tԷ9H mDӒ:`*9Ӯ瘿B>$[+;bN/Lk̔쇽\:+%֓lAȮ=_l!\;eSPY\ӵH%0r1p]HZJ/UL9*\ $=`IkhE;zXBq6BPEȝ\_$+9UM1Ac &eXJS&[V8I%LCrALF*ir_%˂Rz泟OӸ+iғQո5Uva\3}dgqXf;r=[0 1kY?{d4mWpw&5>-~Zm*r&$T.U:h"Q% AKY-=-dOY>]@v/_&wJkgtveXƕ1:EUzAQ帥 'Hy2YjhJM@R+Ȁf&83j`"!ʚmk"8r#bSVs`rZRu{o',ު|S:b !|-] /(*P Ջ%ǼT?+ʓ7ވ "ÙhCk ?sZ-KY ~MYU >>rer#Lbqjڿ:V{kV%":>!Unrgn)WYY`P ۭE>ÃQiʽ:r0d49yRl|>g? p~8o%iNS >Z:Te#u_ZlR ǓhL=q5t Bx!d:l?ɦ#ALʔ!\K"3V*[*3*P#ՅȪX1'hdl9K?,zG?"%7 0NxD%_O~{pً{{oTxPxǿ˳^=Y\y$`XɻNҵ?䫯+[qӚp(`9-:ǣ?8]V3P"|":Psz+,bo^Cq h(868]GYznT`tlb@b00J<׵0Vc#BY`@sDU톭$;KHY5tkvI1imZ)a3wjNyQjW uGُg$rn]tP9&H'h: lg$JCn7UUb;oWdҎu*r% vQj%}MC9Ā 89D!1> t YE*CPxMYכS5Aq)̢|L.i~`Ýjb˭ P%5&o }e:O28 m*S߶%/HHr y> ?z7q~I(=KfTFC%avqT>7:|쮦T Nc(rd-:PO8Sc# |,(|]z/o4sz&$\|Θf JJ j.w|S)Z ղK, 7>L>Q+'fWK@~3pgy\Uz~n̮ă=qص_iXٕMD9)%X.$-<|@?5N%Cmxk,{G!΋*k"i"b/Ev6%aاdBF_O8ޏoR5}!w+(z{fu{DaaKx[`Huj2GY dTw!J_`'J:H|:<Ǭ!zFu]f&pO}Tט48"V9u1CLi~[0x}9><&x=+ ʀXtb54Ư{) -o<-վ*da~; zmwNaM*D́EHvU_^<=QEy|7f5mri[r$凞,$wiݮ4Kxn) բ_\ܨۍez$[ n"7@?XHz|2PpTP'"@A5C_oo;G{}|Up7y*J.J[ 0DCXߎ8jLh8NW>#y]VsY}P-ʆr\)w##9߽azܔ&H.IACoVa_4ܛygW\2,J~d`_.1F [u'am5dhe&ށ=As^ Z8x @叶MD"XX պ^\JkMW;Ƨz_ӺKhr7h=iߋh2Ɠ"G#8pb!lw`gƒTK>tӽq.YCGqV.joه-z,4D RV|`AW+j?11+wEwTE=Sej^|;N+W׋p6 BTNdEʢ輮(5 ez{SZ3U^c;'ٸč&' r%"[?X* e5l2Ia3~ҵHV$M</il[:)e~30GF?;I.ׅjyaFD{}mAQc'xZH2D:yFo/_ '@gh)R1{/ ]w *#` R! dɎn"eJۮlJKTrSu?0!yXh˧:W[_u]TchjN2AH] ]AVFgXKxfr[>r^c ryB+SnmJZ.Y25Afz݈FީZY`0oG q=XTn,"+f{&cJk{˭-<`i.)+c^JAi2\$jh2r)HWhpCUX>l(dS,K^8߉e6O贽@w6*5Ye˛5rqTC ym6N&CPoQ e>ˬ# |F -1Q$([%Z"~tDe$\V8d0?$_|:"8?b_yНh6AARkIKfg+f6łr/<&%!Ub:7U$ZsS4tC}`mbc!ĺgpI8xxHJYҤ qEO ` 02YqkK jˣ%r _ >v^eϣ-@ρ[V(@qDocPx9*Zu\(kG@1"*CcDW"-eYspJ Wu`F=3y׃+v\-|Q tYr7Q/~+~ ?]y(Ȍ:;̜r;IWɂ_ E_]A; ch3Y6-b*OїiRZ2gK(u'Nk_Kf 7w{Y.W9pQ;kwm#k܅'nK]ާ{.)dI! ,v)"\nfbd XO@U釛#Jb5z%ÕN[W|.h̤rb"W }A^B{;US$nKDNf}6`]yYkvUǒ)mЋC 7kO42fs>|Xh[T=gOsU5wVWn[qp(h',7$^Vch+dˁ`)Y30 A:6XĤ".  q@ ^ rek}s_w6]GaQ$|MNIQ%o:si_u% ;Hv\ïvZ6޴_W&&  KlQ6GK;Z.!VU4;͆)4ukPkg7mݮi f\ߕԹW:xzف0zR#d4.ʞ7ア_aQekxyF]Kaһ+W˷U 4_b.BA@'>yKc\yD)'Dp! wc3$'3gO|p"XTlz4U3{I**;_"TU+T~3vӪJCQ;!X5~0bbeDPuB~ vUItR~/[U`#AV%XRrA|쐆 Ybn\6-+ყFh|_zʼn Kjh'&qŀZxG}PZb6J7T: uU2.mQ! x\l3k[Me˽c :NMY@P4Gc$s` G3Tfw3`2&, f^%\`-mcЀ7ve-pfc{r!iN(b5&DI6U?Iy'Z@v@N~Hknh{8%W4EUJJTUUe*3B t]pt>l(`=r1),Ê'׉" uU?F܌EBA7=(H*DOb6O>|1R|I^I. TMlzYrqW1U/źn/yOcڽ1uJ2k 5y\Į͎:+9 :|Xib'aj[Isd\s5)ƶ{L$<‡@8xDhVԏ(4VXBB26XV|r )2;}O\z)o3*8Jt0@m9=zoNtz?!)z>*Ac홊1m=6VcU3&slnߎ,eH~eraGn'7spDmR:61xo@'hOC@-kRxe/ ^C\Xj}MD.`OQ_a\~(܎hL4KM>C8Iv¯XUNԎa͋~ju1,Z^}嗀|pGNKڧ|/WJ:57]Yʠ(u8Le"ƖN+i7ìozh¬l̍hM2_dHRt~=:u͍3GMTŊ6Р 0@D"<Z]KqlApP|(ބC,1$Av5RC0"sÒl[Z)+񍶀yD:՚M: tH^SsXu!$WEgTg IlGz=FN[R"TrN |-?TVcj) .)Ж&L>F8f9jZ\ƛNSA}x)5? Nt+^۸bȄ謊.o!C)%h,4I>)[˂m 3Iߛ{oڋK MLKFAdDs\:UE#&!4r"PaW(5jуhrn:ޔY#O<%~dN88uk%RZW{,-'9G7.He.-}Q4x.oRbr?qyu :#sYUcA}I/YdL9L(T"o8HrDjJF20ݎ3r-/6Р vط`Op>8ՈOOr12xɯkpϦN/j^(̅H1q+ۚij[*#>,b~;aگ sr/Qfz0wtAiM}tTn+CW1Q_Dҁڭ-' iK,睪%EE嵨n%0㮶YMh7;ܐY q.AYf ԆzS/5{OF輺7Hl/7̝bu0A&q8~kF}13M<@|6Ѧ~MЙ$p :r| 䂥P{88Wn2S9O-q@F4).aX\K"a>ғQ\—%bC~vlWaBdwx "p݃?>݋ȝoԦ2=E[kH`-t1qy.!OI|-WdˁPbF< *C &r /<ͲApzSHe msUM.|d/&jvKz JՖa~Զvs!-Ŵ%SV-GTqe _V7͵˓*+MP1CO&<;: Ӄ_9%(}!6ڪVk~fS 3N^̲tinn6(ֵ>"S:bs1+eQUIFօf}9Ə&rppkĮ`$~9pc@YQɏ'Lgr?uv_k]}R0>( N ٦A֖jBX KETYۯ$somxG- ^q\Ք tX/Ŕy%Il*1lMy׹W'oXf,۞eVvSA{q U"Mݤ&؃x2ULu lsdeLjjڮ9ח&PϭثQ[jKHtq7dE@Bݤc?,ۖF}cvzg^NJwq>HKAEic+-lrsuݞ7G-ȕ+tKA\F3,g&mլoE,UȀH1 v.paPNe "_e*? %v<g5D!JuoX6 3PRgh_kv|H:9tА 2<g'.( VsR# ]ԉw!U^*}8a0 Q( d#C02*\VxybЇAVnuAێIs˴ RӀH/t#łTH?ÀjtEai_*K A %75)QWu{/TK`d@H]T^*ijic$x^kViD2FE4ԄWl2RInA"Be:vj*l{@}sy6[RaM{ꘙ +TPP]'QzBu5UNH;`NTn*܃R ]vbU?RiV"@Z }pjȥ;WH¯\cX6/d%ZPΩT$N̉.JUPcMc (E/4~hT#sFЌbjEkjL׿WiHBXK;gxDpox)Dy,c'3L@ i)ZF`8xi_H GƜ٠9eN=~$<^  Zȵ|Me,XE_8݈D9G#ԵKVq}[!o~T9jr;~YIzA gi[zc4l{د9dON1S~%y#Mҗgl*YPu|w{+zh0Ɉ鈽GzE!gu\2eTp¥R2cST684/uک[ J_ ̌o Pz $9ȩЊkTَA-~Gbu[U˪k ]Qv$XQ{Wlq6.R>퓓77(pEkjd=8- `1Z IFF,z=tʦ/" F! PAYlKuQo[ ˜/3v=GFIKˎ}x]|$h)8B_YvF8/u$7F+R۞.KwKجmֈ۞~.Eyw0@eaĉUbYAKwdH3}9 2(bkdu:orjv9g@s%oMr ]7xֳ=XGoks,FQG(869-GvKЩ2f'׉v54k'𰉘Jb6+ 5V1YXO0V=NǠe)&L,dSgrFaj".SiiL*[ao"]y4]F&!&$&sFMלRB-+)2 4tsÒӏ3ZR|c^p*wڸ_2NXQwBt$L5l?[7Z΄J3$S~ D] d/~x~ Pg9auQpܚ^) 0yŸ<7Zr6w<" [30µHf/<ڢޭ]kGTb7ȐHp~=Lx05f5dFl9(FZpSS9OjLh FXX#ab S-aqo9:=~{0ĎhȺHqUʇjxG7WNRkL?%_Zx7K}uZv}FbԶs0SoIGt>L&7gYOc}^qm<'I dnYߧh&*ù> ]Ԇ-|f.r-*dtv umB;tv+@Ϸ{\@?D'@ &{:4:3K#Ѐ `Cd믢_'Q`mvˣ!@uy.#icX> >hW@?i%P,Py(KIPK%QI>NNvnH7d;G32j|-ZQ(:HyVpJt`?כjFilśCB{oyaqֹY%L&?IUPjaʩ0Ȭ\.Yl~vVAb, < ~SL-tTw+i, ]N?xa~~ߍ.>ha%M$%;ߐ5kUe?CM :|Xcy^KF|b%d܎3YVB'aG.9 ^.6qЛ];1lKw] $bs|51 CY9qRG#-,UGމt\/m_ui NT8g^NrjfEc!' +~sX,%̔?>Bf&(f؈0'U`"{WE1= +W.bu6 en.v>cks^lpCu"Zj8KgG2%X2׃ HQ}Ec${4]C.#k1V4[lLܤH ^ȪΧpwZ[ZsMs#i>RJFge4B *{:TӔg3B$K@L#V [B v×陏FL7X f&K!}N(C fI>N &$X9J.ĜRpFW$R;L!.]҂Ћ|nVݣ~( 8Eed8l m²Z# `f)`>9e7͹ag@"aSs)M!^cݿ˽'_\};hOEpyr,5F9_E>ΊQ~nwdC8 [-Q4p|wϐD W{R̠:,hRNX,PWwTf ?B4'w q$ͺA\{5,Y~T+LR\yϊQ<:x~?;~sTi6]\[xë%[}=ulkZ;Cy#AҮHࠡI6KAd{rSؽG2aLm^cŒ^h,D4~)!eC8a:XF6LF gS 9tnȲZ |R,vuJf{/A-L›\uIէ_iKԏ_"bi/%g SRji;_?8<^٬mIp+!sh:$<<8H2|@!HK+dxͷƔ1 i>=9jM[f btsQ{ݿK?zy0;w޷Ʒms?=OmKdЍY4N`kazf,yva+eP Xf P3p%)")k,cMOBgK_]؁3yYR+pCS$4A)SztM {RPwfX",99L[ g_z4?5 4cA7p{z W陇<ZwڗE/==M6{?ƽNl/Lkҩ [g~]0Nw?΍pw4>kk ~|~'٦|A3GCl9^6;޼y};{׾>}[ٛ/;u?:?ڿV ״pjnnt s{y6oM:i^Qu˰MHN;N?Ki/Ը`n#]O.Xk=Q[O|c: og W)߻ǯ{7·y߼2:M=lۻ?v^w}ּ{~jнn]\= gO雩D*on^ⴛpn^MAf՛4N˴x>m87s(Fp,m܃PV.5Si )-cvdWw{`NT>Gw|&Fk=)E hfX3+\ .ӬG=3ls"NgHɴ͇A޻ȏ_n%ye+Nȓm( hӹ{4/c|d1>y=ur٣;l8a3p`=[r rG$-Hiȯyoìƽ_E{z"J*z4t`YA(f_bf2͠wGWu_\O̵Lff$'҈[bA~JڶܛRC6dǣfWF=7u\ L#(p^ŔYF4 z{l_4~_"|'?{aᇧުÙ<9'6c Rrus\ #+x}% tKό6'p#^ʳyWw $1)8? JɝTZ&HS]GZ?J&ǻRVا#h!Ӛ6PJJc< <Ȱ)xCTd˃}B^Tɿ[f/"$y/4lG s07Pm>58 <&SGfr4ʬR6ssgjd~9SItN V])sb.4Od8ήl*cvf0{JP_ۺgi|P4ye2ci)H!v™ _m^`Y#cQ13f&K?Gl4!p$- ͎kP$Z+ ְa2|Ej̋;Ϧ ST':F9["a.WZT^Q:JhǛ3S "ggǿZێSm~3%]hK>M ԨfUaWA'EC* Ҫ;RقKl>&a8Gvdl5 ]_\ T}Y׆+4 NW0X G5 id$k# Oj{@|3\r4ѫiwaj1!flIk#is!]S$ՅqVu02afjzȋє4XN2uH9 2Т Y8,)@N-tVYݘd[f3A4w^D:"`0j%&ΰt?)G/n=!YBGS NKuYw0N05=Ň覃[c`S"$6:K&;% v2? (A# yY*$"#Ji}F@]e#Wv߼ohhK7~ƻ~ejZ "7١@9D(("F`7@6½+@i,JR' 60"ӥ7 6>S#1RP{y(GsAk'Rz`,?OJghkPꋜi`VWL}@/]̬0CSp:@`"t[SOO5,ZˉX. X.JBiY~89Yc cLƫw68sx؂״ "iI^F@?B(t™ސ[Tw +5Phv%(fu#CT\͖/˗0-J^\n+y>,ā}YG07* lu;XVH_TQi=aoI | -3D15c_OzZCOk թPmIO+k0N|F9XvwAy[ZA*0l$ _ϗ (bK+UD6] 38޷\w7=ي9e7UlWHi(]am@ Rch}}G:: 8E2jt@M>ڦr0,$:kPsOqp|:d{yCpfI$W2?qQ3nv-ۂ|~ hjBk#p)D@AX+9ރlnusu$#> ;a=qpͦlRSCD3z7@)#k y Xi8X#5WnA^Bc^rNmjvFbwF{EzvaK(PXPmD<̒\͇ζpYHvA6I ;cނ\.az/OϿ@%A1ھ+YyNR j ĦOJ B@]!.Xxc\KT𾯑9p}Y-Ds> x@]k˚6#[v0; em/( ' ~& 3lìˋEqm-Sm킆x@*gXx pcTߦhB9!6דdsEl |}4Yzu^;ܙ-շ+G1t&5|'B}y;!clMsꃛ;U^& ` bj{ " *4ߛ8l4ΎK 9"ymV ĉyPZ|.E ={fH57:<tۿ,[km7WV~# oA MF7ڃ߀ {r7_~d54/; yCDŽEUcQY^s,D$F7ODfYԆGH̲MpCRbQə|F!$ bU=[]6/gi\pk .Sv'Z+]sx^zWgA60y;tʀfҷ4 )1gz$G(-GcbHl}&1BtK^301Bzx S?P9]+Z"2O_ P]Nfžчc]8BL%6p0Fb+ZqFruB Ukٳ'8\A1aW+cj+Jt pKC):(هbQCՀ+?*8 "]#"#n*48`E{}q"*Mwp)4ilS.[ ֢b!w-Be!3:)]AJ2SkO D֍FJE^1c3v55NOi!΄Z$1YqPQ5Y[tl3ٚkB+`B"m8" fs;ɕ_>=|rX"P_Nִ.X"4&i7\U[ QbDNàxXv6 +7c-ӟͱi6ac g<|^ 콐T`gƸ Yu)q#Kq)y&ٻ;=eAAV;FMŌ%YW6FJk8ol5)? $ŧWI^RC ;'%*&ˊY]O;W _쓺GÓ ۫Rowp.FibO qG*:y)W)!nx {4`t<!PMKXX[fĕ\_[frwq4}c,$؟e*v?72f 6SQx(믺9/0kKh $}\|x<=~5%;_]Ldӧ4Bm \Er{9HDtu z{\DUġ]$1YVNriB6] W8?U9aQ̳"]5~)2OrCY?JL39RPU7+d5n@3/Rkā.[oà i43Zm 52féd]@#Vղ"h={ @E F-:c".dʼy^ݼp,à  Q y"= /&mq= Ѱ Nxey֠=DFEY2FN-c p6gw$f&3*s¾-a0U# lJ= ұYñ WLp`LF(&Hy:GRX]#̼X))@BF,9 0`0r(v'XNZhh'Ŝ||A@2d(;qmJ(|rڡ NyZB ,

gG>={Q\YDYYzżH]4vgeEsx L(hI@}7P]z1 ٸP?kJ}|.3p* n+O<Osά%$xSug/ cQH>53#莆 4(rjݨ_X%w~MiI΅_z У=@#"XrpE"m054R ?<{y"޾/Lr3Sz3O@]5fC$Lay ݓ'4C~D:U/nG–R!BWlc-aQ;)ËA\lVc.)<O9 &|H/kqPlr]Ujv: ldx>+4;~b"A)ښ`.Cltvʈ|)ĭRsckm}̑d %[z4$amp0Bpv mJžEŸ!Tkc#`BGܚycWphjo|mS3`]] r )f,*/ ԩ{9/?#DkXLvORʴ)$V`3.OC-p72.A|.0q`2,P;ୣb+'{{,g{r${! "gHk_'j.J% s"s a0qmj^Sr RotNy [91Av1PsGf?Z4$aNSZ %Tؒ49$K0odIL`8b2|ыL!y 5xx7T;-|Il%vR(,(=|"wbˀĕSaǔ*NNUpNT;*HEBC dP ][(`cK8W G<}C#I?o\"P0W.W;ݨPj*㴘̦ 7JhcUMo90 NsNu##b-P/[`i)ែ- x,)0 *^s2%jxwE_n1okB$y"K)Tj$A$!7L }@d"S4ɮ%*Q}R.)c73v,vtZucfZe64xEqQ11e:o6m_WSé튉x:*,]0hf5IrQ~x:?8FiYJ[y2x_>6;csz(cĊ{a~ْqEH )HAVc&f5኏16ޓscLbAܤ⼈ ֊JްyEIF:ʣ W{@MG兺յK^@ghdX8G)t K)8{/7B-w8f:n1KH"CTmDPq\^] o :Ε"aTm,,B r.P=A^Դ@I4 ʅ]+ޒٷ8 (\⾌ԪWYuS/VoO]Bc^Xmݰ6V ocpujK˒,mT͙u$g Ј/]C^L<^K[2f&E6ܗSL)f!Ĵ[^9fg՞hà`u".00@GAQWOt~0,Ҡ<4tt^s"SG\T'itCMHYĄ6^=vMX8*C |)LM u-9TmȂr(^  XkE/M˗ M|dGԒHMؒS\q#W|(0I8)X_hUb}0vk>ldmJb[%]ǫ.nE u!LJj7XvX|lwsME *s>8$ '|ӀԆ1:2bieJ˸6ט@gT"i% Ǿ\{+غSP)ǰt8a&,tᄲM[×̢Ǩ1+s\O:C'O:ShV{ 9/QK fL;IcLSpDo!sU/q1ޛMnx| ff\s3,PVK+ 㺩hjA4Ʊn, R?ۙ4 FrKCNx H #LA1?ar0'?Dm$0Ӌ ("l$ XMex_@D~ NGצ.4<Q5dɠLe KryT*0 c641޿q$wplɲcҔMKD"u{I 2$D hGOשJ$s>TWW_4 }W zDz}xY &tu!haa pZ@,Y=$~e(ƸVC9!vf+^{ĊCUd_ߏ( V%QHQ: ÞLzr}X=jMgkbwz]^|*A&-5lĔ=UxO,XA";e evkBPXM8oq]"iQ!"︉$D@D3'yY))Zv\,zp|tb48DI:8LXV0XOhKn6N S! anHIW2yc:B9m;0O %'D|MD5*(xʹғ f}-d[U@K,s kp/(j^Zb蔚KeU G5mH\ nmU;PGz>ߩ* 3z&%9OZjP$FbuQE_>0&TvXTG#XRcN#̭SUgćjdF!j2mGxfXc66m ɌoH_`թ'1yE/^%R1vizWhM#Y';kַ©ӌ1DX<~vIAd:bG7_ȿ-Ft֛hh,| @Av 3ݿՏ*[I⑾/z 9k@(yu%K52m:fxCp5Pm6tJ`9:YKZBpy  Qnf|5Qz `ߨN-VhIb``kdG0FLZ>kIA[\rEp Kn"tl#>N'if%9T7VpmUn{l,H5*9ptc&2Hmݴ OCs-,M-xMy{,m x =toT)*?J@@}@4`a%{sl2*ggӧydh5(@1wa FMf}2fZtw)Ƃ$rx jK H/|@T$|{Bɕ&1KR,=h e _y jg |w [v#>*Gڹ϶i& KEn9;&G갧3)5 YaPC*\:0Ӟ,7+bApiPtE=A7r u Hq*>G@]иػ8B"sYYk&a{ǂ"v=:4*#ߋ Q:AM~=KNlnk;cro߫2+tma;{U]\܈`Mo,µ|UPخo7Ri"+9NB׶z= -][tdD5)֘3=v prX GUeTQfIlbaWVJW !T)N$|t3LO6fi%ZhOURM2ȁ4JQS;+> d]zIS-Y3VM {4@H6☃lRJ UD<\.rk̽og9\?EN:L%F09ӭ&Sݧʢ`ẽqK >ލ@E'/0;~5L16W7]^nJo ܌q^4:ψ<= .I7nNB8 МbS# E.qi)168 ̌S ǡ* ~(aH`/<H3q.dR _5]ɝ~`ta{;EDhwlpa80t>I:Dև% dLkeɈ7-sFkk' ^sDlM>4 9w(c))ҁlړv$$-<ohSrDeD/p11}[=jRcwI NE=m]Ԩ4&נ7W,q]ܩ,~u>F*ԋ2͜cѲ z!mc9 1s/pX֔`ȸ^c\`1іS=1dnڲ*Qߒ>{XWDJfC:zYf܃OT1HK!+ v7\&Ah~c*?Ww0/'/y`U\Ox;\ O@;7޾Wg]Dt:0 ѝ8]?G~+5m'Y:k 3g//@wsEK<.Xhج M"tXpDz:)0LAi<ʂdW5Cx5u$hƐL\Ұ:e/eB)_NSо|+0Hh!HA1H5ݦ4~^hÃbH(?h)r&edaͷ^Q"RYJ|=DϹ3Y#zD-I5K 9 E[ʲ|ONXaf#Yg$:뙊W='lTR9<$" N &aJP!Yߥp~DoȫdГN} -ul!rq'`HPXSL֡=i (׃ϡ43=?_9qAڮejT6HZEi8 v ׉wɃxCգxP6mP K~>{I9Lm (cEō)Tʫjժ[ULPYb23s!")>s(]s!a064X:M #ĤNW)  ewAJ^6)kUZBB [0/9@/ì@P3-iR^Zk쵈n@gI }y"'$rQQӣ'4lq:779[~:\,mmmM욝2C6BLLrzޟkH8ٞLJ]m$'uTCd uKkkܦ}tAѷQo~6Wۼm.b򐳮>i-R3sK௙%LA;MrV0avG'RQ ia֐V'|IuY R'ۣBM4dAq2d3Wi!$RoMXdPr,EM'am v`r ll _lȷ2'sQI>" ׻KN\"tGŭͻע) um3.,C/!x :tl0l. f\Bb5",  Njw/W H2F e'y7$PX\Q I=mPu.p#(0P+Yif o+Tv(C@蘭:-mBh]u=B3W u߄ϱ)H6W6drn]Ћ rXƅCѹ6:\M 3Ayvcbaq3&]1j|*M>3">\Bz<>֊iۦhpL?\JS˨23N9\ gn}s + "LBBs &. O4/uρuG0uox"Ekw253\ f@#˜UfL![pvC#'\'KUVU7w~m/4q2lpa8͕fX28fZVP(q@ޚ,1+$Z2+ɓT6vmV|9?g5paas\P_M2:?H8 Q pzvIDX z#z1G>8΃fe@Š@K8#Խ\tRűT`&p*G_}!y6 d3d0E8>yrNK xX36j6C&BkO]#FrZ!Yw&,ӁauB)ITa_oZjG\{ٔ"hC$\]fâ8:{,MqV$)֏ Uxp$ڃ# U-aCA96m::6no{3 >6|!c,QL '@_^= "w &?sG-<-Hm !{K ܞc' >`=/ɩYeD4-@]sә ZE— d o.'IZc'6uvW= bDh !ޠZW1@?9 AiMfN5ñhke>(&􆾴ѱyv% Dq36/ߎ ɒWOJ뻖 NiMQ/{=XM'9rC436y %첧SڟMM!d\ڏ;7a u.AڻC㣗?>x,JW*3sr8iG++/gfG&ύd0,h1fR־_{ ljr=߀>7>O|7͇ xmNssm_=>˽!yfw}ן>ITHN~>Y_;lNk|[?=^lB5[b4_Ï ˝H?ò&d=":jxMRN֡mAqg:s3Y7 !5AWL&V糍k ͔*S?ˈڷl P-zw%IV`#,7e:O^>?;$HUנ=i$B/ ;98|:ɼW='y7;?:6x:\yyÍ\K/N={wd X[Frxw||)uW_.1=(HV_0\ 8]ޚj>`,ln&vy D.BEi{U֪GjG KUVK@;?c pC0zdd=D}(G$Ϡ-rb9*g枘Y9xqbsY7MX#G/мC>kNbxiO t)L;SAUoD :C–ky=[a?b{.;ZA6KZ2k[3%D$[7Lݙ!|w'錹Q{{e9ގC֭e!%Y4Q 1h$3Ъ.8h,*j>dI#%,!q40F%?>6@cT z}1lP Zyh.L`| oU$bQqn5L7fP3ZlhnאK|as98M3g#`5û}3fCۘ\1>gn#ge mÌlz%H%˿iS/=kơȒ~&XYS':K,-~G*is6ZlQij o#\UPށK;p :XrXO?8Io![8$YuGna5vhN9|y]T¡2*+1`ʍSB)Z,\sEmubJT鷑u%ڇlTNؚ߉wQ-AKBrJJuU_nw2҈[5*} E{O'EC2x?rp_!W^~?{ooߤ!օA$X"rv-ӥ `J]xfg(H[ U((fA`Qxө҈O搹>pSo6ˋHaf6 ѣ`,d\) %h: |J[pO .bi([/ [ErAC5x &dXZ8iT7Nw֨ǼWr FGڢF{$˵  nA#e5gKnI~.wgFJqrEvq9'1X)(n]L^ N[N;0vk}=¨2H5`jaPY0= 3bi8 cƒbsn9bWVZ iZDr^v՜oX=wO%%r2Jw@=>(jkt&(sA6LXMt`i{)}I@02⧽% +tjq>(/I Nf0u42~'bs2j*zsi $nӺ½ Zg["f':*1A"ԲVfaoYKy2_i .oC6j]T"9'pSPFb(+4""c1} k?8VԓToW`lQ [+{Z[$oA۾cO3ˋN}qU]# u\gT>h/9Jp<,'j]'07XʠTPk |Mg$?Ta-ٵze0{˯#h >RiJ,HóJ @+yDgULsI *U\fpȳAMqᗏj~nRkc z} caWq~u"𗀾lӦ7u(5=XeEocYvlejsV/ŃKOoHMZ  Fm?~Wt- ܬ=|`-;<@?Ue朱}1ig`O#f;C!s*!4NZ]kU\tD(^f9xo܂<)?ܓG~v"v?paVI7&A v->Et~(AT#4u̐c1Vq籹'FҵcǤۀ ľpoӀ Ͽ&h@;CR&w"m߫V`^8*6ạ|1V?ןڄ gV>Nc*H[ &`H)Ay'0 | awCUc.- {Aaod#Gh_{ek-%MfWRM5a3q:}|C>(-ifl yKpA-@5Ҩ Mb c,n.17Hh|Jg3Ԏ#tΟ: Y*9pC%jd$3Czvjf̋b!u!viw[@G.9Ȑ_p ݡU48Vp ve3 H{7{XRs@ۛS8t`X \FS7fRLlY%n.ev=;[Mn0Bǻav j\Lp ݸV?]dh3&޻С'R4yU[h~~yS )w? la2ǖ"YWM׵L\j:ܾ=o,Ki#I.L+I UY&X{1[hY/p- 0cjH>+& '"^ʫРșu oϸ-Lj1_ˮQCBn}>)A<ϒd݈dTzZp7N`WdO#\.fX1KuR,:eQt@<.T Mǀ=bHI76 6-pnP^,\%O.G3`+t&tWl88Q0BDYޑCevܛ=یt_ܣ己Co!s <@*9$3 .#ZۊsR.eRZ؍#eݣ<<? uԘI36^1-' JA1N\`^V{Pe#V{Ѱ OAZ -I d΁$6 ʏOWk'3cB*w5^prk3xDl] `EqrOЋҢ{ `Le|Y==Gû] Cֳ>,)GLqpcMn4\}A`}vM5XikR(`8j#΅4Z냹 |ZB]9WYaH6he-P{s{\zNM{`u2)eWak^֭"l.76Cy+\ to'38-H7gӋ HrB;Nt7Y#KST)crQE "KY'ZF$H *AWå=ӡc4CmN&`EY16xNˊe.z>Ndk#Psy'WǍT/+y6}̮DPOEו(ĵaĪp6:fB`^uԍp2n@'\Luf]E[,v0o$PQEvj#B*R j9!!TfhR2|g)ReHau#%b(|6K\)Bp] לQ ($QfuH^99U]:TLX=7pC,3 c jPF9d&SX5>·Ύ3c"YQniqi|yqES9 C*ͩ%"cTc䶆Fn#jЂ 9鸙 \/<)X刈)߃kxu෉ x)7"fz;y`ei-pGj>bnp8}:!?;V.j ;%+#` p>t%7FP]KТI_0J,Ͼh$n5d' ׶5Qee(=K$-e";\ZPD_/%\'\[t3-v,8?]p}?HYij@$ԍ-g>2\Ɲ段.cVvf't?wFs%ژl(X'~:FmaڏN|Jhp&|*&U-Ӕjju7=ƼhSBHҝHNu}_NQ1.VDUq0jt2~M XKZLa|ph ݠ9\͸]J.L28!'c-ڰؑmՇF6ú*/r>-tT9xY.c E0Q W~4A6o'LwF@1 YDYIC$Bt)`k'tKPޅ`'_ M]4"~C3v#WTv:3&P%eiħON#lfW/RadQ*ܪDdDAT@.i)$Q,L*#຋jk\ ƃιp_btU,y8x[z5ǹg-Cеpy#5$+INy1yQ@zԝR :1=9s硔^M& g)2.xyI'n#ŰÃj>c}g(% R~CsP|"Fxڅ\rLޖ \9%-4@ uLL ^&qkh@ xHS4si]@$wL;yP 'AaFRԠ):Hnw:5rKVyHыjadȉEv!ΏwNE`Qa'Q,d+RvehKƙ$t\>iqrdzKZcpgGg7&gX}ISTSi)ѣxBa|`V/NWo_`pHRF*cx{+Q*Z;bJW5xE $"|yg͇%$)=~_EH4/cvc(T=` %*@ݗL_`J[t/?.cSiIq@L^4 f{]%s.垨eQ8 u~CtFڸ")Uw`)0dp dp~" 0 );8MQq7`hŋI JFfqܿVП{ w9fPL:h??ciåKKqSCen.ɧRcql<gfq6R[4( ̇{ܮlL0d>HCLPw~^LT'[( ա1nFv44?N~pWA:W65 o#%J3mS$b:*eGr>yKl1MN_H3˯Rc4oA9d ^ftt^\įƼK4(r]AHn ]"HO?&h$"aM֓O|i;v mw^.4>W LGj|!?^/iP>l݇ 5ۇsΚrpYӸ>,"W? L"yYㄊ [.l/asV#FOf5XB8s4>$Q6?Xq+Z92#-7@m3N`b<5 Hl'xNxWj:5 קEs";˘MCZ[[xwX9Ƀ*,U-ؼ)T0s`p >r_d 5)33n"["=2F'C@͕{b켄]\bX| $*j9-yUNQ<vZ:K5=!1Z`jZbPK{~rwKZytWGOɂ"@;HYPZ7RڈW<}']eepp\(YiElSgT%j"`3e^t|4P&a>6W!U@|X)D/Kqs._5tC!!00xc-!(I= )NsEB%5W>5,f főguWK } LZ9=c+3:Y:anO@57ARU.|>HJ~O¼/3x-Q7 H-z>5F7H5cܼ4?Le~dxp.r ).ľEr&:5PR 2^13>{1Ɏ厼QDLUhK8-`m];h[Yҷ]UpjL2x[{jYA`|Bj^@?! EQJQ,2̵FK4Pk,1\bU=_TYt: sG?[/ 'Z>ѩ61uiA`=hҼo ]=ްF"Gi!ӆ}I0ÍlF2V p";dtp8k U+'̚t4ƴ!PtJPEph24}PZEW7)@ !LqKh<cc @-{:@o VqȰsk5LMien&ؕMME֜]53~ 8yOK~rjBx_=Jb/.EU0(b6ڞ󴟖a"Vxe>]opۓ I1 +ÔJjryܓѦ, (7PXY`TS6 мw<|Krxz|w;>8ݫ>jm:ZO*_j/r*KU`9 grOvIUg6SV-ln}[YȎN`%faI90$ك;9fA:&4s&9K'I`^eU1-$Sȩd#xZ>kQ/\qʩT3dV7+V Ԅj!+f*G4"߭׵Զt@O "$Mު~[goa}^ݳ.=J;LFJ?qz>н.P3|1HE W**D&68̚ӍLM}.ixiTZRE>f_?X;$BhÒt?} @[fiy,5vlSۍ8 *j'߳>G>mw+` ySE8d~ߑD%ޚmhI+ϊDQBUN6Xuwea"YVWz팝X3GpՙYle:"ķ!cZ|?;wIOuBfSd L d`$ b t?,̵Ho׭vG7#vR7MxJ~pն΃ηpC|FfkôonTj>FAbxPGY k? /Ѝ 4W #-xFZt0>9JK" )80|cVf:vffo#st0mb>SjA 7l4۔ԙĭ-tpJ7$?F=DnY]{Ax IɊ"ݴs 4?y oC`-#vI)%LǙ p|i;<@$چ r !)d2WK(zfɽtP=GN56 K Xq1.[?KEÁ/9w]N k4lxLԒ^-tdprrdc;hQ0Y] HæƇmz32IUgʬ$:տ}TJVqyDNй'Ȍ:APuPYf4,pH}C F:kR8ev8$$L$oU6l֔,z921(Ɠ |Q\UU,@{H.Ւ]Cʳl wîAu]`#Fgr9'K&U|nEbػk R߰[Y$dE.2Qv=}oG{urD2ؘl!<<*.ӌP na1iAڈN|ގX'mt;$PSR[O"`)6ٶT~ {  P,t.m2 gmv8T: V]a&5h4^8t[s90lN:8+%6!H/7(RbN<-I{pmk);ݰСrxz>gEmɡ)e1'IQ 1Ew(5mM2sH_~f 8ʖ;~R̬6 _>^\r_5YJ}ē#a%zL0m%+% D%**P$W,c [NtAYwW.&{ǃ}PwTN) a !CׁA8/&^1v辅[z0'Xn[aְ&εgv,0)~2 uxtxΉ2MۻTJ#Tq;1KlZ~:2\lcr4&kv/Q!죦F9=p0 ya7-V8\xPu8j9L5")OW<w*tܺܠ!bЙMzu^$! chSÜLָ* @FzOJVlj)kzd} ˍuhhӾF~7/jƓyC7YÈC1R!ÓУpsz׺7Yla~ٜAJ9"熗B›%k5H i\R6_9i\G#|^d4L*P^4J Ӳ *B9^lq QN CADLp'ַ+Ğ!lH߬,h8~*Қ Ss~T\@Tلv'㡻?wЭM$ctˮӤ&l X@0+3"T6[PA2[H5#Xw$Z8’lC)wxd1eñ P= TXtI36Nv1 Zgm[~BPJII6,I |kгfHmQlNd5EOǘ&IΊ-MM?CӢ9v}zV|jA C_ N["Z3 @Sʾm~ z<Q& ;8C®9oPCa` ßl߅H# aWtﶒ?UKx)"\a0)Xҷ\qE NFTBxq]NCiGXLe 2֟AHs^>\EN8Qɥrr9ʂ(;LGsѸ&JjJ0f T*`Djp&BU: R}a4E0h4Y`@Zx!;MMwV@{bWh&TCR1w5J*#\0C?st);a9e >t᜔ԞZt{_عdcx9T(fc }" E`X rMߩ,&Yش ?0ת+w6sa /erh(?Wy:@g-:%nѰ\Zs *D WӑQi:)v v u=^K>&?5l+e-lʼ8 9$ĜO*ͧ l1c-ph0,GO,\,q*E Vj:9%0+YgkeW:@>}Wb_`h@ rIu@T )g4`N.c[j1{HWB%up:x@G* %rUP1EoH k$;YT7=֏$h EA#^`ƳC3҉U=^kςn΂waG?O)CuPO S$@˫U< >~e5fv7C9 h(`%< B01KC! ag| 雍@̦n3 ' 3Pv<В.bZc'#mxX\z SUTC#|ѰPT0x: PaBafdK\s52`8օ6 5YY*GeХmU16N6ZiCʖ#q t$IsVrjsy~.gyjq w)o4L* ?Y;-&L$6EM!3mHlk+C)>Ie !k#nBdŴF u`ԑ*D[Jk?\4SKTnQ` 9W%Êp> h}րl UI.I'!HuLF[gviFpC.|ֶP9Zx|#󋠉̃wq$Y8 rΆz0`@KvyP}ݎ7uϷ^b^Q:\AJWώ+b1/f^,f6 23q<Ι%//77ycK2@0ɿ?`h 94 MB7p13\|9 gSSjaU}$-o#4G+7Ȳ[+L|ߏ>;^1!PU]^N IYmXڡ6jD>LG"\M._G`:D(=7{`67|݃76nw)_5>-gpn{_ƽK : OGK_a XTJiO%'CNF 7d[9}^A`[a,.&0mt(Q>c>>Uw˟&`J3=}:>ۯN޶c{_ c"`t6Rgs#PE]b:Ty#'}zkˆ5<64ZJ+;~4ӿ$G.>=xqˣ=3'1͠0az %Va)o/EvK Zs7hZXn$NpTK\NLؾ ;/E9/vdFwf:s1an9\:4קe۳=O @_V:7cNd3Pn`Ec fWE(_tx,ҸSࢋ<˅;}?;N{'{CCB 'vg;QriGO'̍ḓ BFkePQ]35ӂ^.McXsPiL`02>}yۓc:2ӂq-RCjO@uYaXz~{9FW;A^dn(pjK(*K?uD0XQ˙DrDO [oP8ӓvce?^??L64{jU尐$"ٛQ@'C#\mc/^}JvՃG{K ӓE)%IUgKF#Jf\$yllOO)܆椿hU$E" C=h 'MMcCoKW'{=݃c۝_|qFw?}yO# ^] ![1BQAR4`W  u:zRܧ`5.S=˲ fwоk+ΉncH̆%5jsS dg+=@= DbFϱjk ~VW>5P ں&=þ&r2\]v S[wj ;pl{Z vY F J/s+ +)]Cau/YgBÚ _4 7gvv͎.}_c̷n( G7DXeql̋{1!c4+z 4X`+Oa)3.IΎPű-V; qUÅEP৑FvEֱfqկ!Pa`{Q0W 3cƼ݈i׼ŏCB A=fvsCmm,+8N2Cd:251V%[vM$$\1p_>hGLpY2Bǭ?=>O *=;Z!l ]%,㶜?> k|&#l_'-Ayi6̅IZJrGSUW󾱖좇9%Ҽ1l+whB=f)6_aȤEGpoh3 w.E%`.M{ 8ݐ/&VDbA[q.47<4V4)XBBR$i r#x0OefzvKDr (WM9Ɉ #hH>mr5W<1U_8~9!ۜݔRT(0\l1MMpU@S;_X!F"v4&[[A7&H=fscZ p%ɬǍ2ߕh2e62xۛ@;#-3c~Kъ_$8|G=5}0}`HS۠OyW6'.!N:)NG/^@nE 2;Yd2/3/XcD 滖x(d C4(;5r[`++ڥԞo0hb ʤu/S,u 2C[I&fw=o$8|, ȨEd̩S`fsU'!"0Lo$! ͸ͲXİ]^d!|庭&&>kkeA)ZPYMѮ4kt9X+$լ]L:Ƽ>Faʈ ́/; L>lf $lkC1̜*N%J4A2SMRYcxy.#pB{{ ,F:Ujd*X mJ(@"s1e@Kٻ./?:GNj=7#8PI.ƁBſ.sE{DnV!Vҽ$.t@NbN`qhTn^uENѭZhRvU#S?P CKU=rEΖx¨) 'pC%ZAVpSڔ:D9Q*6T,6"VAL άrrv{]:pGZ\X_+/ml|}d \ɆSbr{^@%k8KΡC`q飴bd"&ksX=x|lo8dcmziP#wѽ'Uew @k^ krfۑf܂dV5T%/%G }Nu<^9oP{^4YXH.tRw*\ҜκcH:}8!3DˆCs.|1깲ڔ܆$!V]A[Luo-/>4f5# 3-8SS3zjDbJҶ"Ǐ{ޤB 3zɰY>V6TxC=2g]M̛:.&VP ݂wo,`<Q *g#=$!LcG:3ܶ܌< "k5goKgSg>1KTa-n̒oඝna'}K9Aœl,:dYd>-zI$A7rc`?魺Qm,oRQPi:>zxxU]Sx@gtkAQP:Sf~)Z} y-V zy6d=urZ!WAJp}K}$ \F#ɴ'HdXY0v`J89GS a+0R140+ǒ@% IHU;F|sΨyEƽႱ&p @?S~Da(iٯ2<.IAI祫lwkjd&Ã| ~4w3ĩj'v #1ʼ VLZA~"|^ `eU~û H7Rr2G`?yw716pC˕j *rMm6P!@7alޯnU%n} $ !Q<Za!~ [Ov[(#i})=tb[OhͲQY]s3OvRW Ĝ* ;q]t]hAtH0( |ܺ`ɩ;7.K}/s:3<Yqf\'U90ҟjrסi-Bc;؛+t#eOXj5:+䶳i>ň6TCdF@&?/J;J @p7¦"qD#)H'w"<;;}xXK>+/SI.ř{-m}*<\@AƗ̓EY |e8[.sf5ɤ94r'=p'aU|`%[ Lx:$E)%6Z!Ȝ8PVÔ 5)Ë!譵8rd).l4M5Oǹ8 _NRXuì^DI^vY>i$+ ?e>=Nv.zӑl5^030]IȀZmY?8@~60s6$PD9E$j0E+5r$riDw=OW?;jt@"uR2;{(&?eA3²#v,kɉϠnmE6Ϟ8P"WA/l}*NhDLz)P{2l=EwlK %@S}ئ s{Cn3OHk kհ6CY.Mz-9~.R$i(ڶ(LUW:pIHI`}yld8/ocp%÷0.]PL] d1~ ftĊ"LU5^9$:=A_vAA);= 9 , ڲv%Z 9k4⫌עL:L`'M)VMRmˡz=@7Ry`$b@fq 4xߍD RМ"[,/9E,|72iQf?cepSZۘWf i$y:23Yr`VQ֝VNZv&IM.H' 0lVH.wdy"Zv3{toZL 0t,VC$߁z>@$f`+f* Y*9+E4'IZ8KviN4_iۆ6lۇ|:e =`6{QtdSOI5',%bx&#tq$l<ދPʅFl|R3E9Eq'̋݃eЫ\ +,*BލZ`]WSC4 1V\uA2?k,)UǼ堰[?U( ϊAe17z/"uБr%T TP>X8ź$/`tAnBYEg9mktlWS"'"g^*F!#!Y_j0j[甬,^ }?xqARPXHZ$'pO6Myk rL9ޣx;8+1ؓjZT\b tA GMWa앙Xehj6c?ć.ں˃CR-abY$KYGUvxr"I2E^ S=ID([['{. rH@ N"Itm?܋.~|&KjGb?2? n\4xKT C=:8wTdRde|ɘ`XթٴmCo1;$uΤ8/d8cbX BW|!M0`15аb,k`I86p67W46l`7sPtb \kކj3\ PV&E'Yrр[!7PR;,݊{A `Pl=A.i03J,i>7eER fpovۗ}D$׾[|3l6\34n*$?7mll~j4c׽\$͟񣟛^)⅛AQgrl#ǂU "i$W[Çd習֛7KnKo%\ C*7[EVh}pFʑD;H7RrEKsKiD4yX~dNRfsO.thiKGWyCvGd^/b;cy@I0jٝ<';W~ʝz~~suNof*Ϭny&lٷKD6f&Oi^ь_74Oky";͡(4 ;촱#iwCNNv,o!uiNw;vmo{M"h.19Nz !9z7R"9A1Jˬxocwb~/{{On+rwp j9zP_`֗|~ty no'ܩj}_S?^|b^f~ @ݑ͍XWs3f8Y{ W|0淳 z|+|s3_͢=~;)?d:g!p_jJp ~=8zx|uDJ.z|p׼.֞- |T(2yZ?d܀;0 5hpu jTaie'.@| bZDa82Q^h-12JkTXwIw80ǹ#+ӤJ`½8ؘE{mǮ'2_LEhB'̕CݒhbAnbW9AZJ 1: ,\kl3S&V.*o|QYco(Ho (c²"B7[%RD'$PBm{ZZ!4Jt7h/d -'TP bx=]Wr\~rPu$QR8C\ECW$(br&~þ~VqU!q-)ݜFȞ8ւ>wՕ$B`BB[=\|D) }yD˄3Đ<`M%k_uRP`m6+Y19&: [,=Fˎ !9gāzzg^F~;~?;>:4w(?D~\=*'SDv<DDa$h_ c(ɟoٗbKG2Ǵ>Z`*Wn@b\:9pT '\ZM &j[*g>rzLKhܹK>_vdߴi>A]383, Tv<`;!"аH2tD;4QZ=(@2ā_'T95X3Q554ne[$D*CIMi6VXDrS)K!"x.2E>0pd!Е$9&"wl̤ERv: M`M)\:){,&?UOh; -]U@ym]^Pyc0f|قa)~N ]{.RϼfRkp݇Q#(GxU WQ.zwsK9%W\,Y49한pL!K:\K6;o/ژ3Y,f }Z׆snd]ɸZ\ -{0MMSR! ME=s U$dCZJ*%`g,8S`f@KM[&mX )qn?Ϯ (A~!Q=ڣ {ՊN;k %_wI<&_m#zlTC0Dzo766SNssEX+&##M1}dg3EP;c# j5 .zf_9go ^!yZ;7̟l`)8\=,($U~1ٶ}WN׽ܣ:eFĀq-.hݵf;+T)+Kl"@[[`j_N7yn >#]\~߮SC=7ta̼F"Tsoq+Wn!8FUVQqb~ mz/vGBBԓ@v{tji5OǷ$fۯ n࿳|xvV;).q8Nahy4o' (&04D ?-3l0&zphf| T&׋%U }43su@OQx;3QeyJCS@o+è]𢤦3Aݧ'_'‰f,SgrXo>&ztoU/W]h1^=EqYא!spUہRۛb{־^)Lc/g3!I-T"ÈMp_lj]olBVWg@LRO;ԛ~SmJ,hRNogfUwݮC$JŽ6콿NMajsQ ֶJtXJ z=3!jFZinnpnRx\G  N.lK`چ5[ԃ WَCP Zk և R˾P\k4oƈQY| ^io5~,#)B_ ?8N18 X7~qoYf('cZCd~l7wر-ʤB+Ss嬎Qc"X~4Ldkb݂QnNmٯj/l*)Ӕ@摣RC[BvUpn4( Wۧy^'w 1* Tpz^oMi$caK$W@i Y`fA%H`Wbܦ/ZKrX,gsjEbLkE-n0Ll+/o+A\0(ySم`"pydsUq>KJuV/ӣ0W˴3^/[q~ W5}M%Bp5|1 5|}TWI&7^keψ]LӑLն[*U,J\!7/K;[٫ IzERTs{sV y(! U#_"wYX(N.Ļ^։̩V<鄉^oLJO>99tlIn)swGf|+) QWUİvtg^Trsɛ 6OTtmFt%?tGu@I=U_Se<t6Ԍ `Q:`lt]?;LAΉ mD$A|<3 GZ~OZ6drM滐2[5lzgqlݕBeκYu-c*hҠX"d/xHF77rO,Tu st%K/M@vuH9$Ko-N(2IL̓> m- 0#J^QvtA3t=kxǭu3s.lFN렆1iahaB"3f xvc߃N)nv.gA0a40YmC-87>SAIm[|Ln5z5ĉ޺$*^7-8ZZRIFT("ִLWgSZş2kYIɕ2]g Lj"^c(+*\g:xxMFoSh:ܕi1t|.v ]YWGį`Kyr 5VEFUBc(E|&mF WaN1y <6yӓaAym3xg{M3;1qg qBr-DZ8tEOÒ@MC`HD=@jWFTD @K;\e\L-on yc>g^|gN;^ڤ%9P+t=V#xO|*%X tgn4^fc _H묫XeoE2tP u-#''pۼN؋ nCѰ[M<6sbX+݉ suY-oIn#7EצkSdFHC4^Lb\^$rXH ư~qh%F"J@-<&O~su~:D=Q Խ pC0BjVڝds SsLյyykAgG/P2NwAc}i8XnNR^A3MtD͡6$'4$E61V%ܱkn [(egWd~ŐŵORX3:)տf4 ѸH$\nN:j0vWLS)0=E0@fXwJz#883 B{27Riĩͬ!yV\Tq;|{fɕz[qZ^ۯo^o~Syi}n^^A5$+ʔ;lyߴp <j!*=F~*Gswֽ>=`%M: E ꖮ);'ָLyc譏dWg0ou>!h`fKabixH3V״[(-3 <O -"ڪ<+êNpShzms$ײ58X瘱xF6Sm2Cwi4sje_sg'J氜v8!b>*0f}PMi+wG55?vp*^qc"KKZX=ǧ+ۣ0__9WOBk˚nچnH,+f*zo}m AB#ndo1@݊7;X3&5f,XU n,XэvpRNv͵鬮ĚLYwǽ86nw׽IAws7\vi%:vViT+Z|0A&W*3V sc'Sdgg#e}3_>cEqvhV>U]9f)d c]QeR>bKnHslA-VԶ_#tY&=۳Δ KkY|j0P:8v(`MT$A ]~Tc Ǐ6giZS O]#EtI.f<)lGM4bٽ;LP"L\.LoOּ_k'abg.y7u Iw;kYSM]m]m&D>jRkf17a`/c+ ֺqw 26 gmEtŰۺX]_v$:ʎՕ5L}lĬgja[wcrFlۨxb\U(8+_5ך[a łq6Z;jaR? >4Ǔ4[͸F-IyJsсvik``)*k85YhXsp;X`^#|Q#7n9U탾ál ;eDkntf$U1R7>TPjeg{f` mԦ-'- #MGN;6M]g@ )(JՓHO~Ɩk7f1}ô-.X2DHp2u)7|E}X{8Y_QTuBCI[ M맭֛o_`ן97h\=dt VU ͳ1{d{'XOd6R\TjIJ X@m O<xf ([iG^V&b%v] aݰC& !3^ٰr]'̀~a>-e<>j:`wgMռ#_x%ЍZtY^Pq<`6mSG`;ubT;nYÒ{2@yD;ktG;&6:f>mL~ۈ(ΚMeVP0%(\QA&i6I$3Qљ(0_ڲa+& Ư5v Mx]WGTR5J) zi,sbWmq~,8 rljjb VNQ@xr;24%sžqfQ6 CY ":w!&@Z_^} ƒC.s\ +/WCRm@=[ W+]SW1 7Yq0xxf'AwE#ϮѴ^Yg>f<0D 5g^Zkms#[G,HS&`;xi"JvL2Ok5Ȼv3 67\) 4ۡAj*mW}u> R-@%Fk`j>/h ͦkMƬ Bvq// z\q`ۼymΞN BC<2c]/}IB_r.@tp;+Ƞ2(k vMxhnI>9a 1ILEN{|d+GmJYB=3ۯ,Cb}Ǎ3$n^n8,u-pVpsј#߁0ʊ-6)<¹KYq+@V>{%+22b4K 4<k1vtST:T2ߛ4SX/zm[21We%a1W9:[ӎWɟoll_!fTP W:T6[w357 $αG寙PM}("8Qw^/%-Td~~pW:SYl\0+"}f?irdi&]H7}j=M$YnNc3|?>>3&!Q j/YtIly5iMwoi{hZ՞9@ԟ5jP͟GuÌc$s!FU~+wF\di^ޔ hžɹX@ڈYB8@L˫-O hYС*K(!H]%BA\;YYZɧV+FCrY=~iX ,a>EVLe,~%'[r1m(qCWBVfEϔ{ɳll'ozqU]T :(jšjU4zY1H,k%4![Mf CD+ȳu "[LU!EG:{O/ ?1 HrVgg1C2=;98y]}~~) ?3| Gم) (O>/Pw115;V]+|(.xQ1pCE okGRSp*j]HȗojwVD1`NTí( H`E$ds gC Q>uzhqv'ĸ^/sN?"OOv8WH?X;M%"h$4wҍkԏ(:* GL}p?7 ^?^!rٌQ^-\Ø.-ᄄy`em<Mj_OPo.ҿAlOӄYF4oeGm_Zѡd~v=>:ڢ#P;߶v*OnFVh&T6b_2x'~b^\2\է\cmw-m; Yku'ys͗PHzV&֥l. -׃v Q_ҀB;ez٥n{'KY w2ɧzkؼzn^ɜ>\*a&H&Eńt<&@P><:܃@@1 Er\P$w"ؙePԺꭤin] V1K<?DUv* 4%aʶ\G|j,!-M?|y:zBt̖0u"GN=;"6( If/\\ ]}Ǻ3Skvϋjy=B4=uby$ķ#NX(=̮% K;` #t:@':SsAf9ȨҦ 6ڌ7C=[ug-RWFKLokc'@\V־*zyqM XQE249W\SS$uU:%sˆYk!ى?[8E"e;- ܮ},@kS|G2^u6~ɼܓZmGLKTfKu[ŇlӸBhuםFnNX"i{{(P;Ƣwů5%+t.{o.8z/|)*Eݴ FL*2pt^oŤԀ76#:Cd}w/95ZڱZ`bQ AeqT=6Y FhufwMns;v׶\d( 9`632y]^礋>%6s~—1F,dMY{.Ob>.TmY9Vnv6U>n, sI.!;ޯoiS #Wݘ߅:BD蛧+=:Y^HhCJ.C[VWS*AMNs(f_}ҜǾQFyAsc{֭-B7_0.klL|nß5*]}>T 9RDUuИ;DyDuE̊-)׹lk~8X-K_`fYq;3gOVUʍH/6mG-!~ψ)q/cg, :8Nwh1U^!޸4sjt&eŴU48I!T w /Mhu9aK~x#\d+(unM?^>J&z?Q"fd/5z _%ZXD^[̤^&+)/m^|~H̓ \C1462t O;)5 W?bJG {Z).ZTcXRk;vT+<1IY'|mLSL3w#"Kxs&B)7瞐Vw*5FOK!ULUt9-N3oALUm&P S2z~,UfO^YU|.UA|cT>8Kn -ԨUI1_ 1>MzߟB%/FuzEm%:=+&ykoV[#07,eŋ:G×kޝyͅ\{hEL3Z:g?!Ql&$p[I$ݳyڳJ \flQ6O㙩+zU2xb{]}f*h1ͲI6¯\WSoDM䣈L2"u(f᪗߬wX޻e)r S'#~=5PSvnOj*9$_2̸SFݳDj?_8B[qYŤW_˼^MOMufxb)y TDkj \dAO)m_!?ٯa rksA/`[{EԘA/ QYQt+6z(/(L͕.ڸnX$DO~1((cM W>@GZ&eQ ߃k< OM%9Q5[" wMn  0@ eдhR<0eq. 85F`@ZzlN )6E O`013c " M)_5X ! \wԾ l)Q -0H&6: \/woSq ڙP]fNJU DqH%bgs('P4`o>)@Dk#hMnoJ›̨kݵ&EB>PdǗBQ{#@wWȴX~dj.uKЎ';sfR>:u9]hjâwOI4فNƾ5(~FtEォl~"ŇiLqW##_O 8dg_==٣e~;{ wvtֶ~6y"(}ǽ'ۍ]{~_>>ƫG?v_ܔ-~ 77=x? ~x߱@{S[C o<4K{v+ݵ5kL7__La׿Ochrԗ?5|=}hFX\?}qP郍Ʀgn{?767M'M0dwبtc{h[_pnݗuy55M~+iN͌ƋCySkY6{dNn<(L~0i`L1fg~]abx.ݷPv?к~hZ_*FE!C6@dff?F3y 7ᒴcቀ=a9l:/NnhW:eFϋyxZ7:-ѲA[ 㡗fr9uWu0}iuΙwWWi>HYn ձa<6=fwש;J*Rjgpן/i -ovެh)_A!ɝG5jC{dZGS7 *Ag|lߴ.K_Y]vm(+Jr` GL ͊P 4^3V[#ݵj0EaxCy%yd*i5ouVߔKd"r_jCϑuE}B-RorMG_l$>4 .i/:l~]X;4.ʇYOl,~.^ü"pN6op6~qMhB:N9>Lϕva_.fg'93`4* "=3L_[Lh/w2^(-[Y tӿ5W+Y|Ҁ`)b=aش1yV[N+;@22xw9L4$ 78 R=wHo;b|Y9SPtJ).v+mmӰgpfgX-$Pd#c*%!n h B|[lnJU (9EաJV tJ.eZ| رۏC>'VkWMbҗ ;;~a hKФ@v };9*fx6 ׽%`zy$K0t9&MX];9+ #(;_rtodeWp3O}Y[}<ȭ6ZluPlSœƣs !]sa G9.EOm,u8?oӻvhhm+JdscBWa)_E3Mhwk]]2P͢O~8|8v mBgIJ@VM1Rrɘ.OA~cmWnk[J׼RPW:mcv~ؾ{x: -=Vq{ $X\]w}ۧmf^I4:K-;-ck:4 썢x]e,O̸JY/t;[ {w}pae=fCB8%_+fJϤpVwK9%Tc")* 7b+aggFbT5iJ}1c}~vO,z !{LXõ\6|f4?13=0%->9:JGGv/-t9;\]&2N:+@ 'qdVVΏbd7%-εx=<`s@њ/~[7-2(JG(`LjAh YQڵQ}9VyH}yǻ釛g_ON_':/ ZE5#VrC}Y!-3w^1I(u3WUjǂJ8~rPd=qlƤwIV`9LQ͜l5g#g#sܻ%n3EVq0WDi Cf܆wݥ>T->@(ͥj$;7pl' Z,iOo-8{֗qZ*l-AK DI`Sp~7cތ)7\u" C.h ё>B)Inu6|42׬*"/#SS3@h%n3xoLJPTyrs[#yih;t4Y?NM(|xm6E_ _O5v2Rt}b$`߆zbVoڢL\ّV`J7߶-[!5:ɷ$+iXjOggЌs' GJ 0tZ%^Ѕ} b78 k^1Αt7j{ ߑ-iǀgH?E6VlnkC{R|~ `/́ihX4 750i1Ѱ f. %l ..(憯 f@t9&7eei=M)Ky Zvj;\vudoTsoн&(o叽ݧQMֽ`Hy}PJP ]q: [gH;we Za+7D|1H6NsQ~xBe6Vfؗ,0х`5#دQ11y86<2>~:}4-otB< 3-CL ~hkf&-ilIr |β F}L4OErjI6HoZ /z-悚&3تo^L7czf6A 6A8*J!ƹpDd*Bw<BLG?v rq؏ZDQ 㴼^ E- &Hլir 'E.1Up:Kёl.z}0Yn0o@)eիVuOp#4Bc:&bs=X-U9!B1P]8\`S#˯X)u\4f .9djQw~jPĜwJDcxI|ZP`Վ s1R'ej?df"d 1OJVjnbhH~;>8ݛ3F Ռ[2q hGnh;< s?~~dQ}&уӣGFb? 8ze];9|~xdD؝G?gbX~Vr\[…mf"Ft1b%doQ.Z,<}yӜoA+/^AHZ advB%gа4WHcJ7M^zHb?v[ ͭk=u?0_3Gw{{4ש ?&E 6cvhlEg'-n&NWyWYs:4'!8{^Z`KUZFtzE3ifռ>bboAEJ&)C-pLT 0f)*ƢAeDFԞyɭ85nY׺ 9& XhWJa\Qj{u5m˳.Mj>$$,(YSWٵ԰7J]0F-sqN1A~qSj$M֜Ix鏞IݹV H+Ο6ÉQ \'vuq @.?N$-+0Ggn|"n?G򓟟#,L"/p|]"Ώ L౑RI)?/I[K/`#7WRcxqUCE檞CTo\Obuzdwr_N_ǀ{?E}{uxNNŃb%T_ƙCdѣT܂T5+Qe0RTivhjؔ(e}x]c?7/;ѵӳ > ~iqu&?OnL+"1s E? FH9Q}OkY j^2k 6yl;Qia>I\G\䓞yNJj|a^B*ʑ8!``AB.૩^Fž^AǓ!hE*q%(#3G\|n0':"9Vc+xXYUTn+{^y\GTg7?Z*ӡB:g􌷘a-Iof[++3W붣N_byX'?A)w?~\xz{RQD5!MH{ܲZ[gޱ&AUTC}g8٤ ]_Lwᕌޖa݊[h1}<4LI'$LHxm~f0ȇd؎ UУ"io{t/na,䪸mMr"&![yb'0Q#g3nv?NSUgs 47XƯ$z+wwoyC=Ч7 g$ &/Qmv#:C&笭Dګ!gw`u@"nJ㇒E$p /LThQ}rF~ҵqm/"A w A cȱ_G'*,..F:|( }7)x<`]  @,T-͏זߏ{>9۳|lGg{waj?׻՞]efk#6HH;dwMK2GC)˖Lt#B, lv]S^r)sVp~w oO㾑/iSM2S7EwL+Qt8-ƌ)E)g|߹fL߷-s/`PDv>PdVcį e]=~pI.J?Q;^ߔXvrs o_Odk^{U $݁[H D3MoU마jmț7 0EwqGA+;jգaaSUδ"v 8]+9 $"'0T, Md t@zl{ӱƥՆ_%Vg< Zm l7HBX~e`Zѣ:\Em+CŽk)f\j"#2 x+cH" 7gƤٞSAR5̜ ̅̇k~z ^_,Ȣ3gkl/R&}PFX/b8Qm%͵E3x@; S#hf(Ez` l $*=1#rƹQK!/Av^ D.s ]g wKv>4S)pܒPW407i4G_fN1 +JODyF\UwcS_dKnA$YwU5#jr}57qMWP -HoseRo٧j q䮕_BR(8z[YףZ>i5~:Sf<$?[se7& Y |H 0DBʇ޼x۱fOGk n`6Hɹ ,$bSaV̅򿠎GQAV>gj:8jc/QSt": HݷI}!1X9"vU f W*4ۗ.k#gH=NVO ygP9 Nn'8}Z ]VM (2i㗅o_|l|o۞3Uh#US a  ,Pkrj>(~F7i:z'o/Teu̥* zwt(43f~sɩ3i?w>^~筿,39̼ՄɍU V|wwk|RZ#=V*bn`%Rj\|i-ˍ~  XqvX'/q:[kR|ɱʧEnl:ַl?19:uS]}Uҳt9Q.otZ )x_Ȍ:-qOnllu/U,$N!$v䶲Ac42 ~kG@ؾpx#cO>:`A :F?.g9.0~|\i.-Ce~`kM hq2 (e'X+ 趼{ٝ[a72+!g ;52էd"6R(4>x:Tz#ۮ\4cm7J]WMtl_x+ю%/%8 :J'`w<$gF\@HXcʷ%%PvmQ=5kJ\.w_8 o : _tdj7bPl:1M\`2Rr`*ZևSb .) |L OR? *JC3(ь ٩|`/Qj8u#"W;9K| ]$,oPWEg_I~;Ak4("PlQgL_NyƆY{I^@%0 pgi'+K_VzBwq߱4nm9-b׻lϕ=FIex;`_.6b"_rbp7s /Ѭ@ ̶r`t^]uR9O>Ͷ~d\V}CV0S`>%$+=A8]3j~ NYȟ6rQ ⒭Øu4QwʄVk&]( 2C8X%{&qR / ؒX" EBhs,d8}-߷"GQDV'˛{BAoIGQ6p ̒qdcۋatY)TmWc}GPzc|x\v;/#RzwMc[g~G6HGe/D8TjDj.TSǨBxglw/^>88=("]͚O>{/^T(2.(?B@@6x"S6 +6"8/=8;(S?Q{E 6gY->o(&:W nI^'Z6kQozT3;ak3_:k:<ŢTO 0>t;f^bWu׬.:ﯦP)-S=SLUqM\n)0[Ȳ$yE[~P/Y' `SĢbD=mGuz&WMi?|3UHW}wm vڵgvԎ ư䭈?_V m6HKpnf ȢTzV| [eUsߩ8^Jݑnxœc΋ެx뛐!Ntugm}ة(%` {yߞy]1sxOY;иⱑ~qUHWn贎Zq ^,1꼻F-cKNw.*$v"Rk6geG!T*xr{zޟ+{_(Y=dAA9Fsl=Ķ/tDј zl'ǚ$5[ېl;XiqC/xH-Mݐ2l2RiWޢ)ɹiݫ,<|?r f`9V2*F~U Akp\}v(dak#8!Ѷ'oߙ#zLW*dISd4d,]8/Ŭ T }+GBXcUs$:X[̆f(g~u/9A80u>])!QE3 8|h26J gt:_3%P]LMt0ClO/!:. ]|O@1(^bSz#3ԋO:L1p Q!^ 7 C)lJ21@O>$K4~90m鮦mUmvTB@pDzL}/\bic[b1'gbz BE6qLAp>4嗊>#qXH!~1{:L?HZ^`*15ᒃ֋-VG8;rO.0>/re#oV}7{cH9*郟|ZI<ݙR>R>Ü0FWm jR޳32' D7sg!N6qAP*Z{qA޲`~Z(Rߩ,#Eٳ]+߹"5dE8yJ=C87+Y$_1%9g\{ƕ-5:H$ۜ0]Rj IlӉϳ܏>E5 N=^BFhFD}UY2qp.wvX#e5#KcM9֫644N(I2/eΜ_iFH103iL 1\TG&g5wry-})5E k, ACGeYi2?j# FFC׭߳pj~!*=3HFYّv4U۬8,r2l8nvf|=. `tJÕQ$)b}X+ @} uv[]ͤ(k%u3WS瑾F@C} E/1k y9td:F>g"'asG;8-Jcn wHc~ᬄSz} :%G+sho-7k J/dwuL$\AamP*k_LHSaw ߁ 7S2AVMrkYF!l^~qe 硨éi.?L#ZiM1 ?35e/EEPx_m׼[d}* L܉fZtԷX=8_~f0-]PQ)Ƙ?">.%X#|⋖K%LYf'/&KUy非pX$+tDӤٴDV*o=lRb/$4/Y!fi{գ|Kgna6-{07JsVa4yc Jne,㚻W|=jrI/5yv2`1u$߅R{6f}sdPݥu9gl ۹gcIAf:y=QejNtrL@ɤf\OQwaFl`ޝ-% ̲˜dWT(\kkZ'PV(l7)0D'vfnvAR"I;\4ÛbCWJ E7ZeEN3md qD'?*v祉MZy?2\V+ӵ_,E-Gq.3-3kT(Z,dK5m蒼:2` mƌz`2LlĠ]ɒ RPܮ8#]HDK\ !pv&TږEoWZ8ɵn$C$R/.8Sdən$J7I-7ās"+pY3ոDbq^/>yh{^wlU}`dDx+=D؎͇["ybٙ ~r%Rcn 8xBWsBwg'*ƙ9:pLarؠBdبyV$/;N!|uxEF^!g:N$0Fg~>wf#Qw>E 2Y˪#2P]H6:;v{z&_) d- z/귝KFF;dXzҷ,U>H&z7v̘U8[d.Bb dpQ JoFq5Md?]i{מּstp{O;vOv/yw4 NZqz1lme%-F#F !ŨM4g6ѰvD1N1:]V띐Z)h2#β#ȹM`'Jo5lX745^mNtyآPF69LQxQ R0>i)b@`d826O%ic$tY \K+_3(LC8QŧaM%TVP6tW-3bL$'5Š1\SUcx>e킃ur[\)%Ϥ1ĄsGy}܄$v gv=rTʮgsybqp&:V.0Aý}uگ5e}MClCI$>ˑsd"Et}|U},6`[z2ĸdpeba\k/ b0wi^>Tv"-$Sթ(y 'Ζ 'X$avBJeCp[ 4 PAs'[8LL_&3Jz3]%&ٓCJ92߼U0*h95\ʸ]-v<)m$_Vpꮟq^= % Z)] |'u-QOu耄 +J;g//K Q0wȹo"` 2PH-H#c J3){ۀHQQNᓴp0c؈R0*-z'Mp!R Z|n{*&Y @6z]I>q+.NHZvrL/nj&fokV4kԑ6M&^1Q@"t &Db$q;Xtg癍ɨ&d:y͌0Z)*5cdޘ# q|$?Tsr0gyRU]i ׮頜s`$fM":IDtAATG}ˊE|]d,qT慠K\'+-zGöa7XL\G߱f7)KyeuI1qΗ&hGM_]içd0(Rh5e8}?Tl^AC/I!u%e DmŁΣC V{Lr2i6bCcx:lA"&5 L$x}OE޽x8G he._R~z3%'Y 'GkcE9QH6A@v寏ojQ[;?S4 Rc.^?%l5D0ZIBG8݊ *PĤZ#TRЖ}]=7Ͼj<~RFfwp 4RjyL]bhl`n?ݏ^#Dm[-9Cpb sOGzR4c$Y[&HIMe39wuYg'2԰w)X9V/EE-MMv@ĶDiWI TP!d˄GYtZR6%7jL7usn*@Ϙًً:>?a![kH14DAY;)n?<aB'(E/;'''nm+:o&xv퍼HF0! Lel';V]RU e| z$\@1Ny ؤ=&iԟ9~P7$jv On] rD!]&0?^H| #hi$E#F8$ѡx&%1`]ph&IW:wG$iԩ&Mʳ>]mk`"9ty:J֯{ݮQ)g /PH(j2mbA+Tz:p>J&Zg (c "qKJKUp'Rf )[V)W}ع)da3na&ƊSoDh )M{UO5kVgU,3m9X0j g9Q-{95TGUFR[PR;#(FƠҡ12w] 2*(iɑ'4jIBa(uNF-3e,Ù:^?,z)jA-bc\,Su-vmDf^›y["*'%/8UUgUG`3g;o2`LԡY:D$MhNvr݈bTtvoC@n7q+.f՟Nc 5,L]|O(wƣ̬Ӯ(V28*SS7$MKJA|cn=n7sdɬl9ŀ#Z|/J1ߕ{ DԸ^wXʏ":GÀѓ2k?ĜzG#s9=f$q)3aD!r;W|'?[z)L04j~jݜM.fA礤QXvxp,vUiW|? VH3;ⳜZr3ZCP$x0`; *%[D=InP>sU,o,$ &{DLv<i¶H${]iR%Mh 6D_V=N#"ʃɔ!'`67DBs{m=$) HvOO_"VQ"}r(= d-ЩK^ISJ6Ao@=DaicN;Տ9C&Jk]s:ѓsdp\ YZrw'uDu}S fu_jwΨS\# -F@g.I >Ã8ɏ&Nw06nqR_sk}crBdTmHZtΑ!E%hf~/,5jH)WXML7z -7C!{cGPJle[_x<$BtB{N**Uq R{KVĤ&f?jnxnedoHMt Ɉg@! >ݼ8*nG ^)uAߢUJ,$=zK9$irÐs5rsv7GMU*rc.S2l.ez3qgȰMmڬn~12Y#MeV<_d*!SCJ~<Φ א![vXiI{Du(ݏb Mn}Σ)4$[Bzw;G@.H(!UӟcXX DZ57Ӣ`CbţO69 ieh㡔u:S  %d/C!Y`YHֳ欕iZ<-E$x~nfHEG0e"cctdx0tvKHI4Jlty>sS7*NurwD_z$()d:x2O@* H:Z'z͐P„35wLbX#}+% NZ nib?xNjw1D(b-smrP 99!h7ɹ㌣կFrҤ{e&)veR*za?$5]=1p r`Ŧdu M|R ̲ B~lgHAXl?#",WHh<=:Iǻe{ 7ML 9#7#KӍ]ڇ: 'α9rʷ#/=? kdri ?Z@YN6dkȠtXi^F,3XO,P!ٹԻXc|ENoS7@q?nnZn&6Jc93J6N>ͭF`ct^V#ҷg4'd SCFF#no*r{瀛5"VUF h +' 3L"ҞS>jԉ.%Oe#:1<[C)d`W~>f5-[Yn>: PJ`澥n84)25L-Ls>)7G ȨR#D)ԝ@FO&CUjƤķ411Q0ge Zj',KE))jMfQ,urWD )y Ih>,UG,ְZUtSĚШIw0!fuF M53t <%=!qaB7S6Δ@mcj>q~Wp6[pE~G~HD'DD0?^4Po]5&\w$ϻyMoŵO[W*F%˱ 1Q9܇0ysGrp *DﭳZB Uw`Z%C[>$_BvI!q[_0C"#_f%Ơ^~o07Ŗ#AaFKz]B8>>hIMgGT,j'QMAAAM\=Sb)ΥR^9<;ЛSCК0E<0<ۧI= GmKxqfN\O=8pp湑y꠻iĊ0dk)3paz}Ԣ)|a=AbSek܀}cڎNg}N N5e@/B~ 6lQUQ`0$@xԍg >.0"' BmM-mӫʧ{/;Ι3"Mt"-R!Z-f7%s%ka` "o0;Cx˓L_:׎{ KLKͦMRt;*y|2 n N50e4ϋ,ax0ud[Y4ͯeiy Y-5/7_=F??oƜFXQFw|I7H~ṞUE&߷zukX,j:!؈ֆ5++ AV! ۄ`4"9< &gOvѷ!Fdv@_`Z;WBJY,~ᥰ dh48'ID ŭ*뱗3AkgJcZ6μ Dwƒ4Q0~HT0TLod/| uLJC1A+TI!ۻ#2:bjsBTGx#&71?nNOv8msycƹ!?Ab;鵚^+ʾ &k&ID?U[@#qP, @.Z>KrYF>-`y"1fjV#=!ˉ?-C"NVX4F#vό4{!U ,50u*yGFP%q7b֯0Ɂ я7ˈ1o;&Lԡ &vQ^6>60VҭRݞ$uyVhkwod0J*@П 2$ChEuya8X؂a`ҩ"^t{*O-(WyDӋMvYD6Y.МtL'?(67S =Lto%}ʞ#"tL8AƄ,SF%_E% 5%oCCl ݸ{֔  }G П)j+]BՎVVᡲx⦃.?q܇| ͗yσ8G (ٽ_}b2Imu.qt~{v|rtv-?@@.XUrj.I#>XylZ *y`si\I0+f12hx|9)cGmçnBV$.8(S8A3_8mddT8n??'p6TvBb)?% ݓ̳E)YТ.ܭ/#"]1X&5!Z+C{!Sh=m 4ܑ|9K-Җ5赚YʬВίO'{/ ?t[ )`->B^N0ƛp0p" `T| jz=?rIuyFչ>ߓU.._|/ˋ˫K'𾶴T[ZHoObm߭)~_SWAomA%``xE?DBKm)=5t quo[ T =="͛Nx݂" Æ= 1c'VT[MEt\m"Sٟy~us/ov>w<++翶/E8ړOfK~c2c׭&Y=@BSx%YRUȘlnwJj `WMc^ ~dl߽רǾ@{1E@ zޡܡ\(ռqk ( Tߐ_V/Ehu<\ֈ:M|R=t*cR/0$3*jW>-龤2r-|'9FEi:=~e3H]u{ң50ܐr=~;{uLm/lD ̧ |D .fݓW_p_G'j[o>QǯONwaAO#퍀 > aWO6c JN~{HclʊM<>gϞ4eZja=\|Ѻ^X`[ťZmR[^)taAYV%պ/~=o?<ɦvO6:ϻd06=X6_'_wn;OCt<@lc{<jP`6构O%,Oh |zU|}{|z{ufEV mf< 7 w`S'7P/ɳMe:6OűΦ_nIj>™y9~~ߖ6]1:JscgZ_ĉ䧟6)U5~8wfBͲ e=$ռʀ+@Є9")!a u6̢MwMjandaljͶȱPx6 GC c.^W%< h u<)e:o[MhEf58FScC:AG‡nb /_:^+N؊)0& lט&H|zwa;b-q<*gl M6ODj8{[>Z q+ϰ3*v\nb5~:T¸iF@|dnS;A;\_!JRs&PwryGP#^ioq7Us * %-4fU-5%e nG/ RiYkE@zBN?QhrUVve\ Ш)^ .uwtރwad'"T`2׉{ܾ] DSDL̢X|iCV*Vtx^ph@/d~Ux ZUs/gv7phO mt*tW.aNE[ GFgØu@M ആ2)sA4U56ܘhMLFW>s*QaF6Qwp.nnu\T,ۥs wEK;[i|Z56Oh~M8X0_&IgT.}ȌOυAG⡓ \EA.i8C;X@< 7Ġ: IoTqQm6[آmːz8'3ՀyWa2ԣ2٨!_ϨwCSZ 쟴Fm!_Mq Pls4l4C#@A!{|UD8C{ǝw>@ xۈ@rd NXɱ&THH/v4R5@qNϐ, ~6̐?l H?"AVG<@[9>TFG yѡ"0mF֜FjL5Z6D'RAE}' #_;涑5EUo"rCã3i@aMPid!t`Ӆ ^iҦr1YPM:xxɦP( P] nnwFXDrQa,1d 2iv!0B E]f7×}% ^ƍ"^"X2ϢX'S7ILrC K-m Y;.!TJH\֋Ee -4,sQrUv|¿ A&햇+DoDhUUmY"@8>L,]rn}"W$e) &3Rr.n  teL0drG'&f@sw S4̕xRuJ,]тo@ǫn%XQA3 ! ЮšwТuY3P|K)[8Ŵɛ:LWrI?":f]fΥ 7D֥H,1Π'DZBUJ5ƵEmߠ;-1kb[\F#=ZK, FVd x{g `#3>IN:!@>eLsx!(&ݴC$+^'DgjgE3*<=9ʻx[XH }4(+S=O7$Lh!4ͭj,@y&W.Bpt7A]ZRtzfwbr!I f`BI/H4fwCdd5w;݋ʹWFgC lրdØ}ƈ@f<ɾhf Z27N +I7 r7K%f IcƓVHCD TIE)KCa'mU $f>x[j>,M/{~Uc}Lo'[wKo8뿦C9K%+V? K{Μ.Lgd\I4y ?1TÅЉ[ܨ.a>kw3j[Zt*;qMGEރG_ǗŹҦGr\ؚ=VK7If^rLhm0;j.#\6l(IXAį'= Eg1N0̅E0g!`*II&A%Mmi|>Ν]'=Z$HIjH=S&7C L-#EBP"B2f _dT\M! IN=Ժ(Hb;Y}$5yit$8lv&93P.:Ӌ9; a0cc ٢ ~q{}Z Cҝ:͑pi`C gb(9#(^ g_!2 OQ`'Ah&)E>DoWIрWH]-@Q;zfl~zϩJ}x 6~Qr#V]B<Ǡz)3daVe\ٶȶC4d#Rr-"TTݝ|Ld`_vO=rXduCFL0]1XS"В%] - c-W6QX߸"/UY]+jY(`K w2u"sz 6;Ri#yd},尛 aTK 1`D}tE*7V \H&CfLI;p6W6yoF¹lCpC ,-YTQjl}[75) . uCM n=L1;ga2P7ۿGdЃX@`]y <ްϖ51汁4O$qN $^~b)5a'f.0i^4OGt7ƃ ޘ<]fȱczW y;\R֢f̰'K_CdTy %K#u u$1`P XL1(+75` urQ@PϲlwöF*k0PUh~e hZ ΍ mq ›$p&Ef#5xcHa?bf:%2Ԭb6Kn5Z fJhԺЄ𪄕Y(^'noo]X3?iKFes#YrjA,Cqx˩$J\_ړ(C8$a]uvɜoJp6VU/\WanbY5/PUmKCh$4Ѐ?;&Ia󛍟 U! ! ϻϊ<ɋrk<{}8R'nڍxE\51DNx;jFy"E*ceŐZG(yg oT{ Bl)R˰0x# *!;#+T%ߓHW QZ)%01bmhtMEZ\h= ~V%5`G͓­ZU'#: b:!A\ VFc1 7CǃE`x<e=~# l; ձtxZ -uxNۢȖE&w63ϝS*@b.4ߑ[1%1f h՘@2a~& F0RNf'4aPM3$t+F9AIBxǕ9z}xtdMvJf .Jl&Jq|nUS2 >1]u[Zd9p/z;m:q%>yS>,Hb*-h8@\$NL!zV r>on~$<)adH%H%#(3NZcuNf\B$4GW#PZ%R!};1$/bj匎솁(*m0U94@g9pk!*%_ydz hx!CCwѩQP#W "Wb*هۖ+ۄoYx7n b`` eM΃P Z8$`>9ira3Vԕ  LK5zϟC'Pցq>$QMc$7bDD|xT{ 87@?Vl!?3tIC(EcM_H4#q=12+2 Tȓ_`] ".+[H4:/"[!p-mxw}{48nx }޽B&Y8֎WxZ\~y.%yi'^~Ӿ cǓX\Ov8\rhc<;y͚!H) rxN>ՖתdS8ii!cCi@ ?5Iu<E/1a*^3׌~ΉE50=<=9޷ˌ&8X1q7T$4A)m,@5 ph`!c20R[@k w:o@MM8!1{O؃ ?C/WۮB˸R[?zjuA55_cΈ# RXx~h{J(ք %GVD+DO?YW7XW|pze91XHxwE@#mg&Ǘ=OH\ k,xY".%; ';Ͱd+D%-Kʹ d̊"AT|Ve;';fBm1SW,(hu9$&,LUָLL䃽N _La#"ifI$eeO\"$-d[y[_2Zћfgm43>VC#63cmAebV^04J vW")A1=:Mtǜ^5G&"oAgט~[}Mo 9Z")Q bea1=رfʜ9)^v6~ {f+#eT@xF2{m@aBP)pN/-xxF5qԧL$x:q! CM(9!5!`itm>Wg'/w L~7b.ckRWx!e# `fuYoM rFN6C VOKQEr6]y#x)KܜφeBZ̋ _vkQš|E}##6 'N אKt PkW/./ّ (woŊwAw] KO 鐪v/#G*7࿏ȼxkQ0}#率_$.Izr 7o%p-+"r)Jb*dQ m3PPǀa6Ss9 b{Tsf?)H=}!P 65" I5c}i #*ޱ7g׆ʈ|U/=YrKSbhfli>ȚE:ɨLdP$V1X"Jq,\4==,9剡SHMybS2y3Yv>@V7 _ f*܆9!hq&h oVh?O*zcTN(dͷ(^dk`%qF\KSiPa8Q qpoLČFF_陣duq$GVAjO[> ii%išY&QI>j$v.FV'EL!G@@W,4 Bgߏ]0tHʪ\(N{`(J!a!i$y`NHy;DX̹IJ/XgZXɐ1u?& :?:&; Vx vTrQrܚ)Q`\ӘWH }'TjFQ7'VCvBqfEIg=6;-$>ο8[EwYVKovO*|ǚ@6eGʼܘB[N=E݆a 31=,'5qd^d34ѸE!uhEyJ056|[< >LaW<+nvjvpc/2:VhB$ iѴI$W9ɵ#p7Iۡ}pp[za?ju3 $FqWd@m4:fvlbx(h2USuԤ[v74+lק֕XF8H '`'(5D2,6 ئߌ p|k1݀M3Æ'+|TUϵ\H56O|h1zzo>@kI#ms:P=HI[^@2^ծG~oTY[M1x$BGj:limc h*2"DWb>E'̖lXh' Q[$Iy|l 6dQh ,|;p >S^ZqrxJ(q[;ɗ(' q؊}bH.L'WmD](Jh/yBcpBQ 񾱃]R q 9~FwXl 'ʼO eCVj6,";=+ڡzԧe1^f Ν!j iIWn.2 {=D(o9_2AY;<7H;wxGvA6lD%cV=+ 7-!jmߴLIXy60nn@ uB:`rFuy~$}O]6Tkfm y };@M6YdmJU{ 5&鸓Ku .s.6/p#B)K/p)UI9!+y-VFYwHϤI*00e7d %K hfximmx6 ]ȇEJq/ۘ)m'Ul6V&B$M#Bt\4B6vnqC k?rf, б|#ԪHZ1dwz]MuwJ0b|3dfSR77[|ʚu:X_>iԉ%1(zo!m,M~ `~-m` :f9oTjaƝ]G';^kı&jcƚ492 `Zd JŏN,ʑ)L} mμ(ֵad94(*m9'׹IdZ4$x* xev YWJtv`BW9OZM}ɛdDX`Th@69 mM,As^1p"~ i #t,3d [l#vQ;z 2:,~N (lר9-@(֐Lr,N0撶 g, HyTr0Aq-zzne-*=c#(Pff,C_;yKEej<嬷\^HVY֜Ce4ȵe!dN D8'gP|g<"% /Y'T'6Uv@+ cDv5#hd} Y9%ck4#9 C}DB0,2f PQ_=yZ:K|]\ʅ[p"I.vqH5Okv(+0ubZfqH<ԇ2ߝ !8X2;4@!e8oE^~Mc-Y-C\I:DT9<;9گn?/'_tw/`WM6#=9dn'Mg5W&uf ط, x*g8X <ϾβasE`/nX3$hb+}_heB%b^2^:l3^]wۨ܅Y8-cWgc)͊&u܋YMX#}x`16+m^H 9d4" 3#orT ]5GhʅK^,7W^u@fhBm+䑰Y݀\fD6o֭Ɵ*O.|]*/WʫOtٕBm1BLUpE_)H۹\aۅZj{5U7PqlvϊeZA#,FwCF?g(vn윭]un] \-8ΈfO.I eN =rD{v@륬EN@xw:m^8/a2qT+uS~uЗ$V6yBi>jZ9KQZsHar@O)"ȹ1QwǑz&I Dž;I:i-E/z/FƘASu,\%S͂'D4NfPB6VkdԭzcywĻ@e ˒)"[{.h<0 fntY7:M_mx!HN1J0T5.uX^M*O$V5.M\ F$c6b7WcC BtB-K-E;,fIZEjxBlW*DA}eqW/l`A%Ŷ/wJs&V7Π߮<1h`?>/˾~ [d(L]q#9V ~}`6س8 ĝP<ڄRToOjS4BNϒ5Nl셑%b| C ~2LAtzG G<&1|bovoK)kĀ ci684Vm<ȟLb )>I5GNKlVG:4dM+X,$yd],znd?xbmkfeֈ뵚k (6 s>S/]<I"yYY0f%/ Ef#Q  N`c)4j-}1Ϸ.s'ۇϏTr%S)_ԸK2Gm| @ \u&"!\hi=;O9'[Ⱥ~]Ƣv۔jm;%^U,gǬ-qF|#4m(Ȏw}2g?6نwMghGDo kS/3Z M6;&FFVgb6 Rԙr4|r ΚYԝCNx͡M,|m!ʆEo9bl>83mfDtF.1rסcDǝ 'Tr>xz:g8E)>LC܎@b;5WP ͇Pb a9pmLtYGaoUTPOHݦ L_6tX]zMe _}ƪmԠz=Wˋ O4ݷyr?R|sf{UqlN WG_&Zj8wMKᬘ)lZӓZ<P#.\)PVDFlyHJK,=edo㫼Vkƀ `-Z;'Ͷ'/t޾N^Ho?(qv~K}O EA {3l\c ZGעɏ8` M MP癑JxwvI0D9҆$"%ו BMYJYNYqDY-Z[Jjm [)u8^dᎇm[5"0XNB7t,(I.&#ڬ  ׀6x@[s#oT jS,GUI>8Ohħ~! (shѸd姮z14+ɷ/lhz5&-XޗRɵա0pt/eڒP1GdF>\ ~il(-7Pm(m+QwuS[& K]uԆR_%"Mjٴ ?8nl.mll^x0Y(;>ߨ{]hKhaQsxjN!H{)L$i<Ԩg4VeN"$&&֞hwnҲV}z ;p\UQ'3ڤqv=ѹN~9pW7ȵm! aIO{Wxʂ$W:qDB>YH!+vc LZw e tSaxm[ʼMA\uu'1t8XOh L@);Q_h@!xt2B9han#͜ бOQt"fh]RMu# EL15᥆7 ^_pDZ[CHkK,໚6E31Y6vVǷ$Vs2&c#=@]M37[ejl:`9y,8\K5Gd>[θQg`lipMM9%Cu  *v4KPs6Թ<h DVqKLf=y)ٴWV.j⪢;H: \Hå,x-M.k4p&3&wUCsY+tY4檾6r lq"ӎ%zr$m"䐠s 3E[n(6-±=|5FXk9'!#E2: &NBqVq_qb] UpZQi@x_M7+Vf8_ܹɧy?{R|P~ X? P*&#}0Rt̰I'lc F*o&<7ܑ&Z*9g-n9JIP6j,]ʾ4 J]cVKtxd r#SNA1`mP|s J}}˿^U?]L6.<称[ԧ  cm4Lc ʦFƷ6Fr1Y2&r+j{Zd$YAFHXu)kQdƶ&/G1kp'"l}48I0D8TkښM[C!t h؁45u 6W d'`؎>,>AcHtD7r5OܠĔC!06Wg5+ ώVԫ:g7-bABD Aw*D`[[XuU[EbČiuٛ.WE@^tZq~".j{(Hr.{l[_V_:84~bDmxk5R1*s%qEhʰ惝3]=܂JE>Y=}w*5X_6kl6|LҦۃ yinQ-(l>=9;ư2mGQnh[ܢ|}6Z-x]4-tY:W}ڋo>m>=QۇajЄkL -ސ70UQa5Iqk)6Z.Ѕ 蘧(mdZ{|n[p"fM]wܠؠ (N zLtg& .vC 7ay'Oj:3{cVm(Ȇe GOI$(/e5pGO$r|ʎ׉v1ḱ42@/ar)I+Di0߶B|w(π+?cd[pY*م14VM`w'}C_c)ƽSNbGn` Qbb,JdqΦ8`Ew_n~F6Hٌa!E';le+Mqh㦳0/vO,$6 yFtBB:L3NbT{Y&]LEfe[\0 b;H{&#l s;nO7)8\3퓗gGI:ɤwI!K`?rMϞm%z#\mLpۂp{`g+ *GyJ)X;`L1b:;Pvi:0aCPt~@kRh#j L$e#wi#8@_>x{<änyvwwѯwwyNϼ/ۇg'KFo!sÛt݌Nuv_@IXН]yd4(j:=ݬ\'mvOO_֟j3FwVpGz[bP_C ޻ Q'%Uy<?ͰG8G Lj)۩Pݱn. &&؊;%B}WMV|s<5:JČN{mz[)tl7$߂N{XTƤ^-޺`aKt|'ΗX1ʛjz?\˖޳v9Xkidu.E6d~͙`;YݓW_p_u^m̴~zD>9>:݅=H7*~)HԨAC:^HEdzÝ.jY㳧:jY֠Zyp9_.n_VϺl+T-Vjˋ5^n?p5ժնZe?m{>Ҫ RU6Fë:ϻTzn E~v;ێoES$5a?}{/y?w>6 9zT|򛩢"LfuИk]0'd!4ZmU )/c@UHNneRp,[ DjsM8jeUHCrM S[A酻Gۧ{'}p+ןeϔް:LZ%Qxn0g}ӸHap9\I̺~hpc0`bfs$[y'vlr'l;ʟtn(vf˺NYՀ0a)e-Oʯn&wajjerEDY}D ݨnER,M4׽ ss92_CD8R6ٴBv0.j"ME֔ܵQJ~U7.  ic? 76X$:"b)ClEzR$t|C>tq$12}5F/4-؂-`O,9vxۉ>9ͦ=oz͖EjG7Jʛy\WM̿˜HGgGFZd`@NQN˥!VG}[V{ .1ׁBmǹ2"|LON"3P'd#Z]N#&A7 @*r ?atTJ11E$ VZ~p:w.A 6vE<4'IzY"6Z!gerBI Oo2$ Ցkr'׆!H)6lxc"s Yu7 dahCiYe]jWÕ mp˝Aے^͖Jm|dO Yeߡ(5g\1H=4 q+%ǀtrA0ʨ^#pN%slݞ ̌-aNz98.R('ddl Ԥ:7Ɩہ.#T6nOj>Hͦ%6Dbq/ OrITFKk)vgņRŘxגIv\ μ_`۪53v]ڍRyC7ve P \'tC/2R:b93Y|EIId=(@]lk;R5UOC*+qǦ$4aŁzҌT2- ie H@ӊa?CYuF%w ^7qbgEidAhتele+w>Amn$đ]iB\K9vˉlLԝZ@n|o]鸁AA='oNTU B-.q- T]?(zQ7,xԘTxxڍcqX3k v 2~i"obArtw@”LA/F=!Z-Je$ /"Dv]W" ' Qa[ .){3"Mw-:p-JVu:2Ap$o'PN6lr^ly 8O<)#wg.F.֋W.Z)&Ғ,*npg.^ K&anLkc P3~gt~8;97J؆hz"MM:;iBM&|t_d5m֬f'wX3G`EbqՋ'}WޏM&":H%#-}UZ9bRs|qtôs&TsDs?:vr$J3FLrwwgI4A|x9?{r,ty ݌0qӺa{rp ,>.߳k<'+KKK˳__mAm] eđK.؊\[;VkKHlZ >_P?ofvF7_P}zyR=.zhBډjP=>^<{vUni8 p[dI6?  T)f㞪~>~<ǶQD`)`OP`04 a]*0ҋĩFHj/ѷCkK9H#}TTHUHM#?D0*c=)R^wjGjb]tn=> ӭ|kPbȭL2[f(FnjI-D >vq0|&3O=<~L-+x[@X MؗA_->[j۫J76,}+}/g+P O6`Ayk:X6Vo%4wWp0TWA*}ǭI`N'Xvio?ը\뷿cnLP/Tc<`Uh.0hATЛ"ktW/P;?RХ_{!gk%4ݏG-+g~U 8;|YG>qyw:ŅZMW:~J*MZ.oF1bv9{80VlG.䭥emT/_/?sW| qj;_! ?no~h^֦DMVj Nuol5ڬ}V^,-^Æ?\ *?וkkZvnz]~Ԋ_)&ம"7?|\‹bvg%$: 󺭮 B(BѷHmoۮ FOs?՝G߻&]FZZWVcadf3f(nFv_K Hͥe~ܬ}.rYScU EPTx FՍyН?c|Fo'1.5"gȀ޹g/ֽQ2<{*,-..UjŲwIELXb7 |oWFh#8x` ܷÛ~w$ ^Ф"bkޠ* ~WtZ٧@" "O_8)bR& ^w~2%TNjT&%}EěZ@ Vڄ܄RD(lmxДQdVZUE>B3¸H%H{}v@Vbe`$7-yDVE_f0Dd919\TCTt/)?}\ݶU!jGLO/M_27~X=t~Spy<≌ٰ=8MpH#FzP%tx߉cPlQV/`rLj1EGTx7H&Shn_l 1]Qĵy0}8AU?O~HP=ReQv^QGA*x 9$>? jCOB> ?GiAi$BNFchYm[WCԾѮҜj`tLˉ'yL_P:Ï9^&r8`O{a!x0zRv Iya jZs>FMr !T`W16nzZ@ p9b&6`%k.t]}ʄlfx s|eP9)v8yLor fEkg eq4kCiI4S#,(Dn?k`ڽLֈcpϜny`":# k}P| ?'?U~ ;?e~@0rפgGLѴ$,(h{F\1Ss-^.MN8t$GR1.̶Zԥub+[8P0@rLiR;.9\7wr>ެ[D'VQ(Zk e霃Ğ /ߑ$f(0Sx9&Ν*x8Nrw\C=yGC. P8 w|.&:V (3cJŃts<ќb:n)[sF2$pΣ )K`0:…VⳀWj: %ըo6^$CHg~lPkçJGώ_y)Ȏ&JB $q\UyXJM'?}N͆T F{l*z)iKl"ILdH-g]#a|z TW5SWi+_b\4()[$г$QԆg5ᄲʜzd,4  5}s X.i0,E7o{nCxۚGߐx$y,5BW:|YNj"FK bN#pT(A6>EK?#l"5Ʌyi[iJ;8lTZ4db7K9s,:]gOdx=fM;fEReNܚ7Q4/d+bDAtQE\E|EJfG!1'M<Bo9qEHtnd,N.4B-z{tp1;,RXC8:wg_턾B'Y#WkQ~$[ʢa$}ʝn{$䕜1ãW.Zf2s.FZ~=7}l0+-8rp"Q@.mKٕeI# hr3,j T]1j5S;;8&V?NY<8jC"/#pt>er_faPE+aRǠGv9Wt Gh"(0@{< AzP:3)[^P~ TV2(e2W6i09:b/k9{O%-y<0y}a2x`{p5)r^z8Ry..GQ$u+^̱tLn8xTAN~[[Cnmr>eIxj1OBF2QQ'.J=f7ul|ynB8}9!W^${ԩO&$JP4ҝ(ڹnEwt,[Q*L]to+0gE\tgע:˴EwYQ3]5 Y4E7a)oSn.^u\ OʷGʪǹ"^L冔$Pl wCd$tXϏ9g+1]sp?N|2M4"H'(߻!p|Jw/$$u2|xM`8WW&J9y,ۍ!3^v5 H&e <ָi8${ܿ#ӭqG>xq8^!뚔qsMJ9%MA iRQ91Cm&ό0fܑиE!BsXi$Z`= 2Qȿ(e:exYM5bɝПizHdpreI($yk7[*'\EE&*7;ٰe:&ݩNt;.%1r?6Li$<gNq_*a3UܘLVp^OocTkz)NwQ4K{wsԭo~Sv$oZu[c㬗 iܷEq oKƸnM9*lz^\9WKH}tz9q Dn/iO^9,Pg[I{;؄,NbJޑ^b9sq]MUvo~z7[@n3v -/TniK 8;rTM`kx@[%ϟm>[w)}X: viG7 u[:v~s9Nw!ސrt|Bo]-<^/KL[JϞw\Z yn^]-.[?/}8>Q{ʨj5#^C$l=΄3Їc5EѢ%] ^3굻7ĥrtb C6"4GSRK)XN#G0'Ee0?cWEҪ7[7n{-cxwEW|O8QMөI@VCQܗ4LJrŐYqo{< ݳ(i&|&{q\oܯ+ 1K%I`^(9 wKs9szI̊i$!sA$z$;X67K0rҼ'1an.r̃ ʶlE#1ۮRbTXhok첝'kiZ`3 7I>CʗIHsz0pk O<ɱfsZmy㥿,j5(Vgi]lPv׾Z\} <=YYZDե'K3/ׅa_8ou$| f.!3K%d2s \B9<8}z\ߨ9cT)~i%VG^7mOT/J_!tKo;heklZ)P72놮sh)m|ש(OQhʗ@7c(.=ߐJ4T۵z9U 4^n3qOɠ2=&㣉%q;zމ>S;;FMY_Xzd4nlR2olޜmTƣm+쪧և/2B}v7wcc=Z+՟aW:JV;W?_~dʓ%%Y'By6V$/Qsfia.7~JpFnp=wPtg0Fdғx;>T>JpjENx;?j3L':r%;*pkXUytm|x}/GcxqBR̔=3eL3S̔=3eL3S$I#Μn Xf@%cYX?Vd!]M@q>f1,,E(y5m[2q\~q|v]wzFьF~진֞{g Vt_sp8^GUYpU@][nu9GumrGS ߯'ߗ^Keh. w̄x.W-T$j͝SEB .4[-\H =GMD|Є:Dj50(`P98 _JqB y4fY1'@sȆÏ6FRNAvY`]9=g3s{x$9#fŒ@3aF <>{6۞n~{Q~w~~K%ڭs]&Wn^o4sFan>}Uqt"~>;:)7ߨF39v=?>ۖN4TZ=^YqK<>(b1}Vӳ'TBACJ7A3uYJ~]Bѵd>:=^HׇZ7Xu>`pa jgz`N5>o}owiv ._|pmUƇV'GVlldVAWU)+@nC|e.FQj_r-+.K;%8'W[F^a"({B ҫ~#~ ? ` Y=]_8?tLgdtI%]Nx:[^k%Qh?9Z%UcӳgyO:+v_Q| 7.?4텒z y\'‰-2Šib#=뾳6(Ѩ7uoo5 `>vbNz;ߝW\CWz@'?gVpSeknyˏ[7_A:4pn~gb>[aN<·3o>E v7QfIoy۵m <^m>ᗋyM?N_맭[\[y/H-.̓&7b+YޣWs+ļIy/Am^o}޸DFcj3kX~t'迁uIsk.& |(OK׭Ե5,4y/{xi.>,Mi}ǽÃCf߉ Y\msQ썡{eWifJ['3\/vm5+ZAK/~=] 25w`l)jSP=ućE̪4eRݣ~Ma5PP3WD y"-R׏xɾѧ@-Em]Dqؠn'ZmDzܭf6(*RzLV}qF5cHk*s+%‡*a #w䏼"voz]mSL(g'8dܖ@N4_7Qi~}`o4O@#F.Zo|}pvR=<˓3;So@&v*pZU*Q ̏ UWឹ8W;nG^Q!FSD΀( 0Gÿ7_w @cnʴ[`ctftTan,R&b9\+j)PẰ ssJUq,s4S2Y)G` 墵'w/Iڹߠgehq!C) YaBFh{uYs}1㔯xCtdJ6RG;ۧtԺ[ 7Wss-ԓ'_ltSz`':[|H.wrq 磠[PH[U,9ćñ {;EBȃsWxڻ upsƑ^x޾}n-frh6 qvBqGk:[HP&Na :!a%V^q.:,SƟIr0,#$9EcRkx_a4 o\1@Y[\^x? -Jx*_)lt1* of Bۨ eR^s++$L_k`&zl:\U!PQ'FOUz'vqqutؘO]3[ˬOiE @e!ta'&|B%6_S{qM?)5O~EqOu1-$Sķ3ՋnM>C$l 23 mϨ*{rsCp(}.;"ڠbV,1ji~<UX !Kt1hd#){Dcț5d[d&R.tӢfF:ebx1]B&f\Il9DcBJ)v́B֨xИ/NƩ<1rk5[SvETaNvRk/.UWEwrG.8Bly@8w>y6i׼e+ڋoq`RRy鉠R?5dȰB/r*c_E"(hEi!]P= iCtoX䠄OB*_]wO>>w2)SفR)ƛ,jqz\]\}\B 3PP!rhTʡ}7':Sb-tٲ^>T!oA.sjDQ=㫇G?`mm=se5i,,ru9y]μ.g^3?e''yS(2)5nLvzyRu['/,$ AVV'Ɓd1ߦ)s8 JCv 0ko8V&N:W:R( XpX-WpOZxx (sAoY?^\MYY|<:=#g؟3bFψ?&âý3tFe&%1PqUVs!yN^x!vkk;c" ݇@Q9ׯ4eWpwKY#e`QOva?J?B>}-pS%VZj7:>,T ˗;x_5bP`U'U~A^jC]w0E6+cAlr|t1t O&wFK5#7-۷o^;)Ѡ/Ήfwߍ-Gn9E Ӕ} 01?_o,/kɓ.#K+Q36.G[[ˏg/oѽ +=KݣڻG?={P{("qwj?=d?QCݣڻGσ=Yi+ ju0 ?$zُ:v4.y AIզM*q;]\D75 ޱJ4n+UfrpnK!ޛuD7ީ U8 ۀp\ZRve'3<eWG?m&UE*jrVԼ\0z lPV_)'eKk/X/:vI[S0,EwBw2u|%#Y 7u@:o!DT\YmlZz%#RY }rPвu^RwȻ8Ya Uu|,hA(:Rv@:R&?A717K 8ZU'4HvFfV7ojN#h3Ua0sLg=pTPfym8,42I42֦ H'!ۅNTr+)(1 ,\,,(aXlɕ7P5_n9Fp {XVK 3Q~Bv@ԂIn/VxÕ(d HQ) MV95[7z7E{f˞,Rs60บ+@tuWIQBʂ7okJ}(bL񛅪 i a eBXl"ܳ`ybe,mBjK+Քge3?3L3?3L=(DX뭑#EP9v#I0:R /$}҉TNƲFH:jc@3٧< ^&&i=^$+oMO-L3W^pk Zrۯip?γYܕ%Bo3ޚ(fc6Ѓr=Ugd/{ mqL/f΂N4^&Tg Pٽ 1L1Ki;xƃn= $F4ʽ?a}97n3m~\Q}|YRد0o#{1G2 Mpܗ]MtWz[BT3$ȏ,ML'<uqqZ?g&g&op1(Ծ)㨏Vu44[Hmt[MGXmbe]G=B\B=F<^wl-;oeܬYr0BH _KJG틢=q{ɿJ6ܽ(-=:^QՑa YBc>޴r*ƿKzbZ%g=~QJ[6fJv)K * ̅[9&HXbAC}FG,iFwj+KcʂzRǂ \c+PKA YKeY"P9BH\lxcX5ސ+.8TV52thhCa\(uNc" ,=ђk,;w/B SZoo}W&hIA(`uhp+g8 w3.S(ңw][r#aQ!Bt>wy23L;3L;Q_@K+~}SeYFel`5 nJVԉڡA!ҷrA ~3%JLt =eFQ^FJQp(|HEΪ!7Ӯ9kRifڲOq26t;\]T{%\[B'3ϗeu D+eJF4s55܊zW݌07ο$v=_{LNj__UeA}D^b'OOVV$/|}1j?{xޟw=P{hE=P{hAݣǃ=P{hAݣǃ=s>ZlwQCnwwplwv|/OՆYt*?q\17~iZT!\•=E| Bu.6*0WQ}'TP~2Rq\QoRKߔ9GOvvV~bKr喓V˭$˭f[M+}Uqt"~>;:)7ߨF3huP|l[:JPkxe-)zn)g;<=߯~{B%+D)O NxãTkɮ|tz潐:op@VpX}[ -=J µX-Y)_*+UVͧCZkG(+P;G;+ՠ۫afy_ԁ3A_PY 61c$%xstbkǻwwvOKܽc)~/p=s a. =ݝT71?L/~~ ޸]'b[DDb=* ?㯭`w>_~94o͙yso^<I/V|?ی]Wږߒ[g*[WT7N3xjiF(=Oz_b6y?4@//pgpM+[F-#r =ſt|`P[!2_pCQD<+8? L,Q9x7nHvl/WxDZzIs>e9<;9ϭfjx'(|I+,/kY^gSG?eT2߶gͮ8 u+r+:Ѣxo)VMK7ҳɺ;[oþzcP[Q6oV3%Y^G֎fsYovs,J~[-=l筎̑m((lؼG/w ]SCiv ?W=V%?]8u/xЯu^?h}# `]fIoy۵m nn'A#u-OdDD߿>uELz@gLI؝fS!ԨukV<ؘ8SMG,8f Yvf{5pQhqϫ+Tꥒ5uZ%܏v>}lM9v]BgyD^AԭXALV(NI t/x%I[S; s:>96~EERld&qV;J:E}G@҇пH \Vfnnf]W-[ FeKkSAӟ~jpx>Z)mZM&)K|-oPWQɣw _0"YiԷO+ۢ&#bg&+Y7_>S%5_8S0&/ZAIتd/ / JgIoyrσ%mn4KY,n7:Kgss؃1ۀiQr%2kb!ukdtbޯ.BX&m Џ^#~ߏ; yOnNӝ9V,AE%y<&Wc.[vJ&9Mp_OFqW~ҟݖ B чRV~u$,@?ۯi3ɛ?8`yW!ezm%?K?o"`gPo+LPވ;lq^:P2;N S/*ks\RYrw~M%]]Pk,^1̾ݎ܎'0wU%n˪d9==>0I#bympg-326 wgrh%@+9r2٢ L8 l)wh$GuO + Xuz~:A[ehHX6Tܺa'cu1"bQ(MN@@;.|'Z5 Va>\=FO6'g)1Gtֺh8j8hh>n|>VdXү Dp'Ob1'5Z'8 $Y\xxqeq&=VR[ N4WFUV+ -H~o >A}/1|! yQS5 <]?`ȥOP:;_⟳xt]xh"R=7~)ϧ]P۞'2]\YXc|]#Fq䈀 8BڀA"ڒ1ߪq1vkz깪z&S }@z}/TD9& @/bCE[W5D 6!Ѳ4u*gr*gr*gr*gr*gr*go }ݵy }C>:_3P=1DCT1DCT1DCT1DCT1DCT1DCT=0DR1 R}+T88C Ȕ(bXQ q6@P19@M NqX'A?B9-qra89q8`Mq(8NN(E8N MF8N4N guF ZV&F?X?'ĒXK~bO,*BlQ0ս睃%q$!B@EARjo<[IqoDuo*_5))) H0 }/U~􀑸QeiE-~<,nlFb -Zk_=P”C Djn V^[mTCc[,zrp 1+- R"b~We$uƙr--Śi^ax,>Tngm㙂IY95.۲|X+6G3"0>P3bl%ErS$V* 2V:o@@n~&-p=A&T'њAͪLŃJ!԰7 !Aٸ&M^6p; i__7lln㊞N5}$tTWnX8:޸8o!\?ʄg{cM]nNsi1Dµ4SMSː/ /?#JQmC|WpTr@ ?]!3xH3 `ttG3/Ac)hXd iaOL*SHQ Xqn fk,d}Oh9Q+h/dt[@X ^'f_T[b9v|˭R2䅳>*Ilsn9(w!RrwX6ť^lMǏC$:]ʉOU<=S7U$s|v!\ߠ%5o "xw"Nq#~o?-Vfr*N'[Rd+@ XFpp%ξ;eX9P E^{r5E2}`@xF5̷Y)5)RdSlri|蓼 E1opN=BOo9 ozG$ _'^,-]հ^!55&_IVz\C12f혵c>X `5.!W4K?\15u:g;"_Jr um Knd <+Zb xBʤ餴x8}1)R4'$^># b&_dZI|ˡLV9 _θC-H\!%r'fkh]V$k^jT0,F?~?Wyz޺kd'$2zZ+EOZrX. ?b\Fj:o}=Xl:&BipDBT)ѶeL5= dK?P]5_ =a Ye_%H~+dڒd2rE]esЋXH J1#a@,b(^;Ā#D౧b)` j5z4` Dje; )14㿇x*6U ~ֻ4<( l9G ̲'˞,{ɲ'˞,{eOfA`fA`z+q&/ޞXtVsE M ! g:+&k`um]=_J5n'>qAs)3R Nh7s̟zŕ'vCX [HD3h䗌wV7pub;+NUIPKĞUОFţ&HIQYo'14V6,~")yK@KwI '1΁+^rL3_@3peFw(z/_{kd [-Dr9mUaslOr (c[:X%lw2]Z]-mk>]__ڞ^[CTzG:( n ѽKCf* "d"7+c#2i"qcyF!m{ҎYWںk]S.e[pA$(r@_vȁLmO3C3E.c)Mϰmn}ڎ80fNm].jG(6MA,Ի(6~ɸHnAvv6  ہʁI/#tfppF(m«vDt2kc 6ꖛ٭/Vs_0I('FէF9awA\٣_>ؒ.,]V#`P$MmO= !XrBر mOr vmonlw!O<Lk]ӒhyvMtWB탽͹ьo,u; O,.sr|c1;2^8y[B$7 0vA9z`]0&}&T8 .]*jqhwVw?:S80pME](@di{GÑ*4%0i5U)zcc'؄]jZL[X񧭌m2Owen3ij#,v!S]J`?}ΎU'ڔ*6+ױ pn8 !a[Uh#Rf y >Zy|<_](n&6ƒ\B9|" =48Dk2/`8 CYXhFƍ-d){`2otT*藢)U}KR}2dOf#J ~nȟ[Qg}-[JҘn1hVdc[T:]D̉Xt3KӾ&5iאAPlO;v6g<) =9 8JDhX'$L`?R6a+LP|uXxtLJx Z%(!#`/>ѽDD\uj8\)d%qhwE):OiK$XޏCʨ7w~9f4k?w~xYOolCM)", YkS$TE}~_y,992 )N@0 ]pOܜS1 b^"&2)XIeD>D}N)ߵm)ǺOvSu>;trhH]`مA Tӈή* 4c|YYԝ*@%&I7-&,IܥĮ+dnqτrLyLf^8A$+>ټ2PD]S7%{={GQO$O$O$O$O'Rfj,B4lk*H^S=;::;{d1.&4" %Rit~z%T6tM)Tف_T41z%B3ѺMwyg`)AS`M%MIj2WMtz(gS7CI9M!5e !x5$LJ1E% bnf.ȿĚuXK2m;uPEZK 9i˟؁%J!bx $"O'cy@Y}y}Ҵq}]W˥z?33?33˞,{ɲ'˞,{ !0\dx WTA=$&\eǛ="Ȗ\$Ƚ s០J)Xyg,`Y;j6uYRZ}axxxR>,1jъG+i5 ouu7e;pՁ9 ?]=͆)"Yv$| jl:w9ҜcPF ePF ePF ePF ePF ePF B w&Pl ءKޚ0V ;'{9?ѓ1<R/؎}] Ϛ7'"_ydNb4%e!!&`"&D$Ԟp$!I d6orP]ى:41D| R1Z%\ON(-R( N߄4DiD]Wt?,xKĦz[A91ֺTW2kS5,le`X"6c)z,>껷sL[M&y EGXiAY~X C[( D$J-5 iQ@) Qz*D W ҙ{(1L,|Eɢ$HQUU ;J;/P #`dHcH8^cSbߦ֞I`0*n&֕}]+sm6WB4!JKQL;NRQ!/6$D":Ol 60+@`57,s:AE:2Q Nݜø͍]:Rl9Ig61%Zs(PE::TMZJZB% |$ܫtz &OVc,KQ؜|3- O 5pm9<.ģ&Sb'BU+燓@xq9xl:K'(l^dN<%~0S*\}V$=/W{D+ըE4] <S$ \j*f{W4Vs1}dh@G!$vɊ$qŴ8:6tM`Va3dY&zn2 Mw>.ܾcV~( n;{@ xsjoj;fKƒ ->8 C~ z- #ɟ߷?ݱ5 - -W,;S Yn]HT'QGeb #+*2OోR(Mښr9A1\GH*!{#x;i~JH]BAH7N GÓMJ@A杓Ú͐ cyW#Mx"H:I(qf#ƜO7O@ cCӛ0SW;g#q`±#T Ƒ.#wfT;Ǜ[[.'U5Nјz\\*ߛP3I𹊠Y.\*,>R#:Ô8l62<3˟9\?Kߨ)|fx"~GM{NV>8Pˎʗߨq o֫V2".艁x&-˵ԫA> #o7 ZF_F@z!>:xY/eu +r-K emhCUjP׆FToEu @?,BXdG o xsn /i" r+b'cDscsq`J/²'˞,{ɲ'˞,{~+'3 0 3 0 1@]!…h4…k46L8|}R [*w#T9S!{3YQ5Q5Q5Q5Q5Q5B ]S◽w?Gn{;[m k}IK /w̜G"!2)!y/᭿;-E,1^3',<0u}W4_CkO碑yZ!@`E H /V#q(ǾWkR&sȄ1ÄPup)k5}&oP4RBߤشqjՏ=<Ի<?)+ Tb'Y%$fWUMYiA (q4l2lڏTCjoiu&0CԊcV7opŧ?Bx k*m 4oV7 r\ȱs@K2d&l2 aPa$)@Fm'Rb !zcLK))ҼXG>^)(;YLmzrE@z)iq(UIe^p@*'eb%:7Ē٦>ȼuXSId)U}KR}dOf#J9epȟ[Qg}] [JҘn屠eVdc{NwxʊKM,%Wi_A+[=#"@٦(mJkiOb,2& c0&_%1"67#< qxMD$4Cp8$2FFgF5=t`+cN0F}:B*-k_P1 Ho__Vk\,WOl-?'Ėۯcyqibg`cg{_ f| %_Ox:@fZgRڑP9N8UфXxϫaޅ Us.P-}Yyk)BFJحO4VkdAv#F|{U2]{OWK^Rjr JVA CP´24LT~2#`{!qz{hFa0HH )R/2iXv']E d]܏I$BY u!zj"t&ċ`qs[gw R[pʨsnQ)44ӅN"mLʈ4*Rd$$KQyMW\nNWƁ!؆7.*EDX>:,ff&Ŵ=;u:{;/vZϡ)~[i6aM6b)D?f吒 Fx4/Fr'xe\C JfK h9o碿\Wr%R)3 0 㿰ɲ'˞,{ɲ'˞ 3 0 3 0 0Wж9c1Y1Y1Y1Y1Y1Y/Tnu&]c,b ߻+K8aa 00000000000}<0o>Cm^?Ja?|WpTfK~uǯWw_z?]<7qlf_??Xy,]L.Ss2]&"V+M%Fe9Lf)ɑgz37SRK\N|𰠱4i0BBHAę'HJk8:) Dib8h[OU,y4%RK^)ƃ+JF) z!KQH=D>#|ś@NRIj7YF \A2ݸl2Ͻ5erpj_IBo fg~lxѢ]0̎ K&賣^mzzi(S^+#}<Eچ@n'Hי 2x|iIhG}Ao8M'h:$ =KRjjҲc.t>3]9+2iYR)]#)tQO(ӆW % P U$W[$"`ۄ"iU7ٔ$C*8L6`?^LJ2w H̭^՚!C #S``6W/x{筧I <.z*gD1b5do{K;+͑o,܍~F+R"o럊d{6(Ul@:Xa~\Is2eUȩN5<("?=ȗY+Z^1ǟn 60dgxVdzE$jk埾$!B)&=B%0[5:o}p,PGzo[jEʅk?wB͎MSh 9 `Ձ .K#_eo2=+\.vDVG`%qS^oD)R@ }~UfLOO.4u,,*M5,.n<܍N3ÍwXGWzvzuڏ+mEmdoi!,v_<ۍZ69lQfs:}|g:\[_ 11w^܉!Le:<6[OK9s(k4]9aէuI$r G3g@Ծ'~.'eOɗ='_˞|ٓ/{F1TM+v76w|Q4fΦ 3@+ZJDea9QRQ,H+5&+/hqՁzNiJSƺg!*0|eUW[*.݁rsRB"mR,)m俉;^zO4j4?PWR^gH]R?z?x%r?yKxKxKxK %?ʳPuI!w0x2,0Tѳ#Ҭ!8T zZb<'(T.Tk:|N~'Wu5EjڷK"˃|3/ 3ijg*dtܴCI,"SUPGrI?\-F0=L2Twf0Rܲ>jݶ] uZX4SK6\=@ZN_[*(-7e⋶FH3xzU5qZ_բ^! ja"1xRohImpJ૔rPVvg`g&ȏA,A_\_)F٣ ySB $q*UQ!h;!v4Vㅝ{g>^hCm~mO"Wҥ)BwmgkkgJ'K>sp<}N5NP'#msoumsg U㋮NקÈyvϫ]}[bJolz}k͞18l?xZj^lmts2{[Ɯwş'SmvVxus_{kd [U Hih?~Cf{fm@҂;H$ٕYӶ6郻]h 9K[[Cv;AYɑL಻K]2[eח/K2d"7+c#2iK_oI;^g㯴u״\˶r90.|Cē/;|@@+PLѮvh+qӓl۵[_;#N6Va'ؓ yuSeD7j8!O;??WM;HΦr@#A`;pS90`a @swAxNԚNfyTO/}v_hmn b%n?w 9raT}aqju=Xz-uku[;N sv8dGCSwB]!k[۝xHZ$8o^vc*g`ogsn4lFNB,KX̴W,g;%D2~mi|k#0GA'ɳ],5ulpBiWl\f,]"n}c}UNT;N23zSd-jW0J7+Yp(Mmɧ:[{g^`\U/f,}D g#WbJ@menx++uIj2=ܨxX%7npMѭMkrcڱLm/$ }ƙmG^dؖ&rZWeuƢ!iO;=&= \O퍡`B@L+i8]e8"b:р lWt_N{0!~pԡ]Ml8Ӄ\Uڌ؍41lo 9dn k9͝B8fiKvlKy3t)CePHtBQ >Z%F@ ݝK-)_o_<{㦘#I/i{]0Fv1#?Ғ"e0,q: nsJ8 -t90W3'z< /byŦH>Of*@fGL2XR*m {MsMӆHlhMY ZG[CAl#ܹ tۤuIQBX,Ln۪A/Ցˬ\G읓ny +9ocb?7tV&(#zBG&g泳><,=OEX<.=/sr, ._bI5r[Sr=ݤm}aAZ0i?х  @CvxA+8PC ^YK;9Vd'd1 :aRtQ MU@AH_;ljNOї"-2 W MnfŊ|1P`OD3'dd_>[쪪I9+ %9< S&MjH-?MG0CԊcV7opŧ?Bx k*m 4oV7 (񢱥s@K2d&l21,v|4)@m'Rb !zcH-{>HoD̷ފ:6[JҘnԮm..NwVWVR72g\%2}MjҎAPlO;6g<) =9 8"[8Mu0F"rrū@. +#_^'~a5|*ddϕ!(ɡiI'Z YRIB|YIXa >BŏW)]Lpq7Fa"M1$$g{ >j+@d s S]j CzBtMw%AM#`=G@UÇh®MM)B ­ze)laW[f46[so wC>BqIS508 *E:pU# .3tmk yCc:i] mX^_h7jXJ@gh k={NQU*P<Uv'wZk3w;MjkhBvHOG)MEŻC'I콪Sݖ~󊗊N@"n4ۗ.-Dגf{r[ Wq">F -fѕM7 fn7!Un( ŦF;j)WPCE0Σwjǐx@~ ҿS QVc&p|<Oރ;n&{n05c+Bl#|'$*KB}|2@aU7~!>A(.%j*`C}!} ;(7YEkVFL_?X?'ĒXK~bO,^guSv:{Qh_,at;JJ͍fH+5׶֛il|Fp$;U.)-&aRX Qa!E&9p^Cґ<\=#A(YS?RܥMXeUTVW< A|ǰ|x{݅Fɸxz~@{T rbZ2/,,߷ UmiC}q=iӷkJhDW Y;gٓeO=YdٓeO=Y;3 0 3 0Y;&msVY;+gwVY;+gK}5!GU{4ΚY;krgMɝ5&wΚY;krgMɝ5&wΚY;krgMɝ5&/=/}Q}RҨʬ;~bO?'v[{;z#J[8{;4|ko)Z89׏hvᗤ J߆ۂ" K0Gp9JC@ hV}@zUu_BU1w1n ~I`nq[\n%Za['gV丿E7ʸ[)yyyyy^tEBʉ5ٿsCg|DR=aogxn?7:\ 23D ,0@56d53aae579b9e7e0f0ca184b7032ab1d48198canțAGg൩>+4?$d 9#)08 < @ H  ((809@:6FGHI\]^def"Cruby-gd0.8.03.el6An interface to Boutell GD libraryRuby/GD (formerly known as "GD") is an extension library to use Thomas Boutell's gd library (http://www.boutell.com/gd/) from Ruby.Oeorigan BSD-likeDevelopment/Rubyruby-gd-0.8.0.gemhttp://rubyforge.org/projects/ruby-gd/linuxx86_64 OPsOd28ef2ac04a472b944bf3c6dd042431c8f16618cd1b0aead6789b718301af75d5 plbplbplbplb gd-develruby-develfreetype-devellibpng-develzlib-develrpmlib(CompressedFileNames)3.0.4-14.8.0OKJG@H@H@Philippe Le Brouster 0.8.0-3el6Thierry Vignaud 0.8.0-2mdv2010.0Guillaume Rousse 0.8.0-1mdv2009.0Guillaume Rousse 0.8.0-1mdv2009.0- make the sitearchdir definition compatible for centos6/el6.+ Revision: 433513 - rebuild+ Revision: 272023 - import ruby-gd- first mdv releaseorigan 1327326698V$V$ruby-gd-0.8.0.gemruby-gd.speccpiogzip9?PԺs8&ڶm۶m۶m۶m׶m[{_w=i3Liڦ lL,&, '`d1c4ae`gief_iLsr557ec3701t1s1t3 3 όLlLK L, ?ǽO P,|k3HQU_!¾ļl@]4Y~7sO oߺ?vGr Ipޮ㮠Pw借4g閧 p@ϮN{S;j3=X\^sӷӹ^oZ6og3-zVKa!Yk6K͉U<?Bf<]f(r|[4kg':,5k|!,N~Y|> a|yv~X xžx4a~` (~sԬzՑkdwPkpW ? ưۄMmwv~woa3۩GQ޴|J(|o\`/Xɑ~0ב\;:Cn[6e#P#Mkif{ΆaT.n$}C5bxR6UŖKziژ vb@m% foLs7 M|00'oWVmIS P{t֟Ac @5C}}"ftlT eB@'g/f+`3P?p?tnOgmq+wmiocc1k_ax>[.*e'+g K. 6u؇ڒYnGTq9`o ;/*=j\wpyR6 o%HX,Bn *L9G*:n*ۭO&\seY`߱؆ߝiPzxz >w)S^)mܧ#0XcQ\AfSae'XV$&,lj>+oDZ+6"v /C6(EQu!iXx b厽&2WWA@"䀟08-=ʶd*` 34s.Lph'Ϟ)VƵkSSS5%-6l2u$^Ww# 5i;8vynn (_oL*\g&.=+ \NQ\@ l6ttsz&yOnJ.D?uܠX4^V(WO]߿ȾŅjͼuwſK"B ankI>l >ܝ~Yz/΁X> kl'Ɇ(*A[4DDN`6.ĬLPxc> %Aɡ0$[)\m 2".\?])y{C *f[gK`;Ul#T6D96$stw/5wK5" o=YY/nvxTN!a nNo ^zЀ@#o"]EH^Cv?`^>S9хK%p!4^JD+4û)q$e6D-/sY(LCֱc~tB^A ţ5wūqKt$pkE7q`tʣU:1@Տ<8+8'Th &|dgsv&P~tȦvi;]n)@IiGe{cB,jp= 3c*Q/cOXAa0ٍkX_cP b[Ȑ$̎yʿ>VF5Ks\q>ozn#MnxpO E@^=y`lBj=AY+{ od_k_:ƔpNoƅӁ) 3.i%yG8ͻ`[Zn庇QǗBGE*zgduѣ&W91W=UIO:c6}B?Zr\]}1:%Hz3ߛnv!5/i5֘g2^<ƥkc=RoS{Bld=)PWkV 큵zCJXdǺ2)X$ؗO|HMbJZv"j=0.Q tH2Z:r[ Bn3c*֪9Y=RABs*)d嘊-Pʷ&o\j1X:ӈy\s_[c߾*ui5cQy2:qX+Ӎl9>&Zcuߞ7^8>wJx] 3ɟ)c1.o[G-{ەux5yˎS8|}@lLZўۄn-dtSl/Vz:O.)rL ʻKe#ߍñVѴ΃~3Č,B;~D824 B=旌s9Ƀ%eSJ:gIY0e ?x%;&c;/LR/!MJDkIr\x>TfO1s L6Ws=1s1UN` -^{&1{F'Ddꔹf>L'["~fKy^m<{ p N1OW$^}(+$Ҥ}+x 8c׬5o%7zŁ;>up$枎 ׅ9s` EN΄2Aue:}: 6f`CgqX N$  .M=] eAe?/]'sCId]{2 Oq(0N>!fʉnXm!au962$=^/u2+ ;8W4gp?ul8Ň#W+`t-sKµGdDKЗa2v^4ʛfNrE}WfLʀ {1em {,nMlxdZw6Y #Q̀,FXD~smܥ\ȁX>4%5 ,-%vXe\z2~|y2gϯJTTr> Gv$ %MEGnQ{s7;q>$-Ĺxڸ (f=dm s߼G3{t ^sbjZ+w v0 7AfLۄQ[4-T)/9)6qxXziA wmM_3-Lt Nmg2{+XV C5/6?W9/^"&0P4o}K/>xcl4R>}D<߃hϬ)fwb渠lKu/O`kQ¸282+d>gOώT=R3 NƢIw{ؕ!GR`r leV^5xEWj+W5cOX9rαP,;y6 .z$?`iʳ ۸xc8S<6/H|IrE YM+bmCjKKfz.31oCJH $c”V,ܷhde<U2_! eSxLGu:SrLi R C8-AySƦJe jQ76ֶq@5!j.UJO{K{:^ nD㏭uz[$jBeIռ+P. ?-bOz[1w d,8kѠȃu_zSe[+ }qXDFUu%#Li0no~b$5>',yEXK}pL?55^J3YrZd~crS@gr=;{1+Q?⫷xY2;A1x+9Y<#z/q~M$(+Rs>Oq+уK[`l"oHa } ęO 3mj7~y݃Hyȑt@a7G鰋-h"sy1<4C1Vp*QMzH+S앱 3*ҏh%vY>o1nƒ=&;JWe' aŸ=PRH+ |"Q~/A.,JY_žI1\I RNiDygY3+aN ! cװp^+$gm_Ad-0Sy(>7eZ>r 6BA0 6&tCO٧DqɄ/ɣJHM`db`ޖf)N/!"Aa | (Lk0}!1&ϤrGU3/[5ʙ_JՒ*-%DzI-:tM"#f$6 QVE[ 40?R'ѸTS[o8"eqq ;L=S6  uV;>$^z:nw|ku 햂PnBK9vVt-@cQO&Tkaqrtx ?e[ohc¸;I`'Fo BCΰ6#0w#jZbfE1v^YzbEg֬̈́A6O9ĜG+P^ܴG)ԍ+ǡ80+֤U &F#R79<'o} ҝ.eHܧ$J{um%)DI\KӨi(G(=Ţ̀.Z:SY0naP(uyIQҖQ@=8C*!ա8~@̛Mhd?B:gEѡK#F! %ߨgbd19QAifP8oaqhWX,đ }YFڈ+ m6JE7pQSjzP!a[gĭNz"/1 h}2@q9Dı9b7s+ 6`d栍0 >RVSkrv+MBPeZWua[nQjZj񳕬.t0w8͟20i>$mYKX+Sq)~dBp2K2<CY'Z 59{ m)3v'xKU޲o4}T,-QxwMMM-^tCn`HOҮ>f _UOno:3+ᷥH8W:q5Y-hw:pC_)*șt K6yLpj1Pi,< SW=&(-W`Z$@Ŗl 9sK؝pLaH 8Y=%oKQûl$):h$u7$%\ SmB9Ѭ#P{7Kx _'Tpt-sh>W76DH]A,{^ $ dډzSM9#sF LtWXx8eTqKFy esG t/5"GC@EHO!b -Mx~;Hȟ:б7X#<55Y zL͖҇x9td5Iw_扏D}Γ'f(~6G RS"k9B|hEL$~xoZY2o=H|uslJQc3 vz^:w i/ 뷡߲NR 9'ӣb2W qWjKYB7VHM2Sh,2 BnLHIg5a5q]ePT ^C lJIH7 P' bU'1/&BJ=Pq_戌lv]I0?spΣ}BSH4t Elc.oyZ(g^ 5t @p4 6@MS!+:O:{t>ͧ|UBs ;[ Zh];xy=f@ yMu(u@YkwKpY{ NtDs:P|Sa5 #jhsJ373 2T~DtK׼{>@) EJđ^:kMr3<$qcygo᧧L܀ %=ZO}``vC^8-L4:i2U ga<  &5Did}hE?py!!$)~UcYغ'.pb;LQڋ஫#loLXiBpo"+U2gHW٫TĦᡶt9w$NG,muQĀMB;9#[ ӳ;UHy-}j}(߰"KheOػg 7j)r2ܝ/S7/W=x }=->~ZHݟxd㳵[at ҥz_!9dNR9ZO28tO¼@?/g"x6]0"j0NZfԸ? o_",Ƕ~KQ[ވ%_ S?53 Hh^1(fuϲyJʲAUĤ@(29ʒ4JtJlNnhVM5\(=A됚9,pXxs)]%zUՐ6FFW˔+%V"EaS-i72qo)ZAA2%Uiㅳ{50'/uIlNz% B;ĥ+8n)+G9;ymgW&":?RKto|g zU:ul/zW}sTݼ~{adcOۿݿkGef/?wiwC; 'o!.)׻3Ϥ?U_gks>N` b9/W% v~竽 -uc:߼?Wu~wE}[봽09U=a} 6[ÒNX?_7:org`T5n@wi9˅fK8+L1tԫ+ ejA ^k|vx/k#~|xy\뜃vsW-.J<-+wC τ~ˍچY$Ҟ7t nKm+|{Z8XA4qCH'XT(Ƕ~O *v;4IV҇*~ޯ1kq{93 8 {R[v_WGDPmD=xF[qٞA=0Èa; |BY:7LFW:j$<4:h3O̮ϙc՟7f犎_ܦ7 ;kc g7:3MxHTDFbg؋.[71έtw?Wxw#{V{Tc. M#`Vx쌛[tS ?j7|Ww"VՍۯT)>WPb0~#F:E>rQǙnџ[}TW'O\h|V Z/"6 ;^ؓhki>Y@uGxf-7&%+{o]?n/Q?er20uhS@V{H7D]FoJ?RyZ0Uf>E9wYj_64J][cic\Yƛʦ4rzbޔI"9w%b9")u-v֤>!VQ<ӱR >(_ POuGH!)pg{ +D~W]Lq'ݭ`b {n]#X^ 6^sSB9fg2Ф_?]Оrf~ϫ7 495J ME/Hґ;)ItT0@כc/VrXpߠu QTSݣY?O3{@g|ޏ1Br&['7/sx+_m oۨ ]TՅR}5L\ey͎鵆P{  հ@lɄf ݶ z{NGgҮebhF~ƺKV|a>5 /+yIME5N)`<~~~cփn@{Ѥw!ik #@o{=S(a&ʄ+Dsn.fo@݆"Pt˜-SOf@ڣN=hY 2Xv\m8If޻Mw?h`ȳ<"(*npeaWp,_lCD"_@&ocY&nqs $DR\$%w1ZgdO3{ v"c(3ǏbMN{ oyU/j w0&N]@a6CE4n.V+@QilF5# 걀3Nvq@pT: }6zg=A@ռ" Ħyq4?E"piq:؝ʄ)3"&:h)?Iop(2/.eթJij-MeJ@h#)'pW|pI8hf`;v s$7U4 ֯ tž^9ًznH -['E^o{צWoĜhE0 5k犷o\[NL_BD%iDohjO E?N4iͤ| !>ޒ20}w֘x>5 +rU@r1z2 jul0K b'NI{$m?ygmGS4rFz@wcpD}L`W a#pCYS&$;[CmL񿟶ɟ0F;Z` [e8g*Ȥ{d7o4ыDLL<433h MdDcjH)D),_3>ަGߞoS*SplD W5WJ[޲TT<ue#oyڐZkҰ, !6(MM'߷Ǵ{ʽh;cyI.fSӬk_ݼM3%'odƑ*\30h^3vQC+(t}qzwQtV ЊF fOk;vBdEV9Ѯ>j^Tw+?v9G)_簿<}d+Nj:v%f9 LܵIiXV$)Nɲ̀ KYLS |ۦ)MH?\ў \vcrL8"Un] M ݮۤWfQLa"d}K)u 㞾xc} i\H=\8 -ar- bٴ] 2l0eE.YR4yu(Go0Q'vUe^[t1I 7'QS?Zl#X CLɾbQ`1z6ODmaBh:bF""Ձ(Nq 6]OPWY-?# `s_kGug%9g Y۵ɗFY=Pp+biPxB!{o`y}^6pY;`b Zj}vd'Q@ĺX'(xZdmM w92MSἉ^m@t!vbԛ捴X`HϩP%=3јODy']%!fGH]wytv& Ve&at5/(1j0Fܑ!$xW/rI\lq7#=Yk*j3%`ޯi*9&~P\,Ըڕηj9|x:PW<ؼEeYN9k| s{=vך H)iBS_V-^Lh JESy&!_:bV6a.-sDM8.2%oL4f(Rb+3!?u(x70CTLbF_a8^R(4TԮ$Y(r;Y1ޮ,5^&7H*b6j0,#mMmÒ<𓛎p%vȜ|)glJ|k~a7,%6. }ƾқVv uppVcB+Kj^=l<[͆ME-6i648+*-|16;"W윌3|~FO_C!45 {c_^!VIp]1g?+N_m=?,3ht KtB997G~O}9Z֞J~]txlW|3Qq8`oeTk6#Gl뜗 z$hjӤ~<:EW],.VxZ$bcg8p 2o<_85Bա2@L*"N3Fi z<&p\!;ѺMY@ȳ*'H5o(kˢ A cc<-ma ,cU-bȧ(cr/p逜["t8 |h'HGMJ c%\Mq ]u1O Z!i&v7qmZ_iau)LyO5Fjɬku+՗5(w~yh0.ԣTpd$!8 kAƃ*Tc̞qwnj(eW?b%E,#)VƳ'uO9]2Y@6s;ނZ..׃$nC$4[bOjj'{*Iȩ.h ?{{gg"%9>mBRͲ3s8LWL<˛7ctyM,8 PŸR?#DVLyO G&g9#hQG"݉0͚pcyM&:͝ 1/7a'o W3C$wMM!^~HOm&/Am侑s>B5Auyt@,EhT0`nwaBc[L یƈ ze@F綃J.!xrUnұL3NZ=< @dձV,qQOۚɍ7"׌ֳXD+T\OZ(qg(QY r˻j0Z}>yCCjx W8!=JX +Eb!Hz{C| ڔXWP֍L+wA̲͊S̭wWYt`'ff_.%g ٓGh  5 &Kk!N0 s7^-dѽ 8^FZ"I!}X, VY] w#ǽ#&]&T2"@;AKr#y0*inUY_}cyrfEiԅlcg‹=5H 1qALʹb=/=ݹXɠHiOӱn2o(o^ZsǴ`bQ|_ A/ &Qs¥ɁoDȃ.\[ JxZ:)%?d1<:>'i7Qym- AMJZ(((-aԵeF`` *HjfG#@wb =H(׼"cmV |bg >%ƚ9Z$ v>7|c@:8,UT@`vzk P5@ʿSd\Qt0$Cm6Wq.b˾+DܽM\@/uMgŸ~%GA*Vs/(Q PSd Ƚɀ_m3Bkٛe+ }N b$n tQUm6#b83o}hT{9Oe~ִC >ńWTF2>3,&[&xz{i‘E#p1=â7 a|(bXcIL /-<Ľ AaR%sn}`tn>=09⸆`dtbQ/PvI둯p~aRӬJ+Oc+ƔGڦpͰ)T} Kr>a=Fh=?&|@ _ۏϵ VT`K֊qxMbmxu+uFH52>-KFFeB{I!Ў}[r~OgM z;4F[PY}cc 8FwZsm{x{iO! ٠1g/6o ji~= [ ٌOQJPa("byNOQiRGy'CN芏GFl%եdiIb™ v}$r5 *R?ƼJį6ZZU3?qƼL2[*K4VMUJ\Qs=2j`n1Ԉܦ.haL^ §!e6,~pP?>`5.WrDl>G*e>eAk@ѤEql@}r<Dn |GV[Y4ú<< ĕAFn(`00ELX@Z0*JBiLX|W[< c [[Qvz|"Y(C#ڇh"3P&xưWZ`9"";ԨxŮ1o\*biʋ} Μı2fyTEf8n<O9mzC>}A/Ut6}9aǜ7 Tԅh1J{R_%KC7XQ4b<7ߘk*U 擴*ЛYgJֆdi?<<nq瓅?̄(ɯIrxuy@ey-f!ҜՒ8FskyY9IswSSXgsL&ʵDON Dj+o$\a4^@4N#{~$+(5{C{~ڡi 咅-6zv؇Pҗ TjzbYӐkY̑%G05]Cv2('i.<h7/0`džlD 6"lƝ4(;0e*%A_["3Ë:¡W4]m$tyIxtXyTQ%E0pב{cI0K0Ȇi/r##JȞBIOhu2_: mk|°5=8/#} ھ6؀ćӇ]tԅ #a1 ;oƋRVsvuq5OsFSkxq}Y-|kG\:۴S\Ǜ _;ZqevtIl{Ͱpt3EA}%!O ְHT<;;,KRh0}-@{E gԱqW}Tx(9s0h+K&L/T8\7A+!T^sOeO`Hj#& kOEO|Bí0g!8GXU*-=Y6<@n J`0a:{alG[Mk#t񔌵ANj;bgI\VD7$v uґh"W[ærBe $Ə#r$Ep ]GKgAo  ]ۧQ\9\1&ʧk2yU3<ޝ*Y(Ɵ Bz*HtN\z:J ' g<#(̩:Q4?A;9{(c."F'5hH|4TT3ZN%-$O<5-IyG^6$_Z ?,d6*WTIgsfŧ Mez!ߒ Wtv $kH:|iĜia\f$WJ쾍өʧ/SKT k""1QU;OpE#QBnTMFcdWO1 i'a+dE-,ɵד.W[8ۄC̹ H*чb$ubl0mj<6 14LQ cfU0RX.Jˆ vg8.2χelm/Gyp<} IO! ;I nsܒ_m˘hoReHk7p7cyEw=yd-I i%:u]|'|(-Yr8f!#\xwY)"5[\tު%[U t;ߦ7>?'/ JF# h]%cAe}D&{w+*<8)t)>Fh13A0aJfoQ0C&6m5jϜlꖪN1 V=i4I@J0zPݷu<ÏfODyָl\[/ytefv[Eyh.2~n *;^DW8Q\)Pc-z!ϏmNO(xLDډ:J bDTd<NYnK{'Yp Jt\bn) ߄ %0iN˟D8r,!Z+@fi B邅9E#cc5غmICC6J26S{o Յ~ eNV6{_*ziQ ]"d.%MBLiZUwjh9g<x1m:NА#n!;w<d=ExF{g1q9?;IM uVr.5(ۮG|)jpH͘?u@Z,Tӭmӝp'|->Q^Z!EhU.0T9([#S ::PY~S_z%-m%{ϗ>r&ʲp%Vllfr̆YN0z o1mtBk4#)U Jv(6^ UZ+%dEx䑢R MχOAl$JwG"Ɓc9HrNFj+1DeIw@+Y KG:{2ҒWqzvSTg{m#Yc,;[pNƆ.B,d.FӔ]Zϲ $N ._tV%mV.-' T[n,exEg;j$9^~Z^[ eeo"WWKFSSs2_r+JV?! 'QI,nk"'vlIR<9Q , (#we^aïVq\%^`Ob^z ̨d,pTG%zeoΨ?'$K֒^5a/bk3]5Ge[b /$Y܈zKnTR.6bi#]5MN,ᕲZB&ҍ-6qI{zȝv4@Z! =0 xQ8p>UCj_0% s7߲x$Ê$k9}v%QtEr^Gb)+h##Iw滦[k+p1`Pw6.?QV`m5+WA*ji{11~YP20W"DkA#@K:ʗ,Ac s9Ct!NOOfM):2"~NkuC3D_Kގ,7M`[kkœˋWn]?qbb̺k-k'CUFP%C%xx]Ae'vZqq=b[bk]uwGNfGJa'+NNkhHɸCœex|ZzFDžKnmn >Ja`}ծ4;8T[@ n[oe1m刐e0G;tsG`k;'%,Ni XYֲNtYAU݉x8ns>`p4·DVO--moo7;=[;Ke]?ʵ1<tX良ʩ? X8 #w="r'WFjZ\tG"N{s :TAO|MkaGN:"1ֱ: iv o_Kz) Բp'Wg/X4V)KH(SxdY+;% !]$Gv'aF억>L-%pdq [ N<9ZDƂgpylȄt`qFJ/X /TK#^(4 Oip&>-r+b:#M^! ?#*QX4,4hȈ@MV[V2P/Y= nEqŏ_<ܙ<Ä CˈPk>I$fˈ(~]䦊P@ Z5=̛ 8.ecך\[G(YD!}V7H&Ae}&ا;|(yM֦>rB"6(G4R4l.'ĖVvczQgJ[V "W mm}w ͳO͞/5YV&@Pmr7)"ee J+{ֈ߆X fI6ݠF=jxg2DhXٛ]¼wɄvQF٠^t+na%bS4:b* W1 lߺ/͒+:Y<s3@֚]Lr>SjGk_:\8χ(p}Q{\ xC KB49kyH!=T+pXfFȆ-˗'-mGc񐏡4o2:MVObT *)Z6aFx5~fSV)i&k/,uv+1whtt>Qdd;dX[ "%&$u q޵&I\ *@0#şO+ܠƜ'Lyb}mZMg; "Wlq-#PRc8[Ѽ/]ZCVYU2ȴ0?OZq47)jqv#rUx>IcoO< g9ӡl5BUAI !+t϶\y|iC>1Uu.QS+"fgt5² NcC!XqEȈI*<$4UI+!7PF{|逜vSP[aG`Um"q7b244){_VN U3cΛ$MISʾ9;Df:TbH"/ţp︂ymmXìqxLl);_ v YU`s|RE.MԾԽ`B %70<2W2.+Kܿ$WXNaOy-Cvܑ`RrQ;8T/7ELΗ.)W$5w?qH^R"'adW@ >G]};mNp+~ͷ )T^WJQH8$*4CBg*AK jܦ(Gxiam5~hB4+PB|gt$]YѤt X'A Р0ȖpnzDI7d V 3G8T(1&medJO߁ӭꉜ[ŚdZLDZ+X3Y.0'slӞO=A+! ղSpc)э!0㤖^T8{j )cHT-p؏7gdyLvi^#Ij(!M.(n 9Q"?yH4up -oDK&E]SРǣC-_ht¨5zdxeN\8:#ŲϜAmդ|DŽ$¹`*QhOX|Xi+qY|!"dK3ȿSfJJhPs<Kq) T0N{\)'ZA.*݈5]+:] \EGww7qb&Wd2^]]n$$k|SxVɈMMsk&T0lN~˜S1Pנ'W>h>:S]43NOOD#Qa!o2+-~ξuoP4 IԞuq@U_(|< g8{ǃ' ]*XMW:\5lIk=.A]K4X0j3U^m8׺n7r,saKmxJ)sx teHVEc'Tv*}4:ZlV:H:3CXYh '-5Dp)\u 3RU-Qϼu9uD5}X4j@–#JCnBB0Gyv(^-J8i҆.Ry5DLe"bDNPqdow őA% u:|i&o{_ a`L~x#,j>z_ $ >͘b dt%Q> y/[KeDꂨ[&Gk$!` 4Bb AE\8krl(Ef D$Q ^RzG쀷ۓ39FI$ONk>b8Ye,术@S] 4Gρ 9|ȃiUT̯Cо/xcsאLi=lF?<ңs#ٷ\^݌иE=r ᩪMtI*$c[6a({E jf2YO3B\3ـVx`voMs& ڌ*? /w[͓ c,aÊN~D=0ٱiKS2-u;pi7HH7 Ƃ^^bN!DuNՍc1ڍ2M2jj"S_ k^?1ƹljMC7eѓEM:KÅQh(W,:wEh2?rb^zyI6ORzvqb`OA¬oaxҪ]c3/̼3/jg ͢|xcAed YHCF9kn*dv$Up=0 22S_MZK~3 eX,pQ1⓳_؋H,3~j68< MİϖY7Fy<>(6FJfފo lVKZ!F_jP̧`9H EcK\dG"^¹NNC8ʞSRb!0DA H`r@[0Mrm;V-ݒCxhrap\D.ڇ/hM=9nk}M2Ь}QrH"͘sq#03r^%Q-M,<ce;7ŘǁA !@Ɩ@!Ŀ!B( ->!.C?F(," $U 5@ %V+9c!vW*!(aDD A)j!iRaVHQL28gZx}ٍM5>W-}"RkW̃.fHѭF@S= Q Z3_*W!H"M@pU$G Щ`Ŵ ;}VDLj(վ󉑃xI龱OH8aX8ߏNa4J)n9H]W#e &h ``ATnQ2gIIpa_='>}n:p Գ8:!8w=2 wPWef E5IFK>\_58 t"IH`*O4GP${:ma*,9Uh-IĂ9h s.Cd,*g̑%03qg"lTH^9jbnyKT)ߡ:.Ҫf )!-T|N_fqVbдͼ̚@4f7Zݰf`G)VLQa~o#Fp>6ǁ Px1F"l_*`i0՘aGqXd,E%`^6(&`#YhLJ^U:'bY-/L !I(4.7<]9e1Br+ss I/֠ .XԠH=k ?z8ZAQ{S,@f[nGguj@ fWT3":vEU7kL![" p%ZobXɱ`fb(CM'쭷lÁw~$;j(0#q.u0ڔh+QI%-#rD9 {zMd %-C@BO'@.9"0$No98Hugd;xChyFjH VvoFc?q>lgP]W>ӬqUy2 JmydU| 1= HwJc̘d@%'%,F|$4gH?b|7z'D+,Bv7ҍ1Vc /Y5ۑ( !$עwLF]mkŗhl^8wɞF 7`лP Bhϫĉ49jqrEGY^BL:ƅmNbJ pRcz\? ⸒ ArļA"&W3s8HFd,-m5hMs*2a(< &EVCk#jN ,U p-< *a;H5 X#NJd霨@՚y7ՀpHS2'O.z5xwT]/ MI@BA1`!IЋt^^R*]@z ;߹qYNJg~3{yvbM 'tt}?(TwZK]ziymW[6;`k4kh$-gxO9 'pxJ]#)kB^OaY] <]ԅY<b~$4ϟ36J Yp] ܱMCU" źo ]&#*~q@VR}A ZE䣌-!KR+5;;ߘGSr<ۺYh[we)ã]G{T=*gXy퇝Z1iE[yj^I(H!(c{6ɑi\A[jYFQO,4l(bTDƟ:`Qw&w)${;w=z̃ԜNQ5Pi]jk@.)9g<4z='}|.>Fyk'&󷊉Ý ԟC $=ʕ}3 ?8]SM'[)CU.!?&4r&i0q~gOzDk2ir g {ΐ;~g.(s$+W852@!,RHAuW]fSLS188EB~i ?_-3MW"o򱏲sGSDΫ 6TT}g4-Hi }bȀu$@#p%هM֏OHKNmͅAπzec3ϯU>W,\g̪ BJ2qcY2sƶ)y@ Pݬ/xʭ҉<'YQ=GyΡuƩ+o[vA4c ֨-(yR8}9L~Rw% ICWW?0It,?~cQ?+Ob2-ɓGqsò9cfE-wѧd=JF@^z<䣓AA ׯ;yE!V;$BsPģL+1c,\Q0݅aa>Fj^.4;$#:FOE!kޗыA-j>OZ[Z1}ucå = `$&邻`91V׶SL߷Bi$ k0kfc->8=~ #pv[6mÈ>Z:IOMc,TE?!dx86=\$4WI 3PMDg?,6C>r& I&>y/kԶ }9o2`ҶwjMeH+NG}Xhp$ s28Fce6K4&{ u)W "SZE65:i179FjsƝirnUO`Laսd_;qҗfWQo^Z Z(L5w ]~dUPv+~㟅3LNy3|&@ytN*G$~G5&X>YMT=cb+E\NU/ ο ]@\:'џ8aRٻUPp_;CzEqlY& +db{Ni ^\ߌjܸWo9wt|wCM1ZMwMYPN}T}yޚP=w4 p^w߇ ࡩ7[qAoW]@髦6̭A͜[o)dB9ks6ü^2LV|=[$ᛥ|WoO6~#o,hTV"Dޢn?/Q<_XQL׾2k;wt| .hKT(]#CDp--\|"WnIѸ÷oњoف1k-D\L{T^>/קh,s5=!)I"= \tW EY TI{C#|^E ?d-Qђ/WAe?_.+Thll~4ޠlQ(CȎ(%.ؠđ4e48: 162[V~DG"Y{@ѱg0Ǐf|]پ"|S>Fے$[йֵHmj1J b2tC%JUr5Re|z=>n3O=̜Ia~9ٗpӵn"KI|Gb,^}_ORzA) no-OlTng+{6A:Dgfw 7L3v2 J֑2N+ʞ9KO߾eǻ̿^k^s]2ax`Sb6hRC IdQ ݹ9V!%=ќsHi,NUzՉ\qK'gL=L\%k,}xA zo9F*U/42\?` ۛxLW-&Icɗ6g' Q]\ SEXdwOiGK~5?]3q-'4?aZ{ϸd\1WHуt1RPg/Nԕk8Zc>lM)=+uc>]{>{\A -3m9Es? WfTf.w&NI; K5/sŴt!s(5/>Sg0w I7E=r6 ay^敯68Gv3>9>DufčosG.(&-\i…^U_@zVl=w"v棨WQS3vW}=]-m1Ǽ;gsLq}ゞؐW=t;'Ć oWK-n-)2 j{n|ni84RH%/fdoyAQLZ@2KܹhHM\k;z<^s7W̺ B [Le6=~%l2G\Ne0Zuv붙)5̜GS'$Μ<9jS*dB|g+fyn$Z-ؼ?jNb{~{vn2dn-yҳV+L<]{M~a~hhk|&W6LOI2vn wz[?{glXİN\J~OW>=O߯?"-V=wo7ۇ45T1#\mLFi#w:~IU.hN&L{Kf/1Ksjtpt^ 'W;/D WJ*0?-/pqG1) M 0ZeeBxyuUG)JZia6YߛNs@[)N< {^Y~;A}ə7_-Xƙ|jt9ǝB,Lԩ`5|ƊBNqLAf|V*+s1vGe[Hj&ꦇ^hYI)@q7 g6GSѻr"e[U-'a{Wȃu2Qf[ݮ w61q_.Y:}y[ks 'JGnTLW?³3| n6D%׎-fn89}HWl6X^9-yqoÞ?U;6ܖvtI檵k[} g?aN*m194:M7x>x]v73Tq_at䫩\Gdj.?~ٴB|-Lh`(ta =]/_,]#(o w\m+ҴTCf3[(Cَ &]&[j6wXűJ{CKc+n,%F!_&D]v>1ըO%kͺ/u Po><#vdWJhƒLg>-1L#?N~4ԍr\˘谬A@F[N.9-蠟TiQt]Dg 9CXq҆@\%: [Ҭ9WzO+^[wT60O8uJ-D` >k23P|z .n.A$שx9@ޢrsiŕxR|x(|c/{ڍӕ5N^jzeP}Zհ|_utjoekڻZލ&]JH)piJl4ߗ \+"y/f8,kn5.hF:yݞ'=5+̝{txn`klqX^ mfQQDνѯZz&%ޙ_9vs\i)ȉ3)^b?0l{"1h%?#`U~ͺJ?0|-qFNR&^ϺAY,sϙNJ<%fŏI h,)Η3v̀W.aqOaP[-&]ԥPVg7{>L ,]>sƱ?DjYINFgT%:tx6yPh|9D7WNZCP  THV_J '@"xyG$?I$hн7ŁXwL\siҴ2#J g]2EѴUr%'*uJ9`jWķX߹`9Q3Ž8C ~8ƮcvI˕1~˅E2;&f~4I1sYra\ g;<ʻtx÷ݒrbJs khWM˚ШJ9fR qҧؘ]fOt́qpĽ'*NU_.Y "G7e;|=u߾nL7y.Au/iO?{A Iߜ\] =[sAdyL /ͺLbRLZ6?A!ej/s0Uf[ ۿiPk =ʠxQ*~)oיkPZ\_]PȹȈIWUV&:`q'C߽MUAΏ7M?~@W0i0EA kHYˁ~@_l?5YIVk FC#h AP,P8[z\<@AWUkVP'B0p (x'@- ("!Qx @8 J~@u^,8oڠ@q8PD$/]Rп[& z / u;o# ;_R-eKm08HD8bPVx@#T5p  %JRtQ0-u8L uH"Z~Iq0"Aw$F8kO9bpPk ;o,cxN8 M  6ۓPMsp?SCfg.T"ሿ$jIAHpp0<ʎH.IEjpp<%45(j ;9X;%QϹ1δ;::JXRᰒ STp; N$abyNw?NϓNuKୀ("Rg'D u==QwCRU @ EL6( ҠD"؟_ZqkSC'AQ CBmp@mjؤe )iAQ=҅ը@ o-+(,!aW+2p@x$}l4a&E&H-#" 'I]L И ? EjPp<5Q[(xkt ƣ4ځ Dms@CgcedddRs`I^%!RC @M%a@C*>y8Ԋ"4G?~)Ŝ4H {HYq OmTΟOVdg`lx5C~~g ./oar-2.5.2/third_party/rpm/SRPMS/ruby-DBI-0.2.0-2.el6.src.rpm0000644000175000017500000031344511757171206020770 0ustar plbplbruby-DBI-0.2.0-2.el6T>D ,0@9c1fd2b42d11583ab56bd164114714975dc31bf3 ՞~]xg">-?d F ( , 0 8 z |(89:A(F/GDHLIT\X]`^defCruby-DBI0.2.02.el6A database independent API to access databasesA database independent API to access databases, similar to Perl's DBI.Ooriganpublic domainIan Macdonald Applications/Rubydbi-0.2.0.tar.gzhttp://ruby-dbi.rubyforge.org/linuxnoarch OOb9836c3853a823432e45bccc4c29d33319847b08974e06c797f0461acf676e6f plbplbplbplb rubyrpmlib(CompressedFileNames)3.0.4-14.8.0O@H)EE@C@A@@@?]>@>@===1@="@<6<@ 0.2.0-2Bruno Bzeznik 0.2.0-1Ian Macdonald 0.1.1-1Ian Macdonald 0.1.0-1Ian Macdonald 0.0.23-1Ian Macdonald 0.0.22-1Ian Macdonald 0.0.21-1Ian Macdonald 0.0.20-1Ian Macdonald 0.0.19-1Ian Macdonald Ian Macdonald Ian Macdonald Ian Macdonald Ian Macdonald Ian Macdonald Ian Macdonald Ian Macdonald Ian Macdonald - update the revision scheme (add the dist as suffix).- 0.2.0 - builds from local directory - renamed with caps like perl-DBI- 0.1.1- 0.1.0- 0.0.23- 0.0.22- 0.0.21- 0.0.20- 0.0.19- 0.0.18- 0.0.17- 0.0.16- 0.0.15- 0.0.14- 0.0.13- updated Source and URL tags- include more documentation from dbd_mysql, dbd_pg and dbi lib directories- 0.0.12noarchorigan 1327401246V$V$dbi-0.2.0.tar.gzruby-dbi.speccpiogzip9?0tzS. xضm|c۶mm۶m۶mk[[OJ*N%Ll̬lɀ &VS&N6c9M w?m7olhAHLHlHo Dhs[ i{ @IIl2o4>#+vLظq@ Mb1}?Yx횭b[[+3󭳧rccfdr{qֈN{PJgխ9>ctv aܚɲKߍ7xy~5\!cA'hx#;V_ tiwDvg-;(S@*{N 8nz֔y`7lwUE}hɛ"8mHEcLZz/s] }*NzkĘۙ} EnhY`ĥ83=;!7èhp貰Vgc I#S(CHD_!,8)9@/$._SVUPk)3P u*[`&=& 2*<|)GpL"a FAaHߩg2vEg_f*yiFucmcr~K?E,97"0E@CpK;|(Ԫb)tݬAg[J'aso ~I3*3(7z],Ix]6geVTU(~B!P:l-nM6b4Goh`6avLΑ'3 ":(Z͹cI vڲ+`)a' ^/Ⱦȉ 5jvJ9# m\.sϲyvםl+!_;f!cH\j7a*o;־qy5U;Gy$-ԏrC'u7 iYh,t~& y^c›TW.Gt=rrb@NqN_XI^ajZْ;1xRnRk! fۯҢPum˛.8 v_XsR@ty2&)mn FkAnD}~I6=0Dp+,FyM5=OJ2 " JG}6`^Qr;M駣3?/#'gnu@`! ݎ?l+BY*I9߁]qxQ`iq(kcWK5同##:ǔPfN.Z =<+bd GϬU'S-w!  eb0KbEB0[#[mKe.ZD$/8,6VZ >bȈ#ݺ85IS0bCCq>Ļo8 D5;o*LM6t==R ]q=X\}Ȯ4;ʬdE cd#H>7/C `sO!o`l8CEr?5cOko13v$vNXK/XqM'|[?mVQ)1!2~#P?Vt݇['B)aΦ rZ9_Q0eB樻rQ| ͷV)_ rQX%C2q"-:zD4E!ZM"=X8< ki d V8W'/Zw-k4+ݵ*ʑov 8gFn -ԽUE`@h-UBuzvH-D)_=xa+$КIfWf }/hDCw# @xf~њօcsF!D@]o:&Yߧ>dlH"4J)=U'c".R#ldX~h{!6:!8qZ 2Y&hA==TԯE?8CMy@5R]y@APËB dD2[ŔM *)AEE@c-$k^Zn\vLG,!pt^D.L&A|镃 `kUqB@1bAlCμ ]A`f򶘗WuWfHϦ9@"l>#2~_@AdF`vKeܛ"m#`P' 3OҌ.A&pȔ'A7'7yh蘑hLG{wPMӛ&E/'P׻ FT>AY?[?$Աİ +#Kf VN{J|yꑪ-'$iV5T$>/.`!S2+)WEvn > Igjټ90 !H 9q7@?c.AmrgK3f*hL1aLԧ5K`g|`L~h߬o*>Z&&IБ7 ]XzG8h8 W,TT5ޫ݌LWgD9>-mhE<5-2M>TQ`l:_!a6:p[NIBnW5^ KL2!dDv& Y[ĝ*儦1:¬w^Kv w79a趌3%,J=}ILt{FRHB EB;B2Ӷv+{RvgFؾ1i1ߧG#YLk~_4uӗO!};@ ѵKg)gl_U"iǜ(Ae1.09>Qd2C ;RGN2\:6rIНk?E?៴\xL2)yA,i ᜆd^$G>4Oixt4u$YZ ĨA9Ig- e, S e Щ% e{wTsd~OzRWƌK'fڨP"DGB#`?pv_+A<ŸfAQ&x*υzhY4"Ն\lRei(=`j2ȶѨ$ LD\%n |2~QDJu(?yNvtoGnMUԻ@uwA79"b\hA(￁ a 2'o @Sn;|-b>]_~\V3%F϶^ޝ@>E0,q2 GPzԘ!xm@X-#X"=۪'me)zkvLǂ*#Mru.7~T?<{([t6Ɇ)Xu {똙!bõ[ΉwSڂb[=|5zT e_:[cr]a]1D,ǜQC)'~Ȅ?9 i_%u9"pa7ɸEP v:~@`)( }/'u^$<n{6v>VjgCtt˵о|!x߮*o[CD#Dga;0Ͼ_Z>1L 84({7]  gYx'4Wfht=HLFY{E~) 5ERt[1-jY#nI^cJFR8c!skVnR w}0Vܘ@%[Ȁ0>X> i`h]3 qj> wǎ.[=)u\{}웚=zXqF*gaP]oZa .3>lH-90/}*ZVD5 sYa^VZ, a z8p|DN; 'w3!<PJ:ޓ$69$.;jQ എq8{0HhWCdwl X ^E'ol=Ho N"]߿nъw/A'juҞ)Z^zOzBln@X>1BpFl{vet~0z#n`/B>ϒn_b<4܁?) lu=c2B[c'3 -3Z,K}EIR0qݣn\)I!4[$F}*9 zK߲[8FUn 俩?!bp2@'`pOq!Τ1înHܷ;"JpY ={7ǫ٨e޷.$s/U+´0qPIZfbt}9w/d@,H-l~G^[}wM~81X(o¯1u _uX \f*rDBU!@}B%'_%]~Jq!@01zuǂ,'S| fw?$(@.p- *#)SD0!CJ ikD` [JkwiyG}2X#rY] pRf !?nf>b6OQ-~)ӅXm_˳9W\=|2pM]mMCdQ -e S=Z(db}4CuM<)LnM}1'T08<..2]ٗQvOSGny.vzsh /· &Tɞ4űrF׵OwcB0T%-wTؤsV'=?3Ęh>pa!8YAnMQ/FS qBid!h'Y ^#DN^0=e T b߯I)v6Ь}sƋo8vQ0d,ȑ!äX*zt]YP(Rj +r,!!8vy``mnGEE' ¸\aNN 8VI\ y+kbR$o\X1oZ61K ~/[T"4A=c[du3 ).^pͳ8SAx-}9,4ɂwJ)" S~ޝQe,VݼiY5wW?B8O& 8$d![+*Tat -[ zSgq2_c)}ROKq~seSq%a|ymڽ+H[Z#$@*tw+!<._WOp=Cji[+jA-^sCORf@ݤ9j@y]RbHfI8WY6&5ˀ nyu>>iFѮt$ɨ=2GSlZ/V}S~';;…%Q_PVByVbQ*- G%",do3C<:z&\ S[CB53楢 ۈ*#(zٿI O<'mykQ{szRxem%ps1ǒ#wQ& wfk+x dOHf΀o5C"͛FǽBw*ޢUKwO`(ا?~i,$ۡir`0(ω7W2ӝp*Z*?1e7u{UL` )c\\O.*0egTS6-K"74hޥ[*>% 1B@) E("d$60X0, ;hRe}ԀRƘj'W*LX L̚.@(ma՟YA6J"m>?w|5v6?xdYVf).kY:UG\Imtx/TuCך*:;8yMJ") "c6/OBpuY|)S_}9\XLR쥌aCk73^alz]ޕ\ƈEx@C#ZZXyZ/F\\(T킂-a"Z@Mz36Vcv`!sǶz3.JƄyW+{ބPw Βo6eË>t2Ool`>qkHJaY xLI}z+g[8:51iޅNi@ $^mFB|pK #{#{Eq>zP8"9HXr?̉҇(+ͧ`ިBdq"KgRsHö́A.>-c+Ъ 图c7UijTtCdzo7K}cShbkuhvwvQުT0b[?q~#)Ri.l'r2p,)Qys цJ&ͳ\CLg*Oj+#-sobM} _8Mk"o[:*c=-i `0յuu5t߽O1H'R?3bN eW5Ig7wp1eKc6;X t*{C$$?7b]^4輣_%">ĊmN:>.,|>b϶zHck*C):,a@6+(C;X,xЧ}(~ qe?|izQb>]ZpzۋO1b3qhjmottS]5_nqNIf ٸ .ĉ]*b+,-5M|Λҟc@WF`N,JN~_Hm2V$s ŹRfR7si۰,'tvK*iY"^TUL K7&)W]u-7Uc!6x#H6=Dt'o%:KlZnWQb/Ɵ|`QH<"[ "7&7&OJgsqxQ@9D%^ w+Z-{`g^fMlqdő. /si{"X5IJ(rzZQ٣`gdBp+>|m=h2J~^T8+u7Gʏ2FP(c,ѿ 5Lp DV cRN3t rqb*)^tZKĸÎߌFdx@Y_ -ܨ9VY݋rX7W#oZ Waʾuɼj@:їNqCTA#V2_kvPcW:[>".Ͳyyd|c m(Va@38%}` KTOF[uUM5]^b)ӓU‘;<&7բ%.g724Il I9&k.kh`zZzdFaŵXRCTUl$֓ V(hѓX0xƑj,1GKApmVcm tnyc.O.`mC&[sjk>W n74(ݸCs^}d_Si ͑Nz: (Lvm}z j5ɸ}&,~=]\y5zaHwD*3?+|ȖŇv_bR6\M&fc[<8_^eH0F{wQܤ=bECH=Jԅ0e)BiT!ev c3)uas`{9Uwʢ$f$.q|k]KH4la!*Q'_jv6 <+#xY^T]!:h0mWjqF0Bk;zrTyh5:4[L!gWi"1nac^~o=&*7x `ܥVN+ꥸH(j|YRDU~N <=p0bRt۲J]Dfw!DkS~Xg[˕+"i'Q7"%$ \{ WnvFvnn~is}mWq> y33ǽȁ׺{4yGvFrΌ=dz+\a'ˑ{َ,);?+uoq.$qujm+tZV/!x6^fޑ-C#6:IpVV73TN,PN8LqXɌSJI@޺I[ʠc;?%\ɾoJg45bHIJ̺=ܳwe#J{ZvR}3il絪2:l7)UEuY0TPa`zu `/ a)f^3o*Y+~룇cօ舥U\[;髡P#~pv Jņ,  Ša߸ϙmk'u=F?GewiJWzx"٣CO%"+3C $E_?bf@qVb Cz.A)W6"»{9ƻ*PP)ԺwS?vU6F>Yl\&^T%V!O(ν"n`:xqqE{/"tǐc Qg9ϩ /Zki[LArG0-\s;`6.  e c.5QશUN+%,B#NqlU*آ'ȓ edڣ8CH2*2\Ju:kz~~<,Z-fǓ"ΡƢxǽ0Kn -jlA&GnL"d[__ x5-VUoG_fju2dmXBPGtc_l>jmjcY]z1?NF^V:l!"|UZG=!Ks(-ns[mHzp= Ur;-v;M۪^aϣPT›/vomnN#lP‡s"d4KKB,e/qP!_8 3J^v1ܷTAGXd~ ekԤ<k؊:e5,~eSF]g](ֲMgJ^'Vqy50t-Q`Ud4FKaܔK4Nş\~e<skt-el} ?#=N>X6?Ʌ_R_>.БMr/cq5aX酩ѤV"rÑG޾ݤ}ORġ0.*־S(?H9.Rh-UnBEʯS,g,Sbt%O\B'΍\S:ilsjRTޮbQ)')/确U yG\24NHޠ?O|- vhO09čbuyR`tMs#xiN:z#9Ւ ;H!Q[ 13,'D=}p)s ښ:jn}h.ZEz'wb>p5Xcu|zE`&o nl NN#`Ym-Nj.`jY~ձbT_,pxUލ}Ƙ^\2 |D,#B6V F=| NYoB$֬%λMWy3%P{ZxY>#TvmNS;Y' `wB$oD'xw}?QB4 y^u.fF=>g[2 5|c;ۚ~߱ l~ zYn` x@|#@>X~%t^! | {bs"3[)Rxk&Q^.\1@X6SsK %<4j67k4f 4cƛۦ\1.r_@JWEہPl{J99F~?wtS^XDm]3#x]i%̺FXz[ߒāuo()SvHǔ3).4 K68س$\:̷ Wf-IK9xW7O{R8:/F3fM4 6=mp#ܔaf]TpY12Z B!JާjiߍAKfK4N&ӽYuyOn#${Q @翓TyuHݒobPBTK~PB|K'ß.Dv904*dJo aޡj]Gm=JQJ!>N@J 0]_jJKJɩJRɞ$h0Q[9i T'z46I Iԅ=8w$uNVuEjC*fdϘh1RۼZ%Wo_-w٨H"# 鄟SE :'Ftq32m1^bQVQ#X=`$jE=s- TIr$Z6NqWtC.oPM]9-4~N;czWxIr.EӭnѹDb PB!\Fņy$#]eO!3_Ӏqn*bV ƹH]4& xt߮Iy]ww(=8`2x@ϩdMtt+C? *:ӏqa!OKt֒bHwJQ|_J_kj]~d̈́,{܅,a`죴I">;Emu5&fB=7A/2^rZ4I _rk : 0UjNj!זLKr\s:L$Ƽha|KA$vTLD߾&|/(*H9?":@TB[wr׀bzINBn**\WZPFDk}2dJz}y3q$f'UB\q,΢wu8SN؂GZq fQU'8$kݩ潵+NZΰH/^3FxJT]:{ˈ nz C\1>QxiSmSXu{JlNg{CIPJ+R*^4m5픊Ï''Y:@̚5AmV750)g\kk5Ih:lU, ĀP>K0٩aCY~` 5 ()oP`WǭDw]eX +tizaΨ2U ܤ -LlgΰjiG?YP{~f/Vqa̻Y1Mܥ^t9 'tzv[a |ئ.< dFiw< OeD1_aLE%qJqwG3k[~k0d:&0m65JVKM긡 Z&P7>ꂬ7sTΑi{olPvj`S`^ RU+ 6\1c쯨*EN irih@XnmEٙ6!0%X)΋N/rS4+^V|Һs4^pޅzwuˡ9&ozu7ߜ w5QU\]˸ڸ֣NC_cxCVBUr!U>;Jw9t𔝿 8%aZ-mÖTPoO9qښd=rH*Cdh NHE%dcgSju_-u5;e? 0Q_ۧ%4>)vRY4{x9GY< .cu7FwT*-J_@:KC{^b:[((:dO1Tѕt$R7cZ]l,&S0.1]Ϥ}JKF25@$ }ҋڤSƌC8L` ')-Bv̇l椠5 nn ; 7`o[GID䳺XKÝmYVUSvz O+Q@ Y>zA.`,EA`.y3h~)b{oȃIr%7[;*)ilm8e<ܽϷ.-j>*'!\Ra(>N\A|Vr k^A^'v[d$;yTP=|0ZңǏ:{/}Uoy߯8QlwЍt5Άw*P/D%طQ͎\WB%I DY &z7;EpPn'?"qWV5Ϥ V..H}¨&\LvkMҶ3IZ&[Z 5H;DrOjDDEQuekXf @WK['1?dXF.HP̜Lz'O2*Iu#;SڣԞN օO-a0>]lx jx h c߸PTa]1`ӹ6g_ɪ|w[ᕌ_VUa# O5aZ-l*^jѹ\_l^禅'Oyx88eLm.Q{<CA 1O!xܣEWo㫺:kHx&U\1Ӡ@JRh؏j|b Vre]6*!ϭ27JpNd'W ㏧ Y~D-grBj ,`͜ߚ%Q%4p [J@_lyy [_SZ`3]Hch(C59`V[NNEi^Rv1E&ybEbWKcܟeFv>N:Ž*N[EQwמSV<$̞!uō9ɸL*=_b,-ړnS:fN)_??0BՊ@:63t`9$(FR`&}])lڣ+Qx  h+5/p2ii'9RZcʤk%i)ƞ/s69iG;&S|2,i~+@{t,jtGEok*V[ YMNSe` @jGɂr#X |xn촹7x$@i?bKHСEq[ A  ǒ5Ch~Yj^hPzCbH8q$@5N<ݔ5J͢xK}]GK@O +r0\kՓDlȭtҌ1ksP|ץ+[ z8孇~#v<)SԜ)6S`;]yZM;SEf檸CU#T˼rEnP RBnj( TIRb@NFSմ0>@z+Q -ߕݗqХ r(Y׿e)gܜSd0VpZiƾŸƢ Jx'iluu^[g2 ~drpyJ տajnR*)Hb^2O&1W3iWF``HmzKichD9rQNbEr졓`\^+l]]QѢ@rSC%)^2o,"HNS5jPbiD<ȡMLKGtЁo mCr 0L\`OXMabE]v2wY9FRs{Ԭb,̄X_ ?/KXGvקT,?="jAgf\aDt_l^7[mzq{}2P}@n#1]ߦŭ^lD}7Yl~ \Sn 7pc H7 ~MlT߿l)^Y |KLp&ϔM]˯y5[?:.'qoƭ/ʣ_??^Q?~ x.U`Ľ=7[}LN LxFAQ4 Hꪌx&Aow1Cl&'4f(XQ ^ 퍋\k7~SyGɅiCȤ8C5wl\؇|*i"tGrܯޱ/F'֓T?5j'GI0H*k YCEG*xDZjg9N'7r9_Yq6>@V.1?VLV`/_$lmnɉ?ŚPjˇ(>^zPDx{ Jd,TT^)!i(e@Y]7s]ƥOZ ꗼ_㻏F«]\Y>uÛq;{ 2Aw3<<|¾ 2kͯjURwLt=qfB\&[o,(JXo3 }As"R,T@L~h;kҡ؋tj>gŠ1:V>ͨHoDNs׌ztnu茣wgze9c~,9gT,lzR|e8==h3_q=3S[YS<_d>._x<vsrs۷SquEAQM,s.'KGᜉɊml8|d\4#礽j38cT}*aYF=)' HnP|r-z"_HˋM)(9GM RU</w'ځNlZnS0ɋӥTJk鳅QEkE&RKè'>tL'n!]\7ʔsti2珶f-zLX~!|#`k|ʷ$`'w o]/+iebٵobb<èLOhpy~q<' P p];̲Yg 餽X)OLmM<&>HcWq]RK?:e |RTZB^رhC"bv1>Y(Kʗ]>k"IjI;*&l Q^p1gll`Aa2itbqc3)sѷ jeF߁JOghƲ,OY(ղ w,{TLr$!ǃ_iAwEŃ L , &My( ='i|oo#\߭|4 `f& L#dX` A&v*W}d5J''v` 7W-Sj2'bN0_"y6XJI6g:B9(Bdh8A'`S\:Q=i2+kS^f3I'>*u)? =$ 9NQG#f! CrE}Kz$P 73$)F8!aT%i7a&Uա`(k]Ep=H3=ʕ *~bUz'L'gB5?ɨ#fw1ExytƳgZCS5$(lT7^꞊+,d )CeYbO9 q + ")X&" j*Ǐg)‰SX2..F Qt6FQhQ6 7n2gl5YT'.K#g>M֭x=JRd4m팒]tSKd`ECZ9AZ)a8hJƧa?IJ/$KM3TmN2c{@֕'Niʼ Qg2ϯ?4%$8\d&ń? Lɟ]S>mXa$qO{?zx) ˰>SY(uaEmzvR|O4x+S?..?~|Ga/YQ Ul]ߎw߅ y .](?2H7P#;- b?.E~8[e4va&:{!sH޿1lN.Fd&ʪY@li2yjdb)<VRA̡t%-}?TlQvrcgц;#q w6 #cK\ U43n!2<((GƐW)bG7kV',ohnoxr&QLxatѮߤIt{eNOU!uXLk7 @u{+`TC>9ingx C?#s|TQ&J=Y[=SCLbS&K455+cxE8WMݵ_íU:L|2[?TH)intbM9gH#GpF=;sZ:~W4ڊ GUr#رB Z c,$M 4r,IߒKB`9հq4`l^l{^䲜U2;]ԍD)+ I ;|.i -:8kuOҳ~%G8SQ]'jeXWUj4Cj2Gz|viuy UuE>C -"%qLU$^-A%40'%`\}/vᰴ;u|W(/ˁ6#HȆ m3SzD76 "=IES 8M۹+7eqSv5,cD_aI;F"~ԕшojwFj7Ύ?`2f ;F1Eۮ7Aog<&G9D,raPU0%uap.v:vݡ^)jIfhuGx5&.]C@ 4#:Nr1-'`0@~OkEs3#scWVW2YP"о:!+Go68"U^'{Ʌ|P9&&MwiWWus!]6?>9;;Hfs%&VLcY}SܛZwYB彤-.N ^{Urh݀sZm|g.r1g +\(DoiF_Tsk r25t3`th@E; AK|nT7E=i&ĭխ<"ryI, E D-\0w  wFXp.X6!Oueǔk*3)PBr ۙ3LEdʲz*niŋڹeom#}MPi6Q4,k݄AdЊ,| LA+׳,*AmC[eD[^2?WЊ;w8Nju E=SU'U_=|IJp+wRT$U[$< 20 L@Orlyvp M]ځmLy󬪨EP: 8]>\g_IW-HnJ'qYs<؝ZY'~(}\7Y< Ws4\ahJhkx?1yDN!KO!t7%=*yJOܰ%1֟(s lXWǻ,h^;dY-s6 ݉HYE@_1x4dN1nEcFDXzQIo}/HLsLTՏ3J&-:]ӞtNcxjqrE[ L%]?0PVWq]1ssFD5b`l!k)Z ) iLΕfȏ1p_1VjY 1h L@|ZCN\2ౡҙg.)dAh΍]8QZ,S$i3N厪#95A"I82c8ДBS!sćUOח>qZe&h*^/HkVqYr|3p^ )xz]Y?% 5͐O~`a<9e1\!yCZ6iɤA**э{EҏFzRn,FAImY̫: .a|64S.̛7 M]'@H/"6M¶  T}|NU7ŀdJ4ⴉVr,h[ [P]3pXSdU=rvIZ(f[f +-+%Z2(Чeۤ_"f-nGϣ [pJhDaOЃmҧUӼKj) 3! £[:| /a 1\:)*"Oז#YUjlܮ.yP&aE=𵸮&h=#}mt "yBYc(y]"0tLQ\ԃTrF ">H h<O7DM3^`q1Y-غ2;PC|~,(5懠r_cn] h{->S?}},OzWꕊQ~1F*n㦶0ch oVqkM쟅?rG (4}Hd0*ٕvRl]Xkȋ2 )2GYa(~j}\q*&0ECCꚾT5)>_*қ@UH?uՑGa85i:FF`,Q뚱2dԛlj-_*'=Fu!9Dۤ,88Y^ͼ+ fU=^|x".lYz+p`J+KKKNtes ,Ln_^iZ{M9љGY}6F>yszOZD0E~9:j yU_?v& <"\d;djm<ƆPn*dc:E d_'vˍQ㲇ox k i*~}Kbt7a$Ѭf|\jz]01L;tqh$'HApr$p Aw%AA 1 zA>qҔ,-BgHD,ꋴ3G 穋yP'O/4g%xpVz=r>^cYa2 ˅di>8;tZNjO>\/Љ"u?Bޔ?뒣=bv{LL{ LjLQJ9/#XJq> V.// xlq>e'#=VyLEJuijDr *b~ߊ3Wј}*dJy]BM%2]W( =jo&W[\joeEfo/{ DL%0DHp68_pՈTSBG+avx6,y8ݷ >8a )0e[33.Z [ai2zJ-YV.%#2m DP135Ļ6`{ v=-O]]ődKc3dwFFLf̼%ݶw+hR)VUSb<Œ%,6g'ӉK\G,# "+gaz+vOTJ,10@ ~bMOOA, P1k|S~ swӳpn{VƇ@&=X16GJ#J8$ ۂdvs )CTQbߗ Zѻ(鹁N92bVtzJAuSТ^o0wpzӘOcnxN6 % P-OX<" -#On7k 2:˃S`y馀ĽnDiJX<]sO} +׎+՟J&I/3uwCEs,,a;SEQ'Rѣ(k C=j/#́{>sA>;Pƫn#eLs))R8nlyxͯj/BռU^/>pX~KNdWqj^cqF3/Jt3 xjyW!])X A_uA])6<W]alnB"%Y Wh N sѝ%/Y1]my1 BP;0qy_sf>9c{KRaN|jcky@4'J<ʒPF-Xc)g_uTGJԪTH]'@%+߲^-iTgשMR=_ջ'ˤ 54dpg'KyQfmjswPgus/3NKdZ{xkTd_IU$z Κ(79$2 P[yYwF!~>.WW"ݐiM"B˳5act4o(T;P#0_?J? 6bsS_}WKI#vXf-AQG\ L b{l_`,,=_?~pii=xxk|8ײ^q ʰܢ$\E)TlυoG}.].\^wry1tn{vN/Me \8R %YS"/bō-xCj .:muQ԰W) 7 X?l~kkޜ%=ibLC.HD綤L^̐KL(s %؟gq Rv8jr:@ſ)asj\[h[=T0d)mOw0#'/k|gD$7opWdtn SYt64:&Kj\/Y8MM@6QK0*LtieDZt& ti~ro5ߊv%_ ٻkkp}yR^sFq00{-Z/^&ut43ǹ0NNT^u1ESȍ*D{Go'vESz*׌%yX?9Y}yB3KCŕ'N؜~`T^iYm'Njqզ.W'rh_ 㸈)T7D V;dA 2M1?<9 <#jB':eyKibCҡ+B'7x05OzqBNGWV sLP%(c!K&)+747;X?ju-I=rAZQ:-,D1 Y0=lfϰLĊ c7ySq|BU~9Oar5T`0Zٸm +hrnP^nJ:&ioMAIʓ%JrXE82Tsf;B{s ~+A;,xHQ τ W> 眣omv4E,Rܾ -K5yPuMҮh88ݱxux'ge 푟)yffP7.п Y3s~J<T5&/N@i饼G"yoRɞJL@TvdVͺ;BY#HFl[ˑ9"|>جub-m/S=w k5<D бUL`Dn&`(bjr-0wdE#YKMϹtD j}z^Q#&ۋD誻nN6$35kGx BqioIc,: KPj+R+\7[[ۄ>By4TW-=dUvx?lɾBvlntK Q)W X͹*hlٛ1jIUw2쵺Y2ʠ;R`KSЎ;f@6A `A}!ut#`(DӦEx$f9 Ï6pQhyT^f&.">5։l̰aX@\ޞSCfҦi[GoKxv J:Ǧ'g4)ZkH&;#Jn GNK¸r#ڳNG%ڸTu؆)M͎sP+Fݰ'7AV(?%P L?C}-/:wԜPP3Ǥ4kQXZwu¿+x {G" i BC@ۢ30D#y<KxƅÝmǁI8;ث֎;JNJzy#i0wp!t=R; py؆' 9n`pv܁jاy!0{/-Po1H no'O[;{t{uDtjGF`*LIr74 3{?al{c&0&X?|.!MYamL܁6-L0 8E-d6`L_\DTxM&$\pיO rp~,Q= `pb/0sy?L_ ?xyM G|swϿ}Rw.MÜN]1rY.\Z6`_=XP;{L%#yyۊwb Y d?E(Ѡ㌅M?RWsdSHf˪W+ 𱫔1+.^WE-X䐖:*T 䭋YԦBªFG}kk؈vwВUByJ*M{F,I'<Ģ.e(Be#6hʿZkS`2ݍ.%FL}jaWK֠!NjF#q%FบPk4ˢDGLɭAW|(٣Or*OAED+/0ZS̩tDcЭԟG;@Q TRܣ.qjuatZ^+E;6r1X' ,Qjk EVk͢oSmǮ(@*߮)xJ@L _| bIq l T)$v,{Hsu7#Ng`BI5*+Ob+ ԗ 7ȼ[U`U; ,K {e,>3ӵ  @7W^,q TgnGl5BGE2wB>|n<8;``A0pKiegb9 9$P㓍N 1 ̣S`si{tO^sdܢhgǨyh|geNU>Z#u7_Pe;Q"\g x K`ÿ#:D&^R?s[UiEwڢ;m-Ru<5a2'n }K.F%dAc3?#Hh w_ʢuҌ)Ozdtw*`e3qܕ齐a*H(R_9PXje8BzrV}SbjԞ[&6 QfN2$6wہ<.NY㻘,a;r}iڱή{>8PRɷLc/3 qh -A; 3}@4J#cxLVn#@iڵhRM XzMp{Eʋf@a#v[gㅖjFol5 Ѝ=U62.F`~8+7P^%JifB.Yz87\z9ryO4B8u^ 06%qg+98۹0 eG.(N PP)j?rs]u:v՚e뻡^xQIvnwS)n0P~|Hh3&̮H>L0F˺<(n(';SULaFSJiZ<̩I3<֘Y .:MAP\)8h-(sk^7YMx\SyN8p'KJ"c,zw3S7;SW~}o_-lܱsfM5؄'SS05슒QYm6^ )-1Е_{0O̅nx69WF=hّW9:벤\Kdz$ʖtZȢ5+jN3 EF|,zaB#9&tH´gy ]%~FAE,cqNmyW885Y;èJ}ۏߏ2N%`Cѩ$ûZ㴇9W\b-h Q51?~byĆg8vZvfNNqugN +bR}d|&w-[tqʚ%OjՔu!v l2 fQЪhplQռ)w3= :qOS0[WMVZrDvxNp<$hpF8؂k"t?SȜPBp''ϒ{^or踜 %7dI R|]xH1qC +r[ ׸7vX L % Y;Ipg#A#$LLTc8Yg>q6hUڏBB@a$( PټŚC`2IE?zeޜWI9-C7a5Z-NeVZa2osm~Cq';Pxd+NxU}8mR}"_+ta_nAR4B\ 2}+BE'gx #Ym|`ȼrb"`Mado gĸ0G*=KJ-XHTNya5FѡBQȅdB6XJB_UlukW,W! ^< `!FQ>ˮrb=S671Һ:K"i0W# Za/u&U]f? rſPA\ 3p[4N{G8HFѿonVUkFVƗoÈKLQ$'pViTYuMBg®dԾ7ƦFY3S+4H\.۪VWTls8[?+RNeRo1ɕǧ(3RQI.XK(6 ddN;ʪhʌs5(3h!T3iɬ!cқ5^ f<%#tJ+a;~M r0 RxΡ.i( ֗>l}vmJRw_yK!-/~ff3ϩn"ANJ@iJ5>?T>Rp^O,ƴpUzSYEM^G+s}Y분ɹG G|'`m3GHU Ogv6JF}|6mrPoZ}%,`ȷ2l-ӚȖ +& x8FR eV9 8q]WfpKN=pkw` j:GbuZ H,gpuD"ו Ӝ*[5QZYZ^!sLJ.4m\zi:|Ti 86<| F}G*0Un央/L"(_%IqJ"L:s. =Rj/u uu=Tx'`A>\yXRzK U[宓OH(Ui OY)#SuyuQ:MDo5k<[ޚxzyx6V:Q3u-'bGxԹ5+,==6"a'k 4wNwNwNwNwN_ ˩Yl7 gR ;=dW39`f dFD >88 mKxf sR/f ^@Ipa`NqGt!0ӧ)<8|o5}Ovs}$rLEi":k%vCU<U$^z 50yW]?<mV$xLJ>&TsBjˌef^3N]q@c @dqne8*=~уG!(R0:Id@ܘ$VFɕ9yae+AwoW29f嚷+ !pM='<SlVxQًSdO 7`/ m{ ߦͬq)Jh)(P21u~*tJ0iRKy% .;E2 U ϟIl&q}-[0P2%@m K ptZ<*[ hN9ߚ 4nB^Ŵj*#Q'Nrlt[*X:,| XIKׇ/i[˴[#=t1@* Q;ڛ*x\o(ȤW&qPH' rjr܆+y}Kp$-4͉[;nim7w_\)*oPu^G!kʘzKIb%A-">-O(8S 4nl`X*{ȥ3-jzrH]ٞr*O59nZ v[m׺]R@u܄7(~<&s8ʏ$ھ[,gOO P>%B{ 2)AOA$[5GZ N}[ͦ~,M`%#5y62j+b)H0@mMA cL^* ).Pnզ7c bGyk M%{^]ݠ׸ƝZLi^ςDTY˘u!c 9)jATLY4&eЋ:}eFF`>*I+?4)ojU}{#7XPi|n>x/G_'M'xf3?)B/`~L\ VTn$ǽ'y#B#K5ңKgINR=?kͰ拭Cu^/L??~ NWqV^cqznaΖR]NHd)01(N+z\ 6jJ>F@H >[ JuD4׏~}3fY} E.Qk)SDaFPҦW]=\.B8h 4Y.<ѱ~;9W]SPZnR;>4/d||}-W2C ,Mo3Y/b>  S]2(+ ŻDqԛ tLvf(;Ig6oLS4 L"D?/nhv0MVp?OS`bDZiCq A ř~{S,.QOKwmRV|bOS΍e>6=~=^~,-?5>mo׏^5<9U_$ aڂvժ}{z !w. ],xY3 }L .>\]A v ˥~ -i0oxϲ'|4Est9"̃EOFп,ܫȍEyWe-BixnoWW!?*62L?Ŷ?P!5Yvߐ߬oPM'=p(yQ+xtBņeRvc.npZ)6 Yȁ>)Eίpe眊K520VOm,$5; pn3sn7nJfjz V#êgI bʫ5&䗏W M.5٦ < @3|!<1xGP'! 3}u-@NJ}&llBf(JrՊ;аd@1p”(#rEYK7,]qB=TQ-n!>'-)z)B>KmE1l g8;5c]z!'b;i5d̒/O]PeX^Zӫ6ʇ+sxxH5,Zg%פK=9<-muKz/qiœ˳˸ݾB#6]Ǖg҇,*TřQܧ4stޒ  's3tF7w@]q4}K2FꔃrEnP^Nd8+J4QX_uE | whⵏ{ӫy3lƴ "B?KH R[4YyybfX/8#i操8oM$NiXCߢg 饸@1~҅{zFk_e\o1Pܣ#1^r!W0Q-b8tm!LV$FPG&1E۬T;yҧǏe^0L5R2gm泦xgy@9)o@G5A)~ qׂS;T.iaI}(-ՔrU5Ӑ.u~*-;L <$ P=Ŏ"UȒO`02ƢxnS CZ%E!A)W (ՖFSEm[]zqx ;QG_kyyVlJˤ 'q#䩕<ƝjʐEvMz1xvO- 2S`g4 ;O:eΣz]g<+1NPUh;4QHq>@P'QCƉs>%T|n#6 5v,?4E.LmdAnw/?6,g =,U ŇXv#$E3@x ϡITŏTO'vJ&1(pPg2..6p9x R 0Z& MaU 9Q3uJBKFfI Uu'6NZZڣnއE$ 2Pv>J$jWR'"yL&>-X~\OK( zZ V tr4z 4'#uiCְNB9BZS:8n杅8dmޅ\fZH"bHgč< sFuNSs| V_!0< ,iAad UЩ !-Zfݍ,M' c-,N":_` q☗zhAɈ9%YwE7zxx_FmiҚĪ=Qd4srDЍ;adھʪ󛝀J>&/|}#`eYo꟦[ma5?WqX_p+M \| Obhݔ,E-헔Ry0IZ2"&A &}(yXup0w`OD>ט?~0 dM 6o29qKM>;U&7ŭ!na=G&]']CK_JrLA-|t'y>ArqwtRZ\ D-RW~\a݉m1,] `wB n ַ }ɧ#? 3ΰ&4[Q`Q?8'i; =hֱōdu~(8u~'e)̪SV@!IQqmmx܁2m7^Sȧ./ #7V_yVF5 3[q fѺZWՎ]?tJ3 3,|oi-L_ R2>? KEŰv UCwgЋ2J-'2[IKy (;Ygu&oF*Cָ9?|5Gmτg.{mv@5zL1pӽ[ƛ0q$/ı\1.b OZŸcN^n WpE;NOQr W-5VIQEvf $aҰ*c$,[ۓ 0ją YYq/b 3 J.*Xä{0.7`̈Ek)fp0W_×X^;7L;%pd&",+pWdp=^Z|jbR5o6(Mxi~d0:ZUvS.,ㄫCM׸,Z-bŖ΀{ $sF2|,f6:][F'LW~ 1-W2g@4kdXJ̔P|B# )5Ly#/[H/F](Bo!JaryW&fKeq['OlNwG~u9`@zWmMyXcL"@ݿ&9XĪ+f"ikya<fj m`ƦDFpK(@>2iki趜fBT9u"fS (4[wWArSe/{VRe/m-^/e/#.^ e//^RcqŌsNys;;8N^@HRIUq3F\ W %+Gș_0EUMwi` 7ˢБI5Tt[ިbVkSG4;.,uN]n_>,em]iU?h,Q:j&809;Ӊz<'43Єd3 \8;7U&t'ǦeYiU>ԩI-LY~vxL!Yx#v_oo-pYnQrU -6\20"iI>yNJ[Tm:f2ggԭYlfcEbiw& CNhݴ@U{nK"wW=:%Y+z3=YuKVS{6z-wEc]7Uyʜ<_6F:Ʃe^vEkI75ѸM?}OJ[:åGw1^|kinLihԝ|v$> !qyԷụx|^l L9:#Kbtόi/ Lq:*FT \px?xԩa,,\ޔ&at$ZW1tFaIh3b(wc*V퍌(Phטhe{t!ZbDT("qØUxqmLs8v"Qڲ˜n>}yώnb؞ Dڊ 1o0)с|4 MLh٣o9T٢Uթaf›+Px3z":3r)#u0Ŏä%Rp,PvxUySx1sW7})8:PL=JVrbMpQnPA$f% amF'MGCb ??uh4 F苷q%Y?Y\} %إC/ʣIPD2i2L ddn<cR D(/M40 QMhI2c#rMo¤[l2UewCիvګmF$:Y4D]C(skbP;(bfK0|}2%MbJ 3(DHD,i24:̑We1l]J6[C\ 9)me6)ϡ6K NVAH}Ifih&e#%lFm;4ͣuto# -g9_e!-nB{~?Z*S+tM3ÈNiÕ j^791@t?\)~O;[P$6_^6l-'jQ2`8;~ r?yo׵.?\y@,/;⻭aK .?\]\%w|?a=O@0cX=~ t u/ e(q+_Q^P'ǾG3fE' x^gT,qMbmF7kS>v$p4>| Wl:l:uj-ڵ܀g^i H@,XPe Ө LGW+'h 3CP#-`PQ{vQR4,!'R\$"'wGYtṢNhS`|8ox؏iO ɡtOFH{e֔s T i7KgE4z?ȃܗnlmݽ#aD/~_coH9-A$Yʻ(Uoau1zĹQm)v {?=Ɠhl՝10=qo/&w#t~FRZ$~*v7T@JzZ r,u~OzJT=h?էQ|IxxI]0r7XKQ G)MI}u 9=X8w}x* sdu1,1ìZ5Yqʛ7'PB޼i=xS67])k4HAj`E(Q3% fJ5SL7O<}:+u(L%Z ӓ3GyD!* 0++в$Lõो?j<ΣbĚk7lix_fsce@yA֧ufRyrfWm(:q,ʶmRzT*"K@.S&4y[{ӳۜT_%mxGFدZ*Ht/f>=X@˳o @JR;ԭL&]ao>Bۖ|z\٬Bnzk !~džgb)DuK'\^ )JytYo<8Qyv$D}Xs([9Rު`åGå.Whv<եK?xx#m\yG^}4v+ﮒV aIuA+/:ݑ2~6&]RnFLZHZ;Ahإ3*e8HOHe3gjp([հD ϚfmђhRGɢ$398Ϙo|K߱DJ:LΒ>^ržn RC!wmId"UdnV ]N5b{ͯk|͹`M\k ܕvWhc -j ˮDةT" ]jssue?,?*>)q .9,O]u ۚ+:=>_ =Z#~ۧrֵ ZR=[U ;fZ>0'1j~W+ˤMnlyƯ}syڷzaIݒf * !^TVvV*pfuڙN^/\tX =(Xil_L،LzIQOXQ*7[)&p#+8QA+OLkb "Vz}bxSGM5nx~[饢+kU)j@S5PFST^Z{GhXle-A8əBcsxA HJ-i-1|IݥyJtWmvq.J0N9w'҄>L|7,2: vPQ2993]%g{(| F1-_IJO̽l"0W-By-J9^deN韼/Fs{,/~UN޿KwX\^]\Y]&j|5X+Nn+0\;pʏ؍QOQDȸ/kzAQcv 1M2/ S~h&WDK"Se܏QO۽#؅(9Ji,;g%VxJ Ay~T|y U2ꚍ`Mʓ<ma_n lY&~Y?8X=Gdؘrlb xoGx41[GCb@bactЀŴ[UݵYQdbo0;qT>m솆?Jc]?28 ̀bN. bż8#źxκŰ4.^70Ye>FLCC 2Fz$+&ÒG^H~'i ,Z\\Eْ?Wt(̺'b9X]/Joyj,=qK3X O oDU @MoḀ1Y:Wd]yН'aĆ1`gUztͦM, |Q#J (Gԁf@2IJڱKXO \ZT^jJw-ߤ1Hj BN>DiǗ^q :HnVAXcm!+%<;a^h Ku$a*]s)*uQQ}\v+5y<1n3Z\FiuiF_m1B%vb,.a?ۍ0"~&s[ ׷_7Ei]<ՙu#nZ`CJ!Fx :/.'2ҝ,áZ"n@2O=p&7qg,ca1c**RdiȾg ~axD+UJa{jɟ#+S8d&ET|@d!XbC* 1rIt N'}/kq搜_9w4XDID$zh] %+s/~\IEs-,kj5[R3ܞՂMN$jhY 푅s@eLk }`5nurmM8q=Du32RGj>0pgF5Rs'{dϗy'5٘%[^ H9:?BOUcFs ~OԻA_W(4Wp!+HRl.F$\PYWJMT< ,ȞvluPKPuQB0vnMDĕ,;0A$dh HFY3b({B Q_77"Ǘ%3Q[_YPFLmѻm'!EQp3$O8PɭV[$l'4Be2>?ZeWgƕNߓZ,wAc=}FdGk" 4m_~3P[}vX:MD_MiAG2#q%$wQ>G.@~sê9CMY {6#`/ΐugB=ƨ DHЇ'ώQHP @ ]gL#m=4q;/H]p:GA7ee¦hYn+;87ΣN5 Vu.#L @|ˇ #4N7\v O.m\x9//iX<ЎnO>1vx"4CF%A'(,;LN2S@U.t%H @o9o꜎6^ ӲC_/K ^uuʷ\.b"1ꠅC`VwE~ \ߘ'}^A0v2(ݗ$} ew܉.ʱ2, v{y2CvUaw:Ҡ5MWVaLim.bz>fH)5XHh9bat8I&K.>i^mýG4|?yk)*"w7u-("yDݍכ[/# 7u%T3PS|hA}uĻЪh=ַP+ ?n2LAs5ͷ ? GG i~# 6v{ \Y$ `oAsQ~~xu)^my?om4{Dׇ:4rNm $A^nvvk/@tjoAv@.HN нu$ƑU<;8:+v/^6w7B@l6k0H[XfkWzA˯80aFPlC[,~%}|$|xp=?܄^|ƌ \Wћ4aap4 X/bϳ~U )2 Mws/bgYs;7F@MI}`Q`8PV@UBdNØ}S;_9-r's6hm)@Swx(#TfVEM揣2$K&*:W~]4HDTXro+]1Ȇ\BֺJ{5nK^O0W*AIDĔ0 唀ުL m5{x9꟰@CYE' eq+wE].# T ɺ!o0TxRǀN:<J UT9q!ٰbɝdӔ7m@O1b`Hޣ?,rwq@$tFR CDzlxT'XQ/tEC@q}"5%c¢e@|hh 3 Z xI17$A>Ĝ4eK > jn GY)u .Xyþ^D !`x9bb/è+^%*7# Ù3pAKC! +8p$׍}Ի"o.WJ!DI%؞| e_ҞJoN1'XN4[+[*$4!4oHx1-1dه,6$s ud(pb,Oy@L r4bhjo=6=x`/?~?Ƹt$*]s !Y|!٣({뿝jM;AQ CB"VQlg'qc_v_rbX=6V-SE=+q݂JA %Z'q #Oaɐ|:eSj!}dRlZTxwoV T=(Y#}rcOhPc2>!a$IE$AŲ^uJ!P&y-,c*Ad7NMIgdx\ E=<1QR{m @5R4&[T?jD(ɠK&ץzNj\ץ.\ $`c yekGgq=+ܲewg@( bG^?Mjik"oM9pvj:jض(eĝn UJWEW:eW9Ex ?=p8+ҁ(eX9Dj~3k2%o14W0j@R9.--5Ɂz?󧡛O-9IZK>S3jS4V캊K݂\¬%@Ú&B@hS}*㴆M .9[I%M/|1TSgzI5W\4~3gO`oʹ;焂^p_)E?;k|Ϸ[iSqJ'ołh(9qlj=M-͋|fo`~0ߦ֌e!% l4rmL?^zˏ>eY[#E1/W-#"C>V޺ˍ5?d ZVrzʙ$gCrV _*=o{ʁ +q{6X&(Kie<F?tN;?<X;ؗjxOpR<;W+CQH̭biiuie9xq*h)H!IWEBT) 4j`]PLጺg2J#Qd P@z(KyxsY* @!iRY^wJSUFؕ^jTnhT:+,K@ U fDjWݹѼ=[2RЭ=ۀRZr1L˴fשUZf` ҡ[B׬qtyʞQ$w%k;E?}A`pVwz^WIg7^ŮU{Z1*nl]N/N>jfDtN&C;`b[m~-7H 2VVDG%ڢ |^.v=o4ثW,?Ww3lfT,D1T9Ak ت2$۴ˈSH-7]~g덽NϮT̪mW0favM.7^m,fV}^5+Horxw*]Яw;{UZY; շ^Wkr==,]pF]nf‚=[ݳzՎױkΑF|l-50R:wV"[5&mNӨw*VaYna&0Hp^k,&[FZ+}|J[o˖aBYl{۱UzpUZ]S_\4vܨtwLݭco{+ffӬD0{ ɹ[ڝrר j8UVV^t2Vy-6+jƫom‘Ykaz+׀y,=FS특IC( Vg4"( {R =O&%D8FJ&-=r,b}iy]«9c"{?mnD_*wN)7Dև1'j&/-iٟyFZ`6:DlZ9e kMӡcOgK .O=Aہ!m oᛖюHٹ~oӏ>meKN F,摆v=.T.=}"j|2 b%Y#c`<"E0◀Δǁ^b4_tF';O&Յ"H‚%Ma_\JDq8<[%3aRĭ+q@Q 760{'RU܌{a^w[+Pnӑ"]kR8&-rUdt2G[H.on(6VD2a|11L耷 F;T [#g0JBσd@ZSr=UW3i NC\j=,t >x0sk172,NށiΓ|q">,AߘC=Q~nH/ X}HCE'fq8qCfd))EhxK1 | xPLl#'nءa,gyT4Ng$̜DƼ) QҰx v>8)=oe,jkhC?a6+XEX6CKӶFriOՃMc+xbs75%/0Q TQ3: M c l G]v􈶊F%Q$92:iTsf_&/G]H3xƒf!v:%F_-i;ԆU612xaL(=Z[u2fcIygՒ9Isͥ`g\٪+ !n` P1(QoctPVɫECa[f*QM) ^T[4 CmD (g|g1(큐+[H4@1ٶ E}K)\gEѨEmD3s=a-q76d,_ԛ6T2s%ڗ`yD%%-.04w^8)C)dpx-H5ֈn kYr0 '3mvM' lwzR3[\ޭ4* erg#w8- LtA#G ᶹk"ۼ4Hc"0әJ)Èa ݳ.=q*h<UzN"?> ZG3dGtAvX]vLU2 #)('ouXAqfP<T{woMFo;-u6y/*qt%G/<E{VU104l}HsgCtql|/Y'@c#Xdԛ 5D8z{Yӗ E|{_C-^t_A\?B~y4A|f\Ⲁ-ܣL a2XH˲ߔwr6*edy[HM5||G;NQA eᜅtCv0ܿ XS;.˝B}AE[|lɻKllQ̌bME(''(/þ}:N1z(ٹ0Uf$P=F|k\tkX9/lG&+u }[\1< /y| =rO'kR#),t L#"n|^ExWx޼^. 䇈P{/Mz[:Pʮ?jkcgP0?lr`!1,JZ~<<;>ғ3Dto`$ϫ=kelT4G§+ k-ljt""+ fLG6Xy4RKO^W~*d?{p UAgùOLt*cK !m6lBO\tv'@e2875#,ZI9.K)8NY+axpd:U4D/%0:|YYq `Y/g7<鎅@X 1 yt?c X!{O3+ %),Q6WĆI#XQ_م *(~rMMb8 w(N"qt/" 镻. /Rՠs<@T‚=IM-uZF#%[Iǟ tN lL7t#v  49g-^RI'l6?K^ETIZ8 I+va꾳GtvdxU7;Aԭt\''(+g4Kn(5ЋDj$[ꩇḎ;4\P4+GחBei~Zl z% tͪ]1Г q3$Dr7ٜ^, +y*n\kuqr4aO̲a4%e"gK?@-H FI9ZB j([`.gJxpZZ@v50U{LGKv;K1R/8ғ^TT=g:h(yw4/Cz,{:i^14"dҝќdP(/$>|%b^%OjR~bCٷd x9$4mΛ=E7xLЭD&s1V3ty1xA@<h&y?2|n"\s1ҩ ׬Cc&!(p|/`8dL9 C4󦄂 InB?҃F2Ϥ.9Gg a,UJƗ9J2}-MʺSǜ"g(Vݩ;Bσwx WB ZJknn۶1'9M)т|KE7h#1oǂܕ}Ўg4GJ;e{D0*XNBr׈3+[][!gy%WǶϥ]#9]gZeFܚ*Yj*5P]:?UB1\*^*FHh $YpNq$^!)F0b͐Y4aP^ATE'RUs\ !,x\b$GbWg٫WޔFgV`r*RCq%D2*Q?xt wvAQ2YjI~WHmMfi. E|#J525$#S+)TS#f-: >Xd :VKNkbDV?+f̾vsFvAZJo0k?4 6) i9 ϶AT*VFG;z-ۢQUw`WfX: RvΑn6Vf35-y@3cwk=7u;3o3pր&Ë#4tVl-W 2D+qhJ)[\AshkQan"In;4g*eE>Y(y$ BdvIrF?vB[3CTНl^hU(9)YCu[Ch%B:b/\gp\(9E'ʪCjV:J\?uo#K!% UYAH ;aG}HP%z'l`aZ AW1uT} BG=Zxo-6]a{gvwK+٠qnBFGU,'z+VC̱xk/jv(ܑGfKIWg;gLdih t7xBǍ2;4sRKFzqo~a/ĭ9[4."U: H:,7# SXG:DSc5Y5u4 XWG/Wȫv/"35Ie$Ke˚-붦lM2`lk]}L c3k:,>Lcm{]\~":,(ǎ%uE,sZvT ^8-~ɏԅcE xRx8k4ue5gCSK87hhc0=+0U0OU!HC}H 3d-bP^R_0΍o1dB5FǶLڵz ߳R*}L ׅ۶һKj.(̇QS=npEG#s ܝoh5mܝ9'8L ?^>_;h{Cw:n-Z,iJEOF7(hRI",kPԘ^9w>vƞR `b@peq?R6DuSJe#/77ty:qAFs{T  ENgT'r{Zs H:$J|@MX¡Ae4TiԞOiJM&ӶӋf%`!msSOZl=]% 7 N[(E.0HI$\Q)>`OpU\[uB.rf乿롥]h ֱ\seg| Vnl.QZF)'9,Clt&6m$JFð(Js6n+.y!b4H ;DkNlyֵ,]a˃8ԝM0PbQbxjY Z'K4Ae] b09Z];HC -1W >]&cJ?b&s&"}w&)tx(㱀Q.tvYJ%Xy2t'c=`u|pFկcXڊEtYޞeҬ;V}(FtLBײXY+bޖ1B+E  'Bcl28EK趭g+R[\ro}t\ٓX@dvO) tN8;@ 8ył8"?ph9.r\f(m>AgBEg05ǘb3a8G f[~0:!!8GNҬNJXwi5="/Ohx`oy8 Dt_Iv47ϒ-B'5~|;EG\L$y״g̈GP-AOP5uDҎ@m` |u(]3}PB!ěp9qc5jW 0ҠGI[zΐG֤p5LCPw5( ^& ;MpM>[bO18𰚍:$Lb _!sςu(ԍ(7:6BD(d yHʱ2ςC#Y]N(X?ò ۸3⁜ɧl5r9XOQR&p$,"5d\` IGw%W'֪5]KEX1m )Wڧ[E/$ؿQ_O(&Ϊ o41/`8`c++ ckUG 9SZpaW̭U<)R9tT0B|IXn_=ev8bnַ|Uo\3p'< yi<^\: +0~uA˘2irUt֡%݆w WU78`pZZzSTYSS/5nW? #s躙c[tFAFG9OW,9w(:̠ؤ7a|3ObLc'2)@ rvjiOaV#ƹ*&x+zH3MXQμ9d[t$;(;kb8;Xo➈//9e5+/\v5EAr@4>"C%3U|B{eqٲ*Wҏ_oã4|AXN+n"\X 06N`ݡ@Xlױ:^@o{My[Za;"C\$ST·ۑ ؒzJIwg'1ʤ5Z ѦaC?Z|RA,jSp/, w7" NY].9jstkObLlm/S6|I닎i2d<"?UlԚ9s۲cCA8(Y%w #+LhV'6z3yZkӊJn;hpe>1sڬ@X2."(#dUgP^7pp)*P% ՆoN!cܠM~D>`|:g_ z'hT4Nv({e͊xL1g8$hqX^v۬T}AhVފ'u40[!(%]2ڕ=փ(JZ)JElS+>N.~F.@ Iώc gtPhabۣ~*P&Zi&V^E8Ue@}aTԱJHPy]%aS؄dn-R\a0ro1p:`uK=- v!~y}+/K|"&W[1o݌`Yg1nEojT4܌C0;y?jpӌn`k _tP 5Y"zQMʬj٘kwlvN)H[^64yƱʼnzHPݽ(. Ź~7,~+Hqy0?qEh/,]qzAzCiuMZlg72uYK"9:GikݙX#(Kqytߣ8D*`]“ hx={ cp%^෻;Gte%(;ձ+)xXKcɸ`@jP]IW햂0 Ҙ/ߛل7/J㳹a>B e"oZx/"0xao޹^("@]Ï9Pлwad 9e2Ru@^Znm iO&N 7%XSxW6ckP}+OC H,tZad2|ӳ?K:ʤp<'nMnԛz,/Q`3:GCm"dD_"5eM2ſqk 7_uV2_GS9UI&V8eݥS"h4QW3Ϧ;¸[$#>5H"Rw0zla kPmyvqc<sXݣ@7:Hp=i)jͦ.NW3qG#cS3S:#ie.3չ;ebySţ١}P~~LϹ݁UoPp"I7X:}rWs.Ib/=a~l$ wL6oRKvԩ@LxHg;D [^Ľ%vԅR? - 'MjcRT/ 'X+mt&ꅑ<뱳M2K۾4LqG{cŬPuV["d&eR:9oR("sg|9GQ E>n.`J8G!O[A R]'Ԣt 1uVSbdXj 3ɒ"xe5C1r@U@^K+eZ7$jR۞;|'khf70D):|'16Ki!h_ެ+){w=!{F9/O{˅'KXqz3T5CpIȕ\ {0B%xױ&:5rk<9BB "cQEfۨΆanw(Vuw$PIw׭>GT!@wg#UL{5fkzPYϸoJIZ *f8nLI4#0IG-GZObRp޺?4auFʩ+L'v5Vd$Y rHI dGAN=!0ԕSKÊ7}`W>k-R9F"N^V-kn8CϔlIw)Db8j˜a~TaNڄKqItRS=;*!,wI'[TQq+1jeUöLD C}JO(#b3(5_WK7;**]G *Lw25w:d. aTLЯl\yo%t(aVG3Λѻq)(ⸯrS?"*,y+un+i@ZhmSWF?Ipt~E5̜j4t<%m9%2O^r|csyo5mz,.(2P; gݻ:Ce #M/K$UlN\U8)tC8<5%omUlǝ8ߗ"8jCkL6o"yf[~ TK^_?Qy /"*>mX{E*xPjkފh``qdJm$kScb3h?uHYG=G*o K\ِ 1vW>@~]u7Meu-y\ԟd STZC.r!Eo0,X*:粠 !,x~(w&"dg r&ݜKR}h"zc$2ӗW cr.6k㮫, t!T#>AUtHzFIr(cĖP8a6u'j+ Yȷ֔+`z["Kx 5_gě"]Z]*ޢ:'0YN 3xD>6*2ÚrT;4[3{U9ȘȟŽjP$VdD0+d!#4bm?p6:mwnGs8kԻ_dq?)2k~CR5~A,dݑ3#}{=wܨedcS]^:om]/ɷ)0yv'G,x2AsG,+&  ka׏d|@%;7@;jdc/X:#j /.~MtAǒ1Hug8]LVj3qtDc0 *f YC'|z /W%%*њo'Vbj kNNsFFh4`!y?3sm33y$B;VMU YW2w9 X=%|RW$~Y0)zWeyp44%ڥ5C+F ̯xq,989>l^r/>e>r=4R{d.*UzzTHG^.vD{1t2}Todțk|1Q.Rv&\񪻢^/h Sˀp%D[4kCi{LzcqLPmC>tRyu~64j5hܨ7FR5leV2脟=q́[O_\VfH Äuz?$Eޔ21$'OxYRnlS ,3HMW䶇&p.^OHjV)1t-],7S Xւ ?R32(Cb*&Q]xFAR0sX*!PF fBxH<=t2& )^%l#]К¶^04]l]>g'g48o.[Ehꀚ*`\.Cmأ74FǧW7Wg0><;h őF~_.poW8WLJW xuvq~89uz؂es| Ǽ7qT`p1"/a/9|Wǽ{r.wƎYejceQ6LØ}%3 X}2,/WA۾>NfvFV@# j5#iؚL+x@%RR=uӁ;x~YƸ@y HCpNz^& ~ ő|/ogOxJbC+xE] * }ch>!z;??I_KLP!p/Jpw 0.Ycy)+ zgcYQ%c .eiqv0Ʒb>ʆŻMd@?|1r73-r)"} 0&r6?JYz}~{Hv"F6c[pI,]G5JaG\ ҼL:5s8(#/ kDvՃ1w0eoY;==2 |(\i#i/aP"xV&!,mXn4Y6Ӿ#i+Aߧ,$@l&6׏o\0aMEucӅ m1G;& l6 a]JE]B'7Pkی8ҦGQ$ WCn{"':?~BOyY%-gP*JZ҉20ؒ5Lh6഍2m6r~2mʸO|񓅒z)H}-9fRKlv؅ln}و Z:lzU(?誼8;9yypJVKR _p4tu:w"Vj7(=xsu;:ӳi6 bZ~~\MWtMjj,t i?@ ,°~Gy5}I_u#{Qq b(| :{l$2EWPQ(fK՜TGeѩh!&B;Dd )J>гD!{!} z!~UYyd-8R"+Y[Ca \D% ItsbGWoN?'~AgaS>8f$?,8*sԕ孎/|K&lG9PDFLQ],NRZd<<JC;{k sa@P->L(|= 8 9$F%ky] (,+U{G5ZAXY+QbPpFT` k߁N rg3TqB^[go[ȎO2Dđg`4uHboqCS3VOA KlYnhY[2  .!uA\04l[z3XrTB 9,h!`OAG^ML!ѯ᳦ 62H;lg̊RNy҇s\("ngGURJe2`ć#QgK802\C. ] jEs.o{w2 ^l]\A'-ޜ?B!1g@9>e'/W짃7-= /ZXn.q NPǃ (ykbt9C낝yyurvp}O%{9jDKŶd2x]Y[i2Fۨ @͋?_?khe]=&Ř.?)KUPKXLѠFh;Q^|*U ׼1b2r6k\co۬ȯW50ӏ5Aۨ)P<AWTLl$y-yLjG;]]aEC(J()?b q]Ժh8\\9}7r~pyBsLQ@#@AEjHpR {!ܶ>?^្ @Bu#@-TEgSq0ڨ|C fVgqfQa7DBhGr8؃b|@%.FA wzPLf@X28t^gV-ɻYf/7ovk-F#`;x޿*"s8*eK0Si *wy'yAWicQi_moZ-6Ol,7K/f>UV6J;;dMZjz qQ>Z v>Owȥ1y3*2qKMF3 K? /,$g$>p~ֲ|HQpƃLy2 ֝Ҡx G[R*~r-O=iLG1 o}}m$Eϰ.g(_̣4DUWA`=f`͞)hUHze0 d],.@_TZʦ"Qǻ"ȇ[3||l6џ B*7`(r]~{mo;mkr/ی\IH2?2#nk '1>_b C5%L"k gR;,XZ\7].,[ HYyH Џq;\Q{4Yk<2Kws;候9>tvOEoE~d[MJU"Ђ)gpL!^,lHXdtE\<^,׬4^aFk[)VidZZսM!i5I7W293, LNѨ 2O(8-C``:4y=^vNvS֩ UDg]b*%ZfWhhDC _~u|j+tud+LwHK$KxLR_m__JͦzsƆRYe<fR(gW!<^i9T#3a= ,Gss#+"z)mF<}GY}룼AdEL(ۅU;JO_%?FOdZ]..ƂOjA_5YO&ӓ斨K챒LPQQ߹-=XnJ̕LKD&+{`HO~tq>E7QrX<7|@kXjvXA/G(x"NoSوAӯLloD٨&Za96[Q;6PoB#aB%p3kóM,]}.Fѝ xI,"d4нNP`r$?3ӳ J0/u8Au?LEfDngso CFq9`R{ WN; lDNW`t2`1Fyw(?*3 lSeW'l%::`X:x@(%V~ E_5r;Ө1أ@g~03`w88q0u z^&#.VO#"r/Ohg֚L%n!h7}B!bXHi| ^(ylM|88%G& egb|cbC1Ş/,7'y4k02?3g)U_\4r |Q!Ky8T+9yۜ)NFj;NpI^2cp>6jƶ؍јm5<;̜}qfO?܋ECQp S$txHV;#lu UjC=]S#>,+/QBMbTZhC{wTj?w?/G=xHh:g 5AP/AieQzpwo9'+5dO.ݶlkLu.>bߒy%RL֎rmt[jD_~-G!1R9<|k>2G_dV 3m%1zf,w'*bWf.h-G8ȟn⟧T9bG>!8ȒNvGXC B|;iZ6̿YG"xhn?)ЃCA(6onLS]ZLܯK MJ߷9"觛>BDMgDWP;da8 q<^]~fhHJÎibt%'PCP8o{v}H,$/C5A ;H+ Bd{#dI){!XY&L(]׆s%pÐ2Bs)S1h^<zs§k4Z[>р~~~A<*ݭa 񷱏Iigzx &{n/qyQ̏^A]){׫+aNRg_ fC^ IXRGE :,sml{NJӖ3.'tMm/m ΰDd߿*tzDC"4Ah"Yve,r%,ZΈйZ(oDJϐ)|Q{V^ev>bG~}zg3|OE]s( 8xT@` /Ć3ЧxcC~ A%[p3]n/ mpP%$%DD߳c'=D%Otϱ̎qMV-RZJDB"Rr)(/ qzo}$+?2C8~ )o3QNCnP CCp;hr~%jN߷ ѡO]cѿ60kz$ǫߑ}_, X$vP+j(Fy^yȌ|qJX6׎G{v-j0tVvaJ-Y> ՎX0W,b:s_u'@ k/ [=C p=j/\}N+µQ1KUj'H:\NzmKNClm E3:ebpA8#gOo쒜;9dZوP ũ/Ti>\'=iOl>n>d vZ&|E2_,S< >J d51EUmk]pg n'M=WK^e> ]ʬطP~<,Az:głVCbJ⏩Oлn-sQ'i5Qi* _q^~q콘  5| taDh5SVw!5;X2gP1ۢVIt;_&sS *8g}a@|Tm_cVQͯ26cl@FF&XuͩP^3.wU|dEK&Uc@qUYzw< U5=޵qRYwxe$7i\h;{I%gѠD7L'{rt=>gw+=itxis>u~I=Pb(6%݉&߁]vW#|zZaį*xV3\r4h:GQbBqU'oS"]D$(Qc:H7(|=܁˯TN@m ^%yf)};e4A?|dƲ(W@b3BֹFWu iϤ\LyΠύiON2vPMZ\$ KEtvr~+3[$۳^Řq.S"Wץ"nw\^Ib cۏZo$veʘQ#J*>;[34V!+B R_,U&&]V7t\*>j-A|meX5&N%M O[IBȵU[@ L!qmQpg^z;n*Xs`4zj(u`%rc`Pܔw0e=1_IPGXΤRevz "1#ޭr%Kx> 6X#`ja*_PJM& &Fl7m$Ԋ,jxֳѼ~$(pHXtǗp)uv(.6F[4BHzc ̽iY)V[o R9z:W .w}&VtI$Ʉ/|.Gܚi]3r3&ahDe`{ɤNDyT}Mys:fn+/eTa'VrBI6}V=~d\\nnZcqPG9l q?y_]xAC1k-QP 1W?!;2~^_ۻ'OoKwٵ[XԘ 32K vN{vSv|ndîJ bX)H?.xW]3qY>Pp<)JyX'iKSR }Rns[C^]&"}o'[ ~ٓ/K_Ua)n% s ]_pG(p*ժbR-éQH5v^j3SQԄ&+PC'&YE2mrs0QgK)ӧb++e/Ӭ9t6O#Y1EZ%WX5`EȟV|*Y4Yti9w&W7 qپ[X砲;}+dNY|O~HNSjǿ+o1p5);Ch>9Bcp|]䫲x5K5םDk}YdY?ugcX--T݈mH:δgK[U T~-7Ug|ΩWh >Txi(Yg6L,"2YjC }tMe> c-i猆f\> ס[{I'|%>&Xw)G ی߉ܭo\ntƢillUmuhJ\Gvȗo:,߽p@\y\mAAN}@0qaf8bNF96s֨<+%Oֲm:Mdz$"($:n!MB"8Yg v)hV [)Lqf $1Z3.nQʭC?rED =?`0땠fRj͙6EGMOɻ\i{͟-y}7H/ ;)7۩YGy(êS%oK wļHM)RS:oƽt'*}e t xsf[`Ȫ=o/"0H&P7.8jb_s< \|; $Gq'2*\˭]XVvi Rnv4q?BѸW V-:H@` j\9[!^+I{CƁqTkDmmΔauD{OBR \l 0[7Nq_K2'\*:+@{[wK4nX$D+XP8=*L6o> ^DMEO}r+lVIOZ;whJN@O||-ᒚEΖu0| 4ɎYphRrN`ow{Ք fOT9@:TUoFL=OiӍٷp>~O˺N.Ci2TXT >.ω+LPjst8S^RzV i\d/[ Egfh!|!Q Oa_0%-|UlI-Mw:<) CM&&bJeS$"/JWa00euh'[6L"=q5%S'"hܷ>i}1j1Mau;ۍ2dOEOEaC*pWFsvA&۾%/Gi)'ʝUZ8 }&G'f8eL.zIJ_qDE?5 k ˠiH& !`N9-8BhN4'4 6;d"!b!Obj)1$:Ü{_K?)AK6n7U{*A zyNrIµ^)CܤgH~b )\b:0xqtMZN71_))zqe(/4l^UI~)(Б/]N"GU1G>Q'9|뽣{ /8Qq0cI!%e澗7!ϳ7AY9ܸ\t]6nGI6EָO>YB۬f ax'G-dyWr~:#-)QCv 6#ED8|=( 2PXJUfp2J~lTb-8c@pNI&k4,[#G69ѻ{Kla lBq)/P? _N!"?ny<y hᓅ*)Ou[[ poF;2-}E0keִ7 OǍ*HMQkpA\%s҈v, ŪMX \2b ~9Z" LFv-jE`6UDX 5$DdV.)[ǫz^E):w*&eB7ADF"i0bjGYFQ *mW02 y;N2Z Ygז?OVش%"a_k1o,'xQ3䝼~YD} \i'#-.hƆAؤ?D:?7NBYl)`v%uwt wWn2%9qiG ywU+ϻXP/c$G\/FVe ҕ~ V/VO_kyP\uZ6N.σK$@C'Pe^$s (bfO׊}0p98xdtlknv/iiPe"? - bD,Qg1^C辗IĘE.Pz>e%HoqL+ r&>sA)3 ʖ(Dڒyݏ*IS")kHk+Al.8{`lx~8]s~ ɵeYڭ[-;@VI"c(.3rJNz֨&,ZU—N^20Ѭ,-,B-qHo{jv^QҙtATSߋ7x9_#ȿR?.˟FR>]'A?8k~Ht)^>.a^=USnBUzŘ2( Xܣ==!Vgf7wϙR״D 3k~p݂ ?42^~/HIwq? ao0~l#>z||On&RW݊g`r_5n "yH7XsLIy2m,tIwr "'mA"0(0WAk NUȘ%1~CT2rԹG,ȟ7[@cKtn 5Wf8/HvkĂ;&iP<]| :LzU.'ߞy@d ͖gArE ZWT~^w;@E0 ׼ eo*>ߺ+حXx+?m0"d'OsY>j?.V}{Zk դ!\N2c hTz|Ȕ$D>q#Fe" th/wѲ`_p'Ƞ&cRL}vV[9uz=q/PGn?Z.f* }r ƶ /Cÿr^p}3usu3 jV 4;􉎔d͘u) 1f}UQy_ r:xk53l%Ҝ}g`rܒٱpLjBTjnTF8>)ogJď߽f߱1 Yӱ _xe.,a}ȍǫr^f 't\5v::/GΉ%&m/ZhG)dE_$H'Px|TCSB+,]ڶ9҄ma2QE.Zj ǢP+<9Tq2g8{#氏˪zWU~Rb~kiEkQ, 1 .$=qrts-(eikx:\ 3~rNx{> Y$%h(a7`WqB3&rG)d8h^TBI?$:)FӑsbӤ|)_1!87&,4f𣙚O7bAMI[tR"XD RWM\Mg|&5u eӥؠ[ ua=J7[#GO}/".8̅@?Cybb#N'Ysj·f>bxZʬʉQo =R IpIw*r 1W_Fd>?QB07 6O`Ii'<^C@DPacvYJӍ5niԂQ#8J[ .S ]p+RЃ[bl\}*d-cLUctn3SsH!4v=j7{ ˁX? 7fv Axz*Ү \q~pQuj,l"6f eVSd:P쐱nC}"[ja]H xktRthH% 7d#@i7w9U-E%&tԻ+h/O n,ͥlj)öC{f5>ְj@qK3C!?nגJgv2F l;!,njm}"r7gYj˧®O0`$|ɤQ+r7zsyƠ;!R"ˡt+hPk.l^N2l*尖|*t:is?o)tb(* jӍcu0ɟ~3?9ţܼm`yZ^(8+Y 䬘@P+aqh sH 7Hd~"G^@@'Li8G `ZaT_px ^1 ccq@ɶHM?8JGmboaTCH6vAj"qt$s^)!!͸P>4c_O־Jcp!z_~xElN*'fvkSŅ~&W9*y/q$HT۷<ߌ|e8W(`!RoNb7-RCL}6cKcDTic];RO 4By)|##"dVCHL-H'o?2`;o1qH!?^~>Kw1׬kV,eMdj1Dc" =k6>^#Pq1ںKLӞ/[|0Y-We'y}|~ﯥ;olڼhMSN,4&QfClC?X?qNQ«MscYCJ=mQFLxZD ~m~o=6Ý Z-Jz/kcnrp M_ӥ 3b3lz ypg>cOpZHhrS"cABDM9<5~ȅN*fOrs75/7G+7.Ӌ?v:mA2X Ƈ{"XoSK- QO *+S8oDʖ<”?"e-"\&pcNPfNۖ+x) #Tq;?/.wZ4 H Z{0"g#N撯#J?8qz$761r{0Uuoa<U VS&nQm`di/ZV[AL qgRHOduJwق~a&xN$o-o^ZG ŁҨ |kͨtP'f+bv-.e;clL^DC~תU^6"6A؜4;?Rs)}JnSD\IpφNgbz~`׼patQf_/پ:GO; @.O}^qϣN6P }n#$f2EbqM 'W%q[{ f2E~& T}MGv&!p"a*hq!ƙl) [9TW.9FlmNKȯsBm3r8(uqgQZx&IO FpTb-;b s`{$+Ǥ/$❧9xYLaLZB 1 '%zѵ2ȜtLF#b'he^s~9.dLX tF"2[;+ A |eN#x|Yv)(AmL)^eYDc,0y1`_0Z@zg>Z2 O7z×>m> 8 !~sMqFr:_LoR:f=!h&lbYa)ӄ<$t^ ѪBzكCMďL D0%@J ؑ&i)q[Tj:h-^.DH"*JY#zȄ*~y7zhGHg m! B?Y԰8'1<^:2;Jܷ`Z):_&}D[V*N1%m\N2``%B2/g**j*g?H@ /ibccuja;裻yܜS+.ncr7ɫR*(*hYu/z⑴$5IcΔ +Uu`̸9n}j]]\UF5ڦLX)L[@1&ph"LJxbx:H5N?li~xobTзy]j]>c4dFpҘR|R1 uQFVR/Q3q,M4%Riy\y~7q'NJRuM>9!,oJ!\$/-Z6OxQTsĖ^fnaUH}5l>2ۿ/!PX.4*N#!abBU1&8п{))zo#jDBA{=0T@CQ޸/Oކ#S XV: ~gV/DB$XV[rv]igUi6h8{a_2W-u Gdo^|; g"T Oh|,~Lp+_"=}B?\;j1-)Lxɳ)`xC=;k^'Ym]{3?Oݯ#k I78ae,pa FjӊĊ)ȭG&[6|,!%5C!ؖ J _!ux&27,r7rX f- t4om>} /䌔pGs);4"ɟÜ[}N[mc ِ Dp.c^/[c6ko+T){H_bPZNaL$ ڟZG!/ރ[dn뷯pOc(=+·5^(mEPW>,Q&Pù'Dx(iB?PDT*sgW(pKw)R*&&j7z牅G29"O9>;0zQ;aqAse[ oS {ʜMCv}?Oj@KoV\0OmBxٺԁs^'{aޚa}^oš*^1{7ͣr97aZ@}a^gɽJ,_C 9v66Q:}Z~*y{RUeàKOK mΊF7k5ƵWV=|I}wuÎ(O^777UigZٽOO;tyt 'gO?vr8}볿3?3V4l/7,簺f /7{%wqhb6z?ùIe97R{s+PtYWN1cUʵACaF:7tʹy>ek:4tFrQE_℈ЩǼ!\6r^}I4B.ΰ ap;u:f\aw 7lx'9#3Tvg9eku~s'9+JDyͪߗtcA'ю>D]0jikzq..*7\?r(#7'nܴ@Շ%}gmlX;jeo^84C7|e#猒ʺ1WյψXp~O:LݽKF=}剂5"?zM' cοpc0Πߣ}b_x}PmDeOArGvX/~\bQ stz,|, *e^7=ٔ6n-?pʣ&)WQ2l)?c¼sesr _nccϜic岲194$7=:Z>/Fc3J!v۹S玑QyUc>y mg*Wrl w+Gʦ:x}s'^ӷ1qp)G&7ݎ;Qh}~?v{7"r^+#~_uGoCwݬ:UyTmsΟ཯lٸ8y2aCR9(^:tA_gw`xSͽ#ٵloGʊS4>X}ߴa\T e_O4T̥6>Nv~qq%G֧; O55i:ovXQ9t?-DU|8ē!T 0CؙB"¬ѳAa GFnj(E1PGp'Tb4VAX!AS: J9s1 yBi0*B(NM(\%ҠJZq(@BH."zA{BK9aP(R mR 8і`sޟQ@(" GLN܁u#o)5ҩwL79'0eyM$Se8quZI$NJи Ôaƕ]%)И@)w cLp3&!RR( W.\IN$1Dwt&Nn)k/EaXgyh&q،s`E' Iڊ*J+v}LV@U4קbLRT#KYJcvFPRc!QEP‚H %K@ F;K<O C>FwZa]kHO5Hb@$q$pj$\܁+9*\G I1\w W %NAtj,P1PjbS- x=F=B z!_ \p[Q͓~d*Oq-QT)s.0l֝ KÙ-Z6>AQkЀ6`tax@eAX)e]k(B 4l)e\ c‹!i`y##ԞaORL B`J a < j`HDJޣPa+К9F0 D "za~+9`vȷ 0 ?޾-bV7Qt}q" z{6vzw<ۥi ]"kGkYV]K@]ؐC >q[qRc@1%(sA  Z/`XMAmɶgc!Xmmp(D\l`xTh#Wkuq./oar-2.5.2/third_party/rpm/SRPMS/perl-Sort-Naturally-1.02-9.el6.src.rpm0000644000175000017500000003477311757171206023211 0ustar plbplbperl-Sort-Naturally-1.02-9.el6T>D ,0@ef1fdd50d8d3757feda648e6d9fae1d54f570b2a8㡫?xeΪ ^3>,Q?Ad!TLPX\m    2 4<DLT`(8 9 : AFGHI\]^4d5e:f?Cperl-Sort-Naturally1.029.el6Sort lexically, but sort numeral parts numericallyThis module exports two functions, 'nsort' and 'ncmp'; they are used in implementing my idea of a "natural sorting" algorithm. Under natural sorting, numeric substrings are compared numerically, and other word-characters are compared lexically.Of origan1GPL+ or ArtisticDevelopment/LibrariesSort-Naturally-1.02.tar.gzhttp://search.cpan.org/dist/Sort-Naturallylinuxnoarch(u uOOOOa173f3f8f519ebae6e5e578e843f6e1c19f30fae2f9f1582eaef07d9a5d241a0 plbplbplbplb perl(ExtUtils::MakeMaker)perl(Test)rpmlib(CompressedFileNames)3.0.4-14.8.0M6@MMRM@K@K@JlE@I@H@Petr Sabata - 1.02-9Marcela Mašláňová - 1.02-8Fedora Release Engineering - 1.02-7Marcela Maslanova - 1.02-6Marcela Maslanova - 1.02-5Stepan Kasal - 1.02-4Fedora Release Engineering - 1.02-3Fedora Release Engineering - 1.02-2Chris Weyl 1.02-1- Perl mass rebuild in dist-f16-perl (d'oh) - Remove now obsolete Buildroot and defattr- Perl 5.14 mass rebuild- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild- 661697 rebuild for fixing problems with vendorach/lib- Mass rebuild with perl-5.12.0- rebuild against perl 5.10.1- Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild- Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild- initial RPM packaging - generated with cpan2dist (CPANPLUS::Dist::RPM version 0.0.1)noarchorigan 1327326732V$V$Sort-Naturally-1.02.tar.gzperl-Sort-Naturally.speccpiogzip9?@u\Tߺ?ĀĦ;`dDPJ$EB@T.IQ:%ߠϹ{u^]aX,%%ąE]D1CR,*aI ?Ӯ/jNߙ h>}0 `$[ f0>ǧ/Uºam@h52V/(:k^mgADZ݋ fhՒX gn1k红+YVnmnH%,$6mLg%040mۚ 9;vǀQC>Qu8C5^wA3]ٯ;)Q_/5#dZdWCd~ڟ(_';_fMފL~$`Cт~:aF_60a$/.CJdF#zN"w[r ˸%MwLn?jD>ktb֙{̅װ$(VY*S˃پf\t!9W,eqg"2AGG qRX 3Ǘ]')`!g|NV K=nz|ŃG?ŏ.0Z[Z3Yլ&ݙ5:1U, l+/h/_5lrUȵ' Q~u֙z S)c!@}W9딣vs5w7Od!˛)FQ5-S$e09,~J6xVMMU^^!-/:Qm={k5"ǨP[Ѽ.T^PAh *"a2Itāڲ|մ#v\_HBZ-/xu e^QSMvˣ66={چF7gV]ݔV|?7&NJNAZj_]eMCգ\OBKqB¥̏߹1vA;QL[}Nj^-uת  |b_]0:"N]wVD spib9gؽ[n_0;ˍb ɺF;%xb~fv^h=40N΋[d$j}EHbY^k5սfɂ,, gDe0V׭4[p%=MȪΤ>3+..Teݕ~e%O5V I]ɶ| v3ӎ*`i{{b12&X@|u8h/dfY0+Nfb4PP 4uVNӖ#l>>r$ZFR.3.W68$yUtk.eW0C82O|D#wo+Swyh:cۣRd;Rk*"Y;zy ZzRs^l[]![Ωs}B[t 3A [㊏>R->bRb%' ^ PD,f"+c7o 8b{v?w9E%XW w4 pZ~⣝u{t4c1: idL>TW]17CT5ҵU*x@N$E rGkh)5Jaa(½ERA#:ãʸx/O+g-/Qꏎ|=_.aO:Y:>i3;,UQ);C8;qfƦWtv?'!stw+k5h%w.: W*cL1R]픽v*MS' EVG; Iuo|L-Y&}vJ;BOp2/6Mu]?iE5 :cShPK=7Lķ@ T=o'k4W{=̍X.$48ƴy.cylӃ@)]vt|){.|٦ߑuYW^ ӹ–,o{w ߔCes[Jr8׀̥_>M&9FR}XI!qq\Q2'xNnBT6]tzݔNh?ͣDA Al'?3q΃&o QyKy,R"u3Sgm?6 @I4-B9;Î9 NڡRwye4W~&Ӹ;G`T0]w%[e֬/ K0 FMt3:]S:>@rܼZdl ydɤ]ud=ɴfZCJwDeD3ij`HQP+(m6~-TNhڻqַtlEw101>ũɕ,!B|ƨ&D*&4EHc#TE~E:(N˸E\l>Z49 bΚ'ť&t2x,ݡdz؂r"'2ݣ˻Cw5Eɂ]iy~h=^6tvcg[aG5<3RNl6fVЖX_3GCjZI#?fy6$Ɣ?{Po;W 0?1U+B\8!)z2y{ uo8gj3׆ } v=n6ڷ8yc_ĥN7>PaX܍4\A=H=AY榲ZWJxG;k Dnu{Ded=fϿiV|ix_{<"f]:/1К7!H6u竁}܉z҉Q.Sv/n<7.%}U4&Yfe@9lNZV=BSBKK6~ 5=[,lL8d˙{<щA%xatmBD^Ӧ#FSm \3md}nIRRSj~m| 4vM.RA`;}ʌI Mv:a3Wa1JMXI yT$^ eC^0to𸀃T0W;]"ǻ@)O7<~?T؁g0J̵ݔSi!W}8#isӣCa7MY'^硍/ *Vai{w('wc_9凄5,6Z 0%nu0]+gawd^&A\d\e~I.yGInk&h1#Ŋc)?jM+!o=̫pry4ΈUNln}zTA%r捱㵨l%dsB0Em'upDؕFH!p|EPKhe覴Hwa:V)sii[~v6D(/PxYDs Md3_^/Iwo~>@z/g&wT~;`G뮽5"C]W*]@m\QN:,ě3jJ+բ"e۱X זJm}ʺ MT<2ɵ.ϩEņ1W ,Es1<n=#`ĸaKj\u)}-/~'72wӼlJw3J+2JuW,`Atܿ0=RYө-YN .ڇ`c:zI=SS.͖;[̈J;[ѷ=]'Q2tU%N~RDo;)$i}9n k}!ȞllFp@H ;Elgd.ٌzs10ҾpꈘqM|v@e{gHܾ[IjQQ=,P3ϭ"63 |K8p1c㰛nsĹ!t&)!?a9#yu- 2gMŒ>2$TbϬ\Ze(d,bMui ^ܒ95;K:j-Pb+-UViSK ],ĺsܛPua}eǥv7 *bIp2A^ZalGŋ-o]4V9KHȴTU,:R~ u; | #^ 13REJrG]8ɮO>g-!P;%tKh m+|~-)uqFcOBѐ.#"n%I.Nu~j :<5rه\$I oI5j MHޤPcBϰ?A<>kL/g\.Cvܑ-mC%X3e+5'97i!no[NUTed~=+TyVjp %=itff`ӒK >$}NwhOH)eTcyfQ }[{6K '5 DE6ksZ,˛fv$lHuKu,H%1ZqږÞ!!'OaN]\ptYM7"5|W<47#*~/eJh$Kَ)u#=gB|9قȞ~WU/{兌w74snKm4!^7 'C\T*{ڿ GS[~ZDv̮}Q_hN)xY^{Xxť 9ڤ01԰x01q-6_P=,9Z,c .tߌ|c(UMgOt~Nkfnw>JS5C-[]{w-]vYiϮ_¶.<uhdDO^; Zwgj|06K't/"C%4Bcts$5vomjI;C+'^ڔ\zN|8cY#'žpqV]r*̕zi_a{ odumÅK!L ;Fo,ڽap 4%173'(xC#@hֳƴ5mԎJdP6YEpA^,NVQ1tH\{MR&i~j>|܈̌y'y y@ẆYFa}Go:;8 *}L7ӷ"z;f6#Wklɑk+j"YQxusU￘Ձlkyú\!9PYNmB=\cTK!.2#JtD}ᯩuǜ4eppIh|"[3dZhR6ԞXc݌FRByL#P|lE!7]7{r~#5<{%i|_{R\'_|XG9^\^^\΢-x//MR;}y-h%E\ }|DXv?HcF-ǣO/4m< PSk*FT^X>C2__}s}i q|4kþ/ΈM+ )^wkbzOJNզj'" /|hvmw fU8fGk9 %_^:뿢SCTb;>aþjuG<.e @MܻTœ6_JP-bBo`{[ߢieE9t*lUnzq#/A*<ԟh!FA\MԮI3K('HnV+Sv:>^̳$zU<+2 2.``6fCɣߡkxd9>;ڈ; 'C)BaKN䁀`۠۽UF kd]lH4jfvtRgS?XdvSἍQOfa%!R+?pO-TTITǝNo3 {j&4@jz=qf'4'Omv 0ݓ'S [D6] srMNv9R[օz;Nb@S-}%R~RoE{{`4L avsՀ!rO(-LQ^qUvTk][K_x;eV?[ܴ.gkkY_zFL^D#B͇kY9GnMjE dq Ezg'NLA6Zk{]O~TiT=$!:4^Z6ƌ/1vPj/YhV%)wG6'Z)H|VhctxIo-lyyˠG̍="$LK4u ~:-_FͲ_f((cz_5nh}}$SHqzs'}.Lˌ;7AOa.t. bMM1z SMYUnM="P3-9b]ԴҳBA\ P2v/p~T~oɾq #o<gfvN-}KQ][kjWT- =5oWN ńgڵ'+Z@aT]=-T5d.2a8N0o[w78dEᔁͼQ̯oFVo !NS(:Jj6Ӽ6 㛓jb7Cnֽho]zih-ʊ#!=n e{+9]o]tv6Ǣ٭/հll5S5S4_ݯƁ<kzXvEZHHX92'[LF]H]Hz#΍ӪA 9dƈyӋ3b"U,!KXl:>:K8f\#E_>5/ٺ!7h), a>\$l}OB$#uƂCBO5//V ^ON7}m~H3M^Ҋah)0ZMw+$m|ժ>yۭ4f9Zb*FP8'Gpyc٤G>.!,X|Ya<(\ڙ:cUh`  n\XI'wr_U* ԙYݞ{u\Ftx[< Ӣ\oLpSSZWGsk Q Yϟ-m 78pBѡ\}92Rn>Z|kSk-ƾmvf;-S5i.lE D#L>H翼tGP0ٝ G T >hw%u00Q|̌uP ?! )2Cf*J (a0pĕ<&>P$%W:8 TtT L8!'ق>s92gw菑 6CCa(ii=[IS asP/4dE{!C>p+/ `=8d;$#! @  rj( .0p3O~gH(ݓ0@_$x8qB)ocOLE` SEBH>gw0_uQgx!!^ 6y|p- U0A Ba~C]@KTIWWUDwA Af19<<]a`7 Bc p@ b>BB!LN`4uF4|#` @B~,p\.w.1 `?B h˿K F 'Swߜp,{ v:"|;YLCpĿ'7?1~!!yeSP`bڀ @ p7ūB@B"BWM%~{ye bzUCaz@tsÐs0Wr"'O8?rESkW`x8a 4 O0wɶƶ?o+LLAUܕ&&hft(L3Ey\](o\jJ U1a?/*˫Ul%99A*rh@P슜$ 1$g?E C]?~BP8 Pj`#ny7󶁆UWۼU{8$ uLU14A~L21?G?cjft3./oar-2.5.2/third_party/rpm/SPECS/0000755000175000017500000000000011757171206014565 5ustar plbplb./oar-2.5.2/third_party/rpm/SPECS/ruby-gd.spec0000644000175000017500000000301411757171206017010 0ustar plbplb%define name ruby-gd %define version 0.8.0 %define release 3%{?dist} # Be backportable %if %(test -n "%ruby_sitearchdir" && echo 1 || echo 0) %define ruby_sitearchdir %(ruby -rrbconfig -e "puts Config::CONFIG['sitearchdir']") %endif Name: %{name} Version: %{version} Release: %{release} Summary: An interface to Boutell GD library Group: Development/Ruby License: BSD-like URL: http://rubyforge.org/projects/ruby-gd/ Source: http://rubyforge.org/frs/download.php/39577/%{name}-%{version}.gem Provides: ruby-GD BuildRequires: gd-devel BuildRequires: ruby-devel BuildRequires: freetype-devel BuildRequires: libpng-devel BuildRequires: zlib-devel BuildRoot: %{_tmppath}/%{name}-%{version} %description Ruby/GD (formerly known as "GD") is an extension library to use Thomas Boutell's gd library (http://www.boutell.com/gd/) from Ruby. %prep %setup -c tar xzf data.tar.gz %build ruby extconf.rb --with-jpeg --with-freetype --with-ttf --enable-gd2_0 %install %makeinstall %files %defattr(-,root,root) %{ruby_sitearchdir}/GD.so %changelog * Mon Jan 23 2012 Philippe Le Brouster 0.8.0-3el6 - make the sitearchdir definition compatible for centos6/el6. * Tue Sep 08 2009 Thierry Vignaud 0.8.0-2mdv2010.0 + Revision: 433513 - rebuild * Thu Aug 14 2008 Guillaume Rousse 0.8.0-1mdv2009.0 + Revision: 272023 - import ruby-gd * Thu Aug 14 2008 Guillaume Rousse 0.8.0-1mdv2009.0 - first mdv release ./oar-2.5.2/third_party/rpm/SPECS/ruby-dbi.spec0000644000175000017500000000542411757171206017163 0ustar plbplbSummary: A database independent API to access databases Name: ruby-DBI %define tarname dbi Version: 0.2.0 Release: 2%{?dist} License: public domain Group: Applications/Ruby Source: http://rubyforge.org/frs/download.php/12368/ruby-dbi/%{tarname}-%{version}.tar.gz URL: http://ruby-dbi.rubyforge.org/ Packager: Ian Macdonald BuildRoot: %{_tmppath}/%{name}-%{version}-build BuildArch: noarch BuildRequires: ruby Requires: ruby >= 1.8.0 Provides: ruby-DBI, ruby-dbi, ruby-dbd-mysql, ruby-DBD-Mysql, ruby-dbd-pg, ruby-DBD-Pg %define _topdir %(pwd) %description A database independent API to access databases, similar to Perl's DBI. %prep %setup -n dbi-%{version} eval sitearchdir=`ruby -r rbconfig -e 'p Config::CONFIG["sitearchdir"]'` eval sitelibdir=`ruby -r rbconfig -e 'p Config::CONFIG["sitelibdir"]'` ruby setup.rb config \ --rb-dir=$RPM_BUILD_ROOT$sitelibdir \ --so-dir=$RPM_BUILD_ROOT$sitearchdir \ --bin-dir=$RPM_BUILD_ROOT%{_bindir} \ --with=dbi,dbd_proxy,dbd_mysql,dbd_msql,dbd_interbase,dbd_oracle,dbd_db2,dbd_ado,dbd_pg,dbd_odbc,dbd_sqlrelay %build ruby setup.rb setup %clean rm -rf $RPM_BUILD_ROOT %install rm -rf $RPM_BUILD_ROOT ruby setup.rb install find $RPM_BUILD_ROOT%{_prefix} -type f -print | \ ruby -pe 'sub(%r(^'$RPM_BUILD_ROOT'), "")' > %{name}-%{version}-filelist %files -f %{name}-%{version}-filelist %defattr(-,root,root) %doc ChangeLog LICENSE README %doc examples/ test/ %changelog * Tue Jan 24 2012 Philippe Le Brouster 0.2.0-2 - update the revision scheme (add the dist as suffix). * Tue May 13 2008 Bruno Bzeznik 0.2.0-1 - 0.2.0 - builds from local directory - renamed with caps like perl-DBI * Mon Oct 30 2006 Ian Macdonald 0.1.1-1 - 0.1.1 * Mon Feb 20 2006 Ian Macdonald 0.1.0-1 - 0.1.0 * Mon Dec 27 2004 Ian Macdonald 0.0.23-1 - 0.0.23 * Fri Apr 23 2004 Ian Macdonald 0.0.22-1 - 0.0.22 * Tue Sep 9 2003 Ian Macdonald 0.0.21-1 - 0.0.21 * Fri Jun 6 2003 Ian Macdonald 0.0.20-1 - 0.0.20 * Sun Apr 27 2003 Ian Macdonald 0.0.19-1 - 0.0.19 * Tue Oct 22 2002 Ian Macdonald - 0.0.18 * Thu Oct 3 2002 Ian Macdonald - 0.0.17 * Wed Jul 3 2002 Ian Macdonald - 0.0.16 * Tue May 21 2002 Ian Macdonald - 0.0.15 * Tue May 14 2002 Ian Macdonald - 0.0.14 * Wed Apr 17 2002 Ian Macdonald - 0.0.13 * Mon Mar 25 2002 Ian Macdonald - updated Source and URL tags * Mon Mar 18 2002 Ian Macdonald - include more documentation from dbd_mysql, dbd_pg and dbi lib directories * Fri Jan 18 2002 Ian Macdonald - 0.0.12 ./oar-2.5.2/third_party/rpm/SPECS/perl-Sort-Naturally.spec0000644000175000017500000000456511757171206021313 0ustar plbplbName: perl-Sort-Naturally Version: 1.02 Release: 9%{?dist} # see lib/Sort/Naturally.pm License: GPL+ or Artistic Group: Development/Libraries Summary: Sort lexically, but sort numeral parts numerically Source: http://search.cpan.org/CPAN/authors/id/S/SB/SBURKE/Sort-Naturally-%{version}.tar.gz Url: http://search.cpan.org/dist/Sort-Naturally BuildArch: noarch Requires: perl(:MODULE_COMPAT_%(eval "`%{__perl} -V:version`"; echo $version)) BuildRequires: perl(ExtUtils::MakeMaker) BuildRequires: perl(Test) %description This module exports two functions, 'nsort' and 'ncmp'; they are used in implementing my idea of a "natural sorting" algorithm. Under natural sorting, numeric substrings are compared numerically, and other word-characters are compared lexically. %prep %setup -q -n Sort-Naturally-%{version} %build %{__perl} Makefile.PL INSTALLDIRS=vendor make %{?_smp_mflags} %install make pure_install PERL_INSTALL_ROOT=%{buildroot} find %{buildroot} -type f -name .packlist -exec rm -f {} ';' find %{buildroot} -depth -type d -exec rmdir {} 2>/dev/null ';' %{_fixperms} %{buildroot}/* %check make test %files %doc ChangeLog README t/ %{perl_vendorlib}/* %{_mandir}/man3/*.3* %changelog * Mon Jun 20 2011 Petr Sabata - 1.02-9 - Perl mass rebuild in dist-f16-perl (d'oh) - Remove now obsolete Buildroot and defattr * Thu Jun 09 2011 Marcela Mašláňová - 1.02-8 - Perl 5.14 mass rebuild * Wed Feb 09 2011 Fedora Release Engineering - 1.02-7 - Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild * Wed Dec 22 2010 Marcela Maslanova - 1.02-6 - 661697 rebuild for fixing problems with vendorach/lib * Thu May 06 2010 Marcela Maslanova - 1.02-5 - Mass rebuild with perl-5.12.0 * Mon Dec 7 2009 Stepan Kasal - 1.02-4 - rebuild against perl 5.10.1 * Sun Jul 26 2009 Fedora Release Engineering - 1.02-3 - Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild * Thu Feb 26 2009 Fedora Release Engineering - 1.02-2 - Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild * Sun Oct 05 2008 Chris Weyl 1.02-1 - initial RPM packaging - generated with cpan2dist (CPANPLUS::Dist::RPM version 0.0.1) ./oar-2.5.2/third_party/rpm/SOURCES/0000755000175000017500000000000011757171206015033 5ustar plbplb./oar-2.5.2/third_party/rpm/SOURCES/ruby-gd-0.8.0.gem0000644000175000017500000012200011757171206017532 0ustar plbplbdata.tar.gz0000644000000000000000000011326200000000000013324 0ustar00wheelwheel00000000000000_sHiq(_XD73ݳ`! 00ɣ3VU5fc'ӶK]-P~^}kytߣyw9>/"2**k0чtWq~# U ՕV?oV?–+T3 #;\wPP}zpivS-77W-|=lqS ۗeПnb;H(hB]ڣ=h)9Ȏ<&cǎܦe[D荢|nnEAz~tb;NAqw'^3 c;;w7gdVRFT>;-2pX [xY7ȏƥZcrtB?8\4wߝ},ۀly_'aͮ|~\;ۇUn)ea2 .@ӭx[On~zoY>%fʑ<\nbUA>!|p'PXHu9A[nv'BJD1@ ?Tg dG]޼tmck7n^uⅆ٧qIUܨe5a>Oԡn C7ܝZTvio2BձI Xtm1Ͷn4nN:S@js@׬vo} ` F#=6oEd.gI? m3粜ZϰvSDT 4bw #~B aB ԰Pz#W;x/o>9(@ DJ6)gtv‘KPZ!q://Pg6gA8e4Ey"#,.a$lQ lsS`MB,ޫj̟rȚsCy})xm->WC <Z2z钨JoLȁGqb* \7¶5nU: /8 z`⸆7xn'uvw<ڀfH%4hP:ae9n&KkuⱝM4 6s,Ϗ.>.~&=P^x5m-X#^[; 4 %3%Shhn1DV\(daW|ՊFS(j(Qlw˾"?6V 4OtCiU/ ԟG}Pw6Q !P׈"*Z]hz_Acaő0&Ǫ?%f0r$񌘝yŬr%z)2>!҆ n8Q,#XK={IYׂj?X7t[q`'lŧΞzqMP޵@e09]dσ]9?lY~Q[++)>>ұ|,26l77Bl[s$ryŦvہASm PmԆv'Q7]}ɞ*} l"Aڰ9x-m"{۶6oOI߸|k.--,S7Mc?ZfcKZH=|7@j}xя.c}hYbtTbؑEMSP h4dMg^E4(yf }ǭg_B N0jPn1-]Ry{ ?7@Pdր兛h5'HrTOXE.7l~#({ PK8}=B 5oَZoYe.aS *<5a3܄ Ln-ǟXv!\k_yj]r"7o0,@_ܳAbSnD[K[qZdG'd4"Pzn(amo>Mй&H y!$eD_pG%Փ;\K6c=OC&njkdmlڸy=@bMڮyWt, 뉧HU dD-5>w*p=.- u|,!cѺ>=fn}x~IPP3v:_GuJh8!Ba.idfn3-LU:Y_v7/ݸxkܸG 2F t@OQsf]ؒ:T'3M?ZۿVqple%l7.^Ρ,t9p2kyՓ_8#{ `{PkU.;\}(\TBj1xgxc:W[R [2-3W}zg9jw< u*a')>'틵iS\>F=ӧY =䏕z9|:G}vJm:dB>hL]*m/n6q(dw1?M'bZFbhPjkv@D8Xy[c90BA'HMAr ?%og42wgL!{B=I'sǵ DOs<]/>?x"M+pqy#k;̌>] uH4{CȚcLpƽ2g4A80}6C|sՇكY Cņ9 }iNM3|7 "v0fw;c[#2G<' 5pdJfk9o%إrfLM Kx. Qg0qq'V4vmw^Fo1 Ɛ_L2fsf~k3u(h/S1 ߆c]5 ^~H<酛6ABzGse|X{N @,Q  .nNrzX);AF 8e%a #sX1BYaj :{g7QRqiA^~yO{q" 99%=A EUy[taDs%L8JyĀ41@]wftӪr0Bժߧ\᰹vpu˸/4m&٣0 ˃;OprPp@PLόN~1qhX!d;*ࢅ2|Ks,r2,ilSv˛0RGќ*^zΟFɉ~͟BW@PIĂk(ZGcm4-мc8f2 =䠦B5߁EФ5Hccg (lo|Nn%_䫻Y36V,h@a{fovqu0t|H9 @:{%ap` !0(){^{+G`(:)V"֎u:-{1?S@B7' ? L6Gq2=n0İЊXWd&. QptdXTRzxbb":szd`rR$if<Š9 3 QQh܏ד.}Ge|X)-(K_UfJ/TV(,_mYǃPeTw?w⹆t\ÀH0g6<5cL09~3Wf?\a)9Oޒp!Gzq,MLyQAUq *Mj<e\W{z_0Z e-&Z9iSZM%V{#zRV#h71PVF*1PV ͘p/[~,Otx*^hRF8hdފ7^4Ć،Z3֕].zkIb9aK#2i:kz{9FMv1=[b֞odQ\΁u#gF+3<duԑqf[[~#K60vP2r).`~znlՊ*f fh% u|{ )"@) U) .ur F>^ N3$GH0n^Z<ٹ-=gc|== XH(T-'NvEP#^co'Nɯ ~ 2 xwTvCǟ1udw! 16)>v7aL)hḰn!tafcRrU>RLh~~aH_J ((o#r@ΜcC#~U3 M!@|˄7JmBED,RP$S=Ƭ| xN ?G:͐aՕSDɛ6+ǁk8Nd쾤3|!/7Xhsnм !:O0, 6)< fTD@;e2޺rg*M(abDlG&hv?,p\.?*:x 6z5]41I95| Nz$N<2jx@ϰe$c'*wSgb?ӪZJ ":ӂǙcgIjPEٱӬaI]Zc $y<:uhj+|5Yz=RN;{=h|݁ٺS0 )oW[RdJܑN.s$QB11111111kW*?_jfM8K ,RRq9 wP^Z,q4P^hG" }4Ķ#`lQvE'z.Z 7ͫ*GjN J, |^S8DGC8; L0 c֕bd_'L] AC>3".6Rkϖ\r9g"-#{HNlA5º5(L JRx5 X9As!lO)=g;TX3;W|Uhh=r'e'ee[ip N\f?N* $pΠFf{'U3j^.w[/J$9չ KY5h-.{(Z'2\@@[<[:O8hsH6<$Ph(硇Y2dz?A$}AʇZ0QH03C + rzCqJ)( *F>2AG#P}Ƀ$J!e|0˂lFgi=tkodOo0]r.CFЁ1{X\eI!+DfzTVb >ET&-ȞU{a<~2[C]EdX5AJPiBeeBha"TtB?"dE!UR 0 ekQeq޺GuꞵԽjYT+d#qq:3 wT\.[B.%C +Yy5{@5>Y_]"ZKE* tFٮd \H6`Z. oň VDe|zyP:+FkЍ|HX ;7Z+ BWxcmlD*6L58ܠFs@z!gR!'9}3T N!dÝISvð=sX D[z[#;g7=oRjD"Zk{q3I6Gyi̾919)G` t5 I]`˱ ϭUCv-fGolkrܤ- '+Rȧ>gj toldRc$W߼yXw#%EO9$UN0\#{vJ0>SBߍnx;N!IH|BuG'^Rg0xįUߩRA /**O<+nts$ܒ LwQ *ž#K^nPťJNlj&74#o`Z{wKOaЦVmh2܀Au݆ɺ])z$3ƉT*DŽO*fwܐt3Մ0/LdAWV;UъtE]`+uVer܆`TEG6(TO gNVj]4؉/4({Zq2^R0&qD94-u(Rsk7'c1k*áLV8-pO0q|^HۋC몰ז- ~2]JFTJ&bInɵSUTiE(-T48JiS$)5?Qe8V!U?ATU:D-Ҷ- nEARgNY2rli I"ц轪,dl {H|;q*& ݢVr"r &dPa `PvIU@ B)1Q'\FRfj:܈D-IȏQ,jp+E zO~I=\89++,E &(g1/MV GYcQ {$ ^eB1֚P!,2B*C`-|(kP3ѣg%l eU *‰TXg"RZIU,]WFT \ʃ3$97KVE/jńPK*q ub! %IBv|vnF*bN*ѴϪ[tՠ & wyM6yKesew3Nt swɧ(CqZRqqC2D+d?XeS}tͪje!є !#N X)Ti%e &*?vP7FIgM,Rctj?/1Vj.onp'ŕnx:|S%nKG.:5 U ߵr[~[+S$t広reӃ|Nt<\-h?@wg8E?~wO??/fߝWѮ,eA7gݽ]r }mw[ߧ:{k2_c%`7чnv{7>d*i__Ў^d~Y4/}c˯gAcݻo}wgW;{_淡{Wpwz ;Ci9$ gZwfUN2c8Acwgs  dfs{wGNHBth?xso5sǘ{of`o50wandOѶf{ TX[z@ hZ:WEs@|wf@v1QG{\e*>>U*&N{75:N19'W"@3,Ĩˈٛ_y]6Lc KVtweǻ7gZd˷/YV,,Gvq;+3_&uw"Pz͠U?56he{O,uBҟo_,.]y٫Wh_!S"x_޽[{{˺V:;S"<Ňhb׻d7/ho߰6Eh8-}瀫ڸvu;?+HXi57n ] )t +Ų@|wq+fHٽ__[;,?^L/I (3MDsk %WY+_ᄂ 8xgM3L{-l%л(e9}HeՉ(Ԟ}鍷g׀mdwr-QGqW8|梟]:^BuQ2bP!-5vO޶PUX?ݗvҏ~7-ޣR|߽  ߳Py*~#BSڟ{$ .+?{AhU$@KJq#ۓP(ƿÿk?f|7HAА2kcԒ);?1@5G|bqu(?';;bEUt n ^7GC&/̸,:n2?a]{xvjuaMd# ٶm? vo5m O@T6H=&l䳁Iӌi {0fׄxI7d *jBsZs8Q&,OGݼm=Gn誽$U; Y p6BXq]whl۸u@捘 1 @s67ݞ#-hؑy Q&gc<(eCc^cTe"hs)Q'\E?8彨Cm^X0p=*])jTm,)&yHq:7%!EmiúnC"Gڷw3,b{0XjK(3bdKBg!^؝4N-/{U|(+ߥz=iŨu{ovoݝ;ȩ%H}E4~ Mh$LwHҮ=;?La]nQj^}?Nb{ݝ{~VZNp] \e6y}8G7f}?\.;?S[dܳx{ٛh}OkJ";Ҩęe$k A^o]ȲZ͔%#30f4,|\Qc f9,1{ojjI-Őo(~dT?QXAب^Vlj޼;{fSMmY뭔^~d} '?3Hz\^`8c= ctTF9wy 3hi)ȽC,-]|jj,@ZĻݨD4NmB􂢱n:y$Ge]ہ? 3R\⿬'ێLl̓6?0;sgfGuY}r>MA-.Ri`,,SSS9Z&l[T-Qo>4gMê-,9? UjM<ϰ+$JF7nadVl01FOȜdw3Lzr27vm/,=2EWCp .e,oy-7Xxr NIK%™km<^a\TI;M'ҒP bq}}|]p9@gFxFFRoch1@< $-D[vĸ+pB+lHxep"Q y^Qa]Y8s^⸀?QUՅ363Du},Q'3Wa^"C$<H׾0S-hw k[ G8<5@DkIdcDh< ;,dd^-̡Ju|YKFļLš: k$-s b|F9.p)!4B;&g\P'K ۩!̰+=#Ca G9厄 #Sf{/&uֳPi-A>W^\2'I ny\UG\"FV5r(W眂'5tsw^+wzTe"Lt^l2_a3~>z0`0ϖVR.ꔞЦT‰Ud.?#>5ƒށXxeKH,_L0Í4!CQ"EniB{Qoncn5D󬝓*OΆ*Ʒ!v6Bvi';.)%tȠmg+3ֲ|Gd0ޛg6PoVu0>ƭl&室Gfض-M_ *ӷ@)v+%"Y7qR=`tچ&112~k8f̵[ڞnPk߆uU+Xw@cc8H sd&DS9">fBjɺ6/-mr6`4O4.7o6){-MۻamX(ZJ-6I&kqVl1v\tUv Ν'e ҍ2pe4 P1jkb7G#msEMїelT.OwJ1q2x2Dlba 2mТZ4z4y݄*T+(CD,Ɩ&]k=@탡Mȵ )}$QQLj@ZpOzQSn@|!9~+% sLjESV (y {x)uIfESWb0ѼYhjYMs)*%cqj&g:m&Єi;(J;%c} iVU`3]hzJxt1N YR`D0R`2HH o&<Bh 0:7YeaE W za^_ēԴcM#@<GOq<~Mu< $>כș&ˀZFGX>\VFYl "BΩg=$j WunMI!9]A`OQ|bd6tCId $4YϜ:s'&z=j1qR zAZ1сt?ΌAѱ&)+^&L8~iüAh*tM52>q h03+'L#vw&݂vry~YDMzvZ/mpOVV͚|~V$>/~hW5ՠG:"~uFBA̷0P7yYf@{f0w b>: 4LX|*B.#BqYnKE7 `Pqmt [&^[k2v=f-yE^B!^C G‘KYx&5?=HԴU#gpև8CzmMvwybNj \Hnp籔[5e&gt'=c :!oT\(0҃X؂8=S73HKsF7ZL$ZQC {z h(yګڎ~)9fx/Z h+,)Y4P!2C?@) d'ep {6^ٍב$wJUƠ:@!ۦf2;18` Uq$ 6#9Mv@=X`;St'(U}jyު1OA蓙饊gMqem<.Pr$Y"3d}X>@]62Tuh|)?. #6o9%" =Kh b 픸+h aSMTh)ٌAd>6Ķ֙,/zQ7:7Z L"gײ+,[O[y+X{ńWTF2>3,&[&xz{i‘E#p1=â7 a|(bXcIL /-<Ľ AaR%sn}`tn>=09⸆`dtbQ/PvI둯p~aRӬJ+Oc+ƔGڦpͰ)T} Kr>a=Fh=?&|@ _ۏϵ VT`K֊qxMbmxu+uFH52>-KFFeB{I!Ў}[r~OgM z;4F[PY}cc 8FwZsm{x{iO! ٠1g/6o ji~= [ ٌOQJPa("byNOQiRGy'CN芏GFl%եdiIb™ v}$r5 *R?ƼJį6ZZU3?qƼL2[*K4VMUJ\Qs=2j`n1Ԉܦ.haL^ §!e6,~pP?>`5.WrDl>G*e>eAk@ѤEql@}r<Dn |GV[Y4ú<< ĕAFn(`00ELX@Z0*JBiLX|W[< c [[Qvz|"Y(C#ڇh"3P&xưWZ`9"";ԨxŮ1o\*biʋ} Μı2fyTEf8n<O9mzC>}A/Ut6}9aǜ7 Tԅh1J{R_%KC7XQ4b<7ߘk*U 擴*ЛYgJֆdi?<<nq瓅?̄(ɯIrxuy@ey-f!ҜՒ8FskyY9IswSSXgsL&ʵDON Dj+o$\a4^@4N#{~$+(5{C{~ڡi 咅-6zv؇Pҗ TjzbYӐkY̑%G05]Cv2('i.<h7/0`džlD 6"lƝ4(;0e*%A_["3Ë:¡W4]m$tyIxtXyTQ%E0pב{cI0K0Ȇi/r##JȞBIOhu2_: mk|°5=8/#} ھ6؀ćӇ]tԅ #a1 ;oƋRVsvuq5OsFSkxq}Y-|kG\:۴S\Ǜ _;ZqevtIl{Ͱpt3EA}%!O ְHT<;;,KRh0}-@{E gԱqW}Tx(9s0h+K&L/T8\7A+!T^sOeO`Hj#& kOEO|Bí0g!8GXU*-=Y6<@n J`0a:{alG[Mk#t񔌵ANj;bgI\VD7$v uґh"W[ærBe $Ə#r$Ep ]GKgAo  ]ۧQ\9\1&ʧk2yU3<ޝ*Y(Ɵ Bz*HtN\z:J ' g<#(̩:Q4?A;9{(c."F'5hH|4TT3ZN%-$O<5-IyG^6$_Z ?,d6*WTIgsfŧ Mez!ߒ Wtv $kH:|iĜia\f$WJ쾍өʧ/SKT k""1QU;OpE#QBnTMFcdWO1 i'a+dE-,ɵד.W[8ۄC̹ H*чb$ubl0mj<6 14LQ cfU0RX.Jˆ vg8.2χelm/Gyp<} IO! ;I nsܒ_m˘hoReHk7p7cyEw=yd-I i%:u]|'|(-Yr8f!#\xwY)"5[\tު%[U t;ߦ7>?'/ JF# h]%cAe}D&{w+*<8)t)>Fh13A0aJfoQ0C&6m5jϜlꖪN1 V=i4I@J0zPݷu<ÏfODyָl\[/ytefv[Eyh.2~n *;^DW8Q\)Pc-z!ϏmNO(xLDډ:J bDTd<NYnK{'Yp Jt\bn) ߄ %0iN˟D8r,!Z+@fi B邅9E#cc5غmICC6J26S{o Յ~ eNV6{_*ziQ ]"d.%MBLiZUwjh9g<x1m:NА#n!;w<d=ExF{g1q9?;IM uVr.5(ۮG|)jpH͘?u@Z,Tӭmӝp'|->Q^Z!EhU.0T9([#S ::PY~S_z%-m%{ϗ>r&ʲp%Vllfr̆YN0z o1mtBk4#)U Jv(6^ UZ+%dEx䑢R MχOAl$JwG"Ɓc9HrNFj+1DeIw@+Y KG:{2ҒWqzvSTg{m#Yc,;[pNƆ.B,d.FӔ]Zϲ $N ._tV%mV.-' T[n,exEg;j$9^~Z^[ eeo"WWKFSSs2_r+JV?! 'QI,nk"'vlIR<9Q , (#we^aïVq\%^`Ob^z ̨d,pTG%zeoΨ?'$K֒^5a/bk3]5Ge[b /$Y܈zKnTR.6bi#]5MN,ᕲZB&ҍ-6qI{zȝv4@Z! =0 xQ8p>UCj_0% s7߲x$Ê$k9}v%QtEr^Gb)+h##Iw滦[k+p1`Pw6.?QV`m5+WA*ji{11~YP20W"DkA#@K:ʗ,Ac s9Ct!NOOfM):2"~NkuC3D_Kގ,7M`[kkœˋWn]?qbb̺k-k'CUFP%C%xx]Ae'vZqq=b[bk]uwGNfGJa'+NNkhHɸCœex|ZzFDžKnmn >Ja`}ծ4;8T[@ n[oe1m刐e0G;tsG`k;'%,Ni XYֲNtYAU݉x8ns>`p4·DVO--moo7;=[;Ke]?ʵ1<tX良ʩ? X8 #w="r'WFjZ\tG"N{s :TAO|MkaGN:"1ֱ: iv o_Kz) Բp'Wg/X4V)KH(SxdY+;% !]$Gv'aF억>L-%pdq [ N<9ZDƂgpylȄt`qFJ/X /TK#^(4 Oip&>-r+b:#M^! ?#*QX4,4hȈ@MV[V2P/Y= nEqŏ_<ܙ<Ä CˈPk>I$fˈ(~]䦊P@ Z5=̛ 8.ecך\[G(YD!}V7H&Ae}&ا;|(yM֦>rB"6(G4R4l.'ĖVvczQgJ[V "W mm}w ͳO͞/5YV&@Pmr7)"ee J+{ֈ߆X fI6ݠF=jxg2DhXٛ]¼wɄvQF٠^t+na%bS4:b* W1 lߺ/͒+:Y<s3@֚]Lr>SjGk_:\8χ(p}Q{\ xC KB49kyH!=T+pXfFȆ-˗'-mGc񐏡4o2:MVObT *)Z6aFx5~fSV)i&k/,uv+1whtt>Qdd;dX[ "%&$u q޵&I\ *@0#şO+ܠƜ'Lyb}mZMg; "Wlq-#PRc8[Ѽ/]ZCVYU2ȴ0?OZq47)jqv#rUx>IcoO< g9ӡl5BUAI !+t϶\y|iC>1Uu.QS+"fgt5² NcC!XqEȈI*<$4UI+!7PF{|逜vSP[aG`Um"q7b244){_VN U3cΛ$MISʾ9;Df:TbH"/ţp︂ymmXìqxLl);_ v YU`s|RE.MԾԽ`B %70<2W2.+Kܿ$WXNaOy-Cvܑ`RrQ;8T/7ELΗ.)W$5w?qH^R"'adW@ >G]};mNp+~ͷ )T^WJQH8$*4CBg*AK jܦ(Gxiam5~hB4+PB|gt$]YѤt X'A Р0ȖpnzDI7d V 3G8T(1&medJO߁ӭꉜ[ŚdZLDZ+X3Y.0'slӞO=A+! ղSpc)э!0㤖^T8{j )cHT-p؏7gdyLvi^#Ij(!M.(n 9Q"?yH4up -oDK&E]SРǣC-_ht¨5zdxeN\8:#ŲϜAmդ|DŽ$¹`*QhOX|Xi+qY|!"dK3ȿSfJJhPs<Kq) T0N{\)'ZA.*݈5]+:] \EGww7qb&Wd2^]]n$$k|SxVɈMMsk&T0lN~˜S1Pנ'W>h>:S]43NOOD#Qa!o2+-~ξuoP4 IԞuq@U_(|< g8{ǃ' ]*XMW:\5lIk=.A]K4X0j3U^m8׺n7r,saKmxJ)sx teHVEc'Tv*}4:ZlV:H:3CXYh '-5Dp)\u 3RU-Qϼu9uD5}X4j@–#JCnBB0Gyv(^-J8i҆.Ry5DLe"bDNPqdow őA% u:|i&o{_ a`L~x#,j>z_ $ >͘b dt%Q> y/[KeDꂨ[&Gk$!` 4Bb AE\8krl(Ef D$Q ^RzG쀷ۓ39FI$ONk>b8Ye,术@S] 4Gρ 9|ȃiUT̯Cо/xcsאLi=lF?<ңs#ٷ\^݌иE=r ᩪMtI*$c[6a({E jf2YO3B\3ـVx`voMs& ڌ*? /w[͓ c,aÊN~D=0ٱiKS2-u;pi7HH7 Ƃ^^bN!DuNՍc1ڍ2M2jj"S_ k^?1ƹljMC7eѓEM:KÅQh(W,:wEh2?rb^zyI6ORzvqb`OA¬oaxҪ]c3/̼3/jg ͢|xcAed YHCF9kn*dv$Up=0 22S_MZK~3 eX,pQ1⓳_؋H,3~j68< MİϖY7Fy<>(6FJfފo lVKZ!F_jP̧`9H EcK\dG"^¹NNC8ʞSRb!0DA H`r@[0Mrm;V-ݒCxhrap\D.ڇ/hM=9nk}M2Ь}QrH"͘sq#03r^%Q-M,<ce;7ŘǁA !@Ɩ@!Ŀ!B( ->!.C?F(," $U 5@ %V+9c!vW*!(aDD A)j!iRaVHQL28gZx}ٍM5>W-}"RkW̃.fHѭF@S= Q Z3_*W!H"M@pU$G Щ`Ŵ ;}VDLj(վ󉑃xI龱OH8aX8ߏNa4J)n9H]W#e &h ``ATnQ2gIIpa_='>}n:p Գ8:!8w=2 wPWef E5IFK>\_58 t"IH`*O4GP${:ma*,9Uh-IĂ9h s.Cd,*g̑%03qg"lTH^9jbnyKT)ߡ:.Ҫf )!-T|N_fqVbдͼ̚@4f7Zݰf`G)VLQa~o#Fp>6ǁ Px1F"l_*`i0՘aGqXd,E%`^6(&`#YhLJ^U:'bY-/L !I(4.7<]9e1Br+ss I/֠ .XԠH=k ?z8ZAQ{S,@f[nGguj@ fWT3":vEU7kL![" p%ZobXɱ`fb(CM'쭷lÁw~$;j(0#q.u0ڔh+QI%-#rD9 {zMd %-C@BO'@.9"0$No98Hugd;xChyFjH VvoFc?q>lgP]W>ӬqUy2 JmydU| 1= HwJc̘d@%'%,F|$4gH?b|7z'D+,Bv7ҍ1Vc /Y5ۑ( !$עwLF]mkŗhl^8wɞF 7`лP Bhϫĉ49jqrEGY^BL:ƅmNbJ pRcz\? ⸒ ArļA"&W3s8HFd,-m5hMs*2a(< &EVCk#jN ,U p-< *a;H5 X#NJd霨@՚y7ՀpHS2'O.z5; WXPCKL}-8%d1:̇G^EJ,YR4=GHs.4!wP6l5MIz(5ϹZzU!`uMvFqw)gOښn֓mHeZ.R{,R,!Q'Fdb6hBr$h- iV%$׊H Ǎ1$`X~x37$eQZ5{ހ7Lzja=_|$6Ug_GFZ];2gӳxp!EbAEml`;%6ېQJ2 44DA(J攎 m 'hЎFшZawRFTVlc"JhckgY3F*cti1FёctN0|bXRiIh7"TBCJS4m>m+lhSu!4qIxq!Cjφ+]E] 'ZcLgţ 4nuF[\ zS?v&=M!,!,-eyPChK,ݩK6CK0I`XCCAoLUX4^(X%>貵tk{JUS㢈bl2$g, ȍsBNkQXj=SSk?#a*HQCJjB 2T8!L]RHAaPK@dCg9%_oP"a2T,矱Q4hSkt-9~4DԒK fLsSDTl(蹆(SU-4d+5D\Gl4Jp =7YcQge41PLQ-aOWE = .L }KbYD<*8**=TCᖬٙJRcH*C3XDC$3UE͆(T`84.+ ]!/˩4xJג=5FhW 7*$>»B!!W٤Ŏ_P0ņz Dԑ"eh_cJb5Ʊ M5$ 9(wƸMUb1J)Cjt,>Czh#79fx5h7T9&- ۪'0 8~ xIr!w} E#}o S# "OD8l !PNΐ2a4֏ybWl+;% G)+ˊ"H*CDޗ2> \Yq  2ju"T!Dڼ.4K=08B<) sK͗z͍@MI *UMCA$jUyIBx!{h!9{p)[G?XZkqX51gV7hFЌ$ 0{"8FE@8J_Z8ƊT䈩@sseD)&ԓQhG7(wB-qX\R^ _b'/n| \L0eaGu,L4cbEW˥a/srE$#IR{wbm"=>r(R ҃!HCK WHS^?*wh5@~yƊ\@!u‚èBטM؉gR-+Ȥ _3)|ՊʧpDJp̅!N]U U@PgaTMzާV@-xJLws *w?[m3-XD-0ꣁ;M5h=J+%Db\Hw+D֜}!2CXN bq0, 9+9XBk*kgqkYD3A)m=^g`x: g;#1*c}? ̔ s-1 9ő$x?<%$HG=0~#,;ؠUH D?dꛘmK^mb3 ?ԴR\HV)rZV45%4+U(NV_T4ZkSӞyVM^Mx%CLgʟ1 bpXP0 L@ WmZ3"ţlQ%GQ9O=aDXlȻdHT :9V;U絛'PĉtVT"NT%p ?5hksiV9$y (jk76%o* @ LifZSM+MF*ȵ苴 6*BsGQp4HKP(r~fEdN6J^?k-6GusB3ܰ} hPhKhgsID] HŷeIv _i/4A0ø\#=0\]NoC[/v gץ͇O{pO]em_סBcP>>ڳ=Ŝa)*N1۹uWNڼh3x=]_7ձz'4ToS:U.UΝ?v_M:Wy ~oYYƚ/v qݏ߬ݵ[]co,{2xQKh_X5kM#|:dP+޵r㰫>~d1zbi'|=S *o䲊y?ou/\7Lƫԭ]? 8g_pӝM{e}.Z oX2e`Ak6'iN U;s뽮gM]W=~C4kʙppoNNLw݋s{ӣ~EϺ_lasͺqˇݺÄ}yվݻ󨻫ܹӿx)oݤEerSۿq{lK|Ys;ryѣlZ1]🮗 uͷij6*ُ̉T;+zv<}ەSo=һZWxNcmujsPG_{5cpޢҗ+*}ﹷUy/c%޺-}Y]'}иunbgvX89j}~{_t˰S|eQWo윊Kfu~K;;,sē򯶉k]QQ罹;u͉eqt㕮'x2a''ǮO[0nS3=3]=fʇ/}kOMy^Lq'~ubuw\;+m|6PkK{oM|1MX]0-[q`ճ?]9Ʒ{σuS_~<]>:u]˯-޶,÷wƟ]..<Խ.?U v}m5mkI7s+^Z`ˡ.ÿ_Z]ܱnH-,gGc7Sc/MG/kBkw߼ȱY43Kf|}<7.v@ՕN>25֫su~۰?csA_)kJuv4FܝDJi7oG玟牮T1P.\wb{w|󓷽yﵗݻp/9ol7jb 75|!3޶c{k]G.4e~dN)WSy/;M/}xjJrc] qvk_tx]=kVO+obEOƬ}^,4?ṉWT9[-'5(|]ƒߛ>۹6`y#~] ~8r8;;rJgg$Pcw4lMWzSɹI.*G 9cy򌞃74KMkX7{YEo<4mzKS3n+z2kXy^+Ǭ:tEޯj # Vu*uyxkr{˰_lXS%W߱jґ Ll]{Z*XK;'}w^#:k;h  |%mVIz57ꤥɹen|]%iud[^IxxҥOX %9]\qj*Fc˂ǣkj[[?ʺ]NX埥s"m爷r;G{$[݈֣L}ApIƩ>} wisv\3G lh߶/lDl=}Шu=0wZq!N˳Gd]ad>;3q]A'nt˯zbh;{ۿ^y~I.]}+g5wp΃νi/X:zu|i՝GY/paD;8ϯo4kc gܴ[:hLw__g]ޫx]G4i%)tyW˺Rc;,ٜ`\nu~w2%e1͹o}_=mŬ>x}o^*c0g'mzlټ[n3zUL,o*x6~O=ƶ(uё^.捯T;]LiX<0)7׽}k-}ʄ_ʚ9LW*ݶ5Ow}̻ wF>vG}Gxe|䅁%;dG[[yLmNs:@>dTAo?{_HMU;gSk{J|86- MK,9:nA']PʷO'j 4m=J RĩJ.&6*^]5C| j [ܩ$A .!ztr.DVCDh1gP 6+\mv-l%:a@LO2Al)LX( w? q* HdKWM/ͨxtY3metadata.gz0000644000000000000000000000105600000000000013403 0ustar00wheelwheel00000000000000_sHTMo0W{'HRT)!z*46vw K9lydY&_ޢmY>Mk4N KXY׈"_,!)9y؎bU>hХO_ڻ6uBu՚XщЃ0gglF׌-eg\.>}~x[ &p淔Bv#q'֣jPF"j2q7C`ÄGx{z, v?WrmqlG"0Tc3l F~t0mW Ѣa]&1)377l; ~ъڀnl<~н־<+[uf^D<k SԂ$bѹϞnϼؙCd5~*ItQ9KQ6s/є ݐy~%`Nyh24xEe< R5z{Ł-wO^C]$uY Me%pfgW?;>|m0 GYDZ} 5&)gIʍ2[-ze 'no2O` ;s`<ϲw رA{GS>sP h[xG%L\N 4aNL7R%vp1 EnjSvFsk wC}͋9gr8'XdLCW:iưBa4 y(Ip$sýCg 5N4"LH# Ѳ&6~yzG` Zw~z~?w_틹{^?!oDy^L A.Us|>E;qz{Uݽw,>E粘n=o0Pr'lpׇ(^GnN:ʶ4z,Ag; &gIA=qilQ31(E%S.¨@E J@-DAI?ғP&)G'@[T/(NCлTj-M^_&',8Y?e=p(D/}{Pèf.Ιa.Li}&U(EwoF(4OYr``dCsPI  R)tj:0=)hO *y~zj[qr $9 ݀iV1}{}-o Z"D=ӢR.FױyjŽ[HަJ( &jxʭsK5,6mibI?DG(:)rԽ$#AK-*h! Z]]͊sNOOikvq-zcD SXnF(E7(ȁYliGUeQ;e(qC N͐1Wg'QF4 Mch\ 'S2[MM 7MT2 el <-m#jZ6x[Lw`_K8biX}V=CE ,jU1T]$2i2\Fl(}y< I#Wb, F?@NfgA\XiFZ`lNci(q!^0]ey?'=!(B?.8kz 0-aNˈ`OF6QNb" WDDBsT%Z T1HF.* iPL϶YMC #A&G5E@tpLm Ob r, 'Cy.(12HQFBc,/:ƿ6&+D$fڧh5~kޚ5\%Q0Ĵ'DK 4 0C%!nMܥ(A "`UDYR8'ȶ#:{[w9po13 - {fx׏I!Fiu1e=t^7Ý:`جCG5t<g.\Pnlyl'gfE5~M`AѭMk \RNt)xm ʂ0XHd)%TdXۡʃ0оn a`"HEFl憙1+~]+fZ DA=xa㌀4,L;Ǵx2= ;d/¸a¾E1G`,g:G2L*H00Y۱lԊ}%&P'(F~]IHe> \s?ǰI㉟RNTSNb ۋ X0秸{O¶Bzl)D,f~)cNUQ'2Z}O9&0ajIqOՖkQdƈ)h 0tj ;B̧8 +h¾kt ؕr|}:ƍbw]³DF*n+9i85n#cF%WU}O%JW $4YZ^iV'u΁ib!s`7'H8&{rBy6֘YjLB(~ E'kd7w '˭IGrC-5wQÀIIHjRɪIĊ2Sв HŢ#P׊9S2Sqx}>tJ ٗ?;cVQDMDyhr5knق 5KW]JF5x;҇eݓ).e[͹\җM'QS@/1w!g/i \4TxxSݻYIGd?q,1~~YG[/A/$=Q=[v]ԞҨ|e$byF qbTGa96OO=Q%bi$_%-5R)ޒrӀ<%K_PB3DZDdr=.qL5șXQ pYOEqs\[d"弜iofY7N47SSTcpx? 8TD4;oHaCu[n*g##x6CbHLT,{z`ie=Лбp3ʥԠ~A6Pd,&vk!2&jCd0qP&Eo/+}/9JZuP04oJaR)SidkY;2V (Qt^-ZEK2z+v/Nl%Hr"8aQK@7Ý W1_uDnk "]FX W =Q)%t.tO!vJҖ2,!(fcSԅ3VkRRBX7)pα Nj|E.Rhanb95KyN(WDlixpNѺDM?njqp Dq%QPa~f0saο D@_O^Q'6ȲOpcOo `|A Оx 6ZQN/\1EqT`*1tlU$ũH 6@m/SDH1\Bhu˃D=nXA=,. E:O3P<S\m S-!ܦ)sO L;l68 dnR h)5pa ukwb]0LcHr32Q^j 9kbic~wyq8++ubKhZ"\}2i`<7マwjii6è|UfgU?f4ZrvĪͤ5*Thn$AWq"Pp > 3ވ{"C/US |xtT%^oաæ(j8k[76M"@F.ZX?=؉MTjYsK7nu"rr-wV'' Hs.gaLRS%h<Z a{{" ԛի(;Ii&u}Ɓcx0sf" Ab`gؒ'-Ώ,[lDJbkoF-%1a2QGa "$Mksӵ{zRÀs~5r9jWNbl{֮T.E{KPЯJfDXbBaC9^a:׫ Oj]>y<Իœ5YGd`Ud`m]`Zm+&1,GGwNi$*ik]J(*H8nUUC [ "ŕX=>@SOWT$K[K.6Ɣ`8k34\z\លGkx=3sc Q6=nT og_[9y4zPa"8Q[)a[rjGKbtpp1Ai&71nb Wg2Ԥαn/cu.N>甊,N;渓nj|ܒ::o2%'ąbZo?zs!^oT+*//5w'0x@Z3*47m)٦ҽսaū)iEԧ 7FY<Q^\$#JAEAkM#틣MwVT#{Tm!o减@0{^G5y px jikyplUR].+Fiu g$y0&]dw8H}0L}T}x$*B$| e)^6_8{ZBsh'2i;t3'nmMYKˋ?Xw_gLѾ@N}T|pY:ڇee>X(bRS?׷{'q%"x\^`C?).]q?0{8%#4h!w[(h )Nċ٧Ζݞ#gC|WR,XD%f誑5?ے/+]/,={*U-R2d'1,m&ҽ҉ʞsPSWiX(-;x=hC%veXΫѪjf~wo8e/?|/ݭ)ﶺByXj,a\G X\^]\Y]z,NQd~ }%`# m,QG z_l<#Tݡ MĴ8|Ϟ۩G4ƀ-'_NH)Q﫤FZz{?5 /a{`õII4f/vpKx$Qڣ3QecpHha6 %X餦I{ΐ$@ˆ)Ff^2G{µ(v5 e'vpJH篏$06ТWqV:/v*)%_Y$\8=<#8aRI%dBי ޝ EvHJa oߖa$7ty^oO+}tf-fV82d z8[ fՑZ9tNΦA<3@tekxhtyd}4ekD* -?Y?M]QnhFn@ SL۞&}Xpf ^7$?Co9牭v [h }|\灴 b-!VA;JVUT5= Vq`WueVSť:'GmjRѩb,V}Ơc*Ru]! 0*.Ŵw>Ԓ0 "SfQq-ec?Õ_3WGcI`䃍?xz>g.!pdD4L/QPp XÏBEջ٤Fakn[q,~?h BlLg3~}[5U]:UYNh(Ã)!֏brT+·b[aӓEJm 0`Uk#d05~FF$|?2%?W_=WVj i ny;VHJd&xfF׭ȇRB_uJRYi,)"m,M6?PȏӤc ^{ٸ鹨uFF04.Ρ`TP,۴ J RqgJ1dv.o|\9᫖Bnȡ6%!P (Je4~Ҳr̅ 9x߂S/{ャi}K}ifm֮ktR@8poggHmo?_+>)HIC[_e\;tt{p܀6|N7㋹_JN˟IՌaBJ]mfAS5DHɚXl{X>gWxy lsG8K4c42Qj Xp\@3ie,Z"7q8&{׻R1&ڐXOW}%űViSDL}2K/!  daeYSsec_eZrd-,jt͖'I {K*ė-gs/MtaTn`Y88JC|R!"߲w7 Nv%Hj F#@heUK+]_z`IfX`hv~jRY98S+ˮ7G"N2^:S[ZoKΘ5VM]5 )x '$b_iQF:/pg◳v{6fVU';yOE3ΙzI$Na6*Gʁ*9~zl029܃\(ׇ ƏN qo/ySrb3iQأ歰c^fS_0E յEb: ^.Ē#.6G,gvI;jP)s޺FeVsw!XP}oPm펭>G/($IaLS3Na6,Z3b=[޹(ҋ rhϢzw/dH}lF3}2^fNEQ9n`V 7Ð"+rnS. AK01-AAcbԗu)>fTM:Go;?C }*')yjP8e(yk)[?X݄~_b@looA!u{<ݍכ[/dv^j*bݣ[[G6_lb{/u~pz{@>;l Dyspc{}k@]54*?7w!BG&Cjh ]:hnաTp_J&`~[6GZl﬿DS >|h#^$6~h(8p@X?ZG=z}E4Bݚx \ʛD̽]0Pc7 j#bj›$ `IqxtqdywH!_5C,M`ejM:z3`-D^?bUF^'TPOQՠO5{u/eJ࿹6BZP1ry'a:{?+c8W2|¡sUJ+*j6AaP㮤0%U`05~Ւ 8Qp7~?C/~+TE݀ Pp-Z_46^~`E lmq-2zі6 $gz;K{xӌZ`^ $g?`!_l~2Bg=hnm,]ןmñPV%pdp`R̼ WŒpJ_H]>B<EUkc3s4Y]0qsrbР8NpP4Z3y,A3gS=Q,fN~ih]L}3>Ds?9>¯iIC#(!rC+B\p{ugc>ykbY~xяrgma3 @w;4qO\I^Q92LA܅՚(<ʕv93۹ҍPو. J/D=zYڹۮYaY)X~! hnM0:nLn;3~-o)u|;uIzJxTلFc ',ַ"Dϭ@9CGN( tTvOe'H99]ϋgaio.j *:>=>Q [HU ^:jMmZF /al 0)&f+v"FIb 9LjA`G.~ݻ݁w/[MƼJ)9n -Py+xVQ7(n}? XmDl#>n3~=dG=&%y"IvAJ3mվU È}^*tfoo2 lC6kf6f0X ybo=էN83v,'b형oQMHd-, .^Q(IG}8bOƢ^FK _1z&#vx : Y+X-q/Z?Z.Wh; pg@"g/M1-<0UcnVͿ<kv#bb([ T:+}5ů޿#/YyW2WC!~#H7J4TyuHݒobPBTK~PB|K'ß.Dv904*dJo aޡj]Gm=JQJ!>N@J 0]_jJKJɩJRɞ$h0Q[9i T'z46I Iԅ=8w$uNVuEjC*fdϘh1RۼZ%Wo_-w٨H"# 鄟SE :'Ftq32m1^bQVQ#X=`$jE=s- TIr$Z6NqWtC.oPM]9-4~N;czWxIr.EӭnѹDb PB!\Fņy$#]eO!3_Ӏqn*bV ƹH]4& xt߮Iy]ww(=8`2x@ϩdMtt+C? *:ӏqa!OKt֒bHwJQ|_J_kj]~d̈́,{܅,a`죴I">;Emu5&fB=7A/2^rZ4I _rk : 0UjNj!זLKr\s:L$Ƽha|KA$vTLD߾&|/(*H9?":@TB[wr׀bzINBn**\WZPFDk}2dJz}y3q$f'UB\q,΢wu8SN؂GZq fQU'8$kݩ潵+NZΰH/^3FxJT]:{ˈ nz C\1>QxiSmSXu{JlNg{CIPJ+R*^4m5픊Ï''Y:@̚5AmV750)g\kk5Ih:lU, ĀP>K0٩aCY~` 5 ()oP`WǭDw]eX +tizaΨ2U ܤ -LlgΰjiG?YP{~f/Vqa̻Y1Mܥ^t9 'tzv[a |ئ.< dFiw< OeD1_aLE%qJqwG3k[~k0d:&0m65JVKM긡 Z&P7>ꂬ7sTΑi{olPvj`S`^ RU+ 6\1c쯨*EN irih@XnmEٙ6!0%X)΋N/rS4+^V|Һs4^pޅzwuˡ9&ozu7ߜ w5QU\]˸ڸ֣NC_cxCVBUr!U>;Jw9t𔝿 8%aZ-mÖTPoO9qښd=rH*Cdh NHE%dcgSju_-u5;e? 0Q_ۧ%4>)vRY4{x9GY< .cu7FwT*-J_@:KC{^b:[((:dO1Tѕt$R7cZ]l,&S0.1]Ϥ}JKF25@$ }ҋڤSƌC8L` ')-Bv̇l椠5 nn ; 7`o[GID䳺XKÝmYVUSvz O+Q@ Y>zA.`,EA`.y3h~)b{oȃIr%7[;*)ilm8e<ܽϷ.-j>*'!\Ra(>N\A|Vr k^A^'v[d$;yTP=|0ZңǏ:{/}Uoy߯8QlwЍt5Άw*P/D%طQ͎\WB%I DY &z7;EpPn'?"qWV5Ϥ V..H}¨&\LvkMҶ3IZ&[Z 5H;DrOjDDEQuekXf @WK['1?dXF.HP̜Lz'O2*Iu#;SڣԞN օO-a0>]lx jx h c߸PTa]1`ӹ6g_ɪ|w[ᕌ_VUa# O5aZ-l*^jѹ\_l^禅'Oyx88eLm.Q{<CA 1O!xܣEWo㫺:kHx&U\1Ӡ@JRh؏j|b Vre]6*!ϭ27JpNd'W ㏧ Y~D-grBj ,`͜ߚ%Q%4p [J@_lyy [_SZ`3]Hch(C59`V[NNEi^Rv1E&ybEbWKcܟeFv>N:Ž*N[EQwמSV<$̞!uō9ɸL*=_b,-ړnS:fN)_??0BՊ@:63t`9$(FR`&}])lڣ+Qx  h+5/p2ii'9RZcʤk%i)ƞ/s69iG;&S|2,i~+@{t,jtGEok*V[ YMNSe` @jGɂr#X |xn촹7x$@i?bKHСEq[ A  ǒ5Ch~Yj^hPzCbH8q$@5N<ݔ5J͢xK}]GK@O +r0\kՓDlȭtҌ1ksP|ץ+[ z8孇~#v<)SԜ)6S`;]yZM;SEf檸CU#T˼rEnP RBnj( TIRb@NFSմ0>@z+Q -ߕݗqХ r(Y׿e)gܜSd0VpZiƾŸƢ Jx'iluu^[g2 ~drpyJ տajnR*)Hb^2O&1W3iWF``HmzKichD9rQNbEr졓`\^+l]]QѢ@rSC%)^2o,"HNS5jPbiD<ȡMLKGtЁo mCr 0L\`OXMabE]v2wY9FRs{Ԭb,̄X_ ?/KXGvקT,?="jAgf\aDt_l^7[mzq{}2P}@n#1]ߦŭ^lD}7Yl~ \Sn 7pc H7 ~MlT߿l)^Y |KLp&ϔM]˯y5[?:.'qoƭ/ʣ_??^Q?~ x.U`Ľ=7[}LN LxFAQ4 Hꪌx&Aow1Cl&'4f(XQ ^ 퍋\k7~SyGɅiCȤ8C5wl\؇|*i"tGrܯޱ/F'֓T?5j'GI0H*k YCEG*xDZjg9N'7r9_Yq6>@V.1?VLV`/_$lmnɉ?ŚPjˇ(>^zPDx{ Jd,TT^)!i(e@Y]7s]ƥOZ ꗼ_㻏F«]\Y>uÛq;{ 2Aw3<<|¾ 2kͯjURwLt=qfB\&[o,(JXo3 }As"R,T@L~h;kҡ؋tj>gŠ1:V>ͨHoDNs׌ztnu茣wgze9c~,9gT,lzR|e8==h3_q=3S[YS<_d>._x<vsrs۷SquEAQM,s.'KGᜉɊml8|d\4#礽j38cT}*aYF=)' HnP|r-z"_HˋM)(9GM RU</w'ځNlZnS0ɋӥTJk鳅QEkE&RKè'>tL'n!]\7ʔsti2珶f-zLX~!|#`k|ʷ$`'w o]/+iebٵobb<èLOhpy~q<' P p];̲Yg 餽X)OLmM<&>HcWq]RK?:e |RTZB^رhC"bv1>Y(Kʗ]>k"IjI;*&l Q^p1gll`Aa2itbqc3)sѷ jeF߁JOghƲ,OY(ղ w,{TLr$!ǃ_iAwEŃ L , &My( ='i|oo#\߭|4 `f& L#dX` A&v*W}d5J''v` 7W-Sj2'bN0_"y6XJI6g:B9(Bdh8A'`S\:Q=i2+kS^f3I'>*u)? =$ 9NQG#f! CrE}Kz$P 73$)F8!aT%i7a&Uա`(k]Ep=H3=ʕ *~bUz'L'gB5?ɨ#fw1ExytƳgZCS5$(lT7^꞊+,d )CeYbO9 q + ")X&" j*Ǐg)‰SX2..F Qt6FQhQ6 7n2gl5YT'.K#g>M֭x=JRd4m팒]tSKd`ECZ9AZ)a8hJƧa?IJ/$KM3TmN2c{@֕'Niʼ Qg2ϯ?4%$8\d&ń? Lɟ]S>mXa$qO{?zx) ˰>SY(uaEmzvR|O4x+S?..?~|Ga/YQ Ul]ߎw߅ y .](?2H7P#;- b?.E~8[e4va&:{!sH޿1lN.Fd&ʪY@li2yjdb)<VRA̡t%-}?TlQvrcgц;#q w6 #cK\ U43n!2<((GƐW)bG7kV',ohnoxr&QLxatѮߤIt{eNOU!uXLk7 @u{+`TC>9ingx C?#s|TQ&J=Y[=SCLbS&K455+cxE8WMݵ_íU:L|2[?TH)intbM9gH#GpF=;sZ:~W4ڊ GUr#رB Z c,$M 4r,IߒKB`9հq4`l^l{^䲜U2;]ԍD)+ I ;|.i -:8kuOҳ~%G8SQ]'jeXWUj4Cj2Gz|viuy UuE>C -"%qLU$^-A%40'%`\}/vᰴ;u|W(/ˁ6#HȆ m3SzD76 "=IES 8M۹+7eqSv5,cD_aI;F"~ԕшojwFj7Ύ?`2f ;F1Eۮ7Aog<&G9D,raPU0%uap.v:vݡ^)jIfhuGx5&.]C@ 4#:Nr1-'`0@~OkEs3#scWVW2YP"о:!+Go68"U^'{Ʌ|P9&&MwiWWus!]6?>9;;Hfs%&VLcY}SܛZwYB彤-.N ^{Urh݀sZm|g.r1g +\(DoiF_Tsk r25t3`th@E; AK|nT7E=i&ĭխ<"ryI, E D-\0w  wFXp.X6!Oueǔk*3)PBr ۙ3LEdʲz*niŋڹeom#}MPi6Q4,k݄AdЊ,| LA+׳,*AmC[eD[^2?WЊ;w8Nju E=SU'U_=|IJp+wRT$U[$< 20 L@Orlyvp M]ځmLy󬪨EP: 8]>\g_IW-HnJ'qYs<؝ZY'~(}\7Y< Ws4\ahJhkx?1yDN!KO!t7%=*yJOܰ%1֟(s lXWǻ,h^;dY-s6 ݉HYE@_1x4dN1nEcFDXzQIo}/HLsLTՏ3J&-:]ӞtNcxjqrE[ L%]?0PVWq]1ssFD5b`l!k)Z ) iLΕfȏ1p_1VjY 1h L@|ZCN\2ౡҙg.)dAh΍]8QZ,S$i3N厪#95A"I82c8ДBS!sćUOח>qZe&h*^/HkVqYr|3p^ )xz]Y?% 5͐O~`a<9e1\!yCZ6iɤA**э{EҏFzRn,FAImY̫: .a|64S.̛7 M]'@H/"6M¶  T}|NU7ŀdJ4ⴉVr,h[ [P]3pXSdU=rvIZ(f[f +-+%Z2(Чeۤ_"f-nGϣ [pJhDaOЃmҧUӼKj) 3! £[:| /a 1\:)*"Oז#YUjlܮ.yP&aE=𵸮&h=#}mt "yBYc(y]"0tLQ\ԃTrF ">H h<O7DM3^`q1Y-غ2;PC|~,(5懠r_cn] h{->S?}},OzWꕊQ~1F*n㦶0ch oVqkM쟅?rG (4}Hd0*ٕvRl]Xkȋ2 )2GYa(~j}\q*&0ECCꚾT5)>_*қ@UH?uՑGa85i:FF`,Q뚱2dԛlj-_*'=Fu!9Dۤ,88Y^ͼ+ fU=^|x".lYz+p`J+KKKNtes ,Ln_^iZ{M9љGY}6F>yszOZD0E~9:j yU_?v& <"\d;djm<ƆPn*dc:E d_'vˍQ㲇ox k i*~}Kbt7a$Ѭf|\jz]01L;tqh$'HApr$p Aw%AA 1 zA>qҔ,-BgHD,ꋴ3G 穋yP'O/4g%xpVz=r>^cYa2 ˅di>8;tZNjO>\/Љ"u?Bޔ?뒣=bv{LL{ LjLQJ9/#XJq> V.// xlq>e'#=VyLEJuijDr *b~ߊ3Wј}*dJy]BM%2]W( =jo&W[\joeEfo/{ DL%0DHp68_pՈTSBG+avx6,y8ݷ >8a )0e[33.Z [ai2zJ-YV.%#2m DP135Ļ6`{ v=-O]]ődKc3dwFFLf̼%ݶw+hR)VUSb<Œ%,6g'ӉK\G,# "+gaz+vOTJ,10@ ~bMOOA, P1k|S~ swӳpn{VƇ@&=X16GJ#J8$ ۂdvs )CTQbߗ Zѻ(鹁N92bVtzJAuSТ^o0wpzӘOcnxN6 % P-OX<" -#On7k 2:˃S`y馀ĽnDiJX<]sO} +׎+՟J&I/3uwCEs,,a;SEQ'Rѣ(k C=j/#́{>sA>;Pƫn#eLs))R8nlyxͯj/BռU^/>pX~KNdWqj^cqF3/Jt3 xjyW!])X A_uA])6<W]alnB"%Y Wh N sѝ%/Y1]my1 BP;0qy_sf>9c{KRaN|jcky@4'J<ʒPF-Xc)g_uTGJԪTH]'@%+߲^-iTgשMR=_ջ'ˤ 54dpg'KyQfmjswPgus/3NKdZ{xkTd_IU$z Κ(79$2 P[yYwF!~>.WW"ݐiM"B˳5act4o(T;P#0_?J? 6bsS_}WKI#vXf-AQG\ L b{l_`,,=_?~pii=xxk|8ײ^q ʰܢ$\E)TlυoG}.].\^wry1tn{vN/Me \8R %YS"/bō-xCj .:muQ԰W) 7 X?l~kkޜ%=ibLC.HD綤L^̐KL(s %؟gq Rv8jr)asj\[h[=T0d)mOw0#'/k|gD$7opWdtn SYt64:&Kj\/Y8MM@6QK0*LtieDZt& ti~ro5ߊv%_ ٻkkp}yR^sFq00{-Z/^&ut43ǹ0NNT^u1ESȍ*D{Go'vESz*׌%yX?9Y}yB3KCŕ'N؜~`T^iYm'Njqզ.W'rh_ 㸈)T7D V;dA 2M1?<9 <#jB':eyKibCҡ+B'7x05OzqBNGWV sLP%(c!K&)+747;X?ju-I=rAZQ:-,D1 Y0=lfϰLĊ c7ySq|BU~9Oar5T`0Zٸm +hrnP^nJ:&ioMAIʓ%JrXE82Tsf;B{s ~+A;,xHQ τ W> 眣omv4E,Rܾ -K5yPuMҮh88ݱxux'ge 푟)yffP7.п Y3s~J<T5&/N@i饼G"yoRɞJL@TvdVͺ;BY#HFl[ˑ9"|>جub-m/S=w k5<D бUL`Dn&`(bjr-0wdE#YKMϹtD j}z^Q#&ۋD誻nN6$35kGx BqioIc,: KPj+R+\7[[ۄ>By4TW-=dUvx?lɾBvlntK Q)W X͹*hlٛ1jIUw2쵺Y2ʠ;R`KSЎ;f@6A `A}!ut#`(DӦEx$f9 Ï6pQhyT^f&.">5։l̰aX@\ޞSCfҦi[GoKxv J:Ǧ'g4)ZkH&;#Jn GNK¸r#ڳNG%ڸTu؆)M͎sP+Fݰ'7AV(?%P L?C}-/:wԜPP3Ǥ4kQXZwu¿+x {G" i BC@ۢ30D#y<KxƅÝmǁI8;ث֎;JNJzy#i0wp!t=R; py؆' 9n`pv܁jاy!0{/-Po1H no'O[;{t{uDtjGF`*LIr74 3{?al{c&0&X?|.!MYamL܁6-L0 8E-d6`L_\DTxM&$\pיO rp~,Q= `pb/0sy?L_ ?xyM G|swϿ}Rw.MÜN]1rY.\Z6`_=XP;{L%#yyۊwb Y d?E(Ѡ㌅M?RWsdSHf˪W+ 𱫔1+.^WE-X䐖:*T 䭋YԦBªFG}kk؈vwВUByJ*M{F,I'<Ģ.e(Be#6hʿZkS`2ݍ.%FL}jaWK֠!NjF#q%FบPk4ˢDGLɭAW|(٣Or*OAED+/0ZS̩tDcЭԟG;@Q TRܣ.qjuatZ^+E;6r1X' ,Qjk EVk͢oSmǮ(@*߮)xJ@L _| bIq l T)$v,{Hsu7#Ng`BI5*+Ob+ ԗ 7ȼ[U`U; ,K {e,>3ӵ  @7W^,q TgnGl5BGE2wB>|n<8;``A0pKiegb9 9$P㓍N 1 ̣S`si{tO^sdܢhgǨyh|geNU>Z#u7_Pe;Q"\g x K`ÿ#:D&^R?s[UiEwڢ;m-Ru<5a2'n }K.F%dAc3?#Hh w_ʢuҌ)Ozdtw*`e3qܕ齐a*H(R_9PXje8BzrV}SbjԞ[&6 QfN2$6wہ<.NY㻘,a;r}iڱή{>8PRɷLc/3 qh -A; 3}@4J#cxLVn#@iڵhRM XzMp{Eʋf@a#v[gㅖjFol5 Ѝ=U62.F`~8+7P^%JifB.Yz87\z9ryO4B8u^ 06%qg+98۹0 eG.(N PP)j?rs]u:v՚e뻡^xQIvnwS)n0P~|Hh3&̮H>L0F˺<(n(';SULaFSJiZ<̩I3<֘Y .:MAP\)8h-(sk^7YMx\SyN8p'KJ"c,zw3S7;SW~}o_-lܱsfM5؄'SS05슒QYm6^ )-1Е_{0O̅nx69WF=hّW9:벤\Kdz$ʖtZȢ5+jN3 EF|,zaB#9&tH´gy ]%~FAE,cqNmyW885Y;èJ}ۏߏ2N%`Cѩ$ûZ㴇9W\b-h Q51?~byĆg8vZvfNNqugN +bR}d|&w-[tqʚ%OjՔu!v l2 fQЪhplQռ)w3= :qOS0[WMVZrDvxNp<$hpF8؂k"t?SȜPBp''ϒ{^or踜 %7dI R|]xH1qC +r[ ׸7vX L % Y;Ipg#A#$LLTc8Yg>q6hUڏBB@a$( PټŚC`2IE?zeޜWI9-C7a5Z-NeVZa2osm~Cq';Pxd+NxU}8mR}"_+ta_nAR4B\ 2}+BE'gx #Ym|`ȼrb"`Mado gĸ0G*=KJ-XHTNya5FѡBQȅdB6XJB_UlukW,W! ^< `!FQ>ˮrb=S671Һ:K"i0W# Za/u&U]f? rſPA\ 3p[4N{G8HFѿonVUkFVƗoÈKLQ$'pViTYuMBg®dԾ7ƦFY3S+4H\.۪VWTls8[?+RNeRo1ɕǧ(3RQI.XK(6 ddN;ʪhʌs5(3h!T3iɬ!cқ5^ f<%#tJ+a;~M r0 RxΡ.i( ֗>l}vmJRw_yK!-/~ff3ϩn"ANJ@iJ5>?T>Rp^O,ƴpUzSYEM^G+s}Y분ɹG G|'`m3GHU Ogv6JF}|6mrPoZ}%,`ȷ2l-ӚȖ +& x8FR eV9 8q]WfpKN=pkw` j:GbuZ H,gpuD"ו Ӝ*[5QZYZ^!sLJ.4m\zi:|Ti 86<| F}G*0Un央/L"(_%IqJ"L:s. =Rj/u uu=Tx'`A>\yXRzK U[宓OH(Ui OY)#SuyuQ:MDo5k<[ޚxzyx6V:Q3u-'bGxԹ5+,==6"a'k 4wNwNwNwNwN_ ˩Yl7 gR ;=dW39`f dFD >88 mKxf sR/f ^@Ipa`NqGt!0ӧ)<8|o5}Ovs}$rLEi":k%vCU<U$^z 50yW]?<mV$xLJ>&TsBjˌef^3N]q@c @dqne8*=~уG!(R0:Id@ܘ$VFɕ9yae+AwoW29f嚷+ !pM='<SlVxQًSdO 7`/ m{ ߦͬq)Jh)(P21u~*tJ0iRKy% .;E2 U ϟIl&q}-[0P2%@m K ptZ<*[ hN9ߚ 4nB^Ŵj*#Q'Nrlt[*X:,| XIKׇ/i[˴[#=t1@* Q;ڛ*x\o(ȤW&qPH' rjr܆+y}Kp$-4͉[;nim7w_\)*oPu^G!kʘzKIb%A-">-O(8S 4nl`X*{ȥ3-jzrH]ٞr*O59nZ v[m׺]R@u܄7(~<&s8ʏ$ھ[,gOO P>%B{ 2)AOA$[5GZ N}[ͦ~,M`%#5y62j+b)H0@mMA cL^* ).Pnզ7c bGyk M%{^]ݠ׸ƝZLi^ςDTY˘u!c 9)jATLY4&eЋ:}eFF`>*I+?4)ojU}{#7XPi|n>x/G_'M'xf3?)B/`~L\ VTn$ǽ'y#B#K5ңKgINR=?kͰ拭Cu^/L??~ NWqV^cqznaΖR]NHd)01(N+z\ 6jJ>F@H >[ JuD4׏~}3fY} E.Qk)SDaFPҦW]=\.B8h 4Y.<ѱ~;9W]SPZnR;>4/d||}-W2C ,Mo3Y/b>  S]2(+ ŻDqԛ tLvf(;Ig6oLS4 L"D?/nhv0MVp?OS`bDZiCq A ř~{S,.QOKwmRV|bOS΍e>6=~=^~,-?5>mo׏^5<9U_$ aڂvժ}{z !w. ],xY3 }L .>\]A v ˥~ -i0oxϲ'|4Est9"̃EOFп,ܫȍEyWe-BixnoWW!?*62L?Ŷ?P!5Yvߐ߬oPM'=p(yQ+xtBņeRvc.npZ)6 Yȁ>)Eίpe眊K520VOm,$5; pn3sn7nJfjz V#êgI bʫ5&䗏W M.5٦ < @3|!<1xGP'! 3}u-@NJ}&llBf(JrՊ;аd@1p”(#rEYK7,]qB=TQ-n!>'-)z)B>KmE1l g8;5c]z!'b;i5d̒/O]PeX^Zӫ6ʇ+sxxH5,Zg%פK=9<-muKz/qiœ˳˸ݾB#6]Ǖg҇,*TřQܧ4stޒ  's3tF7w@]q4}K2FꔃrEnP^Nd8+J4QX_uE | whⵏ{ӫy3lƴ "B?KH R[4YyybfX/8#i操8oM$NiXCߢg 饸@1~҅{zFk_e\o1Pܣ#1^r!W0Q-b8tm!LV$FPG&1E۬T;yҧǏe^0L5R2gm泦xgy@9)o@G5A)~ qׂS;T.iaI}(-ՔrU5Ӑ.u~*-;L <$ P=Ŏ"UȒO`02ƢxnS CZ%E!A)W (ՖFSEm[]zqx ;QG_kyyVlJˤ 'q#䩕<ƝjʐEvMz1xvO- 2S`g4 ;O:eΣz]g<+1NPUh;4QHq>@P'QCƉs>%T|n#6 5v,?4E.LmdAnw/?6,g =,U ŇXv#$E3@x ϡITŏTO'vJ&1(pPg2..6p9x R 0Z& MaU 9Q3uJBKFfI Uu'6NZZڣnއE$ 2Pv>J$jWR'"yL&>-X~\OK( zZ V tr4z 4'#uiCְNB9BZS:8n杅8dmޅ\fZH"bHgč< sFuNSs| V_!0< ,iAad UЩ !-Zfݍ,M' c-,N":_` q☗zhAɈ9%YwE7zxx_FmiҚĪ=Qd4srDЍ;adھʪ󛝀J>&/|}#`eYo꟦[ma5?WqX_p+M \| Obhݔ,E-헔Ry0IZ2"&A &}(yXup0w`OD>ט?~0 dM 6o29qKM>;U&7ŭ!na=G&]']CK_JrLA-|t'y>ArqwtRZ\ D-RW~\a݉m1,] `wB n ַ }ɧ#? 3ΰ&4[Q`Q?8'i; =hֱōdu~(8u~'e)̪SV@!IQqmmx܁2m7^Sȧ./ #7V_yVF5 3[q fѺZWՎ]?tJ3 3,|oi-L_ R2>? KEŰv UCwgЋ2J-'2[IKy (;Ygu&oF*Cָ9?|5Gmτg.{mv@5zL1pӽ[ƛ0q$/ı\1.b OZŸcN^n WpE;NOQr W-5VIQEvf $aҰ*c$,[ۓ 0ją YYq/b 3 J.*Xä{0.7`̈Ek)fp0W_×X^;7L;%pd&",+pWdp=^Z|jbR5o6(Mxi~d0:ZUvS.,ㄫCM׸,Z-bŖ΀{ $sF2|,f6:][F'LW~ 1-W2g@4kdXJ̔P|B# )5Ly#/[H/F](Bo!JaryW&fKeq['OlNwG~u9`@zWmMyXcL&9XĪ+f"ikya<fj m`ƦDFpK(@>2iki趜fBT9u"fS (4[wWArSe/{VRe/m-^/e/#.^ e//^RcqŌsNys;;8N^@HRIUq3F\ W %+Gș_0EUMwi` 7ˢБI5Tt[ިbVkSG4;.,uN]n_>,em]iU?h,Q:j&809;Ӊz<'43Єd3 \8;7U&t'ǦeYiU>ԩI-LY~vxL!Yx#v_oo-pYnQrU -6\20"iI>yNJ[Tm:f2ggԭYlfcEbiw& CNhݴ@U{nK"wW=:%Y+z3=YuKVS{6z-wEc]7Uyʜ<_6F:Ʃe^vEkI75ѸM?}OJ[:åGw1^|kinLihԝ|v$> !qyԷụx|^l L9:#Kbtόi/ Lq:*FT \px?xԩa,,\ޔ&at$ZW1tFaIh3b(wc*V퍌(Phטhe{t!ZbDT("qØUxqmLs8v"Qڲ˜n>}yώnb؞ Dڊ 1o0)с|4 MLh٣o9T٢Uթaf›+Px3z":3r)#u0Ŏä%Rp,PvxUySx1sW7})8:PL=JVrbMpQnPA$f% amF'MGCb ??uh4 F苷q%Y?Y\} %إC/ʣIPD2i2L ddn<cR D(/M40 QMhI2c#rMo¤[l2UewCիvګmF$:Y4D]C(skbP;(bfK0|}2%MbJ 3(DHD,i24:̑We1l]J6[C\ 9)me6)ϡ6K NVAH}Ifih&e#%lFm;4ͣuto# -g9_e!-nB{~?Z*S+tM3ÈNiÕ j^791@t?\)~O;[P$6_^6l-'jQ2`8;~ r?yo׵.?\y@,/;⻭aK .?\]\%w|?a=O@0cX=~ t u/ e(q+_Q^P'ǾG3fE' x^gT,qMbmF7kS>v$p4>| Wl:l:uj-ڵ܀g^i H@,XPe Ө LGW+'h 3CP#-`PQ{vQR4,!'R\$"'wGYtṢNhS`|8ox؏iO ɡtOFH{e֔s T i7KgE4z?ȃܗnlmݽ#aD/~_coH9-A$Yʻ(Uoau1zĹQm)v {?=Ɠhl՝10=qo/&w#t~FRZ$~*v7T@JzZ r,u~OzJT=h?էQ|IxxI]0r7XKQ G)MI}u 9=X8w}x* sdu1,1ìZ5Yqʛ7'PB޼i=xS67])k4HAj`E(Q3% fJ5SL7O<}:+u(L%Z ӓ3GyD!* 0++в$Lõो?j<ΣbĚk7lix_fsce@yA֧ufRyrfWm(:q,ʶmRzT*"K@.S&4y[{ӳۜT_%mxGFدZ*Ht/f>=X@˳o @JR;ԭL&]ao>Bۖ|z\٬Bnzk !~džgb)DuK'\^ )JytYo<8Qyv$D}Xs([9Rު`åGå.Whv<եK?xx#m\yG^}4v+ﮒV aIuA+/:ݑ2~6&]RnFLZHZ;Ahإ3*e8HOHe3gjp([հD ϚfmђhRGɢ$398Ϙo|K߱DJ:LΒ>^ržn RC!wmId"UdnV ]N5b{ͯk|͹`M\k ܕvWhc -j ˮDةT" ]jssue?,?*>)q .9,O]u ۚ+:=>_ =Z#~ۧrֵ ZR=[U ;fZ>0'1j~W+ˤMnlyƯ}syڷzaIݒf * !^TVvV*pfuڙN^/\tX =(Xil_L،LzIQOXQ*7[)&p#+8QA+OLkb "Vz}bxSGM5nx~[饢+kU)j@S5PFST^Z{GhXle-A8əBcsxA HJ-i-1|IݥyJtWmvq.J0N9w'҄>L|7,2: vPQ2993]%g{(| F1-_IJO̽l"0W-By-J9^deN韼/Fs{,/~UN޿KwX\^]\Y]&j|5X+Nn+0\;pʏ؍QOQDȸ/kzAQcv 1M2/ S~h&WDK"Se܏QO۽#؅(9Ji,;g%VxJ Ay~T|y U2ꚍ`Mʓ<ma_n lY&~Y?8X=Gdؘrlb xoGx41[GCb@bactЀŴ[UݵYQdbo0;qT>m솆?Jc]?28 ̀bN. bż8#źxκŰ4.^70Ye>FLCC 2Fz$+&ÒG^H~'i ,Z\\Eْ?Wt(̺'b9X]/Joyj,=qK3X O oDU @MoḀ1Y:Wd]yН'aĆ1`gUztͦM, |Q#J (Gԁf@2IJڱKXO \ZT^jJw-ߤ1Hj BN>DiǗ^q :HnVAXcm!+%<;a^h Ku$a*]s)*uQQ}\v+5y<1n3Z\FiuiF_m1B%vb,.a?ۍ0"~&s[ ׷_7Ei]<ՙu#nZ`CJ!Fx :/.'2ҝ,áZ"n@2O=p&7qg,ca1c**RdiȾg ~axD+UJa{jɟ#+S8d&ET|@d!XbC* 1rIt N'}/kq搜_9w4XDID$zh] %+s/~\IEs-,kj5[R3ܞՂMN$jhY 푅s@eLk }`5nurmM8q=Du32RGj>0pgF5Rs'{dϗy'5٘%[^ H9:?BOUcFs ~OԻA_W(4Wp!+HRl.F$\PYWJMT< ,ȞvluPKPuQB0vnMDĕ,;0A$dh HFY3b({B Q_77"Ǘ%3Q[_YPFLmѻm'!EQp3$O8PɭV[$l'4Be2>?ZeWgƕNߓZ,wAc=}FdGk" 4m_~3P[}vX:MD_MiAG2#q%$wQ>G.@~sê9CMY {6#`/ΐugB=ƨ DHЇ'ώQHP @ ]gL#m=4q;/H]p:GA7ee¦hYn+;87ΣN5 Vu.#L @|ˇ #4N7\v O.m\x9//iX<ЎnO>1vx"4CF%A'(,;LN2S@U.t%H @o9o꜎6^ ӲC_/K ^uuʷ\.b"1ꠅC`VwE~ \ߘ'}^A0v2(ݗ$} ew܉.ʱ2, v{y2CvUaw:Ҡ5MWVaLim.bz>fH)5XHh9bat8I&K.>i^mýG4|?yk)*"w7u-("yDݍכ[/# 7u%T3PS|hA}uĻЪh=ַP+ ?n2LAs5ͷ ? GG i~# 6v{ \Y$ `oAsQ~~xu)^my?om4{Dׇ:4rNm $A^nvvk/@tjoAv@.HN нu$ƑU<;8:+v/^6w7B@l6k0H[XfkWzA˯80aFPlC[,~%}|$|xp=?܄^|ƌ \Wћ4aap4 X/bϳ~U )2 Mws/bgYs;7F@MI}`Q`8PV@UBdNØ}S;_9-r's6hm)@Swx(#TfVEM揣2$K&*:W~]4HDTXro+]1Ȇ\BֺJ{5nK^O0W*AIDĔ0 唀ުL m5{x9꟰@CYE' eq+wE].# T ɺ!o0TxRǀN:<J UT9q!ٰbɝdӔ7m@O1b`Hޣ?,rwq@$tFR CDzlxT'XQ/tEC@q}"5%c¢e@|hh 3 Z xI17$A>Ĝ4eK > jn GY)u .Xyþ^D !`x9bb/è+^%*7# Ù3pAKC! +8p$׍}Ի"o.WJ!DI%؞| e_ҞJoN1'XN4[+[*$4!4oHx1-1dه,6$s ud(pb,Oy@L r4bhjo=6=x`/?~?Ƹt$*]s !Y|!٣({뿝jM;AQ CB"VQlg'qc_v_rbX=6V-SE=+q݂JA %Z'q #Oaɐ|:eSj!}dRlZTxwoV T=(Y#}rcOhPc2>!a$IE$AŲ^uJ!P&y-,c*Ad7NMIgdx\ E=<1QR{m @5R4&[T?jD(ɠK&ץzNj\ץ.\ $`c yekGgq=+ܲewg@( bG^?Mjik"oM9pvj:jض(eĝn UJWEW:eW9Ex ?=p8+ҁ(eX9Dj~3k2%o14W0j@R9.--5Ɂz?󧡛O-9IZK>S3jS4V캊K݂\¬%@Ú&B@hS}*㴆M .9[I%M/|1TSgzI5W\4~3gO`oʹ;焂^p_)E?;k|Ϸ[iSqJ'ołh(9qlj=M-͋|fo`~0ߦ֌e!% l4rmL?^zˏ>eY[#E1/W-#"C>V޺ˍ5?d ZVrzʙ$gCrV _*=o{ʁ +q{6X&(Kie<F?tN;?<X;ؗjxOpR<;W+CQH̭biiuie9xq*h)H!IWEBT) 4j`]PLጺg2J#Qd P@z(KyxsY* @!iRY^wJSUFؕ^jTnhT:+,K@ U fDjWݹѼ=[2RЭ=ۀRZr1L˴fשUZf` ҡ[B׬qtyʞQ$w%k;E?}A`pVwz^WIg7^ŮU{Z1*nl]N/N>jfDtN&C;`b[m~-7H 2VVDG%ڢ |^.v=o4ثW,?Ww3lfT,D1T9Ak ت2$۴ˈSH-7]~g덽NϮT̪mW0favM.7^m,fV}^5+Horxw*]Яw;{UZY; շ^Wkr==,]pF]nf‚=[ݳzՎױkΑF|l-50R:wV"[5&mNӨw*VaYna&0Hp^k,&[FZ+}|J[o˖aBYl{۱UzpUZ]S_\4vܨtwLݭco{+ffӬD0{ ɹ[ڝrר j8UVV^t2Vy-6+jƫom‘Ykaz+׀y,=FS특IC( Vg4"( {R =O&%D8FJ&-=r,b}iy]«9c"{?mnD_*wN)7Dև1'j&/-iٟyFZ`6:DlZ9e kMӡcOgK .O=Aہ!m oᛖюHٹ~oӏ>meKN F,摆v=.T.=}"j|2 b%Y#c`<"E0◀Δǁ^b4_tF';O&Յ"H‚%Ma_\JDq8<[%3aRĭ+q@Q 760{'RU܌{a^w[+Pnӑ"]kR8&-rUdt2G[H.on(6VD2a|11L耷 F;T [#g0JBσd@ZSr=UW3i NC\j=,t >x0sk172,NށiΓ|q">,AߘC=Q~nH/ X}HCE'fq8qCfd))EhxK1 | xPLl#'nءa,gyT4Ng$̜DƼ) QҰx v>8)=oe,jkhC?a6+XEX6CKӶFriOՃMc+xbs75%/0Q TQ3: M c l G]v􈶊F%Q$92:iTsf_&/G]H3xƒf!v:%F_-i;ԆU612xaL(=Z[u2fcIygՒ9Isͥ`g\٪+ !n` P1(QoctPVɫECa[f*QM) ^T[4 CmD (g|g1(큐+[H4@1ٶ E}K)\gEѨEmD3s=a-q76d,_ԛ6T2s%ڗ`yD%%-.04w^8)C)dpx-H5ֈn kYr0 '3mvM' lwzR3[\ޭ4* erg#w8- LtA#G ᶹk"ۼ4Hc"0әJ)Èa ݳ.=q*h<UzN"?> ZG3dGtAvX]vLU2 #)('ouXAqfP<T{woMFo;-u6y/*qt%G/<E{VU104l}HsgCtql|/Y'@c#Xdԛ 5D8z{Yӗ E|{_C-^t_A\?B~y4A|f\Ⲁ-ܣL a2XH˲ߔwr6*edy[HM5||G;NQA eᜅtCv0ܿ XS;.˝B}AE[|lɻKllQ̌bME(''(/þ}:N1z(ٹ0Uf$P=F|k\tkX9/lG&+u }[\1< /y| =rO'kR#),t L#"n|^ExWx޼^. 䇈P{/Mz[:Pʮ?jkcgP0?lr`!1,JZ~<<;>ғ3Dto`$ϫ=kelT4G§+ k-ljt""+ fLG6Xy4RKO^W~*d?{p UAgùOLt*cK !m6lBO\tv'@e2875#,ZI9.K)8NY+axpd:U4D/%0:|YYq `Y/g7<鎅@X 1 yt?c X!{O3+ %),Q6WĆI#XQ_م *(~rMMb8 w(N"qt/" 镻. /Rՠs<@T‚=IM-uZF#%[Iǟ tN lL7t#v  49g-^RI'l6?K^ETIZ8 I+va꾳GtvdxU7;Aԭt\''(+g4Kn(5ЋDj$[ꩇḎ;4\P4+GחBei~Zl z% tͪ]1Г q3$Dr7ٜ^, +y*n\kuqr4aO̲a4%e"gK?@-H FI9ZB j([`.gJxpZZ@v50U{LGKv;K1R/8ғ^TT=g:h(yw4/Cz,{:i^14"dҝќdP(/$>|%b^%OjR~bCٷd x9$4mΛ=E7xLЭD&s1V3ty1xA@<h&y?2|n"\s1ҩ ׬Cc&!(p|/`8dL9 C4󦄂 InB?҃F2Ϥ.9Gg a,UJƗ9J2}-MʺSǜ"g(Vݩ;Bσwx WB ZJknn۶1'9M)т|KE7h#1oǂܕ}Ўg4GJ;e{D0*XNBr׈3+[][!gy%WǶϥ]#9]gZeFܚ*Yj*5P]:?UB1\*^*FHh $YpNq$^!)F0b͐Y4aP^ATE'RUs\ !,x\b$GbWg٫WޔFgV`r*RCq%D2*Q?xt wvAQ2YjI~WHmMfi. E|#J525$#S+)TS#f-: >Xd :VKNkbDV?+f̾vsFvAZJo0k?4 6) i9 ϶AT*VFG;z-ۢQUw`WfX: RvΑn6Vf35-y@3cwk=7u;3o3pր&Ë#4tVl-W 2D+qhJ)[\AshkQan"In;4g*eE>Y(y$ BdvIrF?vB[3CTНl^hU(9)YCu[Ch%B:b/\gp\(9E'ʪCjV:J\?uo#K!% UYAH ;aG}HP%z'l`aZ AW1uT} BG=Zxo-6]a{gvwK+٠qnBFGU,'z+VC̱xk/jv(ܑGfKIWg;gLdih t7xBǍ2;4sRKFzqo~a/ĭ9[4."U: H:,7# SXG:DSc5Y5u4 XWG/Wȫv/"35Ie$Ke˚-붦lM2`lk]}L c3k:,>Lcm{]\~":,(ǎ%uE,sZvT ^8-~ɏԅcE xRx8k4ue5gCSK87hhc0=+0U0OU!HC}H 3d-bP^R_0΍o1dB5FǶLڵz ߳R*}L ׅ۶һKj.(̇QS=npEG#s ܝoh5mܝ9'8L ?^>_;h{Cw:n-Z,iJEOF7(hRI",kPԘ^9w>vƞR `b@peq?R6DuSJe#/77ty:qAFs{T  ENgT'r{Zs H:$J|@MX¡Ae4TiԞOiJM&ӶӋf%`!msSOZl=]% 7 N[(E.0HI$\Q)>`OpU\[uB.rf乿롥]h ֱ\seg| Vnl.QZF)'9,Clt&6m$JFð(Js6n+.y!b4H ;DkNlyֵ,]a˃8ԝM0PbQbxjY Z'K4Ae] b09Z];HC -1W >]&cJ?b&s&"}w&)tx(㱀Q.tvYJ%Xy2t'c=`u|pFկcXڊEtYޞeҬ;V}(FtLBײXY+bޖ1B+E  'Bcl28EK趭g+R[\ro}t\ٓX@dvO) tN8;@ 8ył8"?ph9.r\f(m>AgBEg05ǘb3a8G f[~0:!!8GNҬNJXwi5="/Ohx`oy8 Dt_Iv47ϒ-B'5~|;EG\L$y״g̈GP-AOP5uDҎ@m` |u(]3}PB!ěp9qc5jW 0ҠGI[zΐG֤p5LCPw5( ^& ;MpM>[bO18𰚍:$Lb _!sςu(ԍ(7:6BD(d yHʱ2ςC#Y]N(X?ò ۸3⁜ɧl5r9XOQR&p$,"5d\` IGw%W'֪5]KEX1m )Wڧ[E/$ؿQ_O(&Ϊ o41/`8`c++ ckUG 9SZpaW̭U<)R9tT0B|IXn_=ev8bnַ|Uo\3p'< yi<^\: +0~uA˘2irUt֡%݆w WU78`pZZzSTYSS/5nW? #s躙c[tFAFG9OW,9w(:̠ؤ7a|3ObLc'2)@ rvjiOaV#ƹ*&x+zH3MXQμ9d[t$;(;kb8;Xo➈//9e5+/\v5EAr@4>"C%3U|B{eqٲ*Wҏ_oã4|AXN+n"\X 06N`ݡ@Xlױ:^@o{My[Za;"C\$ST·ۑ ؒzJIwg'1ʤ5Z ѦaC?Z|RA,jSp/, w7" NY].9jstkObLlm/S6|I닎i2d<"?UlԚ9s۲cCA8(Y%w #+LhV'6z3yZkӊJn;hpe>1sڬ@X2."(#dUgP^7pp)*P% ՆoN!cܠM~D>`|:g_ z'hT4Nv({e͊xL1g8$hqX^v۬T}AhVފ'u40[!(%]2ڕ=փ(JZ)JElS+>N.~F.@ Iώc gtPhabۣ~*P&Zi&V^E8Ue@}aTԱJHPy]%aS؄dn-R\a0ro1p:`uK=- v!~y}+/K|"&W[1o݌`Yg1nEojT4܌C0;y?jpӌn`k _tP 5Y"zQMʬj٘kwlvN)H[^64yƱʼnzHPݽ(. Ź~7,~+Hqy0?qEh/,]qzAzCiuMZlg72uYK"9:GikݙX#(Kqytߣ8D*`]“ hx={ cp%^෻;Gte%(;ձ+)xXKcɸ`@jP]IW햂0 Ҙ/ߛل7/J㳹a>B e"oZx/"0xao޹^("@]Ï9Pлwad 9e2Ru@^Znm iO&N 7%XSxW6ckP}+OC H,tZad2|ӳ?K:ʤp<'nMnԛz,/Q`3:GCm"dD_"5eM2ſqk 7_uV2_GS9UI&V8eݥS"h4QW3Ϧ;¸[$#>5H"Rw0zla kPmyvqc<sXݣ@7:Hp=i)jͦ.NW3qG#cS3S:#ie.3չ;ebySţ١}P~~LϹ݁UoPp"I7X:}rWs.Ib/=a~l$ wL6oRKvԩ@LxHg;D [^Ľ%vԅR? - 'MjcRT/ 'X+mt&ꅑ<뱳M2K۾4LqG{cŬPuV["d&eR:9oR("sg|9GQ E>n.`J8G!O[A R]'Ԣt 1uVSbdXj 3ɒ"xe5C1r@U@^K+eZ7$jR۞;|'khf70D):|'16Ki!h_ެ+){w=!{F9/O{˅'KXqz3T5CpIȕ\ {0B%xױ&:5rk<9BB "cQEfۨΆanw(Vuw$PIw׭>GT!@wg#UL{5fkzPYϸoJIZ *f8nLI4#0IG-GZObRp޺?4auFʩ+L'v5Vd$Y rHI dGAN=!0ԕSKÊ7}`W>k-R9F"N^V-kn8CϔlIw)Db8j˜a~TaNڄKqItRS=;*!,wI'[TQq+1jeUöLD C}JO(#b3(5_WK7;**]G *Lw25w:d. aTLЯl\yo%t(aVG3Λѻq)(ⸯrS?"*,y+un+i@ZhmSWF?Ipt~E5̜j4t<%m9%2O^r|csyo5mz,.(2P; gݻ:Ce #M/K$UlN\U8)tC8<5%omUlǝ8ߗ"8jCkL6o"yf[~ TK^_?Qy /"*>mX{E*xPjkފh``qdJm$kScb3h?uHYG=G*o K\ِ 1vW>@~]u7Meu-y\ԟd STZC.r!Eo0,X*:粠 !,x~(w&"dg r&ݜKR}h"zc$2ӗW cr.6k㮫, t!T#>AUtHzFIr(cĖP8a6u'j+ Yȷ֔+`z["Kx 5_gě"]Z]*ޢ:'0YN 3xD>6*2ÚrT;4[3{U9ȘȟŽjP$VdD0+d!#4bm?p6:mwnGs8kԻ_dq?)2k~CR5~A,dݑ3#}{=wܨedcS]^:om]/ɷ)0yv'G,x2AsG,+&  ka׏d|@%;7@;jdc/X:#j /.~MtAǒ1Hug8]LVj3qtDc0 *f YC'|z /W%%*њo'Vbj kNNsFFh4`!y?3sm33y$B;VMU YW2w9 X=%|RW$~Y0)zWeyp44%ڥ5C+F ̯xq,989>l^r/>e>r=4R{d.*UzzTHG^.vD{1t2}Todțk|1Q.Rv&\񪻢^/h Sˀp%D[4kCi{LzcqLPmC>tRyu~64j5hܨ7FR5leV2脟=q́[O_\VfH Äuz?$Eޔ21$'OxYRnlS ,3HMW䶇&p.^OHjV)1t-],7S Xւ ?R32(Cb*&Q]xFAR0sX*!PF fBxH<=t2& )^%l#]К¶^04]l]>g'g48o.[Ehꀚ*`\.Cmأ74FǧW7Wg0><;h őF~_.poW8WLJW xuvq~89uz؂es| Ǽ7qT`p1"/a/9|Wǽ{r.wƎYejceQ6LØ}%3 X}2,/WA۾>NfvFV@# j5#iؚL+x@%RR=uӁ;x~YƸ@y HCpNz^& ~ ő|/ogOxJbC+xE] * }ch>!z;??I_KLP!p/Jpw 0.Ycy)+ zgcYQ%c .eiqv0Ʒb>ʆŻMd@?|1r73-r)"} 0&r6?JYz}~{Hv"F6c[pI,]G5JaG\ ҼL:5s8(#/ kDvՃ1w0eoY;==2 |(\i#i/aP"xV&!,mXn4Y6Ӿ#i+Aߧ,$@l&6׏o\0aMEucӅ m1G;& l6 a]JE]B'7Pkی8ҦGQ$ WCn{"':?~BOyY%-gP*JZ҉20ؒ5Lh6഍2m6r~2mʸO|񓅒z)H}-9fRKlv؅ln}و Z:lzU(?誼8;9yypJVKR _p4tu:w"Vj7(=xsu;:ӳi6 bZ~~\MWtMjj,t i?@ ,°~Gy5}I_u#{Qq b(| :{l$2EWPQ(fK՜TGeѩh!&B;Dd )J>гD!{!} z!~UYyd-8R"+Y[Ca \D% ItsbGWoN?'~AgaS>8f$?,8*sԕ孎/|K&lG9PDFLQ],NRZd<<JC;{k sa@P->L(|= 8 9$F%ky] (,+U{G5ZAXY+QbPpFT` k߁N rg3TqB^[go[ȎO2Dđg`4uHboqCS3VOA KlYnhY[2  .!uA\04l[z3XrTB 9,h!`OAG^ML!ѯ᳦ 62H;lg̊RNy҇s\("ngGURJe2`ć#QgK802\C. ] jEs.o{w2 ^l]\A'-ޜ?B!1g@9>e'/W짃7-= /ZXn.q NPǃ (ykbt9C낝yyurvp}O%{9jDKŶd2x]Y[i2Fۨ @͋?_?khe]=&Ř.?)KUPKXLѠFh;Q^|*U ׼1b2r6k\co۬ȯW50ӏ5Aۨ)P<AWTLl$y-yLjG;]]aEC(J()?b q]Ժh8\\9}7r~pyBsLQ@#@AEjHpR {!ܶ>?^្ @Bu#@-TEgSq0ڨ|C fVgqfQa7DBhGr8؃b|@%.FA wzPLf@X28t^gV-ɻYf/7ovk-F#`;x޿*"s8*eK0Si *wy'yAWicQi_moZ-6Ol,7K/f>UV6J;;dMZjz qQ>Z v>Owȥ1y3*2qKMF3 K? /,$g$>p~ֲ|HQpƃLy2 ֝Ҡx G[R*~r-O=iLG1 o}}m$Eϰ.g(_̣4DUWA`=f`͞)hUHze0 d],.@_TZʦ"Qǻ"ȇ[3||l6џ B*7`(r]~{mo;mkr/ی\IH2?2#nk '1>_b C5%L"k gR;,XZ\7].,[ HYyH Џq;\Q{4Yk<2Kws;候9>tvOEoE~d[MJU"Ђ)gpL!^,lHXdtE\<^,׬4^aFk[)VidZZսM!i5I7W293, LNѨ 2O(8-C``:4y=^vNvS֩ UDg]b*%ZfWhhDC _~u|j+tud+LwHK$KxLR_m__JͦzsƆRYe<fR(gW!<^i9T#3a= ,Gss#+"z)mF<}GY}룼AdEL(ۅU;JO_%?FOdZ]..ƂOjA_5YO&ӓ斨K챒LPQQ߹-=XnJ̕LKD&+{`HO~tq>E7QrX<7|@kXjvXA/G(x"NoSوAӯLloD٨&Za96[Q;6PoB#aB%p3kóM,]}.Fѝ xI,"d4нNP`r$?3ӳ J0/u8Au?LEfDngso CFq9`R{ WN; lDNW`t2`1Fyw(?*3 lSeW'l%::`X:x@(%V~ E_5r;Ө1أ@g~03`w88q0u z^&#.VO#"r/Ohg֚L%n!h7}B!bXHi| ^(ylM|88%G& egb|cbC1Ş/,7'y4k02?3g)U_\4r |Q!Ky8T+9yۜ)NFj;NpI^21[cw-49zyy䀅Y=vZ2~Řt2mlc[\*Jif׹%BAS0mR.1_AQS)EV.WǧAjq U3>z0}\| XAz~rGT!Z֌&=:sliӬ8^`jR.%PB@DaxhX=l_|ǜՇ(iOwc *P|{aia:zZsg GއQ#Оᷛn<,7 ? BojA] jE$ '8Ȍ1{D: cRhsAm)a˻EV)2԰ֿIN7 +֠F 1w#2;޳|`ܦ[+Sz/w\w`[v(*dVDs|'f"^*;nC0<,iBt'hy`9'al9OB䗜fQ#?NZs-Ϗ'Q }(d /.wp)F_ 7ߗ!"FH-/$$0y3O)xw\X));u m:Luz&PÃ=-%PnA"$;DraDgBfZIM_]uUhhBDAh2 gVd֭=, ji3N" Ry 1NPHe ongᘄb0ii*i`rǕ<YS,GI"Hub֭ >&3eHg| Wk}OcOI_QF}! 4rkǬ>V;OiR{,69[ygD[J%qo'iFlF=Ôsy}䡍a{J-KRLyh! pd^IkE\9AV9&oj@ 9tӌ)/N. FqPRx#폎%r<;(8dg׹I&%w,#;HP/TjOi  _p_u ~y٨o*Ev sGp)ѥZ@yS5 ˛K\ ;Gt8ڎ# w+Z1]-ϋ*N3_zHȗ7,b+  dr|[gFu{J וR)aYҍeiQ ~Ƃ\pzQ^m>ӛc0y5 >Q̩b[c8^}Zh)^E^`ճzSߪ5뵍M'H;S=aކqnm#]K{FKeĩ(}́ZK]|yuxS緇="%A)mSR75%zٞcl1{H7[lvs( O}ƬoݘƍY60nʕy4wo*VexS1J<0: oF7jV1 pXjVR[N'Ƹdy_k,c>pHO7jR0ei zS6ava(U(SQj0Us 3#lVoM`MlP)DzY#zsQ} X f׿!V8YNǶgX8O37fTiĺJ@Z .߫qBNsD,Gaᡓ# i#k\)J%(tAkfΓ w8Z(0RԺ/-+,հok $O! `ʟɋi8E|hAQEd\v-#M/Ad2K$LӤd,4x+芌ybSfLƩg\8!0ZXyt[*, BB>7~yCJbT5(08|sq:j_n]^>Լ |"..Xc8sNv͉zgw35qKڎܑ` knZ6 c6 c c"ЋiQx9HD4Aر:u9]k䎜.gq3cÉ؛[id@nn~F{|w>};.W˻p7+{ͲQ*9c'~Ek9J7yRɞvCĶj-+F֟WlBh_*=BdIެ Cނ)trwɆzʵR~\)nkEŮ^짃.І8;>xEnL\0MPX]OHXD:E9&bP=#N/eT$ K7 _,">RB:Lʸm9楃 AHg)-L6O,S\8Sa,oy/Z]ʐrcDGU"z=HIӿ9|[ ه!RT;Rjiڡ L"̓ON\^j`z䔦NVP ` b)0qZɓSzM>1==6"_pg Jèloi/ xTɰݟFԓ|iD&<:wQ~u*)Kݵf[𕭱#hNnbnlO2_BoOTX0hls#"!/lT;Ē= ]N[`ι$Y!jI!]\-&_umRਪ 2>͝ʣ+㩺89'Z$C/}g¼Ҁ54+8=8 -8-x4!rqƭ:Xw`/vq,Mh$١;zg?xI+4\{Tp6.P10ŵq _]0~E}X昂+xsh BXg61X]tB0{?y@Eumuh&e}Pd':~(71^ft$E{.7~Atf f؝vx1!\_VNh 3.1&_DҺO3@8L0}g!oD0+3'RFBDh| O6O= Jp7A| qrH'- wSr62_I*3_?smMӈMc}'SX~Wo]y (*wei2 xiGgl^W9 H,'2llmKo=ѳo{9m olYwe=Ee( ʣL+_G2 O,|TMy ~,(,f==5/m)l,0_@+Z9Jp$lj_j5khO3_SuUF9=:>l5 -v~qW֯  `0F iY˝>1lo7qǬky]g{ow2J %eӗM_6}eӗM_6}eӗM_6}eӗH_Ꮚ_i6}eӗM_6}eӗM_6}eӗM_6}eӗM_6}eӗM_6}eӗM_6}eӗM_6˦/l˦/l˦/lWyO,Oy|7Qo=ţi7ʳ*OO|d~rLff9Z 3Ue2"]aq;F}KD7P߇}{ڽ<{6t|m}Fmo >{ @Y/)k99 Z~'%3թ.֬o <Q.M!r=z$4<-:!umuIw՝ϟjh&<h_ÓQ2O68ՖDA Hd{:ϓf9ܩlpT*䌦TK(jc(k.YZ$MO݌twJ7ۻ2sqJQ#TEAmT*L~\K3vQ5j |a\ G?Eф,sx&MO쏌7@gI*Fk(K6f6^UhZ3t㺃xSB8J73t@~\muSBJ XuZqjtްh#y-!Mcre^ɂ%a"3,b"iVY^HB vfڬյ+)P7ЗBiXNi7e{r˶W]=o/e:h6c]=kb Th~ox Ѩ_40lxlY=|2[*@.{/Vd1hFãJ#!HD,-$-!$*Ll@=ltT ~[d1 ;u֝Qg3mO,dz{5Ϗ-A,Y(Ȋq@Pɚ뫊V .N Q>Ū,,A9ʋr TX](ILhj҇5kϋ4F1tud*kӬ4M8 a\6rXCeY6J=݅-]TWkEMc[\=du紨Ŋ*bjr4dmaڰ`Ӣ+GWO[/.4ReǮ?-&Gkqւ5gyK L#$W"(b 0SM~J4S3:8`8dN'FݸȦk?%pԏ۝;h pzl@JlbTdL 2Yn6Coa*Q t.M; !L>!:rHhk;%i؃mYKzJ|{!)NÐz]]{dZT?,T<T+jFQYP,lt>%<4P]NͱNkנ! a/ÃmIP9WNYL*Œ)8+R H3WT2{KmU} VG!5]A>-TiGc~cJ*3ޔ^[Ä,"U m,W`Ų97_`' *<(S ?Z6wZ WmY/E|_5lHfyXXGRpZ2iXn[fҸ X, gdMdRMZZ6wfǚTg4'`Oi꽷F*TO`1aնONWӃ_p0@B!=50(4/O^&6Kޘ̫̅|Nc^/gc+h[{ />L'M rҨk3gsl#ySwk]܉WM;O! zs6 91%KZ+N_Uؖb< rEsabjTΡKjaنם/uIFU~}v.Z2go\Fvw8L$7 0"J7qPo_-÷ ?aH׭ώ.C͍ ldnFsoU|/PdYEs]}@EE}=HoA ^i~EstHPwLhR DEt=3#iϓa'`lGڶ/f}N24e Ly֭=o`PYǾuFLy8Ԅag` 8lL{cvbyjH k]}ńڤ/5pz`Kw bٲKbp lOJX!G1>G/MW8:^V~Hj/gG/&VwoVz~x~X]󭙕_MJ XXc)m[F &J=Z7xtG|3ppPbwIU`6\8V{sd{^ړ[{* u; 0hm fX/uH'yČU0e-+oDS dm΂%1w4x``{d^ L&փSz.4HP}E*eZYR*(nѯ!ͼQ.9b&v;8u}0Pv Av`6,1|5=s{X6l&{辷{E^k_;a,ZDik7/|]J4_K PyhS q!Ν zLt Ba8xzѳ\I>@0ws=8muNq#!xcQТ: eD=nUb-HД+O.|RTbB1 i;F9,;QyEx8%x0lnMnnllEK1st:wnaE*הHTFԖjɘŒ4,S='¤ W4hywL,wYβP7{76E1"&J8[6MU-4W4 o+#`utϡ]U:0̓Q|2? $uINww[Aj&iU^H1b` @EH &hܽ]4ktkϯ7:RX0dET2  mXo#{YB oOlbHб68lRpl^cW(Mo}?g<\Q@DiƉ__$z[w(qEQu"|[yFWh3ԞN%h%Xmucgji|E:k,*Wtt`J#23RҢ 5&8 dy0vE}.0[gF: 3o p"]p.RB뮳e0_w;)He>m0u!ڤk@o\wFТCyS L79Hḳs `ҨEFms$pH0#WsN0Ho \+ h#Y !]Sew> VY+9 [[Kf5D?A,Q.eh6 -"|&V23& _a.~qd vb0׌:D;hOR7/$)LS3WAȲ1b`7Qo2C+#0^>p_#ť%/p \1K*LKY"UO%p$Km?]߸ })P+rAHA8*Q"x;gh˖=}˭~L"ćIXGl~ɔ:] -= SwGR,8*sC߻HuqjbVM1JKa/ WHՠ=҆ ELaDgZ }Oxv}TEOsk88< ٷzis6d-mu7к9Qt!Z{g䡺5y Н<܌'֨Ah&410oF MQ2nFOOQOn~ c?ǕeQ& ڝ٭20IlUJO4RKLM Vfx&6EΦ;M5ên*欫#82 1ֿo- ƷN;$/7NZn B>%]Pw&ÇZpfTc]^gO2.^bݲK汓֫+vu~v|xkmN QRQ`r:ELJ@h ?Z, ֪5r.ޜ72ufNa𣝁-GF_^T6Tţi&ZT gKf5,N*gյ7u?Y~(9@Z޻OyAA`h 84@ t;Pf͋  \uLjL R!A2Ć]N+ֹ\[ԒA+֔„ѵq$P,h 0S>eXՠFzՠ]Ϋûf5P]M#yVepUېmUJUBkttX#ig u=K˽FvJ,U#' 1{䎄fts;׶شRH]S=@V74\((l- Ch,n>xi>Gm:,|kIgg_բaV6S<zy@1&LMX2L,?2EL"O?k-ʛ@I҅ I3Z*}hF%~.5\_4,}5p\r~_~ (1)L .#8E)m+I1F5{4SB3L.i{փ+T3G z4p^,.uFO^0[i1i;͋pEdH"wOO!',Gqf[F*τw?8Έ7ANW\$92xypb[a5\9"37o ^'| Mt8YՔH@'6CEJ[4:S6u=MCi ʔznSl4Ng 3 ZX3XqYJ>"*s4ț]TK`T /n>B& Maj^~QgOJM Jz*P8D:zPYd[SW'`K%ՍcP>}{GgVk2}F-SLPe-0XPXrI+ FS!USPNl"_?S@c!WaŰKy,8=U2}ގvD%aF~E$z鵅[kYpSV;AP7;< JK|}-0R~n?dyVY6yGpZ\pVbE"B ( ;􊰀uⵏ_K)̱|⛸KJFBro|G[%f]}@F\E:܁1Ipsm#Zͳy6K ./oar-2.5.2/third_party/rpm/SOURCES/Sort-Naturally-1.02.tar.gz0000644000175000017500000002416511757171206021450 0ustar plbplbASort-Naturally-1.02.tar[iwG%H˼x%KrŒ%|cy8ES@Yn!l+}UvMfL8襖[wyR 9␻ڨ՛{Sϳ'O~;yQn'MȶO"2HZk$G>-?;<-=@~<}^?~.>b ͢hq(8t$拈4©"ϵvv9?ar.vvvEl!]93O>fMbP3j@w-{ŒϪ*Z7"T2{Tp^r}>gIc*d$PPeB9#m1pk*|HeF#V_ 23;{Zka=_O>m-EOeɳgM-h6'qIۚs皃FٷZیK*ʛX 1[Ѭ6گhpY?^ zsvJJux˫%<)|#lVo^OoFм78#lfJ0tNԦk7p+#1Ůn!?.s &"?'?*r}߱wRl,&cnr>Bv2ЦdΙA#&'|1 coܟbE ,w3y Q څG| d c[-,4OgEMs?EfUY;`\2Ӡ:pi0]=↻l<;e;zqJ<-6])xH~X %zo|dԶ@HH/%M^mѳg Y iY-4udl-$\tYqG o )|}9(i5:Fь%& WV\VH_ Z0 #nWF0O ؎l&`˜E:^k-&pc  }"b,^=Җ% $(wLJ}j2r뛌M ~zNy(Eu*3Q 65Nkph^kU20#,# co2#07nuxJƷ۔Uȣ?!:O熽Bm$thot).F0b\rl_UJ1$ҼZ̤+8RrS a54"V ~(sNZ6SR-_PA3bZWRwY~{dyv`>arx@dLM`d5U&ȰQax[``A֔ 9ix ,X;dmN,͓v8X!zezFF uC,~XymTcp|qzt):E?tYJ=wHxt_ r宷_p3|J"?Ul& ;:/u4cAxx 'V+@Ċ‡ГQ<ÔŠZ`qL )^ЭH|mIoL1\(ۺu Keֶ ryChVa|ۉNp!A$)=2k!l* R{KrV1+s'?@bNC`U9άbqilTtc=Vq`U#O9[~F@qVVrDOL\A, 9 4#;qiW߮ӌT+Co |aAtkYR u >^ږ@&y- NKJ.+F7Pd:J!Nc"]#/rFホ@|@giN K`_9`\(GR/ JcȫMF >F:`FIk^kEJ}Dlo-&͎@]F, :u+ηN-׶׫…d]HR4-L_Z18Ӳ z=OB^M TE~71˾TZsՇ`,2RבSx&h}> A 6:M\תYMbgpws7#Rlopg쿀lAqTi@bj]$tr7uwJx. ~ID< >pĻyNG=eɔ@98/fͪi +&&V6/A Zy[Fݱ BU/ -LO?pԢ2')\h)PBg,ɌKHۏNTVV)A h͈*UaKs f}|[c JP4‰đJЈOZ^ߥ>yFجVx |Ii5䪻Anw\).tu!'ڇbI!bph%y;K6C%Q Mub4O;t~UD;TbVByw9 VMV8v'rjG0&XHܨGzegh(gl+jFUaD\?!/L,EiPcHC&(jaR5i26 dsם1~voQ+XjBXfU(=/[y:̀UGFqDǐdiϫݝ$2?CS_b Hz4͝n>׾Yޟt31tCqh_c; e1C<5gGa+_1}PR>sjL@A0 \YtoRjmcW B7q^Tś#BVӰQIdfrt\T|KBd&*h}->G=F(8LjD<2,(!'!H[pdxD.2ث !Ƅ!GHp $[0@,WJPFɚr`fHzIW]JD&I|, NXDXFͳTpC7@pH ˢcL-$Z˴k|0cR&D>ig5mI:hT]m sAE:&0"w~y7n )LK3UffOC8e$U=gDaqd Q}K^i,{~z?r& `opҸz'i"f6lR%<XOM?KvN XS>ne:?7_zc> Ky^iLA%BEڜE֌M2NsX;d0 PC>5:n =xME^ܽVhdG'78~y;;`ID^e{i˗ϰ1ݪ5l'$f꠿(+3f3@Djr㴜n깥o/շO2DNefDRIODi*%+rlY1P+@P*+2 rPUqCdcAd'\Tes2fdtؑ{/g7ۿ5+v~Q\E~6}A5D!h@Q $_3!Gڡ F`0԰ gN˙'De=D$zf q+3K+v5.rP6nL?8GQMI7Nci׭[qu[n;9v] 9j,k*"Mly,?kf8솚C9oTPË, $]&'h}Q4VUV +/ U%!tiTJ9<KYLLج mh76\BQ-Z:~ޑ &N9Ae[N QUU *;ybEpl~^Ãnwڜ(8 ՌG! o궄\cr`vE3WDXqD$_߇3HTtt"<]J$MRNX1)/;`PD*Ax _HP5zLcH;vz-iîREXѧ EF.:Ò 6U(q]h3.QE|-$ɔes`O/VNhU W,S_nq2zpSs qo/Z2<x =DžE7HC{=1ϱJyEaGnEr8$Q\&_mpLVu @;"\^$Oa"_Sv*N[$o]n nöI0!o݃N=NsDTK-1xL8 z^Ӟh77pUhrއ'㣣nݭDcRN q=t~C,$]P,lI,Cׇ,!a/iShDbZ67PD"HvmCTO%wyxvNW(0/V.t&>oj cC6%.Hf_ͰgMV C+eєʅe^Q{)rfV.sPr+gY.]ưďQFkj[|.Aۍo%.W_ZZJ*3Lhѩ>O2Ezʨ$`Kh4"X:'1Z*qOѐ R' k4q@Cx?H "_=.xT^[x(6·^f}o^ _ڻ΂b1 /N^t0 _U愳sa6Ow$ Q#=ע #yQܫy-)^cz Ġ鿰4vV6Rsyk_!kT(/#xmW_ܵ F<74s2&rA O]w!u&TѠ?muHUqӇ~9WDi_ܞDb 5-CQdmITˏ[} {|6"[OcaˁlP,q=32bdPSr]yc9ZMr]Q(3 IE>;Jπ:l6^oXfO{-:A߽!/pG]rY5?gI^}xX}Lk[o~>IYcIN%Κ*6=/̇[Nڮљ0%;Z^L+.wCQ my8eKxpI[fNJJ%%)xRgIY ZwALY3QA,7Mŝ|%ޔՉBIP?Ep)8n}*a *aĦPO[QQY 22E Qq_è*QinUVnŲ~ <·\YPW"S +5*WB^Y=+q+{O`;`l~%nƲWHHMeԻKHErowvW77W%`#xW؄}u-v-8KƹHgOOSncW5P$3 F(X]V?\-X("Q82V2X28d!-NޠlCn`e VR0Ж1Go}x߻JCrZݱ*-4өj7[kj8[vEpaYG`X;޽cЌ6( WVUb U}6LDt:ck1OYV{9bhA˱)zK ;$ѨW>}!VhnTQCf\@1*H/  O$| 彫2ybvbssdJv'씤֤mIŴNjRvV4ǹ{z[5oJVd%+YJVd%+YJVd%+YJVd%+YJVdt!./oar-2.5.2/third_party/rpm/RPMS/0000755000175000017500000000000011757171206014471 5ustar plbplb./oar-2.5.2/third_party/rpm/RPMS/x86_64/0000755000175000017500000000000011757171206015427 5ustar plbplb./oar-2.5.2/third_party/rpm/RPMS/x86_64/ruby-gd-0.8.0-3.el6.x86_64.rpm0000644000175000017500000014431511757171206021753 0ustar plbplbruby-gd-0.8.0-3.el6T>D ,0@fcd26d1d5617362a457afec9d1e0a812ecbe8f1fǵbCjkY2˺Hd>7?d   9  $ & ( , M PTY^|}(89:F9GLHPITXXYh\]^bdefltuvwXx\y`zCruby-gd0.8.03.el6An interface to Boutell GD libraryRuby/GD (formerly known as "GD") is an extension library to use Thomas Boutell's gd library (http://www.boutell.com/gd/) from Ruby.OeoriganGGBSD-likeDevelopment/Rubyhttp://rubyforge.org/projects/ruby-gd/linuxx86_64GGOe0702a1819d0a6b6c94d7f518dd9d85a8rootrootruby-gd-0.8.0-3.el6.src.rpmGD.so()(64bit)ruby-GDruby-gdruby-gd(x86-64)@@@@@@@@@@@@@@  @libc.so.6()(64bit)libc.so.6(GLIBC_2.2.5)(64bit)libc.so.6(GLIBC_2.4)(64bit)libcrypt.so.1()(64bit)libdl.so.2()(64bit)libfreetype.so.6()(64bit)libgd.so.2()(64bit)libjpeg.so.62()(64bit)libm.so.6()(64bit)libpng12.so.0()(64bit)libpthread.so.0()(64bit)librt.so.1()(64bit)libruby.so.1.8()(64bit)libz.so.1()(64bit)rpmlib(CompressedFileNames)rpmlib(PayloadFilesHavePrefix)rtld(GNU_HASH)3.0.4-14.0-14.8.0OKJG@H@H@Philippe Le Brouster 0.8.0-3el6Thierry Vignaud 0.8.0-2mdv2010.0Guillaume Rousse 0.8.0-1mdv2009.0Guillaume Rousse 0.8.0-1mdv2009.0- make the sitearchdir definition compatible for centos6/el6.+ Revision: 433513 - rebuild+ Revision: 272023 - import ruby-gd- first mdv releaseorigan 1327326698V$0.8.0-3.el60.8.0-3.el6GD.so/usr/lib64/ruby/site_ruby/1.8/x86_64-linux/-O2 -gcpiogzip9x86_64-redhat-linuxELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, not strippeddirectoryPRRRRRRRRRR R R R R RțAGg൩? X>3l(fJ6** [[Zj0  :!Ji[ *ZeeIf9fԾ<'{{~w z6٠ࠠ.];v6 ?!Ƅ ;N;?Yoγu(?!9i\2tL2c)}H쐮];KNJ?}fjFDT?ZTϩԐVdt(W* Qy-THCOITߘ9\oVqzO|/ JݔS-/R31 P٧<єUsO|!~֣vP2o|BCc=267}t!sKj{X eYm?9o_.6_>y/n"=o/G]'4ݝuO[\%\عޅ ;jqu.sisRq^t.s҅.ʝ"c\=\">}]+ 4.]ԫ ?ra甋uE|u].͜[ Uq~˅Eһs8|炟 .E_uLvjz9/qHK]˅Ņw]{ͅq=.iB ? U^{]3EnsE|Vƅ]W.v?ʅ~ q9IcʐAØJJ筁Yraq} Oŕ cVwni9B/;#Dg %  2;.)a)jšsX3'eH)IIqF}c 1&L QlOǤ,Lɏ1A-z}C+DbK$+ waBu Ϟ1<ݞV fDH6BWW7Dai}2 <8p6̮L4&DA93tlcEPSб8a';>.I! uی w("2Mvp# |qC3ulMK7ځ:M>֋e qd10R;8cVDQ3Ʊs4L]Ƥ%LHKUfORƈx !΢Y]xKKh.=$e`3craDUr8})&$cG|LO &:>a%+ЬDlDD1äLHVgV[@8hVjTdwV:oK}j?FkT^l=%%ƒp^Vä%/yL=ROD%~m-Ń\+~C_$[Z^&x O9_loI */Z5_S\ |ot?ч~O~Rޅt;?[nO'z-S#D'MDtG?ѳD'z2ҝDg ݅O+Hw=gDt՟!Hw=T ݃Otw{Rt/?m~Ot3{SS#J'.aTk"݇OT t8՟ߐ3D_A?՟译6P>t$՟H}DG:O.DoAz ՟HEt ՟7L'uPP?ѓF':T_A?A'%GRT ՟>HT;"=Ot_ XH]8?5GZ_ Y[*C~Ic-H3<0,? 5`*S-Caq06 [| yZ`8t`6t}:+Q, +Yy£zTG>5L~v O k'ˆ:Ͻ +,A&y l/E3XUf{EL2=vr|,E zo׫O1a+@?i &}A:l K$ oJ]ژӐ)) j2?QBuֱ Zmh {'{c ܝE}PB'P$-i8a13LƑx"L|g,(HܒeV&$8IAL3 !_`&3XV,]x}$E(Ic?gT Gyg?[i;g.4Ȁ}$a.H%Q$tmnNL$~O3>&^I=&^č $d)L|22x4a8E<{1'0 7abk8* l/g̣,b&eH6ɦ0Y oK'ގf& 볬L֚0Y)dmH|e*e`ٚd&[$+IlT,1q1aœPlb7V$N*0qiIʹ_!a .0Io2G?f,a'` Hb>2j6:E߽kY ) &~9Xx?*2[LG. P3LgIL dlnf$dܓd+iz&>fNSMj&WR _YCH?yCnMѥ) >]>מԇZj3~0+?3[^,2'2 -/:c? }&D? Ƕ>،}i2}{cU*sK wy' e}C|h-Fl~ɵN_ rw'6J/j~\Q ߧp^zQa{x{`I(KB=HO:OU}ZZ- [ȵ\4$h Q :ԏ٫@{W=0vh4M{KnrMw FhѶ_z( H؈† nXS;Ծy'wY`.QuLfޒRΛTK7b{PQa { zLmI U̥E`q92CWu:`¶7zG x|4 }Z1aƄG&&][դ2Jx8Ck&oaDQ;v^Y#+Ds?U\X_WɰlfbTh /<硗l~ s"ct@9r\xȝ( =L61r.r*r,N^@^# YB|q"(8@|UW lϥ(& 8o@Ԗ,Viq13])c24<*3I}ʠ+ͧ@s CCԀ˟pKe AZHZ?*8;;Hc LQ`$B ?:"#x(t"P#DkB(8tFgޒ>BפMB(8l96|xgC]|і2i@NxH5yh0R*TcEv\ڔ1J\ڔ1oIyW052^P+r/kgvc;a;b;a;`;B;C^A2%+ wBnƼ% A@c^N|On.cNzec(X-8K7\ }~DGi(KT^xs9$2hEċ`͇ i4| ާ;ye0 v "7?w\$AWlCPRalb k*B[¬ el*T'Bi4=c `f9"J+6 Rl8 a MQ0yQ@G q4x'@@C3%F]vQ%)Fw"p"a>cD|*[8T (Fi , ҳv{yeM0O:Re[KI<-ts!{/g'im#om@Y K%\#b(0gehM)R&HܿUs 2ʆ4<3, 7oH iͷN W|_> fH5Poam$NE[@ *gU\x \FԿ)Etw$i-6P_! LЂ;rse4QSa1E[8FmϩexW OEҺoc+?%{l1؟]rcp`%gbKΰd a T [LRul1%gb!,Q7?|"JiDj _[;"=yj_uB-DJ%ɭddgUW$}CٲXg2wn@mX7R;ʜOf/p"+$AIP#I(S;s>ƿh?!pv,74='nG}Ia@~\ylgS@دowpz^cahSX[}U1dWItژL6ic2KNɞYeؿ;܁| F+GC:~Cd *ji\mZ6`ǖp\Sd+McpK\ͼ3 ~ hd=J1*ؑԯKa~ժ $gm똋~^yz:o70KOH#Ea i3|Sҽ%v$EHlk,4Ґtֽ;:" >ctV@K/~M(Mi]6NLˈZ#=๾M6A8j6^ K.O$K/n_< OrOV^>vvYpTrWo?a,"n_:b ̊i?Z 6v6);GAc]T>tzfm;WC5O+?1>:XdgѰRTP,`WlO4Ԑ8Ay6#T㳴P Z";>9kyv6 K?w!_v8c''`J3HfNj wp㥷 B(KDɪ_ZNFEm9.`Od=S{)^8ob,_I\3o+ sӵ2á[ 2VQ9t8?LMp G4a%{Nsp7#B7Qc}.g  Bn Tv#3[P97 \q#`0UO6)7XN0[lG^2%v4;?rT-9EqSuSn+(k1\9I|t"~mLM }-Q1QUaI;Fx4R2ȋ%+oRWΆJ/a B>.#Bϯ5ܨ]]y}ϡVS(%v f`Sy):C6 zBᰜ?7D-{X2c$o@Ntg5;u-L*yyWn|TM~O?SkA76 J\,gq O ؆pX;PP[3\+u;M5ҒہzUZIkjܺJTeKe<@\Do@Χ42h|vsrV-yp@3XVZ1m1*)+;ˤB.$3@zF@tyMGnĥU2 7ݕ~ aL\2ѶD7ݕ~&gY)0|4Z2Fh'$DLil3P=cU'Yt?`oA R7LћYQ3{SM &Kb~O6~ҙOWhP __!$,R㼹J?.۶x(cK#?񀢣 ROY ,8ҍ18b 0xܒ2hKg9iq1!'_N:$s,u%bU:Ϳczp} Ku1~,tG9,~lP{%=@0aXxg>|{'xi0F1G`pnǑ&Z{ ])L1hډ?9P ڡm@ey}J\8%_\822'}C{QK띰t;_:.C}*|U}ϗ9Էcoq[}2ʱU}e~f3ؚH}Xd@EGFliLlzݍ-ih)R+Ӫx~_JvMh*Ce$1>boHsvs눳C QÆGZnq|凡!ݞi^tU1VXj| HN^@1]D P?:kW( P1 f_ HA[wY(a]7:y+ցҽC.4L 5&8u/| (rt||] cUfxq5,h&o]^ͻmU^ʚ6>[??o9%B\w=(_'7o\c{ı=?~dcs]7e r\V],>^݌w7Te旄b j>*!)=tq?|GG txr7.+%Mwx{`KUTGeQy)噊_뿏 |,!>;}vC|9O2>{;iOOp7k92^Bn5}F> J\n|dwq7xh |%_[%\X,xU{1be /#^X6#܅o! ;\Ækwa6hWvk="=ݭEĄe1=bXw.ú+^Dt7lnغJiEDpfbWe}M1yn U8Mܔ> @ %;@|l@cx>툫 >t?v!2 91BwͶ/M'*2p5+>^Mx+c_gL:{RmvI{w|n4sqQ0x+||XI>E{cP>~;)Oy@>1x5yS vj2=&@'|O &-ƥ|]7o'|m^,v~{\!4xǥS){2J5wNK?:5#ZÒ>Ωyf .;'[ڝ]~3~ ֎P;μ+]Pn]:v]3Wc+}t4n w7uwMdTdqcUno mYVd5Vy仒oQ Aǝ: &~Mǃf8'kJ@O|Wr*[N{u~I|lu][m|+8-N']I|mo6WݹY|$y7m<6nvW6;NI|??[]wFgI|6:F'}e_WY|knrķb2b}fo:o__t 냇߾ A;{ MùԍwZFDZR&vW 7/}J0FEy]/toBTTZF|#?U3}D(om,Һaf`>5w?Սnod.f.LOY]' Nȿ(KExq|R~d>8}n[|F[<_*=WYϠ)w_%yg6&#TuU lpo_` Kl u% aro_|T莟7':[ܤn0s7u~{&=}wU{^yUnMSK!O6H'}W¾0>~rO~=ñm:/މެl'c>e@A{n`%]DyɣV+TF`5ڿVoZSPdWxTvCEPPXOVm2!zїXhdݕfCIZ,E'Yj+1IϯuT6&?|-uMf:tRq'Fvh[ױ<;#kԺG ZnWR0~ _G?kܖ~MjIEOװyu D{V3^JMSIod_7d߂]6n:j }[VٟZCiXV}tIϺӟ+Љp}nuj>$|Nj%/]h#_/t+k,~Ն%:ŮO` ^W k}¾u߷>Rط#Xr!]ÿþ{urg9G%OxJN<_x2h{U[1_X~ Vp?a9=cT˟]KW+]';Wr|~Xx>CLC=VTOrίxגk %^s_W|}zk%|]vkr5qkek5U/ /eWeTC_H[x5Jq_; zq_?Y}xbl+\kożm>a}Vj n Ň /^,㕵58ū2%^׹_n_.澾=_'P|=R4o ܇l> >x>[]xZTWRW,^*uv׍dE||}z︎-gsY}W݇9m>Oc͇^T~n_Rm`egOdߵSmƎEⷷmg1߽-מU-}C%Uy556K:uZ+$-">g.&.'\sݗ]"Ëwwַw// \owo9|9帿ьwG:8~.-';BW:f]߅NwݷJ}.M8Z3 YOl?J`njn5&?T7Q#K3 Ic|ĆE);sHYCذvdj݋0QڑHKY[#e_܊/3uvЅ_ͷP/k(m\wY4Ë SuIHdiws6aQ 1mM`SנMw q@9_ZflCᖿt-3,g;QNk^*|qo;lwhۥ?oB|p7ǍbCء:l:#̡qXq)_ضmXar;U^*CUR-`>;^{Gvfr7,+7k.}Vi.닞]:$Qi] x[ ۀ7 2M+=Ӊ?e)-.DO wVO|0{^;=4NJTr\)q] "5*Ègj|ֳ_݋+S$|/O?dEOTNNOcFr6^^/(B{ w' m0x!EpϞݥ覽~ ! ,  ~V@a.ߑ8~#l6f5}P X2 [9l:"5eb㛙Fw}&m >A#YjlߜutW+T iS'\tTS,  .*|)e|z~&ےqJ:TȎh8 ςfM8$l$X U*Z8!>' } VX"pt :0]GpvFb{hbn{hbz].db[h5G/ju,GW'汩V\2be/!c~͉0cH᧓Lbl%u}] WfJZ5%u2~}=Tż̒Ǧb66pLU>SNTH|k󷹏gG|^ϗ83z=>u yl|ޛso=>~(>>8gE| >vϘ}vu|=>sӚogj 4gd'd'b>z >~>gugl|s||nȷg˼'1Ni#v'c6|ɫ<@}|nHlWGv8Wd>kϲg {g:ುf{|ΞP|~#|r<8g˙ V $׆泪S79>+8>?Xdv@9 rt,~w3{+v#H)5:jJG)4 ˵FDx1tgEEȂ~x=/`HV$i.9$HlMf(~e)=hh !ܒϰ-ACsI#ďIO܏I[#?Qހv@w|/U}Zg*}|f7'wcph'j)>Lshz3O}M>ﴣU[sWOgϷ뿝ny}4ASwL??=ߜ׫:Ns8ny5ih~ryy}w/>{S4y})ߚ~3|m4Z}2^ij(?\hel/W9e<=/ӧAKy^:LqˎG^~숗D|^bsŐ/&;Kd'x̩Nҵ5?Z-$^nxEOq2O/r\eG8$ ̱ՓbvIxӃA#^~,ejgx0)^&9Kfsv Sxx94G"=D#(|jzPظ9{\Z n(~*đ"z;R\I16&$e%'%樲RR i&}j\Q?Ef+UpUql4/#-%&u>ƎtF x#?r\Ol܎"0be* H7 ~T1ӔOKNKNNjn&eaчq\4Ę1.qRɘo=xB7. 0ɓq㍪II DU1iBIjP0vtҸtU\BBIeJ#kdc04јaJ7fƏ UJ\:ki)xDCOԥu+OZr!k2,9=1d#R&$@{1* <ɹERb I2\IJHF ᔟ#S ĤWSTTt􈾉+ULQ50-"aa{w20Y3sgUdĸ$2RL-2٪ U D-}Z_RvjVޘ-2I4>N 8jЦe0ȸ2Sd?Wk;T+5>eJ ^B1#%Ʉg&eAzlV 4L>R'ę&Խ:|NR OLCޔh\M5iX`'*EBkz䝠}JVI?Ψ֏1fVRnjU@VJj!V@:Tl՚ %B?>ҋBzՐzH  nt,KPi sVL1!-4Ҡ׭bϳZ+_z^` HֱCVk9XU@f9AVcC:v)i1{ -! v%+DH+a*"H Tbi: ~=AZi6FHs! i&u.%cz[H!ׂא4R^"H!n=i.(Rn5 i̇VjHK -TiGP/ARU rH!]}jUj t1Ї C?!81'HC?@~HC }ҹVz@9*Hu^ g!Cz! iЗоC*A:"Q_6lL!-v! ~O9B{U ҒJ>p*@?@w@8>>>~  b)6|A4!GD 5'W@3߭}[g5/~"Y/ehTy4@}.y樎B`o=YX~98ZOy=b PuwڂT$śn4f^3JFy7e*hE% x+棟>=BO bdxW!0^mX5aby[c>AܰhKQx: iMW%oJ=] ݯ}bB5@^؋YhPx'kwGʝb69cQ3\d)aNCɲ}H?ŏ0~ ~/㠿_b 5&}j=qpWV݊5`l+`VKb7$*u[Fz4nԯ!h\^1hm} =Xngz=  LbUFm^l}{gP z@oho$B4UcQo\l_ ~/gZO:12{̰ #k?KrcٰWlC8uv1Y#E z@Tmg+_Y*K޷Z v8D꨷w?Z>Zܻy^=ػf}x'=AOOY/+K8hxݷg< =5{iԄV7wnutnlM{U <{\񻩔6?\oAo4#`f(E_3OqT'mzj}_=#T;W}oz)8j.UЯ~Ogk9~|Ƿ7y|muP#o4W dzOuG{*{2DQR~Aϊ'+#3Aǭ!jGb_k[+?食@պ铎.n~j+`Wۭi}뚴o YV"nG ۨoiqqO:ݚmaţ^G #E7nϪk*OTO9Ӂ<}<<]<=<7y>-yjWCj:I *F?>yiWc8!H^MB@:-U˾,#OlDi_4+ jzU]b>;Pl v3몱'"Of1XAя'וk B޽O|TVi0NfJ>C"Vsé}{'[T\ ޑ%DվCwsv=f_ƻ}ww{_l\y6݄D{z{r=ۊyAې8=mrJWc9;Pr<oi,Ȼ&y<9, ;H)J*BGjH0s|BwV9x!Ʈ:*K$J{QWHvf前KZhP7Oo OjWcP*>י;QƚN3gCW5BhV{A t/ˑUbS}n *9~c!"/`<5J^x@հ6nB…hR?A<~D< zQPnRA*Pz1T=[po x-39]io}^87mQ=Tj( RiN+*45$y9P0R4[dv)eh!m+*h.wѝ^(tY (ߠ+<Яr&=> Rƒl?!.vv*jFwn{0{GF<$]S{3Hlo&{Yh ^F3wٞ;y>r 5eZWV_9?=ݰެLF_֔7T)76\[*-T֎쯪Jca5ҳ\S:uϖqDV$'H%T/ & tccjLkP=Fe#R!uT7<r?8OZw :8#㌺c3wqF|{eRK!O"m5oPFuxN!k\5YRI&hBdLd"t &$ԬqB6dƙ̏*N. QSge'ָ5y,G-YM4sv<"+zi\A_ݙƝ9=P ^a~,M. zIy\{Ƶ@Lq ֬I&_$2C&?&rL6md7H%'D*y$RMAu'7S̑[H&(מBR>֜^xpj(|#mC#>UT6T#q}e*)LC*Cj6jL E*CFgN*xsn@=IW OꅌkGZD`~?‚[کXXCe h!p,d$v5<'S6Mt. _Vw8n&yQ1r o!Y%0[G"q Y6b' Js0rS[,O6WT竹>^#nE_l Օ^X@4.lb; lw$VlFeTߛ*Ygcm}~oy_sZ tEv)/rC3QƦr4jQMB1j{Gӂ#Fe`QZ~:YKޏ7o/wk֪r}F|K>A2.,.%e/l4{]!~}ij%/Vي7+-AAfr,}q1&ξ=A&#ytYtVWfc=TbLkK ZS(xt%zf֞7ю*Q7|Sp\7G_Sp\6G+'Qs|G)hٙ f9, O Ⱞ)r ˏOaqTⰸ4KO|2?kE'3jNGy+9/ZQfOQ~CFGٖNͳLGY1'9f4%8J._1GצQr >G"L Az!`X/aea>gaѓCLa)]:gNރ<xÅ#f0T2"\j\i&?Tch+b*,qƗadDd{Sxd\Z#ұ4mgȸ> "EV;GEƮ#Wщe!f(sc҇c_}:\r>#opim6!L6`ekz=LJbF>"7{҃93l /.Z1_Wk {9P'^F0s .[ρ~!ĥ@\ӋR>Bn٥PwOZ a1gi'҄B*. U=x(^DQ~bcyKx>b0#X4g0~nY]43Ë .vχXɇX7bW<.+XXr h,w[X{3j5fbᢽbBB,\|¥!E %Q'~Wbx[R8c,.KgX/>:/x¼=eOR^{%y z{\xYt#}ˁ uj*P|GΡȟzt05­ewv,Ƈp!\b 1-2q {T|>>Z]bf\zǁM1?gJ_-g)kPL'dhn[ Y,,û\>XO[L1:1z!y썗QTLv"Tg@RWɌ-\fZ6r4멸R&!e ̢B)WaCvÐI,ȏhDd1~̎w4%gv.H<! I |Y2qp: `Y1FcJ]1NcJOEPf(2bVc$lv81L'f 0yWS&=H]uM@W Ԁ:1LXՈk)=ȽrBޤG$v =ݔ[`Ƶ|Txl-J~Z&SWS82: +p tO*XLJ!|~=rgSL){Nw,;e{t"rQm  l@摔~-,1VD~N%;袌Nیxt Fs@D9âyrͻ+ ,|?sFe;-ƲCg=%1\oI} ;~_c3T P>"ʗ֖/13V'S<\! X:3XF[n|C+j+|⻒/1./1.x%_b\J( 3-s+n) \-A4Ak=]@-L=J,3 ~gaDzUy0*ai"&%+CAi"m'R{K,0̈́ I8ϭl s`vۅ1&=Wbv,]aNߛjxti$3[pN2w9 eV>\lsʥ[riVE.?fqw;ym_hDZM3?mH7D?XDB6 !]DPoD`* m>"cA"$#ۣELk$<=dȃ"tMqĎ`x;f G:-9ς# +(ss@3>EfQ 3mA\ۧ`懮ɠ3>ZkjgKIs)q.]b.ۆ d::4+}:y#x zGӯ6Q@f@7@tHo&R—?bp=g?ַhB)J!^HĚ[IK%4a '?bz>7Q9k PG/\I'OFQG5jD75ҍ_:*WcEޘuh+]7b]~ѵu4KouĥK4x;=ۡ&@)PGM?q4bI ":fr()1;s ud 7:K&Ɉ>B$z?P O Ғx"n'+p ة8hr PGӡ]xNz -hѢF 7&h,a|SpjL:dw:bu{xk1,('v:҈yq)f^G\Zy3#.5݌u7Cs>=F4_ouDZiRO?&`[)/MlB@2[~DOk@s:K`<DŽvb%̹pf"%1b*쭼ч%v>˦㔷 Vb[ n#&:2/ :$u#bn~:|!; ]XGgb`QwQkn:Q:d-=o#.K=bq9c/jvaxv/Gx"z/o'`\Mn .!~D%RRGfL2$.h1XA(r:/EU`nT̻6_1#.?**K a]YikѽT*$[޾ѩH+MrUk6fxK[qߋQݍwQp~> .eQp~> .oQaG1f<6ǛaB|gضGs3[A}{/Ϝ:BcgN]C~ggN"9Y$Mӛtk, {0mfbYϞ9=q_9 f-˥KK{a·VcDf-X8۽ԢRlG v]>a5Gp>Pi]w#ff-:O(!XUB?Ҳ$F8s'x=8g$>PO);.zx)#YP=(AQzxPGbW|"ů9z84Wx=Poy=piA^\ |A!;qReAZ4@ E:cbG"`9Tp@ R FAVtMIcf>E$z1A bV Ғ8n6+N!>sq/ ]!lc@=\ $0Wsj5ֈzX#y#ôVodn:M9nbc=lgpaüv.Rw>K/`=p9(AMl&@s{#b?2a{Zl!ˏ6QPO@Ct `CMDH=<&~03ۢGbS&V HK1iILſ9Ѡ?t+6uDm\ɭm-^$Pr(?e'Pwσݎ8*GiY5{A8Ҷ7=a x$/3q #I1b^L[NM_BJ; CeVRoE;zuT[wӸۧ-t}bi ugis ,s­NۓN7|CD a{jؾpE ;XT߱%5~ S,iɡD`:uH'Zwе 5=*#0"ƊXk)ٳ=n-b ;K܎ZmnQHffQ&VGu:" [,Cb,:~<0{E|'>H4.uPo8uEE !&PAv'<9 T#[ҠGd>=逹: ech"'2@g'gN:/g$&3Q:L tW$o6 џ(ϣa'\{W~ԑjmĖ-X;"&1vyϘe-h[RX-5ޢ|?7e?,YX^[ }Arq75}:)!EEbWT{}ڙ@i7Iǒ"+)* BEA \ (mGՈl=Qf0Ff/K#ǝGlVG]o̯1^';[y{7k]B6ٿ[Z.=R"|bw|)#^ .?d!#X``լDq*J4Z[mC&.϶_f<|/j7Td87;"|~m`񁙓۾4Fv}.g߾z W>1hF9s#8Q^lc=1ΧL-+;IF$?6i#z㛍S?k $ƺ\ssi(Z ̱܉ gL3gkP|Wzbt!1:49k7JR%g,ۚ0|j,[}]yV,э40oklzqc'?T㑵O S/o3gt׶&tWW2{mlM`~lCCQ4qҟl9Ig|CRU}qd )m Nob06Kl|^mwz;Џuvw~ Km#=keد_Nz6%5$+YXG%-mvHWF6_ttADuy}'#h,Ytꮻ+MZQ&c5Ɲ #flt#}6;?)վnm`W!oxS#*is΅/Fw{qgjv{I2woۆF79,lň3Fo]e'j:ˠ?g9:cAĻHm57%E}{FSjɮsNmN9m3$ F-0\ՓYX%?6mp\Gt9m݌O'KlcV#'5i̸$!_ogwGhc xHjmlm\v5_02d'ȘqFzl;s9+/!6?±9$%3Rra |{0r0ͩSgfxڈhz}~I 2V0Ŵiedl=.O|I=6Ժp±i#rگk rW{AD2R#W'+hmXWcICIû0ǸھH{8E4&gw4Rg ?# {;߈L#׃p HtҒ/yE̜m6ؖhյyH[c&ʙv~mi'` L]E󿦬kR' <Ⱦ&㑄OhFO=k/ 2'D3gc_}g WbJ=I4Oד`ܼe¬VV iF6g&{fu,c=no}&`^$ f77eN4+vm|#}δeo_`&4]3/c^¼]dnNm~c뚛87'@U=FbHxֲ֕nۓg9qs9VCq@ցx[}nr!li irD/Է- 6!Mܮsky<ϐ&-DH+wYfs˺ avgcyVxyoJeIɔi xjqi 5Q孋sNϛ662̈v]Ѩ"w|]4eZ ҟ-{^.9W#.1y;#&l"eKX亐%w+NREHx'L[/ZVQy*KUҒ){ ʩaW.a]JjĊYM˯.YKXC(旓~~A E@M"֯gIϜ㙚JLϜ;),/>Kh5o%Sam V[QubyUrK)/xH[Y%ɤd./A`rʒ /SE~o V啵!&9M'I_4d؝'᧤8gΟ8oQYmrR|^rhYSgOL {z9#'[RfgNc D*[j+}e%ޢ(233ͬ旗W@1hX<}ryN~5s_TDݳr(*tQb$˩鲲"XlT̪ƛOjPU&oHd;||j($7e}>XFQh*.&^,5^?%4҃{.-E^HVV6ϱUؚꊂڲəK$s$S؆媫]7Ȳ,%Ǖ#=b뿄o?eb ĈhU{]x4* ɂbyS7 ӮԂ]j* dV#(?F[<>Pq.ti|>I{AE5T9?rqӅ[S’>5ߟOkCL(dR.oa}K W SEyzQ fy'tx*crho.vƆ t2|m@D'-*x%n:[YBԨKV@眮~uUЂ"xw%=!c$ћ^[/P끀er>]̝ 3;S]PQ2,ٲ-LEIhDi)5%z'eU.hFY>=#*SDt9sTR]>d\/D h MLB45qOZF7xUJ1NV3(D j?5UJ_ү9 2 BPe(db?/9ngs#o1~a2w?~k.L3ChAStpD uxޙr3zgy& qY54;7Pg/;M7Ig:녉 ^}<$F&:?[kҭ6KuN@[2tCdBCGޙ0$?ճdjCdBCG\(h.L2;1Pgx=Iӳ%CS 2tWL'L\/*W 4UZ-KYtXF]5³^5Q-T^@ӓcE~}p1VDž*B@[0qaw^N*K%rD61IqMoHhGPy>V ɣ Eag6M3᳍`ЄrtX `ZOLQn +佫}j3: IBpiWmd {qđ vh=MuW#4]#p>BQ&qOx^R{)@Cqa ]+g;9t}cv&9nڡ>$ {tӺ?zG"ݴ@d. }Eд]d"Gʁpu{v!%Tt"t "7*Q:({hZ*Q6‘(zhH(aAA=~n0Skl>Ԡsh4ye//{!|O'σ! #E62. B,ܤO#r,`PAZ5(*Kv#hKKrO?>w74=6X#:'\!T7IÆh ^.~B&iդpgD8U*{iK­#?/:CWqД(M:]Me/7ȜxPZ!r6o/ ɀ G@8DˡC&S2+ *=n"QttE E$("r Vji6I3 M? L=i M:AӹāpPe 1L 7͌hhmB9*̝p̑ʝr+Ma-]atuC1]Y[g)1Oƈ@x F[H4Fb;~~!5~! ri1hJm9>:\;h&$_m4,K- @u1:5­ c;80s8I8MI&‘-L\bQ{9jץb|M4 -BF= ᓱYMFVЧ,4Elj Y2w qBȅpPemx"9!Tn՝s2J DW U JFhWŒBDoRy0v%SʁpH\"SrƋ^nrNmGj&!1 >_tR+UU0U!(LTv&sr 6BE#q`_ m#EzQ#UڳfhzGf Q= r  yEs6jDNW^)ϠّO*2(YÈny*R}i~i0D5w P?#ȽLGt4m% 5տHt? _LJL㣴gwH$[*!r $^y#&,L\}nD*^nDASJ#[eRfe ]v03eCN[nhj7ZS}q gnM7cmdF"TYnhG ዒI@D^7Z; д>`-*+ѲDC2)1MFk3L$.c)j MG ̿scA@ɌpPe!3F[ET L2/CD$B~ =_^2F;cU >â[\5FV{d*.h;F[E';H뉱G "S%hsw"$z(+A4 v0RݫQd"|@HraBx1hzKG/^-zFXZ%r"Bg)D0cx(0˃D7qRr+}ø# 觾Q,ڡ _da!2p׏_@'hrF'3A a ro&rLAF2Q6QUזJ7^̾n+ íԇPώ~#4m&#7^>cSPg "w$3et hZ=K^ ~MN$e㠩`^9G*d&il9iSZ҉p6$:'3#\- hϖ̶:6e,sbsxP9>>W;)];͕7Ut"|IH0tG*nI(C jgR%AEɑ)x>OL -[d|DN2flPaQVHs \(BP9#ʭ)0. E,I}6"^eP.Ex>"l:*Si}/l:!2RMZa2V2Oۡ)NfJ!ՅwÄ 8ɵPf*M0+Q;4C9V Uk'j3Ɍ ֎{^nսet~AD]r(G!ѯn필J0M Gn0q ,6 uZC|즦VaVy`=6ĿnثC6#qД,NJ69V3c W?l*A#\M2nj euHtFfatx Y#V2#βT Sw%jP"hcExRi.A2aBx~t ^Gįnj ӣXc}-ʽ@X$~hj[(~ B e7տ4]ځ M;X)cE&T]/fA~ i}/l:GԨ cu`"M2C/Bx=<""ՏdF_.:%jIc+™^1V\WJf+ JUpgD)zUn'^c2Z5m4X$$TzRcto^&Fs\{%*.A9 UrGʸ/-ߔVo*VJyXJ9w*?V)1ل|P5Rn2D| &b:sCmEЖQ)oQel3 &D Lg"^"e!J:KVJYM೭XzMj7Ҙs\x YUZԿZ )Ucr8PEՀB8FF]_RݹTw.t7FF{ r"l v n'BFʡTFTcjYm@VeclVe&IjdF{rMa<#y4 YP~Huȧ]1! *藅T}]LEqj[2٤B+*ɯr\AaX-Y / /ijD772핪;r,`KJ/Vi@ V+a*Wjw^t@Uw.՝ \yAD !W䂛sPUFLnprY%@v9`yglBST#umH2r4ija3Oq7쳖ĄB!hv A,֊~Hr AuHPmXUTtLWLb IW lQ3Tb]`r~'C﹁N9G>%Uo.DLt \&>Or<[lE1)űT4U U"Uڤ!l["ܢaVݻ[&JP 0MTlo  "rfŝew#-bB|`SǦR87~aR YQ?-I;MVFs(Z#ʁt3ޅpjmԈ _nսet*w?OJm;aPy1BYPYRJ;"WFC}^)1IB+C 4 QY)IUqCpU)uUM]ofQ M]6pT}жVT!|~vAwKuRݹck rDNyBܿJ+w;*rT-@tQη̑12h/3L6!񪑺4Ep2M{4OTgPl2JѼب_L,(4~n:Ө_Ԡ6'#"e532t%f6S3Tb t~ H{׀B.4k)T![aQO)e&f\,U}ÙzzR"hp3Qs\7[! @ƍpPeϤgʀ> n>['K`#c-g5]rlBn9KT#ueZam4ѭIpIQjE~p-~*N[:H*#-&vgx->gOӸjQ|~g5omm3s|h%牰_k>]Bi~2ݔ83~#~3/n7wg|O,=Zǯukoox|]=ȆO~!o<%,= |kaFx~I/G+wϨ!2xpA#+CGíؘwm=Cw >uuAx㩼?11< 6t75w_(8{o]I+%?oO ,ETYƔTԖ /+04Wj)ZQIzOrnZ5 CjPuSVYF.'-&i*g1ROqM~SZT#%KLGŅv/$ƔT.3ʘ"oAm'&CtrYeq0-(֡T^VĔ .jzϱeYA&V橦7* N#[1_g~m#31sOq??53S6'Rt ț5=3:8JS=Lmev!/GF0>SQmBKzS]L7/S _Gb~ a-Ew7>yKxh_9tiΗ=a?/?dN7Cbg`q:zQ ?~2?A80}- >?f;%_?'C7?m'k?KP]nC5;_Oyxk6[^?I(k/}inOy޿K?/mOT_K qj,ӟz5&ovqo%GyF8֟# 8ۊx8>ۋn8;d8;Z>L](X8#XU~.`}?#4S#5>}_FWt|u҅cC9>F3:L46t/y?8ߥ<7@)LL}&}iCI8qx g@ 47dxŤxe. }e#A`I?{I. EW>`U`돕&Wý-g?uo{J{߃ÏZ۸_B>t{~$ O}ܭzό2 ^[x=?ޒ\dT=)_ 0x{_m}y}w{, ǵ:E @a@.AXg[!`}OWo{aǯ>dПǟzx_&zC{x A|O}!^> ~7K>~1пrOg~7ǿGV0/aoA<6>P,ЏCYh.#2ԛ֣&}==q,~'o$l}Xn ;6 wT܏qpF `W/3lŏ_`b6_d7R֭a@z|/@h9Q|q\LNxeXw1H$l|;N r _aZ A¤`\1&.obn ֿ ۂ6~C<@&.G?W_$ 6wVpg%/#;+MDvU?_Fp7>?Ay37·j G*S3_E)/!2@u} K_E)~nl~w!¿E_EM5_F7uD]/~"o'b<߹߃~M =?O! _DTuDy_Bx!$"l!fP%$45#1[ւQ#9Xƀh Mav!u|Չf^2̅'] 1't-y[{4ڒ}uvär>mLCwJ\|T|N|U3{^Us< K^]hX⍪؎Uh\ BcnSK e oȌV`GTFՁA9Ӑ*1\&ϞhhM+E,3ZG16$t&,iE,1Ki莓;#{(1,Y']RgKܰE"tf4 ƾk"o9&O7b=J$Dٯ<32*B5BY d+g @Mp"xHI2(1Eΰ;eC-Ne=Rh24D^#V&QR}debb!iv7nT/ሖ3Uږ\J f_Eh䍳BI|SQM=e:a\H4s/'+΀ϡZ 9Ʀbsa,(R"ĺ؈`a^&X^%Z#kF{@F-78 #?-urLO5#@BtFCX&jt`P`iQ\N`K-84R<9f K&e]Dꎟ!}f;1xӥ1HסݥN^0…m! MfANd ˦ՒU'0P`Ayumbfr}b,kgχne[GEU׺Pd4/15-j~qET(օI LӀ'TDaVcO_j`e*D]صM'dSک&qQ5JnFDD@MR0.s]Ja7 ۱#Bn9z`|C>S#S%CtiQ6߄U;!8VOډif<*=䋁Π#a&:hwUi)[]+My΀<1}LJSa &7q If:c<)gR̔&fg][ Pu fzIj!\T̒{bZY=?P+gxW+B.3lO7SX2*YT)V0Z`!Рسu+ x> bk5"TeF$ZÇnvf0ח{]COzdH./oar-2.5.2/third_party/rpm/RPMS/noarch/0000755000175000017500000000000011757171206015743 5ustar plbplb./oar-2.5.2/third_party/rpm/RPMS/noarch/ruby-DBI-0.2.0-2.el6.noarch.rpm0000644000175000017500000026574411757171206022612 0ustar plbplbruby-DBI-0.2.0-2.el6T>D ,0@926f26f51f554ab94dc7b3f7095226c7067ccd07j7mfx8~iu>80?0d   F  __ F_ _ _ _ `____,(89`:FlG_H_Ix_XY\ _]!_^%b)d)e)f)l)t)_u+P_v,w-_x._y0xz0Cruby-DBI0.2.02.el6A database independent API to access databasesA database independent API to access databases, similar to Perl's DBI.Ooriganpublic domainIan Macdonald Applications/Rubyhttp://ruby-dbi.rubyforge.org/linuxnoarch)08N $i#} u\ ?)NchG T5|' 4A)K>BE P Dc ~ AQ(Q kgsk@>@===1@="@<6<@ 0.2.0-2Bruno Bzeznik 0.2.0-1Ian Macdonald 0.1.1-1Ian Macdonald 0.1.0-1Ian Macdonald 0.0.23-1Ian Macdonald 0.0.22-1Ian Macdonald 0.0.21-1Ian Macdonald 0.0.20-1Ian Macdonald 0.0.19-1Ian Macdonald Ian Macdonald Ian Macdonald Ian Macdonald Ian Macdonald Ian Macdonald Ian Macdonald Ian Macdonald Ian Macdonald - update the revision scheme (add the dist as suffix).- 0.2.0 - builds from local directory - renamed with caps like perl-DBI- 0.1.1- 0.1.0- 0.0.23- 0.0.22- 0.0.21- 0.0.20- 0.0.19- 0.0.18- 0.0.17- 0.0.16- 0.0.15- 0.0.14- 0.0.13- updated Source and URL tags- include more documentation from dbd_mysql, dbd_pg and dbi lib directories- 0.0.12origan 1327401246V%:V%;V%DV%?V%BV%AV%=V%CV%@V%>V%<V%EV%LV%GV%IV%JV%KV%HV%FVA7V%NV%OV%PVA8V%RV%SV%XV%VV%UV%TV%WV%QVA9V%ZVA:VA>V%jV%kV%lVAAV%wVABV%{V%xV%yV%zVACV%|V%}V%~V%VA?V%nV%qV%pV%mV%oVA;V%[V%\V%dV%`V%_V%aV%bV%]V%^V%cVA<VA@V%rV%sV%vV%tV%uVA=V%eV%fV%iV%gV%hVADV%V%V%V%V%V%V%V%V%V%V%V%V%Y0.2.0-2.el6 proxyserver.rbsqlsh.rbADO.rbDB2.rbInterBase.rbMsql.rbMysql.rbODBC.rbOracle.rbPg.rbProxy.rbSQLRelay.rbdbi.rbcolumninfo.rbrow.rbsql.rbtrace.rbutils.rbversion.rbruby-DBI-0.2.0ChangeLogLICENSEREADMEexamplespersistence.rbsqlsh.rb.newtest1.pltest1.rbtest_blob.rbtrace_test.rbxmlgen.rbxmltest.rbtestDBD_TESTSdbdgeneraltest_database.rbtest_statement.rbtest_types.rbmichaelDEPRECATED_DO_NOT_USEpgpg.cfgconfig.shsetup.shteardown.shsqlitesqlite.cfgconfig.shsetup.shteardown.shmysqlbase.rbblob_test.rbdown.sqltest_patches.rbup.sqlpostgresqlTODObase.rbdown.sqltest_blob.rbtest_bytea.rbtest_ping.rbtest_timestamp.rbtest_transactions.rbtestdbipg.rbup.sqlspecsqlitebase.rbtest_database.rbtest_driver.rbtest_statement.rbup.sqlsqlite3base.rbtest_database.rbtest_driver.rbtest_statement.rbup.sqldbitc_columninfo.rbtc_date.rbtc_dbi.rbtc_row.rbtc_sqlbind.rbtc_sqlcoerce.rbtc_sqlquote.rbtc_statementhandle.rbtc_time.rbtc_timestamp.rbsql.logts_dbd.rbts_dbi.rb/usr/bin//usr/lib/ruby/site_ruby/1.8/DBD/ADO//usr/lib/ruby/site_ruby/1.8/DBD/DB2//usr/lib/ruby/site_ruby/1.8/DBD/InterBase//usr/lib/ruby/site_ruby/1.8/DBD/Msql//usr/lib/ruby/site_ruby/1.8/DBD/Mysql//usr/lib/ruby/site_ruby/1.8/DBD/ODBC//usr/lib/ruby/site_ruby/1.8/DBD/Oracle//usr/lib/ruby/site_ruby/1.8/DBD/Pg//usr/lib/ruby/site_ruby/1.8/DBD/Proxy//usr/lib/ruby/site_ruby/1.8/DBD/SQLRelay//usr/lib/ruby/site_ruby/1.8//usr/lib/ruby/site_ruby/1.8/dbi//usr/share/doc//usr/share/doc/ruby-DBI-0.2.0//usr/share/doc/ruby-DBI-0.2.0/examples//usr/share/doc/ruby-DBI-0.2.0/test//usr/share/doc/ruby-DBI-0.2.0/test/dbd//usr/share/doc/ruby-DBI-0.2.0/test/dbd/general//usr/share/doc/ruby-DBI-0.2.0/test/dbd/michael//usr/share/doc/ruby-DBI-0.2.0/test/dbd/michael/pg//usr/share/doc/ruby-DBI-0.2.0/test/dbd/michael/sqlite//usr/share/doc/ruby-DBI-0.2.0/test/dbd/mysql//usr/share/doc/ruby-DBI-0.2.0/test/dbd/postgresql//usr/share/doc/ruby-DBI-0.2.0/test/dbd/sqlite//usr/share/doc/ruby-DBI-0.2.0/test/dbd/sqlite3//usr/share/doc/ruby-DBI-0.2.0/test/dbi/-O2 -gcpiogzip9noarch-redhat-linuxASCII textBourne-Again shell script text executablePOSIX shell script text executableRuby module source textRuby script texta /usr/bin/ruby script text executabledirectoryRRRRRR՞~]xg"?}i[I|~EԳBv_1fk8݋mI*B7J RWdddK/}w+> K?ϖ~\RO; U~Aէ$Mpq0&ͤ?UYɤ{]Jb,jX "vEn8 CӐ?HQq 0` L*7 A؏qu'(`4P$酔Xɵ8˴!O2GgQ/@ $e4}="ï U4<x؏RPe8n妇Y*3R/CI:@f 3Q/l@(`Vw.JhoD0Ob%gzٟn_!;ɐqorǁEq$ v.lw) y\ a>)E(׈膸X~€\|C])hV8\cpNBIGa Ԍp$@TҔ`Goޫm~loCHV&M?hVv77v_7#w$v^jT{N` |]u!h. \G[b`-ͭÍfSШh=oַP y|_n$tis렽qԀtPn|(_ۀo aoP kHmz A{n<<::>j{{4_6ڇ{48LJqNMÿ4Qxhko.콅A<ס& .uFc7#A}ӆ@:ƑU[<;8z*vۯ^w7ڐH![:L!߮Cq?Z As'^ e a7ry!o 7>i[F sZzZ6N0i~) %~-[ߺQT;b RCqgfg}KRsTz`Da 7.`ܻ{C 7t7<Q8g8=zE"LY.:/Ѱ߉V$HA2JWzb@4,=ܭ!0G #x8ւIʲxZz"DQ`i^/hv6àw!n>?)@M Vokux Sxq{6ˆV(֟R 4uVǰ7BqIhhq 0U=p"!5Btry8Ak L`I p a&j}<5$4It5E㱁0 g,j*'K'˧* T@NPEH )G&j0|)t L"H$ѫWSuXn{/-&pbcS !,kM[b, Ng jr}A q:B}dFWE-#\9>ȔZ\ V+ߖq ,E72<%rpլA; \ه'"OHUn-.a|eω?bX?-r]*+?+sUe*$*2GIˣQ(iyd,+ZƲ\,=o,XP-?< Q p&IX8蓐"Y :'5ȑE"5tL 9&TgN3jm1E/4z^_I.pF4bf+lh\&6G|3B, 2OB&KHr{<Ax~5a]. Y-,@)UeYe$-.y%偘M5q?6_`k[75j Ҩ2r7k\Ii z沂r6&gb1"-ґK Al[)8 "c!_G#`f`ߘ{&`< d'sOeH"P%"`d`].zJ8 _Sw=2'a{՚țv}ˇ0(zհUyVo F,mX7nVſKyn8~WM.ՄHwrN^4r`{ĮAHp 8A~Mq}jG y hX6Rp0J]@[j\#IY}MRRxMU4˻Sh<)%a9 x3qEb-H`j$ܒAZRbBS%Nɠ6= .}ZⳳU5e!iG9bh+׊)-7dGjok(9dB&pJ&L;xpkkYߏŧ2dH\)h3xz]D&RA_. 96`0%c=8:>\ՁWɔkf@uo_!h~ݭ < H`j2HeP§ w 9-7a*pxò\NXʖj0 m{L1DLl@H8Y֓4AW[-=!rp+S mo+v ^DtMt4 S^nDn~\>/Wyj'zSËJײhek &,.ռP` L )x$ϸOSEWUAov@!X 4L9 P+YԋC/H( 8o=[cTO !anuk~lbz[b`Da:^fB[L#%$:e OJ~2Q  Only\!߂iNm"Cd@',I\, ^zO<FVY(( ףCFI`p0R0p2` (L.Q6FO4khl5Bn |DO'魘2RZ!h{V`Vviۿ뙛{ʮcr^?6vv)*Pe<~"@`V5rTC9n T S%G{Q{q?HΦ7b[|*E^NsA:Ƨh!E+LG(U(dcl %Jmϗ@n.ɗ(kƣpX#ugڿ7PI+7T,bVԩOaul<&+J7f7Sw9vՍ9> B?mڃlqƇ{D ԫ康R5+?a |Z -X\w)xlZ?>쯿nwۻЌ"?EMw+qSmQYqlħ+=mI_#^ef;Z,u:/pGf NH#wI{Οp/SǒDWٹ_LX|*y)N.hZĨhu& ES>K: T=-WW+ Aٖ!CNm#$ldjKaU1/NJZIXDx9<"67bsc54c(& "&5BXggYX Unm.pk ,+\B\k1} X~ŝ'r+QVPÂF Z<4^\nsP9Mm|`=(KsjeK2n<oo6Se hx(?|JNg(80J,!W-`%9W 2x]^%7t*antE!oߴ}Wlos/mzmrD/כ{%'fjW=n㝗:lobc{x67_bF BE,qfQ堇|"!#b1яfJOYœjj&_n"u .lHEXKICgxiR16+3hJ~މUk~&|Kls_sBbg8Zu({#T@ӟ=-Em=YO4x>w?鉮\=A%4aqgR$buͽGGGGG*'g+19Ƴ@G$%ilbdC\6P{]ćKpJze: Xf^ ""Ċ*C@evkv[U P[{(&@ζO(`8d~Ze(q{Be>,{$c l1#GU 50" PQ9Q'o/o'mmDO8S4LHaQ/`kH 4}t`Cރ`nOy\sG卛r9OНi j 7KC|J+:i(_[\.%o|N>R+T@2Ӊ7Z1Ynݳ1 y .l\Zj/]2^vg跜gQWj&T bMPpQwO(kUq8,Пe=Sۮ9Z}վS*tq4Fv[kbK| i;{= j˴QrRQڠlJYi2/VsE85;`_ >װOzfPwU=(]nVuY62d)xmzTxa-"ѓc0@(I0H0DX6~ҵVY8I!'.`OX꯶ Bl y{vpG[ `;{p-TrRԢqlb YF3XNq/C,O<@ZSZ]2r'I0TuKfS]Oߛ*br]Ȯo7/Fp+ՆG-l8 n =(IY a_rD{?荝KzRc2WDzÁq|sS+J6l <91< gw?[.'|m9zIDjJȓQr(9?J%%~we 9 )Rr9ԧ5 Xldlz}-iz 7ؚۭ7W{JAy=f9/,JZ.f+KŌ{ {0(|2\]P SÑⰹwiHoDS Q 2i(Huw{S|]PQ[mh mYo,r^jt,[JaYI Ntc[~"5,V&-{ ogrOGϯ奕uw62Tǎ_.;g8 I2^;v6w&S2V⏓SE+v:x&t^uw{OPtyZgN20'OO:':R;+/eɶ;>=j~rU.殉|ޚˀYJ o M-<ЧvB:v2^D7+C6u97B' UEvΠ4`.7vhh>$4OJ~-@Ecg◭ԆW'9/0 vU$OO(ՁINWL5l1mnEأ?\c29CY.:oGMC˶XDxIQIh-Z~MtRrtWA֝vq&2`R#F_6Iׯ9]"I R&DF;O xfA%Jy;e@%yG̒JX_x`4^ڻ/ǨV1,qӧh6rs2|8g*DrZpJE szb"KTytbT抛_.0VNh%*9eݕ[mJ;s 8<.?N.AI~Q/^0̠2J`ؓ) aW#L\f*,T t5{A҉{Peo Ag$pMAoܣ wl 'H0u3v䓃~.$-T4L`"D TS)a.;9ҫVhYt8Wuk%-wȨsA|B4nT ~3SsP(Mg{h=0g`2ؼ[Ŝ& 5oKZj&>^P8yX߹/‡SnLC_WAQTqސ0JCh"\Cfb\D#Bl U`KMS)L)i|pE!(Lj-άC^'zfLդQ3$]ucos_mhjP^ j*ZEjQA43=؝\&\`ҹ-˚)I^[n6ecO>@rAe`'Bs:!8K`&y4F'ʃ.:Y Є=F/t}h}KȾoDN$}~v"z$ ߐˏmFzJ/n& eng$^cm0 >:L;II)cjbt.˶!O":9_"|+靘~W3u1})^a21ZbzGE'sMXsPVm^A;ռk??RrlWc'6h3>ˑkrԍ.g ߂ Pޝ w#_ ]xM{O  e=%J=lv0D[W{U^C<"J󀐯 gٮrjQ^h<*r5yfX{Ýmǁ$+2'w8OJ~gx} Fi0{.:G;d$Q۞) 8Ƀ6 >~%7 ߣ߬ w # D*XP`<=C{drXyJޙ"ME݂<[O=eFv~+Wg:"mz$@ Iljf”CMLOܼ)46֏! DȲEe^X3`s0 m*,Hl&ʇ Ce  i*S6[b|$^S!E: 1}.8pVh;ų涵 '?NRhkhkhkhkKQ6gY3>Ί^JSa&RAK2hD?~ ->#vQn $)X*WO3DǙB]v}޳OEz%&gmRwolwpɤȿSE ъ#` EcUR%?Jyyʵjmb 7sk~Q*2jYZ_L@{6.@ưE\;9J~[1\V$Iqo: تBMcq`{1#~iw^v2ggQMa7*ǗBAr;QuwӐ{{ʺ]1{:R(WE$}4qU-oWܾ`^M؞Y>F0~ҩ|%[YXArY>-/I򭒟i-gI/h &&ec~,a_ܘ;-Mc1H%/>0ĝvGu6T=1j t"H9l88t>O< {8`"H0rZ)! gVUM%0l 0/ y g̅J}R5Ӓh" wP:_m4ĭ ҅C3wg\䁗>C!"Q >浂.]`Fc75wy4R<sX>;޿+ZA׹CkDѝi7 N&<+NpfǬ_T^q@DZRmѐy+tF/4Z/\β" ᬋ rxEc.K.e w'-[  e$a Pc̙HGM M|BEm;a*H0xq0faz?RR!`8> N[L9XJ5j0Y dZ0^%a6B65at~ф6GI%$N8\0lD8QHl~}l.4%QF G`7|׽U卨!aY*[E;ͭ.[Syb'ӹ(f{<3BKipdd:fqoSȓ4ǘֆ G!)܏ l`XЏQ6xl"T`oGZ2b8ݪ XS⸺22tXU7PtyLD"콗| 2vt#FA7Dk剝r/ & px\\ܔ^x(go^PtSHCT.(/EQ"/*TF8C7*2>: izWS d?kPF@Ž5(Ky*<[^:i)Rt)x]c;^\%g ɶqG'\GO{B'M54UBKE l4¸EհH}x 8b *67{oQ+P />iWjTT-xd`DVTξ)W0VH,jۛpc(DA)%Q8}xP׽?@6trL2Dq@ jRZV?]n6zSu)[r+׸\Vi' 6s ͭ1ƲJ d'ORV32x Ir.,-`vpoò[C-<58o+z%WX5<.WRgf@DR1FbNsS*-yK,,Tѡ_2)b,a:wC\:Zcɱa>dggЧrv{7+Nڣfur2~74Nʜ㊆ _f\ϹCSlʗO";J|frHQrrc}[-{u/zez5 =va ue;.f7I[eX ;yn gКC'][&es^/~?_S ŃdX}GUDe@gTX}|Ēxj* 0a6!CE,kTfq4(2|[ b8SH~28 VbItkMt yfV-_lҩtŠ!)|5\kՓHSgԲϧtڊ1{աr]KHʂ̋"MbEU%9&N䖵SlS=RrbSSm%T(qdw<);'ݩ"u33U*RБWd#Lc~ T!!b 9H'Ķl0>@J:)v;ibgַ9Qt)cߤK&3FKdo3*f}C4xBm_& ^_"GAa|!iej&Ev4`!Mc?N5,0HiÕވOrw%(seO 8u+5o^_vQТ@X/K`=:hnL;槤o~ Q*r*P;Bw Y=t/j < KcMrWi82{.ۍwB_,v b^{2drAfo? ?OGN?rQ!q!|ܶ>&C"}ߎ۝^> 樾s}i|vPŴ֒`%A0^ a/@{Z /uC,8tu8Twsy^ﳯ9 :PfTk#+BkvmbCsmu^`z;8.W9<ѯӨRp~Z3̼9 |T=R(W|_$fՉZ!AФ\ֺs\|Tf)(}78+eDu+D/ X: i^Cz5ɂ<.}A Q !09 k\؇>g͜>KXcc Z~N}3еԽ=K@xH$m/PEH2%WǡUF R…_&]I?kp` leTe^?`m%tڤ2:jQҳ$>3`p^ 08&Ob(E@ddnU6ߵ;`yq\/5CFWJC~exg*\C#aC&Gɍjʊ/5. Q_::d*d(S\!*MM=Ñ8OC[=dBH:CU%]eeVnmpdhƅ]ǞTX ~, ,FȳaSv=/, j_u6^DRER&V(zC#/ø~c[ܜB2٠ˋM-{PQinP>,Z"X쮻 Hۄ v7}'o]*U,epVd2w' uD}VV>":fUM]:@<ſESWb=dDv QcG V YOF oArz fLi<A8|ź_ 3C4@wG J͍v3JgzjŠ0: ga/{F%=|*_T`9_H: aFj<`Sjki*kˆjS*ij;:Blpj2}m4;xzn{mJ48 >qR|.Lִ~EܚRi$ .'K$ $a]z}ҮzOy!ZŪ6vu2ėxVgeTR~q֖<_sr ه2A[IqEM qɹ4WMRe`B gJv[p'PKX¤yba҃Ql%͂ˆ5X"X)EaAZ2mቺgng!ß] +kQK)cv%7?N9ۭN0=}rz m<%bu.@|/vCmSfYAD?AJc!e.{{mhc,;X_?]OG_/[F`^+ci7{fw^kLT- P!=[?KH}vrdnm"\3=X-<;s~{z!#+x݋onlo#إoE 'U(f5u_s_i` iQz /ٙb})Mg[m8o1:͢5;D<'a5rZzm9k\RNMEYlrʭrl72?_vhu< נ_mP+ϳ4ieK+P3EQɓ'AjgiT74=]'x,=W3fG h5^fY*sISe>"FQJ9i*eSM Ef8M,=1JFTcH姁G4hZC*Mӣ*/OH˛|Cij*V  W,irvS|^7Z*tQtYm骖Ac(Oaf9 |3@tWIhquJ3t¥Nn (Zzr%kG\w\PfLN }W/VpG Wϲ5 e 6l$HíW(lWkO)yvJ}Ev>E 1O< ϭTFIBޅ<3y0{۬=y3ec2ݳ;% ZXWC8`&j qd$ 3f}#\pEfU$Nܮɓ[kߴ&i'#+ '֐dcUw:^3`[*f|ˮhѓL/rkmIֵlTqEi3I7C`m{ahc*CWʖ{Ate[U'U0[(9FsnnIr(S E QήTͽ/8" \]D'H~oy|gk|t"3C@Eq#Xu/-\5fRW_ }-AN'% e[K?_7їhVRlM/|o4%CɨgJw_G=}Kmٔ;Y&1yP kY&6Ar3kzŅ ^z::wYNpQkLS(礛1Y[Hp j}G{T{1,Z+&o09U^z87N=&zg)1idpêpNY2ѡ50=`Mŕ G .ڔ >ٌn˷z YHH}tMs]p{]iUyg+9;|4CJS+h xL| I7'j 5zSҟm4Qaܳӆl)W~͠R|nH q- =R,u ĆвPx}OΚsAn*ʐ)Oz$fwO׾i d$L%n*0EuABu8&s\uPxO*bM.@|ȗ94dG= ݏ9sG?G/j|(W ƜQ?voh.EWx5;a0|ԩzԩzԩzԩzԩTViGU 9/S~O:: ZO\>0uU,g1 $u_E򵀞5);B] LWN\-!+L$#8V9MdOg6a4).>K]b w:`tϫ?x%Cl@-do鹠pm9:0ne; I-gWk`F6R [OPelUn?@ߺ`WqaRSn2ic-廀[7jrq@ڭ)CNkTi4?.,yx=1:YdaYF7(/W.>kXDbr`yQIzC> #8tRQ1:%gse.K$e?5iΨIt.qq. _#lURr*L85"2idPOm^XwWkRueUy HGa#jGkXwQ]/J6j`f;}=Aun<|rATr쀎~P~OޓN߭3LF  v" Z>9p(^Mt .DCracQ3uWaVYI:Gz= m"NȎ} eoR-/3Qoĝ5bdr)=+xn0B/QSQ^az*I%eǏ5-r.G'Ρ`h9a3ZYRQ7$ $ :iEOa2=:N &~$'X4+B'FCN+ĭJ4Q|qԯ;: W\ ͚ HԖռIƝ% ,WUTR#OpZBdDM534fPkVªX$B#< 4f*9U*7mgV܌eMNK3GN/?mD m$ X)m:{ w$&1\# _|zoCP';臙QwMYk**8I1!VO8Fb'kߝgS-c4^K7,hy͙RӸ9m4Y8}N+zh.x9&&COiWWu#szV=9~:;HO>E,ÅrܽsL+72Ή"w!k{,gXDEFGyYsc݄{o#81gޜYa Ϭq 1S^V i/O'>*/E)D:eI+=/Jw 4Aэ%]4~9Q\>lgH IӥJ.po+ V3@+Rvst>Ж`#CgeQWIŋeѯlW:J?QXץeR^inJsR>h}2#h+EMLin٪%o=GIgIOWٛRbϽwJ EFpa] ^KJ %0U)8MaH:2ckz< =yrTg˒+w7CSWd^2"Lk-LmlW-,$fξzU_[~K(u: Ξ㙵rzX/h4rtиOñ_YM= ?w9o.=lc䂝nϵm23-jK ڼ1 F]{4 2EDow"P""졐&c*h`g:^2Q0LH+WŒ5*u~Zi /B+"RF}Y%/#ssf${16Y4u'ggPǃb}PaF~-5.M8|D~]#bɞ[8YRC]$V'B)7T]S=(+5<%* kCs~ Uaa<5ESF?Yq.s$9!v; C8l *] ?qb+!i9iW|Tj1Ho>ޟ` ޞtϫbYbSa{cu1] BɂYn L41,cǤ'&R$he7K,Y+imkUlUCoBVYryȼ{WOA(5X$$RD]d`h)@'Uc؟t=@3+ZHJ3g#]+9PnUsJf⧟4hG֯lrm@a3fϟQ6k{ %L5[ɝG$nzڝ 9w%..gꇽ$^Jf: ]_8Sܹٴ"dHILN[<gl쁐K}a䮇˹m py{K3Ud 5=2zcR/i1ϡ-G#`σ(@y738%_gz)Rhd95!.5e`2+up*B 6J3뒞_+H;iC-eG{A^B!9ޮ%aN3lYi4rZBB yٹ,&7;՛S9vhܦ>aGCzO cV<~2Ի g׵^&F*T/xE6nh O0. 7}VW8F@hQhD?aQfWտ`&bs -YOseh Vg+LTP>WNcF T5s5\ }]A[jN3˭6H#NVgI牪N_S3u}DozsefCZY$a*^{ Bk0ZH EM:Ă}3I0oOٲȖY*pa "xu1En=lz[Іg!0i#z?XNww;pKA,}.[Ilx?;7彰2[ cfbu2H]@mТ X 4ZswA͇fjel/?`xUcr 40T}ST!5pӨe]m.u}Aga t K3ٜ{W[[| z:VwMO|s|YνFG!OwFNrMt%`],ƕg0Y5_khg-,*%JE(D1Ew2F" 2(18ذ 0qzR2<x_w˝֌㒇'RPˏ}uFI܃qROutS%;W}X!NL/E7RQU{n)HwrMFP: 5$'r :< !@]eD\4w+Ll,Խn E㦓b}d InH.ͫ:b .LKctF`ׂ Qi>%?;dZ'K ߟ<]e0]|ν۲#=cvdTk LJrl+4PJaU$4RBвJq1ZWWWh8C>~4|1ǽtњ A EȚP2k* Fҳ< baYJ|J]WJs$FItA)AW{5W{$--yo@TCnwwj~D=GsOW˿%),//`nJ:$k=XXikw{J= eg3tmje8ԹDSZ>nY AD*fR>2 I}| amy"*EgzN3xFVJy%U(IYИT2ae6ZM5~,@񇧘c|lߣ^4t8#rIbQ/M!g߽v%*Tw0g7 Vfg#`?aQ+lܳ3`E0稑mw!!Rixp< 0H"q(} m^+-Ed!jxmC \G'Nl )!ggaړ;3f8tYKܹ, {&t͌ ~)Ti ?a,yՅ4 TnoHULUj+8bsOn%-gjcjxkmUfhwg0 0bF9 cmW<<#:e,tX "/PN]mP2|\\`&<%r9%J:DHz%F=}^o6qDG„L 9TƇUQsXql6C8:3 vל?puh=zlJxbt[e[u &//妚dQ6Ֆ$vm5TGUOa "돯½rH)-K!v$6l=Jpp9#;LʪTԠ`leWd\m9} ᦮Cd}e~Ä̱k}r!յ[/w `x!U,D91T8G Uchur6Uz ì :X^|7 ]PT)2{Q9Y 8'LI(`@K94?or^8pP'$*ϛ {v2CEfgK}]g?~?>ĊVWǫU*xrO$H7B;_˳/wb*-"eDI>R*jU'!z K%*(郱7^W?~,BҸ>pd$ B߀ )~u+c(:L\TN[Ŷ.vDգ$%p*:tWQ0ˈoYG4Pu.Ђ 5~0 V?G1A~[aJ;)#Xx\3H e*Ne{yy k q .x$\i\ƺezdRi gO3UnI;Ւ\Is^V-;'$0iM{aF[֩,#TR.%>ֿN}TMA-p%&*j/]]m߼h <KhJZVKzZ e\IJWV ^q_'43@KTZ^0jY#{S8Jd9釨1HR#JEa?U9@WrtrʝnnvQ;*>q.i*ĔИFh¾вX^e\SPAX-gsTH/,5}aJ1MT4HӠu߃s9ԫc vyz":V'I*w"Cjބ]vm+#ҁ5Z_E$Ev,%maRwi3EJN.yJ> !櫫JHyB]p0hX$f K 5EHNٺ=Ӓp*U ]ZƬKZZD%.>@"N4l`~)Gv03%n=I>!ƍ&glS3Cx>,FӞTm?ÇL(Q\>rF =JvFp0Dc?ZBàaVU n9@uN/ˉ g8 2&l%n1NZ%ã ;XL4Lٱm !cL:Idlb_6N ڍUUJi"\R\>T$XR$\ G>.ȑ;TބYزSKzg^pQ)ґOpGlXW%=f~^G;,(4!$ {1Ϫhs=nHY5KR+ݚQ3GJdFмguY9H..}ȆAIK)`Z`C$m*^\^bTԃ"ECFJϕddSgchJpyraz]4#: TYXR̂t)%ÈZ *)s:2ԱqTVLר~:Sek<}| |kH=*QS\:B3MZ}wy33Dzñ޼fh!La7K_ nz 3 >@vpX W$fHlO Cw 9I.{m*{؝D8CmrQv u,g@z1kjG7c#W)@s.d׆Iajඹ50̀Wpݹ5i:Zp`nl.&, 3'5(Kd25=F4Di?$ 2ตH +a [kg?2a:y7 /)f[% qkkt‣Wfxw|+w#z{o}~9ٸ*BLunw::?#4,Wp KU U U U U U*-@h"筥o[+?Qҍa?pYٲ-Fpz)|^ gȝ@, Ke09>^v`䝯 ՆUXUJ:%EnTNQkXEeSҝqkꔳUSb~qS"VA4ɩNI Y%1)fSLuJSTW"+6-b)&iWRɅnL})9h[gwӔ[l/+fKv-r*@+)A<=a*j8u.!7"}:'?\i|=NCGqd@% R,{`xdu2RbՄ8SW\N'8 = TٙBъ`,uy }IgbMfJ" hz 8Ѕ@lZ:T+j@lVnjG˟+\pi+5B5‰F*BAO%2<3v4, @7Ԋ>8:6P=do{W4`g?k|C!Q1ϱ7A dاOx%0JpT@S|4 0G$k*>6"X%C-^~[L$rJV`qwsu$Z ]A>&pIZGa":o ǬäYKtDO>a8GkQ3xz=dͪ=ZBy9cjRAƜ|?F)qQieVDh$ȬURƤ3Pi%_9C-! +8h+{܏x41jĎ^ZZo|Mn0F4BUqV[nH1mHD=}=em[]}/dqa`Apwg*&^ Բ2GYV7gh/eGRáTBwqJ ekbo(^xy j];fdZ;4\ F@ Nr0=pT'bNqW魵xJa RT J Qu}jQ$"(OlŸcd->~$guV,;.`2Mc0D]c FF&+b;D6Ӽk#[6`}ƴL't {;MfGN(=`ؙ^ásB㩌} 9+if '(8hĪTP<ѥ,'2;Jpr,DZ=vӫB~B(w˻_mLJ4CƁ@?Y< 6wNw*qVU*kBÒS$2 \,b~%:8(1`/7Y&V^k޻6# 5^<4wx'v&ޓ۱7@ch8$Հ13˜3[-RUL厐xJأQe h}g{\zseX4>2Lmu1jfeP!Dؘ^k KsT @U%N%tC/3F323'+>e}՟3ͩ?߿E#6'W}K>07 (b ; l%*&U fkЍzKiKwQ۔8FT0<@CW!a& j$V"Q( @~&nW@܌l :PT pҗϹKJdLTkыtv-YmC7oe2}$@?󖞬6Ǿ3|B!Q\ħ+&IYCL?g ܉3A6t-fwS,#9\rV8ӗ25e 3ݏ"}:iy_ߔ>^,CF*6yoŰ?5Kfxa~ZщϦ2mL`k EC,ElǚOKe"oG+m: 5Zzv-$9>R`p;Io)΂+$ .n)E6ndib""A.W0(Ԗו/8oϝj}I-(Là R+3x-Ԕ!5}69w8gjPpv ,#Nx tĐ%vmAyQLd:@;kZ;Ɓ[Ё&vo5C"5 .Ǥy kikY. 3WOe#?t+7OՀ¢X~v\QR D.˼~Ս` )$2CVUT Oא5cj8kN7RճWbϤ-{xLl` -YT!UV;ϊ7:ek=z.*Ga\-lI~γa+?%URbyg-(53Y{qI;< ׿kQ^* ZB!^aHu%5Qn#iC|k.$sXRUXFjcFa?+T;KܮS ]!S?{2@xȫ |yYr(ߌ9iélZNŐfHͼ]hK S~L%JB0-Zgk# aAUaGIbֹY=t9C4 96 ЙD䲆u Co/X֚JK zS{@ )Lbal$uZ]@IR {`}‡>>*zbSC[WS-uS6zY9 _(iU}2a HdAc%)dN~ߒ! " {3} itO'Ѷzv,"=IhC'ïy{[-wdkIeɒI§b;Aq(^3?X`"|R_^C~U-uŧh&R}i?Θ#HD+)(%3P80ԽiY$ʠ'Gڬٖhm_L  i0[>gk^>r#lH8ED0ڷ{:D sg6X8؇ FjHiS-ZLяab}wl62mwm؈;7n$7n0A+pNM*fbf/asIw.->}v$͙)`WpI3gh>d$ 2>]0 |N?CȤƂ"jq㴳GLo!WC <3B3D89iw=0&"TPO,s4jE*Z2*Н3(pXJ4Q_0]1=*`ƅ>dCT6/9LkaUR`JVHeW0S07GW~o%VLa253ZZ9jC3#s2oJz32qɴ}'[w6i߽('E-;FNIo˺\8q]FhTH1MOQPedݤ!`.֊w2}duI|-Ý^Te7[2Mg{& G 8gPݵ,FY?,7)NR$ /˔)!yo/4#˵ VR N]D-5nD #qz2SCInG9fZZ4s*'s5pe 6[YKPMYKcP`(WR0f1wou<%y\ߞ0H"҉<3>Q0E,VkT(Y|vS Yo.Q!!JL||©c'q0R{?%  qnfݺYK?d1N|wt>|PR)|6h*\R~0|gm}rs6}Uݶ`A2gl;V[{;S^f$M1Nƺk5~[ݮ0.d]Cs'J~k>53#9GPc3vOs#y})S=!Y)qC*K&ob &M rM)eA R`TÇT)VKC'7ì2l"c?ޘ_idh,{,w|LkwGX6"õ~EiJÙ.6qiOjv#SlZBF zXbK6ZbY)xAwu"hJ5wW2׏l->UI=ڃ݈ΰn즒?ʴPgfH[1ܛOT2 ƔLoomL[Uyr4wݩ?_z@Y0Ò&yu0 &6aP -|6c~9y]$8*f(7_գs^lz8;ܦ5A{{(.s2V֪ cB*9i3B˕t5ըXӏ(Ct,< v4Ռ[^8w:6ճ7FvG.*(K)HhQl*km ӯfi?MT\vd=`uAK@UvˤpbwryȯLE=ud(hQ9Yn/+X)r9k{sVd%dXp6aJ8jĪ|PX?,8^=+:Nz~ zvlqkbssr2Ews&4f{SM`֣oߥ}ulX~i=z 07\`E]@܅%0<5y5j/<*#IY9'C ,|T93֌jFpC`U3t2/ ~oZI{bF˾*@HiT۔^+÷a2zW#'FŞ3\)1OFF-_氠2b{8X5,F%"F2k-^ȡqO`1s7tӷ3or |eݰBU`J婧mYnTKT6ueLEDIWQx/QV>KTkYED1VQx/Q㺤X-|΅Hq\\厵ű6[QxoEyV8[QxoEdu/ tWʌ&&Gx̌crpݰMʚa_o.S]=]5;Rh6rU,9A:)o@\p,"5U=]5{ f6!<^3 <'q67^G%*.Ү~<U<[5U%Kff^19Vs5 qYll>$HOʼm$}b[F&d0f1gK϶QBu"4"<˘ Ƙhu$x6<OOZ{ϘDGVeeVlw[mWH_EXCɸڥӫŵ~c|³7_] "Ѿ 9{9 r"j!H@c ؛|.G%d<Fxq:iy,{ ,O8"ebW5 .>Q(dc-nNS"#a p.ĞeV@,blaRZ*i-0xYɸ\UFɲ9)- 5f(qjst^(?W2^/r~ :E[buUSƚp*.9:"|#+DGG#Gqrs.^? d2EZo"bǰwd]rq%cRSRH1vA;s03*/FqHD&ѻD/9 HrR%Qr>|dG7FJvW] ʔg>CZXR%k;NJ*DU: o_oKĖ5T'覘(}K6Vh^ٔ~psVjH'R0ـ1S7f*>`VF%LJ{KP?HZgE+#AB^AF$oT ]No4i ʂ[ (l ; 6ˎx3)#L̳|!7C9woiaI^7Y/6"(I.DjÜ iV >l*xnއf'MqRCSVfӰ"JNEєb}Wii A::_KM4_}*r4{x9X +<8H&QPu96{h`T 63+ o &} ⒋y<(lH&ԺAGFp%\"S;iJdĎLY|4mb>l#T*:~SJG{;ǻp{SX`ƻdZ쭅p1MOJ }ګI8";~39Cs?&A&)< "fD]"B3R-PKo[p%jθ*D{KBXZN~R'0aE˘:9 E$"B|XxVRrU1d%n,UI {_GsGX4hN:֌#`l5,r웉#q8g ?f"~2;. ޥroA XQ[xE-jqkTg98::&PQ7 ߦr,ffQu7kͧfe5 WFc,:%veJ;h T2& yAd+Яbgܝ̾힋߳LKжtR4({qؤPeÊ!~`)o!^ ~dGrX Z%TH)BG?r5tUL経-"F')u)l)zOk_ɡzKI.gB&~1%1Id#{}|0ݢ1Zr9˭H+q##~B hŏp1QcSqib?i>AjlKhbs,,+mf}҇0T%\ʋ27lvn9f{^ntsIVNZmT6՚j N7+}90 ,ytLe :hwWZ768dٴ3_:$Üzv?_! ?x;10^VAȻ?C.ekv}y|PђKQ_7^p$ - /'[y0`]UXE!ҝx0?W jKa*ə#ơw1E#]BC#q? fkmNp_˿\]2rÄFWU+3yIvU #~obG~P1bK #$?0y蘀K K!͙*E~[!ai(gSvOo _K/-ݝ}})T 2ۘ߼No/\5#,EE]Qȷ(O'@aD!1qD/ØQ_IN&L7+:)ҽT;1/-FaP#c~9}_?@_8ŇXl#(O7 kYˑara ؿ>ɓMWV8Y{`?㣍GG9* eyg QQQEɸ,X]'} Ü߉J7fܫLzN)]c|Ps]WUo9^rC3{*=%5Q*P$܀vu7=$*ֆzN#Ƨ|}-P~[BYt Mk^YKMf~L&.' ckS1y29FY.oFߣC4-2Te-F`VI1[[zgKh7.|t Q 9SˋQJk|wG|uTB+&+tATkjWK| y@ D-:AtA{4%1qwKF S.bBp. p^٣X[Z& G2X&W "{y렩_}Y!l t2 t\=;}uz7śs3ywtvq3woOO'>{m&E1F{ V%Xg }gmcYrn0edx턯 EA4΅OwR L;׫%ruݲ[KJnBd cJlC'ZW5ãakl4)uzɭ7s9^%bz\l 4Kǡ9FVqI-(Ζ(}ek+70ߋNd:T^C/5,gN8Sb?t^w|GJ)wAhxwzr>fc<἖Lz#~ܗCw"߾9usrFN̂~ceBwWdbnmvRK^V+j\?j'IH!?˖4OM%ȽK97>I(vՠ?xF3'߄&EA94f9{:_\_ZdbSx)_\OcO , r/G.|}wדҗC>!ξ8Zשznv?R֨\PI8EлM*Dl?=(f|?]hv˽DU2 =WtI7B1p ˰\7H%t u8;s(W氚tYViwL 9O<Ƈ~C?[-R S ǹf`~#G@ k=̱"wuJ(Fwa ߋ\F:dҧB#`$k,R> pu^mHxjB%<AyֿJJIk.q29ʯ_άN i՘.FaO 5`i1r3 `D+~OIDZ } 4WmEu8*nL{e4_Lb{sL!i )HAڱCj[nL[r+e~K?M Vrp9ՒEEd%- R/Y&_7w';oF/-YNZV`-~5Q$7\IQonlh.{W ,bi"wA_;|Q!2ŵm.+/_^߅*ֆ=Z(3)#)m߅jmIQoS֔&)G;XdOOZ]o7`BDb@g }3 5*%-2ۡ'ő•5W|2b ԛ ө Zdp1\f=eq3kcۥuo}O pN!wW%_vcm6ZUr'%B4"lqicʋ5F)G 螄dG@sc{2󈎛&ǢUo@q(GQ/ [ԧB-TP䃍ab>CQxAsZ,XBl,gxR VIhL:aԫ@ bt٨ [!IFu%DE%'%Qi8o74'|+/^3F a$:"RՐGMc fLJwwT[8p\[,V2cqJ~կn'<:̠ZoـAS*^w?S;K7,cj>Tkל#h$Y.3U7 [-DeR8q~Jn9MN[4˙С\ʤ]ٟ&/_<~Ə+JZBL8 p ͣ.AHGΏOo~`&?k@"c*zsYDf9u&NK=׼0(;Ax/ѭD3ZO.2wSԓٗm)#ht 9rD0Zbpt-(C.h}S ZxWy/l<<8~3,Ժbn)M }$UL)6%϶֏@OOy瑳8^Pk;|rth(Лb#9+X-[I:.hY0UO\,P9iV΀oqiLEEϹ[A¡2aHv;wI$Jͥ4_[!뱌cd{ld-'A1ݏ8.>Ӵ=o("X,qퟤO_W蔘v!$Z`)yp,#E8Q)V3E #p%ފvZ/<̻jt;O~OЋiLS>\5PW<Χq_Envhn#7iU&Oeq uE0=EKK]e3lY@y^v9YŬ]7Sf/ARfJ.7G ~x$e$7W>^z#h8 RmA|$* 雨C2/F.gZ=S ze( 2 |O!dyq Q=s:]jDo!#튊JUUl syR?Gt =GFc2NÁ霮7E;=QzO2H>b+{_т_RlQ>^"b†"|%D7 \#gbhcrj)cB}ZG9|q2>9/F}}ί~ʡv:΋yp\{"x)*TqY_@Cc"5K!e 0V{a L؃doDӋ6Z#D#*ԁԨ=[s26'Zoo  sP {ZA[ ;fiתͶ|[u~kzF36:ښњ٫7{U˚sDe䢴ܔ2'C>fׯz:4a5t[5Y)%\L`[5;=7 y8+E@xnnljGZcoڨj_o'G)t"`1A(]GmN^kخFZ߯6]~3Nm@sL8.̪ ;"gOn^/dži6:nu軀+7֫}ռV=\׫Z=U 4^#]EƋ7}nq/:hB*ÈaꤳngX[W;Zv`:\^YɜztszGu9:Xby#qthp-b8%,[r WT_Q?Cn˯x7lպ.XJ۪{nsTm65o^\/i-V5j(Zs1djPPJvۭr]62} &jwC^_oFm_j~ Hd]@^^jJYMiB C'zNpFaMjpW[I\ă:YZCotaѯu^c'=Nь5TYO0ՁmԀ5FatFfmST^y# ԀAo}m%ȚFa/6z []U\Hl pPnxbЀn׺i E UX­wGDl[n"Nz^vfkidX:n7);:!::-DEq0jεJ$$0FJ&MW v]?+rH#Ar/A#:ȹP{Dj~hL/3+P˜C<9M+AV¹߂jX RtMr蜇u^M:X0y9x+j%\X V>Z^YV"g'GǯOzYg"U L5ć΁u&-1 " s#4+>^U{LvO4Z#GY} 40bHv0 #ZA8L#55ȵz?d&~^u}T RԚ,TUn &RjX^o :No0F=%udz,ߠ'E9iq1Gs ?g2+W_c#ݜ9HJKYP:ŸL!,^ᙠ/]gIR}sof׷NސWo|q ,*ɴ.,; :( b ~h}?*H9dij-nƯܢ82qlD."37mx ˉAOAdx[g0l.(#DN<QĞ+ H7%{ -=㚂L D7UM%#,ԤЋ/j:#N;\dQrD4x)$KR3/` |b3П^]!Jƻ]5B!yd*~)a-I&WpeF]ug~Ov *P.ÆݪbLxMSX$ĤnӵLR 3UhoxT~R"SeMi \BXK`i?V)_ nCDPMCÃ*|@<ƜR uTG0JZL>vtt$ٛ.8\e9L%2Zd^ O9Q~pLx[RR"9_x\\/mׇ#jQz8>Xz1#ob]QRC ~'>3{MGY7ߪ"0PkOPa eJB$NЩ櫐+d&u㣃6␰87sjDA̸f cAfcԩoqHU5R_[=GbZeeB$cV$T܃h!7kT Aʗ(9G5\O/$kK: F}S0F `<<fGbn1_H*|CqsJe )pQII)kwdxϞ=q2B?4s+SǦj( @皏ߜ\SytkG^{Vz6 RmIsEw?ǔj-{:%s`t& 4M"L}I ]ɂC! F2ɅPHxr"óXE&p'{-T/9UFxTW t Mlo^j~b!xT*ȢȍD[Im;`ݲ- '.g^n$Z0[bq:x$ƛP{neuT`raL~~aK"lvt9~~Vy4 >R."Ȑ9v>M2'իuɬ&^K7_E}Z@|WOP;K<2J[+LF;ί^8_sAmH>]gLCw65[MsŎN&QINNP݆}Q 0*>UJ|Ydvѹ?je~$ *koiPign4":1Bc\QPY1/j{PknLW]5k9^_*>XLcWx! 䋄<_Ufc!OkOBT6tq$p+ҽWCm ։;Xg(]p}awjw zkE.!%I˻߾ v3`z=` $?G$Ųڍ4;Hо9 | '6HY^v]Fjr$HZ/H-Ez?Xi%ByTe}Aw;ELl*cdbnx^^Ž\ KJqN07P!a+__z\-[#c:Q/uSDQK7:F?*)sDua|]N #>=vq /x0d>XNdVP&Ԭ $}e8OAP&gدY˾U`6b@IwHd+_`Q>W m@i(ݝLα2o\ݑl`F9Ç@ ͕ . /RQ3rD$+i -sڝ=oIxu=n2L3zyy>2 3p`j=9ˁĚsX8Yi`?@/>⠦Ț]db0N B g #bMڢ $,-"2fBwăhS05;Nhdm0fNߣ|]_鍵{vO{)\fQJtd dDO^֪ l[׼Tk`wnQ'J)XY+2 ;D1]!C} x29*TD<Y.g&q! F>W?;:Q9_j<[۱*6ѻya0B2U[;^0D5$ؑȔPLL펒LN OKnAlrĤ\.OnX>>{` ]p5M3p kt,•B4N aBR""#$k Bfyn0vx8>fyZUiP.Iդ?kELHbDm ~Dv8?@:Y;Ǻ#T򔥚yK~{+?2Yam/1S+W;Z;;`WyI"^ sN` . a #,(*ҼD)HAjlvy^xf&:{h1救ơs97PBлBz8n58ϡ<اp#Q8#Ҟm`_H)qs7a3A,< O"խkgc愀 |vͅ'"q%@rsc^lo)D4%_G2PnwEM^tu<HuK'qeװHq4PD 1x]W{.kf+y3@oÈ7 YmKȎ_;y.&[m؍hFs jS͓DW ,I KWjQ_Eq4Y+:萊X ?(hl`0GC#b;IHUݝaյgDϐGp(U@#Nl E1.b|ݛ*[gjC&TwΎPW05E"j]'gi#ٮi~l'M݄A6ٶؚ%]S TwUo-.b1󹔼CFY [y\b$G>?zqBJ'̳V8k6?S~2kN2llf}Gg&cSqc[V:8Ucd% 9p_} ͷ/^gSj66+P E 8&UNx+-jA*k$}EƊ虿7/9ex3u./jDojmВY&wsp$ؑ;N}s@[:րB˱tR%fk h$YscV۩Aj rjis9I-Z@P:H3 J@Qpr[-c 5JW8ƃݵy~Ήi;VpjM̌iyO0;i85֢^(OĖk{E ~=~hdrS#jz#aFJhoFZЊ)+qj -Dĭ`lt*f>rlJEYXU(w[jJs kRv%$l _Mqp+kTXF"pQ죝`'K 1G4( y YC]0ÁLJza$1 ZA :WwH8Z'|*XߢC{Z1('^s9껙I=#),T'Q@LZeѧ`FdƔih qCcMӸn]\pLyJAOq(̣[#U6. ?Fr/"X(mM"S2A@##ǺN?2ZnB*k,-v$"tvx4vchۻ8(P :8O̤8e4@ͥ*?KkmG=nRT{XQ0w,e;{vL xgk7mW"\Jǜsޜ.Сs0)OaXQ +0U1OWeJG@`)П1$m.t$eOAavEE)UڶY^J.m ]R[vg |匚wbcA'lH%앱ASvmjk Ã+"Np1R)7!I[sZ_mk)N-(LֺScn(ifAmkV|]=!m6v[v39=+`o#6/< ybA*+lC ]בֿ%JsP]p0Ɏn֜b9gŶBƗʟ0`ubfi-l BC4i2y!0?we˛MaQt jsk(3 y<j4EWk{~θQ8U1=ӝb .NId>y43 JT% |͞,V3``ދذmODd zs&p!Q:X)^ViI%dʄC̑QQ8$B 6 "p@oI5 ,[PoM3$:n䎄%'vg6X.fĉFٷl`P{Tf(s~kkh2Yejrɹdڣ̻W}$6싺'dKXV+S.]^団'%M9')vfÿTL6U n%^\B+Ʃps+kSߋȫnh 5vJ.XRly_AGm k$a`&hʽTAG{0`JYpA=֊+4wv7*%y]\K(k0ȭ=ibvUl.3^H@{GƼZCJN ؚۥ > !@K"Qh| ƭh6o8'D džd|.(RA,_g_@z@ 1j[>s0Wc j_\ $mW.h9OZ49}:YFMz g/ <$[]+LG*ĴX PTj24?fxdZF"N&]wy Qw #rb{ѫAY8<.3cBDp&Mp,ßw<ݤ> d/oH!rR+)5r[UFO`m v fZat+!#+;BePhQ@u+{'%BX:qF]/,\M:}SCѵ9v7 Gn4)p#z;znIX EF_t?z L܇Rl*wG^\;w"NEYwB\᭙ƈ 60!C4}ڵ:JnLngM$U۔)qk W EO7Fl;]F"ap6 F# XC@sd觡nT`ELT ɂ;sߟ&s#|^7E-2>YU#gUI*!AB(IMW،P)0]Pr[<.\ݑƲ$⯯_)e'bi"5mSU-Yl7ã- 7ca^1ٙEXqv7g^Ĩ1`{qs~}l/CQ]]e8#e\o6\]p;74ȼjioBNHEV=Axn;WJLV * Vlr2?qAc|7wV}W߻=SaP:휥.\acgDG~? +Q:"ߥyљIO[滒[LӮa!yL5WT(#ZF,nT+J$`K%M3YǶ@5Y4y cV71+9okMDhw\-6 5+iŜ{?I!B,tH""SO4:(xESV9sLE0&^3( ]U)ٛPN4h`t&xaV9cHR.UD9yUop/)J+(Rq+zmh58nM3T?dfbl3ZtdUihc&5{ь?f5AA'̥9i$?:p9+> Tf+\YɹnPi[;u<ȰEj&Vl,yRN:{lDZ5rޏˌ)I#s: *NN=ΞS!Aa|3eX $[mr h#t&G{U#ۧH1I"CU&9[ IΦ5| qH85n2ASr'\$g|x35@iSE(ʧxU TK^/Uw6*ό"SѾ+WwU{cVEC #SÁz'm$]׬N /TOlȇ_oU:jn'2'%H&WB7 <ytoP/~UvdAfCYl(!}pyQy&gs.޽K;Αo:@L- "JRmVcqhfݟF9fd`p eRݳ)x( U1LMmƝZ 8*$nΑJ3l|:0þ| u -HT8("k?ųߍKuF奚 Vt GuO My-NJAj SVE*An^ NrW Ԣ) >H0S'\T BXiWW&yLlUzLTP$ &;ϻ 19"ry+2j[}ݽbU'dTVSPb*͘۲݆tfLOQ@wW(Gz|+2#gx+w&,&V&" d?R⬡ P¿PUﲴ7JɚxH&"' a{›bjήO#GH C>X$IJzZi1N;nDɻ0*ӊ|Ϭź׭7xR}syyyu]-9/O,9o_8OΞ?:[{qzz9.Nut{-Z'9}C:'9ys᜿?exɞ]Az>> {kCηc>p>񠣀6 8A'L1PS/9[ZШsbL7/9@P\}ٔ-٧T‚Iws4X9D0{V:*10E8C*dmܤS9׋Ŭw@1*/nZk~s@ߌF<\.a@HN9ē ω$6Z܆KŊjUg{l3) ΅-fBӫL C#X"#B=S.d|ϯ0{S,%T_;qc{U!9(I}jyΉ(DBZ9$dvNR2 ႜ߳~Q rhјS">j6u #;s+","=~ C F?$ҫS8Wo<̞^Qi˨{'ż &Rd l} S[qeuWhCM !m-{$&4vRioflˎcv!G#i4F30h1y$P$ m0K܋=,^fQá=B&߻W_6À(c  8,HkLK׾{ȑ /C: ON'?Bzҋvod`}`?zM 8SD nw 5^ @vR\l݂4֒$xmߘWp)nDyŖ[>x3r㷰4`Mn2.&)` *ln [;X ^.Fʰ_0+)>6t3'\DxXm=&7p G[[77`Y ȍ_,Hv ~eȍ/l#"7Ƶw-ql$MWHBN! ܩMX86k0 (N@}?$`Lwû+{gCfp GCv n&^eō j!m#fqAb=y)#:P~ѹ3+I >ĀkB`!N- E3}t3mP>ƕ&UM]ZJj{KOTŻk}(QG5{\fݏ|[;0iC`9;>^rk刽P CbTN;P^n퐯̄Tۛ~]bϫ< $&*AR<7rI2 o7%x_/{oB3ړFup*8mkF&_sqA~GS\iq;Yds~t D@w(akAE\9 \~xvQNZ4HǯXXt T.䕲z TICC9zzYX^̳܃<b!*NFg[Z2ޙt"ރ c/^wUx_lBgG}b})Qm9M "ZIQ5T;XY`!!A'@.;|{`'StEg&!nIpم!y)LsVM9Tl9%#V| mX\"rF}lvs%T܂aRqܕV4Wp"Iק-zW҈H aO; 8QM8d8;|Zz7#8J,9A;ĉx#b["+~go>ޢ,lgAM18K  {9 $6TE b4]Z-qfgi9-&2^"*|3*;L&) jTpՕ#qFK :h:QG6Z$gnF>|$n{U Z})** %9_N*q'R·);`"Yv^E1}mcTiA*=jZQ)ICZ*g苰\wz)~^ovb 3s>}h_Wo6W򵫹6gb8n6XU3W$P-KeVkl%[_Ia19/ (yrsY뺃3;P[!']O:"!ZHH@6TI^)PgJMY^ *n;˖ަu>̿K^j&I_i-!ԇ݀ť pUBA|dⷹ ̍vsC!`#G2XQ>Rd3'"F/5(^yl1a1yP0P4M.1,Xɂ"J2 1hЀB rM%ʺA'$>Tq[nLǛTH"@{z5޷^6:7G{lkؔV Vd<9WȦ#:*0JBdow@bUMC6xE .$&"N54AYxN@SKF,'^ ǰh1+օs@(~y&4C MJƶoI|ФWrïx_W /ř"u઴c˙0}+f2`"pqQ]~AtԸG!bEԺMF8EEl yt5wX{ORJdM_^8 buk@)_~#89s!rfl'{e|h(O%46ܿGZ`;G 9LY#E̳7 aZBϩ [= \&j]Td+Cfv ̍OkoPK\FR=J R?>g}ٲ|V|sJo aP$SM:e۴QU?U"'4>6wxB"ZwlOIr ˖Y`#lSoz ::KM|:7H!rnfG}@l(né8 lʸl3.q᣶?ˎ>G{[G~G7ckS9WfQ zT/1UE,|݅7оHeƏQX[VT}ߊ&e"վK_Un ț%7 S6?1=c|Z+$J׊wGx~X^ z03oiИ3 qJa _oN<_TEUg1(SQ>K$ByoU3:OS{jn $ zxDbXhc*("‡;GqH٥ {(^Tl 8\Ǵ(:Ǿ)C()`; jAGXC8 ^C/];tN ~ pd嘩?-&@́u޷axpsgrAF5NN~gw٢s8D6Mڇ ;:x~{?ɻ#(ymF^o[Gmß7N0{.j~ͶN5l6=/?pR:z &!)JI}{ON'ыMD)2s e̵.Kd8s(ߒ*/"ubh: -,R_>-M{IY۟d|~pG=M.hlMj.j%n٪W:OJ)"O9H&.w3&6`ʦWj3{!c`D#::h1-z)lukHم=cWlO`\c7@V(/#p.1[&Vtf p(|(!C:eu8GeAuˈBHb /DVw\)/,Ȩg߬,gf}^wSh}NA=<\WpO&a>|=B5ĭW#{M^3$P jA~;i |9pVRaux)7V09UX*}c!\ n@7_H\@Q<Gùoڅh' NhAyVgo$Ҁj+U0ww>ֻc8n%6Ẁ_?b[`Sp6*>GDKe:, NO9wH]~4wA:Rb,K ABɅ4ʑUӆ7,2GV#N9 &9MJ(ǩa}͈B!fP(%U=KPl`<(IZz~/BF_d!ߐSI d 8JFe,#q4w׷LSm~ Ҍfx+Kkŷ]Ƽe8;##upo/stQ̦^ Lپ_LBئ"C3Bkeb'st0E[D5Jmlˆx덮רClAc QO?F!s~1|f8rN!"q%*}bSU0k R T,fT4F_zi tHI) 47$![j#DLbXsdK6D$=AI#I_9~rZTи6M"9;? ?'4U\jTt=)UXtĩÇ6\UD5%&Z^[Y tJ6T-Hɺ%٘&fxٽLZ?zϵ7:y-)d|GsJ|'1naDQSУz} ~"tPd]7g[]tEZ{Xj:y;sR{)VxQk2N1REǪ*.ώLx#"]bY <\-Ouб`HI+^C̈WkQ]CȰИA"QcˊtlP&Xq!]ltD Oa.@֥7:OH1k.ը#bhx0 Mo^-]6uJ.D4U+!Pћ k"6 {I5" p™uk R^\R)p4\WyEq(>]L1 3lsfC)óP$%1[o 9PBs 9 cvH< ԐO(xc3D-bY:~@؆a‎O W%[vmz?c@V=[EPpޣɔ(p+0]+}ygw\LQ,QXZ伀vh}GDG1(P@Pv9z G2'~!Y0FJq6;nc$0JUA$>:c-RdۏR7 ȫxhSȅ6=u> Yzsl5I34/Moѵ9Y~OibDy 6yis/t7jLȨS%"w%;OK"aF)"L^]זaUҞ4k1 'P^.(&ڴQSL0Gf~RxScECr.j `e;袄_% L8(jSi8AXCуfիʟNƾׁUnC1%Z\)lZ@>9"͜#g]Wq>ΫaK^O'μvpcޖ}Uy 0NrijhHx>h?4UYw}HĚu۬eF9Ԩئ9DCrG[8]3vSe:x4_G)Taמ7K뱛)ˍ~hUD]cC1]I>]Z7.tVs˜;hݗ-H븓̢ b@q*4ZY߳'*9:Y$_ < g^сGKj4F2ql޶ww#dmv/ cjtiUX(9_prZśX ʉ_<`rZ2 \` D0&G0ڔ9>҅& }ild\s{Ca ݷíws')T]HB^z Y';3Ҍ=G#ƞ9x:xSngZtAEw ALt5*mPĕzTP=_nb:G0X) :E '<('2·|YNP[$h-3v[ bP'5$"{S[|MO9mgns>[dثċ7߼i?݈(mS&g,gjwO=(|֐ Aj DV\M:݌Z>Su@gg 1P䱵wVy [,hۖ䅯z7Qm* #~fC UѬ 5Vv`RE~Eivbm#_ᡊ?ʲ|,W|"/䦉Ͼ~(l;K@mT*lBZ+t|bݡ>/#(C!Tbq<ƃG(σyAن39[1*b#K{!wZXY"y0t/3 Zheyݏ&i5(YQ5pE3+g W3e|J BɘT'E&OEQPDUﹲkC 0nC_k;D2ztO߿7 x|/&49% {EJ/+. os1F+z9KKp9.~5nadԹ _8l GlE) g`>hxEz~MvZ(Mi1CtI_>v!fXr7 :J @) 2)Rf:)/S=mI&8G`:rf%`n 1˘䥑.+ ?YW׹09} 4<2E}'iaXzkFP;š ͂_gԟ\"çtͨb`tl3~6S [H,^QkʤZY"Gԅ%{$EΨs1SǓ;vwLgT8[t\uY\i~OH#kp 9ěZ;r6wG#ogK~PV_+2YKETz0Z[0@1C#Er:H K U{Ln;)4GYhbnЌ\g2n &cb}Bjb8Qb7:cR+9&4F'n۸trsVJ]d]</ 47mxys zlOىv!0*}D LL£IB$$  JԿ!g:@n7;FF]{!-gK6x l8 n4m9K|ݟU㼙D‹Ycu\=5uehvJg͵Ys~Xճg@5kurLfUV_=;[mOc f&F X^BOwmO""Um\5(}{iyy(6*r[jK LJ*;B #N1p #!ڹ4@` 5ݟpX*KsK@%i (ogІے-!!".gkk|Oן=/{Utaߵş:=' Dư6-hb|{L7gՖn p:B#Tr1>m)-=BfZ|h618ն 'h\yE f)q\njf ԗj V]t?U2i-hި2d A`xYk *j8PENNbE۞3pS7_pumv9-ʁGŹz.ڸ)qMٛEdÈ)]bsŸ{@Pօ#jN]TFP̢T碿Y!h+#j !:f}}:7~O3'8ZAq3h2RieL.v'dz%fM1n}9g|cLI^Rq*r0|()y7Y"eD0I n̄c辌@FIңQ NF I$>=ڇ{c0z4reگVXЃF.z]Պ2Xhy訵>klaJ=̌1;%7hw-OR.%-K:ωFAt !3)keX2= + A^ 2 4on1T7;<ۧ kNy#˅|xss!pRwA 2?gmtPNo['blZZ}ĶZ;&*7UINp)U˖QܛGr߳;}ZsT4qB:ŎH2gfe$K)fߠ/2["QS(DjXU31q7_f7+Oba HĠ!@;Mg݇d9SS32:ǬB56FiE"{c*kWA5 X֒{J,)L}@Ɋxk:g]Wޘ'-Z`=-cRiRhQ{8ܾZjaA3tzrϱ):ccrnU2W&t)y*ų[SKIO[3;K|P Jf%9ķC{\H*Kj /]oZ >jԕrSg 6jWIzmިWW(V#}y Y BB3|JK2Y.#:*-6Z*jy^Ihwm'^wֶY_nbW͹|ڛݼ1;oE{]`wǺk85`7i"i+"n+;8= .!Ӎ jOsT 2O; r{ohW C+D**X ۱} 6 Ǩysmëcsqr͌q5Rrxcf*ųVm~LJvc[v`[PW( }wz/L 9 ʗ9vc|T~ 4KQ3ř#iߒC߫싔 =:m]\0J"+Z]X~SeLԿs;K3n);3jC85㚾"MMmOa3:kT3Sis׵չӳn4|⮖<b.`/s$Si5QvdbP0 tLBE `bb T5D;C$. +vȭHyުh moZuG@JAIu֍wѥ"en79G'IÀ4ԫswJߟ"!5o\Fd;i R}7%cŃ.黎v^ڟ/AKLӢ>\żi佴nf+4Ao+XE溷Jf7ZjlS8M('Tl6[Y KHE@rv4K+ӯ(Y@s 'qLv%L|@vL6NbJZ%<lSXH>`8[Xw J2(1wlrjdd߾-#ZnXRC`nu/pW0^\T#0*3UueVzVH@G Kep#40{Î)=$H./B׌GGRgK3M3SbesɛF36&鎥f@e2B.e?R+Ke)-+/BQ9O{ST.Vtc;JNw\{/!Pѽ(n{ϞSc;,j wsW)b=2lvKU334|3yD4=ٟs% dn,͹LA7JFX#se>Yg?ަi0BkDXlz+kMh8vd.g^+O^m.*v$9GدpRԜӢܼ ykv?ӊ6c7vĐYSvӘfϲB7f=١W撡9s.7$CE њ:yktsLg$&k.69`\E6i:#zh0U3M)kZngNѷ&grی Dz=Z#miֶv}]P'ho{=i{sڳܘ,ǜ\e7"}9۵4銍T1nzaAS}vFB}N OlNU# QqgсRLh,VKoěəK+k%*B"ʴYT_a^0ղ`0:HM]!\yaaqh:RГ툼*KN1C3QDRb{'PWKNKz1EzͨjMU^_*k!TkNƸ@]t!<(@/P[EU hv1<\{%3VB[y7 |ʂpVjY>9{)KX0CJj=Yir@ޜрԱԗ >0F{hiXsAo# TgBM$snΠW!%0p0\49wq՝c"2{¡=hܾ&ɛfFfo v=QشQ̬x! h]‚qʸQš\Лm5D(%&3bH5 %?Dkؗofan #EF3yU;$y*`rv!sGNR. d=7ͬ嚃hoj3 xvbN1^T=K7 j +Eog8dqhal"U`,t)6gy4NN=Mz=km} rU8aB7 *1O)Z4=%$榎#)MQQ{m' j>2H_aH3}MWAhby˭i*7jU+n]+Uaڜ%9Ij)F\솕K.rCnqVDU4).*! x֒99h4L;1y8+/`Hti졕pE'0υFE|S'zhUgm!K 65ogQ&,љ,<C@ԵueC.|a:%dː52hjPlEc˭Ġ :_X K[mKyÖ[[kX__])6n#RrU^RE@6$V}Iw7:<0#K w bbl6;MF6} *PLC'nѱ@9cY{auѠ^9Bo~l-;/l7ƺ2duUAlAɻ /u?tQ}{w73\3,dY+MIK\ Ld`tu]5MMQšz ٧.)Hl}x>9zjO';#c'N4!I&:o(3 r ~:,/08gIZ'vN2GLT5 U"1)M1amyf"WxB}eA lDWFCܑ}3RKYv%-%ڴtVkx6pJZ4U )x'\Q;k3Lu^]vS/NaNB0[ؗ%"LT D{N )ZE(J 6ww=؛os^*! O'h*[;|ga|Ƈc-Q#/l6\a\߀Dx ԗ1K\\#bF3YȂ.0yHpi^ZHUE$%2> >)ZcL&nVouGc#4>Xe>XYpruV)d)j1'<3:V;OXuA}4S&pxBbds35b?dZks 9!o(KL|˒r@i8LIO7ʈQjlz>zg31 K^P,M [,\nTLq\Ɋ\S1E֧wPQȁ/X7lW4\(0su Ε)MYJAJ'bO eI=SNx1^bJQ+:NaQ"ӖXn,l7ba3$+N%uB*8슘R FSV#"ݤ-鼌6TձA!o8fhE5yS45g4m+y~4d/t&#OQmO6 )eZ(#6+=9iF Jҽ9g?5"Ҋ]BFx QԗE4MIa0 \tK&[4e7*&%X]R>>-"Z>GߵZhݬ7,Kʣ@.IOauDjPË5SMc C"8#+dTCL;p-eS7洫Ek6d qx8޴;)U<oVCUa{[<Up/bBX?9`'C=/9:f1 ֛vu~>g6|糃}j(߯R809`T@7h\BӲl0r^]_+|8dۿ25:3&@xg)h} 1 u)XSzqwQ/Z \K1x!{vyl~7h_Yy (S,V$=0`g";KTitr غ`/y^{bIЯ7yfd8IwT˔:`v*LSa-zq gH&8-S!J!ŕ<֘"~߯eՐ,F*Sf՘KƊbVT Iqjq«%dkl}y&{{䍄:%64W&%ue}r0%r hb=eTkñ,]:( J  !d hdn$oW즱KRiFCuS;Kv IRwmu=UgVao8;+aMix*||byTFP,( /og|l3ڪKFl|ܼQBᬨ |Zu\RI /8@2ެFAҘE&̇@bǴpV۠EfYPnLʚj5 kZ7+FΠ@=߅CtY_vo[mrVX]f&dQy+a*2OsOk͇+'Ey'uw~:&s(r5&lq]h&T t0Jc uƎ3xaly}}eupWƳF6ZYkcs;'sQSxDy|"룈Tl3x*F\!UmVyu%x@j(lxf:ξޤQqݓRO!I/]R6 y}}Vm| GIE7'sLkGm "9r YBp&> 3|@D9$29 @"#{$QE+l~2SrQf3R(hN#CS!4Mge;uqPk-Ӑjyz6!w[54%Z5ݻU0 my+ݪ|MC QULAj3-HGzc`eZIMd#=:sVXh:)Dd H̍5!4\@y(DV1KH! xUԼY 9 '.DgB%NCLjּUߵykk}P˕jiJ}FoN%:}W`Aaăy({n|y)+LzN8;ULI݉;+*M2 jkW‰zO8+f8ǰes|ǡ 0kNAsw0MD;rYXjQ: SRIW̴KN+miör#0j0yMzr73rq)tFvʟ|Z֝yh2[8Ց#WKz$ ]I0+vz'W]]20KΣ@kw'[>ja"-9~clƮu}in<2aEsm[s]z֛(T=^k'Kgh׫ 'l# ؗǾȻ1 ;W02J>ZkEwD^"Bq/ `5&08`0w3#]olZM0?ߕ}ͮqmڣ=\ {f,!˜'统1)-V݁ : 8l eb^zW^:= [[m~e߾|c_}vK%9<4͟cw/1%7L-2xdutTMǑPJ"KV2ɦ4*: ODN[Ӊ@0Ԍ2՞WXYNGLMYV8HQHrSa^:l+8 :UJ4*A) g,΄rsD$\/˟0daZA-!y >mYaYk 'ƍ1@_ٷoGov߷m'5Ja>,Z hc+"a@)b\ZPZEX)ħIҦHsI"YՎJ-6nRZ-QtuY`Y l0v ~6>i!o&-9$D't4YFMok%%ZH7 ehUB n(>7uSӞk5qn{fc1Nuͥ#b[9j`2eT Et2a 8i@0s wA3 VXL(bx3)?wmxgOqU!S'2(7Ѽ#I e~Jaa͠80Pk3~o ! 'G[+๊ڈ./oar-2.5.2/third_party/rpm/RPMS/noarch/perl-Sort-Naturally-1.02-9.el6.noarch.rpm0000644000175000017500000004606011757171206025013 0ustar plbplbperl-Sort-Naturally-1.02-9.el6T>D ,0@dff7297644e22b59351001ba2b669a7ff27d5d11K էDCPdT>7 `? Pd ! TLPX\ m       (      M z    (8 9$ : F G ( H L I p X |Y \ ] ^ .b d e f l t  u 4 v Xw x y $z @Cperl-Sort-Naturally1.029.el6Sort lexically, but sort numeral parts numericallyThis module exports two functions, 'nsort' and 'ncmp'; they are used in implementing my idea of a "natural sorting" algorithm. Under natural sorting, numeric substrings are compared numerically, and other word-characters are compared lexically.Of origan3GPL+ or ArtisticDevelopment/Librarieshttp://search.cpan.org/dist/Sort-Naturallylinuxnoarch7_,[A큤A큤A큤Of AvM; LAӫAӗAvOf Of Av;6a556614ff98f6840a2d1c790c86a28aa78360abd8ab71e0240d9b0789343816b7cbbedde079fe80a81bdc474003540177d21cd552651c4d648b1164ef49c607883f6328bcacce20b9ab06308fde02b43996060aa3969763de0e9b0aad2a5ab3rootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootperl-Sort-Naturally-1.02-9.el6.src.rpmperl(Sort::Naturally)perl-Sort-Naturally@ @@@@@   perlperl(:MODULE_COMPAT_5.10.1)perl(Config)perl(Exporter)perl(integer)perl(locale)perl(strict)rpmlib(CompressedFileNames)rpmlib(PayloadFilesHavePrefix)rpmlib(VersionedDependencies)1:53.0.4-14.0-13.0.3-14.8.0M6@MMRM@K@K@JlE@I@H@Petr Sabata - 1.02-9Marcela Mašláňová - 1.02-8Fedora Release Engineering - 1.02-7Marcela Maslanova - 1.02-6Marcela Maslanova - 1.02-5Stepan Kasal - 1.02-4Fedora Release Engineering - 1.02-3Fedora Release Engineering - 1.02-2Chris Weyl 1.02-1- Perl mass rebuild in dist-f16-perl (d'oh) - Remove now obsolete Buildroot and defattr- Perl 5.14 mass rebuild- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild- 661697 rebuild for fixing problems with vendorach/lib- Mass rebuild with perl-5.12.0- rebuild against perl 5.10.1- Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild- Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild- initial RPM packaging - generated with cpan2dist (CPANPLUS::Dist::RPM version 0.0.1)origan 1327326732VA%V$V$VA'V$V$V$VA#V$1.021.02-9.el6perl-Sort-Naturally-1.02ChangeLogREADMEt00_about_verbose.t01_old_junk.tSort::Naturally.3pm.gzSortNaturally.pm/usr/share/doc//usr/share/doc/perl-Sort-Naturally-1.02//usr/share/doc/perl-Sort-Naturally-1.02/t//usr/share/man/man3//usr/share/perl5/vendor_perl//usr/share/perl5/vendor_perl/Sort/-O2 -gcpiogzip9noarch-redhat-linuxASCII textPerl5 module source textdirectorytroff or preprocessor input text (gzip compressed data, from Unix, max compression)PRRRRRR?xeΪ ^? xSE8led.i IIʚ.t h i)7MYچ ( (Ȫ"""," o̽M6J{gΜ9s,3*((UJeUNJQ";TΝ: Mݍp;BFm;0gtv0f'\PUSzRwd зTU.;U] ȧ+"TbKLNJ.Cl:u-6A'1Yp5H$\WwU]5JZEg(Jf+܎"Z3VaB,2NPE;X۪7F9m:uxN9A#`Ɂf_*(5ZAHxyk|JOJKujS+SQҨh"#$X{ZB b\FbaЮBδ E>Eǧ%DɝmfL:x"nyfu[XԊ3tǏHCO1ich+Js7l*5C~ ÿ"7ZՑVFÿd{x~<,hw.zdJh -Ӣ<("2SC}@h85H^AH34as?sX袵,,5t^NY;kգ}D#౹نH*dN ̎(]qUB)'t#Zu{F'+EIL*:en. :6,PޚXhD )"H" $ tjd Sː}qQR:9 HTJzvN|jj#J L]Q=Qcg#0zbVXу3H[uƍK!΂ȏFcoɀl1- oL>i4 `Fy9elHsbVDld H*t % ڜ)@0Et7YMP~KJv< .6Ϸ·HWAKk H$j.,$f$%K* DTi4JE)B PDM4>>9V^)J/Q}D!QT s" B[=Ɏ_zQG" -.Ź~K24%8R2ig',(5 ǔ ΝdXK\Ċى))ph;cJ=o ;EFEݢ0C}뀋_z/9UkNtبZ{hyMtʙjFwe,Wbv?w=xۼsf_OsoU3g:{Uz}ͿY:< z'462ښ'k_ջ͆=d6\6-{ι}GցԓjY7v#Bwf/jХ j tͥ ߯ez[Z[}et|ݔ#gJ3xTg Hҵ{ύNsFnq^!]a<ղO^Լ^:|uE;cM*ogŋ;k>l^}̆Cۍ֍|iQ//rt+_뒌g3|3W{1}յv񼶽2s:ۻ<VE.C=K%dt򓏎ӱL򳄉n`뙘Ό>Sk>^Ar~[9nY>ƶf-=[;xf?}q{g45vhGWO?/7lv՟L~R# ocߢA؝7xe[-{X~hcXg&jRmNpFS8hrE7][O 4kdE?q@Iw bޮv(V]{tch57~Y;UI6Twڸ_~4L>DCG :aw8K_.yvWsGg8qYO/ֹulhÆcۭ({\锔~v̅3}~g KWٲ[rOaƤ=k5ulo{雽ޟ޸ W?Jd[-5-4je*ENET.k:֢yuw~fsO{sMǢ}_,9k^~1}iz_G?A+;&i[&,p^;3s*GVbZbyb ٹ稝Emyq72NS7r.tz>~c^cԎwe^u̬}ژ'0]݁%ݎ}qyDd'=wQŠ/k+-xǺ2da#?Sv_ի]%F/5)2zkcV$ntQ_ /5!MG&u]"講[l1'z}nŁ-Nʩzκ7^n&x͙ROt[sش7DU!~W{rDFV>qm˸]l5SJyȬ/:=&5m.E}q#?"Wq秇$rY!C=~#vȉrӾxʊ=wnظ'ne鶢]NWtȏ7^`eBǫ'~ԺȟR}1 ~p?p\s>R:{F ٭ύ_7so&6jVlzI{;,1^v\aƁ7^>#7f]Yry+ٷ~%Zvo}s!=_;iJ_6ݴ7cшznJm[tuણ_rrKIT3_n~|Ā<ٽa}z&V,_5xM1[כ;ljvbqu S6kLًH=g#K͙{3]yZy֞oeOAfBJ~۽j[whKcg#TC;H/N9|;/;_wi#ҍ7ռU'c~noN(:ڿɳ.{GURsTOGmL}sE].mG,Z=jwU_NL?wU;v|~o8:y%yi/w(hibp帩D:~E?g:Ѫfk+_kK'Z~Pōj G{BZ]xpa^S?$MuzS]w[g^RfA- N1_d;;ӯ^VO\o&ξo,n|cAwLaP.R>a%MuٮVjju>|+n.~~|EoZ6@jr}ufm 9~rafݛ0.w| ښ۹u#kwhŻ[Y#=׎Y?wnm͆a 5os'?*xM a{2&ޕ3ke:Fc) SpTZ f84/zU.X~~eRsߨw1sg'6tBfO6;i3au9͝yh_?Q}餚gڎ}r69Z[?:?-xBߖ>Oq͇k\ ״g\] xOgYˬ~隑=̛7:.r;mu{e)>;rbI'>|usw.})}+kpq{TauzooVը#-v,ӵ!̶jߏIl=G4e[onjs՟V-_Ljt-5mcwNucW]^شŁO+>ыJmOzs꯼a%ߨwj9aX&Ofu_IԲfL#ovgbome90tJ׊rc`-.IS\!ק~MQE3{̍0-aFyR,/:p3~3`Knm9fi3OfR%LPoofl=Zք &>6E 'rpԳ7;//~}Y#ϼ:eUbH ;k7>%Kj[]\Ց]K<ћY_*ߨ`&&,_HC7[;ϳ5 T|s8IgH]1g7{es#.75ۇ-c_?g؄<!Ixd})cw߸H#~iQ^m:4g#hկ|qgDV)P/$lf\tbUKo{'*[^ᄖ:Ղf۫}_u][?{oq4uk9߸jk&T2[6c8zu3q+ -cuI'}rAظ%~ХԱ+Uq/֚1{鷾ޛ{}*8\_~6+EeU_-+>a}76qˎOlː"Ʌ⟟k^E߷%.M+~?nq?茎9 ͊6Μ{E&t:djMVC67ׅN'ݯ3wCna\TuOɊyE/97=RyOpE h€u_^g YKX1xOݗ.؋/vZ7rl;7W۪]ii|oI-7$⻤Ҍ If?SX {~Y]1Y#xik+~6 C~jCy]]Sy5_G}nI {T:!dNv)N!c};{إ{=|hÊ 66&U?rꏗ|`vY+>{eƨα"F%h>t(az};HS';y,qƿ!(%l;8}t2\yk8MI<p>hN84Ȣ'ցd#hV&D&5BW*$Bp9%K(Asv['M* ډ6dā6 p΍ I BK mf6 q g!}3ri\,Hhr|ݯrWNPZLphr ՘J:VprvB01 4th4\4%di*PAK*%H8=Np7~1ep5ס)Y9xpU%~6PV[$̝,,eqSqٗ`wvMJR1Dir"obt$9gl پA nwpM.d"ÕGr0vrʖٹPH!$ UpCtj;yĎ]U+qe!L1.>/ ]?8IC,NV'6'NGd ! xĻp9ƃX)L.p>Mp[N._ mcaS>A@4 Ð("0k )EΑ&OX=Zfn'#W˃'WDMUK-āClA_u |ˍuyIać6SD!]& 䈵*fMN@KABáͯ2UiT!0iqB Mo:>T)q )߰҄=a7Za~ki: {Ik2&Or4B #aEE&⑌$*B6C-0ke8Yvh`pl;@t+91촂A|7ND'fegdd nsɱsƩv"TlBÄ؟Oʁc( $I/pB;6$#IN:t+|CĈ+| *,3,L\||VV@ |moOѾq(Gh-~>D%ўD4a+,E0RheM]-68[Z'i@DA+@Hix,Xu^g/W= [7Iax|Ұ{XxB y?x3nr1SB jW⌬ASkg $3@xd'XŻ B 1:{-^J/D WɃ]+y>),Y~YS+R- deG^O+pgqY>W@T? ĴL<bqß2Nf¡VP*yC `"kM\8l^ -h_khi{% Da'K4IE6R*6>>Cm' 0m΅:`YEH&epR?fxf`\G-Q1@{YIWlDUg({r)VuU"hCR5iP8͒Gq_:`_@@XIB''=NggJLƢ)(@ (*H*תr:uLo:!UvL%вttߪCd%8 +QOP.1S/c}p:= t+>G=_ݹ(9c1R8!5(AuhDVדZ+#;$/Qq)ш2 G=M8_8i׈^#ហܣ=_{OU]ٿRMP܍wSͿ[J4A*VP͋_,+F=M)ܨ'*~m+F=X[m^asPy#xxrN0_iS7niɅn9"P K+-6(F8e_n w`L8]nkM _E ˰3 EOi0C#1ڬMuP~ sP)x4 #me?A=|%@B<Er8[ziG,jpzV =GEP+dIj?# FhzPЋrrQã{'-%xxEilZkGƻM#:',sE~ pڪ"Aa8PPUBH|ؿVpZ 7$T D:7}“.pY Edr2zN1|,.J JZ%R6<ZKkܨ ^]0b1Oaz)9ɉ?gܟ!d!x(}$mG^Ϣy ShB\)U, Rszm6RO'C3gٱX@q,Vw$aaq6PVrpvd wOB]{+>@WUU'.cu)x5NwھGsK99(p8`qx;W/j‰XvR YN q?OWBjY]L׃".Hn#hLm  `"EU";Y]^V92}BC$K7֋RkT!2. H<PKoap}W;+5L\5`hR'+(eKFvg]솊<_ L6rKH.إX<`h'8p> łJ|R2sHƃ rH1f eX$A8Ӡ80޻ŅxGB=Ф(hJ%[̖!uTSa1а3"QN3@Aldt-5b" *ZLD&8R&"&qp L @G$zVaJEzr:w&=uENTE;HB3/[bpw6'+@#hZ8G9Z D!H bΪ+0"BGd7c׿xKDæ%;E&eBO95 5|bM3~%#8i;4S=π-1]1(VjUP nb29c}hak/O\M?i=h*p4ٴllV)M=d6JfAKfsk/WF4QB q,w Cu6o$-#5ףG, GF DA ߽vRdi6,H)אOU|!J$ !VGaA%&#!N2=>5+ežY94}W !!$ ‰MUW`PqF yvRLVIɳ&z72P3 Y, T li9-[(_ =@ksES7U-b.ǎؔ_rᯈDksR?UE;!ñ%r $Y\0 4h?mVTh {b?9r.pg$!+*h) =P/(•M_(Q)%ށ $R nJt2$zN6' FunrieNjS:^N: )ی$,9'p*|2Itl.~\jʹ+f 60{ g8\\\\2Wz^WT>@-p 7Wj @ auAR_2w+*ʦ$ 49lApD-"hJ 5|dQK~^ʪyȉ N SI$AnJN~9!yMÌc>`>ЂI¤`< XP]Md9pS.Y#UMu?\'j?ӛc}LM~fD9¶85@zs# ЪxuMT1e,ty,/byV5o*XGa=, ~;Q +Վ?T#'HXV"K"r "IWE`%S BC7 E*PLͤu|Z#L$P:r?X "> #\6[WF_JS@+!pDDcޤՎ@,@̜Z*X-Cda@;]:O$V*`_0RI)IňF,VvݳX;cr5Y?pW-[&T!(t).4 H9J:X#c .V Bg9 2iX^4Z̙ecJ`.%59]vPbT./oar-2.5.2/third_party/rpm/RPMS/i386/0000755000175000017500000000000011757171206015162 5ustar plbplb./oar-2.5.2/third_party/rpm/RPMS/i386/ruby-gd-0.8.0-3.el6.i386.rpm0000644000175000017500000012546511757171206021246 0ustar plbplbruby-gd-0.8.0-3.el6T>D ,0@8eaa40347c2d198f9a0d64d971575262a8601cb8O&WfMD>50? d   9   " $ ( I LPUZx|(89,:"GHIXY\]^b"dAeFfKlMt`udvhwxyCruby-gd0.8.03.el6An interface to Boutell GD libraryRuby/GD (formerly known as "GD") is an extension library to use Thomas Boutell's gd library (http://www.boutell.com/gd/) from Ruby.Oeorigan+BSD-likeDevelopment/Rubyhttp://rubyforge.org/projects/ruby-gd/linuxi386+Oe8c34353bbee65b861e0288ab617ff9d6rootrootruby-gd-0.8.0-3.el6.src.rpmGD.soruby-GDruby-gdruby-gd(x86-32)@@@@@@@@@@@@@@  @libc.so.6libc.so.6(GLIBC_2.1.3)libc.so.6(GLIBC_2.4)libcrypt.so.1libdl.so.2libfreetype.so.6libgd.so.2libjpeg.so.62libm.so.6libpng12.so.0libpthread.so.0librt.so.1libruby.so.1.8libz.so.1rpmlib(CompressedFileNames)rpmlib(PayloadFilesHavePrefix)rtld(GNU_HASH)3.0.4-14.0-14.8.0OKJG@H@H@Philippe Le Brouster 0.8.0-3el6Thierry Vignaud 0.8.0-2mdv2010.0Guillaume Rousse 0.8.0-1mdv2009.0Guillaume Rousse 0.8.0-1mdv2009.0- make the sitearchdir definition compatible for centos6/el6.+ Revision: 433513 - rebuild+ Revision: 272023 - import ruby-gd- first mdv releaseV$0.8.0-3.el60.8.0-3.el6GD.so/usr/lib/ruby/site_ruby/1.8/i386-linux/-O2 -g -march=i386 -mtune=i686cpiogzip9i386-redhat-linuxELF 32-bit LSB shared object, Intel 80386, version 1 (GNU/Linux), dynamically linked, not strippeddirectoryPRRRRRRRRRR R R R R R?} \TaQPFB#!/0+5+QWbvpA(^`kF _6,5424RTR@B4+P*L4R ae]9gΜQ~qk~3?ppHp  ;/dh\HCRv.xl"681aഌك hz 4lp5x\ؠnvXB 両> <~\_=h8ʧӮdK` !/[Clq1 7OA>?)_ҕMdEdKŬ>˿=ek\K昬Xnw ^'󊣭jg_0mg~*w;!d}z: $XrFҐĔ9؄D}lhZ}611!5Jң_HHN#[DB|1)S麤T(K$S2 Izd1h0l:" Ɇ!0 Mw]Zvt~.҄WЧ%[\Fr $MHj ML׍N'&l az-zOOOX=M3 醔4ъEɆ4b=;!@chƧ0$3KIs ىO'DPiz4Uxk{"&ıR&F$ϑdDج7:"4}.Ndz"Qc5C{lcC"#'Db,.1z8.M(7bILM&<ܧ@[aKtI;,}=&$Ji@f4MKS<&cKtc0t viaztW?|@T}]Ak4cR1eKiDǴ鳓"Җ!ov z/"%1{NJW(fb c8cWJb1C>'^frĜ1sș,㧍`vX;I͉!<'db#r|^VBX1C GG?©QONZs:3#G89?h?Y:?1z?Ba#L#L(ӷSUl.>̫VkieZpk2bǬo7kk3<[ ZKǕ\6j3a?>ػy{ Tob'% xDHNÅļMx64B̙‚Tf#v5h4h晗fcpoqL{[05K"}Jo e&3/i0+ͮ>by|{(d Wg ŧsCnj=nR! КPL\"Y!|s\XɺYq; vH|ߎˋJKmrKD-R.wI@3v \R>%PnwIكY$PuI)RvIYI3(\Gw ]R&+Jwr @KʞD@չ+HC@ˁ)RG@T'`E#_(GCN .)Lrq2%2R&>'PfgDy؎(<یN++-&ʋW\sQ) \R$rKJ7F)j%#erKwZ/d{H,p3ztӟ|CU>-'5r)"exNOWO_:Z&gR7o$ @Hj6fF,H&m#9& k܍oIYOo*C쁼,.)?&JoJ2( C]R>( (=yʾD-Rut)k'(U^(]c{$PqI2PWS$/Ls`,"'߸/ Am3=R',cy@sFoIxx._r#_DmQK'.)D/9QX"PָLd%.DY(ܵ2V4HJ-~_jOM3_R",*5f ⋋S!D9_4IP$]p+r@ySW+Dy*GvY, Rp9x( ]NM"eӮx~mēQ_ۆKb d~3Il>QLq*U,+"m@rQ-Pu4C2(@ 2%OrV@)v uI9~u8 MhŬ/RI9Έ䯞xZri)yϟ#CDVj (=x9Oehh>*jE҂S"gC䠩-$?aX|~*ԋ-w!㎅@@pR)e; 7D,E2YH6"Í$lEj#bgӎB'S,|4bIoh-"zUD6C$kvNv{XRq]L%Ӯ&yBrIMj%D9@KJ/F)jp>; $RQ(o?TRsIFzrWT{q@WsN(M8j~T [8j;L 1.)W'匫XC"B(_qXY|699p댫@e.j~A}.z%YNWUM3MXv}9Μ6 7`c5?e9Nѳtجy,?r?_ MJ~c3i+6~R~Qަ cb1忲` ꛔwή&壩7sbIyOT*M 彡 t{V[ӟw'.n-|<oo)>ˏuԔj}Ar`gP(F4FHo ۧeli |)9js'o׷VC݄'믛2y rBnɳow6-Lm1?VE mΟTTgf4G$I2QQ߀BOjQ_ڊ nOj`cj&o^@5Z_w;tD-̾9sq'PoWPRGE9UerD6xx/V5P9'P j 0i|r Y_tE}R0{z)H9SZh R0R9ʷFa6k^z+ sh4?(М ߜϜb69W=40Ճe)M(pQohT)dWQum[y@|{ T y<00ug~H||u`Ƞa|[Z݅tdr[/6^`mu|?LFMtM_ ?_~_PfՔ)-z+yYs^ƼN;2`[_ 7}]my|`LH֥|~*m~?w]o.`t|^7۲KY;-_[;EL3aN|)AZU`>tzaAF~Z}iTO0PӅfj5|r‘]퓇ZTA|eL5ST90^)nx|Y[<^j:E F 6C(QBO>b& u~'j)a?PbΩU;Xk 1xQjsTQ^UL /\5/PY}Vh99milN|G[s@[U %LڊSUg*ͬ>TeV30dXuÉOFݯ-_PJ'/P{Xj5< ٓg.;{,p~K<߂’yj<NVQ^ eϞ8=1 n$./bs j7g94olAˋ|{,e}pq\"qmNFp.b'BiE<TBB?`@+SY[t|b֖`DA✶ v _ /sZߺOa/s«4n9[:¡?Dxqko?ExQ0&/&|s+46^ںǗu'|D[GAYCmAkSCEZ95.P+}VL%t(m|5>O^- זBq}BxϓõYn /n 6OԘUaQB:[gPb%Kl{TgTc֖sJ և~CgdgLOɔZSSjRGSj)RPvLǻ'O8VaЃzoLz I5$KpoL]iJ,&0VADmH=nSAY~d[]$/?5< ӇRz/hgm3b46Yt-5t?L7K4TL8jRZ|l+ 0'.0lcW071U$dpìH̪f7+iVZBK jd%?CcazIÛERoFICIɫm^ҟv߅ KHP&ZJKK\+h T(Bdz7 x;DXS7ޏLrl|Ue Mׁ|VHS2?~ ٯd(07~ų0+JV0Y{+\USD\8ЪWhNeoEـɫ d73zǛ@U(Z5Ꮓty70jZp6~8^Yo9C|T C,NQ D?v*kdžr>X K1GѤ"9*u"jYm;1(Z_pih>-qꍫ f) 7wؐY86d`6fuАn`dpm3 O&Xha{q3>O] dV=@äEqBQ8O(`g<0~h:oh{ JxJ>a$Z{YcXNaPя󇥮-d)ڡUY1øgQL}vitѾ@9%O9jƽ|rВλeX`v P㼈2~4>k2<͹ᖛI 5~iG͟=d?mi\ohSܷǴm-ӿW jl PByItw3Dh F\ ade`luJyEK3LT~"gfhpadCɎa+%Ioy|ɵ0-/Ө*I(9אdlC6L 6G[P ]{Kjڂɳ1?%e3Nm[3G\QޖEi"NDi%LYCYd\RJ+82 %ǵs+8wftv[iϬ7&Ûp*=K4M9b8w-;| kJk~̖ 'ϞPy-&}gw|T *Ϭál?' !*H-Q ,O!E#m(;L|-k84<辆?w3j*h}VD͞zaT&mB;P_A%2,߸:o 8z6"gO'E5OKHÒ??FJjh-t#~iYu8(M50:_ZJ_Xd1tmN8&-) #|K9 uϒpn/Ta"/~^(!vciC|*-_VցښxVGfe4h9mZ4vSŌv|ZfXђqqϐ*KY>sjCU rv͞mxi-JTc;VPנ8}X>\;?d?IZ"[P>Q51ޯ1C} _Eޢ}=ч{ۏ0=lPŒ~Oq(V =7o1Gt0Z%%1:XϬ}v5TyV[T5L 7*1|[L4p;F }\V燾&,U- x6Ep|jHERۇ1Yؚ+E:"Y<>,v?_ɠ{_Vo( }Ln&=^:*أ[-U=ޮrf|#W?U=NeI+٣/P.x>-^UV:!:e1gqO~b͒9R!^ʨB{FX+Ⱦd+y;#Xrgܗ^E9|FLzpOƒa$/h*i(*h6* ?IZ5Gip8\ N>l{>>?e { kӼ}r?.ʣJkpG=2( 3n`OsLbMVne7Ϧ7K]Gߵ}h{D3) {햢F&`.1uNӰm8hCtats}%' ݧst=p{ksnݞ{=wArm}ޗ}:RVb0Ե-Lhv)9N~_NWd&rr!r=K} }9E)/@zѩ#~}{w{޺ωM}>9w}7S;= M} :~_v^+Jܗ.q~_VWeCJߗY}wRI^[ڱ{F{wߟs^߿lX;b qީOk4=熶vr9Q=a2?0?yK2?a~qҾA"~Y^܂wq*7#f)ί}V &}CIh>lYožc(gCmrZeۖs/=kbn fYpQB?63)`xOӲП@-58%7cXei1Q۾sr1L=Dyο|_+ߑ|/W!~/Z& b6'%!-2aVw%M]IЦ`.mM29]; zAk-gަ#Fsx&MbIxBf~6ki=NNovuw)f8h -/^^gvu{n%v;|.uhrv:h=Ls\_G]q.'Pr=.rnEѵwc(rb_ޒأ-{Կrni_vw qe{|Sbv:x.;V6__7nyх.#g矤ϼo)8tߒiy=E4I͗i ݗS:;;Qh!_h͗W 3\1}RWpq/ U?"p*̈1L_?bUYn'=|~P:_r=p#ѷfynĞU5<; OR}t`~owp->yyai}a=ЯvRd硿oN?}<]KBpy\'|ӛԮ硴%WQj57udc?خ4= wZ` 'nktkpnp} p(E6+lvIzX`s͢OɇxÕJ9&)aכ>fFdsy5;M{ԼyQgWUT'$Z<*3ZX$$Kcdi?JcPꯙǫ@BÄfvw?n@M&S4_,÷{`ҶӴ-+x@H*u<Bg o5//x(Rxd?7)\7m.Bἐ0~:q|6q{.!j^^=[ط=.s֣Sjέ,)y3$y њl~1`uX_-] _\d6;}}?9C9>,d &-,j eSd5AYO ǛFx? }P./WxW^6+\Uhimǖq!>E?ݎFLVQ͖}T3c+Uٟyx@.l %8Z S>Y齭'Vt)f Ja$?bh1oホzhX҂ mLbmƶlfYjL7Kesf;;?oggvv!6v7ήZg?=屿zy?E=YOg]ig ] &}6Nwh۱Yu4ZZdu먬79B4&Yɡ$lzM)#: DHgF'f9y &u|Vuڎ:( oD( A^65NC;ӻ,;gcc$l[ǿ(5:qH<CW~-Z)#uNt29uN'/X_J5#mZe=_O?ykNO?O+rWu$:i_$IS?#9`;^j>EYWv$;*ĵHN[ŸJ}u|ޕLKV\i(۷՟ht!5. ڎZ״X-|gZZٗ/-U|5?շ~?kkni>nՒ#a&+߳W:YǮt~A+o~V~'O5f&kU@A櫌*C+ u!xtidWك­SFgzQ'}EistTl}; Q,¶gB?Hh5ƹ7UKCh?gbyvE?5U_p_4 o_04g^2{v]I_OCUQ;ߊ\!)3v~|WB膧,ǟckX쓻;xo4B0}]~jvWGP\WD쎈tu KH/5Ԛ$K}v}E1 ?A{SUFxc04G_YWA7L@jrjO(Yx+1ֱ߉z> մ>/9g7i-{ͬZG_Affv5KVm2Ѵ]/sX긕>P]~x|?vTh !ή?xQZ4~{1J`vO[=|:}f[6XebׄWQ'M> ?0x5J +~c\pj-|S7''`¾Hל Zw3 R JQ&Fɟ &6{k.tɑmb4 P[kTb!mS(mT8J1ZӚ&נԐ?ʏ\(-cB?-tiS1XA܇G\7K/[\^񾠳xYbkڼщ0iī8` m)k=$syԜ( WvE[&ZQQүUv7[)efm989ڗ].HRDJ ߜ&(oo&Ֆw!M9eYj߇2GpUNkEm5=b)<ȶd,^K[UBYJ?oµm[*S@1+4VC}\_LV9}^_'_NXJ?&-e. n>(DcKo_ݹϖ]‚#x |"]*%KEιxX"_^$G[y_Wx~qx~f$'- sxy<_q-9.p#~v? kޖ9o/:|*)%(F+\ )EW?,7\^1G Bpd}4q1P¼1~}8O (EUrz[!;ht}cvB]e:?5`ȗ njʗds(_ 7<<[:87Y_7^zpK5-Y|̒7B|}~ښ$LNː,˼q|=y;㋛k_es|ye20eko+$S_¿_֛c'$| zB 2d(Λ31COgQ"Bj ?ufȸ$ z{vK$j}% $]=MH&Ȅl.#9V KI6&ˍ s% =MKIHqLgKL>;ɖRHH#tih! &%r=X>ݐŤ$&&@ :;|LbJ:P!㧍f6KgMѧ$f#S Dn4> iT]̟7NHǗD=GbCdžGFy2 =<BT%2r,66ץ?lЧ%bGўNMT]k s DTCpTNa )A^=$L}!!FΥKeKIKc@=Xh!5)zcJ::%1PlccFe |2415^gd>"%) ΰAX0 Z74yT!A.K(CBzXqx'?'$  1/$(T݋"CXq znrX 6m#Q`Ō4> mfK:KL^*-Ig7 F86!+9#)P 1%&&u@>ũNu\0UVq, M!P9lS@2Nx*qsR?S!P3! b~ΐf55+&+\H f!^G>Ӝ=@unH I%cF`RF!p>0$pvA#L)t{^)Vk1W<PyV.Z#p;[zC>0--Z4,X dz{@9Cb p @jFeVk'ȵ*pJ`.uVJ /X3``F`)@+՛֑/pfu-Vk[Vog8 PlXk:`Gkyv= p[Vk- EVk?o[  VM(_ \0` xy/+P >0v?0 ḰC7V`(` @+ U/_Tn5nP``Ck_lvIoN~ABcO}O[,|_`>5ʑ[\9 0 ֋Ug^=  =|^Z-?v VAPOպ_/jUA+VxP@:)MՊ;UesvU)GwqS8 ,pVk_L\ghFHmݺH~AYRIڥ*(y#>~z>O-rqW VYeau@|N\6ƨJ*Ǻqt d{+~8'ZmfJ>);hǯZ@{E޹@j]>FKv>&X Rߑi]o{ˍ| gmdnBGf.(d~]s ~ =(ǀ@?k Q9Gp,.|FWy;[.0/<4Yo0no90ЯqJs+Ͳ> jmPZ{Üyg/ec vwzo~>Яsg#H/nq2 . ĎݠuF+JqPvP[X e#wXݝw6{;Ї=)EvYۘdm˙1H vuwwlG/qv?%|Lz/ ur$8a@?]Xg8 1oFOy@ooC4zӫ`j@_^ccX[ߗŚh0X@{yq#w7mؤ$ q|m>u8,k 8G|E/)hjr q|j}Ñ6[JKeώ}s?C1n.7to? 'G;7hv"T1@[W1T1nПVvo0nX\cV}#蛏\ esJtgav@;٘3@fyctCZ:Eik%U}z(Gi?oIO2]T]UҚ 7{yǝ11Uy[by@OzP^u_{ <}a_aUG}ٿX>˗u򸇨aCfWF I!ǡi<<a<é g4C0/uu="uúdpi$/iĜ!&,"m9YhwLCkPBj=SvF> ШTuv_uP THP0/߱߳ƈSQ)<L#;8@|M|'*&ʃA1/̨(F0@B8&+==P7e w&$q~@I[';yatiٜ“R9xEэgNh9l ί,R =i-mvʬTM@)ʈBaa9ϰu^ sD9"Å"E<×pcI`q"m CEy؁*3P 7[lT (oIr^L9ʕ@9 N,v݅8 ,;9O8Ei7zp^Jݝ *H{ >PU3S,PHG93W)dﴛrK@ }#(pO!œM`tiTK9jƜ,x̉N%? n4ԸR!R7 x݇ ~hi@+@+dvr?Aƥr/[I"bTtX# DbJ>vOXg(=/=`W]@aBIT{߀s=Bс^,J8G%; E 8K=Pn} ޝ=`fy4|%ANuĭLVeǭZC;n*zPt"vKq`ԧBYT4P?JiߔAjWJzc8H&n&!!LABU BS.ʇOggx[m z{bv `0}GMw`) ̶aSy3(K۠lf#`)hvZĆ PGPmZ@Bl e&z uD H7&_*h!>F>:c1*Dw6^T4o: :N::U:zHQ>B?uH7W0ɲalkCmf Q}r Ȧd#an"ӿ@V?Cν,r&Ѧ[U͠}}aLNA޻q8` C70 x4gy"D]|,ACh~Il!Ŋe*BX6P*D##H}f D,tGTUb_ u#2N"֝;)v2c4% v4B)% NhiYŶk|z|U-2e]j Ȩ4Y<_ GvQ͋vjP[1 ˆ$90|881v>?v?ganG!: x0||`&ߋ2Nf\O#סIf/AhѴͲRDDx MqZxpH9I'H.>7P|E>ar7tfrM)H8'FvXË2 ^s`\6\Nٛ#0ѱ)w8:)9 A.DJԨAB2j}*XËw a+'T)ʖK=\ֻedqeK4 - jW9N$3TObIB=PM`P@{]8_$Q^?JKPYC#i¤4~y?I*^gITظFa'JW:Cڹ Zb-T]Y-!T(c q[1 B w"y>܄% J00L I&LHBpxPQPYuDWuquwe~t|ϋN]Usv ԗLH/]J]  ,]Cr>lְ15"5$4R.DnE$[91y5tVxh^aZaRaRaRaRa\a\aZaR R S|a_VypWaQYeyZaTR.,"hFߌz19TTx \5r*TxRJ   9 9 H~^J87ĵmXV#r cZ7qH#l5'OR꟤?IR$ÓOR&dRE}Ca.! F%KS1Ced'j8.LZ, KDL%z''wDs<"e5h06X66X668L:/;?37an7ae02}xt>+5AztUSp%zO<G^,4ś-!cHSi9p@&i3<(wrzϭRRRRR&{ ;,-Gg}}}}}}G"birr~Oew+E= hoz/]^64Éۈ|1ն-hXwp0Cط=%Klj"촵D TP{ tz!F[u$j#Q EnmYDS >3} p=|J%( H@ֲ~6],;AT1DyEl"z^L>sc;.w]i0i'Rh`+ئ' (\|0ß=* ɱѥѥyv!2U%|(hª,ݻĮtˮtRȀ1" \zћ Ur4&^h&Q&ÕGc[gUٟL2>vp6? 8= [N|zBd B,6yT#>B Xq7I,1凉a蟱Yr^4-<*ٔ{Q<=z9J~tljF=8~Jyp1u56.21B݀h Tm`"a%)flaTm5Q{P}զ~u̲/NКDaJ %DF-(iUn'`6>z ~īЩT5jkϝ9V!hK?H. ԋ gr=X'IӀנDŽa( yGj>^-xS(Y[G1 kR5F5G @0kI(M-2:$!<|Q @ {Pc`g;:$#3B>̇h q>fbfH`bf0[>Ӈ}X jF$48hܴ\)ç4OjeHw ^FDEskj.N>kHV4 H ޢ4:(4GFʕ-7h533%(400 D AWNҠbga`B"Ԙ1k,&WɈװbI+{69I\c(06TE}NۦoOl9œ/1[/CNO6xwr<>aoTz>}uvdvloY'{A%cShϲm|܇ X v1(F ċ@$|ЗR"|ox`+T5ӈ 1 +j z8x=(q4<Eړfl£E[oD£s˩OҀ00̈́J$Bhܸ ã4ǵjx\rx Ǎ1z< fAQ`< nOÃ,<^3Yx0<g,6FO@_ /؄^ AN "]w@/."Ce0= k{ p@:Vlb p9'=9U ߦ+m3XxJ *͕&H$#II2$ 3),<诓HШ܎*B5< (ãD EHÃDHÃ?OAѷyxP%c `z6 1ԋoN0&By^pt f$NW ?12gگ9@M$p+99K~o'ao'atR^e"rz+:r2y2kyj;]Mk_qGwnG||||||9aD^zH{{K轥K:*B@6֝Xf1T>L>\>R>0MモD:>(z/;'Q>moFHͼ_(P >;, bnMBgNijڌYDŽ6,P%@\ĔYW2wwulӰt7Y_Be8Wp~ZJllSl8hGU8h/G0e(sV8a32ui ¯p,z2ٵ3s5r=[wдD\b̸03J1W]Wجn\'E"(w=6)Qї[TIDJL&t网,)ȔDRTLifDBɌD*LfuOaeM3u9Z7~.?0mNb1BCJͫV2Am2Qs?[Ö~aE֯9F1G56w\g/lFoq>&>R(,(fK=!3.}۫A #cR>!&Nl_trDpL_&8qkbu}\cY蓹Q&]<"m8XW'm@Z?v铍-F -җ0`0a'cCZWҶ&'{㔬)Y1 |ݜrRӕO&ـ!n0 0zJ˚kf68}zYwW0pOoN1>!~QQb#VdƋ^]㛒jO8?lk#}jDglFJX++v;V'D#}cW"*Xt࠾?f]F+ޜe,6J6a q#&#QcxxP%xZ##㠾Ud=w2yoUzM`/fUe36^x}hXuc3v2&O3ٽ1I=r׌[)FsCȰFOfU;2ar R j  T ?-aO诇ζ>b]ONh^m,K`ĵ;9藬_ U0jZFezLzwUj:Ҙ`O;rFF}׮Il.@dY@%yd-]2N_C@thP #Kdv~xK_qVosޜ^cC̳F(9#Dwtډ=U P`ʴώ4d0Ŭw}Y>Qj&l{R>G d>N4ziS=L)V^o/1|dhﴉ,o_Pڨኬ։y<ޣ$wB ٌ S@z\Lo:a_Pa}Q_g|PxZ7M7F=uUߗ 1ǯGw+*E%ؽ'8GJ* zY}۪j'זR,bZi5KO-XZf/V`NTTnmSC9ӗ*e,H}V_la۲*arn&Y7G kL/jӝ|dx22+adH+̑KFF6xf?n)R=Z| k|7[uSA_Vivi-r<%?Yu]Os͞ ! ?2WU銀U]$nCuA(P"\j* [<\xBs˗"/HQ?Awm}ףUTǥ*x|3u-PY @<@gs+7ySK\9׌si*ڬD`;ୃ:7ZCm#$t!wVdWj \t#vWEaK.Uk֘M^_WYQ:պ˼l:Lp3&7b$T*$ *JQ5s_CT DV]՚-o3fLHHMjϨ9tyվp`6?'p7׍:J؂2OeMVdD4זibt**;ب'OW<)*HDL  5tM}6@'KQw/s}5jASlmC7HV5:@VP H9{a{IьZP0DukR᫃Z$뭩\mҀr[Bf4 TDeZ"x!Pz!=<@ /]0NCH>/oS0wk..EX~J$H$t\\`HIcѓ@"r0HVk5eڲ_'vJĸ804*?8tlZ/L|p(7F:8a52%TW/BC@PGβ @v=E rjYB׻=4I$$$IsyG9ͮ *!|k,gN<8fm~;pr23CBF!?nq{l4IuP#&Ee 4]*  78$yXNNds[Sz9rɪ+P,WV夡Wx!_7hZŢFWoNx#UQTblno?; c?N"!7&9x*B//_}Yf :HaxeǡΚ&֜ u!_mM91VͰFld;ߙYBONZw[$ q m <*oS4CRW `w kI;9+ ߰QzN+$]&ԵEw ~]is/m>.d>Gꂻiq;&۴ŅT"չ ҇YnT M9HDr 'AJ!YEM8<` FיڑԣIpD $Q;j(!1DBě 2Lq0p?a $JRY ^M(PUA?L=\ӞIޅ4e4UcS+HRbQc9Si}E i7Fm:cueGPYzSoA=EV{Z? ;L~.򿥶r9='(?u 66*b{|l %כrQ-Myk 0F3k sOK'V-x{̪_P>RR&&w2Ј N‰kĹiHADW !f ܼgՠrQJ@e[Zx%|Ž=>L,?< T>+E l!&rPdoNCi%-w J.o_΍#qmX9ߙOw`r#q2oб4Zީfմc\dXz3 Lѷ|`loQjBv+峀G1) EI58"p1AF7P6&aLW*6ƷCM[>HbrT ]}4=L$nƇ)5}Ϡ a"l-t: 0OE0ecz#;7s6]ZHVOILL/Vx9yiv.sJZ,VڑD j<(U#14`>r`BBL6tSV .'LH!YDc=B`RBE,&,#E2[S?p.j Q<Kcq\ gCaLĩ_R3ƫbـFa I^ mԆ#>p !ylvXs w3  ,#?JԴIC`B27If&JcR `5bJɰ{7&1^׈hiX2iAJcǓ-L%B"RɥJ52'^YAZjd%Пx} d=#S\xjHQtzHi ښBubz*:*:wܫwQc`afb $h$(VV*QɤT {O a2E*ޅnj_fڕJWBF~U4mTdZzpHZ; +d!0KSnh_~z8zT;IS<~_Uc5-9]Dra:)Js4"IܹV3js*N>}98Si0)d 2.sy!H ˜UEXL{uїpbt.$˳\TbLh(bC-NTa@b?戆A`aBC r*>rI0?r[(Ԛ>oEa Q :&}.q ӘI ˵o+KL%劰=W(Wçk[+ΰ*Ɣ}nF#Sg AB!{{Mip f!k g YgMi$5Ar,2dM)tLau9 !k%*85K8䫳\f }㘄_U P٪WqǦ<'Jބ@3^/]$d}{%W| %]9D ]YS]$.k .185GU(\%}!)yBAP [BG/V/|X/D|eZ@"$^D5)DةZB")0d }@*T**ߣ݈آv#jK FOZ]@itŲp|҃]OHN)Sd!轤cuRα#gSĉ!Xh!H,E'8yѹy<|62>1XiFX>@L˼_E)ωb0+:ܒG*T鵋I&H[DB!UUo"dhJ {2~Db"f ;_@7$a붨50Vm$RFH.(% ="YL^-UCx;1#!|"pQ=t^AɨKV\w ՉaHN: %Suf-E%%KELߣ!YR$LYB-˥X| ՉDBH]0d90_Ju> Kin n\B7u T*A+"JnwQfE9):BQ([<O4 WU=1FӈdrQ=G`ҽEQjD\W" T3Ru^p-L'J>L@_xh_@2ף^G yLz0/zY_@}IW C/ I񲾀4׫?,L^U <,bQAj_ՃI FU}p] HSE@rJy=0&O0(3$s+վQ6HS%Ld}J/V4$eUj_RE*V3#4 1I^ f>1m[{|o;$pאP8ɧi!o@ث2W[ޑ1Z5 91%%BŘ' eʮ(T(bU5j &W*brn*-j^eÐih`RM?TI"dX 8 rwn~$U> yϐREQ?̲pOH?AIj9@0͈1o O|N8($NƲГN+ NSݍd] 3#<_zڅWAm:nC54Pzvҹv)dy۔uHI|Ô-kEN2>O-Ts @\ [!f,n9pFjN@vrvb,qke~i :{ Xo+˵;N@;~8e4mLcIC 'J-$IKL=B3`lrj0fTm{:.i [E(T(;%~K+w{i[κ{ 2, ә1gxPt1V $=d *mc*F n`yx)>XP,c+-{d=/6Sj jZWZC0 {(wcWUSuJ߼CuH2S`HC`Ցj&4@92,}27TRc3h]'NAAcR^BHx1)VmeHjH*ׂm!qE~b~eO3OC}(Hm`z= 1!dǢҐRx ySH$iI^Ti֛!s"Xfar/UuD*Fa6indLuvmbڬS):%Y1ZLY$dEeLق_aƇhu* >#2Z Q5 9 12&pו"uJEEEvTӛdj7 X1)̶2v5v1Wr"\Il4=/{_-SJFm`:z2 1)Y*籨4ܧIx@p ҒҬՐ\ ,4faDUmrp% UW/V~PK(*uSuJuyS!:$YQ)[px`dzз/512J́22FT Hysh:;*ZT ^BXċI7l+CjWCjWs YJsHyeO˞e3^Ri?|5#7e6%K<9S?f%IKJBF"Xk?&+|0P_ ]FKav%Q~1=F-'LY$dh9f:$Y$+*7SՂ/ٵ}HM1Jt)7Xfjh___3gȘ]WFT)=(=(=QbL*aE{meHjH*br=VCrQ8`t~SsJ_ p73LW<Ɣ,XTRT #H`%IKJvp5DL0I*F02E\2ArZuC-ݽLrt O \]]+ ":+7~0$Ǯ",$uVO7jBu&!E&H*lq-Xfde܇׫<'o'q+>En\J1=E bNҙ* RUE>׋E6('_J LEO0kUlN0LsKjP1SIB@&z' ʅXU {7z4r-V[` RLߨbvk#ݝACZ鏙TB;6ӽ0(6A޻a2N/`wH|a0mb`_xt|up sel2oV HyNLLpW[RH1* $Br!VUC5۴Yt}2% եRL6?6Lر2aٴz|sCF&?Ü)|NW@ߍu=w^A rUܵ^W'`"-<ФX ZXCTC5Trm-~&-kO*A3*B,]FIY\EvXY t=%Y%_~lIr=1}Ne_@y6&ӛ[t&C_vm,ŵ/n7} ٵB|uɕ_% ̕We.tk%//Jr]%L?"to$IٝU9]*ɭH)Wr)wߠ^pk76M/I{z{}Krod{{p^V\)ȕG1Ir#&O; U\y^dmr5 Wst%^m8/Krr0(Wp~HTa-HS> kZqEn;Ew ݦ="=KEY}mh'mZSr 6ޱ6m{Ͱ)ɦݓa]orm-r,iOtn/.ogM@t8S.}m[m-=ߗQO&eZv L-\opL+ЛNEpiůN_ Lu }_Ɓw#)Mw'ƹb8P܋RcGx/e ܗp{c~0K++#<Z l<ĂZ0%N´/~. k^6hR{٠C_Su ~OVxV,ǥǽk,oAwy7rdg p>M{/yYV}/[.7wZ73].5(D#q9T? 8MW1N3 p.|X#t4b3~J%U!:; q^ M7 xӔ _i2 8|/@Nz#71|{>9B x/+{>OD<7pfeBubim&`|x 's?ċ W`If,*,/M4u/0{f޵&G'zY3Kf|*cmc8 ixxitg_>>Q>ux½̓L@}>id|2GgC2{@y|?lglp \`=xF ߾|sJڪF "a\x,{n.,O[3w2Ta,m[pgZ< X* lZmтXk>]Um<ق[pW[* ΂Cm[Y wSq? a\`CaWZ |fY~ >jX,'k}xO\]ւo;-1 ~łX'=R{.=Ti,8͂[pae~o- ~̂Z{?Dñ=U| .`E_}OG"ł@(j)\iy]8].@ ʛ&UY/s׸ I+>D!'&iJr ӄ(Q0< D׌9s f8mZ)V<^%tcڙNNN AvQi[Y~ }vw },}Ntixz׶=߱)17"[ [2%wf~b9n#o;bv3g##CG۰t_@:lv{XuO-*mXγ1W_Җa}ԝ=K %sظlE`;]}'}lM-'2 J$54_6KF2 fUʆuݳ׶h#ohdܳ˺Ou0Q]9>5n8~y6b.y s5`Ԝ׼3Lw9S Mryp$z㇠`] 2WWa~s|S&ǐh*Ŕ5iܒ9sfOM#{̀z&r 7K̀qCHoGԁ.yes?}Ư N^&ӊP9tm`s*ku.G"E |o/9AH;%p9C^@l-IڊES8o\c.}b$cՅ|T? X0qDM%8$06[PnsJސe9[%_QuЮtO.־$xʄg+,  E /ƹk͊˂A:|X{KFRbM¼͹ڴg̞:͕(4k]pvTui1GRӶUxq0^rG)EB6f>DxG4'Ϻ"ӃwmWGcByЬN?s o2[GCʎ1q*8yJ;?$ֹ?ehL#S:Y*=Uv}@$`B=.MS|o2yҭ!-5T w[92N8.>0|t]3D./oar-2.5.2/third_party/rpm/README0000644000175000017500000000174711757171206014601 0ustar plbplbIn this directory, you'll find some packages or build directories to provide libs that are needed to some OAR packages. /rhel4 : Some packages that install onto RedHat Enterprise Linux 4 /ruby-DBI : The source tree for building the ruby-DBI package. To create the package, 'cd' into this directory and 'rpmbuild -ba SPECS/ruby-dbi.spec' You may also need to install ruby-mysql or ruby-pg that is needed by the DBI driver you are planning to use. (ie http://rpm.pbone.net/index.php3/stat/4/idpl/4853637/com/ruby-mysql-2.7.3-1.el5.x86_64.rpm.html) /ruby-GD : The source tree for building the ruby-GD package. To create the package, 'cd' into this directory and 'rpmbuild -ba SPECS/ruby-GD.spec' /perl-Sort-Naturally : A noarch package needed by oar-web-status package for the Monika tool. This package has been downloaded from http://rpm.pbone.net/index.php3?stat=26&dist=54&size=17390&name=perl-Sort-Naturally-1.02-4.n0i.7.fc7.noarch.rpm and been placed here for convenience ./oar-2.5.2/third_party/debian/0000755000175000017500000000000011757171206014334 5ustar plbplb./oar-2.5.2/third_party/debian/libtest-taint-perl_1.04.orig.tar.gz0000644000175000017500000003016411757171206022613 0ustar plbplb;ATest-Taint-1.04.tar]}[ɲϿ)ZB`7M4 QxרYwYqT췪{ޘ!=W CwuuU_`{{e뵊ެ>toZMz5Ż|=5}Co6OZQk=a'5|eig={W?C'Y&_ 9_כ $V=]pw{ ^W3.Q;YJX:|$j1rӱ3bD&Qe}L@*Q_$X]:8 -ן,/I BK@cP`v)2L5gq܌QB2YeIKied +tFj% m݈f`I*vi eWԚ_;ͲJYv $IrbDq2]oUu k3]0 _e-xjSOMzz%e{GMZeрfKfVh-6m.%ݦ5{w T\ܦC"U]sRA1U--G]j@XԯXCKQאѶuKQ7z(z 6DR7DNIz.qiFs,/rPyΉCңK3e  DWuxkNOM;!`&QyX[0TQ҃2q@(0|?$1֋yRyr,JS4*Æw.Nۘf_44F[Ĕ!XtoEҞ9%a>sy`p])zf.]XIHyɹepyLk4`km `" 4tmBαeg9Y0M.Ѥri3@:$M|E|X]>JcPpna+PFHq iqR4VC" ʼn.khlDTl qm ئ 4{bt?T dt/dۙMGqCClx}Ycڭ!FfmA*"6VH"euVj2gCRLiF:*y廰.K'bl_E$CЬۋUCLMQ`5ogcP L&(9E: 9a2ĀRYl42ye"-T֙u שɜSO.zt}xwyK-s;v!+ʏ8)[-V1<{nzcGD|j2k3)I;,5(OKSZ$'(9Ug7Z2+5v!zeGYd4Yh,YY|Z_W>6WMSkk@l4[?q[\#|;`w8#Fn`ƬΚ#6ᣊ$Nxӊޗʬp~B@ 9tl+(?+'p}҇qO&G>q;Y!,=r[[;L/!GMy0c YĐ>\>h ) 2Fn; *dPxt?cVkz,nhE̊LUVnۂz`x:mwj1f%9>ш@~7H,XК hْa ا{}YI4߲2BmR~mJX ҂;ubťg (=H!*pȾ,c{Y75[u]o5|);p|N;6]O:L,R1"g5QDxýU/\e՗[oYo>_//<п,J|Ax1Ez+vWٽϬV/!ZTQiaAAA v>0ϐ}װWsjTY^Ęn\ "iL)/9FHtBiy V"Mg68*h6(.xKilKpM+_M?-{09+t~uB>,/}f2q\wO;0w~hL׎5bKRMVxtYQ\dh0ҷ^3W}XA1 6ĵ`0s=»u 6qȺE)C:8+&0/+!<>pl5`JUѷlzlo9@$A fC؂n$Bv  AT`.CByi2R!'1ab2{-X 92##0p: YH+z8 /qV,u %5k l1tVRdz#5,c4=of悓F?"Un0AD4)z9y>4I5![e)ub*Ct̨3p ; .`m)kD4 YUo!&:NoX3.7,-q %!@ܼ\6 BD'; D$%axLb.-[#F)t:j߲v)5( ( %*?Cny=C3CP(4~oM{o@H 5 _=rkkȷμ1]4ݯOϼ50H?W/Q{Q4z(=72h?20zTF\TiyX)  FC2D%䵶wPfA V8iʔHXq.9ox`!P0T*8 یxhX2(mvc V Ɇ ͈g058 '*LNr#[oV}ZY5W ͆,5JvGx)S#M?Zf+=>E*`Z5T0y}a|9ihGq 4Ueyևv!*: ch3X)Eݢ;7.1EH+US)M`J !]ZwJ"ʈ' x_=C"ir!?(.)]SU%-s+R=5%MWް_oeke b2h6@2Lt] ]Գ`(G;+ LDeh=Ys^m+I*|Y:xVY+V00(d| eJR zT}b;U*ziW.W`SR,H{!PYjއ0o O#^o8J +%6v*9kX`kT]"AR|q.=v.~'kZGkd|2 BiuAãNwvNx?/9GΧigZo\DDݗrqh&'EVxXKBA^-孄\(i*x Sz!3c)JBx FXU\4-8mMl TP$VACKNwfܧ&b $۠pA;nSX\8 0"d]׉GqhjВA3cQLw+s޵VxW(hwJ1~Ĭ5K?U7X-^R~h:;䱹)Q?2:X@;xKx]CAzWԠf9prMHX°oMF!E?Q+|\5^{ڰ\ޒɩd{97Ov{?fNv @&㩇K"D="ngDsM_WMmܒ)gX$nB}ȇGix[#DF;y}0>|ާnnx/)|M~k5RvTw7 GGR#Vb&D1qTncOlJߧ.k,s`t30'r=t-Յý?;ճ + >ÃTHK%U, $S41VYNO ̼Wxe^J§cQ?8χ{n@f~?.m.Cg4Y9G"pK-፫{Q+'!ioIƬ} (Pmے `>|W}GYċk? ~Sb[UcG-Z䦉)#߼->63 ڲ#k&`5Pj;j%Sz/ꂑD: $gpB#fڗ!*f\/zTY I Htj"@E#St5kB ̤#l*MR5@۵Wr | Rʶl^P<:|T_^m_\6je 5c;im|:Tl H{JR访A,QľMPo2_A)$W›?w' bro/. ]"JhXT@t aZ>ŏ0 F"өw0 pL}xv5'OHdfڐiXR>&u8KbSougo7O yw&?(h +;* QLdT \+ey,\tVҧ@$tRHśa&(Cvq;۾3SO]Y?:_`| H#髏U!iIʥOa ןet얄Rݟo(KΣڴpKȽ9Qv-c0>۟`h#4>n. #ox~i0ȣ0Pd|O 5}_%3JJ>"c?a #WxT?cH<4ϛjٿ_@q0M J 2~~qqѩbdڦ=7u=wEQ.:9Qb(KܙD0PpO)L9֌z֢gWiyĒ97=gU*R{\l<Ou7St v[ vciҼ(R (ȇcMRqnq #{⮕ʵayRp7E+Li܅ëmL } I_SK&eDXF5KQӞ vExb&>B1 KRJ6P!$-/CQ}o TF~ nW!~SoQ[E76R,ۻEi]- &$D#H1c?k0_^5}J4 }궊uA8wṟb Ed*s=% \M bIJCug,"lW{dm2u"n[S@#^AP̭ dVtS1%qJ ۮOtnQSpN 1^T~/6TIx8ygy9 ҳ7]}ovm$QPͯ`{K?M# /¥p^ K\"$j<<!2P^_x:Ƌ,0¿HNz~4+oݐ?6]?tvn8TbE2$Y_)lxC:/Z ڐ"U{l, Vbyq$PVM=;$vz0VW耏z< ^R2ƣ/jRjn&v l4g֜irMĹ5us& C:b1z=CkjTp2Mls/pM} mpf)8Ո.l24 ӛ&#$} ߘss sFO:i΁:.&/ dۜ+RS1) )FAԻ\5،CPZ\=X5ESh=Z(ǥXxAx, (&s;.ګ69CcsؽOs&׃z,Ce4Ud l+A@[x4:zIrevY'3}dǃwuZs~ba)rF ce;r'jq8]1Baxc x؝yЫb})> 9B\[Ȍ$n#R=#uqk<Q#Uj'%NiEɞE{ٱYS/O'1n1-?nv yuglQO>_rfn@TǸJd A5_xn:G*p<6-twBR$uHQUiJSKq~]2 _Enyoݕ91(WWxR]׈V@'aBׅN~^Vi$Omd<<^ewѸ }y#n2Ec+ݽSr%ߣ՘8pءHrCE}9'QTݲ8~١Z9x< -¼>>pyzKg&~^zF_Ǎ0W-Ljlime1u^?5޴%(]!İ9?#*iO 3Wb,K'?~ vh/!g">L$:ĝ '+QK|8X3KoijByg|&4$|{gN{ M2v lPҊ%p\; |'h3ٵM')Lܩ;|?gYo 3&TPO7ʖ{9 뻁-d, O4*.~ЋTi'U>-ܳQ@tnKq. 3aRyt)j8n‹~}ω(LxLo>~R~BY?RJf}6d|Ͳ1̮85,L-:p9A ܺ:fɵ)wMUzSwwpmF ޗ fQCH2ഥ+ }Al*O!7EAȅ~s~qn-BhRŰ_\>Fof_57\w3xBj͗k -,)PgS ɼw{Ki$F4r&cnw<0P$?KSUhXhvGsD@rV1c^z@ /VCyu +@Ȍ/E* (E08 uȧ[}$ Q܋WzJcj`ÎvG fb+ +z 3(ZO &8Խ"Y~L2ŘbQ,_aI@By|U@}QRDI2tQQt~9ݸt-PZ*Wh*8KLܺ2GҀ eb G34ry _EIEnJ ?Rtb@_Diq(uT(/{޸A*vT?"\oj撌7vli{$NEz(Kglt{M gM.%4=Np?hT~JA]xa'Z|M])%>Jj5_–#\Hk9rF@DdOXQxsk'Iu}Ss >M+o;|ם?j nj~<(Iܛ˱jzn l6b8rLb9QI4ŎƐx?(EA?WDih,I9GZ(~??[mP`<~.?v")xa9!`F2(B_7cؤC#hyǡQs2W0!Ժ3BB|2_bΛĭЛm wb/1ZE= ef^xW vZ 1.k#0r;`%ffǑ7ïG.ӰdzH+zo^._Rub\SDcLћGrM^7!XITZE =Kq>ucS&]@8S.CiH>2Bic.B"eEG,5WtQ> OҨq-#LZ2UNoG~R[V(im3as6Rl_+zVg_/2s*(r٘Jv'¼C k\s 7, y>D ]u p0i.){VK:(D!N"PoOX`C5_ ikIy,dt%EKFb5vHnw-(O5dhWN'^i4 +w~r&BgjQ䈢#t53twS༊,[p[sc}7WI^^I 'q\ .HG1M9#y׎xhJLG.ZJLH$S(7f7T}fװyl~b9jl~m>h% sD*{E^el '|STldt?\@6gzc\GY{q>h9Hmdn>G\D9M%Fz|#tEo/`1{f6:сˍ#@6VaITA_ LzvŤ )4Jв$9>gQB5$Iy&Щ2֟/ӳhY08Q7Řbzy%YC.so?&yFFÈ!RElV }T 'bQ}˗g,25CgXh 8A].q?N]Bjhjntg.A+0n}wuBxlS1ԩ>]A&rsݒhi-S\8W#5ӛÙb<1vEb)5fvB<+&l> FVJeY,A(YS *W>S|t$J+Mu R}CXЊ>Vi"a[_"Sz(eЗYdCO}2ƭAZ ?͓F],''{+e|"- .O-E: d{8v{m*п_R7]<3젏}h1H˿Xl[[#N8<y0VddNqYW Cp( ȳ "P'HKevU?Ig0lp%mxV 5 Aˡ7-|6WOͿ|qx,/2hH4PHAj6xx.拢}urҼOo9 i4Om=l_vrVd%+YJVd%+YJVd%+YJVd%+YJVd%+/G[./oar-2.5.2/third_party/debian/libtest-taint-perl_1.04-1_i386.deb0000644000175000017500000003032411757171206022106 0ustar plbplb! debian-binary 1221137281 0 0 100644 4 ` 2.0 control.tar.gz 1221137281 0 0 100644 960 ` ߯46`ix\q+xv&iJs;;v圤aDSUN9gus^3NM46:a6Fkw'JI=;']U^5;y 7b;! 0CvN-hg4vxO6rJ0:yWӅw#^% 9 bڛ3%|P$ \pV*|KRR<϶aZ,6:fǟ%Sp4}gFmFw=^r2I"TSF벣#'+͸dZO!B5 oP @M g3=իig1+kGla".؁X%+|͑ݏGpFpަ B>%. u sP#hPZ"2@_Ϩ8hBrl0$[ 2x  h+Aë-Fƻ.zee蕬HBg56^DP>i6·#y|>AOv:F? KMQZwq5~{6Ț:}QҹJEP{o:!tWP,0_igUlxy΋?=˚%v2/TnEZAٟ̔?W>׉,jX[9Ws^wRF1d^`7R&ٺ*ry%d.6jCdr5ip bY]m h;ϛ|$F _EvݧrNqMQ䅨rw}S;Qar%TA'}dnbV'j/UrVbgm(}7aBMJE"a*^q缯dSB/V#]&D"H$D"H$D"H$|'(data.tar.gz 1221137281 0 0 100644 11347 ` = @ɲ5'( (\rd $3 *^+^ת}ʪxx+-zI۷uilzgh9zJ hԳ# ;:W_%q '8GN !*>wtsr;=XBbهDER b D|!*Mx'mԟ˷r͙upYW'zלJ%p߿]MͷZ6[|ͻ$_/3y :SWٰȩS0ȨeY$mi>ső܁<2k}{;/:!u[-؅xnv~v>== ʤt+?R1gǸ/yK+dαw{?:_2_ ?'|)rʲ[p}*.ʝ|񭧊okREݙמ߹T@֢_fmO2mH=-7zo}6Y3oz?tE>~`*{ݎ)nm1нCY̺ELX t0^_3Ni\K3w\b'V2QYr9"X%+SbYt|9R_ $PP@mb$VH F` L$ D*0{QH\ 1C_SRFMP%F xHt"`Z*q qCTJ>"b$Z"pP-% !$@T_A()B氹CHB%$"+>@Љp pIpi1 J8 fC.}M?iТZ^o6PP/-lЌCP/%<1Bl C<W *Atv@tn˫'re?{?75n.\wʆ&WtsI-]έu{p6kpRh)H۷4ʣ{!^s{ЌX Of L۹jEG^| `g۪KkomUt;=tĖW]ilxӃ)M6qk$}gga|pu`%vֿ 9ك/C>/[+[=f_<|=bӦ%#"b?mrOޟ -G[_QttZ;rC6瞻,pzeڮۤyJ-?(j,ߴ"nspy>c!Fm =bEtg>U3GiL+i'tNuY?lYvڍu3Щ<㎬#czӳxr:ESv,.Û-L"ٟs>&e]<}1b|/~=k0x}gүo]̀=VOw*t5? \o2 5u%=O=mνm|ҤcaIn]ftgH>E#stɓ[lӦr];?.ٵ3KȫKlqNhD97o( i̊W'%g7 _󦀫ӇXlоmτ‚NNx;{vu:d{[%%?=XttSy%bJ띯=ƱԬۖ/lj` oI2s'*8-4;VJ_|)l}-:0*ݮE/ewM<42t9ͯ͞xigZ9zݶE'L6s%f\'}Hl~'ãD.*NM0~|‘Mr`{x*^t\vnV{΃ 'F_|L} c˗_3|EO![|]72}ᆃYo8iwPv(䘹yhWsOx]w<}lOBs./_2ȥke.Qٶp2{ce.nmܑ,[ _:NolRSo??႟n٫ǔ.4 *~}n4vM־!eX-;2<e36][rZ`on"GNy= moB⪹]oܓR2ȟ-xӦ}^%N{<4/8WW.jT\_7;3x~5*?~Bb-JL_Iz![8q) .UZǤW;sP 4ydz|nGr1^Oq[pwޒJM^+uey7>)hxś5~+sbOͺZʤuJ2wBJwQ`g[|Ysmf݊3j,7۶[g{vmv譛d/y$c[u1JX[})^L]w͵FȾ إ!ۜ1}3dre@aBy\d՗bSĬlpl[K+gnvkp%=E^NeF>so U{UVz.%Yv&V/ri?ue o&8`BBѹ<5w:ڙtXvނ.Vվ=SpAe9c'& w,8*{>\`{NOr֦׬wSMz<+LÍ.V y}7;YϹz~#:su?׿UwNO{ʼn;˟~}DK qt:~;EM̽jb4 ]2`ٰXYzZ8Mf@*C @4dA2:eïVhU՛؅I2uF g4XV ~D8?M|6d@oΌ<ڛ3i izA-:0}wݟ=&0uI\9ׂ׭K!ȇ|.a^S~,;|;Z#ݵS˴;Zd--Z[FJh3pB]iZvWտ+E@(=~V=)d (ƪ~gzBb C(Vu5GE/Tj7X⡪?? JL9O r#䚨 !]Ho P=Z bʀŝ v)Wy<w- 'Yn|3o7 ΋`u9/hY~~,38M3TʪouTjx*(\48dIM#X#5QVTU9\ Ӝ3y:No<)EOc6j)jZbƓGhD (}WAQf,VMn;(^5@A0 /?PU?K]oɀfL1,1x:PR0^rzLF>э}4e \ dia}W?>㓪g;>yG5|?Q]P84$tAa!9DŰGM.6R53ϿiUGv~M y_ЖUfƂW͜[0O `n17rj$4?h磆9J^Н 6#B"nY?0 H+(Rt K { !yaEă`f@}c垏^JB{i"􃊈~h`媫 v ȶt&Sm/j0R6Hpm(&3b|gӶ9r ~f<' lƽ==Xq* H{z2y*cj({J'ڱd¤Fd#0h>2Dc"ID|!bE?N~*e1{M1{Vzw%pO'}&~Dޫԣ&̞٣;M/e~hSy~Szoe%ijR4ԹHA\87P io/ ~vW9  0 q:K@,q/AՌ@BG),NT(RB`Cըĭ:9&pLL8֒š69Nmu8lҀ8|:SC$F{?7cLE?3|##07מgJ=N1a΋0pP`ܩW=ZK FO~|#j/ ُ>0r4L3K8\,ߐO0jԀoHhp0&h3?K5</1p?8U K7Ąd Dpc5\7&k%o;kfʣ*yj{tziyc.ep1m|!~6!!cbb 7&60 < fLLp$nVbg@&"=AۤQl)$ n0z ǁ6KIʲ"0PRm : Hf `V4=6hhؘ?y`jk/u 81w(d Bh=4oUlM.X y*1+l W"4+6ш5XR%>AX{5Bpe5=Xcg;pu`2@>% $:zQwĢbvc b}|mܰbߓGE?Ӓ Ah"#C ܛm˶MB ,D  PNyf1R!6Uێ}@M_ RbmjPYYY틤L\t޷h[]F1 薡@c4 AT=\/$S@HFOg88$j&S7}/ohA LɈ-; "hr :w)h XSaTkf_޽9$`HM7:pj _J4`ny@٨اqPڣ~%M5O@nSm4u VzҀժ} pڐDf5B:"hzC_C!)8rQQf!shϫ+˯ UZ jh.@]o Hj*Z%JjǨŔFICv5pU5Xe48 rxi{F9W Q&V@,WAԎ R":Ao訚$+ o)o0N?=Jc$Mi4:W0i\QK3}{Ɂgο/=Ns1b`⚐W/@D8XP cʪ8G,>&#)g/ЛnTN(.%$ #B_PkuX ޓrz)ƤPMHV>q,6zk ѲQ*@j`ཧrtǀ#HjcmmO!!E@\/n0*csQ1sk15S^bHH apEs+C3 =JimbZ8P@.[U’;5mلfIf[FUSڏQ3V N}O 59*jDy , +mi8JԨ @@ DlF  2*S"ԃ ik")umaX#M[AL_`^BAgj tC0/6*"G-[: _}db/0Z:"*#jz9Aow9Hm9N1Jq!)@Ԏ uWќ *P.z1BG0P̾[hGF/r$` {Pm(w>PZ@lzXr- ap?zsKnhv (*Sz.P&*P3`==\?COh*^B^#i8-be'Pԙ$iSo ~żP%(~#BJ"lAPXT_ PҦ7k 6aoppGul+ DD{첡 W*̟Pp0 *CؘU-T[F|KpsO|1T@i"(9*0GPRtQGĘ4viդ̪J@|\HT̷'@o7חS: $?DЫp?u0.................. ? ./oar-2.5.2/third_party/debian/libtest-taint-perl_1.04-1_i386.changes0000644000175000017500000000303511757171206022763 0ustar plbplbFormat: 1.8 Date: Sat, 6 Sep 2008 00:15:02 +0200 Source: libtest-taint-perl Binary: libtest-taint-perl Architecture: source i386 Version: 1.04-1 Distribution: unstable Urgency: low Maintainer: Pierre Neyron Changed-By: Pierre Neyron Description: libtest-taint-perl - Tools to test taintedness Changes: libtest-taint-perl (1.04-1) unstable; urgency=low . * Initial Release. Checksums-Sha1: 6c983df49ecf314c4dbc6439afecf942bd68bd93 849 libtest-taint-perl_1.04-1.dsc a0b7f19be31fefb1a475466599903ee1e33f1191 12404 libtest-taint-perl_1.04.orig.tar.gz 37041b0bc7c9497f532b44edf82b96ae0b3ab773 1371 libtest-taint-perl_1.04-1.diff.gz fbe6ee3db4f56eb9286b7ca6b748c6fd2b9f730b 12500 libtest-taint-perl_1.04-1_i386.deb Checksums-Sha256: 2bfe6d1bcbf3bd92e4cbb2e15adf28a29bf818e4797017cbb68fc02e262cba3d 849 libtest-taint-perl_1.04-1.dsc ef7ee6bea0323a44f155298c17873ee2871c9aa3ca69b72e62f842057f0eed90 12404 libtest-taint-perl_1.04.orig.tar.gz 2c0ccd137eb9c8990a556086c46cebafefdcdc13d770b3ae627473e8a3b8eee6 1371 libtest-taint-perl_1.04-1.diff.gz 9421835283cdcc1b81fb0a557e8df89d945818c84fe8b56c3f10d863895fe99f 12500 libtest-taint-perl_1.04-1_i386.deb Files: a8baa359887fdc03919b5cf387d51b74 849 perl optional libtest-taint-perl_1.04-1.dsc a005ab5a2e22ab462b4f3661d1d1d5e0 12404 perl optional libtest-taint-perl_1.04.orig.tar.gz 5fe093e494c94fa0ec699dc252e34ec9 1371 perl optional libtest-taint-perl_1.04-1.diff.gz 975ff7eb13e96c1ae08048ade3c07c33 12500 perl optional libtest-taint-perl_1.04-1_i386.deb ./oar-2.5.2/third_party/debian/libtest-taint-perl_1.04-1_amd64.deb0000644000175000017500000003104211757171206022326 0ustar plbplb! debian-binary 1221141994 0 0 100644 4 ` 2.0 control.tar.gz 1221141994 0 0 100644 952 ` [49A\*ŠcLđtH?=ǞV@"(;NvONJeĻo?Ҋuɨt#5N+el_G,z1O\(Ye;HC*1g k֥lY2XN5&Rd;,g{XPOP˦`#W\mۂLʼgY 2`"6:/_GM(Qm/{`9,[K6gJo[Vi(xɆ_( ݓ''R<ON2Ηk,dhcʬ̰aY_:97MaDYS(e>o6|-7BFEd#diJ6iڶ )X&ԙh;[/p xg݈Z#|V3|f<F{(hWo?SQޙ(Nr8\+m @ @ @'>(data.tar.gz 1221141994 0 0 100644 11690 ` }\Sx FT>HQѤC 4SvOӳ7]wxAESς<"ոٝٙ}"˙?O. D9H9R/^ޫd IPrƐKj&J!w7ߋߨ+ǝ=wr?_swͽNE$%P:)BIE=]\]t'ѿGcwtU7wsqd`.:S@_x+d\^*7ǒrK*b*tqcIj,Z8R 'CE8JT $ TYIK%")%˥bLT|8Wx2%8 sKLfQ%S(3bR Bd J\ X8@厘dϕpO*QAt(a1R \JTj00JGvu )&8!/%T#2\1bF d \ % TKUr' y8&MF %+¢UI"! h!gr% TgaX & НÅUP*LC %7RIhP9@K,8 6L(oT{`Br :vv xa4~ZBi20Fj U8Mv,_"i 5+e_]\[! >]cPu*k͍vu촦Y y"`N`3,mJVh}/.9~|BɁ]z#̜Ozxkk47qŪG=>^tu_Oxa{g}IZxU(w 0 m!#2'ώfMin1 3zӆdUSMTs!0cُӹ]mH|x#swĄc$t{2}^ǻᄋ烘~*~n':Yf6B[huy{αW6-uZOפ:xeᡶc}Zn~ƞ455 zz9cpVN[<;ܮK9rD1h~ww'`!fzyhZl ϞO8\;+޽lVy̲S~miN=k$-;szFqgd7dE8$<,c/O*hvr#N/kiz[8d^ۃ_T⎽${V0YD? ̛ݶdxHҪ=V)$zrgο{ݰ-٬d:MPT6CM*̾ptϿ X݄ftEyf'-˯6y59Mokh~}mU!삘KNtυ?.o_UL?:^U0nSn߉(*yt#bzS_rYj^F' o:"~uxᅸM,Xww7N7f7㎱ޮKb[=_}?'iNgjQO#o~w΃#Z\/}!+;U҂ҝn̝[l";x3] ^'s~QU`T2KOi޻|/+4rsIF^BOaڶmQM_;J6|ݕk-2?D\,3.yǮTx乽ii5uq{wg4mfAiء,،C?qwі1O"3\Z6i{Y&[޲ݤ..3=ֹ^kqieJߖ=/g Ip"4N;~cᶛw77d!hɠvVsmB`gБ}M:dTl &n>oݮoDzG:u||Kjn~>t}ͧLy` Z0k[n/@G񄊽FevW}ym%Ak~xxf{3-ry?ɴpYJXYQOZM|au{]ήc"j W/r8F-}t޳:tӝm]HYk o{-̧nI;cbzwpa4K;ijzd'[+ec֥:imTi{鱗{Æ]3yOTؼ̙uh%M.8ܦ.}5ۄkO۵Y?䘶%؍ 'kZp*ͶjKoaV+;צ]\[ppT{fN2hfo֟?qݧzSn9qܷmi#x3]-:'|~z`ۛ>g+,3\nuӸ.w~9j̗ҕs bX)\h]"KQG<`ǂTe 59tqƊç0&OGQHɓK W]%3,ۍ+//'/9f/qѤs'ǩ<.vyN܊KN^1_z^VE쪚MVgscoYֽYbt3}~/.0.soOw^ {ͽ{y"뗿)}e1vV,YHP )lnȰ%W l~&Ҹ]NH)+w6^fS~RֆEUK=gرGDk*)A/q1oލ +z?xg+~=] 61o ڧOb&u5w\֟ӿբĢfDη6R*2_u/bdB׳ <7No>?<@godw~;e%hޭ<3׮LIeW55tLѠٲ+LyIQmy%S,2z#x@т^o ?ss|J$z׶iVӌY,9=#n ``UkF0[">\t-ko~ִYnΰM5ԅutzl5'R.wSN.ΉJg5Y-&۶NogU yNͶka ]sݏ w2dS6J=zZbΚvw9yk3خ!w>>p]VĤ$O Yק7eWͅѦKY<]+f}T*GWTlSiVTӾz{I doH`mMݥOKX:>7Q5{iGYF\".l?ÓoO?2 Nqrz})ᮿ1}Դj^kGJn^_FO{`K!;:Y\zg^qgKm'[o3[kF<>*GMl\h|YtЛ~N,`_]c;y3ݧ>]7jVçOUӵ붝~egf;\#6^fAz*s%m`nv3 U^"6: g=VS=yΕo;p!IbI}~ven~vrf^JXKǮÛz~呲㢖--he8x3{_V;R+v8:U{'Lq^9}x{lN ycoFǤ1la%soe*ԯo mp?Mg\ ;̬Թ7B5~~Mg<OUDWr;e"<,q˶=]l:xjssmfԷ_7?ts"aKwo?|;Gqy8B7~ŵ/7x@w7$^"ߗ\+dҪ0',N*)0D0tMKpkPHLlhT$9+R r`21ؠ.R1KS865;4na:x;`.>.^>M=5!ì@>J 51a}}lYvڲccb 7.6(0<0f\LHa.>|aP3|8@6ƙI"@zí<)fy D4 )ɶP|οn >ـ6} TkB>k0GFCE .! u $wl&J! -ƑozBT).eOa"@8!3%rMJDM~Ik]4f*T A41ַB`Vpm2՝XeJ}"! :bwf"fCb6.:[N}lAҰ&߇{N $o3:Op~71pX+N#\046 `c_bp3i%Ǖ*~6 N($Dd·V222PKt<Dux]l=f-#pE-0 60T*@<#W TW:q2 ZD(&޶BW@Ѻ5cvLfx 6P0Ra0nB U0K&>fP}oٳ@BQa$ jC2~8m!+R cC !Oq pjYZIYm5'};_M ®\ꉚڗ KQcHAKA p$YoM!$6+CD^uR]~瓼iQ .je;#R^_d*VsPB՗X^dtN0iId _gȷqJ"51QXj&@)vW%|rKlFk9f Ҩ ߩ%VKQ2? /\%isV+` |3gE /q( (B *.:d `e-L­t .S`/qnO1l GK >;T|۔;#Ctل5I%M~  ,VE9/ZkyT  &+EhQvH\yJ w 8C}1߭UwL~jhj)9@uĒTJhlHlȞ J)R®ׇN=ks Ctx>ځ)BFYD2W(1dב+5[&MN$k- &Ih )y7:"ك"cGŀrCV)hZAliC&%1bPGA JĴWz .[b؀0h~S3ZCB@La@bv 4bv̆Kux"hS_i"\s!8f"#$9<%g:z9K1.IQ [Bވﵬf%0>/A5kPX>̨脘/D??\RoKw_psO?ݙB{IBՖ }%Q 3;0M sԶolV?g00/kK$3 x~}<}MT> eJ> Swfϩ_3yRӞFt$,ȱ@O[1V |[> ȼw.6ɼ+ь/1Y6#;4>:6(_*EG8Bzx2\Puks+^\}m` dh j-~rh>5F+7B'8)Ki_ܾfW5Bo#FKߢxt#t5&F4TKFI+n վKHi-/$6D9+7iokV[-F9Tap_',n3Ho(.'.Zp9#S2PXN Tp0Y#Z S 5#@'`nP8i}H}l92ƐX:⌸ʐXKx; p2FQ8jQ㊺zPvq;7Јo@0d%)`NҕK$M'BN ~ H.I @>c@xh ˍQ~#pzr1U- ϣ]RU]+Cihpz;wZc1 hp- ޜK47 ނSM% >#ip41}2h64D:f4\܌_BWi 48-cl~yKC `K`'C:aXT9%ƨ 5-k@КKPYФʏ(XT`PJT r ,Cs-a -r?X&U>}`Ry4*24Teh:.l d1TTMP-, MWXևe4~T6h\mQ9,cX6CG;`g7cUה u̥RD* U%mVl9FSs~n1s_n {>;3 cg5A}` t( eMz%woN0gYEgkPԱR`ךAq`"nruJKMk ;V0%+~lךl{MY&D7_&_!nxFBT$+A107^=n02ׯ >u5]k4*6LYVX(dhnbI/S*g3!kD/%# #=5:zB4չ pۏ!FUN@Jan!Qb*Tx3M6zx b,"u.f(M;j<-amv6@KmBP֘5]ӫ|7CQdjB FS*BTm)!ATej}FXY5;N$V,bk }bgB&DEgbgJX)DE Ya;4s6qx#9Eݠ8z"[Z3Q9 øj+@(0ЩEqVjW$TKK-EDkGvi N;uBOOQu tWt/oA'(g߂f&BC2I5F }W>5Mu'D뉟hJxS,[ TpEް "@VQݧZ{r Q׺AO;ʈۮGóY)2 rD^s8(,","ًs& #Q)u!*ȊIuP2w!*Ȋ 5{20M|{5<|l%jRA]4|jt!ik{*װ59(|jQy?-r7rSejS9F^7<QN CTN-i~͵my,?Y?5Oa$jSϏ\E̖OTnO!ݨs<%%SoPcgj/1Om* ){ְgkỐ.~`p{RRw r5§Z?uNy]?o§$C ZW}G(|OzZ碌Z:D~?ZV'|{KKKKKKKKKKKKKKKK{u0 ./oar-2.5.2/third_party/debian/libtest-taint-perl_1.04-1_amd64.changes0000644000175000017500000000304111757171206023202 0ustar plbplbFormat: 1.8 Date: Sat, 6 Sep 2008 00:15:02 +0200 Source: libtest-taint-perl Binary: libtest-taint-perl Architecture: source amd64 Version: 1.04-1 Distribution: unstable Urgency: low Maintainer: Pierre Neyron Changed-By: Pierre Neyron Description: libtest-taint-perl - Tools to test taintedness Changes: libtest-taint-perl (1.04-1) unstable; urgency=low . * Initial Release. Checksums-Sha1: fb06e92f101b398df48813976caff72aaf1fc3f7 849 libtest-taint-perl_1.04-1.dsc a0b7f19be31fefb1a475466599903ee1e33f1191 12404 libtest-taint-perl_1.04.orig.tar.gz 0c42fb183ed1ed6e7347178ae49b069d613ed5d1 1379 libtest-taint-perl_1.04-1.diff.gz b439851e36205ffa8fbc59578079cb11292584ff 12834 libtest-taint-perl_1.04-1_amd64.deb Checksums-Sha256: dcd27dc14bf00105a7f9580877a82e014a99d4fcd85542288ed06d0f89101302 849 libtest-taint-perl_1.04-1.dsc ef7ee6bea0323a44f155298c17873ee2871c9aa3ca69b72e62f842057f0eed90 12404 libtest-taint-perl_1.04.orig.tar.gz b7b5df1221e129f1a922083ad195f57cdfdd705f6f2decfcdd99d9ac28128645 1379 libtest-taint-perl_1.04-1.diff.gz 2abee8ddfc13b1c2ab7910af6f583cf11719e83ed816ec21a5e31fc179f78683 12834 libtest-taint-perl_1.04-1_amd64.deb Files: e70152ada6cf4ab14791875352adcedf 849 perl optional libtest-taint-perl_1.04-1.dsc a005ab5a2e22ab462b4f3661d1d1d5e0 12404 perl optional libtest-taint-perl_1.04.orig.tar.gz 713363e5b9ffb7d2fbe6f1e79baaec83 1379 perl optional libtest-taint-perl_1.04-1.diff.gz 592a21497f93b219fe7c5522bbb4aefc 12834 perl optional libtest-taint-perl_1.04-1_amd64.deb ./oar-2.5.2/third_party/debian/libtest-taint-perl_1.04-1.dsc0000644000175000017500000000152111757171206021351 0ustar plbplbFormat: 1.0 Source: libtest-taint-perl Binary: libtest-taint-perl Architecture: any Version: 1.04-1 Maintainer: Pierre Neyron Homepage: http://search.cpan.org/dist/Test-Taint/ Standards-Version: 3.8.0 Build-Depends: debhelper (>= 7), perl (>= 5.6.10-12) Checksums-Sha1: a0b7f19be31fefb1a475466599903ee1e33f1191 12404 libtest-taint-perl_1.04.orig.tar.gz 37041b0bc7c9497f532b44edf82b96ae0b3ab773 1371 libtest-taint-perl_1.04-1.diff.gz Checksums-Sha256: ef7ee6bea0323a44f155298c17873ee2871c9aa3ca69b72e62f842057f0eed90 12404 libtest-taint-perl_1.04.orig.tar.gz 2c0ccd137eb9c8990a556086c46cebafefdcdc13d770b3ae627473e8a3b8eee6 1371 libtest-taint-perl_1.04-1.diff.gz Files: a005ab5a2e22ab462b4f3661d1d1d5e0 12404 libtest-taint-perl_1.04.orig.tar.gz 5fe093e494c94fa0ec699dc252e34ec9 1371 libtest-taint-perl_1.04-1.diff.gz ./oar-2.5.2/third_party/debian/libtest-taint-perl_1.04-1.diff.gz0000644000175000017500000000253311757171206022133 0ustar plbplb{HVm6\e` oc 2\(:lmb wWrC^$Ϯ$ T*|>iV~:VzR>{$g3H)0E(S) +مkMcdv^>֮Pʬ8K 4Ss" ?7a개--bON}8Ӈ0fSZޚj7{-f0@gXxetrn]}A{slPK+"q{УJ{t;l7(/V7.Du>LQ؈PzdCa2- /K9?Ypr ;jE1|+#RWo-IEEflgL$#V\qڮ1oCpRsDE)__ 4`iMM1=E HFH&(7MgƎAi> f kA4srp낄`t p!*g2 6,IE"EOPe7r|.lp(Ysm(Y]}:&&cJHV #GB k+*X akMkZr\2DCCV8Swl#Ԭ(-6zΝ*##LQnC_`f+C:U8 (ڗyQn'MȶO"2HZk$G>-?;<-=@~<}^?~.>b ͢hq(8t$拈4©"ϵvv9?ar.vvvEl!]93O>fMbP3j@w-{ŒϪ*Z7"T2{Tp^r}>gIc*d$PPeB9#m1pk*|HeF#V_ 23;{Zka=_O>m-EOeɳgM-h6'qIۚs皃FٷZیK*ʛX 1[Ѭ6گhpY?^ zsvJJux˫%<)|#lVo^OoFм78#lfJ0tNԦk7p+#1Ůn!?.s &"?'?*r}߱wRl,&cnr>Bv2ЦdΙA#&'|1 coܟbE ,w3y Q څG| d c[-,4OgEMs?EfUY;`\2Ӡ:pi0]=↻l<;e;zqJ<-6])xH~X %zo|dԶ@HH/%M^mѳg Y iY-4udl-$\tYqG o )|}9(i5:Fь%& WV\VH_ Z0 #nWF0O ؎l&`˜E:^k-&pc  }"b,^=Җ% $(wLJ}j2r뛌M ~zNy(Eu*3Q 65Nkph^kU20#,# co2#07nuxJƷ۔Uȣ?!:O熽Bm$thot).F0b\rl_UJ1$ҼZ̤+8RrS a54"V ~(sNZ6SR-_PA3bZWRwY~{dyv`>arx@dLM`d5U&ȰQax[``A֔ 9ix ,X;dmN,͓v8X!zezFF uC,~XymTcp|qzt):E?tYJ=wHxt_ r宷_p3|J"?Ul& ;:/u4cAxx 'V+@Ċ‡ГQ<ÔŠZ`qL )^ЭH|mIoL1\(ۺu Keֶ ryChVa|ۉNp!A$)=2k!l* R{KrV1+s'?@bNC`U9άbqilTtc=Vq`U#O9[~F@qVVrDOL\A, 9 4#;qiW߮ӌT+Co |aAtkYR u >^ږ@&y- NKJ.+F7Pd:J!Nc"]#/rFホ@|@giN K`_9`\(GR/ JcȫMF >F:`FIk^kEJ}Dlo-&͎@]F, :u+ηN-׶׫…d]HR4-L_Z18Ӳ z=OB^M TE~71˾TZsՇ`,2RבSx&h}> A 6:M\תYMbgpws7#Rlopg쿀lAqTi@bj]$tr7uwJx. ~ID< >pĻyNG=eɔ@98/fͪi +&&V6/A Zy[Fݱ BU/ -LO?pԢ2')\h)PBg,ɌKHۏNTVV)A h͈*UaKs f}|[c JP4‰đJЈOZ^ߥ>yFجVx |Ii5䪻Anw\).tu!'ڇbI!bph%y;K6C%Q Mub4O;t~UD;TbVByw9 VMV8v'rjG0&XHܨGzegh(gl+jFUaD\?!/L,EiPcHC&(jaR5i26 dsם1~voQ+XjBXfU(=/[y:̀UGFqDǐdiϫݝ$2?CS_b Hz4͝n>׾Yޟt31tCqh_c; e1C<5gGa+_1}PR>sjL@A0 \YtoRjmcW B7q^Tś#BVӰQIdfrt\T|KBd&*h}->G=F(8LjD<2,(!'!H[pdxD.2ث !Ƅ!GHp $[0@,WJPFɚr`fHzIW]JD&I|, NXDXFͳTpC7@pH ˢcL-$Z˴k|0cR&D>ig5mI:hT]m sAE:&0"w~y7n )LK3UffOC8e$U=gDaqd Q}K^i,{~z?r& `opҸz'i"f6lR%<XOM?KvN XS>ne:?7_zc> Ky^iLA%BEڜE֌M2NsX;d0 PC>5:n =xME^ܽVhdG'78~y;;`ID^e{i˗ϰ1ݪ5l'$f꠿(+3f3@Djr㴜n깥o/շO2DNefDRIODi*%+rlY1P+@P*+2 rPUqCdcAd'\Tes2fdtؑ{/g7ۿ5+v~Q\E~6}A5D!h@Q $_3!Gڡ F`0԰ gN˙'De=D$zf q+3K+v5.rP6nL?8GQMI7Nci׭[qu[n;9v] 9j,k*"Mly,?kf8솚C9oTPË, $]&'h}Q4VUV +/ U%!tiTJ9<KYLLج mh76\BQ-Z:~ޑ &N9Ae[N QUU *;ybEpl~^Ãnwڜ(8 ՌG! o궄\cr`vE3WDXqD$_߇3HTtt"<]J$MRNX1)/;`PD*Ax _HP5zLcH;vz-iîREXѧ EF.:Ò 6U(q]h3.QE|-$ɔes`O/VNhU W,S_nq2zpSs qo/Z2<x =DžE7HC{=1ϱJyEaGnEr8$Q\&_mpLVu @;"\^$Oa"_Sv*N[$o]n nöI0!o݃N=NsDTK-1xL8 z^Ӟh77pUhrއ'㣣nݭDcRN q=t~C,$]P,lI,Cׇ,!a/iShDbZ67PD"HvmCTO%wyxvNW(0/V.t&>oj cC6%.Hf_ͰgMV C+eєʅe^Q{)rfV.sPr+gY.]ưďQFkj[|.Aۍo%.W_ZZJ*3Lhѩ>O2Ezʨ$`Kh4"X:'1Z*qOѐ R' k4q@Cx?H "_=.xT^[x(6·^f}o^ _ڻ΂b1 /N^t0 _U愳sa6Ow$ Q#=ע #yQܫy-)^cz Ġ鿰4vV6Rsyk_!kT(/#xmW_ܵ F<74s2&rA O]w!u&TѠ?muHUqӇ~9WDi_ܞDb 5-CQdmITˏ[} {|6"[OcaˁlP,q=32bdPSr]yc9ZMr]Q(3 IE>;Jπ:l6^oXfO{-:A߽!/pG]rY5?gI^}xX}Lk[o~>IYcIN%Κ*6=/̇[Nڮљ0%;Z^L+.wCQ my8eKxpI[fNJJ%%)xRgIY ZwALY3QA,7Mŝ|%ޔՉBIP?Ep)8n}*a *aĦPO[QQY 22E Qq_è*QinUVnŲ~ <·\YPW"S +5*WB^Y=+q+{O`;`l~%nƲWHHMeԻKHErowvW77W%`#xW؄}u-v-8KƹHgOOSncW5P$3 F(X]V?\-X("Q82V2X28d!-NޠlCn`e VR0Ж1Go}x߻JCrZݱ*-4өj7[kj8[vEpaYG`X;޽cЌ6( WVUb U}6LDt:ck1OYV{9bhA˱)zK ;$ѨW>}!VhnTQCf\@1*H/  O$| 彫2ybvbssdJv'씤֤mIŴNjRvV4ǹ{z[5oJVd%+YJVd%+YJVd%+YJVd%+YJVdt!./oar-2.5.2/third_party/debian/libsort-naturally-perl_1.02-1_i386.changes0000644000175000017500000000320111757171206023660 0ustar plbplbFormat: 1.8 Date: Sat, 6 Sep 2008 00:20:00 +0200 Source: libsort-naturally-perl Binary: libsort-naturally-perl Architecture: source all Version: 1.02-1 Distribution: unstable Urgency: low Maintainer: Pierre Neyron Changed-By: Pierre Neyron Description: libsort-naturally-perl - Sort::Naturally -- sort lexically, but sort numeral parts numeric Changes: libsort-naturally-perl (1.02-1) unstable; urgency=low . * Initial Release. Checksums-Sha1: 9cb295523cccf4df840218ec9b5aa529df05b1b4 905 libsort-naturally-perl_1.02-1.dsc da7b21e222c8c0e8f9a8cf5587410cc57a836f31 10357 libsort-naturally-perl_1.02.orig.tar.gz d1fe029a6b54bbe1b00017a44b81a6e38fc21cad 1283 libsort-naturally-perl_1.02-1.diff.gz f03ed53963fcb01572adc1838928501406c88a2a 15276 libsort-naturally-perl_1.02-1_all.deb Checksums-Sha256: fc70a1de2ae7f24a8552204593e789748a9609b6e0e652493d283439964ce543 905 libsort-naturally-perl_1.02-1.dsc b8320a18e32d0a15541d95ffae3ba18e64bfca2478b8c74b80947ad2aa6874b1 10357 libsort-naturally-perl_1.02.orig.tar.gz 2b10d81440d976979534d7d1a7b842116942508e35b97b172ac169df491835c3 1283 libsort-naturally-perl_1.02-1.diff.gz 879bd4471aea3e2abead0dbca2bee59fef740fbb87cb6f49f182be587c1be386 15276 libsort-naturally-perl_1.02-1_all.deb Files: 5b329a0489217f5a5ab108590ad95983 905 perl optional libsort-naturally-perl_1.02-1.dsc a173f3f8f519ebae6e5e578e843f6e1c 10357 perl optional libsort-naturally-perl_1.02.orig.tar.gz 977e84e247cb73ed1cd94fa2b3914746 1283 perl optional libsort-naturally-perl_1.02-1.diff.gz 50453887e7fe9bb382f0a3d7d7ea7537 15276 perl optional libsort-naturally-perl_1.02-1_all.deb ./oar-2.5.2/third_party/debian/libsort-naturally-perl_1.02-1_amd64.changes0000644000175000017500000000320111757171206024102 0ustar plbplbFormat: 1.8 Date: Sat, 6 Sep 2008 00:20:00 +0200 Source: libsort-naturally-perl Binary: libsort-naturally-perl Architecture: source all Version: 1.02-1 Distribution: unstable Urgency: low Maintainer: Pierre Neyron Changed-By: Pierre Neyron Description: libsort-naturally-perl - Sort::Naturally -- sort lexically, but sort numeral parts numeric Changes: libsort-naturally-perl (1.02-1) unstable; urgency=low . * Initial Release. Checksums-Sha1: cbff2e23d80ebefa27a2fb562469bb3d687946d6 905 libsort-naturally-perl_1.02-1.dsc da7b21e222c8c0e8f9a8cf5587410cc57a836f31 10357 libsort-naturally-perl_1.02.orig.tar.gz 83f775c5aba5b86502cef5abcc551dfdfd2a08d9 1294 libsort-naturally-perl_1.02-1.diff.gz d57519d6b0f694793ca0e118f74d7d7769abb4f2 15286 libsort-naturally-perl_1.02-1_all.deb Checksums-Sha256: a758ad114a2fb02d9f4b8b560ed0b3e107597591b27c9e35f0b01069b6aed101 905 libsort-naturally-perl_1.02-1.dsc b8320a18e32d0a15541d95ffae3ba18e64bfca2478b8c74b80947ad2aa6874b1 10357 libsort-naturally-perl_1.02.orig.tar.gz 9a5dbe0bd341f9257a99875b866da4c36fa4279b3552a7893bbeaaa8728064d6 1294 libsort-naturally-perl_1.02-1.diff.gz 2426187d0164804677bf3010c00dfe96c0019c28defb8cc7f65fbace9727a4ab 15286 libsort-naturally-perl_1.02-1_all.deb Files: f9382e95f00f7e808a833f5061254ad0 905 perl optional libsort-naturally-perl_1.02-1.dsc a173f3f8f519ebae6e5e578e843f6e1c 10357 perl optional libsort-naturally-perl_1.02.orig.tar.gz d416d1838902644d339711b0d8a783ba 1294 perl optional libsort-naturally-perl_1.02-1.diff.gz 75eca3f58bcb359f409abcdf9ad8fdc4 15286 perl optional libsort-naturally-perl_1.02-1_all.deb ./oar-2.5.2/third_party/debian/libsort-naturally-perl_1.02-1_all.deb0000644000175000017500000003565411757171206023102 0ustar plbplb! debian-binary 1221137263 0 0 100644 4 ` 2.0 control.tar.gz 1221137263 0 0 100644 807 ` ]6_qVZƀQ#m"*ld6__f7&MUm~4}|X$=<9Ëb{2(- 9.NX5}$4Y3>q<\)#SʶP'Jwơ]unbOv~G F7@4zaqp(b?hZ7xFÏ>L>IgG8^KQPOP$eBbZ~]vv`N ycpރ8:77in&r:1Oհz]t?rvb|\8-0An3Ю< º<:DqX`2jl6wc[rzZ#@  48va:P@~gZ5l.&& N'x ;'ͽϿWg&џU}~Κi׽k{u'D?*uZO8+E3JRPieYK[U%RK6]|Tg"mB8>?DTDc;1I qRuok^b;V ^j RaRUG:Wv$_+mXID*d9QuKxUUk}}Qj*Lqִmili$ѷ'a|wMy 2Q=|(v^gBk @ @ @ Wo( data.tar.gz 1221137263 0 0 100644 14276 ` |F8NQV)$8c;,4$@Pl1ؖ ޛege=*P(P:~w'ɖo} ={r2K?? RWJ?T. @P+\6J8^(ʽKmch+~*kg ̿|w?=ZJKOhb(*3VM2 Ipk 4Rz__߷οҧRPFR(W~5Q }-7nEAWm%YG"~iFd3уm}qaEOXbaIw.|z[;/~ݾ/Dݩٰ}.44l_>9k!ؚZhv/^aWk7_ zbq %#Ί 72^L`>ՙG_Tqo_|Ru#MfG?W(||]G݊O!/[WX9pj=RR?>k1ƶ 6_]Z|0Q#ߘy.q'˼hkvڱׄl>?wV~r^[?1䳡ֲmk-i{v}gc%6 *Wj?k6kh-;'qE)xzMf$ڊ IJj|<@4'4eC,VnF:$:: 1fLDwQRf#EhA{:2V%ۛ! Z-XhXư(Fq *)BHݝC,7&A=U`0dH:PR-4 f-Jc3f+a5a)ap Bl 31MYHPBQ FNlF)ʂgl |filbdiF7,Fh/S:DP[,Ѹ`5 ,m5hlF@m &|8p>pYAd0Op M:ƃ |Zo(t.lf-C(ID iqHY@C$a  : 1yVM W3ZHZQ8$38F! FO84p E9@'DDw7W)J_P?rAAvxL `d EP~2R)||T}gt^t2J+/!C&QZ\"cX@e2*n$ kP aGlA R)A&<7ڞ=r=cQRE=чÕx<9 kf$`6x7`b)۵D2̢`-0A_tғgl)rx&Ҹ-&›X+p"I9 6"4٤V  iB 04njIâcRIR<рya ts6jTFh֘,m ͊t! 1$>$p'샷""aS4Ш&9CX9)O8c˂ZϬgj( зvJ HMFu)ଭ\"WT f8`SOep:Y lT)P )3Uhf VCiB-0 ek.IE:m'\HU@,zAH  BS'{If =Pˠ]h5, *H;R<PtF4;Ch4 jq$h j8VDNv&K~!p`P(IHhH#Ę͂zL`یVh, )(@gN:VϚCoI+'x[ugy4E҂pesfWa F)/-4 GB P (_PT ?"BTҧDJiRy=K7He*8@Y"iVL4ق꥖`;beQO/䩆@!J'7OC"A( hĸe>;F 9 L4E`a#@k&g"7ygh{#ݮ1@IEK/)D0Ph &V\8yp3odֱIKX (FHX)0.'> XxIm\VK\urPB(o $,ʀNTcD8$:ò 09Xd $Vӌ/XB XG8g4 S'FE pq[LVI!B/Rp |NIc"Qxf6s0$!iR,`v#q8uj7`$ Y9ZK rlsHv"'$mLw="$? @c!SnnmSE @i8( CĤdu֩mѶk?@k\}|T>1*&__:xGWtΜ>xꏕ7ۘzAk(>:7[dQon{r^u7[6_-iqGgj"m{3b&MY=Y}4r}Bf"jz8WvprT&woYۜs~j92}4~ec^;miX {׺y4NeߠyCF3}Qyٗ*EvR8ZE6ɕO >{X}v&Wxoك6ZɰvKJwKW(hijOv{|i-JPu{OV:ыծrrc{vxOS]dsoV:YcZvy}sԀÊQ-$eoUb޴sVo~ї.&D8yY~өǫ4<+J<%|%WXTm@Xq;?_";3e`2o eFQޯwe:pl1EMHH{MXtaĦ@|W:P(0t'k2LǸèߎe{N?wX@S%w3 O@V`ƍjphWf L_c+}%H]* TG gݛ7خ9ӣZ]_e4ͤ&ٕO4|EiӬS3_OYEUgK [_;p$Eu0ec;/bT/|rfx#ޤ4 ܣ5cR$Užg&YX~e 7t2bIMtaͿ{b]ݦ>:>蠁w-Y񣮊uE_Tlܿ^Ҹ9nSmaO(+zhf&lPpٞ_\?&!̃~$,wNUU ee8aحYϺvfɞKD==6jh;UwjoyVSa}kߝwPF9CM[myteL?a̓ɵ^ݴUBǘtv|bcƮo?6Ҽ)עRi|1'SiU ۉ]wS5t>+;}m_*g~n5thieTI^;mHeF5b:РŬ}l<ݬ7 {|RWudq޼U*Ӣ/[F>'w}b-MF21bkKMۯ: HB0XȳGFœv+S=xn@&Բ4~Nf)K̪eٛ}c;>K]k5ۦ8^~詑gC['Oɴ{w4ud{ٟkͳ |cndї˺M͉.οs;&4^Zc6aEM[yGtoΠ?ɱN2My\dƌm Z*ܸ&y$?Oڭˉ_{ʫ/7ŵn)F=W{S2ۇ\m;z,B2V>VKӬӰWX6ҍ,۹oSWMg;Yaz}jODsgzLwAЧOAe*ψ ݸӲ:z6tgNwpH1C ~_yѐo9fgM+G_JL+ߘr~LpTQ؞3=;r}fܯVO 1U2w7oВ G.Nj|boJ.~IA&wI{GusKJo<‚oQЭ{^ݠ{ѳsdS*xxGQ;uR~cK;oѳ1襹{,]{tAl/qіM5/s_xסC7 焔rK=&m' It^eU#Iṿ$9/M3|7ߺx~fɠߕַQoYg=~:vWɕԯ^sE ɅKN2͒sk^6>h^$g"jy~w*Unzcr^ldޝOƷXerxCa0#l?>pnN<2|sՌivrUfoYם~f)mn-6fƚ_'̞NheW{.m'Nr6yLm,恷mvx&ت&4^u^> fo9&TvQa,/8A72@u?V9=BmG?oˢٕ#W!>|07W[~3zU]~_s^뵪2_c _NZt9%m=QcIbfęsgXZalxº!j{{iƞz\9[c9zTa ?tY)=tQ^eMu6a굳:^QSkJq5_79.gANݘzp+{wWf\ś矕|O)8pĒp%'e55䓌\,wk*xrV-릇Ot1;k_֍"W][]&[Jy>2O6g$nZz!y'݈f7}|@%(҃ɢ7]?i70^jv+N{ Sşh}v*/ O.w3ij ͣ9}ieH&%G~t|^Z 1ϰY=w8uZ~UE- ie:&я{y)-l_ύqݍ0H:|n{XWOnpV˳e _xl?2Y vM,7鮵l d'5 C3.,hC̣ φR%.̿a~5_bGQώk>>=bMEnxxYƪ7wnY8rx5t_hrnKD ^ʕy:xW͛+L}к|کMTYN&[r'g_0ݤuG G;ѭ˧[͚+SnP#5쏳Cvs˩5~1R,˶Au貋G}zs1!]۪!>][0Ysx/6~ir?g+Qlݾ zk^'˟ט]ˏ9dpuc=N+Vk:ڰ|xϑc~_mh?A7E{ڍ>NzwE\GU}-vՑyN/S{6t=mߣtyɖCU1)Brϝ?vcULxs]}5z/zթя[.jgF?[Y{܍Ѕ9s؎X~uheʭ^JhrNY:{7Dw9G#N?=W~ݶ+[vukk/o]ԏL.X3׷X;SLFM8[,s?-ӫr߶skuݤL>t[tJhA5s06Xhs߼:Y OJ`&7:v~سM`dU&}F^}Kɐm uGu|fO_=DmZuIuOk>}(og(z];Diܺ:~=~'G2<&w|4A%ɮpWe~ӟ&XXLjyτ3f;Fc2W}invFҤ"m5;q!菼xh|s=4eن:iN@ Ö}7G\ +Nd̴4]O<{Z;F^ 9Z+y*|ȭ•/z+dushuƟUMJt zkZwCXQ?]]5ivtVcЀGĕSrsc>1w3 49upu6Kjw|ϨAuS~匦bnUKKF,ok JYl%uke6[NJ: 7s[7|Is甁g>rg(_.hScoʈSiZ1;+Ms3/ޜQarcfڐٚY_8ܻc6gSKo5~J(@#K~_i㞆>ƪ/rڹ 3/¼hͣ"&$d/1w'^BLh]Z>$ -|cܸbM`]̜]ژ? 0iOyuܦ[2=Ov3mqFqgIWߥDžBёUU Ӧ-,kwyhزu7|΁I)R0;+uߴray>KmxHzzEy WNOn:ƑjԚ6[ڇZ81nGDE^6=W.:㉎~^i^ 2Y_x_8 ?~Jfx} K_@u#F=l1>_0p)U2U \R)"̽}Lr:1JB%c:B?"SR\ Ē`{1/AquJ*?0] ?4`i~7Vd煹!=+|#cZ^WtLdVBƖ{J^f6B/Q7c`GQfAm`Э '=4٬oxfSFR" =Q#|&pbd#,I\*!n0ʊ,2SЋ= HlO?养00Г޸Jp%5(&zBɻK֛q fil0,.GY0\Q2L!T0da8?kNj|'u<Ȃ Ƈ6 @}j?PPi p ` qC@٣mr-Gr#s Fғvɰ%N@%&K%w¹oA], -)qSAb']ό8-y쮔A*lѝ˺b f~@pT],*phqJIҕ"̃؃7'CbR;b'B ڟsO嵔8HXeJpBV ڃ"ӽk4Rc"BgwCX{0k6 E g/ Z/2U@h`cgn(TA,ŝQ;,5 XMД 5;7 `q |M G8jF0O9N@@bw)$_JT;ri[70s QgU$ *!NОxp-ctJtJNH1nlbL Д&zI҂)\v8{ʜ &^1Ih6M%  aX tZ YpJ)w=V9N*3U1&P@&zt 8hh qKInaP3&hpAs  b1ᆒ.+B}쌵R ˈHujrD:. 4EK/i%"Xfh4R }`j ( ;răZX?$Fj^p!@s` 0edw@ц,@0 @F($op<).04osAnTB{0$m{k`W [g΀{.= O/DAl?Vs#`ZXEš`1))@{b*[ Tks@Wt+X0F \W5phpl/VZ9=R:"(31({Bd8Hw"nm0ʀ7d-$H?Y.lo ^-`}0@`mߖN)(BѶlSL B c 8цHԱ;4X U=Ia=/I{:44"*sW̲W/Y y9$गlP+!8QRKH+V&iF,A vֱ[M3$ep IcU$K?Ƣ dfbXh6X")q S{qm3|ao'b=$^QKN ]Q!>Dwa홻dz_.]Iu}DP X >6aƱLUjr. z{B.{1R [<\%r鷢BaN\\-̞vK0[|[rd>&˖sn-ޑQh+{sfLYXX+I0GR,=ISaCXٰ@dXXǤ* WCOw$Wػ\HmeWqZ7rHg9x} fݑ0¼7`,ͮIßwqc@B4Q IYP(@{|*<ctvk9?aeh1 ECJqa GTQv=BNEң%i>pA؇gPcрXdp90fJnLjOX =3"P.#@(=YiK=h"2E3$k){[jqDgZK;'tL:1bQmk8lKѸHzF39\|0@`B6`Wb` 1o9_8,fW:7wwń`e$uVf$L HF/ABȠZ#MP ABt6x Ebt=x>OM1 h`5Gi 5&[@ޅ{y-ڋLaP$L悤)0=I J}6spva,QtD`陥X$8@ȊǺSYHPnZ }/~/| e& 啖菷39.}R8bw8@gv'3ab85v/4sQ5*1v}ʒvR)f")@$ir=eh.l% ](!AFX@A5U"./;Rnz%eo ̒.0_XΖBvj heN{.ve<^i)dž)64B0{J ! I}pm%QNEPvPvWn /1.%:+Gv%Rr,5rc ~03$I{mXSފeethBۓ?7A4LhU-:ͯ ev`UV fzBUd`|;R^W.K -aAx@$G1v簬v8Y0)E-Qhd<Шox a0**& l^lO /9T\h7y P IT c 9&YeOJ'!p2@a="9a.`d~w,[`qR.aI3DQI i"h˩#r0xwڂALa_@4qx.gEt,/w, Z ,`VTaqf'++Ϛ.8Ő4"a!7&.B+߾=@KcѝYm",ATӳ:E,LrD;M P?!sj v1uwƌrNf4$K7\h+ݐF[ ɱHr[120a0^C ` Sc2$eF[e&Il9诽@E'I:2[)Ra>N޷0X?N9T *+cwfJ,QBZ#k_s#MNb-ƧF$ŁBȂ)m V=#-a71 n(3lȐ "aLykYHLp>#huJT\:>&}ف>0=0ŜNν9'Ɲ #kDa읩wޗj/507R['&!4L/@WtbT d'W"r-> 芣APHQv"Fɕn7NH!›ǐO%.JCB F -ڋBZdxۑP-C*l[5k`]>HNHnx; n 2"`'E)6U8"9P4["8&e?S=|w'$l` xj\2K"ťl3{ld9M py!~h --#`s, y;VC|KT`q7w7}a?lHU,^,^GUAltٻ7 v<B 8/x[.|˅ $aI f$=˄HUYc JA`rR(Xn%W Η m1h{¡ G톖!%aazsY疞-C33ZI˳e%σcQ=znڞ&A_#ԣvؘl;԰<cLG^zHx4)F(Lř CBRKkiKZA` vmACUvinx :4ۉN/dU~IJޣc7At)+pʹ!@R!w F #pBZ|AXIGP`%%wWΕ(D[bR|T\-A%{+UJi9Pq7X >#w0e&wcp졦;b uO9\q+.seS[u_]zݥu>Md>z{ FwitFwitĿdsRCC¦vgNJ/| T!Ul@"qe0޸Rs99HD_J'(W*8z`:$Nm.$o"da,$Kxm6V)Z.!'?9? s:wRËjٍci Mc&!seQhkQ%iM VŇkXv;(f#:kE,O*P္9o,H#=\T6aq4ŜD5eʛ{RmuK2f=J$!IĞbC$r"B!O ew1zX(wƉʠ5|#6HtN ݦFNFy#+ R7FǸSJC 30͖ΖQr-%}x]!rʀUVG"Xrq"8~X=ک)<)ly4rAb%HQD=%x㔊&E\ 5&У# `Pm(CYC!uș{єEi(pB~>Ò=/L)VG;#.DhxĠU >-=HoMKϧr{¿`b]}\r]u.\r]u.\r]u.\r]u../oar-2.5.2/third_party/debian/libsort-naturally-perl_1.02-1.dsc0000644000175000017500000000161111757171206022253 0ustar plbplbFormat: 1.0 Source: libsort-naturally-perl Binary: libsort-naturally-perl Architecture: all Version: 1.02-1 Maintainer: Pierre Neyron Homepage: http://search.cpan.org/dist/Sort-Naturally/ Standards-Version: 3.8.0 Build-Depends: debhelper (>= 7) Build-Depends-Indep: perl (>= 5.6.10-12) Checksums-Sha1: da7b21e222c8c0e8f9a8cf5587410cc57a836f31 10357 libsort-naturally-perl_1.02.orig.tar.gz d1fe029a6b54bbe1b00017a44b81a6e38fc21cad 1283 libsort-naturally-perl_1.02-1.diff.gz Checksums-Sha256: b8320a18e32d0a15541d95ffae3ba18e64bfca2478b8c74b80947ad2aa6874b1 10357 libsort-naturally-perl_1.02.orig.tar.gz 2b10d81440d976979534d7d1a7b842116942508e35b97b172ac169df491835c3 1283 libsort-naturally-perl_1.02-1.diff.gz Files: a173f3f8f519ebae6e5e578e843f6e1c 10357 libsort-naturally-perl_1.02.orig.tar.gz 977e84e247cb73ed1cd94fa2b3914746 1283 libsort-naturally-perl_1.02-1.diff.gz ./oar-2.5.2/third_party/debian/libsort-naturally-perl_1.02-1.diff.gz0000644000175000017500000000240311757171206023031 0ustar plbplbkHVoH|+ 4=2EUN:]O=Wػ> ~3k'M#8J8oD]wq)館(,&NK4=vF+xچcqQĤ4.Cc,w3:%Ir}xݛj3޿Mzz{ڛ[=NjB<7p] e +/">UaS GϟC| l{}A09s|'r&ݯWOl bIUεtjQJV(J&RZ7߯rϝs-)/Ito.F&{ߘ'MHojs#>t;ۄȜ)ȼi7H\ lyhN>VB _#V3[i( PA T޴OU؁{ dؠrR-@(@  ޥ U|XH4Y; g"`1b;IpMW4)bB#rG)Ia$ JtpFmXR6q]"F,{͔DlPCz*ngg] Wmϼ5JmoybfQ?R5'~9u&{ݝQ4=;aU>Nھ񁧆LԄ>+0_?D耙1zqtY}YDEꄡ/Iu>~WَmGgt-7y*І66;sr)fEu^ZbݙuJO轤%ʛOmƿ7h|1ziDcÓa:>׆@tN{)oF@p^Y*ddh(T4, Ig.7-B0UF{w7KSvsrGay?y͑U@1Gr$2}0иXsz҃❩fI5屁d{~b=vz~)>y,"yy[,='Gbt,Z)@a[yks+Z= ; ./oar-2.5.2/third_party/debian/faketime_0.8.orig.tar.gz0000644000175000017500000005130411757171206020577 0ustar plbplb]Hlibfaketime-0.8.tar[ms8ů@U&vl˱nm##%B'%H;t7@z]^Ukg`ѯO7Zi2[DLw_~oyys~>^yw~qy|S2*~]zq1躿z'y0?rDC?_MI޽~|\kNJ)lgʌ>surU3|o~o c$_Qam^eqT@GyW1Ԭ7яi47=uUnKZsXOSlspX1:)K2s,qb"U 3lJ ]fMfy6YX([&R'%RGi ca,5d"/kON&L8,n:zzWZ@Lq'v<Õ];+Ȃ{Lf(Տ [{wdbjYEE o=SodM| h8؂A!huea]DbU5Y6§Y>ӶXO6`ƧN7E,~]D*Wya!5+UeE}`t{E7a.lY[}dIfK3Jϣ ql<|]LB}#iT='zDfa 8v&զ8sfl/iTUU"#q=bɦ؟`/Z' "_:[,s)X`K_+F?WiԖ5usᒈd:3¯'1"O[thZ 9[4WK3/u8YJfY$5'+B˜%Rx"a]m"q2iM夕ʪ;]6J&>7EXdI8DEFCIv#'u' 2_-E_q+yCV+CnW9f!W/g2J#+&[튗;W`SX`5v7dALU J UC>lY \:-O8/IlKfN/ ?3 3ddajpsxKyPà3!VcsK-$D0i'; Q%g;S,ppE ŶV,e-oɧk%(b1|q bR+`YG^r†v̫76 i*I(`9%}͹j.lp!:BsJ'-G?a*9ÈtMD,+@ ntO,B.9nqIyvG#KļLѱ#ysDuC$Y$qEL|ƁD6 <>9go"AQ;v6[G1b<"eSSk,MG V Tixt¼V:9cnQ֛`#`(QPt [g"5Rxf-Idw S@2tAw"hLyp} /8Eq HNb 2 ܤ" wr%Xu+Zd(Ib>IeLV6aGuo*ʅ*lO.${ckL QA _oH A4)9=CS~SA!qMxE 9-g]Ul-KS2΍@h92?ŐzChM9C`A4Wh6ɼ+9adCC0BpLTi.C(YC*`O1r 피f}"Cq+JGfd؅2VV!dS0-:6֬j-Iա9Rg̾Xmȡ|" G2\4SS GLٔD68+ݣ2i39 @*Ýh=`0 1b$r k,Qt֔R?Ԉq>- g$v&4,Ui_a%bɪ1mP 8r-b:yEr NTG NLcН/j)Ĉ=SboLBt /ǟy^' vŨ6 Ԝ*"JC3AE($ԟ0ydd!012mAтBpP/H)JdbOYW.tI1-G`}8/>ΚvP/b ҨISR7/y݉LAxswJ]Z.|1 ukKK k#Ai?P!={&r- ǹ\R[+8*!$J@mkxW ug%;̈qQAYm`[C/T^ ﰃv}9[>;N8^GdwsU/: )LJ)j7pX KYB-T[mZ!R Pwj;+Rm\!|0˨ XxIR/v+{e.8, z Sm¶ImWm03+M V\(R}vP>\J _Lك@gdTX$r8-$%OU% HAO92 }t9 Oo)|{mRL>+)oj:*BMR`Oac'jb/Q9,Et.&ښK`xaqp᧌e"w}H .`=Sؕxw>I_Nu%@"EP2:u$ȑ p,JK&́we+́3¡)Q5;|_"v^>o O(\ iI5a&p!Һ[4AEa _ :uEt纴 [.BWxwϱIER,r]Y DdfdYr%Fn)a+uPwbpws$*".<% k j iRӅVخ!_ouUۓl`$Ե9@wc>N= ILַPYK D jSmVo_pwԭѧrϼNTVƞuT` Yld;n%\1A7n"47)ί\/S'['ʰ|~~yMU 75rQ$!K*+`N@z^sOa=w?ys3y P][ ۷{(seRR ]&NiIe*ib^-Gmp(mB 3JZ./ $vfVd&&mtjm#؁4Ss'&?Luzgܬ#zS'mܺ ŝlY*r .:pfS^T,SwWUBODRFF3oOhrqԒf:u6u!J?\K&V9t]<3iw[<2*`_Zզ<>1ϔ򐕶+[W_2zRJ02U0&xMh< G.ѦWW" "03J-Zj=ݴNJl08AW~(BLR۟m;F݀)"\vE(@&e/ -߬u`YMnᔄ(ɌH+'cF!AHਬĔ3G[H zr8X&}s]x꾯{_!?S?T#"u~&0t0iw<|7wt~썧D/Nz}wi<7| o:?`B<_p0+PN>#_멃W17lI3÷[dqO8~OѤO!@7QNH66w{.G26l2izB^N1\Vfg Y)<xb9igcyIZ!&g=VwN{cYˀ" Qpr_:x}+d<ǕtW3!qi57H 5tKKև-iU>Rֹjj$p?zE@o $_ӡVc3h}M[{p[ljQ=ěפ^ тFob%znWN|Df^?֮6d;!, cq&p e$9qZc[ns|~uK ̅5]UU%*3l1 (]:' b H󉘬(%p(:a4$uU7ŠA+C# (;Pc$|N yerSfצqd>;sV~ 3`7`H-M6jx@GPh/X#ufĦ;}=_lN)4`lcFnC$&qᔗl*rzZċG߂$lg(}0юb')n|ѭSZY?jY%tPޒ,p*?/}x'_>Fʳq-3M=a5JI }^SgSP{)<T_eX2 ۃ9PS$tot 5yE8!@} .4>%Ra6Jk̀m65LLe8s !#^8q/{UespΛ탳ý: 5sGdDeD_Z¸h&gwb*22ifygEqMZ[&?N{ZIko<voyl8|dºeGw47Hv,szΞrVW4 9;tr@2p~:>;*Cj$9Ə ㎨^]q 46X2nxQ:/lwے7:wO^$i(uCku#ㅼ9|{Sghf:::g?-}cݮVStq>VQX/n@d2PZ|+5eV!MUW"Kү ݎyE8oEh-S{ Mw⻯7|Xُ{~7aI`|ޛ#X5 4OIgB\|pf%< 6AI%`a aNsrD%B0﯁8s L.ǾSm((EIw^OoU!Pa>:klצÄ:+ *lֶ }K pzT;U:_d dNX@F!^qQbE`URe絼t@3$Jۗa1hlyVMI6ҧA6.ѣ*Tm@Dq:bzpxi @Y/vţI⁈={{/8:!<"7,0'Vu+DA&'xG=r q3g /7[ò7e_Tی7}̓1y~;pTvLߖ@Xy9NbN3O[*b^YoO ,\$&PPJ35Z@1 j2gj$T|3?)<ѫ+%iĿܵS¼9갞70(KK3VnoDM&vN0_) wF߿4d,-< qId2X7u8dqj8adݛbAݟͣt.?<ߗOQo.xĞ®{PF,4đ72r1y(QDtjejG+e5.Z pqfAeCWStaMa3M2qxXSڝ*Ë )?SR"tiB޼?iu̇V[8b'}+^ K^+g0#e=l8;5 g+S!?6{+?lj3bɷjqTe)s*L~m =M,F>ϒZPMX6F-Tt@&2t 6C16{u|^}f-avQ޽$FL;Rb rb'V"I 0}Q5!Bszԛ0-"(}B8햮hL>M[sk۵ϗdrJJ Tݡؐ9KG|2즈ߩ[ݲc n'G-"Xh_j8뱖y9P`;;;apƅG*ɴT}-ܝ *pT=4,lK%dy@t,7 $KK A,8/Kd mZ%g$vNŠjA*ܸ%ǀ&Ѵ<X4iii.oex/['bЦeL./wKJ:H+Xnk~aQ,QR|tNfQ.r2D0(kMaU1&=jJRVvqFUIŞc< :"dgQ (qU9<^JfR)lP. 0loHkۅxk0-z  _+֯BUU3|jѭjM`KwBBjl\;VFO~X\j)l}~ZA"%$'Þ!%,/fE:<ӊ(׊.9+_j:-D2RÔ9c]Y:.ʵAEJ,1f4x*h lBDiMo< f>Y/ UXFM Wf^]}]@gb 5|ς][_e<{\MBU)I@uݨn І='䙩M9:Ί]+nig$~ktS:?Ke븘7TԮ ljݿmhcXƷ希UaaP-_T`w[&~ӻmm sų@sĨ$`=KR^gs]vn,э x4wUݓPGy>ǥpwY!U뱋cR6Z{A[xߗ4pu׬;G@-;anAjӞUa*"׼hXpN%ƾZw omT4/`(Q s%eZR;t/Q9IHJԾD<0J5)iʆgsۺ @ q>ml4f ?{,vqn ܒMu6zD'N2v 2 ,xw/*m^һT^o;iҏ/$Džʶ>wld `i+l~:x|˽ˬ fo*_tI_DÑ!p|1Al*%L4puhV;MtiCAi96u&s4>qh ؆SѬtyM g0xG$ey:U\SOfLusHa6BA4nHc:ݐ GCۡ4&l0G:D\2 B,rX1l5}٦T(b[zF?t<ݭںj}~6QXBZT}h-y<.UZ*zݓӽS 걧 M Y01Ӏ$ܞo<D?3Q(Mġ:E婴A_yRf>y0@I`S.%NF EY">`SѿTeJp{sW*\E5y#0)dSrE6=.7T6Gd-6Q)E&^z [8VOt|HCyOv'Lpv VXRE1(L] ^Lw؋;+O+KOG5Hß4uAQwkc i"[pWm*[v}0s* WۡT,vQ_&rKK8Ycn9``U-7E]_sI9vQL?r3J@fn:\Ɉ: *T,+K8mixS@<рq&ct.5"9R1;xtGP Ec1w&@lKi%)^>5&K wkL5T '9ºM1k޼*tc&%.&jwh/ BX5K:ZP[I*x=ݬ*T$x"kYv['Do#vS J"ioxA6& D&^a[Pզ ӦLHu?ǻacL3K㶎"!b)4xIUF!4YKxTxwv>ƮE~/ld!Z2qPod)씜[T>CƠ7='+mֵD+,Az"o*퓜<< 93}SG  f3BL @XqZuX[-X5J͖KǵN 9ܩpFc,uG -l7 3~pg8'j.~G>TX?Kd-1U!9:y"xTSC5)kG!~g}Үeo4W4WS?6_a46p㑵:!z1"&qi|iax:fy~1`ٿ!H{;oO滊  <r8 syBA$83m y405OnYAoǛt;~f$zJ T.?TZ<ɶ:ȳLǠ #lӥqBZhVH;\!O5ovWj㷬o+m~:.}/ j14 ,;r"g 6M>oVc^^[^ >*69oݷt5ZOۇ'{o ^t9Oȳۍ76|x7]w@gׂf?ޮt;!{{%6ї0B,TeN3IAOz@Pױ[mm !zR#> usW=t0Lbm|o mZeQOƠ= 1oh^Q6]D.&Kak|&)9nNjBF! `{%w|eqW)N'%&q *ەqxUfU#S/C:ņ( K}dMۋLzh"11>fXr(V '[;e TÌ/ycc w䗧_k~^V:ѳt5"Oצ@؜)׏֨:?,D րEPJ0Fǐ|:6R9SE\A$ |ý>\GRj&.WmlqJ:L4r Aɱ%8Tc5B{LY`cA`Gj:*^QEZ'UE0̼݁G7ڠ/EJO+3a0ͿS? \1&uBaU5en(MjzC0f~?Xgɗ$#KH]l =_|BHf<erOoO %䨻 SݹwL-bx!Љ7hge6+"<]p)+dQ^m\9ѸK2K85j 1Gƙ: :) @C u 7jMY|\7=*-NZᬉ%ʓ%эOܮ9"R=><Rx,40NBdKUlU:FMn="p-_SOb&B!wzQLc|*Z줥 ,$5K˙qB'Ղ۳oC7“1/B[ԟi/5A>)3AWCU#M֨^4eXJhK訙./;Ľq QE2@~|o)}v*'4{xtAO>Z |`gyVxX.r/ը[]+ǣWW e_td3;%O]8H Q:zd7{'o~oNTF",yu7?> 0M~M9lC<:F="L"ŭ;{zlxώNOR6hu^S>я¸h?B#O@3;&Zt!J109H >">^i[g)8`n(4 vhDNq%Yƻ4um3.f7ߴן=⇣5^G` V' v6ۺSc[8tX*Jr f3h vSKhj֤!g$ʂgyzSQ& 4aw3_x 諮<@)/{K}#7y,mm%"IkJ˂8GG/T NySs`I@1.m_i)1ˣACh<<_FJ%sp;}lI.`){q$ )cA@V `&&bk9i,P نAyU52d'N^A4eFm8O%`1U Ylΰ/Қ?W'3QgQ hRJYPBSk"tmh@_l0 fpVM&mÏK.ma7{[%rƟ<*iYd.M%S{2)4rQI*!k`8Ђp^ ՞cp:2CU( NZi(,EzpL#N΍ЀY=qu3`GZ{k6V;=C7N63)Z!l2kH=.. ^ŪW1\gpZ<`.9N Oj]'b kbgauQrQvRdH,`M"VL_W󴴉2MP] 6xLn+3 fDY4dQBG '322>Y٢;n' vĩtӢ23GSns'sˆd54"Yl9!(1aP Tg!aN3SD= cp{кoZ ^,!-ߺe D.`2 :"taHxXH)tiÐD52L)Vz ܜ*`SũyӜĞpUi#ho1GǕTAHTbJ2mR%`}ٳ1זqfcx x6gfH^om]ˆHQqg-ur2[R0:ثR&k-xw஝om4/|?]Z4d-{4NCoPkZcP' doȶ:/)gd7>cϜaqlg UUKU[DlLU >qc|g3lO`LYLP5ѱIȭe5bH 2H.x^=1}ŞɄ36tymгd0*£p&."&bP59 5 жzb8FFsc!Pi1YheZ*&kDh8gR#jxESj[Ê/,2kՖZޛ V^Ĩq(B , 74#8p }m!pTVt8،U(c?54F} 7d7d8v[Q" ;_41t :E!I#Uټ*sBg^M8?xcR _LF\`v@]n'ɷ\-M2"oX1-yBU+s(~h\~Y k_`?0qshzf=`n rH r(`Nq".MRIGy)G,U ^λ Qq!K/_B3^MHbЭs=x)o`mSdN[z%ҧƸw$_َuѧȌ`RH6UQ?5IY,]õC ea&Xtįeu?Қpu=CpbIcᲙ %^")cMe~b{ 1|֨qv|{PaɑUR&J"?5|H08$!d8*肱LʱrzFB (0\)c;hm8Ӭkxc2"v`ed h1bZxt.A3ljPT1Y]|ף8/8}Em xT9v̓yB&y߃ϵ"l;Z )% ]q٠WatDH`)@Zv<(UjšQ-=Qy"7\xއ%),f g0f %~s0*rk:(sĒU".yqGڭ@8th̯te\o*ڠm`s"64AZPs*EeG>(i"BN1e7!c na($(BM)i)"4vm/vڨBD@E ی}vu 1xSH rT =2SʦaD|dstBfH?qZ&%gvb #6A;oJTӖ 4">4$OuY0)PN&vQG; h!]uɰ6饤@CŞ KMX_0sad*Q}+^=<XPT0Q8;SE )9[\veHD$Jy 4ʡ4,&ԴGWgKВ0o}O{E,7n.@|ܠELȋ3 q_wlrn"Y+ $qaY]zwqꠦ(t2Y?)yN͏P6O+כ]P ziHq(2<I&OJ~ڭ:Q!8]8J ǦRm6eܒuBXTY,kCfk^ݠQcb¥kZ=)N/^< ):+16™7Tj57zcaA %mS?)H[Rg#)[#6*M 1 Drh"eoQH!ǔ7M`ڃ:|I(qLq:1 ʠ| 1L0 &gױ|fD9!ZfK B7+4ҩa̷%|S]kvR^g BJ2|2ծv J!+*|T4Oi@`F|:m,a5]8I'"/$?#\H1n fG1eLy*6~yn^ݤ砾 -y4F"x)8}q89yP0m[ŵ^Qh>F̌l0YUVTs h \u@nzS:lf'?S`X8% JF14,d\zT^qH^f W|H|h\YL0kw/X^7wP4N$lBev^D294L=uDy ]8 o\=5X@IH0C=J"FJ,bT3Kf0 SwJe\ƻ??????????????l@./oar-2.5.2/third_party/debian/faketime_0.8-1_i386.deb0000644000175000017500000004714611757171206020105 0ustar plbplb! debian-binary 1222444056 0 0 100644 4 ` 2.0 control.tar.gz 1222444056 0 0 100644 937 ` o61$%J+v+j읢I4H:$'!^?<~wV8)1Zr1{v>HGYeo?(·Kq2Q62#4s a29y|>mEg_!8}2Q>eqR ?yciuN& ;V&J!nE.`#v #?57_ѡm! I> 뮃⓹C1Fir ["XqTALxZ^y }NL8L~=l!lW~Ombok<􋭳_:ʙQֺ0z2` v8[ фǦk hj'݁__<'2IeJN| m@[Ad>z";?yv[Fj 42@BLLɇh2< c4􏆞IteݝOGxƑbƓGQoe$S&vH+oͰU+1S`cqRY{܇u&=O4 )TS3|y~XiƾeP*cRRZyUdP@37thZ<?޽znΪ\s ,J3&s-j|YBg%UQE\R STRVUs쿨\BmpA1SP(R0~Z̦ I.,2%r(ʤaٲдS ƃ=lEs P3H!3%Fs%_l)KJY\RK1OMUfָZU)猗Jg,A4RfX?H$D"H$D"H$|Ԩ ( data.tar.gz 1222444056 0 0 100644 18940 ` }XSv(қHィ ETR@Ih * Fl(b ""X X;5(ҕfg's==?g2{f֬i~qMM\1zohh`fndA9ssb8*- 8 L^zqT L MGJwohfh>aP Giu1k77145vs"=73175156E MMLo05.Ƽ|gES_Xl6(ճ.Eif&Wn=eLWU q>Ϊ}S6 t ~ƷogVys8a#w~sU'll,+ǽꟲD$6>o܉rc+׷^%4#_lV[(4.lm[jyuwi֦OU<ϙ8koPђK2Ew>)×,~PIc-w]J#Pvִ#fNםISYyG^gS~ )7ѤO]W 'MMvNXL&f}zY9ݾU++zIq+1pU8'Xis~|y36x;x V>ۻkHg/?US-sn*y/)WYzܵG-ϫ}tgEVٷsDytr4YiB<#Ϗ}vPã'Ϝ L_c];WQ} |lxcʍ#=7?!7};>oiՃE>ΝOJL}W<_}tөM%Nɯn7nVXf*Z*t*z[|_Mu]ʆC ެa~ypV E9ވ W0E6O;t'Qhڨuf:;n^~hǸe_Kmt+*Q|3c f7nYr!d1OgI.F3O RmṴ0er_q+#4pȦuʸj ӂ5geIޔmq~F>wqHIR~2{N6|jE[ڂ'1V)M(X^ËO͝(SwV}DgD<*hyt=IEoz67h^O[wP3=r١"9ˇ7ն^qޫ܋/bb{VT_יB-FsdK<-C \|;1n yJNI:bcA?_`Ź}20;|ud–tݙY^C1ѥ"W^(1Ivy+hΒİV3ߛW.;ڭ*Zeqbǣ?n::+ <:D'*K2+ YmcMT>n;NrZ`?-7 ,S{(4RkJ?^.,@VV+\fQp~R,| K{N>!Ikr[Qzҫ̐oh^6X͗'c/̿k$1 1d]wdCJKgWHZa׺>&~tKZD yI-|V]GIK*n_H|)^`*F ,M󫤾X'gm"eިм, ZrsˢNrX?։%z߈t] y{ܚb I:xd2Jqӏf ;J؊;u*N@;if'tEKfyԾ2򭕷|мqfjz}KmwۻJ Va7]?zCoFt6k*4K-wʙ +ݣ'PlT=FrgCWǥg>]y _x[盻&uwMh|[7pySK[rlUW 䤅&&}]5l }/okׄB[7VnUp~mծ9?\N=B'oO^tK,9eKJiXڲq{? ݙVdZoHnwvɃz8Ӥ)/UuQ)z }ITۤ{tSo3 Efzh;9ƈ8A^cKpʭmQϸ_-4FbG<v-Ge+SM]~6sj oI"n|7uu\w(,{iiBbóזBfɏx:YeѾF'68}obWCN]ϖvNYH_&C+ya #[38%㔦mf%͋XW`cW~Wu_ qen'6Xk_qk:d?([hWu:Ӷo)/zk53w=[v @oӕ{w]c=*js+~zJ0zJ3A;QZdGnI>[W3ޭaٳm޴w:{}-$_ܾ`f+1|9KT~(\?.'{gLIh+72pŁT~m%ܴ 'H +HO  O$u='59uީ'9珶s}CX~۸G1rҥ}GMTܛpv$ vodajDZf2L\xK)VB SUy$dwMFϾ*َhSrޮoqé|.aѴ< ~'on>S 7*͙X)WǼo_h&Ok -x ?Vt!b~_@(ikSMpMYVL3#N>c6fWT5w'q;5(p2<@ [{T'J|Vzb聢ϓN5sD.,Yصw\׆^maiOqzi-u_a~KeE *Րu.)qG?*w&}=:w??j sL%0f_ %[_Za@e^;;69!"=KV.gron+HGQ)&vN${R'މ̈7Tݘ6;,cZ⡒'G&j)`UK<ܰK(ޣ8kJA̴|ޜNq_Ew"F3b-1ω>97 '}EPp=mϼ.9`sӧ~.GO !o'6vp_Skҷ{TMj]Ly%->3KpOq$U32dD{fK:ϡh^cWjaq=W̜Cdeևj}g %"Q'_k`l`f/677,m)e\{Rn] };n_Ӹfʱ)3FYv5;;̏UT4OZuDWa5}KiuJvy[Lk ;zDrwxCvNXI'(_Un/`]/+m]vix~oIIYErՌ+{jy]7ƒh)_[?;g># 4AOV _= {~O hʛtYEnYj!oW^ݴ o$_zUiF~gw0;2[,3hv~\k-~şv/ydE,/4InaI-fl(&,/1UI otn Oa4_NwcΚ͔Fn y|i~xqۂ499u-ٟ-R3PqPcX&dѳms/g.}6@5dFX("?{}Cr^WBR>P3$BZ mfYbB(<ު s|ڛ33hk 7KZ]N9}ŽDzJ$Iy=Uп~zer0+d%RNN+>c?CuOpv w 젤qQvՏX~Ľl?XS|s1}Gk7?t@x3U,\,XZܭq}FZ.ܹ}nDhO~koy5@'eJ7Ւ׎;|?t~n?YwsGٗIi+}c|%;eRn+-,;aAc2FE)kfU93H~~S?-ӱVg; f-mZ/K'G͚5;ͪ3E)y'MÖ+cg/g-J+ŷU5V28+>rRKՊW.[kuY.n A~ wIi zgI㻶Lyr׳QvxMGM\z1n}u wG]Omq-g$bH:zͱifvMU `Y`ϗ3[W5ʨ$-ڷ6DcQr}@qZf٧~싂]r'ۼ,kl}k'| +hmyo<Ow53ȏ ZT|Qj}՞ܭgBjD5ٚw޹ۅn[1a^hD֝c/ƄU͜/z'Xe  {M-yG ٿ#R'.jV N\@xPRQp+iL\*8gt]UKݵK%fG}sdPkOiS%'N5m y돷<߇t5I/ԉO܈zebWbPztȎyZrG*JV{޺;47׀Z||^ z_{ ZMJ|B &ߜ L)8 󟹙>߼)VF[5NWL+e[FY}09q缛I EJ{U-x#{JA<"cָ4. xcWlG:;٨-Y͢ZGI5s$ErwL'$Qafc3E R<LE՞$!&Ĩh2E&,26N#*> $Mkhk6 nX"|m6vK~ʠ-矑ӿ1U7\AfzΝ4UΕ=wf ZrWtlSJ-i \Ŧx%3:.Ǔ_U+{q%c.^yk|e;AzuH؞ Sȅ4>` ]m3UkW+nFKOsg^X5QĭTGՁ@ӈސ\{Iz?#gje$p*٥mh(+~JD}d4Cx)>5bc}Sz¿fwnڊ3/y\]_PjfwCňRa' He]qEb;[ =%o֫lv&*ഁtKb sJ7^9OG⧪פw/% ֦o2ظ{o+[޵jcZ_i\7ċ ֻKmR3Z]Z޾~dZvkm7?6]h0sȤB[3$q{ڃ\޾uҍg3E6_M8j<61hJ۳q)G7ђ6֎ۛ+H#_yyDB:x׼-w[=~rx\. )i gӃ2OY/0}B}cdV)hKQmg%U[5ie_uitOK=O/s=a ]kB+-ؽr;alU~If4ser63O\&1 Sx&W1msVNÓeͧV/_`WUDJ dsΎ .c:g?U\IW}^[O蚢M/^+'lX5s M0fE">D_)@葜2d:xޫ%t w5 6跮 rs]ֽQݻ:ߕ{Ef{aиj[j7oݞS-NnT,-5rBߌEIdUY+2Gx<_Kq7 c'PH\ix˜ VL 873鸃%i69D?ueEFHWX{Z͙]{~09|j3?z}g;Jޟ!R6l5־,^ofW˽^³|㬸"͞eRވ9ɿ+S;so+J R5-!1Z]{ έ6"OyzwUа>gwmq6fgSOq[cZ\Uϻ(Hjms'g?@y}'4Q9G{1ea1M9u+sugy/`}h&ޙM-s8MЍ{uTc M8kuYkv+n"b<Ho7>}CYfRQqUnĥW[>%tYDNTl[rws|>^,+[V.joaR ͋߮^wI@4O6VI_orgFDw2N' XVf]/wKKEF?Sq3&-7\%vkݺ%~+?u~䆬lj>>M ST\p6]ڒD'!&c.X6,JF\'H+5z!PkZo=+ae@מsnY]yJRBu}}y+y/Of\寮,-c&| iӪVN#&l9lkgF` :9`_su9ۯ1~rJA_Ѫl=o»>J\X'tDoةӟ\s Kk>ު!Y[Vln؛3tJkD ->&2#ܰeQ6\]PF(b/Ff#?ӑ[xʢG%rl?q-stqa?%ÙXCoCp8 nK 0 &prd0$ yA$ z3r~澧 WV h5!,fS% :1e@a,* H2MA1W`6i )pH ?TDluLh /&fռy4p<.UrR4 ̼<8/1Td48v>M0Tp|KbW,KYŧ%)"#͕6&TwoNOkm^|xA*:o 윑(D4qYfj%֫fg|$%)q9fl pU1}u+` cvX\+g`r\0X6yX9/3݀rY, 5F з 4- X!G,m *:XmB,}¸ /y3-k}!J92(~`Bc^&Xz9u-jfb@C Wa+sq>(fh`ZMq yp3+@)`}]ߗwHO * EDIAp7‰$" M Qx\Ь |JS죂T<$:4\X5)|)27 pb塱 KDQR6O rxXp3'4Džb4qTR H4 )4T"I0p EŅT<̋ADGQ0V %P R-q51i?8̈́Es(06Pxg ‡{A*E t2s}̺33\Yc~/~$0~A,da) c()lƦ c04` 6`c61hƠ60-A0`sw ?k` 1/ xgLkg@V =10061Ѝ  7zq;e)ݔoAM,n)3/iKG4 O ]hVٸMݓ4,"Xt ցi[^(4<aMê?6@p!,iMCUhPvơim?`i!M{z~4 &0 MîQ4<4c-HV5pdnAO~@Sg2@Nh(t4Ke߇|Xn-& }my9.˽`-g,gcpéaG@2}Í}=}+a/-a[}rXx˲ Z^Z˶}F^NY()r4[Rؖb[.e_3zX_c[~mXo-r,MPHnc[m,rXxͲ{GGqpfd9Y+|7 1 q!e𒍃 >]Xx¡C#2fw 7L~;PgZ.N ۱#QVF XxBB* [0hUP=tqa.wQ&'T$!hF>Bghls ߌe?m?B#z7sё=Ԏ_uw^':gwѱmvw-GmcwSv{a* k!_ڐ * mɆn ll m匐 l/ d: "w^C4 zn]فu%T) `JR 6jTOɫW՘: mY zEb&5/7KKŮѰD8x#!‹2:. R*0(0eT0k4֘ 9 J)/ҍdTg4V];ښXv]0;#5Sn*|/5i)58 :BiXFB_aP寘张r?ͷA$8*YIk&A  i +` f8`jBZn`sCljc#ǚ!b+}}"Yo x04hmp_'~vgJV-JH捭dw8Jф6c!X71*ˋҰyPn m#i)G$O2Gda^tu;m9rAShgVtI9K+u5%CII*d[J )YÖԀIC$'2%%P[,I(8D3t+ϥ@ix{y9 Nͺy1Z=cjԢαxɓ0* ʠge3=%jI5_ Sz7xsPg|>}N>~d}?#saq-3ĭ~Xܚ_869ֳpkLlpZ.H+1L53\wxz j/{ =/DcR<Ώm cJX%a+٢9Fj3s,*[i$?JcS&9OE,H=(~>">n~0Ʋi4ɬ'NiA24M@5F P?z5m~~nANvԨz)8 1b9©E명!j^C22cohl!n/WA >oq z.2|'=Wß 0;gb>e>|-=3|6~ {v>{l!=b>4ï?łh_ɰ. ݋ rX0 ă\@ O@B'8Y&`> JrEX&c=*JЂCpz8=B0 K"'yhL0K?e|cDpz6=8=>)`j*h- GCAdƬBi,%1/6`paq1C'38^`xYn,&ˋ#2x0)qU70pˋ#l{8`XNj890+ \vq;CoNar"rX_ՆqCW>9D Crz`Pc=/ ␃acrЎz%fc='搫 K88Cb-CAh 9kw5f+n(o9aC\` #C0LcC哘\]+X1k(׏icXZ':\9uצqd0nXsV a?a'3 3$b妿 @L0٦`<9ȣ Q'/+  x pBA`'j@W3Ty@ 2~ mJb\n X4p 2E9sA^ N7TyoAVr~g+ {zi~w1ꂢ WV #xڢp#%IaA$}e0L Gqq9ksKn#;#9|?&rxѴ@߳8MA>dVnC y9i|H>>%|ZP/n&&n+8y_cM^18܂1v` z ɟd3y0̙<s&ۜɟ1H-MjkAU? 7, \8GrCe~P$\qC^' 䁽LeQM1Ĺ fq3à*z/\o rww8ᨲ齜T:hc>%'r'f:`s4)M{ؤnRT%a|2 Oef,:H/ㆼO@. u1!WF5 ǃym̓tqAr ycXd!a!߃$ R!Oo]ߘisAdsD|Pj'mB@9vAi })<\O@>w0!jzh6p7n!Tu aWweGc8:%+!IGp<9 O8`W/ʯP~r?a9C :+0\cj|Íy]fQ:As~foI ~W h9cBm3E aQeL W^ Ƹݧ4ȨFܩJ |v1n˰> vrƄvR[qid2373l<}Z:&sϪY9X&gn22FHEgk5CObBiX88,{P2P2P22!EOfY|d6UU8̟&FO %}$Y>A14?.l2Ϭ\ EomP2,V2ф6mULb3L2W7qYa {XR< ~M?g6rO}d*ȢK{I?IgI"/ْ~d'C-AgIb(@%9H>g2JW2%C)Ĥ;(ۢN(v(j#(R9=O!GN[38<$g|?Gg:d71دzlb)Vh?-(P?թ]/Ǡ/ŝPqi._G>&MJ$6 I10 G³Q|PW\gѬ;0.,/ 9%W.Ë0N.eedqb˅L~̇q}=.; uVp' |S Z  ă\@ O@B'4Y&1-'|Ę_q_;G0 R|渣Q9q p8a=p87w8a ;~q_/ ~qUqq18&38('v9@v9?;wk;wWQq# ;\5~q@ŸCq~!oӑ?0̀?F#\*PS ""*'| 'I)Bb }Y (H 8Ð(b% 4xITVaMPhTF ʺʈ.+O. ְo$a|()nT 인z|'w}- `0} y2D" 1 ' L d!ӬӇ>:_RWzCOɛ6/ Tp7 Χ&i<5q`Eǰl0%"vOL|505J /Vꫳ^G" lne5z;1B D` 0Ih9԰{W "('J Y}6l"Lqk}aw[sZsmGb7kh[kFpX<0.:ՌJ#İH7@|53ڑ`WT)z!pފ- vY!T̀(QSՊc{(s2/Q`Q8D= \A NBL0~=t| b`aehjebum#$$@ PD[4J##hn`Q$71k`D ; 'n(8Hip,уcHl=&|wSѽ:/?/>s5皋d聄<fet>1\MqTT gJ*Ee5ÿ5lDTTT8g6m;s~*Ca)誥ƁU]4D程@0gS++ċ=a_ᨁoD'Ga:ݔץ0#N7)v*U !Qź"XǙ}&ށLc,S 5rXY.~u7=9TE[Ӆ V9>CEY*EN39qTc">46 =p As#/SFkFkFkFkFk?v?i./oar-2.5.2/third_party/debian/faketime_0.8-1_i386.changes0000644000175000017500000000264111757171206020752 0ustar plbplbFormat: 1.8 Date: Tue, 26 Aug 2008 14:37:38 -0400 Source: faketime Binary: faketime Architecture: source i386 Version: 0.8-1 Distribution: unstable Urgency: low Maintainer: Daniel Kahn Gillmor Changed-By: Daniel Kahn Gillmor Description: faketime - report faked system time to programs Closes: 495630 Changes: faketime (0.8-1) unstable; urgency=low . * Initial release (Closes: #495630) Checksums-Sha1: 5fa6c2438fa39ca4978153f80ea0ca3683d9c897 904 faketime_0.8-1.dsc 7053ba5984a94d24cbe63bb7f285a9d1c5418e31 21188 faketime_0.8.orig.tar.gz f407570b0a4254c0b1346f8209b5bd0fcb3ca067 2146 faketime_0.8-1.diff.gz 7a8cc5ceb7365b159b9bb5d7971038ce7d8c2606 20070 faketime_0.8-1_i386.deb Checksums-Sha256: 6052e0e8e22afbb68643bf5c363d5885c1f0e9f98ff1b903b7a6f9689aad8da1 904 faketime_0.8-1.dsc e115120ab4d199f7fd2ecdb87ae2963523d6e8a5314c2e914573afd7d5c704c3 21188 faketime_0.8.orig.tar.gz d6e786c972d78d74df295816bbe5a65820b647123415d3284ca03c5a375a1be6 2146 faketime_0.8-1.diff.gz d3b7c60bd14544359753ac200482954c803e4a013df0053e909202078bc4ce5e 20070 faketime_0.8-1_i386.deb Files: 687ca0c4a995bde6bccb67a366e5c91e 904 utils extra faketime_0.8-1.dsc c83e12a80ca85486bc904905d325d01e 21188 utils extra faketime_0.8.orig.tar.gz 759ba5d4faa482893112c4b214be657e 2146 utils extra faketime_0.8-1.diff.gz 1e3dd85fce11ecb1dbde68b6200f4a51 20070 utils extra faketime_0.8-1_i386.deb ./oar-2.5.2/third_party/debian/faketime_0.8-1_amd64.deb0000644000175000017500000005206011757171206020316 0ustar plbplb! debian-binary 1222780476 0 0 100644 4 ` 2.0 control.tar.gz 1222780476 0 0 100644 945 ` Qo6OH$RE؊Hk` N'$$;MHۗlX?h4[8ϟ1*hTx]*/^pֆ}k?J);g,VTO B8WR5H+o ?yci}ΒNu& [QrU\J3|[ 9ɯOfKkuy䘎$F,v߃>dP2 گHoU7?<4B>r:+'y]UvTY ;s'x2dgB}z `c] H|B; 1\OCWz+5h'ݞxFdzʔy,8"{d=8gXm1-$Dz^`J>qSDi 6lF?w:9 Z߯#Ō'O`iSdR~n;: f\Oڪ%<JcSV8=ill>u:=KHNiyNNTqOd0K{,ڞ"`{xֻLSl^δ|Gi|N55=V8~y=E}WoypdlLNDOIoٴ[q#iy{gkN.I)P;U>'a sX]0ûĶmO-W,[jՔqTeӮ+VVL[1i쨐4թR"{.{y+ޠ̞fHλKWګu`<7n~w8Gڽ:T3>zэy n2Ԕ=QC߆lJvF0O|aGlK-1PS3ydZM]i;yw.J{ZM+G736=|ʝ-3umps+<-0Var.?y%iR=/oec,t0Ytrpŕ㝖ĶK<c}FsDŽO9z9TOZ_'nҋě2^z1|0Dq횭=v nf +~!wcWټǯ !Ag۬NZTsSv*`o_N ͻz:>'=q+A M}5/Duw Ps( >&Mؿ 6e ^9p5԰*Zewy+^|ZrboZ!p`iϦ-%h엧{ZY7Fm\sSmRZ)77g;\*jFbt+}qq םhީx1嚽U'yAVt%V7DVHVC^E/?~NS:T?(m}fO")=顬ށMFJ~^>'lϤbiY˗L{jRZj})o+th]K|e/mZY:1hp̗/}\RʘOj6mg)=Tlu{KBYŒ77EJVeU(,zܐ*dGme~wntXA`is~%i).32vJΞsBDѱNO%T}Zǜ8~H$讚}Fyӝ[oV2mn ]m6ϑV{sdi'ƽO|Ke*:#uPjs5>GlY\!*qq 5rGNI!n[2&TIޢ=F4x2{KСkeC"{ݧjcIՂrJ_ۏ)whqAaYC'-u̻=Sa%?L2޻^XWꬻgGq${ Wi mm{ڮhgKױ>/bI^>\g27Vm/U~8a(5ёFvw(o5/R':]14].62rݬ ˗=P8|I g~8C0y0i7ZX_hc^Cyg^:zMp`́-3hNxl1|ϔ|pD@W"_kG%۬۾t~z!-wlT)6vɣtxdPm*] )HGkY-h=Zp4sEIĪgvN~)hQܡwا1mq=6N/¢AC6U~hpI&8}TԾ &n \Z6^QOL?+UA@jsurwKYK2M{;zR)NU3P>!b$$N1GgSsB/(j#?N o^|IAit$2qUB|g/pjnMvMm1~Ã]7ŭg}<Y{"ghua DѓA:jbÜiц2,w}%ۜ+RO!ĦъtL:EY^[mTSժ`/6:?#0C*>zN"77RuqzeYe5t5ʮnp&z׷ߴ)_J}Ajפ͈o8T>zE  ~?& Ę#k}{2 k'IL?3 T%#p닥Qq b@$Ds5puPGd"}B}ydDW"OsΜjJ}59^\Z|[#!ʻ{:ByeN{xFsL4I{GI".j;ckZlqW=XHQU~#^Hv|b4}H8ymϏ:-(+DΠ$'z~gmcc=[;8XV~g^A T3;*EQXBtb1q.!΀<%! @Iqh $ E4'hc L"Bv[J@xRr2KP4wV8ڐY ݍۘ?;=kqqJ@*GK @1QDBM.$FЀ "Z=$b2`e 1@h F$S!RJPA\ yJTtN 'ZjU<@tJ E5X(@"9%DYE*!.%XC& 747ߠj F_D5 %H!]L pǃ6n'L(ׄ _@/r&@~H'F@z14̰ā&5OJLOPbT@) &1:FzJ"; Yt "f Q4ҏh YB1榐0"Nx[J"(xNv KkpYXZAn~D5J#$SM1. '9iTkdFÁsA$tL"%1>f A[t|FA`@D]6N(xd*˶=1*J0ssWn=++{KlH>E6tjM89Ͼ=#.9L*tezz>rٕ}B?4,Mٳ&Dgтϭ»w}^٬cYןN|7uOo3ެ=HU{oV92:JWys f~S|vp½QF.yݦ08tlj;/imx`|Kil톱v=Y&WD鼁} o'Wg݌ٯe 󳍉_6>}:U^6]_䒠-- &vQNJ v>~J[1+Qsj9@gҺںG&oU~vѲv>g 5f_T#fTrsܤ۬=z:@@c/!y057W{T= ٵ[FJOD5OktC`5{p/*u\x8eőG-rxp ӃOr*!)9>7g\]$vsJϻle}kʥGW+0I(Y/Y^izr\ʵk{5g߱!،uⷄ >mOɴ+)Onh6ع>3@eRk5ElUmqXwvn B7x:˗.X-5yK }'H9yKlp %J _;׆Y,BX^Ư\c܉ց K@g^P|\3| 9X6j0n=d;;pL//X޻;ueNM K)^IJsZdΥ"sKe/+֏\"5MO:AvolzΓCl.׿VJqfʨ(Br/T6oj0wQ)S %K?'/=C<"`q_Ev>eB=)Ҵ~VtkϪn,)펓}o~gE5N 'llxp.(wz|2E xτKU9 _t\Pէ-A֚ILlzz`:~c %w.ix&wy,g;ɇ+CI3 !JO #6\;aL욭2_w2iԄD7h8drĂqQK.f,!o+O uNƿ˶иg82:{SY?7ѳѵŎJ}zHj2'_1!C8hqZW|htU!f+JFM'dxGW4(~.nG2~ OU=-yRBץ_ȿpx]MC Zw5L5ݤ;OKӾZhҹb z]}9}^h<*WLh \v06`e\hUR@,kǩ7\|#S7M?녆 *<;}DȫgCׅ7 =ԹeRksb2R}X:uŵ/.orCݦmZS=[Ȃ&Rאj}~}5F&[jlGȤ#ftqi>{|2_нmtf.lǩobM%kl kZZr?nn1J QmJV馎>1pվn#=ӞS!X,)tcftѮGzHjk"Ad/=xehl>O#˳c,2jG+պ_?a{)q["__ixئke 2kմy;ϯon|@J^' z| v -}O>~Pe\|Guv^N u\/O1 DH*ܜd\BvE1dr6<{+#藧_q|qyc=,B=6gΨo+nWdflwV?{`}XA9D/4躡Af.2W5Nu]YzwϟhzdӆmRSW_eH1 j+"B }W:"6}9dӼ9[ JlhgCn 3_wNGx yE lxՓt;T6h+M3&p5OEWJA5E 8&?!9$mjWofe}'a( bŞ%"CgjuC htN~i4d@!qj|V<=F.*+M#Y//s5|S{u|.}dxy5>jGo7/$j|6yĴQkiH!_TJK\zYo 3-2*Iť/]V-ۚ$:|āeń3}%sxQb-eKܹ{췶NxXs݅$sy8!H*哤U}S۬R}pˑӖܰ^9jͻ|מ7Yʉ[W9)L,Sv7}Jrf\tU֚?'2[_,W8e"%݀*SfM)vWWS33kyWz{7յ{Ko*h8WڷnǗYs^i?OEiqImN[y%OIEzt:ɸɻ*zvVwe{oҔt3Gl1|hvvnqLcέj为+}̑86|{'usY;CVTsEҴ)ʃ+Ry}DOjGx/e4T37P@I"Q.NRn>-:9ݨ꾭t{ҽIQc7Ӑ(cѶfa|/^vnՒIq݉~}H~ʋ7eޗze >n^V@nyeD]D~y`KwRZ'=S=ԥ g'%dX]jhU;}{űD/w۲I&-&F P$|iA[N> a7mmVIqg#{ guǩk,zz^(M 8JMFK~MF.Q_yi܅;~F*[LJ[tܿ7ޠ!zs̵IWo9ec%"~ t}=^(P4_:!ӺI"<w\ROGm Uig<n9T4ްu$,~!|YcVԆ:=Z!sNM-ɳe N/`0Ϸ& nhG6O?Xū3,g+$/8+%7zTřRbI[y-'_ϊtvpɖRz3͝--tc̞{Q}U%%(=tKlm~!)l,}vB}6dJڒH\3Of~WYu۳+ɞ`9#+D*}¤2DwTKy*J Ni|&e/̰SlT/ϗ y儑l俭}c|w/|-m~u+ux8E~=[{]W*ujk#v|ߧC5SE(g,KoQzȉz|ʧ4*GG,0V9Q^Kv "ؕҸpm}gS/E]t͎WhM xXwSj$-2eNv٠N$?4ooiBs/ݑqq](N @i(2@ $CI$Ӑd [(BsxhJ%fS#}!(TB2H)*@5 LAQII4*"zA`chd(0:) OD(H0iA1K*+&N[!iFsB:DݒI`(jrh*(".O?*)azO"DQ Hl`@CqI?&&ҒpH ( ?C6K$;%<$D #gr g)I1 T$xq)؝DKO 9&8$ubKBa.{wE`$bf3!u mD,ks3;9H!c>3bS1`$2rX2O3*D&c^c6LA_pj2(p$R9*8^ s^'GE@Epb@Y2`*@J-ь@! Xr"8>~!k06-cBpЈgz|: Ap@Ad~{ )ffemfm Yvs``bmj[YZVQ,2͝a 4J %AHP@Ow?_@IZ"`-M H7c d1^ VQTHiGn' d!SpAnXGBUT\~<|X㘵rd cx#?277g)ȒPwo 8 J'L=捌=؜J2.6տ}Auuuٽr4]?nm7c< &Eoh@1ܲ)Q9xb郷5[ ?RB% xeiT(65tlsC2B4C}Ɩ.v^lN½g4=]#xN&D;O4'O/?*烎ꂲC[LSef杯;+Z"{YQI/üK'uLZ&KSWX7`^ß-K1gشRM6"_K"ǥoE2n\w+Ǯwswa\z}TǥW@7|25lTq駠HOCewe$NՔjd>qοЃ(DĞQ{[9NiSSP *]tQ=8{~tQcF;;z7/lz@=<|+io#wկG~TA^ >=oQl3R86ջn w"gr r _군ED'Db"PƤGQIs D;i(*@&q82!=2KBDx*6+ 1bZ"16HHi)X1I$jF2K7hm ?G GD1&] IZK>Nxkd).6*A89)D1G%)DZJcȠL ¥$ `$y6"h$YL7\Fs`>&K4bp.WB,4RK8)}&w66c^ۘЧ<~CG5l%A|?1|fzaď ޒY/:\1p}=i=Gg|ے?_gӳ?ձ%*lnT/L<6*o܂S9GQPp>@ @Q3€0d h{~ @ې%`.A=gnq2ڋi3&@3?1Se|-P1!m5f_ l:b$;>x$"ևQ t{^& @k(ҫ`7 B=E:e7,j黁Yϯ"4 2t8}78 =bRWBt _h7z^Lt3x|1L큹UQp@kFw“,gt,s57hj~  P llJd5jO眅Mq##Ng r t-xN1UOUgՆ_r׆x# k@,x0dB#9{]~m&`!}K_X}ǫmWe6qJh<_-8  +cl< e+Ëby$ H /?Z<6n]++//@1@>H`:k@/;:B3<6=1m&z,j>#u3DNrwr?i`S 03~-68yn??nmmAӟ7fH_GDD m/\4-&J, 3`"8R i w`~f揈u !vLnL59;ԈC(CX.1X]WD  E@D+-L?1['Nb4ClFAGĜ> 1O?CW ( Xթ2T_VL#<:0Eu&`-r,6!N }C;=W,j[oq~vK-η|{ʁ(0ѳP^@/ѻB/<^*8U&zV,~@gb񁉞*'z{9STǯozl-oq&*.VqۋGD0YzT =xUL+Ηf!x%IrʜD0Zy_o۔~DLgw =,rKz6s'HkyEhc<{KX=ǓSb"'D$ ;nx&ǺWDΰSH4iWq [T؂{6AXu80t@L:>'KFRIG>yzqUR߅֢jxB]V%lP<&HE<c£_Sɂg{l&<H9' {&< wrcXr#S5]Y>8[8wQ#$9BHBf*]r`a b#'~"a?ȅ.|.mvLbg!|W ef^Ev/ 1$7(}сrawHerm$0}X hic,: iŒ/ dNEuYʸ'Q0]D~؅2>C;46;xxѐ37+\*XXZ&u?2܏WFγ& a8K蕑, p2/ y&$h6Pe6bo>&aziMÚ [z˰VfZ۲[פ3lak [K5fpS$]-|b]ׁ`6eho?B8\^F k^YkL.b|YN Z,I`S@v] 7t^qxLn} 5[ߠ1xÈ #&9cҍĤ q~1ty'1٪w\ Ͱr,US&_f2gaNf.E14o\t uSm~kk;]~W +]i YS}^zp͂!P *(1+Rzf Fn?944ˑ™S"܂p&8ׄ?T s,p1*>krN~~,ęHs0GDVXfKpFӖai7 @a{h @;cU:Sg8/Ny|mRhd2`TYd@"h5;Љr4 rh=_,*>F06 s].u.ԇGawοlo#Ïx?|@ > z? Y4/+SzbO59@?ֆ07zCCH!/AQôkHS5Po1^kS?za귂zmP2L}5W{@pQ1q[}|h+b9BX- }<#Qμn[<{^~5sw, 'HpޯBx(A? Ό+}hy Z?K:UT>G{T~G45J_T\bTDe *PQhTr *}e9 űS4 -*gOL1O&c3p+"iF 5p"@W$B5A8s$לBB2> qdBDB,U™H*!fPc$7h*߂ !&c8(6LcM8ql }ÂcM 훟?󌝧16l_b2k\X$Knl3\5L0'q-LbqiLIs|c3c|:L"br`RGc34 ?warÍ՞n)>R=>X@q_վml;sFw[.oFq9׏ W{l^OYsdW{9mG_lZN:fhH.!g'}C6À g;]+B1+wo )ĸ&lC9qDpFq?p(Do~7~vGDZl^Y.ϹŌ g4M6l%( ';Hh^![qG6&:/>O#6&qyKrTyxq>10max_p-3 ?[o~3o~0No~0lW}+x.(/V]pP}2c=TB7EUs\7P*uy~yBF4g2mN^uO ': T}~0??ks}qF~};!>/D`#qPj%θɫF OFDT'Tƫ<5y?LfLb,f^f_ci2V&볞؟B XҟP&L0dT1gbN7!N/f8 @)tn>7#>|nF|ʖ-܌ȡC9 >-p2rLם嘮;iۑ0#u!C BpЏYvdRq <g dreϘ'#fey3`:f"cbsD(hWƪ1l4A'6ȈBIaNf`ނP~Z`E['M O0&ͱ9+5t blی'i2l#l sbpV8+1譈qV9+X6)"YaS 6=i2a#"BX fc~d8+ `ۺ^BfpV99+P,7ypN="cqV<qE-g SHpqVȜ2Bf 709+dx)dgcpVl4GiRv|ˑ΄9+B Ί*ge"Hc0gpV &g. U(N14kuXB, p6,J6܏]7p$ڒn6y&&r 7 vPI X)z^L Y!YA!vkq }Ń х3ܻXֶ gO8+h/ܜD3 hrV086 4—erV608+S%py$9{g_pVvObrVNB\';SJ:BvŎSr;xP;y;)"?ƒȿgSn?EOaS:{ ~J[ROHa~J'?ŹOySt(?SQ~OD)(?eoS\"| Oy E~O~a~ʮgt?>+ CD~O"مR ~7nx~'nx~SP 7J,WOO` Sʸ)e"9;y\c|?秐 8)৐9bGড়sSB,O^ S *PQ(#Pi1Ee8*\,FAT֠Qߥ8/7k|`oM}I7&ؾ1` !71&x8 vn`27&9IHrHrJoM]LO`w&u]po)oG{|,}㛬Bۯ{|V^`>!?8i a( !Ab P;^]M֛؏vbo{ 5=./oar-2.5.2/third_party/debian/faketime_0.8-1.dsc0000644000175000017500000000161011757171206017335 0ustar plbplbFormat: 1.0 Source: faketime Binary: faketime Architecture: any Version: 0.8-1 Maintainer: Daniel Kahn Gillmor Dm-Upload-Allowed: yes Homepage: http://www.code-wizards.com/projects/libfaketime/ Standards-Version: 3.8.0 Vcs-Browser: http://cmrg.fifthhorseman.net/browser/trunk/faketime Vcs-Svn: http://cmrg.fifthhorseman.net/svn/trunk/faketime Build-Depends: debhelper (>= 7) Checksums-Sha1: 7053ba5984a94d24cbe63bb7f285a9d1c5418e31 21188 faketime_0.8.orig.tar.gz 29cdfcc4785f7eac4383357fc17cd1280eab5062 2135 faketime_0.8-1.diff.gz Checksums-Sha256: e115120ab4d199f7fd2ecdb87ae2963523d6e8a5314c2e914573afd7d5c704c3 21188 faketime_0.8.orig.tar.gz 9708c330c4bbc293c19c79ccade6b4a29ca89d8e366bf2e50e52b9dddc9032b7 2135 faketime_0.8-1.diff.gz Files: c83e12a80ca85486bc904905d325d01e 21188 faketime_0.8.orig.tar.gz be0fa95bd6a622012b9562ce5618184d 2135 faketime_0.8-1.diff.gz ./oar-2.5.2/third_party/debian/faketime_0.8-1.diff.gz0000644000175000017500000000412711757171206020121 0ustar plbplb &HWioH<qG"%G9`E>bxq=˛Ã?>r啴/C,w<=$VDL[ݽk{ρ'Ɔa$3c1i)\< 艰 22 E1$V  TA(>y^t=3u[ŋ2pc":RM4 #sb3dB-s[ɎdF￀n/&/@K{"1AVoSԃRVEvc]_xS^v2UU{[@:7pŊ@:TX s-a;$:OY|AIA16UrlXi# 3a DށER[,>L[T#!qNl!VQ"&p464U$;p6Q{\OV?HRaag)UYV,76VM( Ah2Q" RWÎ9)V,"[o@fpdrn~LԆzI-<\9M(@/T @x]˧,Wڿ{\jy\P%eϑӹ6,E+p9)|}MSs^X]kff G՗OWH{)^VQ~#~ˋ{z[8L ziV]޽u׍?}OyTrt9QV+iuqgQz"pv_$)Y^PZmgwO W ϫBh#~F%Vw2] kmtF37./oar-2.5.2/third_party/debian/README0000644000175000017500000000046311757171206015217 0ustar plbplbThis directory contains Debian source packages for 3rd party software that are not packaged in official Debian distributions yet, but needed by OAR = Note about the Debian packaging of CPAN modules = Packaging is pretty straightforward using the following command: dh-make-perl --dh 7 --cpan Module::Perl ./oar-2.5.2/tests/0000755000175000017500000000000011757171206011723 5ustar plbplb./oar-2.5.2/tests/test_gantt_overlap.pl0000755000175000017500000000450111757171206016167 0ustar plbplb#!/usr/bin/perl # $Id$ # Description: # Simple script to check that a gantt is correct, with regard to overlaping jobs # Usage: # oarstat -g "2008-04-29 18:00:00, 2008-04-30 08:00:00" | ./test_gantt_overlap.pl # Todo: # - handle timesharing jobs # - handle container jobs # - ... use strict; use Data::Dumper; my $gantt_input; while () { $gantt_input .= $_; } my $gantt = eval($gantt_input); my $jobs = $gantt->{jobs}; my @jobids = keys(%$jobs); sub match($$) { my $a1 = shift; my $a2 = shift; my %h; my @res; foreach my $k (@$a1,@$a2) { if (exists($h{$k})) { push @res,$k; } $h{$k}=undef; } return @res; } while (my $jobid0 = shift(@jobids)) { foreach my $jobidN (@jobids) { if (( # job0 across jobN start_time $jobs->{$jobid0}->{start_time} <= $jobs->{$jobidN}->{start_time} and $jobs->{$jobid0}->{stop_time} >= $jobs->{$jobidN}->{start_time} ) or ( # job0 across jobN stop_time $jobs->{$jobid0}->{start_time} <= $jobs->{$jobidN}->{stop_time} and $jobs->{$jobid0}->{stop_time} >= $jobs->{$jobidN}->{stop_time} ) or ( # job0 within jobN $jobs->{$jobid0}->{start_time} >= $jobs->{$jobidN}->{start_time} and $jobs->{$jobid0}->{stop_time} <= $jobs->{$jobidN}->{stop_time} )) { my @m = match($jobs->{$jobid0}->{resources},$jobs->{$jobidN}->{resources}); if ($#m > 0) { # exceptions my $ok; foreach my $r ($jobid0,$jobidN) { my $j = ($r == $jobid0)?$jobidN:$jobid0; if ( # besteffort $jobs->{$r}->{queue_name} eq 'besteffort' and $jobs->{$j}->{queue_name} ne 'besteffort' and $jobs->{$r}->{state} eq 'Waiting' and #other states ? $jobs->{$j}->{state} eq 'Waiting' and #other states ? $jobs->{$r}->{start_time} < $jobs->{$j}->{start_time} ) { $ok=1; } elsif (0) { # container } elsif (0) { # timesharing } last if (defined($ok)) } unless (defined($ok)) { print "CONFLICT: $jobid0 and $jobidN\n"; foreach my $j ($jobid0,$jobidN) { print "|-[$j]-start_time: ".localtime($jobs->{$j}->{start_time})."\n"; print "|-[$j]-stop_time: ".localtime($jobs->{$j}->{stop_time})."\n"; print "|-[$j]-user: ".$jobs->{$j}->{user}."\n"; print "|-[$j]-walltime: ".$jobs->{$j}->{walltime}."\n"; } print "`-conficting resources: ".join(", ",@m)."\n"; } } } } } ./oar-2.5.2/tests/scheduler/0000755000175000017500000000000011757171206013701 5ustar plbplb./oar-2.5.2/tests/scheduler/src0000777000175000017500000000000011757171206016523 2../../sources/ustar plbplb./oar-2.5.2/tests/scheduler/scripts/0000755000175000017500000000000011757171206015370 5ustar plbplb./oar-2.5.2/tests/scheduler/scripts/oar.sh0000644000175000017500000000242711757171206016512 0ustar plbplb#!/bin/bash # $Id$ # Function set to handle oar setup within tests BUILDDIR=${BUILDDIR:-build} DATADIR=${DATADIR:-data} oar_copy_config() { local FILE=${1:-oar.conf} if [ -r "$DATADIR/${BASEPREFIX}_$FILE" ]; then debug "Copying OAR config file from $FILE" mkdir -p $BUILDDIR/etc/oar cp $DATADIR/${BASEPREFIX}_$FILE $BUILDDIR/etc/oar/oar.conf debug "done" else echo "Can't read file: $FILE" exit 1 fi } oar_source_config() { debug "Sourcing OAR config" . $BUILDDIR/etc/oar/oar.conf debug "done" } oar_install() { debug "Installing OAR in the build dir" ( cd $SRCDIR make server-install server-setup \ ETCDIR=$BUILDDIR/etc \ VARLIBDIR=$BUILDDIR/var \ RUNDIR=$BUILDDIR/var/run \ LOGDIR=$BUILDDIR/var/log \ OARUSER=$(id -un) \ OAROWNERGROUP=$(id -gn) \ PREFIX=$BUILDDIR/usr/local \ ROOTUSER=$(id -un) \ ROOTGROUP=$(id -gn) \ > /dev/null ) debug "done" } oar_run_scheduler() { debug "Starting OAR Meta-Scheduler" ( cd $BUILDDIR/usr/local/oar OARCONFFILE=$BUILDDIR/etc/oar/oar.conf OARDIR=$BUILDDIR/usr/local/oar ./oar_meta_sched 1>&2 ) debug "done" } ./oar-2.5.2/tests/scheduler/scripts/mysql.sh0000644000175000017500000000607411757171206017100 0ustar plbplb#!/bin/bash # $Id$ # function set to handle mysql within tests BUILDDIR=${BUILDDIR:-build} DATADIR=${DATADIR:-data} mysql_check_oar_conf() { if [ "x$DB_HOSTNAME" != "x127.0.0.1" ]; then echo "Please set DB_HOSTNAME=\"127.0.0.1\" in oar.conf (localhost means unix-socket)" exit 1 fi debug "OAR config is ok" } mysql_copy_config() { local FILE=${1:-my.cnf} if [ -r "$DATADIR/${BASEPREFIX}_$FILE" ]; then debug "Copying MySQL config file from $FILE" mkdir -p $BUILDDIR/etc/mysql cp $DATADIR/${BASEPREFIX}_$FILE $BUILDDIR/etc/mysql/my.cnf debug "done" else echo "Can't read file: $FILE" exit 1 fi } mysql_init() { debug "Initializing MySQL data..." mysql_install_db --no-defaults --datadir=$BUILDDIR/var/lib/mysql > /dev/null 2>&1 debug "done" } mysql_start() { if ! [ -r $BUILDDIR/var/run/mysqld.pid ]; then if [ -z "$1" ]; then debug "Starting MySQL..." mysqld_safe --defaults-file=$BUILDDIR/etc/mysql/my.cnf \ --user=$USER \ --pid-file=$BUILDDIR/var/run/mysqld.pid \ --socket=$BUILDDIR/var/run/mysqld.sock \ --port=${DB_PORT:-3306} \ --datadir=$BUILDDIR/var/lib/mysql \ > /dev/null 2>&1 & else debug "Starting MySQL at time $@..." faketime -f "$@" mysqld_safe --defaults-file=$BUILDDIR/etc/mysql/my.cnf \ --user=$USER \ --pid-file=$BUILDDIR/var/run/mysqld.pid \ --socket=$BUILDDIR/var/run/mysqld.sock \ --port=${DB_PORT:-3306} \ --datadir=$BUILDDIR/var/lib/mysql \ > /dev/null 2>&1 & fi sleep 1 while ! mysqladmin --no-defaults \ --socket=$BUILDDIR/var/run/mysqld.sock \ ping > /dev/null 2>&1 ; do sleep 1 debug "Not there yet..." done debug "done" else debug "MySQL is already running (pid file found)" fi } mysql_create_oar_db() { debug "Creating OAR database..." cat < /dev/null 2>&1 ; do debug "MySQL still there..." sleep 1 done while [ -e "$BUILDDIR/var/run/mysqld.pid" ]; do debug "Pidfile still there..." sleep 1 done debug "done" else echo "No pid found, is MySQL really running ?" exit 1 fi } ./oar-2.5.2/tests/scheduler/scripts/base.sh0000644000175000017500000000127211757171206016640 0ustar plbplb#!/bin/bash # $Id$ # function set for the test base setup DEBUG=${DEBUG:-0} BUILDDIR=${BUILDDIR:-build} debug() { if [ $DEBUG -gt 0 ]; then echo $* 1>&2 fi } test_print_ok() { echo OK: $* } test_print_ko() { echo KO: $* } test_exit_status () { local TXT=$1 shift if eval $*; then test_print_ok $TXT else test_print_ko $TXT fi } test_prepare() { debug "Create base directories in the build directory" mkdir -p $BUILDDIR/etc mkdir -p $BUILDDIR/tmp mkdir -p $BUILDDIR/usr/local/lib mkdir -p $BUILDDIR/usr/local/bin mkdir -p $BUILDDIR/var/lib mkdir -p $BUILDDIR/var/run debug "done" } test_cleanup() { debug "Clean-up build directory" rm $BUILDDIR/* -rf debug "done" } ./oar-2.5.2/tests/scheduler/data/0000755000175000017500000000000011757171206014612 5ustar plbplb./oar-2.5.2/tests/scheduler/data/003_oar.conf0000644000175000017500000003243511757171206016633 0ustar plbplb# This file must have the bash variable assignment syntax # $Id: oar.conf 1535 2008-07-28 10:35:09Z neyron $ ######################### # General configuration # ############################################################################### # # Database type ("mysql" or "Pg") DB_TYPE="mysql" # DataBase hostname DB_HOSTNAME="127.0.0.1" # Database port DB_PORT="3307" # Database base name DB_BASE_NAME="oar" # DataBase user name DB_BASE_LOGIN="oar" # DataBase user password DB_BASE_PASSWD="oar" # DataBase read only user name #DB_BASE_LOGIN_RO="oar_ro" # DataBase read only user password #DB_BASE_PASSWD_RO="oar_ro" # OAR server hostname SERVER_HOSTNAME="localhost" # OAR server port SERVER_PORT="6666" # when the user does not specify a -l option then oar use this OARSUB_DEFAULT_RESOURCES="/resource_id=1" # force use of job key even if --use-job-key or -k is not set. OARSUB_FORCE_JOB_KEY="no" # OAR log level: 3(debug+warnings+errors), 2(warnings+errors), 1(errors) LOG_LEVEL="3" # If you want to debug oarexec on nodes then affect 1 (only effective if # DETACH_JOB_FROM_SERVER = 1) OAREXEC_DEBUG_MODE="0" # oarexec default temporary directory # This value MUST be the same in all oar.conf on all nodes of the cluster OAR_RUNTIME_DIRECTORY="/var/lib/oar" # OAR log file LOG_FILE="/var/log/oar.log" # OAR Allowed networks # Networks or hosts allowed to submit jobs to OAR and compute nodes may be # specified here ALLOWED_NETWORKS="127.0.0.1/32 0.0.0.0/0" # Specify where we are connected with a job of the deploy type DEPLOY_HOSTNAME="127.0.0.1" # Specify where we are connected with a job of the cosystem type COSYSTEM_HOSTNAME="127.0.0.1" # Specify the database field to use to fill the file on the first node of the # job in $OAR_NODE_FILE (default is 'network_address'). Only resources with # type=default are displayed in this file. #NODE_FILE_DB_FIELD="network_address" # Specify the database field that will be considered to fill the node file used # by the user on the first node of the job. for each different value of this # field then OAR will put 1 line in the node file(by default "cpu"). #NODE_FILE_DB_FIELD_DISTINCT_VALUES="cpu" # If you want to free a process per job on the server you can change this tag # into 1 (you must enable all nodes to connect to SERVER_PORT on the # SERVER_HOSTNAME) DETACH_JOB_FROM_SERVER="0" # Command to use to connect to other nodes (default is "ssh" in the PATH) OPENSSH_CMD="/usr/bin/ssh -p 6667" # Set the timeout value for each ssh connection (default is 120) #OAR_SSH_CONNECTION_TIMEOUT="200" # If you have installed taktuk and want to use it to manage remote # admnistration commands then give the full command path # (with your options except "-m" and "-o"). # You don t also have to give any taktuk command. # (taktuk version must be >= 3.6) #TAKTUK_CMD="/usr/bin/taktuk -s" ############################################################################### ######################################################################## # Pingchecker options: # # How to check if the nodes have a good health or not. This choice is # # directly linked to the Suspected state of the resources. # # By default OAR uses only "ping". it requests no configuration but it # # is not accurate about the state of the nodes and it is slow # ############################################################################### # # Set the frequency for checking Alive and Suspected resources (0 means never) FINAUD_FREQUENCY="0" # Set time after which Suspected resources become Dead (default is 0 and it # means never) #DEAD_SWITCH_TIME="600" # Uncomment only one of the following PINGCHECKER configuration # sentinelle.pl # If you want to use sentinelle.pl then you must use this tag. # (sentinelle.pl is like a "for" of ssh but it adds timeout and window to # avoid overloading the server) # (sentinelle.pl is provided with OAR in the install directory) #PINGCHECKER_SENTINELLE_SCRIPT_COMMAND="/usr/lib/oar/sentinelle.pl -t 5 -w 20" # Taktuk # taktuk may be used to check aliveness of nodes. # Give the arguments of the taktuk command WITHOUT format outputs # (DO NOT use "-o" option). # See TAKTUK_CMD tag in this file to specify the path of the taktuk command #PINGCHECKER_TAKTUK_ARG_COMMAND="-t 3 broadcast exec [ true ]" #PINGCHECKER_TAKTUK_ARG_COMMAND="-t 3 broadcast exec [ oarnodecheckquery ] #PINGCHECKER_TAKTUK_ARG_COMMAND="-t 3 broadcast exec [ /path/on/nodes/to/my/check/script.sh ]" # fping # fping may be used instead of ping to check aliveness of nodes. # uncomment next line to use fping. Give the complete command path. #PINGCHECKER_FPING_COMMAND="/usr/bin/fping -q" # nmap # nmap may be used instead of ping to check aliveness of nodes. # uncomment next line to use nmap. Give the complete command path. # It will test to connect on the ssh port (22) #PINGCHECKER_NMAP_COMMAND="/usr/bin/nmap -p 22 -n -T5" # GENERIC command # a specific script may be used instead of ping to check aliveness of nodes. # uncomment next line and give the complete command path and its arguments. # The script must return bad nodes on STDERR (1 line for a bad node and it must # have exactly the same name that OAR has given in argument of the command) PINGCHECKER_GENERIC_COMMAND="/bin/true" ############################################################################### ###################### # Mail configuration # ############################################################################### # # OAR information may be notified by email to the administror # set accordingly to your configuration and uncomment the next lines to # activate the feature. # (If this tag is right configured then users can use "--notify" option of oarsub # to receive mails about their jobs) #MAIL_SMTP_SERVER="smtp.serveur.com" # You can specify several recipients with a comma between each email address #MAIL_RECIPIENT="user@domain.com" #MAIL_SENDER="oar@domain.com" ############################################################################### ########### # Scripts # ############################################################################### # # Set the timeout for the prologue and epilogue execution on computing nodes #PROLOGUE_EPILOGUE_TIMEOUT="60" # Files to execute before and after each job on the first computing node # (by default nothing is executed) #PROLOGUE_EXEC_FILE="/path/to/prog" #EPILOGUE_EXEC_FILE="/path/to/prog" # Set the timeout for the prologue and epilogue execution on the OAR server #SERVER_PROLOGUE_EPILOGUE_TIMEOUT="60" # Files to execute before and after each job on the OAR server (by default # nothing is executed) #SERVER_PROLOGUE_EXEC_FILE="/path/to/prog" #SERVER_EPILOGUE_EXEC_FILE="/path/to/prog" ######################## # Scheduler parameters # ############################################################################### # # Maximum of seconds used by a scheduler SCHEDULER_TIMEOUT="60" # Time to add between each jobs (for example: time for administration tasks or # time to let computers to reboot) SCHEDULER_JOB_SECURITY_TIME="1" # Minimum time in seconds that can be considered like a hole where a job could # be scheduled in (if you have performance problems, you can try to increase # this) SCHEDULER_GANTT_HOLE_MINIMUM_TIME="300" # You can add an order preference on resources assigned by the # system(SQL ORDER syntax) SCHEDULER_RESOURCE_ORDER="scheduler_priority ASC, suspended_jobs ASC, network_address DESC, resource_id ASC" # If next line is uncommented then OAR will automatically update the value of # "scheduler_priority" field corresponding to the besteffort jobs. # The syntax is field names separated by "/". The value affected to # "scheduler_priority" depends of the position of the field name. SCHEDULER_PRIORITY_HIERARCHY_ORDER="/host/cpu/core/" # You can specify a type of resources that will be always assigned for each job # (for exemple: enable all jobs to be able to log on the cluster frontales) # For more information, see the FAQ #SCHEDULER_RESOURCES_ALWAYS_ASSIGNED_TYPE="frontal" # This says to the scheduler to treate resources of these types, where there is # a suspended job, like free ones. So some other jobs can be scheduled on these # resources. (list resource types separate with spaces; Default value is # nothing so no other job can be scheduled on suspended job resources) #SCHEDULER_AVAILABLE_SUSPENDED_RESOURCE_TYPE="default licence VLAN" SCHEDULER_AVAILABLE_SUSPENDED_RESOURCE_TYPE="default" # For a debug purpose, scheduler decisions can be logged into the database # Uncomment the next line in order to activate the logging mechanism SCHEDULER_LOG_DECISIONS="no" # Time to wait when a reservation has not got all resources that it has reserved # (some resources may have become Suspected or Absent since the job submission) # before to launch the job on the remaining resources (default is 300s) #RESERVATION_WAITING_RESOURCES_TIMEOUT="300" # Set the granularity of the OAR accounting feature (in seconds) # Used by the oaraccounting command and the # oar_sched_gantt_with_timesharing_and_fairsharing to calculate the timesharing # policy. Default is 1 day (86400s) #ACCOUNTING_WINDOW="86400" ############################################################################### ########################################################################### # If you want to manage starting and stopping node feature. OAR gives you # # this API: # ############################################################################### # # When OAR scheduler wants some nodes to wake up then it launches this command # and puts on its STDIN the list of nodes to wake up (one hostname by line). # The scheduler looks at the available_upto field in the resources table to know # if the node will be started for enough time. #SCHEDULER_NODE_MANAGER_WAKE_UP_CMD="/path/to/the/command with your args" # When OAR considers that some nodes can be shut down, it launches this command # and puts the node list on its STDIN(one hostname by line). #SCHEDULER_NODE_MANAGER_SLEEP_CMD="/path/to/the/command args" #SCHEDULER_NODE_MANAGER_SLEEP_CMD="taktuk -s -f - -t 3 b e t 3 k 9 [ sudo halt ]" #SCHEDULER_NODE_MANAGER_SLEEP_CMD="/usr/local/oar/sentinelle.pl -f - -t 3 -p 'sudo halt'" # Parameters for the scheduler to decide when a node is idle. # Number of seconds since the last job was terminated on nodes #SCHEDULER_NODE_MANAGER_IDLE_TIME="600" # Parameters for the scheduler to decide if a node will have enough time to sleep. # Number of seconds before the next job #SCHEDULER_NODE_MANAGER_SLEEP_TIME="600" ################################################################################ ############################## # Suspend/Resume job feature # ############################################################################### # # Name of the perl script that manages suspend/resume. # (default is /etc/oar/suspend_resume_manager.pl) #SUSPEND_RESUME_FILE="/etc/oar/suspend_resume_manager.pl" # Files to execute just after a job was suspended and just before a job was resumed #JUST_AFTER_SUSPEND_EXEC_FILE="/path/to/prog" #JUST_BEFORE_RESUME_EXEC_FILE="/path/to/prog" # Timeout for the two previous scripts #SUSPEND_RESUME_SCRIPT_TIMEOUT="60" ############################################################################### ################## # CPUSET feature # ############################################################################### # Indicate the name of the database field that contains the cpu number of the node. # If this option is set then users must use oarsh instead of ssh to walk on # each nodes that they have reserved via oarsub. # Look at Tools/oarsh/README # (if defined, this otion turn on the cpuset feature in OAR) #CPUSET_RESOURCE_PROPERTY_DB_FIELD="cpuset" # Name of the perl script that manages cpuset. # (default is /etc/oar/cpuset_manager.pl which handles the linux kernel cpuset) CPUSET_FILE="/etc/oar/cpuset_manager.pl" # Path of the relative directory where the cpusets will be created on each # nodes(same value than in /proc/self/cpuset) # If you change the default value (/oar) then you have to edit the file # cpuset_manager.pl and change it accordingly. CPUSET_PATH="/oar" ############################################################################### ######### # OARSH # ############################################################################### # # This variable must be set to enable the use of oarsh from a frontale node # Otherwise you must not set this variable if you are not on a frontale #OARSH_OARSTAT_CMD="/usr/bin/oarstat" # The following variable adds options to ssh (or OPENSSH_CMD if configured). # If one option is not handled by your ssh version just remove it BUT be # careful because these options are there for security reasons OARSH_OPENSSH_DEFAULT_OPTIONS="-oProxyCommand=none -oPermitLocalCommand=no" # If you set this variable to something different from 0 then oarsh will act # like a normal ssh **without** CPUSET restriction. # WARNING: this is a critical functionality (this is only useful if users want # to have a command to connect on every nodes without taking care of there ssh # configuration and act like a ssh) #OARSH_BYPASS_WHOLE_SECURITY="0" ############################################################################### #DESKTOP_COMPUTING_ALLOW_CREATE_NODE="0" #DESKTOP_COMPUTING_EXPIRY="300" #STAGEOUT_DIR="/var/lib/oar/stageouts/" #STAGEIN_DIR="/var/lib/oar/stageins" #STAGEIN_CACHE_EXPIRY="144" ./oar-2.5.2/tests/scheduler/data/003_my.cnf0000777000175000017500000000000011757171206020002 2000_my.cnfustar plbplb./oar-2.5.2/tests/scheduler/data/003_heavy_load.sql0000644000175000017500000111276411757171206020044 0ustar plbplb-- MySQL dump 10.11 -- -- Host: localhost Database: oar -- ------------------------------------------------------ -- Server version 5.0.51a-15-log /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; /*!40101 SET NAMES utf8 */; /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; /*!40103 SET TIME_ZONE='+00:00' */; /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; -- -- Table structure for table `accounting` -- DROP TABLE IF EXISTS `accounting`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `accounting` ( `window_start` int(10) unsigned NOT NULL, `window_stop` int(10) unsigned NOT NULL, `accounting_user` varchar(255) NOT NULL, `accounting_project` varchar(255) NOT NULL, `queue_name` varchar(100) NOT NULL, `consumption_type` enum('ASKED','USED') NOT NULL, `consumption` int(10) unsigned NOT NULL, PRIMARY KEY (`window_start`,`window_stop`,`accounting_user`,`accounting_project`,`queue_name`,`consumption_type`), KEY `accounting_user` (`accounting_user`), KEY `accounting_project` (`accounting_project`), KEY `accounting_queue` (`queue_name`), KEY `accounting_type` (`consumption_type`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `accounting` -- LOCK TABLES `accounting` WRITE; /*!40000 ALTER TABLE `accounting` DISABLE KEYS */; /*!40000 ALTER TABLE `accounting` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `admission_rules` -- DROP TABLE IF EXISTS `admission_rules`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `admission_rules` ( `id` int(10) unsigned NOT NULL auto_increment, `rule` text NOT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM AUTO_INCREMENT=15 DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `admission_rules` -- LOCK TABLES `admission_rules` WRITE; /*!40000 ALTER TABLE `admission_rules` DISABLE KEYS */; INSERT INTO `admission_rules` VALUES (1,'if (not defined($queue_name)) {$queue_name=\"default\";}'),(2,'die (\"[ADMISSION RULE] root and oar users are not allowed to submit jobs.\\n\") if ( $user eq \"root\" or $user eq \"oar\" );'),(3,'\nmy $admin_group = \"admin\";\nif ($queue_name eq \"admin\") {\n my $members; \n (undef,undef,undef, $members) = getgrnam($admin_group);\n my %h = map { $_ => 1 } split(/\\s+/,$members);\n if ( $h{$user} ne 1 ) {\n {die(\"[ADMISSION RULE] Only member of the group \".$admin_group.\" can submit jobs in the admin queue\\n\");}\n }\n}\n'),(4,'\nmy @bad_resources = (\"type\",\"state\",\"next_state\",\"finaud_decision\",\"next_finaud_decision\",\"state_num\",\"suspended_jobs\",\"besteffort\",\"deploy\",\"expiry_date\",\"desktop_computing\",\"last_job_date\",\"available_upto\",\"scheduler_priority\");\nforeach my $mold (@{$ref_resource_list}){\n foreach my $r (@{$mold->[0]}){\n my $i = 0;\n while (($i <= $#{$r->{resources}})){\n if (grep(/^$r->{resources}->[$i]->{resource}$/, @bad_resources)){\n die(\"[ADMISSION RULE] \'$r->{resources}->[$i]->{resource}\' resource is not allowed\\n\");\n }\n $i++;\n }\n }\n}\n'),(5,'\nif (grep(/^besteffort$/, @{$type_list}) and not $queue_name eq \"besteffort\"){\n $queue_name = \"besteffort\";\n print(\"[ADMISSION RULE] Automatically redirect in the besteffort queue\\n\");\n}\nif ($queue_name eq \"besteffort\" and not grep(/^besteffort$/, @{$type_list})) {\n push(@{$type_list},\"besteffort\");\n print(\"[ADMISSION RULE] Automatically add the besteffort type\\n\");\n}\nif (grep(/^besteffort$/, @{$type_list})){\n if ($jobproperties ne \"\"){\n $jobproperties = \"($jobproperties) AND besteffort = \\\'YES\\\'\";\n }else{\n $jobproperties = \"besteffort = \\\'YES\\\'\";\n }\n print(\"[ADMISSION RULE] Automatically add the besteffort constraint on the resources\\n\");\n}\n'),(6,'\nif ((grep(/^besteffort$/, @{$type_list})) and ($reservationField ne \"None\")){\n die(\"[ADMISSION RULE] Error: a job cannot both be of type besteffort and be a reservation.\\n\");\n}\n'),(7,'\nif (grep(/^deploy$/, @{$type_list})){\n if ($jobproperties ne \"\"){\n $jobproperties = \"($jobproperties) AND deploy = \\\'YES\\\'\";\n }else{\n $jobproperties = \"deploy = \\\'YES\\\'\";\n }\n}\n'),(8,'\nmy @bad_resources = (\"core\",\"cpu\",\"resource_id\",);\nif (grep(/^(deploy|allow_classic_ssh)$/, @{$type_list})){\n foreach my $mold (@{$ref_resource_list}){\n foreach my $r (@{$mold->[0]}){\n my $i = 0;\n while (($i <= $#{$r->{resources}})){\n if (grep(/^$r->{resources}->[$i]->{resource}$/, @bad_resources)){\n die(\"[ADMISSION RULE] \'$r->{resources}->[$i]->{resource}\' resource is not allowed with a deploy or allow_classic_ssh type job\\n\");\n }\n $i++;\n }\n }\n }\n}\n'),(9,'\nif (grep(/^desktop_computing$/, @{$type_list})){\n print(\"[ADMISSION RULE] Added automatically desktop_computing resource constraints\\n\");\n if ($jobproperties ne \"\"){\n $jobproperties = \"($jobproperties) AND desktop_computing = \\\'YES\\\'\";\n }else{\n $jobproperties = \"desktop_computing = \\\'YES\\\'\";\n }\n}else{\n if ($jobproperties ne \"\"){\n $jobproperties = \"($jobproperties) AND desktop_computing = \\\'NO\\\'\";\n }else{\n $jobproperties = \"desktop_computing = \\\'NO\\\'\";\n }\n}\n'),(10,'\nif ($reservationField eq \"toSchedule\") {\n my $unlimited=0;\n if (open(FILE, \"< $ENV{HOME}/unlimited_reservation.users\")) {\n while (){\n if (m/^\\s*$user\\s*$/m){\n $unlimited=1;\n }\n }\n close(FILE);\n }\n if ($unlimited > 0) {\n print(\"[ADMISSION RULE] $user is granted the privilege to do unlimited reservations\\n\");\n } else {\n my $max_nb_resa = 2;\n my $nb_resa = $dbh->do(\" SELECT job_id\n FROM jobs\n WHERE\n job_user = \\\'$user\\\' AND\n (reservation = \\\'toSchedule\\\' OR\n reservation = \\\'Scheduled\\\') AND\n (state = \\\'Waiting\\\' OR state = \\\'Hold\\\')\n \");\n if ($nb_resa >= $max_nb_resa){\n die(\"[ADMISSION RULE] Error : you cannot have more than $max_nb_resa waiting reservations.\\n\");\n }\n }\n}\n'),(11,'\nmy $max_walltime = OAR::IO::sql_to_duration(\"12:00:00\");\nif (($jobType eq \"INTERACTIVE\") and ($reservationField eq \"None\")){ \n foreach my $mold (@{$ref_resource_list}){\n if ((defined($mold->[1])) and ($max_walltime < $mold->[1])){\n print(\"[ADMISSION RULE] Walltime to big for an INTERACTIVE job so it is set to $max_walltime.\\n\");\n $mold->[1] = $max_walltime;\n }\n }\n}\n'),(12,'\nmy $default_wall = OAR::IO::sql_to_duration(\"2:00:00\");\nforeach my $mold (@{$ref_resource_list}){\n if (!defined($mold->[1])){\n print(\"[ADMISSION RULE] Set default walltime to $default_wall.\\n\");\n $mold->[1] = $default_wall;\n }\n}\n'),(13,'\nmy @types = (\"container\",\"inner\",\"deploy\",\"desktop_computing\",\"besteffort\",\"cosystem\",\"idempotent\",\"timesharing\",\"allow_classic_ssh\");\nforeach my $t (@{$type_list}){\n my $i = 0;\n while (($types[$i] ne $t) and ($i <= $#types)){\n $i++;\n }\n if (($i > $#types) and ($t !~ /^(timesharing|inner)/)){\n die(\"[ADMISSION RULE] The job type $t is not handled by OAR; Right values are : @types\\n\");\n }\n}\n'),(14,'\nforeach my $mold (@{$ref_resource_list}){\n foreach my $r (@{$mold->[0]}){\n my $prop = $r->{property};\n if (($prop !~ /[\\s\\(]type[\\s=]/) and ($prop !~ /^type[\\s=]/)){\n if (!defined($prop)){\n $r->{property} = \"type = \\\'default\\\'\";\n }else{\n $r->{property} = \"($r->{property}) AND type = \\\'default\\\'\";\n }\n }\n }\n}\nprint(\"[ADMISSION RULE] Modify resource description with type constraints\\n\");\n'); /*!40000 ALTER TABLE `admission_rules` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `assigned_resources` -- DROP TABLE IF EXISTS `assigned_resources`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `assigned_resources` ( `moldable_job_id` int(10) unsigned NOT NULL, `resource_id` int(10) unsigned NOT NULL, `assigned_resource_index` enum('CURRENT','LOG') NOT NULL default 'CURRENT', PRIMARY KEY (`moldable_job_id`,`resource_id`), KEY `mjob_id` (`moldable_job_id`), KEY `log` (`assigned_resource_index`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `assigned_resources` -- LOCK TABLES `assigned_resources` WRITE; /*!40000 ALTER TABLE `assigned_resources` DISABLE KEYS */; INSERT INTO `assigned_resources` VALUES (1,1,'CURRENT'),(1,2,'CURRENT'),(1,3,'CURRENT'),(1,4,'CURRENT'),(1,5,'CURRENT'),(1,6,'CURRENT'),(1,7,'CURRENT'),(1,8,'CURRENT'),(1,9,'CURRENT'),(1,10,'CURRENT'),(1,11,'CURRENT'),(1,12,'CURRENT'),(1,13,'CURRENT'),(1,14,'CURRENT'),(1,15,'CURRENT'),(1,16,'CURRENT'),(1,17,'CURRENT'),(1,18,'CURRENT'),(1,19,'CURRENT'),(1,20,'CURRENT'),(1,21,'CURRENT'),(1,22,'CURRENT'),(1,23,'CURRENT'),(1,24,'CURRENT'),(1,25,'CURRENT'),(1,26,'CURRENT'),(1,27,'CURRENT'),(1,28,'CURRENT'),(1,29,'CURRENT'),(1,30,'CURRENT'),(1,31,'CURRENT'),(1,32,'CURRENT'),(1,33,'CURRENT'),(1,34,'CURRENT'),(1,35,'CURRENT'),(1,36,'CURRENT'),(1,37,'CURRENT'),(1,38,'CURRENT'),(1,39,'CURRENT'),(1,40,'CURRENT'),(1,41,'CURRENT'),(1,42,'CURRENT'),(1,43,'CURRENT'),(1,44,'CURRENT'),(1,45,'CURRENT'),(1,46,'CURRENT'),(1,47,'CURRENT'),(1,48,'CURRENT'),(1,49,'CURRENT'),(1,50,'CURRENT'),(1,51,'CURRENT'),(1,52,'CURRENT'),(1,53,'CURRENT'),(1,54,'CURRENT'),(1,55,'CURRENT'),(1,56,'CURRENT'),(1,57,'CURRENT'),(1,58,'CURRENT'),(1,59,'CURRENT'),(1,60,'CURRENT'),(1,61,'CURRENT'),(1,62,'CURRENT'),(1,63,'CURRENT'),(1,64,'CURRENT'),(1,65,'CURRENT'),(1,66,'CURRENT'),(1,67,'CURRENT'),(1,68,'CURRENT'),(1,69,'CURRENT'),(1,70,'CURRENT'),(1,71,'CURRENT'),(1,72,'CURRENT'),(1,73,'CURRENT'),(1,74,'CURRENT'),(1,75,'CURRENT'),(1,76,'CURRENT'),(1,77,'CURRENT'),(1,78,'CURRENT'),(1,79,'CURRENT'),(1,80,'CURRENT'),(1,81,'CURRENT'),(1,82,'CURRENT'),(1,83,'CURRENT'),(1,84,'CURRENT'),(1,85,'CURRENT'),(1,86,'CURRENT'),(1,87,'CURRENT'),(1,88,'CURRENT'),(1,89,'CURRENT'),(1,90,'CURRENT'),(1,91,'CURRENT'),(1,92,'CURRENT'),(1,93,'CURRENT'),(1,94,'CURRENT'),(1,95,'CURRENT'),(1,96,'CURRENT'),(1,97,'CURRENT'),(1,98,'CURRENT'),(1,99,'CURRENT'),(1,100,'CURRENT'),(1,101,'CURRENT'),(1,102,'CURRENT'),(1,103,'CURRENT'),(1,104,'CURRENT'),(1,105,'CURRENT'),(1,106,'CURRENT'),(1,107,'CURRENT'),(1,108,'CURRENT'),(1,109,'CURRENT'),(1,110,'CURRENT'),(1,111,'CURRENT'),(1,112,'CURRENT'),(1,113,'CURRENT'),(1,114,'CURRENT'),(1,115,'CURRENT'),(1,116,'CURRENT'),(1,117,'CURRENT'),(1,118,'CURRENT'),(1,119,'CURRENT'),(1,120,'CURRENT'),(1,121,'CURRENT'),(1,122,'CURRENT'),(1,123,'CURRENT'),(1,124,'CURRENT'),(1,125,'CURRENT'),(1,126,'CURRENT'),(1,127,'CURRENT'),(1,128,'CURRENT'),(1,129,'CURRENT'),(1,130,'CURRENT'),(1,131,'CURRENT'),(1,132,'CURRENT'),(1,133,'CURRENT'),(1,134,'CURRENT'),(1,135,'CURRENT'),(1,136,'CURRENT'),(1,137,'CURRENT'),(1,138,'CURRENT'),(1,139,'CURRENT'),(1,140,'CURRENT'),(1,141,'CURRENT'),(1,142,'CURRENT'),(1,143,'CURRENT'),(1,144,'CURRENT'),(1,145,'CURRENT'),(1,146,'CURRENT'),(1,147,'CURRENT'),(1,148,'CURRENT'),(1,149,'CURRENT'),(1,150,'CURRENT'),(1,151,'CURRENT'),(1,152,'CURRENT'),(1,153,'CURRENT'),(1,154,'CURRENT'),(1,155,'CURRENT'),(1,156,'CURRENT'),(1,157,'CURRENT'),(1,158,'CURRENT'),(1,159,'CURRENT'),(1,160,'CURRENT'),(1,161,'CURRENT'),(1,162,'CURRENT'),(1,163,'CURRENT'),(1,164,'CURRENT'),(1,165,'CURRENT'),(1,166,'CURRENT'),(1,167,'CURRENT'),(1,168,'CURRENT'),(1,169,'CURRENT'),(1,170,'CURRENT'),(1,171,'CURRENT'),(1,172,'CURRENT'),(1,173,'CURRENT'),(1,174,'CURRENT'),(1,175,'CURRENT'),(1,176,'CURRENT'),(1,177,'CURRENT'),(1,178,'CURRENT'),(1,179,'CURRENT'),(1,180,'CURRENT'),(1,181,'CURRENT'),(1,182,'CURRENT'),(1,183,'CURRENT'),(1,184,'CURRENT'),(1,185,'CURRENT'),(1,186,'CURRENT'),(1,187,'CURRENT'),(1,188,'CURRENT'),(1,189,'CURRENT'),(1,190,'CURRENT'),(1,191,'CURRENT'),(1,192,'CURRENT'),(1,193,'CURRENT'),(1,194,'CURRENT'),(1,195,'CURRENT'),(1,196,'CURRENT'),(1,197,'CURRENT'),(1,198,'CURRENT'),(1,199,'CURRENT'),(1,200,'CURRENT'),(1,201,'CURRENT'),(1,202,'CURRENT'),(1,203,'CURRENT'),(1,204,'CURRENT'),(1,205,'CURRENT'),(1,206,'CURRENT'),(1,207,'CURRENT'),(1,208,'CURRENT'),(1,209,'CURRENT'),(1,210,'CURRENT'),(1,211,'CURRENT'),(1,212,'CURRENT'),(1,213,'CURRENT'),(1,214,'CURRENT'),(1,215,'CURRENT'),(1,216,'CURRENT'),(1,217,'CURRENT'),(1,218,'CURRENT'),(1,219,'CURRENT'),(1,220,'CURRENT'),(1,221,'CURRENT'),(1,222,'CURRENT'),(1,223,'CURRENT'),(1,224,'CURRENT'),(1,225,'CURRENT'),(1,226,'CURRENT'),(1,227,'CURRENT'),(1,228,'CURRENT'),(1,229,'CURRENT'),(1,230,'CURRENT'),(1,231,'CURRENT'),(1,232,'CURRENT'),(1,233,'CURRENT'),(1,234,'CURRENT'),(1,235,'CURRENT'),(1,236,'CURRENT'),(1,237,'CURRENT'),(1,238,'CURRENT'),(1,239,'CURRENT'),(1,240,'CURRENT'),(1,241,'CURRENT'),(1,242,'CURRENT'),(1,243,'CURRENT'),(1,244,'CURRENT'),(1,245,'CURRENT'),(1,246,'CURRENT'),(1,247,'CURRENT'),(1,248,'CURRENT'),(1,249,'CURRENT'),(1,250,'CURRENT'),(1,251,'CURRENT'),(1,252,'CURRENT'),(1,253,'CURRENT'),(1,254,'CURRENT'),(1,255,'CURRENT'),(1,256,'CURRENT'),(1,257,'CURRENT'),(1,258,'CURRENT'),(1,259,'CURRENT'),(1,260,'CURRENT'),(1,261,'CURRENT'),(1,262,'CURRENT'),(1,263,'CURRENT'),(1,264,'CURRENT'),(1,265,'CURRENT'),(1,266,'CURRENT'),(1,267,'CURRENT'),(1,268,'CURRENT'),(1,269,'CURRENT'),(1,270,'CURRENT'),(1,271,'CURRENT'),(1,272,'CURRENT'),(1,273,'CURRENT'),(1,274,'CURRENT'),(1,275,'CURRENT'),(1,276,'CURRENT'),(1,277,'CURRENT'),(1,278,'CURRENT'),(1,279,'CURRENT'),(1,280,'CURRENT'),(1,281,'CURRENT'),(1,282,'CURRENT'),(1,283,'CURRENT'),(1,284,'CURRENT'),(1,285,'CURRENT'),(1,286,'CURRENT'),(1,287,'CURRENT'),(1,288,'CURRENT'),(1,289,'CURRENT'),(1,290,'CURRENT'),(1,291,'CURRENT'),(1,292,'CURRENT'),(1,293,'CURRENT'),(1,294,'CURRENT'),(1,295,'CURRENT'),(1,296,'CURRENT'),(1,297,'CURRENT'),(1,298,'CURRENT'),(1,299,'CURRENT'),(1,300,'CURRENT'),(1,301,'CURRENT'),(1,302,'CURRENT'),(1,303,'CURRENT'),(1,304,'CURRENT'),(1,305,'CURRENT'),(1,306,'CURRENT'),(1,307,'CURRENT'),(1,308,'CURRENT'),(1,309,'CURRENT'),(1,310,'CURRENT'),(1,311,'CURRENT'),(1,312,'CURRENT'),(1,313,'CURRENT'),(1,314,'CURRENT'),(1,315,'CURRENT'),(1,316,'CURRENT'),(1,317,'CURRENT'),(1,318,'CURRENT'),(1,319,'CURRENT'),(1,320,'CURRENT'),(1,321,'CURRENT'),(1,322,'CURRENT'),(1,323,'CURRENT'),(1,324,'CURRENT'),(1,325,'CURRENT'),(1,326,'CURRENT'),(1,327,'CURRENT'),(1,328,'CURRENT'),(1,329,'CURRENT'),(1,330,'CURRENT'),(1,331,'CURRENT'),(1,332,'CURRENT'),(1,333,'CURRENT'),(1,334,'CURRENT'),(1,335,'CURRENT'),(1,336,'CURRENT'),(1,337,'CURRENT'),(1,338,'CURRENT'),(1,339,'CURRENT'),(1,340,'CURRENT'),(1,341,'CURRENT'),(1,342,'CURRENT'),(1,343,'CURRENT'),(1,344,'CURRENT'),(1,345,'CURRENT'),(1,346,'CURRENT'),(1,347,'CURRENT'),(1,348,'CURRENT'),(1,349,'CURRENT'),(1,350,'CURRENT'),(1,351,'CURRENT'),(1,352,'CURRENT'),(1,353,'CURRENT'),(1,354,'CURRENT'),(1,355,'CURRENT'),(1,356,'CURRENT'),(1,357,'CURRENT'),(1,358,'CURRENT'),(1,359,'CURRENT'),(1,360,'CURRENT'),(1,361,'CURRENT'),(1,362,'CURRENT'),(1,363,'CURRENT'),(1,364,'CURRENT'),(1,365,'CURRENT'),(1,366,'CURRENT'),(1,367,'CURRENT'),(1,368,'CURRENT'),(1,369,'CURRENT'),(1,370,'CURRENT'),(1,371,'CURRENT'),(1,372,'CURRENT'),(1,373,'CURRENT'),(1,374,'CURRENT'),(1,375,'CURRENT'),(1,376,'CURRENT'),(1,377,'CURRENT'),(1,378,'CURRENT'),(1,379,'CURRENT'),(1,380,'CURRENT'),(1,381,'CURRENT'),(1,382,'CURRENT'),(1,383,'CURRENT'),(1,384,'CURRENT'); /*!40000 ALTER TABLE `assigned_resources` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `challenges` -- DROP TABLE IF EXISTS `challenges`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `challenges` ( `job_id` int(10) unsigned NOT NULL, `challenge` varchar(255) NOT NULL, `ssh_private_key` text NOT NULL, `ssh_public_key` text NOT NULL, PRIMARY KEY (`job_id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `challenges` -- LOCK TABLES `challenges` WRITE; /*!40000 ALTER TABLE `challenges` DISABLE KEYS */; INSERT INTO `challenges` VALUES (1,'985377625403','',''),(2,'667427831868','',''),(3,'328575936452','',''),(4,'526436358531','',''),(5,'339450127053','',''),(6,'352902324711','',''),(7,'439787935539','',''),(8,'433177980900','',''),(9,'412158820386','',''),(10,'889567426013','',''),(11,'123787477918','',''),(12,'437301955259','',''),(13,'687474423394','',''),(14,'746288048177','',''),(15,'14582702730','',''),(16,'997663565985','',''),(17,'674879145649','',''),(18,'690024722441','',''),(19,'16737794344','',''),(20,'321956478365','',''),(21,'198102593728','',''),(22,'567295823087','',''),(23,'347901824469','',''),(24,'358981637823','',''),(25,'963363095260','',''),(26,'312087497049','',''),(27,'529815651084','',''),(28,'741712139294','',''),(29,'722662389119','',''),(30,'456581414983','',''),(31,'564700135802','',''),(32,'930923546039','',''),(33,'618351217432','',''),(34,'253933886838','',''),(35,'919951469012','',''),(36,'631391253628','',''),(37,'479817063155','',''),(38,'113409831727','',''),(39,'460256586390','',''),(40,'888637919751','',''),(41,'612698466823','',''),(42,'244960600118','',''),(43,'986237258258','',''),(44,'764762977211','',''),(45,'522188644464','',''),(46,'557689930153','',''),(47,'482398586654','',''),(48,'426850456119','',''),(49,'445779774178','',''),(50,'382228959891','',''),(51,'306016431962','',''),(52,'496333322328','',''),(53,'936094115561','',''),(54,'950104920267','',''),(55,'210750320534','',''),(56,'760070355682','',''),(57,'34628360140','',''),(58,'45393074936','',''),(59,'778923778169','',''),(60,'416225297915','',''),(61,'540753317139','',''),(62,'822269853045','',''),(63,'794077736864','',''),(64,'199294504317','',''),(65,'383396045145','',''),(66,'919667708761','',''),(67,'387379707609','',''),(68,'666955499284','',''),(69,'429983211987','',''),(70,'266966335083','',''),(71,'67774423238','',''),(72,'379797035436','',''),(73,'632539517319','',''),(74,'718921154345','',''),(75,'281791602102','',''),(76,'760220841345','',''),(77,'634989993487','',''),(78,'727849559000','',''),(79,'952572563271','',''),(80,'144459665128','',''),(81,'526982657917','',''),(82,'504834232227','',''),(83,'332923251510','',''),(84,'943013631579','',''),(85,'930543974814','',''),(86,'625359243786','',''),(87,'3119980291','',''),(88,'276668661347','',''),(89,'323375521301','',''),(90,'123581744804','',''),(91,'460708778890','',''),(92,'639891122774','',''),(93,'329620894118','',''),(94,'28464907388','',''),(95,'135332705526','',''),(96,'396811226922','',''),(97,'645932971806','',''),(98,'576291043152','',''),(99,'151294307624','',''),(100,'30598331782','',''),(101,'27490717898','',''); /*!40000 ALTER TABLE `challenges` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `event_log_hostnames` -- DROP TABLE IF EXISTS `event_log_hostnames`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `event_log_hostnames` ( `event_id` int(10) unsigned NOT NULL, `hostname` varchar(255) NOT NULL, PRIMARY KEY (`event_id`,`hostname`), KEY `event_hostname` (`hostname`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `event_log_hostnames` -- LOCK TABLES `event_log_hostnames` WRITE; /*!40000 ALTER TABLE `event_log_hostnames` DISABLE KEYS */; /*!40000 ALTER TABLE `event_log_hostnames` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `event_logs` -- DROP TABLE IF EXISTS `event_logs`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `event_logs` ( `event_id` int(10) unsigned NOT NULL auto_increment, `type` varchar(50) NOT NULL, `job_id` int(10) unsigned NOT NULL, `date` int(10) unsigned NOT NULL, `description` varchar(255) NOT NULL, `to_check` enum('YES','NO') NOT NULL default 'YES', PRIMARY KEY (`event_id`), KEY `event_type` (`type`), KEY `event_check` (`to_check`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `event_logs` -- LOCK TABLES `event_logs` WRITE; /*!40000 ALTER TABLE `event_logs` DISABLE KEYS */; /*!40000 ALTER TABLE `event_logs` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `files` -- DROP TABLE IF EXISTS `files`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `files` ( `file_id` int(10) unsigned NOT NULL auto_increment, `md5sum` varchar(255) default NULL, `location` varchar(255) default NULL, `method` varchar(255) default NULL, `compression` varchar(255) default NULL, `size` int(10) unsigned NOT NULL, PRIMARY KEY (`file_id`), KEY `md5sum` (`md5sum`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `files` -- LOCK TABLES `files` WRITE; /*!40000 ALTER TABLE `files` DISABLE KEYS */; /*!40000 ALTER TABLE `files` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `frag_jobs` -- DROP TABLE IF EXISTS `frag_jobs`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `frag_jobs` ( `frag_id_job` int(10) unsigned NOT NULL, `frag_date` int(10) unsigned NOT NULL, `frag_state` enum('LEON','TIMER_ARMED','LEON_EXTERMINATE','FRAGGED') NOT NULL default 'LEON', PRIMARY KEY (`frag_id_job`), KEY `frag_state` (`frag_state`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `frag_jobs` -- LOCK TABLES `frag_jobs` WRITE; /*!40000 ALTER TABLE `frag_jobs` DISABLE KEYS */; /*!40000 ALTER TABLE `frag_jobs` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `gantt_jobs_predictions` -- DROP TABLE IF EXISTS `gantt_jobs_predictions`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `gantt_jobs_predictions` ( `moldable_job_id` int(10) unsigned NOT NULL, `start_time` int(10) unsigned NOT NULL, PRIMARY KEY (`moldable_job_id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `gantt_jobs_predictions` -- LOCK TABLES `gantt_jobs_predictions` WRITE; /*!40000 ALTER TABLE `gantt_jobs_predictions` DISABLE KEYS */; INSERT INTO `gantt_jobs_predictions` VALUES (0,1224778714),(1,1224778628),(2,1224793089),(3,1224825550),(4,1224843611),(5,1224879672),(6,1224901333),(7,1224908594),(8,1224937455),(9,1224793089),(10,1224948316),(11,1224793089),(12,1224966377),(13,1224793089),(14,1224988038),(15,1224793089),(16,1225016899),(17,1224800350); /*!40000 ALTER TABLE `gantt_jobs_predictions` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `gantt_jobs_predictions_log` -- DROP TABLE IF EXISTS `gantt_jobs_predictions_log`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `gantt_jobs_predictions_log` ( `sched_date` int(10) unsigned NOT NULL, `moldable_job_id` int(10) unsigned NOT NULL, `start_time` int(10) unsigned NOT NULL, PRIMARY KEY (`sched_date`,`moldable_job_id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `gantt_jobs_predictions_log` -- LOCK TABLES `gantt_jobs_predictions_log` WRITE; /*!40000 ALTER TABLE `gantt_jobs_predictions_log` DISABLE KEYS */; /*!40000 ALTER TABLE `gantt_jobs_predictions_log` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `gantt_jobs_predictions_visu` -- DROP TABLE IF EXISTS `gantt_jobs_predictions_visu`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `gantt_jobs_predictions_visu` ( `moldable_job_id` int(10) unsigned NOT NULL, `start_time` int(10) unsigned NOT NULL, PRIMARY KEY (`moldable_job_id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `gantt_jobs_predictions_visu` -- LOCK TABLES `gantt_jobs_predictions_visu` WRITE; /*!40000 ALTER TABLE `gantt_jobs_predictions_visu` DISABLE KEYS */; INSERT INTO `gantt_jobs_predictions_visu` VALUES (0,1224778679),(1,1224778628),(2,1224793089),(3,1224825550),(4,1224843611),(5,1224879672),(6,1224901333),(7,1224908594),(8,1224937455),(9,1224793089),(10,1224948316),(11,1224793089),(12,1224966377),(13,1224793089),(14,1224988038),(15,1224793089),(16,1225016899),(17,1224800350),(18,1225049360),(19,1224843611); /*!40000 ALTER TABLE `gantt_jobs_predictions_visu` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `gantt_jobs_resources` -- DROP TABLE IF EXISTS `gantt_jobs_resources`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `gantt_jobs_resources` ( `moldable_job_id` int(10) unsigned NOT NULL, `resource_id` int(10) unsigned NOT NULL, PRIMARY KEY (`moldable_job_id`,`resource_id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `gantt_jobs_resources` -- LOCK TABLES `gantt_jobs_resources` WRITE; /*!40000 ALTER TABLE `gantt_jobs_resources` DISABLE KEYS */; INSERT INTO `gantt_jobs_resources` VALUES (1,1),(1,2),(1,3),(1,4),(1,5),(1,6),(1,7),(1,8),(1,9),(1,10),(1,11),(1,12),(1,13),(1,14),(1,15),(1,16),(1,17),(1,18),(1,19),(1,20),(1,21),(1,22),(1,23),(1,24),(1,25),(1,26),(1,27),(1,28),(1,29),(1,30),(1,31),(1,32),(1,33),(1,34),(1,35),(1,36),(1,37),(1,38),(1,39),(1,40),(1,41),(1,42),(1,43),(1,44),(1,45),(1,46),(1,47),(1,48),(1,49),(1,50),(1,51),(1,52),(1,53),(1,54),(1,55),(1,56),(1,57),(1,58),(1,59),(1,60),(1,61),(1,62),(1,63),(1,64),(1,65),(1,66),(1,67),(1,68),(1,69),(1,70),(1,71),(1,72),(1,73),(1,74),(1,75),(1,76),(1,77),(1,78),(1,79),(1,80),(1,81),(1,82),(1,83),(1,84),(1,85),(1,86),(1,87),(1,88),(1,89),(1,90),(1,91),(1,92),(1,93),(1,94),(1,95),(1,96),(1,97),(1,98),(1,99),(1,100),(1,101),(1,102),(1,103),(1,104),(1,105),(1,106),(1,107),(1,108),(1,109),(1,110),(1,111),(1,112),(1,113),(1,114),(1,115),(1,116),(1,117),(1,118),(1,119),(1,120),(1,121),(1,122),(1,123),(1,124),(1,125),(1,126),(1,127),(1,128),(1,129),(1,130),(1,131),(1,132),(1,133),(1,134),(1,135),(1,136),(1,137),(1,138),(1,139),(1,140),(1,141),(1,142),(1,143),(1,144),(1,145),(1,146),(1,147),(1,148),(1,149),(1,150),(1,151),(1,152),(1,153),(1,154),(1,155),(1,156),(1,157),(1,158),(1,159),(1,160),(1,161),(1,162),(1,163),(1,164),(1,165),(1,166),(1,167),(1,168),(1,169),(1,170),(1,171),(1,172),(1,173),(1,174),(1,175),(1,176),(1,177),(1,178),(1,179),(1,180),(1,181),(1,182),(1,183),(1,184),(1,185),(1,186),(1,187),(1,188),(1,189),(1,190),(1,191),(1,192),(1,193),(1,194),(1,195),(1,196),(1,197),(1,198),(1,199),(1,200),(1,201),(1,202),(1,203),(1,204),(1,205),(1,206),(1,207),(1,208),(1,209),(1,210),(1,211),(1,212),(1,213),(1,214),(1,215),(1,216),(1,217),(1,218),(1,219),(1,220),(1,221),(1,222),(1,223),(1,224),(1,225),(1,226),(1,227),(1,228),(1,229),(1,230),(1,231),(1,232),(1,233),(1,234),(1,235),(1,236),(1,237),(1,238),(1,239),(1,240),(1,241),(1,242),(1,243),(1,244),(1,245),(1,246),(1,247),(1,248),(1,249),(1,250),(1,251),(1,252),(1,253),(1,254),(1,255),(1,256),(1,257),(1,258),(1,259),(1,260),(1,261),(1,262),(1,263),(1,264),(1,265),(1,266),(1,267),(1,268),(1,269),(1,270),(1,271),(1,272),(1,273),(1,274),(1,275),(1,276),(1,277),(1,278),(1,279),(1,280),(1,281),(1,282),(1,283),(1,284),(1,285),(1,286),(1,287),(1,288),(1,289),(1,290),(1,291),(1,292),(1,293),(1,294),(1,295),(1,296),(1,297),(1,298),(1,299),(1,300),(1,301),(1,302),(1,303),(1,304),(1,305),(1,306),(1,307),(1,308),(1,309),(1,310),(1,311),(1,312),(1,313),(1,314),(1,315),(1,316),(1,317),(1,318),(1,319),(1,320),(1,321),(1,322),(1,323),(1,324),(1,325),(1,326),(1,327),(1,328),(1,329),(1,330),(1,331),(1,332),(1,333),(1,334),(1,335),(1,336),(1,337),(1,338),(1,339),(1,340),(1,341),(1,342),(1,343),(1,344),(1,345),(1,346),(1,347),(1,348),(1,349),(1,350),(1,351),(1,352),(1,353),(1,354),(1,355),(1,356),(1,357),(1,358),(1,359),(1,360),(1,361),(1,362),(1,363),(1,364),(1,365),(1,366),(1,367),(1,368),(1,369),(1,370),(1,371),(1,372),(1,373),(1,374),(1,375),(1,376),(1,377),(1,378),(1,379),(1,380),(1,381),(1,382),(1,383),(1,384),(2,21),(2,22),(2,23),(2,24),(2,25),(2,26),(2,27),(2,28),(2,29),(2,30),(2,31),(2,32),(2,33),(2,34),(2,35),(2,36),(2,209),(2,210),(2,211),(2,212),(2,213),(2,214),(2,215),(2,216),(2,217),(2,218),(2,219),(2,220),(2,221),(2,222),(2,223),(2,224),(2,225),(2,226),(2,227),(2,228),(2,229),(2,230),(2,231),(2,232),(2,233),(2,234),(2,235),(2,236),(2,237),(2,238),(2,239),(2,240),(2,241),(2,242),(2,243),(2,244),(2,245),(2,246),(2,247),(2,248),(2,249),(2,250),(2,251),(2,252),(2,253),(2,254),(2,255),(2,256),(2,257),(2,258),(2,259),(2,260),(2,261),(2,262),(2,263),(2,264),(2,265),(2,266),(2,267),(2,268),(2,269),(2,270),(2,271),(2,272),(2,273),(2,274),(2,275),(2,276),(2,277),(2,278),(2,279),(2,280),(2,281),(2,282),(2,283),(2,284),(2,285),(2,286),(2,287),(2,288),(2,289),(2,290),(2,291),(2,292),(2,293),(2,294),(2,295),(2,296),(2,297),(2,298),(2,299),(2,300),(2,301),(2,302),(2,303),(2,304),(2,305),(2,306),(2,307),(2,308),(2,309),(2,310),(2,311),(2,312),(2,313),(2,314),(2,315),(2,316),(2,317),(2,318),(2,319),(2,320),(2,321),(2,322),(2,323),(2,324),(2,325),(2,326),(2,327),(2,328),(2,329),(2,330),(2,331),(2,332),(2,333),(2,334),(2,335),(2,336),(2,337),(2,338),(2,339),(2,340),(2,341),(2,342),(2,343),(2,344),(2,345),(2,346),(2,347),(2,348),(2,349),(2,350),(2,351),(2,352),(2,353),(2,354),(2,355),(2,356),(2,357),(2,358),(2,359),(2,360),(2,361),(2,362),(2,363),(2,364),(2,365),(2,366),(2,367),(2,368),(2,369),(2,370),(2,371),(2,372),(2,373),(2,374),(2,375),(2,376),(2,377),(2,378),(2,379),(2,380),(2,381),(2,382),(2,383),(2,384),(3,9),(3,10),(3,11),(3,12),(3,13),(3,14),(3,15),(3,16),(3,17),(3,18),(3,19),(3,20),(3,21),(3,22),(3,23),(3,24),(3,25),(3,26),(3,27),(3,28),(3,29),(3,30),(3,31),(3,32),(3,33),(3,34),(3,35),(3,36),(3,81),(3,82),(3,83),(3,84),(3,85),(3,86),(3,87),(3,88),(3,89),(3,90),(3,91),(3,92),(3,93),(3,94),(3,95),(3,96),(3,97),(3,98),(3,99),(3,100),(3,101),(3,102),(3,103),(3,104),(3,105),(3,106),(3,107),(3,108),(3,109),(3,110),(3,111),(3,112),(3,113),(3,114),(3,115),(3,116),(3,117),(3,118),(3,119),(3,120),(3,121),(3,122),(3,123),(3,124),(3,125),(3,126),(3,127),(3,128),(3,129),(3,130),(3,131),(3,132),(3,133),(3,134),(3,135),(3,136),(3,137),(3,138),(3,139),(3,140),(3,141),(3,142),(3,143),(3,144),(3,145),(3,146),(3,147),(3,148),(3,149),(3,150),(3,151),(3,152),(3,153),(3,154),(3,155),(3,156),(3,157),(3,158),(3,159),(3,160),(3,161),(3,162),(3,163),(3,164),(3,165),(3,166),(3,167),(3,168),(3,169),(3,170),(3,171),(3,172),(3,173),(3,174),(3,175),(3,176),(3,177),(3,178),(3,179),(3,180),(3,181),(3,182),(3,183),(3,184),(3,185),(3,186),(3,187),(3,188),(3,189),(3,190),(3,191),(3,192),(3,193),(3,194),(3,195),(3,196),(3,197),(3,198),(3,199),(3,200),(3,201),(3,202),(3,203),(3,204),(3,205),(3,206),(3,207),(3,208),(3,209),(3,210),(3,211),(3,212),(3,213),(3,214),(3,215),(3,216),(3,217),(3,218),(3,219),(3,220),(3,221),(3,222),(3,223),(3,224),(3,225),(3,226),(3,227),(3,228),(3,229),(3,230),(3,231),(3,232),(3,233),(3,234),(3,235),(3,236),(3,237),(3,238),(3,239),(3,240),(3,241),(3,242),(3,243),(3,244),(3,245),(3,246),(3,247),(3,248),(3,249),(3,250),(3,251),(3,252),(3,253),(3,254),(3,255),(3,256),(3,257),(3,258),(3,259),(3,260),(3,261),(3,262),(3,263),(3,264),(3,265),(3,266),(3,267),(3,268),(3,269),(3,270),(3,271),(3,272),(3,273),(3,274),(3,275),(3,276),(3,277),(3,278),(3,279),(3,280),(3,281),(3,282),(3,283),(3,284),(3,285),(3,286),(3,287),(3,288),(3,289),(3,290),(3,291),(3,292),(3,293),(3,294),(3,295),(3,296),(3,297),(3,298),(3,299),(3,300),(3,301),(3,302),(3,303),(3,304),(3,305),(3,306),(3,307),(3,308),(3,309),(3,310),(3,311),(3,312),(3,313),(3,314),(3,315),(3,316),(3,317),(3,318),(3,319),(3,320),(3,321),(3,322),(3,323),(3,324),(3,325),(3,326),(3,327),(3,328),(3,329),(3,330),(3,331),(3,332),(3,333),(3,334),(3,335),(3,336),(3,337),(3,338),(3,339),(3,340),(3,341),(3,342),(3,343),(3,344),(3,345),(3,346),(3,347),(3,348),(3,349),(3,350),(3,351),(3,352),(3,353),(3,354),(3,355),(3,356),(3,357),(3,358),(3,359),(3,360),(3,361),(3,362),(3,363),(3,364),(3,365),(3,366),(3,367),(3,368),(3,369),(3,370),(3,371),(3,372),(3,373),(3,374),(3,375),(3,376),(3,377),(3,378),(3,379),(3,380),(3,381),(3,382),(3,383),(3,384),(4,17),(4,18),(4,19),(4,20),(4,21),(4,22),(4,23),(4,24),(4,25),(4,26),(4,27),(4,28),(4,29),(4,30),(4,31),(4,32),(4,33),(4,34),(4,35),(4,36),(4,189),(4,190),(4,191),(4,192),(4,193),(4,194),(4,195),(4,196),(4,197),(4,198),(4,199),(4,200),(4,201),(4,202),(4,203),(4,204),(4,205),(4,206),(4,207),(4,208),(4,209),(4,210),(4,211),(4,212),(4,213),(4,214),(4,215),(4,216),(4,217),(4,218),(4,219),(4,220),(4,221),(4,222),(4,223),(4,224),(4,225),(4,226),(4,227),(4,228),(4,229),(4,230),(4,231),(4,232),(4,233),(4,234),(4,235),(4,236),(4,237),(4,238),(4,239),(4,240),(4,241),(4,242),(4,243),(4,244),(4,245),(4,246),(4,247),(4,248),(4,249),(4,250),(4,251),(4,252),(4,253),(4,254),(4,255),(4,256),(4,257),(4,258),(4,259),(4,260),(4,261),(4,262),(4,263),(4,264),(4,265),(4,266),(4,267),(4,268),(4,269),(4,270),(4,271),(4,272),(4,273),(4,274),(4,275),(4,276),(4,277),(4,278),(4,279),(4,280),(4,281),(4,282),(4,283),(4,284),(4,285),(4,286),(4,287),(4,288),(4,289),(4,290),(4,291),(4,292),(4,293),(4,294),(4,295),(4,296),(4,297),(4,298),(4,299),(4,300),(4,301),(4,302),(4,303),(4,304),(4,305),(4,306),(4,307),(4,308),(4,309),(4,310),(4,311),(4,312),(4,313),(4,314),(4,315),(4,316),(4,317),(4,318),(4,319),(4,320),(4,321),(4,322),(4,323),(4,324),(4,325),(4,326),(4,327),(4,328),(4,329),(4,330),(4,331),(4,332),(4,333),(4,334),(4,335),(4,336),(4,337),(4,338),(4,339),(4,340),(4,341),(4,342),(4,343),(4,344),(4,345),(4,346),(4,347),(4,348),(4,349),(4,350),(4,351),(4,352),(4,353),(4,354),(4,355),(4,356),(4,357),(4,358),(4,359),(4,360),(4,361),(4,362),(4,363),(4,364),(4,365),(4,366),(4,367),(4,368),(4,369),(4,370),(4,371),(4,372),(4,373),(4,374),(4,375),(4,376),(4,377),(4,378),(4,379),(4,380),(4,381),(4,382),(4,383),(4,384),(5,5),(5,6),(5,7),(5,8),(5,9),(5,10),(5,11),(5,12),(5,13),(5,14),(5,15),(5,16),(5,17),(5,18),(5,19),(5,20),(5,21),(5,22),(5,23),(5,24),(5,25),(5,26),(5,27),(5,28),(5,29),(5,30),(5,31),(5,32),(5,33),(5,34),(5,35),(5,36),(5,65),(5,66),(5,67),(5,68),(5,69),(5,70),(5,71),(5,72),(5,73),(5,74),(5,75),(5,76),(5,77),(5,78),(5,79),(5,80),(5,81),(5,82),(5,83),(5,84),(5,85),(5,86),(5,87),(5,88),(5,89),(5,90),(5,91),(5,92),(5,93),(5,94),(5,95),(5,96),(5,97),(5,98),(5,99),(5,100),(5,101),(5,102),(5,103),(5,104),(5,105),(5,106),(5,107),(5,108),(5,109),(5,110),(5,111),(5,112),(5,113),(5,114),(5,115),(5,116),(5,117),(5,118),(5,119),(5,120),(5,121),(5,122),(5,123),(5,124),(5,125),(5,126),(5,127),(5,128),(5,129),(5,130),(5,131),(5,132),(5,133),(5,134),(5,135),(5,136),(5,137),(5,138),(5,139),(5,140),(5,141),(5,142),(5,143),(5,144),(5,145),(5,146),(5,147),(5,148),(5,149),(5,150),(5,151),(5,152),(5,153),(5,154),(5,155),(5,156),(5,157),(5,158),(5,159),(5,160),(5,161),(5,162),(5,163),(5,164),(5,165),(5,166),(5,167),(5,168),(5,169),(5,170),(5,171),(5,172),(5,173),(5,174),(5,175),(5,176),(5,177),(5,178),(5,179),(5,180),(5,181),(5,182),(5,183),(5,184),(5,185),(5,186),(5,187),(5,188),(5,189),(5,190),(5,191),(5,192),(5,193),(5,194),(5,195),(5,196),(5,197),(5,198),(5,199),(5,200),(5,201),(5,202),(5,203),(5,204),(5,205),(5,206),(5,207),(5,208),(5,209),(5,210),(5,211),(5,212),(5,213),(5,214),(5,215),(5,216),(5,217),(5,218),(5,219),(5,220),(5,221),(5,222),(5,223),(5,224),(5,225),(5,226),(5,227),(5,228),(5,229),(5,230),(5,231),(5,232),(5,233),(5,234),(5,235),(5,236),(5,237),(5,238),(5,239),(5,240),(5,241),(5,242),(5,243),(5,244),(5,245),(5,246),(5,247),(5,248),(5,249),(5,250),(5,251),(5,252),(5,253),(5,254),(5,255),(5,256),(5,257),(5,258),(5,259),(5,260),(5,261),(5,262),(5,263),(5,264),(5,265),(5,266),(5,267),(5,268),(5,269),(5,270),(5,271),(5,272),(5,273),(5,274),(5,275),(5,276),(5,277),(5,278),(5,279),(5,280),(5,281),(5,282),(5,283),(5,284),(5,285),(5,286),(5,287),(5,288),(5,289),(5,290),(5,291),(5,292),(5,293),(5,294),(5,295),(5,296),(5,297),(5,298),(5,299),(5,300),(5,301),(5,302),(5,303),(5,304),(5,305),(5,306),(5,307),(5,308),(5,309),(5,310),(5,311),(5,312),(5,313),(5,314),(5,315),(5,316),(5,317),(5,318),(5,319),(5,320),(5,321),(5,322),(5,323),(5,324),(5,325),(5,326),(5,327),(5,328),(5,329),(5,330),(5,331),(5,332),(5,333),(5,334),(5,335),(5,336),(5,337),(5,338),(5,339),(5,340),(5,341),(5,342),(5,343),(5,344),(5,345),(5,346),(5,347),(5,348),(5,349),(5,350),(5,351),(5,352),(5,353),(5,354),(5,355),(5,356),(5,357),(5,358),(5,359),(5,360),(5,361),(5,362),(5,363),(5,364),(5,365),(5,366),(5,367),(5,368),(5,369),(5,370),(5,371),(5,372),(5,373),(5,374),(5,375),(5,376),(5,377),(5,378),(5,379),(5,380),(5,381),(5,382),(5,383),(5,384),(6,17),(6,18),(6,19),(6,20),(6,21),(6,22),(6,23),(6,24),(6,25),(6,26),(6,27),(6,28),(6,29),(6,30),(6,31),(6,32),(6,33),(6,34),(6,35),(6,36),(6,169),(6,170),(6,171),(6,172),(6,173),(6,174),(6,175),(6,176),(6,177),(6,178),(6,179),(6,180),(6,181),(6,182),(6,183),(6,184),(6,185),(6,186),(6,187),(6,188),(6,189),(6,190),(6,191),(6,192),(6,193),(6,194),(6,195),(6,196),(6,197),(6,198),(6,199),(6,200),(6,201),(6,202),(6,203),(6,204),(6,205),(6,206),(6,207),(6,208),(6,209),(6,210),(6,211),(6,212),(6,213),(6,214),(6,215),(6,216),(6,217),(6,218),(6,219),(6,220),(6,221),(6,222),(6,223),(6,224),(6,225),(6,226),(6,227),(6,228),(6,229),(6,230),(6,231),(6,232),(6,233),(6,234),(6,235),(6,236),(6,237),(6,238),(6,239),(6,240),(6,241),(6,242),(6,243),(6,244),(6,245),(6,246),(6,247),(6,248),(6,249),(6,250),(6,251),(6,252),(6,253),(6,254),(6,255),(6,256),(6,257),(6,258),(6,259),(6,260),(6,261),(6,262),(6,263),(6,264),(6,265),(6,266),(6,267),(6,268),(6,269),(6,270),(6,271),(6,272),(6,273),(6,274),(6,275),(6,276),(6,277),(6,278),(6,279),(6,280),(6,281),(6,282),(6,283),(6,284),(6,285),(6,286),(6,287),(6,288),(6,289),(6,290),(6,291),(6,292),(6,293),(6,294),(6,295),(6,296),(6,297),(6,298),(6,299),(6,300),(6,301),(6,302),(6,303),(6,304),(6,305),(6,306),(6,307),(6,308),(6,309),(6,310),(6,311),(6,312),(6,313),(6,314),(6,315),(6,316),(6,317),(6,318),(6,319),(6,320),(6,321),(6,322),(6,323),(6,324),(6,325),(6,326),(6,327),(6,328),(6,329),(6,330),(6,331),(6,332),(6,333),(6,334),(6,335),(6,336),(6,337),(6,338),(6,339),(6,340),(6,341),(6,342),(6,343),(6,344),(6,345),(6,346),(6,347),(6,348),(6,349),(6,350),(6,351),(6,352),(6,353),(6,354),(6,355),(6,356),(6,357),(6,358),(6,359),(6,360),(6,361),(6,362),(6,363),(6,364),(6,365),(6,366),(6,367),(6,368),(6,369),(6,370),(6,371),(6,372),(6,373),(6,374),(6,375),(6,376),(6,377),(6,378),(6,379),(6,380),(6,381),(6,382),(6,383),(6,384),(7,5),(7,6),(7,7),(7,8),(7,9),(7,10),(7,11),(7,12),(7,13),(7,14),(7,15),(7,16),(7,17),(7,18),(7,19),(7,20),(7,21),(7,22),(7,23),(7,24),(7,25),(7,26),(7,27),(7,28),(7,29),(7,30),(7,31),(7,32),(7,33),(7,34),(7,35),(7,36),(7,45),(7,46),(7,47),(7,48),(7,49),(7,50),(7,51),(7,52),(7,53),(7,54),(7,55),(7,56),(7,57),(7,58),(7,59),(7,60),(7,61),(7,62),(7,63),(7,64),(7,65),(7,66),(7,67),(7,68),(7,69),(7,70),(7,71),(7,72),(7,73),(7,74),(7,75),(7,76),(7,77),(7,78),(7,79),(7,80),(7,81),(7,82),(7,83),(7,84),(7,85),(7,86),(7,87),(7,88),(7,89),(7,90),(7,91),(7,92),(7,93),(7,94),(7,95),(7,96),(7,97),(7,98),(7,99),(7,100),(7,101),(7,102),(7,103),(7,104),(7,105),(7,106),(7,107),(7,108),(7,109),(7,110),(7,111),(7,112),(7,113),(7,114),(7,115),(7,116),(7,117),(7,118),(7,119),(7,120),(7,121),(7,122),(7,123),(7,124),(7,125),(7,126),(7,127),(7,128),(7,129),(7,130),(7,131),(7,132),(7,133),(7,134),(7,135),(7,136),(7,137),(7,138),(7,139),(7,140),(7,141),(7,142),(7,143),(7,144),(7,145),(7,146),(7,147),(7,148),(7,149),(7,150),(7,151),(7,152),(7,153),(7,154),(7,155),(7,156),(7,157),(7,158),(7,159),(7,160),(7,161),(7,162),(7,163),(7,164),(7,165),(7,166),(7,167),(7,168),(7,169),(7,170),(7,171),(7,172),(7,173),(7,174),(7,175),(7,176),(7,177),(7,178),(7,179),(7,180),(7,181),(7,182),(7,183),(7,184),(7,185),(7,186),(7,187),(7,188),(7,189),(7,190),(7,191),(7,192),(7,193),(7,194),(7,195),(7,196),(7,197),(7,198),(7,199),(7,200),(7,201),(7,202),(7,203),(7,204),(7,205),(7,206),(7,207),(7,208),(7,209),(7,210),(7,211),(7,212),(7,213),(7,214),(7,215),(7,216),(7,217),(7,218),(7,219),(7,220),(7,221),(7,222),(7,223),(7,224),(7,225),(7,226),(7,227),(7,228),(7,229),(7,230),(7,231),(7,232),(7,233),(7,234),(7,235),(7,236),(7,237),(7,238),(7,239),(7,240),(7,241),(7,242),(7,243),(7,244),(7,245),(7,246),(7,247),(7,248),(7,249),(7,250),(7,251),(7,252),(7,253),(7,254),(7,255),(7,256),(7,257),(7,258),(7,259),(7,260),(7,261),(7,262),(7,263),(7,264),(7,265),(7,266),(7,267),(7,268),(7,269),(7,270),(7,271),(7,272),(7,273),(7,274),(7,275),(7,276),(7,277),(7,278),(7,279),(7,280),(7,281),(7,282),(7,283),(7,284),(7,285),(7,286),(7,287),(7,288),(7,289),(7,290),(7,291),(7,292),(7,293),(7,294),(7,295),(7,296),(7,297),(7,298),(7,299),(7,300),(7,301),(7,302),(7,303),(7,304),(7,305),(7,306),(7,307),(7,308),(7,309),(7,310),(7,311),(7,312),(7,313),(7,314),(7,315),(7,316),(7,317),(7,318),(7,319),(7,320),(7,321),(7,322),(7,323),(7,324),(7,325),(7,326),(7,327),(7,328),(7,329),(7,330),(7,331),(7,332),(7,333),(7,334),(7,335),(7,336),(7,337),(7,338),(7,339),(7,340),(7,341),(7,342),(7,343),(7,344),(7,345),(7,346),(7,347),(7,348),(7,349),(7,350),(7,351),(7,352),(7,353),(7,354),(7,355),(7,356),(7,357),(7,358),(7,359),(7,360),(7,361),(7,362),(7,363),(7,364),(7,365),(7,366),(7,367),(7,368),(7,369),(7,370),(7,371),(7,372),(7,373),(7,374),(7,375),(7,376),(7,377),(7,378),(7,379),(7,380),(7,381),(7,382),(7,383),(7,384),(8,13),(8,14),(8,15),(8,16),(8,17),(8,18),(8,19),(8,20),(8,21),(8,22),(8,23),(8,24),(8,25),(8,26),(8,27),(8,28),(8,29),(8,30),(8,31),(8,32),(8,33),(8,34),(8,35),(8,36),(8,153),(8,154),(8,155),(8,156),(8,157),(8,158),(8,159),(8,160),(8,161),(8,162),(8,163),(8,164),(8,165),(8,166),(8,167),(8,168),(8,169),(8,170),(8,171),(8,172),(8,173),(8,174),(8,175),(8,176),(8,177),(8,178),(8,179),(8,180),(8,181),(8,182),(8,183),(8,184),(8,185),(8,186),(8,187),(8,188),(8,189),(8,190),(8,191),(8,192),(8,193),(8,194),(8,195),(8,196),(8,197),(8,198),(8,199),(8,200),(8,201),(8,202),(8,203),(8,204),(8,205),(8,206),(8,207),(8,208),(8,209),(8,210),(8,211),(8,212),(8,213),(8,214),(8,215),(8,216),(8,217),(8,218),(8,219),(8,220),(8,221),(8,222),(8,223),(8,224),(8,225),(8,226),(8,227),(8,228),(8,229),(8,230),(8,231),(8,232),(8,233),(8,234),(8,235),(8,236),(8,237),(8,238),(8,239),(8,240),(8,241),(8,242),(8,243),(8,244),(8,245),(8,246),(8,247),(8,248),(8,249),(8,250),(8,251),(8,252),(8,253),(8,254),(8,255),(8,256),(8,257),(8,258),(8,259),(8,260),(8,261),(8,262),(8,263),(8,264),(8,265),(8,266),(8,267),(8,268),(8,269),(8,270),(8,271),(8,272),(8,273),(8,274),(8,275),(8,276),(8,277),(8,278),(8,279),(8,280),(8,281),(8,282),(8,283),(8,284),(8,285),(8,286),(8,287),(8,288),(8,289),(8,290),(8,291),(8,292),(8,293),(8,294),(8,295),(8,296),(8,297),(8,298),(8,299),(8,300),(8,301),(8,302),(8,303),(8,304),(8,305),(8,306),(8,307),(8,308),(8,309),(8,310),(8,311),(8,312),(8,313),(8,314),(8,315),(8,316),(8,317),(8,318),(8,319),(8,320),(8,321),(8,322),(8,323),(8,324),(8,325),(8,326),(8,327),(8,328),(8,329),(8,330),(8,331),(8,332),(8,333),(8,334),(8,335),(8,336),(8,337),(8,338),(8,339),(8,340),(8,341),(8,342),(8,343),(8,344),(8,345),(8,346),(8,347),(8,348),(8,349),(8,350),(8,351),(8,352),(8,353),(8,354),(8,355),(8,356),(8,357),(8,358),(8,359),(8,360),(8,361),(8,362),(8,363),(8,364),(8,365),(8,366),(8,367),(8,368),(8,369),(8,370),(8,371),(8,372),(8,373),(8,374),(8,375),(8,376),(8,377),(8,378),(8,379),(8,380),(8,381),(8,382),(8,383),(8,384),(9,197),(9,198),(9,199),(9,200),(9,201),(9,202),(9,203),(9,204),(9,205),(9,206),(9,207),(9,208),(10,13),(10,14),(10,15),(10,16),(10,17),(10,18),(10,19),(10,20),(10,21),(10,22),(10,23),(10,24),(10,25),(10,26),(10,27),(10,28),(10,29),(10,30),(10,31),(10,32),(10,33),(10,34),(10,35),(10,36),(10,133),(10,134),(10,135),(10,136),(10,137),(10,138),(10,139),(10,140),(10,141),(10,142),(10,143),(10,144),(10,145),(10,146),(10,147),(10,148),(10,149),(10,150),(10,151),(10,152),(10,153),(10,154),(10,155),(10,156),(10,157),(10,158),(10,159),(10,160),(10,161),(10,162),(10,163),(10,164),(10,165),(10,166),(10,167),(10,168),(10,169),(10,170),(10,171),(10,172),(10,173),(10,174),(10,175),(10,176),(10,177),(10,178),(10,179),(10,180),(10,181),(10,182),(10,183),(10,184),(10,185),(10,186),(10,187),(10,188),(10,189),(10,190),(10,191),(10,192),(10,193),(10,194),(10,195),(10,196),(10,197),(10,198),(10,199),(10,200),(10,201),(10,202),(10,203),(10,204),(10,205),(10,206),(10,207),(10,208),(10,209),(10,210),(10,211),(10,212),(10,213),(10,214),(10,215),(10,216),(10,217),(10,218),(10,219),(10,220),(10,221),(10,222),(10,223),(10,224),(10,225),(10,226),(10,227),(10,228),(10,229),(10,230),(10,231),(10,232),(10,233),(10,234),(10,235),(10,236),(10,237),(10,238),(10,239),(10,240),(10,241),(10,242),(10,243),(10,244),(10,245),(10,246),(10,247),(10,248),(10,249),(10,250),(10,251),(10,252),(10,253),(10,254),(10,255),(10,256),(10,257),(10,258),(10,259),(10,260),(10,261),(10,262),(10,263),(10,264),(10,265),(10,266),(10,267),(10,268),(10,269),(10,270),(10,271),(10,272),(10,273),(10,274),(10,275),(10,276),(10,277),(10,278),(10,279),(10,280),(10,281),(10,282),(10,283),(10,284),(10,285),(10,286),(10,287),(10,288),(10,289),(10,290),(10,291),(10,292),(10,293),(10,294),(10,295),(10,296),(10,297),(10,298),(10,299),(10,300),(10,301),(10,302),(10,303),(10,304),(10,305),(10,306),(10,307),(10,308),(10,309),(10,310),(10,311),(10,312),(10,313),(10,314),(10,315),(10,316),(10,317),(10,318),(10,319),(10,320),(10,321),(10,322),(10,323),(10,324),(10,325),(10,326),(10,327),(10,328),(10,329),(10,330),(10,331),(10,332),(10,333),(10,334),(10,335),(10,336),(10,337),(10,338),(10,339),(10,340),(10,341),(10,342),(10,343),(10,344),(10,345),(10,346),(10,347),(10,348),(10,349),(10,350),(10,351),(10,352),(10,353),(10,354),(10,355),(10,356),(10,357),(10,358),(10,359),(10,360),(10,361),(10,362),(10,363),(10,364),(10,365),(10,366),(10,367),(10,368),(10,369),(10,370),(10,371),(10,372),(10,373),(10,374),(10,375),(10,376),(10,377),(10,378),(10,379),(10,380),(10,381),(10,382),(10,383),(10,384),(11,17),(11,18),(11,19),(11,20),(11,169),(11,170),(11,171),(11,172),(11,173),(11,174),(11,175),(11,176),(11,177),(11,178),(11,179),(11,180),(11,181),(11,182),(11,183),(11,184),(11,185),(11,186),(11,187),(11,188),(11,189),(11,190),(11,191),(11,192),(11,193),(11,194),(11,195),(11,196),(12,9),(12,10),(12,11),(12,12),(12,13),(12,14),(12,15),(12,16),(12,17),(12,18),(12,19),(12,20),(12,21),(12,22),(12,23),(12,24),(12,25),(12,26),(12,27),(12,28),(12,29),(12,30),(12,31),(12,32),(12,33),(12,34),(12,35),(12,36),(12,117),(12,118),(12,119),(12,120),(12,121),(12,122),(12,123),(12,124),(12,125),(12,126),(12,127),(12,128),(12,129),(12,130),(12,131),(12,132),(12,133),(12,134),(12,135),(12,136),(12,137),(12,138),(12,139),(12,140),(12,141),(12,142),(12,143),(12,144),(12,145),(12,146),(12,147),(12,148),(12,149),(12,150),(12,151),(12,152),(12,153),(12,154),(12,155),(12,156),(12,157),(12,158),(12,159),(12,160),(12,161),(12,162),(12,163),(12,164),(12,165),(12,166),(12,167),(12,168),(12,169),(12,170),(12,171),(12,172),(12,173),(12,174),(12,175),(12,176),(12,177),(12,178),(12,179),(12,180),(12,181),(12,182),(12,183),(12,184),(12,185),(12,186),(12,187),(12,188),(12,189),(12,190),(12,191),(12,192),(12,193),(12,194),(12,195),(12,196),(12,197),(12,198),(12,199),(12,200),(12,201),(12,202),(12,203),(12,204),(12,205),(12,206),(12,207),(12,208),(12,209),(12,210),(12,211),(12,212),(12,213),(12,214),(12,215),(12,216),(12,217),(12,218),(12,219),(12,220),(12,221),(12,222),(12,223),(12,224),(12,225),(12,226),(12,227),(12,228),(12,229),(12,230),(12,231),(12,232),(12,233),(12,234),(12,235),(12,236),(12,237),(12,238),(12,239),(12,240),(12,241),(12,242),(12,243),(12,244),(12,245),(12,246),(12,247),(12,248),(12,249),(12,250),(12,251),(12,252),(12,253),(12,254),(12,255),(12,256),(12,257),(12,258),(12,259),(12,260),(12,261),(12,262),(12,263),(12,264),(12,265),(12,266),(12,267),(12,268),(12,269),(12,270),(12,271),(12,272),(12,273),(12,274),(12,275),(12,276),(12,277),(12,278),(12,279),(12,280),(12,281),(12,282),(12,283),(12,284),(12,285),(12,286),(12,287),(12,288),(12,289),(12,290),(12,291),(12,292),(12,293),(12,294),(12,295),(12,296),(12,297),(12,298),(12,299),(12,300),(12,301),(12,302),(12,303),(12,304),(12,305),(12,306),(12,307),(12,308),(12,309),(12,310),(12,311),(12,312),(12,313),(12,314),(12,315),(12,316),(12,317),(12,318),(12,319),(12,320),(12,321),(12,322),(12,323),(12,324),(12,325),(12,326),(12,327),(12,328),(12,329),(12,330),(12,331),(12,332),(12,333),(12,334),(12,335),(12,336),(12,337),(12,338),(12,339),(12,340),(12,341),(12,342),(12,343),(12,344),(12,345),(12,346),(12,347),(12,348),(12,349),(12,350),(12,351),(12,352),(12,353),(12,354),(12,355),(12,356),(12,357),(12,358),(12,359),(12,360),(12,361),(12,362),(12,363),(12,364),(12,365),(12,366),(12,367),(12,368),(12,369),(12,370),(12,371),(12,372),(12,373),(12,374),(12,375),(12,376),(12,377),(12,378),(12,379),(12,380),(12,381),(12,382),(12,383),(12,384),(13,13),(13,14),(13,15),(13,16),(13,121),(13,122),(13,123),(13,124),(13,125),(13,126),(13,127),(13,128),(13,129),(13,130),(13,131),(13,132),(13,133),(13,134),(13,135),(13,136),(13,137),(13,138),(13,139),(13,140),(13,141),(13,142),(13,143),(13,144),(13,145),(13,146),(13,147),(13,148),(13,149),(13,150),(13,151),(13,152),(13,153),(13,154),(13,155),(13,156),(13,157),(13,158),(13,159),(13,160),(13,161),(13,162),(13,163),(13,164),(13,165),(13,166),(13,167),(13,168),(14,9),(14,10),(14,11),(14,12),(14,13),(14,14),(14,15),(14,16),(14,17),(14,18),(14,19),(14,20),(14,21),(14,22),(14,23),(14,24),(14,25),(14,26),(14,27),(14,28),(14,29),(14,30),(14,31),(14,32),(14,33),(14,34),(14,35),(14,36),(14,93),(14,94),(14,95),(14,96),(14,97),(14,98),(14,99),(14,100),(14,101),(14,102),(14,103),(14,104),(14,105),(14,106),(14,107),(14,108),(14,109),(14,110),(14,111),(14,112),(14,113),(14,114),(14,115),(14,116),(14,117),(14,118),(14,119),(14,120),(14,121),(14,122),(14,123),(14,124),(14,125),(14,126),(14,127),(14,128),(14,129),(14,130),(14,131),(14,132),(14,133),(14,134),(14,135),(14,136),(14,137),(14,138),(14,139),(14,140),(14,141),(14,142),(14,143),(14,144),(14,145),(14,146),(14,147),(14,148),(14,149),(14,150),(14,151),(14,152),(14,153),(14,154),(14,155),(14,156),(14,157),(14,158),(14,159),(14,160),(14,161),(14,162),(14,163),(14,164),(14,165),(14,166),(14,167),(14,168),(14,169),(14,170),(14,171),(14,172),(14,173),(14,174),(14,175),(14,176),(14,177),(14,178),(14,179),(14,180),(14,181),(14,182),(14,183),(14,184),(14,185),(14,186),(14,187),(14,188),(14,189),(14,190),(14,191),(14,192),(14,193),(14,194),(14,195),(14,196),(14,197),(14,198),(14,199),(14,200),(14,201),(14,202),(14,203),(14,204),(14,205),(14,206),(14,207),(14,208),(14,209),(14,210),(14,211),(14,212),(14,213),(14,214),(14,215),(14,216),(14,217),(14,218),(14,219),(14,220),(14,221),(14,222),(14,223),(14,224),(14,225),(14,226),(14,227),(14,228),(14,229),(14,230),(14,231),(14,232),(14,233),(14,234),(14,235),(14,236),(14,237),(14,238),(14,239),(14,240),(14,241),(14,242),(14,243),(14,244),(14,245),(14,246),(14,247),(14,248),(14,249),(14,250),(14,251),(14,252),(14,253),(14,254),(14,255),(14,256),(14,257),(14,258),(14,259),(14,260),(14,261),(14,262),(14,263),(14,264),(14,265),(14,266),(14,267),(14,268),(14,269),(14,270),(14,271),(14,272),(14,273),(14,274),(14,275),(14,276),(14,277),(14,278),(14,279),(14,280),(14,281),(14,282),(14,283),(14,284),(14,285),(14,286),(14,287),(14,288),(14,289),(14,290),(14,291),(14,292),(14,293),(14,294),(14,295),(14,296),(14,297),(14,298),(14,299),(14,300),(14,301),(14,302),(14,303),(14,304),(14,305),(14,306),(14,307),(14,308),(14,309),(14,310),(14,311),(14,312),(14,313),(14,314),(14,315),(14,316),(14,317),(14,318),(14,319),(14,320),(14,321),(14,322),(14,323),(14,324),(14,325),(14,326),(14,327),(14,328),(14,329),(14,330),(14,331),(14,332),(14,333),(14,334),(14,335),(14,336),(14,337),(14,338),(14,339),(14,340),(14,341),(14,342),(14,343),(14,344),(14,345),(14,346),(14,347),(14,348),(14,349),(14,350),(14,351),(14,352),(14,353),(14,354),(14,355),(14,356),(14,357),(14,358),(14,359),(14,360),(14,361),(14,362),(14,363),(14,364),(14,365),(14,366),(14,367),(14,368),(14,369),(14,370),(14,371),(14,372),(14,373),(14,374),(14,375),(14,376),(14,377),(14,378),(14,379),(14,380),(14,381),(14,382),(14,383),(14,384),(15,5),(15,6),(15,7),(15,8),(15,9),(15,10),(15,11),(15,12),(15,57),(15,58),(15,59),(15,60),(15,61),(15,62),(15,63),(15,64),(15,65),(15,66),(15,67),(15,68),(15,69),(15,70),(15,71),(15,72),(15,73),(15,74),(15,75),(15,76),(15,77),(15,78),(15,79),(15,80),(15,81),(15,82),(15,83),(15,84),(15,85),(15,86),(15,87),(15,88),(15,89),(15,90),(15,91),(15,92),(15,93),(15,94),(15,95),(15,96),(15,97),(15,98),(15,99),(15,100),(15,101),(15,102),(15,103),(15,104),(15,105),(15,106),(15,107),(15,108),(15,109),(15,110),(15,111),(15,112),(15,113),(15,114),(15,115),(15,116),(15,117),(15,118),(15,119),(15,120),(16,5),(16,6),(16,7),(16,8),(16,9),(16,10),(16,11),(16,12),(16,13),(16,14),(16,15),(16,16),(16,17),(16,18),(16,19),(16,20),(16,21),(16,22),(16,23),(16,24),(16,25),(16,26),(16,27),(16,28),(16,29),(16,30),(16,31),(16,32),(16,33),(16,34),(16,35),(16,36),(16,77),(16,78),(16,79),(16,80),(16,81),(16,82),(16,83),(16,84),(16,85),(16,86),(16,87),(16,88),(16,89),(16,90),(16,91),(16,92),(16,93),(16,94),(16,95),(16,96),(16,97),(16,98),(16,99),(16,100),(16,101),(16,102),(16,103),(16,104),(16,105),(16,106),(16,107),(16,108),(16,109),(16,110),(16,111),(16,112),(16,113),(16,114),(16,115),(16,116),(16,117),(16,118),(16,119),(16,120),(16,121),(16,122),(16,123),(16,124),(16,125),(16,126),(16,127),(16,128),(16,129),(16,130),(16,131),(16,132),(16,133),(16,134),(16,135),(16,136),(16,137),(16,138),(16,139),(16,140),(16,141),(16,142),(16,143),(16,144),(16,145),(16,146),(16,147),(16,148),(16,149),(16,150),(16,151),(16,152),(16,153),(16,154),(16,155),(16,156),(16,157),(16,158),(16,159),(16,160),(16,161),(16,162),(16,163),(16,164),(16,165),(16,166),(16,167),(16,168),(16,169),(16,170),(16,171),(16,172),(16,173),(16,174),(16,175),(16,176),(16,177),(16,178),(16,179),(16,180),(16,181),(16,182),(16,183),(16,184),(16,185),(16,186),(16,187),(16,188),(16,189),(16,190),(16,191),(16,192),(16,193),(16,194),(16,195),(16,196),(16,197),(16,198),(16,199),(16,200),(16,201),(16,202),(16,203),(16,204),(16,205),(16,206),(16,207),(16,208),(16,209),(16,210),(16,211),(16,212),(16,213),(16,214),(16,215),(16,216),(16,217),(16,218),(16,219),(16,220),(16,221),(16,222),(16,223),(16,224),(16,225),(16,226),(16,227),(16,228),(16,229),(16,230),(16,231),(16,232),(16,233),(16,234),(16,235),(16,236),(16,237),(16,238),(16,239),(16,240),(16,241),(16,242),(16,243),(16,244),(16,245),(16,246),(16,247),(16,248),(16,249),(16,250),(16,251),(16,252),(16,253),(16,254),(16,255),(16,256),(16,257),(16,258),(16,259),(16,260),(16,261),(16,262),(16,263),(16,264),(16,265),(16,266),(16,267),(16,268),(16,269),(16,270),(16,271),(16,272),(16,273),(16,274),(16,275),(16,276),(16,277),(16,278),(16,279),(16,280),(16,281),(16,282),(16,283),(16,284),(16,285),(16,286),(16,287),(16,288),(16,289),(16,290),(16,291),(16,292),(16,293),(16,294),(16,295),(16,296),(16,297),(16,298),(16,299),(16,300),(16,301),(16,302),(16,303),(16,304),(16,305),(16,306),(16,307),(16,308),(16,309),(16,310),(16,311),(16,312),(16,313),(16,314),(16,315),(16,316),(16,317),(16,318),(16,319),(16,320),(16,321),(16,322),(16,323),(16,324),(16,325),(16,326),(16,327),(16,328),(16,329),(16,330),(16,331),(16,332),(16,333),(16,334),(16,335),(16,336),(16,337),(16,338),(16,339),(16,340),(16,341),(16,342),(16,343),(16,344),(16,345),(16,346),(16,347),(16,348),(16,349),(16,350),(16,351),(16,352),(16,353),(16,354),(16,355),(16,356),(16,357),(16,358),(16,359),(16,360),(16,361),(16,362),(16,363),(16,364),(16,365),(16,366),(16,367),(16,368),(16,369),(16,370),(16,371),(16,372),(16,373),(16,374),(16,375),(16,376),(16,377),(16,378),(16,379),(16,380),(16,381),(16,382),(16,383),(16,384),(17,13),(17,14),(17,15),(17,16),(17,17),(17,18),(17,19),(17,20),(17,49),(17,50),(17,51),(17,52),(17,53),(17,54),(17,55),(17,56),(17,121),(17,122),(17,123),(17,124),(17,125),(17,126),(17,127),(17,128),(17,129),(17,130),(17,131),(17,132),(17,133),(17,134),(17,135),(17,136),(17,137),(17,138),(17,139),(17,140),(17,141),(17,142),(17,143),(17,144),(17,145),(17,146),(17,147),(17,148),(17,149),(17,150),(17,151),(17,152),(17,153),(17,154),(17,155),(17,156),(17,157),(17,158),(17,159),(17,160),(17,161),(17,162),(17,163),(17,164),(17,165),(17,166),(17,167),(17,168),(17,169),(17,170),(17,171),(17,172),(17,173),(17,174),(17,175),(17,176),(17,177),(17,178),(17,179),(17,180),(17,181),(17,182),(17,183),(17,184),(17,185),(17,186),(17,187),(17,188),(17,189),(17,190),(17,191),(17,192),(17,193),(17,194),(17,195),(17,196); /*!40000 ALTER TABLE `gantt_jobs_resources` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `gantt_jobs_resources_log` -- DROP TABLE IF EXISTS `gantt_jobs_resources_log`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `gantt_jobs_resources_log` ( `sched_date` int(10) unsigned NOT NULL, `moldable_job_id` int(10) unsigned NOT NULL, `resource_id` int(10) unsigned NOT NULL, PRIMARY KEY (`sched_date`,`moldable_job_id`,`resource_id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `gantt_jobs_resources_log` -- LOCK TABLES `gantt_jobs_resources_log` WRITE; /*!40000 ALTER TABLE `gantt_jobs_resources_log` DISABLE KEYS */; /*!40000 ALTER TABLE `gantt_jobs_resources_log` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `gantt_jobs_resources_visu` -- DROP TABLE IF EXISTS `gantt_jobs_resources_visu`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `gantt_jobs_resources_visu` ( `moldable_job_id` int(10) unsigned NOT NULL, `resource_id` int(10) unsigned NOT NULL, PRIMARY KEY (`moldable_job_id`,`resource_id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `gantt_jobs_resources_visu` -- LOCK TABLES `gantt_jobs_resources_visu` WRITE; /*!40000 ALTER TABLE `gantt_jobs_resources_visu` DISABLE KEYS */; INSERT INTO `gantt_jobs_resources_visu` VALUES (1,1),(1,2),(1,3),(1,4),(1,5),(1,6),(1,7),(1,8),(1,9),(1,10),(1,11),(1,12),(1,13),(1,14),(1,15),(1,16),(1,17),(1,18),(1,19),(1,20),(1,21),(1,22),(1,23),(1,24),(1,25),(1,26),(1,27),(1,28),(1,29),(1,30),(1,31),(1,32),(1,33),(1,34),(1,35),(1,36),(1,37),(1,38),(1,39),(1,40),(1,41),(1,42),(1,43),(1,44),(1,45),(1,46),(1,47),(1,48),(1,49),(1,50),(1,51),(1,52),(1,53),(1,54),(1,55),(1,56),(1,57),(1,58),(1,59),(1,60),(1,61),(1,62),(1,63),(1,64),(1,65),(1,66),(1,67),(1,68),(1,69),(1,70),(1,71),(1,72),(1,73),(1,74),(1,75),(1,76),(1,77),(1,78),(1,79),(1,80),(1,81),(1,82),(1,83),(1,84),(1,85),(1,86),(1,87),(1,88),(1,89),(1,90),(1,91),(1,92),(1,93),(1,94),(1,95),(1,96),(1,97),(1,98),(1,99),(1,100),(1,101),(1,102),(1,103),(1,104),(1,105),(1,106),(1,107),(1,108),(1,109),(1,110),(1,111),(1,112),(1,113),(1,114),(1,115),(1,116),(1,117),(1,118),(1,119),(1,120),(1,121),(1,122),(1,123),(1,124),(1,125),(1,126),(1,127),(1,128),(1,129),(1,130),(1,131),(1,132),(1,133),(1,134),(1,135),(1,136),(1,137),(1,138),(1,139),(1,140),(1,141),(1,142),(1,143),(1,144),(1,145),(1,146),(1,147),(1,148),(1,149),(1,150),(1,151),(1,152),(1,153),(1,154),(1,155),(1,156),(1,157),(1,158),(1,159),(1,160),(1,161),(1,162),(1,163),(1,164),(1,165),(1,166),(1,167),(1,168),(1,169),(1,170),(1,171),(1,172),(1,173),(1,174),(1,175),(1,176),(1,177),(1,178),(1,179),(1,180),(1,181),(1,182),(1,183),(1,184),(1,185),(1,186),(1,187),(1,188),(1,189),(1,190),(1,191),(1,192),(1,193),(1,194),(1,195),(1,196),(1,197),(1,198),(1,199),(1,200),(1,201),(1,202),(1,203),(1,204),(1,205),(1,206),(1,207),(1,208),(1,209),(1,210),(1,211),(1,212),(1,213),(1,214),(1,215),(1,216),(1,217),(1,218),(1,219),(1,220),(1,221),(1,222),(1,223),(1,224),(1,225),(1,226),(1,227),(1,228),(1,229),(1,230),(1,231),(1,232),(1,233),(1,234),(1,235),(1,236),(1,237),(1,238),(1,239),(1,240),(1,241),(1,242),(1,243),(1,244),(1,245),(1,246),(1,247),(1,248),(1,249),(1,250),(1,251),(1,252),(1,253),(1,254),(1,255),(1,256),(1,257),(1,258),(1,259),(1,260),(1,261),(1,262),(1,263),(1,264),(1,265),(1,266),(1,267),(1,268),(1,269),(1,270),(1,271),(1,272),(1,273),(1,274),(1,275),(1,276),(1,277),(1,278),(1,279),(1,280),(1,281),(1,282),(1,283),(1,284),(1,285),(1,286),(1,287),(1,288),(1,289),(1,290),(1,291),(1,292),(1,293),(1,294),(1,295),(1,296),(1,297),(1,298),(1,299),(1,300),(1,301),(1,302),(1,303),(1,304),(1,305),(1,306),(1,307),(1,308),(1,309),(1,310),(1,311),(1,312),(1,313),(1,314),(1,315),(1,316),(1,317),(1,318),(1,319),(1,320),(1,321),(1,322),(1,323),(1,324),(1,325),(1,326),(1,327),(1,328),(1,329),(1,330),(1,331),(1,332),(1,333),(1,334),(1,335),(1,336),(1,337),(1,338),(1,339),(1,340),(1,341),(1,342),(1,343),(1,344),(1,345),(1,346),(1,347),(1,348),(1,349),(1,350),(1,351),(1,352),(1,353),(1,354),(1,355),(1,356),(1,357),(1,358),(1,359),(1,360),(1,361),(1,362),(1,363),(1,364),(1,365),(1,366),(1,367),(1,368),(1,369),(1,370),(1,371),(1,372),(1,373),(1,374),(1,375),(1,376),(1,377),(1,378),(1,379),(1,380),(1,381),(1,382),(1,383),(1,384),(2,21),(2,22),(2,23),(2,24),(2,25),(2,26),(2,27),(2,28),(2,29),(2,30),(2,31),(2,32),(2,33),(2,34),(2,35),(2,36),(2,209),(2,210),(2,211),(2,212),(2,213),(2,214),(2,215),(2,216),(2,217),(2,218),(2,219),(2,220),(2,221),(2,222),(2,223),(2,224),(2,225),(2,226),(2,227),(2,228),(2,229),(2,230),(2,231),(2,232),(2,233),(2,234),(2,235),(2,236),(2,237),(2,238),(2,239),(2,240),(2,241),(2,242),(2,243),(2,244),(2,245),(2,246),(2,247),(2,248),(2,249),(2,250),(2,251),(2,252),(2,253),(2,254),(2,255),(2,256),(2,257),(2,258),(2,259),(2,260),(2,261),(2,262),(2,263),(2,264),(2,265),(2,266),(2,267),(2,268),(2,269),(2,270),(2,271),(2,272),(2,273),(2,274),(2,275),(2,276),(2,277),(2,278),(2,279),(2,280),(2,281),(2,282),(2,283),(2,284),(2,285),(2,286),(2,287),(2,288),(2,289),(2,290),(2,291),(2,292),(2,293),(2,294),(2,295),(2,296),(2,297),(2,298),(2,299),(2,300),(2,301),(2,302),(2,303),(2,304),(2,305),(2,306),(2,307),(2,308),(2,309),(2,310),(2,311),(2,312),(2,313),(2,314),(2,315),(2,316),(2,317),(2,318),(2,319),(2,320),(2,321),(2,322),(2,323),(2,324),(2,325),(2,326),(2,327),(2,328),(2,329),(2,330),(2,331),(2,332),(2,333),(2,334),(2,335),(2,336),(2,337),(2,338),(2,339),(2,340),(2,341),(2,342),(2,343),(2,344),(2,345),(2,346),(2,347),(2,348),(2,349),(2,350),(2,351),(2,352),(2,353),(2,354),(2,355),(2,356),(2,357),(2,358),(2,359),(2,360),(2,361),(2,362),(2,363),(2,364),(2,365),(2,366),(2,367),(2,368),(2,369),(2,370),(2,371),(2,372),(2,373),(2,374),(2,375),(2,376),(2,377),(2,378),(2,379),(2,380),(2,381),(2,382),(2,383),(2,384),(3,9),(3,10),(3,11),(3,12),(3,13),(3,14),(3,15),(3,16),(3,17),(3,18),(3,19),(3,20),(3,21),(3,22),(3,23),(3,24),(3,25),(3,26),(3,27),(3,28),(3,29),(3,30),(3,31),(3,32),(3,33),(3,34),(3,35),(3,36),(3,81),(3,82),(3,83),(3,84),(3,85),(3,86),(3,87),(3,88),(3,89),(3,90),(3,91),(3,92),(3,93),(3,94),(3,95),(3,96),(3,97),(3,98),(3,99),(3,100),(3,101),(3,102),(3,103),(3,104),(3,105),(3,106),(3,107),(3,108),(3,109),(3,110),(3,111),(3,112),(3,113),(3,114),(3,115),(3,116),(3,117),(3,118),(3,119),(3,120),(3,121),(3,122),(3,123),(3,124),(3,125),(3,126),(3,127),(3,128),(3,129),(3,130),(3,131),(3,132),(3,133),(3,134),(3,135),(3,136),(3,137),(3,138),(3,139),(3,140),(3,141),(3,142),(3,143),(3,144),(3,145),(3,146),(3,147),(3,148),(3,149),(3,150),(3,151),(3,152),(3,153),(3,154),(3,155),(3,156),(3,157),(3,158),(3,159),(3,160),(3,161),(3,162),(3,163),(3,164),(3,165),(3,166),(3,167),(3,168),(3,169),(3,170),(3,171),(3,172),(3,173),(3,174),(3,175),(3,176),(3,177),(3,178),(3,179),(3,180),(3,181),(3,182),(3,183),(3,184),(3,185),(3,186),(3,187),(3,188),(3,189),(3,190),(3,191),(3,192),(3,193),(3,194),(3,195),(3,196),(3,197),(3,198),(3,199),(3,200),(3,201),(3,202),(3,203),(3,204),(3,205),(3,206),(3,207),(3,208),(3,209),(3,210),(3,211),(3,212),(3,213),(3,214),(3,215),(3,216),(3,217),(3,218),(3,219),(3,220),(3,221),(3,222),(3,223),(3,224),(3,225),(3,226),(3,227),(3,228),(3,229),(3,230),(3,231),(3,232),(3,233),(3,234),(3,235),(3,236),(3,237),(3,238),(3,239),(3,240),(3,241),(3,242),(3,243),(3,244),(3,245),(3,246),(3,247),(3,248),(3,249),(3,250),(3,251),(3,252),(3,253),(3,254),(3,255),(3,256),(3,257),(3,258),(3,259),(3,260),(3,261),(3,262),(3,263),(3,264),(3,265),(3,266),(3,267),(3,268),(3,269),(3,270),(3,271),(3,272),(3,273),(3,274),(3,275),(3,276),(3,277),(3,278),(3,279),(3,280),(3,281),(3,282),(3,283),(3,284),(3,285),(3,286),(3,287),(3,288),(3,289),(3,290),(3,291),(3,292),(3,293),(3,294),(3,295),(3,296),(3,297),(3,298),(3,299),(3,300),(3,301),(3,302),(3,303),(3,304),(3,305),(3,306),(3,307),(3,308),(3,309),(3,310),(3,311),(3,312),(3,313),(3,314),(3,315),(3,316),(3,317),(3,318),(3,319),(3,320),(3,321),(3,322),(3,323),(3,324),(3,325),(3,326),(3,327),(3,328),(3,329),(3,330),(3,331),(3,332),(3,333),(3,334),(3,335),(3,336),(3,337),(3,338),(3,339),(3,340),(3,341),(3,342),(3,343),(3,344),(3,345),(3,346),(3,347),(3,348),(3,349),(3,350),(3,351),(3,352),(3,353),(3,354),(3,355),(3,356),(3,357),(3,358),(3,359),(3,360),(3,361),(3,362),(3,363),(3,364),(3,365),(3,366),(3,367),(3,368),(3,369),(3,370),(3,371),(3,372),(3,373),(3,374),(3,375),(3,376),(3,377),(3,378),(3,379),(3,380),(3,381),(3,382),(3,383),(3,384),(4,17),(4,18),(4,19),(4,20),(4,21),(4,22),(4,23),(4,24),(4,25),(4,26),(4,27),(4,28),(4,29),(4,30),(4,31),(4,32),(4,33),(4,34),(4,35),(4,36),(4,189),(4,190),(4,191),(4,192),(4,193),(4,194),(4,195),(4,196),(4,197),(4,198),(4,199),(4,200),(4,201),(4,202),(4,203),(4,204),(4,205),(4,206),(4,207),(4,208),(4,209),(4,210),(4,211),(4,212),(4,213),(4,214),(4,215),(4,216),(4,217),(4,218),(4,219),(4,220),(4,221),(4,222),(4,223),(4,224),(4,225),(4,226),(4,227),(4,228),(4,229),(4,230),(4,231),(4,232),(4,233),(4,234),(4,235),(4,236),(4,237),(4,238),(4,239),(4,240),(4,241),(4,242),(4,243),(4,244),(4,245),(4,246),(4,247),(4,248),(4,249),(4,250),(4,251),(4,252),(4,253),(4,254),(4,255),(4,256),(4,257),(4,258),(4,259),(4,260),(4,261),(4,262),(4,263),(4,264),(4,265),(4,266),(4,267),(4,268),(4,269),(4,270),(4,271),(4,272),(4,273),(4,274),(4,275),(4,276),(4,277),(4,278),(4,279),(4,280),(4,281),(4,282),(4,283),(4,284),(4,285),(4,286),(4,287),(4,288),(4,289),(4,290),(4,291),(4,292),(4,293),(4,294),(4,295),(4,296),(4,297),(4,298),(4,299),(4,300),(4,301),(4,302),(4,303),(4,304),(4,305),(4,306),(4,307),(4,308),(4,309),(4,310),(4,311),(4,312),(4,313),(4,314),(4,315),(4,316),(4,317),(4,318),(4,319),(4,320),(4,321),(4,322),(4,323),(4,324),(4,325),(4,326),(4,327),(4,328),(4,329),(4,330),(4,331),(4,332),(4,333),(4,334),(4,335),(4,336),(4,337),(4,338),(4,339),(4,340),(4,341),(4,342),(4,343),(4,344),(4,345),(4,346),(4,347),(4,348),(4,349),(4,350),(4,351),(4,352),(4,353),(4,354),(4,355),(4,356),(4,357),(4,358),(4,359),(4,360),(4,361),(4,362),(4,363),(4,364),(4,365),(4,366),(4,367),(4,368),(4,369),(4,370),(4,371),(4,372),(4,373),(4,374),(4,375),(4,376),(4,377),(4,378),(4,379),(4,380),(4,381),(4,382),(4,383),(4,384),(5,5),(5,6),(5,7),(5,8),(5,9),(5,10),(5,11),(5,12),(5,13),(5,14),(5,15),(5,16),(5,17),(5,18),(5,19),(5,20),(5,21),(5,22),(5,23),(5,24),(5,25),(5,26),(5,27),(5,28),(5,29),(5,30),(5,31),(5,32),(5,33),(5,34),(5,35),(5,36),(5,65),(5,66),(5,67),(5,68),(5,69),(5,70),(5,71),(5,72),(5,73),(5,74),(5,75),(5,76),(5,77),(5,78),(5,79),(5,80),(5,81),(5,82),(5,83),(5,84),(5,85),(5,86),(5,87),(5,88),(5,89),(5,90),(5,91),(5,92),(5,93),(5,94),(5,95),(5,96),(5,97),(5,98),(5,99),(5,100),(5,101),(5,102),(5,103),(5,104),(5,105),(5,106),(5,107),(5,108),(5,109),(5,110),(5,111),(5,112),(5,113),(5,114),(5,115),(5,116),(5,117),(5,118),(5,119),(5,120),(5,121),(5,122),(5,123),(5,124),(5,125),(5,126),(5,127),(5,128),(5,129),(5,130),(5,131),(5,132),(5,133),(5,134),(5,135),(5,136),(5,137),(5,138),(5,139),(5,140),(5,141),(5,142),(5,143),(5,144),(5,145),(5,146),(5,147),(5,148),(5,149),(5,150),(5,151),(5,152),(5,153),(5,154),(5,155),(5,156),(5,157),(5,158),(5,159),(5,160),(5,161),(5,162),(5,163),(5,164),(5,165),(5,166),(5,167),(5,168),(5,169),(5,170),(5,171),(5,172),(5,173),(5,174),(5,175),(5,176),(5,177),(5,178),(5,179),(5,180),(5,181),(5,182),(5,183),(5,184),(5,185),(5,186),(5,187),(5,188),(5,189),(5,190),(5,191),(5,192),(5,193),(5,194),(5,195),(5,196),(5,197),(5,198),(5,199),(5,200),(5,201),(5,202),(5,203),(5,204),(5,205),(5,206),(5,207),(5,208),(5,209),(5,210),(5,211),(5,212),(5,213),(5,214),(5,215),(5,216),(5,217),(5,218),(5,219),(5,220),(5,221),(5,222),(5,223),(5,224),(5,225),(5,226),(5,227),(5,228),(5,229),(5,230),(5,231),(5,232),(5,233),(5,234),(5,235),(5,236),(5,237),(5,238),(5,239),(5,240),(5,241),(5,242),(5,243),(5,244),(5,245),(5,246),(5,247),(5,248),(5,249),(5,250),(5,251),(5,252),(5,253),(5,254),(5,255),(5,256),(5,257),(5,258),(5,259),(5,260),(5,261),(5,262),(5,263),(5,264),(5,265),(5,266),(5,267),(5,268),(5,269),(5,270),(5,271),(5,272),(5,273),(5,274),(5,275),(5,276),(5,277),(5,278),(5,279),(5,280),(5,281),(5,282),(5,283),(5,284),(5,285),(5,286),(5,287),(5,288),(5,289),(5,290),(5,291),(5,292),(5,293),(5,294),(5,295),(5,296),(5,297),(5,298),(5,299),(5,300),(5,301),(5,302),(5,303),(5,304),(5,305),(5,306),(5,307),(5,308),(5,309),(5,310),(5,311),(5,312),(5,313),(5,314),(5,315),(5,316),(5,317),(5,318),(5,319),(5,320),(5,321),(5,322),(5,323),(5,324),(5,325),(5,326),(5,327),(5,328),(5,329),(5,330),(5,331),(5,332),(5,333),(5,334),(5,335),(5,336),(5,337),(5,338),(5,339),(5,340),(5,341),(5,342),(5,343),(5,344),(5,345),(5,346),(5,347),(5,348),(5,349),(5,350),(5,351),(5,352),(5,353),(5,354),(5,355),(5,356),(5,357),(5,358),(5,359),(5,360),(5,361),(5,362),(5,363),(5,364),(5,365),(5,366),(5,367),(5,368),(5,369),(5,370),(5,371),(5,372),(5,373),(5,374),(5,375),(5,376),(5,377),(5,378),(5,379),(5,380),(5,381),(5,382),(5,383),(5,384),(6,17),(6,18),(6,19),(6,20),(6,21),(6,22),(6,23),(6,24),(6,25),(6,26),(6,27),(6,28),(6,29),(6,30),(6,31),(6,32),(6,33),(6,34),(6,35),(6,36),(6,169),(6,170),(6,171),(6,172),(6,173),(6,174),(6,175),(6,176),(6,177),(6,178),(6,179),(6,180),(6,181),(6,182),(6,183),(6,184),(6,185),(6,186),(6,187),(6,188),(6,189),(6,190),(6,191),(6,192),(6,193),(6,194),(6,195),(6,196),(6,197),(6,198),(6,199),(6,200),(6,201),(6,202),(6,203),(6,204),(6,205),(6,206),(6,207),(6,208),(6,209),(6,210),(6,211),(6,212),(6,213),(6,214),(6,215),(6,216),(6,217),(6,218),(6,219),(6,220),(6,221),(6,222),(6,223),(6,224),(6,225),(6,226),(6,227),(6,228),(6,229),(6,230),(6,231),(6,232),(6,233),(6,234),(6,235),(6,236),(6,237),(6,238),(6,239),(6,240),(6,241),(6,242),(6,243),(6,244),(6,245),(6,246),(6,247),(6,248),(6,249),(6,250),(6,251),(6,252),(6,253),(6,254),(6,255),(6,256),(6,257),(6,258),(6,259),(6,260),(6,261),(6,262),(6,263),(6,264),(6,265),(6,266),(6,267),(6,268),(6,269),(6,270),(6,271),(6,272),(6,273),(6,274),(6,275),(6,276),(6,277),(6,278),(6,279),(6,280),(6,281),(6,282),(6,283),(6,284),(6,285),(6,286),(6,287),(6,288),(6,289),(6,290),(6,291),(6,292),(6,293),(6,294),(6,295),(6,296),(6,297),(6,298),(6,299),(6,300),(6,301),(6,302),(6,303),(6,304),(6,305),(6,306),(6,307),(6,308),(6,309),(6,310),(6,311),(6,312),(6,313),(6,314),(6,315),(6,316),(6,317),(6,318),(6,319),(6,320),(6,321),(6,322),(6,323),(6,324),(6,325),(6,326),(6,327),(6,328),(6,329),(6,330),(6,331),(6,332),(6,333),(6,334),(6,335),(6,336),(6,337),(6,338),(6,339),(6,340),(6,341),(6,342),(6,343),(6,344),(6,345),(6,346),(6,347),(6,348),(6,349),(6,350),(6,351),(6,352),(6,353),(6,354),(6,355),(6,356),(6,357),(6,358),(6,359),(6,360),(6,361),(6,362),(6,363),(6,364),(6,365),(6,366),(6,367),(6,368),(6,369),(6,370),(6,371),(6,372),(6,373),(6,374),(6,375),(6,376),(6,377),(6,378),(6,379),(6,380),(6,381),(6,382),(6,383),(6,384),(7,5),(7,6),(7,7),(7,8),(7,9),(7,10),(7,11),(7,12),(7,13),(7,14),(7,15),(7,16),(7,17),(7,18),(7,19),(7,20),(7,21),(7,22),(7,23),(7,24),(7,25),(7,26),(7,27),(7,28),(7,29),(7,30),(7,31),(7,32),(7,33),(7,34),(7,35),(7,36),(7,45),(7,46),(7,47),(7,48),(7,49),(7,50),(7,51),(7,52),(7,53),(7,54),(7,55),(7,56),(7,57),(7,58),(7,59),(7,60),(7,61),(7,62),(7,63),(7,64),(7,65),(7,66),(7,67),(7,68),(7,69),(7,70),(7,71),(7,72),(7,73),(7,74),(7,75),(7,76),(7,77),(7,78),(7,79),(7,80),(7,81),(7,82),(7,83),(7,84),(7,85),(7,86),(7,87),(7,88),(7,89),(7,90),(7,91),(7,92),(7,93),(7,94),(7,95),(7,96),(7,97),(7,98),(7,99),(7,100),(7,101),(7,102),(7,103),(7,104),(7,105),(7,106),(7,107),(7,108),(7,109),(7,110),(7,111),(7,112),(7,113),(7,114),(7,115),(7,116),(7,117),(7,118),(7,119),(7,120),(7,121),(7,122),(7,123),(7,124),(7,125),(7,126),(7,127),(7,128),(7,129),(7,130),(7,131),(7,132),(7,133),(7,134),(7,135),(7,136),(7,137),(7,138),(7,139),(7,140),(7,141),(7,142),(7,143),(7,144),(7,145),(7,146),(7,147),(7,148),(7,149),(7,150),(7,151),(7,152),(7,153),(7,154),(7,155),(7,156),(7,157),(7,158),(7,159),(7,160),(7,161),(7,162),(7,163),(7,164),(7,165),(7,166),(7,167),(7,168),(7,169),(7,170),(7,171),(7,172),(7,173),(7,174),(7,175),(7,176),(7,177),(7,178),(7,179),(7,180),(7,181),(7,182),(7,183),(7,184),(7,185),(7,186),(7,187),(7,188),(7,189),(7,190),(7,191),(7,192),(7,193),(7,194),(7,195),(7,196),(7,197),(7,198),(7,199),(7,200),(7,201),(7,202),(7,203),(7,204),(7,205),(7,206),(7,207),(7,208),(7,209),(7,210),(7,211),(7,212),(7,213),(7,214),(7,215),(7,216),(7,217),(7,218),(7,219),(7,220),(7,221),(7,222),(7,223),(7,224),(7,225),(7,226),(7,227),(7,228),(7,229),(7,230),(7,231),(7,232),(7,233),(7,234),(7,235),(7,236),(7,237),(7,238),(7,239),(7,240),(7,241),(7,242),(7,243),(7,244),(7,245),(7,246),(7,247),(7,248),(7,249),(7,250),(7,251),(7,252),(7,253),(7,254),(7,255),(7,256),(7,257),(7,258),(7,259),(7,260),(7,261),(7,262),(7,263),(7,264),(7,265),(7,266),(7,267),(7,268),(7,269),(7,270),(7,271),(7,272),(7,273),(7,274),(7,275),(7,276),(7,277),(7,278),(7,279),(7,280),(7,281),(7,282),(7,283),(7,284),(7,285),(7,286),(7,287),(7,288),(7,289),(7,290),(7,291),(7,292),(7,293),(7,294),(7,295),(7,296),(7,297),(7,298),(7,299),(7,300),(7,301),(7,302),(7,303),(7,304),(7,305),(7,306),(7,307),(7,308),(7,309),(7,310),(7,311),(7,312),(7,313),(7,314),(7,315),(7,316),(7,317),(7,318),(7,319),(7,320),(7,321),(7,322),(7,323),(7,324),(7,325),(7,326),(7,327),(7,328),(7,329),(7,330),(7,331),(7,332),(7,333),(7,334),(7,335),(7,336),(7,337),(7,338),(7,339),(7,340),(7,341),(7,342),(7,343),(7,344),(7,345),(7,346),(7,347),(7,348),(7,349),(7,350),(7,351),(7,352),(7,353),(7,354),(7,355),(7,356),(7,357),(7,358),(7,359),(7,360),(7,361),(7,362),(7,363),(7,364),(7,365),(7,366),(7,367),(7,368),(7,369),(7,370),(7,371),(7,372),(7,373),(7,374),(7,375),(7,376),(7,377),(7,378),(7,379),(7,380),(7,381),(7,382),(7,383),(7,384),(8,13),(8,14),(8,15),(8,16),(8,17),(8,18),(8,19),(8,20),(8,21),(8,22),(8,23),(8,24),(8,25),(8,26),(8,27),(8,28),(8,29),(8,30),(8,31),(8,32),(8,33),(8,34),(8,35),(8,36),(8,153),(8,154),(8,155),(8,156),(8,157),(8,158),(8,159),(8,160),(8,161),(8,162),(8,163),(8,164),(8,165),(8,166),(8,167),(8,168),(8,169),(8,170),(8,171),(8,172),(8,173),(8,174),(8,175),(8,176),(8,177),(8,178),(8,179),(8,180),(8,181),(8,182),(8,183),(8,184),(8,185),(8,186),(8,187),(8,188),(8,189),(8,190),(8,191),(8,192),(8,193),(8,194),(8,195),(8,196),(8,197),(8,198),(8,199),(8,200),(8,201),(8,202),(8,203),(8,204),(8,205),(8,206),(8,207),(8,208),(8,209),(8,210),(8,211),(8,212),(8,213),(8,214),(8,215),(8,216),(8,217),(8,218),(8,219),(8,220),(8,221),(8,222),(8,223),(8,224),(8,225),(8,226),(8,227),(8,228),(8,229),(8,230),(8,231),(8,232),(8,233),(8,234),(8,235),(8,236),(8,237),(8,238),(8,239),(8,240),(8,241),(8,242),(8,243),(8,244),(8,245),(8,246),(8,247),(8,248),(8,249),(8,250),(8,251),(8,252),(8,253),(8,254),(8,255),(8,256),(8,257),(8,258),(8,259),(8,260),(8,261),(8,262),(8,263),(8,264),(8,265),(8,266),(8,267),(8,268),(8,269),(8,270),(8,271),(8,272),(8,273),(8,274),(8,275),(8,276),(8,277),(8,278),(8,279),(8,280),(8,281),(8,282),(8,283),(8,284),(8,285),(8,286),(8,287),(8,288),(8,289),(8,290),(8,291),(8,292),(8,293),(8,294),(8,295),(8,296),(8,297),(8,298),(8,299),(8,300),(8,301),(8,302),(8,303),(8,304),(8,305),(8,306),(8,307),(8,308),(8,309),(8,310),(8,311),(8,312),(8,313),(8,314),(8,315),(8,316),(8,317),(8,318),(8,319),(8,320),(8,321),(8,322),(8,323),(8,324),(8,325),(8,326),(8,327),(8,328),(8,329),(8,330),(8,331),(8,332),(8,333),(8,334),(8,335),(8,336),(8,337),(8,338),(8,339),(8,340),(8,341),(8,342),(8,343),(8,344),(8,345),(8,346),(8,347),(8,348),(8,349),(8,350),(8,351),(8,352),(8,353),(8,354),(8,355),(8,356),(8,357),(8,358),(8,359),(8,360),(8,361),(8,362),(8,363),(8,364),(8,365),(8,366),(8,367),(8,368),(8,369),(8,370),(8,371),(8,372),(8,373),(8,374),(8,375),(8,376),(8,377),(8,378),(8,379),(8,380),(8,381),(8,382),(8,383),(8,384),(9,197),(9,198),(9,199),(9,200),(9,201),(9,202),(9,203),(9,204),(9,205),(9,206),(9,207),(9,208),(10,13),(10,14),(10,15),(10,16),(10,17),(10,18),(10,19),(10,20),(10,21),(10,22),(10,23),(10,24),(10,25),(10,26),(10,27),(10,28),(10,29),(10,30),(10,31),(10,32),(10,33),(10,34),(10,35),(10,36),(10,133),(10,134),(10,135),(10,136),(10,137),(10,138),(10,139),(10,140),(10,141),(10,142),(10,143),(10,144),(10,145),(10,146),(10,147),(10,148),(10,149),(10,150),(10,151),(10,152),(10,153),(10,154),(10,155),(10,156),(10,157),(10,158),(10,159),(10,160),(10,161),(10,162),(10,163),(10,164),(10,165),(10,166),(10,167),(10,168),(10,169),(10,170),(10,171),(10,172),(10,173),(10,174),(10,175),(10,176),(10,177),(10,178),(10,179),(10,180),(10,181),(10,182),(10,183),(10,184),(10,185),(10,186),(10,187),(10,188),(10,189),(10,190),(10,191),(10,192),(10,193),(10,194),(10,195),(10,196),(10,197),(10,198),(10,199),(10,200),(10,201),(10,202),(10,203),(10,204),(10,205),(10,206),(10,207),(10,208),(10,209),(10,210),(10,211),(10,212),(10,213),(10,214),(10,215),(10,216),(10,217),(10,218),(10,219),(10,220),(10,221),(10,222),(10,223),(10,224),(10,225),(10,226),(10,227),(10,228),(10,229),(10,230),(10,231),(10,232),(10,233),(10,234),(10,235),(10,236),(10,237),(10,238),(10,239),(10,240),(10,241),(10,242),(10,243),(10,244),(10,245),(10,246),(10,247),(10,248),(10,249),(10,250),(10,251),(10,252),(10,253),(10,254),(10,255),(10,256),(10,257),(10,258),(10,259),(10,260),(10,261),(10,262),(10,263),(10,264),(10,265),(10,266),(10,267),(10,268),(10,269),(10,270),(10,271),(10,272),(10,273),(10,274),(10,275),(10,276),(10,277),(10,278),(10,279),(10,280),(10,281),(10,282),(10,283),(10,284),(10,285),(10,286),(10,287),(10,288),(10,289),(10,290),(10,291),(10,292),(10,293),(10,294),(10,295),(10,296),(10,297),(10,298),(10,299),(10,300),(10,301),(10,302),(10,303),(10,304),(10,305),(10,306),(10,307),(10,308),(10,309),(10,310),(10,311),(10,312),(10,313),(10,314),(10,315),(10,316),(10,317),(10,318),(10,319),(10,320),(10,321),(10,322),(10,323),(10,324),(10,325),(10,326),(10,327),(10,328),(10,329),(10,330),(10,331),(10,332),(10,333),(10,334),(10,335),(10,336),(10,337),(10,338),(10,339),(10,340),(10,341),(10,342),(10,343),(10,344),(10,345),(10,346),(10,347),(10,348),(10,349),(10,350),(10,351),(10,352),(10,353),(10,354),(10,355),(10,356),(10,357),(10,358),(10,359),(10,360),(10,361),(10,362),(10,363),(10,364),(10,365),(10,366),(10,367),(10,368),(10,369),(10,370),(10,371),(10,372),(10,373),(10,374),(10,375),(10,376),(10,377),(10,378),(10,379),(10,380),(10,381),(10,382),(10,383),(10,384),(11,17),(11,18),(11,19),(11,20),(11,169),(11,170),(11,171),(11,172),(11,173),(11,174),(11,175),(11,176),(11,177),(11,178),(11,179),(11,180),(11,181),(11,182),(11,183),(11,184),(11,185),(11,186),(11,187),(11,188),(11,189),(11,190),(11,191),(11,192),(11,193),(11,194),(11,195),(11,196),(12,9),(12,10),(12,11),(12,12),(12,13),(12,14),(12,15),(12,16),(12,17),(12,18),(12,19),(12,20),(12,21),(12,22),(12,23),(12,24),(12,25),(12,26),(12,27),(12,28),(12,29),(12,30),(12,31),(12,32),(12,33),(12,34),(12,35),(12,36),(12,117),(12,118),(12,119),(12,120),(12,121),(12,122),(12,123),(12,124),(12,125),(12,126),(12,127),(12,128),(12,129),(12,130),(12,131),(12,132),(12,133),(12,134),(12,135),(12,136),(12,137),(12,138),(12,139),(12,140),(12,141),(12,142),(12,143),(12,144),(12,145),(12,146),(12,147),(12,148),(12,149),(12,150),(12,151),(12,152),(12,153),(12,154),(12,155),(12,156),(12,157),(12,158),(12,159),(12,160),(12,161),(12,162),(12,163),(12,164),(12,165),(12,166),(12,167),(12,168),(12,169),(12,170),(12,171),(12,172),(12,173),(12,174),(12,175),(12,176),(12,177),(12,178),(12,179),(12,180),(12,181),(12,182),(12,183),(12,184),(12,185),(12,186),(12,187),(12,188),(12,189),(12,190),(12,191),(12,192),(12,193),(12,194),(12,195),(12,196),(12,197),(12,198),(12,199),(12,200),(12,201),(12,202),(12,203),(12,204),(12,205),(12,206),(12,207),(12,208),(12,209),(12,210),(12,211),(12,212),(12,213),(12,214),(12,215),(12,216),(12,217),(12,218),(12,219),(12,220),(12,221),(12,222),(12,223),(12,224),(12,225),(12,226),(12,227),(12,228),(12,229),(12,230),(12,231),(12,232),(12,233),(12,234),(12,235),(12,236),(12,237),(12,238),(12,239),(12,240),(12,241),(12,242),(12,243),(12,244),(12,245),(12,246),(12,247),(12,248),(12,249),(12,250),(12,251),(12,252),(12,253),(12,254),(12,255),(12,256),(12,257),(12,258),(12,259),(12,260),(12,261),(12,262),(12,263),(12,264),(12,265),(12,266),(12,267),(12,268),(12,269),(12,270),(12,271),(12,272),(12,273),(12,274),(12,275),(12,276),(12,277),(12,278),(12,279),(12,280),(12,281),(12,282),(12,283),(12,284),(12,285),(12,286),(12,287),(12,288),(12,289),(12,290),(12,291),(12,292),(12,293),(12,294),(12,295),(12,296),(12,297),(12,298),(12,299),(12,300),(12,301),(12,302),(12,303),(12,304),(12,305),(12,306),(12,307),(12,308),(12,309),(12,310),(12,311),(12,312),(12,313),(12,314),(12,315),(12,316),(12,317),(12,318),(12,319),(12,320),(12,321),(12,322),(12,323),(12,324),(12,325),(12,326),(12,327),(12,328),(12,329),(12,330),(12,331),(12,332),(12,333),(12,334),(12,335),(12,336),(12,337),(12,338),(12,339),(12,340),(12,341),(12,342),(12,343),(12,344),(12,345),(12,346),(12,347),(12,348),(12,349),(12,350),(12,351),(12,352),(12,353),(12,354),(12,355),(12,356),(12,357),(12,358),(12,359),(12,360),(12,361),(12,362),(12,363),(12,364),(12,365),(12,366),(12,367),(12,368),(12,369),(12,370),(12,371),(12,372),(12,373),(12,374),(12,375),(12,376),(12,377),(12,378),(12,379),(12,380),(12,381),(12,382),(12,383),(12,384),(13,13),(13,14),(13,15),(13,16),(13,121),(13,122),(13,123),(13,124),(13,125),(13,126),(13,127),(13,128),(13,129),(13,130),(13,131),(13,132),(13,133),(13,134),(13,135),(13,136),(13,137),(13,138),(13,139),(13,140),(13,141),(13,142),(13,143),(13,144),(13,145),(13,146),(13,147),(13,148),(13,149),(13,150),(13,151),(13,152),(13,153),(13,154),(13,155),(13,156),(13,157),(13,158),(13,159),(13,160),(13,161),(13,162),(13,163),(13,164),(13,165),(13,166),(13,167),(13,168),(14,9),(14,10),(14,11),(14,12),(14,13),(14,14),(14,15),(14,16),(14,17),(14,18),(14,19),(14,20),(14,21),(14,22),(14,23),(14,24),(14,25),(14,26),(14,27),(14,28),(14,29),(14,30),(14,31),(14,32),(14,33),(14,34),(14,35),(14,36),(14,93),(14,94),(14,95),(14,96),(14,97),(14,98),(14,99),(14,100),(14,101),(14,102),(14,103),(14,104),(14,105),(14,106),(14,107),(14,108),(14,109),(14,110),(14,111),(14,112),(14,113),(14,114),(14,115),(14,116),(14,117),(14,118),(14,119),(14,120),(14,121),(14,122),(14,123),(14,124),(14,125),(14,126),(14,127),(14,128),(14,129),(14,130),(14,131),(14,132),(14,133),(14,134),(14,135),(14,136),(14,137),(14,138),(14,139),(14,140),(14,141),(14,142),(14,143),(14,144),(14,145),(14,146),(14,147),(14,148),(14,149),(14,150),(14,151),(14,152),(14,153),(14,154),(14,155),(14,156),(14,157),(14,158),(14,159),(14,160),(14,161),(14,162),(14,163),(14,164),(14,165),(14,166),(14,167),(14,168),(14,169),(14,170),(14,171),(14,172),(14,173),(14,174),(14,175),(14,176),(14,177),(14,178),(14,179),(14,180),(14,181),(14,182),(14,183),(14,184),(14,185),(14,186),(14,187),(14,188),(14,189),(14,190),(14,191),(14,192),(14,193),(14,194),(14,195),(14,196),(14,197),(14,198),(14,199),(14,200),(14,201),(14,202),(14,203),(14,204),(14,205),(14,206),(14,207),(14,208),(14,209),(14,210),(14,211),(14,212),(14,213),(14,214),(14,215),(14,216),(14,217),(14,218),(14,219),(14,220),(14,221),(14,222),(14,223),(14,224),(14,225),(14,226),(14,227),(14,228),(14,229),(14,230),(14,231),(14,232),(14,233),(14,234),(14,235),(14,236),(14,237),(14,238),(14,239),(14,240),(14,241),(14,242),(14,243),(14,244),(14,245),(14,246),(14,247),(14,248),(14,249),(14,250),(14,251),(14,252),(14,253),(14,254),(14,255),(14,256),(14,257),(14,258),(14,259),(14,260),(14,261),(14,262),(14,263),(14,264),(14,265),(14,266),(14,267),(14,268),(14,269),(14,270),(14,271),(14,272),(14,273),(14,274),(14,275),(14,276),(14,277),(14,278),(14,279),(14,280),(14,281),(14,282),(14,283),(14,284),(14,285),(14,286),(14,287),(14,288),(14,289),(14,290),(14,291),(14,292),(14,293),(14,294),(14,295),(14,296),(14,297),(14,298),(14,299),(14,300),(14,301),(14,302),(14,303),(14,304),(14,305),(14,306),(14,307),(14,308),(14,309),(14,310),(14,311),(14,312),(14,313),(14,314),(14,315),(14,316),(14,317),(14,318),(14,319),(14,320),(14,321),(14,322),(14,323),(14,324),(14,325),(14,326),(14,327),(14,328),(14,329),(14,330),(14,331),(14,332),(14,333),(14,334),(14,335),(14,336),(14,337),(14,338),(14,339),(14,340),(14,341),(14,342),(14,343),(14,344),(14,345),(14,346),(14,347),(14,348),(14,349),(14,350),(14,351),(14,352),(14,353),(14,354),(14,355),(14,356),(14,357),(14,358),(14,359),(14,360),(14,361),(14,362),(14,363),(14,364),(14,365),(14,366),(14,367),(14,368),(14,369),(14,370),(14,371),(14,372),(14,373),(14,374),(14,375),(14,376),(14,377),(14,378),(14,379),(14,380),(14,381),(14,382),(14,383),(14,384),(15,5),(15,6),(15,7),(15,8),(15,9),(15,10),(15,11),(15,12),(15,57),(15,58),(15,59),(15,60),(15,61),(15,62),(15,63),(15,64),(15,65),(15,66),(15,67),(15,68),(15,69),(15,70),(15,71),(15,72),(15,73),(15,74),(15,75),(15,76),(15,77),(15,78),(15,79),(15,80),(15,81),(15,82),(15,83),(15,84),(15,85),(15,86),(15,87),(15,88),(15,89),(15,90),(15,91),(15,92),(15,93),(15,94),(15,95),(15,96),(15,97),(15,98),(15,99),(15,100),(15,101),(15,102),(15,103),(15,104),(15,105),(15,106),(15,107),(15,108),(15,109),(15,110),(15,111),(15,112),(15,113),(15,114),(15,115),(15,116),(15,117),(15,118),(15,119),(15,120),(16,5),(16,6),(16,7),(16,8),(16,9),(16,10),(16,11),(16,12),(16,13),(16,14),(16,15),(16,16),(16,17),(16,18),(16,19),(16,20),(16,21),(16,22),(16,23),(16,24),(16,25),(16,26),(16,27),(16,28),(16,29),(16,30),(16,31),(16,32),(16,33),(16,34),(16,35),(16,36),(16,77),(16,78),(16,79),(16,80),(16,81),(16,82),(16,83),(16,84),(16,85),(16,86),(16,87),(16,88),(16,89),(16,90),(16,91),(16,92),(16,93),(16,94),(16,95),(16,96),(16,97),(16,98),(16,99),(16,100),(16,101),(16,102),(16,103),(16,104),(16,105),(16,106),(16,107),(16,108),(16,109),(16,110),(16,111),(16,112),(16,113),(16,114),(16,115),(16,116),(16,117),(16,118),(16,119),(16,120),(16,121),(16,122),(16,123),(16,124),(16,125),(16,126),(16,127),(16,128),(16,129),(16,130),(16,131),(16,132),(16,133),(16,134),(16,135),(16,136),(16,137),(16,138),(16,139),(16,140),(16,141),(16,142),(16,143),(16,144),(16,145),(16,146),(16,147),(16,148),(16,149),(16,150),(16,151),(16,152),(16,153),(16,154),(16,155),(16,156),(16,157),(16,158),(16,159),(16,160),(16,161),(16,162),(16,163),(16,164),(16,165),(16,166),(16,167),(16,168),(16,169),(16,170),(16,171),(16,172),(16,173),(16,174),(16,175),(16,176),(16,177),(16,178),(16,179),(16,180),(16,181),(16,182),(16,183),(16,184),(16,185),(16,186),(16,187),(16,188),(16,189),(16,190),(16,191),(16,192),(16,193),(16,194),(16,195),(16,196),(16,197),(16,198),(16,199),(16,200),(16,201),(16,202),(16,203),(16,204),(16,205),(16,206),(16,207),(16,208),(16,209),(16,210),(16,211),(16,212),(16,213),(16,214),(16,215),(16,216),(16,217),(16,218),(16,219),(16,220),(16,221),(16,222),(16,223),(16,224),(16,225),(16,226),(16,227),(16,228),(16,229),(16,230),(16,231),(16,232),(16,233),(16,234),(16,235),(16,236),(16,237),(16,238),(16,239),(16,240),(16,241),(16,242),(16,243),(16,244),(16,245),(16,246),(16,247),(16,248),(16,249),(16,250),(16,251),(16,252),(16,253),(16,254),(16,255),(16,256),(16,257),(16,258),(16,259),(16,260),(16,261),(16,262),(16,263),(16,264),(16,265),(16,266),(16,267),(16,268),(16,269),(16,270),(16,271),(16,272),(16,273),(16,274),(16,275),(16,276),(16,277),(16,278),(16,279),(16,280),(16,281),(16,282),(16,283),(16,284),(16,285),(16,286),(16,287),(16,288),(16,289),(16,290),(16,291),(16,292),(16,293),(16,294),(16,295),(16,296),(16,297),(16,298),(16,299),(16,300),(16,301),(16,302),(16,303),(16,304),(16,305),(16,306),(16,307),(16,308),(16,309),(16,310),(16,311),(16,312),(16,313),(16,314),(16,315),(16,316),(16,317),(16,318),(16,319),(16,320),(16,321),(16,322),(16,323),(16,324),(16,325),(16,326),(16,327),(16,328),(16,329),(16,330),(16,331),(16,332),(16,333),(16,334),(16,335),(16,336),(16,337),(16,338),(16,339),(16,340),(16,341),(16,342),(16,343),(16,344),(16,345),(16,346),(16,347),(16,348),(16,349),(16,350),(16,351),(16,352),(16,353),(16,354),(16,355),(16,356),(16,357),(16,358),(16,359),(16,360),(16,361),(16,362),(16,363),(16,364),(16,365),(16,366),(16,367),(16,368),(16,369),(16,370),(16,371),(16,372),(16,373),(16,374),(16,375),(16,376),(16,377),(16,378),(16,379),(16,380),(16,381),(16,382),(16,383),(16,384),(17,13),(17,14),(17,15),(17,16),(17,17),(17,18),(17,19),(17,20),(17,49),(17,50),(17,51),(17,52),(17,53),(17,54),(17,55),(17,56),(17,121),(17,122),(17,123),(17,124),(17,125),(17,126),(17,127),(17,128),(17,129),(17,130),(17,131),(17,132),(17,133),(17,134),(17,135),(17,136),(17,137),(17,138),(17,139),(17,140),(17,141),(17,142),(17,143),(17,144),(17,145),(17,146),(17,147),(17,148),(17,149),(17,150),(17,151),(17,152),(17,153),(17,154),(17,155),(17,156),(17,157),(17,158),(17,159),(17,160),(17,161),(17,162),(17,163),(17,164),(17,165),(17,166),(17,167),(17,168),(17,169),(17,170),(17,171),(17,172),(17,173),(17,174),(17,175),(17,176),(17,177),(17,178),(17,179),(17,180),(17,181),(17,182),(17,183),(17,184),(17,185),(17,186),(17,187),(17,188),(17,189),(17,190),(17,191),(17,192),(17,193),(17,194),(17,195),(17,196),(18,5),(18,6),(18,7),(18,8),(18,9),(18,10),(18,11),(18,12),(18,13),(18,14),(18,15),(18,16),(18,17),(18,18),(18,19),(18,20),(18,21),(18,22),(18,23),(18,24),(18,25),(18,26),(18,27),(18,28),(18,29),(18,30),(18,31),(18,32),(18,33),(18,34),(18,35),(18,36),(18,57),(18,58),(18,59),(18,60),(18,61),(18,62),(18,63),(18,64),(18,65),(18,66),(18,67),(18,68),(18,69),(18,70),(18,71),(18,72),(18,73),(18,74),(18,75),(18,76),(18,77),(18,78),(18,79),(18,80),(18,81),(18,82),(18,83),(18,84),(18,85),(18,86),(18,87),(18,88),(18,89),(18,90),(18,91),(18,92),(18,93),(18,94),(18,95),(18,96),(18,97),(18,98),(18,99),(18,100),(18,101),(18,102),(18,103),(18,104),(18,105),(18,106),(18,107),(18,108),(18,109),(18,110),(18,111),(18,112),(18,113),(18,114),(18,115),(18,116),(18,117),(18,118),(18,119),(18,120),(18,121),(18,122),(18,123),(18,124),(18,125),(18,126),(18,127),(18,128),(18,129),(18,130),(18,131),(18,132),(18,133),(18,134),(18,135),(18,136),(18,137),(18,138),(18,139),(18,140),(18,141),(18,142),(18,143),(18,144),(18,145),(18,146),(18,147),(18,148),(18,149),(18,150),(18,151),(18,152),(18,153),(18,154),(18,155),(18,156),(18,157),(18,158),(18,159),(18,160),(18,161),(18,162),(18,163),(18,164),(18,165),(18,166),(18,167),(18,168),(18,169),(18,170),(18,171),(18,172),(18,173),(18,174),(18,175),(18,176),(18,177),(18,178),(18,179),(18,180),(18,181),(18,182),(18,183),(18,184),(18,185),(18,186),(18,187),(18,188),(18,189),(18,190),(18,191),(18,192),(18,193),(18,194),(18,195),(18,196),(18,197),(18,198),(18,199),(18,200),(18,201),(18,202),(18,203),(18,204),(18,205),(18,206),(18,207),(18,208),(18,209),(18,210),(18,211),(18,212),(18,213),(18,214),(18,215),(18,216),(18,217),(18,218),(18,219),(18,220),(18,221),(18,222),(18,223),(18,224),(18,225),(18,226),(18,227),(18,228),(18,229),(18,230),(18,231),(18,232),(18,233),(18,234),(18,235),(18,236),(18,237),(18,238),(18,239),(18,240),(18,241),(18,242),(18,243),(18,244),(18,245),(18,246),(18,247),(18,248),(18,249),(18,250),(18,251),(18,252),(18,253),(18,254),(18,255),(18,256),(18,257),(18,258),(18,259),(18,260),(18,261),(18,262),(18,263),(18,264),(18,265),(18,266),(18,267),(18,268),(18,269),(18,270),(18,271),(18,272),(18,273),(18,274),(18,275),(18,276),(18,277),(18,278),(18,279),(18,280),(18,281),(18,282),(18,283),(18,284),(18,285),(18,286),(18,287),(18,288),(18,289),(18,290),(18,291),(18,292),(18,293),(18,294),(18,295),(18,296),(18,297),(18,298),(18,299),(18,300),(18,301),(18,302),(18,303),(18,304),(18,305),(18,306),(18,307),(18,308),(18,309),(18,310),(18,311),(18,312),(18,313),(18,314),(18,315),(18,316),(18,317),(18,318),(18,319),(18,320),(18,321),(18,322),(18,323),(18,324),(18,325),(18,326),(18,327),(18,328),(18,329),(18,330),(18,331),(18,332),(18,333),(18,334),(18,335),(18,336),(18,337),(18,338),(18,339),(18,340),(18,341),(18,342),(18,343),(18,344),(18,345),(18,346),(18,347),(18,348),(18,349),(18,350),(18,351),(18,352),(18,353),(18,354),(18,355),(18,356),(18,357),(18,358),(18,359),(18,360),(18,361),(18,362),(18,363),(18,364),(18,365),(18,366),(18,367),(18,368),(18,369),(18,370),(18,371),(18,372),(18,373),(18,374),(18,375),(18,376),(18,377),(18,378),(18,379),(18,380),(18,381),(18,382),(18,383),(18,384),(19,9),(19,10),(19,11),(19,12),(19,13),(19,14),(19,15),(19,16),(19,81),(19,82),(19,83),(19,84),(19,85),(19,86),(19,87),(19,88),(19,89),(19,90),(19,91),(19,92),(19,93),(19,94),(19,95),(19,96),(19,97),(19,98),(19,99),(19,100),(19,101),(19,102),(19,103),(19,104),(19,105),(19,106),(19,107),(19,108),(19,109),(19,110),(19,111),(19,112),(19,113),(19,114),(19,115),(19,116),(19,117),(19,118),(19,119),(19,120),(19,121),(19,122),(19,123),(19,124),(19,125),(19,126),(19,127),(19,128),(19,129),(19,130),(19,131),(19,132),(19,133),(19,134),(19,135),(19,136),(19,137),(19,138),(19,139),(19,140),(19,141),(19,142),(19,143),(19,144),(19,145),(19,146),(19,147),(19,148),(19,149),(19,150),(19,151),(19,152),(19,153),(19,154),(19,155),(19,156),(19,157),(19,158),(19,159),(19,160),(19,161),(19,162),(19,163),(19,164),(19,165),(19,166),(19,167),(19,168),(19,169),(19,170),(19,171),(19,172),(19,173),(19,174),(19,175),(19,176),(19,177),(19,178),(19,179),(19,180),(19,181),(19,182),(19,183),(19,184),(19,185),(19,186),(19,187),(19,188); /*!40000 ALTER TABLE `gantt_jobs_resources_visu` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `job_dependencies` -- DROP TABLE IF EXISTS `job_dependencies`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `job_dependencies` ( `job_id` int(10) unsigned NOT NULL, `job_id_required` int(10) unsigned NOT NULL, `job_dependency_index` enum('CURRENT','LOG') NOT NULL default 'CURRENT', PRIMARY KEY (`job_id`,`job_id_required`), KEY `id` (`job_id`), KEY `log` (`job_dependency_index`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `job_dependencies` -- LOCK TABLES `job_dependencies` WRITE; /*!40000 ALTER TABLE `job_dependencies` DISABLE KEYS */; /*!40000 ALTER TABLE `job_dependencies` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `job_resource_descriptions` -- DROP TABLE IF EXISTS `job_resource_descriptions`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `job_resource_descriptions` ( `res_job_group_id` int(10) unsigned NOT NULL, `res_job_resource_type` varchar(255) NOT NULL, `res_job_value` int(11) NOT NULL, `res_job_order` int(10) unsigned NOT NULL default '0', `res_job_index` enum('CURRENT','LOG') NOT NULL default 'CURRENT', PRIMARY KEY (`res_job_group_id`,`res_job_resource_type`,`res_job_order`), KEY `resgroup` (`res_job_group_id`), KEY `log` (`res_job_index`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `job_resource_descriptions` -- LOCK TABLES `job_resource_descriptions` WRITE; /*!40000 ALTER TABLE `job_resource_descriptions` DISABLE KEYS */; INSERT INTO `job_resource_descriptions` VALUES (1,'resource_id',-1,0,'CURRENT'),(2,'host',48,0,'CURRENT'),(3,'host',83,0,'CURRENT'),(4,'host',54,0,'CURRENT'),(5,'host',88,0,'CURRENT'),(6,'host',59,0,'CURRENT'),(7,'host',93,0,'CURRENT'),(8,'host',64,0,'CURRENT'),(9,'host',3,0,'CURRENT'),(10,'host',69,0,'CURRENT'),(11,'host',8,0,'CURRENT'),(12,'host',74,0,'CURRENT'),(13,'host',13,0,'CURRENT'),(14,'host',80,0,'CURRENT'),(15,'host',18,0,'CURRENT'),(16,'host',85,0,'CURRENT'),(17,'host',23,0,'CURRENT'),(18,'host',90,0,'CURRENT'),(19,'host',29,0,'CURRENT'),(20,'host',95,0,'CURRENT'),(21,'host',34,0,'CURRENT'),(22,'host',5,0,'CURRENT'),(23,'host',39,0,'CURRENT'),(24,'host',10,0,'CURRENT'),(25,'host',44,0,'CURRENT'),(26,'host',79,0,'CURRENT'),(27,'host',50,0,'CURRENT'),(28,'host',55,0,'CURRENT'),(29,'host',89,0,'CURRENT'),(30,'host',60,0,'CURRENT'),(31,'host',95,0,'CURRENT'),(32,'host',65,0,'CURRENT'),(33,'host',4,0,'CURRENT'),(34,'host',70,0,'CURRENT'),(35,'host',9,0,'CURRENT'),(36,'host',76,0,'CURRENT'),(37,'host',14,0,'CURRENT'),(38,'host',81,0,'CURRENT'),(39,'host',19,0,'CURRENT'),(40,'host',86,0,'CURRENT'),(41,'host',25,0,'CURRENT'),(42,'host',91,0,'CURRENT'),(43,'host',30,0,'CURRENT'),(44,'host',96,0,'CURRENT'),(45,'host',35,0,'CURRENT'),(46,'host',6,0,'CURRENT'),(47,'host',40,0,'CURRENT'),(48,'host',75,0,'CURRENT'),(49,'host',45,0,'CURRENT'),(50,'host',80,0,'CURRENT'),(51,'host',51,0,'CURRENT'),(52,'host',85,0,'CURRENT'),(53,'host',56,0,'CURRENT'),(54,'host',90,0,'CURRENT'),(55,'host',61,0,'CURRENT'),(56,'host',96,0,'CURRENT'),(57,'host',66,0,'CURRENT'),(58,'host',5,0,'CURRENT'),(59,'host',71,0,'CURRENT'),(60,'host',10,0,'CURRENT'),(61,'host',77,0,'CURRENT'),(62,'host',15,0,'CURRENT'),(63,'host',82,0,'CURRENT'),(64,'host',20,0,'CURRENT'),(65,'host',87,0,'CURRENT'),(66,'host',26,0,'CURRENT'),(67,'host',92,0,'CURRENT'),(68,'host',31,0,'CURRENT'),(69,'host',1,0,'CURRENT'),(70,'host',36,0,'CURRENT'),(71,'host',7,0,'CURRENT'),(72,'host',41,0,'CURRENT'),(73,'host',76,0,'CURRENT'),(74,'host',46,0,'CURRENT'),(75,'host',81,0,'CURRENT'),(76,'host',52,0,'CURRENT'),(77,'host',86,0,'CURRENT'),(78,'host',57,0,'CURRENT'),(79,'host',91,0,'CURRENT'),(80,'host',62,0,'CURRENT'),(81,'host',1,0,'CURRENT'),(82,'host',67,0,'CURRENT'),(83,'host',6,0,'CURRENT'),(84,'host',73,0,'CURRENT'),(85,'host',11,0,'CURRENT'),(86,'host',78,0,'CURRENT'),(87,'host',16,0,'CURRENT'),(88,'host',83,0,'CURRENT'),(89,'host',22,0,'CURRENT'),(90,'host',88,0,'CURRENT'),(91,'host',27,0,'CURRENT'),(92,'host',93,0,'CURRENT'),(93,'host',32,0,'CURRENT'),(94,'host',3,0,'CURRENT'),(95,'host',37,0,'CURRENT'),(96,'host',8,0,'CURRENT'),(97,'host',42,0,'CURRENT'),(98,'host',77,0,'CURRENT'),(99,'host',48,0,'CURRENT'),(100,'host',82,0,'CURRENT'),(101,'host',53,0,'CURRENT'); /*!40000 ALTER TABLE `job_resource_descriptions` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `job_resource_groups` -- DROP TABLE IF EXISTS `job_resource_groups`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `job_resource_groups` ( `res_group_id` int(10) unsigned NOT NULL auto_increment, `res_group_moldable_id` int(10) unsigned NOT NULL, `res_group_property` text, `res_group_index` enum('CURRENT','LOG') NOT NULL default 'CURRENT', PRIMARY KEY (`res_group_id`), KEY `moldable_job` (`res_group_moldable_id`), KEY `log` (`res_group_index`) ) ENGINE=MyISAM AUTO_INCREMENT=102 DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `job_resource_groups` -- LOCK TABLES `job_resource_groups` WRITE; /*!40000 ALTER TABLE `job_resource_groups` DISABLE KEYS */; INSERT INTO `job_resource_groups` VALUES (1,1,'type = \'default\'','CURRENT'),(2,2,'type = \'default\'','CURRENT'),(3,3,'type = \'default\'','CURRENT'),(4,4,'type = \'default\'','CURRENT'),(5,5,'type = \'default\'','CURRENT'),(6,6,'type = \'default\'','CURRENT'),(7,7,'type = \'default\'','CURRENT'),(8,8,'type = \'default\'','CURRENT'),(9,9,'type = \'default\'','CURRENT'),(10,10,'type = \'default\'','CURRENT'),(11,11,'type = \'default\'','CURRENT'),(12,12,'type = \'default\'','CURRENT'),(13,13,'type = \'default\'','CURRENT'),(14,14,'type = \'default\'','CURRENT'),(15,15,'type = \'default\'','CURRENT'),(16,16,'type = \'default\'','CURRENT'),(17,17,'type = \'default\'','CURRENT'),(18,18,'type = \'default\'','CURRENT'),(19,19,'type = \'default\'','CURRENT'),(20,20,'type = \'default\'','CURRENT'),(21,21,'type = \'default\'','CURRENT'),(22,22,'type = \'default\'','CURRENT'),(23,23,'type = \'default\'','CURRENT'),(24,24,'type = \'default\'','CURRENT'),(25,25,'type = \'default\'','CURRENT'),(26,26,'type = \'default\'','CURRENT'),(27,27,'type = \'default\'','CURRENT'),(28,28,'type = \'default\'','CURRENT'),(29,29,'type = \'default\'','CURRENT'),(30,30,'type = \'default\'','CURRENT'),(31,31,'type = \'default\'','CURRENT'),(32,32,'type = \'default\'','CURRENT'),(33,33,'type = \'default\'','CURRENT'),(34,34,'type = \'default\'','CURRENT'),(35,35,'type = \'default\'','CURRENT'),(36,36,'type = \'default\'','CURRENT'),(37,37,'type = \'default\'','CURRENT'),(38,38,'type = \'default\'','CURRENT'),(39,39,'type = \'default\'','CURRENT'),(40,40,'type = \'default\'','CURRENT'),(41,41,'type = \'default\'','CURRENT'),(42,42,'type = \'default\'','CURRENT'),(43,43,'type = \'default\'','CURRENT'),(44,44,'type = \'default\'','CURRENT'),(45,45,'type = \'default\'','CURRENT'),(46,46,'type = \'default\'','CURRENT'),(47,47,'type = \'default\'','CURRENT'),(48,48,'type = \'default\'','CURRENT'),(49,49,'type = \'default\'','CURRENT'),(50,50,'type = \'default\'','CURRENT'),(51,51,'type = \'default\'','CURRENT'),(52,52,'type = \'default\'','CURRENT'),(53,53,'type = \'default\'','CURRENT'),(54,54,'type = \'default\'','CURRENT'),(55,55,'type = \'default\'','CURRENT'),(56,56,'type = \'default\'','CURRENT'),(57,57,'type = \'default\'','CURRENT'),(58,58,'type = \'default\'','CURRENT'),(59,59,'type = \'default\'','CURRENT'),(60,60,'type = \'default\'','CURRENT'),(61,61,'type = \'default\'','CURRENT'),(62,62,'type = \'default\'','CURRENT'),(63,63,'type = \'default\'','CURRENT'),(64,64,'type = \'default\'','CURRENT'),(65,65,'type = \'default\'','CURRENT'),(66,66,'type = \'default\'','CURRENT'),(67,67,'type = \'default\'','CURRENT'),(68,68,'type = \'default\'','CURRENT'),(69,69,'type = \'default\'','CURRENT'),(70,70,'type = \'default\'','CURRENT'),(71,71,'type = \'default\'','CURRENT'),(72,72,'type = \'default\'','CURRENT'),(73,73,'type = \'default\'','CURRENT'),(74,74,'type = \'default\'','CURRENT'),(75,75,'type = \'default\'','CURRENT'),(76,76,'type = \'default\'','CURRENT'),(77,77,'type = \'default\'','CURRENT'),(78,78,'type = \'default\'','CURRENT'),(79,79,'type = \'default\'','CURRENT'),(80,80,'type = \'default\'','CURRENT'),(81,81,'type = \'default\'','CURRENT'),(82,82,'type = \'default\'','CURRENT'),(83,83,'type = \'default\'','CURRENT'),(84,84,'type = \'default\'','CURRENT'),(85,85,'type = \'default\'','CURRENT'),(86,86,'type = \'default\'','CURRENT'),(87,87,'type = \'default\'','CURRENT'),(88,88,'type = \'default\'','CURRENT'),(89,89,'type = \'default\'','CURRENT'),(90,90,'type = \'default\'','CURRENT'),(91,91,'type = \'default\'','CURRENT'),(92,92,'type = \'default\'','CURRENT'),(93,93,'type = \'default\'','CURRENT'),(94,94,'type = \'default\'','CURRENT'),(95,95,'type = \'default\'','CURRENT'),(96,96,'type = \'default\'','CURRENT'),(97,97,'type = \'default\'','CURRENT'),(98,98,'type = \'default\'','CURRENT'),(99,99,'type = \'default\'','CURRENT'),(100,100,'type = \'default\'','CURRENT'),(101,101,'type = \'default\'','CURRENT'); /*!40000 ALTER TABLE `job_resource_groups` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `job_state_logs` -- DROP TABLE IF EXISTS `job_state_logs`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `job_state_logs` ( `job_state_log_id` int(10) unsigned NOT NULL auto_increment, `job_id` int(10) unsigned NOT NULL, `job_state` enum('Waiting','Hold','toLaunch','toError','toAckReservation','Launching','Finishing','Running','Suspended','Resuming','Terminated','Error') NOT NULL, `date_start` int(10) unsigned NOT NULL, `date_stop` int(10) unsigned default '0', PRIMARY KEY (`job_state_log_id`), KEY `id` (`job_id`), KEY `state` (`job_state`) ) ENGINE=MyISAM AUTO_INCREMENT=105 DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `job_state_logs` -- LOCK TABLES `job_state_logs` WRITE; /*!40000 ALTER TABLE `job_state_logs` DISABLE KEYS */; INSERT INTO `job_state_logs` VALUES (1,1,'Waiting',1224778627,1224778628),(2,1,'toLaunch',1224778628,1224778628),(3,1,'Launching',1224778628,1224778629),(4,1,'Running',1224778629,0),(5,2,'Waiting',1224778637,0),(6,3,'Waiting',1224778638,0),(7,4,'Waiting',1224778639,0),(8,5,'Waiting',1224778640,0),(9,6,'Waiting',1224778640,0),(10,7,'Waiting',1224778641,0),(11,8,'Waiting',1224778642,0),(12,9,'Waiting',1224778642,0),(13,10,'Waiting',1224778643,0),(14,11,'Waiting',1224778644,0),(15,12,'Waiting',1224778645,0),(16,13,'Waiting',1224778645,0),(17,14,'Waiting',1224778646,0),(18,15,'Waiting',1224778647,0),(19,16,'Waiting',1224778647,0),(20,17,'Waiting',1224778648,0),(21,18,'Waiting',1224778648,0),(22,19,'Waiting',1224778649,0),(23,20,'Waiting',1224778650,0),(24,21,'Waiting',1224778650,0),(25,22,'Waiting',1224778651,0),(26,23,'Waiting',1224778652,0),(27,24,'Waiting',1224778652,0),(28,25,'Waiting',1224778653,0),(29,26,'Waiting',1224778653,0),(30,27,'Waiting',1224778654,0),(31,28,'Waiting',1224778655,0),(32,29,'Waiting',1224778655,0),(33,30,'Waiting',1224778656,0),(34,31,'Waiting',1224778657,0),(35,32,'Waiting',1224778657,0),(36,33,'Waiting',1224778658,0),(37,34,'Waiting',1224778658,0),(38,35,'Waiting',1224778659,0),(39,36,'Waiting',1224778660,0),(40,37,'Waiting',1224778660,0),(41,38,'Waiting',1224778661,0),(42,39,'Waiting',1224778661,0),(43,40,'Waiting',1224778662,0),(44,41,'Waiting',1224778663,0),(45,42,'Waiting',1224778663,0),(46,43,'Waiting',1224778664,0),(47,44,'Waiting',1224778665,0),(48,45,'Waiting',1224778665,0),(49,46,'Waiting',1224778666,0),(50,47,'Waiting',1224778666,0),(51,48,'Waiting',1224778667,0),(52,49,'Waiting',1224778668,0),(53,50,'Waiting',1224778668,0),(54,51,'Waiting',1224778669,0),(55,52,'Waiting',1224778669,0),(56,53,'Waiting',1224778670,0),(57,54,'Waiting',1224778671,0),(58,55,'Waiting',1224778671,0),(59,56,'Waiting',1224778672,0),(60,57,'Waiting',1224778673,0),(61,58,'Waiting',1224778673,0),(62,59,'Waiting',1224778674,0),(63,60,'Waiting',1224778674,0),(64,61,'Waiting',1224778675,0),(65,62,'Waiting',1224778676,0),(66,63,'Waiting',1224778676,0),(67,64,'Waiting',1224778677,0),(68,65,'Waiting',1224778678,0),(69,66,'Waiting',1224778678,0),(70,67,'Waiting',1224778679,0),(71,68,'Waiting',1224778680,0),(72,69,'Waiting',1224778680,0),(73,70,'Waiting',1224778681,0),(74,71,'Waiting',1224778681,0),(75,72,'Waiting',1224778682,0),(76,73,'Waiting',1224778683,0),(77,74,'Waiting',1224778683,0),(78,75,'Waiting',1224778684,0),(79,76,'Waiting',1224778684,0),(80,77,'Waiting',1224778685,0),(81,78,'Waiting',1224778686,0),(82,79,'Waiting',1224778686,0),(83,80,'Waiting',1224778687,0),(84,81,'Waiting',1224778688,0),(85,82,'Waiting',1224778688,0),(86,83,'Waiting',1224778689,0),(87,84,'Waiting',1224778689,0),(88,85,'Waiting',1224778690,0),(89,86,'Waiting',1224778691,0),(90,87,'Waiting',1224778691,0),(91,88,'Waiting',1224778692,0),(92,89,'Waiting',1224778692,0),(93,90,'Waiting',1224778693,0),(94,91,'Waiting',1224778694,0),(95,92,'Waiting',1224778694,0),(96,93,'Waiting',1224778695,0),(97,94,'Waiting',1224778696,0),(98,95,'Waiting',1224778696,0),(99,96,'Waiting',1224778697,0),(100,97,'Waiting',1224778697,0),(101,98,'Waiting',1224778698,0),(102,99,'Waiting',1224778699,0),(103,100,'Waiting',1224778699,0),(104,101,'Waiting',1224778700,0); /*!40000 ALTER TABLE `job_state_logs` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `job_types` -- DROP TABLE IF EXISTS `job_types`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `job_types` ( `job_type_id` int(10) unsigned NOT NULL auto_increment, `job_id` int(10) unsigned NOT NULL, `type` varchar(255) NOT NULL, `types_index` enum('CURRENT','LOG') NOT NULL default 'CURRENT', PRIMARY KEY (`job_type_id`), KEY `log` (`types_index`), KEY `type` (`type`), KEY `id_types` (`job_id`) ) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `job_types` -- LOCK TABLES `job_types` WRITE; /*!40000 ALTER TABLE `job_types` DISABLE KEYS */; INSERT INTO `job_types` VALUES (1,1,'cosystem','CURRENT'); /*!40000 ALTER TABLE `job_types` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `jobs` -- DROP TABLE IF EXISTS `jobs`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `jobs` ( `job_id` int(10) unsigned NOT NULL auto_increment, `initial_request` text, `job_name` varchar(100) default NULL, `job_env` text, `job_type` enum('INTERACTIVE','PASSIVE') NOT NULL default 'PASSIVE', `info_type` varchar(255) default NULL, `state` enum('Waiting','Hold','toLaunch','toError','toAckReservation','Launching','Running','Suspended','Resuming','Finishing','Terminated','Error') NOT NULL, `reservation` enum('None','toSchedule','Scheduled') NOT NULL default 'None', `message` varchar(255) NOT NULL, `scheduler_info` varchar(255) NOT NULL, `job_user` varchar(255) NOT NULL, `project` varchar(255) NOT NULL, `job_group` varchar(255) NOT NULL, `command` text, `exit_code` int(11) default NULL, `queue_name` varchar(100) NOT NULL, `properties` text, `launching_directory` text NOT NULL, `submission_time` int(10) unsigned NOT NULL, `start_time` int(10) unsigned NOT NULL, `stop_time` int(10) unsigned NOT NULL, `file_id` int(10) unsigned default NULL, `accounted` enum('YES','NO') NOT NULL default 'NO', `notify` varchar(255) default NULL, `assigned_moldable_job` int(10) unsigned default '0', `checkpoint` int(10) unsigned NOT NULL default '0', `checkpoint_signal` int(11) NOT NULL, `stdout_file` text, `stderr_file` text, `resubmit_job_id` int(10) unsigned default '0', `suspended` enum('YES','NO') NOT NULL default 'NO', PRIMARY KEY (`job_id`), KEY `state` (`state`), KEY `state_id` (`state`,`job_id`), KEY `reservation` (`reservation`), KEY `queue_name` (`queue_name`), KEY `accounted` (`accounted`), KEY `suspended` (`suspended`) ) ENGINE=MyISAM AUTO_INCREMENT=102 DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `jobs` -- LOCK TABLES `jobs` WRITE; /*!40000 ALTER TABLE `jobs` DISABLE KEYS */; INSERT INTO `jobs` VALUES (1,'oarsub -l nodes=ALL,walltime=4 -t cosystem sleep 14400',NULL,NULL,'PASSIVE','caigang:','Running','None','FIFO scheduling OK','FIFO scheduling OK','neyron','default','','sleep 14400',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778627,1224778628,0,NULL,'NO',NULL,1,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(2,'oarsub -l host=48,walltime=9 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','FIFO scheduling OK','FIFO scheduling OK','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778637,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(3,'oarsub -l host=83,walltime=5 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','FIFO scheduling OK','FIFO scheduling OK','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778638,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(4,'oarsub -l host=54,walltime=10 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','FIFO scheduling OK','FIFO scheduling OK','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778639,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(5,'oarsub -l host=88,walltime=6 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','FIFO scheduling OK','FIFO scheduling OK','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778640,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(6,'oarsub -l host=59,walltime=2 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','FIFO scheduling OK','FIFO scheduling OK','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778640,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(7,'oarsub -l host=93,walltime=8 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','FIFO scheduling OK','FIFO scheduling OK','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778641,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(8,'oarsub -l host=64,walltime=3 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','FIFO scheduling OK','FIFO scheduling OK','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778642,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(9,'oarsub -l host=3,walltime=9 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','FIFO scheduling OK','FIFO scheduling OK','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778642,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(10,'oarsub -l host=69,walltime=5 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','FIFO scheduling OK','FIFO scheduling OK','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778643,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(11,'oarsub -l host=8,walltime=1 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','FIFO scheduling OK','FIFO scheduling OK','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778644,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(12,'oarsub -l host=74,walltime=6 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','FIFO scheduling OK','FIFO scheduling OK','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778645,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(13,'oarsub -l host=13,walltime=2 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','FIFO scheduling OK','FIFO scheduling OK','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778645,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(14,'oarsub -l host=80,walltime=8 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','FIFO scheduling OK','FIFO scheduling OK','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778646,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(15,'oarsub -l host=18,walltime=4 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','FIFO scheduling OK','FIFO scheduling OK','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778647,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(16,'oarsub -l host=85,walltime=9 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','FIFO scheduling OK','FIFO scheduling OK','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778647,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(17,'oarsub -l host=23,walltime=5 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','FIFO scheduling OK','FIFO scheduling OK','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778648,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(18,'oarsub -l host=90,walltime=1 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','FIFO scheduling OK','FIFO scheduling OK','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778648,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(19,'oarsub -l host=29,walltime=6 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','FIFO scheduling OK','FIFO scheduling OK','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778649,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(20,'oarsub -l host=95,walltime=2 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 20','Cannot find enough resources which fit for the job 20','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778650,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(21,'oarsub -l host=34,walltime=8 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 21','Cannot find enough resources which fit for the job 21','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778650,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(22,'oarsub -l host=5,walltime=4 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 22','Cannot find enough resources which fit for the job 22','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778651,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(23,'oarsub -l host=39,walltime=9 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 23','Cannot find enough resources which fit for the job 23','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778652,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(24,'oarsub -l host=10,walltime=5 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 24','Cannot find enough resources which fit for the job 24','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778652,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(25,'oarsub -l host=44,walltime=1 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 25','Cannot find enough resources which fit for the job 25','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778653,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(26,'oarsub -l host=79,walltime=7 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 26','Cannot find enough resources which fit for the job 26','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778653,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(27,'oarsub -l host=50,walltime=2 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 27','Cannot find enough resources which fit for the job 27','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778654,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(28,'oarsub -l host=55,walltime=4 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 28','Cannot find enough resources which fit for the job 28','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778655,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(29,'oarsub -l host=89,walltime=10 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 29','Cannot find enough resources which fit for the job 29','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778655,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(30,'oarsub -l host=60,walltime=5 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 30','Cannot find enough resources which fit for the job 30','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778656,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(31,'oarsub -l host=95,walltime=1 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 31','Cannot find enough resources which fit for the job 31','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778657,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(32,'oarsub -l host=65,walltime=7 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 32','Cannot find enough resources which fit for the job 32','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778657,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(33,'oarsub -l host=4,walltime=5 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 33','Cannot find enough resources which fit for the job 33','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778658,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(34,'oarsub -l host=70,walltime=10 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 34','Cannot find enough resources which fit for the job 34','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778658,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(35,'oarsub -l host=9,walltime=6 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 35','Cannot find enough resources which fit for the job 35','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778659,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(36,'oarsub -l host=76,walltime=2 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 36','Cannot find enough resources which fit for the job 36','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778660,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(37,'oarsub -l host=14,walltime=7 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 37','Cannot find enough resources which fit for the job 37','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778660,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(38,'oarsub -l host=81,walltime=3 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 38','Cannot find enough resources which fit for the job 38','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778661,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(39,'oarsub -l host=19,walltime=9 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 39','Cannot find enough resources which fit for the job 39','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778661,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(40,'oarsub -l host=86,walltime=5 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 40','Cannot find enough resources which fit for the job 40','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778662,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(41,'oarsub -l host=25,walltime=10 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 41','Cannot find enough resources which fit for the job 41','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778663,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(42,'oarsub -l host=91,walltime=6 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 42','Cannot find enough resources which fit for the job 42','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778663,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(43,'oarsub -l host=30,walltime=2 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 43','Cannot find enough resources which fit for the job 43','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778664,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(44,'oarsub -l host=96,walltime=8 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 44','Cannot find enough resources which fit for the job 44','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778665,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(45,'oarsub -l host=35,walltime=3 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 45','Cannot find enough resources which fit for the job 45','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778665,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(46,'oarsub -l host=6,walltime=9 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 46','Cannot find enough resources which fit for the job 46','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778666,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(47,'oarsub -l host=40,walltime=5 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 47','Cannot find enough resources which fit for the job 47','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778666,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(48,'oarsub -l host=75,walltime=1 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 48','Cannot find enough resources which fit for the job 48','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778667,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(49,'oarsub -l host=45,walltime=6 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 49','Cannot find enough resources which fit for the job 49','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778668,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(50,'oarsub -l host=80,walltime=2 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 50','Cannot find enough resources which fit for the job 50','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778668,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(51,'oarsub -l host=51,walltime=8 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 51','Cannot find enough resources which fit for the job 51','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778669,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(52,'oarsub -l host=85,walltime=4 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 52','Cannot find enough resources which fit for the job 52','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778669,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(53,'oarsub -l host=56,walltime=9 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 53','Cannot find enough resources which fit for the job 53','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778670,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(54,'oarsub -l host=90,walltime=5 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 54','Cannot find enough resources which fit for the job 54','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778671,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(55,'oarsub -l host=61,walltime=1 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 55','Cannot find enough resources which fit for the job 55','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778671,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(56,'oarsub -l host=96,walltime=6 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 56','Cannot find enough resources which fit for the job 56','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778672,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(57,'oarsub -l host=66,walltime=2 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 57','Cannot find enough resources which fit for the job 57','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778673,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(58,'oarsub -l host=5,walltime=8 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 58','Cannot find enough resources which fit for the job 58','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778673,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(59,'oarsub -l host=71,walltime=4 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 59','Cannot find enough resources which fit for the job 59','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778674,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(60,'oarsub -l host=10,walltime=9 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 60','Cannot find enough resources which fit for the job 60','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778674,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(61,'oarsub -l host=77,walltime=5 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 61','Cannot find enough resources which fit for the job 61','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778675,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(62,'oarsub -l host=15,walltime=1 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 62','Cannot find enough resources which fit for the job 62','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778676,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(63,'oarsub -l host=82,walltime=7 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 63','Cannot find enough resources which fit for the job 63','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778676,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(64,'oarsub -l host=20,walltime=2 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 64','Cannot find enough resources which fit for the job 64','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778677,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(65,'oarsub -l host=87,walltime=8 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 65','Cannot find enough resources which fit for the job 65','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778678,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(66,'oarsub -l host=26,walltime=4 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','Cannot find enough resources which fit for the job 66','Cannot find enough resources which fit for the job 66','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778678,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(67,'oarsub -l host=92,walltime=10 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','','','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778679,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(68,'oarsub -l host=31,walltime=5 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','','','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778680,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(69,'oarsub -l host=1,walltime=1 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','','','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778680,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(70,'oarsub -l host=36,walltime=7 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','','','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778681,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(71,'oarsub -l host=7,walltime=3 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','','','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778681,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(72,'oarsub -l host=41,walltime=8 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','','','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778682,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(73,'oarsub -l host=76,walltime=4 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','','','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778683,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(74,'oarsub -l host=46,walltime=10 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','','','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778683,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(75,'oarsub -l host=81,walltime=5 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','','','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778684,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(76,'oarsub -l host=52,walltime=1 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','','','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778684,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(77,'oarsub -l host=86,walltime=7 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','','','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778685,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(78,'oarsub -l host=57,walltime=3 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','','','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778686,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(79,'oarsub -l host=91,walltime=8 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','','','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778686,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(80,'oarsub -l host=62,walltime=4 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','','','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778687,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(81,'oarsub -l host=1,walltime=10 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','','','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778688,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(82,'oarsub -l host=67,walltime=6 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','','','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778688,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(83,'oarsub -l host=6,walltime=1 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','','','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778689,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(84,'oarsub -l host=73,walltime=7 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','','','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778689,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(85,'oarsub -l host=11,walltime=3 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','','','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778690,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(86,'oarsub -l host=78,walltime=9 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','','','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778691,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(87,'oarsub -l host=16,walltime=4 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','','','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778691,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(88,'oarsub -l host=83,walltime=10 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','','','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778692,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(89,'oarsub -l host=22,walltime=6 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','','','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778692,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(90,'oarsub -l host=88,walltime=2 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','','','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778693,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(91,'oarsub -l host=27,walltime=7 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','','','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778694,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(92,'oarsub -l host=93,walltime=3 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','','','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778694,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(93,'oarsub -l host=32,walltime=9 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','','','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778695,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(94,'oarsub -l host=3,walltime=4 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','','','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778696,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(95,'oarsub -l host=37,walltime=10 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','','','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778696,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(96,'oarsub -l host=8,walltime=6 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','','','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778697,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(97,'oarsub -l host=42,walltime=2 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','','','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778697,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(98,'oarsub -l host=77,walltime=7 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','','','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778698,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(99,'oarsub -l host=48,walltime=3 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','','','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778699,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(100,'oarsub -l host=82,walltime=9 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','','','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778699,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'),(101,'oarsub -l host=53,walltime=5 sleep 3600',NULL,NULL,'PASSIVE','caigang:','Waiting','None','','','neyron','default','','sleep 3600',NULL,'default','desktop_computing = \'NO\'','/home/neyron',1224778700,0,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'); /*!40000 ALTER TABLE `jobs` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `moldable_job_descriptions` -- DROP TABLE IF EXISTS `moldable_job_descriptions`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `moldable_job_descriptions` ( `moldable_id` int(10) unsigned NOT NULL auto_increment, `moldable_job_id` int(10) unsigned NOT NULL, `moldable_walltime` int(10) unsigned NOT NULL, `moldable_index` enum('CURRENT','LOG') NOT NULL default 'CURRENT', PRIMARY KEY (`moldable_id`), KEY `job` (`moldable_job_id`), KEY `log` (`moldable_index`) ) ENGINE=MyISAM AUTO_INCREMENT=102 DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `moldable_job_descriptions` -- LOCK TABLES `moldable_job_descriptions` WRITE; /*!40000 ALTER TABLE `moldable_job_descriptions` DISABLE KEYS */; INSERT INTO `moldable_job_descriptions` VALUES (1,1,14400,'CURRENT'),(2,2,32400,'CURRENT'),(3,3,18000,'CURRENT'),(4,4,36000,'CURRENT'),(5,5,21600,'CURRENT'),(6,6,7200,'CURRENT'),(7,7,28800,'CURRENT'),(8,8,10800,'CURRENT'),(9,9,32400,'CURRENT'),(10,10,18000,'CURRENT'),(11,11,3600,'CURRENT'),(12,12,21600,'CURRENT'),(13,13,7200,'CURRENT'),(14,14,28800,'CURRENT'),(15,15,14400,'CURRENT'),(16,16,32400,'CURRENT'),(17,17,18000,'CURRENT'),(18,18,3600,'CURRENT'),(19,19,21600,'CURRENT'),(20,20,7200,'CURRENT'),(21,21,28800,'CURRENT'),(22,22,14400,'CURRENT'),(23,23,32400,'CURRENT'),(24,24,18000,'CURRENT'),(25,25,3600,'CURRENT'),(26,26,25200,'CURRENT'),(27,27,7200,'CURRENT'),(28,28,14400,'CURRENT'),(29,29,36000,'CURRENT'),(30,30,18000,'CURRENT'),(31,31,3600,'CURRENT'),(32,32,25200,'CURRENT'),(33,33,18000,'CURRENT'),(34,34,36000,'CURRENT'),(35,35,21600,'CURRENT'),(36,36,7200,'CURRENT'),(37,37,25200,'CURRENT'),(38,38,10800,'CURRENT'),(39,39,32400,'CURRENT'),(40,40,18000,'CURRENT'),(41,41,36000,'CURRENT'),(42,42,21600,'CURRENT'),(43,43,7200,'CURRENT'),(44,44,28800,'CURRENT'),(45,45,10800,'CURRENT'),(46,46,32400,'CURRENT'),(47,47,18000,'CURRENT'),(48,48,3600,'CURRENT'),(49,49,21600,'CURRENT'),(50,50,7200,'CURRENT'),(51,51,28800,'CURRENT'),(52,52,14400,'CURRENT'),(53,53,32400,'CURRENT'),(54,54,18000,'CURRENT'),(55,55,3600,'CURRENT'),(56,56,21600,'CURRENT'),(57,57,7200,'CURRENT'),(58,58,28800,'CURRENT'),(59,59,14400,'CURRENT'),(60,60,32400,'CURRENT'),(61,61,18000,'CURRENT'),(62,62,3600,'CURRENT'),(63,63,25200,'CURRENT'),(64,64,7200,'CURRENT'),(65,65,28800,'CURRENT'),(66,66,14400,'CURRENT'),(67,67,36000,'CURRENT'),(68,68,18000,'CURRENT'),(69,69,3600,'CURRENT'),(70,70,25200,'CURRENT'),(71,71,10800,'CURRENT'),(72,72,28800,'CURRENT'),(73,73,14400,'CURRENT'),(74,74,36000,'CURRENT'),(75,75,18000,'CURRENT'),(76,76,3600,'CURRENT'),(77,77,25200,'CURRENT'),(78,78,10800,'CURRENT'),(79,79,28800,'CURRENT'),(80,80,14400,'CURRENT'),(81,81,36000,'CURRENT'),(82,82,21600,'CURRENT'),(83,83,3600,'CURRENT'),(84,84,25200,'CURRENT'),(85,85,10800,'CURRENT'),(86,86,32400,'CURRENT'),(87,87,14400,'CURRENT'),(88,88,36000,'CURRENT'),(89,89,21600,'CURRENT'),(90,90,7200,'CURRENT'),(91,91,25200,'CURRENT'),(92,92,10800,'CURRENT'),(93,93,32400,'CURRENT'),(94,94,14400,'CURRENT'),(95,95,36000,'CURRENT'),(96,96,21600,'CURRENT'),(97,97,7200,'CURRENT'),(98,98,25200,'CURRENT'),(99,99,10800,'CURRENT'),(100,100,32400,'CURRENT'),(101,101,18000,'CURRENT'); /*!40000 ALTER TABLE `moldable_job_descriptions` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `queues` -- DROP TABLE IF EXISTS `queues`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `queues` ( `queue_name` varchar(100) NOT NULL, `priority` int(10) unsigned NOT NULL, `scheduler_policy` varchar(100) NOT NULL, `state` enum('Active','notActive') NOT NULL default 'Active', PRIMARY KEY (`queue_name`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `queues` -- LOCK TABLES `queues` WRITE; /*!40000 ALTER TABLE `queues` DISABLE KEYS */; INSERT INTO `queues` VALUES ('admin',10,'oar_sched_gantt_with_timesharing','Active'),('default',2,'oar_sched_gantt_with_timesharing','Active'),('besteffort',0,'oar_sched_gantt_with_timesharing','Active'); /*!40000 ALTER TABLE `queues` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `resource_logs` -- DROP TABLE IF EXISTS `resource_logs`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `resource_logs` ( `resource_log_id` int(10) unsigned NOT NULL auto_increment, `resource_id` int(10) unsigned NOT NULL, `attribute` varchar(255) NOT NULL, `value` varchar(255) NOT NULL, `date_start` int(10) unsigned NOT NULL, `date_stop` int(10) unsigned default '0', `finaud_decision` enum('YES','NO') NOT NULL default 'NO', PRIMARY KEY (`resource_log_id`), KEY `resource` (`resource_id`), KEY `attribute` (`attribute`), KEY `finaud` (`finaud_decision`), KEY `val` (`value`) ) ENGINE=MyISAM AUTO_INCREMENT=2305 DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `resource_logs` -- LOCK TABLES `resource_logs` WRITE; /*!40000 ALTER TABLE `resource_logs` DISABLE KEYS */; INSERT INTO `resource_logs` VALUES (1,1,'state','Alive',1224778461,0,'NO'),(2,1,'core ','1',1224778461,0,'NO'),(3,1,'cpu ','1',1224778461,0,'NO'),(4,1,'host ','node-1',1224778461,0,'NO'),(5,1,'cpuset','0',1224778461,0,'NO'),(6,1,'network_address','127.0.2.1',1224778461,0,'NO'),(7,2,'state','Alive',1224778461,0,'NO'),(8,2,'core ','2',1224778461,0,'NO'),(9,2,'cpu ','1',1224778461,0,'NO'),(10,2,'host ','node-1',1224778461,0,'NO'),(11,2,'cpuset','1',1224778461,0,'NO'),(12,2,'network_address','127.0.2.1',1224778461,0,'NO'),(13,3,'state','Alive',1224778462,0,'NO'),(14,3,'core ','3',1224778462,0,'NO'),(15,3,'cpu ','2',1224778462,0,'NO'),(16,3,'host ','node-1',1224778462,0,'NO'),(17,3,'cpuset','2',1224778462,0,'NO'),(18,3,'network_address','127.0.2.1',1224778462,0,'NO'),(19,4,'state','Alive',1224778462,0,'NO'),(20,4,'core ','4',1224778462,0,'NO'),(21,4,'cpu ','2',1224778462,0,'NO'),(22,4,'host ','node-1',1224778462,0,'NO'),(23,4,'cpuset','3',1224778462,0,'NO'),(24,4,'network_address','127.0.2.1',1224778462,0,'NO'),(25,5,'state','Alive',1224778462,0,'NO'),(26,5,'core ','5',1224778462,0,'NO'),(27,5,'cpu ','3',1224778462,0,'NO'),(28,5,'host ','node-2',1224778462,0,'NO'),(29,5,'cpuset','0',1224778462,0,'NO'),(30,5,'network_address','127.0.2.2',1224778462,0,'NO'),(31,6,'state','Alive',1224778462,0,'NO'),(32,6,'core ','6',1224778462,0,'NO'),(33,6,'cpu ','3',1224778462,0,'NO'),(34,6,'host ','node-2',1224778462,0,'NO'),(35,6,'cpuset','1',1224778462,0,'NO'),(36,6,'network_address','127.0.2.2',1224778462,0,'NO'),(37,7,'state','Alive',1224778462,0,'NO'),(38,7,'core ','7',1224778462,0,'NO'),(39,7,'cpu ','4',1224778462,0,'NO'),(40,7,'host ','node-2',1224778462,0,'NO'),(41,7,'cpuset','2',1224778462,0,'NO'),(42,7,'network_address','127.0.2.2',1224778462,0,'NO'),(43,8,'state','Alive',1224778462,0,'NO'),(44,8,'core ','8',1224778462,0,'NO'),(45,8,'cpu ','4',1224778462,0,'NO'),(46,8,'host ','node-2',1224778462,0,'NO'),(47,8,'cpuset','3',1224778462,0,'NO'),(48,8,'network_address','127.0.2.2',1224778462,0,'NO'),(49,9,'state','Alive',1224778463,0,'NO'),(50,9,'core ','9',1224778463,0,'NO'),(51,9,'cpu ','5',1224778463,0,'NO'),(52,9,'host ','node-3',1224778463,0,'NO'),(53,9,'cpuset','0',1224778463,0,'NO'),(54,9,'network_address','127.0.2.3',1224778463,0,'NO'),(55,10,'state','Alive',1224778463,0,'NO'),(56,10,'core ','10',1224778463,0,'NO'),(57,10,'cpu ','5',1224778463,0,'NO'),(58,10,'host ','node-3',1224778463,0,'NO'),(59,10,'cpuset','1',1224778463,0,'NO'),(60,10,'network_address','127.0.2.3',1224778463,0,'NO'),(61,11,'state','Alive',1224778463,0,'NO'),(62,11,'core ','11',1224778463,0,'NO'),(63,11,'cpu ','6',1224778463,0,'NO'),(64,11,'host ','node-3',1224778463,0,'NO'),(65,11,'cpuset','2',1224778463,0,'NO'),(66,11,'network_address','127.0.2.3',1224778463,0,'NO'),(67,12,'state','Alive',1224778463,0,'NO'),(68,12,'core ','12',1224778463,0,'NO'),(69,12,'cpu ','6',1224778463,0,'NO'),(70,12,'host ','node-3',1224778463,0,'NO'),(71,12,'cpuset','3',1224778463,0,'NO'),(72,12,'network_address','127.0.2.3',1224778463,0,'NO'),(73,13,'state','Alive',1224778463,0,'NO'),(74,13,'core ','13',1224778463,0,'NO'),(75,13,'cpu ','7',1224778463,0,'NO'),(76,13,'host ','node-4',1224778463,0,'NO'),(77,13,'cpuset','0',1224778463,0,'NO'),(78,13,'network_address','127.0.2.4',1224778463,0,'NO'),(79,14,'state','Alive',1224778464,0,'NO'),(80,14,'core ','14',1224778464,0,'NO'),(81,14,'cpu ','7',1224778464,0,'NO'),(82,14,'host ','node-4',1224778464,0,'NO'),(83,14,'cpuset','1',1224778464,0,'NO'),(84,14,'network_address','127.0.2.4',1224778464,0,'NO'),(85,15,'state','Alive',1224778464,0,'NO'),(86,15,'core ','15',1224778464,0,'NO'),(87,15,'cpu ','8',1224778464,0,'NO'),(88,15,'host ','node-4',1224778464,0,'NO'),(89,15,'cpuset','2',1224778464,0,'NO'),(90,15,'network_address','127.0.2.4',1224778464,0,'NO'),(91,16,'state','Alive',1224778464,0,'NO'),(92,16,'core ','16',1224778464,0,'NO'),(93,16,'cpu ','8',1224778464,0,'NO'),(94,16,'host ','node-4',1224778464,0,'NO'),(95,16,'cpuset','3',1224778464,0,'NO'),(96,16,'network_address','127.0.2.4',1224778464,0,'NO'),(97,17,'state','Alive',1224778464,0,'NO'),(98,17,'core ','17',1224778464,0,'NO'),(99,17,'cpu ','9',1224778464,0,'NO'),(100,17,'host ','node-5',1224778464,0,'NO'),(101,17,'cpuset','0',1224778464,0,'NO'),(102,17,'network_address','127.0.2.5',1224778464,0,'NO'),(103,18,'state','Alive',1224778464,0,'NO'),(104,18,'core ','18',1224778464,0,'NO'),(105,18,'cpu ','9',1224778464,0,'NO'),(106,18,'host ','node-5',1224778464,0,'NO'),(107,18,'cpuset','1',1224778464,0,'NO'),(108,18,'network_address','127.0.2.5',1224778464,0,'NO'),(109,19,'state','Alive',1224778465,0,'NO'),(110,19,'core ','19',1224778465,0,'NO'),(111,19,'cpu ','10',1224778465,0,'NO'),(112,19,'host ','node-5',1224778465,0,'NO'),(113,19,'cpuset','2',1224778465,0,'NO'),(114,19,'network_address','127.0.2.5',1224778465,0,'NO'),(115,20,'state','Alive',1224778465,0,'NO'),(116,20,'core ','20',1224778465,0,'NO'),(117,20,'cpu ','10',1224778465,0,'NO'),(118,20,'host ','node-5',1224778465,0,'NO'),(119,20,'cpuset','3',1224778465,0,'NO'),(120,20,'network_address','127.0.2.5',1224778465,0,'NO'),(121,21,'state','Alive',1224778465,0,'NO'),(122,21,'core ','21',1224778465,0,'NO'),(123,21,'cpu ','11',1224778465,0,'NO'),(124,21,'host ','node-6',1224778465,0,'NO'),(125,21,'cpuset','0',1224778465,0,'NO'),(126,21,'network_address','127.0.2.6',1224778465,0,'NO'),(127,22,'state','Alive',1224778465,0,'NO'),(128,22,'core ','22',1224778465,0,'NO'),(129,22,'cpu ','11',1224778465,0,'NO'),(130,22,'host ','node-6',1224778465,0,'NO'),(131,22,'cpuset','1',1224778465,0,'NO'),(132,22,'network_address','127.0.2.6',1224778465,0,'NO'),(133,23,'state','Alive',1224778465,0,'NO'),(134,23,'core ','23',1224778465,0,'NO'),(135,23,'cpu ','12',1224778465,0,'NO'),(136,23,'host ','node-6',1224778465,0,'NO'),(137,23,'cpuset','2',1224778465,0,'NO'),(138,23,'network_address','127.0.2.6',1224778465,0,'NO'),(139,24,'state','Alive',1224778466,0,'NO'),(140,24,'core ','24',1224778466,0,'NO'),(141,24,'cpu ','12',1224778466,0,'NO'),(142,24,'host ','node-6',1224778466,0,'NO'),(143,24,'cpuset','3',1224778466,0,'NO'),(144,24,'network_address','127.0.2.6',1224778466,0,'NO'),(145,25,'state','Alive',1224778466,0,'NO'),(146,25,'core ','25',1224778466,0,'NO'),(147,25,'cpu ','13',1224778466,0,'NO'),(148,25,'host ','node-7',1224778466,0,'NO'),(149,25,'cpuset','0',1224778466,0,'NO'),(150,25,'network_address','127.0.2.7',1224778466,0,'NO'),(151,26,'state','Alive',1224778466,0,'NO'),(152,26,'core ','26',1224778466,0,'NO'),(153,26,'cpu ','13',1224778466,0,'NO'),(154,26,'host ','node-7',1224778466,0,'NO'),(155,26,'cpuset','1',1224778466,0,'NO'),(156,26,'network_address','127.0.2.7',1224778466,0,'NO'),(157,27,'state','Alive',1224778466,0,'NO'),(158,27,'core ','27',1224778466,0,'NO'),(159,27,'cpu ','14',1224778466,0,'NO'),(160,27,'host ','node-7',1224778466,0,'NO'),(161,27,'cpuset','2',1224778466,0,'NO'),(162,27,'network_address','127.0.2.7',1224778466,0,'NO'),(163,28,'state','Alive',1224778466,0,'NO'),(164,28,'core ','28',1224778466,0,'NO'),(165,28,'cpu ','14',1224778466,0,'NO'),(166,28,'host ','node-7',1224778466,0,'NO'),(167,28,'cpuset','3',1224778466,0,'NO'),(168,28,'network_address','127.0.2.7',1224778466,0,'NO'),(169,29,'state','Alive',1224778466,0,'NO'),(170,29,'core ','29',1224778466,0,'NO'),(171,29,'cpu ','15',1224778466,0,'NO'),(172,29,'host ','node-8',1224778466,0,'NO'),(173,29,'cpuset','0',1224778466,0,'NO'),(174,29,'network_address','127.0.2.8',1224778466,0,'NO'),(175,30,'state','Alive',1224778467,0,'NO'),(176,30,'core ','30',1224778467,0,'NO'),(177,30,'cpu ','15',1224778467,0,'NO'),(178,30,'host ','node-8',1224778467,0,'NO'),(179,30,'cpuset','1',1224778467,0,'NO'),(180,30,'network_address','127.0.2.8',1224778467,0,'NO'),(181,31,'state','Alive',1224778467,0,'NO'),(182,31,'core ','31',1224778467,0,'NO'),(183,31,'cpu ','16',1224778467,0,'NO'),(184,31,'host ','node-8',1224778467,0,'NO'),(185,31,'cpuset','2',1224778467,0,'NO'),(186,31,'network_address','127.0.2.8',1224778467,0,'NO'),(187,32,'state','Alive',1224778467,0,'NO'),(188,32,'core ','32',1224778467,0,'NO'),(189,32,'cpu ','16',1224778467,0,'NO'),(190,32,'host ','node-8',1224778467,0,'NO'),(191,32,'cpuset','3',1224778467,0,'NO'),(192,32,'network_address','127.0.2.8',1224778467,0,'NO'),(193,33,'state','Alive',1224778467,0,'NO'),(194,33,'core ','33',1224778467,0,'NO'),(195,33,'cpu ','17',1224778467,0,'NO'),(196,33,'host ','node-9',1224778467,0,'NO'),(197,33,'cpuset','0',1224778467,0,'NO'),(198,33,'network_address','127.0.2.9',1224778467,0,'NO'),(199,34,'state','Alive',1224778467,0,'NO'),(200,34,'core ','34',1224778467,0,'NO'),(201,34,'cpu ','17',1224778467,0,'NO'),(202,34,'host ','node-9',1224778467,0,'NO'),(203,34,'cpuset','1',1224778467,0,'NO'),(204,34,'network_address','127.0.2.9',1224778467,0,'NO'),(205,35,'state','Alive',1224778468,0,'NO'),(206,35,'core ','35',1224778468,0,'NO'),(207,35,'cpu ','18',1224778468,0,'NO'),(208,35,'host ','node-9',1224778468,0,'NO'),(209,35,'cpuset','2',1224778468,0,'NO'),(210,35,'network_address','127.0.2.9',1224778468,0,'NO'),(211,36,'state','Alive',1224778468,0,'NO'),(212,36,'core ','36',1224778468,0,'NO'),(213,36,'cpu ','18',1224778468,0,'NO'),(214,36,'host ','node-9',1224778468,0,'NO'),(215,36,'cpuset','3',1224778468,0,'NO'),(216,36,'network_address','127.0.2.9',1224778468,0,'NO'),(217,37,'state','Alive',1224778468,0,'NO'),(218,37,'core ','37',1224778468,0,'NO'),(219,37,'cpu ','19',1224778468,0,'NO'),(220,37,'host ','node-10',1224778468,0,'NO'),(221,37,'cpuset','0',1224778468,0,'NO'),(222,37,'network_address','127.0.2.10',1224778468,0,'NO'),(223,38,'state','Alive',1224778468,0,'NO'),(224,38,'core ','38',1224778468,0,'NO'),(225,38,'cpu ','19',1224778468,0,'NO'),(226,38,'host ','node-10',1224778468,0,'NO'),(227,38,'cpuset','1',1224778468,0,'NO'),(228,38,'network_address','127.0.2.10',1224778468,0,'NO'),(229,39,'state','Alive',1224778468,0,'NO'),(230,39,'core ','39',1224778468,0,'NO'),(231,39,'cpu ','20',1224778468,0,'NO'),(232,39,'host ','node-10',1224778468,0,'NO'),(233,39,'cpuset','2',1224778468,0,'NO'),(234,39,'network_address','127.0.2.10',1224778468,0,'NO'),(235,40,'state','Alive',1224778469,0,'NO'),(236,40,'core ','40',1224778469,0,'NO'),(237,40,'cpu ','20',1224778469,0,'NO'),(238,40,'host ','node-10',1224778469,0,'NO'),(239,40,'cpuset','3',1224778469,0,'NO'),(240,40,'network_address','127.0.2.10',1224778469,0,'NO'),(241,41,'state','Alive',1224778469,0,'NO'),(242,41,'core ','41',1224778469,0,'NO'),(243,41,'cpu ','21',1224778469,0,'NO'),(244,41,'host ','node-11',1224778469,0,'NO'),(245,41,'cpuset','0',1224778469,0,'NO'),(246,41,'network_address','127.0.2.11',1224778469,0,'NO'),(247,42,'state','Alive',1224778469,0,'NO'),(248,42,'core ','42',1224778469,0,'NO'),(249,42,'cpu ','21',1224778469,0,'NO'),(250,42,'host ','node-11',1224778469,0,'NO'),(251,42,'cpuset','1',1224778469,0,'NO'),(252,42,'network_address','127.0.2.11',1224778469,0,'NO'),(253,43,'state','Alive',1224778469,0,'NO'),(254,43,'core ','43',1224778469,0,'NO'),(255,43,'cpu ','22',1224778469,0,'NO'),(256,43,'host ','node-11',1224778469,0,'NO'),(257,43,'cpuset','2',1224778469,0,'NO'),(258,43,'network_address','127.0.2.11',1224778469,0,'NO'),(259,44,'state','Alive',1224778469,0,'NO'),(260,44,'core ','44',1224778469,0,'NO'),(261,44,'cpu ','22',1224778469,0,'NO'),(262,44,'host ','node-11',1224778469,0,'NO'),(263,44,'cpuset','3',1224778469,0,'NO'),(264,44,'network_address','127.0.2.11',1224778469,0,'NO'),(265,45,'state','Alive',1224778470,0,'NO'),(266,45,'core ','45',1224778470,0,'NO'),(267,45,'cpu ','23',1224778470,0,'NO'),(268,45,'host ','node-12',1224778470,0,'NO'),(269,45,'cpuset','0',1224778470,0,'NO'),(270,45,'network_address','127.0.2.12',1224778470,0,'NO'),(271,46,'state','Alive',1224778470,0,'NO'),(272,46,'core ','46',1224778470,0,'NO'),(273,46,'cpu ','23',1224778470,0,'NO'),(274,46,'host ','node-12',1224778470,0,'NO'),(275,46,'cpuset','1',1224778470,0,'NO'),(276,46,'network_address','127.0.2.12',1224778470,0,'NO'),(277,47,'state','Alive',1224778470,0,'NO'),(278,47,'core ','47',1224778470,0,'NO'),(279,47,'cpu ','24',1224778470,0,'NO'),(280,47,'host ','node-12',1224778470,0,'NO'),(281,47,'cpuset','2',1224778470,0,'NO'),(282,47,'network_address','127.0.2.12',1224778470,0,'NO'),(283,48,'state','Alive',1224778470,0,'NO'),(284,48,'core ','48',1224778470,0,'NO'),(285,48,'cpu ','24',1224778470,0,'NO'),(286,48,'host ','node-12',1224778470,0,'NO'),(287,48,'cpuset','3',1224778470,0,'NO'),(288,48,'network_address','127.0.2.12',1224778470,0,'NO'),(289,49,'state','Alive',1224778471,0,'NO'),(290,49,'core ','49',1224778471,0,'NO'),(291,49,'cpu ','25',1224778471,0,'NO'),(292,49,'host ','node-13',1224778471,0,'NO'),(293,49,'cpuset','0',1224778471,0,'NO'),(294,49,'network_address','127.0.2.13',1224778471,0,'NO'),(295,50,'state','Alive',1224778471,0,'NO'),(296,50,'core ','50',1224778471,0,'NO'),(297,50,'cpu ','25',1224778471,0,'NO'),(298,50,'host ','node-13',1224778471,0,'NO'),(299,50,'cpuset','1',1224778471,0,'NO'),(300,50,'network_address','127.0.2.13',1224778471,0,'NO'),(301,51,'state','Alive',1224778471,0,'NO'),(302,51,'core ','51',1224778471,0,'NO'),(303,51,'cpu ','26',1224778471,0,'NO'),(304,51,'host ','node-13',1224778471,0,'NO'),(305,51,'cpuset','2',1224778471,0,'NO'),(306,51,'network_address','127.0.2.13',1224778471,0,'NO'),(307,52,'state','Alive',1224778472,0,'NO'),(308,52,'core ','52',1224778472,0,'NO'),(309,52,'cpu ','26',1224778472,0,'NO'),(310,52,'host ','node-13',1224778472,0,'NO'),(311,52,'cpuset','3',1224778472,0,'NO'),(312,52,'network_address','127.0.2.13',1224778472,0,'NO'),(313,53,'state','Alive',1224778472,0,'NO'),(314,53,'core ','53',1224778472,0,'NO'),(315,53,'cpu ','27',1224778472,0,'NO'),(316,53,'host ','node-14',1224778472,0,'NO'),(317,53,'cpuset','0',1224778472,0,'NO'),(318,53,'network_address','127.0.2.14',1224778472,0,'NO'),(319,54,'state','Alive',1224778472,0,'NO'),(320,54,'core ','54',1224778472,0,'NO'),(321,54,'cpu ','27',1224778472,0,'NO'),(322,54,'host ','node-14',1224778472,0,'NO'),(323,54,'cpuset','1',1224778472,0,'NO'),(324,54,'network_address','127.0.2.14',1224778472,0,'NO'),(325,55,'state','Alive',1224778472,0,'NO'),(326,55,'core ','55',1224778472,0,'NO'),(327,55,'cpu ','28',1224778472,0,'NO'),(328,55,'host ','node-14',1224778472,0,'NO'),(329,55,'cpuset','2',1224778472,0,'NO'),(330,55,'network_address','127.0.2.14',1224778472,0,'NO'),(331,56,'state','Alive',1224778472,0,'NO'),(332,56,'core ','56',1224778472,0,'NO'),(333,56,'cpu ','28',1224778472,0,'NO'),(334,56,'host ','node-14',1224778472,0,'NO'),(335,56,'cpuset','3',1224778472,0,'NO'),(336,56,'network_address','127.0.2.14',1224778472,0,'NO'),(337,57,'state','Alive',1224778473,0,'NO'),(338,57,'core ','57',1224778473,0,'NO'),(339,57,'cpu ','29',1224778473,0,'NO'),(340,57,'host ','node-15',1224778473,0,'NO'),(341,57,'cpuset','0',1224778473,0,'NO'),(342,57,'network_address','127.0.2.15',1224778473,0,'NO'),(343,58,'state','Alive',1224778473,0,'NO'),(344,58,'core ','58',1224778473,0,'NO'),(345,58,'cpu ','29',1224778473,0,'NO'),(346,58,'host ','node-15',1224778473,0,'NO'),(347,58,'cpuset','1',1224778473,0,'NO'),(348,58,'network_address','127.0.2.15',1224778473,0,'NO'),(349,59,'state','Alive',1224778473,0,'NO'),(350,59,'core ','59',1224778473,0,'NO'),(351,59,'cpu ','30',1224778473,0,'NO'),(352,59,'host ','node-15',1224778473,0,'NO'),(353,59,'cpuset','2',1224778473,0,'NO'),(354,59,'network_address','127.0.2.15',1224778473,0,'NO'),(355,60,'state','Alive',1224778473,0,'NO'),(356,60,'core ','60',1224778473,0,'NO'),(357,60,'cpu ','30',1224778473,0,'NO'),(358,60,'host ','node-15',1224778473,0,'NO'),(359,60,'cpuset','3',1224778473,0,'NO'),(360,60,'network_address','127.0.2.15',1224778473,0,'NO'),(361,61,'state','Alive',1224778473,0,'NO'),(362,61,'core ','61',1224778473,0,'NO'),(363,61,'cpu ','31',1224778473,0,'NO'),(364,61,'host ','node-16',1224778473,0,'NO'),(365,61,'cpuset','0',1224778473,0,'NO'),(366,61,'network_address','127.0.2.16',1224778473,0,'NO'),(367,62,'state','Alive',1224778473,0,'NO'),(368,62,'core ','62',1224778473,0,'NO'),(369,62,'cpu ','31',1224778473,0,'NO'),(370,62,'host ','node-16',1224778473,0,'NO'),(371,62,'cpuset','1',1224778473,0,'NO'),(372,62,'network_address','127.0.2.16',1224778473,0,'NO'),(373,63,'state','Alive',1224778474,0,'NO'),(374,63,'core ','63',1224778474,0,'NO'),(375,63,'cpu ','32',1224778474,0,'NO'),(376,63,'host ','node-16',1224778474,0,'NO'),(377,63,'cpuset','2',1224778474,0,'NO'),(378,63,'network_address','127.0.2.16',1224778474,0,'NO'),(379,64,'state','Alive',1224778474,0,'NO'),(380,64,'core ','64',1224778474,0,'NO'),(381,64,'cpu ','32',1224778474,0,'NO'),(382,64,'host ','node-16',1224778474,0,'NO'),(383,64,'cpuset','3',1224778474,0,'NO'),(384,64,'network_address','127.0.2.16',1224778474,0,'NO'),(385,65,'state','Alive',1224778474,0,'NO'),(386,65,'core ','65',1224778474,0,'NO'),(387,65,'cpu ','33',1224778474,0,'NO'),(388,65,'host ','node-17',1224778474,0,'NO'),(389,65,'cpuset','0',1224778474,0,'NO'),(390,65,'network_address','127.0.2.17',1224778474,0,'NO'),(391,66,'state','Alive',1224778474,0,'NO'),(392,66,'core ','66',1224778474,0,'NO'),(393,66,'cpu ','33',1224778474,0,'NO'),(394,66,'host ','node-17',1224778474,0,'NO'),(395,66,'cpuset','1',1224778474,0,'NO'),(396,66,'network_address','127.0.2.17',1224778474,0,'NO'),(397,67,'state','Alive',1224778474,0,'NO'),(398,67,'core ','67',1224778474,0,'NO'),(399,67,'cpu ','34',1224778474,0,'NO'),(400,67,'host ','node-17',1224778474,0,'NO'),(401,67,'cpuset','2',1224778474,0,'NO'),(402,67,'network_address','127.0.2.17',1224778474,0,'NO'),(403,68,'state','Alive',1224778475,0,'NO'),(404,68,'core ','68',1224778475,0,'NO'),(405,68,'cpu ','34',1224778475,0,'NO'),(406,68,'host ','node-17',1224778475,0,'NO'),(407,68,'cpuset','3',1224778475,0,'NO'),(408,68,'network_address','127.0.2.17',1224778475,0,'NO'),(409,69,'state','Alive',1224778475,0,'NO'),(410,69,'core ','69',1224778475,0,'NO'),(411,69,'cpu ','35',1224778475,0,'NO'),(412,69,'host ','node-18',1224778475,0,'NO'),(413,69,'cpuset','0',1224778475,0,'NO'),(414,69,'network_address','127.0.2.18',1224778475,0,'NO'),(415,70,'state','Alive',1224778475,0,'NO'),(416,70,'core ','70',1224778475,0,'NO'),(417,70,'cpu ','35',1224778475,0,'NO'),(418,70,'host ','node-18',1224778475,0,'NO'),(419,70,'cpuset','1',1224778475,0,'NO'),(420,70,'network_address','127.0.2.18',1224778475,0,'NO'),(421,71,'state','Alive',1224778475,0,'NO'),(422,71,'core ','71',1224778475,0,'NO'),(423,71,'cpu ','36',1224778475,0,'NO'),(424,71,'host ','node-18',1224778475,0,'NO'),(425,71,'cpuset','2',1224778475,0,'NO'),(426,71,'network_address','127.0.2.18',1224778475,0,'NO'),(427,72,'state','Alive',1224778475,0,'NO'),(428,72,'core ','72',1224778475,0,'NO'),(429,72,'cpu ','36',1224778475,0,'NO'),(430,72,'host ','node-18',1224778475,0,'NO'),(431,72,'cpuset','3',1224778475,0,'NO'),(432,72,'network_address','127.0.2.18',1224778475,0,'NO'),(433,73,'state','Alive',1224778476,0,'NO'),(434,73,'core ','73',1224778476,0,'NO'),(435,73,'cpu ','37',1224778476,0,'NO'),(436,73,'host ','node-19',1224778476,0,'NO'),(437,73,'cpuset','0',1224778476,0,'NO'),(438,73,'network_address','127.0.2.19',1224778476,0,'NO'),(439,74,'state','Alive',1224778476,0,'NO'),(440,74,'core ','74',1224778476,0,'NO'),(441,74,'cpu ','37',1224778476,0,'NO'),(442,74,'host ','node-19',1224778476,0,'NO'),(443,74,'cpuset','1',1224778476,0,'NO'),(444,74,'network_address','127.0.2.19',1224778476,0,'NO'),(445,75,'state','Alive',1224778476,0,'NO'),(446,75,'core ','75',1224778476,0,'NO'),(447,75,'cpu ','38',1224778476,0,'NO'),(448,75,'host ','node-19',1224778476,0,'NO'),(449,75,'cpuset','2',1224778476,0,'NO'),(450,75,'network_address','127.0.2.19',1224778476,0,'NO'),(451,76,'state','Alive',1224778476,0,'NO'),(452,76,'core ','76',1224778476,0,'NO'),(453,76,'cpu ','38',1224778476,0,'NO'),(454,76,'host ','node-19',1224778476,0,'NO'),(455,76,'cpuset','3',1224778476,0,'NO'),(456,76,'network_address','127.0.2.19',1224778476,0,'NO'),(457,77,'state','Alive',1224778476,0,'NO'),(458,77,'core ','77',1224778476,0,'NO'),(459,77,'cpu ','39',1224778476,0,'NO'),(460,77,'host ','node-20',1224778476,0,'NO'),(461,77,'cpuset','0',1224778476,0,'NO'),(462,77,'network_address','127.0.2.20',1224778476,0,'NO'),(463,78,'state','Alive',1224778477,0,'NO'),(464,78,'core ','78',1224778477,0,'NO'),(465,78,'cpu ','39',1224778477,0,'NO'),(466,78,'host ','node-20',1224778477,0,'NO'),(467,78,'cpuset','1',1224778477,0,'NO'),(468,78,'network_address','127.0.2.20',1224778477,0,'NO'),(469,79,'state','Alive',1224778477,0,'NO'),(470,79,'core ','79',1224778477,0,'NO'),(471,79,'cpu ','40',1224778477,0,'NO'),(472,79,'host ','node-20',1224778477,0,'NO'),(473,79,'cpuset','2',1224778477,0,'NO'),(474,79,'network_address','127.0.2.20',1224778477,0,'NO'),(475,80,'state','Alive',1224778477,0,'NO'),(476,80,'core ','80',1224778477,0,'NO'),(477,80,'cpu ','40',1224778477,0,'NO'),(478,80,'host ','node-20',1224778477,0,'NO'),(479,80,'cpuset','3',1224778477,0,'NO'),(480,80,'network_address','127.0.2.20',1224778477,0,'NO'),(481,81,'state','Alive',1224778477,0,'NO'),(482,81,'core ','81',1224778477,0,'NO'),(483,81,'cpu ','41',1224778477,0,'NO'),(484,81,'host ','node-21',1224778477,0,'NO'),(485,81,'cpuset','0',1224778477,0,'NO'),(486,81,'network_address','127.0.2.21',1224778477,0,'NO'),(487,82,'state','Alive',1224778477,0,'NO'),(488,82,'core ','82',1224778477,0,'NO'),(489,82,'cpu ','41',1224778477,0,'NO'),(490,82,'host ','node-21',1224778477,0,'NO'),(491,82,'cpuset','1',1224778477,0,'NO'),(492,82,'network_address','127.0.2.21',1224778477,0,'NO'),(493,83,'state','Alive',1224778478,0,'NO'),(494,83,'core ','83',1224778478,0,'NO'),(495,83,'cpu ','42',1224778478,0,'NO'),(496,83,'host ','node-21',1224778478,0,'NO'),(497,83,'cpuset','2',1224778478,0,'NO'),(498,83,'network_address','127.0.2.21',1224778478,0,'NO'),(499,84,'state','Alive',1224778478,0,'NO'),(500,84,'core ','84',1224778478,0,'NO'),(501,84,'cpu ','42',1224778478,0,'NO'),(502,84,'host ','node-21',1224778478,0,'NO'),(503,84,'cpuset','3',1224778478,0,'NO'),(504,84,'network_address','127.0.2.21',1224778478,0,'NO'),(505,85,'state','Alive',1224778478,0,'NO'),(506,85,'core ','85',1224778478,0,'NO'),(507,85,'cpu ','43',1224778478,0,'NO'),(508,85,'host ','node-22',1224778478,0,'NO'),(509,85,'cpuset','0',1224778478,0,'NO'),(510,85,'network_address','127.0.2.22',1224778478,0,'NO'),(511,86,'state','Alive',1224778478,0,'NO'),(512,86,'core ','86',1224778478,0,'NO'),(513,86,'cpu ','43',1224778478,0,'NO'),(514,86,'host ','node-22',1224778478,0,'NO'),(515,86,'cpuset','1',1224778478,0,'NO'),(516,86,'network_address','127.0.2.22',1224778478,0,'NO'),(517,87,'state','Alive',1224778478,0,'NO'),(518,87,'core ','87',1224778478,0,'NO'),(519,87,'cpu ','44',1224778478,0,'NO'),(520,87,'host ','node-22',1224778478,0,'NO'),(521,87,'cpuset','2',1224778478,0,'NO'),(522,87,'network_address','127.0.2.22',1224778478,0,'NO'),(523,88,'state','Alive',1224778478,0,'NO'),(524,88,'core ','88',1224778479,0,'NO'),(525,88,'cpu ','44',1224778479,0,'NO'),(526,88,'host ','node-22',1224778479,0,'NO'),(527,88,'cpuset','3',1224778479,0,'NO'),(528,88,'network_address','127.0.2.22',1224778479,0,'NO'),(529,89,'state','Alive',1224778479,0,'NO'),(530,89,'core ','89',1224778479,0,'NO'),(531,89,'cpu ','45',1224778479,0,'NO'),(532,89,'host ','node-23',1224778479,0,'NO'),(533,89,'cpuset','0',1224778479,0,'NO'),(534,89,'network_address','127.0.2.23',1224778479,0,'NO'),(535,90,'state','Alive',1224778479,0,'NO'),(536,90,'core ','90',1224778479,0,'NO'),(537,90,'cpu ','45',1224778479,0,'NO'),(538,90,'host ','node-23',1224778479,0,'NO'),(539,90,'cpuset','1',1224778479,0,'NO'),(540,90,'network_address','127.0.2.23',1224778479,0,'NO'),(541,91,'state','Alive',1224778479,0,'NO'),(542,91,'core ','91',1224778479,0,'NO'),(543,91,'cpu ','46',1224778479,0,'NO'),(544,91,'host ','node-23',1224778479,0,'NO'),(545,91,'cpuset','2',1224778479,0,'NO'),(546,91,'network_address','127.0.2.23',1224778479,0,'NO'),(547,92,'state','Alive',1224778479,0,'NO'),(548,92,'core ','92',1224778479,0,'NO'),(549,92,'cpu ','46',1224778479,0,'NO'),(550,92,'host ','node-23',1224778479,0,'NO'),(551,92,'cpuset','3',1224778479,0,'NO'),(552,92,'network_address','127.0.2.23',1224778479,0,'NO'),(553,93,'state','Alive',1224778479,0,'NO'),(554,93,'core ','93',1224778479,0,'NO'),(555,93,'cpu ','47',1224778479,0,'NO'),(556,93,'host ','node-24',1224778479,0,'NO'),(557,93,'cpuset','0',1224778479,0,'NO'),(558,93,'network_address','127.0.2.24',1224778479,0,'NO'),(559,94,'state','Alive',1224778480,0,'NO'),(560,94,'core ','94',1224778480,0,'NO'),(561,94,'cpu ','47',1224778480,0,'NO'),(562,94,'host ','node-24',1224778480,0,'NO'),(563,94,'cpuset','1',1224778480,0,'NO'),(564,94,'network_address','127.0.2.24',1224778480,0,'NO'),(565,95,'state','Alive',1224778480,0,'NO'),(566,95,'core ','95',1224778480,0,'NO'),(567,95,'cpu ','48',1224778480,0,'NO'),(568,95,'host ','node-24',1224778480,0,'NO'),(569,95,'cpuset','2',1224778480,0,'NO'),(570,95,'network_address','127.0.2.24',1224778480,0,'NO'),(571,96,'state','Alive',1224778480,0,'NO'),(572,96,'core ','96',1224778480,0,'NO'),(573,96,'cpu ','48',1224778480,0,'NO'),(574,96,'host ','node-24',1224778480,0,'NO'),(575,96,'cpuset','3',1224778480,0,'NO'),(576,96,'network_address','127.0.2.24',1224778480,0,'NO'),(577,97,'state','Alive',1224778480,0,'NO'),(578,97,'core ','97',1224778480,0,'NO'),(579,97,'cpu ','49',1224778480,0,'NO'),(580,97,'host ','node-25',1224778480,0,'NO'),(581,97,'cpuset','0',1224778480,0,'NO'),(582,97,'network_address','127.0.2.25',1224778480,0,'NO'),(583,98,'state','Alive',1224778480,0,'NO'),(584,98,'core ','98',1224778480,0,'NO'),(585,98,'cpu ','49',1224778480,0,'NO'),(586,98,'host ','node-25',1224778480,0,'NO'),(587,98,'cpuset','1',1224778480,0,'NO'),(588,98,'network_address','127.0.2.25',1224778480,0,'NO'),(589,99,'state','Alive',1224778481,0,'NO'),(590,99,'core ','99',1224778481,0,'NO'),(591,99,'cpu ','50',1224778481,0,'NO'),(592,99,'host ','node-25',1224778481,0,'NO'),(593,99,'cpuset','2',1224778481,0,'NO'),(594,99,'network_address','127.0.2.25',1224778481,0,'NO'),(595,100,'state','Alive',1224778481,0,'NO'),(596,100,'core ','100',1224778481,0,'NO'),(597,100,'cpu ','50',1224778481,0,'NO'),(598,100,'host ','node-25',1224778481,0,'NO'),(599,100,'cpuset','3',1224778481,0,'NO'),(600,100,'network_address','127.0.2.25',1224778481,0,'NO'),(601,101,'state','Alive',1224778481,0,'NO'),(602,101,'core ','101',1224778481,0,'NO'),(603,101,'cpu ','51',1224778481,0,'NO'),(604,101,'host ','node-26',1224778481,0,'NO'),(605,101,'cpuset','0',1224778481,0,'NO'),(606,101,'network_address','127.0.2.26',1224778481,0,'NO'),(607,102,'state','Alive',1224778481,0,'NO'),(608,102,'core ','102',1224778481,0,'NO'),(609,102,'cpu ','51',1224778481,0,'NO'),(610,102,'host ','node-26',1224778481,0,'NO'),(611,102,'cpuset','1',1224778481,0,'NO'),(612,102,'network_address','127.0.2.26',1224778481,0,'NO'),(613,103,'state','Alive',1224778482,0,'NO'),(614,103,'core ','103',1224778482,0,'NO'),(615,103,'cpu ','52',1224778482,0,'NO'),(616,103,'host ','node-26',1224778482,0,'NO'),(617,103,'cpuset','2',1224778482,0,'NO'),(618,103,'network_address','127.0.2.26',1224778482,0,'NO'),(619,104,'state','Alive',1224778482,0,'NO'),(620,104,'core ','104',1224778482,0,'NO'),(621,104,'cpu ','52',1224778482,0,'NO'),(622,104,'host ','node-26',1224778482,0,'NO'),(623,104,'cpuset','3',1224778482,0,'NO'),(624,104,'network_address','127.0.2.26',1224778482,0,'NO'),(625,105,'state','Alive',1224778482,0,'NO'),(626,105,'core ','105',1224778482,0,'NO'),(627,105,'cpu ','53',1224778482,0,'NO'),(628,105,'host ','node-27',1224778482,0,'NO'),(629,105,'cpuset','0',1224778482,0,'NO'),(630,105,'network_address','127.0.2.27',1224778482,0,'NO'),(631,106,'state','Alive',1224778482,0,'NO'),(632,106,'core ','106',1224778482,0,'NO'),(633,106,'cpu ','53',1224778482,0,'NO'),(634,106,'host ','node-27',1224778482,0,'NO'),(635,106,'cpuset','1',1224778482,0,'NO'),(636,106,'network_address','127.0.2.27',1224778482,0,'NO'),(637,107,'state','Alive',1224778482,0,'NO'),(638,107,'core ','107',1224778482,0,'NO'),(639,107,'cpu ','54',1224778482,0,'NO'),(640,107,'host ','node-27',1224778482,0,'NO'),(641,107,'cpuset','2',1224778482,0,'NO'),(642,107,'network_address','127.0.2.27',1224778482,0,'NO'),(643,108,'state','Alive',1224778483,0,'NO'),(644,108,'core ','108',1224778483,0,'NO'),(645,108,'cpu ','54',1224778483,0,'NO'),(646,108,'host ','node-27',1224778483,0,'NO'),(647,108,'cpuset','3',1224778483,0,'NO'),(648,108,'network_address','127.0.2.27',1224778483,0,'NO'),(649,109,'state','Alive',1224778483,0,'NO'),(650,109,'core ','109',1224778483,0,'NO'),(651,109,'cpu ','55',1224778483,0,'NO'),(652,109,'host ','node-28',1224778483,0,'NO'),(653,109,'cpuset','0',1224778483,0,'NO'),(654,109,'network_address','127.0.2.28',1224778483,0,'NO'),(655,110,'state','Alive',1224778483,0,'NO'),(656,110,'core ','110',1224778483,0,'NO'),(657,110,'cpu ','55',1224778483,0,'NO'),(658,110,'host ','node-28',1224778483,0,'NO'),(659,110,'cpuset','1',1224778483,0,'NO'),(660,110,'network_address','127.0.2.28',1224778483,0,'NO'),(661,111,'state','Alive',1224778483,0,'NO'),(662,111,'core ','111',1224778483,0,'NO'),(663,111,'cpu ','56',1224778483,0,'NO'),(664,111,'host ','node-28',1224778483,0,'NO'),(665,111,'cpuset','2',1224778483,0,'NO'),(666,111,'network_address','127.0.2.28',1224778483,0,'NO'),(667,112,'state','Alive',1224778484,0,'NO'),(668,112,'core ','112',1224778484,0,'NO'),(669,112,'cpu ','56',1224778484,0,'NO'),(670,112,'host ','node-28',1224778484,0,'NO'),(671,112,'cpuset','3',1224778484,0,'NO'),(672,112,'network_address','127.0.2.28',1224778484,0,'NO'),(673,113,'state','Alive',1224778484,0,'NO'),(674,113,'core ','113',1224778484,0,'NO'),(675,113,'cpu ','57',1224778484,0,'NO'),(676,113,'host ','node-29',1224778484,0,'NO'),(677,113,'cpuset','0',1224778484,0,'NO'),(678,113,'network_address','127.0.2.29',1224778484,0,'NO'),(679,114,'state','Alive',1224778484,0,'NO'),(680,114,'core ','114',1224778484,0,'NO'),(681,114,'cpu ','57',1224778484,0,'NO'),(682,114,'host ','node-29',1224778484,0,'NO'),(683,114,'cpuset','1',1224778484,0,'NO'),(684,114,'network_address','127.0.2.29',1224778484,0,'NO'),(685,115,'state','Alive',1224778484,0,'NO'),(686,115,'core ','115',1224778484,0,'NO'),(687,115,'cpu ','58',1224778484,0,'NO'),(688,115,'host ','node-29',1224778484,0,'NO'),(689,115,'cpuset','2',1224778484,0,'NO'),(690,115,'network_address','127.0.2.29',1224778484,0,'NO'),(691,116,'state','Alive',1224778484,0,'NO'),(692,116,'core ','116',1224778484,0,'NO'),(693,116,'cpu ','58',1224778484,0,'NO'),(694,116,'host ','node-29',1224778484,0,'NO'),(695,116,'cpuset','3',1224778484,0,'NO'),(696,116,'network_address','127.0.2.29',1224778484,0,'NO'),(697,117,'state','Alive',1224778485,0,'NO'),(698,117,'core ','117',1224778485,0,'NO'),(699,117,'cpu ','59',1224778485,0,'NO'),(700,117,'host ','node-30',1224778485,0,'NO'),(701,117,'cpuset','0',1224778485,0,'NO'),(702,117,'network_address','127.0.2.30',1224778485,0,'NO'),(703,118,'state','Alive',1224778485,0,'NO'),(704,118,'core ','118',1224778485,0,'NO'),(705,118,'cpu ','59',1224778485,0,'NO'),(706,118,'host ','node-30',1224778485,0,'NO'),(707,118,'cpuset','1',1224778485,0,'NO'),(708,118,'network_address','127.0.2.30',1224778485,0,'NO'),(709,119,'state','Alive',1224778485,0,'NO'),(710,119,'core ','119',1224778485,0,'NO'),(711,119,'cpu ','60',1224778485,0,'NO'),(712,119,'host ','node-30',1224778485,0,'NO'),(713,119,'cpuset','2',1224778485,0,'NO'),(714,119,'network_address','127.0.2.30',1224778485,0,'NO'),(715,120,'state','Alive',1224778485,0,'NO'),(716,120,'core ','120',1224778485,0,'NO'),(717,120,'cpu ','60',1224778485,0,'NO'),(718,120,'host ','node-30',1224778485,0,'NO'),(719,120,'cpuset','3',1224778485,0,'NO'),(720,120,'network_address','127.0.2.30',1224778485,0,'NO'),(721,121,'state','Alive',1224778485,0,'NO'),(722,121,'core ','121',1224778485,0,'NO'),(723,121,'cpu ','61',1224778485,0,'NO'),(724,121,'host ','node-31',1224778485,0,'NO'),(725,121,'cpuset','0',1224778485,0,'NO'),(726,121,'network_address','127.0.2.31',1224778485,0,'NO'),(727,122,'state','Alive',1224778486,0,'NO'),(728,122,'core ','122',1224778486,0,'NO'),(729,122,'cpu ','61',1224778486,0,'NO'),(730,122,'host ','node-31',1224778486,0,'NO'),(731,122,'cpuset','1',1224778486,0,'NO'),(732,122,'network_address','127.0.2.31',1224778486,0,'NO'),(733,123,'state','Alive',1224778486,0,'NO'),(734,123,'core ','123',1224778486,0,'NO'),(735,123,'cpu ','62',1224778486,0,'NO'),(736,123,'host ','node-31',1224778486,0,'NO'),(737,123,'cpuset','2',1224778486,0,'NO'),(738,123,'network_address','127.0.2.31',1224778486,0,'NO'),(739,124,'state','Alive',1224778486,0,'NO'),(740,124,'core ','124',1224778486,0,'NO'),(741,124,'cpu ','62',1224778486,0,'NO'),(742,124,'host ','node-31',1224778486,0,'NO'),(743,124,'cpuset','3',1224778486,0,'NO'),(744,124,'network_address','127.0.2.31',1224778486,0,'NO'),(745,125,'state','Alive',1224778486,0,'NO'),(746,125,'core ','125',1224778486,0,'NO'),(747,125,'cpu ','63',1224778486,0,'NO'),(748,125,'host ','node-32',1224778486,0,'NO'),(749,125,'cpuset','0',1224778486,0,'NO'),(750,125,'network_address','127.0.2.32',1224778486,0,'NO'),(751,126,'state','Alive',1224778486,0,'NO'),(752,126,'core ','126',1224778486,0,'NO'),(753,126,'cpu ','63',1224778486,0,'NO'),(754,126,'host ','node-32',1224778486,0,'NO'),(755,126,'cpuset','1',1224778486,0,'NO'),(756,126,'network_address','127.0.2.32',1224778486,0,'NO'),(757,127,'state','Alive',1224778487,0,'NO'),(758,127,'core ','127',1224778487,0,'NO'),(759,127,'cpu ','64',1224778487,0,'NO'),(760,127,'host ','node-32',1224778487,0,'NO'),(761,127,'cpuset','2',1224778487,0,'NO'),(762,127,'network_address','127.0.2.32',1224778487,0,'NO'),(763,128,'state','Alive',1224778487,0,'NO'),(764,128,'core ','128',1224778487,0,'NO'),(765,128,'cpu ','64',1224778487,0,'NO'),(766,128,'host ','node-32',1224778487,0,'NO'),(767,128,'cpuset','3',1224778487,0,'NO'),(768,128,'network_address','127.0.2.32',1224778487,0,'NO'),(769,129,'state','Alive',1224778487,0,'NO'),(770,129,'core ','129',1224778487,0,'NO'),(771,129,'cpu ','65',1224778487,0,'NO'),(772,129,'host ','node-33',1224778487,0,'NO'),(773,129,'cpuset','0',1224778487,0,'NO'),(774,129,'network_address','127.0.2.33',1224778487,0,'NO'),(775,130,'state','Alive',1224778487,0,'NO'),(776,130,'core ','130',1224778487,0,'NO'),(777,130,'cpu ','65',1224778487,0,'NO'),(778,130,'host ','node-33',1224778487,0,'NO'),(779,130,'cpuset','1',1224778487,0,'NO'),(780,130,'network_address','127.0.2.33',1224778487,0,'NO'),(781,131,'state','Alive',1224778487,0,'NO'),(782,131,'core ','131',1224778487,0,'NO'),(783,131,'cpu ','66',1224778487,0,'NO'),(784,131,'host ','node-33',1224778487,0,'NO'),(785,131,'cpuset','2',1224778487,0,'NO'),(786,131,'network_address','127.0.2.33',1224778487,0,'NO'),(787,132,'state','Alive',1224778487,0,'NO'),(788,132,'core ','132',1224778487,0,'NO'),(789,132,'cpu ','66',1224778487,0,'NO'),(790,132,'host ','node-33',1224778487,0,'NO'),(791,132,'cpuset','3',1224778487,0,'NO'),(792,132,'network_address','127.0.2.33',1224778487,0,'NO'),(793,133,'state','Alive',1224778488,0,'NO'),(794,133,'core ','133',1224778488,0,'NO'),(795,133,'cpu ','67',1224778488,0,'NO'),(796,133,'host ','node-34',1224778488,0,'NO'),(797,133,'cpuset','0',1224778488,0,'NO'),(798,133,'network_address','127.0.2.34',1224778488,0,'NO'),(799,134,'state','Alive',1224778488,0,'NO'),(800,134,'core ','134',1224778488,0,'NO'),(801,134,'cpu ','67',1224778488,0,'NO'),(802,134,'host ','node-34',1224778488,0,'NO'),(803,134,'cpuset','1',1224778488,0,'NO'),(804,134,'network_address','127.0.2.34',1224778488,0,'NO'),(805,135,'state','Alive',1224778488,0,'NO'),(806,135,'core ','135',1224778488,0,'NO'),(807,135,'cpu ','68',1224778488,0,'NO'),(808,135,'host ','node-34',1224778488,0,'NO'),(809,135,'cpuset','2',1224778488,0,'NO'),(810,135,'network_address','127.0.2.34',1224778488,0,'NO'),(811,136,'state','Alive',1224778488,0,'NO'),(812,136,'core ','136',1224778488,0,'NO'),(813,136,'cpu ','68',1224778488,0,'NO'),(814,136,'host ','node-34',1224778488,0,'NO'),(815,136,'cpuset','3',1224778488,0,'NO'),(816,136,'network_address','127.0.2.34',1224778488,0,'NO'),(817,137,'state','Alive',1224778488,0,'NO'),(818,137,'core ','137',1224778488,0,'NO'),(819,137,'cpu ','69',1224778488,0,'NO'),(820,137,'host ','node-35',1224778488,0,'NO'),(821,137,'cpuset','0',1224778488,0,'NO'),(822,137,'network_address','127.0.2.35',1224778488,0,'NO'),(823,138,'state','Alive',1224778489,0,'NO'),(824,138,'core ','138',1224778489,0,'NO'),(825,138,'cpu ','69',1224778489,0,'NO'),(826,138,'host ','node-35',1224778489,0,'NO'),(827,138,'cpuset','1',1224778489,0,'NO'),(828,138,'network_address','127.0.2.35',1224778489,0,'NO'),(829,139,'state','Alive',1224778489,0,'NO'),(830,139,'core ','139',1224778489,0,'NO'),(831,139,'cpu ','70',1224778489,0,'NO'),(832,139,'host ','node-35',1224778489,0,'NO'),(833,139,'cpuset','2',1224778489,0,'NO'),(834,139,'network_address','127.0.2.35',1224778489,0,'NO'),(835,140,'state','Alive',1224778489,0,'NO'),(836,140,'core ','140',1224778489,0,'NO'),(837,140,'cpu ','70',1224778489,0,'NO'),(838,140,'host ','node-35',1224778489,0,'NO'),(839,140,'cpuset','3',1224778489,0,'NO'),(840,140,'network_address','127.0.2.35',1224778489,0,'NO'),(841,141,'state','Alive',1224778489,0,'NO'),(842,141,'core ','141',1224778489,0,'NO'),(843,141,'cpu ','71',1224778489,0,'NO'),(844,141,'host ','node-36',1224778489,0,'NO'),(845,141,'cpuset','0',1224778489,0,'NO'),(846,141,'network_address','127.0.2.36',1224778489,0,'NO'),(847,142,'state','Alive',1224778489,0,'NO'),(848,142,'core ','142',1224778489,0,'NO'),(849,142,'cpu ','71',1224778489,0,'NO'),(850,142,'host ','node-36',1224778489,0,'NO'),(851,142,'cpuset','1',1224778489,0,'NO'),(852,142,'network_address','127.0.2.36',1224778489,0,'NO'),(853,143,'state','Alive',1224778490,0,'NO'),(854,143,'core ','143',1224778490,0,'NO'),(855,143,'cpu ','72',1224778490,0,'NO'),(856,143,'host ','node-36',1224778490,0,'NO'),(857,143,'cpuset','2',1224778490,0,'NO'),(858,143,'network_address','127.0.2.36',1224778490,0,'NO'),(859,144,'state','Alive',1224778490,0,'NO'),(860,144,'core ','144',1224778490,0,'NO'),(861,144,'cpu ','72',1224778490,0,'NO'),(862,144,'host ','node-36',1224778490,0,'NO'),(863,144,'cpuset','3',1224778490,0,'NO'),(864,144,'network_address','127.0.2.36',1224778490,0,'NO'),(865,145,'state','Alive',1224778490,0,'NO'),(866,145,'core ','145',1224778490,0,'NO'),(867,145,'cpu ','73',1224778490,0,'NO'),(868,145,'host ','node-37',1224778490,0,'NO'),(869,145,'cpuset','0',1224778490,0,'NO'),(870,145,'network_address','127.0.2.37',1224778490,0,'NO'),(871,146,'state','Alive',1224778490,0,'NO'),(872,146,'core ','146',1224778490,0,'NO'),(873,146,'cpu ','73',1224778490,0,'NO'),(874,146,'host ','node-37',1224778490,0,'NO'),(875,146,'cpuset','1',1224778490,0,'NO'),(876,146,'network_address','127.0.2.37',1224778490,0,'NO'),(877,147,'state','Alive',1224778490,0,'NO'),(878,147,'core ','147',1224778490,0,'NO'),(879,147,'cpu ','74',1224778490,0,'NO'),(880,147,'host ','node-37',1224778490,0,'NO'),(881,147,'cpuset','2',1224778490,0,'NO'),(882,147,'network_address','127.0.2.37',1224778490,0,'NO'),(883,148,'state','Alive',1224778491,0,'NO'),(884,148,'core ','148',1224778491,0,'NO'),(885,148,'cpu ','74',1224778491,0,'NO'),(886,148,'host ','node-37',1224778491,0,'NO'),(887,148,'cpuset','3',1224778491,0,'NO'),(888,148,'network_address','127.0.2.37',1224778491,0,'NO'),(889,149,'state','Alive',1224778491,0,'NO'),(890,149,'core ','149',1224778491,0,'NO'),(891,149,'cpu ','75',1224778491,0,'NO'),(892,149,'host ','node-38',1224778491,0,'NO'),(893,149,'cpuset','0',1224778491,0,'NO'),(894,149,'network_address','127.0.2.38',1224778491,0,'NO'),(895,150,'state','Alive',1224778491,0,'NO'),(896,150,'core ','150',1224778491,0,'NO'),(897,150,'cpu ','75',1224778491,0,'NO'),(898,150,'host ','node-38',1224778491,0,'NO'),(899,150,'cpuset','1',1224778491,0,'NO'),(900,150,'network_address','127.0.2.38',1224778491,0,'NO'),(901,151,'state','Alive',1224778491,0,'NO'),(902,151,'core ','151',1224778491,0,'NO'),(903,151,'cpu ','76',1224778491,0,'NO'),(904,151,'host ','node-38',1224778491,0,'NO'),(905,151,'cpuset','2',1224778491,0,'NO'),(906,151,'network_address','127.0.2.38',1224778491,0,'NO'),(907,152,'state','Alive',1224778491,0,'NO'),(908,152,'core ','152',1224778491,0,'NO'),(909,152,'cpu ','76',1224778491,0,'NO'),(910,152,'host ','node-38',1224778491,0,'NO'),(911,152,'cpuset','3',1224778491,0,'NO'),(912,152,'network_address','127.0.2.38',1224778491,0,'NO'),(913,153,'state','Alive',1224778491,0,'NO'),(914,153,'core ','153',1224778491,0,'NO'),(915,153,'cpu ','77',1224778491,0,'NO'),(916,153,'host ','node-39',1224778491,0,'NO'),(917,153,'cpuset','0',1224778491,0,'NO'),(918,153,'network_address','127.0.2.39',1224778491,0,'NO'),(919,154,'state','Alive',1224778492,0,'NO'),(920,154,'core ','154',1224778492,0,'NO'),(921,154,'cpu ','77',1224778492,0,'NO'),(922,154,'host ','node-39',1224778492,0,'NO'),(923,154,'cpuset','1',1224778492,0,'NO'),(924,154,'network_address','127.0.2.39',1224778492,0,'NO'),(925,155,'state','Alive',1224778492,0,'NO'),(926,155,'core ','155',1224778492,0,'NO'),(927,155,'cpu ','78',1224778492,0,'NO'),(928,155,'host ','node-39',1224778492,0,'NO'),(929,155,'cpuset','2',1224778492,0,'NO'),(930,155,'network_address','127.0.2.39',1224778492,0,'NO'),(931,156,'state','Alive',1224778492,0,'NO'),(932,156,'core ','156',1224778492,0,'NO'),(933,156,'cpu ','78',1224778492,0,'NO'),(934,156,'host ','node-39',1224778492,0,'NO'),(935,156,'cpuset','3',1224778492,0,'NO'),(936,156,'network_address','127.0.2.39',1224778492,0,'NO'),(937,157,'state','Alive',1224778492,0,'NO'),(938,157,'core ','157',1224778492,0,'NO'),(939,157,'cpu ','79',1224778492,0,'NO'),(940,157,'host ','node-40',1224778492,0,'NO'),(941,157,'cpuset','0',1224778492,0,'NO'),(942,157,'network_address','127.0.2.40',1224778492,0,'NO'),(943,158,'state','Alive',1224778492,0,'NO'),(944,158,'core ','158',1224778492,0,'NO'),(945,158,'cpu ','79',1224778492,0,'NO'),(946,158,'host ','node-40',1224778492,0,'NO'),(947,158,'cpuset','1',1224778492,0,'NO'),(948,158,'network_address','127.0.2.40',1224778492,0,'NO'),(949,159,'state','Alive',1224778493,0,'NO'),(950,159,'core ','159',1224778493,0,'NO'),(951,159,'cpu ','80',1224778493,0,'NO'),(952,159,'host ','node-40',1224778493,0,'NO'),(953,159,'cpuset','2',1224778493,0,'NO'),(954,159,'network_address','127.0.2.40',1224778493,0,'NO'),(955,160,'state','Alive',1224778493,0,'NO'),(956,160,'core ','160',1224778493,0,'NO'),(957,160,'cpu ','80',1224778493,0,'NO'),(958,160,'host ','node-40',1224778493,0,'NO'),(959,160,'cpuset','3',1224778493,0,'NO'),(960,160,'network_address','127.0.2.40',1224778493,0,'NO'),(961,161,'state','Alive',1224778493,0,'NO'),(962,161,'core ','161',1224778493,0,'NO'),(963,161,'cpu ','81',1224778493,0,'NO'),(964,161,'host ','node-41',1224778493,0,'NO'),(965,161,'cpuset','0',1224778493,0,'NO'),(966,161,'network_address','127.0.2.41',1224778493,0,'NO'),(967,162,'state','Alive',1224778493,0,'NO'),(968,162,'core ','162',1224778493,0,'NO'),(969,162,'cpu ','81',1224778493,0,'NO'),(970,162,'host ','node-41',1224778493,0,'NO'),(971,162,'cpuset','1',1224778493,0,'NO'),(972,162,'network_address','127.0.2.41',1224778493,0,'NO'),(973,163,'state','Alive',1224778493,0,'NO'),(974,163,'core ','163',1224778493,0,'NO'),(975,163,'cpu ','82',1224778493,0,'NO'),(976,163,'host ','node-41',1224778493,0,'NO'),(977,163,'cpuset','2',1224778493,0,'NO'),(978,163,'network_address','127.0.2.41',1224778493,0,'NO'),(979,164,'state','Alive',1224778494,0,'NO'),(980,164,'core ','164',1224778494,0,'NO'),(981,164,'cpu ','82',1224778494,0,'NO'),(982,164,'host ','node-41',1224778494,0,'NO'),(983,164,'cpuset','3',1224778494,0,'NO'),(984,164,'network_address','127.0.2.41',1224778494,0,'NO'),(985,165,'state','Alive',1224778494,0,'NO'),(986,165,'core ','165',1224778494,0,'NO'),(987,165,'cpu ','83',1224778494,0,'NO'),(988,165,'host ','node-42',1224778494,0,'NO'),(989,165,'cpuset','0',1224778494,0,'NO'),(990,165,'network_address','127.0.2.42',1224778494,0,'NO'),(991,166,'state','Alive',1224778494,0,'NO'),(992,166,'core ','166',1224778494,0,'NO'),(993,166,'cpu ','83',1224778494,0,'NO'),(994,166,'host ','node-42',1224778494,0,'NO'),(995,166,'cpuset','1',1224778494,0,'NO'),(996,166,'network_address','127.0.2.42',1224778494,0,'NO'),(997,167,'state','Alive',1224778494,0,'NO'),(998,167,'core ','167',1224778494,0,'NO'),(999,167,'cpu ','84',1224778494,0,'NO'),(1000,167,'host ','node-42',1224778494,0,'NO'),(1001,167,'cpuset','2',1224778494,0,'NO'),(1002,167,'network_address','127.0.2.42',1224778494,0,'NO'),(1003,168,'state','Alive',1224778494,0,'NO'),(1004,168,'core ','168',1224778494,0,'NO'),(1005,168,'cpu ','84',1224778494,0,'NO'),(1006,168,'host ','node-42',1224778494,0,'NO'),(1007,168,'cpuset','3',1224778494,0,'NO'),(1008,168,'network_address','127.0.2.42',1224778494,0,'NO'),(1009,169,'state','Alive',1224778495,0,'NO'),(1010,169,'core ','169',1224778495,0,'NO'),(1011,169,'cpu ','85',1224778495,0,'NO'),(1012,169,'host ','node-43',1224778495,0,'NO'),(1013,169,'cpuset','0',1224778495,0,'NO'),(1014,169,'network_address','127.0.2.43',1224778495,0,'NO'),(1015,170,'state','Alive',1224778495,0,'NO'),(1016,170,'core ','170',1224778495,0,'NO'),(1017,170,'cpu ','85',1224778495,0,'NO'),(1018,170,'host ','node-43',1224778495,0,'NO'),(1019,170,'cpuset','1',1224778495,0,'NO'),(1020,170,'network_address','127.0.2.43',1224778495,0,'NO'),(1021,171,'state','Alive',1224778495,0,'NO'),(1022,171,'core ','171',1224778495,0,'NO'),(1023,171,'cpu ','86',1224778495,0,'NO'),(1024,171,'host ','node-43',1224778495,0,'NO'),(1025,171,'cpuset','2',1224778495,0,'NO'),(1026,171,'network_address','127.0.2.43',1224778495,0,'NO'),(1027,172,'state','Alive',1224778495,0,'NO'),(1028,172,'core ','172',1224778495,0,'NO'),(1029,172,'cpu ','86',1224778495,0,'NO'),(1030,172,'host ','node-43',1224778495,0,'NO'),(1031,172,'cpuset','3',1224778495,0,'NO'),(1032,172,'network_address','127.0.2.43',1224778495,0,'NO'),(1033,173,'state','Alive',1224778495,0,'NO'),(1034,173,'core ','173',1224778495,0,'NO'),(1035,173,'cpu ','87',1224778495,0,'NO'),(1036,173,'host ','node-44',1224778495,0,'NO'),(1037,173,'cpuset','0',1224778495,0,'NO'),(1038,173,'network_address','127.0.2.44',1224778495,0,'NO'),(1039,174,'state','Alive',1224778496,0,'NO'),(1040,174,'core ','174',1224778496,0,'NO'),(1041,174,'cpu ','87',1224778496,0,'NO'),(1042,174,'host ','node-44',1224778496,0,'NO'),(1043,174,'cpuset','1',1224778496,0,'NO'),(1044,174,'network_address','127.0.2.44',1224778496,0,'NO'),(1045,175,'state','Alive',1224778496,0,'NO'),(1046,175,'core ','175',1224778496,0,'NO'),(1047,175,'cpu ','88',1224778496,0,'NO'),(1048,175,'host ','node-44',1224778496,0,'NO'),(1049,175,'cpuset','2',1224778496,0,'NO'),(1050,175,'network_address','127.0.2.44',1224778496,0,'NO'),(1051,176,'state','Alive',1224778496,0,'NO'),(1052,176,'core ','176',1224778496,0,'NO'),(1053,176,'cpu ','88',1224778496,0,'NO'),(1054,176,'host ','node-44',1224778496,0,'NO'),(1055,176,'cpuset','3',1224778496,0,'NO'),(1056,176,'network_address','127.0.2.44',1224778496,0,'NO'),(1057,177,'state','Alive',1224778496,0,'NO'),(1058,177,'core ','177',1224778496,0,'NO'),(1059,177,'cpu ','89',1224778496,0,'NO'),(1060,177,'host ','node-45',1224778496,0,'NO'),(1061,177,'cpuset','0',1224778496,0,'NO'),(1062,177,'network_address','127.0.2.45',1224778496,0,'NO'),(1063,178,'state','Alive',1224778497,0,'NO'),(1064,178,'core ','178',1224778497,0,'NO'),(1065,178,'cpu ','89',1224778497,0,'NO'),(1066,178,'host ','node-45',1224778497,0,'NO'),(1067,178,'cpuset','1',1224778497,0,'NO'),(1068,178,'network_address','127.0.2.45',1224778497,0,'NO'),(1069,179,'state','Alive',1224778497,0,'NO'),(1070,179,'core ','179',1224778497,0,'NO'),(1071,179,'cpu ','90',1224778497,0,'NO'),(1072,179,'host ','node-45',1224778497,0,'NO'),(1073,179,'cpuset','2',1224778497,0,'NO'),(1074,179,'network_address','127.0.2.45',1224778497,0,'NO'),(1075,180,'state','Alive',1224778497,0,'NO'),(1076,180,'core ','180',1224778497,0,'NO'),(1077,180,'cpu ','90',1224778497,0,'NO'),(1078,180,'host ','node-45',1224778497,0,'NO'),(1079,180,'cpuset','3',1224778497,0,'NO'),(1080,180,'network_address','127.0.2.45',1224778497,0,'NO'),(1081,181,'state','Alive',1224778497,0,'NO'),(1082,181,'core ','181',1224778497,0,'NO'),(1083,181,'cpu ','91',1224778497,0,'NO'),(1084,181,'host ','node-46',1224778497,0,'NO'),(1085,181,'cpuset','0',1224778497,0,'NO'),(1086,181,'network_address','127.0.2.46',1224778497,0,'NO'),(1087,182,'state','Alive',1224778497,0,'NO'),(1088,182,'core ','182',1224778497,0,'NO'),(1089,182,'cpu ','91',1224778497,0,'NO'),(1090,182,'host ','node-46',1224778497,0,'NO'),(1091,182,'cpuset','1',1224778497,0,'NO'),(1092,182,'network_address','127.0.2.46',1224778497,0,'NO'),(1093,183,'state','Alive',1224778497,0,'NO'),(1094,183,'core ','183',1224778497,0,'NO'),(1095,183,'cpu ','92',1224778497,0,'NO'),(1096,183,'host ','node-46',1224778497,0,'NO'),(1097,183,'cpuset','2',1224778497,0,'NO'),(1098,183,'network_address','127.0.2.46',1224778497,0,'NO'),(1099,184,'state','Alive',1224778498,0,'NO'),(1100,184,'core ','184',1224778498,0,'NO'),(1101,184,'cpu ','92',1224778498,0,'NO'),(1102,184,'host ','node-46',1224778498,0,'NO'),(1103,184,'cpuset','3',1224778498,0,'NO'),(1104,184,'network_address','127.0.2.46',1224778498,0,'NO'),(1105,185,'state','Alive',1224778498,0,'NO'),(1106,185,'core ','185',1224778498,0,'NO'),(1107,185,'cpu ','93',1224778498,0,'NO'),(1108,185,'host ','node-47',1224778498,0,'NO'),(1109,185,'cpuset','0',1224778498,0,'NO'),(1110,185,'network_address','127.0.2.47',1224778498,0,'NO'),(1111,186,'state','Alive',1224778498,0,'NO'),(1112,186,'core ','186',1224778498,0,'NO'),(1113,186,'cpu ','93',1224778498,0,'NO'),(1114,186,'host ','node-47',1224778498,0,'NO'),(1115,186,'cpuset','1',1224778498,0,'NO'),(1116,186,'network_address','127.0.2.47',1224778498,0,'NO'),(1117,187,'state','Alive',1224778498,0,'NO'),(1118,187,'core ','187',1224778498,0,'NO'),(1119,187,'cpu ','94',1224778498,0,'NO'),(1120,187,'host ','node-47',1224778498,0,'NO'),(1121,187,'cpuset','2',1224778498,0,'NO'),(1122,187,'network_address','127.0.2.47',1224778498,0,'NO'),(1123,188,'state','Alive',1224778498,0,'NO'),(1124,188,'core ','188',1224778498,0,'NO'),(1125,188,'cpu ','94',1224778498,0,'NO'),(1126,188,'host ','node-47',1224778498,0,'NO'),(1127,188,'cpuset','3',1224778498,0,'NO'),(1128,188,'network_address','127.0.2.47',1224778498,0,'NO'),(1129,189,'state','Alive',1224778499,0,'NO'),(1130,189,'core ','189',1224778499,0,'NO'),(1131,189,'cpu ','95',1224778499,0,'NO'),(1132,189,'host ','node-48',1224778499,0,'NO'),(1133,189,'cpuset','0',1224778499,0,'NO'),(1134,189,'network_address','127.0.2.48',1224778499,0,'NO'),(1135,190,'state','Alive',1224778499,0,'NO'),(1136,190,'core ','190',1224778499,0,'NO'),(1137,190,'cpu ','95',1224778499,0,'NO'),(1138,190,'host ','node-48',1224778499,0,'NO'),(1139,190,'cpuset','1',1224778499,0,'NO'),(1140,190,'network_address','127.0.2.48',1224778499,0,'NO'),(1141,191,'state','Alive',1224778499,0,'NO'),(1142,191,'core ','191',1224778499,0,'NO'),(1143,191,'cpu ','96',1224778499,0,'NO'),(1144,191,'host ','node-48',1224778499,0,'NO'),(1145,191,'cpuset','2',1224778499,0,'NO'),(1146,191,'network_address','127.0.2.48',1224778499,0,'NO'),(1147,192,'state','Alive',1224778499,0,'NO'),(1148,192,'core ','192',1224778499,0,'NO'),(1149,192,'cpu ','96',1224778499,0,'NO'),(1150,192,'host ','node-48',1224778499,0,'NO'),(1151,192,'cpuset','3',1224778499,0,'NO'),(1152,192,'network_address','127.0.2.48',1224778499,0,'NO'),(1153,193,'state','Alive',1224778499,0,'NO'),(1154,193,'core ','193',1224778499,0,'NO'),(1155,193,'cpu ','97',1224778499,0,'NO'),(1156,193,'host ','node-49',1224778499,0,'NO'),(1157,193,'cpuset','0',1224778499,0,'NO'),(1158,193,'network_address','127.0.2.49',1224778499,0,'NO'),(1159,194,'state','Alive',1224778500,0,'NO'),(1160,194,'core ','194',1224778500,0,'NO'),(1161,194,'cpu ','97',1224778500,0,'NO'),(1162,194,'host ','node-49',1224778500,0,'NO'),(1163,194,'cpuset','1',1224778500,0,'NO'),(1164,194,'network_address','127.0.2.49',1224778500,0,'NO'),(1165,195,'state','Alive',1224778500,0,'NO'),(1166,195,'core ','195',1224778500,0,'NO'),(1167,195,'cpu ','98',1224778500,0,'NO'),(1168,195,'host ','node-49',1224778500,0,'NO'),(1169,195,'cpuset','2',1224778500,0,'NO'),(1170,195,'network_address','127.0.2.49',1224778500,0,'NO'),(1171,196,'state','Alive',1224778500,0,'NO'),(1172,196,'core ','196',1224778500,0,'NO'),(1173,196,'cpu ','98',1224778500,0,'NO'),(1174,196,'host ','node-49',1224778500,0,'NO'),(1175,196,'cpuset','3',1224778500,0,'NO'),(1176,196,'network_address','127.0.2.49',1224778500,0,'NO'),(1177,197,'state','Alive',1224778500,0,'NO'),(1178,197,'core ','197',1224778500,0,'NO'),(1179,197,'cpu ','99',1224778500,0,'NO'),(1180,197,'host ','node-50',1224778500,0,'NO'),(1181,197,'cpuset','0',1224778500,0,'NO'),(1182,197,'network_address','127.0.2.50',1224778500,0,'NO'),(1183,198,'state','Alive',1224778500,0,'NO'),(1184,198,'core ','198',1224778500,0,'NO'),(1185,198,'cpu ','99',1224778500,0,'NO'),(1186,198,'host ','node-50',1224778500,0,'NO'),(1187,198,'cpuset','1',1224778500,0,'NO'),(1188,198,'network_address','127.0.2.50',1224778500,0,'NO'),(1189,199,'state','Alive',1224778501,0,'NO'),(1190,199,'core ','199',1224778501,0,'NO'),(1191,199,'cpu ','100',1224778501,0,'NO'),(1192,199,'host ','node-50',1224778501,0,'NO'),(1193,199,'cpuset','2',1224778501,0,'NO'),(1194,199,'network_address','127.0.2.50',1224778501,0,'NO'),(1195,200,'state','Alive',1224778501,0,'NO'),(1196,200,'core ','200',1224778501,0,'NO'),(1197,200,'cpu ','100',1224778501,0,'NO'),(1198,200,'host ','node-50',1224778501,0,'NO'),(1199,200,'cpuset','3',1224778501,0,'NO'),(1200,200,'network_address','127.0.2.50',1224778501,0,'NO'),(1201,201,'state','Alive',1224778501,0,'NO'),(1202,201,'core ','201',1224778501,0,'NO'),(1203,201,'cpu ','101',1224778501,0,'NO'),(1204,201,'host ','node-51',1224778501,0,'NO'),(1205,201,'cpuset','0',1224778501,0,'NO'),(1206,201,'network_address','127.0.2.51',1224778501,0,'NO'),(1207,202,'state','Alive',1224778501,0,'NO'),(1208,202,'core ','202',1224778501,0,'NO'),(1209,202,'cpu ','101',1224778501,0,'NO'),(1210,202,'host ','node-51',1224778501,0,'NO'),(1211,202,'cpuset','1',1224778501,0,'NO'),(1212,202,'network_address','127.0.2.51',1224778501,0,'NO'),(1213,203,'state','Alive',1224778501,0,'NO'),(1214,203,'core ','203',1224778501,0,'NO'),(1215,203,'cpu ','102',1224778501,0,'NO'),(1216,203,'host ','node-51',1224778501,0,'NO'),(1217,203,'cpuset','2',1224778501,0,'NO'),(1218,203,'network_address','127.0.2.51',1224778501,0,'NO'),(1219,204,'state','Alive',1224778502,0,'NO'),(1220,204,'core ','204',1224778502,0,'NO'),(1221,204,'cpu ','102',1224778502,0,'NO'),(1222,204,'host ','node-51',1224778502,0,'NO'),(1223,204,'cpuset','3',1224778502,0,'NO'),(1224,204,'network_address','127.0.2.51',1224778502,0,'NO'),(1225,205,'state','Alive',1224778502,0,'NO'),(1226,205,'core ','205',1224778502,0,'NO'),(1227,205,'cpu ','103',1224778502,0,'NO'),(1228,205,'host ','node-52',1224778502,0,'NO'),(1229,205,'cpuset','0',1224778502,0,'NO'),(1230,205,'network_address','127.0.2.52',1224778502,0,'NO'),(1231,206,'state','Alive',1224778502,0,'NO'),(1232,206,'core ','206',1224778502,0,'NO'),(1233,206,'cpu ','103',1224778502,0,'NO'),(1234,206,'host ','node-52',1224778502,0,'NO'),(1235,206,'cpuset','1',1224778502,0,'NO'),(1236,206,'network_address','127.0.2.52',1224778502,0,'NO'),(1237,207,'state','Alive',1224778502,0,'NO'),(1238,207,'core ','207',1224778502,0,'NO'),(1239,207,'cpu ','104',1224778502,0,'NO'),(1240,207,'host ','node-52',1224778502,0,'NO'),(1241,207,'cpuset','2',1224778502,0,'NO'),(1242,207,'network_address','127.0.2.52',1224778502,0,'NO'),(1243,208,'state','Alive',1224778502,0,'NO'),(1244,208,'core ','208',1224778502,0,'NO'),(1245,208,'cpu ','104',1224778502,0,'NO'),(1246,208,'host ','node-52',1224778502,0,'NO'),(1247,208,'cpuset','3',1224778502,0,'NO'),(1248,208,'network_address','127.0.2.52',1224778502,0,'NO'),(1249,209,'state','Alive',1224778502,0,'NO'),(1250,209,'core ','209',1224778502,0,'NO'),(1251,209,'cpu ','105',1224778502,0,'NO'),(1252,209,'host ','node-53',1224778502,0,'NO'),(1253,209,'cpuset','0',1224778502,0,'NO'),(1254,209,'network_address','127.0.2.53',1224778502,0,'NO'),(1255,210,'state','Alive',1224778503,0,'NO'),(1256,210,'core ','210',1224778503,0,'NO'),(1257,210,'cpu ','105',1224778503,0,'NO'),(1258,210,'host ','node-53',1224778503,0,'NO'),(1259,210,'cpuset','1',1224778503,0,'NO'),(1260,210,'network_address','127.0.2.53',1224778503,0,'NO'),(1261,211,'state','Alive',1224778503,0,'NO'),(1262,211,'core ','211',1224778503,0,'NO'),(1263,211,'cpu ','106',1224778503,0,'NO'),(1264,211,'host ','node-53',1224778503,0,'NO'),(1265,211,'cpuset','2',1224778503,0,'NO'),(1266,211,'network_address','127.0.2.53',1224778503,0,'NO'),(1267,212,'state','Alive',1224778503,0,'NO'),(1268,212,'core ','212',1224778503,0,'NO'),(1269,212,'cpu ','106',1224778503,0,'NO'),(1270,212,'host ','node-53',1224778503,0,'NO'),(1271,212,'cpuset','3',1224778503,0,'NO'),(1272,212,'network_address','127.0.2.53',1224778503,0,'NO'),(1273,213,'state','Alive',1224778503,0,'NO'),(1274,213,'core ','213',1224778503,0,'NO'),(1275,213,'cpu ','107',1224778503,0,'NO'),(1276,213,'host ','node-54',1224778503,0,'NO'),(1277,213,'cpuset','0',1224778503,0,'NO'),(1278,213,'network_address','127.0.2.54',1224778503,0,'NO'),(1279,214,'state','Alive',1224778503,0,'NO'),(1280,214,'core ','214',1224778503,0,'NO'),(1281,214,'cpu ','107',1224778503,0,'NO'),(1282,214,'host ','node-54',1224778503,0,'NO'),(1283,214,'cpuset','1',1224778503,0,'NO'),(1284,214,'network_address','127.0.2.54',1224778503,0,'NO'),(1285,215,'state','Alive',1224778504,0,'NO'),(1286,215,'core ','215',1224778504,0,'NO'),(1287,215,'cpu ','108',1224778504,0,'NO'),(1288,215,'host ','node-54',1224778504,0,'NO'),(1289,215,'cpuset','2',1224778504,0,'NO'),(1290,215,'network_address','127.0.2.54',1224778504,0,'NO'),(1291,216,'state','Alive',1224778504,0,'NO'),(1292,216,'core ','216',1224778504,0,'NO'),(1293,216,'cpu ','108',1224778504,0,'NO'),(1294,216,'host ','node-54',1224778504,0,'NO'),(1295,216,'cpuset','3',1224778504,0,'NO'),(1296,216,'network_address','127.0.2.54',1224778504,0,'NO'),(1297,217,'state','Alive',1224778504,0,'NO'),(1298,217,'core ','217',1224778504,0,'NO'),(1299,217,'cpu ','109',1224778504,0,'NO'),(1300,217,'host ','node-55',1224778504,0,'NO'),(1301,217,'cpuset','0',1224778504,0,'NO'),(1302,217,'network_address','127.0.2.55',1224778504,0,'NO'),(1303,218,'state','Alive',1224778504,0,'NO'),(1304,218,'core ','218',1224778504,0,'NO'),(1305,218,'cpu ','109',1224778504,0,'NO'),(1306,218,'host ','node-55',1224778504,0,'NO'),(1307,218,'cpuset','1',1224778504,0,'NO'),(1308,218,'network_address','127.0.2.55',1224778504,0,'NO'),(1309,219,'state','Alive',1224778504,0,'NO'),(1310,219,'core ','219',1224778504,0,'NO'),(1311,219,'cpu ','110',1224778504,0,'NO'),(1312,219,'host ','node-55',1224778504,0,'NO'),(1313,219,'cpuset','2',1224778504,0,'NO'),(1314,219,'network_address','127.0.2.55',1224778504,0,'NO'),(1315,220,'state','Alive',1224778505,0,'NO'),(1316,220,'core ','220',1224778505,0,'NO'),(1317,220,'cpu ','110',1224778505,0,'NO'),(1318,220,'host ','node-55',1224778505,0,'NO'),(1319,220,'cpuset','3',1224778505,0,'NO'),(1320,220,'network_address','127.0.2.55',1224778505,0,'NO'),(1321,221,'state','Alive',1224778505,0,'NO'),(1322,221,'core ','221',1224778505,0,'NO'),(1323,221,'cpu ','111',1224778505,0,'NO'),(1324,221,'host ','node-56',1224778505,0,'NO'),(1325,221,'cpuset','0',1224778505,0,'NO'),(1326,221,'network_address','127.0.2.56',1224778505,0,'NO'),(1327,222,'state','Alive',1224778505,0,'NO'),(1328,222,'core ','222',1224778505,0,'NO'),(1329,222,'cpu ','111',1224778505,0,'NO'),(1330,222,'host ','node-56',1224778505,0,'NO'),(1331,222,'cpuset','1',1224778505,0,'NO'),(1332,222,'network_address','127.0.2.56',1224778505,0,'NO'),(1333,223,'state','Alive',1224778505,0,'NO'),(1334,223,'core ','223',1224778505,0,'NO'),(1335,223,'cpu ','112',1224778505,0,'NO'),(1336,223,'host ','node-56',1224778505,0,'NO'),(1337,223,'cpuset','2',1224778505,0,'NO'),(1338,223,'network_address','127.0.2.56',1224778505,0,'NO'),(1339,224,'state','Alive',1224778505,0,'NO'),(1340,224,'core ','224',1224778505,0,'NO'),(1341,224,'cpu ','112',1224778505,0,'NO'),(1342,224,'host ','node-56',1224778505,0,'NO'),(1343,224,'cpuset','3',1224778505,0,'NO'),(1344,224,'network_address','127.0.2.56',1224778505,0,'NO'),(1345,225,'state','Alive',1224778506,0,'NO'),(1346,225,'core ','225',1224778506,0,'NO'),(1347,225,'cpu ','113',1224778506,0,'NO'),(1348,225,'host ','node-57',1224778506,0,'NO'),(1349,225,'cpuset','0',1224778506,0,'NO'),(1350,225,'network_address','127.0.2.57',1224778506,0,'NO'),(1351,226,'state','Alive',1224778506,0,'NO'),(1352,226,'core ','226',1224778506,0,'NO'),(1353,226,'cpu ','113',1224778506,0,'NO'),(1354,226,'host ','node-57',1224778506,0,'NO'),(1355,226,'cpuset','1',1224778506,0,'NO'),(1356,226,'network_address','127.0.2.57',1224778506,0,'NO'),(1357,227,'state','Alive',1224778506,0,'NO'),(1358,227,'core ','227',1224778506,0,'NO'),(1359,227,'cpu ','114',1224778506,0,'NO'),(1360,227,'host ','node-57',1224778506,0,'NO'),(1361,227,'cpuset','2',1224778506,0,'NO'),(1362,227,'network_address','127.0.2.57',1224778506,0,'NO'),(1363,228,'state','Alive',1224778506,0,'NO'),(1364,228,'core ','228',1224778506,0,'NO'),(1365,228,'cpu ','114',1224778506,0,'NO'),(1366,228,'host ','node-57',1224778506,0,'NO'),(1367,228,'cpuset','3',1224778506,0,'NO'),(1368,228,'network_address','127.0.2.57',1224778506,0,'NO'),(1369,229,'state','Alive',1224778506,0,'NO'),(1370,229,'core ','229',1224778506,0,'NO'),(1371,229,'cpu ','115',1224778506,0,'NO'),(1372,229,'host ','node-58',1224778506,0,'NO'),(1373,229,'cpuset','0',1224778506,0,'NO'),(1374,229,'network_address','127.0.2.58',1224778506,0,'NO'),(1375,230,'state','Alive',1224778507,0,'NO'),(1376,230,'core ','230',1224778507,0,'NO'),(1377,230,'cpu ','115',1224778507,0,'NO'),(1378,230,'host ','node-58',1224778507,0,'NO'),(1379,230,'cpuset','1',1224778507,0,'NO'),(1380,230,'network_address','127.0.2.58',1224778507,0,'NO'),(1381,231,'state','Alive',1224778507,0,'NO'),(1382,231,'core ','231',1224778507,0,'NO'),(1383,231,'cpu ','116',1224778507,0,'NO'),(1384,231,'host ','node-58',1224778507,0,'NO'),(1385,231,'cpuset','2',1224778507,0,'NO'),(1386,231,'network_address','127.0.2.58',1224778507,0,'NO'),(1387,232,'state','Alive',1224778507,0,'NO'),(1388,232,'core ','232',1224778507,0,'NO'),(1389,232,'cpu ','116',1224778507,0,'NO'),(1390,232,'host ','node-58',1224778507,0,'NO'),(1391,232,'cpuset','3',1224778507,0,'NO'),(1392,232,'network_address','127.0.2.58',1224778507,0,'NO'),(1393,233,'state','Alive',1224778507,0,'NO'),(1394,233,'core ','233',1224778507,0,'NO'),(1395,233,'cpu ','117',1224778507,0,'NO'),(1396,233,'host ','node-59',1224778507,0,'NO'),(1397,233,'cpuset','0',1224778507,0,'NO'),(1398,233,'network_address','127.0.2.59',1224778507,0,'NO'),(1399,234,'state','Alive',1224778507,0,'NO'),(1400,234,'core ','234',1224778507,0,'NO'),(1401,234,'cpu ','117',1224778507,0,'NO'),(1402,234,'host ','node-59',1224778507,0,'NO'),(1403,234,'cpuset','1',1224778507,0,'NO'),(1404,234,'network_address','127.0.2.59',1224778507,0,'NO'),(1405,235,'state','Alive',1224778507,0,'NO'),(1406,235,'core ','235',1224778507,0,'NO'),(1407,235,'cpu ','118',1224778507,0,'NO'),(1408,235,'host ','node-59',1224778507,0,'NO'),(1409,235,'cpuset','2',1224778507,0,'NO'),(1410,235,'network_address','127.0.2.59',1224778507,0,'NO'),(1411,236,'state','Alive',1224778508,0,'NO'),(1412,236,'core ','236',1224778508,0,'NO'),(1413,236,'cpu ','118',1224778508,0,'NO'),(1414,236,'host ','node-59',1224778508,0,'NO'),(1415,236,'cpuset','3',1224778508,0,'NO'),(1416,236,'network_address','127.0.2.59',1224778508,0,'NO'),(1417,237,'state','Alive',1224778508,0,'NO'),(1418,237,'core ','237',1224778508,0,'NO'),(1419,237,'cpu ','119',1224778508,0,'NO'),(1420,237,'host ','node-60',1224778508,0,'NO'),(1421,237,'cpuset','0',1224778508,0,'NO'),(1422,237,'network_address','127.0.2.60',1224778508,0,'NO'),(1423,238,'state','Alive',1224778508,0,'NO'),(1424,238,'core ','238',1224778508,0,'NO'),(1425,238,'cpu ','119',1224778508,0,'NO'),(1426,238,'host ','node-60',1224778508,0,'NO'),(1427,238,'cpuset','1',1224778508,0,'NO'),(1428,238,'network_address','127.0.2.60',1224778508,0,'NO'),(1429,239,'state','Alive',1224778508,0,'NO'),(1430,239,'core ','239',1224778508,0,'NO'),(1431,239,'cpu ','120',1224778508,0,'NO'),(1432,239,'host ','node-60',1224778508,0,'NO'),(1433,239,'cpuset','2',1224778508,0,'NO'),(1434,239,'network_address','127.0.2.60',1224778508,0,'NO'),(1435,240,'state','Alive',1224778508,0,'NO'),(1436,240,'core ','240',1224778508,0,'NO'),(1437,240,'cpu ','120',1224778508,0,'NO'),(1438,240,'host ','node-60',1224778508,0,'NO'),(1439,240,'cpuset','3',1224778508,0,'NO'),(1440,240,'network_address','127.0.2.60',1224778508,0,'NO'),(1441,241,'state','Alive',1224778509,0,'NO'),(1442,241,'core ','241',1224778509,0,'NO'),(1443,241,'cpu ','121',1224778509,0,'NO'),(1444,241,'host ','node-61',1224778509,0,'NO'),(1445,241,'cpuset','0',1224778509,0,'NO'),(1446,241,'network_address','127.0.2.61',1224778509,0,'NO'),(1447,242,'state','Alive',1224778509,0,'NO'),(1448,242,'core ','242',1224778509,0,'NO'),(1449,242,'cpu ','121',1224778509,0,'NO'),(1450,242,'host ','node-61',1224778509,0,'NO'),(1451,242,'cpuset','1',1224778509,0,'NO'),(1452,242,'network_address','127.0.2.61',1224778509,0,'NO'),(1453,243,'state','Alive',1224778509,0,'NO'),(1454,243,'core ','243',1224778509,0,'NO'),(1455,243,'cpu ','122',1224778509,0,'NO'),(1456,243,'host ','node-61',1224778509,0,'NO'),(1457,243,'cpuset','2',1224778509,0,'NO'),(1458,243,'network_address','127.0.2.61',1224778509,0,'NO'),(1459,244,'state','Alive',1224778509,0,'NO'),(1460,244,'core ','244',1224778509,0,'NO'),(1461,244,'cpu ','122',1224778509,0,'NO'),(1462,244,'host ','node-61',1224778509,0,'NO'),(1463,244,'cpuset','3',1224778509,0,'NO'),(1464,244,'network_address','127.0.2.61',1224778509,0,'NO'),(1465,245,'state','Alive',1224778509,0,'NO'),(1466,245,'core ','245',1224778509,0,'NO'),(1467,245,'cpu ','123',1224778509,0,'NO'),(1468,245,'host ','node-62',1224778509,0,'NO'),(1469,245,'cpuset','0',1224778509,0,'NO'),(1470,245,'network_address','127.0.2.62',1224778509,0,'NO'),(1471,246,'state','Alive',1224778510,0,'NO'),(1472,246,'core ','246',1224778510,0,'NO'),(1473,246,'cpu ','123',1224778510,0,'NO'),(1474,246,'host ','node-62',1224778510,0,'NO'),(1475,246,'cpuset','1',1224778510,0,'NO'),(1476,246,'network_address','127.0.2.62',1224778510,0,'NO'),(1477,247,'state','Alive',1224778510,0,'NO'),(1478,247,'core ','247',1224778510,0,'NO'),(1479,247,'cpu ','124',1224778510,0,'NO'),(1480,247,'host ','node-62',1224778510,0,'NO'),(1481,247,'cpuset','2',1224778510,0,'NO'),(1482,247,'network_address','127.0.2.62',1224778510,0,'NO'),(1483,248,'state','Alive',1224778510,0,'NO'),(1484,248,'core ','248',1224778510,0,'NO'),(1485,248,'cpu ','124',1224778510,0,'NO'),(1486,248,'host ','node-62',1224778510,0,'NO'),(1487,248,'cpuset','3',1224778510,0,'NO'),(1488,248,'network_address','127.0.2.62',1224778510,0,'NO'),(1489,249,'state','Alive',1224778510,0,'NO'),(1490,249,'core ','249',1224778510,0,'NO'),(1491,249,'cpu ','125',1224778510,0,'NO'),(1492,249,'host ','node-63',1224778510,0,'NO'),(1493,249,'cpuset','0',1224778510,0,'NO'),(1494,249,'network_address','127.0.2.63',1224778510,0,'NO'),(1495,250,'state','Alive',1224778510,0,'NO'),(1496,250,'core ','250',1224778510,0,'NO'),(1497,250,'cpu ','125',1224778510,0,'NO'),(1498,250,'host ','node-63',1224778510,0,'NO'),(1499,250,'cpuset','1',1224778510,0,'NO'),(1500,250,'network_address','127.0.2.63',1224778510,0,'NO'),(1501,251,'state','Alive',1224778511,0,'NO'),(1502,251,'core ','251',1224778511,0,'NO'),(1503,251,'cpu ','126',1224778511,0,'NO'),(1504,251,'host ','node-63',1224778511,0,'NO'),(1505,251,'cpuset','2',1224778511,0,'NO'),(1506,251,'network_address','127.0.2.63',1224778511,0,'NO'),(1507,252,'state','Alive',1224778511,0,'NO'),(1508,252,'core ','252',1224778511,0,'NO'),(1509,252,'cpu ','126',1224778511,0,'NO'),(1510,252,'host ','node-63',1224778511,0,'NO'),(1511,252,'cpuset','3',1224778511,0,'NO'),(1512,252,'network_address','127.0.2.63',1224778511,0,'NO'),(1513,253,'state','Alive',1224778511,0,'NO'),(1514,253,'core ','253',1224778511,0,'NO'),(1515,253,'cpu ','127',1224778511,0,'NO'),(1516,253,'host ','node-64',1224778511,0,'NO'),(1517,253,'cpuset','0',1224778511,0,'NO'),(1518,253,'network_address','127.0.2.64',1224778511,0,'NO'),(1519,254,'state','Alive',1224778511,0,'NO'),(1520,254,'core ','254',1224778511,0,'NO'),(1521,254,'cpu ','127',1224778511,0,'NO'),(1522,254,'host ','node-64',1224778511,0,'NO'),(1523,254,'cpuset','1',1224778511,0,'NO'),(1524,254,'network_address','127.0.2.64',1224778511,0,'NO'),(1525,255,'state','Alive',1224778511,0,'NO'),(1526,255,'core ','255',1224778511,0,'NO'),(1527,255,'cpu ','128',1224778511,0,'NO'),(1528,255,'host ','node-64',1224778511,0,'NO'),(1529,255,'cpuset','2',1224778511,0,'NO'),(1530,255,'network_address','127.0.2.64',1224778511,0,'NO'),(1531,256,'state','Alive',1224778512,0,'NO'),(1532,256,'core ','256',1224778512,0,'NO'),(1533,256,'cpu ','128',1224778512,0,'NO'),(1534,256,'host ','node-64',1224778512,0,'NO'),(1535,256,'cpuset','3',1224778512,0,'NO'),(1536,256,'network_address','127.0.2.64',1224778512,0,'NO'),(1537,257,'state','Alive',1224778512,0,'NO'),(1538,257,'core ','257',1224778512,0,'NO'),(1539,257,'cpu ','129',1224778512,0,'NO'),(1540,257,'host ','node-65',1224778512,0,'NO'),(1541,257,'cpuset','0',1224778512,0,'NO'),(1542,257,'network_address','127.0.2.65',1224778512,0,'NO'),(1543,258,'state','Alive',1224778512,0,'NO'),(1544,258,'core ','258',1224778512,0,'NO'),(1545,258,'cpu ','129',1224778512,0,'NO'),(1546,258,'host ','node-65',1224778512,0,'NO'),(1547,258,'cpuset','1',1224778512,0,'NO'),(1548,258,'network_address','127.0.2.65',1224778512,0,'NO'),(1549,259,'state','Alive',1224778512,0,'NO'),(1550,259,'core ','259',1224778512,0,'NO'),(1551,259,'cpu ','130',1224778512,0,'NO'),(1552,259,'host ','node-65',1224778512,0,'NO'),(1553,259,'cpuset','2',1224778512,0,'NO'),(1554,259,'network_address','127.0.2.65',1224778512,0,'NO'),(1555,260,'state','Alive',1224778512,0,'NO'),(1556,260,'core ','260',1224778512,0,'NO'),(1557,260,'cpu ','130',1224778512,0,'NO'),(1558,260,'host ','node-65',1224778512,0,'NO'),(1559,260,'cpuset','3',1224778512,0,'NO'),(1560,260,'network_address','127.0.2.65',1224778512,0,'NO'),(1561,261,'state','Alive',1224778512,0,'NO'),(1562,261,'core ','261',1224778512,0,'NO'),(1563,261,'cpu ','131',1224778512,0,'NO'),(1564,261,'host ','node-66',1224778512,0,'NO'),(1565,261,'cpuset','0',1224778512,0,'NO'),(1566,261,'network_address','127.0.2.66',1224778512,0,'NO'),(1567,262,'state','Alive',1224778513,0,'NO'),(1568,262,'core ','262',1224778513,0,'NO'),(1569,262,'cpu ','131',1224778513,0,'NO'),(1570,262,'host ','node-66',1224778513,0,'NO'),(1571,262,'cpuset','1',1224778513,0,'NO'),(1572,262,'network_address','127.0.2.66',1224778513,0,'NO'),(1573,263,'state','Alive',1224778513,0,'NO'),(1574,263,'core ','263',1224778513,0,'NO'),(1575,263,'cpu ','132',1224778513,0,'NO'),(1576,263,'host ','node-66',1224778513,0,'NO'),(1577,263,'cpuset','2',1224778513,0,'NO'),(1578,263,'network_address','127.0.2.66',1224778513,0,'NO'),(1579,264,'state','Alive',1224778513,0,'NO'),(1580,264,'core ','264',1224778513,0,'NO'),(1581,264,'cpu ','132',1224778513,0,'NO'),(1582,264,'host ','node-66',1224778513,0,'NO'),(1583,264,'cpuset','3',1224778513,0,'NO'),(1584,264,'network_address','127.0.2.66',1224778513,0,'NO'),(1585,265,'state','Alive',1224778513,0,'NO'),(1586,265,'core ','265',1224778513,0,'NO'),(1587,265,'cpu ','133',1224778513,0,'NO'),(1588,265,'host ','node-67',1224778513,0,'NO'),(1589,265,'cpuset','0',1224778513,0,'NO'),(1590,265,'network_address','127.0.2.67',1224778513,0,'NO'),(1591,266,'state','Alive',1224778513,0,'NO'),(1592,266,'core ','266',1224778513,0,'NO'),(1593,266,'cpu ','133',1224778513,0,'NO'),(1594,266,'host ','node-67',1224778513,0,'NO'),(1595,266,'cpuset','1',1224778513,0,'NO'),(1596,266,'network_address','127.0.2.67',1224778513,0,'NO'),(1597,267,'state','Alive',1224778514,0,'NO'),(1598,267,'core ','267',1224778514,0,'NO'),(1599,267,'cpu ','134',1224778514,0,'NO'),(1600,267,'host ','node-67',1224778514,0,'NO'),(1601,267,'cpuset','2',1224778514,0,'NO'),(1602,267,'network_address','127.0.2.67',1224778514,0,'NO'),(1603,268,'state','Alive',1224778514,0,'NO'),(1604,268,'core ','268',1224778514,0,'NO'),(1605,268,'cpu ','134',1224778514,0,'NO'),(1606,268,'host ','node-67',1224778514,0,'NO'),(1607,268,'cpuset','3',1224778514,0,'NO'),(1608,268,'network_address','127.0.2.67',1224778514,0,'NO'),(1609,269,'state','Alive',1224778514,0,'NO'),(1610,269,'core ','269',1224778514,0,'NO'),(1611,269,'cpu ','135',1224778514,0,'NO'),(1612,269,'host ','node-68',1224778514,0,'NO'),(1613,269,'cpuset','0',1224778514,0,'NO'),(1614,269,'network_address','127.0.2.68',1224778514,0,'NO'),(1615,270,'state','Alive',1224778514,0,'NO'),(1616,270,'core ','270',1224778514,0,'NO'),(1617,270,'cpu ','135',1224778514,0,'NO'),(1618,270,'host ','node-68',1224778514,0,'NO'),(1619,270,'cpuset','1',1224778514,0,'NO'),(1620,270,'network_address','127.0.2.68',1224778514,0,'NO'),(1621,271,'state','Alive',1224778514,0,'NO'),(1622,271,'core ','271',1224778514,0,'NO'),(1623,271,'cpu ','136',1224778514,0,'NO'),(1624,271,'host ','node-68',1224778514,0,'NO'),(1625,271,'cpuset','2',1224778514,0,'NO'),(1626,271,'network_address','127.0.2.68',1224778514,0,'NO'),(1627,272,'state','Alive',1224778515,0,'NO'),(1628,272,'core ','272',1224778515,0,'NO'),(1629,272,'cpu ','136',1224778515,0,'NO'),(1630,272,'host ','node-68',1224778515,0,'NO'),(1631,272,'cpuset','3',1224778515,0,'NO'),(1632,272,'network_address','127.0.2.68',1224778515,0,'NO'),(1633,273,'state','Alive',1224778515,0,'NO'),(1634,273,'core ','273',1224778515,0,'NO'),(1635,273,'cpu ','137',1224778515,0,'NO'),(1636,273,'host ','node-69',1224778515,0,'NO'),(1637,273,'cpuset','0',1224778515,0,'NO'),(1638,273,'network_address','127.0.2.69',1224778515,0,'NO'),(1639,274,'state','Alive',1224778515,0,'NO'),(1640,274,'core ','274',1224778515,0,'NO'),(1641,274,'cpu ','137',1224778515,0,'NO'),(1642,274,'host ','node-69',1224778515,0,'NO'),(1643,274,'cpuset','1',1224778515,0,'NO'),(1644,274,'network_address','127.0.2.69',1224778515,0,'NO'),(1645,275,'state','Alive',1224778515,0,'NO'),(1646,275,'core ','275',1224778515,0,'NO'),(1647,275,'cpu ','138',1224778515,0,'NO'),(1648,275,'host ','node-69',1224778515,0,'NO'),(1649,275,'cpuset','2',1224778515,0,'NO'),(1650,275,'network_address','127.0.2.69',1224778515,0,'NO'),(1651,276,'state','Alive',1224778515,0,'NO'),(1652,276,'core ','276',1224778515,0,'NO'),(1653,276,'cpu ','138',1224778515,0,'NO'),(1654,276,'host ','node-69',1224778515,0,'NO'),(1655,276,'cpuset','3',1224778515,0,'NO'),(1656,276,'network_address','127.0.2.69',1224778515,0,'NO'),(1657,277,'state','Alive',1224778516,0,'NO'),(1658,277,'core ','277',1224778516,0,'NO'),(1659,277,'cpu ','139',1224778516,0,'NO'),(1660,277,'host ','node-70',1224778516,0,'NO'),(1661,277,'cpuset','0',1224778516,0,'NO'),(1662,277,'network_address','127.0.2.70',1224778516,0,'NO'),(1663,278,'state','Alive',1224778516,0,'NO'),(1664,278,'core ','278',1224778516,0,'NO'),(1665,278,'cpu ','139',1224778516,0,'NO'),(1666,278,'host ','node-70',1224778516,0,'NO'),(1667,278,'cpuset','1',1224778516,0,'NO'),(1668,278,'network_address','127.0.2.70',1224778516,0,'NO'),(1669,279,'state','Alive',1224778516,0,'NO'),(1670,279,'core ','279',1224778516,0,'NO'),(1671,279,'cpu ','140',1224778516,0,'NO'),(1672,279,'host ','node-70',1224778516,0,'NO'),(1673,279,'cpuset','2',1224778516,0,'NO'),(1674,279,'network_address','127.0.2.70',1224778516,0,'NO'),(1675,280,'state','Alive',1224778516,0,'NO'),(1676,280,'core ','280',1224778516,0,'NO'),(1677,280,'cpu ','140',1224778516,0,'NO'),(1678,280,'host ','node-70',1224778516,0,'NO'),(1679,280,'cpuset','3',1224778516,0,'NO'),(1680,280,'network_address','127.0.2.70',1224778516,0,'NO'),(1681,281,'state','Alive',1224778516,0,'NO'),(1682,281,'core ','281',1224778516,0,'NO'),(1683,281,'cpu ','141',1224778516,0,'NO'),(1684,281,'host ','node-71',1224778516,0,'NO'),(1685,281,'cpuset','0',1224778516,0,'NO'),(1686,281,'network_address','127.0.2.71',1224778516,0,'NO'),(1687,282,'state','Alive',1224778516,0,'NO'),(1688,282,'core ','282',1224778516,0,'NO'),(1689,282,'cpu ','141',1224778516,0,'NO'),(1690,282,'host ','node-71',1224778516,0,'NO'),(1691,282,'cpuset','1',1224778516,0,'NO'),(1692,282,'network_address','127.0.2.71',1224778516,0,'NO'),(1693,283,'state','Alive',1224778517,0,'NO'),(1694,283,'core ','283',1224778517,0,'NO'),(1695,283,'cpu ','142',1224778517,0,'NO'),(1696,283,'host ','node-71',1224778517,0,'NO'),(1697,283,'cpuset','2',1224778517,0,'NO'),(1698,283,'network_address','127.0.2.71',1224778517,0,'NO'),(1699,284,'state','Alive',1224778517,0,'NO'),(1700,284,'core ','284',1224778517,0,'NO'),(1701,284,'cpu ','142',1224778517,0,'NO'),(1702,284,'host ','node-71',1224778517,0,'NO'),(1703,284,'cpuset','3',1224778517,0,'NO'),(1704,284,'network_address','127.0.2.71',1224778517,0,'NO'),(1705,285,'state','Alive',1224778517,0,'NO'),(1706,285,'core ','285',1224778517,0,'NO'),(1707,285,'cpu ','143',1224778517,0,'NO'),(1708,285,'host ','node-72',1224778517,0,'NO'),(1709,285,'cpuset','0',1224778517,0,'NO'),(1710,285,'network_address','127.0.2.72',1224778517,0,'NO'),(1711,286,'state','Alive',1224778517,0,'NO'),(1712,286,'core ','286',1224778517,0,'NO'),(1713,286,'cpu ','143',1224778517,0,'NO'),(1714,286,'host ','node-72',1224778517,0,'NO'),(1715,286,'cpuset','1',1224778517,0,'NO'),(1716,286,'network_address','127.0.2.72',1224778517,0,'NO'),(1717,287,'state','Alive',1224778517,0,'NO'),(1718,287,'core ','287',1224778517,0,'NO'),(1719,287,'cpu ','144',1224778517,0,'NO'),(1720,287,'host ','node-72',1224778517,0,'NO'),(1721,287,'cpuset','2',1224778517,0,'NO'),(1722,287,'network_address','127.0.2.72',1224778517,0,'NO'),(1723,288,'state','Alive',1224778518,0,'NO'),(1724,288,'core ','288',1224778518,0,'NO'),(1725,288,'cpu ','144',1224778518,0,'NO'),(1726,288,'host ','node-72',1224778518,0,'NO'),(1727,288,'cpuset','3',1224778518,0,'NO'),(1728,288,'network_address','127.0.2.72',1224778518,0,'NO'),(1729,289,'state','Alive',1224778518,0,'NO'),(1730,289,'core ','289',1224778518,0,'NO'),(1731,289,'cpu ','145',1224778518,0,'NO'),(1732,289,'host ','node-73',1224778518,0,'NO'),(1733,289,'cpuset','0',1224778518,0,'NO'),(1734,289,'network_address','127.0.2.73',1224778518,0,'NO'),(1735,290,'state','Alive',1224778518,0,'NO'),(1736,290,'core ','290',1224778518,0,'NO'),(1737,290,'cpu ','145',1224778518,0,'NO'),(1738,290,'host ','node-73',1224778518,0,'NO'),(1739,290,'cpuset','1',1224778518,0,'NO'),(1740,290,'network_address','127.0.2.73',1224778518,0,'NO'),(1741,291,'state','Alive',1224778518,0,'NO'),(1742,291,'core ','291',1224778518,0,'NO'),(1743,291,'cpu ','146',1224778518,0,'NO'),(1744,291,'host ','node-73',1224778518,0,'NO'),(1745,291,'cpuset','2',1224778518,0,'NO'),(1746,291,'network_address','127.0.2.73',1224778518,0,'NO'),(1747,292,'state','Alive',1224778518,0,'NO'),(1748,292,'core ','292',1224778518,0,'NO'),(1749,292,'cpu ','146',1224778518,0,'NO'),(1750,292,'host ','node-73',1224778518,0,'NO'),(1751,292,'cpuset','3',1224778518,0,'NO'),(1752,292,'network_address','127.0.2.73',1224778518,0,'NO'),(1753,293,'state','Alive',1224778519,0,'NO'),(1754,293,'core ','293',1224778519,0,'NO'),(1755,293,'cpu ','147',1224778519,0,'NO'),(1756,293,'host ','node-74',1224778519,0,'NO'),(1757,293,'cpuset','0',1224778519,0,'NO'),(1758,293,'network_address','127.0.2.74',1224778519,0,'NO'),(1759,294,'state','Alive',1224778519,0,'NO'),(1760,294,'core ','294',1224778519,0,'NO'),(1761,294,'cpu ','147',1224778519,0,'NO'),(1762,294,'host ','node-74',1224778519,0,'NO'),(1763,294,'cpuset','1',1224778519,0,'NO'),(1764,294,'network_address','127.0.2.74',1224778519,0,'NO'),(1765,295,'state','Alive',1224778519,0,'NO'),(1766,295,'core ','295',1224778519,0,'NO'),(1767,295,'cpu ','148',1224778519,0,'NO'),(1768,295,'host ','node-74',1224778519,0,'NO'),(1769,295,'cpuset','2',1224778519,0,'NO'),(1770,295,'network_address','127.0.2.74',1224778519,0,'NO'),(1771,296,'state','Alive',1224778519,0,'NO'),(1772,296,'core ','296',1224778519,0,'NO'),(1773,296,'cpu ','148',1224778519,0,'NO'),(1774,296,'host ','node-74',1224778519,0,'NO'),(1775,296,'cpuset','3',1224778519,0,'NO'),(1776,296,'network_address','127.0.2.74',1224778519,0,'NO'),(1777,297,'state','Alive',1224778519,0,'NO'),(1778,297,'core ','297',1224778519,0,'NO'),(1779,297,'cpu ','149',1224778519,0,'NO'),(1780,297,'host ','node-75',1224778519,0,'NO'),(1781,297,'cpuset','0',1224778519,0,'NO'),(1782,297,'network_address','127.0.2.75',1224778519,0,'NO'),(1783,298,'state','Alive',1224778520,0,'NO'),(1784,298,'core ','298',1224778520,0,'NO'),(1785,298,'cpu ','149',1224778520,0,'NO'),(1786,298,'host ','node-75',1224778520,0,'NO'),(1787,298,'cpuset','1',1224778520,0,'NO'),(1788,298,'network_address','127.0.2.75',1224778520,0,'NO'),(1789,299,'state','Alive',1224778520,0,'NO'),(1790,299,'core ','299',1224778520,0,'NO'),(1791,299,'cpu ','150',1224778520,0,'NO'),(1792,299,'host ','node-75',1224778520,0,'NO'),(1793,299,'cpuset','2',1224778520,0,'NO'),(1794,299,'network_address','127.0.2.75',1224778520,0,'NO'),(1795,300,'state','Alive',1224778520,0,'NO'),(1796,300,'core ','300',1224778520,0,'NO'),(1797,300,'cpu ','150',1224778520,0,'NO'),(1798,300,'host ','node-75',1224778520,0,'NO'),(1799,300,'cpuset','3',1224778520,0,'NO'),(1800,300,'network_address','127.0.2.75',1224778520,0,'NO'),(1801,301,'state','Alive',1224778520,0,'NO'),(1802,301,'core ','301',1224778520,0,'NO'),(1803,301,'cpu ','151',1224778520,0,'NO'),(1804,301,'host ','node-76',1224778520,0,'NO'),(1805,301,'cpuset','0',1224778520,0,'NO'),(1806,301,'network_address','127.0.2.76',1224778520,0,'NO'),(1807,302,'state','Alive',1224778520,0,'NO'),(1808,302,'core ','302',1224778520,0,'NO'),(1809,302,'cpu ','151',1224778520,0,'NO'),(1810,302,'host ','node-76',1224778520,0,'NO'),(1811,302,'cpuset','1',1224778520,0,'NO'),(1812,302,'network_address','127.0.2.76',1224778520,0,'NO'),(1813,303,'state','Alive',1224778521,0,'NO'),(1814,303,'core ','303',1224778521,0,'NO'),(1815,303,'cpu ','152',1224778521,0,'NO'),(1816,303,'host ','node-76',1224778521,0,'NO'),(1817,303,'cpuset','2',1224778521,0,'NO'),(1818,303,'network_address','127.0.2.76',1224778521,0,'NO'),(1819,304,'state','Alive',1224778521,0,'NO'),(1820,304,'core ','304',1224778521,0,'NO'),(1821,304,'cpu ','152',1224778521,0,'NO'),(1822,304,'host ','node-76',1224778521,0,'NO'),(1823,304,'cpuset','3',1224778521,0,'NO'),(1824,304,'network_address','127.0.2.76',1224778521,0,'NO'),(1825,305,'state','Alive',1224778521,0,'NO'),(1826,305,'core ','305',1224778521,0,'NO'),(1827,305,'cpu ','153',1224778521,0,'NO'),(1828,305,'host ','node-77',1224778521,0,'NO'),(1829,305,'cpuset','0',1224778521,0,'NO'),(1830,305,'network_address','127.0.2.77',1224778521,0,'NO'),(1831,306,'state','Alive',1224778521,0,'NO'),(1832,306,'core ','306',1224778521,0,'NO'),(1833,306,'cpu ','153',1224778521,0,'NO'),(1834,306,'host ','node-77',1224778521,0,'NO'),(1835,306,'cpuset','1',1224778521,0,'NO'),(1836,306,'network_address','127.0.2.77',1224778521,0,'NO'),(1837,307,'state','Alive',1224778521,0,'NO'),(1838,307,'core ','307',1224778521,0,'NO'),(1839,307,'cpu ','154',1224778521,0,'NO'),(1840,307,'host ','node-77',1224778521,0,'NO'),(1841,307,'cpuset','2',1224778521,0,'NO'),(1842,307,'network_address','127.0.2.77',1224778521,0,'NO'),(1843,308,'state','Alive',1224778521,0,'NO'),(1844,308,'core ','308',1224778521,0,'NO'),(1845,308,'cpu ','154',1224778521,0,'NO'),(1846,308,'host ','node-77',1224778521,0,'NO'),(1847,308,'cpuset','3',1224778521,0,'NO'),(1848,308,'network_address','127.0.2.77',1224778521,0,'NO'),(1849,309,'state','Alive',1224778522,0,'NO'),(1850,309,'core ','309',1224778522,0,'NO'),(1851,309,'cpu ','155',1224778522,0,'NO'),(1852,309,'host ','node-78',1224778522,0,'NO'),(1853,309,'cpuset','0',1224778522,0,'NO'),(1854,309,'network_address','127.0.2.78',1224778522,0,'NO'),(1855,310,'state','Alive',1224778522,0,'NO'),(1856,310,'core ','310',1224778522,0,'NO'),(1857,310,'cpu ','155',1224778522,0,'NO'),(1858,310,'host ','node-78',1224778522,0,'NO'),(1859,310,'cpuset','1',1224778522,0,'NO'),(1860,310,'network_address','127.0.2.78',1224778522,0,'NO'),(1861,311,'state','Alive',1224778522,0,'NO'),(1862,311,'core ','311',1224778522,0,'NO'),(1863,311,'cpu ','156',1224778522,0,'NO'),(1864,311,'host ','node-78',1224778522,0,'NO'),(1865,311,'cpuset','2',1224778522,0,'NO'),(1866,311,'network_address','127.0.2.78',1224778522,0,'NO'),(1867,312,'state','Alive',1224778522,0,'NO'),(1868,312,'core ','312',1224778522,0,'NO'),(1869,312,'cpu ','156',1224778522,0,'NO'),(1870,312,'host ','node-78',1224778522,0,'NO'),(1871,312,'cpuset','3',1224778522,0,'NO'),(1872,312,'network_address','127.0.2.78',1224778522,0,'NO'),(1873,313,'state','Alive',1224778522,0,'NO'),(1874,313,'core ','313',1224778522,0,'NO'),(1875,313,'cpu ','157',1224778522,0,'NO'),(1876,313,'host ','node-79',1224778522,0,'NO'),(1877,313,'cpuset','0',1224778522,0,'NO'),(1878,313,'network_address','127.0.2.79',1224778522,0,'NO'),(1879,314,'state','Alive',1224778523,0,'NO'),(1880,314,'core ','314',1224778523,0,'NO'),(1881,314,'cpu ','157',1224778523,0,'NO'),(1882,314,'host ','node-79',1224778523,0,'NO'),(1883,314,'cpuset','1',1224778523,0,'NO'),(1884,314,'network_address','127.0.2.79',1224778523,0,'NO'),(1885,315,'state','Alive',1224778523,0,'NO'),(1886,315,'core ','315',1224778523,0,'NO'),(1887,315,'cpu ','158',1224778523,0,'NO'),(1888,315,'host ','node-79',1224778523,0,'NO'),(1889,315,'cpuset','2',1224778523,0,'NO'),(1890,315,'network_address','127.0.2.79',1224778523,0,'NO'),(1891,316,'state','Alive',1224778523,0,'NO'),(1892,316,'core ','316',1224778523,0,'NO'),(1893,316,'cpu ','158',1224778523,0,'NO'),(1894,316,'host ','node-79',1224778523,0,'NO'),(1895,316,'cpuset','3',1224778523,0,'NO'),(1896,316,'network_address','127.0.2.79',1224778523,0,'NO'),(1897,317,'state','Alive',1224778523,0,'NO'),(1898,317,'core ','317',1224778523,0,'NO'),(1899,317,'cpu ','159',1224778523,0,'NO'),(1900,317,'host ','node-80',1224778523,0,'NO'),(1901,317,'cpuset','0',1224778523,0,'NO'),(1902,317,'network_address','127.0.2.80',1224778523,0,'NO'),(1903,318,'state','Alive',1224778524,0,'NO'),(1904,318,'core ','318',1224778524,0,'NO'),(1905,318,'cpu ','159',1224778524,0,'NO'),(1906,318,'host ','node-80',1224778524,0,'NO'),(1907,318,'cpuset','1',1224778524,0,'NO'),(1908,318,'network_address','127.0.2.80',1224778524,0,'NO'),(1909,319,'state','Alive',1224778524,0,'NO'),(1910,319,'core ','319',1224778524,0,'NO'),(1911,319,'cpu ','160',1224778524,0,'NO'),(1912,319,'host ','node-80',1224778524,0,'NO'),(1913,319,'cpuset','2',1224778524,0,'NO'),(1914,319,'network_address','127.0.2.80',1224778524,0,'NO'),(1915,320,'state','Alive',1224778524,0,'NO'),(1916,320,'core ','320',1224778524,0,'NO'),(1917,320,'cpu ','160',1224778524,0,'NO'),(1918,320,'host ','node-80',1224778524,0,'NO'),(1919,320,'cpuset','3',1224778524,0,'NO'),(1920,320,'network_address','127.0.2.80',1224778524,0,'NO'),(1921,321,'state','Alive',1224778524,0,'NO'),(1922,321,'core ','321',1224778524,0,'NO'),(1923,321,'cpu ','161',1224778524,0,'NO'),(1924,321,'host ','node-81',1224778524,0,'NO'),(1925,321,'cpuset','0',1224778524,0,'NO'),(1926,321,'network_address','127.0.2.81',1224778524,0,'NO'),(1927,322,'state','Alive',1224778525,0,'NO'),(1928,322,'core ','322',1224778525,0,'NO'),(1929,322,'cpu ','161',1224778525,0,'NO'),(1930,322,'host ','node-81',1224778525,0,'NO'),(1931,322,'cpuset','1',1224778525,0,'NO'),(1932,322,'network_address','127.0.2.81',1224778525,0,'NO'),(1933,323,'state','Alive',1224778525,0,'NO'),(1934,323,'core ','323',1224778525,0,'NO'),(1935,323,'cpu ','162',1224778525,0,'NO'),(1936,323,'host ','node-81',1224778525,0,'NO'),(1937,323,'cpuset','2',1224778525,0,'NO'),(1938,323,'network_address','127.0.2.81',1224778525,0,'NO'),(1939,324,'state','Alive',1224778525,0,'NO'),(1940,324,'core ','324',1224778525,0,'NO'),(1941,324,'cpu ','162',1224778525,0,'NO'),(1942,324,'host ','node-81',1224778525,0,'NO'),(1943,324,'cpuset','3',1224778525,0,'NO'),(1944,324,'network_address','127.0.2.81',1224778525,0,'NO'),(1945,325,'state','Alive',1224778525,0,'NO'),(1946,325,'core ','325',1224778525,0,'NO'),(1947,325,'cpu ','163',1224778525,0,'NO'),(1948,325,'host ','node-82',1224778525,0,'NO'),(1949,325,'cpuset','0',1224778525,0,'NO'),(1950,325,'network_address','127.0.2.82',1224778525,0,'NO'),(1951,326,'state','Alive',1224778525,0,'NO'),(1952,326,'core ','326',1224778525,0,'NO'),(1953,326,'cpu ','163',1224778525,0,'NO'),(1954,326,'host ','node-82',1224778525,0,'NO'),(1955,326,'cpuset','1',1224778525,0,'NO'),(1956,326,'network_address','127.0.2.82',1224778525,0,'NO'),(1957,327,'state','Alive',1224778525,0,'NO'),(1958,327,'core ','327',1224778525,0,'NO'),(1959,327,'cpu ','164',1224778525,0,'NO'),(1960,327,'host ','node-82',1224778525,0,'NO'),(1961,327,'cpuset','2',1224778526,0,'NO'),(1962,327,'network_address','127.0.2.82',1224778526,0,'NO'),(1963,328,'state','Alive',1224778526,0,'NO'),(1964,328,'core ','328',1224778526,0,'NO'),(1965,328,'cpu ','164',1224778526,0,'NO'),(1966,328,'host ','node-82',1224778526,0,'NO'),(1967,328,'cpuset','3',1224778526,0,'NO'),(1968,328,'network_address','127.0.2.82',1224778526,0,'NO'),(1969,329,'state','Alive',1224778526,0,'NO'),(1970,329,'core ','329',1224778526,0,'NO'),(1971,329,'cpu ','165',1224778526,0,'NO'),(1972,329,'host ','node-83',1224778526,0,'NO'),(1973,329,'cpuset','0',1224778526,0,'NO'),(1974,329,'network_address','127.0.2.83',1224778526,0,'NO'),(1975,330,'state','Alive',1224778526,0,'NO'),(1976,330,'core ','330',1224778526,0,'NO'),(1977,330,'cpu ','165',1224778526,0,'NO'),(1978,330,'host ','node-83',1224778526,0,'NO'),(1979,330,'cpuset','1',1224778526,0,'NO'),(1980,330,'network_address','127.0.2.83',1224778526,0,'NO'),(1981,331,'state','Alive',1224778526,0,'NO'),(1982,331,'core ','331',1224778526,0,'NO'),(1983,331,'cpu ','166',1224778526,0,'NO'),(1984,331,'host ','node-83',1224778526,0,'NO'),(1985,331,'cpuset','2',1224778526,0,'NO'),(1986,331,'network_address','127.0.2.83',1224778526,0,'NO'),(1987,332,'state','Alive',1224778526,0,'NO'),(1988,332,'core ','332',1224778526,0,'NO'),(1989,332,'cpu ','166',1224778526,0,'NO'),(1990,332,'host ','node-83',1224778526,0,'NO'),(1991,332,'cpuset','3',1224778526,0,'NO'),(1992,332,'network_address','127.0.2.83',1224778526,0,'NO'),(1993,333,'state','Alive',1224778527,0,'NO'),(1994,333,'core ','333',1224778527,0,'NO'),(1995,333,'cpu ','167',1224778527,0,'NO'),(1996,333,'host ','node-84',1224778527,0,'NO'),(1997,333,'cpuset','0',1224778527,0,'NO'),(1998,333,'network_address','127.0.2.84',1224778527,0,'NO'),(1999,334,'state','Alive',1224778527,0,'NO'),(2000,334,'core ','334',1224778527,0,'NO'),(2001,334,'cpu ','167',1224778527,0,'NO'),(2002,334,'host ','node-84',1224778527,0,'NO'),(2003,334,'cpuset','1',1224778527,0,'NO'),(2004,334,'network_address','127.0.2.84',1224778527,0,'NO'),(2005,335,'state','Alive',1224778527,0,'NO'),(2006,335,'core ','335',1224778527,0,'NO'),(2007,335,'cpu ','168',1224778527,0,'NO'),(2008,335,'host ','node-84',1224778527,0,'NO'),(2009,335,'cpuset','2',1224778527,0,'NO'),(2010,335,'network_address','127.0.2.84',1224778527,0,'NO'),(2011,336,'state','Alive',1224778527,0,'NO'),(2012,336,'core ','336',1224778527,0,'NO'),(2013,336,'cpu ','168',1224778527,0,'NO'),(2014,336,'host ','node-84',1224778527,0,'NO'),(2015,336,'cpuset','3',1224778527,0,'NO'),(2016,336,'network_address','127.0.2.84',1224778527,0,'NO'),(2017,337,'state','Alive',1224778527,0,'NO'),(2018,337,'core ','337',1224778527,0,'NO'),(2019,337,'cpu ','169',1224778527,0,'NO'),(2020,337,'host ','node-85',1224778527,0,'NO'),(2021,337,'cpuset','0',1224778527,0,'NO'),(2022,337,'network_address','127.0.2.85',1224778527,0,'NO'),(2023,338,'state','Alive',1224778528,0,'NO'),(2024,338,'core ','338',1224778528,0,'NO'),(2025,338,'cpu ','169',1224778528,0,'NO'),(2026,338,'host ','node-85',1224778528,0,'NO'),(2027,338,'cpuset','1',1224778528,0,'NO'),(2028,338,'network_address','127.0.2.85',1224778528,0,'NO'),(2029,339,'state','Alive',1224778528,0,'NO'),(2030,339,'core ','339',1224778528,0,'NO'),(2031,339,'cpu ','170',1224778528,0,'NO'),(2032,339,'host ','node-85',1224778528,0,'NO'),(2033,339,'cpuset','2',1224778528,0,'NO'),(2034,339,'network_address','127.0.2.85',1224778528,0,'NO'),(2035,340,'state','Alive',1224778528,0,'NO'),(2036,340,'core ','340',1224778528,0,'NO'),(2037,340,'cpu ','170',1224778528,0,'NO'),(2038,340,'host ','node-85',1224778528,0,'NO'),(2039,340,'cpuset','3',1224778528,0,'NO'),(2040,340,'network_address','127.0.2.85',1224778528,0,'NO'),(2041,341,'state','Alive',1224778528,0,'NO'),(2042,341,'core ','341',1224778528,0,'NO'),(2043,341,'cpu ','171',1224778528,0,'NO'),(2044,341,'host ','node-86',1224778528,0,'NO'),(2045,341,'cpuset','0',1224778528,0,'NO'),(2046,341,'network_address','127.0.2.86',1224778528,0,'NO'),(2047,342,'state','Alive',1224778528,0,'NO'),(2048,342,'core ','342',1224778528,0,'NO'),(2049,342,'cpu ','171',1224778528,0,'NO'),(2050,342,'host ','node-86',1224778528,0,'NO'),(2051,342,'cpuset','1',1224778528,0,'NO'),(2052,342,'network_address','127.0.2.86',1224778528,0,'NO'),(2053,343,'state','Alive',1224778529,0,'NO'),(2054,343,'core ','343',1224778529,0,'NO'),(2055,343,'cpu ','172',1224778529,0,'NO'),(2056,343,'host ','node-86',1224778529,0,'NO'),(2057,343,'cpuset','2',1224778529,0,'NO'),(2058,343,'network_address','127.0.2.86',1224778529,0,'NO'),(2059,344,'state','Alive',1224778529,0,'NO'),(2060,344,'core ','344',1224778529,0,'NO'),(2061,344,'cpu ','172',1224778529,0,'NO'),(2062,344,'host ','node-86',1224778529,0,'NO'),(2063,344,'cpuset','3',1224778529,0,'NO'),(2064,344,'network_address','127.0.2.86',1224778529,0,'NO'),(2065,345,'state','Alive',1224778529,0,'NO'),(2066,345,'core ','345',1224778529,0,'NO'),(2067,345,'cpu ','173',1224778529,0,'NO'),(2068,345,'host ','node-87',1224778529,0,'NO'),(2069,345,'cpuset','0',1224778529,0,'NO'),(2070,345,'network_address','127.0.2.87',1224778529,0,'NO'),(2071,346,'state','Alive',1224778529,0,'NO'),(2072,346,'core ','346',1224778529,0,'NO'),(2073,346,'cpu ','173',1224778529,0,'NO'),(2074,346,'host ','node-87',1224778529,0,'NO'),(2075,346,'cpuset','1',1224778529,0,'NO'),(2076,346,'network_address','127.0.2.87',1224778529,0,'NO'),(2077,347,'state','Alive',1224778529,0,'NO'),(2078,347,'core ','347',1224778529,0,'NO'),(2079,347,'cpu ','174',1224778529,0,'NO'),(2080,347,'host ','node-87',1224778529,0,'NO'),(2081,347,'cpuset','2',1224778529,0,'NO'),(2082,347,'network_address','127.0.2.87',1224778529,0,'NO'),(2083,348,'state','Alive',1224778530,0,'NO'),(2084,348,'core ','348',1224778530,0,'NO'),(2085,348,'cpu ','174',1224778530,0,'NO'),(2086,348,'host ','node-87',1224778530,0,'NO'),(2087,348,'cpuset','3',1224778530,0,'NO'),(2088,348,'network_address','127.0.2.87',1224778530,0,'NO'),(2089,349,'state','Alive',1224778530,0,'NO'),(2090,349,'core ','349',1224778530,0,'NO'),(2091,349,'cpu ','175',1224778530,0,'NO'),(2092,349,'host ','node-88',1224778530,0,'NO'),(2093,349,'cpuset','0',1224778530,0,'NO'),(2094,349,'network_address','127.0.2.88',1224778530,0,'NO'),(2095,350,'state','Alive',1224778530,0,'NO'),(2096,350,'core ','350',1224778530,0,'NO'),(2097,350,'cpu ','175',1224778530,0,'NO'),(2098,350,'host ','node-88',1224778530,0,'NO'),(2099,350,'cpuset','1',1224778530,0,'NO'),(2100,350,'network_address','127.0.2.88',1224778530,0,'NO'),(2101,351,'state','Alive',1224778530,0,'NO'),(2102,351,'core ','351',1224778530,0,'NO'),(2103,351,'cpu ','176',1224778530,0,'NO'),(2104,351,'host ','node-88',1224778530,0,'NO'),(2105,351,'cpuset','2',1224778530,0,'NO'),(2106,351,'network_address','127.0.2.88',1224778530,0,'NO'),(2107,352,'state','Alive',1224778530,0,'NO'),(2108,352,'core ','352',1224778530,0,'NO'),(2109,352,'cpu ','176',1224778530,0,'NO'),(2110,352,'host ','node-88',1224778530,0,'NO'),(2111,352,'cpuset','3',1224778530,0,'NO'),(2112,352,'network_address','127.0.2.88',1224778530,0,'NO'),(2113,353,'state','Alive',1224778530,0,'NO'),(2114,353,'core ','353',1224778530,0,'NO'),(2115,353,'cpu ','177',1224778530,0,'NO'),(2116,353,'host ','node-89',1224778530,0,'NO'),(2117,353,'cpuset','0',1224778530,0,'NO'),(2118,353,'network_address','127.0.2.89',1224778530,0,'NO'),(2119,354,'state','Alive',1224778531,0,'NO'),(2120,354,'core ','354',1224778531,0,'NO'),(2121,354,'cpu ','177',1224778531,0,'NO'),(2122,354,'host ','node-89',1224778531,0,'NO'),(2123,354,'cpuset','1',1224778531,0,'NO'),(2124,354,'network_address','127.0.2.89',1224778531,0,'NO'),(2125,355,'state','Alive',1224778531,0,'NO'),(2126,355,'core ','355',1224778531,0,'NO'),(2127,355,'cpu ','178',1224778531,0,'NO'),(2128,355,'host ','node-89',1224778531,0,'NO'),(2129,355,'cpuset','2',1224778531,0,'NO'),(2130,355,'network_address','127.0.2.89',1224778531,0,'NO'),(2131,356,'state','Alive',1224778531,0,'NO'),(2132,356,'core ','356',1224778531,0,'NO'),(2133,356,'cpu ','178',1224778531,0,'NO'),(2134,356,'host ','node-89',1224778531,0,'NO'),(2135,356,'cpuset','3',1224778531,0,'NO'),(2136,356,'network_address','127.0.2.89',1224778531,0,'NO'),(2137,357,'state','Alive',1224778531,0,'NO'),(2138,357,'core ','357',1224778531,0,'NO'),(2139,357,'cpu ','179',1224778531,0,'NO'),(2140,357,'host ','node-90',1224778531,0,'NO'),(2141,357,'cpuset','0',1224778531,0,'NO'),(2142,357,'network_address','127.0.2.90',1224778531,0,'NO'),(2143,358,'state','Alive',1224778531,0,'NO'),(2144,358,'core ','358',1224778531,0,'NO'),(2145,358,'cpu ','179',1224778531,0,'NO'),(2146,358,'host ','node-90',1224778531,0,'NO'),(2147,358,'cpuset','1',1224778531,0,'NO'),(2148,358,'network_address','127.0.2.90',1224778531,0,'NO'),(2149,359,'state','Alive',1224778532,0,'NO'),(2150,359,'core ','359',1224778532,0,'NO'),(2151,359,'cpu ','180',1224778532,0,'NO'),(2152,359,'host ','node-90',1224778532,0,'NO'),(2153,359,'cpuset','2',1224778532,0,'NO'),(2154,359,'network_address','127.0.2.90',1224778532,0,'NO'),(2155,360,'state','Alive',1224778532,0,'NO'),(2156,360,'core ','360',1224778532,0,'NO'),(2157,360,'cpu ','180',1224778532,0,'NO'),(2158,360,'host ','node-90',1224778532,0,'NO'),(2159,360,'cpuset','3',1224778532,0,'NO'),(2160,360,'network_address','127.0.2.90',1224778532,0,'NO'),(2161,361,'state','Alive',1224778532,0,'NO'),(2162,361,'core ','361',1224778532,0,'NO'),(2163,361,'cpu ','181',1224778532,0,'NO'),(2164,361,'host ','node-91',1224778532,0,'NO'),(2165,361,'cpuset','0',1224778532,0,'NO'),(2166,361,'network_address','127.0.2.91',1224778532,0,'NO'),(2167,362,'state','Alive',1224778532,0,'NO'),(2168,362,'core ','362',1224778532,0,'NO'),(2169,362,'cpu ','181',1224778532,0,'NO'),(2170,362,'host ','node-91',1224778532,0,'NO'),(2171,362,'cpuset','1',1224778532,0,'NO'),(2172,362,'network_address','127.0.2.91',1224778532,0,'NO'),(2173,363,'state','Alive',1224778532,0,'NO'),(2174,363,'core ','363',1224778532,0,'NO'),(2175,363,'cpu ','182',1224778532,0,'NO'),(2176,363,'host ','node-91',1224778532,0,'NO'),(2177,363,'cpuset','2',1224778532,0,'NO'),(2178,363,'network_address','127.0.2.91',1224778532,0,'NO'),(2179,364,'state','Alive',1224778533,0,'NO'),(2180,364,'core ','364',1224778533,0,'NO'),(2181,364,'cpu ','182',1224778533,0,'NO'),(2182,364,'host ','node-91',1224778533,0,'NO'),(2183,364,'cpuset','3',1224778533,0,'NO'),(2184,364,'network_address','127.0.2.91',1224778533,0,'NO'),(2185,365,'state','Alive',1224778533,0,'NO'),(2186,365,'core ','365',1224778533,0,'NO'),(2187,365,'cpu ','183',1224778533,0,'NO'),(2188,365,'host ','node-92',1224778533,0,'NO'),(2189,365,'cpuset','0',1224778533,0,'NO'),(2190,365,'network_address','127.0.2.92',1224778533,0,'NO'),(2191,366,'state','Alive',1224778533,0,'NO'),(2192,366,'core ','366',1224778533,0,'NO'),(2193,366,'cpu ','183',1224778533,0,'NO'),(2194,366,'host ','node-92',1224778533,0,'NO'),(2195,366,'cpuset','1',1224778533,0,'NO'),(2196,366,'network_address','127.0.2.92',1224778533,0,'NO'),(2197,367,'state','Alive',1224778533,0,'NO'),(2198,367,'core ','367',1224778533,0,'NO'),(2199,367,'cpu ','184',1224778533,0,'NO'),(2200,367,'host ','node-92',1224778533,0,'NO'),(2201,367,'cpuset','2',1224778533,0,'NO'),(2202,367,'network_address','127.0.2.92',1224778533,0,'NO'),(2203,368,'state','Alive',1224778533,0,'NO'),(2204,368,'core ','368',1224778533,0,'NO'),(2205,368,'cpu ','184',1224778533,0,'NO'),(2206,368,'host ','node-92',1224778533,0,'NO'),(2207,368,'cpuset','3',1224778533,0,'NO'),(2208,368,'network_address','127.0.2.92',1224778533,0,'NO'),(2209,369,'state','Alive',1224778534,0,'NO'),(2210,369,'core ','369',1224778534,0,'NO'),(2211,369,'cpu ','185',1224778534,0,'NO'),(2212,369,'host ','node-93',1224778534,0,'NO'),(2213,369,'cpuset','0',1224778534,0,'NO'),(2214,369,'network_address','127.0.2.93',1224778534,0,'NO'),(2215,370,'state','Alive',1224778534,0,'NO'),(2216,370,'core ','370',1224778534,0,'NO'),(2217,370,'cpu ','185',1224778534,0,'NO'),(2218,370,'host ','node-93',1224778534,0,'NO'),(2219,370,'cpuset','1',1224778534,0,'NO'),(2220,370,'network_address','127.0.2.93',1224778534,0,'NO'),(2221,371,'state','Alive',1224778534,0,'NO'),(2222,371,'core ','371',1224778534,0,'NO'),(2223,371,'cpu ','186',1224778534,0,'NO'),(2224,371,'host ','node-93',1224778534,0,'NO'),(2225,371,'cpuset','2',1224778534,0,'NO'),(2226,371,'network_address','127.0.2.93',1224778534,0,'NO'),(2227,372,'state','Alive',1224778534,0,'NO'),(2228,372,'core ','372',1224778534,0,'NO'),(2229,372,'cpu ','186',1224778534,0,'NO'),(2230,372,'host ','node-93',1224778534,0,'NO'),(2231,372,'cpuset','3',1224778534,0,'NO'),(2232,372,'network_address','127.0.2.93',1224778534,0,'NO'),(2233,373,'state','Alive',1224778534,0,'NO'),(2234,373,'core ','373',1224778534,0,'NO'),(2235,373,'cpu ','187',1224778534,0,'NO'),(2236,373,'host ','node-94',1224778534,0,'NO'),(2237,373,'cpuset','0',1224778534,0,'NO'),(2238,373,'network_address','127.0.2.94',1224778534,0,'NO'),(2239,374,'state','Alive',1224778535,0,'NO'),(2240,374,'core ','374',1224778535,0,'NO'),(2241,374,'cpu ','187',1224778535,0,'NO'),(2242,374,'host ','node-94',1224778535,0,'NO'),(2243,374,'cpuset','1',1224778535,0,'NO'),(2244,374,'network_address','127.0.2.94',1224778535,0,'NO'),(2245,375,'state','Alive',1224778535,0,'NO'),(2246,375,'core ','375',1224778535,0,'NO'),(2247,375,'cpu ','188',1224778535,0,'NO'),(2248,375,'host ','node-94',1224778535,0,'NO'),(2249,375,'cpuset','2',1224778535,0,'NO'),(2250,375,'network_address','127.0.2.94',1224778535,0,'NO'),(2251,376,'state','Alive',1224778535,0,'NO'),(2252,376,'core ','376',1224778535,0,'NO'),(2253,376,'cpu ','188',1224778535,0,'NO'),(2254,376,'host ','node-94',1224778535,0,'NO'),(2255,376,'cpuset','3',1224778535,0,'NO'),(2256,376,'network_address','127.0.2.94',1224778535,0,'NO'),(2257,377,'state','Alive',1224778535,0,'NO'),(2258,377,'core ','377',1224778535,0,'NO'),(2259,377,'cpu ','189',1224778535,0,'NO'),(2260,377,'host ','node-95',1224778535,0,'NO'),(2261,377,'cpuset','0',1224778535,0,'NO'),(2262,377,'network_address','127.0.2.95',1224778535,0,'NO'),(2263,378,'state','Alive',1224778535,0,'NO'),(2264,378,'core ','378',1224778535,0,'NO'),(2265,378,'cpu ','189',1224778535,0,'NO'),(2266,378,'host ','node-95',1224778535,0,'NO'),(2267,378,'cpuset','1',1224778535,0,'NO'),(2268,378,'network_address','127.0.2.95',1224778535,0,'NO'),(2269,379,'state','Alive',1224778535,0,'NO'),(2270,379,'core ','379',1224778535,0,'NO'),(2271,379,'cpu ','190',1224778535,0,'NO'),(2272,379,'host ','node-95',1224778535,0,'NO'),(2273,379,'cpuset','2',1224778535,0,'NO'),(2274,379,'network_address','127.0.2.95',1224778535,0,'NO'),(2275,380,'state','Alive',1224778536,0,'NO'),(2276,380,'core ','380',1224778536,0,'NO'),(2277,380,'cpu ','190',1224778536,0,'NO'),(2278,380,'host ','node-95',1224778536,0,'NO'),(2279,380,'cpuset','3',1224778536,0,'NO'),(2280,380,'network_address','127.0.2.95',1224778536,0,'NO'),(2281,381,'state','Alive',1224778536,0,'NO'),(2282,381,'core ','381',1224778536,0,'NO'),(2283,381,'cpu ','191',1224778536,0,'NO'),(2284,381,'host ','node-96',1224778536,0,'NO'),(2285,381,'cpuset','0',1224778536,0,'NO'),(2286,381,'network_address','127.0.2.96',1224778536,0,'NO'),(2287,382,'state','Alive',1224778536,0,'NO'),(2288,382,'core ','382',1224778536,0,'NO'),(2289,382,'cpu ','191',1224778536,0,'NO'),(2290,382,'host ','node-96',1224778536,0,'NO'),(2291,382,'cpuset','1',1224778536,0,'NO'),(2292,382,'network_address','127.0.2.96',1224778536,0,'NO'),(2293,383,'state','Alive',1224778536,0,'NO'),(2294,383,'core ','383',1224778536,0,'NO'),(2295,383,'cpu ','192',1224778536,0,'NO'),(2296,383,'host ','node-96',1224778536,0,'NO'),(2297,383,'cpuset','2',1224778536,0,'NO'),(2298,383,'network_address','127.0.2.96',1224778536,0,'NO'),(2299,384,'state','Alive',1224778536,0,'NO'),(2300,384,'core ','384',1224778536,0,'NO'),(2301,384,'cpu ','192',1224778536,0,'NO'),(2302,384,'host ','node-96',1224778536,0,'NO'),(2303,384,'cpuset','3',1224778536,0,'NO'),(2304,384,'network_address','127.0.2.96',1224778536,0,'NO'); /*!40000 ALTER TABLE `resource_logs` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `resources` -- DROP TABLE IF EXISTS `resources`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `resources` ( `resource_id` int(10) unsigned NOT NULL auto_increment, `type` varchar(100) NOT NULL default 'default', `network_address` varchar(100) NOT NULL, `state` enum('Alive','Dead','Suspected','Absent') NOT NULL, `next_state` enum('UnChanged','Alive','Dead','Absent','Suspected') NOT NULL default 'UnChanged', `finaud_decision` enum('YES','NO') NOT NULL default 'NO', `next_finaud_decision` enum('YES','NO') NOT NULL default 'NO', `state_num` int(11) NOT NULL default '0', `suspended_jobs` enum('YES','NO') NOT NULL default 'NO', `scheduler_priority` int(10) unsigned NOT NULL default '0', `cpuset` int(10) unsigned NOT NULL default '0', `besteffort` enum('YES','NO') NOT NULL default 'YES', `deploy` enum('YES','NO') NOT NULL default 'NO', `expiry_date` int(10) unsigned NOT NULL, `desktop_computing` enum('YES','NO') NOT NULL default 'NO', `last_job_date` int(10) unsigned default '0', `available_upto` int(10) unsigned NOT NULL default '0', `host` varchar(255) default NULL, `cpu` int(11) default NULL, `core` int(11) default NULL, PRIMARY KEY (`resource_id`), KEY `state` (`state`), KEY `next_state` (`next_state`), KEY `suspended_jobs` (`suspended_jobs`), KEY `type` (`type`), KEY `network_address` (`network_address`) ) ENGINE=MyISAM AUTO_INCREMENT=385 DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `resources` -- LOCK TABLES `resources` WRITE; /*!40000 ALTER TABLE `resources` DISABLE KEYS */; INSERT INTO `resources` VALUES (1,'default','127.0.2.1','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-1',1,1),(2,'default','127.0.2.1','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-1',1,2),(3,'default','127.0.2.1','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-1',2,3),(4,'default','127.0.2.1','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-1',2,4),(5,'default','127.0.2.2','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-2',3,5),(6,'default','127.0.2.2','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-2',3,6),(7,'default','127.0.2.2','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-2',4,7),(8,'default','127.0.2.2','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-2',4,8),(9,'default','127.0.2.3','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-3',5,9),(10,'default','127.0.2.3','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-3',5,10),(11,'default','127.0.2.3','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-3',6,11),(12,'default','127.0.2.3','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-3',6,12),(13,'default','127.0.2.4','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-4',7,13),(14,'default','127.0.2.4','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-4',7,14),(15,'default','127.0.2.4','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-4',8,15),(16,'default','127.0.2.4','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-4',8,16),(17,'default','127.0.2.5','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-5',9,17),(18,'default','127.0.2.5','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-5',9,18),(19,'default','127.0.2.5','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-5',10,19),(20,'default','127.0.2.5','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-5',10,20),(21,'default','127.0.2.6','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-6',11,21),(22,'default','127.0.2.6','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-6',11,22),(23,'default','127.0.2.6','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-6',12,23),(24,'default','127.0.2.6','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-6',12,24),(25,'default','127.0.2.7','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-7',13,25),(26,'default','127.0.2.7','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-7',13,26),(27,'default','127.0.2.7','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-7',14,27),(28,'default','127.0.2.7','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-7',14,28),(29,'default','127.0.2.8','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-8',15,29),(30,'default','127.0.2.8','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-8',15,30),(31,'default','127.0.2.8','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-8',16,31),(32,'default','127.0.2.8','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-8',16,32),(33,'default','127.0.2.9','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-9',17,33),(34,'default','127.0.2.9','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-9',17,34),(35,'default','127.0.2.9','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-9',18,35),(36,'default','127.0.2.9','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-9',18,36),(37,'default','127.0.2.10','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-10',19,37),(38,'default','127.0.2.10','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-10',19,38),(39,'default','127.0.2.10','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-10',20,39),(40,'default','127.0.2.10','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-10',20,40),(41,'default','127.0.2.11','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-11',21,41),(42,'default','127.0.2.11','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-11',21,42),(43,'default','127.0.2.11','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-11',22,43),(44,'default','127.0.2.11','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-11',22,44),(45,'default','127.0.2.12','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-12',23,45),(46,'default','127.0.2.12','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-12',23,46),(47,'default','127.0.2.12','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-12',24,47),(48,'default','127.0.2.12','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-12',24,48),(49,'default','127.0.2.13','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-13',25,49),(50,'default','127.0.2.13','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-13',25,50),(51,'default','127.0.2.13','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-13',26,51),(52,'default','127.0.2.13','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-13',26,52),(53,'default','127.0.2.14','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-14',27,53),(54,'default','127.0.2.14','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-14',27,54),(55,'default','127.0.2.14','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-14',28,55),(56,'default','127.0.2.14','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-14',28,56),(57,'default','127.0.2.15','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-15',29,57),(58,'default','127.0.2.15','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-15',29,58),(59,'default','127.0.2.15','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-15',30,59),(60,'default','127.0.2.15','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-15',30,60),(61,'default','127.0.2.16','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-16',31,61),(62,'default','127.0.2.16','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-16',31,62),(63,'default','127.0.2.16','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-16',32,63),(64,'default','127.0.2.16','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-16',32,64),(65,'default','127.0.2.17','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-17',33,65),(66,'default','127.0.2.17','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-17',33,66),(67,'default','127.0.2.17','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-17',34,67),(68,'default','127.0.2.17','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-17',34,68),(69,'default','127.0.2.18','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-18',35,69),(70,'default','127.0.2.18','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-18',35,70),(71,'default','127.0.2.18','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-18',36,71),(72,'default','127.0.2.18','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-18',36,72),(73,'default','127.0.2.19','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-19',37,73),(74,'default','127.0.2.19','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-19',37,74),(75,'default','127.0.2.19','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-19',38,75),(76,'default','127.0.2.19','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-19',38,76),(77,'default','127.0.2.20','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-20',39,77),(78,'default','127.0.2.20','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-20',39,78),(79,'default','127.0.2.20','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-20',40,79),(80,'default','127.0.2.20','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-20',40,80),(81,'default','127.0.2.21','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-21',41,81),(82,'default','127.0.2.21','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-21',41,82),(83,'default','127.0.2.21','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-21',42,83),(84,'default','127.0.2.21','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-21',42,84),(85,'default','127.0.2.22','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-22',43,85),(86,'default','127.0.2.22','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-22',43,86),(87,'default','127.0.2.22','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-22',44,87),(88,'default','127.0.2.22','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-22',44,88),(89,'default','127.0.2.23','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-23',45,89),(90,'default','127.0.2.23','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-23',45,90),(91,'default','127.0.2.23','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-23',46,91),(92,'default','127.0.2.23','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-23',46,92),(93,'default','127.0.2.24','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-24',47,93),(94,'default','127.0.2.24','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-24',47,94),(95,'default','127.0.2.24','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-24',48,95),(96,'default','127.0.2.24','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-24',48,96),(97,'default','127.0.2.25','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-25',49,97),(98,'default','127.0.2.25','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-25',49,98),(99,'default','127.0.2.25','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-25',50,99),(100,'default','127.0.2.25','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-25',50,100),(101,'default','127.0.2.26','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-26',51,101),(102,'default','127.0.2.26','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-26',51,102),(103,'default','127.0.2.26','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-26',52,103),(104,'default','127.0.2.26','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-26',52,104),(105,'default','127.0.2.27','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-27',53,105),(106,'default','127.0.2.27','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-27',53,106),(107,'default','127.0.2.27','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-27',54,107),(108,'default','127.0.2.27','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-27',54,108),(109,'default','127.0.2.28','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-28',55,109),(110,'default','127.0.2.28','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-28',55,110),(111,'default','127.0.2.28','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-28',56,111),(112,'default','127.0.2.28','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-28',56,112),(113,'default','127.0.2.29','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-29',57,113),(114,'default','127.0.2.29','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-29',57,114),(115,'default','127.0.2.29','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-29',58,115),(116,'default','127.0.2.29','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-29',58,116),(117,'default','127.0.2.30','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-30',59,117),(118,'default','127.0.2.30','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-30',59,118),(119,'default','127.0.2.30','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-30',60,119),(120,'default','127.0.2.30','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-30',60,120),(121,'default','127.0.2.31','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-31',61,121),(122,'default','127.0.2.31','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-31',61,122),(123,'default','127.0.2.31','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-31',62,123),(124,'default','127.0.2.31','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-31',62,124),(125,'default','127.0.2.32','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-32',63,125),(126,'default','127.0.2.32','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-32',63,126),(127,'default','127.0.2.32','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-32',64,127),(128,'default','127.0.2.32','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-32',64,128),(129,'default','127.0.2.33','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-33',65,129),(130,'default','127.0.2.33','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-33',65,130),(131,'default','127.0.2.33','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-33',66,131),(132,'default','127.0.2.33','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-33',66,132),(133,'default','127.0.2.34','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-34',67,133),(134,'default','127.0.2.34','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-34',67,134),(135,'default','127.0.2.34','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-34',68,135),(136,'default','127.0.2.34','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-34',68,136),(137,'default','127.0.2.35','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-35',69,137),(138,'default','127.0.2.35','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-35',69,138),(139,'default','127.0.2.35','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-35',70,139),(140,'default','127.0.2.35','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-35',70,140),(141,'default','127.0.2.36','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-36',71,141),(142,'default','127.0.2.36','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-36',71,142),(143,'default','127.0.2.36','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-36',72,143),(144,'default','127.0.2.36','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-36',72,144),(145,'default','127.0.2.37','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-37',73,145),(146,'default','127.0.2.37','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-37',73,146),(147,'default','127.0.2.37','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-37',74,147),(148,'default','127.0.2.37','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-37',74,148),(149,'default','127.0.2.38','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-38',75,149),(150,'default','127.0.2.38','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-38',75,150),(151,'default','127.0.2.38','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-38',76,151),(152,'default','127.0.2.38','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-38',76,152),(153,'default','127.0.2.39','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-39',77,153),(154,'default','127.0.2.39','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-39',77,154),(155,'default','127.0.2.39','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-39',78,155),(156,'default','127.0.2.39','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-39',78,156),(157,'default','127.0.2.40','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-40',79,157),(158,'default','127.0.2.40','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-40',79,158),(159,'default','127.0.2.40','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-40',80,159),(160,'default','127.0.2.40','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-40',80,160),(161,'default','127.0.2.41','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-41',81,161),(162,'default','127.0.2.41','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-41',81,162),(163,'default','127.0.2.41','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-41',82,163),(164,'default','127.0.2.41','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-41',82,164),(165,'default','127.0.2.42','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-42',83,165),(166,'default','127.0.2.42','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-42',83,166),(167,'default','127.0.2.42','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-42',84,167),(168,'default','127.0.2.42','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-42',84,168),(169,'default','127.0.2.43','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-43',85,169),(170,'default','127.0.2.43','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-43',85,170),(171,'default','127.0.2.43','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-43',86,171),(172,'default','127.0.2.43','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-43',86,172),(173,'default','127.0.2.44','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-44',87,173),(174,'default','127.0.2.44','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-44',87,174),(175,'default','127.0.2.44','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-44',88,175),(176,'default','127.0.2.44','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-44',88,176),(177,'default','127.0.2.45','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-45',89,177),(178,'default','127.0.2.45','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-45',89,178),(179,'default','127.0.2.45','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-45',90,179),(180,'default','127.0.2.45','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-45',90,180),(181,'default','127.0.2.46','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-46',91,181),(182,'default','127.0.2.46','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-46',91,182),(183,'default','127.0.2.46','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-46',92,183),(184,'default','127.0.2.46','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-46',92,184),(185,'default','127.0.2.47','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-47',93,185),(186,'default','127.0.2.47','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-47',93,186),(187,'default','127.0.2.47','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-47',94,187),(188,'default','127.0.2.47','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-47',94,188),(189,'default','127.0.2.48','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-48',95,189),(190,'default','127.0.2.48','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-48',95,190),(191,'default','127.0.2.48','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-48',96,191),(192,'default','127.0.2.48','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-48',96,192),(193,'default','127.0.2.49','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-49',97,193),(194,'default','127.0.2.49','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-49',97,194),(195,'default','127.0.2.49','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-49',98,195),(196,'default','127.0.2.49','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-49',98,196),(197,'default','127.0.2.50','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-50',99,197),(198,'default','127.0.2.50','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-50',99,198),(199,'default','127.0.2.50','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-50',100,199),(200,'default','127.0.2.50','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-50',100,200),(201,'default','127.0.2.51','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-51',101,201),(202,'default','127.0.2.51','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-51',101,202),(203,'default','127.0.2.51','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-51',102,203),(204,'default','127.0.2.51','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-51',102,204),(205,'default','127.0.2.52','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-52',103,205),(206,'default','127.0.2.52','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-52',103,206),(207,'default','127.0.2.52','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-52',104,207),(208,'default','127.0.2.52','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-52',104,208),(209,'default','127.0.2.53','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-53',105,209),(210,'default','127.0.2.53','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-53',105,210),(211,'default','127.0.2.53','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-53',106,211),(212,'default','127.0.2.53','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-53',106,212),(213,'default','127.0.2.54','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-54',107,213),(214,'default','127.0.2.54','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-54',107,214),(215,'default','127.0.2.54','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-54',108,215),(216,'default','127.0.2.54','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-54',108,216),(217,'default','127.0.2.55','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-55',109,217),(218,'default','127.0.2.55','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-55',109,218),(219,'default','127.0.2.55','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-55',110,219),(220,'default','127.0.2.55','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-55',110,220),(221,'default','127.0.2.56','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-56',111,221),(222,'default','127.0.2.56','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-56',111,222),(223,'default','127.0.2.56','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-56',112,223),(224,'default','127.0.2.56','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-56',112,224),(225,'default','127.0.2.57','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-57',113,225),(226,'default','127.0.2.57','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-57',113,226),(227,'default','127.0.2.57','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-57',114,227),(228,'default','127.0.2.57','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-57',114,228),(229,'default','127.0.2.58','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-58',115,229),(230,'default','127.0.2.58','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-58',115,230),(231,'default','127.0.2.58','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-58',116,231),(232,'default','127.0.2.58','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-58',116,232),(233,'default','127.0.2.59','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-59',117,233),(234,'default','127.0.2.59','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-59',117,234),(235,'default','127.0.2.59','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-59',118,235),(236,'default','127.0.2.59','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-59',118,236),(237,'default','127.0.2.60','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-60',119,237),(238,'default','127.0.2.60','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-60',119,238),(239,'default','127.0.2.60','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-60',120,239),(240,'default','127.0.2.60','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-60',120,240),(241,'default','127.0.2.61','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-61',121,241),(242,'default','127.0.2.61','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-61',121,242),(243,'default','127.0.2.61','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-61',122,243),(244,'default','127.0.2.61','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-61',122,244),(245,'default','127.0.2.62','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-62',123,245),(246,'default','127.0.2.62','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-62',123,246),(247,'default','127.0.2.62','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-62',124,247),(248,'default','127.0.2.62','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-62',124,248),(249,'default','127.0.2.63','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-63',125,249),(250,'default','127.0.2.63','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-63',125,250),(251,'default','127.0.2.63','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-63',126,251),(252,'default','127.0.2.63','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-63',126,252),(253,'default','127.0.2.64','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-64',127,253),(254,'default','127.0.2.64','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-64',127,254),(255,'default','127.0.2.64','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-64',128,255),(256,'default','127.0.2.64','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-64',128,256),(257,'default','127.0.2.65','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-65',129,257),(258,'default','127.0.2.65','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-65',129,258),(259,'default','127.0.2.65','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-65',130,259),(260,'default','127.0.2.65','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-65',130,260),(261,'default','127.0.2.66','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-66',131,261),(262,'default','127.0.2.66','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-66',131,262),(263,'default','127.0.2.66','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-66',132,263),(264,'default','127.0.2.66','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-66',132,264),(265,'default','127.0.2.67','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-67',133,265),(266,'default','127.0.2.67','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-67',133,266),(267,'default','127.0.2.67','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-67',134,267),(268,'default','127.0.2.67','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-67',134,268),(269,'default','127.0.2.68','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-68',135,269),(270,'default','127.0.2.68','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-68',135,270),(271,'default','127.0.2.68','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-68',136,271),(272,'default','127.0.2.68','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-68',136,272),(273,'default','127.0.2.69','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-69',137,273),(274,'default','127.0.2.69','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-69',137,274),(275,'default','127.0.2.69','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-69',138,275),(276,'default','127.0.2.69','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-69',138,276),(277,'default','127.0.2.70','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-70',139,277),(278,'default','127.0.2.70','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-70',139,278),(279,'default','127.0.2.70','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-70',140,279),(280,'default','127.0.2.70','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-70',140,280),(281,'default','127.0.2.71','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-71',141,281),(282,'default','127.0.2.71','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-71',141,282),(283,'default','127.0.2.71','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-71',142,283),(284,'default','127.0.2.71','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-71',142,284),(285,'default','127.0.2.72','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-72',143,285),(286,'default','127.0.2.72','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-72',143,286),(287,'default','127.0.2.72','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-72',144,287),(288,'default','127.0.2.72','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-72',144,288),(289,'default','127.0.2.73','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-73',145,289),(290,'default','127.0.2.73','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-73',145,290),(291,'default','127.0.2.73','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-73',146,291),(292,'default','127.0.2.73','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-73',146,292),(293,'default','127.0.2.74','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-74',147,293),(294,'default','127.0.2.74','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-74',147,294),(295,'default','127.0.2.74','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-74',148,295),(296,'default','127.0.2.74','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-74',148,296),(297,'default','127.0.2.75','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-75',149,297),(298,'default','127.0.2.75','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-75',149,298),(299,'default','127.0.2.75','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-75',150,299),(300,'default','127.0.2.75','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-75',150,300),(301,'default','127.0.2.76','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-76',151,301),(302,'default','127.0.2.76','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-76',151,302),(303,'default','127.0.2.76','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-76',152,303),(304,'default','127.0.2.76','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-76',152,304),(305,'default','127.0.2.77','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-77',153,305),(306,'default','127.0.2.77','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-77',153,306),(307,'default','127.0.2.77','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-77',154,307),(308,'default','127.0.2.77','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-77',154,308),(309,'default','127.0.2.78','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-78',155,309),(310,'default','127.0.2.78','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-78',155,310),(311,'default','127.0.2.78','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-78',156,311),(312,'default','127.0.2.78','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-78',156,312),(313,'default','127.0.2.79','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-79',157,313),(314,'default','127.0.2.79','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-79',157,314),(315,'default','127.0.2.79','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-79',158,315),(316,'default','127.0.2.79','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-79',158,316),(317,'default','127.0.2.80','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-80',159,317),(318,'default','127.0.2.80','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-80',159,318),(319,'default','127.0.2.80','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-80',160,319),(320,'default','127.0.2.80','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-80',160,320),(321,'default','127.0.2.81','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-81',161,321),(322,'default','127.0.2.81','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-81',161,322),(323,'default','127.0.2.81','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-81',162,323),(324,'default','127.0.2.81','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-81',162,324),(325,'default','127.0.2.82','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-82',163,325),(326,'default','127.0.2.82','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-82',163,326),(327,'default','127.0.2.82','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-82',164,327),(328,'default','127.0.2.82','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-82',164,328),(329,'default','127.0.2.83','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-83',165,329),(330,'default','127.0.2.83','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-83',165,330),(331,'default','127.0.2.83','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-83',166,331),(332,'default','127.0.2.83','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-83',166,332),(333,'default','127.0.2.84','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-84',167,333),(334,'default','127.0.2.84','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-84',167,334),(335,'default','127.0.2.84','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-84',168,335),(336,'default','127.0.2.84','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-84',168,336),(337,'default','127.0.2.85','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-85',169,337),(338,'default','127.0.2.85','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-85',169,338),(339,'default','127.0.2.85','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-85',170,339),(340,'default','127.0.2.85','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-85',170,340),(341,'default','127.0.2.86','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-86',171,341),(342,'default','127.0.2.86','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-86',171,342),(343,'default','127.0.2.86','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-86',172,343),(344,'default','127.0.2.86','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-86',172,344),(345,'default','127.0.2.87','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-87',173,345),(346,'default','127.0.2.87','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-87',173,346),(347,'default','127.0.2.87','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-87',174,347),(348,'default','127.0.2.87','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-87',174,348),(349,'default','127.0.2.88','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-88',175,349),(350,'default','127.0.2.88','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-88',175,350),(351,'default','127.0.2.88','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-88',176,351),(352,'default','127.0.2.88','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-88',176,352),(353,'default','127.0.2.89','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-89',177,353),(354,'default','127.0.2.89','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-89',177,354),(355,'default','127.0.2.89','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-89',178,355),(356,'default','127.0.2.89','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-89',178,356),(357,'default','127.0.2.90','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-90',179,357),(358,'default','127.0.2.90','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-90',179,358),(359,'default','127.0.2.90','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-90',180,359),(360,'default','127.0.2.90','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-90',180,360),(361,'default','127.0.2.91','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-91',181,361),(362,'default','127.0.2.91','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-91',181,362),(363,'default','127.0.2.91','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-91',182,363),(364,'default','127.0.2.91','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-91',182,364),(365,'default','127.0.2.92','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-92',183,365),(366,'default','127.0.2.92','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-92',183,366),(367,'default','127.0.2.92','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-92',184,367),(368,'default','127.0.2.92','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-92',184,368),(369,'default','127.0.2.93','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-93',185,369),(370,'default','127.0.2.93','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-93',185,370),(371,'default','127.0.2.93','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-93',186,371),(372,'default','127.0.2.93','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-93',186,372),(373,'default','127.0.2.94','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-94',187,373),(374,'default','127.0.2.94','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-94',187,374),(375,'default','127.0.2.94','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-94',188,375),(376,'default','127.0.2.94','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-94',188,376),(377,'default','127.0.2.95','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-95',189,377),(378,'default','127.0.2.95','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-95',189,378),(379,'default','127.0.2.95','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-95',190,379),(380,'default','127.0.2.95','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-95',190,380),(381,'default','127.0.2.96','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0,'node-96',191,381),(382,'default','127.0.2.96','Alive','UnChanged','NO','NO',1,'NO',0,1,'YES','NO',0,'NO',0,0,'node-96',191,382),(383,'default','127.0.2.96','Alive','UnChanged','NO','NO',1,'NO',0,2,'YES','NO',0,'NO',0,0,'node-96',192,383),(384,'default','127.0.2.96','Alive','UnChanged','NO','NO',1,'NO',0,3,'YES','NO',0,'NO',0,0,'node-96',192,384); /*!40000 ALTER TABLE `resources` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `schema` -- DROP TABLE IF EXISTS `schema`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `schema` ( `version` varchar(255) NOT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `schema` -- LOCK TABLES `schema` WRITE; /*!40000 ALTER TABLE `schema` DISABLE KEYS */; INSERT INTO `schema` VALUES ('2.3.0+svn1369'); /*!40000 ALTER TABLE `schema` ENABLE KEYS */; UNLOCK TABLES; /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; -- Dump completed on 2008-10-23 16:18:51 ./oar-2.5.2/tests/scheduler/data/002_oar.conf0000777000175000017500000000000011757171206020627 2000_oar.confustar plbplb./oar-2.5.2/tests/scheduler/data/002_my.cnf0000777000175000017500000000000011757171206020001 2000_my.cnfustar plbplb./oar-2.5.2/tests/scheduler/data/002_advance_reservation_with_dead_resource.sql0000644000175000017500000007456411757171206025675 0ustar plbplb-- MySQL dump 10.11 -- -- Host: localhost Database: oar -- ------------------------------------------------------ -- Server version 5.0.51a-15 /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; /*!40101 SET NAMES utf8 */; /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; /*!40103 SET TIME_ZONE='+00:00' */; /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; -- -- Table structure for table `accounting` -- DROP TABLE IF EXISTS `accounting`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `accounting` ( `window_start` int(10) unsigned NOT NULL, `window_stop` int(10) unsigned NOT NULL, `accounting_user` varchar(255) NOT NULL, `accounting_project` varchar(255) NOT NULL, `queue_name` varchar(100) NOT NULL, `consumption_type` enum('ASKED','USED') NOT NULL, `consumption` int(10) unsigned NOT NULL, PRIMARY KEY (`window_start`,`window_stop`,`accounting_user`,`accounting_project`,`queue_name`,`consumption_type`), KEY `accounting_user` (`accounting_user`), KEY `accounting_project` (`accounting_project`), KEY `accounting_queue` (`queue_name`), KEY `accounting_type` (`consumption_type`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `accounting` -- LOCK TABLES `accounting` WRITE; /*!40000 ALTER TABLE `accounting` DISABLE KEYS */; /*!40000 ALTER TABLE `accounting` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `admission_rules` -- DROP TABLE IF EXISTS `admission_rules`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `admission_rules` ( `id` int(10) unsigned NOT NULL auto_increment, `rule` text NOT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM AUTO_INCREMENT=15 DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `admission_rules` -- LOCK TABLES `admission_rules` WRITE; /*!40000 ALTER TABLE `admission_rules` DISABLE KEYS */; INSERT INTO `admission_rules` VALUES (1,'if (not defined($queue_name)) {$queue_name=\"default\";}'),(2,'die (\"[ADMISSION RULE] root and oar users are not allowed to submit jobs.\\n\") if ( $user eq \"root\" or $user eq \"oar\" );'),(3,'\nmy $admin_group = \"admin\";\nif ($queue_name eq \"admin\") {\n my $members; \n (undef,undef,undef, $members) = getgrnam($admin_group);\n my %h = map { $_ => 1 } split(/\\s+/,$members);\n if ( $h{$user} ne 1 ) {\n {die(\"[ADMISSION RULE] Only member of the group \".$admin_group.\" can submit jobs in the admin queue\\n\");}\n }\n}\n'),(4,'\nmy @bad_resources = (\"type\",\"state\",\"next_state\",\"finaud_decision\",\"next_finaud_decision\",\"state_num\",\"suspended_jobs\",\"besteffort\",\"deploy\",\"expiry_date\",\"desktop_computing\",\"last_job_date\",\"available_upto\",\"scheduler_priority\");\nforeach my $mold (@{$ref_resource_list}){\n foreach my $r (@{$mold->[0]}){\n my $i = 0;\n while (($i <= $#{$r->{resources}})){\n if (grep(/^$r->{resources}->[$i]->{resource}$/, @bad_resources)){\n die(\"[ADMISSION RULE] \'$r->{resources}->[$i]->{resource}\' resource is not allowed\\n\");\n }\n $i++;\n }\n }\n}\n'),(5,'\nif (grep(/^besteffort$/, @{$type_list}) and not $queue_name eq \"besteffort\"){\n $queue_name = \"besteffort\";\n print(\"[ADMISSION RULE] Automatically redirect in the besteffort queue\\n\");\n}\nif ($queue_name eq \"besteffort\" and not grep(/^besteffort$/, @{$type_list})) {\n push(@{$type_list},\"besteffort\");\n print(\"[ADMISSION RULE] Automatically add the besteffort type\\n\");\n}\nif (grep(/^besteffort$/, @{$type_list})){\n if ($jobproperties ne \"\"){\n $jobproperties = \"($jobproperties) AND besteffort = \\\'YES\\\'\";\n }else{\n $jobproperties = \"besteffort = \\\'YES\\\'\";\n }\n print(\"[ADMISSION RULE] Automatically add the besteffort constraint on the resources\\n\");\n}\n'),(6,'\nif ((grep(/^besteffort$/, @{$type_list})) and ($reservationField ne \"None\")){\n die(\"[ADMISSION RULE] Error: a job cannot both be of type besteffort and be a reservation.\\n\");\n}\n'),(7,'\nif (grep(/^deploy$/, @{$type_list})){\n if ($jobproperties ne \"\"){\n $jobproperties = \"($jobproperties) AND deploy = \\\'YES\\\'\";\n }else{\n $jobproperties = \"deploy = \\\'YES\\\'\";\n }\n}\n'),(8,'\nmy @bad_resources = (\"core\",\"cpu\",\"resource_id\",);\nif (grep(/^(deploy|allow_classic_ssh)$/, @{$type_list})){\n foreach my $mold (@{$ref_resource_list}){\n foreach my $r (@{$mold->[0]}){\n my $i = 0;\n while (($i <= $#{$r->{resources}})){\n if (grep(/^$r->{resources}->[$i]->{resource}$/, @bad_resources)){\n die(\"[ADMISSION RULE] \'$r->{resources}->[$i]->{resource}\' resource is not allowed with a deploy or allow_classic_ssh type job\\n\");\n }\n $i++;\n }\n }\n }\n}\n'),(9,'\nif (grep(/^desktop_computing$/, @{$type_list})){\n print(\"[ADMISSION RULE] Added automatically desktop_computing resource constraints\\n\");\n if ($jobproperties ne \"\"){\n $jobproperties = \"($jobproperties) AND desktop_computing = \\\'YES\\\'\";\n }else{\n $jobproperties = \"desktop_computing = \\\'YES\\\'\";\n }\n}else{\n if ($jobproperties ne \"\"){\n $jobproperties = \"($jobproperties) AND desktop_computing = \\\'NO\\\'\";\n }else{\n $jobproperties = \"desktop_computing = \\\'NO\\\'\";\n }\n}\n'),(10,'\nif ($reservationField eq \"toSchedule\") {\n my $unlimited=0;\n if (open(FILE, \"< $ENV{HOME}/unlimited_reservation.users\")) {\n while (){\n if (m/^\\s*$user\\s*$/m){\n $unlimited=1;\n }\n }\n close(FILE);\n }\n if ($unlimited > 0) {\n print(\"[ADMISSION RULE] $user is granted the privilege to do unlimited reservations\\n\");\n } else {\n my $max_nb_resa = 2;\n my $nb_resa = $dbh->do(\" SELECT job_id\n FROM jobs\n WHERE\n job_user = \\\'$user\\\' AND\n (reservation = \\\'toSchedule\\\' OR\n reservation = \\\'Scheduled\\\') AND\n (state = \\\'Waiting\\\' OR state = \\\'Hold\\\')\n \");\n if ($nb_resa >= $max_nb_resa){\n die(\"[ADMISSION RULE] Error : you cannot have more than $max_nb_resa waiting reservations.\\n\");\n }\n }\n}\n'),(11,'\nmy $max_walltime = OAR::IO::sql_to_duration(\"12:00:00\");\nif (($jobType eq \"INTERACTIVE\") and ($reservationField eq \"None\")){ \n foreach my $mold (@{$ref_resource_list}){\n if ((defined($mold->[1])) and ($max_walltime < $mold->[1])){\n print(\"[ADMISSION RULE] Walltime to big for an INTERACTIVE job so it is set to $max_walltime.\\n\");\n $mold->[1] = $max_walltime;\n }\n }\n}\n'),(12,'\nmy $default_wall = OAR::IO::sql_to_duration(\"2:00:00\");\nforeach my $mold (@{$ref_resource_list}){\n if (!defined($mold->[1])){\n print(\"[ADMISSION RULE] Set default walltime to $default_wall.\\n\");\n $mold->[1] = $default_wall;\n }\n}\n'),(13,'\nmy @types = (\"container\",\"inner\",\"deploy\",\"desktop_computing\",\"besteffort\",\"cosystem\",\"idempotent\",\"timesharing\",\"allow_classic_ssh\");\nforeach my $t (@{$type_list}){\n my $i = 0;\n while (($types[$i] ne $t) and ($i <= $#types)){\n $i++;\n }\n if (($i > $#types) and ($t !~ /^(timesharing|inner)/)){\n die(\"[ADMISSION RULE] The job type $t is not handled by OAR; Right values are : @types\\n\");\n }\n}\n'),(14,'\nforeach my $mold (@{$ref_resource_list}){\n foreach my $r (@{$mold->[0]}){\n my $prop = $r->{property};\n if (($prop !~ /[\\s\\(]type[\\s=]/) and ($prop !~ /^type[\\s=]/)){\n if (!defined($prop)){\n $r->{property} = \"type = \\\'default\\\'\";\n }else{\n $r->{property} = \"($r->{property}) AND type = \\\'default\\\'\";\n }\n }\n }\n}\nprint(\"[ADMISSION RULE] Modify resource description with type constraints\\n\");\n'); /*!40000 ALTER TABLE `admission_rules` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `assigned_resources` -- DROP TABLE IF EXISTS `assigned_resources`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `assigned_resources` ( `moldable_job_id` int(10) unsigned NOT NULL, `resource_id` int(10) unsigned NOT NULL, `assigned_resource_index` enum('CURRENT','LOG') NOT NULL default 'CURRENT', PRIMARY KEY (`moldable_job_id`,`resource_id`), KEY `mjob_id` (`moldable_job_id`), KEY `log` (`assigned_resource_index`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `assigned_resources` -- LOCK TABLES `assigned_resources` WRITE; /*!40000 ALTER TABLE `assigned_resources` DISABLE KEYS */; /*!40000 ALTER TABLE `assigned_resources` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `challenges` -- DROP TABLE IF EXISTS `challenges`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `challenges` ( `job_id` int(10) unsigned NOT NULL, `challenge` varchar(255) NOT NULL, `ssh_private_key` text NOT NULL, `ssh_public_key` text NOT NULL, PRIMARY KEY (`job_id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `challenges` -- LOCK TABLES `challenges` WRITE; /*!40000 ALTER TABLE `challenges` DISABLE KEYS */; INSERT INTO `challenges` VALUES (1,'841021991175','',''); /*!40000 ALTER TABLE `challenges` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `event_log_hostnames` -- DROP TABLE IF EXISTS `event_log_hostnames`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `event_log_hostnames` ( `event_id` int(10) unsigned NOT NULL, `hostname` varchar(255) NOT NULL, PRIMARY KEY (`event_id`,`hostname`), KEY `event_hostname` (`hostname`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `event_log_hostnames` -- LOCK TABLES `event_log_hostnames` WRITE; /*!40000 ALTER TABLE `event_log_hostnames` DISABLE KEYS */; /*!40000 ALTER TABLE `event_log_hostnames` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `event_logs` -- DROP TABLE IF EXISTS `event_logs`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `event_logs` ( `event_id` int(10) unsigned NOT NULL auto_increment, `type` varchar(50) NOT NULL, `job_id` int(10) unsigned NOT NULL, `date` int(10) unsigned NOT NULL, `description` varchar(255) NOT NULL, `to_check` enum('YES','NO') NOT NULL default 'YES', PRIMARY KEY (`event_id`), KEY `event_type` (`type`), KEY `event_check` (`to_check`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `event_logs` -- LOCK TABLES `event_logs` WRITE; /*!40000 ALTER TABLE `event_logs` DISABLE KEYS */; /*!40000 ALTER TABLE `event_logs` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `files` -- DROP TABLE IF EXISTS `files`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `files` ( `file_id` int(10) unsigned NOT NULL auto_increment, `md5sum` varchar(255) default NULL, `location` varchar(255) default NULL, `method` varchar(255) default NULL, `compression` varchar(255) default NULL, `size` int(10) unsigned NOT NULL, PRIMARY KEY (`file_id`), KEY `md5sum` (`md5sum`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `files` -- LOCK TABLES `files` WRITE; /*!40000 ALTER TABLE `files` DISABLE KEYS */; /*!40000 ALTER TABLE `files` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `frag_jobs` -- DROP TABLE IF EXISTS `frag_jobs`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `frag_jobs` ( `frag_id_job` int(10) unsigned NOT NULL, `frag_date` int(10) unsigned NOT NULL, `frag_state` enum('LEON','TIMER_ARMED','LEON_EXTERMINATE','FRAGGED') NOT NULL default 'LEON', PRIMARY KEY (`frag_id_job`), KEY `frag_state` (`frag_state`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `frag_jobs` -- LOCK TABLES `frag_jobs` WRITE; /*!40000 ALTER TABLE `frag_jobs` DISABLE KEYS */; /*!40000 ALTER TABLE `frag_jobs` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `gantt_jobs_predictions` -- DROP TABLE IF EXISTS `gantt_jobs_predictions`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `gantt_jobs_predictions` ( `moldable_job_id` int(10) unsigned NOT NULL, `start_time` int(10) unsigned NOT NULL, PRIMARY KEY (`moldable_job_id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `gantt_jobs_predictions` -- LOCK TABLES `gantt_jobs_predictions` WRITE; /*!40000 ALTER TABLE `gantt_jobs_predictions` DISABLE KEYS */; INSERT INTO `gantt_jobs_predictions` VALUES (0,1223567271),(1,1223575200); /*!40000 ALTER TABLE `gantt_jobs_predictions` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `gantt_jobs_predictions_log` -- DROP TABLE IF EXISTS `gantt_jobs_predictions_log`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `gantt_jobs_predictions_log` ( `sched_date` int(10) unsigned NOT NULL, `moldable_job_id` int(10) unsigned NOT NULL, `start_time` int(10) unsigned NOT NULL, PRIMARY KEY (`sched_date`,`moldable_job_id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `gantt_jobs_predictions_log` -- LOCK TABLES `gantt_jobs_predictions_log` WRITE; /*!40000 ALTER TABLE `gantt_jobs_predictions_log` DISABLE KEYS */; /*!40000 ALTER TABLE `gantt_jobs_predictions_log` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `gantt_jobs_predictions_visu` -- DROP TABLE IF EXISTS `gantt_jobs_predictions_visu`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `gantt_jobs_predictions_visu` ( `moldable_job_id` int(10) unsigned NOT NULL, `start_time` int(10) unsigned NOT NULL, PRIMARY KEY (`moldable_job_id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `gantt_jobs_predictions_visu` -- LOCK TABLES `gantt_jobs_predictions_visu` WRITE; /*!40000 ALTER TABLE `gantt_jobs_predictions_visu` DISABLE KEYS */; INSERT INTO `gantt_jobs_predictions_visu` VALUES (0,1223567271),(1,1223575200); /*!40000 ALTER TABLE `gantt_jobs_predictions_visu` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `gantt_jobs_resources` -- DROP TABLE IF EXISTS `gantt_jobs_resources`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `gantt_jobs_resources` ( `moldable_job_id` int(10) unsigned NOT NULL, `resource_id` int(10) unsigned NOT NULL, PRIMARY KEY (`moldable_job_id`,`resource_id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `gantt_jobs_resources` -- LOCK TABLES `gantt_jobs_resources` WRITE; /*!40000 ALTER TABLE `gantt_jobs_resources` DISABLE KEYS */; INSERT INTO `gantt_jobs_resources` VALUES (1,1),(1,2),(1,3); /*!40000 ALTER TABLE `gantt_jobs_resources` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `gantt_jobs_resources_log` -- DROP TABLE IF EXISTS `gantt_jobs_resources_log`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `gantt_jobs_resources_log` ( `sched_date` int(10) unsigned NOT NULL, `moldable_job_id` int(10) unsigned NOT NULL, `resource_id` int(10) unsigned NOT NULL, PRIMARY KEY (`sched_date`,`moldable_job_id`,`resource_id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `gantt_jobs_resources_log` -- LOCK TABLES `gantt_jobs_resources_log` WRITE; /*!40000 ALTER TABLE `gantt_jobs_resources_log` DISABLE KEYS */; /*!40000 ALTER TABLE `gantt_jobs_resources_log` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `gantt_jobs_resources_visu` -- DROP TABLE IF EXISTS `gantt_jobs_resources_visu`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `gantt_jobs_resources_visu` ( `moldable_job_id` int(10) unsigned NOT NULL, `resource_id` int(10) unsigned NOT NULL, PRIMARY KEY (`moldable_job_id`,`resource_id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `gantt_jobs_resources_visu` -- LOCK TABLES `gantt_jobs_resources_visu` WRITE; /*!40000 ALTER TABLE `gantt_jobs_resources_visu` DISABLE KEYS */; INSERT INTO `gantt_jobs_resources_visu` VALUES (1,1),(1,2),(1,3); /*!40000 ALTER TABLE `gantt_jobs_resources_visu` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `job_dependencies` -- DROP TABLE IF EXISTS `job_dependencies`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `job_dependencies` ( `job_id` int(10) unsigned NOT NULL, `job_id_required` int(10) unsigned NOT NULL, `job_dependency_index` enum('CURRENT','LOG') NOT NULL default 'CURRENT', PRIMARY KEY (`job_id`,`job_id_required`), KEY `id` (`job_id`), KEY `log` (`job_dependency_index`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `job_dependencies` -- LOCK TABLES `job_dependencies` WRITE; /*!40000 ALTER TABLE `job_dependencies` DISABLE KEYS */; /*!40000 ALTER TABLE `job_dependencies` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `job_resource_descriptions` -- DROP TABLE IF EXISTS `job_resource_descriptions`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `job_resource_descriptions` ( `res_job_group_id` int(10) unsigned NOT NULL, `res_job_resource_type` varchar(255) NOT NULL, `res_job_value` int(11) NOT NULL, `res_job_order` int(10) unsigned NOT NULL default '0', `res_job_index` enum('CURRENT','LOG') NOT NULL default 'CURRENT', PRIMARY KEY (`res_job_group_id`,`res_job_resource_type`,`res_job_order`), KEY `resgroup` (`res_job_group_id`), KEY `log` (`res_job_index`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `job_resource_descriptions` -- LOCK TABLES `job_resource_descriptions` WRITE; /*!40000 ALTER TABLE `job_resource_descriptions` DISABLE KEYS */; INSERT INTO `job_resource_descriptions` VALUES (1,'resource_id',3,0,'CURRENT'); /*!40000 ALTER TABLE `job_resource_descriptions` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `job_resource_groups` -- DROP TABLE IF EXISTS `job_resource_groups`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `job_resource_groups` ( `res_group_id` int(10) unsigned NOT NULL auto_increment, `res_group_moldable_id` int(10) unsigned NOT NULL, `res_group_property` text, `res_group_index` enum('CURRENT','LOG') NOT NULL default 'CURRENT', PRIMARY KEY (`res_group_id`), KEY `moldable_job` (`res_group_moldable_id`), KEY `log` (`res_group_index`) ) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `job_resource_groups` -- LOCK TABLES `job_resource_groups` WRITE; /*!40000 ALTER TABLE `job_resource_groups` DISABLE KEYS */; INSERT INTO `job_resource_groups` VALUES (1,1,'type = \'default\'','CURRENT'); /*!40000 ALTER TABLE `job_resource_groups` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `job_state_logs` -- DROP TABLE IF EXISTS `job_state_logs`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `job_state_logs` ( `job_state_log_id` int(10) unsigned NOT NULL auto_increment, `job_id` int(10) unsigned NOT NULL, `job_state` enum('Waiting','Hold','toLaunch','toError','toAckReservation','Launching','Finishing','Running','Suspended','Resuming','Terminated','Error') NOT NULL, `date_start` int(10) unsigned NOT NULL, `date_stop` int(10) unsigned default '0', PRIMARY KEY (`job_state_log_id`), KEY `id` (`job_id`), KEY `state` (`job_state`) ) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `job_state_logs` -- LOCK TABLES `job_state_logs` WRITE; /*!40000 ALTER TABLE `job_state_logs` DISABLE KEYS */; INSERT INTO `job_state_logs` VALUES (1,1,'Waiting',1223567218,1223567219),(2,1,'toAckReservation',1223567219,1223567219),(3,1,'Waiting',1223567219,0); /*!40000 ALTER TABLE `job_state_logs` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `job_types` -- DROP TABLE IF EXISTS `job_types`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `job_types` ( `job_type_id` int(10) unsigned NOT NULL auto_increment, `job_id` int(10) unsigned NOT NULL, `type` varchar(255) NOT NULL, `types_index` enum('CURRENT','LOG') NOT NULL default 'CURRENT', PRIMARY KEY (`job_type_id`), KEY `log` (`types_index`), KEY `type` (`type`), KEY `id_types` (`job_id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `job_types` -- LOCK TABLES `job_types` WRITE; /*!40000 ALTER TABLE `job_types` DISABLE KEYS */; /*!40000 ALTER TABLE `job_types` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `jobs` -- DROP TABLE IF EXISTS `jobs`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `jobs` ( `job_id` int(10) unsigned NOT NULL auto_increment, `initial_request` text, `job_name` varchar(100) default NULL, `job_env` text, `job_type` enum('INTERACTIVE','PASSIVE') NOT NULL default 'PASSIVE', `info_type` varchar(255) default NULL, `state` enum('Waiting','Hold','toLaunch','toError','toAckReservation','Launching','Running','Suspended','Resuming','Finishing','Terminated','Error') NOT NULL, `reservation` enum('None','toSchedule','Scheduled') NOT NULL default 'None', `message` varchar(255) NOT NULL, `scheduler_info` varchar(255) NOT NULL, `job_user` varchar(255) NOT NULL, `project` varchar(255) NOT NULL, `job_group` varchar(255) NOT NULL, `command` text, `exit_code` int(11) default NULL, `queue_name` varchar(100) NOT NULL, `properties` text, `launching_directory` text NOT NULL, `submission_time` int(10) unsigned NOT NULL, `start_time` int(10) unsigned NOT NULL, `stop_time` int(10) unsigned NOT NULL, `file_id` int(10) unsigned default NULL, `accounted` enum('YES','NO') NOT NULL default 'NO', `notify` varchar(255) default NULL, `assigned_moldable_job` int(10) unsigned default '0', `checkpoint` int(10) unsigned NOT NULL default '0', `checkpoint_signal` int(11) NOT NULL, `stdout_file` text, `stderr_file` text, `resubmit_job_id` int(10) unsigned default '0', `suspended` enum('YES','NO') NOT NULL default 'NO', PRIMARY KEY (`job_id`), KEY `state` (`state`), KEY `state_id` (`state`,`job_id`), KEY `reservation` (`reservation`), KEY `queue_name` (`queue_name`), KEY `accounted` (`accounted`), KEY `suspended` (`suspended`) ) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `jobs` -- LOCK TABLES `jobs` WRITE; /*!40000 ALTER TABLE `jobs` DISABLE KEYS */; INSERT INTO `jobs` VALUES (1,'oarsub -r 2008-10-09 20:00:00 -l nodes=3',NULL,NULL,'INTERACTIVE','grelon-41.nancy.grid5000.fr:43469','Waiting','Scheduled','','','pneyron','default','','',NULL,'default','desktop_computing = \'NO\'','/home/grenoble/pneyron',1223567218,1223575200,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'); /*!40000 ALTER TABLE `jobs` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `moldable_job_descriptions` -- DROP TABLE IF EXISTS `moldable_job_descriptions`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `moldable_job_descriptions` ( `moldable_id` int(10) unsigned NOT NULL auto_increment, `moldable_job_id` int(10) unsigned NOT NULL, `moldable_walltime` int(10) unsigned NOT NULL, `moldable_index` enum('CURRENT','LOG') NOT NULL default 'CURRENT', PRIMARY KEY (`moldable_id`), KEY `job` (`moldable_job_id`), KEY `log` (`moldable_index`) ) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `moldable_job_descriptions` -- LOCK TABLES `moldable_job_descriptions` WRITE; /*!40000 ALTER TABLE `moldable_job_descriptions` DISABLE KEYS */; INSERT INTO `moldable_job_descriptions` VALUES (1,1,7200,'CURRENT'); /*!40000 ALTER TABLE `moldable_job_descriptions` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `queues` -- DROP TABLE IF EXISTS `queues`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `queues` ( `queue_name` varchar(100) NOT NULL, `priority` int(10) unsigned NOT NULL, `scheduler_policy` varchar(100) NOT NULL, `state` enum('Active','notActive') NOT NULL default 'Active', PRIMARY KEY (`queue_name`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `queues` -- LOCK TABLES `queues` WRITE; /*!40000 ALTER TABLE `queues` DISABLE KEYS */; INSERT INTO `queues` VALUES ('admin',10,'oar_sched_gantt_with_timesharing','Active'),('default',2,'oar_sched_gantt_with_timesharing','Active'),('besteffort',0,'oar_sched_gantt_with_timesharing','Active'); /*!40000 ALTER TABLE `queues` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `resource_logs` -- DROP TABLE IF EXISTS `resource_logs`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `resource_logs` ( `resource_log_id` int(10) unsigned NOT NULL auto_increment, `resource_id` int(10) unsigned NOT NULL, `attribute` varchar(255) NOT NULL, `value` varchar(255) NOT NULL, `date_start` int(10) unsigned NOT NULL, `date_stop` int(10) unsigned default '0', `finaud_decision` enum('YES','NO') NOT NULL default 'NO', PRIMARY KEY (`resource_log_id`), KEY `resource` (`resource_id`), KEY `attribute` (`attribute`), KEY `finaud` (`finaud_decision`), KEY `val` (`value`) ) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `resource_logs` -- LOCK TABLES `resource_logs` WRITE; /*!40000 ALTER TABLE `resource_logs` DISABLE KEYS */; INSERT INTO `resource_logs` VALUES (1,1,'state','Alive',1223566377,0,'NO'),(2,2,'state','Alive',1223566380,0,'NO'),(3,3,'state','Alive',1223566381,0,'NO'); /*!40000 ALTER TABLE `resource_logs` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `resources` -- DROP TABLE IF EXISTS `resources`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `resources` ( `resource_id` int(10) unsigned NOT NULL auto_increment, `type` varchar(100) NOT NULL default 'default', `network_address` varchar(100) NOT NULL, `state` enum('Alive','Dead','Suspected','Absent') NOT NULL, `next_state` enum('UnChanged','Alive','Dead','Absent','Suspected') NOT NULL default 'UnChanged', `finaud_decision` enum('YES','NO') NOT NULL default 'NO', `next_finaud_decision` enum('YES','NO') NOT NULL default 'NO', `state_num` int(11) NOT NULL default '0', `suspended_jobs` enum('YES','NO') NOT NULL default 'NO', `scheduler_priority` int(10) unsigned NOT NULL default '0', `cpuset` int(10) unsigned NOT NULL default '0', `besteffort` enum('YES','NO') NOT NULL default 'YES', `deploy` enum('YES','NO') NOT NULL default 'NO', `expiry_date` int(10) unsigned NOT NULL, `desktop_computing` enum('YES','NO') NOT NULL default 'NO', `last_job_date` int(10) unsigned default '0', `available_upto` int(10) unsigned NOT NULL default '0', PRIMARY KEY (`resource_id`), KEY `state` (`state`), KEY `next_state` (`next_state`), KEY `suspended_jobs` (`suspended_jobs`), KEY `type` (`type`), KEY `network_address` (`network_address`) ) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `resources` -- LOCK TABLES `resources` WRITE; /*!40000 ALTER TABLE `resources` DISABLE KEYS */; INSERT INTO `resources` VALUES (1,'default','127.0.2.1','Alive','Dead','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0),(2,'default','127.0.2.2','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0),(3,'default','127.0.2.3','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0); /*!40000 ALTER TABLE `resources` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `schema` -- DROP TABLE IF EXISTS `schema`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `schema` ( `version` varchar(255) NOT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `schema` -- LOCK TABLES `schema` WRITE; /*!40000 ALTER TABLE `schema` DISABLE KEYS */; INSERT INTO `schema` VALUES ('2.3.0+svn1369'); /*!40000 ALTER TABLE `schema` ENABLE KEYS */; UNLOCK TABLES; /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; -- Dump completed on 2008-10-09 15:50:05 ./oar-2.5.2/tests/scheduler/data/001_oar.conf0000777000175000017500000000000011757171206020626 2000_oar.confustar plbplb./oar-2.5.2/tests/scheduler/data/001_my.cnf0000777000175000017500000000000011757171206020000 2000_my.cnfustar plbplb./oar-2.5.2/tests/scheduler/data/001_advance_reservation_with_absent_resource.sql0000644000175000017500000007456611757171206026255 0ustar plbplb-- MySQL dump 10.11 -- -- Host: localhost Database: oar -- ------------------------------------------------------ -- Server version 5.0.51a-15 /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; /*!40101 SET NAMES utf8 */; /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; /*!40103 SET TIME_ZONE='+00:00' */; /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; -- -- Table structure for table `accounting` -- DROP TABLE IF EXISTS `accounting`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `accounting` ( `window_start` int(10) unsigned NOT NULL, `window_stop` int(10) unsigned NOT NULL, `accounting_user` varchar(255) NOT NULL, `accounting_project` varchar(255) NOT NULL, `queue_name` varchar(100) NOT NULL, `consumption_type` enum('ASKED','USED') NOT NULL, `consumption` int(10) unsigned NOT NULL, PRIMARY KEY (`window_start`,`window_stop`,`accounting_user`,`accounting_project`,`queue_name`,`consumption_type`), KEY `accounting_user` (`accounting_user`), KEY `accounting_project` (`accounting_project`), KEY `accounting_queue` (`queue_name`), KEY `accounting_type` (`consumption_type`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `accounting` -- LOCK TABLES `accounting` WRITE; /*!40000 ALTER TABLE `accounting` DISABLE KEYS */; /*!40000 ALTER TABLE `accounting` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `admission_rules` -- DROP TABLE IF EXISTS `admission_rules`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `admission_rules` ( `id` int(10) unsigned NOT NULL auto_increment, `rule` text NOT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM AUTO_INCREMENT=15 DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `admission_rules` -- LOCK TABLES `admission_rules` WRITE; /*!40000 ALTER TABLE `admission_rules` DISABLE KEYS */; INSERT INTO `admission_rules` VALUES (1,'if (not defined($queue_name)) {$queue_name=\"default\";}'),(2,'die (\"[ADMISSION RULE] root and oar users are not allowed to submit jobs.\\n\") if ( $user eq \"root\" or $user eq \"oar\" );'),(3,'\nmy $admin_group = \"admin\";\nif ($queue_name eq \"admin\") {\n my $members; \n (undef,undef,undef, $members) = getgrnam($admin_group);\n my %h = map { $_ => 1 } split(/\\s+/,$members);\n if ( $h{$user} ne 1 ) {\n {die(\"[ADMISSION RULE] Only member of the group \".$admin_group.\" can submit jobs in the admin queue\\n\");}\n }\n}\n'),(4,'\nmy @bad_resources = (\"type\",\"state\",\"next_state\",\"finaud_decision\",\"next_finaud_decision\",\"state_num\",\"suspended_jobs\",\"besteffort\",\"deploy\",\"expiry_date\",\"desktop_computing\",\"last_job_date\",\"available_upto\",\"scheduler_priority\");\nforeach my $mold (@{$ref_resource_list}){\n foreach my $r (@{$mold->[0]}){\n my $i = 0;\n while (($i <= $#{$r->{resources}})){\n if (grep(/^$r->{resources}->[$i]->{resource}$/, @bad_resources)){\n die(\"[ADMISSION RULE] \'$r->{resources}->[$i]->{resource}\' resource is not allowed\\n\");\n }\n $i++;\n }\n }\n}\n'),(5,'\nif (grep(/^besteffort$/, @{$type_list}) and not $queue_name eq \"besteffort\"){\n $queue_name = \"besteffort\";\n print(\"[ADMISSION RULE] Automatically redirect in the besteffort queue\\n\");\n}\nif ($queue_name eq \"besteffort\" and not grep(/^besteffort$/, @{$type_list})) {\n push(@{$type_list},\"besteffort\");\n print(\"[ADMISSION RULE] Automatically add the besteffort type\\n\");\n}\nif (grep(/^besteffort$/, @{$type_list})){\n if ($jobproperties ne \"\"){\n $jobproperties = \"($jobproperties) AND besteffort = \\\'YES\\\'\";\n }else{\n $jobproperties = \"besteffort = \\\'YES\\\'\";\n }\n print(\"[ADMISSION RULE] Automatically add the besteffort constraint on the resources\\n\");\n}\n'),(6,'\nif ((grep(/^besteffort$/, @{$type_list})) and ($reservationField ne \"None\")){\n die(\"[ADMISSION RULE] Error: a job cannot both be of type besteffort and be a reservation.\\n\");\n}\n'),(7,'\nif (grep(/^deploy$/, @{$type_list})){\n if ($jobproperties ne \"\"){\n $jobproperties = \"($jobproperties) AND deploy = \\\'YES\\\'\";\n }else{\n $jobproperties = \"deploy = \\\'YES\\\'\";\n }\n}\n'),(8,'\nmy @bad_resources = (\"core\",\"cpu\",\"resource_id\",);\nif (grep(/^(deploy|allow_classic_ssh)$/, @{$type_list})){\n foreach my $mold (@{$ref_resource_list}){\n foreach my $r (@{$mold->[0]}){\n my $i = 0;\n while (($i <= $#{$r->{resources}})){\n if (grep(/^$r->{resources}->[$i]->{resource}$/, @bad_resources)){\n die(\"[ADMISSION RULE] \'$r->{resources}->[$i]->{resource}\' resource is not allowed with a deploy or allow_classic_ssh type job\\n\");\n }\n $i++;\n }\n }\n }\n}\n'),(9,'\nif (grep(/^desktop_computing$/, @{$type_list})){\n print(\"[ADMISSION RULE] Added automatically desktop_computing resource constraints\\n\");\n if ($jobproperties ne \"\"){\n $jobproperties = \"($jobproperties) AND desktop_computing = \\\'YES\\\'\";\n }else{\n $jobproperties = \"desktop_computing = \\\'YES\\\'\";\n }\n}else{\n if ($jobproperties ne \"\"){\n $jobproperties = \"($jobproperties) AND desktop_computing = \\\'NO\\\'\";\n }else{\n $jobproperties = \"desktop_computing = \\\'NO\\\'\";\n }\n}\n'),(10,'\nif ($reservationField eq \"toSchedule\") {\n my $unlimited=0;\n if (open(FILE, \"< $ENV{HOME}/unlimited_reservation.users\")) {\n while (){\n if (m/^\\s*$user\\s*$/m){\n $unlimited=1;\n }\n }\n close(FILE);\n }\n if ($unlimited > 0) {\n print(\"[ADMISSION RULE] $user is granted the privilege to do unlimited reservations\\n\");\n } else {\n my $max_nb_resa = 2;\n my $nb_resa = $dbh->do(\" SELECT job_id\n FROM jobs\n WHERE\n job_user = \\\'$user\\\' AND\n (reservation = \\\'toSchedule\\\' OR\n reservation = \\\'Scheduled\\\') AND\n (state = \\\'Waiting\\\' OR state = \\\'Hold\\\')\n \");\n if ($nb_resa >= $max_nb_resa){\n die(\"[ADMISSION RULE] Error : you cannot have more than $max_nb_resa waiting reservations.\\n\");\n }\n }\n}\n'),(11,'\nmy $max_walltime = OAR::IO::sql_to_duration(\"12:00:00\");\nif (($jobType eq \"INTERACTIVE\") and ($reservationField eq \"None\")){ \n foreach my $mold (@{$ref_resource_list}){\n if ((defined($mold->[1])) and ($max_walltime < $mold->[1])){\n print(\"[ADMISSION RULE] Walltime to big for an INTERACTIVE job so it is set to $max_walltime.\\n\");\n $mold->[1] = $max_walltime;\n }\n }\n}\n'),(12,'\nmy $default_wall = OAR::IO::sql_to_duration(\"2:00:00\");\nforeach my $mold (@{$ref_resource_list}){\n if (!defined($mold->[1])){\n print(\"[ADMISSION RULE] Set default walltime to $default_wall.\\n\");\n $mold->[1] = $default_wall;\n }\n}\n'),(13,'\nmy @types = (\"container\",\"inner\",\"deploy\",\"desktop_computing\",\"besteffort\",\"cosystem\",\"idempotent\",\"timesharing\",\"allow_classic_ssh\");\nforeach my $t (@{$type_list}){\n my $i = 0;\n while (($types[$i] ne $t) and ($i <= $#types)){\n $i++;\n }\n if (($i > $#types) and ($t !~ /^(timesharing|inner)/)){\n die(\"[ADMISSION RULE] The job type $t is not handled by OAR; Right values are : @types\\n\");\n }\n}\n'),(14,'\nforeach my $mold (@{$ref_resource_list}){\n foreach my $r (@{$mold->[0]}){\n my $prop = $r->{property};\n if (($prop !~ /[\\s\\(]type[\\s=]/) and ($prop !~ /^type[\\s=]/)){\n if (!defined($prop)){\n $r->{property} = \"type = \\\'default\\\'\";\n }else{\n $r->{property} = \"($r->{property}) AND type = \\\'default\\\'\";\n }\n }\n }\n}\nprint(\"[ADMISSION RULE] Modify resource description with type constraints\\n\");\n'); /*!40000 ALTER TABLE `admission_rules` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `assigned_resources` -- DROP TABLE IF EXISTS `assigned_resources`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `assigned_resources` ( `moldable_job_id` int(10) unsigned NOT NULL, `resource_id` int(10) unsigned NOT NULL, `assigned_resource_index` enum('CURRENT','LOG') NOT NULL default 'CURRENT', PRIMARY KEY (`moldable_job_id`,`resource_id`), KEY `mjob_id` (`moldable_job_id`), KEY `log` (`assigned_resource_index`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `assigned_resources` -- LOCK TABLES `assigned_resources` WRITE; /*!40000 ALTER TABLE `assigned_resources` DISABLE KEYS */; /*!40000 ALTER TABLE `assigned_resources` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `challenges` -- DROP TABLE IF EXISTS `challenges`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `challenges` ( `job_id` int(10) unsigned NOT NULL, `challenge` varchar(255) NOT NULL, `ssh_private_key` text NOT NULL, `ssh_public_key` text NOT NULL, PRIMARY KEY (`job_id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `challenges` -- LOCK TABLES `challenges` WRITE; /*!40000 ALTER TABLE `challenges` DISABLE KEYS */; INSERT INTO `challenges` VALUES (1,'841021991175','',''); /*!40000 ALTER TABLE `challenges` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `event_log_hostnames` -- DROP TABLE IF EXISTS `event_log_hostnames`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `event_log_hostnames` ( `event_id` int(10) unsigned NOT NULL, `hostname` varchar(255) NOT NULL, PRIMARY KEY (`event_id`,`hostname`), KEY `event_hostname` (`hostname`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `event_log_hostnames` -- LOCK TABLES `event_log_hostnames` WRITE; /*!40000 ALTER TABLE `event_log_hostnames` DISABLE KEYS */; /*!40000 ALTER TABLE `event_log_hostnames` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `event_logs` -- DROP TABLE IF EXISTS `event_logs`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `event_logs` ( `event_id` int(10) unsigned NOT NULL auto_increment, `type` varchar(50) NOT NULL, `job_id` int(10) unsigned NOT NULL, `date` int(10) unsigned NOT NULL, `description` varchar(255) NOT NULL, `to_check` enum('YES','NO') NOT NULL default 'YES', PRIMARY KEY (`event_id`), KEY `event_type` (`type`), KEY `event_check` (`to_check`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `event_logs` -- LOCK TABLES `event_logs` WRITE; /*!40000 ALTER TABLE `event_logs` DISABLE KEYS */; /*!40000 ALTER TABLE `event_logs` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `files` -- DROP TABLE IF EXISTS `files`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `files` ( `file_id` int(10) unsigned NOT NULL auto_increment, `md5sum` varchar(255) default NULL, `location` varchar(255) default NULL, `method` varchar(255) default NULL, `compression` varchar(255) default NULL, `size` int(10) unsigned NOT NULL, PRIMARY KEY (`file_id`), KEY `md5sum` (`md5sum`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `files` -- LOCK TABLES `files` WRITE; /*!40000 ALTER TABLE `files` DISABLE KEYS */; /*!40000 ALTER TABLE `files` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `frag_jobs` -- DROP TABLE IF EXISTS `frag_jobs`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `frag_jobs` ( `frag_id_job` int(10) unsigned NOT NULL, `frag_date` int(10) unsigned NOT NULL, `frag_state` enum('LEON','TIMER_ARMED','LEON_EXTERMINATE','FRAGGED') NOT NULL default 'LEON', PRIMARY KEY (`frag_id_job`), KEY `frag_state` (`frag_state`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `frag_jobs` -- LOCK TABLES `frag_jobs` WRITE; /*!40000 ALTER TABLE `frag_jobs` DISABLE KEYS */; /*!40000 ALTER TABLE `frag_jobs` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `gantt_jobs_predictions` -- DROP TABLE IF EXISTS `gantt_jobs_predictions`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `gantt_jobs_predictions` ( `moldable_job_id` int(10) unsigned NOT NULL, `start_time` int(10) unsigned NOT NULL, PRIMARY KEY (`moldable_job_id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `gantt_jobs_predictions` -- LOCK TABLES `gantt_jobs_predictions` WRITE; /*!40000 ALTER TABLE `gantt_jobs_predictions` DISABLE KEYS */; INSERT INTO `gantt_jobs_predictions` VALUES (0,1223567271),(1,1223575200); /*!40000 ALTER TABLE `gantt_jobs_predictions` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `gantt_jobs_predictions_log` -- DROP TABLE IF EXISTS `gantt_jobs_predictions_log`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `gantt_jobs_predictions_log` ( `sched_date` int(10) unsigned NOT NULL, `moldable_job_id` int(10) unsigned NOT NULL, `start_time` int(10) unsigned NOT NULL, PRIMARY KEY (`sched_date`,`moldable_job_id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `gantt_jobs_predictions_log` -- LOCK TABLES `gantt_jobs_predictions_log` WRITE; /*!40000 ALTER TABLE `gantt_jobs_predictions_log` DISABLE KEYS */; /*!40000 ALTER TABLE `gantt_jobs_predictions_log` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `gantt_jobs_predictions_visu` -- DROP TABLE IF EXISTS `gantt_jobs_predictions_visu`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `gantt_jobs_predictions_visu` ( `moldable_job_id` int(10) unsigned NOT NULL, `start_time` int(10) unsigned NOT NULL, PRIMARY KEY (`moldable_job_id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `gantt_jobs_predictions_visu` -- LOCK TABLES `gantt_jobs_predictions_visu` WRITE; /*!40000 ALTER TABLE `gantt_jobs_predictions_visu` DISABLE KEYS */; INSERT INTO `gantt_jobs_predictions_visu` VALUES (0,1223567271),(1,1223575200); /*!40000 ALTER TABLE `gantt_jobs_predictions_visu` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `gantt_jobs_resources` -- DROP TABLE IF EXISTS `gantt_jobs_resources`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `gantt_jobs_resources` ( `moldable_job_id` int(10) unsigned NOT NULL, `resource_id` int(10) unsigned NOT NULL, PRIMARY KEY (`moldable_job_id`,`resource_id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `gantt_jobs_resources` -- LOCK TABLES `gantt_jobs_resources` WRITE; /*!40000 ALTER TABLE `gantt_jobs_resources` DISABLE KEYS */; INSERT INTO `gantt_jobs_resources` VALUES (1,1),(1,2),(1,3); /*!40000 ALTER TABLE `gantt_jobs_resources` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `gantt_jobs_resources_log` -- DROP TABLE IF EXISTS `gantt_jobs_resources_log`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `gantt_jobs_resources_log` ( `sched_date` int(10) unsigned NOT NULL, `moldable_job_id` int(10) unsigned NOT NULL, `resource_id` int(10) unsigned NOT NULL, PRIMARY KEY (`sched_date`,`moldable_job_id`,`resource_id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `gantt_jobs_resources_log` -- LOCK TABLES `gantt_jobs_resources_log` WRITE; /*!40000 ALTER TABLE `gantt_jobs_resources_log` DISABLE KEYS */; /*!40000 ALTER TABLE `gantt_jobs_resources_log` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `gantt_jobs_resources_visu` -- DROP TABLE IF EXISTS `gantt_jobs_resources_visu`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `gantt_jobs_resources_visu` ( `moldable_job_id` int(10) unsigned NOT NULL, `resource_id` int(10) unsigned NOT NULL, PRIMARY KEY (`moldable_job_id`,`resource_id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `gantt_jobs_resources_visu` -- LOCK TABLES `gantt_jobs_resources_visu` WRITE; /*!40000 ALTER TABLE `gantt_jobs_resources_visu` DISABLE KEYS */; INSERT INTO `gantt_jobs_resources_visu` VALUES (1,1),(1,2),(1,3); /*!40000 ALTER TABLE `gantt_jobs_resources_visu` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `job_dependencies` -- DROP TABLE IF EXISTS `job_dependencies`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `job_dependencies` ( `job_id` int(10) unsigned NOT NULL, `job_id_required` int(10) unsigned NOT NULL, `job_dependency_index` enum('CURRENT','LOG') NOT NULL default 'CURRENT', PRIMARY KEY (`job_id`,`job_id_required`), KEY `id` (`job_id`), KEY `log` (`job_dependency_index`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `job_dependencies` -- LOCK TABLES `job_dependencies` WRITE; /*!40000 ALTER TABLE `job_dependencies` DISABLE KEYS */; /*!40000 ALTER TABLE `job_dependencies` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `job_resource_descriptions` -- DROP TABLE IF EXISTS `job_resource_descriptions`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `job_resource_descriptions` ( `res_job_group_id` int(10) unsigned NOT NULL, `res_job_resource_type` varchar(255) NOT NULL, `res_job_value` int(11) NOT NULL, `res_job_order` int(10) unsigned NOT NULL default '0', `res_job_index` enum('CURRENT','LOG') NOT NULL default 'CURRENT', PRIMARY KEY (`res_job_group_id`,`res_job_resource_type`,`res_job_order`), KEY `resgroup` (`res_job_group_id`), KEY `log` (`res_job_index`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `job_resource_descriptions` -- LOCK TABLES `job_resource_descriptions` WRITE; /*!40000 ALTER TABLE `job_resource_descriptions` DISABLE KEYS */; INSERT INTO `job_resource_descriptions` VALUES (1,'resource_id',3,0,'CURRENT'); /*!40000 ALTER TABLE `job_resource_descriptions` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `job_resource_groups` -- DROP TABLE IF EXISTS `job_resource_groups`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `job_resource_groups` ( `res_group_id` int(10) unsigned NOT NULL auto_increment, `res_group_moldable_id` int(10) unsigned NOT NULL, `res_group_property` text, `res_group_index` enum('CURRENT','LOG') NOT NULL default 'CURRENT', PRIMARY KEY (`res_group_id`), KEY `moldable_job` (`res_group_moldable_id`), KEY `log` (`res_group_index`) ) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `job_resource_groups` -- LOCK TABLES `job_resource_groups` WRITE; /*!40000 ALTER TABLE `job_resource_groups` DISABLE KEYS */; INSERT INTO `job_resource_groups` VALUES (1,1,'type = \'default\'','CURRENT'); /*!40000 ALTER TABLE `job_resource_groups` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `job_state_logs` -- DROP TABLE IF EXISTS `job_state_logs`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `job_state_logs` ( `job_state_log_id` int(10) unsigned NOT NULL auto_increment, `job_id` int(10) unsigned NOT NULL, `job_state` enum('Waiting','Hold','toLaunch','toError','toAckReservation','Launching','Finishing','Running','Suspended','Resuming','Terminated','Error') NOT NULL, `date_start` int(10) unsigned NOT NULL, `date_stop` int(10) unsigned default '0', PRIMARY KEY (`job_state_log_id`), KEY `id` (`job_id`), KEY `state` (`job_state`) ) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `job_state_logs` -- LOCK TABLES `job_state_logs` WRITE; /*!40000 ALTER TABLE `job_state_logs` DISABLE KEYS */; INSERT INTO `job_state_logs` VALUES (1,1,'Waiting',1223567218,1223567219),(2,1,'toAckReservation',1223567219,1223567219),(3,1,'Waiting',1223567219,0); /*!40000 ALTER TABLE `job_state_logs` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `job_types` -- DROP TABLE IF EXISTS `job_types`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `job_types` ( `job_type_id` int(10) unsigned NOT NULL auto_increment, `job_id` int(10) unsigned NOT NULL, `type` varchar(255) NOT NULL, `types_index` enum('CURRENT','LOG') NOT NULL default 'CURRENT', PRIMARY KEY (`job_type_id`), KEY `log` (`types_index`), KEY `type` (`type`), KEY `id_types` (`job_id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `job_types` -- LOCK TABLES `job_types` WRITE; /*!40000 ALTER TABLE `job_types` DISABLE KEYS */; /*!40000 ALTER TABLE `job_types` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `jobs` -- DROP TABLE IF EXISTS `jobs`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `jobs` ( `job_id` int(10) unsigned NOT NULL auto_increment, `initial_request` text, `job_name` varchar(100) default NULL, `job_env` text, `job_type` enum('INTERACTIVE','PASSIVE') NOT NULL default 'PASSIVE', `info_type` varchar(255) default NULL, `state` enum('Waiting','Hold','toLaunch','toError','toAckReservation','Launching','Running','Suspended','Resuming','Finishing','Terminated','Error') NOT NULL, `reservation` enum('None','toSchedule','Scheduled') NOT NULL default 'None', `message` varchar(255) NOT NULL, `scheduler_info` varchar(255) NOT NULL, `job_user` varchar(255) NOT NULL, `project` varchar(255) NOT NULL, `job_group` varchar(255) NOT NULL, `command` text, `exit_code` int(11) default NULL, `queue_name` varchar(100) NOT NULL, `properties` text, `launching_directory` text NOT NULL, `submission_time` int(10) unsigned NOT NULL, `start_time` int(10) unsigned NOT NULL, `stop_time` int(10) unsigned NOT NULL, `file_id` int(10) unsigned default NULL, `accounted` enum('YES','NO') NOT NULL default 'NO', `notify` varchar(255) default NULL, `assigned_moldable_job` int(10) unsigned default '0', `checkpoint` int(10) unsigned NOT NULL default '0', `checkpoint_signal` int(11) NOT NULL, `stdout_file` text, `stderr_file` text, `resubmit_job_id` int(10) unsigned default '0', `suspended` enum('YES','NO') NOT NULL default 'NO', PRIMARY KEY (`job_id`), KEY `state` (`state`), KEY `state_id` (`state`,`job_id`), KEY `reservation` (`reservation`), KEY `queue_name` (`queue_name`), KEY `accounted` (`accounted`), KEY `suspended` (`suspended`) ) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `jobs` -- LOCK TABLES `jobs` WRITE; /*!40000 ALTER TABLE `jobs` DISABLE KEYS */; INSERT INTO `jobs` VALUES (1,'oarsub -r 2008-10-09 20:00:00 -l nodes=3',NULL,NULL,'INTERACTIVE','grelon-41.nancy.grid5000.fr:43469','Waiting','Scheduled','','','pneyron','default','','',NULL,'default','desktop_computing = \'NO\'','/home/grenoble/pneyron',1223567218,1223575200,0,NULL,'NO',NULL,0,0,12,'OAR.%jobid%.stdout','OAR.%jobid%.stderr',0,'NO'); /*!40000 ALTER TABLE `jobs` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `moldable_job_descriptions` -- DROP TABLE IF EXISTS `moldable_job_descriptions`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `moldable_job_descriptions` ( `moldable_id` int(10) unsigned NOT NULL auto_increment, `moldable_job_id` int(10) unsigned NOT NULL, `moldable_walltime` int(10) unsigned NOT NULL, `moldable_index` enum('CURRENT','LOG') NOT NULL default 'CURRENT', PRIMARY KEY (`moldable_id`), KEY `job` (`moldable_job_id`), KEY `log` (`moldable_index`) ) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `moldable_job_descriptions` -- LOCK TABLES `moldable_job_descriptions` WRITE; /*!40000 ALTER TABLE `moldable_job_descriptions` DISABLE KEYS */; INSERT INTO `moldable_job_descriptions` VALUES (1,1,7200,'CURRENT'); /*!40000 ALTER TABLE `moldable_job_descriptions` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `queues` -- DROP TABLE IF EXISTS `queues`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `queues` ( `queue_name` varchar(100) NOT NULL, `priority` int(10) unsigned NOT NULL, `scheduler_policy` varchar(100) NOT NULL, `state` enum('Active','notActive') NOT NULL default 'Active', PRIMARY KEY (`queue_name`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `queues` -- LOCK TABLES `queues` WRITE; /*!40000 ALTER TABLE `queues` DISABLE KEYS */; INSERT INTO `queues` VALUES ('admin',10,'oar_sched_gantt_with_timesharing','Active'),('default',2,'oar_sched_gantt_with_timesharing','Active'),('besteffort',0,'oar_sched_gantt_with_timesharing','Active'); /*!40000 ALTER TABLE `queues` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `resource_logs` -- DROP TABLE IF EXISTS `resource_logs`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `resource_logs` ( `resource_log_id` int(10) unsigned NOT NULL auto_increment, `resource_id` int(10) unsigned NOT NULL, `attribute` varchar(255) NOT NULL, `value` varchar(255) NOT NULL, `date_start` int(10) unsigned NOT NULL, `date_stop` int(10) unsigned default '0', `finaud_decision` enum('YES','NO') NOT NULL default 'NO', PRIMARY KEY (`resource_log_id`), KEY `resource` (`resource_id`), KEY `attribute` (`attribute`), KEY `finaud` (`finaud_decision`), KEY `val` (`value`) ) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `resource_logs` -- LOCK TABLES `resource_logs` WRITE; /*!40000 ALTER TABLE `resource_logs` DISABLE KEYS */; INSERT INTO `resource_logs` VALUES (1,1,'state','Alive',1223566377,0,'NO'),(2,2,'state','Alive',1223566380,0,'NO'),(3,3,'state','Alive',1223566381,0,'NO'); /*!40000 ALTER TABLE `resource_logs` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `resources` -- DROP TABLE IF EXISTS `resources`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `resources` ( `resource_id` int(10) unsigned NOT NULL auto_increment, `type` varchar(100) NOT NULL default 'default', `network_address` varchar(100) NOT NULL, `state` enum('Alive','Dead','Suspected','Absent') NOT NULL, `next_state` enum('UnChanged','Alive','Dead','Absent','Suspected') NOT NULL default 'UnChanged', `finaud_decision` enum('YES','NO') NOT NULL default 'NO', `next_finaud_decision` enum('YES','NO') NOT NULL default 'NO', `state_num` int(11) NOT NULL default '0', `suspended_jobs` enum('YES','NO') NOT NULL default 'NO', `scheduler_priority` int(10) unsigned NOT NULL default '0', `cpuset` int(10) unsigned NOT NULL default '0', `besteffort` enum('YES','NO') NOT NULL default 'YES', `deploy` enum('YES','NO') NOT NULL default 'NO', `expiry_date` int(10) unsigned NOT NULL, `desktop_computing` enum('YES','NO') NOT NULL default 'NO', `last_job_date` int(10) unsigned default '0', `available_upto` int(10) unsigned NOT NULL default '0', PRIMARY KEY (`resource_id`), KEY `state` (`state`), KEY `next_state` (`next_state`), KEY `suspended_jobs` (`suspended_jobs`), KEY `type` (`type`), KEY `network_address` (`network_address`) ) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `resources` -- LOCK TABLES `resources` WRITE; /*!40000 ALTER TABLE `resources` DISABLE KEYS */; INSERT INTO `resources` VALUES (1,'default','127.0.2.1','Alive','Absent','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0),(2,'default','127.0.2.2','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0),(3,'default','127.0.2.3','Alive','UnChanged','NO','NO',1,'NO',0,0,'YES','NO',0,'NO',0,0); /*!40000 ALTER TABLE `resources` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `schema` -- DROP TABLE IF EXISTS `schema`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `schema` ( `version` varchar(255) NOT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Dumping data for table `schema` -- LOCK TABLES `schema` WRITE; /*!40000 ALTER TABLE `schema` DISABLE KEYS */; INSERT INTO `schema` VALUES ('2.3.0+svn1369'); /*!40000 ALTER TABLE `schema` ENABLE KEYS */; UNLOCK TABLES; /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; -- Dump completed on 2008-10-09 15:49:48 ./oar-2.5.2/tests/scheduler/data/000_oar.conf0000644000175000017500000003251111757171206016623 0ustar plbplb# This file must have the bash variable assignment syntax # $Id: oar.conf 1535 2008-07-28 10:35:09Z neyron $ ######################### # General configuration # ############################################################################### # # Database type ("mysql" or "Pg") DB_TYPE="mysql" # DataBase hostname DB_HOSTNAME="127.0.0.1" # Database port DB_PORT="3307" # Database base name DB_BASE_NAME="oar" # DataBase user name DB_BASE_LOGIN="oar" # DataBase user password DB_BASE_PASSWD="oar" # DataBase read only user name #DB_BASE_LOGIN_RO="oar_ro" # DataBase read only user password #DB_BASE_PASSWD_RO="oar_ro" # OAR server hostname SERVER_HOSTNAME="localhost" # OAR server port SERVER_PORT="6666" # when the user does not specify a -l option then oar use this OARSUB_DEFAULT_RESOURCES="/resource_id=1" # force use of job key even if --use-job-key or -k is not set. OARSUB_FORCE_JOB_KEY="no" # OAR log level: 3(debug+warnings+errors), 2(warnings+errors), 1(errors) LOG_LEVEL="3" # If you want to debug oarexec on nodes then affect 1 (only effective if # DETACH_JOB_FROM_SERVER = 1) OAREXEC_DEBUG_MODE="0" # oarexec default temporary directory # This value MUST be the same in all oar.conf on all nodes of the cluster OAR_RUNTIME_DIRECTORY="/var/lib/oar" # OAR log file LOG_FILE="/var/log/oar.log" # OAR Allowed networks # Networks or hosts allowed to submit jobs to OAR and compute nodes may be # specified here ALLOWED_NETWORKS="127.0.0.1/32 0.0.0.0/0" # Specify where we are connected with a job of the deploy type DEPLOY_HOSTNAME="127.0.0.1" # Specify where we are connected with a job of the cosystem type COSYSTEM_HOSTNAME="127.0.0.1" # Specify the database field to use to fill the file on the first node of the # job in $OAR_NODE_FILE (default is 'network_address'). Only resources with # type=default are displayed in this file. #NODE_FILE_DB_FIELD="network_address" # Specify the database field that will be considered to fill the node file used # by the user on the first node of the job. for each different value of this # field then OAR will put 1 line in the node file(by default "cpu"). #NODE_FILE_DB_FIELD_DISTINCT_VALUES="cpu" # If you want to free a process per job on the server you can change this tag # into 1 (you must enable all nodes to connect to SERVER_PORT on the # SERVER_HOSTNAME) DETACH_JOB_FROM_SERVER="0" # Command to use to connect to other nodes (default is "ssh" in the PATH) OPENSSH_CMD="/usr/bin/ssh -p 6667" # Set the timeout value for each ssh connection (default is 120) #OAR_SSH_CONNECTION_TIMEOUT="200" # If you have installed taktuk and want to use it to manage remote # admnistration commands then give the full command path # (with your options except "-m" and "-o"). # You don t also have to give any taktuk command. # (taktuk version must be >= 3.6) #TAKTUK_CMD="/usr/bin/taktuk -s" ############################################################################### ######################################################################## # Pingchecker options: # # How to check if the nodes have a good health or not. This choice is # # directly linked to the Suspected state of the resources. # # By default OAR uses only "ping". it requests no configuration but it # # is not accurate about the state of the nodes and it is slow # ############################################################################### # # Set the frequency for checking Alive and Suspected resources (0 means never) FINAUD_FREQUENCY="300" # Set time after which Suspected resources become Dead (default is 0 and it # means never) #DEAD_SWITCH_TIME="600" # Uncomment only one of the following PINGCHECKER configuration # sentinelle.pl # If you want to use sentinelle.pl then you must use this tag. # (sentinelle.pl is like a "for" of ssh but it adds timeout and window to # avoid overloading the server) # (sentinelle.pl is provided with OAR in the install directory) #PINGCHECKER_SENTINELLE_SCRIPT_COMMAND="/usr/lib/oar/sentinelle.pl -t 5 -w 20" # Taktuk # taktuk may be used to check aliveness of nodes. # Give the arguments of the taktuk command WITHOUT format outputs # (DO NOT use "-o" option). # See TAKTUK_CMD tag in this file to specify the path of the taktuk command #PINGCHECKER_TAKTUK_ARG_COMMAND="-t 3 broadcast exec [ true ]" #PINGCHECKER_TAKTUK_ARG_COMMAND="-t 3 broadcast exec [ oarnodecheckquery ] #PINGCHECKER_TAKTUK_ARG_COMMAND="-t 3 broadcast exec [ /path/on/nodes/to/my/check/script.sh ]" # fping # fping may be used instead of ping to check aliveness of nodes. # uncomment next line to use fping. Give the complete command path. #PINGCHECKER_FPING_COMMAND="/usr/bin/fping -q" # nmap # nmap may be used instead of ping to check aliveness of nodes. # uncomment next line to use nmap. Give the complete command path. # It will test to connect on the ssh port (22) #PINGCHECKER_NMAP_COMMAND="/usr/bin/nmap -p 22 -n -T5" # GENERIC command # a specific script may be used instead of ping to check aliveness of nodes. # uncomment next line and give the complete command path and its arguments. # The script must return bad nodes on STDERR (1 line for a bad node and it must # have exactly the same name that OAR has given in argument of the command) #PINGCHECKER_GENERIC_COMMAND="/path/to/command arg1 arg2" ############################################################################### ###################### # Mail configuration # ############################################################################### # # OAR information may be notified by email to the administror # set accordingly to your configuration and uncomment the next lines to # activate the feature. # (If this tag is right configured then users can use "--notify" option of oarsub # to receive mails about their jobs) #MAIL_SMTP_SERVER="smtp.serveur.com" # You can specify several recipients with a comma between each email address #MAIL_RECIPIENT="user@domain.com" #MAIL_SENDER="oar@domain.com" ############################################################################### ########### # Scripts # ############################################################################### # # Set the timeout for the prologue and epilogue execution on computing nodes #PROLOGUE_EPILOGUE_TIMEOUT="60" # Files to execute before and after each job on the first computing node # (by default nothing is executed) #PROLOGUE_EXEC_FILE="/path/to/prog" #EPILOGUE_EXEC_FILE="/path/to/prog" # Set the timeout for the prologue and epilogue execution on the OAR server #SERVER_PROLOGUE_EPILOGUE_TIMEOUT="60" # Files to execute before and after each job on the OAR server (by default # nothing is executed) #SERVER_PROLOGUE_EXEC_FILE="/path/to/prog" #SERVER_EPILOGUE_EXEC_FILE="/path/to/prog" ######################## # Scheduler parameters # ############################################################################### # # Maximum of seconds used by a scheduler SCHEDULER_TIMEOUT="120" # Time to add between each jobs (for example: time for administration tasks or # time to let computers to reboot) SCHEDULER_JOB_SECURITY_TIME="1" # Minimum time in seconds that can be considered like a hole where a job could # be scheduled in (if you have performance problems, you can try to increase # this) SCHEDULER_GANTT_HOLE_MINIMUM_TIME="300" # You can add an order preference on resources assigned by the # system(SQL ORDER syntax) SCHEDULER_RESOURCE_ORDER="scheduler_priority ASC, suspended_jobs ASC, switch ASC, network_address DESC, resource_id ASC" # If next line is uncommented then OAR will automatically update the value of # "scheduler_priority" field corresponding to the besteffort jobs. # The syntax is field names separated by "/". The value affected to # "scheduler_priority" depends of the position of the field name. SCHEDULER_PRIORITY_HIERARCHY_ORDER="switch/network_address/cpu" # You can specify a type of resources that will be always assigned for each job # (for exemple: enable all jobs to be able to log on the cluster frontales) # For more information, see the FAQ #SCHEDULER_RESOURCES_ALWAYS_ASSIGNED_TYPE="frontal" # This says to the scheduler to treate resources of these types, where there is # a suspended job, like free ones. So some other jobs can be scheduled on these # resources. (list resource types separate with spaces; Default value is # nothing so no other job can be scheduled on suspended job resources) #SCHEDULER_AVAILABLE_SUSPENDED_RESOURCE_TYPE="default licence VLAN" SCHEDULER_AVAILABLE_SUSPENDED_RESOURCE_TYPE="default" # For a debug purpose, scheduler decisions can be logged into the database # Uncomment the next line in order to activate the logging mechanism SCHEDULER_LOG_DECISIONS="no" # Time to wait when a reservation has not got all resources that it has reserved # (some resources may have become Suspected or Absent since the job submission) # before to launch the job on the remaining resources (default is 300s) #RESERVATION_WAITING_RESOURCES_TIMEOUT="300" # Set the granularity of the OAR accounting feature (in seconds) # Used by the oaraccounting command and the # oar_sched_gantt_with_timesharing_and_fairsharing to calculate the timesharing # policy. Default is 1 day (86400s) #ACCOUNTING_WINDOW="86400" ############################################################################### ########################################################################### # If you want to manage starting and stopping node feature. OAR gives you # # this API: # ############################################################################### # # When OAR scheduler wants some nodes to wake up then it launches this command # and puts on its STDIN the list of nodes to wake up (one hostname by line). # The scheduler looks at the available_upto field in the resources table to know # if the node will be started for enough time. #SCHEDULER_NODE_MANAGER_WAKE_UP_CMD="/path/to/the/command with your args" # When OAR considers that some nodes can be shut down, it launches this command # and puts the node list on its STDIN(one hostname by line). #SCHEDULER_NODE_MANAGER_SLEEP_CMD="/path/to/the/command args" #SCHEDULER_NODE_MANAGER_SLEEP_CMD="taktuk -s -f - -t 3 b e t 3 k 9 [ sudo halt ]" #SCHEDULER_NODE_MANAGER_SLEEP_CMD="/usr/local/oar/sentinelle.pl -f - -t 3 -p 'sudo halt'" # Parameters for the scheduler to decide when a node is idle. # Number of seconds since the last job was terminated on nodes #SCHEDULER_NODE_MANAGER_IDLE_TIME="600" # Parameters for the scheduler to decide if a node will have enough time to sleep. # Number of seconds before the next job #SCHEDULER_NODE_MANAGER_SLEEP_TIME="600" ################################################################################ ############################## # Suspend/Resume job feature # ############################################################################### # # Name of the perl script that manages suspend/resume. # (default is /etc/oar/suspend_resume_manager.pl) #SUSPEND_RESUME_FILE="/etc/oar/suspend_resume_manager.pl" # Files to execute just after a job was suspended and just before a job was resumed #JUST_AFTER_SUSPEND_EXEC_FILE="/path/to/prog" #JUST_BEFORE_RESUME_EXEC_FILE="/path/to/prog" # Timeout for the two previous scripts #SUSPEND_RESUME_SCRIPT_TIMEOUT="60" ############################################################################### ################## # CPUSET feature # ############################################################################### # Indicate the name of the database field that contains the cpu number of the node. # If this option is set then users must use oarsh instead of ssh to walk on # each nodes that they have reserved via oarsub. # Look at Tools/oarsh/README # (if defined, this otion turn on the cpuset feature in OAR) #CPUSET_RESOURCE_PROPERTY_DB_FIELD="cpuset" # Name of the perl script that manages cpuset. # (default is /etc/oar/cpuset_manager.pl which handles the linux kernel cpuset) CPUSET_FILE="/etc/oar/cpuset_manager.pl" # Path of the relative directory where the cpusets will be created on each # nodes(same value than in /proc/self/cpuset) # If you change the default value (/oar) then you have to edit the file # cpuset_manager.pl and change it accordingly. CPUSET_PATH="/oar" ############################################################################### ######### # OARSH # ############################################################################### # # This variable must be set to enable the use of oarsh from a frontale node # Otherwise you must not set this variable if you are not on a frontale #OARSH_OARSTAT_CMD="/usr/bin/oarstat" # The following variable adds options to ssh (or OPENSSH_CMD if configured). # If one option is not handled by your ssh version just remove it BUT be # careful because these options are there for security reasons OARSH_OPENSSH_DEFAULT_OPTIONS="-oProxyCommand=none -oPermitLocalCommand=no" # If you set this variable to something different from 0 then oarsh will act # like a normal ssh **without** CPUSET restriction. # WARNING: this is a critical functionality (this is only useful if users want # to have a command to connect on every nodes without taking care of there ssh # configuration and act like a ssh) #OARSH_BYPASS_WHOLE_SECURITY="0" ############################################################################### #DESKTOP_COMPUTING_ALLOW_CREATE_NODE="0" #DESKTOP_COMPUTING_EXPIRY="300" #STAGEOUT_DIR="/var/lib/oar/stageouts/" #STAGEIN_DIR="/var/lib/oar/stageins" #STAGEIN_CACHE_EXPIRY="144" ./oar-2.5.2/tests/scheduler/data/000_mysql_structure.sql0000644000175000017500000002520611757171206021204 0ustar plbplb# $Id: mysql_structure.sql 1416 2008-05-23 16:19:56Z neyron $ # Creation de la base de donnees #CREATE DATABASE IF NOT EXISTS oar; # Creation de l utilisateur oar #CONNECT mysql; #INSERT INTO user (Host,User,Password) VALUES('localhost','oar',PASSWORD('oar')); #INSERT INTO user (Host,User,Password) VALUES('%','oar',PASSWORD('oar')); #INSERT INTO db (Host,Db,User,Select_priv,Insert_priv,Update_priv,Delete_priv, Create_priv,Drop_priv) VALUES # ('localhost','oar','oar','Y','Y','Y','Y','Y','Y'); #INSERT INTO db (Host,Db,User,Select_priv,Insert_priv,Update_priv,Delete_priv, Create_priv,Drop_priv) VALUES # ('%','oar','oar','Y','Y','Y','Y','Y','Y'); #FLUSH PRIVILEGES; #GRANT ALL ON oar.* TO oar@localhost; #GRANT ALL ON oar.* TO oar@"%"; #GRANT SELECT ON oar.* TO oarreader@localhost; #GRANT SELECT ON oar.* TO oarreader@"%"; #FLUSH PRIVILEGES; #CONNECT oar; # Creation des tables dans la base de donnees oar # schema version, change here if you have updated the db schema CREATE TABLE IF NOT EXISTS `schema` ( version VARCHAR( 255 ) NOT NULL ); INSERT INTO `schema` VALUES ('2.3.0+svn1369'); #DROP TABLE IF EXISTS jobs; CREATE TABLE IF NOT EXISTS jobs ( job_id INT UNSIGNED NOT NULL AUTO_INCREMENT, initial_request TEXT, job_name VARCHAR( 100 ) , job_env TEXT , job_type ENUM('INTERACTIVE','PASSIVE') DEFAULT 'PASSIVE' NOT NULL , info_type VARCHAR( 255 ) , state ENUM('Waiting','Hold','toLaunch','toError','toAckReservation','Launching','Running','Suspended','Resuming','Finishing','Terminated','Error') NOT NULL , reservation ENUM('None','toSchedule','Scheduled') DEFAULT 'None' NOT NULL , message VARCHAR( 255 ) NOT NULL , scheduler_info VARCHAR( 255 ) NOT NULL , job_user VARCHAR( 255 ) NOT NULL , project VARCHAR( 255 ) NOT NULL , job_group VARCHAR( 255 ) NOT NULL , command TEXT , exit_code INT DEFAULT NULL , queue_name VARCHAR( 100 ) NOT NULL , properties TEXT , launching_directory TEXT NOT NULL , submission_time INT UNSIGNED NOT NULL , start_time INT UNSIGNED NOT NULL , stop_time INT UNSIGNED NOT NULL , file_id INT UNSIGNED, accounted ENUM("YES","NO") NOT NULL DEFAULT "NO" , notify VARCHAR( 255 ) DEFAULT NULL , assigned_moldable_job INT UNSIGNED DEFAULT 0 , checkpoint INT UNSIGNED NOT NULL DEFAULT 0 , checkpoint_signal INT NOT NULL, stdout_file TEXT , stderr_file TEXT , resubmit_job_id INT UNSIGNED DEFAULT 0, suspended ENUM("YES","NO") NOT NULL DEFAULT "NO" , INDEX state (state), INDEX state_id (state,job_id), INDEX reservation (reservation), INDEX queue_name (queue_name), INDEX accounted (accounted), INDEX suspended (suspended), PRIMARY KEY (job_id) ); #DROP TABLE IF EXISTS job_types; CREATE TABLE IF NOT EXISTS job_types ( job_type_id INT UNSIGNED NOT NULL AUTO_INCREMENT, job_id INT UNSIGNED NOT NULL , type VARCHAR(255) NOT NULL , types_index ENUM('CURRENT','LOG') DEFAULT 'CURRENT' NOT NULL , INDEX log (types_index), INDEX type (type), INDEX id_types (job_id), PRIMARY KEY (job_type_id) ); #DROP TABLE IF EXISTS challenges; CREATE TABLE IF NOT EXISTS challenges ( job_id INT UNSIGNED NOT NULL , challenge VARCHAR(255) NOT NULL , ssh_private_key TEXT NOT NULL DEFAULT "" , ssh_public_key TEXT NOT NULL DEFAULT "" , PRIMARY KEY (job_id) ); #DROP TABLE IF EXISTS moldable_job_descriptions; CREATE TABLE IF NOT EXISTS moldable_job_descriptions ( moldable_id INT UNSIGNED NOT NULL AUTO_INCREMENT, moldable_job_id INT UNSIGNED NOT NULL , moldable_walltime INT UNSIGNED NOT NULL , moldable_index ENUM('CURRENT','LOG') DEFAULT 'CURRENT' NOT NULL , INDEX job (moldable_job_id) , INDEX log (moldable_index) , PRIMARY KEY (moldable_id) ); #DROP TABLE IF EXISTS job_resource_groups; CREATE TABLE IF NOT EXISTS job_resource_groups ( res_group_id INT UNSIGNED NOT NULL AUTO_INCREMENT , res_group_moldable_id INT UNSIGNED NOT NULL , res_group_property TEXT , res_group_index ENUM('CURRENT','LOG') DEFAULT 'CURRENT' NOT NULL , INDEX moldable_job (res_group_moldable_id), INDEX log (res_group_index) , PRIMARY KEY (res_group_id) ); #DROP TABLE IF EXISTS job_resource_descriptions; CREATE TABLE IF NOT EXISTS job_resource_descriptions ( res_job_group_id INT UNSIGNED NOT NULL, res_job_resource_type VARCHAR(255) NOT NULL, res_job_value INT NOT NULL, res_job_order INT UNSIGNED NOT NULL DEFAULT 0, res_job_index ENUM('CURRENT','LOG') DEFAULT 'CURRENT' NOT NULL , INDEX resgroup (res_job_group_id), INDEX log (res_job_index) , PRIMARY KEY (res_job_group_id,res_job_resource_type,res_job_order) ); #DROP TABLE IF EXISTS job_state_logs; CREATE TABLE IF NOT EXISTS job_state_logs ( job_state_log_id INT UNSIGNED NOT NULL AUTO_INCREMENT, job_id INT UNSIGNED NOT NULL , job_state ENUM('Waiting','Hold','toLaunch','toError','toAckReservation','Launching','Finishing','Running','Suspended','Resuming','Terminated','Error') NOT NULL , date_start INT UNSIGNED NOT NULL, date_stop INT UNSIGNED DEFAULT 0, INDEX id (job_id), INDEX state (job_state), PRIMARY KEY (job_state_log_id) ); #DROP TABLE IF EXISTS frag_jobs; CREATE TABLE IF NOT EXISTS frag_jobs ( frag_id_job INT UNSIGNED NOT NULL , frag_date INT UNSIGNED NOT NULL , frag_state ENUM('LEON','TIMER_ARMED','LEON_EXTERMINATE','FRAGGED') DEFAULT 'LEON' NOT NULL , INDEX frag_state (frag_state), PRIMARY KEY (frag_id_job) ); #DROP TABLE IF EXISTS assigned_resources; CREATE TABLE IF NOT EXISTS assigned_resources ( moldable_job_id INT UNSIGNED NOT NULL , resource_id INT UNSIGNED NOT NULL , assigned_resource_index ENUM('CURRENT','LOG') DEFAULT 'CURRENT' NOT NULL , INDEX mjob_id (moldable_job_id), INDEX log (assigned_resource_index), PRIMARY KEY (moldable_job_id,resource_id) ); #DROP TABLE IF EXISTS resources; CREATE TABLE IF NOT EXISTS resources ( resource_id INT UNSIGNED NOT NULL AUTO_INCREMENT, type VARCHAR( 100 ) NOT NULL DEFAULT "default" , network_address VARCHAR( 100 ) NOT NULL , state ENUM('Alive','Dead','Suspected','Absent') NOT NULL , next_state ENUM('UnChanged','Alive','Dead','Absent','Suspected') DEFAULT 'UnChanged' NOT NULL , finaud_decision ENUM('YES','NO') DEFAULT 'NO' NOT NULL , next_finaud_decision ENUM('YES','NO') DEFAULT 'NO' NOT NULL , state_num INT NOT NULL DEFAULT 0 , suspended_jobs ENUM('YES','NO') DEFAULT 'NO' NOT NULL , scheduler_priority INT UNSIGNED NOT NULL DEFAULT 0 , cpuset INT UNSIGNED NOT NULL DEFAULT 0 , besteffort ENUM('YES','NO') DEFAULT 'YES' NOT NULL , deploy ENUM('YES','NO') DEFAULT 'NO' NOT NULL , expiry_date INT UNSIGNED NOT NULL , desktop_computing ENUM('YES','NO') DEFAULT 'NO' NOT NULL, last_job_date INT UNSIGNED DEFAULT 0, available_upto INT UNSIGNED DEFAULT 0 NOT NULL , INDEX state (state), INDEX next_state (next_state), INDEX suspended_jobs (suspended_jobs), INDEX type (type), INDEX network_address (network_address), PRIMARY KEY (resource_id) ); #DROP TABLE IF EXISTS resource_logs; CREATE TABLE IF NOT EXISTS resource_logs ( resource_log_id INT UNSIGNED NOT NULL AUTO_INCREMENT, resource_id INT UNSIGNED NOT NULL , attribute VARCHAR( 255 ) NOT NULL , value VARCHAR( 255 ) NOT NULL , date_start INT UNSIGNED NOT NULL, date_stop INT UNSIGNED DEFAULT 0 , finaud_decision ENUM('YES','NO') DEFAULT 'NO' NOT NULL , INDEX resource (resource_id), INDEX attribute (attribute), INDEX finaud (finaud_decision), INDEX val (value), PRIMARY KEY (resource_log_id) ); #DROP TABLE IF EXISTS queues; CREATE TABLE IF NOT EXISTS queues ( queue_name VARCHAR( 100 ) NOT NULL , priority INT UNSIGNED NOT NULL , scheduler_policy VARCHAR( 100 ) NOT NULL , state ENUM('Active','notActive') NOT NULL DEFAULT 'Active', PRIMARY KEY (queue_name) ); #DROP TABLE IF EXISTS admission_rules; CREATE TABLE IF NOT EXISTS admission_rules ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, rule TEXT NOT NULL, PRIMARY KEY (id) ); #DROP TABLE IF EXISTS gantt_jobs_predictions; CREATE TABLE IF NOT EXISTS gantt_jobs_predictions ( moldable_job_id INT UNSIGNED NOT NULL , start_time INT UNSIGNED NOT NULL , PRIMARY KEY (moldable_job_id) ); #DROP TABLE IF EXISTS gantt_jobs_predictions_visu; CREATE TABLE IF NOT EXISTS gantt_jobs_predictions_visu ( moldable_job_id INT UNSIGNED NOT NULL , start_time INT UNSIGNED NOT NULL , PRIMARY KEY (moldable_job_id) ); #DROP TABLE IF EXISTS gantt_jobs_predictions_log; CREATE TABLE IF NOT EXISTS gantt_jobs_predictions_log ( sched_date INT UNSIGNED NOT NULL , moldable_job_id INT UNSIGNED NOT NULL , start_time INT UNSIGNED NOT NULL , PRIMARY KEY (sched_date,moldable_job_id) ); #DROP TABLE IF EXISTS gantt_jobs_resources; CREATE TABLE IF NOT EXISTS gantt_jobs_resources ( moldable_job_id INT UNSIGNED NOT NULL , resource_id INT UNSIGNED NOT NULL , PRIMARY KEY (moldable_job_id,resource_id) ); #DROP TABLE IF EXISTS gantt_jobs_resources_visu; CREATE TABLE IF NOT EXISTS gantt_jobs_resources_visu ( moldable_job_id INT UNSIGNED NOT NULL , resource_id INT UNSIGNED NOT NULL , PRIMARY KEY (moldable_job_id,resource_id) ); #DROP TABLE IF EXISTS gantt_jobs_resources_log; CREATE TABLE IF NOT EXISTS gantt_jobs_resources_log ( sched_date INT UNSIGNED NOT NULL , moldable_job_id INT UNSIGNED NOT NULL , resource_id INT UNSIGNED NOT NULL , PRIMARY KEY (sched_date,moldable_job_id,resource_id) ); #DROP TABLE IF EXISTS files; CREATE TABLE IF NOT EXISTS files ( file_id INT UNSIGNED NOT NULL AUTO_INCREMENT, md5sum VARCHAR( 255 ) , location VARCHAR( 255 ) , method VARCHAR( 255 ) , compression VARCHAR( 255 ) , size INT UNSIGNED NOT NULL , INDEX md5sum (md5sum), PRIMARY KEY (file_id) ); #DROP TABLE IF EXISTS event_logs; CREATE TABLE IF NOT EXISTS event_logs ( event_id INT UNSIGNED NOT NULL AUTO_INCREMENT, type VARCHAR(50) NOT NULL, job_id INT UNSIGNED NOT NULL , date INT UNSIGNED NOT NULL , description VARCHAR(255) NOT NULL, to_check ENUM('YES','NO') NOT NULL DEFAULT 'YES', INDEX event_type (type), INDEX event_check (to_check), PRIMARY KEY (event_id) ); #DROP TABLE IF EXISTS event_log_hostnames; CREATE TABLE IF NOT EXISTS event_log_hostnames ( event_id INT UNSIGNED NOT NULL, hostname VARCHAR( 255 ) NOT NULL , INDEX event_hostname (hostname), PRIMARY KEY (event_id, hostname) ); #DROP TABLE IF EXISTS accounting; CREATE TABLE IF NOT EXISTS accounting ( window_start INT UNSIGNED NOT NULL , window_stop INT UNSIGNED NOT NULL , accounting_user VARCHAR( 255 ) NOT NULL , accounting_project VARCHAR( 255 ) NOT NULL , queue_name VARCHAR( 100 ) NOT NULL , consumption_type ENUM("ASKED","USED") NOT NULL , consumption INT UNSIGNED NOT NULL , INDEX accounting_user (accounting_user), INDEX accounting_project (accounting_project), INDEX accounting_queue (queue_name), INDEX accounting_type (consumption_type), PRIMARY KEY (window_start,window_stop,accounting_user,accounting_project,queue_name,consumption_type) ); #DROP TABLE IF EXISTS job_dependencies; CREATE TABLE IF NOT EXISTS job_dependencies ( job_id INT UNSIGNED NOT NULL , job_id_required INT UNSIGNED NOT NULL, job_dependency_index ENUM('CURRENT','LOG') DEFAULT 'CURRENT' NOT NULL , INDEX id (job_id), INDEX log (job_dependency_index), PRIMARY KEY (job_id,job_id_required) ); ./oar-2.5.2/tests/scheduler/data/000_mysql_default_admission_rules.sql0000644000175000017500000002032511757171206024045 0ustar plbplb# Default admission rules for OAR 2 # $Id: mysql_default_admission_rules.sql 1680 2008-09-25 17:22:25Z neyron $ DROP TABLE IF EXISTS admission_rules; CREATE TABLE IF NOT EXISTS admission_rules ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, rule TEXT NOT NULL, PRIMARY KEY (id) ); # Default admission rules # Specify the default value for queue parameter INSERT IGNORE INTO admission_rules (rule) VALUES ('if (not defined($queue_name)) {$queue_name="default";}'); # Prevent root and oar to submit jobs. INSERT IGNORE INTO admission_rules (rule) VALUES ('die ("[ADMISSION RULE] root and oar users are not allowed to submit jobs.\\n") if ( $user eq "root" or $user eq "oar" );'); # Avoid users except admin to go in the admin queue INSERT IGNORE INTO admission_rules (rule) VALUES (' my $admin_group = "admin"; if ($queue_name eq "admin") { my $members; (undef,undef,undef, $members) = getgrnam($admin_group); my %h = map { $_ => 1 } split(/\\s+/,$members); if ( $h{$user} ne 1 ) { {die("[ADMISSION RULE] Only member of the group ".$admin_group." can submit jobs in the admin queue\\n");} } } '); # Prevent the use of system properties INSERT IGNORE INTO admission_rules (rule) VALUES (' my @bad_resources = ("type","state","next_state","finaud_decision","next_finaud_decision","state_num","suspended_jobs","besteffort","deploy","expiry_date","desktop_computing","last_job_date","available_upto","scheduler_priority"); foreach my $mold (@{$ref_resource_list}){ foreach my $r (@{$mold->[0]}){ my $i = 0; while (($i <= $#{$r->{resources}})){ if (grep(/^$r->{resources}->[$i]->{resource}$/i, @bad_resources)){ die("[ADMISSION RULE] \'$r->{resources}->[$i]->{resource}\' resource is not allowed\\n"); } $i++; } } } '); # Force besteffort jobs to run in the besteffort queue # Force job of the besteffort queue to be of the besteffort type # Force besteffort jobs to run on nodes with the besteffort property INSERT IGNORE INTO admission_rules (rule) VALUES (' if (grep(/^besteffort$/, @{$type_list}) and not $queue_name eq "besteffort"){ $queue_name = "besteffort"; print("[ADMISSION RULE] Automatically redirect in the besteffort queue\\n"); } if ($queue_name eq "besteffort" and not grep(/^besteffort$/, @{$type_list})) { push(@{$type_list},"besteffort"); print("[ADMISSION RULE] Automatically add the besteffort type\\n"); } if (grep(/^besteffort$/, @{$type_list})){ if ($jobproperties ne ""){ $jobproperties = "($jobproperties) AND besteffort = \\\'YES\\\'"; }else{ $jobproperties = "besteffort = \\\'YES\\\'"; } print("[ADMISSION RULE] Automatically add the besteffort constraint on the resources\\n"); } '); # Verify if besteffort jobs are not reservations INSERT IGNORE INTO admission_rules (rule) VALUES (' if ((grep(/^besteffort$/, @{$type_list})) and ($reservationField ne "None")){ die("[ADMISSION RULE] Error: a job cannot both be of type besteffort and be a reservation.\\n"); } '); # Force deploy jobs to go on resources with the deploy property INSERT IGNORE INTO admission_rules (rule) VALUES (' if (grep(/^deploy$/, @{$type_list})){ if ($jobproperties ne ""){ $jobproperties = "($jobproperties) AND deploy = \\\'YES\\\'"; }else{ $jobproperties = "deploy = \\\'YES\\\'"; } } '); # Prevent deploy and allow_classic_ssh type jobs on none entire nodes INSERT IGNORE INTO admission_rules (rule) VALUES (' my @bad_resources = ("core","cpu","resource_id",); if (grep(/^(deploy|allow_classic_ssh)$/, @{$type_list})){ foreach my $mold (@{$ref_resource_list}){ foreach my $r (@{$mold->[0]}){ my $i = 0; while (($i <= $#{$r->{resources}})){ if (grep(/^$r->{resources}->[$i]->{resource}$/i, @bad_resources)){ die("[ADMISSION RULE] \'$r->{resources}->[$i]->{resource}\' resource is not allowed with a deploy or allow_classic_ssh type job\\n"); } $i++; } } } } '); # Force desktop_computing jobs to go on nodes with the desktop_computing property INSERT IGNORE INTO admission_rules (rule) VALUES (' if (grep(/^desktop_computing$/, @{$type_list})){ print("[ADMISSION RULE] Added automatically desktop_computing resource constraints\\n"); if ($jobproperties ne ""){ $jobproperties = "($jobproperties) AND desktop_computing = \\\'YES\\\'"; }else{ $jobproperties = "desktop_computing = \\\'YES\\\'"; } }else{ if ($jobproperties ne ""){ $jobproperties = "($jobproperties) AND desktop_computing = \\\'NO\\\'"; }else{ $jobproperties = "desktop_computing = \\\'NO\\\'"; } } '); # Limit the number of reservations that a user can do. # (overrided on user basis using the file: ~oar/unlimited_reservation.users) INSERT IGNORE INTO admission_rules (rule) VALUES (' if ($reservationField eq "toSchedule") { my $unlimited=0; if (open(FILE, "< $ENV{HOME}/unlimited_reservation.users")) { while (){ if (m/^\\s*$user\\s*$/m){ $unlimited=1; } } close(FILE); } if ($unlimited > 0) { print("[ADMISSION RULE] $user is granted the privilege to do unlimited reservations\\n"); } else { my $max_nb_resa = 2; my $nb_resa = $dbh->do(" SELECT job_id FROM jobs WHERE job_user = \\\'$user\\\' AND (reservation = \\\'toSchedule\\\' OR reservation = \\\'Scheduled\\\') AND (state = \\\'Waiting\\\' OR state = \\\'Hold\\\') "); if ($nb_resa >= $max_nb_resa){ die("[ADMISSION RULE] Error : you cannot have more than $max_nb_resa waiting reservations.\\n"); } } } '); ## How to perform actions if the user name is in a file #INSERT IGNORE INTO admission_rules (rule) VALUES (' #open(FILE, "/tmp/users.txt"); #while (($queue_name ne "admin") and ($_ = )){ # if ($_ =~ m/^\\s*$user\\s*$/m){ # print("[ADMISSION RULE] Change assigned queue into admin\\n"); # $queue_name = "admin"; # } #} #close(FILE); #'); # Limit walltime for interactive jobs INSERT IGNORE INTO admission_rules (rule) VALUES (' my $max_walltime = OAR::IO::sql_to_duration("12:00:00"); if (($jobType eq "INTERACTIVE") and ($reservationField eq "None")){ foreach my $mold (@{$ref_resource_list}){ if ((defined($mold->[1])) and ($max_walltime < $mold->[1])){ print("[ADMISSION RULE] Walltime to big for an INTERACTIVE job so it is set to $max_walltime.\\n"); $mold->[1] = $max_walltime; } } } '); # specify the default walltime if it is not specified INSERT IGNORE INTO admission_rules (rule) VALUES (' my $default_wall = OAR::IO::sql_to_duration("2:00:00"); foreach my $mold (@{$ref_resource_list}){ if (!defined($mold->[1])){ print("[ADMISSION RULE] Set default walltime to $default_wall.\\n"); $mold->[1] = $default_wall; } } '); # Check if types given by the user are right INSERT IGNORE INTO admission_rules (rule) VALUES (' my @types = ("container","inner","deploy","desktop_computing","besteffort","cosystem","idempotent","timesharing","allow_classic_ssh"); foreach my $t (@{$type_list}){ my $i = 0; while (($types[$i] ne $t) and ($i <= $#types)){ $i++; } if (($i > $#types) and ($t !~ /^(timesharing|inner)/)){ die("[ADMISSION RULE] The job type $t is not handled by OAR; Right values are : @types\\n"); } } '); # If resource types are not specified, then we force them to default INSERT IGNORE INTO admission_rules (rule) VALUES (' foreach my $mold (@{$ref_resource_list}){ foreach my $r (@{$mold->[0]}){ my $prop = $r->{property}; if (($prop !~ /[\\s\\(]type[\\s=]/) and ($prop !~ /^type[\\s=]/)){ if (!defined($prop)){ $r->{property} = "type = \\\'default\\\'"; }else{ $r->{property} = "($r->{property}) AND type = \\\'default\\\'"; } } } } print("[ADMISSION RULE] Modify resource description with type constraints\\n"); '); ./oar-2.5.2/tests/scheduler/data/000_my.cnf0000644000175000017500000000706611757171206016317 0ustar plbplb# # The MySQL database server configuration file. # # You can copy this to one of: # - "/etc/mysql/my.cnf" to set global options, # - "~/.my.cnf" to set user-specific options. # # One can use all long options that the program supports. # Run program with --help to get a list of available options and with # --print-defaults to see which it would actually understand and use. # # For explanations see # http://dev.mysql.com/doc/mysql/en/server-system-variables.html # This will be passed to all mysql clients # It has been reported that passwords should be enclosed with ticks/quotes # escpecially if they contain "#" chars... # Remember to edit /etc/mysql/debian.cnf when changing the socket location. [client] port = 3306 socket = /var/run/mysqld/mysqld.sock # Here is entries for some specific programs # The following values assume you have at least 32M ram # This was formally known as [safe_mysqld]. Both versions are currently parsed. [mysqld_safe] socket = /var/run/mysqld/mysqld.sock nice = 0 [mysqld] # # * Basic Settings # user = mysql pid-file = /var/run/mysqld/mysqld.pid socket = /var/run/mysqld/mysqld.sock port = 3306 basedir = /usr datadir = /var/lib/mysql tmpdir = /tmp language = /usr/share/mysql/english skip-external-locking # # Instead of skip-networking the default is now to listen only on # localhost which is more compatible and is not less secure. bind-address = 127.0.0.1 # # * Fine Tuning # key_buffer = 16M max_allowed_packet = 16M thread_stack = 128K thread_cache_size = 8 #max_connections = 100 #table_cache = 64 #thread_concurrency = 10 # # * Query Cache Configuration # query_cache_limit = 1M query_cache_size = 16M # # * Logging and Replication # # Both location gets rotated by the cronjob. # Be aware that this log type is a performance killer. #log = /var/log/mysql/mysql.log # # Error logging goes to syslog. This is a Debian improvement :) # # Here you can see queries with especially long duration #log_slow_queries = /var/log/mysql/mysql-slow.log #long_query_time = 2 #log-queries-not-using-indexes # # The following can be used as easy to replay backup logs or for replication. # note: if you are setting up a replication slave, see README.Debian about # other settings you may need to change. #server-id = 1 #log_bin = /var/log/mysql/mysql-bin.log #expire_logs_days = 10 #max_binlog_size = 100M #binlog_do_db = oar #binlog_ignore_db = oar2 # # * BerkeleyDB # # Using BerkeleyDB is now discouraged as its support will cease in 5.1.12. skip-bdb # # * InnoDB # # InnoDB is enabled by default with a 10MB datafile in /var/lib/mysql/. # Read the manual for more InnoDB related options. There are many! # You might want to disable InnoDB to shrink the mysqld process by circa 100MB. #skip-innodb # # * Security Features # # Read the manual, too, if you want chroot! # chroot = /var/lib/mysql/ # # For generating SSL certificates I recommend the OpenSSL GUI "tinyca". # # ssl-ca=/etc/mysql/cacert.pem # ssl-cert=/etc/mysql/server-cert.pem # ssl-key=/etc/mysql/server-key.pem [mysqldump] quick quote-names max_allowed_packet = 16M [mysql] #no-auto-rehash # faster start of mysql but no tab completition [isamchk] key_buffer = 16M # # * NDB Cluster # # See /usr/share/doc/mysql-server-*/README.Debian for more information. # # The following configuration is read by the NDB Data Nodes (ndbd processes) # not from the NDB Management Nodes (ndb_mgmd processes). # # [MYSQL_CLUSTER] # ndb-connectstring=127.0.0.1 # # * IMPORTANT: Additional settings that can override those from this file! # #!includedir /etc/mysql/conf.d/ ./oar-2.5.2/tests/scheduler/data/000_default_data.sql0000644000175000017500000000067311757171206020335 0ustar plbplbINSERT INTO queues (queue_name, priority, scheduler_policy) VALUES ('admin','10','oar_sched_gantt_with_timesharing'); INSERT INTO queues (queue_name, priority, scheduler_policy) VALUES ('default','2','oar_sched_gantt_with_timesharing'); INSERT INTO queues (queue_name, priority, scheduler_policy) VALUES ('besteffort','0','oar_sched_gantt_with_timesharing'); INSERT INTO gantt_jobs_predictions (moldable_job_id , start_time) VALUES ('0','0'); ./oar-2.5.2/tests/scheduler/README0000644000175000017500000001057311757171206014567 0ustar plbplb= About = This directory proposes a unit tests suite for OAR's scheduler. It basically consists of a framework to help writing little scripts that will check the scheduler's decisions. Since OAR's API comes down to the Database, input and output data for the unit tests are SQL data. Input data are SQL file to be generated using mysqldump on a live OAR installation, or on a fake installation where resources aren't real (e.g. all resources targets are on the single test machine), then injected in the test database. Output data is actually the test database as it is let once the scheduler ran. Unit test results are to be computed using SQL statements on that database. Several ideas behind this test framework come from Perl unit test packages mechanism (Test::More). = Notes = As a matter of implementation, it was needed to be able to run the Database server with a faked time, so that input SQL files are not outdated once inserted later after their creation. This is achieved using the faketime program, and tranparent thanks to the framework functions. Only the MySQL database is supported for now. A dedicated MySQL server is run for tests, hence you must watch out which TCP port is available: by default the dedicated MySQL server run on the 3307 port in order to avoid an obvious conflict with the default MySQL server instance which might be run on system startup. = Files = Any test is a file which is prefixed by 3 digits, e.g. 001_advance_reservation_with_absent_resource.sh . A test file uses file with the same prefix in the "data" directory, e.g. 001_oar.conf . Tests use bash functions that are defined in the "scripts" directory. Upon a test script execution, the execution environement is created in the "build" directory. Finally the "src" symlink is pointing to OAR source directory where the installation Makefile is located, e.g. /trunk . = Configuration = Running those tests requires: * MySQL server * MySQL client * faketime * perl, aso... The src symlink pointing to OAR source directory, e.g. /trunk . = Running tests = Running a test is only the matter of running the 3 digit prefixed bash script. The test might output to stderr a lot of verbose information, but only tests results goes to stdout. A test is successful if it outputs only "OK: ..." lines. A test fails as soon as it outputs one "KO: ..." line. = Writing tests = The easiest way to write a test is to copy one of the existing one, and adapt it. The test script process begins with the test_cleanup call, everthing above should be left as it is. 3 kinds of functions are provided: * base, which provides the framework base functions, related to the actual testing * mysql, which provides functions to handle the MySQL database process * oar, which provides functions to handle OAR mechanisms == base == * debug(message) Print (to stderr) debug info if debug is activated (DEBUG variable) * test_print_ok(message) Print the OK flag, along with a message * test_print_ko(message) Print the KO flag, along with a message * test_exit_status(message, command) { Print the OK or KO flag depending on the exit status of command, along with a message * test_preparer() Prepare the test environment * test_cleanup() Clean-up the test environment == MySQL == * mysql_check_oar_conf() Check that the oar.conf used if ok for the test * mysql_copy_config(file=my.cnf) Fetch mysql config file from the data directory, by adding the test prefix to file. file defaults to "my.cnf". * mysql_init() Initialize the dedicated MySQL server own data. * mysql_start(date=null) Start the dedicated MySQL server, possibily a the specified date, or now if no date is given. * mysql_stop() Stop the dedicated MySQL server. * mysql_create_oar_db() Create the oar DB, as given in oar.conf, and grant privilegies. * mysql_query(query) Run the SQL query that is provided as parameter. * mysql_query_from_file(file) Run the SQL query from file, looked up in the data directory after prepending the test prefix. == OAR == * oar_copy_config(file=oar.conf) Fetch OAR config file from the data directory, by adding the test prefix to file. file defaults to "oar.conf". * oar_source_conf() Source OAR parameters, making then available for the test functions. * oar_install() Perform OAR installation within the test enviroment. oar_run_scheduler() Run one pass of OAR meta scheduler, i.e. one pass of the queue's scheduler on each queue. ./oar-2.5.2/tests/scheduler/003_heavy_load.sh0000755000175000017500000000145311757171206016740 0ustar plbplb#!/bin/bash # $Id: 001_advance_reservation_with_absent_resource.sh 1712 2008-10-17 08:41:53Z neyron $ # Basic example of test scenario BASENAME=${0#$PWD/} BASEPREFIX=${BASENAME%%_*} BASEDIR=$PWD/${BASENAME%/*} BUILDDIR=$BASEDIR/build DATADIR=$BASEDIR/data SCRIPTDIR=$BASEDIR/scripts SRCDIR=$BASEDIR/src DEBUG=1 . $SCRIPTDIR/base.sh . $SCRIPTDIR/oar.sh . $SCRIPTDIR/mysql.sh test_cleanup test_prepare oar_install oar_copy_config oar_source_config mysql_check_oar_conf mysql_copy_config mysql_init mysql_start "2008-10-23 15:00:00" mysql_create_oar_db mysql_query_from_file heavy_load.sql oar_run_scheduler THRESHOLD=42 test_exit_status "Scheduler performance test (threshold = $THRESHOLD)" \ mysql_query "SELECT count\(*\) FROM gantt_jobs_predictions" \| \( read \; test \$REPLY -ge $THRESHOLD \) mysql_stop ./oar-2.5.2/tests/scheduler/002_advance_reservation_with_dead_resource.sh0000755000175000017500000000173411757171206024567 0ustar plbplb#!/bin/bash # $Id$ # Basic example of test scenario BASENAME=${0#$PWD/} BASEPREFIX=${BASENAME%%_*} BASEDIR=$PWD/${BASENAME%/*} BUILDDIR=$BASEDIR/build DATADIR=$BASEDIR/data SCRIPTDIR=$BASEDIR/scripts SRCDIR=$BASEDIR/src DEBUG=1 . $SCRIPTDIR/base.sh . $SCRIPTDIR/oar.sh . $SCRIPTDIR/mysql.sh test_cleanup test_prepare oar_install oar_copy_config oar_source_config mysql_check_oar_conf mysql_copy_config mysql_init mysql_start "2008-10-09 18:00:00" mysql_create_oar_db mysql_query_from_file advance_reservation_with_dead_resource.sql oar_run_scheduler test_exit_status "Scheduler before advance reseration start time" \ mysql_query "SELECT state, reservation FROM jobs WHERE job_id = 1" \| grep -q -e "^Waiting[[:space:]]Scheduled$" mysql_stop mysql_start "2008-10-09 20:01:00" oar_run_scheduler test_exit_status "Scheduler at advance reseration start time" \ mysql_query "SELECT state, reservation FROM jobs WHERE job_id = 1" \| grep -q -e "^toLaunch[[:space:]]Scheduled$" mysql_stop ./oar-2.5.2/tests/scheduler/001_advance_reservation_with_absent_resource.sh0000755000175000017500000000173611757171206025147 0ustar plbplb#!/bin/bash # $Id$ # Basic example of test scenario BASENAME=${0#$PWD/} BASEPREFIX=${BASENAME%%_*} BASEDIR=$PWD/${BASENAME%/*} BUILDDIR=$BASEDIR/build DATADIR=$BASEDIR/data SCRIPTDIR=$BASEDIR/scripts SRCDIR=$BASEDIR/src DEBUG=1 . $SCRIPTDIR/base.sh . $SCRIPTDIR/oar.sh . $SCRIPTDIR/mysql.sh test_cleanup test_prepare oar_install oar_copy_config oar_source_config mysql_check_oar_conf mysql_copy_config mysql_init mysql_start "2008-10-09 18:00:00" mysql_create_oar_db mysql_query_from_file advance_reservation_with_absent_resource.sql oar_run_scheduler test_exit_status "Scheduler before advance reseration start time" \ mysql_query "SELECT state, reservation FROM jobs WHERE job_id = 1" \| grep -q -e "^Waiting[[:space:]]Scheduled$" mysql_stop mysql_start "2008-10-09 20:01:00" oar_run_scheduler test_exit_status "Scheduler at advance reseration start time" \ mysql_query "SELECT state, reservation FROM jobs WHERE job_id = 1" \| grep -q -e "^toLaunch[[:space:]]Scheduled$" mysql_stop ./oar-2.5.2/tests/scheduler/000_basic.sh0000755000175000017500000000121211757171206015674 0ustar plbplb#!/bin/bash # $Id$ # Basic example of test scenario BASENAME=${0#$PWD/} BASEPREFIX=${BASENAME%%_*} BASEDIR=$PWD/${BASENAME%/*} BUILDDIR=$BASEDIR/build DATADIR=$BASEDIR/data SCRIPTDIR=$BASEDIR/scripts SRCDIR=$BASEDIR/src DEBUG=1 . $SCRIPTDIR/base.sh . $SCRIPTDIR/oar.sh . $SCRIPTDIR/mysql.sh test_cleanup test_prepare oar_install oar_copy_config oar_source_config mysql_check_oar_conf mysql_copy_config mysql_init mysql_start mysql_create_oar_db mysql_query_from_file mysql_structure.sql mysql_query_from_file default_data.sql mysql_query_from_file mysql_default_admission_rules.sql oar_run_scheduler mysql_stop test_print_ok "Sanity test is ok" ./oar-2.5.2/tests/rspec/0000755000175000017500000000000011757171206013037 5ustar plbplb./oar-2.5.2/tests/rspec/shared_examples.rb0000644000175000017500000001023411757171206016530 0ustar plbplb# All data structures minimum requirements shared_examples_for "All structures" do specify { @api.value.should have_key('api_timestamp') } specify('api_timestamp should be an integer') { @api.value['api_timestamp'].should be_a(Integer) } specify { @api.value.should have_key('links') } specify('links should be an array') { @api.value['links'].should be_an(Array) } specify('links should not be empty') { @api.value['links'].should_not be_empty } specify('self link should not be nil') { @api.get_self_link_href.should_not be_nil } end shared_examples_for "All list structures" do it_should_behave_like "All structures" specify { @api.value.should have_key('items') } specify('items should be an array') { @api.value['items'].should be_an(Array) } specify { @api.value['items'].should_not be_empty } specify { @api.value.should have_key('total') } specify('total should be an integer') { @api.value['total'].should be_a(Integer) } specify('total should be positive') { @api.value['total'].to_i.should > 0} specify { @api.value.should have_key('offset') } specify('offset should be an integer') { @api.value['offset'].should be_a(Integer) } end # Item structure minimum requirements shared_examples_for "Item" do it_should_behave_like "All structures" specify { @api.value.should have_key('id') } specify('id should be an integer') { @api.value['id'].should be_a(Integer) } specify('should have a self link') { @api.get_self_link_href.should be_a(String) } end # Job structure minimum requirements shared_examples_for "JobId" do it_should_behave_like "Item" specify('self link should be correct') { @api.get_self_link_href.should == "/oarapi-priv/jobs/#{@api.value['id']}" } end shared_examples_for "Job" do it_should_behave_like "JobId" specify { @api.value.should have_key('owner') } specify('job owner should not be nil') { @api.value['owner'].should_not be_nil } specify { @api.value.should have_key('state') } specify('job state should not be nil') { @api.value['state'].should_not be_nil } specify { @api.value.should have_key('queue') } specify('job queue should not be nil') { @api.value['queue'].should_not be_nil } specify { @api.value.should have_key('name') } specify('resources link should not be nil') { @api.get_link_href('resources').should_not be_nil } specify('resources link should be correct') { @api.get_link_href('resources').should == "/oarapi-priv/jobs/#{@api.value['id']}/resources" } specify('array_id should be an integer') { if not @api.value['array_id'].nil? @api.value['array_id'].should be_a(Integer) end } specify('start_time should be an integer') { if not @api.value['start_time'].nil? @api.value['start_time'].should be_a(Integer) end } end # Resource structure minimum requirements shared_examples_for "ResourceId" do it_should_behave_like "Item" specify('self link should be correct') { @api.get_self_link_href.should == "/oarapi-priv/resources/#{@api.value['id']}" } specify('available_upto should be an integer') { if not @api.value['available_upto'].nil? @api.value['available_upto'].should be_a(Integer) end } end shared_examples_for "Resource" do it_should_behave_like "ResourceId" specify { @api.value.should have_key('state') } specify('resource state should not be nil') { @api.value['state'].should_not be_nil } specify { @api.value.should have_key('network_address') } specify { @api.value.should have_key('available_upto') } specify('node link should not be nil') { @api.get_link_href('node').should_not be_nil } specify('jobs link should not be nil') { @api.get_link_href('jobs').should_not be_nil } end # Node structure minimum requirements shared_examples_for "Node" do it_should_behave_like "Item" specify('self link should be correct') { @api.get_self_link_href.should == "/oarapi-priv/resources/nodes/#{@api.value['network_address']}" } end ./oar-2.5.2/tests/rspec/resources_creation_spec.rb0000644000175000017500000002341411757171206020300 0ustar plbplbrequire 'oarrestapi_lib' require 'shared_examples' APIURI="http://oar:kameleon@localhost/oarapi-priv/" describe OarApi do before :all do @oar_server = OarApi.new(APIURI) end describe "Array of resources submission" do before(:all) do $resources = [ {'network_address' => "rtest1" , 'besteffort' => "NO", 'cpuset' => 0 }, {'network_address' => "rtest1", 'besteffort' => "NO", 'cpuset' => 1 }, {'network_address' => "rtest2" , 'besteffort' => "YES", 'cpuset' => 0 }, {'network_address' => "rtest2", 'besteffort' => "YES", 'cpuset' => 1 } ] lambda { @oar_server.create_resources($resources) }.should_not raise_exception end #Checking the resources it "should return an items array " do @oar_server.resstatus['items'].should_not be_empty end it "should return 4 items " do @oar_server.resstatus['items'].length.should == 4 end it "should contain ids of newly created resources" do @oar_server.resstatus['items'].each do |item| item["id"].to_i.should be_a(Integer) end end it "should return the new resources links" do @oar_server.resstatus['items'].each do |item| @oar_server.get_link_href_from_array_by_rel(item["links"],"self").should be_a(String) end end it "should have created resources having asked properties" do i=0 @oar_server.resstatus['items'].each do |item| link=@oar_server.get_link_href_from_array_by_rel(item["links"],"self") @oar_server.get_hash(link) #puts @oar_server.value["network_address"] @oar_server.value["network_address"].should == $resources[i]["network_address"] @oar_server.value["besteffort"].should == $resources[i]["besteffort"] @oar_server.value["cpuset"].to_i.should == $resources[i]["cpuset"].to_i i+=1 end end it "should have created Alive resources" do i=0 @oar_server.resstatus['items'].each do |item| link=@oar_server.get_link_href_from_array_by_rel(item["links"],"self") @oar_server.get_hash(link) @oar_server.value["state"].should == "Alive" i+=1 end end # Cleaning it "should delete the test resources" do @oar_server.resstatus['items'].each do |item| @oar_server.post(@oar_server.api,"resources/#{item['id']}/state",{"state" => "Dead"}) @oar_server.delete_resource(item["id"]) end end end describe "Unique resource submission" do before(:all) do $resource = {'network_address' => "rtest" , 'besteffort' => "NO", 'cpuset' => 1 } lambda { @oar_server.create_resources($resource) }.should_not raise_exception end #Checking the resources it "should return an items array " do @oar_server.resstatus['items'].should_not be_empty end it "should return 1 item " do @oar_server.resstatus['items'].length.should == 1 end it "should contain id of newly created resource" do @oar_server.resstatus['items'][0]["id"].to_i.should be_a(Integer) end it "should return the new resource link" do links=@oar_server.resstatus['items'][0]["links"] @oar_server.get_link_href_from_array_by_rel(links,"self").should be_a(String) end it "should have created a resource having asked properties" do links=@oar_server.resstatus['items'][0]["links"] link=@oar_server.get_link_href_from_array_by_rel(links,"self") @oar_server.get_hash(link) @oar_server.value["network_address"].should == $resource["network_address"] @oar_server.value["besteffort"].should == $resource["besteffort"] @oar_server.value["cpuset"].to_i.should == $resource["cpuset"].to_i end it "should have created an Alive resource" do links=@oar_server.resstatus['items'][0]["links"] link=@oar_server.get_link_href_from_array_by_rel(links,"self") @oar_server.get_hash(link) @oar_server.value["state"].should == "Alive" end # Cleaning it "should delete the test resource" do id=@oar_server.resstatus['items'][0]["id"] @oar_server.post(@oar_server.api,"resources/#{id}/state",{"state" => "Dead"}) @oar_server.delete_resource(id) end end describe "Error checking" do context "(when submitting system properties)" do before(:all) do $resources = [ {'network_address' => "rtest1", 'besteffort' => "NO", 'cpuset' => 1 }, {'network_address' => "rtest2" , 'besteffort' => "YES", 'state' => 'Alive' } ] end it "should raise an exception" do lambda { @oar_server.create_resources($resources) }.should raise_exception end it "should raise a 403 error" do begin @oar_server.create_resources($resources) rescue => e e.should respond_to('http_code') e.http_code.should == 403 end end end context "(when forgetting network_address)" do before(:all) do $resources = [ {'besteffort' => "NO", 'cpuset' => 1 }, {'network_address' => "rtest2" , 'besteffort' => "YES", 'state' => 'Alive' } ] end it "should raise an exception" do lambda { @oar_server.create_resources($resources) }.should raise_exception end it "should raise a 400 error" do begin @oar_server.create_resources($resources) rescue => e e.should respond_to('http_code') e.http_code.should == 400 end end end end describe "GENERATE RESOURCES: post /resources/generate" do before(:all) do @api = OarApi.new(APIURI) generate = { 'resources' => '/nodes=node{2}.test.generate/cpu={2}/core={2}', 'properties' => { 'besteffort' => 'NO', 'available_upto' => '42' } } lambda { begin @api.value=@api.post(@api.api,"resources/generate",generate) rescue => e if e.respond_to?('http_code') puts "ERROR #{e.http_code}:\n #{e.response.body}" else puts "Parse error:" puts e.inspect end exit 1 end }.should_not raise_exception end it_should_behave_like "All list structures" it "should return correct resources" do resources=[ { 'network_address' => 'node1.test.generate', 'available_upto' => 42, 'besteffort' => 'NO', 'cpu' => '1', 'cpuset' => 0, 'core' => '1' }, { 'network_address' => 'node1.test.generate', 'available_upto' => 42, 'besteffort' => 'NO', 'cpu' => '1', 'cpuset' => 1, 'core' => '2' }, { 'network_address' => 'node1.test.generate', 'available_upto' => 42, 'besteffort' => 'NO', 'cpu' => '2', 'cpuset' => 2, 'core' => '3' }, { 'network_address' => 'node1.test.generate', 'available_upto' => 42, 'besteffort' => 'NO', 'cpu' => '2', 'cpuset' => 3, 'core' => '4' }, { 'network_address' => 'node2.test.generate', 'available_upto' => 42, 'besteffort' => 'NO', 'cpu' => '3', 'cpuset' => 0, 'core' => '5' }, { 'network_address' => 'node2.test.generate', 'available_upto' => 42, 'besteffort' => 'NO', 'cpu' => '3', 'cpuset' => 1, 'core' => '6' }, { 'network_address' => 'node2.test.generate', 'available_upto' => 42, 'besteffort' => 'NO', 'cpu' => '4', 'cpuset' => 2, 'core' => '7' }, { 'network_address' => 'node2.test.generate', 'available_upto' => 42, 'besteffort' => 'NO', 'cpu' => '4', 'cpuset' => 3, 'core' => '8' } ] i=0 @api.value["items"].each do |resource| resource.should == resources[i] i+=1 end end context "(when injecting results into POST /resources)" do before(:all) do lambda { @api.create_resources(@api.value["items"]) }.should_not raise_exception end it "should have created the resources (should return 8 items) " do @api.resstatus['items'].length.should == 8 end # Cleaning it "should delete the test resources" do @api.resstatus['items'].each do |item| @api.post(@api.api,"resources/#{item['id']}/state",{"state" => "Dead"}) @api.delete_resource(item["id"]) end end end end describe "GENERATE RESOURCES: post /resources/generate with auto_offset" do before(:all) do @api = OarApi.new(APIURI) generate = {'resources' => '/nodes=node{3}.test.generate/cpu={2}', 'auto_offset' => 1 } lambda { begin @api.value=@api.post(@api.api,"resources/generate",generate) rescue => e if e.respond_to?('http_code') puts "ERROR #{e.http_code}:\n #{e.response.body}" else puts "Parse error:" puts e.inspect end exit 1 end }.should_not raise_exception end it_should_behave_like "All list structures" it "should return resources with cpu number > 1" do @api.value["items"][0]["cpu"].to_i.should > 1 end end end ./oar-2.5.2/tests/rspec/oarrestapi_lib.rb0000644000175000017500000003733311757171206016374 0ustar plbplb#require 'rubygems' require 'rest_client' require 'json' require 'pp' require 'uri' ####################################################################### #Coded By Narayanan K - GSOC Testsuites project - RESTful API Library # Modified by B. Bzeznik 2010-2011 ####################################################################### class OarApi attr_accessor :jobhash, :statushash, :specificjobdetails, :oarv, :oartz, :jobarray, :deletehash, :apiuri, :value attr_reader :deletestatus,:jobstatus,:api,:chkpointstatus,:holdjob,:rholdjob,:signalreturn,:resumejob,:resources,:resourcedetails,:resstatus,:specificres,:noderesources def initialize(apiuri,get_uri="") @api = RestClient::Resource.new apiuri @apiuri = URI.parse(apiuri) end # Converts the given uri, to something relative # to the base of the API def rel_uri(uri) abs_uri=@apiuri.merge(uri).to_s target_uri=URI.parse(abs_uri).to_s @apiuri.route_to(target_uri).to_s end ######################################################################## # # GET REST OAR API # # Purpose: Function to get objects from the api # # Result: We use the JSON format # ######################################################################## def get(api,uri) uri=rel_uri(uri) # begin return JSON.parse(api[uri].get(:accept => 'application/json')) # rescue => e # if e.respond_to?('http_code') # puts "ERROR #{e.http_code}:\n #{e.response.body}" # else # puts "Parse error:" # puts e.inspect # end # exit 1 # end end ######################################################################## # # POST REST OAR API # # Purpose: Function to create/delete/hold/resume objects through the api # # Result: We use the JSON format. # ######################################################################## def post(api,uri,j) uri=rel_uri(uri) j=j.to_json return JSON.parse(api[uri].post( j,:content_type => 'application/json')) end ######################################################################## # # DELETE REST OAR API # # Purpose: Function to Delete objects through the api # # Result: We use the JSON format. # ######################################################################## def delete(api, uri) uri=rel_uri(uri) begin return JSON.parse(api[uri].delete(:content_type => 'application/json')) rescue => e if e.respond_to?('http_code') puts "ERROR #{e.http_code}:\n #{e.response.body}" else puts "Parse error:" puts e.inspect end exit 1 end end ######################################################################## # # GENERIC FUNCTIONS TO GET REST OBJECTS # ######################################################################## def get_hash(uri) @value = get(@api, uri) if !@value.is_a?(Hash) raise "Error: GET #{uri} should return a hash" end end ######################################################################## # # Method: oar_version # # Usecase01: Gives version info & Timezone about OAR and OAR API/Server. # # Input: Nil # # Result: GETs the Version details(hash)and stores in Hash oarv # ######################################################################## def oar_version @oarv = get(@api, '/version') if !@oarv.is_a?(Hash) or @oarv.empty? raise 'Error: In return value of GET /version API' end end ######################################################################## # # Method: oar_timezone # # Usecase02: Gives the timezone of the OAR API server. # # Input: Nil # # Result: GETs the Timezone details(hash)and stores in Hash oart # ######################################################################## def oar_timezone @oartz = get(@api, '/timezone') if !@oartz.is_a?(Hash) or @oartz.empty? raise 'Error: In return value of GET /timezone API' end end ######################################################################## # # Method: full_job_details # # Usecase03: List the current jobs & some details like assigned resources # # Input: Nil # # Result: GETs details of current jobs(array of hashes)& stores in jobhash # ######################################################################## def full_job_details @jobarray = get(@api,'jobs/details') if !@jobarray.is_a?(Hash) raise 'Error: In return value of GET /jobs/details API' end end ######################################################################## # # Method: run_job_details # # Usecase04: List currently running jobs # # Input: Nil # # Result: GETs details of running jobs(array of hashes)& stores in jobhash # ######################################################################## def run_job_details @jobarray = get(@api,'jobs') if !@jobarray.is_a?(Hash) raise 'Error: In return value of GET /jobs API' end end ######################################################################## # # Method: specific_job_details(jobid) # # Usecase05: Get Details of a specific job # # Input: jobid # # Result: GETs details of specific job & stores in hash specificjobdetails # ######################################################################## def specific_job_details(jobid) @specificjobdetails = get(@api, "jobs/#{jobid}") if !@specificjobdetails.is_a?(Hash) or @specificjobdetails.empty? raise 'Error: In return value of GET /jobs/ API' end end ######################################################################## # # Method: dump_job_table # # Usecase06: Dump the jobs table (only current jobs) # # Input: None # # Result: Dumps details of current jobs into array of hash - jobhash # ######################################################################## def dump_job_table @jobarray = get(@api,'jobs/table') if !@jobarray.is_a?(Hash) raise 'Error: In return value of GET /jobs/table API' end end ######################################################################## # # Method: submit_job(jhash) # # Usecase07: Submits job # # Input: jhash containing details of resources,jobscript in hash form # # Result: Returns the submitted job Details in Hash and stores in jobstatus # ######################################################################## def submit_job(jhash) @jobstatus = post(@api, 'jobs', jhash) if !@jobstatus.is_a?(Hash) or @jobstatus.empty? raise 'Error: In return value of POST /jobs API' end end ######################################################################## # # Method: del_job(jobid) # # Usecase08: Delete job - POST /jobs/id/deletions/new # # Input: jobid # # Result: Returns the deleted job Details in Hash and stores in deletestatus # ######################################################################## def del_job(jobid) @deletestatus = post(@api,"jobs/#{jobid}/deletions/new", '') if !@deletestatus.is_a?(Hash) or @deletestatus.empty? raise 'Error: In return value of POST /jobs//deletions/new API' end end def del_array_job(jobid) @deletestatus = post(@api,"jobs/array/#{jobid}/deletions/new", '') if !@deletestatus.is_a?(Hash) or @deletestatus.empty? raise 'Error: In return value of POST /jobs/array//deletions/new API' end end ######################################################################## # # Method: send_checkpoint(jobid) # # Usecase09: Send checkpoint signal to a job # # Input: jobid # # Result: Returns details of checkpointed job in hash - chkpointstatus # ######################################################################## def send_checkpoint(jobid) @chkpointstatus = post(@api,"jobs/#{jobid}/checkpoints/new", '') if !@chkpointstatus.is_a?(Hash) or @chkpointstatus.empty? raise 'Error: In return value of POST /jobs//checkpoints/new API' end end ######################################################################## # # Method: hold_waiting_job(jobid) # # Usecase10: Hold a Waiting job # # Input: jobid # # Result: Returns details of holded job in hash - holdjob # ######################################################################## def hold_waiting_job(jobid) @holdjob = post(@api,"jobs/#{jobid}/holds/new", '') if !@holdjob.is_a?(Hash) or @holdjob.empty? raise 'Error: In return value of POST /jobs//holds/new API' end end ######################################################################## # # Method: hold_running_job(jobid) # # Usecase11: Hold a Running job # # Input: jobid # # Result: Returns details of holded job in hash - rholdjob # ######################################################################## def hold_running_job(jobid) @rholdjob = post(@api,"jobs/#{jobid}/rholds/new", '') if !@rholdjob.is_a?(Hash) or @rholdjob.empty? raise 'Error: In return value of POST /jobs//rholds/new API' end end ######################################################################## # # Method: resume_hold_job(jobid) # # Usecase12: Resume a Holded job # # Input: jobid # # Result: Returns details of resumed job in hash - resumejob # ######################################################################## def resume_hold_job(jobid) @resumejob = post(@api,"jobs/#{jobid}/resumption/new", '') if !@resumejob.is_a?(Hash) or @resumejob.empty? raise 'Error: In return value of POST /jobs//resumption/new API' end end ######################################################################## # # Method: send_signal_job(jobid, signo) # # Usecase13: Send signal to a job with signalno. # # Input: jobid, signal number # # Result: Returns details of signalled job in hash - signalreturn # ######################################################################## def send_signal_job(jobid, signo) @signalreturn = post(@api,"jobs/#{jobid}/signal/#{signo}", '') if !@signalreturn.is_a?(Hash) or @signalreturn.empty? raise 'Error: In return value of POST /jobs//signal/ API' end end ######################################################################## # # Method: update_job(jobid, actionhash) # # Usecase14: Update a job # # Input: jobid, actionhash # # Result: Returns details of updated job in hash - updatehash # ######################################################################## def update_job(jobid, actionhash) @updatehash = post(@api, "jobs/#{jobid}",actionhash) if !@updatehash.is_a?(Hash) or @updatehash.empty? raise 'Error: In return value of POST /jobs// API' end end ######################################################################## # # Method: resource_list_state # # Usecase15: Get list of Resources and state # # Input: None # # Result: Returns details of resources & states in array of hashes - resources # ######################################################################## def resource_list_state @resources = get(@api, 'resources') if !@resources.is_a?(Hash) raise 'Error: In return value of GET /resources API' end end ######################################################################## # # Method: list_resource_details # # Usecase16: Get list of all the resources and all their details # # Input: None # # Result: Returns details of resource list in array of hashes - resourcedetails # ######################################################################## def list_resource_details @resourcedetails = get(@api, 'resources/full') if !@resourcedetails.is_a?(Hash) raise 'Error: In return value of GET /resources/full API' end end ######################################################################## # # Method: specific_resource_details(jobid) # # Usecase17: Get details of resources identified by an ID # # Input: jobid # # Result: Returns details of specific resource in array of hashes - specificres # ######################################################################## def specific_resource_details(jobid) @specificres = get(@api, "jobs/#{jobid}/resources") if !@specificres.is_a?(Hash) or @specificres.empty? raise 'Error: In return value of GET /jobs//resources API' end end ######################################################################## # # Method: resource_of_nodes(netaddr) # # Usecase18: Get details about the resources belonging to the node identified by network address # # Input: netaddr # # Result: Returns details of resource of nodes - noderesources # ######################################################################## def resource_of_nodes(netaddr) @noderesources = get(@api,"resources/nodes/#{netaddr}") if !@noderesources.is_a?(Hash) or @noderesources.empty? raise 'Error: In return value of GET /resources/nodes/ API' end end ######################################################################## # Resource creation methods ######################################################################## def create_resource(rhash) @resstatus = post(@api,'resources', rhash) if !@resstatus.is_a?(Hash) or @resstatus.empty? raise 'Error: In return value of POST /resources API' end end def create_resources(array) @resstatus = post(@api,'resources', array) if !@resstatus.is_a?(Hash) or @resstatus.empty? raise 'Error: In return value of POST /resources API' end end ######################################################################## # # Method: statechange_resource(jobid, hasharray) # # Usecase20: Change the state of resources of a job # # Input: jobid, hasharray # # Result: Returns details of created resources in hash- statushash # ######################################################################## def statechange_resource(jobid, hasharray) @statushash = post(@api, 'resources/#{jobid}/state', hasharray) if !@statushash.is_a?(Hash) or @statushash.empty? raise 'Error: In return value of POST /resources//state API' end end ######################################################################## # # Method: delete_job(jobid) # # Usecase21: Delete or kill a job. # # Input: jobid # # Result: Returns details of deleted job in hash- deletehash # ######################################################################## def delete_job(jobid) @deletehash = delete(@api,"/jobs/#{jobid}") if !@deletehash.is_a?(Hash) or @deletehash.empty? raise 'Error: In return value of DELETE /jobs/ API' end end ######################################################################## # # Method: delete_resource(resid) # # Usecase22: Delete the resource identified by id # # Input: resid # # Result: Returns details of deleted resources in hash- deletehash # ######################################################################## def delete_resource(resid) @deletehash = delete(@api,"resources/#{resid}") if !@deletehash.is_a?(Hash) or @deletehash.empty? raise 'Error: In return value of DELETE /resources/ API' end end ######################################################################## # # Method: delete_resource_cpuset(node, cpuid) # # Usecase23: Delete the resource corresponding to cpuset id on node node. # # Input: node, cpuid # # Result: Returns details of deleted resources in hash- deletehash # ######################################################################## def delete_resource_cpuset(node, cpuid) @deletehash = delete(@api,"/resources/#{node}/#{cpuid}") if !@deletehash.is_a?(Hash) or @deletehash.empty? raise 'Error: In return value of DELETE /resources// API' end end def get_link_href(title) @value['links'].each do |link| if link.is_a?(Hash) && link['title'] == title return link['href'] end end raise "#{title} link not found!" end def get_link_href_by_rel(rel) @value['links'].each do |link| if link.is_a?(Hash) && link['rel'] == rel return link['href'] end end raise "#{rel} link not found!" end def get_self_link_href get_link_href_by_rel("self") end def get_next_link_href get_link_href_by_rel("next") end def get_previous_link_href get_link_href_by_rel("previous") end def get_link_href_from_array(array,title) array.each do |link| if link.is_a?(Hash) && link['title'] == title return link['href'] end end raise "#{title} link not found!" end def get_link_href_from_array_by_rel(array,rel) array.each do |link| if link.is_a?(Hash) && link['rel'] == rel return link['href'] end end raise "#{rel} link not found!" end end ./oar-2.5.2/tests/rspec/job_submit_spec.rb0000644000175000017500000000557011757171206016542 0ustar plbplbrequire 'oarrestapi_lib' $jobid = "" APIURI="http://kameleon:kameleon@localhost/oarapi-priv/" describe OarApi do before :all do # Custom variables # APIURI="http://www.grenoble.grid5000.fr/oarapi" #Object of OarApis class @oar_server = OarApi.new(APIURI) end describe "Submission" do #Submitting a job it "should submit a job successfully " do script="#!/bin/bash #OAR --name rspec_test wc -l $OAR_FILE_NODES > /tmp/$OAR_JOB_ID.count echo \"Hello World\" pwd whoami sleep 120 " job = { 'resource' => "/nodes=1/core=2" , 'script' => script, 'scanscript' => 1 } lambda { @oar_server.submit_job(job) }.should_not raise_exception @oar_server.jobstatus['id'].to_i.should > 0 $jobid=@oar_server.jobstatus['id'] end end #Checking the queue (Can use GET /jobs to check) immediately. describe "Submitted job" do before :all do lambda { @oar_server.full_job_details }.should_not raise_exception end it "should have id in current queue" do timeout=60 found=0 t=0 while found==0 && t < timeout do @oar_server.jobarray['items'].each do |j| found=1 if j["id"] == $jobid && j["state"] == "Running" end @oar_server.full_job_details sleep 1 t += 1 printf "." $stdout.flush end found.should==1 end it "should be named 'rspec_test'" do @oar_server.specific_job_details($jobid) @oar_server.specificjobdetails["name"].should == "rspec_test" end it "should have created a file into /tmp" do sleep 1 File.exists?("/tmp/#{$jobid}.count").should == true end it "should return the good number of resources into the created file" do f = File.open("/tmp/#{$jobid}.count") f.gets.to_i.should == 2 end end #Delete the job describe "Deletion" do it "should delete the currently submitted job using the post api and jobid" do lambda { @oar_server.del_job($jobid) }.should_not raise_exception @oar_server.deletestatus['status'].should == "Delete request registered" end #Check the queue to ensure the job deleted is no more there #Negative Test it "should not contain the deleted job in the queue now" do timeout=60 lambda { @oar_server.full_job_details }.should_not raise_exception t=0 c=1 while t "/nodes=1/core=1" , 'script' => "id" , 'array' => '15' } @api.submit_job(jhash) $jobid = @api.jobstatus['id'] $jobid.should be_a(Integer) $jobid.should > 0 # Now, we wait for the 15th job to be terminated # It is necessary for the rest of the tests to pass timeout=180 t=0 c=0 while t "/nodes=1/core=2" , 'property' => "network_address like 'node%'", 'script' => "ls;pwd;whoami;sleep 60" , 'array' => '10' } @api.submit_job(jhash) $jobid = @api.jobstatus['id'] $jobid.should be_a(Integer) $jobid.should > 0 # Now, we wait for the 4th job to be running # It is necessary for the rest of the tests to pass timeout=60 t=0 c=0 while t "/nodes=1/core=1" , 'script' => "ls;pwd;whoami;sleep 60" , 'reservation' => '2037-01-01 01:00:00' } @api.submit_job(jhash) $rjobid = @api.jobstatus['id'] $rjobid.should be_a(Integer) $rjobid.should > 0 end end ############################################################# # URIs basic structure format checking ############################################################# uris=[ "jobs","jobs/details","jobs/table", "jobs?state=Running,Waiting,Launching","jobs?state=Terminated&limit=10", "jobs?state=Running,Terminated&limit=10","jobs?from=0&to=2147483647&limit=10", "resources","resources/full" ] uris.each do |uri| describe "#{uri} basic data structure" do before(:all) do @api = OarApi.new(APIURI) @api.get_hash(uri) end it_should_behave_like "All list structures" end end ############################################################# # Specific URI tests ############################################################# describe "JOBS CHECKINGS: /jobs data structure" do before(:all) do @api = OarApi.new(APIURI) end context "(using state=Running,Launching,Waiting&limit=2)" do before(:all) do $uri="jobs?state=Running,Launching,Waiting&limit=2" @api.get_hash($uri) end it "should correctly limit the number of results" do @api.value['items'].length.should == 2 end it "should return 2 links" do @api.value['links'].length.should == 2 end it "should return a total of 11" do @api.value['total'].to_i.should == 11 end it "should return an offset of 0" do @api.value['offset'].to_i.should == 0 end it "should return a correct self link" do @api.get_self_link_href.should == "/oarapi-priv/jobs?state=Running%2CLaunching%2CWaiting&limit=2&offset=0" end it "should return a correct next link" do @api.get_next_link_href.should == "/oarapi-priv/jobs?state=Running%2CLaunching%2CWaiting&limit=2&offset=2" end end context "(using /jobs?state=Running&limit=2&offset=3)" do before(:all) do $uri="jobs?state=Running,Waiting,Launching&limit=2&offset=3" @api.get_hash($uri) end it "should correctly limit the number of results" do @api.value['items'].length.should == 2 end it "should return 3 links" do @api.value['links'].length.should == 3 end it "should return a total of 11" do @api.value['total'].to_i.should == 11 end it "should return an offset of 3" do @api.value['offset'].to_i.should == 3 end it "should return a correct self link" do @api.get_self_link_href.should == "/oarapi-priv/jobs?state=Running%2CWaiting%2CLaunching&limit=2&offset=3" end it "should return a correct previous link" do @api.get_previous_link_href.should == "/oarapi-priv/jobs?state=Running%2CWaiting%2CLaunching&limit=2&offset=1" end it "should return a correct next link" do @api.get_next_link_href.should == "/oarapi-priv/jobs?state=Running%2CWaiting%2CLaunching&limit=2&offset=5" end end context "(using same as above plus Terminated jobs)" do before(:all) do $uri="jobs?state=Running,Waiting,Launching,Terminated&limit=15" @api.get_hash($uri) end it "should return a total >= 20" do @api.value['total'].to_i.should >= 20 end end context "(with state=Terminated)" do before(:all) do @api.get_hash("jobs?state=Terminated") end it "should return only terminated jobs" do @api.value['items'].each do |item| item['state'].should == "Terminated" end end end context "(with state=Running [note: add a sleep if test jobs are not yet running])" do before(:all) do @api.get_hash("jobs?state=Running&user=kameleon") end it "should return a few running jobs" do @api.value['items'].length.should > 0 end it "should return only running jobs" do @api.value['items'].each do |item| item['state'].should == "Running" end end it "should return jobs having a correct self link" do links=@api.value['items'][0]['links'] id=@api.value['items'][0]['id'] @api.get_link_href_from_array_by_rel(links,"self").should == "/oarapi-priv/jobs/#{id}" end it "should return jobs having a correct resources link" do links=@api.value['items'][0]['links'] id=@api.value['items'][0]['id'] @api.get_link_href_from_array(links,"resources").should == "/oarapi-priv/jobs/#{id}/resources" end it "should return jobs owned by the kameleon user" do @api.value['items'][0]['owner'].should == "kameleon" end end context "(with non-existent obiwankenobi user)" do before(:all) do @api.get_hash("jobs?state=Terminated&user=obiwankenobi") end it "should return no jobs" do @api.value['items'].length.should == 0 end end context "(inside job)" do before(:all) do @api.get_hash("jobs?state=Running") @api.value=@api.value['items'][0] end it_should_behave_like "Job" end context "(with limit=1)" do before(:all) do @api.get_hash("jobs?limit=1") end it_should_behave_like "All list structures" end context "(with limit=1, insider job)" do before(:all) do @api.get_hash("jobs?limit=1") @api.value=@api.value['items'][0] end it_should_behave_like "Job" end end describe "JOB DETAILS CHECKING: /jobs/ data structure" do context "(with normal job)" do before(:all) do @api = OarApi.new(APIURI) @api.get_hash("jobs/#{$jobid}") end it_should_behave_like "Job" it "should be owned by the kameleon user" do @api.value['owner'].should == "kameleon" end end context "(with non-existent job)" do before(:all) do @api = OarApi.new(APIURI) end it "should raise an exception" do lambda { @api.get_hash("jobs/00") }.should raise_exception end it "should return a 404 error" do begin @api.get_hash("jobs/00") rescue => e e.should respond_to('http_code') e.http_code.should == 404 end end end end describe "JOB DETAILS CHECKING: /jobs/details data structure" do before(:all) do @api = OarApi.new(APIURI) @api.get_hash("jobs/details") end context "(basic structure)" do it_should_behave_like "All list structures" end context "(insider job)" do before(:all) do @api.value=@api.value["items"][0] end it_should_behave_like "Job" it "should have resources and nodes details" do @api.value['resources'].should be_an(Array) @api.value['nodes'].should be_an(Array) end context "should have resources behaving correctly" do before(:all) do @api.value=@api.value['resources'][0] end it_should_behave_like "ResourceId" end context "should have nodes behaving correctly" do before(:all) do @api.value=@api.value['nodes'][0] end it_should_behave_like "Node" end end end describe "JOB RESOURCES: /jobs//resources" do before(:all) do @api = OarApi.new(APIURI) @api.get_hash("jobs/#{$jobid}/resources") end context "(basic data structure)" do it_should_behave_like "All list structures" end context "(inside resource)" do before(:all) do @api.value=@api.value['items'][0] end it_should_behave_like "ResourceId" it "should have a status field" do @api.value['status'].should_not be_nil end it "should have a valid status field (reserved or assigned)" do @api.value['status'].should match(/(reserved|assigned)/) end end end describe "JOB NODES: /jobs//nodes" do before(:all) do @api = OarApi.new(APIURI) @api.get_hash("jobs/#{$jobid}/nodes") end context "(basic data structure)" do it_should_behave_like "All list structures" end context "(inside node)" do before(:all) do @api.value=@api.value['items'][0] end it_should_behave_like "Node" end end describe "FUTUR JOBS CHECKING: /jobs" do before(:all) do @api = OarApi.new(APIURI) end context "(from 2036 to 2038)" do before(:all) do from=Time.local(2036,"jan",1,1,1,1).to_i to=Time.local(2038,"jan",1,1,1,1).to_i @api.get_hash("jobs?from=#{from}&to=#{to}") end it "should return only one job" do @api.value["items"].length.should == 1 end it "should return the previously submitted reservation" do @api.value["items"][0]["id"].should == $rjobid end end end describe "RESOURCES CHECKING: /resources" do before(:all) do @api = OarApi.new(APIURI) end context "(with limit=2)" do before(:all) do @api.get_hash("resources?limit=2") end it "should correctly limit the number of results" do @api.value['items'].length.should == 2 end it "should return 2 links" do @api.value['links'].length.should == 2 end it "should return a total > 4" do @api.value['total'].to_i.should > 4 end it "should return an offset of 0" do @api.value['offset'].to_i.should == 0 end it "should return a correct self link" do @api.get_self_link_href.should == "/oarapi-priv/resources?limit=2&offset=0" end it "should return a correct next link" do @api.get_next_link_href.should == "/oarapi-priv/resources?limit=2&offset=2" end end context "(with limit=2&offset=2)" do before(:all) do @api.get_hash("resources?limit=2&offset=2") end it "should correctly limit the number of results" do @api.value['items'].length.should == 2 end it "should return 3 links" do @api.value['links'].length.should == 3 end it "should return a total > 4" do @api.value['total'].to_i.should > 4 end it "should return an offset of 2" do @api.value['offset'].to_i.should == 2 end it "should return a correct self link" do @api.get_self_link_href.should == "/oarapi-priv/resources?limit=2&offset=2" end it "should return a correct next link" do @api.get_next_link_href.should == "/oarapi-priv/resources?limit=2&offset=4" end it "should return a correct previous link" do @api.get_previous_link_href.should == "/oarapi-priv/resources?limit=2&offset=0" end end context "(inside resource)" do before(:all) do @api.get_hash("resources?limit=2&offset=1") @api.value=@api.value['items'][0] end it_should_behave_like "Resource" end context "(with limit=1)" do before(:all) do @api.get_hash("resources?limit=1") end it_should_behave_like "All list structures" end context "(with limit=1, insider job)" do before(:all) do @api.get_hash("resources?limit=1") @api.value=@api.value['items'][0] end it_should_behave_like "Resource" end end describe "RESOURCES DETAILS CHECKING: /resources/3 data structure" do before(:all) do @api = OarApi.new(APIURI) @api.get_hash("resources/3") end it_should_behave_like "Resource" end describe "NON-EXISTENT RESOURCE" do before(:all) do @api = OarApi.new(APIURI) end it "should raise an exception" do lambda { @api.get(@api.api,"resources/00") }.should raise_exception end it "should return a 404 error" do begin @api.get(@api.api,"resources/00") rescue => e e.should respond_to('http_code') e.http_code.should == 404 end end end describe "NODE RESOURCES: /resources/nodes/" do before(:all) do @api = OarApi.new(APIURI) @api.get_hash("resources?limit=1") node_link=@api.get_link_href_from_array(@api.value["items"][0]["links"],"node") @api.get_hash(node_link) end context "(basic data structure)" do it_should_behave_like "All list structures" end context "(insider resource)" do before(:all) do @api.value=@api.value['items'][0] end it_should_behave_like "ResourceId" end end describe "RESOURCES JOBS: /resources/3/jobs" do before(:all) do @api = OarApi.new(APIURI) @api.get_hash("resources/3/jobs") end context "(basic data structure)" do it_should_behave_like "All list structures" end context "(inside job)" do before(:all) do @api.value=@api.value['items'][0] end it_should_behave_like "JobId" end end describe "NODE JOBS: /resources/nodes//jobs" do before(:all) do @api = OarApi.new(APIURI) @api.get_hash("resources?limit=1") node_link=@api.get_link_href_from_array(@api.value["items"][0]["links"],"node") @api.get_hash(node_link+"/jobs") end context "(basic data structure)" do it_should_behave_like "All list structures" end context "(inside job)" do before(:all) do @api.value=@api.value['items'][0] end it_should_behave_like "JobId" end end describe "NODE JOBS: /resources/nodes//jobs with state=Running filter" do before(:all) do @api = OarApi.new(APIURI) @api.get_hash("resources?limit=1") node_link=@api.get_link_href_from_array(@api.value["items"][0]["links"],"node") @api.get_hash(node_link+"/jobs?state=Running") end context "(basic data structure)" do it_should_behave_like "All list structures" end context "(inside job)" do before(:all) do @api.value=@api.value['items'][0] end it_should_behave_like "JobId" end end describe "NODE JOBS: /resources/nodes//jobs with state=Obiwan filter" do before(:all) do @api = OarApi.new(APIURI) @api.get_hash("resources?limit=1") node_link=@api.get_link_href_from_array(@api.value["items"][0]["links"],"node") @api.get_hash(node_link+"/jobs?state=Obiwan") end it "should return an empty list of jobs" do @api.value=@api.value['items'].length.should==0 end end ############################################################# # Job submission responses checks ############################################################# describe "Job submission" do it "should return a self link" do jhash = { 'resource' => "/nodes=1/core=1" , 'script' => "ls;pwd;whoami;sleep 60"} @api.submit_job(jhash) $ljobid = @api.jobstatus['id'] @api.value= @api.jobstatus @api.get_self_link_href.should == "/oarapi-priv/jobs/#{$ljobid}" end it "should return a 400 error on bad reservation date" do jhash = { 'resource' => "/nodes=1/core=1" , 'script' => "ls;pwd;whoami;sleep 60", 'reservation' => '1973-06-03 18:00:00' } begin @api.submit_job(jhash) rescue => e #puts e.response.body e.should respond_to('http_code') e.http_code.should == 400 end end end ############################################################# # Cleaning ############################################################# describe "Cleaning queue" do it "should delete the test jobs" do @api.del_array_job($jobid) @api.deletestatus['status'].should == "Delete request registered" end it "should delete the test reservation" do @api.del_array_job($rjobid) @api.deletestatus['status'].should == "Delete request registered" end it "should delete the test job" do @api.del_array_job($ljobid) @api.deletestatus['status'].should == "Delete request registered" end end ############################################################# # Resubmission ############################################################# describe "Re-submission of a terminated job" do before(:all) do # Wait for the 2nd job to be terminated timeout=180 t=0 c=0 while t e puts e.response.body end end it "should have a self link" do @api.get_self_link_href.should == "/oarapi-priv/jobs/#{@api.value['id']}" end it "should have a parent link" do @api.get_link_href_by_rel('parent').should == "/oarapi-priv/jobs/#{$jobid+1}" end it "should delete the test job (cleaning)" do @api.del_job(@api.value['id']) @api.deletestatus['status'].should == "Delete request registered" end end end ./oar-2.5.2/tests/rspec/Makefile0000644000175000017500000000053511757171206014502 0ustar plbplbSPEC_OPTS=--colour --format nested all: format_checking job_submit resources_creation usage: @echo "usage: make < format_checking | job_submit | resources_creation >" format_checking: rspec format_checking_spec.rb ${SPEC_OPTS} job_submit: rspec job_submit_spec.rb ${SPEC_OPTS} resources_creation: rspec resources_creation_spec.rb ${SPEC_OPTS} ./oar-2.5.2/tests/oarsub.expect.pl0000755000175000017500000000470611757171206015054 0ustar plbplb#!/usr/bin/perl -w # $Id$ use strict; use warnings; use Data::Dumper; use Expect; my $prompt = qr/^\w+@\w+-\d+:.+\$\s$/m; my $timeout = 10; my $command = "ssh -t rennes.g5k oarsub -I -l core=1,walltime=0:05:00"; my $exp = new Expect; $exp->raw_pty(0); $exp->log_user(1); $exp->exp_internal(0); $exp->restart_timeout_upon_receive(1); my $jobid; my $nodefile; my $cpuset; $exp ->spawn($command) or die "Cannot spawn $command: $!\n"; $exp->expect($timeout, [ qr/^Generate a job key...\r\n$/m => sub { my $exp = shift; print "--> OK generate job key\n"; exp_continue; } ], [ qr/^OAR_JOB_ID=(\d+)\r\n$/m => sub { my $exp = shift; $jobid = ($exp->matchlist())[0]; print "--> OK got job id: $jobid\n"; exp_continue; } ], [ $prompt => sub { my $exp = shift; # print "--> OK got prompt\n"; } ], [ eof => sub { my $exp = shift; die "--> Premature EOF !\n"; } ], [ timeout => sub { my $exp = shift; die "--> Timeout !\n"; } ] ); $exp->send("echo \$OAR_NODEFILE\n"); $exp->expect($timeout, [ qr/^(.+\/$jobid)\r$/m => sub { my $exp = shift; $nodefile = ($exp->matchlist())[0]; print "--> OK got nodefile: $nodefile\n"; exp_continue; } ], [ $prompt => sub { my $exp = shift; # print "--> OK got prompt\n"; } ], [ eof => sub { my $exp = shift; die "--> Premature EOF !\n"; } ], [ timeout => sub { my $exp = shift; die "--> Timeout !\n"; } ] ); $exp->send("cat /proc/self/cpuset\n"); $exp->expect($timeout, [ qr/^(\/oar\/\w+_$jobid)\r$/m => sub { my $exp = shift; $cpuset = ($exp->matchlist())[0]; print "--> OK got cpuset: $cpuset\n"; exp_continue; } ], [ $prompt => sub { my $exp = shift; # print "--> OK got prompt\n"; } ], [ eof => sub { my $exp = shift; die "--> Premature EOF !\n"; } ], [ timeout => sub { my $exp = shift; die "--> Timeout !\n"; } ] ); #print "--> Entering interactive session\n"; #$exp->interact(\*STDIN, '\cq' ); #print "--> Interactive session ended\n"; $exp->send(eof); ./oar-2.5.2/tests/oarsub.expect0000755000175000017500000000112011757171206014425 0ustar plbplb#!/usr/bin/expect set user "pneyron" set prompt "${user}@\\w+-\\d+:.+\$$" log_user 0 spawn ssh -t genepi.g5k oarsub -I set timeout 60 expect "Generate a job key..." { send_user "OK generate job key\n" } expect -re "OAR_JOB_ID=\(\\d+\)" { set jobid $expect_out(1,string) send_user "OK got jobid: $jobid\n" } expect -re $prompt send "ls -l /var/lib/oar/${jobid}\n" expect -re "-rw-r--r-- 1 oar oar" { send_user "OK nodefile is readable\n" } expect -re $prompt send "cat /proc/self/cpuset\n" expect "/oar/${user}_${jobid}" { send_user "OK got correct cpuset\n" } #interact send eof ./oar-2.5.2/sources/0000755000175000017500000000000011757171206012244 5ustar plbplb./oar-2.5.2/sources/visualization_interfaces/0000755000175000017500000000000011757171206017350 5ustar plbplb./oar-2.5.2/sources/visualization_interfaces/apache.conf.in0000644000175000017500000000027211757171206022046 0ustar plbplbScriptAlias /monika %%CGIDIR%%/monika.cgi ScriptAlias /drawgantt %%CGIDIR%%/drawgantt.cgi Alias /monika.css %%WWWDIR%%/monika.css Alias /drawgantt-files %%OARHOMEDIR%%/drawgantt-files ./oar-2.5.2/sources/visualization_interfaces/Monika/0000755000175000017500000000000011757171206020566 5ustar plbplb./oar-2.5.2/sources/visualization_interfaces/Monika/userInfos.cgi0000755000175000017500000000036011757171206023231 0ustar plbplb#!/bin/bash echo Content-type: text/plain echo echo "# id $QUERY_STRING" id "$QUERY_STRING" #echo #echo "# oarstat -u $QUERY_STRING --accounting '2006-03-30, 2008-04-30'" #oarstat -u "$QUERY_STRING" --accounting '2000-01-01, 2010-12-31' ./oar-2.5.2/sources/visualization_interfaces/Monika/overlib.js0000755000175000017500000012503011757171206022572 0ustar plbplb//\////////////////////////////////////////////////////////////////////////////////// //\ overLIB 3.51 -- This notice must remain untouched at all times. //\ Copyright Erik Bosrup 1998-2002. All rights reserved. //\ //\ By Erik Bosrup (erik@bosrup.com). Last modified 2002-11-01. //\ Portions by Dan Steinman (dansteinman.com). Additions by other people are //\ listed on the overLIB homepage. //\ //\ Get the latest version at http://www.bosrup.com/web/overlib/ //\ //\ This script is published under an open source license. Please read the license //\ agreement online at: http://www.bosrup.com/web/overlib/license.html //\ If you have questions regarding the license please contact erik@bosrup.com. //\ //\ This script library was originally created for personal use. By request it has //\ later been made public. This is free software. Do not sell this as your own //\ work, or remove this copyright notice. For full details on copying or changing //\ this script please read the license agreement at the link above. //\ //\ Please give credit on sites that use overLIB and submit changes of the script //\ so other people can use them as well. This script is free to use, don't abuse. //\////////////////////////////////////////////////////////////////////////////////// //\mini //////////////////////////////////////////////////////////////////////////////////// // CONSTANTS // Don't touch these. :) //////////////////////////////////////////////////////////////////////////////////// var INARRAY = 1; var CAPARRAY = 2; var STICKY = 3; var BACKGROUND = 4; var NOCLOSE = 5; var CAPTION = 6; var LEFT = 7; var RIGHT = 8; var CENTER = 9; var OFFSETX = 10; var OFFSETY = 11; var FGCOLOR = 12; var BGCOLOR = 13; var TEXTCOLOR = 14; var CAPCOLOR = 15; var CLOSECOLOR = 16; var WIDTH = 17; var BORDER = 18; var STATUS = 19; var AUTOSTATUS = 20; var AUTOSTATUSCAP = 21; var HEIGHT = 22; var CLOSETEXT = 23; var SNAPX = 24; var SNAPY = 25; var FIXX = 26; var FIXY = 27; var FGBACKGROUND = 28; var BGBACKGROUND = 29; var PADX = 30; // PADX2 out var PADY = 31; // PADY2 out var FULLHTML = 34; var ABOVE = 35; var BELOW = 36; var CAPICON = 37; var TEXTFONT = 38; var CAPTIONFONT = 39; var CLOSEFONT = 40; var TEXTSIZE = 41; var CAPTIONSIZE = 42; var CLOSESIZE = 43; var FRAME = 44; var TIMEOUT = 45; var FUNCTION = 46; var DELAY = 47; var HAUTO = 48; var VAUTO = 49; var CLOSECLICK = 50; var CSSOFF = 51; var CSSSTYLE = 52; var CSSCLASS = 53; var FGCLASS = 54; var BGCLASS = 55; var TEXTFONTCLASS = 56; var CAPTIONFONTCLASS = 57; var CLOSEFONTCLASS = 58; var PADUNIT = 59; var HEIGHTUNIT = 60; var WIDTHUNIT = 61; var TEXTSIZEUNIT = 62; var TEXTDECORATION = 63; var TEXTSTYLE = 64; var TEXTWEIGHT = 65; var CAPTIONSIZEUNIT = 66; var CAPTIONDECORATION = 67; var CAPTIONSTYLE = 68; var CAPTIONWEIGHT = 69; var CLOSESIZEUNIT = 70; var CLOSEDECORATION = 71; var CLOSESTYLE = 72; var CLOSEWEIGHT = 73; //////////////////////////////////////////////////////////////////////////////////// // DEFAULT CONFIGURATION // You don't have to change anything here if you don't want to. All of this can be // changed on your html page or through an overLIB call. //////////////////////////////////////////////////////////////////////////////////// // Main background color (the large area) // Usually a bright color (white, yellow etc) if (typeof ol_fgcolor == 'undefined') { var ol_fgcolor = "#CCCCFF";} // Border color and color of caption // Usually a dark color (black, brown etc) if (typeof ol_bgcolor == 'undefined') { var ol_bgcolor = "#333399";} // Text color // Usually a dark color if (typeof ol_textcolor == 'undefined') { var ol_textcolor = "#000000";} // Color of the caption text // Usually a bright color if (typeof ol_capcolor == 'undefined') { var ol_capcolor = "#FFFFFF";} // Color of "Close" when using Sticky // Usually a semi-bright color if (typeof ol_closecolor == 'undefined') { var ol_closecolor = "#9999FF";} // Font face for the main text if (typeof ol_textfont == 'undefined') { var ol_textfont = "Verdana,Arial,Helvetica";} // Font face for the caption if (typeof ol_captionfont == 'undefined') { var ol_captionfont = "Verdana,Arial,Helvetica";} // Font face for the close text if (typeof ol_closefont == 'undefined') { var ol_closefont = "Verdana,Arial,Helvetica";} // Font size for the main text // When using CSS this will be very small. if (typeof ol_textsize == 'undefined') { var ol_textsize = "2";} // Font size for the caption // When using CSS this will be very small. if (typeof ol_captionsize == 'undefined') { var ol_captionsize = "1";} // Font size for the close text // When using CSS this will be very small. if (typeof ol_closesize == 'undefined') { var ol_closesize = "1";} // Width of the popups in pixels // 100-300 pixels is typical if (typeof ol_width == 'undefined') { var ol_width = "200";} // How thick the ol_border should be in pixels // 1-3 pixels is typical if (typeof ol_border == 'undefined') { var ol_border = "1";} // How many pixels to the right/left of the cursor to show the popup // Values between 3 and 12 are best if (typeof ol_offsetx == 'undefined') { var ol_offsetx = 10;} // How many pixels to the below the cursor to show the popup // Values between 3 and 12 are best if (typeof ol_offsety == 'undefined') { var ol_offsety = 10;} // Default text for popups // Should you forget to pass something to overLIB this will be displayed. if (typeof ol_text == 'undefined') { var ol_text = "Default Text"; } // Default caption // You should leave this blank or you will have problems making non caps popups. if (typeof ol_cap == 'undefined') { var ol_cap = ""; } // Decides if sticky popups are default. // 0 for non, 1 for stickies. if (typeof ol_sticky == 'undefined') { var ol_sticky = 0; } // Default background image. Better left empty unless you always want one. if (typeof ol_background == 'undefined') { var ol_background = ""; } // Text for the closing sticky popups. // Normal is "Close". if (typeof ol_close == 'undefined') { var ol_close = "Close"; } // Default vertical alignment for popups. // It's best to leave RIGHT here. Other options are LEFT and CENTER. if (typeof ol_hpos == 'undefined') { var ol_hpos = RIGHT; } // Default status bar text when a popup is invoked. if (typeof ol_status == 'undefined') { var ol_status = ""; } // If the status bar automatically should load either text or caption. // 0=nothing, 1=text, 2=caption if (typeof ol_autostatus == 'undefined') { var ol_autostatus = 0; } // Default height for popup. Often best left alone. if (typeof ol_height == 'undefined') { var ol_height = -1; } // Horizontal grid spacing that popups will snap to. // 0 makes no grid, anything else will cause a snap to that grid spacing. if (typeof ol_snapx == 'undefined') { var ol_snapx = 0; } // Vertical grid spacing that popups will snap to. // 0 makes no grid, andthing else will cause a snap to that grid spacing. if (typeof ol_snapy == 'undefined') { var ol_snapy = 0; } // Sets the popups horizontal position to a fixed column. // Anything above -1 will cause fixed position. if (typeof ol_fixx == 'undefined') { var ol_fixx = -1; } // Sets the popups vertical position to a fixed row. // Anything above -1 will cause fixed position. if (typeof ol_fixy == 'undefined') { var ol_fixy = -1; } // Background image for the popups inside. if (typeof ol_fgbackground == 'undefined') { var ol_fgbackground = ""; } // Background image for the popups frame. if (typeof ol_bgbackground == 'undefined') { var ol_bgbackground = ""; } // How much horizontal left padding text should get by default when BACKGROUND is used. if (typeof ol_padxl == 'undefined') { var ol_padxl = 1; } // How much horizontal right padding text should get by default when BACKGROUND is used. if (typeof ol_padxr == 'undefined') { var ol_padxr = 1; } // How much vertical top padding text should get by default when BACKGROUND is used. if (typeof ol_padyt == 'undefined') { var ol_padyt = 1; } // How much vertical bottom padding text should get by default when BACKGROUND is used. if (typeof ol_padyb == 'undefined') { var ol_padyb = 1; } // If the user by default must supply all html for complete popup control. // Set to 1 to activate, 0 otherwise. if (typeof ol_fullhtml == 'undefined') { var ol_fullhtml = 0; } // Default vertical position of the popup. Default should normally be BELOW. // ABOVE only works when HEIGHT is defined. if (typeof ol_vpos == 'undefined') { var ol_vpos = BELOW; } // Default height of popup to use when placing the popup above the cursor. if (typeof ol_aboveheight == 'undefined') { var ol_aboveheight = 0; } // Default icon to place next to the popups caption. if (typeof ol_capicon == 'undefined') { var ol_capicon = ""; } // Default frame. We default to current frame if there is no frame defined. if (typeof ol_frame == 'undefined') { var ol_frame = self; } // Default timeout. By default there is no timeout. if (typeof ol_timeout == 'undefined') { var ol_timeout = 0; } // Default javascript funktion. By default there is none. if (typeof ol_function == 'undefined') { var ol_function = null; } // Default timeout. By default there is no timeout. if (typeof ol_delay == 'undefined') { var ol_delay = 0; } // If overLIB should decide the horizontal placement. if (typeof ol_hauto == 'undefined') { var ol_hauto = 0; } // If overLIB should decide the vertical placement. if (typeof ol_vauto == 'undefined') { var ol_vauto = 0; } // If the user has to click to close stickies. if (typeof ol_closeclick == 'undefined') { var ol_closeclick = 0; } // This variable determines if you want to use CSS or inline definitions. // CSSOFF=no CSS CSSSTYLE=use CSS inline styles CSSCLASS=use classes if (typeof ol_css == 'undefined') { var ol_css = CSSOFF; } // Main background class (eqv of fgcolor) // This is only used if CSS is set to use classes (ol_css = CSSCLASS) if (typeof ol_fgclass == 'undefined') { var ol_fgclass = ""; } // Frame background class (eqv of bgcolor) // This is only used if CSS is set to use classes (ol_css = CSSCLASS) if (typeof ol_bgclass == 'undefined') { var ol_bgclass = ""; } // Main font class // This is only used if CSS is set to use classes (ol_css = CSSCLASS) if (typeof ol_textfontclass == 'undefined') { var ol_textfontclass = ""; } // Caption font class // This is only used if CSS is set to use classes (ol_css = CSSCLASS) if (typeof ol_captionfontclass == 'undefined') { var ol_captionfontclass = ""; } // Close font class // This is only used if CSS is set to use classes (ol_css = CSSCLASS) if (typeof ol_closefontclass == 'undefined') { var ol_closefontclass = ""; } // Unit to be used for the text padding above // Only used if CSS inline styles are being used (ol_css = CSSSTYLE) // Options include "px", "%", "in", "cm" if (typeof ol_padunit == 'undefined') { var ol_padunit = "px";} // Unit to be used for height of popup // Only used if CSS inline styles are being used (ol_css = CSSSTYLE) // Options include "px", "%", "in", "cm" if (typeof ol_heightunit == 'undefined') { var ol_heightunit = "px";} // Unit to be used for width of popup // Only used if CSS inline styles are being used (ol_css = CSSSTYLE) // Options include "px", "%", "in", "cm" if (typeof ol_widthunit == 'undefined') { var ol_widthunit = "px";} // Font size unit for the main text // Only used if CSS inline styles are being used (ol_css = CSSSTYLE) if (typeof ol_textsizeunit == 'undefined') { var ol_textsizeunit = "px";} // Decoration of the main text ("none", "underline", "line-through" or "blink") // Only used if CSS inline styles are being used (ol_css = CSSSTYLE) if (typeof ol_textdecoration == 'undefined') { var ol_textdecoration = "none";} // Font style of the main text ("normal" or "italic") // Only used if CSS inline styles are being used (ol_css = CSSSTYLE) if (typeof ol_textstyle == 'undefined') { var ol_textstyle = "normal";} // Font weight of the main text ("normal", "bold", "bolder", "lighter", ect.) // Only used if CSS inline styles are being used (ol_css = CSSSTYLE) if (typeof ol_textweight == 'undefined') { var ol_textweight = "normal";} // Font size unit for the caption // Only used if CSS inline styles are being used (ol_css = CSSSTYLE) if (typeof ol_captionsizeunit == 'undefined') { var ol_captionsizeunit = "px";} // Decoration of the caption ("none", "underline", "line-through" or "blink") // Only used if CSS inline styles are being used (ol_css = CSSSTYLE) if (typeof ol_captiondecoration == 'undefined') { var ol_captiondecoration = "none";} // Font style of the caption ("normal" or "italic") // Only used if CSS inline styles are being used (ol_css = CSSSTYLE) if (typeof ol_captionstyle == 'undefined') { var ol_captionstyle = "normal";} // Font weight of the caption ("normal", "bold", "bolder", "lighter", ect.) // Only used if CSS inline styles are being used (ol_css = CSSSTYLE) if (typeof ol_captionweight == 'undefined') { var ol_captionweight = "bold";} // Font size unit for the close text // Only used if CSS inline styles are being used (ol_css = CSSSTYLE) if (typeof ol_closesizeunit == 'undefined') { var ol_closesizeunit = "px";} // Decoration of the close text ("none", "underline", "line-through" or "blink") // Only used if CSS inline styles are being used (ol_css = CSSSTYLE) if (typeof ol_closedecoration == 'undefined') { var ol_closedecoration = "none";} // Font style of the close text ("normal" or "italic") // Only used if CSS inline styles are being used (ol_css = CSSSTYLE) if (typeof ol_closestyle == 'undefined') { var ol_closestyle = "normal";} // Font weight of the close text ("normal", "bold", "bolder", "lighter", ect.) // Only used if CSS inline styles are being used (ol_css = CSSSTYLE) if (typeof ol_closeweight == 'undefined') { var ol_closeweight = "normal";} //////////////////////////////////////////////////////////////////////////////////// // ARRAY CONFIGURATION // You don't have to change anything here if you don't want to. The following // arrays can be filled with text and html if you don't wish to pass it from // your html page. //////////////////////////////////////////////////////////////////////////////////// // Array with texts. if (typeof ol_texts == 'undefined') { var ol_texts = new Array("Text 0", "Text 1"); } // Array with captions. if (typeof ol_caps == 'undefined') { var ol_caps = new Array("Caption 0", "Caption 1"); } //////////////////////////////////////////////////////////////////////////////////// // END CONFIGURATION // Don't change anything below this line, all configuration is above. //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// // INIT //////////////////////////////////////////////////////////////////////////////////// // Runtime variables init. Used for runtime only, don't change, not for config! var o3_text = ""; var o3_cap = ""; var o3_sticky = 0; var o3_background = ""; var o3_close = "Close"; var o3_hpos = RIGHT; var o3_offsetx = 2; var o3_offsety = 2; var o3_fgcolor = ""; var o3_bgcolor = ""; var o3_textcolor = ""; var o3_capcolor = ""; var o3_closecolor = ""; var o3_width = 100; var o3_border = 1; var o3_status = ""; var o3_autostatus = 0; var o3_height = -1; var o3_snapx = 0; var o3_snapy = 0; var o3_fixx = -1; var o3_fixy = -1; var o3_fgbackground = ""; var o3_bgbackground = ""; var o3_padxl = 0; var o3_padxr = 0; var o3_padyt = 0; var o3_padyb = 0; var o3_fullhtml = 0; var o3_vpos = BELOW; var o3_aboveheight = 0; var o3_capicon = ""; var o3_textfont = "Verdana,Arial,Helvetica"; var o3_captionfont = "Verdana,Arial,Helvetica"; var o3_closefont = "Verdana,Arial,Helvetica"; var o3_textsize = "1"; var o3_captionsize = "1"; var o3_closesize = "1"; var o3_frame = self; var o3_timeout = 0; var o3_timerid = 0; var o3_allowmove = 0; var o3_function = null; var o3_delay = 0; var o3_delayid = 0; var o3_hauto = 0; var o3_vauto = 0; var o3_closeclick = 0; var o3_css = CSSOFF; var o3_fgclass = ""; var o3_bgclass = ""; var o3_textfontclass = ""; var o3_captionfontclass = ""; var o3_closefontclass = ""; var o3_padunit = "px"; var o3_heightunit = "px"; var o3_widthunit = "px"; var o3_textsizeunit = "px"; var o3_textdecoration = ""; var o3_textstyle = ""; var o3_textweight = ""; var o3_captionsizeunit = "px"; var o3_captiondecoration = ""; var o3_captionstyle = ""; var o3_captionweight = ""; var o3_closesizeunit = "px"; var o3_closedecoration = ""; var o3_closestyle = ""; var o3_closeweight = ""; // Display state variables var o3_x = 0; var o3_y = 0; var o3_allow = 0; var o3_showingsticky = 0; var o3_removecounter = 0; // Our layer var over = null; var fnRef; // Decide browser version var ns4 = (navigator.appName == 'Netscape' && parseInt(navigator.appVersion) == 4); var ns6 = (document.getElementById)? true:false; var ie4 = (document.all)? true:false; if (ie4) var docRoot = 'document.body'; var ie5 = false; if (ns4) { var oW = window.innerWidth; var oH = window.innerHeight; window.onresize = function () {if (oW!=window.innerWidth||oH!=window.innerHeight) location.reload();} } // Microsoft Stupidity Check(tm). if (ie4) { if ((navigator.userAgent.indexOf('MSIE 5') > 0) || (navigator.userAgent.indexOf('MSIE 6') > 0)) { if(document.compatMode && document.compatMode == 'CSS1Compat') docRoot = 'document.documentElement'; ie5 = true; } if (ns6) { ns6 = false; } } // Capture events, alt. diffuses the overlib function. if ( (ns4) || (ie4) || (ns6)) { document.onmousemove = mouseMove if (ns4) document.captureEvents(Event.MOUSEMOVE) } else { overlib = no_overlib; nd = no_overlib; ver3fix = true; } // Fake function for 3.0 users. function no_overlib() { return ver3fix; } //////////////////////////////////////////////////////////////////////////////////// // PUBLIC FUNCTIONS //////////////////////////////////////////////////////////////////////////////////// // overlib(arg0, ..., argN) // Loads parameters into global runtime variables. function overlib() { // Load defaults to runtime. o3_text = ol_text; o3_cap = ol_cap; o3_sticky = ol_sticky; o3_background = ol_background; o3_close = ol_close; o3_hpos = ol_hpos; o3_offsetx = ol_offsetx; o3_offsety = ol_offsety; o3_fgcolor = ol_fgcolor; o3_bgcolor = ol_bgcolor; o3_textcolor = ol_textcolor; o3_capcolor = ol_capcolor; o3_closecolor = ol_closecolor; o3_width = ol_width; o3_border = ol_border; o3_status = ol_status; o3_autostatus = ol_autostatus; o3_height = ol_height; o3_snapx = ol_snapx; o3_snapy = ol_snapy; o3_fixx = ol_fixx; o3_fixy = ol_fixy; o3_fgbackground = ol_fgbackground; o3_bgbackground = ol_bgbackground; o3_padxl = ol_padxl; o3_padxr = ol_padxr; o3_padyt = ol_padyt; o3_padyb = ol_padyb; o3_fullhtml = ol_fullhtml; o3_vpos = ol_vpos; o3_aboveheight = ol_aboveheight; o3_capicon = ol_capicon; o3_textfont = ol_textfont; o3_captionfont = ol_captionfont; o3_closefont = ol_closefont; o3_textsize = ol_textsize; o3_captionsize = ol_captionsize; o3_closesize = ol_closesize; o3_timeout = ol_timeout; o3_function = ol_function; o3_delay = ol_delay; o3_hauto = ol_hauto; o3_vauto = ol_vauto; o3_closeclick = ol_closeclick; o3_css = ol_css; o3_fgclass = ol_fgclass; o3_bgclass = ol_bgclass; o3_textfontclass = ol_textfontclass; o3_captionfontclass = ol_captionfontclass; o3_closefontclass = ol_closefontclass; o3_padunit = ol_padunit; o3_heightunit = ol_heightunit; o3_widthunit = ol_widthunit; o3_textsizeunit = ol_textsizeunit; o3_textdecoration = ol_textdecoration; o3_textstyle = ol_textstyle; o3_textweight = ol_textweight; o3_captionsizeunit = ol_captionsizeunit; o3_captiondecoration = ol_captiondecoration; o3_captionstyle = ol_captionstyle; o3_captionweight = ol_captionweight; o3_closesizeunit = ol_closesizeunit; o3_closedecoration = ol_closedecoration; o3_closestyle = ol_closestyle; o3_closeweight = ol_closeweight; fnRef = ''; // Special for frame support, over must be reset... if ( (ns4) || (ie4) || (ns6) ) { if (over) cClick(); o3_frame = ol_frame; if (ns4) over = o3_frame.document.overDiv if (ie4) over = o3_frame.overDiv.style if (ns6) over = o3_frame.document.getElementById("overDiv"); } // What the next argument is expected to be. var parsemode = -1, udf, v = null; var ar = arguments; udf = (!ar.length ? 1 : 0); for (i = 0; i < ar.length; i++) { if (parsemode < 0) { // Arg is maintext, unless its a PARAMETER if (typeof ar[i] == 'number') { udf = (ar[i] == FUNCTION ? 0 : 1); i--; } else { o3_text = ar[i]; } parsemode = 0; } else { // Note: NS4 doesn't like switch cases with vars. if (ar[i] == INARRAY) { udf = 0; o3_text = ol_texts[ar[++i]]; continue; } if (ar[i] == CAPARRAY) { o3_cap = ol_caps[ar[++i]]; continue; } if (ar[i] == STICKY) { o3_sticky = 1; continue; } if (ar[i] == BACKGROUND) { o3_background = ar[++i]; continue; } if (ar[i] == NOCLOSE) { o3_close = ""; continue; } if (ar[i] == CAPTION) { o3_cap = ar[++i]; continue; } if (ar[i] == CENTER || ar[i] == LEFT || ar[i] == RIGHT) { o3_hpos = ar[i]; continue; } if (ar[i] == OFFSETX) { o3_offsetx = ar[++i]; continue; } if (ar[i] == OFFSETY) { o3_offsety = ar[++i]; continue; } if (ar[i] == FGCOLOR) { o3_fgcolor = ar[++i]; continue; } if (ar[i] == BGCOLOR) { o3_bgcolor = ar[++i]; continue; } if (ar[i] == TEXTCOLOR) { o3_textcolor = ar[++i]; continue; } if (ar[i] == CAPCOLOR) { o3_capcolor = ar[++i]; continue; } if (ar[i] == CLOSECOLOR) { o3_closecolor = ar[++i]; continue; } if (ar[i] == WIDTH) { o3_width = ar[++i]; continue; } if (ar[i] == BORDER) { o3_border = ar[++i]; continue; } if (ar[i] == STATUS) { o3_status = ar[++i]; continue; } if (ar[i] == AUTOSTATUS) { o3_autostatus = (o3_autostatus == 1) ? 0 : 1; continue; } if (ar[i] == AUTOSTATUSCAP) { o3_autostatus = (o3_autostatus == 2) ? 0 : 2; continue; } if (ar[i] == HEIGHT) { o3_height = ar[++i]; o3_aboveheight = ar[i]; continue; } // Same param again. if (ar[i] == CLOSETEXT) { o3_close = ar[++i]; continue; } if (ar[i] == SNAPX) { o3_snapx = ar[++i]; continue; } if (ar[i] == SNAPY) { o3_snapy = ar[++i]; continue; } if (ar[i] == FIXX) { o3_fixx = ar[++i]; continue; } if (ar[i] == FIXY) { o3_fixy = ar[++i]; continue; } if (ar[i] == FGBACKGROUND) { o3_fgbackground = ar[++i]; continue; } if (ar[i] == BGBACKGROUND) { o3_bgbackground = ar[++i]; continue; } if (ar[i] == PADX) { o3_padxl = ar[++i]; o3_padxr = ar[++i]; continue; } if (ar[i] == PADY) { o3_padyt = ar[++i]; o3_padyb = ar[++i]; continue; } if (ar[i] == FULLHTML) { o3_fullhtml = 1; continue; } if (ar[i] == BELOW || ar[i] == ABOVE) { o3_vpos = ar[i]; continue; } if (ar[i] == CAPICON) { o3_capicon = ar[++i]; continue; } if (ar[i] == TEXTFONT) { o3_textfont = ar[++i]; continue; } if (ar[i] == CAPTIONFONT) { o3_captionfont = ar[++i]; continue; } if (ar[i] == CLOSEFONT) { o3_closefont = ar[++i]; continue; } if (ar[i] == TEXTSIZE) { o3_textsize = ar[++i]; continue; } if (ar[i] == CAPTIONSIZE) { o3_captionsize = ar[++i]; continue; } if (ar[i] == CLOSESIZE) { o3_closesize = ar[++i]; continue; } if (ar[i] == FRAME) { opt_FRAME(ar[++i]); continue; } if (ar[i] == TIMEOUT) { o3_timeout = ar[++i]; continue; } if (ar[i] == FUNCTION) { udf = 0; if (typeof ar[i+1] != 'number') v = ar[++i]; opt_FUNCTION(v); continue; } if (ar[i] == DELAY) { o3_delay = ar[++i]; continue; } if (ar[i] == HAUTO) { o3_hauto = (o3_hauto == 0) ? 1 : 0; continue; } if (ar[i] == VAUTO) { o3_vauto = (o3_vauto == 0) ? 1 : 0; continue; } if (ar[i] == CLOSECLICK) { o3_closeclick = (o3_closeclick == 0) ? 1 : 0; continue; } if (ar[i] == CSSOFF) { o3_css = ar[i]; continue; } if (ar[i] == CSSSTYLE) { o3_css = ar[i]; continue; } if (ar[i] == CSSCLASS) { o3_css = ar[i]; continue; } if (ar[i] == FGCLASS) { o3_fgclass = ar[++i]; continue; } if (ar[i] == BGCLASS) { o3_bgclass = ar[++i]; continue; } if (ar[i] == TEXTFONTCLASS) { o3_textfontclass = ar[++i]; continue; } if (ar[i] == CAPTIONFONTCLASS) { o3_captionfontclass = ar[++i]; continue; } if (ar[i] == CLOSEFONTCLASS) { o3_closefontclass = ar[++i]; continue; } if (ar[i] == PADUNIT) { o3_padunit = ar[++i]; continue; } if (ar[i] == HEIGHTUNIT) { o3_heightunit = ar[++i]; continue; } if (ar[i] == WIDTHUNIT) { o3_widthunit = ar[++i]; continue; } if (ar[i] == TEXTSIZEUNIT) { o3_textsizeunit = ar[++i]; continue; } if (ar[i] == TEXTDECORATION) { o3_textdecoration = ar[++i]; continue; } if (ar[i] == TEXTSTYLE) { o3_textstyle = ar[++i]; continue; } if (ar[i] == TEXTWEIGHT) { o3_textweight = ar[++i]; continue; } if (ar[i] == CAPTIONSIZEUNIT) { o3_captionsizeunit = ar[++i]; continue; } if (ar[i] == CAPTIONDECORATION) { o3_captiondecoration = ar[++i]; continue; } if (ar[i] == CAPTIONSTYLE) { o3_captionstyle = ar[++i]; continue; } if (ar[i] == CAPTIONWEIGHT) { o3_captionweight = ar[++i]; continue; } if (ar[i] == CLOSESIZEUNIT) { o3_closesizeunit = ar[++i]; continue; } if (ar[i] == CLOSEDECORATION) { o3_closedecoration = ar[++i]; continue; } if (ar[i] == CLOSESTYLE) { o3_closestyle = ar[++i]; continue; } if (ar[i] == CLOSEWEIGHT) { o3_closeweight = ar[++i]; continue; } } } if (udf && o3_function) o3_text = o3_function(); if (o3_delay == 0) { return overlib351(); } else { o3_delayid = setTimeout("overlib351()", o3_delay); return false; } } // Clears popups if appropriate function nd() { if ( o3_removecounter >= 1 ) { o3_showingsticky = 0 }; if ( (ns4) || (ie4) || (ns6) ) { if ( o3_showingsticky == 0 ) { o3_allowmove = 0; if (over != null) hideObject(over); } else { o3_removecounter++; } } return true; } //////////////////////////////////////////////////////////////////////////////////// // OVERLIB 3.51 FUNCTION //////////////////////////////////////////////////////////////////////////////////// // This function decides what it is we want to display and how we want it done. function overlib351() { // Make layer content var layerhtml; if (o3_background != "" || o3_fullhtml) { // Use background instead of box. layerhtml = ol_content_background(o3_text, o3_background, o3_fullhtml); } else { // They want a popup box. // Prepare popup background if (o3_fgbackground != "" && o3_css == CSSOFF) { o3_fgbackground = "BACKGROUND=\""+o3_fgbackground+"\""; } if (o3_bgbackground != "" && o3_css == CSSOFF) { o3_bgbackground = "BACKGROUND=\""+o3_bgbackground+"\""; } // Prepare popup colors if (o3_fgcolor != "" && o3_css == CSSOFF) { o3_fgcolor = "BGCOLOR=\""+o3_fgcolor+"\""; } if (o3_bgcolor != "" && o3_css == CSSOFF) { o3_bgcolor = "BGCOLOR=\""+o3_bgcolor+"\""; } // Prepare popup height if (o3_height > 0 && o3_css == CSSOFF) { o3_height = "HEIGHT=" + o3_height; } else { o3_height = ""; } // Decide which kinda box. if (o3_cap == "") { // Plain layerhtml = ol_content_simple(o3_text); } else { // With caption if (o3_sticky) { // Show close text layerhtml = ol_content_caption(o3_text, o3_cap, o3_close); } else { // No close text layerhtml = ol_content_caption(o3_text, o3_cap, ""); } } } // We want it to stick! if (o3_sticky) { if (o3_timerid > 0) { clearTimeout(o3_timerid); o3_timerid = 0; } o3_showingsticky = 1; o3_removecounter = 0; } // Write layer layerWrite(layerhtml); // Prepare status bar if (o3_autostatus > 0) { o3_status = o3_text; if (o3_autostatus > 1) { o3_status = o3_cap; } } // When placing the layer the first time, even stickies may be moved. o3_allowmove = 0; // Initiate a timer for timeout if (o3_timeout > 0) { if (o3_timerid > 0) clearTimeout(o3_timerid); o3_timerid = setTimeout("cClick()", o3_timeout); } // Show layer disp(o3_status); // Stickies should stay where they are. if (o3_sticky) o3_allowmove = 0; return (o3_status != ''); } //////////////////////////////////////////////////////////////////////////////////// // LAYER GENERATION FUNCTIONS //////////////////////////////////////////////////////////////////////////////////// // Makes simple table without caption function ol_content_simple(text) { if (o3_css == CSSCLASS) txt = "
"+text+"
"; if (o3_css == CSSSTYLE) txt = "
"+text+"
"; if (o3_css == CSSOFF) txt = "
"+text+"
"; set_background(""); return txt; } // Makes table with caption and optional close link function ol_content_caption(text, title, close) { closing = ""; closeevent = "onMouseOver"; if (o3_closeclick == 1) closeevent = "onClick"; if (o3_capicon != "") o3_capicon = " "; if (close != "") { if (o3_css == CSSCLASS) closing = "
"+close+""; if (o3_css == CSSSTYLE) closing = ""+close+""; if (o3_css == CSSOFF) closing = ""+close+""; } if (o3_css == CSSCLASS) txt = "
"+closing+"
"+o3_capicon+title+"
"+text+"
"; if (o3_css == CSSSTYLE) txt = "
"+closing+"
"+o3_capicon+title+"
"+text+"
"; if (o3_css == CSSOFF) txt = "
"+closing+"
"+o3_capicon+title+"
"+text+"
"; set_background(""); return txt; } // Sets the background picture, padding and lots more. :) function ol_content_background(text, picture, hasfullhtml) { var txt; if (hasfullhtml) { txt = text; } else { var pU, hU, wU; pU = (o3_padunit == '%' ? '%' : ''); hU = (o3_heightunit == '%' ? '%' : ''); wU = (o3_widthunit == '%' ? '%' : ''); if (o3_css == CSSCLASS) txt = "
"+text+"
"; if (o3_css == CSSSTYLE) txt = "
"+text+"
"; if (o3_css == CSSOFF) txt = "
"+text+"
"; } set_background(picture); return txt; } // Loads a picture into the div. function set_background(pic) { if (pic == "") { if (ns4) over.background.src = null; if (ie4) over.backgroundImage = "none"; if (ns6) over.style.backgroundImage = "none"; } else { if (ns4) { over.background.src = pic; } else if (ie4) { over.backgroundImage = "url("+pic+")"; } else if (ns6) { over.style.backgroundImage = "url("+pic+")"; } } } //////////////////////////////////////////////////////////////////////////////////// // HANDLING FUNCTIONS //////////////////////////////////////////////////////////////////////////////////// // Displays the popup function disp(statustext) { if ( (ns4) || (ie4) || (ns6) ) { if (o3_allowmove == 0) { placeLayer(); showObject(over); o3_allowmove = 1; } } if (statustext != "") { self.status = statustext; } } // Decides where we want the popup. function placeLayer() { var placeX, placeY; // HORIZONTAL PLACEMENT if (o3_fixx > -1) { // Fixed position placeX = o3_fixx; } else { winoffset = (ie4) ? eval('o3_frame.'+docRoot+'.scrollLeft') : o3_frame.pageXOffset; if (ie4) iwidth = eval('o3_frame.'+docRoot+'.clientWidth'); if (ns4 || ns6) iwidth = o3_frame.innerWidth; // If HAUTO, decide what to use. if (o3_hauto == 1) { if ( (o3_x - winoffset) > ((eval(iwidth)) / 2)) { o3_hpos = LEFT; } else { o3_hpos = RIGHT; } } // From mouse if (o3_hpos == CENTER) { // Center placeX = o3_x+o3_offsetx-(o3_width/2); if (placeX < winoffset) placeX = winoffset; } if (o3_hpos == RIGHT) { // Right placeX = o3_x+o3_offsetx; if ( (eval(placeX) + eval(o3_width)) > (winoffset + iwidth) ) { placeX = iwidth + winoffset - o3_width; if (placeX < 0) placeX = 0; } } if (o3_hpos == LEFT) { // Left placeX = o3_x-o3_offsetx-o3_width; if (placeX < winoffset) placeX = winoffset; } // Snapping! if (o3_snapx > 1) { var snapping = placeX % o3_snapx; if (o3_hpos == LEFT) { placeX = placeX - (o3_snapx + snapping); } else { // CENTER and RIGHT placeX = placeX + (o3_snapx - snapping); } if (placeX < winoffset) placeX = winoffset; } } // VERTICAL PLACEMENT if (o3_fixy > -1) { // Fixed position placeY = o3_fixy; } else { scrolloffset = (ie4) ? eval('o3_frame.'+docRoot+'.scrollTop') : o3_frame.pageYOffset; // If VAUTO, decide what to use. if (o3_vauto == 1) { if (ie4) iheight = eval('o3_frame.'+docRoot+'.clientHeight'); if (ns4 || ns6) iheight = o3_frame.innerHeight; iheight = (eval(iheight)) / 2; if ( (o3_y - scrolloffset) > iheight) { o3_vpos = ABOVE; } else { o3_vpos = BELOW; } } // From mouse if (o3_vpos == ABOVE) { if (o3_aboveheight == 0) { var divref = (ie4) ? o3_frame.document.all['overDiv'] : over; o3_aboveheight = (ns4) ? divref.clip.height : divref.offsetHeight; } placeY = o3_y - (o3_aboveheight + o3_offsety); if (placeY < scrolloffset) placeY = scrolloffset; } else { // BELOW placeY = o3_y + o3_offsety; } // Snapping! if (o3_snapy > 1) { var snapping = placeY % o3_snapy; if (o3_aboveheight > 0 && o3_vpos == ABOVE) { placeY = placeY - (o3_snapy + snapping); } else { placeY = placeY + (o3_snapy - snapping); } if (placeY < scrolloffset) placeY = scrolloffset; } } // Actually move the object. repositionTo(over, placeX, placeY); } // Moves the layer function mouseMove(e) { if ( (ns4) || (ns6) ) {o3_x=e.pageX; o3_y=e.pageY;} if (ie4) {o3_x=event.x; o3_y=event.y;} if (ie5) {o3_x=eval('event.x+o3_frame.'+docRoot+'.scrollLeft'); o3_y=eval('event.y+o3_frame.'+docRoot+'.scrollTop');} if (o3_allowmove == 1) { placeLayer(); } } // The Close onMouseOver function for stickies function cClick() { hideObject(over); o3_showingsticky = 0; return false; } // Makes sure target frame has overLIB function compatibleframe(frameid) { if (ns4) { if (typeof frameid.document.overDiv =='undefined') return false; } else if (ie4) { if (typeof frameid.document.all["overDiv"] =='undefined') return false; } else if (ns6) { if (frameid.document.getElementById('overDiv') == null) return false; } return true; } //////////////////////////////////////////////////////////////////////////////////// // LAYER FUNCTIONS //////////////////////////////////////////////////////////////////////////////////// // Writes to a layer function layerWrite(txt) { txt += "\n"; if (ns4) { var lyr = o3_frame.document.overDiv.document lyr.write(txt) lyr.close() } else if (ie4) { o3_frame.document.all["overDiv"].innerHTML = txt } else if (ns6) { range = o3_frame.document.createRange(); range.setStartBefore(over); domfrag = range.createContextualFragment(txt); while (over.hasChildNodes()) { over.removeChild(over.lastChild); } over.appendChild(domfrag); } } // Make an object visible function showObject(obj) { if (ns4) obj.visibility = "show"; else if (ie4) obj.visibility = "visible"; else if (ns6) obj.style.visibility = "visible"; } // Hides an object function hideObject(obj) { if (ns4) obj.visibility = "hide"; else if (ie4) obj.visibility = "hidden"; else if (ns6) obj.style.visibility = "hidden"; if (o3_timerid > 0) clearTimeout(o3_timerid); if (o3_delayid > 0) clearTimeout(o3_delayid); o3_timerid = 0; o3_delayid = 0; self.status = ""; } // Move a layer function repositionTo(obj,xL,yL) { if ( (ns4) || (ie4) ) { obj.left = (ie4 ? xL + 'px' : xL); obj.top = (ie4 ? yL + 'px' : yL); } else if (ns6) { obj.style.left = xL + "px"; obj.style.top = yL+ "px"; } } function getFrameRef(thisFrame, ofrm) { var retVal = ''; for (var i=0; i 0) { retVal = getFrameRef(thisFrame[i],ofrm); if (retVal == '') continue; } else if (thisFrame[i] != ofrm) continue; retVal = '['+i+']' + retVal; break; } return retVal; } //////////////////////////////////////////////////////////////////////////////////// // PARSER FUNCTIONS //////////////////////////////////////////////////////////////////////////////////// // Defines which frame we should point to. function opt_FRAME(frm) { o3_frame = compatibleframe(frm) ? frm : ol_frame; if (o3_frame != ol_frame) { var tFrm = getFrameRef(top.frames, o3_frame); var sFrm = getFrameRef(top.frames, ol_frame); if (sFrm.length == tFrm.length) { l = tFrm.lastIndexOf('['); if (l) { while(sFrm.substring(0,l) != tFrm.substring(0,l)) l = tFrm.lastIndexOf('[',l-1); tFrm = tFrm.substr(l); sFrm = sFrm.substr(l); } } var cnt = 0, p = '', str = tFrm; while((k = str.lastIndexOf('[')) != -1) { cnt++; str = str.substring(0,k); } for (var i=0; i ./oar-2.5.2/sources/visualization_interfaces/Monika/monika.conf.in0000755000175000017500000002062711757171206023332 0ustar plbplb## Created on November 2007 by Joseph.Emeras@imag.fr ################# ## monika.conf ## ################# ############################################################################## ## WARNING: About the location of this file: ## monika.cgi is looking first for its configuration file in its own ## directory then in /etc/oar/ ############################################################################## ############################################################################## ## CSS path for the HTML diplay ## - default is "/monika.css" ############################################################################## css_path = %%WWW_ROOTDIR%%/monika.css ############################################################################## ## clustername: set the name of the cluster ## - default is "Cluster" ## - ex: clustername = "MyCluster" ############################################################################## clustername = OAR Cluster ############################################################################## ## DataBase : set the connection parameters for accessing the OAR DataBase ############################################################################## hostname = localhost dbport = 3306 # dbtype can be mysql or psql for postgresql dbtype = mysql dbname = oar username = oar password = oar ############################################################################## ## nodes_synonym: which real resource must be used when using the "nodes" ## keyword ? ## - ex: nodes_synonym = resource_id ## nodes_synonym = host ## - default is resource_id ############################################################################## nodes_synonym = network_address ############################################################################## ## Summary Display: specify how to display the summary status. For each type ## of resource, a resource hierarchy can be specified. Syntax is: ## [:[,]...][;...] ## -ex: summary_display = default:nodes_synonym;licence:resource_id; ## memory:resource_id;userid:resource_id ## -ex: summary_display = default:host,cpu,core;licence;memory;userid ## -ex: summary_display = default:network_address ## -ex: summary_display = default:nodes_synonym ############################################################################## summary_display = default:nodes_synonym,resource_id ############################################################################## ## nodes_per_line: set the number of node to be displayed per line in the ## reservation table ## - default is 10 ## - ex: nodes_per_line = 5 ############################################################################## nodes_per_line = 4 ############################################################################## ## max_cores_per_line: set the number of cores to be displayed per line in the ## reservation table ## - default is 16 ## - ex: max_cores_per_line = 32 ############################################################################## max_cores_per_line = 16 ############################################################################## ## nodename_regex: set the regular expression to extract nodes' short ## names (ex: node22 => 22). Use Perl regular expression syntax. ## - default is "(\d+)" ie extract the first number from the left in nodenames ## - ex: nodename_regex = cluster5node(\d+) ie basename contains a number... ## - ex: nodename_regex = ([^.]+) ie to extract the short hostname. ## - ex: nodename_regex = (.+) ie to keep the whole word if really needed. ## Rq: this regex is used to sort nodes ############################################################################## nodename_regex = (.*) ############################################################################## ## nodename_regex_display: set the regex display on the page node names ## it is just for final display ## Rq: monika looks better with numerical only short nodenames... ############################################################################## nodename_regex_display = (.*) ############################################################################## ## server_do_mail: set the capability of the server to handle mail. If ## true, then monika use the job owner attribut as a valid email address ## unless -M is specified in qsub and then used instead ## - default is "False" ## - ex: server_do_mail = "True" ############################################################################## #server_do_mail = "False" ############################################################################## ## user_infos : if server_do_mail is not set then you can specify a cgi page ## wich can display informations about a user. It is a link on the user name. ## The link pints on the content of user_infos+user_name ############################################################################## #user_infos = "userInfos.cgi?" ############################################################################## ## node_group: define a group of nodes ## - ex: node_group group1 = 5-10 25 32 40-50 ## - ex: node_group group2 = master nodeone nodetwo ## * Rq: monika looks better with numerical only node names. ## * Rq: nodes you define this way may be either extra nodes (ex: login nodes) ## or the nodes in order to give them a "rescue" state (ex: Missing) ############################################################################## #node_group login = 1-4 #node_group batch = 5-225 ############################################################################## ## default_state: define the default state for a node group defined above ## - ex: default_state group1 = "StateGroup1" ############################################################################## #default_state login = Login #default_state batch = Missing ############################################################################## ## set_color: associate a HTML color to a node state ## - ex: set_color Down = "red" ## - ex: set_color Free = "#33ff33" ############################################################################## set_color Down = "red" set_color Free = "#ffffff" set_color Absent = "#c22200" set_color StandBy = "cyan" set_color Suspected = "#ff7b7b" #set_color Missing = "grey" #set_color Login = "cyan" ############################################################################## ## color_pool: add an HTML color to dynamically use in HTML table generation ## - ex: color_pool = "#9999ff" ## color_pool = "#ff6600" ## color_pool = "#00cccc" ############################################################################## color_pool = "#9999ff" color_pool = "#00cccc" color_pool = "pink" color_pool = "yellow" color_pool = "orange" color_pool = "#ff22ff" color_pool = "#33cc00" color_pool = "#cc66cc" color_pool = "#99ff99" color_pool = "#995522" color_pool = "orange" color_pool = "#999999" ############################################################################## ## hidden_property: define properties not to be shown in the main page ## - ex: hidden_property = hostname ## hidden_property = besteffort ## hidden_property = expiryDate ############################################################################## #hidden_property = network_address #hidden_property = besteffort #hidden_property = expiry_date #hidden_property = desktop_computing #hidden_property = cpu #hidden_property = cpuset #hidden_property = available_upto #hidden_property = core #hidden_property = finaud_decision #hidden_property = last_job_date #hidden_property = resource_id #hidden_property = state #hidden_property = state_num #hidden_property = type #hidden_property = mem #hidden_property = suspended_jobs #hidden_property = next_state #hidden_property = next_finaud_decision #hidden_property = deploy #hidden_property = host hidden_property = network_address #hidden_property = besteffort hidden_property = expiry_date #hidden_property = desktop_computing hidden_property = cpu hidden_property = cpuset hidden_property = available_upto hidden_property = last_available_upto hidden_property = core hidden_property = finaud_decision hidden_property = last_job_date hidden_property = resource_id #hidden_property = state hidden_property = state_num #hidden_property = type #hidden_property = mem hidden_property = suspended_jobs hidden_property = next_state hidden_property = next_finaud_decision hidden_property = deploy hidden_property = host hidden_property = ip hidden_property = hostname hidden_property = scheduler_priority #hidden_property = switch ./oar-2.5.2/sources/visualization_interfaces/Monika/monika.cgi.in0000755000175000017500000001132711757171206023144 0ustar plbplb#!/usr/bin/perl ############################################################################## ## Monika is a small web interface to monitor OAR node reservations. ## It tries to display a very synthetic view of the cluster usage. ## Monika should work also with PBSPro or OpenPBS. ## Author: pierre.neyron@imag.fr ## Modified by: joseph.emeras@imag.fr ############################################################################## use strict; #use warnings; use OAR::Monika::monikaCGI; use OAR::Monika::Conf; use OAR::Monika::OAR; use Data::Dumper; ## begin CGI stuff my ($basename) = $0 =~ /([^\/]+)\.cgi$/; my $cgi = OAR::Monika::monikaCGI->new; my $file = $cgi->param("conf"); ## first get nodes description from the configuration file my $Oardir = "%%OARCONFDIR%%"; my $conf = OAR::Monika::Conf->new; if ( defined $file and -r $file) { $conf->parse($file); } elsif (-r "$Oardir/$basename.conf") { $conf->parse("$Oardir/$basename.conf"); } elsif (-r "./$basename.conf") { $conf->parse("./$basename.conf"); } elsif (-r "/etc/$basename.conf") { $conf->parse("/etc/$basename.conf"); } else { die "Neither $Oardir/$basename.conf nor /etc/$basename.conf nor ./$basename.conf are readable. I need a configuration file !"; } ## then get nodes description my $oar = OAR::Monika::OAR->new; $oar->oarnodes; $oar->qstat($cgi); ## my global node container... my %nodes; ## ... filled in with previousely got nodes: ## first with node descriptions from the configuration file foreach my $key ( keys %{$conf->allnodes()}) { $nodes{$key}=$conf->allnodes->{$key}; } ## then with node descriptions (which may overwrite descriptions ## from the configuration file, which is what we want for instance if the ## configuration file define a default state (say "missing") for nodes ## that may actually not be described) foreach my $key ( keys %{$oar->allnodes()}) { $nodes{$key}=$oar->allnodes->{$key}; } ## set color scheme up my $colorHash = $conf->colorHash; while (my ($state,$color) = each %{$colorHash}) { $cgi->setColor($state,$color); } $cgi->setColorPool($conf->colorPool()); ## begin html printing print $cgi->page_head($conf->clustername." - Monika"); my $css_path = OAR::Monika::Conf::myself->css_path; print ""; print $cgi->h1({-align => "center"}, $conf->clustername()." "." nodes"); ## if node param is present, show detailed view of the pointed node if (defined $cgi->param('node') and defined $nodes{$cgi->param('node')}) { my $node = $cgi->param('node'); print $cgi->h2({-align => "center"}, "Node ".$node." detailed status:"); print $nodes{$node}->htmlStatusTable($cgi); print $cgi->h3({ -align => "center" }, $cgi->a({ -href => $cgi->url(-absolute=>1,-query=>0)}, "back to main page" )); ## if job param is present, show detailed view of the pointed job } elsif (defined $cgi->param('job')) { # and defined $oar->alljobs()->{$cgi->param('job')} my $job = $cgi->param('job'); print $cgi->h2({-align => "center"}, "Job ".$job." detailed status:"); if(exists($oar->alljobs()->{$job})){ print $oar->alljobs()->{$job}->htmlStatusTable($cgi); } else{ my $jobInfos = $oar->getJobProperties($job, $cgi); print $jobInfos->htmlStatusTable($cgi); } print $cgi->h3({ -align => "center" }, $cgi->a({ -href => $cgi->url(-absolute=>1,-query=>0)}, "back to main page" )); ## else show the main page } else { ## print oar status summary print $oar->htmlSummaryTable($cgi); print $cgi->br(); ## print nodes reservations table print $cgi->h2({-align => "center"}, "Reservations:"); ## print resources for each of the properties if asked in the CGI request, or all resources. if (defined $cgi->param('props')) { ## print resources property, for the properties selected in the CGI request print $oar->htmlNodeByProperty($cgi); } else { ## print all resources print $cgi->nodeReservationTable(\%nodes); } print $cgi->h5({-align => "center"}, "*: Running job but suspected resources."); ## print oar node property chooser print $cgi->br(); print $oar->htmlPropertyChooser($cgi); ## print oar job status print $cgi->br(); print $cgi->h2({-align => "center"}, "Job details:"); print $oar->htmlJobTable($cgi); } #open VERSION, "<./monika/VERSION"; #my $version=""; #while () { # $version.=$_; #} #close VERSION; ## print © stuff print $cgi->h6({ -align => "center", onmouseover => "popup('Link description here','yellow')", onmouseout => "kill()" }, "- ". $cgi->a({ -href => 'http://oar.imag.fr'}, "Monika"). " - ©". $cgi->a({ -href => 'http://www-id.imag.fr'}, 'Laboratoire d\'Informatique de Grenoble'). " -" ); print $cgi->end_html(); ./oar-2.5.2/sources/visualization_interfaces/Monika/lib/0000755000175000017500000000000011757171206021334 5ustar plbplb./oar-2.5.2/sources/visualization_interfaces/Monika/lib/OAR/0000755000175000017500000000000011757171206021755 5ustar plbplb./oar-2.5.2/sources/visualization_interfaces/Monika/lib/OAR/Monika/0000755000175000017500000000000011757171206023173 5ustar plbplb./oar-2.5.2/sources/visualization_interfaces/Monika/lib/OAR/Monika/monikaCGI.pm0000755000175000017500000001347311757171206025345 0ustar plbplb## This package inherits CGI.pm and adds support for color management package OAR::Monika::monikaCGI; use strict; use File::Basename; use Sort::Naturally; #use warnings; use base qw(CGI); use Data::Dumper; ## class constructor sub new { my $proto = shift; my $class = ref($proto) || $proto; my $self = $class->SUPER::new(); $self->{USED_COLORS} = {}; $self->{COLOR_POOL} = []; bless ($self, $class); return $self; } ## get the color for a key sub getColor { my $self = shift; my $key = shift; if (not $self->{USED_COLORS}->{$key}) { $self->{USED_COLORS}->{$key} = $self->getColorFromPool($key); } return $self->{USED_COLORS}->{$key}; } ## set a color for a key sub setColor { my $self = shift; my $key = shift; my $color = shift; $self->{USED_COLORS}->{$key} = $color; return 1; } ## set color pool sub setColorPool { my $self = shift; $self->{COLOR_POOL} = shift; return 1; } ## get a color from color pool, and rotate the pool by one. sub getColorFromPool { my $self = shift; my $key = shift; my $color_pool = $self->{COLOR_POOL}; my $color = $color_pool->[$key % ($#{$color_pool}+1)]; #my $color = shift @$color_pool; #push @$color_pool,$color; return $color; } ## print a HTML with a background color corresponding to the text ## given as parameter sub colorTd { my $self = shift; my $txt = shift; my $width; my $href; my $title; if (@_) { $width = shift; } else { $width = undef; } if (@_) { $href = shift; } else { $href = undef; } if (@_) { $title = shift; } else { $title = undef; } my $txt2; if ($href) { $txt2 = $self->a({-href => $href, -title => $title }, $self->b($txt) ); } else { $txt2 = $self->b($txt); } return $self->td({ -bgcolor => $self->getColor($txt), -width => $width, -align => "center" }, $txt2); } # print a HTML with a background color corresponding to the text ## given as parameter sub colorjavascriptTd { my $self = shift; my $txt = shift; my $width; my $href; my $title; my $javascript; if (@_) { $width = shift; } else { $width = undef; } if (@_) { $javascript = shift; } else { $javascript = undef; } if (@_) { $href = shift; } else { $href = undef; } if (@_) { $title = shift; } else { $title = undef; } my $txt2; if ($href) { $txt2 = $self->a({-href => $href, -title => $title, }, $self->b($txt) ); } else { $txt2 = $self->b($txt); } if ($javascript) { return $self->td({-class => "fixed_td", -bgcolor => $self->getColor($txt), -width => $width, -align => "center", -onmouseout => "return nd()", -onmouseover => "return overlib('$javascript')" }, $txt2); } else { return $self->td({ -bgcolor => $self->getColor($txt), -width => $width, -align => "center" }, $txt2); } } ## debug function: show color settings sub colorTable { my $self = shift; my $output = ""; $output .= $self->start_table({-border => "1", -align => "center"}); foreach my $key (keys %{$self->{USED_COLORS}}) { $output .= $self->start_Tr(); $output .= $self->td({-bgcolor => $self->{USED_COLORS}->{$key}, -width => "20"},""); $output .= $self->td({-align => "center"},$key); $output .= $self->end_Tr(); } my $i=0; foreach my $color (@{$self->{COLOR_POOL}}) { $output .= $self->start_Tr(); $output .= $self->td({-bgcolor => $color, -width => "20"},""); $output .= $self->td({-align => "center"},"Pool ".$i++); $output .= $self->end_Tr(); } $output .= $self->end_table(); } sub nodeReservationTable { my $self = shift; my $nodes = shift; my $nodelist = shift; my $output = ""; my @names; if (defined $nodelist) { @names = @$nodelist; } else { @names = keys %$nodes; } my $is_sorted = 1; #@names = sort {$a <=> $b or $a cmp $b} @names or $is_sorted = 0; @names = sort {$a <=> $b or Sort::Naturally::ncmp($a,$b)} @names or $is_sorted = 0; $output .= $self->start_table({-border=>"1", -align => "center" }); $output .= $self->start_Tr(); my $i=1; ## each nodes get printed in the right order foreach my $name (@names) { # $output .= $self->start_td({-align => "center",}); # $output .= $self->start_form({ action => "input_button.htm"}); # $output .= $self->start_form(); # $output .= $self->input({ # type => "button", # name => "lien", # value => "$name", # onclick => "self.location.href = '".$self->self_url(-query=>0)."?node=".$name."'" # }); # $output .= $self->endform(); # $output .= $self->end_td({-align => "center",}); $output .= $self->start_td({-align => "center", }); #$output .= $self->b($self->small($self->small($self->a({ -href => $self->self_url(-query=>0)."?node=".$name, my $cgiName = File::Basename::basename($self->self_url(-query=>0)); $output .= $self->b($self->small($self->small($self->a({ -href => $cgiName."?node=".$name, -title => "click to see node details" },$$nodes{$name}->displayHTMLname())))); $output .= $self->end_td(); $output .= $self->start_td(); ## print this node status sub table $output .= $$nodes{$name}->htmlTable($self); $output .= $self->end_td(); ## 10 nodes per line if ($i++ % OAR::Monika::Conf::myself()->nodes_per_line() == 0) { $output .= $self->end_Tr(); $output .= $self->start_Tr(); } } $output .= $self->end_Tr(); $output .= $self->end_table(); return $output; } ## print page head sub page_head { my $self = shift; my $title = shift; my $output = ""; $output .= $self->header(); $output .= $self->start_html(-title => $title, -link => "black", -vlink => "black", -alink => "darkblue" ); } ## that's all return 1; ./oar-2.5.2/sources/visualization_interfaces/Monika/lib/OAR/Monika/db_io.pm0000755000175000017500000003774211757171206024625 0ustar plbplb## Created on November 2007 by Joseph.Emeras@imag.fr package OAR::Monika::db_io; use DBI; use strict; use Data::Dumper; use warnings; use Time::Local; use POSIX qw(strftime); my $Db_type; sub get_database_type(){ return($Db_type); } my $nodes_synonym; ########################################################################################### ## Methods for Monika exclusively: # ########################################################################################### # Creates a connection to the DB and returns it sub dbConnection($$$$$$){ my $host = shift; my $port = shift; my $dbtype = shift; my $dbname = shift; my $user = shift; my $pwd = shift; if($dbtype eq "psql"){ $dbtype = "Pg"; } $Db_type = $dbtype; $nodes_synonym = OAR::Monika::Conf::myself->nodes_synonym; my $connection_string; if($port eq "" || !($port>1 && $port<65535)){ $connection_string = "DBI:$dbtype:database=$dbname;host=$host"; } else{ $connection_string = "DBI:$dbtype:database=$dbname;host=$host;port=$port"; } my $dbh= DBI->connect($connection_string, $user, $pwd, {AutoCommit => 1, RaiseError => 1}); return $dbh; } sub dbDisconnect($) { my $dbh = shift; # Disconnect from the database. $dbh->disconnect(); } # get_properties_values # returns the list of the fields of the job table and their values # usefull for the 'properties' section in Monika # parameters : base, list of excluded fields # return value : list of fields end values # side effects : / sub get_properties_values($$) { my $dbh = shift; my $excluded = shift; my @result; my $sth; if ($Db_type eq "Pg"){ #$sth = $dbh->prepare("SELECT a.attname # FROM pg_class AS c, pg_attribute AS a # WHERE relname = 'resources' AND c.oid = a.attrelid AND a.attnum > 0;"); $sth = $dbh->prepare("SELECT column_name FROM information_schema.columns WHERE table_name = \'resources\'"); } else{ $sth = $dbh->prepare("DESC resources"); } $sth->execute(); while (my $ref = $sth->fetchrow_hashref()){ my $current_value; if ($Db_type eq "Pg" || $Db_type eq "psql"){ $current_value = $ref->{'column_name'}; } else{ $current_value = $ref->{'Field'}; } unless (defined($excluded->{$current_value})){ push(@result, $current_value); } } $sth->finish(); my $str = "SELECT DISTINCT "; foreach(@result){ $str = $str.$_.", "; } $str = substr $str, 0, length($str) - 2; $str = $str." FROM resources;"; my $sth2 = $dbh->prepare($str); $sth2->execute(); my $ref; my $i = 0; while (my $current = $sth2->fetchrow_hashref()){ $i++; $ref->{$i} = $current; } $sth2->finish(); return $ref; } # get_all_resources_on_node # returns the current resources on node whose hostname is passed in parameter # parameters : base, hostname # return value : weight # side effects : / my %Resources_on_nodes; sub get_all_resources_on_node($$) { my $dbh = shift; my $hostname = shift; if (defined($Resources_on_nodes{$hostname})){ return(@{$Resources_on_nodes{$hostname}}); }else{ my $sth = $dbh->prepare(" SELECT resources.resource_id as resource, resources.$nodes_synonym as node FROM resources "); $sth->execute(); my @result; while (my $ref = $sth->fetchrow_hashref()){ push(@{$Resources_on_nodes{$ref->{node}}}, $ref->{resource}); } $sth->finish(); return(@{$Resources_on_nodes{$hostname}}); } } # get_queued_jobs # returns the list of queued jobs: running, waiting... # parameters : base # return value : list of jobid # side effects : / sub get_queued_jobs($) { my $dbh = shift; my $sth = $dbh->prepare(" SELECT jobs.job_id FROM (jobs INNER JOIN moldable_job_descriptions ON jobs.job_id = moldable_job_descriptions.moldable_job_id) LEFT JOIN assigned_resources ON assigned_resources.moldable_job_id = moldable_job_descriptions.moldable_job_id WHERE jobs.state IN (\'Waiting\',\'Hold\',\'toLaunch\',\'toError\',\'toAckReservation\',\'Launching\',\'Running\',\'Suspended\',\'Resuming\') "); $sth->execute(); my @res = (); while (my $ref = $sth->fetchrow_hashref()) { push(@res, $ref->{'job_id'}); } return @res; } # get_job_stat_infos # returns the information about the given job # parameters : base, job_id # return value : list of information # side effects : / my %Job_stat_infos; sub get_job_stat_infos($$) { my $dbh = shift; my $job= shift; if (defined($Job_stat_infos{$job})){ return($Job_stat_infos{$job}); }else{ my $sth = $dbh->prepare(" SELECT * FROM jobs WHERE jobs.job_id = $job "); $sth->execute(); my $ref = $sth->fetchrow_hashref(); $sth->finish(); $Job_stat_infos{$job} = $ref; return $ref; } } # get_job_cores # returns the list of cores used by the given job # parameters : base, job # return value : list of cores ressources # side effects : / #sub get_job_cores($$) { # my $dbh = shift; # my $job = shift; # my $sth = $dbh->prepare(" SELECT resources.resource_id # FROM ((jobs INNER JOIN moldable_job_descriptions ON moldable_job_descriptions.moldable_job_id = jobs.job_id) LEFT JOIN assigned_resources ON assigned_resources.moldable_job_id = moldable_job_descriptions.moldable_id) INNER JOIN resources ON assigned_resources.resource_id = resources.resource_id # WHERE # assigned_resources.assigned_resource_index = \'CURRENT\' # AND moldable_job_descriptions.moldable_index = \'CURRENT\' # AND jobs.job_id = $job # AND jobs.state != \'Terminated\' # AND jobs.state != \'Error\' # "); # $sth->execute(); # my @res = (); # while (my $ref = $sth->fetchrow_hashref()) { # push(@res, $ref->{'resource_id'}); # } # return @res; #} ########################################################################################### ## Methods from the OAR IOLIB: # ########################################################################################### # get_resource_job # returns the list of jobs associated to the resource passed in parameter # parameters : base, resource # return value : list of jobid # side effects : / my %Resource_job; my $Resource_job_init = 0; sub get_resource_job($$) { my $dbh = shift; my $resource = shift; if ($Resource_job_init > 0){ if (defined($Resource_job{$resource})){ return(@{$Resource_job{$resource}}); }else{ return(()); } }else{ my $sth = $dbh->prepare(" SELECT assigned_resources.resource_id, jobs.job_id FROM assigned_resources, moldable_job_descriptions, jobs WHERE assigned_resources.assigned_resource_index = \'CURRENT\' AND moldable_job_descriptions.moldable_index = \'CURRENT\' AND assigned_resources.moldable_job_id = moldable_job_descriptions.moldable_id AND moldable_job_descriptions.moldable_job_id = jobs.job_id AND jobs.state != \'Terminated\' AND jobs.state != \'Error\' "); $sth->execute(); my @res = (); $Resource_job_init++; while (my $ref = $sth->fetchrow_hashref()) { push(@{$Resource_job{$ref->{'resource_id'}}}, $ref->{'job_id'}); } if (defined($Resource_job{$resource})){ return(@{$Resource_job{$resource}}); }else{ return(()); } } } # list_nodes # gets the list of all nodes. # parameters : base # return value : list of hostnames # side effects : / sub list_nodes($) { my $dbh = shift; my $sth = $dbh->prepare(" SELECT distinct($nodes_synonym) FROM resources ORDER BY $nodes_synonym ASC "); $sth->execute(); my @res = (); while (my $ref = $sth->fetchrow_hashref()) { push(@res, $ref->{$nodes_synonym}); } $sth->finish(); return(@res); } # get_resource_info # returns a ref to some hash containing data for the nodes of the resource passed in parameter # parameters : base, resource id # return value : ref # side effects : / my %Resource_info; sub get_resource_info($$) { my $dbh = shift; my $resource = shift; if (defined($Resource_info{$resource})){ return($Resource_info{$resource}); }else{ my $sth = $dbh->prepare(" SELECT * FROM resources "); $sth->execute(); while (my $ref = $sth->fetchrow_hashref()) { $Resource_info{$ref->{resource_id}} = $ref; } $sth->finish(); return($Resource_info{$resource}); } } # Get start_time for a given job # args : base, job id my %Gantt_job_start_time; my $Gantt_job_start_time_init = 0; sub get_gantt_job_start_time($$){ my $dbh = shift; my $job = shift; if ($Gantt_job_start_time_init > 0){ if (defined($Gantt_job_start_time{$job})){ return($Gantt_job_start_time{$job},$job); }else{ return(undef); } }else{ $Gantt_job_start_time_init = 1; my $sth = $dbh->prepare("SELECT gantt_jobs_predictions_visu.start_time, moldable_job_descriptions.moldable_job_id FROM gantt_jobs_predictions_visu,moldable_job_descriptions WHERE moldable_job_descriptions.moldable_index = \'CURRENT\' AND moldable_job_descriptions.moldable_id = gantt_jobs_predictions_visu.moldable_job_id GROUP BY gantt_jobs_predictions_visu.start_time, moldable_job_descriptions.moldable_job_id "); $sth->execute(); while (my @res = $sth->fetchrow_array()){ $Gantt_job_start_time{$res[1]} = $res[0]; } $sth->finish(); if (defined($Gantt_job_start_time{$job})){ return($Gantt_job_start_time{$job},$job); }else{ return(undef); } } } # local_to_sql # converts a date specified in an integer local time format to the format used # by the sql database # parameters : date integer # return value : date string # side effects : / sub local_to_sql($) { my $local=shift; #my ($year,$mon,$mday,$hour,$min,$sec)=local_to_ymdhms($local); #return ymdhms_to_sql($year,$mon,$mday,$hour,$min,$sec); #return $year."-".$mon."-".$mday." $hour:$min:$sec"; return(strftime("%F %T",localtime($local))); } # Return a data structure with the resource description of the given job # arg : database ref, job id # return a data structure (an array of moldable jobs): # example for the first moldable job of the list: # $result = [ # [ # { # property => SQL property # resources => [ # { # resource => resource name # value => number of this wanted resource # } # ] # } # ], # walltime, # moldable_id # ] my %Resources_data_structure_current_job; sub get_resources_data_structure_current_job($$){ my $dbh = shift; my $job_id = shift; # my $sth = $dbh->prepare(" SELECT moldable_job_descriptions.moldable_id, job_resource_groups.res_group_id, moldable_job_descriptions.moldable_walltime, job_resource_groups.res_group_property, job_resource_descriptions.res_job_resource_type, job_resource_descriptions.res_job_value # FROM moldable_job_descriptions, job_resource_groups, job_resource_descriptions, jobs # WHERE # moldable_job_descriptions.moldable_index = \'CURRENT\' # AND job_resource_groups.res_group_index = \'CURRENT\' # AND job_resource_descriptions.res_job_index = \'CURRENT\' # AND jobs.job_id = $job_id # AND jobs.job_id = moldable_job_descriptions.moldable_job_id # AND job_resource_groups.res_group_moldable_id = moldable_job_descriptions.moldable_id # AND job_resource_descriptions.res_job_group_id = job_resource_groups.res_group_id # ORDER BY moldable_job_descriptions.moldable_id, job_resource_groups.res_group_id, job_resource_descriptions.res_job_order ASC # "); if (defined($Resources_data_structure_current_job{$job_id})){ return($Resources_data_structure_current_job{$job_id}); }else{ my $sth = $dbh->prepare(" SELECT moldable_job_descriptions.moldable_id, job_resource_groups.res_group_id, moldable_job_descriptions.moldable_walltime, job_resource_groups.res_group_property, job_resource_descriptions.res_job_resource_type, job_resource_descriptions.res_job_value FROM moldable_job_descriptions, job_resource_groups, job_resource_descriptions, jobs WHERE jobs.job_id = $job_id AND jobs.job_id = moldable_job_descriptions.moldable_job_id AND job_resource_groups.res_group_moldable_id = moldable_job_descriptions.moldable_id AND job_resource_descriptions.res_job_group_id = job_resource_groups.res_group_id ORDER BY moldable_job_descriptions.moldable_id, job_resource_groups.res_group_id, job_resource_descriptions.res_job_order ASC "); $sth->execute(); my $result; my $group_index = -1; my $moldable_index = -1; my $previous_group = 0; my $previous_moldable = 0; while (my @ref = $sth->fetchrow_array()){ if ($previous_moldable != $ref[0]){ $moldable_index++; $previous_moldable = $ref[0]; $group_index = 0; $previous_group = $ref[1]; }elsif ($previous_group != $ref[1]){ $group_index++; $previous_group = $ref[1]; } # Store walltime $result->[$moldable_index]->[1] = $ref[2]; $result->[$moldable_index]->[2] = $ref[0]; #Store properties group $result->[$moldable_index]->[0]->[$group_index]->{property} = $ref[3]; my %tmp_hash = ( resource => $ref[4], value => $ref[5] ); push(@{$result->[$moldable_index]->[0]->[$group_index]->{resources}}, \%tmp_hash); } $sth->finish(); $Resources_data_structure_current_job{$job_id} = $result; return($result); } } return 1; ./oar-2.5.2/sources/visualization_interfaces/Monika/lib/OAR/Monika/OARNode.pm0000755000175000017500000002064511757171206024772 0ustar plbplb## Created on November 2007 by Joseph.Emeras@imag.fr ## this package handles a OAR node description package OAR::Monika::OARNode; use strict; use warnings; use OAR::Monika::monikaCGI; use OAR::Monika::Conf; use Data::Dumper; use Time::Local; use POSIX qw(strftime); ## class constructor sub new { my $proto = shift; my $class = ref($proto) || $proto; my $self = {}; $self->{Nodename} = shift or die "missing a node name !"; $self->{Ressources} = shift or die "missing node ressources !"; bless ($self, $class); return $self; } ## return node full name sub name { my $self = shift; return $self->{Nodename}; } ## return node's ressource state # parameters : node, resource id # return value : state of the given ressource sub ressourceState($$){ my $self = shift; my $ressource= shift; return $self->{Ressources}->{$ressource}->{infos}->{state}; } ## return node's state # parameters : node # return value : hash table containing (ressource, state) couples sub state($){ my $self = shift; my %hashResult; foreach my $key (keys %{$self->{Ressources}}){ #$hashResult{$key}= $self->{Ressources}->{$key}->{infos}->{state}; $hashResult{$key}= $self->ressourceState($key); } return %hashResult; } ## return this node's ressource cores count # parameters : node, resource id # return value : number of cores of the given ressource #sub ressourceCores($$) { # my $self = shift; # my $ressource= shift; # return $self->{Ressources}->{$ressource}->{infos}->{cpucore}; #} ## return this node cpu/cores resource count: if a cpu is composed of two cores, the return value will be 2... sub nodeResources { my $node = shift; my $cpt=0; #my %alreadyCounted; foreach (keys %{$node->{Ressources}}){ #unless (defined($alreadyCounted{$node->{Ressources}->{$_}->{infos}->{cpu}})){ #$cpt+=$node->{Ressources}->{$_}->{infos}->{cpucore}; #$alreadyCounted{$node->{Ressources}->{$_}->{infos}->{cpu}}= 1; #} $cpt++; } return $cpt; } ## alias cpus --> core sub cpus { my $node = shift; my $result= $node->nodeResources; return $result; } ## extract the name to display using a regex from the node real name sub displayname { my $self = shift; $self->name() =~ OAR::Monika::Conf::myself()->nodenameRegex(); my $shortname = $1 or die "Fail to extract node' shortname"; return $shortname;; } ## extract the name to display on the page sub displayHTMLname { my $self = shift; $self->name() =~ OAR::Monika::Conf::myself()->nodenameRegexDisplay(); my $shortname = $1 or die "Fail to extract node' shortname"; return $shortname; } ## return a hash containing (ressource,\@jobs) couple. sub jobs { my $self = shift; my %hashResult; foreach my $key (keys %{$self->{Ressources}}){ $hashResult{$key}= $self->{Ressources}->{$key}->{jobs}; } return %hashResult; } ## return an array containing ressource's jobs. sub ressourceJobs { my $self = shift; my $ressource= shift; my $ptResult= $self->{Ressources}->{$ressource}->{jobs}; return @$ptResult; } sub isRessourceWorking{ my $self = shift; my $ressource= shift; if (defined(@{$self->{Ressources}->{$ressource}->{jobs}})){ return 1; }else{ return 0; } } ## return the hash table of properties of this node's ressource sub ressourceProperties($$) { my $self = shift; my $ressource= shift; return %{$self->{Ressources}->{$ressource}->{infos}}; } ## return the (ressource, \%properties) couple for this node sub properties { my $self = shift; my %hashResult; foreach my $key (keys %{$self->{Ressources}}){ #$hashResult{$key}= $self->{Ressources}->{$key}->{infos}; my %hashtemp= $self->ressourceProperties($key); $hashResult{$key}= \%hashtemp; } return %hashResult; } ## print this node status HTML table sub htmlTable { my $self = shift; my $cgi = shift; my $output = ""; $output .= $cgi->start_table({-border => "1", -cellspacing => "0", -cellpadding => "0", -width => "100%" }); $output .= $cgi->start_Tr({-align => "center"}); my $cgiName = File::Basename::basename($cgi->self_url(-query=>0)); my $max_cores_per_line = OAR::Monika::Conf::myself()->max_cores_per_line(); my $nb_cells = 0; foreach my $currentRessource (sort keys %{$self->{Ressources}}){ if (($nb_cells++ % $max_cores_per_line) == 0){ $output .= $cgi->end_Tr(); $output .= $cgi->start_Tr({-align => "center"}); } #my $ressourceState= $self->{Ressources}->{$currentRessource}->{infos}->{state}; my $ressourceState= $self->ressourceState($currentRessource); if ($ressourceState eq "Alive" && $self->isRessourceWorking($currentRessource) eq '1') { my @jobs = @{$self->{Ressources}->{$currentRessource}->{jobs}}; $output .= $cgi->start_td(); $output .= $cgi->start_table({-border => "1", -cellspacing => "0", -cellpadding => "0", -width => "100%"}); foreach my $curr_job (@jobs){ $output .= $cgi->start_Tr({-align => "center"}); $output .= $cgi->colorTd($curr_job, 100/$self->cpus."%",$cgiName."?job=$curr_job"); $output .= $cgi->end_Tr(); } $output .= $cgi->end_table(); $output .= $cgi->end_td(); } elsif ($ressourceState eq "Alive" && $self->isRessourceWorking($currentRessource) eq '0'){ $output .= $cgi->colorTd("Free",100/$self->cpus."%"); } elsif ($ressourceState eq "Down") { $output .= $cgi->colorTd("Down",100/$self->cpus."%"); } elsif ($ressourceState eq "Absent") { my $available_upto = $self->{Ressources}->{$currentRessource}->{infos}->{available_upto}; if(defined($available_upto) && $available_upto ne '0'){ #my $now= `date +%s`; my $now= time(); if($now < $available_upto && $available_upto ne '2147483647'){ $output .= $cgi->colorTd("StandBy",100/$self->cpus."%"); }else{ $output .= $cgi->colorTd("Absent",100/$self->cpus."%"); } } else{ $output .= $cgi->colorTd("Absent",100/$self->cpus."%"); } } elsif ($ressourceState eq "Suspected") { # if the resource is suspected and running a job, we must display it # differently if($self->isRessourceWorking($currentRessource) eq '1'){ my @jobs = @{$self->{Ressources}->{$currentRessource}->{jobs}}; $output .= $cgi->colorTd($jobs[0].'*',100/$self->cpus."%"); #$output .= $cgi->colorTd($cgi->i("Suspected"),100/$self->cpus."%"); } else{ $output .= $cgi->colorTd("Suspected",100/$self->cpus."%"); } } else { $output .= $cgi->colorTd("Down",100/$self->cpus."%"); } } $output .= $cgi->end_Tr(); $output .= $cgi->end_table(); return $output; } sub getRessourceInfos { my $self = shift; my $ressource = shift; defined $ressource or die "which ressource ?"; exists $self->{Ressources}->{$ressource}->{infos} or die "Unknown ressource: ".$ressource; my %return = %{$self->{Ressources}->{$ressource}->{infos}}; return %return; } sub htmlStatusTable { my $self = shift; my $cgi = shift; my $output = ""; my $nodes_synonym = OAR::Monika::Conf::myself->nodes_synonym; $output .= $cgi->start_table({-border=>"1", -align => "center"}); $output .= $cgi->start_Tr(); $output .= $cgi->th({-align => "left", bgcolor => "^c0c0c0"}, $cgi->i("Nodename")); $output .= $cgi->th({-align => "left"}, $self->name()); $output .= $cgi->end_Tr(); $output .= $cgi->end_table(); $output .= $cgi->br(); $output .= $cgi->start_table({-border=>"1", -align => "center"}); my @keylist = keys %{$self->{Ressources}}; my @properties= keys %{$self->{Ressources}->{$keylist[0]}->{infos}}; $output .= $cgi->start_Tr(); $output .= $cgi->th({-align => "left", bgcolor => "^c0c0c0"}, $cgi->i("Ressource no.")); foreach my $key (sort @keylist) { $output .= $cgi->th({-align => "left", bgcolor => "^c0c0c0"}, $cgi->i($key)); } $output .= $cgi->end_Tr(); foreach my $prop (@properties) { $output .= $cgi->start_Tr(); $output .= $cgi->th({-align => "left", bgcolor => "^c0c0c0"}, $cgi->i($prop)); foreach my $key (sort @keylist) { my $value= $self->{Ressources}->{$key}->{infos}->{$prop}; if($prop eq $nodes_synonym){ $value= $self->displayHTMLname(); } if($prop eq 'available_upto'){ $value= strftime("%F %T",localtime($value)); } $output .= $cgi->td({-valign => "top", bgcolor => "^c0c0c0"}, $cgi->i($value)); } $output .= $cgi->end_Tr(); } $output .= $cgi->end_table(); return $output; } ## that's all. return 1; ./oar-2.5.2/sources/visualization_interfaces/Monika/lib/OAR/Monika/OARJob.pm0000755000175000017500000001137711757171206024621 0ustar plbplb## Created on November 2007 by Joseph.Emeras@imag.fr ## this package handles a OAR job description package OAR::Monika::OARJob; use strict; #use warnings; use OAR::Monika::monikaCGI; use File::Basename; use Data::Dumper; my $bestEffortColor = "#dddddd"; ## class constructor sub new { my $proto = shift; my $class = ref($proto) || $proto; my $self = {}; my $jobId = shift; defined $jobId or die "missing jobId !"; $self->{job_id} = $jobId; bless ($self,$class); return $self; } sub jobId { my $self = shift; return $self->{job_id}; } sub owner { my $self = shift; my $owner = $self->get("job_user"); $owner =~ s/@.+//; return $owner; } sub set { my $self = shift; my $key = shift; defined $key or die "which key ?"; my $value = shift; # for best effort jobs my $cgi = shift; # for best effort jobs #defined $value or die "which value for $key ?"; #unless (exists $self->{$key}) { #$self->{$key} = []; $self->{$key} = $value; #} #push @{$self->{$key}},$value; # for best effort jobs if ($self->get("queue_name") eq "besteffort"){ $cgi->setColor($self->jobId(),$bestEffortColor); } # for best effort jobs return 1; } sub get { my $self = shift; my $key = shift; defined $key or die "which key ?"; if (exists $self->{$key}) { #return $self->{$key}->[0]; return $self->{$key}; } else { return undef; } } sub getList { my $self = shift; my $key = shift; defined $key or die "which key ?"; exists $self->{$key} or die "Unknown key: ".$key; return $self->{$key}; } sub htmlTableRow { my $self = shift; my $cgi = shift; my $output = ""; $output .= $cgi->start_Tr(); my $cgiName = File::Basename::basename($cgi->self_url(-query=>0)); $output .= $cgi->colorTd($self->jobId(),undef,$cgiName."?job=".$self->jobId(), "click to see job details"); if (OAR::Monika::Conf::myself()->server_do_mail()) { $output .= $cgi->td({-align => "center"}, $cgi->a({ -href => "mailto:".$self->get("job_user"), -title => "click to send mail" }, $self->owner())); } elsif (OAR::Monika::Conf::myself()->user_infos() ne "") { $output .= $cgi->td({-align => "center"}, $cgi->a({ -href => OAR::Monika::Conf::myself()->user_infos().$self->get("job_user"), -title => "click for more informations" }, $self->owner())); } else { $output .= $cgi->td({-align => "center"},$self->owner()); } #Until now, we've displayed jobId and User... my $wanted_resources=$self->get("wanted_resources"); my $walltime=$self->get("walltime"); my $state= $self->get("state"); my $queue= $self->get("queue_name"); my $type= $self->get("job_type"); my $properties= $self->get("properties"); my $reservation= $self->get("reservation"); my $submission_time = $self->get("submission_time"); my $start_time = $self->get("start_time"); my $scheduled_start = $self->get("scheduled_start"); my $initial_request = $self->get("initial_request"); if($initial_request =~ / -t container/){ $type.=" - container"; } elsif($initial_request =~ / -t inner=(\d+)/){ $type.=" - inner job (container=$1)"; } elsif($initial_request =~ / -t timesharing/){ $type.=" - timesharing"; } $output .= $cgi->td({-align => "center"},$state); $output .= $cgi->td({-align => "center"},$queue); $output .= $cgi->td({-align => "center"},$wanted_resources); $output .= $cgi->td({-align => "center"},$type); $output .= $cgi->td({-align => "center"},$properties); $output .= $cgi->td({-align => "center"},$reservation); $output .= $cgi->td({-align => "center"},$walltime); $output .= $cgi->td({-align => "center"},$submission_time); $output .= $cgi->td({-align => "center"},$start_time); $output .= $cgi->td({-align => "center"},$scheduled_start); $output .= $cgi->end_Tr(); return $output; } sub htmlStatusTable { my $self = shift; my $cgi = shift; my $output = ""; $output .= $cgi->start_table({-border=>"1", -align => "center" }); $output .= $cgi->start_Tr(); $output .= $cgi->th({-align => "left", bgcolor => "#c0c0c0"}, $cgi->i("Job Id")); $output .= $cgi->th({-align => "left"}, $self->jobId()); $output .= $cgi->end_Tr(); my @keylist = keys %{$self}; foreach my $key (sort @keylist) { if(($key eq "job_id")){ #if(($key eq "job_id") or ($key eq "initial_request")){ next; } $output .= $cgi->start_Tr(); $output .= $cgi->td({-valign => "top", bgcolor=> "#c0c0c0"}, $cgi->i($key)); my $list = $self->getList($key); my $val = join $cgi->br(),$list; #$val =~ s/([+,]\s*)/\1
/g; $output .= $cgi->td($val); $output .= $cgi->end_Tr(); } $output .= $cgi->end_table(); return $output; } ## that's all. return 1; ./oar-2.5.2/sources/visualization_interfaces/Monika/lib/OAR/Monika/OAR.pm0000755000175000017500000004162311757171206024163 0ustar plbplb## Modified on November 2007 by Joseph.Emeras@imag.fr ## added: OAR2 compatibility ## added methods used for managing OAR2 nodes and display ## This package handles OAR stuff... ## It uses OARNode.pm and OARJob.pm to store nodes and Jobs descriptions package OAR::Monika::OAR; use strict; use warnings; use Data::Dumper; use OAR::Monika::Conf; use OAR::Monika::db_io; use OAR::Monika::OARNode; use OAR::Monika::OARJob; use Tie::IxHash; ## Class constructor sub new { my $proto = shift; my $class = ref($proto) || $proto; my $self = {}; $self->{ALLNODES} = {}; $self->{PROPERTIES} = {}; $self->{ALLJOBS} = {}; bless ($self,$class); return $self; } ## return all nodes sub allnodes { my $self = shift; return $self->{ALLNODES}; } sub properties { my $self = shift; return $self->{PROPERTIES}; } ## acces DataBase and get information about nodes sub oarnodes { my $self = shift; my $hostname= OAR::Monika::Conf::myself->hostname; my $port = OAR::Monika::Conf::myself->dbport; my $dbtype= OAR::Monika::Conf::myself->dbtype; my $dbname= OAR::Monika::Conf::myself->dbname; my $username= OAR::Monika::Conf::myself->username; my $pwd= OAR::Monika::Conf::myself->password; my $dbh = OAR::Monika::db_io::dbConnection($hostname, $port, $dbtype, $dbname, $username, $pwd); my @nodeNames= OAR::Monika::db_io::list_nodes($dbh); foreach my $currentNode (@nodeNames){ next unless $currentNode; my @currentNodeRessources= OAR::Monika::db_io::get_all_resources_on_node($dbh, $currentNode); my %hashInfoCurrentNodeRessources; foreach my $currentRessource (@currentNodeRessources){ my %hashInfosJobs; $hashInfosJobs{infos}= OAR::Monika::db_io::get_resource_info($dbh, $currentRessource);## get_resource_info returns a hash ref my @jobs= OAR::Monika::db_io::get_resource_job($dbh, $currentRessource);## get_resource_job returns an array $hashInfosJobs{jobs}= \@jobs; $hashInfoCurrentNodeRessources{$currentRessource}= \%hashInfosJobs; } if ( $currentNode =~ OAR::Monika::Conf::myself()->nodenameRegex() ) { my $node= new OAR::Monika::OARNode($currentNode, \%hashInfoCurrentNodeRessources); $self->allnodes()->{$node->displayname} = $node; } } OAR::Monika::db_io::dbDisconnect($dbh); } ## retrieve a OAR job description. sub getJobProperties { my $myself = shift; my $currentJobId = shift; my $cgi = shift; my $hostname= OAR::Monika::Conf::myself->hostname; my $port = OAR::Monika::Conf::myself->dbport; my $dbtype= OAR::Monika::Conf::myself->dbtype; my $dbname= OAR::Monika::Conf::myself->dbname; my $username= OAR::Monika::Conf::myself->username; my $pwd= OAR::Monika::Conf::myself->password; my $dbh = OAR::Monika::db_io::dbConnection($hostname, $port, $dbtype, $dbname, $username, $pwd); my $jobInfos= OAR::Monika::db_io::get_job_stat_infos($dbh, $currentJobId); my $job = OAR::Monika::OARJob->new($currentJobId); foreach my $key (keys %{$jobInfos}){ my $value= $jobInfos->{$key}; $job->set($key,$value,$cgi); } ## let's count the nodes and cpu used. my $structure= OAR::Monika::db_io::get_resources_data_structure_current_job($dbh, $currentJobId); my $parrayRessources= $structure->[0]->[0]->[0]->{'resources'}; my $property= $structure->[0]->[0]->[0]->{'property'}; my $walltime= $structure->[0]->[1]; my $string= "-l \"{$property}"; foreach my $ressourceGroup (@$parrayRessources){ $string.= "/".$ressourceGroup->{resource}."=".$ressourceGroup->{value}; } my $sec=$walltime%60; $walltime/=60; my $min=$walltime%60; $walltime = int($walltime / 60); my $hour=$walltime; my $hWallTime= "$hour:$min:$sec"; $job->set("walltime",$hWallTime,$cgi); $string.=",walltime=$hWallTime\""; $job->set("wanted_resources",$string,$cgi); ## dates formatting my $submission_time= $job->get("submission_time"); #my ($year,$mon,$mday,$hour,$min,$sec)= OAR::Monika::db_io::local_to_ymdhms($submission_time); #$submission_time= "$year-$mon-$mday $hour:$min:$sec"; $job->set("submission_time",OAR::Monika::db_io::local_to_sql($submission_time),$cgi); my $start_time= $job->get("start_time"); $job->set("start_time",OAR::Monika::db_io::local_to_sql($start_time),$cgi); my $stop_time= $job->get("stop_time"); $job->set("stop_time",OAR::Monika::db_io::local_to_sql($stop_time),$cgi); OAR::Monika::db_io::dbDisconnect($dbh); return $job; } ## retrieve OAR jobs description and store them in the ALLJOBS hash. sub qstat { my $self = shift; my $cgi = shift; my $hostname= OAR::Monika::Conf::myself->hostname; my $port = OAR::Monika::Conf::myself->dbport; my $dbtype= OAR::Monika::Conf::myself->dbtype; my $dbname= OAR::Monika::Conf::myself->dbname; my $username= OAR::Monika::Conf::myself->username; my $pwd= OAR::Monika::Conf::myself->password; my $dbh = OAR::Monika::db_io::dbConnection($hostname, $port, $dbtype, $dbname, $username, $pwd); my @jobIds= OAR::Monika::db_io::get_queued_jobs($dbh); foreach my $currentJobId (@jobIds){ my $jobInfos= OAR::Monika::db_io::get_job_stat_infos($dbh, $currentJobId); my $job = OAR::Monika::OARJob->new($currentJobId); foreach my $key (keys %{$jobInfos}){ my $value= $jobInfos->{$key}; $job->set($key,$value,$cgi); } ## let's count the nodes and cpu used. my $structure= OAR::Monika::db_io::get_resources_data_structure_current_job($dbh, $currentJobId); my $parrayRessources= $structure->[0]->[0]->[0]->{'resources'}; my $property= $structure->[0]->[0]->[0]->{'property'}; my $walltime= $structure->[0]->[1]; my $string= "-l \"{$property}"; foreach my $ressourceGroup (@$parrayRessources){ $string.= "/".$ressourceGroup->{resource}."=".$ressourceGroup->{value}; } my $sec=$walltime%60; $walltime/=60; my $min=$walltime%60; $walltime = int($walltime / 60); my $hour=$walltime; my $hWallTime= "$hour:$min:$sec"; $job->set("walltime",$hWallTime,$cgi); $string.=",walltime=$hWallTime\""; $job->set("wanted_resources",$string,$cgi); ## dates formatting my $submission_time= $job->get("submission_time"); #my ($year,$mon,$mday,$hour,$min,$sec)= OAR::Monika::db_io::local_to_ymdhms($submission_time); #$submission_time= "$year-$mon-$mday $hour:$min:$sec"; $job->set("submission_time",OAR::Monika::db_io::local_to_sql($submission_time),$cgi); my $start_time= $job->get("start_time"); if($start_time ne '0'){ $job->set("start_time",OAR::Monika::db_io::local_to_sql($start_time),$cgi); } else{ $job->set("start_time", "n/a", $cgi); } my @scheduled_start_array= OAR::Monika::db_io::get_gantt_job_start_time($dbh, $currentJobId); my $scheduled_start= $scheduled_start_array[0]; if(defined $scheduled_start && $scheduled_start ne '0'){ $job->set("scheduled_start",OAR::Monika::db_io::local_to_sql($scheduled_start),$cgi); } else{ $job->set("scheduled_start", "no prediction", $cgi); } $self->alljobs()->{$currentJobId} = $job; } OAR::Monika::db_io::dbDisconnect($dbh); } ## return nodes verifying a property sub nodelistByProperty { my $self = shift; my $property = shift; my @nodes = values %{$self->allnodes}; my %alreadyCounted; my @nodesSelected; foreach my $node(@nodes){ my %hashRessProp= $node->properties(); foreach my $ress (keys %hashRessProp){ my %hashProp= %{$hashRessProp{$ress}}; foreach my $p (keys %hashProp) { my $hidden = undef; my $prop= $p."=".$hashProp{$p}; if($prop eq $property){ unless (defined($alreadyCounted{$node})){ $alreadyCounted{$node}= 1; #push @nodesSelected, $node->name; push @nodesSelected, $node->displayHTMLname; } } } } } #print STDOUT Dumper(@nodesSelected); return \@nodesSelected; } ## return all jobs sub alljobs { my $self = shift; return $self->{ALLJOBS}; } ## print a HTML summary table of the current usage of the nodes. sub htmlSummaryTable { my $self = shift; my $cgi = shift; my $output = ""; my $summary_display = OAR::Monika::Conf::myself->summary_display; my $nodes_synonym = OAR::Monika::Conf::myself->nodes_synonym; $summary_display = $summary_display.";"; ## add a ., to the end for parsing the string my %hash_display; tie %hash_display, "Tie::IxHash"; ## for hash insertion order while($summary_display ne ""){ $summary_display =~ s/(.*?);//; my $tmp=$1; my $key; if($tmp =~ m/(.*?):/){ $tmp = $tmp.","; $tmp =~ s/(.*?)://; $key = ($1); my @array_values; while($tmp ne ""){ $tmp =~ s/(.*?),//; my $value = $1; if($value eq 'nodes_synonym'){ $value = $nodes_synonym; } push @array_values, $value; } $hash_display{$key} = \@array_values; } else{ $key = $tmp; my @array_values; push @array_values, "resource_id"; $hash_display{"$key"} = \@array_values; } } $output .= $cgi->start_table({-border=>"1", -align =>"center" }); $output .= $cgi->start_Tr({-align => "center"}); my $pt_resources = $self->getResources(); foreach my $type_res (keys %hash_display){ $output .= $cgi->td(); $output .= $cgi->start_table({-border=>"1",-align =>"center"}); $output .= $cgi->start_Tr({-align => "center"}); $output .= $cgi->td($cgi->i($cgi->u($type_res." summary"))); $output .= $cgi->end_Tr(); $output .= $cgi->start_Tr({-align=>"center"}); $output .= $cgi->td($cgi->i("")); $output .= $cgi->td($cgi->b("Free")); $output .= $cgi->td($cgi->b("Busy")); $output .= $cgi->td($cgi->b("Total")); $output .= $cgi->end_Tr(); if($type_res eq 'default'){ foreach my $val (@{$hash_display{$type_res}}){ $output .= $cgi->td($cgi->b($val)); my ($free, $busy, $total) = $self->resourceCount($type_res, $val, $pt_resources); $output .= $cgi->td([$free, $busy, $total]); $output .= $cgi->end_Tr(); } } else{ foreach my $val (@{$hash_display{$type_res}}){ my ($free, $busy, $total) = $self->resourceCount($type_res, $val, $pt_resources); $output .= $cgi->td($cgi->b($val)); $output .= $cgi->td([$free, $busy, $total]); $output .= $cgi->end_Tr(); } } $output .= $cgi->end_table(); } $output .= $cgi->end_Tr(); $output .= $cgi->end_table(); return $output; } sub getResources{ my $self = shift; my %resources; foreach my $resource_name (keys %{$self->{'ALLNODES'}}){ foreach my $resource_id (keys %{$self->{'ALLNODES'}->{$resource_name}->{'Ressources'}}){ $resources{$resource_id} = $self->{'ALLNODES'}->{$resource_name}->{'Ressources'}->{$resource_id}; } } return \%resources; } ## compute a summary of the usage of OAR nodes sub resourceCount($$$$) { my $self = shift; my $type_resource = shift; my $att_name = shift; my $all_resources = shift; my ($free, $busy, $total) = (0,0,0); my %hashtotal; my %hashfree; my %alreadyCounted; my @associated_resources; foreach my $resource_id (keys %{$all_resources}){ foreach my $att (keys %{$all_resources->{$resource_id}->{'infos'}}){ my $value= $all_resources->{$resource_id}->{'infos'}->{$att}; if($att eq $att_name && $type_resource eq $all_resources->{$resource_id}->{'infos'}->{'type'}){ push @associated_resources, $resource_id; unless(exists $alreadyCounted{$value}){ $total++; if(!(@{$all_resources->{$resource_id}->{'jobs'}} > 0)){ if($all_resources->{$resource_id}->{'infos'}->{'state'} eq 'Alive'){ $hashfree{"$value:$att:$type_resource"}++; } } $alreadyCounted{$value} = "$att:$type_resource"; $hashtotal{"$value:$att:$type_resource"} = 1; } else{ $hashtotal{"$value:$att:$type_resource"}++; unless(@{$all_resources->{$resource_id}->{'jobs'}} > 0){ $hashfree{"$value:$att:$type_resource"}++; } } } } } my %cpt_att; foreach (@associated_resources){ if(@{$all_resources->{$_}->{'jobs'}} > 0){ my $value= $all_resources->{$_}->{'infos'}->{$att_name}; $cpt_att{$value} = ''; } } foreach (keys %cpt_att){ $busy++; } foreach (keys %hashfree){ if($hashfree{$_} eq $hashtotal{$_}){ $free++; } } return ($free, $busy, $total); } ## print a HTML tables describing current OAR jobs. sub htmlJobTable { my $self = shift; my $cgi = shift; my $output = ""; # $output .= $cgi->start_form(); $output .= $cgi->start_table({-border=>"1", -align => "center"}); my $alljobs = $self->alljobs(); my @keys = keys %$alljobs; if ($#keys < 0) { $output .= $cgi->Tr($cgi->td("No job currently in queues")); } else { $output .= $cgi->start_Tr(); $output .= $cgi->td({-align => "center"},"Id"); $output .= $cgi->td({-align => "center"},"User"); $output .= $cgi->td({-align => "center"},"State"); $output .= $cgi->td({-align => "center"},"Queue"); #$output .= $cgi->td({-align => "center"},"NbNodes"); #$output .= $cgi->td({-align => "center"},"NbCores"); $output .= $cgi->td({-align => "center"},"wanted_resources"); $output .= $cgi->td({-align => "center"},"Type"); $output .= $cgi->td({-align => "center"},"Properties"); $output .= $cgi->td({-align => "center"},"Reservation"); $output .= $cgi->td({-align => "center"},"Walltime"); $output .= $cgi->td({-align => "center"},"Submission Time"); $output .= $cgi->td({-align => "center"},"Start Time"); # $output .= $cgi->td({-align => "center"},"Comment"); $output .= $cgi->td({-align => "center"},"Scheduled Start"); $output .= $cgi->end_Tr(); @keys = sort {$a <=> $b} @keys; foreach my $key (@keys) { $output .= $alljobs->{$key}->htmlTableRow($cgi); } } $output .= $cgi->end_table(); return $output; } sub htmlPropertyChooser { my $self = shift; my $cgi = shift; my $output = ""; # do not show hidden properties... my @checkboxes = (); my @hiddenProperties = OAR::Monika::Conf::myself()->hiddenProperties(); my @nodes = values %{$self->allnodes}; #my %alreadyCounted; my %hiddenHash; foreach(@hiddenProperties){ $hiddenHash{$_} = ''; } my $hostname= OAR::Monika::Conf::myself->hostname; my $port = OAR::Monika::Conf::myself->dbport; my $dbtype= OAR::Monika::Conf::myself->dbtype; my $dbname= OAR::Monika::Conf::myself->dbname; my $username= OAR::Monika::Conf::myself->username; my $pwd= OAR::Monika::Conf::myself->password; my $dbh = OAR::Monika::db_io::dbConnection($hostname, $port, $dbtype, $dbname, $username, $pwd); my $result = OAR::Monika::db_io::get_properties_values($dbh, \%hiddenHash); OAR::Monika::db_io::dbDisconnect($dbh); my %hashcheckboxes; foreach(keys %{$result}){ foreach my $prop (keys %{$result->{$_}}){ if(defined($result->{$_}->{$prop})){ my $str = $prop."=".$result->{$_}->{$prop}; $hashcheckboxes{$str} = ''; } } } foreach(keys %hashcheckboxes){ push @checkboxes, $_; } # old one, very slow... #foreach my $node(@nodes){ # my %hashRessProp= $node->properties(); # foreach my $ress (keys %hashRessProp){ # my %hashProp= %{$hashRessProp{$ress}}; # foreach my $p (keys %hashProp) { # my $hidden = undef; # my $prop= $p."=".$hashProp{$p}; # foreach my $h (@hiddenProperties) { # $prop =~ /^$h=/ and $hidden = 1; # } # unless (defined($alreadyCounted{$prop})){ # defined $hidden or push @checkboxes, $prop; # $alreadyCounted{$prop}= 1; # } # } # } #} if ($#checkboxes >= 0) { my @sortedCheckboxes= sort { $a cmp $b } @checkboxes; $output .= $cgi->start_div({ -align => "center" }); $output .= $cgi->start_form({ -method => "get" }); $output .= $cgi->b("OAR properties:"); $output .= $cgi->checkbox_group({ -name=> 'props', -values=> \@sortedCheckboxes, -columns => 5, -title => "click to select property" }); $output .= $cgi->submit("Action","Display nodes for these properties"); $output .= $cgi->end_div(); $output .= $cgi->endform(); } return $output; } sub htmlNodeByProperty { my $self = shift; my $cgi = shift; my $output = ""; my @selected = sort $cgi->param('props'); #my @nodesList; foreach my $prop (@selected) { if (defined($self->nodelistByProperty($prop))){ $output .= $cgi->h3({-align => "center"}, "Reservations for property $prop:"); #push @nodesList, @{$self->nodelistByProperty($prop)}; $output .= $cgi->nodeReservationTable($self->allnodes(),$self->nodelistByProperty($prop)); } } #$output .= $cgi->nodeReservationTable($self->allnodes(),\@nodesList); return $output; } ## that's all. return 1; ./oar-2.5.2/sources/visualization_interfaces/Monika/lib/OAR/Monika/ConfNode.pm0000755000175000017500000000351711757171206025235 0ustar plbplb## this package handles a node description got from the configuration file package OAR::Monika::ConfNode; use strict; use warnings; use OAR::Monika::monikaCGI; ## class constructor sub new { my $proto = shift; my $class = ref($proto) || $proto; my $self = {}; $self->{NAME} = shift; $self->{STATE} = shift; bless ($self, $class); return $self; } ## return node number sub name { my $self = shift; return $self->{NAME}; } ## extract the name to display on the page sub displayHTMLname { my $self = shift; $self->name() =~ OAR::Monika::Conf::myself()->nodenameRegexDisplay(); my $shortname = $1 or die "Fail to extract node' shortname"; return $shortname; } ## return node state as define in the configuration file. sub state { my $self = shift; return $self->{STATE}; } ## print this node status HTML table sub htmlTable { my $self = shift; my $cgi = shift; my $output = ""; $output .= $cgi->start_table({-border => "1", -cellspacing => "0", -cellpadding => "0", -width => "100%" }); $output .= $cgi->start_Tr({-align => "center"}); $output .= $cgi->colorTd($self->state()); $output .= $cgi->end_Tr(); $output .= $cgi->end_table(); return $output; } sub htmlStatusTable { my $self = shift; my $cgi = shift; my $output = ""; $output .= $cgi->start_table({-border=>"1", -align => "center" }); $output .= $cgi->start_Tr(); $output .= $cgi->th({-align => "left", bgcolor => "#c0c0c0"}, $cgi->i("Nodename")); $output .= $cgi->th({-align => "left"}, $self->name()); $output .= $cgi->end_Tr(); $output .= $cgi->start_Tr(); $output .= $cgi->td({-align => "left", bgcolor => "#c0c0c0"}, $cgi->i("State")); $output .= $cgi->td({-align => "left"}, $self->state()); $output .= $cgi->end_Tr(); $output .= $cgi->end_table(); return $output; } ## that's all return 1; ./oar-2.5.2/sources/visualization_interfaces/Monika/lib/OAR/Monika/Conf.pm0000755000175000017500000001601211757171206024421 0ustar plbplb## Modified on November 2007 by Joseph.Emeras@imag.fr ## added: OAR2 compatibility ## This package handles monika.conf file. ## it uses ConfNode.pm to store nodes description got from the configuration file. package OAR::Monika::Conf; use strict; use warnings; use AppConfig qw(:expand :argcount); use OAR::Monika::ConfNode; ## class constructor my $myself; sub new { my $proto = shift; my $class = ref($proto) || $proto; my $self = {}; $self->{ALLNODES} = {}; bless ($self, $class); $myself = $self; return $self; } sub myself () { defined $myself or die "do first a new and parse please..."; return $myself; } ## parse config file sub parse { my $self = shift; if (@_) { $self->{FILE} = shift; } else { $self->{FILE} = "/etc/oar/monika.conf"; } my $config = AppConfig->new({ GLOBAL => { DEFAULT => "", ARGCOUNT => ARGCOUNT_ONE, } }); $config->define("clustername", { DEFAULT => "Cluster" }); $config->define("max_cores_per_line", { DEFAULT => "16" }); $config->define("css_path", { DEFAULT => "/monika.css" }); $config->define("gridname", { DEFAULT => "Grid" }); $config->define("summary_display", { DEFAULT => "default" }); $config->define("hostname", { DEFAULT => "" }); $config->define("dbport", { DEFAULT => "" }); $config->define("nodes_synonym", { DEFAULT => "resource_id" }); $config->define("dbtype", { DEFAULT => "" }); $config->define("dbname", { DEFAULT => "" }); $config->define("username", { DEFAULT => "" }); $config->define("password", { DEFAULT => "" }); $config->define("nodes_per_line", { DEFAULT => 10 }); $config->define("nodename_regex", { DEFAULT => '(.*)' }); $config->define("nodename_regex_display", { DEFAULT => '(.*)' }); $config->define("loadimgpath", { DEFAULT => "/tmp/" }); $config->define("oargridstat", { DEFAULT => "oargridstat --monitor" }); $config->define("server_do_mail", { DEFAULT => "no", }); $config->define("user_infos", { DEFAULT => "", }); $config->define("node_group", { ARGCOUNT => ARGCOUNT_HASH }); $config->define("default_state", { ARGCOUNT => ARGCOUNT_HASH }); $config->define("set_color", { ARGCOUNT => ARGCOUNT_HASH }); $config->define("color_pool", { ARGCOUNT => ARGCOUNT_LIST }); $config->define("hidden_property", { ARGCOUNT => ARGCOUNT_LIST }); $config->file($self->{FILE}); $self->{CLUSTERNAME} = $config->clustername(); $self->{MAXCORESPERLINE} = $config->max_cores_per_line(); $self->{CSS_PATH} = $config->css_path(); $self->{GRIDNAME} = $config->gridname(); $self->{SUMMARY_DISPLAY} = $config->summary_display(); $self->{HOSTNAME} = $config->hostname(); $self->{DBPORT} = $config->dbport(); $self->{NODES_SYNONYM} = $config->nodes_synonym(); $self->{DBTYPE} = $config->dbtype(); $self->{DBNAME} = $config->dbname(); $self->{USERNAME} = $config->username(); $self->{PASSWORD} = $config->password(); $self->{NODESPERLINE} = $config->nodes_per_line(); my $regex = $config->nodename_regex(); $self->{NODENAMEREGEX} = qr/$regex/; my $regex_display = $config->nodename_regex_display(); $self->{NODENAMEREGEXDISPLAY} = qr/$regex_display/; $self->{LOADIMGPATH} = $config->loadimgpath(); $self->{OARGRIDSTATCMD} = $config->oargridstat(); $self->{SERVER_DO_MAIL} = $config->server_do_mail(); $self->{NODE_GROUP} = $config->node_group(); $self->{DEFAULT_STATE} = $config->default_state(); $self->{SET_COLOR} = $config->set_color(); $self->{COLOR_POOL} = $config->color_pool(); $self->{HIDDEN_PROPERTIES} = $config->hidden_property(); $self->{USER_INFOS} = $config->user_infos(); my $allnodes = $self->{ALLNODES}; foreach my $nodeType (keys %{$self->{NODE_GROUP}}) { my $state; if ($self->{DEFAULT_STATE}->{$nodeType}) { $state = $self->{DEFAULT_STATE}->{$nodeType}; } else { $state = $nodeType; } my @nodes = split /\s+/,$self->{NODE_GROUP}->{$nodeType}; foreach (@nodes) { if (/^(\d+)-(\d+)$/) { foreach ($1..$2) { $allnodes->{$_} = OAR::Monika::ConfNode->new($_,$state); } } else { $allnodes->{$_} = OAR::Monika::ConfNode->new($_,$state); } } } return 1; } ## return cluster name sub clustername { my $self = shift; return $self->{CLUSTERNAME}; } ## return max cores to display per line sub max_cores_per_line { my $self = shift; return $self->{MAXCORESPERLINE}; } ## return css path sub css_path { my $self = shift; return $self->{CSS_PATH}; } ## return grid name sub gridname { my $self = shift; return $self->{GRIDNAME}; } ## return summary_display properties sub summary_display { my $self = shift; return $self->{SUMMARY_DISPLAY}; } ## return the hostname sub hostname { my $self = shift; return $self->{HOSTNAME}; } ## return the db port sub dbport { my $self = shift; return $self->{DBPORT}; } ## return the nodes_synonym sub nodes_synonym { my $self = shift; return $self->{NODES_SYNONYM}; } ## return the dbtype sub dbtype { my $self = shift; return $self->{DBTYPE}; } ## return the dbname sub dbname { my $self = shift; return $self->{DBNAME}; } ## return the username sub username { my $self = shift; return $self->{USERNAME}; } ## return the password sub password { my $self = shift; return $self->{PASSWORD}; } ## return the number of node to be diplayed per line in the reservation table sub nodes_per_line { my $self = shift; return $self->{NODESPERLINE}; } ## return the regex to extract a node display name from its real name sub nodenameRegex { my $self = shift; return $self->{NODENAMEREGEX}; } ## return the regex to extract a the name displayed on the www page sub nodenameRegexDisplay { my $self = shift; return $self->{NODENAMEREGEXDISPLAY}; } ## return oargridstat command (with arg) line as set in config file sub oargridstatCmd { my $self = shift; return $self->{OARGRIDSTATCMD}; } ## return loadimgPath path sub loadimgPath { my $self = shift; return $self->{LOADIMGPATH}; } ## return true if config file says server also is a mail server sub server_do_mail { my $self = shift; $_ = $self->{SERVER_DO_MAIL}; return (/^\s*yes\s*$/i or /^\s*true\s*$/i); } ## return a hash containing (state,color) couples sub colorHash { my $self = shift; return $self->{SET_COLOR}; } sub colorPool { my $self = shift; return $self->{COLOR_POOL}; } ## return the list of properties not to be shown in the property chooser sub hiddenProperties { my $self = shift; return @{$self->{HIDDEN_PROPERTIES}}; } ## return a hash containing all node descriptions got from the config file sub allnodes { my $self = shift; return $self->{ALLNODES}; } ## retrun the string for property USER_INFOS sub user_infos { my $self = shift; return $self->{USER_INFOS}; } ## that's all. return 1; ./oar-2.5.2/sources/visualization_interfaces/Monika/VERSION0000755000175000017500000000000611757171206021635 0ustar plbplb0.6.2 ./oar-2.5.2/sources/visualization_interfaces/DrawGantt/0000755000175000017500000000000011757171206021243 5ustar plbplb./oar-2.5.2/sources/visualization_interfaces/DrawGantt/js/0000755000175000017500000000000011757171206021657 5ustar plbplb./oar-2.5.2/sources/visualization_interfaces/DrawGantt/js/overlib.js0000644000175000017500000012742211757171206023667 0ustar plbplb//\////////////////////////////////////////////////////////////////////////////////// //\ overLIB 3.51 -- This notice must remain untouched at all times. //\ Copyright Erik Bosrup 1998-2002. All rights reserved. //\ //\ By Erik Bosrup (erik@bosrup.com). Last modified 2002-11-01. //\ Portions by Dan Steinman (dansteinman.com). Additions by other people are //\ listed on the overLIB homepage. //\ //\ Get the latest version at http://www.bosrup.com/web/overlib/ //\ //\ This script is published under an open source license. Please read the license //\ agreement online at: http://www.bosrup.com/web/overlib/license.html //\ If you have questions regarding the license please contact erik@bosrup.com. //\ //\ This script library was originally created for personal use. By request it has //\ later been made public. This is free software. Do not sell this as your own //\ work, or remove this copyright notice. For full details on copying or changing //\ this script please read the license agreement at the link above. //\ //\ Please give credit on sites that use overLIB and submit changes of the script //\ so other people can use them as well. This script is free to use, don't abuse. //\////////////////////////////////////////////////////////////////////////////////// //\mini //////////////////////////////////////////////////////////////////////////////////// // CONSTANTS // Don't touch these. :) //////////////////////////////////////////////////////////////////////////////////// var INARRAY = 1; var CAPARRAY = 2; var STICKY = 3; var BACKGROUND = 4; var NOCLOSE = 5; var CAPTION = 6; var LEFT = 7; var RIGHT = 8; var CENTER = 9; var OFFSETX = 10; var OFFSETY = 11; var FGCOLOR = 12; var BGCOLOR = 13; var TEXTCOLOR = 14; var CAPCOLOR = 15; var CLOSECOLOR = 16; var WIDTH = 17; var BORDER = 18; var STATUS = 19; var AUTOSTATUS = 20; var AUTOSTATUSCAP = 21; var HEIGHT = 22; var CLOSETEXT = 23; var SNAPX = 24; var SNAPY = 25; var FIXX = 26; var FIXY = 27; var FGBACKGROUND = 28; var BGBACKGROUND = 29; var PADX = 30; // PADX2 out var PADY = 31; // PADY2 out var FULLHTML = 34; var ABOVE = 35; var BELOW = 36; var CAPICON = 37; var TEXTFONT = 38; var CAPTIONFONT = 39; var CLOSEFONT = 40; var TEXTSIZE = 41; var CAPTIONSIZE = 42; var CLOSESIZE = 43; var FRAME = 44; var TIMEOUT = 45; var FUNCTION = 46; var DELAY = 47; var HAUTO = 48; var VAUTO = 49; var CLOSECLICK = 50; var CSSOFF = 51; var CSSSTYLE = 52; var CSSCLASS = 53; var FGCLASS = 54; var BGCLASS = 55; var TEXTFONTCLASS = 56; var CAPTIONFONTCLASS = 57; var CLOSEFONTCLASS = 58; var PADUNIT = 59; var HEIGHTUNIT = 60; var WIDTHUNIT = 61; var TEXTSIZEUNIT = 62; var TEXTDECORATION = 63; var TEXTSTYLE = 64; var TEXTWEIGHT = 65; var CAPTIONSIZEUNIT = 66; var CAPTIONDECORATION = 67; var CAPTIONSTYLE = 68; var CAPTIONWEIGHT = 69; var CLOSESIZEUNIT = 70; var CLOSEDECORATION = 71; var CLOSESTYLE = 72; var CLOSEWEIGHT = 73; //////////////////////////////////////////////////////////////////////////////////// // DEFAULT CONFIGURATION // You don't have to change anything here if you don't want to. All of this can be // changed on your html page or through an overLIB call. //////////////////////////////////////////////////////////////////////////////////// // Main background color (the large area) // Usually a bright color (white, yellow etc) if (typeof ol_fgcolor == 'undefined') { var ol_fgcolor = "#CCCCFF";} // Border color and color of caption // Usually a dark color (black, brown etc) if (typeof ol_bgcolor == 'undefined') { var ol_bgcolor = "#333399";} // Text color // Usually a dark color if (typeof ol_textcolor == 'undefined') { var ol_textcolor = "#000000";} // Color of the caption text // Usually a bright color if (typeof ol_capcolor == 'undefined') { var ol_capcolor = "#FFFFFF";} // Color of "Close" when using Sticky // Usually a semi-bright color if (typeof ol_closecolor == 'undefined') { var ol_closecolor = "#9999FF";} // Font face for the main text if (typeof ol_textfont == 'undefined') { var ol_textfont = "Verdana,Arial,Helvetica";} // Font face for the caption if (typeof ol_captionfont == 'undefined') { var ol_captionfont = "Verdana,Arial,Helvetica";} // Font face for the close text if (typeof ol_closefont == 'undefined') { var ol_closefont = "Verdana,Arial,Helvetica";} // Font size for the main text // When using CSS this will be very small. if (typeof ol_textsize == 'undefined') { var ol_textsize = "1";} // Font size for the caption // When using CSS this will be very small. if (typeof ol_captionsize == 'undefined') { var ol_captionsize = "1";} // Font size for the close text // When using CSS this will be very small. if (typeof ol_closesize == 'undefined') { var ol_closesize = "1";} // Width of the popups in pixels // 100-300 pixels is typical if (typeof ol_width == 'undefined') { var ol_width = "200";} // How thick the ol_border should be in pixels // 1-3 pixels is typical if (typeof ol_border == 'undefined') { var ol_border = "1";} // How many pixels to the right/left of the cursor to show the popup // Values between 3 and 12 are best if (typeof ol_offsetx == 'undefined') { var ol_offsetx = 10;} // How many pixels to the below the cursor to show the popup // Values between 3 and 12 are best if (typeof ol_offsety == 'undefined') { var ol_offsety = 10;} // Default text for popups // Should you forget to pass something to overLIB this will be displayed. if (typeof ol_text == 'undefined') { var ol_text = "Default Text"; } // Default caption // You should leave this blank or you will have problems making non caps popups. if (typeof ol_cap == 'undefined') { var ol_cap = ""; } // Decides if sticky popups are default. // 0 for non, 1 for stickies. if (typeof ol_sticky == 'undefined') { var ol_sticky = 0; } // Default background image. Better left empty unless you always want one. if (typeof ol_background == 'undefined') { var ol_background = ""; } // Text for the closing sticky popups. // Normal is "Close". if (typeof ol_close == 'undefined') { var ol_close = "Close"; } // Default vertical alignment for popups. // It's best to leave RIGHT here. Other options are LEFT and CENTER. if (typeof ol_hpos == 'undefined') { var ol_hpos = RIGHT; } // Default status bar text when a popup is invoked. if (typeof ol_status == 'undefined') { var ol_status = ""; } // If the status bar automatically should load either text or caption. // 0=nothing, 1=text, 2=caption if (typeof ol_autostatus == 'undefined') { var ol_autostatus = 0; } // Default height for popup. Often best left alone. if (typeof ol_height == 'undefined') { var ol_height = -1; } // Horizontal grid spacing that popups will snap to. // 0 makes no grid, anything else will cause a snap to that grid spacing. if (typeof ol_snapx == 'undefined') { var ol_snapx = 0; } // Vertical grid spacing that popups will snap to. // 0 makes no grid, andthing else will cause a snap to that grid spacing. if (typeof ol_snapy == 'undefined') { var ol_snapy = 0; } // Sets the popups horizontal position to a fixed column. // Anything above -1 will cause fixed position. if (typeof ol_fixx == 'undefined') { var ol_fixx = -1; } // Sets the popups vertical position to a fixed row. // Anything above -1 will cause fixed position. if (typeof ol_fixy == 'undefined') { var ol_fixy = -1; } // Background image for the popups inside. if (typeof ol_fgbackground == 'undefined') { var ol_fgbackground = ""; } // Background image for the popups frame. if (typeof ol_bgbackground == 'undefined') { var ol_bgbackground = ""; } // How much horizontal left padding text should get by default when BACKGROUND is used. if (typeof ol_padxl == 'undefined') { var ol_padxl = 1; } // How much horizontal right padding text should get by default when BACKGROUND is used. if (typeof ol_padxr == 'undefined') { var ol_padxr = 1; } // How much vertical top padding text should get by default when BACKGROUND is used. if (typeof ol_padyt == 'undefined') { var ol_padyt = 1; } // How much vertical bottom padding text should get by default when BACKGROUND is used. if (typeof ol_padyb == 'undefined') { var ol_padyb = 1; } // If the user by default must supply all html for complete popup control. // Set to 1 to activate, 0 otherwise. if (typeof ol_fullhtml == 'undefined') { var ol_fullhtml = 0; } // Default vertical position of the popup. Default should normally be BELOW. // ABOVE only works when HEIGHT is defined. if (typeof ol_vpos == 'undefined') { var ol_vpos = BELOW; } // Default height of popup to use when placing the popup above the cursor. if (typeof ol_aboveheight == 'undefined') { var ol_aboveheight = 0; } // Default icon to place next to the popups caption. if (typeof ol_capicon == 'undefined') { var ol_capicon = ""; } // Default frame. We default to current frame if there is no frame defined. if (typeof ol_frame == 'undefined') { var ol_frame = self; } // Default timeout. By default there is no timeout. if (typeof ol_timeout == 'undefined') { var ol_timeout = 0; } // Default javascript funktion. By default there is none. if (typeof ol_function == 'undefined') { var ol_function = null; } // Default timeout. By default there is no timeout. if (typeof ol_delay == 'undefined') { var ol_delay = 0; } // If overLIB should decide the horizontal placement. if (typeof ol_hauto == 'undefined') { var ol_hauto = 0; } // If overLIB should decide the vertical placement. if (typeof ol_vauto == 'undefined') { var ol_vauto = 0; } // If the user has to click to close stickies. if (typeof ol_closeclick == 'undefined') { var ol_closeclick = 0; } // This variable determines if you want to use CSS or inline definitions. // CSSOFF=no CSS CSSSTYLE=use CSS inline styles CSSCLASS=use classes if (typeof ol_css == 'undefined') { var ol_css = CSSOFF; } // Main background class (eqv of fgcolor) // This is only used if CSS is set to use classes (ol_css = CSSCLASS) if (typeof ol_fgclass == 'undefined') { var ol_fgclass = ""; } // Frame background class (eqv of bgcolor) // This is only used if CSS is set to use classes (ol_css = CSSCLASS) if (typeof ol_bgclass == 'undefined') { var ol_bgclass = ""; } // Main font class // This is only used if CSS is set to use classes (ol_css = CSSCLASS) if (typeof ol_textfontclass == 'undefined') { var ol_textfontclass = ""; } // Caption font class // This is only used if CSS is set to use classes (ol_css = CSSCLASS) if (typeof ol_captionfontclass == 'undefined') { var ol_captionfontclass = ""; } // Close font class // This is only used if CSS is set to use classes (ol_css = CSSCLASS) if (typeof ol_closefontclass == 'undefined') { var ol_closefontclass = ""; } // Unit to be used for the text padding above // Only used if CSS inline styles are being used (ol_css = CSSSTYLE) // Options include "px", "%", "in", "cm" if (typeof ol_padunit == 'undefined') { var ol_padunit = "px";} // Unit to be used for height of popup // Only used if CSS inline styles are being used (ol_css = CSSSTYLE) // Options include "px", "%", "in", "cm" if (typeof ol_heightunit == 'undefined') { var ol_heightunit = "px";} // Unit to be used for width of popup // Only used if CSS inline styles are being used (ol_css = CSSSTYLE) // Options include "px", "%", "in", "cm" if (typeof ol_widthunit == 'undefined') { var ol_widthunit = "px";} // Font size unit for the main text // Only used if CSS inline styles are being used (ol_css = CSSSTYLE) if (typeof ol_textsizeunit == 'undefined') { var ol_textsizeunit = "px";} // Decoration of the main text ("none", "underline", "line-through" or "blink") // Only used if CSS inline styles are being used (ol_css = CSSSTYLE) if (typeof ol_textdecoration == 'undefined') { var ol_textdecoration = "none";} // Font style of the main text ("normal" or "italic") // Only used if CSS inline styles are being used (ol_css = CSSSTYLE) if (typeof ol_textstyle == 'undefined') { var ol_textstyle = "normal";} // Font weight of the main text ("normal", "bold", "bolder", "lighter", ect.) // Only used if CSS inline styles are being used (ol_css = CSSSTYLE) if (typeof ol_textweight == 'undefined') { var ol_textweight = "normal";} // Font size unit for the caption // Only used if CSS inline styles are being used (ol_css = CSSSTYLE) if (typeof ol_captionsizeunit == 'undefined') { var ol_captionsizeunit = "px";} // Decoration of the caption ("none", "underline", "line-through" or "blink") // Only used if CSS inline styles are being used (ol_css = CSSSTYLE) if (typeof ol_captiondecoration == 'undefined') { var ol_captiondecoration = "none";} // Font style of the caption ("normal" or "italic") // Only used if CSS inline styles are being used (ol_css = CSSSTYLE) if (typeof ol_captionstyle == 'undefined') { var ol_captionstyle = "normal";} // Font weight of the caption ("normal", "bold", "bolder", "lighter", ect.) // Only used if CSS inline styles are being used (ol_css = CSSSTYLE) if (typeof ol_captionweight == 'undefined') { var ol_captionweight = "bold";} // Font size unit for the close text // Only used if CSS inline styles are being used (ol_css = CSSSTYLE) if (typeof ol_closesizeunit == 'undefined') { var ol_closesizeunit = "px";} // Decoration of the close text ("none", "underline", "line-through" or "blink") // Only used if CSS inline styles are being used (ol_css = CSSSTYLE) if (typeof ol_closedecoration == 'undefined') { var ol_closedecoration = "none";} // Font style of the close text ("normal" or "italic") // Only used if CSS inline styles are being used (ol_css = CSSSTYLE) if (typeof ol_closestyle == 'undefined') { var ol_closestyle = "normal";} // Font weight of the close text ("normal", "bold", "bolder", "lighter", ect.) // Only used if CSS inline styles are being used (ol_css = CSSSTYLE) if (typeof ol_closeweight == 'undefined') { var ol_closeweight = "normal";} //////////////////////////////////////////////////////////////////////////////////// // ARRAY CONFIGURATION // You don't have to change anything here if you don't want to. The following // arrays can be filled with text and html if you don't wish to pass it from // your html page. //////////////////////////////////////////////////////////////////////////////////// // Array with texts. if (typeof ol_texts == 'undefined') { var ol_texts = new Array("Text 0", "Text 1"); } // Array with captions. if (typeof ol_caps == 'undefined') { var ol_caps = new Array("Caption 0", "Caption 1"); } //////////////////////////////////////////////////////////////////////////////////// // END CONFIGURATION // Don't change anything below this line, all configuration is above. //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// // INIT //////////////////////////////////////////////////////////////////////////////////// // Runtime variables init. Used for runtime only, don't change, not for config! var o3_text = ""; var o3_cap = ""; var o3_sticky = 0; var o3_background = ""; var o3_close = "Close"; var o3_hpos = RIGHT; var o3_offsetx = 2; var o3_offsety = 2; var o3_fgcolor = ""; var o3_bgcolor = ""; var o3_textcolor = ""; var o3_capcolor = ""; var o3_closecolor = ""; var o3_width = 100; var o3_border = 1; var o3_status = ""; var o3_autostatus = 0; var o3_height = -1; var o3_snapx = 0; var o3_snapy = 0; var o3_fixx = -1; var o3_fixy = -1; var o3_fgbackground = ""; var o3_bgbackground = ""; var o3_padxl = 0; var o3_padxr = 0; var o3_padyt = 0; var o3_padyb = 0; var o3_fullhtml = 0; var o3_vpos = BELOW; var o3_aboveheight = 0; var o3_capicon = ""; var o3_textfont = "Verdana,Arial,Helvetica"; var o3_captionfont = "Verdana,Arial,Helvetica"; var o3_closefont = "Verdana,Arial,Helvetica"; var o3_textsize = "1"; var o3_captionsize = "1"; var o3_closesize = "1"; var o3_frame = self; var o3_timeout = 0; var o3_timerid = 0; var o3_allowmove = 0; var o3_function = null; var o3_delay = 0; var o3_delayid = 0; var o3_hauto = 0; var o3_vauto = 0; var o3_closeclick = 0; var o3_css = CSSOFF; var o3_fgclass = ""; var o3_bgclass = ""; var o3_textfontclass = ""; var o3_captionfontclass = ""; var o3_closefontclass = ""; var o3_padunit = "px"; var o3_heightunit = "px"; var o3_widthunit = "px"; var o3_textsizeunit = "px"; var o3_textdecoration = ""; var o3_textstyle = ""; var o3_textweight = ""; var o3_captionsizeunit = "px"; var o3_captiondecoration = ""; var o3_captionstyle = ""; var o3_captionweight = ""; var o3_closesizeunit = "px"; var o3_closedecoration = ""; var o3_closestyle = ""; var o3_closeweight = ""; // Display state variables var o3_x = 0; var o3_y = 0; var o3_allow = 0; var o3_showingsticky = 0; var o3_removecounter = 0; // Our layer var over = null; var fnRef; // Decide browser version var ns4 = (navigator.appName == 'Netscape' && parseInt(navigator.appVersion) == 4); var ns6 = (document.getElementById)? true:false; var ie4 = (document.all)? true:false; if (ie4) var docRoot = 'document.body'; var ie5 = false; if (ns4) { var oW = window.innerWidth; var oH = window.innerHeight; window.onresize = function () {if (oW!=window.innerWidth||oH!=window.innerHeight) location.reload();} } // Microsoft Stupidity Check(tm). if (ie4) { if ((navigator.userAgent.indexOf('MSIE 5') > 0) || (navigator.userAgent.indexOf('MSIE 6') > 0)) { if(document.compatMode && document.compatMode == 'CSS1Compat') docRoot = 'document.documentElement'; ie5 = true; } if (ns6) { ns6 = false; } } // Capture events, alt. diffuses the overlib function. if ( (ns4) || (ie4) || (ns6)) { document.onmousemove = mouseMove if (ns4) document.captureEvents(Event.MOUSEMOVE) } else { overlib = no_overlib; nd = no_overlib; ver3fix = true; } // Fake function for 3.0 users. function no_overlib() { return ver3fix; } //////////////////////////////////////////////////////////////////////////////////// // PUBLIC FUNCTIONS //////////////////////////////////////////////////////////////////////////////////// // overlib(arg0, ..., argN) // Loads parameters into global runtime variables. function overlib() { // Load defaults to runtime. o3_text = ol_text; o3_cap = ol_cap; o3_sticky = ol_sticky; o3_background = ol_background; o3_close = ol_close; o3_hpos = ol_hpos; o3_offsetx = ol_offsetx; o3_offsety = ol_offsety; o3_fgcolor = ol_fgcolor; o3_bgcolor = ol_bgcolor; o3_textcolor = ol_textcolor; o3_capcolor = ol_capcolor; o3_closecolor = ol_closecolor; o3_width = ol_width; o3_border = ol_border; o3_status = ol_status; o3_autostatus = ol_autostatus; o3_height = ol_height; o3_snapx = ol_snapx; o3_snapy = ol_snapy; o3_fixx = ol_fixx; o3_fixy = ol_fixy; o3_fgbackground = ol_fgbackground; o3_bgbackground = ol_bgbackground; o3_padxl = ol_padxl; o3_padxr = ol_padxr; o3_padyt = ol_padyt; o3_padyb = ol_padyb; o3_fullhtml = ol_fullhtml; o3_vpos = ol_vpos; o3_aboveheight = ol_aboveheight; o3_capicon = ol_capicon; o3_textfont = ol_textfont; o3_captionfont = ol_captionfont; o3_closefont = ol_closefont; o3_textsize = ol_textsize; o3_captionsize = ol_captionsize; o3_closesize = ol_closesize; o3_timeout = ol_timeout; o3_function = ol_function; o3_delay = ol_delay; o3_hauto = ol_hauto; o3_vauto = ol_vauto; o3_closeclick = ol_closeclick; o3_css = ol_css; o3_fgclass = ol_fgclass; o3_bgclass = ol_bgclass; o3_textfontclass = ol_textfontclass; o3_captionfontclass = ol_captionfontclass; o3_closefontclass = ol_closefontclass; o3_padunit = ol_padunit; o3_heightunit = ol_heightunit; o3_widthunit = ol_widthunit; o3_textsizeunit = ol_textsizeunit; o3_textdecoration = ol_textdecoration; o3_textstyle = ol_textstyle; o3_textweight = ol_textweight; o3_captionsizeunit = ol_captionsizeunit; o3_captiondecoration = ol_captiondecoration; o3_captionstyle = ol_captionstyle; o3_captionweight = ol_captionweight; o3_closesizeunit = ol_closesizeunit; o3_closedecoration = ol_closedecoration; o3_closestyle = ol_closestyle; o3_closeweight = ol_closeweight; fnRef = ''; // Special for frame support, over must be reset... if ( (ns4) || (ie4) || (ns6) ) { if (over) cClick(); o3_frame = ol_frame; if (ns4) over = o3_frame.document.overDiv if (ie4) over = o3_frame.overDiv.style if (ns6) over = o3_frame.document.getElementById("overDiv"); } // What the next argument is expected to be. var parsemode = -1, udf, v = null; var ar = arguments; udf = (!ar.length ? 1 : 0); for (i = 0; i < ar.length; i++) { if (parsemode < 0) { // Arg is maintext, unless its a PARAMETER if (typeof ar[i] == 'number') { udf = (ar[i] == FUNCTION ? 0 : 1); i--; } else { o3_text = ar[i]; } parsemode = 0; } else { // Note: NS4 doesn't like switch cases with vars. if (ar[i] == INARRAY) { udf = 0; o3_text = ol_texts[ar[++i]]; continue; } if (ar[i] == CAPARRAY) { o3_cap = ol_caps[ar[++i]]; continue; } if (ar[i] == STICKY) { o3_sticky = 1; continue; } if (ar[i] == BACKGROUND) { o3_background = ar[++i]; continue; } if (ar[i] == NOCLOSE) { o3_close = ""; continue; } if (ar[i] == CAPTION) { o3_cap = ar[++i]; continue; } if (ar[i] == CENTER || ar[i] == LEFT || ar[i] == RIGHT) { o3_hpos = ar[i]; continue; } if (ar[i] == OFFSETX) { o3_offsetx = ar[++i]; continue; } if (ar[i] == OFFSETY) { o3_offsety = ar[++i]; continue; } if (ar[i] == FGCOLOR) { o3_fgcolor = ar[++i]; continue; } if (ar[i] == BGCOLOR) { o3_bgcolor = ar[++i]; continue; } if (ar[i] == TEXTCOLOR) { o3_textcolor = ar[++i]; continue; } if (ar[i] == CAPCOLOR) { o3_capcolor = ar[++i]; continue; } if (ar[i] == CLOSECOLOR) { o3_closecolor = ar[++i]; continue; } if (ar[i] == WIDTH) { o3_width = ar[++i]; continue; } if (ar[i] == BORDER) { o3_border = ar[++i]; continue; } if (ar[i] == STATUS) { o3_status = ar[++i]; continue; } if (ar[i] == AUTOSTATUS) { o3_autostatus = (o3_autostatus == 1) ? 0 : 1; continue; } if (ar[i] == AUTOSTATUSCAP) { o3_autostatus = (o3_autostatus == 2) ? 0 : 2; continue; } if (ar[i] == HEIGHT) { o3_height = ar[++i]; o3_aboveheight = ar[i]; continue; } // Same param again. if (ar[i] == CLOSETEXT) { o3_close = ar[++i]; continue; } if (ar[i] == SNAPX) { o3_snapx = ar[++i]; continue; } if (ar[i] == SNAPY) { o3_snapy = ar[++i]; continue; } if (ar[i] == FIXX) { o3_fixx = ar[++i]; continue; } if (ar[i] == FIXY) { o3_fixy = ar[++i]; continue; } if (ar[i] == FGBACKGROUND) { o3_fgbackground = ar[++i]; continue; } if (ar[i] == BGBACKGROUND) { o3_bgbackground = ar[++i]; continue; } if (ar[i] == PADX) { o3_padxl = ar[++i]; o3_padxr = ar[++i]; continue; } if (ar[i] == PADY) { o3_padyt = ar[++i]; o3_padyb = ar[++i]; continue; } if (ar[i] == FULLHTML) { o3_fullhtml = 1; continue; } if (ar[i] == BELOW || ar[i] == ABOVE) { o3_vpos = ar[i]; continue; } if (ar[i] == CAPICON) { o3_capicon = ar[++i]; continue; } if (ar[i] == TEXTFONT) { o3_textfont = ar[++i]; continue; } if (ar[i] == CAPTIONFONT) { o3_captionfont = ar[++i]; continue; } if (ar[i] == CLOSEFONT) { o3_closefont = ar[++i]; continue; } if (ar[i] == TEXTSIZE) { o3_textsize = ar[++i]; continue; } if (ar[i] == CAPTIONSIZE) { o3_captionsize = ar[++i]; continue; } if (ar[i] == CLOSESIZE) { o3_closesize = ar[++i]; continue; } if (ar[i] == FRAME) { opt_FRAME(ar[++i]); continue; } if (ar[i] == TIMEOUT) { o3_timeout = ar[++i]; continue; } if (ar[i] == FUNCTION) { udf = 0; if (typeof ar[i+1] != 'number') v = ar[++i]; opt_FUNCTION(v); continue; } if (ar[i] == DELAY) { o3_delay = ar[++i]; continue; } if (ar[i] == HAUTO) { o3_hauto = (o3_hauto == 0) ? 1 : 0; continue; } if (ar[i] == VAUTO) { o3_vauto = (o3_vauto == 0) ? 1 : 0; continue; } if (ar[i] == CLOSECLICK) { o3_closeclick = (o3_closeclick == 0) ? 1 : 0; continue; } if (ar[i] == CSSOFF) { o3_css = ar[i]; continue; } if (ar[i] == CSSSTYLE) { o3_css = ar[i]; continue; } if (ar[i] == CSSCLASS) { o3_css = ar[i]; continue; } if (ar[i] == FGCLASS) { o3_fgclass = ar[++i]; continue; } if (ar[i] == BGCLASS) { o3_bgclass = ar[++i]; continue; } if (ar[i] == TEXTFONTCLASS) { o3_textfontclass = ar[++i]; continue; } if (ar[i] == CAPTIONFONTCLASS) { o3_captionfontclass = ar[++i]; continue; } if (ar[i] == CLOSEFONTCLASS) { o3_closefontclass = ar[++i]; continue; } if (ar[i] == PADUNIT) { o3_padunit = ar[++i]; continue; } if (ar[i] == HEIGHTUNIT) { o3_heightunit = ar[++i]; continue; } if (ar[i] == WIDTHUNIT) { o3_widthunit = ar[++i]; continue; } if (ar[i] == TEXTSIZEUNIT) { o3_textsizeunit = ar[++i]; continue; } if (ar[i] == TEXTDECORATION) { o3_textdecoration = ar[++i]; continue; } if (ar[i] == TEXTSTYLE) { o3_textstyle = ar[++i]; continue; } if (ar[i] == TEXTWEIGHT) { o3_textweight = ar[++i]; continue; } if (ar[i] == CAPTIONSIZEUNIT) { o3_captionsizeunit = ar[++i]; continue; } if (ar[i] == CAPTIONDECORATION) { o3_captiondecoration = ar[++i]; continue; } if (ar[i] == CAPTIONSTYLE) { o3_captionstyle = ar[++i]; continue; } if (ar[i] == CAPTIONWEIGHT) { o3_captionweight = ar[++i]; continue; } if (ar[i] == CLOSESIZEUNIT) { o3_closesizeunit = ar[++i]; continue; } if (ar[i] == CLOSEDECORATION) { o3_closedecoration = ar[++i]; continue; } if (ar[i] == CLOSESTYLE) { o3_closestyle = ar[++i]; continue; } if (ar[i] == CLOSEWEIGHT) { o3_closeweight = ar[++i]; continue; } } } if (udf && o3_function) o3_text = o3_function(); if (o3_delay == 0) { return overlib351(); } else { o3_delayid = setTimeout("overlib351()", o3_delay); return false; } } // Clears popups if appropriate function nd() { if ( o3_removecounter >= 1 ) { o3_showingsticky = 0 }; if ( (ns4) || (ie4) || (ns6) ) { if ( o3_showingsticky == 0 ) { o3_allowmove = 0; if (over != null) hideObject(over); } else { o3_removecounter++; } } return true; } //////////////////////////////////////////////////////////////////////////////////// // OVERLIB 3.51 FUNCTION //////////////////////////////////////////////////////////////////////////////////// // This function decides what it is we want to display and how we want it done. function overlib351() { // Make layer content var layerhtml; if (o3_background != "" || o3_fullhtml) { // Use background instead of box. layerhtml = ol_content_background(o3_text, o3_background, o3_fullhtml); } else { // They want a popup box. // Prepare popup background if (o3_fgbackground != "" && o3_css == CSSOFF) { o3_fgbackground = "BACKGROUND=\""+o3_fgbackground+"\""; } if (o3_bgbackground != "" && o3_css == CSSOFF) { o3_bgbackground = "BACKGROUND=\""+o3_bgbackground+"\""; } // Prepare popup colors if (o3_fgcolor != "" && o3_css == CSSOFF) { o3_fgcolor = "BGCOLOR=\""+o3_fgcolor+"\""; } if (o3_bgcolor != "" && o3_css == CSSOFF) { o3_bgcolor = "BGCOLOR=\""+o3_bgcolor+"\""; } // Prepare popup height if (o3_height > 0 && o3_css == CSSOFF) { o3_height = "HEIGHT=" + o3_height; } else { o3_height = ""; } // Decide which kinda box. if (o3_cap == "") { // Plain layerhtml = ol_content_simple(o3_text); } else { // With caption if (o3_sticky) { // Show close text layerhtml = ol_content_caption(o3_text, o3_cap, o3_close); } else { // No close text layerhtml = ol_content_caption(o3_text, o3_cap, ""); } } } // We want it to stick! if (o3_sticky) { if (o3_timerid > 0) { clearTimeout(o3_timerid); o3_timerid = 0; } o3_showingsticky = 1; o3_removecounter = 0; } // Write layer layerWrite(layerhtml); // Prepare status bar if (o3_autostatus > 0) { o3_status = o3_text; if (o3_autostatus > 1) { o3_status = o3_cap; } } // When placing the layer the first time, even stickies may be moved. o3_allowmove = 0; // Initiate a timer for timeout if (o3_timeout > 0) { if (o3_timerid > 0) clearTimeout(o3_timerid); o3_timerid = setTimeout("cClick()", o3_timeout); } // Show layer disp(o3_status); // Stickies should stay where they are. if (o3_sticky) o3_allowmove = 0; return (o3_status != ''); } //////////////////////////////////////////////////////////////////////////////////// // LAYER GENERATION FUNCTIONS //////////////////////////////////////////////////////////////////////////////////// // Makes simple table without caption function ol_content_simple(text) { if (o3_css == CSSCLASS) txt = "
"+text+"
"; if (o3_css == CSSSTYLE) txt = "
"+text+"
"; if (o3_css == CSSOFF) txt = "
"+text+"
"; set_background(""); return txt; } // Makes table with caption and optional close link function ol_content_caption(text, title, close) { closing = ""; closeevent = "onMouseOver"; if (o3_closeclick == 1) closeevent = "onClick"; if (o3_capicon != "") o3_capicon = " "; if (close != "") { if (o3_css == CSSCLASS) closing = ""+close+""; if (o3_css == CSSSTYLE) closing = ""+close+""; if (o3_css == CSSOFF) closing = ""+close+""; } if (o3_css == CSSCLASS) txt = "
"+closing+"
"+o3_capicon+title+"
"+text+"
"; if (o3_css == CSSSTYLE) txt = "
"+closing+"
"+o3_capicon+title+"
"+text+"
"; if (o3_css == CSSOFF) txt = "
"+closing+"
"+o3_capicon+title+"
"+text+"
"; set_background(""); return txt; } // Sets the background picture, padding and lots more. :) function ol_content_background(text, picture, hasfullhtml) { var txt; if (hasfullhtml) { txt = text; } else { var pU, hU, wU; pU = (o3_padunit == '%' ? '%' : ''); hU = (o3_heightunit == '%' ? '%' : ''); wU = (o3_widthunit == '%' ? '%' : ''); if (o3_css == CSSCLASS) txt = "
"+text+"
"; if (o3_css == CSSSTYLE) txt = "
"+text+"
"; if (o3_css == CSSOFF) txt = "
"+text+"
"; } set_background(picture); return txt; } // Loads a picture into the div. function set_background(pic) { if (pic == "") { if (ns4) over.background.src = null; if (ie4) over.backgroundImage = "none"; if (ns6) over.style.backgroundImage = "none"; } else { if (ns4) { over.background.src = pic; } else if (ie4) { over.backgroundImage = "url("+pic+")"; } else if (ns6) { over.style.backgroundImage = "url("+pic+")"; } } } //////////////////////////////////////////////////////////////////////////////////// // HANDLING FUNCTIONS //////////////////////////////////////////////////////////////////////////////////// // Displays the popup function disp(statustext) { if ( (ns4) || (ie4) || (ns6) ) { if (o3_allowmove == 0) { placeLayer(); showObject(over); o3_allowmove = 1; } } if (statustext != "") { self.status = statustext; } } // Decides where we want the popup. function placeLayer() { var placeX, placeY; // HORIZONTAL PLACEMENT if (o3_fixx > -1) { // Fixed position placeX = o3_fixx; } else { winoffset = (ie4) ? eval('o3_frame.'+docRoot+'.scrollLeft') : o3_frame.pageXOffset; if (ie4) iwidth = eval('o3_frame.'+docRoot+'.clientWidth'); if (ns4 || ns6) iwidth = o3_frame.innerWidth; // If HAUTO, decide what to use. if (o3_hauto == 1) { if ( (o3_x - winoffset) > ((eval(iwidth)) / 2)) { o3_hpos = LEFT; } else { o3_hpos = RIGHT; } } // From mouse if (o3_hpos == CENTER) { // Center placeX = o3_x+o3_offsetx-(o3_width/2); if (placeX < winoffset) placeX = winoffset; } if (o3_hpos == RIGHT) { // Right placeX = o3_x+o3_offsetx; if ( (eval(placeX) + eval(o3_width)) > (winoffset + iwidth) ) { placeX = iwidth + winoffset - o3_width; if (placeX < 0) placeX = 0; } } if (o3_hpos == LEFT) { // Left placeX = o3_x-o3_offsetx-o3_width; if (placeX < winoffset) placeX = winoffset; } // Snapping! if (o3_snapx > 1) { var snapping = placeX % o3_snapx; if (o3_hpos == LEFT) { placeX = placeX - (o3_snapx + snapping); } else { // CENTER and RIGHT placeX = placeX + (o3_snapx - snapping); } if (placeX < winoffset) placeX = winoffset; } } // VERTICAL PLACEMENT if (o3_fixy > -1) { // Fixed position placeY = o3_fixy; } else { scrolloffset = (ie4) ? eval('o3_frame.'+docRoot+'.scrollTop') : o3_frame.pageYOffset; // If VAUTO, decide what to use. if (o3_vauto == 1) { if (ie4) iheight = eval('o3_frame.'+docRoot+'.clientHeight'); if (ns4 || ns6) iheight = o3_frame.innerHeight; iheight = (eval(iheight)) / 2; if ( (o3_y - scrolloffset) > iheight) { o3_vpos = ABOVE; } else { o3_vpos = BELOW; } } // From mouse if (o3_vpos == ABOVE) { if (o3_aboveheight == 0) { var divref = (ie4) ? o3_frame.document.all['overDiv'] : over; o3_aboveheight = (ns4) ? divref.clip.height : divref.offsetHeight; } placeY = o3_y - (o3_aboveheight + o3_offsety); if (placeY < scrolloffset) placeY = scrolloffset; } else { // BELOW placeY = o3_y + o3_offsety; } // Snapping! if (o3_snapy > 1) { var snapping = placeY % o3_snapy; if (o3_aboveheight > 0 && o3_vpos == ABOVE) { placeY = placeY - (o3_snapy + snapping); } else { placeY = placeY + (o3_snapy - snapping); } if (placeY < scrolloffset) placeY = scrolloffset; } } // Actually move the object. repositionTo(over, placeX, placeY); } // Moves the layer function mouseMove(e) { if ( (ns4) || (ns6) ) {o3_x=e.pageX; o3_y=e.pageY;} if (ie4) {o3_x=event.x; o3_y=event.y;} if (ie5) {o3_x=eval('event.x+o3_frame.'+docRoot+'.scrollLeft'); o3_y=eval('event.y+o3_frame.'+docRoot+'.scrollTop');} if (o3_allowmove == 1) { placeLayer(); } } // The Close onMouseOver function for stickies function cClick() { hideObject(over); o3_showingsticky = 0; return false; } // Makes sure target frame has overLIB function compatibleframe(frameid) { if (ns4) { if (typeof frameid.document.overDiv =='undefined') return false; } else if (ie4) { if (typeof frameid.document.all["overDiv"] =='undefined') return false; } else if (ns6) { if (frameid.document.getElementById('overDiv') == null) return false; } return true; } //////////////////////////////////////////////////////////////////////////////////// // LAYER FUNCTIONS //////////////////////////////////////////////////////////////////////////////////// // Writes to a layer function layerWrite(txt) { txt += "\n"; if (ns4) { var lyr = o3_frame.document.overDiv.document lyr.write(txt) lyr.close() } else if (ie4) { o3_frame.document.all["overDiv"].innerHTML = txt } else if (ns6) { range = o3_frame.document.createRange(); range.setStartBefore(over); domfrag = range.createContextualFragment(txt); while (over.hasChildNodes()) { over.removeChild(over.lastChild); } over.appendChild(domfrag); } } // Make an object visible function showObject(obj) { if (ns4) obj.visibility = "show"; else if (ie4) obj.visibility = "visible"; else if (ns6) obj.style.visibility = "visible"; } // Hides an object function hideObject(obj) { if (ns4) obj.visibility = "hide"; else if (ie4) obj.visibility = "hidden"; else if (ns6) obj.style.visibility = "hidden"; if (o3_timerid > 0) clearTimeout(o3_timerid); if (o3_delayid > 0) clearTimeout(o3_delayid); o3_timerid = 0; o3_delayid = 0; self.status = ""; } // Move a layer function repositionTo(obj,xL,yL) { if ( (ns4) || (ie4) ) { obj.left = (ie4 ? xL + 'px' : xL); obj.top = (ie4 ? yL + 'px' : yL); } else if (ns6) { obj.style.left = xL + "px"; obj.style.top = yL+ "px"; } } function getFrameRef(thisFrame, ofrm) { var retVal = ''; for (var i=0; i 0) { retVal = getFrameRef(thisFrame[i],ofrm); if (retVal == '') continue; } else if (thisFrame[i] != ofrm) continue; retVal = '['+i+']' + retVal; break; } return retVal; } //////////////////////////////////////////////////////////////////////////////////// // PARSER FUNCTIONS //////////////////////////////////////////////////////////////////////////////////// // Defines which frame we should point to. function opt_FRAME(frm) { o3_frame = compatibleframe(frm) ? frm : ol_frame; if (o3_frame != ol_frame) { var tFrm = getFrameRef(top.frames, o3_frame); var sFrm = getFrameRef(top.frames, ol_frame); if (sFrm.length == tFrm.length) { l = tFrm.lastIndexOf('['); if (l) { while(sFrm.substring(0,l) != tFrm.substring(0,l)) l = tFrm.lastIndexOf('[',l-1); tFrm = tFrm.substr(l); sFrm = sFrm.substr(l); } } var cnt = 0, p = '', str = tFrm; while((k = str.lastIndexOf('[')) != -1) { cnt++; str = str.substring(0,k); } for (var i=0; i 0))?($_GET['relative_start']):86400; $gantt_relative_stop_date = (array_key_exists('relative_stop',$_GET) or ($_GET['relative_stop'] > 0))?($_GET['relative_stop']):86400; $resource_filter = $_GET['filter']; //////////////////////////////////////////////////////////////////////////////// // Configuration //////////////////////////////////////////////////////////////////////////////// $CONF=array(); $CONF['hierarchy_resource_width'] = 10; $CONF['scale'] = 10; $CONF['time_ruler_scale'] = 5; $CONF['gantt_top'] = 30; $CONF['bottom_margin'] = 45; $CONF['right_margin'] = 10; $CONF['label_right_align'] = 105; $CONF['hierarchy_left_align'] = 110; $CONF['gantt_left_align'] = 160; $CONF['gantt_width'] = 1000; $CONF['gantt_min_job_width_for_label'] = 50; $CONF['resource_hierarchy'] = array('cluster','host','cpu','core'); $CONF['resource_properties'] = array('ib10g', 'core', 'deploy', 'cpuset', 'besteffort', 'ip', 'ib10gmodel', 'disktype', 'nodemodel', 'memnode', 'memcore', 'ethnb', 'cluster', 'cpuarch', 'myri2gmodel', 'cpu', 'cpucore', 'myri10g', 'memcpu', 'network_address', 'virtual', 'host', 'rconsole', 'myri10gmodel', 'cputype', 'switch', 'cpufreq', 'type', 'myri2g'); $CONF['resource_labels'] = array('host','cpuset'); $CONF['state_colors'] = array('Absent' => 'url(#absentPattern)', 'Suspected' => 'url(#suspectedPattern)', 'Dead' => 'url(#deadPattern)'); $CONF['job_colors'] = array('besteffort' => 'url(#besteffortPattern)', 'deploy' => 'url(#deployPattern)', 'container' => 'url(#containerPattern)', 'timesharing=.*' => 'url(#timesharingPattern)'); //////////////////////////////////////////////////////////////////////////////// // Utility functions //////////////////////////////////////////////////////////////////////////////// // conversion function from date to pixel coordinates in the gantt function date2px($date) { global $CONF, $gantt_start_date, $gantt_stop_date, $gantt_now; if ($date < $gantt_start_date) { return $CONF['gantt_left_align']; } if ($date > $gantt_stop_date) { return $CONF['gantt_left_align'] + $CONF['gantt_width']; } return round($CONF['gantt_left_align'] + ($CONF['gantt_width'] * ($date - $gantt_start_date)) / ($gantt_stop_date - $gantt_start_date)); } // sort function for resource_ids function resource_id_sort($r1, $r2) { $m1 = array(); $m2 = array(); $regex = '/^(\w+)-(\d+)\./'; preg_match($regex, $r1->resources['host']->id, $m1); preg_match($regex, $r2->resources['host']->id, $m2); return ($m1[1] > $m2[1]) or (($m1[1] == $m2[1]) and ($m1[2] > $m2[2])) or (($m1[1] == $m2[1]) and ($m1[2] == $m2[2]) and ($r1->cpuset > $r2->cpuset)); } // display function for resource labels function custom_resource_label($r) { if ($r->type == 'host') { return preg_replace('/^(\w+-\d+)\..*$$/','$1',$r->id); } return $r->id; } //////////////////////////////////////////////////////////////////////////////// // Some classes to handle data //////////////////////////////////////////////////////////////////////////////// // Storage class for State class State { public $value, $start, $stop; function __construct($value, $start, $stop) { global $gantt_start_date, $gantt_stop_date; $this->value = $value; $this->start = ($start < $gantt_start_date)?$gantt_start_date:$start; $this->stop = (($stop == 0) or ($stop > $gantt_stop_date))?$gantt_stop_date:$stop; } function svg_text() { $output = "State: {$this->value}"; $output .= "|Since: ".date("r", $this->start); $output .= "|Until: ".date("r", $this->stop); return $output; } } // Storage class for jobs class Job { public $job_id,$job_type,$state,$job_user,$command,$queue_name,$moldable_walltime,$properties,$launching_directory,$submission_time,$start_time,$stop_time,$resource_ids,$network_addresses,$types; protected $color; function __construct($job_id,$job_type,$state,$job_user,$command,$queue_name,$moldable_walltime,$properties,$launching_directory,$submission_time,$start_time,$stop_time) { $this->job_id = $job_id; $this->job_type = $job_type; $this->state = $state; $this->job_user = $job_user; $this->command = $command; $this->queue_name = $queue_name; $this->moldable_walltime = $moldable_walltime; $this->properties = $properties; $this->launching_directory = $launching_directory; $this->submission_time = $submission_time; $this->start_time = $start_time; $this->stop_time = $stop_time; $this->resource_ids = array(); $this->network_addresses = array(); $this->types = array(); $this->color = NULL; } function add_resource_id($resource_id) { $this->resource_ids[$resource_id->id] = $resource_id; } function add_type($type) { if (! in_array($type, $this->types)) { array_push($this->types, $type); } } function add_network_address($network_address) { if (! in_array($network_address, $this->network_addresses)) { array_push($this->network_addresses, $network_address); } } function group_resource_ids($resource_ids) { $grp = NULL; foreach ($resource_ids as $rid) { if (array_key_exists($rid->id, $this->resource_ids)) { if ($grp == NULL) { $grp = new JobResourceIdGroup($this, $rid); } else { $grp->add_resource_id($rid); } } else { $grp = NULL; } } } function svg_text() { $output = "Jobid: {$this->job_id}"; $output .= "|User: {$this->job_user}"; $output .= "|Kind: {$this->job_type}"; $output .= "|Queue: {$this->queue_name}"; $output .= "|Types: ".join(", ",$this->types); $output .= "|Walltime: {$this->moldable_walltime}"; $output .= "|Resources: ".count($this->resource_ids); $output .= "|Machines: ".count($this->network_addresses); $output .= "|Submission: ".date("r", $this->submission_time); $output .= "|Start: ".date("r", $this->start_time); $output .= "|Stop: ".(($this->stop_time > 0)?(date("r", $this->start_time)):(date("r", $this->start_time + $this->moldable_walltime))); $output .= "|State: {$this->state}"; //$output .= "|Properties: {$this->properties}"; return $output; } function color() { if ($this->color == NULL) { $this->color = 'rgb('.(rand(128,191)).','.(rand(64,255)).','.(rand(64,255)).')'; } return $this->color; } } // Container class for a job resource_ids, split wrt the resource_ids display order (resources part and not part of job may be interleaved) class JobResourceIdGroup { public $job, $resource_ids; function __construct($job, $resource_id) { $this->job = $job; $resource_id->add_job_resource_id_group($this); $this->resource_ids = array( $resource_id->id => $resource_id ); } function add_resource_id($resource_id) { array_push($this->resource_ids, $resource_id); } function size() { return count($this->resource_ids); } } // Storage class for the resource_ids class ResourceId { public $id, $cpuset, $states, $job_resource_id_groups, $resources, $properties; function __construct($id, $cpuset) { $this->id = $id; $this->cpuset = $cpuset; $this->states = array(); $this->job_resource_id_groups = array(); $this->resources = array(); $this->properties = array(); } function add_state($value, $start, $stop) { array_push($this->states, new State($value, $start, $stop)); } function add_resource($resource) { $this->resources[$resource->type] = $resource; } function add_property($key, $value) { $this->properties[$key] = $value; ksort($this->properties); } function add_job_resource_id_group($job_resource_id_group) { array_push($this->job_resource_id_groups, $job_resource_id_group); } function resource_label($type) { if ($type == 'cpuset') { return $this->cpuset; } else { return custom_resource_label($this->resources[$type]); } } function svg_text() { $sep = ""; $output = ""; foreach ($this->properties as $key => $value) { $output .= $sep."{$key}: {$value}"; $sep = "|"; } return $output; } function svg_label($y) { global $CONF; $output = ''; $labels = array(); foreach ($CONF['resource_labels'] as $type) { array_push($labels, $this->resource_label($type)); } $output .= join("/", $labels); $output .= ''; return $output; } function svg_lines($y) { global $CONF; $output .= ''; return $output; } function svg_states($y) { global $CONF; foreach ($this->states as $state) { $output .= ''; } return $output; } function svg_jobs($y) { global $CONF, $gantt_now; foreach ($this->job_resource_id_groups as $grp) { if($grp->job->stop_time > 0) { $width = date2px($grp->job->stop_time) - date2px($grp->job->start_time); } else { if (in_array('besteffort', $grp->job->types) and ($grp->job->state == "Running")) { $width = date2px($gantt_now) - date2px($grp->job->start_time); } else { $width = date2px($grp->job->start_time + $grp->job->moldable_walltime) - date2px($grp->job->start_time); } } foreach ($CONF['job_colors'] as $type => $color) { if (preg_grep("/^{$type}$/", $grp->job->types)) { $output .= ''; } } $output .= ''; if ($width > $CONF['gantt_min_job_width_for_label']) { $output .= ''; $output .= $grp->job->job_id; $output .= ''; } } return $output; } } // Storage for the abstract resources, e.g. host, cpu, core, a.s.o, i.e. not resource_id class Resource { public $id, $type, $parent, $childs, $resource_ids; function __construct($id, $type, $parent) { $this->id = $id; $this->type = $type; $this->parent = $parent; $this->childs = array(); $this->resource_ids = array(); } function add_child($id, $type) { if (! array_key_exists($id,$this->childs)) { $this->childs[$id] = new Resource($id, $type, $this); } return $this->childs[$id]; } function add_resource_id($rid) { $this->resource_ids[$rid->id] = $rid; $rid->add_resource($this); } function svg_hierarchy_text() { if ($this->parent == NULL) { return $this->type.": ".$this->id; } else { return $this->parent->svg_hierarchy_text()."|".$this->type.": ".$this->id; } } } /////////////////////////////////////////////////////////////////////////////// // Retrieve OAR data from database /////////////////////////////////////////////////////////////////////////////// // Connecting, selecting database $link = mysql_connect("mysql.$site.grid5000.fr", 'oarreader', 'read') or die('Could not connect: ' . mysql_error()); mysql_select_db('oar2') or die('Could not select database'); // Retrieve the "now" date $query = 'SELECT UNIX_TIMESTAMP()'; $result = mysql_query($query) or die('Query failed: ' . mysql_error()); $array = mysql_fetch_array($result,MYSQL_NUM); $gantt_now = $array[0]; mysql_free_result($result); if ($gantt_start_date == 0) { $gantt_start_date = $gantt_now - $gantt_relative_start_date; } if ($gantt_stop_date == 0) { $gantt_stop_date = $gantt_now + $gantt_relative_stop_date; } // Retrieve the resource hierarchy $resource_root = new Resource($site, 'site', NULL); $resource_ids = array(); $query = 'SELECT ' . join(',',array_unique(array_merge($CONF['resource_properties'], $CONF['resource_hierarchy'], array('cpuset', 'resource_id')))) . ' FROM resources' . ($resource_filter?' WHERE '.stripslashes($resource_filter):''); $result = mysql_query($query) or die('Query failed: ' . mysql_error()); while ($line = mysql_fetch_array($result, MYSQL_ASSOC)) { $rid = new ResourceId($line['resource_id'], $line['cpuset']); foreach ($CONF['resource_properties'] as $rp) { $rid->add_property($rp, $line[$rp]); } $resource_ids[$line['resource_id']] = $rid; $resource_root->add_resource_id($rid); $r = $resource_root; foreach ($CONF['resource_hierarchy'] as $rh) { $r = $r->add_child($line[$rh],$rh); $r->add_resource_id($rid); } } mysql_free_result($result); // sort resource_ids uasort($resource_ids, "resource_id_sort"); // Retrieve the states of resources $query = <<= {$gantt_start_date} ) EOT; $result = mysql_query($query) or die('Query failed: ' . mysql_error()); while ($line = mysql_fetch_array($result, MYSQL_ASSOC)) { if (array_key_exists($line['resource_id'], $resource_ids)) { $resource_ids[$line['resource_id']]->add_state($line['value'], $line['date_start'], $line['date_stop']); } } mysql_free_result($result); // Array to store jobs $jobs = array(); // Retrieve past and current jobs $query = <<= {$gantt_start_date} OR ( jobs.stop_time = '0' AND ( jobs.state = 'Running' OR jobs.state = 'Suspended' OR jobs.state = 'Resuming' ) ) ) AND jobs.start_time < {$gantt_stop_date} AND jobs.assigned_moldable_job = assigned_resources.moldable_job_id AND moldable_job_descriptions.moldable_job_id = jobs.job_id AND resources.resource_id = assigned_resources.resource_id ORDER BY jobs.job_id EOT; $result = mysql_query($query) or die('Query failed: ' . mysql_error()); while ($line = mysql_fetch_array($result, MYSQL_ASSOC)) { if (! array_key_exists($line['job_id'], $jobs)) { $jobs[$line['job_id']] = new Job($line['job_id'], $line['job_type'], $line['state'], $line['job_user'], $line['command'], $line['queue_name'], $line['moldable_walltime'], $line['properties'], $line['launching_directory'], $line['submission_time'], $line['start_time'], $line['stop_time']); } if (array_key_exists($line['resource_id'], $resource_ids)) { $jobs[$line['job_id']]->add_resource_id($resource_ids[$line['resource_id']]); } else { // create new resource_id so than the job gets the right resource_id count, even if that resource_id is not diplayed in the grid... (filter) $jobs[$line['job_id']]->add_resource_id(new ResourceId($line['resource_id'], -1)); } $jobs[$line['job_id']]->add_network_address($line['network_address']); $jobs[$line['job_id']]->add_type($line['type']); } mysql_free_result($result); // Retrieve predicted jobs (future) $query = <<= {$gantt_start_date} AND jobs.job_id NOT IN ( SELECT job_id FROM job_types WHERE type = 'besteffort' AND types_index = 'CURRENT' ) AND job_types.job_id = jobs.job_id ORDER BY jobs.job_id EOT; $result = mysql_query($query) or die('Query failed: ' . mysql_error()); while ($line = mysql_fetch_array($result, MYSQL_ASSOC)) { if (! array_key_exists($line['job_id'], $jobs)) { $jobs[$line['job_id']] = new Job($line['job_id'], $line['job_type'], $line['state'], $line['job_user'], $line['command'], $line['queue_name'], $line['moldable_walltime'], $line['properties'], $line['launching_directory'], $line['submission_time'], $line['start_time'], $line['stop_time']); } if (array_key_exists($line['resource_id'], $resource_ids)) { $jobs[$line['job_id']]->add_resource_id($resource_ids[$line['resource_id']]); } else { // create new resource_id so than the job gets the right resource_id count, even if that resource_id is not displayed in the grid... (filter) $jobs[$line['job_id']]->add_resource_id(new ResourceId($line['resource_id'], -1)); } $jobs[$line['job_id']]->add_network_address($line['network_address']); $jobs[$line['job_id']]->add_type($line['type']); } mysql_free_result($result); // Split resource_ids for jobs in groups for gantt display: resources which belong to the job may be interleaved with resources which don't foreach ($jobs as $job) { $job->group_resource_ids($resource_ids); } // Closing connection to the database mysql_close($link); /////////////////////////////////////////////////////////////////////////////// // SVG document generation /////////////////////////////////////////////////////////////////////////////// // compute page size $page_height = $CONF['gantt_top'] + count($resource_ids) * $CONF['scale'] + $CONF['bottom_margin']; $page_width = $CONF['gantt_left_align'] + $CONF['gantt_width'] + $CONF['right_margin']; // begin SVG doc + script + texture patterns $output = << B C D T EOT; // print gantt border $output .= ''; // print top time ruler $output .= ''.date("Y-m-d",$gantt_start_date).''; $output .= ''.date("H:i:s",$gantt_start_date).''; $output .= ''.date("Y-m-d",$gantt_stop_date).''; $output .= ''.date("H:i:s",$gantt_stop_date).''; for($i=1;$i<($CONF['time_ruler_scale']);$i++) { $d = $gantt_start_date + $i * ($gantt_stop_date - $gantt_start_date) / ($CONF['time_ruler_scale']); $output .= ''.date("Y-m-d",$d).''; $output .= ''.date("H:i:s",$d).''; } // print bottom time ruler $output .= ''.date("Y-m-d",$gantt_stop_date).''; $output .= ''.date("H:i:s",$gantt_stop_date).''; $output .= ''.date("Y-m-d",$gantt_start_date).''; $output .= ''.date("H:i:s",$gantt_start_date).''; for($i=1;$i<($CONF['time_ruler_scale']);$i++) { $d = $gantt_start_date + $i * ($gantt_stop_date - $gantt_start_date) / ($CONF['time_ruler_scale']); $output .= ''.date("Y-m-d",$d).''; $output .= ''.date("H:i:s",$d).''; } // print time grid lines for($i=1;$i<($CONF['time_ruler_scale']);$i++) { $d = $gantt_start_date + $i * ($gantt_stop_date - $gantt_start_date) / ($CONF['time_ruler_scale']); $output .= ''; } // print resource_id labels $y = $CONF['gantt_top']; foreach ($resource_ids as $rid) { $output .= $rid->svg_label($y); $y += $CONF['scale']; } // print resource hierarchy $x = $CONF['hierarchy_left_align']; foreach ($CONF['resource_hierarchy'] as $rh) { $r0 = NULL; $y0 = $CONF['gantt_top']; $y = $y0; foreach ($resource_ids as $rid) { if (($r0 != NULL) and ($rid->resources[$rh]->id != $r0->id)) { $output .= ''; $y0 = $y; } $r0 = $rid->resources[$rh]; $y += $CONF['scale']; } if ($r0) { $output .= ''; $x += $CONF['scale']; } } // print resource_id lines $y = $CONF['gantt_top']; foreach ($resource_ids as $rid) { $output .= $rid->svg_lines($y); $y += $CONF['scale']; } // print resource states $y = $CONF['gantt_top']; foreach ($resource_ids as $rid) { $output .= $rid->svg_states($y); $y += $CONF['scale']; } // print jobs $y = $CONF['gantt_top']; foreach ($resource_ids as $rid) { $output .= $rid->svg_jobs($y); $y += $CONF['scale']; } // print mobile time ruler $output .= ''; $output .= ''; $output .= ''.date("Y-m-d",$gantt_start_date).''; $output .= ''.date("H:i:s",$gantt_start_date).''; $output .= ''.date("Y-m-d",$gantt_stop_date).''; $output .= ''.date("H:i:s",$gantt_stop_date).''; for($i=1;$i<($CONF['time_ruler_scale']);$i++) { $d = $gantt_start_date + $i * ($gantt_stop_date - $gantt_start_date) / ($CONF['time_ruler_scale']); $output .= ''.date("Y-m-d",$d).''; $output .= ''.date("H:i:s",$d).''; } $output .= ''; // print now line $output .= ''; // end SVG doc $output .= << EOT; header("Content-Type: image/svg+xml"); header('Content-Encoding: gzip'); print gzencode($output); ?> ./oar-2.5.2/sources/visualization_interfaces/DrawGantt/drawgantt.conf.in0000644000175000017500000001205211757171206024512 0ustar plbplb--- ############################ # DrawGrantt configuration. ############################ # # This is a YAML formated file # # # oar DB parameters # # Database type ("mysql" or "Pg") DB_TYPE: mysql # Database connection port (default 3306 for mysql or 5432 for Pg) DB_PORT: 3306 # DataBase hostname DB_HOSTNAME: localhost # Database base name DB_BASE_NAME: oar # DataBase read only user name DB_BASE_LOGIN_RO: oar # DataBase read only user password DB_BASE_PASSWD_RO: oar ############################################################################## ## visual settings ############################################################################## title: 'Gantt Chart' sizex: 1000 # sizey: 600 offsetgridy: 50 left_offsetgridx: 190 right_offsetgridx: 10 tics_node: 1 points_per_cpu: 10 #background: '#000000' #gridcolor: '#C0C0C0' background: '#FFFFFF' gridcolor: '#101010' default_range: '1 day' #default_range: '3 days' #default_hour: '12:00' #NOT YET IMPLEMENTED ############################################################################## ## control job display by size area ############################################################################## #xratio: ratio between sizex / jobx xratio: 1000 #number of contiguous resources nb_cont_res: 0 ############################################################################## ## box and default BestEffort Job drawing setting ############################################################################## #NOT YET IMPLEMENTED #drawBestEffortbox: yes drawBestEffortbox: no #drawBestEffortDefault: BestEffort ############################################################################## ## color setting and disabling diplaying job id for cosystem Job ############################################################################## #cosystem: true #cosystem_color: '#D0D0D0' ############################################################################## ## directory for image, map, icons and javascript ############################################################################## web_root: '%%OARHOMEDIR%%' directory: 'drawgantt-files' web_cache_directory: 'cache' web_icons_directory: 'Icons' web_path_js_directory: 'js' ############################################################################## ## number file limit in cache ############################################################################## # # principle: when the limit is reach the cache is empty with half # note: nb_file = image and map files #NOT YET TESTED nb_file_cache_limit: 100 ############################################################################## ## resources sorting and labelling label displaying and sorting ############################################################################## # resource_filter: property: "network_address" regex: "(.*)" ############################################################################## ## resources sorting and labelling label displaying and sorting ############################################################################## # # Principles: # # First, you need sorting and labelling parameter for each type of resource # (the type be default is "default") # # Second, groups of even typed resources is order according to the order # of parameter subsections below. # # Third : two resource field properties (from oar database) can be used for # resources sorting and labbelling. # # Fourth for sorting, regular expressions must be given to extract # matching expressions for sorting. Sorting can be numerical, string or # natural ordered. Default setting is natural order. # # Fifth for labelling, regular expressions must be also given to extract # matching expressions for display. # # Popular regular expressions ## - ex: regex: "(\d+)" ie extract the first number from the left in nodenames ## - ex: regex: ([^.]+) ie to extract the short hostname. ## - ex: regex: (.+) ie to keep the whole word if really needed. ## ## sub-subsection is separated by "-", indentation is sensitive and must be ## provide by space and NOT tab character. ## sort_label_conf: - type: "default" # the two resource field properties first_field_property: "network_address" second_field_property: "cpu" #sorting first_sorting_order: "natural" first_sorting_regex: "(.+)" second_sorting_order: "numerical" second_sorting_regex: "(\d+)" #labelling first_displaying_regex: "(.+)" separator: "/" second_displaying_regex: "(\d+)" - type: "mathlab" # the two resource field properties first_field_property: "type" second_field_property: "cpu" #sorting first_sorting_order: "string" first_sorting_regex: "(.+)" second_sorting_order: "numerical" second_sorting_regex: "(\d+)" #labelling first_displaying_regex: "(.+)" separator: "#" second_displaying_regex: "(\d+)" ############################################################################## ## display properties' resource hierarchy ############################################################################## # # Select properties to display prop_hierarchy: #- cluster - network_address - cpu # - core ./oar-2.5.2/sources/visualization_interfaces/DrawGantt/drawgantt.cgi.in0000644000175000017500000010547311757171206024341 0ustar plbplb#!/usr/bin/ruby -w # $Id$ # drawgantt.cgi displays gantt chart for oar resource management system # # author: auguste@imag.fr # # requirements: # ruby1.8 (or greater) # libdbi-ruby # libdbd-mysql-ruby or libdbd-pg-ruby # libgd-ruby1.8 # libyaml-ruby # # # TODO: # - transparencies for aggregation and timesharing # - debug postgresql case # - wizard for auto configuration ? # - display state node information for status different of alive # - sparkline chart for workload information sumarize? # - job/user highlighting (by apply some picture manipulation from general picture ?) # - fix quote problem javascript popup # require 'dbi' require 'cgi' require 'time' require 'optparse' require 'yaml' require 'GD' require 'pp' MONTHS = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'] DAYS = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'] RANGE_ORDER = ['1/6 day','1/2 day','1 day','3 days','week','month'] RANGE_SEC = {'1/6 day'=>14400,'1/2 day'=>43200,'1 day'=>86400,'3 days'=>259200, 'week'=>604800,'month'=>2678400,'year'=>31622400} RANGE_STEP = {'1/6 day'=>3600,'1/2 day'=>10800,'1 day'=>43200,'3 days'=>86400, 'week'=>345600,'month'=>604800} $verbose = false $val = "" # variable for debug purpose ########################################################################## # usefull method # def String.natural_order(regex=Regexp.new('(.*)'),nocase=false) proc do |str| i = true str =~ regex str = $1 str = str.upcase if nocase str.gsub(/\s+/, '').split(/(\d+)/).map {|x| (i = !i) ? x.to_i : x} end end ########################################################################## # database operations # def base_connect db_type = $conf['DB_TYPE'] if db_type == "mysql" db_type == "Mysql" end if !($conf['DB_PORT'].nil?) db_port = $conf['DB_PORT'] elsif db_type == "Mysql" db_port = 3306 else db_port = 5432 end connection_string = "DBI:#{db_type}:database=#{$conf['DB_BASE_NAME']};host=#{$conf['DB_HOSTNAME']};port=#{db_port}" return DBI.connect(connection_string, "#{$conf['DB_BASE_LOGIN_RO']}", "#{$conf['DB_BASE_PASSWD_RO']}") end # Build the list of the property fields of the resource_properties table # parameters : dbh # return value : list of resource properties # side effects : set $resource_properties_fields def build_resource_properties_fields_list(dbh) if ( $resource_properties_fields ) return $resource_properties_fields end db_type = $conf['DB_TYPE'] if (db_type == "Pg") q = "SELECT pg_attribute.attname AS field FROM pg_class, pg_attribute WHERE pg_class.relname = \'resources\' and pg_attribute.attnum > 0 and pg_attribute.attrelid = pg_class.oid" else q = "SHOW COLUMNS FROM resources" end res = dbh.execute(q) $resource_properties_fields = [] res.each do |r| $resource_properties_fields << r.first end return $resource_properties_fields end # build_resources_list # Build the filtered list of all target resources # parameters : dbh # return value : list of resources # side effects : set $filtered_resources def build_resources_list(dbh) if ( $filtered_resources ) return $filtered_resources end # First, make sure $resource_properties_fields has been built build_resource_properties_fields_list( dbh ) if ( $conf["resource_filter"] ) resource_filter_property_index = $resource_properties_fields.index( $conf["resource_filter"]["property"] ) resource_filter_regex = Regexp::compile( $conf["resource_filter"]["regex"] ) else resource_filter_property_index = $resource_properties_fields.index( "network_address" ) resource_filter_regex = Regexp::compile( "(.*)" ) end q = "SELECT * FROM resources" res = dbh.execute(q) $filtered_resources = {} res.each do |r| if ( r[resource_filter_property_index] =~ resource_filter_regex ) $filtered_resources[r[0]] = r.clone end end res.finish return $filtered_resources end # get all jobs in a range of dates in future from predictions_visu tables # (= results from the previous scheduling pass) # args : dbh, start range, end range def get_jobs_gantt_scheduled(dbh,date_begin,date_end) q = "SELECT jobs.job_id,jobs.job_type,jobs.state,jobs.job_user,jobs.command,jobs.queue_name,moldable_job_descriptions.moldable_walltime,jobs.properties,jobs.launching_directory,jobs.submission_time,gantt_jobs_predictions_visu.start_time,(gantt_jobs_predictions_visu.start_time + moldable_job_descriptions.moldable_walltime),gantt_jobs_resources_visu.resource_id FROM jobs, moldable_job_descriptions, gantt_jobs_resources_visu, gantt_jobs_predictions_visu, resources WHERE gantt_jobs_predictions_visu.moldable_job_id = gantt_jobs_resources_visu.moldable_job_id AND gantt_jobs_predictions_visu.moldable_job_id = moldable_job_descriptions.moldable_id AND jobs.job_id = moldable_job_descriptions.moldable_job_id AND gantt_jobs_predictions_visu.start_time < #{date_end} AND resources.resource_id = gantt_jobs_resources_visu.resource_id AND gantt_jobs_predictions_visu.start_time + moldable_job_descriptions.moldable_walltime >= #{date_begin} AND jobs.job_id NOT IN ( SELECT job_id FROM job_types WHERE type = 'besteffort' AND types_index = 'CURRENT' ) ORDER BY jobs.job_id" res = dbh.execute(q) results = {} res.each do |r| if ( $filtered_resources[r[12]] ) # skip jobs scheduled on ignored resources only if (results[r[0]] == nil) results[r[0]] = { 'job_type' => r[1], 'state' => r[2], 'user' => r[3], 'command' => r[4], 'queue_name' => r[5], 'walltime' => r[6], 'properties' => r[7], 'launching_directory' => r[8], 'submission_time' => r[9], 'start_time' => r[10], 'stop_time' => r[11], 'resources' => [ r[12] ] } else results[r[0]]['resources'].push(r[12]) #add resources at already existing job end end end res.finish return results end # get all jobs in a range of date (from past to now) # args : dbh, start range, end range def get_jobs_range_dates(dbh,date_begin,date_end) q = "SELECT jobs.job_id,jobs.job_type,jobs.state,jobs.job_user,jobs.command,jobs.queue_name,moldable_job_descriptions.moldable_walltime,jobs.properties,jobs.launching_directory,jobs.submission_time,jobs.start_time,jobs.stop_time,assigned_resources.resource_id,(jobs.start_time + moldable_job_descriptions.moldable_walltime) FROM jobs, assigned_resources, moldable_job_descriptions, resources WHERE ( jobs.stop_time >= #{date_begin} OR ( jobs.stop_time = '0' AND (jobs.state = 'Running' OR jobs.state = 'Suspended' OR jobs.state = 'Resuming') ) ) AND jobs.start_time < #{date_end} AND jobs.assigned_moldable_job = assigned_resources.moldable_job_id AND moldable_job_descriptions.moldable_job_id = jobs.job_id AND resources.resource_id = assigned_resources.resource_id ORDER BY jobs.job_id" res = dbh.execute(q) results = {} res.each do |r| if ( $filtered_resources[r[12]] ) # skip jobs having run on ignored resources only if (results[r[0]] == nil) results[r[0]] = { 'job_type' => r[1], 'state' => r[2], 'user' => r[3], 'command' => r[4], 'queue_name' => r[5], 'walltime' => r[6], 'properties' => r[7], 'launching_directory' => r[8], 'submission_time' => r[9], 'start_time' => r[10], 'stop_time' => r[11], 'resources' => [ r[12] ], 'limit_stop_time' => r[13] } else results[r[0]]['resources'].push(r[12]) #add resources at already existing job end end end res.finish return results end # Return date of the gantt for visu def get_gantt_visu_date(dbh) q= "SELECT start_time FROM gantt_jobs_predictions_visu WHERE moldable_job_id = 0" res = dbh.execute(q) r = res.fetch_array return r[0] end #get the range when nodes are dead between two dates # arg : dbh, start date, end date def get_resource_dead_range_date(dbh,date_begin,date_end) q = "SELECT resource_id, date_start, date_stop, value FROM resource_logs WHERE attribute = 'state' AND ( value = 'Absent' OR value = 'Dead' OR value = 'Suspected' ) AND date_start <= #{date_end} AND ( date_stop = 0 OR date_stop >= #{date_begin} )" res = dbh.execute(q) results = {} res.each do |r| if ( $filtered_resources[r[0]] ) # skip ignored resources interval_stopDate = r[2] if (interval_stopDate == nil) interval_stopDate = date_end; end results[r[0]]=[] if (results[r[0]]==nil) results[r[0]].push([r[1],interval_stopDate,r[3]]) end end res.finish return results end # get_job_types # return a hash table with all types for the given job ID def get_job_types(dbh,job_id) q = "SELECT type FROM job_types WHERE job_id = #{job_id}" result = [] res = dbh.execute(q) res.each do |r| result << r[0] end res.finish return result end # # Methods adapted from oarstat and iolib # def get_history(dbh,date_start,date_stop) resources = build_resources_list(dbh) job_gantt = get_jobs_gantt_scheduled(dbh,date_start,date_stop) #print finished or running jobs jobs = job_gantt if $conf['cosystem'] jobs.each do |job_id,job| job['cosystem'] = true if get_job_types(dbh,job_id).include?("cosystem") end end jobs_history = get_jobs_range_dates(dbh,date_start,date_stop) jobs_history.each do |job_id,job| a_type = get_job_types(dbh,job_id) jobs_history[job_id]['cosystem'] = true if a_type.include?('cosystem') if (job_gantt[job_id] == nil) || (a_type.include?("besteffort")) if (jobs_history[job_id]['state'] == "Running") || (jobs_history[job_id]['state'] == "toLaunch") || (jobs_history[job_id]['state'] == "Suspended") || (jobs_history[job_id]['state'] == "Resuming") || (jobs_history[job_id]['state'] == "Launching") if a_type.include?("besteffort") jobs_history[job_id]['stop_time'] = get_gantt_visu_date(dbh); else #This job must be already printed by gantt next end end jobs[job_id] = jobs_history[job_id]; end end #print Down or Suspected resources dead_resources = get_resource_dead_range_date(dbh,date_start,date_stop) #p dead_resources return resources,jobs,dead_resources end # get_date # returns the current time in the format used by the sql database # parameters : database # return value : date string # side effects : / def get_date(dbh) db_type = $conf['DB_TYPE'] if (db_type == "Pg") q = "select EXTRACT(EPOCH FROM current_timestamp)"; else q = "SELECT UNIX_TIMESTAMP()"; end res = dbh.execute(q) r = res.fetch_array return r[0] end def draw_string(img,x,y,label) img.string(GD::Font::SmallFont, x - (7 * label.length) / 2, y, label, $gridcolor) end def draw_resource_hierarchy(img) deltay = ($sizey-(2*$offsetgridy))/$sorted_resources.length.to_f x0 = $x_per_prop * $prop_hierarchy.length + 2 x1 = -x0 + $left_offsetgridx x2 = x1 + $x_per_prop * $prop_hierarchy.length y = $sizey - $offsetgridy img.filledRectangle(x1,$offsetgridy, x2, y, $orange) img.line(x1, y, x2, y, $gridcolor) (0..$prop_hierarchy.length ).each do |i| x = -x0 + $left_offsetgridx + $x_per_prop * i img.line(x, $offsetgridy, x, $sizey - $offsetgridy, $gridcolor) end $prop_hierarchy.each_with_index do |prop,p_index| prev_r_index = -1 $sorted_resources.each_with_index do |resource, r_index| label = $resources[resource][$resource_properties_fields.index(prop)] if (r_index+1 < $sorted_resources.length) next_label = $resources[$sorted_resources[r_index+1]][$resource_properties_fields.index(prop)] else next_label = nil end if (label != next_label) x1 = -x0 + $left_offsetgridx + $x_per_prop * p_index y1 = $offsetgridy + deltay * (prev_r_index +1 ) x2 = x1 + $x_per_prop y2 = $offsetgridy + deltay * (r_index + 1) if p_index == 0 img.line(x1, y1, $sizex - $right_offsetgridx , y1, $blue); else img.line(x1, y1, x2, y1, $gridcolor); # img.filledRectangle(x1, y1, x2, y2, $color_gray[( ( (3 * r_index) % 15) + 16 * p_index) % 31 ]) end str_resource = "" 0.upto(p_index-1) do |i| plabel = $prop_hierarchy[i] plabel = 'host' if plabel == 'network_address' str_resource << plabel + "/" end plabel = prop plabel = 'host' if plabel == 'network_address' str_resource << plabel + ':
' 0.upto(p_index-1) {|i| str_resource << ($resources[resource][$resource_properties_fields.index($prop_hierarchy[i])]).to_s+"/" } str_resource << label.to_s $map_prop_hierarchy << [x1, y1, x2, y2, str_resource] prev_r_index = r_index end end end end def draw_grid(img,resource_labels,origin,origin_label,range) if (range == 'month') (0..31).each do |i| day = Time.at(origin).strftime("%e") origin = origin + RANGE_SEC['1 day'] x = $left_offsetgridx + i * (($sizex - $sum_offsetgridx) / 31.0) draw_string(img, x ,$offsetgridy / 2, day) img.line(x,$offsetgridy ,x, $sizey - $offsetgridy , $gridcolor) end elsif (range == 'week') (0..14).each do |i| x = $left_offsetgridx + i * (($sizex - $sum_offsetgridx) / 14.0) if ((i & 1) == 1) draw_string(img, x ,$offsetgridy / 2, "12h") else draw_string(img, x ,$offsetgridy / 2, origin_label) origin_label = DAYS[ (DAYS.index(origin_label) + 1) % 7] end img.line(x,$offsetgridy ,x, $sizey - $offsetgridy , $gridcolor) end elsif (range == '3 days') (0..6).each do |i| x = $left_offsetgridx + i * (($sizex - $sum_offsetgridx) / 6.0) if ((i & 1) == 1) draw_string(img,x ,$offsetgridy / 2, "12h") else draw_string(img, x ,$offsetgridy / 2, origin_label) origin_label = DAYS[ (DAYS.index(origin_label) + 1) % 7] end img.line(x,$offsetgridy ,x, $sizey - $offsetgridy , $gridcolor) end elsif (range == '1 day') (0..24).each do |i| x = $left_offsetgridx + i * (($sizex - $sum_offsetgridx) / 24.0) hour = (i + origin_label.to_i) % 24 draw_string(img, x ,$offsetgridy / 2, "#{hour.to_s}h") img.line(x, $offsetgridy, x, $sizey - $offsetgridy, $gridcolor) end elsif (range == '1/2 day') (0..12).each do |i| hour = (i + origin_label.to_i) % 24 x = $left_offsetgridx + i * (($sizex - $sum_offsetgridx) / 12.0) draw_string(img, x ,$offsetgridy / 2, "#{hour.to_s}h") img.line(x, $offsetgridy, x, $sizey - $offsetgridy, $gridcolor) end elsif (range == '1/6 day') (0..8).each do |i| hour = (i/2 + origin_label.to_i) % 24 if ((i % 2) == 1) min = "30" else min = "00" end x = $left_offsetgridx + i * (($sizex - $sum_offsetgridx) / 8.0) draw_string(img, x, $offsetgridy / 2, "#{hour}:#{min}") img.line(x, $offsetgridy, x, $sizey - $offsetgridy, $gridcolor); end else puts "Range doesn't exit" exit 1 end deltay = ($sizey-(2*$offsetgridy))/resource_labels.length.to_f i=0 resource_labels.each do |label| if ((i % $conf['tics_node'].to_i)==0) y = $offsetgridy + i * deltay draw_string(img, $left_offsetgridx / 2, y , label) img.line($left_offsetgridx - 1 , y, $sizex - $right_offsetgridx, y, $gridcolor) end i = i + 1 end img.line($left_offsetgridx - 1, $sizey - $offsetgridy, $sizex - $right_offsetgridx, $sizey - $offsetgridy, $gridcolor) end def draw_nowline(img,origin,range,color) now_x = $left_offsetgridx + (Time.now.to_i-origin) * (($sizex - $sum_offsetgridx) / RANGE_SEC[range].to_f) if ((now_x > $left_offsetgridx ) && (now_x < ($sizex - $right_offsetgridx - 1))) img.line(now_x,(3*$offsetgridy/4) ,now_x, $sizey - (3*$offsetgridy)/4 , color); img.line(now_x+1,(3*$offsetgridy)/4 ,now_x+1, $sizey - (3*$offsetgridy)/4 , color); end return end def build_image(origin, year, month, wday, day, hour, range, file_img, file_map) dbh = $dbh #resources, jobs, dead_resources = get_history(dbh,1155041223,1155047311) $resources, jobs, dead_resources = get_history(dbh,origin,origin+RANGE_SEC[range]) dbh.disconnect p jobs if $verbose $sizey = $resources.length * $points_per_cpu + 2 * $offsetgridy ; img = GD::Image.new($sizex ,$sizey ) # allocate some colors $background = img.colorAllocate($conf['background']) $gridcolor = img.colorAllocate($conf['gridcolor']) if !$conf['cosystem_color'].nil? $cosystem_color = img.colorAllocate($conf['cosystem_color']) else $cosystem_color = img.colorAllocate(0xD0,0xD0,0xD0) end $white = img.colorAllocate(255,255,255) $black = img.colorAllocate(0,0,0) $red = img.colorAllocate(255,0,0) $blue = img.colorAllocate(0,0,255) $orange = img.colorAllocate(0xFF,0x99,0x33) $state_color = Hash.new($red) $state_color['Dead'] = $red $state_color['Suspected'] = img.colorAllocate(0xFF,0x7B,0x7B) $state_color['Absent'] = img.colorAllocate(0xC2,0x22,0x00) $color=[] $color[0] = $white (1..155).each {|i| $color[i] = img.colorAllocate((i%9) * 25, i ,(50 * i) % 255 )} # make the background transparent #img.transparent(white) #sort resources typed_resources = {} $resources.each do |r_id,r| typed_resources[r[1]]= Hash.new() if (typed_resources[r[1]]==nil) typed_resources[r[1]][r_id] = r end # p typed_resources # # resources sorting and labelling # resource_labels = [] # get conf values for sorting and labelling sort_label_conf = $conf["sort_label_conf"] sort_label_conf.each do |sort_label_cf| type = sort_label_cf["type"] type = "default" if type == nil first_field_property = sort_label_cf["first_field_property"] first_displaying_regex = Regexp.new(sort_label_cf["first_displaying_regex"]) first_sorting_order = sort_label_cf["first_sorting_order"] first_sorting_regex = Regexp.new(sort_label_cf["first_sorting_regex"]) separator = sort_label_cf["separator"] second_field_property = sort_label_cf["second_field_property"] second_displaying_regex = Regexp.new(sort_label_cf["second_displaying_regex"]) second_sorting_order = sort_label_cf["second_sorting_order"] second_sorting_regex = Regexp.new(sort_label_cf["second_sorting_regex"]) first_field_index = $resource_properties_fields.index(first_field_property) second_field_index = $resource_properties_fields.index(second_field_property) # group resources by first label first_label_groups={} if typed_resources[type] != nil typed_resources[type].each do |res_id,res_desc| label = res_desc[first_field_index] #puts label if first_label_groups[label] == nil first_label_groups[label] = Hash.new() key = $resources[res_id][second_field_index].to_s first_label_groups[label][key] = res_id else key = $resources[res_id][second_field_index].to_s first_label_groups[label][key] = res_id end end end #sort first label sorted_first_label = [] if (first_sorting_order == "string") sorted_first_label = first_label_groups.keys.sort_by{|label| label =~ first_sorting_regex; $1.to_s} elsif (first_sorting_order == "numerical") #numerical sorting order sorted_first_label = first_label_groups.keys.sort_by{|label| label=~ first_sorting_regex; $1.to_i} else #natural sorting order sorted_first_label = first_label_groups.keys.sort_by(&String.natural_order(first_sorting_regex)) end #p sorted_first_label #puts "sorted_label" #p sorted_label sorted_typed_resources = [] #sorting each group by second label sorted_first_label.each do |first_label| second_label_sorted = [] if (second_sorting_order == "string") second_label_sorted = first_label_groups[first_label].keys.sort_by {|label| label =~ second_sorting_regex; $1.to_s} elsif (second_sorting_order == "numerical") #numerical sorting order second_label_sorted = first_label_groups[first_label].keys.sort_by {|label| label =~ second_sorting_regex; $1.to_i} else #natural sorting order second_label_sorted = first_label_groups[first_label].keys.sort_by(&String.natural_order(second_sorting_regex)) end second_label_sorted.each do |label| sorted_typed_resources << first_label_groups[first_label][label] end end $sorted_resources = $sorted_resources + sorted_typed_resources #puts "sorted_resources" #p $sorted_resources # resources labelling sorted_typed_resources.each do |r| $resources[r][first_field_index] =~ first_displaying_regex displayed_label = $1 displayed_label = "" if ($1==nil) if (separator != nil) displayed_label = displayed_label + separator end if (second_field_index != nil) $resources[r][second_field_index].to_s =~ second_displaying_regex displayed_label = displayed_label + $1 if ($1!=nil) end resource_labels << displayed_label end end #p resource_labels deltay = ($sizey-(2*$offsetgridy))/$sorted_resources.length.to_f origin_label = "" if ( (range == '3 days') || (range == 'week')) origin_label = wday elsif ( (range == '1/2 day') || (range == '1/6 day') || (range == '1 day')) origin_label = hour else #month origin_label = day end draw_grid(img,resource_labels,origin,origin_label,range) draw_resource_hierarchy(img) scale = ($sizex - $sum_offsetgridx).to_f / (RANGE_SEC[range].to_f) ; map_info = [] jobs.each do |job_id,j| start_x = ((j['start_time'].to_i - origin).to_f * scale.to_f).to_i; start_x = 1 if (start_x < 1) stop_x = ((j['stop_time'].to_i - origin).to_f * scale.to_f).to_i; stop_x = $sizex - $sum_offsetgridx - 1 if (stop_x > ($sizex - $sum_offsetgridx - 1)) start_x = start_x + $left_offsetgridx stop_x = stop_x + $left_offsetgridx ares_index = [] j['resources'].each do |r| r_index = $sorted_resources.index(r) if !(r_index.nil?) ares_index << r_index if j['cosystem'] color = $cosystem_color else color = $color[(job_id.to_i % 154) + 1] end img.filledRectangle(start_x,$offsetgridy + deltay * r_index,stop_x,$offsetgridy + deltay * (r_index+1), color) # yop = "*#{start_x} #{$offsetgridy + deltay * r_index} #{stop_x} #{$offsetgridy + deltay * (r_index+1)}*" end end #display job_id if (ares_index.length > $nb_cont_res && (stop_x - start_x) > $sizex / $xratio) sorted_index = ares_index.sort i_low = sorted_index.first i_high = sorted_index.first sorted_index.each do |r| if (r != i_high) if (r > i_high + 1 ) if (i_high -i_low) >= $nb_cont_res draw_string(img,(stop_x+start_x)/2,$offsetgridy+deltay*(i_high+i_low+1)/2-5,job_id.to_s) if !j['cosystem'] map_info << [start_x,$offsetgridy+deltay*i_low,stop_x,$offsetgridy+deltay*(i_high+1),job_id] end i_low = r i_high = r else i_high = i_high + 1 end end end if (i_high -i_low) >= $nb_cont_res draw_string(img,(stop_x+start_x)/2,$offsetgridy+deltay*(i_high+i_low+1)/2.0-5,job_id.to_s) if !j['cosystem'] map_info << [start_x,$offsetgridy+deltay*i_low,stop_x,$offsetgridy+deltay*(i_high+1),job_id] end end end dead_map = "" dead_resources.each do |resource,dead_period| dead_period.each do |a| start_time,stop_time,value = a start_x = ((start_time.to_i - origin).to_f * scale.to_f).to_i; start_x = 1 if (start_x < 1) stop_x = ((stop_time.to_i - origin).to_f * scale.to_f).to_i; stop_x = $sizex - $sum_offsetgridx - 1 if ( (stop_x > ($sizex - $sum_offsetgridx - 1)) || (stop_x < 0) ) start_x = start_x + $left_offsetgridx stop_x = stop_x + $left_offsetgridx r_index = $sorted_resources.index(resource) if !r_index.nil? x1 = start_x x2 = stop_x y1 = $offsetgridy + deltay * r_index y2 = $offsetgridy + deltay * (r_index+1) img.filledRectangle(x1,y1,x2,y2, $state_color[value]) dead_map << 'Host: #{$resources[resource][$resource_properties_fields.index('network_address')]}" + "
State: #{value}" + '\')" >' end end end #draw_nowline draw_nowline(img,origin,range,$red) f_img = File::new("#{$conf['web_root']}/#{$conf['directory']}/#{$conf['web_cache_directory']}/#{file_img}", 'w') img.png(f_img) f_img.close f_map = File::new("#{$conf['web_root']}/#{$conf['directory']}/#{$conf['web_cache_directory']}/#{file_map}", 'w') f_map.puts '' map_info.each do |info| j = jobs[info[4]] if j['cosystem'] cosystem = "
Job Cosystem" else cosystem = '' end f_map.puts 'User: #{j['user']}" + "
Type: #{j['job_type']}" + "
State: #{j['state']}" + # "
Command: #{j['command']}" + TODO: fix quote problem "
Queue: #{j['queue_name']}" + "
Nb resources: #{j['resources'].length}" + "
Submission: #{Time.at(j['submission_time'].to_i).strftime("%a %b %e %H:%M %Y")}" + "
Start: #{Time.at(j['start_time'].to_i).strftime("%a %b %e %H:%M %Y")}" + "
End: #{Time.at(j['stop_time'].to_i).strftime("%a %b %e %H:%M %Y")}" + '\')" >' end $map_prop_hierarchy.each do |info| f_map.puts '' end f_map.puts dead_map f_map.puts '
' f_map.close return end ################################## def cgi_html(cgi) popup_hour = ["00:00","01:00","02:00","03:00","04:00","05:00","06:00","07:00","08:00","09:00","10:00","11:00","12:00","13:00","14:00","15:00","16:00","17:00","18:00","19:00","20:00","21:00","22:00","23:00"] popup_day = ["1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21","22","23","24","25","26","27","28","29","30","31"] popup_month = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct', 'Nov','Dec'] popup_year = [] popup_range = RANGE_ORDER # cgi = CGI.new("html3") # add HTML generation methods # now = Time.now now = Time.at(Float(get_date($dbh))) range = $conf['default_range'] month, day, hour, year = now.strftime("%b %e %H:00 %Y").split(" ") popup_year = [(year.to_i-1).to_s, year, (year.to_i+1).to_s ] origin = 0 if (cgi.params['day'].length>0) hour = cgi.params['hour'].to_s day = cgi.params['day'].to_s month = cgi.params['month'].to_s year = cgi.params['year'].to_s range = cgi.params['range'].to_s origin = Time.local(year,month,day,hour).to_i else range = $conf['default_range'] origin = now.to_i-RANGE_SEC[range]/2 end # # zoom +-, left,rigth, default ? # if (cgi.params['plus.x'].length>0) if (range != RANGE_ORDER.first) next_range = RANGE_ORDER[RANGE_ORDER.index(range) - 1] origin = origin + (RANGE_SEC[range] - RANGE_SEC[next_range]) / 2 range = next_range end end if (cgi.params['minus.x'].length>0) if (range != RANGE_ORDER.last) next_range = RANGE_ORDER[RANGE_ORDER.index(range) + 1] origin = origin + (RANGE_SEC[range] - RANGE_SEC[next_range]) / 2 range = next_range end end if (cgi.params['left.x'].length>0) origin = origin - RANGE_STEP[range] end if (cgi.params['right.x'].length>0) origin = origin + RANGE_STEP[range] end if (cgi.params['action'].to_s == 'Default') range = $conf['default_range'] origin = now.to_i-RANGE_SEC[range]/2 end if (range == '3 days') || (range == 'week') || (range == 'month') origin = origin - origin % 86400 - Time.at(origin).gmt_offset + 86400 else origin = origin - origin % 3600 end wday, month, day, hour, year = Time.at(origin).strftime("%a %b %e %H:00 %Y").split(" ") # #set displaying params # popup_year[popup_year.index(year)]= [year,true] popup_month[popup_month.index(month)]= [month,true] popup_day[popup_day.index(day)]= [day,true] popup_hour[popup_hour.index(hour)]= [hour,true] popup_range[popup_range.index(range)]= [range,true] # #image and map files naming # file_range = range file_range = '1_day' if (range =='1 day') file_range = '1_2_day' if (range =='1/2 day') file_range = '1_6_day' if (range == '1/6 day') file_img = $prefix+ '_gantt_' + year + '_' + month + '_' + day + '_' + hour + '_' + file_range file_map = $prefix+ '_map_' + year + '_' + month + '_' + day + '_' + hour + '_' + file_range #test if it's on old file ? if (origin + RANGE_SEC[range] > now.to_i) file_img = file_img + '_' + now.to_i.to_s + '.png'; file_map = file_map + '_' + now.to_i.to_s + '.map'; else file_img = file_img + '.png'; file_map = file_map + '.map'; end # #cache flushing according to nb_file_cache_limit # path_file = "#{$conf['web_root']}/#{$conf['directory']}/#{$conf['web_cache_directory']}/" file_list = Dir.glob("#{path_file}*.{png,map}") #puts file_list.length if (file_list.length > $conf['nb_file_cache_limit']) file_time = {} file_list.each do |file| file_time[file] = File.atime(file).to_i end file_time_sorted = file_time.sort{|a,b| a[1]<=>b[1]} #puts file_time_sorted.length/2 file_time_sorted[0..file_time_sorted.length/2].each do |f| begin File.delete(f.first) rescue $stderr.print "Can't flush file: " + $! if $verbose end end end #build image file build_image(origin, year, month, wday, day, hour, range, file_img, file_map) if !File.exist?(path_file+file_img) map = "" path_file_map = "#{$conf['web_root']}/#{$conf['directory']}/#{$conf['web_cache_directory']}/#{file_map}" File.open(path_file_map) do |file| while line = file.gets map << line end end #$stderr.print ">>>>#{cgi.params['mode'].class}" if (cgi.params['mode'].to_s=='image_map_only') f_img = file_img f_map = file_map if cgi.params['path'].to_s != "no" f_img = "/#{$conf['directory']}/#{$conf['web_cache_directory']}/#{file_img}" f_map = "/#{$conf['directory']}/#{$conf['web_cache_directory']}/#{file_map}" end cgi.out("text/plain") { "#{f_img}" + "\n" + "#{f_map}" } else cgi.out { cgi.html { cgi.head { "\n"+cgi.title{$title} } + cgi.body { "\n"+ # "##### #{$val} #####" + cgi.form("get"){ # "**#{cgi.params}**\n" + # $title + cgi.h3 { $title } + "\n"+ # Javascript stuff thanks to NCSA TITAN cluster's page CGI.escapeElement('

') + "\n" + CGI.escapeElement('') + "\n" + CGI.escapeElement(" Origin ") + cgi.popup_menu("NAME" => "year", "VALUES" => popup_year) + cgi.popup_menu("NAME" => "month", "VALUES" => popup_month) + cgi.popup_menu("NAME" => "day", "VALUES" => popup_day) + cgi.popup_menu("NAME" => "hour", "VALUES" => popup_hour) + CGI.escapeElement(" Range ") + cgi.popup_menu("NAME" => "range", "VALUES" => popup_range) + cgi.submit("Draw","action") + cgi.submit("Default","action") + cgi.image_button("/#{$conf['directory']}/#{$conf['web_icons_directory']}/gorilla-left.png", "left", "left") + cgi.image_button("/#{$conf['directory']}/#{$conf['web_icons_directory']}/gorilla-right.png", "right", "right") + cgi.image_button("/#{$conf['directory']}/#{$conf['web_icons_directory']}/gorilla-minus.png", "minus", "minus") + cgi.image_button("/#{$conf['directory']}/#{$conf['web_icons_directory']}/gorilla-plus.png", "plus", "plus") + cgi.br + "\n" + CGI.escapeElement(map) + "\n" + CGI.escapeElement('
') + # cgi.img("/#{$conf['directory']}/#{$conf['web_cache_directory']}/yop.png", "gantt image","" ) + cgi.img("SRC" => "/#{$conf['directory']}/#{$conf['web_cache_directory']}/#{file_img}", "ALT" => "gantt image", "USEMAP" => "#" + $prefix +"_ganttmap" ) + CGI.escapeElement('
'); } } } } end end ################################################################################################################# ### main ################################################################################################################# cgi = CGI.new("html3") # add HTML generation methods configfile = '%%OARCONFDIR%%/drawgantt.conf' configfile = cgi.params['configfile'].to_s if (cgi.params['configfile'].length>0) $prefix= "" $prefix= cgi.params['prefix'].to_s if (cgi.params['prefix'].length>0) puts "### Reading configuration file..." if $verbose $conf = YAML::load(IO::read(configfile)) if cgi['conf'].length > 0 conf = YAML::load(cgi['conf']) #override configuration parameters with received ones conf.each do |key,value| $conf.delete(key) if $conf[key] $conf[key] = value end end $title = $conf['title'] || 'Gantt Chart' $sizex = $conf['sizex'] #$sizey = $conf['sizey'] $offsetgridy = $conf['offsetgridy'] $left_offsetgridx = $conf['left_offsetgridx'] $right_offsetgridx = $conf['right_offsetgridx'] $sum_offsetgridx = $left_offsetgridx + $right_offsetgridx $tics_node = $conf['tics_node'] $points_per_cpu = $conf['points_per_cpu'] $xratio = $conf['xratio'] $nb_cont_res = $conf['nb_cont_res'] $prop_hierarchy = $conf['prop_hierarchy'] $x_per_prop = 10 $map_prop_hierarchy = [] $sorted_resources = [] $dbh = base_connect cgi_html(cgi) #p list_resource_properties_fields(base_connect) #p list_resources(base_connect) ./oar-2.5.2/sources/visualization_interfaces/DrawGantt/Icons/0000755000175000017500000000000011757171206022316 5ustar plbplb./oar-2.5.2/sources/visualization_interfaces/DrawGantt/Icons/gorilla-right.png0000644000175000017500000000356211757171206025576 0ustar plbplbPNG  IHDR szzgAMA7tEXtSoftwareAdobe ImageReadyqe<IDATxbd أc@j| qP5 @ lʊ0`oxOpb#đed1ce1eada3~p2l< @%@|j"8r}- 7 _`?i[1l9fG.[@,S`b ero~oCNP>'×@XGV\ , L@@ÿ?? r3cȝ+ @hX oa`Y tg:'lad`d788T_ &/4x%BbFK 8?X?f`'(E0}O,g^u &PAF&&;3BX`baCxD1h`h_ *F0fDbC&<#] @LH0o3dz10¤ Y!i(``cE8*lV@Gh@ň"Y`* +9OP*a`ZΈ`e`>(BA( L#;zPFK؀3 gZpB-9?  gV\ wl>djƇ8Z OpA~~bxtr"H4 bd>;`* ^h)ïSVÊZ GAz&P/42֎o^3/WQ\p `T1^j5 Rٿ!?.5~ h$˳> 4#n<;l'@~+ X l #@5X%l J`Q8,6aps_GXaCeܞj9(!/x &-8HfHu<"*> ,@1H0 :(?~GN*ÏpXwcz\zh=p+$6IP ݠ -T!qrğ?=v=0@lʠ$ĀZ"; Jp41dQl01[`!1_g }mf94zGv5(@Y@:&Ơ #';#ro%ܭ)]3"0Q]3)@1t t+ bIENDB`./oar-2.5.2/sources/visualization_interfaces/DrawGantt/Icons/gorilla-plus.png0000644000175000017500000000343511757171206025443 0ustar plbplbPNG  IHDR szzgAMA7tEXtSoftwareAdobe ImageReadyqe<IDATxbd أc@j| qP5 @ lʊ0`oxOpb#đed1ce1eada3~p2l< @%@|j"8r}- 7 _`?i[1l9fG.[@,S`b ero~oCNP>'×@XGV\ , L@@ÿ?? r3cȝ+ @hX oa`Y tg:'l]‹Gu`0i#nF#&  ^  R{(O2r210-d*X! t3? d/e`3|\ RB c:q @De( #Ph! @0Bu`(0A0&821#3"!RAG. &BF2 '<fP@,VH3BZ= ^Xa@0) +B10e;V ?ԐPa60D! `6F6 @l/L^gC> MU \hCJ8 ׌>00, % '\M9Fh` X\H 3BJ7`0L7Z0/AXl?"L9Wl. hXBB on2<<9(8P{ ia&p6YNL ,2 A /3 *&A#F EW7tC<w(@,%D|faaw?Hh?H) t?Pg n<=##4J_,`4:_bxuk9  %T!^FxVz:e#@B`zŁ4HZ+B*? 5$}10₃ ?pZ T.`6| ߂,TψvrH @0_}k5P #@5h9G9! Ppȅp m2'×@XGV\ , L@@ÿ?? r3cȝ+ @hX oa`Y tg:'lad`d788T_ &/4x%BbFK 8?X?f`'(?@tb9# %p?.M 0DTr00201} t  -L, L@׀( L :(v@1!2 ͐'<fX h,3Ea.FaWVX|i /EP LYg , p "C 5FkC@`;paa KalJ` ~7`H<~=JP``SUx & a0Wg&,Fſ_ PaJD '4 X`q!-1zA  Kz2a`&uv`FNB on2<<9(8P{ ia&p6YNL ,2,G@/3-cʒ a`F8u>vC @*`)$3 :Rlh?@px{FFhl303k24- / -Wv 9S@Z2Jeg%pv\&1"Y+j#߰VTHA!鋁G X.U@ 'nkj0r#*z R=#@9cva€j08d!#~|>G820\ wN9`v/ãP,01"W l'@jÇТQ#VU+#< ?$Q"4>0g/1: F}AGȩ/!o u'(: d @k @mPC?А? t? #7Ra$־m JL L +<+BlP 1|ȍS&@mr#~, fjL1e5ӡ!'1o 6A-` l C?|-` uL@Y%& l2 HʽpsFc@dw͈Duh;8sC}`NIENDB`./oar-2.5.2/sources/visualization_interfaces/DrawGantt/Icons/gorilla-left.png0000644000175000017500000000360211757171206025406 0ustar plbplbPNG  IHDR szzgAMA7tEXtSoftwareAdobe ImageReadyqe<IDATxbd أc@j| qP5 @ lʊ0`oxOpb#đed1ce1eada3~p2l< @%@|j"8r}- 7 _`?i[1l9fG.[@,S`b ero~oCNP>'×@XGV\ , L@@ÿ?? r3cȝ+ @hX oa`Y tg:'lad`d788T_ &/4x%BbFK 8?X?f`'@mb 4tb9# %p?.M X0DTr00201㛉h! @0/\ T &&{@ldbБgd?̮ @1!2 ͐ ^80+$ C? rxrL@i>$!-E+P>e4a"`;0TtCxd/ 7CA!hm`Cl'@.lN 0 @0_} Vj} vKxr.`; b ^C30|y}D? !t# 'W l'@1!hA2{GAӫ w&3G_?A,= B [NC~p $q`{@Q#bP3׷ ne[_[O$,l [D8Xu%1HeH^h)#'6#c1,&m JL L +2/PgKhcr&@mr#~, fjL1e?XH@| rן6|߅x[DYhk`kf0| Jpo?3l (D!!Ɏ` wp>Gj;&Dv׌LT s @=0}B7IENDB`./oar-2.5.2/sources/visualization_interfaces/DrawGantt/Icons/exchange-32.png0000644000175000017500000000332611757171206025034 0ustar plbplbPNG  IHDR!iIDATx[l\W}.3>3n'q:N%rHTU 7/ *(BHH\$BD!-!U<@@6I5dlggecǨyAbIKsf4{Z{&>c#[xD%*#1'VbpcƝ; Sftqe:S@z-:QjT❳s=}w892//rOɎ1sLOSi))ReV(ED nЃzs*5~vIwg`xfɣZJ{7ݾ)&_7'D1)i9^9,R5O 6(u]r|_t;7ke-}scSHp@G"A(wJGtq3ޟ#\r,!v` {tDŽ5}L]Iɹ^Ia_X$1pW!GV?n;kߚ)G } ?7Kk0X(<,`wG1 \-- v^-šıN^pC=<F 1n nˋ\=)T4l+=ZjmA#@SIeSm.K4@`-P`ߛuf~}Jt$ 5LXD xS w]'||4[tJyjO!K:*_W"Qfy?;L)@耇SWʂ:u7]+YE7z- US꼵.8 1q.:ܕߪ-Qy-VMU Zn'Ej$r1 gU:r?4btshC{Lnmۍݴ;O~ӗmp./Y;2Q01c'/~Bt1-Z%_:V֫Nh<|‰ޝ1eH!q(l|w /ynF L#&t*BU1o4)M[Eֈ5g|@7 ҧxZht8˪?=w!D*Fk̛p6w6'7* M5ºaÆo޼yxNٳ;[b8~7.vyP6m-(ƍximm=$I aPTv-,Pii0PX:;_|mڹK &WB2 r:@{yq*(\ y%`C#[t*Aby293.bȫBƋS3r{_ I`-|7wݵW]+/x0h{} 9? ɉG g\ۆC"ȩL} qwM:ZVvOEH4j w" *@ބxDR2.0 u~@eY@C"ry&@AA5;u0de(f*Χ:\ q?@54 ;(x<`K+2 !Ex~hxХ#A, &xMQۅwm۶HQ"iO`d R=!DɮmmB?M "i\ק~SOOuCCf}c񆆆zzG錕U IR, Y6Voo]gﴷ&RnM==QUKc{6iaϲtR*G< +C⨚6U~… .!%Z ٿA!ɡ 㤖 LBSJ!acڱd\ n@= HV Zi宴fM޽{OIK6 ^nhy&D4~UU!o]ݯߞ:;xH(WF rB 0l7#{! rzGtlU Y<_%0rȯҩٌFqb &%Id"1 E3@qDn(z2z&ͨ ̊>p%" #gçP_2,4K⩇\ F]@IENDB`./oar-2.5.2/sources/extra/0000755000175000017500000000000011757171206013367 5ustar plbplb./oar-2.5.2/sources/extra/orpheus/0000755000175000017500000000000011757171206015054 5ustar plbplb./oar-2.5.2/sources/extra/orpheus/modules/0000755000175000017500000000000011757171206016524 5ustar plbplb./oar-2.5.2/sources/extra/orpheus/modules/test.rst0000644000175000017500000000276011757171206020242 0ustar plbplb sudo -u oar oarnodesetting -h node2 -s "Alive" sudo -u oar oarnodesetting -h node3 -s "Alive" oarsub plop oarsub "plop {exec_time=20}" oarsub "plop {exec_time=10,io=1,io_workload=1}" OK oarsub "plop {exec_time=10,io=1,io_workload=10}" OK oarsub "plop {exec_time=10,io=1,io_workload=20}" OK test: multiple oarsub "plop {exec_time=10,io=1,io_workload=5}" & oarsub "plop {exec_time=10,io=1,io_workload=5}" & 10sec oarsub "plop {exec_time=10,io=1,io_workload=10}" & oarsub "plop {exec_time=10,io=1,io_workload=10}" & 20sec oarsub "plop {exec_time=10,io=1,io_workload=10}" & oarsub "plop {exec_time=10,io=1,io_workload=10}" & oarsub "plop {exec_time=10}" & 20-20-10 oarsub "plop {exec_time=30,io=1,io_workload=10}" & sleep 5; oarsub "plop {exec_time=10,io=1,io_workload=10}" 40-20 -> OK -- test if old already terminated io_job is not counted again oarsub "plop {exec_time=20,io=1,io_workload=10}"; sleep 5; oarsub "plop {exec_time=5,io=1,io_workload=10}"; sleep 25; oarsub "plop {exec_time=10,io=1,io_workload=10}"; ========================================================================= nb_jobs launched: 3 terminated: 3 running: 0 nb_recv_signal/nb_get_jobs 3 3 io_workload: 0 ========================================================================= create iofs resource for ((i=0;i<10;i++)) ; do echo "i vaut $i" oarnodesetting -a -h "" -p type=io -p ops=$i done oarsub -l "{type = 'default'}/resource_id=1+{type = 'io'}/resource_id=1, walltime=???" "plop {exec_time=20,io=1,io_workload=10}"; ./oar-2.5.2/sources/extra/orpheus/modules/orpheus.rst0000644000175000017500000000626711757171206020756 0ustar plbplb======= Orpheus ======= Orpheus is a simple jobs executor simulator. It replaces the use of runner, oarexec, bipbip and real or sleeping execution jobs. Its purposes is to experiment and benchmark oar's frontend, scheduling modules and some part of resource management. Principle: ---------- A daemon named Orpheus is launched before the firsts job submission. This daemon will simulate jobs' launching and jobs' termination. Runner module is replaced be a symbolic link to /tmp/orpheus_signal_sender (created by orpheus daemon at its launching). When Metascheduler launchs runner, the orpheus_signal_sender is executed and send a signal to orpheus which will retrieve jobs to launch from the database. Jobs' execution times are fixed with command argument (field command in table jobs). The argument's format follow the Lua's table one. At second accuracy Orpheus tests if jobs have terminated, if this is the case it sets these jobs to terminated state in database and sends a "Scheduling" command in Almighty's TCP socket. Orpheus provides some basic IO contention simple. Up to now only one IO model is provided. It is qualified of linear model where central IO capacity is share among competing jobs wich have IO requirements during all their execution. At contention, jobs face to slowdown equal to capacity divide amount of jobs' IO requirement. This factor is update at each start or end of job with IO requirements. Limitations: ------------ * no support of Interactive job (no way) * besteffort and kill/delete job (in todo) * does not read oar.conf for db parameters and almighty port (aloso todo) * not validated/extensively tested Installation: ------------- Note: execute following commands as oar user * cd /usr/lib/oar/ * mv runner orig.runner #backup the orignal runner script * sudo -u oar touch /tmp/orpheus_signal_sender * ln -s /tmp/orpheus_signal_sender runner * in oar.conf you must stop periodic node checking by setting FINAUD_FREQUENCY="0" * orpheus.lua and oar.lua must be located in the same directory (oar.lua is in /libs) * install lua5.1 liblua5.1-socket2 * as root: * cd lua-signal * make && make install * Can be compiled with llvm-lua Running and usage: ------------------- Launch the orpheus daemon. It's needed before first submition either some resources will be suspected. * sudo -u oar lua orpheus.lua Submit a fake script (yop) with default resource requirement (depending of actual oar configuration oar.conf or/and admission rules). The fake yop script does not exist. * oarsub yop Submit a fake script with its execution time specified in second and one node required * oarsub -l nodes=1 "yop {exec_time=100}" Submit a fake script with execution time and io settings, io=1 to indicate the job is an io one and io workload paramter according to io model. * oarsub "yop {exec_time=10,io=1,io_workload=20}" Todo: ----- * Support Killing job (for best effort and enerfy saving) * More test * support Hulot (energy saving)? * job and node faults * install/uninstall(active/unactive?) script * kameleon step * simple I/O simulation (need more test) Comments, bugs, request: ------------------------ * send mail to: oar-devel@lists.gforge.inria.fr ./oar-2.5.2/sources/extra/orpheus/modules/orpheus.lua0000644000175000017500000002207311757171206020720 0ustar plbplb-- --[[ SIGUSR1 signal triggers the get to Features no supported - Interactive Job - Killing job - Best effort (side effect of killing job capacity) - Resource / Job Fault TODO: * Pour le kill: le plus simple proposer une alternative de signal_oarexec($$$$$$$) dans OAR::Tools.pm et modifier leon.pl pour l'appeler (masquable dynamiquement ???) * doc command options in orpheus.rst * walltime (optional) only command ! * install/uninstall script * arg (walltime) * Readme * Kill job * tests (à la oar_api rspec) ??? * kameleon step I/O contention/perturbation simulation Pseudo algorithm: at each job starting or terminating events do compute new global I/O contention compute new remaing work and terminating time for each I/O jobs end ]]-- require "oar" require "socket" require "signal" -- some globals chronos = {} -- array to keep end of execution time of jobs io_jobs = {} -- tables of job with io activities terminated_io_jobs = {} -- to keep trace of already terminated io_jobs io_model = "linear" -- NOT USED YET, in the eventually of mutiple io models io_capacity = 10 -- TODO: option argument ??? io_workload = 0 -- current global io workload next_io_workload = 0 -- to store the next io_worload value due to new or terminated io jobs -- (simple model constant io workload per job) oar.conf_load() con = oar.connect() default_walltime = 10 tmp_job = {} -- store temparaly job's parameter/desciption from oar's db t0 = os.time() n_last = 1 -- os.time - os.time() + 1 (for chronos indice) nb_launched_jobs = 0 nb_terminated_jobs = 0 nb_received_signals =0 nb_get_jobs = 0 if not oar.conf["SERVER_HOSTNAME"] then almighty_host = 'localhost' else almighty_host = oar.conf["SERVER_HOSTNAME"] end if not oar.conf["SERVER_PORT"] then almighty_port = 6666 else almighty_port = oar.conf["SERVER_PORT"] end -- signal handler function use to notify orpheus that there are jobs to run signal.signal("SIGUSR1", function(n, i) print("signal handler", n, i) nb_received_signals = nb_received_signals +1 end); -- get pid of current orpheus process function getpid() local fproc = io.open("/proc/self/stat","r") return oar.wssplit(fproc:read("*l")) end -- create the orpheus signal sender function create_signal_sender() local pid = getpid() os.execute("rm -f /tmp/orpheus_signal_sender; echo 'kill -s USR1 " .. pid .."' > /tmp/orpheus_signal_sender; chmod 755 /tmp/orpheus_signal_sender;") end -- get job to launch + set running + insert in chronos function get_jobs() local job_ids = "" local execution_time = default_walltime local delta_io_workload = 0 -- io_worload evolution due to new or terminated jobs local new_io_jobs = false local ts = os.time() - t0 -- orpheus start_time for this get jobs cycle -- be carefull of SQL processing time it'll include in global time job execution for row in oar.rows("SELECT job_id, command from jobs WHERE state='toLaunch'") do local job = {} job_id = row[1] job_ids = job_ids .. job_id .. ',' -- determine the execution time if row[2] then -- options is set as valid lua table in place cli argument testio {exec_time=1234,io=1,io_workload=1, io_sensivity=1 ...} local f,l,opts = string.find(row[2], ".%S+%s(.*)") if opts then print("OPTS: "..opts) assert(loadstring("tmp_job = " .. opts))() --assert(loadstring("tmp_job =".."{exec_time=5}"))() job = oar.deepcopy(tmp_job) if job.exec_time then execution_time = job.exec_time end end end -- this job is one with I/O requirements if job.io then new_io_jobs = true -- ad to I/O list job io_jobs[job_id] = job io_jobs[job_id].t_prev = ts -- save time where work and io perturbation have been updated io_jobs[job_id].work = 0 -- work which had been executed (normalized to 1 unit per second) -- add job's I/O workload contribution delta_io_workload = delta_io_workload + job.io_workload else local i = ts + execution_time if not chronos[i] then chronos[i] = {} end -- initialize slot end_time if needed -- job id insert in slot end_time chronos[i][#chronos[i]+1] = job_id end nb_launched_jobs = nb_launched_jobs + 1 end job_ids = string.sub(job_ids, 1, -2) --chomp the last ',' if new_io_jobs then print("new_io_jobs") -- update next io_workload next_io_workload = io_workload + delta_io_workload update_io_jobs_execution_time() end -- set job running -- p = "UPDATE jobs SET state='Running' WHERE job_id IN "..jobids -- print(p)i oar.sql("UPDATE jobs SET state='Running' WHERE job_id IN ("..job_ids..')') end function update_io_jobs_execution_time() tc = os.time() - t0 print("update_io_job") print("io_workload: ".. io_workload) print("next_io_workload: ".. next_io_workload) for j_id,job in pairs(io_jobs) do print("io_jobid: "..j_id) -- for linear model local executed_work = (tc - job.t_prev) job.t_prev = tc -- for the next compute of executed work step -- proportional slowdown if io_workload had exceeded IO system's capacity if io_workload > io_capacity then executed_work = executed_work * (io_capacity / io_workload) end job.work = job.work + executed_work -- update executed work -- compute new ending time local new_remaning_time = job.exec_time - job.work if next_io_workload > io_capacity then new_remaning_time = (job.exec_time - job.work) * (next_io_workload / io_capacity) end print("remainng time:".. new_remaning_time) if new_remaning_time > 0 then -- new insert in chronos old one will be ignored at expiration time local i = new_remaning_time + tc if not chronos[i] then chronos[i] = {} end -- initialize slot end_time if needed chronos[i][#chronos[i]+1] = j_id job.ending_time = i -- used to test the job's ending else -- TODO Do we have a trouble ? print("warning (new_remaning_time =< 0) something dirty is happen with iojob: "..j_id) end end io_workload = next_io_workload end function terminated_jobs() local delta_io_workload = 0 -- io_worload evolution due to new or terminated jobs local n1 = os.time() - t0 local terminated_jobs = 0 local terminated_job_ids = "" for i=n_last,n1 do local io_jobs_terminated = false if chronos[i] then for k,j_id in ipairs(chronos[i]) do if io_jobs[j_id] then local job = io_jobs[j_id] -- io job terminated if (i == job.ending_time) then io_jobs_terminated = true -- remove job's io_workload delta_io_workload = delta_io_workload - job.io_workload io_jobs[j_id] = nil -- remove io_job from the list print("terminated io_job_id: ",j_id,"i:",i) terminated_job_ids = terminated_job_ids .. j_id .. ',' terminated_jobs = terminated_jobs + 1 terminated_io_jobs[j_id] = true end else if not terminated_io_jobs[j_id] then print("terminated job_id: ",j_id,"i:",i) terminated_job_ids = terminated_job_ids .. j_id .. ',' terminated_jobs = terminated_jobs + 1 end end end chronos[i] = nil --bye bye job_ids end if io_jobs_terminated then -- update next io_workload next_io_workload = io_workload + delta_io_workload print ("iow,n_iow,d_iow", io_workload, next_io_workload, delta_io_workload) update_io_jobs_execution_time() end end n_last = n1 if terminated_jobs ~= 0 then nb_terminated_jobs = nb_terminated_jobs + terminated_jobs terminated_job_ids = string.sub(terminated_job_ids, 1, -2) --chomp the last ',' -- update jobs oar db table and set stop time ?? oar.sql("UPDATE jobs SET state='Terminated',stop_time='"..t0+n1.."' WHERE job_id IN ("..terminated_job_ids..")") -- send Scheduling command to almighty notify_almighty() end end -- notify jobs' termination function notify_almighty() local client = socket.connect(almighty_host,almighty_port) client:send("Scheduling\n") client:close() end -- main loop socket.select(nil,nil,1) -- create orpheus_signal_sender (runner moduler must be a symlink to it) create_signal_sender() --oar.set_all_jobs_toLaunch() -- only for dev and debug --get_jobs() -- select k = 0 while true do socket.select(nil,nil,1) if (nb_received_signals > nb_get_jobs) then -- set_all_jobs_toLaunch() -- ONLY for dev and debug get_jobs() nb_get_jobs = nb_received_signals end terminated_jobs() if (k % 10) == 0 then print('=========================================================================') print("nb_jobs launched:", nb_launched_jobs, "terminated: ", nb_terminated_jobs , "running: ", nb_launched_jobs-nb_terminated_jobs, "\nnb_recv_signal/nb_get_jobs", nb_received_signals, nb_get_jobs, "\nio_workload: ", io_workload) print('=========================================================================') end k = k +1 end con:close() env:close() ./oar-2.5.2/sources/extra/orpheus/modules/lua-signal/0000755000175000017500000000000011757171206020560 5ustar plbplb./oar-2.5.2/sources/extra/orpheus/modules/lua-signal/test.lua0000644000175000017500000000046111757171206022243 0ustar plbplbrequire "signal" signal.signal("SIGTERM", function(n, i) print("signal handler", n, i); end); signal.raise("SIGTERM"); signal.signal("SIGTERM", "ignore"); signal.raise("SIGTERM"); print "signal ignored"; signal.signal("SIGTERM", "default"); print "signal default action"; signal.raise("SIGTERM"); ./oar-2.5.2/sources/extra/orpheus/modules/lua-signal/lsignal.c0000644000175000017500000002363311757171206022364 0ustar plbplb/* * lsignal.c -- Signal Handler Library for Lua * * Copyright (C) 2010 Patrick J. Donnelly (batrick@batbytes.com) * * This software is distributed under the same license as Lua 5.0: * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #define LUA_LIB_NAME "signal" #define LUA_LIB_VERSION 1.2 #define LUA_SIGNAL_NAME "LUA_SIGNAL" #if !(defined(_POSIX_SOURCE) || defined(sun) || defined(__sun)) #define INCLUDE_KILL 1 #define INCLUDE_PAUSE 1 #define USE_SIGACTION 1 #endif #include #include #include #include #include #include #include struct lua_signal { const char *name; /* name of the signal */ const int sig; /* the signal */ }; static const struct lua_signal lua_signals[] = { /* ANSI C signals */ #ifdef SIGABRT {"SIGABRT", SIGABRT}, #endif #ifdef SIGFPE {"SIGFPE", SIGFPE}, #endif #ifdef SIGILL {"SIGILL", SIGILL}, #endif #ifdef SIGINT {"SIGINT", SIGINT}, #endif #ifdef SIGSEGV {"SIGSEGV", SIGSEGV}, #endif #ifdef SIGTERM {"SIGTERM", SIGTERM}, #endif /* posix signals */ #ifdef SIGHUP {"SIGHUP", SIGHUP}, #endif #ifdef SIGQUIT {"SIGQUIT", SIGQUIT}, #endif #ifdef SIGTRAP {"SIGTRAP", SIGTRAP}, #endif #ifdef SIGKILL {"SIGKILL", SIGKILL}, #endif #ifdef SIGUSR1 {"SIGUSR1", SIGUSR1}, #endif #ifdef SIGUSR2 {"SIGUSR2", SIGUSR2}, #endif #ifdef SIGPIPE {"SIGPIPE", SIGPIPE}, #endif #ifdef SIGALRM {"SIGALRM", SIGALRM}, #endif #ifdef SIGCHLD {"SIGCHLD", SIGCHLD}, #endif #ifdef SIGCONT {"SIGCONT", SIGCONT}, #endif #ifdef SIGSTOP {"SIGSTOP", SIGSTOP}, #endif #ifdef SIGTTIN {"SIGTTIN", SIGTTIN}, #endif #ifdef SIGTTOU {"SIGTTOU", SIGTTOU}, #endif /* some BSD signals */ #ifdef SIGIOT {"SIGIOT", SIGIOT}, #endif #ifdef SIGBUS {"SIGBUS", SIGBUS}, #endif #ifdef SIGCLD {"SIGCLD", SIGCLD}, #endif #ifdef SIGURG {"SIGURG", SIGURG}, #endif #ifdef SIGXCPU {"SIGXCPU", SIGXCPU}, #endif #ifdef SIGXFSZ {"SIGXFSZ", SIGXFSZ}, #endif #ifdef SIGVTALRM {"SIGVTALRM", SIGVTALRM}, #endif #ifdef SIGPROF {"SIGPROF", SIGPROF}, #endif #ifdef SIGWINCH {"SIGWINCH", SIGWINCH}, #endif #ifdef SIGPOLL {"SIGPOLL", SIGPOLL}, #endif #ifdef SIGIO {"SIGIO", SIGIO}, #endif /* add odd signals */ #ifdef SIGSTKFLT {"SIGSTKFLT", SIGSTKFLT}, /* stack fault */ #endif #ifdef SIGSYS {"SIGSYS", SIGSYS}, #endif {NULL, 0} }; /* * The signal counts in the 1st half of the array are modified by * the handler. The corresponding signal counts in the 2nd half * are modifed by the hook routine. */ static volatile sig_atomic_t *signal_stack = NULL; static int signal_stack_top; static lua_State *ML = NULL; static struct hook { lua_Hook hook; int mask; int count; } old_hook = {NULL, 0, 0}; static void hook (lua_State *L, lua_Debug *ar) { int i, j; assert(L == ML); for (i = 0; i < signal_stack_top; i++) while (signal_stack[i] != signal_stack[i+signal_stack_top]) { lua_getfield(L, LUA_REGISTRYINDEX, LUA_SIGNAL_NAME); lua_pushinteger(L, i); lua_rawget(L, -2); lua_replace(L, -2); /* replace _R.LUA_SIGNAL_NAME */ assert(lua_isfunction(L, -1)); for (j = 0; lua_signals[j].name != NULL; j++) if (lua_signals[j].sig == i) { lua_pushstring(L, lua_signals[j].name); break; } if (lua_signals[j].name == NULL) lua_pushliteral(L, ""); lua_pushinteger(L, i); lua_call(L, 2, 0); signal_stack[i+signal_stack_top]++; } lua_sethook(ML, old_hook.hook, old_hook.mask, old_hook.count); old_hook.hook = NULL; } static void handle (int sig) { assert(ML != NULL); if (old_hook.hook == NULL) /* replace it */ { old_hook.hook = lua_gethook(ML); old_hook.mask = lua_gethookmask(ML); old_hook.count = lua_gethookcount(ML); lua_sethook(ML, hook, LUA_MASKCOUNT, 1); } signal_stack[sig]++; } static int get_signal (lua_State *L, int idx) { switch (lua_type(L, idx)) { case LUA_TNUMBER: return (int) lua_tointeger(L, idx); case LUA_TSTRING: lua_pushvalue(L, idx); lua_rawget(L, LUA_ENVIRONINDEX); if (!lua_isnumber(L, -1)) return luaL_argerror(L, idx, "invalid signal string"); lua_replace(L, idx); return (int) lua_tointeger(L, idx); default: return luaL_argerror(L, idx, "expected signal string/number"); } } static int status (lua_State *L, int s) { if (s) { lua_pushboolean(L, 1); return 1; } else { lua_pushnil(L); lua_pushstring(L, strerror(errno)); return 2; } } /* * old_handler[, err] == signal(signal [, func]) * * signal = signal number or string * func/"ignore"/"default" = Lua function to call */ static int l_signal (lua_State *L) { enum {IGNORE, DEFAULT, SET}; static const char *options[] = {"ignore", "default", NULL}; int sig = get_signal(L, 1); int option; if (lua_isstring(L, 2)) option = luaL_checkoption(L, 2, NULL, options); else if (lua_isnil(L, 2)) option = DEFAULT; else option = (luaL_checktype(L, 2, LUA_TFUNCTION), SET); lua_pushvalue(L, 1); lua_rawget(L, LUA_ENVIRONINDEX); /* return old handler */ lua_pushvalue(L, 1); switch (option) { case IGNORE: lua_pushnil(L); lua_rawset(L, LUA_ENVIRONINDEX); signal(sig, SIG_IGN); signal_stack[sig+signal_stack_top] = signal_stack[sig] = 0; break; case DEFAULT: lua_pushnil(L); lua_rawset(L, LUA_ENVIRONINDEX); signal(sig, SIG_DFL); signal_stack[sig+signal_stack_top] = signal_stack[sig] = 0; break; case SET: lua_pushvalue(L, 2); lua_rawset(L, LUA_ENVIRONINDEX); #if USE_SIGACTION { struct sigaction act; act.sa_handler = handle; sigemptyset(&act.sa_mask); act.sa_flags = 0; if (sigaction(sig, &act, NULL)) return status(L, 0); } #else if (signal(sig, handle) == SIG_ERR) return status(L, 0); #endif break; default: assert(0); } return 1; } /* * status, err = raise(signal) * * signal = signal number or string */ static int l_raise (lua_State *L) { return status(L, raise(get_signal(L, 1)) == 0); } #if INCLUDE_KILL /* define some posix only functions */ /* * status, err = kill(pid, signal) * * pid = process id * signal = signal number or string */ static int l_kill (lua_State *L) { return status(L, kill(luaL_checkinteger(L, 1), get_signal(L, 2)) == 0); } #endif #if INCLUDE_PAUSE static int l_pause (lua_State *L) /* race condition free */ { sigset_t mask, old_mask; if (sigfillset(&mask) == -1) return status(L, 0); if (sigprocmask(SIG_BLOCK, &mask, &old_mask) == -1) return status(L, 0); if (sigsuspend(&old_mask) != -1) abort(); /* that's strange */ return status(L, 0); } #endif static int interrupted (lua_State *L) { return luaL_error(L, "interrupted!"); } static int library_gc (lua_State *L) { lua_getfield(L, LUA_REGISTRYINDEX, LUA_SIGNAL_NAME); lua_pushnil(L); while (lua_next(L, -2)) { if (lua_isnumber(L, -2)) /* */ signal((int) lua_tointeger(L, -2), SIG_DFL); lua_pop(L, 1); /* value */ } signal_stack = NULL; ML = NULL; old_hook.hook = NULL; signal_stack_top = 0; return 0; } int luaopen_signal (lua_State *L) { static const struct luaL_Reg lib[] = { {"signal", l_signal}, {"raise", l_raise}, #if INCLUDE_KILL {"kill", l_kill}, #endif #if INCLUDE_PAUSE {"pause", l_pause}, #endif {NULL, NULL} }; int i; int max_signal; ML = L; if (lua_pushthread(L)) lua_pop(L, 1); else luaL_error(L, "library should be opened by the main thread"); /* environment */ lua_newtable(L); lua_replace(L, LUA_ENVIRONINDEX); lua_pushvalue(L, LUA_ENVIRONINDEX); lua_setfield(L, LUA_REGISTRYINDEX, LUA_SIGNAL_NAME); /* for hooks */ /* add the library */ luaL_register(L, LUA_LIB_NAME, lib); lua_pushnumber(L, LUA_LIB_VERSION); lua_setfield(L, -2, "version"); for (i = 0, max_signal = 0; lua_signals[i].name != NULL; i++) if (lua_signals[i].sig > max_signal) max_signal = lua_signals[i].sig+1; /* +1 !!! (for < loops) */ signal_stack = lua_newuserdata(L, sizeof(volatile sig_atomic_t)*max_signal*2); lua_newtable(L); lua_pushcfunction(L, library_gc); lua_setfield(L, -2, "__gc"); lua_setmetatable(L, -2); /* when userdata is gc'd, close library */ memset((void *) signal_stack, 0, sizeof(volatile sig_atomic_t)*max_signal*2); signal_stack_top = max_signal; lua_pushboolean(L, 1); lua_rawset(L, LUA_ENVIRONINDEX); while (i--) /* i set from previous for loop */ { lua_pushstring(L, lua_signals[i].name); lua_pushinteger(L, lua_signals[i].sig); lua_rawset(L, LUA_ENVIRONINDEX); /* add copy to environment table */ lua_pushstring(L, lua_signals[i].name); lua_pushinteger(L, lua_signals[i].sig); lua_settable(L, -3); /* add copy to signal table */ } /* set default interrupt handler */ lua_getfield(L, -1, "signal"); lua_pushinteger(L, SIGINT); lua_pushcfunction(L, interrupted); lua_call(L, 2, 0); return 1; } ./oar-2.5.2/sources/extra/orpheus/modules/lua-signal/README.txt0000644000175000017500000000765611757171206022274 0ustar plbplbThis is a signal library for Lua 5.1. It depends on ANSI C signals and has some extensions that are available in POSIX, such as kill(). Use Make to compile and install: make && make install You can set the destination manually using: make install SIGNAL_DESTINATION=/path/to/location This code is distributed under the same license as Lua 5.0. You may view the license at the top of any of the source files. =============================================================================== -----------------------------------API----------------------------------------- =============================================================================== All of these functions are placed inside the signal table. old_handler, err = signal(sig, handler) sig = number or string representing the signal for the handler. handler = nil or "default" --> set signal handling to default (SIG_DFL) "ignore" --> set signal handling to ignore (SIG_IGN) function --> sets handler to run upon receipt of the signal Notes: Registers a signal handler for `sig`. Can also set the signal handler to default behavior (as defined by the OS) or set the signal handler to ignore the signal. status[, err] = raise(sig) sig = number or string representing the signal for the handler. Notes: Sends signal `sig` to itself. ======================== For POSIX compliant systems, the following are defined: status[, err] = kill(pid, sig) pid = number representing the process to receive the signal. sig = number or string representing the signal to be sent. Notes: Sends to the process identified by the integer `pid` the signal `sig`. status[, err] = pause() Notes: Pauses the execution of the process until delivery of a signal that would cause a signal handler to run or terminate the process. ======================== SIGNALS: Here are some common signals defined below, but the values can change depending on the system the library is compiled on. You can check all the available signals to you inside the signal library using this script: for k in pairs(signal) do print(k) end Making changes to those signals, or removing them, has no effect on the operation of the signal library. They are provided as a convenience and reference. SIGHUP 1 /* Hangup (POSIX). */ SIGINT 2 /* Interrupt (ANSI). */ SIGQUIT 3 /* Quit (POSIX). */ SIGILL 4 /* Illegal instruction (ANSI). */ SIGTRAP 5 /* Trace trap (POSIX). */ SIGABRT 6 /* Abort (ANSI). */ SIGIOT 6 /* IOT trap (4.2 BSD). */ SIGBUS 7 /* BUS error (4.2 BSD). */ SIGFPE 8 /* Floating-point exception (ANSI). */ SIGKILL 9 /* Kill, unblockable (POSIX). */ SIGUSR1 10 /* User-defined signal 1 (POSIX). */ SIGSEGV 11 /* Segmentation violation (ANSI). */ SIGUSR2 12 /* User-defined signal 2 (POSIX). */ SIGPIPE 13 /* Broken pipe (POSIX). */ SIGALRM 14 /* Alarm clock (POSIX). */ SIGTERM 15 /* Termination (ANSI). */ SIGSTKFLT 16 /* Stack fault. */ SIGCLD SIGCHLD /* Same as SIGCHLD (System V). */ SIGCHLD 17 /* Child status has changed (POSIX). */ SIGCONT 18 /* Continue (POSIX). */ SIGSTOP 19 /* Stop, unblockable (POSIX). */ SIGTSTP 20 /* Keyboard stop (POSIX). */ SIGTTIN 21 /* Background read from tty (POSIX). */ SIGTTOU 22 /* Background write to tty (POSIX). */ SIGURG 23 /* Urgent condition on socket (4.2 BSD). */ SIGXCPU 24 /* CPU limit exceeded (4.2 BSD). */ SIGXFSZ 25 /* File size limit exceeded (4.2 BSD). */ SIGVTALRM 26 /* Virtual alarm clock (4.2 BSD). */ SIGPROF 27 /* Profiling alarm clock (4.2 BSD). */ SIGWINCH 28 /* Window size change (4.3 BSD, Sun). */ SIGPOLL SIGIO /* Pollable event occurred (System V). */ SIGIO 29 /* I/O now possible (4.2 BSD). */ SIGPWR 30 /* Power failure restart (System V). */ SIGSYS 31 /* Bad system call. */ ./oar-2.5.2/sources/extra/orpheus/modules/lua-signal/Makefile0000644000175000017500000000053111757171206022217 0ustar plbplbSIGNAL_DESTINATION ?= /usr/local/lib/lua/5.1/ DESTINATION = $(SIGNAL_DESTINATION) LIBRARY = signal.so OBJECTS = $(LIBRARY) INSTALL = install -c -v CC = gcc CFLAGS = -Wall -fPIC -O2 all: signal.so install: $(LIBRARY) $(INSTALL) -m 755 $(LIBRARY) $(DESTINATION) clean: -rm $(OBJECTS) signal.so: lsignal.c $(CC) $(CFLAGS) -shared -o $@ $^ ./oar-2.5.2/sources/extra/orpheus/lib/0000755000175000017500000000000011757171206015622 5ustar plbplb./oar-2.5.2/sources/extra/orpheus/lib/oar.lua0000644000175000017500000003767111757171206017124 0ustar plbplb-- -- a oarlib draft for lua modules and tools -- -- TODO: -- postgresql support require "luasql.mysql" oar= {} log_level = 2 log_file = "/var/log/oar.log" local env, con oar.conf={} -- TODO change oar.conf location oar_confile_name = "/etc/oar/oar.conf" --oar_confile_name = "/home/auguste/prog/oar/trunk/tools/oar.conf" -- lazy programmed table printers function oar.print_table(t) for k,v in pairs(t) do print(k..'->'..v) end end function oar.print_tt(t) for k,v in pairs(t) do print(">>>"..k) oar.print_table(v) end end -- -- Load oar.conf and intialize configuration variables (oar.conf -- function oar.conf_load() local f = assert(io.open(oar_confile_name, "r")) -- local oar_confile = f:read("*all") local key local value if #oar.conf==0 then -- test conf is already loaded while true do local line = f:read() if line == nil then break end if (not line:match("^%s*#")) and (line:match("%S")) then -- ignore comments which begins by # a,b,key,value = line:find("(%S+)%s*=%s*(%S+)") a,b,c = value:find("\"(%S+)\"") if a then value = c else a,b,c = value:find("'(%S+)'") if a then value = c end end oar.conf[key] = value end end f:close() end if oar.conf["LOG_LEVEL"] then log_level = oar.conf["LOG_LEVEL"] end if oar.conf["LOG_FILE"] then log_file = oar.conf["LOG_FILE"] end end function oar.config_dump() for k,v in pairs(oar.conf) do print(k,v) end end -- -- connect to db -- function oar.connect() -- create environment object env = assert (luasql.mysql()) local db_host = oar.conf["DB_HOSTNAME"] local db_name = oar.conf["DB_BASE_NAME"] local db_user = oar.conf["DB_BASE_LOGIN"] local db_passwd = oar.conf["DB_BASE_PASSWD"] local db_port = oar.conf["DB_PORT"] assert(oar.conf["DB_TYPE"]=="mysql") -- connect to data source con = assert (env:connect(db_name,db_user,db_passwd,db_host,db_port)) end function oar.disconnect() con:close() env:close() end -- usefull iterator function oar.rows (sql_statement) -- print(sql_statement) local cursor = assert (con:execute (sql_statement)) return function () return cursor:fetch({}) end end -- set_all_job_toLaunch (for dev and debug use only) function oar.set_all_jobs_toLaunch() assert (con:execute"UPDATE jobs SET state='toLaunch'") end function oar.sql(query) assert (con:execute(query)) end -- get waiting job with first resource request (no moldable support) -- return an hash table key -> job_ids, value -> jobs description function oar.get_waiting_jobs_no_moldable(queue) if not queue then queue = "default" end local waiting_jobs = {} local query = "SELECT jobs.job_id, moldable_job_descriptions.moldable_walltime, jobs.properties , moldable_job_descriptions.moldable_id, job_resource_descriptions.res_job_resource_type, job_resource_descriptions.res_job_value, job_resource_descriptions.res_job_order, job_resource_groups.res_group_property FROM moldable_job_descriptions, job_resource_groups, job_resource_descriptions, jobs \ WHERE \ moldable_job_descriptions.moldable_index = 'CURRENT' \ AND job_resource_groups.res_group_index = 'CURRENT' \ AND job_resource_descriptions.res_job_index = 'CURRENT' \ AND jobs.state = 'Waiting' \ AND jobs.queue_name = '" .. queue .. "' \ AND jobs.reservation = 'None' \ AND jobs.job_id = moldable_job_descriptions.moldable_job_id \ AND job_resource_groups.res_group_index = 'CURRENT' \ AND job_resource_groups.res_group_moldable_id = moldable_job_descriptions.moldable_id \ AND job_resource_descriptions.res_job_index = 'CURRENT' \ AND job_resource_descriptions.res_job_group_id = job_resource_groups.res_group_id \ ORDER BY moldable_job_descriptions.moldable_id, job_resource_groups.res_group_id, job_resource_descriptions.res_job_order ASC;" for row in oar.rows(query) do waiting_jobs[row[1]]=row end return waiting_jobs end -- get waiting job with first resource request (no moldable support) -- return an hash table key -> job_ids, value -> jobs description function oar.get_waiting_jobs_black_maria(queue) if not queue then queue = "default" end local waiting_jobs = {} -- note: jobs.scheduler_info = '' below is use to filter job already submited to foreign JRMS local query = "SELECT jobs.job_id, moldable_job_descriptions.moldable_walltime, jobs.properties , moldable_job_descriptions.moldable_id, job_resource_descriptions.res_job_resource_type, job_resource_descriptions.res_job_value, jobs.job_user FROM moldable_job_descriptions, job_resource_groups, job_resource_descriptions, jobs \ WHERE \ moldable_job_descriptions.moldable_index = 'CURRENT' \ AND job_resource_groups.res_group_index = 'CURRENT' \ AND job_resource_descriptions.res_job_index = 'CURRENT' \ AND jobs.state = 'Waiting' \ AND jobs.scheduler_info = ''\ AND jobs.queue_name = '" .. queue .. "' \ AND jobs.reservation = 'None' \ AND jobs.job_id = moldable_job_descriptions.moldable_job_id \ AND job_resource_groups.res_group_index = 'CURRENT' \ AND job_resource_groups.res_group_moldable_id = moldable_job_descriptions.moldable_id \ AND job_resource_descriptions.res_job_index = 'CURRENT' \ AND job_resource_descriptions.res_job_group_id = job_resource_groups.res_group_id \ ORDER BY moldable_job_descriptions.moldable_id, job_resource_groups.res_group_id, job_resource_descriptions.res_job_order ASC;" local i =1 for row in oar.rows(query) do waiting_jobs[i]=row i = i + 1 end return waiting_jobs end -- update scheduler_info field for a set of jobs function oar.set_scheduler_message_range(j_ids,msg) local job_ids = "" for i,j_id in ipairs(j_ids) do job_ids = job_ids .. j_id .. ',' end job_ids = string.sub(job_ids, 1, -2) --chomp the last ',' local query = "UPDATE jobs SET scheduler_info='"..msg.."' WHERE job_id IN ("..job_ids.. ")" print(query) assert (con:execute(query)) end -- retreive resource_ids by node function oar.get_nodes_resources_black_maria() local nodes_resources = {} local query = "SELECT resource_id, network_address FROM resources" for row in oar.rows(query) do local n = row[2] local r_id = row[1] if nodes_resources[n] then nodes_resources[n][#nodes_resources[n]+1] = r_id else nodes_resources[n]={r_id} end end return nodes_resources end -- set_assigned_moldable_job -- sets the assigned_moldable_job field to the given value function oar.set_assigned_moldable_job(job_id,moldable_job_id) local query = "UPDATE jobs SET assigned_moldable_job =" .. moldable_job_id .. "WHERE job_id = " .. job_id ..")" assert(con:execute(query)) end -- save resources assignemet for one job function oar.save_assignements_black_maria(moldable_job_id,resource_ids) local values = "" for i,r_id in ipairs(resource_ids) do values = values ..'('.. moldable_job_id .. ',' .. r_id .. ',\'CURRENT\'),' end values = string.sub(values, 1, -2) --chomp the last ',' local query = "INSERT INTO assigned_resources (moldable_job_id,resource_id,assigned_resource_index) VALUES " .. values assert (con:execute(query)) end --- --- helper section --- -- white space split string function returns multiple strings function oar.wssplit(text, start) local s,e,word = string.find(text, "(%S+)", start or 1) if s then return word, oar.wssplit(text, e+1); end end -- white space split string function, returns a table function oar.tsplit(str) local t = {} local function helper(word) table.insert(t, word) return "" end if not str:gsub("%S+", helper):find"%S" then return t end end function oar.write_log(msg) -- TODO print(msg) end function oar.debug(msg) if log_level > 2 then oar.write_log('[debug] '..msg) end end function oar.warn(msg) if log_level > 1 then oar.write_log('[info] '..msg) end end function oar.error(msg) oar.write_log('[error] '..msg) end --- --- this code come from lua's wiki site --- http://lua-users.org/wiki/SortedIteration --- -------------------------------------- -- Insert value of any type into array -------------------------------------- local function arrayInsert( ary, val, idx ) -- Needed because table.insert has issues -- An "array" is a table indexed by sequential -- positive integers (no empty slots) local lastUsed = #ary + 1 local nextAvail = lastUsed + 1 -- Determine correct index value local index = tonumber(idx) -- Don't use idx after this line! if (index == nil) or (index > nextAvail) then index = nextAvail elseif (index < 1) then index = 1 end -- Insert the value if ary[index] == nil then ary[index] = val else -- TBD: Should we try to allow for skipped indices? for j = nextAvail,index,-1 do ary[j] = ary[j-1] end ary[index] = val end end -------------------------------- -- Compare two items of any type -------------------------------- local function compareAnyTypes( op1, op2 ) -- Return the comparison result -- Inspired by http://lua-users.org/wiki/SortedIteration local type1, type2 = type(op1), type(op2) local num1, num2 = tonumber(op1), tonumber(op2) if ( num1 ~= nil) and (num2 ~= nil) then -- Number or numeric string return num1 < num2 -- Numeric compare elseif type1 ~= type2 then -- Different types return type1 < type2 -- String compare of type name -- From here on, types are known to match (need only single compare) elseif type1 == "string" then -- Non-numeric string return op1 < op2 -- Default compare elseif type1 == "boolean" then return op1 -- No compare needed! -- Handled above: number, string, boolean else -- What's left: function, table, thread, userdata return tostring(op1) < tostring(op2) -- String representation end end ------------------------------------------- -- Iterate over a table in sorted key order ------------------------------------------- local function pairsByKeys (tbl, func) -- Inspired by http://www.lua.org/pil/19.3.html -- and http://lua-users.org/wiki/SortedIteration if func == nil then func = compareAnyTypes end -- Build a sorted array of the keys from the passed table -- Use an insertion sort, since table.sort fails on non-numeric keys local ary = {} local lastUsed = 0 for key --[[, val--]] in pairs(tbl) do if (lastUsed == 0) then ary[1] = key else local done = false for j=1,lastUsed do -- Do an insertion sort if (func(key, ary[j]) == true) then arrayInsert( ary, key, j ) done = true break end end if (done == false) then ary[lastUsed + 1] = key end end lastUsed = lastUsed + 1 end -- Define (and return) the iterator function local i = 0 -- iterator variable local iter = function () -- iterator function i = i + 1 if ary[i] == nil then return nil else return ary[i], tbl[ary[i]] end end return iter end --------------------------------------------- -- Return indentation string for passed level --------------------------------------------- local function tabs(i) return string.rep(".",i).." " -- Dots followed by a space end ----------------------------------------------------------- -- Return string representation of parameter's value & type ----------------------------------------------------------- local function toStrType(t) local function fttu2hex(t) -- Grab hex value from tostring() output local str = tostring(t); if str == nil then return "tostring() failure! \n" else local str2 = string.match(str,"[ :][ (](%x+)") if str2 == nil then return "string.match() failure: "..str.."\n" else return "0x"..str2 end end end -- Stringify a value of a given type using a table of functions keyed -- by the name of the type (Lua's version of C's switch() statement). local stringify = { -- Keys are all possible strings that type() may return, -- per http://www.lua.org/manual/5.1/manual.html#pdf-type. ["nil"] = function(v) return "nil (nil)" end, ["string"] = function(v) return '"'..v..'" (string)' end, ["number"] = function(v) return v.." (number)" end, ["boolean"] = function(v) return tostring(v).." (boolean)" end, ["function"] = function(v) return fttu2hex(v).." (function)" end, ["table"] = function(v) return fttu2hex(v).." (table)" end, ["thread"] = function(v) return fttu2hex(v).." (thread)" end, ["userdata"] = function(v) return fttu2hex(v).." (userdata)" end } return stringify[type(t)](t) end ------------------------------------- -- Count elements in the passed table ------------------------------------- local function lenTable(t) -- What Lua builtin does this simple thing? local n=0 -- '#' doesn't work with mixed key types if ("table" == type(t)) then for key in pairs(t) do -- Just count 'em n = n + 1 end return n else return nil end end -------------------------------- -- Pretty-print the passed table -------------------------------- local function do_dumptable(t, indent, seen) -- "seen" is an initially empty table used to track all tables -- that have been dumped so far. No table is dumped twice. -- This also keeps the code from following self-referential loops, -- the need for which was found when first dumping "_G". if ("table" == type(t)) then -- Dump passed table seen[t] = 1 if (indent == 0) then print ("The passed table has "..lenTable(t).." entries:") indent = 1 end for f,v in pairsByKeys(t) do if ("table" == type(v)) and (seen[v] == nil) then -- Recurse print( tabs(indent)..toStrType(f).." has "..lenTable(v).." entries: {") do_dumptable(v, indent+1, seen) print( tabs(indent).."}" ) else print( tabs(indent)..toStrType(f).." = "..toStrType(v)) end end else print (tabs(indent).."Not a table!") end end -------------------------------- -- Wrapper to handle persistence -------------------------------- function dumptable(t) -- Only global declaration in the package -- This wrapper exists only to set the environment for the first run: -- The second param is the indentation level. -- The third param is the list of tables dumped during this call. -- Getting this list allocated and freed was a pain, and this -- wrapper was the best solution I came up with... return do_dumptable(t, 0, {}) end -------------- -------------- -- -- copy table function from lua-users.oar/wiki -- http://lua-users.org/wiki/CopyTable -- function oar.deepcopy(object) local lookup_table = {} local function _copy(object) if type(object) ~= "table" then return object elseif lookup_table[object] then return lookup_table[object] end local new_table = {} lookup_table[object] = new_table for index, value in pairs(object) do new_table[_copy(index)] = _copy(value) end return setmetatable(new_table, getmetatable(object)) end return _copy(object) end --[[ -- The Whole Enchillada print("\ndumptable(_G=", _G, "):") dumptable(_G) -- Empty table -- print("\ndumptable({}):") dumptable({}) -- Module table -- print("\ndumptable(os=", os, "):") dumptable(os) ]]-- ./oar-2.5.2/sources/extra/ocaml-schedulers/0000755000175000017500000000000011757171206016621 5ustar plbplb./oar-2.5.2/sources/extra/ocaml-schedulers/simple_cbf_mb_h_ct_oar/0000755000175000017500000000000011757171206023240 5ustar plbplb./oar-2.5.2/sources/extra/ocaml-schedulers/simple_cbf_mb_h_ct_oar/types.ml0000644000175000017500000000423411757171206024741 0ustar plbplb(* open Mysql *) (* TODO *) open Int64 open Interval type time_t = int64 (* 64 bits int because of unix_time use *) type jobid_t = int (* type job_state_t = Running | ToLaunch | Waiting (* Do we need it for simple_cbf_oar ? *) *) type resource_state_t = Alive | Suspected | Absent | Dead let rstate_of_string = function "Alive" -> Alive | "Suspected" -> Suspected | "Absent" -> Absent | "Dead" -> Dead | s -> Conf.error (Printf.sprintf "rstate_of_string : unknown state %s" s) let rstate_to_string = function Alive -> "Alive" | Suspected -> "Suspected" | Absent -> "Absent" | Dead -> "Dead" type resource = { resource_id: int; network_address: string; state: resource_state_t; available_upto: time_t; } type job = { jobid : jobid_t; moldable_id : int; jobstate : string; user : string; project : string; mutable time_b : time_t; mutable walltime : time_t; (* mutable need to reset besteffort's one*) mutable types : (string * string) list; mutable hy_level_rqt : string list list; mutable hy_nb_rqt : int list list; mutable constraints : set_of_resources list; mutable set_of_rs : set_of_resources; } (* job_required_status is used for job dependencies *) type job_required_status = { (* jr_id : jobid_t; *) jr_state : string; jr_jtype : string; jr_exit_code : int; (* jr_start_time : time_t; (* remove ? *) jr_walltime : time_t; (* remove ? *) *) } (* Pretty - printing ** TO MOVE in helpers ??? **) let job_to_string t = let itv2str itv = Printf.sprintf "[%d,%d]" itv.b itv.e in (Printf.sprintf "job_id: %d start_time: %s walltime: %s " t.jobid (Int64.to_string t.time_b) (Int64.to_string t.walltime)) ^ (Printf.sprintf " res_itv: ") ^ (String.concat ", " (List.map itv2str t.set_of_rs)) ^ (Printf.sprintf "\n types: %s " (Helpers.concatene_sep "," (fun n -> String.concat "*" [fst(n);snd(n)]) t.types)) ^ (Printf.sprintf "h_type: level: "^ (String.concat "*" (List.flatten t.hy_level_rqt)))^ (Printf.sprintf " nb: "^ (String.concat "*" (List.map string_of_int (List.flatten t.hy_nb_rqt)))) let resource_to_string n = Printf.sprintf "(%d) -%s- %s" n.resource_id (rstate_to_string n.state) n.network_address ./oar-2.5.2/sources/extra/ocaml-schedulers/simple_cbf_mb_h_ct_oar/test_unit.ml0000644000175000017500000001323211757171206025611 0ustar plbplbopen Interval open Types open Hierarchy open Helpers open Simple_cbf_mb_h_ct open OUnit let h0 = [{b = 1; e = 16};{b = 17; e = 32};];; let h1 = [{b = 1; e = 8}; {b = 9; e = 16}; {b = 17; e = 24}; {b = 25; e = 32}];; let h2 = [{b = 1; e = 4}; {b = 5; e = 8}; {b = 9; e =12}; {b = 13; e = 16}; {b = 17; e = 20}; {b = 21; e = 24}; {b = 25; e = 28}; {b = 29; e = 32}];; hierarchy_levels := [ ("node",h0);("cpu",h1);("core",h2) ];; let master_top = {b = 1; e = 32} ;; let c0 = [{b = 1; e = 40}] ;; (* let j1 = { time_b = 0L; walltime = 3L; hy_level_rqt = [["cpu"]]; hy_nb_rqt = [[4]]; constraints =[c0]; set_of_rs = []; types = []};; let slots_init = [slot_max 100];; let h_slots_0 = couples2hash [(0,slots_init)];; let j0 = { time_b = 0L; walltime = 3L; hy_level_rqt = [["cpu"]]; hy_nb_rqt = [[4]]; constraints =[[{b = 1; e = 40}]]; set_of_rs = []; types = []};; let h_j0 = couples2hash [(1,j0)];; let j10 = {time_b = 0L; walltime = 40L; hy_level_rqt = [["cpu"]]; hy_nb_rqt = [[4]]; constraints = [[{b = 1; e = 40}]]; set_of_rs = [] ; types = [("container","")]};; let j11 = {time_b = 0L; walltime = 20L; hy_level_rqt = [["cpu"]]; hy_nb_rqt = [[1]]; constraints = [[{b = 1; e = 40}]]; set_of_rs = [] ; types = [("inner","1")]};; let assign_ct_0 = [{time_b = 0L; walltime = 40L; hy_level_rqt = [["cpu"]]; hy_nb_rqt = [[4]]; types = [("container", "")]; constraints = [[{b = 1; e = 40}]]; set_of_rs = [{b = 1; e = 8}; {b = 9; e = 16}; {b = 17; e = 24}; {b = 25; e = 32}]}; {time_b = 0L; walltime = 20L; hy_level_rqt = [["cpu"]]; hy_nb_rqt = [[1]]; types = [("inner", "1")]; constraints = [[{b = 1; e = 40}]]; set_of_rs = [{b = 1; e = 8}]}];; (* simple job scheduling *) let test_schedule_jobs_0 _ = let c0 = [{b = 1; e = 40}] in let j0 = { time_b = 0L; walltime = 3L; hy_level_rqt = [["cpu"]]; hy_nb_rqt = [[4]]; constraints =[c0]; set_of_rs = []; types = []} in let assign_j0 = [ {time_b = 0L; walltime = 3L; hy_level_rqt = [["cpu"]]; hy_nb_rqt = [[4]]; constraints = [[{b = 1; e = 40}]]; set_of_rs = [{b = 1; e = 8}; {b = 9; e = 16};{b = 17; e = 24}; {b = 25; e = 32}] ; types = []} ] in assert_equal assign_j0 (schedule_jobs [j0] [slot_max 100]) (* test with job start time (time_b) not equal to 0 needed to support job dependencies *) let test_schedule_jobs_start_time _ = let c0 = [{b = 1; e = 40}] in let j0 = { time_b = 100L; walltime = 3L; hy_level_rqt = [["cpu"]]; hy_nb_rqt = [[4]]; constraints =[c0]; set_of_rs = []; types = []} in let j1 = { time_b = 0L; walltime = 300L; hy_level_rqt = [["cpu"]]; hy_nb_rqt = [[4]]; constraints =[c0]; set_of_rs = []; types = []} in let j2 = { time_b = 0L; walltime = 30L; hy_level_rqt = [["cpu"]]; hy_nb_rqt = [[4]]; constraints =[c0]; set_of_rs = []; types = []} in let assign_j012 = [ {time_b = 100L; walltime = 3L; hy_level_rqt = [["cpu"]]; hy_nb_rqt = [[4]]; types = []; constraints = [[{b = 1; e = 40}]]; set_of_rs = [{b = 1; e = 8}; {b = 9; e = 16}; {b = 17; e = 24}; {b = 25; e = 32}]}; {time_b = 103L; walltime = 300L; hy_level_rqt = [["cpu"]]; hy_nb_rqt = [[4]]; types = []; constraints = [[{b = 1; e = 40}]]; set_of_rs = [{b = 1; e = 8}; {b = 9; e = 16}; {b = 17; e = 24}; {b = 25; e = 32}]}; {time_b = 0L; walltime = 30L; hy_level_rqt = [["cpu"]]; hy_nb_rqt = [[4]]; types = []; constraints = [[{b = 1; e = 40}]]; set_of_rs = [{b = 1; e = 8}; {b = 9; e = 16}; {b = 17; e = 24}; {b = 25; e = 32}]} ] in assert_equal assign_j012 (schedule_jobs [j0;j1;j2] [slot_max 100]) (* test set_slots_with_prev_scheduled_jobs with container considerations *) let test_schedule_ct_0 _ = let h_j = couples2hash [(1,j10);(2,j11)] in let h_slots_0 = couples2hash [(0,[slot_max 100])] in assert_equal assign_ct_0 (schedule_id_jobs_ct h_slots_0 h_j [1;2]) (* test simple dependencies let test_schedule_ct_dep_0 _ = let c0 = [{b = 1; e = 40}] in let j1 = { time_b = 0L; walltime = 100L; hy_level_rqt = [["cpu"]]; hy_nb_rqt = [[1]]; constraints =[c0]; set_of_rs = []; types = []} in let j2 = { time_b = 0L; walltime = 50L; hy_level_rqt = [["cpu"]]; hy_nb_rqt = [[1]]; constraints =[c0]; set_of_rs = []; types = []} in let j3 = { time_b = 0L; walltime = 30L; hy_level_rqt = [["cpu"]]; hy_nb_rqt = [[1]]; constraints =[c0]; set_of_rs = []; types = []} in let h_deps = couples2hash [(2,[1]);(3,[1;2])] in let h_j_status = couples2hash [(1,{jr_state="Waiting";jr_jtype="PASSIVE";jr_exit_code=0}); (2,{jr_state="Waiting";jr_jtype="PASSIVE";jr_exit_code=0}); (3,{jr_state="Waiting";jr_jtype="PASSIVE";jr_exit_code=0})] in let h_j = couples2hash [(1,j1);(2,j2);(3,j3)] in let h_slots_0 = couples2hash [(0,[slot_max 100])] in let a = [{time_b = 0L; walltime = 100L; hy_level_rqt = [["cpu"]]; hy_nb_rqt = [[1]]; types = []; constraints = [[{b = 1; e = 40}]]; set_of_rs = [{b = 1; e = 8}]}; {time_b = 100L; walltime = 50L; hy_level_rqt = [["cpu"]]; hy_nb_rqt = [[1]]; types = []; constraints = [[{b = 1; e = 40}]]; set_of_rs = [{b = 9; e = 16}]}; {time_b = 150L; walltime = 30L; hy_level_rqt = [["cpu"]]; hy_nb_rqt = [[1]]; types = []; constraints = [[{b = 1; e = 40}]]; set_of_rs = [{b = 17; e = 24}]}] in assert_equal a (schedule_id_jobs_ct_dep h_slots_0 h_j h_deps h_j_status [1;2;3]) *) *) let suite = "Unit test for simple_cbf_mb_h_ct" >::: [ (* "test_schedule_jobs_0" >:: test_schedule_jobs_0; *) (* "test_schedule_jobs__start_time" >:: test_schedule_jobs_start_time; "test_schedule_ct_0" >:: test_schedule_ct_0; "test_schedule_ct_0" >:: test_schedule_ct_0; *) (* "test_schedule_ct_dep_0" >:: test_schedule_ct_dep_0; *) ] let _ = run_test_tt ~verbose:true suite ./oar-2.5.2/sources/extra/ocaml-schedulers/simple_cbf_mb_h_ct_oar/test_iolib.ml0000644000175000017500000000037511757171206025734 0ustar plbplbopen Iolib let _= (* let conn = let r = Iolib_pg.connect () in at_exit (fun () -> Iolib_pg.disconnect r); r in *) (* let r = Iolib_pg.connect () in let g = get_resource_list r in test_db r ;; *) let r = Iolib.connect () in get_resource_list r ;; ./oar-2.5.2/sources/extra/ocaml-schedulers/simple_cbf_mb_h_ct_oar/test.ml0000644000175000017500000001112611757171206024552 0ustar plbplb(* *) (* This file is not use for compilation only for debuging purpose *) (* to track jobs' overlapping bug *) (* Author: auguste@imag.fr *) (* *) (* Bug on itv ordering # s3;; - : Simple_cbf_mb_h_ct.slot = {time_s = 0L; time_e = 2147483648L; set_of_res = [{b = 1; e = 5}]} # split_slots_prev_scheduled_one_job [s3] j24;; - : Simple_cbf_mb_h_ct.slot list = [{time_s = 0L; time_e = 80L; set_of_res = [{b = 5; e = 5}; {b = 1; e = 2}]}; {time_s = 81L; time_e = 2147483648L; set_of_res = [{b = 1; e = 5}]}] -> split_slots [s3] j24;; -> sub_intervals s3.set_of_res j24.set_of_rs;; - : Interval.interval list = [{b = 5; e = 5}; {b = 1; e = 2}] Expected: - : Simple_cbf_mb_h_ct.slot list = [{time_s = 0L; time_e = 80L; set_of_res = [{b = 1; e = 2};{b = 5; e = 5};]}; {time_s = 81L; time_e = 2147483648L; set_of_res = [{b = 1; e = 5}]}] sub_intervals [{b = 1; e = 5}] [{b = 3; e = 4}; {b = 12; e = 12}];; [{b = 1; e = 2};{b = 5; e = 5};] *) open Interval;; open Hierarchy;; open Simple_cbf_mb_h_ct;; open Types;; let hierarchy_levels = ref [ ("resource_id",[{b = 1; e = 12}]) ];; let toplevel_itv = ref {b = 1; e = 12} ;; (* let smax = slot_max 12;;*) let s0 = {time_s = 0L; time_e = 2147483648L; set_of_res = [{b = 1; e = 12}]};; let s1 = {time_s = 0L; time_e = 2147483648L; set_of_res = [{b=7;e=8}; {b=5;e=5}; {b=1;e=2};{b=11;e=11}]};; let s2 = {time_s = 0L; time_e = 2147483648L; set_of_res = [{b=5;e=5}; {b=1;e=2}]};; let s3 = {time_s = 0L; time_e = 2147483648L; set_of_res = [{b = 1; e = 5}; {b = 7; e = 8}; {b = 11; e = 12}]};; let s3 = {time_s = 0L; time_e = 2147483648L; set_of_res = [{b = 1; e = 5}; {b = 7; e = 8}]};; let s3 = {time_s = 0L; time_e = 2147483648L; set_of_res = [{b = 1; e = 5}]};; let hslots = Hashtbl.create 10;; Hashtbl.add hslots 0 [s0]; ;; (* Prev now= 1328014985 job_id: 27 start_time: 1328014984 walltime: 88 res_itv: [6,6], [9,10] 88-1 = 87 job_id: 24 start_time: 1328014978 walltime: 88 res_itv: [3,4], [12,12] 88-7 = 81 job_id: 23 start_time: 1328014975 walltime: 88 res_itv: [1,2], [11,11] 88-10 = 78 job_id: 19 start_time: 1328014867 walltime: 258 res_itv: [7,8] 258 - 118 =140 job_id: 17 start_time: 1328014862 walltime: 336 res_itv: [5,5] 336 - 123 = 213 *) let j27 = {jobid = 27; moldable_id = 27; jobstate = ""; time_b = 0L; walltime = 87L; types = []; hy_level_rqt = [["resource_id"]]; hy_nb_rqt = [[3]]; constraints = [[{b = 1; e = 12}]]; set_of_rs = [{b = 6; e = 6};{b = 9; e = 10}]} ;; let j24 = {jobid = 24; moldable_id = 24; jobstate = ""; time_b = 0L; walltime = 81L; types = []; hy_level_rqt = [["resource_id"]]; hy_nb_rqt = [[3]]; constraints = [[{b = 1; e = 12}]]; set_of_rs = [{b = 3; e = 4};{b = 12; e = 12}]} ;; let j23 = {jobid = 23; moldable_id = 23; jobstate = ""; time_b = 0L; walltime = 78L; types = []; hy_level_rqt = [["resource_id"]]; hy_nb_rqt = [[3]]; constraints = [[{b = 1; e = 12}]]; set_of_rs = [{b = 1; e = 2};{b = 11; e = 11}]} ;; let j19 = {jobid = 19; moldable_id = 19; jobstate = ""; time_b = 0L; walltime = 140L; types = []; hy_level_rqt = [["resource_id"]]; hy_nb_rqt = [[2]]; constraints = [[{b = 1; e = 12}]]; set_of_rs = [{b = 7; e = 8}]} ;; let j17 = {jobid = 17; moldable_id = 17; jobstate = ""; time_b = 0L; walltime = 213L; types = []; hy_level_rqt = [["resource_id"]]; hy_nb_rqt = [[1]]; constraints = [[{b = 1; e = 12}]]; set_of_rs = [{b = 5; e = 5}]} ;; let j0 = {jobid = 0; moldable_id = 0; jobstate = ""; time_b = 0L; walltime = 81L; types = []; hy_level_rqt = [["resource_id"]]; hy_nb_rqt = [[2]]; constraints = [[{b = 1; e = 12}]]; set_of_rs = [{b = 3; e = 4}]} ;; let hjobs = Hashtbl.create 10;; Hashtbl.add hjobs 27 j27; Hashtbl.add hjobs 24 j24; Hashtbl.add hjobs 23 j23; Hashtbl.add hjobs 19 j19; Hashtbl.add hjobs 17 j17; ;; let slot0 = try Hashtbl.find hslots 0 with Not_found -> failwith "bou" in Conf.log ("slot0:\n" ^ (Helpers.concatene_sep "\n " slot_to_string slot0)); (* let jobids = [27;24;23;19;17];; *) (* BOU *) (* let jobids = [17;19;23;24;27];; *) let jobids = [27;24;23;];; (* Bou *) set_slots_with_prev_scheduled_jobs hslots hjobs jobids 60L;; let hjobs23 = Hashtbl.create 10;; Hashtbl.add hjobs23 23 j23; ;; let jobids23 = [23];; let hslots23 = Hashtbl.create 10;; Hashtbl.add hslots23 0 [s0]; ;; let slot23 = try Hashtbl.find hslots23 0 with Not_found -> failwith "bou" in Conf.log ("slot23:\n" ^ (Helpers.concatene_sep "\n " slot_to_string slot23)); set_slots_with_prev_scheduled_jobs hslots23 hjobs23 jobids23 60L;; ./oar-2.5.2/sources/extra/ocaml-schedulers/simple_cbf_mb_h_ct_oar/simple_cbf_mb_h_ct_oar.ml0000644000175000017500000003746311757171206030226 0ustar plbplbopen Types open Interval open Conf open Simple_cbf_mb_h_ct (* TODO 1) Add security_time_overhead = SCHEDULER_JOB_SECURITY_TIME | 60 1.1) Suspend / SCHEDULER_AVAILABLE_SUSPENDED_RESOURCE_TYPE 1.2) Fairsharing 1.0.1) Test SCHEDULER_JOB_SECURITY_TIME in container context 3) Besteffort (need resource reverse order => not sure ?) 4) Message scheduler/job (same and more than perl scheduler) (* need to be optimize vectorize*) 8) export OARCONFFILE=oar.conf as in perl version 9) to test: resources types Done but not tested -------------------- 1) Add security_time_overhead = SCHEDULER_JOB_SECURITY_TIME | 60 *) (* To be supported: ---------------- * SCHEDULER_AVAILABLE_SUSPENDED_RESOURCE_TYPE * SCHEDULER_RESOURCES_ALWAYS_ASSIGNED_TYPE (???) Not supported: -------------- * SCHEDULER_GANTT_HOLE_MINIMUM_TIME * SCHEDULER_RESOURCE_ORDER * SCHEDULER_NB_PROCESSES * SCHEDULER_TIMEOUT *) let besteffort_duration = Int64.of_int (5*60) (* >> 2**31 => 2147483648 >> 2**31 -1 => 2147483647 *) let max_time = 2147483648L let max_time_minus_one = 2147483647L (* Constant duration time of a besteffort job *) let besteffort_duration = 300L (* *) (* Karma and Fairsharing stuff *) (* *) (* test if Fairsharing is enabled*) let fairsharing_flag = Conf.test_key("FAIRSHARING_ENABLED") let fairsharing_nb_job_limit = Conf.get_default_value "SCHEDULER_FAIRSHARING_MAX_JOB_PER_USER" "30" let karma_window_size = Int64.of_int ( 3600 * 30 * 24 ) (* 30 days *) (* defaults values for fairsharing *) let k_proj_targets = "{default => 21.0}" let k_user_targets = "{default => 22.0}" let k_coeff_proj_consumption = "0" let k_coeff_user_consumption = "1" let k_karma_coeff_user_asked_consumption = "1" (* get fairsharing config if any *) let karma_proj_targets = Conf.str_perl_hash_to_pairs_w_convert (Conf.get_default_value "SCHEDULER_FAIRSHARING_PROJECT_TARGETS" k_proj_targets) float_of_string_e let karma_user_targets = Conf.str_perl_hash_to_pairs_w_convert (Conf.get_default_value "SCHEDULER_FAIRSHARING_USER_TARGETS" k_user_targets) float_of_string_e let karma_coeff_proj_consumption = float_of_string_e (Conf.get_default_value "SCHEDULER_FAIRSHARING_COEF_PROJECT" k_coeff_proj_consumption) let karma_coeff_user_consumption = float_of_string_e (Conf.get_default_value "SCHEDULER_FAIRSHARING_COEF_USER" k_coeff_user_consumption) let karma_coeff_user_asked_consumption = float_of_string_e (Conf.get_default_value "SCHEDULER_FAIRSHARING_COEF_USER_ASK" k_karma_coeff_user_asked_consumption) (* *) (* Sort jobs accordingly to karma value (fairsharing) *) (* *) let jobs_karma_sorting dbh queue now karma_window_size jobs_ids h_jobs = let start_window = Int64.sub now karma_window_size and stop_window = now in let karma_sum_time_asked, karma_sum_time_used = Iolib.get_sum_accounting_window dbh queue start_window stop_window and karma_projects_asked, karma_projects_used = Iolib.get_sum_accounting_for_param dbh queue "accounting_project" start_window stop_window and karma_users_asked, karma_users_used = Iolib.get_sum_accounting_for_param dbh queue "accounting_user" start_window stop_window in let karma j = let job = try Hashtbl.find h_jobs j with Not_found -> failwith "Karma: not found job" in let user = job.user and proj = job.project in let karma_proj_used_j = try Hashtbl.find karma_projects_used proj with Not_found -> 0.0 and karma_user_used_j = try Hashtbl.find karma_users_used user with Not_found -> 0.0 and karma_user_asked_j = try Hashtbl.find karma_users_asked user with Not_found -> 0.0 (* TODO test *) and karma_proj_target = try List.assoc proj karma_proj_targets with Not_found -> 0.0 (* TODO verify in perl 0 also ? *) and karma_user_target = (try List.assoc user karma_user_targets with Not_found -> 0.0 ) /. 100.0 (* TODO verify in perl 0 also ? *) in karma_coeff_proj_consumption *. ((karma_proj_used_j /. karma_sum_time_used) -. (karma_proj_target /. 100.0)) +. karma_coeff_user_consumption *. ((karma_user_used_j /. karma_sum_time_used) -. (karma_user_target /. 100.0)) +. karma_coeff_user_asked_consumption *. ((karma_user_asked_j /. karma_sum_time_asked) -. (karma_user_target /. 100.0)) in let kompare x y = let kdiff = (karma x) -. (karma y) in if kdiff = 0.0 then 0 else if kdiff > 0.0 then 1 else -1 in List.sort kompare jobs_ids;; (* *) (* Suspend stuff *) (* *) (* my $sched_available_suspended_resource_type_tmp = get_conf("SCHEDULER_AVAILABLE_SUSPENDED_RESOURCE_TYPE"); if (!defined($sched_available_suspended_resource_type_tmp)){ push(@Sched_available_suspended_resource_type, "default"); }else{ @Sched_available_suspended_resource_type = split(" ",$sched_available_suspended_resource_type_tmp); } *) let argv = if (Array.length(Sys.argv) > 2) then (Sys.argv.(1), (Int64.of_string Sys.argv.(2))) else ("default", Int64.of_float (Unix.time ())) (* *) (* Determine Global Resource Intervals and Initial Slot *) (* with or without resource availabilty (field available_upto in resources table) *) (* *) let resources_init_slots_determination dbh now = let potential_resources = Iolib.get_resource_list dbh in let flag_wake_up_cmd = Conf.test_key("SCHEDULER_NODE_MANAGER_WAKE_UP_CMD") || (((compare (Conf.get_default_value "ENERGY_SAVING_INTERNAL" "no") "yes")==0) && Conf.test_key("ENERGY_SAVING_NODE_MANAGER_WAKE_UP_CMD")) in if flag_wake_up_cmd then (* *) (* generate initial slot with no dead and suspected resources and with resources (nodes) which can be waked up *) (* *) begin Conf.log "Energy Saving and Wakeup Mode are enabled"; let hash_available_upto_by_resources = Hashtbl.create 10 in let hash_available_upto_by_resources_populate r = let res_lst = try Hashtbl.find hash_available_upto_by_resources r.available_upto with Not_found -> Hashtbl.add hash_available_upto_by_resources r.available_upto [r.resource_id];[] in match res_lst with [] -> () | x -> Hashtbl.replace hash_available_upto_by_resources r.available_upto (x @ [r.resource_id]) in let resources = List.filter (fun n -> if (n.state = Alive) || (n.state = Absent) then begin hash_available_upto_by_resources_populate n; true end else false ) potential_resources in let resource_intervals = if ((List.length resources) = 0) then begin Conf.log "none available ressources for scheduling any jobs"; exit 0 end else ints2intervals (List.map (fun n -> n.resource_id) resources) in (* create corresponding job from available_up parameter of resource *) let pseudo_job_av_upto a_upto res_itv = { jobid=0; jobstate=""; moldable_id=0; time_b = if (a_upto if (avail_upto < max_time_minus_one) then (pseudo_job_av_upto avail_upto (ints2intervals r_set)) :: acc else acc ) hash_available_upto_by_resources [] in (* generate initial slot with no dead and suspected resources and with resources (nodes) which can be waked up *) let slot_init = {time_s = now; time_e = max_time; set_of_res = resource_intervals} in let slots_init_available_upto_resources = split_slots_prev_scheduled_jobs [slot_init] pseudo_jobs_resources_available_upto in (resource_intervals,slots_init_available_upto_resources) end else let resources = List.filter (fun n -> n.state = Alive) potential_resources in let resource_intervals = ints2intervals (List.map (fun n -> n.resource_id) resources) in (resource_intervals,[{time_s = now; time_e = max_time; set_of_res = resource_intervals}]) (* *) (* Main function *) (* *) let _ = try Conf.log "Starting"; (* get hierarchy description from oar.conf and convert it in hierarchy levels and set master_top (toplevel interval) *) Hierarchy.hierarchy_levels := Hierarchy.h_desc_to_h_levels Conf.get_hierarchy_info; Hierarchy.toplevel_itv := List.hd (List.assoc "resource_id" !Hierarchy.hierarchy_levels) ; let (queue,now) = argv in let security_time_overhead = Int64.of_string (Conf.get_default_value "SCHEDULER_JOB_SECURITY_TIME" "60") in (* int no for ? *) let conn = let r = Iolib.connect () in at_exit (fun () -> Iolib.disconnect r); r in let h_slots = Hashtbl.create 10 in (* Hashtbl.add h_slots 0 [slot_init]; *) let (resource_intervals,slots_init_available_upto_resources) = resources_init_slots_determination conn now in Hashtbl.add h_slots 0 slots_init_available_upto_resources; let (waiting_j_ids,h_waiting_jobs) = if fairsharing_flag then let limited_job_ids = Iolib.get_limited_by_user_job_ids_to_schedule conn queue fairsharing_nb_job_limit in Iolib.get_job_list_fairsharing conn resource_intervals queue besteffort_duration security_time_overhead fairsharing_flag limited_job_ids else Iolib.get_job_list_fairsharing conn resource_intervals queue besteffort_duration security_time_overhead fairsharing_flag [] in (* TODOfalse -> alive_resource_intervals, must be also filter by type-default !!! Are-you sure ??? *) Conf.log ("job waiting ids: "^ (Helpers.concatene_sep "," string_of_int waiting_j_ids)); if (List.length waiting_j_ids) > 0 then (* Jobs to schedule ?*) begin (* get types attributs of wating jobs *) ignore (Iolib.get_job_types conn waiting_j_ids h_waiting_jobs); (* fill slots with prev scheduled jobs *) let prev_scheduled_jobs = Iolib.get_scheduled_jobs conn [] security_time_overhead now in (* TODO available_suspended_res_itvs *) if not ( prev_scheduled_jobs = []) then let (h_prev_scheduled_jobs_types, prev_scheduled_job_ids_tmp) = Iolib.get_job_types_hash_ids conn prev_scheduled_jobs in let prev_scheduled_job_ids = if queue != "besteffort" then (* exclude besteffort jobs *) let besteffort_mem_remove job_id = let test_bt = List.mem_assoc "besteffort" ( try Hashtbl.find h_prev_scheduled_jobs_types job_id with Not_found -> failwith "Must no failed here: besteffort_mem").types in if test_bt then Hashtbl.remove h_prev_scheduled_jobs_types job_id else (); test_bt in List.filter (fun n -> not (besteffort_mem_remove n)) prev_scheduled_job_ids_tmp (* Conf.log ("Previous Scheduled jobs no besteffort:\n"^ (Helpers.concatene_sep "\n\n" job_to_string prev_scheduled_jobs_no_bt) ); *) else prev_scheduled_job_ids_tmp in (* display previous scheduled jobs Hashtbl.iter (fun k v -> printf "prev job: %s, %s\n" k ) h_prev_scheduled_jobs_types; *) (* Conf.log ("length h_slots:"^(string_of_int (Hashtbl.length h_slots))); *) (* let slots_with_scheduled_jobs = try Hashtbl.find h_slots 0 with Not_found -> failwith "Can't slots #0" in Conf.log ("slots_with_scheduled_jobs_before #0:\n " ^ (Helpers.concatene_sep "\n " slot_to_string slots_with_scheduled_jobs)); Conf.log ("length h_prev_scheduled_jobs_types:"^(string_of_int (Hashtbl.length h_prev_scheduled_jobs_types))); *) set_slots_with_prev_scheduled_jobs h_slots h_prev_scheduled_jobs_types prev_scheduled_job_ids security_time_overhead; (* let slots_with_scheduled_jobs = try Hashtbl.find h_slots 0 with Not_found -> failwith "Can't slots #0" in Conf.log ("slots_with_scheduled_jobs after #0:\n " ^ (Helpers.concatene_sep "\n " slot_to_string slots_with_scheduled_jobs)); *) else (); Conf.log "go to make a schedule"; (* get jobs' dependencies information *) let h_jobs_dependencies = Iolib.get_current_jobs_dependencies conn in let h_req_jobs_status = Iolib.get_current_jobs_required_status conn in let ordered_waiting_j_ids = if fairsharing_flag then (* ordering jobs indexes accordingly to fairsharing functions *) jobs_karma_sorting conn queue now karma_window_size waiting_j_ids h_waiting_jobs else waiting_j_ids in (* now compute an assignement for waiting jobs - MAKE A SCHEDULE *) let (assignement_jobs, noscheduled_jids) = schedule_id_jobs_ct_dep h_slots h_waiting_jobs h_jobs_dependencies h_req_jobs_status ordered_waiting_j_ids security_time_overhead in Conf.log ((Printf.sprintf "Queue: %s, Now: %s" queue (Int64.to_string now))); (*Conf.log ("slot_init:\n " ^ slot_to_string slot_init);*) (* let slots_with_scheduled_jobs = try Hashtbl.find h_slots 0 with Not_found -> failwith "Can't slots #0" in Conf.log ("slots_with_scheduled_jobs #0:\n " ^ (Helpers.concatene_sep "\n " slot_to_string slots_with_scheduled_jobs));*) (* Conf.log ("Resources found:\n " ^ (Helpers.concatene_sep "\n " resource_to_string resources) ); *) (* Conf.log ("Waiting jobs:\n"^ (Helpers.concatene_sep "\n " job_waiting_to_string waiting_jobs) ); *) (* Conf.log ("Previous Scheduled jobs:\n"^ (Helpers.concatene_sep "\n\n" job_to_string prev_scheduled_jobs) ); Conf.log ("Assigns:\n" ^ (Helpers.concatene_sep "\n\n" job_to_string assignement_jobs)); *) Conf.log ("Ids of noscheduled jobs:" ^ (Helpers.concatene_sep "," (fun n-> Printf.sprintf "%d" n) noscheduled_jids) ); (* save assignements into db *) Iolib.save_assigns conn assignement_jobs; Conf.log "Terminated"; exit 0 end else Conf.log "No jobs to schedule, terminated"; exit 0 with e -> let error_message = Printexc.to_string e in Conf.error error_message;; ./oar-2.5.2/sources/extra/ocaml-schedulers/simple_cbf_mb_h_ct_oar/simple_cbf_mb_h_ct.ml0000644000175000017500000004617711757171206027367 0ustar plbplb(* *) (* A of Conservative Backfilling scheduler with some additional features *) (* *) (* Features: *) (* - conservative backfilling :-) *) (* - resource matching*) (* - insertion of previously scheduled jobs *) (* - multiple resource requests *) (* NOT TESTED *) (* - multiple resource types *) (* NOT TESTED *) (* - job container *) (* NOT TESTED *) (* - dependencies *) (* NOT TESTED *) (* - security_time_overhead *) (* *) (* Not supported features: *) (* - moldable jobs (use only first request set*) (* - timesharing *) (* - job array *) (* - fairesharing *) (* - suspend/resume, desktop compting feature do we need do address them in main scheduler ??? *) (* - no errors catching/namming *) (* - ordering in resources selection *) (* TODO need can't scheduled job -> to error state ??? *) open Int64 open Interval open Types open Hierarchy type slot = { time_s : time_t; time_e : time_t; set_of_res : set_of_resources; } let slot_to_string slot = let itv2str itv = Printf.sprintf "[%d,%d]" itv.b itv.e in (Printf.sprintf "slot: time_s %s, time_e: %s itv:={ " (to_string slot.time_s) (to_string slot.time_e) ) ^ (String.concat ", " (List.map itv2str slot.set_of_res)) ^ "}\n" let slot_max nb_res = {time_s = zero; time_e = max_int; set_of_res = [{b = 1; e = nb_res}]} (*******************************************************) (* find_first_contiguous_slots_time *) (* where job can fit in time *) (* provides contiguous_slots which fit job_walltime and retrieve slots list *) let find_contiguous_slots_time slot_l job = (* take into account time_b *) let rec find_ctg_slots slots ctg_slots prev_slots = match slots with | s::n when (s.time_e >= (add (add job.time_b job.walltime) minus_one)) -> (ctg_slots @ [s], prev_slots , n) | s::n when ((add s.time_e one) <> (List.hd n).time_s) -> job.time_b <- (List.hd n).time_s; find_ctg_slots n [] (prev_slots @ ctg_slots @ [s]) | s::n -> find_ctg_slots n (ctg_slots @ [s]) prev_slots | _ -> failwith "Not contiguous job is too long (BUG??)"; in let next_slot_time_s = (List.hd slot_l).time_s in if job.time_b < next_slot_time_s then job.time_b <- next_slot_time_s; find_ctg_slots slot_l [] [];; (* No exclusive hierarchy assignement *) let find_resource_hierarchies_job itv_slot job = let rec requests_iter result hys r_rqts cts = match (hys, r_rqts, cts) with | ([],[],[]) -> List.flatten (List.rev result) (* TODO to optimze ??? *) | (x::n,y::m,z::o) -> begin let h = List.map (fun k -> try List.assoc k !hierarchy_levels with Not_found -> failwith ("Can't find coresponding hierarchy level: "^k)) x in let itv_cts_slot = inter_intervals itv_slot z in let sub_result = find_resource_hierarchies itv_cts_slot h y in match sub_result with | [] -> [] | res -> requests_iter (res::result) n m o end | (_,_,_) -> failwith "Not possible to be here" in requests_iter [] job.hy_level_rqt job.hy_nb_rqt job.constraints;; let inter_slots slots = let rec iter_slots sls itv = match sls with | x::n -> let itv1 = inter_intervals itv x.set_of_res in iter_slots n itv1 | [] -> itv in iter_slots (List.tl slots) (List.hd slots).set_of_res;; (* find_first_suitable_contiguous_slots for job *) let find_first_suitable_contiguous_slots slots j = let rec find_suitable_contiguous_slots slot_l pre_slots job = let (next_ctg_time_slot, prev_slots, remain_slots) = find_contiguous_slots_time slot_l job in let itv_inter_slots = inter_slots next_ctg_time_slot in let itv_res_assignement = find_resource_hierarchies_job itv_inter_slots job in match itv_res_assignement with | [] -> find_suitable_contiguous_slots (List.tl next_ctg_time_slot @ remain_slots) (pre_slots @ prev_slots @ [List.hd next_ctg_time_slot]) job | itv -> (itv, next_ctg_time_slot, (pre_slots @ prev_slots), remain_slots) in find_suitable_contiguous_slots slots [] j ;; (*******************************************************) (* split slot accordingly with job resource assignment *) (* new slot A + B + C (A, B and C can be null) *) (* ------ |A|B|C| |A|J|C| |A|B|C| ------ *) (* TODO ??? *) (* generate A slot *) (* slot before job's begin *) let slot_before_job_begin slot job = { time_s = slot.time_s; time_e = add job.time_b minus_one; (* nb_free_res = slot.nb_free_res; TOREMOVE*) set_of_res = slot.set_of_res; };; (* generate B slot *) let slot_during_job slot job = { time_s = max job.time_b slot.time_s; time_e = min (add (add job.time_b job.walltime) minus_one) slot.time_e ; set_of_res = sub_intervals slot.set_of_res job.set_of_rs; } ;; (* generate C slot *) (* slot after job's end *) let slot_after_job_end slot job = { time_s = add job.time_b job.walltime ; time_e = slot.time_e ; set_of_res = slot.set_of_res; };; let split_slots slots job = let split_slot slt = if job.time_b > slt.time_s then (* AAA *) if (add (add job.time_b job.walltime) minus_one) > slt.time_e then (* if slt.nb_free_res > job.nb_res then TOREMOVE *) (* A+B *) (slot_before_job_begin slt job) :: [(slot_during_job slt job)] (* TOREMOVE else (* A *) [slot_before_job_begin slt job] *) else (* TOREMOVE if slt.nb_free_res > job.nb_res then *) (* A+B+C *) (slot_before_job_begin slt job) :: (slot_during_job slt job) :: [(slot_after_job_end slt job)] (* TOREMOVE else (slot_before_job_begin slt job) :: [(slot_after_job_end slt job)] *) else if (add (add job.time_b job.walltime) minus_one) >= slt.time_e then (* TOREMOVE if slt.nb_free_res > job.nb_res then *) (* B *) [slot_during_job slt job] (* TOREMOVE else [] *) else (* TOREMOVE if slt.nb_free_res > job.nb_res then *) (* B+C *) ( slot_during_job slt job) :: [(slot_after_job_end slt job )] (* TOREMOVE else [slot_after_job_end slt job] *) in List.flatten (List.map (fun slot -> split_slot slot ) slots) ;; let resources_assign_job nb_res itv_l = let rec res_assign_job r itv_l res_itv_l = match itv_l with | x::n -> if (x.e-x.b+1) >= r then List.rev ({b = x.b; e = x.b + r-1}::res_itv_l) else res_assign_job (r -x.e + x.b - 1) n (x::res_itv_l) | _ -> failwith "Not enougth resources (BUG!)" in res_assign_job nb_res itv_l [];; let assign_resources_job_split_slots job slots = let (resource_assigned, ctg_slots, prev_slots, remain_slots) = find_first_suitable_contiguous_slots slots job in job.set_of_rs <- resource_assigned; (job, prev_slots @ (split_slots ctg_slots job) @ remain_slots);; (* previous schedule function, it's not use for ct support *) let schedule_jobs jobs slots = let rec assign_res_jobs jobs scheduled_jobs slot_list = match jobs with | [] -> List.rev scheduled_jobs | j::n -> let (job, updated_slots ) = assign_resources_job_split_slots j slot_list in assign_res_jobs n (job::scheduled_jobs) updated_slots in assign_res_jobs jobs [] slots;; (* is it use ? *) let schedule_id_jobs jids h_jobs slots = let rec assign_res_jobs j_ids scheduled_jobs slot_list = match j_ids with | [] -> List.rev scheduled_jobs | j_id::n -> let j = try Hashtbl.find h_jobs j_id with Not_found -> failwith "Can't Hashtbl.find job" in (* Printf.printf "Job:\n%s\n" (job_to_string j); *) let (job, updated_slots ) = assign_resources_job_split_slots j slot_list in assign_res_jobs n (job::scheduled_jobs) updated_slots in assign_res_jobs jids [] slots;; (* *) (* Schedule loop with support for jobs container - can be recursive (recursivity has not be tested) *) (* *) (* let schedule_id_jobs_ct jids h_jobs h_slots = *) let schedule_id_jobs_ct h_slots h_jobs jids security_time_overhead = let find_slots s_id = try Hashtbl.find h_slots s_id with Not_found -> failwith "Can't Hashtbl.find slots (schedule_id_jobs_ct)" in let find_job j_id = try Hashtbl.find h_jobs j_id with Not_found -> failwith "Can't Hashtbl.find job (schedule_id_jobs_ct)" in let test_type job job_type = try (true, (List.assoc job_type job.types)) with Not_found -> (false,"") in let rec assign_res_jobs j_ids scheduled_jobs = match j_ids with | [] -> List.rev scheduled_jobs | jid::n -> let j = find_job jid in let (test_inner, value_in) = test_type j "inner" in let num_set_slots = if test_inner then (int_of_string value_in) else 0 in (* let num_set_slots = if test_inner then (try int_of_string value with _ -> 0) else 0 in *)(* job_error *) begin let (test_container, value) = test_type j "container" in let (job, updated_slots ) = assign_resources_job_split_slots j (find_slots num_set_slots) in Hashtbl.replace h_slots num_set_slots updated_slots; if test_container then (* create new slot / container *) (* substract j.walltime security_time_overhead *) Hashtbl.add h_slots jid [{ time_s = job.time_b; time_e = add job.time_b (sub job.walltime security_time_overhead) ; set_of_res = job.set_of_rs}]; assign_res_jobs n (job::scheduled_jobs) end in assign_res_jobs jids [] (* *) (* Schedule loop with support for jobs container - can be recursive (recursivity has not be tested) *) (* plus dependencies support *) (* let schedule_id_jobs_ct jids h_jobs h_slots = *) let schedule_id_jobs_ct_dep h_slots h_jobs h_jobs_dependencies h_req_jobs_status jids security_time_overhead = let find_slots s_id = try Hashtbl.find h_slots s_id with Not_found -> failwith "Can't Hashtbl.find slots (schedule_id_jobs_ct)" in let find_job j_id = try Hashtbl.find h_jobs j_id with Not_found -> failwith "Can't Hashtbl.find job (schedule_id_jobs_ct)" in let test_type job job_type = try (true, (List.assoc job_type job.types)) with Not_found -> (false,"") in (* dependencies evaluation *) let test_no_dep jid = try (false, (Hashtbl.find h_jobs_dependencies jid)) with Not_found -> (true,[]) in (* let test_job_scheduled = try (Hashtbl.find h_jobs j) with Not_found -> failwith "Can't Hashtbl.find h_jobs (test_job_scheduled )" in *) let dependencies_evaluation j_id job_init = (* are there denpendencies*) let (tst_no_dep, deps) = test_no_dep j_id in if tst_no_dep then (false, job_init) (* don't skip, no dep *) else let rec jobs_required_iter dependencies = match dependencies with | [] -> (false, job_init) | jr_id::n -> let jrs = try (Hashtbl.find h_req_jobs_status jr_id) with Not_found -> failwith "Can't Hashtbl.find jr in h_req_jobs_status" in if (jrs.jr_state != "Terminated") then let jsched = find_job jr_id in (* test is job scheduled*) if (jsched.set_of_rs != []) then begin if (add jsched.time_b jsched.walltime) > job_init.time_b then job_init.time_b <- (add jsched.time_b jsched.walltime); jobs_required_iter n end else (* job message: "Cannot determine scheduling time due to dependency with the job $d"; *) (* oar_debug("[oar_sched_gantt_with_timesharing] [$j->{job_id}] $message\n"); *) (true, job_init) (* skip *) else (* job is Terminated *) if (jrs.jr_jtype = "PASSIVE") && (jrs.jr_exit_code != 0) then (* my $message = "Cannot determine scheduling time due to dependency with the job $d (exit code != 0)"; OAR::IO::set_job_message($base,$j->{job_id},$message); OAR::IO::set_job_scheduler_info($base,$j->{job_id},$message); oar_debug("[oar_sched_gantt_with_timesharing] [$j->{job_id}] $message\n"); *) (true, job_init) (* skip *) else jobs_required_iter n in jobs_required_iter deps in (* assign ressource for all waiting jobs *) let rec assign_res_jobs j_ids scheduled_jobs nosched_jids = match j_ids with | [] -> (List.rev scheduled_jobs, List.rev nosched_jids) | jid::n -> let j_init = find_job jid in let (test_skip, j) = dependencies_evaluation jid j_init in let (test_inner, value_in) = test_type j "inner" in let num_set_slots = if test_inner then (int_of_string value_in) else 0 in (* let num_set_slots = if test_inner then (try int_of_string value with _ -> 0) else 0 in *)(* job_error *) begin let (test_container, value) = test_type j "container" in let current_slots = find_slots num_set_slots in let (ok, ns_jids, (job, updated_slots) ) = try (true, nosched_jids, assign_resources_job_split_slots j current_slots) with _ -> (false, (jid::nosched_jids), (j_init, current_slots)) in if ok then begin Hashtbl.replace h_slots num_set_slots updated_slots; if test_container then (* create new slot / container *) (* substract j.walltime security_time_overhead *) Hashtbl.add h_slots jid [{ time_s = job.time_b; time_e = add job.time_b (sub job.walltime security_time_overhead); set_of_res=job.set_of_rs}]; (* replace updated/assgined job in job hashtable *) Hashtbl.replace h_jobs jid job; assign_res_jobs n (job::scheduled_jobs) ns_jids end else assign_res_jobs n scheduled_jobs ns_jids end in assign_res_jobs jids [] [] (* function insert previously occupied slots in slots *) (* job must be sorted by start_time *) let split_slots_prev_scheduled_jobs slots jobs = let rec find_first_slot left_slots right_slots job = match right_slots with | x::n when ((x.time_s > job.time_b) || ((x.time_s <= job.time_b) && (job.time_b <= x.time_e))) -> (left_slots,x,n) | x::n -> find_first_slot (left_slots @ [x]) n job | [] -> failwith "Argl cannot failed here" in let rec find_slots_aux encompass_slots r_slots job = match r_slots with (* find timed slots *) | x::n when (x.time_e > (add job.time_b job.walltime)) -> (encompass_slots @ [x],n) | x::n -> find_slots_aux (encompass_slots @ [x]) n job | [] -> failwith "Argl cannot failed here" in let find_slots_encompass first_slot right_slots job = if (first_slot.time_e > (add job.time_b job.walltime)) then ([first_slot],right_slots) else find_slots_aux [first_slot] right_slots job in let rec split_slots_next_job prev_slots remain_slots job_l = match job_l with | [] -> prev_slots @ remain_slots | x::n -> let (l_slots, first_slot, r_slots) = find_first_slot prev_slots remain_slots x in let (encompass_slots, ri_slots) = find_slots_encompass first_slot r_slots x in let splitted_slots = split_slots encompass_slots x in split_slots_next_job l_slots (splitted_slots @ ri_slots) n in split_slots_next_job [] slots jobs (* function insert previously one scheduled job in slots *) (* job must be sorted by start_time *) let split_slots_prev_scheduled_one_job slots job = let rec find_first_slot left_slots right_slots job = match right_slots with | x::n when ((x.time_s > job.time_b) || ((x.time_s <= job.time_b) && (job.time_b <= x.time_e))) -> (left_slots,x,n) | x::n -> find_first_slot (left_slots @ [x]) n job | [] -> failwith "Argl cannot failed here" in let rec find_slots_aux encompass_slots r_slots job = match r_slots with (* find timed slots *) | x::n when (x.time_e > (add job.time_b job.walltime)) -> (encompass_slots @ [x],n) | x::n -> find_slots_aux (encompass_slots @ [x]) n job | [] -> failwith "Argl cannot failed here" in let find_slots_encompass first_slot right_slots job = if (first_slot.time_e > (add job.time_b job.walltime)) then ([first_slot],right_slots) else find_slots_aux [first_slot] right_slots job in let (l_slots, first_slot, r_slots) = find_first_slot [] slots job in let (encompass_slots, ri_slots) = find_slots_encompass first_slot r_slots job in let splitted_slots = split_slots encompass_slots job in splitted_slots @ ri_slots (* function insert previously scheduled job in slots with containers consideration *) (* job must be sorted by start_time *) (* loop across ordered jobs' id by start_time and create new set_slots or split slots when needed *) let set_slots_with_prev_scheduled_jobs h_slots h_jobs ordered_id_jobs security_time_overhead = let find_slots s_id = try Hashtbl.find h_slots s_id with Not_found -> failwith "Can't Hashtbl.find slots (set_slots_with_prev_scheduled_jobs)" in let find_job j_id = try Hashtbl.find h_jobs j_id with Not_found -> failwith "Can't Hashtbl.find job (set_slots_with_prev_scheduled_jobs)" in let test_type job job_type = try (true, (List.assoc job_type job.types)) with Not_found -> (false,"0") in let rec loop_jobs od_id_jobs = match od_id_jobs with | [] -> () (* terminated *) | jid::m -> let j = find_job jid in let (test_inner, value_in) = test_type j "inner" in let num_set_slots = if test_inner then (int_of_string value_in) else 0 in begin let (test_container, value) = test_type j "container" in if test_container then (* create new slot / container *) (* substract j.walltime security_time_overhead *) Hashtbl.add h_slots jid [{ time_s = j.time_b; time_e = add j.time_b (sub j.walltime security_time_overhead); set_of_res = j.set_of_rs}]; (* TODO perhaps we'll need to optimize split_slots_prev_scheduled_jobs...made for jobs list *) Hashtbl.replace h_slots num_set_slots (split_slots_prev_scheduled_one_job (find_slots num_set_slots) j); loop_jobs m end in loop_jobs ordered_id_jobs ./oar-2.5.2/sources/extra/ocaml-schedulers/simple_cbf_mb_h_ct_oar/postgresql_driver.ml0000644000175000017500000000251311757171206027351 0ustar plbplbopen Postgresql open Conf type q_res = { result: Postgresql.result; ntuples: int; mutable nt: int; } let connect () = let user = get_optional_value "DB_BASE_LOGIN" and name = get_optional_value "DB_BASE_NAME" and host = get_optional_value "DB_HOSTNAME" and passwd = get_optional_value "DB_BASE_PASSWD" in let o = function None -> "" | Some s -> s in log ( Printf.sprintf "Connecting as %s@%s on %s.\n" (o user) (o host) (o name)); let str_connection = Printf.sprintf "host=%s user=%s password=%s dbname=%s" (o host) (o user) (o passwd) (o name) in (* let str_connection = Some "host=" in *) new connection ~conninfo:str_connection ();; let disconnect dbh = dbh#finish;; let execQuery (dbh:Postgresql.connection) q = (* TODO must be completed *) let res = dbh#exec q in { result = res; ntuples = res#ntuples; nt = 0; } (* TODO test status see dbi_postgresql.ml in ocamldbi *) ;; (* let map (q_result:Postgresql.result) f = *) let map (qres) f = let rec loop i result_lst = match i with | -1 -> result_lst | _ -> loop (i-1) ((f (qres.result#get_tuple i))::result_lst) in loop (qres.ntuples-1) [] ;; let fetch qres = if (qres.nt == qres.ntuples) then None else let r = qres.result#get_tuple qres.nt in qres.nt <- qres.nt + 1; Some r;; ./oar-2.5.2/sources/extra/ocaml-schedulers/simple_cbf_mb_h_ct_oar/mysql_driver.ml0000644000175000017500000000211611757171206026312 0ustar plbplbopen Mysql open Conf let connect () = (* let conn = Conf.get_conn () in *) let user = get_optional_value "DB_BASE_LOGIN" and name = get_optional_value "DB_BASE_NAME" and host = get_optional_value "DB_HOSTNAME" in let conn = { dbuser = user; dbpwd = get_optional_value "DB_BASE_PASSWD"; dbname = name; dbhost = host; dbport = None; } in try log (let o = function None -> "default" | Some s -> s in Printf.sprintf "Connecting as %s@%s on %s.\n" (o user) (o host) (o name)); connect conn with e -> ( Conf.error ("[MoldSched]: [Iolib] Connection Failed : "^(Printexc.to_string e)^"\n")) let disconnect = Mysql.disconnect let execQuery db q = (* log (Printf.sprintf "[SQL] execQuery --%s--" q); *) let r = exec db q in match errmsg db with None -> r | Some s -> ignore (Conf.error ("[Iolib] : "^s)); ignore (Conf.error (" *** Query was:\n"^q)); failwith "execQuery" let map = Mysql.map let fetch = Mysql.fetch let not_null = Mysql.not_null let str2ml = Mysql.str2ml ./oar-2.5.2/sources/extra/ocaml-schedulers/simple_cbf_mb_h_ct_oar/iolib.ml0000644000175000017500000007617311757171206024706 0ustar plbplb(*pp cpp -P -w *) (* previous line is to ask OcamlMakefile to preprocess this file with cpp preprocesseur *) (* Postgresql very sensitive ? "type = \"default\"" "type = 'default'" *) (* preprocessor part *) #ifdef POSTGRESQL #define DBD Postgresql_driver #define NoN #define NoNStr #else #define DBD Mysql_driver #define NoN not_null #define NoNStr not_null str2ml #endif open Int64 open DBD open Types open Interval open Helpers let connect () = DBD.connect ();; let disconnect dbh = DBD.disconnect dbh;; let get_resource_list dbh = let query = "SELECT resource_id, network_address, state, available_upto FROM resources" in let res = execQuery dbh query in let get_one_resource a = { resource_id = NoN int_of_string a.(0); (* resource_id *) network_address = NoNStr a.(1); (* network_address *) state = NoN rstate_of_string a.(2); (* state *) available_upto = NoN Int64.of_string a.(3) ;} (* available_upto *) in map res get_one_resource ;; (* *) (* get distinct availableupto *) (* to remove, can be obtain from resources list *) (* *) let get_group_available_uptos dbh = let query = "SELECT available_upto FROM resources GROUP BY available_upto" in let res = execQuery dbh query in let get_one a = NoN Int64.of_string a.(0) (* available_upto *) in map res get_one;; (* get the amount of time in the suspended state of a job args : base, job id, time in seconds adapted from OAR::IO::get_job_suspended_sum_duration *) let get_job_suspended_sum_duration dbh job_id now = let query = Printf.sprintf "SELECT date_start, date_stop FROM job_state_logs WHERE job_id = %d AND (job_state = 'Suspended' OR job_state = 'Resuming')" job_id in let res = execQuery dbh query in let rec summation sum next_rest = match next_rest with | None -> sum | Some a -> let date_start = NoN Int64.of_string a.(0) and date_stop = NoN Int64.of_string a.(1) in let res_time = if (date_stop = 0L) then sub now date_start else sub date_stop date_start in if (res_time > 0L) then add sum res_time else sum in summation 0L (fetch res);; (* *) (* get_job_list_fairsharing: retrieve jobs to schedule with important relative information *) (* *) let get_job_list_fairsharing dbh default_resources queue besteffort_duration security_time_overhead fairsharing_flag fs_jobids = let flag_besteffort = if (queue == "besteffort") then true else false in let jobs = Hashtbl.create 1000 in (* Hashtbl.add jobs jid ( blabla *) let constraints = Hashtbl.create 10 in (* Hashtable of constraints to avoid recomputing of corresponding interval list*) let get_constraints j_ppt r_ppt = if (j_ppt = "") && ( r_ppt = "type = 'default'" || r_ppt = "" ) then default_resources else let and_sql = if ((j_ppt = "") || (r_ppt = "")) then "" else " AND " in let sql_cts = j_ppt ^ and_sql^ r_ppt in try Hashtbl.find constraints sql_cts with Not_found -> begin let query = Printf.sprintf "SELECT resource_id FROM resources WHERE state = 'Alive' AND ( %s )" sql_cts in let res = execQuery dbh query in let get_one_resource a = NoN int_of_string a.(0) (* resource_id *) in let matching_resources = (map res get_one_resource) in let itv_cts = ints2intervals matching_resources in Hashtbl.add constraints sql_cts itv_cts; itv_cts end in let query_base = Printf.sprintf " SELECT jobs.job_id, moldable_job_descriptions.moldable_walltime, jobs.properties, moldable_job_descriptions.moldable_id, job_resource_descriptions.res_job_resource_type, job_resource_descriptions.res_job_value, job_resource_descriptions.res_job_order, job_resource_groups.res_group_property, jobs.job_user, jobs.project FROM moldable_job_descriptions, job_resource_groups, job_resource_descriptions, jobs WHERE moldable_job_descriptions.moldable_index = 'CURRENT' AND job_resource_groups.res_group_index = 'CURRENT' AND job_resource_descriptions.res_job_index = 'CURRENT' " and query_end = " AND jobs.reservation = 'None' AND jobs.job_id = moldable_job_descriptions.moldable_job_id AND job_resource_groups.res_group_index = 'CURRENT' AND job_resource_groups.res_group_moldable_id = moldable_job_descriptions.moldable_id AND job_resource_descriptions.res_job_index = 'CURRENT' AND job_resource_descriptions.res_job_group_id = job_resource_groups.res_group_id ORDER BY moldable_job_descriptions.moldable_id, job_resource_groups.res_group_id, job_resource_descriptions.res_job_order ASC;" in let query = if fairsharing_flag then query_base ^ " AND jobs.job_id IN (" ^ (Helpers.concatene_sep "," id fs_jobids) ^ ") " ^ query_end else query_base ^ " AND jobs.state = 'Waiting' AND jobs.queue_name = '" ^ queue ^"' "^ query_end in let res = execQuery dbh query in let get_one_row a = ( NoN int_of_string a.(0), (* job_id *) (if flag_besteffort then besteffort_duration else NoN Int64.of_string a.(1)), (* moldable_walltime *) NoN int_of_string a.(3), (* moldable_id *) NoNStr a.(2), (* properties *) NoNStr a.(4), (* res_job_resource_type *) NoN int_of_string a.(5), (* res_job_value *) NoN int_of_string a.(6), (* res_job_order *) NoNStr a.(7), (* res_group_property *) NoNStr a.(8), (* job_user *) NoNStr a.(9) (* job_project *) ) in let result = map res get_one_row in let rec scan_res res_query prev_job r_o r_t r_v cts jids = match res_query with [] -> begin (* complete previous job *) prev_job.hy_level_rqt <- r_t; prev_job.hy_nb_rqt <- r_v; prev_job.constraints <- cts; (* add job to hashtable *) Hashtbl.add jobs prev_job.jobid prev_job; (List.rev jids, jobs) (* return list of job_ids jobs' hashtable *) end | row::m -> let (j_id,j_walltime, j_moldable_id, properties, r_type, r_value, r_order, r_properties, user, project) = row in if (prev_job.jobid != j_id) then (* next job *) begin (* complete prev job *) if (prev_job.jobid !=0) then begin prev_job.hy_level_rqt <- List.rev r_t; prev_job.hy_nb_rqt <- List.rev r_v; prev_job.constraints <- List.rev cts; (* Printf.printf "jobs: %s\n" (job_to_string prev_job); *) Hashtbl.add jobs prev_job.jobid prev_job end; (* prepare next job *) let j = { jobid = j_id; jobstate = ""; moldable_id = j_moldable_id; time_b = Int64.zero; walltime = add j_walltime security_time_overhead; (* add security_time_overhead *) types = []; constraints = []; hy_level_rqt = []; hy_nb_rqt = []; set_of_rs = []; user = user; project = project; } in scan_res m j r_order [[r_type]] [[r_value]] [(get_constraints properties r_properties)] (j_id::jids) end else begin (* same job *) if r_order = 0 then (*new resource request*) scan_res m prev_job r_order ([r_type]::r_t) ([r_value]::r_v) ((get_constraints properties r_properties)::cts) jids else (*one hierarchy requirement to resource request*) scan_res m prev_job r_order (((List.hd r_t) @ [r_type])::(List.tl r_t)) (((List.hd r_v) @ [r_value])::(List.tl r_v)) cts jids end in scan_res result {jobid=0;jobstate="";moldable_id =0;time_b=Int64.zero;walltime=Int64.zero; (* the job is used *) types=[];constraints=[];hy_level_rqt=[];hy_nb_rqt=[]; set_of_rs =[]; user="";project=""} 0 [] [] [] [];; (* *) (* get_scheduled_jobs_no_suspend : retrieve already previously scheduled jobs *) (* OAR::IO::get_gantt_scheduled_jobs in perl version *) (* without suspend jobs support *) let get_scheduled_jobs_no_suspend dbh security_time_overhead = let query = "SELECT j.job_id, g2.start_time, m.moldable_walltime, g1.resource_id, j.queue_name, j.state, j.job_user, j.job_name,m.moldable_id,j.suspended, j.project FROM gantt_jobs_resources g1, gantt_jobs_predictions g2, moldable_job_descriptions m, jobs j WHERE m.moldable_index = 'CURRENT' AND g1.moldable_job_id = g2.moldable_job_id AND m.moldable_id = g2.moldable_job_id AND j.job_id = m.moldable_job_id ORDER BY j.start_time, j.job_id;" in let res = execQuery dbh query in let first_res = function | None -> [] | Some first_job -> (* if not (first_res = None) then *) let newjob_res a = (* function | None -> failwith "pas glop" | Some job_res -> *) let j_id = NoN int_of_string a.(0) (* job_id *) and j_state = NoNStr a.(5) (* job state *) and j_walltime = NoN Int64.of_string a.(2) (* moldable_walltime *) and j_moldable_id = NoN int_of_string a.(8) (* moldable_id *) and j_start_time = NoN Int64.of_string a.(1) (* start_time *) and j_nb_res = NoN int_of_string a.(3) (* resource_id *) and j_user = NoNStr a.(4) (* job_user *) and j_project = NoNStr a.(9) in (* project *) ( { jobid = j_id; jobstate = j_state; moldable_id = j_moldable_id; time_b = j_start_time; walltime = add j_walltime security_time_overhead; (* add security_time_overhead *) types = []; constraints = []; (* constraints irrelevant fortest_container already scheduled job *) hy_level_rqt = []; (* *) hy_nb_rqt = []; (* *) set_of_rs = []; (* will be set when all resource_id are fetched *) user = j_user; project = j_project; }, [j_nb_res]) in let get_job_res a = let j_id = NoN int_of_string a.(0) (* job_id *) and j_nb_res = NoN int_of_string a.(3) in (*resource_id *) (j_id, j_nb_res) in let rec aux result job_l current_job_res = match result with | None -> let job = fst current_job_res in job.set_of_rs <- ints2intervals (snd current_job_res); List.rev (job::job_l) | Some x -> let j_r = get_job_res x in let j_current = fst current_job_res in if ((fst j_r) = j_current.jobid) then begin aux (fetch res) job_l (j_current, (snd j_r) :: (snd current_job_res)) end else begin j_current.set_of_rs <- ints2intervals (snd current_job_res); aux (fetch res) (j_current::job_l) (newjob_res x) end in aux (fetch res) [] (newjob_res first_job) in first_res (fetch res) (* *) (* get_scheduled_jobs: retrieve already previously scheduled jobs *) (* OAR::IO::get_gantt_scheduled_jobs in perl version *) (* with suspend jobs support *) (* TODO Remove used field in query ??? *) let get_scheduled_jobs dbh available_suspended_res_itvs security_time_overhead now = let query = "SELECT j.job_id, g2.start_time, m.moldable_walltime, g1.resource_id, j.queue_name, j.state, j.job_user, j.job_name,m.moldable_id,j.suspended, j.project FROM gantt_jobs_resources g1, gantt_jobs_predictions g2, moldable_job_descriptions m, jobs j WHERE m.moldable_index = 'CURRENT' AND g1.moldable_job_id = g2.moldable_job_id AND m.moldable_id = g2.moldable_job_id AND j.job_id = m.moldable_job_id ORDER BY j.start_time, j.job_id;" in (* ORDER BY j.start_time; in *) let res = execQuery dbh query in let first_res = function | None -> [] | Some first_job -> let newjob_res a = let j_id = NoN int_of_string a.(0) (* job_id *) and j_walltime_init = NoN Int64.of_string a.(2) in (* moldable_walltime *) let j_start_time = NoN Int64.of_string a.(1) (* start_time *) and j_walltime = if ( NoNStr a.(9)) == "YES" then (* take into account suspended time *) add j_walltime_init (get_job_suspended_sum_duration dbh j_id now) else j_walltime_init and j_state = NoNStr a.(5) (* job state *) and j_moldable_id = NoN int_of_string a.(8) (* moldable_id *) and j_nb_res = NoN int_of_string a.(3) (*resource_id *) and j_user = NoNStr a.(4) (* job_user *) and j_project = NoNStr a.(9) in (* project *) ( { jobid = j_id; jobstate = j_state; moldable_id = j_moldable_id; time_b = j_start_time; walltime = add j_walltime security_time_overhead; (* add security_time_overhead *) types = []; constraints = []; (* constraints irrelevant fortest_container already scheduled job *) hy_level_rqt = []; (* *) hy_nb_rqt = []; (* *) set_of_rs = []; (* will be set when all resource_id are fetched *) user = j_user; project = j_project; }, [j_nb_res]) in let get_job_res a = let j_id = NoN int_of_string a.(0) (* job_id *) and j_nb_res = NoN int_of_string a.(3) in (* resource_id *) (j_id, j_nb_res) in let job_itv_res_setting state job_res = let set_res = ints2intervals job_res in if (state == "Suspended") then (* TODO Remove resources of the type specified in SCHEDULER_AVAILABLE_SUSPENDED_RESOURCE_TYPE *) inter_intervals set_res available_suspended_res_itvs else set_res in let rec aux result job_l current_job_res = match result with | None -> let job = fst current_job_res in job.set_of_rs <- job_itv_res_setting job.jobstate (snd current_job_res); (* TODO VERIFY-TEST_overlapping_bug List.rev (job::job_l) *) job::job_l | Some x -> let j_r = get_job_res x in let j_current = fst current_job_res in if ((fst j_r) = j_current.jobid) then begin aux (fetch res) job_l (j_current, (snd j_r) :: (snd current_job_res)) end else begin j_current.set_of_rs <- job_itv_res_setting j_current.jobstate (snd current_job_res); aux (fetch res) (j_current::job_l) (newjob_res x) end in aux (fetch res) [] (newjob_res first_job) in first_res (fetch res) (* *) (* Save ONE job assign *) (* *) let save_assignt_one_job dbh job = let moldable_job_id = string_of_int job.moldable_id in let moldable_job_id_start_time j = (* Printf.sprintf "(%s, %s)" moldable_job_id (Int64.to_string j.time_b) in *) "(" ^ moldable_job_id ^ "," ^ (Int64.to_string j.time_b) ^ ")" in let query_pred = "INSERT INTO gantt_jobs_predictions (moldable_job_id,start_time) VALUES "^ (moldable_job_id_start_time job) in (* ignore (execQuery conn query_pred) *) let resource_to_value res_id = (* Printf.sprintf "(%s, %s)" moldable_job_id (ml2int res_id) in *) "(" ^ moldable_job_id ^ "," ^ (string_of_int res_id ) ^ ")" in let query_job_resources = "INSERT INTO gantt_jobs_resources (moldable_job_id,resource_id) VALUES "^ (String.concat ", " (List.map resource_to_value (intervals2ints job.set_of_rs))) in (* Conf.log query_pred; Conf.log query_job_resources; *) ignore (execQuery dbh query_pred); ignore (execQuery dbh query_job_resources) (* *) (* Save jobs' assignemnts *) (* *) let save_assigns dbh jobs = List.iter (fun x -> save_assignt_one_job dbh x) jobs;; (* *) (* Save jobs assignements into 2 SQL request *) (* Be careful this does not scale after beyond 1 millions of (moldable_job_id,start_time) *) (* *) let save_assigns_2_rqts conn jobs = let moldable_job_id_start_time j = (* Printf.sprintf "(%s, %s)" (ml2int j.moldable_id) (ml642int j.time_b) in *) "(" ^ (string_of_int j.moldable_id) ^ "," ^ (Int64.to_string j.time_b) ^ ")" in let query_pred = "INSERT INTO gantt_jobs_predictions (moldable_job_id,start_time) VALUES "^ (String.concat ", " (List.map moldable_job_id_start_time jobs)) in (* ignore (execQuery conn query_pred) *) let job_resource_to_value j = let moldable_id = string_of_int j.moldable_id in let resource_to_value res = (* Printf.sprintf "(%s, %s)" moldable_id (ml2int res) in *) "(" ^ moldable_id ^ "," ^ (string_of_int res) ^ ")" in String.concat ", " (List.map resource_to_value (intervals2ints j.set_of_rs)) in let query_job_resources = "INSERT INTO gantt_jobs_resources (moldable_job_id,resource_id) VALUES "^ (String.concat ",\n " (List.map job_resource_to_value jobs)) in (* Conf.log query_pred; Conf.log query_job_resources; *) ignore (execQuery conn query_pred); ignore (execQuery conn query_job_resources) (* *) (** retrieve job_type for all jobs in the hashtable *) (* *) let get_job_types dbh job_ids h_jobs = let job_ids_str = Helpers.concatene_sep "," string_of_int job_ids in let query = "SELECT job_id, type FROM job_types WHERE types_index = 'CURRENT' AND job_id IN (" ^ job_ids_str ^ ");" in let res = execQuery dbh query in let add_id_types a = let job = try Hashtbl.find h_jobs ( NoN int_of_string a.(0)) (* job_id *) with Not_found -> failwith "get_job_type error can't find job_id" in let jt0 = Helpers.split "=" (NoNStr a.(1)) in (* type *) let jt = if ((List.length jt0) = 1) then (List.hd jt0)::[""] else jt0 in job.types <- ((List.hd jt), (List.nth jt 1))::job.types in ignore (map res add_id_types);; (* *) (* retrieve job_type for all jobs in the hashtable *) (* TODO factorize with get_job_types ?? change simple_cbf**.ml ??? REMOVE ??? *) (* *) let get_job_types_hash_ids dbh jobs = let h_jobs = Hashtbl.create 1000 in let job_ids = List.map (fun n -> Hashtbl.add h_jobs n.jobid n; n.jobid) jobs in let job_ids_str = Helpers.concatene_sep "," string_of_int job_ids in let query = "SELECT job_id, type FROM job_types WHERE types_index = 'CURRENT' AND job_id IN (" ^ job_ids_str ^ ");" in let res = execQuery dbh query in let add_id_types a = let job = try Hashtbl.find h_jobs (NoN int_of_string a.(0)) (* job_id *) with Not_found -> failwith "get_job_type error can't find job_id" in let jt0 = Helpers.split "=" (NoNStr a.(1)) in (* type *) let jt = if ((List.length jt0) = 1) then (List.hd jt0)::[""] else jt0 in job.types <- ((List.hd jt), (List.nth jt 1))::job.types in ignore (map res add_id_types); (* Conf.log ("length h_jobs:"^(string_of_int (Hashtbl.length h_jobs))); *) (h_jobs, job_ids);; (* retrieve jobs dependencies *) (* return an hashtable, key = job_id, value = list of required jobs *) let get_current_jobs_dependencies dbh = let h_jobs_dependencies = Hashtbl.create 100 in let query = "SELECT job_id, job_id_required FROM job_dependencies WHERE job_dependency_index = 'CURRENT'" in let res = execQuery dbh query in let get_one a = let job_id = NoN int_of_string a.(0) in (* job_id *) let job_id_required = NoN int_of_string a.(1) in (* job_id_required *) let dependencies = try Hashtbl.find h_jobs_dependencies job_id with Not_found -> (Hashtbl.add h_jobs_dependencies job_id []; []) in Hashtbl.replace h_jobs_dependencies job_id (job_id_required::dependencies) in ignore (map res get_one); h_jobs_dependencies;; (* *) (* retrieve status of required jobs of jobs with dependencies *) (* return an hashtable, key = job_id, value = list of required jobs_status *) let get_current_jobs_required_status dbh = let h_jobs_required_status = Hashtbl.create 100 in (* TODO to simplify ??? / remove unsed fields *) let query = " SELECT jobs.job_id, jobs.state, jobs.job_type, jobs.exit_code, jobs.start_time, moldable_job_descriptions.moldable_walltime FROM jobs,job_dependencies, moldable_job_descriptions WHERE job_dependencies.job_dependency_index = 'CURRENT' AND jobs.job_id = job_dependencies.job_id_required AND jobs.job_id = moldable_job_descriptions.moldable_job_id GROUP BY jobs.job_id;" in let res = execQuery dbh query in let get_one a = let j_id = NoN int_of_string a.(0) (* job_id *) and j_state = NoNStr a.(1) (* state *) and j_jtype = NoNStr a.(2) (* job_type *) and j_exit_code = NoN int_of_string a.(3) (* exit_code *) (* and j_start_time = not_null int642ml (get "start_time") and j_walltime = not_null int642ml (get "moldable_walltime") *) in (j_id, { (* jr_id = j_id; *) jr_state = j_state; jr_jtype = j_jtype; jr_exit_code = j_exit_code; (* jr_start_time = j_start_time; jr_walltime = j_walltime; *) }) in let results = map res get_one in ignore ( List.iter (fun x -> Hashtbl.add h_jobs_required_status (fst x) (snd x) ) results); h_jobs_required_status;; (* set_job_message sets the message field of the job of id passed in parameter parameters : dbh, job_id, message return value : / side effects : changes the field message of the job in the table Jobs *) let set_job_message dbh job_id message = let query = Printf.sprintf "UPDATE jobs SET message = '%s' WHERE job_id = %d" message job_id in ignore (execQuery dbh query) (* set_job_scheduler_info sets the scheduler_info field of the job of id passed in parameter parameters : dbh, job_id, message return value : / *) let set_job_scheduler_info dbh job_id message = let query = Printf.sprintf "UPDATE jobs SET scheduler_info = '%s' WHERE job_id = %d" message job_id in ignore (execQuery dbh query) let set_job_and_scheduler_message dbh job_id message = let query = Printf.sprintf "UPDATE jobs SET message = '%s', scheduler_info = '%s', WHERE job_id = %d" message message job_id in ignore (execQuery dbh query) let set_job_and_scheduler_message_range dbh job_ids message = let job_ids_str = Helpers.concatene_sep "," string_of_int job_ids in let query = Printf.sprintf "UPDATE jobs SET message = '%s', scheduler_info = '%s', WHERE IN ('%s');" message message job_ids_str in ignore (execQuery dbh query) (* get_job_current_resources returns the list of resources associated to the job passed in parameter parameters : base, jobid return value : list of resources *) let get_job_current_resources dbh job_id no_type_lst = let partial_query = function | None -> Printf.sprintf "FROM assigned_resources WHERE assigned_resources.assigned_resource_index = 'CURRENT' AND assigned_resources.moldable_job_id = %d" job_id | Some x -> let str_t_lst = String.concat "," x in Printf.sprintf "FROM assigned_resources,resources WHERE assigned_resources.assigned_resource_index = 'CURRENT' AND assigned_resources.moldable_job_id = %d AND resources.resource_id = assigned_resources.resource_id AND resources.type NOT IN ('%s')" job_id str_t_lst in let query = "SELECT assigned_resources.resource_id " ^ (partial_query no_type_lst) ^ "ORDER BY assigned_resources.resource_id ASC" in let res = execQuery dbh query in map res (function a -> NoN int_of_string a.(0));; (* *) (* Fairsharing Stuff *) (* *) (* *) (* get_sum_accounting_window *) (* Adapted form OAR::IO::get_sum_accounting_window *) (* return two value karma_sum_time_asked and karma_sum_time_used *) (* *) let get_sum_accounting_window dbh queue start_window stop_window = let query = Printf.sprintf " SELECT consumption_type, SUM(consumption) FROM accounting WHERE queue_name = '%s' AND window_start >= %Lu AND window_start < %Lu GROUP BY consumption_type" queue start_window stop_window in let res = execQuery dbh query in let get_one a = (NoNStr a.(0), NoN float_of_string a.(1)) in let results = map res get_one in let rec scan_results r asked used = match r with | [] -> (asked, used) | x::m -> let extract = function | ("ASKED", a) -> scan_results m a used | ("USED", u) -> scan_results m asked u | (_,_) -> failwith "Consumption type is not supported: " in extract x in scan_results results 1.0 1.0 ;; (* *) (* get_sum_accounting_for_param *) (* Adapted form OAR::IO:: get_sum_accounting_for_param *) (* return ... *) let get_sum_accounting_for_param dbh queue param_name start_window stop_window = let karma_used = Hashtbl.create 1000 and karma_asked = Hashtbl.create 1000 in let query = Printf.sprintf "SELECT %s,consumption_type, SUM(consumption) FROM accounting WHERE queue_name = '%s' AND window_start >= %Lu AND window_start < %Lu GROUP BY %s,consumption_type" param_name queue start_window stop_window param_name in let res = execQuery dbh query in let get_one a = (NoNStr a.(1), NoNStr a.(0), NoN float_of_string a.(2)) in let results = map res get_one in let rec scan_results r = match r with | [] -> (karma_asked, karma_used) | x::m -> let extract = function | ("ASKED", k, v) -> begin Hashtbl.add karma_asked k v; scan_results m end | ("USED", k, v) -> begin Hashtbl.add karma_asked k v; scan_results m end | (_,_,_) -> failwith ("Consumption type is not supported (with param) : " ^ param_name) in extract x in scan_results results ;; (* *) (* get_limited_by_user_job_ids_to_schedule *) (* Adapted form OAR::IO::get_fairsharing_jobs_to_schedule *) (* return job indices with limited number of job by user *) (* *) let get_limited_by_user_job_ids_to_schedule dbh queue limit = (* get a limit number of job ids for a given user *) let get_job_ids_user user = let query = Printf.sprintf " SELECT * FROM jobs WHERE state = 'Waiting' AND reservation = 'None' AND queue_name = '%s' AND job_user = '%s' ORDER BY job_id LIMIT %s;" queue user limit in let res = execQuery dbh query in (* let get_one a = NoNStr a.(0) in *) map res (function a -> NoNStr a.(0) ) in let get_waiting_users = (* get all users with a waiting job *) let query = Printf.sprintf " SELECT distinct(job_user) FROM jobs WHERE state = 'Waiting' AND reservation = 'None' AND queue_name = '%s';" queue in let res = execQuery dbh query in map res (function a -> NoNStr a.(0) ) in List.flatten (List.map get_job_ids_user get_waiting_users) ./oar-2.5.2/sources/extra/ocaml-schedulers/simple_cbf_mb_h_ct_oar/Makefile0000644000175000017500000000261211757171206024701 0ustar plbplb# # By default simple_cbf_mb_h_ct_oar_mysql is built # to obtain simple_cbf_mb_h_ct_oar_postgresql use: # > make POSTGRESQL=true # OCAMLMAKEFILE := ../common/OCamlMakefile # make POSTGRESQL=true ifdef POSTGRESQL PPFLAGS := -DPOSTGRESQL THREADS := true PACKS := postgresql LIBS := str SOURCES := ../common/interval.ml ../common/helpers.ml ../common/conf.ml\ types.ml postgresql_driver.ml iolib.ml \ ../common/hierarchy.ml simple_cbf_mb_h_ct.ml simple_cbf_mb_h_ct_oar.ml RESULT := simple_cbf_mb_h_ct_oar_postgresql else PACKS := mysql oUnit LIBS := str SOURCES := ../common/interval.ml ../common/helpers.ml ../common/conf.ml\ types.ml mysql_driver.ml iolib.ml \ ../common/hierarchy.ml simple_cbf_mb_h_ct.ml simple_cbf_mb_h_ct_oar.ml RESULT := simple_cbf_mb_h_ct_oar_mysql endif define PROJ_test_unit PACKS = oUnit LIBS = str SOURCES = ../common/interval.ml ../common/helpers.ml ../common/conf.ml types.ml\ ../common/hierarchy.ml simple_cbf_mb_h_ct.ml\ test_unit.ml RESULT = test_unit endef export PROJ_test_unit ifndef SUBPROJS # export SUBPROJS = simple_cbf_mb_h_ct test_unit # export SUBPROJS = test_unit endif # Default target to use all: nc include $(OCAMLMAKEFILE) # Catch-all target will be applied to all subprojects automatically %: @$(MAKE) -f $(OCAMLMAKEFILE) subprojs SUBTARGET=$@ ./oar-2.5.2/sources/extra/ocaml-schedulers/misc/0000755000175000017500000000000011757171206017554 5ustar plbplb./oar-2.5.2/sources/extra/ocaml-schedulers/misc/hierarchy_extractor.rb0000755000175000017500000000306111757171206024155 0ustar plbplb#!/usr/bin/ruby require 'sequel' DB = Sequel.mysql( #DB = Sequel.postgres( "oar", # "oargofree", :user=>"oar", :password=>"oar", :host => "localhost" ) $DEFAULT_LABELS = ['resource_id','cluster','switch','node','cpu','core'] #$DEFAULT_LABELS = ['resource_id','cluster','cpu','core'] $hierarchy_labels = [] $hierarchy_set = DB["SELECT * FROM resources ORDER BY resource_id ASC"] $fields = DB[:resources].columns def idemize(s) m =-1 nb = 1 b=[] v=[] s.each do |n| if (m==n) then nb = nb + 1 else b << nb v << m m = n nb = 1 end end b << nb v << m [b[1..b.size], v[1..b.size]] end def h_synth(s) h=[] b,v = idemize(s) nb_block,block_size = idemize(b) id =1 nb_block.each_with_index do |nb,i| #puts "#{id},#{block_size[i]},#{nb}" h << "(#{id},#{block_size[i]},#{nb})" id = id + nb * block_size[i] end h.join(',') end #HIERARCHY_LABELS="node,cpu,core" #node="(1,16,2), (32,8,4)" #cpu="(1,8,8)" #core="(1,1,64)" if ARGV.length == 0 $hierarchy_labels = $DEFAULT_LABELS else $hierarchy_labels = ARGV.first.split(',') end h_labels = "HIERARCHY_LABELS=\"" h_desc = "" $hierarchy_labels.each do |label| sym_label = label.to_sym if $fields.include?(sym_label) h_level = h_synth($hierarchy_set.map(sym_label)) h_desc << "#{label}=\"#{h_level}\"\n" h_labels << label << ',' end end h_labels.chop! << '"' puts h_labels puts h_desc #f = IO.readlines("oar.conf") #f.each do |l| # if !(l =~ /^#/) then # l =~ /^(.*)=(.*)\s*/ # puts l,$2 # end #end ./oar-2.5.2/sources/extra/ocaml-schedulers/error_message_info.txt0000644000175000017500000000053311757171206023233 0ustar plbplb List of commons error or information messages that a scheduler programm should provide. There two categoris of messages, one for scheduler for logging or debug purpose, one for each job for users information or metascheduler tools. Scheduler message: log: none available ressources for scheduling any jobs info: Job message error: info: ./oar-2.5.2/sources/extra/ocaml-schedulers/common/0000755000175000017500000000000011757171206020111 5ustar plbplb./oar-2.5.2/sources/extra/ocaml-schedulers/common/interval.ml0000644000175000017500000003263111757171206022274 0ustar plbplb (* todo *) (* suppress the result list in inter_interval list argument !!! *) (* need to propage in all directory !!! *) (* interval type and some operations on them *) (* interval is the basic unit to construct set of resources *) type interval = {b : int; e : int} type set_of_resources = interval list (** Convert interval to string*) let itv2str itv = Printf.sprintf "{b:%d,e:%d}" itv.b itv.e;; (** Convert list of interval to string*) let itvs2str itvs = "["^(String.concat ", " (List.map itv2str itvs))^"]";; (* generate list of intervals for list of unordered ints with greedy approach *) (* must be quicker with a dichotomic approach in most cases *) let ints2intervals ints = let ordered_ints = List.sort Pervasives.compare ints in let rec aux list_int itv_b prev itvs = match list_int with | [] -> [{b=itv_b; e=itv_b}] | x::[] -> if x > (prev+1) then List.rev ({b=x; e=x}::{b=itv_b; e=prev}::itvs) else List.rev ({b=itv_b; e=x}::itvs) | x::n -> if x > (prev+1) then aux n x x ({b=itv_b; e=prev}::itvs) else aux n itv_b x itvs in aux (List.tl ordered_ints) (List.hd ordered_ints) (List.hd ordered_ints) [];; (* # ints2intervals [];; WARNING TO MODIFY ???? Exception: Failure "hd". # ints2intervals [2];; - : interval list = [{b = 2; e = 2}] # ints2intervals [2;4;1;3];; - : interval list = [{b = 1; e = 4}] # ints2intervals [2;4;1;];; - : interval list = [{b = 1; e = 2}; {b = 4; e = 4}] # ints2intervals [2;4];; - : interval list = [{b = 2; e = 2}; {b = 4; e = 4}] # ints2intervals [4;5;7;1;2;3;6;9;8];; - : interval list = [{b = 1; e = 9}] *) (* generate list of ints from list of intervals *) let intervals2ints itv_l = let rec aux itvs ints = match itvs with | [] -> ints | x::n -> let r = ref [] in for i = x.b to x.e do r := i::!r done; aux n (ints @ !r) in aux itv_l [];; (* # intervals2ints [{b = 1; e = 4}];; - : int list = [4; 3; 2; 1] # intervals2ints [{b = 2; e = 2}; {b = 4; e = 4}] ;; - : int list = [2; 4] # intervals2ints [{b = 1; e = 2}; {b = 4; e = 4}] ;; - : int list = [2; 1; 4] *) (* compute intersection of 2 resource intervals *) let inter_intervals itv1s itv2s = let rec inter_itvs itv_l_1 itv_l_2 itv_l_inter = match (itv_l_1,itv_l_2) with | (x::n,y::m) -> if (y.e < x.b) then inter_itvs (x::n) m itv_l_inter else (* y before x w/ no overlap *) if (y.b > x.e) then inter_itvs n (y::m) itv_l_inter else (* x before y w/ no overlap *) if (y.b >= x.b) then if (y.e <= x.e) then (* y before y w/ no overlap *) inter_itvs ({b=y.e+1;e=x.e}::n) m ({b=y.b;e=y.e}::itv_l_inter) else inter_itvs n ({b=x.e+1;e=y.e}::m) ({b=y.b;e=x.e}::itv_l_inter) else if (y.e <= x.e) then inter_itvs ({b=y.e+1;e=x.e}::n) m ({b=x.b;e=y.e}::itv_l_inter) else inter_itvs n ({b=x.e+1;e=y.e}::m) ({b=x.b;e=x.e}::itv_l_inter) | (_,_) -> List.rev itv_l_inter in inter_itvs itv1s itv2s [];; (* compute intersection of 2 intervals resources*) (* with resources counter nb_res *) let inter_intervals_n itv1s itv2s = let rec inter_itvs_n itv_l_1 itv_l_2 itv_l_inter nb_res = match (itv_l_1,itv_l_2) with | (x::n,y::m) -> if (y.e < x.b) then inter_itvs_n (x::n) m itv_l_inter nb_res else (* y before x w/ no overlap *) if (y.b > x.e) then inter_itvs_n n (y::m) itv_l_inter nb_res else (* x before y w/ no overlap *) if (y.b >= x.b) then if (y.e <= x.e) then (* y before y w/ no overlap *) inter_itvs_n ({b=y.e+1;e=x.e}::n) m ({b=y.b;e=y.e}::itv_l_inter) (nb_res + y.e - y.b + 1) else inter_itvs_n n ({b=x.e+1;e=y.e}::m) ({b=y.b;e=x.e}::itv_l_inter) (nb_res + x.e - y.b + 1) else if (y.e <= x.e) then inter_itvs_n ({b=y.e+1;e=x.e}::n) m ({b=x.b;e=y.e}::itv_l_inter) (nb_res + y.e - x.b + 1) else inter_itvs_n n ({b=x.e+1;e=y.e}::m) ({b=x.b;e=x.e}::itv_l_inter) (nb_res + x.e - x.b + 1) | (_,_) -> (List.rev itv_l_inter,nb_res) in inter_itvs_n itv1s itv2s [] 0;; (* let x1 = {b = 11; e = 20};; let y1 = {b = 1; e = 5};; let y2 = {b = 26; e = 30};; let y3 = {b = 12; e = 15};; let y4 = {b = 12; e = 25};; let y5 = {b = 5; e = 15};; let y6 = {b = 5; e = 25};; let x2 = [{b = 1; e = 2}; {b = 5; e = 5}] let yl1 = [{b = 5; e = 13};{b = 15; e = 16 };{b = 19; e = 19}];; inter_intervals [x1] [y1] [];; (* [] *) inter_intervals [x1] [y2] [];; (* [] *) inter_intervals [x1] [y3] [];; (* [{b = 12; e = 15}] *) inter_intervals [x1] [y4] [];; (* [{b = 12; e = 20}] *) inter_intervals [x1] [y5] [];; (* [{b = 11; e = 15}] *) inter_intervals [x1] [y6] [];; (* [{b = 11; e = 20}] *) inter_intervals [x1] [y7] [];; (* [{b = 11; e = 20}] *) inter_intervals_0 [x1] yl1 [];;(* *) inter_intervals [x1] [y1] [] 0;; (* [] *) inter_intervals [x1] [y2] [] 0;; (* [] *) inter_intervals [x1] [y3] [] 0;; (* [{b = 12; e = 15}] *) inter_intervals [x1] [y4] [] 0;; (* [{b = 12; e = 20}] *) inter_intervals [x1] [y5] [] 0;; (* [{b = 11; e = 15}] *) inter_intervals [x1] [y6] [] 0;; (* [{b = 11; e = 20}] *) inter_intervals [x1] [y7] [] 0;; (* [{b = 11; e = 20}] *) inter_intervals [x1] yl1 [] 0;;(* ([{b = 11; e = 13}; {b = 15; e = 16}; {b = 19; e = 19}], 6) *) *) (* some converter to string *) let itv2str itv = Printf.sprintf "{%d,%d}" itv.b itv.e ;; let itvs2str itvs = "["^(String.concat ", " (List.map itv2str itvs))^"]" ;; let itva2str itva = "[|"^(String.concat ", " (List.map itv2str (Array.to_list itva)))^"|]" ;; (* compute substraction of 2 resource intervals *) let sub_intervals_orig x_l y_l = let rec sub_interval_l itv_l_1 itv_l_2 sub_itv_l = match (itv_l_1,itv_l_2) with | (x::n,y::m) -> if (y.e < x.b) then sub_interval_l (x::n) m sub_itv_l else (* y before x w/ no overlap *) if (y.b > x.e) then sub_interval_l n (y::m) (sub_itv_l @ [x]) else (* x before y w/ no overlap *) if (y.b > x.b) then if (y.e < x.e) then (* y before y w/ no overlap *) sub_interval_l ({b=y.e+1;e=x.e}::n) m ({b=x.b;e=y.b-1}::sub_itv_l) else sub_interval_l n (y::m) ({b=x.b;e=y.b-1}::sub_itv_l) else if (y.e < x.e) then sub_interval_l ({b=y.e+1;e=x.e}::n) m sub_itv_l else sub_interval_l n (y::m) sub_itv_l | (x_l,[]) -> (List.rev sub_itv_l) @ x_l | (_,_) -> List.rev sub_itv_l in sub_interval_l x_l y_l [];; (* compute substraction of 2 resource intervals *) let sub_intervals x_l y_l = let rec sub_interval_l itv_l_1 itv_l_2 sub_itv_l = match (itv_l_1,itv_l_2) with | (x::n,y::m) -> if (y.e < x.b) then sub_interval_l (x::n) m sub_itv_l else (* y before x w/ no overlap *) if (y.b > x.e) then sub_interval_l n (y::m) (x::sub_itv_l) else (* x before y w/ no overlap *) if (y.b > x.b) then if (y.e < x.e) then (* x overlap totaly y*) sub_interval_l ({b=y.e+1;e=x.e}::n) m ({b=x.b;e=y.b-1}::sub_itv_l) (* x overlap totally y*) else sub_interval_l n (y::m) ({b=x.b;e=y.b-1}::sub_itv_l) (* x overlap partially y*) else if (y.e < x.e) then sub_interval_l ({b=y.e+1;e=x.e}::n) m sub_itv_l else sub_interval_l n (y::m) sub_itv_l | (x_l,[]) -> (List.rev sub_itv_l) @ x_l | (_,_) -> List.rev sub_itv_l in sub_interval_l x_l y_l [];; sub_intervals [{b = 1; e = 5}] [{b = 3; e = 4}; {b = 12; e = 12}];; (* sub_intervals [x1] [y1] ;; [x1] sub_intervals [x1] [y2] ;; [x1] sub_intervals [x1] [y3] ;; [{b = 11; e = 11}; {b = 16; e = 20}] sub_intervals [x1] [y4] ;; [{b = 11; e = 11}] sub_intervals [x1] [y5] ;; [{b = 16; e = 20}] sub_intervals [x1] [y6] ;; [] sub_intervals [x1] [x1] ;; [] sub_intervals [x1] [];; [x1] sub_intervals [x1] yl1 ;; [{b = 14; e = 14}; {b = 17; e = 18}; {b = 20; e = 20}] sub_intervals [y5] [x1];; [{b = 5; e = 10}] sub_intervals x2 [y3];; [{b = 1; e = 2}; {b = 5; e = 5}] *) (* return [] *) (* itv_l_a itv_l_reference MUST BE ORDERED by ascending resource id *) (* Is it use ?*) let extract_n_block_itv itv_l_a itv_l_reference n = let itv_l_seg = inter_intervals itv_l_a itv_l_reference in let rec extract_n_itv itv_l_1 itv_l_ref itv_l_result nb_itv = match (itv_l_1,itv_l_ref) with | ([],_) | (_,[]) -> [] | (x::n,y::m) -> if x=y then if nb_itv=1 then List.rev (x::itv_l_result) else extract_n_itv n m (x::itv_l_result) (nb_itv -1) else if y.e < x.b then extract_n_itv (x::n) m itv_l_result nb_itv else if x.e < y.e then extract_n_itv n (y::m) itv_l_result nb_itv else extract_n_itv n m itv_l_result nb_itv in extract_n_itv itv_l_seg itv_l_reference [] n;; (* let y = [{b = 5; e = 13}; {b = 15; e = 16}; {b = 19; e = 19}] # extract_n_block_itv y y 1 ;; - : interval list = [{b = 5; e = 13}] # extract_n_block_itv y y 2 ;; - : interval list = [{b = 5; e = 13}; {b = 15; e = 16}] # extract_n_block_itv y y 3 ;; - : interval list = [{b = 5; e = 13}; {b = 15; e = 16}; {b = 19; e = 19}] # extract_n_block_itv [{b = 15; e = 16}; {b = 19; e = 19}] y 3 ;; - : interval list = [] # extract_n_block_itv [{b = 15; e = 16}; {b = 19; e = 19}] y 2 ;; - : interval list = [{b = 15; e = 16}; {b = 19; e = 19}] # extract_n_block_itv [{b = 15; e = 16}; {b = 19; e = 19}] y 2 ;; - : interval list = [{b = 15; e = 16}; {b = 19; e = 19}] *) (* Is it use ?*) let extract_block_itv itv_l_a itv_l_reference = let itv_l_seg = inter_intervals itv_l_a itv_l_reference in let rec extract_itv itv_l_1 itv_l_ref itv_l_result = match (itv_l_1,itv_l_ref) with | (x::n,y::m) -> if x=y then extract_itv n m (x::itv_l_result) else if y.e < x.b then extract_itv (x::n) m itv_l_result else if x.e < y.e then extract_itv n (y::m) itv_l_result else extract_itv n m itv_l_result |(_,_) -> List.rev itv_l_result in extract_itv itv_l_seg itv_l_reference [];; (* Is it use ?*) let extract_n_min_block_itv itv_l_a itv_l_reference n = let itv_l_seg = inter_intervals itv_l_a itv_l_reference in let rec extract_n_min_itv itv_l_1 itv_l_ref itv_l_result nb_itv = match (itv_l_1,itv_l_ref) with | (x::n,y::m) -> if x=y then extract_n_min_itv n m (x::itv_l_result) (nb_itv -1) else if y.e < x.b then extract_n_min_itv (x::n) m itv_l_result nb_itv else if x.e < y.e then extract_n_min_itv n (y::m) itv_l_result nb_itv else extract_n_min_itv n m itv_l_result nb_itv | (_,_) -> if (nb_itv < 1) then List.rev itv_l_result else [] in extract_n_min_itv itv_l_seg itv_l_reference [] n;; (* extract_no_empty_bk : keep reference intervals where intersection with itv_l *) let extract_no_empty_bk itv_l_a itv_l_reference = let rec extract_itv itv_l_ref result = match itv_l_ref with | [] -> List.rev result | (x::n) -> let inter_itvs = inter_intervals itv_l_a [x] in match inter_itvs with | [] -> extract_itv n result | y -> extract_itv n (x::result) in extract_itv itv_l_reference [];; (* let a = [{b = 1; e = 8}; {b = 9; e = 16}; {b = 17; e = 24}; {b = 25; e = 32}];; let b1 = [{b = 1; e = 8}; {b = 9; e = 16}; {b = 17; e = 24}; {b = 25; e = 32}];; let b2 = [{b = 1; e = 8}; {b = 17; e = 24}; {b = 25; e = 32}];; let b3 = [{b = 9; e = 16}; {b = 17; e = 24}; {b = 25; e = 32}];; let b4 = [{b = 1; e = 8}; {b = 9; e = 16}; {b = 17; e = 24}];; let b5 = [{b = 10; e = 12}; {b = 13; e = 14};];; let b6 = [{b = 3; e = 4}; {b = 19; e = 20};];; *) (* extract interval list intersect for each reference intervals, also give the nb of non empty intersection*) let extract_itv_by_itv_nb_inter itv_l_a itv_l_reference = let rec extract_itv itv_l_ref result nb_inter = match itv_l_ref with | [] -> (List.rev result,nb_inter) | (x::n) -> let inter_itvs = inter_intervals itv_l_a [x] in match inter_itvs with | [] -> extract_itv n result nb_inter | y -> extract_itv n (y::result) (nb_inter + 1) in extract_itv itv_l_reference [] 0;; (* let a = [{b = 1; e = 8}; {b = 9; e = 16}; {b = 17; e = 24}; {b = 25; e = 32}];; let b1 = [{b = 1; e = 8}; {b = 9; e = 16}; {b = 17; e = 24}; {b = 25; e = 32}];; let b2 = [{b = 1; e = 8}; {b = 17; e = 24}; {b = 25; e = 32}];; let b3 = [{b = 9; e = 16}; {b = 17; e = 24}; {b = 25; e = 32}];; let b4 = [{b = 1; e = 8}; {b = 9; e = 16}; {b = 17; e = 24}];; let b5 = [{b = 10; e = 12}; {b = 13; e = 14};];; # extract_itv_by_itv_nb_inter b1 a;; - : interval list list * int = ([[{b = 1; e = 8}]; [{b = 9; e = 16}]; [{b = 17; e = 24}]; [{b = 25; e = 32}]], 4) # extract_itv_by_itv_nb_inter b2 a;; - : interval list list * int = ([[{b = 1; e = 8}]; [{b = 17; e = 24}]; [{b = 25; e = 32}]], 3) # extract_itv_by_itv_nb_inter b3 a;; - : interval list list * int = ([[{b = 9; e = 16}]; [{b = 17; e = 24}]; [{b = 25; e = 32}]], 3) # extract_itv_by_itv_nb_inter b4 a;; - : interval list list * int = ([[{b = 1; e = 8}]; [{b = 9; e = 16}]; [{b = 17; e = 24}]], 3) # extract_itv_by_itv_nb_inter b5 a;; - : interval list list * int = ([[{b = 10; e = 12}; {b = 13; e = 14}]], 1) *) ./oar-2.5.2/sources/extra/ocaml-schedulers/common/hierarchy.ml0000644000175000017500000001016111757171206022420 0ustar plbplbopen Interval (* hierarchy_levels and master_top is updated a runtime *) (* let hierarchy_levels = ref (h_desc_to_h_levels [ ("resource_id",[(1,1,1)]) ] ) ;;*) let hierarchy_levels = ref [ ("resource_id",[{b = 1; e = 1}]) ];; let toplevel_itv = ref {b = 1; e = 64} ;; let h_triplets_to_itvs h_triplets = let rec h_t_itvs ht itvs = match ht with | [] -> itvs | (x::n) -> let (orig, bk_size,nb_bk) = x in let rec loop_bk i l_itv = if i = 0 then h_t_itvs n l_itv else loop_bk (i-1) ({b = orig + bk_size * (i-1); e = orig + bk_size * i -1;}::l_itv) in loop_bk nb_bk itvs in h_t_itvs (List.rev h_triplets) [] ;; let h_desc_to_h_levels h_desc = let rec desc_to_itvs h_d h_l = match h_d with | [] -> h_l | (x::n) -> let (label,triplets) = x in desc_to_itvs n ((label, (h_triplets_to_itvs triplets))::h_l) in desc_to_itvs h_desc [];; let find_resource_hierarchies itv_l hy r_rqt_l = let rec find_resource_n_h (top: Interval.interval) h r = match (h, r) with | ([],_) | (_,[]) -> failwith "Bug ??- need to raise exception ???\n"; (* TODO *) | (tops::tl_h, n0::m) -> let h_itv = inter_intervals tops [top] in let available_bk = extract_no_empty_bk itv_l h_itv in if (List.length available_bk) < n0 then [] else if List.length m = 1 then (* iter sur top *) let rec iter_n_no_empty result n bks = match (bks,n) with | (_,0) -> List.rev result (* win *) | (bk::tl_bks, nn) -> begin let h_itv = inter_intervals (List.hd tl_h) [bk] in let sub_result = extract_n_block_itv itv_l h_itv (List.hd m) in match sub_result with | [] -> iter_n_no_empty result nn tl_bks | x -> iter_n_no_empty (sub_result::result) (nn-1) tl_bks end | ([],_) -> [] (* failed*) in iter_n_no_empty [] n0 available_bk else let rec iter_n_find (result: Interval.interval list list) n (bks: Interval.interval list) = match (bks,n) with | (_,0) -> List.rev result (* win *) | (bk::tl_bks, nn) -> begin let sub_result = find_resource_n_h bk tl_h m in match sub_result with | [] -> iter_n_find result nn tl_bks | x -> iter_n_find ((List.flatten x)::result) (nn-1) tl_bks end | ([],_) -> [] (* failed*) in iter_n_find [] n0 available_bk in if (List.length hy) = 1 then extract_n_block_itv itv_l (List.hd hy) (List.hd r_rqt_l) else List.flatten (find_resource_n_h !toplevel_itv hy r_rqt_l);; (* let h0 = [{b = 1; e = 16};{b = 17; e = 32};];; let h1 = [{b = 1; e = 8}; {b = 9; e = 16}; {b = 17; e = 24}; {b = 25; e = 32}];; let h = [|h0;h1|] ;; let r = [|2;1|] ;; let e1 = [{b = 16; e = 23}] ;; let e2 = [{b = 16; e = 23}] ;; let e3 = [{b = 16; e = 23}] ;; let res1 = [{b = 1; e = 8}];; let t = [ (e1,h,r,res1); (e2,h,r,res1); (e3,h,r,res1); ];; *) (* let test_find_hierarchies test_list = let test = fun x -> let (input, hys, r_reqts, result) = x in let r = find_resource_hierarchies input hys r_reqts in if r = [result] then Printf.printf "****** OK ******\n" else Printf.printf "####### Bou ######\n" in List.iter (fun x -> test x) test_list;; *) (* let _= find_resource_hierarchies {b = 1; e = 32} [{b = 1; e = 32}] [h0;h1;h2] [2;1;1];; *) (* let h = [|h0;h1|] in let r = [|2;1|] in find_resource_hierarchies [{b = 1; e = 32}] [|h0;h1;h2|] [|2; 1; 1|];; *) (* find_resource_hierarchies [{b = 1; e = 32}] h r;; *) (* find_resource_hierarchies [{b = 10; e = 32}] [|h0;h1|] [|2; 2|]; bug find_resource_hierarchies [{b = 1; e = 32}] [|h0;h1;h2|] [|1; 1; 1|]; find_resource_hierarchies [{b = 1; e = 32}] [|h0;h1;h2|] [|1; 1; 1|];; (OK) find_resource_hierarchies [{b = 1; e = 32}] [|h0;h1;h2|] [|2; 1; 1|];; (Failed) *) (* let h = [|h0;h1|] in let r = [|2;1|] in find_resource_2_h [{b = 1; e = 32}] h r;; *) ./oar-2.5.2/sources/extra/ocaml-schedulers/common/helpers.ml0000644000175000017500000001235011757171206022106 0ustar plbplb(* *) (* Main author: Lionel Eyraud-Dubois *) (* *) (* function manipulations *) let id x = x let foldi n f start = let rec aux i state = if i < n then aux (i+1) (f i state) else state in aux 0 start let (@@) f g = function x -> f (g x) let rev f x y = f y x let bicompose f g h x y = f (g x) (h y) let cmp critere x y = Pervasives.compare (critere x) (critere y) let curry f x y = f (x, y) let uncurry f (x, y) = f x y let agrege f transforme start = List.fold_left (bicompose f id transforme) start let somme transforme = agrege (+) transforme 0 let sommefloat transforme = agrege (+.) transforme 0. let concatene transforme = agrege (^) transforme "" let concatene_sep sep transforme = let rec aux buf = function [] -> buf | [x] -> buf ^ (transforme x) | x::xs -> aux (buf ^ (transforme x) ^ sep) xs in aux "" (** [arrange p arr l] : examine dans l'ordre les éléments de [l], arrange les deux premiers entre eux par la fonction [arr], enlève le resultat s'il vérifie le prédicat [p]. Renvoie la liste des éléments arrangés. *) let arrange p arr l = let rec aux buffer = function [] -> buffer | [a] -> a::buffer | a::b::next -> let c = arr a b in if p c then aux (c::buffer) next else aux buffer (c::next) in aux [] l let cut_string sep s = let l = String.length s in let rec drop_sep p = if p >= l then l else if s.[p] == sep then drop_sep (p+1) else p in let rec cut_aux p buf = if p >= l then buf else let i = try (String.index_from s p sep) with Not_found -> l in let j = drop_sep (i+1) in cut_aux j ((String.sub s p (i-p))::buf) in List.rev (cut_aux (drop_sep 0) []) (* list manipulations *) let first_n l n = let rec aux buf l n = match n, l with 0, _ | _, [] -> List.rev buf | n, x::xs -> aux (x::buf) xs (n-1) in aux [] l n let rec remove_first x = function [] -> [] | y::s when x = y -> s | a::s -> a::(remove_first x s) let remove_list what from = List.fold_left (fun l x -> remove_first x l) from what let cheap_min f l = let aux (y, yv) x = let xv = f x in if (compare xv yv) > 0 then (y, yv) else (x, xv) in match l with [] -> raise (Failure "Outils.min") | x::xs -> List.fold_left aux (x, f x) xs let cheap_max f l = let aux (y, yv) x = let xv = f x in if (compare xv yv) < 0 then (y, yv) else (x, xv) in match l with [] -> raise (Failure "Outils.min") | x::xs -> List.fold_left aux (x, f x) xs let gen_min comp = function [] -> raise (Failure "Outils.gen_min") | x::xs -> List.fold_left (fun x y -> if (comp x y) > 0 then y else x) x xs let gen_max comp = function [] -> raise (Failure "Outils.gen_min") | x::xs -> List.fold_left (fun x y -> if (comp x y) < 0 then y else x) x xs let min f l = fst (cheap_min f l) let max f l = fst (cheap_max f l) let rec revmap_concat f l = function [] -> l | x::xs -> revmap_concat f ( (f x)::l) xs let for_int a b = let rec aux p buf = if p > b then List.rev buf else aux (p+1) (p::buf) in if b < a then failwith "for" else aux a [] let for_float a b step = if b = a then [ a ] else if step = 0. then failwith "for" else let tmp = let s = int_of_float ((b -. a) /. step) in for_int 0 s in List.map (fun x -> a +. (float x) *. step) tmp type ('a, 'b) union = One of 'a | Two of 'b let filter_map2 f l = let rec aux buf1 buf2 = function [] -> (List.rev buf1, List.rev buf2) | x::xs -> match f x with One u -> aux (u::buf1) buf2 xs | Two v -> aux buf1 (v::buf2) xs in aux [] [] l (* Be carefull: it's greedy and heavy *) let rec cross = function [] -> [] | [ l ] -> List.map (fun x -> [x]) l | l::ls -> let reste = cross ls in List.flatten (List.map (fun x -> List.map (fun a -> x::a) reste) l) (* array manipulations *) let swap tab a b = let x = tab.(a) in tab.(a) <- tab.(b); tab.(b) <- x let partial_sums ?(transform = id) t = let k = Array.length t in let r = Array.make k 0 and v = ref 0 in for i = 0 to k - 1 do r.(i) <- !v; v := !v + (transform t.(i)); done; r (* operations on float *) let logb b = let l = log b in function x -> (log x) /. l let log2 = logb 2. (* misc *) let get_option = function Some x -> x | None -> failwith "get_option" let time f arg = let deb = Sys.time () in let res = f arg in let fin = Sys.time () in (fin -. deb, res) open Str let replace input output = Str.global_replace (Str.regexp_string input) output let replace_regexp input output = Str.global_replace (Str.regexp input) output let split a = Str.split (Str.regexp_string a);; let hash_iter f l h = List.iter (fun x-> let j = try Hashtbl.find h x with Not_found -> failwith "Can't Hashtbl.find " in f j) l let couples2hash l = let h = Hashtbl.create 10 in begin ignore ( List.iter (fun x -> Hashtbl.add h (fst x) (snd x) ) l); h end let filter_map f_filter f_map = let rec find accu = function [] -> List.rev accu | x :: l -> if (f_filter x) then find ((f_map x) :: accu) l else find accu l in find [] (* remove quotes from string *) let remove_quotes str = replace " " "" str ./oar-2.5.2/sources/extra/ocaml-schedulers/common/conf.ml0000644000175000017500000001437111757171206021376 0ustar plbplb(* *) (* Configuration and logging functions *) (* *) (* Code borrowed, in large par from oar 1.6 series and *) (* developped by Lionel Eyraud-Dubois *) (* *) let log_file = ref None let print_log s = prerr_string s; flush stderr; match !log_file with Some d -> output_string d s; flush d | None -> () open Unix let formatted_time () = let rest, seconds = modf (gettimeofday ()) in let tm = localtime seconds and microseconds = int_of_float (rest *. 1e6) in Printf.sprintf "%04d-%02d-%02d %02d:%02d:%02d.%06d" (tm.tm_year + 1900) (tm.tm_mon + 1) tm.tm_mday tm.tm_hour tm.tm_min tm.tm_sec microseconds let do_message e s = print_log (Printf.sprintf "[%s] [%s] [Simple-CBF] %s\n" e (formatted_time ()) s) let error s = do_message "*Fatal Error*" s; failwith ("Fatal Error : "^s) let conf_file_name = "oar.conf" let besteffortQueueName = "besteffort" let conf_file = let dirs = (try [Sys.getenv "OARDIR"] with Not_found -> []) @ ["/etc/oar/"] in let exists d = Sys.file_exists (Filename.concat d conf_file_name) in try Filename.concat (List.find exists dirs) conf_file_name with Not_found -> error ("Cannot find Conf File "^conf_file_name) let chop s = try let l = String.length s in let rec start i = if i < l then if s.[i] = ' ' then start (i+1) else i else raise Exit in let rec finish i = if i < 0 then raise Exit else if s.[i] = ' ' then finish (i-1) else i in let a = start 0 and b = finish (l-1) in String.sub s a (b-a+1) with Exit -> String.create 0 let conf_values = let t = Hashtbl.create 10 in try ( let file = open_in conf_file in let rec parse () = let l = chop (input_line file) in ( if String.length l = 0 or l.[0] = '#' then () else try let i = String.index l '=' in let key = chop (String.sub l 0 i) and value = chop (String.sub l (i+1) ((String.length l) - i - 1)) in let value_chomp_quote = if value.[0] = '"' then String.sub value 1 ((String.length value) - 2) else value in Hashtbl.add t key value_chomp_quote with Not_found -> error ("Cannot parse "^l)); parse() in try parse () with End_of_file -> t) with Sys_error s -> error ("Cannot open conf file "^conf_file^" : "^s) (* Log is automatically redirected in /var/log/oar.log by metascheduler *) (* Use LOG_SCHED_FILE is a separed log file is wished *) (* let _ = let f = try Hashtbl.find conf_values "LOG_SCHED_FILE" with Not_found -> "/var/log/oar.log" in try let d = open_out_gen [ Open_append; Open_text] 0o600 f in log_file := Some d; at_exit (fun () -> close_out d); with Sys_error s -> ( do_message "Warn" (Printf.sprintf "Cannot open log file '%s': %s" f s) ) *) (* let now, queueName = match Array.length Sys.argv with x when x >= 3 -> ( let n = try float_of_string (Sys.argv.(2)) with _ -> error ("Second arg should be a time, not " ^ (Sys.argv.(1))) in let q = Sys.argv.(1) in TimeConversion.unixtime2secs n, q ) | x -> error "Expecting 2 args : time queueName" *) let loglevel = try let s = Hashtbl.find conf_values "LOG_LEVEL" in ( try int_of_string s with _ -> error ("LOG_LEVEL in Conffile should be INT, not " ^ s) ) with Not_found -> ( do_message "Warn" ("Couldn't find LOG_LEVEL value; assuming the worst"); 25 ) let log s = if loglevel > 2 then do_message "debug" s let warn s = if loglevel > 1 then do_message "warn" s let get_value s = try Hashtbl.find conf_values s with Not_found -> error (Printf.sprintf "Value %s not found in confFile %s" s conf_file) let test_key s = try (ignore (Hashtbl.find conf_values s); true) with Not_found -> false let get_optional_value s = try Some (Hashtbl.find conf_values s) with Not_found -> ( warn (Printf.sprintf "Value %s not found in confFile %s\n" s conf_file); None ) let get_default_value s d = (*Printf.sprintf "get_default_value: %s\n" s; *) try Hashtbl.find conf_values s with Not_found -> d let get_hierarchy_info = (* ignore(Printf.sprintf "get_hierarchy_info HIERARCHY_LABELS\n"); *) let hierarchy_labels = Helpers.split "," (Helpers.replace " " "" (get_value "HIERARCHY_LABELS")) in let str_to_triplet y = let z = List.map (fun x-> int_of_string x ) (Helpers.split "," y) in (List.nth z 0,List.nth z 1,List.nth z 2) in let extract_triplet str_ts = let b = Helpers.replace "),(" ";" str_ts in let c = Helpers.replace_regexp "(\\|)\\| " "" b in let str_triplets = Helpers.split ";" c in (* Printf.printf "yop: %s\n" c; *) List.map (fun x-> str_to_triplet x) str_triplets in List.map (fun x-> (x, extract_triplet (get_value x))) hierarchy_labels ;; (* function which trasnform a perl string hash in list of couple *) (* ex { first => 75, default => 25 } : (string * int) list = [("first", 75); ("default", 25)] *) let str_perl_hash_to_pairs str = let str_pairs = Helpers.split "," (Helpers.replace_regexp "{\\|}\\| " "" str) in let extraxt_pair s = let str_lpair = Helpers.split "=>" s in (List.hd str_lpair), (List.hd (List.tl str_lpair)) in List.map (fun x-> extraxt_pair x) str_pairs (* function which transform a perl string hash in list of couple with conversion application function*) (* ex { first => 75, default => 25 } : (string * int) list = [("first", 75); ("default", 25)] *) let str_perl_hash_to_pairs_w_convert str convert= (* ignore(Printf.sprintf "Str_perl_hash_to_pairs_w_convert: %s" str;) *) let str_pairs = Helpers.split "," (Helpers.replace_regexp "{\\|}\\| " "" str) in let extraxt_pair s = let str_lpair = Helpers.split "=>" s in (List.hd str_lpair), (convert (List.hd (List.tl str_lpair))) in List.map (fun x-> extraxt_pair x) str_pairs (* float_of_string_e with exception*) let float_of_string_e s = try float_of_string s with _ -> error ("float_of_string failed:"^s) ./oar-2.5.2/sources/extra/ocaml-schedulers/common/OCamlMakefile0000644000175000017500000010176411757171206022476 0ustar plbplb########################################################################### # OCamlMakefile # Copyright (C) 1999-2007 Markus Mottl # # For updates see: # http://www.ocaml.info/home/ocaml_sources.html # ########################################################################### # Modified by damien for .glade.ml compilation # Set these variables to the names of the sources to be processed and # the result variable. Order matters during linkage! ifndef SOURCES SOURCES := foo.ml endif export SOURCES ifndef RES_CLIB_SUF RES_CLIB_SUF := _stubs endif export RES_CLIB_SUF ifndef RESULT RESULT := foo endif export RESULT := $(strip $(RESULT)) export LIB_PACK_NAME ifndef DOC_FILES DOC_FILES := $(filter %.mli, $(SOURCES)) endif export DOC_FILES FIRST_DOC_FILE := $(firstword $(DOC_FILES)) export BCSUFFIX export NCSUFFIX ifndef TOPSUFFIX TOPSUFFIX := .top endif export TOPSUFFIX # Eventually set include- and library-paths, libraries to link, # additional compilation-, link- and ocamlyacc-flags # Path- and library information needs not be written with "-I" and such... # Define THREADS if you need it, otherwise leave it unset (same for # USE_CAMLP4)! export THREADS export VMTHREADS export ANNOTATE export USE_CAMLP4 export INCDIRS export LIBDIRS export EXTLIBDIRS export RESULTDEPS export OCAML_DEFAULT_DIRS export LIBS export CLIBS export CFRAMEWORKS export OCAMLFLAGS export OCAMLNCFLAGS export OCAMLBCFLAGS export OCAMLLDFLAGS export OCAMLNLDFLAGS export OCAMLBLDFLAGS export OCAMLMKLIB_FLAGS ifndef OCAMLCPFLAGS OCAMLCPFLAGS := a endif export OCAMLCPFLAGS ifndef DOC_DIR DOC_DIR := doc endif export DOC_DIR export PPFLAGS export LFLAGS export YFLAGS export IDLFLAGS export OCAMLDOCFLAGS export OCAMLFIND_INSTFLAGS export DVIPSFLAGS export STATIC # Add a list of optional trash files that should be deleted by "make clean" export TRASH ECHO := echo ifdef REALLY_QUIET export REALLY_QUIET ECHO := true LFLAGS := $(LFLAGS) -q YFLAGS := $(YFLAGS) -q endif #################### variables depending on your OCaml-installation ifdef MINGW export MINGW WIN32 := 1 CFLAGS_WIN32 := -mno-cygwin endif ifdef MSVC export MSVC WIN32 := 1 ifndef STATIC CPPFLAGS_WIN32 := -DCAML_DLL endif CFLAGS_WIN32 += -nologo EXT_OBJ := obj EXT_LIB := lib ifeq ($(CC),gcc) # work around GNU Make default value ifdef THREADS CC := cl -MT else CC := cl endif endif ifeq ($(CXX),g++) # work around GNU Make default value CXX := $(CC) endif CFLAG_O := -Fo endif ifdef WIN32 EXT_CXX := cpp EXE := .exe endif ifndef EXT_OBJ EXT_OBJ := o endif ifndef EXT_LIB EXT_LIB := a endif ifndef EXT_CXX EXT_CXX := cc endif ifndef EXE EXE := # empty endif ifndef CFLAG_O CFLAG_O := -o # do not delete this comment (preserves trailing whitespace)! endif export CC export CXX export CFLAGS export CXXFLAGS export LDFLAGS export CPPFLAGS ifndef RPATH_FLAG ifdef ELF_RPATH_FLAG RPATH_FLAG := $(ELF_RPATH_FLAG) else RPATH_FLAG := -R endif endif export RPATH_FLAG ifndef MSVC ifndef PIC_CFLAGS PIC_CFLAGS := -fPIC endif ifndef PIC_CPPFLAGS PIC_CPPFLAGS := -DPIC endif endif export PIC_CFLAGS export PIC_CPPFLAGS BCRESULT := $(addsuffix $(BCSUFFIX), $(RESULT)) NCRESULT := $(addsuffix $(NCSUFFIX), $(RESULT)) TOPRESULT := $(addsuffix $(TOPSUFFIX), $(RESULT)) ifndef OCAMLFIND OCAMLFIND := ocamlfind endif export OCAMLFIND ifndef OCAMLC OCAMLC := ocamlc endif export OCAMLC ifndef OCAMLOPT OCAMLOPT := ocamlopt endif export OCAMLOPT ifndef OCAMLMKTOP OCAMLMKTOP := ocamlmktop endif export OCAMLMKTOP ifndef OCAMLCP OCAMLCP := ocamlcp endif export OCAMLCP ifndef OCAMLDEP OCAMLDEP := ocamldep endif export OCAMLDEP ifndef OCAMLLEX OCAMLLEX := ocamllex endif export OCAMLLEX ifndef OCAMLYACC OCAMLYACC := ocamlyacc endif export OCAMLYACC ifndef OCAMLMKLIB OCAMLMKLIB := ocamlmklib endif export OCAMLMKLIB ifndef OCAML_GLADECC OCAML_GLADECC := lablgladecc2 endif export OCAML_GLADECC ifndef OCAML_GLADECC_FLAGS OCAML_GLADECC_FLAGS := endif export OCAML_GLADECC_FLAGS ifndef CAMELEON_REPORT CAMELEON_REPORT := report endif export CAMELEON_REPORT ifndef CAMELEON_REPORT_FLAGS CAMELEON_REPORT_FLAGS := endif export CAMELEON_REPORT_FLAGS ifndef CAMELEON_ZOGGY CAMELEON_ZOGGY := camlp4o pa_zog.cma pr_o.cmo endif export CAMELEON_ZOGGY ifndef CAMELEON_ZOGGY_FLAGS CAMELEON_ZOGGY_FLAGS := endif export CAMELEON_ZOGGY_FLAGS ifndef OXRIDL OXRIDL := oxridl endif export OXRIDL ifndef CAMLIDL CAMLIDL := camlidl endif export CAMLIDL ifndef CAMLIDLDLL CAMLIDLDLL := camlidldll endif export CAMLIDLDLL ifndef NOIDLHEADER MAYBE_IDL_HEADER := -header endif export NOIDLHEADER export NO_CUSTOM ifndef CAMLP4 CAMLP4 := camlp4 endif export CAMLP4 ifndef REAL_OCAMLFIND ifdef PACKS ifndef CREATE_LIB ifdef THREADS PACKS += threads endif endif empty := space := $(empty) $(empty) comma := , ifdef PREDS PRE_OCAML_FIND_PREDICATES := $(subst $(space),$(comma),$(PREDS)) PRE_OCAML_FIND_PACKAGES := $(subst $(space),$(comma),$(PACKS)) OCAML_FIND_PREDICATES := -predicates $(PRE_OCAML_FIND_PREDICATES) # OCAML_DEP_PREDICATES := -syntax $(PRE_OCAML_FIND_PREDICATES) OCAML_FIND_PACKAGES := $(OCAML_FIND_PREDICATES) -package $(PRE_OCAML_FIND_PACKAGES) OCAML_DEP_PACKAGES := $(OCAML_DEP_PREDICATES) -package $(PRE_OCAML_FIND_PACKAGES) else OCAML_FIND_PACKAGES := -package $(subst $(space),$(comma),$(PACKS)) OCAML_DEP_PACKAGES := endif OCAML_FIND_LINKPKG := -linkpkg REAL_OCAMLFIND := $(OCAMLFIND) endif endif export OCAML_FIND_PACKAGES export OCAML_DEP_PACKAGES export OCAML_FIND_LINKPKG export REAL_OCAMLFIND ifndef OCAMLDOC OCAMLDOC := ocamldoc endif export OCAMLDOC ifndef LATEX LATEX := latex endif export LATEX ifndef DVIPS DVIPS := dvips endif export DVIPS ifndef PS2PDF PS2PDF := ps2pdf endif export PS2PDF ifndef OCAMLMAKEFILE OCAMLMAKEFILE := OCamlMakefile endif export OCAMLMAKEFILE ifndef OCAMLLIBPATH OCAMLLIBPATH := \ $(shell $(OCAMLC) 2>/dev/null -where || echo /usr/lib/ocaml) endif export OCAMLLIBPATH ifndef OCAML_LIB_INSTALL OCAML_LIB_INSTALL := $(OCAMLLIBPATH)/contrib endif export OCAML_LIB_INSTALL ########################################################################### #################### change following sections only if #################### you know what you are doing! # delete target files when a build command fails .PHONY: .DELETE_ON_ERROR .DELETE_ON_ERROR: # for pedants using "--warn-undefined-variables" export MAYBE_IDL export REAL_RESULT export CAMLIDLFLAGS export THREAD_FLAG export RES_CLIB export MAKEDLL export ANNOT_FLAG export C_OXRIDL export SUBPROJS export CFLAGS_WIN32 export CPPFLAGS_WIN32 INCFLAGS := SHELL := /bin/sh MLDEPDIR := ._d BCDIDIR := ._bcdi NCDIDIR := ._ncdi FILTER_EXTNS := %.mli %.ml %.mll %.mly %.idl %.oxridl %.c %.m %.$(EXT_CXX) %.rep %.zog %.glade FILTERED := $(filter $(FILTER_EXTNS), $(SOURCES)) SOURCE_DIRS := $(filter-out ./, $(sort $(dir $(FILTERED)))) FILTERED_REP := $(filter %.rep, $(FILTERED)) DEP_REP := $(FILTERED_REP:%.rep=$(MLDEPDIR)/%.d) AUTO_REP := $(FILTERED_REP:.rep=.ml) FILTERED_ZOG := $(filter %.zog, $(FILTERED)) DEP_ZOG := $(FILTERED_ZOG:%.zog=$(MLDEPDIR)/%.d) AUTO_ZOG := $(FILTERED_ZOG:.zog=.ml) FILTERED_GLADE := $(filter %.glade, $(FILTERED)) DEP_GLADE := $(FILTERED_GLADE:%.glade=$(MLDEPDIR)/%.d) AUTO_GLADE := $(FILTERED_GLADE:.glade=.ml) FILTERED_ML := $(filter %.ml, $(FILTERED)) DEP_ML := $(FILTERED_ML:%.ml=$(MLDEPDIR)/%.d) FILTERED_MLI := $(filter %.mli, $(FILTERED)) DEP_MLI := $(FILTERED_MLI:.mli=.di) FILTERED_MLL := $(filter %.mll, $(FILTERED)) DEP_MLL := $(FILTERED_MLL:%.mll=$(MLDEPDIR)/%.d) AUTO_MLL := $(FILTERED_MLL:.mll=.ml) FILTERED_MLY := $(filter %.mly, $(FILTERED)) DEP_MLY := $(FILTERED_MLY:%.mly=$(MLDEPDIR)/%.d) $(FILTERED_MLY:.mly=.di) AUTO_MLY := $(FILTERED_MLY:.mly=.mli) $(FILTERED_MLY:.mly=.ml) FILTERED_IDL := $(filter %.idl, $(FILTERED)) DEP_IDL := $(FILTERED_IDL:%.idl=$(MLDEPDIR)/%.d) $(FILTERED_IDL:.idl=.di) C_IDL := $(FILTERED_IDL:%.idl=%_stubs.c) ifndef NOIDLHEADER C_IDL += $(FILTERED_IDL:.idl=.h) endif OBJ_C_IDL := $(FILTERED_IDL:%.idl=%_stubs.$(EXT_OBJ)) AUTO_IDL := $(FILTERED_IDL:.idl=.mli) $(FILTERED_IDL:.idl=.ml) $(C_IDL) FILTERED_OXRIDL := $(filter %.oxridl, $(FILTERED)) DEP_OXRIDL := $(FILTERED_OXRIDL:%.oxridl=$(MLDEPDIR)/%.d) $(FILTERED_OXRIDL:.oxridl=.di) AUTO_OXRIDL := $(FILTERED_OXRIDL:.oxridl=.mli) $(FILTERED_OXRIDL:.oxridl=.ml) $(C_OXRIDL) FILTERED_C_CXX := $(filter %.c %.m %.$(EXT_CXX), $(FILTERED)) OBJ_C_CXX := $(FILTERED_C_CXX:.c=.$(EXT_OBJ)) OBJ_C_CXX := $(OBJ_C_CXX:.m=.$(EXT_OBJ)) OBJ_C_CXX := $(OBJ_C_CXX:.$(EXT_CXX)=.$(EXT_OBJ)) PRE_TARGETS += $(AUTO_MLL) $(AUTO_MLY) $(AUTO_IDL) $(AUTO_OXRIDL) $(AUTO_ZOG) $(AUTO_REP) $(AUTO_GLADE) ALL_DEPS := $(DEP_ML) $(DEP_MLI) $(DEP_MLL) $(DEP_MLY) $(DEP_IDL) $(DEP_OXRIDL) $(DEP_ZOG) $(DEP_REP) $(DEP_GLADE) MLDEPS := $(filter %.d, $(ALL_DEPS)) MLIDEPS := $(filter %.di, $(ALL_DEPS)) BCDEPIS := $(MLIDEPS:%.di=$(BCDIDIR)/%.di) NCDEPIS := $(MLIDEPS:%.di=$(NCDIDIR)/%.di) ALLML := $(filter %.mli %.ml %.mll %.mly %.idl %.oxridl %.rep %.zog %.glade, $(FILTERED)) IMPLO_INTF := $(ALLML:%.mli=%.mli.__) IMPLO_INTF := $(foreach file, $(IMPLO_INTF), \ $(basename $(file)).cmi $(basename $(file)).cmo) IMPLO_INTF := $(filter-out %.mli.cmo, $(IMPLO_INTF)) IMPLO_INTF := $(IMPLO_INTF:%.mli.cmi=%.cmi) IMPLX_INTF := $(IMPLO_INTF:.cmo=.cmx) INTF := $(filter %.cmi, $(IMPLO_INTF)) IMPL_CMO := $(filter %.cmo, $(IMPLO_INTF)) IMPL_CMX := $(IMPL_CMO:.cmo=.cmx) IMPL_ASM := $(IMPL_CMO:.cmo=.asm) IMPL_S := $(IMPL_CMO:.cmo=.s) OBJ_LINK := $(OBJ_C_IDL) $(OBJ_C_CXX) OBJ_FILES := $(IMPL_CMO:.cmo=.$(EXT_OBJ)) $(OBJ_LINK) EXECS := $(addsuffix $(EXE), \ $(sort $(TOPRESULT) $(BCRESULT) $(NCRESULT))) ifdef WIN32 EXECS += $(BCRESULT).dll $(NCRESULT).dll endif CLIB_BASE := $(RESULT)$(RES_CLIB_SUF) ifneq ($(strip $(OBJ_LINK)),) RES_CLIB := lib$(CLIB_BASE).$(EXT_LIB) endif ifdef WIN32 DLLSONAME := $(CLIB_BASE).dll else DLLSONAME := dll$(CLIB_BASE).so endif NONEXECS := $(INTF) $(IMPL_CMO) $(IMPL_CMX) $(IMPL_ASM) $(IMPL_S) \ $(OBJ_FILES) $(PRE_TARGETS) $(BCRESULT).cma $(NCRESULT).cmxa \ $(NCRESULT).$(EXT_LIB) $(BCRESULT).cmi $(BCRESULT).cmo \ $(NCRESULT).cmi $(NCRESULT).cmx $(NCRESULT).o \ $(RES_CLIB) $(IMPL_CMO:.cmo=.annot) \ $(LIB_PACK_NAME).cmi $(LIB_PACK_NAME).cmo $(LIB_PACK_NAME).cmx $(LIB_PACK_NAME).o ifndef STATIC NONEXECS += $(DLLSONAME) endif ifndef LIBINSTALL_FILES LIBINSTALL_FILES := $(RESULT).mli $(RESULT).cmi $(RESULT).cma \ $(RESULT).cmxa $(RESULT).$(EXT_LIB) $(RES_CLIB) ifndef STATIC ifneq ($(strip $(OBJ_LINK)),) LIBINSTALL_FILES += $(DLLSONAME) endif endif endif export LIBINSTALL_FILES ifdef WIN32 # some extra stuff is created while linking DLLs NONEXECS += $(BCRESULT).$(EXT_LIB) $(BCRESULT).exp $(NCRESULT).exp $(CLIB_BASE).exp $(CLIB_BASE).lib endif TARGETS := $(EXECS) $(NONEXECS) # If there are IDL-files ifneq ($(strip $(FILTERED_IDL)),) MAYBE_IDL := -cclib -lcamlidl endif ifdef USE_CAMLP4 CAMLP4PATH := \ $(shell $(CAMLP4) -where 2>/dev/null || echo /usr/lib/camlp4) INCFLAGS := -I $(CAMLP4PATH) CINCFLAGS := -I$(CAMLP4PATH) endif DINCFLAGS := $(INCFLAGS) $(SOURCE_DIRS:%=-I %) $(OCAML_DEFAULT_DIRS:%=-I %) INCFLAGS := $(DINCFLAGS) $(INCDIRS:%=-I %) CINCFLAGS += $(SOURCE_DIRS:%=-I%) $(INCDIRS:%=-I%) $(OCAML_DEFAULT_DIRS:%=-I%) ifndef MSVC CLIBFLAGS += $(SOURCE_DIRS:%=-L%) $(LIBDIRS:%=-L%) \ $(EXTLIBDIRS:%=-L%) $(OCAML_DEFAULT_DIRS:%=-L%) ifeq ($(ELF_RPATH), yes) CLIBFLAGS += $(EXTLIBDIRS:%=-Wl,$(RPATH_FLAG)%) endif endif ifndef PROFILING INTF_OCAMLC := $(OCAMLC) else ifndef THREADS INTF_OCAMLC := $(OCAMLCP) -p $(OCAMLCPFLAGS) else # OCaml does not support profiling byte code # with threads (yet), therefore we force an error. ifndef REAL_OCAMLC $(error Profiling of multithreaded byte code not yet supported by OCaml) endif INTF_OCAMLC := $(OCAMLC) endif endif ifndef MSVC COMMON_LDFLAGS := $(LDFLAGS:%=-ccopt %) $(SOURCE_DIRS:%=-ccopt -L%) \ $(LIBDIRS:%=-ccopt -L%) $(EXTLIBDIRS:%=-ccopt -L%) \ $(EXTLIBDIRS:%=-ccopt -Wl $(OCAML_DEFAULT_DIRS:%=-ccopt -L%)) ifeq ($(ELF_RPATH),yes) COMMON_LDFLAGS += $(EXTLIBDIRS:%=-ccopt -Wl,$(RPATH_FLAG)%) endif else COMMON_LDFLAGS := -ccopt "/link -NODEFAULTLIB:LIBC $(LDFLAGS:%=%) $(SOURCE_DIRS:%=-LIBPATH:%) \ $(LIBDIRS:%=-LIBPATH:%) $(EXTLIBDIRS:%=-LIBPATH:%) \ $(OCAML_DEFAULT_DIRS:%=-LIBPATH:%) " endif CLIBS_OPTS := $(CLIBS:%=-cclib -l%) $(CFRAMEWORKS:%=-cclib '-framework %') ifdef MSVC ifndef STATIC # MSVC libraries do not have 'lib' prefix CLIBS_OPTS := $(CLIBS:%=-cclib %.lib) endif endif ifneq ($(strip $(OBJ_LINK)),) ifdef CREATE_LIB OBJS_LIBS := -cclib -l$(CLIB_BASE) $(CLIBS_OPTS) $(MAYBE_IDL) else OBJS_LIBS := $(OBJ_LINK) $(CLIBS_OPTS) $(MAYBE_IDL) endif else OBJS_LIBS := $(CLIBS_OPTS) $(MAYBE_IDL) endif # If we have to make byte-code ifndef REAL_OCAMLC BYTE_OCAML := y # EXTRADEPS is added dependencies we have to insert for all # executable files we generate. Ideally it should be all of the # libraries we use, but it's hard to find the ones that get searched on # the path since I don't know the paths built into the compiler, so # just include the ones with slashes in their names. EXTRADEPS := $(addsuffix .cma,$(foreach i,$(LIBS),$(if $(findstring /,$(i)),$(i)))) SPECIAL_OCAMLFLAGS := $(OCAMLBCFLAGS) REAL_OCAMLC := $(INTF_OCAMLC) REAL_IMPL := $(IMPL_CMO) REAL_IMPL_INTF := $(IMPLO_INTF) IMPL_SUF := .cmo DEPFLAGS := MAKE_DEPS := $(MLDEPS) $(BCDEPIS) ifdef CREATE_LIB override CFLAGS := $(PIC_CFLAGS) $(CFLAGS) override CPPFLAGS := $(PIC_CPPFLAGS) $(CPPFLAGS) ifndef STATIC ifneq ($(strip $(OBJ_LINK)),) MAKEDLL := $(DLLSONAME) ALL_LDFLAGS := -dllib $(DLLSONAME) endif endif endif ifndef NO_CUSTOM ifneq "$(strip $(OBJ_LINK) $(THREADS) $(MAYBE_IDL) $(CLIBS) $(CFRAMEWORKS))" "" ALL_LDFLAGS += -custom endif endif ALL_LDFLAGS += $(INCFLAGS) $(OCAMLLDFLAGS) $(OCAMLBLDFLAGS) \ $(COMMON_LDFLAGS) $(LIBS:%=%.cma) CAMLIDLDLLFLAGS := ifdef THREADS ifdef VMTHREADS THREAD_FLAG := -vmthread else THREAD_FLAG := -thread endif ALL_LDFLAGS := $(THREAD_FLAG) $(ALL_LDFLAGS) ifndef CREATE_LIB ifndef REAL_OCAMLFIND ALL_LDFLAGS := unix.cma threads.cma $(ALL_LDFLAGS) endif endif endif # we have to make native-code else EXTRADEPS := $(addsuffix .cmxa,$(foreach i,$(LIBS),$(if $(findstring /,$(i)),$(i)))) ifndef PROFILING SPECIAL_OCAMLFLAGS := $(OCAMLNCFLAGS) PLDFLAGS := else SPECIAL_OCAMLFLAGS := -p $(OCAMLNCFLAGS) PLDFLAGS := -p endif ifndef LIB_PACK_NAME SPECIAL_OCAMLFLAGS := $(OCAMLNCFLAGS) else SPECIAL_OCAMLFLAGS := -for-pack $(LIB_PACK_NAME) $(OCAMLNCFLAGS) endif REAL_IMPL := $(IMPL_CMX) REAL_IMPL_INTF := $(IMPLX_INTF) IMPL_SUF := .cmx override CPPFLAGS := -DNATIVE_CODE $(CPPFLAGS) DEPFLAGS := -native MAKE_DEPS := $(MLDEPS) $(NCDEPIS) ALL_LDFLAGS := $(PLDFLAGS) $(INCFLAGS) $(OCAMLLDFLAGS) \ $(OCAMLNLDFLAGS) $(COMMON_LDFLAGS) CAMLIDLDLLFLAGS := -opt ifndef CREATE_LIB ALL_LDFLAGS += $(LIBS:%=%.cmxa) else override CFLAGS := $(PIC_CFLAGS) $(CFLAGS) override CPPFLAGS := $(PIC_CPPFLAGS) $(CPPFLAGS) endif ifdef THREADS THREAD_FLAG := -thread ALL_LDFLAGS := $(THREAD_FLAG) $(ALL_LDFLAGS) ifndef CREATE_LIB ifndef REAL_OCAMLFIND ALL_LDFLAGS := unix.cmxa threads.cmxa $(ALL_LDFLAGS) endif endif endif endif export MAKE_DEPS ifdef ANNOTATE ANNOT_FLAG := -dtypes else endif ALL_OCAMLCFLAGS := $(THREAD_FLAG) $(ANNOT_FLAG) $(OCAMLFLAGS) \ $(INCFLAGS) $(SPECIAL_OCAMLFLAGS) ifdef make_deps -include $(MAKE_DEPS) PRE_TARGETS := endif ########################################################################### # USER RULES # Call "OCamlMakefile QUIET=" to get rid of all of the @'s. QUIET=@ # generates byte-code (default) byte-code: $(PRE_TARGETS) $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) $(BCRESULT) \ REAL_RESULT="$(BCRESULT)" make_deps=yes bc: byte-code byte-code-nolink: $(PRE_TARGETS) $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) nolink \ REAL_RESULT="$(BCRESULT)" make_deps=yes bcnl: byte-code-nolink top: $(PRE_TARGETS) $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) $(TOPRESULT) \ REAL_RESULT="$(BCRESULT)" make_deps=yes # generates native-code native-code: $(PRE_TARGETS) $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) $(NCRESULT) \ REAL_RESULT="$(NCRESULT)" \ REAL_OCAMLC="$(OCAMLOPT)" \ make_deps=yes nc: native-code native-code-nolink: $(PRE_TARGETS) $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) nolink \ REAL_RESULT="$(NCRESULT)" \ REAL_OCAMLC="$(OCAMLOPT)" \ make_deps=yes ncnl: native-code-nolink # generates byte-code libraries byte-code-library: $(PRE_TARGETS) $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) \ $(RES_CLIB) $(BCRESULT).cma \ REAL_RESULT="$(BCRESULT)" \ CREATE_LIB=yes \ make_deps=yes bcl: byte-code-library # generates native-code libraries native-code-library: $(PRE_TARGETS) $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) \ $(RES_CLIB) $(NCRESULT).cmxa \ REAL_RESULT="$(NCRESULT)" \ REAL_OCAMLC="$(OCAMLOPT)" \ CREATE_LIB=yes \ make_deps=yes ncl: native-code-library ifdef WIN32 # generates byte-code dll byte-code-dll: $(PRE_TARGETS) $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) \ $(RES_CLIB) $(BCRESULT).dll \ REAL_RESULT="$(BCRESULT)" \ make_deps=yes bcd: byte-code-dll # generates native-code dll native-code-dll: $(PRE_TARGETS) $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) \ $(RES_CLIB) $(NCRESULT).dll \ REAL_RESULT="$(NCRESULT)" \ REAL_OCAMLC="$(OCAMLOPT)" \ make_deps=yes ncd: native-code-dll endif # generates byte-code with debugging information debug-code: $(PRE_TARGETS) $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) $(BCRESULT) \ REAL_RESULT="$(BCRESULT)" make_deps=yes \ OCAMLFLAGS="-g $(OCAMLFLAGS)" \ OCAMLLDFLAGS="-g $(OCAMLLDFLAGS)" dc: debug-code debug-code-nolink: $(PRE_TARGETS) $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) nolink \ REAL_RESULT="$(BCRESULT)" make_deps=yes \ OCAMLFLAGS="-g $(OCAMLFLAGS)" \ OCAMLLDFLAGS="-g $(OCAMLLDFLAGS)" dcnl: debug-code-nolink # generates byte-code with debugging information (native code) debug-native-code: $(PRE_TARGETS) $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) $(NCRESULT) \ REAL_RESULT="$(NCRESULT)" make_deps=yes \ REAL_OCAMLC="$(OCAMLOPT)" \ OCAMLFLAGS="-g $(OCAMLFLAGS)" \ OCAMLLDFLAGS="-g $(OCAMLLDFLAGS)" dnc: debug-native-code debug-native-code-nolink: $(PRE_TARGETS) $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) nolink \ REAL_RESULT="$(NCRESULT)" make_deps=yes \ REAL_OCAMLC="$(OCAMLOPT)" \ OCAMLFLAGS="-g $(OCAMLFLAGS)" \ OCAMLLDFLAGS="-g $(OCAMLLDFLAGS)" dncnl: debug-native-code-nolink # generates byte-code libraries with debugging information debug-code-library: $(PRE_TARGETS) $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) \ $(RES_CLIB) $(BCRESULT).cma \ REAL_RESULT="$(BCRESULT)" make_deps=yes \ CREATE_LIB=yes \ OCAMLFLAGS="-g $(OCAMLFLAGS)" \ OCAMLLDFLAGS="-g $(OCAMLLDFLAGS)" dcl: debug-code-library # generates byte-code libraries with debugging information (native code) debug-native-code-library: $(PRE_TARGETS) $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) \ $(RES_CLIB) $(NCRESULT).cma \ REAL_RESULT="$(NCRESULT)" make_deps=yes \ REAL_OCAMLC="$(OCAMLOPT)" \ CREATE_LIB=yes \ OCAMLFLAGS="-g $(OCAMLFLAGS)" \ OCAMLLDFLAGS="-g $(OCAMLLDFLAGS)" dncl: debug-native-code-library # generates byte-code for profiling profiling-byte-code: $(PRE_TARGETS) $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) $(BCRESULT) \ REAL_RESULT="$(BCRESULT)" PROFILING="y" \ make_deps=yes pbc: profiling-byte-code # generates native-code profiling-native-code: $(PRE_TARGETS) $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) $(NCRESULT) \ REAL_RESULT="$(NCRESULT)" \ REAL_OCAMLC="$(OCAMLOPT)" \ PROFILING="y" \ make_deps=yes pnc: profiling-native-code # generates byte-code libraries profiling-byte-code-library: $(PRE_TARGETS) $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) \ $(RES_CLIB) $(BCRESULT).cma \ REAL_RESULT="$(BCRESULT)" PROFILING="y" \ CREATE_LIB=yes \ make_deps=yes pbcl: profiling-byte-code-library # generates native-code libraries profiling-native-code-library: $(PRE_TARGETS) $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) \ $(RES_CLIB) $(NCRESULT).cmxa \ REAL_RESULT="$(NCRESULT)" PROFILING="y" \ REAL_OCAMLC="$(OCAMLOPT)" \ CREATE_LIB=yes \ make_deps=yes pncl: profiling-native-code-library # packs byte-code objects pack-byte-code: $(PRE_TARGETS) $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) $(BCRESULT).cmo \ REAL_RESULT="$(BCRESULT)" \ PACK_LIB=yes make_deps=yes pabc: pack-byte-code # packs native-code objects pack-native-code: $(PRE_TARGETS) $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) \ $(NCRESULT).cmx $(NCRESULT).o \ REAL_RESULT="$(NCRESULT)" \ REAL_OCAMLC="$(OCAMLOPT)" \ PACK_LIB=yes make_deps=yes panc: pack-native-code # generates HTML-documentation htdoc: $(DOC_DIR)/$(RESULT)/html/index.html # generates Latex-documentation ladoc: $(DOC_DIR)/$(RESULT)/latex/doc.tex # generates PostScript-documentation psdoc: $(DOC_DIR)/$(RESULT)/latex/doc.ps # generates PDF-documentation pdfdoc: $(DOC_DIR)/$(RESULT)/latex/doc.pdf # generates all supported forms of documentation doc: htdoc ladoc psdoc pdfdoc ########################################################################### # LOW LEVEL RULES $(REAL_RESULT): $(REAL_IMPL_INTF) $(OBJ_LINK) $(EXTRADEPS) $(RESULTDEPS) $(REAL_OCAMLFIND) $(REAL_OCAMLC) \ $(OCAML_FIND_PACKAGES) $(OCAML_FIND_LINKPKG) \ $(ALL_LDFLAGS) $(OBJS_LIBS) -o $@$(EXE) \ $(REAL_IMPL) nolink: $(REAL_IMPL_INTF) $(OBJ_LINK) ifdef WIN32 $(REAL_RESULT).dll: $(REAL_IMPL_INTF) $(OBJ_LINK) $(CAMLIDLDLL) $(CAMLIDLDLLFLAGS) $(OBJ_LINK) $(CLIBS) \ -o $@ $(REAL_IMPL) endif %$(TOPSUFFIX): $(REAL_IMPL_INTF) $(OBJ_LINK) $(EXTRADEPS) $(REAL_OCAMLFIND) $(OCAMLMKTOP) \ $(OCAML_FIND_PACKAGES) $(OCAML_FIND_LINKPKG) \ $(ALL_LDFLAGS) $(OBJS_LIBS) -o $@$(EXE) \ $(REAL_IMPL) .SUFFIXES: .mli .ml .cmi .cmo .cmx .cma .cmxa .$(EXT_OBJ) \ .mly .di .d .$(EXT_LIB) .idl %.oxridl .c .m .$(EXT_CXX) .h .so \ .rep .zog .glade ifndef STATIC ifdef MINGW $(DLLSONAME): $(OBJ_LINK) $(CC) $(CFLAGS) $(CFLAGS_WIN32) $(OBJ_LINK) -shared -o $@ \ -Wl,--whole-archive $(wildcard $(foreach dir,$(LIBDIRS),$(CLIBS:%=$(dir)/lib%.a))) \ $(OCAMLLIBPATH)/ocamlrun.a \ -Wl,--export-all-symbols \ -Wl,--no-whole-archive else ifdef MSVC $(DLLSONAME): $(OBJ_LINK) link /NOLOGO /DLL /OUT:$@ $(OBJ_LINK) \ $(wildcard $(foreach dir,$(LIBDIRS),$(CLIBS:%=$(dir)/%.lib))) \ $(OCAMLLIBPATH)/ocamlrun.lib else $(DLLSONAME): $(OBJ_LINK) $(OCAMLMKLIB) $(INCFLAGS) $(CLIBFLAGS) \ -o $(CLIB_BASE) $(OBJ_LINK) $(CLIBS:%=-l%) $(CFRAMEWORKS:%=-framework %) \ $(OCAMLMKLIB_FLAGS) endif endif endif ifndef LIB_PACK_NAME $(RESULT).cma: $(REAL_IMPL_INTF) $(MAKEDLL) $(EXTRADEPS) $(RESULTDEPS) $(REAL_OCAMLFIND) $(REAL_OCAMLC) -a $(ALL_LDFLAGS) $(OBJS_LIBS) -o $@ $(REAL_IMPL) $(RESULT).cmxa $(RESULT).$(EXT_LIB): $(REAL_IMPL_INTF) $(EXTRADEPS) $(RESULTDEPS) $(REAL_OCAMLFIND) $(OCAMLOPT) -a $(ALL_LDFLAGS) $(OBJS_LIBS) -o $@ $(REAL_IMPL) else # Packing a bytecode library ifdef BYTE_OCAML $(LIB_PACK_NAME).cmi $(LIB_PACK_NAME).cmo: $(REAL_IMPL_INTF) $(REAL_OCAMLFIND) $(REAL_OCAMLC) -pack -o $(LIB_PACK_NAME).cmo $(OCAMLLDFLAGS) $(REAL_IMPL) # Packing into a unit which can be transformed into a library # Remember the .ml's must have been compiled with -for-pack $(LIB_PACK_NAME) else $(LIB_PACK_NAME).cmi $(LIB_PACK_NAME).cmx: $(REAL_IMPL_INTF) $(REAL_OCAMLFIND) $(OCAMLOPT) -pack -o $(LIB_PACK_NAME).cmx $(OCAMLLDFLAGS) $(REAL_IMPL) endif $(RESULT).cma: $(LIB_PACK_NAME).cmi $(LIB_PACK_NAME).cmo $(MAKEDLL) $(EXTRADEPS) $(RESULTDEPS) $(REAL_OCAMLFIND) $(REAL_OCAMLC) -a $(ALL_LDFLAGS) $(OBJS_LIBS) -o $@ $(LIB_PACK_NAME).cmo $(RESULT).cmxa $(RESULT).$(EXT_LIB): $(LIB_PACK_NAME).cmi $(LIB_PACK_NAME).cmx $(EXTRADEPS) $(RESULTDEPS) $(REAL_OCAMLFIND) $(OCAMLOPT) -a $(filter-out -custom, $(ALL_LDFLAGS)) $(OBJS_LIBS) -o $@ $(LIB_PACK_NAME).cmx endif $(RES_CLIB): $(OBJ_LINK) ifndef MSVC ifneq ($(strip $(OBJ_LINK)),) $(AR) rcs $@ $(OBJ_LINK) endif else ifneq ($(strip $(OBJ_LINK)),) lib -nologo -debugtype:cv -out:$(RES_CLIB) $(OBJ_LINK) endif endif .mli.cmi: $(EXTRADEPS) $(QUIET)pp=`sed -n -e '/^#/d' -e 's/(\*pp \([^*]*\) \*)/\1/p;q' $<`; \ if [ -z "$$pp" ]; then \ $(ECHO) $(REAL_OCAMLFIND) $(INTF_OCAMLC) $(OCAML_FIND_PACKAGES) \ -c $(THREAD_FLAG) $(ANNOT_FLAG) \ $(OCAMLFLAGS) $(INCFLAGS) $<; \ $(REAL_OCAMLFIND) $(INTF_OCAMLC) $(OCAML_FIND_PACKAGES) \ -c $(THREAD_FLAG) $(ANNOT_FLAG) \ $(OCAMLFLAGS) $(INCFLAGS) $<; \ else \ $(ECHO) $(REAL_OCAMLFIND) $(INTF_OCAMLC) $(OCAML_FIND_PACKAGES) \ -c -pp \"$$pp $(PPFLAGS)\" $(THREAD_FLAG) $(ANNOT_FLAG) \ $(OCAMLFLAGS) $(INCFLAGS) $<; \ $(REAL_OCAMLFIND) $(INTF_OCAMLC) $(OCAML_FIND_PACKAGES) \ -c -pp "$$pp $(PPFLAGS)" $(THREAD_FLAG) $(ANNOT_FLAG) \ $(OCAMLFLAGS) $(INCFLAGS) $<; \ fi .ml.cmi .ml.$(EXT_OBJ) .ml.cmx .ml.cmo: $(EXTRADEPS) $(QUIET)pp=`sed -n -e '/^#/d' -e 's/(\*pp \([^*]*\) \*)/\1/p;q' $<`; \ if [ -z "$$pp" ]; then \ $(ECHO) $(REAL_OCAMLFIND) $(REAL_OCAMLC) $(OCAML_FIND_PACKAGES) \ -c $(ALL_OCAMLCFLAGS) $<; \ $(REAL_OCAMLFIND) $(REAL_OCAMLC) $(OCAML_FIND_PACKAGES) \ -c $(ALL_OCAMLCFLAGS) $<; \ else \ $(ECHO) $(REAL_OCAMLFIND) $(REAL_OCAMLC) $(OCAML_FIND_PACKAGES) \ -c -pp \"$$pp $(PPFLAGS)\" $(ALL_OCAMLCFLAGS) $<; \ $(REAL_OCAMLFIND) $(REAL_OCAMLC) $(OCAML_FIND_PACKAGES) \ -c -pp "$$pp $(PPFLAGS)" $(ALL_OCAMLCFLAGS) $<; \ fi ifdef PACK_LIB $(REAL_RESULT).cmo $(REAL_RESULT).cmx $(REAL_RESULT).o: $(REAL_IMPL_INTF) $(OBJ_LINK) $(EXTRADEPS) $(REAL_OCAMLFIND) $(REAL_OCAMLC) -pack $(ALL_LDFLAGS) \ $(OBJS_LIBS) -o $@ $(REAL_IMPL) endif .PRECIOUS: %.ml %.ml: %.mll $(OCAMLLEX) $(LFLAGS) $< .PRECIOUS: %.ml %.mli %.ml %.mli: %.mly $(OCAMLYACC) $(YFLAGS) $< $(QUIET)pp=`sed -n -e 's/.*(\*pp \([^*]*\) \*).*/\1/p;q' $<`; \ if [ ! -z "$$pp" ]; then \ mv $*.ml $*.ml.temporary; \ echo "(*pp $$pp $(PPFLAGS)*)" > $*.ml; \ cat $*.ml.temporary >> $*.ml; \ rm $*.ml.temporary; \ mv $*.mli $*.mli.temporary; \ echo "(*pp $$pp $(PPFLAGS)*)" > $*.mli; \ cat $*.mli.temporary >> $*.mli; \ rm $*.mli.temporary; \ fi .PRECIOUS: %.ml %.ml: %.rep $(CAMELEON_REPORT) $(CAMELEON_REPORT_FLAGS) -gen $< .PRECIOUS: %.ml %.ml: %.zog $(CAMELEON_ZOGGY) $(CAMELEON_ZOGGY_FLAGS) -impl $< > $@ .PRECIOUS: %.ml %.ml: %.glade $(OCAML_GLADECC) $(OCAML_GLADECC_FLAGS) $< > $@ .PRECIOUS: %.ml %.mli %.ml %.mli: %.oxridl $(OXRIDL) $< .PRECIOUS: %.ml %.mli %_stubs.c %.h %.ml %.mli %_stubs.c %.h: %.idl $(CAMLIDL) $(MAYBE_IDL_HEADER) $(IDLFLAGS) \ $(CAMLIDLFLAGS) $< $(QUIET)if [ $(NOIDLHEADER) ]; then touch $*.h; fi .c.$(EXT_OBJ): $(OCAMLC) -c -cc "$(CC)" -ccopt "$(CFLAGS) \ $(CPPFLAGS) $(CPPFLAGS_WIN32) \ $(CFLAGS_WIN32) $(CINCFLAGS) $(CFLAG_O)$@ " $< .m.$(EXT_OBJ): $(CC) -c $(CFLAGS) $(CINCFLAGS) $(CPPFLAGS) \ -I'$(OCAMLLIBPATH)' \ $< $(CFLAG_O)$@ .$(EXT_CXX).$(EXT_OBJ): $(CXX) -c $(CXXFLAGS) $(CINCFLAGS) $(CPPFLAGS) \ -I'$(OCAMLLIBPATH)' \ $< $(CFLAG_O)$@ $(MLDEPDIR)/%.d: %.ml $(QUIET)if [ ! -d $(@D) ]; then mkdir -p $(@D); fi $(QUIET)pp=`sed -n -e '/^#/d' -e 's/(\*pp \([^*]*\) \*)/\1/p;q' $<`; \ if [ -z "$$pp" ]; then \ $(ECHO) $(REAL_OCAMLFIND) $(OCAMLDEP) $(OCAML_DEP_PACKAGES) \ $(DINCFLAGS) $< \> $@; \ $(REAL_OCAMLFIND) $(OCAMLDEP) $(OCAML_DEP_PACKAGES) \ $(DINCFLAGS) $< > $@; \ else \ $(ECHO) $(REAL_OCAMLFIND) $(OCAMLDEP) $(OCAML_DEP_PACKAGES) \ -pp \"$$pp $(PPFLAGS)\" $(DINCFLAGS) $< \> $@; \ $(REAL_OCAMLFIND) $(OCAMLDEP) $(OCAML_DEP_PACKAGES) \ -pp "$$pp $(PPFLAGS)" $(DINCFLAGS) $< > $@; \ fi $(BCDIDIR)/%.di $(NCDIDIR)/%.di: %.mli $(QUIET)if [ ! -d $(@D) ]; then mkdir -p $(@D); fi $(QUIET)pp=`sed -n -e '/^#/d' -e 's/(\*pp \([^*]*\) \*)/\1/p;q' $<`; \ if [ -z "$$pp" ]; then \ $(ECHO) $(REAL_OCAMLFIND) $(OCAMLDEP) $(DEPFLAGS) $(DINCFLAGS) $< \> $@; \ $(REAL_OCAMLFIND) $(OCAMLDEP) $(DEPFLAGS) $(DINCFLAGS) $< > $@; \ else \ $(ECHO) $(REAL_OCAMLFIND) $(OCAMLDEP) $(DEPFLAGS) \ -pp \"$$pp $(PPFLAGS)\" $(DINCFLAGS) $< \> $@; \ $(REAL_OCAMLFIND) $(OCAMLDEP) $(DEPFLAGS) \ -pp "$$pp $(PPFLAGS)" $(DINCFLAGS) $< > $@; \ fi $(DOC_DIR)/$(RESULT)/html: mkdir -p $@ $(DOC_DIR)/$(RESULT)/html/index.html: $(DOC_DIR)/$(RESULT)/html $(DOC_FILES) rm -rf $= cbf_mb_h) * need to test multi-request with non exclusive resource selection (since >= cbf_mb_h) * Scalability performance testing * compute mode support * timesharing, container, faireshare, ComputeMode, Postgresql... * add always SCHEDULER_RESOURCES_ALWAYS_ASSIGNED_TYPE (is it really needed ?) * errors logging (at least same error support as provide in perl scheduler) * how to emulate resource order ?, need of specifique scheduler for besteffort or use perl scheduler for besteffort ??? * Do we need global scheduling timeout ??? (at timeout expiration we can save succesfully scheduled job, can also be the way for incremental assignement saving...) * dump first k ready launchable jobs (for performance /reactivity issue) * nb_asked_resource = 0 raise an error (>= cbf_mb_h) * time_end_slot(n-1) = time_start_slot(n) for same resource must be time_start_slot(n) = time_end_slot(n-1) + 1 ! rajouter + 1 au walltime des jobs + une garde temporelle ?!! (can be address by the use of security_time_overhead ?) To test: * DB postgresql / mysql * besteffort * container * available_upto (???) * multi-resource-type * multi-request with non exclusive resource selection ToDo: * switch name to kamelot * complete fairsharing * test_unit: better compilation process * Ounit (cf archive) * test sub_intevals * With 64 bits machine we can use ocaml's int with 63 bits instead of Int64. Done: * container * Support of postgresql * Preliminary performance comparaison (perl version timesharing only scheduler from oar-server_2.3.4-1_all.deb against cbf_mh_h). Perl scheduler doesn't seem to scale with number of resources) * modify itv_intersect in Interval / remove itv2str, itvs2str (>= cbf_mh_h) * multi-resource-type (since >= cbf_mh_h) ( * multi-request with non exclusive resource selection (since >= cbf_mh_h) Remarks and misc: * http://martin.jambon.free.fr/ocaml.htm Bugs: Debug: make bc ocamlmktop -I /usr/lib/ocaml/ -o yop str.cma unix.cma ../common/interval.cmo ../common/helpers.cmo ../common/conf.cmo types.cmo ../common/hierarchy.cmo ./simple_cbf_mb_h_ct.cmo rlwrap ./yop -I ../common -I . ./oar-2.5.2/sources/extra/oar_drmaa/0000755000175000017500000000000011757171206015314 5ustar plbplb./oar-2.5.2/sources/extra/oar_drmaa/util.h0000644000175000017500000000265611757171206016453 0ustar plbplb/* $Id: util.h 323 2010-09-21 21:31:29Z mmatloka $ */ /* * FedStage DRMAA for PBS Pro * Copyright (C) 2006-2009 FedStage Systems * * 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 3 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* * Adapted from pbs_drmaa/util.h */ #ifndef __OAR_DRMAA__UTIL_H #define __OAR_DRMAA__UTIL_H #ifdef HAVE_CONFIG_H # include #endif void oardrmaa_exc_raise_oar( const char *function ); int oardrmaa_map_oar_errno( int _oar_errno ); struct attrl; void oardrmaa_free_attrl( struct attrl *list ); void oardrmaa_dump_attrl( const struct attrl *attribute_list, const char *prefix ); /** * Writes temporary file. * @param content Buffer with content to write. * @param len Buffer's length. * @return Path to temporary file. */ char * oardrmaa_write_tmpfile( const char *content, size_t len ); #endif /* __OAR_DRMAA__UTIL_H */ ./oar-2.5.2/sources/extra/oar_drmaa/util.c0000644000175000017500000002110411757171206016433 0ustar plbplb/* $Id: util.c 323 2010-09-21 21:31:29Z mmatloka $ */ /* * FedStage DRMAA for PBS Pro * Copyright (C) 2006-2007 FedStage Systems * * 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 3 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* * Adapted from pbs_drmaa/util.c */ /** * @file oar_drmaa/util.c * OAR DRMAA utilities. */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #ifndef lint static char rcsid[] # ifdef __GNUC__ __attribute__ ((unused)) # endif = "$Id: util.c 323 2010-09-21 21:31:29Z mmatloka $"; #endif void oardrmaa_dump_attrl( const struct attrl *attribute_list, const char *prefix ) { const struct attrl *i; if( prefix == NULL ) prefix = ""; for( i = attribute_list; i != NULL; i = i->next ) fsd_log_info(("var:%s value:%s \n",i->name, i->value ? i->value : "null")); } void oardrmaa_free_attrl( struct attrl *attr ) { while( attr != NULL ) { struct attrl *p = attr; attr = attr->next; fsd_free( p->name ); fsd_free( p->value ); fsd_free( p->resource ); fsd_free( p ); } } void oardrmaa_exc_raise_oar( const char *function ) { int _oar_errno; int fsd_errno; const char *message = NULL; _oar_errno = oar_errno; /* * Gathering error messages differ between PBS forks. * - OpenPBS - ... * - Torque - pbse_to_txt takes PBS error code (stored in pbs_errno) * and returns corresponding error message. * - PBS Pro - stores errno of last operation inside pbs_errno variable; * pbse_to_txt always return NULL. * All of them define pbs_geterrmsg which returns last error message * for given connection. */ /* XXX: PBSPro has some link problems with pbse_to_txt function */ message = oar_errno_to_txt( oar_errno ); fsd_errno = oardrmaa_map_oar_errno( _oar_errno ); fsd_log_error(( "call to %s returned with error %d:%s mapped to %d:%s", function, _oar_errno, message, fsd_errno, fsd_strerror(fsd_errno) )); fsd_exc_raise_fmt( fsd_errno, "%s: %s", function, message ); } /** Maps OAR error code into DMRAA code. */ /* TODO */ int oardrmaa_map_oar_errno( int _oar_errno ) { fsd_log_enter(( "(oar_errno=%d)", _oar_errno )); switch( _oar_errno ) { case OAR_ERRNO_NONE: /* no error */ return FSD_ERRNO_SUCCESS; case OAR_ERRNO_UNKJOBID: /* Unknown Job Identifier */ return FSD_DRMAA_ERRNO_INVALID_JOB; case OAR_ERRNO_NOATTR: /* Undefined Attribute */ case OAR_ERRNO_ATTRRO: /* attempt to set READ ONLY attribute */ case OAR_ERRNO_IVALREQ: /* Invalid request */ case OAR_ERRNO_UNKREQ: /* Unknown batch request */ return FSD_ERRNO_INTERNAL_ERROR; case OAR_ERRNO_PERM: /* No permission */ case OAR_ERRNO_BADHOST: /* access from host not allowed */ return FSD_ERRNO_AUTHZ_FAILURE; case OAR_ERRNO_JOBEXIST: /* job already exists */ case OAR_ERRNO_SVRDOWN: /* req rejected -server shutting down */ case OAR_ERRNO_EXECTHERE: /* cannot execute there */ case OAR_ERRNO_NOSUP: /* Feature/function not supported */ case OAR_ERRNO_EXCQRESC: /* Job exceeds Queue resource limits */ case OAR_ERRNO_QUENODFLT: /* No Default Queue Defined */ case OAR_ERRNO_NOTSNODE: /* no time-shared nodes */ return FSD_ERRNO_DENIED_BY_DRM; case OAR_ERRNO_SYSTEM: /* system error occurred */ case OAR_ERRNO_INTERNAL: /* internal server error occurred */ case OAR_ERRNO_REGROUTE: /* parent job of dependent in rte que */ case OAR_ERRNO_UNKSIG: /* unknown signal name */ return FSD_ERRNO_INTERNAL_ERROR; case OAR_ERRNO_BADATVAL: /* bad attribute value */ case OAR_ERRNO_BADATLST: /* Bad attribute list structure */ case OAR_ERRNO_BADUSER: /* Bad user - no password entry */ case OAR_ERRNO_BADGRP: /* Bad Group specified */ case OAR_ERRNO_BADACCT: /* Bad Account attribute value */ case OAR_ERRNO_UNKQUE: /* Unknown queue name */ case OAR_ERRNO_UNKRESC: /* Unknown resource */ case OAR_ERRNO_UNKNODEATR: /* node-attribute not recognized */ case OAR_ERRNO_BADNDATVAL: /* Bad node-attribute value */ case OAR_ERRNO_BADDEPEND: /* Invalid dependency */ case OAR_ERRNO_DUPLIST: /* Duplicate entry in List */ return FSD_ERRNO_INVALID_VALUE; case OAR_ERRNO_MODATRRUN: /* Cannot modify attrib in run state */ case OAR_ERRNO_BADSTATE: /* request invalid for job state */ case OAR_ERRNO_BADCRED: /* Invalid Credential in request */ case OAR_ERRNO_EXPIRED: /* Expired Credential in request */ case OAR_ERRNO_QUNOENB: /* Queue not enabled */ return FSD_ERRNO_INTERNAL_ERROR; case OAR_ERRNO_QACESS: /* No access permission for queue */ return FSD_ERRNO_AUTHZ_FAILURE; case OAR_ERRNO_HOPCOUNT: /* Max hop count exceeded */ case OAR_ERRNO_QUEEXIST: /* Queue already exists */ case OAR_ERRNO_ATTRTYPE: /* incompatable queue attribute type */ return FSD_ERRNO_INTERNAL_ERROR; # ifdef OAR_ERRNO_QUEBUSY case OAR_ERRNO_QUEBUSY: /* Queue Busy (not empty) */ # endif case OAR_ERRNO_MAXQUED: /* Max number of jobs in queue */ case OAR_ERRNO_NOCONNECTS: /* No free connections */ case OAR_ERRNO_TOOMANY: /* Too many submit retries */ case OAR_ERRNO_RESCUNAV: /* Resources temporarily unavailable */ return FSD_ERRNO_TRY_LATER; case 111: case OAR_ERRNO_PROTOCOL: /* Protocol (ASN.1) error */ case OAR_ERRNO_DISPROTO: /* Bad DIS based Request Protocol */ return FSD_ERRNO_DRM_COMMUNICATION_FAILURE; #if 0 case OAR_ERRNO_QUENBIG: /* Queue name too long */ case OAR_ERRNO_QUENOEN: /* Cannot enable queue,needs add def */ case OAR_ERRNO_NOSERVER: /* No server to connect to */ case OAR_ERRNO_NORERUN: /* Job Not Rerunnable */ case OAR_ERRNO_ROUTEREJ: /* Route rejected by all destinations */ case OAR_ERRNO_ROUTEEXPD: /* Time in Route Queue Expired */ case OAR_ERRNO_MOMREJECT: /* Request to MOM failed */ case OAR_ERRNO_BADSCRIPT: /* (qsub) cannot access script file */ case OAR_ERRNO_STAGEIN: /* Stage In of files failed */ case OAR_ERRNO_CKPBSY: /* Checkpoint Busy, may be retries */ case OAR_ERRNO_EXLIMIT: /* Limit exceeds allowable */ case OAR_ERRNO_ALRDYEXIT: /* Job already in exit state */ case OAR_ERRNO_NOCOPYFILE: /* Job files not copied */ case OAR_ERRNO_CLEANEDOUT: /* unknown job id after clean init */ case OAR_ERRNO_NOSYNCMSTR: /* No Master in Sync Set */ case OAR_ERRNO_SISREJECT: /* sister rejected */ case OAR_ERRNO_SISCOMM: /* sister could not communicate */ case OAR_ERRNO_CKPSHORT: /* not all tasks could checkpoint */ case OAR_ERRNO_UNKNODE: /* Named node is not in the list */ case OAR_ERRNO_NONODES: /* Server has no node list */ case OAR_ERRNO_NODENBIG: /* Node name is too big */ case OAR_ERRNO_NODEEXIST: /* Node name already exists */ case OAR_ERRNO_MUTUALEX: /* State values are mutually exclusive */ case OAR_ERRNO_GMODERR: /* Error(s) during global modification of nodes */ case OAR_ERRNO_NORELYMOM: /* could not contact Mom */ return FSD_ERRNO_INTERNAL_ERROR; #endif default: return FSD_ERRNO_INTERNAL_ERROR; } } ./oar-2.5.2/sources/extra/oar_drmaa/test_oar_restapi.c0000644000175000017500000000175411757171206021036 0ustar plbplb/* gcc */ /* gcc -lcurl -o test_oar_restapi test_oar_restapi.c */ #include #include int main(void) { CURL *curl_handle; CURLcode res; long http_code = 0; struct curl_slist *headers = NULL; char rest_req[] = "{\"script_path\":\"sleep\"}"; curl_handle = curl_easy_init(); if(curl_handle) { headers = curl_slist_append(headers, "Content-Type: application/json"); curl_easy_setopt(curl_handle, CURLOPT_URL, "http://localhost/oarapi/jobs.json"); curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headers); curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, rest_req); printf("YOP\n"); res = curl_easy_perform(curl_handle); curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &http_code); printf("res: %d error: %s\n",res, curl_easy_strerror(res)); printf("http code %ld\n",http_code); /* always cleanup */ curl_easy_cleanup(curl_handle); } return 0; } ./oar-2.5.2/sources/extra/oar_drmaa/test_drmaa.c0000644000175000017500000001227611757171206017613 0ustar plbplb/* simple test program based on different documentation gathered on the web to compile: gcc -g test_drmaa.c -L.libs -loardrmaa -o test_drmaa export LD_LIBRARY_PATH=.libs */ #include #include extern const char *drmaa_control_to_str( int action ); /* ??? */ void instance_handling() { char contact[DRMAA_CONTACT_BUFFER]; char drm_system[DRMAA_DRM_SYSTEM_BUFFER]; char drmaa_impl[DRMAA_DRM_SYSTEM_BUFFER]; unsigned int major = 0; unsigned int minor = 0; drmaa_get_contact (contact, DRMAA_CONTACT_BUFFER, NULL,0); drmaa_get_DRM_system (drm_system, DRMAA_DRM_SYSTEM_BUFFER,NULL, 0); drmaa_get_DRMAA_implementation (drm_system, DRMAA_DRM_SYSTEM_BUFFER, NULL, 0); drmaa_version (&major, &minor, NULL, 0); printf ("Contact: %s\n", contact); printf ("DRM System: %s\n", drm_system); printf ("DRMAA Implementation: %s\n", drmaa_impl); printf ("Version: %d.%d\n", major, minor); } int main () { char error[DRMAA_ERROR_STRING_BUFFER]; int errnum = 0; char jobid[DRMAA_JOBNAME_BUFFER]; char jobid_2[DRMAA_JOBNAME_BUFFER]; /* Init Session */ /* drmaa_init | oar_connect */ errnum = drmaa_init ("localhost", error, DRMAA_ERROR_STRING_BUFFER); if (errnum != DRMAA_ERRNO_SUCCESS) { fprintf (stderr, "Couldn't init DRMAA library: %s\n", error); return 1; } /* Do Stuff */ /*instance handling*/ //instance_handling(); /* Allocate Job Template */ drmaa_job_template_t *jt = NULL; errnum = drmaa_allocate_job_template (&jt, error,DRMAA_ERROR_STRING_BUFFER); if (errnum != DRMAA_ERRNO_SUCCESS) { fprintf (stderr, "Couldn't allocate job template: %s\n", error); return 1; } char cmd1[]="/bin/sleep"; char cmd2[]="exit"; /* Job Templates */ errnum = drmaa_set_attribute (jt, DRMAA_REMOTE_COMMAND, cmd2, error, DRMAA_ERROR_STRING_BUFFER); if (errnum != DRMAA_ERRNO_SUCCESS) { fprintf (stderr, "Couldn't set remote command: %s\n", error); return 1; } /* set HOLD state at submission */ errnum = drmaa_set_attribute(jt, DRMAA_JS_STATE, DRMAA_SUBMISSION_STATE_HOLD, NULL, 0); const char *args[2] = {"5", NULL}; errnum = drmaa_set_vector_attribute (jt, DRMAA_V_ARGV, args, error, DRMAA_ERROR_STRING_BUFFER); if (errnum != DRMAA_ERRNO_SUCCESS) { fprintf (stderr, "Couldn't set remote command args: %s\n", error); return 1; } /* Run Job */ errnum = drmaa_run_job (jobid, DRMAA_JOBNAME_BUFFER, jt, error, DRMAA_ERROR_STRING_BUFFER); if (errnum != DRMAA_ERRNO_SUCCESS) { fprintf (stderr, "Couldn't run job: %s\n", error); return 1; } else { printf ("Your job has been submitted with id %s\n", jobid); } /* Run Job */ /* errnum = drmaa_run_job (jobid_2, DRMAA_JOBNAME_BUFFER, jt, error, DRMAA_ERROR_STRING_BUFFER); */ /* Get Job State */ /** * The possible values of * a program's staus are: * - DRMAA_PS_UNDETERMINED * - DRMAA_PS_QUEUED_ACTIVE * - DRMAA_PS_SYSTEM_ON_HOLD * - DRMAA_PS_USER_ON_HOLD * - DRMAA_PS_USER_SYSTEM_ON_HOLD * - DRMAA_PS_RUNNING * - DRMAA_PS_SYSTEM_SUSPENDED * - DRMAA_PS_USER_SUSPENDED * - DRMAA_PS_DONE * - DRMAA_PS_FAILED * Terminated jobs have a status of DRMAA_PS_FAILED. */ /* int remote_ps; errnum = drmaa_job_ps(jobid, &remote_ps, error, DRMAA_ERROR_STRING_BUFFER); printf("drmaa_job_ps: job_id: %s job_ps: %d\n",jobid,remote_ps); errnum = drmaa_job_ps(jobid, &remote_ps, error, DRMAA_ERROR_STRING_BUFFER); printf("2-drmaa_job_ps: job_id: %s job_ps: %d\n",jobid,remote_ps); */ /* Conrol Job */ /* * - DRMAA_CONTROL_SUSPEND 0 * - DRMAA_CONTROL_RESUME 1 * - DRMAA_CONTROL_HOLD 2 * - DRMAA_CONTROL_RELEASE 3 * - DRMAA_CONTROL_TERMINATE 4 */ /* Delete Job */ /* int i; for(i=0;i<0;i++) { errnum = drmaa_control(jobid, i, error, DRMAA_ERROR_STRING_BUFFER); if (errnum != DRMAA_ERRNO_SUCCESS) { fprintf (stderr, "Couldn't drmaa_control job: %s\n", error); } else { const char *str = drmaa_control_to_str(i); printf("drmaa_control: job_id: %s action: %s\n", jobid, str); } } */ sleep(7); printf("drmaa_wait \n"); int stat; drmaa_wait(DRMAA_JOB_IDS_SESSION_ANY, jobid, sizeof(jobid)-1, &stat, 20, NULL, NULL, 0); printf("drmaa_wait JobId: %s\n", jobid); /* const char **job_ids = NULL; job_ids = calloc( 2, sizeof(char *) ); job_ids[0]=jobid; printf("drmaa_synchronize\n"); errnum = drmaa_synchronize(job_ids, 200, 0, error, DRMAA_ERROR_STRING_BUFFER); */ /* TODO free fsb_free_vector(job_ids); */ if (errnum != DRMAA_ERRNO_SUCCESS) { fprintf (stderr, "Couldn't drmaa_synchronize: %s\n", error); } /* Delete Job Template*/ errnum = drmaa_delete_job_template (jt, error, DRMAA_ERROR_STRING_BUFFER); if (errnum != DRMAA_ERRNO_SUCCESS) { fprintf (stderr, "Couldn't delete job template: %s\n", error); return 1; } /* Exit Session */ /* drmaa_exit | oar_disconnect */ errnum = drmaa_exit (error, DRMAA_ERROR_STRING_BUFFER); if (errnum != DRMAA_ERRNO_SUCCESS) { fprintf (stderr, "Couldn't exit DRMAA library: %s\n", error); return 1; } return 0; /* return drmaa_init("hello"); return drmaa_exit() ; return 0; */ } ./oar-2.5.2/sources/extra/oar_drmaa/test-json-glib.c0000644000175000017500000000406311757171206020324 0ustar plbplb/* export LD_LIBRARY_PATH=/home/auguste/prog/json-glib-0.12.0/lib gcc -I include/json-glib-1.0/ -I /usr/include/glib-2.0/ -I /usr/lib/glib-2.0/include/ -Llib/ -ljson-glib-1.0 builder-test.c */ #include #include #include #include #include static const gchar *complex_object = "{ \"depth1\" : [ 1, { \"depth2\" : [ 2.3, [ null ], \"after array\" ], \"value2\" : true } ], \"object1\" : { } }\") == 0)"; int main (int argc, char *argv[]) { g_type_init (); /* only once ???*/ JsonBuilder *builder = json_builder_new (); JsonNode *node; JsonGenerator *generator; gsize length; gchar *data; json_builder_begin_object (builder); json_builder_set_member_name (builder, "depth1"); json_builder_begin_array (builder); json_builder_add_int_value (builder, 1); json_builder_begin_object (builder); json_builder_set_member_name (builder, "depth2"); json_builder_begin_array (builder); json_builder_add_double_value (builder, 2.3); json_builder_begin_array (builder); json_builder_add_null_value (builder); json_builder_end_array (builder); json_builder_add_string_value (builder, "after\"yop\" array"); json_builder_end_array (builder); /* depth2 */ json_builder_set_member_name (builder, "value2"); json_builder_add_boolean_value (builder, TRUE); json_builder_end_object (builder); json_builder_end_array (builder); /* depth1 */ json_builder_set_member_name (builder, "object1"); json_builder_begin_object (builder); json_builder_end_object (builder); json_builder_end_object (builder); node = json_builder_get_root (builder); g_object_unref (builder); generator = json_generator_new (); json_generator_set_root (generator, node); data = json_generator_to_data (generator, &length); if (g_test_verbose ()) { g_print ("Builder complex: %*s", (int)length, data); } /* g_assert (strncmp (data, complex_object, length) == 0); */ printf("data: %s \n",data); g_free (data); json_node_free (node); g_object_unref (generator); } ./oar-2.5.2/sources/extra/oar_drmaa/submit.h0000644000175000017500000000530111757171206016767 0ustar plbplb/* $Id: submit.h 338 2010-09-28 14:48:45Z mamonski $ */ /* * FedStage DRMAA for PBS Pro * Copyright (C) 2006-2009 FedStage Systems * * 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 3 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* * Adpated from pbs_drmaa/submit.h */ #ifndef __OAR_DRMAA__SUBMIT_H #define __OAR_DRMAA__SUBMIT_H #ifdef HAVE_CONFIG_H # include #endif #include typedef struct oardrmaa_submit_s oardrmaa_submit_t; oardrmaa_submit_t * oardrmaa_submit_new( fsd_drmaa_session_t *session, const fsd_template_t *job_template, int bulk_idx ); struct oardrmaa_submit_s { void (* destroy)( oardrmaa_submit_t *self ); char * (* submit)( oardrmaa_submit_t *self ); void (* eval)( oardrmaa_submit_t *self ); void (*apply_defaults)( oardrmaa_submit_t *self ); void (*apply_job_script)( oardrmaa_submit_t *self ); void (*apply_job_state)( oardrmaa_submit_t *self ); void (*apply_job_files)( oardrmaa_submit_t *self ); void (*apply_file_staging)( oardrmaa_submit_t *self ); void (*apply_job_resources)( oardrmaa_submit_t *self ); void (*apply_job_environment)( oardrmaa_submit_t *self ); void (*apply_email_notification)( oardrmaa_submit_t *self ); void (*apply_job_category)( oardrmaa_submit_t *self ); void (*apply_native_specification)( oardrmaa_submit_t *self, const char *native_specification ); void (*set)( oardrmaa_submit_t *self, const char *oar_attr, char *value, unsigned placeholders ); fsd_drmaa_session_t *session; const fsd_template_t *job_template; char *script_path; /* oar job parameter */ char *workdir; /* oar job parameter */ char *destination_queue; fsd_template_t *oar_job_attributes; fsd_expand_drmaa_ph_t *expand_ph; /* struct attrl *oar_attribs; */ /* uint32_t oar_attribs_bitset[ PBS_ATTRIBS_BITSET_SIZE ]; */ /*TODO: What is it ?*/ }; void oardrmaa_submit_apply_native_specification( oardrmaa_submit_t *self, const char *native_specification ); #endif /* __OAR_DRMAA__SUBMIT_H */ ./oar-2.5.2/sources/extra/oar_drmaa/submit.c0000644000175000017500000005302611757171206016771 0ustar plbplb/* $Id: submit.c 386 2011-01-06 18:13:33Z mamonski $ */ /* * FedStage DRMAA for /oar Pro * Copyright (C) 2006-2009 FedStage Systems * * 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 3 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* * Adapted from pbs_drmaa/submit.c */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef lint static char rcsid[] # ifdef __GNUC__ __attribute__ ((unused)) # endif = "$Id: submit.c 386 2011-01-06 18:13:33Z mamonski $"; #endif static void oardrmaa_submit_destroy( oardrmaa_submit_t *self ); static char * oardrmaa_submit_submit( oardrmaa_submit_t *self ); static void oardrmaa_submit_eval( oardrmaa_submit_t *self ); static void oardrmaa_submit_set( oardrmaa_submit_t *self, const char *oar_attr, char *value, unsigned placeholders ); static void oardrmaa_submit_apply_defaults( oardrmaa_submit_t *self ); static void oardrmaa_submit_apply_job_script( oardrmaa_submit_t *self ); static void oardrmaa_submit_apply_job_state( oardrmaa_submit_t *self ); static void oardrmaa_submit_apply_job_files( oardrmaa_submit_t *self ); static void oardrmaa_submit_apply_file_staging( oardrmaa_submit_t *self ); static void oardrmaa_submit_apply_job_resources( oardrmaa_submit_t *self ); static void oardrmaa_submit_apply_job_environment( oardrmaa_submit_t *self ); static void oardrmaa_submit_apply_email_notification( oardrmaa_submit_t *self ); static void oardrmaa_submit_apply_job_category( oardrmaa_submit_t *self ); oardrmaa_submit_t * oardrmaa_submit_new( fsd_drmaa_session_t *session, const fsd_template_t *job_template, int bulk_idx ) { oardrmaa_submit_t *volatile self = NULL; TRY { fsd_malloc( self, oardrmaa_submit_t ); self->session = session; self->job_template = job_template; self->script_path = NULL; self->workdir = NULL; self->destination_queue = NULL; self->oar_job_attributes = NULL; self->expand_ph = NULL; self->destroy = oardrmaa_submit_destroy; self->submit = oardrmaa_submit_submit; self->eval = oardrmaa_submit_eval; self->set = oardrmaa_submit_set; self->apply_defaults = oardrmaa_submit_apply_defaults; self->apply_job_category = oardrmaa_submit_apply_job_category; self->apply_job_script = oardrmaa_submit_apply_job_script; self->apply_job_state = oardrmaa_submit_apply_job_state; self->apply_job_files = oardrmaa_submit_apply_job_files; self->apply_file_staging = oardrmaa_submit_apply_file_staging; self->apply_job_resources = oardrmaa_submit_apply_job_resources; self->apply_job_environment = oardrmaa_submit_apply_job_environment; self->apply_email_notification = oardrmaa_submit_apply_email_notification; self->apply_native_specification = oardrmaa_submit_apply_native_specification; self->oar_job_attributes = oardrmaa_oar_template_new(); self->expand_ph = fsd_expand_drmaa_ph_new( NULL, NULL, (bulk_idx >= 0) ? fsd_asprintf("%d", bulk_idx) : NULL ); } EXCEPT_DEFAULT { if( self ) self->destroy( self ); } END_TRY return self; } void oardrmaa_submit_destroy( oardrmaa_submit_t *self ) { if( self-> script_path) fsd_free( self->script_path ); if( self-> workdir) fsd_free( self->workdir ); if( self->oar_job_attributes ) self->oar_job_attributes->destroy( self->oar_job_attributes ); if( self->expand_ph ) self->expand_ph->destroy( self->expand_ph ); fsd_free( self->destination_queue ); fsd_free( self ); } char * oardrmaa_submit_submit( oardrmaa_submit_t *self ) { volatile bool conn_lock = false; struct attrl *volatile oar_attr = NULL; char *volatile job_id = NULL; TRY { const fsd_template_t *oar_tmpl = self->oar_job_attributes; unsigned i; for( i = 0; i < OARDRMAA_N_OAR_ATTRIBUTES; i++ ) { const char *name = oar_tmpl->by_code( oar_tmpl, i )->name; if( name && name[0] != '!' && oar_tmpl->get_attr( oar_tmpl, name ) ) { struct attrl *p; const char *resource; const char *value; value = oar_tmpl->get_attr( oar_tmpl, name ); fsd_malloc( p, struct attrl ); memset( p, 0, sizeof(struct attrl) ); p->next = oar_attr; oar_attr = p; resource = strchr( name, '.' ); if( resource ) { p->name = fsd_strndup( name, resource-name ); p->resource = fsd_strdup( resource+1 ); } else p->name = fsd_strdup( name ); fsd_log_debug(("set attr: %s = %s", name, value)); p->value = fsd_strdup( value ); /* p->op = SET; */ /* TODO: can we remove ir ?*/ } } conn_lock = fsd_mutex_lock( &self->session->drm_connection_mutex ); retry: job_id = oar_submit( ((oardrmaa_session_t*)self->session)->oar_conn, (struct attropl*)oar_attr, self->script_path, self->workdir, self->destination_queue ); fsd_log_info(("oar_submit() =%s", job_id)); if( job_id == NULL ) { if (oar_errno == OAR_ERRNO_PROTOCOL || oar_errno == OAR_ERRNO_EXPIRED) { oardrmaa_session_t *oarself = (oardrmaa_session_t*)self->session; if (oarself->oar_conn >= 0 ) oar_disconnect( oarself->oar_conn ); sleep(1); oarself->oar_conn = oar_connect( oarself->super.contact ); if( oarself->oar_conn < 0 ) oardrmaa_exc_raise_oar( "oar_connect" ); else goto retry; } else { oardrmaa_exc_raise_oar( "oar_submit" ); } } conn_lock = fsd_mutex_unlock( &self->session->drm_connection_mutex ); } EXCEPT_DEFAULT { fsd_free( job_id ); fsd_exc_reraise(); } FINALLY { if( conn_lock ) conn_lock = fsd_mutex_unlock( &self->session->drm_connection_mutex ); if( oar_attr ) oardrmaa_free_attrl( oar_attr ); } END_TRY return job_id; } void oardrmaa_submit_eval( oardrmaa_submit_t *self ) { self->apply_defaults( self ); self->apply_job_category( self ); self->apply_job_script( self ); self->apply_job_state( self ); self->apply_job_files( self ); self->apply_file_staging( self ); self->apply_job_resources( self ); self->apply_job_environment( self ); self->apply_email_notification( self ); self->apply_native_specification( self, NULL ); } void oardrmaa_submit_set( oardrmaa_submit_t *self, const char *name, char *value, unsigned placeholders ) { fsd_template_t *oar_attr = self->oar_job_attributes; TRY { if( placeholders ) value = self->expand_ph->expand( self->expand_ph, value, placeholders ); oar_attr->set_attr( oar_attr, name, value ); } FINALLY { fsd_free( value ); } END_TRY } void oardrmaa_submit_apply_defaults( oardrmaa_submit_t *self ) { /* TODO default */ fsd_template_t *oar_attr = self->oar_job_attributes; /* oar_attr->set_attr( oar_attr, OARDRMAA_STDOUT_FILE, "prout" ); oar_attr->set_attr( oar_attr, OARDRMAA_JOB_NAME, "yopyop" ); */ /* oar_attr->set_attr( oar_attr, OARDRMAA_CHECKPOINT, "u" ); oar_attr->set_attr( oar_attr, OARDRMAA_KEEP_FILES, "n" ); oar_attr->set_attr( oar_attr, OARDRMAA_PRIORITY, "0" ); */ } void oardrmaa_submit_apply_job_script( oardrmaa_submit_t *self ) { const fsd_template_t *jt = self->job_template; char *script_path = NULL; size_t script_path_len; const char *executable; const char *wd; const char *const *argv; const char *input_path; const char *const *i; executable = jt->get_attr( jt, DRMAA_REMOTE_COMMAND ); wd = jt->get_attr( jt, DRMAA_WD ); argv = jt->get_v_attr( jt, DRMAA_V_ARGV ); input_path = jt->get_attr( jt, DRMAA_INPUT_PATH ); if( wd ) { self->workdir = fsd_strdup(wd); } if( input_path != NULL ) { if( input_path[0] == ':' ) input_path++; } if( executable == NULL ) fsd_exc_raise_code( FSD_DRMAA_ERRNO_INVALID_ATTRIBUTE_VALUE ); if (argv) { /* compute script_lengh length */ /* script_path_len = 1;*/ /* begining double quote */ /* TODO: to remove ? */ script_path_len += strlen(executable); for( i = argv; *i != NULL; i++ ) script_path_len += 3+strlen(*i); if( input_path != NULL ) script_path_len += strlen(" <") + strlen(input_path); /* script_path_len +=1;*/ /* ending double quote */ /* TODO: to remove ? */ fsd_calloc( script_path, script_path_len+1, char ); char *s; s = script_path; /*s += sprintf( s,"\""); */ /* begining double quote */ /* TODO: to remove ? */ s += sprintf( s,"%s",executable); for( i = argv; *i != NULL; i++ ) s += sprintf( s, " '%s'", *i ); if( input_path != NULL ) s += sprintf( s, " <%s", input_path ); /* s += sprintf( s,"\""); */ /* ending double quote */ /* TODO: to remove ? */ fsd_assert( s == script_path+script_path_len ); self->script_path = script_path; } else { self->script_path = fsd_strdup(executable); } } void oardrmaa_submit_apply_job_state( oardrmaa_submit_t *self ) { const fsd_template_t *jt = self->job_template; fsd_template_t *oar_attr = self->oar_job_attributes; const char *job_name = NULL; const char *submit_state = NULL; const char *drmaa_start_time = NULL; job_name = jt->get_attr( jt, DRMAA_JOB_NAME ); submit_state = jt->get_attr( jt, DRMAA_JS_STATE ); drmaa_start_time = jt->get_attr( jt, DRMAA_START_TIME ); if( job_name != NULL ) oar_attr->set_attr( oar_attr, OARDRMAA_JOB_NAME, job_name ); if( submit_state != NULL ) { printf("TODO hold_types\n"); if( !strcmp(submit_state, DRMAA_SUBMISSION_STATE_HOLD) ) oar_attr->set_attr( oar_attr, OARDRMAA_HOLD, "1" ); /* TODO const char *hold_types; if( !strcmp(submit_state, DRMAA_SUBMISSION_STATE_ACTIVE) ) hold_types = "n"; else if( !strcmp(submit_state, DRMAA_SUBMISSION_STATE_HOLD) ) hold_types = "u"; else fsd_exc_raise_fmt( FSD_ERRNO_INVALID_VALUE, "invalid value of %s attribute (%s|%s)", DRMAA_JS_STATE, DRMAA_SUBMISSION_STATE_ACTIVE, DRMAA_SUBMISSION_STATE_HOLD ); oar_attr->set_attr( oar_attr, OARDRMAA_HOLD_TYPES, hold_types ); */ } if( drmaa_start_time != NULL ) { /* time_t start_time; char oar_start_time[20]; struct tm start_time_tm; */ printf("NOT YET IMPLEMENTED --- TODO hold_types\n"); /* start_time = fsd_datetime_parse( drmaa_start_time ); localtime_r( &start_time, &start_time_tm ); sprintf( oar_start_time, "%04d%02d%02d%02d%02d.%02d", start_time_tm.tm_year + 1900, start_time_tm.tm_mon + 1, start_time_tm.tm_mday, start_time_tm.tm_hour, start_time_tm.tm_min, start_time_tm.tm_sec ); oar_attr->set_attr( oar_attr, OARDRMAA_EXECUTION_TIME, oar_start_time ); */ } } void oardrmaa_submit_apply_job_files( oardrmaa_submit_t *self ) { /* TODO STDOUT....*/ const fsd_template_t *jt = self->job_template; fsd_template_t *oar_attr = self->oar_job_attributes; const char *join_files; bool b_join_files; int i; for( i = 0; i < 2; i++ ) { const char *drmaa_name; const char *oar_name; const char *path; if( i == 0 ) { drmaa_name = DRMAA_OUTPUT_PATH; oar_name = OARDRMAA_STDOUT_FILE; } else { drmaa_name = DRMAA_ERROR_PATH; oar_name = OARDRMAA_STDERR_FILE; } path = jt->get_attr( jt, drmaa_name ); if( path != NULL ) { if( path[0] == ':' ) path++; self->set(self, oar_name, fsd_strdup(path), FSD_DRMAA_PH_HD | FSD_DRMAA_PH_WD | FSD_DRMAA_PH_INCR); } } /* TODO*/ /* join_files = jt->get_attr( jt, DRMAA_JOIN_FILES ); b_join_files = join_files != NULL && !strcmp(join_files,"1"); oar_attr->set_attr( oar_attr, OARDRMAA_JOIN_FILES, (b_join_files ? "y" : "n") ); */ } void oardrmaa_submit_apply_file_staging( oardrmaa_submit_t *self ) { /*TODO: Do we need it ? */ } void oardrmaa_submit_apply_job_resources( oardrmaa_submit_t *self ) { const fsd_template_t *jt = self->job_template; fsd_template_t *oar_attr = self->oar_job_attributes; const char *cpu_time_limit = NULL; const char *walltime_limit = NULL; /*TODO: In OAR we haven't a cpu_time_limit */ cpu_time_limit = jt->get_attr( jt, DRMAA_DURATION_HLIMIT ); walltime_limit = jt->get_attr( jt, DRMAA_WCT_HLIMIT ); if( cpu_time_limit ) { oar_attr->set_attr( oar_attr, "Resource_List.pcput", cpu_time_limit ); /*TODO no resource...*/ oar_attr->set_attr( oar_attr, "Resource_List.cput", cpu_time_limit ); } if( walltime_limit ) oar_attr->set_attr( oar_attr, "Resource_List.walltime", walltime_limit ); } void oardrmaa_submit_apply_job_environment( oardrmaa_submit_t *self ) { const fsd_template_t *jt = self->job_template; const char *const *env_v; env_v = jt->get_v_attr( jt, DRMAA_V_ENV); if (env_v) { char *env_c = NULL; int ii = 0, len = 0; ii = 0; while (env_v[ii]) { len += strlen(env_v[ii]) + 1; ii++; } fsd_calloc(env_c, len + 1, char); env_c[0] = '\0'; ii = 0; while (env_v[ii]) { strcat(env_c, env_v[ii]); strcat(env_c, ","); ii++; } env_c[strlen(env_c) -1 ] = '\0'; /*remove the last ',' */ /* TODO */ self->oar_job_attributes->set_attr(self->oar_job_attributes, "Variable_List", env_c); fsd_free(env_c); } } void oardrmaa_submit_apply_email_notification( oardrmaa_submit_t *self ) { /* TODO */ } void oardrmaa_submit_apply_job_category( oardrmaa_submit_t *self ) { const char *job_category = NULL; const char *category_spec = NULL; fsd_conf_option_t *value = NULL; job_category = self->job_template->get_attr( self->job_template, DRMAA_JOB_CATEGORY ); if( job_category == NULL || job_category[0] == '\0' ) job_category = "default"; value = fsd_conf_dict_get( self->session->job_categories, job_category ); if( value != NULL && value->type == FSD_CONF_STRING ) category_spec = value->val.string; if( category_spec != NULL ) self->apply_native_specification( self, category_spec ); } static void parse_resources(fsd_template_t *oar_attr,const char *resources) { char * volatile name = NULL; char *arg = NULL; char *value = NULL; char *ctxt = NULL; char * volatile resources_copy = fsd_strdup(resources); TRY { for (arg = strtok_r(resources_copy, ",", &ctxt); arg; arg = strtok_r(NULL, ",",&ctxt) ) { char *psep = strchr(arg, '='); if (psep) { *psep = '\0'; name = fsd_asprintf("Resource_List.%s", arg); value = ++psep; oar_attr->set_attr( oar_attr, name , value ); fsd_free(name); name = NULL; } else { fsd_exc_raise_fmt(FSD_DRMAA_ERRNO_INVALID_ATTRIBUTE_VALUE, "Invalid native specification: %s (Invalid resource specification: %s)", resources, arg); } } } FINALLY { fsd_free(name); fsd_free(resources_copy); } END_TRY } static void parse_additional_attr(fsd_template_t *oar_attr,const char *add_attr) { char * volatile name = NULL; char *arg = NULL; char *value = NULL; char *ctxt = NULL, *ctxt2 = NULL; char * volatile add_attr_copy = fsd_strdup(add_attr); TRY { for (arg = strtok_r(add_attr_copy, ",", &ctxt); arg; arg = strtok_r(NULL, ":",&ctxt) ) { name = fsd_strdup(strtok_r(arg, "=", &ctxt2)); value = strtok_r(NULL, "=", &ctxt2); oar_attr->set_attr( oar_attr, name , value ); fsd_free(name); name = NULL; } } FINALLY { fsd_free(name); fsd_free(add_attr_copy); } END_TRY } /* TODO TODO TODO */ void oardrmaa_submit_apply_native_specification( oardrmaa_submit_t *self, const char *native_specification ) { if( native_specification == NULL ) native_specification = self->job_template->get_attr( self->job_template, DRMAA_NATIVE_SPECIFICATION ); if( native_specification == NULL ) return; { fsd_iter_t * volatile args_list = fsd_iter_new(NULL, 0); fsd_template_t *oar_attr = self->oar_job_attributes; char *arg = NULL; volatile char * native_spec_copy = fsd_strdup(native_specification); char * ctxt = NULL; int opt = 0; TRY { for (arg = strtok_r(native_spec_copy, " \t", &ctxt); arg; arg = strtok_r(NULL, " \t",&ctxt) ) { if (!opt) { if ( (arg[0] != '-') || ((strlen(arg) != 2) && arg[2] != ' ' && arg[1] !='-' ) ) fsd_exc_raise_fmt(FSD_DRMAA_ERRNO_INVALID_ATTRIBUTE_VALUE, "Invalid native specification: %s", native_specification); if(arg[1] == '-') { parse_additional_attr(oar_attr, arg+2); } else { opt = arg[1]; } } else { switch (opt) { case 'W' : parse_additional_attr(oar_attr, arg); break; case 'N' : oar_attr->set_attr( oar_attr, "Job_Name" , arg ); break; case 'o' : oar_attr->set_attr( oar_attr, "Output_Path" , arg ); break; case 'e' : oar_attr->set_attr( oar_attr, "Error_Path" , arg ); break; case 'j' : oar_attr->set_attr( oar_attr, "Join_Path" , arg ); break; case 'm' : oar_attr->set_attr( oar_attr, "Mail_Points" , arg ); break; case 'a' : oar_attr->set_attr( oar_attr, "Execution_Time" , arg ); break; case 'h' : oar_attr->set_attr( oar_attr, "Hold_Types" , arg ); break; case 'A' : oar_attr->set_attr( oar_attr, "Account_Name" , arg ); break; case 'c' : oar_attr->set_attr( oar_attr, "Checkpoint" , arg ); break; case 'k' : oar_attr->set_attr( oar_attr, "Keep_Files" , arg ); break; case 'p' : oar_attr->set_attr( oar_attr, "Priority" , arg ); break; case 'q' : self->destination_queue = fsd_strdup( arg ); break; case 'r' : oar_attr->set_attr( oar_attr, "Rerunable" , arg ); break; case 'S' : oar_attr->set_attr( oar_attr, "Shell_Path_List" , arg ); break; case 'u' : oar_attr->set_attr( oar_attr, "User_List" , arg ); break; case 'v' : case 'V' : oar_attr->set_attr( oar_attr, "Variable_List" , arg ); break; case 'M' : oar_attr->set_attr( oar_attr, "Mail_Users" , arg ); break; case 'l' : parse_resources(oar_attr, arg); break; default : fsd_exc_raise_fmt(FSD_DRMAA_ERRNO_INVALID_ATTRIBUTE_VALUE, "Invalid native specification: %s (Unsupported option: -%c)", native_specification, opt); } opt = 0; } } if (opt) /* option without optarg */ fsd_exc_raise_fmt(FSD_DRMAA_ERRNO_INVALID_ATTRIBUTE_VALUE, "Invalid native specification: %s", native_specification); } FINALLY { oar_attr->set_attr( oar_attr, "submit_args", native_specification); args_list->destroy(args_list); fsd_free(native_spec_copy); } END_TRY } } ./oar-2.5.2/sources/extra/oar_drmaa/session.h0000644000175000017500000000356711757171206017163 0ustar plbplb/* $Id: session.h 323 2010-09-21 21:31:29Z mmatloka $ */ /* * FedStage DRMAA for PBS Pro * Copyright (C) 2006-2009 FedStage Systems * * 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 3 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* * Adapted from pbs_drmaa/session.h */ #ifndef __OAR_DRMAA__SESSION_H #define __OAR_DRMAA__SESSION_H #ifdef HAVE_CONFIG_H # include #endif #include typedef struct oardrmaa_session_s oardrmaa_session_t; fsd_drmaa_session_t * oardrmaa_session_new( const char *contact ); struct oardrmaa_session_s { fsd_drmaa_session_t super; bool (* do_drm_keeps_completed_jobs)( oardrmaa_session_t *self ); void (* super_destroy)( fsd_drmaa_session_t *self ); void (* super_apply_configuration)(fsd_drmaa_session_t *self); /* * Pointer to standard wait_thread drmaa_utils function */ void* (* super_wait_thread)( fsd_drmaa_session_t *self ); /* * oar connection (or -1 if not connected). * A descriptor of socket conencted to oar server. */ int oar_conn; /*TODO we need more information ? */ /* tp remove * List of attributes which will be used to query jobs. */ /* struct attrl *status_attrl; */ /*TODO we can support it */ }; #endif /* __oar_DRMAA__SESSION_H */ ./oar-2.5.2/sources/extra/oar_drmaa/session.c0000644000175000017500000003452311757171206017152 0ustar plbplb/* $Id: session.c 385 2011-01-04 18:24:05Z mamonski $ */ /* * FedStage DRMAA for PBS Pro * Copyright (C) 2006-2009 FedStage Systems * * 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 3 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* * Adapted from pbs_drmaa/session.c */ /* TODO remove oardrmaa_create_status_attrl status_attrl in session */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef lint static char rcsid[] # ifdef __GNUC__ __attribute__ ((unused)) # endif = "$Id: session.c 385 2011-01-04 18:24:05Z mamonski $"; #endif static void oardrmaa_session_destroy( fsd_drmaa_session_t *self ); static void oardrmaa_session_apply_configuration( fsd_drmaa_session_t *self ); static fsd_job_t * oardrmaa_session_new_job( fsd_drmaa_session_t *self, const char *job_id ); static bool oardrmaa_session_do_drm_keeps_completed_jobs( oardrmaa_session_t *self ); static void oardrmaa_session_update_all_jobs_status( fsd_drmaa_session_t *self ); static void *oardrmaa_session_wait_thread( fsd_drmaa_session_t *self ); static char * oardrmaa_session_run_impl( fsd_drmaa_session_t *self, const fsd_template_t *jt, int bulk_idx ); /* TODO to remove static struct attrl * oardrmaa_create_status_attrl(void); */ fsd_drmaa_session_t * oardrmaa_session_new( const char *contact ) { oardrmaa_session_t *volatile self = NULL; if( contact == NULL ) contact = ""; TRY { self = (oardrmaa_session_t*)fsd_drmaa_session_new(contact); fsd_realloc( self, 1, oardrmaa_session_t ); self->super_wait_thread = NULL; self->oar_conn = -1; /* self->status_attrl = NULL;*/ /* to remove ??? */ self->super_destroy = self->super.destroy; self->super.destroy = oardrmaa_session_destroy; self->super.new_job = oardrmaa_session_new_job; self->super.update_all_jobs_status = oardrmaa_session_update_all_jobs_status; self->super.run_impl = oardrmaa_session_run_impl; self->super_apply_configuration = self->super.apply_configuration; self->super.apply_configuration = oardrmaa_session_apply_configuration; self->do_drm_keeps_completed_jobs = oardrmaa_session_do_drm_keeps_completed_jobs; /* to remove self->status_attrl = oardrmaa_create_status_attrl(); */ self->oar_conn = oar_connect( self->super.contact ); fsd_log_info(( "oar_connect(%s) =%d", self->super.contact, self->oar_conn )); if( self->oar_conn < 0 ) oardrmaa_exc_raise_oar( "oar_connect" ); self->super.load_configuration( &self->super, "oar_drmaa" ); self->super.missing_jobs = FSD_IGNORE_MISSING_JOBS; if( self->do_drm_keeps_completed_jobs( self ) ) self->super.missing_jobs = FSD_IGNORE_QUEUED_MISSING_JOBS; } EXCEPT_DEFAULT { if( self ) { self->super.destroy( &self->super ); self = NULL; } } END_TRY return (fsd_drmaa_session_t*)self; } void oardrmaa_session_destroy( fsd_drmaa_session_t *self ) { oardrmaa_session_t *oarself = (oardrmaa_session_t*)self; self->stop_wait_thread( self ); if( oarself->oar_conn >= 0 ) oar_disconnect( oarself->oar_conn ); /* fsd_free( oarself->status_attrl ); */ /* TODO: to remove */ oarself->super_destroy( self ); } static char * oardrmaa_session_run_impl( fsd_drmaa_session_t *self, const fsd_template_t *jt, int bulk_idx ) { char *volatile job_id = NULL; fsd_job_t *volatile job = NULL; oardrmaa_submit_t *volatile submit = NULL; fsd_log_enter(( "(jt=%p, bulk_idx=%d)", (void*)jt, bulk_idx )); TRY { submit = oardrmaa_submit_new( self, jt, bulk_idx ); submit->eval( submit ); job_id = submit->submit( submit ); job = self->new_job( self, job_id ); job->submit_time = time(NULL); job->flags |= FSD_JOB_CURRENT_SESSION; self->jobs->add( self->jobs, job ); job->release( job ); job = NULL; } EXCEPT_DEFAULT { fsd_free( job_id ); fsd_exc_reraise(); } FINALLY { if( submit ) submit->destroy( submit ); if( job ) job->release( job ); } END_TRY fsd_log_return(( " =%s", job_id )); return job_id; } static fsd_job_t * oardrmaa_session_new_job( fsd_drmaa_session_t *self, const char *job_id ) { fsd_job_t *job; job = oardrmaa_job_new( fsd_strdup(job_id) ); job->session = self; return job; } void oardrmaa_session_apply_configuration( fsd_drmaa_session_t *self ) { /* TODO pbs_home log file ???*/ oardrmaa_session_t *oarself = (oardrmaa_session_t*)self; /* fsd_conf_option_t *pbs_home; pbs_home = fsd_conf_dict_get(self->configuration, "pbs_home" ); if( pbs_home ) { if( pbs_home->type == FSD_CONF_STRING ) { struct stat statbuf; char * volatile log_path; struct tm tm; pbsself->pbs_home = pbs_home->val.string; fsd_log_debug(("pbs_home: %s",pbsself->pbs_home)); pbsself->super_wait_thread = pbsself->super.wait_thread; pbsself->super.wait_thread = pbsdrmaa_session_wait_thread; pbsself->wait_thread_log = true; time(&pbsself->log_file_initial_time); localtime_r(&pbsself->log_file_initial_time,&tm); if((log_path = fsd_asprintf("%s/server_logs/%04d%02d%02d", pbsself->pbs_home, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday)) == NULL) { fsd_exc_raise_fmt(FSD_ERRNO_INTERNAL_ERROR,"WT - Memory allocation wasn't possible"); } if(stat(log_path,&statbuf) == -1) { char errbuf[256] = "InternalError"; (void)strerror_r(errno, errbuf, 256); fsd_exc_raise_fmt(FSD_ERRNO_INTERNAL_ERROR,"stat error: %s",errbuf); } fsd_log_debug(("Log file %s size %d",log_path,(int) statbuf.st_size)); pbsself->log_file_initial_size = statbuf.st_size; fsd_free(log_path); } else { */ oarself->super.enable_wait_thread = false; /* oarself->wait_thread_log = false; */ /* TODO: to remove */ fsd_log_debug(("Running standard wait_thread (pooling).")); /* } } */ oarself->super_apply_configuration(self); /* call method from the superclass */ } void oardrmaa_session_update_all_jobs_status( fsd_drmaa_session_t *self ) { volatile bool conn_lock = false; volatile bool jobs_lock = false; oardrmaa_session_t *oarself = (oardrmaa_session_t*)self; fsd_job_set_t *jobs = self->jobs; struct batch_status *volatile status = NULL; char **job_ids = NULL; jobs_lock = fsd_mutex_lock( &jobs->mutex ); job_ids = jobs->get_all_job_ids(jobs); jobs_lock = fsd_mutex_unlock( &jobs->mutex ); fsd_log_enter(("")); TRY { conn_lock = fsd_mutex_lock( &self->drm_connection_mutex ); retry: status = oar_multiple_statjob( oarself->oar_conn, job_ids); fsd_log_info(( "oar_multiple_statjob( fd=%d, job_id=NULL, attribs={...} ) =%p", oarself->oar_conn, (void*)status )); if( status == NULL && oar_errno != 0 ) { if (oar_errno == OAR_ERRNO_PROTOCOL || oar_errno == OAR_ERRNO_EXPIRED) { if ( oarself->oar_conn >= 0) oar_disconnect( oarself->oar_conn ); sleep(1); oarself->oar_conn = oar_connect( oarself->super.contact ); if( oarself->oar_conn < 0 ) oardrmaa_exc_raise_oar( "oar_connect" ); else goto retry; } else { oardrmaa_exc_raise_oar( "oar_statjob" ); } } conn_lock = fsd_mutex_unlock( &self->drm_connection_mutex ); { size_t i; fsd_job_t *job; jobs_lock = fsd_mutex_lock( &jobs->mutex ); for( i = 0; i < jobs->tab_size; i++ ) for( job = jobs->tab[i]; job != NULL; job = job->next ) { fsd_mutex_lock( &job->mutex ); job->flags |= FSD_JOB_MISSING; fsd_mutex_unlock( &job->mutex ); } jobs_lock = fsd_mutex_unlock( &jobs->mutex ); } { struct batch_status *volatile i; for( i = status; i != NULL; i = i->next ) { fsd_job_t *job = NULL; fsd_log_debug(( "job_id=%s", i->name )); job = self->get_job( self, i->name ); if( job != NULL ) { job->flags &= ~FSD_JOB_MISSING; TRY { ((oardrmaa_job_t*)job)->update( job, i ); } FINALLY { job->release( job ); } END_TRY } } } { size_t volatile i; fsd_job_t *volatile job; jobs_lock = fsd_mutex_lock( &jobs->mutex ); for( i = 0; i < jobs->tab_size; i++ ) for( job = jobs->tab[i]; job != NULL; job = job->next ) { fsd_mutex_lock( &job->mutex ); TRY { if( job->flags & FSD_JOB_MISSING ) job->on_missing( job ); } FINALLY{ fsd_mutex_unlock( &job->mutex ); } END_TRY } jobs_lock = fsd_mutex_unlock( &jobs->mutex ); } } FINALLY { if( status != NULL ) oar_statfree( status ); if( conn_lock ) conn_lock = fsd_mutex_unlock( &self->drm_connection_mutex ); if( jobs_lock ) jobs_lock = fsd_mutex_unlock( &jobs->mutex ); } END_TRY fsd_free_vector( job_ids ); fsd_log_return(("")); } /* to remove struct attrl * oardrmaa_create_status_attrl(void) { struct attrl *result = NULL; struct attrl *i; const int max_attribs = 16; int n_attribs; int j = 0; */ /* TODO to adapt */ /* printf("Prout...................................................\n"); */ /* fsd_log_enter(("")); fsd_calloc( result, max_attribs, struct attrl ); result[j++].name="job_state"; printf("name %s\n",result[j-1].name); result[j++].name="exit_status"; */ /* result[j++].name="resources_used"; result[j++].name="ctime"; result[j++].name="mtime"; result[j++].name="qtime"; result[j++].name="etime"; result[j++].name="queue"; result[j++].name="Account_Name"; result[j++].name="exec_host"; result[j++].name="start_time"; result[j++].name="mtime"; */ /* n_attribs = j; for( i = result; true; i++ ) if( i+1 < result + n_attribs ) i->next = i+1; else { i->next = NULL; break; } printf("Prout\n"); return result; */ bool oardrmaa_session_do_drm_keeps_completed_jobs( oardrmaa_session_t *self ) { /*TODO: to adapt */ /* struct attrl default_queue_query; struct attrl keep_completed_query; struct batch_status *default_queue_result = NULL; struct batch_status *keep_completed_result = NULL; const char *default_queue = NULL; const char *keep_completed = NULL; volatile bool result = false; volatile bool conn_lock = false; TRY { default_queue_query.next = NULL; default_queue_query.name = "default_queue"; default_queue_query.resource = NULL; default_queue_query.value = NULL; keep_completed_query.next = NULL; keep_completed_query.name = "keep_completed"; keep_completed_query.resource = NULL; keep_completed_query.value = NULL; conn_lock = fsd_mutex_lock( &self->super.drm_connection_mutex ); default_queue_result = pbs_statserver( self->pbs_conn, &default_queue_query, NULL ); if( default_queue_result == NULL ) pbsdrmaa_exc_raise_pbs( "pbs_statserver" ); if( default_queue_result->attribs && !strcmp( default_queue_result->attribs->name, "default_queue" ) ) default_queue = default_queue_result->attribs->value; fsd_log_debug(( "default_queue: %s", default_queue )); if( default_queue ) { keep_completed_result = pbs_statque( self->pbs_conn, (char*)default_queue, &keep_completed_query, NULL ); if( keep_completed_result == NULL ) pbsdrmaa_exc_raise_pbs( "pbs_statque" ); if( keep_completed_result->attribs && !strcmp( keep_completed_result->attribs->name, "keep_completed" ) ) keep_completed = keep_completed_result->attribs->value; } fsd_log_debug(( "keep_completed: %s", keep_completed )); } EXCEPT_DEFAULT { const fsd_exc_t *e = fsd_exc_get(); fsd_log_warning(( "PBS server seems not to keep completed jobs\n" "detail: %s", e->message(e) )); result = false; } ELSE { result = false; if( default_queue == NULL ) fsd_log_warning(( "no default queue set on PBS server" )); else if( keep_completed == NULL && self->pbs_home == NULL ) fsd_log_warning(( "PBS server is not configured to keep completed jobs\n" "in Torque: set keep_completed parameter of default queue\n" " $ qmgr -c 'set queue batch keep_completed = 60'\n" " or configure DRMAA to utilize log files" )); else result = true; } FINALLY { if( default_queue_result ) pbs_statfree( default_queue_result ); if( keep_completed_result ) pbs_statfree( keep_completed_result ); if( conn_lock ) conn_lock = fsd_mutex_unlock( &self->super.drm_connection_mutex ); } END_TRY */ return false; } void * oardrmaa_session_wait_thread( fsd_drmaa_session_t *self ) { /*TODO: to adapt */ /* pbsdrmaa_log_reader_t *log_reader = NULL; fsd_log_enter(( "" )); TRY { log_reader = pbsdrmaa_log_reader_new( self, NULL); log_reader->read_log( log_reader ); } FINALLY { pbsdrmaa_log_reader_destroy( log_reader ); } END_TRY */ fsd_log_return(( " =NULL" )); return NULL; } ./oar-2.5.2/sources/extra/oar_drmaa/oar_error.h0000644000175000017500000001175011757171206017463 0ustar plbplb #ifndef __OAR_DRMAA__OAR_ERROR_H #define __OAR_DRMAA__OAR_ERROR_H /* from number and text from pbs_error.h must replaced by OAR code */ #define OAR_ERRNO_NONE 0 /* no error */ #define OAR_ERRNO_UNKJOBID 15001 /* Unknown Job Identifier */ #define OAR_ERRNO_NOATTR 15002 /* Undefined Attribute */ #define OAR_ERRNO_ATTRRO 15003 /* attempt to set READ ONLY attribute */ #define OAR_ERRNO_IVALREQ 15004 /* Invalid request */ #define OAR_ERRNO_UNKREQ 15005 /* Unknown batch request */ #define OAR_ERRNO_TOOMANY 15006 /* Too many submit retries */ #define OAR_ERRNO_PERM 15007 /* No permission */ #define OAR_ERRNO_BADHOST 15008 /* access from host not allowed */ #define OAR_ERRNO_JOBEXIST 15009 /* job already exists */ #define OAR_ERRNO_SYSTEM 15010 /* system error occurred */ #define OAR_ERRNO_INTERNAL 15011 /* internal server error occurred */ #define OAR_ERRNO_REGROUTE 15012 /* parent job of dependent in rte que */ #define OAR_ERRNO_UNKSIG 15013 /* unknown signal name */ #define OAR_ERRNO_BADATVAL 15014 /* bad attribute value */ #define OAR_ERRNO_MODATRRUN 15015 /* Cannot modify attrib in run state */ #define OAR_ERRNO_BADSTATE 15016 /* request invalid for job state */ #define OAR_ERRNO_UNKQUE 15018 /* Unknown queue name */ #define OAR_ERRNO_BADCRED 15019 /* Invalid Credential in request */ #define OAR_ERRNO_EXPIRED 15020 /* Expired Credential in request */ #define OAR_ERRNO_QUNOENB 15021 /* Queue not enabled */ #define OAR_ERRNO_QACESS 15022 /* No access permission for queue */ #define OAR_ERRNO_BADUSER 15023 /* Bad user - no password entry */ #define OAR_ERRNO_HOPCOUNT 15024 /* Max hop count exceeded */ #define OAR_ERRNO_QUEEXIST 15025 /* Queue already exists */ #define OAR_ERRNO_ATTRTYPE 15026 /* incompatable queue attribute type */ #define OAR_ERRNO_QUEBUSY 15027 /* Queue Busy (not empty) */ #define OAR_ERRNO_QUENBIG 15028 /* Queue name too long */ #define OAR_ERRNO_NOSUP 15029 /* Feature/function not supported */ #define OAR_ERRNO_QUENOEN 15030 /* Cannot enable queue,needs add def */ #define OAR_ERRNO_PROTOCOL 15031 /* Protocol (ASN.1) error */ #define OAR_ERRNO_BADATLST 15032 /* Bad attribute list structure */ #define OAR_ERRNO_NOCONNECTS 15033 /* No free connections */ #define OAR_ERRNO_NOSERVER 15034 /* No server to connect to */ #define OAR_ERRNO_UNKRESC 15035 /* Unknown resource */ #define OAR_ERRNO_EXCQRESC 15036 /* Job exceeds Queue resource limits */ #define OAR_ERRNO_QUENODFLT 15037 /* No Default Queue Defined */ #define OAR_ERRNO_NORERUN 15038 /* Job Not Rerunnable */ #define OAR_ERRNO_ROUTEREJ 15039 /* Route rejected by all destinations */ #define OAR_ERRNO_ROUTEEXPD 15040 /* Time in Route Queue Expired */ #define OAR_ERRNO_MOMREJECT 15041 /* Request to MOM failed */ #define OAR_ERRNO_BADSCRIPT 15042 /* (qsub) cannot access script file */ #define OAR_ERRNO_STAGEIN 15043 /* Stage In of files failed */ #define OAR_ERRNO_RESCUNAV 15044 /* Resources temporarily unavailable */ #define OAR_ERRNO_BADGRP 15045 /* Bad Group specified */ #define OAR_ERRNO_MAXQUED 15046 /* Max number of jobs in queue */ #define OAR_ERRNO_CKPBSY 15047 /* Checkpoint Busy, may be retries */ #define OAR_ERRNO_EXLIMIT 15048 /* Limit exceeds allowable */ #define OAR_ERRNO_BADACCT 15049 /* Bad Account attribute value */ #define OAR_ERRNO_ALRDYEXIT 15050 /* Job already in exit state */ #define OAR_ERRNO_NOCOPYFILE 15051 /* Job files not copied */ #define OAR_ERRNO_CLEANEDOUT 15052 /* unknown job id after clean init */ #define OAR_ERRNO_NOSYNCMSTR 15053 /* No Master in Sync Set */ #define OAR_ERRNO_BADDEPEND 15054 /* Invalid dependency */ #define OAR_ERRNO_DUPLIST 15055 /* Duplicate entry in List */ #define OAR_ERRNO_DISPROTO 15056 /* Bad DIS based Request Protocol */ #define OAR_ERRNO_EXECTHERE 15057 /* cannot execute there */ #define OAR_ERRNO_SISREJECT 15058 /* sister rejected */ #define OAR_ERRNO_SISCOMM 15059 /* sister could not communicate */ #define OAR_ERRNO_SVRDOWN 15060 /* req rejected -server shutting down */ #define OAR_ERRNO_CKPSHORT 15061 /* not all tasks could checkpoint */ #define OAR_ERRNO_UNKNODE 15062 /* Named node is not in the list */ #define OAR_ERRNO_UNKNODEATR 15063 /* node-attribute not recognized */ #define OAR_ERRNO_NONODES 15064 /* Server has no node list */ #define OAR_ERRNO_NODENBIG 15065 /* Node name is too big */ #define OAR_ERRNO_NODEEXIST 15066 /* Node name already exists */ #define OAR_ERRNO_BADNDATVAL 15067 /* Bad node-attribute value */ #define OAR_ERRNO_MUTUALEX 15068 /* State values are mutually exclusive */ #define OAR_ERRNO_GMODERR 15069 /* Error(s) during global modification of nodes */ #define OAR_ERRNO_NORELYMOM 15070 /* could not contact Mom */ #define OAR_ERRNO_NOTSNODE 15071 /* no time-shared nodes */ /* TODO: to adapt */ #define USER_HOLD 0 /* TODO */ struct oar_err_to_txt { int err_no; char **err_txt; }; char *oar_errno_to_txt(int err_no); extern int oar_errno; /* global value to store oar error from OAR_REST_API request. TODO: to verify */ #endif /* __OAR_DRMAA__OAR_ERROR_H */ ./oar-2.5.2/sources/extra/oar_drmaa/oar_attrib.h0000644000175000017500000000330311757171206017612 0ustar plbplb/* $Id: pbs_attrib.h 256 2010-08-10 16:31:35Z mamonski $ */ /* * FedStage DRMAA for PBS Pro * Copyright (C) 2006-2009 FedStage Systems * * 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 3 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* * Adapted from pbs_drmaa/pbs_attrib.h */ #ifndef __OAR_DRMAA__OAR_ATTRIB_H #define __OAR_DRMAA__OAR_ATTRIB_H #ifdef HAVE_CONFIG_H # include #endif #include fsd_template_t *oardrmaa_oar_template_new(void); int oardrmaa_oar_attrib_by_name( const char *name ); #define OARDRMAA_JOB_NAME "job_name" #define OARDRMAA_STDOUT_FILE "stdout" #define OARDRMAA_STDERR_FILE "stderr" #define OARDRMAA_WALLTIME "walltime" #define OARDRMAA_ARGS "args" #define OARDRMAA_JOB_STATE "job_state" #define OARDRMAA_HOLD "hold" typedef enum { OARDRMAA_ATTR_JOB_NAME, OARDRMAA_ATTR_STDOUT_FILE, OARDRMAA_ATTR_STDERR_FILE, OARDRMAA_ATTR_WALLTIME, OARDRMAA_ATTR_ARGS, OARDRMAA_ATTR_JOB_STATE, OARDRMAA_ATTR_HOLD, OARDRMAA_N_OAR_ATTRIBUTES } oar_attribute_t; #endif /* __OAR_DRMAA__OAR_ATTRIB_H */ ./oar-2.5.2/sources/extra/oar_drmaa/oar_attrib.gperf0000644000175000017500000000710511757171206020472 0ustar plbplb%{ /* $Id: pbs_attrib.gperf 2677 2009-09-08 14:32:22Z mmamonski $ */ /* * FedStage DRMAA for PBS Pro * Copyright (C) 2006-2009 FedStage Systems * * 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 3 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* * Adapted from pbs_drmaa/pbs_attrib.gperf */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #ifndef lint static char rcsid[] # ifdef __GNUC__ __attribute__ ((unused)) # endif = "$Id: pbs_attrib.gperf 2677 2009-09-08 14:32:22Z mmamonski $"; #endif extern const fsd_attribute_t oardrmaa_oar_attributes[]; #define t(code) \ ( & oardrmaa_oar_attributes[ code ] ) %} %language=ANSI-C %includes %readonly-tables %pic %define lookup-function-name oardrmaa_oar_attrib_lookup %struct-type struct oar_attrib { int name; const fsd_attribute_t *attr; } %% job_name, t(OARDRMAA_ATTR_JOB_NAME) stdout, t(OARDRMAA_ATTR_STDOUT_FILE) stderr, t(OARDRMAA_ATTR_STDERR_FILE) walltime, t(OARDRMAA_ATTR_WALLTIME) args, t(OARDRMAA_ATTR_ARGS) job_state, t(OARDRMAA_ATTR_JOB_STATE) hold, t(OARDRMAA_ATTR_HOLD) %% #undef t int oardrmaa_oar_attrib_by_name( const char *name ) { const struct oar_attrib *found; found = oardrmaa_oar_attrib_lookup( name, strlen(name) ); if( found ) return found->attr->code; else return -1; } static const fsd_attribute_t * oardrmaa_oar_template_by_name( const fsd_template_t *self, const char *name ) { const struct oar_attrib *found; found = oardrmaa_oar_attrib_lookup( name, strlen(name) ); if( found != NULL ) { fsd_assert( found->attr - oardrmaa_oar_attributes == found->attr->code ); return found->attr; } else fsd_exc_raise_fmt( FSD_ERRNO_INVALID_ARGUMENT, "invalid OAR attribute name: %s", name ); } static const fsd_attribute_t * oardrmaa_oar_template_by_code( const fsd_template_t *self, int code ) { if( 0 <= code && code < OARDRMAA_N_OAR_ATTRIBUTES ) { fsd_assert( oardrmaa_oar_attributes[code].code == code ); return & oardrmaa_oar_attributes[ code ]; } else fsd_exc_raise_fmt( FSD_ERRNO_INVALID_ARGUMENT, "invalid OAR attribute code: %d", code ); } fsd_template_t * oardrmaa_oar_template_new(void) { return fsd_template_new( oardrmaa_oar_template_by_name, oardrmaa_oar_template_by_code, OARDRMAA_N_OAR_ATTRIBUTES ); } const fsd_attribute_t oardrmaa_oar_attributes[ OARDRMAA_N_OAR_ATTRIBUTES ] = { {"job_name", OARDRMAA_ATTR_JOB_NAME, false }, {"stdout", OARDRMAA_ATTR_STDOUT_FILE, false }, {"stderr", OARDRMAA_ATTR_STDERR_FILE, false }, {"walltime", OARDRMAA_ATTR_WALLTIME,false }, {"args", OARDRMAA_ATTR_ARGS, false }, {"job_state", OARDRMAA_ATTR_JOB_STATE, false }, {"hold", OARDRMAA_ATTR_HOLD, false }, }; /* vim: set ft=c: */ ./oar-2.5.2/sources/extra/oar_drmaa/oar.h0000644000175000017500000000516711757171206016257 0ustar plbplb #ifndef __OAR_H #define __OAR_H #ifdef HAVE_CONFIG_H # include #endif #define OAR_JS_WAITING "Waiting" #define OAR_JS_HOLD "Hold" #define OAR_JS_TOLAUNCH "toLaunch" #define OAR_JS_TOERROR "toError" #define OAR_JS_TOASKRESERV "toAskReservation" #define OAR_JS_LAUNCHING "Launching" #define OAR_JS_RUNNING "Running" #define OAR_JS_SUSPENDED "Suspended" #define OAR_JS_RESUMING "Resuming" #define OAR_JS_FINISHING "Finishing" #define OAR_JS_TERMINATED "Terminated" #define OAR_JS_ERROR "Error" /** * The drmaa_job_ps() function SHALL store in @a remote_ps the program * status of the job identified by @a job_id. The possible values of * a program's staus are: * - DRMAA_PS_UNDETERMINED Waiting DRMAA_PS_QUEUED_ACTIVE Hold (+event ???) DRMAA_PS_SYSTEM_ON_HOLD / DRMAA_PS_USER_ON_HOLD [default] / DRMAA_PS_USER_SYSTEM_ON_HOLD toLaunch DRMAA_PS_QUEUED_ACTIVE toError DRMAA_PS_FAILED toAskReservation DRMAA_PS_UNDETERMINED Launching DRMAA_PS_RUNNING Running DRMAA_PS_RUNNING Suspended(event) DRMAA_PS_SYSTEM_SUSPENDED / DRMAA_PS_USER_SUSPENDED [default] Resuming(event) DRMAA_PS_SYSTEM_SUSPENDED / DRMAA_PS_USER_SUSPENDED [default] Finishing DRMAA_PS_RUNNING Terminated DRMAA_PS_DONE Error DRMAA_PS_FAILED */ /* Conrol Job */ /* * - DRMAA_CONTROL_SUSPEND 0 * - DRMAA_CONTROL_RESUME 1 * - DRMAA_CONTROL_HOLD 2 * - DRMAA_CONTROL_RELEASE 3 * - DRMAA_CONTROL_TERMINATE 4 */ const char *drmaa_control_to_oar_rest[]; struct attrl { struct attrl *next; char *name; char *resource; char *value; }; /* TODO: to remove, duplicate with attrl */ struct attropl { struct attropl *next; char *name; char *resource; char *value; }; struct oar_job_status { int id; char *state; int exit_status; int walltime; char *queue; }; struct batch_status { struct batch_status *next; char *name; struct oar_job_status *status; char *text; }; int oar_disconnect(int connect); int oar_connect(char *server); int oar_control_job(int connect, char *job_id, int action); void oar_statfree(struct batch_status *stat); void oar_status_dump(struct batch_status *stat); struct batch_status *oar_statjob(int connect, char *id); struct batch_status *oar_multiple_statjob(int connect, char **job_id); char *oar_submit(int connect, struct attropl *attrib, char *script_path, char *workdir, char *queue_destination); #endif /* __OAR_H */ ./oar-2.5.2/sources/extra/oar_drmaa/oar.c0000644000175000017500000003523211757171206016246 0ustar plbplb#ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include #include #include #include #include /* curl to perform http access*/ #include /* json-glib and glib to manipulate json. Be carefull need version >= 0.12.0 (for reader/builder) */ #include #include #ifndef lint static char rcsid[] # ifdef __GNUC__ __attribute__ ((unused)) # endif = "$Id: $"; #endif int oar_errno = 0; const char *drmaa_control_to_oar_rest[]={ "/rholds/new.json", /* DRMAA_CONTROL_SUSPEND 0 */ "/resumptions/new.json", /* DRMAA_CONTROL_RESUME 1 */ "/holds/new.json", /* DRMAA_CONTROL_HOLD 2 */ "/resumptions/new.json", /* DRMAA_CONTROL_RELEASE 3 */ "/deletions/new.json" /* DRMAA_CONTROL_TERMINATE 4 */ }; struct memory_struct { char *memory; size_t size; }; CURL *curl_handle; CURLcode res; struct memory_struct recv_data; /* to store oar_rest_api get results */ static size_t write_memory_callback(void *ptr, size_t size, size_t nmemb, void *data) { size_t realsize = size * nmemb; struct memory_struct *mem = (struct memory_struct *)data; mem->memory = realloc(mem->memory, mem->size + realsize + 1); if (mem->memory == NULL) { /* out of memory! */ printf("not enough memory (realloc returned NULL)\n"); exit(EXIT_FAILURE); } memcpy(&(mem->memory[mem->size]), ptr, realsize); mem->size += realsize; mem->memory[mem->size] = 0; return realsize; } struct memory_struct recv_data; void init_recv_data() { if(recv_data.memory) { free(recv_data.memory); /* ready for next receive */ recv_data.memory = malloc(1); /* will be grown as needed by the realloc above */ recv_data.size = 0; } /* send all received data to this function */ curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_memory_callback); /* we pass our 'recv_data' struct to the callback function */ curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&recv_data); } /* perform oar_api_request */ int oar_api_request(int request_type) { long http_code = 0; char *rest_url[256]; struct curl_slist *headers = NULL; } int oar_connect(char *server) { g_type_init (); /* only once */ JsonParser *parser = json_parser_new (); JsonReader *reader = json_reader_new (NULL); GError *error = NULL; /* intialize recv_data in memory storage */ recv_data.memory = malloc(1); /* will be grown as needed by the realloc above */ recv_data.size = 0; curl_global_init(CURL_GLOBAL_ALL); curl_handle = curl_easy_init(); if(!curl_handle) { printf("no curl handle\n"); exit(EXIT_FAILURE); } /* send all received data to this function */ curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_memory_callback); /* we pass our 'recv_data' struct to the callback function */ curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&recv_data); /* some servers don't like requests that are made without a user-agent field, so we provide one */ curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0"); curl_easy_setopt(curl_handle, CURLOPT_URL, "http://localhost/oarapi/resources.json"); res = curl_easy_perform(curl_handle); /* */ printf("%lu bytes retrieved\n", (long)recv_data.size); json_parser_load_from_data (parser, recv_data.memory, -1, &error); json_reader_set_root (reader, json_parser_get_root (parser)); printf("number of members: %d\n", json_reader_count_members (reader)); json_reader_read_member (reader, "total"); json_reader_is_value (reader); printf("total: %d\n",json_reader_get_int_value (reader)); g_object_unref (reader); g_object_unref (parser); if(recv_data.memory) { free(recv_data.memory); /* ready for next receive */ recv_data.memory = malloc(1); /* will be grown as needed by the realloc above */ recv_data.size = 0; } fsd_set_verbosity_level(FSD_LOG_ALL); fsd_log_debug(( "OAR-CONNECT\n")); return 0; } int oar_disconnect(int connect) { printf("TODO: oar_disconnect\n"); /* we're done with libcurl, so clean it up */ curl_global_cleanup(); return 0; } int oar_control_job(int connect, char *job_id, int action) { printf("oar_job_control: job_id: %s action: %i drmaa_control: %s\n", job_id, action, drmaa_control_to_str(action)); /* CURL */ long http_code = 0; char rest_url[256]; struct curl_slist *headers = NULL; sprintf(rest_url,"http://localhost/oarapi/jobs/%s%s",job_id,drmaa_control_to_oar_rest[action]); /*printf("url:%s\n",rest_url); */ headers = curl_slist_append(headers, "Content-Type: application/json"); curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headers); curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, ""); curl_easy_setopt(curl_handle, CURLOPT_URL, rest_url); res = curl_easy_perform(curl_handle); curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &http_code); printf("http code %ld\n",http_code); /* read response */ JsonParser *parser = json_parser_new (); JsonReader *reader = json_reader_new (NULL); GError *error = NULL; printf("%lu bytes retrieved\n", (long)recv_data.size); json_parser_load_from_data (parser, recv_data.memory, -1, &error); json_reader_set_root (reader, json_parser_get_root (parser)); /* test return http status */ if (http_code >= 200 && http_code < 300) /* http successful */ { json_reader_read_member (reader,"id"); int job_id = json_reader_get_int_value (reader); json_reader_end_element (reader); printf("Http request successfull OK: job id: %d\n",job_id); } else { char *title=NULL; char *message=NULL; int code=0; json_reader_read_member(reader,"title"); title = json_reader_get_string_value(reader); json_reader_end_element (reader); json_reader_read_member(reader,"message"); message = json_reader_get_string_value (reader); json_reader_end_element (reader); json_reader_read_member(reader,"code"); code = json_reader_get_int_value (reader); json_reader_end_element (reader); /* printf("title: %s\nmessage: %s\ncode: %d\n",title,message,code); */ } /* clean recv/reader/parser stuff */ g_object_unref (reader); g_object_unref (parser); if(recv_data.memory) { free(recv_data.memory); /* ready for next receive */ recv_data.memory = malloc(1); /* will be grown as needed by the realloc above */ recv_data.size = 0; } /* TODO */ return 0; } struct batch_status *oar_statjob(int connect, char *id) { char *state = NULL; int exit_code = -2; int walltime = 0; char *queue = NULL; int job_id = 0; struct batch_status *b_status = malloc(sizeof(struct batch_status)); /* CURL stuff */ long http_code = 0; char rest_url[256]; char *exit_code_str; struct curl_slist *headers = NULL; sprintf(rest_url,"http://localhost/oarapi/jobs/%s.json",id); /* fsd_log_debug(("url:%s\n",rest_url)); */ headers = curl_slist_append(headers, "Content-Type: application/json"); init_recv_data(); curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headers); curl_easy_setopt(curl_handle, CURLOPT_HTTPGET, 1L); /* perform a get http request */ curl_easy_setopt(curl_handle, CURLOPT_URL, rest_url); res = curl_easy_perform(curl_handle); curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &http_code); /* read response */ JsonParser *parser = json_parser_new (); JsonReader *reader = json_reader_new (NULL); GError *error = NULL; /* fsd_log_debug(("%lu bytes retrieved\n", (long)recv_data.size)); */ json_parser_load_from_data (parser, recv_data.memory, -1, &error); json_reader_set_root (reader, json_parser_get_root (parser)); /* test return http status */ if (http_code >= 200 && http_code < 300) /* http successful */ { json_reader_read_member (reader,"id"); job_id = json_reader_get_int_value (reader); json_reader_end_element (reader); json_reader_read_member (reader,"state"); state = json_reader_get_string_value(reader); /* need to free after use ??? */ json_reader_end_element (reader); json_reader_read_member(reader,"exit_code"); if (!json_reader_get_null_value(reader)) { exit_code_str = json_reader_get_string_value(reader); exit_code = atoi(exit_code_str); if((exit_code & 0x7f) == 0) { exit_code = exit_code >> 8; } else { exit_code = (exit_code & 0x7f) + 128; } /* fsd_log_debug(("exit_code_str: %s exit_code8: %d\n",exit_code_str, exit_code)); */ } json_reader_end_element (reader); json_reader_read_member (reader,"walltime"); if (!json_reader_get_null_value(reader)) { walltime = atoi(json_reader_get_string_value(reader)); } json_reader_end_element (reader); json_reader_read_member (reader,"queue"); queue = json_reader_get_string_value(reader); json_reader_end_element (reader); printf("job state: %s\n",state); fsd_log_debug(("job state: %s\n",state)); } else { /* TODO */ } struct oar_job_status *j_status = malloc(sizeof(struct oar_job_status )); j_status->id = job_id; /*TODO do we need it ? assert between job_id and atoi(id) */ j_status->state = fsd_strdup(state); j_status->exit_status = exit_code; j_status->walltime = walltime; j_status->queue = fsd_strdup(queue); b_status->name = fsd_strdup(id); b_status->next = NULL; b_status->status = j_status; b_status->text = fsd_strdup(""); /* In order to avoir freeing problems in pbs_statfree */ /* clean recv/reader/parser stuff TODO */ g_object_unref (reader); g_object_unref (parser); if(recv_data.memory) { free(recv_data.memory); recv_data.memory = malloc(1); recv_data.size = 0; } /* TODO if return NULL and set oar_errno approprietly*/ return b_status; } struct batch_status * oar_multiple_statjob(int connect, char **job_ids) { struct batch_status *j_status; struct batch_status *b_status=NULL; struct batch_status *cur_status=NULL; /* iterate on job_ids (list of job id) */ while(*job_ids !=NULL) { j_status = oar_statjob(connect, *job_ids); if(j_status==NULL) { fsd_log_debug(("TODO oar_statjob return NULL in oar_multiple_statjob\n")); } if (cur_status == NULL) { b_status = j_status; cur_status = j_status; } else { cur_status->next = j_status; cur_status = j_status; } job_ids++; } return b_status; /* TODO return null if ERROR */ } char *oar_submit(int connect, struct attropl *attrib, char *script_path, char *workdir, char *queue_destination) { struct attropl *i; fsd_log_info(("oar_submit: script_path: %s \nqueue_destination %s\n", script_path, queue_destination)); oardrmaa_dump_attrl( attrib, NULL ); char *job_id_str; /* builder */ JsonBuilder *builder = json_builder_new (); JsonNode *node; JsonGenerator *generator; gsize length; gchar *data; /* reader */ JsonParser *parser = json_parser_new (); JsonReader *reader = json_reader_new (NULL); GError *error = NULL; /* build request */ json_builder_begin_object (builder); json_builder_set_member_name (builder, "script_path"); json_builder_add_string_value (builder, script_path); for( i = attrib; i != NULL; i = i->next ) { json_builder_set_member_name (builder, i->name); json_builder_add_string_value (builder, i->value); } json_builder_end_object (builder); node = json_builder_get_root (builder); generator = json_generator_new (); json_generator_set_root (generator, node); data = json_generator_to_data (generator, &length); fsd_log_debug(("data: >>>%s<<< \n",data)); g_object_unref (builder); json_node_free (node); g_object_unref (generator); /* CURL */ struct curl_slist *headers = NULL; headers = curl_slist_append(headers, "Content-Type: application/json"); curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headers); curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, data); curl_easy_setopt(curl_handle, CURLOPT_URL, "http://localhost/oarapi/jobs.json"); res = curl_easy_perform(curl_handle); fsd_log_debug(("%lu bytes retrieved\n", (long)recv_data.size)); g_free (data); json_parser_load_from_data (parser, recv_data.memory, -1, &error); json_reader_set_root (reader, json_parser_get_root (parser)); json_reader_read_member (reader,"id"); int job_id = json_reader_get_int_value (reader); /* TODO if jobid = 0 erreur !!! , http error also ???? */ g_object_unref (reader); g_object_unref (parser); if(recv_data.memory) { free(recv_data.memory); /* ready for next receive */ recv_data.memory = malloc(1); /* will be grown as needed by the realloc above */ recv_data.size = 0; } job_id_str = (char *)malloc(50); sprintf(job_id_str,"%d",job_id); return job_id_str; } void oar_status_dump(struct batch_status *stat) { struct batch_status *next; while( stat!=NULL) { next = stat->next; fsd_log_debug(("job name: %s\n",stat->name)); fsd_log_debug(("status: id: %d state: %s exit_status: %d walltime: %d queue: %s\n", stat->status->id, stat->status->state, stat->status->exit_status, stat->status->walltime, stat->status->queue)); fsd_log_debug(("text: %s\n",stat->text)); stat = next; } } void free_oar_job_status(struct oar_job_status *j_status) { free(j_status->state); free(j_status->queue); free(j_status); } void oar_statfree(struct batch_status *stat) { #if 1 struct batch_status *next; fsd_log_debug(("oar_statfree\n")); while( stat!=NULL) { next = stat->next; free(stat->name); free_oar_job_status(stat->status); free(stat->text); free(stat); stat = next; } #endif } char *oar_errno_to_txt(int err_no) { printf("TODO: oar_errno_to_txt\n"); return 0; } ./oar-2.5.2/sources/extra/oar_drmaa/job.h0000644000175000017500000000246011757171206016241 0ustar plbplb/* $Id: job.h 2 2009-10-12 09:51:22Z mamonski $ */ /* * FedStage DRMAA for PBS Pro * Copyright (C) 2006-2009 FedStage Systems * * 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 3 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* * Adapted from pbs_drmaa/job.h */ #ifndef __OAR_DRMAA__JOB_H #define __OAR_DRMAA__JOB_H #ifdef HAVE_CONFIG_H # include #endif #include typedef struct oardrmaa_job_s oardrmaa_job_t; /* Do not depend on */ struct batch_status; fsd_job_t * oardrmaa_job_new( char *job_id ); struct oardrmaa_job_s { fsd_job_t super; void (* update)( fsd_job_t *self, struct batch_status *status ); }; void oardrmaa_job_on_missing_standard( fsd_job_t *self ); #endif /* __OAR_DRMAA__JOB_H */ ./oar-2.5.2/sources/extra/oar_drmaa/job.c0000644000175000017500000004160411757171206016237 0ustar plbplb/* $Id: job.c 370 2010-11-16 09:51:00Z mamonski $ */ /* * FedStage DRMAA for PBS Pro * Copyright (C) 2006-2009 FedStage Systems/ * * 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 3 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* * Adapted from pbs_drmaa/job.c */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #ifndef lint static char rcsid[] # ifdef __GNUC__ __attribute__ ((unused)) # endif = "$Id: job.c 370 2010-11-16 09:51:00Z mamonski $"; #endif static void oardrmaa_job_control( fsd_job_t *self, int action ); static void oardrmaa_job_update_status( fsd_job_t *self ); static void oardrmaa_job_on_missing( fsd_job_t *self ); void oardrmaa_job_on_missing_standard( fsd_job_t *self ); void oardrmaa_job_on_missing_log_based( fsd_job_t *self ); static void oardrmaa_job_update( fsd_job_t *self, struct batch_status* ); fsd_job_t * oardrmaa_job_new( char *job_id ) { oardrmaa_job_t *self = (oardrmaa_job_t*)fsd_job_new( job_id ); fsd_realloc( self, 1, oardrmaa_job_t ); self->super.control = oardrmaa_job_control; self->super.update_status = oardrmaa_job_update_status; self->super.on_missing = oardrmaa_job_on_missing; self->update = oardrmaa_job_update; return (fsd_job_t*)self; } static void oardrmaa_job_control( fsd_job_t *self, int action ) { volatile bool conn_lock = false; oardrmaa_session_t *session = (oardrmaa_session_t*)self->session; const char *job_id = self->job_id; const char *apicall = NULL; int rc = OAR_ERRNO_NONE; /*TODO: to adapt */ fsd_log_enter(( "({job_id=%s}, action=%d)", self->job_id, action )); TRY { int try_count; const int max_tries = 3; conn_lock = fsd_mutex_lock( &self->session->drm_connection_mutex ); /*TODO reconnect */ for( try_count=0; try_count < max_tries; try_count++ ) { switch( action ) { /* * We cannot know whether we did suspend job * in other way than remembering this inside DRMAA session. * Note: With OAR we can (job event):) Can we exploit this ? */ case DRMAA_CONTROL_SUSPEND: apicall = "oar_job_control_rhold"; rc = oar_control_job(session->oar_conn, (char*)job_id, action); fsd_log_info(("oar_job_control (%s, RHOLD) =%d", job_id, rc)); if( rc == OAR_ERRNO_NONE) self->flags |= FSD_JOB_SUSPENDED; break; case DRMAA_CONTROL_RESUME: apicall = "oar_job_control_resumptions"; rc = oar_control_job(session->oar_conn, (char*)job_id, action); fsd_log_info(("oar_job_control(%s, RESUMPTIONS/RESUMES) =%d", job_id, rc)); if( rc == OAR_ERRNO_NONE ) self->flags &= ~FSD_JOB_SUSPENDED; break; case DRMAA_CONTROL_HOLD: apicall = "oar_job_control_hold"; rc = oar_control_job(session->oar_conn, (char*)job_id, action); fsd_log_info(("oar_job_control(%s, HOLD) =%d", job_id, rc)); if( rc == OAR_ERRNO_NONE ) self->flags |= FSD_JOB_HOLD; break; case DRMAA_CONTROL_RELEASE: apicall = "oar_job_control_resumptions"; rc = oar_control_job(session->oar_conn, (char*)job_id, action); fsd_log_info(("oar_job_control(%s, RESUMPTIONS/RELEASE) =%d", job_id, rc)); if( rc == OAR_ERRNO_NONE ) self->flags &= FSD_JOB_HOLD; break; case DRMAA_CONTROL_TERMINATE: apicall = "oar_job_control_deletions"; rc = oar_control_job(session->oar_conn, (char*)job_id, action); fsd_log_info(("oar_job_control(%s, DELETIONS) =%d", job_id, rc)); if( rc == OAR_ERRNO_NONE ) { self->flags &= FSD_JOB_TERMINATED_MASK; if( (self->flags & FSD_JOB_TERMINATED) == 0 ) self->flags |= FSD_JOB_TERMINATED | FSD_JOB_ABORTED; } break; } if( rc == OAR_ERRNO_NONE ) break; else if( rc == OAR_ERRNO_INTERNAL ) /*TODO: to adapt: PBSE_INTERNAL */ { /* TODO: to remove ? */ /* * In PBS Pro pbs_sigjob raises internal server error (PBSE_INTERNAL) * when job just changed its state to running. */ fsd_log_debug(( "repeating request (%d of %d)", try_count+2, max_tries )); sleep( 1 ); } else oardrmaa_exc_raise_oar( apicall ); } /* end for */ } FINALLY { if( conn_lock ) conn_lock = fsd_mutex_unlock( &self->session->drm_connection_mutex ); } END_TRY fsd_log_return(("")); } void oardrmaa_job_update_status( fsd_job_t *self ) { volatile bool conn_lock = false; struct batch_status *volatile status = NULL; oardrmaa_session_t *session = (oardrmaa_session_t*)self->session; fsd_log_enter(( "({job_id=%s})", self->job_id )); TRY { conn_lock = fsd_mutex_lock( &self->session->drm_connection_mutex ); retry: status = oar_statjob( session->oar_conn, self->job_id); fsd_log_info(( "oar_statjob(fd=%d, job_id=%s, attribs={...}) =%p", session->oar_conn, self->job_id, (void*)status )); if( status == NULL ) { fsd_log_error(("oar_statjob error: %d, %s", oar_errno, oar_errno_to_txt(oar_errno))); switch( oar_errno ) { case OAR_ERRNO_UNKJOBID: /*TODO: to adapt */ break; case OAR_ERRNO_PROTOCOL: case OAR_ERRNO_EXPIRED: if ( session->oar_conn >= 0 ) oar_disconnect( session->oar_conn ); sleep(1); session->oar_conn = oar_connect( session->super.contact ); if( session->oar_conn < 0 ) oardrmaa_exc_raise_oar( "oar_connect" ); else { fsd_log_error(("retry:")); goto retry; } default: oardrmaa_exc_raise_oar( "oar_statjob" ); break; case 0: /* ? */ fsd_exc_raise_code( FSD_ERRNO_INTERNAL_ERROR ); break; } } conn_lock = fsd_mutex_unlock( &self->session->drm_connection_mutex ); if( status != NULL ) { ((oardrmaa_job_t*)self)->update( self, status ); } else if( self->state < DRMAA_PS_DONE ) self->on_missing( self ); } FINALLY { if( conn_lock ) conn_lock = fsd_mutex_unlock( &self->session->drm_connection_mutex ); if( status != NULL ) oar_statfree( status ); } END_TRY fsd_log_return(("")); } void oardrmaa_job_update( fsd_job_t *self, struct batch_status *b_status ) { char *oar_state = NULL; int exit_status = -2; struct oar_job_status *status = b_status->status; /* TODO to remove ??? const char *cpu_usage = NULL; const char *mem_usage = NULL; const char *vmem_usage = NULL; */ const char *walltime = NULL; long unsigned int modify_time = 0; /* TODO */ fsd_log_enter(( "({job_id=%s})", self->job_id )); #ifdef DEBUGGING oardrmaa_dump_attrl( attribs, NULL ); #endif fsd_assert( !strcmp( self->job_id, b_status->name ) ); /* TODO: to adapt */ oar_status_dump(b_status); oar_state = status->state; exit_status = status->exit_status; self->walltime = status -> walltime; if (!self->queue) self->queue = fsd_strdup(status->queue); /* TODO in oar.c*/ /* if (!self->project) self->project = fsd_strdup(status->project); */ /* TODO if (!self->execution_hosts) { fsd_log_debug(("execution_hosts = %s", i->value)); self->execution_hosts = fsd_strdup(i->value); */ /* TODO long unsigned int start_time; if (self->start_time == 0 && sscanf(i->value, "%lu", &start_time) == 1) self->start_time = start_time; break; } case OARDRMAA_ATTR_MTIME: if (sscanf(i->value, "%lu", &modify_time) != 1) modify_time = 0; */ /* if (!strcmp(oar_state,OAR_JS_TERMINATED)) { fsd_log_debug((("YOP %s %s\n",oar_state,OAR_JS_TERMINATED); } else { fsd_log_debug((("PAS GLOP %s %s\n",oar_state,OAR_JS_TERMINATED); } */ if( oar_state ) fsd_log_debug(( "oar_state: %s", oar_state )); if( exit_status != -2 ) { fsd_log_debug(( "exit_status: %d", exit_status )); self->exit_status = exit_status; } if(oar_state) { if(!strcmp(oar_state,OAR_JS_WAITING)||!strcmp(oar_state,OAR_JS_TOLAUNCH)) { /* DRMAA_PS_QUEUED_ACTIVE */ self->state = DRMAA_PS_QUEUED_ACTIVE; self->flags &= ~FSD_JOB_HOLD; } else if (!strcmp(oar_state,OAR_JS_HOLD)) { /* DRMAA_PS_SYSTEM_ON_HOLD / DRMAA_PS_USER_ON_HOLD [default] / DRMAA_PS_USER_SYSTEM_ON_HOLD */ /* TODO: system/user */ self->state = DRMAA_PS_USER_ON_HOLD; self->flags |= FSD_JOB_HOLD; } else if (!strcmp(oar_state,OAR_JS_TOERROR)||!strcmp(oar_state,OAR_JS_ERROR)) { /* DRMAA_PS_FAILED */ printf("OAR_JS_TOERROR||OAR_JS_ERROR -> DRMAA_PS_FAILED\n"); self->state = DRMAA_PS_FAILED; self->exit_status = -1; } else if (!strcmp(oar_state,OAR_JS_LAUNCHING)||!strcmp(oar_state,OAR_JS_RUNNING)||!strcmp(oar_state,OAR_JS_FINISHING)) { /* DRMAA_PS_RUNNING */ self->state = DRMAA_PS_RUNNING; } else if (!strcmp(oar_state,OAR_JS_SUSPENDED)||!strcmp(oar_state,OAR_JS_RESUMING)) { /* DRMAA_PS_SYSTEM_SUSPENDED / DRMAA_PS_USER_SUSPENDED [default] */ /* TODO: system/user */ self->state = DRMAA_PS_USER_SUSPENDED; } else if (!strcmp(oar_state,OAR_JS_TERMINATED)) { /* DRMAA_PS_DONE */ fsd_log_debug(("strcmp(oar_state,OAR_JS_TERMINATED)/n")); self->flags &= FSD_JOB_TERMINATED_MASK; self->flags |= FSD_JOB_TERMINATED; if (exit_status != -2) { /*has exit code */ if( self->exit_status == 0) { fsd_log_debug(("DRMAA_PS_DONE\n")); self->state = DRMAA_PS_DONE; } else { /* TODO: is not possible with OAR ??? */ fsd_log_debug(("DRMAA_PS_FAILED\n")); self->state = DRMAA_PS_FAILED; } } else { /* TODO: is not possible with OAR ??? */ self->state = DRMAA_PS_FAILED; self->exit_status = -1; } self->end_time = modify_time; /* END_TIME */ /* TODO */ } else /* OAR_JS_TOASKRESERV || other */ { /* DRMAA_PS_UNDETERMINED */ self->state = DRMAA_PS_UNDETERMINED; } } /* switch( oar_state ) */ /* TODO: must be adapted { case 'C': #*Job is completed after having run. *# self->flags &= FSD_JOB_TERMINATED_MASK; self->flags |= FSD_JOB_TERMINATED; if (exit_status != -2) { #*has exit code *# if( self->exit_status == 0) self->state = DRMAA_PS_DONE; else self->state = DRMAA_PS_FAILED; } else { self->state = DRMAA_PS_FAILED; self->exit_status = -1; } self->end_time = modify_time; #*take last modify time as end time *# break; case 'E': #*Job is exiting after having run. - MM: ignore exiting state (transient state) - outputs might have not been transfered yet, MM2: mark job as running if current job status is undetermined - fix "ps after job was ripped" *# if (self->state == DRMAA_PS_UNDETERMINED) self->state = DRMAA_PS_RUNNING; break; case 'H': #*Job is held. *# self->state = DRMAA_PS_USER_ON_HOLD; self->flags |= FSD_JOB_HOLD; break; case 'Q': #*Job is queued, eligible to run or routed. *# case 'W': #*Job is waiting for its execution time to be reached. *# self->state = DRMAA_PS_QUEUED_ACTIVE; self->flags &= ~FSD_JOB_HOLD; break; case 'R': #*Job is running. *# case 'T': #*Job is being moved to new location (?). *# { if( self->flags & FSD_JOB_SUSPENDED ) self->state = DRMAA_PS_USER_SUSPENDED; else self->state = DRMAA_PS_RUNNING; break; } case 'S': #*(Unicos only) job is suspend. *# self->state = DRMAA_PS_SYSTEM_SUSPENDED; break; case 0: default: self->state = DRMAA_PS_UNDETERMINED; break; } */ fsd_log_debug(( "job_ps: %s", drmaa_job_ps_to_str(self->state) )); /* TODO adapt ??? */ /* { int hours, minutes, seconds; long mem; if( cpu_usage && sscanf( cpu_usage, "%d:%d:%d", &hours, &minutes, &seconds ) == 3 ) { self->cpu_usage = 60*( 60*hours + minutes ) + seconds; fsd_log_debug(( "cpu_usage: %s=%lds", cpu_usage, self->cpu_usage )); } if( mem_usage && sscanf( mem_usage, "%ldkb", &mem ) == 1 ) { self->mem_usage = 1024*mem; fsd_log_debug(( "mem_usage: %s=%ldB", mem_usage, self->mem_usage )); } if( vmem_usage && sscanf( vmem_usage, "%ldkb", &mem ) == 1 ) { self->vmem_usage = 1024*mem; fsd_log_debug(( "vmem_usage: %s=%ldB", vmem_usage, self->vmem_usage )); } if( walltime && sscanf( walltime, "%d:%d:%d", &hours, &minutes, &seconds ) == 3 ) { self->walltime = 60*( 60*hours + minutes ) + seconds; fsd_log_debug(( "walltime: %s=%lds", walltime, self->walltime )); } } */ } void oardrmaa_job_on_missing( fsd_job_t *self ) /*TODO: do we need of this ? */ { fsd_drmaa_session_t *session = self->session; unsigned missing_mask = 0; fsd_log_enter(( "({job_id=%s})", self->job_id )); fsd_log_warning(( "self %s missing from DRM queue", self->job_id )); switch( session->missing_jobs ) { case FSD_REVEAL_MISSING_JOBS: missing_mask = 0; break; case FSD_IGNORE_MISSING_JOBS: missing_mask = 0x73; break; case FSD_IGNORE_QUEUED_MISSING_JOBS: missing_mask = 0x13; break; } fsd_log_debug(( "last job_ps: %s (0x%02x); mask: 0x%02x", drmaa_job_ps_to_str(self->state), self->state, missing_mask )); if( self->state < DRMAA_PS_DONE && (self->state & ~missing_mask) ) fsd_exc_raise_fmt( FSD_ERRNO_INTERNAL_ERROR, "self %s missing from queue", self->job_id ); if( (self->flags & FSD_JOB_TERMINATED_MASK) == 0 ) { self->flags &= FSD_JOB_TERMINATED_MASK; self->flags |= FSD_JOB_TERMINATED; } if( (self->flags & FSD_JOB_ABORTED) == 0 && session->missing_jobs == FSD_IGNORE_MISSING_JOBS ) { /* assume everthing was ok */ self->state = DRMAA_PS_DONE; self->exit_status = 0; } else { /* job aborted */ self->state = DRMAA_PS_FAILED; self->exit_status = -1; } fsd_log_return(( "; job_ps=%s, exit_status=%d", drmaa_job_ps_to_str(self->state), self->exit_status )); } ./oar-2.5.2/sources/extra/oar_drmaa/drmaa.c0000644000175000017500000001153011757171206016544 0ustar plbplb/* $Id: drmaa.c 353 2010-10-18 13:45:14Z mamonski $ */ /* * FedStage DRMAA for PBS Pro * Copyright (C) 2006-2007 FedStage Systems * * 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 3 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* * Adapted from pbs_drmaa/drmaa.c */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include static fsd_drmaa_session_t * oardrmaa_new_session( fsd_drmaa_singletone_t *self, const char *contact ) { return oardrmaa_session_new( contact ); } static fsd_template_t * oardrmaa_new_job_template( fsd_drmaa_singletone_t *self ) { return drmaa_template_new(); } static const char * oardrmaa_get_contact( fsd_drmaa_singletone_t *self ) { const char *contact = NULL; fsd_mutex_lock( &self->session_mutex ); if( self->session ) contact = self->session->contact; if( contact == NULL ) contact = "localhost"; fsd_mutex_unlock( &self->session_mutex ); return contact; } static void oardrmaa_get_version( fsd_drmaa_singletone_t *self, unsigned *major, unsigned *minor ) { *major = 1; *minor = 0; } static const char * oardrmaa_get_DRM_system( fsd_drmaa_singletone_t *self ) { return "OAR"; } static const char * oardrmaa_get_DRMAA_implementation( fsd_drmaa_singletone_t *self ) { return PACKAGE_NAME" v. "PACKAGE_VERSION " "; } fsd_iter_t * oardrmaa_get_attribute_names( fsd_drmaa_singletone_t *self ) { static const char *attribute_names[] = { DRMAA_REMOTE_COMMAND, DRMAA_JS_STATE, DRMAA_WD, DRMAA_JOB_CATEGORY, DRMAA_NATIVE_SPECIFICATION, DRMAA_BLOCK_EMAIL, DRMAA_START_TIME, DRMAA_JOB_NAME, DRMAA_INPUT_PATH, DRMAA_OUTPUT_PATH, DRMAA_ERROR_PATH, DRMAA_JOIN_FILES, DRMAA_TRANSFER_FILES, DRMAA_WCT_HLIMIT, DRMAA_DURATION_HLIMIT, NULL }; return fsd_iter_new_const( attribute_names, -1 ); } fsd_iter_t * oardrmaa_get_vector_attribute_names( fsd_drmaa_singletone_t *self ) { static const char *attribute_names[] = { DRMAA_V_ARGV, DRMAA_V_ENV, DRMAA_V_EMAIL, NULL }; return fsd_iter_new_const( attribute_names, -1 ); } static int oardrmaa_wifexited( int *exited, int stat, char *error_diagnosis, size_t error_diag_len ) { *exited = (stat <= 125); return DRMAA_ERRNO_SUCCESS; } static int oardrmaa_wexitstatus( int *exit_status, int stat, char *error_diagnosis, size_t error_diag_len ) { *exit_status = stat & 0xff; return DRMAA_ERRNO_SUCCESS; } static int oardrmaa_wifsignaled( int *signaled, int stat, char *error_diagnosis, size_t error_diag_len ) { *signaled = (stat > 128 ); return DRMAA_ERRNO_SUCCESS; } static int oardrmaa_wtermsig( char *signal, size_t signal_len, int stat, char *error_diagnosis, size_t error_diag_len ) { int sig = stat & 0x7f; strlcpy( signal, fsd_strsignal(sig), signal_len ); return DRMAA_ERRNO_SUCCESS; } static int oardrmaa_wcoredump( int *core_dumped, int stat, char *error_diagnosis, size_t error_diag_len ) { /* TODO: Can OAR support it ? */ *core_dumped = 0; return DRMAA_ERRNO_SUCCESS; } static int oardrmaa_wifaborted( int *aborted, int stat, char *error_diagnosis, size_t error_diag_len ) { fsd_log_info(("wifaborted(%d)>>>>", stat)); fsd_log_debug(("wifaborted(%d)", stat)); if ( stat == -1 ) { *aborted = true; } else if ( stat <= 125 ) { *aborted = false; } else if ( stat == 126 || stat == 127 ) { *aborted = true; } else switch( stat & 0x7f ) { case SIGTERM: case SIGKILL: *aborted = true; break; default: *aborted = false; break; } return DRMAA_ERRNO_SUCCESS; } fsd_drmaa_singletone_t _fsd_drmaa_singletone = { NULL, FSD_MUTEX_INITIALIZER, oardrmaa_new_session, oardrmaa_new_job_template, oardrmaa_get_contact, oardrmaa_get_version, oardrmaa_get_DRM_system, oardrmaa_get_DRMAA_implementation, oardrmaa_get_attribute_names, oardrmaa_get_vector_attribute_names, oardrmaa_wifexited, oardrmaa_wexitstatus, oardrmaa_wifsignaled, oardrmaa_wtermsig, oardrmaa_wcoredump, oardrmaa_wifaborted }; ./oar-2.5.2/sources/extra/oar_drmaa/README.rst0000644000175000017500000000410411757171206017002 0ustar plbplboar_dramaa =========== A try to provide drmaa api for oar based on pbs-drmaa Main adpatation ================ * remove log reader stuff because OAR keep job history in database. Features ========= - DRMAA_REMOTE_COMMAND (TODO/totest) - DRMAA_WD (TODO)/totest - DRMAA_V_ARGV (TODO/totest) - DRMAA_INPUT_PATH (TODO/totested) Limitation and not supported features ====================================== - DRMAA_START_TIME (oar can do it by dependenct on advance reservation job log === oar_drmaa/Makefile.am modify configure.ace autoreconf --install adapt drmaa s/pbs/oar/ todo ==== * oar_statjob (id / attrl = NULL see man pbs_statjob) * oar_sumbit / attributs / default attributs * end_time * start_time * frag trick * doc * testing * glib-json * install / packaging * attributes * ressources * native specification options * default_attributes * system/user hold event * resume/suspend-user/system event * curl/json-glib/error modularities * OAR_ERROR * free json_reader result * complet copyright header * example with basic authentification/httpsn, other ??? log of modification ==================== basic tests: curl and json-glib ------------------------------- - use json-glib-0.12.0 for reader / builder json_builder_reset (builder); oar_drmaa file -------------- - in general: s/pbs/oar/ - drmaa.c: general fonction nothing tricky) - session.h: remove some struct pbsdrmaa_session_s fields - session.c: remove PBS_PRO, comment some code to adpat later - job.h: - job.c: big function to adapt: oardrmaa_job_update - submit.h: - submit.c: - util.h: - util.c: remove pbsdrmaa_write_tmpfile, fsd_getline and fsd_getline_buffered.Todo: Maps PBS error code into DMRAA code. - oar.h: to replace pbs_ifl.h - oar_error.h: to replace pbs_error.h - oar.c: general: ~~~~~~~~ - do we need of oar_attrib.gperf (hash between drmaa / oar attributes) . - OAR_ERRNO_X in oar_erroh.h - oar_errno (global variable ! Do we need a mutex ? Not sure, must verify that there are mutexes around oar_drmaa function build system ------------ - oar_drmaa/Makefile.am - modify configure.ac - autoreconf --install ./oar-2.5.2/sources/extra/oar_drmaa/Makefile.am0000644000175000017500000000355111757171206017354 0ustar plbplb# $Id: $ # # Adapted by Auguste # # FedStage DRMAA for PBS Professional # Copyright (C) 2006-2009 FedStage Systems # # 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 3 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # GPERF = @GPERF@ GPERFFLAGS = --readonly-tables AM_CPPFLAGS = -I$(top_srcdir)/drmaa_utils -I /home/auguste/prog/json-glib-0.12.0/include/json-glib-1.0/ -I /usr/include/glib-2.0/ -I /usr/lib/glib-2.0/include/ -fPIC lib_LTLIBRARIES = liboardrmaa.la # Below do we need of oar_attrib.gperf and oar_attrib.h ? liboardrmaa_la_SOURCES = \ drmaa.c \ job.c job.h \ session.c session.h \ oar_attrib.gperf oar_attrib.h \ submit.c submit.h \ util.c util.h \ oar.c oar.h \ oar_error.h BUILT_SOURCES = oar_attrib.c EXTRA_DIST = oar_attrib.c #dist_sysconf_DATA = oar_drmaa.conf.example liboardrmaa_la_LIBADD = ../drmaa_utils/drmaa_utils/libdrmaa_utils.la liboardrmaa_la_LDFLAGS = -lcurl -version-info @OAR_DRMAA_VERSION_INFO@ -L/home/auguste/prog/json-glib-0.12.0/lib/ -ljson-glib-1.0 # see ../m4/ax_pbs.m4 for @OAR_xxx@ #liboardrmaa_la_LIBADD = ../drmaa_utils/drmaa_utils/libdrmaa_utils.la @OAR_LIBS@ #liboardrmaa_la_LDFLAGS = @OAR_LDFLAGS@ -version-info @OAR_DRMAA_VERSION_INFO@ .gperf.c: $(GPERF) $(GPERFFLAGS) --output-file=$@ $< if GCC oar_attrib.lo: CFLAGS += @GCC_W_NO_MISSING_FIELD_INITIALIZERS@ endif ./oar-2.5.2/sources/extra/cpp-scheduler/0000755000175000017500000000000011757171206016125 5ustar plbplb./oar-2.5.2/sources/extra/cpp-scheduler/oarsched.pro0000644000175000017500000000051411757171206020437 0ustar plbplbCONFIG+=qt debug console QT-= gui QT+= sql SOURCES+=Oar_iolib.cc SOURCES+=Oar_conflib.cc SOURCES+=Gantt_hole_storage.cc SOURCES+=Oar_resource_tree.cc SOURCES+=Oar_sched_gantt_with_timesharing_and_fairsharing.cc HEADERS+=Gantt_hole_storage.H HEADERS+=Oar_resource_tree.H TARGET=Oar_sched_gantt_with_timesharing_and_fairsharing ./oar-2.5.2/sources/extra/cpp-scheduler/UnitaryTests/0000755000175000017500000000000011757171206020603 5ustar plbplb./oar-2.5.2/sources/extra/cpp-scheduler/UnitaryTests/TestResourceTree.cc0000644000175000017500000001501011757171206024356 0ustar plbplb#include #include "TestResourceTree.H" #include "../Oar_resource_tree.H" using namespace OAR::Schedulers::ResourceTree; CPPUNIT_TEST_SUITE_REGISTRATION( TestResourceTree ); void TestResourceTree::setUp() { } void TestResourceTree::tearDown() { } void TestResourceTree::testConstructor() { TreeNode *n, *n2; // essai du constructeur avec juste le needed_children_number n = new TreeNode(123); CPPUNIT_ASSERT_EQUAL( 123, n->needed_children_number); CPPUNIT_ASSERT_EQUAL( static_cast(0), n->father ); CPPUNIT_ASSERT_EQUAL( static_cast(0), n->children.size() ); CPPUNIT_ASSERT_EQUAL( std::string(), n->name ); CPPUNIT_ASSERT_EQUAL( std::string(), n->value ); CPPUNIT_ASSERT_EQUAL( 0, n->level ); CPPUNIT_ASSERT_EQUAL( 0, n->max_available_children ); CPPUNIT_ASSERT_EQUAL( static_cast(0), n->prev_brother ); CPPUNIT_ASSERT_EQUAL( static_cast(0), n->next_brother ); CPPUNIT_ASSERT_EQUAL( static_cast(0), n->first_child ); CPPUNIT_ASSERT_EQUAL( 0, n->current_child_number ); // essai du constructeur avec l'ensemble des parametres n2 = n; n = new TreeNode(n2, static_cast(0), "toto", "tutu", 10, 11, 12, n2+1, n2+2, n2+3, 14 ); CPPUNIT_ASSERT_EQUAL( 11, n->needed_children_number); CPPUNIT_ASSERT_EQUAL( n2, n->father ); CPPUNIT_ASSERT_EQUAL( static_cast(0), n->children.size() ); CPPUNIT_ASSERT_EQUAL( std::string("toto"), n->name ); CPPUNIT_ASSERT_EQUAL( std::string("tutu"), n->value ); CPPUNIT_ASSERT_EQUAL( 10, n->level ); CPPUNIT_ASSERT_EQUAL( 12, n->max_available_children ); CPPUNIT_ASSERT_EQUAL( static_cast(n2+1), n->prev_brother ); CPPUNIT_ASSERT_EQUAL( static_cast(n2+2), n->next_brother ); CPPUNIT_ASSERT_EQUAL( static_cast(n2+3), n->first_child ); CPPUNIT_ASSERT_EQUAL( 14, n->current_child_number ); delete n; delete n2; } void TestResourceTree::testAccessor() { TreeNode *n; TreeNode *n2= (TreeNode *)123456; // essai du constructeur avec l'ensemble des parametres n = new TreeNode(static_cast((void*)5), static_cast(0), "toto", "tutu", 10, 11, 12, n2+1, n2+2, n2+3, 14 ); CPPUNIT_ASSERT_EQUAL( true, is_node_a_leaf(n) ); CPPUNIT_ASSERT_EQUAL( 21, set_needed_children_number(n, 21) ); CPPUNIT_ASSERT_EQUAL( 21, n->needed_children_number ); CPPUNIT_ASSERT_EQUAL( 21, get_needed_children_number(n) ); CPPUNIT_ASSERT_EQUAL( static_cast(0), n->children.size() ); CPPUNIT_ASSERT_EQUAL( std::string("toto"), get_current_resource_name(n) ); CPPUNIT_ASSERT_EQUAL( std::string("tutu"), get_current_resource_value(n) ); CPPUNIT_ASSERT_EQUAL( 10, n->level ); CPPUNIT_ASSERT_EQUAL( 12, n->max_available_children ); CPPUNIT_ASSERT_EQUAL( static_cast(n2+1), get_previous_brother(n) ); CPPUNIT_ASSERT_EQUAL( static_cast(n2+2), get_next_brother(n) ); CPPUNIT_ASSERT_EQUAL( static_cast(n2+3), get_initial_child(n) ); CPPUNIT_ASSERT_EQUAL( static_cast((void*)5), get_father(n) ); CPPUNIT_ASSERT_EQUAL( 14, get_current_children_number(n) ); CPPUNIT_ASSERT_EQUAL( 10, get_current_level(n) ); CPPUNIT_ASSERT_EQUAL( 12, get_max_available_children(n) ); delete n; } /** This fonction builds a tree with 5 nodes : a root (cluster, alpha) , with two childs (network, giga) (network, myri). One of the child (network, giga) with two other childs (node, p1) et (node, p2). This tree is cloned, then the subtree without child is suppressed. */ static void test1fils(TreeNode *a) { CPPUNIT_ASSERT_EQUAL( static_cast(1), a->children.size() ); CPPUNIT_ASSERT_EQUAL( a->children.find("giga")->second, get_initial_child( a ) ); CPPUNIT_ASSERT_EQUAL( a, get_father( get_initial_child( a ) ) ); CPPUNIT_ASSERT_EQUAL( static_cast(0), get_next_brother( get_initial_child ( a ) ) ); CPPUNIT_ASSERT_EQUAL( static_cast(0), get_next_brother( a ) ); CPPUNIT_ASSERT_EQUAL( static_cast(0), get_previous_brother( get_initial_child ( a ) ) ); CPPUNIT_ASSERT_EQUAL( static_cast(0), get_previous_brother( a ) ); } static void test2fils( TreeNode *a ) { CPPUNIT_ASSERT_EQUAL( static_cast(2), a->children.size() ); // le nouveau fils a ete ajoute en premier (insertion en tete) //std::cerr << " myri :" << a->children.find("myri")->second // << " giga :" << a->children.find("giga")->second << std::endl; CPPUNIT_ASSERT_EQUAL( a->children.find("myri")->second, get_initial_child( a ) ); CPPUNIT_ASSERT_EQUAL( a, get_father( get_initial_child( a ) ) ); CPPUNIT_ASSERT_EQUAL( static_cast( a->children.find("giga")->second ), get_next_brother( get_initial_child ( a ) ) ); CPPUNIT_ASSERT_EQUAL( static_cast(0), get_next_brother( get_next_brother( a ) ) ); CPPUNIT_ASSERT_EQUAL( static_cast(0), get_previous_brother( get_initial_child ( a ) ) ); CPPUNIT_ASSERT_EQUAL( static_cast(get_initial_child( a ) ), get_previous_brother( get_next_brother( get_initial_child( a ) ) ) ); CPPUNIT_ASSERT_EQUAL( static_cast(0), get_previous_brother( a ) ); } void TestResourceTree::testTreeManipulation() { TreeNode *nroot; TreeNode *cnroot; // essai du constructeur avec l'ensemble des parametres nroot = new TreeNode(static_cast(0), static_cast(0), "cluster", "alpha", 1, 0, 0, 0, 0, 0, 0); add_child(nroot, "network", "giga"); test1fils(nroot); add_child(nroot, "network", "giga"); // this should not be inserted CPPUNIT_ASSERT_EQUAL( static_cast(1), nroot->children.size() ); // ajout du deuxième fils add_child(nroot, "network", "myri"); test2fils( nroot ); // ajout de deux fils au fils giga add_child(nroot->children.find( "giga" )->second , "node", "p1"); add_child(nroot->children.find( "giga" )->second , "node", "p2"); CPPUNIT_ASSERT_EQUAL( static_cast(2), nroot->children.find("giga")->second->children.size() ); CPPUNIT_ASSERT_EQUAL( nroot, get_father( get_father( nroot->first_child->next_brother->first_child->next_brother ) ) ); // On clone cnroot = clone(nroot); // il faudrait refaire les mêmes tests CPPUNIT_ASSERT( cnroot != nroot ); test2fils(cnroot); // on efface le clone delete_subtree(cnroot); test2fils(nroot); // on efface l'original delete_subtree( nroot ); } // tester: clone, is_node_a_leaf, add_child, // delete_subtree,delete_tree_nodes_with_not_enough_resources, // get_tree_leafs, delete_unnecessary_subtree ./oar-2.5.2/sources/extra/cpp-scheduler/UnitaryTests/TestResourceTree.H0000644000175000017500000000072511757171206024167 0ustar plbplb#ifndef TESTRESOURCETREE_H #define TESTRESOURCETREE_H #include class TestResourceTree : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE( TestResourceTree ); CPPUNIT_TEST( testConstructor ); CPPUNIT_TEST( testAccessor ); CPPUNIT_TEST( testTreeManipulation ); CPPUNIT_TEST_SUITE_END(); public: void setUp(); void tearDown(); void testConstructor(); void testAccessor(); void testTreeManipulation(); }; #endif ./oar-2.5.2/sources/extra/cpp-scheduler/UnitaryTests/README0000644000175000017500000000006611757171206021465 0ustar plbplbUtilization of libcpppunit to do some test on the code./oar-2.5.2/sources/extra/cpp-scheduler/UnitaryTests/Makefile0000644000175000017500000000033311757171206022242 0ustar plbplbCC= g++ CFLAGS= -g -Wall CXXFLAGS= -g -Wall LDLIBS= -ldl -lcppunit all: AllTest AllTest: AllTest.o TestResourceTree.o ../Oar_resource_tree.H ../Oar_resource_tree.o ../Gantt_hole_storage.o clean: rm *.o ../*.o ./oar-2.5.2/sources/extra/cpp-scheduler/UnitaryTests/AllTest.cc0000644000175000017500000000074711757171206022472 0ustar plbplb#include #include #include int main(int argc, char **argv) { CppUnit::Test *suite = CppUnit::TestFactoryRegistry::getRegistry().makeTest(); CppUnit::TextUi::TestRunner runner; runner.addTest( suite ); runner.setOutputter( new CppUnit::CompilerOutputter( &runner.result(), std::cerr ) ); bool wasSuccess = runner.run(); return wasSuccess ? 0 : 1; } ./oar-2.5.2/sources/extra/cpp-scheduler/Oar_sched_gantt_with_timesharing_and_fairsharing.cc0000644000175000017500000006257311757171206030361 0ustar plbplb/* #!/usr/bin/perl # $Id$ #-d:DProf use strict; use DBI(); use OAR::IO; use Data::Dumper; use OAR::Modules::Judas qw(oar_debug oar_warn oar_error set_current_log_category); use OAR::Conf qw(init_conf dump_conf get_conf is_conf); use Gantt_hole_storage; use Storable qw(dclone); use Time::HiRes qw(gettimeofday); # Log category set_current_log_category('scheduler'); */ #include "Oar_conflib.H" #include "Oar_iolib.H" #include "Oar_resource_tree.H" #include using namespace std; /* ############################################################################### # Fairsharing parameters # ########################## */ /// # Avoid problems if there are too many waiting jobs static const unsigned int Karma_max_number_of_jobs_treated_per_user = 30; ///# number of seconds to consider for the fairsharing static const unsigned int Karma_window_size = 3600 * 30 * 24; ///# specify the target percentages for project names (0 if not specified) struct karma_proj_target_t { unsigned int first; unsigned int default; }; const struct karma_proj_target_t Karma_project_targets = { 75, 25 }; ///# specify the target percentages for users (0 if not specified) map Karma_user_targets = map(pair("oar", 100)); ///# weight given to each criteria static const unsigned int Karma_coeff_project_consumption = 0; static const unsigend int Karma_coeff_user_consumption = 2; static const unsigned int Karma_coeff_user_asked_consumption = 1; //############################################################################### static time_t initial_time; static unsigned int timeout = 10; static unsigned int Minimum_timeout_per_job = 2; //# Constant duration time of a besteffort job static const unsigned int besteffort_duration = 5*60; // # $security_time_overhead is the security time (second) used to be sure there // # are no problem with overlaping jobs static unsigned int security_time_overhead = 1; static unsigned int minimum_hole_time = 0; //# You can add an order preference on resources assigned by the //# system(SQL ORDER syntax) static string Order_part; static vector Sched_available_suspended_resource_type; static string Resources_to_always_add_type; static vector Resources_to_always_add; static unsigned int current_time; static string queue; // what are timesharing gantts ? map, Gant_hole_storage::Gantt *> timesharing_gantts; // # Create the Gantt Diagrams // #Init the gantt chart with all resources Gant_hole_storage::Gantt *pgantt = 0; // variables used in real scheduling vector alive_resources_vector; vector Dead_resources; vector jobs; // variables used in karma sorting map Karma_sum_time; map, unsigned int> Karma_projects; map, unsigned int> Karma_users; void init_conf(int argc, char **argv) { initial_time = time(); init_conf(getenv("OARCONFFILE")); timeout = CONFDEFAULT_INT("SCHEDULER_TIMEOUT", "10"); // # $security_time_overhead is the security time (second) used to be sure there // # are no problem with overlaping jobs security_time_overhead = CONFDEFAULT_INT("SCHEDULER_JOB_SECURITY_TIME", "1"); minimum_hole_time = CONFDEFAULT_INT("SCHEDULER_GANTT_HOLE_MINIMUM_TIME", "0"); Order_part = get_conf("SCHEDULER_RESOURCE_ORDER"); string sched_available_suspended_resource_type_tmp = get_conf("SCHEDULER_AVAILABLE_SUSPENDED_RESOURCE_TYPE"); if (sched_available_suspended_resource_type_tmp == "") Sched_available_suspended_resource_type.push_back("default"); else { int cur_pos = 0; int end_pos; while(cur_pos < npos ) { end_pos = sched_available_suspended_resource_type_tmp.find_first_of(" ", cur_pos); if (endpos != npos) { Sched_available_suspended_resource_type.push_back(sched_available_suspended_resource_type_tmp.substr(cur_pos, end_pos-cur_pos)); cur_pos = end_pos + 1; } else { Sched_available_suspended_resource_type.push_back(sched_available_suspended_resource_type_tmp.substr(cur_pos, npos)); cur_pos = npos; } } } // # Look at resources that we must add for each job Resources_to_always_add_type = get_conf("SCHEDULER_RESOURCES_ALWAYS_ASSIGNED_TYPE"); Resources_to_always_add; if (argc < 3) { cerr << "[oar_sched_gantt_with_timesharing_and_fairsharing] no queue specified on command line" << endl; exit(1); } else { queue = argv[1]; current_time = atoi( argv[2] ); } } void init_sched() { //# Init // my $base = OAR::IO::connect(); // my $base_ro = OAR::IO::connect_ro(); // why use two bd access ? performance ? // I translate with only one access OAR::IO::connect(); //oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] Begining of Gantt scheduler on queue $queue at time $current_time\n"); // # First check states of resources that we must add for each job if ( Resources_to_always_add_type != "") { multimap tmp_result_state_resources = OAR::IO::get_specific_resource_states( Resources_to_always_add_type); if ( tmp_result_state_resources.count("Suspected") > 0 ) { cerr << "[oar_sched_gantt_with_timesharing] There are resources that are specified in oar.conf (SCHEDULER_RESOURCES_ALWAYS_ASSIGNED_TYPE) which are Suspected. So I cannot schedule any job now." << endl; exit(1); } else { if (tmp_result_state_resources.count("Alive") > 0) { // copy alive resource_id multimap::iterator it = tmp_result_state_resources.lower_bound("Alive"); multimap::iterator itend = tmp_result_state_resources.upper_bound("Alive"); vector res_vec; while(it != it.end) { res_vec.insert( it->second ); it++; } Resources_to_always_add = res_vec; // oar_debug("[oar_sched_gantt_with_timesharing] Assign these resources for each jobs: @Resources_to_always_add\n"); } } } } void init_gantt() { // # Create the Gantt Diagrams // #Init the gantt chart with all resources unsigned int max_resources=0 for(vector::it = res.begin(); it != res.end(); it++) { max_resources = max( max_resources, it->resource_id); } vector vec(max_resources, 0); vector res = OAR::IO::list_resources(); for(vector::it = res.begin(); it != res.end(); it++) { vec[it->resource_id] = 1; } pgantt = Gantt_hole_storage::(max_resources, minimum_hole_time); Gantt_hole_storage::add_new_resources(pgantt, vec); } static boost::regex re_plit_coma(","); static boost::regex re_user_name("^\\s*([\\w\\*]+)\\s*$"); pair parse_timesharing(string str, string job_user, string job_name ) { string user = "*"; string name = "*"; /* use boost regex instead of QtSQL (better c++ integration (string)) */ boost::sregex_token_iterator i(s.begin(), s.end(), re_split_coma, -1); boost::sregex_token_iterator j; while(i != j) { string &s =*i; cmatch user_or_name; if ( boost::regex_match(s, user_or_name, re_user_name) ) { if ( user_or_name[1] == "user" ) user = job_user; else if ( user_or_name[1] == "name" && job_name != "" ) name = job_name; } i++; } return pair(user, name); } void init_gantt_scheduled_job() { //# Take care of currently scheduled jobs (gantt in the database) // TODO: la partie order ne sert a rien ? pair< vector, map > order_and_already_scheduled = OAR::IO::get_gantt_scheduled_jobs(); for( order_and_already_scheduled.second.iterator it = order_and_already_scheduled.second.begin(); it != order_and_already_scheduled.second.end(); it++) { i = it->first; map types = OAR::IO::get_current_job_types(i); // # Do not take care of besteffort jobs if ( types.find("besteffort") == types.end() or queue == "besteffort" ) { string user; string name; if ( types.find("timesharing") != types.end() ) { pair user_name = parse_timesharing( types["timesharing"], it->second.job_user, it->second.job_name); string user = user_name.first; string name = user_name.second; if ( timesharing_gantts.find( pair( user, name) ) == timesharing_gantts.end() ) { timesharing_gantts[pair( user, name)] = dclone(pgantt); // oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] Create new gantt for ($user, $name)\n"); } } // foreach my $i (keys(%already_scheduled_jobs)){ // my $types = OAR::IO::get_current_job_types($base,$i); // # Do not take care of besteffort jobs // if ((! defined($types->{besteffort})) or ($queue eq "besteffort")){ // my $user; // my $name; // if (defined($types->{timesharing})){ // ($user, $name) = parse_timesharing($types->{timesharing}, $already_scheduled_jobs{$i}->[5], $already_scheduled_jobs{$i}->[6]); // if (!defined($timesharing_gantts->{$user}->{$name})){ // $timesharing_gantts->{$user}->{$name} = dclone($gantt); // oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] Create new gantt for ($user, $name)\n"); // } // } vector resource_list = it->second.resource_id_vec; job_duration = it->second.moldable_walltime; // TODO: est-ce bien moldable walltime ? if ( it->second.state == "Suspended" ) { //# Remove resources of the type specified in SCHEDULER_AVAILABLE_SUSPENDED_RESOURCE_TYPE resource_list = OAR::IO::get_job_current_resources(it->second.moldable_id, Sched_available_suspended_resource_type); if (resource_list.size() == 0) continue; } if ( it->second.state.suspended ) { //# This job was suspended so we must recalculate the walltime job_duration += OAR::IO::get_job_suspended_sum_duration(i, current_time); // TODO: i == jobid ? assert(i == it->second.job_id); } // my @resource_list = @{$already_scheduled_jobs{$i}->[3]}; // my $job_duration = $already_scheduled_jobs{$i}->[1]; // if ($already_scheduled_jobs{$i}->[4] eq "Suspended"){ // # Remove resources of the type specified in SCHEDULER_AVAILABLE_SUSPENDED_RESOURCE_TYPE // @resource_list = OAR::IO::get_job_current_resources($base, $already_scheduled_jobs{$i}->[7],\@Sched_available_suspended_resource_type); // next if ($#resource_list < 0); // } // if ($already_scheduled_jobs{$i}->[8] eq "YES"){ // # This job was suspended so we must recalculate the walltime // $job_duration += OAR::IO::get_job_suspended_sum_duration($base,$i,$current_time); // } unsigned int max_resources = *max_element(resource_list.begin() resource_list.end()); vector vec(max_resources, 0); for(resource_list.iterator r = resource_list.begin(); r != resource_list.end(); r++) vec[*r]=1; // my $vec = ''; // foreach my $r (@resource_list){ // vec($vec,$r,1) = 1; // } //#Fill all other gantts for( timesharing_gantts.iterator itts = timesharing_gantts.begin(); itts != timesharing_gantts.end(); itts++) { u = itts->first.first; n = itts->first.second; if (user == "" || name == "" || u != user || n != name) { Gantt_hole_storage::set_occupation(itts->second, it->second.start_time, job_duration + security_time_overhead, vec ); } } Gantt_hole_storage::set_occupation( pgantt, it->second.start_time, job_duration + security_time_overhead, vec); } } } // #Fill all other gantts // foreach my $u (keys(%{$timesharing_gantts})){ // foreach my $n (keys(%{$timesharing_gantts->{$u}})){ // if ((!defined($user)) or (!defined($name)) or (($u ne $user) or ($n ne $name))){ // Gantt_hole_storage::set_occupation($timesharing_gantts->{$u}->{$n}, // $already_scheduled_jobs{$i}->[0], // $job_duration + $security_time_overhead, // $vec // ); // } // } // } // Gantt_hole_storage::set_occupation( $gantt, // $already_scheduled_jobs{$i}->[0], // $job_duration + $security_time_overhead, // $vec // ); // } // } //oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] End gantt initialization\n"); /********** # End of the initialisation ************/ void real_scheduling_begin() { //# Begining of the real scheduling //# Get list of Alive resources vector resource_list = OAR::IO::get_resources_in_state($base,"Alive"); unsigned int max_resources=0; for(resource_list.iterator r = resource_list.begin(); r != resource_list.end(); r++) max_resources = max( max_resources, r->resource_id ); alive_resources_vector = vector(max_resources, 0); for(resource_list.iterator r = resource_list.begin(); r != resource_list.end(); r++) alive_resources_vector[r->resource_id]=1; resource_list = OAR::IO::get_resources_in_state($base,"Dead"); for(resource_list.iterator r = resource_list.begin(); r != resource_list.end(); r++) Dead_resources.push_back(r->resource_id); jobs = OAR::IO::get_fairsharing_jobs_to_schedule(queue, Karma_max_number_of_jobs_treated_per_user); } /* ############################################################################### # Sort jobs depending on their previous usage # Karma sort algorithm */ void karma_sort() { Karma_sum_time = OAR::IO::get_sum_accounting_window(queue, current_time - Karma_window_size, current_time); if (Karma_sum_time.find("ASKED") == Karma_sum_time.end() ) Karma_sum_time["ASKED"] = 1; if (Karma_sum_time.find("ASKED") == Karma_sum_time.end() ) Karma_sum_time["ASKED"] = 1; Karma_projects = OAR::IO::get_sum_accounting_for_param(queue,"accounting_project", current_time - Karma_window_size, current_time); Karma_users = OAR::IO::get_sum_accounting_for_param(queue,"accounting_user", current_time - Karma_window_size, current_time); } int karma(OAR::IO::jobs_iolib_restrict j) { int note = 0; note = Karma_coeff_project_consumption * (( Karma_projects[pair(j.project,"USED")] / Karma_sum_time["USED"]) - ( Karma_project_targets[j.project] / 100)); note += Karma_coeff_user_consumption * (( Karma_users[pair(j.job_user, "USED")] / Karma_sum_time["USED"]) - (Karma_user_targets[j.project] / 100)); note += Karma_coeff_user_asked_consumption * ((Karma_users[pair(j.job_user,"ASKED")] / Karma_sum_time["ASKED"]) - (Karma_user_targets[j.project] / 100)); return(note); } struct less_jobs_iolib_restrict : public binary_function { bool operator()(OAR::IO::jobs_iolib_restrict a, OAR::IO::jobs_iolib_restrict b) { return karma(a) < karma(b); } } //############################################################################### void real_scheduler_main() { // sort jobs by karma sort(jobs.begin(), jobs.end(), less_jobs_iolib_restrict()); int job_index = 0; while ((job_index <= jobs.size() ) and ((time() - initial_time) < timeout)) { OAR::IO::jobs_iolib_restrict j = jobs[job_index]; job_index ++; oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] [" + j.job_id + "] Start scheduling (Karma note = " + karma($j) + ")\n"); unsigned int scheduler_init_date = current_time; //# Search for dependencies int skip_job = 0; // skip jobs if it is not ready vector vjobdep = OAR::IO::get_current_job_dependencies(j.job_id); for(unisgend int d = vjobdep.begin(); d != vjobdep.end(); vjobdep++) { if (skip_job) break; jobs_get_job_iolib_restrict dep_job = OAR::IO::get_job_restrict(d); if (dep_job.state != "Terminated") { gantt_job_start_time date_tmp = OAR::IO::get_gantt_job_start_time(d); if (date_tmp.start_time != 0 || date_tmp.moldable_job_id != 0) { unsigned int mold_dep_moldable_walltime = OAR::IO::get_current_moldable_job(date_tmp.moldable_job_id); unsigned int sched_tmp = date_tmp.start_time + mold_dep_moldable_walltime; if ( scheduler_init_date < sched_tmp) { scheduler_init_date = sched_tmp; } } else { string message = "Cannot determine scheduling time due to dependency with the job "<< d; OAR::IO::set_job_message(j.job_id, message); oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] ["+j.job_id+"] "+message+"\n"); skip_job = 1; break; } } else if ((dep_job.job_type == "PASSIVE") && (dep_job.exit_code != 0)) { string message = "Cannot determine scheduling time due to dependency with the job "+ d +<< "(exit code != 0)"; OAR::IO::set_job_message(j.job_id, message); oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] ["+j.job_id+"] "+message+"\n"); skip_job = 1; break; } } if (skip_job == 1) continue; Gant_hole_storage::Gantt *gantt_to_use = pgantt; map types = OAR::IO::get_current_job_types(j.job_id); if ( types.find("timesharing") != types.end() ) { pair user_name = parse_timesharing(types["timesharing"], j.job_user, j.job_name); if ( timesharing_gantts.find(user_name) == timesharing_gantts.end() ) { timesharing_gantts[user_name] = OAR::Schedulers::ResourceTree::dclone(gantt); oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] Create new gantt in phase II for ("+user_name.first+" "+user_name.second+")\n"); } gantt_to_use = timesharing_gantts[user_name]; oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] Use gantt for ("+user_name.first+" "+user_name.second+"\n"); } //#oar_debug("[oar_sched_gantt_with_timesharing] Use gantt for $j->{job_id}:\n".Gantt_hole_storage::pretty_print($gantt_to_use)."\n"); string job_properties = "'1'"; if (j.properties != "") { job_properties = j.properties; } //# Choose the moldable job to schedule // TODO: type ??? my @moldable_results; vector job_descriptions = OAR::IO::get_resources_data_structure_current_job(j.job_id); for(vector::iterator moldable = job_descriptions[0].prop_res.begin(); moldable != job_descriptions[0].prop_res.end(); moldable++) { //#my $moldable = $job_descriptions->[0]; unsigned int duration; if (types.find("besteffort") != types.end() ) { duration = besteffort_duration; } else { duration = moldable->walltime + security_time_overhead; } //# CM part vector alive_resources_vector_store = alive_resources_vector; if ( conflib::is_conf("SCHEDULER_NODE_MANAGER_WAKE_UP_CMD") ) { /**** TODO TO DO ****/ foreach my $r (OAR::IO::get_resources_that_can_be_waked_up($base, OAR::IO::get_date($base) + $duration)){ vec($alive_resources_vector, $r->{resource_id}, 1) = 1; } foreach my $r (OAR::IO::get_resources_that_will_be_out($base, OAR::IO::get_date($base) + $duration)){ vec($alive_resources_vector, $r->{resource_id}, 1) = 0; } my $str_tmp = "state_num ASC, available_upto DESC"; if (defined($Order_part)){ $Order_part = $str_tmp.",".$Order_part; }else{ $Order_part = $str_tmp; } } # CM part my $resource_id_used_list_vector = ''; my @tree_list; foreach my $m (@{$moldable->[0]}){ my $tmp_properties = "\'1\'"; if ((defined($m->{property})) and ($m->{property} ne "")){ $tmp_properties = $m->{property}; } my $tmp_tree = OAR::IO::get_possible_wanted_resources($base_ro,$alive_resources_vector,$resource_id_used_list_vector,\@Dead_resources,"$job_properties AND $tmp_properties", $m->{resources}, $Order_part); push(@tree_list, $tmp_tree); my @leafs = OAR::Schedulers::ResourceTree::get_tree_leafs($tmp_tree); foreach my $l (@leafs){ vec($resource_id_used_list_vector, OAR::Schedulers::ResourceTree::get_current_resource_value($l), 1) = 1; } } my $gantt_timeout = ($timeout - (time() - $initial_time)) / 4; $gantt_timeout = $Minimum_timeout_per_job if ($gantt_timeout < ($timeout / 3)); oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] [$j->{job_id}] find_first_hole with a timeout of $gantt_timeout\n"); my @hole = Gantt_hole_storage::find_first_hole($gantt_to_use, $scheduler_init_date, $duration, \@tree_list,$gantt_timeout); # print("[GANTT] 10 ".gettimeofday."\n"); my @res_trees; my @resources; foreach my $t (@{$hole[1]}){ # print("[GANTT] 11 ".gettimeofday."\n"); my $minimal_tree = OAR::Schedulers::ResourceTree::delete_unnecessary_subtrees($t); # print("[GANTT] 12 ".gettimeofday."\n"); push(@res_trees, $minimal_tree); foreach my $r (OAR::Schedulers::ResourceTree::get_tree_leafs($minimal_tree)){ push(@resources, OAR::Schedulers::ResourceTree::get_current_resource_value($r)); } # print("[GANTT] 13 ".gettimeofday."\n"); } push(@moldable_results, { resources => \@resources, start_date => $hole[0], duration => $duration, moldable_id => $moldable->[2] }); # CM part $alive_resources_vector = $alive_resources_vector_store ; # CM part } # Choose moldable job which will finish the first my $index_to_choose = -1; my $best_stop_time; # print("[GANTT] 14 ".gettimeofday."\n"); for (my $i=0; $i <= $#moldable_results; $i++){ #my @tmp_array = @{$moldable_results[$i]->{resources}}; if ($#{@{$moldable_results[$i]->{resources}}} >= 0){ my $tmp_stop_date = $moldable_results[$i]->{start_date} + $moldable_results[$i]->{duration}; if ((!defined($best_stop_time)) or ($best_stop_time > $tmp_stop_date)){ $best_stop_time = $tmp_stop_date; $index_to_choose = $i; } } } if ($index_to_choose >= 0){ # We can schedule the job # print("[GANTT] 15 ".gettimeofday."\n"); my $vec = ''; foreach my $r (@{$moldable_results[$index_to_choose]->{resources}}){ vec($vec, $r, 1) = 1; } Gantt_hole_storage::set_occupation( $gantt, $moldable_results[$index_to_choose]->{start_date}, $moldable_results[$index_to_choose]->{duration}, $vec ); #Fill all other gantts foreach my $u (keys(%{$timesharing_gantts})){ # print("[GANTT] 17 ".gettimeofday."\n"); foreach my $n (keys(%{$timesharing_gantts->{$u}})){ if (($gantt_to_use != $timesharing_gantts->{$u}->{$n})){ Gantt_hole_storage::set_occupation( $timesharing_gantts->{$u}->{$n}, $moldable_results[$index_to_choose]->{start_date}, $moldable_results[$index_to_choose]->{duration}, $vec ); } } } #update database push(@{$moldable_results[$index_to_choose]->{resources}},@Resources_to_always_add); OAR::IO::add_gantt_scheduled_jobs($base,$moldable_results[$index_to_choose]->{moldable_id}, $moldable_results[$index_to_choose]->{start_date},$moldable_results[$index_to_choose]->{resources}); OAR::IO::set_job_message($base,$j->{job_id},"Karma = ".sprintf("%.3f",karma($j))); }else{ my $message = "Cannot find enough resources which fit for the job $j->{job_id}"; OAR::IO::set_job_message($base,$j->{job_id},$message); oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] [$j->{job_id}] $message\n"); } # print("[GANTT] 18 ".gettimeofday."\n"); oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] [$j->{job_id}] End scheduling\n"); } OAR::IO::disconnect($base); OAR::IO::disconnect($base_ro); if ($job_index <= $#jobs){ oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing_and_fairsharing] I am not able to schedule all waiting jobs in the specified time : $timeout s\n"); } oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] End of scheduler for queue $queue\n"); ./oar-2.5.2/sources/extra/cpp-scheduler/Oar_resource_tree.cc0000644000175000017500000007474511757171206022124 0ustar plbplb#include #include "Oar_resource_tree.H" namespace OAR::Schedulers::ResourceTree { // # $Id$ // package OAR::Schedulers::ResourceTree; // use warnings; // use strict; // use Data::Dumper; // use Storable qw(dclone); // use Time::HiRes qw(gettimeofday); // ############################################################################### // # RESOURCE TREE MANAGEMENT # // ############################################################################### // # Prototypes // sub new(); // sub clone($); // sub add_child($$$); // sub get_children_list($); // sub is_node_a_leaf($); // sub get_a_child($$); // sub get_father($); // sub get_current_resource_name($); // sub get_current_resource_value($); // sub get_current_children_number($); // sub get_current_level($); // sub get_max_available_children($); // sub set_needed_children_number($$); // sub get_needed_children_number($); // sub delete_subtree($); // sub get_previous_brother($); // sub get_next_brother($); // sub get_initial_child($); // ############################################################################### // sub get_tree_leafs($); // sub delete_tree_nodes_with_not_enough_resources($); // sub delete_unnecessary_subtrees($); // ############################################################################### // # Create a tree // # arg : number of needed children // # return the ref of the created tree // sub new(){ // my $needed_children_number = shift; // my $tree_ref; // $tree_ref->[0] = undef ; # father ref // $tree_ref->[1] = undef ; # ref of a hashtable with children // $tree_ref->[2] = undef ; # name of the resource // $tree_ref->[3] = undef ; # value of this resource // $tree_ref->[4] = 0 ; # level indicator // $tree_ref->[5] = 0 ; # needed children number : // # -1 means ALL (Alive + Absent + Suspected resources) // # -2 means BEST (Alive resources at the time) // $tree_ref->[6] = 0 ; # maximum available children // $tree_ref->[7] = undef ; # previous brother ref // $tree_ref->[8] = undef ; # next brother ref // $tree_ref->[9] = undef ; # first child ref // $tree_ref->[10] = 0 ; # current children number // return($tree_ref); // } /** c'est non trivial en c++ ? J'ai fait trivial : hypothese: c'est bien un arbre et pas un dag ! # clone the tree # arg : tree ref # return a copy of the tree ref */ static TreeNode *dclone(TreeNode *tree_ref) { int i; TreeNode *last_inserted_child = NULL; assert(tree_ref != 0); TreeNode *newnode = new TreeNode(tree_ref->needed_children_number); newnode->name = tree_ref->name; newnode->value = tree_ref->value; newnode->level = tree_ref->level; // pour chaque fils, creer un sous-arbre // et l'insérer comme fils dans le nouvel arbre TreeNode *current_child = tree_ref->first_child; assert( tree_ref->current_child_number == (int) tree_ref->children.size() ); for(i=0; i < tree_ref->current_child_number; i++, current_child = current_child->next_brother ) { assert( current_child != NULL ); TreeNode *newchild; newchild = dclone( current_child ); newchild->father = newnode; // on prend le name de chaque fils, et on l'insere la nouvelle ref newnode->children.insert( std::pair(current_child->value, newchild) ); if ( last_inserted_child == NULL ) { newnode->first_child = newchild; } else { newchild->prev_brother = last_inserted_child; last_inserted_child->next_brother = newchild; } last_inserted_child = newchild; newnode->current_child_number ++; } assert( newnode->current_child_number == tree_ref->current_child_number ); // std::cerr << "lvl :" << newnode->level << " newnode ch size :" // << newnode->children.size() << " tree_ref ch size :" // << tree_ref->children.size() << std::endl; assert( newnode->children.size() == tree_ref->children.size() ); return newnode; } TreeNode *clone(TreeNode *tree_ref) { if (tree_ref == NULL) return NULL; return ( dclone(tree_ref) ); } // # clone the tree // # arg : tree ref // # return a copy of the tree ref // sub clone($){ // my $tree_ref = shift; // return(dclone($tree_ref)); // } /** return 1 if node is a leaf (no child) otherwise retuurn 0 */ bool is_node_a_leaf(TreeNode *tree_ref){ if ( tree_ref->children.size() > 0 ) return false; else return true; } // # return 1 if node is a leaf (no child) // # otherwise retuurn 0 // sub is_node_a_leaf($){ // my $tree_ref = shift; // if (defined($tree_ref->[1])){ // return(0); // }else{ // return(1); // } // } /** add a child to the given tree ref (if child resource name is undef it seems that this child is a leaf of the tree) arg : tree ref, resource name, resource value return the ref of the child */ TreeNode *add_child(TreeNode *tree_ref, std::string resource_name, std::string resource_value) { TreeNode *tmp_ref; if ( tree_ref->children.find(resource_value) == tree_ref->children.end() ) { // Create a new tree node tmp_ref = new TreeNode(tree_ref, 0, resource_name, resource_value, tree_ref->level+1, 0, 0, NULL, NULL, NULL, 0 ); tree_ref->children.insert( std::pair(resource_value, tmp_ref) ); tree_ref->max_available_children++; tree_ref->current_child_number++; // Add new brother if (tree_ref->first_child != NULL) { tmp_ref->next_brother = tree_ref->first_child; tree_ref->first_child->prev_brother = tmp_ref; } tree_ref->first_child = tmp_ref; } else { tmp_ref = tree_ref->children.find(resource_value)->second; } return(tmp_ref); } // # add a child to the given tree ref (if child resource name is undef it seems // # that this child is a leaf of the tree) // # arg : tree ref, resource name, resource value // # return the ref of the child // sub add_child($$$){ // my $tree_ref = shift; // my $resource_name = shift; // my $resource_value = shift; // my $tmp_ref; // if (!defined($tree_ref->[1]->{$resource_value})){ // # Create a new tree node // $tmp_ref = [ $tree_ref, undef, $resource_name, $resource_value, $tree_ref->[4] + 1, 0, 0, undef, undef, undef, 0]; // $tree_ref->[1]->{$resource_value} = $tmp_ref; // $tree_ref->[6] = $tree_ref->[6] + 1; // $tree_ref->[10] = $tree_ref->[10] + 1; // # Add new brother // if (defined($tree_ref->[9])){ // $tmp_ref->[8] = $tree_ref->[9]; // $tree_ref->[9]->[7] = $tmp_ref; // } // $tree_ref->[9] = $tmp_ref; // }else{ // $tmp_ref = $tree_ref->[1]->{$resource_value}; // } // return($tmp_ref); // } /** Store information about the number of needed children */ int set_needed_children_number(TreeNode *tree_ref, int needed_children_number) { tree_ref->needed_children_number = needed_children_number; return needed_children_number; } // # Store information about the number of needed children // sub set_needed_children_number($$){ // my $tree_ref = shift; // my $needed_children_number = shift; // $tree_ref->[5] = $needed_children_number; // } /** Get previous brother on the same level for the same father */ TreeNode *get_previous_brother(TreeNode *tree_ref) { if ( tree_ref == NULL ) return NULL; else return tree_ref->prev_brother; } // # Get previous brother on the same level for the same father // sub get_previous_brother($){ // my $tree_ref = shift; // if (!defined($tree_ref)){ // return(undef); // }else{ // return($tree_ref->[7]); // } // } //# Get next brother on the same level for the same father TreeNode *get_next_brother(TreeNode *tree_ref) { if (tree_ref == NULL) return NULL; else return tree_ref->next_brother; } // # Get next brother on the same level for the same father // sub get_next_brother($){ // my $tree_ref = shift; // if (!defined($tree_ref)){ // return(undef); // }else{ // return($tree_ref->[8]); // } // } /** Get initial child ref */ TreeNode *get_initial_child(TreeNode *tree_ref) { if (tree_ref == NULL) return NULL; else return tree_ref->first_child; } // # Get initial child ref // sub get_initial_child($){ // my $tree_ref = shift; // if (!defined($tree_ref)){ // return(undef); // }else{ // return($tree_ref->[9]); // } // } /** Get a specific child arg : tree ref, name of a child return a ref of a tree */ TreeNode *get_a_child(TreeNode *tree_ref, std::string child_name) { std::map::iterator ch; if ( tree_ref == NULL || tree_ref->children.size() == 0 || (ch = tree_ref->children.find(child_name) ) == tree_ref->children.end() ) return NULL; else return ch->second; } // # Get a specific child // # arg : tree ref, name of a child // # return a ref of a tree // sub get_a_child($$){ // my $tree_ref = shift; // my $child_name = shift; // if (!defined($tree_ref) || !defined($tree_ref->[1]) || !defined($tree_ref->[1]->{$child_name})){ // return(undef); // }else{ // return($tree_ref->[1]->{$child_name}); // } // } /** Get the ref of the father tree arg : tree ref return a tree ref */ TreeNode *get_father(TreeNode *tree_ref) { if ( tree_ref == NULL || tree_ref->father == NULL ) return NULL; else return tree_ref->father; } // # Get the ref of the father tree // # arg : tree ref // # return a tree ref // sub get_father($){ // my $tree_ref = shift; // if (!defined($tree_ref) || !defined($tree_ref->[0])){ // return(undef); // }else{ // return($tree_ref->[0]); // } // } /** Get the current resource name arg : tree ref return the resource name */ std::string get_current_resource_name(TreeNode *tree_ref) { if (tree_ref == NULL || tree_ref->name == "" ) return ""; else return tree_ref->name; } // # Get the current resource name // # arg : tree ref // # return the resource name // sub get_current_resource_name($){ // my $tree_ref = shift; // if (!defined($tree_ref) || !defined($tree_ref->[2])){ // return(undef); // }else{ // return($tree_ref->[2]); // } // } /** Get the current resource value arg : tree ref return the resource value */ std::string get_current_resource_value(TreeNode *tree_ref) { if ( tree_ref == NULL || tree_ref->value == "" ) return ""; else return tree_ref->value; } // # Get the current resource value // # arg : tree ref // # return the resource value // sub get_current_resource_value($){ // my $tree_ref = shift; // if (!defined($tree_ref) || !defined($tree_ref->[3])){ // return(undef); // }else{ // return($tree_ref->[3]); // } // } /** Get the current children number arg : tree ref return the resource value */ int get_current_children_number(TreeNode *tree_ref) { if (tree_ref == NULL) // j'ai enleve le test non sens en C++ : // tree_ref->current_child_number est toujours definis return -1; else return tree_ref->current_child_number; } // # Get the current children number // # arg : tree ref // # return the resource value // sub get_current_children_number($){ // my $tree_ref = shift; // if (!defined($tree_ref) || !defined($tree_ref->[10])){ // return(undef); // }else{ // return($tree_ref->[10]); // } // } /** Get the current level indicator arg : tree ref return the level indicator */ int get_current_level(TreeNode *tree_ref) { if (tree_ref == NULL || tree_ref->level == 0) return 0; else return tree_ref->level; } // # Get the current level indicator // # arg : tree ref // # return the level indicator // sub get_current_level($){ // my $tree_ref = shift; // if (!defined($tree_ref) || !defined($tree_ref->[4])){ // return(0); // }else{ // return($tree_ref->[4]); // } // } /** Get the maximum available number of children (just after the creation of the tree) arg : tree ref return an integer >= 0 */ int get_max_available_children(TreeNode *tree_ref) { if ( tree_ref == NULL || tree_ref->max_available_children == 0 ) return 0; else return tree_ref->max_available_children; } // # Get the maximum available number of children // # (just after the creation of the tree) // # arg : tree ref // # return an integer >= 0 // sub get_max_available_children($){ // my $tree_ref = shift; // if (!defined($tree_ref) || !defined($tree_ref->[6])){ // return(0); // }else{ // return($tree_ref->[6]); // } // } /** Get the number of needed children arg : tree ref return the number of needed children */ int get_needed_children_number(TreeNode *tree_ref) { if ( tree_ref == NULL || tree_ref->needed_children_number == 0 ) return 0; else return tree_ref->needed_children_number; } // # Get the number of needed children // # arg : tree ref // # return the number of needed children // sub get_needed_children_number($){ // my $tree_ref = shift; // if (!defined($tree_ref) || !defined($tree_ref->[5])){ // return(0); // }else{ // return($tree_ref->[5]); // } // } /** Delete a subtree arg : tree ref to delete return father tree ref */ /** recursive delete of nodes, including Hypothesys: the graph is really a tree (a single path to a node) */ static void ddelete_subtree(TreeNode *tree_ref) { int i; TreeNode *current_child = get_initial_child( tree_ref ); TreeNode *next_child = 0; if (current_child != 0) next_child = get_next_brother( current_child ); for(i = 0; i < get_current_children_number( tree_ref ); i++, current_child = next_child) { assert(current_child != 0); get_next_brother( current_child ); ddelete_subtree( current_child ); } delete tree_ref; } TreeNode *delete_subtree(TreeNode *tree_ref) { if (tree_ref == NULL) return NULL; TreeNode *father_ref = tree_ref->father; TreeNode *prev_brother = tree_ref->prev_brother; TreeNode *next_brother = tree_ref->next_brother; if ( prev_brother == NULL ) { if (father_ref != NULL) { assert(father_ref->first_child == tree_ref); // BUG version PERL? father_ref->first_child = next_brother; } } else { prev_brother->next_brother = next_brother; } if ( next_brother != NULL ) { next_brother->prev_brother = prev_brother; } if (father_ref != NULL) // BUG PERL ? ne verifie pas si la reference est nulle if ( father_ref->children.size() > 0 ) { // s'effacer soit meme de la table assert( father_ref->children.find(tree_ref->value)->second == tree_ref); assert( father_ref->children.erase(tree_ref->value) ); // effacer les enfants et s'enlever de la mémoire soit meme ddelete_subtree(tree_ref); father_ref->current_child_number --; return father_ref; } return static_cast(0); } // # Delete a subtree // # arg : tree ref to delete // # return father tree ref // sub delete_subtree($){ // my $tree_ref = shift; // return(undef) if (!defined($tree_ref)); // my $father_ref = $tree_ref->[0]; // my $prev_brother = $tree_ref->[7]; // my $next_brother = $tree_ref->[8]; // if (!defined($prev_brother)){ // if (defined($father_ref)){ // $father_ref->[9] = $next_brother; // } // }else{ // $prev_brother->[8] = $next_brother; // } // if (defined($next_brother)){ // $next_brother->[7] = $prev_brother; // } // if (defined($father_ref->[1])){ // delete($father_ref->[1]->{$tree_ref->[3]}); // $father_ref->[10] = $father_ref->[10] - 1; // return($father_ref); // }else{ // return(undef); // } // } //############################################################################### /** # delete_tree_nodes_with_not_enough_resources # Delete subtrees that do not fit wanted resources # args: tree ref # side effect : modify tree data structure */ TreeNode *delete_tree_nodes_with_not_enough_resources(TreeNode *tree_ref) { //#print("START delete_tree_nodes_with_not_enough_resources\n"); //# Search if there are enough values for each resource //# Tremaux algorithm (Deep first) TreeNode *current_node = tree_ref; do { if ( (get_needed_children_number(current_node) > get_current_children_number(current_node) ) || ( (get_needed_children_number(current_node) == -1) // ALL && (get_max_available_children(current_node) > get_current_children_number(current_node))) || ( (get_needed_children_number(current_node) == -2) // BEST && (get_current_children_number(current_node) <= 0) ) ) { // we want to delete the root if (tree_ref == current_node) return NULL; // Delete sub tree that does not fit with wanted resources // print("DELETE ".get_current_resource_value($current_node)."\n"); current_node = delete_subtree(current_node); } if ( get_initial_child(current_node) != NULL) { // Go to child current_node = get_initial_child(current_node); //print("Go to CHILD =".get_current_resource_value($current_node)."\n"); } else { // Treate leaf while( (current_node != NULL) && ( (get_father(current_node) == 0) || (get_next_brother(current_node) == 0))) { // Step up //print("TOTO ".get_current_children_number($current_node)."\n"); //print("Go to FATHER : ".get_current_resource_value($current_node)."\n") if (defined(get_current_resource_value($current_node))); if ( (get_needed_children_number(current_node) > get_current_children_number(current_node) ) || ( (get_needed_children_number(current_node) == -1) // ALL && ( get_max_available_children(current_node) > get_current_children_number(current_node) ) ) || ( (get_needed_children_number(current_node) == -2) // BEST && (get_current_children_number(current_node) <= 0))) { // we want to delete the root if (tree_ref == current_node) return NULL; //print("DELETE 1".get_current_resource_value($current_node)."\n"); // Delete sub tree that does not fit with wanted resources current_node = delete_subtree(current_node); } else { current_node = get_father(current_node); } } if ((get_father(current_node) != NULL) && (get_next_brother(current_node) != NULL)) { // Treate brother TreeNode *brother_node = get_next_brother(current_node); if ((get_needed_children_number(current_node) > get_current_children_number(current_node)) || ((get_needed_children_number(current_node) == -1) // ALL && (get_max_available_children(current_node) > get_current_children_number(current_node))) || ((get_needed_children_number(current_node) == -2) // BEST && (get_current_children_number(current_node) <= 0))) { //print("DELETE 2".get_current_resource_value($current_node)."\n"); // Delete sub tree that does not fit with wanted resources delete_subtree(current_node); } current_node = brother_node; //print("Go to BROTHER : ".get_current_resource_value($current_node)."\n"); } } }while( (current_node != NULL) ); if ( (get_initial_child(tree_ref) == NULL) ) { return NULL; } else { return tree_ref; } } // # delete_tree_nodes_with_not_enough_resources // # Delete subtrees that do not fit wanted resources // # args: tree ref // # side effect : modify tree data structure // sub delete_tree_nodes_with_not_enough_resources($){ // my $tree_ref = shift; // #print("START delete_tree_nodes_with_not_enough_resources\n"); // # Search if there are enough values for each resource // # Tremaux algorithm (Deep first) // my $current_node = $tree_ref; // do{ // if ((get_needed_children_number($current_node) > get_current_children_number($current_node)) // or ((get_needed_children_number($current_node) == -1) # ALL // and (get_max_available_children($current_node) > get_current_children_number($current_node))) // or ((get_needed_children_number($current_node) == -2) # BEST // and (get_current_children_number($current_node) <= 0)) // ){ // # we want to delete the root // return(undef) if ($tree_ref == $current_node); // # Delete sub tree that does not fit with wanted resources // #print("DELETE ".get_current_resource_value($current_node)."\n"); // $current_node = delete_subtree($current_node); // } // if (defined(get_initial_child($current_node))){ // # Go to child // $current_node = get_initial_child($current_node); // #print("Go to CHILD =".get_current_resource_value($current_node)."\n"); // }else{ // # Treate leaf // while(defined($current_node) and (!defined(get_father($current_node)) or !defined(get_next_brother($current_node)))){ // # Step up // #print("TOTO ".get_current_children_number($current_node)."\n"); // #print("Go to FATHER : ".get_current_resource_value($current_node)."\n") if (defined(get_current_resource_value($current_node))); // if ((get_needed_children_number($current_node) > get_current_children_number($current_node)) // or ((get_needed_children_number($current_node) == -1) # ALL // and (get_max_available_children($current_node) > get_current_children_number($current_node))) // or ((get_needed_children_number($current_node) == -2) # BEST // and (get_current_children_number($current_node) <= 0)) // ){ // # we want to delete the root // return(undef) if ($tree_ref == $current_node); // #print("DELETE 1".get_current_resource_value($current_node)."\n"); // # Delete sub tree that does not fit with wanted resources // $current_node = delete_subtree($current_node); // }else{ // $current_node = get_father($current_node); // } // } // if (defined(get_father($current_node)) and defined(get_next_brother($current_node))){ // # Treate brother // my $brother_node = get_next_brother($current_node); // if ((get_needed_children_number($current_node) > get_current_children_number($current_node)) // or ((get_needed_children_number($current_node) == -1) # ALL // and (get_max_available_children($current_node) > get_current_children_number($current_node))) // or ((get_needed_children_number($current_node) == -2) # BEST // and (get_current_children_number($current_node) <= 0)) // ){ // #print("DELETE 2".get_current_resource_value($current_node)."\n"); // # Delete sub tree that does not fit with wanted resources // delete_subtree($current_node); // } // $current_node = $brother_node; // #print("Go to BROTHER : ".get_current_resource_value($current_node)."\n"); // } // } // }while(defined($current_node)); // if (!defined(get_initial_child($tree_ref))){ // return(undef); // }else{ // return($tree_ref); // } // } /** # get_tree_leaf # return a list of tree leaf # arg: tree ref */ std::vectorget_tree_leafs(TreeNode *tree) { std::vector result; if (tree == NULL) return result; // Search leafs // Tremaux algorithm (Deep first) TreeNode *current_node = tree; do { if ( (get_initial_child(current_node) != NULL) ) { // Go to child current_node = get_initial_child(current_node); //print("Go to CHILD =".get_current_resource_value($current_node)."\n"); } else { // Treate leaf if ( is_node_a_leaf(current_node) == true) { //push(@result, $node_name_pile[0]); result.push_back(current_node); //print("Leaf: ".get_current_resource_value($current_node)."\n"); } // Look at brothers while( (current_node != NULL) && ( (get_father(current_node) == NULL) || (get_next_brother(current_node) == NULL))) { // Step up current_node = get_father(current_node); //print("Go to FATHER : ".get_current_resource_value($current_node)."\n") if (defined(get_current_resource_value($current_node))); } if ( (get_father(current_node) != NULL) && (get_next_brother(current_node) != NULL)) { // Treate brother current_node = get_next_brother(current_node); //print("Go to BROTHER : ".get_current_resource_value($current_node)."\n"); } } } while( (current_node != NULL) ); return result; } // # get_tree_leaf // # return a list of tree leaf // # arg: tree ref // sub get_tree_leafs($){ // my $tree = shift; // my @result; // return(@result) if (!defined($tree)); // # Search leafs // # Tremaux algorithm (Deep first) // my $current_node = $tree; // do{ // if (defined(get_initial_child($current_node))){ // # Go to child // $current_node = get_initial_child($current_node); // #print("Go to CHILD =".get_current_resource_value($current_node)."\n"); // }else{ // # Treate leaf // if (is_node_a_leaf($current_node) == 1){ // #push(@result, $node_name_pile[0]); // push(@result, $current_node); // #print("Leaf: ".get_current_resource_value($current_node)."\n"); // } // # Look at brothers // while(defined($current_node) and (!defined(get_father($current_node)) or !defined(get_next_brother($current_node)))){ // # Step up // $current_node = get_father($current_node); // #print("Go to FATHER : ".get_current_resource_value($current_node)."\n") if (defined(get_current_resource_value($current_node))); // } // if (defined(get_father($current_node)) and defined(get_next_brother($current_node))){ // # Treate brother // $current_node = get_next_brother($current_node); // #print("Go to BROTHER : ".get_current_resource_value($current_node)."\n"); // } // } // }while(defined($current_node)); // return(@result); // } /** # delete_unnecessary_subtrees # Delete subtrees that are not necessary (watch needed_children_number) # args: tree ref # side effect : modify tree data structure */ TreeNode *delete_unnecessary_subtrees(TreeNode *tree_ref) { if (tree_ref == NULL) return tree_ref; // Search if there are enough values for each resource // Tremaux algorithm (Deep first) TreeNode *current_node = tree_ref; do { if ( (get_needed_children_number(current_node) >= 0) && (get_needed_children_number(current_node) < (get_current_children_number(current_node)))) { // Delete extra sub tree delete_subtree(get_initial_child(current_node)); } else { if ( (get_initial_child(current_node)) != NULL ) { // Go to child current_node = get_initial_child(current_node); //#print("Go to CHILD =".get_current_resource_value($current_node)."\n"); } else { // Look at brothers while( (current_node != NULL) && ( (get_father(current_node) == NULL) || (get_next_brother(current_node) == NULL))) { // Step up current_node = get_father(current_node); //print("Go to FATHER : ".get_current_resource_value($current_node)."\n") if (defined(get_current_resource_value($current_node))); } if ( (get_father(current_node) != NULL) && (get_next_brother(current_node) != NULL) ) { // Treate brother current_node = get_next_brother(current_node); //print("Go to BROTHER : ".get_current_resource_value($current_node)."\n"); } } } } while( (current_node != NULL) ); return tree_ref; } // # delete_unnecessary_subtrees // # Delete subtrees that are not necessary (watch needed_children_number) // # args: tree ref // # side effect : modify tree data structure // sub delete_unnecessary_subtrees($){ // my $tree_ref = shift; // return($tree_ref) if (!defined($tree_ref)); // # Search if there are enough values for each resource // # Tremaux algorithm (Deep first) // my $current_node = $tree_ref; // do{ // if ((get_needed_children_number($current_node) >= 0) and (get_needed_children_number($current_node) < (get_current_children_number($current_node)))){ // # Delete extra sub tree // delete_subtree(get_initial_child($current_node)); // }else{ // if (defined(get_initial_child($current_node))){ // # Go to child // $current_node = get_initial_child($current_node); // #print("Go to CHILD =".get_current_resource_value($current_node)."\n"); // }else{ // # Look at brothers // while(defined($current_node) and (!defined(get_father($current_node)) or !defined(get_next_brother($current_node)))){ // # Step up // $current_node = get_father($current_node); // #print("Go to FATHER : ".get_current_resource_value($current_node)."\n") if (defined(get_current_resource_value($current_node))); // } // if (defined(get_father($current_node)) and defined(get_next_brother($current_node))){ // # Treate brother // $current_node = get_next_brother($current_node); // #print("Go to BROTHER : ".get_current_resource_value($current_node)."\n"); // } // } // } // }while(defined($current_node)); // return($tree_ref); // } //return 1; } ./oar-2.5.2/sources/extra/cpp-scheduler/Oar_resource_tree.H0000644000175000017500000000604511757171206021712 0ustar plbplb#ifndef OAR_RESOURCE_TREE_H #define OAR_RESOURCE_TREE_H #include #include #include #include #include namespace OAR::Schedulers::ResourceTree { class TreeNode; class TreeNode { public: TreeNode *father; // father ref $tree_ref->[0] std::map children; // ref of a hashtable with children $tree_ref->[1 std::string name; // name of the resource $tree_ref->[2] std::string value; // value of this resource $tree_ref->[3] int level; // level indicator $tree_ref->[4] int needed_children_number; /* needed children number : $tree_ref->[5] -1 means ALL (Alive + Absent + Suspected resources) -2 means BEST (Alive resources at the time)*/ int max_available_children; // maximum available children: $tree_ref->[6]; TreeNode *prev_brother; // previous brother ref $tree_ref->[7] TreeNode *next_brother; // next brother ref $tree_ref->[8] TreeNode *first_child; // first child ref $tree_ref->[9] int current_child_number; // current children number $tree_ref->[10] = 0 ; /** # Create a tree # arg : number of needed children # return the ref of the created tree */ TreeNode(int need_child_num) : father(NULL), level(0), needed_children_number(need_child_num),max_available_children(0), prev_brother(NULL), next_brother(NULL), first_child(NULL),current_child_number(0) {}; TreeNode(TreeNode *fat, TreeNode *child, std::string res_name, std::string res_value, int lev, int need_ch_numb, int max_avai_ch, TreeNode *prev_bro, TreeNode *next_bro, TreeNode *first_ch, int cur_ch_numb ) : father(fat), name(res_name), value(res_value),level(lev), needed_children_number(need_ch_numb), max_available_children(max_avai_ch), prev_brother(prev_bro), next_brother(next_bro), first_child(first_ch), current_child_number(cur_ch_numb) { assert( child == 0 ); // not implemented }; }; TreeNode *clone(TreeNode *tree_ref); bool is_node_a_leaf(TreeNode *tree_ref); TreeNode *add_child(TreeNode *tree_ref, std::string resource_name, std::string resource_value); int set_needed_children_number(TreeNode *tree_ref, int needed_children_number); TreeNode *get_previous_brother(TreeNode *tree_ref); TreeNode *get_next_brother(TreeNode *tree_ref); TreeNode *get_initial_child(TreeNode *tree_ref); TreeNode *get_a_child(TreeNode *tree_ref, std::string child_name); TreeNode *get_father(TreeNode *tree_ref); std::string get_current_resource_name(TreeNode *tree_ref); std::string get_current_resource_value(TreeNode *tree_ref); int get_current_children_number(TreeNode *tree_ref); int get_current_level(TreeNode *tree_ref); int get_max_available_children(TreeNode *tree_ref); int get_needed_children_number(TreeNode *tree_ref); TreeNode *delete_subtree(TreeNode *tree_ref); TreeNode *delete_tree_nodes_with_not_enough_resources(TreeNode *tree_ref); std::vector get_tree_leafs(TreeNode *tree); TreeNode *delete_unnecessary_subtrees(TreeNode *tree_ref); } #endif ./oar-2.5.2/sources/extra/cpp-scheduler/Oar_iolib.cc0000644000175000017500000010002611757171206020332 0ustar plbplb/****************************************************************************** # This is the iolib, which manages the layer between the modules and the # database. This is the only base-dependent layer. # When adding a new function, the following comments are required before the code of the function: # - the name of the function # - a short description of the function # - the list of the parameters it expect # - the list of the return values # - the list of the side effects ******************************************************************************/ #include #include #include #include extern "C" { #include } #include "Oar_resource_tree.H" using namespace std; /* fonction de la iolib utilisee dans sched_gantt_... */ /* connect_db() - DONE connect() - DONE connect_ro() - DONE disconnect() - DONE get_specific_resource_states($base,$Resources_to_always_add_type); - DONE list_resources($base) - DONE OAR::IO::get_gantt_scheduled_jobs(); - DONE get_current_job_types($base,$i); - DONE get_job_current_resources($base, $already_scheduled_jobs{$i}->[7],\@Sched_available_suspended_resource_type); - DONE get_job_suspended_sum_duration($base,$i,$current_time); - DONE OAR::IO::get_resources_in_state($base,"Alive"); - DONE get_resources_in_state($base,"Dead")); - DONE get_fairsharing_jobs_to_schedule($base,$queue,$Karma_max_number_of_jobs_treated_per_user); - DONE get_sum_accounting_window($base,$queue,$current_time - $Karma_window_size,$current_time); - DONE get_sum_accounting_for_param($base,$queue,"accounting_project",$current_time - $Karma_window_size,$current_time); - DONE get_sum_accounting_for_param($base,$queue,"accounting_user",$current_time - $Karma_window_size,$current_time); - DONE get_current_job_dependencies($base,$j->{job_id})) - DONE get_job($base,$d); - DONE get_gantt_job_start_time($base,$d); - DONE get_current_moldable_job($base,$date_tmp[1]); - DONE set_job_message($base,$j->{job_id},$message); - DONE set_job_message($base,$j->{job_id},$message); - DONE IDEM PREC get_current_job_types($base,$j->{job_id}); - DONE get_resources_data_structure_current_job($base,$j->{job_id}); - DONE get_resources_that_can_be_waked_up($base, OAR::IO::get_date($base) + $duration)) - DONE get_resources_that_will_be_out($base, OAR::IO::get_date($base) + $duration)) - DONE - MERGED WITH PRECEDENT get_possible_wanted_resources($base_ro,$alive_resources_vector,$resource_id_used_list_vector,\@Dead_resources,"$job_properties AND $tmp_properties", $m->{resources}, $Order_part); - DONE add_gantt_scheduled_jobs($base,$moldable_results[$index_to_choose]->{moldable_id}, $moldable_results[$index_to_choose]->{start_date},$moldable_results[$index_to_choose]->{resources}); - DONE set_job_message($base,$j->{job_id},"Karma = ".sprintf("%.3f",karma($j))); - DONE PREV disconnect($base_ro); - DONE PREV */ /** Questions ouvertes: - dans OAR perl, il y a dans des string double-cote avec " " il y a plein de " \'CURRENT\' " qui sont equivalents a " 'CURRENT' ". Etonnant non ? C'est idem en C++, donc je les laisse :-) */ namespace iolib { QSqlDatabase db; /* # connect_db # Connects to database and returns the base identifier # return value : base */ int connect_db(string dbhost, int dbport, string dbname, string dblogin, string dbpasswd, int debug_level=0) { db = QSqlDatabase::addDatabase("oardb"); db.setHostName(QString::fromStdString( dbhost )); db.setPort(dbport); db.setDatabaseName(QString::fromStdString( dbname )); db.setUsername(QString::fromStdString( dblogin )); db.setpassword(QString::fromStdString( dbpasswd )); bool ok = db.open(); /* TODO: ajouter le traitement d'erreur if (!defined($dbh)){ oar_error("[IOlib] Cannot connect to database (type=$Db_type, host=$host, user=$user, database=$name) : $DBI::errstr\n"); if ($Timeout_db_connection < $Max_db_connection_timeout){ $Timeout_db_connection += 2; } oar_warn("[IOlib] I will retry to connect to the database in $Timeout_db_connection s\n"); send_log_by_email("OAR database connection failed","[IOlib] I will retry to connect to the database in $Timeout_db_connection s\n"); sleep($Timeout_db_connection); } */ return ok; } /** # connect # Connects to database and returns the base identifier # parameters : / # return value : base # side effects : opens a connection to the base specified in ConfLib */ bool connect() { //# Connect to the database. init_conf(getenv("OARCONFFILE")); string dbhost = get_conf("DB_HOSTNAME"); string dbport = get_conf("DB_PORT"); string dbname = get_conf("DB_BASE_NAME"); string dblogin = get_conf("DB_BASE_LOGIN"); string dbpwd = get_conf("DB_BASE_PASSWD"); string Db_type = get_conf("DB_TYPE"); string log_level = get_conf("LOG_LEVEL"); string Remote_host = get_conf("SERVER_HOSTNAME"); string Remote_port = get_conf("SERVER_PORT"); return connect_db(dbhost, dbport, dbname, dblogin, dbpasswd, debug_level); } /* # connect_ro # Connects to database and returns the base identifier # parameters : / # return value : base # side effects : opens a connection to the base specified in ConfLib */ bool connect_ro() { return connect(); } /* # disconnect # Disconnect from database # parameters : base # return value : / # side effects : closes a previously opened connection to the specified base */ void disconnect() { assert( db.isValid() ); db.close(); } /* # get_specific_resource_states # returns a hashtable with each given resources and their states # parameters : base, resource type */ multimap< string, string > get_specific_resource_states(string type) { assert( db.isValid() ); QSqlQuery query; query.setForwardOnly(true); string req = " SELECT resource_id, state\ FROM resources\ WHERE\ type = \'" +type+"\'\ "; query.exec(req); multimap< pair< string, string> > result; while( query.next() ) { string resource_id = query.value(0).toString(); string state = query.value(1).toString(); result.insert( pair(state, resource_id) ); } return result; } /* # list_resources # gets the list of all resources # parameters : base # return value : list of resources # side effects : / */ static bool yesNo2Bool(string s) { if ( s == "YES" ) return true; if ( s == "NO" ) return false; assert( s == "YES" || s == "NO" ); } static struct resources_iolib fillResourcesStruct(QSqlQuery &req) { /* the value of the structure must be given in the same order a filled by the SELECT call. */ resources_iolib result; result.resource_id = req.value(0).toUInt(); result.type = req.value(1).toString(); result.network_address = req.value(2).toString(); result.state = req.value(3).toString(); result.next_state = req.value(4).toString(); result.finaud_decision = yesNo2Bool(req.value(5).toString()); result.next_finaud_decision = yesNo2Bool(req.value(6).toString()); result.state_num = req.value(7).toUInt(); result.suspended_jobs = yesNo2Bool(req.value(8).toString()); result.scheduler_priority = req.value(9).toUInt(); result.switch_name = req.value(10).toString(); result.cpu = req.value(11).toUInt(); result.cpuset = req.value(12).toUInt(); result.besteffort = yesNo2Bool(req.value(13).toString()); result.deploy = yesNo2Bool(req.value(14).toString()); result.expiry_date = req.value(15).toUInt(); result.desktop_computing = yesNo2Bool(req.value(16).toString()); result.last_job_date = req.value(17).toUInt(); result.available_upto = req.value(18).toUInt(); return result; } /** TODO SIMPLIFICATION : l usage de cette fonction est uniquement d'obtenir la liste des ID pour remplir le vecteur de bit ! TODO RTFM: il est possible de faire semblable au caode perl avec QtSQLModel ! readonly avec */ vector resources_extractor(bool withState, string state="") { assert(db.isValid()); QSqlQuery query; query.setForwardOnly(true); vector result; string req = "SELECT resource_id, type, network_address, state, next_state, finaud_decision, next_finaud_decision, state_num, suspended_jobs, scheduler_priority, switch, cpu, cpuset, besteffort, deploy, expiry_date, desktop_computing, last_job_date, available_upto\ FROM resources\ "; if (withState) { req += "WHERE\ state = \'" << state << "\'"); } query.exec(req); while( query.next() ) { result.push_back( fillResourcesStruct(req) ); } return result; } vector list_resources() { return resources_extractor(false); } /* # GANTT MANAGEMENT #get previous scheduler decisions #args : base #return a hashtable : job_id --> [start_time,walltime,queue_name,\@resources,state] # TODO commentaire PERL faux: bien plus d'information et pas de resssource ! */ pair< vector, map > get_gantt_scheduled_jobs(){ QSqlQuery query; query.setForwardOnly(true); map result; vector order; assert(db.isValid()); string req("SELECT j.job_id, g2.start_time, m.moldable_walltime, g1.resource_id, j.queue_name, j.state, j.job_user, j.job_name,m.moldable_id,j.suspended\ FROM gantt_jobs_resources g1, gantt_jobs_predictions g2, moldable_job_descriptions m, jobs j\ WHERE\ m.moldable_index = \'CURRENT\'\ AND g1.moldable_job_id = g2.moldable_job_id\ AND m.moldable_id = g2.moldable_job_id\ AND j.job_id = m.moldable_job_id\ ORDER BY j.start_time, j.job_id\ "); query.exec(req); while( query.next() ) { struct gantt_sched_jobs val; unsigned int jid; if (result.find(query.value(0).toUint()) != result.end()) { jid = query.value(0).toUint(); val.job_id = jid; val.start_time = query.value(1).toUint(); moldable_walltime = query.value(2).toUint(); queue_name = query.value(4).toString(); state = query.value(5).toString(); job_user = query.value(6).toString(); job_name = query.value(7).toString(); moldable_id = query.value(7).toUInt(); suspended = yesNo2Bool(req.value(8).toString()); order.push_back(jid); result.insert( pair(jid, val) ); } result[jid].resource_id_vec.push_back( query.value(3).toUint() ); }; return pair< vector, map >(order, result); } /** # get_current_job_types # return a hash table with all types for the given job ID */ static regexp *rexp_get_current_job_types = 0; map get_current_job_types(unsigned int jobId){ QSqlQuery query; query.setForwardOnly(true); map res; if (rexp == 0) rexp_get_current_job_types = regcomp("^\s*(\w+)\s*=\s*(.+)$") string req = " SELECT type\ FROM job_types\ WHERE\ types_index = \'CURRENT\'\ AND job_id = $jobId\ " assert(db.isValid()); query.exec(req); while( query.next() ) { int valrec; valres = regexec(rexp_get_current_job_types, query.value(0).toString() ); if (valres) { res.insert( pair( string(startp[1], endp[1]), string(startp[2], endp[1]) ) ); } else { res.insert(pair( query.value(0).toString(), "true") ); } } return res; } /* # get_job_current_resources # returns the list of resources associated to the job passed in parameter # parameters : base, jobid # return value : list of resources # side effects : / */ static string quote_sql2(const string s) { string res=s; size_t value = std::string::npos; value = res.find_first_of("'", 0); while(value != std::string::npos) { res.replace(value, 1, "''"); value +=2; value = res.find_first_of("'", value); } return res; } vector get_job_current_resources(unsigned int jobid, vector not_type_list) { vector result; QSql query; string tmp_str; assert(db.isValid()); string req; if (not_type_list.size() == 0) { tmp_str= "FROM assigned_resources\ WHERE\ assigned_resources.assigned_resource_index = \'CURRENT\' AND\ assigned_resources.moldable_job_id = "; tmp_str << jobid; } else { string type_str; for(i=0; i< not_type_list.size(); i++) { if (i > 0) type_str << ","; type_str << quote_sql2( not_type_list[i] ); } tmp_str = "FROM assigned_resources,resources\ WHERE\ assigned_resources.assigned_resource_index = \'CURRENT\' AND\ assigned_resources.moldable_job_id ="; tmp_str << jobid << " AND\ resources.resource_id = assigned_resources.resource_id AND\ resources.type NOT IN (" << type_str << ")"; } req = "SELECT assigned_resources.resource_id as resource "; req << tmp_str << " ORDER BY assigned_resources.resource_id ASC"; query.exec(req); while( query.next() ) { unsigned int resource_id = query.value(0).toUint(); result.push_back( resource_id ); } return result; } /** # get the amount of time in the suspended state of a job # args : base, job id, time in seconds */ unsigned int get_job_suspended_sum_duration(unsigned int job_id, unsigned int current_time) { assert(db.isValid()); QSqlQuery query; query.setForwardOnly(true); unsigned int sum; string req = "SELECT date_start, date_stop\ FROM job_state_logs\ WHERE\ job_id = " << job_id<< " AND\ (job_state = \'Suspended\' OR\ job_state = \'Resuming\')"; query.exec(req); sum =0; while( query.next() ) { unsigned int tmp_sum = 0; unsigned int date_start = query.value(0).toUInt(); unsigned int date_stop = query.value(1).toUInt(); if ( date_stop == 0 ) tmp_sum = current_time - date_start; else tmp_sum = date_stop - date_start; sum += tmp_sum; } return sum; } /** # get_resources_in_state # returns the list of resources in the state specified # parameters : base, state # return value : list of resource ref c'est une quasi-copie de list_resources, j'ai mutualiser le code */ vector get_resources_in_state(string state) { return resources_extractor(true, state); } /** remplissage de la structure jobs restreinte */ static struct jobs_iolib_restrict fillJobsStructRestrict(QSqlQuery &query) { /* the value of the structure must be given in the same order a filled by the SELECT call. */ jobs_iolib_restrict result; result.job_id = query.value(0).toUInt(); result.job_name = query.value(1).toString(); result.job_user = query.value(2).toString(); result.properties = query.value(3).toString(); result.project = query.value(4).toString(); return result; } /** # get_fairsharing_jobs_to_schedule # args : base ref, queue name */ vector get_fairsharing_jobs_to_schedule(string queue, unsigned int limit) { assert(db.isValid()); QSqlQuery query; query.setForwardOnly(true); unsigned int sum; string req = "SELECT distinct(job_user)\ FROM jobs\ WHERE\ state = \'Waiting\'\ AND reservation = \'None\'\ AND queue_name = \'" << queue << "\'\ "; vector users; query.exec(req); while( query.next() ) { users.insert( string(query.value(0).toString() ) ); } vector res; for(vector::iterator u = users.begin(); u != users.end(); u++) { string req2 = "SELECT job_id,job_name,job_user,properties,project\ FROM jobs\ WHERE \ state = \'Waiting\'\ AND reservation = \'None\'\ AND queue_name = \'" << queue << "\' \ AND job_user = \'" << *u << "\' \ ORDER BY job_id \ LIMIT "<< limit <<" \ "; query.exec(req); while( query.next() ) { res.insert( fillJobsStructRestrict(query) ); } } return res; } map get_sum_accounting_window(string queue, unsigned int start_window, unsigned int stop_window) { assert(db.isValid()); QSqlQuery query; query.setForwardOnly(true); unsigned int sum; string req = "SELECT consumption_type, SUM(consumption)\ FROM accounting \ WHERE \ queue_name = \'" << queue << "\' AND \ window_start >= " << start_window << " AND \ window_start < " << stop_window << " \ GROUP BY consumption_type \ "; query.exec(req); sum =0; map results while( query.next() ) { string consumption_type = query.value(0).toString(); unsigned int sum_consumption = query.value(1).toUInt(); results.insert( pair( consumption_type, sum_consumption)); } return results; } /** TODO: no comment in perl ! */ map, unsigned int> get_sum_accounting_for_param(string queue, string param_name, unsigned int start_window, unsigned int stop_window) { assert(db.isValid()); QSqlQuery query; query.setForwardOnly(true); unsigned int sum; string req = " SELECT " << param_name<< ",consumption_type, SUM(consumption)\ FROM accounting\ WHERE\ queue_name = \'" << queue<< "\' AND\ window_start >= " << start_window<< " AND\ window_start < "<< stop_window<< "\ GROUP BY " << param_name<< ",consumption_type\ "; query.exec(req); sum =0; map< pair, unsigned int> results while( query.next() ) { string param = query.value(0).toString(); string consumption_type = query.value(1).toString(); string sum_consumption = query.value(2).toUInt(); results.insert( pair, unsigned int>( pair( param, consumption_type ), sum_consumption)); } return results; } /** # get_current_job_dependencies # return an array table with all dependencies for the given job ID */ vector get_current_job_dependencies(unsigned int jobId) { assert(db.isValid()); QSqlQuery query; query.setForwardOnly(true); string req = " SELECT job_id_required\ FROM job_dependencies\ WHERE\ job_dependency_index = \'CURRENT\'\ AND job_id = "<< jobId <<"\ "; query.exec(req); vector results while( query.next() ) { unsigned int job_id_required = query.value(0).toUInt(); results.push_back( job_id_required ); } return results; } /** # get_job # returns a ref to some hash containing data for the job of id passed in # parameter # parameters : base, jobid # return value : ref # side effects : / job extraction is restricted to - state - job_type - exit_code */ struct jobs_get_job_iolib_restrict get_job_restrict(unsigned int idJob) { assert(db.isValid()); QSqlQuery query; query.setForwardOnly(true); string req = " SELECT state, job_type, exit_code\ FROM jobs\ WHERE\ job_id = << " idJob << "\ "; query.exec(req); struct jobs_get_job_iolib_restrict results; results.state = "ONE VALUE" while( query.next() ) { string state = query.value(0).toString(); string job_type = query.value(1).toString(); int exit_code = query.value(2).toInt(); assert(results.state == "ONE VALUE"); results.state = state; results.job_type = job_type; results.exit_code = exit_code; } return results; } /** # Return a data structure with the resource description of the given job # arg : database ref, job id # return a data structure (an array of moldable jobs): # example for the first moldable job of the list: # $result = [ # [ # { # property => SQL property # resources => [ # { # resource => resource name # value => number of this wanted resource # } # ] # } # ], # walltime, # moldable_job_id # ] */ vector get_resources_data_structure_current_job(unsigned int job_id) { assert(db.isValid()); QSqlQuery query; query.setForwardOnly(true); string req = " SELECT moldable_job_descriptions.moldable_id, job_resource_groups.res_group_id, moldable_job_descriptions.moldable_walltime, job_resource_groups.res_group_property, job_resource_descriptions.res_job_resource_type, job_resource_descriptions.res_job_value\ FROM moldable_job_descriptions, job_resource_groups, job_resource_descriptions, jobs\ WHERE\ jobs.job_id = " << job_id << "\ AND jobs.job_id = moldable_job_descriptions.moldable_job_id\ AND job_resource_groups.res_group_moldable_id = moldable_job_descriptions.moldable_id\ AND job_resource_descriptions.res_job_group_id = job_resource_groups.res_group_id\ ORDER BY moldable_job_descriptions.moldable_id, job_resource_groups.res_group_id, job_resource_descriptions.res_job_order ASC\ "; query.exec(req); vector result; int group_index = -1; int moldable_index = -1; int previous_group = 0; int previous_moldable = 0; while( query.next() ) { // moldable_job_descriptions.moldable_id unsigned int moldable = query.value(0).toUInt(); // job_resource_groups.res_group_id unsigned int group = query.value(1).toUInt(); // moldable_job_descriptions.moldable_walltime unsigned int walltime = query.value(2).toUInt(); // job_resource_groups.res_group_property string property = query.value(3).toString(); // job_resource_descriptions.res_job_resource_type string resource = query.value(4).toString(); // job_resource_descriptions.res_job_value string value = query.value(5).toString(); if (previous_moldable != moldable) { moldable_index ++; previous_moldable = moldable; group_index = 0; previous_group = group; } else if (previous_group != group) { group_index++; previous_group = group; } // Store walltime if (result.size() < moldable+1) result.push_back(resources_data_moldable()); result[moldable_index].walltime = walltime; result[moldable_index].moldable_job_id = moldable; // Store properties group if (result[moldable_index].prop_res.size() < group_index+1) result[moldable_index].prop_res.push_back(property_resources_per_job()); result[moldable_index].prop_res[group_index].property = property; resources_per_job tmp_res; tmp_res.resource = resource; tmp_res.value = value; result[moldable_index].prop_res[group_index].resources.push_back(tmp_res); } return(result); } /** # get_resources_that_can_be_waked_up # returns a list of resources # parameters : base, date max # return value : list of resource ref # get_resources_that_will_be_out # returns a list of resources # parameters : base, job max date # return value : list of resource ref restricted version to resource_id (the only used data in the scheduler) */ vector get_resources_that_can_be_waked_up_or_will_be_out(unsigned int max_date, bool waked_up) { assert(db.isValid()); QSqlQuery query; query.setForwardOnly(true); string status; string compar_oper; switch(waked_up) { case true: status = "Absent"; compare_oper = ">"; break; case false: status = "Alive"; compare_oper = "<"; break; } string req = " SELECT resource_id\ FROM resources\ WHERE\ state = \'" << status << "\' AND\ resources.available_upto " << compare_oper <<" " << max_date << "\ "; query.exec(req); vector results; while( query.next() ) { unsigned int resource_id = query.value(0).toUInt(); results.push_back(resource_id); } return results; } /** # Get start_time for a given job # args : base, job id WARNING: no undef are returned in this version ! */ struct gantt_job_start_time get_gantt_job_start_time(unsigned int job) { assert(db.isValid()); QSqlQuery query; query.setForwardOnly(true); string req = " SELECT gantt_jobs_predictions.start_time, gantt_jobs_predictions.moldable_job_id\ FROM gantt_jobs_predictions,moldable_job_descriptions\ WHERE\ moldable_job_descriptions.moldable_job_id = " << job <<"\ AND gantt_jobs_predictions.moldable_job_id = moldable_job_descriptions.moldable_id\ "; query.exec(req); struct gantt_job_start_time results={}; if ( query.next() ) { results.start_time = query.value(0).toUInt(); results.moldable_job_id = query.value(1).toUInt(); } else assert(0); assert(! query.next()); /* only one answer ! */ return results; } /** # get_current_moldable_job_restrict_moldable_wall_time # returns a ref to some hash containing data for the moldable job of id passed in # parameter # parameters : base, moldable job id # return value : ref # side effects : / restricted to moldable_wall_time */ unsigned int get_current_moldable_job_restrict_moldable_walltime(unsigned int moldableJobId) { assert(db.isValid()); QSqlQuery query; query.setForwardOnly(true); string req = " SELECT moldable_walltime\ FROM moldable_job_descriptions\ WHERE\ moldable_index = \'CURRENT\'\ AND moldable_id = " << moldableJobId << "\ "; query.exec(req); unsigned int results=0; if ( query.next() ) { results = query.value(0).toUInt(); } else assert(0); assert(! query.next()); /* only one answer ! */ return results; } /** # set_job_message # sets the message field of the job of id passed in parameter # parameters : base, jobid, message # return value : / # side effects : changes the field message of the job in the table Jobs */ int set_job_message(unsigned int idJob, string message) { assert(db.isValid()); QSqlQuery query; string req = " UPDATE jobs\ SET message = " << message << "\ WHERE\ job_id = "<< idJob<<"\ "; query.exec(req); return 0; } /** # get_possible_wanted_resources # return a tree ref : a data structure with corresponding resources with what is asked */ TreeNode *get_possible_wanted_resources( vector possible_resources_vector, vector impossible_resources_vector, vector resources_to_ignore_array, string properties, vector wanted_resources_ref, /* TODO: verify type */ string order_part) { string sql_in_string= "\'1\'"; if (resources_to_ignore_array.size() > 0 ) { sql_in_string = "resource_id NOT IN ("; for(vector::iterator i = resources_to_ignore_array.begin(); i != resources_to_ignore_array.end(); i++) { static bool debut=1; switch (debut) { case 0: sql_in_string += ","; case 1: debut = 0; } sql_in_string += "" << *i; } sql_in_string = ")"; } if (order_part != "") order_part = "ORDER BY " << order_part; /* copy !*/ wanted_resources = wanted_resources_ref; int nb_res = wanted_resources.resources.size(); if (wanted_resources.resources[nb_res - 1]->resource != "resource_id") wanted_resources.resources.push_back( resources_per_job("resource_id", "-1")); string sql_where_string = "\'1\'"; if (properties != "") sql_where_string += " AND ( " << properties <<" )"; /* #Get only wanted resources */ string resource_string; for(vector::iterator i = wanted_resources.resources.begin(); i != wanted_resources.resources.end(); i++) { static bool debut=1; switch (debut) { case 0: resource_string += ","; case 1: debut = 0; break; } resource_string += "" << i->resource; } assert(db.isValid()); QSqlQuery query; query.setForwardOnly(true); string req = "SELECT " << resource_string << "\ FROM resources\ WHERE\ ("<< sql_where_string<< ") AND\ " << sql_in_string << "\ " << order_part <<"\ "; query.exec(req); /* how to send back Undef ? why ? */ /* # Initialize root */ TreeNode *result; result = new( TreeNode(0) ); int wanted_children_number = wanted_resources.resources[0].value; result->set_needed_children_number(wanted_children_number); while( query.next() ) { TreeNode *father_ref = result; for(i=0; i < wanted_resources.resources.size(); i++) { /** # Feed the tree for all resources */ father_ref = father_ref->add_child(wanted_resources.resources[i].resource, query.value(i).toString() ); int wanted_children_number; if ( i < wanted_resources.resources.size() - 1) wanted_children_number = wanted_resources.resources[i+1].value; else wanted_children_number = 0; father_ref->set_needed_children_number(wanted_children_number); /* # Verify if we must keep this child if this is resource_id resource name */ if ( wanted_resources.resources[i].resource == "resource_id" ) { if (impossible_resources_vector.size() > 0 && impossible_resources_vector[ query.value(i).toInt() ] ) { father_ref->delete_subtree(); i = wanted_resources.resources.size(); /* the end */ } else if ( possible_resources_vector.size() > 0 && ! possible_resources_vector[ query.value(i).toInt() ]) { father_ref->delete_subtree(); i = wanted_resources.resources.size(); /* the end */ } } } } result->delete_tree_nodes_with_not_enough_resources(); return result; } /** #add scheduler decisions #args : base,moldable_job_id,start_time,\@resources #return nothing */ void add_gantt_scheduled_jobs(unsigned int id_moldable_job, unsigned int start_time, vector resource_list) { assert(db.isValid()); QSqlQuery query; string req = " INSERT INTO gantt_jobs_predictions (moldable_job_id,start_time)\ VALUES ("<< id_moldable_job<< ",\'"<< start_time<<"\')\ "; query.exec(req); for(vector::iterator i = resource_list.begin(); i != resource_list.end(); i++) { QSqlQuery query2; string req2 = " INSERT INTO gantt_jobs_resources (moldable_job_id,resource_id)\ VALUES ("< #include "Oar_resource_tree.H" using namespace std; /* Type utilisé pour l'extraction de des données (pas de hashref par query) Version prise de la doc OAR le 8 octobre 2008 (OAR >= 2.3.0) */ namespace iolib { struct resources_iolib { unsigned int resource_id; // INT UNSIGNED resource identifier string type; // VARCHAR(100) DEFAULT "default" resource type (used // for licence resources for example) string network_address; // VARCHAR(100) node name (used to connect // via SSH) string state; // ENUM('Alive', 'Dead' , 'Suspected', 'Absent') // resource state string next_state;// ENUM('UnChanged', 'Alive', 'Dead', 'Absent', // 'Suspected') DEFAULT 'UnChanged' state for the // resource to switch bool finaud_decision; // ENUM('YES', 'NO') DEFAULT 'NO' tell if // the actual state results in a "finaud" // module decision bool next_finaud_decision; // ENUM('YES', 'NO') DEFAULT 'NO' tell // if the next node state results in a // "finaud" module decision int state_num; // INT corresponding state number (useful with the // SQL "ORDER" query) bool suspended_jobs; // ENUM('YES','NO') specify if there is // at least one suspended job on the // resource unsigned int scheduler_priority;// INT UNSIGNED arbitrary number // given by the system to select // resources with more // intelligence string switch_name; // VARCHAR(50) name of the switch unsigned int cpu; // INT UNSIGNED global cluster cpu number unsigned int cpuset; // INT UNSIGNED field used with the // JOB_RESOURCE_MANAGER_PROPERTY_DB_FIELD bool besteffort; // ENUM('YES','NO') accept or not besteffort jobs bool deploy; // ENUM('YES','NO') specify if the resource is deployable unsigned int expiry_date; // INT UNSIGNED field used for the desktop // computing feature bool desktop_computing; // ENUM('YES','NO') tell if it is a desktop // computing resource (with an agent) unsigned int last_job_date; // INT UNSIGNED store the date when the // resource was used for the last time unsigned int available_upto; //INT UNSIGNED used with compute mode //features to know if an Absent //resource can be switch on }; struct gantt_sched_jobs { unsigned int job_id; unsigned int start_time; unsigned int moldable_walltime; vector resource_id_vec; string queue_name; string state; string job_user; string job_name; unsigned int moldable_id; bool suspended; }; /** Extraction de la table jobs (select * dans perl, limite ici) usage de - get_fairsharing_jobs_to_schedule limite a (job_id, job_user, job_name, properties) + appel a karma limite a (project, job_user) */ struct jobs_iolib_restrict { unsigned int job_id; // INT UNSIGNED job identifier string job_name; // VARCHAR(100) name given by the user string job_user; // VARCHAR(255) user name string properties; // TEXT properties that assigned nodes must match string project; // VARCHAR(255) arbitrary name given by the user or an admission rule }; /** Extraction de la table job dans get_job limite a - state ENUM('Waiting','Hold', 'toLaunch', 'toError', 'toAckReservation', 'Launching', 'Running' 'Suspended', 'Resuming', , 'Finishing', 'Terminated', 'Error') job state - job_type ENUM('INTERACTIVE', 'PASSIVE') DEFAULT 'PASSIVE' specify if the user wants to launch a program or get an interactive shell - exit_code INT DEFAULT 0 exit code for passive jobs */ struct jobs_get_job_iolib_restrict { string state; string job_type; int exit_code; }; /** # Return a data structure with the resource description of the given job # arg : database ref, job id # return a data structure (an array of moldable jobs): # example for the first moldable job of the list: # $result = [ # [ # { # property => SQL property # resources => [ # { # resource => resource name # value => number of this wanted resource # } # ] # } # ], # walltime, # moldable_job_id # ] */ struct resources_per_job { string resource; string value; }; struct property_resources_per_job { string property; vector resources; }; struct resources_data_moldable { vector prop_res; unsigned int walltime; unsigned int moldable_job_id; }; /** # Get start_time for a given job # args : base, job id */ struct gantt_job_start_time { unsigned int start_time; unsigned int moldable_job_id; }; /** listes des fonctions */ /** # connect_db # Connects to database and returns the base identifier # return value : base */ extern int connect_db(string dbhost, int dbport, string dbname, string dblogin, string dbpasswd, int debug_level=0); /** # connect # Connects to database and returns the base identifier # parameters : / # return value : base # side effects : opens a connection to the base specified in ConfLib */ extern bool connect(); /** # connect_ro # Connects to database and returns the base identifier # parameters : / # return value : base # side effects : opens a connection to the base specified in ConfLib */ extern bool connect_ro(); /** # disconnect # Disconnect from database # parameters : base # return value : / # side effects : closes a previously opened connection to the specified base */ extern void disconnect(); /* # get_specific_resource_states # returns a hashtable with each given resources and their states # parameters : base, resource type */ extern map< string, string > get_specific_resource_states(string type); /* # list_resources # gets the list of all resources # parameters : base # return value : list of resources # side effects : / */ extern vector list_resources(); /* # GANTT MANAGEMENT #get previous scheduler decisions #args : base #return a hashtable : job_id --> [start_time,walltime,queue_name,\@resources,state] # TODO commentaire PERL faux: bien plus d'information et pas de resssource ! */ extern pair< vector, map > get_gantt_scheduled_jobs(); /** # get_current_job_types # return a hash table with all types for the given job ID */ extern map get_current_job_types(unsigned int jobId); /* # get_job_current_resources # returns the list of resources associated to the job passed in parameter # parameters : base, jobid # return value : list of resources # side effects : / */ extern vector get_job_current_resources(unsigned int jobid, vector not_type_list); /** # get the amount of time in the suspended state of a job # args : base, job id, time in seconds */ extern unsigned int get_job_suspended_sum_duration(unsigned int job_id, unsigned int current_time); /** # get_resources_in_state # returns the list of resources in the state specified # parameters : base, state # return value : list of resource ref c'est une quasi-copie de list_resources, j'ai mutualiser le code */ extern vector get_resources_in_state(string state); /** # get_fairsharing_jobs_to_schedule # args : base ref, queue name */ extern vector get_fairsharing_jobs_to_schedule(string queue, unsigned int limit); extern map get_sum_accounting_window(string queue, unsigned int start_window, unsigned int stop_window); extern map, unsigned int> get_sum_accounting_for_param(string queue, string param_name, unsigned int start_window, unsigned int stop_window); /** # get_current_job_dependencies # return an array table with all dependencies for the given job ID */ extern vector get_current_job_dependencies(unsigned int jobId); /** # get_job # returns a ref to some hash containing data for the job of id passed in # parameter # parameters : base, jobid # return value : ref # side effects : / job extraction is restricted to - state - job_type - exit_code */ extern struct jobs_get_job_iolib_restrict get_job_restrict(unsigned int idJob); /** # Return a data structure with the resource description of the given job # arg : database ref, job id # return a data structure (an array of moldable jobs): # example for the first moldable job of the list: # $result = [ # [ # { # property => SQL property # resources => [ # { # resource => resource name # value => number of this wanted resource # } # ] # } # ], # walltime, # moldable_job_id # ] */ extern vector get_resources_data_structure_current_job(unsigned int job_id); /** # get_resources_that_can_be_waked_up # returns a list of resources # parameters : base, date max # return value : list of resource ref # get_resources_that_will_be_out # returns a list of resources # parameters : base, job max date # return value : list of resource ref restricted version to resource_id (the only used data in the scheduler) */ extern vector get_resources_that_can_be_waked_up_or_will_be_out(unsigned int max_date, bool waked_up); /** # Get start_time for a given job # args : base, job id WARNING: no undef are returned in this version ! */ extern struct gantt_job_start_time get_gantt_job_start_time(unsigned int job); /** # get_current_moldable_job_restrict_moldable_wall_time # returns a ref to some hash containing data for the moldable job of id passed in # parameter # parameters : base, moldable job id # return value : ref # side effects : / restricted to moldable_wall_time */ extern unsigned int get_current_moldable_job_restrict_moldable_walltime(unsigned int moldableJobId); /** # set_job_message # sets the message field of the job of id passed in parameter # parameters : base, jobid, message # return value : / # side effects : changes the field message of the job in the table Jobs */ extern int set_job_message(unsigned int idJob, string message); /** # get_possible_wanted_resources # return a tree ref : a data structure with corresponding resources with what is asked */ extern OAR::Schedulers::ResourceTree::TreeNode * get_possible_wanted_resources( vector possible_resources_vector, vector impossible_resources_vector, vector resources_to_ignore_array, string properties, vector wanted_resources_ref, /* TODO: verify type */ string order_part); /* #add scheduler decisions #args : base,moldable_job_id,start_time,\@resources #return nothing */ extern void add_gantt_scheduled_jobs(unsigned int id_moldable_job, unsigned int start_time, vector resource_list); } #endif ./oar-2.5.2/sources/extra/cpp-scheduler/Oar_conflib.cc0000644000175000017500000000773711757171206020667 0ustar plbplb/** ############################################################################### ## *** ConfLib: *** ## ## Description: ## Home brewed module managing configuration file for OAR ## ## - Usage: init_conf(); ## Read the first file matching in ## . current directory ## . $OARDIR directory ## . /etc directory ## ## - Configuration file format: ## A line of the configuration file looks like that: ## > truc = 45 machin chose bidule 23 # any comment ## "truc" is a configuration entry being assigned "45 machin chose bidule 23" ## Anything placed after a dash (#) is ignored i ## (for instance lines begining with a dash are comment lines then ignored) ## Any line not matching the regexp defined below are also ignored ## ## Module must be initialized using init_conf(), then ## any entry is retrieved using get_conf(). ## is_conf() may be used to check if any entry actually exists. ## ## - Example: ## > use ConfLib qw(init_conf get_conf is_conf); ## > init_conf("oar.conf"); ## > print "toto = ".get_conf("toto")."\n" if is_conf("toto"); ## ############################################################################### # $Id$ */ #include #include #include #include #include #include #include #include "Oar_conflib.H" using namespace std; /// ## configuration file regexp (one line). /// inclusion of the quote matching in the main string static QRegExp regexp(QString("^\\s*([^#=\\s]+)\\s*=\\s*([\\\"\\']?)([^#]*)\\2")); /// unloaded conf static bool loaded_conf = 0; /// parameters container static map params; /// full conf file name static string filename; namespace conflib { /** ## Initialization of the configuration # param: configuration file pathname # Result: 0 if conf was already loaded # 1 if conf was actually loaded # 2 if conf was not found */ unsigned int init_conf (string file) { // If file already loaded, exit immediately if (loaded_conf) return 0; // try to open the various files ifstream ifile; filename = file; ifile.open(filename.c_str()); if ( ifile.fail() ) { string oarpath = getenv("OARDIR"); filename = oarpath+file; ifile.open( filename.c_str() ); if ( ifile.fail()) { filename = "/etc/"+file; ifile.open( filename.c_str() ); if ( ifile.fail()) { cerr << "Unable to open configuration file: " << filename << endl; return 2; } } } while(! ifile.eof() ) { string line; getline(ifile, line); int pos = regexp.indexIn(line.c_str()); if (pos > -1) { string key = regexp.cap(1).toStdString(); string val = regexp.cap(3).toStdString(); params[key] = val; } } ifile.close(); loaded_conf = 1; return 1; } /** ## retrieve a parameter if exists, set it to the default value otherwise ## params: arg1 param name, arg2 default value */ string get_conf_with_default_param ( string key, string defval) { map::iterator keyval; keyval = params.find(key); if (keyval == params.end() ) return defval; else return params[key]; } /** ## retrieve a parameter */ string get_conf ( string key ) { map::iterator keyval; keyval = params.find(key); if (keyval == params.end() ) return ""; else return params[key]; } /** ## check if a parameter is defined */ bool is_conf ( string key ) { map::iterator keyval; keyval = params.find(key); return (keyval != params.end() ); } /** ## debug: dump parameters */ int dump_conf () { cout << "Config file is: " << filename << endl; for(map::iterator keyval= params.begin(); keyval != params.end(); keyval++) cout << " " << keyval->first << " = " << keyval->second << endl; return 1; } /* ## reset the module state */ int reset_conf () { filename = string(); params = map(); return 1; } }; ./oar-2.5.2/sources/extra/cpp-scheduler/Oar_conflib.H0000644000175000017500000000075511757171206020462 0ustar plbplb#ifndef OAR_CONFLIB_H #define OAR_CONFLIB_H #include namespace conflib { unsigned int init_conf (std::string file); std::string get_conf_with_default_param ( std::string key, std::string defval); std::string get_conf ( std::string key ); bool is_conf ( std::string key ); int dump_conf (); int reset_conf (); }; #define CONFDEFAULT_INT(x,y) ( atoi( get_conf_with_default_param(x, y).c_str()) ) #define CONFDEFAULT_STR(x,y) ( get_conf_with_default_param(x, y).c_str() ) #endif ./oar-2.5.2/sources/extra/cpp-scheduler/Makefile_1oct080000644000175000017500000000007411757171206020664 0ustar plbplb all: cd UnitaryTests; make clean; make; valgrind ./AllTest./oar-2.5.2/sources/extra/cpp-scheduler/Makefile0000644000175000017500000001736011757171206017574 0ustar plbplb############################################################################# # Makefile for building: Oar_sched_gantt_with_timesharing_and_fairsharing # Generated by qmake (2.01a) (Qt 4.4.3) on: lun. janv. 5 16:38:00 2009 # Project: oarsched.pro # Template: app # Command: /usr/bin/qmake-qt4 -unix -o Makefile oarsched.pro ############################################################################# ####### Compiler, tools and options CC = gcc CXX = g++ DEFINES = -DQT_SQL_LIB -DQT_CORE_LIB -DQT_SHARED CFLAGS = -pipe -g -D_REENTRANT -Wall -W $(DEFINES) CXXFLAGS = -pipe -g -D_REENTRANT -Wall -W $(DEFINES) INCPATH = -I/usr/share/qt4/mkspecs/linux-g++ -I. -I/usr/include/qt4/QtCore -I/usr/include/qt4/QtCore -I/usr/include/qt4/QtSql -I/usr/include/qt4/QtSql -I/usr/include/qt4 -I. -I. LINK = g++ LFLAGS = LIBS = $(SUBLIBS) -L/usr/lib -lQtSql -lQtCore -lpthread AR = ar cqs RANLIB = QMAKE = /usr/bin/qmake-qt4 TAR = tar -cf COMPRESS = gzip -9f COPY = cp -f SED = sed COPY_FILE = $(COPY) COPY_DIR = $(COPY) -r INSTALL_FILE = install -m 644 -p INSTALL_DIR = $(COPY_DIR) INSTALL_PROGRAM = install -m 755 -p DEL_FILE = rm -f SYMLINK = ln -sf DEL_DIR = rmdir MOVE = mv -f CHK_DIR_EXISTS= test -d MKDIR = mkdir -p ####### Output directory OBJECTS_DIR = ./ ####### Files SOURCES = Oar_iolib.cc \ Oar_conflib.cc \ Gantt_hole_storage.cc \ Oar_resource_tree.cc \ Oar_sched_gantt_with_timesharing_and_fairsharing.cc OBJECTS = Oar_iolib.o \ Oar_conflib.o \ Gantt_hole_storage.o \ Oar_resource_tree.o \ Oar_sched_gantt_with_timesharing_and_fairsharing.o DIST = /usr/share/qt4/mkspecs/common/g++.conf \ /usr/share/qt4/mkspecs/common/unix.conf \ /usr/share/qt4/mkspecs/common/linux.conf \ /usr/share/qt4/mkspecs/qconfig.pri \ /usr/share/qt4/mkspecs/features/qt_functions.prf \ /usr/share/qt4/mkspecs/features/qt_config.prf \ /usr/share/qt4/mkspecs/features/exclusive_builds.prf \ /usr/share/qt4/mkspecs/features/default_pre.prf \ /usr/share/qt4/mkspecs/features/debug.prf \ /usr/share/qt4/mkspecs/features/default_post.prf \ /usr/share/qt4/mkspecs/features/qt.prf \ /usr/share/qt4/mkspecs/features/unix/thread.prf \ /usr/share/qt4/mkspecs/features/moc.prf \ /usr/share/qt4/mkspecs/features/warn_on.prf \ /usr/share/qt4/mkspecs/features/resources.prf \ /usr/share/qt4/mkspecs/features/uic.prf \ /usr/share/qt4/mkspecs/features/yacc.prf \ /usr/share/qt4/mkspecs/features/lex.prf \ oarsched.pro QMAKE_TARGET = Oar_sched_gantt_with_timesharing_and_fairsharing DESTDIR = TARGET = Oar_sched_gantt_with_timesharing_and_fairsharing first: all ####### Implicit rules .SUFFIXES: .o .c .cpp .cc .cxx .C .cpp.o: $(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<" .cc.o: $(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<" .cxx.o: $(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<" .C.o: $(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<" .c.o: $(CC) -c $(CFLAGS) $(INCPATH) -o "$@" "$<" ####### Build rules all: Makefile $(TARGET) $(TARGET): $(OBJECTS) $(LINK) $(LFLAGS) -o $(TARGET) $(OBJECTS) $(OBJCOMP) $(LIBS) Makefile: oarsched.pro /usr/share/qt4/mkspecs/linux-g++/qmake.conf /usr/share/qt4/mkspecs/common/g++.conf \ /usr/share/qt4/mkspecs/common/unix.conf \ /usr/share/qt4/mkspecs/common/linux.conf \ /usr/share/qt4/mkspecs/qconfig.pri \ /usr/share/qt4/mkspecs/features/qt_functions.prf \ /usr/share/qt4/mkspecs/features/qt_config.prf \ /usr/share/qt4/mkspecs/features/exclusive_builds.prf \ /usr/share/qt4/mkspecs/features/default_pre.prf \ /usr/share/qt4/mkspecs/features/debug.prf \ /usr/share/qt4/mkspecs/features/default_post.prf \ /usr/share/qt4/mkspecs/features/qt.prf \ /usr/share/qt4/mkspecs/features/unix/thread.prf \ /usr/share/qt4/mkspecs/features/moc.prf \ /usr/share/qt4/mkspecs/features/warn_on.prf \ /usr/share/qt4/mkspecs/features/resources.prf \ /usr/share/qt4/mkspecs/features/uic.prf \ /usr/share/qt4/mkspecs/features/yacc.prf \ /usr/share/qt4/mkspecs/features/lex.prf \ /usr/lib/libQtSql.prl \ /usr/lib/libQtCore.prl $(QMAKE) -unix -o Makefile oarsched.pro /usr/share/qt4/mkspecs/common/g++.conf: /usr/share/qt4/mkspecs/common/unix.conf: /usr/share/qt4/mkspecs/common/linux.conf: /usr/share/qt4/mkspecs/qconfig.pri: /usr/share/qt4/mkspecs/features/qt_functions.prf: /usr/share/qt4/mkspecs/features/qt_config.prf: /usr/share/qt4/mkspecs/features/exclusive_builds.prf: /usr/share/qt4/mkspecs/features/default_pre.prf: /usr/share/qt4/mkspecs/features/debug.prf: /usr/share/qt4/mkspecs/features/default_post.prf: /usr/share/qt4/mkspecs/features/qt.prf: /usr/share/qt4/mkspecs/features/unix/thread.prf: /usr/share/qt4/mkspecs/features/moc.prf: /usr/share/qt4/mkspecs/features/warn_on.prf: /usr/share/qt4/mkspecs/features/resources.prf: /usr/share/qt4/mkspecs/features/uic.prf: /usr/share/qt4/mkspecs/features/yacc.prf: /usr/share/qt4/mkspecs/features/lex.prf: /usr/lib/libQtSql.prl: /usr/lib/libQtCore.prl: qmake: FORCE @$(QMAKE) -unix -o Makefile oarsched.pro dist: @$(CHK_DIR_EXISTS) .tmp/Oar_sched_gantt_with_timesharing_and_fairsharing1.0.0 || $(MKDIR) .tmp/Oar_sched_gantt_with_timesharing_and_fairsharing1.0.0 $(COPY_FILE) --parents $(SOURCES) $(DIST) .tmp/Oar_sched_gantt_with_timesharing_and_fairsharing1.0.0/ && $(COPY_FILE) --parents Gantt_hole_storage.H Oar_resource_tree.H .tmp/Oar_sched_gantt_with_timesharing_and_fairsharing1.0.0/ && $(COPY_FILE) --parents Oar_iolib.cc Oar_conflib.cc Gantt_hole_storage.cc Oar_resource_tree.cc Oar_sched_gantt_with_timesharing_and_fairsharing.cc .tmp/Oar_sched_gantt_with_timesharing_and_fairsharing1.0.0/ && (cd `dirname .tmp/Oar_sched_gantt_with_timesharing_and_fairsharing1.0.0` && $(TAR) Oar_sched_gantt_with_timesharing_and_fairsharing1.0.0.tar Oar_sched_gantt_with_timesharing_and_fairsharing1.0.0 && $(COMPRESS) Oar_sched_gantt_with_timesharing_and_fairsharing1.0.0.tar) && $(MOVE) `dirname .tmp/Oar_sched_gantt_with_timesharing_and_fairsharing1.0.0`/Oar_sched_gantt_with_timesharing_and_fairsharing1.0.0.tar.gz . && $(DEL_FILE) -r .tmp/Oar_sched_gantt_with_timesharing_and_fairsharing1.0.0 clean:compiler_clean -$(DEL_FILE) $(OBJECTS) -$(DEL_FILE) *~ core *.core ####### Sub-libraries distclean: clean -$(DEL_FILE) $(TARGET) -$(DEL_FILE) Makefile mocclean: compiler_moc_header_clean compiler_moc_source_clean mocables: compiler_moc_header_make_all compiler_moc_source_make_all compiler_moc_header_make_all: compiler_moc_header_clean: compiler_rcc_make_all: compiler_rcc_clean: compiler_image_collection_make_all: qmake_image_collection.cpp compiler_image_collection_clean: -$(DEL_FILE) qmake_image_collection.cpp compiler_moc_source_make_all: compiler_moc_source_clean: compiler_uic_make_all: compiler_uic_clean: compiler_yacc_decl_make_all: compiler_yacc_decl_clean: compiler_yacc_impl_make_all: compiler_yacc_impl_clean: compiler_lex_make_all: compiler_lex_clean: compiler_clean: ####### Compile Oar_iolib.o: Oar_iolib.cc Oar_resource_tree.H $(CXX) -c $(CXXFLAGS) $(INCPATH) -o Oar_iolib.o Oar_iolib.cc Oar_conflib.o: Oar_conflib.cc $(CXX) -c $(CXXFLAGS) $(INCPATH) -o Oar_conflib.o Oar_conflib.cc Gantt_hole_storage.o: Gantt_hole_storage.cc Gantt_hole_storage.H \ Oar_resource_tree.H $(CXX) -c $(CXXFLAGS) $(INCPATH) -o Gantt_hole_storage.o Gantt_hole_storage.cc Oar_resource_tree.o: Oar_resource_tree.cc Oar_resource_tree.H $(CXX) -c $(CXXFLAGS) $(INCPATH) -o Oar_resource_tree.o Oar_resource_tree.cc Oar_sched_gantt_with_timesharing_and_fairsharing.o: Oar_sched_gantt_with_timesharing_and_fairsharing.cc $(CXX) -c $(CXXFLAGS) $(INCPATH) -o Oar_sched_gantt_with_timesharing_and_fairsharing.o Oar_sched_gantt_with_timesharing_and_fairsharing.cc ####### Install install: FORCE uninstall: FORCE FORCE: ./oar-2.5.2/sources/extra/cpp-scheduler/Gantt_hole_storage.cc0000644000175000017500000006730011757171206022252 0ustar plbplb /** \brief gant manipulation for the scheduler \author Gregory Mounié \version 0.1 \date 07 05 2008 version 0.1: Re-writing of the original perl code of OAR 2.1 to get a C++ version of the same algorithm. The use of the STL is done to be the closest possible of the perl version */ #include #include "Gantt_hole_storage.H" #include "Oar_resource_tree.H" #include // # $Id$ // package Gantt_hole_storage; // require Exporter; // use OAR::Schedulers::ResourceTree; // use Data::Dumper; // use warnings; // use strict; // # Note : All dates are in seconds // # Resources are integer so we store them in bit vectors // # Warning : this gantt cannot manage overlaping time slots // # 2^32 is infinity in 32 bits stored time //my $Infinity = 4294967296; // # Prototypes // # gantt chart management // sub new($$); // sub new_with_1_hole($$$$$); // sub add_new_resources($$); // sub set_occupation($$$$); // sub get_free_resources($$$); // sub find_first_hole($$$$$); // sub pretty_print($); // sub get_infinity_value(); namespace Gantt_hole_storage { unsigned int get_infinity_value() { // le meme que dans le code perl // BUG perl: 1 de trop assert(sizeof(unsigned int) == 4); return 4294967295UL; } // sub get_infinity_value(){ // return($Infinity); // } /** affichage des vecteurs de bool inserer dans la structure du gantt */ int pretty_print(Gantt *gantt) { } /* sub pretty_print($){ my $gantt = shift; my @bits = split(//, unpack("b*", $gantt->[0]->[2])); print("@bits\n"); foreach my $g (@{$gantt}){ print("BEGIN : $g->[0]\n"); foreach my $h (@{$g->[1]}){ @bits = split(//, unpack("b*", $h->[1])); print(" $h->[0] : @bits\n"); } print("\n"); } } */ /** Creates an empty Gantt arg : number of the max resource id */ /** fonction d'initialisation */ void Gantt::initGantt(int max_resource_number, int minimum_hole_duration) { std::vector empty_vec(max_resource_number); Hole h(0); h.holestop.push_back(StopTime(get_infinity_value(), empty_vec)); h.first_hole = true; h.all_inserted_resources = empty_vec; // zero_vec est une variable du Gant et pas du trou mantenant zero_vec = empty_vec; h.minimum_hole_duration = minimum_hole_duration; h.max_time_find_first_hole = std::pair(get_infinity_value(), get_infinity_value()); holes.push_back(h); } Gantt::Gantt(int max_resource_number, int minimum_hole_duration = 0) { initGantt(max_resource_number, minimum_hole_duration); } // # Creates an empty Gantt // # arg : number of the max resource id // sub new($$){ // my $max_resource_number = shift; // my $minimum_hole_duration = shift; // $minimum_hole_duration = 0 if (!defined($minimum_hole_duration)); // my $empty_vec = ''; // vec($empty_vec, $max_resource_number, 1) = 0; // my $result =[ // [ // 0, # start time of this hole // [ # ref of a structure which contains hole stop times and corresponding resources (ordered by end time) // [$Infinity, $empty_vec] // ], // $empty_vec, # Store all inserted resources (Only for the first Gantt hole) // $empty_vec, # Store empty vec with enough 0 (Only for the first hole) // $minimum_hole_duration, # minimum time for a hole // [$Infinity,$Infinity] # times that find_first_hole must not go after // ] // ]; // return($result); // } /** * Creates a Gantt with 1 hole * args number of the max resource id */ Gantt::Gantt(int max_resource_number, int minimum_hole_duration, int date, int duration, std::vector resources_vec) : Gantt::Gantt(max_resource_number, minimum_hole_duration) { resources_vec |= zero_vec; holes[0].start_time = date; holes[0].holestop = std::vector().push_back( StopTime(date + duration, resources_vec)); } // # Creates a Gantt with 1 hole // # arg : number of the max resource id // sub new_with_1_hole($$$$$){ // my $max_resource_number = shift; // my $minimum_hole_duration = shift; // my $date = shift; // my $duration = shift; // my $resources_vec = shift; // my $gantt = Gantt_hole_storage::new($max_resource_number, $minimum_hole_duration); // # Feed vector with enough 0 // $resources_vec |= $gantt->[0]->[3]; // $gantt->[0]->[0] = $date; // $gantt->[0]->[1] = [[($date + $duration), $resources_vec]]; // return($gantt); // } /** * Adds and initializes new resources in the gantt * args : gantt ref, bit vector of resources */ int add_new_resources(Gantt *gantt, std::vector resources_vec) { // Feed vector with enough 0 resources_vec |= gantt->zero_vec; // Verify which resources are not already inserted std::vector resource_to_add_vec = resources_vec & ( ! gantt->holes[0].all_inserted_resources); if ( resource_to_add_vec != gantt->zero_vec ) { // We need to insert new resources on all hole int i = 0; for(i=0; i < gantt->holes.size(); i++) // Add resources if ( gantt->.holes[i].holestop[ gantt->holes[i].holestop.size()-1 ].stop_time == get_infinity_value() ) { gant->holes[i].holestop[ gant->holes[i].holestop.size()-1 ].r |= resources_to_add_vec; } else { gantt->holes[i].holestop.push_back( StopTime(get_infinity_value(), resources_vec) ); } // Keep already inserted resources in mind gantt->holes[0].all_inserted_resources |= resources_vec; } return 0; } // # Adds and initializes new resources in the gantt // # args : gantt ref, bit vector of resources // sub add_new_resources($$) { // my ($gantt, $resources_vec) = @_; // # Feed vector with enough 0 // $resources_vec |= $gantt->[0]->[3]; // # Verify which resources are not already inserted // my $resources_to_add_vec = $resources_vec & (~ $gantt->[0]->[2]); // if (unpack("%32b*",$resources_to_add_vec) > 0){ // # We need to insert new resources on all hole // my $g = 0; // while ($g <= $#{@{$gantt}}){ // # Add resources // if ($gantt->[$g]->[1]->[$#{@{$gantt->[$g]->[1]}}]->[0] == $Infinity){ // $gantt->[$g]->[1]->[$#{@{$gantt->[$g]->[1]}}]->[1] |= $resources_to_add_vec; // }else{ // push(@{$gantt->[$g]->[1]}, [$Infinity, $resources_vec]); // } // $g++; // } // # Keep already inserted resources in mind // $gantt->[0]->[2] |= $resources_vec; // } // } /** Inserts in the gantt new resource occupations args : gantt ref, start slot date, slot duration, resources bit vector */ void set_occupation(Gantt *gantt, int start_date, int duration, std::vector resources_vec) { // Feed vector with enough 0 resources_vec |= gantt->zero_vec; // If a resource was not initialized add_new_resources(gantt, resources_vec); // If it is not yet done Hole new_hole = new Hole(date+duration+1); // + vecteur vide de holestop int g; for(g= 0; g <= gantt->holes.size() && gantt->holes[g].start_time <= new_hole.start_time; g++) { int slot_deleted = 0; // Look at all holes that are before the end of the occupation if (gantt->holes[g].holestop.size() >= 1 && gantt->holes[g].holestop[ gantt.holes[g].holestop.size() -1 ].stop_time >= date) { // Look at holes with a biggest slot >= $date int h = 0; int slot_date_here = 0; for(h = 0; h < gantt->holes[g].holestop.size(); h++) { // Look at all slots if (gantt->holes[g].holestop[h].stop_time == date) slot_date_here = 1; if (gantt->holes[g].holestop[h].stop_time > date) { // This slot ends after $date //print($date - $gantt->[$g]->[0]." -- $gantt->[0]->[4]\n"); if ( (gantt->holes[g].start_time < date) && (slot_date_here == 0) && ( date - gantt.holes[g].start_time > gantt.minimum_hole_duration) ) { // We must create a smaller slot (hole start time < $date) gantt->holes[g].holestop.insert(h, StopTime(date, gantt->holes[g].holestop[h].r)); h++; // Go to the slot that we were on it before the splice slot_date_here = 1; } // Add new slots in the new hole if ( (new_hole.start_time < gantt->holes[g].holestop[h].stop_time) && ( gantt->holes[g].holestop[h].stop_time - new_hole.start_time > gantt->holes[0].minimum_hole_duration ) ) { // copy slot in the new hole if needed int slot = 0; while( (slot <= new_hole.holestop.size() - 1 ) && ( new_hole.holestop[slot].stop_time < gantt->holes[g].holestop[h].stop_time ) ) { // Find right index in the sorted slot array slot++; } if (slot <= new_hole.holestop.size() - 1) { if ( new_hole.holestop[slot].stop_time == gantt->holes[g].holestop[h].stop_time ) { // If the slot already exists, binary OR vector resources new_hole.holestop[slot].r |= gantt->holes[g].holestop[h].r; } else { // Insert the new slot from the gantt // TODO: optimiser insertion new_hole.holestop.insert($slot, StopTime( gantt->holes[h].holestop[h].stop_time, gant.holes[g].holestop[h].r) ); } } else if (new_hole.start_time < gantt->holes[g].holestop[h].stop_time) { // There is no slot so we create one new_hole.holestop.push_back( StopTime(gantt->holes[g].holestop[h].stop_time, gantt->holes[g].holestop[h].r ) ); } } // Remove new occupied resources from the current slot gant.holes[g].holestop[h].r &= ( ! resources_vec ); if ( gantt->holes[g].holestop[h].r == std::vector(gantt->holes[g].holestop[h].r.size(), 0) ) { // There is no free resource on this slot so we delete it gantt->holes[g].holestop.erase(h); h --; slot_deleted = 1; } } } } if ( (slot_deleted == 1) && ( gantt->holes.holestop.size() == 0 )) { // There is no free slot on the current hole so we delete it gantt->holes.erase[g]; g--; } } } // sub set_occupation($$$$){ // my ($gantt, $date, $duration, $resources_vec) = @_; // # Feed vector with enough 0 // $resources_vec |= $gantt->[0]->[3]; // # If a resource was not initialized // add_new_resources($gantt,$resources_vec); # If it is not yet done // my $new_hole = [ // $date + $duration + 1, // [] // ]; // my $g = 0; // while (($g <= $#{@{$gantt}}) and ($gantt->[$g]->[0] <= $new_hole->[0])){ // my $slot_deleted = 0; // # Look at all holes that are before the end of the occupation // if (($#{@{$gantt->[$g]->[1]}} >= 0) and ($gantt->[$g]->[1]->[$#{@{$gantt->[$g]->[1]}}]->[0] >= $date)){ // # Look at holes with a biggest slot >= $date // my $h = 0; // my $slot_date_here = 0; // while ($h <= $#{@{$gantt->[$g]->[1]}}){ // # Look at all slots // $slot_date_here = 1 if ($gantt->[$g]->[1]->[$h]->[0] == $date); // if ($gantt->[$g]->[1]->[$h]->[0] > $date){ // # This slot ends after $date // #print($date - $gantt->[$g]->[0]." -- $gantt->[0]->[4]\n"); // if (($gantt->[$g]->[0] < $date) and ($slot_date_here == 0) and ($date - $gantt->[$g]->[0] > $gantt->[0]->[4])){ // # We must create a smaller slot (hole start time < $date) // splice(@{$gantt->[$g]->[1]}, $h, 0, [ $date , $gantt->[$g]->[1]->[$h]->[1] ]); // $h++; # Go to the slot that we were on it before the splice // $slot_date_here = 1; // } // # Add new slots in the new hole // if (($new_hole->[0] < $gantt->[$g]->[1]->[$h]->[0]) and ($gantt->[$g]->[1]->[$h]->[0] - $new_hole->[0] > $gantt->[0]->[4])){ // # copy slot in the new hole if needed // my $slot = 0; // while (($slot <= $#{@{$new_hole->[1]}}) and ($new_hole->[1]->[$slot]->[0] < $gantt->[$g]->[1]->[$h]->[0])){ // # Find right index in the sorted slot array // $slot++; // } // if ($slot <= $#{@{$new_hole->[1]}}){ // if ($new_hole->[1]->[$slot]->[0] == $gantt->[$g]->[1]->[$h]->[0]){ // # If the slot already exists // $new_hole->[1]->[$slot]->[1] |= $gantt->[$g]->[1]->[$h]->[1]; // }else{ // # Insert the new slot // splice(@{$new_hole->[1]}, $slot, 0, [$gantt->[$g]->[1]->[$h]->[0], $gantt->[$g]->[1]->[$h]->[1]]); // } // }elsif ($new_hole->[0] < $gantt->[$g]->[1]->[$h]->[0]){ // # There is no slot so we create one // push(@{$new_hole->[1]}, [ $gantt->[$g]->[1]->[$h]->[0], $gantt->[$g]->[1]->[$h]->[1] ]); // } // } // # Remove new occupied resources from the current slot // $gantt->[$g]->[1]->[$h]->[1] &= (~ $resources_vec) ; // if (unpack("%32b*",$gantt->[$g]->[1]->[$h]->[1]) == 0){ // # There is no free resource on this slot so we delete it // splice(@{$gantt->[$g]->[1]}, $h, 1); // $h--; // $slot_deleted = 1; // } // } // # Go to the next slot // $h++; // } // } // if (($slot_deleted == 1) and ($#{@{$gantt->[$g]->[1]}} < 0)){ // # There is no free slot on the current hole so we delete it // splice(@{$gantt}, $g, 1); // $g--; // } // # Go to the next hole // $g++; // } // if ($#{@{$new_hole->[1]}} >= 0){ // # Add the new hole // if (($g > 0) and ($g - 1 <= $#{@{$gantt}}) and ($gantt->[$g - 1]->[0] == $new_hole->[0])){ // # Verify if the hole does not already exist // splice(@{$gantt}, $g - 1, 1, $new_hole); // }else{ // splice(@{$gantt}, $g, 0, $new_hole); // } // } // } /** Find the first hole in the data structure that can fit the given slot */ int find_hole(Gantt *gantt, int begin_date, int duration) { int end_date = begin_date + duration; unsigned int g = 0; for(g=0; g < gantt->holes.size() && gantt->holes[g].start_time < begin_date && gantt->holes[g].holestop.rbegin()->stop_time < end_date; g++); return g; } // sub find_hole($$$){ // my ($gantt, $begin_date, $duration) = @_; // my $end_date = $begin_date + $duration; // my $g = 0; // while (($g <= $#{@{$gantt}}) and ($gantt->[$g]->[0] < $begin_date) and ($gantt->[$g]->[1]->[$#{@{$gantt->[$g]->[1]}}]->[0] < $end_date)){ // $g++ // } // return($g); // } /** Returns the vector of the maximum free resources at the given date for the given duration */ std::vector get_free_resources(Gantt *gantt, int begin_date, int duration) { int end_date = begin_date + duration; int hole_index = 0; // search the nearest hole for(hole_index=0; hole_index < gantt->holes.size() && gantt->holes[hole_index].start_time < begin_date // regarder le derner stop du trou && ( ( gantt->holes[hole_index].holestop.rbegin()->stop_time < end_date ) || (hole_index+1 < gantt->holes.size() && gantt->holes[hole_index+1].start_time < begin_date) ); hole_index++); if ( hole_index >= gantt->holes.size() ) return gantt->holes[0].minimum_hole_duration; int h = 0; for (h=0; h < gantt->holes[hole_index].holestop.size() && gantt->holes[hole_index].holestop[h].stop_time < end_date; h++); return gantt->holes[hole_index].holestop[h].r; } // sub get_free_resources($$$){ // my ($gantt, $begin_date, $duration) = @_; // my $end_date = $begin_date + $duration; // my $hole_index = 0; // # search the nearest hole // while (($hole_index <= $#{@{$gantt}}) and ($gantt->[$hole_index]->[0] < $begin_date) and // (($gantt->[$hole_index]->[1]->[$#{@{$gantt->[$hole_index]->[1]}}]->[0] < $end_date) or // (($hole_index + 1 <= $#{@{$gantt}}) and $gantt->[$hole_index + 1]->[0] < $begin_date))){ // $hole_index++; // } // return($gantt->[0]->[4]) if ($hole_index > $#{@{$gantt}}); // my $h = 0; // while (($h <= $#{@{$gantt->[$hole_index]->[1]}}) and ($gantt->[$hole_index]->[1]->[$h]->[0] < $end_date)){ // $h++; // } // return($gantt->[$hole_index]->[1]->[$h]->[1]); // } /** Take a list of resource trees and find a hole that fit args : gantt ref, initial time from which the search will begin, job duration, list of resource trees */ pair > find_first_hole(Gantt *gantt, int initial_time, int duration, vector tree_description_list, int timeout) { /* $tree_description_list->[0] --> First resource group corresponding tree $tree_description_list->[1] --> Second resource group corresponding tree ... */ if (tree_description_list.size() == 0 ) return pair >(get_infinity_value(), vector() ); vector result_tree_list; int end_loop = 0; int current_time = initial_time; int timeout_initial_time = time(NULL); // begin research at the first potential hole int current_hole_index = find_hole(gantt, initial_time, duration); int h = 0; while(end_loop == 0) { // Go to a right hole while( (current_hole_index < gantt.holes.size()) && ( // BUG ? test bizarre, comment est-on sur de h>0 ? (gantt.holes[current_hole_index].start_time + duration > gantt.holes[current_hole_index].holestop[h].stop_time) || ( (initial_time > gantt.holes[current_hole_index].start_time) && (initial_time + duration > gantt.holes[current_hole_index].holestop[h].stop_time ) ) ) ) { // BUG ? surtout que l'on ne teste h que la et il commence pas a 0 for( ; h < gant.hole[current_hole_index].holestop.size() && ( ( gantt.holes[current_hole_index].start_time + duration > gantt.holes[current_hole_index].holestop[h].stop_time ) || ( (inital_time > gantt.holes[current_hole_index].start_time) || (inital_time + duration > gantt.holes[current_hole_index].holestop[h].stop_time) ) ); h++); if (h >= gantt.holes[current_hole_index].holestop.size() ) { // in this hole no slot fits so we must search in the next hole h = 0; current_hole_index++; } } if (current_hole_index >= gantt.hole.size() ) { // no hole fits current_time = get_infinity_value(); result_tree_list = vector(0); end_loop = 1; } else { // printf("Treate hole %d, %d : %d --> %d\n", current_hole_index, h, gantt.holes[current_hole_index].start_time, gantt.holes[current_hole_index].holestop[h].stop_time); if ( inital_time < gantt.holes[current_hole_index].start_time ) current_time = gantt.holes[current_hole_index].start_time; //Check all trees TreeNode *tree_clone; int i = 0; do{ //# clone the tree, so we can work on it without damage tree_clone = OAR::Schedulers::ResourceTree::clone(tree_description_list[i]); //#Remove tree leafs that are not free vector vn = OAR::Schedulers::ResourceTree::get_tree_leafs(tree_clone); vector::iterator l = vn.begin(); while( l != vn.end() ) { if ( ! gantt.holes[current_hole_index].holestop[h].r[OAR::Schedulers::ResourceTree::get_current_resource_value(l)] ) { OAR::Schedulers::ResourceTree::delete_subtree(l); } l++; } // #print(Dumper($tree_clone)); tree_clone = OAR::Schedulers::ResourceTree::delete_tree_nodes_with_not_enough_resources(tree_clone); // #$Data::Dumper::Purity = 0; // #$Data::Dumper::Terse = 0; // #$Data::Dumper::Indent = 1; // #$Data::Dumper::Deepcopy = 0; // # print(Dumper($tree_clone)); result_tree_list[i] = tree_clone; i ++; } while(tree_clone != NULL && (i < tree_description_list.size())); if (tree_clone != NULL) { //# We find the first hole end_loop = 1; } else { //# Go to the next slot of this hole if (h >= gantt.holes[current_hole_index].holestop.size() ) { h = 0; current_hole_index++; } else { h++; } } } //# Check timeout // BUG: Gregory: I did not port the timeout for the moment // if (($current_hole_index <= $#{@{$gantt}}) and // (((time() - $timeout_initial_time) >= $timeout) or // (($gantt->[$current_hole_index]->[0] == $gantt->[0]->[5]->[0]) and ($gantt->[$current_hole_index]->[1]->[$h]->[0] >= $gantt->[0]->[5]->[1])) or // ($gantt->[$current_hole_index]->[0] > $gantt->[0]->[5]->[0]))){ // if (($gantt->[0]->[5]->[0] == $gantt->[$current_hole_index]->[0]) and // ($gantt->[0]->[5]->[1] > $gantt->[$current_hole_index]->[1]->[$h]->[0])){ // $gantt->[0]->[5]->[1] = $gantt->[$current_hole_index]->[1]->[$h]->[0]; // }elsif ($gantt->[0]->[5]->[0] > $gantt->[$current_hole_index]->[0]){ // $gantt->[0]->[5]->[0] = $gantt->[$current_hole_index]->[0]; // $gantt->[0]->[5]->[1] = $gantt->[$current_hole_index]->[1]->[$h]->[0]; // } // #print("TTTTTTT $gantt->[0]->[5]->[0] $gantt->[0]->[5]->[1] -- $gantt->[$current_hole_index]->[0] $gantt->[$current_hole_index]->[1]->[$h]->[0]\n"); // $current_time = $Infinity; // @result_tree_list = (); // $end_loop = 1; // } } return pair >(current_time, result_tree_list); } // sub find_first_hole($$$$$){ // my ($gantt, $initial_time, $duration, $tree_description_list, $timeout) = @_; // # $tree_description_list->[0] --> First resource group corresponding tree // # $tree_description_list->[1] --> Second resource group corresponding tree // # ... // return ($Infinity, ()) if (!defined($tree_description_list->[0])); // my @result_tree_list = (); // my $end_loop = 0; // my $current_time = $initial_time; // my $timeout_initial_time = time(); // # begin research at the first potential hole // my $current_hole_index = find_hole($gantt, $initial_time, $duration); // my $h = 0; // while ($end_loop == 0){ // # Go to a right hole // while (($current_hole_index <= $#{@{$gantt}}) and // (($gantt->[$current_hole_index]->[0] + $duration > $gantt->[$current_hole_index]->[1]->[$h]->[0]) or // (($initial_time > $gantt->[$current_hole_index]->[0]) and // ($initial_time + $duration > $gantt->[$current_hole_index]->[1]->[$h]->[0])))){ // while (($h <= $#{@{$gantt->[$current_hole_index]->[1]}}) and // (($gantt->[$current_hole_index]->[0] + $duration > $gantt->[$current_hole_index]->[1]->[$h]->[0]) or // (($initial_time > $gantt->[$current_hole_index]->[0]) and // ($initial_time + $duration > $gantt->[$current_hole_index]->[1]->[$h]->[0])))){ // $h++; // } // if ($h > $#{@{$gantt->[$current_hole_index]->[1]}}){ // # in this hole no slot fits so we must search in the next hole // $h = 0; // $current_hole_index++; // } // } // if ($current_hole_index > $#{@{$gantt}}){ // # no hole fits // $current_time = $Infinity; // @result_tree_list = (); // $end_loop = 1; // }else{ // #print("Treate hole $current_hole_index, $h : $gantt->[$current_hole_index]->[0] --> $gantt->[$current_hole_index]->[1]->[$h]->[0]\n"); // $current_time = $gantt->[$current_hole_index]->[0] if ($initial_time < $gantt->[$current_hole_index]->[0]); // #Check all trees // my $tree_clone; // my $i = 0; // do{ // # clone the tree, so we can work on it without damage // $tree_clone = OAR::Schedulers::ResourceTree::clone($tree_description_list->[$i]); // #Remove tree leafs that are not free // foreach my $l (OAR::Schedulers::ResourceTree::get_tree_leafs($tree_clone)){ // if (!vec($gantt->[$current_hole_index]->[1]->[$h]->[1],OAR::Schedulers::ResourceTree::get_current_resource_value($l),1)){ // OAR::Schedulers::ResourceTree::delete_subtree($l); // } // } // #print(Dumper($tree_clone)); // $tree_clone = OAR::Schedulers::ResourceTree::delete_tree_nodes_with_not_enough_resources($tree_clone); // #$Data::Dumper::Purity = 0; // #$Data::Dumper::Terse = 0; // #$Data::Dumper::Indent = 1; // #$Data::Dumper::Deepcopy = 0; // # print(Dumper($tree_clone)); // $result_tree_list[$i] = $tree_clone; // $i ++; // }while(defined($tree_clone) && ($i <= $#$tree_description_list)); // if (defined($tree_clone)){ // # We find the first hole // $end_loop = 1; // }else{ // # Go to the next slot of this hole // if ($h >= $#{@{$gantt->[$current_hole_index]->[1]}}){ // $h = 0; // $current_hole_index++; // }else{ // $h++; // } // } // } // # Check timeout // if (($current_hole_index <= $#{@{$gantt}}) and // (((time() - $timeout_initial_time) >= $timeout) or // (($gantt->[$current_hole_index]->[0] == $gantt->[0]->[5]->[0]) and ($gantt->[$current_hole_index]->[1]->[$h]->[0] >= $gantt->[0]->[5]->[1])) or // ($gantt->[$current_hole_index]->[0] > $gantt->[0]->[5]->[0]))){ // if (($gantt->[0]->[5]->[0] == $gantt->[$current_hole_index]->[0]) and // ($gantt->[0]->[5]->[1] > $gantt->[$current_hole_index]->[1]->[$h]->[0])){ // $gantt->[0]->[5]->[1] = $gantt->[$current_hole_index]->[1]->[$h]->[0]; // }elsif ($gantt->[0]->[5]->[0] > $gantt->[$current_hole_index]->[0]){ // $gantt->[0]->[5]->[0] = $gantt->[$current_hole_index]->[0]; // $gantt->[0]->[5]->[1] = $gantt->[$current_hole_index]->[1]->[$h]->[0]; // } // #print("TTTTTTT $gantt->[0]->[5]->[0] $gantt->[0]->[5]->[1] -- $gantt->[$current_hole_index]->[0] $gantt->[$current_hole_index]->[1]->[$h]->[0]\n"); // $current_time = $Infinity; // @result_tree_list = (); // $end_loop = 1; // } // } // return($current_time, \@result_tree_list); // } // return 1; } ./oar-2.5.2/sources/extra/cpp-scheduler/Gantt_hole_storage.H0000644000175000017500000000301311757171206022043 0ustar plbplb#ifndef GANTT_HOLE_STORAGE_H #define GANTT_HOLE_STORAGE_H #include //const unsigned int INFINITY = MAX_INT; #include "Oar_resource_tree.H" namespace Gant_hole_storage { class StopTime { public: unsigned int stop_time; std::vector r; StopTime(unsigned int time, std::vector res) : stop_time(time), r(res) {}; }; class Hole { public: int start_time; // $gantt->[0]->[0] std::vector holestop; /* just for the first hole */ bool first_hole; std::vector all_inserted_resources; int minimum_hole_duration; // $gantt->[0]->[4] std::pair max_time_find_first_hole; Hole(int start) : start_time(start) {}; }; class Gantt { void initGantt(int, int); public: std::vector zero_vec; // ex-perl $gantt->[0]->[3]; std::vector holes; Gantt(int, int); Gantt(int max_resource_number, int minimum_hole_duration, int date, int duration, std::vector resources_vec); }; //int new_1_with_holes(a,b,c,d,e); int add_new_resources(Gantt *a, std::vector b); int set_occupation(Gantt *a, int b, int c, std::vector d); int get_free_resources(Gantt *gantt, int begin_date, int duration); int find_hole(Gantt *gantt, int begin_date, int duration); std::pair > find_first_hole(Gantt *gantt, int initial_time, int duration, std::vector tree_description_list, int timeout); int pretty_print(Gantt *a); unsigned int get_infinity_value(); } #endif ./oar-2.5.2/sources/extra/cpp-scheduler/DBTests/0000755000175000017500000000000011757171206017435 5ustar plbplb./oar-2.5.2/sources/extra/cpp-scheduler/DBTests/qttest.pro0000644000175000017500000000031211757171206021477 0ustar plbplb SOURCES += qttest.cc ../Oar_iolib.cc ../Oar_resource_tree.cc ../Gantt_hole_storage.cc HEADERS += ../Oar_iolib.H ../Gantt_hole_storage.H ../Oar_resource_tree.H CONFIG += qt debug QT += sql QT -= gui ./oar-2.5.2/sources/extra/cpp-scheduler/DBTests/qttest.cc0000644000175000017500000000071011757171206021266 0ustar plbplb#include #include #include #include "../Oar_iolib.H" using namespace std; /** test of database management in qt sql by default, it use a sqlite database for testing purpose right now */ int miniconnect() { QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE"); db.setDatabaseName("mydb.db"); bool ok = db.open(); cout << "connect " << ok; } int main(int argc, char **argv) { miniconnect(); } ./oar-2.5.2/sources/extra/cpp-scheduler/DBTests/mydb.db0000644000175000017500000000000011757171206020665 0ustar plbplb./oar-2.5.2/sources/extra/cpp-scheduler/DBTests/Makefile0000644000175000017500000001544011757171206021101 0ustar plbplb############################################################################# # Makefile for building: qttest # Generated by qmake (2.01a) (Qt 4.4.3) on: mer. dc. 17 17:08:23 2008 # Project: qttest.pro # Template: app # Command: /usr/bin/qmake-qt4 -unix -o Makefile qttest.pro ############################################################################# ####### Compiler, tools and options CC = gcc CXX = g++ DEFINES = -DQT_SQL_LIB -DQT_CORE_LIB -DQT_SHARED CFLAGS = -pipe -g -D_REENTRANT -Wall -W $(DEFINES) CXXFLAGS = -pipe -g -D_REENTRANT -Wall -W $(DEFINES) INCPATH = -I/usr/share/qt4/mkspecs/linux-g++ -I. -I/usr/include/qt4/QtCore -I/usr/include/qt4/QtCore -I/usr/include/qt4/QtSql -I/usr/include/qt4/QtSql -I/usr/include/qt4 -I. -I. LINK = g++ LFLAGS = LIBS = $(SUBLIBS) -L/usr/lib -lQtSql -lQtCore -lpthread AR = ar cqs RANLIB = QMAKE = /usr/bin/qmake-qt4 TAR = tar -cf COMPRESS = gzip -9f COPY = cp -f SED = sed COPY_FILE = $(COPY) COPY_DIR = $(COPY) -r INSTALL_FILE = install -m 644 -p INSTALL_DIR = $(COPY_DIR) INSTALL_PROGRAM = install -m 755 -p DEL_FILE = rm -f SYMLINK = ln -sf DEL_DIR = rmdir MOVE = mv -f CHK_DIR_EXISTS= test -d MKDIR = mkdir -p ####### Output directory OBJECTS_DIR = ./ ####### Files SOURCES = qttest.cc \ ../Oar_iolib.cc \ ../Oar_resource_tree.cc \ ../Gantt_hole_storage.cc OBJECTS = qttest.o \ Oar_iolib.o \ Oar_resource_tree.o \ Gantt_hole_storage.o DIST = /usr/share/qt4/mkspecs/common/g++.conf \ /usr/share/qt4/mkspecs/common/unix.conf \ /usr/share/qt4/mkspecs/common/linux.conf \ /usr/share/qt4/mkspecs/qconfig.pri \ /usr/share/qt4/mkspecs/features/qt_functions.prf \ /usr/share/qt4/mkspecs/features/qt_config.prf \ /usr/share/qt4/mkspecs/features/exclusive_builds.prf \ /usr/share/qt4/mkspecs/features/default_pre.prf \ /usr/share/qt4/mkspecs/features/debug.prf \ /usr/share/qt4/mkspecs/features/default_post.prf \ /usr/share/qt4/mkspecs/features/qt.prf \ /usr/share/qt4/mkspecs/features/unix/thread.prf \ /usr/share/qt4/mkspecs/features/moc.prf \ /usr/share/qt4/mkspecs/features/warn_on.prf \ /usr/share/qt4/mkspecs/features/resources.prf \ /usr/share/qt4/mkspecs/features/uic.prf \ /usr/share/qt4/mkspecs/features/yacc.prf \ /usr/share/qt4/mkspecs/features/lex.prf \ qttest.pro QMAKE_TARGET = qttest DESTDIR = TARGET = qttest first: all ####### Implicit rules .SUFFIXES: .o .c .cpp .cc .cxx .C .cpp.o: $(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<" .cc.o: $(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<" .cxx.o: $(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<" .C.o: $(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<" .c.o: $(CC) -c $(CFLAGS) $(INCPATH) -o "$@" "$<" ####### Build rules all: Makefile $(TARGET) $(TARGET): $(OBJECTS) $(LINK) $(LFLAGS) -o $(TARGET) $(OBJECTS) $(OBJCOMP) $(LIBS) Makefile: qttest.pro /usr/share/qt4/mkspecs/linux-g++/qmake.conf /usr/share/qt4/mkspecs/common/g++.conf \ /usr/share/qt4/mkspecs/common/unix.conf \ /usr/share/qt4/mkspecs/common/linux.conf \ /usr/share/qt4/mkspecs/qconfig.pri \ /usr/share/qt4/mkspecs/features/qt_functions.prf \ /usr/share/qt4/mkspecs/features/qt_config.prf \ /usr/share/qt4/mkspecs/features/exclusive_builds.prf \ /usr/share/qt4/mkspecs/features/default_pre.prf \ /usr/share/qt4/mkspecs/features/debug.prf \ /usr/share/qt4/mkspecs/features/default_post.prf \ /usr/share/qt4/mkspecs/features/qt.prf \ /usr/share/qt4/mkspecs/features/unix/thread.prf \ /usr/share/qt4/mkspecs/features/moc.prf \ /usr/share/qt4/mkspecs/features/warn_on.prf \ /usr/share/qt4/mkspecs/features/resources.prf \ /usr/share/qt4/mkspecs/features/uic.prf \ /usr/share/qt4/mkspecs/features/yacc.prf \ /usr/share/qt4/mkspecs/features/lex.prf \ /usr/lib/libQtSql.prl \ /usr/lib/libQtCore.prl $(QMAKE) -unix -o Makefile qttest.pro /usr/share/qt4/mkspecs/common/g++.conf: /usr/share/qt4/mkspecs/common/unix.conf: /usr/share/qt4/mkspecs/common/linux.conf: /usr/share/qt4/mkspecs/qconfig.pri: /usr/share/qt4/mkspecs/features/qt_functions.prf: /usr/share/qt4/mkspecs/features/qt_config.prf: /usr/share/qt4/mkspecs/features/exclusive_builds.prf: /usr/share/qt4/mkspecs/features/default_pre.prf: /usr/share/qt4/mkspecs/features/debug.prf: /usr/share/qt4/mkspecs/features/default_post.prf: /usr/share/qt4/mkspecs/features/qt.prf: /usr/share/qt4/mkspecs/features/unix/thread.prf: /usr/share/qt4/mkspecs/features/moc.prf: /usr/share/qt4/mkspecs/features/warn_on.prf: /usr/share/qt4/mkspecs/features/resources.prf: /usr/share/qt4/mkspecs/features/uic.prf: /usr/share/qt4/mkspecs/features/yacc.prf: /usr/share/qt4/mkspecs/features/lex.prf: /usr/lib/libQtSql.prl: /usr/lib/libQtCore.prl: qmake: FORCE @$(QMAKE) -unix -o Makefile qttest.pro dist: @$(CHK_DIR_EXISTS) .tmp/qttest1.0.0 || $(MKDIR) .tmp/qttest1.0.0 $(COPY_FILE) --parents $(SOURCES) $(DIST) .tmp/qttest1.0.0/ && $(COPY_FILE) --parents ../Oar_iolib.H ../Gantt_hole_storage.H ../Oar_resource_tree.H .tmp/qttest1.0.0/ && $(COPY_FILE) --parents qttest.cc ../Oar_iolib.cc ../Oar_resource_tree.cc ../Gantt_hole_storage.cc .tmp/qttest1.0.0/ && (cd `dirname .tmp/qttest1.0.0` && $(TAR) qttest1.0.0.tar qttest1.0.0 && $(COMPRESS) qttest1.0.0.tar) && $(MOVE) `dirname .tmp/qttest1.0.0`/qttest1.0.0.tar.gz . && $(DEL_FILE) -r .tmp/qttest1.0.0 clean:compiler_clean -$(DEL_FILE) $(OBJECTS) -$(DEL_FILE) *~ core *.core ####### Sub-libraries distclean: clean -$(DEL_FILE) $(TARGET) -$(DEL_FILE) Makefile mocclean: compiler_moc_header_clean compiler_moc_source_clean mocables: compiler_moc_header_make_all compiler_moc_source_make_all compiler_moc_header_make_all: compiler_moc_header_clean: compiler_rcc_make_all: compiler_rcc_clean: compiler_image_collection_make_all: qmake_image_collection.cpp compiler_image_collection_clean: -$(DEL_FILE) qmake_image_collection.cpp compiler_moc_source_make_all: compiler_moc_source_clean: compiler_uic_make_all: compiler_uic_clean: compiler_yacc_decl_make_all: compiler_yacc_decl_clean: compiler_yacc_impl_make_all: compiler_yacc_impl_clean: compiler_lex_make_all: compiler_lex_clean: compiler_clean: ####### Compile qttest.o: qttest.cc ../Oar_iolib.H \ ../Oar_resource_tree.H $(CXX) -c $(CXXFLAGS) $(INCPATH) -o qttest.o qttest.cc Oar_iolib.o: ../Oar_iolib.cc ../Oar_resource_tree.H $(CXX) -c $(CXXFLAGS) $(INCPATH) -o Oar_iolib.o ../Oar_iolib.cc Oar_resource_tree.o: ../Oar_resource_tree.cc ../Oar_resource_tree.H $(CXX) -c $(CXXFLAGS) $(INCPATH) -o Oar_resource_tree.o ../Oar_resource_tree.cc Gantt_hole_storage.o: ../Gantt_hole_storage.cc ../Gantt_hole_storage.H \ ../Oar_resource_tree.H $(CXX) -c $(CXXFLAGS) $(INCPATH) -o Gantt_hole_storage.o ../Gantt_hole_storage.cc ####### Install install: FORCE uninstall: FORCE FORCE: ./oar-2.5.2/sources/extra/black-maria/0000755000175000017500000000000011757171206015532 5ustar plbplb./oar-2.5.2/sources/extra/black-maria/sbatch0000755000175000017500000000015711757171206016727 0ustar plbplb#!/bin/bash echo "FAKE sbatch Slurm command" echo "ARGS:" $@ echo "sbatch: Submitted batch job " $RANDOM ./oar-2.5.2/sources/extra/black-maria/black-maria.rst0000644000175000017500000000277611757171206020443 0ustar plbplb======================================= Black-Maria: a coupling with other JRMS ======================================= Black-Maria (BKM) is an extension to couple foreign resource and job management system (RJMS) with OAR. BKM is primarly developed to meet the needs of ANR-SPADES project. Principle: ---------- Workflow 1) submission to oar to a specific queue 2) oar metascheduler launch black-maria-sched 3) black-maria-sched submit to foreign RJMS and set parameter for black_maria_pilot.sh 4) black_maria_pilot.sh is launched when foreign RJMS started job 5) black_maria_pilot.sh signals black-maria-synch daemon (throught tcp via nc) 6) black-maria-synch set node allocated by foreign RJMS in oar's db and signal Almigthy (OAR?) 7) black_maria_pilot.sh sleep (default oar walltime minus a timeguard) Limitations: ------------ * Only Slurm is supported as foreign RJMS (04/03/11) * Walltime must be expressed in second Dependencies: ------------- * lua: 5.1 or higher * lua libraries: liblua5.1-copas0 blua5.1-coxpcall0 liblua5.1-sql-mysql-2 * nc for bkm-pilot script to communicate to bkm-synch (not secure !!!) Installation: ------------- * see black-maria-test.rst Running: -------- * see black-maria-test.rst Todo: ----- * an alpha version ;) * more RJMS connector * support node nodelist with slurm node list grammar ex: dev[0-8,12,13,18-25] * recette/makefile installation Comments, bugs, request: ------------------------ * Caution it's a prototype and only developed for experimentation purpose Log: ---- ./oar-2.5.2/sources/extra/black-maria/black-maria-test.rst0000644000175000017500000001115111757171206021403 0ustar plbplb================== Black-Maria Test ================== Some tests conduct with oar_slurm an BKM locale: Cannot set LC_ALL to default locale: No such file or directory sudo dpkg-reconfigure locales en_US.UTF-8 locale-gen en_US.UTF-8 (a test pour le mode batch???) copy des clés (?) mkdir .ssh scp -P 2222 ~/.ssh/id_rsa.pub root@localhost:/root/.ssh/authorized_keys Install BKM =========== Get oar source --------------- apt-get install subversion svn checkout svn://scm.gforge.inria.fr/svn/oar/trunk cd ~/prog/oar/trunk/modules/scheduler scp -P 2222 -r black-maria root@localhost:/root apt-get install phpmyadmin apt-get install lua5.1 liblua5.1-copas0 blua5.1-coxpcall0 liblua5.1-sql-mysql-2 echo "lua /root/black-maria/black-maria-sched.lua \$1" > /usr/lib/oar/schedulers/black-maria-sched ln -s /root/black-maria/oar.lua /usr/share/lua/5.1/ ln -s /root/black-maria/black-maria-pilot.sh /bin/ chmod 755 /usr/lib/oar/schedulers/black-maria-sched chmod 755 /root *IMPORTANT* oardodo est sensible ! ln -s /usr/local/bin/sbatch /usr/bin/sbatch_oardodo #sync from external black-maria rsync -avL . kam:gvim /root/black-maria/ # install new oar queue (spades for instance) oarnotify --add_queue "spades,5,black-maria-sched" Multiple slurmd support: slurm must be recompiled with "--enable-multiple-slurmd" parameter at configure step. For more information, see: https://computing.llnl.gov/linux/slurm/programmer_guide.html (realized in kameleon's slurm step kameleon/steps/slurm) Running and helpers: ==================== Launch BKM-sync: ---------------- sudo -iu oar lua /root/black-maria/black-maria-sync.lua Launch manually for test purpose BKM-sched ------------------------------------------- sudo -iu oar /usr/lib/oar/schedulers/black-maria-sched spades Truncate jobs ------------- ~/trunk/modules/scheduler/ocaml-schedulers/test# irb -r oar_db_setting.rb irb(main):001:0> oar_truncate_jobs Oarsub w/o container -------------------- oarsub -q spades -l nodes=1,walltime=00:1:00 fo test oarsub container ---------------------- oarsub -t container -l nodes=1,walltime=00:10:00 "sleep 500" [ADMISSION RULE] Modify resource description with type constraints OAR_JOB_ID=1 oarsub -t inner=1 -l nodes=1,walltime=00:8:00 "sleep 200" Oarsub simple cycle: -------------------- oarsub -t container -q spades -l nodes=1,walltime=00:10:00 foo oarsub -t inner=1 -l nodes=1,walltime=00:6:00 "sleep 200" Slurm commandes: ---------------- sinfo, squeue, scancel scancel -p test cancel all job from test partition Logs tests: =========== next ---- doc / install log message multiple nodes 06/03/11 -------- simple oarsub cycle ok: oarsub -t container -q spades -l nodes=1,walltime=00:10:00 foo oarsub -t inner=1 -l nodes=1,walltime=00:6:00 "sleep 200" kameleon@kameleon:~$ squeue JOBID PARTITION NAME USER ST TIME NODES NODELIST(REASON) 138 test black-ma kameleon R 4:20 1 node1 kameleon@kameleon:~$ oarstat Job id Name User Submission Date S Queue ---------- -------------- -------------- ------------------- - ---------- 1 kameleon 2011-03-06 18:21:35 R spades 2 kameleon 2011-03-06 18:22:12 R default 05/03/11 -------- * Besoin de vider la table job OAR:... en lua ??? -> non utilisation deruby * faire test cyle complet * 02/03/11 -------- * ./kvm-tcp oar-slurm-v2.raw Notes: kvm-tcp -> sudo kvm -m 512 -redir tcp:2222::22 $1 oarsub -I sinfo OK 17/10/10 -------- * test oar_slurm.raw * user/pwd: root/kameleon * fix locales * test: oarsub -I OK * test: slurmmeleon@kameleon:~$ sinfo PARTITION AVAIL TIMELIMIT NODES STATE NODELIST local* up infinite 1 idle kameleon kameleon@kameleon:~$ squeue JOBID PARTITION NAME USER ST TIME NODES NODELIST(REASON) kameleon@kameleon:~$ scontrol show partition PartitionName=local AllocNodes=ALL AllowGroups=ALL Default=YES DefaultTime=NONE DisableRootJobs=NO Hidden=NO MaxNodes=UNLIMITED MaxTime=UNLIMITED MinNodes=1 Nodes=kameleon Priority=1 RootOnly=NO Shared=NO State=UP TotalCPUs=2 TotalNodes=1 kameleon@kameleon:~$ scontrol show node NodeName=kameleon Arch=i686 CoresPerSocket=2 CPUAlloc=0 CPUErr=0 CPUTot=2 Features=(null) OS=Linux RealMemory=2000 Sockets=1 State=IDLE ThreadsPerCore=1 TmpDisk=0 Weight=1 Reason=(null) To update node when it's in down state scontrol update NodeName=kameleon State=RESUME SLURM_JOB_NODELIST is the list of allocated node in condensed format exemple lx[15,18,32-33] dev[0-8,18-25],edev[0-25] ??? is this possible ??? "rack[0-63]_blade[0-41]" => valid but we doesn't support it in BKM ./oar-2.5.2/sources/extra/black-maria/black-maria-sync.lua0000644000175000017500000000602711757171206021357 0ustar plbplbrequire "oar" require "copas" oar.conf_load() oar.connect() if not oar.conf["BKM_SYNC_PORT"] then bkm_sync_port = 2220 else bkm_sync_port = oar.conf["BKM_SYNC_PORT"] end -- -- Transform a compact node list as use in Slurm to list of individual nodes -- ex: nodes[23,45,50-52] -> {nodes23, node45, node50, node51, node52} -- TODO: this funcion must be move to an helper library -- TODO: support different named nodes function flatten_nodelist(nodelist_compact) x,y,node,desc = nodelist_compact:find("^(%S+)%[(%S+)%]") if not x then -- only one node return {nodelist_compact} end d = oar.tsplit(desc:gsub(","," ")) local nodelist = {} for i,n in ipairs(d) do x,y, b,e = n:find("(%d+)-(%d+)") if x then for i=b,e,1 do nodelist[#nodelist+1]=node..i end else nodelist[#nodelist+1]=node..n end end return nodelist end function notify_almighty() local client = socket.connect(oar.conf["SERVER_HOSTNAME"],oar.conf["SERVER_PORT"]) client:send("Scheduling\n") client:close() end function handler(c, host, port) local peer = host .. ":" .. port print("BKM-sync: connection from", peer) -- c:send("Hello\r\n") -- get data local data = (c:receive"*l") print("data from", peer, data) assert(loadstring("job_info =" .. data))() dumptable(job_info) print(job_info.node_list) -- read RJMS' node file and build resource id list -- TODO: SLURM_JOB_NODELIST= lx[15,18,32-33] -> use flatten_nodelist resource_ids = {} k = 1 -- convert node_list or node_file allocated and given by foreign RJMS to OAR's resource id list -- TODO need to uniq the node_file if job_info.node_list=='' then local f = assert(io.open(job_info.node_file, "r")) for line in f:lines() do print(line) for i,r_id in ipairs(nodes_resources_ids[line]) do resource_ids[k]=r_id k = k + 1 end end else print("BKM-sync: dump flatten_nodellist") dumptable(flatten_nodelist(job_info.node_list)) for i, node in ipairs(flatten_nodelist(job_info.node_list)) do print("BKM-sync: allocated node:"..node) for i,r_id in ipairs(nodes_resources_ids[node]) do resource_ids[k]=r_id k = k + 1 end end end print("BKM-sync: dump resource_ids") dumptable(resource_ids) oar.save_assignements_black_maria(job_info.moldable_j_id,resource_ids) -- update job's state to Running and set assigned_moldable_job field oar.sql("UPDATE jobs SET state = ".."'Running'".. ",assigned_moldable_job = ".. job_info.moldable_j_id.. " WHERE job_id = ".. job_info.j_id) -- notify almighy (schedule cycle ? will update Gantt Diagram ??) notify_almighty() -- notifiy for external tools ? end -- -- main -- -- retreive resource_ids by node nodes_resources_ids = oar.get_nodes_resources_black_maria() dumptable(nodes_resources_ids) -- add tcp server copas.addserver(assert(socket.bind("*",bkm_sync_port)), function(c) return handler(copas.wrap(c), c:getpeername()) end ) -- launch server copas.loop() ./oar-2.5.2/sources/extra/black-maria/black-maria-sched.lua0000644000175000017500000000503211757171206021464 0ustar plbplbrequire "oar" oar.conf_load() oar.connect() submitted_jobs = {} if not oar.conf["BKM_SYNC_HOST"] then bkm_sync_host = oar.conf["SERVER_HOSTNAME"] else bkm_sync_host = oar.conf["BKM_SYNC_HOST"] end if not oar.conf["BKM_SYNC_PORT"] then bkm_sync_port = 2220 else bkm_sync_port = oar.conf["BKM_SYNC_PORT"] end -- index correspondance for job's attributs in return of get_waiting_jobs_black_maria function j_id= 1 walltime = 2 nb_res = 6 modalble_id = 4 user = 7 -- Submit job on foreign LRMS -- Jobs are submitted sequentially. It's made the assumption that there not a lot of these kind of job at a given time. -- TODO: a connector for each supported JRMS (priority to SLURM) function submit_to_jrms(jobs_to_launch) local job_ids = {} -- TODO must add oardodo user local submit_cmd_part1 = " sbatch_oardodo --workdir=/tmp/ -n" local submit_cmd_part2 = " black-maria-pilot.sh " for i,job in ipairs(jobs_to_launch) do local oardodo = "export OARDO_BECOME_USER="..job[user].."; oardodo " job_ids[i] = job[j_id] print("BKM: submit job to LRMS") local nb_nodes = job[nb_res] if oar.conf["BKM_RESOURCE_FACTOR"] then nb_nodes = nb_nodes / oar.conf["BKM_RESOURCE_FACTOR"] end local cmd = oardodo .. submit_cmd_part1 .. nb_nodes .. " -t " .. job[walltime]/60 .. ":10" .. -- TODO adapt for each RJMSand parametrize timeguard 10 seconds here submit_cmd_part2 .. bkm_sync_host .. " " .. bkm_sync_port .. " " .. job[j_id] .." " .. job[modalble_id] .. " " .. job[walltime] -- cmd = "export OARDO_BECOME_USER=kameleon; oardodo yop1" print("BKM: " .. cmd) f = assert (io.popen (cmd)) -- TODO retrieve the exist status value for line in f:lines() do print(line) -- retrieve foreign_jobid -- sbatch: Submitted batch job 469 b,i,jid_jrms = line:find("sbatch.*%s(%d+)$") if b then print("Prout:" .. jid_jrms) -- save serialize job info ( break end end end -- update scheduler_info jobs' field to not to resubmit in next cycle -- TODO: build job_ids -- oar.set_scheduler_message_range(job_ids,"submitted to JRMS") end -- -- main -- queue = "default" if arg[1] then queue = arg[1] end print("BKM-sched launched with queue: "..queue) waiting_jobs = oar.get_waiting_jobs_black_maria(queue) dumptable(waiting_jobs) if #waiting_jobs>0 then submit_to_jrms(waiting_jobs) else print("BKM-sched: nothing to do") end ./oar-2.5.2/sources/extra/black-maria/black-maria-pilot.sh0000644000175000017500000000102211757171206021351 0ustar plbplb#!/bin/bash BKM_SYNC_HOST=$1 BKM_SYNC_PORT=$2 OAR_JOB_ID=$3 OAR_MOLDABLE_JOB_ID=$4 WALLTIME=$5 echo "BKM-pilot args:" $@ echo "BKM-pilot SLURM_NODELIST: " $SLURM_NODELIST #TODO switch case depending of foreign rjms NODE_FILE= NODE_LIST=$SLURM_NODELIST # # notify black-maria-sync daemon # BKM_SYNC_DATA="{j_id=$OAR_JOB_ID, moldable_j_id=$OAR_MOLDABLE_JOB_ID, node_file='$NODE_FILE', node_list='$NODE_LIST'}" echo $BKM_SYNC_DATA echo $BKM_SYNC_DATA | nc $BKM_SYNC_HOST $BKM_SYNC_PORT # time to sleep sleep $WALLTIME sleep 2 ./oar-2.5.2/sources/desktop_computing/0000755000175000017500000000000011757171206016002 5ustar plbplb./oar-2.5.2/sources/desktop_computing/steps/0000755000175000017500000000000011757171206017140 5ustar plbplb./oar-2.5.2/sources/desktop_computing/steps/server.conf0000644000175000017500000000066411757171206021323 0ustar plbplblocal 192.168.56.101 tls-server dev tap server 10.0.0.0 255.255.255.0 max-clients 25 ca /etc/openvpn/easy-rsa/2.0/keys/ca.crt cert /etc/openvpn/easy-rsa/2.0/keys/192.168.56.101.crt key /etc/openvpn/easy-rsa/2.0/keys/192.168.56.101.key dh /etc/openvpn/easy-rsa/2.0/keys/dh1024.pem tls-auth /etc/openvpn/easy-rsa/2.0/keys/ta.key 0 cipher BF-CBC comp-lzo keepalive 10 120 log-append /var/log/openvpn.log status /var/log/openvpn-status.log ./oar-2.5.2/sources/desktop_computing/steps/oar_desktop_server.yaml0000644000175000017500000000143611757171206023730 0ustar plbplboar_desktop_server: - oar_desktop_server: - exec_chroot: apt-get install -y --force-yes openvpn #bind9 - write_file: - /etc/openvpn/server.conf - | local 192.168.56.1 dev tap daemon server 10.0.0.1 255.255.255.0 push "route 192.168.56.0 255.255.255.0" push "dhcp-option DNS 192.168.1.50" max-clients 25 ca /etc/openvpn/keys/ca.crt cert /etc/openvpn/keys/xena.crt key /etc/openvpn/keys/xena.key dh /etc/openvpn/keys/dh1024.pem tls-auth /etc/openvpn/keys/ta.key 0 cipher BF-CBC comp-lzo keepalive 10 120 log-append /var/log/openvpn.log status /var/log/openvpn-status.log ifconfig-pool-persist /etc/openvpn/ipp.txt mute 20 verb 4 ./oar-2.5.2/sources/desktop_computing/steps/oar_desktop_client.yaml0000644000175000017500000000120411757171206023671 0ustar plbplboar_desktop_client: - oar_desktop_client: - append_file: - /etc/apt/sources.list - | $$oar_repository - exec_chroot: apt-get update - exec_chroot: apt-get install -y --force-yes oar-desktop-computing-agent openvpn subversion - exec_chroot: svn export svn://scm.gforge.inria.fr/svn/oar/branches/gsoc-2010/desktop_computing/oar-agent.pl /usr/bin/oar-agent - write_file: - /etc/openvpn/client1.conf - | ## openvpn client1.conf remote 192.168.56.101 dev tun proto udp ifconfig 10.0.0.2 10.0.0.1 secret /etc/openvpn/keys/static.key comp-lzo keepalive 10 60 ./oar-2.5.2/sources/desktop_computing/steps/client1.conf0000644000175000017500000000034211757171206021345 0ustar plbplbdev tap proto udp remote 192.168.56.101 tls-client ca /etc/openvpn/keys/ca.crt cert /etc/openvpn/keys/192.168.56.102.crt key /etc/openvpn/keys/192.168.56.102.key tls-auth /etc/openvpn/keys/ta.key 1 cipher BF-CBC comp-lzo pull ./oar-2.5.2/sources/desktop_computing/recipes/0000755000175000017500000000000011757171206017434 5ustar plbplb./oar-2.5.2/sources/desktop_computing/recipes/debian_oar_desktop_server.yaml0000755000175000017500000000511211757171206025524 0ustar plbplb# ### debian.yaml Kameleon recipe ### # This recipe can be used to create any Debian appliance. It has been # tested succesfully to generate etch, lenny and squeeze as of April 2010. # The steps and variables beginning with "oar" can be activated to make # a OAR client/server demo/test appliance. # By default, it makes a tgz image and a qcow2 directly bootable into KVM. # global: # # Where Kameleon stores tmp files and appliances workdir_base: /var/tmp/kameleon # # Debian specific distrib: debian debian_version_name: squeeze distrib_repository: http://ftp.fr.debian.org/debian/ # # Architecture arch: i386 kernel_arch: "686" # # Extra packages to install on the minimal base system #extra_packages: "vim less bzip2 openssh-server rsync gnupg locales debian-keyring console-tools mingetty" # If you're building a OAR appliance, use this package list: extra_packages: "vim less bzip2 openssh-server rsync gnupg locales debian-keyring ruby libyaml-perl libjson-perl mysql-server mysql-client sudo libdbi-perl libdbd-mysql-perl perl-suid taktuk pidentd console-tools mingetty curl" # # Network configuration network_hostname: oar # Following variables are used by the "network_config_static" step #network_eth0_ip: 129.88.70.251 #network_eth0_mask: 255.255.255.248 #network_gateway: 129.88.70.249 #network_dns: 129.88.30.10 #network_domain: imag.fr # # If you're building a Xen appliance, it may help: #xen_domu_modules: /lib/modules/2.6.32-4-xen-amd64 # # You can start with a "checkpoint_resume" step if you want # to start form a pre-built image. The image can be made # with the "checkpoint" step. #checkpoint_file: /var/tmp/kameleon_checkpoint.tgz # # OAR specific oar_repository: "deb http://oar.imag.fr/debian/2.5 unstable main" steps: - debian_check_deps - check_deps: - rsync - building_appliance - building_kvm_images - oar/oar_precheck # - checkpoint_resume - bootstrap - system_config # - network_config_static - root_passwd - mount_proc - software_install: - extra_packages - kernel_install # - checkpoint - oar/oar_debian_install - oar/oar_system_config - oar/oar_config - oar/oar_devel - oar/oar_desktop_server - oar/oar_clean # - kameleon/kameleon_debian_install - autologin - strip - umount_proc # - xen_domu # - oar/oar_build_tgz - build_appliance: - clean_udev # - save_as_tgz - create_raw_image - create_nbd_device - mkfs - mount_image - copy_system_tree # - grub_197_workaround - install_grub - umount_image # - save_as_raw # - save_as_vmdk # - save_as_qcow2 - save_as_vdi # - save_as_iso - clean ./oar-2.5.2/sources/desktop_computing/recipes/debian_oar_desktop_agent.yaml0000755000175000017500000000516211757171206025321 0ustar plbplb# ### debian.yaml Kameleon recipe ### # This recipe can be used to create any Debian appliance. It has been # tested succesfully to generate etch, lenny and squeeze as of April 2010. # The steps and variables beginning with "oar" can be activated to make # a OAR client/server demo/test appliance. # By default, it makes a tgz image and a qcow2 directly bootable into KVM. # global: # # Where Kameleon stores tmp files and appliances workdir_base: /var/tmp/kameleon # # Debian specific distrib: debian debian_version_name: squeeze distrib_repository: http://ftp.fr.debian.org/debian/ # # Architecture arch: i386 kernel_arch: "686" # # Extra packages to install on the minimal base system #extra_packages: "vim less bzip2 openssh-server rsync gnupg locales debian-keyring console-tools mingetty" # If you're building a OAR appliance, use this package list: extra_packages: "vim less bzip2 openssh-server rsync gnupg locales debian-keyring ruby libyaml-perl libjson-perl mysql-server mysql-client sudo libdbi-perl libdbd-mysql-perl perl-suid taktuk pidentd console-tools mingetty curl libcrypt-ssleay-perl libfile-slurp-perl" # # Network configuration network_hostname: oar # Following variables are used by the "network_config_static" step #network_eth0_ip: 129.88.70.251 #network_eth0_mask: 255.255.255.248 #network_gateway: 129.88.70.249 #network_dns: 129.88.30.10 #network_domain: imag.fr # # If you're building a Xen appliance, it may help: #xen_domu_modules: /lib/modules/2.6.32-4-xen-amd64 # # You can start with a "checkpoint_resume" step if you want # to start form a pre-built image. The image can be made # with the "checkpoint" step. #checkpoint_file: /var/tmp/kameleon_checkpoint.tgz # # OAR specific oar_repository: "deb http://oar.imag.fr/debian/2.5 unstable main" steps: - debian_check_deps - check_deps: - rsync - building_appliance - building_kvm_images - oar/oar_precheck # - checkpoint_resume - bootstrap - system_config # - network_config_static - root_passwd - mount_proc - software_install: - extra_packages - kernel_install # - checkpoint - oar/oar_debian_install - oar/oar_system_config - oar/oar_config - oar/oar_devel - oar/oar_desktop_client - oar/oar_clean # - kameleon/kameleon_debian_install - autologin - strip - umount_proc # - xen_domu # - oar/oar_build_tgz - build_appliance: - clean_udev # - save_as_tgz - create_raw_image - create_nbd_device - mkfs - mount_image - copy_system_tree # - grub_197_workaround - install_grub - umount_image # - save_as_raw # - save_as_vmdk # - save_as_qcow2 - save_as_vdi # - save_as_iso - clean ./oar-2.5.2/sources/desktop_computing/oarres.pl0000755000175000017500000000204611757171206017637 0ustar plbplb#!/usr/bin/perl # $Id$ # # job stageout delivery use strict; use DBI(); use OAR::IO; my $jobid = shift or die "Job id is missing.\n"; my $stageoutfile = shift or die "Job stageout filename is missing.\n"; ( -r $stageoutfile ) or die "Stageout file not found.\n"; my $base = OAR::IO::connect(); my $job = OAR::IO::get_job($base,$jobid) or die "Failed to get job information\n"; ((defined $job->{'job_user'}) and (defined $job->{'launching_directory'})) or die "Some of the job information are missing\n"; #system "sudo -u ".$job->{'user'}." tar xvfz $stageoutfile -C ".$job->{'launchingDirectory'}." && rm -v $stageoutfile" and die "Stageout delivery failed: $!\n"; #system "sudo -u ".$job->{'job_user'}." tar xfz $stageoutfile -C ".$job->{'launching_directory'}.">& /dev/null && rm $stageoutfile" and die "Stageout delivery failed: $!\n"; $ENV{OARDO_BECOME_USER} = $job->{job_user}; system "oardodo tar xfz $stageoutfile -C $job->{'launching_directory'} >& /dev/null && rm $stageoutfile" and die "Stageout delivery failed: $!\n"; OAR::IO::disconnect($base); ./oar-2.5.2/sources/desktop_computing/oarfetch.sh0000755000175000017500000000276311757171206020144 0ustar plbplb#!/bin/bash # this script allows to retrieve desktop computing jobs results # $Id$ usage() { cat < Please provide the Id of the desktop computing job you want to retrieve the results of. EOF exit 1 } JOBID=$1 [ -n "$JOBID" ] || usage SERVER_HOSTNAME=`grep SERVER_HOSTNAME /etc/oar.conf| cut -f2 -d=` ssh -T $SERVER_HOSTNAME < /dev/null DB_HOSTNAME=\`grep DB_HOSTNAME /etc/oar.conf | cut -f2 -d=\` DB_BASE_NAME=\`grep DB_BASE_NAME /etc/oar.conf | cut -f2 -d=\` DB_BASE_LOGIN=\`grep DB_BASE_LOGIN /etc/oar.conf | cut -f2 -d=\` DB_BASE_PASSWD=\`grep DB_BASE_PASSWD /etc/oar.conf | cut -f2 -d=\` JOBUSER=\$(mysql -u\$DB_BASE_LOGIN -p\$DB_BASE_PASSWD -h\$DB_HOSTNAME <&2 exit 3 fi STAGEOUT_DIR=\`grep STAGEOUT_DIR /etc/oar.conf | cut -f2 -d=\` STAGEOUT=\$STAGEOUT_DIR/$JOBID.tgz if [ ! -r \$STAGEOUT ]; then echo "Sorry, no stageout file found for job $JOBID." 1>&2 exit 4 fi cat \$STAGEOUT exit 0 SSHEOF if [ $? != 0 ]; then echo "Error !" 2>&1 exit 1 fi # exit 0 # read -p "Purge this job result data on server ? [Y/n]" -n 1 -s PURGE if [ "x$PURGE" != "xn" -a "x$PURGE" != "xN" ]; then ssh -T $SERVER_HOSTNAME < 2) and warn $msg; } # serialize data # parameters: perl data # return: serialized data sub serialize($) { my $data = shift; my $serialized = Dumper($data); return $serialized; } # unserialize data # parameters: serialized data # return: perl data sub unserialize($) { my $serialized = shift; my $data = eval($serialized); return $data; } # write http message # parameters: content, content-type sub httpwrite($$) { my $content = shift; my $content_type = shift; defined $content_type or $content_type = 'text/plain'; print $cgi->header($content_type); print $content; } ###### # pull request handling ###### # pull function # First update node status in OAR database -> Alive + expiry-data++ unless we actually quit # if node does not exist yet, we try to declare it, if the configuration allows that. # Then process jobs, to kill or to launch, with error handling sub pull() { my $quit=$cgi->param('QUIT'); my $hostname=$cgi->param('HOSTNAME') or die "Node hostname missing."; my $base = OAR::IO::connect() or die "cannot connect to the data base\n"; my $is_desktop_computing = OAR::IO::is_node_desktop_computing($base,$hostname); my $do_notify; $do_notify=undef; if ($quit) { if (defined $is_desktop_computing and $is_desktop_computing eq 'YES') { OAR::IO::lock_table($base,["resources"]); OAR::IO::set_node_nextState($base,$hostname,"Absent"); OAR::IO::set_node_expiryDate($base,$hostname,OAR::IO::get_date($base)); OAR::IO::unlock_table($base); $do_notify=1; } else { OAR::IO::disconnect($base); my $msg = "$hostname is not declared as a Desktop Computing node in OAR database.\n"; my $data = { 'error' => $msg }; httpwrite(serialize($data),'application/data'); exit 0; } } else { if (defined $is_desktop_computing) { if ($is_desktop_computing eq 'YES') { OAR::IO::lock_table($base,["resources"]); if (OAR::IO::set_node_nextState_if_necessary($base,$hostname,"Alive") > 0){ $do_notify=1; } OAR::IO::set_node_expiryDate($base,$hostname, OAR::IO::get_date($base) + $expiry); OAR::IO::unlock_table($base); } else { OAR::IO::disconnect($base); my $msg = "$hostname is not declared as a Desktop Computing node in OAR database.\n"; my $data = { 'error' => $msg }; httpwrite(serialize($data),'application/data'); exit 0; } } else { if ($allow_create_node) { message "Trying to add $hostname to OAR database...\n"; my $resource = OAR::IO::add_resource($base, $hostname, "Alive"); OAR::IO::set_resource_property($base,$resource,"desktop_computing","YES"); OAR::IO::set_resource_nextState($base,$resource,"Alive"); OAR::IO::set_node_expiryDate($base,$hostname, OAR::IO::get_date($base) + $expiry); $do_notify=1; } else { OAR::IO::disconnect($base); my $msg = "$hostname is not a known Desktop Computing node, declare it in OAR database first.\n"; my $data = { 'error' => $msg }; httpwrite(serialize($data),'application/data'); exit 0; } } } if ($do_notify) { OAR::Tools::notify_tcp_socket($remote_host,$remote_port,"ChState"); } my $agentJobs=unserialize($cgi->param('JOBS')); my $dbJobs = OAR::IO::get_desktop_computing_host_jobs($base,$hostname); my $toLaunchJobs = undef; my $toKillJobs = undef; foreach my $jobid (keys %$dbJobs) { if (OAR::IO::is_tokill_job($base, $jobid)) { message "$jobid must be killed\n"; $toKillJobs->{$jobid}=$dbJobs->{$jobid}; OAR::IO::job_arm_leon_timer($base, $jobid) } else { message "$jobid must be kept running\n"; } if (not $quit and $dbJobs->{$jobid}->{'state'} eq "toLaunch") { $toLaunchJobs->{$jobid}=$dbJobs->{$jobid}; # $toLaunchJobs->{$jobid}->{'pulltime'} = OAR::IO::get_unix_timestamp($base); my $stagein = OAR::IO::get_job_stagein($base,$jobid); if (defined $stagein->{'md5sum'}) { $toLaunchJobs->{$jobid}->{'stagein'}->{'md5sum'}=$stagein->{'md5sum'}; $toLaunchJobs->{$jobid}->{'stagein'}->{'compression'}=$stagein->{'compression'}; $toLaunchJobs->{$jobid}->{'stagein'}->{'size'}=$stagein->{'size'}; } # OAR::IO::set_job_state($base,$jobid,"Launching"); OAR::IO::set_running_date($base,$jobid); OAR::IO::set_job_state($base,$jobid,"Running"); } elsif ($dbJobs->{$jobid}->{'state'} eq "Launching" or $dbJobs->{$jobid}->{'state'} eq "Running" ) { unless (grep $jobid, keys %$agentJobs) { message("[oar-cgi $jobid] Job $jobid terminated\n"); OAR::IO::lock_table($base,["jobs","job_state_logs","resources","assigned_resources","event_logs","challenges","moldable_job_descriptions","job_types","job_dependencies","job_resource_groups","job_resource_descriptions"]); OAR::IO::set_finish_date($base,$jobid); my $strWARN = "[oar-cgi $jobid] Job was killed"; message("$strWARN\n"); OAR::IO::set_job_state($base,$jobid,"Error"); OAR::IO::set_job_message($base,$jobid,"$strWARN"); OAR::IO::unlock_table($base); } } } $do_notify=undef; foreach my $jobid (keys %$agentJobs) { if (defined $agentJobs->{$jobid}->{'terminated'}) { $do_notify = 1; # TODO: As soon as BibBip becomes a library, replace this copy of BipBip code by a function call. # my $base = OAR::IO::connect() or die "cgi-job-end: cannot connect to the data base\n"; message("Job $jobid terminated\n"); OAR::IO::lock_table($base,["jobs","job_state_logs","resources","assigned_resources","event_logs","challenges","moldable_job_descriptions","job_types","job_dependencies","job_resource_groups","job_resource_descriptions"]); my $refJob = OAR::IO::get_job($base,$jobid); if ($refJob->{'state'} eq "Running"){ OAR::IO::set_finish_date($base,$jobid); message("Release nodes for $jobid\n"); if ($agentJobs->{$jobid}->{'terminated'} eq 'exit' and $agentJobs->{$jobid}->{'exitstatus'} == 0) { message("Launch completed OK for $jobid\n"); OAR::IO::set_job_state($base,$jobid,"Terminated"); OAR::IO::set_job_message($base,$jobid,"ALL is GOOD"); } else { my $strWARN = "Job $jobid failed (maybe killed)"; message("$strWARN\n"); OAR::IO::set_job_state($base,$jobid,"Error"); OAR::IO::set_job_message($base,$jobid,"$strWARN"); } } else { message("Job $jobid was previously killed or Terminated but I did not know that!!\n"); } OAR::IO::unlock_table($base); } } OAR::IO::disconnect($base); if ($do_notify) { OAR::Tools::notify_tcp_socket($remote_host,$remote_port,"Term"); } # End of BipBip code. my $data = { 'launch' => $toLaunchJobs, 'kill' => $toKillJobs, }; message "toLaunchJobs=".serialize($toLaunchJobs)."\n"; message "toKillJobs=".serialize($toKillJobs)."\n"; # message "pull=".serialize($data)."\n"; httpwrite(serialize($data),'application/data'); } ###### # stagein request handling ###### # job stagein function # gives the stagein for a job sub jobStageIn() { my $jobid =$cgi->param('JOBID') or die "JOBID not found.\n"; my $base = OAR::IO::connect() or die "cannot connect to the data base\n"; my $stagein = OAR::IO::get_job_stagein($base,$jobid); OAR::IO::disconnect($base); if ($stagein->{'method'} eq "FILE") { httpwrite("","data/binary"); open F,"< ".$stagein->{'location'} or die "Can't open stagein ".$stagein->{'location'}.": $!"; print ; close F; } else { die "Stagein method ".$stagein->{'method'}." not yet implemented.\n"; } # OAR::IO::set_running_date($base,$jobid); # OAR::IO::set_job_state($base,$jobid,"Running"); # OAR::IO::disconnect($base); } ###### # stageout request handling ###### # job stageout function # retrieve a job stageout sub jobStageOut() { my $jobid =$cgi->param('JOBID'); defined $jobid or die "JOBID not found.\n"; my $out = $cgi->upload('STAGEOUT'); my $filename = $stageout_dir.$jobid.".tgz"; copy($out,$filename) or message "Job $jobid stageout retrieval failed $!\n"; httpwrite("Job $jobid stageout.","text/plain"); system "$ENV{OARDIR}/oarres $jobid $filename < /dev/null >& /dev/null &"; } ##### # main function ##### sub main() { my $reqtype = $cgi->param('REQTYPE') or "REQTYPE not found.\n"; if ($reqtype eq 'PULL') { pull(); } elsif ( $reqtype eq 'STAGEIN') { jobStageIn(); } elsif ( $reqtype eq 'STAGEOUT') { jobStageOut(); } else { die "Invalid REQTYPE: ".$reqtype."\n"; } } ##### # Here we go ! ##### main(); ./oar-2.5.2/sources/desktop_computing/oar-cgi0000755000175000017500000000026511757171206017254 0ustar plbplb#!/bin/bash # $Id$ OARDIR=/usr/local/oar OARUSER=oar export PERL5LIB=$OARDIR:$PERL5LIB export OARDIR exec sudo -u $OARUSER /home/neyron/huron/oar-dc/DesktopComputing/oar-cgi.pl ./oar-2.5.2/sources/desktop_computing/oar-agent.pl0000755000175000017500000003503511757171206020225 0ustar plbplb#!/usr/bin/perl # $Id$ # OAR agent for Desktop Computing ##### # use packages ##### # {{{ use warnings; use strict; use Sys::Hostname; use POSIX qw(:signal_h :errno_h :sys_wait_h); use Data::Dumper; use LWP::UserAgent; use HTTP::Request::Common qw(POST $DYNAMIC_FILE_UPLOAD); use URI::URL; use Fcntl ':flock'; use IO::Handle; use Getopt::Long; use File::Basename; use REST::Client; use JSON; use File::Slurp qw ( slurp ) ; # }}} ###### # Command line arameters... ###### # {{{ my $hostname; my $sleep_next_pull; my $stageindir; my $cache_timeout; my $pidfile; my $verbose; my $help; my $client = REST::Client->new(); Getopt::Long::Configure ("gnu_getopt"); GetOptions ("nodename|n=s" => \$hostname, "pull-interval|i=i" => \$sleep_next_pull, "stagein-directory|d=s" => \$stageindir, "stagein-timeout|t=s" => \$cache_timeout, "pidfile|p=s" => \$pidfile, "verbose|v" => \$verbose, "help|h" => \$help, ); sub usage() { print < Run OAR Desktop Computing HTTP agent, using URL as the CGI proxy to OAR server Options are: -n, --nodename= OAR node hostname (default: system hostname) -i, --pull-interval= OAR server pull interval in seconds (default: 30) -d, --stagein-directory= directory where stageins are stored (default: ./stageins) -r, --stagein-timeout= how long do we keep a stagein in cache (default: 300) -p, --pidfile= write the main process pid in this file -v, --verbose increase verbosity -h, --help show this message EOS exit 1; } (defined $help) and usage(); my $url = $ARGV[0]; (defined $url) or usage(); (defined $hostname) or $hostname = hostname(); (defined $sleep_next_pull) or $sleep_next_pull = 30; (defined $stageindir) or $stageindir = "stageins"; system "mkdir -p $stageindir"; (defined $cache_timeout) or $cache_timeout = 300; if (defined $pidfile) { open F, "> $pidfile" or die "Open pidfile $pidfile failed: $!\n"; print F "$$\n"; close F; } # }}} ###### # Miscelanious common functions ###### # {{{ # prints messages on STDERR if in verbose mode # parameters: a message sub message($) { my $msg = shift; (defined $verbose) and warn $msg; } # blocks a list of signals (unused currently) # parameters: a list of signals # return: sigset to use to unblock sub sigBlock(@) { my $sigset = POSIX::SigSet->new(@_); my $old_sigset = POSIX::SigSet->new; unless (defined sigprocmask(SIG_BLOCK, $sigset, $old_sigset)) { die "Could not block signals\n"; } return $old_sigset; } # unblocks signals previously blocked (unused currently) # parameters: the return of the previous sigBlock function call sub sigUnblock($) { my $old_sigset = shift; unless (defined sigprocmask(SIG_UNBLOCK, $old_sigset)) { die "Could not unblock signals\n"; } } # serialize data # parameters: perl data # return: serialized data sub serialize($) { my $data = shift; my $serialized = Dumper($data); return $serialized; } # unserialize data # parameters: serialized data # return: perl data sub unserialize($) { my $serialized = shift; my $data = eval($serialized); return $data; } # }}} ###### # Internal data ###### # {{{ # suitable Data::Dumper configuration for serialization $Data::Dumper::Purity = 1; $Data::Dumper::Terse = 1; $Data::Dumper::Indent = 0; $Data::Dumper::Deepcopy = 1; # HTTP user agent my $cgiUrl = new URI::URL($url); my $userAgent = LWP::UserAgent->new(); $userAgent->agent("OAR-Agent/0.1 "); my $maxretry=4; my $kill_timeout = 5; my %childs; my %job2pid; my %borndead; my $launch_job_pid = undef; my $killchild = undef; my $stageintime; my $quit=0; my $stageinprefix = "oar-"; my $reader = undef; my $writer = undef; # }}} ###### # Signal handlers ###### sub sigQuitHandler(); # {{{ $SIG{QUIT} = \&sigQuitHandler; sub sigQuitHandler() { $SIG{QUIT} = \&sigQuitHandler; #$quit++; # we should not do system calls in a handler !:( } # }}} sub sigIntHandler(); # {{{ $SIG{INT} = \&sigIntHandler; sub sigIntHandler() { $SIG{INT} = \&sigIntHandler; #$quit++; # we should not do system calls in a handler !:( } # }}} sub sigUSR1Handler(); # {{{ $SIG{USR1} = \&sigUSR1Handler; sub sigUSR1Handler() { $SIG{USR1} = \&sigUSR1Handler; message "\t\tUSR1:".serialize(\%childs)."\n"; # we should not do system calls in a handler !:( } # }}} sub sigChildHandler; # {{{ $SIG{CHLD} = \&sigChildHandler; sub sigChildHandler { $SIG{CHLD} = \&sigChildHandler; # we should not do system calls in a handler !:( # warn "#CHLD"; } # }}} sub sigTermHandler; # {{{ $SIG{TERM} = 'DEFAULT'; sub sigTermHandler { $SIG{TERM} = \&sigTermHandler; # we should not do system calls in a handler !:( $killchild = 1; # warn "#TERM"; } # }}} ###### # Main process (agent) functions ###### # gets information from OAR server via CGI proxy: oar-cgi sub request($$$); # {{{ sub request($$$) { my $retry = $maxretry; $client->GET("$url/oarapi/resources/nodes/$hostname/jobs.json"); my $result; do { unless ($client->responseCode() eq '200') { $retry and print "HTTP request failed, retrying ($retry)\n" or die "HTTP request failed\n"; $retry--; } } until ($client->responseCode() eq '200'); $result = decode_json $client->responseContent(); return ($result, 'application/json'); } sub do_fork($$) { my $jobid = shift; my $toLaunchJobs = shift; my $pid = fork(); if ($pid > 0) { $client->POST($url."oarapi/jobs/$jobid/run.json"); } elsif ($pid == 0) { launch_job($jobid,$toLaunchJobs->{$jobid}); exit 0; } else { die "fork"; } } # }}} # processes pulled job information # setups job child processes # handles to kill then to launch jobs. sub process_jobs(); # {{{ sub process_jobs() { # Get the list of jobs to launch $client->GET("$url/oarapi/resources/nodes/$hostname/jobs.json"); $client->responseCode() == 200 or die "Couldn't get the job list"; my $toLaunchJobs = decode_json $client->responseContent(); my $toKillJobs; foreach my $jobid (keys %$toKillJobs) { message "\t\tKilling job $jobid\n"; my $pid = $job2pid{$jobid}; if (defined ($pid)) { kill TERM => $pid; } else { $borndead{$jobid}=1; } } foreach my $jobid (keys %$toLaunchJobs) { do_fork($jobid, $toLaunchJobs); } } # }}} # cleans up stageins cache directory # to old stagein files are removed sub stagein_cleanup (); # {{{ sub stagein_cleanup () { opendir DIR, "$stageindir/" or die "Can't open $stageindir: $!"; while( defined (my $file = readdir DIR) ) { if ($file =~ /^$stageinprefix/ and not ($file =~ /\.lock$/ or $file eq ".." or $file eq ".")) { # lock to be sure no new job will try to use this file while we are removing it. my $lockfile = "$file.lock"; open LOCKFILE,"> $lockfile" or warn "Open lockfile failed: $!\n"; flock LOCKFILE,LOCK_EX or warn "Lock lockfile failed: $!\n"; if (time - (stat "$stageindir/$file")[8] > $cache_timeout) { message "\t\tCache cleanup: deleting stagein file: $file\n"; unlink "$stageindir/$file"; } flock LOCKFILE,LOCK_UN or warn "Unlock lockfile failed: $!\n"; close LOCKFILE or warn "Close lockfile failed: $!\n"; (-e $lockfile ) and ( unlink $lockfile or warn "Unlink lockfile failed: $!\n" ); } } closedir(DIR); } # }}} # send a signal to all jobs child processes sub kill_them_all($); # {{{ sub kill_them_all($) { my $killsignal = shift; foreach my $pid (keys %childs) { message "\t\tKilling $pid with $killsignal...\n"; kill $killsignal => $pid; } } # }}} ###### # Child processes (jobs) functions ###### # recursive function use to build a child process hierarchy, given the father. # may be used to clean up job exec in case of INT or oardel # not use yet sub psLoop($); # {{{ sub psLoop($) { my $pid = shift; my $ptree = {}; my @plist; message "\t($$) Suspending process $pid\n"; kill STOP => $pid or die "kill -STOP $pid failed: $!\n"; open PS, "ps --ppid $pid -opid |" or die "open ps command pipe failed: $!\n"; ; foreach my $p () { chomp $p; push @plist,$p; } close PS; foreach my $p (@plist) { $ptree->{$p} = psLoop($p); } return $ptree; } # }}} # recursive function use to kill a child process hierarchy, given the hierarchy built with psLoop. # may be used to clean up job exec in case of INT or oardel # not use yet sub killLoop($); # {{{ sub killLoop($) { my $ptree = shift; foreach my $p (keys %$ptree) { killLoop($ptree->{$p}); message "\t($$) Killing process $p\n"; kill KILL => $p or die "kill -KILL $p failed: $!\n"; } } # }}} # stagein prcessed sub stagein($$); # {{{ sub stagein($$) { my $jobid = shift; my $job = shift; if (defined $job->{'stagein'}) { (-d $stageindir) or mkdir $stageindir; my $file = "$stageindir/$stageinprefix".$job->{'stagein'}->{'md5sum'}; # locks file to insure no other job or stagein_cleanup is not messing everything up. my $lockfile = "$file.lock"; open LOCKFILE,"> $lockfile" or warn "($$)Open lockfile failed: $!\n"; flock LOCKFILE,LOCK_EX or warn "($$)Lock lockfile failed: $!\n"; $launch_job_pid = fork(); if ($launch_job_pid == 0) { $SIG{CHLD}='DEFAULT'; $SIG{TERM}='DEFAULT'; if ( -r $file and (stat $file)[7] == $job->{'stagein'}->{'size'} ) { message "($$)Job $jobid stagein already fetched\n"; } else { message "($$)Fetching job $jobid stagein (".$job->{'stagein'}->{'size'}." bytes)\n"; request("form-data",[ 'REQTYPE' => 'STAGEIN', 'JOBID' => $jobid ],$file); } message "($$)Deploying job $jobid stagein\n"; if ($job->{'stagein'}->{'compression'} eq "tar.gz") { exec "tar xfz $file -m -C $jobid/ && touch $jobid"; die "($$)Exec Failed: $!\n"; } else { die "($$)Stagein compression method ".$job->{'stagein'}->{'compression'}." not yet implemented\n"; } } message "\t($$)Forked stagein process with pid: $launch_job_pid\n"; my $status = undef; my $rin = ''; do { my $pid = waitpid(-1,WNOHANG); if ($pid == $launch_job_pid) { # stagein child teminated $status = $?; } elsif (defined $killchild ) { # got a kill signal kill KILL => $launch_job_pid; } } until (defined $status); $launch_job_pid = undef; #until (wait == $launch_job_pid) {}; #my $status = $?; #$launch_job_pid = undef; if ($status == 0) { $stageintime=(stat $jobid)[9]+1; sleep 1; } else { message "\t($$)Stagein failed or aborted, cleaning up...\n"; if ( -r $file ) { unless ((stat $file)[7] == $job->{'stagein'}->{'size'} ) { unlink $file; } } } flock LOCKFILE,LOCK_UN or warn "($$)Unlock lockfile failed: $!\n"; close LOCKFILE or warn "($$)Close lockfile failed: $!\n"; ( -e $lockfile ) and ( unlink $lockfile or warn "($$)Unlink lockfile failed: $!\n" ); return $status; } else { return 0; } } # }}} # home brewed version of the system command sub runcmd($$); # {{{ sub runcmd($$) { my $jobid = shift; my $job = shift; my $cmd = $job->{'command'}; my $directory = $job->{'directory'}; $cmd =~ s#^$directory/?##; $launch_job_pid = fork(); if ($launch_job_pid == 0) { $ENV{'OAR_JOBID'} = $jobid; message "($$)Executing '$cmd'\n"; if ((!open(STDOUT, ">>$job->{'stdout_file'}")) or (!open(STDERR, ">>$job->{'stderr_file'}"))){ die("($$)Cannot write stdout and stderr files: $job->{'stdout_file'} $job->{'stderr_file'}\n"); } exec $cmd; die "($$)Exec Failed: $!\n"; } message "\t($$)Forked process pid: $launch_job_pid\n"; my $already_killed = undef; my $status = undef; my $rin = ''; do { my $pid = waitpid(-1,WNOHANG); if ($pid == $launch_job_pid) { # runcmd child teminated $status = $?; } elsif (defined $killchild and not defined $already_killed ) { # got a kill signal my $ptree = {}; $ptree->{$launch_job_pid} = psLoop($launch_job_pid); killLoop($ptree); $already_killed = 1; } } until (defined $status); $launch_job_pid = undef; message "\t($$)Cmd exit: $status\n"; return $status; } # }}} # stageout processed sub stageout($$); # {{{ sub stageout($$) { my $jobid = shift; my $job = shift; # we pack only files created after the stagein unarchiving my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($stageintime); my $date = sprintf("%04d-%02d-%02d %02d:%02d:%02d",$year+1900,$mon+1,$mday,$hour,$min,$sec); my $stageout = $jobid.".tgz"; system "tar cfz $stageout -C $jobid --newer-mtime \"$date\" ."; $client->addHeader("Content-Type", "application/gzip"); $client->POST("$url/oarapi/jobs/$jobid/stageout.json", scalar slurp("$jobid.tgz")); $client->POST("$url/oarapi/jobs/$jobid/terminate.json"); return 0; } # }}} # launches a job: manages stagein, runcmd, and stageout sub launch_job($$); # {{{ sub launch_job($$) { my $jobid = shift; my $job = shift; my $in = serialize($jobid); # if we already receive the order to kill the job if ($borndead{$jobid}) { message "\t($$)Job $jobid is borndead\n"; exit 1; } mkdir $jobid or warn "($$)Cannot create job $jobid directory: $!\n"; # fetch and setup stagein is there is one $stageintime=(stat $jobid)[9]; my $stagein_status = stagein($jobid, $job); if ($stagein_status != 0 or $killchild ) { message "\t($$)Job $jobid killed during its stagein\n"; system "rm -rf $jobid $jobid.tgz"; exit 2; } # execute the job chdir $jobid or warn "($$)Cannot change directory to $jobid: $!\n"; my $runcmd_status = runcmd($jobid, $job); chdir ".." or warn "($$)Cannot change directory: $!\n"; # handle stageout my $stageout_status = stageout($jobid, $job); if ($stageout_status != 0 or $killchild) { message "\t($$)Job $jobid killed during its stageout\n"; system "rm -rf $jobid $jobid.tgz"; exit 4; } # clean up and exit system "rm -rf $jobid $jobid.tgz"; exit 0; } # }}} sub sign_in(){ $client->addHeader("Content-Type", "application/json"); $client->POST("$url/oarapi/desktop/agents.json", "{\"hostname\": \"$hostname\" }"); if ($client->responseCode() ne '200') { die "Couldn't connect with the oar server"; } } ###### # main function ###### sub main(); # {{{ sub main() { sign_in(); while(1){ #maybe inline? process_jobs(); sleep 5; } kill_them_all('TERM'); exit 0; } # }}} ###### # Here we go ! ###### main(); ./oar-2.5.2/sources/desktop_computing/agent/0000755000175000017500000000000011757171206017100 5ustar plbplb./oar-2.5.2/sources/desktop_computing/agent/test/0000755000175000017500000000000011757171206020057 5ustar plbplb./oar-2.5.2/sources/desktop_computing/agent/test/systemtest.rb0000644000175000017500000000004711757171206022631 0ustar plbplb#!/usr/bin/env ruby system "sleep 30" ./oar-2.5.2/sources/desktop_computing/agent/test/restclienttest.rb0000644000175000017500000000030411757171206023455 0ustar plbplb#!/usr/bin/env ruby require 'rubygems' require 'rest_client' response = RestClient.get "http://192.168.56.101/oarapi/jobs/4/stagein.tgz" File.open("4.tgz", 'w') {|f| f.write(response) } ./oar-2.5.2/sources/desktop_computing/agent/test/rest_client_test.rb0000644000175000017500000000024511757171206023757 0ustar plbplb#!/usr/bin/ruby require 'rubygems' require 'rest_client' result = RestClient.get 'http://192.168.56.101/oarapi/resources/nodes/192.168.56.1/jobs.json' puts result ./oar-2.5.2/sources/desktop_computing/agent/test/pstest.rb0000644000175000017500000000017211757171206021726 0ustar plbplb#!/usr/bin/env ruby if system "ps -o pid,state -p #{ARGV[0]} 1>/dev/null 2>/dev/null" puts "sim" else puts "nao" end ./oar-2.5.2/sources/desktop_computing/agent/test/newforktest.rb0000644000175000017500000000040111757171206022752 0ustar plbplb#!/usr/bin/env ruby require 'job' job = Job.new("1", {"command"=>"sleep 60", "directory"=>"/root", "stdout_file"=>"OAR.1.stdout", "stderr_file"=>"OAR.1.stderr", "state"=>"toLaunch"}) Signal.trap("TERM") do job.kill exit end pid = job.wrap "sleep 60" ./oar-2.5.2/sources/desktop_computing/agent/test/killtest.rb0000644000175000017500000000035211757171206022237 0ustar plbplb#!/usr/bin/env ruby require 'job' job = Job.new("1", {"command"=>"sleep 60", "directory"=>"/root", "stdout_file"=>"OAR.1.stdout", "stderr_file"=>"OAR.1.stderr", "state"=>"toLaunch"}) pid = job.fork sleep 5 Process.kill("HUP", pid) ./oar-2.5.2/sources/desktop_computing/agent/test/jsontest.rb0000644000175000017500000000051211757171206022253 0ustar plbplb#!/usr/bin/env ruby require 'rubygems' require 'json' job_hash = JSON.parse('{ "1" : { "stderr_file" : "OAR.1.stderr", "stdout_file" : "OAR.1.stdout", "directory" : "/root", "command" : "date", "state" : "toLaunch" }}') job_hash.each_key do |k| system job_hash[k]["command"] end puts 'working!' ./oar-2.5.2/sources/desktop_computing/agent/test/forktest2.rb0000644000175000017500000000010211757171206022320 0ustar plbplb#!/usr/bin/env ruby puts Process.fork { exec "pwd && sleep 60" } ./oar-2.5.2/sources/desktop_computing/agent/test/forktest.rb0000644000175000017500000000076111757171206022251 0ustar plbplb#!/usr/bin/env ruby require 'job' job = Job.new("4", { "stderr_file" => "OAR.4.stderr", "stdout_file" => "OAR.4.stdout", "directory" => "/home/thiago/myjob", "command" => "sleep 90", "state" => "toLaunch" }) job2 = Job.new("1", { "stderr_file" => "OAR.4.stderr", "stdout_file" => "OAR.4.stdout", "directory" => "/home/thiago/myjob", "command" => "sleep 90", "state" => "toLaunch" }) pid = job.fork puts pid pid = job2.fork puts pid sleep 60 ./oar-2.5.2/sources/desktop_computing/agent/spec/0000755000175000017500000000000011757171206020032 5ustar plbplb./oar-2.5.2/sources/desktop_computing/agent/spec/job_spec.rb0000644000175000017500000000417711757171206022154 0ustar plbplbrequire 'job.rb' require 'client.rb' describe Job, "#new" do it "should create a new job instance" do @job = Job.new("72") @job.should_not be_nil @job.should be_instance_of(Job) end it "should get the job details from the server" do RestClient.should_receive(:get).and_return('{ "job_uid" : null, "reservation" : "None", "dependencies" : [], "state" : "toLaunch", "job_user" : "kameleon", "id" : 72, "startTime" : "1285579830", "links" : [ { "rel" : "self", "href" : "/jobs/72" }, { "rel" : "resources", "href" : "/jobs/72/resources" } ], "initial_request" : "oarsub -t desktop_computing date", "name" : null, "jobType" : "PASSIVE", "properties" : "desktop_computing = \'YES\'", "queue" : "default", "Job_Id" : 72, "walltime" : "7200", "resubmit_job_id" : 0, "types" : [ "desktop_computing" ], "array_index" : 1, "assigned_network_address" : [ "vnodg1" ], "project" : "default", "submissionTime" : "1285579829", "scheduledStart" : "1285579830", "array_id" : 72, "wanted_resources" : "-l \"{type = \'default\'}/resource_id=1,walltime=2:0:0\" ", "exit_code" : null, "command" : "date", "owner" : "kameleon", "cpuset_name" : "kameleon_72", "api_timestamp" : 1285586457, "message" : "FIFO scheduling OK", "assigned_resources" : [ "38" ], "events" : [], "launchingDirectory" : "/root" } ') @job = Job.new("72") end end describe Job, "#run" do context "there is no stagein file" do before :each do @job_resource = double(RestClient::Resource).as_null_object @job_resource.stub!(:has_stagein).and_return(false) RestClient::Resource.stub!(:new).and_return(@job_resource) @job = Job.new("1") end it "should not get the stagein" do @job_resource.should_not_receive(:get_stagein) @job.run end it "should not signalize error" do @job_resource.should_not_receive(:error) @job.run end it "should not raise exceptions" do lambda { @job.run }.should_not raise_exception end end end ./oar-2.5.2/sources/desktop_computing/agent/spec/job_set_spec.rb0000644000175000017500000000011511757171206023013 0ustar plbplbrequire 'job_set.rb' require 'job_client.rb' describe JobSet, '#run' do end ./oar-2.5.2/sources/desktop_computing/agent/spec/job_resource_spec.rb0000644000175000017500000000070511757171206024054 0ustar plbplbrequire 'rest_client' require 'job_resource.rb' describe JobResource, "#has_stagein" do context "there is a stagein" do before :each do @job = JobResource.new("75") end it "should return true" do @job.has_stagein.should be_true end end context "there is no stagein" do before :each do @job = JobResource.new(74) end it "should return true" do @job.has_stagein.should be_false end end end ./oar-2.5.2/sources/desktop_computing/agent/spec/job_client_spec.rb0000644000175000017500000000241611757171206023504 0ustar plbplbrequire 'job_client.rb' describe JobClient, "#get_jobs" do context 'there is a job to be run' do before(:each) do # Once for the agent sign up and another for the job list RestClient.should_receive(:get).with('http://192.168.56.101/oarapi/resources/nodes/vnode1/jobs.json').and_return('{ "api_timestamp" : 1285582177, "total" : 1, "links" : [ { "rel" : "self", "href" : "/resources/nodes/vnodg1/jobs.json" } ], "offset" : 0, "items" : [ { "api_timestamp" : 1285582177, "id" : 72, "links" : [ { "rel" : "self", "href" : "/jobs/72" }, { "rel" : "resources", "href" : "/jobs/72/resources" } ] } ] }') RestClient.should_receive(:get).with('http://192.168.56.101/oarapi/desktop/agents.json').and_return('{ "hostname" : "vnode1" }') @client = JobClient.new('192.168.56.101') end it "should return a empty JobSet to run" do @client.get_jobs_to_run.should be_empty end it "should return an empty JobSet to kill" do @client.get_jobs_to_kill.should be_empty end end end ./oar-2.5.2/sources/desktop_computing/agent/man/0000755000175000017500000000000011757171206017653 5ustar plbplb./oar-2.5.2/sources/desktop_computing/agent/man/man1/0000755000175000017500000000000011757171206020507 5ustar plbplb./oar-2.5.2/sources/desktop_computing/agent/man/man1/oarcache.pod0000644000175000017500000000061211757171206022757 0ustar plbplb=head1 NAME oarcache - clean stagein cache up =head1 SYNOPSIS B =head1 DESCRIPTION This command is a part of the OAR desktop computing system. =head1 COPYRIGHTS Copyright 2003-2012 LIG http://lig.imag.fr This software is licensed under the GNU General Public License Version 2 or above. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ./oar-2.5.2/sources/desktop_computing/agent/man/man1/oar-agent.pod0000644000175000017500000000052311757171206023070 0ustar plbplb=head1 NAME oaragent - OAR Desktop Computing Agent =head1 SYNOPSIS B =head1 DESCRIPTION =head1 COPYRIGHTS Copyright 2003-2012 LIG http://lig.imag.fr This software is licensed under the GNU General Public License Version 2 or above. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ./oar-2.5.2/sources/desktop_computing/agent/man/man1/oar-agent-daemon.pod0000644000175000017500000000055011757171206024331 0ustar plbplb=head1 NAME oar-agent-daemon - OAR agent daemon =head1 SYNOPSIS B =head1 DESCRIPTION oar-agent-daemon =head1 COPYRIGHTS Copyright 2003-2012 LIG http://lig.imag.fr This software is licensed under the GNU General Public License Version 2 or above. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ./oar-2.5.2/sources/desktop_computing/agent/lib/0000755000175000017500000000000011757171206017646 5ustar plbplb./oar-2.5.2/sources/desktop_computing/agent/lib/job_resource.rb0000644000175000017500000000222111757171206022651 0ustar plbplbclass JobResource attr_accessor :id def initialize(id) @id = id @remotehost = Configuration.instance.host @user = Configuration.instance.user @pass = Configuration.instance.pass @resource = RestClient::Resource.new "http://#@user:#@pass@#{@remotehost}/oarapi-priv/jobs/#{id}" end def get_command response = RestClient.get "http://#@user:#@pass@#{@remotehost}/oarapi-priv/jobs/#@id.json" job_hash = JSON.parse(response.body)['command'] end def has_stagein begin resp = RestClient.head "#{@resource.url}/stagein.tgz" return resp.code == 200 rescue return false end end def get_stagein system "wget -P #@id #{@resource.url}/stagein.tgz 1>/dev/null 2>/dev/null" end def send_stageout @resource['stageout.tgz'].post :myfile => File.new("#@id.tgz", 'rb') end def run @resource['state.json'].post({ 'state' => 'running' }.to_json, :content_type => :json) end def terminate @resource['state.json'].post({'state'=>'terminated'}.to_json, :content_type => :json) end def error @resource['state.json'].post({'state'=>'error'}.to_json, :content_type => :json) end end ./oar-2.5.2/sources/desktop_computing/agent/lib/job_execution_exception.rb0000644000175000017500000000005411757171206025105 0ustar plbplbclass JobExecutionException < Exception end ./oar-2.5.2/sources/desktop_computing/agent/lib/job.rb0000644000175000017500000000254711757171206020755 0ustar plbplbdef Process.descendant_processes(base=Process.pid) descendants = Hash.new{|ht,k| ht[k]=[k]} Hash[*`ps -eo pid,ppid`.scan(/\d+/).map{|x|x.to_i}].each{|pid,ppid| descendants[ppid] << descendants[pid] } descendants[base].flatten - [base] end class Job attr_accessor :id, :client def initialize(job_id=nil, remotehost=nil) @job = JobResource.new(job_id) end # def run @job.run system "mkdir #{@job.id}" if @job.has_stagein # Extracts and delete the tarball @job.get_stagein system "tar zxvf #{@job.id}/stagein.tgz -C ./#{@job.id} 1>/dev/null 2>/dev/null " system "rm -rf #{@job.id}stagein.tgz" end begin execute_command @job.terminate rescue @job.error end # Packs the stageout system "tar cvzf #{@job.id}.tgz #{@job.id}/ 1>/dev/null 2>/dev/null" @job.send_stageout # Clean system "rm -rf #{@job.id}.tgz" system "rm -rf #{@job.id}" end def kill children = Process.descendant_processes(@worker_pid) children.each{|pid| Process.kill("TERM", pid) } Process.kill("TERM", @worker_pid) @job.error end def fork @worker_pid = Process.fork { run } end private def execute_command if system("cd #{@job.id} && #{@job.get_command} 1>stdout 2>stderr") != true raise JobExecutionException end end end ./oar-2.5.2/sources/desktop_computing/agent/lib/daemon.rb0000644000175000017500000000011211757171206021430 0ustar plbplb#!/usr/bin/env ruby require 'daemons' Daemons.run('/usr/bin/oar-agent') ./oar-2.5.2/sources/desktop_computing/agent/lib/config.rb0000644000175000017500000000121411757171206021436 0ustar plbplbclass Configuration include Singleton attr_reader :host, :user, :pass def initialize file = File.open('/etc/oar/oar.conf') server_hostname = nil file.each_line do |line| if line =~ /OAR_REST_SERVER=(.*)/ @host = $1 elsif line =~ /DESKTOP_COMPUTING_AGENT_USERNAME=(.*)/ @user = $1 elsif line =~ /DESKTOP_COMPUTING_AGENT_PASSWORD=(.*)/ @pass = $1 end end file.close end def check raise "Please set the OAR_REST_SERVER, DESKTOP_COMPUTING_AGENT_USERNAME and DESKTOP_COMPUTING_AGENT_PASSWORD properties on /etc/oar/oar.conf" unless (@host && @user && @pass) end end ./oar-2.5.2/sources/desktop_computing/agent/lib/client.rb0000644000175000017500000000203711757171206021453 0ustar plbplbrequire 'rest_client' require 'json' require File.join(File.dirname(__FILE__), "job.rb") require File.join(File.dirname(__FILE__), "job_resource.rb") class Client attr_accessor :remotehost, :localhost def initialize @remotehost = Configuration.instance.host @localhost = get_hostname end def get_jobs_to_run jobs = get_jobs jobs.each do |item| Job.new(item['id']).fork end end def get_jobs_to_kill response = RestClient.get "http://#@remotehost/oarapi/resources/nodes/#@localhost/jobs.json?state=toKill" JSON.parse(response.body)['items'] end # Get a hostname from the server # The server will add this hostname to the resource pool def get_hostname response = RestClient.get "http://#@remotehost/oarapi/desktop/agents.json" JSON.parse(response.body)['hostname'] end # Get the jobs for the given hostname def get_jobs response = RestClient.get "http://#@remotehost/oarapi/resources/nodes/#@localhost/jobs.json?state=toLaunch" JSON.parse(response.body)['items'] end end ./oar-2.5.2/sources/desktop_computing/agent/lib/agent.rb0000755000175000017500000000053011757171206021272 0ustar plbplb#!/usr/bin/env ruby $LOAD_PATH.unshift '/usr/lib/oar/desktop_computing' require 'client' require 'singleton' require 'config' class Agent def run client = Client.new while true do client.get_jobs_to_run client.get_jobs_to_kill sleep 30 end end end Configuration.instance.check agent = Agent.new agent.run ./oar-2.5.2/sources/desktop_computing/agent/lib/agent.conf0000644000175000017500000000001711757171206021611 0ustar plbplb192.168.56.101 ./oar-2.5.2/sources/desktop_computing/agent/autotest/0000755000175000017500000000000011757171206020750 5ustar plbplb./oar-2.5.2/sources/desktop_computing/agent/autotest/discover.rb0000644000175000017500000000004411757171206023111 0ustar plbplbAutotest.add_discovery { "rspec2" } ./oar-2.5.2/sources/desktop_computing/agent/Rakefile0000644000175000017500000000033011757171206020541 0ustar plbplbrequire 'rake' require 'spec/rake/spectask' desc "Run all examples with RCov" Spec::Rake::SpecTask.new('spec') do |t| t.spec_files = FileList['spec/*'] t.rcov = true t.rcov_opts = ['--exclude','examples'] end ./oar-2.5.2/sources/core/0000755000175000017500000000000011757171206013174 5ustar plbplb./oar-2.5.2/sources/core/tools/0000755000175000017500000000000011757171206014334 5ustar plbplb./oar-2.5.2/sources/core/tools/wake_up_nodes.sh0000644000175000017500000000033211757171206017511 0ustar plbplb#!/bin/bash # Sample script for energy saving (wake-up) IPMI_HOST="admin" POWER_ON_CMD="cpower --up --quiet" NODES=`cat` for NODE in $NODES do /usr/lib/oar/oardodo/oardodo ssh $IPMI_HOST $POWER_ON_CMD $NODE done ./oar-2.5.2/sources/core/tools/update_cpuset_id.sh.in0000755000175000017500000000222411757171206020621 0ustar plbplb#!/bin/bash set -e # This script takes the ip of a node as an argument and check # the core affinity with physical cpus to update the oar cpuset # property corresponding to this node. # This script is intented to be inserted into the oarnodesetting_ssh # script that makes a node alive at boot time. OARNODESETTINGCMD=%%SBINDIR%%/oarnodesetting OARNODESCMD=%%BINDIR%%/oarnodes if [ $# != 1 ] then echo "usage: $0 " exit 1 fi CORE_MATHING=`ssh -p 6667 $1 cat /proc/cpuinfo |awk -F"\t*: *" '{if ($1 == "processor") proc=$2; if ($1 == "physical id") phys=$2; if ($1 == "core id") print phys ":" $2 ":" proc}'|sort` CURRENT_CORES=`$OARNODESCMD -Y --sql "ip='$1'"|awk -F" *: *" '{if (match($1," +core$")) core=$2; if (match($1," +cpu$")) print $2 ":" core}'|sort` if [ "`echo $CURRENT_CORES |wc -w`" != "`echo $CORE_MATHING |wc -w`" ] then echo "ERROR: Number of cores on the node mismatch with the number of cores into OAR!" exit 2 fi declare -i c=0 for i in $CORE_MATHING do let c=c+1 CORE_ID=`echo $CURRENT_CORES |cut -d" " -f$c|cut -d: -f2` PROC_ID=`echo $i|cut -d":" -f3` $OARNODESETTINGCMD -p "cpuset=$PROC_ID" --sql "core=$CORE_ID" done ./oar-2.5.2/sources/core/tools/trace_collect/0000755000175000017500000000000011757171206017137 5ustar plbplb./oar-2.5.2/sources/core/tools/trace_collect/ztracempi.c0000644000175000017500000003016711757171206021310 0ustar plbplb#define _GNU_SOURCE #define _REENTRANT #include #include #include #include #include #include #include #include #include #include #include "mpi.h" //#include #define MPI_EVENTS 11 //#define BUFFER_SIZE 100 #define TIME_INTERVAL 1 /// Data structure for the MPICall /// typedef struct reg_trace REG_TRACE; typedef struct event_stat EVENT_STATS; static int faketime=0; static int buffer=0; static char trace_dir[100]; static FILE *trace; static int mpi_program=0; struct reg_trace { time_t seconds; time_t useconds; char * call; int parameter; }; struct event_stat { char *call; long long bytes; int num; }; extern const char *__progname; //static REG_TRACE BuffTrace[BUFFER_SIZE]; static EVENT_STATS mpistat[MPI_EVENTS]; static time_t time_start_block; static time_t interval; static void writetodisk(void); static void initstats(void); static void printstats(time_t, time_t); //Declaration of MPI's Calls int ( *libMPI_Init) (int *, char ***); int ( *libMPI_Init_thread) (int *, char ***, int, int*); int ( *libMPI_Comm_size) (MPI_Comm, int *); int ( *libMPI_Comm_rank) (MPI_Comm, int *); int ( *libMPI_Barrier) (MPI_Comm); int ( *libMPI_Wait) (MPI_Request *, MPI_Status *status); int ( *libsend) (void *buf, int count, MPI_Datatype datatype,int dest, int tag,MPI_Comm comm); int ( *librecv) (void *buf, int count, MPI_Datatype datatype,int source, int tag, MPI_Comm comm, MPI_Status *status); int ( *libirecv) (void *buf, int count, MPI_Datatype datatype,int source, int tag, MPI_Comm comm, MPI_Request *request); int ( *libisend) ( void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm, MPI_Request *request ); int (* libMPI_Waitall) ( int count, MPI_Request array_of_requests[], MPI_Status array_of_statuses[] ); int (* libMPI_Bcast) ( void *buffer, int count, MPI_Datatype datatype, int root,MPI_Comm comm ); int (* libMPI_Allreduce )( void *sendbuf, void *recvbuf, int count,MPI_Datatype datatype, MPI_Op op, MPI_Comm comm ); int (* libMPI_Gather ) ( void *sendbuf, int sendcnt, MPI_Datatype sendtype, void *recvbuf, int recvcount, MPI_Datatype recvtype, int root, MPI_Comm comm ); int (* libMPI_Reduce ) ( void *sendbuf, void *recvbuf, int count, MPI_Datatype datatype, MPI_Op op, int root, MPI_Comm comm); /* static int read_conf_file(void) { //FILE *conf; //FILE *check; //############## PATH to configuration file ############################ //conf=fopen("/etc/mpitrace.conf","r"); //###################################################################### //char line[80]; //char prefix[200]; //if(conf==NULL) //{ // openlog("mpi-trace", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1); // syslog (LOG_INFO, "Can't Open Configuration file"); // syslog (LOG_INFO,"Using /tmp/ to save the traces"); // closelog(); strcpy(trace_dir,"/tmp/"); // return 0; //} // while(fgets(line, 80, conf) != NULL) //{ //sscanf (line, "%s", &trace_dir); // convert the string to a long int //printf ("%s\n", trace_dir); //} //check if we can write into the directory //sprintf(line,"%smpi-trace-write",trace_dir); check=fopen(line,"w"); if(check==NULL) { openlog("mpi-trace", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1); syslog (LOG_INFO, "The directory chosen is not writable"); syslog (LOG_INFO,"Using /tmp/ to save the traces"); strcpy(trace_dir,"/tmp/"); closelog(); return 0; } } */ static void initstats(void) { int i; for(i=0;i time_start_block+TIME_INTERVAL) //we have to print the acumulated values { printstats(time_start_block,tim.tv_sec); time_start_block=tim.tv_sec; } /* BuffTrace[buffer].seconds=tim.tv_sec; BuffTrace[buffer].useconds=tim.tv_usec; BuffTrace[buffer].call=call; BuffTrace[buffer].parameter=parameter; faketime++; buffer++; if(buffer>BUFFER_SIZE-1) { //writetodisk(); buffer=0; } */ } static void writetodisk() { //char *filename; //printf(" printing data node : %s \n",buf); int index; for(index=0;index\$Numres,"hostfile=s" => \$hostfile,"mca=s"=>\$mca); $cont=0; foreach (@ARGV) { #print $_."\n"; if( -B $_ ){ if(( -s $_)){ #print "its binary \n"; $ind=$cont; } } $cont++; } splice(@ARGV,$ind,0,"mpi_trace"); system("/usr/bin/mpirun @ARGV"); #print " @ARGV \n"; ./oar-2.5.2/sources/core/tools/trace_collect/mpitrace.conf0000644000175000017500000000005211757171206021607 0ustar plbplb/applis/ciment/stow/x86_64/trace_collect/ ./oar-2.5.2/sources/core/tools/trace_collect/mpi_trace0000755000175000017500000000014011757171206021023 0ustar plbplb#!/bin/sh export LD_PRELOAD=/usr/lib/ztracempi.so "$@" retval=$? unset LD_PRELOAD exit $retval ./oar-2.5.2/sources/core/tools/trace_collect/init.conf0000644000175000017500000000006211757171206020747 0ustar plbplb#APP_DIR=/applis/ciment/stow/x86_64/trace_collect ./oar-2.5.2/sources/core/tools/trace_collect/copytraces.sh0000755000175000017500000000516011757171206021654 0ustar plbplb#!/bin/sh #Some parameters TRACEDIR=/var/log/oar/ SERVER=edel-58 DEBUGFILE=epilogdebug.log OAR_JOB_ID=$1 OAR_KEY=1 OAR_NODE_FILE=$3 TAKTUKCMD=0 mkdir -p $TRACEDIR echo $OAR_JOB_ID >$TRACEDIR/$DEBUGFILE HOSTNAME=`hostname -a` echo "hostname : $HOSTNAME ">>$TRACEDIR/$DEBUGFILE uniq $OAR_NODE_FILE >>$TRACEDIR/$DEBUGFILE NUMNODES=`uniq $OAR_NODE_FILE | wc -l ` if [ "$NUMNODES" -lt "2" ] then echo "One resource reserved">>$TRACEDIR/$DEBUGFILE else echo "More than one resource reserved">>$TRACEDIR/$DEBUGFILE echo "Tranfering files from other nodes" >>$TRACEDIR/$DEBUGFILE for i in $(uniq $OAR_NODE_FILE) do echo "node : $i">>$TRACEDIR/$DEBUGFILE if [ "$HOSTNAME" != "$i" ] then echo "copying file : /var/log/oar/trace-$i-$OAR_JOB_ID.log" >>$TRACEDIR/$DEBUGFILE taktuk -c "/usr/bin/ssh -p 6667" -m $i broadcast get [ $TRACEDIR/trace-$i-$OAR_JOB_ID.log ] [ $TRACEDIR/ ] taktuk -c "/usr/bin/ssh -p 6667" -m $i broadcast exec [ 'for i in $(ls /tmp/trace-MPI.node-*'$OAR_JOB_ID'*); do cat $i >>/tmp/tracempi'$OAR_JOB_ID'; /usr/lib/oar/oardodo/oardodo rm $i ; done' ] taktuk -c "/usr/bin/ssh -p 6667" -m $i broadcast get [ /tmp/tracempi$OAR_JOB_ID ] [ $TRACEDIR/tracempi-$i-$OAR_JOB_ID.log ] #Erasing the trace files taktuk -c "/usr/bin/ssh -p 6667" -m $i broadcast exec [ 'rm $TRACEDIR/trace-$i-'$OAR_JOB_ID'.log ; rm /tmp/tracempi'$OAR_JOB_ID'' ] else echo "we dont tranfer to this node $i" >>$TRACEDIR/$DEBUGFILE if [ -w $TRACEDIR ] then echo "The directory is writable" >>$TRACEDIR/$DEBUGFILE else echo "The directory is not writable">>$TRACEDIR/$DEBUGFILE fi ###getting the MPI TRACE for k in $(ls /tmp/trace-MPI.node-*$OAR_JOB_ID*) do echo $k >> $TRACEDIR/$DEBUGFILE cat $k >> $TRACEDIR/tracempi-$i-$OAR_JOB_ID.log /usr/lib/oar/oardodo/oardodo rm $k done fi done fi cd $TRACEDIR for i in $(uniq $OAR_NODE_FILE) do #taktuk -m $SERVER broadcast put [ $TRACEDIR/trace-$i-$OAR_JOB_ID.log ] [ $TRACEDIR/ ] tar -uvf $TRACEDIR/trace-$OAR_JOB_ID.tar trace-$i-$OAR_JOB_ID.log tar -uvf $TRACEDIR/trace-$OAR_JOB_ID.tar tracempi-$i-$OAR_JOB_ID.log #taktuk -m $SERVER broadcast put [ $TRACEDIR/tracempi-$i-$OAR_JOB_ID.log ] [ $TRACEDIR/ ] rm trace-$i-$OAR_JOB_ID.log rm tracempi-$i-$OAR_JOB_ID.log done bzip2 $TRACEDIR/trace-$OAR_JOB_ID.tar taktuk -c "/usr/bin/ssh -p 6667" -m $SERVER broadcast put [ $TRACEDIR/trace-$OAR_JOB_ID.tar.bz2 ] [ $TRACEDIR/ ] rm $TRACEDIR/trace-$OAR_JOB_ID.tar.bz2 #ersaing the file in the node #rm $TRACEDIR/trace-$HOSTNAME-$OAR_JOB_ID.log echo "end collect process ">> $TRACEDIR/$DEBUGFILE ./oar-2.5.2/sources/core/tools/trace_collect/clustermon.pl0000755000175000017500000002222511757171206021675 0ustar plbplb#!/usr/bin/perl use Getopt::Long; use Getopt::Long; use strict; #important files and fields to capture #######/proc/pid/stat################# # field 10 number of minor faults # field 12 number of major faults # filed 22 virtual memory size (better to take this field form status file) # field 23 residen set memory size ( better to take this field form status file) ######/proc/pid/status################ #VmPeak peak virtual memory size #VmSize total program size #VmRSS size of memory portions #VmSwap size of swap usage (the number of referred swapents) #####/proc/pid/statm ########## #Field Content #1-size total program size (pages) #2-resident size of memory portions (pages) (same as VmRSS in status) #3-shared number of pages that are shared (i.e. backed by a file) #####/proc/pid/io ####### #rchar - bytes read #wchar - byres written #syscr - number of read syscalls #syscw - number of write syscalls #read_bytes - number of bytes caused by this process to read from underlying storage #write_bytes - number of bytes caused by this process to written from underlying storage #mesauring the %CPU Utilization #ps -p PID -o "pcpu" | sed '/%CPU/d' gives us the CPU % per pid #####NETWORK################ #####file /proc/PID/net/dev ##Format################ #Interface, Receive : bytes, packets, # Trasmit : bytes, packets, ############NFS FILE SYSTEM ############# #cat /proc/$pid/mountstats | grep WRITE | awk '{print $5}' bytes writes to a NFS system #cat /proc/$pid/mountstats | sed -n '/READ$/p' | awk '{print $5}' ####################################################################################################### ###File Format ############## #timestamp #OAR_JOB_ID #PID #command line #number of minor faults #number of major faults #VmPeak peak virtual memory size #VmSize total program size #VmRSS size of memory portions #VmSwap size of swap usage (the number of referred swapents) #size total program size (pages) #resident size of memory portions (pages) (same as VmRSS in status) #shared number of pages that are shared (i.e. backed by a file) #rchar - bytes read #wchar - byres written #syscr - number of read syscalls #syscw - number of write syscalls #read_bytes - number of bytes caused by this process to read from underlying storage #write_bytes - number of bytes caused by this process to written from underlying storage #%CPU Utilization #Receive : bytes #Trasmit : bytes ################################################################################################### ###### Parameters ############################### #Trace directory #CPUSET directory my @task; my $dir=""; my $conf_file="clustermon.conf"; ##### default config file my $log_file="/var/log/clustermon.log"; my $cmdline; my %params=(); my $pid; my $numtask; my $jobid; my $hostname; my $vmswap; my $vmsize; my $vmpeak; my $vmrss; my $syscr; my $charread; my $charwrite; my $interface; my $cpu; my $syscw; my @dirs; my $timestamp; my $read_bytes; my $write_bytes; my $isolated; my $recvtotal=0; my $sendtotal=0; sub usage() { print < \&cleanAndExit, 'INT', 'ABRT', 'QUIT', 'TERM'; sub cleanAndExit(){ print "Cleaning up and exiting\n"; exit(1); } sub readconf { open CONF,$conf_file or die "Cannot open configuration file \n"; %params=(); while() { chomp; my($key,$val)=split(/:/,$_); $val =~ s/^\s+//; ##getting rid of possble blank spaces at the begining of the variable $params{$key}=$val; } close(CONF); } sub writelog($) { my $message=shift(); my $timelog=time(); open (LOG, ">>$log_file"); print LOG "$timelog $message \n"; close(LOG); } sub readtask($) { my @taskread=(); my $dirtask=shift(); open TASKS, "$params{CPUSETDIR}$dirtask/tasks " or die "Cannot open file \n"; #print "/dev/cpuset/oar/$dir/tasks \n"; while ($numtask=) { chomp($numtask); #Getting ride of the OAR tasks #Testing my $jobid=(split(/_/,$dirtask))[1]; my $oartask2;my $oartask3; if( -e "/var/lib/oar/pid_of_oarexec_for_jobId_$jobid"){$oartask2=`cat /var/lib/oar/pid_of_oarexec_for_jobId_$jobid`;} if( -e "/var/lib/oar/oarsub_connections_$jobid"){chomp($oartask3=`cat /var/lib/oar/oarsub_connections_$jobid`);} my $processname=`ps -p $numtask | sed -n 2p | awk '{print \$4}'`; chomp(my $oartask1=`ps ax | grep $numtask | sed '2 d' | awk '{print \$6}'`); if($oartask1 !~ /.*oar\@notty.+/ && $oartask2 !~ $numtask && $oartask3 !~ $numtask && $processname ne "" ){push(@taskread,$numtask);} } close (TASKS); return(@taskread); } Getopt::Long::Configure("gnu_getopt"); GetOptions("f=s" =>\$conf_file); #Start #Reading configuration file readconf; #getting hostname chomp($hostname=`hostname `); # attempt to create TRACEDIR if it doesn't exist if (!-d $params{TRACEDIR}) { mkdir $params{TRACEDIR} or die "Unable to create $params{TRACEDIR}" ;} system("chown","oar","$params{TRACEDIR}"); for (;;){ # Getting directories my $temp; #### to keep the directories #### if( -d $params{CPUSETDIR}){ $temp=`ls $params{CPUSETDIR} | grep -E '[0-9]\$'`;} else { writelog( "Directory CPUSET does not exist ");} @dirs=(); foreach $dir (split(/\n/,$temp)){push(@dirs,$dir);} #print " dirs: @dirs \n"; if(@dirs) { $isolated=0; #verifying if there are more than one job in the machine if(@dirs>1){$isolated=1;} #reading PID foreach $dir (@dirs) { #print "#############################################\n"; @task=readtask($dir); #print "#############################################\n"; foreach $pid (@task){ if( -d "/proc/$pid"){ #Getting time $timestamp=time(); #getting the OAR JOB ID my $oarjobid=(split(/_/,$dir))[1]; #Open the trace file open (TRACE, ">>$params{TRACEDIR}trace-$hostname-$oarjobid.log"); #Setting permission for user oar system("chown","oar","$params{TRACEDIR}trace-$hostname-$oarjobid.log"); ## getting the cmdline $cmdline=`ps -p $pid | sed -n 2p | awk '{print \$4}'`; $cmdline =~ s/\n//; $cmdline=substr($cmdline,0,15); ###Adjusting it to a length of 15 characters ##reading /proc/pid/stat file #### my $stat=`cat /proc/$pid/stat `; my $minorfaults=(split(/ /,$stat))[9]; my $majorfaults=(split(/ /,$stat))[11]; ## reading /proc/pid/status file ### chomp($vmpeak=`cat /proc/$pid/status | sed -n '/VmPeak/p'`); $vmpeak=(split(/ */,$vmpeak))[1]; chomp($vmsize=`cat /proc/$pid/status | sed -n '/VmSize/p'`); $vmsize=(split(/ */,$vmsize))[1]; chomp($vmrss=`cat /proc/$pid/status | sed -n '/VmRSS/p'`); $vmrss=(split(/ */,$vmrss))[1]; chomp($vmswap=`cat /proc/$pid/status | sed -n '/VmSwap/p'`); my $vmswap=(split(/ */,$vmswap))[1]; if($vmswap eq ""){ $vmswap=0;} ###reading /proc/pid/statm file ## my $statm=`cat /proc/$pid/statm `; my $msize=(split(/ /,$statm))[0]; my $mresident=(split(/ /,$statm))[1]; my $mshared=(split(/ /,$statm))[2]; ###reading /proc/pid/io file ####### if(-e "/proc/$pid/io") { my $iostatus=`cat /proc/$pid/io `; chomp($charread=(split(/\n/,$iostatus))[0]); $charread=(split(/ /,$charread))[1]; chomp($charwrite=(split(/\n/,$iostatus))[1]); $charwrite=(split(/ /,$charwrite))[1]; chomp($syscr=(split(/\n/,$iostatus))[2]); $syscr=(split(/ /,$syscr))[1]; chomp($syscw=(split(/\n/,$iostatus))[3]); $syscw=(split(/ /,$syscw))[1]; chomp($read_bytes=(split(/\n/,$iostatus))[4]); $read_bytes=(split(/ /,$read_bytes))[1]; chomp($write_bytes=(split(/\n/,$iostatus))[5]); $write_bytes=(split(/ /,$write_bytes))[1]; } else{$charread=0;$charwrite=0;$syscr=0;$syscw=0;$read_bytes=0;$write_bytes=0;} ### getting the CPU % used by the process #### chomp($cpu=`ps -p $pid -o "pcpu" | sed '/CPU/d'`); ##### Network information /proc/PID/net/dev #### if (-e "/proc/$pid/net/dev") { my $network=`cat /proc/$pid/net/dev`;#####testing code my $recv; my $send; foreach $interface (split(/\n/,$network)){ if ($interface =~ /^\s+(\w+):\s*(\d+)\s+\d+\s+\d+\s+\d+\s+\d+\s+\d+\s+\d+\s+\d+\s+(\d+)/){ $recv=$2; $send=$3; } $recvtotal=sprintf("%d",$recv)+$recvtotal; $sendtotal=sprintf("%d",$send)+$sendtotal; } } else{$recvtotal=0;$sendtotal=0;} #printing information into file print TRACE "$timestamp $oarjobid $pid $cmdline $minorfaults $majorfaults $vmpeak $vmsize $vmrss $vmswap $msize $mresident $mshared $charread $charwrite $syscr $syscw $read_bytes $write_bytes $cpu $recvtotal $sendtotal \n"; if($isolated==1){print TRACE "Warning: There are more than one Job in the machine \n";} } } } } close(TRACE); sleep(60); } ./oar-2.5.2/sources/core/tools/trace_collect/clustermon.conf0000644000175000017500000000007411757171206022202 0ustar plbplbTRACEDIR: /var/lib/oar/traces/ CPUSETDIR: /dev/cpuset/oar/ ./oar-2.5.2/sources/core/tools/trace_collect/clustermon0000755000175000017500000000151711757171206021264 0ustar plbplb#!/bin/sh APP_DIR=/var/lib/oar if [ -f `dirname $0`/init.conf ] then . `dirname $0`/init.conf fi CONF="" if [ -f $APP_DIR/clustermon.conf ] then CONF="-f $APP_DIR/clustermon.conf" fi case "$1" in start) if [ -e "/var/run/oar/jobmonitor.pid" ] then echo "Monitoring is running" exit 0 fi echo "Starting Job monitoring" mkdir -p /var/run/oar $APP_DIR/clustermon.pl $CONF & echo $! >/var/run/oar/jobmonitor.pid ;; stop) echo "Stopping Job monitoring" pid=`cat /var/run/oar/jobmonitor.pid` kill $pid rm /var/run/oar/jobmonitor.pid ;; restart) echo "Stopping Job monitoring" pid=`cat /var/run/oar/jobmonitor.pid` kill $pid echo "Starting Job monitoring" $APP_DIR/clustermon.pl $CONF & echo $! >/var/run/oar/jobmonitor.pid ;; esac exit 0 ./oar-2.5.2/sources/core/tools/trace_collect/Makefile0000644000175000017500000000036711757171206020605 0ustar plbplbMPI_INCLUDE=/usr/include/mpich2/ INCLUDE = -I$(MPI_INCLUDE) all: ztracempi.c gcc -shared -ldl -fPIC ztracempi.c -o ztracempi.so -lmpi $(INCLUDE) debug: ztracempi.c gcc -shared -ldl -fPIC ztracempi.c -o ztracempi.so -lmpi $(INCLUDE) -DDEBUG ./oar-2.5.2/sources/core/tools/token_script_example.sh0000755000175000017500000000117411757171206021115 0ustar plbplb#!/bin/sh # You can use this script to define constraints upon a token licence server. # See oar.conf (SCHEDULER_TOKEN_SCRIPTS) # # $1: license servers # $2: license name # $3: Pattern to match #PATTERN='^Users of VP_SOLVER' LMUTIL=/opt/intel/lmutil LINE=$($LMUTIL lmstat -c "$1" -f "$2" | grep "$3") #echo $LINE TOTAL=$(echo $LINE | awk -F " " '{print $6}') USED=$(echo $LINE | awk -F " " '{print $11}') #echo $TOTAL #echo $USED ((FREE = $TOTAL - $USED)) echo $FREE ./oar-2.5.2/sources/core/tools/suspend_resume_manager.pl0000644000175000017500000000420111757171206021421 0ustar plbplb# This script is executed as oar # $Id$ # ARGV[0] can have two different values: # - "suspend" : then this script must perform right commands to suspend # all processes of the corresponding cpuset # - "resume" : then this script must resume alld processes previously # suspended my $Old_umask = sprintf("%lo",umask()); umask(oct("022")); my $Hash; my $tmp = ""; while (){ $tmp .= $_; } $Hash = eval($tmp); # Get the cpuset name my $Cpuset_name = $Hash->{name}; if (!defined($Cpuset_name)){ print("[suspend_resume_manager] Bad SSH hashtable transfered\n"); exit(2); } # From now, "Hash" is of the form: # $Hash = { # name => "cpuset name", # job_id => "job id", # oarexec_pid_file => "file which contains the oarexec pid" # } # # $Cpuset_name is the name of the cpuset my $oarexec_pid_file = $Hash->{oarexec_pid_file}; if ($ARGV[0] eq "suspend"){ # Suspend all processes of the cpuset if (-r "/dev/oar_cgroups/oar/$Cpuset_name/freezer.state"){ # We use the FREEZER cgroups feature if it is available system('echo FROZEN > /dev/oar_cgroups/oar/'.$Cpuset_name.'/freezer.state'); }else{ system('#set -x; PROC=0; test -e '.$oarexec_pid_file.' && PROC=$(cat '.$oarexec_pid_file.'); for p in $(cat /dev/cpuset/oar/'.$Cpuset_name.'/tasks) do if [ $PROC != $p ] then oardodo kill -SIGSTOP $p; fi done '); } }elsif ($ARGV[0] eq "resume"){ # Resume all processes of the cpuset if (-r "/dev/oar_cgroups/oar/$Cpuset_name/freezer.state"){ # We use the FREEZER cgroups feature if it is available system('echo THAWED > /dev/oar_cgroups/oar/'.$Cpuset_name.'/freezer.state'); }else{ system('PROCESSES=$(cat /dev/cpuset/oar/'.$Cpuset_name.'/tasks) oardodo kill -SIGCONT $PROCESSES '); } }else{ print("[suspend_resume_manager] Bad command line argument $ARGV[0].\n"); exit(3); } exit(0); ./oar-2.5.2/sources/core/tools/sshd_config.in0000644000175000017500000000264111757171206017155 0ustar plbplb# OAR sshd configuration file # What ports, IPs and protocols we listen for Port 6667 # Use these options to restrict which interfaces/protocols sshd will bind to #ListenAddress :: #ListenAddress 0.0.0.0 Protocol 2 # HostKeys for protocol version 2 HostKey %%OARCONFDIR%%/oar_ssh_host_rsa_key HostKey %%OARCONFDIR%%/oar_ssh_host_dsa_key #Privilege Separation is turned on for security UsePrivilegeSeparation yes # Lifetime and size of ephemeral version 1 server key KeyRegenerationInterval 3600 ServerKeyBits 768 # Logging SyslogFacility AUTH LogLevel INFO # Authentication: LoginGraceTime 600 PermitRootLogin no StrictModes yes RSAAuthentication yes PubkeyAuthentication yes #AuthorizedKeysFile %h/.ssh/authorized_keys # Don't read the user's ~/.rhosts and ~/.shosts files IgnoreRhosts yes # For this to work you will also need host keys in /etc/ssh_known_hosts RhostsRSAAuthentication no # similar for protocol version 2 HostbasedAuthentication no # To enable empty passwords, change to yes (NOT RECOMMENDED) PermitEmptyPasswords no # Change to yes to enable tunnelled clear text passwords PasswordAuthentication no # With nodes with a lot of core, 10 // connections could be too few MaxStartups 1024 X11Forwarding yes X11DisplayOffset 10 PrintMotd no PrintLastLog yes KeepAlive yes TCPKeepAlive yes AcceptEnv OAR_CPUSET OAR_JOB_USER PermitUserEnvironment yes UseLogin no AllowUsers %%OAROWNER%% XAuthLocation %%XAUTHCMDPATH%% ./oar-2.5.2/sources/core/tools/shut_down_nodes.sh0000644000175000017500000000021611757171206020071 0ustar plbplb#!/bin/bash # Sample script for energy saving (shut-down) NODES=`cat` for NODE in $NODES do ssh -p 6667 $NODE oardodo /sbin/halt -p done ./oar-2.5.2/sources/core/tools/sentinelle.pl0000755000175000017500000001501011757171206017033 0ustar plbplb#!/usr/bin/perl # $Id$ # sentinelle, version in Perl # # Description : execute a commande on several nodes with a connector like ssh or # rsh in parrallel. There is a window which limits the number of # processus at the same time; usefull in a cluster # Author : nicolas.capit@imag.fr # Copyright : 2005 - Laboratoire Informatique et Distribution (ID-IMAG) # License : GNU GPL version 2 # use Getopt::Long; use Data::Dumper; use strict; use warnings; use POSIX ":sys_wait_h"; # Try to load high precision time module my $useTime = 1; my $timeStart; my $timeEnd; unless (eval "use Time::HiRes qw(gettimeofday tv_interval);1"){ $useTime = 0; } # Print help message sub usage(){ print STDERR < \@nodes, "program|p=s" => \$command, "connector|c=s" => \$connector, "timeout|t=i" => \$timeout, "window|w=i" => \$window_size, "verbose|v" => \$verbose, "file|f=s" => \@files, "file_fd=i" => \@file_from_fd, "help|h" => \$sos ); # Treate -h or --help option if (defined($sos)){ usage(); exit(0); } # Treate -f or --file option (load node names from the file) foreach my $fileName (@files){ open(FILE, "$fileName") or die("/!\\ Can not open file $fileName.\n"); while (){ # Remove commentaries $_ =~ s/#.*$//s; if ($_ =~ m/^\s*(\S+)\s*$/m){ push(@nodes, $1); } } } foreach my $fd (@file_from_fd){ open(FILE, "<&",$fd) or die("/!\\ Can not open file descriptor $fd.\n"); while (){ # Remove commentaries $_ =~ s/#.*$//s; if ($_ =~ m/^\s*(\S+)\s*$/m){ push(@nodes, $1); } } } # Check if there is at least one node to connect to if ($#nodes < 0){ die("/!\\ No node specified (use -h option for more explanations)\n"); } # Check window size integrity if ($window_size < 1){ die("/!\\ Window size $window_size too small; minimum is 1!\n"); } select STDOUT; $| = 1; my $nbNodes = $#nodes + 1; my $index = 0; my %running_processes; my $nb_running_processes = 0; my %finished_processes; my %processDuration; # Treate finished processes sub register_wait_results($$){ my $pid = shift; my $returnCode = shift; my $exit_value = $returnCode >> 8; my $signal_num = $returnCode & 127; my $dumped_core = $returnCode & 128; if ($pid > 0){ if (defined($running_processes{$pid})){ $processDuration{$running_processes{$pid}}->{"end"} = [gettimeofday()] if ($useTime == 1); print(STDERR "[VERBOSE] Child process $pid ended : exit_value = $exit_value, signal_num = $signal_num, dumped_core = $dumped_core \n") if ($verbose); $finished_processes{$running_processes{$pid}} = [$exit_value,$signal_num,$dumped_core]; delete($running_processes{$pid}); $nb_running_processes--; } } } $timeStart = [gettimeofday()] if ($useTime == 1); # Start to launch subprocesses with the window limitation my @timeout; my $pid; while (($index <= $#nodes) or ($#timeout >= 0)){ # Check if window is full or not while((($nb_running_processes) < $window_size) and ($index <= $#nodes)){ print(STDERR "[VERBOSE] fork process for the node $nodes[$index]\n") if ($verbose); $processDuration{$index}->{"start"} = [gettimeofday()] if ($useTime == 1); $pid = fork(); if (defined($pid)){ $running_processes{$pid} = $index; $nb_running_processes++; push(@timeout, [$pid,time()+$timeout]); if ($pid == 0){ #In the child my $cmd = "$connector $nodes[$index] $command"; print(STDERR "[VERBOSE] Execute command : $cmd\n") if ($verbose); exec($cmd); } }else{ print(STDERR "/!\\ fork system call failed for node $nodes[$index].\n"); } $index++; } while(($pid = waitpid(-1, WNOHANG)) > 0) { register_wait_results($pid, $?); } my $t = 0; while(defined($timeout[$t]) and (($timeout[$t]->[1] < time()) or (!defined($running_processes{$timeout[$t]->[0]})))){ if (!defined($running_processes{$timeout[$t]->[0]})){ splice(@timeout,$t,1); }else{ if ($timeout[$t]->[1] <= time()){ kill(9,$timeout[$t]->[0]); } } $t++; } select(undef,undef,undef,0.1) if ($t == 0); } my $exit_code = 0; # Print summary for each nodes foreach my $i (keys(%finished_processes)){ my $verdict = "BAD"; if (($finished_processes{$i}->[0] == 0) && ($finished_processes{$i}->[1] == 0) && ($finished_processes{$i}->[2] == 0)){ $verdict = "GOOD"; }else{ $exit_code = 1; } print(STDERR "$nodes[$i] : $verdict ($finished_processes{$i}->[0],$finished_processes{$i}->[1],$finished_processes{$i}->[2]) "); if ($useTime == 1){ my $duration = tv_interval($processDuration{$i}->{"start"}, $processDuration{$i}->{"end"}); printf(STDERR "%.3f s",$duration); } print(STDERR "\n"); } foreach my $i (keys(%running_processes)){ print(STDERR "$nodes[$running_processes{$i}] : BAD (-1,-1,-1) -1 s process disappeared\n"); $exit_code = 1; } # Print global duration if ($useTime == 1){ $timeEnd = [gettimeofday()]; printf(STDERR "Total duration : %.3f s (%d nodes)\n", tv_interval($timeStart, $timeEnd), $nbNodes); } exit($exit_code); ./oar-2.5.2/sources/core/tools/oarsh/0000755000175000017500000000000011757171206015450 5ustar plbplb./oar-2.5.2/sources/core/tools/oarsh/oarsh_sudowrapper.sh.in0000755000175000017500000000702411757171206022166 0ustar plbplb#!/bin/bash # $Id$ # OARSH and SSH hosts REGEXs OARSH_HOSTS_INCLUDE_FILE=~/.oarsh-hosts-include OARSH_HOSTS_EXCLUDE_FILE=~/.oarsh-hosts-exclude # SSH command location SSHCMD=/usr/bin/ssh # OAR variables OARDIR=%%OARDIR%% OARSHCMD=%%OARSHCMD%% DEBUGFILE= # unset bash glob expension set -f # Parse SSH options (must be called twice, see below parse_args) # Be carefull: watch openssh ssh.c source file for new options... parse_opts() { OPTIND= while getopts "1246ab:c:e:fgi:kl:m:no:p:qstvxACD:F:I:L:MNO:PR:S:TVw:XY" OPT; do case $OPT in 1 | 2 | 4 | 6 | n | f | x | X | Y | g | P | a | A | k | t | v | V | q | M | s | T | N | C ) SSHARGS_OPT[SSHARGS_OPTCOUNT]=$OPT SSHARGS_OPTARG[$((SSHARGS_OPTCOUNT++))]="" ;; O | i | I | w | e | c | m | p | l | L | R | D | o | S | b | F ) SSHARGS_OPT[$SSHARGS_OPTCOUNT]=$OPT SSHARGS_OPTARG[$((SSHARGS_OPTCOUNT++))]=$OPTARG ;; * ) SSHARGS_ERROR=255 ;; esac done } # Parse the SSH command args # Syntax is `ssh [opts] [user@] [opts] [command]' parse_args() { unset SSHARGS_OPT unset SSHARGS_OPTARG SSHARGS_ERROR=0 SSHARGS_OPTCOUNT=0 parse_opts "$@" shift $((OPTIND-1)) SSHARGS_HOST="${1/#*@/}" [ -n "$SSHARGS_HOST" ] || SSHARGS_ERROR=255 SSHARGS_USER="${1/%$SSHARGS_HOST/}" SSHARGS_USER="${SSHARGS_USER/%@/}" shift 1 parse_opts "$@" shift $((OPTIND-1)) SSHARGS_COMMAND="$@" } # Debug function: dump parsed information dump() { for i in $(seq 0 $((SSHARGS_OPTCOUNT - 1))); do echo "SSHARGS_OPT[$i]="${SSHARGS_OPT[$i]} >> $DEBUGFILE echo "SSHARGS_OPTARG[$i]="${SSHARGS_OPTARG[$i]} >> $DEBUGFILE done cat >> $DEBUGFILE < $DEBUGFILE dump echo "${OPTS[@]}" >> $DEBUGFILE fi # Sudowrapper mechanism to call oarsh exec $OARDIR/$OARSHCMD "${OPTS[@]}" $SSHARGS_HOST "$SSHARGS_COMMAND" echo "OARSH wrapper failed." 1>&2 exit 1 ./oar-2.5.2/sources/core/tools/oarsh/oarsh_shell.in0000755000175000017500000000630011757171206020305 0ustar plbplb#!/bin/bash # $Id$ # This is the shell program used with the oarsh script. # It adds its own process in the cpuset OAR_CPUSET and launches # the shell or the script of the user OAR_JOB_USER. DEFAULT_SHELL=/bin/bash XAUTH_LOCATION="%%XAUTHCMDPATH%%" CPUSET_MOUNT_POINT="/dev/cpuset" OARDIR="%%OARDIR%%" export PATH=$OARDIR/oardodo:$PATH OLDUMASK=$(umask) umask 0022 # $1 = Name of the cpuset # $2,$3,... = PIDs to add # Add PIDS into the cpuset $1 function add_process_to_cpuset() { [ "$1" = "undef" ] && return ###################### # CPUSET assignation # ########################################################################### #CPUSET_MOUNT_POINT=$(mount -t cpuset | cut -d " " -f 3 | head -1) CPUSETFILE="$CPUSET_MOUNT_POINT/$1/tasks" if [ ! -w $CPUSETFILE ] then echo "oarsh: Cannot find cpuset file : $CPUSETFILE" 1>&2 exit 61 fi shift for p in $@ do $OARDIR/oardodo/oardodo sh -c "echo $p > $CPUSETFILE" || return 1 done ########################################################################### } if [ "$OAR_JOB_USER" = "" ] then if [ "$SSH_CLIENT" != "" ] && [ "$OAR_KEY" != "1" ] then echo "oarsh: The OAR_KEY environment variable is not defined and this seems to be a oar user connection." 1>&2 echo "oarsh: See 'Important notes' part of the 'CPUSET installation' section of the OAR documentation." 1>&2 exit 65 fi # It must be oar if [ "$OAR_CPUSET" != "" ] then add_process_to_cpuset $OAR_CPUSET $$ $PPID || exit 62 fi $OARDIR/oardodo/oardodo renice 0 $$ $PPID > /dev/null 2>&1 export SHELL=$DEFAULT_SHELL umask $OLDUMASK exec $DEFAULT_SHELL "$@" echo "oarsh: exec failed" 1>&2 exit 66 else if [ "$OAR_CPUSET" = "" ] then echo "oarsh: OAR_CPUSET variable is empty; Is your sshd right configured with 'AcceptEnv OAR_CPUSET OAR_JOB_USER' on all computing nodes?" 1>&2 exit 63 fi add_process_to_cpuset $OAR_CPUSET $$ $PPID || exit 62 #Manage display if [ -n "$DISPLAY" ] then if [ -x "$XAUTH_LOCATION" ] then $XAUTH_LOCATION -q extract - ${DISPLAY#localhost} | OARDO_BECOME_USER=${OAR_JOB_USER%_*} $OARDIR/oardodo/oardodo $XAUTH_LOCATION merge - [ "${OAR_JOB_USER%_*}" != "$OAR_JOB_USER" ] && OARDO_BECOME_USER=${OAR_JOB_USER%_*} $OARDIR/oardodo/oardodo bash --noprofile --norc -c 'chmod 660 $HOME/.Xauthority' fi fi #Change tty owner TTY=$(tty) && test -e $TTY && $OARDIR/oardodo/oardodo chown $OAR_JOB_USER:oar $TTY && $OARDIR/oardodo/oardodo chmod 660 $TTY $OARDIR/oardodo/oardodo renice 0 $$ $PPID > /dev/null 2>&1 if [ "$1" = "" ] then # Simulate initial login export OARDO_BECOME_USER=$OAR_JOB_USER umask $OLDUMASK exec $OARDIR/oardodo/oardodo #exec oardodo su - $OAR_JOB_USER echo "oarsh: exec failed" 1>&2 exit 66 else export OARDO_BECOME_USER=$OAR_JOB_USER export OARDO_USE_USER_SHELL=1 umask $OLDUMASK exec $OARDIR/oardodo/oardodo "$@" echo "oarsh: exec failed" 1>&2 exit 66 fi fi echo "oarsh: Really bad error" 1>&2 exit 67 ./oar-2.5.2/sources/core/tools/oarsh/oarsh.in0000755000175000017500000001457411757171206017132 0ustar plbplb#!/bin/bash # $Id$ # In sshd_config you must have this line : # AcceptEnv OAR_CPUSET OAR_JOB_USER OLDUMASK=$(umask) umask 0022 ############################################################################### # Default variable definitions. # If you want to change them then make it in the configuration file (oar.conf) # #echo "OAR configuration file : $OARCONFFILE" OARSH_OARSTAT_CMD= OPENSSH_CMD=/usr/bin/ssh OARSH_OPENSSH_DEFAULT_OPTIONS="-oProxyCommand=none -oPermitLocalCommand=no" CPUSET_PATH= # If you set this variable to something different from 0 then oarsh will act # like a normal ssh without CPUSET restriction OARSH_BYPASS_WHOLE_SECURITY="0" ############################################################################### source "$OARCONFFILE" || exit 2 # Add security option for X11 forwarding XAUTH_LOCATION="%%XAUTHCMDPATH%%" if [ -x "$XAUTH_LOCATION" ]; then OARSH_OPENSSH_DEFAULT_OPTIONS="$OARSH_OPENSSH_DEFAULT_OPTIONS -o XAuthLocation=$XAUTH_LOCATION" else OARSH_OPENSSH_DEFAULT_OPTIONS="$OARSH_OPENSSH_DEFAULT_OPTIONS -o XAuthLocation=/bin/true" fi [ -n "$OAR_RUNTIME_DIRECTORY" ] || OAR_RUNTIME_DIRECTORY="/tmp/oar_runtime" # Manage display if [ -n "$DISPLAY" ] then if [ -x "$XAUTH_LOCATION" ] then # first, get rid of remaining unused .Xautority.{pid} files if any... for f in $HOME/.Xauthority.*; do [ -e "/proc/${f#$HOME/.Xauthority.}" ] || rm -f $f done # set the .Xautority.{pid} file as the xauthority file. NEW_XAUTHORITY=$HOME/.Xauthority.$$ # retrieve the X cookie from the user to user oar. OARDO_BECOME_USER=${OARDO_USER%_*} oardodo bash --noprofile --norc -c "$XAUTH_LOCATION extract - ${DISPLAY/#localhost:/:}" | XAUTHORITY=$NEW_XAUTHORITY $XAUTH_LOCATION merge - 2> /dev/null export XAUTHORITY=$NEW_XAUTHORITY # ssh will push that cookie in the connection. fi fi # -0- Check OARSH_BYPASS_WHOLE_SECURITY variable # (oarsh acts like a ssh and can connect on every nodes) if [ "$OARSH_BYPASS_WHOLE_SECURITY" != "0" ]; then export OAR_CPUSET="undef" exec $OPENSSH_CMD $OARSH_OPENSSH_DEFAULT_OPTIONS -oSendEnv="OAR_CPUSET OAR_JOB_USER" "$@" echo "oarsh: Failed to connect using cpuset environement" exit 5 fi # -1- try connection using a user provided job key file for a job using the job key mechanism if [ -n "$OAR_JOB_KEY_FILE" ] then # first, get rid of remaining unused .Xautority.{pid} files if any... for f in $OAR_RUNTIME_DIRECTORY/oarsh.jobkey.*; do [ -e "/proc/${f#$OAR_RUNTIME_DIRECTORY/oarsh.jobkey.}" ] || rm -f $f done TMP_JOB_KEY_FILE=$OAR_RUNTIME_DIRECTORY/oarsh.jobkey.$$ TMPOLDUMASK=$(umask) umask 0177 OARDO_BECOME_USER=${OARDO_USER%_*} oardodo cat "$OAR_JOB_KEY_FILE" > $TMP_JOB_KEY_FILE if [ $? -ne 0 ]; then echo "oarsh: Failed to read job key: $OAR_JOB_KEY_FILE." 1>&2 rm -f $TMP_JOB_KEY_FILE exit 3 fi umask $TMPOLDUMASK umask $OLDUMASK exec $OPENSSH_CMD $OARSH_OPENSSH_DEFAULT_OPTIONS -i $TMP_JOB_KEY_FILE "$@" echo "oarsh: Failed to connect using the job key: $OAR_JOB_KEY_FILE" exit 3 fi # -2- try connection using a job key pushed by OAR for a job using the job key mechanism. # (oarsh is run from one of the node of the job) TMP_JOB_KEY_FILE="$OAR_RUNTIME_DIRECTORY/$OARDO_USER.jobkey" if [ -r $TMP_JOB_KEY_FILE ]; then umask $OLDUMASK exec $OPENSSH_CMD $OARSH_OPENSSH_DEFAULT_OPTIONS -i $TMP_JOB_KEY_FILE "$@" echo "oarsh: Failed to connect using the cpuset job key: $TMP_JOB_KEY_FILE" exit 4 fi if [ "$CPUSET_PATH" != "" ]; then if [ -r /proc/self/cpuset ]; then OAR_CPUSET=$(< /proc/self/cpuset) if [ "${OAR_CPUSET%/*}" = "$CPUSET_PATH" ] || [ "${OAR_CPUSET%/*}" = "$CPUSET_PATH/" ]; then JOB_KEY_FILE="$OAR_RUNTIME_DIRECTORY/${OAR_CPUSET##*/}.jobkey" if [ -r $JOB_KEY_FILE ]; then umask $OLDUMASK exec $OPENSSH_CMD $OARSH_OPENSSH_DEFAULT_OPTIONS -i $JOB_KEY_FILE "$@" echo "oarsh: Failed to connect using the cpuset job key: $JOB_KEY_FILE" exit 4 fi export OAR_CPUSET export OAR_JOB_USER=$OARDO_USER umask $OLDUMASK exec $OPENSSH_CMD $OARSH_OPENSSH_DEFAULT_OPTIONS -oSendEnv="OAR_CPUSET OAR_JOB_USER" "$@" echo "oarsh: Failed to connect using cpuset environement" exit 5 fi fi fi # -3- try connection using the job id information (job key mechanism not needed) if [ -n "$OAR_JOB_ID" ]; then # dirty check to insure that OAR_JOB_ID is an integer if ! [ "$OAR_JOB_ID" -gt 0 ] 2>/dev/null; then # dirty check to insure that OAR_JOB_ID is an integer echo "oarsh: Invalid job id: $OAR_JOB_ID" 1>&2 exit 5 fi if [ ! -x "$OARSH_OARSTAT_CMD" ]; then echo "oarsh: Cannot connect using job id from this host." 1>&2 exit 5 fi STR=$($OARSH_OARSTAT_CMD -fj $OAR_JOB_ID | \ while read l; do if [ "$l" != "" ]; then if [ "${l##cpuset_name*}" = "" ] ;then echo "OAR_CPUSET="${l/*cpuset_name = /} else if [ "${l##job_user*}" = "" ] ;then echo "OAR_JOB_USER="${l/*job_user = /} exit fi fi fi done) eval $STR if [ "$OAR_CPUSET" = "" ]; then echo "oarsh: Cannot retrieve the job cpuset name for job id: $OAR_JOB_ID" 1>&2 exit 5 else if [ "$OAR_CPUSET" != "$OARDO_USER"_"$OAR_JOB_ID" ]; then echo "oarsh: Permission denied, seems like job $OAR_JOB_ID is not yours." 1>&2 exit 5 fi fi # Check if we must use a tmp user id for this job if [ "$OAR_JOB_USER" = "" ]; then OAR_JOB_USER=$OARDO_USER fi if [ "$CPUSET_PATH" != "" ]; then OAR_CPUSET="$CPUSET_PATH/$OAR_CPUSET" else OAR_CPUSET="undef" fi export OAR_JOB_USER export OAR_CPUSET umask $OLDUMASK exec $OPENSSH_CMD $OARSH_OPENSSH_DEFAULT_OPTIONS -oSendEnv="OAR_CPUSET OAR_JOB_USER" "$@" echo "oarsh: Failed to connect using cpuset environement" exit 5 fi cat 1>&2 < $CHECKLOGFILE ./oar-2.5.2/sources/core/tools/oarnodecheck/oarnodecheckrun.in0000755000175000017500000000165311757171206022473 0ustar plbplb#!/bin/bash # $Id$ OARUSER=%%OAROWNER%% eval CHECKLOGDIR=~$OARUSER/checklogs CHECKSCRIPTDIR=%%OARCONFDIR%%/check.d CPUSET_DIR=/dev/cpuset/oar LOCKFILE=$CHECKSCRIPTDIR/$(basename $0).lock STAMPFILE=$CHECKSCRIPTDIR/oarnodecheckrun.lastrun if ! [ -r $CHECKLOGDIR ]; then echo 1>&2 "Error: Checklogs directory does not exist ($CHECKLOGDIR) !" exit 1 fi if ! [ -r $CHECKSCRIPTDIR ]; then echo 1>&2 "Error: Checkscripts directory does not exist ($CHECKSCRIPTDIR) !" exit 1 fi shopt -s nullglob # If there is any running job then exit. ls $CPUSET_DIR/*/tasks 2>/dev/null | grep -E "^.*_[0-9]*/tasks$" >/dev/null \ && exit 0 # If there is another instance running then exit. [ -f $LOCKFILE ] && exit 0 # Else take the lock and stamp this run. touch $LOCKFILE $STAMPFILE cd $CHECKSCRIPTDIR for f in *; do [ -x $f ] && CHECKLOGFILE=$CHECKLOGDIR/$f.$(date +%F.%T) ./$f done cd - > /dev/null # Release the lock. rm -f $LOCKFILE ./oar-2.5.2/sources/core/tools/oarnodecheck/oarnodecheckquery.in0000755000175000017500000000110111757171206023020 0ustar plbplb#!/bin/bash # $id$ OARUSER=%%OAROWNER%% eval CHECKLOGDIR=~$OARUSER/checklogs CHECKSCRIPTDIR=%%OARCONFDIR%%/check.d OARNODECHECKRUN=%%OARDIR%%/oarnodecheckrun OARDODO=%%OARDIR%%/oardodo/oardodo STAMPFILE=$CHECKSCRIPTDIR/oarnodecheckrun.lastrun DELAY=3600 # seconds shopt -s nullglob if [ -f $STAMPFILE ]; then if [ $(expr `date +"%s"` - `stat --printf="%Y" $STAMPFILE`) -ge $DELAY ]; then SUDO="" # User $OARUSER MUST be sudoer ! [ "$UID" -eq 0 ] || SUDO="$OARDODO" $SUDO $OARNODECHECKRUN || exit 1 fi fi for f in $CHECKLOGDIR/*; do exit 2 done exit 0 ./oar-2.5.2/sources/core/tools/oarnodecheck/oarnodechecklist.in0000755000175000017500000000054511757171206022641 0ustar plbplb#!/bin/bash # $Id$ OARUSER=%%OAROWNER%% eval CHECKLOGDIR=~$OARUSER/checklogs if ! [ -d $CHECKLOGDIR ]; then echo 1>&2 "Error: Checklogs directory does not exist ($CHECKLOGDIR) !" exit 1 fi echo "Checklogs list in $CHECKLOGDIR:" shopt -s nullglob cd $CHECKLOGDIR/ for t in *; do for f in *; do echo " $f" done exit 0 done echo " " exit 0 ./oar-2.5.2/sources/core/tools/oarnodecheck/README0000644000175000017500000000330211757171206017637 0ustar plbplboarnodecheck is a simple local node check mechanism working as follows: * the oarnodecheckquery script is meant to be called by OAR ping checker, to report the node health status. In OAR server's oar.conf: PINGCHECKER_TAKTUK_ARG_COMMAND="-t 3 broadcast exec [ /path/to/oarnodecheckquery ] * health status is bad as soon as any check log file exists in the checklog directory (~oar/checklogs/) * the directory of check scripts (/etc/oar/check.d/) is meant to contain admin defined scripts that perform checks with regard to possible errors * if and only if an error is detected, a checklog file must be created within the checklog directory (using the pathname defined by the CHECKLOGFILE environmenet variable) * the oarnodecheckrun must be run as root by cron (on an hourly basis for instance) to execute all scripts of the directory of check scripts * if cpuset is enabled, then oarnodecheckrun do not launch the check scripts while any job is running on the node; to avoid situations when oarnodecheckrun would never launch the check scripts, it creates a stamp file when the scripts are actually run * oarnodecheckquery checks for the existence of the oarnodecheckrun stamp file and if it is older than one hour, then it runs oarnodecheckrun before looking into the checklog directory (NB: the OAR ping checker is not called while at least one job is running on a node) * since oarnodecheckquery may run the check scripts, the OAR ping chekcer timeout must be tuned accordingly on the server side * the oarnodechecklist command lists the currently recorder check logs See the template script for a template of check script to place in the directory of check scripts (/etc/oar/check.d) ./oar-2.5.2/sources/core/tools/oarmonitor_sensor.pl0000644000175000017500000002210011757171206020446 0ustar plbplb# $Id: oarmonitor_sensor.pl 598 2007-07-05 08:13:30Z neyron $ # # This file is executed on each nodes and retrive all data for the specified # cpuset name. use Data::Dumper; my $Old_umask = sprintf("%lo",umask()); umask(oct("022")); $| = 1; my $Job_id = $ARGV[0]; my $Cpuset_name = "/dev/cpuset/"; $Cpuset_name .= $ARGV[1] if (defined($ARGV[1])); # Get names of cpus inside into the cpuset my @Cpus; if (!open(CPUS, "$Cpuset_name/cpus")){ if (!open(CPUS, "$Cpuset_name/cpuset.cpus")){ warn("ERROR: Cannot open $Cpuset_name/cpus neither $Cpuset_name/cpuset.cpus\n"); print("ERROR\n"); exit(3); } } my $str = ; chop($str); $str =~ s/\-/\.\./g; @Cpus = eval($str); close(CPUS); warn("Starting sensor on the cpuset $Cpuset_name for the job $Job_id\n"); my $cpuset_processes; my $cpus_data; my $network_interfaces; my $tic = ""; while ((-r "$Cpuset_name/tasks") and ($tic = ) and ($tic ne "STOP\n")){ chop($tic); $cpuset_processes = get_info_on_cpuset_tasks($Cpuset_name,$cpuset_processes); $cpus_data = get_info_on_cpus($cpus_data); $network_interfaces = get_network_data($network_interfaces); # print the DB table name and the values for each fields to store my $cpu_res = calculate_cpu_percentages($cpus_data,$cpuset_processes,\@Cpus); print("generic $tic network_address=$ENV{TAKTUK_HOSTNAME} type=global_cpu_percent value=$cpu_res->{ALL}->{CPUPERCENT} subtype=nb_forks subvalue=$cpu_res->{ALL}->{NEWPROCESSES}\n") if (defined($cpu_res->{ALL})); foreach my $c (keys(%{$cpu_res->{EACH}})){ print("generic $tic network_address=$ENV{TAKTUK_HOSTNAME} type=cpu value=$c subtype=cpu_percent subvalue=$cpu_res->{EACH}->{$c}->{CPUPERCENT}\n"); } print("generic $tic network_address=$ENV{TAKTUK_HOSTNAME} type=job_id value=$Job_id subtype=cpuset_vsize subvalue=$cpu_res->{CPUSET}->{VSIZE}\n") if (defined($cpu_res->{CPUSET}->{VSIZE})); print("generic $tic network_address=$ENV{TAKTUK_HOSTNAME} type=job_id value=$Job_id subtype=cpuset_cpu_percent subvalue=$cpu_res->{CPUSET}->{CPUPERCENT}\n") if (defined($cpu_res->{CPUSET}->{CPUPERCENT})); my $net_consumption = calculate_network_percentages($network_interfaces); foreach my $i (keys(%{$net_consumption})){ $i =~ /^(\D+)(\d+)$/; print("generic $tic network_address=$ENV{TAKTUK_HOSTNAME} type=network_$1 value=$2 subtype=download subvalue=$net_consumption->{$i}->{DOWN}\n"); print("generic $tic network_address=$ENV{TAKTUK_HOSTNAME} type=network_$1 value=$2 subtype=upload subvalue=$net_consumption->{$i}->{UP}\n"); } print("END\n"); # avoid to become crazy select(undef,undef,undef,0.2); } if ($tic eq "STOP\n"){ warn("Stopping sensor as requested.\n"); print("STOP_REQUESTED\n"); exit(0); }elsif (! -r "$Cpuset_name/tasks"){ warn("Stopping sensor, the cpuset $Cpuset_name does not exist anymore.\n"); print("STOP\n"); exit(1); }else{ warn("ERROR: out of the loop.\n"); print("ERROR\n"); exit(2); } ############################################################################### sub get_info_on_cpus($){ my $cpu_hash = shift(); $cpu_hash->{PREV} = $cpu_hash->{CURR} if (defined($cpu_hash->{CURR})); delete($cpu_hash->{CURR}); my $cpu_percent; my $cpu_cpuset_percent; if (open(CPU, "/proc/stat")){ my $stat_line; while ($stat_line = ){ if ($stat_line =~ /^cpu\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/){ $cpu_hash->{CURR}->{ALL}->{ALLTIME} = $1 + $2 + $3 + $4 + $5 + $6 + $7 + $8; $cpu_hash->{CURR}->{ALL}->{IDLETIME} = $4 + $5; }elsif ($stat_line =~ /^cpu(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/){ $cpu_hash->{CURR}->{EACH}->{$1}->{ALLTIME} = $2 + $3 + $4 + $5 + $6 + $7 + $8 + $9; $cpu_hash->{CURR}->{EACH}->{$1}->{IDLETIME} = $5 + $6; }elsif ($stat_line =~ /^processes\s(\d+)/){ $cpu_hash->{CURR}->{ALL}->{PROCESSES} = $1; } } close(CPU); } return($cpu_hash); } # arg1: cpuset name # arg2: previous cpuset data structure to update sub get_info_on_cpuset_tasks($$){ my $task_path = shift()."/tasks"; my $cpuset_tasks = shift(); $cpuset_tasks->{PREV} = $cpuset_tasks->{CURR} if (defined($cpuset_tasks->{CURR})); delete($cpuset_tasks->{CURR}); if (open(TASKS, "$task_path")){ my $task; while ($task = ){ chop($task); if (open(PROCESSSTAT, "/proc/$task/stat")){ my @stats = split(' ',); $cpuset_tasks->{CURR}->{$task}->{STAT} = \@stats; close(PROCESSSTAT); } } close(TASKS); } return($cpuset_tasks); } # arg1: cpus hash # arg2: cpuset hash # arg3: cpuset cpus list sub calculate_cpu_percentages($$$){ my $cpus_hash = shift; my $cpuset_hash = shift; my $cpus = shift; my $results; if ((defined($cpus_hash->{CURR}->{ALL})) and (defined($cpus_hash->{PREV}->{ALL}))){ $results->{ALL}->{CPUPERCENT} = $cpus_hash->{CURR}->{ALL}->{ALLTIME} - $cpus_hash->{PREV}->{ALL}->{ALLTIME}; if ($results->{ALL}->{CPUPERCENT} > 0){ $results->{ALL}->{CPUPERCENT} = sprintf("%.0f",(100 - (100 * ($cpus_hash->{CURR}->{ALL}->{IDLETIME} - $cpus_hash->{PREV}->{ALL}->{IDLETIME}) / $results->{ALL}->{CPUPERCENT}))); $results->{ALL}->{CPUPERCENT} = 100 if ($results->{ALL}->{CPUPERCENT} > 100); $results->{ALL}->{CPUPERCENT} = 0 if ($results->{ALL}->{CPUPERCENT} < 0); }else{ $results->{ALL}->{CPUPERCENT} = 0; } $results->{ALL}->{NEWPROCESSES} = $cpus_hash->{CURR}->{ALL}->{PROCESSES} - $cpus_hash->{PREV}->{ALL}->{PROCESSES}; } my $cumul_prev_cpus_all = 0; my $cumul_curr_cpus_all = 0; foreach my $c (@{$cpus}){ if ((defined($cpus_hash->{CURR}->{EACH}->{$c})) and (defined($cpus_hash->{PREV}->{EACH}->{$c}))){ $results->{EACH}->{$c}->{CPUPERCENT} = $cpus_hash->{CURR}->{EACH}->{$c}->{ALLTIME} - $cpus_hash->{PREV}->{EACH}->{$c}->{ALLTIME}; if ($results->{EACH}->{$c}->{CPUPERCENT} > 0){ $results->{EACH}->{$c}->{CPUPERCENT} = sprintf("%.0f",(100 - (100 * ($cpus_hash->{CURR}->{EACH}->{$c}->{IDLETIME} - $cpus_hash->{PREV}->{EACH}->{$c}->{IDLETIME}) / $results->{EACH}->{$c}->{CPUPERCENT}))); $results->{EACH}->{$c}->{CPUPERCENT} = 100 if ($results->{EACH}->{$c}->{CPUPERCENT} > 100); $results->{EACH}->{$c}->{CPUPERCENT} = 0 if ($results->{EACH}->{$c}->{CPUPERCENT} < 0); }else{ $results->{EACH}->{$c}->{CPUPERCENT} = 0; } $cumul_curr_cpus_all += $cpus_hash->{CURR}->{EACH}->{$c}->{ALLTIME}; $cumul_prev_cpus_all += $cpus_hash->{PREV}->{EACH}->{$c}->{ALLTIME}; } } # Track percentages for all processes from the cpuset my $curr_cumul_process_all_time = 0; my $prev_cumul_process_all_time = 0; foreach my $p (keys(%{$cpuset_hash->{CURR}})){ if (defined($cpuset_hash->{PREV}->{$p}->{STAT})){ # add jiffies in user and kernel mode $curr_cumul_process_all_time += $cpuset_hash->{CURR}->{$p}->{STAT}->[13] + $cpuset_hash->{CURR}->{$p}->{STAT}->[14]; $prev_cumul_process_all_time += $cpuset_hash->{PREV}->{$p}->{STAT}->[13] + $cpuset_hash->{PREV}->{$p}->{STAT}->[14]; } $results->{CPUSET}->{VSIZE} += $cpuset_hash->{CURR}->{$p}->{STAT}->[22]; } my $cpuset_cpus_all_time = $cumul_curr_cpus_all - $cumul_prev_cpus_all; if ($cpuset_cpus_all_time > 0){ $results->{CPUSET}->{CPUPERCENT} = sprintf("%.0f",(100 * ($curr_cumul_process_all_time - $prev_cumul_process_all_time) / $cpuset_cpus_all_time)); $results->{CPUSET}->{CPUPERCENT} = 100 if ($results->{CPUSET}->{CPUPERCENT} > 100); $results->{CPUSET}->{CPUPERCENT} = 0 if ($results->{CPUSET}->{CPUPERCENT} < 0); } return($results); } # Get network interfaces data sub get_network_data($){ my $network_data = shift(); $network_data->{PREV} = $network_data->{CURR} if (defined($network_data->{CURR})); delete($network_data->{CURR}); if (open(NET, "/proc/net/dev")){ while ($_ = ){ if ($_ =~ /^\s+(\w+):\s*(\d+)\s+\d+\s+\d+\s+\d+\s+\d+\s+\d+\s+\d+\s+\d+\s+(\d+)/){ $network_data->{CURR}->{$1}->{DOWN} = $2; $network_data->{CURR}->{$1}->{UP} = $3; } } close(NET); } return($network_data); } sub calculate_network_percentages($){ my $network_data = shift(); my $results; foreach my $i (keys(%{$network_data->{CURR}})){ if ($i ne "lo"){ if ((defined($network_data->{PREV}->{$i}->{DOWN})) and (defined($network_data->{PREV}->{$i}->{UP}))){ $results->{$i}->{DOWN} = $network_data->{CURR}->{$i}->{DOWN} - $network_data->{PREV}->{$i}->{DOWN}; $results->{$i}->{UP} = $network_data->{CURR}->{$i}->{UP} - $network_data->{PREV}->{$i}->{UP}; } } } return($results); } ./oar-2.5.2/sources/core/tools/oarmonitor_graph_gen.pl0000755000175000017500000000457711757171206021114 0ustar plbplb#!/usr/bin/perl # $Id$ use DBI; use strict; use warnings; use POSIX qw(strftime); use Getopt::Long; my $Db_type = "Pg"; my $Db_host = "127.0.0.1"; my $Db_name = "oar"; my $Db_user = "oar"; my $Db_pass = "oar"; Getopt::Long::Configure("gnu_getopt"); my $Job_id; my $sos; GetOptions ( "job_id|j=i" => \$Job_id, "help|h" => \$sos, ); # Display command help sub usage { print <connect("DBI:$Db_type:database=$Db_name;host=$Db_host", $Db_user, $Db_pass, {'InactiveDestroy' => 1, 'PrintError' => 1}); my $sth = $Db->prepare(" SELECT window_stop, network_address, subvalue FROM monitoring_generic WHERE type = \'job_id\' AND subtype = \'cpuset_cpu_percent\' AND value = $Job_id ORDER BY network_address "); $sth->execute(); my %files; while (my @ref = $sth->fetchrow_array()) { if (open(FILE,">> $ref[0].cpuset.dat")){ print(FILE "$ref[1] $ref[2]\n"); close(FILE); $files{"$ref[0]"} = 1; }else{ die("Cannot write $ref[0].dat\n"); } } $Db->disconnect(); print("Generating PNG files "); foreach my $f (keys(%files)){ my $date = strftime("%F %T",localtime($f)); open(CMD, "|gnuplot"); print CMD < if ARGV is empty * then run the root shell with a dash in front of the process * name (login shell) * else * ARGV is executed with root privileges * * - The user OARUSER executes this wrapper and OARDO_BECOME_USER is set * --> if ARGV is empty * then run the OARDO_BECOME_USER shell with a dash in front of * the process name (login shell) * else * if OARDO_USE_USER_SHELL is set * then execute "shell ARGV" with OARDO_BECOME_USER * privileges and the user shell * else * ARGV is executed with OARDO_BECOME_USER privileges */ /////////////////////////////////////////////////////////////////////////////// // Static conf to edit // ///////////////////////// #define OARDIR "%%OARDIR%%" #define OARCONFFILE "%%OARCONFDIR%%/oar.conf" #define OARXAUTHLOCATION "%%XAUTHCMDPATH%%" #define OARUSER "%%OAROWNER%%" /////////////////////////////////////////////////////////////////////////////// #define DEFAULTUSERTOBECOME "%%ROOTUSER%%" #define MAX_NB_SECONDARY_GROUPS 65536 #include #include #include #include #include #include #include #include // Print on stderr a string and exit with the given exit code void error(char *error_str, int exit_code){ fprintf(stderr, "[OARDODO] ERROR: %s (%s)\n", error_str, strerror(errno)); exit(exit_code); } int main(int ac, char **av){ struct passwd *passwd_initial_user_pointer; struct passwd *passwd_user_to_become_pointer; char *user_to_become; // Get user information: effective user passwd_initial_user_pointer = getpwuid(getuid()); if (passwd_initial_user_pointer == NULL){ error("Cannot get current user information", 52); } // Set right environment variables if (setenv("OARDO_USER", passwd_initial_user_pointer->pw_name, 1)){ error("Cannot change environment variable OARDO_USER", 52); } char str_tmp[256]; sprintf(str_tmp, "%i", passwd_initial_user_pointer->pw_uid); if (setenv("OARDO_UID", str_tmp, 1)){ error("Cannot change environment variable OARDO_UID", 52); } if (setenv("OARDIR", OARDIR, 1)){ error("Cannot change environment variable OARDIR", 52); } if (setenv("PERL5LIB", OARDIR, 1)){ error("Cannot change environment variable PERL5LIB", 52); } if (setenv("OARUSER", OARUSER, 1)){ error("Cannot change environment variable OARUSER", 52); } if (setenv("OARXAUTHLOCATION", OARXAUTHLOCATION, 1)){ error("Cannot change environment variable OARXAUTHLOCATION", 52); } if (setenv("OARCONFFILE", OARCONFFILE, 1)){ error("Cannot change environment variable OARCONFFILE", 52); } // Clean some environment variables if (unsetenv("IFS")){ error("Cannot unset environment variable IFS", 52); } if (unsetenv("CDPATH")){ error("Cannot unset environment variable CDPATH", 52); } if (unsetenv("MAIL")){ error("Cannot unset environment variable MAIL", 52); } if (unsetenv("LD_LIBRARY_PATH")){ error("Cannot unset environment variable LD_LIBRARY_PATH", 52); } // Check if we become root or a specific user if ((getenv("OARDO_BECOME_USER") != NULL) && (strlen(getenv("OARDO_BECOME_USER")) > 0)){ user_to_become = getenv("OARDO_BECOME_USER"); if (unsetenv("OARDO_BECOME_USER")){ error("Cannot unset environment variable OARDO_BECOME_USER", 52); } }else{ user_to_become = DEFAULTUSERTOBECOME; } // Tell OOM to kill the user processes first except for root and oar if ( (strcmp(user_to_become, "root") != 0) && (strcmp(user_to_become, OARUSER) != 0) ){ FILE *oom_file; if ((oom_file = fopen("/proc/self/oom_score_adj", "w")) != NULL){ fprintf(oom_file, "1000"); fclose(oom_file); }else{ if ((oom_file = fopen("/proc/self/oom_adj", "w")) != NULL){ fprintf(oom_file, "15"); fclose(oom_file); } } } // Change process owner passwd_user_to_become_pointer = getpwnam(user_to_become); if (passwd_user_to_become_pointer == NULL){ error("Cannot get user to become information", 52); } // Get the whole list of groups gid_t secondary_groups[MAX_NB_SECONDARY_GROUPS]; int ngroups = MAX_NB_SECONDARY_GROUPS; if (getgrouplist(user_to_become, passwd_user_to_become_pointer->pw_gid, secondary_groups, &ngroups) == -1) { error("Cannot get secondary groups", 52); } if (setgid(passwd_user_to_become_pointer->pw_gid)){ error("Cannot setgid", 52); } if (setgroups(ngroups, secondary_groups)){ error("Cannot setgroups", 52); } if (setuid(passwd_user_to_become_pointer->pw_uid)){ error("Cannot setuid", 52); } // Set new owner env variables if (setenv("USER", passwd_user_to_become_pointer->pw_name, 1)){ error("Cannot change environment variable USER", 52); } if (setenv("LOGNAME", passwd_user_to_become_pointer->pw_name, 1)){ error("Cannot change environment variable LOGNAME", 52); } if (setenv("SHELL", passwd_user_to_become_pointer->pw_shell, 1)){ error("Cannot change environment variable SHELL", 52); } if (setenv("HOME", passwd_user_to_become_pointer->pw_dir, 1)){ error("Cannot change environment variable HOME", 52); } if (ac <= 1){ // Login shell if (chdir(passwd_user_to_become_pointer->pw_dir)){ error("Cannot go to the home directory of the user to become", 52); } // First argument of the shell is - so it is a login shell execl(passwd_user_to_become_pointer->pw_shell, "-oardodo", NULL); error("Cannot run a login shell", 51); }else{ if ((getenv("OARDO_USE_USER_SHELL") != NULL) && (strlen(getenv("OARDO_USE_USER_SHELL")) > 0)){ if (unsetenv("OARDO_USE_USER_SHELL")){ error("Cannot unset environment variable OARDO_USE_USER_SHELL", 52); } if (chdir(passwd_user_to_become_pointer->pw_dir)){ error("Cannot go to the home directory of the user to become", 52); } // Execute the command with the user's shell like '/bin/sh -c' av[0] = passwd_user_to_become_pointer->pw_shell; execv(passwd_user_to_become_pointer->pw_shell, av); error("Cannot run the command with the shell of the user", 51); }else{ // Ececute the rest of the command line without passing by the // shell of the user av = &av[1]; ac--; // execvp will search through the current PATH execvp(av[0], av); error("Cannot run the command", 52); } } } ./oar-2.5.2/sources/core/tools/oardo.c0000644000175000017500000001027511757171206015611 0ustar plbplb/* * This wrapper is used to execute user commands with oar privileges. * The executable must: * chown root:root xxxxxxxx * chmod +s xxxxxxxx */ /////////////////////////////////////////////////////////////////////////////// // Static conf to edit // ///////////////////////// #define CMD_WRAPPER "TT/usr/local/oar/oarsub" #define OARDIR "TT/usr/local/oar" #define OARCONFFILE "TT/etc/oar/oar.conf" #define OARXAUTHLOCATION "TT/usr/bin/xauth" #define USERTOBECOME "TToar" #define PATH2SET "TT/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/usr/local/oar/oardodo" /////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include // Print on stderr a string and exit with the given exit code void error(char *error_str, int exit_code){ fprintf(stderr, "[OARDO] ERROR: %s (%s)\n", error_str, strerror(errno)); exit(exit_code); } int main(int ac, char **av){ struct passwd *passwd_user_pointer; struct passwd *passwd_oar_user_pointer; // Get user information: effective user and oar passwd_user_pointer = getpwuid(getuid()); if (passwd_user_pointer == NULL){ error("Cannot get current user information", 2); } passwd_oar_user_pointer = getpwnam(USERTOBECOME); if (passwd_oar_user_pointer == NULL){ error("Cannot get oar user information", 2); } // Set right environment variables if (setenv("OARDO_USER", passwd_user_pointer->pw_name, 1)){ error("Cannot change environment variable OARDO_USER", 2); } if (setenv("OARDIR", OARDIR, 1)){ error("Cannot change environment variable OARDIR", 2); } char str_tmp[256]; sprintf(str_tmp, "%i", passwd_user_pointer->pw_uid); if (setenv("OARDO_UID", str_tmp, 1)){ error("Cannot change environment variable OARDO_UID", 2); } if (setenv("PERL5LIB", OARDIR, 1)){ error("Cannot change environment variable PERL5LIB", 2); } if (setenv("RUBYLIB", OARDIR, 1)){ error("Cannot change environment variable RUBYLIB", 2); } if (setenv("OARCONFFILE", OARCONFFILE, 1)){ error("Cannot change environment variable OARCONFFILE", 2); } if (setenv("OARXAUTHLOCATION", OARXAUTHLOCATION, 1)){ error("Cannot change environment variable OARXAUTHLOCATION", 2); } // Clean some environment variables if (setenv("PATH", PATH2SET, 1)){ error("Cannot change environment variable PATH", 2); } if (unsetenv("IFS")){ error("Cannot unset environment variable IFS", 2); } if (unsetenv("CDPATH")){ error("Cannot unset environment variable CDPATH", 2); } if (unsetenv("MAIL")){ error("Cannot unset environment variable MAIL", 2); } if (unsetenv("ENV")){ error("Cannot unset environment variable ENV", 2); } if (unsetenv("BASH_ENV")){ error("Cannot unset environment variable BASH_ENV", 2); } if (unsetenv("LD_LIBRARY_PATH")){ error("Cannot unset environment variable LD_LIBRARY_PATH", 2); } // Become oar if (setgid(passwd_oar_user_pointer->pw_gid)){ error("Cannot setgid with oar gid", 2); } if (setuid(passwd_oar_user_pointer->pw_uid)){ error("Cannot setuid with oar uid", 2); } // Set environment variables to match the new oar user ownership if (setenv("USER", passwd_oar_user_pointer->pw_name, 1)){ error("Cannot change environment variable USER", 2); } if (setenv("OARUSER", passwd_oar_user_pointer->pw_name, 1)){ error("Cannot change environment variable OARUSER", 2); } if (setenv("LOGNAME", passwd_oar_user_pointer->pw_name, 1)){ error("Cannot change environment variable LOGNAME", 2); } if (setenv("SHELL", passwd_oar_user_pointer->pw_shell, 1)){ error("Cannot change environment variable SHELL", 2); } if (setenv("HOME", passwd_oar_user_pointer->pw_dir, 1)){ error("Cannot change environment variable HOME", 2); } // execute the OAR command with oar privileges execv(CMD_WRAPPER, av); error("Cannot run with oar privileges "CMD_WRAPPER, 1); } ./oar-2.5.2/sources/core/tools/oardo0000755000175000017500000000341511757171206015371 0ustar plbplb#!/usr/bin/perl # $Id$ # This wrapper is used to execute user commands with oar privileges. use English '-no_match_vars'; ############################################################################### # Static conf to edit # ####################### my $Cmd_wrapper = ""; my $Oardir = ""; my $Oarconffile = "/etc/oar/oar.conf"; my $Oarxauthlocation = "/usr/bin/xauth"; ############################################################################### $ENV{OARDO_USER} = getpwuid($UID); $ENV{OARDO_UID} = $UID; $ENV{OARDIR} = $Oardir; $ENV{PERL5LIB} = $Oardir; $ENV{RUBYLIB} = $Oardir; $ENV{OARCONFFILE} = $Oarconffile; $ENV{OARXAUTHLOCATION} = $Oarxauthlocation; my @clean_argv; # BEGIN CLEAN # Clean ARGV to prevent tainted perl behaviour for (my $i=0; $i <= $#ARGV; $i++){ my $str = ""; foreach my $l (split("\n",$ARGV[$i])){ $l =~ m/(.*)/m; $str .= $1."\n"; } chop($str); $clean_argv[$i] = $str; } #warn("@clean_argv\n"); # clean PATH $ENV{PATH} = "/bin:/sbin:/usr/bin:/usr/sbin:$Oardir/../bin:$Oardir/../sbin:$Oardir/oardodo"; delete @ENV{'IFS', 'CDPATH', 'MAIL', 'ENV','BASH_ENV','LD_LIBRARY_PATH'}; # END CLEAN undef($ERRNO); # GID = EGID $GID = $EGID; if ($ERRNO){ warn("$Cmd_wrapper: $ERRNO\n Cannot set GID to '$EGID'.\n"); exit(2); } # UID = EUID $UID = $EUID; if ($ERRNO){ warn("$Cmd_wrapper: $ERRNO\n Cannot set UID to '$EUID'.\n"); exit(2); } # Get oar user info my @tmp = getpwuid($EUID); if ($#tmp < 0){ warn("oardo: Cannot get information from uid $EUID.\n"); exit(2); } $ENV{USER} = $tmp[0]; $ENV{LOGNAME} = $tmp[0]; $ENV{SHELL} = $tmp[8]; $ENV{HOME} = $tmp[7]; $ENV{OARUSER} = $tmp[0]; exec($Cmd_wrapper,@clean_argv); warn("$Cmd_wrapper: Cannot execute with oar privileges : @clean_argv\n"); exit(1); ./oar-2.5.2/sources/core/tools/oararchive/0000755000175000017500000000000011757171206016457 5ustar plbplb./oar-2.5.2/sources/core/tools/oararchive/oararchive.conf0000644000175000017500000000307411757171206021455 0ustar plbplb# This file must have the bash variable assignment syntax ############################# # OAR Archive configuration # ############################################################################### # ## Archive dataBase hostname DB_ARCHIVE_HOSTNAME="localhost" ## Archive database base name DB_ARCHIVE_BASE_NAME="oar2-archive" ## Archive dataBase user name DB_ARCHIVE_BASE_LOGIN="oar_archive" ## Archive dataBase user password DB_ARCHIVE_BASE_PASSWD="oar_archive" # Oararchive log file ARCHIVE_LOG_FILE="/var/log/oararchive.log" # Oararchive log level: 3(debug+warnings+errors), 2(warnings+errors), 1(errors), 0(no log) ARCHIVE_LOG_LEVEL="3" ## Hash containing the list of tables to archive (separated by a comma) # Describe a couple 'table' => 'primary_key'. # Take care to onsly specify the primary_key if the type is integer. Else set 'NO_ID' # Format : ('table1' => 'primary_key_table1', 'table2' => 'NO_ID', 'table3' => 'primary_key_table3') HASH_TABLES_TO_ARCHIVE="('accounting' => 'window_start', 'admission_rules' => 'id', 'assigned_resources' => 'moldable_job_id', 'challenges' => 'job_id', 'event_logs' => 'event_id', 'event_log_hostnames' => 'event_id', 'files' => 'file_id', 'frag_jobs' => 'frag_id_job', 'jobs' => 'job_id', 'job_dependencies' => 'job_id', 'job_resource_descriptions' => 'res_job_group_id', 'job_resource_groups' => 'res_group_id', 'job_state_logs' => 'job_state_log_id', 'job_types' => 'job_type_id', 'moldable_job_descriptions' => 'moldable_id', 'queues' => 'NO_ID', 'resources' => 'resource_id', 'resource_logs' => 'resource_log_id', 'schema' => 'NO_ID')" ./oar-2.5.2/sources/core/tools/oararchive/oararchive0000755000175000017500000006742411757171206020545 0ustar plbplb#!/usr/bin/perl use strict; use warnings; use Data::Dumper; use DBI(); use Getopt::Long; use Term::ANSIColor; # To send the ANSI color-change sequences to the user's terminal use POSIX qw(strftime); use Time::HiRes qw(gettimeofday); ######-------######## ??????? ######## # # # TO FIX !!!!!!! # Le print error dans les fonctions connect_xxx ne s'affiche pas en cas d'erreur de connexion aux BDD ! # ## Path to configurations files my $oar_conf_file = "/etc/oar/oar.conf"; my $oararchive_conf_file = "/etc/oar/oararchive.conf"; ## Script version my $scriptVersion="1.0"; ## the configuration file. my $file = undef; ## parameters container... my %params; ## configuration file regexp (one line). my $regex = qr{^\s*([^#=\s]+)\s*=\s*([^#]*)}; ## Initializes dump file path my $dump_file = "/tmp/dump-oar.sql"; ### Generic variables ### my $VERBOSE = 0; # option variable for verbose mode with default value (false) my $DEBUG = 0; # option variable for debug mode with default value (false) my $QUIET = 0; # By default, display all informations my $command = `basename $0`; # base command chomp($command); ### Variables declaration ### my $backup; my $cmd; my $list_table; my $usage; my $version; my $log_level = 2; # By default, level = 2 my $log_file = "/var/log/oararchive.log"; # Sets the default file my $str; my $start_time; my $end_time; ## Checks if user is root my $uid = $>; if($uid ne 0){ $log_level = 0; # Do not write in log file error("You must be root to run \'$command\'.\n"); exit 1; } # LOAD CONFIGURATION sub init_conf($); sub get_conf ($); sub reset_conf (); # CONNECTION my $Db_type ; my $Timeout_db_connection = 0; my $Max_db_connection_timeout = 10; sub connect_oar(); sub connect_archive(); sub disconnect($); #usage if ((@ARGV < 1) || !($ARGV[0] =~ /^\d+$/)); #my $Resource = $ARGV[0]; #system("echo -e \"\nwhoami: \" `whoami`"); #system("echo -e \"\nenv: `env`\""); ## Retrieves oar database parameters reset_conf(); init_conf($oar_conf_file); my $dbType = get_conf("DB_TYPE"); my $dbHost = get_conf("DB_HOSTNAME"); my $dbName = get_conf("DB_BASE_NAME"); my $dbUserName = get_conf("DB_BASE_LOGIN"); my $dbUserPassword = get_conf("DB_BASE_PASSWD"); ## Retrieves archive database parameters reset_conf(); init_conf($oararchive_conf_file); my $dbArchiveHost = get_conf("DB_ARCHIVE_HOSTNAME"); my $dbArchiveName = get_conf("DB_ARCHIVE_BASE_NAME"); my $dbArchiveUserName = get_conf("DB_ARCHIVE_BASE_LOGIN"); my $dbArchiveUserPassword = get_conf("DB_ARCHIVE_BASE_PASSWD"); $log_level = get_conf("ARCHIVE_LOG_LEVEL"); if (!defined($log_level)){ $log_level = 2; } $log_file = get_conf("ARCHIVE_LOG_FILE"); if (!defined($log_file)){ $log_file = "/var/log/oararchive.log"; } ## Retrieves list of tables to archive with their primary key my %hash_table_to_archive = eval(get_conf("HASH_TABLES_TO_ARCHIVE")) ; ################## ## Main ################## # Parses command line option Getopt::Long::Configure ("gnu_getopt"); GetOptions (# Specific options "list-table|l" => \$list_table, # List tables that will be archived "version|V" => \$version, # Show version # Generic (classical) options "verbose|v" => \$VERBOSE, # Verbose mode "quiet|q" => \$QUIET, # Quiet mode "debug" => sub { $DEBUG = 1; $VERBOSE = 1; }, # Debug mode "help|h" => \$usage # Show help ); if (defined ($usage)) { &print_usage; exit(0); } if (defined ($version)) { &print_version; exit(0); } if (defined ($list_table)) { &print_list_table; exit(0); } &launch_archiving; exit(0); ################## ## END Main ################## ################## ## Methods ################## # connect_archive # Connects to archive database and returns the base identifier # parameters : / # return value : base # side effects : opens a connection to the archive base specified in ConfLib sub connect_archive() { # Connects to the archive database. my $dbh = undef; my $connect_attempt=0; while (!defined($dbh)){ reset_conf(); init_conf($oar_conf_file); $Db_type = get_conf("DB_TYPE"); my $log_level = get_conf("LOG_LEVEL"); reset_conf(); init_conf($oararchive_conf_file); my $host = get_conf("DB_ARCHIVE_HOSTNAME"); my $name = get_conf("DB_ARCHIVE_BASE_NAME"); my $user = get_conf("DB_ARCHIVE_BASE_LOGIN"); my $pwd = get_conf("DB_ARCHIVE_BASE_PASSWD"); #$Remote_host = get_conf("SERVER_HOSTNAME"); #$Remote_port = get_conf("SERVER_PORT"); $dbh = connect_db($host,$name,$user,$pwd,$log_level); if (!defined($dbh) && $connect_attempt>5){ error("Cannot connect to database (type=$Db_type, host=$host, user=$user, database=$name)\n"); exit 1 ; } $connect_attempt ++ ; } return($dbh); } # connect_db # Connects to database and returns the base identifier # return value : base sub connect_db($$$$$) { my $host = shift; my $name = shift; my $user = shift; my $pwd = shift; my $debug_level = shift; my $printerror = 0; if (defined($debug_level) and ($debug_level >= 3)){ $printerror = 1; } my $type; if ($Db_type eq "Pg"){ $type = "Pg"; }elsif ($Db_type eq "mysql"){ $type = "mysql"; }else{ warning("Cannot recognize DB_TYPE tag \"$Db_type\". So we are using \"mysql\" type.\n"); $type = "mysql"; $Db_type = "mysql"; } my $dbh = DBI->connect("DBI:$type:database=$name;host=$host", $user, $pwd, {'InactiveDestroy' => 1, 'PrintError' => $printerror}); if (!defined($dbh)){ warning("Cannot connect to database (type=$Db_type, host=$host, user=$user, database=$name) : $DBI::errstr\n"); if ($Timeout_db_connection < $Max_db_connection_timeout){ $Timeout_db_connection += 2; } warning("I will retry to connect to the database in $Timeout_db_connection s\n"); sleep($Timeout_db_connection); } return($dbh); } # connect_oar # Connects to database and returns the base identifier # parameters : / # return value : base # side effects : opens a connection to the base specified in ConfLib sub connect_oar() { # Connects to the OAR database. my $dbh = undef; my $connect_attempt=0; while (!defined($dbh)){ reset_conf(); init_conf($oar_conf_file); my $host = get_conf("DB_HOSTNAME"); my $name = get_conf("DB_BASE_NAME"); my $user = get_conf("DB_BASE_LOGIN"); my $pwd = get_conf("DB_BASE_PASSWD"); $Db_type = get_conf("DB_TYPE"); my $log_level = get_conf("LOG_LEVEL"); #$Remote_host = get_conf("SERVER_HOSTNAME"); #$Remote_port = get_conf("SERVER_PORT"); $dbh = connect_db($host,$name,$user,$pwd,$log_level); if (!defined($dbh) && $connect_attempt>5){ error("Cannot connect to database (type=$Db_type, host=$host, user=$user, database=$name)\n"); exit 1 ; } $connect_attempt ++ ; } return($dbh); } ## disconnect # Disconnect from database # parameters : base # return value : / # side effects : closes a previously opened connection to the specified base sub disconnect($) { my $dbh = shift; # Disconnects from the database. $dbh->disconnect(); } ## execute_maxid_query # Execute a query to obtain a MAX ID and return the result # parameters : base, request # return value : The MAX_ID sub execute_maxid_query( $ $) { my $dbh = shift; my $sql = shift; my $sth = $dbh->prepare($sql); $sth->execute(); my $ref = $sth->fetchrow_hashref(); $sth->finish(); unless ($ref->{'MAX_ID'}){ $ref->{'MAX_ID'} = 0; debug("Function execute_maxid_query. No result for this request \"$sql\". Set to 0 ref->[MAX_ID]\n"); } return $ref->{'MAX_ID'}; } ## Retrieves a parameter from the configuration file sub get_conf ( $ ) { my $key = shift; (defined $key) or die "missing a key!"; return $params{$key}; } ## get_max_id # Gets the max ID # parameters : base, table, field name # return value : max ID sub get_max_id( $ $ $ ){ my $dbh = shift; my $table = shift; my $field = shift; my $sth = $dbh->prepare("SELECT max( $field ) MAX_ID FROM $table"); $sth->execute(); my $ref = $sth->fetchrow_hashref(); $sth->finish(); unless ($ref->{'MAX_ID'}){ $ref->{'MAX_ID'} = 0; debug("Function get_max_id. No result for table \"$table\". Set to 0 ref->[MAX_ID]\n"); } return($ref->{'MAX_ID'}); } ## get_max_job_id # Gets the max job ID with state terminated or error (without others states in) # parameters : base # return value : max job ID sub get_max_job_id ( $ ) { my $dbh = shift; my $sth = $dbh->prepare(" SELECT max( job_id ) MAX_JOB_ID FROM jobs WHERE (state = \'Error\' OR state = \'Terminated\') AND job_id < ( SELECT min( job_id ) FROM jobs WHERE state IN (\'Waiting\', \'Hold\', \'toLaunch\', \'toError\', \'toAckReservation\', \'Launching\', \'Running\', \'Suspended\', \'Resuming\', \'Finishing\') )"); $sth->execute(); my $ref = $sth->fetchrow_hashref(); $sth->finish(); return($ref->{'MAX_JOB_ID'}); } ## get_number_rows # Retrieves number of rows to archive # parameters : base, query # return value : number of rows sub get_number_rows ( $ $ ) { my $dbh = shift; my $sql = shift; my $sth = $dbh->prepare($sql); $sth->execute(); return($sth->rows); } ## get_tables_list # Retrieves the tables list in specified database # parameters : base # return value : a reference on the array containing the tables list sub get_tables_list ( $ ) { my $dbh = shift; my @table_list = (); my $sth = $dbh->prepare("SHOW TABLES"); $sth->execute(); while (my @ref = $sth->fetchrow_array()) { push(@table_list,$ref[0]); } return \@table_list; } ## get_time_to_log # Returns the current date formated like : [08 Sep 11 14:47:09.829] # parameters : - # return value : the current date formated sub get_time_to_log (){ my ($seconds, $microseconds) = gettimeofday(); $microseconds = int($microseconds / 1000); $microseconds = sprintf("%03d",$microseconds); my $time = "[".sprintf("%02d",strftime("%Y",localtime($seconds)) % 100)." ".strftime("%b %d %T",localtime($seconds)).".$microseconds]"; return $time; } ## Initialization of the configuration # param: configuration file pathname # Result: 0 if conf was already loaded # 1 if conf was actually loaded # 2 if conf was not found sub init_conf ($){ # If file already loaded, exit immediately (defined $file) and return 0; $file = shift; (defined $file) or return 2; if (open( CONF, $file)){ %params = (); foreach my $line () { if ($line =~ $regex) { my ($key,$val) = ($1,$2); $val =~ /^([\"\']?)(.+)\1\s*$/; $val = $2 if ($2 ne ""); $params{$key}=$val; } } close CONF; return 1; } else{ error("Failed to open configuration file ($file)\n"); exit 1; } } ## is_exists_in_array # Returns true if the value exists in the array # parameters: Value searched, ref on the array # return value: boolean # side effects: / sub is_exists_in_array ( $ $ ){ my $value = shift; my $array = shift; my $res=0; if ( "@$array" =~ /$value/) { $res=1; } else { $res=0; } return ($res) } sub launch_archiving { $start_time = time(); write_log("\n[START] ".get_time_to_log()." Starts to archive database $dbName\n"); my $base = connect_oar(); my $base_archive = connect_archive(); my $archive_tables_list; my $dump_option; my $table_exists; my $oar_tables_list; my $sth; my $max_table_id_to_archive; my $table; my $nb_row_to_archive; my $nb_row_before_insert; my $nb_row_after_insert; my $where_clause; my @archive_existing_tables; my %oar_table_max_id; my %archive_table_max_id; ## Creates dump file and restrict access to OAR user debug("Create dump file ($dump_file)\n"); debug("chmod 600 on dump file ($dump_file)\n"); verbose("=> Create dump file and restrict access ....."); system("touch ".$dump_file); system("chmod 600 ".$dump_file); verbose("Done\n"); if ($dbType eq "Pg"){ error("This script doesn't support PostgreSQL databases.\n"); exit 1; } elsif ($dbType eq "mysql"){ ## Gets the max job ID (the max with terminated and error state strictly) my $maxJobId = get_max_job_id($base); debug("Max JOB ID = ".$maxJobId."\n"); ## Retrieves the tables list in OAR database $oar_tables_list = get_tables_list($base); ## Retrieves the tables list in archive database $archive_tables_list = get_tables_list($base_archive); ## Checks if all tables described in conf file really exist in OAR database foreach $table (keys(%hash_table_to_archive)){ if(!is_exists_in_array($table,$oar_tables_list)) { error("Table \"$table\" described in conf file doesn't exist in OAR databases.\n"); exit 1; } } ## For each table, export datas from the OAR database and insert them in the archive database foreach $table (keys(%hash_table_to_archive)){ $dump_option = ""; $table_exists = 0; $where_clause = " --where=\"1 "; $max_table_id_to_archive = 0; ## If debug mode, ventilates the output if ($DEBUG == 1) { print("\n"); } $str = "Archive table \"$table\"\n"; info($str); write_log($str); debug("Couple table/primary_key : \"$table\"->$hash_table_to_archive{$table}\n"); ## Gets the max tuple to archive according to the table ## Depends on the MAX JOB ID. SWITCH: for ($table) { /accounting/ && do { ## No direct relation with maxJobId limit. Nothing to do. last; }; /admission_rules/ && do { ## No direct relation with maxJobId limit. Nothing to do. last; }; /assigned_resources/ && do { $max_table_id_to_archive = execute_maxid_query($base, "SELECT max( $table.$hash_table_to_archive{$table} ) MAX_ID FROM $table, moldable_job_descriptions, jobs where $table.moldable_job_id = moldable_job_descriptions.moldable_id and moldable_job_descriptions.moldable_job_id = jobs.job_id and jobs.job_id <= $maxJobId"); debug("Max ID to archive in \'$table\' : ($hash_table_to_archive{$table} = $max_table_id_to_archive)\n"); $where_clause .= " AND ".$hash_table_to_archive{$table}."<=".$max_table_id_to_archive." "; last; }; /challenges/ && do { $max_table_id_to_archive = execute_maxid_query($base, "SELECT max( $table.$hash_table_to_archive{$table} ) MAX_ID FROM $table where $table.job_id <= $maxJobId"); debug("Max ID to archive in \'$table\' : ($hash_table_to_archive{$table} = $max_table_id_to_archive)\n"); $where_clause .= " AND ".$hash_table_to_archive{$table}."<=".$max_table_id_to_archive." "; last; }; /event_logs/ && do { $max_table_id_to_archive = execute_maxid_query($base, "SELECT max( $table.$hash_table_to_archive{$table} ) MAX_ID FROM $table where $table.job_id <= $maxJobId"); debug("Max ID to archive in \'$table\' : ($hash_table_to_archive{$table} = $max_table_id_to_archive)\n"); $where_clause .= " AND ".$hash_table_to_archive{$table}."<=".$max_table_id_to_archive." "; last; }; /event_log_hostnames/ && do { $max_table_id_to_archive = execute_maxid_query($base, "SELECT max( $table.$hash_table_to_archive{$table} ) MAX_ID FROM $table, event_logs where $table.event_id = event_logs.event_id and event_logs.job_id <= $maxJobId"); debug("Max ID to archive in \'$table\' : ($hash_table_to_archive{$table} = $max_table_id_to_archive)\n"); $where_clause .= " AND ".$hash_table_to_archive{$table}."<=".$max_table_id_to_archive." "; last; }; /files/ && do { $max_table_id_to_archive = execute_maxid_query($base, "SELECT max( $table.$hash_table_to_archive{$table} ) MAX_ID FROM $table, jobs where $table.file_id = jobs.file_id and jobs.job_id <= $maxJobId"); debug("Max ID to archive in \'$table\' : ($hash_table_to_archive{$table} = $max_table_id_to_archive)\n"); $where_clause .= " AND ".$hash_table_to_archive{$table}."<=".$max_table_id_to_archive." "; last; }; /frag_jobs/ && do { $max_table_id_to_archive = execute_maxid_query($base, "SELECT max( $table.$hash_table_to_archive{$table} ) MAX_ID FROM $table where $table.frag_id_job <= $maxJobId"); debug("Max ID to archive in \'$table\' : ($hash_table_to_archive{$table} = $max_table_id_to_archive)\n"); $where_clause .= " AND ".$hash_table_to_archive{$table}."<=".$max_table_id_to_archive." "; last; }; /jobs/ && do { debug("Max ID to archive in \'$table\' : ($hash_table_to_archive{$table} = $maxJobId)\n"); $where_clause .= " AND ".$hash_table_to_archive{$table}."<=".$maxJobId." "; last; }; /job_dependencies/ && do { $max_table_id_to_archive = execute_maxid_query($base, "SELECT max( $table.$hash_table_to_archive{$table} ) MAX_ID FROM $table, jobs where $table.job_id = jobs.job_id and jobs.job_id <= $maxJobId"); debug("Max ID to archive in \'$table\' : ($hash_table_to_archive{$table} = $max_table_id_to_archive)\n"); $where_clause .= " AND ".$hash_table_to_archive{$table}."<=".$max_table_id_to_archive." "; last; }; /job_resource_descriptions/ && do { $max_table_id_to_archive = execute_maxid_query($base, "SELECT max( $table.$hash_table_to_archive{$table} ) MAX_ID FROM $table, job_resource_groups, moldable_job_descriptions, jobs where $table.res_job_group_id = job_resource_groups.res_group_id and job_resource_groups.res_group_moldable_id = moldable_job_descriptions.moldable_id and moldable_job_descriptions.moldable_job_id = jobs.job_id and jobs.job_id <= $maxJobId"); debug("Max ID to archive in \'$table\' : ($hash_table_to_archive{$table} = $max_table_id_to_archive)\n"); $where_clause .= " AND ".$hash_table_to_archive{$table}."<=".$max_table_id_to_archive." "; last; }; /job_resource_groups/ && do { $max_table_id_to_archive = execute_maxid_query($base, "SELECT max( $table.$hash_table_to_archive{$table} ) MAX_ID FROM $table, moldable_job_descriptions, jobs where $table.res_group_moldable_id = moldable_job_descriptions.moldable_id and moldable_job_descriptions.moldable_job_id = jobs.job_id and jobs.job_id <= $maxJobId"); debug("Max ID to archive in \'$table\' : ($hash_table_to_archive{$table} = $max_table_id_to_archive)\n"); $where_clause .= " AND ".$hash_table_to_archive{$table}."<=".$max_table_id_to_archive." "; last; }; /job_state_logs/ && do { $max_table_id_to_archive = execute_maxid_query($base, "SELECT max( $table.$hash_table_to_archive{$table} ) MAX_ID FROM $table, jobs where $table.job_id = jobs.job_id and jobs.job_id <= $maxJobId"); debug("Max ID to archive in \'$table\' : ($hash_table_to_archive{$table} = $max_table_id_to_archive)\n"); $where_clause .= " AND ".$hash_table_to_archive{$table}."<=".$max_table_id_to_archive." "; last; }; /job_types/ && do { $max_table_id_to_archive = execute_maxid_query($base, "SELECT max( $table.$hash_table_to_archive{$table} ) MAX_ID FROM $table where $table.job_id <= $maxJobId"); debug("Max ID to archive in \'$table\' : ($hash_table_to_archive{$table} = $max_table_id_to_archive)\n"); $where_clause .= " AND ".$hash_table_to_archive{$table}."<=".$max_table_id_to_archive." "; last; }; /moldable_job_descriptions/ && do { $max_table_id_to_archive = execute_maxid_query($base, "SELECT max( $table.$hash_table_to_archive{$table} ) MAX_ID FROM $table, jobs where $table.moldable_job_id = jobs.job_id and jobs.job_id <= $maxJobId"); debug("Max ID to archive in \'$table\' : ($hash_table_to_archive{$table} = $max_table_id_to_archive)\n"); $where_clause .= " AND ".$hash_table_to_archive{$table}."<=".$max_table_id_to_archive." "; last; }; /queues/ && do { ## No direct relation with maxJobId limit. Nothing to do. last; }; /resources/ && do { ## No direct relation with maxJobId limit. Nothing to do. last; }; /resource_logs/ && do { ## No direct relation with maxJobId limit. Nothing to do. last; }; /schema/ && do { ## No direct relation with maxJobId limit. Nothing to do. last; }; error("Table \'$table\' doesn't seem be a default OAR table or it is not needed to archive this table.\n This script is not able to archive table: \'$table\'.\n Remove it in $oararchive_conf_file or contact the OAR Team\n"); exit 1; } ## Checks if table exists in the archive database $table_exists = is_exists_in_array($table,$archive_tables_list); if ($table_exists == 1) { push(@archive_existing_tables,$table); ## Retrieves number of rows before insert in archive database $nb_row_before_insert = get_number_rows ($base_archive, "SELECT 1 FROM \`$table\` "); print(">>> nb_row_before_insert in archive database: ".$nb_row_before_insert."\n"); ## If already exists, don't export table structure $dump_option .= " --add-drop-table=FALSE --no-create-info "; debug("Table \"$table\" already exists in archive database\n"); ## Retrieves the max ID of the table if the primary key is an integer field ## Here we determine the "where_clause" to specify tuples to archive if ($hash_table_to_archive{$table} ne "NO_ID"){ ## For OAR database $oar_table_max_id{$table} = get_max_id($base,$table,$hash_table_to_archive{$table}); debug("DB OAR - table \"$table\" -> max ID = $oar_table_max_id{$table}\n"); ## For archive database $archive_table_max_id{$table} = get_max_id($base_archive,$table,$hash_table_to_archive{$table}); debug("DB ARCHIVE - table \"$table\" -> max ID = $archive_table_max_id{$table}\n"); #$where_clause = " --where=\"".$hash_table_to_archive{$table}.">".$archive_table_max_id{$table}."\" "; $where_clause .= " AND ".$hash_table_to_archive{$table}.">".$archive_table_max_id{$table}." "; }else{ ## We overwrite the following tables SWITCH: for ($table) { /queues/ && do { ## Even if table exists we overwrite it $dump_option = " --add-drop-table "; debug("Overwrite table \"$table\" in archive database\n"); last; }; /schema/ && do { ## Even if table exists we overwrite it $dump_option = " --add-drop-table "; debug("Overwrite table \"$table\" in archive database\n"); last; }; error("Table \'$table\' doesn't seem be a default OAR table or it is not needed to archive this table.\n This script is not able to archive table: \'$table\'.\n Remove it in $oararchive_conf_file or contact the OAR Team\n"); exit 1; } } $where_clause .= " \""; }else{ debug("Table \"$table\" doesn't exist in archive database. I create it.\n"); $where_clause .= " \""; $nb_row_before_insert = 0; } ## Retrieves number of rows to archive my $tmpWhere = $where_clause; $tmpWhere =~ /.*--where="(.*)"/; $nb_row_to_archive = get_number_rows ($base, "SELECT 1 FROM \`$table\` where $1"); print(">>> nb_row_to_archive: ".$nb_row_to_archive."\n"); $cmd = "mysqldump ".$dump_option." ".$where_clause." --host=\'$dbHost\' --user=\'$dbUserName\' --password=\'$dbUserPassword\' $dbName $table > $dump_file"; $cmd =~ /(.*--password=').*('.*)/; debug("Dump command: ".$1."".$2."\n"); verbose(" => Dump table ".$dbName.".".$table." ..... "); system($cmd); verbose("Done\n"); if ($? != 0){ error("This command aborted: $cmd; \$?=$?, $! \n"); exit 1; } ## Imports data in the archive database $cmd = "mysql --host=\'$dbArchiveHost\' --user=\'$dbArchiveUserName\' --password=\'$dbArchiveUserPassword\' $dbArchiveName < $dump_file"; $cmd =~ /(.*--password=').*('.*)/; debug("MySQL command: ".$1."".$2."\n"); verbose(" => Import data in ".$dbArchiveName.".".$table." ..... "); system($cmd); verbose("Done\n"); if ($? != 0){ error("This command aborted: $cmd; \$?=$?, $! \n"); exit 1; } ## Retrieves number of rows after insert in archive database $nb_row_after_insert = get_number_rows ($base_archive, "SELECT 1 FROM \`$table\` "); print(">>> nb_row_after_insert in archive database: ".$nb_row_after_insert."\n"); } } ## Removes dump file $cmd = "rm -f ".$dump_file; debug("Remove command: ".$cmd."\n"); verbose("=> Remove dump file ..... "); system($cmd); verbose("Done\n"); $str = "Done.\n"; info($str); write_log("[END] ".get_time_to_log()." ".$str); disconnect($base); disconnect($base_archive); $end_time = time(); verbose("=> Total duration: ".int($end_time - $start_time)."s\n"); } ###### # Display tables that will be archived ## sub print_list_table { my $i=0; print "\nTables to archive :\n"; foreach my $table (keys(%hash_table_to_archive)){ $i++; print"\t- $table\n"; } print"$i tables will be archived\n"; exit(0); } sub print_usage { print <'; my $prefix = $_[1] ? $_[1] : '*'; print "$prefix $_[0]" unless $QUIET; } ###### # Print verbose information (i.e print only if $VERBOSE is set) # usage: verbose(text) ## sub verbose { PRINT_ERROR_THEN_EXIT( '[' . (caller(0))[3] . '] missing text argument') unless @_; print @_ if ${VERBOSE}; ## Writes in log file write_log($_[0]); } ###### # Print debug information (i.e print only if $DEBUG is set) # usage: debug(text) ## sub debug { PRINT_ERROR_THEN_EXIT( '[' . (caller(0))[3] . '] missing text argument') unless @_; info(@_, '['. color("yellow") . 'DEBUG' . color("reset") . ']') if ${DEBUG}; ## Writes in log file if ($log_level >= 3){ my $str = $_[0]; write_log("[DEBUG] ".get_time_to_log()." ".$str); } } ###### # Print error message # usage: error(text) ## sub error { PRINT_ERROR_THEN_EXIT( '[' . (caller(0))[3] . '] missing text argument') unless @_; info(@_, '['. color("red") . 'ERROR' . color("reset") . ']'); ## Writes in log file if ($log_level >= 1){ my $str = $_[0]; write_log("[ERROR] ".get_time_to_log()." ".$str); } } ###### # Print error message and exit program # usage: error(text) ## sub error_and_quit { PRINT_ERROR_THEN_EXIT( '[' . (caller(0))[3] . '] missing text argument') unless @_; info(@_, '['. color("red") . 'ERROR' . color("reset") . ']'); exit 1; } ###### # Print warning message # usage: warning(text) ## sub warning { PRINT_ERROR_THEN_EXIT( '[' . (caller(0))[3] . '] missing text argument') unless @_; info(@_, '['. color("magenta") . 'WARNING' . color("reset") . ']'); ## Writes in log file if ($log_level >= 2){ my $str = $_[0]; write_log("[WARNING] ".get_time_to_log()." ".$str); } } ## write_log # Writes output text into the log file # parameters : string to write # return value : - sub write_log( $ ) { my $str = shift; if (open(LOG,">>$log_file")){ print(LOG "$str"); close(LOG); }else{ error_and_quit("Cannot write in file $log_file\n"); } } ./oar-2.5.2/sources/core/tools/oar_phoenix.pl0000644000175000017500000001302711757171206017207 0ustar plbplb#!/usr/bin/perl # OAR Script to check and automatically reboot Suspected nodes. # This script is intended to be started periodically from cron # for example: # */10 * * * * root /usr/sbin/oar_phoenix # It needs the nodes to be configured to set up themselves # in the Alive state at boot time. # !!! Work in progress: this script needs to be improved !!! # - No locking! # - No logging! # - Timed out processes may still run! # - Hard timeout is not yet implemented: # it should trig a call to Judas to send an e-mail # to the admin. use strict; use warnings; use YAML; use OAR::IO; ################### CUSTOMIZABLE PART ####################### # File where phoenix stores the states of the broken nodes my $PHOENIX_DBFILE="/var/lib/oar/phoenix/oar_phoenix.db"; # Directory where logfiles are created in case of problems # !! NOT YET IMPLEMENTED !! my $PHOENIX_LOGDIR="/var/lib/oar/phoenix/"; # Command sent to reboot a node (first attempt) #my $PHOENIX_SOFT_REBOOTCMD="ssh -p 6667 {NODENAME} oardodo reboot"; my $PHOENIX_SOFT_REBOOTCMD="echo 'Soft reboot command for {NODENAME}: PHOENIX_SOFT_REBOOTCMD not configured'"; # Timeout for a soft rebooted node to be considered hard rebootable my $PHOENIX_SOFT_TIMEOUT=300; # Command sent to rebopot a node (seond attempt) #my $PHOENIX_HARD_REBOOTCMD="oardodo ipmitool -U USERID -P PASSW0RD -H {NODENAME}-mgt power off;sleep 2;oardodo ipmitool -U USERID -P PASSW0RD -H {NODENAME}-mgt power on"; my $PHOENIX_HARD_REBOOTCMD="echo 'Hard reboot command for {NODENAME}: PHOENIX_HARD_REBOOTCMD not configured'"; # Timeout (s) for a hard rebooted node to be considered really broken, then # an email is sent # !! NOT YET IMPLEMENTED!! my $PHOENIX_HARD_TIMEOUT=300; # Max number of simultaneous reboots (soft OR hard) my $PHOENIX_MAX_REBOOTS=10; # Timout (s) for unix commands my $PHOENIX_CMD_TIMEOUT=15; # Get the broken nodes list (SQL request to customize) my $base = OAR::IO::connect(); #my $date=OAR::IO::get_date($base); #my @db=OAR::IO::get_nodes_with_given_sql($base,"(state='Absent' AND (available_upto < $date OR available_upto = 0)) or state='Suspected'"); #my @broken_nodes=OAR::IO::get_nodes_with_given_sql($base,"state='Suspected' and network_address NOT IN (SELECT distinct(network_address) FROM resources where resource_id IN (SELECT resource_id FROM assigned_resources WHERE assigned_resource_index = 'CURRENT')) and network_address != '6po'"); my @broken_nodes=OAR::IO::get_nodes_with_given_sql($base,"state='Suspected' and network_address != '6po'"); OAR::IO::disconnect($base); ################ END OF CUSTOMIZABLE PART #################### # Send a unix command ant timeout if necessary sub send_cmd($) { my $cmd=shift; eval { open(my $LOGFILE,">>$PHOENIX_LOGDIR/oar_phoenix.log") or die "can't open logfile into $PHOENIX_LOGDIR/ for writing!: $!"; local $SIG{ALRM} = sub {die "alarm\n"}; alarm $PHOENIX_CMD_TIMEOUT; my $res = `$cmd 2>&1`; print $LOGFILE $res ."\n"; close($LOGFILE); alarm 0; }; if ($@) { die unless $@ eq "alarm\n"; return "Timed out!\n"; } } # Load the DB file sub load_db($) { my $file = shift; open(my $YAMLFILE,$file) or die "can't open db file $file for reading: $!"; return YAML::LoadFile($YAMLFILE); close($YAMLFILE); } # Export DB to file sub save_db($$) { my $file = shift; my $ref = shift; open (my $YAMLFILE,">$file") or die "can't open db file $file for writing: $!"; print $YAMLFILE YAML::Dump($ref); close($YAMLFILE); } # Init DB file sub init_db($) { my $file = shift; if (!-s $file) { my %empty_hash; save_db($file,\%empty_hash); } } # Remove nodes that are no longer broken from DB sub clean_db($$) { my $db=shift; my $broken_nodes=shift; foreach my $node (keys %$db) { if (!grep {$_ eq $node} @broken_nodes) { delete($db->{$node}); # TODO: add a log entry } } } # Get nodes to soft_reboot sub get_nodes_to_soft_reboot($$) { my $db=shift; my $broken_nodes=shift; my $nodes; my $c=0; foreach my $node (@broken_nodes) { if (!defined($db->{$node})) { $c++; push (@$nodes,$node); } last if ($c>=$PHOENIX_MAX_REBOOTS); } return $nodes; } # Get nodes to hard_reboot sub get_nodes_to_hard_reboot($$) { my $db=shift; my $broken_nodes=shift; my $nodes; my $c=0; foreach my $node (@broken_nodes) { if (defined($db->{$node})) { if (defined($db->{$node}->{"soft_reboot"})) { if (time() > $db->{$node}->{"soft_reboot"} + $PHOENIX_SOFT_TIMEOUT) { $c++; push (@$nodes,$node); } last if ($c>=$PHOENIX_MAX_REBOOTS); } } } return $nodes; } # Soft reboot nodes sub soft_reboot_nodes($$) { my $db=shift; my $nodes=shift; my $cmd; my $res; foreach my $node (@$nodes) { $cmd=$PHOENIX_SOFT_REBOOTCMD; $cmd =~ s/\{NODENAME\}/$node/g; print "Soft rebooting the broken node $node\n"; $db->{$node}={ 'soft_reboot' => time() }; send_cmd($cmd); } } # Hard reboot nodes sub hard_reboot_nodes($$) { my $db=shift; my $nodes=shift; my $cmd; my $res; foreach my $node (@$nodes) { $cmd=$PHOENIX_HARD_REBOOTCMD; $cmd =~ s/\{NODENAME\}/$node/g; print "Hard rebooting the broken node $node\n"; delete($db->{$node}); $db->{$node}={ 'hard_reboot' => time() }; $res=send_cmd($cmd); } } init_db($PHOENIX_DBFILE); my $db=load_db($PHOENIX_DBFILE); clean_db($db,@broken_nodes); my $nodes=get_nodes_to_soft_reboot($db,@broken_nodes); soft_reboot_nodes($db,$nodes); $nodes=get_nodes_to_hard_reboot($db,@broken_nodes); hard_reboot_nodes($db,$nodes); save_db($PHOENIX_DBFILE,$db); ./oar-2.5.2/sources/core/tools/oar_fix_database_indexes.sh0000755000175000017500000000563311757171206021674 0ustar plbplb#!/bin/bash # $Id$ # Script to fix a corrupted oar database, where old job_ids still refer to CURRENT data in various tables. OARCONFFILE=/etc/oar/oar.conf if [ ! -r $OARCONFFILE ]; then cat <&1 Error: OAR configuration file not found or not readable. Please run this script as the user oar or root from OAR server machine. EOF exit 1 fi . $OARCONFFILE TMPFILE=/tmp/$(basename $0).$$ if [ $DB_TYPE = "mysql" ]; then mysql -N -h$DB_HOSTNAME -u$DB_BASE_LOGIN -p$DB_BASE_PASSWD $DB_BASE_NAME < $TMPFILE #assigned_resources SELECT DISTINCT jobs.job_id FROM moldable_job_descriptions, assigned_resources, jobs WHERE moldable_job_descriptions.moldable_id = assigned_resources.moldable_job_id AND moldable_job_descriptions.moldable_job_id = jobs.job_id AND ( state = "Error" OR state = "Terminated" ) AND assigned_resources.assigned_resource_index = 'CURRENT'; #moldable_job_descriptions SELECT DISTINCT jobs.job_id FROM moldable_job_descriptions, jobs WHERE moldable_job_descriptions.moldable_job_id = jobs.job_id AND ( state = "Error" OR state = "Terminated" ) AND moldable_job_descriptions.moldable_index = 'CURRENT'; #job_types SELECT DISTINCT jobs.job_id FROM jobs, job_types WHERE jobs.job_id = job_types.job_id AND ( state = "Error" OR state = "Terminated" ) AND job_types.types_index = 'CURRENT'; #job_dependencies SELECT DISTINCT jobs.job_id FROM jobs, job_dependencies WHERE jobs.job_id = job_dependencies.job_id AND ( state = "Error" OR state = "Terminated" ) AND job_dependencies.job_dependency_index = 'CURRENT'; #job_resource_groups SELECT DISTINCT jobs.job_id FROM moldable_job_descriptions, jobs, job_resource_groups WHERE moldable_job_descriptions.moldable_job_id = jobs.job_id AND ( state = "Error" OR state = "Terminated" ) AND job_resource_groups.res_group_moldable_id = moldable_job_descriptions.moldable_id AND job_resource_groups.res_group_index = 'CURRENT'; #job_resource_descriptions SELECT DISTINCT jobs.job_id FROM moldable_job_descriptions, jobs, job_resource_groups, job_resource_descriptions WHERE moldable_job_descriptions.moldable_job_id = jobs.job_id AND job_resource_groups.res_group_moldable_id = moldable_job_descriptions.moldable_id AND job_resource_groups.res_group_id = job_resource_descriptions.res_job_group_id AND ( state = "Error" OR state = "Terminated" ) AND job_resource_descriptions.res_job_index = 'CURRENT'; EOF else echo "Postgres is not supported" exit 3 fi if [ ! -s $TMPFILE ]; then echo "Nothing to fix, everthing is ok !" exit 0 fi echo "Need to fix tables for the following jobs:" cat $TMPFILE read -p"OK [y/N]" -n 1 -s if [ "x$REPLY" != "xy" ]; then echo echo Aborting. exit 2 fi echo echo Fix in progress: export OARCONFFILE for j in $(sort -u $TMPFILE); do echo -n "$j " # oarstat -fj $j (cd /usr/lib/oar; perl -e "use OAR::IO; \$db = OAR::IO::connect(); OAR::IO::log_job(\$db,$j); OAR::IO::disconnect(\$db);") done echo echo "done" exit 0 ./oar-2.5.2/sources/core/tools/oar_checkdb.pl0000755000175000017500000000043011757171206017115 0ustar plbplb#!/usr/bin/perl # $Id$ # Check if the database connection is ok # Return exit status >0 otherwise use strict; use warnings; use DBI(); use OAR::IO; use OAR::Conf qw(init_conf dump_conf get_conf is_conf); if (defined(OAR::IO::connect_ro_one_log("log"))) {exit 0;} else{exit 1;} ./oar-2.5.2/sources/core/tools/oar.conf.in0000644000175000017500000006261711757171206016405 0ustar plbplb# This file must have the bash variable assignment syntax # $Id$ ######################### # General configuration # ############################################################################### # # Database type ("mysql" or "Pg") DB_TYPE="mysql" # DataBase hostname DB_HOSTNAME="localhost" # DataBase port DB_PORT="3306" # Database base name DB_BASE_NAME="oar" # DataBase user name DB_BASE_LOGIN="oar" # DataBase user password DB_BASE_PASSWD="" # DataBase read only user name DB_BASE_LOGIN_RO="oar_ro" # DataBase read only user password DB_BASE_PASSWD_RO="" # OAR server hostname SERVER_HOSTNAME="localhost" # OAR server port SERVER_PORT="6666" # when the user does not specify a -l option then oar use this OARSUB_DEFAULT_RESOURCES="/resource_id=1" # which real resource must be used when using the "nodes" keyword ? OARSUB_NODES_RESOURCES="network_address" # force use of job key even if --use-job-key or -k is not set. OARSUB_FORCE_JOB_KEY="no" # OAR log level: 3(debug+warnings+errors), 2(warnings+errors), 1(errors) LOG_LEVEL="2" # Log categories to display in the log file. # Ex: LOG_CATEGORIES="scheduler,main,energy" # if LOG_CATEGORIES="all" every category will be logged. LOG_CATEGORIES="all" # If you want to debug oarexec on nodes then affect 1 (only effective if DETACH_JOB_FROM_SERVER = 1) OAREXEC_DEBUG_MODE="0" # oarexec default temporary directory # This value MUST be the same in all oar.conf on all nodes of the cluster OAR_RUNTIME_DIRECTORY="%%OARHOMEDIR%%" # OAR log file LOG_FILE="%%LOGDIR%%/oar.log" # Specify where we are connected with a job of the deploy type DEPLOY_HOSTNAME="127.0.0.1" # Specify where we are connected with a job of the cosystem type COSYSTEM_HOSTNAME="127.0.0.1" # Specify the database field to use to fill the file on the first node of the # job in $OAR_NODE_FILE (default is 'network_address'). Only resources with # type=default are displayed in this file. #NODE_FILE_DB_FIELD="network_address" # Specify the database field that will be considered to fill the node file used # by the user on the first node of the job. for each different value of this # field then OAR will put 1 line in the node file (default: resource_id). #NODE_FILE_DB_FIELD_DISTINCT_VALUES="resource_id" # If you want to free a process per job on the server you can change this tag # into 1 (you must enable all nodes to connect to SERVER_PORT on the # SERVER_HOSTNAME) DETACH_JOB_FROM_SERVER="1" # If DETACH_JOB_FROM_SERVER=1 then OAR is able to launch jobs with a sliding # window to avoid the overload of the server (default is 5). # (This reduce the number of "bipbip" processes seen at a time.) # If you want to disable this sliding window feature then just put the value 0 # to this tag. #RUNNER_SLIDING_WINDOW_SIZE="5" # Command to use to connect to other nodes (default is "ssh" in the PATH) OPENSSH_CMD="/usr/bin/ssh -p 6667" # Set the timeout value for each ssh connection (default is 120) #OAR_SSH_CONNECTION_TIMEOUT="200" # When a oardel is requested on a the job then OAR will try to kill it and if # nothing respond in JOBDEL_WALLTIME seconds then the job is EXTERMINATED and # the resources turned into the Suspected state (default is 300s) #JOBDEL_WALLTIME="300" # If you have installed taktuk and want to use it to manage remote # admnistration commands then give the full command path # (with your options except "-m" and "-o"). # You don t also have to give any taktuk command. # (taktuk version must be >= 3.6) #TAKTUK_CMD="/usr/bin/taktuk -t 30 -s" ############################################################################### ######################################################################## # Pingchecker options: # # How to check if the nodes have a good health or not. This choice is # # directly linked to the Suspected state of the resources. # # By default OAR uses only "ping". it requests no configuration but it # # is not accurate about the state of the nodes and it is slow # ############################################################################### # # Set the frequency for checking Alive and Suspected resources (0 means never) FINAUD_FREQUENCY="300" # Set time after which Suspected resources become Dead (default is 0 and it # means never) #DEAD_SWITCH_TIME="600" # Set to yes if you want to check the nodes that runs jobs. (no is the default value) #CHECK_NODES_WITH_RUNNING_JOB='no' # Set to yes if you want to check the aliveness of nodes just after the end of # each jobs. #ACTIVATE_PINGCHECKER_AT_JOB_END="no" # Uncomment only one of the following PINGCHECKER configuration # sentinelle.pl # If you want to use sentinelle.pl then you must use this tag. # (sentinelle.pl is like a "for" of ssh but it adds timeout and window to # avoid overloading the server) # (sentinelle.pl is provided with OAR in the install directory) PINGCHECKER_SENTINELLE_SCRIPT_COMMAND="%%OARDIR%%/sentinelle.pl -t 5 -w 20" # Taktuk # taktuk may be used to check aliveness of nodes. # Give the arguments of the taktuk command WITHOUT format outputs # (DO NOT use "-o" option). # See TAKTUK_CMD tag in this file to specify the path of the taktuk command #PINGCHECKER_TAKTUK_ARG_COMMAND="broadcast exec timeout 5 kill 9 [ true ]" #PINGCHECKER_TAKTUK_ARG_COMMAND="broadcast exec timeout 5 kill 9 [ oarnodecheckquery ]" #PINGCHECKER_TAKTUK_ARG_COMMAND="broadcast exec timeout 5 kill 9 [ /path/on/nodes/to/my/check/script.sh ]" # fping # fping may be used instead of ping to check aliveness of nodes. # uncomment next line to use fping. Give the complete command path. #PINGCHECKER_FPING_COMMAND="/usr/bin/fping -q" # nmap # nmap may be used instead of ping to check aliveness of nodes. # uncomment next line to use nmap. Give the complete command path. # It will test to connect on the ssh port (22) #PINGCHECKER_NMAP_COMMAND="/usr/bin/nmap -p 22 -n -T5" # GENERIC command # a specific script may be used instead of ping to check aliveness of nodes. # uncomment next line and give the complete command path and its arguments. # The script must return bad nodes on STDERR (1 line for a bad node and it must # have exactly the same name that OAR has given in argument of the command) #PINGCHECKER_GENERIC_COMMAND="/path/to/command arg1 arg2" ############################################################################### ###################### # Mail configuration # ############################################################################### # # OAR information may be notified by email to the administror # set accordingly to your configuration and uncomment the next lines to # activate the feature. # (If this tag is right configured then users can use "--notify" option of oarsub # to receive mails about their jobs) #MAIL_SMTP_SERVER="smtp.serveur.com" # You can specify several recipients with a comma between each email address #MAIL_RECIPIENT="user@domain.com" #MAIL_SENDER="oar@domain.com" ############################################################################### ########### # Scripts # ############################################################################### # # Set the timeout for the prologue and epilogue execution on computing nodes #PROLOGUE_EPILOGUE_TIMEOUT="60" # Files to execute before and after each job on the first computing node # (by default nothing is executed) #PROLOGUE_EXEC_FILE="/path/to/prog" #EPILOGUE_EXEC_FILE="/path/to/prog" # Set the timeout for the prologue and epilogue execution on the OAR server #SERVER_PROLOGUE_EPILOGUE_TIMEOUT="60" # Files to execute before and after each job on the OAR server (by default # nothing is executed) #SERVER_PROLOGUE_EXEC_FILE="/path/to/prog" #SERVER_EPILOGUE_EXEC_FILE="/path/to/prog" # # File to execute just after a group of resources has been supected. It may # be used to trigger automatic actions to heal the resources. The script is # started with the list of resources put in its STDIN: resource_id followed # by a space and the network_address (one line per resource) #SUSPECTED_HEALING_EXEC_FILE="/path/to/prog" #SUSPECTED_HEALING_TIMEOUT="10" ######################## # Scheduler parameters # ############################################################################### # # Maximum of seconds used by a scheduler SCHEDULER_TIMEOUT="30" # Number of processes to use when performing scheduling calculations # (default is 1) SCHEDULER_NB_PROCESSES=4 # Time to add between each jobs (for example: time for administration tasks or # time to let computers to reboot) # minimum time is 1 second # default time is 60 seconds SCHEDULER_JOB_SECURITY_TIME="60" # Minimum time in seconds that can be considered like a hole where a job could # be scheduled in (if you have performance problems, you can try to increase # this) SCHEDULER_GANTT_HOLE_MINIMUM_TIME="300" # You can add an order preference on resources assigned by the # system(SQL ORDER syntax) SCHEDULER_RESOURCE_ORDER="scheduler_priority ASC, state_num ASC, available_upto DESC, suspended_jobs ASC, network_address DESC, resource_id ASC" # If next line is uncommented then OAR will automatically update the value of # "scheduler_priority" field corresponding to the besteffort jobs. # The syntax is field names separated by "/". The value affected to # "scheduler_priority" depends of the position of the field name. SCHEDULER_PRIORITY_HIERARCHY_ORDER="network_address/resource_id" # You can specify a type of resources that will be always assigned for each job # (for exemple: enable all jobs to be able to log on the cluster frontales) # For more information, see the FAQ #SCHEDULER_RESOURCES_ALWAYS_ASSIGNED_TYPE="frontal" # This says to the scheduler to treate resources of these types, where there is # a suspended job, like free ones. So some other jobs can be scheduled on these # resources. (list resource types separate with spaces; Default value is # nothing so no other job can be scheduled on suspended job resources) #SCHEDULER_AVAILABLE_SUSPENDED_RESOURCE_TYPE="default licence VLAN" SCHEDULER_AVAILABLE_SUSPENDED_RESOURCE_TYPE="default" # For a debug purpose, scheduler decisions can be logged into the database # Uncomment the next line in order to activate the logging mechanism #SCHEDULER_LOG_DECISIONS="yes" # Time to wait when a reservation has not got all resources that it has reserved # (some resources may have become Suspected or Absent since the job submission) # before to launch the job on the remaining resources (default is 300s) #RESERVATION_WAITING_RESOURCES_TIMEOUT="300" # Set the granularity of the OAR accounting feature (in seconds) # Used by the oaraccounting command and the # oar_sched_gantt_with_timesharing_and_fairsharing to calculate the timesharing # policy. Default is 1 day (86400s) #ACCOUNTING_WINDOW="86400" ############################## # OCAML Scheduler parameters # ############################################################################### # You can use hierarchy_extractor.rb to generate those variables #HIERARCHY_LABELS="node,cpu,core" #node="(1,16,2), (33,8,4)" #cpu="(1,8,8)" #core="(1,1,64)" ############################################## ############################################################## # Parameters available if you are using the # # oar_sched_gantt_with_timesharing_and_fairsharing scheduler # ############################################################################### # # Specify the number of job to take care at each time # Default is 30 SCHEDULER_FAIRSHARING_MAX_JOB_PER_USER=30 # Number of seconds to consider for the fairsharing # Default is 30 days #SCHEDULER_FAIRSHARING_WINDOW_SIZE=2592000 # Specify the target percentages for project names (0 if not specified) # /!\ the syntax is a perl hash table definition with project names as keys # AND EVERYTHING MUST BE ON THE SAME LINE #SCHEDULER_FAIRSHARING_PROJECT_TARGETS="{ first => 75, default => 25 }" # Specify the target percentages for users (0 if not specified) # /!\ the syntax is a perl hash table definition with project names as keys # AND EVERYTHING MUST BE ON THE SAME LINE #SCHEDULER_FAIRSHARING_USER_TARGETS="{ toto => 75, titi => 10, tutu => 15 }" # Weight given to each criteria # By default the job project name is not taken in account #SCHEDULER_FAIRSHARING_COEF_PROJECT=0 # By default, effective job duration counts twice than the asked one ("asked" = # walltime given by the user ) #SCHEDULER_FAIRSHARING_COEF_USER=2 #SCHEDULER_FAIRSHARING_COEF_USER_ASK=1 ############################################## ############################################################## # TOKEN feature # # Parameters available if you are using the # # oar_sched_gantt_with_timesharing_and_fairsharing scheduler # ############################################################################### # # With this token feature you are able to filter which jobs can be scheduled # depending on outside resources (like licence server for some proprietary # softwares) # So the users can do: # oarsub -l nodes=2 -t token:fluent=12 ./script.sh # This job will be launched only if the script corresponding to the "fluent" # token returns a value greater or equal than 12. # You can use several "-t token:..." arguments (all token constraints must be # ok) # Specify the scripts to use for each token # The scripts MUST print only 1 line with a number #SCHEDULER_TOKEN_SCRIPTS="{ fluent => '/usr/local/bin/check_fluent.sh arg1 arg2', soft2 => '/usr/local/bin/check_soft2.sh arg1' }" ############################################################################### ########################################################################### # ENERGY SAVING # # (Management of automatic wake-up and shut-down of the nodes when they # # are not used) # # You have to set up the "available_upto" property of your resources: # # available_upto=0 : to disable the wake-up and shutdown # # available_upto=1 : to disable the wake-up (but not the halt) # # available_upto=2147483647 : to disable the halt (but not the wake-up) # # available_upto=2147483646 : to enable wake-up/halt forever # # available_upto= : to enable the halt, and the wake-up until # # the date given by # # Energy saving features also consider that you have set up a mechanism # # to automatically set the nodes in the Alive state when they are booted. # # For this, you can follow the steps described here: # # http://wiki-oar.imag.fr/index.php/Configuration_tips#Start.2Fstop_of_nodes_using_ssh_keys ############################################################################### # # Parameter for the scheduler to decide when a node is idle. # Number of seconds since the last job was terminated on nodes #SCHEDULER_NODE_MANAGER_IDLE_TIME="600" # Parameter for the scheduler to decide if a node will have enough time to sleep. # Number of seconds before the next job #SCHEDULER_NODE_MANAGER_SLEEP_TIME="600" # Parameter for the scheduler to know when a node has to be woken up before the # beginning of the job when a reservation is scheduled on a resource on this node # Number of seconds for a node to wake up #SCHEDULER_NODE_MANAGER_WAKEUP_TIME="600" # When OAR scheduler wants some nodes to wake up then it launches this command # and puts on its STDIN the list of nodes to wake up (one hostname by line). # !! This variable is ignored if you set ENERGY_SAVING_INTERNAL to yes. !! # The scheduler looks at the available_upto field in the resources table to know # if the node will be started for enough time. # There's no nodes management with this method: if you want nodes to be suspected # when they do not wake up in time, then you have to use ENERGY_SAVING_INTERNAL=yes # and set up ENERGY_SAVING_NODE_MANAGER_WAKE_UP_CMD. #SCHEDULER_NODE_MANAGER_WAKE_UP_CMD="%%OARCONFDIR%%/wake_up_nodes.sh" # When OAR considers that some nodes can be shut down, it launches this command # and puts the node list on its STDIN (one hostname by line). # !! This variable is ignored if you set ENERGY_SAVING_INTERNAL to yes. !! # There's no nodes management with this method: if you want some nodes to be kept # alive to be reactive to small jobs, then you have to use ENERGY_SAVING_INTERNAL=yes # and set up ENERGY_SAVING_NODE_MANAGER_SLEEP_CMD. #SCHEDULER_NODE_MANAGER_SLEEP_CMD="/path/to/the/command args" #SCHEDULER_NODE_MANAGER_SLEEP_CMD="taktuk -s -f - -t 3 b e t 3 k 9 [ oardodo halt ]" #SCHEDULER_NODE_MANAGER_SLEEP_CMD="%%OARDIR%%/sentinelle.pl -f - -t 3 -p 'oardodo halt'" # Choose wether to use the internal energy saving module or not. If set to yes, # please, also provide convenient configuration for all the ENERGY_* variables. # If set to no, then you have to set up SCHEDULER_NODE_MANAGER_WAKE_UP_CMD # and SCHEDULER_NODE_MANAGER_SLEEP_CMD # Benefits of this module are: # - nodes are suspected if they do not wake up before a timeout # - some nodes can be kept always alive depending on some properties # - the launching of wakeup/shutdown commands can be windowized to prevent # from electric peeks # Possible values are "yes" and "no" ENERGY_SAVING_INTERNAL="no" # Path to the script used by the energy saving module to wake up nodes. # This command is executed from the oar server host. # OAR puts the node list on its STDIN (one hostname by line). # The scheduler looks at the available_upto field in the resources table to know # if the node will be started for enough time. #ENERGY_SAVING_NODE_MANAGER_WAKE_UP_CMD="%%OARCONFDIR%%/wake_up_nodes.sh" # Path to the script used by the energy saving module to shut down nodes. # This command is executed from the oar server host. # OAR puts the node list on its STDIN (one hostname by line). #ENERGY_SAVING_NODE_MANAGER_SLEEP_CMD="%%OARCONFDIR%%/shut_down_nodes.sh" # Timeout to consider a node broken (suspected) if it has not woken up # The value can be an integer of seconds or a set of pairs. # For example, "1:500 11:1000 21:2000" will produce a timeout of 500 # seconds if 1 to 10 nodes have to wakeup, 1000 seconds if 11 t 20 nodes # have to wake up and 2000 seconds otherwise. #ENERGY_SAVING_NODE_MANAGER_WAKEUP_TIMEOUT="900" # You can set up a number of nodes that must always be on. You can use the # syntax in the examples if you want a number of alive nodes of different types # (corresponding to a specific sql properties requierement). # Example 1: keep alive 10 nodes on the whole cluster: #ENERGY_SAVING_NODES_KEEPALIVE="type='default':10" # Example 2: keep alive 4 nodes on the paradent cluster AND 6 nodes on the # paraquad cluster AND 2 nodes accepting besteffort #ENERGY_SAVING_NODES_KEEPALIVE="cluster='paradent':4 & cluster='paraquad':6 & besteffort='YES':2" # By default, keepalive is disabled: #ENERGY_SAVING_NODES_KEEPALIVE="type='default':0" # Parameter for the window launching mechanism embedded in OAR energy saving module # to know the number of commands that can be executed in parallel. # This mechanism is used in order to sleep and wake up nodes gradually. # Window size minimum is 1 #ENERGY_SAVING_WINDOW_SIZE="25" # Parameter to bypas the window mechanism embedded in the energy saving module. # Possible values are "yes" and "no" # When set to "yes", the list of nodes to wake up or shut down is passed to # ENERGY_SAVING_NODE_MANAGER_*_CMD through stdin. #ENERGY_SAVING_WINDOW_FORKER_BYPASS="no" # Time in second between execution of each window. # Minimum is 0 to set no delay between each window. # This value must be smaller than ENERGY_SAVING_NODE_MANAGER_TIMEOUT. #ENERGY_SAVING_WINDOW_TIME="60" # Timeout to set the maximum duration for a execution window # This value must be greater than ENERGY_SAVING_WINDOW_TIME. #ENERGY_SAVING_WINDOW_TIMEOUT="120" # The energy saving module can be automatically restarted after reaching # this number of cycles. This is a workaround for some DBD modules that do # not always free memory correctly. #ENERGY_MAX_CYCLES_UNTIL_REFRESH=5000 ################################################################################ ############################## # Suspend/Resume job feature # ############################################################################### # # Name of the perl script that manages suspend/resume. # (default is %%OARCONFDIR%%/suspend_resume_manager.pl) #SUSPEND_RESUME_FILE="%%OARCONFDIR%%/suspend_resume_manager.pl" # Files to execute just after a job was suspended and just before a job was resumed #JUST_AFTER_SUSPEND_EXEC_FILE="/path/to/prog" #JUST_BEFORE_RESUME_EXEC_FILE="/path/to/prog" # Timeout for the two previous scripts #SUSPEND_RESUME_SCRIPT_TIMEOUT="60" ############################################################################### ################################ # JOB_RESOURCE_MANAGER feature # ############################################################################### # Specify the name of the database field that will be passed to the # JOB_RESOURCE_MANAGER script. # If this option is set then users must use oarsh instead of ssh to walk on # the nodes they reserve using oarsub. # Look at the CPUSET file # (if defined, this option turn on the execution of JOB_RESOURCE_MANAGER script # execution on each job nodes: initialize cpuset, job keys, clean nodes, ...) JOB_RESOURCE_MANAGER_PROPERTY_DB_FIELD="cpuset" # Name of the perl script that manages cpuset. # (default is %%OARCONFDIR%%/job_resource_manager.pl which handles the linux kernel # cpuset, job keys, clean processes, ...) #JOB_RESOURCE_MANAGER_FILE="%%OARCONFDIR%%/job_resource_manager.pl" # Path of the relative directory where the cpusets will be created on each # nodes(same value than in /proc/self/cpuset). # WARNING: Change this value only if you know what you are doing. # (Note: comment this line to disable cpuset feature on computing nodes. Thus # if you only want to initialize job user without the cpuset, you have # to set OARSUB_FORCE_JOB_KEY="yes") CPUSET_PATH="/oar" # Resource "type" DB field to use if you want to enable the job uid feature. # (create a unique user id per job on each nodes of the job) #JOB_RESOURCE_MANAGER_JOB_UID_TYPE="userid" # Name of the perl script the retrieve monitoring data from compute nodes. # This is used in oarmonitor command. #OARMONITOR_SENSOR_FILE="%%OARCONFDIR%%/oarmonitor_sensor.pl" ############################################################################### ######### # OARSH # ############################################################################### # # This variable must be set to enable the use of oarsh from a frontale node # Otherwise you must not set this variable if you are not on a frontale #OARSH_OARSTAT_CMD="%%BINDIR%%/oarstat" # The following variable adds options to ssh (or OPENSSH_CMD if configured). # If one option is not handled by your ssh version just remove it BUT be # careful because these options are there for security reasons OARSH_OPENSSH_DEFAULT_OPTIONS="-oProxyCommand=none -oPermitLocalCommand=no" # If you set this variable to something different from 0 then oarsh will act # like a normal ssh **without** CPUSET restriction. # WARNING: this is a critical functionality (this is only useful if users want # to have a command to connect on every nodes without taking care of there ssh # configuration and act like a ssh) #OARSH_BYPASS_WHOLE_SECURITY="0" ############################################################################### #DESKTOP_COMPUTING_ALLOW_CREATE_NODE="0" #DESKTOP_COMPUTING_EXPIRY="300" # Remember to create these directories and give ownership to the oar user! #STAGEOUT_DIR="%%OARHOMEDIR%%/stageouts/" #STAGEIN_DIR="%%OARHOMEDIR%%/stageins" #STAGEIN_CACHE_EXPIRY="144" ############ # OARADMIN # ############# # # Uncomment the next line in order to activate the versioning mechanism # for admission rules and oar conf files # Repository is in %%OARHOMEDIR%%/.oaradmin # %%OARHOMEDIR%%/.oaradmin/rp : contains repository # %%OARHOMEDIR%%/.oaradmin/wc : contains working copy #OARADMIN_VERSIONING="yes" ############################################################################### ########### # OAR API # ############################################################################### # Disable this if you are not ok with a simple pidentd "authentication" # It is safe enough if you fully trust the client hosts (with an apropriate # ip-based access control into apache for example) #API_TRUST_IDENT="1" # Custom header for the html browsable format of the API #API_HTML_HEADER="%%OARCONFDIR%%/api_html_header.pl" # Custom form for posting jobs with html to the API #API_HTML_FORM="%%OARCONFDIR%%/api_html_postform.pl" # Default data structure variant # Value can be "oar" or "simple" (the default is "simple") # This can be override with the "?structure=" part of the queries # The "oar" variant tries to be as near as possible to the data structures # used by the export options of the oarstat/oarnodes commands. # The "simple" variant aims to be more simple, using arrays instead of hashes # when it is possible. # The first is more human readable; the second is simpler for programming. #API_DEFAULT_DATA_STRUCTURE="simple" # Maximum default number of items #API_DEFAULT_MAX_ITEMS_NUMBER=500 # Default parameters for the /jobs uri # if a "&limit=" is given, the API_DEFAULT_MAX_ITEMS_NUMBER is ignored for this uri #API_JOBS_URI_DEFAULT_PARAMS="state=Finishing,Running,Resuming,Suspended,Launching,toLaunch,Waiting,toAckReservation,Hold" # Set to 0 if you want the API to provide relative uris. # Relative uris may help if your API is behind a reverse proxy, # as you don't have to rewrite the uris, but due to the possible confusion # between "resources" and "resources/", it may not work with some libraries # like "ruby Restfully". #API_ABSOLUTE_URIS=1 ./oar-2.5.2/sources/core/tools/job_resource_manager_g5k.pl0000644000175000017500000004315711757171206021624 0ustar plbplb# $Id$ # # The job_resource_manager script is a perl script that oar server deploys on nodes # to manage cpusets, users, job keys, ... # # Usage: # This script is deployed from the server and executed as oar on the nodes # ARGV[0] can have two different values: # - "init" : then this script must create the right cpuset and assign # corresponding cpus:w # - "clean" : then this script must kill all processes in the cpuset and # clean the cpuset structure # TAKTUK_HOSTNAME environment variable must be defined and must be a name # that we will be able to find in the transfered hashtable ($Cpuset variable). use Fcntl ':flock'; #use Data::Dumper; sub exit_myself($$); sub print_log($$); my $Old_umask = sprintf("%lo",umask()); umask(oct("022")); my $Cpuset; my $Log_level; my $Allow_SSH_type = "allow_classic_ssh"; my $Security_pam_file = "$ENV{HOME}/access.conf"; my $Security_pam_file_tmp = "$ENV{HOME}/access.conf.tmp"; my $Cpuset_lock_file = "$ENV{HOME}/cpuset.lock."; # Retrieve parameters from STDIN in the "Cpuset" structure which looks like: # $Cpuset = { # job_id => id of the corresponding job # name => "cpuset name", # cpuset_path => "relative path in the cpuset FS", # nodes => hostname => [array with the content of the database cpuset field] # ssh_keys => { # public => { # file_name => "~oar/.ssh/authorized_keys" # key => "public key content" # } # private => { # file_name => "directory where to store the private key" # key => "private key content" # } # } # oar_tmp_directory => "path to the temp directory" # user => "user name" # job_user => "job user" # job_uid => "job uid for the job_user if needed" # types => hashtable with job types as keys # log_level => debug level number # } my $tmp = ""; while (){ $tmp .= $_; } $Cpuset = eval($tmp); if (!defined($Cpuset->{log_level})){ exit_myself(2,"Bad SSH hashtable transfered"); } $Log_level = $Cpuset->{log_level}; my $Cpuset_path_job; my @Cpuset_cpus; # Get the data structure only for this node if (defined($Cpuset->{cpuset_path})){ $Cpuset_path_job = $Cpuset->{cpuset_path}.'/'.$Cpuset->{name}; @Cpuset_cpus = @{$Cpuset->{nodes}->{$ENV{TAKTUK_HOSTNAME}}}; } print_log(3,"$ARGV[0]"); if ($ARGV[0] eq "init"){ # Initialize cpuset for this node # First, create the tmp oar directory if (!(((-d $Cpuset->{oar_tmp_directory}) and (-O $Cpuset->{oar_tmp_directory})) or (mkdir($Cpuset->{oar_tmp_directory})))){ exit_myself(13,"Directory $Cpuset->{oar_tmp_directory} does not exist and cannot be created"); } if (defined($Cpuset->{job_uid})){ my $prevuser = getpwuid($Cpuset->{job_uid}); system("oardodo /usr/sbin/userdel -f $prevuser") if (defined($prevuser)); my @tmp = getpwnam($Cpuset->{user}); if ($#tmp < 0){ exit_myself(15,"Cannot get information from user '$Cpuset->{user}'"); } if (system("oardodo /usr/sbin/adduser --disabled-password --gecos 'OAR temporary user' --no-create-home --force-badname --quiet --home $tmp[7] --gid $tmp[3] --shell $tmp[8] --uid $Cpuset->{job_uid} $Cpuset->{job_user}")){ exit_myself(15,"Failed to create $Cpuset->{job_user} with uid $Cpuset->{job_uid} and home $tmp[7] and group $tmp[3] and shell $tmp[8]"); } } if (defined($Cpuset_path_job)){ if (open(LOCKFILE,"> $Cpuset->{oar_tmp_directory}/job_manager_lock_file")){ flock(LOCKFILE,LOCK_EX) or exit_myself(17,"flock failed: $!"); if (system('oardodo mount -t cpuset | grep " /dev/cpuset " > /dev/null 2>&1')){ if (system('oardodo mkdir -p /dev/cpuset && oardodo mount -t cpuset none /dev/cpuset')){ exit_myself(4,"Failed to mount cpuset pseudo filesystem"); } } if (!(-d '/dev/cpuset/'.$Cpuset->{cpuset_path})){ if (system( 'oardodo mkdir -p /dev/cpuset/'.$Cpuset->{cpuset_path}.' &&'. 'oardodo chown -R oar /dev/cpuset/'.$Cpuset->{cpuset_path}.' &&'. '/bin/echo 0 | cat > /dev/cpuset/'.$Cpuset->{cpuset_path}.'/notify_on_release && '. '/bin/echo 0 | cat > /dev/cpuset/'.$Cpuset->{cpuset_path}.'/cpu_exclusive && '. 'cat /dev/cpuset/mems > /dev/cpuset/'.$Cpuset->{cpuset_path}.'/mems &&'. 'cat /dev/cpuset/cpus > /dev/cpuset/'.$Cpuset->{cpuset_path}.'/cpus' )){ exit_myself(4,"Failed to create cpuset $Cpuset->{cpuset_path}"); } } flock(LOCKFILE,LOCK_UN) or exit_myself(17,"flock failed: $!"); close(LOCKFILE); }else{ exit_myself(16,"Failed to open or create $Cpuset->{oar_tmp_directory}/job_manager_lock_file"); } #'for c in '."@Cpuset_cpus".';do cat /sys/devices/system/cpu/cpu$c/topology/physical_package_id > /dev/cpuset/'.$Cpuset_path_job.'/mems; done && '. # Locking around the creation of the cpuset for that user, to prevent race condition during the durty-user-based cleanup if (open(LOCK,">", $Cpuset_lock_file.$Cpuset->{user})){ flock(LOCK,LOCK_EX) or die "flock failed: $!\n"; # Be careful with the physical_package_id. Is it corresponding to the memory banc? if (system( 'oardodo mkdir -p /dev/cpuset/'.$Cpuset_path_job.' && '. 'oardodo chown -R oar /dev/cpuset/'.$Cpuset_path_job.' && '. '/bin/echo 0 | cat > /dev/cpuset/'.$Cpuset_path_job.'/notify_on_release && '. '/bin/echo 0 | cat > /dev/cpuset/'.$Cpuset_path_job.'/cpu_exclusive && '. 'cat /dev/cpuset/mems > /dev/cpuset/'.$Cpuset_path_job.'/mems && '. '/bin/echo '.join(",",@Cpuset_cpus).' | cat > /dev/cpuset/'.$Cpuset_path_job.'/cpus' )){ exit_myself(5,"Failed to create and feed the cpuset $Cpuset_path_job"); } flock(LOCK,LOCK_UN) or die "flock failed: $!\n"; close(LOCK); }else{ exit_myself(16,"[cpuset_manager] Error opening $Cpuset_lock_file"); } } # PAM part if (!defined($Cpuset->{types}->{timesharing})){ my $file_str = "# File generated by OAR.\n"; if (defined($Cpuset->{types}->{$Allow_SSH_type}) and ! system('diff /dev/cpuset/'.$Cpuset_path_job.'/cpus /dev/cpuset/'.$Cpuset->{cpuset_path}.'/cpus > /dev/null 2>&1')){ $file_str .= "+:".$Cpuset->{user}.":ALL\n"; } $file_str .= "-:ALL:ALL\n"; if (open(ACCESS, "> $Security_pam_file_tmp")){ print(ACCESS "$file_str"); close(ACCESS); }else{ exit(14); } rename $Security_pam_file_tmp,$Security_pam_file or die "Cannot replace access.conf file."; } # PAM part # Copy ssh key files if ($Cpuset->{ssh_keys}->{private}->{key} ne ""){ # private key if (open(PRIV, ">".$Cpuset->{ssh_keys}->{private}->{file_name})){ chmod(0600,$Cpuset->{ssh_keys}->{private}->{file_name}); if (!print(PRIV $Cpuset->{ssh_keys}->{private}->{key})){ unlink($Cpuset->{ssh_keys}->{private}->{file_name}); exit_myself(8,"Error writing $Cpuset->{ssh_keys}->{private}->{file_name}"); } close(PRIV); if (defined($Cpuset->{job_uid})){ system("ln -s $Cpuset->{ssh_keys}->{private}->{file_name} $Cpuset->{oar_tmp_directory}/$Cpuset->{job_user}.jobkey"); } }else{ exit_myself(7,"Error opening $Cpuset->{ssh_keys}->{private}->{file_name}"); } # public key if (open(PUB,"+<",$Cpuset->{ssh_keys}->{public}->{file_name})){ flock(PUB,LOCK_EX) or exit_myself(17,"flock failed: $!"); seek(PUB,0,0) or exit_myself(18,"seek failed: $!"); my $out = "\n".$Cpuset->{ssh_keys}->{public}->{key}."\n"; while (){ if ($_ =~ /environment=\"OAR_KEY=1\"/){ # We are reading a OAR key $_ =~ /(ssh-dss|ssh-rsa)\s+([^\s^\n]+)/; my $oar_key = $2; $Cpuset->{ssh_keys}->{public}->{key} =~ /(ssh-dss|ssh-rsa)\s+([^\s^\n]+)/; my $curr_key = $2; if ($curr_key eq $oar_key){ exit_myself(13,"ERROR: the user has specified the same ssh key than used by the user oar"); } $out .= $_; }elsif ($_ =~ /environment=\"OAR_CPUSET=([\w\/]+)\"/){ # Remove from authorized keys outdated keys (typically after a reboot) if (-d "/dev/cpuset/$1"){ $out .= $_; } }else{ $out .= $_; } } if (!(seek(PUB,0,0) and print(PUB $out) and truncate(PUB,tell(PUB)))){ exit_myself(9,"Error writing $Cpuset->{ssh_keys}->{public}->{file_name}"); } flock(PUB,LOCK_UN) or exit_myself(17,"flock failed: $!"); close(PUB); }else{ unlink($Cpuset->{ssh_keys}->{private}->{file_name}); exit_myself(10,"Error opening $Cpuset->{ssh_keys}->{public}->{file_name}"); } } }elsif ($ARGV[0] eq "clean"){ # delete ssh key files if ($Cpuset->{ssh_keys}->{private}->{key} ne ""){ # private key unlink($Cpuset->{ssh_keys}->{private}->{file_name}); if (defined($Cpuset->{job_uid})){ unlink("$Cpuset->{oar_tmp_directory}/$Cpuset->{job_user}.jobkey"); } # public key if (open(PUB,"+<", $Cpuset->{ssh_keys}->{public}->{file_name})){ flock(PUB,LOCK_EX) or exit_myself(17,"flock failed: $!"); seek(PUB,0,0) or exit_myself(18,"seek failed: $!"); #Change file on the fly my $out = ""; while (){ if (($_ ne "\n") and ($_ ne $Cpuset->{ssh_keys}->{public}->{key})){ $out .= $_; } } if (!(seek(PUB,0,0) and print(PUB $out) and truncate(PUB,tell(PUB)))){ exit_myself(12,"Error changing $Cpuset->{ssh_keys}->{public}->{file_name}"); } flock(PUB,LOCK_UN) or exit_myself(17,"flock failed: $!"); close(PUB); }else{ exit_myself(11,"Error opening $Cpuset->{ssh_keys}->{public}->{file_name}"); } } # PAM part if (!defined($Cpuset->{types}->{timesharing})){ my $file_str = "# File generated by OAR.\n"; $file_str .= "-:ALL:ALL\n"; if (open(ACCESS, "> $Security_pam_file_tmp")){ print(ACCESS "$file_str"); close(ACCESS); }else{ exit_myself(14,"Failed to set pam access restriction"); } rename $Security_pam_file_tmp,$Security_pam_file or die "Cannot replace access.conf file."; if (defined($Cpuset->{types}->{$Allow_SSH_type}) and ! system('diff /dev/cpuset/'.$Cpuset_path_job.'/cpus /dev/cpuset/'.$Cpuset->{cpuset_path}.'/cpus > /dev/null 2>&1')){ unless ($Cpuset->{user} eq "root" or $Cpuset->{user} eq "oar") { system("OARDO_BECOME_USER=$Cpuset->{user} oardodo kill -9 -1"); } } } # PAM part # Clean cpuset on this node if (defined($Cpuset_path_job)){ system('PROCESSES=$(cat /dev/cpuset/'.$Cpuset_path_job.'/tasks) while [ "$PROCESSES" != "" ] do oardodo kill -9 $PROCESSES PROCESSES=$(cat /dev/cpuset/'.$Cpuset_path_job.'/tasks) done' ); # Locking around the cleanup of the cpuset for that user, to prevent a creation to occure at the same time # which would allow race condition for the dirty-user-based clean-up mechanism if (open(LOCK,">", $Cpuset_lock_file.$Cpuset->{user})){ flock(LOCK,LOCK_EX) or die "flock failed: $!\n"; if (system('oardodo rmdir /dev/cpuset'.$Cpuset_path_job)){ # Uncomment this line if you want to use several network_address properties # which are the same physical computer (linux kernel) #exit(0); exit_myself(6,"Failed to delete the cpuset $Cpuset_path_job"); } if (not defined($Cpuset->{job_uid})){ # dirty-user-based cleanup: do cleanup only if that is the last job of the user on that host. my @cpusets = (); if (opendir(DIR, "/dev/cpuset/".$Cpuset->{cpuset_path}.'/')) { @cpusets = grep { /^$Cpuset->{user}_\d+$/ } readdir(DIR); closedir DIR; } else { exit_myself(18,"Can't opendir: /dev/cpuset/$Cpuset->{cpuset_path}"); } if ($#cpusets < 0) { my $useruid=getpwnam($Cpuset->{user}); my $ipcrm_args=""; if (open(IPCMSG,"< /proc/sysvipc/msg")) { ; while () { if (/^\s*\d+\s+(\d+)(?:\s+\d+){5}\s+$useruid(?:\s+\d+){6}/) { $ipcrm_args .= " -q $1"; print_log(3,"Found IPC MSG for user $useruid: $1."); } } close (IPCMSG); } else { print_log(3,"Cannot open /proc/sysvipc/msg: $!."); } if (open(IPCSHM,"< /proc/sysvipc/shm")) { ; while () { if (/^\s*\d+\s+(\d+)(?:\s+\d+){5}\s+$useruid(?:\s+\d+){6}/) { $ipcrm_args .= " -m $1"; print_log(3,"Found IPC SHM for user $useruid: $1."); } } close (IPCSHM); } else { print_log(3,"Cannot open /proc/sysvipc/shm: $!.\n"); } if (open(IPCSEM,"< /proc/sysvipc/sem")) { ; while () { if (/^\s*[\d\-]+\s+(\d+)(?:\s+\d+){2}\s+$useruid(?:\s+\d+){5}/) { $ipcrm_args .= " -s $1"; print_log(3,"Found IPC SEM for user $useruid: $1."); } } close (IPCSEM); } else { print_log(3,"Cannot open /proc/sysvipc/sem: $!."); } if ($ipcrm_args) { print_log (3,"Purging SysV IPC: ipcrm $ipcrm_args."); system("OARDO_BECOME_USER=$Cpuset->{user} oardodo ipcrm $ipcrm_args"); } print_log (3,"Purging /tmp..."); system("oardodo find /tmp/. -user $Cpuset->{user} -delete"); } else { print_log(2,"Not purging SysV IPC and /tmp as $Cpuset->{user} still has a job running on this host.\n"); } } flock(LOCK,LOCK_UN) or die "flock failed: $!\n"; close(LOCK); } } if (defined($Cpuset->{job_uid})){ my $ipcrm_args=""; if (open(IPCMSG,"< /proc/sysvipc/msg")) { ; while () { if (/\s+\d+\s+(\d+)(?:\s+\d+){5}\s+$Cpuset->{job_uid}(?:\s+\d+){6}$/) { $ipcrm_args .= " -q $1"; } } close (IPCMSG); }else{ exit_myself(14,"Cannot open /proc/sysvipc/msg: $!"); } if (open(IPCSHM,"< /proc/sysvipc/shm")) { ; while () { if (/\s+\d+\s+(\d+)(?:\s+\d+){5}\s+$Cpuset->{job_uid}(?:\s+\d+){6}$/) { $ipcrm_args .= " -m $1"; } } close (IPCSHM); }else{ exit_myself(14,"Cannot open /proc/sysvipc/shm: $!"); } if (open(IPCSEM,"< /proc/sysvipc/sem")) { ; while () { if (/\s+\d+\s+(\d+)(?:\s+\d+){2}\s+$Cpuset->{job_uid}(?:\s+\d+){5}$/) { $ipcrm_args .= " -s $1"; } } close (IPCSEM); }else{ exit_myself(14,"Cannot open /proc/sysvipc/sem: $!"); } if ($ipcrm_args) { print_log(3,"Purging SysV IPC: ipcrm $ipcrm_args"); if(system("oardodo ipcrm $ipcrm_args")){ exit_myself(14,"Failed to purge IPC: ipcrm $ipcrm_args"); } } print_log(3,"Purging /tmp..."); #system("oardodo find /tmp/ -user $Cpuset->{job_user} -exec rm -rfv {} \\;"); system("oardodo find /tmp/. -user $Cpuset->{job_user} -delete"); system("oardodo /usr/sbin/userdel -f $Cpuset->{job_user}"); } }else{ exit_myself(3,"Bad command line argument $ARGV[0]"); } exit(0); # Print error message and exit sub exit_myself($$){ my $exit_code = shift; my $str = shift; warn("[job_resource_manager][$Cpuset->{job_id}][ERROR] ".$str."\n"); exit($exit_code); } # Print log message depending on the LOG_LEVEL config value sub print_log($$){ my $l = shift; my $str = shift; if ($l <= $Log_level){ print("[job_resource_manager][$Cpuset->{job_id}][DEBUG] $str\n"); } } ./oar-2.5.2/sources/core/tools/job_resource_manager_cgroups.pl0000644000175000017500000004543511757171206022621 0ustar plbplb# $Id$ # # The job_resource_manager_cgroups script is a perl script that oar server # deploys on nodes to manage cpusets, users, job keys, ... # # In this script some cgroup Linux features are added in addition to cpuset: # - Tag each network packet from processes of this job with the class id = # $OAR_JOB_ID # - Put an IO share corresponding to the ratio between reserved cpus and # the number of the node # # Usage: # This script is deployed from the server and executed as oar on the nodes # ARGV[0] can have two different values: # - "init" : then this script must create the right cpuset and assign # corresponding cpus # - "clean" : then this script must kill all processes in the cpuset and # clean the cpuset structure # TAKTUK_HOSTNAME environment variable must be defined and must be a name # that we will be able to find in the transfered hashtable ($Cpuset variable). use Fcntl ':flock'; #use Data::Dumper; sub exit_myself($$); sub print_log($$); my $Old_umask = sprintf("%lo",umask()); umask(oct("022")); my $Cgroup_mount_point = "/dev/oar_cgroups"; my $Cpuset; my $Log_level; my $Cpuset_lock_file = "$ENV{HOME}/cpuset.lock."; # Retrieve parameters from STDIN in the "Cpuset" structure which looks like: # $Cpuset = { # job_id => id of the corresponding job # name => "cpuset name", # cpuset_path => "relative path in the cpuset FS", # nodes => hostname => [array with the content of the database cpuset field] # ssh_keys => { # public => { # file_name => "~oar/.ssh/authorized_keys" # key => "public key content" # } # private => { # file_name => "directory where to store the private key" # key => "private key content" # } # } # oar_tmp_directory => "path to the temp directory" # user => "user name" # job_user => "job user" # job_uid => "job uid for the job_user if needed" # types => hashtable with job types as keys # log_level => debug level number # } my $tmp = ""; while (){ $tmp .= $_; } $Cpuset = eval($tmp); if (!defined($Cpuset->{log_level})){ exit_myself(2,"Bad SSH hashtable transfered"); } $Log_level = $Cpuset->{log_level}; my $Cpuset_path_job; my @Cpuset_cpus; # Get the data structure only for this node if (defined($Cpuset->{cpuset_path})){ $Cpuset_path_job = $Cpuset->{cpuset_path}.'/'.$Cpuset->{name}; @Cpuset_cpus = @{$Cpuset->{nodes}->{$ENV{TAKTUK_HOSTNAME}}}; } print_log(3,"$ARGV[0]"); if ($ARGV[0] eq "init"){ # Initialize cpuset for this node # First, create the tmp oar directory if (!(((-d $Cpuset->{oar_tmp_directory}) and (-O $Cpuset->{oar_tmp_directory})) or (mkdir($Cpuset->{oar_tmp_directory})))){ exit_myself(13,"Directory $Cpuset->{oar_tmp_directory} does not exist and cannot be created"); } if (defined($Cpuset->{job_uid})){ my $prevuser = getpwuid($Cpuset->{job_uid}); system("oardodo /usr/sbin/userdel -f $prevuser") if (defined($prevuser)); my @tmp = getpwnam($Cpuset->{user}); if ($#tmp < 0){ exit_myself(15,"Cannot get information from user '$Cpuset->{user}'"); } if (system("oardodo /usr/sbin/adduser --disabled-password --gecos 'OAR temporary user' --no-create-home --force-badname --quiet --home $tmp[7] --gid $tmp[3] --shell $tmp[8] --uid $Cpuset->{job_uid} $Cpuset->{job_user}")){ exit_myself(15,"Failed to create $Cpuset->{job_user} with uid $Cpuset->{job_uid} and home $tmp[7] and group $tmp[3] and shell $tmp[8]"); } } if (defined($Cpuset_path_job)){ if (open(LOCKFILE,"> $Cpuset->{oar_tmp_directory}/job_manager_lock_file")){ flock(LOCKFILE,LOCK_EX) or exit_myself(17,"flock failed: $!"); if (!(-r $Cgroup_mount_point.'/tasks')){ if (system('oardodo mkdir -p '.$Cgroup_mount_point.' && oardodo mount -t cgroup -o cpuset,cpu,cpuacct,devices,freezer,net_cls,blkio none '.$Cgroup_mount_point.'; oardodo rm -f /dev/cpuset; oardodo ln -s '.$Cgroup_mount_point.' /dev/cpuset')){ exit_myself(4,"Failed to mount cgroup pseudo filesystem"); } } if (!(-d $Cgroup_mount_point.'/'.$Cpuset->{cpuset_path})){ if (system( 'oardodo mkdir -p '.$Cgroup_mount_point.'/'.$Cpuset->{cpuset_path}.' &&'. 'oardodo chown -R oar '.$Cgroup_mount_point.'/'.$Cpuset->{cpuset_path}.' &&'. '/bin/echo 0 | cat > '.$Cgroup_mount_point.'/'.$Cpuset->{cpuset_path}.'/notify_on_release && '. '/bin/echo 0 | cat > '.$Cgroup_mount_point.'/'.$Cpuset->{cpuset_path}.'/cpuset.cpu_exclusive && '. 'cat '.$Cgroup_mount_point.'/cpuset.mems > '.$Cgroup_mount_point.'/'.$Cpuset->{cpuset_path}.'/cpuset.mems &&'. 'cat '.$Cgroup_mount_point.'/cpuset.cpus > '.$Cgroup_mount_point.'/'.$Cpuset->{cpuset_path}.'/cpuset.cpus &&'. '/bin/echo 1000 | cat > '.$Cgroup_mount_point.'/'.$Cpuset->{cpuset_path}.'/blkio.weight' )){ exit_myself(4,"Failed to create cgroup $Cpuset->{cpuset_path}"); } } flock(LOCKFILE,LOCK_UN) or exit_myself(17,"flock failed: $!"); close(LOCKFILE); }else{ exit_myself(16,"Failed to open or create $Cpuset->{oar_tmp_directory}/job_manager_lock_file"); } #'MEM= ;for c in '."@Cpuset_cpus".';do MEM=$(cat /sys/devices/system/cpu/cpu$c/topology/physical_package_id),$MEM; done; echo $MEM > /dev/cpuset/'.$Cpuset_path_job.'/mems && '. #'MEM= ;for c in '."@Cpuset_cpus".';do for n in /sys/devices/system/node/node* ;do if [ -r "$n/cpu$c" ]; then MEM=$(basename $n | sed s/node//g),$MEM; fi; done; done;echo $MEM > /dev/cpuset/'.$Cpuset_path_job.'/mems && '. # 'cat '.$Cgroup_mount_point.'/cpuset.mems > '.$Cgroup_mount_point.'/'.$Cpuset_path_job.'/cpuset.mems && '. # Be careful with the physical_package_id. Is it corresponding to the memory banc? if (system( 'oardodo mkdir -p '.$Cgroup_mount_point.'/'.$Cpuset_path_job.' && '. 'oardodo chown -R oar '.$Cgroup_mount_point.'/'.$Cpuset_path_job.' && '. '/bin/echo 0 | cat > '.$Cgroup_mount_point.'/'.$Cpuset_path_job.'/notify_on_release && '. '/bin/echo 0 | cat > '.$Cgroup_mount_point.'/'.$Cpuset_path_job.'/cpuset.cpu_exclusive && '. 'MEM= ;for c in '."@Cpuset_cpus".';do for n in /sys/devices/system/node/node* ;do if [ -r "$n/cpu$c" ]; then MEM=$(basename $n | sed s/node//g),$MEM; fi; done; done;echo $MEM > '.$Cgroup_mount_point.'/'.$Cpuset_path_job.'/cpuset.mems && '. '/bin/echo '.join(",",@Cpuset_cpus).' | cat > '.$Cgroup_mount_point.'/'.$Cpuset_path_job.'/cpuset.cpus' )){ exit_myself(5,"Failed to create and feed the cpuset $Cpuset_path_job"); } # Tag network packets from processes of this job if (system( '/bin/echo '.$Cpuset->{job_id}.' | cat > '.$Cgroup_mount_point.'/'.$Cpuset_path_job.'/net_cls.classid' )){ exit_myself(5,"Failed to tag network packets of the cgroup $Cpuset_path_job"); } # Put a share for IO disk corresponding of the ratio between the number # of cpus of this cgroup and the number of cpus of the node my @cpu_cgroup_uniq_list; my %cpu_cgroup_name_hash; foreach my $i (@Cpuset_cpus){ if (!defined($cpu_cgroup_name_hash{$i})){ $cpu_cgroup_name_hash{$i} = 1; push(@cpu_cgroup_uniq_list, $i); } } # Get the whole cpus of the node my @node_cpus; if (open(CPUS, "$Cgroup_mount_point/cpuset.cpus")){ my $str = ; chop($str); $str =~ s/\-/\.\./g; @node_cpus = eval($str); close(CPUS); }else{ exit_myself(5,"Failed to retrieve the cpu list of the node $Cgroup_mount_point/cpuset.cpus"); } # Need to do more tests to validate # my $IO_ratio = sprintf("%.0f",(($#cpu_cgroup_uniq_list + 1) / ($#node_cpus + 1) * 1000)) ; # if (system( '/bin/echo '.$IO_ratio.' | cat > '.$Cgroup_mount_point.'/'.$Cpuset_path_job.'/blkio.weight')){ # exit_myself(5,"Failed to set the blkio.weight to $IO_ratio"); # } } # Copy ssh key files if ($Cpuset->{ssh_keys}->{private}->{key} ne ""){ # private key if (open(PRIV, ">".$Cpuset->{ssh_keys}->{private}->{file_name})){ chmod(0600,$Cpuset->{ssh_keys}->{private}->{file_name}); if (!print(PRIV $Cpuset->{ssh_keys}->{private}->{key})){ unlink($Cpuset->{ssh_keys}->{private}->{file_name}); exit_myself(8,"Error writing $Cpuset->{ssh_keys}->{private}->{file_name}"); } close(PRIV); if (defined($Cpuset->{job_uid})){ system("ln -s $Cpuset->{ssh_keys}->{private}->{file_name} $Cpuset->{oar_tmp_directory}/$Cpuset->{job_user}.jobkey"); } }else{ exit_myself(7,"Error opening $Cpuset->{ssh_keys}->{private}->{file_name}"); } # public key if (open(PUB,"+<",$Cpuset->{ssh_keys}->{public}->{file_name})){ flock(PUB,LOCK_EX) or exit_myself(17,"flock failed: $!"); seek(PUB,0,0) or exit_myself(18,"seek failed: $!"); my $out = "\n".$Cpuset->{ssh_keys}->{public}->{key}."\n"; while (){ if ($_ =~ /environment=\"OAR_KEY=1\"/){ # We are reading a OAR key $_ =~ /(ssh-dss|ssh-rsa)\s+([^\s^\n]+)/; my $oar_key = $2; $Cpuset->{ssh_keys}->{public}->{key} =~ /(ssh-dss|ssh-rsa)\s+([^\s^\n]+)/; my $curr_key = $2; if ($curr_key eq $oar_key){ exit_myself(13,"ERROR: the user has specified the same ssh key than used by the user oar"); } $out .= $_; }elsif ($_ =~ /environment=\"OAR_CPUSET=([\w\/]+)\"/){ # Remove from authorized keys outdated keys (typically after a reboot) if (-d "/dev/cpuset/$1"){ $out .= $_; } }else{ $out .= $_; } } if (!(seek(PUB,0,0) and print(PUB $out) and truncate(PUB,tell(PUB)))){ exit_myself(9,"Error writing $Cpuset->{ssh_keys}->{public}->{file_name}"); } flock(PUB,LOCK_UN) or exit_myself(17,"flock failed: $!"); close(PUB); }else{ unlink($Cpuset->{ssh_keys}->{private}->{file_name}); exit_myself(10,"Error opening $Cpuset->{ssh_keys}->{public}->{file_name}"); } } }elsif ($ARGV[0] eq "clean"){ # delete ssh key files if ($Cpuset->{ssh_keys}->{private}->{key} ne ""){ # private key unlink($Cpuset->{ssh_keys}->{private}->{file_name}); if (defined($Cpuset->{job_uid})){ unlink("$Cpuset->{oar_tmp_directory}/$Cpuset->{job_user}.jobkey"); } # public key if (open(PUB,"+<", $Cpuset->{ssh_keys}->{public}->{file_name})){ flock(PUB,LOCK_EX) or exit_myself(17,"flock failed: $!"); seek(PUB,0,0) or exit_myself(18,"seek failed: $!"); #Change file on the fly my $out = ""; while (){ if (($_ ne "\n") and ($_ ne $Cpuset->{ssh_keys}->{public}->{key})){ $out .= $_; } } if (!(seek(PUB,0,0) and print(PUB $out) and truncate(PUB,tell(PUB)))){ exit_myself(12,"Error changing $Cpuset->{ssh_keys}->{public}->{file_name}"); } flock(PUB,LOCK_UN) or exit_myself(17,"flock failed: $!"); close(PUB); }else{ exit_myself(11,"Error opening $Cpuset->{ssh_keys}->{public}->{file_name}"); } } # Clean cpuset on this node if (defined($Cpuset_path_job)){ system('echo THAWED > '.$Cgroup_mount_point.'/'.$Cpuset_path_job.'/freezer.state PROCESSES=$(cat '.$Cgroup_mount_point.'/'.$Cpuset_path_job.'/tasks) while [ "$PROCESSES" != "" ] do oardodo kill -9 $PROCESSES PROCESSES=$(cat '.$Cgroup_mount_point.'/'.$Cpuset_path_job.'/tasks) done' ); # Locking around the cleanup of the cpuset for that user, to prevent a creation to occure at the same time # which would allow race condition for the dirty-user-based clean-up mechanism if (open(LOCK,">", $Cpuset_lock_file.$Cpuset->{user})){ flock(LOCK,LOCK_EX) or die "flock failed: $!\n"; if (system('oardodo rmdir '.$Cgroup_mount_point.'/'.$Cpuset_path_job)){ # Uncomment this line if you want to use several network_address properties # which are the same physical computer (linux kernel) #exit(0); exit_myself(6,"Failed to delete the cpuset $Cpuset_path_job"); } if (not defined($Cpuset->{job_uid})){ # dirty-user-based cleanup: do cleanup only if that is the last job of the user on that host. my @cpusets = (); if (opendir(DIR, $Cgroup_mount_point.'/'.$Cpuset->{cpuset_path}.'/')) { @cpusets = grep { /^$Cpuset->{user}_\d+$/ } readdir(DIR); closedir DIR; } else { exit_myself(18,"Can't opendir: $Cgroup_mount_point/$Cpuset->{cpuset_path}"); } if ($#cpusets < 0) { # No other jobs on this node at this time my $useruid=getpwnam($Cpuset->{user}); my $ipcrm_args=""; if (open(IPCMSG,"< /proc/sysvipc/msg")) { ; while () { if (/^\s*\d+\s+(\d+)(?:\s+\d+){5}\s+$useruid(?:\s+\d+){6}/) { $ipcrm_args .= " -q $1"; print_log(3,"Found IPC MSG for user $useruid: $1."); } } close (IPCMSG); } else { print_log(3,"Cannot open /proc/sysvipc/msg: $!."); } if (open(IPCSHM,"< /proc/sysvipc/shm")) { ; while () { if (/^\s*\d+\s+(\d+)(?:\s+\d+){5}\s+$useruid(?:\s+\d+){6}/) { $ipcrm_args .= " -m $1"; print_log(3,"Found IPC SHM for user $useruid: $1."); } } close (IPCSHM); } else { print_log(3,"Cannot open /proc/sysvipc/shm: $!."); } if (open(IPCSEM,"< /proc/sysvipc/sem")) { ; while () { if (/^\s*[\d\-]+\s+(\d+)(?:\s+\d+){2}\s+$useruid(?:\s+\d+){5}/) { $ipcrm_args .= " -s $1"; print_log(3,"Found IPC SEM for user $useruid: $1."); } } close (IPCSEM); } else { print_log(3,"Cannot open /proc/sysvipc/sem: $!."); } if ($ipcrm_args) { print_log (3,"Purging SysV IPC: ipcrm $ipcrm_args."); system("OARDO_BECOME_USER=$Cpuset->{user} oardodo ipcrm $ipcrm_args"); } print_log (3,"Purging /tmp /dev/shm /var/tmp..."); system("oardodo find /tmp/. /dev/shm/. /var/tmp/. -user $Cpuset->{user} -delete"); } else { print_log(2,"Not purging SysV IPC and /tmp as $Cpuset->{user} still has a job running on this host."); } } flock(LOCK,LOCK_UN) or die "flock failed: $!\n"; close(LOCK); } } if (defined($Cpuset->{job_uid})){ my $ipcrm_args=""; if (open(IPCMSG,"< /proc/sysvipc/msg")) { ; while () { if (/^\s*\d+\s+(\d+)(?:\s+\d+){5}\s+$Cpuset->{job_uid}(?:\s+\d+){6}$/) { $ipcrm_args .= " -q $1"; } else { print_log(3,"Cannot parse IPC MSG: $_."); } } close (IPCMSG); }else{ exit_myself(14,"Cannot open /proc/sysvipc/msg: $!"); } if (open(IPCSHM,"< /proc/sysvipc/shm")) { ; while () { if (/^\s*\d+\s+(\d+)(?:\s+\d+){5}\s+$Cpuset->{job_uid}(?:\s+\d+){6}$/) { $ipcrm_args .= " -m $1"; } else { print_log(3,"Cannot parse IPC SHM: $_."); } } close (IPCSHM); }else{ exit_myself(14,"Cannot open /proc/sysvipc/shm: $!"); } if (open(IPCSEM,"< /proc/sysvipc/sem")) { ; while () { if (/^\s*\d+\s+(\d+)(?:\s+\d+){2}\s+$Cpuset->{job_uid}(?:\s+\d+){5}$/) { $ipcrm_args .= " -s $1"; } else { print_log(3,"Cannot parse IPC SEM: $_."); } } close (IPCSEM); }else{ exit_myself(14,"Cannot open /proc/sysvipc/sem: $!"); } if ($ipcrm_args) { print_log(3,"Purging SysV IPC: ipcrm $ipcrm_args"); if(system("oardodo ipcrm $ipcrm_args")){ exit_myself(14,"Failed to purge IPC: ipcrm $ipcrm_args"); } } print_log(3,"Purging /tmp..."); #system("oardodo find /tmp/ -user $Cpuset->{job_user} -exec rm -rfv {} \\;"); system("oardodo find /tmp/. /dev/shm/. /var/tmp/. -user $Cpuset->{job_user} -delete"); system("oardodo /usr/sbin/userdel -f $Cpuset->{job_user}"); } }else{ exit_myself(3,"Bad command line argument $ARGV[0]"); } exit(0); # Print error message and exit sub exit_myself($$){ my $exit_code = shift; my $str = shift; warn("[job_resource_manager_cgroups][$Cpuset->{job_id}][ERROR] ".$str."\n"); exit($exit_code); } # Print log message depending on the LOG_LEVEL config value sub print_log($$){ my $l = shift; my $str = shift; if ($l <= $Log_level){ print("[job_resource_manager_cgroups][$Cpuset->{job_id}][DEBUG] $str\n"); } } ./oar-2.5.2/sources/core/tools/job_resource_manager_altix450.pl0000644000175000017500000003136311757171206022504 0ustar plbplb# $Id$ # # The job_resource_manager script is a perl script that oar server deploys on nodes # to manage cpusets, users, job keys, ... # # Usage: # This script is deployed from the server and executed as oar on the nodes # ARGV[0] can have two different values: # - "init" : then this script must create the right cpuset and assign # corresponding cpus # - "clean" : then this script must kill all processes in the cpuset and # clean the cpuset structure # TAKTUK_HOSTNAME environment variable must be defined and must be a name # that we will be able to find in the transfered hashtable ($Cpuset variable). use Fcntl ':flock'; #use Data::Dumper; sub exit_myself($$); sub print_log($$); my $Old_umask = sprintf("%lo",umask()); umask(oct("022")); my $Cpuset; my $Log_level; # Retrieve parameters from STDIN in the "Cpuset" structure which looks like: # $Cpuset = { # job_id => id of the corresponding job # name => "cpuset name", # cpuset_path => "relative path in the cpuset FS", # nodes => hostname => [array with the content of the database cpuset field] # ssh_keys => { # public => { # file_name => "~oar/.ssh/authorized_keys" # key => "public key content" # } # private => { # file_name => "directory where to store the private key" # key => "private key content" # } # } # oar_tmp_directory => "path to the temp directory" # user => "user name" # job_user => "job user" # job_uid => "job uid for the job_user if needed" # types => hashtable with job types as keys # log_level => debug level number # } my $tmp = ""; while (){ $tmp .= $_; } $Cpuset = eval($tmp); if (!defined($Cpuset->{log_level})){ exit_myself(2,"Bad SSH hashtable transfered"); } $Log_level = $Cpuset->{log_level}; my $Cpuset_path_job; my @Cpuset_cpus; # Get the data structure only for this node if (defined($Cpuset->{cpuset_path})){ $Cpuset_path_job = $Cpuset->{cpuset_path}.'/'.$Cpuset->{name}; @Cpuset_cpus = @{$Cpuset->{nodes}->{$ENV{TAKTUK_HOSTNAME}}}; } print_log(3,"$ARGV[0]"); if ($ARGV[0] eq "init"){ # Initialize cpuset for this node # First, create the tmp oar directory if (!(((-d $Cpuset->{oar_tmp_directory}) and (-O $Cpuset->{oar_tmp_directory})) or (mkdir($Cpuset->{oar_tmp_directory})))){ exit_myself(13,"Directory $Cpuset->{oar_tmp_directory} does not exist and cannot be created"); } if (defined($Cpuset->{job_uid})){ my $prevuser = getpwuid($Cpuset->{job_uid}); system("oardodo /usr/sbin/userdel -f $prevuser") if (defined($prevuser)); my @tmp = getpwnam($Cpuset->{user}); if ($#tmp < 0){ exit_myself(15,"Cannot get information from user '$Cpuset->{user}'"); } if (system("oardodo /usr/sbin/adduser --disabled-password --gecos 'OAR temporary user' --no-create-home --force-badname --quiet --home $tmp[7] --gid $tmp[3] --shell $tmp[8] --uid $Cpuset->{job_uid} $Cpuset->{job_user}")){ exit_myself(15,"Failed to create $Cpuset->{job_user} with uid $Cpuset->{job_uid} and home $tmp[7] and group $tmp[3] and shell $tmp[8]"); } } if (defined($Cpuset_path_job)){ if (open(LOCKFILE,"> $Cpuset->{oar_tmp_directory}/job_manager_lock_file")){ flock(LOCKFILE,LOCK_EX) or exit_myself(17,"flock failed: $!"); if (system('oardodo mount -t cpuset | grep " /dev/cpuset " > /dev/null 2>&1')){ if (system('oardodo mkdir -p /dev/cpuset && oardodo mount -t cpuset none /dev/cpuset')){ exit_myself(4,"Failed to mount cpuset pseudo filesystem"); } } if (!(-d '/dev/cpuset/'.$Cpuset->{cpuset_path})){ if (system( 'oardodo mkdir -p /dev/cpuset/'.$Cpuset->{cpuset_path}.' &&'. 'oardodo chown -R oar /dev/cpuset/'.$Cpuset->{cpuset_path}.' &&'. '/bin/echo 0 | cat > /dev/cpuset/'.$Cpuset->{cpuset_path}.'/notify_on_release && '. '/bin/echo 0 | cat > /dev/cpuset/'.$Cpuset->{cpuset_path}.'/cpu_exclusive && '. 'cat /dev/cpuset/mems > /dev/cpuset/'.$Cpuset->{cpuset_path}.'/mems &&'. 'cat /dev/cpuset/cpus > /dev/cpuset/'.$Cpuset->{cpuset_path}.'/cpus' )){ exit_myself(4,"Failed to create cpuset $Cpuset->{cpuset_path}"); } } flock(LOCKFILE,LOCK_UN) or exit_myself(17,"flock failed: $!"); close(LOCKFILE); }else{ exit_myself(16,"Failed to open or create $Cpuset->{oar_tmp_directory}/job_manager_lock_file"); } #'for c in '."@Cpuset_cpus".';do cat /sys/devices/system/cpu/cpu$c/topology/physical_package_id > /dev/cpuset/'.$Cpuset_path_job.'/mems; done && '. # Be careful with the physical_package_id. Is it corresponding to the memory banc? if (system( 'oardodo mkdir -p /dev/cpuset/'.$Cpuset_path_job.' && '. 'oardodo chown -R oar /dev/cpuset/'.$Cpuset_path_job.' && '. 'oardodo "echo 0 > /dev/cpuset/mem_exclusive" && '. '/bin/echo 0 | cat > /dev/cpuset/'.$Cpuset_path_job.'/notify_on_release && '. '/bin/echo 0 | cat > /dev/cpuset/'.$Cpuset_path_job.'/cpu_exclusive && '. # 'cat /dev/cpuset/mems > /dev/cpuset/'.$Cpuset_path_job.'/mems && '. 'for c in '.join(" ",@Cpuset_cpus).';do pnode=$(basename $(dirname $(find /sys/devices/system/node -name cpu$c))); pmems="$pmems,${pnode#node}";done && '. 'echo ${pmems#,} > /dev/cpuset/'.$Cpuset_path_job.'/mems && '. '/bin/echo '.join(",",@Cpuset_cpus).' | cat > /dev/cpuset/'.$Cpuset_path_job.'/cpus' )){ exit_myself(5,"Failed to create and feed the cpuset $Cpuset_path_job"); } } # Copy ssh key files if ($Cpuset->{ssh_keys}->{private}->{key} ne ""){ # private key if (open(PRIV, ">".$Cpuset->{ssh_keys}->{private}->{file_name})){ chmod(0600,$Cpuset->{ssh_keys}->{private}->{file_name}); if (!print(PRIV $Cpuset->{ssh_keys}->{private}->{key})){ unlink($Cpuset->{ssh_keys}->{private}->{file_name}); exit_myself(8,"Error writing $Cpuset->{ssh_keys}->{private}->{file_name}"); } close(PRIV); if (defined($Cpuset->{job_uid})){ system("ln -s $Cpuset->{ssh_keys}->{private}->{file_name} $Cpuset->{oar_tmp_directory}/$Cpuset->{job_user}.jobkey"); } }else{ exit_myself(7,"Error opening $Cpuset->{ssh_keys}->{private}->{file_name}"); } # public key if (open(PUB,"+<",$Cpuset->{ssh_keys}->{public}->{file_name})){ flock(PUB,LOCK_EX) or exit_myself(17,"flock failed: $!"); seek(PUB,0,0) or exit_myself(18,"seek failed: $!"); my $out = "\n".$Cpuset->{ssh_keys}->{public}->{key}."\n"; while (){ if ($_ =~ /environment=\"OAR_KEY=1\"/){ # We are reading a OAR key $_ =~ /(ssh-dss|ssh-rsa)\s+([^\s^\n]+)/; my $oar_key = $2; $Cpuset->{ssh_keys}->{public}->{key} =~ /(ssh-dss|ssh-rsa)\s+([^\s^\n]+)/; my $curr_key = $2; if ($curr_key eq $oar_key){ exit_myself(13,"ERROR: the user has specified the same ssh key than used by the user oar"); } $out .= $_; }elsif ($_ =~ /environment=\"OAR_CPUSET=([\w\/]+)\"/){ # Remove from authorized keys outdated keys (typically after a reboot) if (-d "/dev/cpuset/$1"){ $out .= $_; } }else{ $out .= $_; } } if (!(seek(PUB,0,0) and print(PUB $out) and truncate(PUB,tell(PUB)))){ exit_myself(9,"Error writing $Cpuset->{ssh_keys}->{public}->{file_name}"); } flock(PUB,LOCK_UN) or exit_myself(17,"flock failed: $!"); close(PUB); }else{ unlink($Cpuset->{ssh_keys}->{private}->{file_name}); exit_myself(10,"Error opening $Cpuset->{ssh_keys}->{public}->{file_name}"); } } }elsif ($ARGV[0] eq "clean"){ # delete ssh key files if ($Cpuset->{ssh_keys}->{private}->{key} ne ""){ # private key unlink($Cpuset->{ssh_keys}->{private}->{file_name}); if (defined($Cpuset->{job_uid})){ unlink("$Cpuset->{oar_tmp_directory}/$Cpuset->{job_user}.jobkey"); } # public key if (open(PUB,"+<", $Cpuset->{ssh_keys}->{public}->{file_name})){ flock(PUB,LOCK_EX) or exit_myself(17,"flock failed: $!"); seek(PUB,0,0) or exit_myself(18,"seek failed: $!"); #Change file on the fly my $out = ""; while (){ if (($_ ne "\n") and ($_ ne $Cpuset->{ssh_keys}->{public}->{key})){ $out .= $_; } } if (!(seek(PUB,0,0) and print(PUB $out) and truncate(PUB,tell(PUB)))){ exit_myself(12,"Error changing $Cpuset->{ssh_keys}->{public}->{file_name}"); } flock(PUB,LOCK_UN) or exit_myself(17,"flock failed: $!"); close(PUB); }else{ exit_myself(11,"Error opening $Cpuset->{ssh_keys}->{public}->{file_name}"); } } # Clean cpuset on this node if (defined($Cpuset_path_job)){ system('PROCESSES=$(cat /dev/cpuset/'.$Cpuset_path_job.'/tasks) while [ "$PROCESSES" != "" ] do oardodo kill -9 $PROCESSES PROCESSES=$(cat /dev/cpuset/'.$Cpuset_path_job.'/tasks) done' ); if (system('oardodo rmdir /dev/cpuset'.$Cpuset_path_job)){ # Uncomment this line if you want to use several network_address properties # which are the same physical computer (linux kernel) #exit(0); exit_myself(6,"Failed to delete the cpuset $Cpuset_path_job"); } } if (defined($Cpuset->{job_uid})){ my $ipcrm_args=""; if (open(IPCMSG,"< /proc/sysvipc/msg")) { ; while () { if (/\s+\d+\s+(\d+)(?:\s+\d+){5}\s+$Cpuset->{job_uid}(?:\s+\d+){6}$/) { $ipcrm_args .= " -q $1"; } } close (IPCMSG); }else{ exit_myself(14,"Cannot open /proc/sysvipc/msg: $!"); } if (open(IPCSHM,"< /proc/sysvipc/shm")) { ; while () { if (/\s+\d+\s+(\d+)(?:\s+\d+){5}\s+$Cpuset->{job_uid}(?:\s+\d+){6}$/) { $ipcrm_args .= " -m $1"; } } close (IPCSHM); }else{ exit_myself(14,"Cannot open /proc/sysvipc/shm: $!"); } if (open(IPCSEM,"< /proc/sysvipc/sem")) { ; while () { if (/\s+\d+\s+(\d+)(?:\s+\d+){2}\s+$Cpuset->{job_uid}(?:\s+\d+){5}$/) { $ipcrm_args .= " -s $1"; } } close (IPCSEM); }else{ exit_myself(14,"Cannot open /proc/sysvipc/sem: $!"); } if ($ipcrm_args) { print_log(3,"Purging SysV IPC: ipcrm $ipcrm_args"); if(system("oardodo ipcrm $ipcrm_args")){ exit_myself(14,"Failed to purge IPC: ipcrm $ipcrm_args"); } } print_log(3,"Purging /tmp..."); #system("oardodo find /tmp/ -user $Cpuset->{job_user} -exec rm -rfv {} \\;"); system("oardodo find /tmp/. -user $Cpuset->{job_user} -delete"); system("oardodo /usr/sbin/userdel -f $Cpuset->{job_user}"); } }else{ exit_myself(3,"Bad command line argument $ARGV[0]"); } exit(0); # Print error message and exit sub exit_myself($$){ my $exit_code = shift; my $str = shift; warn("[job_resource_manager][$Cpuset->{job_id}][ERROR] ".$str."\n"); exit($exit_code); } # Print log message depending on the LOG_LEVEL config value sub print_log($$){ my $l = shift; my $str = shift; if ($l <= $Log_level){ print("[job_resource_manager][$Cpuset->{job_id}][DEBUG] $str\n"); } } ./oar-2.5.2/sources/core/tools/job_resource_manager.pl0000644000175000017500000004025511757171206021052 0ustar plbplb# $Id$ # # The job_resource_manager script is a perl script that oar server deploys on nodes # to manage cpusets, users, job keys, ... # # Usage: # This script is deployed from the server and executed as oar on the nodes # ARGV[0] can have two different values: # - "init" : then this script must create the right cpuset and assign # corresponding cpus # - "clean" : then this script must kill all processes in the cpuset and # clean the cpuset structure # TAKTUK_HOSTNAME environment variable must be defined and must be a name # that we will be able to find in the transfered hashtable ($Cpuset variable). use Fcntl ':flock'; #use Data::Dumper; sub exit_myself($$); sub print_log($$); my $Old_umask = sprintf("%lo",umask()); umask(oct("022")); my $Cpuset; my $Log_level; my $Cpuset_lock_file = "$ENV{HOME}/cpuset.lock."; # Retrieve parameters from STDIN in the "Cpuset" structure which looks like: # $Cpuset = { # job_id => id of the corresponding job # name => "cpuset name", # cpuset_path => "relative path in the cpuset FS", # nodes => hostname => [array with the content of the database cpuset field] # ssh_keys => { # public => { # file_name => "~oar/.ssh/authorized_keys" # key => "public key content" # } # private => { # file_name => "directory where to store the private key" # key => "private key content" # } # } # oar_tmp_directory => "path to the temp directory" # user => "user name" # job_user => "job user" # job_uid => "job uid for the job_user if needed" # types => hashtable with job types as keys # log_level => debug level number # } my $tmp = ""; while (){ $tmp .= $_; } $Cpuset = eval($tmp); if (!defined($Cpuset->{log_level})){ exit_myself(2,"Bad SSH hashtable transfered"); } $Log_level = $Cpuset->{log_level}; my $Cpuset_path_job; my @Cpuset_cpus; # Get the data structure only for this node if (defined($Cpuset->{cpuset_path})){ $Cpuset_path_job = $Cpuset->{cpuset_path}.'/'.$Cpuset->{name}; @Cpuset_cpus = @{$Cpuset->{nodes}->{$ENV{TAKTUK_HOSTNAME}}}; } print_log(3,"$ARGV[0]"); if ($ARGV[0] eq "init"){ # Initialize cpuset for this node # First, create the tmp oar directory if (!(((-d $Cpuset->{oar_tmp_directory}) and (-O $Cpuset->{oar_tmp_directory})) or (mkdir($Cpuset->{oar_tmp_directory})))){ exit_myself(13,"Directory $Cpuset->{oar_tmp_directory} does not exist and cannot be created"); } if (defined($Cpuset->{job_uid})){ my $prevuser = getpwuid($Cpuset->{job_uid}); system("oardodo /usr/sbin/userdel -f $prevuser") if (defined($prevuser)); my @tmp = getpwnam($Cpuset->{user}); if ($#tmp < 0){ exit_myself(15,"Cannot get information from user '$Cpuset->{user}'"); } if (system("oardodo /usr/sbin/adduser --disabled-password --gecos 'OAR temporary user' --no-create-home --force-badname --quiet --home $tmp[7] --gid $tmp[3] --shell $tmp[8] --uid $Cpuset->{job_uid} $Cpuset->{job_user}")){ exit_myself(15,"Failed to create $Cpuset->{job_user} with uid $Cpuset->{job_uid} and home $tmp[7] and group $tmp[3] and shell $tmp[8]"); } } if (defined($Cpuset_path_job)){ if (open(LOCKFILE,"> $Cpuset->{oar_tmp_directory}/job_manager_lock_file")){ flock(LOCKFILE,LOCK_EX) or exit_myself(17,"flock failed: $!"); if (system('oardodo grep " /dev/cpuset " /proc/mounts > /dev/null 2>&1')){ if (system('oardodo mkdir -p /dev/cpuset && oardodo mount -t cpuset none /dev/cpuset')){ exit_myself(4,"Failed to mount cpuset pseudo filesystem"); } } if (!(-d '/dev/cpuset/'.$Cpuset->{cpuset_path})){ if (system( 'oardodo mkdir -p /dev/cpuset/'.$Cpuset->{cpuset_path}.' &&'. 'oardodo chown -R oar /dev/cpuset/'.$Cpuset->{cpuset_path}.' &&'. '/bin/echo 0 | cat > /dev/cpuset/'.$Cpuset->{cpuset_path}.'/notify_on_release && '. '/bin/echo 0 | cat > /dev/cpuset/'.$Cpuset->{cpuset_path}.'/cpu_exclusive && '. 'cat /dev/cpuset/mems > /dev/cpuset/'.$Cpuset->{cpuset_path}.'/mems &&'. 'cat /dev/cpuset/cpus > /dev/cpuset/'.$Cpuset->{cpuset_path}.'/cpus' )){ exit_myself(4,"Failed to create cpuset $Cpuset->{cpuset_path}"); } } flock(LOCKFILE,LOCK_UN) or exit_myself(17,"flock failed: $!"); close(LOCKFILE); }else{ exit_myself(16,"Failed to open or create $Cpuset->{oar_tmp_directory}/job_manager_lock_file"); } # Be careful with the physical_package_id. Is it corresponding to the memory banc? if (system( 'oardodo mkdir -p /dev/cpuset/'.$Cpuset_path_job.' && '. 'oardodo chown -R oar /dev/cpuset/'.$Cpuset_path_job.' && '. '/bin/echo 0 | cat > /dev/cpuset/'.$Cpuset_path_job.'/notify_on_release && '. '/bin/echo 0 | cat > /dev/cpuset/'.$Cpuset_path_job.'/cpu_exclusive && '. 'cat /dev/cpuset/mems > /dev/cpuset/'.$Cpuset_path_job.'/mems && '. '/bin/echo '.join(",",@Cpuset_cpus).' | cat > /dev/cpuset/'.$Cpuset_path_job.'/cpus' )){ #'MEM= ;for c in '."@Cpuset_cpus".';do MEM=$(cat /sys/devices/system/cpu/cpu$c/topology/physical_package_id),$MEM; done; echo $MEM > /dev/cpuset/'.$Cpuset_path_job.'/mems && '. #'MEM= ;for c in '."@Cpuset_cpus".';do for n in /sys/devices/system/node/node* ;do if [ -r "$n/cpu$c" ]; then MEM=$(basename $n | sed s/node//g),$MEM; fi; done; done;echo $MEM > /dev/cpuset/'.$Cpuset_path_job.'/mems && '. exit_myself(5,"Failed to create and feed the cpuset $Cpuset_path_job"); } } # Copy ssh key files if ($Cpuset->{ssh_keys}->{private}->{key} ne ""){ # private key if (open(PRIV, ">".$Cpuset->{ssh_keys}->{private}->{file_name})){ chmod(0600,$Cpuset->{ssh_keys}->{private}->{file_name}); if (!print(PRIV $Cpuset->{ssh_keys}->{private}->{key})){ unlink($Cpuset->{ssh_keys}->{private}->{file_name}); exit_myself(8,"Error writing $Cpuset->{ssh_keys}->{private}->{file_name}"); } close(PRIV); if (defined($Cpuset->{job_uid})){ system("ln -s $Cpuset->{ssh_keys}->{private}->{file_name} $Cpuset->{oar_tmp_directory}/$Cpuset->{job_user}.jobkey"); } }else{ exit_myself(7,"Error opening $Cpuset->{ssh_keys}->{private}->{file_name}"); } # public key if (open(PUB,"+<",$Cpuset->{ssh_keys}->{public}->{file_name})){ flock(PUB,LOCK_EX) or exit_myself(17,"flock failed: $!"); seek(PUB,0,0) or exit_myself(18,"seek failed: $!"); my $out = "\n".$Cpuset->{ssh_keys}->{public}->{key}."\n"; while (){ if ($_ =~ /environment=\"OAR_KEY=1\"/){ # We are reading a OAR key $_ =~ /(ssh-dss|ssh-rsa)\s+([^\s^\n]+)/; my $oar_key = $2; $Cpuset->{ssh_keys}->{public}->{key} =~ /(ssh-dss|ssh-rsa)\s+([^\s^\n]+)/; my $curr_key = $2; if ($curr_key eq $oar_key){ exit_myself(13,"ERROR: the user has specified the same ssh key than used by the user oar"); } $out .= $_; }elsif ($_ =~ /environment=\"OAR_CPUSET=([\w\/]+)\"/){ # Remove from authorized keys outdated keys (typically after a reboot) if (-d "/dev/cpuset/$1"){ $out .= $_; } }else{ $out .= $_; } } if (!(seek(PUB,0,0) and print(PUB $out) and truncate(PUB,tell(PUB)))){ exit_myself(9,"Error writing $Cpuset->{ssh_keys}->{public}->{file_name}"); } flock(PUB,LOCK_UN) or exit_myself(17,"flock failed: $!"); close(PUB); }else{ unlink($Cpuset->{ssh_keys}->{private}->{file_name}); exit_myself(10,"Error opening $Cpuset->{ssh_keys}->{public}->{file_name}"); } } }elsif ($ARGV[0] eq "clean"){ # delete ssh key files if ($Cpuset->{ssh_keys}->{private}->{key} ne ""){ # private key unlink($Cpuset->{ssh_keys}->{private}->{file_name}); if (defined($Cpuset->{job_uid})){ unlink("$Cpuset->{oar_tmp_directory}/$Cpuset->{job_user}.jobkey"); } # public key if (open(PUB,"+<", $Cpuset->{ssh_keys}->{public}->{file_name})){ flock(PUB,LOCK_EX) or exit_myself(17,"flock failed: $!"); seek(PUB,0,0) or exit_myself(18,"seek failed: $!"); #Change file on the fly my $out = ""; while (){ if (($_ ne "\n") and ($_ ne $Cpuset->{ssh_keys}->{public}->{key})){ $out .= $_; } } if (!(seek(PUB,0,0) and print(PUB $out) and truncate(PUB,tell(PUB)))){ exit_myself(12,"Error changing $Cpuset->{ssh_keys}->{public}->{file_name}"); } flock(PUB,LOCK_UN) or exit_myself(17,"flock failed: $!"); close(PUB); }else{ exit_myself(11,"Error opening $Cpuset->{ssh_keys}->{public}->{file_name}"); } } # Clean cpuset on this node if (defined($Cpuset_path_job)){ system('PROCESSES=$(cat /dev/cpuset/'.$Cpuset_path_job.'/tasks) while [ "$PROCESSES" != "" ] do oardodo kill -9 $PROCESSES PROCESSES=$(cat /dev/cpuset/'.$Cpuset_path_job.'/tasks) done' ); # Locking around the cleanup of the cpuset for that user, to prevent a creation to occure at the same time # which would allow race condition for the dirty-user-based clean-up mechanism if (open(LOCK,">", $Cpuset_lock_file.$Cpuset->{user})){ flock(LOCK,LOCK_EX) or die "flock failed: $!\n"; if (system('oardodo rmdir /dev/cpuset'.$Cpuset_path_job)){ # Uncomment this line if you want to use several network_address properties # which are the same physical computer (linux kernel) #exit(0); exit_myself(6,"Failed to delete the cpuset $Cpuset_path_job"); } if (not defined($Cpuset->{job_uid})){ # dirty-user-based cleanup: do cleanup only if that is the last job of the user on that host. my @cpusets = (); if (opendir(DIR, "/dev/cpuset/".$Cpuset->{cpuset_path}.'/')) { @cpusets = grep { /^$Cpuset->{user}_\d+$/ } readdir(DIR); closedir DIR; } else { exit_myself(18,"Can't opendir: /dev/cpuset/$Cpuset->{cpuset_path}"); } if ($#cpusets < 0) { # No other jobs on this node at this time my $useruid=getpwnam($Cpuset->{user}); my $ipcrm_args=""; if (open(IPCMSG,"< /proc/sysvipc/msg")) { ; while () { if (/^\s*\d+\s+(\d+)(?:\s+\d+){5}\s+$useruid(?:\s+\d+){6}/) { $ipcrm_args .= " -q $1"; print_log(3,"Found IPC MSG for user $useruid: $1."); } } close (IPCMSG); } else { print_log(3,"Cannot open /proc/sysvipc/msg: $!."); } if (open(IPCSHM,"< /proc/sysvipc/shm")) { ; while () { if (/^\s*\d+\s+(\d+)(?:\s+\d+){5}\s+$useruid(?:\s+\d+){6}/) { $ipcrm_args .= " -m $1"; print_log(3,"Found IPC SHM for user $useruid: $1."); } } close (IPCSHM); } else { print_log(3,"Cannot open /proc/sysvipc/shm: $!."); } if (open(IPCSEM,"< /proc/sysvipc/sem")) { ; while () { if (/^\s*[\d\-]+\s+(\d+)(?:\s+\d+){2}\s+$useruid(?:\s+\d+){5}/) { $ipcrm_args .= " -s $1"; print_log(3,"Found IPC SEM for user $useruid: $1."); } } close (IPCSEM); } else { print_log(3,"Cannot open /proc/sysvipc/sem: $!."); } if ($ipcrm_args) { print_log (3,"Purging SysV IPC: ipcrm $ipcrm_args."); system("OARDO_BECOME_USER=$Cpuset->{user} oardodo ipcrm $ipcrm_args"); } print_log (3,"Purging /tmp /dev/shm /var/tmp..."); system("oardodo find /tmp/. /dev/shm/. /var/tmp/. -user $Cpuset->{user} -delete"); } else { print_log(2,"Not purging SysV IPC and files (in /tmp /dev/shm /var/tmp) as $Cpuset->{user} still has a job running on this host."); } } flock(LOCK,LOCK_UN) or die "flock failed: $!\n"; close(LOCK); } } if (defined($Cpuset->{job_uid})){ my $ipcrm_args=""; if (open(IPCMSG,"< /proc/sysvipc/msg")) { ; while () { if (/^\s*\d+\s+(\d+)(?:\s+\d+){5}\s+$Cpuset->{job_uid}(?:\s+\d+){6}$/) { $ipcrm_args .= " -q $1"; } else { print_log(3,"Cannot parse IPC MSG: $_."); } } close (IPCMSG); }else{ exit_myself(14,"Cannot open /proc/sysvipc/msg: $!"); } if (open(IPCSHM,"< /proc/sysvipc/shm")) { ; while () { if (/^\s*\d+\s+(\d+)(?:\s+\d+){5}\s+$Cpuset->{job_uid}(?:\s+\d+){6}$/) { $ipcrm_args .= " -m $1"; } else { print_log(3,"Cannot parse IPC SHM: $_."); } } close (IPCSHM); }else{ exit_myself(14,"Cannot open /proc/sysvipc/shm: $!"); } if (open(IPCSEM,"< /proc/sysvipc/sem")) { ; while () { if (/^\s*\d+\s+(\d+)(?:\s+\d+){2}\s+$Cpuset->{job_uid}(?:\s+\d+){5}$/) { $ipcrm_args .= " -s $1"; } else { print_log(3,"Cannot parse IPC SEM: $_."); } } close (IPCSEM); }else{ exit_myself(14,"Cannot open /proc/sysvipc/sem: $!"); } if ($ipcrm_args) { print_log(3,"Purging SysV IPC: ipcrm $ipcrm_args"); if(system("oardodo ipcrm $ipcrm_args")){ exit_myself(14,"Failed to purge IPC: ipcrm $ipcrm_args"); } } print_log(3,"Purging /tmp /dev/shm /var/tmp ..."); #system("oardodo find /tmp/ -user $Cpuset->{job_user} -exec rm -rfv {} \\;"); system("oardodo find /tmp/. /dev/shm/. /var/tmp/. -user $Cpuset->{job_user} -delete"); system("oardodo /usr/sbin/userdel -f $Cpuset->{job_user}"); } }else{ exit_myself(3,"Bad command line argument $ARGV[0]"); } exit(0); # Print error message and exit sub exit_myself($$){ my $exit_code = shift; my $str = shift; warn("[job_resource_manager][$Cpuset->{job_id}][ERROR] ".$str."\n"); exit($exit_code); } # Print log message depending on the LOG_LEVEL config value sub print_log($$){ my $l = shift; my $str = shift; if ($l <= $Log_level){ print("[job_resource_manager][$Cpuset->{job_id}][DEBUG] $str\n"); } } ./oar-2.5.2/sources/core/tools/initd_oar.sh0000644000175000017500000000112311757171206016635 0ustar plbplb#!/bin/bash # $Id$ # script for mandrake # Source function library. . /etc/rc.d/init.d/functions RETVAL=0 # See how we were called. case "$1" in start) su - oar -c "/usr/bin/oarnodesetting -s Alive" touch /var/lock/subsys/oar echo ;; stop) su - oar -c "/usr/bin/oarnodesetting -s Absent" rm -f /var/lock/subsys/oar echo ;; kill) /usr/bin/oarnodesetting -s Absent rm -f /var/lock/subsys/oar echo ;; *) gprintf "*** Usage: oar {start|stop|kill}\n" exit 1 esac exit $RETVAL ./oar-2.5.2/sources/core/tools/graph_up_nodes.pl0000644000175000017500000000315611757171206017673 0ustar plbplb#!/usr/bin/perl # Simple script to graph the number of nodes that are not Absent # (supposing "not in energy saving standby") depending on the time # Result can be plotted using gnuplot: #gnuplot> set style fill solid 1.00 border #gnuplot> set xdata time #gnuplot> set timefmt "%s" #gnuplot> set yrange [0:] #gnuplot> plot '/tmp/up_nodes.txt' using 1:2 with boxes use strict; my $total_nodes=48; my $oar_db="oar"; my $oar_user="oar"; my $oar_passwd="oar"; if (not defined($ARGV[2])) { print "usage: graph_up_nodes.pl \n"; exit 1; } my $file=`mktemp`; `mysql -N -p$oar_passwd $oar_db -u$oar_user -e "select distinct network_address,date_start,date_stop from resource_logs,resources where value=\\"Absent\\" and resource_logs.resource_id=resources.resource_id and date_start <= $ARGV[1] and (date_stop > $ARGV[0] or date_stop=0) order by date_start" > $file`; # to be accurate, we should filter here the nodes that are Absent, but not # in standby (available_upto < now) open(FILE,"$file"); my %absent; foreach () { my @array; (my $node,@array[0],@array[1]) = split; if (@array[1]==0) {@array[1]=time()}; if(not defined($absent{$node})) {$absent{$node}=[]}; push(@{$absent{$node}},\@array); } for (my $timestamp = $ARGV[0]; $timestamp <= $ARGV[1]; $timestamp+=$ARGV[2]) { my $absent_nodes=0; foreach my $node (keys(%absent)) { foreach my $arr (@{$absent{$node}}) { if ($timestamp > @{$arr}[0] && $timestamp < @{$arr}[1]) { $absent_nodes++ ."\n"; } } } print "$timestamp ". ($total_nodes - $absent_nodes) ."\n"; } unlink $file; ./oar-2.5.2/sources/core/tools/failure_detector_agent.pl0000755000175000017500000001405011757171206021372 0ustar plbplb#!/usr/bin/perl # $Id$ use IO::Socket::INET; use Getopt::Long; use POSIX qw(strftime :sys_wait_h); $SIG{INT} = 'IGNORE'; ############################################################################### # Global variables ############################################################################### # Socket port opened when everything works my $Socket_port = 8888; # Timeout of the global test execution my $Timeout = 10; # Time between each check campaign my $Check_interval = 10; # Number of // connections accepted my $Socket_max_connections = 10; ############################################################################### # Initialization part ############################################################################### Getopt::Long::Configure ("gnu_getopt"); my $sos; GetOptions ("help|h" => \$sos, "port|p=i" => \$Socket_port, "timeout|t=i" => \$Timeout, "check_interval|c=i" => \$Check_interval ); if (defined($sos) or ($#ARGV < 0)){ print < 0); close_socket(); print("Exit normally\n"); exit(0); } $SIG{INT} = \&sig_handler; $SIG{TERM} = \&sig_handler; ############################################################################### # Test part ############################################################################### # Permit to display directly if the socket could not be opened (when user test # it interactively) open_socket($Socket_port, $Socket_max_connections); close_socket(); while (1){ # specify if we must exit from the loop immediately my $end_test_cmds = 0; my $i = 0; while (($i <= $#ARGV) and ($end_test_cmds == 0)){ # user command to launch my $cmd = $ARGV[$i]; print("[".strftime("%F %T", localtime)."] Launch command : $cmd\n"); my $exit_status = 0; $cmd_pid = fork(); if (!defined($cmd_pid)){ warn("[SYSTEM-ERROR] Cannot fork a process to launch the command : $cmd. So we close the socket.\n"); $end_test_cmds = 1; close_socket(); }elsif ($cmd_pid == 0){ #CHILD exec($cmd); warn("[ERROR] Cannot find $cmd\n"); exit(-1); }else{ # Wait the end of the user command or the timeout my $initial_time = time; my $result_wait = 0; while (($result_wait != $cmd_pid) and (time - $initial_time <= $Timeout)){ # Also avoid zombies if (($result_wait = waitpid(-1, WNOHANG)) > 0){ $exit_status = $?; } select(undef,undef,undef,0.5); } if ($result_wait != $cmd_pid){ print("[".strftime("%F %T", localtime)."] [ERROR] Timeout ($Timeout s) of the command '$cmd' SO we close the socket.\n"); kill('SIGKILL', $cmd_pid); close_socket(); $end_test_cmds = 1; }elsif($exit_status != 0){ print("[".strftime("%F %T", localtime)."] [ERROR] The command '$cmd' has returned an exit code != 0 ($exit_status) SO we close the socket.\n"); close_socket(); $end_test_cmds = 1; } } $cmd_pid = 0; # Go to the next user command $i++; } if ($end_test_cmds == 0){ print("[".strftime("%F %T", localtime)."] [SUCCESS] Tests are a success so we open the socket.\n"); open_socket($Socket_port, $Socket_max_connections); } print("[".strftime("%F %T", localtime)."] We are waiting $Check_interval s before the next check.\n"); sleep($Check_interval); } ############################################################################### # Internal functions ############################################################################### sub open_socket($$){ my $server_port = shift; my $server_max_connect = shift; if ($Socket_process_pid == 0){ $Socket_process_pid = manage_socket($server_port, $server_max_connect) ; }else{ warn("[INFO] Socket already opened.\n"); } } sub close_socket(){ if ($Socket_process_pid != 0){ kill('SIGINT', $Socket_process_pid); #waitpid($Socket_process_pid,0); $Socket_process_pid = 0; }else{ warn("[INFO] Socket already closed.\n"); } } sub manage_socket($$){ my $server_port = shift; my $server_max_connect = shift; my $server = IO::Socket::INET->new(LocalPort=> $server_port, Type => SOCK_STREAM, Reuse => 1, Listen => $server_max_connect); if (!defined($server)){ warn("[SYSTEM-ERROR] Cannot open a socket on the port $server_port.\n"); return(undef); } my $pid = fork(); if (!defined($pid)){ warn("[SYSTEM-ERROR] Cannot fork a process to listen on the socket.\n"); return(undef); }elsif ($pid == 0){ #CHILD $SIG{INT} = sub {$server->close()}; while (my $client=$server->accept()){ $client->close(); } exit(0); } return($pid); } ./oar-2.5.2/sources/core/tools/detect_resources0000755000175000017500000000607111757171206017630 0ustar plbplb#!/bin/bash # $Id$ # Detect resources on a list of nodes and print corresponding OAR commands #set -x OPENSSH_CMD=/usr/bin/ssh . "$OARCONFFILE" || exit 20 OARNODES="oarnodesetting" OARPROPERTY="oarproperty" FILE_WITH_OAR_COMMANDS_GENERATED="/tmp/oar_resources_init.txt" # check the file with the nodes to add if [ -z "$1" ] || [ ! -r "$1" ] then echo "Specify a file containing the list of nodes to add into OAR in argument of this command (must be readable by oar user)" exit 1 fi echo "Did you configured the OAR SSH key on all the nodes? [yes/NO] " read l ; [ "$l" != "yes" ] && exit 2 # Check output file if [ -e "$FILE_WITH_OAR_COMMANDS_GENERATED" ] then echo "Commands to run will be written into "$FILE_WITH_OAR_COMMANDS_GENERATED". Do you want to override it? [yes/NO] " read l ; [ "$l" != "yes" ] && exit 3 rm $FILE_WITH_OAR_COMMANDS_GENERATED || exit 4 fi # Add properties echo "$OARPROPERTY -a cpu" > $FILE_WITH_OAR_COMMANDS_GENERATED echo "$OARPROPERTY -a core" >> $FILE_WITH_OAR_COMMANDS_GENERATED echo "$OARPROPERTY -c -a host" >> $FILE_WITH_OAR_COMMANDS_GENERATED echo "$OARPROPERTY -a cpuset" >> $FILE_WITH_OAR_COMMANDS_GENERATED echo "$OARPROPERTY -a mem" >> $FILE_WITH_OAR_COMMANDS_GENERATED # Init CPU id CPU=$(oarnodes --sql true | grep 'cpu=' | awk -F " cpu=" '{print $2}' | awk -F "," '{print $1}' | sort -r -n | head -1) [ -z "$CPU" ] && CPU=0 # Init CORE id CORE=$(oarnodes --sql true | grep 'core=' | awk -F " core=" '{print $2}' | awk -F "," '{print $1}' | sort -r -n | head -1) [ -z "$CORE" ] && CORE=0 echo for host in $(< $1) do echo -n "Checking $host ... " # Store stderr and redirect it to /dev/null exec 3>&2 exec 2> /dev/null mem=$($OPENSSH_CMD $host cat /proc/meminfo | grep ^MemTotal | awk '{print $2}') # Get CORE info in the form: # core:cpu # core:cpu # core:cpu # ... if [ "`$OPENSSH_CMD $host cat /proc/cpuinfo|grep 'physical id'`" != "" ] then DATA=$($OPENSSH_CMD $host cat /proc/cpuinfo| awk '{if ($1 == "processor") core=$3; if ($1 == "physical" && $2 =="id") print $4 ":" core}'|sort -n) else DATA=$($OPENSSH_CMD $host cat /proc/cpuinfo| awk '{if ($1 == "processor") print $3 ":" $3}'|sort -n) fi # Restore stderr exec 2>&3 if [ -z "$mem" ] || [ -z "$DATA" ] then echo "*** KO ***" rm $FILE_WITH_OAR_COMMANDS_GENERATED echo echo "ERROR with node $host. Please check the ssh connection with oar user ($OPENSSH_CMD $host)..." exit 5 fi ((mem = $mem / 1024)) prev_cpu="-1" for i in $DATA do core=$(echo $i|awk -F: '{print $2}') cpu=$(echo $i|awk -F: '{print $1}') [ "$cpu" != "$prev_cpu" ] && ((CPU++)) ((CORE++)) prev_cpu=$cpu echo "oarnodesetting -a -h $host -p host=$host -p cpu=$CPU -p core=$CORE -p cpuset=$core -p mem=$mem" >> $FILE_WITH_OAR_COMMANDS_GENERATED done echo "OK" done echo echo "If the content of '$FILE_WITH_OAR_COMMANDS_GENERATED' is OK for you then you just need to execute:" echo " source $FILE_WITH_OAR_COMMANDS_GENERATED" ./oar-2.5.2/sources/core/tools/deploy_nodes.sh0000755000175000017500000000357211757171206017366 0ustar plbplb#!/bin/bash # $Id$ usage() { echo "Usage: `basename $0` [-e ] " echo "Where method := { rsh | ssh | rshp/rsh | rshp/ssh }" exit 1 } # Home brewed getopt OPT=$1 shift while [ -n "$OPT" ]; do case $OPT in -e) [ -z "$METHOD" ] || usage METHOD=$1 shift ;; -H) [ -z "$OARHOME" ] || usage OARHOME=$1 shift ;; -C) [ -z "$OARCONF" ] || usage OARCONF=$1 shift ;; -h) usage ;; *) [ -z $NODE_FILE ] || usage NODE_FILE=$OPT ;; esac OPT=$1 shift done # Is oar home ok ? [ -n "$OARHOME" ] || OARHOME=/var/lib/oar [ -d "$OARHOME" -a -r "$OARHOME" ] || ( echo "Error: oar home not readable ($OARHOME)"; usage ) # Is oar configuration ok ? [ -n "$OARCONF" ] || OARCONF=/etc/oar.conf [ -r "$OARCONF" ] || ( echo "Error: oar configuration file not readable ($OARCONF)"; usage ) # Is node file readable ? [ -n "$NODE_FILE" ] || usage [ -r "$NODE_FILE" ] || ( echo "Error: Can't read node file: $NODE_FILE"; usage ) # Is deployment method ok ? Defaulting to ssh case "$METHOD" in rsh|ssh|rshp/rsh|rshp/ssh) ;; *) [ -n "$METHOD" ] && echo "Warning: unknown method: $METHOD, defaulting to ssh." METHOD="ssh" esac # The real stuff: # deploy oar .ssh directory and oar.conf file using piped tar case "$METHOD" in rsh) for n in `cat $NODE_FILE`; do tar c $OARHOME/.ssh $OARCONF | rsh $n tar x -C / oarnodesetting -h $n -s Alive done ;; ssh) for n in `cat $NODE_FILE`; do tar c $OARHOME/.ssh $OARCONF | ssh -x -o StrictHostKeyChecking=no $n tar x -C / oarnodesetting -h $n -s Alive done ;; rshp/rsh) tar c $OARHOME/.ssh $OARCONF | rshp -d -c rsh -f $NODE_FILE -- tar x -C / for n in `cat $NODE_FILE`; do oarnodesetting -h $n -s Alive done ;; rshp/ssh) tar c $OARHOME/.ssh $OARCONF | rshp -d -c ssh -f $NODE_FILE -- tar x -C / for n in `cat $NODE_FILE`; do oarnodesetting -h $n -s Alive done ;; esac ./oar-2.5.2/sources/core/tools/besteffort/0000755000175000017500000000000011757171206016477 5ustar plbplb./oar-2.5.2/sources/core/tools/besteffort/synchro_sge_oar.rb0000644000175000017500000000300211757171206022203 0ustar plbplb#!/usr/bin/ruby # Besteffort OAR/SGE coupling # This script synchronizes the status of the OAR ressources depending on # the SGE queues status. It uses the XML output of the SGE qstat cmd. require 'rexml/document' # Custom variables $STATUSDIR='/home_nfs/bzizou/oar_status/' $XML_STATUS_CMD='qstat -u "*" -f -xml' $OARNODESETTING_CMD='/usr/local/sbin/oarnodesetting' # Get the qstat output in XML format xml_data=`#{$XML_STATUS_CMD}` # Parse the XML output doc = REXML::Document.new(xml_data) cond="false" doc.elements.each('job_info/queue_info/Queue-List') do |queue| # For each node node=queue.elements["name"].text.split('@')[1] core=0 queue.elements.each('job_list') do |job| # For each job running on the node job_id=job.elements["JB_job_number"].text job_status_file=File.new("#{$STATUSDIR}/#{job_id}","a") job.elements["slots"].text.to_i.times do # Mark "slots" cores as used resources core += 1 resource="#{node}.#{core}" # Update the status file of the job job_status_file.puts(resource) # Touch a file identifing the used resource resource_status_file=File.new("#{$STATUSDIR}/#{resource}","w") resource_status_file.close # Update the SQL condition for oarnodesetting cond += " or (network_address='#{node}' and cpuset=#{core-1})" end job_status_file.close end end # Update the OAR resources if cond != "false" `#{$OARNODESETTING_CMD} -s Alive --sql "besteffort='YES'"` `#{$OARNODESETTING_CMD} -s Absent --sql "#{cond}"` end ./oar-2.5.2/sources/core/tools/besteffort/prologue.sge.pl0000644000175000017500000000400311757171206021442 0ustar plbplb#!/usr/bin/perl # Prologue script for OAR/SGE besteffort coupling # Needs the Epilogue script for the same purpose use strict; #################################################################### # CUSTOMIZATION # Status directory #my $STATUSDIR="/home_nfs/bzizou/oar_status_tests/"; my $STATUSDIR="/home_nfs/bzizou/oar_status/"; # SGE resources #my $PE_HOSTS="/home_nfs/bzizou/test_pe_hosts" ; my $PE_HOSTS="$ENV{SGE_JOB_SPOOL_DIR}/pe_hostfile"; #my $JOB_ID=9999; my $JOB_ID=$ENV{JOB_ID}; # Oarnodesetting cmd #my $OAR_REMOTE_CMD="echo"; my $OAR_REMOTE_CMD="ssh p2chpd-cluster"; my $OARNODESETTING_CMD="/usr/local/sbin/oarnodesetting -s Absent"; ####################################################################### #print "Prologue starting...\n"; open (FILE,$PE_HOSTS); my @NODES=(); close(FILE); my $host; my $sge_weight; my $cond="false"; foreach (@NODES) { ($host,$sge_weight)=split(); my $cpu=1; my $max_cpus=8; # <- TODO: this has to be guessed (qstat -f) my $oar_weight=0; while ($cpu<=$max_cpus && $oar_weight<$sge_weight) { if (!(-r "$STATUSDIR/$host.$cpu")) { # Touch a file corresponding to the OAR resource open(FILE,">","$STATUSDIR/$host.$cpu"); close(FILE); # Put this resource in the Absent state #print "Setting cpuset $cpu on OAR node $host in the Absent state\n"; $cond.=" or (network_address='$host' and cpuset="; $cond.= $cpu - 1; $cond.=")"; #my $CMD="ssh p2chpd-cluster \" /usr/local/sbin/oarnodesetting -s Absent --sql \\\"network_address='$host' and cpuset="; #$CMD.= $cpu - 1 ."\\\" \""; #print $CMD."\n"; #`$CMD`; # Update the status file that will be used by the epilogue script open (FILE,">>","$STATUSDIR/$JOB_ID"); printf FILE "$host:$cpu\n"; close(FILE); $oar_weight++; } $cpu++; } } # Send the command if ("$cond" ne "false") { my $CMD="$OAR_REMOTE_CMD \"$OARNODESETTING_CMD --sql \\\"$cond\\\"\""; #print $CMD; `$CMD` } #print "Prologue end.\n"; exit 0; ./oar-2.5.2/sources/core/tools/besteffort/prologue.pbs.pl0000644000175000017500000000520411757171206021454 0ustar plbplb#!/usr/bin/perl # Prologue script for OAR/PBS besteffort coupling # Needs the Epilogue script for the same purpose use strict; #################################################################### # CUSTOMIZATION # Configuration variables my $PBSNODES_CMD="/usr/pbs/bin/pbsnodes -v"; my @NODES=("healthphy[1]", "healthphy[2]", "healthphy[3]", "healthphy[4]", "healthphy[5]", "healthphy[6]", "healthphy[7]", "healthphy[8]", "healthphy[9]", "healthphy[10]", "healthphy[11]", "healthphy[12]", "healthphy[13]", "healthphy[14]", "healthphy[15]", "healthphy[16]", "healthphy[17]", "healthphy-xeon1", "healthphy-xeon2", "healthphy-xeon3", "healthphy-xeon4", "healthphy-xeon5", "healthphy-xeon6", "healthphy-xeon7" ); my $OARNODESETTING_CMD="ssh healthphy /usr/local/sbin/oarnodesetting"; # Definition of how PBS resources correspond to OAR resources: my %oar_resource_id; # Healthphy (from core 4 to 71 -> resources 5 to 72): my $oar_id=5; for (my $pbs_node = 1; $pbs_node <= 17; $pbs_node++) { for (my $pbs_id = 0; $pbs_id <= 3; $pbs_id++) { $oar_resource_id{"healthphy[$pbs_node]"}{$pbs_id}=$oar_id; $oar_id++; } } # Healthphy-xeon (-> resources 73 to 100) for (my $pbs_node = 1; $pbs_node <= 7; $pbs_node++) { for (my $pbs_id = 0; $pbs_id <= 3; $pbs_id++) { $oar_resource_id{"healthphy-xeon$pbs_node"}{$pbs_id}=$oar_id; $oar_id++; } } ####################################################################### #print "Prologue starting...\n"; foreach my $node (@NODES) { my $FILE; my $key; my $value; open($FILE,"$PBSNODES_CMD $node|"); my @PBSRESOURCES=(<$FILE>); close($FILE); foreach (@PBSRESOURCES) { ($key,$value)=split(/\s*=\s*/); $key=~s/^\s*//; $key=~s/\s*$//; if ($key eq "jobs") { $value=~s/^\s*//; $value=~s/\s*$//; my @jobs=split(/\s*,\s*/,$value); foreach my $job (@jobs) { chomp($job); (my $j,my $cpu)=split(/\//,$job); if ($j eq $ARGV[0]) { #print "Setting OAR resource $oar_resource_id{$node}{$cpu} to absent state\n"; `$OARNODESETTING_CMD -r $oar_resource_id{$node}{$cpu} -s Absent`; ######### Healthphy specific: my $second_resource=sprintf("1%02i",$oar_resource_id{$node}{$cpu}); `$OARNODESETTING_CMD -r $second_resource -s Absent`; ######### } } } } } #print "Prologue end.\n"; ./oar-2.5.2/sources/core/tools/besteffort/epilogue.sge.pl0000755000175000017500000000264611757171206021435 0ustar plbplb#!/usr/bin/perl # Epilogue script for OAR/SGE besteffort coupling # Needs the Prologue script for the same purpose use strict; #################################################################### # CUSTOMIZATION # Status directory #my $STATUSDIR="/home_nfs/bzizou/oar_status_tests/"; my $STATUSDIR="/home_nfs/bzizou/oar_status/"; # SGE resources #my $PE_HOSTS="/home_nfs/bzizou/test_pe_hosts" ; my $PE_HOSTS="$ENV{SGE_JOB_SPOOL_DIR}/pe_hostfile"; #my $JOB_ID=9999; my $JOB_ID=$ENV{JOB_ID}; # Oarnodesetting cmd #my $OAR_REMOTE_CMD="echo"; my $OAR_REMOTE_CMD="ssh p2chpd-cluster"; my $OARNODESETTING_CMD="/usr/local/sbin/oarnodesetting -s Alive"; ####################################################################### #print "Epilogue starting...\n"; open (FILE,"$STATUSDIR/$JOB_ID"); my @NODES=(); close(FILE); my $host, my $cpu; my $cond="false"; foreach (@NODES) { ($host,$cpu)=split(/:/); $cond.=" or (network_address='$host' and cpuset="; $cond.= $cpu - 1; $cond.=")"; #my $CMD="ssh p2chpd-cluster \" /usr/local/sbin/oarnodesetting -s Alive --sql \\\"network_address='$host' and cpuset="; #$CMD.= $cpu - 1 ."\\\" \""; #print $CMD."\n"; #`$CMD`; `rm -f $STATUSDIR/$host.$cpu`; } # Send the command if ("$cond" ne "false") { my $CMD="$OAR_REMOTE_CMD \"$OARNODESETTING_CMD --sql \\\"$cond\\\"\""; #print $CMD; `$CMD` } unlink("$STATUSDIR/$JOB_ID"); #print "Prologue end.\n"; exit 0; ./oar-2.5.2/sources/core/tools/besteffort/epilogue.pbs.pl0000755000175000017500000000517711757171206021445 0ustar plbplb#!/usr/bin/perl # Epilogue script for OAR/PBS besteffort coupling # Needs the Prologue script for the same purpose use strict; #################################################################### # CUSTOMIZATION # Configuration variables my $PBSNODES_CMD="/usr/pbs/bin/pbsnodes -v"; my @NODES=("healthphy[1]", "healthphy[2]", "healthphy[3]", "healthphy[4]", "healthphy[5]", "healthphy[6]", "healthphy[7]", "healthphy[8]", "healthphy[9]", "healthphy[10]", "healthphy[11]", "healthphy[12]", "healthphy[13]", "healthphy[14]", "healthphy[15]", "healthphy[16]", "healthphy[17]", "healthphy-xeon1", "healthphy-xeon2", "healthphy-xeon3", "healthphy-xeon4", "healthphy-xeon5", "healthphy-xeon6", "healthphy-xeon7" ); my $OARNODESETTING_CMD="ssh healthphy /usr/local/sbin/oarnodesetting"; # Definition of how PBS resources correspond to OAR resources: my %oar_resource_id; # Healthphy (from core 4 to 71 -> resources 5 to 72): my $oar_id=5; for (my $pbs_node = 1; $pbs_node <= 17; $pbs_node++) { for (my $pbs_id = 0; $pbs_id <= 3; $pbs_id++) { $oar_resource_id{"healthphy[$pbs_node]"}{$pbs_id}=$oar_id; $oar_id++; } } # Healthphy-xeon (-> resources 73 to 100) for (my $pbs_node = 1; $pbs_node <= 7; $pbs_node++) { for (my $pbs_id = 0; $pbs_id <= 3; $pbs_id++) { $oar_resource_id{"healthphy-xeon$pbs_node"}{$pbs_id}=$oar_id; $oar_id++; } } ####################################################################### print "Epilogue starting...\n"; foreach my $node (@NODES) { my $FILE; my $key; my $value; open($FILE,"$PBSNODES_CMD $node|"); my @PBSRESOURCES=(<$FILE>); close($FILE); foreach (@PBSRESOURCES) { ($key,$value)=split(/\s*=\s*/); $key=~s/^\s*//; $key=~s/\s*$//; if ($key eq "jobs") { $value=~s/^\s*//; $value=~s/\s*$//; my @jobs=split(/\s*,\s*/,$value); foreach my $job (@jobs) { chomp($job); (my $j,my $cpu)=split(/\//,$job); if ($j eq $ARGV[0]) { print "Setting OAR resource $oar_resource_id{$node}{$cpu} to alive state\n"; `$OARNODESETTING_CMD -r $oar_resource_id{$node}{$cpu} -s Alive`; ######### Healthphy specific: my $second_resource=sprintf("1%02i",$oar_resource_id{$node}{$cpu}); `$OARNODESETTING_CMD -r $second_resource -s Alive`; ######### } } } } } print "Epilogue end.\n"; ./oar-2.5.2/sources/core/server/0000755000175000017500000000000011757171206014502 5ustar plbplb./oar-2.5.2/sources/core/server/sbin/0000755000175000017500000000000011757171206015435 5ustar plbplb./oar-2.5.2/sources/core/server/sbin/oar-server.in0000644000175000017500000000101211757171206020044 0ustar plbplb#! /usr/bin/perl use POSIX; my $pidfile="%%RUNDIR%%/oar-server.pid"; my $AlmightyBin="%%SBINDIR%%/Almighty"; POSIX::setsid or die "setsid: $!"; my $pid = fork (); if ($pid < 0) { die "fork: $!"; } elsif ($pid) { open(PIDFILE,">$pidfile"); print PIDFILE "$pid\n"; close(PIDFILE); exit 0; } chdir "/"; umask 0; foreach (0 .. (POSIX::sysconf (&POSIX::_SC_OPEN_MAX) || 1024)) { POSIX::close $_ } open (STDIN, "/dev/null"); open (STDERR, ">&STDOUT"); exec $AlmightyBin; ./oar-2.5.2/sources/core/server/lib/0000755000175000017500000000000011757171206015250 5ustar plbplb./oar-2.5.2/sources/core/server/lib/OAR/0000755000175000017500000000000011757171206015671 5ustar plbplb./oar-2.5.2/sources/core/server/lib/OAR/WindowForker.pm0000644000175000017500000002113511757171206020651 0ustar plbplbpackage OAR::WindowForker; require Exporter; use strict; use warnings; use POSIX ":sys_wait_h"; use OAR::Conf qw(init_conf get_conf is_conf get_conf_with_default_param); use OAR::Modules::Judas qw(oar_debug oar_warn oar_error set_current_log_category); use IPC::SysV qw(IPC_NOWAIT); use Data::Dumper; use OAR::IO; # Log category set_current_log_category('WindowForker'); # Declaration of the named pipe used by Hulot module my $FIFO="/tmp/oar_hulot_pipe"; my $NOTIFY_HULOT = 1; my $USE_TIME = 1; unless (eval "use Time::HiRes qw(gettimeofday tv_interval);1"){ $USE_TIME = 0; } my $DEFAULT_WINDOW_SIZE = 5; my $DEFAULT_TIMEOUT = 30; select STDOUT; $| = 1; # Treate finished processes sub register_wait_results($$$$$$$){ my ($pid, $return_code, $running_processes, $process_duration, $finished_processes, $nb_running_processes, $verbose) = @_; my $exit_value = $return_code >> 8; my $signal_num = $return_code & 127; my $dumped_core = $return_code & 128; if ($pid > 0){ if (defined($running_processes->{$pid})){ $process_duration->{$running_processes->{$pid}}->{"end"} = [gettimeofday()] if ($USE_TIME == 1); warn("[VERBOSE] Child process $pid ended : exit_value = $exit_value, signal_num = $signal_num, dumped_core = $dumped_core \n") if ($verbose); $finished_processes->{$running_processes->{$pid}} = [$exit_value,$signal_num,$dumped_core]; delete($running_processes->{$pid}); $$nb_running_processes--; } } } ## launch # Input parameters: # - commands (ref on an array) # - window size (scalar) # - window time (scalar) # - timeout (scalar) # - verbose (0 or 1) # - type of task (ref on a hash). Default is : %hash = ("type" => "default") sub launch($$$$$$){ my ($commands, $window_size, $window_time, $timeout, $verbose, $type) = @_; my $index = 0; my %running_processes; my $nb_running_processes = 0; my %finished_processes; my %process_duration; my $nextWindowTime = 0 ; my $nb_launching_processes_in_window = 0 ; # Check if there is at least one command to connect to if ($#{$commands} < 0){ warn("/!\\ No command specified\n"); return(\%finished_processes, \%process_duration); } # Check window size integrity if (!defined($window_size)){ $window_size = $DEFAULT_WINDOW_SIZE; }elsif ($window_size < 1){ warn("/!\\ Window size $window_size too small; minimum is 1!\n"); return(\%finished_processes, \%process_duration); } # Check timeout if (!defined($timeout)){ $timeout = $DEFAULT_TIMEOUT; }elsif ($timeout <= 0){ warn("/!\\ Timeout cannot be negative; $timeout\n"); return(\%finished_processes, \%process_duration); } # Check Window time (in seconds) if (!defined($window_time)){ $window_time = 0; }elsif ($window_time < 0){ warn("/!\\ Time between each window cannot be negative; $window_time.\nMinimum is 0 for no limit between each window"); return(\%finished_processes, \%process_duration); } # Check window time integrity with timeout if ($window_time >= $timeout){ warn("/!\\ Time between each window ($window_time sec) must be smaller than timeout ($timeout sec)"); return(\%finished_processes, \%process_duration); } if (!defined($type)){ $type = {"type" => "default"}; $NOTIFY_HULOT = 0; } my %forker_type = %$type; # Check type if (keys(%forker_type)<=0){ oar_error("[WindowForker] No type specified. Set to default type\n"); %forker_type = ("type" => "default"); } warn("[VERBOSE] Window size : $window_size\n") if ($verbose); warn("[VERBOSE] Timeout for each command : $timeout\n") if ($verbose); # Start to launch subprocesses with the window limitation my @timeout; my $pid; while (($index <= $#{$commands}) or ($#timeout >= 0)){ warn("[VERBOSE] ".time." | $index / $#{$commands}\n") if ($verbose); # Check if window is full or not while((($nb_running_processes) < $window_size) and ($index <= $#{$commands})){ # Check if previous window time is finished if((time() >= $nextWindowTime) and ($nb_launching_processes_in_window < $window_size)){ warn("[VERBOSE] ".time." | fork process: $commands->[$index]\n") if ($verbose); $process_duration{$index}->{"start"} = [gettimeofday()] if ($USE_TIME == 1); $pid = fork(); warn("[VERBOSE] ".time." | $pid pid = $pid\n") if ($verbose); if (defined($pid)){ if ($pid == 0){ #In the child warn("[VERBOSE] ".time." | $pid Execute command : $commands->[$index]\n") if ($verbose); if($forker_type{"type"} eq "Hulot"){ # If Hulot request my $command_to_exec=""; (my $cmd, my $node)=split(/:/,$commands->[$index],2); my $base = OAR::IO::connect() or die("[Hulot] Cannot connect to the database\n"); if ($cmd eq "WAKEUP"){ $command_to_exec = "echo \"$node\" | ".get_conf("ENERGY_SAVING_NODE_MANAGER_WAKE_UP_CMD"); OAR::IO::add_new_event_with_host($base,"WAKEUP_NODE",0,"Node $node wake-up request",[$node] ); }elsif ($cmd eq "HALT"){ $command_to_exec = "echo \"$node\" | ".get_conf("ENERGY_SAVING_NODE_MANAGER_SLEEP_CMD"); OAR::IO::add_new_event_with_host($base,"HALT_NODE",0,"Node $node halt request",[$node] ); } OAR::IO::disconnect($base); system($command_to_exec); if (!msgsnd($forker_type{"id_msg"}, pack($forker_type{"template"}, 1, "$node:$cmd:".$?), IPC_NOWAIT)){ oar_error("[WindowForker] Failed to send message to Hulot by msgsnd(): $!\n"); } exit 0; }else{ exec($commands->[$index]); } } else{ $running_processes{$pid} = $index; $nb_running_processes++; push(@timeout, [$pid,time()+$timeout]); $nb_launching_processes_in_window++; if ($nb_launching_processes_in_window >= $window_size){ # This window is full and we will be ready to start a new window once the current window will finish warn("[DEBUG WINDFORKER] [".time."] [$pid] (NB launching = $nb_launching_processes_in_window) This window is full and we will be ready to start a new window once the current window will finish\n") if ($verbose); $nb_launching_processes_in_window=0; $nextWindowTime = time()+$window_time; warn("[DEBUG WINDFORKER] [".time."] [$pid] (NB launching = $nb_launching_processes_in_window) Set new nextWindowTime at $nextWindowTime)\n") if ($verbose); } } }else{ warn("/!\\ fork system call failed for command: $commands->[$index]\n"); } $index++; } } # Check child endings warn("[VERBOSE] ".time." | $pid Check child endings\n") if ($verbose); while(($pid = waitpid(-1, WNOHANG)) > 0) { register_wait_results($pid, $?, \%running_processes, \%process_duration, \%finished_processes, \$nb_running_processes, $verbose); } # Check timeouts (at least every 0.1s) warn("[VERBOSE] ".time." | $pid Check timeouts\n") if ($verbose); my $t = 0; while(defined($timeout[$t]) and (($timeout[$t]->[1] < time()) or (!defined($running_processes{$timeout[$t]->[0]})))){ if (!defined($running_processes{$timeout[$t]->[0]})){ splice(@timeout,$t,1); }else{ if ($timeout[$t]->[1] <= time()){ kill(9,$timeout[$t]->[0]); } } $t++; } select(undef,undef,undef,0.1) if ($t == 0); } if ($NOTIFY_HULOT == 1){ ## Here, send "CHECK signal" to Hulot by the named pipe ? unless (open(FIFO, "> $FIFO")) { oar_error("[WindowForker] Could not open the fifo $FIFO!\n"); return 1; } print FIFO "CHECK"; close(FIFO); } return(\%finished_processes, \%process_duration); } return 1; ./oar-2.5.2/sources/core/server/lib/OAR/Schedulers/0000755000175000017500000000000011757171206017772 5ustar plbplb./oar-2.5.2/sources/core/server/lib/OAR/Schedulers/Scheduler.pm0000644000175000017500000007233511757171206022260 0ustar plbplb# $Id$ package OAR::Schedulers::Scheduler; use Data::Dumper; use strict; use warnings; use OAR::IO; use OAR::Schedulers::GanttHoleStorage; use OAR::Modules::Judas qw(oar_debug oar_warn oar_error set_current_log_category); use OAR::Conf qw(init_conf get_conf is_conf get_conf_with_default_param); # Log category set_current_log_category('scheduler'); init_conf($ENV{OARCONFFILE}); my @Sched_available_suspended_resource_type; my $sched_available_suspended_resource_type_tmp = get_conf("SCHEDULER_AVAILABLE_SUSPENDED_RESOURCE_TYPE"); if (!defined($sched_available_suspended_resource_type_tmp)){ push(@Sched_available_suspended_resource_type, "default"); }else{ @Sched_available_suspended_resource_type = split(" ",$sched_available_suspended_resource_type_tmp); } # Look at resources that we must add for each job my $Resources_to_always_add_type = get_conf("SCHEDULER_RESOURCES_ALWAYS_ASSIGNED_TYPE"); my @Resources_to_always_add = (); # Do we log every scheduler's computation ? my $Log_scheduling = get_conf("SCHEDULER_LOG_DECISIONS"); if (defined($Log_scheduling) and $Log_scheduling ne "yes") { $Log_scheduling = undef; } #minimum of seconds between each jobs my $Security_time_overhead = 1; #minimum of seconds to be considered like a hole in the gantt my $Minimum_hole_time = 0; # waiting time when a reservation has not all of its nodes my $Reservation_waiting_timeout = 300; # global variables : initialized in init_scheduler function my %besteffort_resource_occupation; my $current_time_sec = 0; my $current_time_sql = "0000-00-00 00:00:00"; # Give initial time in second and sql formats in a hashtable. sub get_initial_time(){ my %time = ( "sec" => $current_time_sec, "sql" => $current_time_sql ); return(%time); } #Initialize Gantt tables with scheduled reservation jobs, Running jobs, toLaunch jobs and Launching jobs; # arg1 --> database ref sub init_scheduler($$$$$$){ my $dbh = shift; my $dbh_ro = shift; my $secure_time = shift; my $hole_time = shift; my $order_part = shift; my $resa_admin_waiting_timeout = shift; # First get resources that we must add for each reservation jobs if (defined($Resources_to_always_add_type)){ my $tmp_result_state_resources = OAR::IO::get_specific_resource_states($dbh,$Resources_to_always_add_type); if (defined($tmp_result_state_resources->{"Alive"})){ @Resources_to_always_add = @{$tmp_result_state_resources->{"Alive"}}; oar_debug("[OAR::Schedulers::Scheduler] Resources automatically added to every reservations: @Resources_to_always_add\n"); } } $Reservation_waiting_timeout = $resa_admin_waiting_timeout if (defined($resa_admin_waiting_timeout)); if ($secure_time > 1){ $Security_time_overhead = $secure_time; } if ($hole_time >= 0){ $Minimum_hole_time = $hole_time; } # Take care of the currently (or nearly) running jobs # Lock to prevent bipbip update in same time OAR::IO::lock_table($dbh,["jobs","assigned_resources","gantt_jobs_predictions","gantt_jobs_resources","job_types","moldable_job_descriptions","resources","job_state_logs","gantt_jobs_predictions_log","gantt_jobs_resources_log"]); #calculate now date with no overlap with other jobs my $previous_ref_time_sec = OAR::IO::get_gantt_date($dbh); $current_time_sec = OAR::IO::get_date($dbh); if ($current_time_sec < $previous_ref_time_sec){ # The system is very fast!!! $current_time_sec = $previous_ref_time_sec; } $current_time_sec++; $current_time_sql = OAR::IO::local_to_sql($current_time_sec); my $reservation_already_there = OAR::IO::get_waiting_reservations_already_scheduled($dbh); OAR::IO::gantt_flush_tables($dbh, $reservation_already_there, $Log_scheduling); OAR::IO::set_gantt_date($dbh,$current_time_sec); my @initial_jobs = OAR::IO::get_jobs_in_multiple_states($dbh, ["Running","toLaunch","Launching","Finishing","Suspended","Resuming"]); my $max_resources = 50; #Init the gantt chart with all resources my $vec = ''; foreach my $r (OAR::IO::list_resources($dbh)){ vec($vec,$r->{resource_id},1) = 1; $max_resources = $r->{resource_id} if ($r->{resource_id} > $max_resources); } my $gantt = OAR::Schedulers::GanttHoleStorage::new($max_resources, $Minimum_hole_time); OAR::Schedulers::GanttHoleStorage::add_new_resources($gantt, $vec); # Add already scheduled reservations into the gantt foreach my $resa (keys(%{$reservation_already_there})){ my $vec = ''; foreach my $r (@{$reservation_already_there->{$resa}->{resources}}){ vec($vec, $r, 1) = 1; } OAR::Schedulers::GanttHoleStorage::set_occupation( $gantt, $reservation_already_there->{$resa}->{start_time}, $reservation_already_there->{$resa}->{walltime} + $Security_time_overhead, $vec ); oar_debug("[OAR::Schedulers::Scheduler] init_scheduler : add in gantt already scheduled reservation (moldable id $resa) at $reservation_already_there->{$resa}->{start_time} with walltime=$reservation_already_there->{$resa}->{walltime} on resources @{$reservation_already_there->{$resa}->{resources}}\n"); } foreach my $i (@initial_jobs){ next if ($i->{assigned_moldable_job} == 0); my $mold = OAR::IO::get_current_moldable_job($dbh,$i->{assigned_moldable_job}); # The list of resources on which the job is running my @resource_list = OAR::IO::get_job_current_resources($dbh, $i->{assigned_moldable_job},undef); my $date ; if ($i->{start_time} == 0) { $date = $current_time_sec; }elsif ($i->{start_time} + $mold->{moldable_walltime} < $current_time_sec){ $date = $current_time_sec - $mold->{moldable_walltime}; }else{ $date = $i->{start_time}; } oar_debug("[OAR::Schedulers::Scheduler] init_scheduler : add in gantt job $i->{job_id}\n"); OAR::IO::add_gantt_scheduled_jobs($dbh,$i->{assigned_moldable_job},$date,\@resource_list); # Treate besteffort jobs like nothing! my $types = OAR::IO::get_current_job_types($dbh,$i->{job_id}); if (! defined($types->{besteffort})){ my $job_duration = $mold->{moldable_walltime}; if ($i->{state} eq "Suspended"){ # Remove resources of the type specified in SCHEDULER_AVAILABLE_SUSPENDED_RESOURCE_TYPE @resource_list = OAR::IO::get_job_current_resources($dbh, $i->{assigned_moldable_job},\@Sched_available_suspended_resource_type); } if ($i->{suspended} eq "YES"){ # This job was suspended so we must recalculate the walltime $job_duration += OAR::IO::get_job_suspended_sum_duration($dbh,$i->{job_id},$current_time_sec); } my $vec = ''; foreach my $r (@resource_list){ vec($vec, $r, 1) = 1; } OAR::Schedulers::GanttHoleStorage::set_occupation( $gantt, $date, $job_duration + $Security_time_overhead, $vec ); }else{ #Stock information about besteffort jobs foreach my $j (@resource_list){ $besteffort_resource_occupation{$j} = $i->{job_id}; } } } OAR::IO::unlock_table($dbh); #Add in Gantt reserved jobs already scheduled # my $str_tmp = "state_num ASC"; # if (is_conf("SCHEDULER_NODE_MANAGER_WAKE_UP_CMD")){ # $str_tmp .= ", available_upto DESC"; # } # if (!defined($order_part) or ($order_part eq "")){ # $order_part = $str_tmp; # }else{ # $order_part = "$str_tmp, $order_part"; # } my @Rjobs = OAR::IO::get_waiting_reservation_jobs($dbh); foreach my $job (@Rjobs){ my $job_descriptions = OAR::IO::get_resources_data_structure_current_job($dbh,$job->{job_id}); # For reservation we take the first moldable job my $moldable = $job_descriptions->[0]; next if (defined($reservation_already_there->{$moldable->[2]})); my $available_resources_vector = ''; #my $alive_resources_vector = ''; my @tmp_resource_list; # Get the list of resources where the reservation will be able to be launched push(@tmp_resource_list, OAR::IO::get_resources_in_state($dbh,"Alive")); push(@tmp_resource_list, OAR::IO::get_resources_in_state($dbh,"Suspected")); #push(@tmp_resource_list, OAR::IO::get_resources_in_state($dbh,"Dead")); push(@tmp_resource_list, OAR::IO::get_resources_in_state($dbh,"Absent")); #OAR::Schedulers::GanttHoleStorage::pretty_print($gantt); my $free_resources_vec = OAR::Schedulers::GanttHoleStorage::get_free_resources( $gantt, $job->{start_time}, $moldable->[1] + $Security_time_overhead, ); foreach my $r (@tmp_resource_list){ if (vec($free_resources_vec, $r->{resource_id}, 1) == 1){ vec($available_resources_vector, $r->{resource_id}, 1) = 1; } } # # CM part # if (is_conf("SCHEDULER_NODE_MANAGER_WAKE_UP_CMD")){ # foreach my $r (OAR::IO::get_resources_that_can_be_waked_up($dbh,$job->{start_time} + $moldable->[1] + $Security_time_overhead)){ # if (vec($free_resources_vec, $r->{resource_id}, 1) == 1){ # vec($available_resources_vector, $r->{resource_id}, 1) = 1; # } # } # foreach my $r (OAR::IO::get_resources_that_will_be_out($dbh,$job->{start_time} + $moldable->[1] + $Security_time_overhead)){ # vec($available_resources_vector, $r->{resource_id}, 1) = 0; # } # } # # CM part # else{ # foreach my $r (OAR::IO::get_resources_in_state($dbh,"Absent")){ # if (vec($free_resources_vec, $r->{resource_id}, 1) == 1){ # vec($available_resources_vector, $r->{resource_id}, 1) = 1; # } # } # } my @dead_resources; foreach my $r (OAR::IO::get_resources_in_state($dbh,"Dead")){ push(@dead_resources, $r->{resource_id}); } my $job_properties = "\'1\'"; if ((defined($job->{properties})) and ($job->{properties} ne "")){ $job_properties = $job->{properties}; } my $resource_id_used_list_vector = ''; my @tree_list; foreach my $m (@{$moldable->[0]}){ my $tmp_properties = "\'1\'"; if ((defined($m->{property})) and ($m->{property} ne "")){ $tmp_properties = $m->{property}; } my $tmp_tree; ## Try first with only alive nodes #$tmp_tree = OAR::IO::get_possible_wanted_resources($dbh_ro,$alive_resources_vector,$resource_id_used_list_vector,\@dead_resources,"$job_properties AND $tmp_properties", $m->{resources}, $order_part); #if (!defined($tmp_tree)){ $tmp_tree = OAR::IO::get_possible_wanted_resources($dbh_ro,$available_resources_vector,$resource_id_used_list_vector,\@dead_resources,"$job_properties AND $tmp_properties", $m->{resources}, "".$order_part); #} $tmp_tree = OAR::Schedulers::ResourceTree::delete_unnecessary_subtrees($tmp_tree); push(@tree_list, $tmp_tree); my @leafs = OAR::Schedulers::ResourceTree::get_tree_leafs($tmp_tree); foreach my $l (@leafs){ vec($resource_id_used_list_vector, OAR::Schedulers::ResourceTree::get_current_resource_value($l), 1) = 1; } } # A SUPPRIMER???? my @res_trees; my @resources; foreach my $t (@tree_list){ #my $minimal_tree = OAR::Schedulers::ResourceTree::delete_unnecessary_subtrees($t); push(@res_trees, $t); foreach my $r (OAR::Schedulers::ResourceTree::get_tree_leafs($t)){ push(@resources, OAR::Schedulers::ResourceTree::get_current_resource_value($r)); } } if ($#resources >= 0){ # We can schedule the job my $vec = ''; foreach my $r (@resources){ vec($vec, $r, 1) = 1; } OAR::Schedulers::GanttHoleStorage::set_occupation( $gantt, $job->{start_time}, $moldable->[1] + $Security_time_overhead, $vec ); # Update database push(@resources, @Resources_to_always_add); OAR::IO::add_gantt_scheduled_jobs($dbh,$moldable->[2],$job->{start_time},\@resources); oar_debug("[OAR::Schedulers::Scheduler] Treate waiting reservation $job->{job_id}: add in gantt values\n"); OAR::IO::set_job_message($dbh,$job->{job_id},""); }else{ oar_warn("[OAR::Schedulers::Scheduler] Treate waiting reservation $job->{job_id}: cannot find resources for this reservation, did you remove some resources or change states into Dead???\n"); OAR::IO::set_job_message($dbh,$job->{job_id},"Not able to find resources for this reservation"); } } } # launch right reservation jobs # arg1 : database ref # arg2 : queue name # return 1 if there is at least a job to treate, 2 if besteffort jobs must die sub treate_waiting_reservation_jobs($$){ my $dbh = shift; my $queueName = shift; oar_debug("[OAR::Schedulers::Scheduler] treate_waiting_reservation_jobs: Search for waiting reservations in $queueName queue\n"); my $return = 0; my @arrayJobs = OAR::IO::get_waiting_reservation_jobs_specific_queue($dbh,$queueName); # See if there are reserved jobs to launch foreach my $job (@arrayJobs){ my $job_descriptions = OAR::IO::get_resources_data_structure_current_job($dbh,$job->{job_id}); my $moldable = $job_descriptions->[0]; my $start = $job->{start_time}; my $max = $moldable->[1]; # Test if the job is in the past if ($current_time_sec > $start+$max ){ oar_warn("[OAR::Schedulers::Scheduler] treate_waiting_reservation_jobs : Reservation $job->{job_id} in ERROR\n"); OAR::IO::set_job_state($dbh, $job->{job_id}, "Error"); OAR::IO::set_job_message($dbh,$job->{job_id},"[OAR::Schedulers::Scheduler] Reservation has expired and it cannot be started."); $return = 1; } my @resa_alive_resources = OAR::IO::get_gantt_Alive_resources_for_job($dbh,$moldable->[2]); # test if the job is going to be launched and there is no Alive node if (($#resa_alive_resources < 0) && ($job->{start_time} <= $current_time_sec)){ oar_warn("[OAR::Schedulers::Scheduler] Reservation $job->{job_id} is in waiting mode because no resource is present\n"); OAR::IO::set_gantt_job_startTime($dbh,$moldable->[2],$current_time_sec + 1); }elsif($job->{start_time} <= $current_time_sec){ my @resa_resources = OAR::IO::get_gantt_resources_for_job($dbh,$moldable->[2]); if ($job->{start_time} + $Reservation_waiting_timeout > $current_time_sec){ if ($#resa_resources > $#resa_alive_resources){ # we have not the same number of nodes than in the query --> wait the specified timeout oar_warn("[OAR::Schedulers::Scheduler] Reservation $job->{job_id} is in waiting mode because all nodes are not yet available.\n"); OAR::IO::set_gantt_job_startTime($dbh,$moldable->[2],($current_time_sec + 1)); } }else{ #Check if resources are in Alive state otherwise remove them, the job is going to be launched foreach my $r (@resa_resources){ my $resource_info = OAR::IO::get_resource_info($dbh,$r); if ($resource_info->{state} ne "Alive"){ oar_warn("[OAR::Schedulers::Scheduler] Reservation $job->{job_id}: remove resource $r because it state is $resource_info->{state}\n"); OAR::IO::remove_gantt_resource_job($dbh, $moldable->[2], $r); } } if ($#resa_resources > $#resa_alive_resources){ OAR::IO::add_new_event($dbh,"SCHEDULER_REDUCE_NB_NODES_FOR_RESERVATION",$job->{job_id},"[OAR::Schedulers::Scheduler] Reduce the number of resources for the job $job->{job_id}."); } } } } return($return); } # check for jobs with reservation # arg1 : database ref # arg2 : queue name # return 1 if there is at least a job to treate else 0 sub check_reservation_jobs($$$$){ my $dbh = shift; my $dbh_ro = shift; my $queue_name = shift; my $order_part = shift; oar_debug("[OAR::Schedulers::Scheduler] check_reservation_jobs: Check for new reservation in the $queue_name queue\n"); my $return = 0; #Init the gantt chart with all resources my $max_resources = 50; my $vec = ''; foreach my $r (OAR::IO::list_resources($dbh)){ vec($vec,$r->{resource_id},1) = 1; $max_resources = $r->{resource_id} if ($r->{resource_id} > $max_resources); } my $gantt = OAR::Schedulers::GanttHoleStorage::new($max_resources, $Minimum_hole_time); OAR::Schedulers::GanttHoleStorage::add_new_resources($gantt, $vec); # Find jobs to check my @jobs_to_sched = OAR::IO::get_waiting_toSchedule_reservation_jobs_specific_queue($dbh,$queue_name); if ($#jobs_to_sched >= 0){ # Build gantt diagram of other jobs # Take care of currently scheduled jobs except besteffort jobs if queue_name is not besteffort my ($order, %already_scheduled_jobs) = OAR::IO::get_gantt_scheduled_jobs($dbh); foreach my $i (keys(%already_scheduled_jobs)){ my $types = OAR::IO::get_current_job_types($dbh,$i); if ((! defined($types->{besteffort})) or ($queue_name eq "besteffort")){ my @resource_list = @{$already_scheduled_jobs{$i}->[3]}; my $job_duration = $already_scheduled_jobs{$i}->[1]; if ($already_scheduled_jobs{$i}->[4] eq "Suspended"){ # Remove resources of the type specified in SCHEDULER_AVAILABLE_SUSPENDED_RESOURCE_TYPE @resource_list = OAR::IO::get_job_current_resources($dbh, $already_scheduled_jobs{$i}->[7],\@Sched_available_suspended_resource_type); next if ($#resource_list < 0); } if ($already_scheduled_jobs{$i}->[8] eq "YES"){ # This job was suspended so we must recalculate the walltime $job_duration += OAR::IO::get_job_suspended_sum_duration($dbh,$i,$current_time_sec); } my $vec = ''; foreach my $r (@resource_list){ vec($vec, $r, 1) = 1; } OAR::Schedulers::GanttHoleStorage::set_occupation( $gantt, $already_scheduled_jobs{$i}->[0], $job_duration + $Security_time_overhead, $vec ); } } } foreach my $job (@jobs_to_sched){ my $job_descriptions = OAR::IO::get_resources_data_structure_current_job($dbh,$job->{job_id}); # It is a reservation, we take care only of the first moldable job my $moldable = $job_descriptions->[0]; my $duration = $moldable->[1]; #look if reservation is too old if ($current_time_sec >= ($job->{start_time} + $duration)){ oar_warn("[OAR::Schedulers::Scheduler] check_reservation_jobs: Cancel reservation $job->{job_id}, job is too old\n"); OAR::IO::set_job_message($dbh, $job->{job_id}, "reservation too old"); OAR::IO::set_job_state($dbh, $job->{job_id}, "toError"); }else{ if ($job->{start_time} < $current_time_sec){ $job->{start_time} = $current_time_sec; #OAR::IO::set_running_date_arbitrary($dbh,$job->{job_id},$current_time_sql); } my $available_resources_vector = ''; my @tmp_resource_list; # Get the list of resources where the reservation will be able to be launched push(@tmp_resource_list, OAR::IO::get_resources_in_state($dbh,"Alive")); push(@tmp_resource_list, OAR::IO::get_resources_in_state($dbh,"Absent")); push(@tmp_resource_list, OAR::IO::get_resources_in_state($dbh,"Suspected")); foreach my $r (@tmp_resource_list){ vec($available_resources_vector, $r->{resource_id}, 1) = 1; } my @dead_resources; foreach my $r (OAR::IO::get_resources_in_state($dbh,"Dead")){ push(@dead_resources, $r->{resource_id}); } # my $str_tmp = "state_num ASC"; # if (is_conf("SCHEDULER_NODE_MANAGER_WAKE_UP_CMD")){ # $str_tmp .= ", available_upto DESC"; # } # if (!defined($order_part) or ($order_part eq "")){ # $order_part = $str_tmp; # }else{ # $order_part = "$str_tmp, $order_part"; # } my $job_properties = "\'1\'"; if ((defined($job->{properties})) and ($job->{properties} ne "")){ $job_properties = $job->{properties}; } #my $resource_id_used_list_vector = ''; my @tree_list; foreach my $m (@{$moldable->[0]}){ my $tmp_properties = "\'1\'"; if ((defined($m->{property})) and ($m->{property} ne "")){ $tmp_properties = $m->{property}; } my $tmp_tree = OAR::IO::get_possible_wanted_resources($dbh_ro,$available_resources_vector,undef,\@dead_resources,"$job_properties AND $tmp_properties", $m->{resources}, $order_part); push(@tree_list, $tmp_tree); #my @leafs = OAR::Schedulers::ResourceTree::get_tree_leafs($tmp_tree); #foreach my $l (@leafs){ # vec($resource_id_used_list_vector, OAR::Schedulers::ResourceTree::get_current_resource_value($l), 1) = 1; #} } my @hole = OAR::Schedulers::GanttHoleStorage::find_first_hole($gantt,$job->{start_time}, $duration + $Security_time_overhead, \@tree_list, 30); if ($hole[0] == $job->{start_time}){ # The reservation can be scheduled my @res_trees; my @resources; foreach my $t (@{$hole[1]}){ #my $minimal_tree = OAR::Schedulers::ResourceTree::delete_unnecessary_subtrees($t); push(@res_trees, $t); foreach my $r (OAR::Schedulers::ResourceTree::get_tree_leafs($t)){ push(@resources, OAR::Schedulers::ResourceTree::get_current_resource_value($r)); } } # We can schedule the job oar_warn("[OAR::Schedulers::Scheduler] check_reservation_jobs: Confirm reservation $job->{job_id} and add in gantt (@resources)\n"); my $vec = ''; foreach my $r (@resources){ vec($vec, $r, 1) = 1; } OAR::Schedulers::GanttHoleStorage::set_occupation( $gantt, $job->{start_time}, $duration + $Security_time_overhead, $vec ); # Update database push(@resources, @Resources_to_always_add); OAR::IO::add_gantt_scheduled_jobs($dbh,$moldable->[2],$job->{start_time},\@resources); OAR::IO::set_job_state($dbh, $job->{job_id}, "toAckReservation"); }else{ oar_warn("[OAR::Schedulers::Scheduler] check_reservation_jobs: Cancel reservation $job->{job_id}, not enough nodes\n"); OAR::IO::set_job_state($dbh, $job->{job_id}, "toError"); if ($hole[0] == OAR::Schedulers::GanttHoleStorage::get_infinity_value()){ OAR::IO::set_job_message($dbh, $job->{job_id}, "This reservation cannot be run"); }else{ OAR::IO::set_job_message($dbh, $job->{job_id}, "This reservation may be run at ".OAR::IO::local_to_sql($hole[0])); } } } OAR::IO::set_job_resa_state($dbh, $job->{job_id}, "Scheduled"); $return = 1; } return($return); } # Detect if there are besteffort jobs to kill # arg1 --> database ref # return 1 if there is at least 1 job to frag otherwise 0 sub check_jobs_to_kill($){ my $dbh = shift; oar_debug("[OAR::Schedulers::Scheduler] check_jobs_to_kill: check besteffort jobs\n"); my $return = 0; my %nodes_for_jobs_to_launch = OAR::IO::get_gantt_resources_for_jobs_to_launch($dbh,$current_time_sec); my %fragged_jobs = (); foreach my $r (keys(%nodes_for_jobs_to_launch)){ if (defined($besteffort_resource_occupation{$r})) { oar_debug("[OAR::Schedulers::Scheduler] check_jobs_to_kill: resource $r is needed for job $nodes_for_jobs_to_launch{$r}, besteffort job $besteffort_resource_occupation{$r} must be killed\n"); unless (defined($fragged_jobs{$besteffort_resource_occupation{$r}})) { OAR::IO::add_new_event($dbh,"BESTEFFORT_KILL",$besteffort_resource_occupation{$r},"[OAR::Schedulers::Scheduler] kill the besteffort job $besteffort_resource_occupation{$r}"); OAR::IO::lock_table($dbh,["frag_jobs","event_logs","jobs"]); OAR::IO::frag_job($dbh, $besteffort_resource_occupation{$r}); OAR::IO::unlock_table($dbh); $return = 1; $fragged_jobs{$besteffort_resource_occupation{$r}} = 1; } } } return($return); } # Detect if there are jobs to launch # arg1 --> database ref # return 1 if there is at least 1 job to launch otherwise 0 sub check_jobs_to_launch($){ my $dbh = shift; oar_debug("[OAR::Schedulers::Scheduler] check_jobs_to_launch: check jobs with a start time <= $current_time_sql\n"); my $return_code = 0; my %jobs_to_launch = OAR::IO::get_gantt_jobs_to_launch($dbh,$current_time_sec); foreach my $i (keys(%jobs_to_launch)){ oar_debug("[OAR::Schedulers::Scheduler] check_jobs_to_launch: set job $i in state toLaunch ($current_time_sql)\n"); # We must look at reservations to not go after the initial stop time my $mold = OAR::IO::get_current_moldable_job($dbh,$jobs_to_launch{$i}->[0]); my $job = OAR::IO::get_job($dbh,$i); if (($job->{reservation} eq "Scheduled") and ($job->{start_time} < $current_time_sec)){ my $max_time = $mold->{moldable_walltime} - ($current_time_sec - $job->{start_time}); OAR::IO::set_moldable_job_max_time($dbh,$jobs_to_launch{$i}->[0], $max_time); OAR::IO::set_gantt_job_startTime($dbh,$jobs_to_launch{$i}->[0],$current_time_sec); oar_warn("[OAR::Schedulers::Scheduler] Reduce job ($i) walltime to $max_time instead of $mold->{moldable_walltime}\n"); OAR::IO::add_new_event($dbh,"REDUCE_RESERVATION_WALLTIME",$i,"Change walltime from $mold->{moldable_walltime} to $max_time"); } OAR::IO::set_running_date_arbitrary($dbh,$i,$current_time_sec); OAR::IO::set_assigned_moldable_job($dbh,$i,$jobs_to_launch{$i}->[0]); foreach my $r (@{$jobs_to_launch{$i}->[1]}){ OAR::IO::add_resource_job_pair($dbh,$jobs_to_launch{$i}->[0],$r); } OAR::IO::set_job_state($dbh, $i, "toLaunch"); $return_code = 1; } return($return_code); } #Update gantt visualization tables with new scheduling #arg : database ref sub update_gantt_visu_tables($){ my $dbh = shift; OAR::IO::update_gantt_visualization($dbh); } # Look at nodes that are unused for a duration sub get_idle_nodes($$$){ my $dbh = shift; my $idle_duration = shift; my $sleep_duration = shift; my %nodes = OAR::IO::search_idle_nodes($dbh, $current_time_sec); my $tmp_time = $current_time_sec - $idle_duration; my @res; foreach my $n (keys(%nodes)){ if ($nodes{$n} < $tmp_time){ # Search if the node has enough time to sleep my $tmp = OAR::IO::get_next_job_date_on_node($dbh,$n); if (!defined($tmp) or ($tmp - $sleep_duration > $current_time_sec)){ push(@res, $n); } } } return(@res); } # Get nodes where the scheduler wants to schedule jobs but which is in the # Absent state sub get_nodes_to_wake_up($){ my $dbh = shift; my $wakeup_time = get_conf_with_default_param("SCHEDULER_NODE_MANAGER_WAKEUP_TIME", 1); return(OAR::IO::get_gantt_hostname_to_wake_up($dbh, $current_time_sec, $wakeup_time)); } return(1); ./oar-2.5.2/sources/core/server/lib/OAR/Schedulers/GanttHoleStorage.pm0000644000175000017500000006655511757171206023563 0ustar plbplb# $Id$ package OAR::Schedulers::GanttHoleStorage; require Exporter; use OAR::Schedulers::ResourceTree; use Data::Dumper; use POSIX ":sys_wait_h"; use POSIX qw(strftime); use Storable qw(store_fd fd_retrieve); use warnings; use strict; # Note : All dates are in seconds # Resources are integer so we store them in bit vectors # Warning : this gantt cannot manage overlaping time slots # 2^32 is infinity in 32 bits stored time my $Infinity = 4294967296; # Prototypes # gantt chart management sub new($$); sub new_with_1_hole($$$$$$); sub add_new_resources($$); sub set_occupation($$$$); sub get_free_resources($$$); sub find_first_hole($$$$$); sub pretty_print($); sub get_infinity_value(); ############################################################################### sub get_infinity_value(){ return($Infinity); } sub pretty_print($){ my $gantt = shift; print("--------------------------------------------------------------------\n"); my @bits = split(//, unpack("b*", $gantt->[0]->[2])); print("@bits\n"); foreach my $g (@{$gantt}){ print("BEGIN : $g->[0](".strftime("%F %T",localtime($g->[0])).")\n"); foreach my $h (@{$g->[1]}){ @bits = split(//, unpack("b*", $h->[1])); print(" $h->[0](".strftime("%F %T",localtime($h->[0])).") : @bits\n"); } print("\n"); } print("--------------------------------------------------------------------\n"); } # Creates an empty Gantt # arg : number of the max resource id sub new($$){ my $max_resource_number = shift; my $minimum_hole_duration = shift; $minimum_hole_duration = 0 if (!defined($minimum_hole_duration)); my $empty_vec = ''; vec($empty_vec, $max_resource_number, 1) = 0; my $result =[ [ 0, # start time of this hole [ # ref of a structure which contains hole stop times and corresponding resources (ordered by end time) [$Infinity, $empty_vec] ], $empty_vec, # Store all inserted resources (Only for the first Gantt hole) $empty_vec, # Store empty vec with enough 0 (Only for the first hole) $minimum_hole_duration, # minimum time for a hole [$Infinity,$Infinity] # times that find_first_hole must not go after ] ]; return($result); } # Creates a Gantt with 1 hole # arg : number of the max resource id sub new_with_1_hole($$$$$$){ my $max_resource_number = shift; my $minimum_hole_duration = shift; my $date = shift; my $duration = shift; my $resources_vec = shift; my $all_resources_vec = shift; my $gantt = OAR::Schedulers::GanttHoleStorage::new($max_resource_number, $minimum_hole_duration); # initiate the first hole with a fake date (ensure to keep it intact with all the configuration) $gantt->[0]->[1]->[0]->[0] = 86400; # Init the whole resource list directly $gantt->[0]->[2] = $all_resources_vec; # Feed vector with enough 0 $resources_vec |= $gantt->[0]->[3]; # Create the only hole $gantt->[1]->[0] = $date; $gantt->[1]->[1] = [[($date + $duration), $resources_vec]]; return($gantt); } # Adds and initializes new resources in the gantt # args : gantt ref, bit vector of resources sub add_new_resources($$) { my ($gantt, $resources_vec) = @_; # Feed vector with enough 0 $resources_vec |= $gantt->[0]->[3]; # Verify which resources are not already inserted my $resources_to_add_vec = $resources_vec & (~ $gantt->[0]->[2]); if (unpack("%32b*",$resources_to_add_vec) > 0){ # We need to insert new resources on all hole my $g = 0; while ($g <= $#{$gantt}){ # Add resources if ($gantt->[$g]->[1]->[$#{$gantt->[$g]->[1]}]->[0] == $Infinity){ $gantt->[$g]->[1]->[$#{$gantt->[$g]->[1]}]->[1] |= $resources_to_add_vec; }else{ push(@{$gantt->[$g]->[1]}, [$Infinity, $resources_vec]); } $g++; } # Keep already inserted resources in mind $gantt->[0]->[2] |= $resources_vec; } } # Inserts in the gantt new resource occupations # args : gantt ref, start slot date, slot duration, resources bit vector sub set_occupation($$$$){ my ($gantt, $date, $duration, $resources_vec) = @_; # Feed vector with enough 0 $resources_vec |= $gantt->[0]->[3]; # If a resource was not initialized add_new_resources($gantt,$resources_vec); # If it is not yet done my $new_hole = [ $date + $duration + 1, [] ]; my $g = 0; while (($g <= $#{$gantt}) and ($gantt->[$g]->[0] <= $new_hole->[0])){ my $slot_deleted = 0; # Look at all holes that are before the end of the occupation if (($#{$gantt->[$g]->[1]} >= 0) and ($gantt->[$g]->[1]->[$#{$gantt->[$g]->[1]}]->[0] >= $date)){ # Look at holes with a biggest slot >= $date my $h = 0; my $slot_date_here = 0; while ($h <= $#{$gantt->[$g]->[1]}){ # Look at all slots $slot_date_here = 1 if ($gantt->[$g]->[1]->[$h]->[0] == $date); if ($gantt->[$g]->[1]->[$h]->[0] > $date){ # This slot ends after $date #print("-->[1]\n ".($date - $gantt->[$g]->[0])." -- $gantt->[0]->[4]\n<--[1]\n"); if (($gantt->[$g]->[0] < $date) and ($slot_date_here == 0) and ($date - $gantt->[$g]->[0] > $gantt->[0]->[4])){ # We must create a smaller slot (hole start time < $date) splice(@{$gantt->[$g]->[1]}, $h, 0, [ $date , $gantt->[$g]->[1]->[$h]->[1] ]); $h++; # Go to the slot that we were on it before the splice $slot_date_here = 1; #print("-->[2]\n "); pretty_print($gantt); print("<--[2]\n"); } # Add new slots in the new hole if (($new_hole->[0] < $gantt->[$g]->[1]->[$h]->[0]) and ($gantt->[$g]->[1]->[$h]->[0] - $new_hole->[0] > $gantt->[0]->[4])){ # copy slot in the new hole if needed my $slot = 0; while (($slot <= $#{$new_hole->[1]}) and ($new_hole->[1]->[$slot]->[0] < $gantt->[$g]->[1]->[$h]->[0])){ # Find right index in the sorted slot array $slot++; } if ($slot <= $#{$new_hole->[1]}){ if ($new_hole->[1]->[$slot]->[0] == $gantt->[$g]->[1]->[$h]->[0]){ # If the slot already exists $new_hole->[1]->[$slot]->[1] |= $gantt->[$g]->[1]->[$h]->[1]; }else{ # Insert the new slot splice(@{$new_hole->[1]}, $slot, 0, [$gantt->[$g]->[1]->[$h]->[0], $gantt->[$g]->[1]->[$h]->[1]]); } }elsif ($new_hole->[0] < $gantt->[$g]->[1]->[$h]->[0]){ # There is no slot so we create one push(@{$new_hole->[1]}, [ $gantt->[$g]->[1]->[$h]->[0], $gantt->[$g]->[1]->[$h]->[1] ]); #print("-->[3]\n Add new hole $new_hole->[0]: $gantt->[$g]->[1]->[$h]->[0]:\n".Dumper($new_hole)."\n -->[3]\n"); } } # Remove new occupied resources from the current slot $gantt->[$g]->[1]->[$h]->[1] &= (~ $resources_vec) ; if (unpack("%32b*",$gantt->[$g]->[1]->[$h]->[1]) == 0){ # There is no free resource on this slot so we delete it #print("-->[4]\n Delete slot: $gantt->[$g]->[0],$gantt->[$g]->[1]->[$h]->[0] \n<--[4]\n"); splice(@{$gantt->[$g]->[1]}, $h, 1); $h--; $slot_deleted = 1; #print("-->[5]\n "); pretty_print($gantt); print("<--[5]\n"); }elsif ($h > 0){ # check if this is the same hole than the previous one my $tmp_vec = $gantt->[$g]->[1]->[$h-1]->[1] ^ $gantt->[$g]->[1]->[$h]->[1]; if (unpack("%32b*",$tmp_vec) == 0){ splice(@{$gantt->[$g]->[1]}, $h-1, 1); $h--; } } } # Go to the next slot $h++; } } if (($slot_deleted == 1) and ($#{$gantt->[$g]->[1]} < 0)){ # There is no free slot on the current hole so we delete it splice(@{$gantt}, $g, 1); $g--; }elsif($g > 0){ # Test if there is a same hole my $different = 0; #print("-->[6]\n ");pretty_print($gantt);print("<--[6]\n"); #print("-->[7]\nG-1=$gantt->[$g - 1]->[0] G=$gantt->[$g]->[0] \n<--[7]\n"); if ($#{$gantt->[$g - 1]->[1]} != $#{$gantt->[$g]->[1]}){ $different = 1; } my $tmp_h = 0; while (($different == 0) and (defined($gantt->[$g]->[1]->[$tmp_h]))){ if ($gantt->[$g - 1]->[1]->[$tmp_h]->[0] != $gantt->[$g]->[1]->[$tmp_h]->[0]){ $different = 1; }else{ my $tmp_vec = $gantt->[$g - 1]->[1]->[$tmp_h]->[1] ^ $gantt->[$g]->[1]->[$tmp_h]->[1]; if (unpack("%32b*",$tmp_vec) != 0){ $different = 1; } } $tmp_h++; } if ($different == 0){ #print("-->[8]\n Delete Hole: $gantt->[$g]->[0] \n-->[8]\n"); splice(@{$gantt}, $g, 1); $g--; } } # Go to the next hole $g++; } #print("-->[9]\n "); pretty_print($gantt); print("<--[9]\n"); if ($#{$new_hole->[1]} >= 0){ # Add the new hole if (($g > 0) and ($g - 1 <= $#{$gantt}) and ($gantt->[$g - 1]->[0] == $new_hole->[0])){ # Verify if the hole does not already exist splice(@{$gantt}, $g - 1, 1, $new_hole); }else{ splice(@{$gantt}, $g, 0, $new_hole); } } } # Find the first hole in the data structure that can fit the given slot sub find_hole($$$){ my ($gantt, $begin_date, $duration) = @_; my $end_date = $begin_date + $duration; my $g = 0; while (($g < $#{$gantt}) and (($gantt->[$g+1]->[0] <= $begin_date) or (($g <= $#{$gantt}) and ($gantt->[$g]->[0] < $begin_date) and ($gantt->[$g]->[1]->[$#{$gantt->[$g]->[1]}]->[0] < $end_date)))){ $g++ } return($g); } # Returns the vector of the maximum free resources at the given date for the given duration sub get_free_resources($$$){ my ($gantt, $begin_date, $duration) = @_; my $end_date = $begin_date + $duration; my $hole_index = 0; # search the nearest hole while (($hole_index <= $#{$gantt}) and ($gantt->[$hole_index]->[0] < $begin_date) and (($gantt->[$hole_index]->[1]->[$#{$gantt->[$hole_index]->[1]}]->[0] < $end_date) or (($hole_index + 1 <= $#{$gantt}) and $gantt->[$hole_index + 1]->[0] < $begin_date))){ $hole_index++; } return($gantt->[0]->[4]) if ($hole_index > $#{$gantt}); my $h = 0; while (($h <= $#{$gantt->[$hole_index]->[1]}) and ($gantt->[$hole_index]->[1]->[$h]->[0] < $end_date)){ $h++; } return($gantt->[$hole_index]->[1]->[$h]->[1]); } # Take a list of resource trees and find a hole that fit # args : gantt ref, initial time from which the search will begin, job duration, list of resource trees sub find_first_hole($$$$$){ my ($gantt, $initial_time, $duration, $tree_description_list, $timeout) = @_; # $tree_description_list->[0] --> First resource group corresponding tree # $tree_description_list->[1] --> Second resource group corresponding tree # ... # Test if all groups are populated my $return_infinity = 0; my $g = 0; while (($return_infinity == 0) and ($g <= $#$tree_description_list)){ if (!defined($tree_description_list->[$g])){ $return_infinity = 1; } $g++; } return ($Infinity, ()) if ($return_infinity > 0); my @result_tree_list = (); my $end_loop = 0; my $current_time = $initial_time; my $timeout_initial_time = time(); # begin research at the first potential hole my $current_hole_index = find_hole($gantt, $initial_time, $duration); my $h = 0; while ($end_loop == 0){ # Go to a right hole while (($current_hole_index <= $#{$gantt}) and (($gantt->[$current_hole_index]->[0] + $duration > $gantt->[$current_hole_index]->[1]->[$h]->[0]) or (($initial_time > $gantt->[$current_hole_index]->[0]) and ($initial_time + $duration > $gantt->[$current_hole_index]->[1]->[$h]->[0])))){ while (($h <= $#{$gantt->[$current_hole_index]->[1]}) and (($gantt->[$current_hole_index]->[0] + $duration > $gantt->[$current_hole_index]->[1]->[$h]->[0]) or (($initial_time > $gantt->[$current_hole_index]->[0]) and ($initial_time + $duration > $gantt->[$current_hole_index]->[1]->[$h]->[0])))){ $h++; } if ($h > $#{$gantt->[$current_hole_index]->[1]}){ # in this hole no slot fits so we must search in the next hole $h = 0; $current_hole_index++; } } if ($current_hole_index > $#{$gantt}){ # no hole fits $current_time = $Infinity; @result_tree_list = (); $end_loop = 1; }else{ #print("Treate hole $current_hole_index, $h : $gantt->[$current_hole_index]->[0] --> $gantt->[$current_hole_index]->[1]->[$h]->[0]\n"); $current_time = $gantt->[$current_hole_index]->[0] if ($initial_time < $gantt->[$current_hole_index]->[0]); #Check all trees my $tree_clone; my $i = 0; # Initiate already used resources with the empty vector my $already_occupied_resources_vec = $gantt->[0]->[3]; do{ foreach my $l (OAR::Schedulers::ResourceTree::get_tree_leafs($tree_clone)){ vec($already_occupied_resources_vec, OAR::Schedulers::ResourceTree::get_current_resource_value($l), 1) = 1; } # clone the tree, so we can work on it without damage $tree_clone = OAR::Schedulers::ResourceTree::clone($tree_description_list->[$i]); #Remove tree leafs that are not free foreach my $l (OAR::Schedulers::ResourceTree::get_tree_leafs($tree_clone)){ if ((!vec($gantt->[$current_hole_index]->[1]->[$h]->[1],OAR::Schedulers::ResourceTree::get_current_resource_value($l),1)) or (vec($already_occupied_resources_vec,OAR::Schedulers::ResourceTree::get_current_resource_value($l),1)) ){ OAR::Schedulers::ResourceTree::delete_subtree($l); } } #print(Dumper($tree_clone)); $tree_clone = OAR::Schedulers::ResourceTree::delete_tree_nodes_with_not_enough_resources($tree_clone); $tree_clone = OAR::Schedulers::ResourceTree::delete_unnecessary_subtrees($tree_clone); #$Data::Dumper::Purity = 0; #$Data::Dumper::Terse = 0; #$Data::Dumper::Indent = 1; #$Data::Dumper::Deepcopy = 0; # print(Dumper($tree_clone)); $result_tree_list[$i] = $tree_clone; $i ++; }while(defined($tree_clone) && ($i <= $#$tree_description_list)); if (defined($tree_clone)){ # We find the first hole $end_loop = 1; }else{ # Go to the next slot of this hole if ($h >= $#{$gantt->[$current_hole_index]->[1]}){ $h = 0; $current_hole_index++; }else{ $h++; } } } # Check timeout if (($current_hole_index <= $#{$gantt}) and (((time() - $timeout_initial_time) >= $timeout) or (($gantt->[$current_hole_index]->[0] == $gantt->[0]->[5]->[0]) and ($gantt->[$current_hole_index]->[1]->[$h]->[0] >= $gantt->[0]->[5]->[1])) or ($gantt->[$current_hole_index]->[0] > $gantt->[0]->[5]->[0])) and ($gantt->[$current_hole_index]->[0] > $initial_time)){ if (($gantt->[0]->[5]->[0] == $gantt->[$current_hole_index]->[0]) and ($gantt->[0]->[5]->[1] > $gantt->[$current_hole_index]->[1]->[$h]->[0])){ $gantt->[0]->[5]->[1] = $gantt->[$current_hole_index]->[1]->[$h]->[0]; }elsif ($gantt->[0]->[5]->[0] > $gantt->[$current_hole_index]->[0]){ $gantt->[0]->[5]->[0] = $gantt->[$current_hole_index]->[0]; $gantt->[0]->[5]->[1] = $gantt->[$current_hole_index]->[1]->[$h]->[0]; } #print("TTTTTTT $gantt->[0]->[5]->[0] $gantt->[0]->[5]->[1] -- $gantt->[$current_hole_index]->[0] $gantt->[$current_hole_index]->[1]->[$h]->[0]\n"); $current_time = $Infinity; @result_tree_list = (); $end_loop = 1; } } return($current_time, \@result_tree_list); } # Take a list of resource trees and find a hole that fit # args : gantt ref, initial time from which the search will begin, job duration, list of resource trees sub find_first_hole_parallel($$$$$$){ my ($gantt, $initial_time, $duration, $tree_description_list, $timeout, $max_children) = @_; # $tree_description_list->[0] --> First resource group corresponding tree # $tree_description_list->[1] --> Second resource group corresponding tree # ... # Test if all groups are populated my $return_infinity = 0; my $g = 0; while (($return_infinity == 0) and ($g <= $#$tree_description_list)){ if (!defined($tree_description_list->[$g])){ $return_infinity = 1; } $g++; } return ($Infinity, ()) if ($return_infinity > 0); my @result_tree_list = (); my $end_loop = 0; my $current_time = $initial_time; my $timeout_initial_time = time(); my %children; my $process_index = 0; my $process_index_to_check = 0; my @result_children; # begin research at the first potential hole my $current_hole_index = find_hole($gantt, $initial_time, $duration); my $current_process_hole_index = 0; my $h = 0; while ($end_loop == 0){ # Go to a right hole while (($current_hole_index <= $#{$gantt}) and (($gantt->[$current_hole_index]->[0] + $duration > $gantt->[$current_hole_index]->[1]->[$h]->[0]) or (($initial_time > $gantt->[$current_hole_index]->[0]) and ($initial_time + $duration > $gantt->[$current_hole_index]->[1]->[$h]->[0])))){ while (($h <= $#{$gantt->[$current_hole_index]->[1]}) and (($gantt->[$current_hole_index]->[0] + $duration > $gantt->[$current_hole_index]->[1]->[$h]->[0]) or (($initial_time > $gantt->[$current_hole_index]->[0]) and ($initial_time + $duration > $gantt->[$current_hole_index]->[1]->[$h]->[0])))){ $h++; } if ($h > $#{$gantt->[$current_hole_index]->[1]}){ # in this hole no slot fits so we must search in the next hole $h = 0; $current_hole_index++; } } if (($current_hole_index > $#{$gantt}) and (keys(%children) <= 0)){ # no hole fits $current_time = $Infinity; @result_tree_list = (); $end_loop = 1; }else{ my $select_timeout = 0.1; if (($current_hole_index <= $#{$gantt}) and (keys(%children) < $max_children)){ $select_timeout = 0; $current_process_hole_index = $current_hole_index if ($process_index == 0); my $P1; my $P2; pipe($P1,$P2); my $pid = fork(); if ($pid == 0){ #Child close($P1); #print "PID $$ : $process_index ($current_hole_index)\n"; #print("Treate hole $current_hole_index, $h : $gantt->[$current_hole_index]->[0] --> $gantt->[$current_hole_index]->[1]->[$h]->[0]\n"); $current_time = $gantt->[$current_hole_index]->[0] if ($initial_time < $gantt->[$current_hole_index]->[0]); #Check all trees my $tree_clone; my $tree_list; my $i = 0; # Initiate already used resources with the empty vector my $already_occupied_resources_vec = $gantt->[0]->[3]; do{ foreach my $l (OAR::Schedulers::ResourceTree::get_tree_leafs($tree_clone)){ vec($already_occupied_resources_vec, OAR::Schedulers::ResourceTree::get_current_resource_value($l), 1) = 1; } $tree_clone = $tree_description_list->[$i]; #Remove tree leafs that are not free foreach my $l (OAR::Schedulers::ResourceTree::get_tree_leafs($tree_clone)){ if ((!vec($gantt->[$current_hole_index]->[1]->[$h]->[1],OAR::Schedulers::ResourceTree::get_current_resource_value($l),1)) or (vec($already_occupied_resources_vec,OAR::Schedulers::ResourceTree::get_current_resource_value($l),1)) ){ OAR::Schedulers::ResourceTree::delete_subtree($l); } } #print(Dumper($tree_clone)); $tree_clone = OAR::Schedulers::ResourceTree::delete_tree_nodes_with_not_enough_resources($tree_clone); $tree_clone = OAR::Schedulers::ResourceTree::delete_unnecessary_subtrees($tree_clone); $tree_list->[$i] = $tree_clone; $i ++; }while(defined($tree_clone) && ($i <= $#$tree_description_list)); my %result = ( process_index => $process_index, current_time => $current_time, current_hole_index => $current_hole_index ); if (defined($tree_clone)){ #print "PID $$; INDEX $process_index : I found a hole\n"; $result{result_tree_list} = $tree_list; }else{ $result{result_tree_list} = undef; } select($P2); $| = 1; store_fd(\%result, $P2); close($P2); exit(0); } #Father $children{$pid} = { process_index => $process_index, pipe_read => $P1 }; $process_index++; # Go to the next slot of this hole if ($h >= $#{$gantt->[$current_hole_index]->[1]}){ $h = 0; $current_hole_index++; }else{ $h++; } } # check children results my $rin = ''; foreach my $p (keys(%children)){ vec($rin, fileno($children{$p}->{pipe_read}), 1) = 1; } my $rout; if (select($rout=$rin, undef, undef, $select_timeout)){ foreach my $p (keys(%children)){ if (vec($rout,fileno($children{$p}->{pipe_read}),1)){ my $fh = $children{$p}->{pipe_read}; my $hash = fd_retrieve($fh); #print "MASTER child $children{$p}->{process_index} FINISHED\n"; $result_children[$children{$p}->{process_index}] = $hash; delete($children{$p}); close($fh); } } } while ((defined($result_children[$process_index_to_check])) and ($end_loop == 0)){ if (defined($result_children[$process_index_to_check]->{result_tree_list})){ # We find the first hole #print "MASTER using hole from process index $process_index_to_check\n"; $current_time = $result_children[$process_index_to_check]->{current_time}; @result_tree_list = @{$result_children[$process_index_to_check]->{result_tree_list}}; $end_loop = 1; } $current_process_hole_index = $result_children[$process_index_to_check]->{current_hole_index}; $process_index_to_check++; } # Avoid zombies my $kid = 1; while ($kid > 0){ $kid = waitpid(-1, WNOHANG); } # Check timeout #print "CURRENT HOLE: $current_process_hole_index\n"; if (($end_loop == 0) and ($current_process_hole_index <= $#{$gantt}) and (((time() - $timeout_initial_time) >= $timeout) or (($gantt->[$current_process_hole_index]->[0] == $gantt->[0]->[5]->[0]) and ($gantt->[$current_process_hole_index]->[1]->[$h]->[0] >= $gantt->[0]->[5]->[1])) or ($gantt->[$current_process_hole_index]->[0] > $gantt->[0]->[5]->[0])) and ($gantt->[$current_process_hole_index]->[0] > $initial_time)){ if (($gantt->[0]->[5]->[0] == $gantt->[$current_process_hole_index]->[0]) and ($gantt->[0]->[5]->[1] > $gantt->[$current_process_hole_index]->[1]->[$h]->[0])){ $gantt->[0]->[5]->[1] = $gantt->[$current_process_hole_index]->[1]->[$h]->[0]; }elsif ($gantt->[0]->[5]->[0] > $gantt->[$current_process_hole_index]->[0]){ $gantt->[0]->[5]->[0] = $gantt->[$current_process_hole_index]->[0]; $gantt->[0]->[5]->[1] = $gantt->[$current_process_hole_index]->[1]->[$h]->[0]; } #print("TTTTTTT $gantt->[0]->[5]->[0] $gantt->[0]->[5]->[1] -- $gantt->[$current_hole_index]->[0] $gantt->[$current_hole_index]->[1]->[$h]->[0]\n"); $current_time = $Infinity; @result_tree_list = (); $end_loop = 1; } } } kill(9,keys(%children)); # Avoid zombies my $kid = 1; while ($kid > 0){ $kid = waitpid(-1, WNOHANG); } return($current_time, \@result_tree_list); } return 1; ./oar-2.5.2/sources/core/server/lib/OAR/PingChecker.pm0000644000175000017500000003040211757171206020410 0ustar plbplb# $Id$ package OAR::PingChecker; require Exporter; use strict; use Data::Dumper; use OAR::Modules::Judas qw(oar_debug oar_warn oar_error send_log_by_email set_current_log_category); use OAR::Conf qw(init_conf dump_conf get_conf is_conf); use IPC::Open3; use OAR::Tools; require Exporter; our (@ISA,@EXPORT,@EXPORT_OK); @ISA = qw(Exporter); @EXPORT_OK = qw(ping_hosts sentinelle_hosts test_hosts); # Log category set_current_log_category('main'); my $Timeout_ping = 5; my $Timeout_fping = 15; my $Timeout_nmap = 30; my $Timeout_sentinelle = 300; my $Timeout_script_sentinelle = 300; my $Default_timeout = 300; #resolve host name and give its IP or return 0 sub get_host_ip($){ my ($host_name) = shift; my (@addrlist,$name,$altnames,$addrtype,$len); my ($ip); if (!(($name, $altnames, $addrtype, $len, @addrlist) = gethostbyname($host_name))) { return (0); }else{ $ip=join('.',(unpack("C4",$addrlist[0]))); return ($ip); } } #Launch the right sub sub test_hosts(@){ init_conf($ENV{OARCONFFILE}); if (is_conf("OAR_SSH_CONNECTION_TIMEOUT")){ OAR::Tools::set_ssh_timeout(get_conf("OAR_SSH_CONNECTION_TIMEOUT")); } if (is_conf("PINGCHECKER_TAKTUK_ARG_COMMAND") and is_conf("TAKTUK_CMD")){ return(taktuk_hosts(@_)); }elsif (is_conf("PINGCHECKER_SENTINELLE_SCRIPT_COMMAND")){ return(sentinelle_script_hosts(@_)); }elsif (is_conf("PINGCHECKER_FPING_COMMAND")){ return(fping_hosts(@_)); }elsif (is_conf("PINGCHECKER_NMAP_COMMAND")){ return(nmap_hosts(@_)); }elsif (is_conf("PINGCHECKER_GENERIC_COMMAND")){ return(generic_hosts(@_)); }else{ return(ping_hosts(@_)); } } #Ping hosts and return each one dead # arg1 --> array of hosts to test sub ping_hosts(@){ my @hosts = @_; my @bad_hosts; my $exit_value; foreach my $i (@hosts){ oar_debug("[PingChecker] PING $i\n"); $ENV{IFS}=""; $ENV{ENV}=""; eval { $SIG{ALRM} = sub { die "alarm\n" }; alarm($Timeout_ping); #$exit_value = system("ping -c 1 $i > /dev/null"); $exit_value = system("oardodo ping -c 10 -l 9 $i > /dev/null"); alarm(0); }; oar_debug("[PingChecker] PONG with exit_value=$exit_value and alarm=$@\n"); if (($exit_value != 0) || ($@)){ push(@bad_hosts, $i); } } return(@bad_hosts); } #Test hosts with taktuk and return each one dead # arg1 --> array of hosts to test sub taktuk_hosts(@){ my @hosts = @_; init_conf($ENV{OARCONFFILE}); my $taktuk_cmd = get_conf("TAKTUK_CMD"); oar_debug("[PingChecker] command to run : $taktuk_cmd\n"); my ($cmd, @null) = split(" ",$taktuk_cmd); oar_debug("[PingChecker] command to run with arguments : $cmd\n"); if (!defined($taktuk_cmd) || (! -x $cmd)){ oar_error("[PingChecker] You call taktuk_hosts but TAKTUK_CMD in oar.conf is not valid.\n"); return(@hosts); } my $openssh_cmd = get_conf("OPENSSH_CMD"); $openssh_cmd = OAR::Tools::get_default_openssh_cmd() if (!defined($openssh_cmd)); my %check_test_nodes; $taktuk_cmd .= " -c '$openssh_cmd'".' -o status=\'"STATUS $host $line\n"\' -f - '.get_conf("PINGCHECKER_TAKTUK_ARG_COMMAND"); my @bad_hosts; oar_debug("[PingChecker] $taktuk_cmd\n"); $ENV{IFS}=""; $ENV{ENV}=""; my $pid; eval { $SIG{ALRM} = sub { kill(19,$pid); die("alarm\n") }; alarm(OAR::Tools::get_taktuk_timeout()); $pid = open3(\*WRITER, \*READER, \*ERROR, $taktuk_cmd); foreach my $i (@hosts){ print(WRITER "$i\n"); $check_test_nodes{$i} = 1; } close(WRITER); while(){ if ($_ =~ /^STATUS ([\w\.\-\d]+) (\d+)$/){ if ($2 == 0){ delete($check_test_nodes{$1}) if (defined($check_test_nodes{$1})); } }else{ oar_debug("[PingChecker][TAKTUK OUTPUT] $_"); } } close(ERROR); close(READER); waitpid($pid, 0); alarm(0); }; oar_debug("[PingChecker] End of command; alarm=$@\n"); if ($@){ oar_error("[PingChecker] taktuk command times out : it is bad\n"); if (defined($pid)){ # Kill all taktuk children my ($children,$cmd_name) = OAR::Tools::get_one_process_children($pid); kill(9,@{$children}); } # return(@hosts); }#else{ @bad_hosts = keys(%check_test_nodes); return(@bad_hosts); # } } #Test hosts with sentinelle script and return each one dead # arg1 --> array of hosts to test sub sentinelle_script_hosts(@){ my @hosts = @_; # Set the parameter of the -c option of sentinelle init_conf($ENV{OARCONFFILE}); my $sentinelle_cmd = get_conf("PINGCHECKER_SENTINELLE_SCRIPT_COMMAND"); oar_debug("[PingChecker] command to run : $sentinelle_cmd\n"); my ($cmd, @null) = split(" ",$sentinelle_cmd); oar_debug("[PingChecker] command to run with arguments : $cmd\n"); if (!defined($sentinelle_cmd) || (! -x $cmd)){ oar_error("[PingChecker] You call sentinelle_script_hosts but PINGCHECKER_SENTINELLE_SCRIPT_COMMAND in oar.conf is not valid\n"); return(@hosts); } my $openssh_cmd = get_conf("OPENSSH_CMD"); $openssh_cmd = OAR::Tools::get_default_openssh_cmd() if (!defined($openssh_cmd)); my %check_test_nodes; $sentinelle_cmd .= " -c '$openssh_cmd' -f - "; my @bad_hosts; oar_debug("[PingChecker] $sentinelle_cmd \n"); $ENV{IFS}=""; $ENV{ENV}=""; eval { my $pid; $SIG{ALRM} = sub { kill(9,$pid); die("alarm\n") }; alarm($Timeout_script_sentinelle); $pid = open3(\*WRITER, \*READER, \*ERROR, $sentinelle_cmd); foreach my $i (@hosts){ print(WRITER "$i\n"); $check_test_nodes{$i} = 1; } close(WRITER); while(){ chomp($_); #if ($_ =~ m/^([\w\.]+)\s:\sBAD\s.*$/m){ if ($_ =~ m/^([\w\.\-]+)\s:\sBAD\s.*$/m){ if ($check_test_nodes{$1} == 1){ oar_debug("[PingChecker] Bad host = $1 \n"); push(@bad_hosts, $1); } } } close(READER); close(ERROR); waitpid($pid, 0); alarm(0); }; oar_debug("[PingChecker] End of command; alarm=$@\n"); if ($@){ oar_error("[PingChecker] sentinelle script command times out : it is bad\n"); return(@hosts); }else{ return(@bad_hosts); } } #Ping hosts with fping program return each one dead # arg1 --> array of hosts to test sub fping_hosts(@){ my @hosts = @_; # Get fping command from oar.conf init_conf($ENV{OARCONFFILE}); my $fping_cmd = get_conf("PINGCHECKER_FPING_COMMAND"); oar_debug("[PingChecker] command to run : $fping_cmd\n"); my ($cmd, @null) = split(" ",$fping_cmd); oar_debug("[PingChecker] command to run with arguments : $cmd\n"); if (!defined($fping_cmd) || (! -x $cmd)){ oar_error("[PingChecker] You want to call fping test method but PINGCHECKER_FPING_COMMAND in oar.conf is not valid\n"); return(@hosts); } $fping_cmd .= " -u"; my %check_test_nodes; foreach my $i (@hosts){ $fping_cmd .= " $i"; $check_test_nodes{$i} = 1; } my @bad_hosts; oar_debug("[PingChecker] $fping_cmd\n"); $ENV{IFS}=""; $ENV{ENV}=""; eval { my $pid; $SIG{ALRM} = sub { kill(9,$pid); die("alarm\n") }; alarm($Timeout_fping); $pid = open3(\*WRITER, \*READER, \*ERROR, $fping_cmd); close(WRITER); foreach my $i (\*READER, \*ERROR){ while(<$i>){ chomp($_); #$_ =~ m/^\s*([\w\.]+)\s*$/m; $_ =~ m/^\s*([\w\.-\d]+)\s*(.*)$/m; if ($check_test_nodes{$1} == 1){ if (!defined($2) || !($2 =~ m/alive/m)){ oar_debug("[PingChecker] Bad host = $1 \n"); push(@bad_hosts, $1); } } } } close(ERROR); close(READER); waitpid($pid, 0); alarm(0); }; oar_debug("[PingChecker] End of command; alarm=$@\n"); if ($@){ oar_error("[PingChecker] fping command times out : it is bad\n"); return(@hosts); }else{ return(@bad_hosts); } } # use nmap to determine if hosts are alive or not # arg1 --> array of hosts to test sub nmap_hosts(@){ my @hosts = @_; # Get nmap command from oar.conf init_conf($ENV{OARCONFFILE}); my $nmap_cmd = get_conf("PINGCHECKER_NMAP_COMMAND"); oar_debug("[PingChecker] command to run : $nmap_cmd\n"); my ($cmd, @null) = split(" ",$nmap_cmd); oar_debug("[PingChecker] command to run with arguments : $cmd\n"); if (!defined($nmap_cmd) || (! -x $cmd)){ oar_error("[PingChecker] You want to call nmap test method but PINGCHECKER_NMAP_COMMAND in oar.conf is not valid\n"); return(@hosts); } $nmap_cmd .= " -oG -"; my %ip2name; my @bad_hosts; foreach my $i (@hosts){ my $ip = get_host_ip($i); if ($ip != 0){ if (!defined($ip2name{$ip})){ $nmap_cmd .= " $ip"; } push(@{$ip2name{$ip}}, $i); } } my %good_hosts; oar_debug("[PingChecker] $nmap_cmd\n"); $ENV{IFS}=""; $ENV{ENV}=""; eval { my $pid; $SIG{ALRM} = sub { kill(9,$pid); die("alarm\n") }; alarm($Timeout_nmap); $pid = open3(\*WRITER, \*READER, \*ERROR, $nmap_cmd); close(WRITER); while(){ chomp($_); if ($_ =~ m/^Host:\s(\d+\.\d+\.\d+\.\d+)\s(.*)$/m){ if (defined($ip2name{$1})){ my $tmp_ip = $1; if (defined($2) && ($2 =~ m/open/m)){ oar_debug("[PingChecker] Good host = $tmp_ip \n"); foreach my $i (@{$ip2name{$tmp_ip}}){ $good_hosts{$i} = 1; } } } } } close(ERROR); close(READER); waitpid($pid, 0); alarm(0); }; oar_debug("[PingChecker] End of command; alarm=$@\n"); if ($@){ oar_error("[PingChecker] nmap command times out : it is bad\n"); return(@hosts); }else{ foreach my $n (@hosts){ if (!defined($good_hosts{$n})){ push(@bad_hosts, $n); } } return(@bad_hosts); } } # use a command which takes the list of nodes in arguments and write on STDERR # the list of bad nodes # arg1 --> array of hosts to test sub generic_hosts(@){ my @hosts = @_; # Get generic command from oar.conf init_conf($ENV{OARCONFFILE}); my $test_cmd = get_conf("PINGCHECKER_GENERIC_COMMAND"); oar_debug("[PingChecker] command to run : $test_cmd\n"); my ($cmd, @null) = split(" ",$test_cmd); oar_debug("[PingChecker] command to run with arguments : $cmd\n"); if (!defined($test_cmd) || (! -x $cmd)){ oar_error("[PingChecker] You want to call a generic test method but PINGCHECKER_GENERIC_COMMAND in oar.conf is not valid\n"); return(@hosts); } my %check_test_nodes; foreach my $i (@hosts){ $test_cmd .= " $i"; $check_test_nodes{$i} = 1; } my @bad_hosts; oar_debug("[PingChecker] $test_cmd \n"); $ENV{IFS}=""; $ENV{ENV}=""; eval { my $pid; $SIG{ALRM} = sub { kill(9,$pid); die("alarm\n") }; alarm($Default_timeout); $pid = open3(\*WRITER, \*READER, \*ERROR, $test_cmd); while(){ chomp($_); #$_ =~ m/^\s*([\w\.]+)\s*$/m; $_ =~ m/^\s*([\w\.\-]+)\s*$/m; if ($check_test_nodes{$1} == 1){ oar_debug("[PingChecker] Bad host = $1 \n"); push(@bad_hosts, $1); } } close(ERROR); close(WRITER); close(READER); waitpid($pid, 0); alarm(0); }; oar_debug("[PingChecker] End of command; alarm=$@\n"); if ($@){ oar_error("[PingChecker] PINGCHECKER_GENERIC_COMMAND timed out : it is bad\n"); return(@hosts); }else{ return(@bad_hosts); } } return 1; ./oar-2.5.2/sources/core/server/lib/OAR/Modules/0000755000175000017500000000000011757171206017301 5ustar plbplb./oar-2.5.2/sources/core/server/lib/OAR/Modules/Hulot.pm0000644000175000017500000007242611757171206020745 0ustar plbplbpackage OAR::Modules::Hulot; require Exporter; # This module is responsible of waking up / shutting down nodes # when the scheduler decides it (writes it on a named pipe) # CHECK command is sent on the named pipe to Hulot: # - by windowForker module: # - to avoid zombie process # - to messages received in queue (IPC) # - by MetaScheduler if there is no node to wake up / shut down in order: # - to check timeout and check memorized nodes list # - to check booting nodes status #*********************************************** # [TODO] # Todo?: Group nodes passed to the sleeping command (to do inside windowforker) # #*********************************************** use strict; use OAR::Conf qw(init_conf get_conf is_conf get_conf_with_default_param); use POSIX qw(strftime sys_wait_h); use Time::HiRes qw(gettimeofday); use OAR::IO; use OAR::Tools; use OAR::Modules::Judas qw(oar_debug oar_warn oar_error send_log_by_email set_current_log_category); use OAR::WindowForker; use IPC::SysV qw(IPC_PRIVATE IPC_RMID IPC_CREAT S_IRUSR S_IWUSR IPC_NOWAIT); use OAR::Schedulers::Scheduler; #use Devel::Cycle; #use Devel::Peek; use Data::Dumper; require Exporter; our ( @ISA, @EXPORT, @EXPORT_OK ); @ISA = qw(Exporter); @EXPORT_OK = qw(start_energy_loop); my $FIFO = "/tmp/oar_hulot_pipe"; # Default values if not specified in oar.conf my $ENERGY_SAVING_WINDOW_SIZE = 25; my $ENERGY_SAVING_WINDOW_TIME = 60; my $ENERGY_SAVING_WINDOW_TIMEOUT = 120; my $ENERGY_SAVING_NODE_MANAGER_WAKEUP_TIMEOUT = 900; # Log category set_current_log_category('Hulot'); # Get OAR configuration init_conf( $ENV{OARCONFFILE} ); my $remote_host = get_conf("SERVER_HOSTNAME"); my $remote_port = get_conf("SERVER_PORT"); ## add_to_hash # parameters: ref on: source hash, destination hash sub add_to_hash($$) { my $hash_source = shift; my $hash_dest = shift; my $tmp; foreach $tmp ( keys(%$hash_source) ) { @$hash_dest{$tmp} = @$hash_source{$tmp}; } } ## change_node_state # Changes node state # parameters: base, nodeToChange, StateToSet # return value: / # side effects: / sub change_node_state($$$) { my ( $base, $node, $state ) = @_; #oar_debug("[Hulot] Changing state of node '$node' to '$state'\n"); OAR::IO::set_node_nextState( $base, $node, $state ); OAR::Tools::notify_tcp_socket( $remote_host, $remote_port, "ChState" ); } ## check # Sends 'check' signal on the named pipe to Hulot sub check() { my @tab = (); return send_cmd_to_fifo( \@tab, "CHECK" ); } ##check_reminded_list # Checks if some nodes in list_to_remind can be processed # parameters: ref to hash : running list, reminded list and list to process # return value: / # side effects: move nodes from reminded list to list to process if it's possible. sub check_reminded_list($$$) { my ( $tmp_list_running, $tmp_list_to_remind, $tmp_list_to_process ) = @_; foreach my $rmd_node ( keys(%$tmp_list_to_remind) ) { my $tmp_nodeFinded = 0; foreach my $run_node ( keys(%$tmp_list_running) ) { if ( $rmd_node eq $run_node ) { $tmp_nodeFinded = 1; } } if ( $tmp_nodeFinded == 0 ) { # move this node from reminded list to list to process # oar_debug( #"[Hulot] Adding '$rmd_node=>$$tmp_list_to_remind{$rmd_node}' to list to process\n" # ); $$tmp_list_to_process{$rmd_node} = { 'command' => $$tmp_list_to_remind{$rmd_node}, 'timeout' => -1 }; # oar_debug( # "[Hulot] Removing node '$rmd_node' from list to remember\n"); remove_from_hash( $tmp_list_to_remind, $rmd_node ); } } } ## check_returned_cmd # Checks received messages from WindowForker module # parameters: base, received messages and ref to hash : running list, reminded list and list to process # return value: / # side effects: - Removes halted node from the running list ; # - Suspects node if an error is returned by WindowForker module. sub check_returned_cmd($$$$$) { my ( $base, $tmp_message, $tmp_list_running, $tmp_list_to_remind, $tmp_list_to_process ) = @_; ( my $tmp_node, my $tmp_cmd, my $tmp_return ) = split( /:/, $tmp_message, 3 ); oar_debug( "[Hulot] Received from WindowForker : Node=$tmp_node ; Action=$tmp_cmd ; ReturnCode : $tmp_return\n" ); if ( $tmp_return == 0 ) { if ( $tmp_cmd eq "HALT" ) { # Remove halted node from the list running nodes because we don't monitor the turning off remove_from_hash( $tmp_list_running, $tmp_node ); } } else { # Suspect node if error change_node_state( $base, $tmp_node, "Suspected" ); my $str = "[Hulot] Node $tmp_node was suspected because an error occurred with a command launched by Hulot"; OAR::IO::add_new_event_with_host($base, "LOG_SUSPECTED", 0, $str, [$tmp_node]); oar_debug("$str\n"); } } ## halt_nodes sub halt_nodes($) { my $nodes = shift; return send_cmd_to_fifo( $nodes, "HALT" ); } ## is_exists_in_array # Returns true if the value exists in the array # parameters: Value searched, ref on the array # return value: boolean # side effects: / sub is_exists_in_array ( $ $ ) { my $value = shift; my $array = shift; my $res = 0; if ( "@$array" =~ /$value/ ) { $res = 1; } else { $res = 0; } return ($res); } ## remove_from_array # Remove a value from an array # parameters: Reference on an array, value to remove # return value: None sub remove_from_array($$) { my $array_toclean = shift; my $value = shift; my $tmp; my @cleaned_array; foreach $tmp (@$array_toclean) { if ( $tmp ne $value ) { @cleaned_array = $tmp; } } @$array_toclean = @cleaned_array; } ## remove_from_hash # Remove a value from a hash # parameters: Reference on a hash, value to remove # return value: None sub remove_from_hash($$) { my $hash_toclean = shift; my $value = shift; my $tmp; my %cleaned_hash; foreach $tmp ( keys(%$hash_toclean) ) { if ( $tmp ne $value ) { $cleaned_hash{$tmp} = @$hash_toclean{$tmp}; } } %$hash_toclean = %cleaned_hash; } ## send_cmd_to_fifo sub send_cmd_to_fifo($$) { my $nodes = shift; my $command = shift; my $nodes_list = join( ' ', @$nodes ); unless ( open( FIFO, "> $FIFO" ) ) { oar_error("[Hulot] Could not open the fifo $FIFO!\n"); return 1; } print FIFO "$command:$nodes_list\n"; close(FIFO); return 0; } ## start_energy_loop sub start_energy_loop() { my %nodes_list_to_process; my %nodes_list_to_remind; my %nodes_list_running; my $forker_pid; my $id_msg_hulot; my $pack_template = "l! a*"; my $max_cycles=int(get_conf_with_default_param( "ENERGY_MAX_CYCLES_UNTIL_REFRESH", "5000" )); my $runtime_directory=get_conf_with_default_param( "OAR_RUNTIME_DIRECTORY", "/tmp/oar_runtime" ); my %timeouts = fill_timeouts(get_conf_with_default_param( "ENERGY_SAVING_NODE_MANAGER_WAKEUP_TIMEOUT", $ENERGY_SAVING_NODE_MANAGER_WAKEUP_TIMEOUT)); oar_debug("[Hulot] Starting Hulot, the energy saving module\n"); # Load state if exists if (-s "$runtime_directory/hulot_status.dump") { my $ref = do "$runtime_directory/hulot_status.dump"; if ($ref) { if (defined($ref->[0]) && defined($ref->[1]) && ref($ref->[0]) eq "HASH" && ref($ref->[1]) eq "HASH") { oar_debug("[Hulot] State file found, loading it\n"); %nodes_list_running = %{$ref->[0]}; %nodes_list_to_remind = %{$ref->[1]}; } } } unlink "$runtime_directory/hulot_status.dump"; # Init keepalive values ie construct a hash: # sql properties => number of nodes to keepalive # given the ENERGY_SAVING_NODES_KEEPALIVE variable such as: # "cluster=paradent:nodes=4,cluster=paraquad:nodes=6" my %keepalive; # Number of nodes to keepalive per properties: # $keepalive{}{"min"}=int # Number of nodes currently alive and with no jobs, per properties: # $keepalive{}{"cur_idle"}=int # List of nodes corresponding to properties: # $keepalive{}{"nodes"}=@; my $keepalive_string=get_conf_with_default_param( "ENERGY_SAVING_NODES_KEEPALIVE", "type='default':0" ); if (not $keepalive_string =~ /.+:\d+,*/) { oar_debug("[Hulot] Syntax error into ENERGY_SAVING_NODES_KEEPALIVE!\n"); exit(3); }else{ my @keepalive_items=split(/\s*\&\s*/,$keepalive_string); foreach my $item (@keepalive_items) { (my $properties, my $nodes_number)=split(/:/,$item); if (not $nodes_number =~ /^(\d+)$/) { oar_error("[Hulot] Syntax error into ENERGY_SAVING_NODES_KEEPALIVE! (not an integer)\n"); exit(2); } $keepalive{$properties}=(); $keepalive{$properties}{"nodes"}=[]; $keepalive{$properties}{"min"}=$nodes_number; oar_debug("[Hulot] Keepalive(". $properties .") => ". $nodes_number ."\n"); } } # Creates the fifo if it doesn't exist unless ( -p $FIFO ) { unlink $FIFO; system( 'mknod', '-m', '600', $FIFO, 'p' ); } # Test if the FIFO has been correctly created unless ( -p $FIFO ) { oar_error("[Hulot] Could not create the fifo $FIFO!\n"); exit(1); } # Create message queue for Inter Processus Communication $id_msg_hulot = msgget( IPC_PRIVATE, IPC_CREAT | S_IRUSR | S_IWUSR ); if ( !defined $id_msg_hulot ) { oar_error("[Hulot] Cannot create message queue : msgget failed\n"); exit(1); } my $count_cycles; # Open the fifo while (1) { unless ( open( FIFO, "$FIFO" ) ) { oar_error("[Hulot] Could not open the fifo $FIFO!\n"); exit(2); } #debug #open(DUMP,">>/tmp/hulot_dump"); #my $pid=$$; # Start to manage commands and nodes comming on the fifo while () { #print DUMP "point 1:"; print `ps -p $pid -o rss h >> /tmp/hulot_dump`; my $key; my $nodeFinded = 0; my $nodeToAdd = 0; my $nodeToRemind = 0; my $rcvd; my $type_rcvd; my $base = OAR::IO::connect() or die("[Hulot] Cannot connect to the database\n"); if ( msgrcv( $id_msg_hulot, $rcvd, 600, 0, IPC_NOWAIT ) ) { ( $type_rcvd, $rcvd ) = unpack( $pack_template, $rcvd ); check_returned_cmd( $base, $rcvd, \%nodes_list_running, \%nodes_list_to_remind, \%nodes_list_to_process ); while ( msgrcv( $id_msg_hulot, $rcvd, 600, 0, IPC_NOWAIT ) ) { ( $type_rcvd, $rcvd ) = unpack( $pack_template, $rcvd ); check_returned_cmd( $base, $rcvd, \%nodes_list_running, \%nodes_list_to_remind, \%nodes_list_to_process ); } } ( my $cmd, my $nodes ) = split(/:/, $_, 2 ); chomp($nodes); my @nodes = split(/ /, $nodes ); if ( $cmd eq "CHECK" ) { oar_debug("[Hulot] Got request '$cmd'\n"); } else { oar_debug("[Hulot] Got request '$cmd' for nodes : $nodes\n"); } #print DUMP "point 2:"; print `ps -p $pid -o rss h >> /tmp/hulot_dump`; # Check idle and occupied nodes my @all_occupied_nodes=OAR::IO::get_alive_nodes_with_jobs($base); my @nodes_that_can_be_waked_up=OAR::IO::get_nodes_that_can_be_waked_up($base,OAR::IO::get_date($base)); foreach my $properties (keys %keepalive) { my @occupied_nodes; my @idle_nodes; $keepalive{$properties}{"nodes"} = [ OAR::IO::get_nodes_with_given_sql($base,$properties) ]; $keepalive{$properties}{"cur_idle"}=0; foreach my $alive_node (OAR::IO::get_nodes_with_given_sql($base, $properties. " and (state='Alive' or next_state='Alive')")) { if (grep(/^$alive_node$/,@all_occupied_nodes)) { push(@occupied_nodes,$alive_node); }else{ $keepalive{$properties}{"cur_idle"}+=1; push(@idle_nodes,$alive_node); } } #oar_debug("[Hulot] cur_idle($properties) => " # .$keepalive{$properties}{"cur_idle"}."\n"); #print DUMP "point 3:"; print `ps -p $pid -o rss h >> /tmp/hulot_dump`; # Wake up some nodes corresponding to properties if needed my $ok_nodes=$keepalive{$properties}{"cur_idle"} - $keepalive{$properties}{"min"}; my $wakeable_nodes=@{$keepalive{$properties}{"nodes"}} - @idle_nodes - @occupied_nodes; while ($ok_nodes < 0 && $wakeable_nodes > 0) { foreach my $node (@{$keepalive{$properties}{"nodes"}}) { unless (grep(/^$node$/,@idle_nodes) || grep(/^$node$/,@occupied_nodes)) { # we have a good candidate to wake up # now, check if the node has a good status $wakeable_nodes--; if (grep(/^$node$/,@nodes_that_can_be_waked_up)) { $ok_nodes++; # add WAKEUP:$node to list of commands if not already # into the current command list if (not defined($nodes_list_running{$node})) { $nodes_list_to_process{$node} = { 'command' => "WAKEUP", 'timeout' => -1 }; oar_debug("[Hulot] Waking up $node to satisfy '$properties' keepalive (ok_nodes=$ok_nodes, wakeable_nodes=$wakeable_nodes)\n"); }else{ if ($nodes_list_running{$node}->{'command'} ne "WAKEUP") { oar_debug("[Hulot] Wanted to wake up $node to satisfy '$properties' keepalive, but a command is already running on this node. So doing nothing and waiting for the next cycles to converge.\n"); } } } last if ($ok_nodes >=0 || $wakeable_nodes <= 0); } } } } #print DUMP "point 4:"; print `ps -p $pid -o rss h >> /tmp/hulot_dump`; # Retrieve list of nodes having at least one resource Alive my @nodes_alive = OAR::IO::get_nodes_with_given_sql($base,"state='Alive'"); # Checks if some booting nodes need to be suspected foreach $key ( keys(%nodes_list_running) ) { if ( $nodes_list_running{$key}->{'command'} eq "WAKEUP" ) { if (grep(/^$key$/,@nodes_alive)) { oar_debug( "[Hulot] Booting node '$key' seems now up, so removing it from running list.\n" ); # Remove node from the list running nodes remove_from_hash( \%nodes_list_running, $key ); } elsif (time > $nodes_list_running{$key}->{'timeout'}) { change_node_state( $base, $key, "Suspected" ); my $str = "[Hulot] Node $key was suspected because it did not wake up before the end of the timeout\n"; OAR::IO::add_new_event_with_host($base, "LOG_SUSPECTED", 0, $str, [$key]); # Remove suspected node from the list running nodes remove_from_hash( \%nodes_list_running, $key ); # Remove this node from received list (if node is present) because it was suspected remove_from_array( \@nodes, $key ); } } } # Check if some nodes in list_to_remind can be processed check_reminded_list( \%nodes_list_running, \%nodes_list_to_remind, \%nodes_list_to_process ); # Checking if each couple node/command was already received or not foreach my $node (@nodes) { $nodeFinded = 0; $nodeToAdd = 0; $nodeToRemind = 0; if ( keys(%nodes_list_running) > 0 ) { # Checking foreach $key ( keys(%nodes_list_running) ) { if ( $node eq $key ) { $nodeFinded = 1; if ( $cmd ne $nodes_list_running{$key}->{'command'} ) { # This node is already planned for an other action # We have to keep in memory this new couple node/command $nodeToRemind = 1; } else { oar_debug( "[Hulot] Command '$nodes_list_running{$key}->{'command'}' is already running on node '$node'\n" ); } } } if ( $nodeFinded == 0 ) { $nodeToAdd = 1; } } else { $nodeToAdd = 1; } if ( $nodeToAdd == 1 ) { # Adding couple node/command to the list to process # oar_debug( # "[Hulot] Adding '$node=>$cmd' to list to process\n"); $nodes_list_to_process{$node} = { 'command' => $cmd, 'timeout' => -1 }; } if ( $nodeToRemind == 1 ) { # Adding couple node/command to the list to remind # oar_debug( # "[Hulot] Adding '$node=>$cmd' to list to remember\n"); $nodes_list_to_remind{$node} = $cmd; } } # Creating command list my @commandToLaunch = (); my @dont_halt; my $match=0; # Get the timeout taking into account the number of nodes # already waking up + the number of nodes to wake up my $timeout = get_timeout(\%timeouts, scalar keys(%nodes_list_running) + scalar keys(%nodes_list_to_process)); foreach $key ( keys(%nodes_list_to_process) ) { SWITCH: for ( $nodes_list_to_process{$key}->{'command'} ) { /WAKEUP/ && do { #Save the timeout for the nodes to be processed. $nodes_list_to_process{$key}->{'timeout'} = time + $timeout; push( @commandToLaunch, "WAKEUP:$key" ); last; }; /HALT/ && do { # Don't halt nodes that needs to be kept alive $match=0; foreach my $properties (keys %keepalive) { my @nodes=@{$keepalive{$properties}{"nodes"}}; if (@nodes>0 && grep(/$key/,@nodes)) { if ($keepalive{$properties}{"cur_idle"} <= $keepalive{$properties}{"min"}) { oar_debug( "[Hulot] Not halting '$key' because I need to keep alive ". $keepalive{$properties}{"min"} ." nodes having '$properties'\n" ); $match=1; remove_from_hash(\%nodes_list_running,$key); remove_from_hash(\%nodes_list_to_process,$key); } } } # If the node is ok to be halted unless($match) { # Update the keepalive counts foreach my $properties (keys %keepalive) { my @nodes=@{$keepalive{$properties}{"nodes"}}; if (@nodes>0 && grep(/$key/,@nodes)) { $keepalive{$properties}{"cur_idle"}-=1; } } # Change state node to "Absent" and halt it change_node_state( $base, $key, "Absent" ); oar_debug( "[Hulot] Hulot module put node '$key' in energy saving mode (state~Absent)\n" ); push( @commandToLaunch, "HALT:$key" ); } last; }; oar_error("[Hulot] Unknown command: '".$nodes_list_to_process{$key}->{'command'}."' for node '$key'\n"); exit 1; } } OAR::IO::disconnect($base); # Launching commands if ( $#commandToLaunch >= 0 ) { my %forker_type = ( "type" => "Hulot", "id_msg" => $id_msg_hulot, "template" => $pack_template ); if (get_conf_with_default_param("ENERGY_SAVING_WINDOW_FORKER_BYPASS", "no") eq "yes") { #Bypassing OAR::WindowForker oar_debug("[Hulot] Launching commands to nodes\n"); #Strings that will be passed to wakeup and shutdown commands my @nodesToWakeUp = (); my @nodesToShutDown = (); #Build strings to pass to wakeup and shutdown commands my $base = OAR::IO::connect(); foreach my $command ( @commandToLaunch ) { (my $cmd, my $node)=split(/:/,$command, 2); if ( $cmd eq "HALT" ) { push(@nodesToShutDown, $node); OAR::IO::add_new_event_with_host($base,"HALT_NODE",0,"Node $node halt request",[$node]); } elsif ( $cmd eq "WAKEUP" ) { push(@nodesToWakeUp, $node); OAR::IO::add_new_event_with_host($base,"WAKEUP_NODE",0,"Node $node wake-up request",[$node]); } } OAR::IO::disconnect($base); execute_action(get_conf("ENERGY_SAVING_NODE_MANAGER_WAKE_UP_CMD"), \@nodesToWakeUp, "WAKEUP", \%forker_type); execute_action(get_conf("ENERGY_SAVING_NODE_MANAGER_SLEEP_CMD"), \@nodesToShutDown, "HALT", \%forker_type); } else { # Use the window forker to execute commands in parallel oar_debug( "[Hulot] Launching commands to nodes by using WindowForker\n" ); # fork in order to don't block the pipe listening $forker_pid = fork(); if ( defined($forker_pid) ) { if ( $forker_pid == 0 ) { ( my $t, my $y ) = OAR::WindowForker::launch( \@commandToLaunch, get_conf_with_default_param( "ENERGY_SAVING_WINDOW_SIZE", $ENERGY_SAVING_WINDOW_SIZE ), get_conf_with_default_param( "ENERGY_SAVING_WINDOW_TIME", $ENERGY_SAVING_WINDOW_TIME ), get_conf_with_default_param( "ENERGY_SAVING_WINDOW_TIMEOUT", $ENERGY_SAVING_WINDOW_TIMEOUT ), 0, \%forker_type ); exit 0; } } else { oar_error("[Hulot] Fork system call failed\n"); } } } # Check child endings while ( ( $forker_pid = waitpid( -1, WNOHANG ) ) > 0 ) { register_wait_results( $forker_pid, $? ); } # Adds to running list last new launched commands add_to_hash( \%nodes_list_to_process, \%nodes_list_running ); # Cleaning the list to process %nodes_list_to_process = (); # Suicide to workaround memory leaks. Almighty will restart hulot. $count_cycles++; if ($count_cycles > $max_cycles) { oar_warn("[Hulot] Reached $max_cycles cycles. Suiciding (place aux jeunes).\n"); # cleaning ipc shmctl($id_msg_hulot, IPC_RMID, 0); # <- doesn't work... why?? # saving state if (open(FILE,">$runtime_directory/hulot_status.dump")) { # removing HALT commands from state file as we don't check timeout on that foreach my $node ( keys(%nodes_list_running) ) { if ($nodes_list_running{$node}->{'command'} eq "HALT") { remove_from_hash( \%nodes_list_running, $node ); } } print FILE Dumper([\%nodes_list_running,\%nodes_list_to_remind]); }else{ oar_error("[Hulot] could not open $runtime_directory/hulot_status.dump for writing!"); } close(FIFO); unlink $FIFO; exit(42); } } close(FIFO); # Unfortunately, never reached: shmctl($id_msg_hulot, IPC_RMID, 0); } } sub execute_action($$$$) { my ($command, $nodes, $cmd, $type) = @_; my %forker_type = %$type; if ($#{$nodes} >= 0) { my $command_to_exec = "echo \"" . join(" ", @{$nodes}) . "\" | " . $command; print $command_to_exec . " \n"; my $forker_pid = fork(); if ( defined($forker_pid) ) { if ( $forker_pid == 0 ) { system($command_to_exec); foreach my $node ( @{$nodes} ) { if (!msgsnd($forker_type{"id_msg"}, pack($forker_type{"template"}, 1, "$node:$cmd:".$?), IPC_NOWAIT)){ oar_error("[Hulot] Failed to send message by msgsnd(): $!\n"); } } exit 0; } } else { oar_error("[Hulot] Fork system call failed, command \"" . $command_to_exec . "\" not executing\n"); } } } # Treate finished processes sub register_wait_results($$) { my ( $pid, $return_code ) = @_; my $exit_value = $return_code >> 8; my $signal_num = $return_code & 127; my $dumped_core = $return_code & 128; if ( $pid > 0 ) { #oar_debug("[DEBUG-HULOT] Child process $pid ended : exit_value = $exit_value, signal_num = $signal_num, dumped_core = $dumped_core \n"); } } ## wake_up_nodes sub wake_up_nodes($) { my $nodes = shift; return send_cmd_to_fifo( $nodes, "WAKEUP" ); } #Fill the timeouts hash with the different timeouts sub fill_timeouts ($) { my $string = shift; my %timeouts = (); # test if the timeout is a simple duration in seconds if ($string =~ /^\s*\d+\s*$/ ){ $timeouts{1} = int($string); } else { #Remove front spaces $string =~ s/^\s+//; #Values must be separated by non-printable characters my @words = split( /\s+/, $string); my @vals = (); foreach my $couple (@words) { #Each couple of values is only composed of digits separated by if ($couple =~ /^\d+:\d+$/) { @vals = split(/:/, $couple); $timeouts{$vals[0]} = $vals[1]; } else { oar_warning("[Hulot] \"$couple\" is not a valid couple for a timeout\n"); } } } #If no good value has been found, use the default one if ( keys( %timeouts ) == 0) { $timeouts{1} = $ENERGY_SAVING_NODE_MANAGER_WAKEUP_TIMEOUT; oar_warning("[Hulot] Timeout not properly defined, using default value: $ENERGY_SAVING_NODE_MANAGER_WAKEUP_TIMEOUT\n"); } return %timeouts; } #Choose a timeout based on the number of nodes to wake up sub get_timeout($$) { my ($timeouts, $nb_nodes) = @_; my $timeout = $ENERGY_SAVING_NODE_MANAGER_WAKEUP_TIMEOUT; $timeout = @$timeouts{1} if (defined(@$timeouts{1})); #Search for the timeout of the corresponding interval foreach my $tmp ( sort { $a <=> $b } keys( %$timeouts ) ) { last if ($nb_nodes < $tmp); $timeout = @$timeouts{$tmp}; } return $timeout; } return (1); ./oar-2.5.2/sources/core/scripts/0000755000175000017500000000000011757171206014663 5ustar plbplb./oar-2.5.2/sources/core/scripts/server_prologue0000755000175000017500000000061311757171206020033 0ustar plbplb#!/bin/bash # $Id$ # # This a placeholder script for OAR jobs server side prologue. # This script is to be run on OAR server upon job start. # This feature is activated if and only if SERVER_PROLOGUE_EXEC_FILE is set # in OAR configuration file. # Usage: # Script is run under uid of oar who is sudo # argv[1] is the jobid # Other job information are meant to be retrieved using oarstat. exit ./oar-2.5.2/sources/core/scripts/server_epilogue0000755000175000017500000000062111757171206020007 0ustar plbplb#!/bin/bash # $Id$ # # This a placeholder script for OAR jobs server side epilogue. # This script is to be run on OAR server upon job termination. # This feature is activated if and only if SERVER_EPILOGUE_EXEC_FILE is set # in OAR configuration file. # Usage: # Script is run under uid of oar who is sudo # argv[1] is the jobid # Other job information are meant to be retrieved using oarstat. exit ./oar-2.5.2/sources/core/scripts/prologue0000755000175000017500000000070411757171206016446 0ustar plbplb#!/bin/bash # $Id$ # # This a placeholder script for OAR jobs prologue. # This script is to be run on the first node of a job, upon start. # This feature is activated if and only if PROLOGUE_EXEC_FILE is set in OAR # configuration file. # # Usage: # Script is run under uid of oar who is sudo # argv[1] is the jobid # argv[2] is the user's name # argv[3] is the file which contains the list of nodes used # argv[4] is the job walltime in seconds exit ./oar-2.5.2/sources/core/scripts/pam/0000755000175000017500000000000011757171206015440 5ustar plbplb./oar-2.5.2/sources/core/scripts/pam/common-account0000644000175000017500000000100111757171206020275 0ustar plbplb# # /etc/pam.d/common-account - authorization settings common to all services # # This file is included from other service-specific PAM config files, # and should contain a list of the authorization modules that define # the central access policy for use on the system. The default is to # only deny service to users whose accounts are expired in /etc/shadow. # account required pam_unix.so account sufficient pam_access.so account required pam_access.so accessfile=/etc/security/access_cluster.conf ./oar-2.5.2/sources/core/scripts/pam/access_cluster.conf0000644000175000017500000000002611757171206021307 0ustar plbplb#+:user:ALL -:ALL:ALL ./oar-2.5.2/sources/core/scripts/pam/access.conf0000644000175000017500000000471711757171206017561 0ustar plbplb# Login access control table. # # When someone logs in, the table is scanned for the first entry that # matches the (user, host) combination, or, in case of non-networked # logins, the first entry that matches the (user, tty) combination. The # permissions field of that table entry determines whether the login will # be accepted or refused. # # Format of the login access control table is three fields separated by a # ":" character: # # [Note, if you supply a 'fieldsep=|' argument to the pam_access.so # module, you can change the field separation character to be # '|'. This is useful for configurations where you are trying to use # pam_access with X applications that provide PAM_TTY values that are # the display variable like "host:0".] # # permission : users : origins # # The first field should be a "+" (access granted) or "-" (access denied) # character. # # The second field should be a list of one or more login names, group # names, or ALL (always matches). A pattern of the form user@host is # matched when the login name matches the "user" part, and when the # "host" part matches the local machine name. # # The third field should be a list of one or more tty names (for # non-networked logins), host names, domain names (begin with "."), host # addresses, internet network numbers (end with "."), ALL (always # matches) or LOCAL (matches any string that does not contain a "." # character). # # If you run NIS you can use @netgroupname in host or user patterns; this # even works for @usergroup@@hostgroup patterns. Weird. # # The EXCEPT operator makes it possible to write very compact rules. # # The group file is searched only when a name does not match that of the # logged-in user. Both the user's primary group is matched, as well as # groups in which users are explicitly listed. # # TTY NAMES: Must be in the form returned by ttyname(3) less the initial # "/dev/" (e.g. tty1 or vc/1) # ############################################################################## # # Disallow non-root logins on tty1 # #-:ALL EXCEPT root:tty1 # # Disallow console logins to all but a few accounts. # #-:ALL EXCEPT wheel shutdown sync:LOCAL # # Disallow non-local logins to privileged accounts (group wheel). # #-:wheel:ALL EXCEPT LOCAL .win.tue.nl # # Some accounts are not allowed to login from anywhere: # #-:wsbscaro wsbsecr wsbspac wsbsym wscosor wstaiwde:ALL # # All other accounts are allowed to login from anywhere. # #-:ALL EXCEPT root oar:ALL +:ALL:LOCAL -:ALL EXCEPT root oar:ALL ./oar-2.5.2/sources/core/scripts/oar_server_proepilogue.pl0000755000175000017500000001037611757171206022013 0ustar plbplb#!/usr/bin/perl # This script launch a oarstat command to retreive all data about the given job. # Then all nodes are contacted and their /etc/security/access_cluster.conf # are populated to enable the ssh connection to this OAR job owner if the whole # nodes are allocated. # # Typically you have to write in oar.conf: # # SERVER_PROLOGUE_EXEC_FILE="/etc/oar/oar_server_proepilogue.pl START" # SERVER_EPILOGUE_EXEC_FILE="/etc/oar/oar_server_proepilogue.pl STOP" # use Fcntl; my $Taktuk_cmd = "/usr/bin/taktuk -s -c 'ssh'"; my $Oarstat_cmd = "oarstat"; my $Security_file = "/etc/security/access_cluster.conf"; my $Job_id = $ARGV[1]; if ($Job_id !~ m/^\d+$/m){ warn("ERROR: cannot get the job id\n"); exit(1); } my $struct; if (open(OARSTAT, "$Oarstat_cmd -fj $Job_id -D |")){ $struct = eval(); close(OARSTAT); if (!defined($struct->{state})){ warn("ERROR: Cannot read correctly the result of the command $Oarstat_cmd\n"); exit(4); } }else{ warn("ERROR: Cannot execute command $Oarstat_cmd\n"); exit(3); } if ($ARGV[0] eq "START"){ my $str = ' my $file_str = "# File generated by OAR.\n"; if (! system("diff /dev/cpuset/oar/'.$struct->{cpuset_name}.'/cpus /dev/cpuset/cpus > /dev/null 2>&1")){ $file_str .= "+:'.$struct->{owner}.':ALL\n"; } $file_str .= "-:ALL:ALL\n"; if (open(ACCESS, "> '.$Security_file.'")){ print(ACCESS "$file_str"); close(ACCESS); }else{ exit(1); } '; my @tmp = diffuse_cmd($struct->{assigned_network_address},$str,$Job_id); if ($#tmp >= 0){ warn("ERROR: Some nodes are inaccessible @tmp\n"); exit(5); } }elsif ($ARGV[0] eq "STOP"){ my $str = ' if (open(ACCESS, "> '.$Security_file.'")){ print(ACCESS "# File generated by OAR.\n"); print(ACCESS "-:ALL:ALL\n"); close(ACCESS); }else{ exit(1); } if (! system("diff /dev/cpuset/oar/'.$struct->{cpuset_name}.'/cpus /dev/cpuset/cpus > /dev/null 2>&1")){ # system("su - '.$struct->{owner}.' -c \'kill -9 -1\'"); } '; my @tmp = diffuse_cmd($struct->{assigned_network_address},$str,$Job_id); if ($#tmp >= 0){ warn("ERROR: Some nodes are inaccessible @tmp\n"); exit(5); } }else{ warn("ERROR: unknown first argument $ARGV[0]\n"); exit(2); } sub diffuse_cmd($$$){ my $hosts = shift; my $perl_script = shift; my $job_id = shift; pipe(tak_node_read,tak_node_write); pipe(tak_stdin_read,tak_stdin_write); pipe(tak_stdout_read,tak_stdout_write); my $pid = fork; if($pid == 0){ #CHILD $SIG{CHLD} = 'DEFAULT'; $SIG{TERM} = 'DEFAULT'; $SIG{INT} = 'DEFAULT'; $SIG{QUIT} = 'DEFAULT'; $SIG{USR1} = 'DEFAULT'; $SIG{USR2} = 'DEFAULT'; my $cmd = "$Taktuk_cmd ".'-o status=\'"STATUS $host $line\n"\''." -f '<&=".fileno(tak_node_read)."' broadcast exec [ sudo perl - SERVER_SCRIPT ], broadcast input file [ - ], broadcast input close"; fcntl(tak_node_read, F_SETFD, 0); close(tak_node_write); close(tak_stdout_read); close(STDOUT); # Redirect taktuk output into the pipe open(STDOUT, ">& tak_stdout_write"); # Use the child STDIN to send the user command close(tak_stdin_write); close(STDIN); open(STDIN, "<& tak_stdin_read"); exec($cmd); warn("[ERROR] Cannot execute $cmd\n"); exit(-1); } close(tak_node_read); close(tak_stdin_read); close(tak_stdout_write); my $tmp_node_hash; # Send node list foreach my $n (@{$hosts}){ $tmp_node_hash{$n} = 1; print(tak_node_write "$n\n"); } close(tak_node_write); eval{ $SIG{ALRM} = sub { die "alarm\n" }; alarm(50); # Send data structure to all nodes print(tak_stdin_write $perl_script); close(tak_stdin_write); # Check good nodes from the stdout taktuk while(){ if ($_ =~ /^STATUS ([\w\.\-\d]+) (\d+)$/){ if ($2 == 0){ delete($tmp_node_hash{$1}) if (defined($tmp_node_hash{$1})); } }else{ print("[TAKTUK OUTPUT] $_"); } } close(tak_stdout_read); alarm(0); }; @bad = keys(%tmp_node_hash); return(@bad); } ./oar-2.5.2/sources/core/scripts/oar_prologue_local0000755000175000017500000000261311757171206020462 0ustar plbplb#!/bin/bash # $Id$ # prologue_local arguments # Script is run under uid of oar who is sudo # argv[1] is the jobid # argv[2] is the user's name # argv[3] is the job script name if exists . ~oar/lock_user.sh USERFILE="/tmp/oar_user_list.oar.$2" USERFILETMP="$USERFILE.tmp" LOCKRESOURCE="oarlock" #LOCKRESOURCE=$2 lock_file $1 $LOCKRESOURCE if (( $? == 1 )) then echo "/!\\ Lock command timed out, it overrides previous command" fi echo $1 >> $USERFILE ######################################## # PROLOGUE ACCESS_FILE=/etc/security/access_cluster.conf #ALLOWEDUSERS=`cat $ACCESS_FILE | grep -v "\#" | grep "^-" | sed s/\-:ALL\ EXCEPT\ // | sed s/:ALL/\ $2/ | sed s/^\ *// | sed s/\ *$//` #echo "+:ALL:LOCAL" > $ACCESS_FILE #echo "-:ALL EXCEPT $ALLOWEDUSERS:ALL" >> $ACCESS_FILE ACCESS_FILE_NEW=/tmp/access_cluster.conf.new echo "# File generated by OAR." > "${ACCESS_FILE_NEW}" echo "# You can remove it, but don't modify it!" >> "${ACCESS_FILE_NEW}" echo "+:${2}:ALL" >> "${ACCESS_FILE_NEW}" grep -v "^[-#]" < "${ACCESS_FILE}" >> "${ACCESS_FILE_NEW}" echo "-:ALL:ALL" >> "${ACCESS_FILE_NEW}" sudo mv -f "${ACCESS_FILE_NEW}" "${ACCESS_FILE}" ######################################## unlock_file $1 $LOCKRESOURCE if (( $? == 1 )) then echo "/!\\unlock command says that it is not our lock" fi ./oar-2.5.2/sources/core/scripts/oar_prologue0000755000175000017500000000075111757171206017311 0ustar plbplb#!/bin/bash # $Id$ # prologue arguments # Script is run under uid of oar who is sudo # argv[1] is the jobid # argv[2] is the user's name # argv[3] is the file which contains the list of nodes used # argv[4] is the job script name if exists exit # Redirect stdout and stderr in a file exec &> /tmp/oar_prologue.log #Exemple for mono processor cluster DIFFUSE_SCRIPT=~oar/oar_diffuse_script PROLOGUE_LOCAL=~oar/oar_prologue_local CMD="$PROLOGUE_LOCAL $1 $2 $4" $DIFFUSE_SCRIPT $3 $CMD ./oar-2.5.2/sources/core/scripts/oar_epilogue_local0000755000175000017500000000344211757171206020440 0ustar plbplb#!/bin/bash # $Id$ # prologue_local arguments # Script is run under uid of oar who is sudo # argv[1] is the jobid # argv[2] is the user's name # argv[3] is the job script name if exists . ~oar/lock_user.sh USERFILE="/tmp/oar_user_list.oar.$2" USERFILETMP="$USERFILE.tmp" LOCKRESOURCE="oarlock" #LOCKRESOURCE=$2 lock_file $1 $LOCKRESOURCE if (( $? == 1 )) then echo "/!\\ Lock command timed out, it overrides previous command" fi grep -v "^$1$" $USERFILE > $USERFILETMP NBOTHERJOBS=`grep -vc "^$1$" $USERFILE` mv -f $USERFILETMP $USERFILE ################################################## # EPILOGUE echo "NBOTHERJOBS= $NBOTHERJOBS" if (( $NBOTHERJOBS < 1 )) then #look if the user is "root" or "oar" or an admin user # TO CHANGE WITH A BETTER TEST !!!!!!! cat /etc/security/access.conf | grep -v "\#" | grep $2 > /dev/null if (( $? != 0 )) then # we are sure that this user $2 has no other reservation active on this node echo "We can frag everything owned by user $2 : sudo -u $2 kill -9 -1 " fi fi ACCESS_FILE=/etc/security/access_cluster.conf #ALLOWEDUSERS=`cat $ACCESS_FILE | grep -v "\#" | grep "^-" | sed s/\-:ALL\ EXCEPT\ // | sed s/:ALL// | sed s/$2// | sed s/^\ *// | sed s/\ *$//` #echo "+:ALL:LOCAL" > $ACCESS_FILE #echo "-:ALL EXCEPT $ALLOWEDUSERS:ALL" >> $ACCESS_FILE ACCESS_FILE_NEW=/tmp/access_cluster.conf.new NB_SAMELINES=`grep -c "^+:${2}" < "${ACCESS_FILE}"` (( NB_SAMELINES = NB_SAMELINES - 1 )) grep -v "^+:${2}" < "${ACCESS_FILE}" > "${ACCESS_FILE_NEW}" grep -m $NB_SAMELINES "^+:${2}" < "${ACCESS_FILE}" >> "${ACCESS_FILE_NEW}" sudo mv -f "${ACCESS_FILE_NEW}" "${ACCESS_FILE}" ################################################### unlock_file $1 $LOCKRESOURCE if (( $? == 1 )) then echo "/!\\unlock command says that it is not our lock" fi ./oar-2.5.2/sources/core/scripts/oar_epilogue0000755000175000017500000000075111757171206017266 0ustar plbplb#!/bin/bash # $Id$ # prologue arguments # Script is run under uid of oar who is sudo # argv[1] is the jobid # argv[2] is the user's name # argv[3] is the file which contains the list of nodes used # argv[4] is the job script name if exists exit # Redirect stdout and stderr in a file exec &> /tmp/oar_epilogue.log #Exemple for mono processor cluster DIFFUSE_SCRIPT=~oar/oar_diffuse_script EPILOGUE_LOCAL=~oar/oar_epilogue_local CMD="$EPILOGUE_LOCAL $1 $2 $4" $DIFFUSE_SCRIPT $3 $CMD ./oar-2.5.2/sources/core/scripts/oar_diffuse_script0000755000175000017500000000151511757171206020465 0ustar plbplb#!/bin/bash # $Id$ # diffuse command on each nodes # Script is run under uid of oar who is sudo # argv[1] is the file which contains the list of nodes used # argv[2...] is the command to run on each node #exit #Exemple for mono processor cluster #SENTINELLE=/usr/bin/sentinelle SSH=ssh NODEFILE=$1 shift CMD=$@ if [ "a$SENTINELLE" != "a" ] then echo "Launch $CMD on each nodes with sentinelle" for i in `sort -u $NODEFILE` do NODESENTINELLE="$NODESENTINELLE -m $i" done echo "$SENTINELLE -cconnect=$SSH,timeout=30000 $NODESENTINELLE -- $CMD" $SENTINELLE -cconnect=$SSH,timeout=30000 $NODESENTINELLE -- "$CMD" else echo "Launch $CMD on each nodes with ssh" for i in `sort -u $NODEFILE` do ($SSH $i "$CMD" || echo "/!\\ SOMETHING GOES WRONG WITH $i, exit_code = $?") & done wait fi ./oar-2.5.2/sources/core/scripts/lock_user.sh0000644000175000017500000000240311757171206017204 0ustar plbplb# $Id$ #It is a lock on idJob per user name LOCKFILE=/tmp/.OARlockfile MAXBLOCKTIME=120 SLEEPTIME=1 # take the lock # $1 : job ID for the reservation # $2 : user login #return 1 if the lock times out lock_file () { SPECIFICLOCKFILE=$LOCKFILE"_"$2 RETURNCODE=0 WAITINGTIME=0 set -C until ( echo $1 > $SPECIFICLOCKFILE ) 2> /dev/null do echo "[LOCK] I am waiting for $WAITINGTIME seconds" if (( $WAITINGTIME > $MAXBLOCKTIME)) then echo "[LOCK] It is enough, it may be a bug; I take the lock" rm -f $SPECIFICLOCKFILE RETURNCODE=1 else sleep $SLEEPTIME fi ((WAITINGTIME= $WAITINGTIME + $SLEEPTIME)) done set +C echo "[LOCK] Lock taken" return $RETURNCODE } #release semaphore # $1 : job ID for the reservation # $2 : user login # return 1 if lock file is not right unlock_file () { SPECIFICLOCKFILE=$LOCKFILE"_"$2 RETURNCODE=0 LOCKFILEID=`cat $SPECIFICLOCKFILE` if [ "x$LOCKFILEID" == "x$1" ] then echo "[LOCK] I release the lock" rm -f $SPECIFICLOCKFILE else echo "[LOCK] this lock is not mine, execution was too long; maximum is $MAXBLOCKTIME" RETURNCODE=1 fi return $RETURNCODE } ./oar-2.5.2/sources/core/scripts/init.d/0000755000175000017500000000000011757171206016050 5ustar plbplb./oar-2.5.2/sources/core/scripts/init.d/README0000644000175000017500000000040411757171206016726 0ustar plbplbInit scripts are available for debian and rpm based distribution. Get the one you need in the packaging trees: Server: DEB: debian/oar-server.init.d RPM: rpm/SOURCES/oar-server.init.d Node: DEB: debian/oar-node.init.d RPM: rpm/SOURCES/oar-node.init.d ./oar-2.5.2/sources/core/scripts/epilogue0000755000175000017500000000071311757171206016423 0ustar plbplb#!/bin/bash # $Id$ # # This a placeholder script for OAR jobs epilogue. # This script is to be run on the first node of a job, upon termination. # This feature is activated if and only if EPILOGUE_EXEC_FILE is set in OAR # configuration file. # # Usage: # Script is run under uid of oar who is sudo # argv[1] is the jobid # argv[2] is the user's name # argv[3] is the file which contains the list of nodes used # argv[4] is the job walltime in seconds exit ./oar-2.5.2/sources/core/scripts/README0000644000175000017500000000711511757171206015547 0ustar plbplbExemple files description: We give some prologue and epilogue files for exemple. If you want, you can change them to your cluster specifications. oar_prologue : arg1 : jobId arg2 : user name arg3 : node list file arg4 : name of user script executed This script is executed by OAR system before user job. The user "oar" launchs this and he can do everything with "sudo" command (so you can do administrative tasks on your computer as root). This script is executed only on the first job node. That why we use "oar_diffuse_script" to propagate commands. oar_epilogue : arg1 : jobId arg2 : user name arg3 : node list file arg4 : name of user script executed This script is executed by OAR system after user job. The user "oar" launchs this and he can do everything with "sudo" command (so you can do administrative tasks on your computer as root). This script is executed only on the first job node. That why we use "oar_diffuse_script" to propagate commands. oar_diffuse_script : arg1 : file name which contains the list of nodes where to launch the commands arg2..end : command line to launch on each node This script executes a command on a node set. You have two choices : - default choice : it make a loop of "ssh" commands in background and it waits end of all "ssh". - optimal choice : you have installed a tool like "rshp" or "sentinelle" on each nodes (these tools make a "ssh" connection tree with nodes and are faster than the previous choice) and you can execute the command via this tool. You can also configure the connection system. "ssh" is the default but you can use "rsh" instead. lock_user.sh : This file contains two functions : - lock_file : blocks execution until the file is removed. There is a lock file per resource. - unlock_file : remove lock file. These functions give an exclusive access to a node on a specified resource. oar_prologue_local : arg1 : jobId arg2 : user name This script is executed by "oar_diffuse_script" on each nodes before brgining of each jobs. It uses "lock_user.sh" and can clean the computer safely. oar_epilogue_local : arg1 : jobId arg2 : user name This script is executed by "oar_diffuse_script" on each nodes after end of each jobs. It uses "lock_user.sh" and can clean the computer safely. ######### # NOTES # ######### If you want to daemonize a command in the prologue or in epilogue (it is not recommended), don't forget to use nohup: ex: nohup toto.sh &>/dev/null & (toto.sh can execute itself even if the prologue or epilogue is ended) ####################### # access restrictions # ####################### You can disable all authentication on reserved nodes except for the user of this one: - edit pam/common-account and add in your /etc/pam.d/common-account the line with pam_access.so module - edit /etc/security/access.conf to add users that can log into the node all the time (see pam/access.conf) - edit /etc/security/access_cluster.conf and copy the lines from pam/access_cluster.conf (all local users can log into the node and only listed users from the network) ################### # Troubleshooting # ################### The processus executed by OAR to prepare and launch the user job may be detached from its ssh connexion. So we must redirect STDIN, STDOUT and STDERR into /dev/null (when you detach a processus you are obliged to do that or some programs could hang). In this way you cannot see the results of "echo" command in the prologue and epilogue. I suggest to write your debug informations in files instead. ./oar-2.5.2/sources/core/qfunctions/0000755000175000017500000000000011757171206015365 5ustar plbplb./oar-2.5.2/sources/core/qfunctions/oarsub0000755000175000017500000012724711757171206016623 0ustar plbplb#!/usr/bin/perl #Sumbit job for execution package oarsub; require Exporter; use strict; #use warnings; use POSIX qw(strftime); use Fcntl; use Data::Dumper; use Sys::Hostname; use Getopt::Long; use File::Basename; use File::Temp qw/ tempfile /; use OAR::Conf qw(init_conf dump_conf get_conf is_conf); use OAR::Sub; my $Old_umask = sprintf("%lo",umask()); umask(oct("022")); select(STDOUT); $| = 1; #Try to load XML module my $XML_enabled = 1; unless (eval "use XML::Dumper qw(pl2xml);1"){ $XML_enabled = 0; } #Try to load YAML module my $YAML_enabled = 1; unless (eval "use YAML;1"){ $YAML_enabled = 0; } #Try to load JSON module my $JSON_enabled = 1; unless (eval "use JSON;1"){ $JSON_enabled = 0; } # suitable Data::Dumper configuration for serialization $Data::Dumper::Purity = 1; $Data::Dumper::Terse = 1; $Data::Dumper::Indent = 0; $Data::Dumper::Deepcopy = 1; #For interactive my $remote_host; my $remote_port; my $Deploy_hostname; my $Cosystem_hostname; my $Cpuset_field; my $Cpuset_path; my $Host = hostname ; my $Server; my $Job_id; my $Job_id_list_ref; my $Interactive = 0; my $Reservation = "0"; my $Default_resources; my $Nodes_resources; my $connect_job ; my $Scan_script; my @resource; my $Queue_name; my $Job_sql_properties = ""; my $Cmd_executor; my $stagein = undef; my $idFile = undef; my $md5sum = undef; my $stageindir; my $sos; my $Checkpoint=0; my $notify; my $job_name; my $job_env; my $job_hold; my $Directory; my $Directory_default = $ENV{PWD}; my @Type; my @Anterior_job; my $Checkpoint_signal; my $Checkpoint_signal_default = 12; # SIGUSR2 my $Stdout_file; my $Stderr_file; my $Resubmit; my $Project; my $Project_default = "default"; my $XML_mode; my $YAML_mode; my $JSON_mode; my $DUMPER_mode; my $use_job_key; my $job_key_priv = ""; my $job_key_pub = ""; my $import_job_key_file = ""; my $import_job_key_inline = ""; my $export_job_key_file = ""; my $Job_uid_resource_type; my $Initial_request_string = "oarsub @ARGV"; # a non array-job is an array-job with 1 subjob my $array_job_nb; my $array_param_file; my $array_params_ref; #to catch ^C signal $SIG{INT} = \&qdel; $SIG{HUP} = \&qdel; $SIG{PIPE} = \&qdel; # to address ^C in interactive submission sub qdel($) { my $Al_dead = shift; if (defined($Job_id_list_ref)) { if ($Al_dead eq "INT" ) { print(STDERR "\n\nCaught Interrupt (^C), "); } OAR::Sub::delete_jobs($Job_id_list_ref, $remote_host, $remote_port); exit(1); } } # Connect to a job and give the shell of the user on the remote host sub connect_job($$$){ my $job_id = shift; my $stop_oarexec = shift; my $openssh_cmd = shift; my $xauth_path = $ENV{OARXAUTHLOCATION}; OAR::Sub::open_ro_db_connection(); my $lusr= $ENV{OARDO_USER}; my $job = OAR::Sub::get_job($job_id); if ((($lusr eq $job->{job_user}) or ($lusr eq "oar")) && ($job->{state} eq "Running")) { my $hosts_tmp = OAR::Sub::get_job_current_hostnames($job_id); my @hosts = @$hosts_tmp; my $host_to_connect_via_ssh = $hosts[0]; #deploy, cosystem and no host part my $types = OAR::Sub::get_current_job_types($job_id); if ((defined($types->{cosystem})) or ($#hosts < 0)){ $host_to_connect_via_ssh = $Cosystem_hostname; }elsif (defined($types->{deploy})){ $host_to_connect_via_ssh = $Deploy_hostname; } #cpuset part if ((defined($Cpuset_field) and defined($Cpuset_path) and (!defined($types->{cosystem})) and (!defined($types->{deploy})) and ($#hosts >= 0))){ $ENV{OAR_CPUSET} = $Cpuset_path.'/'.OAR::Sub::get_job_cpuset_name($job_id); }else{ $ENV{OAR_CPUSET} = ""; } my $moldable = OAR::Sub::get_current_moldable_job($job->{assigned_moldable_job}); my $job_cpuset_uid; $job_cpuset_uid = OAR::Sub::get_job_cpuset_uid($job->{assigned_moldable_job}, $Job_uid_resource_type, $Cpuset_field) if (defined($Job_uid_resource_type)); my $job_user = OAR::Sub::format_job_user($job->{job_user},$job_id,$job_cpuset_uid); OAR::Sub::close_db_connection(); my @passinfo = getpwnam($lusr) or die("Cannot retreive system information for user $lusr\n"); my $shell=$passinfo[8]; unless ((defined($xauth_path)) and (-x $xauth_path) and ($ENV{DISPLAY} =~ /^[\w.-]*:\d+(?:\.\d+)?$/)) { $ENV{DISPLAY}=""; } if ($ENV{DISPLAY} ne ""){ print("Initialize X11 forwarding...\n"); # first, get rid of remaining unused .Xautority.{pid} files... system({"bash"} "bash","-c",'for f in $HOME/.Xauthority.*; do [ -e "/proc/${f#$HOME/.Xauthority.}" ] || rm -f $f; done'); my $new_xauthority = $ENV{HOME}."/.Xauthority.$$"; system({"bash"} "bash","-c",'[ -x "'.$xauth_path.'" ] && OARDO_BECOME_USER='.$lusr.' oardodo bash --noprofile --norc -c "'.$xauth_path.' extract - ${DISPLAY/#localhost:/:}" | XAUTHORITY='.$new_xauthority.' '.$xauth_path.' -q merge - 2>/dev/null'); $ENV{XAUTHORITY} = $new_xauthority; } my $node_file = OAR::Sub::get_default_oarexec_directory()."/$job_id"; my $res_file = OAR::Sub::get_default_oarexec_directory()."/$job_id"."_resources"; my $oarsub_pids = OAR::Sub::get_oarsub_connections_file_name($job_id); my %params = ( "node_file" => $node_file, "job_id" => $job_id, "array_id" => $job->{array_id}, "array_index" => $job->{array_index}, "user" => $lusr, "shell" => $shell, "launching_directory" => $job->{launching_directory}, "resource_file" => $res_file, "job_name" => $job->{job_name}, "job_project" => $job->{project}, "job_walltime" => OAR::Sub::duration_to_sql($moldable->{moldable_walltime}), "job_walltime_sec" => $moldable->{moldable_walltime}, "job_env" => $job->{job_env} ); my $str = OAR::Sub::get_oarexecuser_script_for_oarsub(\%params); my ($cmd_name,@cmd_opts) = split(" ",$openssh_cmd); my @cmd; my $i = 0; $cmd[$i] = $cmd_name;$i++; foreach my $p (@cmd_opts){ $cmd[$i] = $p;$i++; } if ($ENV{OAR_CPUSET} ne ""){ $cmd[$i] = "-oSendEnv=OAR_CPUSET"; $i++; } if ($ENV{DISPLAY} ne ""){ $cmd[$i] = "-X"; }else{ $cmd[$i] = "-x"; } $i++; $cmd[$i] = "-t";$i++; $cmd[$i] = $host_to_connect_via_ssh;$i++; $str =~ s/\n//g; if ($ENV{DISPLAY} ne ""){ $cmd[$i] = "bash -c 'echo \$PPID >> $oarsub_pids && ($xauth_path -q extract - \${DISPLAY/#localhost:/:} | OARDO_BECOME_USER=$lusr oardodo $xauth_path merge -) && [ \"$lusr\" != \"$job_user\" ] && OARDO_BECOME_USER=$lusr oardodo bash --noprofile --norc -c \"chmod 660 \\\$HOME/.Xauthority\" ;TTY=\$(tty) && test -e \$TTY && oardodo chown $job_user:oar \$TTY && oardodo chmod 660 \$TTY' && OARDO_BECOME_USER=$job_user oardodo bash --noprofile --norc -c '$str'";$i++; }else{ $cmd[$i] = "bash -c 'echo \$PPID >> $oarsub_pids && TTY=\$(tty) && test -e \$TTY && oardodo chown $job_user:oar \$TTY && oardodo chmod 660 \$TTY' && OARDO_BECOME_USER=$job_user oardodo bash --noprofile --norc -c '$str'";$i++; } #print("oarsub launchs command : @cmd\n"); #essential : you become oar instead of the user #UID=EUID $< = $>; print("Connect to OAR job $job_id via the node $host_to_connect_via_ssh\n"); system({$cmd[0]} @cmd); my $exit_value = $? >> 8; if ($exit_value == 2){ warn("[ERROR] Cannot go into the working directory : $job->{launching_directory}\n"); }elsif($exit_value != 0){ warn("[ERROR] An unknown error occured : $?\n"); } if ($stop_oarexec > 0){ my %params = ( "host" => $host_to_connect_via_ssh, "job_id" => $job_id, "signal" => "USR1", "time_to_wait" => 0, "ssh_cmd" => $openssh_cmd ); OAR::Sub::signal_oarexec(\%params); } print("Disconnected from OAR job $job_id\n"); }else{ if ($job->{state} ne "Running"){ warn("/!\\ ERROR : the job $job_id is not running. Its current state is $job->{state}.\n"); } if (($lusr ne $job->{job_user}) and ($lusr ne "oar")){ warn("/!\\ ERROR : you are not the right user for the job $job_id. This job is owned by $job->{job_user}.\n"); } OAR::Sub::close_db_connection(); return(20); } return(0); } #Print help message sub usage() { print <] Submit a job the OAR batch scheduler Options are: -I, --interactive Request an interactive job. Open a login shell on the first node of the reservation instead of running a script. -C, --connect= Connect to a running job -l, --resource= Set the requested resources for the job. The different parameters are resource properties registered in OAR database, and `walltime' which specifies the duration before the job must be automatically terminated if still running. Walltime format is [hour:mn:sec|hour:mn|hour]. Ex: host=4/cpu=1,walltime=2:00:00 --array Specify an array job with 'number' subjobs --array-param-file Specify an array job on which each subjob will receive one line of the file as parameter -S, --scanscript Batch mode only: asks oarsub to scan the given script for OAR directives (#OAR -l ...) -q, --queue= Set the queue to submit the job to -p, --property="" Add constraints to properties for the job. (format is a WHERE clause from the SQL syntax) -r, --reservation= Request a job start time reservation, instead of a submission. The date format is "YYYY-MM-DD HH:MM:SS". --checkpoint= Enable the checkpointing for the job. A signal is sent DELAY seconds before the walltime on the first processus of the job --signal=<#sig> Specify the signal to use when checkpointing Use signal numbers, default is 12 (SIGUSR2) -t, --type= Specify a specific type (deploy, besteffort, cosystem, checkpoint, timesharing) -d, --directory= Specify the directory where to launch the command (default is current directory) --project= Specify a name of a project the job belongs to -n, --name= Specify an arbitrary name for the job -a, --anterior= Anterior job that must be terminated to start this new one --notify= Specify a notification method (mail or command to execute). Ex: --notify "mail:name\@domain.com" --notify "exec:/path/to/script args" --resubmit= Resubmit the given job as a new one -k, --use-job-key Activate the job-key mechanism. -i, --import-job-key-from-file= Import the job-key to use from a files instead of generating a new one. --import-job-key-inline= Import the job-key to use inline instead of generating a new one. -e --export-job-key-to-file= Export the job key to a file. Warning: the file will be overwritten if it already exists. (the %jobid% pattern is automatically replaced) -O --stdout= Specify the file that will store the standart output stream of the job. (the %jobid% pattern is automatically replaced) -E --stderr= Specify the file that will store the standart error stream of the job. (the %jobid% pattern is automatically replaced) --hold Set the job state into Hold instead of Waiting, so that it is not scheduled (you must run "oarresume" to turn it into the Waiting state) -s, --stagein= Set the stagein directory or archive --stagein-md5sum= Set the stagein file md5sum -D, --dumper Print result in DUMPER format -X, --xml Print result in XML format -Y, --yaml Print result in YAML format -J, --json Print result in JSON format -h, --help Print this help message -V, --version Print OAR version number EOS } ## manage the job key if option is activated ## read job key file if import from file ## generate a job key if no import. ## function must exit with $job_key_priv and $job_key_pub set if $use_job_key is set. #sub job_key_management() { # if (defined ($use_job_key) and !($import_job_key_inline ne "") and !($import_job_key_file ne "") and defined($ENV{OAR_JOB_KEY_FILE})){ # $import_job_key_file=$ENV{OAR_JOB_KEY_FILE}; # } # if ((!defined($use_job_key)) and (($import_job_key_inline ne "") or ($import_job_key_file ne "") or ($export_job_key_file ne ""))){ # warn("Error: You must set the --use-job-key (or -k) option in order to use other job key related options.\n"); # exit(15); # } # if (defined($use_job_key)){ # if (($import_job_key_inline ne "") and ($import_job_key_file ne "")){ # warn("Error: You cannot import a job key both inline and from a file at the same time.\n"); # exit(15); # } # my $tmp_job_key_file = OAR::Sub::get_default_oarexec_directory()."/oarsub_$$.jobkey"; # if (($import_job_key_inline ne "") or ($import_job_key_file ne "")){ # # job key is imported # if ($import_job_key_inline ne "") { # # inline import # print ("Import job key inline.\n"); # unless (sysopen(FH,"$tmp_job_key_file",O_CREAT|O_WRONLY,0600)) { # warn("Error: Cannot open tmp file for writing: $tmp_job_key_file\n"); # exit(14); # } # syswrite(FH,$import_job_key_inline); # close(F); # } else { # # file import # print ("Import job key from file: $import_job_key_file\n"); # my $lusr= $ENV{OARDO_USER}; # # read key files: oardodo su - user needed in order to be able to read the file for sure # # safer way to do a `cmd`, see perl cookbook # my $pid; # die "cannot fork: $!" unless defined ($pid = open(SAFE_CHILD, "-|")); # if ($pid == 0) { # $ENV{OARDO_BECOME_USER} = $lusr; # unless (exec({"oardodo"} "oardodo","cat $import_job_key_file")) { # warn ("Error: Cannot cannot read key file:$import_job_key_file\n"); # exit(14); # } # }else{ # unless (sysopen(FH,"$tmp_job_key_file",O_CREAT|O_WRONLY,0600)) { # warn("Error: Cannot open tmp file for writing: $tmp_job_key_file\n"); # exit(14); # } # while () { # syswrite(FH,$_); # } # close(FH); # } # close(SAFE_CHILD); # } # # extract the public key from the private one # system({"bash"} "bash","-c","SSH_ASKPASS=/bin/true ssh-keygen -y -f $tmp_job_key_file < /dev/null 2> /dev/null > $tmp_job_key_file.pub"); # if ($? != 0){ # warn ("Error: Fail to extract the public key. Please verify that the job key to import is valid.\n"); # if (-e $tmp_job_key_file) { # unlink($tmp_job_key_file); # } # if (-e $tmp_job_key_file.".pub") { # unlink($tmp_job_key_file.".pub"); # } # exit(14); # } # } else { # # we must generate the key # print("Generate a job key...\n"); # # ssh-keygen: no passphrase, smallest key (1024 bits), ssh2 rsa faster than dsa. # system({"bash"} "bash","-c",'ssh-keygen -b 1024 -N "" -t rsa -f "'.$tmp_job_key_file.'" > /dev/null'); # if ($? != 0) { # warn ("Error: Job key generation failed ($?).\n"); # exit(14); # } # } # # priv and pub key file must now exist. # unless (open(F, "< $tmp_job_key_file")){ # warn ("Error: fail to read private key.\n"); # exit(14); # } # while ($_ = ){ # $job_key_priv .= $_; # } # close(F); # unless (open(F, "< $tmp_job_key_file.pub")){ # warn ("Error: fail to read private key.\n"); # exit(14); # } # while ($_ = ){ # $job_key_pub .= $_; # } # close(F); # unlink($tmp_job_key_file,$tmp_job_key_file.".pub"); # } # # # last checks # if (defined($use_job_key)){ # if ($job_key_pub eq "") { # warn("Error: missing job public key (private key found).\n"); # exit(15); # } # if ($job_key_priv eq "") { # warn("Error: missing job private key (public key found).\n"); # exit(15); # } # if ($job_key_pub !~ /^(ssh-rsa|ssh-dss)\s.+\n*$/){ # warn("Error: Bad job key format. The public key must begin with either `ssh-rsa' or `ssh-dss' and is only 1 line.\n"); # exit(14); # } # $job_key_pub =~ s/\n//g; # } #} # # Parse -l options and return an array of hashtables with resources for a moldable job sub parse_resource_descriptions($){ my $resource_ref = shift; my @resource= @{$resource_ref}; if ($#resource < 0){ push(@resource,$Default_resources); } #print "--@resource--\n"; my @result; foreach my $r (@resource){ my @resource_groups; my $end_loop = 0; while ($end_loop == 0){ my $initial_resource = $r; my %tmp_result; if ($r =~ /^\s*(\++|\,+|\s*)\s*\{(.+?)}(.*)$/){ # $1 = property string $tmp_result{property} = $2; $r = $3; } my $resources_to_parse; if (($r =~ /^\s*(\++|\,+|\s*)\s*[\/]*([^\,\+]+)\s*(.*)$/) and ($2 !~ /^\s*walltime/)){ $resources_to_parse = $2; $r = $3; }else{ $Default_resources =~ /^\s*[\/]*(.+)$/; # Remove first / $resources_to_parse = $1; } my @slash_split = split('\/', $resources_to_parse); my @resources_list; foreach my $l (@slash_split){ if ($l =~ /^\s*(\w+)\s*=\s*(\d+)\s*$/){ my $tmp = $1; $tmp = $Nodes_resources if ($tmp eq "nodes"); push(@resources_list, { resource => $tmp, value => $2 }); }elsif ($l =~ /^\s*(\w+)\s*=\s*ALL\s*$/){ my $tmp = $1; $tmp = $Nodes_resources if ($tmp eq "nodes"); push(@resources_list, { resource => $tmp, value => -1 }); }elsif ($l =~ /^\s*(\w+)\s*=\s*BEST\s*$/){ my $tmp = $1; $tmp = $Nodes_resources if ($tmp eq "nodes"); push(@resources_list, { resource => $tmp, value => -2 }); }else{ die("/!\\ Cannot recognize the resource description : $l\n"); } } $tmp_result{resources} = \@resources_list; if ($r =~ /^[\s\,]*walltime\s*=\s*([\d|:]+)\s*$/){ #walltime part my ($w_h,$w_mn,$w_sec) = split(':',$1); if (defined($w_h)){ if (not defined($w_mn)){ $resource_groups[1] = "$w_h:00:00"; }elsif (not defined($w_sec)){ $resource_groups[1] = "$w_h:$w_mn:00"; }else{ $resource_groups[1] = "$w_h:$w_mn:$w_sec"; } $resource_groups[1] = OAR::Sub::sql_to_duration("$resource_groups[1]"); }else{ die("/!\\ Cannot recognize walltime resource value\n"); } $r = $2; } if ($r eq $initial_resource){ die("/!\\ Cannot recognize -- $r -- resource\n"); } push(@{$resource_groups[0]}, \%tmp_result); if ($r =~ /^\s*$/){ $end_loop = 1; } } push(@result, \@resource_groups); } # Add resource constraints for JOB_RESOURCE_MANAGER_JOB_UID_TYPE for each resource groups if (defined($Job_uid_resource_type) and defined($Cpuset_field) and !(grep(/^desktop_computing$/, @Type))){ my %tmp = ( property => "type = '$Job_uid_resource_type'", resources => [ {resource => $Cpuset_field, value => 1} ] ); foreach my $moldable (@result){ push(@{$moldable->[0]},\%tmp); } } return(@result); } # # Main # init_conf($ENV{OARCONFFILE}); $remote_host = get_conf("SERVER_HOSTNAME"); $remote_port = get_conf("SERVER_PORT"); $stageindir = get_conf("STAGEIN_DIR"); $Default_resources = get_conf("OARSUB_DEFAULT_RESOURCES"); if (!defined($Default_resources)){ $Default_resources = "/resource_id=1"; } $Nodes_resources = get_conf("OARSUB_NODES_RESOURCES"); if (!defined($Nodes_resources)){ $Nodes_resources = "resource_id"; } $Deploy_hostname = get_conf("DEPLOY_HOSTNAME"); if (!defined($Deploy_hostname)){ $Deploy_hostname = $remote_host; } $Cosystem_hostname = get_conf("COSYSTEM_HOSTNAME"); if (!defined($Cosystem_hostname)){ $Cosystem_hostname = $remote_host; } $Cpuset_field = get_conf("JOB_RESOURCE_MANAGER_PROPERTY_DB_FIELD"); $Cpuset_path = get_conf("CPUSET_PATH"); $Job_uid_resource_type = get_conf("JOB_RESOURCE_MANAGER_JOB_UID_TYPE"); if (is_conf("OAR_RUNTIME_DIRECTORY")){ OAR::Sub::set_default_oarexec_directory(get_conf("OAR_RUNTIME_DIRECTORY")); } my $default_oar_dir = OAR::Sub::get_default_oarexec_directory(); if (!(((-d $default_oar_dir) and (-O $default_oar_dir)) or (mkdir($default_oar_dir)))){ die("ERROR: Cannot create the OAR directory $default_oar_dir or bad permissions.\n"); } my $binpath; if (defined($ENV{OARDIR})){ $binpath = $ENV{OARDIR}."/"; }else{ die("ERROR: OARDIR env variable must be defined.\n"); } my $Openssh_cmd = get_conf("OPENSSH_CMD"); $Openssh_cmd = OAR::Sub::get_default_openssh_cmd() if (!defined($Openssh_cmd)); if (is_conf("OAR_SSH_CONNECTION_TIMEOUT")){ OAR::Sub::set_ssh_timeout(get_conf("OAR_SSH_CONNECTION_TIMEOUT")); } Getopt::Long::Configure ("gnu_getopt"); my $Version; GetOptions ("resource|l=s" => \@resource, "queue|q=s" => \$Queue_name, "interactive|I" => \$Interactive, "property|p=s" => \$Job_sql_properties, "reservation|r=s" => \$Reservation, "connect|C=i" => \$connect_job, "stagein|s=s" => \$stagein, "stagein-md5sum=s" => \$md5sum, "checkpoint=i" => \$Checkpoint, "help|h" => \$sos, "notify=s" => \$notify, "type|t=s" => \@Type, "directory|d=s" => \$Directory, "name|n=s" => \$job_name, "project=s" => \$Project, "hold" => \$job_hold, "array=i" => \$array_job_nb, "array-param-file=s" => \$array_param_file, "anterior|a=i" => \@Anterior_job, "signal=i" => \$Checkpoint_signal, "stdout|O=s" => \$Stdout_file, "stderr|E=s" => \$Stderr_file, "resubmit=i" => \$Resubmit, "scanscript|S" => \$Scan_script, "xml|X" => \$XML_mode, "yaml|Y" => \$YAML_mode, "json|J" => \$JSON_mode, "dumper|D" => \$DUMPER_mode, "use-job-key|k" => \$use_job_key, "import-job-key-inline-priv=s" => \$import_job_key_inline, "import-job-key-from-file|i=s" => \$import_job_key_file, "export-job-key-to-file|e=s" => \$export_job_key_file, "version|V" => \$Version ); if (defined($Version)){ print("OAR version : ".OAR::Sub::get_oar_version()."\n"); exit(0); } if (defined($sos)){ usage(); exit(0); } # Check the default name of the key if we have to generate it if (is_conf("OARSUB_FORCE_JOB_KEY")){ if (lc(get_conf("OARSUB_FORCE_JOB_KEY")) eq "yes"){ $use_job_key = 1; } } OAR::Sub::open_db_connection(); if (defined($Resubmit)){ print("Resubmitting job $Resubmit ..."); my $err = OAR::Sub::resubmit_job($Resubmit); OAR::Sub::close_db_connection(); if ($err > 0){ $Job_id = $err; print("DONE\n"); print("OAR_JOB_ID=$Job_id\n"); if (defined(OAR::Sub::signal_almighty($remote_host,$remote_port,"Qsub"))){ warn("Cannot connect to executor $remote_host:$remote_port. Is OAR started?\n"); exit(3); } exit(0); }else{ print("ERROR\n"); if ($err == -1){ warn("/!\\ An interactive job or a reservation cannot be resubmitted\n"); }elsif ($err == -2){ warn("/!\\ The job must be in Error or Terminated state\n"); }elsif ($err == -3){ warn("/!\\ You are not the right user\n"); }elsif ($err == -4){ warn("/!\\ Another active job is using the same ssh keys\n"); }else{ warn("/!\\ Unknown error\n"); } exit(4); } } if ((@ARGV != 1) && ($Interactive == 0) && ($Reservation eq "0") && (!defined($connect_job))){ usage(); exit(5); } if (($Interactive == 1) and ($Reservation ne "0")){ warn("/!\\ A reservation cannot be interactive.\n"); usage(); exit(7); } if (($Interactive == 1) and (grep(/^desktop_computing$/, @Type))){ warn("/!\\ A desktop computing job cannot be interactive.\n"); usage(); exit(17); } if (defined($notify) && $notify =~ m/\s*exec:(.+)$/m){ my $notify_exec_regexp = '[a-zA-Z0-9_.\/ -]+'; unless ($notify =~ m/\s*exec:($notify_exec_regexp)$/m){ warn("/!\\ Insecure characters found in notification method (the allowed regexp is: $notify_exec_regexp).\n"); exit(16); } } # Connect to a reservation if (defined($connect_job)){ # Do not kill the job if the user close the window $SIG{HUP} = 'DEFAULT'; exit(connect_job($connect_job,0,$Openssh_cmd)); } # End connection to a reservation if (($Interactive == 0) and ($ARGV[0] ne "")) { my $exec = $ARGV[0]; if (defined($Scan_script)){ my $scan_result_tmp = OAR::Sub::scan_script($exec, $Initial_request_string) if ($exec ne ""); my %scan_result = %$scan_result_tmp; $Initial_request_string = $scan_result{initial_request}; if (defined($scan_result{queue}) && !defined($Queue_name)){ $Queue_name = $scan_result{queue} }elsif(defined($scan_result{queue}) && defined($Queue_name)){ warn("/!\\ Ignore script value for queue parameter : $scan_result{queue}; another value was given on the command line.\n"); } if (defined($scan_result{property}) && ($Job_sql_properties eq "")){ $Job_sql_properties = $scan_result{property}; }elsif(defined($scan_result{property}) && ($Job_sql_properties ne "")){ warn("/!\\ Ignore script value for property parameter : $scan_result{property}; another value was given on the command line.\n"); } if (defined($scan_result{resources})){ push(@resource, @{$scan_result{resources}}); } if (defined($scan_result{types})){ push(@Type, @{$scan_result{types}}); } if (defined($scan_result{anterior})){ push(@Anterior_job, @{$scan_result{anterior}}); } if (defined($scan_result{checkpoint}) && ($Checkpoint == 0)){ $Checkpoint = $scan_result{checkpoint}; }elsif(defined($scan_result{checkpoint}) && ($Checkpoint != 0)){ warn("/!\\ Ignore script value for checkpoint parameter : $scan_result{checkpoint}; another value was given on the command line.\n"); } if (defined($scan_result{notify}) && (!defined($notify))){ $notify = $scan_result{notify}; }elsif(defined($scan_result{notify}) && (defined($notify))){ warn("/!\\ Ignore script value for notify parameter : $scan_result{notify}; another value was given on the command line.\n"); } if (defined($scan_result{directory})){ $Directory = $scan_result{directory}; } if (defined($scan_result{directory}) && (!defined($Directory))){ $Directory = $scan_result{directory}; }elsif(defined($scan_result{directory}) && (defined($Directory))){ warn("/!\\ Ignore script value for name parameter : $scan_result{directory}; another value was given on the command line.\n"); } if (defined($scan_result{name}) && (!defined($job_name))){ $job_name = $scan_result{name}; }elsif(defined($scan_result{name}) && (defined($job_name))){ warn("/!\\ Ignore script value for name parameter : $scan_result{name}; another value was given on the command line.\n"); } if (defined($scan_result{project}) && (!defined($Project))){ $Project = $scan_result{project}; }elsif(defined($scan_result{project}) && (defined($Project))){ warn("/!\\ Ignore script value for name parameter : $scan_result{project}; another value was given on the command line.\n"); } if (defined($scan_result{hold}) && (!defined($job_hold))){ $job_hold = $scan_result{hold}; }elsif(defined($scan_result{hold}) && (defined($job_hold))){ warn("/!\\ Ignore script value for hold parameter : $scan_result{hold}; another value was given on the command line.\n"); } if (defined($scan_result{signal}) && (!defined($Checkpoint_signal))){ $Checkpoint_signal = $scan_result{signal}; }elsif(defined($scan_result{signal}) && (defined($Checkpoint_signal))){ warn("/!\\ Ignore script value for hold parameter : $scan_result{signal}; another value was given on the command line.\n"); } if (defined($scan_result{stdout}) && (!defined($Stdout_file))){ $Stdout_file = $scan_result{stdout}; }elsif(defined($scan_result{stdout}) && (defined($Stdout_file))){ warn("/!\\ Ignore script value for stdout parameter : $scan_result{stdout}; another value was given on the command line.\n"); } if (defined($scan_result{stderr}) && (!defined($Stderr_file))){ $Stderr_file = $scan_result{stderr}; }elsif(defined($scan_result{stderr}) && (defined($Stderr_file))){ warn("/!\\ Ignore script value for stderr parameter : $scan_result{stderr}; another value was given on the command line.\n"); } if (defined($scan_result{usejobkey}) && (!defined($use_job_key))){ $use_job_key = $scan_result{usejobkey}; }elsif(defined($scan_result{usejobkey}) && (defined($use_job_key))){ warn("/!\\ Ignore script value for use-job-key parameter : $scan_result{usejobkey}; another value was given on the command line.\n"); } if (defined($scan_result{importjobkeyinlinepriv}) && ($import_job_key_inline eq "")){ $import_job_key_inline = $scan_result{importjobkeyinlinepriv}; }elsif(defined($scan_result{importjobkeyinlinepriv}) && ($import_job_key_inline ne "")){ warn("/!\\ Ignore script value for import-job-key-inline-priv parameter : $scan_result{importjobkeyinlinepriv}; another value was given on the command line.\n"); } if (defined($scan_result{importjobkeyfromfile}) && ($import_job_key_file eq "")){ $import_job_key_file = $scan_result{importjobkeyfromfile}; }elsif(defined($scan_result{importjobkeyfromfile}) && ($import_job_key_file ne "")){ warn("/!\\ Ignore script value for import-job-key-from-file parameter : $scan_result{importjobkeyfromfile}; another value was given on the command line.\n"); } if (defined($scan_result{exportjobkeytofile}) && ($export_job_key_file eq "")){ $export_job_key_file = $scan_result{exportjobkeytofile}; }elsif(defined($scan_result{exportjobkeytofile}) && ($export_job_key_file ne "")){ warn("/!\\ Ignore script value for export-job-key-to-file parameter : $scan_result{exportjobkeytofile}; another value was given on the command line.\n"); } if (defined($scan_result{stagein}) && (!defined($stagein))){ $stagein = $scan_result{stagein}; }elsif (defined($scan_result{stagein}) && (defined($stagein))){ warn("/!\\ Ignore script value for stagein parameter : $scan_result{stagein}; another value was given on the command line.\n"); } if (defined($scan_result{stageinmd5sum}) && (!defined($md5sum))){ $md5sum = $scan_result{stageinmd5sum}; }elsif(defined($scan_result{stageinmd5sum}) && (defined($md5sum))){ warn("/!\\ Ignore script value for stagein-md5sum parameter : $scan_result{stageinmd5sum}; another value was given on the command line.\n"); } if (defined($scan_result{array}) && (!defined($array_job_nb))){ $array_job_nb = $scan_result{array}; }elsif(defined($scan_result{array}) && (defined($array_job_nb))){ warn("/!\\ Ignore script value for array parameter : $scan_result{array}; another value was given on the command line.\n"); } if (defined($scan_result{arrayparamfile}) && (!defined($array_param_file))){ $array_param_file = $scan_result{arrayparamfile}; }elsif(defined($scan_result{arrayparamfile}) && (defined($array_param_file))){ warn("/!\\ Ignore script value for array-param-file parameter : $scan_result{arrayparamfile}; another value was given on the command line.\n"); } } my @resource_list = parse_resource_descriptions(\@resource); $Project = $Project_default if (!defined($Project)); $Checkpoint_signal = $Checkpoint_signal_default if (!defined($Checkpoint_signal)); $Directory = $Directory_default if (!defined($Directory)); # array_ids are assigned automatically if(defined($array_param_file)){ $array_params_ref = OAR::Sub::read_array_param_file($array_param_file); $array_job_nb = scalar @{$array_params_ref}; } $array_job_nb = 1 if (!defined($array_job_nb)); if($array_job_nb <= 0){ warn("/!\\ An array job must have a positive number of sub-jobs\n"); usage(); exit(6); } # job_key_management(); #if (!($exec =~ m/^\/.+$/m)){ # WARNING: we are not the real user, we are oar user!!! # so $exec properties are not correct # if ( -e "$exec" ){ # $exec = $Directory."/".$exec ; # } #} $Cmd_executor = "Qsub"; my $server_port; if ($Reservation ne "0"){ #Test if this job is a reservation and the syntax is right if ($Reservation =~ m/^\s*(\d{4}\-\d{1,2}\-\d{1,2})\s+(\d{1,2}:\d{1,2}:\d{1,2})\s*$/m){ $Reservation = OAR::Sub::sql_to_local("$1 $2"); }else{ warn("Syntax error near -r or --reservation option. Reservation date exemple : \"2007-03-25 17:32:12\"\n"); OAR::Sub::close_db_connection; exit(7); } ($Server, $server_port) = OAR::Sub::init_tcp_server(); } # TODO: put in lib # stagein machinery if ((defined $stagein) and (grep(/^desktop_computing$/, @Type))) { #my $tempfile_to_delete; print "Setting up stagein...\n"; if (-d $stagein) { print "Archiving the content of the directory \"$stagein\" for the job stagein...\n"; my (undef, $filename) = tempfile (SUFFIX=>".oar-stagein.tgz",OPEN => 0); system "tar cfz $filename $stagein" and die "Failed to archive the directory: $?\n"; print "Stagein archive = $filename\n"; print "(You may save this file if you plan to submit other jobs later with the same stagein)\n"; $stagein = $filename; #$tempfile_to_delete = $filename; $md5sum = undef; } ( -r $stagein ) or die "Stagein file not found: $stagein\n"; unless (defined $md5sum) { print "Computing stagein md5sum...\n"; ($md5sum) = split(" ",`md5sum $stagein`); print "md5sum = $md5sum\n"; print "(You may use the stagein-md5sum option with this md5sum for other job submitions with the same stagein)\n"; } OAR::Sub::get_lock($md5sum,3600) or die "Failed to lock stagein\n"; $idFile = OAR::Sub::get_stagein_id($md5sum); if (defined $idFile) { print "This stagein is already stored on the server.\n"; } else { my $location = "$stageindir/$md5sum"; my $method = "FILE"; my $compression = "tar.gz"; print "Uploading stagein...\n"; system "scp $stagein $location" and die "Stagein upload failed\n"; my @stats = stat $stagein; my %params = ( "md5sum" => $md5sum, "location" => $location, "method" => $method, "compression" => $compression, "size" => $stats[7] ); $idFile=OAR::Sub::set_stagein(\%params); defined $idFile or die "Failed to setup stagein\n"; } OAR::Sub::release_lock($md5sum) or die "Failed to unlock stagein\n"; #unlink($tempfile_to_delete) if (defined($tempfile_to_delete)); print "Stagein completed.\n"; } $Job_id_list_ref = OAR::Sub::add_micheline_job("PASSIVE", \@resource_list, $exec, "$Host:$server_port", $Queue_name, $Job_sql_properties, $Reservation, defined ($idFile)?$idFile:"NULL", $Checkpoint, $Checkpoint_signal, $notify, $job_name,$job_env,\@Type, $Directory,\@Anterior_job,$Stdout_file,$Stderr_file,$job_hold,$Project,$use_job_key,$import_job_key_inline,$import_job_key_file,$export_job_key_file,$Initial_request_string,$array_job_nb,$array_params_ref); }else{ if ($ARGV[0] ne ""){ warn("/!\\ You asked for an Interactive job SO I will ignore arguments: $ARGV[0] ; Is your syntax right?\n"); } $Cmd_executor = "Qsub -I"; my @resource_list = parse_resource_descriptions(\@resource); if (defined($array_param_file)){ warn("/!\\ A parameter file-based array job cannot be interactive.\n"); # user defined array_ids, 0 if interactive and/or non-array job usage(); exit(9); } $Project = $Project_default if (!defined($Project)); $Checkpoint_signal = $Checkpoint_signal_default if (!defined($Checkpoint_signal)); $Directory = $Directory_default if (!defined($Directory)); $array_job_nb = 1 if (!defined($array_job_nb)); if ($array_job_nb != 1){ warn("/!\\ An array job cannot be interactive.\n"); usage(); exit(8); } # job_key_management(); if ($Reservation ne "0"){ #Test if this job is a reservation and the syntax is right if ($Reservation =~ m/^\s*(\d{4}\-\d{1,2}\-\d{1,2})\s+(\d{1,2}:\d{1,2}:\d{1,2})\s*$/m){ $Reservation = OAR::Sub::sql_to_local("$1 $2"); }else{ warn("Syntax error near -r or --reservation option. Reservation date exemple : \"2007-03-25 17:32:12\"\n"); OAR::Sub::close_db_connection(); exit(7); } } my $server_port; ($Server, $server_port) = OAR::Sub::init_tcp_server(); $Job_id_list_ref = OAR::Sub::add_micheline_job("INTERACTIVE", \@resource_list, "", "$Host:$server_port", $Queue_name, $Job_sql_properties, $Reservation, defined ($idFile)?$idFile:"NULL", $Checkpoint, $Checkpoint_signal, $notify, $job_name,$job_env,\@Type, $Directory,\@Anterior_job,$Stdout_file,$Stderr_file,$job_hold,$Project,$use_job_key,$import_job_key_inline,$import_job_key_file,$export_job_key_file,$Initial_request_string,$array_job_nb,$array_params_ref); } if ((!defined($Job_id_list_ref)) or (ref($Job_id_list_ref) ne "ARRAY")){ OAR::Sub::close_db_connection(); exit($Job_id_list_ref); } if(@{$Job_id_list_ref} > 1){ for my $j (@{$Job_id_list_ref}){ print("OAR_JOB_ID=".$j."\n"); } print ("OAR_ARRAY_ID=".OAR::Sub::get_job_array_id($Job_id_list_ref->[0])."\n"); }else{ print("OAR_JOB_ID=".$Job_id_list_ref->[0]."\n"); } if ((defined($DUMPER_mode)) or (defined($YAML_mode) or ($XML_mode) or ($JSON_mode))){ print("\n##########\n"); foreach my $Job_id (@{$Job_id_list_ref}){ my $tmp = { job_id => $Job_id }; if (defined($DUMPER_mode)){ print(Dumper($tmp)); }elsif(defined($XML_mode)){ if ($XML_enabled == 1){ my $dump = new XML::Dumper; $dump->dtd; print($dump->pl2xml($tmp)); }else{ warn("XML module not available on the system. Ask your administrator to install it if needed.\n"); } }elsif(defined($YAML_mode)){ if ($YAML_enabled == 1){ print(YAML::Dump($tmp)); }else{ warn("YAML module not available on the system. Ask your administrator to install it if needed.\n"); } }elsif(defined($JSON_mode)){ if ($JSON_enabled == 1){ print(JSON->new->pretty(1)->encode($tmp)); }else{ warn("JSON module not available on the system. Ask your administrator to install it if needed.\n"); } } } print("\n##########\n\n"); } OAR::Sub::close_db_connection(); if ((@{$Job_id_list_ref} < 1) or ($Job_id_list_ref->[-1] <= 0)){ warn("Oarsub failed: please verify your request syntax or ask for support to your admin.\n"); exit(8); } #Signal Almigthy if (defined(OAR::Sub::signal_almighty($remote_host,$remote_port,"$Cmd_executor"))){ #qdel(1); warn("Cannot connect to executor $remote_host:$remote_port so I kill this job. Is OAR started?\n"); exit(9); } my $answer; if ($Reservation ne "0"){ #Reservation mode print("Reservation mode : waiting validation...\n"); my $client = $Server->accept(); $answer = <$client>; chop($answer); if ($answer eq "GOOD RESERVATION"){ print("Reservation valid --> OK\n"); }else{ print("Reservation not valid --> KO ($answer)\n"); exit(10); } }elsif ($Interactive==1) { #Interactive mode print("Interactive mode : waiting...\n"); my $prev_str = ""; do{ my $client = $Server->accept(); $answer = <$client>; chop($answer); if ($answer =~ /\](.*)$/){ if ($1 ne $prev_str){ print("$answer\n"); $prev_str = $1; } }elsif ($answer ne "GOOD JOB"){ print("$answer\n"); } }while (($answer ne "GOOD JOB") and ($answer ne "BAD JOB") and ($answer ne "JOB KILLED") and ($answer !~ /^ERROR/)); print("\n"); if ($answer eq "GOOD JOB"){ exit(connect_job($Job_id_list_ref->[0],1,$Openssh_cmd)); }else{ exit(11); } } exit(0); ./oar-2.5.2/sources/core/qfunctions/oarstat.v2_30000755000175000017500000007605411757171206017554 0ustar plbplb#!/usr/bin/perl -w # $Id: oarstat 2136 2009-04-09 17:37:59Z capitn $ # print active job properties use strict; use warnings; use Data::Dumper; use OAR::IO; use Getopt::Long; use OAR::Version; use OAR::Conf qw(init_conf dump_conf get_conf is_conf); my $Old_umask = sprintf("%lo",umask()); umask(oct("022")); # Read config init_conf($ENV{OARCONFFILE}); my $Cpuset_field = get_conf("JOB_RESOURCE_MANAGER_PROPERTY_DB_FIELD"); my $Job_uid_resource_type = get_conf("JOB_RESOURCE_MANAGER_JOB_UID_TYPE"); #Try to load XML module my $XML_enabled = 1; unless (eval "use XML::Dumper qw(pl2xml);1"){ $XML_enabled = 0; } #Try to load YAML module my $YAML_enabled = 1; unless (eval "use YAML;1"){ $YAML_enabled = 0; } # suitable Data::Dumper configuration for serialization $Data::Dumper::Purity = 1; $Data::Dumper::Terse = 1; $Data::Dumper::Indent = 0; $Data::Dumper::Deepcopy = 1; my $Printed_jobs = 0; my @job_ids; my $array_id; my $full_view; my $sql_property; my $XML_mode; my $YAML_mode; my $DUMPER_mode; my $Old; my $gantt_query; my $accounting_query; my $events_query; my $properties_query; my @jobs; my $user; my $base; warn("WARNING: This command will be deprecated in a future release!\n"); Getopt::Long::Configure ("gnu_getopt"); GetOptions ("help|h" => \&show_usage, "version|V" => \&show_version, "job|j=i" => \@job_ids, "full|f" => \$full_view, "user|u:s" => \$user, "array:i" => \$array_id, "gantt|g=s" => \$gantt_query, "events|e" => \$events_query, "properties|p" => \$properties_query, "accounting=s" => \$accounting_query, "sql=s" => \$sql_property, "xml|X" => \$XML_mode, "yaml|Y" => \$YAML_mode, "dumper|D" => \$DUMPER_mode, "backward-compatible|backward_compatible" => \$Old ); if (defined($XML_mode) and $XML_enabled != 1){ warn("XML module not available on the system. Ask your administrator to install it if needed.\n"); exit(1) } if (defined($YAML_mode) and $YAML_enabled != 1){ warn("Yaml module not available on the system. Ask your administrator to install it if needed.\n"); exit(1); } $user = $ENV{OARDO_USER} if (defined($user) and ($user eq '')); if (defined($array_id) && $array_id !=0 && (scalar @job_ids > 0)){ warn("/!\\ ERROR : Conflicting Job IDs and Array IDs (--array and -j cannot be used together)\n"); exit(1); } $base = OAR::IO::connect_ro(); if (defined ($gantt_query)) { &show_gantt; } elsif (defined ($accounting_query)) { &show_accounting; } elsif (defined ($events_query)) { &show_events; } elsif (defined ($properties_query)) { &show_properties; } else { &show_job; } OAR::IO::disconnect($base); exit(0); ### sub routines ### sub show_usage { print <{$user}->{ASKED})) { $asked=$Consumptions->{$user}->{ASKED}; } else { $asked=0; } if (defined($Consumptions->{$user}->{USED})) { $used=$Consumptions->{$user}->{USED}; } else { $used=0; } if (!defined($Consumptions->{$user}->{begin})) {$Consumptions->{$user}->{begin}="No window found";} if (!defined($Consumptions->{$user}->{end})) {$Consumptions->{$user}->{end}="No window found";} print "Usage summary for user '$user' from $1 to $2:\n"; print "-------------------------------------------------------------\n"; printf ("%-28s %s\n","Start of the first window:",OAR::IO::local_to_sql($Consumptions->{$user}->{begin})); printf ("%-28s %s\n","End of the last window:",OAR::IO::local_to_sql($Consumptions->{$user}->{end})); printf ("%-28s %s ( %s)\n","Asked consumption:",$asked,duration($asked)); printf ("%-28s %s ( %s)\n","Used consumption:",$used,duration($used)); print "By project consumption:\n"; $Consumptions=OAR::IO::get_accounting_summary_byproject($base,OAR::IO::sql_to_local($date1),OAR::IO::sql_to_local($date2),$user); foreach my $project (keys %{$Consumptions}) { print " $project:\n"; $asked=$Consumptions->{$project}->{ASKED}->{$user}; $used=$Consumptions->{$project}->{USED}->{$user}; printf ("%-28s %s ( %s)\n"," Asked :",$asked,duration($asked)); printf ("%-28s %s ( %s)\n"," Used :",$used,duration($used)); if (my @last_karma=OAR::IO::get_last_project_karma($base,$user,$project,OAR::IO::sql_to_local($date2))) { printf ("%-28s %s\n"," Last Karma :",$last_karma[0]); } } # All users array output }else{ print <{$login}->{ASKED})) {$Consumptions->{$login}->{ASKED}=0;} if (!defined($Consumptions->{$login}->{USED})) {$Consumptions->{$login}->{USED}=0;} printf("%-10.10s %-19s %-19s %16s %16s\n", $login, OAR::IO::local_to_sql($Consumptions->{$login}->{begin}), OAR::IO::local_to_sql($Consumptions->{$login}->{end}), $Consumptions->{$login}->{ASKED}, $Consumptions->{$login}->{USED} ); } } }else{ print("Bad syntax for --accounting\n"); OAR::IO::disconnect($base); exit(1); } } sub show_gantt { if ($gantt_query =~ m/\s*(\d{4}\-\d{1,2}\-\d{1,2})\s+(\d{1,2}:\d{1,2}:\d{1,2})\s*,\s*(\d{4}\-\d{1,2}\-\d{1,2})\s+(\d{1,2}:\d{1,2}:\d{1,2})\s*/m){ my $hist = get_history($base, "$1 $2", "$3 $4"); if (defined($DUMPER_mode)){ print(Dumper($hist)); }elsif(defined($XML_mode)){ my $dump = new XML::Dumper; $dump->dtd; print($dump->pl2xml($hist)); }elsif(defined($YAML_mode)){ print(YAML::Dump($hist)); }else{ $Data::Dumper::Purity = 1; $Data::Dumper::Terse = 1; $Data::Dumper::Indent = 1; $Data::Dumper::Deepcopy = 0; print(Dumper($hist)); } }else{ warn("Bad syntax for --gantt\n"); OAR::IO::disconnect($base); exit(1); } } sub show_properties { if(defined($array_id) && $array_id !=0){ push(@job_ids, OAR::IO::get_array_job_ids($base, $array_id)); } if($#job_ids >= 0){ my @resources; foreach my $j (@job_ids){ push (@resources, OAR::IO::get_job_resources_properties($base, $j)); } foreach my $r (@resources){ my $line = ""; foreach my $p (keys(%{$r})){ if(OAR::Tools::check_resource_system_property($p) != 1){ $r->{$p} = "" if (!defined($r->{$p})); $line .= " $p = '$r->{$p}' ," } } chop($line); print("$line\n") or exit(5); } } else { warn("No job specified\n"); OAR::IO::disconnect($base); exit(1); } } sub show_events { if(defined($array_id) && $array_id !=0){ push(@job_ids, OAR::IO::get_array_job_ids($base, $array_id)); } if($#job_ids >= 0){ foreach my $j (@job_ids){ my @events = OAR::IO::get_job_events($base, $j); foreach my $e (@events){ print_event_logs($e); } } } else { warn("No job specified\n"); OAR::IO::disconnect($base); exit(1); } } sub print_event_logs($){ my $event_hashref = shift; printf("%s| %s| %s: %s\n", OAR::IO::local_to_sql($event_hashref->{'date'}), $event_hashref->{'job_id'}, $event_hashref->{'type'}, $event_hashref->{'description'} ); } sub show_job { if ($#job_ids < 0){ if (defined($sql_property)){ push(@jobs, OAR::IO::get_jobs_with_given_properties($base,$sql_property)); }elsif(defined($array_id) && $array_id !=0){ push(@jobs, OAR::IO::get_array_subjobs($base, $array_id)); }else{ push(@jobs, OAR::IO::get_jobs_in_state_for_user($base, "Finishing", $user)); push(@jobs, OAR::IO::get_jobs_in_state_for_user($base, "Running", $user)); push(@jobs, OAR::IO::get_jobs_in_state_for_user($base, "Resuming", $user)); push(@jobs, OAR::IO::get_jobs_in_state_for_user($base, "Suspended", $user)); push(@jobs, OAR::IO::get_jobs_in_state_for_user($base, "Launching", $user)); push(@jobs, OAR::IO::get_jobs_in_state_for_user($base, "toLaunch", $user)); push(@jobs, OAR::IO::get_jobs_in_state_for_user($base, "Waiting", $user)); push(@jobs, OAR::IO::get_jobs_in_state_for_user($base, "toAckReservation", $user)); push(@jobs, OAR::IO::get_jobs_in_state_for_user($base, "Hold", $user)); } }elsif($#job_ids >= 0){ foreach my $j (@job_ids){ my $tmp = OAR::IO::get_job($base,$j); if (defined($tmp)){ push(@jobs, $tmp); } } } my %data_to_print; foreach my $g (@jobs) { $data_to_print{$g->{job_id}} = get_job_data($base,$g); } print_job_data(\%data_to_print,\@jobs); } sub get_job_data($$){ my $dbh = shift; my $job_info = shift; my @nodes; my @node_hostnames; my $mold; my @date_tmp; my @job_events; my %data_to_display; my $job_user; my $job_cpuset_uid; my @job_dependencies; my @job_types = OAR::IO::get_job_types($dbh,$job_info->{job_id}); my $cpuset_name; $cpuset_name = OAR::IO::get_job_cpuset_name($dbh, $job_info->{job_id}) if (defined($Cpuset_field)); my $resources_string = ""; my $reserved_resources; if ($job_info->{assigned_moldable_job} ne ""){ @nodes = OAR::IO::get_job_resources($dbh,$job_info->{assigned_moldable_job}); @node_hostnames = OAR::IO::get_job_network_address($dbh,$job_info->{assigned_moldable_job}); $mold = OAR::IO::get_moldable_job($dbh,$job_info->{assigned_moldable_job}); } if ($job_info->{reservation} eq "Scheduled" and $job_info->{state} eq "Waiting") { $reserved_resources = OAR::IO::get_gantt_visu_scheduled_job_resources($dbh,$job_info->{job_id}); } if (defined($full_view) or defined($Old)){ @date_tmp = OAR::IO::get_gantt_job_start_time_visu($dbh,$job_info->{job_id}); @job_events = OAR::IO::get_job_events($dbh,$job_info->{job_id}); @job_dependencies = OAR::IO::get_current_job_dependencies($dbh,$job_info->{job_id}); $job_cpuset_uid = OAR::IO::get_job_cpuset_uid($dbh, $job_info->{assigned_moldable_job}, $Job_uid_resource_type, $Cpuset_field) if ((defined($Job_uid_resource_type)) and (defined($Cpuset_field))); $job_user = OAR::Tools::format_job_user($job_info->{job_user},$job_info->{job_id},$job_cpuset_uid); #Get the job resource description to print -l option my $job_descriptions = OAR::IO::get_resources_data_structure_current_job($dbh,$job_info->{job_id}); foreach my $moldable (@{$job_descriptions}){ my $tmp_str = ""; foreach my $group (@{$moldable->[0]}){ if ($tmp_str ne ""){ # add a new group $tmp_str .= "+"; }else{ # first group $tmp_str .= "-l \""; } if ((defined($group->{property})) and ($group->{property} ne "")){ $tmp_str .= "{$group->{property}}"; } foreach my $resource (@{$group->{resources}}){ my $tmp_val = $resource->{value}; if ($tmp_val == -1){ $tmp_val = "ALL"; }elsif ($tmp_val == -2){ $tmp_val = "BEST"; } $tmp_str .= "/$resource->{resource}=$tmp_val"; } } $tmp_str .= ",walltime=".OAR::IO::duration_to_sql($moldable->[1])."\" "; $resources_string .= $tmp_str; } %data_to_display = ( Job_Id => $job_info->{job_id}, array_id => $job_info->{array_id}, array_index => $job_info->{array_index}, name => $job_info->{job_name}, owner => $job_info->{job_user}, job_user => $job_user, job_uid => $job_cpuset_uid, state => $job_info->{state}, assigned_resources => \@nodes, assigned_network_address => \@node_hostnames, queue => $job_info->{queue_name}, command => $job_info->{command}, launchingDirectory => $job_info->{launching_directory}, jobType => $job_info->{job_type}, properties => $job_info->{properties}, reservation => $job_info->{reservation}, walltime => $mold->{moldable_walltime}, submissionTime => $job_info->{submission_time}, startTime => $job_info->{start_time}, message => $job_info->{message}, scheduledStart => $date_tmp[0], resubmit_job_id => $job_info->{resubmit_job_id}, events => \@job_events, wanted_resources => $resources_string, project => $job_info->{project}, cpuset_name => $cpuset_name, types => \@job_types, dependencies => \@job_dependencies, exit_code => $job_info->{exit_code}, initial_request => "" ); if (($ENV{OARDO_USER} eq $job_info->{job_user}) or ($ENV{OARDO_USER} eq "oar") or ($ENV{OARDO_USER} eq "root")){ $data_to_display{initial_request} = $job_info->{initial_request}; } }else{ %data_to_display = ( Job_Id => $job_info->{job_id}, array_id => $job_info->{array_id}, array_index => $job_info->{array_index}, name => $job_info->{job_name}, owner => $job_info->{job_user}, state => $job_info->{state}, assigned_resources => \@nodes, assigned_network_address => \@node_hostnames, queue => $job_info->{queue_name}, command => $job_info->{command}, launchingDirectory => $job_info->{launching_directory}, jobType => $job_info->{job_type}, properties => $job_info->{properties}, reservation => $job_info->{reservation}, submissionTime => $job_info->{submission_time}, startTime => $job_info->{start_time}, message => $job_info->{message}, resubmit_job_id => $job_info->{resubmit_job_id}, project => $job_info->{project}, cpuset_name => $cpuset_name, types => \@job_types, dependencies => \@job_dependencies ); } if (defined($reserved_resources)) { $data_to_display{'reserved_resources'}=$reserved_resources; } return(\%data_to_display); } sub print_job_data($$){ my $data = shift; my $job_array = shift; if (defined($DUMPER_mode)){ print(Dumper($data)); }elsif(defined($XML_mode)){ my $dump = new XML::Dumper; $dump->dtd; print($dump->pl2xml($data)); }elsif(defined($YAML_mode)){ print(YAML::Dump($data)); }else{ my %hashestat = ( 'Waiting' => 'W', 'toLaunch' => 'L', 'Launching' => 'L', 'Hold' => 'H', 'Running' => 'R', 'Terminated' => 'T', 'Error' => 'E', 'toError' => 'E', 'Finishing' => 'F', 'Suspended' => 'S', 'Resuming' => 'S', 'toAckReservation' => 'W' ); foreach my $job_info (@{$job_array}){ if (defined($Old)){ print("Job Id: $job_info->{job_id}.oar\n"); $job_info->{job_name} = '' if (!defined($job_info->{job_name})); print(" job_array_id = $job_info->{array_id}\n"); print(" job_array_index = $job_info->{array_index},\n"); print(" Job_Name = $job_info->{job_name}\n"); print(" Job_Owner = $job_info->{job_user}\n"); print(" job_state = $hashestat{$job_info->{state}}\n"); print(" comment = $job_info->{message}\n"); print(" wanted_resources = $data->{$job_info->{job_id}}->{wanted_resources}\n"); print(" queue = $job_info->{queue_name}\n"); print(" types = ".join(", ",@{$data->{$job_info->{job_id}}->{types}})."\n"); $job_info->{command} = '' if (!defined($job_info->{command})); print(" command = $job_info->{command}\n"); print(" launchingDirectory = $job_info->{launching_directory}\n"); print(" jobType = $job_info->{job_type}\n"); print(" properties = $job_info->{properties}\n"); print(" reservation = $job_info->{reservation}\n"); if (!defined($data->{$job_info->{job_id}}->{walltime})){ $data->{$job_info->{job_id}}->{walltime} = ''; }else{ $data->{$job_info->{job_id}}->{walltime} = OAR::IO::duration_to_sql($data->{$job_info->{job_id}}->{walltime}); } print(" walltime = $data->{$job_info->{job_id}}->{walltime}\n"); print(" submissionTime = ".OAR::IO::local_to_sql($job_info->{submission_time})."\n"); print(" startTime = ".OAR::IO::local_to_sql($job_info->{start_time})."\n") if ($job_info->{start_time} > 0); print(" stopTime = ".OAR::IO::local_to_sql($job_info->{stop_time})."\n") if ($job_info->{stop_time} > 0); if (!defined($data->{$job_info->{job_id}}->{scheduledStart})){ $data->{$job_info->{job_id}}->{scheduledStart} = "no prediction"; }else{ $data->{$job_info->{job_id}}->{scheduledStart} = OAR::IO::local_to_sql($data->{$job_info->{job_id}}->{scheduledStart}); } print(" scheduledStart = $data->{$job_info->{job_id}}->{scheduledStart}\n"); print(" assigned_resources = ".join("+",sort({ $a <=> $b } @{$data->{$job_info->{job_id}}->{assigned_resources}}))."\n"); print(" assigned_hostnames = ".join("+",@{$data->{$job_info->{job_id}}->{assigned_network_address}})."\n"); print(" nbNodes = ". ($#{$data->{$job_info->{job_id}}->{assigned_network_address}} +1) ."\n"); print(" weight = ". ($#{$data->{$job_info->{job_id}}->{assigned_resources}} + 1) ."\n"); print(" dependencies = ".join(" ",@{$data->{$job_info->{job_id}}->{dependencies}})."\n"); print("\n"); }elsif (defined($full_view)){ print("Job_Id: $job_info->{job_id}\n"); $job_info->{job_name} = '' if (!defined($job_info->{job_name})); print(" job_array_id = $job_info->{array_id}\n"); print(" job_array_index = $job_info->{array_index}\n"); print(" name = $job_info->{job_name}\n"); print(" project = $job_info->{project}\n"); print(" owner = $job_info->{job_user}\n"); print(" state = $job_info->{state}\n"); print(" wanted_resources = $data->{$job_info->{job_id}}->{wanted_resources}\n"); print(" types = ".join(", ",@{$data->{$job_info->{job_id}}->{types}})."\n"); print(" dependencies = ".join(" ",@{$data->{$job_info->{job_id}}->{dependencies}})."\n"); print(" assigned_resources = ".join("+",@{$data->{$job_info->{job_id}}->{assigned_resources}})."\n"); print(" assigned_hostnames = ".join("+",@{$data->{$job_info->{job_id}}->{assigned_network_address}})."\n"); print(" queue = $job_info->{queue_name}\n"); $job_info->{command} = '' if (!defined($job_info->{command})); print(" command = $job_info->{command}\n"); if (defined($job_info->{exit_code})){ my $exit_code = $job_info->{exit_code} >> 8; my $exit_num = $job_info->{exit_code} & 127; my $exit_core = $job_info->{exit_code} & 128; print(" exit_code = $job_info->{exit_code} ($exit_code,$exit_num,$exit_core)\n"); } print(" launchingDirectory = $job_info->{launching_directory}\n"); print(" jobType = $job_info->{job_type}\n"); print(" properties = $job_info->{properties}\n"); print(" reservation = $job_info->{reservation}\n"); if (defined $data->{$job_info->{job_id}}->{'reserved_resources'}) { print(" reserved_resources = "); my @tmp_array_ok; my @tmp_array_ko; for my $r (keys %{$data->{$job_info->{job_id}}->{'reserved_resources'}}) { if ($data->{$job_info->{job_id}}->{'reserved_resources'}->{$r}->{'current_state'} eq "Alive") { push (@tmp_array_ok,$r); } else { push (@tmp_array_ko,$r); } } my $tmp_str_ok = join("+",sort ({ $a <=> $b } @tmp_array_ok)); my $tmp_str_ko = join("+",sort ({ $a <=> $b } @tmp_array_ko)); if ( $tmp_str_ok ne "" ) { print $tmp_str_ok; } else { print("none"); } if ( $tmp_str_ko ne "" ) { print "+($tmp_str_ko)"; } print("\n"); } if (!defined($data->{$job_info->{job_id}}->{walltime})){ $data->{$job_info->{job_id}}->{walltime} = ''; }else{ $data->{$job_info->{job_id}}->{walltime} = OAR::IO::duration_to_sql($data->{$job_info->{job_id}}->{walltime}); } print(" walltime = $data->{$job_info->{job_id}}->{walltime}\n"); print(" submissionTime = ".OAR::IO::local_to_sql($job_info->{submission_time})."\n"); print(" startTime = ".OAR::IO::local_to_sql($job_info->{start_time})."\n") if ($job_info->{start_time} > 0); print(" stopTime = ".OAR::IO::local_to_sql($job_info->{stop_time})."\n") if ($job_info->{stop_time} > 0); if (defined($data->{$job_info->{job_id}}->{cpuset_name})){ print(" cpuset_name = $data->{$job_info->{job_id}}->{cpuset_name}\n"); } if (defined($data->{$job_info->{job_id}}->{job_uid})){ print(" job_user = $data->{$job_info->{job_id}}->{job_user}\n"); print(" job_uid = $data->{$job_info->{job_id}}->{job_uid}\n"); } if ((defined($job_info->{initial_request})) and (($ENV{OARDO_USER} eq $job_info->{job_user}) or ($ENV{OARDO_USER} eq "oar") or ($ENV{OARDO_USER} eq "root")) ){ print(" initial_request = $job_info->{initial_request}\n"); }else{ print(" initial_request = \n"); } print(" message = $job_info->{message}\n"); if (!defined($data->{$job_info->{job_id}}->{scheduledStart})){ $data->{$job_info->{job_id}}->{scheduledStart} = "no prediction"; }else{ $data->{$job_info->{job_id}}->{scheduledStart} = OAR::IO::local_to_sql($data->{$job_info->{job_id}}->{scheduledStart}); } print(" scheduledStart = $data->{$job_info->{job_id}}->{scheduledStart}\n"); print(" resubmit_job_id = $job_info->{resubmit_job_id}\n"); print(" events = "); foreach my $e (@{$data->{$job_info->{job_id}}->{events}}){ print("[".OAR::IO::local_to_sql($e->{date})."] $e->{type}:$e->{description}"); print(" , "); } print("\n\n"); }else{ if(defined($array_id)){ if ($Printed_jobs == 0){ print <{'command'} = '' if (!defined($job_info->{'command'})); $job_info->{job_name} = '' if (!defined($job_info->{job_name})); printf("%-9.9s %-9.9s %-9.9s %-10.10s %-8.8s %-19.19s %1.1s %-8.8s\n", $job_info->{'job_id'}, $job_info->{'array_id'}, $job_info->{'array_index'}, $job_info->{'job_name'}, $job_info->{'job_user'}, OAR::IO::local_to_sql($job_info->{'submission_time'}), $hashestat{$job_info->{'state'}}, $job_info->{'queue_name'} ); $Printed_jobs ++; }else{ if ($Printed_jobs == 0){ print <{'command'} = '' if (!defined($job_info->{'command'})); $job_info->{job_name} = '' if (!defined($job_info->{job_name})); printf("%-10.10s %-14.14s %-14.14s %-19.19s %1.1s %-10.10s\n", $job_info->{'job_id'}, $job_info->{'job_name'}, $job_info->{'job_user'}, OAR::IO::local_to_sql($job_info->{'submission_time'}), $hashestat{$job_info->{'state'}}, $job_info->{'queue_name'} ); $Printed_jobs ++; } } } } } sub get_history($$$){ my ($base,$date_start,$date_stop) = @_; $date_start = OAR::IO::sql_to_local($date_start); $date_stop = OAR::IO::sql_to_local($date_stop); my %hash_dumper_result; my @nodes = OAR::IO::list_resources($base); $hash_dumper_result{resources} = \@nodes; my %job_gantt = OAR::IO::get_jobs_gantt_scheduled($base,$date_start,$date_stop); $hash_dumper_result{jobs} = \%job_gantt; #print(Dumper(%hash_dumper_result)); #print finished or running jobs my %jobs_history = OAR::IO::get_jobs_range_dates($base,$date_start,$date_stop); foreach my $i (keys(%jobs_history)){ my $types = OAR::IO::get_current_job_types($base,$i); if (!defined($job_gantt{$i}) || (defined($types->{besteffort}))){ if (($jobs_history{$i}->{state} eq "Running") || ($jobs_history{$i}->{state} eq "toLaunch") || ($jobs_history{$i}->{state} eq "Suspended") || ($jobs_history{$i}->{state} eq "Resuming") || ($jobs_history{$i}->{state} eq "Launching")){ if (defined($types->{besteffort})){ $jobs_history{$i}->{stop_time} = OAR::IO::get_gantt_visu_date($base); }else{ #This job must be already printed by gantt next; } } $hash_dumper_result{jobs}{$i} = $jobs_history{$i}; } } #print Down or Suspected resources my %dead_resource_dates = OAR::IO::get_resource_dead_range_date($base,$date_start,$date_stop); $hash_dumper_result{dead_resources} = \%dead_resource_dates; return(\%hash_dumper_result); } sub duration($){ # Converts a number of seconds in a human readable duration (years,days,hours,mins,secs) my $time=shift; my $seconds; my $minutes; my $hours; my $days; my $years; my $duration=""; $years=int($time/31536000); if ($years==1) { $duration .="1 year ";} elsif ($years) { $duration .="$years years ";}; $days=int($time/86400)%365; if ($days==1) { $duration .="1 day ";} elsif ($days) { $duration .="$days days ";}; $hours=int($time/3600)%24; if ($hours==1) { $duration .="1 hour ";} elsif ($hours) { $duration .="$hours hours ";}; $minutes=int($time/60)%60; if ($minutes==1) { $duration .="1 minute ";} elsif ($minutes) { $duration .="$minutes minutes ";}; $seconds=$time%60; if ($seconds==1) { $duration .="1 second ";} elsif ($seconds) { $duration .="$seconds seconds ";}; if ($duration eq "") {$duration="0 seconds ";}; return $duration; } ./oar-2.5.2/sources/core/qfunctions/oarstat0000755000175000017500000005006211757171206016773 0ustar plbplb#!/usr/bin/perl -w # $Id$ # print active job properties use strict; use warnings; use Data::Dumper; #use OAR::IO; use Getopt::Long; #use OAR::Version; use OAR::Conf qw(init_conf dump_conf get_conf is_conf); use OAR::Stat; my $Old_umask = sprintf("%lo",umask()); umask(oct("022")); # Read config init_conf($ENV{OARCONFFILE}); my $Cpuset_field = get_conf("JOB_RESOURCE_MANAGER_PROPERTY_DB_FIELD"); my $Job_uid_resource_type = get_conf("JOB_RESOURCE_MANAGER_JOB_UID_TYPE"); #Try to load XML module my $XML_enabled = 1; unless (eval "use XML::Dumper qw(pl2xml);1"){ $XML_enabled = 0; } #Try to load YAML module my $YAML_enabled = 1; unless (eval "use YAML;1"){ $YAML_enabled = 0; } #Try to load JSON module my $JSON_enabled = 1; unless (eval "use JSON;1"){ $JSON_enabled = 0; } # suitable Data::Dumper configuration for serialization $Data::Dumper::Purity = 1; $Data::Dumper::Terse = 1; $Data::Dumper::Indent = 1; $Data::Dumper::Deepcopy = 1; ### Variables declaration ### my $Printed_jobs = 0; my @job_ids; my $array_id; my $full_view; my $state; my $sql_property; my $XML_mode; my $YAML_mode; my $JSON_mode; my $DUMPER_mode; my $gantt_query; my $accounting_query; my $events_query; my $properties_query; my $usage; my $version; my $user; ### END Variables declaration ### ### Main ### # parse command line option Getopt::Long::Configure ("gnu_getopt"); GetOptions ("help|h" => \$usage, "version|V" => \$version, "job|j=i" => \@job_ids, "full|f" => \$full_view, "state|s" => \$state, "user|u:s" => \$user, "array:i" => \$array_id, "gantt|g=s" => \$gantt_query, "events|e" => \$events_query, "properties|p" => \$properties_query, "accounting=s" => \$accounting_query, "sql=s" => \$sql_property, "xml|X" => \$XML_mode, "yaml|Y" => \$YAML_mode, "json|J" => \$JSON_mode, "dumper|D" => \$DUMPER_mode ); if ($usage){ &print_usage; exit(0); } if ($version){ &print_oar_version; exit(0); } if (defined($XML_mode) and $XML_enabled != 1){ warn("XML module not available on the system. Ask your administrator to install it if needed.\n"); exit(1) } if (defined($YAML_mode) and $YAML_enabled != 1){ warn("Yaml module not available on the system. Ask your administrator to install it if needed.\n"); exit(1); } if (defined($JSON_mode) and $JSON_enabled != 1){ warn("Json module not available on the system. Ask your administrator to install it if needed.\n"); exit(1); } $user = $ENV{OARDO_USER} if (defined($user) and ($user eq '')); if (defined($array_id) && $array_id !=0 && (scalar @job_ids > 0)){ warn("/!\\ ERROR : Conflicting Job IDs and Array IDs (--array and -j cannot be used together)\n"); exit(1); } OAR::Stat::open_db_connection() or die "DB connection error, exiting.\n"; if (defined ($gantt_query)) { &print_gantt; } elsif (defined ($accounting_query)) { &show_accounting; } elsif (defined ($events_query)) { &print_events; } elsif (defined ($properties_query)) { &print_properties; } elsif (defined ($state)) { &print_state; } else { &print_job; } OAR::Stat::close_db_connection(); exit(0); ### END Main ### ### Print Methods ### sub print_usage{ print <{'date'} ), $event_hashref->{'job_id'}, $event_hashref->{'type'}, $event_hashref->{'description'} ); } } else { warn("No job specified\n"); OAR::Stat::close_db_connection(); exit(1); } } sub print_gantt { my $hist = OAR::Stat::get_gantt($gantt_query); if (defined($hist)){ if (defined($DUMPER_mode)) { print(Dumper($hist)); }elsif (defined($XML_mode)) { my $dump = new XML::Dumper; $dump->dtd; print($dump->pl2xml($hist)); }elsif (defined($YAML_mode)) { print(YAML::Dump($hist)); }elsif (defined($JSON_mode)){ print(JSON->new->pretty(1)->encode($hist)); }else{ $Data::Dumper::Purity = 1; $Data::Dumper::Terse = 1; $Data::Dumper::Indent = 1; $Data::Dumper::Deepcopy = 0; print( Dumper($hist) ); } }else{ warn("Bad syntax for --gantt\n"); OAR::Stat::close_db_connection(); exit(1); } } sub print_job { my $jobs; if ( $#job_ids < 0 ) { if ( defined($sql_property) ) { $jobs = OAR::Stat::get_jobs_with_given_properties($sql_property); }elsif(defined($array_id) && $array_id !=0){ $jobs = OAR::Stat::get_array_subjobs($array_id); }else{ $jobs = OAR::Stat::get_all_jobs_for_user($user); } } elsif ( $#job_ids >= 0 ) { $jobs = OAR::Stat::get_specific_jobs(\@job_ids); } my %data_to_print; foreach my $g (@$jobs) { $data_to_print{ $g->{job_id} } = OAR::Stat::get_job_data($g,$full_view); } print_job_data( \%data_to_print, $jobs ); } sub print_job_data($$){ my $data = shift; my $job_array = shift; if (defined($DUMPER_mode)){ print(Dumper($data)); }elsif(defined($XML_mode)){ my $dump = new XML::Dumper; $dump->dtd; print($dump->pl2xml($data)); }elsif(defined($YAML_mode)){ print(YAML::Dump($data)); }elsif(defined($JSON_mode)){ print(JSON->new->pretty(1)->encode($data)); }else{ my %hashestat = ( 'Waiting' => 'W', 'toLaunch' => 'L', 'Launching' => 'L', 'Hold' => 'H', 'Running' => 'R', 'Terminated' => 'T', 'Error' => 'E', 'toError' => 'E', 'Finishing' => 'F', 'Suspended' => 'S', 'Resuming' => 'S', 'toAckReservation' => 'W' ); foreach my $job_info (@{$job_array}){ if (defined($full_view)){ print("Job_Id: $job_info->{job_id}\n"); $job_info->{job_name} = '' if (!defined($job_info->{job_name})); print(" job_array_id = $job_info->{array_id}\n"); print(" job_array_index = $job_info->{array_index}\n"); print(" name = $job_info->{job_name}\n"); print(" project = $job_info->{project}\n"); print(" owner = $job_info->{job_user}\n"); print(" state = $job_info->{state}\n"); print(" wanted_resources = $data->{$job_info->{job_id}}->{wanted_resources}\n"); print(" types = ".join(", ",@{$data->{$job_info->{job_id}}->{types}})."\n"); print(" dependencies = ".join(" ",@{$data->{$job_info->{job_id}}->{dependencies}})."\n"); print(" assigned_resources = ".join("+",@{$data->{$job_info->{job_id}}->{assigned_resources}})."\n"); print(" assigned_hostnames = ".join("+",@{$data->{$job_info->{job_id}}->{assigned_network_address}})."\n"); print(" queue = $job_info->{queue_name}\n"); $job_info->{command} = '' if (!defined($job_info->{command})); print(" command = $job_info->{command}\n"); if (defined($job_info->{exit_code})){ my $exit_code = $job_info->{exit_code} >> 8; my $exit_num = $job_info->{exit_code} & 127; my $exit_core = $job_info->{exit_code} & 128; print(" exit_code = $job_info->{exit_code} ($exit_code,$exit_num,$exit_core)\n"); } print(" launchingDirectory = $job_info->{launching_directory}\n"); print(" stdout_file = $data->{$job_info->{job_id}}->{stdout_file}\n") ; print(" stderr_file = $data->{$job_info->{job_id}}->{stderr_file}\n"); print(" jobType = $job_info->{job_type}\n"); print(" properties = $job_info->{properties}\n"); print(" reservation = $job_info->{reservation}\n"); if (defined $data->{$job_info->{job_id}}->{'reserved_resources'}) { print(" reserved_resources = "); my @tmp_array_ok; my @tmp_array_ko; for my $r (keys %{$data->{$job_info->{job_id}}->{'reserved_resources'}}) { if ($data->{$job_info->{job_id}}->{'reserved_resources'}->{$r}->{'current_state'} eq "Alive") { push (@tmp_array_ok,$r); } else { push (@tmp_array_ko,$r); } } my $tmp_str_ok = join("+",sort ({ $a <=> $b } @tmp_array_ok)); my $tmp_str_ko = join("+",sort ({ $a <=> $b } @tmp_array_ko)); if ( $tmp_str_ok ne "" ) { print $tmp_str_ok; } else { print("none"); } if ( $tmp_str_ko ne "" ) { print "+($tmp_str_ko)"; } print("\n"); } if (!defined($data->{$job_info->{job_id}}->{walltime})){ $data->{$job_info->{job_id}}->{walltime} = ''; }else{ $data->{$job_info->{job_id}}->{walltime} = OAR::Stat::duration_to_sql($data->{$job_info->{job_id}}->{walltime}); } print(" walltime = $data->{$job_info->{job_id}}->{walltime}\n"); print(" submissionTime = ".OAR::Stat::local_to_sql($job_info->{submission_time})."\n"); print(" startTime = ".OAR::Stat::local_to_sql($job_info->{start_time})."\n") if ($job_info->{start_time} > 0); print(" stopTime = ".OAR::Stat::local_to_sql($job_info->{stop_time})."\n") if ($job_info->{stop_time} > 0); if (defined($data->{$job_info->{job_id}}->{cpuset_name})){ print(" cpuset_name = $data->{$job_info->{job_id}}->{cpuset_name}\n"); } if (defined($data->{$job_info->{job_id}}->{job_uid})){ print(" job_user = $data->{$job_info->{job_id}}->{job_user}\n"); print(" job_uid = $data->{$job_info->{job_id}}->{job_uid}\n"); } if ((defined($job_info->{initial_request})) and (($ENV{OARDO_USER} eq $job_info->{job_user}) or ($ENV{OARDO_USER} eq "oar") or ($ENV{OARDO_USER} eq "root")) ){ print(" initial_request = $job_info->{initial_request}\n"); }else{ print(" initial_request = \n"); } print(" message = $job_info->{message}\n"); if (!defined($data->{$job_info->{job_id}}->{scheduledStart})){ $data->{$job_info->{job_id}}->{scheduledStart} = "no prediction"; }else{ $data->{$job_info->{job_id}}->{scheduledStart} = OAR::Stat::local_to_sql($data->{$job_info->{job_id}}->{scheduledStart}); } print(" scheduledStart = $data->{$job_info->{job_id}}->{scheduledStart}\n"); print(" resubmit_job_id = $job_info->{resubmit_job_id}\n"); print(" events = "); foreach my $e (@{$data->{$job_info->{job_id}}->{events}}){ print("[".OAR::Stat::local_to_sql($e->{date})."] $e->{type}:$e->{description}"); print(" , "); } print("\n\n"); }else{ if(defined($array_id)){ if ($Printed_jobs == 0){ print <{'command'} = '' if (!defined($job_info->{'command'})); $job_info->{job_name} = '' if (!defined($job_info->{job_name})); printf("%-9.9s %-9.9s %-9.9s %-10.10s %-8.8s %-19.19s %1.1s %-8.8s\n", $job_info->{'job_id'}, $job_info->{'array_id'}, $job_info->{'array_index'}, $job_info->{'job_name'}, $job_info->{'job_user'}, OAR::Stat::local_to_sql($job_info->{'submission_time'}), $hashestat{$job_info->{'state'}}, $job_info->{'queue_name'} ); $Printed_jobs ++; }else{ if ($Printed_jobs == 0){ print <{'command'} = '' if (!defined($job_info->{'command'})); $job_info->{job_name} = '' if (!defined($job_info->{job_name})); printf("%-10.10s %-14.14s %-14.14s %-19.19s %1.1s %-10.10s\n", $job_info->{'job_id'}, $job_info->{'job_name'}, $job_info->{'job_user'}, OAR::Stat::local_to_sql($job_info->{'submission_time'}), $hashestat{$job_info->{'state'}}, $job_info->{'queue_name'} ); $Printed_jobs ++; } } } } } sub print_job_state_data($){ my $data = shift; if (defined($DUMPER_mode)){ print(Dumper($data)); }elsif(defined($XML_mode)){ my $dump = new XML::Dumper; $dump->dtd; print($dump->pl2xml($data)); }elsif(defined($YAML_mode)){ print(YAML::Dump($data)); }elsif(defined($JSON_mode)){ print(JSON->new->pretty(1)->encode($data)); }else{ foreach my $j (keys %$data){ print "$j: ". $data->{$j} ."\n"; } } } sub print_oar_version { print "OAR version : ".OAR::Stat::get_oar_version()."\n"; } sub print_properties { if(defined($array_id) && $array_id !=0){ push(@job_ids, OAR::Stat::get_array_job_ids($array_id)); } if($#job_ids >= 0){ my @resources; foreach my $j (@job_ids){ push (@resources, OAR::Stat::get_job_resources_properties($j)); } foreach my $r (@resources){ my $line = ""; foreach my $p (keys(%{$r})){ if(OAR::Tools::check_resource_system_property($p) != 1){ $r->{$p} = "" if (!defined($r->{$p})); $line .= " $p = '$r->{$p}' ," } } chop($line); print("$line\n") or exit(5); } } else { warn("No job specified\n"); OAR::Stat::close_db_connection(); exit(1); } } sub print_state { my %job_state; if ($#job_ids < 0){ warn("--state can only be used with an id\n"); exit(1); }elsif($#job_ids >= 0){ foreach my $j (@job_ids){ my $state_string = OAR::Stat::get_job_state($j); if (defined($state_string)){ $job_state{$j}=$state_string; } } } print_job_state_data(\%job_state); } sub show_accounting { if ($accounting_query =~ m/\s*(\d{4}\-\d{1,2}\-\d{1,2})\s*,\s*(\d{4}\-\d{1,2}\-\d{1,2})\s*/m){ my ($date1,$date2) = ($1." 00:00:00",$2." 00:00:00"); my $login; my $Consumptions=OAR::Stat::get_accounting_summary(OAR::Stat::sql_to_local($date1),OAR::Stat::sql_to_local($date2),$user,$sql_property); # One user output if (defined($user)) { my $asked; my $used; if (defined($Consumptions->{$user}->{ASKED})) { $asked=$Consumptions->{$user}->{ASKED}; } else { $asked=0; } if (defined($Consumptions->{$user}->{USED})) { $used=$Consumptions->{$user}->{USED}; } else { $used=0; } if (!defined($Consumptions->{$user}->{begin})) {$Consumptions->{$user}->{begin}="No window found";} if (!defined($Consumptions->{$user}->{end})) {$Consumptions->{$user}->{end}="No window found";} print "Usage summary for user '$user' from $1 to $2:\n"; print "-------------------------------------------------------------\n"; printf ("%-28s %s\n","Start of the first window:",OAR::Stat::local_to_sql($Consumptions->{$user}->{begin})); printf ("%-28s %s\n","End of the last window:",OAR::Stat::local_to_sql($Consumptions->{$user}->{end})); printf ("%-28s %s ( %s)\n","Asked consumption:",$asked,OAR::Stat::get_duration($asked)); printf ("%-28s %s ( %s)\n","Used consumption:",$used,OAR::Stat::get_duration($used)); print "By project consumption:\n"; $Consumptions=OAR::Stat::get_accounting_summary_byproject(OAR::Stat::sql_to_local($date1),OAR::Stat::sql_to_local($date2),$user); foreach my $project (keys %{$Consumptions}) { print " $project:\n"; $asked=$Consumptions->{$project}->{ASKED}->{$user}; $used=$Consumptions->{$project}->{USED}->{$user}; printf ("%-28s %s ( %s)\n"," Asked :",$asked,OAR::Stat::get_duration($asked)); printf ("%-28s %s ( %s)\n"," Used :",$used,OAR::Stat::get_duration($used)); if (my @last_karma=OAR::Stat::get_last_project_karma($user,$project,OAR::Stat::sql_to_local($date2))) { printf ("%-28s %s\n"," Last Karma :",$last_karma[0]); } } # All users array output }else{ print <{$login}->{ASKED})) {$Consumptions->{$login}->{ASKED}=0;} if (!defined($Consumptions->{$login}->{USED})) {$Consumptions->{$login}->{USED}=0;} printf("%-10.10s %-19s %-19s %16s %16s\n", $login, OAR::Stat::local_to_sql($Consumptions->{$login}->{begin}), OAR::Stat::local_to_sql($Consumptions->{$login}->{end}), $Consumptions->{$login}->{ASKED}, $Consumptions->{$login}->{USED} ); } } }else{ print("Bad syntax for --accounting\n"); OAR::Stat::close_db_connection(); exit(1); } } ### END Print Methods ### ./oar-2.5.2/sources/core/qfunctions/oarresume0000755000175000017500000000631011757171206017315 0ustar plbplb#!/usr/bin/perl # $Id$ # resume a job --> it will be rescheduled use strict; use warnings; use Data::Dumper; use DBI(); use OAR::IO; use OAR::Conf qw(init_conf dump_conf get_conf is_conf); use OAR::Tools; use OAR::Version; use Getopt::Long; my $Old_umask = sprintf("%lo",umask()); umask(oct("022")); sub usage { print < \$Version, "sql=s" => \$Sql_property, "array" => \$array, "help|h" => \$Help ); if (defined($Help)){ usage(); exit(0); } if (defined($Version)){ print("OAR version : ".OAR::Version::get_version()."\n"); exit(0); } if (($#ARGV < 0) and (!defined($Sql_property))){ usage(); exit(1); } init_conf($ENV{OARCONFFILE}); my $remote_host = get_conf("SERVER_HOSTNAME"); my $remote_port = get_conf("SERVER_PORT"); my @job_ids; my $exit_code = 0; foreach my $j (@ARGV){ if ($j =~ m/^\d+$/m){ #if defined --array, delete all the sub-jobs if(defined($array)){ my $db = OAR::IO::connect_ro(); my @tmp_jobs = OAR::IO::get_array_job_ids($db,$j); if(scalar @tmp_jobs == 0){ warn("[ERROR] \"$j\" is not a valid array job\n"); $exit_code = 2; }else{ foreach my $j (@tmp_jobs){ push(@job_ids, $j); } } OAR::IO::disconnect($db); }else{ push(@job_ids, $j); } }else{ if(defined($array)){ warn("[ERROR] \"$j\" is not a valid job array identifier\n"); }else{ warn("[ERROR] \"$j\" is not a valid job identifier\n"); } $exit_code = 2; } } if (defined($Sql_property)){ my $db = OAR::IO::connect_ro(); foreach my $j (OAR::IO::get_jobs_with_given_properties($db,$Sql_property)){ push(@job_ids, $j->{job_id}); } OAR::IO::disconnect($db); } my $base = OAR::IO::connect(); foreach my $j (@job_ids){ my $err = OAR::IO::resume_job($base,$j); if ($err != 0) { my $str = "/!\\ Cannot resume $j :"; if ($err == -1){ warn("$str this job does not exist.\n"); }elsif ($err == -2){ warn("$str you are not the right user.\n"); }elsif ($err == -3){ warn("$str the job is not in the Hold or Suspended state.\n"); }elsif ($err == -4){ warn("$str only oar or root user can resume Suspended jobs.\n"); }else{ warn("$str unknown reason.\n"); } $exit_code = 1; }else{ print("[$j] Resume request was sent to the OAR server.\n"); } } OAR::IO::disconnect($base); #Signal Almigthy OAR::Tools::notify_tcp_socket($remote_host,$remote_port,"ChState"); exit($exit_code); ./oar-2.5.2/sources/core/qfunctions/oarremoveresource0000755000175000017500000000441511757171206021066 0ustar plbplb#!/usr/bin/perl # $Id$ # use strict; use warnings; use Data::Dumper; use DBI(); use OAR::IO; my $Old_umask = sprintf("%lo",umask()); umask(oct("022")); sub usage(){ print <{state}) && ($resource_ref->{state} eq "Dead")){ my $sth = $base->prepare(" SELECT jobs.job_id, jobs.assigned_moldable_job FROM assigned_resources, jobs WHERE assigned_resources.resource_id = $Resource AND assigned_resources.moldable_job_id = jobs.assigned_moldable_job "); $sth->execute(); my @jobList; while (my @ref = $sth->fetchrow_array()) { push(@jobList, [$ref[0], $ref[1]]); } $sth->finish(); foreach my $i (@jobList){ print("\tRemove the job $i->[0], it was run on the resource $Resource\n"); $base->do("DELETE from event_logs WHERE job_id = $i->[0]"); $base->do("DELETE from frag_jobs WHERE frag_id_job = $i->[0]"); $base->do("DELETE from jobs WHERE job_id = $i->[0]"); $base->do("DELETE from assigned_resources WHERE moldable_job_id = $i->[1]"); } $base->do("DELETE from assigned_resources WHERE resource_id = $Resource"); $base->do("DELETE from resource_logs WHERE resource_id = $Resource"); $base->do("DELETE from resources WHERE resource_id = $Resource"); print("Resource $Resource removed.\n"); }else{ print("/!\\ The state of the resource $Resource must be set to Dead before.\n"); $exit_code = 2; } OAR::IO::unlock_table($base); OAR::IO::disconnect($base); exit($exit_code); ./oar-2.5.2/sources/core/qfunctions/oarproperty0000755000175000017500000001211411757171206017700 0ustar plbplb#!/usr/bin/perl -w # $Id$ # # This script aims at managing the node properties (list, add, delete). # To set the properties values, use oarnodesettings. # # To use the quiet mode, just do something like: # echo -e "mysqlroot\nmysqlpassword\n" | oar_property.pl -q -l use strict; use warnings; use OAR::IO; use OAR::Conf qw(init_conf get_conf is_conf); use Getopt::Long; use OAR::Version; use OAR::Tools; my $Old_umask = sprintf("%lo",umask()); umask(oct("022")); # The functions are appended at the end of this file sub print_usage(); sub check_property_name($); Getopt::Long::Configure("gnu_getopt"); my ($Help, $List_properties, $Quiet, $Varchar); my @Add_property; my @Delete_property; my @Rename_property; my $Version; GetOptions( "help|h" => \$Help, "add|a=s" => \@Add_property, "delete|d=s" => \@Delete_property, "rename|r=s" => \@Rename_property, "list|l" => \$List_properties, "quiet|q" => \$Quiet, "varchar|c" => \$Varchar, "version|V" => \$Version ); if (defined($Version)){ print("OAR version : ".OAR::Version::get_version()."\n"); exit(0); } if ($Help || (($#Add_property < 0) and ($#Delete_property < 0) and ($#Rename_property < 0) and (! defined($List_properties)))) { print_usage(); exit(1); } my $base = OAR::IO::connect(); my $query; if (defined($List_properties)) { my %list = OAR::IO::list_resource_properties_fields($base); foreach my $l (keys(%list)){ unless (OAR::Tools::check_resource_property($l) == 1){ print("$l\n"); } } } foreach my $r (@Delete_property){ check_property_name($r); $base->do("ALTER TABLE resources DROP COLUMN $r") or die("DB error: " . $base->errstr."\n"); if (! $Quiet) { print("Removed property: $r\n"); } } foreach my $a (@Add_property){ check_property_name($a); my $req; if ($Varchar){ $req = "ALTER TABLE resources ADD COLUMN $a VARCHAR(255)"; }else{ $req = "ALTER TABLE resources ADD COLUMN $a INT"; } $base->do($req) or die("DB error: " . $base->errstr."\n"); if (! $Quiet) { print("Added property: $a\n"); } } foreach my $p (@Rename_property){ my ($old_prop,$new_prop); if ($p !~ m/^\s*([a-z0-9_]+)\s*,\s*([a-z0-9_]+)\s*$/m){ print_usage(); exit(1); } $old_prop = $1; $new_prop = $2; check_property_name($old_prop); check_property_name($new_prop); if (OAR::IO::get_database_type() eq "Pg"){ $base->do(" ALTER TABLE resources RENAME $old_prop TO $new_prop") or die("DB error: " . $base->errstr."\n"); }else{ my $sth = $base->prepare(" SHOW FIELDS FROM resources "); $sth->execute() or die("DB error : ".$base->errstr."\n"); my $type; my $field = ""; while (($field ne $old_prop) and (my @ref = $sth->fetchrow_array())){ $field = $ref[0]; $type = $ref[1]; } $sth->finish(); die("Unknown field $old_prop\n") if ($field ne $old_prop); $base->do(" ALTER TABLE resources CHANGE $old_prop $new_prop $type"); } $base->do(" UPDATE resource_logs SET attribute = \'$new_prop\' WHERE attribute = \'$old_prop\'") or die("DB error: " . $base->errstr."\n"); $base->do(" UPDATE job_resource_descriptions SET res_job_resource_type = \'$new_prop\' WHERE res_job_resource_type = \'$old_prop\'") or die("DB error: " . $base->errstr."\n"); if (! $Quiet) { print("Rename property $old_prop into $new_prop\n"); } } OAR::IO::disconnect($base); exit(0); ############################################################################# # FUNCTIONS # #Display usage sub print_usage() { print < \$input, "format|F=s" => \$fmt, "properties|P=s" => \$properties, "comma|C=s" => \$comma, "token|T=s" => \$replace, "list|l" => \$list, "help|h" => sub {usage(); exit(0);} ); ################################################################################ ## usage() sub usage() { print STDERR < Print a sorted output of the resources of a job with regard to a key property, with a customisable format. Options: -f input file, default: \$OAR_RESOURCE_PROPERTIES_FILE -P property to display separated by commas, default: key property -F customised output format, default: "%" -T substitution token in the format string, default: % -C separator when displaying lists, default: , -l list available properties and exit -h print this help and exit Examples: On the job connection node (where \$OAR_RESOURCE_PROPERTIES_FILE is defined): > oarprint host -P host,cpu,core -F "host: % cpu: % core: %" -C+ On the submission frontend: > oarstat -j 42 -p | oarprint core -P host,cpuset,mem -F "%[%] (%)" -f - EOF } ################################################################################ ## init_resource() sub init_resources { my $fh; if (not defined($input)) { die "$0: no input data available\n"; } if ($input eq "-") { $fh = \*STDIN; } else { open($fh, "< $input") or die "$0: $!\n"; } my @T; while (<$fh>) { chomp; s/ = / => /g; push @T,"{ $_ }"; } if ($input ne "-") { close $fh; } my $res = eval("[".join(',',@T)."];"); return $res; } ################################################################################ ## list_properties() sub list_properties { my $resources=shift; print "List of properties:\n"; print join(", ",(keys %{$resources->[0]}))."\n"; } ################################################################################ ## print_output() sub print_output { my $resources=shift; my $property=shift; # build a hash tree sorted on unique values of the key property my $h = {}; foreach my $r (@$resources) {; exists ($r->{$property}) or die "$0: Unknown property '$property'\n"; my $v = $r->{$property}; for my $o (@properties_list) { exists ($r->{$o}) or die "$0: Unknown property '$o'\n"; $h->{$v}->{$o}->{$r->{$o}} = undef; } } #print Dumper($h)."\n"; #print data using the specified format foreach my $v (values %$h) { my @f = split($replace,$fmt); my $i=0; foreach my $o (@properties_list) { if ($#f < 0) { $i++ and print " "; } else { print shift(@f); } print join($comma,keys(%{$v->{$o}})); } if ($#f >= 0) { print shift(@f); } print "\n"; } } ################################################################################ ## Main my $resources = init_resources(); if (defined($list)) { list_properties($resources); } else { $key = shift; if (not defined($key)) { die "$0: Need a property name\n"; } if (defined($properties)) { @properties_list = split(/\s*,\s*/,$properties); } if ($#properties_list < 0) { @properties_list = ($key); } print_output($resources,$key); } ./oar-2.5.2/sources/core/qfunctions/oarnotify0000755000175000017500000000655611757171206017341 0ustar plbplb#!/usr/bin/perl # $Id$ # notify Almighty use strict; use warnings; use Data::Dumper; use OAR::Conf qw(init_conf dump_conf get_conf is_conf); use Getopt::Long; use OAR::Version; use OAR::Tools; use OAR::IO; my $Old_umask = sprintf("%lo",umask()); umask(oct("022")); init_conf($ENV{OARCONFFILE}); my $remote_host = get_conf("SERVER_HOSTNAME"); my $remote_port = get_conf("SERVER_PORT"); Getopt::Long::Configure("gnu_getopt"); my $Version; my @Enable_queue; my @Disable_queue; my @Add_queue; my @Remove_queue; my $Enable_all; my $Disable_all; my $List_queues; my $sos; GetOptions ("version|V" => \$Version, "enable_queue|e=s" => \@Enable_queue, "disable_queue|d=s" => \@Disable_queue, "Enable_all_queues|E" => \$Enable_all, "Disable_all_queues|D" => \$Disable_all, "add_queue=s" => \@Add_queue, "remove_queue=s" => \@Remove_queue, "list_queues|l" => \$List_queues, "help|h" => \$sos ); # Display command help sub usage { print <{priority}\n"); print("\tscheduler = $queues{$q}->{scheduler_policy}\n"); print("\tstate = $queues{$q}->{state}\n"); } } OAR::IO::disconnect($base); my $tag = "Term"; if (defined($ARGV[0])){ $tag = $ARGV[0]; } #print("tag = $tag\n"); OAR::Tools::notify_tcp_socket($remote_host,$remote_port,"$tag"); exit(0); ./oar-2.5.2/sources/core/qfunctions/oarnodesetting0000755000175000017500000003251511757171206020346 0ustar plbplb#!/usr/bin/perl # $Id$ # change node state dynamically use strict; use DBI(); use Data::Dumper; use OAR::IO; use OAR::Conf qw(init_conf dump_conf get_conf is_conf); use Sys::Hostname; use Getopt::Long; use OAR::Tools; use OAR::Version; my $Old_umask = sprintf("%lo",umask()); umask(oct("022")); $| = 1; my $exit_code = 0; my @notify_server_tag_list; my @hostnames; my $base; my $state; my $maintenance; my $nowaitMode; my @properties; my $new_resource; my $sos; my @resource; my $file; my $Sql_property; # Get OAR configuration init_conf($ENV{OARCONFFILE}); my $remote_host = get_conf("SERVER_HOSTNAME"); my $remote_port = get_conf("SERVER_PORT"); sub set_hostname_properties($$$){ my $base = shift; my $hostname = shift; my $arrayProp = shift; foreach my $p (@{$arrayProp}){ if ($p =~ m/(.+)\s*=\s*(.+)/m){ if (OAR::Tools::check_resource_system_property($1) == 1){ warn("/!\\ Cannot update property $1 because it is a system field.\n"); $exit_code = 5; next; } print("Update property $1 with value $2 on node $hostname ..."); my $ret = OAR::IO::set_node_property($base,$hostname,$1,$2); if ($ret == 0){ print("DONE\n"); }else{ print("ERROR (wrong property or wrong value)\n"); $exit_code = 6; } }else{ warn("/!\\ Bad property syntax : -p property=value\n"); $exit_code = 7; } } } sub set_resource_properties($$$){ my $base = shift; my $resource = shift; my $arrayProp = shift; foreach my $p (@{$arrayProp}){ if ($p =~ m/(.+)\s*=\s*(.+)/m){ if (OAR::Tools::check_resource_system_property($1) == 1){ warn("/!\\ Cannot update property $1 because it is a system field.\n"); $exit_code = 8; next; } print("Update property $1 with value $2 ..."); my $ret = OAR::IO::set_resource_property($base,$resource,$1,$2); if ($ret == 0){ print("DONE\n"); }elsif($ret == 2){ print("SAME (the property is already equal to the value)\n"); }else{ print("ERROR (wrong property or wrong value)\n"); $exit_code = 9; } }else{ warn("/!\\ Bad property syntax : -p property=value\n"); $exit_code = 10; } } } sub wait_end_of_running_jobs($$){ my $dbh = shift; my $job_list = shift; my $max_timeout = 30; my $jobInfo; foreach my $j (sort(@{$job_list})){ $jobInfo = {'state' => 'Running'}; # active waiting : it is not very nice but it works!! print("\t$j "); my $timeCount = 0; while ((($jobInfo->{'state'} ne "Terminated") and ($jobInfo->{'state'} ne "Error")) and ($timeCount < $max_timeout)){ $jobInfo = OAR::IO::get_job($dbh,$j); sleep(1); print("."); $timeCount++; } if ($timeCount >= $max_timeout){ print(" Timouted\n"); $exit_code = 11; }else{ print(" Deleted\n"); } } print("Check done\n"); } sub set_maintenance_on($$$$$){ my $base = shift; my $resources_list = shift; my $remote_host = shift; my $remote_port = shift; my $nowaitMode = shift; foreach my $res (@{$resources_list}){ my $res_info = OAR::IO::get_resource_info($base,$res); if (!defined($res_info)){ warn("/!\\ The resource $res does not exist in OAR database.\n"); $exit_code = 1; }else{ print "maintenance mode set to \"ON\" on resource $res\n"; OAR::IO::add_event_maintenance_on($base, $res, OAR::IO::get_date($base)); my @prop_to_set; my $last_available_upto = $res_info->{'available_upto'}; push @prop_to_set, "available_upto=0"; push @prop_to_set, "last_available_upto=$last_available_upto" if $last_available_upto != 0; set_resource_properties($base, $res, \@prop_to_set); OAR::IO::set_resource_nextState($base,$res,"Absent"); OAR::Tools::notify_tcp_socket($remote_host,$remote_port,"ChState"); if (!$nowaitMode){ print("Check jobs to delete on resource $res :\n"); my @jobs = OAR::IO::get_resource_job_to_frag($base,$res); wait_end_of_running_jobs($base,\@jobs); } } } } sub set_maintenance_off($$$$$){ my $base = shift; my $resources_list = shift; my $remote_host = shift; my $remote_port = shift; my $nowaitMode = shift; foreach my $res (@{$resources_list}){ my $res_info = OAR::IO::get_resource_info($base,$res); if (!defined($res_info)){ warn("/!\\ The resource $res does not exist in OAR database.\n"); $exit_code = 1; }else{ print "maintenance mode set to \"OFF\" on resource $res\n"; OAR::IO::add_event_maintenance_off($base, $res, OAR::IO::get_date($base)); my @prop_to_set; my $available_upto = $res_info->{'last_available_upto'}; push @prop_to_set, "available_upto=$available_upto" if $available_upto != 0;; set_resource_properties($base, $res, \@prop_to_set); OAR::IO::set_resource_nextState($base,$res,"Alive"); OAR::Tools::notify_tcp_socket($remote_host,$remote_port,"ChState"); } } } # print explanations for the user and exit sub usage { print < \$state, "maintenance|m=s" => \$maintenance, "care=s" => \$maintenance, # care is an alias for maintenance "hostname|h=s" => \@hostnames, "no-wait|n" => \$nowaitMode, "property|p=s" => \@properties, "add|a" => \$new_resource, "help" => \$sos, "sql=s" => \$Sql_property, "resource|r=i" => \@resource, "file|f=s" => \$file, "version|V" => \$Version ); usage() if (defined($sos)); if (defined($Version)){ print("OAR version : ".OAR::Version::get_version()."\n"); exit(0); } ($#properties >= 0) || defined($state) || defined($new_resource) || defined($maintenance) or usage(); if (defined($state) && !(($state eq 'Alive') || ($state eq 'Absent') || ($state eq 'Dead'))){ warn("/!\\ Bad state value. Possibilities are : Alive | Absent | Dead \n"); usage(); } if (defined($maintenance) && !(($maintenance eq 'on') || ($maintenance eq 'off'))){ warn("/!\\ Bad maintenance mode value. Possibilities are : on | off\n"); usage(); } if (defined($Sql_property)){ my $db = OAR::IO::connect_ro(); foreach my $r (OAR::IO::get_resources_with_given_sql($db,$Sql_property)){ push(@resource, $r); } OAR::IO::disconnect($db); if ($#resource < 0){ warn("/!\\ Your SQL clause returns nothing and there is no resource specified.\n"); $exit_code = 12; } } # Get hostnames from a file if (defined($file)){ if (open(FILE, "< $file")){ while () { my $line = $_; if ($line !~ m/^\s*$/m){ chop($line); push(@hostnames, $line); } } close(FILE); }else{ warn("/!\\ Cannot open the file $file.\n"); $exit_code = 13; } } if (defined($new_resource)){ if (($#resource >= 0) or (defined($Sql_property))){ warn("/!\\ You cannot use -r|--resource and (-a|--add or --sql) options together\n"); usage(); } } defined($hostnames[0]) or $hostnames[0] = hostname(); #print("$hostname\n"); my $base = OAR::IO::connect() or die("Cannot connect to the database\n"); if (defined($new_resource)){ # Create a new resources $state = "Alive" if (!defined($state)); foreach my $h (@hostnames){ print("new resource\n"); print("$h added in the database\n"); push(@resource,OAR::IO::add_resource($base, $h, $state)); } push(@notify_server_tag_list, "ChState"); push(@notify_server_tag_list, "Term"); }else{ if (($#resource >= 0) or (defined($Sql_property))){ if (defined($state)){ my @resources_to_check; foreach my $r (@resource){ #$base->do("LOCK TABLE resources WRITE"); if (OAR::IO::set_resource_nextState($base,$r,$state) > 0){ #$base->do("UNLOCK TABLES"); print("$r --> $state\n"); push(@resources_to_check, $r); }else{ warn("/!\\ The resource $r does not exist in OAR database.\n"); $exit_code = 3; } } OAR::Tools::notify_tcp_socket($remote_host,$remote_port,"ChState"); if (($state eq 'Dead') || ($state eq 'Absent')){ if (!$nowaitMode){ foreach my $r (@resources_to_check){ print("Check jobs to delete on resource $r :\n"); my @jobs = OAR::IO::get_resource_job_to_frag($base,$r); wait_end_of_running_jobs($base,\@jobs); } } }elsif ($state eq 'Alive'){ print("Done\n"); } } if (defined($maintenance)){ if ($maintenance eq 'on'){ set_maintenance_on($base, \@resource, $remote_host, $remote_port, $nowaitMode); } if ($maintenance eq 'off'){ set_maintenance_off($base, \@resource, $remote_host, $remote_port, $nowaitMode); } } }else{ # update all resources with netwokAdress = $hostname if (defined($maintenance)){ my @res_to_maintain; foreach my $h (@hostnames){ push @res_to_maintain, OAR::IO::get_all_resources_on_node($base, $h); } if ($maintenance eq 'on'){ set_maintenance_on($base, \@res_to_maintain, $remote_host, $remote_port, $nowaitMode); } if ($maintenance eq 'off'){ set_maintenance_off($base, \@res_to_maintain, $remote_host, $remote_port, $nowaitMode); } } if (defined($state)){ my @nodes_to_check; foreach my $h (@hostnames){ #$base->do("LOCK TABLE resources WRITE"); if (OAR::IO::set_node_nextState($base,$h,$state) > 0){ #$base->do("UNLOCK TABLES"); print("$h --> $state\n"); push(@nodes_to_check, $h); }else{ warn("/!\\ The node $h does not exist in OAR database.\n"); $exit_code = 4; } } OAR::Tools::notify_tcp_socket($remote_host,$remote_port,"ChState"); if (($state eq 'Dead') || ($state eq 'Absent')){ if (!$nowaitMode){ foreach my $h (@nodes_to_check){ print("Check jobs to delete on node $h :\n"); my @jobs = OAR::IO::get_node_job_to_frag($base,$h); wait_end_of_running_jobs($base,\@jobs); } } } } } } # Update properties if ($#properties >= 0){ if (($#resource >= 0) or (defined($Sql_property))){ foreach my $r (@resource){ if (!defined(OAR::IO::get_resource_info($base,$r))){ warn("/!\\ The resource $r does not exist in OAR database.\n"); $exit_code = 1; }else{ set_resource_properties($base,$r,\@properties); } } }else{ foreach my $h (@hostnames){ if (OAR::IO::is_node_exists($base,$h) == 0){ warn("/!\\ The node $h does not exist in OAR database. First you must add it with -a|--add option.\n"); $exit_code = 2; }else{ set_hostname_properties($base,$h,\@properties); } } } push(@notify_server_tag_list, "Term"); } OAR::IO::disconnect($base); foreach my $t (@notify_server_tag_list){ OAR::Tools::notify_tcp_socket($remote_host,$remote_port,$t); } exit($exit_code); ./oar-2.5.2/sources/core/qfunctions/oarnodes.v2_30000755000175000017500000004307611757171206017707 0ustar plbplb#!/usr/bin/perl # $Id: oarnodes 1813 2008-12-22 14:31:55Z bzizou $ # print OAR node properties # # EXAMPLES: # oarnodes -l # => returns the complete list without information - status = 0 # oarnodes -a # => returns the complete list with information - status = 0 # oarnodes -s # => returns only the state of nodes - status = 0 # oarnodes -h|--help # => returns a help message - status = 0 # oarnodes host1 [.. hostn] # => returns the information for hostX - status is 0 for every host known - 1 otherwise # use strict; use warnings; use OAR::Conf qw(init_conf dump_conf get_conf is_conf); use OAR::Tools; use Data::Dumper; use OAR::IO; use Getopt::Long; use OAR::Version; my $Old_umask = sprintf("%lo",umask()); umask(oct("022")); #Try to load XML module my $XML_enabled = 1; unless (eval "use XML::Dumper qw(pl2xml);1"){ $XML_enabled = 0; } #Try to load YAML module my $YAML_enabled = 1; unless (eval "use YAML;1"){ $YAML_enabled = 0; } # suitable Data::Dumper configuration for serialization $Data::Dumper::Purity = 1; $Data::Dumper::Terse = 1; $Data::Dumper::Indent = 0; $Data::Dumper::Deepcopy = 1; warn("WARNING: This command will be deprecated in a future release!\n"); Getopt::Long::Configure ("gnu_getopt"); my $stateMode; my $usage; my $listall; my $listallwithinfo; my @resources; my $sql_property; my $XML_mode; my $YAML_mode; my $DUMPER_mode; my $Version; my $Old; my $smp; my $events; GetOptions ("state|s" => \$stateMode, "help|h" => \$usage, "list|l" => \$listall, "all|a" => \$listallwithinfo, "events|e:s" => \$events, "resource|r=i" => \@resources, "sql=s" => \$sql_property, "xml|X" => \$XML_mode, "yaml|Y" => \$YAML_mode, "dumper|D" => \$DUMPER_mode, "backward-compatible" => \$Old, "smp" => \$smp, "version|V" => \$Version ); if ($usage){ print <{'date'}), $event_hashref->{'job_id'}, $event_hashref->{'type'}, $event_hashref->{'description'} ); } if (defined ($events)) { my $hostname = $ARGV[0]; if (not defined($hostname)) { die("No hostname specified\n"); } # here $events can contain the date from which we have to display info. my @events = OAR::IO::get_events_for_hostname($base, $hostname, $events); foreach my $current_event (@events) { print_event_logs($current_event); } OAR::IO::disconnect($base); exit 0; } if (($#resources >= 0) or (defined($sql_property))){ if (defined($sql_property)){ push(@resources, OAR::IO::get_resources_with_given_sql($base,$sql_property)); } my @data_to_display; my %state_hash; foreach my $r (@resources) { my $resource_info = OAR::IO::get_resource_info($base,$r); if (!defined($resource_info)) { warn("/!\\ $r UNKNOWN\n"); $exitstatus = 1; next; } $state_hash{$r} = $resource_info->{state}; my %tmp_hash; $tmp_hash{state} = $resource_info->{state}; $tmp_hash{network_address} = $resource_info->{network_address}; $tmp_hash{properties} = $resource_info; #if ($resource_info->{state} eq "Alive") { my @jobs = OAR::IO::get_resource_job($base,$resource_info->{resource_id}); if ($#jobs >= 0){ $tmp_hash{jobs} = \@jobs; } #} $tmp_hash{resource_id} = $r; push(@data_to_display, \%tmp_hash); } my $data_to_dump = \@data_to_display; if (defined($stateMode)){ $data_to_dump = \%state_hash; } if (defined($DUMPER_mode)){ print(Dumper($data_to_dump)); }elsif(defined($XML_mode)){ if ($XML_enabled == 1){ my $dump = new XML::Dumper; $dump->dtd; print($dump->pl2xml($data_to_dump)); }else{ warn("XML module not available on the system. Ask your administrator to install it if needed.\n"); $exitstatus = 5; } }elsif(defined($YAML_mode)){ if ($YAML_enabled == 1){ print(YAML::Dump($data_to_dump)); }else{ warn("YAML module not available on the system. Ask your administrator to install it if needed.\n"); $exitstatus = 6; } }else{ foreach my $h (@data_to_display){ my %a = %{$h}; if (defined($stateMode)){ print("$a{resource_id} : $a{state}\n"); next; } print("resource_id : $a{resource_id}\n"); foreach my $b (keys(%a)){ if ($b eq "properties"){ my $str = "properties : "; foreach my $prop (sort(keys(%{$a{$b}}))){ if (OAR::Tools::check_resource_system_property($prop) == 0){ if (defined($a{$b}->{$prop})){ $str .= "$prop=$a{$b}->{$prop},"; }else{ $str .= "$prop=,"; } } } chop($str); print("$str\n"); }elsif ($b eq "jobs"){ print("job : @{$a{$b}}\n"); }elsif ($b eq "resource_id"){ next; }else{ print("$b : $a{$b}\n"); } } print("\n"); } } OAR::IO::disconnect($base); exit($exitstatus); } # Nodes list handling (set @nodes to what has been requested) my @nodes; if($ARGV[0]){ @nodes = @ARGV; }else{ @nodes = OAR::IO::list_nodes($base); } if (defined($stateMode)){ # Display the state of each resources my %data_to_display; foreach my $node ( @nodes ) { my @node_info = OAR::IO::get_node_info($base,$node); if ($#node_info >= 0){ my %tmp_data; foreach my $n (@node_info){ $tmp_data{$n->{resource_id}} = $n->{state}; } $data_to_display{$node} = \%tmp_data; }else{ $data_to_display{$node} = undef; } } if (defined($DUMPER_mode)){ print(Dumper(\%data_to_display)); }elsif(defined($XML_mode)){ if ($XML_enabled == 1){ my $dump = new XML::Dumper; $dump->dtd; print($dump->pl2xml(\%data_to_display)); }else{ warn("XML module not available on the system. Ask your administrator to install it if needed.\n"); $exitstatus = 5; } }elsif(defined($YAML_mode)){ if ($YAML_enabled == 1){ print(YAML::Dump(\%data_to_display)); }else{ warn("YAML module not available on the system. Ask your administrator to install it if needed.\n"); $exitstatus = 6; } }else{ foreach my $d (sort(keys(%data_to_display))){ print("$d\n"); foreach my $e (sort({$a <=> $b} keys(%{$data_to_display{$d}}))){ print(" $e : $data_to_display{$d}->{$e}\n"); } print("\n"); } } }elsif(defined($listall)){ # Simple list handling if (defined($DUMPER_mode)){ print(Dumper(\@nodes)); }elsif(defined($XML_mode)){ if ($XML_enabled == 1){ my $dump = new XML::Dumper; $dump->dtd; print($dump->pl2xml(\@nodes)); }else{ warn("XML module not available on the system. Ask your administrator to install it if needed.\n"); $exitstatus = 5; } }elsif(defined($YAML_mode)){ if ($YAML_enabled == 1){ print(YAML::Dump(\@nodes)); }else{ warn("YAML module not available on the system. Ask your administrator to install it if needed.\n"); $exitstatus = 6; } }else{ foreach my $node ( @nodes ) { print($node,"\n"); } } }else{ # Default display of oarnodes my %data_to_display; foreach my $node ( @nodes ) { my @node_info = OAR::IO::get_node_info($base,$node); if ($#node_info < 0) { warn("/!\\ $node UNKNOWN\n"); $exitstatus = 1; next; } my %tmp_hash1; foreach my $r (@node_info){ my %tmp_hash2; $tmp_hash2{state} = $r->{state}; #if ($r->{state} eq "Alive") { my @jobs = OAR::IO::get_resource_job($base,$r->{resource_id}); if ($#jobs >= 0){ $tmp_hash2{jobs} = \@jobs; } #} $tmp_hash2{network_address} = $r->{network_address}; my $properties = OAR::IO::get_resource_info($base,$r->{resource_id}); $tmp_hash2{properties} = $properties; $tmp_hash1{$r->{resource_id}} = \%tmp_hash2; } $data_to_display{$node} = \%tmp_hash1; } if (defined($DUMPER_mode)){ print(Dumper(\%data_to_display)); }elsif(defined($XML_mode)){ if ($XML_enabled == 1){ my $dump = new XML::Dumper; $dump->dtd; print($dump->pl2xml(\%data_to_display)); }else{ warn("XML module not available on the system. Ask your administrator to install it if needed.\n"); $exitstatus = 5; } }elsif(defined($YAML_mode)){ if ($YAML_enabled == 1){ print(YAML::Dump(\%data_to_display)); }else{ warn("YAML module not available on the system. Ask your administrator to install it if needed.\n"); $exitstatus = 6; } }elsif($Old){ my $Node_file_db_field_distinct_values = get_conf("NODE_FILE_DB_FIELD_DISTINCT_VALUES"); $Node_file_db_field_distinct_values = OAR::Tools::get_default_node_file_db_field_distinct_values() if (!defined($Node_file_db_field_distinct_values)); foreach my $aa (sort(keys(%data_to_display))){ my @res = keys(%{$data_to_display{$aa}}); my %tmp_already_there; my %tmp_already_there2; my $nb = 0; my $nb_jobs = 0; my $state; my $properties; my $hostname; my $job_str = ''; my $pass = 0; #my %weight_index; foreach my $bb (sort({$a <=> $b} keys(%{$data_to_display{$aa}}))){ my $cpuset; my @jobs; foreach my $cc (sort(keys(%{$data_to_display{$aa}->{$bb}}))){ if ($cc eq "properties"){ my $str = "properties = "; foreach my $prop (sort(keys(%{$data_to_display{$aa}->{$bb}->{$cc}}))){ if ($prop eq "cpuset") { $cpuset=$data_to_display{$aa}->{$bb}->{$cc}->{$prop};} if ($prop eq $Node_file_db_field_distinct_values) { if (!defined($tmp_already_there{$data_to_display{$aa}->{$bb}->{$cc}->{$prop}})){ $nb++; $tmp_already_there{$data_to_display{$aa}->{$bb}->{$cc}->{$prop}} = 1; } } $pass = 1 if (($prop eq "type") and ($data_to_display{$aa}->{$bb}->{$cc}->{$prop} eq "frontal")); if (defined($data_to_display{$aa}->{$bb}->{$cc}->{$prop})){ $str .= "$prop=$data_to_display{$aa}->{$bb}->{$cc}->{$prop},"; }else{ $str .= "$prop=,"; } } chop($str); $properties = $str; }elsif ($cc eq "jobs"){ #$nb_jobs += $#{$data_to_display{$aa}->{$bb}->{$cc}} + 1; my $c=0; foreach my $j (@{$data_to_display{$aa}->{$bb}->{$cc}}){ #if (!defined($weight_index{$aa})){ # $weight_index{$aa} = 0; #}else{ # $weight_index{$aa}++; #} #$job_str .= "$weight_index{$aa}/$j.oarserver,"; if (!defined($tmp_already_there2{$data_to_display{$aa}->{$bb}->{properties}->{$Node_file_db_field_distinct_values}})){ $tmp_already_there2{$data_to_display{$aa}->{$bb}->{properties}->{$Node_file_db_field_distinct_values}} = 1; $jobs[$c]="$j.oarserver"; $c++; $nb_jobs++; } } }elsif ($cc eq "network_address"){ $hostname = $data_to_display{$aa}->{$res[0]}->{$cc}; }elsif ($cc eq "state"){ if ($smp) { $state = $data_to_display{$aa}->{$bb}->{$cc}; }else{ $state = $data_to_display{$aa}->{$res[0]}->{$cc}; } } } # Construct the jobs string my $c=0; foreach my $j (@jobs) { if ($cpuset ne "" && !$smp) { $job_str .= "$cpuset/$j,"; }else{ $job_str .= "$c/$j,"; } $c++; } if ($smp) { next if ($hostname eq ""); next if ($pass == 1); printf("%s[%03d]\n",$hostname,$bb); print(" pcpus = 1\n"); chop($job_str) if (defined($job_str)); print(" jobs = $job_str\n"); print(" weight = $nb_jobs\n"); print(" hostname = $hostname\n"); $state = "free" if ($state eq "Alive"); $state = "job" if ($job_str ne ''); print(" state = $state\n"); print(" $properties\n"); print("\n"); $job_str = ""; } } if (!$smp) { next if ($hostname eq ""); next if ($pass == 1); print("$aa\n"); print(" pcpus = $nb\n"); chop($job_str) if (defined($job_str)); print(" jobs = $job_str\n"); print(" weight = $nb_jobs\n"); print(" hostname = $hostname\n"); $state = "free" if ($state eq "Alive"); $state = "job" if ($nb == $nb_jobs); print(" state = $state\n"); print(" $properties\n"); print("\n"); } } }else{ foreach my $aa (sort(keys(%data_to_display))){ print("$aa\n"); foreach my $bb (sort({$a <=> $b} keys(%{$data_to_display{$aa}}))){ print(" $bb\n"); foreach my $cc (sort(keys(%{$data_to_display{$aa}->{$bb}}))){ if ($cc eq "properties"){ my $str = "properties : "; foreach my $prop (sort(keys(%{$data_to_display{$aa}->{$bb}->{$cc}}))){ if (OAR::Tools::check_resource_system_property($prop) == 0){ if (defined($data_to_display{$aa}->{$bb}->{$cc}->{$prop})){ $str .= "$prop=$data_to_display{$aa}->{$bb}->{$cc}->{$prop},"; }else{ $str .= "$prop=,"; } } } chop($str); print(" $str\n"); }elsif ($cc eq "jobs"){ print(" jobs : @{$data_to_display{$aa}->{$bb}->{$cc}}\n"); }else{ print(" $cc : $data_to_display{$aa}->{$bb}->{$cc}\n"); } } print("\n"); } } } } OAR::IO::disconnect($base); exit($exitstatus); ./oar-2.5.2/sources/core/qfunctions/oarnodes0000755000175000017500000001743311757171206017135 0ustar plbplb#!/usr/bin/perl # $Id$ # print OAR node properties # # EXAMPLES: # oarnodes -l # => returns the complete list without information - status = 0 # oarnodes -s # => returns only the state of nodes - status = 0 # oarnodes -h|--help # => returns a help message - status = 0 # oarnodes host1 [.. hostn] # => returns the information for hostX - status is 0 for every host known - 1 otherwise # use strict; use warnings; use Data::Dumper; use Getopt::Long; use OAR::Nodes; use OAR::Tools; ### CONFIG STUFF ### Getopt::Long::Configure ("gnu_getopt"); # suitable Data::Dumper configuration for serialization $Data::Dumper::Purity = 1; $Data::Dumper::Terse = 1; $Data::Dumper::Indent = 0; $Data::Dumper::Deepcopy = 1; ### END CONFIG STUFF ### ### Variables declaration ### my $stateMode; my $usage; my $listall; my $sql_property; my $XML_mode; my $YAML_mode; my $JSON_mode; my $DUMPER_mode; my $Version; my $events; my @resources; my @nodes; ### END Variables declaration ### ### Print Methods ### sub print_usage(){ print <{'date'}), $current_event->{'job_id'}, $current_event->{'type'}, $current_event->{'description'} ); } } } format_result(\%result) if $dump_mode; } sub add_sql_resources($){ my $sql_clause = shift; my $sql_resources = OAR::Nodes::get_resources_with_given_sql($sql_clause); push @resources, @$sql_resources; } sub format_result($){ my $result = shift; if (defined($DUMPER_mode)){ print(Dumper($result)."\n"); }elsif(defined($XML_mode)){ print(OAR::Nodes::encode_result($result, "XML")."\n"); }elsif(defined($YAML_mode)){ print(OAR::Nodes::encode_result($result, "YAML")."\n"); }elsif(defined($JSON_mode)){ print(OAR::Nodes::encode_result($result, "JSON")."\n"); } } sub print_resources_states_for_hosts($){ my $nodes = shift; my %result; my $dump_mode=0; $dump_mode=1 if (defined($DUMPER_mode) || defined($XML_mode) || defined($YAML_mode) || defined($JSON_mode)); foreach my $current_host (@$nodes){ $result{$current_host}=OAR::Nodes::get_resources_states_for_host($current_host); if (!$dump_mode) { print "$current_host\n"; foreach my $k (sort keys %{$result{$current_host}} ) { #while ( my ($k,$v) = each %{$result{$current_host}} ){ print " $k : $result{$current_host}{$k}\n"; } } } format_result(\%result) if $dump_mode; } sub print_resources_states($){ my $resources = shift; my $resources_states = OAR::Nodes::get_resources_states($resources); if (defined($DUMPER_mode) || defined($XML_mode) || defined($YAML_mode) || defined($JSON_mode)) { format_result($resources_states); } else { while ( my ($k,$v) = each %$resources_states ){ print "$k : $v\n"; } } } sub print_all_hostnames(){ my $nodes_to_print = OAR::Nodes::get_all_hosts(); foreach my $current_node (@$nodes_to_print) { print "$current_node\n"; } } sub print_hosts_infos($){ my $nodes = shift; my $infos; foreach my $current_node (@$nodes){ my $infos_for_host = OAR::Nodes::get_resources_infos_for_host($current_node); while ( my ($id,$info) = each %$infos_for_host ){ $infos->{$id} = $info; # modify here to add depth (sorted by network_address) } } if (defined($DUMPER_mode) || defined($XML_mode) || defined($YAML_mode) || defined($JSON_mode)){ format_result($infos); } else{ print_resources_flat_way($infos); } } sub print_resources_infos($){ my $resources = shift; my $res_infos = OAR::Nodes::get_resources_infos($resources); if (defined($DUMPER_mode) || defined($XML_mode) || defined($YAML_mode) || defined($JSON_mode)){ format_result($res_infos); } else{ print_resources_flat_way($res_infos); } } # INFO: function to change if you want to change the user std output sub print_resources_flat_way($){ my $resources_info = shift; foreach my $id ( sort keys %$resources_info ){ my $info = $resources_info->{$id}; print "network_address : $info->{network_address}\n"; print "resource_id : $info->{resource_id}\n"; if ($info->{state} eq "Absent" && $info->{available_upto} >= time()) { $info->{state} .= " (standby)"; } print "state : $info->{state}\n"; if (exists($info->{jobs})){print "jobs: $info->{jobs}\n";} my $properties_to_display=''; while ( my ($k,$v) = each %$info ){ if (OAR::Tools::check_resource_system_property($k) == 0){ if(defined($v)){ $properties_to_display .= "$k=$v, "; }else{ $properties_to_display .= "$k=, "; } } } chop($properties_to_display); # remove last space chop($properties_to_display); # remove last , print "properties : $properties_to_display\n\n"; } } ### END Print Methods ### ### Main ### # parse command line option GetOptions ("state|s" => \$stateMode, "help|h" => \$usage, "list|l" => \$listall, "events|e:s" => \$events, "resource|r=i" => \@resources, "sql=s" => \$sql_property, "xml|X" => \$XML_mode, "yaml|Y" => \$YAML_mode, "json|J" => \$JSON_mode, "dumper|D" => \$DUMPER_mode, "version|V" => \$Version ); if ($usage){ print_usage(); exit(0); } if ($Version){ print_oar_version(); exit(0); } OAR::Nodes::open_db_connection() or die "DB connection error, exiting.\n"; # Nodes list handling (set @nodes to what has been requested) if($ARGV[0]){ @nodes = @ARGV; }else{ my $nodes_tmp = OAR::Nodes::get_all_hosts(); @nodes = @$nodes_tmp; } # SQL where handling, add resources that match sql clause to @resources if (defined($sql_property)) { add_sql_resources($sql_property); } if (defined($events)) { print_events($events, \@nodes); } elsif ($stateMode){ if ($#resources >= 0){ print_resources_states(\@resources); } else{ print_resources_states_for_hosts(\@nodes); } } elsif ($listall){ print_all_hostnames(); } elsif ($#resources >= 0 || defined($sql_property)){ # -r or --sql where called print_resources_infos(\@resources); } elsif ($#nodes >= 0){ # a hostname was given or not (in this case all hostnames) print_hosts_infos(\@nodes); } else{ print "\/!\\ No nodes to display...\n"; } OAR::Nodes::close_db_connection(); ### END Main ### ./oar-2.5.2/sources/core/qfunctions/oarmonitor0000755000175000017500000001666111757171206017516 0ustar plbplb#!/usr/bin/perl # $Id: oarmonitor 598 2007-07-05 08:13:30Z neyron $ # Feed the monitoring tables use strict; use warnings; use Data::Dumper; use OAR::Conf qw(init_conf dump_conf get_conf is_conf); use Getopt::Long; use OAR::Version; use OAR::Tools; use OAR::IO; use Fcntl; use Storable qw(dclone); use Sys::Hostname; my $Old_umask = sprintf("%lo",umask()); umask(oct("022")); init_conf($ENV{OARCONFFILE}); my $Monitor_file = get_conf("OARMONITOR_SENSOR_FILE"); $Monitor_file = OAR::Tools::get_default_monitor_sensor_file() if (!defined($Monitor_file)); $Monitor_file = "$ENV{OARDIR}/$Monitor_file" if ($Monitor_file !~ /^\//); my $Taktuk_cmd = get_conf("TAKTUK_CMD"); if (!defined($Taktuk_cmd)){ warn("ERROR: TAKTUK_CMD must be defined in $ENV{OARCONFFILE}.\n"); exit(1); } my $Openssh_cmd = get_conf("OPENSSH_CMD"); $Openssh_cmd = OAR::Tools::get_default_openssh_cmd() if (!defined($Openssh_cmd)); my $Cpuset_path = get_conf("CPUSET_PATH"); if (!defined($Cpuset_path) or (! is_conf("JOB_RESOURCE_MANAGER_PROPERTY_DB_FIELD"))){ warn("The CPUSET feature must be configured to use this command\n"); exit(6); } Getopt::Long::Configure("gnu_getopt"); my $Version; my $Job_id; my $Frequency = 60; my $Default_frequency = $Frequency; my $Frequency_min = 1; my $Timeout = 120; my $sos; my $Verbose; GetOptions ("version|V" => \$Version, "job_id|j=i" => \$Job_id, "frequency|f=i" => \$Frequency, "help|h" => \$sos, "verbose|v" => \$Verbose ); # Display command help sub usage { print <& tak_stdout_write"); # Use the child STDIN to send the user command close(tak_stdin_write); close(STDIN); open(STDIN, "<& tak_stdin_read"); exec($cmd); warn("[ERROR] Cannot execute $cmd\n"); exit(-1); } close(tak_node_read); close(tak_stdin_read); close(tak_stdout_write); # Send node list my $hosts_hash; foreach my $n (@hosts){ $hosts_hash->{$n} = 1; print(tak_node_write "$n\n"); } close(tak_node_write); my $oldfh; $oldfh = select(tak_stdin_write); $| = 1; select($oldfh); $oldfh = select(tak_stdout_read); $| = 1; select($oldfh); ############################################################################### # Send sensor perl script to all nodes if (open(SENSOR, $Monitor_file)){ while(){ print(tak_stdin_write $_); } close(SENSOR); print(tak_stdin_write "\n__END__\n"); }else{ warn("Cannot read the monitor sensor file $Monitor_file\n"); exit(3); } # Signals management sub exit_monitor(){ print(tak_stdin_write "STOP\n"); } $SIG{TERM} = \&exit_monitor; $SIG{INT} = \&exit_monitor; $SIG{QUIT} = \&exit_monitor; $SIG{USR2} = sub {return}; # Node outputs parsing my $monitor_process_id = $Job_id.'_'.hostname().'_'.time().'_'.$$; my $tmp_hosts_hash; my $nb_end_nodes = $#hosts + 1; my $stop = 0; my $tic_time = 0; my $tic_time_prev = $tic_time; while (($stop == 0) and ($nb_end_nodes > $#hosts)){ my $sleep_time = $Frequency - (OAR::IO::get_date($base) - $tic_time); if ($sleep_time > 0){ OAR::IO::disconnect($base); sleep($sleep_time); $base = OAR::IO::connect(); } $tic_time_prev = $tic_time; $tic_time = OAR::IO::get_date($base); $tic_time = $tic_time_prev if ($tic_time < $tic_time_prev); eval{ $SIG{ALRM} = sub { die "alarm\n" }; alarm($Frequency + $Timeout); # Ask every nodes to print their monitoring values print(tak_stdin_write "monitor_process_id=$monitor_process_id window_start=$tic_time_prev window_stop=$tic_time\n"); # Check the taktuk STDOUT $tmp_hosts_hash = dclone($hosts_hash); $nb_end_nodes = 0; while (($nb_end_nodes <= $#hosts) and ($stop == 0) and ($_ = )){ chop; if ($_ =~ /^OUTPUT\s+([\w\.\-\d]+)\s+END$/){ delete($tmp_hosts_hash->{$1}) if (defined($tmp_hosts_hash->{$1})); $nb_end_nodes++; }elsif ($_ =~ /^OUTPUT\s+([\w\.\-\d]+)\s+(\w+)\s*(.*)$/){ if (($2 eq "STOP") or ($2 eq "STOP_REQUESTED") or ($2 eq "ERROR")){ warn(" $1 $2\n"); $stop++; }else{ # Sensor gives the DB table name ($2) and the values for # each fields separated by spaces ($3) if ($tic_time_prev > 0){ my @fields; my @values; my @tmp = split(" ",$3); foreach my $i (@tmp){ my ($field,$value) = split('=',$i); if ((defined($field)) and (defined($value))){ push(@fields, $field); push(@values, $value); } } if (defined($Verbose)){ print("@fields\n"); print("@values\n"); } if (OAR::IO::register_monitoring_values($base,$2,\@fields,\@values) > 0){ warn("[ERROR] Cannot register in the database: @fields, @values\n"); $stop++; } } } }else{ warn("[TAKTUK] $_\n"); } } alarm(0); }; } close(tak_stdin_write); close(tak_stdout_read); if ($stop > 0){ warn("At least one sensor stopped to retrieve data.\n"); exit(0); } my @bads = keys(%{$tmp_hosts_hash}); if ($#bads >= 0){ warn("Some nodes timeouted: @bads\n"); exit(5); } ./oar-2.5.2/sources/core/qfunctions/oarhold0000755000175000017500000001023611757171206016745 0ustar plbplb#!/usr/bin/perl # $Id$ #Hold a job --> it will not be scheduled use strict; use warnings; use Data::Dumper; use DBI(); use OAR::IO; use OAR::Conf qw(init_conf dump_conf get_conf is_conf); use OAR::Tools; use Getopt::Long; use OAR::Version; my $Old_umask = sprintf("%lo",umask()); umask(oct("022")); sub usage { print < \$Help, "version|V" => \$Version, "array" => \$array, "sql=s" => \$Sql_property, "running|r" => \$Suspend_running ); if (defined($Help)){ usage(); exit(0); } if (defined($Version)){ print("OAR version : ".OAR::Version::get_version()."\n"); exit(0); } init_conf($ENV{OARCONFFILE}); my $remote_host = get_conf("SERVER_HOSTNAME"); my $remote_port = get_conf("SERVER_PORT"); my $Cpuset_field = get_conf("JOB_RESOURCE_MANAGER_PROPERTY_DB_FIELD"); if ((@ARGV < 1) and (!defined($Sql_property))){ usage(); exit(1); } my @job_ids; my $exit_code = 0; foreach my $j (@ARGV){ if ($j =~ m/^\d+$/m){ #if defined --array, delete all the sub-jobs if(defined($array)){ my $db = OAR::IO::connect_ro(); my @tmp_jobs = OAR::IO::get_array_job_ids($db,$j); if(scalar @tmp_jobs == 0){ warn("[ERROR] \"$j\" is not a valid array job\n"); $exit_code = 2; }else{ foreach my $j (@tmp_jobs){ push(@job_ids, $j); } } OAR::IO::disconnect($db); }else{ push(@job_ids, $j); } }else{ if(defined($array)){ warn("[ERROR] \"$j\" is not a valid job array identifier\n"); }else{ warn("[ERROR] \"$j\" is not a valid job identifier\n"); } $exit_code = 2; } } if (defined($Sql_property)){ my $db = OAR::IO::connect_ro(); foreach my $j (OAR::IO::get_jobs_with_given_properties($db,$Sql_property)){ push(@job_ids, $j->{job_id}); } OAR::IO::disconnect($db); } my $base = OAR::IO::connect(); foreach my $j (@job_ids){ my $err_job = 0; if (defined($Suspend_running)){ my $types = OAR::IO::get_current_job_types($base,$j); if (!defined($Cpuset_field)){ warn("CPUSET tag is not configured in the oar.conf.\n"); $err_job = 2; }elsif (defined($types->{cosystem})){ warn("This job is of the cosystem type. We cannot suspend this kind of jobs.\n"); $err_job = 3; }elsif (defined($types->{deploy})){ warn("This job is of the deploy type. We cannot suspend this kind of jobs.\n"); $err_job = 4; } } if ($err_job == 0){ my $err = OAR::IO::hold_job($base,$j,$Suspend_running); if ($err != 0) { my $str = "/!\\ Cannot hold $j :"; if ($err == -1){ warn("$str this job does not exist.\n"); }elsif ($err == -2){ warn("$str you are not the right user.\n"); }elsif ($err == -3){ warn("$str the job is not in the right state (try '-r' option).\n"); }elsif ($err == -4){ warn("$str only oar or root users can use '-r' option.\n"); }else{ warn("$str unknown reason.\n"); } $exit_code = 1; }else{ print("[$j] Hold request was sent to the OAR server.\n"); } }else{ $exit_code = 2; } } OAR::IO::disconnect($base); #Signal Almigthy OAR::Tools::notify_tcp_socket($remote_host,$remote_port,"ChState"); exit($exit_code); ./oar-2.5.2/sources/core/qfunctions/oardel0000755000175000017500000002735611757171206016576 0ustar plbplb#!/usr/bin/perl # $Id$ # delete a job use strict; use warnings; use Data::Dumper; use DBI(); use OAR::IO; #use IO::Socket::INET; use OAR::Conf qw(init_conf dump_conf get_conf is_conf); use OAR::Tools; use Getopt::Long; use OAR::Version; my $Old_umask = sprintf("%lo",umask()); umask(oct("022")); my $exitValue = 0; # Display command help sub usage { print < \$Checkpoint, "signal|s:s" => \$signal, "besteffort|b" => \$Besteffort, "help|h" => \$sos, "array" => \$array, "sql=s" => \$Sql_property, "version|V" => \$Version ); if (defined($sos)){ usage(); exit(0); } if (defined($Version)){ print("OAR version : ".OAR::Version::get_version()."\n"); exit(0); } if (($#ARGV < 0) and (!defined($Sql_property))){ usage(); exit(1); } my @job_ids; foreach my $j (@ARGV){ if ($j =~ m/^\d+$/m){ #if defined --array, delete all the sub-jobs if(defined($array)){ my $db = OAR::IO::connect_ro(); my @tmp_jobs = OAR::IO::get_array_job_ids($db,$j); if(scalar @tmp_jobs == 0){ warn("[ERROR] \"$j\" is not a valid array job\n"); $exitValue = 4; }else{ foreach my $j (@tmp_jobs){ push(@job_ids, $j); } } OAR::IO::disconnect($db); }else{ push(@job_ids, $j); } }else{ if(defined($array)){ warn("[ERROR] \"$j\" is not a valid job array identifier\n"); }else{ warn("[ERROR] \"$j\" is not a valid job identifier\n"); } $exitValue = 4; } } if (defined($Sql_property)){ my $db = OAR::IO::connect_ro(); foreach my $j (OAR::IO::get_jobs_with_given_properties($db,$Sql_property)){ push(@job_ids, $j->{job_id}); } OAR::IO::disconnect($db); } my $base = OAR::IO::connect(); # oardel is used to checkpoint some jobs if (defined($Checkpoint)){ foreach my $idJob (@job_ids){ print("Checkpointing the job $idJob ..."); # Try to insert checkpoint information in the database my $err = OAR::IO::ask_checkpoint_job($base,$idJob); if ($err > 0) { print("ERROR.\n"); if ($err == 1){ warn("Cannot checkpoint $idJob ; You are not the right user.\n"); $exitValue = 1; }elsif ($err == 3){ warn("Cannot checkpoint $idJob ; The job is Interactive.\n"); $exitValue = 7; }else{ warn("Cannot checkpoint $idJob ; This job is not running.\n"); $exitValue = 5; } }else{ my $strComment; # Retrieve node names used by the job my @hosts = OAR::IO::get_job_current_hostnames($base,$idJob); my $types = OAR::IO::get_current_job_types($base,$idJob); my $host_to_connect = $hosts[0]; if ((defined($types->{cosystem})) or ($#hosts < 0)){ $host_to_connect = $Cosystem_hostname; }elsif (defined($types->{deploy})){ $host_to_connect = $Deploy_hostname; } my $timeoutSSH = OAR::Tools::get_ssh_timeout(); # Timeout the ssh command eval { $SIG{ALRM} = sub { die "alarm\n" }; alarm($timeoutSSH); OAR::Tools::signal_oarexec($host_to_connect,$idJob,"SIGUSR2",1, $base, $Openssh_cmd, ''); alarm(0); }; if ($@){ print("ERROR.\n"); if ($@ eq "alarm\n"){ $exitValue = 3; $strComment = "Cannot contact $host_to_connect, operation timouted ($timeoutSSH s)."; warn("$strComment\n"); OAR::IO::add_new_event($base,"CHECKPOINT_ERROR",$idJob,$strComment); }else{ $strComment = "An unknown error occured."; warn("$strComment\n"); OAR::IO::add_new_event($base,"CHECKPOINT_ERROR",$idJob,$strComment); } }else{ print("DONE.\n"); $strComment = "The job $idJob was notified to checkpoint itself on $host_to_connect."; print("$strComment\n"); OAR::IO::add_new_event($base,"CHECKPOINT_SUCCESS",$idJob,$strComment); } } } }elsif (defined($signal)){ foreach my $idJob (@job_ids){ print("Signaling the job $idJob with $signal signal.\n"); # Try to insert signal information in the database my $err = OAR::IO::ask_signal_job($base,$idJob,$signal); if ($err > 0) { print("ERROR.\n"); if ($err == 1){ warn("Cannot signal $idJob ; You are not the right user.\n"); $exitValue = 1; }elsif ($err == 3){ warn("Cannot signal $idJob ; The job is Interactive.\n"); $exitValue = 7; }else{ warn("Cannot signal $idJob ; This job is not running.\n"); $exitValue = 5; } }else{ my $strComment; # Retrieve node names used by the job my @hosts = OAR::IO::get_job_current_hostnames($base,$idJob); my $types = OAR::IO::get_current_job_types($base,$idJob); my $host_to_connect = $hosts[0]; if ((defined($types->{cosystem})) or ($#hosts < 0)){ $host_to_connect = $Cosystem_hostname; }elsif (defined($types->{deploy})){ $host_to_connect = $Deploy_hostname; } my $timeoutSSH = OAR::Tools::get_ssh_timeout(); # Timeout the ssh command eval { $SIG{ALRM} = sub { die "alarm\n" }; alarm($timeoutSSH); OAR::Tools::signal_oarexec($host_to_connect,$idJob,"SIGURG",1, $base, $Openssh_cmd, $signal); alarm(0); }; if ($@){ print("ERROR.\n"); if ($@ eq "alarm\n"){ $exitValue = 3; $strComment = "Cannot contact $host_to_connect, operation timouted ($timeoutSSH s)."; warn("$strComment\n"); OAR::IO::add_new_event($base,"SIG_ERROR",$idJob,$strComment); }else{ $strComment = "An unknown error occured."; warn("$strComment\n"); OAR::IO::add_new_event($base,"SIG_ERROR",$idJob,$strComment); } }else{ print("DONE.\n"); $strComment = "The job $idJob was notified to signal itself with $signal on $host_to_connect."; print("$strComment\n"); OAR::IO::add_new_event($base,"SIG_SUCCESS",$idJob,$strComment); } } } }elsif (defined($Besteffort)){ my $lusr= $ENV{OARDO_USER}; if (($lusr ne "oar") and ($lusr ne "root")){ $exitValue = 8; warn("You must be oar or root to use the -b option\n"); }else{ foreach my $j (@job_ids){ OAR::IO::lock_table($base,["jobs","job_types","resources","assigned_resources","event_logs"]); my $job = OAR::IO::get_job($base, $j); if (defined($job->{state}) and ($job->{state} eq "Running")){ my $types = OAR::IO::get_current_job_types($base,$j); if (defined($types->{besteffort})){ OAR::IO::update_current_scheduler_priority($base,$job->{job_id},$job->{assigned_moldable_job},"-2","STOP"); OAR::IO::remove_current_job_types($base,$job->{job_id},"besteffort"); OAR::IO::add_new_event($base,"DELETE_BESTEFFORT_JOB_TYPE",$job->{job_id},"[oardel] User $lusr removed the besteffort type."); print("Remove besteffort type for the job $j.\n"); }else{ OAR::IO::add_current_job_types($base,$job->{job_id},"besteffort"); OAR::IO::update_current_scheduler_priority($base,$job->{job_id},$job->{assigned_moldable_job},"+2","START"); OAR::IO::add_new_event($base,"ADD_BESTEFFORT_JOB_TYPE",$job->{job_id},"[oardel] User $lusr added the besteffort type."); print("Add besteffort type for the job $j.\n"); } }else{ $exitValue = 9; warn("The job $j is not in the Running state.\n"); } OAR::IO::unlock_table($base); } OAR::IO::disconnect($base); my $strError = OAR::Tools::notify_tcp_socket($remote_host,$remote_port,"Term"); if (defined($strError)){ warn("$strError\n"); $exitValue = 2; } } }else{ # oardel is used to delete some jobs my @jobRegistered; foreach my $idJob (@job_ids) { print("Deleting the job = $idJob ..."); # Try to insert delete informations in the database OAR::IO::lock_table($base,["frag_jobs","event_logs","jobs"]); my $err = OAR::IO::frag_job($base,$idJob); OAR::IO::unlock_table($base); if ($err == -1) { print("ERROR.\n"); warn("Cannot frag $idJob ; You are not the right user.\n"); $exitValue = 1; }elsif ($err == -2){ print("ERROR.\n"); warn("Cannot frag $idJob ; This job was already killed.\n"); $exitValue = 6; }else{ print("REGISTERED.\n"); push(@jobRegistered,$idJob); } } OAR::IO::disconnect($base); if (@jobRegistered){ #Signal Almigthy my $strError = OAR::Tools::notify_tcp_socket($remote_host,$remote_port,"ChState"); $strError = OAR::Tools::notify_tcp_socket($remote_host,$remote_port,"Qdel") if (!defined($strError)); if (defined($strError)){ warn("$strError\n"); $exitValue = 2; }else{ print("The job(s) [ @jobRegistered ] will be deleted in a near future.\n"); } } } exit($exitValue); ./oar-2.5.2/sources/core/qfunctions/oarconnect0000755000175000017500000000016011757171206017443 0ustar plbplb#!/bin/bash # $Id$ # Simple wrapper to oarsub -C JOBID=$1 shift ${0/%oarconnect/oarsub} -C $JOBID "$@" ./oar-2.5.2/sources/core/qfunctions/oaradmin/0000755000175000017500000000000011757171206017157 5ustar plbplb./oar-2.5.2/sources/core/qfunctions/oaradmin/oaradmin_modules.rb0000755000175000017500000014355711757171206023050 0ustar plbplb#!/usr/bin/ruby # $Id: oaradmin_modules.rb 1 2008-05-06 16:00:00 ddepoisi $ # Modules, classes and other definitions for oaradmin utility # # requirements: # ruby1.8 (or greater) # # To activate the verbose mode, add -w at the end of the first line. Ex : #!/usr/bin/ruby -w # require 'fileutils' require 'rexml/document' require 'time' ########################### # DEFINITIONS FOR RESOURCES ########################### module Resources # Test syntax in command line # Return : # 0 : no error # 1 : one parameter is wrong # 2 : a parameter after an option is missing # 3 : one error occurs in {...} expression with -a or -s option. # only a numeric value with optional numeric format and offset are allowed # 4 : one error occurs in {...} expression with -p option # only % character with optional numeric format and offset are allowed # 5 : {...} expression not allowed with -d option def Resources.parsing r=0 i=0 while i 0 r = 3 return r end end end end end i+=1 when ARGV[i] == "-p" || ARGV[i] == "--property" if !(ARGV[i+1] =~ /\S+=\S+/) r=1 return r else r = Resources.parsing_value(ARGV[i+1].split('=')[1], "-p") if r > 0 r = 4 return r end end i+=1 when ARGV[i] == "-s" || ARGV[i] == "--select" if !(ARGV[i+1] =~ /^\w\S*=\S+/) r=1 return r else r = Resources.parsing_value(ARGV[i+1].split('=')[1], "-s") if r > 0 r = 3 return r end end i+=1 when ARGV[i] == "-d" || ARGV[i] == "--delete" if ARGV[i+1] && ARGV[i+1] != "-c" && ARGV[i+1] != "--commit" if !(ARGV[i+1] =~ /^\w\S*=\S+/) r=1 return r else if ARGV[i+1] =~ /\{.*\}/ r=5 return r end end end i+=1 when ARGV[i] == "-c" || ARGV[i] == "--commit" # Nothing to do when ARGV[i] =~ /^\-\-\S+=\S+$/ # Nothing to do else r=1 return r end # case i+=1 end # while i property_name = switch # property_fixed_value : the fixed part of the property value. Ex : /switch=sw{3} => property_fixed_value = sw # property_fixed_value2 : the second part of the property value. Ex : /nodes=host{12}.domain => property_fixed_value2 = .domain # property_nb : the number of elements in the hierarchy. Ex : /switch=sw{3} => property_nb = 3 # property_ndx : current index for increments # index : position to store values in $cmd_user[] # Ex : -a /switch=sw{3} => $cmd_user[0] = {:property_name => "switch", :property_fixed_value="sw", property_fixed_value2="", :property_nb=3} # -a /nodes=host{12}.domain => $cmd_user[0] = {:property_name => "nodes", :property_fixed_value="host", :property_fixed_value2=".domain", :property_nb=12} # -a /nodes=host-[1-12,18],host_b # => $cmd_user[0] = {:property_name => "nodes", :property_fixed_value="", :property_fixed_value2="" ,:property_nb="host-[1-12,18].domain,host_b.domain"} # -p infiniband=NO => $cmd_user[n] = {:property_name => "infiniband", :property_fixed_value="", :property_fixed_value2="", :property_nb="NO"} # -a /nodes=host{12+40offset} => $cmd_user[0] = {:property_name => "nodes" .../... :offset=>40} to create host41, host42, host43 .../... host52 # -a /nodes=host{%3d12} => $cmd_user[0] = {:property_name => "nodes" .../... :format_num => "%03d"} to create host001, host002... # We can use /nodes or /node. Ex : -a /node=mycluster[1-10] def Resources.decompose_argv (0..ARGV.length-1).each do |i| if ARGV[i] == "-a" || ARGV[i] == "--add" ARGV[i+1].split('/').each do |item| if item != "" property_name, property_fixed_value, property_fixed_value2, property_nb, format_num, offset = Resources.decompose_param(ARGV[i], item) if $options[:auto_offset] case property_name when "node" cluster_uid = property_fixed_value.split("-").first row = Bdd.connection.select_one("SELECT network_address FROM resources WHERE network_address like \"#{cluster_uid}-%\" ORDER BY resource_id DESC LIMIT 0,1") offset = row["network_address"].split(".")[0].split("-")[1].to_i unless row.nil? when "cpu" row = Bdd.connection.select_one("SELECT MAX(cpu) as max_cpu FROM resources") offset = row["max_cpu"] unless row.nil? when "core" row = Bdd.connection.select_one("SELECT MAX(core) as max_core FROM resources") offset = row["max_core"] unless row.nil? end end $cmd_user.push({:property_name => property_name, :property_fixed_value => property_fixed_value, :property_fixed_value2 => property_fixed_value2, :property_ndx => 0, :property_nb => property_nb, :offset => offset, :format_num => format_num }) end end end if ARGV[i] == "-s" || ARGV[i] == "--select" property_name, property_fixed_value, property_fixed_value2, property_nb, format_num, offset = Resources.decompose_param(ARGV[i], ARGV[i+1]) $cmd_user = $cmd_user.insert(0, {:property_name => property_name, :property_fixed_value => property_fixed_value, :property_fixed_value2 => property_fixed_value2, :property_ndx => 0, :property_nb => property_nb, :offset => offset, :format_num => format_num }) end if ARGV[i] == "-p" || ARGV[i] == "--property" property_name, property_fixed_value, property_fixed_value2, property_nb, format_num, offset = Resources.decompose_param(ARGV[i], ARGV[i+1]) $cmd_user.push({:property_name => property_name, :property_fixed_value => property_fixed_value, :property_fixed_value2 => property_fixed_value2, :property_ndx => 0, :property_nb => property_nb, :offset => offset, :format_num => format_num }) end if ARGV[i] == "-d" || ARGV[i] == "--delete" property_name = property_fixed_value = property_fixed_value2 = property_nb = "" if ARGV[i+1] =~ /=/ property_name = $` property_nb = $' $cmd_user.push({:property_name => property_name, :property_fixed_value => property_fixed_value, :property_fixed_value2 => property_fixed_value2, :property_ndx => 0, :property_nb => property_nb}) end end end p $cmd_user if $VERBOSE end # decompose_argv # Decompose parameters property=value - retrieve offset and numeric format def Resources.decompose_param(form, str) property_name = property_fixed_value = property_fixed_value2 = property_nb = format_num = str2 = "" offset=0 if str =~ /=/ property_name = $` property_fixed_value = val_tmp = $' if val_tmp =~ /\{.*\}/ # case with follows forms : # - with a number and/or numeric format and/or offset : param={5} param=part_a{12}part_b param=part_a{%2d+20offset12} # - with % as increment operator and/or numeric format and/or offset : param={%} param=part_a{%}part_b param=part_a{%2d%+20offset} # numeric format and offset can be anywhere in {...} property_fixed_value = $` property_fixed_value2 = $' str2 = $&[1..$&.length-2] if str2 =~ /(\+|-)\d*(offset)*/ # offset : +50offset -1offset offset = $&.to_i str2 = $` + $' end if str2 =~ /%\d*d/ # numeric format format_num = $& format_num = format_num[0..0] + "0" + format_num[1..format_num.length] str2 = $` + $' end property_nb = str2.to_i if form=="-a" || form=="--add" || form=="-s" || form=="--select" # Only {number} form allowed with -a and -s params property_nb = 1 if form=="-p" || form=="--property" # Only {%} form allowed with -p params else # case with follows forms : # - param=host1,host[1-5,18] # - param=host1,host[%2d1-5,18] property_fixed_value="" property_nb = val_tmp end end property_nb = "\"#{property_nb}\"" if property_nb.kind_of?(String) && property_nb =~ /\s/ return property_name, property_fixed_value, property_fixed_value2, property_nb, format_num, offset end # decompose_param # Decompose an expression of type host_a,host-[1-12,18,24-30],host_b,host_c in a table where each element is a value # Return a table with for example : ["host_a","host-1","host-2","host-3"..."host-18","host-24",..."host_b","host_c"] def Resources.decompose_list_values(str) t = [] # numeric format format_num = "" if str =~ /%\d*d/ format_num = $& format_num = format_num[0..0] + "0" + format_num[1..format_num.length] str = $` + $' end j = 0 str1 = "" while j <= str.length-1 c = str[j..j] case when c == "," if str1.length > 0 then t.push(str1) str1 = "" end j+=1 when c == "[" k = str[j..str.length-1] =~ /\]/ str3=$' if str3 =~ /,/ str3=$` end str2 = str[j+1,k-1] str2.split(',').each do |item| if item =~ /-/ val_inf = $`.to_i val_sup = $'.to_i (val_inf..val_sup).each do |val_tmp| v = val_tmp v = sprintf("#{format_num}", v) if format_num.length > 0 t.push(str1 + v.to_s + str3) end else v = item.to_i v = sprintf("#{format_num}", v) if format_num.length > 0 t.push(str1 + v.to_s + str3) end end j+=k+1+str3.length str1="" else str1 += c j+=1 end end t.push(str1) if str1 != "" return t end # Resources.decompose_list_values # Test if properties specified in command line exist in OAR database. Use "oarproperty -l" command # Test done only with -c option. # If one property does not exist, display error message and exit def Resources.properties_exists properties = nil prop_command_line=[] # properties from command line if $options[:commit] # retrieve all properties from command line ARGV.each do |i| if i =~ /\// i.split('/').each do |j| prop_command_line.push($`) if j =~ /=/ end elsif i =~ /=/ prop_command_line.push($`) end end # Test also cpuset property name if defined by user prop_command_line.push($options[:cpusetproperty_name]) if $options[:cpusetproperty_name] != "" # "nodes" or "node" are keywords for oaradmin prop_command_line.delete_if {|x| x == "nodes" || x == "node" } r1 = `oarproperty -l` r2 = $?.exitstatus if r2 > 0 $stderr.puts "[OARADMIN ERROR]: can't execute oarproperty -l command." exit(2) else properties = prop_command_line - r1.split("\n") if !properties.empty? $stderr.print "[OARADMIN ERROR]: One or more properties does not exist : " $stderr.print properties.join(", ") + " ! \n" $stderr.print "[OARADMIN ERROR]: Please, use oarproperty command before.\n" exit(3) end end end end # Resources.properties_exists # Explore $cmd_user[] table and create oar commands - recursiv algorithm def Resources.tree n, str , resources_hash # n : the current level # str : string contains the oar command to execute # resources : array of resources (for YAML export) property="" value="" value2="" if n <= $cmd_user.length # Create oarnodesetting command with correct syntax if $cmd_user[n-1][:property_name] == "nodes" || $cmd_user[n-1][:property_name] == "node" hostname=$cmd_user[n-1][:property_fixed_value] property="network_address" value=hostname str += "-h " + hostname resources_hash[property]=hostname else property=$cmd_user[n-1][:property_name] value=$cmd_user[n-1][:property_fixed_value] str += "-p " + property + "=" + value resources_hash[property]=value end str2 = str resources_hash2={} resources_hash2.replace(resources_hash) if $cmd_user[n-1][:property_nb].is_a?(Fixnum) # We have a form /param={3} for i in (1..$cmd_user[n-1][:property_nb].to_i) $cmd_user[n-1][:property_ndx] += 1 v = ($cmd_user[n-1][:property_ndx] + $cmd_user[n-1][:offset]) v = sprintf("#{$cmd_user[n-1][:format_num]}", v) if $cmd_user[n-1][:format_num].length > 0 value2=v.to_s + $cmd_user[n-1][:property_fixed_value2] str = str2 + value2 + " " resources_hash[property]=resources_hash2[property].to_s + value2 # For cpuset no if $cmd_user[n-1][:property_name]=="nodes" || $cmd_user[n-1][:property_name]=="node" $cpuset_host_current=$cmd_user[n-1][:property_fixed_value] + v.to_s + $cmd_user[n-1][:property_fixed_value2] end if $cpuset_property_name != "" if $cpuset_property_name == $cmd_user[n-1][:property_name] $cpuset_property_current_value = $cmd_user[n-1][:property_fixed_value] + v.to_s + $cmd_user[n-1][:property_fixed_value2] end end tree n+1, str, resources_hash end else # We have a form /param=host_a,host_b, host[10-20,30,35-50,70],host_c,host[80-120] list_val = Resources.decompose_list_values($cmd_user[n-1][:property_nb]) list_val.each do |item| str = str2 + item + " " resources_hash[property]=resources_hash2[property].to_s + item # For cpuset no if $cmd_user[n-1][:property_name]=="nodes" || $cmd_user[n-1][:property_name]=="node" $cpuset_host_current=item end tree n+1, str, resources_hash end end # if $cmd_user[n-1][:property_nb].is_a?(Fixnum) else # Cpuset if $cpuset_host_current != $cpuset_host_previous $cpuset_no=0 $cpuset_host_previous=$cpuset_host_current $cpuset_property_previous_value = $cpuset_property_current_value else if $cpuset_property_name != "" if $cpuset_property_previous_value != $cpuset_property_current_value $cpuset_no+=1 $cpuset_property_previous_value = $cpuset_property_current_value end else $cpuset_no+=1 end end str += " -p cpuset="+$cpuset_no.to_s resources_hash["cpuset"]=$cpuset_no # End of levels - execution execute_command(str) hash={} hash.replace(resources_hash) $resources << hash str = $oar_cmd end end # end tree # Execute oar command def Resources.execute_command(str) puts str unless $options[:yaml] if $options[:commit] r1 = `#{str}` r2 = $?.exitstatus if r2 > 0 $stderr.puts "[OARADMIN ERROR]" + " command : " + str $stderr.puts r1 end end end end # module Resources module Edit # Define parameters for editing files # Parameter : hash table containing oar.conf parameters # Return : editor : vi or the editor defined in $EDITOR # directory : temporary directory used to edit files def Edit.env(config) # Text editor to edit an admission rule editor = "vi" editor = ENV['EDITOR'] if ENV['EDITOR'] # Directory to edit an admission rule directory="/tmp/" if config['OAR_RUNTIME_DIRECTORY'] directory = config['OAR_RUNTIME_DIRECTORY'] directory += "/" if directory[directory.length-1..directory.length-1]!="/" end return editor, directory end # Edit.env end # module Edit ################################# # DEFINITIONS FOR ADMISSION RULES ################################# module Admission_rules # Retrieve list of rules given by user in the command line # Return : # r : array contains list of admission rules def Admission_rules.rule_list_from_command_line r = [] i = 0 while i < ARGV.length if ARGV[i] == "-f" || ARGV[i]== "--file" || ARGV[i] == "-n" || ARGV[i] == "--number" i += 1 else r.push(ARGV[i]) if !(ARGV[i] =~ /[^0-9]+/) end i += 1 end return r end # rule_list_from_command_line # Test params on command line # Parameters allowed : options -f, -n, numbers # others parameters are wrong # Return # false : all params are ok, true : one parameter is wrong def Admission_rules.test_params error = false i = 0 while i < ARGV.length if ARGV[i] == "-f" i += 1 elsif ARGV[i] == "-n" || ARGV[i] == "--number" i+=1 if ARGV[i].nil? || (ARGV[i] =~ /[^0-9]+/) error=true break end elsif ARGV[i][0..0] != "-" if ARGV[i] =~ /[^0-9]+/ error=true break end end i+=1 end error end # test_params # Test if the file given by user is readable or not # Read the content of the file which contains admission rule # Return : # status 0 : no error - 1 : an error occurs # script : content of admission rule def Admission_rules.load_rule_from_file status=0 script = file_name = "" if $options[:file] (0..ARGV.length-1).each do |i| if ARGV[i] == "-f" file_name = ARGV[i+1] if i < ARGV.length-1 end end if !File.readable?(file_name) $stderr.puts "Error : file "+file_name+" not found or unreadable" status = 1 else File.open(file_name) do |file| while line = file.gets script << line end end end end # if $options[:file] return status, script end # load_rule_from_file end # module Admission_rules ########################### # DEFINITIONS FOR CONF_FILE ########################### module Conf_file # Retrieve files given by user # Used with : -e --edit, -H --history, -n --number # Return : # status == 0 : Only one file_name given by user # status == 1 : No file_name given # status == 2 : Too many parameters # status == 3 : A number must be specified # file_name : file_name given by user def Conf_file.test_params status=0 file_name=nil files = 0 i = 0 while i < ARGV.length if ARGV[i] == "-n" || ARGV[i] == "--number" i+=1 if ARGV[i].nil? || (ARGV[i] =~ /[^0-9]+/) status=3 break end elsif ARGV[i][0..0] != "-" file_name = ARGV[i] files += 1 end i+=1 end status = 1 if files == 0 status = 2 if files > 1 return status, file_name end # Conf_file.test_params # Test parameters # Used with : -R --revert # In this case, only the form -R conf_file rev is possible. # Return : # status == 0 : no error # status == 1 : one parameter is bad # p[0] : file_name given by user # p[1] : revision number def Conf_file.test_params2 status=0 p = [] opt_found = false i = 0 while i < ARGV.length if ARGV[i] == "-R" || ARGV[i] == "--revert" opt_found = !opt_found elsif ARGV[i][0..0] != "-" if !opt_found # Parameter before option ? status = 1 break end p.push(ARGV[i]) end i+=1 end status = 1 if p.length != 2 status = 1 if !p[1].nil? && p[1] =~ /[^0-9]+/ return status, p[0], p[1] end # Conf_file.test_params2 end # module Conf_file # Object Rule represent an admission rule # Methods : # - display : display the rule # - add : add the rule # - update : update the rule # - edit : edit the rule with a text editor # - delete : delete the rule # - export : export the rule # - comment : comment or delete comments in the rule class Rule attr_accessor :rule_id, :exist, # rule exist y/n ? :script, # content of the admission rule - script Perl :export_file_name, # filename used to export :export_file_name_with_rule_id, # filename must use number rule y/n :rule_id_must_exist, # the rule_id must be exist or not : true/false :file_name, # temporary file name to store the admission rule :editor, # command to be used for the text editor :action, # true : comment - false : delete comments :silent_mode, # silent mode for output y/n :context # work context : admission rule or file - useful for messages def initialize(bdd, rule_id) @bdd = bdd @rule_id = rule_id @script="" @exist=false @export_file_name="" @export_file_name_with_rule_id=false @rule_id_must_exist=false @file_name="" @editor="" @action=false @silent_mode = false @context="rule" if !rule_id.nil? q = "SELECT * FROM admission_rules WHERE id = " + rule_id.to_s rows = @bdd.select_one(q) if rows @script = rows["rule"] @exist = true end end @repository = Repository.new end # Display rule def display(display_level) puts "------" puts "Rule : " + @rule_id.to_s # rule number no_char = 65 mark_more_text = "..." if display_level == 2 # display all text with -lll option no_char = -1 mark_more_text="" end description_end = false @script.each_with_index do |line,line_index| if line_index == 0 # title or object of the admission rule str = line[0..no_char] str += mark_more_text if line.length > no_char+2 puts str end if (display_level==1 || display_level==2) && line_index > 0 && !description_end # description of the admission rule if line[0..0]=="#" str = line[0..no_char] str += mark_more_text if line.length > no_char+2 puts str else description_end = !description_end end end if display_level==2 && line_index > 0 && description_end # rest of the admission rule str = line[0..no_char] str += mark_more_text if line.length > no_char+2 puts str end end end # def display(display_level) # Add rule # if no number rule specified => add at the end of table # if number specified => insert admission rule at the rule_id position # the numbers above or equal to rule_id are increased by 1 if necessary # Return : # status : 0 : no error - > 0 : one error occurs def add status = status2 = 0 msg = [] msg[0] = "Admission rule added" id_tmp = 0 @repository.create if @exist # rule id already exist in database : add +1 to the rule ids q = "SELECT * FROM admission_rules WHERE id >= " + @rule_id.to_s + " ORDER BY id DESC" rows = @bdd.execute(q) rows.each do |r| q = "UPDATE admission_rules SET id = " + (r["id"] + 1).to_s + " WHERE id = " + r["id"].to_s status = Bdd.do(@bdd, q) if status==0 && @repository.active && status2==0 @repository.file_name="admission_rule_"+(r["id"] + 1).to_s @repository.file_content=r["rule"] if File.exist?(@repository.path_working_copy+"/"+@repository.file_name) status2 = @repository.write else status2 = @repository.write @repository.add if status2==0 end end end rows.finish # Add rule in database q = "INSERT INTO admission_rules (id, rule) VALUES(?, ?)" status = Bdd.do(@bdd, q, @rule_id, @script) if status == 0 puts msg[0] if status2 == 0 && @repository.active @repository.file_name="admission_rule_"+@rule_id.to_s @repository.file_content=@script status2 = @repository.write @repository.log_commit = "Add new admission rule #" + @rule_id.to_s + "\nNumber that already existed" @repository.commit end end else if !@rule_id.nil? # Add rule in database q = "INSERT INTO admission_rules (id, rule) VALUES(?, ?)" status = Bdd.do(@bdd, q, @rule_id, @script) if status == 0 puts msg[0] id_tmp=@rule_id end else # add admission rule at the end of table q = "INSERT INTO admission_rules (rule) VALUES(?)" status = Bdd.do(@bdd, q, @script) if status==0 puts msg[0] if @repository.active # Retrieve id for versioning q = "SELECT max(id) FROM admission_rules" rows = @bdd.select_one(q) id_tmp = rows[0] end end end if status==0 && @repository.active @repository.file_name="admission_rule_"+id_tmp.to_s @repository.file_content=@script if @repository.write == 0 @repository.add @repository.log_commit = "Add new admission rule #" + id_tmp.to_s @repository.commit end end end status end # def add # Update one rule # Return : # status : 0 : no error - > 0 : one error occurs def update status = 0 msg = [] msg[0] = "Admission rule updated" if @exist @repository.create q = "UPDATE admission_rules SET rule = ? WHERE id = " + @rule_id.to_s status = Bdd.do(@bdd, q, @script) if status == 0 puts msg[0] if @repository.active @repository.file_name="admission_rule_"+@rule_id.to_s @repository.file_content=@script if @repository.write == 0 @repository.log_commit = "Update admission rule #" + @rule_id.to_s @repository.commit end end end else $stderr.puts "Error : the rule " + @rule_id.to_s + " does not exist" status = 1 end status end # def update # Delete the admission rule # Return : # status : 0 : no error - > 0 : one error occurs def delete status_1 = status_2 = 0 if @exist q = "DELETE FROM admission_rules WHERE id = " + @rule_id.to_s status_2 = Bdd.do(@bdd, q) puts "Admission rule " + @rule_id.to_s + " deleted" if status_2 == 0 else $stderr.puts "Error : the rule " + @rule_id.to_s + " does not exist" status_1 = 1 end (status_1 != 0 || status_2 != 0) ? 1 : 0 end # def delete # Export one rule into a file def export status=0 f_name = @export_file_name f_name += @rule_id.to_s if @export_file_name_with_rule_id begin f = File.new(f_name, "w") f.print @script f.close puts "Export admission rule " + @rule_id.to_s + " into file " + f_name if silent_mode==false rescue Exception => e $stderr.puts "Error while creating file "+f_name $stderr.puts e.message status=1 end status end # def export # Edit an admission rule # Return : # status : 0 : no error - > 0 : one error occurs # user_choice : 0 : continue and commit changes in oar database - 1 : abort changes, nothing is done in oar database def edit status = 0 user_choice = 1 msg = { 'rule' => [ "(e)dit admission rule again,", "(c)ommit changes in oar database and quit,", "(Q)uit and abort without changes in oar database : " ], 'file' => [ "(e)dit file again,", "(c)ommit changes and quit,", "(Q)uit and abort without changes : " ] } if @exist==false && @rule_id_must_exist $stderr.puts "Error : the rule " + @rule_id.to_s + " does not exist" status = 1 end if status == 0 begin @script = "# Title : \n# Description : \n\n" if @script.length == 0 && @context == "rule" f = File.new(file_name, "w") f.print @script f.close rescue Exception => e $stderr.puts "Error while creating temporary file " $stderr.puts e.message status=1 end if status==0 user_choice = "" status1 = true str = @editor + " " + @file_name old_script = @script begin status1 = system(str) if status1 @script = "" begin File.open(file_name) do |file| while line = file.gets @script << line end end rescue Exception => e end # Ask question to user only if changes are made in admission rule content or in file content if @script != old_script begin puts msg[@context][0] puts msg[@context][1] print msg[@context][2] user_choice = $stdin.gets.chomp end while(user_choice != "e" && user_choice != "c" && user_choice != "Q") else user_choice = "Q" end else $stderr.puts "Error during the launch of the text editor with the command : " + str end end while(user_choice != "c" && user_choice != "Q" && status1) status = 1 if !status1 user_choice = 0 if status == 0 && user_choice == "c" end end begin File.delete(file_name) rescue Exception => e end return status, user_choice end # def edit # Disable a rule : commenting the code # add # at the beginning of each line # Enable a rule : removing comments # delete # at the beginning of each line # Return : # status : 0 : no error - > 0 : one error occurs def comment status=0 if @exist # The rule is already commented or not ? str="" already_commented=true @script.each do |line| already_commented=false if line[0..0]!="#" && line.strip.length > 0 end if @action if !already_commented @script.each do |line| str = str + "#" + line end @script = str status = self.update else puts "The rule is already disabled" end else if already_commented @script.each do |line| line.length>0 ? str = str + line[1..line.length-1] : str += line end @script = str status = self.update else puts "The rule is already enabled" end end else $stderr.puts "Error : the rule " + @rule_id.to_s + " does not exist" status = 1 end status end # comment end # class Rule # Object Rules_set contains several admission rules # Methods : # - display : display rules # - delete : display rules # - export : export rules into files class Rules_set attr_accessor :export_file_name, # filename used to export :export_file_name_with_rule_id, # filename must use number rule y/n :silent_mode # silent mode for output y/n def initialize(bdd, rules_set_user) @bdd = bdd @rules_set_user = rules_set_user @export_file_name="" @export_file_name_with_rule_id=false @silent_mode = false # No rules specified by user # => load all rules from database if @rules_set_user.length==0 q = "SELECT id FROM admission_rules ORDER BY id" rows = @bdd.execute(q) rows.each do |r| @rules_set_user.push(r["id"]) end rows.finish end @rules_set=[] @rules_set_user.each do |r| @rules_set.push(Rule.new(@bdd, r)) end end # Display rules # Parameters : # display_level : nb level for more details # Return : # status : 0 : no error - > 0 : one error occurs def display(display_level) status = 0 @rules_set.each do |r| if r.exist r.display(display_level) else $stderr.puts "Error : the rule " + r.rule_id.to_s + " does not exist" status = 1 end end status end # def display # Delete one or several admission rules specified by user # Return : # status : 0 : no error - > 0 : one error occurs def delete status_1 = status_2 = 0 rule_ids_deleted="" repository = Repository.new @rules_set.each do |r| repository.create if r.exist status_2 = r.delete status_1 = 1 if status_2 != 0 if status_2 == 0 && repository.active repository.file_name = "admission_rule_" + r.rule_id.to_s rule_ids_deleted += "#" + r.rule_id.to_s + " " repository.delete end end if repository.active && rule_ids_deleted != "" repository.log_commit = "Delete admission(s) rule(s) "+rule_ids_deleted repository.commit end status_1 end # def delete # Export admission rules into files # Return : # status : 0 : no error - > 0 : one error occurs def export status = 0 overwrite_files = true # Files already exists ? Question to user : Overwrite y/n ? files_already_exists = [] @rules_set.each do |r| if @export_file_name_with_rule_id files_already_exists.push(@export_file_name + r.rule_id.to_s) if File.exist?(@export_file_name + r.rule_id.to_s) else files_already_exists.push(@export_file_name) if File.exist?(@export_file_name) end end if !files_already_exists.empty? c = "" files_already_exists.each { |f| c += f c += " " } puts "Warning ! Some files already exists : " + c print "Overwrite [N/y] ? " r = "" begin r = $stdin.gets.chomp end while ( r != "" && r != "N" && r != "y" ) overwrite_files = r == "y" else overwrite_files = true end if overwrite_files @rules_set.each do |r| if r.exist r.export_file_name = @export_file_name r.export_file_name_with_rule_id = @export_file_name_with_rule_id r.silent_mode = @silent_mode r.export else $stderr.puts "Error : the rule " + r.rule_id.to_s + " does not exist" status = 1 end end end status end # def export end # class Rules_set # Object Repository # Methods : # - create : create repository and working copy # - write : write data in working copy # - add : execute svn add command # - delete : delete file(s) in working copy and execute svn delete command # - commit : execute svn commit command # - display_status : display error messages if repository does not exists or is unreadable # - file_exist? : file exist in repository y/n class Repository attr_accessor :file_name, # File name to write in working copy :file_content, # Content of data to write in working copy :log_commit, # Log for commit :path_working_copy, # Path working copy :active, # Versioning feature is active or not :exists, # Repository exists y/n :silent_mode, # Silent mode for output y/n :display_diff_changes # Number changes to display def initialize @file_name = "" @file_content = "" @log_commit = "" @active = false @path_repository = "" # Path of the repository @path_working_copy = "" # Path of the working copy @access_method = "file://" @exists = false # Repository exists y/n @silent_mode=false @display_diff_changes=nil conf = Oar.load_configuration @active = true if !conf['OARADMIN_VERSIONING'].nil? && conf['OARADMIN_VERSIONING'].upcase == "YES" # Files already exists ? home_user_oar = "" s = `getent passwd oar` home_user_oar = s.split(":")[5].to_s @path_repository = home_user_oar + "/.oaradmin/rp/svn_repository" @path_working_copy = home_user_oar + "/.oaradmin/wc/svn_repository" @exists = true if File.exist?(@path_repository) end # Write file in working copy # Return : # status 0 : no error - 1 : one error occurs def write status=0 begin f = File.new(@path_working_copy+"/"+@file_name,"w") f.print @file_content f.close rescue Exception => e $stderr.puts "[OARADMIN ERROR]: Error while writing data in working copy for versioning" $stderr.puts "[OARADMIN ERROR]: " + e.message status=1 end status end # Execute svn add command # Return : # status 0 : no error - 1 : one error occurs def add status=0 str = "svn add " + @path_working_copy+"/"+@file_name `#{str}` r = $?.exitstatus if r > 0 $stderr.puts "[OARADMIN ERROR]: Error while the svn add command" status=1 end status end # Delete a file in working copy and execute svn delete command # Return : # status 0 : no error - 1 : one error occurs def delete status=0 begin File.delete(@path_working_copy+"/"+@file_name) rescue Exception => e $stderr.puts "[OARADMIN ERROR]: Error while deleting data in working copy for versioning" $stderr.puts "[OARADMIN ERROR]: " + e.message status=1 end if status==0 str = "svn delete " + @path_working_copy+"/"+@file_name `#{str}` r = $?.exitstatus if r > 0 $stderr.puts "[OARADMIN ERROR]: Error while the svn delete command" status=1 end end status end # Execute svn commit # Return : # status 0 : no error - 1 : one error occurs def commit status=0 str = "svn commit " + @path_working_copy + " -m " + '"' + @log_commit + '"' `#{str}` r = $?.exitstatus if r > 0 $stderr.puts "[OARADMIN ERROR]: Error while the svn commit command" status=1 else puts "Versioning done" if !@silent_mode end status end # Create repository and working copy # Return : # status 0 : no error - 1 : one error occurs def create status=0 if @active && !@exists puts "Initialization of repository" paths = @path_repository.split("/") current_dir = "" (1..paths.length-2).each { |i| current_dir += "/" + paths[i] } begin FileUtils.mkdir_p current_dir rescue Exception => e $stderr.puts "[OARADMIN ERROR]: can't create " + current_dir + " directory for repository" $stderr.puts "[OARADMIN ERROR]: " + e.message status=1 end if status==0 str = "svnadmin create " + @path_repository `#{str}` r = $?.exitstatus if r > 0 $stderr.puts "[OARADMIN ERROR]: Error while creating repository" status=1 end end if status==0 paths = @path_working_copy.split("/") current_dir = "" (1..paths.length-2).each { |i| current_dir += "/" + paths[i] } begin FileUtils.mkdir_p current_dir rescue Exception => e $stderr.puts "[OARADMIN ERROR]: can't create " + current_dir + " directory for working copy." $stderr.puts "[OARADMIN ERROR]: " + e.message status=1 end end if status==0 str = "svn checkout " + @access_method + @path_repository + " " + @path_working_copy `#{str}` r = $?.exitstatus if r > 0 $stderr.puts "[OARADMIN ERROR]: Error while creating working copy" status=1 end end if status==0 # Add all admission rules in working copy list_rules = [] $config=Oar.load_configuration dbh = Bdd.connect($config) rules = Rules_set.new(dbh, list_rules) rules.export_file_name=@path_working_copy+"/"+"admission_rule_" rules.export_file_name_with_rule_id=true rules.silent_mode=true rules.export files = Dir[@path_working_copy+"/*"] files.sort! files.each do |f| @file_name = File.split(f)[1] self.add end @silent_mode=true # Add conf files all_conf_files=["oar.conf", "monika.conf", "drawgantt.conf"] all_conf_files.each do |file| r, conf_file = Oar.conf_file_readable(file) if r==0 f = IO::read(conf_file) @file_name=file @file_content=f self.write self.add end end # Commit @log_commit = "Initialization of repository" self.commit @silent_mode=false end @exists=true if status==0 end # if @active && !@exists status end # create # Display error messages if repository does not exists or is unreadable # Return : # status 0 : no error - 1 : one error occurs def display_status status=0 if !@active $stderr.puts "[OARADMIN ERROR]: Versioning feature is not active" $stderr.puts "[OARADMIN ERROR]: You can activate this feature with the parameter OARADMIN_VERSIONING in the OAR conf file" status=1 elsif !@exists $stderr.puts "[OARADMIN ERROR]: The repository does not exists or is unreadable" status=1 end status end # display_status # Test if a file exist in repository # Return : true if file exists def file_exist? f_found = false str = "svn list " + @access_method + @path_repository + " --xml" r = `#{str}` xml = REXML::Document.new(r) xml.root.each_element { |e| e.each_element { |f| f_found = true if f.elements["name"].get_text == @file_name } } f_found end # file_exist? end # class Repository # Object Revisions : contains revisions of Repository # Methods : # - display_diff : display diff between revisions class Revisions < Repository attr_accessor :rev_id # rev_id given by user # Load revisions def initialize(file_name) super() @rev=[] # Contains revisions numbers and dates @file_name = file_name @rev_id=nil if !@active || !@exists display_status else # Retrieve all log from repository # Select all revisions numbers where a file, or admission rule is mentioned status=0 str = "svn log " + @access_method + @path_repository + " -v --xml" r = `#{str}` status = $?.exitstatus if status > 0 $stderr.puts "[OARADMIN ERROR]: Error while browsing the repository" status=1 else xml_tags_not_found_paths = xml_tags_not_found_date = xml_tags_not_found_revision = false xml = REXML::Document.new(r) xml.root.each_element { |e| if !e.elements["paths"].nil? e.elements["paths"].each_element { |f| if f.get_text == "/" + @file_name d = nil if !e.elements["date"].nil? d = e.elements["date"].get_text.to_s # with svn xml output format date is 2008-08-03T17:50:57.759877Z # convert to 2008-08-03 19:50:57 +0200 d1 = Time.xmlschema(d).localtime.strftime("%Y-%m-%d %H:%M:%S") d2 = Time.xmlschema(d).localtime.rfc822.to_s d = d1 + " " + d2[26,5] else xml_tags_not_found_date = true end if !e.attributes["revision"].nil? @rev.push({:rev=>e.attributes["revision"], :date=>d, :action=>f.attributes["action"]}) else xml_tags_not_found_revision = true end end } else xml_tags_not_found_paths = true end } @rev.push({:rev=>"0", :date=>""}) # For diff while add admission rules or files if xml_tags_not_found_paths || xml_tags_not_found_date || xml_tags_not_found_revision str2="[OARADMIN ERROR]: Some xml attribute(s) or tag(s) not found : " str2 += "paths " if xml_tags_not_found_paths str2 += "date " if xml_tags_not_found_date str2 += "revision " if xml_tags_not_found_revision $stderr.puts str2 $stderr.puts "[OARADMIN ERROR]: Please check xml format with command "+str status=1 end end # if status > 0 end # if !@active || !@exists end # initialize # Display historical changes # Execute diffs between revisions # Return : # status 0 : no error - 1 : one error occurs def display_diff status=0 if @rev.length >= 2 # First index in revisions @rev[0] is the latest revision in repository: r #latest # Last index in revisions @rev[length-1] is the older revision in repository : r #1 # We display changes, so we must have at least 2 revisions for an admission rule or a file i=0 k=1 if @display_diff_changes.nil? @display_diff_changes=@rev.length-1 else @display_diff_changes=@display_diff_changes.to_i end all_diffs = "" while k <= @display_diff_changes && i <= @rev.length-2 cmd_diff = "svn diff " + @access_method + @path_repository + " -r " + @rev[i+1][:rev] + ":" + @rev[i][:rev] r = `#{cmd_diff}` status = $?.exitstatus if status==0 one_diff = "" file_found = false r.each do |line| if file_found if line[0..6]=="Index: " if line != "Index: "+file_name break end else one_diff += line end end if line.chomp == "Index: "+file_name file_found=true end end # r.each do |line| all_diffs += "Change(s) between r"+@rev[i+1][:rev]+" " all_diffs += "("+@rev[i+1][:date].to_s+") " if @rev[i+1][:rev].to_s != "0" all_diffs += "and r"+@rev[i][:rev]+" ("+@rev[i][:date].to_s+")\n" all_diffs += one_diff all_diffs += "\n" else $stderr.puts "[OARADMIN ERROR]: Error while browsing the repository" $stderr.puts "[OARADMIN ERROR]: Error while command : "+cmd_diff status=1 end i+=1 k+=1 end # Display results puts all_diffs else $stderr.puts "[OARADMIN ERROR]: File " + @file_name + " not found in repository" status=1 end # if @rev.length >= 2 status end # display_diff # Retrieve the content of a file as it existed in a revision number # Return : # 0 : no error # 1 : The revision 0 exists but contains no file. # There is no file at rev 0 in a svn repository # 2 : The revision number specified by user is greater # than the revision number of repository # 3 : svn cat repository/file@rev is impossible. # The #rev specified by user does not contains the file @file_name def retrieve_file_rev status=0 # @rev contains always r0. So we must have at least 2 elements in @rev for the file if @rev.length < 2 $stderr.puts "[OARADMIN ERROR]: File " + @file_name + " not found in repository" return 1 end # Retrieve revision max from repository rev_max_repository=nil str = "svn info " + @access_method + @path_repository + " --xml" r = `#{str}` xml = REXML::Document.new(r) xml.root.each_element { |e| rev_max_repository = e.attributes["revision"].to_i } if @rev_id > rev_max_repository $stderr.puts "[OARADMIN ERROR]: The revision #"+ @rev_id.to_s + " does not exist in repository" $stderr.puts "[OARADMIN ERROR]: The latest revision in repository is #" + rev_max_repository.to_s return 2 end # #rev given by user exists in repository # test if the #rev given by user exists for the file str = "svn list " + @access_method + @path_repository + " -r " + @rev_id.to_s + " --xml" r = `#{str}` xml = REXML::Document.new(r) file_exist_in_rev = false xml.root.each_element { |e| e.each_element { |f| file_exist_in_rev = true if f.elements["name"].get_text == @file_name } } if !file_exist_in_rev # test for more information to user # perhaps #rev given by user is a revision where the file was deleted file_deleted_in_rev=false (0..@rev.length-2).each do |i| file_deleted_in_rev=true if @rev[i][:rev].to_i == @rev_id && @rev[i][:action]=="D" end # (0..@rev.length-2).each do |i| $stderr.puts "[OARADMIN ERROR]: Bad revision number. The file " + @file_name + " does not exist in revision #"+ @rev_id.to_s if file_deleted_in_rev $stderr.puts "[OARADMIN ERROR]: In #" + @rev_id.to_s + " the file was deleted. So it does not exist. To retrieve the content, try an older #rev" end return 3 end # File @file_name exist in #rev str = "svn cat " + @access_method + @path_repository + "/" + @file_name + "@" + @rev_id.to_s @file_content = `#{str}` status end # retrieve_file_rev end # class Revisions ./oar-2.5.2/sources/core/qfunctions/oaradmin/oaradmin.rb0000755000175000017500000011602511757171206021306 0ustar plbplb#!/usr/bin/ruby # $Id: oaradmin.rb 1 2008-05-05 16:00:00 ddepoisi $ # # oaradmin : utility to manage oar configuration # # requirements: # ruby1.8 (or greater) # libdbi-ruby # libdbd-mysql-ruby or libdbd-pg-ruby # libyaml-ruby # # To activate the verbose mode, add -w at the end of the first line. Ex : #!/usr/bin/ruby -w # require 'optparse' require 'dbi' require 'yaml' require 'oar_modules' require 'oaradmin_modules' $msg = [] $options= {} $subcommand = [] $subcommand[0] = {:short_form=>"re", :long_form=>"resources", :description=>"manage resources in oar database"} $subcommand[1] = {:short_form=>"ru", :long_form=>"rules", :description=>"manage admission rules"} $subcommand[2] = {:short_form=>"", :long_form=>"conf", :description=>"edit conf file and keep changes in Svn repository"} $subcommand[8] = {:short_form=>"", :long_form=>"help", :description=>"print this help message"} $subcommand[9] = {:short_form=>"ver", :long_form=>"version", :description=>"print OAR version number"} class CLI class <" puts "Utility to manage oar configuration" puts puts "Available subcommands : " (0..$subcommand.length-1).each do |i| if !$subcommand[i].nil? str = " "+$subcommand[i][:long_form] str += " ("+$subcommand[i][:short_form]+")" if $subcommand[i][:short_form].length>0 str = str.ljust(27)+$subcommand[i][:description] puts str end end puts puts "See also oaradmin man page for more information" puts exit(1) end def self.setup # Load configuration $config=Oar.load_configuration if $config.empty? $stderr.puts "Configuration file not found" exit(3) end editor, directory = Edit.env($config) # Connect to the database @dbh = Bdd.connect($config) exit(4) if dbh.nil? end def self.invoke command, *args case command when 0 setup # ################ # Manage resources # ################ $msg[0] = "Incoherence or syntax error in specified options" $msg[1] = "A parameter after an option is missing" $msg[2] = "With -a and -s options, in {...} expression only a numeric value with optional numeric format and offset are allowed" $msg[3] = "With -p option, in {...} expression only % character with optional numeric format and offset are allowed" $msg[4] = "With -d option, {...} expression is not allowed" $msg[5] = "[OARADMIN]: No resource selected" $msg[6] = "[OARADMIN ERROR]: Oaradmin uses oarnodes -Y command. So perl-yaml package must be installed with oarnodes" $msg[7] = "[OARADMIN ERROR]: Oaradmin uses oarnodes command. So oar-user package must be installed" $cmd_user = [] $oar_cmd = "" $resources = [] $list_resources_id=[] # contains the list of resources to delete # Options for parsing $options = {} $options[:auto_offset] = false $options[:yaml] = false $options[:add] = $options[:select] = $options[:property] = $options[:delete] = $options[:commit] = false $options[:cpusetproperty] = false $options[:cpusetproperty_name] = "" opts = OptionParser.new do |opts| opts.banner = "Usage: oaradmin resources [-a [--cpusetproperty=prop][-p] [--auto-offset] [--yaml]] [-s -p] [-d] [-c]" # add resources opts.on("-a","--add","Add new resources") do $options[:add] = true end # auto_offset opts.on("--auto-offset", "Automatically compute the node, cpu, and core offsets for you based on what is already registered in the OAR database") do ARGV.delete("--auto-offset") # CR: avoid crappy errors in following code since options are actually parsed at least 3 times... Don't want to mess with that. $options[:auto_offset] = true end # yaml export opts.on("-Y","--yaml", "Export yaml datastructure instead of oarnodesetting commands") do $options[:yaml] = true ARGV.delete("--yaml") # BB: avoid crappy errors... same thing as CR ;-) ARGV.delete("-Y") # BB: avoid crappy errors... same thing as CR ;-) end # cpusetproperty opts.on("--cpusetproperty=prop","Property name for cpuset numbers") do |opt| $options[:cpusetproperty]=true $options[:cpusetproperty_name] = opt end # update resources opts.on("-s","--select","Select resources for update") do $options[:select] = true end opts.on("-p","--property","Set value for a property") do $options[:property] = true end # delete resources opts.on("-d","--delete","Delete resources") do $options[:delete] = true end # execute and modify database oar opts.on("-c","--commit", "Commit in oar database") do $options[:commit] = true end # help opts.on_tail("-h", "--help", "Show this message") do puts opts exit(1) end end begin opts.parse ARGV rescue OptionParser::ParseError => no_erreur puts no_erreur puts opts exit(1) end # Other tests on syntax if ARGV.empty? puts opts exit(1) end if !( ($options[:add] && !$options[:property] && !$options[:select] && !$options[:delete]) || # -a alone ($options[:add] && $options[:property] && !$options[:select] && !$options[:delete]) || # -a -p ($options[:select] && $options[:property] && !$options[:add] && !$options[:cpusetproperty] && !$options[:delete]) || # -s -p (!$options[:select] && !$options[:property] && !$options[:add] && !$options[:cpusetproperty] && $options[:delete]) ) # -d alone puts $msg[0] puts opts exit(1) end # Tests syntax in command line r = Resources.parsing if r > 0 puts $msg[0] if r == 1 puts $msg[1] if r == 2 puts $msg[2] if r == 3 puts $msg[3] if r == 4 puts $msg[4] if r == 5 puts opts exit(1) end # add resources if $options[:add] $oar_cmd = "oarnodesetting -a " # Test syntax for --cpusetproperty # FIXME: I guess there is a bug here.. if [:cpusetproperty] i=0 while i val_max end end end end end end end elsif $?.exitstatus == 6 $stderr.puts $msg[6] exit(2) elsif $?.exitstatus == 127 $stderr.puts $msg[7] exit(2) end # if $?.exitstatus == 0 resources_selected=false i = j = k = 1 i += $cmd_user[0][:offset] while i <= val_max && j+$cmd_user[0][:offset] <= val_max # First part of oar command str = "oarnodesetting " # Properties in the oar command (1..$cmd_user.length-1).each do |n| str += "-p " + $cmd_user[n][:property_name] + "=" if $cmd_user[n][:property_nb].is_a?(Fixnum) # we can have the follows forms : property=param{%} property=text_part_a{%2d%+20offset}text_part_b v = k + $cmd_user[n][:offset] v = sprintf("#{$cmd_user[n][:format_num]}", v) if $cmd_user[n][:format_num].length > 0 str += $cmd_user[n][:property_fixed_value] + v.to_s + $cmd_user[n][:property_fixed_value2] else # we can have : property=value but without {%} operator str += $cmd_user[n][:property_nb] end str += " " end # --sql clause in the oar command or -h hostname v = i v = sprintf("#{$cmd_user[0][:format_num]}", v) if $cmd_user[0][:format_num].length > 0 v = v.to_s if $cmd_user[0][:property_name] == "nodes" || $cmd_user[0][:property_name] == "node" str = str + "-h " + $cmd_user[0][:property_fixed_value] + v + $cmd_user[0][:property_fixed_value2] else str += "--sql " str += '"' str = str + $cmd_user[0][:property_name] + "=" if $cmd_user[0][:property_fixed_value].length > 0 str = str + "'" + $cmd_user[0][:property_fixed_value] + v + $cmd_user[0][:property_fixed_value2]+ "'" else str += v end str += '"' end # Execution Resources.execute_command(str) resources_selected=true i += 1 j += 1 if j > $cmd_user[0][:property_nb] j = 1 k += 1 end end puts $msg[5] if !resources_selected else # We have a form param=host_a,host_b, host[10-20,30,35-50,70],host_c,host[80-120] list_val = Resources.decompose_list_values($cmd_user[0][:property_nb]) k = 1 list_val.each do |item| # First part of oar command str = "oarnodesetting " # Properties in the oar command (1..$cmd_user.length-1).each do |n| str += "-p " + $cmd_user[n][:property_name] + "=" if $cmd_user[n][:property_nb].is_a?(Fixnum) # we can have the follows forms : property=param{%} property=text_part_a{%2d%+20offset}text_part_b v = k + $cmd_user[n][:offset] v = sprintf("#{$cmd_user[n][:format_num]}", v) if $cmd_user[n][:format_num].length > 0 str += $cmd_user[n][:property_fixed_value] + v.to_s + $cmd_user[n][:property_fixed_value2] else # we can have : property=value but without {%} operator str += $cmd_user[n][:property_nb] end str += " " end # --sql clause in the oar command or -h hostname if $cmd_user[0][:property_name] == "nodes" || $cmd_user[0][:property_name] == "node" str = str + "-h " + item else str += "--sql " str += '"' str = str + $cmd_user[0][:property_name] + "=" if item =~ /[^0-9]/ str = str + "'" + item + "'" else str += item end str += '"' end # Execution Resources.execute_command(str) k += 1 end end # if $cmd_user[0][:property_nb].is_a?(Fixnum) end # if $options[:select] && $options[:property] # delete resources if $options[:delete] # Properties exists in OAR database ? Resources.properties_exists # Decompose ARGV[] in hash table $cmd_user Resources.decompose_argv if $cmd_user.length > 0 # search properties matched condition # -d nodes=host[1-50] or -d cluster=zeus list_val = Resources.decompose_list_values($cmd_user[0][:property_nb]) # Recover list of resource_id # $list_resources_id[] contains the list of resources to delete r = `oarnodes -a -Y` if $?.exitstatus == 0 r_hash = YAML::load(r) if $cmd_user[0][:property_name] == "nodes" || $cmd_user[0][:property_name] == "node" str = "network_address" else str = $cmd_user[0][:property_name] end list_val.each do |v| r_hash_list_nodes=r_hash.keys r_hash_list_nodes.each do |n| r_hash_list_resources = r_hash[n].keys r_hash_list_resources.each do |r| r_hash[n][r]['properties'].each do |key,value| if key==str value=value.to_s $list_resources_id.push(r_hash[n][r]['properties']['resource_id'].to_s) if value==v end end end end end elsif $?.exitstatus == 6 $stderr.puts $msg[6] exit(3) elsif $?.exitstatus == 127 $stderr.puts $msg[7] exit(3) end # if $?.exitstatus == 0 else # search all resources_id r = `oarnodes -a -Y` if $?.exitstatus == 0 r_hash = YAML::load(r) r_hash_list_nodes=r_hash.keys r_hash_list_nodes.each do |n| r_hash_list_resources = r_hash[n].keys r_hash_list_resources.each do |r| $list_resources_id.push(r_hash[n][r]['properties']['resource_id'].to_s) end end elsif $?.exitstatus == 6 $stderr.puts $msg[6] exit(3) elsif $?.exitstatus == 127 $stderr.puts $msg[7] exit(3) end end # Sort $list_resources_id.each do |item| item=item.to_i end $list_resources_id.sort! $list_resources_id.each do |item| item=item.to_s end # Delete each resource_id if !$list_resources_id.empty? $list_resources_id.each do |r| str = "oarnodesetting -r " + r + " -s Dead -n " Resources.execute_command(str) str = "oarremoveresource " + r Resources.execute_command(str) end else puts $msg[5] end end # if $options[:delete] when 1 setup # ###################### # Manage admission rules # ###################### $msg[0] = "Incoherence in specified options" $msg[1] = "Configuration file not found" $msg[2] = "Error : bad admission rule number" $msg[3] = "Error : no admission rule number given" $msg[4] = "Error : too many admission rule numbers" $msg[5] = "One parameter is bad" $msg[6] = "Error : two parameters are required, rule_id and revision number" $msg[7] = "Error : a revision number must be greater than zero" $script = "" # Options for parsing $options = {} $options[:list] = $options[:add] = $options[:file] = $options[:edit] = $options[:export] = $options[:delete] = false $options[:history] = $options[:history_no] = $options[:revert] = false $options[:comment] = nil opts = OptionParser.new do |opts| opts.banner = "Usage: oaradmin rules [-l|-ll|-lll [rule_ids]] [-a [rule_id] [-f file]] [-d rule_id [rule_ids]] \n [-x [rule_ids] [-f file]] [-e rule_id [-f file]] [-1 rule_id] [-0 rule_id]\n [-H rule_id [-n number]] [-R rule_id rev]" # list admission rules opts.on("-l","--list","List admission rules") do $options[:list] = true end # add an admission rule opts.on("-a","--add","Add an admission rule") do $options[:add] = true end # file name opts.on("-f","--file","File which contains script for admission rule") do $options[:file] = true end # delete admission rules opts.on("-d","--delete","Delete admission rules") do $options[:delete] = true end # export admission rules opts.on("-x","--export","Export admission rules") do $options[:export] = true end # edit an admission rule opts.on("-e","--edit","Edit an admission rule") do $options[:edit] = true end # Enable the admission rule opts.on("-1","--enable","Enable the admission rule (removing comments)") do $options[:comment] = false end # Disable the admission rule opts.on("-0","--disable","Disable the admission rule (commenting the code)") do $options[:comment] = true end # Show the changes made on the admission rule opts.on("-H","--history","Show all changes made on the admission rule") do $options[:history] = true end # Number of latest changes to display opts.on("-n","--number","Number of latest changes to display") do $options[:history_no] = true end # Revert to the admission rule as it existed in a revision number opts.on("-R","--revert","Revert to the admission rule as it existed in a revision number") do $options[:revert] = true end # help opts.on_tail("-h", "--help", "Show this message") do puts opts exit end end begin opts.parse ARGV rescue OptionParser::ParseError => no_erreur puts no_erreur puts opts exit(1) end # Other tests on syntax if ARGV.empty? puts opts exit(1) end if !( ( $options[:list] && !$options[:add] && !$options[:file] && !$options[:edit] && !$options[:delete] && !$options[:export] && $options[:comment].nil? && !$options[:history] && !$options[:history_no] && !$options[:revert] ) || # -l (!$options[:list] && $options[:add] && !$options[:file] && !$options[:edit] && !$options[:delete] && !$options[:export] && $options[:comment].nil? && !$options[:history] && !$options[:history_no] && !$options[:revert] ) || # -a (!$options[:list] && $options[:add] && $options[:file] && !$options[:edit] && !$options[:delete] && !$options[:export] && $options[:comment].nil? && !$options[:history] && !$options[:history_no] && !$options[:revert] ) || # -a -f (!$options[:list] && !$options[:add] && !$options[:file] && $options[:edit] && !$options[:delete] && !$options[:export] && $options[:comment].nil? && !$options[:history] && !$options[:history_no] && !$options[:revert] ) || # -e (!$options[:list] && !$options[:add] && $options[:file] && $options[:edit] && !$options[:delete] && !$options[:export] && $options[:comment].nil? && !$options[:history] && !$options[:history_no] && !$options[:revert] ) || # -e -f (!$options[:list] && !$options[:add] && !$options[:file] && !$options[:edit] && $options[:delete] && !$options[:export] && $options[:comment].nil? && !$options[:history] && !$options[:history_no] && !$options[:revert] ) || # -d (!$options[:list] && !$options[:add] && !$options[:file] && !$options[:edit] && !$options[:delete] && $options[:export] && $options[:comment].nil? && !$options[:history] && !$options[:history_no] && !$options[:revert] ) || # -e (!$options[:list] && !$options[:add] && $options[:file] && !$options[:edit] && !$options[:delete] && $options[:export] && $options[:comment].nil? && !$options[:history] && !$options[:history_no] && !$options[:revert] ) || # -e -f (!$options[:list] && !$options[:add] && !$options[:file] && !$options[:edit] && !$options[:delete] && !$options[:export] && !$options[:comment].nil? && !$options[:history] && !$options[:history_no] && !$options[:revert] ) || # comment (!$options[:list] && !$options[:add] && !$options[:file] && !$options[:edit] && !$options[:delete] && !$options[:export] && $options[:comment].nil? && $options[:history] && !$options[:history_no] && !$options[:revert] ) || # -H (!$options[:list] && !$options[:add] && !$options[:file] && !$options[:edit] && !$options[:delete] && !$options[:export] && $options[:comment].nil? && $options[:history] && $options[:history_no] && !$options[:revert] ) || # -H -n (!$options[:list] && !$options[:add] && !$options[:file] && !$options[:edit] && !$options[:delete] && !$options[:export] && $options[:comment].nil? && !$options[:history] && !$options[:history_no] && $options[:revert] ) ) # -R puts $msg[0] puts opts exit(1) end if Admission_rules.test_params puts $msg[5] puts opts exit(1) end filename_base = "admission_rule_" case when $options[:list] # List admission rules # rules given by user list_rules = Admission_rules.rule_list_from_command_line level = level_max = 0 (0..ARGV.length-1).each do |i| level = 1 if ARGV[i]=~/-ll/ level = 2 if ARGV[i]=~/-lll/ level_max = level if level > level_max end level = level_max rules = Rules_set.new(dbh, list_rules) status = rules.display(level) exit(5) if status != 0 when $options[:add] editor, directory = Edit.env($config) # Add admission rule rule_id = nil list_rules = Admission_rules.rule_list_from_command_line if list_rules.length > 1 $stderr.puts $msg[4] puts opts exit(6) end rule_id = list_rules[0].to_i if !list_rules.empty? if rule_id && rule_id == 0 $stderr.puts $msg[2] exit(6) end if $options[:file] # add admission rule from a file status, $script = Admission_rules.load_rule_from_file exit(6) if status != 0 rule = Rule.new(dbh, rule_id) rule.script = $script status = rule.add exit(6) if status != 0 else # add admission rule using a text editor file_name = directory + "OAR_tmp_" + filename_base file_name += rule_id.to_s if rule_id rule = Rule.new(dbh, rule_id) rule.script = "" rule.rule_id_must_exist = false rule.file_name = file_name rule.editor = editor status, user_choice = rule.edit if status==0 && user_choice==0 status = rule.add exit(6) if status != 0 end end when $options[:edit] editor, directory = Edit.env($config) # Update admission rule # rules given by user list_rules = Admission_rules.rule_list_from_command_line if list_rules.length == 0 $stderr.puts $msg[3] puts opts exit(7) end if list_rules.length > 1 $stderr.puts $msg[4] puts opts exit(7) end rule_id = list_rules[0].to_i if rule_id == 0 $stderr.puts $msg[2] exit(7) end if $options[:file] # update admission rule from a file status, $script = Admission_rules.load_rule_from_file exit(7) if status != 0 rule = Rule.new(dbh, rule_id) rule.script = $script status = rule.update exit(7) if status != 0 else # edit admission rule using a text editor file_name = directory + "OAR_tmp_" + filename_base file_name += rule_id.to_s if rule_id rule = Rule.new(dbh, rule_id) rule.rule_id_must_exist = true rule.file_name = file_name rule.editor = editor status, user_choice = rule.edit exit(7) if status != 0 if status==0 && user_choice==0 status = rule.update exit(7) if status != 0 end end when $options[:delete] # Delete admission rules # rules given by user list_rules = Admission_rules.rule_list_from_command_line if list_rules.length > 0 rules = Rules_set.new(dbh, list_rules) status = rules.delete exit(8) if status != 0 else $stderr.puts $msg[3] puts opts exit(8) end when $options[:export] # Export admission rules # rules given by user list_rules = Admission_rules.rule_list_from_command_line if $options[:file] && list_rules.length !=1 puts $msg[0] puts opts exit(9) end rules = Rules_set.new(dbh, list_rules) if $options[:file] user_file_name = "" (0..ARGV.length-1).each do |i| if ARGV[i] == "-f" user_file_name = ARGV[i+1] if i < ARGV.length-1 end end rules.export_file_name=user_file_name rules.export_file_name_with_rule_id=false else rules.export_file_name=filename_base rules.export_file_name_with_rule_id=true end status = rules.export exit(9) if status != 0 when !$options[:comment].nil? # Enable or disable an admission rule list_rules = Admission_rules.rule_list_from_command_line if list_rules.length == 0 $stderr.puts $msg[3] puts opts exit(10) end if list_rules.length > 1 puts $msg[4] puts opts exit(10) end rule = Rule.new(dbh, list_rules[0]) rule.action = $options[:comment] status = rule.comment exit(10) if status != 0 when $options[:history] # Show the changes made on the admission rule rule_id = nil list_rules = Admission_rules.rule_list_from_command_line if list_rules.length > 1 $stderr.puts $msg[4] puts opts exit(11) end if list_rules.length == 0 $stderr.puts $msg[3] puts opts exit(11) end rule_id = list_rules[0].to_i if rule_id == 0 $stderr.puts $msg[2] exit(11) end revisions = Revisions.new("admission_rule_"+rule_id.to_s) exit(11) if !revisions.active || !revisions.exists if $options[:history_no] (0..ARGV.length-1).each do |i| if ARGV[i] == "-n" || ARGV[i] == "--number" if i < ARGV.length-1 && ARGV[i+1].to_i > 0 revisions.display_diff_changes = ARGV[i+1] else $stderr.puts "The number of changes must be greater than zero" exit(11) end end end end status = revisions.display_diff exit(11) if status != 0 when $options[:revert] # Revert to the admission rule as it existed in a revision number rule_id = nil list_rules = Admission_rules.rule_list_from_command_line if list_rules.length != 2 # In this case, list_rules[0] is the rule_id $stderr.puts $msg[6] # and list_rules[1] is the revision number puts opts exit(12) end rule_id = list_rules[0].to_i if rule_id == 0 $stderr.puts $msg[2] exit(12) end if list_rules[1].to_i == 0 $stderr.puts $msg[7] exit(12) end revisions = Revisions.new("admission_rule_"+rule_id.to_s) exit(12) if !revisions.active || !revisions.exists revisions.rev_id = list_rules[1].to_i status = revisions.retrieve_file_rev exit(12) if status != 0 # Add or update in oar database rule = Rule.new(dbh, rule_id) if rule.exist # rule already exist in oar database => update rule.script = revisions.file_content status = rule.update exit(12) if status != 0 else # rule does not exist in oar database and we have an old content # from repository. So, rule was deleted rule.script = revisions.file_content status = rule.add exit(12) if status != 0 end end # case when 2 # ############################################## # Edit conf file and keep changes for versioning # ############################################## $msg[0] = "Incoherence in specified options" $msg[1] = "Error : no file name given" $msg[2] = "Error : too many file name given" $msg[3] = "Error : a number must be specified" $msg[4] = "One parameter is bad" $msg[5] = "Error : a revision number must be greater than zero" # Options for parsing $options = {} $options[:edit] = $options[:history] = $options[:history_no] = $options[:revert] = false opts = OptionParser.new do |opts| opts.banner = "Usage: oaradmin conf [-e conf_file] [-H conf_file [-n number]] [-R conf_file rev]" # edit the conf file opts.on("-e","--edit","Edit the conf file") do $options[:edit] = true end # Show the changes made on conf file opts.on("-H","--history","Show all changes made on the conf file") do $options[:history] = true end # Number of latest changes to display opts.on("-n","--number","Number of latest changes to display") do $options[:history_no] = true end # Revert to the conf file as it existed in a revision number opts.on("-R","--revert","Revert to the conf file as it existed in a revision number") do $options[:revert] = true end # help opts.on_tail("-h", "--help", "Show this message") do puts opts exit end end begin opts.parse ARGV rescue OptionParser::ParseError => no_erreur puts no_erreur puts opts exit(1) end # Other tests on syntax if ARGV.empty? puts opts exit(1) end if !( ( $options[:edit] && !$options[:history] && !$options[:history_no] && !$options[:revert] ) || # -e (!$options[:edit] && $options[:history] && !$options[:history_no] && !$options[:revert] ) || # -H (!$options[:edit] && $options[:history] && $options[:history_no] && !$options[:revert] ) || # -H -n (!$options[:edit] && !$options[:history] && !$options[:history_no] && $options[:revert] ) ) # -R puts $msg[0] puts opts exit(1) end case when $options[:edit] # Edit conf file status, file_name = Conf_file.test_params if status == 1 $stderr.puts $msg[1] puts opts exit(1) end if status == 2 $stderr.puts $msg[2] puts opts exit(1) end if !File.readable?(file_name) $stderr.puts "Error : file "+file_name+" not found or unreadable" status = exit(1) end $config=Oar.load_configuration editor, directory = Edit.env($config) # edit file rule = Rule.new(nil, nil) rule.file_name = directory + "OAR_tmp_" + File.basename(file_name) rule.editor = editor rule.context="file" rule.script = "" File.open(file_name) do |file| while line = file.gets rule.script << line end end file_content_before_update = rule.script status, user_choice = rule.edit exit(2) if status != 0 if status==0 && user_choice==0 repository = Repository.new if !repository.active repository.display_status else repository.create end # if !repository.active # update file begin f = File.open(file_name, "w") f.print rule.script f.close puts "Update file " + file_name rescue Exception => e $stderr.puts "Error while updating file "+file_name $stderr.puts e.message exit(2) end if repository.active && repository.exists repository.file_name = File.basename(file_name) # First add in repository before update if file does not exist if !repository.file_exist? repository.file_content=file_content_before_update if repository.write==0 repository.add repository.log_commit = "Add file before update with oaradmin conf" repository.silent_mode=true repository.commit repository.silent_mode=false end end # Update in repository repository.file_content=rule.script if repository.write==0 repository.log_commit = "Update file " repository.commit end end # if repository.active end # if status==0 && user_choice==0 when $options[:history] # Show the changes made on the file status, file_name = Conf_file.test_params if status != 0 $stderr.puts $msg[1] if status==1 $stderr.puts $msg[2] if status==2 $stderr.puts $msg[3] if status==3 puts opts exit(1) end revisions = Revisions.new(File.basename(file_name)) exit(3) if !revisions.active || !revisions.exists if $options[:history_no] (0..ARGV.length-1).each do |i| if ARGV[i] == "-n" || ARGV[i] == "--number" if i < ARGV.length-1 && ARGV[i+1].to_i > 0 revisions.display_diff_changes = ARGV[i+1] else $stderr.puts "The number of changes must be greater than zero" exit(3) end end end end status = revisions.display_diff exit(3) if status != 0 when $options[:revert] # Revert to the file as it existed in a revision number status, file_name, rev = Conf_file.test_params2 if status != 0 puts $msg[4] puts opts exit(4) end if rev.to_i == 0 puts $msg[5] exit(4) end revisions = Revisions.new(File.basename(file_name)) exit(4) if !revisions.active || !revisions.exists revisions.rev_id = rev.to_i status = revisions.retrieve_file_rev exit(4) if status != 0 # We have a content for the file # Test if the specified file in command line exists or not # If exist, try to versioning it before overwrite # It is not really necessary. Repository must have the older version of the file # But file can be change without using oaradmin conf. So the latest revision in repository # can be different from the file on disk ask_overwrite = resp_overwrite = false repository = Repository.new if File.exists?(file_name) if File.readable?(file_name) str = "" File.open(file_name) do |file| while line = file.gets str << line end end repository.file_name = File.basename(file_name) repository.file_content=str if repository.write==0 repository.log_commit = "Versioning file before overwrite it with --revert command" repository.silent_mode=true repository.commit else ask_overwrite = true end else $stderr.puts "[OARADMIN ERROR]: file " + file_name + " is unreadable" ask_overwrite = true end end # if File.exists?(file_name) if ask_overwrite puts "[OARADMIN]: Warning ! Versioning of file " + file_name + " is impossible" puts "[OARADMIN]: So, if you overwrite file, the current version will be lost" print "[OARADMIN]: Overwrite [N/y] ? " r = "" begin r = $stdin.gets.chomp end while ( r != "" && r != "N" && r != "y" ) resp_overwrite = r == "y" end if (ask_overwrite && resp_overwrite) || !ask_overwrite begin f = File.open(file_name, "w") f.print revisions.file_content f.close puts "Update file " + file_name rescue Exception => e $stderr.puts "Error while updating file "+file_name $stderr.puts e.message exit(4) end # Update repository repository.file_name = File.basename(file_name) repository.file_content=revisions.file_content if repository.write==0 repository.log_commit = "Update file" repository.silent_mode=false repository.commit end end # if (ask_overwrite && resp_overwrite) || !ask_overwrite end # case when 8 # ############# # Display usage # ############ subcommand_usage when 9 # ######################## # Print OAR version number # ######################## Oar.version_number end # Disconnect from database dbh.disconnect if dbh end end ##################### # MAIN PROGRAM ##################### if __FILE__ == $0 command=-1 $subcommand.each_index do |i| command = i if !$subcommand[i].nil? && !ARGV[0].nil? && (ARGV[0]==$subcommand[i][:short_form] || ARGV[0]==$subcommand[i][:long_form]) end if command == -1 $stderr.puts "Incoherence in specified subcommand" if !ARGV[0].nil? CLI.subcommand_usage end # New ARGV with options only ARGV.delete_at(0) CLI.invoke command, ARGV end ./oar-2.5.2/sources/core/qfunctions/oaradmin/oar_modules.rb0000755000175000017500000001045511757171206022025 0ustar plbplb#!/usr/bin/ruby # # $Id: oar_modules.rb 2008-04-05 16:00:00 ddepoisi $ # Contains ruby modules # # requirements: # ruby1.8 (or greater) module Oar # Search a oar conf file in differents path # search also in the directory of oar.conf # Return : # status : 0 file is readable / 1 otherwise # config_file : full path name of the file def Oar.conf_file_readable(config_file) status=1 full_path="" # search config file if File.readable?(config_file) full_path = config_file status=0 elsif ENV['OARCONFFILE'] path_tmp = File.split(ENV['OARCONFFILE'])[0] if File.readable?(path_tmp+"/"+config_file) full_path = path_tmp+"/"+config_file status=0 end elsif File.readable?("/etc/" + config_file) full_path = "/etc/" + config_file status=0 elsif File.readable?("/etc/oar/" + config_file) full_path = "/etc/oar/" + config_file status=0 end return status, full_path end # Oar.conf_file_readable # Load configuration # Search config file : # 1) in the current directory # 2) in $OARDIR directory # 3) /etc/ directory # return value : # Always return a hash # If the configuration file not found, the hash is empty def Oar.load_configuration result = 0 r = {} # search config file config_file = "oar.conf" if !File.readable?(config_file) if ENV['OARCONFFILE'] && File.readable?(ENV['OARCONFFILE']) config_file = ENV['OARCONFFILE'] elsif File.readable?("/etc/" + config_file) config_file = "/etc/" + config_file elsif File.readable?("/etc/oar/" + config_file) config_file = "/etc/oar/" + config_file else result = 1 end end # load parameters if result == 0 File.foreach(config_file) do |line| line.strip! # Skip comments and whitespace if (line[0] != ?# and line =~ /\S/ ) i = line.index('=') if (i) v = line[i + 1..-1].strip v = v[1..v.length-2] r[line[0..i - 1].strip] = v else r[line] = '' end end end end return r end # load_configuration # Print OAR version number def Oar.version_number version="OAR version : " conf = Oar.load_configuration dbh = Bdd.connect(conf) if dbh table = "schema" table = "`" + table + "`" if conf['DB_TYPE'].upcase == "MYSQL" q = "SELECT * FROM " + table + " ORDER BY version DESC" rows = dbh.select_one(q) version += rows["version"] if rows dbh.disconnect end puts version end # Oar.version_number end # module Oar # This module contains database features : access, update tables... using DBI # Return values : # dbh : Database handle # dbh == nil if an error occurs class Bdd class << self # Connection to the database # Params : conf is a hash def connect(conf) db_type = conf['DB_TYPE'] db_type = "Mysql" if db_type == "mysql" dbh = nil !conf['DB_PORT'].nil? && conf['DB_PORT'].to_i>1 && conf['DB_PORT'].to_i < 65535 ? db_port=";port="+conf['DB_PORT'] : db_port="" begin dbh = DBI.connect("dbi:#{db_type}:host=#{conf['DB_HOSTNAME']};database=#{conf['DB_BASE_NAME']}#{db_port}", "#{conf['DB_BASE_LOGIN']}","#{conf['DB_BASE_PASSWD']}") rescue DBI::DatabaseError => e $stderr.puts "Error during the connection to the database" $stderr.puts "Error code: #{e.err}" $stderr.puts "Error message: #{e.errstr}" end @connection = dbh end # def connect # Access to the database and catch errors # Parameters : # dbh : databasehandle # q : sql order # *params : parameters for sql order # Return : # status => code error if an error occurs def do(dbh, q, *params) status = 0 begin dbh.do(q, *params) rescue DBI::DatabaseError => e status = e.err $stderr.puts "Error access to the database" $stderr.puts "Error code: #{e.err}" $stderr.puts "Error message: #{e.errstr}" end status end # def do def connection @connection || raise(DBI::DatabaseError, "The SQL connection is not correctly set up") end end end # class Bdd ./oar-2.5.2/sources/core/qfunctions/oaraccounting0000755000175000017500000000355111757171206020153 0ustar plbplb#!/usr/bin/perl # $Id$ use strict; use warnings; use Data::Dumper; use OAR::IO; use OAR::Conf qw(init_conf dump_conf get_conf is_conf); use Getopt::Long; use OAR::Version; my $Old_umask = sprintf("%lo",umask()); umask(oct("022")); Getopt::Long::Configure ("gnu_getopt"); my $Version; my $Help; my $Delete_windows_before; my $Reinitialize; GetOptions ( "reinitialize" => \$Reinitialize, "delete-before=i" => \$Delete_windows_before, "help|h" => \$Help, "version|V" => \$Version ); if (defined($Version)){ print("OAR version : ".OAR::Version::get_version()."\n") ; exit(0); } if (defined($Help)){ print < delete every records number_of_seconds ago -h, --help show this help screen -V, --version print OAR version number EOS exit(0); } # Default window size my $windowSize = 86400; init_conf($ENV{OARCONFFILE}); if (is_conf("ACCOUNTING_WINDOW")){ $windowSize = get_conf("ACCOUNTING_WINDOW"); } my $base = OAR::IO::connect_one(); my $lockName = "ACCOUNTING"; OAR::IO::get_lock($base,$lockName,3600); if (defined($Reinitialize)){ print("Deleting all records from the acounting table...\n"); OAR::IO::delete_all_from_accounting($base); }elsif (defined($Delete_windows_before)){ print("Deleting records older than $Delete_windows_before seconds ago...\n"); $Delete_windows_before = OAR::IO::get_date($base) - $Delete_windows_before; OAR::IO::delete_accounting_windows_before($base,$Delete_windows_before); }else{ OAR::IO::check_accounting_update($base,$windowSize); } OAR::IO::release_lock($base,$lockName); OAR::IO::disconnect($base); exit 0; ./oar-2.5.2/sources/core/modules/0000755000175000017500000000000011757171206014644 5ustar plbplb./oar-2.5.2/sources/core/modules/scheduler/0000755000175000017500000000000011757171206016622 5ustar plbplb./oar-2.5.2/sources/core/modules/scheduler/test_gant.pl0000755000175000017500000000624211757171206021156 0ustar plbplb#!/usr/bin/perl -I../Iolib -I../ConfLib -w # $Id$ use strict; use Data::Dumper; use OAR::IO; use OAR::Schedulers::OarResourceTree; use OAR::Schedulers::GanttHoleStorage; my $max = 30; my $gantt = OAR::Schedulers::GanttHoleStorage::new($max); my $vec = ''; vec($vec,3,1) = 1; OAR::Schedulers::GanttHoleStorage::add_new_resources($gantt, $vec); $vec = ''; vec($vec,2,1) = 1; vec($vec,1,1) = 1; OAR::Schedulers::GanttHoleStorage::add_new_resources($gantt, $vec); #$vec = ''; #for (my $i = 0; $i < $max; $i++){ # vec($vec,$i,1) = 1; #} #print("---S\n"); #OAR::Schedulers::GanttHoleStorage::add_new_resource($gantt, $vec); #print("---E\n"); #$vec = ''; #vec($vec,100000,1) = 1; #OAR::Schedulers::GanttHoleStorage::add_new_resource($gantt, $vec); #sleep 10; #print vec($gantt->[3]->[0]->[1], 30, 1)."\n"; #print vec($gantt->[4], 5, 1)."\n"; #print unpack("b*",$gantt->[4])."\n"; #print unpack("%2b*",$gantt->[4])."\n"; #Gantt::set_occupation($gantt, 20, 5, "2"); #Gantt::set_occupation($gantt, 26, 10, "2"); #Gantt::set_occupation($gantt, 40, 5, "2"); #Gantt::set_occupation($gantt, 20, 5, "2"); #Gantt::set_occupation($gantt, 20, 24, "2"); #print(Gantt::pretty_print($gantt)."\n"); #print(Dumper($gantt)); #print(Gantt::is_resource_free($gantt, 337, 1, "node1")); #print(Gantt::pretty_print($gantt)."\n"); # my $base = OAR::IO::connect(); #my @r = OAR::IO::list_resources($base); #print(Dumper(@r)); #foreach my $i (@r){ # Gantt::add_new_resource($gantt, $i->{resourceId}); #} $vec = ''; #vec($vec,1,1) = 1; #vec($vec,2,1) = 1; #vec($vec,3,1) = 1; #OAR::Schedulers::GanttHoleStorage::set_occupation($gantt, 2, 30, $vec); $vec = ''; vec($vec,3,1) = 1; OAR::Schedulers::GanttHoleStorage::set_occupation($gantt, 5, 5, $vec); vec($vec,2,1) = 1; OAR::Schedulers::GanttHoleStorage::set_occupation($gantt, 8, 5, $vec); $vec = ''; vec($vec,1,1) = 1; OAR::Schedulers::GanttHoleStorage::set_occupation($gantt, 5, 10, $vec); #OAR::Schedulers::GanttHoleStorage::set_occupation($gantt, 2, 1, $vec); #$vec = ''; #vec($vec,1,1) = 1; #OAR::Schedulers::GanttHoleStorage::set_occupation($gantt, 7, 8, $vec); #$vec = ''; #vec($vec,3,1) = 1; #OAR::Schedulers::GanttHoleStorage::set_occupation($gantt, 13, 3, $vec); #print(Dumper($gantt)); OAR::Schedulers::GanttHoleStorage::pretty_print($gantt); $vec = ''; vec($vec,2,1) = 1; vec($vec,1,1) = 1; print(OAR::Schedulers::GanttHoleStorage::is_resources_free($gantt,2,2,$vec)."\n"); exit; #for (my $i=100000; $i > 1000; $i-=101){ # Gantt::set_occupation($gantt, $i, $i-50, "4"); #} #print(Gantt::pretty_print($gantt)."\n"); #exit; print("INIT\n"); #print(Dumper($gantt)); my $resGroup = OAR::IO::get_resources_data_structure_job($base, 2); print(Dumper($resGroup)); my $h1 = OAR::IO::get_possible_wanted_resources($base,[],[],"", $resGroup->[0]->[0]->[0]->{resources}); print(Dumper($h1)); #my $data = (); #$data->[0] = { # "resources" => $resGroup->[0]->[0]->[0]->{resources}, # "tree" => $h1 #}; #print(Dumper($data)); my @a = Gantt::find_first_hole($gantt,3, 30, [$h1]); print(Dumper(@a)); print("TO_OT\n"); #print(Gantt::pretty_print($gantt)."\n"); foreach my $t (@{$a[1]}){ print(Dumper(OAR::Schedulers::ResourceTree::delete_unnecessary_subtrees($t))); } #sleep 30; ./oar-2.5.2/sources/core/modules/scheduler/test/0000755000175000017500000000000011757171206017601 5ustar plbplb./oar-2.5.2/sources/core/modules/scheduler/test/scheduler_test_documentation.rst0000644000175000017500000000231411757171206026301 0ustar plbplb Schedulers Testing ==================== These documentation notes are only for schedulers. Tests are set of ruby scripts which help to manipulate database and to launch targed scheduler. Requirements ------------ Before testing, database must be initiated by *oar_mysql_db_init.pl* or/and *oar_psql_db_init.pl* script together with *oar.conf*. Scheduler to test must located accordingly to your installation, by example in /usr/lib/oar/schedulers/ or /usr/local/lib/oar/schedulers/ directory. Tests *must* be launched as oar or root. Scritps and files purposes -------------------------- oar_db_setting.rb: provides function to manipulate oar tables. It uses ./oar_test_conf as oar.conf. oar_test_scheduler_helpers.rb: scheduler_test.rb: Usage and tips --------------- * irb enhancement with wirble (completion, color and so on) http://pablotron.org/software/wirble/ * halt oar server $ sudo service oar-server stop * time conversion in ruby >> Time.now.to_i => 1313765725 >> Time.at(1313765725) => Fri Aug 19 16:55:25 0200 2011 * launch irb with oar_db_setting.rb preloading (it uses ./oar_test_conf as oar.conf) $ irb -r oar_db_setting.rb * launch tests $ ./scheduler_test.rb scheduler ./oar-2.5.2/sources/core/modules/scheduler/test/scheduler_test.rb0000644000175000017500000000267211757171206023152 0ustar plbplbrequire 'oar_db_setting' require 'oar_test_scheduler_helpers' require 'test/unit' require 'getoptlong' $db_state = 'unready' opts = GetoptLong.new( [ '--help', '-h', GetoptLong::NO_ARGUMENT ], [ '--scheduler', '-s', GetoptLong::REQUIRED_ARGUMENT ], [ '--db_type', '-d', GetoptLong::REQUIRED_ARGUMENT ] ) # process the parsed options opts.each do |opt, arg| puts "Option: #{opt}, arg #{arg.inspect}" end puts "Remaining args: #{ARGV.join(', ')}" class TestBase < Test::Unit::TestCase @@db_up = false def setup if (!@@db_up) oar_db_clean #clean jobs and resources oar_db_resource("core=16") oar_conf_create $db_up = true else oar_db_truncate_jobs end # test 1 simple job def test_one_job jid = oar_job_insert assert(oar_job(:fisrt end # test backfilling def test_backfilling end end class TestNormal < Test::Unit::TestCase def setup end def test_previous_job end def test_ressource_matching end def test_hierarchy end def test_multiple_resource_requests end def test_multiple_resource_types end def job_container end def test_besteffort end def test_job_depencies end end class TestBasicError < Test::Unit::TestCase def setup end def test_notenought_resources end end class TestAdvanced < Test::Unit::TestCase def setup end def test_all_ressources end def test_best_ressources end end ./oar-2.5.2/sources/core/modules/scheduler/test/oar_test_scheduler_helpers.rb0000644000175000017500000000657011757171206025536 0ustar plbplb#!/usr/bin/ruby require 'oar_db_setting.rb' def oar_conf_add_hierarchy nb_resources hierarchy_conf = "HIERARCHY_LABELS=\"resource_id\"\nresource_id=\"(1,1,#{nb_resources})\"" system "cat oar_base.conf > oar.conf; echo '#{hierarchy_conf}' >> oar.conf" end def oar_conf_modify_hierarchy_1h nb_resources system "sudo sed 's/resource_id=\"(1,1,[0-9]*)\"/resource_id=\"(1,1,#{nb_resources})\"/' /etc/oar/oar.conf > /tmp/oar.conf_tmp; sudo sh -c 'cat /tmp/oar.conf_tmp > /etc/oar/oar.conf'" end def oar_resources_1h(nb_ressources) oar_truncate_resources nb_ressources.times do |i| oar_resource_insert end end def oar_resources_3h(h_r) #HIERARCHY_LABELS="node,cpu,core" #host="(1,16,2), (33,8,4)" start_index/size_block/nb_blocl #cpu="(1,8,8)" #core="(1,1,64)" #oar_resources_3h {:host=>[[1,16,2], [33,8,4]],:cpu=>[1,8,8],:core=>[1,1,64]} oar_truncate_resources puts h_r h_ids = {:host=>[],:cpu=>[],:core=>[]} h_r.each do |k,v| i=1 if v.first.is_a?(Array) v.each do |a| a[2].times do #nb_blocks a[1].times do #block_size h_ids[k] << i end i += 1 end end else v[2].times do #nb_blocks v[1].times do #block_size h_ids[k] << i end i += 1 end end end h_ids[:core].each_with_index do |c_id,i| host = h_ids[:host][i] cpu = h_ids[:cpu][i] core = c_id $dbh.execute("insert into resources (state, host, cpu, core) values ('Alive', #{host},#{cpu},#{core})").finish end end def timed_exec sched t0 = Time.now; e = system sched; t1 = Time.now return t1-t0,e end def bench_stairway (steps,sched) if steps.class == Fixnum steps = [s] end steps.each do |k| oar_db_clean k.times do |i| oar_resource_insert end oar_conf_add_hierarchy k k.times do |i| oar_job_insert(:res=>"resource_id=#{i}",:walltime=> 300) end t,e = timed_exec sched if e puts "bench_stairway (nb,tps) #{k} #{t}" else puts "bench_stairway ERROR system result = FASLE (nb) #{k}" end end end def simple_scheduler_test(nb_resources=100, nb_jobs=100) if (nb_jobs > nb_resources) puts "Nb resources must be >= nb jobs" else oar_db_clean; oar_truncate_gantt; oar_reset_all_jobs puts "Create Ressources" oar_resource_insert(:nb_resources=>nb_resources) oar_conf_modify_hierarchy_1h(nb_resources) puts "Create Jobs" nb_jobs.times do |i| oar_job_insert(:res=>"resource_id=#{i+1}",:walltime=> 300) end puts "Launch Scheduler" tps, ok = timed_exec "sudo /usr/local/lib/oar/schedulers/simple_cbf_mb_h_ct_oar_mysql" pp [tps, ok] end end def simple_scheduler_test_dec(nb_resources=100, nb_jobs=100) if (nb_jobs > nb_resources) puts "Nb resources must be >= nb jobs" else oar_db_clean; oar_truncate_gantt; oar_reset_all_jobs puts "Create Ressources" oar_resource_insert(:nb_resources=>nb_resources) oar_conf_modify_hierarchy_1h(nb_resources) puts "Create Jobs" job_nb_res = nb_resources-1 nb_jobs.times do |i| job_nb_res -= 1 if job_nb_res >= 2 puts "job #{i}: resource_id=#{job_nb_res} walltime=> 300" oar_job_insert(:res=>"resource_id=#{job_nb_res}",:walltime=> 300) end puts "Launch Scheduler" tps, ok = timed_exec "sudo /usr/local/lib/oar/schedulers/simple_cbf_mb_h_ct_oar_mysql" pp [tps, ok] end end ./oar-2.5.2/sources/core/modules/scheduler/test/oar_db_setting.rb0000755000175000017500000004522411757171206023123 0ustar plbplb#!/usr/bin/ruby require 'dbi' require 'yaml' require 'rest_client' require 'pp' DEFAULT_JOB_ARGS = { :queue => "default", :walltime => 7200, :res => "resource_id=1", :propreties => "", :type => nil, :user => "toto" } SCHED_PERL_FS = "/usr/local/lib/oar/schedulers/oar_sched_gantt_with_timesharing_and_fairsharing" SCHED_OCAML = "/usr/local/lib/oar/schedulers/simple_cbf_mb_h_ct_oar_mysql" SCHED_KAMELOT = "/usr/local/lib/oar/schedulers/kamelot_mysql" def oar_load_test_config puts "### Reading configuration ./oar_test_conf file ..." $conf = YAML::load(IO::read('./oar_test.conf')) $db_type = $conf['DB_TYPE'] puts "DB TYPE: #{$conf['DB_TYPE']}" pp $conf end def oar_db_connect # if $db_type == "mysql" # $db_type = "Mysql" # else # $db_type = "Pg" #postgresql # end puts "dbi:#{$db_type}:#{$conf['DB_BASE_NAME']}:#{$conf['DB_HOSTNAME']}", "#{$conf['DB_BASE_LOGIN']}","#{$conf['DB_BASE_PASSWD']}" $dbh = DBI.connect("dbi:#{$db_type}:#{$conf['DB_BASE_NAME']}:#{$conf['DB_HOSTNAME']}", "#{$conf['DB_BASE_LOGIN']}","#{$conf['DB_BASE_PASSWD']}") puts "DB Connection Establised" end def oar_db_disconnect $dbh.disconnect end def get_last_insert_id(seq) id = 0 if ($db_type == "Mysql" || $db_type == "mysql" ) id=$dbh.select_one("SELECT LAST_INSERT_ID()")[0] else id=$dbh.select_one("SELECT CURRVAL('#{seq}')")[0] end return id end #$jobs = DB[:jobs] #$moldable = DB[:moldable_job_descriptions] #$job_resource_groups = DB[:job_resource_groups] #$job_resource_description = DB[:job_resource_descriptions] #$job_types = DB[:job_types] #$resources = DB[:resources] # "{ sql1 }/prop1=1/prop2=3+{sql2}/prop3=2/prop4=1/prop5=1+...,walltime=1:00:00" # "/switch=2/nodes=10+{type = 'mathlab'}/licence=20" # oar_job_insert({:res=>"host=1/cpu=2/core=2+cpu=1/core=2"}) def oar_job_insert(j_args={}) args = {} DEFAULT_JOB_ARGS.each do |k,v| if j_args[k].nil? args[k]=v else args[k]=j_args[k] end end sth = $dbh.execute("insert into jobs (job_name,state,queue_name,properties,launching_directory,checkpoint_signal,job_user) values ('yop','Waiting','#{args[:queue]}','#{args[:properties]}','yop',0,'#{args[:user]}')") sth.finish job_id= get_last_insert_id('jobs_job_id_seq') #moldable_id = $moldable.insert(:moldable_job_id => job_id, :moldable_walltime => walltime) $dbh.execute("insert into moldable_job_descriptions (moldable_job_id,moldable_walltime) values (#{job_id},#{args[:walltime]})").finish moldable_id = get_last_insert_id('moldable_job_descriptions_moldable_id_seq') args[:res].split("+").each do |r| #res_group_id = $job_resource_groups.insert(:res_group_moldable_id => moldable_id, :res_group_property => 'type = "default"') $dbh.execute("insert into job_resource_groups (res_group_moldable_id,res_group_property) values (#{moldable_id},'type = ''default''')").finish res_group_id = get_last_insert_id('job_resource_groups_res_group_id_seq') r.split('/').each_with_index do |type_value,order| type,value = type_value.split('=') #$job_resource_description.insert(:res_job_group_id => res_group_id.to_i, :res_job_resource_type => type, :res_job_value => value.to_i, :res_job_order => order.to_i) $dbh.execute("insert into job_resource_descriptions (res_job_group_id, res_job_resource_type, res_job_value, res_job_order ) values (#{res_group_id.to_i},'#{type}',#{value.to_i},#{order.to_i})").finish end end #job's types insertion if !args[:types].nil? !args[:types].split(',').each do |type| # $job_types.insert(:job_id => job_id, :type => type) $dbh.execute("insert into job_types (job_id, type) values (#{job_id},'#{type}')").finish end end return job_id end def multiple_requests_execute(reqs) #Strange dbi_mysql doesn't accept multiple request in one dbh.execute ??? reqs.split(';').each do |r| if (r =~ /\w/) $dbh.execute(r).finish end end end def oar_truncate_jobs # DB << " requests = " TRUNCATE accounting; TRUNCATE assigned_resources; TRUNCATE challenges; TRUNCATE event_logs; TRUNCATE event_log_hostnames; TRUNCATE files; TRUNCATE frag_jobs; TRUNCATE gantt_jobs_predictions; TRUNCATE gantt_jobs_predictions_log; TRUNCATE gantt_jobs_predictions_visu; TRUNCATE gantt_jobs_resources; TRUNCATE gantt_jobs_resources_log; TRUNCATE gantt_jobs_resources_visu; TRUNCATE jobs; TRUNCATE job_dependencies; TRUNCATE job_resource_descriptions; TRUNCATE job_resource_groups; TRUNCATE job_state_logs; TRUNCATE job_types; TRUNCATE moldable_job_descriptions; TRUNCATE resource_logs; " multiple_requests_execute(requests) end def oar_update_visu requests = " DELETE FROM gantt_jobs_predictions_visu; DELETE FROM gantt_jobs_resources_visu; INSERT INTO gantt_jobs_predictions_visu SELECT * FROM gantt_jobs_predictions; INSERT INTO gantt_jobs_resources_visu SELECT * FROM gantt_jobs_resources; " multiple_requests_execute(requests) end def oar_truncate_gantt requests = " TRUNCATE gantt_jobs_predictions; TRUNCATE gantt_jobs_predictions_log; TRUNCATE gantt_jobs_predictions_visu; TRUNCATE gantt_jobs_resources; TRUNCATE gantt_jobs_resources_log; TRUNCATE gantt_jobs_resources_visu; " multiple_requests_execute(requests) end def oar_sql_file(file_name) if $db_type == "mysql" puts "mysql --user=#{$conf['DB_BASE_LOGIN']} --password= #{$conf['DB_BASE_PASSWD']} #{$conf['DB_BASE_NAME']} < #{file_name}" system("mysql --user=#{$conf['DB_BASE_LOGIN']} --password=#{$conf['DB_BASE_PASSWD']} #{$conf['DB_BASE_NAME']} < #{file_name}") else puts "Sorry not implemented" end # requests = File.open(file_name, "r").read # multiple_requests_execute(requests) end def oar_resource_insert(args={}) if (args=={}) $dbh.execute("insert into resources (state) values ('Alive')").finish else if !args[:nb_resources].nil? nb_res = args[:nb_resources].to_i nb_100 = nb_res/100 nb_residual = nb_res - 100 * nb_100 puts "nb_100: #{nb_100} , nb_residual: #{nb_residual}" if (nb_100>0) ressources_100 = ("('localhost','Alive')," * 100).chop nb_100.times do $dbh.execute("insert into resources (network_address, state) values #{ressources_100}").finish end end if (nb_residual>0) nb_residual_ressources = ("('localhost','Alive')," * nb_residual).chop $dbh.execute("insert into resources (network_address, state) values #{nb_residual_ressources}").finish end end end end def oar_test_insert(k,x, alter=false) oar_truncate_resources puts "nb_insert: #{k}, size of insert in nb_resources: #{x} nb_ressources: #{k*x} alter: #{alter}" t0 = Time.now ressources = ("('localhost','Alive')," * x).chop t_string = Time.now - t0 puts "t_string: #{t_string}" t0 = Time.now $dbh.execute("ALTER TABLE resources DISABLE KEYS").finish if alter k.times do $dbh.execute("insert into resources (network_address, state) values #{ressources}").finish end $dbh.execute("ALTER TABLE resources ENABLE KEYS").finish if alter t_insert = Time.now - t0 puts "t_insert: #{t_insert}" puts "t_total: #{t_string+t_insert} t_string: #{t_string} t_insert: #{t_insert}" end def oar_truncate_resources # DB << " requests = " TRUNCATE resources; TRUNCATE resource_logs; " multiple_requests_execute(requests) end def oar_db_clean oar_truncate_jobs oar_truncate_resources end def get_start_time(job_id) $dbh.execute("select jobs.start_time from jobs where jobs.job_id=#{job_id}").first.first end def get_start_stop_time(job_id) res=$dbh.execute("select jobs.start_time, jobs.stop_time from jobs where jobs.job_id=#{job_id}") r = res.first res.finish return r end def delete_assignements_from_start_time(start_time) # $dbh.execute("SELECT * FROM assigned_resources,jobs, moldable_job_descriptions WHERE # jobs.start_time > #{start_time} AND # jobs.assigned_moldable_job = assigned_resources.moldable_job_id )" $dbh.execute("DELETE assigned_resources FROM assigned_resources,jobs, moldable_job_descriptions WHERE jobs.start_time > #{start_time} AND jobs.assigned_moldable_job = assigned_resources.moldable_job_id") end # limitations # * advance reservation # * submission time is not translated def reset_job_from_start_time(start_time, now, delay = 10) # Running jobs: # change state # change start time and stop time delta = now - start_time $dbh.execute("UPDATE jobs SET state='Running', start_time = #{delta} + jobs.start_time, stop_time = 0 WHERE start_time < #{start_time} AND stop_time > #{start_time} ") # Reset future jobs # delete_assignements delete_assignements_from_start_time(start_time) # change state waiting $dbh.execute("UPDATE jobs SET state='Waiting' WHERE jobs.start_time > #{start_time}") end # reset all jobs to state=waiting, remove assigned resources and switch index to CURRENT def oar_reset_all_jobs(state="'Waiting'") $dbh.execute("TRUNCATE assigned_resources") $dbh.execute("UPDATE jobs SET state=#{state}") $dbh.execute("UPDATE moldable_job_descriptions SET moldable_index = 'CURRENT'") $dbh.execute("UPDATE job_resource_groups SET res_group_index = 'CURRENT'") $dbh.execute("UPDATE job_resource_descriptions SET res_job_index = 'CURRENT'") $dbh.execute("UPDATE job_resource_groups SET res_group_index = 'CURRENT'") $dbh.execute("UPDATE job_resource_descriptions SET res_job_index = 'CURRENT'") end # oar_jobs_sleepify: # Remove previous allocations # Sets command field by sleep with job execution time as argument and jobs' state to hold. # Returns array which contains job_ids and corresponding submission times begin from 0 (first submitted job) def oar_jobs_sleepify(user=ENV['USER']) resume_seq=[] # Remove previous allocations requests = " TRUNCATE accounting; TRUNCATE assigned_resources; TRUNCATE challenges; TRUNCATE event_logs; TRUNCATE event_log_hostnames; TRUNCATE files; TRUNCATE frag_jobs; TRUNCATE gantt_jobs_predictions; TRUNCATE gantt_jobs_predictions_log; TRUNCATE gantt_jobs_predictions_visu; TRUNCATE gantt_jobs_resources; TRUNCATE gantt_jobs_resources_log; TRUNCATE gantt_jobs_resources_visu; TRUNCATE job_state_logs; TRUNCATE resource_logs; " multiple_requests_execute(requests) oar_reset_all_jobs("'Hold'") q = "SELECT job_id, submission_time, start_time, stop_time FROM jobs" puts "Be careful we're scanning all jobs" res = $dbh.execute(q) orig_subtime = 0 res.each do |j| execution_time = j[3]-j[2] if orig_subtime == 0 orig_subtime = j[1] end subtime = j[1] - orig_subtime puts "job_id: #{j[0]} start_time: #{j[1]} modify_subtime: #{subtime} execution_time: #{execution_time}" resume_seq.push([j[0],subtime]) # new to add user switching $dbh.execute("UPDATE jobs SET command = 'sleep #{execution_time}', launching_directory = '/tmp', stdout_file= '/tmp/oar.#{j[0]}.out', stderr_file= '/tmp/oar.#{j[0]}.err', job_user = '#{user}' WHERE job_id = #{j[0]}") end res.finish return resume_seq end def oar_replay(sequence,user=ENV['USER']) # RestClient.post 'http://kameleon:kameleon@localhost/oarapi-priv/jobs/1/resumptions/new.yaml','' ref_time = Time.now.to_f + 1 #puts "ref_time: #{ref_time}" sequence.each do |step| job_id, release_time = step time2sleep = release_time - (Time.now.to_f - ref_time) sleep time2sleep if (time2sleep > 0) puts "Release job:#{job_id} error_release_time: #{Time.now.to_f - ref_time - release_time}" #puts "Release job:#{job_id} release_time: #{release_time} effective_release_time: #{Time.now.to_f - ref_time}" if user=="kameleon" RestClient.post "http://kameleon:kameleon@localhost/oarapi-priv/jobs/#{job_id}/resumptions/new.yaml",'' else RestClient.post "http://localhost/oarapi/jobs/#{job_id}/resumptions/new.yaml",'' end end end #SELECT assigned_resources.resource_id #FROM jobs, assigned_resources, moldable_job_descriptions, resources #WHERE # jobs.job_id = 2 AND # jobs.assigned_moldable_job = assigned_resources.moldable_job_id AND # moldable_job_descriptions.moldable_job_id = 2 AND # resources.resource_id = assigned_resources.resource_id def oar_job_times(job_id) end def oar_job_resources(job_id) q = " SELECT assigned_resources.resource_id FROM jobs, assigned_resources, moldable_job_descriptions WHERE jobs.job_id = #{job_id} AND jobs.assigned_moldable_job = assigned_resources.moldable_job_id AND moldable_job_descriptions.moldable_job_id = #{job_id}" res = $dbh.execute(q) r=[] res.each do |r_id| r << r_id.first end res.finish return r end def oar_jobs_overlap? q = "SELECT job_id, start_time, stop_time FROM jobs" puts "Be careful we're scanning all jobs" res = $dbh.execute(q) previous_jobs = [] print "Test job:" res.each do |j| print "#{j[0]} " r = oar_job_resources(j[0]) previous_jobs.each do |k| if ((j[1]>k[1]) and (j[1]k[1]) and (j[2]k[2])) if (r&k[3])!=[] puts puts "Jobs overlap: #{j[0]} #{k[0]}" pp [j[0],j[1],j[2],r] pp k end end end previous_jobs.push([j[0],j[1],j[2], oar_job_resources(j[0])]) #oar_job_resources ugly (bad performance) end end def oar_jobs_overlap_after_scheduling?(security_time=60) puts "oar_jobs_overlap_after_scheduling?" puts "/!\\ BE CAREFULL, we suppose moldable_id == job id /!\\" puts "Security_time: #{security_time}" #Get already scheduled (with running, launching or tolaunch jobs) q1 = "SELECT j.job_id, g2.start_time, m.moldable_walltime, g1.resource_id, j.state,m.moldable_id FROM gantt_jobs_resources g1, gantt_jobs_predictions g2, moldable_job_descriptions m, jobs j WHERE m.moldable_index = 'CURRENT' AND g1.moldable_job_id = g2.moldable_job_id AND m.moldable_id = g2.moldable_job_id AND j.job_id = m.moldable_job_id ORDER BY j.start_time, j.job_id;" puts "We're scanning 'Running' jobs" r1 = $dbh.execute(q1) end_time={} sched_ressources = {} r1.each do |r| if end_time[r[0]].nil? end_time[r[0]] = r[1] + r[2] + security_time sched_ressources[r[0]] = [] raise "/!\\ job_id not equal to .moldable_id : #{r[0]} #{r[5]}" if r[0] != r[5] end sched_ressources[r[0]] << r[3] end # # Get newly_scheduled jobs # n_start_time={} n_sched_ressources = {} puts "/!\\ Get scheduled job information. BE CAREFULL, we suppose moldable_id == job id /!\\" q2 = "SELECT moldable_job_id, start_time FROM gantt_jobs_predictions" r2 = $dbh.execute(q2) r2.each do |r| n_start_time[r[0]] = r[1] n_sched_ressources[r[0]] = [] end r2.finish # TODO TOREMOVE ??? # q3 = "SELECT moldable_job_id,resource_id FROM gantt_jobs_resources" # r3 = $dbh.execute(q3) # r3.each do |r| # sched_ressources[r[0]] << r[1] # end # r3.finish puts "test overlapping" #iterate on newly scheduled job n_start_time.each do |jid,start_time| #iterate on "running" job end_time.each do |r_jid,e_time| if (e_time > start_time) #test resources overlap if (n_sched_ressources[jid] & sched_ressources[r_jid])!=[] puts "jobs overlapping #{rjid} #{jid}" pp [r_jid, end_time, sched_ressources[r_jid]] pp [jid, start_time, n_sched_ressources[jid]] end end end end puts "oar_jobs_overlap_after_scheduling? end" end # # oar_sleepy: populate db from dump (oar.ocaml.12res.20110323.sql), reset jobs' state to wainting, set scheduler to ocaml and re # def oar_sleepfy puts "/!\\ BE CAREFULL, not portable action...surely it'll fail /!\\" oar_sql_file("/home/auguste/prog/test_oar_sched/oar.ocaml.12res.20110323.sql") oar_sql_file("/home/auguste/oar/sources/core/database/mysql_default_admission_rules.sql") $dbh.execute("UPDATE queues SET scheduler_policy='simple_cbf_mb_h_ct_oar_mysql', state='Active' WHERE queue_name='default'").finish return oar_jobs_sleepify end def oar_fairsharing_test sched #TODO test if oar running puts "Be carefull be sure to oar-server is stopped" now = Time.now.to_i oar_db_clean users = [] if false oar_sql_file("/home/auguste/prog/test_oar_sched/oar_foehn+nanostar-accounting.sql") users = ["debreu","pianezj","lebacq","gallee","drouet","wiesenfe","meunie8x","thibert","chardon","lafaysse"] else 10.times do |u| user = "zozo"+u.to_s users.push user 10.times do |i| j = 24 * 36000 w_start = now - j*(i+1) w_stop = now - j*(i+1) + j/10 consum = 100000 * (10-u) sth = $dbh.execute("insert into accounting (window_start,window_stop,accounting_user,accounting_project,queue_name,consumption_type,consumption) values (#{w_start},#{w_stop},'#{user}','default','default','USED',#{consum})") sth.finish sth = $dbh.execute("insert into accounting (window_start,window_stop,accounting_user,accounting_project,queue_name,consumption_type, consumption) values (#{w_start},#{w_stop},'#{user}','default','default','ASKED',#{consum})") sth.finish end end end #add ressources nb_r = 12 oar_resource_insert({:nb_resources=>nb_r}) #add jobs jids = [] users.each do |u| puts "add job for user: #{u}" 5.times do jids.push oar_job_insert(:res=>"resource_id=#{nb_r}",:walltime=> 300, :user=>u) end end puts jids #execute sched sched_cmd = 'OARCONFFILE="/etc/oar/oar.conf" '+ sched + " default #{Time.now.to_i}" puts "Launch sched: #{sched}" puts "cmd: #{sched_cmd}" system sched_cmd res=$dbh.execute("SELECT * FROM gantt_jobs_predictions") h_job_time = {} res.each do |r| h_job_time[r[0]]=r[1] end job_time_sorted = h_job_time.sort_by { |id, t_start| t_start } return job_time_sorted end # # strip oar.conf: remove test config from /etc/oar/oar.conf to /tmp/oar.conf # def oar_strip_config system("rm /tmp/oar.conf") oar_tmp = File.open("/tmp/oar.conf", "w") end_tag = "#BEGIN TEST CONFIGURATION" File.open("/etc/oar/oar.conf").each do |line| break if line =~ /#{end_tag}/ oar_tmp.puts line end oar_tmp.puts end_tag oar_tmp.close system "sudo sh -c 'cat /tmp/oar.conf_tmp > /etc/oar/oar.conf'" end oar_load_test_config oar_db_connect if ($0=='irb') puts 'irb session detected, db connection launched' elsif (/oar_db_setting/ =~ $0) puts "oar_db_setting used as command" eval(ARGV[0]) end # 50.times do |i| oar_job_insert(:res=>"resource_id=#{i}",:walltime=> 300) end ./oar-2.5.2/sources/core/modules/scheduler/test/README0000644000175000017500000000046211757171206020463 0ustar plbplb This directory contains files and scripts to simplify scheduler testing. These tests are focused only on scheduling phase and dont' encompass other phases like launching, runing, checking and so on. To execute theses tests only oar database and scheduler are required (and meta-scheduler for some tests). ././@LongLink0000000000000000000000000000015400000000000011565 Lustar rootroot./oar-2.5.2/sources/core/modules/scheduler/oar_sched_gantt_with_timesharing_and_fairsharing_and_placeholder./oar-2.5.2/sources/core/modules/scheduler/oar_sched_gantt_with_timesharing_and_fairsharing_and_plac0000755000175000017500000007676511757171206032366 0ustar plbplb#!/usr/bin/perl # $Id$ #-d:DProf use strict; use DBI(); use OAR::IO; use Data::Dumper; use OAR::Modules::Judas qw(oar_debug oar_warn oar_error set_current_log_category); use OAR::Conf qw(init_conf dump_conf get_conf is_conf get_conf_with_default_param); use OAR::Schedulers::GanttHoleStorage; use Storable qw(dclone); use Time::HiRes qw(gettimeofday); # Log category set_current_log_category('scheduler'); init_conf($ENV{OARCONFFILE}); ############################################################################### # Fairsharing parameters # ########################## # Avoid problems if there are too many waiting jobs my $Karma_max_number_of_jobs_treated_per_user = 30; if (is_conf("SCHEDULER_FAIRSHARING_MAX_JOB_PER_USER")) { $Karma_max_number_of_jobs_treated_per_user = get_conf("SCHEDULER_FAIRSHARING_MAX_JOB_PER_USER"); } # number of seconds to consider for the fairsharing my $Karma_window_size = 3600 * 30 * 24; if (is_conf("SCHEDULER_FAIRSHARING_WINDOW_SIZE")) { $Karma_window_size = get_conf("SCHEDULER_FAIRSHARING_WINDOW_SIZE"); } # specify the target percentages for project names (0 if not specified) my $Karma_project_targets = { first => 75, default => 25 }; if (is_conf("SCHEDULER_FAIRSHARING_PROJECT_TARGETS")) { $Karma_project_targets = eval(get_conf("SCHEDULER_FAIRSHARING_PROJECT_TARGETS").";"); if ($@) { oar_error("Syntax error in configuration file: SCHEDULER_FAIRSHARING_PROJECT_TARGETS"); exit (1); } } # specify the target percentages for users (0 if not specified) my $Karma_user_targets = { oar => 100 }; if (is_conf("SCHEDULER_FAIRSHARING_USER_TARGETS")) { $Karma_user_targets = eval(get_conf("SCHEDULER_FAIRSHARING_USER_TARGETS").";"); if ($@) { oar_error("Syntax error in configuration file: SCHEDULER_FAIRSHARING_USER_TARGETS"); exit (1); } } #print Dumper($Karma_user_targets); # weight given to each criteria my $Karma_coeff_project_consumption = 0; if (is_conf("SCHEDULER_FAIRSHARING_COEF_PROJECT")) { $Karma_coeff_project_consumption = get_conf("SCHEDULER_FAIRSHARING_COEF_PROJECT"); } my $Karma_coeff_user_consumption = 2; if (is_conf("SCHEDULER_FAIRSHARING_COEF_USER")) { $Karma_coeff_user_consumption = get_conf("SCHEDULER_FAIRSHARING_COEF_USER"); } my $Karma_coeff_user_asked_consumption = 1; if (is_conf("SCHEDULER_FAIRSHARING_COEF_USER_ASK")) { $Karma_coeff_user_asked_consumption = get_conf("SCHEDULER_FAIRSHARING_COEF_USER_ASK"); } my $Token_scripts = {}; if (is_conf("SCHEDULER_TOKEN_SCRIPTS")) { $Token_scripts = eval(get_conf("SCHEDULER_TOKEN_SCRIPTS").";"); if ($@) { oar_error("Syntax error in configuration file: SCHEDULER_TOKEN_SCRIPTS"); exit (1); } } ############################################################################### my $initial_time = time(); my $timeout = 10; my $Minimum_timeout_per_job = 0; if (is_conf("SCHEDULER_TIMEOUT")){ $timeout = get_conf("SCHEDULER_TIMEOUT"); } # Constant duration time of a besteffort job my $besteffort_duration = 5*60; # $security_time_overhead is the security time (second) used to be sure there # are no problem with overlaping jobs my $security_time_overhead = 60; if (is_conf("SCHEDULER_JOB_SECURITY_TIME")){ $security_time_overhead = get_conf("SCHEDULER_JOB_SECURITY_TIME"); } my $minimum_hole_time = 0; if (is_conf("SCHEDULER_GANTT_HOLE_MINIMUM_TIME")){ $minimum_hole_time = get_conf("SCHEDULER_GANTT_HOLE_MINIMUM_TIME"); } my $Order_part = get_conf("SCHEDULER_RESOURCE_ORDER"); my @Sched_available_suspended_resource_type; my $sched_available_suspended_resource_type_tmp = get_conf("SCHEDULER_AVAILABLE_SUSPENDED_RESOURCE_TYPE"); if (!defined($sched_available_suspended_resource_type_tmp)){ push(@Sched_available_suspended_resource_type, "default"); }else{ @Sched_available_suspended_resource_type = split(" ",$sched_available_suspended_resource_type_tmp); } # Look at resources that we must add for each job my $Resources_to_always_add_type = get_conf("SCHEDULER_RESOURCES_ALWAYS_ASSIGNED_TYPE"); my @Resources_to_always_add = (); my $Max_nb_processes = get_conf_with_default_param("SCHEDULER_NB_PROCESSES",1); my $current_time ; my $queue; if (defined($ARGV[0]) && defined($ARGV[1]) && $ARGV[1] =~ m/\d+/m) { $queue = $ARGV[0]; $current_time = $ARGV[1]; }else{ oar_error("[oar_sched_gantt_with_timesharing_and_fairsharing_and_placeholder] no queue specified on command line\n"); exit(1); } # Init my $base = OAR::IO::connect(); my $base_ro = OAR::IO::connect_ro(); oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing_and_placeholder] Begining of Gantt scheduler on queue $queue at time $current_time\n"); # First check states of resources that we must add for each job if (defined($Resources_to_always_add_type)){ my $tmp_result_state_resources = OAR::IO::get_specific_resource_states($base,$Resources_to_always_add_type); if ($#{$tmp_result_state_resources->{"Suspected"}} >= 0){ oar_warn("[oar_sched_gantt_with_timesharing_and_fairsharing_and_placeholder] There are resources that are specified in oar.conf (SCHEDULER_RESOURCES_ALWAYS_ASSIGNED_TYPE) which are Suspected. So I cannot schedule any job now.\n"); exit(1); }else{ if (defined($tmp_result_state_resources->{"Alive"})){ @Resources_to_always_add = @{$tmp_result_state_resources->{"Alive"}}; oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing_and_placeholder] Assign these resources for each jobs: @Resources_to_always_add\n"); } } } my $timesharing_gantts; # Create the Gantt Diagrams #Init the gantt chart with all resources my $All_resource_list_vec = ''; my $max_resources = 1; foreach my $r (OAR::IO::list_resources($base)){ vec($All_resource_list_vec,$r->{resource_id},1) = 1; $max_resources = $r->{resource_id} if ($r->{resource_id} > $max_resources); } my %Container_gantt_hash; $Container_gantt_hash{0} = OAR::Schedulers::GanttHoleStorage::new($max_resources, $minimum_hole_time); OAR::Schedulers::GanttHoleStorage::add_new_resources($Container_gantt_hash{0}, $All_resource_list_vec); my %Placeholder_gantt_hash; sub parse_timesharing($$$){ my $str = shift; my $job_user = shift; my $job_name = shift; my $user = "*"; my $name = "*"; foreach my $s (split(',', $str)){ if ($s =~ m/^\s*([\w\*]+)\s*$/m){ if ($1 eq "user"){ $user = $job_user; }elsif (($1 eq "name") and ($job_name ne "")){ $name = $job_name; } } } return($user,$name); } # Token feature (see oar.conf) # Keep the track of the usage for each token my %Token_values; # Take care of currently scheduled jobs (gantt in the database) my ($order,%already_scheduled_jobs) = OAR::IO::get_gantt_scheduled_jobs($base); foreach my $i (@{$order}){ my $types = OAR::IO::get_current_job_types($base,$i); # Do not take care of besteffort jobs if ((! defined($types->{besteffort})) or ($queue eq "besteffort")){ my @resource_list = @{$already_scheduled_jobs{$i}->[3]}; my $job_duration = $already_scheduled_jobs{$i}->[1]; if ($already_scheduled_jobs{$i}->[4] eq "Suspended"){ # Remove resources of the type specified in SCHEDULER_AVAILABLE_SUSPENDED_RESOURCE_TYPE @resource_list = OAR::IO::get_job_current_resources($base, $already_scheduled_jobs{$i}->[7],\@Sched_available_suspended_resource_type); next if ($#resource_list < 0); } if ($already_scheduled_jobs{$i}->[8] eq "YES"){ # This job was suspended so we must recalculate the walltime $job_duration += OAR::IO::get_job_suspended_sum_duration($base,$i,$current_time); } my $vec = ''; foreach my $r (@resource_list){ vec($vec,$r,1) = 1; } my $gantt_to_use = $Container_gantt_hash{0}; my $container_num = 0; if (defined($types->{container})){ oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing_and_placeholder] Create gantt for the container $i\n"); $Container_gantt_hash{$i} = OAR::Schedulers::GanttHoleStorage::new_with_1_hole($max_resources, $minimum_hole_time, $already_scheduled_jobs{$i}->[0], $job_duration + $security_time_overhead, $vec, $All_resource_list_vec); $gantt_to_use = $Container_gantt_hash{$i}; $container_num = $i; } my $placeholder_name = "0"; if (defined($types->{set_placeholder})){ if (!defined($Placeholder_gantt_hash{$types->{set_placeholder}})){ $Placeholder_gantt_hash{$types->{set_placeholder}} = dclone($gantt_to_use); $placeholder_name = $types->{placeholder}; oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing_and_placeholder] Create gantt for the placeholder $types->{set_placeholder}\n"); } } my $user; my $name; if (defined($types->{timesharing})){ ($user, $name) = parse_timesharing($types->{timesharing}, $already_scheduled_jobs{$i}->[5], $already_scheduled_jobs{$i}->[6]); if (!defined($timesharing_gantts->{$container_num}->{$placeholder_name}->{$user}->{$name})){ $timesharing_gantts->{$container_num}->{$placeholder_name}->{$user}->{$name} = dclone($gantt_to_use); oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing_and_placeholder] Create new gantt for ($user, $name)\n"); } } #Fill all other gantts foreach my $g (keys(%Container_gantt_hash)){ if ($i != $g){ oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing_and_placeholder] Add occupation in container $g for job $i\n"); OAR::Schedulers::GanttHoleStorage::set_occupation( $Container_gantt_hash{$g}, $already_scheduled_jobs{$i}->[0], $job_duration + $security_time_overhead, $vec ); } #print("\nOOOOOOOOOO $i - $g $current_time\n"); #OAR::Schedulers::GanttHoleStorage::pretty_print($Container_gantt_hash{$g}); } foreach my $c (keys(%{$timesharing_gantts})){ foreach my $p (keys(%{$timesharing_gantts->{$c}})){ foreach my $u (keys(%{$timesharing_gantts->{$c}->{$p}})){ foreach my $n (keys(%{$timesharing_gantts->{$c}->{$p}->{$u}})){ if ((!defined($user)) or (!defined($name)) or (($u ne $user) or ($n ne $name))){ OAR::Schedulers::GanttHoleStorage::set_occupation( $timesharing_gantts->{$c}->{$p}->{$u}->{$n}, $already_scheduled_jobs{$i}->[0], $job_duration + $security_time_overhead, $vec ); } } } } } # For placeholder foreach my $p (keys(%Placeholder_gantt_hash)){ if ((!defined($types->{set_placeholder})) or ($p ne $types->{set_placeholder})){ OAR::Schedulers::GanttHoleStorage::set_occupation( $Placeholder_gantt_hash{$p}, $already_scheduled_jobs{$i}->[0], $job_duration + $security_time_overhead, $vec ); } } } } oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing_and_placeholder] End gantt initialization\n"); # End of the initialisation # Begining of the real scheduling # Get list of Alive resources my $alive_resources_vector = ''; foreach my $r (OAR::IO::get_resources_in_state($base,"Alive")){ vec($alive_resources_vector, $r->{resource_id}, 1) = 1; } # ENERGY SAVING: add fake occupations/holes from energy saving configuration # CM part and Hulot part (wake up nodes in energy saving mode) if (is_conf("SCHEDULER_NODE_MANAGER_WAKE_UP_CMD") or (get_conf("ENERGY_SAVING_INTERNAL") eq "yes" and is_conf("ENERGY_SAVING_NODE_MANAGER_WAKE_UP_CMD"))){ oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] [START] Checking EnergySaving occupation\n"); # Check the resources that can be waked_up or shut down my $upto_availability = OAR::IO::get_energy_saving_resources_availability($base, $current_time); foreach my $t (keys(%{$upto_availability})){ my $vec = ''; foreach my $r (@{$upto_availability->{$t}}){ vec($alive_resources_vector, $r, 1) = 1; vec($vec,$r,1) = 1; } #Fill all the gantts foreach my $g (keys(%Container_gantt_hash)){ oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] Add EnergySaving occupation in container $g for available_upto = $t\n"); OAR::Schedulers::GanttHoleStorage::set_occupation( $Container_gantt_hash{$g}, $t, OAR::Schedulers::GanttHoleStorage::get_infinity_value(), $vec ); } foreach my $c (keys(%{$timesharing_gantts})){ foreach my $u (keys(%{$timesharing_gantts->{$c}})){ foreach my $n (keys(%{$timesharing_gantts->{$c}->{$u}})){ OAR::Schedulers::GanttHoleStorage::set_occupation( $timesharing_gantts->{$c}->{$u}->{$n}, $t, OAR::Schedulers::GanttHoleStorage::get_infinity_value(), $vec ); } } } } oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] [END] Checking EnergySaving occupation\n"); } # CM part my @Dead_resources; foreach my $r (OAR::IO::get_resources_in_state($base,"Dead")){ push(@Dead_resources, $r->{resource_id}); } my @jobs = OAR::IO::get_fairsharing_jobs_to_schedule($base,$queue,$Karma_max_number_of_jobs_treated_per_user); ############################################################################### # Sort jobs depending on their previous usage # Karma sort algorithm my $Karma_sum_time = OAR::IO::get_sum_accounting_window($base,$queue,$current_time - $Karma_window_size,$current_time); $Karma_sum_time->{ASKED} = 1 if (!defined($Karma_sum_time->{ASKED})); $Karma_sum_time->{USED} = 1 if (!defined($Karma_sum_time->{USED})); my $Karma_projects = OAR::IO::get_sum_accounting_for_param($base,$queue,"accounting_project",$current_time - $Karma_window_size,$current_time); my $Karma_users = OAR::IO::get_sum_accounting_for_param($base,$queue,"accounting_user",$current_time - $Karma_window_size,$current_time); sub karma($){ my $j = shift; my $note = 0; $note = $Karma_coeff_project_consumption * (($Karma_projects->{$j->{project}}->{USED} / $Karma_sum_time->{USED}) - ($Karma_project_targets->{$j->{project}} / 100)); $note += $Karma_coeff_user_consumption * (($Karma_users->{$j->{job_user}}->{USED} / $Karma_sum_time->{USED}) - ($Karma_user_targets->{$j->{job_user}} / 100)); $note += $Karma_coeff_user_asked_consumption * (($Karma_users->{$j->{job_user}}->{ASKED} / $Karma_sum_time->{ASKED}) - ($Karma_user_targets->{$j->{job_user}} / 100)); return($note); } ############################################################################### @jobs = sort({karma($a) <=> karma($b)} @jobs); my $job_index = 0; while (($job_index <= $#jobs) and ((time() - $initial_time) < $timeout)){ my $j = $jobs[$job_index]; $job_index ++; oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing_and_placeholder] [$j->{job_id}] Start scheduling (Karma note = ".karma($j).")\n"); my $scheduler_init_date = $current_time; # Search for dependencies my $skip_job = 0; # Check the job dependencies foreach my $d (OAR::IO::get_current_job_dependencies($base,$j->{job_id})){ next if ($skip_job == 1); my $dep_job = OAR::IO::get_job($base,$d); if (($dep_job->{state} ne "Terminated")){ my @date_tmp = OAR::IO::get_gantt_job_start_time($base,$d); if (defined($date_tmp[0])){ my $mold_dep = OAR::IO::get_current_moldable_job($base,$date_tmp[1]); my $sched_tmp = $date_tmp[0] + $mold_dep->{moldable_walltime}; if ($scheduler_init_date < $sched_tmp){ $scheduler_init_date = $sched_tmp + (2 * $security_time_overhead); } }else{ my $message = "Cannot determine scheduling time due to dependency with the job $d"; OAR::IO::set_job_message($base,$j->{job_id},$message); OAR::IO::set_job_scheduler_info($base,$j->{job_id},$message); oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing_and_placeholder] [$j->{job_id}] $message\n"); $skip_job = 1; next; } }elsif (($dep_job->{job_type} eq "PASSIVE") and ($dep_job->{exit_code} != 0)){ my $message = "Cannot determine scheduling time due to dependency with the job $d (exit code != 0)"; OAR::IO::set_job_message($base,$j->{job_id},$message); OAR::IO::set_job_scheduler_info($base,$j->{job_id},$message); oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing_and_placeholder] [$j->{job_id}] $message\n"); $skip_job = 1; next; } } next if ($skip_job == 1); my $gantt_to_use = $Container_gantt_hash{0}; my $container_num = 0; my $types = OAR::IO::get_current_job_types($base,$j->{job_id}); if (defined($types->{inner}) and ($types->{inner} =~ /^(\d+)$/)){ if (defined($Container_gantt_hash{$1})){ $gantt_to_use = $Container_gantt_hash{$1}; $container_num = $1; oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing_and_placeholder] [$j->{job_id}] Using container $container_num\n"); }else{ oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing_and_placeholder] [$j->{job_id}] Container $1 does not exist.\n"); OAR::IO::set_job_message($base,$j->{job_id},"Container $1 does not exist"); OAR::IO::set_job_scheduler_info($base,$j->{job_id},"Container $1 does not exist"); next; } } my $placeholder_name = "0"; if (defined($types->{allow_placeholder})){ if (defined($Placeholder_gantt_hash{$types->{allow_placeholder}})){ $gantt_to_use = $Placeholder_gantt_hash{$types->{allow_placeholder}}; $placeholder_name = $types->{allow_placeholder}; oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing_and_placeholder] [$j->{job_id}] Using placeholder $placeholder_name\n"); }else{ oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing_and_placeholder] [$j->{job_id}] Placeholder $types->{allow_placeholder} does not exist. So I use the default placeholder\n"); } } if (defined($types->{timesharing})){ my ($user, $name) = parse_timesharing($types->{timesharing}, $j->{job_user}, $j->{job_name}); if (!defined($timesharing_gantts->{$container_num}->{$placeholder_name}->{$user}->{$name})){ $timesharing_gantts->{$container_num}->{$placeholder_name}->{$user}->{$name} = dclone($gantt_to_use); oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing_and_placeholder] Create new gantt in phase II for ($user, $name)\n"); } $gantt_to_use = $timesharing_gantts->{$container_num}->{$placeholder_name}->{$user}->{$name}; oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing_and_placeholder] Use gantt for ($container_num,$user,$name)\n"); } if (defined($types->{token})){ foreach my $t (keys(%{$types->{token}})){ next if ($skip_job == 1); oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing_and_placeholder] [$j->{job_id}] Check token: $t ($types->{token}->{$t}).\n"); # Check if we must execute the corresponding script if ((!defined($Token_values{$t})) and (defined($Token_scripts->{$t}))){ oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing_and_placeholder] [$j->{job_id}] Execute $Token_scripts->{$t}\n"); if (open(TOKSCRIPT, "$Token_scripts->{$t} |")){ my $num = ; chop($num); if ($num =~ /^\d+$/){ $Token_values{$t} = $num; oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing_and_placeholder] [$j->{job_id}] $Token_scripts->{$t} returns $num\n"); }else{ oar_warn("[oar_sched_gantt_with_timesharing_and_fairsharing_and_placeholder] [$j->{job_id}] The token script $Token_scripts->{$t} does not return a number ($num).\n"); } close(TOKSCRIPT); } } if (defined($Token_values{$t})){ if ($Token_values{$t} < $types->{token}->{$t}){ oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing_and_placeholder] [$j->{job_id}] No enough Tokens: $t ($Token_values{$t} < $types->{token}->{$t}).\n"); OAR::IO::set_job_message($base,$j->{job_id},"No enough Token: $t ($Token_values{$t} < $types->{token}->{$t})"); OAR::IO::set_job_scheduler_info($base,$j->{job_id},"No enough Token: $t ($Token_values{$t} < $types->{token}->{$t})"); $skip_job = 1; } }else{ oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing_and_placeholder] [$j->{job_id}] Token value cannot be retrieved ($t).\n"); OAR::IO::set_job_message($base,$j->{job_id},"Token value cannot be retrieved ($t)"); OAR::IO::set_job_scheduler_info($base,$j->{job_id},"Token value cannot be retrieved ($t)"); $skip_job = 1; } } next if ($skip_job == 1); } #oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing_and_placeholder] Use gantt for $j->{job_id}:\n".OAR::Schedulers::GanttHoleStorage::pretty_print($gantt_to_use)."\n"); my $job_properties = "\'1\'"; if ((defined($j->{properties})) and ($j->{properties} ne "")){ $job_properties = $j->{properties}; } # Choose the moldable job to schedule my @moldable_results; my $job_descriptions = OAR::IO::get_resources_data_structure_current_job($base,$j->{job_id}); foreach my $moldable (@{$job_descriptions}){ #my $moldable = $job_descriptions->[0]; my $duration; if (defined($types->{besteffort})){ $duration = $besteffort_duration; }else{ $duration = $moldable->[1] + $security_time_overhead; } my @tree_list; foreach my $m (@{$moldable->[0]}){ my $tmp_properties = "\'1\'"; if ((defined($m->{property})) and ($m->{property} ne "")){ $tmp_properties = $m->{property}; } my $tmp_tree = OAR::IO::get_possible_wanted_resources($base_ro,$alive_resources_vector,undef,\@Dead_resources,"$job_properties AND $tmp_properties", $m->{resources}, $Order_part); push(@tree_list, $tmp_tree); #my @leafs = OAR::Schedulers::ResourceTree::get_tree_leafs($tmp_tree); #foreach my $l (@leafs){ # vec($resource_id_used_list_vector, OAR::Schedulers::ResourceTree::get_current_resource_value($l), 1) = 1; #} } my $gantt_timeout = ($timeout - (time() - $initial_time)) / 4; $gantt_timeout = $Minimum_timeout_per_job if ($gantt_timeout <= ($timeout / 8)); oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing_and_placeholder] [$j->{job_id}] find_first_hole with a timeout of $gantt_timeout\n"); my @hole; if ($Max_nb_processes <= 1){ @hole = OAR::Schedulers::GanttHoleStorage::find_first_hole($gantt_to_use, $scheduler_init_date, $duration, \@tree_list,$gantt_timeout); }else{ oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing_and_placeholder] [$j->{job_id}] Using Gantt PARALLEL algorithm\n"); @hole = OAR::Schedulers::GanttHoleStorage::find_first_hole_parallel($gantt_to_use, $scheduler_init_date, $duration, \@tree_list,$gantt_timeout,$Max_nb_processes); } # print("[GANTT] 10 ".gettimeofday."\n"); my @res_trees; my @resources; foreach my $t (@{$hole[1]}){ # print("[GANTT] 11 ".gettimeofday."\n"); #my $minimal_tree = OAR::Schedulers::ResourceTree::delete_unnecessary_subtrees($t); # print("[GANTT] 12 ".gettimeofday."\n"); push(@res_trees, $t); foreach my $r (OAR::Schedulers::ResourceTree::get_tree_leafs($t)){ push(@resources, OAR::Schedulers::ResourceTree::get_current_resource_value($r)); } # print("[GANTT] 13 ".gettimeofday."\n"); } # print("[GANTT] @resources\n"); push(@moldable_results, { resources => \@resources, start_date => $hole[0], duration => $duration, moldable_id => $moldable->[2] }); } # Choose moldable job which will finish the first my $index_to_choose = -1; my $best_stop_time; # print("[GANTT] 14 ".gettimeofday."\n"); for (my $i=0; $i <= $#moldable_results; $i++){ #my @tmp_array = @{$moldable_results[$i]->{resources}}; if ($#{$moldable_results[$i]->{resources}} >= 0){ my $tmp_stop_date = $moldable_results[$i]->{start_date} + $moldable_results[$i]->{duration}; if ((!defined($best_stop_time)) or ($best_stop_time > $tmp_stop_date)){ $best_stop_time = $tmp_stop_date; $index_to_choose = $i; } } } if ($index_to_choose >= 0){ # We can schedule the job # print("[GANTT] 15 ".gettimeofday."\n"); my $vec = ''; foreach my $r (@{$moldable_results[$index_to_choose]->{resources}}){ vec($vec, $r, 1) = 1; } #Fill all other gantts foreach my $g (keys(%Container_gantt_hash)){ if ($j->{job_id} != $g){ oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing_and_placeholder] Add live occupation in container $g for job $j->{job_id}\n"); OAR::Schedulers::GanttHoleStorage::set_occupation( $Container_gantt_hash{$g}, $moldable_results[$index_to_choose]->{start_date}, $moldable_results[$index_to_choose]->{duration}, $vec ); } } if (defined($types->{container})){ $Container_gantt_hash{$j->{job_id}} = OAR::Schedulers::GanttHoleStorage::new_with_1_hole($max_resources, $minimum_hole_time, $moldable_results[$index_to_choose]->{start_date}, $moldable_results[$index_to_choose]->{duration}, $vec, $All_resource_list_vec); } foreach my $c (keys(%{$timesharing_gantts})){ foreach my $p (keys(%{$timesharing_gantts->{$c}})){ foreach my $u (keys(%{$timesharing_gantts->{$c}->{$p}})){ foreach my $n (keys(%{$timesharing_gantts->{$c}->{$p}->{$u}})){ if (($gantt_to_use != $timesharing_gantts->{$c}->{$p}->{$u}->{$n})){ OAR::Schedulers::GanttHoleStorage::set_occupation( $timesharing_gantts->{$c}->{$p}->{$u}->{$n}, $moldable_results[$index_to_choose]->{start_date}, $moldable_results[$index_to_choose]->{duration}, $vec ); } } } } } # For placeholder foreach my $p (keys(%Placeholder_gantt_hash)){ if ((!defined($types->{set_placeholder})) or ($types->{set_placeholder} ne $p)){ oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing_and_placeholder] Add live occupation in the placeholder $p\n"); OAR::Schedulers::GanttHoleStorage::set_occupation( $Placeholder_gantt_hash{$p}, $moldable_results[$index_to_choose]->{start_date}, $moldable_results[$index_to_choose]->{duration}, $vec ); } } if (defined($types->{set_placeholder})){ if (!defined($Placeholder_gantt_hash{$types->{set_placeholder}})){ $Placeholder_gantt_hash{$types->{set_placeholder}} = dclone($gantt_to_use); $placeholder_name = $types->{placeholder}; oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing_and_placeholder] Create gantt for the placeholder $types->{set_placeholder}\n"); } } #update database push(@{$moldable_results[$index_to_choose]->{resources}},@Resources_to_always_add); OAR::IO::add_gantt_scheduled_jobs($base,$moldable_results[$index_to_choose]->{moldable_id}, $moldable_results[$index_to_choose]->{start_date},$moldable_results[$index_to_choose]->{resources}); OAR::IO::set_job_message($base,$j->{job_id},"Karma = ".sprintf("%.3f",karma($j))." , container=$container_num , placeholder=$placeholder_name"); OAR::IO::set_job_scheduler_info($base,$j->{job_id},"Karma = ".sprintf("%.3f",karma($j))." , container=$container_num , placeholder=$placeholder_name"); # Update Token values if (defined($types->{token}) and ($moldable_results[$index_to_choose]->{start_date} <= $current_time)){ foreach my $t (keys(%{$types->{token}})){ $Token_values{$t} = $Token_values{$t} - $types->{token}->{$t}; oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing_and_fairsharing_and_placeholder] Update TOKEN $t to $Token_values{$t}\n"); } } }else{ my $message = "Cannot find enough resources which fit for the job $j->{job_id}"; OAR::IO::set_job_message($base,$j->{job_id},$message); OAR::IO::set_job_scheduler_info($base,$j->{job_id},$message); oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing_and_placeholder] [$j->{job_id}] $message\n"); } # print("[GANTT] 18 ".gettimeofday."\n"); oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing_and_placeholder] [$j->{job_id}] End scheduling\n"); } OAR::IO::disconnect($base); OAR::IO::disconnect($base_ro); if ($job_index <= $#jobs){ oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing_and_fairsharing_and_placeholder] I am not able to schedule all waiting jobs in the specified time : $timeout s\n"); } oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing_and_placeholder] End of scheduler for queue $queue\n"); ./oar-2.5.2/sources/core/modules/scheduler/oar_sched_gantt_with_timesharing_and_fairsharing0000755000175000017500000006714711757171206030537 0ustar plbplb#!/usr/bin/perl # $Id$ #-d:DProf use strict; use DBI(); use OAR::IO; use Data::Dumper; use OAR::Modules::Judas qw(oar_debug oar_warn oar_error set_current_log_category); use OAR::Conf qw(init_conf dump_conf get_conf is_conf get_conf_with_default_param); use OAR::Schedulers::GanttHoleStorage; use Storable qw(dclone); use Time::HiRes qw(gettimeofday); # Log category set_current_log_category('scheduler'); init_conf($ENV{OARCONFFILE}); ############################################################################### # Fairsharing parameters # ########################## # Avoid problems if there are too many waiting jobs my $Karma_max_number_of_jobs_treated_per_user = 30; if (is_conf("SCHEDULER_FAIRSHARING_MAX_JOB_PER_USER")) { $Karma_max_number_of_jobs_treated_per_user = get_conf("SCHEDULER_FAIRSHARING_MAX_JOB_PER_USER"); } # number of seconds to consider for the fairsharing my $Karma_window_size = 3600 * 30 * 24; if (is_conf("SCHEDULER_FAIRSHARING_WINDOW_SIZE")) { $Karma_window_size = get_conf("SCHEDULER_FAIRSHARING_WINDOW_SIZE"); } # specify the target percentages for project names (0 if not specified) my $Karma_project_targets = { first => 75, default => 25 }; if (is_conf("SCHEDULER_FAIRSHARING_PROJECT_TARGETS")) { $Karma_project_targets = eval(get_conf("SCHEDULER_FAIRSHARING_PROJECT_TARGETS").";"); if ($@) { oar_error("Syntax error in configuration file: SCHEDULER_FAIRSHARING_PROJECT_TARGETS"); exit (1); } } # specify the target percentages for users (0 if not specified) my $Karma_user_targets = { oar => 100 }; if (is_conf("SCHEDULER_FAIRSHARING_USER_TARGETS")) { $Karma_user_targets = eval(get_conf("SCHEDULER_FAIRSHARING_USER_TARGETS").";"); if ($@) { oar_error("Syntax error in configuration file: SCHEDULER_FAIRSHARING_USER_TARGETS"); exit (1); } } #print Dumper($Karma_user_targets); # weight given to each criteria my $Karma_coeff_project_consumption = 0; if (is_conf("SCHEDULER_FAIRSHARING_COEF_PROJECT")) { $Karma_coeff_project_consumption = get_conf("SCHEDULER_FAIRSHARING_COEF_PROJECT"); } my $Karma_coeff_user_consumption = 2; if (is_conf("SCHEDULER_FAIRSHARING_COEF_USER")) { $Karma_coeff_user_consumption = get_conf("SCHEDULER_FAIRSHARING_COEF_USER"); } my $Karma_coeff_user_asked_consumption = 1; if (is_conf("SCHEDULER_FAIRSHARING_COEF_USER_ASK")) { $Karma_coeff_user_asked_consumption = get_conf("SCHEDULER_FAIRSHARING_COEF_USER_ASK"); } my $Token_scripts = {}; if (is_conf("SCHEDULER_TOKEN_SCRIPTS")) { $Token_scripts = eval(get_conf("SCHEDULER_TOKEN_SCRIPTS").";"); if ($@) { oar_error("Syntax error in configuration file: SCHEDULER_TOKEN_SCRIPTS"); exit (1); } } ############################################################################### my $initial_time = time(); my $timeout = 10; my $Minimum_timeout_per_job = 0; if (is_conf("SCHEDULER_TIMEOUT")){ $timeout = get_conf("SCHEDULER_TIMEOUT"); } # Constant duration time of a besteffort job my $besteffort_duration = 5*60; # $security_time_overhead is the security time (second) used to be sure there # are no problem with overlaping jobs my $security_time_overhead = 60; if (is_conf("SCHEDULER_JOB_SECURITY_TIME")){ $security_time_overhead = get_conf("SCHEDULER_JOB_SECURITY_TIME"); } my $minimum_hole_time = 0; if (is_conf("SCHEDULER_GANTT_HOLE_MINIMUM_TIME")){ $minimum_hole_time = get_conf("SCHEDULER_GANTT_HOLE_MINIMUM_TIME"); } my $Order_part = get_conf("SCHEDULER_RESOURCE_ORDER"); my @Sched_available_suspended_resource_type; my $sched_available_suspended_resource_type_tmp = get_conf("SCHEDULER_AVAILABLE_SUSPENDED_RESOURCE_TYPE"); if (!defined($sched_available_suspended_resource_type_tmp)){ push(@Sched_available_suspended_resource_type, "default"); }else{ @Sched_available_suspended_resource_type = split(" ",$sched_available_suspended_resource_type_tmp); } # Look at resources that we must add for each job my $Resources_to_always_add_type = get_conf("SCHEDULER_RESOURCES_ALWAYS_ASSIGNED_TYPE"); my @Resources_to_always_add = (); my $Max_nb_processes = get_conf_with_default_param("SCHEDULER_NB_PROCESSES",1); my $current_time ; my $queue; if (defined($ARGV[0]) && defined($ARGV[1]) && $ARGV[1] =~ m/\d+/m) { $queue = $ARGV[0]; $current_time = $ARGV[1]; }else{ oar_error("[oar_sched_gantt_with_timesharing_and_fairsharing] no queue specified on command line\n"); exit(1); } # Init my $base = OAR::IO::connect(); my $base_ro = OAR::IO::connect_ro(); oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] Begining of Gantt scheduler on queue $queue at time $current_time\n"); # First check states of resources that we must add for each job if (defined($Resources_to_always_add_type)){ my $tmp_result_state_resources = OAR::IO::get_specific_resource_states($base,$Resources_to_always_add_type); if ($#{$tmp_result_state_resources->{"Suspected"}} >= 0){ oar_warn("[oar_sched_gantt_with_timesharing_and_fairsharing] There are resources that are specified in oar.conf (SCHEDULER_RESOURCES_ALWAYS_ASSIGNED_TYPE) which are Suspected. So I cannot schedule any job now.\n"); exit(1); }else{ if (defined($tmp_result_state_resources->{"Alive"})){ @Resources_to_always_add = @{$tmp_result_state_resources->{"Alive"}}; oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] Assign these resources for each jobs: @Resources_to_always_add\n"); } } } my $timesharing_gantts; # Create the Gantt Diagrams #Init the gantt chart with all resources my $All_resource_list_vec = ''; my $max_resources = 1; foreach my $r (OAR::IO::list_resources($base)){ vec($All_resource_list_vec,$r->{resource_id},1) = 1; $max_resources = $r->{resource_id} if ($r->{resource_id} > $max_resources); } my %Container_gantt_hash; $Container_gantt_hash{0} = OAR::Schedulers::GanttHoleStorage::new($max_resources, $minimum_hole_time); OAR::Schedulers::GanttHoleStorage::add_new_resources($Container_gantt_hash{0}, $All_resource_list_vec); sub parse_timesharing($$$){ my $str = shift; my $job_user = shift; my $job_name = shift; my $user = "*"; my $name = "*"; foreach my $s (split(',', $str)){ if ($s =~ m/^\s*([\w\*]+)\s*$/m){ if ($1 eq "user"){ $user = $job_user; }elsif (($1 eq "name") and ($job_name ne "")){ $name = $job_name; } } } return($user,$name); } # Token feature (see oar.conf) # Keep the track of the usage for each token my %Token_values; # Take care of currently scheduled jobs (gantt in the database) my ($order,%already_scheduled_jobs) = OAR::IO::get_gantt_scheduled_jobs($base); foreach my $i (@{$order}){ my $types = OAR::IO::get_current_job_types($base,$i); # Do not take care of besteffort jobs if ((! defined($types->{besteffort})) or ($queue eq "besteffort")){ my @resource_list = @{$already_scheduled_jobs{$i}->[3]}; my $job_duration = $already_scheduled_jobs{$i}->[1]; if ($already_scheduled_jobs{$i}->[4] eq "Suspended"){ # Remove resources of the type specified in SCHEDULER_AVAILABLE_SUSPENDED_RESOURCE_TYPE @resource_list = OAR::IO::get_job_current_resources($base, $already_scheduled_jobs{$i}->[7],\@Sched_available_suspended_resource_type); next if ($#resource_list < 0); } if ($already_scheduled_jobs{$i}->[8] eq "YES"){ # This job was suspended so we must recalculate the walltime $job_duration += OAR::IO::get_job_suspended_sum_duration($base,$i,$current_time); } my $vec = ''; foreach my $r (@resource_list){ vec($vec,$r,1) = 1; } my $gantt_to_use = $Container_gantt_hash{0}; my $container_num = 0; if (defined($types->{container})){ oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] Create gantt for the container $i\n"); $Container_gantt_hash{$i} = OAR::Schedulers::GanttHoleStorage::new_with_1_hole($max_resources, $minimum_hole_time, $already_scheduled_jobs{$i}->[0], $job_duration + $security_time_overhead, $vec, $All_resource_list_vec); $gantt_to_use = $Container_gantt_hash{$i}; $container_num = $i; } my $user; my $name; if (defined($types->{timesharing})){ ($user, $name) = parse_timesharing($types->{timesharing}, $already_scheduled_jobs{$i}->[5], $already_scheduled_jobs{$i}->[6]); if (!defined($timesharing_gantts->{$container_num}->{$user}->{$name})){ $timesharing_gantts->{$container_num}->{$user}->{$name} = dclone($gantt_to_use); oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] Create new gantt for ($user, $name)\n"); } } #Fill all other gantts foreach my $g (keys(%Container_gantt_hash)){ if ($i != $g){ oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] Add occupation in container $g for job $i\n"); OAR::Schedulers::GanttHoleStorage::set_occupation( $Container_gantt_hash{$g}, $already_scheduled_jobs{$i}->[0], $job_duration + $security_time_overhead, $vec ); } #print("\nOOOOOOOOOO $i - $g $current_time\n"); #OAR::Schedulers::GanttHoleStorage::pretty_print($Container_gantt_hash{$g}); } foreach my $c (keys(%{$timesharing_gantts})){ foreach my $u (keys(%{$timesharing_gantts->{$c}})){ foreach my $n (keys(%{$timesharing_gantts->{$c}->{$u}})){ if ((!defined($user)) or (!defined($name)) or (($u ne $user) or ($n ne $name))){ OAR::Schedulers::GanttHoleStorage::set_occupation( $timesharing_gantts->{$c}->{$u}->{$n}, $already_scheduled_jobs{$i}->[0], $job_duration + $security_time_overhead, $vec ); } } } } } } oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] End gantt initialization\n"); # End of the initialisation # Begining of the real scheduling # Get list of Alive resources my $alive_resources_vector = ''; foreach my $r (OAR::IO::get_resources_in_state($base,"Alive")){ vec($alive_resources_vector, $r->{resource_id}, 1) = 1; } # ENERGY SAVING: add fake occupations/holes from energy saving configuration # CM part and Hulot part (wake up nodes in energy saving mode) if (is_conf("SCHEDULER_NODE_MANAGER_WAKE_UP_CMD") or (get_conf("ENERGY_SAVING_INTERNAL") eq "yes" and is_conf("ENERGY_SAVING_NODE_MANAGER_WAKE_UP_CMD"))){ oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] [START] Checking EnergySaving occupation\n"); # Check the resources that can be waked_up or shut down my $upto_availability = OAR::IO::get_energy_saving_resources_availability($base, $current_time); foreach my $t (keys(%{$upto_availability})){ my $vec = ''; foreach my $r (@{$upto_availability->{$t}}){ vec($alive_resources_vector, $r, 1) = 1; vec($vec,$r,1) = 1; } #Fill all the gantts foreach my $g (keys(%Container_gantt_hash)){ oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] Add EnergySaving occupation in container $g for available_upto = $t\n"); OAR::Schedulers::GanttHoleStorage::set_occupation( $Container_gantt_hash{$g}, $t, OAR::Schedulers::GanttHoleStorage::get_infinity_value(), $vec ); } foreach my $c (keys(%{$timesharing_gantts})){ foreach my $u (keys(%{$timesharing_gantts->{$c}})){ foreach my $n (keys(%{$timesharing_gantts->{$c}->{$u}})){ OAR::Schedulers::GanttHoleStorage::set_occupation( $timesharing_gantts->{$c}->{$u}->{$n}, $t, OAR::Schedulers::GanttHoleStorage::get_infinity_value(), $vec ); } } } } oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] [END] Checking EnergySaving occupation\n"); } # CM part my @Dead_resources; foreach my $r (OAR::IO::get_resources_in_state($base,"Dead")){ push(@Dead_resources, $r->{resource_id}); } my @jobs = OAR::IO::get_fairsharing_jobs_to_schedule($base,$queue,$Karma_max_number_of_jobs_treated_per_user); ############################################################################### # Sort jobs depending on their previous usage # Karma sort algorithm my $Karma_sum_time = OAR::IO::get_sum_accounting_window($base,$queue,$current_time - $Karma_window_size,$current_time); $Karma_sum_time->{ASKED} = 1 if (!defined($Karma_sum_time->{ASKED})); $Karma_sum_time->{USED} = 1 if (!defined($Karma_sum_time->{USED})); my $Karma_projects = OAR::IO::get_sum_accounting_for_param($base,$queue,"accounting_project",$current_time - $Karma_window_size,$current_time); my $Karma_users = OAR::IO::get_sum_accounting_for_param($base,$queue,"accounting_user",$current_time - $Karma_window_size,$current_time); sub karma($){ my $j = shift; my $note = 0; $note = $Karma_coeff_project_consumption * (($Karma_projects->{$j->{project}}->{USED} / $Karma_sum_time->{USED}) - ($Karma_project_targets->{$j->{project}} / 100)); $note += $Karma_coeff_user_consumption * (($Karma_users->{$j->{job_user}}->{USED} / $Karma_sum_time->{USED}) - ($Karma_user_targets->{$j->{job_user}} / 100)); $note += $Karma_coeff_user_asked_consumption * (($Karma_users->{$j->{job_user}}->{ASKED} / $Karma_sum_time->{ASKED}) - ($Karma_user_targets->{$j->{job_user}} / 100)); return($note); } ############################################################################### @jobs = sort({karma($a) <=> karma($b)} @jobs); my $job_index = 0; while (($job_index <= $#jobs) and ((time() - $initial_time) < $timeout)){ my $j = $jobs[$job_index]; $job_index ++; oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] [$j->{job_id}] Start scheduling (Karma note = ".karma($j).")\n"); my $scheduler_init_date = $current_time; # Search for dependencies my $skip_job = 0; # Check the job dependencies foreach my $d (OAR::IO::get_current_job_dependencies($base,$j->{job_id})){ next if ($skip_job == 1); my $dep_job = OAR::IO::get_job($base,$d); if (($dep_job->{state} ne "Terminated")){ my @date_tmp = OAR::IO::get_gantt_job_start_time($base,$d); if (defined($date_tmp[0])){ my $mold_dep = OAR::IO::get_current_moldable_job($base,$date_tmp[1]); my $sched_tmp = $date_tmp[0] + $mold_dep->{moldable_walltime}; if ($scheduler_init_date < $sched_tmp){ $scheduler_init_date = $sched_tmp + (2 * $security_time_overhead); } }else{ my $message = "Cannot determine scheduling time due to dependency with the job $d"; OAR::IO::set_job_message($base,$j->{job_id},$message); OAR::IO::set_job_scheduler_info($base,$j->{job_id},$message); oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] [$j->{job_id}] $message\n"); $skip_job = 1; next; } }elsif (($dep_job->{job_type} eq "PASSIVE") and ($dep_job->{exit_code} != 0)){ my $message = "Cannot determine scheduling time due to dependency with the job $d (exit code != 0)"; OAR::IO::set_job_message($base,$j->{job_id},$message); OAR::IO::set_job_scheduler_info($base,$j->{job_id},$message); oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] [$j->{job_id}] $message\n"); $skip_job = 1; next; } } next if ($skip_job == 1); my $gantt_to_use = $Container_gantt_hash{0}; my $container_num = 0; my $types = OAR::IO::get_current_job_types($base,$j->{job_id}); if (defined($types->{inner}) and ($types->{inner} =~ /^(\d+)$/)){ if (defined($Container_gantt_hash{$1})){ $gantt_to_use = $Container_gantt_hash{$1}; $container_num = $1; oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] [$j->{job_id}] Using container $container_num\n"); }else{ oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] [$j->{job_id}] Container $1 does not exist.\n"); OAR::IO::set_job_message($base,$j->{job_id},"Container $1 does not exist"); OAR::IO::set_job_scheduler_info($base,$j->{job_id},"Container $1 does not exist"); next; } } if (defined($types->{timesharing})){ my ($user, $name) = parse_timesharing($types->{timesharing}, $j->{job_user}, $j->{job_name}); if (!defined($timesharing_gantts->{$container_num}->{$user}->{$name})){ $timesharing_gantts->{$container_num}->{$user}->{$name} = dclone($gantt_to_use); oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] Create new gantt in phase II for ($user, $name)\n"); } $gantt_to_use = $timesharing_gantts->{$container_num}->{$user}->{$name}; oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] Use gantt for ($container_num,$user,$name)\n"); } if (defined($types->{token})){ foreach my $t (keys(%{$types->{token}})){ next if ($skip_job == 1); oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] [$j->{job_id}] Check token: $t ($types->{token}->{$t}).\n"); # Check if we must execute the corresponding script if ((!defined($Token_values{$t})) and (defined($Token_scripts->{$t}))){ oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] [$j->{job_id}] Execute $Token_scripts->{$t}\n"); if (open(TOKSCRIPT, "$Token_scripts->{$t} |")){ my $num = ; chop($num); if ($num =~ /^\d+$/){ $Token_values{$t} = $num; oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] [$j->{job_id}] $Token_scripts->{$t} returns $num\n"); }else{ oar_warn("[oar_sched_gantt_with_timesharing_and_fairsharing] [$j->{job_id}] The token script $Token_scripts->{$t} does not return a number ($num).\n"); } close(TOKSCRIPT); } } if (defined($Token_values{$t})){ if ($Token_values{$t} < $types->{token}->{$t}){ oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] [$j->{job_id}] No enough Tokens: $t ($Token_values{$t} < $types->{token}->{$t}).\n"); OAR::IO::set_job_message($base,$j->{job_id},"No enough Token: $t ($Token_values{$t} < $types->{token}->{$t})"); OAR::IO::set_job_scheduler_info($base,$j->{job_id},"No enough Token: $t ($Token_values{$t} < $types->{token}->{$t})"); $skip_job = 1; } }else{ oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] [$j->{job_id}] Token value cannot be retrieved ($t).\n"); OAR::IO::set_job_message($base,$j->{job_id},"Token value cannot be retrieved ($t)"); OAR::IO::set_job_scheduler_info($base,$j->{job_id},"Token value cannot be retrieved ($t)"); $skip_job = 1; } } next if ($skip_job == 1); } #oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] Use gantt for $j->{job_id}:\n".OAR::Schedulers::GanttHoleStorage::pretty_print($gantt_to_use)."\n"); my $job_properties = "\'1\'"; if ((defined($j->{properties})) and ($j->{properties} ne "")){ $job_properties = $j->{properties}; } # Choose the moldable job to schedule my @moldable_results; my $job_descriptions = OAR::IO::get_resources_data_structure_current_job($base,$j->{job_id}); foreach my $moldable (@{$job_descriptions}){ #my $moldable = $job_descriptions->[0]; my $duration; if (defined($types->{besteffort})){ $duration = $besteffort_duration; }else{ $duration = $moldable->[1] + $security_time_overhead; } my @tree_list; foreach my $m (@{$moldable->[0]}){ my $tmp_properties = "\'1\'"; if ((defined($m->{property})) and ($m->{property} ne "")){ $tmp_properties = $m->{property}; } my $tmp_tree = OAR::IO::get_possible_wanted_resources($base_ro,$alive_resources_vector,undef,\@Dead_resources,"$job_properties AND $tmp_properties", $m->{resources}, $Order_part); push(@tree_list, $tmp_tree); #my @leafs = OAR::Schedulers::ResourceTree::get_tree_leafs($tmp_tree); #foreach my $l (@leafs){ # vec($resource_id_used_list_vector, OAR::Schedulers::ResourceTree::get_current_resource_value($l), 1) = 1; #} } my $gantt_timeout = ($timeout - (time() - $initial_time)) / 4; $gantt_timeout = $Minimum_timeout_per_job if ($gantt_timeout <= ($timeout / 8)); oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] [$j->{job_id}] find_first_hole with a timeout of $gantt_timeout\n"); my @hole; if ($Max_nb_processes <= 1){ @hole = OAR::Schedulers::GanttHoleStorage::find_first_hole($gantt_to_use, $scheduler_init_date, $duration, \@tree_list,$gantt_timeout); }else{ oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] [$j->{job_id}] Using Gantt PARALLEL algorithm\n"); @hole = OAR::Schedulers::GanttHoleStorage::find_first_hole_parallel($gantt_to_use, $scheduler_init_date, $duration, \@tree_list,$gantt_timeout,$Max_nb_processes); } # print("[GANTT] 10 ".gettimeofday."\n"); my @res_trees; my @resources; foreach my $t (@{$hole[1]}){ # print("[GANTT] 11 ".gettimeofday."\n"); #my $minimal_tree = OAR::Schedulers::ResourceTree::delete_unnecessary_subtrees($t); # print("[GANTT] 12 ".gettimeofday."\n"); push(@res_trees, $t); foreach my $r (OAR::Schedulers::ResourceTree::get_tree_leafs($t)){ push(@resources, OAR::Schedulers::ResourceTree::get_current_resource_value($r)); } # print("[GANTT] 13 ".gettimeofday."\n"); } # print("[GANTT] @resources\n"); push(@moldable_results, { resources => \@resources, start_date => $hole[0], duration => $duration, moldable_id => $moldable->[2] }); } # Choose moldable job which will finish the first my $index_to_choose = -1; my $best_stop_time; # print("[GANTT] 14 ".gettimeofday."\n"); for (my $i=0; $i <= $#moldable_results; $i++){ #my @tmp_array = @{$moldable_results[$i]->{resources}}; if ($#{$moldable_results[$i]->{resources}} >= 0){ my $tmp_stop_date = $moldable_results[$i]->{start_date} + $moldable_results[$i]->{duration}; if ((!defined($best_stop_time)) or ($best_stop_time > $tmp_stop_date)){ $best_stop_time = $tmp_stop_date; $index_to_choose = $i; } } } if ($index_to_choose >= 0){ # We can schedule the job # print("[GANTT] 15 ".gettimeofday."\n"); my $vec = ''; foreach my $r (@{$moldable_results[$index_to_choose]->{resources}}){ vec($vec, $r, 1) = 1; } #Fill all other gantts foreach my $g (keys(%Container_gantt_hash)){ if ($j->{job_id} != $g){ oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] Add live occupation in container $g for job $j->{job_id}\n"); OAR::Schedulers::GanttHoleStorage::set_occupation( $Container_gantt_hash{$g}, $moldable_results[$index_to_choose]->{start_date}, $moldable_results[$index_to_choose]->{duration}, $vec ); } } if (defined($types->{container})){ $Container_gantt_hash{$j->{job_id}} = OAR::Schedulers::GanttHoleStorage::new_with_1_hole($max_resources, $minimum_hole_time, $moldable_results[$index_to_choose]->{start_date}, $moldable_results[$index_to_choose]->{duration}, $vec, $All_resource_list_vec); } foreach my $c (keys(%{$timesharing_gantts})){ foreach my $u (keys(%{$timesharing_gantts->{$c}})){ # print("[GANTT] 17 ".gettimeofday."\n"); foreach my $n (keys(%{$timesharing_gantts->{$c}->{$u}})){ if (($gantt_to_use != $timesharing_gantts->{$c}->{$u}->{$n})){ OAR::Schedulers::GanttHoleStorage::set_occupation( $timesharing_gantts->{$c}->{$u}->{$n}, $moldable_results[$index_to_choose]->{start_date}, $moldable_results[$index_to_choose]->{duration}, $vec ); } } } } #update database push(@{$moldable_results[$index_to_choose]->{resources}},@Resources_to_always_add); OAR::IO::add_gantt_scheduled_jobs($base,$moldable_results[$index_to_choose]->{moldable_id}, $moldable_results[$index_to_choose]->{start_date},$moldable_results[$index_to_choose]->{resources}); OAR::IO::set_job_message($base,$j->{job_id},"Karma = ".sprintf("%.3f",karma($j))); OAR::IO::set_job_scheduler_info($base,$j->{job_id},"Karma = ".sprintf("%.3f",karma($j))); # Update Token values if (defined($types->{token}) and ($moldable_results[$index_to_choose]->{start_date} <= $current_time)){ foreach my $t (keys(%{$types->{token}})){ $Token_values{$t} = $Token_values{$t} - $types->{token}->{$t}; oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] Update TOKEN $t to $Token_values{$t}\n"); } } }else{ my $message = "Cannot find enough resources which fit for the job $j->{job_id}"; OAR::IO::set_job_message($base,$j->{job_id},$message); OAR::IO::set_job_scheduler_info($base,$j->{job_id},$message); oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] [$j->{job_id}] $message\n"); } # print("[GANTT] 18 ".gettimeofday."\n"); oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] [$j->{job_id}] End scheduling\n"); } OAR::IO::disconnect($base); OAR::IO::disconnect($base_ro); if ($job_index <= $#jobs){ oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] I am not able to schedule all waiting jobs in the specified time : $timeout s\n"); } oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] End of scheduler for queue $queue\n"); ./oar-2.5.2/sources/core/modules/scheduler/oar_sched_gantt_with_timesharing0000755000175000017500000005114111757171206025323 0ustar plbplb#!/usr/bin/perl # $Id$ #-d:DProf use strict; use DBI(); use OAR::IO; use Data::Dumper; use OAR::Modules::Judas qw(oar_debug oar_warn oar_error set_current_log_category); use OAR::Conf qw(init_conf dump_conf get_conf is_conf get_conf_with_default_param); use OAR::Schedulers::GanttHoleStorage; use Storable qw(dclone); use Time::HiRes qw(gettimeofday); # Log category set_current_log_category('scheduler'); my $initial_time = time(); my $timeout = 10; my $Minimum_timeout_per_job = 0; init_conf($ENV{OARCONFFILE}); if (is_conf("SCHEDULER_TIMEOUT")){ $timeout = get_conf("SCHEDULER_TIMEOUT"); } # Constant duration time of a besteffort job my $besteffort_duration = 5*60; my $max_waiting_jobs_to_schedule = 1000; # $security_time_overhead is the security time (second) used to be sure there # are no problem with overlaping jobs my $security_time_overhead = 60; if (is_conf("SCHEDULER_JOB_SECURITY_TIME")){ $security_time_overhead = get_conf("SCHEDULER_JOB_SECURITY_TIME"); } my $minimum_hole_time = 0; if (is_conf("SCHEDULER_GANTT_HOLE_MINIMUM_TIME")){ $minimum_hole_time = get_conf("SCHEDULER_GANTT_HOLE_MINIMUM_TIME"); } my $Order_part = get_conf("SCHEDULER_RESOURCE_ORDER"); my @Sched_available_suspended_resource_type; my $sched_available_suspended_resource_type_tmp = get_conf("SCHEDULER_AVAILABLE_SUSPENDED_RESOURCE_TYPE"); if (!defined($sched_available_suspended_resource_type_tmp)){ push(@Sched_available_suspended_resource_type, "default"); }else{ @Sched_available_suspended_resource_type = split(" ",$sched_available_suspended_resource_type_tmp); } # Look at resources that we must add for each job my $Resources_to_always_add_type = get_conf("SCHEDULER_RESOURCES_ALWAYS_ASSIGNED_TYPE"); my @Resources_to_always_add = (); my $Max_nb_processes = get_conf_with_default_param("SCHEDULER_NB_PROCESSES",1); my $current_time ; my $queue; if (defined($ARGV[0]) && defined($ARGV[1]) && $ARGV[1] =~ m/\d+/m) { $queue = $ARGV[0]; $current_time = $ARGV[1]; }else{ oar_error("[oar_sched_gantt_with_timesharing] no queue specified on command line\n"); exit(1); } # Init my $base = OAR::IO::connect(); my $base_ro = OAR::IO::connect_ro(); oar_debug("[oar_sched_gantt_with_timesharing] Begining of Gantt scheduler on queue $queue at time $current_time\n"); # First check states of resources that we must add for each job if (defined($Resources_to_always_add_type)){ my $tmp_result_state_resources = OAR::IO::get_specific_resource_states($base,$Resources_to_always_add_type); if ($#{$tmp_result_state_resources->{"Suspected"}} >= 0){ oar_warn("[oar_sched_gantt_with_timesharing] There are resources that are specified in oar.conf (SCHEDULER_RESOURCES_ALWAYS_ASSIGNED_TYPE) which are Suspected. So I cannot schedule any job now.\n"); exit(1); }else{ if (defined($tmp_result_state_resources->{"Alive"})){ @Resources_to_always_add = @{$tmp_result_state_resources->{"Alive"}}; oar_debug("[oar_sched_gantt_with_timesharing] Assign these resources for each jobs: @Resources_to_always_add\n"); } } } my $timesharing_gantts; # Create the Gantt Diagrams #Init the gantt chart with all resources my $All_resource_list_vec = ''; my $max_resources = 1; foreach my $r (OAR::IO::list_resources($base)){ vec($All_resource_list_vec,$r->{resource_id},1) = 1; $max_resources = $r->{resource_id} if ($r->{resource_id} > $max_resources); } my %Container_gantt_hash; $Container_gantt_hash{0} = OAR::Schedulers::GanttHoleStorage::new($max_resources, $minimum_hole_time); OAR::Schedulers::GanttHoleStorage::add_new_resources($Container_gantt_hash{0}, $All_resource_list_vec); sub parse_timesharing($$$){ my $str = shift; my $job_user = shift; my $job_name = shift; my $user = "*"; my $name = "*"; foreach my $s (split(',', $str)){ if ($s =~ m/^\s*([\w\*]+)\s*$/m){ if ($1 eq "user"){ $user = $job_user; }elsif (($1 eq "name") and ($job_name ne "")){ $name = $job_name; } } } return($user,$name); } # Take care of currently scheduled jobs (gantt in the database) my ($order,%already_scheduled_jobs) = OAR::IO::get_gantt_scheduled_jobs($base); foreach my $i (@{$order}){ my $types = OAR::IO::get_current_job_types($base,$i); # Do not take care of besteffort jobs if ((! defined($types->{besteffort})) or ($queue eq "besteffort")){ my @resource_list = @{$already_scheduled_jobs{$i}->[3]}; my $job_duration = $already_scheduled_jobs{$i}->[1]; if ($already_scheduled_jobs{$i}->[4] eq "Suspended"){ # Remove resources of the type specified in SCHEDULER_AVAILABLE_SUSPENDED_RESOURCE_TYPE @resource_list = OAR::IO::get_job_current_resources($base, $already_scheduled_jobs{$i}->[7],\@Sched_available_suspended_resource_type); next if ($#resource_list < 0); } if ($already_scheduled_jobs{$i}->[8] eq "YES"){ # This job was suspended so we must recalculate the walltime $job_duration += OAR::IO::get_job_suspended_sum_duration($base,$i,$current_time); } my $vec = ''; foreach my $r (@resource_list){ vec($vec,$r,1) = 1; } my $gantt_to_use = $Container_gantt_hash{0}; my $container_num = 0; if (defined($types->{container})){ oar_debug("[oar_sched_gantt_with_timesharing] Create gantt for the container $i\n"); $Container_gantt_hash{$i} = OAR::Schedulers::GanttHoleStorage::new_with_1_hole($max_resources, $minimum_hole_time, $already_scheduled_jobs{$i}->[0], $job_duration + $security_time_overhead, $vec, $All_resource_list_vec); $gantt_to_use = $Container_gantt_hash{$i}; $container_num = $i; } my $user; my $name; if (defined($types->{timesharing})){ ($user, $name) = parse_timesharing($types->{timesharing}, $already_scheduled_jobs{$i}->[5], $already_scheduled_jobs{$i}->[6]); if (!defined($timesharing_gantts->{$container_num}->{$user}->{$name})){ $timesharing_gantts->{$container_num}->{$user}->{$name} = dclone($gantt_to_use); oar_debug("[oar_sched_gantt_with_timesharing] Create new gantt for ($user, $name)\n"); } } #Fill all other gantts foreach my $g (keys(%Container_gantt_hash)){ if ($i != $g){ oar_debug("[oar_sched_gantt_with_timesharing] Add occupation in container $g for job $i\n"); OAR::Schedulers::GanttHoleStorage::set_occupation( $Container_gantt_hash{$g}, $already_scheduled_jobs{$i}->[0], $job_duration + $security_time_overhead, $vec ); } } foreach my $c (keys(%{$timesharing_gantts})){ foreach my $u (keys(%{$timesharing_gantts->{$c}})){ foreach my $n (keys(%{$timesharing_gantts->{$c}->{$u}})){ if ((!defined($user)) or (!defined($name)) or (($u ne $user) or ($n ne $name))){ OAR::Schedulers::GanttHoleStorage::set_occupation( $timesharing_gantts->{$c}->{$u}->{$n}, $already_scheduled_jobs{$i}->[0], $job_duration + $security_time_overhead, $vec ); } } } } } } oar_debug("[oar_sched_gantt_with_timesharing] End gantt initialization\n"); # End of the initialisation # Begining of the real scheduling # Get list of Alive resources my $alive_resources_vector = ''; foreach my $r (OAR::IO::get_resources_in_state($base,"Alive")){ vec($alive_resources_vector, $r->{resource_id}, 1) = 1; } # ENERGY SAVING: add fake occupations/holes from energy saving configuration # CM part and Hulot part (wake up nodes in energy saving mode) if (is_conf("SCHEDULER_NODE_MANAGER_WAKE_UP_CMD") or (get_conf("ENERGY_SAVING_INTERNAL") eq "yes" and is_conf("ENERGY_SAVING_NODE_MANAGER_WAKE_UP_CMD"))){ oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] [START] Checking EnergySaving occupation\n"); # Check the resources that can be waked_up or shut down my $upto_availability = OAR::IO::get_energy_saving_resources_availability($base, $current_time); foreach my $t (keys(%{$upto_availability})){ my $vec = ''; foreach my $r (@{$upto_availability->{$t}}){ vec($alive_resources_vector, $r, 1) = 1; vec($vec,$r,1) = 1; } #Fill all the gantts foreach my $g (keys(%Container_gantt_hash)){ oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] Add EnergySaving occupation in container $g for available_upto = $t\n"); OAR::Schedulers::GanttHoleStorage::set_occupation( $Container_gantt_hash{$g}, $t, OAR::Schedulers::GanttHoleStorage::get_infinity_value(), $vec ); } foreach my $c (keys(%{$timesharing_gantts})){ foreach my $u (keys(%{$timesharing_gantts->{$c}})){ foreach my $n (keys(%{$timesharing_gantts->{$c}->{$u}})){ OAR::Schedulers::GanttHoleStorage::set_occupation( $timesharing_gantts->{$c}->{$u}->{$n}, $t, OAR::Schedulers::GanttHoleStorage::get_infinity_value(), $vec ); } } } } oar_debug("[oar_sched_gantt_with_timesharing_and_fairsharing] [END] Checking EnergySaving occupation\n"); } # CM part my @Dead_resources; foreach my $r (OAR::IO::get_resources_in_state($base,"Dead")){ push(@Dead_resources, $r->{resource_id}); } my @jobs = OAR::IO::get_jobs_to_schedule($base,$queue,$max_waiting_jobs_to_schedule); my $job_index = 0; while (($job_index <= $#jobs) and ((time() - $initial_time) < $timeout)){ my $j = $jobs[$job_index]; $job_index ++; oar_debug("[oar_sched_gantt_with_timesharing] [$j->{job_id}] Start scheduling\n"); my $scheduler_init_date = $current_time; # Search for dependencies my $skip_job = 0; foreach my $d (OAR::IO::get_current_job_dependencies($base,$j->{job_id})){ next if ($skip_job == 1); my $dep_job = OAR::IO::get_job($base,$d); if (($dep_job->{state} ne "Terminated")){ my @date_tmp = OAR::IO::get_gantt_job_start_time($base,$d); if (defined($date_tmp[0])){ my $mold_dep = OAR::IO::get_current_moldable_job($base,$date_tmp[1]); my $sched_tmp = $date_tmp[0] + $mold_dep->{moldable_walltime}; if ($scheduler_init_date < $sched_tmp){ $scheduler_init_date = $sched_tmp + (2 * $security_time_overhead); } }else{ my $message = "Cannot determine scheduling time due to dependency with the job $d"; OAR::IO::set_job_message($base,$j->{job_id},$message); OAR::IO::set_job_scheduler_info($base,$j->{job_id},$message); oar_debug("[oar_sched_gantt_with_timesharing] [$j->{job_id}] $message\n"); $skip_job = 1; next; } }elsif (($dep_job->{job_type} eq "PASSIVE") and ($dep_job->{exit_code} != 0)){ my $message = "Cannot determine scheduling time due to dependency with the job $d (exit code != 0)"; OAR::IO::set_job_message($base,$j->{job_id},$message); OAR::IO::set_job_scheduler_info($base,$j->{job_id},$message); oar_debug("[oar_sched_gantt_with_timesharing] [$j->{job_id}] $message\n"); $skip_job = 1; next; } } next if ($skip_job == 1); my $gantt_to_use = $Container_gantt_hash{0}; my $container_num = 0; my $types = OAR::IO::get_current_job_types($base,$j->{job_id}); if (defined($types->{inner}) and ($types->{inner} =~ /^(\d+)$/)){ if (defined($Container_gantt_hash{$1})){ $gantt_to_use = $Container_gantt_hash{$1}; $container_num = $1; oar_debug("[oar_sched_gantt_with_timesharing] [$j->{job_id}] Using container $container_num\n"); }else{ oar_debug("[oar_sched_gantt_with_timesharing] [$j->{job_id}] Container $1 does not exist.\n"); OAR::IO::set_job_message($base,$j->{job_id},"Container $1 does not exist"); OAR::IO::set_job_scheduler_info($base,$j->{job_id},"Container $1 does not exist"); next; } } if (defined($types->{timesharing})){ my ($user, $name) = parse_timesharing($types->{timesharing}, $j->{job_user}, $j->{job_name}); if (!defined($timesharing_gantts->{$container_num}->{$user}->{$name})){ $timesharing_gantts->{$container_num}->{$user}->{$name} = dclone($gantt_to_use); oar_debug("[oar_sched_gantt_with_timesharing] Create new gantt in phase II for ($user, $name)\n"); } $gantt_to_use = $timesharing_gantts->{$container_num}->{$user}->{$name}; oar_debug("[oar_sched_gantt_with_timesharing] Use gantt for ($container_num,$user,$name)\n"); } #oar_debug("[oar_sched_gantt_with_timesharing] Use gantt for $j->{job_id}:\n".OAR::Schedulers::GanttHoleStorage::pretty_print($gantt_to_use)."\n"); my $job_properties = "\'1\'"; if ((defined($j->{properties})) and ($j->{properties} ne "")){ $job_properties = $j->{properties}; } # Choose the moldable job to schedule my @moldable_results; my $job_descriptions = OAR::IO::get_resources_data_structure_current_job($base,$j->{job_id}); foreach my $moldable (@{$job_descriptions}){ #my $moldable = $job_descriptions->[0]; my $duration; if (defined($types->{besteffort})){ $duration = $besteffort_duration; }else{ $duration = $moldable->[1] + $security_time_overhead; } my @tree_list; foreach my $m (@{$moldable->[0]}){ my $tmp_properties = "\'1\'"; if ((defined($m->{property})) and ($m->{property} ne "")){ $tmp_properties = $m->{property}; } my $tmp_tree = OAR::IO::get_possible_wanted_resources($base_ro,$alive_resources_vector,undef,\@Dead_resources,"$job_properties AND $tmp_properties", $m->{resources}, $Order_part); push(@tree_list, $tmp_tree); #my @leafs = OAR::Schedulers::ResourceTree::get_tree_leafs($tmp_tree); #foreach my $l (@leafs){ # vec($resource_id_used_list_vector, OAR::Schedulers::ResourceTree::get_current_resource_value($l), 1) = 1; #} } my $gantt_timeout = ($timeout - (time() - $initial_time)) / 4; $gantt_timeout = $Minimum_timeout_per_job if ($gantt_timeout <= ($timeout / 8)); oar_debug("[oar_sched_gantt_with_timesharing] [$j->{job_id}] find_first_hole with a timeout of $gantt_timeout\n"); my @hole; if ($Max_nb_processes <= 1){ @hole = OAR::Schedulers::GanttHoleStorage::find_first_hole($gantt_to_use, $scheduler_init_date, $duration, \@tree_list,$gantt_timeout); }else{ oar_debug("[oar_sched_gantt_with_timesharing] [$j->{job_id}] Using Gantt PARALLEL algorithm\n"); @hole = OAR::Schedulers::GanttHoleStorage::find_first_hole_parallel($gantt_to_use, $scheduler_init_date, $duration, \@tree_list,$gantt_timeout,$Max_nb_processes); } # print("[GANTT] 10 ".gettimeofday."\n"); my @res_trees; my @resources; foreach my $t (@{$hole[1]}){ # print("[GANTT] 11 ".gettimeofday."\n"); #my $minimal_tree = OAR::Schedulers::ResourceTree::delete_unnecessary_subtrees($t); # print("[GANTT] 12 ".gettimeofday."\n"); push(@res_trees, $t); foreach my $r (OAR::Schedulers::ResourceTree::get_tree_leafs($t)){ push(@resources, OAR::Schedulers::ResourceTree::get_current_resource_value($r)); } # print("[GANTT] 13 ".gettimeofday."\n"); } push(@moldable_results, { resources => \@resources, start_date => $hole[0], duration => $duration, moldable_id => $moldable->[2] }); } # Choose moldable job which will finish the first my $index_to_choose = -1; my $best_stop_time; # print("[GANTT] 14 ".gettimeofday."\n"); for (my $i=0; $i <= $#moldable_results; $i++){ #my @tmp_array = @{$moldable_results[$i]->{resources}}; if ($#{$moldable_results[$i]->{resources}} >= 0){ my $tmp_stop_date = $moldable_results[$i]->{start_date} + $moldable_results[$i]->{duration}; if ((!defined($best_stop_time)) or ($best_stop_time > $tmp_stop_date)){ $best_stop_time = $tmp_stop_date; $index_to_choose = $i; } } } if ($index_to_choose >= 0){ # We can schedule the job # print("[GANTT] 15 ".gettimeofday."\n"); my $vec = ''; foreach my $r (@{$moldable_results[$index_to_choose]->{resources}}){ vec($vec, $r, 1) = 1; } #Fill all other gantts foreach my $g (keys(%Container_gantt_hash)){ if ($j->{job_id} != $g){ oar_debug("[oar_sched_gantt_with_timesharing] Add live occupation in container $g for job $j->{job_id}\n"); OAR::Schedulers::GanttHoleStorage::set_occupation( $Container_gantt_hash{$g}, $moldable_results[$index_to_choose]->{start_date}, $moldable_results[$index_to_choose]->{duration}, $vec ); } } if (defined($types->{container})){ $Container_gantt_hash{$j->{job_id}} = OAR::Schedulers::GanttHoleStorage::new_with_1_hole($max_resources, $minimum_hole_time, $moldable_results[$index_to_choose]->{start_date}, $moldable_results[$index_to_choose]->{duration}, $vec, $All_resource_list_vec); } foreach my $c (keys(%{$timesharing_gantts})){ foreach my $u (keys(%{$timesharing_gantts->{$c}})){ # print("[GANTT] 17 ".gettimeofday."\n"); foreach my $n (keys(%{$timesharing_gantts->{$c}->{$u}})){ if (($gantt_to_use != $timesharing_gantts->{$c}->{$u}->{$n})){ OAR::Schedulers::GanttHoleStorage::set_occupation( $timesharing_gantts->{$c}->{$u}->{$n}, $moldable_results[$index_to_choose]->{start_date}, $moldable_results[$index_to_choose]->{duration}, $vec ); } } } } #update database push(@{$moldable_results[$index_to_choose]->{resources}},@Resources_to_always_add); OAR::IO::add_gantt_scheduled_jobs($base,$moldable_results[$index_to_choose]->{moldable_id}, $moldable_results[$index_to_choose]->{start_date},$moldable_results[$index_to_choose]->{resources}); OAR::IO::set_job_message($base,$j->{job_id},"FIFO scheduling OK"); OAR::IO::set_job_scheduler_info($base,$j->{job_id},"FIFO scheduling OK"); }else{ my $message = "Cannot find enough resources which fit for the job $j->{job_id}"; OAR::IO::set_job_message($base,$j->{job_id},$message); OAR::IO::set_job_scheduler_info($base,$j->{job_id},$message); oar_debug("[oar_sched_gantt_with_timesharing] [$j->{job_id}] $message\n"); } # print("[GANTT] 18 ".gettimeofday."\n"); oar_debug("[oar_sched_gantt_with_timesharing] [$j->{job_id}] End scheduling\n"); } OAR::IO::disconnect($base); OAR::IO::disconnect($base_ro); if ($job_index <= $#jobs){ oar_debug("[oar_sched_gantt_with_timesharing] I am not able to schedule all waiting jobs in the specified time : $timeout s\n"); } oar_debug("[oar_sched_gantt_with_timesharing] End of scheduler for queue $queue\n"); ./oar-2.5.2/sources/core/modules/scheduler/oar_meta_sched0000755000175000017500000002607711757171206021521 0ustar plbplb#!/usr/bin/perl # $Id$ use strict; use warnings; use DBI(); use OAR::IO; use OAR::Modules::Judas qw(oar_debug oar_warn oar_error set_current_log_category); use OAR::Conf qw(init_conf dump_conf get_conf is_conf get_conf_with_default_param); use Data::Dumper; use OAR::Schedulers::Scheduler; use OAR::Tools; use OAR::Modules::Hulot; # Log category set_current_log_category('scheduler'); my $exit_code = 0; my $base = OAR::IO::connect(); my $base_ro = OAR::IO::connect_ro(); init_conf($ENV{OARCONFFILE}); my $security_time_overhead = 60; if (is_conf("SCHEDULER_JOB_SECURITY_TIME")){ $security_time_overhead = get_conf("SCHEDULER_JOB_SECURITY_TIME"); } my $minimum_hole_time = 0; if (is_conf("SCHEDULER_GANTT_HOLE_MINIMUM_TIME")){ $minimum_hole_time = get_conf("SCHEDULER_GANTT_HOLE_MINIMUM_TIME"); } my $Order_part = get_conf("SCHEDULER_RESOURCE_ORDER"); my $binpath; if (defined($ENV{OARDIR})){ $binpath = $ENV{OARDIR}."/"; }else{ die("[MetaSched] OARDIR env variable must be defined\n"); } my $Cpuset_field = get_conf("JOB_RESOURCE_MANAGER_PROPERTY_DB_FIELD"); my $Remote_host = get_conf("SERVER_HOSTNAME"); my $Remote_port = get_conf("SERVER_PORT"); oar_debug("[MetaSched] Start of meta scheduler\n"); OAR::Schedulers::Scheduler::init_scheduler($base,$base_ro,$security_time_overhead,$minimum_hole_time,$Order_part,get_conf("RESERVATION_WAITING_RESOURCES_TIMEOUT")); my %initial_time = OAR::Schedulers::Scheduler::get_initial_time(); my @queues = OAR::IO::get_active_queues($base); my $name; my $policy; foreach my $i (@queues){ $name = $i->[0]; $policy = $i->[1]; my $waiting_jobs = OAR::IO::is_waiting_job_specific_queue_present($base,$name); if ($waiting_jobs == 1){ oar_debug("[MetaSched] Launching scheduler $name with $policy at time $initial_time{sql}\n"); my ($sched_exit_code,$sched_signal_num,$sched_dumped_core) = OAR::Tools::launch_command("$binpath/schedulers/$policy $name $initial_time{sec} \"$initial_time{sql}\""); if (($sched_signal_num != 0) || ($sched_dumped_core != 0)){ oar_error("[MetaSched] Something wrong occured, we inactive the queue $name (signal or core dumped or '$binpath/schedulers/$policy' cannot be executed)!!! Look at 'oarnotify' if you want to change queue settings.\n"); OAR::IO::stop_a_queue($base,$name); } if ($sched_exit_code == 1){ $exit_code = 1; }elsif($sched_exit_code != 0){ oar_error("[MetaSched] Scheduler $binpath/schedulers/$policy on queue $name at time $initial_time{sec} returns a bad value : $sched_exit_code. So this queue will be disabled (look at 'oarnotify' if you want to change queue settings).\n"); OAR::IO::stop_a_queue($base,$name); #$exit_code = 3; } if (OAR::Schedulers::Scheduler::treate_waiting_reservation_jobs($base,$name) != 0){ $exit_code = 1; } if (OAR::Schedulers::Scheduler::check_reservation_jobs($base,$base_ro,$name,$Order_part) != 0){ $exit_code = 1; } }else{ oar_debug("[MetaSched] No waiting job in $name queue\n"); } } if ($exit_code == 0){ if (OAR::Schedulers::Scheduler::check_jobs_to_kill($base) == 1){ # We must kill besteffort jobs OAR::Tools::notify_tcp_socket($Remote_host,$Remote_port,"ChState"); $exit_code = 2; }elsif (OAR::Schedulers::Scheduler::check_jobs_to_launch($base) == 1){ $exit_code = 1; } } #Update visu gantt tables OAR::Schedulers::Scheduler::update_gantt_visu_tables($base); OAR::IO::disconnect($base_ro); # Manage dynamic node feature my $timeout_cmd = 10; my $flagHulot=0; if (is_conf("SCHEDULER_TIMEOUT")){ $timeout_cmd = get_conf("SCHEDULER_TIMEOUT"); } if ((is_conf("SCHEDULER_NODE_MANAGER_SLEEP_CMD") or (get_conf("ENERGY_SAVING_INTERNAL") eq "yes" and is_conf("ENERGY_SAVING_NODE_MANAGER_SLEEP_CMD"))) and is_conf("SCHEDULER_NODE_MANAGER_SLEEP_TIME") and is_conf("SCHEDULER_NODE_MANAGER_IDLE_TIME")){ my @node_halt= OAR::Schedulers::Scheduler::get_idle_nodes( $base, get_conf("SCHEDULER_NODE_MANAGER_IDLE_TIME"), get_conf("SCHEDULER_NODE_MANAGER_SLEEP_TIME") ); if ($#node_halt >= 0){ oar_debug("[MetaSched] Some nodes can be halted : @node_halt\n"); # Using the built-in energy saving module to shut down nodes if (get_conf_with_default_param("ENERGY_SAVING_INTERNAL", "no") eq "yes") { if (OAR::Modules::Hulot::halt_nodes(\@node_halt) ) { oar_error("[MetaSched] Communication problem with Hulot (the energy saving module)!\n"); } $flagHulot=1; } # Not using the built-in energy saving module to shut down nodes else { my $cmd = get_conf("SCHEDULER_NODE_MANAGER_SLEEP_CMD"); if (! defined(OAR::Tools::fork_and_feed_stdin($cmd, $timeout_cmd, \@node_halt))){ oar_error("[MetaSched] Try to launch the command $cmd to stop some nodes but the command timeouted($timeout_cmd s).\n"); } oar_debug("[MetaSched] @node_halt should be shutting down now.\n"); } } } if (is_conf("SCHEDULER_NODE_MANAGER_WAKE_UP_CMD") or (get_conf("ENERGY_SAVING_INTERNAL") eq "yes" and is_conf("ENERGY_SAVING_NODE_MANAGER_WAKE_UP_CMD"))){ # Wake up right nodes my @nodes = OAR::Schedulers::Scheduler::get_nodes_to_wake_up($base); if ($#nodes >= 0){ oar_debug("[MetaSched] Some nodes must be started : @nodes\n"); # Using the built-in energy saving module to wake up nodes if (get_conf_with_default_param("ENERGY_SAVING_INTERNAL", "no") eq "yes") { if (OAR::Modules::Hulot::wake_up_nodes(\@nodes) ) { oar_error("[MetaSched] Communication problem with Hulot (the energy saving module)!\n"); } $flagHulot=1; } # Not using the built-in energy saving module to wake up nodes else { my $cmd = get_conf("SCHEDULER_NODE_MANAGER_WAKE_UP_CMD"); if (! defined(OAR::Tools::fork_and_feed_stdin($cmd, $timeout_cmd, \@nodes))){ oar_error("[MetaSched] Try to launch the command $cmd to wake up some nodes but the command timeouted($timeout_cmd s).\n"); } } } } # Send CHECK signal to Hulot if needed if(!$flagHulot and (get_conf_with_default_param("ENERGY_SAVING_INTERNAL", "no") eq "yes")){ if (OAR::Modules::Hulot::check() ) { oar_error("[MetaSched] Communication problem with Hulot (the energy saving module)!\n"); } } # Search jobs to resume foreach my $j (OAR::IO::get_jobs_in_state($base, "Resuming")){ my @other_jobs = OAR::IO::get_jobs_on_resuming_job_resources($base,$j->{job_id}); # TODO : look for timesharing other jobs. What do we do????? if ($#other_jobs < 0){ # We can resume the job oar_debug("[MetaSched] We can resume the job $j\n"); ############### # RESUME PART # ############### my $script = get_conf("JUST_BEFORE_RESUME_EXEC_FILE"); my $timeout = get_conf("SUSPEND_RESUME_SCRIPT_TIMEOUT"); $timeout = OAR::Tools::get_default_suspend_resume_script_timeout() if (!defined($timeout)); my $skip = 0; if (defined($script)){ # Launch admin script my $script_error = 0; eval { $SIG{ALRM} = sub { die "alarm\n" }; alarm($timeout); oar_debug("[OAR_META_SCHED] [$j->{job_id}] LAUNCH the script just after the suspend : $script $j->{job_id}\n"); $script_error = system("$script script $j->{job_id}"); oar_debug("[OAR_META_SCHED] [$j->{job_id}] END the script just after the suspend : $script $j->{job_id}\n"); alarm(0); }; if( $@ || ($script_error != 0)){ my $str = "[OAR_META_SCHED] [$j->{job_id}] Suspend script error : $@; return code = $script_error\n"; oar_error($str); OAR::IO::add_new_event($base,"RESUME_SCRIPT_ERROR",$j->{job_id},$str); OAR::IO::frag_job($base,$j->{job_id}); OAR::Tools::notify_tcp_socket($Remote_host,$Remote_port,"Qdel"); $skip = 1; } } if ((defined($Cpuset_field)) and ($skip == 0)){ my $cpuset_name = OAR::IO::get_job_cpuset_name($base, $j->{job_id}) if (defined($Cpuset_field)); my $cpuset_nodes = OAR::IO::get_cpuset_values_for_a_moldable_job($base,$Cpuset_field,$j->{assigned_moldable_job}); my $suspend_data_hash = { name => $cpuset_name, job_id => $j->{job_id}, oarexec_pid_file => OAR::Tools::get_oar_pid_file_name($j->{job_id}), }; if (defined($cpuset_nodes)){ my $taktuk_cmd = get_conf("TAKTUK_CMD"); my $openssh_cmd = get_conf("OPENSSH_CMD"); $openssh_cmd = OAR::Tools::get_default_openssh_cmd() if (!defined($openssh_cmd)); if (is_conf("OAR_SSH_CONNECTION_TIMEOUT")){ OAR::Tools::set_ssh_timeout(get_conf("OAR_SSH_CONNECTION_TIMEOUT")); } my $suspend_file = get_conf("SUSPEND_RESUME_FILE"); $suspend_file = OAR::Tools::get_default_suspend_resume_file() if (!defined($suspend_file)); $suspend_file = "$ENV{OARDIR}/$suspend_file" if ($suspend_file !~ /^\//); my ($tag,@bad) = OAR::Tools::manage_remote_commands([keys(%{$cpuset_nodes})],$suspend_data_hash,$suspend_file,"resume",$openssh_cmd,$taktuk_cmd,$base); if ($tag == 0){ my $str = "[OAR_META_SCHED] [SUSPEND_RESUME] [$j->{job_id}] Bad suspend/resume file : $suspend_file\n"; oar_error($str); OAR::IO::add_new_event($base, "SUSPEND_RESUME_MANAGER_FILE", $j->{job_id}, $str); }else{ if (($#bad < 0)){ OAR::IO::resume_job_action($base,$j->{job_id}); }else{ my $str = "[OAR_META_SCHED] [SUSPEND_RESUME] [$j->{job_id}] Error on several nodes : @bad\n"; oar_error($str); OAR::IO::add_new_event_with_host($base,"RESUME_ERROR",$j->{job_id},$str,\@bad); OAR::IO::frag_job($base,$j->{job_id}); # A Leon must be run $exit_code = 2; } } } } ##################### # RESUME PART, END # ##################### } } # Notify oarsub -I when they will be launched foreach my $j (OAR::IO::get_gantt_waiting_interactive_prediction_date($base)){ my ($addr,$port) = split(/:/,$j->{info_type}); my $new_start_prediction = OAR::IO::local_to_sql($j->{start_time}); oar_debug("[MetaSched] [$j->{job_id}] Notifying user of the start prediction: $new_start_prediction (".$j->{message}.")\n"); OAR::Tools::notify_tcp_socket($addr,$port,"[$initial_time{sql}] Start prediction: $new_start_prediction (".$j->{message}.")"); } OAR::IO::disconnect($base); oar_debug("[MetaSched] End of meta scheduler\n"); exit($exit_code); ./oar-2.5.2/sources/core/modules/scheduler/README0000644000175000017500000000302411757171206017501 0ustar plbplbDefinition of the scheduler's interface: The scheduler can be any executable file, stored in the schedulers repository (/usr/lib/oar/schedulers/ for installations using the Debian packages). The scheduler is executed by the "metascheduler", that handles sequentially the different queues by priority order and makes the glue between them (besteffort jobs to kill, etc). The scheduler executable has the following interface: - it gets 3 arguments: 1. queue name 2. reference time in second 3. reference time in sql format (for conveniance only) - only jobs of your queue and with the state "Waiting" or "Reservation = None" should be manipulated - any information stored in the database can however be taken into account (read). - previous decisions of the other schedulers (other queues) should be taken into account: information from tables gantt_jobs_predictions_ and gantt_jobs_resources_, in order to avoid conflicts between jobs of different queues. - decisions must be stored in the tables gantt_jobs_predictions_ and gantt_jobs_resources_ - the state of some jobs can be set to "toError" so that OAR delete them after the scheduler's run. If any job is set to that state, the scheduler must return an exit code equal to 1, in order to notify the metascheduler, otherwise exit code must be 0. As an example, you can look at the default OAR scheduler "perl/oar_sched_gantt_with_timesharing". It uses a gantt and a resource tree libraries that are essential to take some decisions. ./oar-2.5.2/sources/core/modules/sarko.pl0000755000175000017500000002054111757171206016325 0ustar plbplb#!/usr/bin/perl # $Id$ #Almighty module : check walltimes and jobs to frag use strict; use DBI(); use Data::Dumper; use OAR::IO; use OAR::Modules::Judas qw(oar_debug oar_warn oar_error set_current_log_category); use OAR::Conf qw(init_conf dump_conf get_conf is_conf); use OAR::Tools; # Log category set_current_log_category('main'); # Get job delete and checkpoint walltime values my $Leon_soft_walltime = OAR::Tools::get_default_leon_soft_walltime(); my $Leon_walltime = OAR::Tools::get_default_leon_walltime(); init_conf($ENV{OARCONFFILE}); if (is_conf("JOBDEL_SOFTWALLTIME")){ $Leon_soft_walltime = get_conf("JOBDEL_SOFTWALLTIME"); } if (is_conf("JOBDEL_WALLTIME")){ $Leon_walltime = get_conf("JOBDEL_WALLTIME"); } if ($Leon_walltime <= $Leon_soft_walltime){ $Leon_walltime = $Leon_soft_walltime + 1; oar_warn("[sarko] (JOBDEL_WALLTIME <= JOBDEL_SOFTWALLTIME) so I must set JOBDEL_WALLTIME to $Leon_walltime\n"); } my $Server_hostname = get_conf("SERVER_HOSTNAME"); my $Deploy_hostname = get_conf("DEPLOY_HOSTNAME"); if (!defined($Deploy_hostname)){ $Deploy_hostname = $Server_hostname; } my $Cosystem_hostname = get_conf("COSYSTEM_HOSTNAME"); if (!defined($Cosystem_hostname)){ $Cosystem_hostname = $Server_hostname; } my $Openssh_cmd = get_conf("OPENSSH_CMD"); $Openssh_cmd = OAR::Tools::get_default_openssh_cmd() if (!defined($Openssh_cmd)); if (is_conf("OAR_SSH_CONNECTION_TIMEOUT")){ OAR::Tools::set_ssh_timeout(get_conf("OAR_SSH_CONNECTION_TIMEOUT")); } if (is_conf("OAR_RUNTIME_DIRECTORY")){ OAR::Tools::set_default_oarexec_directory(get_conf("OAR_RUNTIME_DIRECTORY")); } oar_debug("[sarko] JOBDEL_SOFTWALLTIME = $Leon_soft_walltime; JOBDEL_WALLTIME = $Leon_walltime\n"); # get script args my $base = OAR::IO::connect(); if (!defined($base)){ oar_error("[sarko] Can not connect to the database\n"); exit(1); } oar_debug("[sarko] Hello, identity control !!!\n"); my $guilty_found=0; my $current = OAR::IO::get_date($base); oar_debug("[sarko] Current time : $current\n"); # Look at leon timers # Decide if OAR must retry to delete the job or just change values in the database foreach my $j (OAR::IO::get_timered_job($base)){ my $job_ref = OAR::IO::get_job($base,$j->{job_id}); if (($job_ref->{state} eq "Terminated") || ($job_ref->{state} eq "Error") || ($job_ref->{state} eq "Finishing")){ OAR::IO::job_fragged($base,$j->{job_id}); oar_debug("[sarko] I set to FRAGGED the job $j->{job_id}\n"); }else{ my $frag_date = OAR::IO::get_frag_date($base,$j->{job_id}); oar_debug("[sarko] frag date : $frag_date , $frag_date\n"); if (($current > $frag_date+$Leon_soft_walltime) && ($current <= $frag_date+$Leon_walltime)){ oar_debug("[sarko] Leon will RE-FRAG bipbip of job $j->{job_id}\n"); OAR::IO::job_refrag($base,$j->{job_id}); $guilty_found=1; }elsif ($current > $frag_date+$Leon_walltime){ oar_debug("[sarko] Leon will EXTERMINATE bipbip of job $j->{job_id}\n"); OAR::IO::job_leon_exterminate($base,$j->{job_id}); $guilty_found=1; }else{ oar_debug("[sarko] The leon timer is not yet expired for the job $j->{job_id}; I do nothing\n"); } } } # Look at job walltimes foreach my $job (OAR::IO::get_jobs_in_state($base, "Running")){ my ($start, $max); # Get starting time $start = $job->{start_time}; # Get maxtime my $mold_job = OAR::IO::get_current_moldable_job($base,$job->{assigned_moldable_job}); $max = $mold_job->{moldable_walltime}; if ($job->{suspended} eq "YES"){ # This job was suspended so we must recalculate the walltime $max += OAR::IO::get_job_suspended_sum_duration($base,$job->{job_id},$current); } oar_debug("[sarko] Job [$job->{job_id}] from $start with $max; current time=$current\n"); if ($current > $start+$max){ oar_debug("--> (Elapsed)\n"); $guilty_found=1; OAR::IO::lock_table($base,["frag_jobs","event_logs","jobs"]); OAR::IO::frag_job($base, $job->{job_id}); OAR::IO::unlock_table($base); OAR::IO::add_new_event($base,"WALLTIME",$job->{job_id},"[sarko] Job [$job->{job_id}] from $start with $max; current time=$current (Elapsed)"); }elsif (($job->{checkpoint} > 0) && ($current >= ($start+$max-$job->{checkpoint}))){ # OAR must notify the job to checkpoint itself oar_debug("[sarko] Send checkpoint signal to the job $job->{job_id}\n"); # Retrieve node names used by the job my @hosts = OAR::IO::get_job_current_hostnames($base,$job->{job_id}); my $types = OAR::IO::get_current_job_types($base,$job->{job_id}); my $host_to_connect = $hosts[0]; if ((defined($types->{cosystem})) or ($#hosts < 0)){ $host_to_connect = $Cosystem_hostname; }elsif (defined($types->{deploy})){ $host_to_connect = $Deploy_hostname; } OAR::IO::add_new_event($base,"CHECKPOINT",$job->{job_id},"User oar (sarko) requested a checkpoint on the job $job->{job_id} on $host_to_connect"); my $str_comment; my @exit_codes; # Timeout the ssh command eval { $SIG{ALRM} = sub { die "alarm\n" }; alarm(OAR::Tools::get_ssh_timeout()); @exit_codes = OAR::Tools::signal_oarexec($host_to_connect,$job->{job_id},"SIGUSR2",1,$base, $Openssh_cmd, ''); alarm(0); }; if ($@){ if ($@ eq "alarm\n"){ $str_comment = "[sarko] Cannot contact $hosts[0], operation timouted (".OAR::Tools::get_ssh_timeout()." s). So I cannot send checkpoint signal to the job $job->{job_id} on $host_to_connect"; oar_warn("$str_comment\n"); OAR::IO::add_new_event($base,"CHECKPOINT_ERROR",$job->{job_id},$str_comment); }else{ $str_comment = "[sarko] An unknown error occured during the sending of the checkpoint signal to the job $job->{job_id} on the host $host_to_connect"; oar_warn("$str_comment\n"); OAR::IO::add_new_event($base,"CHECKPOINT_ERROR",$job->{job_id},$str_comment); } }else{ if ($exit_codes[0] == 0){ $str_comment = "[sarko] The job $job->{job_id} was notified to checkpoint itself on the node $host_to_connect"; oar_debug("$str_comment\n"); OAR::IO::add_new_event($base,"CHECKPOINT_SUCCESSFULL",$job->{job_id},$str_comment); }else{ $str_comment = "[sarko] The kill command return a bad exit code (@exit_codes) for the job $job->{job_id} on the node $host_to_connect"; oar_warn("$str_comment\n"); OAR::IO::add_new_event($base,"CHECKPOINT_ERROR",$job->{job_id},$str_comment); } } } } # Retrieve nodes with expiry_dates in the past # special for Desktop computing my @resources = OAR::IO::get_expired_resources($base); if ($#resources >= 0) { # First mark the nodes as dead foreach my $r (@resources) { OAR::IO::set_resource_nextState($base, $r, 'Suspected'); my $rinfo = OAR::IO::get_resource_info($base, $r); OAR::IO::add_new_event_with_host($base, "LOG_SUSPECTED", 0, "The DESKTOP COMPUTING resource $r has expired on node $rinfo->{network_address}", [$rinfo->{network_address}]); } # Then notify Almighty my $remote_host = get_conf("SERVER_HOSTNAME"); my $remote_port = get_conf("SERVER_PORT"); OAR::Tools::notify_tcp_socket($remote_host,$remote_port,"ChState"); } my $dead_switch_time = OAR::Tools::get_default_dead_switch_time(); if (is_conf("DEAD_SWITCH_TIME")){ $dead_switch_time = get_conf("DEAD_SWITCH_TIME"); } # Get Absent and Suspected nodes for more than 5 mn (default) if ($dead_switch_time > 0){ my $notify = 0; foreach my $r (OAR::IO::get_absent_suspected_resources_for_a_timeout($base,$dead_switch_time)){ OAR::IO::set_resource_nextState($base,$r,"Dead"); OAR::IO::update_resource_nextFinaudDecision($base,$r,"YES"); oar_debug("[Sarko] Set the next state of $r to Dead\n"); $notify = 1; } if ($notify > 0){ my $remote_host = get_conf("SERVER_HOSTNAME"); my $remote_port = get_conf("SERVER_PORT"); OAR::Tools::notify_tcp_socket($remote_host,$remote_port,"ChState"); } } OAR::IO::disconnect($base); exit($guilty_found); ./oar-2.5.2/sources/core/modules/runner/0000755000175000017500000000000011757171206016155 5ustar plbplb./oar-2.5.2/sources/core/modules/runner/runner0000755000175000017500000001424511757171206017422 0ustar plbplb#!/usr/bin/perl # $Id$ #Almighty module : launch job bipbips use strict; use DBI(); use Data::Dumper; use OAR::IO; use OAR::Modules::Judas qw(oar_debug oar_warn oar_error set_current_log_category); use OAR::Conf qw(init_conf dump_conf get_conf is_conf get_conf_with_default_param); use OAR::WindowForker; use OAR::Tools; # Log category set_current_log_category('main'); init_conf($ENV{OARCONFFILE}); my $binpath; if (defined($ENV{OARDIR})){ $binpath = $ENV{OARDIR}."/"; }else{ oar_error("[Runner] OARDIR env variable must be defined\n"); exit(3); } my $batch = $binpath."bipbip"; my $Exit_code = 0; sub answer($$$){ my $jobid = shift; my $info = shift; my $message = shift; my $error = 0; my ($addr,$port) = split(/:/,$info); oar_debug("[Runner] oarsub addr:port = $addr:$port info = $info\n"); if(!defined(OAR::Tools::notify_tcp_socket($addr,$port,"$message"))){ oar_debug("[Runner] Notification done\n"); }else{ oar_debug("[Runner] Cannot open connection to oarsub client for job $jobid !\n"); $error = 1; } return($error); } my $base = OAR::IO::connect(); # for toError jobs foreach my $j (OAR::IO::get_jobs_in_state($base,"toError")){ oar_debug("[Runner] Treate job $j->{job_id} in toError state\n"); if ($j->{job_type} eq "INTERACTIVE"){ oar_debug("[Runner] Notify oarsub for an INTERACTIVE job (num:$j->{job_id}) in error; jobInfo=$j->{info_type}\n"); answer($j->{job_id},$j->{info_type},"$j->{message}"); answer($j->{job_id},$j->{info_type},"BAD JOB"); }elsif(($j->{job_type} eq "PASSIVE") && ($j->{reservation} eq "Scheduled")){ oar_debug("[Runner] Notify oarsub for a reservation job (num:$j->{job_id}) in error; jobInfo=$j->{info_type}\n"); answer($j->{job_id},$j->{info_type},"$j->{message}"); answer($j->{job_id},$j->{info_type},"BAD JOB"); } oar_debug("[Runner] Set job $j->{job_id} to state Error\n"); OAR::IO::set_job_state($base, $j->{job_id}, "Error"); } # for toAckReservation jobs foreach my $j (OAR::IO::get_jobs_in_state($base,"toAckReservation")){ oar_debug("[Runner] Treate job $j->{job_id} in toAckReservation state\n"); if (answer($j->{job_id},$j->{info_type},"GOOD RESERVATION") == 1){ oar_warn("[Runner] Frag job $j->{job_id}, I cannot notify oarsub for the reservation\n"); OAR::IO::add_new_event($base,"CANNOT_NOTIFY_OARSUB",$j->{job_id},"[runner] Can not notify oarsub for the job $j->{job_id}"); OAR::IO::lock_table($base,["frag_jobs","event_logs","jobs"]); OAR::IO::frag_job($base,$j->{job_id}); OAR::IO::unlock_table($base); $Exit_code = 1; }else{ oar_debug("[Runner] Notify oarsub for a RESERVATION (idJob=$j->{job_id}) --> OK; jobInfo=$j->{info_type}\n"); OAR::IO::set_job_state($base,$j->{job_id} , "Waiting"); # Test if we must notify Almighty to Launch scheduler for the reservation if ($Exit_code == 0){ if ($j->{start_time} - 1 <= OAR::IO::get_date($base)){ $Exit_code = 2; } } } } # for toLaunch jobs my $Detach_oarexec = get_conf("DETACH_JOB_FROM_SERVER"); if (!defined($Detach_oarexec)){ $Detach_oarexec = 0; } # To communicate with bipbip if we detach or not oarexec if ($Detach_oarexec == 0){ $ENV{OAR_DETACH_OAREXEC} = 0; }else{ $ENV{OAR_DETACH_OAREXEC} = 1; } my @commands_to_launch_with_sliding_window; my $runner_launcher_window_size = get_conf_with_default_param( "RUNNER_SLIDING_WINDOW_SIZE", OAR::Tools::get_default_runner_sliding_window_size() ); foreach my $job (OAR::IO::get_jobs_in_state($base,"toLaunch")){ my $jobid = $job->{job_id}; my $jobtype = $job->{job_type}; my $jobinfo = $job->{info_type}; my $is_desktop_computing = OAR::IO::is_job_desktop_computing($base,$jobid); oar_debug("[Runner] is_desktop_computing = $is_desktop_computing\n"); if ($is_desktop_computing) { oar_debug("[Runner] Desktop computing job, I don't handle it !\n"); next; } OAR::IO::set_job_state($base,$jobid,"Launching"); if (($Detach_oarexec == 0) or ($runner_launcher_window_size < 1)){ # Every bipbip are forked now oar_debug("[Runner] Launching job : $batch $jobid\n"); OAR::Tools::fork_no_wait("$batch $jobid",$base); }else{ # Each bipbip will be launched with a sliding window to avoid to crash # the server oar_debug("[Runner] Launching job with the sliding window: $batch $jobid\n"); push(@commands_to_launch_with_sliding_window, "$batch $jobid"); } } OAR::IO::disconnect($base); # If bipbip is detached then we can use sliding window to avoid to kill the # server if there are a lot of job to launch at the same time if ($#commands_to_launch_with_sliding_window >= 0){ # Default values if not specified in oar.conf my $runner_launcher_command_timeout = 900; # Timeouts are processed into bipbip my $pid; $SIG{PIPE} = 'IGNORE'; # We have to put this in background otherwise a pingchecker or a ssh or # something else in bipbip could freeze the Almighty my $pid = fork(); if(defined($pid)){ if($pid == 0){ #child undef($base); $SIG{USR1} = 'IGNORE'; $SIG{INT} = 'IGNORE'; $SIG{TERM} = 'IGNORE'; oar_debug("[Runner] Beginning of the sliding window: $$, with a window of $runner_launcher_window_size\n"); ( my $t, my $y ) = OAR::WindowForker::launch( \@commands_to_launch_with_sliding_window, $runner_launcher_window_size, undef, $runner_launcher_command_timeout, 0, undef ); oar_debug("[Runner] End of the sliding window: $$\n"); #oar_debug("[Runner] Finished processes".Dumper($t)."\n"); #oar_debug("[Runner] Processes times".Dumper($y)."\n"); exit(0); }else{ oar_debug("[Runner] Sliding window fork with pid: $pid\n"); } }else{ oar_error("[Runner] Not able to create the child fork for the sliding window\n"); } } exit($Exit_code); ./oar-2.5.2/sources/core/modules/runner/oarexec0000755000175000017500000005273711757171206017547 0ustar plbplb# $Id$ #script executed on the first reserved node # Exit codes are: # - 0 : everything worked normally # - 1 : prologue error (exit value not equal to 0 or timeout exceeded) # - 2 : epilogue error (exit value not equal to 0 or timeout exceeded) # - 3 : this script was killed by Leon normally # - 4 : this script was killed by Leon normally and epilogue was in error (like for 2) # - 5 : this script was not able to create the node file # - 6 : this script was not able to create the pid file # - 7 : cannot get the shell value for the user # - 8 : this script was not able to create tmp directory # - 12 : cannot go into the launching directory of the job and epilogue was in error # - 10 : cannot go into the launching directory of the job # - 20 : cannot create STDOUT and STDERR files # - 22 : cannot create STDOUT and STDERR files and epilogue was in error # - 30 : timeout of the bipbip hashtable send # - 33 : oarexec was stopped normally via USR1 signal but there was an epilogue error # - 34 : all worked normally and this is the result of an USR1 signal # - 40 : all worked normally and the user process received the checkpoint signal # - 41 : oarexec has sent the checkpoint signal but there was an epilogue error # - 42 : oarexec has sent the user defined signal (oardel -s) # # The exit code is sent to the Almighty via its TCP socket # # oarexec read a hashtable from bipbip, execute the prologue and then close STD file descriptor + fork a child and kill itself. # So the SSH return just after job initialisation and prologue BEGIN{ if (!defined(&OAR::Tools::get_all_process_children())){ require(OAR::Tools); } } use strict; use Sys::Hostname; use POSIX qw(:signal_h :errno_h :sys_wait_h); my $Old_umask = sprintf("%lo",umask()); umask(oct("022")); $SIG{PIPE} = 'IGNORE'; $SIG{HUP} = 'IGNORE'; # To deblock sshd father when oardel in suspended state $SIG{CONT} = sub{ kill('CONT',getppid()); }; my $Job_id = shift; $| = 1; # block signals until we define the handlers my $sigset = POSIX::SigSet->new; my $Blockset = POSIX::SigSet->new(SIGINT, SIGTERM, SIGQUIT, SIGCHLD, SIGUSR2, SIGUSR1); sigprocmask(SIG_BLOCK, $Blockset, $sigset); my $Bin_Path = $ENV{OARDIR}."/"; my $Tmp_file_prefix; my $Debug_mode; my $Node_file; my $Res_file; my $Pid_file; my $Kill_myself = 0; my $Oarexec_exit_code = 0; my $Exit_script_code = 'N'; #Notify Almighty. #arg1 --> exit value sub quit_oarexec($$){ my $exit_value = shift; my $job_data = shift; if ($job_data->{detach_oarexec} == 1){ # We must contact Almighty my $max_wait_time = 900; my $wait_time = 30; my $max_retry = 48; my $nb_retry = 0; while (1){ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time); my $string = sprintf("%02d:%02d:%02d", $hour,$min,$sec); if (!defined(OAR::Tools::notify_tcp_socket($job_data->{almighty_hostname},$job_data->{almighty_port},"OAREXEC_$job_data->{job_id}"."_$exit_value"."_$Exit_script_code"."_$job_data->{challenge}"))){ if ((defined($job_data->{cpuset_full_path})) and ($nb_retry < $max_retry)){ print("[oarexec $Job_id] [$string] I notified Almighty with my exit value $exit_value and I will retry in $max_wait_time seconds if I am not killed by the cpuset_manager before\n"); $wait_time = $max_wait_time; }else{ print("[oarexec $Job_id] [$string] I notified Almighty with my exit value $exit_value; I am exiting\n"); exit($exit_value); } $nb_retry++; }else{ print("[oarexec $Job_id] I cannot notify Almighty; maybe the server is down or network is wrong configured\n"); } sleep($wait_time); if ($wait_time < $max_wait_time){ $wait_time = 2 * $wait_time; if ($wait_time > $max_wait_time){ $wait_time = $max_wait_time; } } } } exit($exit_value); } #delete temporary file sub clean_all(){ #system("rm -f $OAR_FILE_NODES $oarPidFile"); unlink($Node_file,$Res_file,$Pid_file,OAR::Tools::get_oar_user_signal_file_name($Job_id)); # Kill oarsub connections my $file = OAR::Tools::get_oarsub_connections_file_name($Job_id); if (open(OARSUBS,$file)){ while ($_ = ){ if ($_ =~ m/^(\d+)$/m){ my $ppid = $1; my ($children,$cmd_name) = OAR::Tools::get_one_process_children($ppid); my @pid_list = @{$children}; shift(@pid_list); shift(@pid_list); if ($cmd_name =~ m/$file/m){ # remove ppid print("[oarexec $Job_id] kill oarsub connections @pid_list\n"); system({"oardodo"} "oardodo","kill","-9",@pid_list) if ($#pid_list >= 0); }else{ print("[oarexec $Job_id] want to kill oarsub connections @pid_list BUT it seems that there is no OAR oarsub processes\n"); } } } close(OARSUBS); } unlink($file); # Delete unwanted children my ($children,$cmd_name) = OAR::Tools::get_one_process_children($$); shift(@{$children}); kill(9,@{$children}); } # read a line on a socket # arg1 --> socket # arg2 --> timeout # return 0 if the read times out sub read_socket_line($$){ my $sock = shift; my $timeout = shift; my $char = "a"; my $res = 1; my $rin = ''; my $line; vec($rin,fileno($sock),1) = 1; my $rin_tmp; while (($res > 0) && ($char ne "\n") && ($char ne "")){ $res = select($rin_tmp = $rin, undef, undef, $timeout); if ($res > 0){ sysread($sock,$char,1); if ($char ne "\n"){ $line .= $char; } } } return($res,$line); } #kill all child of the pid # arg1 --> child pid sub kill_children($){ my $child_pid = shift; my ($children, $cmd_name) = OAR::Tools::get_one_process_children($child_pid); system({"oardodo"} "oardodo","kill","-9",@{$children}); } sub send_kill_signal_to_myself(){ my $father = $$; my $pid=fork; if($pid==0){ sleep(5); kill('SIGUSR2', $father); exit(); } } # Retrieve job information from bipbip my $Job; eval { $SIG{ALRM} = sub { die "alarm\n" }; alarm(OAR::Tools::get_bipbip_ssh_hashtable_send_timeout()); my $tmp = ""; while (){ $tmp .= $_; } $Job = eval($tmp); alarm(0); }; if( $@ ){ print("[oarexec $Job_id] Timeout of hashtable SSH transfer\n"); exit(30); } if (!defined($Job->{job_id})){ print("[oarexec $Job_id] Bad SSH hashtable transfered\n"); exit(31); } my $host = hostname(); print("[oarexec $Job_id] job id : $Job_id user : $Job->{job_user}; launchingDirectory : $Job->{launching_directory}\n"); OAR::Tools::set_default_oarexec_directory($Job->{tmp_directory}); $Tmp_file_prefix = OAR::Tools::get_default_oarexec_directory(); # Test if OAR directory exists and is owned by oar or if we can create it if (!(((-d $Tmp_file_prefix) and (-O $Tmp_file_prefix)) or (mkdir($Tmp_file_prefix)))){ print("[oarexec $Job_id] Directory $Tmp_file_prefix does not exist and I cannot create it\n"); exit(8); } chdir($Tmp_file_prefix); #create node set file $Node_file = "$Tmp_file_prefix/$Job_id"; if (! open(FILE,">$Node_file")){ print("[oarexec $Job_id] I cannot write file $Node_file\n"); exit(5); } #Feed the node file my @tmp_res; my %tmp_already_there; foreach my $r (@{$Job->{resources}}){ if (($r->{$Job->{node_file_db_fields}} ne "") and ($r->{type} eq "default")){ if (($r->{$Job->{node_file_db_fields_distinct_values}} ne "") and (!defined($tmp_already_there{$r->{$Job->{node_file_db_fields_distinct_values}}}))){ push(@tmp_res, $r->{$Job->{node_file_db_fields}}); $tmp_already_there{$r->{$Job->{node_file_db_fields_distinct_values}}} = 1; } } } foreach my $f (sort(@tmp_res)){ print(FILE "$f\n") or exit(5); } close(FILE); #create resource set file $Res_file = "$Tmp_file_prefix/$Job_id"."_resources"; if (! open(FILE,">$Res_file")){ print("[oarexec $Job_id] I cannot write file $Res_file\n"); exit(5); } #Feed the resource file foreach my $r (@{$Job->{resources}}){ my $line = ""; foreach my $p (keys(%{$r})){ if(OAR::Tools::check_resource_system_property($p) != 1){ $r->{$p} = "" if (!defined($r->{$p})); $line .= " $p = '$r->{$p}' ," } } chop($line); print(FILE "$line\n") or exit(5); } close(FILE); print("[oarexec $Job_id] command name [$Job->{command}]\n"); if (defined($Job->{prologue})){ # Launch prologue script my $script_error = -1; eval { my $pid; $SIG{ALRM} = sub { kill(9,$pid); die "alarm\n" }; print("[oarexec $Job_id] LAUNCH prologue : $Job->{prologue} $Job_id $Job->{job_user} $Node_file $Job->{walltime_seconds}\n"); alarm($Job->{pro_epi_timeout}); #$script_error = system("$Job->{prologue} $Job_id $Job->{user} $Node_file"); $pid = fork(); if($pid == 0){ #CHILD $SIG{ALRM} = 'DEFAULT'; exec("$Job->{prologue} $Job_id $Job->{job_user} $Node_file"); warn("[ERROR] Cannot execute $Job->{prologue}\n"); exit(-1); } my $wait_res = -1; # Avoid to be disrupted by a signal while ((defined($pid)) and ($wait_res != $pid)){ $wait_res = waitpid($pid,0); $script_error = $?; } alarm(0); print("[oarexec $Job_id] END prologue : $Job->{prologue}\n"); }; if( $@ || ($script_error != 0)){ print("[oarexec $Job_id] Prologue error : $@; return code = $script_error\n"); clean_all(); print("[oarexec $Job_id] Error in the OAR prologue execution\n"); exit(1); } } # Get user shell my @pass_info = getpwnam($Job->{job_user}); my $shell = $pass_info[8]; if (!defined($shell)){ clean_all(); print("[oarexec $Job_id] Error : user $Job->{job_user} does not exist on this node, $host\n"); exit(7); } my @cmd; my $stdin_script_to_send; if ( $Job->{mode} eq "PASSIVE" ){ print("[oarexec $Job_id] Begin a passive command\n"); my $str; ($str, $stdin_script_to_send) = OAR::Tools::get_oarexecuser_perl_script_for_oarexec($Node_file,$Job_id,$Job->{array_id},$Job->{array_index},$Job->{user},$shell,$Job->{launching_directory},$Job->{stdout_file},$Job->{stderr_file},$Res_file,$Job->{name},$Job->{project},$Job->{walltime},$Job->{walltime_seconds},$Job->{job_env},$Job->{command}); #print($str."\n"); #@cmd = ("oardo","su","-","$Job->{job_user}","-c","perl -e '$str'"); @cmd = ("oardodo","perl","-e",$str); #$ENV{OARDO_BECOME_USER}=$Job->{job_user}; #@cmd = ("oardo","perl","-e",$str); } #resolve terminal type problems my $terminal = $ENV{TERM}; if (($terminal ne "") and ($terminal ne "unknown")){ $ENV{TERM} = $terminal; }else{ $ENV{TERM} = "xterm"; } #oar own the tty #so we must change owner for the user system({"bash"} "bash","-c","TTY=\$(tty) && test -e \$TTY && oardodo chown $Job->{job_user}:oar \$TTY && oardodo chmod 660 \$TTY"); # pipe for notify the end of a child process pipe(pipe_child_read,pipe_child_write); autoflush pipe_child_write 1; autoflush pipe_child_read 1; sub child_signal_handler { $SIG{CHLD} = \&child_signal_handler; my $wait_pid_ret ; while (($wait_pid_ret = waitpid(-1,WNOHANG)) > 0){ my $exit_value = $? >> 8; print(pipe_child_write "$wait_pid_ret $exit_value\n"); } } $SIG{CHLD} = \&child_signal_handler; #For kill signal pipe(pipe_kill_read,pipe_kill_write); autoflush pipe_kill_write 1; autoflush pipe_kill_read 1; sub user_defined_signal_handler { $SIG{URG} = \&user_defined_signal_handler; print("[oarexec $Job_id] In user defined signal handler of @_\n"); my $signal; open(FILE, OAR::Tools::get_oar_user_signal_file_name($Job_id)); while () { $signal = $_; } close(FILE); print(pipe_kill_write "SIGNAL_$signal\n"); } $SIG{URG} = \&user_defined_signal_handler; sub kill_signal_handler { $SIG{TERM} = \&kill_signal_handler; $SIG{INT} = \&kill_signal_handler; $SIG{QUIT} = \&kill_signal_handler; print("[oarexec $Job_id] In kill signal handler of @_\n"); print(pipe_kill_write "KILL\n"); } $SIG{TERM} = \&kill_signal_handler; $SIG{INT} = \&kill_signal_handler; $SIG{QUIT} = \&kill_signal_handler; sub stop_signal_handler { $SIG{USR1} = \&stop_signal_handler; print("[oarexec $Job_id] In stop signal handler of @_\n"); print(pipe_kill_write "STOP\n"); } $SIG{USR1} = \&stop_signal_handler; sub chekpoint_signal_handler { $SIG{USR2} = \&chekpoint_signal_handler; print("[oarexec $Job_id] In checkpoint signal handler of @_\n"); print(pipe_kill_write "CHECKPOINT\n"); } $SIG{USR2} = \&chekpoint_signal_handler; if ($Job->{detach_oarexec} == 1){ #Detach process from bipbip SSH print("[oarexec $Job_id] I am detaching the oarexec process\n"); if(fork() != 0){ print(OAR::Tools::get_bipbip_oarexec_rendez_vous()); # Exit from main oarexec exit(0); } # We must redirect STD to close SSH open(STDIN, "/dev/zero"); if ($Job->{debug_mode} > 0){ open(STDOUT, ">>$Tmp_file_prefix/oar.log"); open(STDERR, ">>$Tmp_file_prefix/oar.log"); }else{ open(STDOUT, ">/dev/null"); open(STDERR, ">/dev/null"); } }else{ print(OAR::Tools::get_bipbip_oarexec_rendez_vous()); } #Write file with this oarexec pid $Pid_file = OAR::Tools::get_oar_pid_file_name($Job_id); if ((!open(FILEPID,">$Pid_file")) or (!print(FILEPID "$$"))){ print("[oarexec $Job_id] I cannot write file $Pid_file\n"); clean_all(); quit_oarexec(6,$Job); } close(FILEPID); sigprocmask(SIG_UNBLOCK, $Blockset); my $pid = $$; my $rin_script = ''; if ($Job->{mode} eq "PASSIVE"){ # For parsing script outputs pipe(pipe_script_read,pipe_script_write); autoflush pipe_script_write 1; autoflush pipe_script_read 1; vec($rin_script,fileno(pipe_script_read),1) = 1; # To send command line pipe(pipe_stdin_read,pipe_stdin_write); autoflush pipe_stdin_write 1; autoflush pipe_stdin_read 1; #print("[oarexec $Job_id] Launch the command : $cmd\n"); $pid = fork; if($pid == 0){ #CHILD $SIG{CHLD} = 'DEFAULT'; $SIG{TERM} = 'DEFAULT'; $SIG{INT} = 'DEFAULT'; $SIG{QUIT} = 'DEFAULT'; $SIG{USR1} = 'DEFAULT'; $SIG{USR2} = 'DEFAULT'; $SIG{PIPE} = 'DEFAULT'; $SIG{HUP} = 'DEFAULT'; print("[oarexec $Job_id] child exec: @cmd\n"); close(pipe_script_read); close(STDOUT); # Redirect script output into the pipe open(STDOUT, ">& pipe_script_write"); # Use the child STDIN to send the user command close(pipe_stdin_write); close(STDIN); open(STDIN, "<& pipe_stdin_read"); $ENV{OARDO_BECOME_USER} = $Job->{job_user}; umask(oct($Old_umask)); exec(@cmd); warn("[ERROR] Cannot find @cmd\n"); exit(-1); } close(pipe_script_write); close(pipe_stdin_read); # Send user command without any interpretation security isssue!!! #print("[oarexec $Job_id] ########### $stdin_script_to_send\n"); # This is the most secure manner that I find print(pipe_stdin_write $stdin_script_to_send."\n"); open(pipe_stdin_write,"<& /dev/null"); } print("[oarexec $Job_id] child pid = $pid\n"); my $kill_myself = 0; my $res_read; my $line_read; my $rin = ''; my $rin_sig = ''; my $rin_pipe = ''; vec($rin_sig,fileno(pipe_kill_read),1) = 1; vec($rin_pipe,fileno(pipe_child_read),1) = 1; $rin = $rin_sig | $rin_pipe | $rin_script; my $rin_tmp; my $Stop_signal = 0; my $Checkpoint_signal = 0; my $user_signal = 0; my $stop_loop = 0; # wait end of the child process or KILL notification while (($line_read != $pid) and ($Stop_signal == 0) and ($stop_loop == 0)){ print("[oarexec $Job_id] wait end of child process or kill notification\n"); select($rin_tmp = $rin, undef, undef, undef); if (vec($rin_tmp, fileno(pipe_script_read), 1)){ ($res_read,$line_read) = read_socket_line(\*pipe_script_read,1); if (($res_read == 1) and ($line_read eq "")){ # Script is finished close(pipe_script_read); close(pipe_stdin_write); }else{ print("[oarexec $Job_id] pipe script writes : $res_read,$line_read\n"); if ($line_read =~ /^EXIT_CODE\s(\d+)$/){ $Exit_script_code = $1; print("OAREXEC_SCRIPT_EXIT_VALUE $Exit_script_code\n"); } } }elsif (vec($rin_tmp, fileno(pipe_child_read), 1)){ ($res_read,$line_read) = read_socket_line(\*pipe_child_read,1); print("[oarexec $Job_id] PIPE reads : $res_read,$line_read\n"); if ($line_read =~ m/(\d+) (\d+)/m){ if ($1 == $pid){ print("[oarexec $Job_id] Reset CHLD signal handler\n"); $line_read = $pid; $Oarexec_exit_code = $2 * 10; } } }elsif (vec($rin_tmp, fileno(pipe_kill_read), 1)){ ($res_read,$line_read) = read_socket_line(\*pipe_kill_read,1); print("[oarexec $Job_id] pipe kill signal : $res_read,$line_read\n"); if ($line_read eq "KILL"){ print("[oarexec $Job_id] Kill children\n"); if ($Job->{mode} eq "INTERACTIVE"){ $stop_loop = 1; }else{ kill_children($pid); } $kill_myself = 1; }elsif ($line_read eq "STOP"){ print("[oarexec $Job_id] Receive STOP notification\n"); if ($Job->{mode} eq "INTERACTIVE"){ $Stop_signal = 1; }else{ print("[oarexec $Job_id] Receive USR1 signal so someone wants to finish this job but it is not an INTERACTIVE one\n"); } }elsif ($line_read eq "CHECKPOINT"){ #We must send SIGUSR2 to the child of $pid my ($tmp_hash, $tmp_cmd_hash) = OAR::Tools::get_all_process_children(); my $pid_to_send_kill = @{$tmp_hash->{$pid}}[0]; if (defined($pid_to_send_kill)){ print("[oarexec $Job_id] Checkpoint received, send signal $Job->{checkpoint_signal} to the pid $pid_to_send_kill\n"); system({"oardodo"} "oardodo","kill","-s",$Job->{checkpoint_signal},$pid_to_send_kill); }else{ print("[oarexec $Job_id] Cannot find pid of user process??? I will retry in 5 seconds\n"); send_kill_signal_to_myself(); } $Checkpoint_signal = 1; }elsif ($line_read =~ m/^SIGNAL_(.*)/){ my $signal = $1; #We must send $signal to the child of $pid my ($tmp_hash, $tmp_cmd_hash) = OAR::Tools::get_all_process_children(); my $pid_to_send_kill = @{$tmp_hash->{$pid}}[0]; if (defined($pid_to_send_kill)){ print("[oarexec $Job_id] Signal received, send signal $signal to the pid $pid_to_send_kill\n"); system({"oardodo"} "oardodo","kill","-s",$signal,$pid_to_send_kill); }else{ print("[oarexec $Job_id] Cannot find pid of user process??? I will retry in 5 seconds\n"); send_kill_signal_to_myself(); } $user_signal = 1; } } } $SIG{CHLD} = 'DEFAULT'; $SIG{TERM} = 'IGNORE'; $SIG{INT} = 'IGNORE'; $SIG{QUIT} = 'IGNORE'; $SIG{USR1} = 'DEFAULT'; $SIG{USR2} = 'DEFAULT'; close(pipe_script_read) if ($rin_script ne ''); close(pipe_child_write); close(pipe_child_read); close(pipe_kill_write); close(pipe_kill_read); print("[oarexec $Job_id] Job Terminated\n"); if (defined($Job->{epilogue})){ # Launch epilogue script my $script_error = -1; eval { my $pid; $SIG{ALRM} = sub { kill(9, $pid); die "alarm\n" }; print("[oarexec $Job_id] LAUNCH epilogue : $Job->{epilogue} $Job_id $Job->{job_user} $Node_file $Job->{walltime_seconds}\n"); alarm($Job->{pro_epi_timeout}); #$script_error = system("$Job->{epilogue} $Job_id $Job->{user} $Node_file $Job->{command}"); $pid = fork(); if($pid == 0){ #CHILD $SIG{ALRM} = 'DEFAULT'; exec("$Job->{epilogue} $Job_id $Job->{job_user} $Node_file"); warn("[ERROR] Cannot execute $Job->{epilogue}\n"); exit(-1); } my $wait_res = -1; # Avaoid to be disrupted by a signal while ((defined($pid)) and ($wait_res != $pid)){ $wait_res = waitpid($pid,0); $script_error = $?; } alarm(0); print("[oarexec $Job_id] END epilogue : $Job->{epilogue}\n"); }; if( $@ || ($script_error != 0)){ print("[oarexec $Job_id] Epilogue error : $@; return code = $script_error\n"); clean_all(); print("[oarexec $Job_id] Error in the OAR epilogue execution\n"); if ($Kill_myself == 1){ quit_oarexec(4,$Job); }elsif($Stop_signal == 1){ quit_oarexec(33,$Job); }elsif($Checkpoint_signal == 1){ quit_oarexec(41,$Job); }else{ quit_oarexec(2+$Oarexec_exit_code,$Job); } } } clean_all(); if ($kill_myself == 1){ quit_oarexec(3,$Job); }elsif($Stop_signal == 1){ quit_oarexec(34,$Job); }elsif($Checkpoint_signal == 1){ quit_oarexec(40,$Job); }elsif($user_signal == 1){ quit_oarexec(42,$Job); }else{ quit_oarexec(0+$Oarexec_exit_code,$Job); } ./oar-2.5.2/sources/core/modules/runner/fping_note.txt0000644000175000017500000000034211757171206021045 0ustar plbplbFrom fping sources : http://www.fping.com/ in fping.c : set MIN_INTERVAL to 0 in option.h : set DEFAULT_SELECT_TIME to 0 or 0.01 (to avoid using too much cpu) set DEFAULT_INTERVAL to 0 the command: fping -q ./oar-2.5.2/sources/core/modules/runner/bipbip.in0000755000175000017500000005133511757171206017764 0ustar plbplb#!/usr/bin/perl # $Id$ # use OAR::IO; use Sys::Hostname; use OAR::Conf qw(init_conf dump_conf get_conf is_conf); use OAR::Modules::Judas qw(oar_debug oar_warn oar_error send_log_by_email set_current_log_category); use OAR::PingChecker; use OAR::Tools; use Data::Dumper; use IPC::Open2; my $OAR_Tools_module_filepath="%%PERLLIBDIR%%/OAR/Tools.pm"; # suitable Data::Dumper configuration for serialization $Data::Dumper::Purity = 1; $Data::Dumper::Terse = 1; $Data::Dumper::Indent = 0; $Data::Dumper::Deepcopy = 1; # Log category set_current_log_category('main'); my $Job_id= shift; my $Oarexec_reattach_exit_value = shift; my $Oarexec_reattach_script_exit_value = shift; my $Oarexec_challenge = shift; #get server conf init_conf($ENV{OARCONFFILE}); my $Remote_host = get_conf("SERVER_HOSTNAME"); my $Remote_port = get_conf("SERVER_PORT"); my $Node_file_db_field = get_conf("NODE_FILE_DB_FIELD"); $Node_file_db_field = OAR::Tools::get_default_node_file_db_field() if (!defined($Node_file_db_field)); my $Node_file_db_field_distinct_values = get_conf("NODE_FILE_DB_FIELD_DISTINCT_VALUES"); $Node_file_db_field_distinct_values = OAR::Tools::get_default_node_file_db_field_distinct_values() if (!defined($Node_file_db_field_distinct_values)); my $Openssh_cmd = get_conf("OPENSSH_CMD"); $Openssh_cmd = OAR::Tools::get_default_openssh_cmd() if (!defined($Openssh_cmd)); my $Almighty_hostname = $Remote_host; if (($Almighty_hostname =~ /^\s*localhost.*$/) or ($Almighty_hostname =~ /^\s*127.*$/)){ $Almighty_hostname = hostname(); } my $Deploy_hostname = get_conf("DEPLOY_HOSTNAME"); if (!defined($Deploy_hostname)){ $Deploy_hostname = $Remote_host; } my $Cosystem_hostname = get_conf("COSYSTEM_HOSTNAME"); if (!defined($Cosystem_hostname)){ $Cosystem_hostname = $Remote_host; } my $Debug_mode = get_conf("OAREXEC_DEBUG_MODE"); if (!defined($Debug_mode)){ $Debug_mode = 0; } my $Detach_oarexec = $ENV{OAR_DETACH_OAREXEC}; # Must be defined by runner # ATTENTION: not initialized if executed by Almighty or another module because not needed #oar_debug("[bipbip $Job_id] OAR_DETACH_OAREXEC = $Detach_oarexec\n"); if (is_conf("OAR_RUNTIME_DIRECTORY")){ OAR::Tools::set_default_oarexec_directory(get_conf("OAR_RUNTIME_DIRECTORY")); } my $Server_prologue = get_conf("SERVER_PROLOGUE_EXEC_FILE"); my $Server_epilogue = get_conf("SERVER_EPILOGUE_EXEC_FILE"); my $Cpuset_field = get_conf("JOB_RESOURCE_MANAGER_PROPERTY_DB_FIELD"); my $Cpuset_file = get_conf("JOB_RESOURCE_MANAGER_FILE"); $Cpuset_file = OAR::Tools::get_default_cpuset_file() if (!defined($Cpuset_file)); $Cpuset_file = "$ENV{OARDIR}/$Cpuset_file" if ($Cpuset_file !~ /^\//); if (is_conf("OAR_SSH_CONNECTION_TIMEOUT")){ OAR::Tools::set_ssh_timeout(get_conf("OAR_SSH_CONNECTION_TIMEOUT")); } my $Job_uid_resource_type = get_conf("JOB_RESOURCE_MANAGER_JOB_UID_TYPE"); my $Job_user; my $base; $base = OAR::IO::connect(); my $Cpuset_path = get_conf("CPUSET_PATH"); my $Cpuset_name = OAR::IO::get_job_cpuset_name($base, $Job_id) if (defined($Cpuset_field)); my $Cpuset_full_path; if (defined($Cpuset_path) and defined($Cpuset_field)){ $Cpuset_full_path = $Cpuset_path.'/'.$Cpuset_name; } my $job = OAR::IO::get_job($base,$Job_id); my ($job_challenge,$ssh_private_key,$ssh_public_key) = OAR::IO::get_job_challenge($base,$Job_id); my @hosts = OAR::IO::get_job_current_hostnames($base,$Job_id); if (!(($job->{state} eq "Launching") or ($job->{state} eq "Running") or ($job->{state} eq "Suspended") or ($job->{state} eq "Resuming"))){ oar_error("[bipbip $Job_id] A Job is requested but it is already treated. This job must be already killed (exterminated) and come back from hell ???\n"); OAR::IO::add_new_event($base, "BIPBIP_BAD_JOBID", $Job_id, "A Job is requested but it is already treated. This job must be already killed (exterminated) and come back from hell ???"); OAR::IO::disconnect($base); exit 1; } #deploy, cosystem and no host part #check if we must treate the end of a oarexec if (defined($Oarexec_reattach_exit_value)){ if ($Oarexec_reattach_exit_value =~ m/\d+/m){ if ($Oarexec_challenge eq $job_challenge){ OAR::IO::check_end_of_job($base, $Job_id, $Oarexec_reattach_script_exit_value, $Oarexec_reattach_exit_value, \@hosts, $Remote_host, $Remote_port,$job->{job_user},$job->{launching_directory},$Server_epilogue); OAR::IO::disconnect($base); exit(0); }else{ oar_error("[bipbip $Job_id] Bad challenge from oarexec, perhaps a pirate attack??? ($Oarexec_challenge).\n"); OAR::IO::add_new_event($base, "BIPBIP_CHALLENGE", $Job_id, "Bad challenge from oarexec, perhaps a pirate attack??? ($Oarexec_challenge)"); OAR::IO::disconnect($base); exit(2); } }else{ oar_error("[bipbip $Job_id] Bad argument for bipbip : $Oarexec_reattach_exit_value\n"); OAR::IO::disconnect($base); exit(2); } } # ELSE we must launch oarexec on the first node oar_debug("[bipbip $Job_id] JOB: $Job_id; User: $job->{job_user}; Command: $job->{command} ==> hosts :[@hosts]\n"); if (($job->{job_type} eq "INTERACTIVE") and ($job->{reservation} eq "None")){ my ($addr,$port) = split(/:/,$job->{info_type}); OAR::Tools::notify_tcp_socket($addr,$port,"Starting..."); } my $job_cpuset_uid; $Job_user = OAR::Tools::format_job_user($job->{job_user},$Job_id,$job_cpuset_uid); my $types = OAR::IO::get_current_job_types($base,$Job_id); if ((!defined($types->{deploy})) and (!(defined($types->{cosystem})) and ($#hosts >= 0))){ my $event_type; my @bad; ############### # CPUSET PART # ############### my $cpuset_nodes; $cpuset_nodes = OAR::IO::get_cpuset_values_for_a_moldable_job($base,$Cpuset_field,$job->{assigned_moldable_job}) if (defined($Cpuset_field)); $job_cpuset_uid = OAR::IO::get_job_cpuset_uid($base, $job->{assigned_moldable_job}, $Job_uid_resource_type, $Cpuset_field) if ((defined($Job_uid_resource_type)) and (defined($Cpuset_field))); $Job_user = OAR::Tools::format_job_user($job->{job_user},$Job_id,$job_cpuset_uid); $ssh_public_key = OAR::Tools::format_ssh_pub_key($ssh_public_key,$Cpuset_full_path,$job->{job_user},$Job_user); my $cpuset_data_hash = { job_id => $Job_id, name => $Cpuset_name, nodes => $cpuset_nodes, cpuset_path => $Cpuset_path, ssh_keys => { public => { file_name => OAR::Tools::get_default_oar_ssh_authorized_keys_file(), key => $ssh_public_key }, private => { file_name => OAR::Tools::get_private_ssh_key_file_name($Cpuset_name), key => $ssh_private_key } }, oar_tmp_directory => OAR::Tools::get_default_oarexec_directory(), user => $job->{job_user}, job_user => $Job_user, job_uid => $job_cpuset_uid, types => $types, log_level => OAR::Modules::Judas::get_log_level() }; if (defined($cpuset_nodes)){ my $taktuk_cmd = get_conf("TAKTUK_CMD"); my ($tag,@bad_tmp) = OAR::Tools::manage_remote_commands([keys(%{$cpuset_nodes})],$cpuset_data_hash,$Cpuset_file,"init",$Openssh_cmd,$taktuk_cmd,$base); if ($tag == 0){ my $str = "[bipbip $Job_id] [CPUSET] Bad cpuset file : $Cpuset_file\n"; OAR::Modules::Judas::oar_error($str); OAR::IO::add_new_event($base, "CPUSET_MANAGER_FILE", $Job_id, $str); }else{ push (@bad, @bad_tmp); $event_type = "CPUSET_ERROR"; # Clean already configured cpuset my @tmp_array = keys(%{$cpuset_nodes}); if (($#bad >= 0) and ($#tmp_array > $#bad)){ # Verify if the job is a reservation if ($job->{reservation} ne "None"){ # Look at if there is at least one alive node for the reservation my @tmp_hosts; # Keep only alive nodes foreach my $n (@hosts){ my $i = 0; while (($i <= $#bad) and ($n ne $bad[$i])){ $i++; } push(@tmp_hosts, $n) if ($i > $#bad); } OAR::IO::lock_table($base,["jobs","job_state_logs","resources","assigned_resources","frag_jobs","challenges","moldable_job_descriptions","job_types","job_dependencies","job_resource_groups","job_resource_descriptions","event_logs","event_log_hostnames"]); OAR::IO::set_job_message($base,$Job_id,"One or several nodes are not cpuset responding correctly"); OAR::IO::add_new_event_with_host($base,"$event_type",$Job_id,"[bipbip] OAR cpuset suspects nodes for the job $Job_id : @bad",\@bad); OAR::IO::archive_some_moldable_job_nodes($base,$job->{assigned_moldable_job},\@bad); OAR::IO::unlock_table($base); OAR::Tools::notify_tcp_socket($Remote_host,$Remote_port,"ChState"); @hosts = @tmp_hosts; @bad = (); }else{ # remove non initialized nodes foreach my $b (@bad){ delete($cpuset_nodes->{$b}); } $cpuset_data_hash->{nodes}=$cpuset_nodes; my ($tag,@bad_tmp) = OAR::Tools::manage_remote_commands([keys(%{$cpuset_nodes})],$cpuset_data_hash,$Cpuset_file,"clean",$Openssh_cmd,$taktuk_cmd,$base); push(@bad, @bad_tmp); } } } } ##################### # CPUSET PART, END # ##################### #if (($#bad < 0) and (!defined($cpuset_nodes))){ if ($#bad < 0){ # CHECK nodes oar_debug("[bipbip $Job_id] Check nodes : @hosts\n"); $event_type = "PING_CHECKER_NODE_SUSPECTED"; @bad = OAR::PingChecker::test_hosts(@hosts); } if ($#bad >= 0){ OAR::IO::lock_table($base,["jobs","job_state_logs","resources","assigned_resources","frag_jobs","challenges","moldable_job_descriptions","job_types","job_dependencies","job_resource_groups","job_resource_descriptions","event_logs","event_log_hostnames"]); OAR::IO::set_job_message($base,$Job_id,"One or several nodes are not responding correctly"); oar_error("[bipbip $Job_id] /!\\ Some nodes are inaccessible ($event_type):\n@bad\n"); my $exit_bipbip = 1; if (($job->{job_type} eq "INTERACTIVE") and ($job->{reservation} eq "None")){ my ($addr,$port) = split(/:/,$job->{info_type}); OAR::Tools::notify_tcp_socket($addr,$port,"ERROR: some resources did not respond"); }else{ # Verify if the job is a reservation if ($job->{reservation} ne "None"){ # Look at if there is at least one alive node for the reservation my @tmp_hosts; # Keep only alive nodes foreach my $n (@hosts){ my $i = 0; while (($i <= $#bad) and ($n ne $bad[$i])){ $i++; } push(@tmp_hosts, $n) if ($i > $#bad); } if ($#tmp_hosts < 0){ OAR::IO::add_new_event($base,"RESERVATION_NO_NODE",$job->{job_id},"There is no alive node for the reservation $job->{job_id}."); }else{ @hosts = @tmp_hosts; $exit_bipbip = 0; } } } #OAR::IO::set_job_state($base,$Job_id,"Error"); OAR::IO::add_new_event_with_host($base,"$event_type",$Job_id,"[bipbip] OAR suspects nodes for the job $Job_id : @bad",\@bad); OAR::IO::unlock_table($base); OAR::Tools::notify_tcp_socket($Remote_host,$Remote_port,"ChState"); if ($exit_bipbip == 1){ OAR::IO::disconnect($base); exit(2); } }else{ oar_debug("[bipbip $Job_id] No (enough) bad node\n"); } # end CHECK } ################################### # PROLOGUE EXECUTED ON OAR SERVER # ################################### # # Script is executing with job id in arguments if (defined($Server_prologue)){ my $cmd = "$Server_prologue $Job_id"; my $pid; my $exit_value; my $signal_num; my $dumped_core; my $timeout = OAR::Tools::get_default_server_prologue_epilogue_timeout(); if (is_conf("SERVER_PROLOGUE_EPILOGUE_TIMEOUT")){ $timeout = get_conf("SERVER_PROLOGUE_EPILOGUE_TIMEOUT"); } eval{ $SIG{PIPE} = 'IGNORE'; $SIG{ALRM} = sub { die "alarm\n" }; alarm($timeout); $pid = fork(); if ($pid == 0){ undef($base); exec($cmd); warn("[ERROR] Cannot find $cmd\n"); exit(-1); } my $wait_res = 0; # Avoid to be disrupted by a signal while ($wait_res != $pid){ $wait_res = waitpid($pid,0); } alarm(0); $exit_value = $? >> 8; $signal_num = $? & 127; $dumped_core = $? & 128; }; if ($@){ if ($@ eq "alarm\n"){ if (defined($pid)){ my ($children,$cmd_name) = OAR::Tools::get_one_process_children($pid); kill(9,@{$children}); } my $str = "[bipbip $Job_id] Server prologue timeouted (cmd : $cmd)"; oar_error("$str\n"); OAR::IO::add_new_event($base,"SERVER_PROLOGUE_TIMEOUT",$Job_id,"$str"); OAR::IO::disconnect($base); OAR::Tools::notify_tcp_socket($Remote_host,$Remote_port,"ChState"); if (($job->{job_type} eq "INTERACTIVE") and ($job->{reservation} eq "None")){ my ($addr,$port) = split(/:/,$job->{info_type}); OAR::Tools::notify_tcp_socket($addr,$port,"ERROR: SERVER PROLOGUE timeouted"); } exit(2); } }elsif ($exit_value != 0){ my $str = "[bipbip $Job_id] Server prologue exit code $exit_value (!=0) (cmd : $cmd)"; oar_error("$str\n"); OAR::IO::add_new_event($base,"SERVER_PROLOGUE_EXIT_CODE_ERROR",$Job_id,"$str"); OAR::IO::disconnect($base); OAR::Tools::notify_tcp_socket($Remote_host,$Remote_port,"ChState"); if (($job->{job_type} eq "INTERACTIVE") and ($job->{reservation} eq "None")){ my ($addr,$port) = split(/:/,$job->{info_type}); OAR::Tools::notify_tcp_socket($addr,$port,"ERROR: SERVER PROLOGUE returned a bad value"); } exit(2); } } ############################################################################### #CALL OAREXEC ON THE FIRST NODE my $moldable = OAR::IO::get_current_moldable_job($base,$job->{assigned_moldable_job}); my @resources = OAR::IO::get_current_assigned_job_resources($base,$job->{assigned_moldable_job}); my $pro_epi_timeout = OAR::Tools::get_default_prologue_epilogue_timeout(); if (is_conf("PROLOGUE_EPILOGUE_TIMEOUT")){ $pro_epi_timeout = get_conf("PROLOGUE_EPILOGUE_TIMEOUT"); } my $prologue_exec_file; if (is_conf("PROLOGUE_EXEC_FILE")){ $prologue_exec_file = get_conf("PROLOGUE_EXEC_FILE"); } my $epilogue_exec_file; if (is_conf("EPILOGUE_EXEC_FILE")){ $epilogue_exec_file = get_conf("EPILOGUE_EXEC_FILE"); } my @oarexec_files = ($OAR_Tools_module_filepath,"$ENV{OARDIR}/oarexec"); my $host_to_connect_via_ssh = $hosts[0]; #deploy, cosystem and no host part if ((defined($types->{cosystem})) or ($#hosts < 0)){ $host_to_connect_via_ssh = $Cosystem_hostname; }elsif (defined($types->{deploy})){ $host_to_connect_via_ssh = $Deploy_hostname; } oar_debug("[bipbip $Job_id] execute oarexec on node $host_to_connect_via_ssh\n"); my $oarexec_cpuset_path; if ((defined($Cpuset_full_path) and (!defined($types->{cosystem})) and (!defined($types->{deploy})) and ($#hosts >= 0))){ # So oarexec will retry several times to contact Almighty until it will be # killed by the cpuset manager $oarexec_cpuset_path = $Cpuset_full_path; } my %data_to_transfer = ( job_id => $Job_id, array_id => $job->{array_id}, array_index => $job->{array_index}, stdout_file => OAR::Tools::replace_jobid_tag_in_string($job->{stdout_file},$Job_id), stderr_file => OAR::Tools::replace_jobid_tag_in_string($job->{stderr_file},$Job_id), launching_directory => $job->{launching_directory}, job_env => $job->{job_env}, resources => \@resources, node_file_db_fields => $Node_file_db_field, node_file_db_fields_distinct_values => $Node_file_db_field_distinct_values, user => $job->{job_user}, job_user => $Job_user, types => $types, name => $job->{job_name}, project => $job->{project}, reservation => $job->{reservation}, walltime_seconds => $moldable->{moldable_walltime}, walltime => OAR::IO::duration_to_sql($moldable->{moldable_walltime}), command => $job->{command}, challenge => $job_challenge, almighty_hostname => $Almighty_hostname, almighty_port => $Remote_port, checkpoint_signal => $job->{checkpoint_signal}, debug_mode => $Debug_mode, mode => $job->{job_type}, pro_epi_timeout => $pro_epi_timeout, prologue => $prologue_exec_file, epilogue => $epilogue_exec_file, tmp_directory => OAR::Tools::get_default_oarexec_directory, detach_oarexec => $Detach_oarexec, cpuset_full_path => $oarexec_cpuset_path ); my $error = 50; my $exit_script_value = 'N'; my $init_done = 0; eval { my $pid; $SIG{ALRM} = sub { kill(9,$pid); die "alarm\n" }; alarm($pro_epi_timeout + OAR::Tools::get_bipbip_ssh_hashtable_send_timeout() + OAR::Tools::get_ssh_timeout()); my $ssh_option = ""; if ((defined($Cpuset_full_path) and (!defined($types->{cosystem})) and (!defined($types->{deploy})) and ($#hosts >= 0))){ # for oarsh_shell connection $ENV{OAR_CPUSET} = $Cpuset_full_path; $ssh_option = "-oSendEnv=OAR_CPUSET"; }else{ $ENV{OAR_CPUSET} = ""; } $pid = open2(\*READ, \*WRITE, "$Openssh_cmd $ssh_option -x -T $host_to_connect_via_ssh perl - $Job_id OAREXEC"); foreach my $f (@oarexec_files){ open(FILE, $f) or die("Cannot open $f\n"); while(){ print(WRITE); } close(FILE); } # End of oarexec script transfer print(WRITE "\n__END__\n"); # Send data structure for oarexec print(WRITE Dumper(\%data_to_transfer)."\n"); close(WRITE); while(){ oar_debug("[bipbip $Job_id] $_"); if (($init_done == 0) and ($_ eq OAR::Tools::get_bipbip_oarexec_rendez_vous())){ $init_done = 1; if ($Detach_oarexec == 0){ alarm(0); } OAR::IO::set_job_state($base,$Job_id,"Running"); # Notify interactive oarsub if (($job->{job_type} eq "INTERACTIVE") and ($job->{reservation} eq "None")){ oar_debug("[bipbip $Job_id] Interactive request ;Answer to the client Qsub -I\n"); my ($addr,$port) = split(/:/,$job->{info_type}); if (defined(OAR::Tools::notify_tcp_socket($addr,$port,"GOOD JOB"))){ oar_warn("[bipbip $Job_id] Frag job $Job_id because I cannot notify oarsub\n"); OAR::IO::lock_table($base,["frag_jobs","event_logs","jobs"]); OAR::IO::frag_job($base,$Job_id); OAR::IO::unlock_table($base); OAR::Tools::notify_tcp_socket($Remote_host,$Remote_port,"Qdel"); } } OAR::IO::disconnect($base) if ($Detach_oarexec == 0); } # Get user script exit code if ($_ =~ /^OAREXEC_SCRIPT_EXIT_VALUE\s(\d+|N)$/){ $exit_script_value = $1; } } close(READ); waitpid($pid, 0); $error = $? >> 8; alarm(0); }; if (($Detach_oarexec == 1) and ($error == 0)){ oar_debug("[bipbip $Job_id] Exit from bipbip normally\n"); exit(0); }else{ if ($init_done == 0){ if (($job->{job_type} eq "INTERACTIVE") and ($job->{reservation} eq "None")){ my ($addr,$port) = split(/:/,$job->{info_type}); OAR::Tools::notify_tcp_socket($addr,$port,"ERROR: an error occured on the first job node"); } } $base = OAR::IO::connect() if ($Detach_oarexec == 0); OAR::IO::check_end_of_job($base,$Job_id,$exit_script_value, $error,\@hosts,$Remote_host,$Remote_port,$job->{job_user},$job->{launching_directory},$Server_epilogue); } OAR::IO::disconnect($base); exit(0); ./oar-2.5.2/sources/core/modules/node_change_state.pl0000755000175000017500000004635511757171206020653 0ustar plbplb#!/usr/bin/perl # $Id$ #Almighty module which changes node state use English; use OAR::IO; use Data::Dumper; use OAR::Modules::Judas qw(oar_debug oar_warn oar_error send_log_by_email set_current_log_category); use IO::Socket::INET; use OAR::Conf qw(init_conf dump_conf get_conf is_conf); use strict; # Log category set_current_log_category('main'); init_conf($ENV{OARCONFFILE}); my $Remote_host = get_conf("SERVER_HOSTNAME"); my $Remote_port = get_conf("SERVER_PORT"); my $Cpuset_field = get_conf("JOB_RESOURCE_MANAGER_PROPERTY_DB_FIELD"); my $Healing_exec_file = get_conf("SUSPECTED_HEALING_EXEC_FILE"); my @resources_to_heal; my $Exit_code = 0; my $base = OAR::IO::connect(); OAR::IO::lock_table($base,["resources","assigned_resources","jobs","job_state_logs","event_logs","event_log_hostnames","frag_jobs","moldable_job_descriptions","challenges","job_types","job_dependencies","job_resource_groups","job_resource_descriptions","resource_logs"]); # Check event logs my @events_to_check = OAR::IO::get_to_check_events($base); foreach my $i (@events_to_check){ oar_debug("[NodeChangeState] Check event for the job $i->{job_id} with type $i->{type}\n"); my $job = OAR::IO::get_job($base,$i->{job_id}); #################################################### # Check if we must resubmit the idempotent jobs # #################################################### if ((($i->{type} eq "SWITCH_INTO_TERMINATE_STATE") or ($i->{type} eq "SWITCH_INTO_ERROR_STATE")) and (defined($job->{exit_code}) and ( ($job->{exit_code} >> 8) == 99))){ my $jobtypes = OAR::IO::get_current_job_types($base, $i->{job_id}); if ((defined($jobtypes->{idempotent}))){ if (($job->{reservation} eq "None") and ($job->{job_type} eq "PASSIVE") and (OAR::IO::is_job_already_resubmitted($base, $i->{job_id}) == 0) and (OAR::IO::is_an_event_exists($base, $i->{job_id},"SEND_KILL_JOB") <= 0)){ my $new_job_id = OAR::IO::resubmit_job($base,$i->{job_id}); oar_warn("[NodeChangeState] We resubmit the job $i->{job_id} (new id = $new_job_id) because it is of the type idempotent and its exit code is 99.\n"); OAR::IO::add_new_event($base,"RESUBMIT_JOB_AUTOMATICALLY",$i->{job_id},"idempotent job type + exit code of 99 = resubmit it (new id = $new_job_id)."); } } } #################################################### # Check if we must expressely change the job state # #################################################### if ($i->{type} eq "SWITCH_INTO_TERMINATE_STATE"){ OAR::IO::set_job_state($base,$i->{job_id},"Terminated"); }elsif ($i->{type} eq "SWITCH_INTO_ERROR_STATE"){ OAR::IO::set_job_state($base,$i->{job_id},"Error"); } ######################################### # Check if we must change the job state # ######################################### if ( ($i->{type} eq "PING_CHECKER_NODE_SUSPECTED") || ($i->{type} eq "CPUSET_ERROR") || ($i->{type} eq "PROLOGUE_ERROR") || ($i->{type} eq "CANNOT_WRITE_NODE_FILE") || ($i->{type} eq "CANNOT_WRITE_PID_FILE") || ($i->{type} eq "USER_SHELL") || ($i->{type} eq "EXTERMINATE_JOB") || ($i->{type} eq "CANNOT_CREATE_TMP_DIRECTORY") || ($i->{type} eq "LAUNCHING_OAREXEC_TIMEOUT") || ($i->{type} eq "RESERVATION_NO_NODE") || ($i->{type} eq "BAD_HASHTABLE_DUMP") || ($i->{type} eq "SSH_TRANSFER_TIMEOUT") || ($i->{type} eq "EXIT_VALUE_OAREXEC") ){ if (($job->{reservation} eq "None") or ($i->{type} eq "RESERVATION_NO_NODE") or ($job->{assigned_moldable_job} == 0)){ OAR::IO::set_job_state($base,$i->{job_id},"Error"); }elsif ($job->{reservation} ne "None"){ if ( ($i->{type} ne "PING_CHECKER_NODE_SUSPECTED") && ($i->{type} ne "CPUSET_ERROR") ){ OAR::IO::set_job_state($base,$i->{job_id},"Error"); } } } if ( ($i->{type} eq "CPUSET_CLEAN_ERROR") || ($i->{type} eq "EPILOGUE_ERROR") ){ # At this point the job was executed normally # The state is changed here to avoid to schedule other jobs # on nodes that will be Suspected OAR::IO::set_job_state($base,$i->{job_id},"Terminated"); } ####################################### # Check if we must suspect some nodes # ####################################### if (($i->{type} eq "PING_CHECKER_NODE_SUSPECTED") || ($i->{type} eq "CPUSET_ERROR") || ($i->{type} eq "CPUSET_CLEAN_ERROR") || ($i->{type} eq "SUSPEND_ERROR") || ($i->{type} eq "RESUME_ERROR") || ($i->{type} eq "PROLOGUE_ERROR") || ($i->{type} eq "EPILOGUE_ERROR") || ($i->{type} eq "CANNOT_WRITE_NODE_FILE") || ($i->{type} eq "CANNOT_WRITE_PID_FILE") || ($i->{type} eq "USER_SHELL") || ($i->{type} eq "EXTERMINATE_JOB") || ($i->{type} eq "CANNOT_CREATE_TMP_DIRECTORY") || ($i->{type} eq "SSH_TRANSFER_TIMEOUT") || ($i->{type} eq "BAD_HASHTABLE_DUMP") || ($i->{type} eq "LAUNCHING_OAREXEC_TIMEOUT") || ($i->{type} eq "EXIT_VALUE_OAREXEC") ){ my @hosts; my $finaud_tag = "NO"; # Restrict Suspected state to the first node (node really connected with OAR) for some event types if (($i->{type} eq "PING_CHECKER_NODE_SUSPECTED") ){ @hosts = OAR::IO::get_hostname_event($base,$i->{event_id}); $finaud_tag = "YES"; }elsif (($i->{type} eq "CPUSET_ERROR") || ($i->{type} eq "CPUSET_CLEAN_ERROR") || ($i->{type} eq "SUSPEND_ERROR") || ($i->{type} eq "RESUME_ERROR") ){ @hosts = OAR::IO::get_hostname_event($base,$i->{event_id}); }else{ @hosts = OAR::IO::get_job_host_log($base,$job->{assigned_moldable_job}); if (($i->{type} ne "EXTERMINATE_JOB") && ($i->{type} ne "PROLOGUE_ERROR") && ($i->{type} ne "EPILOGUE_ERROR") && ($i->{type} ne "CPUSET_ERROR") && ($i->{type} ne "CPUSET_CLEAN_ERROR") ){ @hosts = ($hosts[0]); }else{ # If we exterminate a job and the cpuset feature is configured # then the CPUSET clean will tell us which nodes are dead my $cpuset_field = get_conf("JOB_RESOURCE_MANAGER_PROPERTY_DB_FIELD"); if (defined($cpuset_field) && ($i->{type} eq "EXTERMINATE_JOB")){ #@hosts = ($hosts[0]); @hosts = (); } } OAR::IO::add_new_event_with_host($base, "LOG_SUSPECTED", 0, $i->{description}, \@hosts); } if ($#hosts >= 0){ my %already_treated_host; foreach my $j (@hosts){ next if ((defined($already_treated_host{$j}) or ($j eq ""))); $already_treated_host{$j} = 1; #my @free_resources = OAR::IO::get_current_free_resources_of_node($base, $j); #if ($#free_resources >= 0){ # foreach my $r (@free_resources){ #OAR::IO::set_resource_state($base,$r,"Suspected",$finaud_tag); OAR::IO::set_node_state($base,$j,"Suspected",$finaud_tag); foreach my $r (OAR::IO::get_all_resources_on_node($base,$j)){ push(@resources_to_heal,"$r $j"); } $Exit_code = 1; # } #} } oar_warn("[NodeChangeState] error ($i->{type}) on the nodes:\n\n@hosts\n\nSo we are suspecting corresponding free resources\n"); send_log_by_email("Suspecting nodes","[NodeChangeState] error ($i->{type}) on the nodes:\n\n@hosts\n\nSo we are suspecting corresponding free resources\n"); } } ######################################## # Check if we must stop the scheduling # ######################################## if ( ($i->{type} eq "SERVER_PROLOGUE_TIMEOUT") || ($i->{type} eq "SERVER_PROLOGUE_EXIT_CODE_ERROR") || ($i->{type} eq "SERVER_EPILOGUE_TIMEOUT") || ($i->{type} eq "SERVER_EPILOGUE_EXIT_CODE_ERROR") ){ oar_warn("[NodeChangeState] Server admin script error so we stop all scheduling queues : $i->{type}. When the error will be fixed then you can execute : oarnotify -E\n"); send_log_by_email("Stop all scheduling queues","[NodeChangeState] Server admin script error so we stop all scheduling queues : $i->{type}. When the error will be fixed then you can execute : oarnotify -E\n"); OAR::IO::stop_all_queues($base); OAR::IO::set_job_state($base,$i->{job_id},"Error"); } ##################################### # Check if we must resubmit the job # ##################################### if ( ($i->{type} eq "SERVER_PROLOGUE_TIMEOUT") || ($i->{type} eq "SERVER_PROLOGUE_EXIT_CODE_ERROR") || ($i->{type} eq "SERVER_PROLOGUE_ERROR") || ($i->{type} eq "PING_CHECKER_NODE_SUSPECTED") || ($i->{type} eq "CPUSET_ERROR") || ($i->{type} eq "PROLOGUE_ERROR") || ($i->{type} eq "CANNOT_WRITE_NODE_FILE") || ($i->{type} eq "CANNOT_WRITE_PID_FILE") || ($i->{type} eq "USER_SHELL") || ($i->{type} eq "CANNOT_CREATE_TMP_DIRECTORY") || ($i->{type} eq "LAUNCHING_OAREXEC_TIMEOUT") ){ if (($job->{reservation} eq "None") and ($job->{job_type} eq "PASSIVE") and (OAR::IO::is_job_already_resubmitted($base, $i->{job_id}) == 0)){ my $new_job_id = OAR::IO::resubmit_job($base,$i->{job_id}); oar_warn("[NodeChangeState] We resubmit the job $i->{job_id} (new id = $new_job_id) because the event was $i->{type} and the job is neither a reservation nor an interactive job.\n"); OAR::IO::add_new_event($base,"RESUBMIT_JOB_AUTOMATICALLY",$i->{job_id},"An ERROR occured and we cannot launch this job so we resubmit it (new id = $new_job_id)."); } } #################################### # Check Suspend/Resume job feature # #################################### if ( ($i->{type} eq "HOLD_WAITING_JOB") || ($i->{type} eq "HOLD_RUNNING_JOB") || ($i->{type} eq "RESUME_JOB") ){ if ((($i->{type} eq "HOLD_WAITING_JOB") or ($i->{type} eq "HOLD_RUNNING_JOB")) and (($job->{state} eq "Waiting"))){ OAR::IO::set_job_state($base,$job->{job_id},"Hold"); if ($job->{job_type} eq "INTERACTIVE"){ my ($addr,$port) = split(/:/,$job->{info_type}); OAR::Tools::notify_tcp_socket($addr,$port,"Start prediction: undefined (Hold)"); } }elsif ((($i->{type} eq "HOLD_WAITING_JOB") or ($i->{type} eq "HOLD_RUNNING_JOB")) and (($job->{state} eq "Resuming"))){ OAR::IO::set_job_state($base,$job->{job_id},"Suspended"); OAR::Tools::notify_tcp_socket($Remote_host,$Remote_port,"Term"); }elsif (($i->{type} eq "HOLD_RUNNING_JOB") and ($job->{state} eq "Running")){ # Launch suspend command on all nodes ################ # SUSPEND PART # ################ if (defined($Cpuset_field)){ my $cpuset_name = OAR::IO::get_job_cpuset_name($base, $i->{job_id}) if (defined($Cpuset_field)); my $cpuset_nodes = OAR::IO::get_cpuset_values_for_a_moldable_job($base,$Cpuset_field,$job->{assigned_moldable_job}); my $suspend_data_hash = { name => $cpuset_name, job_id => $i->{job_id}, oarexec_pid_file => OAR::Tools::get_oar_pid_file_name($i->{job_id}), }; if (defined($cpuset_nodes)){ my $taktuk_cmd = get_conf("TAKTUK_CMD"); my $openssh_cmd = get_conf("OPENSSH_CMD"); $openssh_cmd = OAR::Tools::get_default_openssh_cmd() if (!defined($openssh_cmd)); if (is_conf("OAR_SSH_CONNECTION_TIMEOUT")){ OAR::Tools::set_ssh_timeout(get_conf("OAR_SSH_CONNECTION_TIMEOUT")); } my $suspend_file = get_conf("SUSPEND_RESUME_FILE"); $suspend_file = OAR::Tools::get_default_suspend_resume_file() if (!defined($suspend_file)); $suspend_file = "$ENV{OARDIR}/$suspend_file" if ($suspend_file !~ /^\//); my ($tag,@bad) = OAR::Tools::manage_remote_commands([keys(%{$cpuset_nodes})],$suspend_data_hash,$suspend_file,"suspend",$openssh_cmd,$taktuk_cmd,$base); if ($tag == 0){ my $str = "[NodeChangeState] [SUSPEND_RESUME] [$i->{job_id}] Bad suspend/resume file : $suspend_file\n"; oar_error($str); OAR::IO::add_new_event($base, "SUSPEND_RESUME_MANAGER_FILE", $i->{job_id}, $str); }else{ if (($#bad < 0)){ OAR::IO::suspend_job_action($base,$i->{job_id},$job->{assigned_moldable_job}); my $suspend_script = get_conf("JUST_AFTER_SUSPEND_EXEC_FILE"); my $timeout = get_conf("SUSPEND_RESUME_SCRIPT_TIMEOUT"); $timeout = OAR::Tools::get_default_suspend_resume_script_timeout() if (!defined($timeout)); if (defined($suspend_script)){ # Launch admin script my $script_error = 0; eval { $SIG{ALRM} = sub { die "alarm\n" }; alarm($timeout); oar_debug("[NodeChangeState] [$i->{job_id}] LAUNCH the script just after the suspend : $suspend_script $i->{job_id}\n"); $script_error = system("$suspend_script $i->{job_id}"); oar_debug("[NodeChangeStat]e [$i->{job_id}] END the script just after the suspend : $suspend_script $i->{job_id}\n"); alarm(0); }; if( $@ || ($script_error != 0)){ my $str = "[NodeChangeState] [$i->{job_id}] Suspend script error (so we are resuming it): $@; return code = $script_error\n"; oar_warn($str); send_log_by_email("Suspend script error","$str"); OAR::IO::add_new_event($base,"SUSPEND_SCRIPT_ERROR",$i->{job_id},$str); OAR::IO::set_job_state($base,$i->{job_id},"Resuming"); OAR::Tools::notify_tcp_socket($Remote_host,$Remote_port,"Qresume"); } } }else{ my $str = "[NodeChangeState] [SUSPEND_RESUME] [$i->{job_id}] Error on several nodes : @bad\n"; oar_error($str); OAR::IO::add_new_event_with_host($base,"SUSPEND_ERROR",$i->{job_id},$str,\@bad); OAR::IO::frag_job($base,$i->{job_id}); # A Leon must be run $Exit_code = 2; } } } OAR::Tools::notify_tcp_socket($Remote_host,$Remote_port,"Term"); } ###################### # SUSPEND PART, END # ###################### }elsif (($i->{type} eq "RESUME_JOB") and ($job->{state} eq "Suspended")){ OAR::IO::set_job_state($base,$i->{job_id},"Resuming"); OAR::Tools::notify_tcp_socket($Remote_host,$Remote_port,"Qresume"); }elsif (($i->{type} eq "RESUME_JOB") and ($job->{state} eq "Hold")){ OAR::IO::set_job_state($base,$job->{job_id},"Waiting"); OAR::Tools::notify_tcp_socket($Remote_host,$Remote_port,"Qresume"); } } #################################### # Check if we must notify the user # #################################### if ( ($i->{type} eq "FRAG_JOB_REQUEST") ){ my ($addr,$port) = split(/:/,$job->{info_type}); OAR::Modules::Judas::notify_user($base,$job->{notify},$addr,$job->{job_user},$job->{job_id},$job->{job_name},"INFO","Your job was asked to be deleted - $i->{description}"); } OAR::IO::check_event($base, $i->{type}, $i->{job_id}); } # Treate nextState field my %resources_to_change = OAR::IO::get_resources_change_state($base); # A Term command must be added in the Almighty oar_debug("[NodeChangeState] number of resources to change state = ".keys(%resources_to_change)."\n"); if (keys(%resources_to_change) > 0){ $Exit_code = 1; } my %debug_info; foreach my $i (keys(%resources_to_change)){ my $resource_info = OAR::IO::get_resource_info($base,$i); if ($resource_info->{state} ne $resources_to_change{$i}){ OAR::IO::set_resource_state($base,$i,$resources_to_change{$i},$resource_info->{next_finaud_decision}); OAR::IO::set_resource_nextState($base,$i,'UnChanged'); $debug_info{$resource_info->{network_address}}->{$i} = $resources_to_change{$i}; if ($resources_to_change{$i} eq 'Suspected') { push(@resources_to_heal,$i." ".$resource_info->{network_address}); } if (($resources_to_change{$i} eq 'Dead') || ($resources_to_change{$i} eq 'Absent')){ oar_debug("[NodeChangeState] Check jobs to delete on $i ($resource_info->{network_address}):\n"); my @jobs = OAR::IO::get_resource_job_to_frag($base,$i); foreach my $j (@jobs){ oar_debug("[NodeChangeState]\tThe job $j is fragging.\n"); OAR::IO::frag_job($base,$j); # A Leon must be run $Exit_code = 2; } oar_debug("[NodeChangeState] Check done\n"); } }else{ oar_debug("[NodeChangeState] ($resource_info->{network_address}) $i is already in the $resources_to_change{$i} state\n"); OAR::IO::set_resource_nextState($base,$i,'UnChanged'); } } my $str; foreach my $h (keys(%debug_info)){ $str .= "\n$h"; foreach my $r (keys(%{$debug_info{$h}})){ $str .= "\n\t$r --> $debug_info{$h}->{$r}"; } $str .= "\n"; } if (defined($str)){ oar_warn("[NodeChangeState] Resource state changes requested:\n$str\n"); send_log_by_email("Resource state modifications","[NodeChangeState] Resource state changes requested:\n$str\n"); } OAR::IO::unlock_table($base); OAR::IO::disconnect($base); my $timeout_cmd = 10; if (is_conf("SUSPECTED_HEALING_TIMEOUT")){ $timeout_cmd = get_conf("SUSPECTED_HEALING_TIMEOUT"); } if (defined($Healing_exec_file) && @resources_to_heal > 0){ oar_warn("[NodeChangeState] Running healing script for suspected resources.\n"); if (! defined(OAR::Tools::fork_and_feed_stdin($Healing_exec_file, $timeout_cmd, \@resources_to_heal))){ oar_error("[NodeChangeState] Try to launch the command $Healing_exec_file to heal resources, but the command timed out($timeout_cmd s).\n"); } } exit($Exit_code); ./oar-2.5.2/sources/core/modules/leon.pl0000755000175000017500000001164311757171206016146 0ustar plbplb#!/usr/bin/perl # $Id$ # use English; use OAR::IO; use Sys::Hostname; use OAR::Conf qw(init_conf dump_conf get_conf is_conf); use IPC::Open2; use IPC::Open3; use Data::Dumper; use OAR::Modules::Judas qw(oar_debug oar_warn oar_error set_current_log_category); use IO::Socket::INET; use OAR::Tools; use OAR::PingChecker qw(test_hosts); # Log category set_current_log_category('main'); init_conf($ENV{OARCONFFILE}); my $Server_hostname = get_conf("SERVER_HOSTNAME"); my $Server_port = get_conf("SERVER_PORT"); my $Deploy_hostname = get_conf("DEPLOY_HOSTNAME"); if (!defined($Deploy_hostname)){ $Deploy_hostname = $Server_hostname; } my $Cosystem_hostname = get_conf("COSYSTEM_HOSTNAME"); if (!defined($Cosystem_hostname)){ $Cosystem_hostname = $Server_hostname; } my $Server_epilogue = get_conf("SERVER_EPILOGUE_EXEC_FILE"); my $Openssh_cmd = get_conf("OPENSSH_CMD"); $Openssh_cmd = OAR::Tools::get_default_openssh_cmd() if (!defined($Openssh_cmd)); if (is_conf("OAR_SSH_CONNECTION_TIMEOUT")){ OAR::Tools::set_ssh_timeout(get_conf("OAR_SSH_CONNECTION_TIMEOUT")); } if (is_conf("OAR_RUNTIME_DIRECTORY")){ OAR::Tools::set_default_oarexec_directory(get_conf("OAR_RUNTIME_DIRECTORY")); } my $Exit_code = 0; my $base = OAR::IO::connect(); #do it for all job in state LEON in the data base table fragJobs OAR::IO::lock_table($base,["jobs","job_state_logs","resources","assigned_resources","frag_jobs","event_logs","moldable_job_descriptions","job_types","job_resource_descriptions","job_resource_groups","challenges","job_dependencies","gantt_jobs_predictions"]); foreach my $j (OAR::IO::get_to_kill_jobs($base)){ if (OAR::IO::is_job_desktop_computing($base,$j->{job_id})) { oar_debug("[Leon] Job $j->{job_id} is affected to a DesktopComputing resource, I do not handle it\n"); next; } oar_debug("[Leon] Normal kill : I treate the job $j->{job_id}\n"); if (($j->{state} eq "Waiting") || ($j->{state} eq "Hold")){ oar_debug("[Leon] Job is not launched\n"); OAR::IO::set_job_state($base,$j->{job_id},"Error"); OAR::IO::set_job_message($base,$j->{job_id},"job killed by Leon directly"); if ($j->{job_type} eq "INTERACTIVE"){ oar_debug("[Leon] I notify oarsub in waiting mode\n"); #answer($Jid,$refJob->{'infoType'},"JOB KILLED"); my ($addr,$port) = split(/:/,$j->{info_type}); if (!defined(OAR::Tools::notify_tcp_socket($addr, $port, "JOB KILLED"))){ oar_debug("[Leon] Notification done\n"); }else{ oar_debug("[Leon] Cannot open connection to oarsub client for job $j->{job_id}, it is normal if user typed Ctrl-C !!!!!!\n"); } } $Exit_code = 1; }elsif (($j->{state} eq "Terminated") || ($j->{state} eq "Error") || ($j->{state} eq "Finishing")){ oar_debug("[Leon] Job is terminated or is terminating I do nothing\n"); }else{ my $types = OAR::IO::get_current_job_types($base,$j->{job_id}); my @hosts = OAR::IO::get_job_current_hostnames($base,$j->{job_id}); my $host_to_connect_via_ssh = $hosts[0]; #deploy, cosystem and no host part if ((defined($types->{cosystem})) or ($#hosts < 0)){ $host_to_connect_via_ssh = $Cosystem_hostname; }elsif (defined($types->{deploy})){ $host_to_connect_via_ssh = $Deploy_hostname; } #deploy, cosystem and no host part if (defined($host_to_connect_via_ssh)){ OAR::IO::add_new_event($base,"SEND_KILL_JOB",$j->{job_id},"[Leon] Send kill signal to oarexec on $host_to_connect_via_ssh for the job $j->{job_id}"); OAR::Tools::signal_oarexec($host_to_connect_via_ssh, $j->{job_id}, "TERM", 0, $base, $Openssh_cmd, ''); } } OAR::IO::job_arm_leon_timer($base,$j->{job_id}); } #I treate jobs in state EXTERMINATED in the table fragJobs foreach my $j (OAR::IO::get_to_exterminate_jobs($base)){ oar_debug("[Leon] EXTERMINATE the job $j->{job_id}\n"); OAR::IO::set_job_state($base,$j->{job_id},"Finishing"); if ($j->{start_time} == 0){ OAR::IO::set_running_date($base,$j->{job_id}); } OAR::IO::set_finish_date($base,$j->{job_id}); OAR::IO::set_job_message($base,$j->{job_id},"job exterminated by Leon"); OAR::IO::job_arm_leon_timer($base,$j->{job_id}); $Exit_code = 2; $SIG{PIPE} = 'IGNORE'; my $pid = fork(); if ($pid == 0){ #CHILD undef($base); $SIG{USR1} = 'IGNORE'; $SIG{INT} = 'IGNORE'; $SIG{TERM} = 'IGNORE'; my $str = "[Leon] I exterminate the job $j->{job_id}"; my @events; push(@events, {type => "EXTERMINATE_JOB", string => $str}); my $dbh = OAR::IO::connect(); OAR::IO::job_finishing_sequence($dbh,$Server_epilogue,$Server_hostname,$Server_port,$j->{job_id},\@events); OAR::IO::disconnect($dbh); exit(0); } } OAR::IO::unlock_table($base); OAR::IO::disconnect($base); exit($Exit_code); ./oar-2.5.2/sources/core/modules/finaud.pl0000755000175000017500000000525511757171206016461 0ustar plbplb#!/usr/bin/perl # $Id$ # This program aims to change node state automatically when they are inaccesible or they become alive use OAR::IO; use OAR::Modules::Judas qw(oar_debug oar_warn oar_error set_current_log_category); use OAR::PingChecker qw(test_hosts); use OAR::Conf qw(init_conf dump_conf get_conf is_conf); use Data::Dumper; use strict; use IO::Socket::INET; # Log category set_current_log_category('main'); oar_debug("[finaud] Finaud started\n"); oar_debug("[finaud] Check Alive and Suspected nodes\n"); my $base = OAR::IO::connect(); my @node_list_tmp = OAR::IO::get_finaud_nodes($base); my $Occupied_nodes; my $check_occupied_nodes = 'no'; # get in conf the options that tells if we have to check nodes # that are running jobs. init_conf($ENV{OARCONFFILE}); if (is_conf("CHECK_NODES_WITH_RUNNING_JOB")){ $check_occupied_nodes = get_conf("CHECK_NODES_WITH_RUNNING_JOB"); } if ($check_occupied_nodes eq 'no'){ $Occupied_nodes = OAR::IO::get_current_assigned_nodes($base); } my %Nodes_hash; foreach my $i (@node_list_tmp){ if ($check_occupied_nodes eq 'no'){ if (!defined($Occupied_nodes->{$i->{network_address}})){ $Nodes_hash{$i->{network_address}} = $i; } }else{ $Nodes_hash{$i->{network_address}} = $i; } } my @Nodes_to_check = keys(%Nodes_hash); oar_debug("[finaud] Testing resource(s) on : @Nodes_to_check\n"); # Call the right program to test each nodes my %bad_node_hash; foreach my $i (test_hosts(@Nodes_to_check)){ $bad_node_hash{$i} = 1; } #Make the decisions my $return_value = 0; foreach my $i (values(%Nodes_hash)){ if (defined($bad_node_hash{$i->{network_address}}) and ($i->{state} eq "Alive")){ OAR::IO::set_node_nextState($base,$i->{network_address},"Suspected"); OAR::IO::update_node_nextFinaudDecision($base,$i->{network_address},"YES"); OAR::IO::add_new_event_with_host($base, "FINAUD_ERROR", 0, "Finaud has detected an error on the node", [$i->{network_address}]); $return_value = 1; oar_debug("[finaud] Set the next state of $i->{network_address} to Suspected\n"); }elsif (!defined($bad_node_hash{$i->{network_address}}) and ($i->{state} eq "Suspected")){ OAR::IO::set_node_nextState($base,$i->{network_address},"Alive"); OAR::IO::update_node_nextFinaudDecision($base,$i->{network_address},"YES"); OAR::IO::add_new_event_with_host($base, "FINAUD_RECOVER", 0, "Finaud has detected that the node comes back", [$i->{network_address}]); $return_value = 1; oar_debug("[finaud] Set the next state of $i->{network_address} to Alive\n"); } } OAR::IO::disconnect($base); oar_debug("[finaud] Finaud ended : $return_value\n"); exit($return_value); ./oar-2.5.2/sources/core/modules/almighty.pl0000755000175000017500000005110311757171206017022 0ustar plbplb#!/usr/bin/perl use strict; use Data::Dumper; use IO::Socket::INET; use OAR::Modules::Judas qw(oar_debug oar_warn oar_error send_log_by_email set_current_log_category); use OAR::Conf qw(init_conf dump_conf get_conf is_conf get_conf_with_default_param); use OAR::Tools; use OAR::Modules::Hulot; use POSIX qw(:signal_h :errno_h :sys_wait_h); # Log category set_current_log_category('main'); my $Old_umask = sprintf("%lo",umask()); umask(oct("022")); my $oldfh = select(STDERR); $| = 1; select($oldfh); $oldfh = select(STDOUT); $| = 1; select($oldfh); #Everything is run by oar user $ENV{OARDO_UID}=$<; my $Redirect_STD_process = OAR::Modules::Judas::redirect_everything(); oar_warn("[Almighty] Start Almighty\n"); send_log_by_email("Start OAR server","[Almighty] Start Almighty"); # Signal handle system my $finishTag = 0; sub signalHandler(){ $finishTag = 1; } #To avoid zombie processes $SIG{CHLD} = 'IGNORE'; $SIG{USR1} = \&signalHandler; $SIG{INT} = \&signalHandler; $SIG{TERM} = \&signalHandler; init_conf($ENV{OARCONFFILE}); my $binpath; if (defined($ENV{OARDIR})){ $binpath = $ENV{OARDIR}."/"; }else{ oar_error("[Almighty] OARDIR env variable must be defined\n"); exit(1); } my $scheduler_command = $binpath."oar_meta_sched"; my $runner_command = $binpath."runner"; my $check_for_villains_command = $binpath."sarko"; my $check_for_node_changes = $binpath."finaud"; my $leon_command = $binpath."Leon"; my $nodeChangeState_command = $binpath."NodeChangeState"; my $bipbip_command = $binpath."bipbip"; my $server; my $serverport; if (is_conf("SERVER_PORT")){ $serverport = get_conf("SERVER_PORT"); }else{ oar_error("[Almighty] You must have a oar.conf file with a valid SERVER_PORT tag\n"); exit(1); } my $servermaxconnect=100; # This timeout is used by appendice to prevent a client to block # reception by letting a connection opened # should be left at a positive value my $appendice_connection_timeout = 5; # This timeout is used to slowdown the main automaton when the # command queue is empty, it correspond to a blocking read of # new commands. A High value is likely to reduce the CPU usage of # the Almighty. # Setting it to 0 or a low value is not likely to improve performance # dramatically (because it blocks only when nothing else is to be done). # Nevertheless it is closely related to the precision at which the # internal counters are checked my $read_commands_timeout = 10; # This parameter sets the number of pending commands read from # appendice before proceeding with internal work # should not be set at a too high value as this would make the # Almighty weak against flooding my $max_successive_read = 100; # Max waiting time before new scheduling attempt (in the case of # no notification) my $schedulertimeout = 60; # Max waiting time before check for jobs whose time allowed has elapsed my $villainstimeout = 10; # Max waiting time before check node states my $checknodestimeout = get_conf_with_default_param("FINAUD_FREQUENCY", 300); # Max number of concurrent bipbip processes my $Max_bipbip_processes = 100; my %bipbip_children = (); # Internal stuff, not relevant for average user my $lastscheduler; my $lastvillains; my $lastchecknodes; my @internal_command_file; my $appendice_pid; my $energy_pid; # launch the command line passed in parameter sub launch_command($){ my $command = shift; oar_debug("[Almighty] Launching command : [$command]\n"); #$ENV{PATH}="/bin:/usr/bin:/usr/local/bin"; ####### THE LINE BELOW SHOULD NOT BE COMMENTED IN NORMAL USE ##### $SIG{CHLD} = 'DEFAULT'; #system $command; my $pid=0; $pid=fork; if($pid==0){ #CHILD $SIG{USR1} = 'IGNORE'; $SIG{INT} = 'IGNORE'; $SIG{TERM} = 'IGNORE'; $0="Almighty: $command"; exec($command); } my $kid; while ($kid != $pid){ #release zombie and check the end of $command $kid = wait(); } $SIG{CHLD} = 'IGNORE'; my $exit_value = $? >> 8; my $signal_num = $? & 127; my $dumped_core = $? & 128; oar_debug("[Almighty] $command terminated :\n"); oar_debug("[Almighty] Exit value : $exit_value\n"); oar_debug("[Almighty] Signal num : $signal_num\n"); oar_debug("[Almighty] Core dumped : $dumped_core\n"); if ($signal_num || $dumped_core){ oar_error("[Almighty] Something wrong occured (signal or core dumped) when trying to call [$command] command\n"); $finishTag = 1; #exit(2); } return $exit_value; } # listening procedure used by the appendice, a forked process dedicated # to the listening of commands sub qget_appendice(){ my $answer; my $rin = ''; my $res; my $carac; my $client=$server->accept(); oar_debug("[Almighty] Appendice received a connection\n"); if (!defined($client)){ oar_error("[Almighty] End of appendice listening : the socket disappeared\n"); exit(16); } # non-blocking read $rin = ''; my $rinTmp = ''; vec($rin,fileno($client),1) = 1; $res = select($rinTmp = $rin, undef, undef, $appendice_connection_timeout); $carac="A"; while (($res > 0) && ($carac ne "\n")){ if (!defined(sysread($client, $carac, 1))){ oar_warn("[Almighty] End of appendice listening for the current client, client socket is undef; MAYBE SOMEONE USE NMAP ON THE SERVER SOCKET !!!\n"); #exit(3); $res = 0; }elsif ($carac eq ""){ #something wrong occured, we quit this loop. socket was closed $res = 0; }elsif ($carac ne "\n"){ $answer = $answer.$carac; $res = select($rinTmp = $rin, undef, undef, $appendice_connection_timeout); } } # cleans the answer of all unwanted trailing characters $carac=chop $answer; while ($answer && $carac !~ '[a-zA-Z0-9]'){ $carac=chop $answer; } $answer = $answer.$carac; # with nmap these lines crash the appendice #print $client "Your request [$answer] was received\n"; close($client); return $answer; } # main body of the appendice, a forked process dedicated # to the listening of commands # the interest of such a forked process is to ensure that clients get their # notification as soon as possible (i.e. reactivity) even if the almighty is # performing some other internal task in the meantime sub comportement_appendice(){ close READ; while (1){ my $answer = qget_appendice(); if ($answer =~ m/OAREXEC_(\d+)_(\d+)_(\d+|N)_(\d+)/m){ # Check if there are not too many bipbip processes already # running to not overload the server my $wait_bipbip = 1; while ($wait_bipbip == 1){ foreach my $b (keys(%bipbip_children)){ my $wpid_res = waitpid($b, WNOHANG); if (($wpid_res == -1) or ($wpid_res == $b)){ delete($bipbip_children{$b}); } } if (keys(%bipbip_children) >= $Max_bipbip_processes){ oar_warn("[Almighty] Wait the end of bipbip processes (nb children: ".keys(%bipbip_children)."/$Max_bipbip_processes)\n"); sleep(1); }else{ $wait_bipbip = 0; } } my $pid=0; $pid=fork; if($pid==0){ #CHILD $SIG{USR1} = 'IGNORE'; $SIG{INT} = 'IGNORE'; $SIG{TERM} = 'IGNORE'; #$SIG{CHLD} = 'IGNORE'; $SIG{CHLD} = 'DEFAULT'; $0="Almighty: bipbip"; exec("$bipbip_command $1 $2 $3 $4"); } $bipbip_children{$pid} = 1; oar_debug("[Almighty] called bipbip with params: $1 $2 $3 $4 (nb concurrent bipbip = ".keys(%bipbip_children).")\n"); #launch_command("$bipbip_command $1 ATTACH &"); }elsif ($answer ne ""){ oar_debug("[Almighty] Appendice has read on the socket : $answer\n"); print WRITE "$answer\n"; flush WRITE; }else{ oar_debug("[Almighty] A connection was opened but nothing was written in the socket\n"); #sleep(1); } } } # hulot module forking sub start_hulot(){ $energy_pid = fork(); if(!defined($energy_pid)){ oar_error("[Almighty] Cannot fork Hulot, the energy saving module\n"); exit(6); } if (!$energy_pid){ $SIG{CHLD} = 'DEFAULT'; $SIG{USR1} = 'IGNORE'; $SIG{INT} = 'IGNORE'; $SIG{TERM} = 'IGNORE'; $0="Almighty: hulot"; OAR::Modules::Hulot::start_energy_loop(); oar_error("[Almighty] Energy saving loop (hulot) exited. This should not happen.\n"); exit(7); } } # check the hulot process sub check_hulot(){ return kill 0, $energy_pid; } # Clean ipcs sub ipc_clean(){ open(IPCS,"/proc/sysvipc/msg"); my @oar = getpwnam('oar'); while () { my @ipcs=split; if ($ipcs[7] eq $oar[2]) { my $ipc=$ipcs[1]; oar_debug("[Almighty] cleaning ipc $ipc\n"); `/usr/bin/ipcrm -q $ipc`; } } close(IPCS); } # initial stuff that has to be done sub init(){ if(!(pipe (READ, WRITE))){ oar_error("[Almighty] Cannot open pipe !!!\n"); exit(5); } autoflush READ 1; autoflush WRITE 1; $server = IO::Socket::INET->new(LocalPort=> $serverport, Type => SOCK_STREAM, Reuse => 1, Listen => $servermaxconnect); #or die "ARG.... Can't open server socket\n"; if (!defined($server)){ warn("ARG.... Cannot open server socket, an Almighty process must be already started\n"); oar_error("[Almighty] ARG.... Cannot open server socket, an Almighty process must be already started\n"); exit(4); } $SIG{PIPE} = 'IGNORE'; #Must be catch otherwise the appendice can finish abnormally $appendice_pid = fork(); if(!defined($appendice_pid)){ oar_error("[Almighty] Cannot fork appendice (fork process dedicated to the listening of commands from clients)\n"); exit(6); } if (!$appendice_pid){ $SIG{USR1} = 'IGNORE'; $SIG{INT} = 'IGNORE'; $SIG{TERM} = 'IGNORE'; $0="Almighty: appendice"; comportement_appendice(); oar_error("[Almighty] Returned from comportement_appendice, this should not happen (infinite loop for listening messages on the server socket)\n"); exit(7); } close WRITE; close $server; # Starting of Hulot, the Energy saving module if (get_conf_with_default_param("ENERGY_SAVING_INTERNAL", "no") eq "yes") { start_hulot(); } $lastscheduler= time; $lastvillains= time; $lastchecknodes= time; @internal_command_file = (); oar_debug("[Almighty] Init done\n"); } # function used by the main automaton to get notifications pending # inside the appendice sub qget($){ my $timeout = shift; my $answer=""; my $rin = ''; my $rinTmp = ''; my $carac; vec($rin,fileno(READ),1) = 1; my $res = select($rinTmp = $rin, undef, undef, $timeout); if ($res > 0){ $carac="OAR"; while ($carac ne "\n"){ if ((!defined(sysread(READ, $carac, 1))) || ($carac eq "")){ oar_error("[Almighty] Error while reading in pipe : I guess Appendice has died\n"); exit(8); } if ($carac ne "\n"){ $answer = $answer.$carac; } } }elsif ($res < 0){ if ($finishTag == 1){ oar_debug("[Almighty] Premature end of select cmd. res = $res. It is normal, Almighty is stopping\n"); $answer = "Time"; }else{ oar_error("[Almighty] Error while reading in pipe : I guess Appendice has died, the result code of select = $res\n"); exit(15); } }else{ $answer = "Time"; } return $answer; } # functions for managing the file of commands pending sub add_command($){ my $command = shift; # as commands are just notifications that will # handle all the modifications in the base up to now, we should # avoid duplication in the command file if (!grep(/^$command$/,@internal_command_file)){ push @internal_command_file, $command; } } # read commands until reaching the maximal successive read value or # having read all of the pending commands sub read_commands($){ my $timeout = shift; my $command = ""; my $remaining = $max_successive_read; while (($command ne "Time") && $remaining){ if ($remaining != $max_successive_read){ $timeout = 0; } $command = qget($timeout); add_command($command); $remaining--; oar_debug("[Almighty] Got command $command, $remaining remaining\n"); } # The special case of the Time command # semantic : the queue is empty so the Almighty should go # directly to the state of updating of its internal counters push @internal_command_file,"Time" unless scalar @internal_command_file; } # functions associated with each state of the automaton sub scheduler(){ return launch_command $scheduler_command; } sub runner(){ return launch_command $runner_command; } sub time_update(){ my $current = time; my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($current); $year+=1900; $mon+=1; oar_debug("[Almighty] Timeouts check : $year-$mon-$mday $hour:$min:$sec\n"); # check timeout for scheduler if ($current>=($lastscheduler+$schedulertimeout)){ oar_debug("[Almighty] Scheduling timeout\n"); #$lastscheduler = $lastscheduler+$schedulertimeout; $lastscheduler = $current + $schedulertimeout; add_command("Scheduling"); } if ($current>=($lastvillains+$villainstimeout)){ oar_debug("[Almighty] Villains check timeout\n"); #$lastvillains = $lastvillains+$villainstimeout; $lastvillains = $current + $villainstimeout; add_command("Villains"); } if (($current>=($lastchecknodes+$checknodestimeout)) and ($checknodestimeout > 0)){ oar_debug("[Almighty] Node check timeout\n"); #$lastchecknodes = $lastchecknodes+$checknodestimeout; $lastchecknodes = $current + $checknodestimeout; add_command("Finaud"); } } sub check_for_villains(){ return launch_command $check_for_villains_command; } sub check_nodes(){ return launch_command $check_for_node_changes; } sub leon(){ return launch_command "$leon_command"; } sub nodeChangeState(){ return launch_command $nodeChangeState_command; } # MAIN PROGRAM : Almighty AUTOMATON my $state= "Init"; my $command; my $id; my $node; my $pid; while (1){ oar_debug("[Almighty] Current state [$state]\n"); #We stop Almighty and its child if ($finishTag == 1){ if (defined($energy_pid)) { oar_debug("[Almighty] kill child process $energy_pid\n"); kill(9,$energy_pid); } oar_debug("[Almighty] kill child process $appendice_pid\n"); kill(9,$appendice_pid); kill(9,$Redirect_STD_process) if ($Redirect_STD_process > 0); ipc_clean(); oar_warn("[Almighty] Stop Almighty\n"); send_log_by_email("Stop OAR server","[Almighty] Stop Almighty"); exit(10); } # We check Hulot if (defined($energy_pid) && !check_hulot()) { oar_warn("[Almighty] Energy saving module (hulot) died. Restarting it.\n"); sleep 5; ipc_clean(); start_hulot(); } # INIT if($state eq "Init"){ init(); $state="Qget"; } # QGET elsif($state eq "Qget"){ if (scalar @internal_command_file){ read_commands(0); }else{ read_commands($read_commands_timeout); } oar_debug("[Almighty] Command queue : @internal_command_file\n"); my $current_command = shift(@internal_command_file); my ($command,$arg1,$arg2,$arg3) = split(/ /,$current_command); oar_debug("[Almighty] Qtype = [$command]\n"); if (($command eq "Qsub") || ($command eq "Term") || ($command eq "BipBip") || ($command eq "Scheduling") || ($command eq "Qresume") ){ $state="Scheduler"; }elsif( $command eq "Qdel"){ $state="Leon"; }elsif($command eq "Villains"){ $state="Check for villains"; }elsif($command eq "Finaud"){ $state="Check node states"; }elsif ($command eq "Time"){ $state="Time update"; }elsif ($command eq "ChState"){ $state="Change node state"; }else{ oar_debug("[Almighty] Unknown command found in queue : $command\n"); } } # SCHEDULER elsif($state eq "Scheduler"){ # First, check pending events my $check_result=nodeChangeState(); if ($check_result == 2){ $state="Leon"; add_command("Term"); }elsif ($check_result == 1){ $state="Scheduler"; }elsif ($check_result == 0){ #Launch the scheduler # We check Hulot just before starting the scheduler # because if the pipe is not read, it may freeze oar if (defined($energy_pid) && !check_hulot()) { oar_warn("[Almighty] Energy saving module (hulot) died. Restarting it.\n"); sleep 5; ipc_clean(); start_hulot(); } my $scheduler_result=scheduler(); if ($scheduler_result == 1){ $state="Runner"; }elsif ($scheduler_result == 0){ $state="Time update"; }elsif ($scheduler_result == 2){ $state="Leon"; }else{ oar_error("[Almighty] Scheduler returned an unknown value : $scheduler_result\n"); $finishTag = 1; } }else{ oar_error("[Almighty] $nodeChangeState_command returned an unknown value\n"); $finishTag = 1; } } # RUNNER elsif($state eq "Runner"){ my $check_result=runner(); if ($check_result == 1){ $state="Leon"; }elsif ($check_result == 2){ $state="Scheduler"; }else{ $state="Time update"; } } # TIME UPDATE elsif($state eq "Time update"){ time_update(); $state="Qget"; } # CHECK FOR VILLAINS elsif($state eq "Check for villains"){ my $check_result=check_for_villains(); if ($check_result == 1){ $state="Leon"; }elsif ($check_result == 0){ $state="Time update"; }else{ oar_error("[Almighty] $check_for_villains_command returned an unknown value : $check_result\n"); $finishTag = 1; } } # CHECK NODE STATES elsif($state eq "Check node states"){ my $check_result=check_nodes(); if ($check_result == 1){ $state="Change node state"; }elsif ($check_result == 0){ $state="Time update"; }else{ oar_error("[Almighty] $check_for_node_changes returned an unknown value\n"); $finishTag = 1; } } # LEON elsif($state eq "Leon"){ my $check_result = leon(); $state="Time update"; if ($check_result == 1){ add_command("Term"); }elsif($check_result == 2){ $state = "Change node state"; } } # Change state for dynamic nodes elsif($state eq "Change node state"){ my $check_result=nodeChangeState(); if ($check_result == 2){ $state="Leon"; add_command("Term"); }elsif ($check_result == 1){ $state="Scheduler"; }elsif ($check_result == 0){ $state="Time update"; }else{ oar_error("[Almighty] $nodeChangeState_command returned an unknown value\n"); $finishTag = 1; } }else{ oar_warn("[Almighty] Critical bug !!!!\n"); oar_error("[Almighty] Almighty just falled into an unknown state !!!\n"); $finishTag = 1; } } ./oar-2.5.2/sources/core/man/0000755000175000017500000000000011757171206013747 5ustar plbplb./oar-2.5.2/sources/core/man/man1/0000755000175000017500000000000011757171206014603 5ustar plbplb./oar-2.5.2/sources/core/man/man1/oarsub.pod0000644000175000017500000002426611757171206016614 0ustar plbplb=head1 NAME oarsub - OAR batch scheduler job submission command. =head1 SYNOPSIS B [OPTIONS] B [OPTIONS] -I B [OPTIONS] -C =head1 DESCRIPTION One uses B to submit a job to the B managing the resources of a B. A job is defined by the description of a set of resources needed to execute a task and a script or executable to run. A job may also be run interactively, and one may also use oarsub to connect to a previously submitted job. The scheduler is in charge of providing a set of resources matching the oarsub command request. Once scheduled and then launched, a job consists of one process executed on the first node of the resources it was attibuted, with a set of environment variables that define the resources which are at the job disposal. That means that the job's executable is responsible for connecting those resources and dispatching the tasks. =head1 OPTIONS =over =item B<-I, --interactive> Request an interactive job. Open a login shell on the first node of the reservation instead of running a script. =item B<-C, --connect> Connect to a running job. =item B<-l, --resource> Set the requested resources for the job. The different parameters are resource properties registered in OAR database, and `walltime' which specifies the duration before the job must be automatically terminated if still running. Walltime format is [hour:mn:sec|hour:mn|hour]. Ex: nodes=4/cpu=1,walltime=2:00:00 You can specify multiple -l options at the same line. This tells OAR that this is a moldable job so it can take different shapes. For example if you have an application that is very scalable: oarsub -l cpu=2,walltime=20:00:00 -l cpu=4,walltime=10:00:00 -l cpu=8,walltime=5:00:00 ./script.sh OAR will schedule one of these three resource definitions (depending of the current load of the cluster). =item B<--array> Submit an array job containing "NUMBER" subjobs. All the subjobs share the same array_id but each subjob is independent and has its own job_id. All the subjobs have the same characteristics (script, requirements) and can be identified by an environment variable $OAR_ARRAY_INDEX. Array jobs can neither be Interactive (-I) nor a reservation (-r). =item B<--array-param-file> Submit a parametric array job. Each non-empty line of "FILE" defines the parameters for the submition of new subjob. All the subjobs have the same characteristics (script, requirements) and can be identified by an environment variable $OAR_ARRAY_INDEX. '#' is the comment sign. Parametric array jobs can neither be Interactive (-I) nor a reservation (-r). =item B<-S, --scanscript> Batch mode only: ask oarsub to scan the given script for OAR directives (#OAR -l ...) =item B<-q, --queue> Set the the queue to submit the job to. =item B<-p, --property> "" Add a list of constraints to properties for the job. The format of a contraint is the one of a WHERE clause using the SQL syntax. =item B<-r, --reservation> Request that the job starts at a specified time. A job creation using this option is called a I and instead of a I. =item B<--checkpoint> Enable the checkpointing mechanism for the job. A signal will be sent DELAY seconds before the walltime to the first processus of the job (on the first node of the resources). =item B<--signal> <#SIG> Specify the signal to use when checkpointing. Use signal numbers (see kill -l), default is I<12> (SIGUSR2). =item B<-t, --type> Specify a specific type (I, I, I, I, I, I, I, I) Note: a job with a type of B will be automatically resubmitted if its exit code is 99. =item B<-d, --directory> Specify the directory where to launch the command (default is current directory) =item B<--project> Specify a name of a project the job belongs to. =item B<-n, --name> Specify an arbitrary name for the job. =item B<-a, --anterior> Previously submitted job that this new job execution must depend on. The new job will only start upon the end of the previous one. =item B<--notify> Specify a notification method (mail or command to execute). Ex: --notify "mail:name@domain.com" --notify "exec:/path/to/script args" args are job_id,job_name,TAG,comment TAG can be: - RUNNING : when the job is launched - END : when the job is finished normally - ERROR : when the job is finished abnormally - INFO : used when oardel is called on the job - SUSPENDED : when the job is suspended - RESUMING : when the job is resumed =item B<--resubmit> Resubmit the given job as a new one. =item B<-k, --use-job-key> Activate the job-key mechanism. A job-key will be generated allowing to connect the job from outside the set of resources managed by OAR. The job-key mechanism may be activated by default in your OAR environment. In this case this option is useless. =item B<-i, --import-job-key-from-file> Import the job-key to use from existing files (public and private key files) instead of generating a new one. One may also use the OAR_JOB_KEY_FILE environment variable. =item B<--import-job-key-inline> Import the job-key to use inline instead of generating a new one. =item B<-e, --export-job-key-to-file> Export the the job key to a file. I: the file will be overwritten if it already exists. (the %jobid% pattern is automatically replaced) =item B<-O> Specify the files that will store the standard output stream of the job. The %jobid% pattern is automatically replaced. =item B<-E> Specify the files that will store the standard error stream of the job. The %jobid% pattern is automatically replaced. =item B<--hold> Set the job state into Hold instead of Waiting, so that it is not scheduled (you must run I to turn it into the Waiting state). =item B<-D, --dumper> Print result in Perl Data::Dumper format. =item B<-X, --xml> Print result in XML format. =item B<-Y, --yaml> Print result in YAML format. =item B<-J, --json> Print result in JSON format. =item B<-h, --help> Print this help message. =item B<-V, --version> Print the version of OAR. =back =head1 ENVIRONMENT =over =item B aka B aka B Pathname to the file containing the list of the nodes that are allocated to the job. =item B Name of the job as given using the B<-n> option. =item B aka B Id of the job. Each job get a unique job identifier. This identifier can be use to retrieve information about the job using B, or to connect to a running job using B or B for instance. =item B aka B Array Id of the job. Each array job get an unique array identifier that is shared by all the subjobs of the array job. This identifier can be used to identify the different subjobs pertaining to a same array job. It can also be used to deal with all the subjobs of a given array at once (by means of the option --array in the case of oarstat, oarhold, oarresume and oardel). By definition, single jobs are considered array jobs with only one subjob. =item B aka B Array Index of the job. On an array job, each job get an unique an unique (on a given array) job index. This identifier can be used to differ jobs on the context of a given array, for instance to give a different behaviour to each of the subjobs. By definition, single jobs are considered array jobs with only one subjob, having OAR_JOB_INDEX = 0. =item B resp. B Walltime of the job in the hh:mm:ss format resp. in seconds. =item B Pathname to the file containing the list of all resources attributes for the job, and their value =item B Name of the project the job is part of, as given using the B<--project> option. =item B and B Pathname to the files storing the standard output and error of the job's executable when not running in interactive mode. =item B aka B aka B Working directory for the job. The job executable will be executed in this directory on the first node allocated to the job. =item B Key file to use for the submission (or for oarsh) if using a job key (-k or --use-job-key option). One may provide the job key to import using the -i or --import-job-key-from-file option as well. =back =head1 SCRIPT Script can contain the description of the job. Lines with options must begin by the key B<#OAR>. There are the same options as previous. =head1 EXAMPLES Job submission with arguments : =over =item oarsub -l /nodes=4 -I =item oarsub -q default -l /nodes=10/cpu=3,walltime=50:30:00 -p "switch = 'sw1'" /home/users/toto/prog =item oarsub -r "2009-04-27 11:00:00" -l /nodes=12/cpu=2 =item oarsub -C 154 =back Submit an array job with 10 identic subjobs: =over =item oarsub -l /nodes=4 /home/users/toto/prog --array 10 =back Submit a parametric array job (file params.txt): =over =item oarsub /home/users/toto/prog --array-param-file /home/users/toto/params.txt =back =over =item Parameter File example (params.txt): =item # my param file =item #single param =item 100 =item #a subjob without parameters =item "" =item #a subjob with a string containing spaces as parameter =item "arg1a arg1b arg1c" "arg2a arg2b" =back Script example (file /home/users/toto/script.sh): =over =item #!/bin/bash =item #OAR -l /nodes=4/cpu=1,walltime=3:15:00 =item #OAR -p switch = 'sw3' or switch = 'sw5' =item #OAR -t besteffort =item #OAR -t type2 =item #OAR -k =item #OAR -e /path/to/job/key =item #OAR --stdout stdoutfile.log =item /home/users/toto/prog =back Submit the script : =over =item oarsub -S /home/users/toto/script.sh =back =head1 SEE ALSO oarsh(1), oardel(1), oarstat(1), oarnodes(1), oarhold(1), oarresume(1) =head1 COPYRIGHTS Copyright 2008 Laboratoire d'Informatique de Grenoble (http://www.liglab.fr). This software is licensed under the GNU Library General Public License. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ./oar-2.5.2/sources/core/man/man1/oarstat.pod0000644000175000017500000000476211757171206016775 0ustar plbplb=head1 NAME oarstat - show information about jobs =head1 SYNOPSIS B [-X|-Y|-J|-D|-f] [-j jobid|--array arrayid] [--sql SQL_properties] [-u user] [--array] B [-e|-p] [-j jobid | --array arrayid] B -s [-X|-Y|-J|-D] -j jobid B [-X|-Y|-J|-D] --gantt "YYYY-MM-DD hh:mm:ss, YYYY-MM-DD hh:mm:ss" B --accounting "YYYY-MM-DD, YYYY-MM-DD" =head1 DESCRIPTION This command is used to show information of jobs in OAR batch scheduler. =head1 OPTIONS =over =item B<-f, --full> Very detailed display. =item B<-j, --job> job_id Print information from a specific job. =item B<-e, --events> Print job events only. =item B<-p, --properties> Print job properties in the same format as in the $OAR_RESOURCE_PROPERTIES_FILE file on the nodes. =item B<-s, --state> Print only the state of a specified job. It makes a minimal query to the database. It is optimized to allow scripting for example. Must be used with -j. =item B<-u, --user> [login] Print information for the current user or the one given. If used with --accounting, print a more detailed accounting report for the user. =item B<--array> [array_id] Toggle the visualization of array information on. If an array_id is provided, print information relative to the subjobs of the given array job. =item B<--sql> Restricts display with the SQL where clause on the table jobs (ex: "project = 'p1'") =item B<-D, --DUMPER> Print result in DUMPER format. =item B<-X, --XML> Print result in XML format. =item B<-Y, --YAML> Print result in YAML format. =item B<-J, --JSON> Print result in JSON format. =item B<-g, --gantt> "date_start,date_stop" Print history of jobs and state of resources between two dates like "2006-03-30 13:49:27, 2006-04-30 13:49:27" =item B<--accounting> "date_start,date_stop" Shows accounting information between two dates like "2006-03-30, 2006-04-30". If --user is also used, more details are shown for this particular user. Warning: the accounting table must be up to date. The update must be done at superuser level with the oaraccounting command. =item B<-V, --version> Print OAR version number. =item B<-h, --help> Print help message. =back =head1 SEE ALSO oarprint(1), oarsub(1), oardel(1), oarnodes(1), oarhold(1), oarresume(1) =head1 COPYRIGHTS Copyright 2008 Laboratoire d'Informatique de Grenoble (http://www.liglab.fr). This software is licensed under the GNU Library General Public License. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ./oar-2.5.2/sources/core/man/man1/oarsh.pod0000644000175000017500000000626611757171206016435 0ustar plbplb=head1 NAME oarsh - remote shell connector for OAR batch scheduler. oarcp - oarsh compagnon to copy files from a node or to a node. =head1 SYNOPSIS B [OPTIONS] [COMMAND] B [OPTIONS] [NODENAME:] [NODENAME:] =head1 DESCRIPTION Connect a node from the submission frontal of the cluster or any node. =head1 OPTIONS B uses I client (the ssh command) underneath to perform the connection. Thus any OpenSSH option can be used. =head1 ENVIRONMENT =over =item B From the frontal of the cluster or any node, specify the Id of the job B must connect to. =item B Specify a job key oarsh must use, e.g. the one that was used for the submission of the job you want to connect to. This is mandatory when connecting to a node of a job from a host that does not belong to the nodes managed by the OAR server the job was submitted to. The -i option may be used as well. =back =head1 CONFIGURATION In order to provide the user with the ability to use B to connect both the nodes of his job or other hosts that live out of the scope of his job, B tries to read two configuration files: first B<~/.oarsh-host-include> then B<~/.oarsh-hosts-exclude>. If exist, those files must contain one regular expression matching a hostname per line. At execution time, if B finds in B<~/.oarsh-host-include> a match for the hostname used in the command line, it continues with the execution of B, skipping B<~/.oarsh-hosts-exclude> file. If not, it tries to find a match in B<~/.oarsh-hosts-exclude> and if one is found, then executes B with the same command line. Finally, it no match is found (or for instance, if none of those files exists), it continues with the execution of B. For instance, if all nodes look like I, one may place I<^[^\.]+-[[:digit:]]+> in B<~/.oarsh-host-include> and I<.*> in B<~/.oarsh-hosts-exclude> and then can use B to connect any host. The feature finally becomes really sexy when one considers placing a symlink to B named B, and then can always use the B command to connect any host. =head1 EXAMPLES =over =item Connecting from within our job, from one node to another one (node23): > oarsh node-23 =item Connecting to a node (node23) of our job (Id: 4242) from the frontal of the cluster: > OAR_JOB_ID=4242 oarsh node-23 =item Connecting to a node (node23) of our job that was submitted using a job key: > OAR_JOB_KEY_FILE=~/my_key oarsh node-23 =item Same thing but using OpenSSH-like I<-i> option: > oarsh -i ~/my_key node-23 =back =head1 NOTES All OpenSSH features should be inherited by B, for instance X11 forwarding. However, one feature that B does break is the SSH Agent. None of OpenSSH user configuration files (within I<~/.ssh> directory) are used by B. =head1 SEE ALSO oarsub(1), oardel(1) oarstat(1), oarnodes(1), oarhold(1), oarresume(1) =head1 COPYRIGHTS Copyright 2008 Laboratoire d'Informatique de Grenoble (http://www.liglab.fr). This software is licensed under the GNU Library General Public License. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ./oar-2.5.2/sources/core/man/man1/oarresume.pod0000644000175000017500000000162611757171206017316 0ustar plbplb=head1 NAME oarresume - resume a job in OAR batch scheduler. =head1 SYNOPSIS B [--array][job_ids][--sql "sql syntax"][-V][-h] =head1 DESCRIPTION Ask OAR to change job_ids states into Waiting when it is Hold or in Running if it is Suspended. =head1 OPTIONS =over =item B<--array> Resume array job(s) passed as parameter (all the sub-jobs of the given array job(s)) =item B<--sql> Resume jobs which repond to the SQL where clause on the table jobs (ex: "project = 'p1'") =item B<-V, --version> Print OAR version number. =item B<-h, --help> Print help command message. oarsub(1), oardel(1) oarstat(1), oarnodes(1), oarhold(1) =back =head1 COPYRIGHTS Copyright 2008 Laboratoire d'Informatique de Grenoble (http://www.liglab.fr). This software is licensed under the GNU Library General Public License. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ./oar-2.5.2/sources/core/man/man1/oarremoveresource.pod0000644000175000017500000000113011757171206021051 0ustar plbplb=head1 NAME oarremoveresource - Remove a resource from the database. =head1 SYNOPSIS B resource_number =head1 DESCRIPTION this command removes all records in the database about "resource_number". This "resource_number" must be in the "Dead" state (see oarnodesetting). So you will loose this resource history and jobs executed on this one. =head1 COPYRIGHTS Copyright 2003-2012 LIG http://lig.imag.fr This software is licensed under the GNU General Public License Version 2 or above. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ./oar-2.5.2/sources/core/man/man1/oarproperty.pod0000644000175000017500000000153011757171206017674 0ustar plbplb=head1 NAME oarproperty - add or remove resource properties =head1 SYNOPSIS B [ -l | [ -a | -d ] ] [ -q ] =head1 DESCRIPTION This command is used to add or remove resource properties in the resource table. =head1 OPTIONS =over =item B<-l, --list> List properties. =item B<-a NAME, --add NAME> Add property =item B<-c, --varchar> Ask for a new Sql field of type VARCHAR(255) (default is integer). =item B<-d NAME, --delete> NAME Delete property. =item B<-r "OLD_NAME,NEW_NAME", --rename ...> Rename property OLD_NAME into NEW_NAME. =item B<-q, --quiet> Quiet mode (no extra output). =back =head1 COPYRIGHTS Copyright 2003-2012 LIG http://lig.imag.fr This software is licensed under the GNU General Public License Version 2 or above. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ./oar-2.5.2/sources/core/man/man1/oarprint.pod0000644000175000017500000000265411757171206017154 0ustar plbplb=head1 NAME oarprint - pretty print of a job resources =head1 SYNOPSIS B [options] =head1 DESCRIPTION Print a sorted output of the resources of a job with regard to a key property, with a customisable format. =head1 OPTION =over =item B<-f, --file> Input file, default is $OAR_RESOURCE_PROPERTIES_FILE (available within a job). See the EXAMPLES section below for more details. =item B<-P, --properties> Property to display separated by commas, default: key property. =item B<-F,--format> Customised output format, default: "%" . =item B<-T, --token> Substitution token to replace in the format string, default: % . =item B<-C, --comma> Separator to use when displaying lists, default: , . =item B<-l, --list> List available properties and exit. =item B<-h, --help> Show help usage. =back =head1 EXAMPLES On the job connection node (where $OAR_RESOURCE_PROPERTIES_FILE is defined): oarprint host -P host,cpu,core -F "host: % cpu: % core: %" -C+ On the submission frontend: oarstat -j 42 -p | oarprint core -P host,cpuset,mem -F "%[%] (%)" -f - =head1 SEE ALSO oarstat(1), oarnodes(1), oarsub(1), oardel(1), oarhold(1), oarresume(1) =head1 COPYRIGHTS Copyright 2008 Laboratoire d'Informatique de Grenoble (http://www.liglab.fr). This software is licensed under the GNU Library General Public License. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ./oar-2.5.2/sources/core/man/man1/oarnotify.pod0000644000175000017500000000232611757171206017324 0ustar plbplb=head1 NAME oarnotify - Multi usage command for queues and Almighty notification. =head1 SYNOPSIS B [-e str] [-d str] [-E] [-D] [--add_queue str] [--remove_queue] [-l] [tag_to_Almighty] =head1 DESCRIPTION This command is used to manage scheduling queues and to notify the OAR server (Almighty). =head1 OPTIONS =over =item B<-e, --enable_queue> Active an existing queue. =item B<-d, --disable_queue> Inactive an existing queue. =item B<-E, --Enable_all_queues> Active all queues. =item B<-D, --Disable_all_queues> Inactive all queues. =item B<--add_queue> Add a new queue; syntax is name,priority,scheduler. (ex: "name,3,oar_sched_gantt_with_timesharing") =item B<--remove_queue> Remove an existing queue. =item B<-l, --list_queues> List all queues and there status. =item B The non exhaustive list of these tags is: - "Term" : ask OAR server to launch the scheduler - "Finaud" : ask OAR server to launch the check of empty and Suspected nodes =back =head1 COPYRIGHTS Copyright 2003-2012 LIG http://lig.imag.fr This software is licensed under the GNU General Public License Version 2 or above. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ./oar-2.5.2/sources/core/man/man1/oarnodesetting.pod0000644000175000017500000000251611757171206020340 0ustar plbplb=head1 NAME oarnodesetting - Change the state and properties of a node in OAR. =head1 SYNOPSIS B [-h hostname | [[-r resource_number][--sql "sql syntax"] | -a]] =head1 DESCRIPTION This command is used to Change the state and properties of a node in OAR. =head1 OPTIONS =over =item B<-a, --add> Add a new resource. =item B<-s, --state> STATE Set the new state of the node. The available states are: Alive, Absent and Dead. =item B<-m, --maintenance> on/off Set the maintenance mode on/off (resource state will be set to absent and available_upto field set to 0). =item B<-h, --hostname> HOSTNAME Set the node hostname. =item B<-r, --resource> Set the resource. =item B<--sql> Get resource identifiers which respond to the SQL where clause on the table jobs (ex: "type = 'default'"). =item B<-p, --property> "PROPERTY=VALUE" Set the property of the node to the given value. =item B<-n, --nowait> Do not wait for job ends when the node switches to Absent or Dead. =back =head1 EXAMPLES =over =item oarnodesetting -s Alive -h node012 =item oarnodesetting -s Absent -h node012 =back =head1 COPYRIGHTS Copyright 2003-2012 LIG http://lig.imag.fr This software is licensed under the GNU General Public License Version 2 or above. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ./oar-2.5.2/sources/core/man/man1/oarnodes.pod0000644000175000017500000000271411757171206017125 0ustar plbplb=head1 NAME oarnodes - show information about the nodes managed by OAR. =head1 SYNOPSIS B [ -a | -s | -r | --sql | -l | -h | -V ] [] [ -e [] | -X | -Y | -D ] =head1 DESCRIPTION Display information about the nodes managed by OAR. =head1 OPTION =over =item B<-a, --all> Show all resources with their properties. =item B<-r, --resource> Show only the resource whose id is given as parameter with its properties. =item B<-s, --state> Show the states of the resources. =item B<-l, --list> Show the nodes list. =item B<-e, --events> Print events related to a node: either since the date given as parameter or the last 20. =item B<--sql> Select the resources which match the SQL where clause given as parameter (e.g. "state = 'Suspected'") and display them. =item B<-D, --DUMPER> Print result in Perl Data::Dumper format. =item B<-X, --XML> Print result in XML format. =item B<-Y, --YAML> Print result in YAML format. =item B<-J, --JSON> Print result in JSON format. =item B<-V, --version> Print OAR version number. =item B<-h, --help> Show the help message. =back =head1 SEE ALSO oarsub(1), oardel(1), oarstat(1), oarhold(1), oarresume(1) =head1 COPYRIGHTS Copyright 2008 Laboratoire d'Informatique de Grenoble (http://www.liglab.fr). This software is licensed under the GNU Library General Public License. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ./oar-2.5.2/sources/core/man/man1/oarnodecheckquery.pod0000644000175000017500000000067611757171206021033 0ustar plbplb=encoding UTF-8 =head1 NAME oarnodecheckquery - node checking =head1 SYNOPSIS B =head1 DESCRIPTION This command checks for the existence of the oarnodecheckrun stamp file and if it is older than one hour, then it runs oarnodecheckrun before looking into the checklog directory (NB: the OAR ping checker is not called while at least one job is running on a node) =head1 SEE ALSO See %%DOCIR%%/oarnodecheck/README =cut ./oar-2.5.2/sources/core/man/man1/oarnodechecklist.pod0000644000175000017500000000041711757171206020632 0ustar plbplb=encoding UTF-8 =head1 NAME oarnodechecklist - a simple local node check mechanism =head1 SYNOPSIS B =head1 DESCRIPTION This command lists the currently recorder check logs for this node. =head1 SEE ALSO see %%DOCIR%%/oarnodecheck/README =cut ./oar-2.5.2/sources/core/man/man1/oarmonitor_graph_gen.pod0000644000175000017500000000113111757171206021506 0ustar plbplb=head1 NAME oarmonitor_graph_gen - Generate graphics from a job resource usage. =head1 SYNOPSIS B [-h] | [-j job_id] =head1 DESCRIPTION This command is an example of graphics that can be generated when the OAR monitoring feture is activated on jobs. =head1 OPTIONS =over =item B<-j, --job_id> jobid Give the job id to get data. =back =head1 COPYRIGHTS Copyright 2003-2012 LIG http://lig.imag.fr This software is licensed under the GNU General Public License Version 2 or above. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ./oar-2.5.2/sources/core/man/man1/oarmonitor.pod0000644000175000017500000000113611757171206017501 0ustar plbplb=head1 NAME oarmonitor - Launch the monitoring of a job. =head1 SYNOPSIS B [-h] [-f seconds] -j jobid =head1 DESCRIPTION This command is used to retrieve data from process on every job nodes. =head1 OPTIONS =over =item B<-j, --job_id> Job id to monitor. =item B<-f, --frequency> Number of seconds between each data collect (default is 60s). =back =head1 COPYRIGHTS Copyright 2003-2012 LIG http://lig.imag.fr This software is licensed under the GNU General Public License Version 2 or above. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ./oar-2.5.2/sources/core/man/man1/oarhold.pod0000644000175000017500000000173311757171206016743 0ustar plbplb=head1 NAME oarhold - hold a job in OAR batch scheduler. =head1 SYNOPSIS B [--array][job_ids][--sql "sql syntax"][-r][-h][-V] =head1 DESCRIPTION Ask OAR to not schedule jobs until oarresume command will be executed. =head1 OPTIONS =over =item B<-r, --running> Manage not only Waiting jobs but also Running one (can suspend the job) =item B<--array> Hold array job(s) passed as parameter (all the sub-jobs of the given array job(s)) =item B<--sql> Hold jobs which repond to the SQL where clause on the table jobs (ex: "project = 'p1'") =item B<-V, --version> Print OAR version number. =item B<-h, --help> Print help command message. oarsub(1), oardel(1) oarstat(1), oarnodes(1), oarresume(1) =back =head1 COPYRIGHTS Copyright 2008 Laboratoire d'Informatique de Grenoble (http://www.liglab.fr). This software is licensed under the GNU Library General Public License. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ./oar-2.5.2/sources/core/man/man1/oardel.pod0000644000175000017500000000243611757171206016562 0ustar plbplb=head1 NAME oardel - delete or checkpoint job(s). =head1 SYNOPSIS B [-c|-b][--array][job_ids][-h][-V] =head1 DESCRIPTION This command is used to delete or checkpoint job(s). Jobs are designed by their number. =head1 OPTIONS =over =item B<-c, --checkpoint> Send the checkpoint signal designed from the "--signal" oarsub command option (default is SIGUSR2) to the process launched by the job "job_id". =item B<-s, --signal> Send the signal given as parameter to the process launched by the job "job_id". =item B<-b, --besteffort> Tag specified jobs as besteffort (or remove it if they are already besteffort) =item B<--array> Delete/checkpoint array job(s) passed as parameter (all the sub-jobs of the given array job(s)) =item B<--sql> Delete/checkpoint jobs which respond to the SQL where clause on the table jobs (ex: "project = 'p1'"). =item B<-V, --version> Print OAR version number. =item B<-h, --help> Print help command message. =back =head1 SEE ALSO oarsub(1), oarstat(1), oarnodes(1), oarhold(1), oarresume(1) =head1 COPYRIGHTS Copyright 2008 Laboratoire d'Informatique de Grenoble (http://www.liglab.fr). This software is licensed under the GNU Library General Public License. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ./oar-2.5.2/sources/core/man/man1/oaradmin.pod.in0000644000175000017500000003112311757171206017506 0ustar plbplb=head1 NAME oaradmin - OAR administration tool. =head1 SYNOPSIS B resources OPTIONS B rules OPTIONS B conf OPTIONS B version =head1 DESCRIPTION This command is used to manage OAR configuration. The command B manages resources in OAR database : add, update and delete many resources in a single command line. The command B manages admission rules : list, add, edit/update, delete, export, enable or disable one or several admission rules. A versioning feature, using Subversion, allows one to keep all admission rules changes. The command B is useful for modify OAR conf files, register changes in a Subversion repository and revert to a conf file at a particular revision if needed. The command B print the version of OAR. Each subcommand has a long and an optional short form : B or B, B or B for examples. =head1 OPTIONS =head2 Options for resources subcommand =over =item B<-a> [--cpusetproperty=property] [B<-p> ] These options can create and define many resources in OAR database. In a single command line, it is possible to create the whole cluster definition. With -a option, form is like : (/property=value)+. Several can be used : /property=value/property=value... Property field correspond to the name of a property in OAR database : cpu, core, switch for example. A specific keyword "node" (or "nodes") is use to indicate nodes names. Order of expressions is important : it describes the resources properties hierarchy in the cluster. Properties used in must exist and must be created with oarproperty command before. '/' caracter is mandatory at the beginning of each expression, no space between two expressions. Ex : oaradmin resources -a /node=mycluster{12}.local/cpu={2}/core={2}. Number in {} is the number of elements for one element of previous level. So in this example 12 nodes, each node has 2 cpus, each cpu has 2 cores. Oaradmin creates following resources : mycluster1.local cpu=1 core=1, mycluster1.local cpu=1 core=2, mycluster1.local cpu=2 core=3, mycluster1.local cpu=2 core=4, mycluster2.local cpu=3 core=5... A second form is useful to name nodes. The expression -a /node=mycluster[1-100].local creates nodes mycluster1.local, mycluster2.local... mycluster100.local. It is possible to specify lists and groups for hosts. Ex : -a /node=mycluster[1-8,10,15,20-30],host_a,host_b/cpu={2}. Of course, in [...] only numeric values are allowed. Expression -a /node=chemistry_cluster[1-10,18],physics_cluster[1-50]/cpu={2} is also possible. For nodes naming, a numeric format and an offset are available. The numeric format is specified with %xd where x is the number of digits. Ex : -a /node=mycluster-{%2d12} will create nodes mycluster-01, mycluster-02... The offset is specified with +x (or +xoffset) where x is the offset value. mycluster{12+20} will create mycluster21 as first node name. Remember that the first node name with mycluster{12} is mycluster1. So the first numeric value is increased by +20. Offset can be negative with -x. Numeric format and offset can be used anywhere in {} expression : {12%3d+20} or {+20offset12%3d}. With [] form in nodes names, offset is useless ; only numeric format can be used in this case. mycluster[1-100] define values of names. mycluster{12} defined at the same time a number of elements and values of names. Some expressions does not match or are not allowed : -a /switch={2}/node=mycluster[1-12]. If an expression uses [], it must be the first expression behind -a. -a /node=cluster_a{12},cluster_b{12}. Only one argument must be used with {} form in each . -a /node=cluster_a{12},cluster_b[1-10]. {} and [] forms are exclusive. --cpusetproperty option is useful to define the property used for numbers cpusets. Ex : -a /node=mycluster{12}/cpu={2}/core={2} --cpusetproperty=cpu means one cpuset per cpu. If the option is not used, one cpuset is created per resource_id. -p option is useful to define other properties that are not in properties hierarchy : memory, project... With -p option form is as follows : property=value. Several expressions -p property=value can be used. -p is mandatory at the beginning of each . Ex : -p mem=1024 -p arch=amd_64 -p infiniband=YES. An operator {%} is available to increment by 1 a property value. Ex : -p ip=192.168.1.{%} Witout -c option, oaradmin print only oarnodesetting commands. No changes are made in OAR database. Use -c option to execute commands and commit changes. Other examples : oaradmin resources -a /switch=sw{5}/node=mycluster{12}.domain/cpu={2} Creates a cluster with 5 switchs, 12 nodes per switch, 2 cpus per node. oaradmin resources -a /node=mycluster-{%3d250}/cpu={2} oaradmin resources -a /node=mycluster-[%3d1-250]/cpu={2} Creates nodes from mycluster-001 to mycluster-250 with 2 cpus per node. oaradmin resources -a /node=mycluster[1-25]/cpu={2} -p mem=1024 -p arch=amd_64 -p infiniband=YES -c =item B<--auto-offset> This option automatically computes the node, cpu, and core offsets for you based on what is already registered in the OAR database. Node names must be of the form - for oaradmin to automatically find the good . =item B<-s> B<-p> These options update several resources in OAR database. -s defines the condition to select resources and -p defines the data to update on the selected resources. The form is : property=value. As add feature, property field corresponds to the name of a property in OAR database already created with oarproperty command. With -s option, only one expression can be specified. The boolean operators AND and OR cannot be used. With -p option, several expressions can be used. When digits are used in host names, the expression -s node=mycluster[%3d1-8] must be used instead of -s node=mycluster[001-008]. It is possible to group resources with {x} and {%} operators. {x} defines a group of x elements or means "each x elements". {%} means an increment of 1 to 1. Ex : oaradmin resources -s core={4} -p cpu={%}. Creates one cpu each 4 cores. {x} is allowed only in the selected expression. {%} is allowed only with -p option. Offset can be used in this form : oaradmin resources -s core={4+56} -p cpu={%+56}. Witout -c option, oaradmin prints only oarnodesetting commands. No changes are made in OAR database. Use -c option to execute commands and commit changes. Other examples : oaradmin resources -s node=mycluster-[%3d1-100] -p infiniband=YES -p project=chemistry Update infiniband and project properties on nodes mycluster-001, mycluster-002... oaradmin resources -s v_node={4} -p p_node={%} Create one physical node (p_node property) every each 4 virtual nodes (v_node property). =item B<-d> [] Delete many resources in OAR database. -d alone delete all resources. -d delete selected resources by expression. It's form is property=value. As -s option, boolean operators does not match and only one expression can be specified. Examples : oaradmin resources -d -c oaradmin resources -d node=mycluster[1-100] -c =item B<-c> Execute commands specified with -a, -s -p... and commit changes in OAR database. =item B<-h, --help> Print help message. =back =head2 Options for rules subcommand =over =item B<-l, --list> | B<-ll> | B<-lll> [rule_ids] The recommended format is as follows. The first line commented with # is the title of the rule. The following lines also commented contains the description or algorithm of the rule. The following lines contains the content of the Perl script. This format is not mandatory. Admission rules can contains only Perl script lines without comments. The -l option prints only the title line. The -ll option prints title and description lines and -lll prints the entire contents. If no rules numbers given, it prints all admission rules. Ex : oaradmin rules -l oaradmin rules -lll 3 5 8 =item B<-a, --add> [rule_id] [-f file] Add an admission rule with a text editor or directly from a file. $EDITOR variable environment is used to launch the preferred text editor. If not defined, vi is used. Without rule_id, the new rule is added at the end of the admission rules table in OAR database. If rule_id is defined, the new rule is inserted at the rule_id position. The numbers above or equals to rule_id are increased by 1. This is useful to group the rules that have the same type of treatment. The filename must be specified just after -f option. Ex : oaradmin rules -a oaradmin rules -a 10 oaradmin rules -a -f my_file =item B<-d, --delete> rule_id [rule_ids] Delete one or more admission rules. Ex : oaradmin rules -d 2 oaradmin rules -d 2 3 4 =item B<-x, --export> [rule_ids] | B<-x, --export> rule_id -f file Export admission rules into files. The default filename used for export is admission_rule_ and these files are created in the current directory. The -x option alone exports all admission rules. With -f file option, only one rule can be exported. If files already exists, oaradmin asks confirmation to overwrite. Ex : oaradmin rules -x oaradmin rules -x 1 5 18 oaradmin rules -x 3 -f my_file oaradmin rules -x 18 -f /my_directory/my_file =item B<-e, --edit> rule_id [-f file] Edit the rule_id admission rule using a text editor. A temporary file is used to edit the admission rule. This file is stored in the directory specified by OAR_RUNTIME_DIRECTORY in the OAR configuration file. If this parameter is not defined, /tmp is used. Oaradmin asks user to commit in database only if changes are made in the rule. Editing differents admission rules at the same time, into two consoles is possible. Editing the same admission rule into two consoles at the same time is also possible. But careful, in this case, depending on manipulations some data can be lost. Like add feature, $EDITOR variable environment can specify a text editor. If a filename is specified whith -f option, oaradmin directly update the rule_id admission rule in database without editing. Ex : oaradmin rules -e 3 oaradmin rules -e 3 -f my_file =item B<-1, --enable> rule_id Enable the rule_id admission rule. The # comments character at the beginning of each line is deleted. So the admission rule is enabled and evaluated in oarsub command. Ex : oaradmin rules -1 10 =item B<-0, --disable> rule_id Disable the rule_id admission rule. The # comments character is added at the beginning of each line of the rule. So the admission rule is not evaluated and has no effect in oarsub command. Ex : oaradmin rules -0 10 =item B<-H, --history> rule_id [-n number] Show all changes made on the admission rule rule_id. Output is displayed in unified diff format. With -n number option, only the #number latest changes are displayed. Versioning feature can be activated with the parameter OARADMIN_VERSIONING in the OAR conf file. The repository is created when a change is made in an admission rule. Ex : oaradmin rules -H 10 =item B<-R, --revert> rule_id rev Revert to the admission rule rule_id as it existed in #rev revision. Ex : oaradmin rules -R 10 3 =item B<-h, --help> Print help message. =back =head2 Options for conf subcommand =over =item B<-e, --edit> conf_file Edit the file conf_file using a text editor. $EDITOR variable environment is used to launch the preferred text editor. If $EDITOR is not defined, vi is used. If the versioning feature is enabled, after editing the file, changes are registered in a Subversion repository. Versioning feature can be activated with the parameter OARADMIN_VERSIONING in the OAR conf file. The repository is created when a change is made in the file. Ex : oaradmin conf -e %%OARCONFDIR%%/oar.conf =item B<-H, --history> conf_file [-n number] Show all changes made on the file conf_file. Output is displayed in unified diff format. With -n number option, only the #number latest changes are displayed. Ex : oaradmin conf -H oar.conf =item B<-R, --revert> conf_file rev Revert to the file conf_file as it existed in #rev revision. Ex : oaradmin conf -R %%OARCONFDIR%%/oar.conf 3 =item B<-h, --help> Print help message. =back =head1 NOTES Oaradmin resources subcommand uses oarnodes outputs with yaml format. So, Perl yaml and Ruby yaml packages must be installed. =head1 SEE ALSO oarnodes(1), oarnodesetting(1), oarproperty(1), oarremoveresource(1), oarsub(1) =head1 COPYRIGHTS Copyright 2003 Laboratoire Id-Imag (http://www-id.imag.fr). This software is licensed under the GNU Library General Public License. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ./oar-2.5.2/sources/core/man/man1/oaraccounting.pod0000644000175000017500000000136211757171206020145 0ustar plbplb=head1 NAME oaraccounting - Fill the database with the summary of the resource usage. =head1 SYNOPSIS B [-h] [--reinitialize | --delete_before] =head1 DESCRIPTION This command is used to summarize information about the jobs. Hence this can be used by the scheduler with the faire sharing algorithm. =head1 OPTIONS =over =item B<--reinitialize> Delete everything and recheck every jobs and feed the table. =item B<--delete-before=number_of_seconds> Delete every records number_of_seconds ago. =back =head1 COPYRIGHTS Copyright 2003-2012 LIG http://lig.imag.fr This software is licensed under the GNU General Public License Version 2 or above. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ./oar-2.5.2/sources/core/man/man1/oar_resources_init.pod0000644000175000017500000000117211757171206021206 0ustar plbplb=encoding UTF-8 =head1 NAME oar_resource_init - Help to define the resources provided by a given set of nodes. =head1 SYNOPSIS B I =head1 DESCRIPTION This script take a list of hosts from a file given in the parameter and write the OAR commands to execute to add the corresponding resources in the oar database to the file /tmp/oar_resources_init.txt The must contain one host per line and the corresponding hosts must be configured to allow OAR to access by ssh. =head1 OPTIONS =over =item B The path of the file containing the host list. =back =cut ./oar-2.5.2/sources/core/man/man1/oar_psql_db_init.pod0000644000175000017500000000065311757171206020623 0ustar plbplb=head1 NAME oar_psql_server_init - Initialize the OAR Postgresql database. =head1 SYNOPSIS B =head1 DESCRIPTION This command initialize the OAR Postgresql database. =head1 COPYRIGHTS Copyright 2003-2012 LIG http://lig.imag.fr This software is licensed under the GNU General Public License Version 2 or above. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ./oar-2.5.2/sources/core/man/man1/oar_phoenix.pod.in0000644000175000017500000000056611757171206020236 0ustar plbplb=encoding UTF-8 =head1 NAME oar_phoenix - Check and automatically reboot Suspected nodes. =head1 SYNOPSIS B =head1 DESCRIPTION This script check and automatically reboot Suspected nodes. This script is intended to be started periodically from cron. =head1 EXAMPLES You can add into the crontab : */10 * * * * root %%SBINDIR%%/oar_phoenix =cut ./oar-2.5.2/sources/core/man/man1/oar_mysql_db_init.pod0000644000175000017500000000056011757171206021006 0ustar plbplb=head1 NAME Almighty - OAR server daemon. =head1 SYNOPSIS B =head1 DESCRIPTION This command is the OAR server daemon. =head1 COPYRIGHTS Copyright 2003-2012 LIG http://lig.imag.fr This software is licensed under the GNU General Public License Version 2 or above. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ./oar-2.5.2/sources/core/man/man1/oar_checkdb.pod0000644000175000017500000000032711757171206017535 0ustar plbplb=encoding UTF-8 =head1 NAME oar_checkdb - Check the database connection =head1 SYNOPSIS B =head1 DESCRIPTION Check the database connection. Return 0 if it is ok, an exit code >0 otherwise. =cut ./oar-2.5.2/sources/core/man/man1/oar-server.pod0000644000175000017500000000031511757171206017373 0ustar plbplb=encoding UTF-8 =head1 NAME oar-server - daemon wrapper to start the oar server (Almighty) =head1 SYNOPSIS B =head1 DESCRIPTION Start the oar server (Almighty) as a daemon process =cut ./oar-2.5.2/sources/core/man/man1/oar-database.pod.in0000644000175000017500000001246511757171206020247 0ustar plbplb=head1 NAME oar-database - create/initialize/upgrade/reset/drop the oar database =head1 SYNOPSIS B I<--create> [I] B I<--drop> [I] B I<--setup> [I] B I<--reset> [I] =head1 DESCRIPTION Manage the oar database. =over =item B<--setup> Initialize/Upgrade an existing database. =item B<--reset> Reset an existing database. =item B<--create> Create and initialize a new database/user. =item B<--drop> Drop an existing database/user. =back =head1 OPTIONS =head2 General parameters =over =item B<--conf>=I Define the oar configuration file to use. By default I<%%OARCONFDIR%%/oar.conf> is used. This file doesn't exist, the default parameters for each value are used. =item B<--update-conf> The database parameters given in the command line are writen to the OAR_CONF_FILE =item B<-h,--help> Display this help. =item B<-d,--debug> Display more information during the script execution =item B<-f,--force-sql> Force to resume the execution even if an sql instruction fails =item B<-y,--force-yes> This option will cause oar-database to continue without prompting if it is doins something potentially harmful =back =head2 Database admin parameters These parameters are only needed for database/user creation or removing. =over =item B<--db-is-local> For B<--create> or B<--drop>, this option tells that the database is local. oar-database can use local admin account to execute command (useful for postgres). =item B<--db-admin-user>=I For B<--create> or B<--drop>, this option gives the privilegied user to use. =item B<--db-admin-pass>=I For B<--create> or B<--drop>, this option gives the privilegied user pass to use. =back =head2 SQL parameters By default, if not specified, all the sql parameters are taken from the I. It is preferable to set these values directly to this file. =over =item B<--db-type>=I The type of the SQL database. It can be currently, I or I (for postgresql). =item B<--db-user>=I Connect to the database as the user DB_USER instead of the one given in OAR_CONF_FILE. By default, if OAR_CONF_FILE doesn't specify a user, it is I<%%OARUSER%%>. =item B<--db-pass>=I Connect to the database with the password DB_PASS instead of the one given in OAR_CONF_FILE. =item B<--db-host>=I Connect to the database on the host DB_HOST, By default, if OAR_CONF_FILE doesn't specify a host, it is I. =item B<--db-port>=I Connect to the database on the port DB_PORT, By default, if OAR_CONF_FILE doesn't specify a port, the value depend on the DB_TYPE. if DB_TYPE is I, DB_PORT is I<3306>. If DB_TYPE is I, DB_PORT is I<5432>. =item B<--db-name>=I Connect to the database on the host DB_HOST, By default, if OAR_CONF_FILE doesn't specify a database name, it is I. =item B<--db-ro-user>=I same as B<--db-user> except that it is for the read only access. =item B<--db-ro-pass>=I same as B<--db-pass> except that it is for the read only access. =back =head1 EXAMPLES =head2 Mysql First you need to specify the sql parameters in %%OARCONFDIR%%/oar.conf. These parameters will be used by B. To create a new database (assuming that the sql root password is B): B To upgrade an existing database: B =head2 Postgresql First you need to specify the sql parameters in %%OARCONFDIR%%/oar.conf. These parameters will be used by B. Then if your postgresql database is on the local system, you can use the option B<--db-is-local> (otherwise, the procedure is the same as Mysql). So B will use the postgres unix user to administrate the database (you need privilegied access to the local system). To create a new database: B To upgrade an existing database: B =head2 Advanced usages To make some tests or to administrate several databases, you can also specify the sql parameters on the command line. For example: B will create the oar database and the oar user on the postgresql system hosted by B. The user B must have the right to create new databases and new roles on this system. =head1 FILES =over =item %%OARDIR%%/database/mysql_structure.sql, %%OARDIR%%/database/pg_structure.sql SQL instructions for creating the structure of the oar database. =item %%OARDIR%%/database/mysql_default_admission_rules.sql, %%OARDIR%%/database/pg_default_admission_rules.sql SQL instructions for inserting the default admission rules in the oar database. =item %%OARDIR%%/database/default_data.sql SQL instructions for inserting the default data in the oar database. =item %%OARDIR%%/database/mysql_reset_structure.sql, %%OARDIR%%/database/pg_reset_structure.sql SQL instruction for emptying an existing oar database. =item %%OARDIR%%/database/mysql_structure_upgrade_*.sql, %%OARDIR%%/database/pg_structure_upgrade_*.sql SQL instructions for upgrading an existing database. =back =cut ./oar-2.5.2/sources/core/man/man1/Almighty.pod0000644000175000017500000000056011757171206017066 0ustar plbplb=head1 NAME Almighty - OAR server daemon. =head1 SYNOPSIS B =head1 DESCRIPTION This command is the OAR server daemon. =head1 COPYRIGHTS Copyright 2003-2012 LIG http://lig.imag.fr This software is licensed under the GNU General Public License Version 2 or above. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ./oar-2.5.2/sources/core/man/README0000644000175000017500000000014211757171206014624 0ustar plbplbPlease run `make man' in the parent directory to generate the man page from the pod files. $Id$ ./oar-2.5.2/sources/core/database/0000755000175000017500000000000011757171206014740 5ustar plbplb./oar-2.5.2/sources/core/database/pg_structure_upgrade_2.5.0-2.5.2.sql0000644000175000017500000000026311757171206023103 0ustar plbplb ALTER TABLE jobs ADD array_index INTEGER NOT NULL default '1'; -- Update the database schema version DELETE FROM schema; INSERT INTO schema(version, name) VALUES ('2.5.2', ''); ./oar-2.5.2/sources/core/database/pg_structure_upgrade_2.4.0-2.5.0.sql0000644000175000017500000000040111757171206023072 0ustar plbplb-- Gantt output time optimization CREATE INDEX date_start ON resource_logs (date_start); CREATE INDEX date_stop ON resource_logs (date_stop); -- Update the database schema version DELETE FROM schema; INSERT INTO schema(version, name) VALUES ('2.5.0', ''); ./oar-2.5.2/sources/core/database/pg_structure_upgrade_2.3.3-2.4.0.sql0000644000175000017500000000110411757171206023074 0ustar plbplbalter table resources rename column cm_availability to available_upto; alter table resources add last_available_upto integer NOT NULL default '0'; delete from schema; alter table schema add name VARCHAR( 255 ) NOT NULL; INSERT INTO schema(version, name) VALUES ('2.4.0', 'Thriller'); CREATE TABLE scheduler ( name VARCHAR(100) NOT NULL, script VARCHAR(100) NOT NULL, description VARCHAR(255) NOT NULL, PRIMARY KEY (name) ); CREATE INDEX job_array_id ON jobs(array_id); CREATE INDEX event_job_id ON event_logs (job_id); CREATE INDEX challenge_job_id ON challenges(job_id); ./oar-2.5.2/sources/core/database/pg_structure_upgrade_2.3.1-2.3.3.sql0000644000175000017500000000020611757171206023076 0ustar plbplbALTER TABLE jobs ADD array_id INTEGER NOT NULL default '0'; UPDATE jobs SET array_id = job_id ; INSERT INTO schema VALUES ('2.3.3'); ./oar-2.5.2/sources/core/database/pg_structure_upgrade_2.3.0+svn1369.sql0000644000175000017500000000121711757171206023564 0ustar plbplb-- $Id$ -- info version, change here if you have updated the db schema DROP TABLE schema; CREATE TABLE schema ( version VARCHAR( 255 ) NOT NULL ); INSERT INTO schema VALUES ('2.3.0+svn1369'); CREATE TABLE gantt_jobs_predictions_log ( sched_date integer NOT NULL default '0', moldable_job_id integer NOT NULL default '0', start_time integer NOT NULL default '0', PRIMARY KEY (sched_date, moldable_job_id) ); CREATE TABLE gantt_jobs_resources_log ( sched_date integer NOT NULL default '0', moldable_job_id integer NOT NULL default '0', resource_id integer NOT NULL default '0', PRIMARY KEY (sched_date, moldable_job_id,resource_id) ); ./oar-2.5.2/sources/core/database/pg_structure.sql0000644000175000017500000002665511757171206020225 0ustar plbplb-- $Id$ -- info version, change here if you have updated the db schema CREATE TABLE schema ( version VARCHAR( 255 ) NOT NULL, name VARCHAR( 255 ) NOT NULL ); INSERT INTO schema VALUES ('2.5.0',''); CREATE TABLE accounting ( window_start integer NOT NULL , window_stop integer NOT NULL DEFAULT '0', accounting_user varchar(255) NOT NULL default '', accounting_project varchar(255) NOT NULL default '', queue_name varchar(100) NOT NULL default '', consumption_type varchar(5) check (consumption_type in ('ASKED','USED')) NOT NULL default 'ASKED', consumption integer NOT NULL default '0', PRIMARY KEY (window_start,window_stop,accounting_user,accounting_project,queue_name,consumption_type) ); CREATE INDEX accounting_user ON accounting (accounting_user); CREATE INDEX accounting_project ON accounting (accounting_project); CREATE INDEX accounting_queue ON accounting (queue_name); CREATE INDEX accounting_type ON accounting (consumption_type); CREATE TABLE admission_rules ( id bigserial, rule text NOT NULL, PRIMARY KEY (id) ); CREATE TABLE assigned_resources ( moldable_job_id integer NOT NULL default '0', resource_id integer NOT NULL default '0', assigned_resource_index varchar(7) check (assigned_resource_index in ('CURRENT','LOG')) NOT NULL default 'CURRENT', PRIMARY KEY (moldable_job_id,resource_id) ); CREATE INDEX mjob_id ON assigned_resources (moldable_job_id); CREATE INDEX log ON assigned_resources (assigned_resource_index); CREATE TABLE challenges ( job_id integer NOT NULL default '0', challenge varchar(255) NOT NULL default '', ssh_private_key text NOT NULL default '' , ssh_public_key text NOT NULL default '' , PRIMARY KEY (job_id) ); CREATE INDEX challenge_job_id ON challenges (job_id); CREATE TABLE event_log_hostnames ( event_id integer NOT NULL default '0', hostname varchar(255) NOT NULL default '', PRIMARY KEY (event_id,hostname) ); CREATE INDEX event_hostname ON event_log_hostnames (hostname); CREATE TABLE event_logs ( event_id bigserial, type varchar(50) NOT NULL default '', job_id integer NOT NULL default '0', date integer NOT NULL default '0', description varchar(255) NOT NULL default '', to_check varchar(3) check (to_check in ('YES','NO')) NOT NULL default 'YES', PRIMARY KEY (event_id) ); CREATE INDEX event_type ON event_logs (type); CREATE INDEX event_check ON event_logs (to_check); CREATE INDEX event_job_id ON event_logs (job_id); CREATE TABLE files ( file_id bigserial, md5sum varchar(255) default NULL, location varchar(255) default NULL, method varchar(255) default NULL, compression varchar(255) default NULL, size integer NOT NULL default '0', PRIMARY KEY (file_id) ); CREATE INDEX md5sum ON files (md5sum); CREATE TABLE frag_jobs ( frag_id_job integer NOT NULL default '0', frag_date integer NOT NULL default '0', frag_state varchar(16) check (frag_state in ('LEON','TIMER_ARMED','LEON_EXTERMINATE','FRAGGED')) NOT NULL default 'LEON', PRIMARY KEY (frag_id_job) ); CREATE INDEX frag_state ON frag_jobs (frag_state); CREATE TABLE gantt_jobs_predictions ( moldable_job_id integer NOT NULL default '0', start_time integer NOT NULL default '0', PRIMARY KEY (moldable_job_id) ); CREATE TABLE gantt_jobs_predictions_visu ( moldable_job_id integer NOT NULL default '0', start_time integer NOT NULL default '0', PRIMARY KEY (moldable_job_id) ); CREATE TABLE gantt_jobs_predictions_log ( sched_date integer NOT NULL default '0', moldable_job_id integer NOT NULL default '0', start_time integer NOT NULL default '0', PRIMARY KEY (sched_date, moldable_job_id) ); CREATE TABLE gantt_jobs_resources ( moldable_job_id integer NOT NULL default '0', resource_id integer NOT NULL default '0', PRIMARY KEY (moldable_job_id,resource_id) ); CREATE TABLE gantt_jobs_resources_visu ( moldable_job_id integer NOT NULL default '0', resource_id integer NOT NULL default '0', PRIMARY KEY (moldable_job_id,resource_id) ); CREATE TABLE gantt_jobs_resources_log ( sched_date integer NOT NULL default '0', moldable_job_id integer NOT NULL default '0', resource_id integer NOT NULL default '0', PRIMARY KEY (sched_date, moldable_job_id,resource_id) ); CREATE TABLE job_dependencies ( job_id integer NOT NULL default '0', job_id_required integer NOT NULL default '0', job_dependency_index varchar(7) check (job_dependency_index in ('CURRENT','LOG')) NOT NULL default 'CURRENT', PRIMARY KEY (job_id,job_id_required) ); CREATE INDEX id_dep ON job_dependencies (job_id); CREATE INDEX log_dep ON job_dependencies (job_dependency_index); CREATE TABLE job_resource_groups ( res_group_id bigserial, res_group_moldable_id integer NOT NULL default '0', res_group_property text, res_group_index varchar(7) check (res_group_index in ('CURRENT','LOG')) NOT NULL default 'CURRENT', PRIMARY KEY (res_group_id) ); CREATE INDEX moldable_job ON job_resource_groups (res_group_moldable_id); CREATE INDEX log_res ON job_resource_groups (res_group_index); CREATE TABLE job_resource_descriptions ( res_job_group_id integer NOT NULL default '0', res_job_resource_type varchar(255) NOT NULL default '', res_job_value integer NOT NULL default '0', res_job_order integer NOT NULL default '0', res_job_index varchar(7) check (res_job_index in ('CURRENT','LOG')) NOT NULL default 'CURRENT', PRIMARY KEY (res_job_group_id,res_job_resource_type,res_job_order) ); CREATE INDEX resgroup ON job_resource_descriptions (res_job_group_id); CREATE INDEX log_res_desc ON job_resource_descriptions (res_job_index); CREATE TABLE job_state_logs ( job_state_log_id bigserial, job_id integer NOT NULL default '0', job_state varchar(16) check (job_state in ('Waiting','Hold','toLaunch','toError','toAckReservation','Launching','Running','Suspended','Resuming','Finishing','Terminated','Error')) NOT NULL default 'Waiting', date_start integer NOT NULL default '0', date_stop integer NOT NULL default '0', PRIMARY KEY (job_state_log_id) ); CREATE INDEX id_job_log ON job_state_logs (job_id); CREATE INDEX state_job_log ON job_state_logs (job_state); CREATE TABLE job_types ( job_type_id bigserial, job_id integer NOT NULL default '0', type varchar(255) NOT NULL default '', types_index varchar(7) check (types_index in ('CURRENT','LOG')) NOT NULL default 'CURRENT', PRIMARY KEY (job_type_id) ); CREATE INDEX log_types ON job_types (types_index); CREATE INDEX type ON job_types (type); CREATE INDEX id_types ON job_types (job_id); CREATE TABLE jobs ( job_id bigserial, array_id integer NOT NULL default '0', array_index integer NOT NULL default '1', initial_request text, job_name varchar(100) , job_env text , job_type varchar(11) check (job_type in ('INTERACTIVE','PASSIVE')) NOT NULL default 'PASSIVE', info_type varchar(255) default NULL, state varchar(16) check (state in ('Waiting','Hold','toLaunch','toError','toAckReservation','Launching','Running','Suspended','Resuming','Finishing','Terminated','Error')) NOT NULL default 'Waiting', reservation varchar(10) check (reservation in ('None','toSchedule','Scheduled')) NOT NULL default 'None', message varchar(255) NOT NULL default '', scheduler_info varchar(255) NOT NULL default '', job_user varchar(255) NOT NULL default '', project varchar(255) NOT NULL default '', job_group varchar(255) NOT NULL default '', command text, exit_code integer default NULL, queue_name varchar(100) NOT NULL default '', properties text, launching_directory text NOT NULL , submission_time integer NOT NULL default '0', start_time integer NOT NULL default '0', stop_time integer NOT NULL default '0', file_id integer default NULL, accounted varchar(3) check (accounted in ('YES','NO')) NOT NULL default 'NO', notify varchar(255) default NULL, assigned_moldable_job integer default '0', checkpoint integer NOT NULL default '0', checkpoint_signal integer NOT NULL, stdout_file text , stderr_file text , resubmit_job_id integer NOT NULL default '0', suspended varchar(3) check (suspended in ('YES','NO')) NOT NULL default 'NO', PRIMARY KEY (job_id) ); CREATE INDEX state ON jobs (state); CREATE INDEX state_id ON jobs (state,job_id); CREATE INDEX reservation ON jobs (reservation); CREATE INDEX queue_name ON jobs (queue_name); CREATE INDEX accounted ON jobs (accounted); CREATE INDEX suspended ON jobs (suspended); CREATE INDEX job_array_id ON jobs (array_id); CREATE TABLE moldable_job_descriptions ( moldable_id bigserial, moldable_job_id integer NOT NULL default '0', moldable_walltime integer NOT NULL default '0', moldable_index varchar(7) check (moldable_index in ('CURRENT','LOG')) NOT NULL default 'CURRENT', PRIMARY KEY (moldable_id) ); CREATE INDEX job_mold ON moldable_job_descriptions (moldable_job_id); CREATE INDEX log_mold_desc ON moldable_job_descriptions (moldable_index); CREATE TABLE queues ( queue_name varchar(100) NOT NULL default '', priority integer NOT NULL default '0', scheduler_policy varchar(100) NOT NULL default '', state varchar(9) check (state in ('Active','notActive')) NOT NULL default 'Active', PRIMARY KEY (queue_name) ); CREATE TABLE scheduler ( name VARCHAR(100) NOT NULL, script VARCHAR(100) NOT NULL, description VARCHAR(255) NOT NULL, PRIMARY KEY (name) ); CREATE TABLE resource_logs ( resource_log_id bigserial, resource_id integer NOT NULL default '0', attribute varchar(255) NOT NULL default '', value varchar(255) NOT NULL default '', date_start integer NOT NULL default '0', date_stop integer NOT NULL default '0', finaud_decision varchar(3) check (finaud_decision in ('YES','NO')) NOT NULL default 'NO', PRIMARY KEY (resource_log_id) ); CREATE INDEX resource ON resource_logs (resource_id); CREATE INDEX attribute ON resource_logs (attribute); CREATE INDEX resource_id ON resource_logs (resource_id); CREATE INDEX finaud ON resource_logs (finaud_decision); CREATE INDEX val ON resource_logs (value); CREATE INDEX date_start ON resource_logs (date_start); CREATE INDEX date_stop ON resource_logs (date_stop); CREATE TABLE resources ( resource_id bigserial, type varchar(100) NOT NULL default 'default', network_address varchar(100) NOT NULL default '', state varchar(9) check (state in ('Alive','Dead','Suspected','Absent')) NOT NULL default 'Alive', next_state varchar(9) check (next_state in ('UnChanged','Alive','Dead','Absent','Suspected')) NOT NULL default 'UnChanged', finaud_decision varchar(3) check (finaud_decision in ('YES','NO')) NOT NULL default 'NO', next_finaud_decision varchar(3) check (next_finaud_decision in ('YES','NO')) NOT NULL default 'NO', state_num integer NOT NULL default '0', suspended_jobs varchar(3) check (suspended_jobs in ('YES','NO')) NOT NULL default 'NO', scheduler_priority integer NOT NULL default '0', cpuset integer NOT NULL default '0', besteffort varchar(3) check (besteffort in ('YES','NO')) NOT NULL default 'YES', deploy varchar(3) check (deploy in ('YES','NO')) NOT NULL default 'NO', expiry_date integer NOT NULL default '0', desktop_computing varchar(3) check (desktop_computing in ('YES','NO')) NOT NULL default 'NO', last_job_date integer NOT NULL default '0', available_upto integer NOT NULL default '2147483647', last_available_upto integer NOT NULL default '0', PRIMARY KEY (resource_id) ); CREATE INDEX resource_state ON resources (state); CREATE INDEX resource_next_state ON resources (next_state); CREATE INDEX resource_suspended_jobs ON resources (suspended_jobs); CREATE INDEX resource_type ON resources (type); CREATE INDEX resource_network_address ON resources (network_address); ./oar-2.5.2/sources/core/database/pg_reset_structure.sql0000644000175000017500000000134011757171206021407 0ustar plbplb DROP TABLE schema; DROP TABLE jobs; DROP TABLE job_types; DROP TABLE challenges; DROP TABLE moldable_job_descriptions; DROP TABLE job_resource_groups; DROP TABLE job_resource_descriptions; DROP TABLE job_state_logs; DROP TABLE frag_jobs; DROP TABLE assigned_resources; DROP TABLE resources; DROP TABLE resource_logs; DROP TABLE queues; DROP TABLE scheduler; DROP TABLE admission_rules; DROP TABLE gantt_jobs_predictions; DROP TABLE gantt_jobs_predictions_visu; DROP TABLE gantt_jobs_predictions_log; DROP TABLE gantt_jobs_resources; DROP TABLE gantt_jobs_resources_visu; DROP TABLE gantt_jobs_resources_log; DROP TABLE files; DROP TABLE event_logs; DROP TABLE event_log_hostnames; DROP TABLE accounting; DROP TABLE job_dependencies; ./oar-2.5.2/sources/core/database/pg_default_admission_rules.sql0000644000175000017500000002002111757171206023046 0ustar plbplb-- Default admission rules for OAR 2 -- $Id$ DROP TABLE admission_rules; CREATE TABLE admission_rules ( id bigserial, rule text NOT NULL, PRIMARY KEY (id) ); -- Default admission rules -- Specify the default value for queue parameter INSERT INTO admission_rules (rule) VALUES (E'if (not defined($queue_name)) {$queue_name="default";}'); -- Prevent root and oar to submit jobs. INSERT INTO admission_rules (rule) VALUES (E'die ("[ADMISSION RULE] root and oar users are not allowed to submit jobs.\\n") if ( $user eq "root" or $user eq "oar" );'); -- Avoid users except admin to go in the admin queue INSERT INTO admission_rules (rule) VALUES (E' my $admin_group = "admin"; if ($queue_name eq "admin") { my $members; (undef,undef,undef, $members) = getgrnam($admin_group); my %h = map { $_ => 1 } split(/\\s+/,$members); if ( $h{$user} ne 1 ) { {die("[ADMISSION RULE] Only member of the group ".$admin_group." can submit jobs in the admin queue\\n");} } } '); -- Prevent the use of system properties INSERT INTO admission_rules (rule) VALUES (E' my @bad_resources = ("type","state","next_state","finaud_decision","next_finaud_decision","state_num","suspended_jobs","besteffort","deploy","expiry_date","desktop_computing","last_job_date","available_upto","scheduler_priority"); foreach my $mold (@{$ref_resource_list}){ foreach my $r (@{$mold->[0]}){ my $i = 0; while (($i <= $#{$r->{resources}})){ if (grep(/^$r->{resources}->[$i]->{resource}$/i, @bad_resources)){ die("[ADMISSION RULE] \'$r->{resources}->[$i]->{resource}\' resource is not allowed\\n"); } $i++; } } } '); -- Force besteffort jobs to run in the besteffort queue -- Force job of the besteffort queue to be of the besteffort type -- Force besteffort jobs to run on nodes with the besteffort property INSERT INTO admission_rules (rule) VALUES (E' if (grep(/^besteffort$/, @{$type_list}) and not $queue_name eq "besteffort"){ $queue_name = "besteffort"; print("[ADMISSION RULE] Automatically redirect in the besteffort queue\\n"); } if ($queue_name eq "besteffort" and not grep(/^besteffort$/, @{$type_list})) { push(@{$type_list},"besteffort"); print("[ADMISSION RULE] Automatically add the besteffort type\\n"); } if (grep(/^besteffort$/, @{$type_list})){ if ($jobproperties ne ""){ $jobproperties = "($jobproperties) AND besteffort = ''YES''"; }else{ $jobproperties = "besteffort = ''YES''"; } print("[ADMISSION RULE] Automatically add the besteffort constraint on the resources\\n"); } '); -- Verify if besteffort jobs are not reservations INSERT INTO admission_rules (rule) VALUES (E' if ((grep(/^besteffort$/, @{$type_list})) and ($reservationField ne "None")){ die("[ADMISSION RULE] Error: a job cannot both be of type besteffort and be a reservation.\\n"); } '); -- Force deploy jobs to go on resources with the deploy property INSERT INTO admission_rules (rule) VALUES (E' if (grep(/^deploy$/, @{$type_list})){ if ($jobproperties ne ""){ $jobproperties = "($jobproperties) AND deploy = ''YES''"; }else{ $jobproperties = "deploy = ''YES''"; } } '); -- Prevent deploy and allow_classic_ssh type jobs on none entire nodes INSERT INTO admission_rules (rule) VALUES (E' my @bad_resources = ("core","cpu","resource_id",); if (grep(/^(deploy|allow_classic_ssh)$/, @{$type_list})){ foreach my $mold (@{$ref_resource_list}){ foreach my $r (@{$mold->[0]}){ my $i = 0; while (($i <= $#{$r->{resources}})){ if (grep(/^$r->{resources}->[$i]->{resource}$/i, @bad_resources)){ die("[ADMISSION RULE] \'$r->{resources}->[$i]->{resource}\' resource is not allowed with a deploy or allow_classic_ssh type job\\n"); } $i++; } } } } '); -- Force desktop_computing jobs to go on nodes with the desktop_computing property INSERT INTO admission_rules (rule) VALUES (E' if (grep(/^desktop_computing$/, @{$type_list})){ print("[ADMISSION RULE] Added automatically desktop_computing resource constraints\\n"); if ($jobproperties ne ""){ $jobproperties = "($jobproperties) AND desktop_computing = ''YES''"; }else{ $jobproperties = "desktop_computing = ''YES''"; } }else{ if ($jobproperties ne ""){ $jobproperties = "($jobproperties) AND desktop_computing = ''NO''"; }else{ $jobproperties = "desktop_computing = ''NO''"; } } '); -- Limit the number of reservations that a user can do. -- (overrided on user basis using the file: ~oar/unlimited_reservation.users) INSERT INTO admission_rules (rule) VALUES (E' if ($reservationField eq "toSchedule") { my $unlimited=0; if (open(FILE, "< $ENV{HOME}/unlimited_reservation.users")) { while (){ if (m/^\\s*$user\\s*$/m){ $unlimited=1; } } close(FILE); } if ($unlimited > 0) { print("[ADMISSION RULE] $user is granted the privilege to do unlimited reservations\\n"); } else { my $max_nb_resa = 2; my $nb_resa = $dbh->do(" SELECT job_id FROM jobs WHERE job_user = ''$user'' AND (reservation = ''toSchedule'' OR reservation = ''Scheduled'') AND (state = ''Waiting'' OR state = ''Hold'') "); if ($nb_resa >= $max_nb_resa){ die("[ADMISSION RULE] Error : you cannot have more than $max_nb_resa waiting reservations.\\n"); } } } '); -- How to perform actions if the user name is in a file --INSERT INTO admission_rules (rule) VALUES (E' --open(FILE, "/tmp/users.txt"); --while (($queue_name ne "admin") and ($_ = )){ -- if ($_ =~ m/^\\s*$user\\s*$/m){ -- print("[ADMISSION RULE] Change assigned queue into admin\\n"); -- $queue_name = "admin"; -- } --} --close(FILE); --'); -- Limit walltime for interactive jobs INSERT INTO admission_rules (rule) VALUES (E' my $max_walltime = OAR::IO::sql_to_duration("12:00:00"); if (($jobType eq "INTERACTIVE") and ($reservationField eq "None")){ foreach my $mold (@{$ref_resource_list}){ if ((defined($mold->[1])) and ($max_walltime < $mold->[1])){ print("[ADMISSION RULE] Walltime to big for an INTERACTIVE job so it is set to $max_walltime.\\n"); $mold->[1] = $max_walltime; } } } '); -- specify the default walltime if it is not specified INSERT INTO admission_rules (rule) VALUES (E' my $default_wall = OAR::IO::sql_to_duration("2:00:00"); foreach my $mold (@{$ref_resource_list}){ if (!defined($mold->[1])){ print("[ADMISSION RULE] Set default walltime to $default_wall.\\n"); $mold->[1] = $default_wall; } } '); -- Check if types given by the user are right INSERT INTO admission_rules (rule) VALUES (E' my @types = ("container","inner","deploy","desktop_computing","besteffort","cosystem","idempotent","timesharing","allow_classic_ssh","token\\:xxx=yy"); foreach my $t (@{$type_list}){ my $i = 0; while (($types[$i] ne $t) and ($i <= $#types)){ $i++; } if (($i > $#types) and ($t !~ /^(timesharing|inner|token\\:\\w+\\=\\d+)/)){ die("[ADMISSION RULE] The job type $t is not handled by OAR; Right values are : @types\\n"); } } '); -- If resource types are not specified, then we force them to default INSERT INTO admission_rules (rule) VALUES (E' foreach my $mold (@{$ref_resource_list}){ foreach my $r (@{$mold->[0]}){ my $prop = $r->{property}; if (($prop !~ /[\\s\\(]type[\\s=]/) and ($prop !~ /^type[\\s=]/)){ if (!defined($prop)){ $r->{property} = "type = ''default''"; }else{ $r->{property} = "($r->{property}) AND type = ''default''"; } } } } print("[ADMISSION RULE] Modify resource description with type constraints\\n"); '); ./oar-2.5.2/sources/core/database/oar-database.in0000755000175000017500000006322611757171206017627 0ustar plbplb#!/usr/bin/perl use strict; use warnings; use DBI(); use OAR::IO; use OAR::Conf qw(init_conf dump_conf get_conf is_conf set_value); use File::Basename; use Getopt::Long; use Sort::Versions; use Term::UI; use Term::ReadLine; Getopt::Long::Configure ("gnu_getopt"); # # Default values used in this script. # my $DEFAULT_CONFFILE = "%%OARCONFDIR%%/oar.conf"; my $DEFAULT_MYSQL_PORT = 3306; my $DEFAULT_PGSQL_PORT = 5432; my $DEFAULT_DB_NAME = "oar"; my $DEFAULT_DB_HOST = "localhost"; my $DEFAULT_DB_USER = "oar"; my $DEFAULT_DB_RO_USER = "oar_ro"; my $DEFAULT_MYSQL_STRUCTURE="%%OARDIR%%/database/mysql_structure.sql"; my $DEFAULT_MYSQL_ADMISSION_RULES="%%OARDIR%%/database/mysql_default_admission_rules.sql"; my $DEFAULT_MYSQL_DEFAULT_DATA="%%OARDIR%%/database/default_data.sql"; my $DEFAULT_MYSQL_RESET="%%OARDIR%%/database/mysql_reset_structure.sql"; my $DEFAULT_PGSQL_STRUCTURE="%%OARDIR%%/database/pg_structure.sql"; my $DEFAULT_PGSQL_ADMISSION_RULES="%%OARDIR%%/database/pg_default_admission_rules.sql"; my $DEFAULT_PGSQL_DEFAULT_DATA="%%OARDIR%%/database/default_data.sql"; my $DEFAULT_PGSQL_RESET="%%OARDIR%%/database/pg_reset_structure.sql"; my %DEFAULT_PGSQL_UPGRADES = (); my $DEFAULT_PGSQL_LAST_VERSION; my %DEFAULT_MYSQL_UPGRADES = (); my $DEFAULT_MYSQL_LAST_VERSION; # # meta functions/parameters defined during the execution (depending on the db type) # my $exec_sql; my $exec_sql_file; my %DEFAULT_UPGRADES; my $DEFAULT_LAST_VERSION; my $DEFAULT_STRUCTURE; my $DEFAULT_ADMISSION_RULES; my $DEFAULT_DATA; my $DEFAULT_RESET; # # Options from the command line # my $usage; my $debug; my $force_sql; my $force_yes; my $db_type; my $db_host; my $db_port; my $db_name; my $db_user; my $db_pass; my $db_ro_user; my $db_ro_pass; my $db_admin_user; my $db_admin_pass; my $db_is_local; my $action; my $conffile; # # database handler # my $dbh; # # Gather the sql file information for upgrading the database # open LIST, "find %%OARDIR%%/database -name '*_structure_upgrade_*-*.sql' |" or die "Unable to list the upstructure upgrade sql file"; while () { my ($line) = $_; chomp($line); if ($line =~ /.*\/(mysql|pg)_structure_upgrade_([\d\.]+)-([\d\.]+)\.sql$/) { my ($type, $version_from, $version_to) = ($1,$2,$3); if ($type eq "mysql") { $DEFAULT_MYSQL_UPGRADES{$version_from} = [$version_to,$line]; # process the last existing version if ($DEFAULT_MYSQL_LAST_VERSION) { if (versioncmp($version_to,$DEFAULT_MYSQL_LAST_VERSION) > 0) { $DEFAULT_MYSQL_LAST_VERSION = $version_to; } } else { $DEFAULT_MYSQL_LAST_VERSION = $version_to; } } elsif ($type eq "pg") { $DEFAULT_PGSQL_UPGRADES{$version_from} = [$version_to,$line]; # process the last existing version if ($DEFAULT_PGSQL_LAST_VERSION) { if (versioncmp($version_to,$DEFAULT_PGSQL_LAST_VERSION) > 0) { $DEFAULT_PGSQL_LAST_VERSION = $version_to; } } else { $DEFAULT_PGSQL_LAST_VERSION = $version_to; } } } } # # Display usage # sub print_usage() { print <prepare($query); $sth->execute(); my $ver = $sth->fetchrow_hashref; unless ($ver) { return ""; } return $ver->{'version'}; } sub init_database() { check_user_db_access($db_user,$db_pass); print "initializing the database '$db_name'...\n"; &$exec_sql_file($DEFAULT_STRUCTURE) == 0 or die "Fail to execute $DEFAULT_STRUCTURE.\n"; &$exec_sql_file($DEFAULT_DATA) == 0 or die "Fail to execute $DEFAULT_DATA.\n"; &$exec_sql_file($DEFAULT_ADMISSION_RULES) == 0 or die "Fail to execute $DEFAULT_ADMISSION_RULES.\n"; } sub reset_database() { print "resetting the database '$db_name'...\n"; &$exec_sql_file($DEFAULT_RESET) == 0 or die "Fail to execute $DEFAULT_RESET\n."; init_database(); } sub upgrade_database() { my ($to_version, $to_file); my $current_version = get_current_schema_version(); while($current_version ne $DEFAULT_LAST_VERSION) { if ($DEFAULT_UPGRADES{$current_version}) { ($to_version, $to_file) = @{$DEFAULT_UPGRADES{$current_version}}; print "uprgading from '$current_version' to '$to_version'...\n"; &$exec_sql_file($to_file) == 0 or die "Fail to upgrade the database from $current_version to $to_file with $to_file\n."; } else { die "Unable to upgrade from the version '$current_version'\n"; } $current_version = get_current_schema_version(); } } sub setup_database() { check_user_db_access($db_user,$db_pass); # Retrieve the current version (check if shema table exist before) my $db_version = get_current_schema_version(); if (!$db_version) { print "No schema version found. Initialising the database.\n"; init_database(); } elsif ($db_version eq $DEFAULT_LAST_VERSION) { print "The database is up to date (version $db_version).\n"; } else { print "The current schema version is $db_version. Upgrading to $DEFAULT_LAST_VERSION.\n"; upgrade_database(); } } sub mysql_ro_user_privileges() { # All except the challenge table (localhost) mysql_admin_exec_sql("GRANT SELECT ON $db_name.schema TO '$db_ro_user'".'@'."'localhost' IDENTIFIED BY '$db_ro_pass'") ; mysql_admin_exec_sql("GRANT SELECT ON $db_name.accounting TO '$db_ro_user'".'@'."'localhost' IDENTIFIED BY '$db_ro_pass'") ; mysql_admin_exec_sql("GRANT SELECT ON $db_name.admission_rules TO '$db_ro_user'".'@'."'localhost' IDENTIFIED BY '$db_ro_pass'") ; mysql_admin_exec_sql("GRANT SELECT ON $db_name.assigned_resources TO '$db_ro_user'".'@'."'localhost' IDENTIFIED BY '$db_ro_pass'") ; mysql_admin_exec_sql("GRANT SELECT ON $db_name.event_log_hostnames TO '$db_ro_user'".'@'."'localhost' IDENTIFIED BY '$db_ro_pass'") ; mysql_admin_exec_sql("GRANT SELECT ON $db_name.event_logs TO '$db_ro_user'".'@'."'localhost' IDENTIFIED BY '$db_ro_pass'") ; mysql_admin_exec_sql("GRANT SELECT ON $db_name.files TO '$db_ro_user'".'@'."'localhost' IDENTIFIED BY '$db_ro_pass'") ; mysql_admin_exec_sql("GRANT SELECT ON $db_name.frag_jobs TO '$db_ro_user'".'@'."'localhost' IDENTIFIED BY '$db_ro_pass'") ; mysql_admin_exec_sql("GRANT SELECT ON $db_name.gantt_jobs_predictions TO '$db_ro_user'".'@'."'localhost' IDENTIFIED BY '$db_ro_pass'") ; mysql_admin_exec_sql("GRANT SELECT ON $db_name.gantt_jobs_predictions_visu TO '$db_ro_user'".'@'."'localhost' IDENTIFIED BY '$db_ro_pass'") ; mysql_admin_exec_sql("GRANT SELECT ON $db_name.gantt_jobs_resources TO '$db_ro_user'".'@'."'localhost' IDENTIFIED BY '$db_ro_pass'") ; mysql_admin_exec_sql("GRANT SELECT ON $db_name.gantt_jobs_resources_visu TO '$db_ro_user'".'@'."'localhost' IDENTIFIED BY '$db_ro_pass'") ; mysql_admin_exec_sql("GRANT SELECT ON $db_name.job_dependencies TO '$db_ro_user'".'@'."'localhost' IDENTIFIED BY '$db_ro_pass'") ; mysql_admin_exec_sql("GRANT SELECT ON $db_name.job_resource_descriptions TO '$db_ro_user'".'@'."'localhost' IDENTIFIED BY '$db_ro_pass'") ; mysql_admin_exec_sql("GRANT SELECT ON $db_name.job_resource_groups TO '$db_ro_user'".'@'."'localhost' IDENTIFIED BY '$db_ro_pass'") ; mysql_admin_exec_sql("GRANT SELECT ON $db_name.job_state_logs TO '$db_ro_user'".'@'."'localhost' IDENTIFIED BY '$db_ro_pass'") ; mysql_admin_exec_sql("GRANT SELECT ON $db_name.job_types TO '$db_ro_user'".'@'."'localhost' IDENTIFIED BY '$db_ro_pass'") ; mysql_admin_exec_sql("GRANT SELECT ON $db_name.jobs TO '$db_ro_user'".'@'."'localhost' IDENTIFIED BY '$db_ro_pass'") ; mysql_admin_exec_sql("GRANT SELECT ON $db_name.moldable_job_descriptions TO '$db_ro_user'".'@'."'localhost' IDENTIFIED BY '$db_ro_pass'") ; mysql_admin_exec_sql("GRANT SELECT ON $db_name.queues TO '$db_ro_user'".'@'."'localhost' IDENTIFIED BY '$db_ro_pass'") ; mysql_admin_exec_sql("GRANT SELECT ON $db_name.resource_logs TO '$db_ro_user'".'@'."'localhost' IDENTIFIED BY '$db_ro_pass'") ; mysql_admin_exec_sql("GRANT SELECT ON $db_name.resources TO '$db_ro_user'".'@'."'localhost' IDENTIFIED BY '$db_ro_pass'") ; # All except the challenge table (other hosts) mysql_admin_exec_sql("GRANT SELECT ON $db_name.schema TO '$db_ro_user'".'@'."'%' IDENTIFIED BY '$db_ro_pass'") ; mysql_admin_exec_sql("GRANT SELECT ON $db_name.accounting TO '$db_ro_user'".'@'."'%' IDENTIFIED BY '$db_ro_pass'") ; mysql_admin_exec_sql("GRANT SELECT ON $db_name.admission_rules TO '$db_ro_user'".'@'."'%' IDENTIFIED BY '$db_ro_pass'") ; mysql_admin_exec_sql("GRANT SELECT ON $db_name.assigned_resources TO '$db_ro_user'".'@'."'%' IDENTIFIED BY '$db_ro_pass'") ; mysql_admin_exec_sql("GRANT SELECT ON $db_name.event_log_hostnames TO '$db_ro_user'".'@'."'%' IDENTIFIED BY '$db_ro_pass'") ; mysql_admin_exec_sql("GRANT SELECT ON $db_name.event_logs TO '$db_ro_user'".'@'."'%' IDENTIFIED BY '$db_ro_pass'") ; mysql_admin_exec_sql("GRANT SELECT ON $db_name.files TO '$db_ro_user'".'@'."'%' IDENTIFIED BY '$db_ro_pass'") ; mysql_admin_exec_sql("GRANT SELECT ON $db_name.frag_jobs TO '$db_ro_user'".'@'."'%' IDENTIFIED BY '$db_ro_pass'") ; mysql_admin_exec_sql("GRANT SELECT ON $db_name.gantt_jobs_predictions TO '$db_ro_user'".'@'."'%' IDENTIFIED BY '$db_ro_pass'") ; mysql_admin_exec_sql("GRANT SELECT ON $db_name.gantt_jobs_predictions_visu TO '$db_ro_user'".'@'."'%' IDENTIFIED BY '$db_ro_pass'") ; mysql_admin_exec_sql("GRANT SELECT ON $db_name.gantt_jobs_resources TO '$db_ro_user'".'@'."'%' IDENTIFIED BY '$db_ro_pass'") ; mysql_admin_exec_sql("GRANT SELECT ON $db_name.gantt_jobs_resources_visu TO '$db_ro_user'".'@'."'%' IDENTIFIED BY '$db_ro_pass'") ; mysql_admin_exec_sql("GRANT SELECT ON $db_name.job_dependencies TO '$db_ro_user'".'@'."'%' IDENTIFIED BY '$db_ro_pass'") ; mysql_admin_exec_sql("GRANT SELECT ON $db_name.job_resource_descriptions TO '$db_ro_user'".'@'."'%' IDENTIFIED BY '$db_ro_pass'") ; mysql_admin_exec_sql("GRANT SELECT ON $db_name.job_resource_groups TO '$db_ro_user'".'@'."'%' IDENTIFIED BY '$db_ro_pass'") ; mysql_admin_exec_sql("GRANT SELECT ON $db_name.job_state_logs TO '$db_ro_user'".'@'."'%' IDENTIFIED BY '$db_ro_pass'") ; mysql_admin_exec_sql("GRANT SELECT ON $db_name.job_types TO '$db_ro_user'".'@'."'%' IDENTIFIED BY '$db_ro_pass'") ; mysql_admin_exec_sql("GRANT SELECT ON $db_name.jobs TO '$db_ro_user'".'@'."'%' IDENTIFIED BY '$db_ro_pass'") ; mysql_admin_exec_sql("GRANT SELECT ON $db_name.moldable_job_descriptions TO '$db_ro_user'".'@'."'%' IDENTIFIED BY '$db_ro_pass'") ; mysql_admin_exec_sql("GRANT SELECT ON $db_name.queues TO '$db_ro_user'".'@'."'%' IDENTIFIED BY '$db_ro_pass'") ; mysql_admin_exec_sql("GRANT SELECT ON $db_name.resource_logs TO '$db_ro_user'".'@'."'%' IDENTIFIED BY '$db_ro_pass'") ; mysql_admin_exec_sql("GRANT SELECT ON $db_name.resources TO '$db_ro_user'".'@'."'%' IDENTIFIED BY '$db_ro_pass'") ; } sub fix_ro_user_privileges() { # This revokes the wildcard select privilege that was made by previous # versions of this script and set up the correct privileges for the ro user # (the bug affected only mysql databases) if ($db_type eq "mysql") { print "Fixing the ro user privileges...\n"; mysql_admin_exec_sql("REVOKE SELECT ON $db_name.* FROM '$db_ro_user'".'@'."'localhost'") ; mysql_admin_exec_sql("REVOKE SELECT ON $db_name.* FROM '$db_ro_user'".'@'."'%'") ; mysql_ro_user_privileges(); } } sub create_database() { if ($db_type eq "mysql") { print "Creating the database...\n"; mysql_admin_exec_sql("CREATE DATABASE $db_name CHARACTER SET latin1") ; print "Creating the database user...\n"; mysql_admin_exec_sql("GRANT ALL PRIVILEGES ON $db_name.* TO '$db_user'" . '@' . "'localhost' IDENTIFIED BY '$db_pass'") ; mysql_admin_exec_sql("GRANT ALL PRIVILEGES ON $db_name.* TO '$db_user'" . '@' ."'%' IDENTIFIED BY '$db_pass'"); } elsif ($db_type eq "Pg") { print "Creating the database user...\n"; pgsql_admin_exec_sql("CREATE ROLE $db_user LOGIN PASSWORD '$db_pass';"); if ($db_ro_user and $db_ro_pass) { print "Creating the database read-only user...\n"; pgsql_admin_exec_sql("CREATE ROLE $db_ro_user LOGIN PASSWORD '$db_ro_pass';"); } print "Creating the database...\n"; pgsql_admin_exec_sql("CREATE DATABASE $db_name OWNER $db_user"); pgsql_admin_exec_sql("GRANT ALL PRIVILEGES ON DATABASE $db_name TO $db_user"); } init_database(); if ($db_ro_user and $db_ro_pass) { if ($db_type eq "Pg") { pgsql_admin_exec_sql("GRANT SELECT ON schema,accounting,admission_rules,assigned_resources,event_log_hostnames,event_logs,files,frag_jobs,gantt_jobs_predictions,gantt_jobs_predictions_visu,gantt_jobs_resources,gantt_jobs_resources_visu,job_dependencies,job_resource_descriptions,job_resource_groups,job_state_logs,job_types,jobs,moldable_job_descriptions,queues,resource_logs,resources,admission_rules_id_seq,event_logs_event_id_seq,files_file_id_seq,job_resource_groups_res_group_id_seq,job_state_logs_job_state_log_id_seq,job_types_job_type_id_seq,moldable_job_descriptions_moldable_id_seq,resource_logs_resource_log_id_seq,resources_resource_id_seq,jobs_job_id_seq TO $db_ro_user", $db_name); } elsif ($db_type eq "mysql") { print "Creating the database read-only user...\n"; mysql_ro_user_privileges(); } } } sub drop_database() { print "Removing the database...\n"; if ($db_type eq "mysql") { mysql_admin_exec_sql("DROP DATABASE $db_name"); mysql_admin_exec_sql("DROP USER $db_user". '@' . "'%'"); mysql_admin_exec_sql("DROP USER $db_user" . '@localhost'); if ($db_ro_user) { mysql_admin_exec_sql("DROP USER $db_ro_user" .'@'. "'%'"); mysql_admin_exec_sql("DROP USER $db_ro_user" . '@localhost'); } } elsif ($db_type eq "Pg") { pgsql_admin_exec_sql("DROP DATABASE $db_name"); pgsql_admin_exec_sql("DROP USER $db_user"); if ($db_ro_user) { pgsql_admin_exec_sql("DROP USER $db_ro_user"); } } } sub mysql_exec_sql_file { my ($file) = @_; if ($debug) { print "executing '$file'\n"; } if ($force_sql) { return system("mysql --force --user='$db_user' --password='$db_pass' --host='$db_host' --port='$db_port' $db_name < $file"); } else { return system("mysql --user='$db_user' --password='$db_pass' --host='$db_host' --port='$db_port' $db_name < $file"); } } sub pgsql_exec_sql_file { my ($file) = @_; if ($debug) { print "executing '$file'\n"; } return system("PGUSER='$db_user' PGPASSWORD='$db_pass' PGHOST='$db_host' PGPORT='$db_port' psql -q $db_name < $file"); } sub mysql_exec_sql { my ($query) = @_; if ($debug) { print "executing '$query'\n"; } if ($force_sql) { return system("mysql --force --user='$db_user' --password='$db_pass' --host='$db_host' --port='$db_port' $db_name -e \"$query\""); } else { return system("mysql --user='$db_user' --password='$db_pass' --host='$db_host' --port='$db_port' $db_name -e \"$query\""); } } sub mysql_admin_exec_sql { my ($query) = @_; if ($debug) { print "executing '$query'\n"; } return system("mysql --user='$db_admin_user' --password='$db_admin_pass' --host='$db_host' --port='$db_port' -e \"$query\""); } sub pgsql_exec_sql { my ($query) = @_; if ($debug) { print "executing '$query'\n"; } return system("PGUSER='$db_user' PGPASSWORD='$db_pass' PGHOST='$db_host' PGPORT='$db_port' psql -q $db_name -c \"$query\""); } sub pgsql_admin_exec_sql { my ($query,$db) = @_; unless ($db) { $db=""; } if ($debug) { print "executing '$query'\n"; } if ($db_is_local) { return system("echo \"$query\" | su - postgres -c \"psql $db\""); } else { return system("PGUSER='$db_admin_user' PGPASSWORD='$db_admin_pass' PGHOST='$db_host' PGPORT='$db_port' psql $db -c \"$query\""); } } GetOptions ("help|h" => \$usage, "debug|d" => \$debug, "force-sql|f" => \$force_sql, "force-yes|y" => \$force_yes, "setup|s" => sub { $action = 'setup' }, "reset|r" => sub { $action = 'reset' }, "create|c" => sub { $action = 'create' }, "drop" => sub { $action = 'drop' }, "check" => sub { $action = 'check' }, "fix-ro-user-priv" => sub { $action = 'fix-ro-user-priv' }, "conf=s" => \$conffile, # "update-conf" => sub { $action = 'update-conf' }, "db-type=s" => \$db_type, "db-host=s" => \$db_host, "db-port=i" => \$db_port, "db-user=s" => \$db_user, "db-pass=s" => \$db_pass, "db-ro-user=s" => \$db_ro_user, "db-ro-pass=s" => \$db_ro_pass, "db-name=s" => \$db_name, "db-admin-user=s" => \$db_admin_user, "db-admin-pass=s" => \$db_admin_pass, "db-is-local" => \$db_is_local ); # # Usage # if ($usage){ print_usage(); exit(0); } # # Check the action # if ( !$action ) { print "You must specify the action do be done.\n"; print_usage(); exit(1); } # # Check if the given conffile. # if ( ! $conffile ) { $conffile=$DEFAULT_CONFFILE; } unless ( -r $conffile ) { print "The configuration file isn't readable ! Failing.\n"; print_usage(); exit(1); } # # Set the parameters to use # OAR::Conf::init_conf($conffile); if (! $db_type) { if (OAR::Conf::is_conf("DB_TYPE")) { $db_type = OAR::Conf::get_conf("DB_TYPE"); if (OAR::Conf::is_conf("DB_PORT")) { $db_port = OAR::Conf::get_conf("DB_PORT"); } } else { print "Your must set the database type ! Failing \n"; print_usage(); exit(1); } } elsif ( $db_type ne "Pg" && $db_type ne "mysql") { print "The database type must be 'Pg' or 'mysql'. Failing.\n"; print_usage(); exit(1); } if (! $db_port ) { if ($db_type eq "mysql") { $db_port = $DEFAULT_MYSQL_PORT; } if ($db_type eq "Pg") { $db_port = $DEFAULT_PGSQL_PORT; } } unless ($db_host) { $db_host = (OAR::Conf::is_conf("DB_HOSTNAME") ? OAR::Conf::get_conf("DB_HOSTNAME") : $DEFAULT_DB_HOST); } unless ($db_user) { $db_user = (OAR::Conf::is_conf("DB_BASE_LOGIN") ? OAR::Conf::get_conf("DB_BASE_LOGIN") : $DEFAULT_DB_USER); } unless ($db_pass) { $db_pass = (OAR::Conf::is_conf("DB_BASE_PASSWD") ? OAR::Conf::get_conf("DB_BASE_PASSWD") : ""); if (($db_pass eq "") or ($db_pass eq '""')) { print "The user password is empty. Please define a password. Failing.\n"; exit(1); } } unless ($db_ro_user) { if (OAR::Conf::is_conf("DB_BASE_LOGIN_RO")) { $db_ro_user = OAR::Conf::get_conf("DB_BASE_LOGIN_RO"); } } unless ($db_ro_pass) { if (OAR::Conf::is_conf("DB_BASE_PASSWD_RO")) { $db_ro_pass = OAR::Conf::get_conf("DB_BASE_PASSWD_RO"); } if (($db_ro_pass eq "") or ($db_ro_pass eq '""')) { print "The user read-only password is empty. Please define a password. Failing.\n"; exit(1); } } unless ($db_name) { $db_name = (OAR::Conf::is_conf("DB_BASE_NAME") ? OAR::Conf::get_conf("DB_BASE_NAME") : $DEFAULT_DB_NAME); } if ($action eq "create" or $action eq "drop" or $action eq "fix-ro-user-priv") { unless ($db_is_local and $db_type eq "Pg") { unless ($db_admin_user) { print "You need to specify the admin user (--db-admin-user) and probably the password (--db-admin-pass) to use. See the manual for more information. Failing.\n"; exit(1); } unless ($db_admin_pass) { print "The admin password is empty. Proceeding with an empty password.\n"; $db_admin_pass=""; } } } # # Set the meta function depending on the db type # if ($db_type eq "mysql") { $exec_sql = \&mysql_exec_sql; $exec_sql_file = \&mysql_exec_sql_file; $DEFAULT_LAST_VERSION = $DEFAULT_MYSQL_LAST_VERSION; %DEFAULT_UPGRADES = %DEFAULT_MYSQL_UPGRADES; $DEFAULT_RESET = $DEFAULT_MYSQL_RESET; $DEFAULT_STRUCTURE = $DEFAULT_MYSQL_STRUCTURE; $DEFAULT_ADMISSION_RULES = $DEFAULT_MYSQL_ADMISSION_RULES; $DEFAULT_DATA = $DEFAULT_MYSQL_DEFAULT_DATA; } elsif ($db_type eq "Pg") { $exec_sql = \&pgsql_exec_sql; $exec_sql_file = \&pgsql_exec_sql_file; $DEFAULT_LAST_VERSION = $DEFAULT_PGSQL_LAST_VERSION; %DEFAULT_UPGRADES = %DEFAULT_PGSQL_UPGRADES; $DEFAULT_RESET = $DEFAULT_PGSQL_RESET; $DEFAULT_STRUCTURE = $DEFAULT_PGSQL_STRUCTURE; $DEFAULT_ADMISSION_RULES = $DEFAULT_PGSQL_ADMISSION_RULES; $DEFAULT_DATA = $DEFAULT_PGSQL_DEFAULT_DATA; } else { die "You shouldn't execute this part of the code. Contact the developper\n"; } # # try a connection to the database # sub check_user_db_access { my ($user,$pass) = @_; my $dbc = "DBI:$db_type:database=$db_name;host=$db_host;port=$db_port"; $dbh = DBI->connect($dbc, $user,$pass, {'InactiveDestroy' => 1, 'PrintError' => 1}); if (!defined($dbh)) { print "The connection to the database has failed (user=$user,host=$db_host,port=$db_port,database=$db_name). Please check if the database server is up and ready.\n"; print "Please see the file %%DOCDIR%%/README.database for the instructions.\n"; exit(1); } } # # update oar.conf if asked # if ($action eq "update-conf") { print "Updating $conffile with the given parameters.\n"; OAR::Conf::set_value("DB_HOSTNAME",$db_host); OAR::Conf::set_value("DB_PORT",$db_port); OAR::Conf::set_value("DB_BASE_NAME",$db_name); OAR::Conf::set_value("DB_BASE_LOGIN",$db_user); OAR::Conf::set_value("DB_BASE_PASSWD",$db_pass); OAR::Conf::set_value("DB_BASE_LOGIN_RO",$db_ro_user); OAR::Conf::set_value("DB_BASE_PASSWD_RO",$db_ro_pass); OAR::Conf::set_value("DB_TYPE",$db_type); } # # setup the database # my $term = Term::ReadLine->new("brand"); $Term::UI::VERBOSE = 0; if ($action eq "check") { $ENV{'OARCONFFILE'} = "%%OARCONFDIR%%/oar.conf"; if (defined(OAR::IO::connect_ro_one_log("log"))) { print "The OAR database is accessible and ready to be used.\n"; exit 0; } elsei { print "The OAR database isn't ready.\n"; exit 1; } } elsif ($action eq "setup") { check_user_db_access($db_user,$db_pass); if($force_yes || $term->ask_yn( prompt => "Are you sure you want to upgrade your database ? (Please backup the database before)", default => 'n')) { setup_database(); } else { print "The database hasn't been changed\n." } } elsif ($action eq "reset") { check_user_db_access($db_user,$db_pass); if ($force_yes || $term->ask_yn( prompt => "Are you sure you want to reset your database ? (The database content will be lost)", default => 'n')) { reset_database(); } else { print "The database hasn't been changed\n." } } elsif ($action eq "create") { create_database(); } elsif ($action eq "drop") { drop_database(); } elsif ($action eq "fix-ro-user-priv") { fix_ro_user_privileges(); } ./oar-2.5.2/sources/core/database/mysql_structure_upgrade_2.5.0-2.5.2.sql0000644000175000017500000000027311757171206023643 0ustar plbplb ALTER TABLE jobs ADD array_index INT UNSIGNED NOT NULL DEFAULT 1; -- Update the database schema version DELETE FROM `schema`; INSERT INTO `schema`(version, name) VALUES ('2.5.2', ''); ./oar-2.5.2/sources/core/database/mysql_structure_upgrade_2.4.0-2.5.0.sql0000644000175000017500000000042011757171206023632 0ustar plbplb-- Gantt output time optimization alter table resource_logs add index date_stop(date_stop); alter table resource_logs add index date_start(date_start); -- Update the database schema version DELETE FROM `schema`; INSERT INTO `schema`(version, name) VALUES ('2.5.0', ''); ./oar-2.5.2/sources/core/database/mysql_structure_upgrade_2.3.3-2.4.0.sql0000644000175000017500000000115711757171206023643 0ustar plbplbalter table resources change cm_availability available_upto INT UNSIGNED DEFAULT 0 NOT NULL; alter table resources add last_available_upto INT UNSIGNED DEFAULT 0 NOT NULL; delete from `schema`; alter table `schema` add name VARCHAR( 255 ) NOT NULL; INSERT INTO `schema`(version, name) VALUES ('2.4.0', 'Thriller'); CREATE TABLE IF NOT EXISTS scheduler ( name VARCHAR( 100 ) NOT NULL, script VARCHAR( 100 ) NOT NULL, description VARCHAR( 255 ) NOT NULL, PRIMARY KEY (name) ); CREATE INDEX job_array_id ON jobs(array_id); CREATE INDEX event_job_id ON event_logs (job_id); CREATE INDEX challenge_job_id ON challenges(job_id); ./oar-2.5.2/sources/core/database/mysql_structure_upgrade_2.3.1-2.3.3.sql0000644000175000017500000000021311757171206023633 0ustar plbplbALTER TABLE jobs ADD array_id INT UNSIGNED NOT NULL DEFAULT 0; UPDATE jobs SET array_id = job_id ; INSERT INTO `schema` VALUES ('2.3.3'); ./oar-2.5.2/sources/core/database/mysql_structure_upgrade_2.3.0+svn1369.sql0000644000175000017500000000126211757171206024323 0ustar plbplb# $Id$ DROP TABLE IF EXISTS `schema`; CREATE TABLE IF NOT EXISTS `schema` ( version VARCHAR( 255 ) NOT NULL ); INSERT INTO `schema` VALUES ('2.3.0+svn1369'); #DROP TABLE IF EXISTS gantt_jobs_predictions_log; CREATE TABLE IF NOT EXISTS gantt_jobs_predictions_log ( sched_date INT UNSIGNED NOT NULL , moldable_job_id INT UNSIGNED NOT NULL , start_time INT UNSIGNED NOT NULL , PRIMARY KEY (sched_date,moldable_job_id) ); #DROP TABLE IF EXISTS gantt_jobs_resources_log; CREATE TABLE IF NOT EXISTS gantt_jobs_resources_log ( sched_date INT UNSIGNED NOT NULL , moldable_job_id INT UNSIGNED NOT NULL , resource_id INT UNSIGNED NOT NULL , PRIMARY KEY (sched_date,moldable_job_id,resource_id) ); ./oar-2.5.2/sources/core/database/mysql_structure.sql0000644000175000017500000002606711757171206020761 0ustar plbplb# $Id$ # Creation de la base de donnees #CREATE DATABASE IF NOT EXISTS oar; # Creation de l utilisateur oar #CONNECT mysql; #INSERT INTO user (Host,User,Password) VALUES('localhost','oar',PASSWORD('oar')); #INSERT INTO user (Host,User,Password) VALUES('%','oar',PASSWORD('oar')); #INSERT INTO db (Host,Db,User,Select_priv,Insert_priv,Update_priv,Delete_priv, Create_priv,Drop_priv) VALUES # ('localhost','oar','oar','Y','Y','Y','Y','Y','Y'); #INSERT INTO db (Host,Db,User,Select_priv,Insert_priv,Update_priv,Delete_priv, Create_priv,Drop_priv) VALUES # ('%','oar','oar','Y','Y','Y','Y','Y','Y'); #FLUSH PRIVILEGES; #GRANT ALL ON oar.* TO oar@localhost; #GRANT ALL ON oar.* TO oar@"%"; #GRANT SELECT ON oar.* TO oarreader@localhost; #GRANT SELECT ON oar.* TO oarreader@"%"; #FLUSH PRIVILEGES; #CONNECT oar; # Creation des tables dans la base de donnees oar # schema version, change here if you have updated the db schema CREATE TABLE IF NOT EXISTS `schema` ( version VARCHAR( 255 ) NOT NULL, name VARCHAR( 255 ) NOT NULL ); INSERT INTO `schema` VALUES ('2.5.0', ''); #DROP TABLE IF EXISTS jobs; CREATE TABLE IF NOT EXISTS jobs ( job_id INT UNSIGNED NOT NULL AUTO_INCREMENT, array_id INT UNSIGNED NOT NULL DEFAULT 0, array_index INT UNSIGNED NOT NULL DEFAULT 1, initial_request TEXT, job_name VARCHAR( 100 ) , job_env TEXT , job_type ENUM('INTERACTIVE','PASSIVE') DEFAULT 'PASSIVE' NOT NULL , info_type VARCHAR( 255 ) , state ENUM('Waiting','Hold','toLaunch','toError','toAckReservation','Launching','Running','Suspended','Resuming','Finishing','Terminated','Error') NOT NULL , reservation ENUM('None','toSchedule','Scheduled') DEFAULT 'None' NOT NULL , message VARCHAR( 255 ) NOT NULL , scheduler_info VARCHAR( 255 ) NOT NULL , job_user VARCHAR( 255 ) NOT NULL , project VARCHAR( 255 ) NOT NULL , job_group VARCHAR( 255 ) NOT NULL , command TEXT , exit_code INT DEFAULT NULL , queue_name VARCHAR( 100 ) NOT NULL , properties TEXT , launching_directory TEXT NOT NULL , submission_time INT UNSIGNED NOT NULL , start_time INT UNSIGNED NOT NULL , stop_time INT UNSIGNED NOT NULL , file_id INT UNSIGNED, accounted ENUM("YES","NO") NOT NULL DEFAULT "NO" , notify VARCHAR( 255 ) DEFAULT NULL , assigned_moldable_job INT UNSIGNED DEFAULT 0 , checkpoint INT UNSIGNED NOT NULL DEFAULT 0 , checkpoint_signal INT NOT NULL, stdout_file TEXT , stderr_file TEXT , resubmit_job_id INT UNSIGNED DEFAULT 0, suspended ENUM("YES","NO") NOT NULL DEFAULT "NO" , INDEX state (state), INDEX state_id (state,job_id), INDEX reservation (reservation), INDEX queue_name (queue_name), INDEX accounted (accounted), INDEX suspended (suspended), INDEX job_array_id (array_id), PRIMARY KEY (job_id) ); #DROP TABLE IF EXISTS job_types; CREATE TABLE IF NOT EXISTS job_types ( job_type_id INT UNSIGNED NOT NULL AUTO_INCREMENT, job_id INT UNSIGNED NOT NULL , type VARCHAR(255) NOT NULL , types_index ENUM('CURRENT','LOG') DEFAULT 'CURRENT' NOT NULL , INDEX log (types_index), INDEX type (type), INDEX id_types (job_id), PRIMARY KEY (job_type_id) ); #DROP TABLE IF EXISTS challenges; CREATE TABLE IF NOT EXISTS challenges ( job_id INT UNSIGNED NOT NULL , challenge VARCHAR(255) NOT NULL , ssh_private_key TEXT NOT NULL DEFAULT "" , ssh_public_key TEXT NOT NULL DEFAULT "" , INDEX challenge_job_id (job_id), PRIMARY KEY (job_id) ); #DROP TABLE IF EXISTS moldable_job_descriptions; CREATE TABLE IF NOT EXISTS moldable_job_descriptions ( moldable_id INT UNSIGNED NOT NULL AUTO_INCREMENT, moldable_job_id INT UNSIGNED NOT NULL , moldable_walltime INT UNSIGNED NOT NULL , moldable_index ENUM('CURRENT','LOG') DEFAULT 'CURRENT' NOT NULL , INDEX job (moldable_job_id) , INDEX log (moldable_index) , PRIMARY KEY (moldable_id) ); #DROP TABLE IF EXISTS job_resource_groups; CREATE TABLE IF NOT EXISTS job_resource_groups ( res_group_id INT UNSIGNED NOT NULL AUTO_INCREMENT , res_group_moldable_id INT UNSIGNED NOT NULL , res_group_property TEXT , res_group_index ENUM('CURRENT','LOG') DEFAULT 'CURRENT' NOT NULL , INDEX moldable_job (res_group_moldable_id), INDEX log (res_group_index) , PRIMARY KEY (res_group_id) ); #DROP TABLE IF EXISTS job_resource_descriptions; CREATE TABLE IF NOT EXISTS job_resource_descriptions ( res_job_group_id INT UNSIGNED NOT NULL, res_job_resource_type VARCHAR(255) NOT NULL, res_job_value INT NOT NULL, res_job_order INT UNSIGNED NOT NULL DEFAULT 0, res_job_index ENUM('CURRENT','LOG') DEFAULT 'CURRENT' NOT NULL , INDEX resgroup (res_job_group_id), INDEX log (res_job_index) , PRIMARY KEY (res_job_group_id,res_job_resource_type,res_job_order) ); #DROP TABLE IF EXISTS job_state_logs; CREATE TABLE IF NOT EXISTS job_state_logs ( job_state_log_id INT UNSIGNED NOT NULL AUTO_INCREMENT, job_id INT UNSIGNED NOT NULL , job_state ENUM('Waiting','Hold','toLaunch','toError','toAckReservation','Launching','Finishing','Running','Suspended','Resuming','Terminated','Error') NOT NULL , date_start INT UNSIGNED NOT NULL, date_stop INT UNSIGNED DEFAULT 0, INDEX id (job_id), INDEX state (job_state), PRIMARY KEY (job_state_log_id) ); #DROP TABLE IF EXISTS frag_jobs; CREATE TABLE IF NOT EXISTS frag_jobs ( frag_id_job INT UNSIGNED NOT NULL , frag_date INT UNSIGNED NOT NULL , frag_state ENUM('LEON','TIMER_ARMED','LEON_EXTERMINATE','FRAGGED') DEFAULT 'LEON' NOT NULL , INDEX frag_state (frag_state), PRIMARY KEY (frag_id_job) ); #DROP TABLE IF EXISTS assigned_resources; CREATE TABLE IF NOT EXISTS assigned_resources ( moldable_job_id INT UNSIGNED NOT NULL , resource_id INT UNSIGNED NOT NULL , assigned_resource_index ENUM('CURRENT','LOG') DEFAULT 'CURRENT' NOT NULL , INDEX mjob_id (moldable_job_id), INDEX log (assigned_resource_index), PRIMARY KEY (moldable_job_id,resource_id) ); #DROP TABLE IF EXISTS resources; CREATE TABLE IF NOT EXISTS resources ( resource_id INT UNSIGNED NOT NULL AUTO_INCREMENT, type VARCHAR( 100 ) NOT NULL DEFAULT "default" , network_address VARCHAR( 100 ) NOT NULL , state ENUM('Alive','Dead','Suspected','Absent') NOT NULL , next_state ENUM('UnChanged','Alive','Dead','Absent','Suspected') DEFAULT 'UnChanged' NOT NULL , finaud_decision ENUM('YES','NO') DEFAULT 'NO' NOT NULL , next_finaud_decision ENUM('YES','NO') DEFAULT 'NO' NOT NULL , state_num INT NOT NULL DEFAULT 0 , suspended_jobs ENUM('YES','NO') DEFAULT 'NO' NOT NULL , scheduler_priority INT UNSIGNED NOT NULL DEFAULT 0 , cpuset INT UNSIGNED NOT NULL DEFAULT 0 , besteffort ENUM('YES','NO') DEFAULT 'YES' NOT NULL , deploy ENUM('YES','NO') DEFAULT 'NO' NOT NULL , expiry_date INT UNSIGNED NOT NULL , desktop_computing ENUM('YES','NO') DEFAULT 'NO' NOT NULL, last_job_date INT UNSIGNED DEFAULT 0, available_upto INT UNSIGNED DEFAULT 2147483647 NOT NULL, last_available_upto INT UNSIGNED DEFAULT 0 NOT NULL, INDEX state (state), INDEX next_state (next_state), INDEX suspended_jobs (suspended_jobs), INDEX type (type), INDEX network_address (network_address), PRIMARY KEY (resource_id) ); #DROP TABLE IF EXISTS resource_logs; CREATE TABLE IF NOT EXISTS resource_logs ( resource_log_id INT UNSIGNED NOT NULL AUTO_INCREMENT, resource_id INT UNSIGNED NOT NULL , attribute VARCHAR( 255 ) NOT NULL , value VARCHAR( 255 ) NOT NULL , date_start INT UNSIGNED NOT NULL, date_stop INT UNSIGNED DEFAULT 0 , finaud_decision ENUM('YES','NO') DEFAULT 'NO' NOT NULL , INDEX resource (resource_id), INDEX attribute (attribute), INDEX finaud (finaud_decision), INDEX val (value), INDEX date_stop (date_stop), INDEX date_start (date_start), PRIMARY KEY (resource_log_id) ); #DROP TABLE IF EXISTS queues; CREATE TABLE IF NOT EXISTS queues ( queue_name VARCHAR( 100 ) NOT NULL , priority INT UNSIGNED NOT NULL , scheduler_policy VARCHAR( 100 ) NOT NULL , state ENUM('Active','notActive') NOT NULL DEFAULT 'Active', PRIMARY KEY (queue_name) ); CREATE TABLE IF NOT EXISTS scheduler ( name VARCHAR( 100 ) NOT NULL, script VARCHAR( 100 ) NOT NULL, description VARCHAR( 255 ) NOT NULL, PRIMARY KEY (name) ); #DROP TABLE IF EXISTS admission_rules; CREATE TABLE IF NOT EXISTS admission_rules ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, rule TEXT NOT NULL, PRIMARY KEY (id) ); #DROP TABLE IF EXISTS gantt_jobs_predictions; CREATE TABLE IF NOT EXISTS gantt_jobs_predictions ( moldable_job_id INT UNSIGNED NOT NULL , start_time INT UNSIGNED NOT NULL , PRIMARY KEY (moldable_job_id) ); #DROP TABLE IF EXISTS gantt_jobs_predictions_visu; CREATE TABLE IF NOT EXISTS gantt_jobs_predictions_visu ( moldable_job_id INT UNSIGNED NOT NULL , start_time INT UNSIGNED NOT NULL , PRIMARY KEY (moldable_job_id) ); #DROP TABLE IF EXISTS gantt_jobs_predictions_log; CREATE TABLE IF NOT EXISTS gantt_jobs_predictions_log ( sched_date INT UNSIGNED NOT NULL , moldable_job_id INT UNSIGNED NOT NULL , start_time INT UNSIGNED NOT NULL , PRIMARY KEY (sched_date,moldable_job_id) ); #DROP TABLE IF EXISTS gantt_jobs_resources; CREATE TABLE IF NOT EXISTS gantt_jobs_resources ( moldable_job_id INT UNSIGNED NOT NULL , resource_id INT UNSIGNED NOT NULL , PRIMARY KEY (moldable_job_id,resource_id) ); #DROP TABLE IF EXISTS gantt_jobs_resources_visu; CREATE TABLE IF NOT EXISTS gantt_jobs_resources_visu ( moldable_job_id INT UNSIGNED NOT NULL , resource_id INT UNSIGNED NOT NULL , PRIMARY KEY (moldable_job_id,resource_id) ); #DROP TABLE IF EXISTS gantt_jobs_resources_log; CREATE TABLE IF NOT EXISTS gantt_jobs_resources_log ( sched_date INT UNSIGNED NOT NULL , moldable_job_id INT UNSIGNED NOT NULL , resource_id INT UNSIGNED NOT NULL , PRIMARY KEY (sched_date,moldable_job_id,resource_id) ); #DROP TABLE IF EXISTS files; CREATE TABLE IF NOT EXISTS files ( file_id INT UNSIGNED NOT NULL AUTO_INCREMENT, md5sum VARCHAR( 255 ) , location VARCHAR( 255 ) , method VARCHAR( 255 ) , compression VARCHAR( 255 ) , size INT UNSIGNED NOT NULL , INDEX md5sum (md5sum), PRIMARY KEY (file_id) ); #DROP TABLE IF EXISTS event_logs; CREATE TABLE IF NOT EXISTS event_logs ( event_id INT UNSIGNED NOT NULL AUTO_INCREMENT, type VARCHAR(50) NOT NULL, job_id INT UNSIGNED NOT NULL , date INT UNSIGNED NOT NULL , description VARCHAR(255) NOT NULL, to_check ENUM('YES','NO') NOT NULL DEFAULT 'YES', INDEX event_type (type), INDEX event_check (to_check), INDEX event_job_id (job_id), PRIMARY KEY (event_id) ); #DROP TABLE IF EXISTS event_log_hostnames; CREATE TABLE IF NOT EXISTS event_log_hostnames ( event_id INT UNSIGNED NOT NULL, hostname VARCHAR( 255 ) NOT NULL , INDEX event_hostname (hostname), PRIMARY KEY (event_id, hostname) ); #DROP TABLE IF EXISTS accounting; CREATE TABLE IF NOT EXISTS accounting ( window_start INT UNSIGNED NOT NULL , window_stop INT UNSIGNED NOT NULL , accounting_user VARCHAR( 255 ) NOT NULL , accounting_project VARCHAR( 255 ) NOT NULL , queue_name VARCHAR( 100 ) NOT NULL , consumption_type ENUM("ASKED","USED") NOT NULL , consumption INT UNSIGNED NOT NULL , INDEX accounting_user (accounting_user), INDEX accounting_project (accounting_project), INDEX accounting_queue (queue_name), INDEX accounting_type (consumption_type), PRIMARY KEY (window_start,window_stop,accounting_user,accounting_project,queue_name,consumption_type) ); #DROP TABLE IF EXISTS job_dependencies; CREATE TABLE IF NOT EXISTS job_dependencies ( job_id INT UNSIGNED NOT NULL , job_id_required INT UNSIGNED NOT NULL, job_dependency_index ENUM('CURRENT','LOG') DEFAULT 'CURRENT' NOT NULL , INDEX id (job_id), INDEX log (job_dependency_index), PRIMARY KEY (job_id,job_id_required) ); ./oar-2.5.2/sources/core/database/mysql_reset_structure.sql0000644000175000017500000000174311757171206022155 0ustar plbplb DROP TABLE IF EXISTS schema; DROP TABLE IF EXISTS jobs; DROP TABLE IF EXISTS job_types; DROP TABLE IF EXISTS challenges; DROP TABLE IF EXISTS moldable_job_descriptions; DROP TABLE IF EXISTS job_resource_groups; DROP TABLE IF EXISTS job_resource_descriptions; DROP TABLE IF EXISTS job_state_logs; DROP TABLE IF EXISTS frag_jobs; DROP TABLE IF EXISTS assigned_resources; DROP TABLE IF EXISTS resources; DROP TABLE IF EXISTS resource_logs; DROP TABLE IF EXISTS queues; DROP TABLE IF EXISTS scheduler; DROP TABLE IF EXISTS admission_rules; DROP TABLE IF EXISTS gantt_jobs_predictions; DROP TABLE IF EXISTS gantt_jobs_predictions_visu; DROP TABLE IF EXISTS gantt_jobs_predictions_log; DROP TABLE IF EXISTS gantt_jobs_resources; DROP TABLE IF EXISTS gantt_jobs_resources_visu; DROP TABLE IF EXISTS gantt_jobs_resources_log; DROP TABLE IF EXISTS files; DROP TABLE IF EXISTS event_logs; DROP TABLE IF EXISTS event_log_hostnames; DROP TABLE IF EXISTS accounting; DROP TABLE IF EXISTS job_dependencies; ./oar-2.5.2/sources/core/database/mysql_default_admission_rules.sql0000644000175000017500000002026511757171206023617 0ustar plbplb# Default admission rules for OAR 2 # $Id$ DROP TABLE IF EXISTS admission_rules; CREATE TABLE IF NOT EXISTS admission_rules ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, rule TEXT NOT NULL, PRIMARY KEY (id) ); # Default admission rules # Specify the default value for queue parameter INSERT IGNORE INTO admission_rules (rule) VALUES ('if (not defined($queue_name)) {$queue_name="default";}'); # Prevent root and oar to submit jobs. INSERT IGNORE INTO admission_rules (rule) VALUES ('die ("[ADMISSION RULE] root and oar users are not allowed to submit jobs.\\n") if ( $user eq "root" or $user eq "oar" );'); # Avoid users except admin to go in the admin queue INSERT IGNORE INTO admission_rules (rule) VALUES (' my $admin_group = "admin"; if ($queue_name eq "admin") { my $members; (undef,undef,undef, $members) = getgrnam($admin_group); my %h = map { $_ => 1 } split(/\\s+/,$members); if ( $h{$user} ne 1 ) { {die("[ADMISSION RULE] Only member of the group ".$admin_group." can submit jobs in the admin queue\\n");} } } '); # Prevent the use of system properties INSERT IGNORE INTO admission_rules (rule) VALUES (' my @bad_resources = ("type","state","next_state","finaud_decision","next_finaud_decision","state_num","suspended_jobs","besteffort","deploy","expiry_date","desktop_computing","last_job_date","available_upto","scheduler_priority"); foreach my $mold (@{$ref_resource_list}){ foreach my $r (@{$mold->[0]}){ my $i = 0; while (($i <= $#{$r->{resources}})){ if (grep(/^$r->{resources}->[$i]->{resource}$/i, @bad_resources)){ die("[ADMISSION RULE] \'$r->{resources}->[$i]->{resource}\' resource is not allowed\\n"); } $i++; } } } '); # Force besteffort jobs to run in the besteffort queue # Force job of the besteffort queue to be of the besteffort type # Force besteffort jobs to run on nodes with the besteffort property INSERT IGNORE INTO admission_rules (rule) VALUES (' if (grep(/^besteffort$/, @{$type_list}) and not $queue_name eq "besteffort"){ $queue_name = "besteffort"; print("[ADMISSION RULE] Automatically redirect in the besteffort queue\\n"); } if ($queue_name eq "besteffort" and not grep(/^besteffort$/, @{$type_list})) { push(@{$type_list},"besteffort"); print("[ADMISSION RULE] Automatically add the besteffort type\\n"); } if (grep(/^besteffort$/, @{$type_list})){ if ($jobproperties ne ""){ $jobproperties = "($jobproperties) AND besteffort = \\\'YES\\\'"; }else{ $jobproperties = "besteffort = \\\'YES\\\'"; } print("[ADMISSION RULE] Automatically add the besteffort constraint on the resources\\n"); } '); # Verify if besteffort jobs are not reservations INSERT IGNORE INTO admission_rules (rule) VALUES (' if ((grep(/^besteffort$/, @{$type_list})) and ($reservationField ne "None")){ die("[ADMISSION RULE] Error: a job cannot both be of type besteffort and be a reservation.\\n"); } '); # Force deploy jobs to go on resources with the deploy property INSERT IGNORE INTO admission_rules (rule) VALUES (' if (grep(/^deploy$/, @{$type_list})){ if ($jobproperties ne ""){ $jobproperties = "($jobproperties) AND deploy = \\\'YES\\\'"; }else{ $jobproperties = "deploy = \\\'YES\\\'"; } } '); # Prevent deploy and allow_classic_ssh type jobs on none entire nodes INSERT IGNORE INTO admission_rules (rule) VALUES (' my @bad_resources = ("core","cpu","resource_id",); if (grep(/^(deploy|allow_classic_ssh)$/, @{$type_list})){ foreach my $mold (@{$ref_resource_list}){ foreach my $r (@{$mold->[0]}){ my $i = 0; while (($i <= $#{$r->{resources}})){ if (grep(/^$r->{resources}->[$i]->{resource}$/i, @bad_resources)){ die("[ADMISSION RULE] \'$r->{resources}->[$i]->{resource}\' resource is not allowed with a deploy or allow_classic_ssh type job\\n"); } $i++; } } } } '); # Force desktop_computing jobs to go on nodes with the desktop_computing property INSERT IGNORE INTO admission_rules (rule) VALUES (' if (grep(/^desktop_computing$/, @{$type_list})){ print("[ADMISSION RULE] Added automatically desktop_computing resource constraints\\n"); if ($jobproperties ne ""){ $jobproperties = "($jobproperties) AND desktop_computing = \\\'YES\\\'"; }else{ $jobproperties = "desktop_computing = \\\'YES\\\'"; } }else{ if ($jobproperties ne ""){ $jobproperties = "($jobproperties) AND desktop_computing = \\\'NO\\\'"; }else{ $jobproperties = "desktop_computing = \\\'NO\\\'"; } } '); # Limit the number of reservations that a user can do. # (overrided on user basis using the file: ~oar/unlimited_reservation.users) INSERT IGNORE INTO admission_rules (rule) VALUES (' if ($reservationField eq "toSchedule") { my $unlimited=0; if (open(FILE, "< $ENV{HOME}/unlimited_reservation.users")) { while (){ if (m/^\\s*$user\\s*$/m){ $unlimited=1; } } close(FILE); } if ($unlimited > 0) { print("[ADMISSION RULE] $user is granted the privilege to do unlimited reservations\\n"); } else { my $max_nb_resa = 2; my $nb_resa = $dbh->do(" SELECT job_id FROM jobs WHERE job_user = \\\'$user\\\' AND (reservation = \\\'toSchedule\\\' OR reservation = \\\'Scheduled\\\') AND (state = \\\'Waiting\\\' OR state = \\\'Hold\\\') "); if ($nb_resa >= $max_nb_resa){ die("[ADMISSION RULE] Error : you cannot have more than $max_nb_resa waiting reservations.\\n"); } } } '); ## How to perform actions if the user name is in a file #INSERT IGNORE INTO admission_rules (rule) VALUES (' #open(FILE, "/tmp/users.txt"); #while (($queue_name ne "admin") and ($_ = )){ # if ($_ =~ m/^\\s*$user\\s*$/m){ # print("[ADMISSION RULE] Change assigned queue into admin\\n"); # $queue_name = "admin"; # } #} #close(FILE); #'); # Limit walltime for interactive jobs INSERT IGNORE INTO admission_rules (rule) VALUES (' my $max_walltime = OAR::IO::sql_to_duration("12:00:00"); if (($jobType eq "INTERACTIVE") and ($reservationField eq "None")){ foreach my $mold (@{$ref_resource_list}){ if ((defined($mold->[1])) and ($max_walltime < $mold->[1])){ print("[ADMISSION RULE] Walltime to big for an INTERACTIVE job so it is set to $max_walltime.\\n"); $mold->[1] = $max_walltime; } } } '); # specify the default walltime if it is not specified INSERT IGNORE INTO admission_rules (rule) VALUES (' my $default_wall = OAR::IO::sql_to_duration("2:00:00"); foreach my $mold (@{$ref_resource_list}){ if (!defined($mold->[1])){ print("[ADMISSION RULE] Set default walltime to $default_wall.\\n"); $mold->[1] = $default_wall; } } '); # Check if types given by the user are right INSERT IGNORE INTO admission_rules (rule) VALUES (' my @types = ("container","inner","deploy","desktop_computing","besteffort","cosystem","idempotent","timesharing","allow_classic_ssh","token\\:xxx=yy"); foreach my $t (@{$type_list}){ my $i = 0; while (($types[$i] ne $t) and ($i <= $#types)){ $i++; } if (($i > $#types) and ($t !~ /^(timesharing|inner|token\\:\\w+\\=\\d+)/)){ die("[ADMISSION RULE] The job type $t is not handled by OAR; Right values are : @types\\n"); } } '); # If resource types are not specified, then we force them to default INSERT IGNORE INTO admission_rules (rule) VALUES (' foreach my $mold (@{$ref_resource_list}){ foreach my $r (@{$mold->[0]}){ my $prop = $r->{property}; if (($prop !~ /[\\s\\(]type[\\s=]/) and ($prop !~ /^type[\\s=]/)){ if (!defined($prop)){ $r->{property} = "type = \\\'default\\\'"; }else{ $r->{property} = "($r->{property}) AND type = \\\'default\\\'"; } } } } print("[ADMISSION RULE] Modify resource description with type constraints\\n"); '); ./oar-2.5.2/sources/core/database/default_data.sql0000644000175000017500000000075311757171206020103 0ustar plbplbINSERT INTO queues (queue_name, priority, scheduler_policy) VALUES ('admin','10','oar_sched_gantt_with_timesharing_and_fairsharing'); INSERT INTO queues (queue_name, priority, scheduler_policy) VALUES ('default','2','oar_sched_gantt_with_timesharing_and_fairsharing'); INSERT INTO queues (queue_name, priority, scheduler_policy) VALUES ('besteffort','0','oar_sched_gantt_with_timesharing_and_fairsharing'); INSERT INTO gantt_jobs_predictions (moldable_job_id , start_time) VALUES ('0','0'); ./oar-2.5.2/sources/core/database/big_smp_admission_rules.sql0000644000175000017500000000730311757171206022364 0ustar plbplb# $Id$ # OAR Admission rules for big shared memory computer # The first rule adds the quantitative concept for the memory property # The second rule aims to match cpus on physical nodes to prevent # dispersion of the jobs if the job is of the type "optimize" # The property "pnode" must have been defined to group resources # (cpus) on "pnodes" (physical nodes). # Memory management rule insert into admission_rules (rule) values (' # Memory of a CPU bank: my $memory_bank_size=1947; foreach my $mold (@{$ref_resource_list}){ foreach my $r (@{$mold->[0]}){ my $i=0; my $cpu=0; my $cpufound=0; # Catch the mem resource foreach my $resource (@{$r->{resources}}) { if ($resource->{resource} eq "mem") { $cpu=int($resource->{value}/$memory_bank_size)+1; print "[MEM ADMISSION RULE] Found a mem property that needs $cpu memory bank(s) of $memory_bank_size\n"; splice (@{$r->{resources}},$i,1); } $i++; } $i=0; # Catch the cpu resource and modify it if necessary foreach my $resource (@{$r->{resources}}) { if ("$resource->{resource}" eq "cpu") { if ($cpu > $resource->{value}) { $resource->{value}=$cpu; print "[MEM ADMISSION RULE] Adapting cpu number to match memory size banks: cpu=$cpu\n"; } $cpufound=1; } $i++; } # If a cpu property doesnt not exist, fix it if necessary if ($cpu && !$cpufound) { print "[MEM ADMISSION RULE] Adding a cpu resource cpu=$cpu\n"; my $resource; $resource->{resource}="cpu"; $resource->{value}=$cpu; push (@{$r->{resources}},$resource); } # Print final resources print "[MEM ADMISSION RULE] Real resources: /"; foreach my $resource (@{$r->{resources}}) { print $resource->{resource} ."=". $resource->{value} ."/"; } print "\n"; } } '); # Physical nodes matching rule insert into admission_rules (rule) values (' # Number of CPU per node my $ncpus=2; if (grep(/^optimize$/, @{$type_list})){ print "[OPTIM ADMISSION RULE] optimize type given. Will try to match resources on physical nodes.\n"; foreach my $mold (@{$ref_resource_list}){ foreach my $r (@{$mold->[0]}){ my $pnode_found=0; my $cpus=0; my $pnodes=0; # Search for an already pnode property foreach my $resource (@{$r->{resources}}) { if ($resource->{resource} eq "pnode") { $pnode_found=1; print "[OPTIM ADMISSION RULE] A pnode property is already given. Nothing to optimize.\n"; } } # Catch the cpu resource and modify it into a pnode resource if possible if (!$pnode_found) { foreach my $resource (@{$r->{resources}}) { if ($resource->{resource} eq "cpu") { $cpus=$resource->{value}; $pnodes=int($cpus/$ncpus); if (($pnodes*$ncpus) == $cpus) { print "[OPTIM ADMISSION RULE] Modifing cpu=$cpus into pnode=$pnodes.\n"; $resource->{resource}="pnode"; $resource->{value}=$pnodes; }else { print "[OPTIM ADMISSION RULE] Unable to match $cpus cpus with pnodes of $ncpus cpus each. Nothing optimized.\n"; } } } } # Print final resources print "[OPTIM ADMISSION RULE] Real resources: /"; foreach my $resource (@{$r->{resources}}) { print $resource->{resource} ."=". $resource->{value} ."/"; } print "\n"; } } } '); ./oar-2.5.2/sources/core/database/README0000644000175000017500000000044211757171206015620 0ustar plbplbThese are choices implemented for the compatibility between MySQL and PostrgreSQL: ================================================================================== * All field and table names must be lowercase. * MySQL AUTO_INCREMENT is replaced by PostgreSQL constraint sequence field. ./oar-2.5.2/sources/core/common-libs/0000755000175000017500000000000011757171206015413 5ustar plbplb./oar-2.5.2/sources/core/common-libs/lib/0000755000175000017500000000000011757171206016161 5ustar plbplb./oar-2.5.2/sources/core/common-libs/lib/OAR/0000755000175000017500000000000011757171516016606 5ustar plbplb./oar-2.5.2/sources/core/common-libs/lib/OAR/Version.pm0000644000175000017500000000027311757171516020573 0ustar plbplbpackage OAR::Version; require Exporter; my $OARVersion = "2.5.2"; my $OARName = "tzuhtzjo:m uy-u:xlaju:n pik"; sub get_version(){ return $OARVersion." (".$OARName.")"; } return 1; ./oar-2.5.2/sources/core/common-libs/lib/OAR/Tools.pm0000644000175000017500000006232011757171206020243 0ustar plbplb# $Id$ { package OAR::Tools; use IO::Socket::INET; use strict; use POSIX ":sys_wait_h"; use IPC::Open2; use Data::Dumper; use Fcntl; # Constants my $Default_leon_soft_walltime = 20; my $Default_leon_walltime = 300; my $Timeout_ssh = 120; my $Default_server_prologue_epilogue_Timeout = 60; my $bipbip_oarexec_hashtable_send_timeout = 30; my $Default_Dead_switch_time = 0; my $Default_oarexec_directory = "/tmp/oar_runtime/"; my $Oarexec_pid_file_name = "pid_of_oarexec_for_jobId_"; my $Oarsub_file_name_prefix = "oarsub_connections_"; my $Default_prologue_epilogue_timeout = 60; my $Default_suspend_resume_script_timeout = 60; my $Ssh_rendez_vous = "oarexec is initialized and ready to do the job\n"; my $Default_openssh_cmd = "ssh"; my $Default_cpuset_file_manager = "/etc/oar/job_resource_manager.pl"; my $Default_monitor_file_sensor = "/etc/oar/oarmonitor_sensor.pl"; my $Default_suspend_resume_file_manager = "/etc/oar/suspend_resume_manager.pl"; my $Default_oar_ssh_authorized_keys_file = ".ssh/authorized_keys"; my $Default_node_file_db_field = "network_address"; my $Default_node_file_db_field_distinct_values = "resource_id"; my $Default_runner_sliding_window_size = 5; # Prototypes sub get_all_process_children(); sub get_one_process_children($); sub notify_tcp_socket($$$); sub signal_oarexec($$$$$$$); sub get_default_oarexec_directory(); sub set_default_oarexec_directory($); sub get_default_openssh_cmd(); sub get_oar_pid_file_name($); sub get_oar_user_signal_file_name($); sub get_oarsub_connections_file_name($); sub get_ssh_timeout(); sub get_taktuk_timeout(); sub get_default_leon_soft_walltime(); sub get_default_leon_walltime(); sub get_default_dead_switch_time(); sub fork_no_wait($$); sub launch_command($); sub get_default_prologue_epilogue_timeout(); sub get_default_server_prologue_epilogue_timeout(); sub get_default_suspend_resume_file(); sub get_bipbip_ssh_hashtable_send_timeout(); sub get_bipbip_oarexec_rendez_vous(); sub sentinelle($$$$$); sub check_resource_property($); sub check_resource_system_property($); sub get_private_ssh_key_file_name($); sub format_ssh_pub_key($$$$); sub get_default_oar_ssh_authorized_keys_file(); sub get_default_node_file_db_field(); sub get_default_node_file_db_field_distinct_values(); sub replace_jobid_tag_in_string($$); # Get default value for PROLOGUE_EPILOGUE_TIMEOUT sub get_default_prologue_epilogue_timeout(){ return($Default_prologue_epilogue_timeout); } # Get default value for SERVER_PROLOGUE_EPILOGUE_TIMEOUT sub get_default_server_prologue_epilogue_timeout(){ return($Default_server_prologue_epilogue_Timeout); } # Get default value for SUSPEND_RESUME_SCRIPT_TIMEOUT sub get_default_suspend_resume_script_timeout(){ return($Default_suspend_resume_script_timeout); } # Get value for ssh hashtable timeout sub get_bipbip_ssh_hashtable_send_timeout(){ return($bipbip_oarexec_hashtable_send_timeout); } # Get default value for DEAD_SWITCH_TIME tag sub get_default_dead_switch_time(){ return($Default_Dead_switch_time); } # Get default Leon walltime value for Sarko sub get_default_leon_soft_walltime(){ return($Default_leon_soft_walltime); } # Get default Leon soft walltime value for Sarko sub get_default_leon_walltime(){ return($Default_leon_walltime); } # Get default value for OPENSSH_CMD tag sub get_default_openssh_cmd(){ return($Default_openssh_cmd); } # Get default value for JOB_RESOURCE_MANAGER_FILE tag sub get_default_cpuset_file(){ return($Default_cpuset_file_manager); } # Get default value for SUSPEND_RESUME_FILE tag sub get_default_suspend_resume_file(){ return($Default_suspend_resume_file_manager); } # Get then file name where are stored all oar ssh public keys sub get_default_oar_ssh_authorized_keys_file(){ return($Default_oar_ssh_authorized_keys_file); } # Get the name of the DB field to use to fill the OAR_NODE_FILE sub get_default_node_file_db_field(){ return($Default_node_file_db_field); } # Get the name of the DB field to use to fill the OAR_NODE_FILE sub get_default_node_file_db_field_distinct_values(){ return($Default_node_file_db_field_distinct_values); } # Get default value for OARMONITOR_SENSOR_FILE tag sub get_default_monitor_sensor_file(){ return($Default_monitor_file_sensor); } # Get default value for RUNNER_SLIDING_WINDOW_SIZE tag sub get_default_runner_sliding_window_size(){ return($Default_runner_sliding_window_size); } # Get the name of the file of the private ssh key for the given cpuset name sub get_private_ssh_key_file_name($){ my $cpuset_name = shift; return($Default_oarexec_directory.'/'.$cpuset_name.".jobkey"); } # Add right environment variables to the given public key sub format_ssh_pub_key($$$$){ my $key = shift; my $cpuset = shift; my $user = shift; my $job_user = shift; $job_user = $user if (!defined($job_user)); $cpuset = "undef" if (!defined($cpuset)); return('environment="OAR_CPUSET='.$cpuset.'",environment="OAR_JOB_USER='.$job_user.'" '.$key."\n"); } # Return the name of the user on the computing nodes sub format_job_user($$$){ my $user = shift; my $jobid = shift; my $uid = shift; my $res = $user; $res .= '_'.$jobid if (defined($uid)); return($res); } # return a hashtable of all child in arrays and a hashtable with process command names sub get_all_process_children(){ my %process_hash; my %process_cmd_hash; open(CMD, "ps -e -o pid,ppid,args |"); while (){ chomp($_); if ($_ =~ /^\s*(\d+)\s+(\d+)\s+(.+)$/){ if (!defined($process_hash{$2})){ $process_hash{$2} = [$1]; }else{ push(@{$process_hash{$2}}, $1); } $process_cmd_hash{$1} = $3; } } close(CMD); return(\%process_hash,\%process_cmd_hash); } # return an array of children sub get_one_process_children($){ my $pid = shift; my $one_father = $pid; my ($tmp1,$pid_cmd_hash) = get_all_process_children(); my %process_hash = %{$tmp1}; my @child_pids; my @potential_father; while (defined($one_father)){ push(@child_pids, $one_father); #Get children of this process foreach my $i (@{$process_hash{$one_father}}){ push(@potential_father, $i); } $one_father = shift(@potential_father); } return(\@child_pids,$pid_cmd_hash->{$pid}); } # Send a Tag on a socket # args = hostname, socket port, Tag sub notify_tcp_socket($$$){ my $almighty_host = shift; my $almighty_port = shift; my $tag = shift; my $socket = IO::Socket::INET->new(PeerAddr => $almighty_host, PeerPort => $almighty_port, Proto => "tcp", Type => SOCK_STREAM) or return("Could not connect to the socket $almighty_host:$almighty_port"); print($socket "$tag\n") or return("Print $tag failed to $almighty_host:$almighty_port"); close($socket) or return("Socket close failed: $almighty_host:$almighty_port"); return(undef); } # Return the constant SSH timeout to use sub get_ssh_timeout(){ return($Timeout_ssh); } sub set_ssh_timeout($){ $Timeout_ssh = shift; } sub get_taktuk_timeout(){ return($Timeout_ssh * 2); } sub get_default_oarexec_directory(){ return($Default_oarexec_directory); } sub set_default_oarexec_directory($){ $Default_oarexec_directory = shift; } # Get the name of the file which contains the pid of oarexec # arg : job id sub get_oar_pid_file_name($){ my $job_id = shift; return($Default_oarexec_directory."/".$Oarexec_pid_file_name.$job_id); } # Get the name of the file which contains the signal given by the user # arg : job id sub get_oar_user_signal_file_name($){ my $job_id = shift; return($Default_oarexec_directory."/USER_SIGNAL_".$job_id); } # Get the name of the file which contains parent pids of oarsub connections # arg : job id sub get_oarsub_connections_file_name($){ my $job_id = shift; return($Default_oarexec_directory."/".$Oarsub_file_name_prefix.$job_id); } # Replace %jobid% in the string by the given job id # args: string, job id sub replace_jobid_tag_in_string($$){ my $str = shift; my $job_id = shift; $str =~ s/%jobid%/$job_id/g; return($str); } # Send the given signal to the right oarexec process # args : host name, job id, signal, wait or not (0 or 1), # DB ref (to close it in the child process), ssh cmd, user defined signal # for oardel -s (null by default if not used) # return an array with exit values sub signal_oarexec($$$$$$$){ my $host = shift; my $job_id = shift; my $signal = shift; my $wait = shift; my $base = shift; my $ssh_cmd = shift; my $user_signal = shift; my $file = get_oar_pid_file_name($job_id); #my $cmd = "$ssh_cmd -x -T $host \"test -e $file && cat $file | xargs kill -s $signal\""; #my $cmd = "$ssh_cmd -x -T $host bash -c \"test -e $file && PROC=\\\$(cat $file) && kill -s CONT \\\$PROC && kill -s $signal \\\$PROC\""; my ($cmd_name,@cmd_opts) = split(" ",$ssh_cmd); my @cmd; my $c = 0; $cmd[$c] = $cmd_name;$c++; foreach my $p (@cmd_opts){ $cmd[$c] = $p;$c++; } $cmd[$c] = "-x";$c++; $cmd[$c] = "-T";$c++; $cmd[$c] = $host;$c++; if (defined($user_signal) && $user_signal ne ''){ my $signal_file = OAR::Tools::get_oar_user_signal_file_name($job_id); $cmd[$c] = "bash -c 'echo $user_signal > $signal_file && test -e $file && PROC=\$(cat $file) && kill -s CONT \$PROC && kill -s $signal \$PROC'";$c++; } else { $cmd[$c] = "bash -c 'test -e $file && PROC=\$(cat $file) && kill -s CONT \$PROC && kill -s $signal \$PROC'";$c++; } $SIG{PIPE} = 'IGNORE'; my $pid = fork(); if($pid == 0){ #CHILD undef($base); my $exit_code; my $ssh_pid; eval{ $SIG{PIPE} = 'IGNORE'; $SIG{ALRM} = sub { die "alarm\n" }; alarm(get_ssh_timeout()); $ssh_pid = fork(); if ($ssh_pid == 0){ exec({$cmd_name} @cmd); warn("[ERROR] Cannot find @cmd\n"); exit(-1); } my $wait_res = -1; # Avaoid to be disrupted by a signal while ((defined($ssh_pid)) and ($wait_res != $ssh_pid)){ $wait_res = waitpid($ssh_pid,0); } alarm(0); $exit_code = $?; }; if ($@){ if ($@ eq "alarm\n"){ if (defined($ssh_pid)){ my ($children,$cmd_name) = get_one_process_children($ssh_pid); kill(9,@{$children}); } } } # Exit from child exit($exit_code); } if ($wait > 0){ waitpid($pid,0); my $exit_value = $? >> 8; my $signal_num = $? & 127; my $dumped_core = $? & 128; return($exit_value,$signal_num,$dumped_core); }else{ return(undef); } } # exec a command and do not wait its end # arg : command, DB ref (to close in the child) sub fork_no_wait($$){ my $cmd = shift; my $base = shift; # $ENV{PATH}="/bin:/usr/bin:/usr/local/bin"; my $pid; $SIG{PIPE} = 'IGNORE'; $pid = fork(); if(defined($pid)){ if($pid == 0){ #child undef($base); $SIG{USR1} = 'IGNORE'; $SIG{INT} = 'IGNORE'; $SIG{TERM} = 'IGNORE'; exec($cmd); warn("[ERROR] Cannot find $cmd\n"); exit(-1); } } return($pid); } # exec a command and feed its STDIN # arg : command, data to send, DB ref (to close it in the child) sub fork_and_feed_stdin($$$){ my $cmd = shift; my $timeout = shift; my $feed = shift; my $ret; my $pid; eval{ $SIG{ALRM} = sub { die "alarm\n" }; alarm($timeout); $pid = open(CMDTOFEED, "| $cmd"); foreach my $s (@{$feed}){ print(CMDTOFEED "$s\n"); } $ret = close(CMDTOFEED); alarm(0); }; if ($@){ if ($@ eq "alarm\n"){ if (defined($pid)){ my ($children,$cmd_name) = get_one_process_children($pid); kill(9,@{$children}); } } } return($ret); } # exec a command, wait its end and return exit codes # arg : command sub launch_command($){ my $command = shift; # $ENV{PATH}="/bin:/usr/bin:/usr/local/bin:$ENV{OARDIR}"; system($command); my $exit_value = $? >> 8; my $signal_num = $? & 127; my $dumped_core = $? & 128; return($exit_value,$signal_num,$dumped_core); } # Create the perl script used to execute right command for the user # The resulting script can be launched with : perl -e 'script' sub get_oarexecuser_perl_script_for_oarexec($$$$$$$$$$$$$$$@){ my ($node_file, $job_id, $array_id, $array_index, $user, $shell, $launching_directory, $stdout_file, $stderr_file, $resource_file, $job_name, $job_project, $job_walltime, $job_walltime_sec, $job_env, @cmd) = @_; # if ((defined($job_env)) and ($job_env !~ /^\s*$/)){ # $cmd[0] = "export $job_env;".$cmd[0]; # } # suitable Data::Dumper configuration for serialization $Data::Dumper::Purity = 1; $Data::Dumper::Terse = 1; $Data::Dumper::Indent = 0; $Data::Dumper::Deepcopy = 1; my $cmd_serial = Dumper(\@cmd); my $script = ' $ENV{TERM} = "unknown"; $ENV{BASH_ENV} = "$ENV{HOME}/.bashrc"; $ENV{OAR_STDOUT} = "'.$stdout_file.'"; $ENV{OAR_STDERR} = "'.$stderr_file.'"; $ENV{OAR_FILE_NODES} = "'.$node_file.'"; $ENV{OAR_JOBID} = '.$job_id.'; $ENV{OAR_ARRAYID} = '.$array_id.'; $ENV{OAR_ARRAYINDEX} = '.$array_index.'; $ENV{OAR_USER} = "'.$user.'"; $ENV{OAR_WORKDIR} = "'.$launching_directory.'"; $ENV{OAR_RESOURCE_PROPERTIES_FILE} = "'.$resource_file.'"; $ENV{OAR_JOB_NAME} = "'.$job_name.'"; $ENV{OAR_PROJECT_NAME} = "'.$job_project.'"; $ENV{OAR_JOB_WALLTIME} = "'.$job_walltime.'"; $ENV{OAR_JOB_WALLTIME_SECONDS} = '.$job_walltime_sec.'; $ENV{OAR_NODEFILE} = $ENV{OAR_FILE_NODES}; $ENV{OAR_O_WORKDIR} = $ENV{OAR_WORKDIR}; $ENV{OAR_NODE_FILE} = $ENV{OAR_FILE_NODES}; $ENV{OAR_RESOURCE_FILE} = $ENV{OAR_FILE_NODES}; $ENV{OAR_WORKING_DIRECTORY} = $ENV{OAR_WORKDIR}; $ENV{OAR_JOB_ID} = $ENV{OAR_JOBID}; $ENV{OAR_ARRAY_ID} = $ENV{OAR_ARRAYID}; $ENV{OAR_ARRAY_INDEX} = $ENV{OAR_ARRAYINDEX}; $ENV{SUDO_COMMAND}="OAR"; delete($ENV{SSH_CLIENT}); delete($ENV{SSH2_CLIENT}); $ENV{SHLVL}=0; # Test if we can go into the launching directory if (!chdir($ENV{OAR_WORKING_DIRECTORY})){ exit(1); } my $shell_executed = "'.$shell.'"; #Store old FD open(OLDSTDOUT, ">& STDOUT"); open(OLDSTDERR, ">& STDERR"); #Test if we can write into stdout and stderr files if ((!open(STDOUT, ">>$ENV{OAR_STDOUT}")) or (!open(STDERR, ">>$ENV{OAR_STDERR}"))){ exit(2); } # Get user command by the STDIN (for SECURITY) my $tmp = ""; while (){ $tmp .= $_; } my $cmd_exec = eval($tmp); my $pid = fork; if($pid == 0){ # To be sure that user do not try to do something nasty close(OLDSTDERR); close(OLDSTDOUT); exec({$shell_executed} $shell_executed,"-c",@{$cmd_exec}); # if exec do not find the command warn("[OAR] Cannot find @{$cmd_exec}\n"); exit(-1); } waitpid($pid,0); print(OLDSTDOUT "EXIT_CODE $?"); close(STDOUT); close(STDERR); exit(0); '; return($script,$cmd_serial); } # Create the shell script used to execute right command for the user # The resulting script can be launched with : bash -c 'script' sub get_oarexecuser_script_for_oarsub($$$$$$$$$$$$$){ my ($node_file, $job_id, $array_id, $array_index, $user, $shell, $launching_directory, $resource_file, $job_name, $job_project, $job_walltime, $job_walltime_sec, $job_env) = @_; my $exp_env = ""; if ($job_env !~ /^\s*$/){ #$job_env =~ s/\"/\\"/g; $exp_env .= "export $job_env ;"; } my $script = ' if [ "a$TERM" == "a" ] || [ "x$TERM" == "xunknown" ]; then export TERM=xterm; fi; '.$exp_env.' export OAR_FILE_NODES="'.$node_file.'"; export OAR_JOBID='.$job_id.'; export OAR_ARRAYID='.$array_id.'; export OAR_ARRAYINDEX='.$array_index.'; export OAR_USER="'.$user.'"; export OAR_WORKDIR="'.$launching_directory.'"; export OAR_RESOURCE_PROPERTIES_FILE="'.$resource_file.'"; export OAR_NODEFILE=$OAR_FILE_NODES; export OAR_O_WORKDIR=$OAR_WORKDIR; export OAR_NODE_FILE=$OAR_FILE_NODES; export OAR_RESOURCE_FILE=$OAR_FILE_NODES; export OAR_WORKING_DIRECTORY=$OAR_WORKDIR; export OAR_JOB_ID=$OAR_JOBID; export OAR_ARRAY_ID=$OAR_ARRAYID; export OAR_ARRAY_INDEX=$OAR_ARRAYINDEX; export OAR_JOB_NAME="'.$job_name.'"; export OAR_PROJECT_NAME="'.$job_project.'"; export OAR_JOB_WALLTIME="'.$job_walltime.'"; export OAR_JOB_WALLTIME_SECONDS='.$job_walltime_sec.'; export SHELL="'.$shell.'"; export SUDO_COMMAND=OAR; SHLVL=1; if ( cd $OAR_WORKING_DIRECTORY &> /dev/null ); then cd $OAR_WORKING_DIRECTORY; else exit 2; fi; (exec -a -${SHELL##*/} $SHELL); exit 0 '; return($script); } sub get_bipbip_oarexec_rendez_vous(){ return($Ssh_rendez_vous); } # Execute comands with a specified timeout and a maximum number in the same time : window. # Aavoid to overload the computer. # args : window size, timeout, command to execute sub sentinelle($$$$$){ my $window = shift; my $timeout = shift; my $nodes = shift; my $input_string = shift; my $base = shift; my @bad_nodes; my $index = 0; my %running_processes; my $nb_running_processes = 0; my %finished_processes; # Start to launch subprocesses with the window limitation my @timeout; my $pid; while (($index <= $#$nodes) or ($#timeout >= 0)){ # Check if window is full or not while((($nb_running_processes) < $window) and ($index <= $#$nodes)){ $SIG{PIPE} = 'IGNORE'; $pid = fork(); if (defined($pid)){ $running_processes{$pid} = $index; $nb_running_processes++; push(@timeout, [$pid,time()+$timeout]); if ($pid == 0){ #In the child undef($base); if (defined($input_string)){ my $cmd_pid = open(HANDLE, "| $nodes->[$index]"); $SIG{USR2} = sub {kill(9,$cmd_pid)}; print(HANDLE $input_string); close(HANDLE); my $exit_value = $? >> 8; my $signal_num = $? & 127; my $dumped_core = $? & 128; if (($exit_value != 0) or ($signal_num != 0) or ($dumped_core != 0)){ exit(1); }else{ exit(0); } }else{ exec($nodes->[$index]); warn("[ERROR] Cannot find $nodes->[$index]\n"); exit(-1); } } }else{ push(@bad_nodes, $index); } $index++; } # Check ended proceses while(($pid = waitpid(-1, WNOHANG)) > 0) { my $exit_value = $? >> 8; my $signal_num = $? & 127; my $dumped_core = $? & 128; if ($pid > 0){ if (defined($running_processes{$pid})){ if (($exit_value != 0) or ($signal_num != 0) or ($dumped_core != 0)){ push(@bad_nodes, $running_processes{$pid}); } delete($running_processes{$pid}); $nb_running_processes--; } } } my $t = 0; while(defined($timeout[$t]) and (($timeout[$t]->[1] <= time()) or (!defined($running_processes{$timeout[$t]->[0]})))){ if (!defined($running_processes{$timeout[$t]->[0]})){ splice(@timeout,$t,1); }else{ if ($timeout[$t]->[1] <= time()){ # DRING, timeout !!! if (defined($input_string)){ kill(12,$timeout[$t]->[0]); }else{ kill(9,$timeout[$t]->[0]); } } } $t++; } select(undef,undef,undef,0.1) if ($t == 0); } return(@bad_nodes); } # Check if a property can be deleted or created by a user # return 0 if all is good otherwise return 1 sub check_resource_property($){ my $prop = shift; if ($prop =~ /^(resource_id|network_address|state|state_num|next_state|finaud_decision|next_finaud_decision|besteffort|desktop_computing|deploy|expiry_date|last_job_date|available_upto|walltime|nodes|type|suspended_jobs|scheduler_priority)$/){ return(1); }else{ return(0); } } # Check if a property can be manipulated by a user # return 0 if all is good otherwise return 1 sub check_resource_system_property($){ my $prop = shift; if ($prop =~ /^(resource_id|state|state_num|next_state|finaud_decision|next_finaud_decision|last_job_date|suspended_jobs|expiry_date|scheduler_priority)$/ ) { return(1); }else{ return(0); } } # Manage commands on several nodes like cpuset or suspend job # args : array of host to connect to, hashtable to transfer, name of the file containing the perl script, action to perform (start or stop), SSH command to use, taktuk cmd or undef, database ref sub manage_remote_commands($$$$$$$){ my $connect_hosts = shift; my $data_hash = shift; my $manage_file = shift; my $action = shift; my $ssh_cmd = shift; my $taktuk_cmd = shift; my $base = shift; my @bad; $ssh_cmd = $Default_openssh_cmd if (!defined($ssh_cmd)); # Prepare commands to run on each node my $string_to_transfer; open(FILE, $manage_file) or return(0,undef); while(){ $string_to_transfer .= $_; } close(FILE); $string_to_transfer .= "__END__\n"; # suitable Data::Dumper configuration for serialization $Data::Dumper::Purity = 1; $Data::Dumper::Terse = 1; $Data::Dumper::Indent = 0; $Data::Dumper::Deepcopy = 1; $string_to_transfer .= Dumper($data_hash); if (!defined($taktuk_cmd)){ # Dispatch via sentinelle my @node_commands; my @node_corresponding; foreach my $n (@{$connect_hosts}){ my $cmd = "$ssh_cmd -x -T $n TAKTUK_HOSTNAME=$n perl - $action"; push(@node_commands, $cmd); push(@node_corresponding, $n); } my @bad_tmp = sentinelle(10,get_ssh_timeout(), \@node_commands, $string_to_transfer,$base); foreach my $b (@bad_tmp){ push(@bad, $node_corresponding[$b]); } }else{ # Dispatch via taktuk my %tmp_node_hash; pipe(tak_node_read,tak_node_write); pipe(tak_stdin_read,tak_stdin_write); pipe(tak_stdout_read,tak_stdout_write); my $pid = fork; if($pid == 0){ #CHILD $SIG{CHLD} = 'DEFAULT'; $SIG{TERM} = 'DEFAULT'; $SIG{INT} = 'DEFAULT'; $SIG{QUIT} = 'DEFAULT'; $SIG{USR1} = 'DEFAULT'; $SIG{USR2} = 'DEFAULT'; my $cmd = "$taktuk_cmd -c '$ssh_cmd' ".'-o status=\'"STATUS $host $line\n"\''." -f '<&=".fileno(tak_node_read)."' broadcast exec [ perl - $action ], broadcast input file [ - ], broadcast input close"; fcntl(tak_node_read, F_SETFD, 0); close(tak_node_write); close(tak_stdout_read); close(STDOUT); # Redirect taktuk output into the pipe open(STDOUT, ">& tak_stdout_write"); # Use the child STDIN to send the user command close(tak_stdin_write); close(STDIN); open(STDIN, "<& tak_stdin_read"); exec($cmd); warn("[ERROR] Cannot execute $cmd\n"); exit(-1); } close(tak_node_read); close(tak_stdin_read); close(tak_stdout_write); # Send node list foreach my $n (@{$connect_hosts}){ $tmp_node_hash{$n} = 1; print(tak_node_write "$n\n"); } close(tak_node_write); eval{ $SIG{ALRM} = sub { kill(19,$pid); die "alarm\n" }; alarm(OAR::Tools::get_taktuk_timeout()); # Send data structure to all nodes print(tak_stdin_write $string_to_transfer); close(tak_stdin_write); # Check good nodes from the stdout taktuk while(){ if ($_ =~ /^STATUS ([\w\.\-\d]+) (\d+)$/){ if ($2 == 0){ delete($tmp_node_hash{$1}) if (defined($tmp_node_hash{$1})); } }else{ print("[TAKTUK OUTPUT] $_"); } } close(tak_stdout_read); waitpid($pid,0); alarm(0); }; if ($@){ if (defined($pid)){ # Kill all taktuk children my ($children,$cmd_name) = get_one_process_children($pid); kill(9,@{$children}); } } @bad = keys(%tmp_node_hash); } return(1,@bad); } 1; } ./oar-2.5.2/sources/core/common-libs/lib/OAR/Sub.pm0000644000175000017500000002677011757171206017705 0ustar plbplbpackage OAR::Sub; use strict; use warnings; use OAR::Version; use OAR::Tools; use OAR::IO; use IO::Socket::INET; my $base; sub open_db_connection(){ $base = OAR::IO::connect(); } sub open_ro_db_connection(){ $base = OAR::IO::connect_ro(); } sub close_db_connection(){ OAR::IO::disconnect($base); $base = undef; } sub lock_tables($){ my $tables_to_lock = shift; OAR::IO::lock_table($base,$tables_to_lock); } sub unlock_tables(){ OAR::IO::unlock_table($base); } sub encode_result($$){ my $result = shift or die("[OAR::Nodes] encode_result: no result to encode"); my $encoding = shift or die("[OAR::Nodes] encode_result: no format to encode to"); if($encoding eq "XML"){ eval "use XML::Dumper qw(pl2xml);1" or die ("XML module not available"); my $dump = new XML::Dumper; $dump->dtd; my $return = $dump->pl2xml($result) or die("XML conversion failed"); return $return; }elsif($encoding eq "YAML"){ eval "use YAML;1" or die ("YAML module not available"); my $return = YAML::Dump($result) or die("YAML conversion failed"); return $return; }elsif($encoding eq "JSON"){ eval "use JSON;1" or die ("JSON module not available"); my $return = JSON->new->pretty(1)->encode($result) or die("JSON conversion failed"); return $return; } } sub get_oar_version(){ return OAR::Version::get_version(); } sub signal_almighty($$$){ my $almighty_hostname = shift; my $almighty_port = shift; my $message = shift; OAR::Tools::notify_tcp_socket($almighty_hostname,$almighty_port,$message); } sub get_job($){ my $job_id = shift; return OAR::IO::get_job($base, $job_id); } sub frag_job($){ my $job = shift; return OAR::IO::frag_job($base,$job); } #Used when we must have a response from the server sub init_tcp_server(){ my $server = IO::Socket::INET->new( Proto => 'tcp', Reuse => 1, Listen => 1 ) or die("/!\\ Cannot initialize a TCP socket server.\n"); my $server_port = $server->sockport(); return($server,$server_port); } #Read user script and extract OAR submition options sub scan_script($$){ my $file = shift; my $Initial_request_string = shift; my %result; my $error = 0; ($file) = split(" ",$file); my $lusr= $ENV{OARDO_USER}; $ENV{OARDO_BECOME_USER} = $lusr; if (open(FILE, "oardodo cat $file |")){ if ( =~ /^#/){ while () { if ( /^#OAR\s+/ ){ my $line = $_; if ($line =~ m/^#OAR\s+(-l|--resource)\s*(.+)\s*$/m){ push(@{$result{resources}}, $2); }elsif ($line =~ m/^#OAR\s+(-q|--queue)\s*(.+)\s*$/m) { $result{queue} = $2; }elsif ($line =~ m/^#OAR\s+(-p|--property)\s*(.+)\s*$/m) { $result{property} = $2; }elsif ($line =~ m/^#OAR\s+(--checkpoint)\s*(\d+)\s*$/m) { $result{checkpoint} = $2; }elsif ($line =~ m/^#OAR\s+(--notify)\s*(.+)\s*$/m) { $result{notify} = $2; }elsif ($line =~ m/^#OAR\s+(-t|--type)\s*(.+)\s*$/m) { push(@{$result{types}}, $2); }elsif ($line =~ m/^#OAR\s+(-d|--directory)\s*(.+)\s*$/m) { $result{directory} = $2; }elsif ($line =~ m/^#OAR\s+(-n|--name)\s*(.+)\s*$/m) { $result{name} = $2; }elsif ($line =~ m/^#OAR\s+(--project)\s*(.+)\s*$/m) { $result{project} = $2; }elsif ($line =~ m/^#OAR\s+(--hold)\s*$/m) { $result{hold} = 1; }elsif ($line =~ m/^#OAR\s+(-a|--anterior)\s*(\d+)\s*$/m) { push(@{$result{anterior}}, $2); }elsif ($line =~ m/^#OAR\s+(--signal)\s*(\d+)\s*$/m) { $result{signal} = $2; }elsif ($line =~ m/^#OAR\s+(-O|--stdout)\s*(.+)\s*$/m) { $result{stdout} = $2; }elsif ($line =~ m/^#OAR\s+(-E|--stderr)\s*(.+)\s*$/m) { $result{stderr} = $2; }elsif ($line =~ m/^#OAR\s+(-k|--use-job-key)\s*$/m) { $result{usejobkey} = 1; }elsif ($line =~ m/^#OAR\s+(--import-job-key-inline-priv)\s*(.+)\s*$/m) { $result{importjobkeyinlinepriv} = $2; }elsif ($line =~ m/^#OAR\s+(-i|--import-job-key-from-file)\s*(.+)\s*$/m) { $result{importjobkeyfromfile} = $2; }elsif ($line =~ m/^#OAR\s+(-e|--export-job-key-to-file)\s*(.+)\s*$/m) { $result{exportjobkeytofile} = $2; }elsif ($line =~ m/^#OAR\s+(-s|--stagein)\s*(.+)\s*$/m) { $result{stagein} = $2; }elsif ($line =~ m/^#OAR\s+(--stagein-md5sum)\s*(.+)\s*$/m) { $result{stageinmd5sum} = $2; }elsif ($line =~ m/^#OAR\s+(--array)\s*(\d+)\s*$/m) { $result{array} = $2; }elsif ($line =~ m/^#OAR\s+(--array-param-file)\s*(.+)\s*$/m) { $result{arrayparamfile} = $2; }else{ warn("/!\\ Not able to scan file line: $line"); $error++; } chop($line); $Initial_request_string .= "; $line"; } } } if (!close(FILE)){ warn("[ERROR] Cannot open the file $file. Check if it is readable by everybody (744).\n"); exit(12); } }else{ warn("[ERROR] Cannot execute: oardodo cat $file\n"); exit(12); } if ($error > 0){ warn("[ERROR] $error error(s) encountered while parsing the file $file.\n"); exit(12); } $result{initial_request} = $Initial_request_string; return(\%result); } sub read_array_param_file($){ my $array_param_file = shift; my @array_params; if (open(PARAMETER_FILE, "oardodo cat $array_param_file |")){ while() { s/#.*//; # ignore comments by erasing them next if /^\s*$/; # skip blank lines chomp; # remove trailing newline characters push (@array_params, $_); } if (!close(PARAMETER_FILE)){ warn("[ERROR] Cannot open the parameter file $array_param_file.\n"); exit(12); } }else{ warn("[ERROR] Cannot execute: oardodo cat $array_param_file\n"); exit(12); } return \@array_params; } sub get_job_current_hostnames($){ my $job_id = shift; my @return = OAR::IO::get_job_current_hostnames($base,$job_id); return \@return; } sub get_current_job_types($){ my $job_id = shift; return OAR::IO::get_current_job_types($base,$job_id); } sub get_job_cpuset_name($){ my $job_id = shift; return OAR::IO::get_job_cpuset_name($base,$job_id); } sub get_current_moldable_job($){ my $moldable_job_id = shift; return OAR::IO::get_current_moldable_job($base,$moldable_job_id); } sub get_job_cpuset_uid($$$){ my $job_id = shift; my $resource_type = shift; my $cpuset_field = shift; return OAR::IO::get_job_cpuset_uid($base, $job_id, $resource_type, $cpuset_field); } sub format_job_user($$$){ my $user = shift; my $job_id = shift; my $uid = shift; return OAR::Tools::format_job_user($user, $job_id, $uid); } sub get_default_oarexec_directory(){ return OAR::Tools::get_default_oarexec_directory(); } sub set_default_oarexec_directory($){ my $dir = shift; OAR::Tools::set_default_oarexec_directory($dir); } sub get_oarsub_connections_file_name($){ my $job_id = shift; return OAR::Tools::get_oarsub_connections_file_name($job_id); } sub get_default_openssh_cmd(){ return OAR::Tools::get_default_openssh_cmd(); } sub set_ssh_timeout($){ my $timeout = shift; OAR::Tools::set_ssh_timeout($timeout); } sub get_oarexecuser_script_for_oarsub($){ my $params = shift; my $node_file = $params->{node_file}; my $job_id = $params->{job_id}; my $array_id = $params->{array_id}; my $array_index = $params->{array_index}; my $user = $params->{user}; my $shell = $params->{shell}; my $launching_directory = $params->{launching_directory}; my $resource_file = $params->{resource_file}; my $job_name = $params->{job_name}; my $job_project = $params->{job_project}; my $job_walltime = $params->{job_walltime}; my $job_walltime_sec = $params->{job_walltime_sec}; my $job_env = $params->{job_env}; return OAR::Tools::get_oarexecuser_script_for_oarsub($node_file, $job_id, $array_id, $array_index, $user, $shell, $launching_directory, $resource_file, $job_name, $job_project, $job_walltime, $job_walltime_sec, $job_env); } sub signal_oarexec($){ my $params = shift; my $host = $params->{host}; my $job_id = $params->{job_id}; my $signal = $params->{signal}; my $wait = $params->{time_to_wait}; my $base = undef; my $ssh_cmd = $params->{ssh_cmd}; return OAR::Tools::signal_oarexec($host, $job_id, $signal, $wait, $base, $ssh_cmd, ''); } sub duration_to_sql($){ my $duration = shift; return OAR::IO::duration_to_sql($duration); } sub sql_to_duration($){ my $date = shift; return OAR::IO::sql_to_duration($date); } sub sql_to_local($){ my $date = shift; return OAR::IO::sql_to_local($date); } sub resubmit_job($){ my $job_id = shift; return OAR::IO::resubmit_job($base, $job_id); } sub get_lock($$){ my $mutex = shift; my $timeout = shift; return OAR::IO::get_lock($base,$mutex,$timeout); } sub release_lock($){ my $mutex = shift; return OAR::IO::release_lock($base,$mutex); } sub get_stagein_id($){ my $md5sum = shift; return OAR::IO::get_stagein_id($base, $md5sum); } sub set_stagein($) { my $params = shift; my $md5sum = $params->{md5sum}; my $location = $params->{location}; my $method = $params->{method}; my $compression = $params->{compression}; my $size = $params->{size}; return OAR::IO::set_stagein($base, $md5sum, $location, $method, $compression, $size); } sub get_job_array_id($){ my $job_id = shift; return OAR::IO::get_job_array_id($base, $job_id); } sub add_micheline_job{ my ($jobType, $ref_resource_list, $command, $infoType, $queue_name, $jobproperties, $startTimeReservation, $idFile, $checkpoint, $checkpoint_signal, $notify, $job_name,$job_env,$type_list,$launching_directory,$anterior_ref,$stdout,$stderr,$job_hold,$project,$use_job_key,$import_job_key_inline,$import_job_key_file,$export_job_key_file,$initial_request_string, $array_job_nb,$array_params_ref) = @_; my $base_ro = OAR::IO::connect_ro(); return OAR::IO::add_micheline_job($base, $base_ro, $jobType, $ref_resource_list, $command, $infoType, $queue_name, $jobproperties, $startTimeReservation, $idFile, $checkpoint, $checkpoint_signal, $notify, $job_name,$job_env,$type_list,$launching_directory,$anterior_ref,$stdout,$stderr,$job_hold,$project,$use_job_key,$import_job_key_inline,$import_job_key_file,$export_job_key_file,$initial_request_string, $array_job_nb,$array_params_ref); } sub delete_jobs($$$){ my $job_ids = shift; my $remote_host = shift; my $remote_port = shift; open_db_connection(); lock_tables(["frag_jobs","event_logs","jobs"]); foreach my $Job_id (@{$job_ids}) { warn("Deleting the job $Job_id ...\n"); my $err = frag_job($Job_id); } unlock_tables(); close_db_connection(); warn("Job(s) deleted\n"); #Signal Almigthy signal_almighty($remote_host,$remote_port,"Qdel"); } 1; ./oar-2.5.2/sources/core/common-libs/lib/OAR/Stat.pm0000644000175000017500000004224611757171206020063 0ustar plbplbpackage OAR::Stat; use strict; use warnings; use Data::Dumper; use OAR::Version; use OAR::IO; use OAR::Conf qw(init_conf dump_conf get_conf is_conf); my $base; # Read config init_conf($ENV{OARCONFFILE}); my $Cpuset_field = get_conf("JOB_RESOURCE_MANAGER_PROPERTY_DB_FIELD"); my $Job_uid_resource_type = get_conf("JOB_RESOURCE_MANAGER_JOB_UID_TYPE"); sub open_db_connection(){ $base = OAR::IO::connect_ro_one(); if (defined($base)) { return 1; } else {return 0; } } sub close_db_connection(){ OAR::IO::disconnect($base); } sub get_oar_version(){ return OAR::Version::get_version(); } sub local_to_sql($){ my $date = shift; return OAR::IO::local_to_sql($date); } sub sql_to_local($){ my $date = shift; return OAR::IO::sql_to_local($date); } sub duration_to_sql($) { my $duration = shift; return OAR::IO::duration_to_sql($duration); } sub set_quote($) { my $string = shift; my $result; if (defined ($base)) { $result = $base->quote($string); } else { open_db_connection(); $result = $base->quote($string); close_db_connection(); } return $result; } sub get_jobs_with_given_properties { my $sql_property = shift; my @jobs; my @jobs_with_given_properties = OAR::IO::get_jobs_with_given_properties($base,$sql_property); push( @jobs, @jobs_with_given_properties ); return \@jobs; } sub get_accounting_summary($$$$){ my $start = shift; my $stop = shift; my $user = shift; my $sql_property = shift; return OAR::IO::get_accounting_summary($base,$start,$stop,$user,$sql_property); } sub get_accounting_summary_byproject($$$){ my $start = shift; my $stop = shift; my $user = shift; return OAR::IO::get_accounting_summary_byproject($base,$start,$stop,$user); } sub get_array_job_ids($){ my $array_id = shift; my @array_job_ids = OAR::IO::get_array_job_ids($base, $array_id); return @array_job_ids; } sub get_array_subjobs { my $array_id = shift; my @jobs; my @array_subjobs = OAR::IO::get_array_subjobs($base, $array_id); push( @jobs, @array_subjobs ); return \@jobs; } sub get_all_jobs_for_user { my $user = shift; my @jobs; my @states = ("Finishing", "Running", "Resuming", "Suspended", "Launching", "toLaunch", "Waiting", "toAckReservation", "Hold"); my @get_jobs_in_state_for_user; foreach my $current_state (@states){ @get_jobs_in_state_for_user = OAR::IO::get_jobs_in_state_for_user($base, $current_state, $user); push( @jobs, @get_jobs_in_state_for_user ); } return \@jobs; } sub get_jobs_for_user_query { my $user = shift; my $from = shift; my $to = shift; my $state = shift; my $limit = shift; my $offset = shift; my $array = shift; if (defined($state)) { my @states = split(/,/,$state); my $statement; foreach my $s (@states) { $statement .= $base->quote($s); $statement .= ","; } chop($statement); $state = $statement; } my %jobs = OAR::IO::get_jobs_for_user_query($base,$from,$to,$state,$limit,$offset,$user,$array); return (\%jobs); } sub count_jobs_for_user_query { my $user = shift; my $from = shift; my $to = shift; my $state = shift; my $array = shift; if (defined($state)) { my @states = split(/,/,$state); my $statement; foreach my $s (@states) { $statement .= $base->quote($s); $statement .= ","; } chop($statement); $state = $statement; } my $total = OAR::IO::count_jobs_for_user_query($base,$from,$to,$state,$user,undef,undef,$array); return $total; } sub get_all_admission_rules() { my @admission_rules = OAR::IO::list_admission_rules($base); return \@admission_rules; } sub get_requested_admission_rules { my $limit = shift; my $offset = shift; my @rules = OAR::IO::get_requested_admission_rules($base,$limit,$offset); return \@rules; } sub count_all_admission_rules { my $total = OAR::IO::count_all_admission_rules($base); return $total; } sub get_specific_admission_rule { my $rule_id = shift; my $rule; $rule = OAR::IO::get_admission_rule($base,$rule_id); return $rule; } sub add_admission_rule { my $rule = shift; my $id = OAR::IO::add_admission_rule($base,$rule); return $id; } sub delete_specific_admission_rule { my $rule_id = shift; OAR::IO::delete_admission_rule($base,$rule_id); } sub get_duration($){ # Converts a number of seconds in a human readable duration (years,days,hours,mins,secs) my $time=shift; my $seconds; my $minutes; my $hours; my $days; my $years; my $duration=""; $years=int($time/31536000); if ($years==1) { $duration .="1 year ";} elsif ($years) { $duration .="$years years ";}; $days=int($time/86400)%365; if ($days==1) { $duration .="1 day ";} elsif ($days) { $duration .="$days days ";}; $hours=int($time/3600)%24; if ($hours==1) { $duration .="1 hour ";} elsif ($hours) { $duration .="$hours hours ";}; $minutes=int($time/60)%60; if ($minutes==1) { $duration .="1 minute ";} elsif ($minutes) { $duration .="$minutes minutes ";}; $seconds=$time%60; if ($seconds==1) { $duration .="1 second ";} elsif ($seconds) { $duration .="$seconds seconds ";}; if ($duration eq "") {$duration="0 seconds ";}; return $duration; } sub get_events { my $job_ids = shift; my @return; if ( $#$job_ids >= 0 ) { foreach my $j (@$job_ids) { my @events = OAR::IO::get_job_events($base,$j); push @return, @events; } } return \@return; } sub get_gantt { my $gantt_query = shift; if ($gantt_query =~ m/\s*(\d{4}\-\d{1,2}\-\d{1,2})\s+(\d{1,2}:\d{1,2}:\d{1,2})\s*,\s*(\d{4}\-\d{1,2}\-\d{1,2})\s+(\d{1,2}:\d{1,2}:\d{1,2})\s*/m) { my $hist = get_history( "$1 $2", "$3 $4" ); return $hist; }else{ return undef; } } sub get_history($$){ my ($date_start,$date_stop) = @_; $date_start = sql_to_local($date_start); $date_stop = sql_to_local($date_stop); my %hash_dumper_result; my @nodes = OAR::IO::list_resources($base); $hash_dumper_result{resources} = \@nodes; my %job_gantt = OAR::IO::get_jobs_gantt_scheduled($base,$date_start,$date_stop); $hash_dumper_result{jobs} = \%job_gantt; #print(Dumper(%hash_dumper_result)); #print finished or running jobs my %jobs_history = OAR::IO::get_jobs_range_dates($base,$date_start,$date_stop); foreach my $i (keys(%jobs_history)){ my $types = OAR::IO::get_current_job_types($base,$i); if (!defined($job_gantt{$i}) || (defined($types->{besteffort}))){ if (($jobs_history{$i}->{state} eq "Running") || ($jobs_history{$i}->{state} eq "toLaunch") || ($jobs_history{$i}->{state} eq "Suspended") || ($jobs_history{$i}->{state} eq "Resuming") || ($jobs_history{$i}->{state} eq "Launching")){ if (defined($types->{besteffort})){ $jobs_history{$i}->{stop_time} = OAR::IO::get_gantt_visu_date($base); }else{ #This job must be already printed by gantt next; } } $hash_dumper_result{jobs}{$i} = $jobs_history{$i}; } } #print Down or Suspected resources my %dead_resource_dates = OAR::IO::get_resource_dead_range_date($base,$date_start,$date_stop); $hash_dumper_result{dead_resources} = \%dead_resource_dates; return(\%hash_dumper_result); } sub get_last_project_karma($$$) { my $user = shift; my $project = shift; my $date = shift; my @last_karma=OAR::IO::get_last_project_karma($base,$user,$project,$date); return(@last_karma); } #sub get_properties { #my $job_ids = shift; #my $return_hash; #my @resources; #foreach my $j (@$job_ids){ #my @job_resources_properties = OAR::IO::get_job_resources_properties($base, $j); #push ( @resources, @job_resources_properties); #} #foreach my $r (@resources){ #my $hash_resource_properties; #foreach my $p (keys(%{$r})){ #if(OAR::Tools::check_resource_system_property($p) != 1){ #$hash_resource_properties->{$p}= $r->{$p}; #} #} #$return_hash->{$r->{resource_id}} = $hash_resource_properties; #} #return $return_hash; #} sub get_specific_jobs { my $job_ids = shift; my @jobs; foreach my $j (@$job_ids) { my $tmp = OAR::IO::get_job($base, $j); if (defined($tmp)){ push(@jobs, $tmp); } } return \@jobs; } sub get_job_resources($) { my $job_info=shift; my $reserved_resources=[]; my $scheduled_resources=[]; my @assigned_resources; my @assigned_hostnames; if (defined($job_info->{assigned_moldable_job}) && $job_info->{assigned_moldable_job} ne ""){ @assigned_resources = OAR::IO::get_job_resources($base,$job_info->{assigned_moldable_job}); @assigned_hostnames = OAR::IO::get_job_network_address($base,$job_info->{assigned_moldable_job}); } if ($job_info->{reservation} eq "Scheduled" and $job_info->{state} eq "Waiting") { $reserved_resources = OAR::IO::get_gantt_visu_scheduled_job_resources($base,$job_info->{job_id}); } if ($job_info->{reservation} eq "None" and $job_info->{state} eq "Waiting") { $scheduled_resources = OAR::IO::get_gantt_visu_scheduled_job_resources($base,$job_info->{job_id}); } return { assigned_resources => \@assigned_resources, assigned_hostnames => \@assigned_hostnames, reserved_resources => $reserved_resources, scheduled_resources => $scheduled_resources }; } sub get_job_data($$){ my $job_info = shift; my $full_view = shift; my $dbh = $base; my @nodes; my @node_hostnames; my $mold; my @date_tmp; my @job_events; my %data_to_display; my $job_user; my $job_cpuset_uid; my @job_dependencies; my @job_types = OAR::IO::get_job_types($dbh,$job_info->{job_id}); my $cpuset_name; $cpuset_name = OAR::IO::get_job_cpuset_name($dbh, $job_info->{job_id}) if (defined($Cpuset_field)); my $resources_string = ""; my $reserved_resources; if ($job_info->{assigned_moldable_job} ne "" && $job_info->{assigned_moldable_job} ne "0"){ @nodes = OAR::IO::get_job_resources($dbh,$job_info->{assigned_moldable_job}); @node_hostnames = OAR::IO::get_job_network_address($dbh,$job_info->{assigned_moldable_job}); $mold = OAR::IO::get_moldable_job($dbh,$job_info->{assigned_moldable_job}); }else{ # Try to get the moldable description of a waiting job $mold = OAR::IO::get_scheduled_job_description($dbh,$job_info->{job_id}); } if ($job_info->{reservation} eq "Scheduled" and $job_info->{state} eq "Waiting") { $reserved_resources = OAR::IO::get_gantt_visu_scheduled_job_resources($dbh,$job_info->{job_id}); } if (defined($full_view)){ @date_tmp = OAR::IO::get_gantt_job_start_time_visu($dbh,$job_info->{job_id}); @job_events = OAR::IO::get_job_events($dbh,$job_info->{job_id}); @job_dependencies = OAR::IO::get_current_job_dependencies($dbh,$job_info->{job_id}); $job_cpuset_uid = OAR::IO::get_job_cpuset_uid($dbh, $job_info->{assigned_moldable_job}, $Job_uid_resource_type, $Cpuset_field) if ((defined($Job_uid_resource_type)) and (defined($Cpuset_field))); $job_user = OAR::Tools::format_job_user($job_info->{job_user},$job_info->{job_id},$job_cpuset_uid); #Get the job resource description to print -l option my $job_descriptions = OAR::IO::get_resources_data_structure_current_job($dbh,$job_info->{job_id}); foreach my $moldable (@{$job_descriptions}){ my $tmp_str = ""; foreach my $group (@{$moldable->[0]}){ if ($tmp_str ne ""){ # add a new group $tmp_str .= "+"; }else{ # first group $tmp_str .= "-l \""; } if ((defined($group->{property})) and ($group->{property} ne "")){ $tmp_str .= "{$group->{property}}"; } foreach my $resource (@{$group->{resources}}){ my $tmp_val = $resource->{value}; if ($tmp_val == -1){ $tmp_val = "ALL"; }elsif ($tmp_val == -2){ $tmp_val = "BEST"; } $tmp_str .= "/$resource->{resource}=$tmp_val"; } } $tmp_str .= ",walltime=".OAR::IO::duration_to_sql($moldable->[1])."\" "; $resources_string .= $tmp_str; } %data_to_display = ( Job_Id => $job_info->{job_id}, array_id => $job_info->{array_id}, array_index => $job_info->{array_index}, name => $job_info->{job_name}, owner => $job_info->{job_user}, job_user => $job_user, job_uid => $job_cpuset_uid, state => $job_info->{state}, assigned_resources => \@nodes, assigned_network_address => \@node_hostnames, queue => $job_info->{queue_name}, command => $job_info->{command}, launchingDirectory => $job_info->{launching_directory}, jobType => $job_info->{job_type}, properties => $job_info->{properties}, reservation => $job_info->{reservation}, walltime => $mold->{moldable_walltime}, submissionTime => $job_info->{submission_time}, startTime => $job_info->{start_time}, stopTime => $job_info->{stop_time}, message => $job_info->{message}, scheduledStart => $date_tmp[0], resubmit_job_id => $job_info->{resubmit_job_id}, events => \@job_events, wanted_resources => $resources_string, project => $job_info->{project}, cpuset_name => $cpuset_name, types => \@job_types, dependencies => \@job_dependencies, exit_code => $job_info->{exit_code}, stdout_file => OAR::Tools::replace_jobid_tag_in_string($job_info->{stdout_file},$job_info->{job_id}), stderr_file => OAR::Tools::replace_jobid_tag_in_string($job_info->{stderr_file},$job_info->{job_id}), initial_request => "" ); if (($ENV{OARDO_USER} eq $job_info->{job_user}) or ($ENV{OARDO_USER} eq "oar") or ($ENV{OARDO_USER} eq "root")){ $data_to_display{initial_request} = $job_info->{initial_request}; } }else{ %data_to_display = ( Job_Id => $job_info->{job_id}, array_id => $job_info->{array_id}, array_index => $job_info->{array_index}, name => $job_info->{job_name}, owner => $job_info->{job_user}, state => $job_info->{state}, assigned_resources => \@nodes, assigned_network_address => \@node_hostnames, queue => $job_info->{queue_name}, command => $job_info->{command}, launchingDirectory => $job_info->{launching_directory}, stdout_file => OAR::Tools::replace_jobid_tag_in_string($job_info->{stdout_file},$job_info->{job_id}), stderr_file => OAR::Tools::replace_jobid_tag_in_string($job_info->{stderr_file},$job_info->{job_id}), jobType => $job_info->{job_type}, properties => $job_info->{properties}, reservation => $job_info->{reservation}, submissionTime => $job_info->{submission_time}, startTime => $job_info->{start_time}, message => $job_info->{message}, resubmit_job_id => $job_info->{resubmit_job_id}, project => $job_info->{project}, cpuset_name => $cpuset_name, types => \@job_types, dependencies => \@job_dependencies ); } if (defined($reserved_resources)) { $data_to_display{'reserved_resources'}=$reserved_resources; } return(\%data_to_display); } sub get_job_resources_properties($) { my $jobid= shift; my @job_resources_properties = OAR::IO::get_job_resources_properties($base, $jobid); return @job_resources_properties; } sub get_job_state($) { my $idJob = shift; my $state_string = OAR::IO::get_job_state($base,$idJob); return $state_string; } #sub get_default_job_infos($$){ #my $job_array = shift; #my $hashestat = shift; #print "\nDumper job_array: ".Dumper(@$job_array); #print "\nDumper hashestat: ".Dumper(%$hashestat); #my %default_job_infos; #foreach my $job_info (@$job_array){ #print "\nDumper job_info: ".Dumper($job_info); ##$job_info->{'command'} = '' if (!defined($job_info->{'command'})); #$job_info->{job_name} = '' if (!defined($job_info->{job_name})); #print ("\nDEBUG : ".$job_info->{'job_id'}. #"\n".$job_info->{'job_name'}. #"\n".$job_info->{'job_user'}. #"\n".$job_info->{'submission_time'}. #"\n".$job_info->{'state'}. #"\n".$job_info->{'queue_name'}); #$default_job_infos = [ $job_info->{'job_id'}, #$job_info->{'job_name'}, #$job_info->{'job_user'}, #OAR::IO::local_to_sql($job_info->{'submission_time'}), #%$hashestat{$job_info->{'state'}}, #$job_info->{'queue_name'} ]; #} #print "\nDumper default_job_infos: ".Dumper(%default_job_infos); #exit 0; #return(\%default_job_infos); #} 1; ./oar-2.5.2/sources/core/common-libs/lib/OAR/Schedulers/0000755000175000017500000000000011757171206020703 5ustar plbplb./oar-2.5.2/sources/core/common-libs/lib/OAR/Schedulers/ResourceTree.pm0000644000175000017500000003321411757171206023653 0ustar plbplb# $Id$ package OAR::Schedulers::ResourceTree; use warnings; use strict; use Data::Dumper; use Storable qw(dclone); use Time::HiRes qw(gettimeofday); ############################################################################### # RESOURCE TREE MANAGEMENT # ############################################################################### # Prototypes sub new(); sub clone($); sub add_child($$$); sub get_children_list($); sub is_node_a_leaf($); sub get_a_child($$); sub get_father($); sub get_current_resource_name($); sub get_current_resource_value($); sub get_current_children_number($); sub get_current_level($); sub get_max_available_children($); sub set_needed_children_number($$); sub get_needed_children_number($); sub delete_subtree($); sub get_previous_brother($); sub get_next_brother($); sub get_initial_child($); ############################################################################### sub get_tree_leafs($); sub delete_tree_nodes_with_not_enough_resources($); sub delete_unnecessary_subtrees($); ############################################################################### # Create a tree # arg : number of needed children # return the ref of the created tree sub new(){ my $needed_children_number = shift; my $tree_ref; $tree_ref->[0] = undef ; # father ref $tree_ref->[1] = undef ; # ref of a hashtable with children $tree_ref->[2] = undef ; # name of the resource $tree_ref->[3] = undef ; # value of this resource $tree_ref->[4] = 0 ; # level indicator $tree_ref->[5] = 0 ; # needed children number : # -1 means ALL (Alive + Absent + Suspected resources) # -2 means BEST (Alive resources at the time) $tree_ref->[6] = 0 ; # maximum available children $tree_ref->[7] = undef ; # previous brother ref $tree_ref->[8] = undef ; # next brother ref $tree_ref->[9] = undef ; # first child ref $tree_ref->[10] = 0 ; # current children number return($tree_ref); } # clone the tree # arg : tree ref # return a copy of the tree ref sub clone($){ my $tree_ref = shift; return(dclone($tree_ref)); } # return 1 if node is a leaf (no child) # otherwise retuurn 0 sub is_node_a_leaf($){ my $tree_ref = shift; if (defined($tree_ref->[1])){ return(0); }else{ return(1); } } # add a child to the given tree ref (if child resource name is undef it seems # that this child is a leaf of the tree) # arg : tree ref, resource name, resource value # return the ref of the child sub add_child($$$){ my $tree_ref = shift; my $resource_name = shift; my $resource_value = shift; my $tmp_ref; if (!defined($tree_ref->[1]->{$resource_value})){ # Create a new tree node $tmp_ref = [ $tree_ref, undef, $resource_name, $resource_value, $tree_ref->[4] + 1, 0, 0, undef, undef, undef, 0]; $tree_ref->[1]->{$resource_value} = $tmp_ref; $tree_ref->[6] = $tree_ref->[6] + 1; $tree_ref->[10] = $tree_ref->[10] + 1; # Add new brother if (defined($tree_ref->[9])){ $tmp_ref->[8] = $tree_ref->[9]; $tree_ref->[9]->[7] = $tmp_ref; } $tree_ref->[9] = $tmp_ref; }else{ $tmp_ref = $tree_ref->[1]->{$resource_value}; } return($tmp_ref); } # Store information about the number of needed children sub set_needed_children_number($$){ my $tree_ref = shift; my $needed_children_number = shift; $tree_ref->[5] = $needed_children_number; } # Get previous brother on the same level for the same father sub get_previous_brother($){ my $tree_ref = shift; if (!defined($tree_ref)){ return(undef); }else{ return($tree_ref->[7]); } } # Get next brother on the same level for the same father sub get_next_brother($){ my $tree_ref = shift; if (!defined($tree_ref)){ return(undef); }else{ return($tree_ref->[8]); } } # Get initial child ref sub get_initial_child($){ my $tree_ref = shift; if (!defined($tree_ref)){ return(undef); }else{ return($tree_ref->[9]); } } # Get a specific child # arg : tree ref, name of a child # return a ref of a tree sub get_a_child($$){ my $tree_ref = shift; my $child_name = shift; if (!defined($tree_ref) || !defined($tree_ref->[1]) || !defined($tree_ref->[1]->{$child_name})){ return(undef); }else{ return($tree_ref->[1]->{$child_name}); } } # Get the ref of the father tree # arg : tree ref # return a tree ref sub get_father($){ my $tree_ref = shift; if (!defined($tree_ref) || !defined($tree_ref->[0])){ return(undef); }else{ return($tree_ref->[0]); } } # Get the current resource name # arg : tree ref # return the resource name sub get_current_resource_name($){ my $tree_ref = shift; if (!defined($tree_ref) || !defined($tree_ref->[2])){ return(undef); }else{ return($tree_ref->[2]); } } # Get the current resource value # arg : tree ref # return the resource value sub get_current_resource_value($){ my $tree_ref = shift; if (!defined($tree_ref) || !defined($tree_ref->[3])){ return(undef); }else{ return($tree_ref->[3]); } } # Get the current children number # arg : tree ref # return the resource value sub get_current_children_number($){ my $tree_ref = shift; if (!defined($tree_ref) || !defined($tree_ref->[10])){ return(undef); }else{ return($tree_ref->[10]); } } # Get the current level indicator # arg : tree ref # return the level indicator sub get_current_level($){ my $tree_ref = shift; if (!defined($tree_ref) || !defined($tree_ref->[4])){ return(0); }else{ return($tree_ref->[4]); } } # Get the maximum available number of children # (just after the creation of the tree) # arg : tree ref # return an integer >= 0 sub get_max_available_children($){ my $tree_ref = shift; if (!defined($tree_ref) || !defined($tree_ref->[6])){ return(0); }else{ return($tree_ref->[6]); } } # Get the number of needed children # arg : tree ref # return the number of needed children sub get_needed_children_number($){ my $tree_ref = shift; if (!defined($tree_ref) || !defined($tree_ref->[5])){ return(0); }else{ return($tree_ref->[5]); } } # Delete a subtree # arg : tree ref to delete # return father tree ref sub delete_subtree($){ my $tree_ref = shift; return(undef) if (!defined($tree_ref)); my $father_ref = $tree_ref->[0]; my $prev_brother = $tree_ref->[7]; my $next_brother = $tree_ref->[8]; if (!defined($prev_brother)){ if (defined($father_ref)){ $father_ref->[9] = $next_brother; } }else{ $prev_brother->[8] = $next_brother; } if (defined($next_brother)){ $next_brother->[7] = $prev_brother; } if (defined($father_ref->[1])){ delete($father_ref->[1]->{$tree_ref->[3]}); $father_ref->[10] = $father_ref->[10] - 1; return($father_ref); }else{ return(undef); } } ############################################################################### # delete_tree_nodes_with_not_enough_resources # Delete subtrees that do not fit wanted resources # args: tree ref # side effect : modify tree data structure sub delete_tree_nodes_with_not_enough_resources($){ my $tree_ref = shift; #print("START delete_tree_nodes_with_not_enough_resources\n"); # Search if there are enough values for each resource # Tremaux algorithm (Deep first) my $current_node = $tree_ref; do{ if (defined(get_initial_child($current_node))){ # Go to child $current_node = get_initial_child($current_node); #print("Go to CHILD =".get_current_resource_value($current_node)."\n"); }else{ # Treate leaf while(defined($current_node) and (!defined(get_next_brother($current_node)))){ # Step up #print("Go to FATHER : ".get_current_resource_value($current_node)."\n") if (defined(get_current_resource_value($current_node))); if ((get_needed_children_number($current_node) > get_current_children_number($current_node)) or ((get_needed_children_number($current_node) == -1) # ALL and (get_max_available_children($current_node) > get_current_children_number($current_node))) or ((get_needed_children_number($current_node) == -2) # BEST and (get_current_children_number($current_node) <= 0)) ){ # we want to delete the root return(undef) if ($tree_ref == $current_node); #print("DELETE 1".get_current_resource_value($current_node)."\n"); # Delete sub tree that does not fit with wanted resources $current_node = delete_subtree($current_node); }else{ $current_node = get_father($current_node); } } if (defined(get_next_brother($current_node))){ # Treate brother if ((get_needed_children_number($current_node) > get_current_children_number($current_node)) or ((get_needed_children_number($current_node) == -1) # ALL and (get_max_available_children($current_node) > get_current_children_number($current_node))) or ((get_needed_children_number($current_node) == -2) # BEST and (get_current_children_number($current_node) <= 0)) ){ #print("DELETE 2".get_current_resource_value($current_node)."\n"); # Delete sub tree that does not fit with wanted resources delete_subtree($current_node); } $current_node = get_next_brother($current_node); #print("Go to BROTHER : ".get_current_resource_value($current_node)."\n"); } } }while(defined($current_node)); if (!defined(get_initial_child($tree_ref))){ return(undef); }else{ return($tree_ref); } } # get_tree_leaf # return a list of tree leaf # arg: tree ref sub get_tree_leafs($){ my $tree = shift; my @result; return(@result) if (!defined($tree)); # Search leafs # Tremaux algorithm (Deep first) my $current_node = $tree; do{ if (defined(get_initial_child($current_node))){ # Go to child $current_node = get_initial_child($current_node); #print("Go to CHILD =".get_current_resource_value($current_node)."\n"); }else{ # Treate leaf if (is_node_a_leaf($current_node) == 1){ #push(@result, $node_name_pile[0]); push(@result, $current_node); #print("Leaf: ".get_current_resource_value($current_node)."\n"); } # Look at brothers while(defined($current_node) and (!defined(get_next_brother($current_node)))){ # Step up $current_node = get_father($current_node); #print("Go to FATHER : ".get_current_resource_value($current_node)."\n") if (defined(get_current_resource_value($current_node))); } if (defined(get_next_brother($current_node))){ # Treate brother $current_node = get_next_brother($current_node); #print("Go to BROTHER : ".get_current_resource_value($current_node)."\n"); } } }while(defined($current_node)); return(@result); } # delete_unnecessary_subtrees # Delete subtrees that are not necessary (watch needed_children_number) # args: tree ref # side effect : modify tree data structure sub delete_unnecessary_subtrees($){ my $tree_ref = shift; return($tree_ref) if (!defined($tree_ref)); # Search if there are enough values for each resource # Tremaux algorithm (Deep first) my $current_node = $tree_ref; do{ if ((get_needed_children_number($current_node) >= 0) and (get_needed_children_number($current_node) < (get_current_children_number($current_node)))){ # Delete extra sub tree delete_subtree(get_initial_child($current_node)); }else{ if (defined(get_initial_child($current_node))){ # Go to child $current_node = get_initial_child($current_node); #print("Go to CHILD =".get_current_resource_value($current_node)."\n"); }else{ # Look at brothers while(defined($current_node) and (!defined(get_next_brother($current_node)))){ # Step up $current_node = get_father($current_node); #print("Go to FATHER : ".get_current_resource_value($current_node)."\n") if (defined(get_current_resource_value($current_node))); } if (defined(get_next_brother($current_node))){ # Treate brother $current_node = get_next_brother($current_node); #print("Go to BROTHER : ".get_current_resource_value($current_node)."\n"); } } } }while(defined($current_node)); return($tree_ref); } return 1; ./oar-2.5.2/sources/core/common-libs/lib/OAR/Nodes.pm0000644000175000017500000001311511757171206020211 0ustar plbplbpackage OAR::Nodes; use strict; use warnings; use OAR::Version; use OAR::IO; use OAR::Tools; use OAR::Conf qw(init_conf dump_conf get_conf is_conf); my $base; sub open_db_connection(){ $base = OAR::IO::connect_ro_one(); if (defined($base)) { return 1; } else {return 0; } } sub close_db_connection(){ OAR::IO::disconnect($base); } sub encode_result($$){ my $result = shift or die("[OAR::Nodes] encode_result: no result to encode"); my $encoding = shift or die("[OAR::Nodes] encode_result: no format to encode to"); if($encoding eq "XML"){ eval "use XML::Dumper qw(pl2xml);1" or die ("XML module not available"); my $dump = new XML::Dumper; $dump->dtd; my $return = $dump->pl2xml($result) or die("XML conversion failed"); return $return; }elsif($encoding eq "YAML"){ eval "use YAML;1" or die ("YAML module not available"); my $return = YAML::Dump($result) or die("YAML conversion failed"); return $return; }elsif($encoding eq "JSON"){ eval "use JSON;1" or die ("JSON module not available"); my $return = JSON->new->pretty(1)->encode($result) or die("JSON conversion failed"); return $return; } } sub get_oar_version(){ return OAR::Version::get_version(); } sub format_date($){ my $date = shift; return OAR::IO::local_to_sql($date); } sub get_all_hosts(){ my @nodes = OAR::IO::list_nodes($base); return \@nodes; } sub heartbeat($){ my $hostname = shift; if (OAR::IO::set_node_nextState_if_necessary($base,$hostname,"Alive") > 0){ my $remote_host = get_conf("SERVER_HOSTNAME"); my $remote_port = get_conf("SERVER_PORT"); OAR::Tools::notify_tcp_socket($remote_host,$remote_port,"ChState"); } } sub get_all_resources(){ my @resources = OAR::IO::list_resources($base); return \@resources; } sub count_all_resources() { my $total = OAR::IO::count_all_resources($base); return $total; } sub get_requested_resources($$){ my $limit = shift; my $offset = shift; my @resources = OAR::IO::get_requested_resources($base,$limit,$offset); return \@resources; } sub get_events($$){ my $hostname = shift; my $date_from = shift; my @events = OAR::IO::get_events_for_hostname($base, $hostname, $date_from); return \@events; } sub get_resources_with_given_sql($){ my $sql_clause = shift; my @sql_resources = OAR::IO::get_resources_with_given_sql($base,$sql_clause); return \@sql_resources; } sub get_nodes_with_given_sql($){ my $sql_clause = shift; my @sql_resources = OAR::IO::get_nodes_with_given_sql($base,$sql_clause); return \@sql_resources; } sub get_resources_states($){ my $resources = shift; my %resources_states; foreach my $current_resource (@$resources){ my $properties = OAR::IO::get_resource_info($base, $current_resource); if ($properties->{state} eq "Absent" && $properties->{available_upto} >= time()) { $properties->{state} .= " (standby)"; } $resources_states{$current_resource} = $properties->{state}; } return \%resources_states; } sub get_resources_states_for_host($){ my $hostname = shift; my @node_info = OAR::IO::get_node_info($base, $hostname); my @resources; foreach my $info (@node_info){ push @resources, $info->{resource_id}; } return get_resources_states(\@resources); } sub get_resource_infos($){ my $id=shift; my $resource = OAR::IO::get_resource_info($base,$id); } sub is_job_tokill($){ my $id=shift; return OAR::IO::is_tokill_job($base,$id); } sub get_resources_infos($){ my $resources = shift; my %resources_infos; foreach my $current_resource (@$resources){ my $properties = OAR::IO::get_resource_info($base, $current_resource); add_running_jobs_to_resource_properties($properties); $resources_infos{$current_resource} = $properties } return \%resources_infos; } sub get_resources_for_host($){ my $hostname = shift; my @resources = OAR::IO::get_node_info($base, $hostname); return \@resources; } sub get_resources_infos_for_host($){ my $hostname = shift; my @node_info = OAR::IO::get_node_info($base, $hostname); my @resources; foreach my $info (@node_info){ push @resources, $info->{resource_id}; } return get_resources_infos(\@resources); } sub get_jobs_running_on_resource($){ my $resource_id = shift; my @jobs = OAR::IO::get_resource_job($base, $resource_id); return \@jobs; } sub get_jobs_running_on_node($){ my $node = shift; my @node_info = OAR::IO::get_node_info($base, $node); my @jobs; foreach my $info (@node_info){ my @resource_jobs = OAR::IO::get_resource_job($base, $info->{resource_id}); foreach my $job (@resource_jobs) { push(@jobs,$job); } } return \@jobs; } sub get_jobs_on_node($$){ my $node = shift; my $state = shift; my @node_info = OAR::IO::get_node_info($base, $node); my @jobs; foreach my $info (@node_info){ my @resource_jobs = OAR::IO::get_resource_job_with_state($base, $info->{resource_id},$state); foreach my $job (@resource_jobs) { push(@jobs,$job); } } return \@jobs; } sub add_running_jobs_to_resource_properties($){ my $info = shift; if ($info->{state} eq "Alive"){ my $jobs = get_jobs_running_on_resource($info->{resource_id}); if (@$jobs > 0){ # my $jobs_string = Dumper($jobs); # not proud of it... # $jobs = join(', ', split(/,/, $jobs_string)); # $jobs =~ s/[\[\]\']//g; my $jobs_string = ''; foreach my $current_job (@$jobs){ $jobs_string .= $current_job.", "; } chop($jobs_string); # remove last space chop($jobs_string); # remove last , $info->{jobs} = $jobs_string; } } } 1; ./oar-2.5.2/sources/core/common-libs/lib/OAR/Modules/0000755000175000017500000000000011757171206020212 5ustar plbplb./oar-2.5.2/sources/core/common-libs/lib/OAR/Modules/Judas.pm0000644000175000017500000002426311757171206021625 0ustar plbplbpackage OAR::Modules::Judas; require Exporter; # this module allows to log on a file and stdout with three different level # $Id$ use strict; use Data::Dumper; use OAR::Conf qw(init_conf get_conf is_conf); use Net::SMTP; use Sys::Hostname; use POSIX qw(strftime); use Time::HiRes qw(gettimeofday); use OAR::IO; use OAR::Tools; require Exporter; our (@ISA,@EXPORT,@EXPORT_OK); @ISA = qw(Exporter); @EXPORT_OK = qw(oar_warn oar_debug oar_error send_log_by_email set_current_log_category); $| = 1; my $CURRENT_LOG_CAT; #Get log level in oar.conf file init_conf($ENV{OARCONFFILE}); my $log_level = get_conf("LOG_LEVEL"); if (!defined($log_level)){ $log_level = 2; } my $log_file = get_conf("LOG_FILE"); if (!defined($log_file)){ $log_file = "/var/log/oar.log"; } my %log_categories; if (is_conf("LOG_CATEGORIES")){ my @prelogs = split(/,/, get_conf("LOG_CATEGORIES")); foreach(@prelogs){ $log_categories{$_} = 1; } } else{ $log_categories{"all"} = 1; } my $mail_recipient = get_conf("MAIL_RECIPIENT"); my $Openssh_cmd = get_conf("OPENSSH_CMD"); $Openssh_cmd = OAR::Tools::get_default_openssh_cmd() if (!defined($Openssh_cmd)); if (is_conf("OAR_SSH_CONNECTION_TIMEOUT")){ OAR::Tools::set_ssh_timeout(get_conf("OAR_SSH_CONNECTION_TIMEOUT")); } # this function redirect STDOUT and STDERR into the log file # return the pid of the fork process sub redirect_everything(){ return(0) if (! is_conf("LOG_FILE")); pipe(judas_read,judas_write); my $pid = fork(); if ($pid == 0){ close(judas_write); while (){ if (open(REDIRECTFILE,">>$log_file")){ print(REDIRECTFILE "$_"); close(REDIRECTFILE); } } exit(1); } close(judas_read); my $old_fd = select(judas_write); $|=1; select($old_fd); open(STDOUT, ">&".fileno(judas_write)); open(STDERR, ">&".fileno(judas_write)); return($pid); } sub get_log_level(){ return($log_level); } # this function must be called by each module that has something to say in # the logs with his proper category name. sub set_current_log_category($){ $CURRENT_LOG_CAT = shift; } # this function writes both on the stdout and in the log file sub write_log($){ my $str = shift; $CURRENT_LOG_CAT = "all" if !defined $CURRENT_LOG_CAT; if(exists($log_categories{$CURRENT_LOG_CAT}) || exists($log_categories{"all"})){ if (open(LOG,">>$log_file")){ print(LOG "$str"); close(LOG); }else{ print("$str"); } } } # Send an email to the admin sub send_log_by_email($$){ my $subject = shift; my $body = shift; if (!defined($subject)){ my ($sub,@null) = split("\n",$body); $subject = substr($sub,0,70); } send_mail($mail_recipient, $subject,$body,0); } sub oar_debug($){ my $string = shift; if ($log_level >= 3){ my ($seconds, $microseconds) = gettimeofday(); $microseconds = int($microseconds / 1000); $microseconds = sprintf("%03d",$microseconds); $string = "[".strftime("%F %T",localtime($seconds)).".$microseconds] $string"; write_log("[debug] $string"); } } sub oar_warn($){ my $string = shift; if ($log_level >= 2){ my ($seconds, $microseconds) = gettimeofday(); $microseconds = int($microseconds / 1000); $microseconds = sprintf("%03d",$microseconds); $string = "[".strftime("%F %T",localtime($seconds)).".$microseconds] $string"; write_log("[info] $string"); } } sub oar_error($){ my $string = shift; send_log_by_email(undef,"[error] $string"); my ($seconds, $microseconds) = gettimeofday(); $microseconds = int($microseconds / 1000); $microseconds = sprintf("%03d",$microseconds); $string = "[".strftime("%F %T",localtime($seconds)).".$microseconds] $string"; write_log("[error] $string"); } # Must be only used in the fork of the send_mail function to store errors in OAR DB sub treate_mail_error($$$$$$$){ my $smtpServer = shift; my $mailSenderAddress = shift; my $mailRecipientAddress = shift; my $object = shift; my $body = shift; my $error = shift; my $job_id = shift; #my $base = OAR::IO::connect(); # #OAR::IO::add_new_event($base,"MAIL_NOTIFICATION_ERROR",$job_id,"$error --> SMTP server used : $smtpServer, sender : $mailSenderAddress, recipients : $mailRecipientAddress, object : $object, body : $body"); # #OAR::IO::disconnect($base); oar_debug("[Judas] Mail ERROR: $job_id $error --> SMTP server used : $smtpServer, sender : $mailSenderAddress, recipients : $mailRecipientAddress, object : $object, body : $body\n"); exit(1); } # send mail to OAR admin sub send_mail($$$$){ my $mail_recipient_address = shift; my $object = shift; my $body = shift; my $job_id = shift; my $smtp_server = get_conf("MAIL_SMTP_SERVER"); my $mail_sender_address = get_conf("MAIL_SENDER"); if (!defined($smtp_server) || !defined($mail_sender_address) || !defined($mail_recipient_address)){ oar_debug("[Judas] Mail is not configured\n"); return(); } $SIG{PIPE} = 'IGNORE'; my $pid=fork; if ($pid == 0){ $SIG{USR1} = 'IGNORE'; $SIG{INT} = 'IGNORE'; $SIG{TERM} = 'IGNORE'; my $smtp = Net::SMTP->new( $smtp_server, # Host => $smtp_server , Timeout => 120 , Hello => hostname(), Debug => 0 ) or treate_mail_error($smtp_server,$mail_sender_address,$mail_recipient_address,$object,$body,"No SMTP connexion",$job_id); $smtp->mail($mail_sender_address) or treate_mail_error($smtp_server,$mail_sender_address,$mail_recipient_address,$object,$body,"MAIL FROM",$job_id); my @recipients = split(',',$mail_recipient_address); $smtp->to(@recipients) or treate_mail_error($smtp_server,$mail_sender_address,$mail_recipient_address,$object,$body,"RCPT TO",$job_id); $smtp->data() or treate_mail_error($smtp_server,$mail_sender_address,$mail_recipient_address,$object,$body,"DATA",$job_id); $smtp->datasend("To: $mail_recipient_address\n") or treate_mail_error($smtp_server,$mail_sender_address,$mail_recipient_address,$object,$body,"Cannot send",$job_id); $smtp->datasend("Subject: $object\n") or treate_mail_error($smtp_server,$mail_sender_address,$mail_recipient_address,$object,$body,"Cannot send",$job_id); $smtp->datasend($body) or treate_mail_error($smtp_server,$mail_sender_address,$mail_recipient_address,$object,$body,"Cannot send",$job_id); $smtp->dataend() or treate_mail_error($smtp_server,$mail_sender_address,$mail_recipient_address,$object,$body,"DATA END",$job_id); $smtp->quit or treate_mail_error($smtp_server,$mail_sender_address,$mail_recipient_address,$object,$body,"QUIT",$job_id); exit(0); } } # Parse notify method and send an email or execute a command # args : notify method string, frontal host, user, job id, job name, tag, comments sub notify_user($$$$$$$$){ my $base = shift; my $method = shift; my $host = shift; my $user = shift; my $job_id = shift; my $job_name = shift; my $tag = shift; my $comments = shift; return() if (!defined($method)); if ($method =~ m/^\s*mail:(.+)$/m){ OAR::IO::add_new_event($base,"USER_MAIL_NOTIFICATION",$job_id,"[Judas] Send a mail to $1 --> $tag"); my $server_hostname = hostname(); send_mail($1,"*OAR* [$tag]: $job_id ($job_name) on $server_hostname",$comments,$job_id); }elsif($method =~ m/\s*exec:([a-zA-Z0-9_.\/ -]+)$/m){ my $cmd = "$Openssh_cmd -x -T $host OARDO_BECOME_USER=$user oardodo $1 $job_id $job_name $tag \\\"$comments\\\" > /dev/null 2>&1"; $SIG{PIPE} = 'IGNORE'; my $pid = fork(); if ($pid == 0){ undef($base); $SIG{USR1} = 'IGNORE'; $SIG{INT} = 'IGNORE'; $SIG{TERM} = 'IGNORE'; my $exit_value; my $signal_num; my $dumped_core; my $ssh_pid; eval{ $SIG{ALRM} = sub { die "alarm\n" }; alarm(OAR::Tools::get_ssh_timeout()); $ssh_pid = fork(); if ($ssh_pid == 0){ exec($cmd); warn("[ERROR] Cannot find $cmd\n"); exit(-1); } my $wait_res = 0; # Avaoid to be disrupted by a signal while ($wait_res != $ssh_pid){ $wait_res = waitpid($ssh_pid,0); } alarm(0); $exit_value = $? >> 8; $signal_num = $? & 127; $dumped_core = $? & 128; }; if ($@){ if ($@ eq "alarm\n"){ if (defined($ssh_pid)){ my ($children,$cmd_name) = OAR::Tools::get_one_process_children($ssh_pid); kill(9,@{$children}); } my $dbh = OAR::IO::connect(); my $str = "[Judas] User notification failed : ssh timeout, on node $host (cmd : $cmd)"; oar_error("$str\n"); OAR::IO::add_new_event($dbh,"USER_EXEC_NOTIFICATION_ERROR",$job_id,"$str"); OAR::IO::disconnect($dbh); } }else{ my $dbh = OAR::IO::connect(); my $str = "[Judas] Launched user notification command : $cmd; exit value = $exit_value, signal num = $signal_num, dumped core = $dumped_core"; oar_debug("$str\n"); OAR::IO::add_new_event($dbh,"USER_EXEC_NOTIFICATION",$job_id,"$str"); OAR::IO::disconnect($dbh); } # Exit from child exit(0); }elsif (!defined($pid)){ oar_error("[Judas] Error when forking process to execute notify user command : $cmd\n"); } }else{ oar_debug("[Judas] No correct notification method found ($method) for the job $job_id\n"); } } return(1); ./oar-2.5.2/sources/core/common-libs/lib/OAR/IO.pm0000644000175000017500000074653511757171206017473 0ustar plbplb# This is the iolib, which manages the layer between the modules and the # database. This is the only base-dependent layer. # When adding a new function, the following comments are required before the code of the function: # - the name of the function # - a short description of the function # - the list of the parameters it expect # - the list of the return values # - the list of the side effects # $Id$ package OAR::IO; require Exporter; use DBI; use OAR::Conf qw(init_conf get_conf is_conf reset_conf); use Data::Dumper; use Time::Local; use OAR::Modules::Judas qw(oar_debug oar_warn oar_error send_log_by_email set_current_log_category); use strict; use Fcntl; use OAR::Schedulers::ResourceTree; use OAR::Tools; use POSIX qw(strftime); # suitable Data::Dumper configuration for serialization $Data::Dumper::Purity = 1; $Data::Dumper::Terse = 1; $Data::Dumper::Indent = 0; $Data::Dumper::Deepcopy = 1; # PROTOTYPES # CONNECTION sub connect(); sub connect_ro(); sub disconnect($); # JOBS MANAGEMENT sub get_job_challenge($$); sub get_jobs_in_state($$); sub get_jobs_in_state_for_user($$$); sub is_job_desktop_computing($$); sub get_job_current_hostnames($$); sub get_job_current_resources($$$); sub get_job_host_log($$); sub get_job_resources($$); sub get_job_resources_properties($$); sub get_to_kill_jobs($); sub is_tokill_job($$); sub get_timered_job($); sub get_to_exterminate_jobs($); sub get_frag_date($$); sub set_running_date($$); sub set_running_date_arbitrary($$$); sub set_assigned_moldable_job($$$); sub set_finish_date($$); sub get_possible_wanted_resources($$$$$$$); sub add_micheline_job($$$$$$$$$$$$$$$$$$$$$$$$$$$$$); sub get_job($$); sub get_job_state($$); sub get_current_moldable_job($$); sub set_job_state($$$); sub set_job_resa_state($$$); sub set_job_message($$$); sub frag_job($$); sub ask_checkpoint_job($$); sub ask_signal_job($$$); sub hold_job($$$); sub resume_job($$); sub job_fragged($$); sub job_arm_leon_timer($$); sub job_refrag($$); sub job_leon_exterminate($$); sub get_waiting_reservation_jobs($); sub get_waiting_reservation_jobs_specific_queue($$); sub get_waiting_toSchedule_reservation_jobs_specific_queue($$); sub get_jobs_range_dates($$$); sub get_jobs_gantt_scheduled; sub get_jobs_for_user_query; sub count_jobs_for_user_query; sub get_desktop_computing_host_jobs($$); sub get_stagein_id($$); sub set_stagein($$$$$$); sub get_job_stagein($$); sub is_stagein_deprecated($$$); sub del_stagein($$); sub get_jobs_to_schedule($$$); sub get_current_job_types($$); sub set_moldable_job_max_time($$$); #ARRAY JOBS MANAGEMENT sub get_jobs_in_array($$); sub get_job_array_id($$); sub get_array_subjobs($$); sub get_array_job_ids($$); # PROCESSJOBS MANAGEMENT (Resource assignment to jobs) sub get_resource_job($$); sub get_resource_job_to_frag($$); sub get_node_job($$); sub get_node_job_to_frag($$); sub get_resources_in_state($$); sub add_resource_job_pair($$$); # RESOURCES MANAGEMENT sub add_resource($$$); sub list_nodes($); sub list_resources($); sub count_all_resources($); sub get_requested_resources($$$); sub get_resource_info($$); sub is_node_exists($$); sub get_resources_on_node($$); sub set_node_state($$$$); sub update_resource_nextFinaudDecision($$$); sub get_resources_change_state($); sub set_resource_nextState($$$); sub set_node_nextState($$$); sub set_node_expiryDate($$$); sub set_node_property($$$$); sub set_resource_property($$$$); sub get_resource_dead_range_date($$$); sub get_expired_resources($); sub is_node_desktop_computing($$); sub get_resources_data_structure_current_job($$); sub get_hosts_state($); sub get_alive_nodes_with_jobs($); # QUEUES MANAGEMENT sub get_active_queues($); sub get_all_queue_informations($); # GANTT MANAGEMENT sub get_gantt_scheduled_jobs($); sub get_gantt_visu_scheduled_jobs($); sub add_gantt_scheduled_jobs($$$$); sub gantt_flush_tables($$$); sub set_gantt_date($$); sub get_gantt_date($); sub get_gantt_visu_date($); sub get_gantt_jobs_to_launch($$); sub get_gantt_resources_for_jobs_to_launch($$); sub get_gantt_resources_for_job($$); sub set_gantt_job_startTime($$$); sub update_gantt_visualization($); sub get_gantt_visu_scheduled_job_resources($$); # ADMISSION RULES MANAGEMENT sub add_admission_rule($$); sub list_admission_rules($); sub get_admission_rule($$); sub get_requested_admission_rules($$$); sub count_all_admission_rules($); sub delete_admission_rule($$); # TIME CONVERSION sub ymdhms_to_sql($$$$$$); sub sql_to_ymdhms($); sub ymdhms_to_local($$$$$$); sub local_to_ymdhms($); sub sql_to_local($); sub local_to_sql($); sub sql_to_hms($); sub hms_to_duration($$$); sub hms_to_sql($$$); sub duration_to_hms($); sub duration_to_sql($); sub sql_to_duration($); sub get_date($); #EVENTS LOG MANAGEMENT sub add_new_event($$$$); sub add_new_event_with_host($$$$$); sub check_event($$$); sub get_to_check_events($); sub get_hostname_event($$); sub get_job_events($$); sub get_events_for_hostname($$$); sub get_last_event_from_type($$); # ACCOUNTING sub check_accounting_update($$); sub update_accounting($$$$$$$$$); sub get_accounting_summary($$$$$); sub get_accounting_summary_byproject($$$$); sub get_last_project_karma($$$$); # LOCK FUNCTIONS: sub get_lock($$$); sub release_lock($$); # END OF PROTOTYPES my $Remote_host; my $Remote_port; my %State_to_num = ( "Alive" => 1, "Absent" => 2, "Suspected" => 3, "Dead" => 4 ); # Log category # set_current_log_category('main'); my $Db_type = "mysql"; sub get_database_type(){ return($Db_type); } # Duration to add to all jobs when matching the available_upto resource property field my $Cm_security_duration = 600; # CONNECTION my $Max_db_connection_timeout = 30; my $Timeout_db_connection = 2; # connect_db # Connects to database and returns the base identifier # return value : base sub connect_db($$$$$$) { my $host = shift; my $dbport = shift; my $name = shift; my $user = shift; my $pwd = shift; my $debug_level = shift; if($host=~m/;/){ my $oldHost = $host; $host=~s/;.*//; oar_error("[IOlib] Error reading $oldHost attribute from OAR configuration file, using $host instead.\n"); } if($dbport=~m/;/){ my $oldDbport = $dbport; $dbport=~s/;.*//; oar_error("[IOlib] Error reading $oldDbport attribute from OAR configuration file, using $dbport instead.\n"); } if($name=~m/;/){ my $oldname = $name; $name=~s/;.*//; oar_error("[IOlib] Error reading $oldname attribute from OAR configuration file, using $name instead.\n"); } if($user=~m/;/){ my $olduser = $user; $user=~s/;.*//; oar_error("[IOlib] Error reading $olduser attribute from OAR configuration file, using $user instead.\n"); } my $printerror = 0; if (defined($debug_level) and ($debug_level >= 3)){ $printerror = 1; } my $type; if ($Db_type eq "Pg" || $Db_type eq "psql"){ $type = "Pg"; }elsif ($Db_type eq "mysql"){ $type = "mysql"; }else{ oar_error("[IOlib] Cannot recognize DB_TYPE tag \"$Db_type\". So we are using \"mysql\" type.\n"); $type = "mysql"; $Db_type = "mysql"; } my $connection_string; if($dbport eq "" || !($dbport>1 && $dbport<65535)){ $connection_string = "DBI:$type:database=$name;host=$host"; } else{ $connection_string = "DBI:$type:database=$name;host=$host;port=$dbport"; } my $dbh = DBI->connect($connection_string, $user, $pwd, {'InactiveDestroy' => 1, 'PrintError' => $printerror}); if (!defined($dbh)){ oar_error("[IOlib] Cannot connect to database (type=$Db_type, host=$host, user=$user, database=$name) : $DBI::errstr\n"); } return($dbh); } # timeout_db # Check the provided db handler and wait for an incremented time if not ok sub timeout_db($$) { my $dbh = shift; my $Max_db_connection_timeout = shift; if (!defined($dbh)) { oar_warn("[IOlib] I will retry to connect to the database in $Timeout_db_connection s\n"); send_log_by_email("OAR database connection failed","[IOlib] I will retry to connect to the database in $Timeout_db_connection s\n"); my $sleep_time = 0; while ($sleep_time <= 1){ $sleep_time = sleep($Timeout_db_connection); } if ($Timeout_db_connection < $Max_db_connection_timeout){ $Timeout_db_connection += 2; } } } # connect # Connects to database and returns the base identifier # parameters : / # return value : base # side effects : opens a connection to the base specified in ConfLib sub connect() { # Connect to the database. my $dbh = undef; while (!defined($dbh)){ $dbh = connect_one(); my $max_timeout = get_conf("MAX_DB_CONNECTION_TIMEOUT"); $max_timeout = $Max_db_connection_timeout unless defined($max_timeout); timeout_db($dbh,$max_timeout); } return($dbh); } # connect_one # Connects to database and returns the base identifier. No loop for retry. # parameters : / # return value : base # side effects : opens a connection to the base specified in ConfLib sub connect_one() { # Connect to the database. my $dbh = undef; reset_conf(); init_conf($ENV{OARCONFFILE}); my $host = get_conf("DB_HOSTNAME"); my $dbport = get_conf("DB_PORT"); if (not defined($dbport)) { $dbport = ""; } my $name = get_conf("DB_BASE_NAME"); my $user = get_conf("DB_BASE_LOGIN"); my $pwd = get_conf("DB_BASE_PASSWD"); $Db_type = get_conf("DB_TYPE"); my $log_level = get_conf("LOG_LEVEL"); $Remote_host = get_conf("SERVER_HOSTNAME"); $Remote_port = get_conf("SERVER_PORT"); return connect_db($host,$dbport,$name,$user,$pwd,$log_level); } # connect_ro # Connects to database and returns the base identifier # parameters : / # return value : base # side effects : opens a connection to the base specified in ConfLib sub connect_ro() { # Connect to the database. my $dbh = undef; while (!defined($dbh)){ $dbh = connect_ro_one(); my $max_timeout = get_conf("MAX_DB_CONNECTION_TIMEOUT"); $max_timeout = $Max_db_connection_timeout unless defined($max_timeout); timeout_db($dbh,$max_timeout); } return($dbh); } # connect_ro_one # Connects to database and returns the base identifier. No loop for retry. # parameters : / # return value : base # side effects : opens a connection to the base specified in ConfLib sub connect_ro_one() { connect_ro_one_log(undef); } sub connect_ro_one_log($) { my $log=shift; # Connect to the database. reset_conf(); init_conf($ENV{OARCONFFILE}); my $host = get_conf("DB_HOSTNAME"); my $dbport = get_conf("DB_PORT"); if (not defined($dbport)) { $dbport = ""; } my $name = get_conf("DB_BASE_NAME"); my $user = get_conf("DB_BASE_LOGIN_RO"); $user = get_conf("DB_BASE_LOGIN") if (!defined($user)); my $pwd = get_conf("DB_BASE_PASSWD_RO"); $pwd = get_conf("DB_BASE_PASSWD") if (!defined($pwd)); $Db_type = get_conf("DB_TYPE"); my $log_level; if (defined($log)) { $log_level = 3; } else { $log_level = get_conf("LOG_LEVEL"); } $Remote_host = get_conf("SERVER_HOSTNAME"); $Remote_port = get_conf("SERVER_PORT"); return connect_db($host,$dbport,$name,$user,$pwd,$log_level); } # disconnect # Disconnect from database # parameters : base # return value : / # side effects : closes a previously opened connection to the specified base sub disconnect($) { my $dbh = shift; # Disconnect from the database. $dbh->disconnect(); } sub get_last_insert_id($$){ my $dbh = shift; my $seq = shift; my $id; my $sth; if ($Db_type eq "Pg"){ $sth = $dbh->prepare("SELECT CURRVAL('$seq')"); $sth->execute(); my $ref = $sth->fetchrow_hashref(); my @tmp_array = values(%$ref); $id = $tmp_array[0]; $sth->finish(); }else{ $sth = $dbh->prepare("SELECT LAST_INSERT_ID()"); $sth->execute(); my $ref = $sth->fetchrow_hashref(); my @tmp_array = values(%$ref); $id = $tmp_array[0]; $sth->finish(); } return($id); } # JOBS MANAGEMENT # Get the cpuset name for the given job # args : database ref, job id sub get_job_cpuset_name($$){ my $dbh = shift; my $job_id = shift; my $sth = $dbh->prepare(" SELECT job_user FROM jobs WHERE job_id = $job_id "); $sth->execute(); my @res = $sth->fetchrow_array(); my $cpuset = $res[0]."_".$job_id; return($cpuset); } # get_job_challenge # gets the challenge string of a OAR Job # parameters : base, jobid # return value : challenge # side effects : / sub get_job_challenge($$){ my $dbh = shift; my $job_id = shift; my $sth = $dbh->prepare("SELECT challenge,ssh_private_key,ssh_public_key FROM challenges WHERE job_id = $job_id "); $sth->execute(); my $ref = $sth->fetchrow_hashref(); $sth->finish(); return($ref->{challenge},$ref->{ssh_private_key},$ref->{ssh_public_key}); } # get_count_same_ssh_keys_current_jobs # return the number of current jobs with the same ssh keys sub get_count_same_ssh_keys_current_jobs($$$$){ my $dbh = shift; my $user = shift; my $ssh_private_key = shift; my $ssh_public_key = shift; $ssh_private_key = $dbh->quote($ssh_private_key); $ssh_public_key = $dbh->quote($ssh_public_key); my $sth = $dbh->prepare(" SELECT COUNT(challenges.job_id) FROM challenges, jobs WHERE jobs.state IN (\'Waiting\',\'Hold\',\'toLaunch\',\'toError\',\'toAckReservation\',\'Launching\',\'Running\',\'Suspended\',\'Resuming\') AND challenges.job_id = jobs.job_id AND challenges.ssh_private_key = $ssh_private_key AND challenges.ssh_public_key = $ssh_public_key AND jobs.job_user != '$user' AND challenges.ssh_private_key != \'\' "); $sth->execute(); my @ref = $sth->fetchrow_array(); $sth->finish(); return($ref[0]); } # get_jobs_in_state # returns the jobs in the specified state # parameters : base, job state # return value : flatened list of hashref jobs # side effects : / sub get_jobs_in_state($$) { my $dbh = shift; my $state = $dbh->quote(shift); my $sth = $dbh->prepare(" SELECT * FROM jobs WHERE state = $state "); $sth->execute(); my @res = (); while (my $ref = $sth->fetchrow_hashref()) { push(@res, $ref); } return(@res); } # get_jobs_in_multiple_states # returns the jobs in the specified states # parameters : base, job state list # return value : flatened list of hashref jobs # side effects : / sub get_jobs_in_multiple_states($$) { my $dbh = shift; my $states = shift; my $state_str; foreach my $s (@{$states}){ $state_str .= $dbh->quote($s); $state_str .= ","; } chop($state_str); my $sth = $dbh->prepare(" SELECT * FROM jobs WHERE state IN (".$state_str.") "); $sth->execute(); my @res = (); while (my $ref = $sth->fetchrow_hashref()) { push(@res, $ref); } return(@res); } # get_jobs_in_state_for_user # returns the jobs in the specified state for the optionaly specified user # parameters : base, job state, user # return value : flatened list of hashref jobs # side effects : / sub get_jobs_in_state_for_user($$$) { my $dbh = shift; my $state = $dbh->quote(shift); my $user = shift; my $user_query=""; if (defined $user and "$user" ne "" ) { $user_query="AND job_user =" . $dbh->quote($user); } my $sth = $dbh->prepare(" SELECT * FROM jobs WHERE state = $state $user_query ORDER BY job_id "); $sth->execute(); my @res = (); while (my $ref = $sth->fetchrow_hashref()) { push(@res, $ref); } return(@res); } # get_jobs_with_given_properties # returns the jobs with specified properties # parameters : base, where SQL constraints # return value : flatened list of hashref jobs # side effects : / sub get_jobs_with_given_properties($$) { my $dbh = shift; my $where = shift; my $sth = $dbh->prepare(" SELECT * FROM jobs WHERE $where "); $sth->execute(); my @res = (); while (my $ref = $sth->fetchrow_hashref()) { push(@res, $ref); } return(@res); } # is_job_desktop_computing # return true if the job will run on desktop_computing nodes # parameters: base, jobid # return value: boolean # side effects: / sub is_job_desktop_computing($$){ my $dbh = shift; my $jobid = shift; my $sth = $dbh->prepare(" SELECT COUNT(desktop_computing) FROM assigned_resources, resources, jobs WHERE jobs.job_id = $jobid AND assigned_resources.moldable_job_id = jobs.assigned_moldable_job AND assigned_resources.resource_id = resources.resource_id AND resources.desktop_computing = \'YES\' "); $sth->execute(); my ($count) = $sth->fetchrow_array(); return($count > 0); } # get_job_current_hostnames # returns the list of hosts associated to the job passed in parameter # parameters : base, jobid # return value : list of distinct hostnames # side effects : / sub get_job_current_hostnames($$) { my $dbh = shift; my $jobid= shift; my $sth = $dbh->prepare("SELECT resources.network_address as hostname FROM assigned_resources, resources, moldable_job_descriptions WHERE assigned_resources.assigned_resource_index = \'CURRENT\' AND moldable_job_descriptions.moldable_index = \'CURRENT\' AND assigned_resources.resource_id = resources.resource_id AND moldable_job_descriptions.moldable_id = assigned_resources.moldable_job_id AND moldable_job_descriptions.moldable_job_id = $jobid AND resources.network_address != \'\' AND resources.type = \'default\' GROUP BY resources.network_address ORDER BY resources.network_address ASC"); $sth->execute(); my @res = (); while (my $ref = $sth->fetchrow_hashref()) { push(@res, $ref->{hostname}); } return @res; } # get_job_current_resources # returns the list of resources associated to the job passed in parameter # parameters : base, jobid # return value : list of resources # side effects : / sub get_job_current_resources($$$) { my $dbh = shift; my $jobid= shift; my $not_type_list = shift; my $tmp_str; if (!defined($not_type_list)){ $tmp_str = "FROM assigned_resources WHERE assigned_resources.assigned_resource_index = \'CURRENT\' AND assigned_resources.moldable_job_id = $jobid"; }else{ my $type_str; foreach my $t (@{$not_type_list}){ $type_str .= $dbh->quote($t); $type_str .= ','; } chop($type_str); $tmp_str = "FROM assigned_resources,resources WHERE assigned_resources.assigned_resource_index = \'CURRENT\' AND assigned_resources.moldable_job_id = $jobid AND resources.resource_id = assigned_resources.resource_id AND resources.type NOT IN (".$type_str.")"; } my $sth = $dbh->prepare("SELECT assigned_resources.resource_id as resource $tmp_str ORDER BY assigned_resources.resource_id ASC"); $sth->execute(); my @res = (); while (my $ref = $sth->fetchrow_hashref()) { push(@res, $ref->{resource}); } return(@res); } # get_job_cpuset_uid # returns the uid of the user for this job # parameters : base, jobid, resource type, cpuset field # return value : number sub get_job_cpuset_uid($$$$) { my $dbh = shift; my $mjobid= shift; my $resource_type = shift; my $cpuset_field = shift; my $sth = $dbh->prepare(" SELECT resources.$cpuset_field FROM jobs, resources, assigned_resources WHERE resources.type = \'$resource_type\' AND assigned_resources.moldable_job_id = $mjobid AND assigned_resources.resource_id = resources.resource_id ORDER BY resources.resource_id ASC LIMIT 1"); $sth->execute(); my $result; if (my @res = $sth->fetchrow_array()){ $result = $res[0]; } return($result); } # get_job_resources # returns the list of resources associated to the job passed in parameter # parameters : base, jobid # return value : list of resources # side effects : / sub get_job_resources($$) { my $dbh = shift; my $jobid= shift; my $sth = $dbh->prepare("SELECT resource_id as resource FROM assigned_resources WHERE moldable_job_id = $jobid ORDER BY resource_id ASC"); $sth->execute(); my @res = (); while (my $ref = $sth->fetchrow_hashref()) { push(@res, $ref->{resource}); } return @res; } # get_job_network_address # returns the list of network_address associated to the job passed in parameter # parameters : base, jobid # return value : list of resources # side effects : / sub get_job_network_address($$) { my $dbh = shift; my $jobid= shift; my $sth = $dbh->prepare("SELECT DISTINCT(resources.network_address) as hostname FROM assigned_resources, resources WHERE assigned_resources.moldable_job_id = $jobid AND resources.resource_id = assigned_resources.resource_id AND resources.type = \'default\' ORDER BY resources.network_address ASC"); $sth->execute(); my @res = (); while (my $ref = $sth->fetchrow_hashref()) { push(@res, $ref->{hostname}); } return(@res); } # get_job_resource_properties # returns the list of resources properties associated to the job passed in # parameter # parameters : base, jobid # return value : list of hashs of each resource properties # side effects : / sub get_job_resources_properties($$) { my $dbh = shift; my $jobid= shift; my $sth = $dbh->prepare("SELECT resources.* FROM resources, assigned_resources, jobs WHERE jobs.job_id = $jobid AND jobs.assigned_moldable_job = assigned_resources.moldable_job_id AND assigned_resources.resource_id = resources.resource_id ORDER BY resource_id ASC"); $sth->execute(); my @res = (); while (my $ref = $sth->fetchrow_hashref()) { push(@res, $ref); } return @res; } # get_job_host_log # returns the list of hosts associated to the moldable job passed in parameter # parameters : base, moldablejobid # return value : list of distinct hostnames # side effects : / sub get_job_host_log($$) { my $dbh = shift; my $moldablejobid = shift; my $sth = $dbh->prepare(" SELECT resources.network_address, resources.resource_id FROM assigned_resources, resources WHERE assigned_resources.moldable_job_id = $moldablejobid AND resources.resource_id = assigned_resources.resource_id AND resources.network_address != \'\' AND resources.type = \'default\' ORDER BY resources.resource_id ASC "); $sth->execute(); my @res = (); while (my $ref = $sth->fetchrow_hashref()) { push(@res, $ref->{network_address}); } return @res; } # is_tokill_job # returns true if the job has its frag state to LEON # parameters : base, jobid # return value : boolean # side effects : / sub is_tokill_job($$) { my $dbh = shift; my $jobid = shift; my $sth = $dbh->prepare(" SELECT frag_id_job FROM frag_jobs WHERE frag_state = \'LEON\' AND frag_id_job = $jobid"); $sth->execute(); my @res = $sth->fetchrow_array(); $sth->finish(); return ($#res >= 0) } # get_to_kill_jobs # returns the list of jobs that have their frag state to LEON # parameters : base # return value : list of jobid # side effects : / sub get_to_kill_jobs($) { my $dbh = shift; my $sth = $dbh->prepare("SELECT jobs.* FROM frag_jobs, jobs WHERE frag_state = \'LEON\' AND jobs.job_id = frag_jobs.frag_id_job AND jobs.state != \'Error\' AND jobs.state != \'Terminated\' AND jobs.state != \'Finishing\' "); $sth->execute(); my @res = (); while (my $ref = $sth->fetchrow_hashref()) { push(@res, $ref); } $sth->finish(); return(@res); } # get_timered_job # returns the list of jobs that have their frag state to TIMER_ARMED # parameters : base # return value : list of jobid # side effects : / sub get_timered_job($) { my $dbh = shift; my $sth = $dbh->prepare(" SELECT jobs.* FROM frag_jobs, jobs WHERE frag_jobs.frag_state = \'TIMER_ARMED\' AND frag_jobs.frag_id_job = jobs.job_id "); $sth->execute(); my @res = (); while (my $ref = $sth->fetchrow_hashref()) { push(@res, $ref); } $sth->finish(); return(@res); } # get_toexterminate_job # returns the list of jobs that have their frag state to LEON_EXTERMINATE # parameters : base # return value : list of jobid # side effects : / sub get_to_exterminate_jobs($) { my $dbh = shift; my $sth = $dbh->prepare(" SELECT jobs.* FROM frag_jobs, jobs WHERE frag_state = \'LEON_EXTERMINATE\' AND frag_jobs.frag_id_job = jobs.job_id "); $sth->execute(); my @res = (); while (my $ref = $sth->fetchrow_hashref()) { push(@res, $ref); } $sth->finish(); return(@res); } # set_assigned_moldable_job # sets the assigned_moldable_job field to the given value # parameters : base, jobid, moldable id # return value : / sub set_assigned_moldable_job($$$) { my $dbh = shift; my $idJob = shift; my $moldable = shift; $dbh->do(" UPDATE jobs SET assigned_moldable_job = $moldable WHERE job_id = $idJob "); } # set_running_date # sets the starting time of the job passed in parameter to the current time # parameters : base, jobid # return value : / # side effects : changes the field startTime of the job in the table Jobs sub set_running_date($$) { my $dbh = shift; my $idJob = shift; my $runningDate; my $date = get_date($dbh); my $minDate = get_gantt_date($dbh); if ($date < $minDate){ $runningDate = $minDate; }else{ $runningDate = $date; } my $sth = $dbh->prepare(" UPDATE jobs SET start_time = \'$runningDate\' WHERE job_id = $idJob "); $sth->execute(); $sth->finish(); } # set_running_date_arbitrary # sets the starting time of the job passed in parameter to arbitrary time # parameters : base, jobid # return value : / # side effects : changes the field start_time of the job in the table Jobs sub set_running_date_arbitrary($$$) { my $dbh = shift; my $idJob = shift; my $date = shift; $dbh->do("UPDATE jobs SET start_time = \'$date\' WHERE job_id = $idJob "); } # set_finish_date # sets the maximal stoping time of the job passed in parameter to the current # time # parameters : base, jobid # return value : / # side effects : changes the field stop_time of the job in the table Jobs sub set_finish_date($$) { my $dbh = shift; my $idJob = shift; my $finishDate; my $date = get_date($dbh); my $jobInfo = get_job($dbh,$idJob); my $minDate = $jobInfo->{'start_time'}; if ($date < $minDate){ $finishDate = $minDate; }else{ $finishDate = $date; } my $sth = $dbh->prepare(" UPDATE jobs SET stop_time = \'$finishDate\' WHERE job_id = $idJob "); $sth->execute(); $sth->finish(); } # set_job_exit_code # parameters : base, jobid, exit code sub set_job_exit_code($$$) { my $dbh = shift; my $idJob = shift; my $exit_code = shift; $dbh->do(" UPDATE jobs SET exit_code = $exit_code WHERE job_id = $idJob "); } # get_possible_wanted_resources # return a tree ref : a data structure with corresponding resources with what is asked sub get_possible_wanted_resources($$$$$$$){ my $dbh = shift; my $possible_resources_vector = shift; my $impossible_resources_vector = shift; my $resources_to_ignore_array = shift; my $properties = shift; my $wanted_resources_ref = shift; my $order_part = shift; my $sql_in_string = "\'1\'"; if (defined($resources_to_ignore_array) and ($#{$resources_to_ignore_array} >= 0)){ $sql_in_string = "resource_id NOT IN ("; $sql_in_string .= join(",",@{$resources_to_ignore_array}); $sql_in_string .= ")"; } if (defined($order_part)){ $order_part = "ORDER BY $order_part"; }else{ $order_part = ""; } my @wanted_resources = @{$wanted_resources_ref}; if ($wanted_resources[$#wanted_resources]->{resource} ne "resource_id"){ push(@wanted_resources, { resource => "resource_id", value => -1, }); } my $sql_where_string = "\'1\'"; if ((defined($properties)) and ($properties ne "")){ $sql_where_string .= " AND ( $properties )"; } #Get only wanted resources my $resource_string; foreach my $r (@wanted_resources){ $resource_string .= " $r->{resource},"; } chop($resource_string); my $sth = $dbh->prepare("SELECT $resource_string FROM resources WHERE ($sql_where_string) AND $sql_in_string $order_part "); if (!$sth->execute()){ return(undef); } # Initialize root my $result ; $result = OAR::Schedulers::ResourceTree::new(); my $wanted_children_number = $wanted_resources[0]->{value}; OAR::Schedulers::ResourceTree::set_needed_children_number($result,$wanted_children_number); while (my @sql = $sth->fetchrow_array()){ my $father_ref = $result; foreach (my $i = 0; $i <= $#wanted_resources; $i++){ # Feed the tree for all resources $father_ref = OAR::Schedulers::ResourceTree::add_child($father_ref, $wanted_resources[$i]->{resource}, $sql[$i]); if ($i < $#wanted_resources){ $wanted_children_number = $wanted_resources[$i+1]->{value}; }else{ $wanted_children_number = 0; } OAR::Schedulers::ResourceTree::set_needed_children_number($father_ref,$wanted_children_number); # Verify if we must keep this child if this is resource_id resource name if ($wanted_resources[$i]->{resource} eq "resource_id"){ if ((defined($impossible_resources_vector)) and (vec($impossible_resources_vector, $sql[$i], 1))){ OAR::Schedulers::ResourceTree::delete_subtree($father_ref); $i = $#wanted_resources + 1; }elsif ((defined($possible_resources_vector)) and (!vec($possible_resources_vector, $sql[$i], 1))){ OAR::Schedulers::ResourceTree::delete_subtree($father_ref); $i = $#wanted_resources + 1; } } } } $sth->finish(); $result = OAR::Schedulers::ResourceTree::delete_tree_nodes_with_not_enough_resources($result); return($result); } # manage the job key if option is activated # read job key file if import from file # generate a job key if no import. # function returns with $job_key_priv and $job_key_pub set if $use_job_key is set. sub job_key_management($$$$) { my ($use_job_key,$import_job_key_inline,$import_job_key_file,$export_job_key_file) = @_; my $job_key_priv = ''; my $job_key_pub = ''; if (defined ($use_job_key) and !($import_job_key_inline ne "") and !($import_job_key_file ne "") and defined($ENV{OAR_JOB_KEY_FILE})){ $import_job_key_file=$ENV{OAR_JOB_KEY_FILE}; } if ((!defined($use_job_key)) and (($import_job_key_inline ne "") or ($import_job_key_file ne "") or ($export_job_key_file ne ""))){ warn("Error: You must set the --use-job-key (or -k) option in order to use other job key related options.\n"); return(-15,'',''); } if (defined($use_job_key)){ if (($import_job_key_inline ne "") and ($import_job_key_file ne "")){ warn("Error: You cannot import a job key both inline and from a file at the same time.\n"); return(-15,'',''); } my $tmp_job_key_file = OAR::Tools::get_default_oarexec_directory()."/oarsub_$$.jobkey"; if (($import_job_key_inline ne "") or ($import_job_key_file ne "")){ # job key is imported if ($import_job_key_inline ne "") { # inline import print ("Import job key inline.\n"); unless (sysopen(FH,"$tmp_job_key_file",O_CREAT|O_WRONLY,0600)) { warn("Error: Cannot open tmp file for writing: $tmp_job_key_file\n"); return(-14,'',''); } syswrite(FH,$import_job_key_inline); close(F); } else { # file import print ("Import job key from file: $import_job_key_file\n"); my $lusr= $ENV{OARDO_USER}; # read key files: oardodo su - user needed in order to be able to read the file for sure # safer way to do a `cmd`, see perl cookbook my $pid; die "cannot fork: $!" unless defined ($pid = open(SAFE_CHILD, "-|")); if ($pid == 0) { $ENV{OARDO_BECOME_USER} = $lusr; unless (exec({"oardodo"} "oardodo","cat $import_job_key_file")) { warn ("Error: Cannot cannot read key file:$import_job_key_file\n"); exit(-14); } exit(0); }else{ unless (sysopen(FH,"$tmp_job_key_file",O_CREAT|O_WRONLY,0600)) { warn("Error: Cannot open tmp file for writing: $tmp_job_key_file\n"); return(-14,'',''); } while () { syswrite(FH,$_); } close(FH); } close(SAFE_CHILD); } # extract the public key from the private one system({"bash"} "bash","-c","SSH_ASKPASS=/bin/true ssh-keygen -y -f $tmp_job_key_file < /dev/null 2> /dev/null > $tmp_job_key_file.pub"); if ($? != 0){ warn ("Error: Fail to extract the public key. Please verify that the job key to import is valid.\n"); if (-e $tmp_job_key_file) { unlink($tmp_job_key_file); } if (-e $tmp_job_key_file.".pub") { unlink($tmp_job_key_file.".pub"); } return(-14,'',''); } } else { # we must generate the key print("Generate a job key...\n"); # ssh-keygen: no passphrase, smallest key (1024 bits), ssh2 rsa faster than dsa. system({"bash"} "bash","-c",'ssh-keygen -b 1024 -N "" -t rsa -f "'.$tmp_job_key_file.'" > /dev/null'); if ($? != 0) { warn ("Error: Job key generation failed ($?).\n"); return(-14,'',''); } } # priv and pub key file must now exist. unless (open(F, "< $tmp_job_key_file")){ warn ("Error: fail to read private key.\n"); return(-14,'',''); } while ($_ = ){ $job_key_priv .= $_; } close(F); unless (open(F, "< $tmp_job_key_file.pub")){ warn ("Error: fail to read private key.\n"); return(-14,'',''); } while ($_ = ){ $job_key_pub .= $_; } close(F); unlink($tmp_job_key_file,$tmp_job_key_file.".pub"); } # last checks if (defined($use_job_key)){ if ($job_key_pub eq "") { warn("Error: missing job public key (private key found).\n"); return(-15,'',''); } if ($job_key_priv eq "") { warn("Error: missing job private key (public key found).\n"); return(-15,'',''); } if ($job_key_pub !~ /^(ssh-rsa|ssh-dss)\s.+\n*$/){ warn("Error: Bad job key format. The public key must begin with either `ssh-rsa' or `ssh-dss' and is only 1 line.\n"); return(-14,'',''); } $job_key_pub =~ s/\n//g; } return(0,$job_key_priv, $job_key_pub); } # add_micheline_job # adds a new job(or multiple in case of array-job) to the table Jobs applying # the admission rules from the base parameters : base, jobtype, nbnodes, # weight, command, infotype, maxtime, queuename, jobproperties, # startTimeReservation # return value : ref. of array of created jobids # side effects : adds an entry to the table Jobs # the first jobid is found taking the maximal jobid from # jobs in the table plus 1, the next (if any) takes the next # jobid. Array-job submission is atomic and array_index are # sequential # the rules in the base are pieces of perl code directly # evaluated here, so in theory any side effect is possible # in normal use, the unique effect of an admission rule should # be to change parameters sub add_micheline_job($$$$$$$$$$$$$$$$$$$$$$$$$$$$$){ my ($dbh, $dbh_ro, $jobType, $ref_resource_list, $command, $infoType, $queue_name, $jobproperties, $startTimeReservation, $idFile, $checkpoint, $checkpoint_signal, $notify, $job_name,$job_env,$type_list,$launching_directory,$anterior_ref,$stdout,$stderr,$job_hold,$project,$use_job_key,$import_job_key_inline,$import_job_key_file,$export_job_key_file,$initial_request_string, $array_job_nb,$array_params_ref) = @_; my $array_id = 0; my $default_walltime = "3600"; my $startTimeJob = "0"; my $reservationField = "None"; #Test if this job is a reservation if ($startTimeReservation > 0){ $reservationField = "toSchedule"; $startTimeJob = $startTimeReservation; } my $rules; my $user= $ENV{OARDO_USER}; # Verify notify syntax if ((defined($notify)) and ($notify !~ m/^\s*(mail:|exec:).+$/m)){ warn("/!\\Bad syntax for the notify option\n"); return(-6); } # # Verify job name # if ($job_name !~ m/^\w*$/m){ # warn("ERROR : The job name must contain only alphanumeric characters plus '_'\n"); # return(-7); # } # # Verify the content of user command # if ( "$command" !~ m/^[\w\s\/\.\-]*$/m ){ # warn("ERROR : The command to launch contains bad characters -- $command\n"); # return(-4); # } # Verify the content of env variables if ( "$job_env" !~ m/^[\w\=\s\/\.\-\"]*$/m ){ warn("ERROR : The specified environnement variables contains bad characters -- $job_env\n"); return(-9); } #Retrieve Micheline's rules from the table my $sth = $dbh->prepare("SELECT rule FROM admission_rules ORDER BY id"); $sth->execute(); while (my $ref = $sth->fetchrow_hashref()) { $rules = $rules.$ref->{'rule'}; } $sth->finish(); #Apply rules eval $rules; if ($@) { warn("Admission Rule ERROR : $@ \n"); return(-2); } #Test if the queue exists my %all_queues = get_all_queue_informations($dbh); if (!defined($all_queues{$queue_name})){ warn("ERROR : The queue $queue_name does not exist\n"); return(-8); } my $array_index = 1; my @Job_id_list; if(!defined($array_params_ref)){ for (my $i=0; $i<$array_job_nb; $i++){ my ($err,$ssh_priv_key,$ssh_pub_key) = job_key_management($use_job_key,$import_job_key_inline,$import_job_key_file,$export_job_key_file); if ($err != 0){ push(@Job_id_list, $err); return(\@Job_id_list); } push(@Job_id_list, add_micheline_subjob($dbh, $dbh_ro, $jobType, $ref_resource_list, $command, $infoType, $queue_name, $jobproperties, $startTimeReservation, $idFile, $checkpoint, $checkpoint_signal, $notify, $job_name,$job_env,$type_list,$launching_directory,$anterior_ref,$stdout,$stderr,$job_hold,$project,$ssh_priv_key,$ssh_pub_key,$initial_request_string, $array_id, $user, $reservationField, $startTimeJob, $default_walltime, $array_index)); if ($Job_id_list[-1] <= 0){ return(\@Job_id_list); }else{ if ($array_id <= 0){ $array_id = $Job_id_list[0]; } $array_index++; } if (defined($use_job_key) and ($export_job_key_file ne "")){ # we must copy the keys in the directory specified with the right name my $export_job_key_file_tmp = $export_job_key_file; $export_job_key_file_tmp =~ s/%jobid%/$Job_id_list[-1]/g; my $lusr= $ENV{OARDO_USER}; my $pid; # write the private job key with the user ownership unless (defined ($pid = open(SAFE_CHILD, "|-"))) { warn ("Error: Cannot open pipe ($?)"); exit(-14); } if ($pid == 0) { umask(oct("177")); $ENV{OARDO_BECOME_USER} = $lusr; open(STDERR, ">/dev/null"); unless (exec({"oardodo"} "oardodo","dd of=$export_job_key_file_tmp")) { warn ("Error: Cannot exec user shell ($?)"); exit(-14); } }else{ print SAFE_CHILD $ssh_priv_key; unless (close(SAFE_CHILD)) { warn ("Error: Cannot close pipe {$?}"); push(@Job_id_list,-14); return(@Job_id_list); } } print "Export job key to file: ".$export_job_key_file_tmp."\n"; } } }else{ foreach my $param (@{$array_params_ref}){ my ($err,$ssh_priv_key,$ssh_pub_key) = job_key_management($use_job_key,$import_job_key_inline,$import_job_key_file,$export_job_key_file); if ($err != 0){ push(@Job_id_list, $err); return(\@Job_id_list); } push(@Job_id_list, add_micheline_subjob($dbh, $dbh_ro, $jobType, $ref_resource_list, $command." $param", $infoType, $queue_name, $jobproperties, $startTimeReservation, $idFile, $checkpoint, $checkpoint_signal, $notify, $job_name,$job_env,$type_list,$launching_directory,$anterior_ref,$stdout,$stderr,$job_hold,$project,$ssh_priv_key,$ssh_pub_key,$initial_request_string, $array_id, $user, $reservationField, $startTimeJob, $default_walltime, $array_index)); if ($Job_id_list[-1] <= 0){ return(\@Job_id_list); }else{ $array_index++; if ($array_id <= 0){ $array_id = $Job_id_list[0]; } } if (defined($use_job_key) and ($export_job_key_file ne "")){ # we must copy the keys in the directory specified with the right name my $export_job_key_file_tmp = $export_job_key_file; $export_job_key_file_tmp =~ s/%jobid%/$Job_id_list[-1]/g; my $lusr= $ENV{OARDO_USER}; my $pid; # write the private job key with the user ownership unless (defined ($pid = open(SAFE_CHILD, "|-"))) { warn ("Error: Cannot open pipe ($?)"); exit(-14); } if ($pid == 0) { umask(oct("177")); $ENV{OARDO_BECOME_USER} = $lusr; open(STDERR, ">/dev/null"); unless (exec({"oardodo"} "oardodo","dd of=$export_job_key_file_tmp")) { warn ("Error: Cannot exec user shell ($?)"); push(@Job_id_list,-14); return(@Job_id_list); } }else{ print SAFE_CHILD $ssh_priv_key; unless (close(SAFE_CHILD)) { warn ("Error: Cannot close pipe {$?}"); push(@Job_id_list,-14); return(@Job_id_list); } } print "Export job key to file: ".$export_job_key_file_tmp."\n"; } } } return(\@Job_id_list); } sub add_micheline_subjob($$$$$$$$$$$$$$$$$$$$$$$$$$$$$$){ my ($dbh, $dbh_ro, $jobType, $ref_resource_list, $command, $infoType, $queue_name, $jobproperties, $startTimeReservation, $idFile, $checkpoint, $checkpoint_signal, $notify, $job_name,$job_env,$type_list,$launching_directory,$anterior_ref,$stdout,$stderr,$job_hold,$project,$ssh_priv_key,$ssh_pub_key,$initial_request_string, $array_id, $user, $reservationField, $startTimeJob, $default_walltime, $array_index) = @_; # Test if properties and resources are coherent my @dead_resources; foreach my $r (OAR::IO::get_resources_in_state($dbh,"Dead")){ push(@dead_resources, $r->{resource_id}); } my $wanted_resources; foreach my $moldable_resource (@{$ref_resource_list}){ if (!defined($moldable_resource->[1])){ $moldable_resource->[1] = $default_walltime; } my $resource_id_list_vector = ''; foreach my $r (@{$moldable_resource->[0]}){ # SECURITY : we must use read only database access for this request my $tmp_properties = $r->{property}; if ((defined($jobproperties)) and ($jobproperties ne "")){ if (!defined($tmp_properties)){ $tmp_properties = $jobproperties; }else{ $tmp_properties = "($tmp_properties) AND ($jobproperties)" } } my $tree = get_possible_wanted_resources($dbh_ro, undef, $resource_id_list_vector, \@dead_resources, $tmp_properties, $r->{resources}, undef); $tree = OAR::Schedulers::ResourceTree::delete_unnecessary_subtrees($tree); if (!defined($tree)){ # Resource description does not match with the content of the database if ($DBI::errstr ne ""){ my $tmp_err = $DBI::errstr; chop($tmp_err); warn("Bad resource request ($tmp_err)\n"); }else{ warn("There are not enough resources for your request\n"); } return(-5); }else{ my @leafs = OAR::Schedulers::ResourceTree::get_tree_leafs($tree); foreach my $l (@leafs){ vec($resource_id_list_vector, OAR::Schedulers::ResourceTree::get_current_resource_value($l), 1) = 1; } } } } lock_table($dbh,["challenges","jobs"]); # Verify the content of the ssh keys if (($ssh_pub_key ne "") or ($ssh_priv_key ne "")){ # Check if the keys are used by other jobs if (get_count_same_ssh_keys_current_jobs($dbh,$user,$ssh_priv_key,$ssh_pub_key) > 0){ warn("/!\\ Another job is using the same ssh keys\n"); return(-10); } } # Check the user validity if (! $user =~ /[a-zA-Z0-9_-]+/ ) { warn("/!\\ Invalid username: '$user'\n"); return(-11); } #Insert job my $date = get_date($dbh); #lock_table($dbh,["jobs"]); my $job_name_quoted = $dbh->quote($job_name); $notify = $dbh->quote($notify); $command = $dbh->quote($command); $job_env = $dbh->quote($job_env); $jobproperties = $dbh->quote($jobproperties); $launching_directory = $dbh->quote($launching_directory); $project = $dbh->quote($project); $initial_request_string = $dbh->quote($initial_request_string); $dbh->do("INSERT INTO jobs (job_type,info_type,state,job_user,command,submission_time,queue_name,properties,launching_directory,reservation,start_time,file_id,checkpoint,job_name,notify,checkpoint_signal,job_env,project,initial_request,array_id,array_index) VALUES (\'$jobType\',\'$infoType\',\'Hold\',\'$user\',$command,\'$date\',\'$queue_name\',$jobproperties,$launching_directory,\'$reservationField\',\'$startTimeJob\',$idFile,$checkpoint,$job_name_quoted,$notify,\'$checkpoint_signal\',$job_env,$project,$initial_request_string,$array_id,$array_index) "); my $job_id = get_last_insert_id($dbh,"jobs_job_id_seq"); #unlock_table($dbh); if ($array_id <= 0){ $dbh->do(" UPDATE jobs SET array_id = $job_id WHERE job_id = $job_id "); } $ssh_priv_key = $dbh->quote($ssh_priv_key); $ssh_pub_key = $dbh->quote($ssh_pub_key); my $random_number = int(rand(1000000000000)); $dbh->do("INSERT INTO challenges (job_id,challenge,ssh_private_key,ssh_public_key) VALUES ($job_id,\'$random_number\',$ssh_priv_key,$ssh_pub_key) "); unlock_table($dbh); if (!defined($stdout) or ($stdout eq "")){ $stdout = "OAR"; $stdout .= ".$job_name" if (defined($job_name)); $stdout .= '.%jobid%.stdout'; } if (!defined($stderr) or ($stderr eq "")){ $stderr = "OAR"; $stderr .= ".$job_name" if (defined($job_name)); $stderr .= '.%jobid%.stderr'; } $stdout = $dbh->quote($stdout); $stderr = $dbh->quote($stderr); $dbh->do("UPDATE jobs SET stdout_file = $stdout, stderr_file = $stderr WHERE state = \'Hold\' AND job_id = $job_id "); foreach my $moldable_resource (@{$ref_resource_list}){ #lock_table($dbh,["moldable_job_descriptions"]); $dbh->do(" INSERT INTO moldable_job_descriptions (moldable_job_id,moldable_walltime) VALUES ($job_id,\'$moldable_resource->[1]\') "); my $moldable_id = get_last_insert_id($dbh,"moldable_job_descriptions_moldable_id_seq"); #unlock_table($dbh); foreach my $r (@{$moldable_resource->[0]}){ #lock_table($dbh,["job_resource_groups"]); my $property = $r->{property}; $property = $dbh->quote($property); $dbh->do(" INSERT INTO job_resource_groups (res_group_moldable_id,res_group_property) VALUES ($moldable_id,$property) "); my $res_group_id = get_last_insert_id($dbh,"job_resource_groups_res_group_id_seq"); #unlock_table($dbh); my $order = 0; foreach my $l (@{$r->{resources}}){ $dbh->do(" INSERT INTO job_resource_descriptions (res_job_group_id,res_job_resource_type,res_job_value,res_job_order) VALUES ($res_group_id,\'$l->{resource}\',\'$l->{value}\',$order) "); $order ++; } } } foreach my $t (@{$type_list}){ my $quoted_t = $dbh->quote($t); $dbh->do(" INSERT INTO job_types (job_id,type) VALUES ($job_id,$quoted_t) "); } foreach my $a (@{$anterior_ref}){ $dbh->do(" INSERT INTO job_dependencies (job_id,job_id_required) VALUES ($job_id,$a) "); } if (!defined($job_hold)) { $dbh->do("INSERT INTO job_state_logs (job_id,job_state,date_start) VALUES ($job_id,\'Waiting\',$date) "); $dbh->do(" UPDATE jobs SET state = \'Waiting\' WHERE job_id = $job_id "); }else{ $dbh->do("INSERT INTO job_state_logs (job_id,job_state,date_start) VALUES ($job_id,\'Hold\',$date) "); } #$dbh->do("UNLOCK TABLES"); return($job_id); } # get_job # returns a ref to some hash containing data for the job of id passed in # parameter # parameters : base, jobid # return value : ref # side effects : / sub get_job($$) { my $dbh = shift; my $idJob = shift; my $sth = $dbh->prepare(" SELECT * FROM jobs WHERE job_id = $idJob "); $sth->execute(); my $ref = $sth->fetchrow_hashref(); $sth->finish(); return($ref); } # get_job_state # returns only the state of a job # parameter # parameters : base, jobid # return value : string # side effects : / sub get_job_state($$) { my $dbh = shift; my $idJob = shift; my @res = $dbh->selectrow_array(" SELECT state FROM jobs WHERE job_id = $idJob "); return($res[0]); } # get_current_moldable_job # returns a ref to some hash containing data for the moldable job of id passed in # parameter # parameters : base, moldable job id # return value : ref # side effects : / sub get_current_moldable_job($$) { my $dbh = shift; my $moldableJobId = shift; my $sth = $dbh->prepare(" SELECT * FROM moldable_job_descriptions WHERE moldable_index = \'CURRENT\' AND moldable_id = $moldableJobId "); $sth->execute(); my $ref = $sth->fetchrow_hashref(); $sth->finish(); return $ref; } # get_moldable_job # returns a ref to some hash containing data for the moldable job of id passed in # parameter # parameters : base, moldable job id # return value : ref # side effects : / sub get_moldable_job($$) { my $dbh = shift; my $moldableJobId = shift; my $sth = $dbh->prepare(" SELECT * FROM moldable_job_descriptions WHERE moldable_id = $moldableJobId "); $sth->execute(); my $ref = $sth->fetchrow_hashref(); $sth->finish(); return $ref; } # get_scheduled_job_description # returns a ref to some hash containing data for the moldable job corresponding # to the waiting job given by the id passed in # parameter # parameters : base, moldable job id # return value : ref # side effects : / sub get_scheduled_job_description($$) { my $dbh = shift; my $job_id = shift; my $sth = $dbh->prepare(" SELECT * FROM moldable_job_descriptions,gantt_jobs_predictions_visu WHERE moldable_job_descriptions.moldable_job_id = $job_id AND gantt_jobs_predictions_visu.moldable_job_id = moldable_id "); $sth->execute(); my $ref = $sth->fetchrow_hashref(); $sth->finish(); return $ref; } # set_job_state # sets the state field of the job of id passed in parameter # parameters : base, jobid, state # return value : / # side effects : changes the field reservation of the job in the table Jobs sub set_job_state($$$) { my $dbh = shift; my $job_id = shift; my $state = shift; if ($dbh->do(" UPDATE jobs SET state = \'$state\' WHERE job_id = $job_id AND state != \'Error\' AND state != \'Terminated\' AND state != \'$state\' ") > 0){ my $date = get_date($dbh); $dbh->do(" UPDATE job_state_logs SET date_stop = \'$date\' WHERE date_stop = 0 AND job_id = $job_id "); $dbh->do(" INSERT INTO job_state_logs (job_id,job_state,date_start) VALUES ($job_id,\'$state\',\'$date\') "); if (($state eq "Terminated") or ($state eq "Error") or ($state eq "toLaunch") or ($state eq "Running") or ($state eq "Suspended") or ($state eq "Resuming")){ my $job = get_job($dbh,$job_id); my ($addr,$port) = split(/:/,$job->{info_type}); if ($state eq "Suspended"){ OAR::Modules::Judas::notify_user($dbh,$job->{notify},$addr,$job->{job_user},$job->{job_id},$job->{job_name},"SUSPENDED","Job is suspended."); }elsif ($state eq "Resuming"){ OAR::Modules::Judas::notify_user($dbh,$job->{notify},$addr,$job->{job_user},$job->{job_id},$job->{job_name},"RESUMING","Job is resuming."); }elsif ($state eq "Running"){ OAR::Modules::Judas::notify_user($dbh,$job->{notify},$addr,$job->{job_user},$job->{job_id},$job->{job_name},"RUNNING","Job is running."); }elsif ($state eq "toLaunch"){ update_current_scheduler_priority($dbh,$job->{job_id},$job->{assigned_moldable_job},"+2","START"); }elsif (($state eq "Terminated") or ($state eq "Error")){ #$dbh->do(" DELETE FROM challenges # WHERE job_id = $job_id # "); if ($job->{stop_time} < $job->{start_time}){ $dbh->do(" UPDATE jobs SET stop_time = start_time WHERE job_id = $job_id "); } if (defined($job->{assigned_moldable_job}) and ($job->{assigned_moldable_job} ne "")){ # Update last_job_date field for resources used OAR::IO::update_scheduler_last_job_date($dbh, $date,$job->{assigned_moldable_job}); } if ($state eq "Terminated"){ OAR::Modules::Judas::notify_user($dbh,$job->{notify},$addr,$job->{job_user},$job->{job_id},$job->{job_name},"END","Job stopped normally."); }else{ # Verify if the job was suspended and if the resource # property suspended is updated if ($job->{suspended} eq "YES"){ my @r = get_current_resources_with_suspended_job($dbh); if ($#r >= 0){ $dbh->do(" UPDATE resources SET suspended_jobs = \'NO\' WHERE resource_id NOT IN (".join(",",@r).") "); }else{ $dbh->do(" UPDATE resources SET suspended_jobs = \'NO\' "); } } OAR::Modules::Judas::notify_user($dbh,$job->{notify},$addr,$job->{job_user},$job->{job_id},$job->{job_name},"ERROR","Job stopped abnormally or an OAR error occured."); } update_current_scheduler_priority($dbh,$job->{job_id},$job->{assigned_moldable_job},"-2","STOP"); # Here we must not be asynchronously with the scheduler OAR::IO::log_job($dbh,$job->{job_id}); # $dbh is valid so these 2 variables must be defined OAR::Tools::notify_tcp_socket($Remote_host,$Remote_port,"ChState"); } } } } # log_job # sets the index fields to LOG on several tables # this will speed up future queries # parameters : base, jobid # return value : / sub log_job($$){ my $dbh = shift; my $job_id = shift; my $job = get_job($dbh,$job_id); if ($Db_type eq "Pg"){ $dbh->do(" UPDATE moldable_job_descriptions SET moldable_index = \'LOG\' WHERE moldable_job_descriptions.moldable_index = \'CURRENT\' AND moldable_job_descriptions.moldable_job_id = $job_id "); $dbh->do(" UPDATE job_resource_descriptions SET res_job_index = \'LOG\' FROM moldable_job_descriptions, job_resource_groups WHERE job_resource_groups.res_group_index = \'CURRENT\' AND moldable_job_descriptions.moldable_index = \'LOG\' AND job_resource_descriptions.res_job_index = \'CURRENT\' AND moldable_job_descriptions.moldable_job_id = $job_id AND job_resource_groups.res_group_moldable_id = moldable_job_descriptions.moldable_id AND job_resource_descriptions.res_job_group_id = job_resource_groups.res_group_id "); $dbh->do(" UPDATE job_resource_groups SET res_group_index = \'LOG\' FROM moldable_job_descriptions WHERE job_resource_groups.res_group_index = \'CURRENT\' AND moldable_job_descriptions.moldable_index = \'LOG\' AND moldable_job_descriptions.moldable_job_id = $job_id AND job_resource_groups.res_group_moldable_id = moldable_job_descriptions.moldable_id "); }else{ $dbh->do(" UPDATE moldable_job_descriptions, job_resource_groups, job_resource_descriptions SET job_resource_groups.res_group_index = \'LOG\', job_resource_descriptions.res_job_index = \'LOG\', moldable_job_descriptions.moldable_index = \'LOG\' WHERE moldable_job_descriptions.moldable_index = \'CURRENT\' AND job_resource_groups.res_group_index = \'CURRENT\' AND job_resource_descriptions.res_job_index = \'CURRENT\' AND moldable_job_descriptions.moldable_job_id = $job_id AND job_resource_groups.res_group_moldable_id = moldable_job_descriptions.moldable_id AND job_resource_descriptions.res_job_group_id = job_resource_groups.res_group_id "); } $dbh->do(" UPDATE job_types SET types_index = \'LOG\' WHERE job_types.types_index = \'CURRENT\' AND job_types.job_id = $job_id "); $dbh->do(" UPDATE job_dependencies SET job_dependency_index = \'LOG\' WHERE job_dependencies.job_dependency_index = \'CURRENT\' AND job_dependencies.job_id = $job_id "); if (defined($job->{assigned_moldable_job}) and ($job->{assigned_moldable_job} ne "")){ $dbh->do(" UPDATE assigned_resources SET assigned_resource_index = \'LOG\' WHERE assigned_resource_index = \'CURRENT\' AND moldable_job_id = $job->{assigned_moldable_job} "); } } # archive_some_moldable_job_nodes # sets the index fields to LOG in the table assigned_resources # parameters : base, mjobid, hostnames # return value : / sub archive_some_moldable_job_nodes($$$){ my $dbh = shift; my $mjob_id = shift; my $hosts = shift; my $value_str; foreach my $v (@{$hosts}){ $value_str .= $dbh->quote($v); $value_str .= ','; } chop($value_str); if ($Db_type eq "Pg"){ $dbh->do(" UPDATE assigned_resources SET assigned_resource_index = \'LOG\' FROM resources WHERE assigned_resources.assigned_resource_index = \'CURRENT\' AND assigned_resources.moldable_job_id = $mjob_id AND resources.resource_id = assigned_resources.resource_id AND resources.network_address IN (".$value_str.") "); }else{ $dbh->do(" UPDATE assigned_resources, resources SET assigned_resources.assigned_resource_index = \'LOG\' WHERE assigned_resources.assigned_resource_index = \'CURRENT\' AND assigned_resources.moldable_job_id = $mjob_id AND resources.resource_id = assigned_resources.resource_id AND resources.network_address IN (".$value_str.") "); } } # Resubmit a job and give the new job_id # args : database, job id sub resubmit_job($$){ my $dbh = shift; my $job_id = shift; my $lusr= $ENV{OARDO_USER}; my $job = get_job($dbh, $job_id); return(0) if (!defined($job->{job_id})); return(-1) if ($job->{job_type} ne "PASSIVE"); return(-2) if (($job->{state} ne "Error") and ($job->{state} ne "Terminated") and ($job->{state} ne "Finishing")); return(-3) if (($lusr ne $job->{job_user}) and ($lusr ne "oar") and ($lusr ne "root")); lock_table($dbh,["challenges","jobs"]); # Verify the content of the ssh keys my ($job_challenge,$ssh_private_key,$ssh_public_key) = OAR::IO::get_job_challenge($dbh,$job_id); if (($ssh_public_key ne "") or ($ssh_private_key ne "")){ # Check if the keys are used by other jobs if (get_count_same_ssh_keys_current_jobs($dbh,$job->{job_user},$ssh_private_key,$ssh_public_key) > 0){ return(-4); } } my $command = $dbh->quote($job->{command}); my $jobproperties = $dbh->quote($job->{properties}); my $launching_directory = $dbh->quote($job->{launching_directory}); my $file_id = $dbh->quote($job->{file_id}); my $jenv = $dbh->quote($job->{job_env}); my $project = $dbh->quote($job->{project}); my $initial_request_string = $dbh->quote($job->{initial_request}); my $job_name = $dbh->quote($job->{job_name}); my $date = get_date($dbh); my $start_time = 0; $start_time = $job->{start_time} if ($job->{reservation} ne "None"); #lock_table($dbh,["jobs"]); $dbh->do("INSERT INTO jobs (job_type,info_type,state,job_user,command,submission_time,queue_name,properties,launching_directory,file_id,checkpoint,job_name,notify,checkpoint_signal,reservation,resubmit_job_id,start_time,job_env,project,initial_request,array_id,array_index) VALUES (\'$job->{job_type}\',\'$job->{info_type}\',\'Hold\',\'$job->{job_user}\',$command,\'$date\',\'$job->{queue_name}\',$jobproperties,$launching_directory,$file_id,$job->{checkpoint},$job_name,\'$job->{notify}\',\'$job->{checkpoint_signal}\',\'$job->{reservation}\',$job_id,\'$start_time\',$jenv,$project,$initial_request_string,$job->{array_id},$job->{array_index}) "); my $new_job_id = get_last_insert_id($dbh,"jobs_job_id_seq"); #unlock_table($dbh); my $random_number = int(rand(1000000000000)); #$dbh->do("INSERT INTO challenges (job_id,challenge) # VALUES ($new_job_id,\'$random_number\') # "); my $pub_key = ""; my $priv_key = ""; my $sth = $dbh->prepare(" SELECT ssh_private_key, ssh_public_key FROM challenges WHERE job_id = $job_id "); $sth->execute(); my $ref_keys = $sth->fetchrow_hashref(); $sth->finish(); if (defined($ref_keys)){ $priv_key = $ref_keys->{ssh_private_key}; $pub_key = $ref_keys->{ssh_public_key}; } $priv_key = $dbh->quote($priv_key); $pub_key = $dbh->quote($pub_key); $dbh->do("INSERT INTO challenges (job_id,challenge,ssh_private_key,ssh_public_key) VALUES ($new_job_id,\'$random_number\',$priv_key,$pub_key) "); unlock_table($dbh); my $stdout_file = $dbh->quote($job->{stdout_file}); my $stderr_file = $dbh->quote($job->{stderr_file}); $dbh->do("UPDATE jobs SET stdout_file = $stdout_file, stderr_file = $stderr_file WHERE state = \'Hold\' AND job_id = $new_job_id "); $sth = $dbh->prepare(" SELECT moldable_id,moldable_walltime FROM moldable_job_descriptions WHERE moldable_job_id = $job_id "); $sth->execute(); my @moldable_ids = (); while (my @ref = $sth->fetchrow_array()) { push(@moldable_ids, [$ref[0], $ref[1]]); } $sth->finish(); foreach my $m (@moldable_ids){ my $moldable_resource = $m->[0]; #lock_table($dbh,["moldable_job_descriptions"]); $dbh->do(" INSERT INTO moldable_job_descriptions (moldable_job_id,moldable_walltime) VALUES ($new_job_id,\'$m->[1]\') "); my $moldable_id = get_last_insert_id($dbh,"moldable_job_descriptions_moldable_id_seq"); #unlock_table($dbh); $sth = $dbh->prepare(" SELECT res_group_id,res_group_property FROM job_resource_groups WHERE res_group_moldable_id = $moldable_resource "); $sth->execute(); my @groups = (); while (my @ref = $sth->fetchrow_array()) { push(@groups, [$ref[0],$ref[1]]); } $sth->finish(); foreach my $res (@groups){ my $r = $res->[0]; #lock_table($dbh,["job_resource_groups"]); my $prop = $dbh->quote($res->[1]); $dbh->do(" INSERT INTO job_resource_groups (res_group_moldable_id,res_group_property) VALUES ($moldable_id,$prop) "); my $res_group_id = get_last_insert_id($dbh,"job_resource_groups_res_group_id_seq"); #unlock_table($dbh); $sth = $dbh->prepare(" SELECT res_job_group_id,res_job_resource_type,res_job_value,res_job_order FROM job_resource_descriptions WHERE res_job_group_id = $r "); $sth->execute(); my @groups_desc = (); while (my @ref = $sth->fetchrow_array()) { push(@groups_desc, [$ref[0],$ref[1],$ref[2],$ref[3]]); } $sth->finish(); foreach my $d (@groups_desc){ $dbh->do(" INSERT INTO job_resource_descriptions (res_job_group_id,res_job_resource_type,res_job_value,res_job_order) VALUES ($res_group_id,\'$d->[1]\',$d->[2],$d->[3]) "); } } } $sth = $dbh->prepare(" SELECT type FROM job_types WHERE job_id = $job_id "); $sth->execute(); my @types = (); while (my @ref = $sth->fetchrow_array()) { push(@types, $ref[0]); } $sth->finish(); foreach my $t (@types){ $t = $dbh->quote($t); $dbh->do(" INSERT INTO job_types (job_id,type) VALUES($new_job_id, $t) "); } $dbh->do(" UPDATE job_dependencies SET job_id_required = $new_job_id WHERE job_id_required = $job_id "); $dbh->do("INSERT INTO job_state_logs (job_id,job_state,date_start) VALUES ($new_job_id,\'Waiting\',$date) "); $dbh->do(" UPDATE jobs SET state = \'Waiting\' WHERE job_id = $new_job_id "); return($new_job_id); } # is_job_already_resubmitted # Check if the job was already resubmitted # args : db ref, job id sub is_job_already_resubmitted($$){ my $dbh = shift; my $job_id = shift; my $sth = $dbh->prepare(" SELECT COUNT(*) FROM jobs WHERE resubmit_job_id = $job_id "); $sth->execute(); my @ref = $sth->fetchrow_array(); $sth->finish(); return($ref[0]); } # set_job_resa_state # sets the reservation field of the job of id passed in parameter # parameters : base, jobid, state # return value : / # side effects : changes the field state of the job in the table Jobs sub set_job_resa_state($$$){ my $dbh = shift; my $idJob = shift; my $state = shift; my $sth = $dbh->prepare("UPDATE jobs SET reservation = \'$state\' WHERE job_id = $idJob"); $sth->execute(); $sth->finish(); } # set_job_message # sets the message field of the job of id passed in parameter # parameters : base, jobid, message # return value : / # side effects : changes the field message of the job in the table Jobs sub set_job_message($$$) { my $dbh = shift; my $idJob = shift; my $message = shift; $message = $dbh->quote($message); $dbh->do(" UPDATE jobs SET message = $message WHERE job_id = $idJob "); } # set_job_scheduler_info # sets the scheduler_info field of the job of id passed in parameter # parameters : base, jobid, message # return value : / sub set_job_scheduler_info($$$) { my $dbh = shift; my $idJob = shift; my $message = shift; $message = $dbh->quote($message); $dbh->do(" UPDATE jobs SET scheduler_info = $message WHERE job_id = $idJob "); } # frag_job # sets the flag 'ToFrag' of a job to 'Yes' # parameters : base, jobid # return value : 0 on success, -1 on error (if the user calling this method # is not the user running the job or oar), -2 if the job was # already killed # side effects : changes the field ToFrag of the job in the table Jobs sub frag_job($$) { my $dbh = shift; my $idJob = shift; my $lusr= $ENV{OARDO_USER}; my $job = get_job($dbh, $idJob); if((defined($job)) && (($lusr eq $job->{job_user}) or ($lusr eq "oar") or ($lusr eq "root"))) { my $nbRes = $dbh->do("SELECT * FROM frag_jobs WHERE frag_id_job = $idJob "); if ( $nbRes < 1 ){ my $date = get_date($dbh); $dbh->do("INSERT INTO frag_jobs (frag_id_job,frag_date) VALUES ($idJob,\'$date\') "); add_new_event($dbh,"FRAG_JOB_REQUEST",$idJob,"User $lusr requested to frag the job $idJob"); return(0); }else{ # Job already killed return(-2); } }else{ return(-1); } } # ask_checkpoint_job # Verify if the user is able to checkpoint the job # args : database ref, job id # returns : 0 if all is good, 1 if the user cannot do this, 2 if the job is not running, 3 if the job is Interactive sub ask_checkpoint_job($$){ my $dbh = shift; my $idJob = shift; my $lusr= $ENV{OARDO_USER}; my $job = get_job($dbh, $idJob); return(3) if ((defined($job)) and ($job->{job_type} eq "INTERACTIVE")); if((defined($job)) && (($lusr eq $job->{job_user}) or ($lusr eq "oar") or ($lusr eq "root"))) { if ($job->{state} eq "Running"){ #$dbh->do("LOCK TABLE event_log WRITE"); add_new_event($dbh,"CHECKPOINT",$idJob,"User $lusr requested a checkpoint on the job $idJob"); #$dbh->do("UNLOCK TABLES"); return(0); }else{ return(2); } }else{ return(1); } } # ask_signal_job # Verify if the user is able to signal the job # args : database ref, job id, signal # returns : 0 if all is good, 1 if the user cannot do this, 2 if the job is not running, 3 if the job is Interactive sub ask_signal_job($$$){ my $dbh = shift; my $idJob = shift; my $signal = shift; my $lusr= $ENV{OARDO_USER}; my $job = get_job($dbh, $idJob); return(3) if ((defined($job)) and ($job->{job_type} eq "INTERACTIVE")); if((defined($job)) && (($lusr eq $job->{job_user}) or ($lusr eq "oar") or ($lusr eq "root"))) { if ($job->{state} eq "Running"){ #$dbh->do("LOCK TABLE event_log WRITE"); add_new_event($dbh,"SIGNAL_$signal",$idJob,"User $lusr requested the signal $signal on the job $idJob"); #$dbh->do("UNLOCK TABLES"); #oar_debug("[OAR::IO] added an event of type SIGNAL_$signal for job $idJob\n"); return(0); }else{ return(2); } }else{ return(1); } } # hold_job # sets the state field of a job to 'Hold' # equivalent to set_job_state(base,jobid,"Hold") except for permissions on user # parameters : base, jobid # return value : 0 on success, -1 on error (if the user calling this method # is not the user running the job) # side effects : changes the field state of the job to 'Hold' in the table Jobs sub hold_job($$$) { my $dbh = shift; my $idJob = shift; my $waiting_and_running = shift; my $lusr = $ENV{OARDO_USER}; my $job = get_job($dbh, $idJob); my $event_type = "HOLD_WAITING_JOB"; $event_type = "HOLD_RUNNING_JOB" if (defined($waiting_and_running)); if (defined($job)){ if (defined($waiting_and_running) and ($lusr ne "oar") and ($lusr ne "root")){ return(-4); }elsif (($lusr eq $job->{job_user}) || ($lusr eq "oar") || ($lusr eq "root")){ if (($job->{'state'} eq "Waiting") or ($job->{'state'} eq "Resuming")){ add_new_event($dbh, $event_type, $idJob, "User $lusr launched oarhold on the job $idJob"); return 0; }elsif((defined($waiting_and_running)) and (($job->{state} eq "toLaunch") or ($job->{state} eq "Launching") or ($job->{state} eq "Running"))){ add_new_event($dbh, $event_type, $idJob, "User $lusr launched oarhold on the job $idJob"); return 0; }else{ return(-3); } }else{ return(-2); } }else{ return(-1); } } # resume_job # returns the state of the job from 'Hold' to 'Waiting' # equivalent to set_job_state(base,jobid,"Waiting") except for permissions on # user and the fact the job must already be in 'Hold' state # parameters : base, jobid # return value : 0 on success, -1 on error (if the user calling this method # is not the user running the job) # side effects : changes the field state of the job to 'Waiting' in the table # Jobs sub resume_job($$) { my $dbh = shift; my $idJob = shift; my $lusr = $ENV{OARDO_USER}; my $job = get_job($dbh, $idJob); if (defined($job)){ if (($job->{'state'} eq "Suspended") and ($lusr ne "oar") and ($lusr ne "root")){ return(-4); }elsif (($lusr eq $job->{job_user}) || ($lusr eq "oar") || ($lusr eq "root")){ if (($job->{'state'} eq "Hold") or ($job->{'state'} eq "Suspended")){ add_new_event($dbh, "RESUME_JOB", $idJob, "User $lusr launched oarresume on the job $idJob"); return(0); } return(-3); } return(-2); } else { return(-1); } } # get the amount of time in the suspended state of a job # args : base, job id, time in seconds sub get_job_suspended_sum_duration($$$){ my $dbh = shift; my $job_id = shift; my $current_time = shift; my $sth = $dbh->prepare(" SELECT date_start, date_stop FROM job_state_logs WHERE job_id = $job_id AND (job_state = \'Suspended\' OR job_state = \'Resuming\') "); $sth->execute(); my $sum = 0; while (my $ref = $sth->fetchrow_hashref()) { my $tmp_sum = 0; if ($ref->{date_stop} == 0){ $tmp_sum = $current_time - $ref->{date_start}; }else{ $tmp_sum += $ref->{date_stop} - $ref->{date_start}; } $sum += $tmp_sum if ($tmp_sum > 0); } $sth->finish(); return($sum); } # Return the list of jobs running on resources allocated to another given job # args : base, resume job id sub get_jobs_on_resuming_job_resources($$){ my $dbh = shift; my $job_id = shift; my $sth = $dbh->prepare(" SELECT DISTINCT(j2.job_id) as jobid FROM jobs j1,jobs j2,assigned_resources a1,assigned_resources a2 WHERE a1.assigned_resource_index = \'CURRENT\' AND a2.assigned_resource_index = \'CURRENT\' AND j1.job_id = $job_id AND a1.moldable_job_id = j1.assigned_moldable_job AND a2.resource_id = a1.resource_id AND a2.moldable_job_id = j2.assigned_moldable_job AND j2.state NOT IN (\'Suspended\',\'Resuming\') "); $sth->execute(); my @res; while (my $ref = $sth->fetchrow_hashref()) { push(@res,$ref->{jobid}); } $sth->finish(); return(@res); } # Return the list of resources where there are Suspended jobs # args: base sub get_current_resources_with_suspended_job($){ my $dbh = shift; my $sth = $dbh->prepare(" SELECT resource_id FROM assigned_resources, jobs WHERE assigned_resources.assigned_resource_index = \'CURRENT\' AND jobs.state = \'Suspended\' AND jobs.assigned_moldable_job = assigned_resources.moldable_job_id "); $sth->execute(); my @res = (); while (my $ref = $sth->fetchrow_hashref()) { push(@res, $ref->{resource_id}); } $sth->finish(); return(@res); } # suspend_job_action # perform all action when a job is suspended # parameters : base, jobid, moldable jobid sub suspend_job_action($$$) { my $dbh = shift; my $job_id = shift; my $moldable_job_id = shift; set_job_state($dbh,$job_id,"Suspended"); $dbh->do(" UPDATE jobs SET suspended = \'YES\' WHERE job_id = $job_id "); my @r = get_current_resources_with_suspended_job($dbh); $dbh->do(" UPDATE resources SET suspended_jobs = \'YES\' WHERE resource_id IN (".join(",",@r).") "); } # resume_job_action # perform all action when a job is suspended # parameters : base, jobid sub resume_job_action($$) { my $dbh = shift; my $job_id = shift; set_job_state($dbh,$job_id,"Running"); my @r = get_current_resources_with_suspended_job($dbh); if ($#r >= 0){ $dbh->do(" UPDATE resources SET suspended_jobs = \'NO\' WHERE resource_id NOT IN (".join(",",@r).") "); }else{ $dbh->do(" UPDATE resources SET suspended_jobs = \'NO\' "); } } # job_fragged # sets the flag 'ToFrag' of a job to 'No' # parameters : base, jobid # return value : / # side effects : changes the field ToFrag of the job in the table Jobs sub job_fragged($$) { my $dbh = shift; my $idJob = shift; $dbh->do("UPDATE frag_jobs SET frag_state = \'FRAGGED\' WHERE frag_id_job = $idJob "); } # job_arm_leon_timer # sets the state to TIMER_ARMED of job # parameters : base, jobid # return value : / sub job_arm_leon_timer($$) { my $dbh = shift; my $idJob = shift; $dbh->do(" UPDATE frag_jobs SET frag_state = \'TIMER_ARMED\' WHERE frag_id_job = $idJob "); } # job_refrag # sets the state to LEON of job # parameters : base, jobid # return value : / sub job_refrag($$) { my $dbh = shift; my $idJob = shift; $dbh->do("UPDATE frag_jobs SET frag_state = \'LEON\' WHERE frag_id_job = $idJob "); } # job_leon_exterminate # sets the state LEON_EXTERMINATE of job # parameters : base, jobid # return value : / sub job_leon_exterminate($$) { my $dbh = shift; my $idJob = shift; $dbh->do("UPDATE frag_jobs SET frag_state = \'LEON_EXTERMINATE\' WHERE frag_id_job = $idJob "); } # get_frag_date # gets the date of the frag of a job # parameters : base, jobid # return value : date sub get_frag_date($$) { my $dbh = shift; my $idJob = shift; my $sth = $dbh->prepare("SELECT frag_date FROM frag_jobs WHERE frag_id_job = $idJob "); $sth->execute(); my $ref = $sth->fetchrow_hashref(); $sth->finish(); return($ref->{'frag_date'}); } # Get all waiting reservation jobs # parameter : database ref # return an array of job informations sub get_waiting_reservation_jobs($){ my $dbh = shift; my $sth = $dbh->prepare(" SELECT * FROM jobs j WHERE (j.state = \'Waiting\' OR j.state = \'toAckReservation\') AND j.reservation = \'Scheduled\' ORDER BY j.job_id "); $sth->execute(); my @res = (); while (my $ref = $sth->fetchrow_hashref()) { push(@res, $ref); } $sth->finish(); return @res; } # Get all waiting reservation jobs in the specified queue # parameter : database ref, queuename # return an array of job informations sub get_waiting_reservation_jobs_specific_queue($$){ my $dbh = shift; my $queue = shift; my $sth = $dbh->prepare(" SELECT * FROM jobs j WHERE j.state=\'Waiting\' AND j.reservation = \'Scheduled\' AND j.queue_name = \'$queue\' ORDER BY j.job_id "); $sth->execute(); my @res = (); while (my $ref = $sth->fetchrow_hashref()) { push(@res, $ref); } $sth->finish(); return @res; } # Get if it exists waiting jobs in the specified queue or not # parameter : database ref, queuename # return 0 --> no # 1 --> yes sub is_waiting_job_specific_queue_present($$){ my $dbh = shift; my $queue = shift; my $sth = $dbh->prepare(" SELECT count(*) FROM jobs WHERE state=\'Waiting\' AND queue_name = \'$queue\' LIMIT 1 "); $sth->execute(); my ($res) = $sth->fetchrow_array(); $sth->finish(); return ($res > 0); } # get_jobs_to_schedule # args : base ref, queue name sub get_jobs_to_schedule($$$){ my $dbh = shift; my $queue = shift; my $limit = shift; my $sth = $dbh->prepare(" SELECT * FROM jobs WHERE state = \'Waiting\' AND reservation = \'None\' AND queue_name = \'$queue\' ORDER BY job_id LIMIT $limit "); $sth->execute(); my @res = (); while (my $ref = $sth->fetchrow_hashref()) { push(@res, $ref); } $sth->finish(); return(@res); } # get_fairsharing_jobs_to_schedule # args : base ref, queue name sub get_fairsharing_jobs_to_schedule($$$){ my $dbh = shift; my $queue = shift; my $limit = shift; my $req = "SELECT distinct(job_user) FROM jobs WHERE state = \'Waiting\' AND reservation = \'None\' AND queue_name = \'$queue\' "; my $sth = $dbh->prepare($req); $sth->execute(); my @users = (); while (my @ref = $sth->fetchrow_array()) { push(@users, $ref[0]); } $sth->finish(); my @res = (); foreach my $u (@users){ my $req2 = "SELECT * FROM jobs WHERE state = \'Waiting\' AND reservation = \'None\' AND queue_name = \'$queue\' AND job_user = \'$u\' ORDER BY job_id LIMIT $limit "; my $sth = $dbh->prepare($req2); $sth->execute(); while (my $ref = $sth->fetchrow_hashref()) { push(@res, $ref); } $sth->finish(); } return(@res); } # get_current_job_types # return a hash table with all types for the given job ID sub get_current_job_types($$){ my $dbh = shift; my $jobId = shift; my $sth = $dbh->prepare(" SELECT type FROM job_types WHERE types_index = \'CURRENT\' AND job_id = $jobId "); $sth->execute(); my %res; while (my $ref = $sth->fetchrow_hashref()) { if ($ref->{type} =~ m/^\s*(token)\s*\:\s*(\w+)\s*=\s*(\d+)\s*$/m){ $res{$1}->{$2} = $3; }elsif ($ref->{type} =~ m/^\s*(\w+)\s*=\s*(.+)$/m){ $res{$1} = $2; }else{ $res{$ref->{type}} = "true"; } } $sth->finish(); return(\%res); } # get_job_types # return the list of types for the given job ID sub get_job_types($$){ my $dbh = shift; my $jobId = shift; my $sth = $dbh->prepare(" SELECT type FROM job_types WHERE job_id = $jobId "); $sth->execute(); my @res; while (my $ref = $sth->fetchrow_hashref()) { push(@res,$ref->{type}); } $sth->finish(); return(@res); } # add_current_job_types sub add_current_job_types($$$){ my $dbh = shift; my $jobId = shift; my $type = shift; $dbh->do(" INSERT INTO job_types (job_id,type,types_index) VALUES ($jobId,\'$type\',\'CURRENT\') "); } # remove_current_job_types sub remove_current_job_types($$$){ my $dbh = shift; my $jobId = shift; my $type = shift; $dbh->do(" DELETE FROM job_types WHERE job_id = $jobId AND type = \'$type\' AND types_index = \'CURRENT\' "); } # get_current_job_dependencies # return an array table with all dependencies for the given job ID sub get_current_job_dependencies($$){ my $dbh = shift; my $jobId = shift; my $sth = $dbh->prepare(" SELECT job_id_required FROM job_dependencies WHERE job_dependency_index = \'CURRENT\' AND job_id = $jobId "); $sth->execute(); my @res; while (my $ref = $sth->fetchrow_hashref()) { push(@res, $ref->{job_id_required}); } $sth->finish(); return(@res); } # get_job_dependencies # return an array table with all dependencies for the given job ID sub get_job_dependencies($$){ my $dbh = shift; my $jobId = shift; my $sth = $dbh->prepare(" SELECT job_id_required FROM job_dependencies WHERE job_id = $jobId "); $sth->execute(); my @res; while (my $ref = $sth->fetchrow_hashref()) { push(@res, $ref->{job_id_required}); } $sth->finish(); return(@res); } # Get all waiting toSchedule reservation jobs in the specified queue # parameter : database ref, queuename # return an array of job informations sub get_waiting_toSchedule_reservation_jobs_specific_queue($$){ my $dbh = shift; my $queue = shift; my $sth = $dbh->prepare(" SELECT j.* FROM jobs j WHERE j.state=\'Waiting\' AND j.reservation = \'toSchedule\' AND j.queue_name = \'$queue\' ORDER BY j.job_id "); $sth->execute(); my @res = (); while (my $ref = $sth->fetchrow_hashref()) { push(@res, $ref); } $sth->finish(); return @res; } # set walltime for a moldable job sub set_moldable_job_max_time($$$){ my ($dbh, $mol, $walltime) = @_; $dbh->do(" UPDATE moldable_job_descriptions SET moldable_walltime = \'$walltime\' WHERE moldable_id = $mol "); } #ARRAY JOBS MANAGEMENT # get_jobs_in_array # returns the jobs within a same array # parameters : base, array_id # return value : flatened list of hashref jobs ids sub get_jobs_in_array($$) { my $dbh = shift; my $array_id = $dbh->quote(shift); my $sth = $dbh->prepare(" SELECT job_id FROM jobs WHERE array_id = $array_id "); $sth->execute(); my @res = (); while (my $ref = $sth->fetchrow_hashref()) { push(@res, $ref); } return(@res); } # get_job_array_id($$) # get array_id of a job with given job_id # parameters : base, job_id # return value : array_id of the job # side effects : / sub get_job_array_id($$){ my $dbh = shift; my $job_id = shift; my $sth; $sth = $dbh->prepare(" SELECT array_id FROM jobs WHERE job_id = $job_id "); $sth->execute(); my $ref = $sth->fetchrow_hashref(); my @tmp_array = values(%$ref); my $array_id = $tmp_array[0]; $sth->finish(); return($array_id); } # get_array_subjobs($$) # Get all the jobs of a given array_job # parameters : base, array_id # return value : array of jobs of a given array_job # side effects : / sub get_array_subjobs($$){ my $dbh = shift; my $array_id = shift; my $sth = $dbh->prepare(" SELECT * FROM jobs WHERE array_id = $array_id ORDER BY job_id "); $sth->execute(); my @res = (); while (my $ref = $sth->fetchrow_hashref()) { push(@res, $ref); } $sth->finish(); return(@res); } # get_array_job_ids($$) # Get all the job_ids of a given array_job # parameters : base, array_id # return value : array of jobids of a given array_job # side effects : / sub get_array_job_ids($$){ my $dbh = shift; my $array_id = shift; my $sth = $dbh->prepare(" SELECT job_id FROM jobs WHERE array_id = $array_id ORDER BY job_id "); $sth->execute(); my @res = (); while (my $ref = $sth->fetchrow_hashref()) { my @tmp_array = values(%$ref); push(@res, $tmp_array[0]); } $sth->finish(); return(@res); } # PROCESSJOBS MANAGEMENT (Host assignment to jobs) # get_resource_job # returns the list of jobs associated to the resource passed in parameter # parameters : base, resource # return value : list of jobid # side effects : / sub get_resource_job($$) { my $dbh = shift; my $resource = shift; my $sth = $dbh->prepare(" SELECT jobs.job_id FROM assigned_resources, moldable_job_descriptions, jobs WHERE assigned_resources.assigned_resource_index = \'CURRENT\' AND moldable_job_descriptions.moldable_index = \'CURRENT\' AND assigned_resources.resource_id = $resource AND assigned_resources.moldable_job_id = moldable_job_descriptions.moldable_id AND moldable_job_descriptions.moldable_job_id = jobs.job_id AND (jobs.state = \'Waiting\' OR jobs.state = \'Hold\' OR jobs.state = \'toLaunch\' OR jobs.state = \'toAckReservation\' OR jobs.state = \'Launching\' OR jobs.state = \'Running\' OR jobs.state = \'Suspended\' OR jobs.state = \'Resuming\' OR jobs.state = \'Finishing\') "); $sth->execute(); my @res = (); while (my $ref = $sth->fetchrow_hashref()) { push(@res, $ref->{'job_id'}); } return @res; } # get_resource_job_with_state # returns the list of jobs associated to the resource passed in parameter # parameters : base, resource # return value : list of jobid # side effects : / sub get_resource_job_with_state($$$) { my $dbh = shift; my $resource = shift; my $state = shift; my $sth = $dbh->prepare(" SELECT jobs.job_id FROM assigned_resources, moldable_job_descriptions, jobs WHERE assigned_resources.assigned_resource_index = \'CURRENT\' AND moldable_job_descriptions.moldable_index = \'CURRENT\' AND assigned_resources.resource_id = $resource AND assigned_resources.moldable_job_id = moldable_job_descriptions.moldable_id AND moldable_job_descriptions.moldable_job_id = jobs.job_id AND jobs.state = \'$state\' "); $sth->execute(); my @res = (); while (my $ref = $sth->fetchrow_hashref()) { push(@res, $ref->{'job_id'}); } return @res; } # get_resource_job_to_frag # same as get_resource_job but excepts the cosystem jobs # parameters : base, resource # return value : list of jobid # side effects : / sub get_resource_job_to_frag($$) { my $dbh = shift; my $resource = shift; my $sth = $dbh->prepare(" SELECT jobs.job_id FROM assigned_resources, moldable_job_descriptions, jobs WHERE assigned_resources.assigned_resource_index = \'CURRENT\' AND moldable_job_descriptions.moldable_index = \'CURRENT\' AND assigned_resources.resource_id = $resource AND assigned_resources.moldable_job_id = moldable_job_descriptions.moldable_id AND moldable_job_descriptions.moldable_job_id = jobs.job_id AND jobs.state != \'Terminated\' AND jobs.state != \'Error\' AND jobs.job_id NOT IN ( SELECT job_id from job_types WHERE type=\'cosystem\' AND types_index=\'CURRENT\' ) "); $sth->execute(); my @res = (); while (my $ref = $sth->fetchrow_hashref()) { push(@res, $ref->{'job_id'}); } return @res; } # get_node_job # returns the list of jobs associated to the hostname passed in parameter # parameters : base, hostname # return value : list of jobid # side effects : / sub get_node_job($$) { my $dbh = shift; my $hostname = shift; my $sth = $dbh->prepare(" SELECT jobs.job_id FROM assigned_resources, moldable_job_descriptions, jobs, resources WHERE assigned_resources.assigned_resource_index = \'CURRENT\' AND moldable_job_descriptions.moldable_index = \'CURRENT\' AND resources.network_address = \'$hostname\' AND assigned_resources.resource_id = resources.resource_id AND assigned_resources.moldable_job_id = moldable_job_descriptions.moldable_id AND moldable_job_descriptions.moldable_job_id = jobs.job_id AND jobs.state != \'Terminated\' AND jobs.state != \'Error\' "); $sth->execute(); my @res = (); while (my $ref = $sth->fetchrow_hashref()) { push(@res, $ref->{'job_id'}); } return @res; } # get_alive_nodes_with_jobs # returns the list of occupied nodes # parameters : base # return value : list of node names # side effects : / sub get_alive_nodes_with_jobs($) { my $dbh = shift; my $sth = $dbh->prepare(" SELECT resources.network_address FROM assigned_resources, moldable_job_descriptions, jobs, resources WHERE assigned_resources.assigned_resource_index = \'CURRENT\' AND moldable_job_descriptions.moldable_index = \'CURRENT\' AND (resources.state = 'Alive' or resources.next_state='Alive') AND assigned_resources.resource_id = resources.resource_id AND assigned_resources.moldable_job_id = moldable_job_descriptions.moldable_id AND moldable_job_descriptions.moldable_job_id = jobs.job_id AND jobs.state != \'Terminated\' AND jobs.state != \'Error\' "); $sth->execute(); my @res = (); while (my @ary = $sth->fetchrow_array) { push(@res, $ary[0]); } return @res; $sth->finish(); } # get_node_job_to_frag # same as get_node_job but excepts cosystem jobs # parameters : base, hostname # return value : list of jobid # side effects : / sub get_node_job_to_frag($$) { my $dbh = shift; my $hostname = shift; my $sth = $dbh->prepare(" SELECT jobs.job_id FROM assigned_resources, moldable_job_descriptions, jobs, resources WHERE assigned_resources.assigned_resource_index = \'CURRENT\' AND moldable_job_descriptions.moldable_index = \'CURRENT\' AND resources.network_address = \'$hostname\' AND assigned_resources.resource_id = resources.resource_id AND assigned_resources.moldable_job_id = moldable_job_descriptions.moldable_id AND moldable_job_descriptions.moldable_job_id = jobs.job_id AND jobs.state != \'Terminated\' AND jobs.state != \'Error\' AND jobs.job_id NOT IN ( SELECT job_id from job_types WHERE type=\'cosystem\' AND types_index=\'CURRENT\' ) "); $sth->execute(); my @res = (); while (my $ref = $sth->fetchrow_hashref()) { push(@res, $ref->{'job_id'}); } return @res; } # get_resources_in_state # returns the list of resources in the state specified # parameters : base, state # return value : list of resource ref sub get_resources_in_state($$) { my $dbh = shift; my $state = shift; my $sth = $dbh->prepare(" SELECT * FROM resources WHERE state = \'$state\' "); $sth->execute(); my @res = (); while (my $ref = $sth->fetchrow_hashref()) { push(@res, $ref); } return @res; } # get_finaud_nodes # returns the list of nodes for finaud # parameters : base # return value : list of nodes sub get_finaud_nodes($) { my $dbh = shift; my $sth = ""; if ($Db_type eq "Pg"){ $sth = $dbh->prepare(" SELECT DISTINCT(network_address), * FROM resources WHERE (state = \'Alive\' OR (state = \'Suspected\' AND finaud_decision = \'YES\')) AND type = \'default\' AND desktop_computing = \'NO\' "); } else{ my @result; my $presth = $dbh->prepare("DESC resources"); $presth->execute(); while (my $ref = $presth->fetchrow_hashref()){ my $current_value = $ref->{'Field'}; push(@result, $current_value); } $presth->finish(); my $str = "SELECT DISTINCT(network_address)"; foreach(@result){ $str = $str.", ".$_; } $str = $str." FROM resources WHERE (state = \'Alive\' OR (state = \'Suspected\' AND finaud_decision = \'YES\')) AND type = \'default\' AND desktop_computing = \'NO\'"; $sth = $dbh->prepare($str); } $sth->execute(); my @res = (); while (my $ref = $sth->fetchrow_hashref()) { push(@res, $ref); } return @res; } # get_resources_that_can_be_waked_up # returns a list of resources # parameters : base, date max # return value : list of resource ref sub get_resources_that_can_be_waked_up($$) { my $dbh = shift; my $max_date = shift; $max_date = $max_date + $Cm_security_duration; my $sth = $dbh->prepare(" SELECT * FROM resources WHERE state = \'Absent\' AND resources.available_upto > $max_date "); $sth->execute(); my @res = (); while (my $ref = $sth->fetchrow_hashref()) { push(@res, $ref); } return(@res); } # get_nodes_that_can_be_waked_up # returns a list of resources # parameters : base, date max # return value : list of node names sub get_nodes_that_can_be_waked_up($$) { my $dbh = shift; my $max_date = shift; $max_date = $max_date + $Cm_security_duration; my $sth = $dbh->prepare(" SELECT distinct(network_address) FROM resources WHERE state = \'Absent\' AND resources.available_upto > $max_date "); $sth->execute(); my @res = (); while (my @ary = $sth->fetchrow_array) { push(@res, $ary[0]); } return @res; } # get_resources_that_will_be_out # returns a list of resources # parameters : base, job max date # return value : list of resource ref sub get_resources_that_will_be_out($$) { my $dbh = shift; my $max_date = shift; $max_date = $max_date + $Cm_security_duration; my $sth = $dbh->prepare(" SELECT * FROM resources WHERE state = \'Alive\' AND resources.available_upto < $max_date "); $sth->execute(); my @res = (); while (my $ref = $sth->fetchrow_hashref()) { push(@res, $ref); } return(@res); } # get_energy_saving_resources_availability # returns a list of resources and when they will be available # parameters : base, min_start_date # return value : end_date_availability => [resource id list] sub get_energy_saving_resources_availability($$) { my $dbh = shift; my $current_time = shift; my $sth = $dbh->prepare(" SELECT resource_id, available_upto FROM resources WHERE (state = \'Absent\' AND available_upto > $current_time) OR (state = \'Alive\' AND available_upto < 2147483646) "); $sth->execute(); my %res = (); while (my @ref = $sth->fetchrow_array()) { push(@{$res{$ref[1]}}, $ref[0]); } return(\%res); } # add_resource_job_pair # adds a new pair (jobid, resource) to the table assigned_resources # parameters : base, jobid, resource id # return value : / #TODO: consider the use of multiple values insertion within one request : INSERT INTO tbl_name (a,b,c) VALUES(1,2,3),(4,5,6),(7,8,9); sub add_resource_job_pair($$$) { my $dbh = shift; my $moldable = shift; my $resource = shift; $dbh->do("INSERT INTO assigned_resources (moldable_job_id,resource_id,assigned_resource_index) VALUES ($moldable,$resource,\'CURRENT\')"); } # get all jobs in a range of date # args : base, start range, end range sub get_jobs_range_dates($$$){ my $dbh = shift; my $date_start = shift; my $date_end = shift; my $req = "SELECT jobs.job_id,jobs.job_type,jobs.state,jobs.job_user,jobs.command,jobs.queue_name,moldable_job_descriptions.moldable_walltime,jobs.properties,jobs.launching_directory,jobs.submission_time,jobs.start_time,jobs.stop_time,assigned_resources.resource_id,resources.network_address,(jobs.start_time + moldable_job_descriptions.moldable_walltime) FROM jobs, assigned_resources, moldable_job_descriptions, resources WHERE ( jobs.stop_time >= $date_start OR ( jobs.stop_time = \'0\' AND (jobs.state = \'Running\' OR jobs.state = \'Suspended\' OR jobs.state = \'Resuming\') ) ) AND jobs.start_time < $date_end AND jobs.assigned_moldable_job = assigned_resources.moldable_job_id AND moldable_job_descriptions.moldable_job_id = jobs.job_id AND resources.resource_id = assigned_resources.resource_id ORDER BY jobs.job_id"; my $sth = $dbh->prepare($req); $sth->execute(); my %results; while (my @ref = $sth->fetchrow_array()) { if (!defined($results{$ref[0]})){ $results{$ref[0]} = { 'job_type' => $ref[1], 'state' => $ref[2], 'user' => $ref[3], 'command' => $ref[4], 'queue_name' => $ref[5], 'walltime' => $ref[6], 'properties' => $ref[7], 'launching_directory' => $ref[8], 'submission_time' => $ref[9], 'start_time' => $ref[10], 'stop_time' => $ref[11], 'resources' => [ $ref[12] ], 'limit_stop_time' => $ref[14] } }else{ push(@{$results{$ref[0]}->{resources}}, $ref[12]); } } $sth->finish(); return %results; } # get all jobs in a range of date in the gantt # args : base, start range, end range sub get_jobs_gantt_scheduled($$$) { my $dbh = shift; my $date_start = shift; my $date_end = shift; my $req = "SELECT jobs.job_id,jobs.job_type,jobs.state,jobs.job_user,jobs.command,jobs.queue_name,moldable_job_descriptions.moldable_walltime,jobs.properties,jobs.launching_directory,jobs.submission_time,gantt_jobs_predictions_visu.start_time,(gantt_jobs_predictions_visu.start_time + moldable_job_descriptions.moldable_walltime),gantt_jobs_resources_visu.resource_id, resources.network_address FROM jobs, moldable_job_descriptions, gantt_jobs_resources_visu, gantt_jobs_predictions_visu, resources WHERE gantt_jobs_predictions_visu.moldable_job_id = gantt_jobs_resources_visu.moldable_job_id AND gantt_jobs_predictions_visu.moldable_job_id = moldable_job_descriptions.moldable_id AND jobs.job_id = moldable_job_descriptions.moldable_job_id AND gantt_jobs_predictions_visu.start_time < $date_end AND resources.resource_id = gantt_jobs_resources_visu.resource_id AND gantt_jobs_predictions_visu.start_time + moldable_job_descriptions.moldable_walltime >= $date_start ORDER BY jobs.job_id"; my $sth = $dbh->prepare($req); $sth->execute(); my %results; while (my @ref = $sth->fetchrow_array()) { if (!defined($results{$ref[0]})){ $results{$ref[0]} = { 'job_type' => $ref[1], 'state' => $ref[2], 'user' => $ref[3], 'command' => $ref[4], 'queue_name' => $ref[5], 'walltime' => $ref[6], 'properties' => $ref[7], 'launching_directory' => $ref[8], 'submission_time' => $ref[9], 'start_time' => $ref[10], 'stop_time' => $ref[11], 'resources' => [ $ref[12] ], } }else{ push(@{$results{$ref[0]}->{resources}}, $ref[12]); } } $sth->finish(); return %results; } # get all distinct jobs for a user query # args : base, start range, end range, jobs states, limit, offset, user sub get_jobs_for_user_query { my $dbh = shift; my $date_start = shift || ""; my $date_end = shift || ""; my $state = shift || ""; my $limit = shift || ""; my $offset = shift; my $user = shift || ""; my $array_id = shift || ""; my $first_query_date_start = ""; my $second_query_date_start = ""; my $third_query_date_start = ""; my $first_query_date_end = ""; my $second_query_date_end = ""; my $third_query_date_end = ""; if ($date_start ne "") { $first_query_date_start = "( jobs.stop_time >= $date_start OR ( jobs.stop_time = \'0\' AND ( (jobs.state = \'Running\' AND jobs.start_time + moldable_job_descriptions.moldable_walltime >= $date_start ) OR jobs.state = \'Suspended\' OR jobs.state = \'Resuming\') ) ) AND"; $second_query_date_start = " AND gantt_jobs_predictions_visu.start_time + moldable_job_descriptions.moldable_walltime >= $date_start "; $third_query_date_start = " AND $date_start <= jobs.submission_time"; } if ($date_end ne "") { $first_query_date_end = "jobs.start_time < $date_end AND"; $second_query_date_end = " AND gantt_jobs_predictions_visu.start_time < $date_end "; $third_query_date_end = " AND jobs.submission_time <= $date_end"; } if ($state ne "") { $state = " AND jobs.state IN (".$state.") ";} if ($limit ne "") { $limit = "LIMIT $limit"; } if (defined($offset)) { $offset = "OFFSET $offset"; } if ($user ne "") { $user = " AND jobs.job_user = ".$dbh->quote($user); } if ($array_id ne "") { $array_id = " AND jobs.array_id = ".$dbh->quote($array_id); } my $req = " SELECT jobs.job_id,jobs.job_name,jobs.state,jobs.job_user,jobs.queue_name,jobs.submission_time, jobs.assigned_moldable_job,jobs.reservation,jobs.project,jobs.properties,jobs.exit_code,jobs.command,jobs.initial_request,jobs.launching_directory,jobs.message,jobs.job_type,jobs.array_id,jobs.stderr_file,jobs.stdout_file,jobs.start_time,moldable_job_descriptions.moldable_walltime,jobs.stop_time FROM jobs LEFT JOIN moldable_job_descriptions ON jobs.assigned_moldable_job = moldable_job_descriptions.moldable_id WHERE jobs.job_id IN ( SELECT DISTINCT jobs.job_id FROM jobs, assigned_resources, moldable_job_descriptions WHERE $first_query_date_start $first_query_date_end jobs.assigned_moldable_job = assigned_resources.moldable_job_id AND moldable_job_descriptions.moldable_job_id = jobs.job_id $state $user $array_id UNION SELECT DISTINCT jobs.job_id FROM jobs, moldable_job_descriptions, gantt_jobs_resources_visu, gantt_jobs_predictions_visu WHERE gantt_jobs_predictions_visu.moldable_job_id = gantt_jobs_resources_visu.moldable_job_id AND gantt_jobs_predictions_visu.moldable_job_id = moldable_job_descriptions.moldable_id AND jobs.job_id = moldable_job_descriptions.moldable_job_id $second_query_date_start $second_query_date_end $state $user $array_id UNION SELECT DISTINCT jobs.job_id FROM jobs WHERE jobs.start_time = \'0\' $third_query_date_start $third_query_date_end $state $user $array_id ) ORDER BY jobs.job_id $limit $offset"; my $sth = $dbh->prepare($req); $sth->execute(); my %results; while (my @ref = $sth->fetchrow_array()) { $results{$ref[0]} = { 'job_name' => $ref[1], 'state' => $ref[2], 'job_user' => $ref[3], 'queue_name' => $ref[4], 'submission_time' => $ref[5], 'assigned_moldable_job' => $ref[6], 'reservation' => $ref[7], 'project' => $ref[8], 'properties' => $ref[9], 'exit_code' => $ref[10], 'command' => $ref[11], 'initial_request' => $ref[12], 'launching_directory' => $ref[13], 'message' => $ref[14], 'job_type' => $ref[15], 'array_id' => $ref[16], 'stdout_file' => $ref[17], 'stderr_file' => $ref[18], 'start_time' => $ref[19], 'walltime' => $ref[20], 'stop_time' => $ref[21] }; } $sth->finish(); return %results; } # count all distinct jobs for a user query # args : base, start range, end range, jobs states, limit, offset, user sub count_jobs_for_user_query { my $dbh = shift; my $date_start = shift || ""; my $date_end = shift || ""; my $state = shift || ""; my $limit = shift || ""; my $offset = shift; my $user = shift || ""; my $array_id = shift || ""; my $first_query_date_start = ""; my $second_query_date_start = ""; my $third_query_date_start = ""; my $first_query_date_end = ""; my $second_query_date_end = ""; my $third_query_date_end = ""; if ($date_start ne "") { $first_query_date_start = "( jobs.stop_time >= $date_start OR ( jobs.stop_time = \'0\' AND (jobs.state = \'Running\' OR jobs.state = \'Suspended\' OR jobs.state = \'Resuming\') ) ) AND"; $second_query_date_start = " AND gantt_jobs_predictions_visu.start_time + moldable_job_descriptions.moldable_walltime >= $date_start "; $third_query_date_start = " AND $date_start <= jobs.submission_time"; } if ($date_end ne "") { $first_query_date_end = "jobs.start_time < $date_end AND"; $second_query_date_end = " AND gantt_jobs_predictions_visu.start_time < $date_end "; $third_query_date_end = " AND jobs.submission_time <= $date_end"; } if ($state ne "") { $state = " AND jobs.state IN (".$state.") ";} if ($limit ne "") { $limit = "LIMIT $limit"; } if (defined($offset)) { $offset = "OFFSET $offset"; } if ($user ne "") { $user = " AND jobs.job_user = ".$dbh->quote($user); } if ("$array_id" ne "") { $array_id = " AND jobs.array_id = ".$dbh->quote($array_id); } my $req = " SELECT COUNT(jobs.job_id) FROM jobs WHERE jobs.job_id IN ( SELECT DISTINCT jobs.job_id FROM jobs, assigned_resources, moldable_job_descriptions WHERE $first_query_date_start $first_query_date_end jobs.assigned_moldable_job = assigned_resources.moldable_job_id AND moldable_job_descriptions.moldable_job_id = jobs.job_id $state $user $array_id UNION SELECT DISTINCT jobs.job_id FROM jobs, moldable_job_descriptions, gantt_jobs_resources_visu, gantt_jobs_predictions_visu WHERE gantt_jobs_predictions_visu.moldable_job_id = gantt_jobs_resources_visu.moldable_job_id AND gantt_jobs_predictions_visu.moldable_job_id = moldable_job_descriptions.moldable_id AND jobs.job_id = moldable_job_descriptions.moldable_job_id $second_query_date_start $second_query_date_end $state $user $array_id UNION SELECT DISTINCT jobs.job_id FROM jobs WHERE jobs.start_time = \'0\' $third_query_date_start $third_query_date_end $state $user $array_id )"; my $sth = $dbh->prepare($req); $sth->execute(); my ($count) = $sth->fetchrow_array(); return $count ; } # get scheduling informations about Interactive jobs in Waiting state # args : base sub get_gantt_waiting_interactive_prediction_date($){ my $dbh = shift; my $req = "SELECT jobs.job_id, jobs.info_type, gantt_jobs_predictions_visu.start_time, jobs.message FROM jobs, moldable_job_descriptions, gantt_jobs_predictions_visu WHERE jobs.state = \'Waiting\' AND jobs.reservation = \'None\' AND moldable_job_descriptions.moldable_index = \'CURRENT\' AND moldable_job_descriptions.moldable_job_id = jobs.job_id AND gantt_jobs_predictions_visu.moldable_job_id = moldable_job_descriptions.moldable_id "; my $sth = $dbh->prepare($req); $sth->execute(); my @results; while (my @ref = $sth->fetchrow_array()) { my $tmp = { 'job_id' => $ref[0], 'info_type' => $ref[1], 'start_time' => $ref[2], 'message' => $ref[3], }; push(@results, $tmp); } $sth->finish(); return(@results); } # get_desktop_computing_host_jobs($$); # get the list of jobs and attributs affected to a desktop computing node # parameters: base, nodename # return value: jobs hash # side effects: none sub get_desktop_computing_host_jobs($$) { my $dbh = shift; my $hostname = shift; my $sth = $dbh->prepare(" SELECT jobs.job_id, jobs.state, jobs.command, jobs.launching_directory, jobs.stdout_file, jobs.stderr_file FROM jobs, assigned_resources, resources WHERE resources.network_address = \'$hostname\' AND resources.desktop_computing = \'YES\' AND resources.resource_id = assigned_resources.resource_id AND jobs.assigned_moldable_job = assigned_resources.moldable_job_id AND assigned_resources.assigned_resource_index = \'CURRENT\' "); $sth->execute; my $results; while (my @array = $sth->fetchrow_array()) { $results->{$array[0]} = { state => $array[1], command => $array[2], directory => $array[3], stdout_file => OAR::Tools::replace_jobid_tag_in_string($array[4],$array[0]), stderr_file => OAR::Tools::replace_jobid_tag_in_string($array[5],$array[0]) }; } return($results); } # get_stagein_id($$); # retrieve stagein idFile form its md5sum # parameters: base, md5sum # return value: idFile or undef if md5sum is not found # side effects: none sub get_stagein_id($$) { my $dbh = shift; my $md5sum = shift; my $sth = $dbh->prepare(" SELECT file_id FROM files WHERE md5sum = \'$md5sum\' "); $sth->execute(); my $ref = $sth->fetchrow_hashref(); $sth->finish(); return($ref->{file_id}); } # set_stagein($$$$$$); # set a stagein information # parameters: base, md5sum, location, method, compression, size # return value: the idFile of the new stagein. # side effects: none sub set_stagein($$$$$$) { my $dbh = shift; my $md5sum = shift; my $location = shift; my $method = shift; my $compression = shift; my $size = shift; my $idFile; lock_table($dbh, ["files"]); $dbh->do("INSERT INTO files (md5sum,location,method,compression,size) VALUES (\'$md5sum\',\'$location\',\'$method\',\'$compression\',$size)"); $idFile = get_last_insert_id($dbh,"files_file_id_seq"); unlock_table($dbh); return $idFile; } # del_stagein($$); # remove a stagein from database # parameters: base, md5sum # return value: none # side effects: none sub del_stagein($$) { my $dbh = shift; my $md5sum = shift; lock_table($dbh, ["files"]); $dbh->do("DELETE FROM files WHERE md5sum = \'$md5sum\'"); unlock_table($dbh); } # is_stagein_deprecated($$$); # check if a stagein file is to old to be kept in cache # parameters: base, md5sum, expiry # return value: boolean, true if deprecated # side effects: none sub is_stagein_deprecated($$$) { my $dbh = shift; my $md5sum = shift; my $expiry_delay = shift; my $date = get_date($dbh); my $sth = $dbh->prepare(" SELECT jobs.start_time FROM jobs, files WHERE jobs.file_id = files.file_id AND files.md5sum = \'$md5sum\' "); $sth->execute(); my $result = 1; while (my @res = $sth->fetchrow_array()){ if (($res[0] + $expiry_delay) > $date){ $result = 0; return(0); } } return ($result); } # get_job_stagein($$); # get a job stagein information: pathname and md5sum # parameters: base, jobid # return value: a hash with 2 keys: pathname and md5sum # side effects: none sub get_job_stagein($$) { my $dbh = shift; my $jobid = shift; my $sth = $dbh->prepare(" SELECT files.md5sum,files.location,files.method,files.compression,files.size FROM jobs, files WHERE jobs.job_id = $jobid AND jobs.file_id = files.file_id "); $sth->execute(); my $ref = $sth->fetchrow_hashref(); $sth->finish(); return($ref); } # ADMISSION RULES MANAGEMENT # add_admission_rule # adds a new rule in the table admission_rule # parameters : base, rule # return value : new admission rule id sub add_admission_rule($$) { my $dbh = shift; my $rule = $dbh->quote(shift); $dbh->do(" INSERT INTO admission_rules (rule) VALUES ($rule) "); my $id = get_last_insert_id($dbh,"admission_rules_id_seq"); return($id); } # list_admission_rules # get the list of all admission rules # parameters : base # return value : list of admission rules # side effects : / sub list_admission_rules($) { my $dbh = shift; my $sth = $dbh->prepare(" SELECT * FROM admission_rules "); $sth->execute(); my @res = (); while (my $ref = $sth->fetchrow_hashref()) { push(@res, $ref); } $sth->finish(); return @res; } # get_requested_admission_rules # get requested admission rules # parameters : base limit offset # side effects : / sub get_requested_admission_rules($$$) { my $dbh = shift; my $limit = shift; my $offset= shift; my $sth = $dbh->prepare(" SELECT * FROM admission_rules ORDER BY id LIMIT $limit OFFSET $offset "); $sth->execute(); my @res = (); while (my $ref = $sth->fetchrow_hashref()) { push(@res, $ref); } $sth->finish(); return(@res); } # count_all_admission_rules # count all admissions rules # parameters : base # side effects : / sub count_all_admission_rules($) { my $dbh = shift; my $sth = $dbh->prepare(" SELECT COUNT(*) FROM admission_rules "); $sth->execute(); my ($count) = $sth->fetchrow_array(); return $count ; } # get_admission_rule # returns a ref to some hash containing data for the admission rule of id passed in # parameter # parameters : base, admission_rule_id # return value : ref # side effects : / sub get_admission_rule($$) { my $dbh = shift; my $rule_id = shift; my $sth = $dbh->prepare(" SELECT * FROM admission_rules WHERE id = $rule_id "); $sth->execute(); my $ref = $sth->fetchrow_hashref(); $sth->finish(); return($ref); } # delete_admission_rule # parameter # parameters : base, admission_rule_id sub delete_admission_rule($$) { my $dbh = shift; my $rule_id = shift; my $sth = $dbh->prepare(" DELETE FROM admission_rules WHERE id = $rule_id "); $sth->execute(); } # NODES MANAGEMENT # add_resource # adds a new resource in the table resources and resource_properties # parameters : base, name, state # return value : new resource id sub add_resource($$$) { my $dbh = shift; my $name = shift; my $state = shift; #lock_table($dbh,["resources"]); $dbh->do(" INSERT INTO resources (network_address,state,state_num) VALUES (\'$name\',\'$state\',$State_to_num{$state}) "); my $id = get_last_insert_id($dbh,"resources_resource_id_seq"); #unlock_table($dbh); my $date = get_date($dbh); $dbh->do(" INSERT INTO resource_logs (resource_id,attribute,value,date_start) VALUES ($id,\'state\',\'$state\',\'$date\') "); return $id; } # get_last_resource_id # get the last resource id sub get_last_resource_id($) { my $dbh = shift; my $sth = $dbh->prepare("SELECT MAX(resource_id) from resources"); $sth->execute(); my @arr = $sth->fetchrow_array(); $sth->finish(); return $arr[0]; } # add_resources # adds an array of resources in the table resources in block # parameters : base, name, resources # return value : array of newly created resources ids sub add_resources($$) { my $dbh = shift; my $resources = shift; lock_table($dbh,["resources","resource_logs"]); # Getting the last id as we are not using auto_increment my $id=get_last_resource_id($dbh); my @ids; # Construct the properties list my @properties; foreach my $r (@$resources) { foreach my $prop (keys(%$r)) { if(!grep(/^$prop$/,@properties)) { push(@properties,$prop); } } } # Construct the queries my $query="INSERT INTO resources (resource_id,state,state_num,".join(",",@properties).") VALUES "; my $log_query="INSERT INTO resource_logs (resource_id,attribute,value,date_start) VALUES "; my $date = get_date($dbh); my $first=1; my @values; my @log_values; foreach my $r (@$resources) { if ($first) { $query.="("; } else { $query.=",(";} @values=(); $id++; push(@values,$id); push(@log_values,"($id,\'state\',\'Alive\',\'$date\')"); push(@values,"\'Alive\'"); push(@values,$State_to_num{"Alive"}); push(@ids,$id); foreach my $p (@properties) { if (defined($r->{$p})) { push(@values,"\'".$r->{$p}."\'"); push(@log_values,"($id,\'$p\',\'$r->{$p}\',\'$date\')"); } else { push(@values,"NULL"); } } $query.=join(",",@values); $query.=")"; $first=0; } $log_query.=join(",",@log_values); # Execute the query $dbh->do($query); if ($DBI::err) { @ids=["Error: ". $DBI::errstr ."Query: " .$query]; }else{ $dbh->do($log_query); } unlock_table($dbh); return(@ids); } # get_alive_resources # gets the list of resources in the Alive state. # parameters : base # return value : list of resource refs # side effects : / sub get_alive_resources($) { my $dbh = shift; my $sth = $dbh->prepare(" SELECT * FROM resources WHERE state = \'Alive\' OR state = \'Suspected\' OR state = \'Absent\' "); $sth->execute(); my @res = (); while (my $ref = $sth->fetchrow_hashref()) { push(@res, $ref); } $sth->finish(); return @res; } # list_resources # gets the list of all resources # parameters : base # return value : list of resources # side effects : / sub list_resources($) { my $dbh = shift; my $sth = $dbh->prepare(" SELECT * FROM resources "); $sth->execute(); my @res = (); while (my $ref = $sth->fetchrow_hashref()) { push(@res, $ref); } $sth->finish(); return(@res); } # count_all_resources # count all resources # parameters : base # side effects : / sub count_all_resources($) { my $dbh = shift; my $sth = $dbh->prepare(" SELECT COUNT(*) FROM resources "); $sth->execute(); my ($count) = $sth->fetchrow_array(); return $count ; } # get_requested_resources # get requested resources # parameters : base limit offset # side effects : / sub get_requested_resources($$$) { my $dbh = shift; my $limit = shift; my $offset= shift; my $sth = $dbh->prepare(" SELECT * FROM resources ORDER BY resource_id LIMIT $limit OFFSET $offset "); $sth->execute(); my @res = (); while (my $ref = $sth->fetchrow_hashref()) { push(@res, $ref); } $sth->finish(); return(@res); } # list_nodes # gets the list of all nodes. # parameters : base # return value : list of hostnames # side effects : / sub list_nodes($) { my $dbh = shift; #my $sth = $dbh->prepare(" SELECT distinct(network_address), resource_id # FROM resources # ORDER BY resource_id ASC"); my $sth = $dbh->prepare(" SELECT distinct(network_address) FROM resources ORDER BY network_address ASC "); $sth->execute(); my @res = (); while (my $ref = $sth->fetchrow_hashref()) { push(@res, $ref->{'network_address'}); } $sth->finish(); return(@res); } # get_resource_info # returns a ref to some hash containing data for the nodes of the resource passed in parameter # parameters : base, resource id # return value : ref # side effects : / sub get_resource_info($$) { my $dbh = shift; my $resource = shift; my $sth = $dbh->prepare(" SELECT * FROM resources WHERE resource_id = $resource "); $sth->execute(); my $ref = $sth->fetchrow_hashref(); $sth->finish(); return $ref; } # get_node_info # returns a ref to some hash containing data for the node passed in parameter # parameters : base, hostname # return value : ref sub get_node_info($$) { my $dbh = shift; my $hostname = shift; my $sth = $dbh->prepare(" SELECT * FROM resources WHERE network_address = \'$hostname\' ORDER BY resource_id ASC "); $sth->execute(); my @res = (); while (my $ref = $sth->fetchrow_hashref()) { push(@res, $ref); } $sth->finish(); return(@res); } # is_node_exists # returns 1 if the given hostname exists in the database otherwise 0. # parameters : base, hostname # return value : ref # side effects : / sub is_node_exists($$) { my $dbh = shift; my $hostname = shift; my $sth = $dbh->prepare(" SELECT * FROM resources WHERE network_address = \'$hostname\' "); $sth->execute(); my $ref = $sth->fetchrow_hashref(); $sth->finish(); if (defined($ref)){ return(1); }else{ return(0); } } # get_current_assigned_nodes # returns the current nodes # parameters : base sub get_current_assigned_nodes($) { my $dbh = shift; my $sth = $dbh->prepare(" SELECT DISTINCT(resources.network_address) FROM assigned_resources, resources WHERE assigned_resources.assigned_resource_index = \'CURRENT\' AND resources.resource_id = assigned_resources.resource_id "); $sth->execute(); my %result; while (my @ref = $sth->fetchrow_array()){ $result{$ref[0]} = 1; } $sth->finish(); return(\%result); } # get_current_assigned_resources # returns the current resources # parameters : base sub get_current_assigned_resources($) { my $dbh = shift; my $sth = $dbh->prepare(" SELECT resource_id FROM assigned_resources WHERE assigned_resource_index = \'CURRENT\' "); $sth->execute(); my @result; while (my $ref = $sth->fetchrow_hashref()){ push(@result, $ref->{resource_id}); } $sth->finish(); return(@result); } # get_current_assigned_job_resources # returns the current resources ref for a job # parameters : base, moldable id sub get_current_assigned_job_resources($$){ my $dbh = shift; my $mold_id = shift; my $sth = $dbh->prepare(" SELECT resources.* FROM assigned_resources, resources WHERE assigned_resource_index = \'CURRENT\' AND assigned_resources.moldable_job_id = $mold_id AND resources.resource_id = assigned_resources.resource_id "); $sth->execute(); my @result; while (my $ref = $sth->fetchrow_hashref()){ push(@result, $ref); } $sth->finish(); return(@result); } # get_specific_resource_states # returns a hashtable with each given resources and their states # parameters : base, resource type sub get_specific_resource_states($$){ my $dbh = shift; my $type = shift; my %result; my $sth = $dbh->prepare(" SELECT resource_id, state FROM resources WHERE type = \'$type\' "); $sth->execute(); while (my @ref = $sth->fetchrow_array()){ push(@{$result{$ref[1]}}, $ref[0]); } $sth->finish(); return(\%result); } # get_resource_state # returns the state for the resource # parameters : base, resource_id # return value : string # side effects : / # sub get_resource_state($$){ # my $dbh = shift; # my $resource_id = shift; # my $result; # my $sth = $dbh->prepare(" SELECT state # FROM resources # WHERE # resource_id = $resource_id # ") # $sth->execute(); # while (my @ref = $sth->fetchrow_array()){ # $result = $ref[0]; # } # $sth->finish(); # return($result); # } # get_current_free_resources_of_node # return an array of free resources for the specified network_address sub get_current_free_resources_of_node($$){ my $dbh = shift; my $host = shift; my $sth = $dbh->prepare(" SELECT resource_id FROM resources WHERE network_address = \'$host\' AND resource_id NOT IN ( SELECT resource_id from assigned_resources where assigned_resource_index = \'CURRENT\' ) "); $sth->execute(); my @result; while (my $ref = $sth->fetchrow_hashref()){ push(@result, $ref->{resource_id}); } $sth->finish(); return @result; } # get_resources_on_node # returns the current resources on node whose hostname is passed in parameter # parameters : base, hostname # return value : weight # side effects : / sub get_resources_on_node($$) { my $dbh = shift; my $hostname = shift; my $sth = $dbh->prepare(" SELECT resources.resource_id as resource FROM assigned_resources, resources WHERE assigned_resources.assigned_resource_index = \'CURRENT\' AND resources.network_address = \'$hostname\' AND resources.resource_id = assigned_resources.resource_id "); $sth->execute(); my @result; while (my $ref = $sth->fetchrow_hashref()){ push(@result, $ref->{resource}); } $sth->finish(); return @result; } # get_all_resources_on_node # returns the current resources on node whose hostname is passed in parameter # parameters : base, hostname # return value : resources ids # side effects : / sub get_all_resources_on_node($$) { my $dbh = shift; my $hostname = shift; my $sth = $dbh->prepare(" SELECT resource_id FROM resources WHERE network_address = \'$hostname\' "); $sth->execute(); my @result; while (my $ref = $sth->fetchrow_hashref()){ push(@result, $ref->{resource_id}); } $sth->finish(); return @result; } # set_node_state # sets the state field of some node identified by its hostname in the base. # parameters : base, hostname, state, finaudDecision # return value : / # side effects : changes the state value in some field of the nodes table sub set_node_state($$$$) { my $dbh = shift; my $hostname = shift; my $state = shift; my $finaud = shift; if ($state eq "Suspected"){ if ($dbh->do(" UPDATE resources SET state = \'$state\', finaud_decision = \'$finaud\', state_num = $State_to_num{$state} WHERE network_address = \'$hostname\' AND (state = \'Alive\' OR (state = \'Suspected\' AND \'$finaud\' = \'NO\' AND finaud_decision = \'YES\')) ") <= 0){ # OAR wants to turn the node into Suspected state but it is not in # the Alive state --> so we do nothing OAR::Modules::Judas::oar_debug("[OAR::IO] Try to turn the node $hostname into Suspected but it is not into the Alive state SO we do nothing\n"); return(); } }else{ $dbh->do(" UPDATE resources SET state = \'$state\', finaud_decision = \'$finaud\', state_num = $State_to_num{$state} WHERE network_address = \'$hostname\' "); } my $date = get_date($dbh); if ($Db_type eq "Pg"){ $dbh->do(" UPDATE resource_logs SET date_stop = \'$date\' FROM resources WHERE resource_logs.date_stop = 0 AND resource_logs.attribute = \'state\' AND resources.network_address = \'$hostname\' AND resource_logs.resource_id = resources.resource_id "); }else{ $dbh->do(" UPDATE resource_logs, resources SET resource_logs.date_stop = \'$date\' WHERE resource_logs.date_stop = 0 AND resource_logs.attribute = \'state\' AND resources.network_address = \'$hostname\' AND resource_logs.resource_id = resources.resource_id "); } $dbh->do("INSERT INTO resource_logs (resource_id,attribute,value,date_start,finaud_decision) SELECT resources.resource_id,\'state\',\'$state\',\'$date\',\'$finaud\' FROM resources WHERE resources.network_address = \'$hostname\' "); } # set_resource_nextState # sets the nextState field of a resource identified by its resource_id # parameters : base, resource id, nextState # return value : / sub set_resource_nextState($$$) { my $dbh = shift; my $resource = shift; my $nextState = shift; my $result = $dbh->do(" UPDATE resources SET next_state = \'$nextState\', next_finaud_decision = \'NO\' WHERE resource_id = $resource "); return($result); } # set_resource_state # sets the state field of a resource # parameters : base, resource id, state, finaudDecision sub set_resource_state($$$$) { my $dbh = shift; my $resource_id = shift; my $state = shift; my $finaud = shift; $dbh->do(" UPDATE resources SET state = \'$state\', finaud_decision = \'$finaud\', state_num = $State_to_num{$state} WHERE resource_id = $resource_id "); my $date = get_date($dbh); $dbh->do(" UPDATE resource_logs SET date_stop = \'$date\' WHERE date_stop = 0 AND attribute = \'state\' AND resource_id = $resource_id "); $dbh->do("INSERT INTO resource_logs (resource_id,attribute,value,date_start,finaud_decision) VALUES ($resource_id, \'state\', \'$state\',\'$date\',\'$finaud\') "); } # set_node_nextState # sets the nextState field of a node identified by its network_address # parameters : base, network_address, nextState # return value : / sub set_node_nextState($$$) { my $dbh = shift; my $hostname = shift; my $nextState = shift; my $result = $dbh->do(" UPDATE resources SET next_state = \'$nextState\', next_finaud_decision = \'NO\' WHERE network_address = \'$hostname\' "); return($result); } # set_node_nextState_if_necessary sub set_node_nextState_if_necessary($$$) { my $dbh = shift; my $hostname = shift; my $nextState = shift; my $result = $dbh->do(" UPDATE resources SET next_state = \'$nextState\', next_finaud_decision = \'NO\' WHERE network_address = \'$hostname\' AND state != \'$nextState\' AND next_state = \'UnChanged\' "); return($result); } # update_resource_nextFinaudDecision # update nextFinaudDecision field # parameters : base, resource_id, "YES" or "NO" sub update_resource_nextFinaudDecision($$$){ my $dbh = shift; my $resourceId = shift; my $finaud = shift; $dbh->do(" UPDATE resources SET next_finaud_decision = \'$finaud\' WHERE resource_id = $resourceId "); } # update_node_nextFinaudDecision # update nextFinaudDecision field # parameters : base, network_address, "YES" or "NO" sub update_node_nextFinaudDecision($$$){ my $dbh = shift; my $node = shift; my $finaud = shift; $dbh->do(" UPDATE resources SET next_finaud_decision = \'$finaud\' WHERE network_address = \'$node\' "); } # set_node_expiryDate # sets the expiryDate field of some node identified by its hostname in the base. # parameters : base, hostname, expiryDate # return value : / # side effects : changes the expiryDate value in some field of the nodeProperties table sub set_node_expiryDate($$$) { my $dbh = shift; my $hostname = shift; my $expiryDate = shift; # FIX ME: check first that the expiryDate is actually in the future, return error else $dbh->do(" UPDATE resources SET expiry_date = \'$expiryDate\' WHERE network_address =\'$hostname\' "); } # set a node property # change resource_properties table value for resources with the specified network_address # parameters : base, hostname, property name, value # return : 0 if all is good, otherwise 1 if the property does not exist or the value is incorrect sub set_node_property($$$$){ my $dbh = shift; my $hostname = shift; my $property = shift; my $value = shift; # Test if we must change the property my $nbRowsAffected; eval{ $nbRowsAffected = $dbh->do("UPDATE resources SET $property = \'$value\' WHERE resources.network_address = \'$hostname\' "); }; if ($nbRowsAffected < 1){ return(1); }else{ #Update LOG table my $date = get_date($dbh); if ($Db_type eq "Pg"){ $dbh->do(" UPDATE resource_logs SET date_stop = \'$date\' FROM resources WHERE resource_logs.date_stop = 0 AND resources.network_address = \'$hostname\' AND resource_logs.attribute = \'$property\' AND resources.resource_id = resource_logs.resource_id "); }else{ $dbh->do(" UPDATE resources, resource_logs SET resource_logs.date_stop = \'$date\' WHERE resource_logs.date_stop = 0 AND resources.network_address = \'$hostname\' AND resource_logs.attribute = \'$property\' AND resources.resource_id = resource_logs.resource_id "); } $dbh->do(" INSERT INTO resource_logs (resource_id,attribute,value,date_start) SELECT resources.resource_id, \'$property\', \'$value\', \'$date\' FROM resources WHERE resources.network_address = \'$hostname\' "); return(0); } } # set a resource property # change resource_properties table value for resource specified # parameters : base, resource, property name, value # return : 0 if all is good, otherwise 1 if the property does not exist or the value is incorrect sub set_resource_property($$$$){ my $dbh = shift; my $resource = shift; my $property = shift; my $value = shift; # Test if we must change the property my $nbRowsAffected; eval{ $nbRowsAffected = $dbh->do("UPDATE resources SET $property = \'$value\' WHERE resource_id = \'$resource\' "); }; if ($nbRowsAffected < 1){ warn "query UPDATE resources SET $property = $value WHERE resource_ID = $resource"; warn "rows affected $nbRowsAffected"; return(1); }else{ #Update LOG table my $date = get_date($dbh); $dbh->do(" UPDATE resource_logs SET date_stop = \'$date\' WHERE date_stop = 0 AND resource_id = \'$resource\' AND attribute = \'$property\' "); $dbh->do(" INSERT INTO resource_logs (resource_id,attribute,value,date_start) VALUES ($resource, \'$property\', \'$value\', \'$date\') "); return(0); } } # add_event_maintenance_on # add an event in the table resource_logs indicating that this # resource is in maintenance (state = Absent, available_upto = 0) # params: base, resource_id, date_start sub add_event_maintenance_on($$$){ my $dbh = shift; my $resource_id = shift; my $date_start = shift; $dbh->do(" INSERT INTO resource_logs (resource_id,attribute,value,date_start) VALUES ($resource_id, \'maintenance\', \'on\', \'$date_start\') "); return(0); } # add_event_maintenance_off # update the event in the table resource_logs indicating that this # resource is in maintenance (state = Absent, available_upto = 0) # set the date_stop # params: base, resource_id, date_stop sub add_event_maintenance_off($$$){ my $dbh = shift; my $resource_id = shift; my $date_stop = shift; $dbh->do(" UPDATE resource_logs SET date_stop = \'$date_stop\' WHERE date_stop = 0 AND resource_id = \'$resource_id\' AND attribute = \'maintenance\' "); return(0); } # get_resources_with_given_sql # gets the resource list with the given sql properties # parameters : base, $sql where clause # return value : list of resource id # side effects : / sub get_resources_with_given_sql($$) { my $dbh = shift; my $where = shift; my $sth = $dbh->prepare(" SELECT resource_id FROM resources WHERE $where "); $sth->execute(); my @res = (); while (my $ref = $sth->fetchrow_hashref()) { push(@res, $ref->{resource_id}); } $sth->finish(); return(@res); } # get_nodes_with_given_sql # gets the nodes list with the given sql properties # parameters : base, $sql where clause # return value : list of network addresses # side effects : / sub get_nodes_with_given_sql($$) { my $dbh = shift; my $where = shift; my $sth = $dbh->prepare(" SELECT distinct(network_address) FROM resources WHERE $where "); $sth->execute(); my @res = (); while (my $ref = $sth->fetchrow_hashref()) { push(@res, $ref->{network_address}); } $sth->finish(); return(@res); } ## return all properties for a specific resource ## parameters : base, resource #sub get_resource_properties($$){ # my $dbh = shift; # my $resource = shift; # # my $sth = $dbh->prepare(" SELECT * # FROM resources # WHERE # resource_id = $resource"); # $sth->execute(); # my %results = %{$sth->fetchrow_hashref()}; # $sth->finish(); # # return(%results); #} # get resource names that will change their state # parameters : base sub get_resources_change_state($){ my $dbh = shift; my $sth = $dbh->prepare(" SELECT resource_id, next_state FROM resources WHERE next_state != \'UnChanged\'"); $sth->execute(); my %results; while (my @ref = $sth->fetchrow_array()) { $results{$ref[0]} = $ref[1]; } $sth->finish(); return(%results); } # list property fields of the resource_properties table # args : db ref sub list_resource_properties_fields($){ my $dbh = shift; my $req; if ($Db_type eq "Pg"){ $req = "SELECT column_name AS field FROM information_schema.columns WHERE table_name = \'resources\'"; }else{ $req = "SHOW COLUMNS FROM resources"; } my $sth = $dbh->prepare($req); $sth->execute(); my %results; while (my @ref = $sth->fetchrow_array()) { $results{$ref[0]} = 1; } $sth->finish(); return(%results); } # update_current_scheduler_priority # Update the scheduler_priority field of the table resources sub update_current_scheduler_priority($$$$$){ my $dbh = shift; my $job_id = shift; my $moldable_id = shift; my $value = shift; my $state = shift; $state = "STOP" if ($state ne "START"); if (is_conf("SCHEDULER_PRIORITY_HIERARCHY_ORDER")){ my $types = OAR::IO::get_current_job_types($dbh,$job_id); if (((defined($types->{besteffort})) or (defined($types->{timesharing}))) and (($state eq "START" and (is_an_event_exists($dbh,$job_id,"SCHEDULER_PRIORITY_UPDATED_START") <= 0)) or (($state eq "STOP") and (is_an_event_exists($dbh,$job_id,"SCHEDULER_PRIORITY_UPDATED_START") > 0))) ){ my $coeff = 1; if ((defined($types->{timesharing})) and !(defined($types->{besteffort}))){ $coeff = 10; } my $index = 0; foreach my $f (split('/',get_conf("SCHEDULER_PRIORITY_HIERARCHY_ORDER"))){ next if ($f eq ""); $index++; my $sth = $dbh->prepare(" SELECT distinct(resources.$f) FROM assigned_resources, resources WHERE assigned_resource_index = \'CURRENT\' AND moldable_job_id = $moldable_id AND assigned_resources.resource_id = resources.resource_id "); $sth->execute(); my $value_str; while (my @ref = $sth->fetchrow_array()){ $value_str .= $dbh->quote($ref[0]); $value_str .= ','; } $sth->finish(); return if (!defined($value_str)); chop($value_str); my $req = "UPDATE resources SET scheduler_priority = scheduler_priority + ($value * $index * $coeff) WHERE $f IN (".$value_str.") "; $dbh->do($req); } add_new_event($dbh,"SCHEDULER_PRIORITY_UPDATED_$state",$job_id,"Scheduler priority for job $job_id updated (".get_conf("SCHEDULER_PRIORITY_HIERARCHY_ORDER").")"); } } } #get the range when nodes are dead between two dates # arg : base, start date, end date sub get_resource_dead_range_date($$$){ my $dbh = shift; my $date_start = shift; my $date_end = shift; # get dead nodes between two dates my $req = "SELECT resource_id, date_start, date_stop, value FROM resource_logs WHERE attribute = \'state\' AND ( value = \'Absent\' OR value = \'Dead\' OR value = \'Suspected\' ) AND date_start <= $date_end AND ( date_stop = 0 OR date_stop >= $date_start ) "; my $sth = $dbh->prepare($req); $sth->execute(); my %results; while (my @ref = $sth->fetchrow_array()) { my $interval_stopDate = $ref[2]; if (!defined($interval_stopDate)){ $interval_stopDate = $date_end; } push(@{$results{$ref[0]}}, [$ref[1],$interval_stopDate,$ref[3]]); } $sth->finish(); return(%results); } # get_expired_resources # get the list of resources whose expiry_date is in the past and which are not dead yet. # 0000-00-00 00:00:00 is always considered as in the future # parameters: base # return value: list of resources # side effects: / sub get_expired_resources($){ my $dbh = shift; # get expired nodes my $date = get_date($dbh); my $req = "SELECT resources.resource_id FROM resources WHERE resources.state = \'Alive\' AND resources.expiry_date > 0 AND resources.desktop_computing = \'YES\' AND resources.expiry_date < $date "; my $sth = $dbh->prepare($req); $sth->execute(); my @results; while (my @res = $sth->fetchrow_array()){ push(@results, $res[0]); } $sth->finish(); return(@results); } # is_node_desktop_computing # tell if a node is for desktop computing. # parameters: base, hostname # return value: boolean # side effects: / sub is_node_desktop_computing($$){ my $dbh = shift; my $hostname = shift; my $sth = $dbh->prepare(" SELECT desktop_computing FROM resources WHERE network_address = \'$hostname\' "); $sth->execute(); my @ref; my $result; while (@ref = $sth->fetchrow_array()){ $result = $ref[0]; if ($result ne "YES"){ return($result); } } return($result); } # Return a data structure with the resource description of the given job # arg : database ref, job id # return a data structure (an array of moldable jobs): # example for the first moldable job of the list: # $result = [ # [ # { # property => SQL property # resources => [ # { # resource => resource name # value => number of this wanted resource # } # ] # } # ], # walltime, # moldable_job_id # ] sub get_resources_data_structure_current_job($$){ my $dbh = shift; my $job_id = shift; # my $sth = $dbh->prepare(" SELECT moldable_job_descriptions.moldable_id, job_resource_groups.res_group_id, moldable_job_descriptions.moldable_walltime, job_resource_groups.res_group_property, job_resource_descriptions.res_job_resource_type, job_resource_descriptions.res_job_value # FROM moldable_job_descriptions, job_resource_groups, job_resource_descriptions, jobs # WHERE # moldable_job_descriptions.moldable_index = \'CURRENT\' # AND job_resource_groups.res_group_index = \'CURRENT\' # AND job_resource_descriptions.res_job_index = \'CURRENT\' # AND jobs.job_id = $job_id # AND jobs.job_id = moldable_job_descriptions.moldable_job_id # AND job_resource_groups.res_group_moldable_id = moldable_job_descriptions.moldable_id # AND job_resource_descriptions.res_job_group_id = job_resource_groups.res_group_id # ORDER BY moldable_job_descriptions.moldable_id, job_resource_groups.res_group_id, job_resource_descriptions.res_job_order ASC # "); my $sth = $dbh->prepare(" SELECT moldable_job_descriptions.moldable_id, job_resource_groups.res_group_id, moldable_job_descriptions.moldable_walltime, job_resource_groups.res_group_property, job_resource_descriptions.res_job_resource_type, job_resource_descriptions.res_job_value FROM moldable_job_descriptions, job_resource_groups, job_resource_descriptions, jobs WHERE jobs.job_id = $job_id AND jobs.job_id = moldable_job_descriptions.moldable_job_id AND job_resource_groups.res_group_moldable_id = moldable_job_descriptions.moldable_id AND job_resource_descriptions.res_job_group_id = job_resource_groups.res_group_id ORDER BY moldable_job_descriptions.moldable_id, job_resource_groups.res_group_id, job_resource_descriptions.res_job_order ASC "); $sth->execute(); my $result; my $group_index = -1; my $moldable_index = -1; my $previous_group = 0; my $previous_moldable = 0; while (my @ref = $sth->fetchrow_array()){ if ($previous_moldable != $ref[0]){ $moldable_index++; $previous_moldable = $ref[0]; $group_index = 0; $previous_group = $ref[1]; }elsif ($previous_group != $ref[1]){ $group_index++; $previous_group = $ref[1]; } # Store walltime $result->[$moldable_index]->[1] = $ref[2]; $result->[$moldable_index]->[2] = $ref[0]; #Store properties group $result->[$moldable_index]->[0]->[$group_index]->{property} = $ref[3]; my %tmp_hash = ( resource => $ref[4], value => $ref[5] ); push(@{$result->[$moldable_index]->[0]->[$group_index]->{resources}}, \%tmp_hash); } $sth->finish(); return($result); } # get_absent_suspected_resources_for_a_timeout # args : base ref, timeout in seconds sub get_absent_suspected_resources_for_a_timeout($$){ my $dbh = shift; my $timeout = shift; my $date = get_date($dbh); my $req = "SELECT resource_id FROM resource_logs WHERE attribute = \'state\' AND date_stop = 0 AND date_start + $timeout < $date "; my $sth = $dbh->prepare($req); $sth->execute(); my @results; while (my @ref = $sth->fetchrow_array()) { push(@results, $ref[0]); } $sth->finish(); return(@results); } # get_cpuset_values_for_a_moldable_job # get cpuset values for each nodes of a MJob sub get_cpuset_values_for_a_moldable_job($$$){ my $dbh = shift; my $cpuset_field = shift; my $mjob_id = shift; my $sql_where_string = "\'0\'"; my $resources_to_always_add_type = get_conf("SCHEDULER_RESOURCES_ALWAYS_ASSIGNED_TYPE"); if (defined($resources_to_always_add_type) and ($resources_to_always_add_type ne "")){ $sql_where_string = "resources.type = \'$resources_to_always_add_type\'"; } my $sth = $dbh->prepare(" SELECT resources.network_address, resources.$cpuset_field FROM resources, assigned_resources WHERE assigned_resources.moldable_job_id = $mjob_id AND assigned_resources.resource_id = resources.resource_id AND resources.network_address != \'\' AND (resources.type = \'default\' OR $sql_where_string) GROUP BY resources.network_address, resources.$cpuset_field "); $sth->execute(); my $results; while (my @ref = $sth->fetchrow_array()) { push(@{$results->{$ref[0]}}, $ref[1]); } return($results); } # QUEUES MANAGEMENT # get_queues # create the list of queues sorted by descending priority # only return the Active queues. # return value : list of queues # side effects : / sub get_active_queues($) { my $dbh = shift; my $sth = $dbh->prepare(" SELECT queue_name,scheduler_policy FROM queues WHERE state = \'Active\' ORDER BY priority DESC "); $sth->execute(); my @res ; while (my $ref = $sth->fetchrow_hashref()) { push(@res, [ $ref->{'queue_name'}, $ref->{'scheduler_policy'} ]); } $sth->finish(); return @res; } # get_all_queue_informations # return a hashtable with all queues and their properties sub get_all_queue_informations($){ my $dbh = shift; my $sth = $dbh->prepare(" SELECT * FROM queues "); $sth->execute(); my %res = (); while (my $ref = $sth->fetchrow_hashref()) { $res{$ref->{queue_name}} = $ref ; } $sth->finish(); return %res; } # stop_all_queues sub stop_all_queues($){ my $dbh = shift; $dbh->do(" UPDATE queues SET state = \'notActive\' "); } # start_all_queues sub start_all_queues($){ my $dbh = shift; $dbh->do(" UPDATE queues SET state = \'Active\' "); } # stop_a_queue sub stop_a_queue($$){ my $dbh = shift; my $queue = shift; $dbh->do(" UPDATE queues SET state = \'notActive\' WHERE queue_name = \'$queue\' "); } # start_a_queue sub start_a_queue($$){ my $dbh = shift; my $queue = shift; $dbh->do(" UPDATE queues SET state = \'Active\' WHERE queue_name = \'$queue\' "); } # delete a queue sub delete_a_queue($$){ my $dbh = shift; my $queue = shift; $dbh->do("DELETE FROM queues WHERE queue_name = \'$queue\'"); } # create a queue sub create_a_queue($$$$){ my $dbh = shift; my $queue = shift; my $policy = shift; my $priority = shift; $dbh->do(" INSERT INTO queues (queue_name,priority,scheduler_policy) VALUES (\'$queue\',$priority,\'$policy\')"); } # GANTT MANAGEMENT #get previous scheduler decisions #args : base #return a hashtable : job_id --> [start_time,walltime,queue_name,\@resources,state] sub get_gantt_scheduled_jobs($){ my $dbh = shift; my $sth = $dbh->prepare("SELECT j.job_id, g2.start_time, m.moldable_walltime, g1.resource_id, j.queue_name, j.state, j.job_user, j.job_name,m.moldable_id,j.suspended FROM gantt_jobs_resources g1, gantt_jobs_predictions g2, moldable_job_descriptions m, jobs j WHERE m.moldable_index = \'CURRENT\' AND g1.moldable_job_id = g2.moldable_job_id AND m.moldable_id = g2.moldable_job_id AND j.job_id = m.moldable_job_id ORDER BY j.start_time, j.job_id "); $sth->execute(); my %res ; my @order; while (my @ref = $sth->fetchrow_array()) { if (!defined($res{$ref[0]})){ $res{$ref[0]}->[0] = $ref[1]; $res{$ref[0]}->[1] = $ref[2]; $res{$ref[0]}->[2] = $ref[4]; $res{$ref[0]}->[4] = $ref[5]; $res{$ref[0]}->[5] = $ref[6]; $res{$ref[0]}->[6] = $ref[7]; $res{$ref[0]}->[7] = $ref[8]; $res{$ref[0]}->[8] = $ref[9]; push(@order,$ref[0]); } push(@{$res{$ref[0]}->[3]}, $ref[3]); } $sth->finish(); return(\@order, %res); } #get previous scheduler decisions for visu #args : base #return a hashtable : job_id --> [start_time,weight,walltime,queue_name,\@nodes] sub get_gantt_visu_scheduled_jobs($){ my $dbh = shift; my $sth = $dbh->prepare("SELECT g2.job_id, g2.start_time, j.weight, j.maxTime, g1.hostname, j.queue_name, j.state FROM ganttJobsNodes_visu g1, ganttJobsPrediction_visu g2, jobs j WHERE g1.job_id = g2.job_id AND j.job_id = g1.job_id "); $sth->execute(); my %res ; while (my @ref = $sth->fetchrow_array()) { if (!defined($res{$ref[0]})){ $res{$ref[0]}->[0] = $ref[1]; $res{$ref[0]}->[1] = $ref[2]; $res{$ref[0]}->[2] = $ref[3]; $res{$ref[0]}->[3] = $ref[5]; $res{$ref[0]}->[5] = $ref[6]; } push(@{$res{$ref[0]}->[4]}, $ref[4]); } $sth->finish(); return %res; } #add scheduler decisions #args : base,moldable_job_id,start_time,\@resources #return nothing sub add_gantt_scheduled_jobs($$$$){ my $dbh = shift; my $id_moldable_job = shift; my $start_time = shift; my $resource_list = shift; $dbh->do("INSERT INTO gantt_jobs_predictions (moldable_job_id,start_time) VALUES ($id_moldable_job,\'$start_time\') "); my $str = ""; foreach my $i (@{$resource_list}){ $str .= "($id_moldable_job,$i),"; } chop($str); $dbh->do("INSERT INTO gantt_jobs_resources (moldable_job_id,resource_id) VALUES $str "); } # Remove an entry in the gantt # params: base, job_id, resource sub remove_gantt_resource_job($$$){ my $dbh = shift; my $job = shift; my $resource = shift; $dbh->do("DELETE FROM gantt_jobs_resources WHERE moldable_job_id = $job AND resource_id = $resource"); } # Add gantt date (now) in database # args : base, date sub set_gantt_date($$){ my $dbh = shift; my $date = shift; $dbh->do("INSERT INTO gantt_jobs_predictions (moldable_job_id,start_time) VALUES (0,\'$date\') "); } # Update start_time in gantt for a specified job # args : base, job id, date sub set_gantt_job_startTime($$$){ my $dbh = shift; my $job = shift; my $date = shift; $dbh->do("UPDATE gantt_jobs_predictions SET start_time = \'$date\' WHERE moldable_job_id = $job "); } # Get start_time for a given job # args : base, job id sub get_gantt_job_start_time($$){ my $dbh = shift; my $job = shift; my $sth = $dbh->prepare("SELECT gantt_jobs_predictions.start_time, gantt_jobs_predictions.moldable_job_id FROM gantt_jobs_predictions,moldable_job_descriptions WHERE moldable_job_descriptions.moldable_job_id = $job AND gantt_jobs_predictions.moldable_job_id = moldable_job_descriptions.moldable_id "); $sth->execute(); my @res = $sth->fetchrow_array(); $sth->finish(); if (defined($res[0])){ return($res[0],$res[1]); }else{ return(undef); } } # Get start_time for a given job # args : base, job id sub get_gantt_job_start_time_visu($$){ my $dbh = shift; my $job = shift; my $sth = $dbh->prepare("SELECT gantt_jobs_predictions_visu.start_time, gantt_jobs_predictions_visu.moldable_job_id FROM gantt_jobs_predictions_visu,moldable_job_descriptions WHERE moldable_job_descriptions.moldable_job_id = $job AND gantt_jobs_predictions_visu.moldable_job_id = moldable_job_descriptions.moldable_id "); $sth->execute(); my @res = $sth->fetchrow_array(); $sth->finish(); if (defined($res[0])){ return($res[0],$res[1]); }else{ return(undef); } } # Update ganttJobsPrediction_visu and ganttJobsNodes_visu with values in ganttJobsPrediction and in ganttJobsNodes # arg: database ref sub update_gantt_visualization($){ my $dbh = shift; lock_table($dbh, ["gantt_jobs_predictions_visu","gantt_jobs_resources_visu","gantt_jobs_predictions","gantt_jobs_resources"]); $dbh->do("DELETE FROM gantt_jobs_predictions_visu"); $dbh->do("DELETE FROM gantt_jobs_resources_visu"); # $dbh->do("OPTIMIZE TABLE ganttJobsResources_visu, ganttJobsPredictions_visu"); $dbh->do("INSERT INTO gantt_jobs_predictions_visu SELECT * FROM gantt_jobs_predictions "); $dbh->do("INSERT INTO gantt_jobs_resources_visu SELECT * FROM gantt_jobs_resources "); unlock_table($dbh); } # Return date of the gantt sub get_gantt_date($){ my $dbh = shift; my $sth = $dbh->prepare("SELECT start_time FROM gantt_jobs_predictions WHERE moldable_job_id = 0 "); $sth->execute(); my @res = $sth->fetchrow_array(); $sth->finish(); return $res[0]; } # Return date of the gantt for visu sub get_gantt_visu_date($){ my $dbh = shift; my $sth = $dbh->prepare("SELECT start_time FROM gantt_jobs_predictions_visu WHERE moldable_job_id = 0 "); $sth->execute(); my @res = $sth->fetchrow_array(); $sth->finish(); return($res[0]); } # Get all waiting reservation jobs # parameter : database ref # return an array of moldable job informations sub get_waiting_reservations_already_scheduled($){ my $dbh = shift; my $sth = $dbh->prepare(" SELECT moldable_job_descriptions.moldable_id, gantt_jobs_predictions.start_time, gantt_jobs_resources.resource_id, moldable_job_descriptions.moldable_walltime FROM jobs, moldable_job_descriptions, gantt_jobs_predictions, gantt_jobs_resources WHERE (jobs.state = \'Waiting\' OR jobs.state = \'toAckReservation\') AND jobs.reservation = \'Scheduled\' AND jobs.job_id = moldable_job_descriptions.moldable_job_id AND gantt_jobs_predictions.moldable_job_id = moldable_job_descriptions.moldable_id AND gantt_jobs_resources.moldable_job_id = moldable_job_descriptions.moldable_id "); $sth->execute(); my $res; while (my @ref = $sth->fetchrow_array()) { push(@{$res->{$ref[0]}->{resources}}, $ref[2]); $res->{$ref[0]}->{start_time} = $ref[1]; $res->{$ref[0]}->{walltime} = $ref[3]; } $sth->finish(); return($res); } #Flush gantt tables sub gantt_flush_tables($$$){ my $dbh = shift; my $reservations_to_keep = shift; my $log = shift; if (defined($log)){ my $date = get_gantt_date($dbh); $dbh->do(" INSERT INTO gantt_jobs_predictions_log (sched_date,moldable_job_id,start_time) SELECT \'$date\', gantt_jobs_predictions.moldable_job_id, gantt_jobs_predictions.start_time FROM gantt_jobs_predictions WHERE gantt_jobs_predictions.moldable_job_id != 0 "); $dbh->do(" INSERT INTO gantt_jobs_resources_log (sched_date,moldable_job_id,resource_id) SELECT \'$date\', gantt_jobs_resources.moldable_job_id, gantt_jobs_resources.resource_id FROM gantt_jobs_resources "); } my $sql = "\'1\'"; my @jobs_to_keep = keys(%{$reservations_to_keep}); if ($#jobs_to_keep >= 0){ $sql = "moldable_job_id NOT IN (".join(',',@jobs_to_keep).")"; } #$dbh->do("TRUNCATE TABLE gantt_jobs_predictions"); $dbh->do(" DELETE FROM gantt_jobs_predictions WHERE $sql "); #$dbh->do("TRUNCATE TABLE gantt_jobs_resources"); $dbh->do(" DELETE FROM gantt_jobs_resources WHERE $sql "); # $dbh->do("OPTIMIZE TABLE ganttJobs, ganttJobsPrediction"); } sub update_scheduler_last_job_date($$$){ my $dbh = shift; my $date = shift; my $moldable_id = shift; my $req; if ($Db_type eq "Pg"){ $req = "UPDATE resources SET last_job_date = $date FROM assigned_resources WHERE assigned_resources.moldable_job_id = $moldable_id AND assigned_resources.resource_id = resources.resource_id "; }else{ $req = "UPDATE resources, assigned_resources SET resources.last_job_date = $date WHERE assigned_resources.moldable_job_id = $moldable_id AND assigned_resources.resource_id = resources.resource_id "; } return($dbh->do($req)); } sub search_idle_nodes($$){ my $dbh = shift; my $date = shift; my $req = "SELECT resources.network_address FROM resources, gantt_jobs_resources, gantt_jobs_predictions WHERE resources.resource_id = gantt_jobs_resources.resource_id AND gantt_jobs_predictions.start_time <= $date AND resources.network_address != \'\' AND resources.type = \'default\' AND gantt_jobs_predictions.moldable_job_id = gantt_jobs_resources.moldable_job_id GROUP BY resources.network_address "; my $sth = $dbh->prepare($req); $sth->execute(); my %nodes_occupied; while (my @ref = $sth->fetchrow_array()) { $nodes_occupied{$ref[0]} = 1; } $sth->finish(); $req = "SELECT resources.network_address, MAX(resources.last_job_date) FROM resources WHERE resources.state = \'Alive\' AND resources.network_address != \'\' AND resources.type = \'default\' AND resources.available_upto < 2147483647 AND resources.available_upto > 0 GROUP BY resources.network_address"; $sth = $dbh->prepare($req); $sth->execute(); my %res ; while (my @ref = $sth->fetchrow_array()) { if (!defined($nodes_occupied{$ref[0]})){ $res{$ref[0]} = $ref[1]; } } $sth->finish(); return(%res); } sub get_next_job_date_on_node($$){ my $dbh = shift; my $hostname = shift; my $req = "SELECT MIN(gantt_jobs_predictions.start_time) FROM resources, gantt_jobs_predictions, gantt_jobs_resources WHERE resources.network_address = \'$hostname\' AND gantt_jobs_resources.resource_id = resources.resource_id AND gantt_jobs_predictions.moldable_job_id = gantt_jobs_resources.moldable_job_id "; my $sth = $dbh->prepare($req); $sth->execute(); my @ref = $sth->fetchrow_array(); $sth->finish(); return($ref[0]); } #Get jobs to launch at a given date #args : base, date in sql format sub get_gantt_jobs_to_launch($$){ my $dbh = shift; my $date = shift; my $req = "SELECT DISTINCT(j.job_id) FROM gantt_jobs_resources g1, gantt_jobs_predictions g2, jobs j, moldable_job_descriptions m, resources WHERE m.moldable_index = \'CURRENT\' AND g1.moldable_job_id = g2.moldable_job_id AND m.moldable_id = g1.moldable_job_id AND j.job_id = m.moldable_job_id AND g2.start_time <= $date AND j.state = \'Waiting\' AND resources.resource_id = g1.resource_id AND resources.state != \'Alive\' "; my $sth = $dbh->prepare($req); $sth->execute(); my %jobs_not_to_launch; while (my @ref = $sth->fetchrow_array()) { $jobs_not_to_launch{$ref[0]} = 1; } $sth->finish(); $req = " SELECT g2.moldable_job_id, g1.resource_id, j.job_id FROM gantt_jobs_resources g1, gantt_jobs_predictions g2, jobs j, moldable_job_descriptions m, resources WHERE m.moldable_index = \'CURRENT\' AND g1.moldable_job_id = g2.moldable_job_id AND m.moldable_id = g1.moldable_job_id AND j.job_id = m.moldable_job_id AND g2.start_time <= $date AND j.state = \'Waiting\' AND resources.resource_id = g1.resource_id AND resources.state = \'Alive\' "; $sth = $dbh->prepare($req); $sth->execute(); my %res ; while (my @ref = $sth->fetchrow_array()) { if(!defined($jobs_not_to_launch{$ref[2]})){ $res{$ref[2]}->[0] = $ref[0]; push(@{$res{$ref[2]}->[1]}, $ref[1]); } } $sth->finish(); return(%res); } #Get hostname that we must wake up to launch jobs #args : base, date in sql format, time to wait for the node to wake up sub get_gantt_hostname_to_wake_up($$$){ my $dbh = shift; my $date = shift; my $wakeup_time = shift; my $req = "SELECT resources.network_address FROM gantt_jobs_resources g1, gantt_jobs_predictions g2, jobs j, moldable_job_descriptions m, resources WHERE m.moldable_index = \'CURRENT\' AND g1.moldable_job_id= g2.moldable_job_id AND m.moldable_id = g1.moldable_job_id AND j.job_id = m.moldable_job_id AND g2.start_time <= $date + $wakeup_time AND j.state = \'Waiting\' AND resources.resource_id = g1.resource_id AND resources.state = \'Absent\' AND resources.network_address != \'\' AND resources.type = \'default\' AND (g2.start_time + m.moldable_walltime) <= resources.available_upto GROUP BY resources.network_address "; my $sth = $dbh->prepare($req); $sth->execute(); my @res ; while (my @ref = $sth->fetchrow_array()) { push(@res, $ref[0]); } $sth->finish(); return(@res); } #Get informations about resources for jobs to launch at a given date #args : base, date in sql format sub get_gantt_resources_for_jobs_to_launch($$){ my $dbh = shift; my $date = shift; my $req = "SELECT g1.resource_id, j.job_id FROM gantt_jobs_resources g1, gantt_jobs_predictions g2, jobs j, moldable_job_descriptions m WHERE m.moldable_index = \'CURRENT\' AND g1.moldable_job_id = m.moldable_id AND m.moldable_job_id = j.job_id AND g1.moldable_job_id = g2.moldable_job_id AND g2.start_time <= $date AND j.state = \'Waiting\' "; my $sth = $dbh->prepare($req); $sth->execute(); my %res ; while (my @ref = $sth->fetchrow_array()) { $res{$ref[0]} = $ref[1]; } $sth->finish(); return %res; } #Get resources for job in the gantt diagram #args : base, moldable job id sub get_gantt_resources_for_job($$){ my $dbh = shift; my $job = shift; my $sth = $dbh->prepare("SELECT g.resource_id FROM gantt_jobs_resources g WHERE g.moldable_job_id = $job "); $sth->execute(); my @res ; while (my @ref = $sth->fetchrow_array()) { push( @res, $ref[0]); } $sth->finish(); return @res; } #Get Alive resources for a job #args : base, moldable job id sub get_gantt_Alive_resources_for_job($$){ my $dbh = shift; my $job = shift; my $sth = $dbh->prepare("SELECT g.resource_id FROM gantt_jobs_resources g, resources r WHERE g.moldable_job_id = $job AND r.resource_id = g.resource_id AND r.state = \'Alive\' "); $sth->execute(); my @res ; while (my @ref = $sth->fetchrow_array()) { push( @res, $ref[0]); } $sth->finish(); return(@res); } #Get network_address allocated to a (waiting) reservation #args : base, job id sub get_gantt_visu_scheduled_job_resources($$){ my $dbh = shift; my $job = shift; my $sth = $dbh->prepare("SELECT r.resource_id, r.network_address, r.state FROM gantt_jobs_resources_visu g, moldable_job_descriptions m, resources r WHERE m.moldable_job_id = $job AND m.moldable_id = g.moldable_job_id AND g.resource_id = r.resource_id "); $sth->execute(); my %h; while (my @ref = $sth->fetchrow_array()) { $h{$ref[0]}->{'network_address'}=$ref[1]; $h{$ref[0]}->{'current_state'}=$ref[2]; } $sth->finish(); return \%h; } # TIME CONVERSION # ymdhms_to_sql # converts a date specified as year, month, day, minutes, secondes to a string # in the format used by the sql database # parameters : year, month, day, hours, minutes, secondes # return value : date string # side effects : / sub ymdhms_to_sql($$$$$$) { my ($year,$mon,$mday,$hour,$min,$sec)=@_; return ($year+1900)."-".($mon+1)."-".$mday." $hour:$min:$sec"; } # sql_to_ymdhms # converts a date specified in the format used by the sql database to year, # month, day, minutes, secondes values # parameters : date string # return value : year, month, day, hours, minutes, secondes # side effects : / sub sql_to_ymdhms($) { my $date=shift; $date =~ tr/-:/ /; my ($year,$mon,$mday,$hour,$min,$sec) = split / /,$date; # adjustment for localtime (since 1st january 1900, month from 0 to 11) $year-=1900; $mon-=1; return ($year,$mon,$mday,$hour,$min,$sec); } # ymdhms_to_local # converts a date specified as year, month, day, minutes, secondes into an # integer local time format # parameters : year, month, day, hours, minutes, secondes # return value : date integer # side effects : / sub ymdhms_to_local($$$$$$) { my ($year,$mon,$mday,$hour,$min,$sec)=@_; return Time::Local::timelocal_nocheck($sec,$min,$hour,$mday,$mon,$year); } # local_to_ymdhms # converts a date specified into an integer local time format to year, month, # day, minutes, secondes values # parameters : date integer # return value : year, month, day, hours, minutes, secondes # side effects : / sub local_to_ymdhms($) { my $date=shift; my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($date); $year += 1900; $mon += 1; return ($year,$mon,$mday,$hour,$min,$sec); } # sql_to_local # converts a date specified in the format used by the sql database to an # integer local time format # parameters : date string # return value : date integer # side effects : / sub sql_to_local($) { my $date=shift; my ($year,$mon,$mday,$hour,$min,$sec)=sql_to_ymdhms($date); #if ($year <= 1971){ # return(0); #}else{ return ymdhms_to_local($year,$mon,$mday,$hour,$min,$sec); #} } # local_to_sql # converts a date specified in an integer local time format to the format used # by the sql database # parameters : date integer # return value : date string # side effects : / sub local_to_sql($) { my $local=shift; #my ($year,$mon,$mday,$hour,$min,$sec)=local_to_ymdhms($local); #return ymdhms_to_sql($year,$mon,$mday,$hour,$min,$sec); #return $year."-".$mon."-".$mday." $hour:$min:$sec"; return(strftime("%F %T",localtime($local))); } # sql_to_hms # converts a date specified in the format used by the sql database to hours, # minutes, secondes values # parameters : date string # return value : hours, minutes, secondes # side effects : / sub sql_to_hms($) { my $date=shift; my ($hour,$min,$sec) = split /:/,$date; return ($hour,$min,$sec); } # hms_to_duration # converts a date specified in hours, minutes, secondes values to a duration # in seconds # parameters : hours, minutes, secondes # return value : duration # side effects : / sub hms_to_duration($$$) { my ($hour,$min,$sec) = @_; return $hour*3600 +$min*60 +$sec; } # hms_to_sql # converts a date specified in hours, minutes, secondes values to the format # used by the sql database # parameters : hours, minutes, secondes # return value : date string # side effects : / sub hms_to_sql($$$) { my ($hour,$min,$sec) = @_; return "$hour:$min:$sec"; } # duration_to_hms # converts a date specified as a duration in seconds to hours, minutes, # secondes values # parameters : duration # return value : hours, minutes, secondes # side effects : / sub duration_to_hms($) { my $date=shift; my $sec=$date%60; $date/=60; my $min=$date%60; $date = int($date / 60); my $hour=$date; return ($hour,$min,$sec); } # duration_to_sql # converts a date specified as a duration in seconds to the format used by the # sql database # parameters : duration # return value : date string # side effects : / sub duration_to_sql($) { my $duration=shift; my ($hour,$min,$sec)=duration_to_hms($duration); return hms_to_sql($hour,$min,$sec); } # sql_to_duration # converts a date specified in the format used by the sql database to a # duration in seconds # parameters : date string # return value : duration # side effects : / sub sql_to_duration($) { my $date=shift; my ($hour,$min,$sec)=sql_to_hms($date); return hms_to_duration($hour,$min,$sec); } # get_date # returns the current time in the format used by the sql database # parameters : database # return value : date string # side effects : / sub get_date($) { #my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime; #return ymdhms_to_sql($year,$mon,$mday,$hour,$min,$sec); my $dbh = shift; my $req; if ($Db_type eq "Pg"){ $req = "select EXTRACT(EPOCH FROM current_timestamp)"; }else{ $req = "SELECT UNIX_TIMESTAMP()"; } my $sth = $dbh->prepare($req); $sth->execute(); my @ref = $sth->fetchrow_array(); $sth->finish(); return(int($ref[0])); } # MONITORING sub register_monitoring_values($$$$){ my $dbh = shift; my $table = shift; my $fields = shift; my $values = shift; if (! insert_monitoring_row($dbh,$table,$fields,$values)){ my $create_str; $create_str = "CREATE TABLE monitoring_$table ("; for (my $i=0; $i <= $#{$fields}; $i++){ if ($values->[$i] =~ /^\d+$/){ if ($Db_type eq "Pg"){ $create_str .= "$fields->[$i] integer,"; }else{ $create_str .= "$fields->[$i] INT,"; } }else{ $create_str .= "$fields->[$i] varchar(255)," } } chop($create_str); $create_str .= ")"; oar_debug("$create_str\n"); if ($dbh->do($create_str)){ if (! insert_monitoring_row($dbh,$table,$fields,$values)){ return(2); } }else{ return(1); } } return(0); } sub insert_monitoring_row($$$$){ my $dbh = shift; my $table = shift; my $fields = shift; my $values = shift; my $value_str; foreach my $v (@{$values}){ $value_str .= $dbh->quote($v); $value_str .= ','; } chop($value_str); return($dbh->do(" INSERT INTO monitoring_$table (".join(",",@{$fields}).") VALUES (".$value_str.") ")); } # ACCOUNTING # check jobs that are not treated in accounting table # params : base, window size sub check_accounting_update($$){ my $dbh = shift; my $windowSize = shift; my $req = "SELECT jobs.start_time, jobs.stop_time, moldable_job_descriptions.moldable_walltime, jobs.job_id, jobs.job_user, jobs.queue_name, count(assigned_resources.resource_id), jobs.project FROM jobs, moldable_job_descriptions, assigned_resources, resources WHERE jobs.accounted = \'NO\' AND (jobs.state = \'Terminated\' OR jobs.state = \'Error\') AND jobs.stop_time >= jobs.start_time AND jobs.start_time > 0 AND jobs.assigned_moldable_job = moldable_job_descriptions.moldable_id AND assigned_resources.moldable_job_id = moldable_job_descriptions.moldable_id AND assigned_resources.resource_id = resources.resource_id AND resources.type = 'default' GROUP BY jobs.start_time, jobs.stop_time, moldable_job_descriptions.moldable_walltime, jobs.job_id, jobs.project, jobs.job_user, jobs.queue_name "; my $sth = $dbh->prepare("$req"); $sth->execute(); while (my @ref = $sth->fetchrow_array()) { my $start = $ref[0]; my $stop = $ref[1]; my $theoricalStopTime = $ref[2] + $start; print("[ACCOUNTING] Treate job $ref[3]\n"); update_accounting($dbh,$start,$stop,$windowSize,$ref[4],$ref[7],$ref[5],"USED",$ref[6]); update_accounting($dbh,$start,$theoricalStopTime,$windowSize,$ref[4],$ref[7],$ref[5],"ASKED",$ref[6]); $dbh->do(" UPDATE jobs SET accounted = \'YES\' WHERE job_id = $ref[3] "); } } # insert accounting data in table accounting # params : base, start date in second, stop date in second, window size, user, queue, type(ASKED or USED) sub update_accounting($$$$$$$$$){ my $dbh = shift; my $start = shift; my $stop = shift; my $windowSize = shift; my $user = shift; my $project = shift; my $queue = shift; my $type = shift; my $nb_resources = shift; use integer; my $nbWindows = $start / $windowSize; my $windowStart = $nbWindows * $windowSize; my $windowStop = $windowStart + $windowSize - 1; my $conso; # Accounting algo while ($stop > $start){ if ($stop <= $windowStop){ $conso = $stop - $start; }else{ $conso = $windowStop - $start + 1; } $conso = $conso * $nb_resources; add_accounting_row($dbh,$windowStart,$windowStop,$user,$project,$queue,$type,$conso); $windowStart = $windowStop + 1; $start = $windowStart; $windowStop += $windowSize; } } # start and stop in SQL syntax sub add_accounting_row($$$$$$$$){ my $dbh = shift; my $start = shift; my $stop = shift; my $user = shift; my $project = shift; my $queue = shift; my $type = shift; my $conso = shift; # Test if the window exists my $sth = $dbh->prepare(" SELECT consumption FROM accounting WHERE accounting_user = \'$user\' AND accounting_project = \'$project\' AND consumption_type = \'$type\' AND queue_name = \'$queue\' AND window_start = \'$start\' AND window_stop = \'$stop\' "); $sth->execute(); my @ref = $sth->fetchrow_array(); if (defined($ref[0])){ # Update the existing window $conso += $ref[0]; print("[ACCOUNTING] Update the existing window $start --> $stop , project $project, user $user, queue $queue, type $type with conso = $conso s\n"); $dbh->do(" UPDATE accounting SET consumption = $conso WHERE accounting_user = \'$user\' AND accounting_project = \'$project\' AND consumption_type = \'$type\' AND queue_name = \'$queue\' AND window_start = \'$start\' AND window_stop = \'$stop\' "); }else{ # Create the window print("[ACCOUNTING] Create new window $start --> $stop , project $project, user $user, queue $queue, type $type with conso = $conso s\n"); $dbh->do(" INSERT INTO accounting (accounting_user,consumption_type,queue_name,window_start,window_stop,consumption,accounting_project) VALUES (\'$user\',\'$type\',\'$queue\',\'$start\',\'$stop\',$conso,\'$project\') "); } } sub get_sum_accounting_window($$$$){ my $dbh = shift; my $queue = shift; my $start_window = shift; my $stop_window = shift; my $sth = $dbh->prepare(" SELECT consumption_type, SUM(consumption) FROM accounting WHERE queue_name = \'$queue\' AND window_start >= $start_window AND window_start < $stop_window GROUP BY consumption_type "); $sth->execute(); my $results; while (my @r = $sth->fetchrow_array()) { $results->{$r[0]} = $r[1]; } $sth->finish(); return($results); } sub get_sum_accounting_for_param($$$$$){ my $dbh = shift; my $queue = shift; my $param_name = shift; my $start_window = shift; my $stop_window = shift; my $sth = $dbh->prepare(" SELECT $param_name,consumption_type, SUM(consumption) FROM accounting WHERE queue_name = \'$queue\' AND window_start >= $start_window AND window_start < $stop_window GROUP BY $param_name,consumption_type "); $sth->execute(); my $results; while (my @r = $sth->fetchrow_array()) { $results->{$r[0]}->{$r[1]} = $r[2]; } $sth->finish(); return($results); } # Get an array of consumptions by users # params: base, start date, ending date, optional user sub get_accounting_summary($$$$$){ my $dbh = shift; my $start = shift; my $stop = shift; my $user = shift; my $sql_property = shift; my $user_query=""; my $property=""; if (defined($user) && "$user" ne "") { $user_query="AND accounting_user = ". $dbh->quote($user); } if (defined($sql_property) && "$sql_property" ne "") { $property="AND ( $sql_property )"; }else{ $property=""; } my $sth = $dbh->prepare(" SELECT accounting_user as user, consumption_type, sum(consumption) as seconds, floor(sum(consumption)/3600) as hours, min(window_start) as first_window_start, max(window_stop) as last_window_stop FROM accounting WHERE window_stop > $start AND window_start < $stop $user_query $property GROUP BY accounting_user,consumption_type ORDER BY seconds "); $sth->execute(); my $results; while (my @r = $sth->fetchrow_array()) { $results->{$r[0]}->{$r[1]} = $r[2]; $results->{$r[0]}->{begin} = $r[4]; $results->{$r[0]}->{end} = $r[5]; } $sth->finish(); return($results); } # Get an array of consumptions by project for a given user # params: base, start date, ending date, user sub get_accounting_summary_byproject($$$$){ my $dbh = shift; my $start = shift; my $stop = shift; my $user = shift; my $user_query=""; if ("$user" ne "") { $user_query="AND accounting_user = ". $dbh->quote($user); } my $sth = $dbh->prepare(" SELECT accounting_user as user, consumption_type, sum(consumption) as seconds, accounting_project as project FROM accounting WHERE window_stop > $start AND window_start < $stop $user_query GROUP BY accounting_user,project,consumption_type ORDER BY project,consumption_type,seconds "); $sth->execute(); my $results; while (my @r = $sth->fetchrow_array()) { $results->{$r[3]}->{$r[1]}->{$r[0]} = $r[2]; } $sth->finish(); return($results); } # Empty the table accounting and update the jobs table sub delete_all_from_accounting($){ my $dbh = shift; $dbh->do("DELETE FROM accounting"); $dbh->do("UPDATE jobs SET accounted = 'NO'"); } # Remove windows from accounting sub delete_accounting_windows_before($$){ my $dbh = shift; my $duration = shift; $dbh->do("DELETE FROM accounting WHERE window_stop <= $duration"); } # Get the last project Karma of user at a given date # params: base,user,project,date sub get_last_project_karma($$$$) { my $dbh = shift; my $user = $dbh->quote(shift); my $project = $dbh->quote(shift); my $date = shift; my $sth = $dbh->prepare(" SELECT message,project,start_time FROM jobs WHERE job_user = $user AND message like \'Karma = %\' AND project = $project AND start_time < $date ORDER BY start_time desc LIMIT 1 "); $sth->execute(); return($sth->fetchrow_array()); } #EVENTS LOG MANAGEMENT #add a new entry in event_log table #args : database ref, event type, job_id , description sub add_new_event($$$$){ my $dbh = shift; my $type = shift; my $job_id = shift; my $description = substr(shift,0,254); $description = $dbh->quote($description); my $date = get_date($dbh); $dbh->do("INSERT INTO event_logs (type,job_id,date,description) VALUES (\'$type\',$job_id,\'$date\',$description)"); } #add a new entry in event_log_hosts table #args : database ref, type, job id, description, ref of an array of resource ids sub add_new_event_with_host($$$$$){ my $dbh = shift; my $type = shift; my $idJob = shift; my $description = substr(shift,0,254); my $hostnames = shift; my $date = get_date($dbh); #lock_table($dbh,["event_logs"]); $dbh->do(" INSERT INTO event_logs (type,job_id,date,description) VALUES (\'$type\',$idJob,\'$date\',\'$description\') "); my $event_id = get_last_insert_id($dbh,"event_logs_event_id_seq"); #unlock_table($dbh); my %tmp; foreach my $n (@{$hostnames}){ if (!defined($tmp{$n})){ $dbh->do(" INSERT INTO event_log_hostnames (event_id,hostname) VALUES ($event_id,\'$n\') "); $tmp{$n} = 1; } } } # Turn the field toCheck into NO #args : database ref, event type, job_id sub check_event($$$){ my $dbh = shift; my $type = shift; my $idJob = shift; $dbh->do(" UPDATE event_logs SET to_check = \'NO\' WHERE to_check = \'YES\' AND type = \'$type\' AND job_id = $idJob "); } # Get all events with toCheck field on YES # args: database ref sub get_to_check_events($){ my $dbh = shift; my $sth = $dbh->prepare(" SELECT type, job_id, event_id, description FROM event_logs WHERE to_check = \'YES\' ORDER BY event_id "); $sth->execute(); my @results; while (my $ref = $sth->fetchrow_hashref()) { push(@results, $ref); } $sth->finish(); return(@results); } # Get hostnames corresponding to an event Id # args: database ref, event id sub get_hostname_event($$){ my $dbh = shift; my $eventId = shift; my $sth = $dbh->prepare(" SELECT hostname FROM event_log_hostnames WHERE event_id = $eventId "); $sth->execute(); my @results; while (my $ref = $sth->fetchrow_hashref()) { push(@results, $ref->{hostname}); } $sth->finish(); return(@results); } # Get events for the hostname given as parameter # If date is given, returns events since that date, else return the 30 last events. # args: database ref, network_address, date sub get_events_for_hostname($$$){ my $dbh = shift; my $host = shift; my $date = shift; my $sth; if ($date eq "") { $sth = $dbh->prepare("SELECT * FROM event_log_hostnames, event_logs WHERE event_log_hostnames.event_id = event_logs.event_id AND event_log_hostnames.hostname = '$host' ORDER BY event_logs.date DESC LIMIT 30"); } else { $sth = $dbh->prepare("SELECT * FROM event_log_hostnames, event_logs WHERE event_log_hostnames.event_id = event_logs.event_id AND event_log_hostnames.hostname = '$host' AND event_logs.date >= " . sql_to_local($date) . " ORDER BY event_logs.date DESC"); } $sth->execute(); my @results; while (my $ref = $sth->fetchrow_hashref()) { unshift(@results, $ref); } $sth->finish(); return(@results); } # Get the last event for the given type # args: database ref, event type # returns: the requested event sub get_last_event_from_type($$){ my $dbh = shift; my $type = shift; my $sth = $dbh->prepare("SELECT * FROM event_logs WHERE type = '$type' ORDER BY event_id DESC LIMIT 1"); $sth->execute(); my $ref = $sth->fetchrow_hashref(); $sth->finish(); return($ref); } # Get events for the specified job # args: database ref, job id sub get_job_events($$){ my $dbh =shift; my $jobId = shift; my $sth = $dbh->prepare(" SELECT * FROM event_logs WHERE job_id = $jobId "); $sth->execute(); my @results; while (my $ref = $sth->fetchrow_hashref()) { push(@results, $ref); } $sth->finish(); return(@results); } sub is_an_event_exists($$$){ my $dbh =shift; my $jobId = shift; my $event = shift; my $sth = $dbh->prepare(" SELECT COUNT(*) FROM event_logs WHERE job_id = $jobId AND type = \'$event\' LIMIT 1 "); $sth->execute(); my @r = $sth->fetchrow_array(); $sth->finish(); return($r[0]); } # LOCK FUNCTIONS: # get_lock # lock a mysql mutex variable # parameters : base, mutex, timeout # return value : 1 if the lock was obtained successfully, 0 if the attempt timed out or undef if an error occurred # side effects : a second get_lock of the same mutex will be blocked until release_lock is called on the mutex sub get_lock($$$) { my $dbh = shift; my $mutex = shift; my $timeout = shift; if ($Db_type eq "Pg"){ #$dbh->begin_work(); #Cannot find the GET_LOCK function into postgres... return 1; }else{ my $sth = $dbh->prepare("SELECT GET_LOCK(\"$mutex\",$timeout)"); $sth->execute(); my ($res) = $sth->fetchrow_array(); $sth->finish(); if ($res eq "0") { return 0; } elsif ($res eq "1") { return 1; } } return undef; } # release_lock # unlock a mysql mutex variable # parameters : base, mutex # return value : 1 if the lock was released, 0 if the lock wasn't locked by this thread , and NULL if the named lock didn't exist # side effects : unlock the mutex, a blocked get_lock may be unblocked sub release_lock($$) { my $dbh = shift; my $mutex = shift; if ($Db_type eq "Pg"){ #$dbh->commit(); return 1; }else{ my $sth = $dbh->prepare("SELECT RELEASE_LOCK(\"$mutex\")"); $sth->execute(); my ($res) = $sth->fetchrow_array(); $sth->finish(); if ($res eq "0") { return 0; } elsif ($res eq "1") { return 1; } } return undef; } sub lock_table($$){ my $dbh = shift; my $tables= shift; if ($Db_type eq "Pg"){ $dbh->begin_work(); }else{ my $str = "LOCK TABLE "; foreach my $t (@{$tables}){ $str .= "$t WRITE,"; } chop($str); $dbh->do($str); } } sub unlock_table($){ my $dbh = shift; if ($Db_type eq "Pg"){ $dbh->commit(); }else{ $dbh->do("UNLOCK TABLE"); } } # check_end_job($$$){ sub check_end_of_job($$$$$$$$$$){ my $base = shift; my $Jid = shift; my $exit_script_value = shift; my $error = shift; my $hosts = shift; my $remote_host = shift; my $remote_port = shift; my $user = shift; my $launchingDirectory = shift; my $server_epilogue_script = shift; #lock_table($base,["jobs","job_state_logs","resources","assigned_resources","resource_state_logs","event_logs","challenges","moldable_job_descriptions","job_types","job_dependencies","job_resource_groups","job_resource_descriptions"]); lock_table($base,["jobs","job_state_logs","assigned_resources"]); my $refJob = get_job($base,$Jid); if (($refJob->{'state'} eq "Running") or ($refJob->{'state'} eq "Launching") or ($refJob->{'state'} eq "Suspended") or ($refJob->{'state'} eq "Resuming")){ OAR::Modules::Judas::oar_debug("[bipbip $Jid] Job $Jid is ended\n"); set_finish_date($base,$Jid); set_job_state($base,$Jid,"Finishing"); set_job_exit_code($base,$Jid,$exit_script_value) if ($exit_script_value =~ /^\d+$/); unlock_table($base); my @events; if($error == 0){ OAR::Modules::Judas::oar_debug("[bipbip $Jid] User Launch completed OK\n"); push(@events, {type => "SWITCH_INTO_TERMINATE_STATE", string => "[bipbip $Jid] Ask to change the job state"}); job_finishing_sequence($base,$server_epilogue_script,$remote_host,$remote_port,$Jid,\@events); OAR::Tools::notify_tcp_socket($remote_host,$remote_port,"Term"); }elsif ($error == 1){ #Prologue error my $strWARN = "[bipbip $Jid] error of oarexec prologue"; push(@events, {type => "PROLOGUE_ERROR", string => $strWARN}); job_finishing_sequence($base,$server_epilogue_script,$remote_host,$remote_port,$Jid,\@events); }elsif ($error == 2){ #Epilogue error my $strWARN = "[bipbip $Jid] error of oarexec epilogue (jobId = $Jid)"; push(@events, {type => "EPILOGUE_ERROR", string => $strWARN}); job_finishing_sequence($base,$server_epilogue_script,$remote_host,$remote_port,$Jid,\@events); }elsif ($error == 3){ #Oarexec is killed by Leon normaly push(@events, {type => "SWITCH_INTO_ERROR_STATE", string => "[bipbip $Jid] Ask to change the job state"}); my $strWARN = "[bipbip $Jid] the job $Jid was killed by Leon"; OAR::Modules::Judas::oar_debug("$strWARN\n"); my $types = OAR::IO::get_current_job_types($base,$Jid); if ((defined($types->{besteffort})) and (defined($types->{idempotent}))){ if (OAR::IO::is_an_event_exists($base,$Jid,"BESTEFFORT_KILL") > 0){ my $new_job_id = OAR::IO::resubmit_job($base,$Jid); oar_warn("[bipbip] We resubmit the job $Jid (new id = $new_job_id) because it is a besteffort and idempotent job.\n"); push(@events, {type => "RESUBMIT_JOB_AUTOMATICALLY", string => "[bipbip $Jid] the job $Jid is a besteffort and idempotent job so we resubmit it (new id = $new_job_id)"}); } } job_finishing_sequence($base,$server_epilogue_script,$remote_host,$remote_port,$Jid,\@events); }elsif ($error == 4){ #Oarexec was killed by Leon and epilogue of oarexec is in error push(@events, {type => "SWITCH_INTO_ERROR_STATE", string => "[bipbip $Jid] Ask to change the job state"}); my $strWARN = "[bipbip $Jid] The job $Jid was killed by Leon and oarexec epilogue was in error"; my $types = OAR::IO::get_current_job_types($base,$Jid); if ((defined($types->{besteffort})) and (defined($types->{idempotent}))){ if (OAR::IO::is_an_event_exists($base,$Jid,"BESTEFFORT_KILL") > 0){ my $new_job_id = OAR::IO::resubmit_job($base,$Jid); oar_warn("[bipbip] We resubmit the job $Jid (new id = $new_job_id) because it is a besteffort and idempotent job.\n"); push(@events, {type => "RESUBMIT_JOB_AUTOMATICALLY", string => "[bipbip $Jid] The job $Jid is a besteffort and idempotent job so we resubmit it (new id = $new_job_id)"}); } } push(@events, {type => "EPILOGUE_ERROR", string => $strWARN}); job_finishing_sequence($base,$server_epilogue_script,$remote_host,$remote_port,$Jid,\@events); }elsif ($error == 5){ #Oarexec is not able to write in the node file my $strWARN = "[bipbip $Jid] oarexec cannot create the node file"; push(@events, {type => "CANNOT_WRITE_NODE_FILE", string => $strWARN}); job_finishing_sequence($base,$server_epilogue_script,$remote_host,$remote_port,$Jid,\@events); }elsif ($error == 6){ #Oarexec can not write its pid file my $strWARN = "[bipbip $Jid] oarexec cannot write its pid file"; push(@events, {type => "CANNOT_WRITE_PID_FILE", string => $strWARN}); job_finishing_sequence($base,$server_epilogue_script,$remote_host,$remote_port,$Jid,\@events); }elsif ($error == 7){ #Can t get shell of user my $strWARN = "[bipbip $Jid] Cannot get shell of user $user, so I suspect node $hosts->[0]"; push(@events, {type => "USER_SHELL", string => $strWARN}); job_finishing_sequence($base,$server_epilogue_script,$remote_host,$remote_port,$Jid,\@events); }elsif ($error == 8){ #Oarexec can not create tmp directory my $strWARN = "[bipbip $Jid] oarexec cannot create tmp directory on $hosts->[0] : ".OAR::Tools::get_default_oarexec_directory(); push(@events, {type => "CANNOT_CREATE_TMP_DIRECTORY", string => $strWARN}); job_finishing_sequence($base,$server_epilogue_script,$remote_host,$remote_port,$Jid,\@events); }elsif ($error == 10){ #oarexecuser.sh can not go into working directory push(@events, {type => "SWITCH_INTO_ERROR_STATE", string => "[bipbip $Jid] Ask to change the job state"}); my $strWARN = "[bipbip $Jid] Cannot go into the working directory $launchingDirectory of the job on node $hosts->[0]"; push(@events, {type => "WORKING_DIRECTORY", string => $strWARN}); job_finishing_sequence($base,$server_epilogue_script,$remote_host,$remote_port,$Jid,\@events); }elsif ($error == 20){ #oarexecuser.sh can not write stdout and stderr files push(@events, {type => "SWITCH_INTO_ERROR_STATE", string => "[bipbip $Jid] Ask to change the job state"}); my $strWARN = "[bipbip $Jid] Cannot create .stdout and .stderr files in $launchingDirectory on the node $hosts->[0]"; push(@events, {type => "OUTPUT_FILES", string => $strWARN}); job_finishing_sequence($base,$server_epilogue_script,$remote_host,$remote_port,$Jid,\@events); }elsif ($error == 12){ #oarexecuser.sh can not go into working directory and epilogue is in error push(@events, {type => "SWITCH_INTO_ERROR_STATE", string => "[bipbip $Jid] Ask to change the job state"}); my $strWARN = "[bipbip $Jid] Cannot go into the working directory $launchingDirectory of the job on node $hosts->[0] AND epilogue is in error"; oar_warn("$strWARN\n"); push(@events, {type => "WORKING_DIRECTORY", string => $strWARN}); push(@events, {type => "EPILOGUE_ERROR", string => $strWARN}); job_finishing_sequence($base,$server_epilogue_script,$remote_host,$remote_port,$Jid,\@events); }elsif ($error == 22){ #oarexecuser.sh can not create STDOUT and STDERR files and epilogue is in error push(@events, {type => "SWITCH_INTO_ERROR_STATE", string => "[bipbip $Jid] Ask to change the job state"}); my $strWARN = "[bipbip $Jid] Cannot create STDOUT and STDERR files AND epilogue is in error"; oar_warn("$strWARN\n"); push(@events, {type => "OUTPUT_FILES", string => $strWARN}); push(@events, {type => "EPILOGUE_ERROR", string => $strWARN}); job_finishing_sequence($base,$server_epilogue_script,$remote_host,$remote_port,$Jid,\@events); }elsif ($error == 30){ #oarexec timeout on bipbip hashtable transfer via SSH my $strWARN = "[bipbip $Jid] Timeout SSH hashtable transfer on $hosts->[0]"; push(@events, {type => "SSH_TRANSFER_TIMEOUT", string => $strWARN}); job_finishing_sequence($base,$server_epilogue_script,$remote_host,$remote_port,$Jid,\@events); }elsif ($error == 31){ #oarexec got a bad hashtable dump from bipbip my $strWARN = "[bipbip $Jid] Bad hashtable dump on $hosts->[0]"; push(@events, {type => "BAD_HASHTABLE_DUMP", string => $strWARN}); job_finishing_sequence($base,$server_epilogue_script,$remote_host,$remote_port,$Jid,\@events); }elsif ($error == 33){ #oarexec received a SIGUSR1 signal and there was an epilogue error push(@events, {type => "SWITCH_INTO_TERMINATE_STATE", string => "[bipbip $Jid] Ask to change the job state"}); my $strWARN = "[bipbip $Jid] oarexec received a SIGUSR1 signal and there was an epilogue error"; #add_new_event($base,"STOP_SIGNAL_RECEIVED",$Jid,"$strWARN"); push(@events, {type => "EPILOGUE_ERROR", string => $strWARN}); job_finishing_sequence($base,$server_epilogue_script,$remote_host,$remote_port,$Jid,\@events); }elsif ($error == 34){ #oarexec received a SIGUSR1 signal push(@events, {type => "SWITCH_INTO_TERMINATE_STATE", string => "[bipbip $Jid] Ask to change the job state"}); my $strWARN = "[bipbip $Jid] oarexec received a SIGUSR1 signal; so INTERACTIVE job is ended"; OAR::Modules::Judas::oar_debug("$strWARN\n"); #add_new_event($base,"STOP_SIGNAL_RECEIVED",$Jid,"$strWARN"); job_finishing_sequence($base,$server_epilogue_script,$remote_host,$remote_port,$Jid,\@events); OAR::Tools::notify_tcp_socket($remote_host,$remote_port,"Term"); }elsif ($error == 50){ # launching oarexec timeout my $strWARN = "[bipbip $Jid] launching oarexec timeout, exit value = $error; the job $Jid is in Error and the node $hosts->[0] is Suspected"; push(@events, {type => "LAUNCHING_OAREXEC_TIMEOUT", string => $strWARN}); job_finishing_sequence($base,$server_epilogue_script,$remote_host,$remote_port,$Jid,\@events); }elsif ($error == 40){ #oarexec received a SIGUSR2 signal push(@events, {type => "SWITCH_INTO_TERMINATE_STATE", string => "[bipbip $Jid] Ask to change the job state"}); my $strWARN = "[bipbip $Jid] oarexec received a SIGUSR2 signal; so user process has received a checkpoint signal"; OAR::Modules::Judas::oar_debug("$strWARN\n"); # my $types = OAR::IO::get_current_job_types($base,$Jid); # if ((defined($types->{idempotent})) and ($exit_script_value =~ /^\d+$/)){ # if ($exit_script_value == 0){ # my $new_job_id = OAR::IO::resubmit_job($base,$Jid); # oar_warn("[bipbip] We resubmit the job $Jid (new id = $new_job_id) because it was checkpointed and it is of the type 'idempotent'.\n"); # push(@events, {type => "RESUBMIT_JOB_AUTOMATICALLY", string => "[bipbip $Jid] The job $Jid was checkpointed and it is of the type 'idempotent' so we resubmit it (new id = $new_job_id)"}); # }else{ # oar_warn("[bipbip] We cannot resubmit the job $Jid even if it was checkpointed and of the type 'idempotent' because its exit code was not 0 ($exit_script_value).\n"); # push(@events, {type => "RESUBMIT_JOB_AUTOMATICALLY_CANCELLED", string => "The job $Jid was checkpointed and it is of the type 'idempotent' but its exit code is $exit_script_value"}); # } # } job_finishing_sequence($base,$server_epilogue_script,$remote_host,$remote_port,$Jid,\@events); OAR::Tools::notify_tcp_socket($remote_host,$remote_port,"Term"); }elsif ($error == 42){ #oarexec received a user signal push(@events, {type => "SWITCH_INTO_TERMINATE_STATE", string => "[bipbip $Jid] Ask to change the job state"}); my $strWARN = "[bipbip $Jid] oarexec received a SIGURG signal; so user process has received the user defined signal"; OAR::Modules::Judas::oar_debug("$strWARN\n"); # my $types = OAR::IO::get_current_job_types($base,$Jid); # if ((defined($types->{idempotent})) and ($exit_script_value =~ /^\d+$/)){ # if ($exit_script_value == 0){ # my $new_job_id = OAR::IO::resubmit_job($base,$Jid); # oar_warn("[bipbip] We resubmit the job $Jid (new id = $new_job_id) because it was signaled and it is of the type 'idempotent'.\n"); # push(@events, {type => "RESUBMIT_JOB_AUTOMATICALLY", string => "[bipbip $Jid] The job $Jid was signaled and it is of the type 'idempotent' so we resubmit it (new id = $new_job_id)"}); # }else{ # oar_warn("[bipbip] We cannot resubmit the job $Jid even if it was signaled and of the type 'idempotent' because its exit code was not 0 ($exit_script_value).\n"); # push(@events, {type => "RESUBMIT_JOB_AUTOMATICALLY_CANCELLED", string => "The job $Jid was signaled and it is of the type 'idempotent' but its exit code is $exit_script_value"}); # } # } job_finishing_sequence($base,$server_epilogue_script,$remote_host,$remote_port,$Jid,\@events); OAR::Tools::notify_tcp_socket($remote_host,$remote_port,"Term"); }elsif ($error == 41){ #oarexec received a SIGUSR2 signal push(@events, {type => "SWITCH_INTO_TERMINATE_STATE", string => "[bipbip $Jid] Ask to change the job state"}); my $strWARN = "[bipbip $Jid] oarexec received a SIGUSR2 signal and there was an epilogue error; so user process has received a checkpoint signal"; OAR::Modules::Judas::oar_debug("$strWARN\n"); # my $types = OAR::IO::get_current_job_types($base,$Jid); # if ((defined($types->{idempotent})) and ($exit_script_value =~ /^\d+$/)){ # if ($exit_script_value == 0){ # my $new_job_id = OAR::IO::resubmit_job($base,$Jid); # oar_warn("[bipbip] We resubmit the job $Jid (new id = $new_job_id) because it was checkpointed and it is of the type 'idempotent'.\n"); # push(@events, {type => "RESUBMIT_JOB_AUTOMATICALLY", string => "[bipbip $Jid] The job $Jid was checkpointed and it is of the type 'idempotent' so we resubmit it (new id = $new_job_id)"}); # }else{ # oar_warn("[bipbip] We cannot resubmit the job $Jid even if it was checkpointed and of the type 'idempotent' because its exit code was not 0 ($exit_script_value).\n"); # push(@events, {type => "RESUBMIT_JOB_AUTOMATICALLY_CANCELLED", string => "[bipbip $Jid] The job $Jid was checkpointed and it is of the type 'idempotent' but its exit code is $exit_script_value"}); # } # } push(@events, {type => "EPILOGUE_ERROR", string => $strWARN}); job_finishing_sequence($base,$server_epilogue_script,$remote_host,$remote_port,$Jid,\@events); OAR::Tools::notify_tcp_socket($remote_host,$remote_port,"Term"); }else{ my $strWARN = "[bipbip $Jid] error of oarexec, exit value = $error; the job $Jid is in Error and the node $hosts->[0] is Suspected; If this job is of type cosystem or deploy, check if the oar server is able to connect to the corresponding nodes, oar-node started"; push(@events, {type => "EXIT_VALUE_OAREXEC", string => $strWARN}); job_finishing_sequence($base,$server_epilogue_script,$remote_host,$remote_port,$Jid,\@events); } }else{ OAR::Modules::Judas::oar_debug("[bipbip $Jid] I was previously killed or Terminated but I did not know that!!\n"); unlock_table($base); } OAR::Tools::notify_tcp_socket($remote_host,$remote_port,"BipBip"); } sub job_finishing_sequence($$$$$$){ my ($dbh, $epilogue_script, $almighty_host, $almighty_port, $job_id, $events) = @_; if (defined($epilogue_script)){ # launch server epilogue my $cmd = "$epilogue_script $job_id"; OAR::Modules::Judas::oar_debug("[JOB FINISHING SEQUENCE] Launching command : $cmd\n"); my $pid; my $exit_value; my $signal_num; my $dumped_core; my $timeout = OAR::Tools::get_default_server_prologue_epilogue_timeout(); if (is_conf("PROLOGUE_EPILOGUE_TIMEOUT")){ $timeout = get_conf("SERVER_PROLOGUE_EPILOGUE_TIMEOUT"); } eval{ $SIG{PIPE} = 'IGNORE'; $SIG{ALRM} = sub { die "alarm\n" }; alarm($timeout); $pid = fork(); if ($pid == 0){ undef($dbh); exec($cmd); warn("[ERROR] Cannot find $cmd\n"); exit(-1); } my $wait_res = 0; # Avaoid to be disrupted by a signal while ($wait_res != $pid){ $wait_res = waitpid($pid,0); } alarm(0); $exit_value = $? >> 8; $signal_num = $? & 127; $dumped_core = $? & 128; }; if ($@){ if ($@ eq "alarm\n"){ if (defined($pid)){ my ($children,$cmd_name) = OAR::Tools::get_one_process_children($pid); kill(9,@{$children}); } my $str = "[JOB FINISHING SEQUENCE] Server epilogue timeouted (cmd : $cmd)"; oar_error("$str\n"); push(@{$events}, {type => "SERVER_EPILOGUE_TIMEOUT", string => $str}); } }elsif ($exit_value != 0){ my $str = "[JOB FINISHING SEQUENCE] Server epilogue exit code $exit_value (!=0) (cmd : $cmd)"; oar_error("$str\n"); push(@{$events}, {type => "SERVER_EPILOGUE_EXIT_CODE_ERROR", string => $str}); } } my $types = OAR::IO::get_current_job_types($dbh,$job_id); if ((!defined($types->{deploy})) and (!defined($types->{cosystem}))){ ############### # CPUSET PART # ############### # Clean all CPUSETs if needed my $cpuset_field = get_conf("JOB_RESOURCE_MANAGER_PROPERTY_DB_FIELD"); if (defined($cpuset_field)){ my $cpuset_name = OAR::IO::get_job_cpuset_name($dbh, $job_id); my $openssh_cmd = get_conf("OPENSSH_CMD"); $openssh_cmd = OAR::Tools::get_default_openssh_cmd() if (!defined($openssh_cmd)); if (is_conf("OAR_SSH_CONNECTION_TIMEOUT")){ OAR::Tools::set_ssh_timeout(get_conf("OAR_SSH_CONNECTION_TIMEOUT")); } my $cpuset_file = get_conf("JOB_RESOURCE_MANAGER_FILE"); $cpuset_file = OAR::Tools::get_default_cpuset_file() if (!defined($cpuset_file)); $cpuset_file = "$ENV{OARDIR}/$cpuset_file" if ($cpuset_file !~ /^\//); my $cpuset_path = get_conf("CPUSET_PATH"); my $cpuset_full_path; if (defined($cpuset_path) and defined($cpuset_field)){ $cpuset_full_path = $cpuset_path.'/'.$cpuset_name; } my $job_uid_resource_type = get_conf("JOB_RESOURCE_MANAGER_JOB_UID_TYPE"); my $job = get_job($dbh, $job_id); my $cpuset_nodes = OAR::IO::get_cpuset_values_for_a_moldable_job($dbh,$cpuset_field,$job->{assigned_moldable_job}); if (defined($cpuset_nodes) and (keys(%{$cpuset_nodes}) > 0)){ OAR::Modules::Judas::oar_debug("[JOB FINISHING SEQUENCE] [CPUSET] [$job_id] Clean cpuset on each nodes\n"); my $taktuk_cmd = get_conf("TAKTUK_CMD"); my $job_cpuset_uid; $job_cpuset_uid = OAR::IO::get_job_cpuset_uid($dbh, $job->{assigned_moldable_job}, $job_uid_resource_type, $cpuset_field) if (defined($job_uid_resource_type)); my $job_user = OAR::Tools::format_job_user($job->{job_user},$job_id,$job_cpuset_uid); my ($job_challenge,$ssh_private_key,$ssh_public_key) = OAR::IO::get_job_challenge($dbh,$job_id); $ssh_public_key = OAR::Tools::format_ssh_pub_key($ssh_public_key,$cpuset_full_path,$job->{job_user},$job_user); my $cpuset_data_hash = { job_id => $job_id, name => $cpuset_name, nodes => $cpuset_nodes, cpuset_path => $cpuset_path, ssh_keys => { public => { file_name => OAR::Tools::get_default_oar_ssh_authorized_keys_file(), key => $ssh_public_key }, private => { file_name => OAR::Tools::get_private_ssh_key_file_name($cpuset_name), key => $ssh_private_key } }, oar_tmp_directory => OAR::Tools::get_default_oarexec_directory(), user => $job->{job_user}, job_user => $job_user, job_uid => $job_cpuset_uid, types => $types, log_level => OAR::Modules::Judas::get_log_level() }; my ($tag,@bad) = OAR::Tools::manage_remote_commands([keys(%{$cpuset_nodes})],$cpuset_data_hash,$cpuset_file,"clean",$openssh_cmd,$taktuk_cmd,$dbh); if ($tag == 0){ my $str = "[JOB FINISHING SEQUENCE] [CPUSET] [$job_id] Bad cpuset file : $cpuset_file\n"; oar_error($str); push(@{$events}, {type => "CPUSET_MANAGER_FILE", string => $str}); }elsif ($#bad >= 0){ oar_error("[job_finishing_sequence] [$job_id] Cpuset error and register event CPUSET_CLEAN_ERROR on nodes : @bad\n"); push(@{$events}, {type => "CPUSET_CLEAN_ERROR", string => "[job_finishing_sequence] OAR suspects nodes for the job $job_id : @bad", hosts => \@bad}); } } } #################### # CPUSET PART, END # #################### } # Execute PING_CHECKER if asked if ((is_conf("ACTIVATE_PINGCHECKER_AT_JOB_END")) and (lc(get_conf("ACTIVATE_PINGCHECKER_AT_JOB_END")) eq "yes")){ my @hosts = OAR::IO::get_job_current_hostnames($dbh,$job_id); oar_debug("[job_finishing_sequence] [$job_id] Run pingchecker to test nodes at the end of the job on nodes: @hosts\n"); my @bad_pingchecker = OAR::PingChecker::test_hosts(@hosts); if ($#bad_pingchecker >= 0){ oar_error("[job_finishing_sequence] [$job_id] PING_CHECKER_NODE_SUSPECTED OAR suspects nodes for the job $job_id : @bad_pingchecker\n"); push(@{$events}, {type => "PING_CHECKER_NODE_SUSPECTED", string => "[job_finishing_sequence] OAR suspects nodes for the job $job_id : @bad_pingchecker", hosts => \@bad_pingchecker}); } } # foreach my $e (@{$events}){ OAR::Modules::Judas::oar_debug("$e->{string}\n"); if (defined($e->{hosts})){ add_new_event_with_host($dbh,$e->{type},$job_id,$e->{string},$e->{hosts}); }else{ add_new_event($dbh,$e->{type},$job_id,$e->{string}); } } # Just to force commit lock_table($dbh,["accounting"]); unlock_table($dbh); OAR::Tools::notify_tcp_socket($almighty_host,$almighty_port,"ChState") if ($#{$events} >= 0); } # Generic count of a select query # args: database ref, query sub sql_count($$){ my $dbh = shift; my $query = shift; my $sth = $dbh->prepare(" SELECT count(*) $query"); $sth->execute(); my ($count) = $sth->fetchrow_array(); return $count ; } # Generic select query # args: database ref, query, limit and offset sub sql_select($$$$){ my $dbh = shift; my $query = shift; my $limit = shift; my $offset = shift; if ($offset != 0) {$offset = "OFFSET $offset";} else {$offset = ""}; my $sth = $dbh->prepare("SELECT * $query LIMIT $limit $offset"); $sth->execute(); my @res = (); while (my $ref = $sth->fetchrow_hashref()) { push(@res, $ref); } return(\@res); } # END OF THE MODULE return 1; ./oar-2.5.2/sources/core/common-libs/lib/OAR/Conf.pm0000644000175000017500000001075211757171206020032 0ustar plbplb############################################################################### ## *** ConfLib: *** ## ## - Description: ## Home brewed module managing configuration file for OAR ## ## - Usage: init_conf(); ## Read the first file matching in ## . current directory ## . $OARDIR directory ## . /etc directory ## ## - Configuration file format: ## A line of the configuration file looks like that: ## > truc = 45 machin chose bidule 23 # any comment ## "truc" is a configuration entry being assigned "45 machin chose bidule 23" ## Anything placed after a dash (#) is ignored i ## (for instance lines begining with a dash are comment lines then ignored) ## Any line not matching the regexp defined below are also ignored ## ## Module must be initialized using init_conf(), then ## any entry is retrieved using get_conf(). ## is_conf() may be used to check if any entry actually exists. ## ## - Example: ## > use ConfLib qw(init_conf get_conf is_conf); ## > init_conf("oar.conf"); ## > print "toto = ".get_conf("toto")."\n" if is_conf("toto"); ## ############################################################################### # $Id$ package OAR::Conf; use strict; use warnings; require Exporter; our (@ISA,@EXPORT,@EXPORT_OK); @ISA = qw(Exporter); @EXPORT_OK = qw(init_conf get_conf is_conf dump_conf get_conf_list reset_conf get_conf_with_default_param set_value); ## the configuration file. my $file = undef; ## parameters container... my %params; ## configuration file regexp (one line). my $regex = qr{^\s*([^#=\s]+)\s*=\s*([^#]*)}; ## Initialization of the configuration # param: configuration file pathname # Result: 0 if conf was already loaded # 1 if conf was actually loaded # 2 if conf was not found sub init_conf ($){ # If file already loaded, exit immediately (defined $file) and return 0; $file = shift; (defined $file) or return 2; unless ( -r $file ) { if ( defined $ENV{OARDIR} and -r $ENV{OARDIR}."/".$file ) { $file = $ENV{OARDIR}."/".$file; } elsif ( -r "/etc/".$file ) { $file = "/etc/".$file; } else { warn "Configuration file not found."; $file = undef; return 2; } } open CONF, $file or die "Open configuration file"; %params = (); foreach my $line () { if ($line =~ $regex) { my ($key,$val) = ($1,$2); $val =~ /^([\"\']?)(.+)\1\s*$/; $val = $2 if ($2 ne ""); $params{$key}=$val; } } close CONF; return 1; } ## retrieve a parameter if exists, set it to the default value otherwise ## params: arg1 param name, arg2 default value sub get_conf_with_default_param ( $$ ) { my $key = shift; my $default = shift; (defined $key) or die "missing a key!"; (defined $default) or die "missing a default value!"; if(exists $params{$key}){ return $params{$key}; } else {return $default;} } ## retrieve a parameter sub get_conf ( $ ) { my $key = shift; (defined $key) or die "missing a key!"; return $params{$key}; } ## return the list of configured parameters sub get_conf_list () { return (\%params); } ## check if a parameter is defined sub is_conf ( $ ) { my $key = shift; (defined $key) or die "missing a key!"; return exists $params{$key}; } ## debug: dump parameters sub dump_conf () { print "Config file is: ".$file."\n"; while (my ($key,$val) = each %params) { print " ".$key." = ".$val."\n"; } return 1; } ## reset the module state sub reset_conf () { $file = undef; %params = (); return 1; } ## set value to a parameter sub set_value ($$){ my $variable = shift; my $value = shift; my $new_file_content; open CONF, $file or die "Open configuration file"; %params = (); foreach my $line () { if ($line =~ $regex) { my ($key,$val) = ($1,$2); if ($key eq $variable) { $new_file_content .= $key."="."\"$value\""."\n"; } else { $new_file_content .= $key."=".$val; } } else { if (!defined($new_file_content)) { $new_file_content = $line; } else { $new_file_content.= $line; } } } close CONF; # writing data in the config file write_config($new_file_content); return 1; } ## write content in the config file sub write_config { my $config_content = shift; open NEW_CONF, ">$file" or die "Open configuration file"; print NEW_CONF $config_content; close NEW_CONF; } return 1; ./oar-2.5.2/sources/api/0000755000175000017500000000000011757171206013015 5ustar plbplb./oar-2.5.2/sources/api/oarapi_examples.txt0000644000175000017500000000627711757171206016743 0ustar plbplb### Examples using ruby restclient ### > gem install rest-client > export PATH=$PATH:/var/lib/gems/1.8/bin > restclient http://kameleon:kameleon@localhost/oarapi-priv # Getting resources infos # in JSON irb(main):004:0> puts get('/resources.json') # in YAML irb(main):005:0> puts get('/resources.yaml') # Same thing irb(main):050:0> puts get('/resources', :accept=>"text/yaml") # Specifying the "oar" data structure irb(main):050:0> puts get('/resources.json?structure=oar') # Specifying the "simple" data structure irb(main):050:0> puts get('/resources.json?structure=simple') # Details about a resource irb(main):008:0> puts get('/resources/1.yaml') # Details and resources of a node irb(main):007:0> puts get('/resources/nodes/liza-1.yaml') # Details of all the resources (expansion) irb(main):007:0> puts get('/resources/all.yaml') # Getting jobs infos irb(main):006:0> puts get('/jobs.yaml') irb(main):009:0> puts get('/jobs/12.yaml') # Submiting a job (using JSON format) irb(main):010:0> require 'json' irb(main):012:0> j={ 'resource' => '/nodes=2/cpu=1', 'command' => '/usr/bin/id' } irb(main):015:0> job=post('/jobs' , j.to_json , :content_type => 'application/json') # Getting details about the previously submited job irb(main):035:0> uri=JSON.parse(job)['links'].find { |l| l["rel"]=="self" }["href"] irb(main):035:0> puts get(uri+"yaml") # Submitting a job using JSON format, but requiring the result in YAML irb(main):037:0> job=post('/jobs.yaml' , j.to_json , :content_type => 'application/json') # Submitting a job with a provided inline script irb(main):024:0> script="#!/bin/bash irb(main):025:0" #OAR --name test irb(main):025:0" echo \"Hello world\" irb(main):026:0" whoami irb(main):027:0" sleep 300 irb(main):028:0" " irb(main):029:0> j={ 'resource' => '/nodes=2/cpu=1', 'script' => script , 'scanscript' ==> 1, 'workdir' => '~kameleon'} irb(main):030:0> job=post('/jobs' , j.to_json , :content_type => 'application/json') # Deleting a job irb(main):111:0> delete("/jobs/#{JSON.parse(job)['id']}.yaml") # Send the checkpoint signal to a job irb(main):102:0> puts post('/jobs/2911/checkpoints/new','',:content_type => "application/json") # Suspending/resuming a job irb(main):102:0> puts post('/jobs/2911/holds/new','',:content_type => "application/json") irb(main):102:0> puts post('/jobs/2911/resumptions/new','',:content_type => "application/json") # Adding new resources (oar user only) irb(main):078:0> r=[{ 'network_address' => 'test1', 'besteffort'=>'NO' , 'cpu' => '10' }, { 'network_address' => 'test2', 'besteffort'=>'NO' , 'cpu' => '11' }] irb(main):078:0> puts post('/resources', r.to_json , :content_type => 'application/json') # Changing the state of a resource irb(main):079:0> puts post('/resources/11/state','{"state":"Dead"}',:content_type => 'application/json') # Deleting a resource by id (oar user only) irb(main):079:0> puts delete('/resources/11.yaml') # Deleting a resource by node/cpuset (oar user only) irb(main):080:0> puts delete('/resources/test2-p/1') ### Example using curl ### curl -i -X POST http://www/oarapi/jobs.json -H'Content-Type: application/json' -d '{"resource":"/nodes=1,walltime=00:10:00", "script_path":"\"sleep 600\"", "type":"inner=986078"}' ./oar-2.5.2/sources/api/oarapi.pl0000755000175000017500000022164611757171206014643 0ustar plbplb#!/usr/bin/perl -w # vim: set foldmethod=marker:syntax: # # # This is the main cgi script for the OAR REST API # This script is part of the OAR project. # # Please, make a good usage of folding markers: {{{ and }}} # into comments. Under vim, use "za" to fold and "zo" to # unfold (or just go inside the title to automatically unfold) # You have to ":set modeline" into vim for the above modeline # to be interpreted by vim or just type the :set command given # by this modeline. # # Copyright (C) 2009-2010 Bruno.Bzeznik@imag.fr # # 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 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 # use strict; use DBI(); use OAR::API; use OAR::Conf qw(init_conf dump_conf get_conf_list get_conf is_conf set_value); use OAR::IO; use OAR::Stat; use OAR::Nodes; use OAR::Tools; use OAR::Version; use POSIX; use JSON; use IO::Handle; use File::Temp qw/ tempfile /; use File::Basename; use CGI::Fast qw/ standard /; #use Data::Dumper; my $VERSION="1.0.1alpha1"; ############################################################################## # CONFIGURATION AND INITIALIZATION STUFF ############################################################################## # Load config my $oardir; if (defined($ENV{OARDIR})){ $oardir = $ENV{OARDIR}."/"; }else{ die("ERROR: OARDIR env variable must be defined.\n"); } if (defined($ENV{OARCONFFILE})){ init_conf($ENV{OARCONFFILE}); }else{ init_conf("/etc/oar/oar.conf"); } # Oar commands my $OARSUB_CMD = "oarsub"; my $OARDEL_CMD = "oardel"; my $OARHOLD_CMD = "oarhold"; my $OARRESUME_CMD = "oarresume"; my $OARADMIN_CMD = "oaradmin"; my $OARNODES_CMD = "oarnodes"; my $OARDODO_CMD = "$ENV{OARDIR}/oardodo/oardodo"; # OAR server my $remote_host = get_conf("SERVER_HOSTNAME"); my $remote_port = get_conf("SERVER_PORT"); my $stageout_dir = get_conf("STAGEOUT_DIR"); my $stagein_dir = get_conf("STAGEIN_DIR"); my $allow_create_node = get_conf("DESKTOP_COMPUTING_ALLOW_CREATE_NODE"); my $expiry = get_conf("DESKTOP_COMPUTING_EXPIRY"); # Enable this if you are ok with a simple pidentd "authentication" # Not very secure, but useful for testing (no need for login/password) # or in the case you fully trust the client hosts (with an apropriate # ip-based access control into apache for example) my $TRUST_IDENT = 1; if (is_conf("API_TRUST_IDENT")){ $TRUST_IDENT = get_conf("API_TRUST_IDENT"); } # Default data structure variant my $STRUCTURE="simple"; if (is_conf("API_DEFAULT_DATA_STRUCTURE")){ $STRUCTURE = get_conf("API_DEFAULT_DATA_STRUCTURE"); } my $DEFAULT_STRUCTURE=$STRUCTURE; # Get the default maximum number of items my $MAX_ITEMS=500; if (is_conf("API_DEFAULT_MAX_ITEMS_NUMBER")){ $MAX_ITEMS = get_conf("API_DEFAULT_MAX_ITEMS_NUMBER"); } # Relative/absolute uris config variable $OAR::API::ABSOLUTE_URIS=1; if (is_conf("API_ABSOLUTE_URIS")){ $OAR::API::ABSOLUTE_URIS=get_conf("API_ABSOLUTE_URIS"); } # TMP directory my $TMPDIR="/tmp"; if (defined($ENV{TMPDIR})) { $TMPDIR=$ENV{TMPDIR}; } # Load the html header file my $file; if (is_conf("API_HTML_HEADER")){ $file=get_conf("API_HTML_HEADER"); } else { $file="/etc/oar/api_html_header.pl"; } open(FILE,$file); my(@html_header_lines) = ; close(FILE); ############################################################################## # FastCGI loop starting ############################################################################## my $q; FCGI: while ($q = new CGI::Fast) { # Sets the cgi handler of the OAR::API (global variable) $OAR::API::q=$q; # Activate debug mode when the script name contains "debug" or when a # debug parameter is found. my $DEBUG_MODE=0; if ( $q->url(-relative=>1) =~ /.*debug.*/ ) { $DEBUG_MODE = 1; }; if ( defined( $q->param('debug') ) && $q->param('debug') eq "1" ) { $DEBUG_MODE = 1; } $OAR::API::DEBUG_MODE=$DEBUG_MODE; # Check a possible extension my $extension; if ( $q->path_info =~ /^$/ ) { $extension = "html"; } elsif ( $q->path_info =~ /.*\.(yaml|json|html|tgz|tar\.gz)$/ ) { $extension = $1; }; $extension=OAR::API::set_ext($q,$extension); $OAR::API::extension=$extension; # Base uri of the api my $apiuri=OAR::API::get_api_uri_relative_base(); $apiuri =~ s/\/$//; $OAR::API::apiuri=$apiuri; # Header for html version my $HTML_HEADER=""; eval join("\n",@html_header_lines); $OAR::API::HTML_HEADER=$HTML_HEADER; ############################################################################## # Authentication ############################################################################## my $authenticated_user = ""; if ( defined( $ENV{AUTHENTICATE_UID} ) && $ENV{AUTHENTICATE_UID} ne "" ) { $authenticated_user = $ENV{AUTHENTICATE_UID}; } else { if ( $TRUST_IDENT && defined( $q->http('X_REMOTE_IDENT') ) && $q->http('X_REMOTE_IDENT') ne "" && $q->http('X_REMOTE_IDENT') ne "unknown" && $q->http('X_REMOTE_IDENT') ne "(null)" ) { $authenticated_user = $q->http('X_REMOTE_IDENT'); } } ############################################################################## # Data structure variants ############################################################################## $STRUCTURE=$DEFAULT_STRUCTURE; if (defined $q->param('structure')) { $STRUCTURE=$q->param('structure'); } if ($STRUCTURE ne "oar" && $STRUCTURE ne "simple") { OAR::API::ERROR 406, "Unknown $STRUCTURE format", "Unknown $STRUCTURE format for data structure"; last FCGI; } ############################################################################## # URI management ############################################################################## SWITCH: for ($q) { my $URI; ########################################### # API informations ########################################### # #{{{ GET /: Root links # $URI = qr{^\/*\.*(yaml|json|html)*$}; OAR::API::GET( $_, $URI ) && do { $_->path_info =~ m/$URI/; my $ext = OAR::API::set_ext($q,$1); (my $header, my $type)=OAR::API::set_output_format($ext); my $root={ "oar_version" => OAR::Version::get_version(), "api_version" => $VERSION, "apilib_version" => OAR::API::get_version(), "api_timestamp" => time(), "api_timezone" => strftime("%Z", localtime()), "links" => [ { 'rel' => 'self' , 'href' => OAR::API::htmlize_uri(OAR::API::make_uri("./",$ext,0),$ext) }, { 'rel' => 'collection', 'href' => OAR::API::htmlize_uri(OAR::API::make_uri("resources",$ext,0),$ext), 'title' => 'resources' } , { 'rel' => 'collection', 'href' => OAR::API::htmlize_uri(OAR::API::make_uri("resources/full",$ext,0),$ext), 'title' => 'full_resources' } , { 'rel' => 'collection', 'href' => OAR::API::htmlize_uri(OAR::API::make_uri("jobs",$ext,0),$ext), 'title' => 'jobs' } , { 'rel' => 'collection', 'href' => OAR::API::htmlize_uri(OAR::API::make_uri("jobs/details",$ext,0),$ext), 'title' => 'detailed_jobs' } , { 'rel' => 'collection', 'href' => OAR::API::htmlize_uri(OAR::API::make_uri("jobs/table",$ext,0),$ext), 'title' => 'jobs_table' } , { 'rel' => 'collection', 'href' => OAR::API::htmlize_uri(OAR::API::make_uri("config",$ext,0),$ext), 'title' => 'config' } , { 'rel' => 'collection', 'href' => OAR::API::htmlize_uri(OAR::API::make_uri("admission_rules",$ext,0),$ext), 'title' => 'admission_rules' } ] }; print $header; print $HTML_HEADER if ($ext eq "html"); print OAR::API::export($root,$ext); last; }; #}}} # #{{{ GET /version : Version informations # $URI = qr{^/version\.*(yaml|json|html)*$}; OAR::API::GET( $_, $URI ) && do { $_->path_info =~ m/$URI/; my $ext = OAR::API::set_ext($q,$1); (my $header, my $type)=OAR::API::set_output_format($ext); my $version={ "oar_version" => OAR::Version::get_version(), "apilib_version" => OAR::API::get_version(), "api_timestamp" => time(), "api_timezone" => strftime("%Z", localtime()), "api_version" => $VERSION }; print $header; print $HTML_HEADER if ($ext eq "html"); print OAR::API::export($version,$ext); last; }; #}}} # #{{{ GET /timezone: Timezone information # $URI = qr{^/timezone\.*(yaml|json|html)*$}; OAR::API::GET( $_, $URI ) && do { $_->path_info =~ m/$URI/; my $ext = OAR::API::set_ext($q,$1); (my $header, my $type)=OAR::API::set_output_format($ext); my $version={ "api_timestamp" => time(), "timezone" => strftime("%Z", localtime()) }; print $header; print $HTML_HEADER if ($ext eq "html"); print OAR::API::export($version,$ext); last; }; #}}} ########################################### # Jobs ########################################### # #{{{ GET /jobs[/details|table]?state=,from=,to= : List of jobs # $URI = qr{^/jobs(/details|/table)*\.*(yaml|json|html)*$}; OAR::API::GET( $_, $URI ) && do { $_->path_info =~ m/$URI/; my $ext=OAR::API::set_ext($q,$2); my $header ; my $type; my $more_infos=$1; if (!defined($more_infos)) { ($header,$type)=OAR::API::set_output_format($ext,"GET, POST"); }else{ ($header,$type)=OAR::API::set_output_format($ext); } # Get the id of the user as more details may be obtained for her jobs if ( $authenticated_user =~ /(\w+)/ ) { $authenticated_user = $1; $ENV{OARDO_USER} = $authenticated_user; } OAR::Stat::open_db_connection or OAR::API::ERROR(500, "Cannot connect to the database", "Cannot connect to the database" ); # default parameters for the parameters my $JOBS_URI_DEFAULT_PARAMS = "state=Finishing,Running,Resuming,Suspended,Launching,toLaunch,Waiting,toAckReservation,Hold"; if (is_conf("API_JOBS_URI_DEFAULT_PARAMS")){ $JOBS_URI_DEFAULT_PARAMS = get_conf("API_JOBS_URI_DEFAULT_PARAMS"); } # query string parameters my $from = $q->param('from'); my $to = $q->param('to'); my $state = $q->param('state'); my $user = $q->param('user'); my $array = $q->param('array'); if (!defined($q->param('from')) && !defined($q->param('to')) && !defined($q->param('state')) && !defined($q->param('array'))) { my $param = qr{.*from=(.*?)(&|$)}; if ($JOBS_URI_DEFAULT_PARAMS =~ m/$param/) { $from = $1; } $param = qr{.*to=(.*?)(&|$)}; if ($JOBS_URI_DEFAULT_PARAMS =~ m/$param/) { $to = $1; } $param = qr{.*state=(.*?)(&|$)}; if ($JOBS_URI_DEFAULT_PARAMS =~ m/$param/) { $state = $1; } } if (!defined($array)) { $array=""; }; # GET max items from configuration parameter if (!defined($q->param('from')) && !defined($q->param('to')) && !defined($q->param('state')) && !defined($q->param('limit'))) { # get limit from defaut url my $param = qr{.*limit=(.*?)(&|$)}; if ($JOBS_URI_DEFAULT_PARAMS =~ m/$param/) { $MAX_ITEMS = $1; } } # GET max items from uri parameter if (defined($q->param('limit'))) { $MAX_ITEMS = $q->param('limit'); } # set offset / GET offset from uri parameter my $offset = 0; if (defined($q->param('offset'))) { $offset = $q->param('offset'); } # requested user jobs my $jobs = OAR::Stat::get_jobs_for_user_query($user,$from,$to,$state,$MAX_ITEMS,$offset,$array); my $total_jobs = OAR::Stat::count_jobs_for_user_query($user,$from,$to,$state,$array); if ( !defined $jobs || keys %$jobs == 0 ) { $jobs = OAR::API::struct_empty($STRUCTURE); } else { $jobs = OAR::API::struct_job_list_hash_to_array($jobs); OAR::API::add_joblist_uris($jobs,$ext); if (defined($more_infos)) { if ($more_infos eq "/details") { # will be useful for cigri and behaves as a oarstat -D # Warning: it's a kind of all in one query and may result in a lot of # SQL queries. Maybe to optimize... my $detailed_jobs; foreach my $j (@$jobs) { my $job_resources = OAR::Stat::get_job_resources($j); $j = OAR::Stat::get_job_data($j,1); my $resources = OAR::API::struct_job_resources($job_resources,$STRUCTURE); my $nodes= OAR::API::struct_job_nodes($job_resources,$STRUCTURE); OAR::API::add_resources_uris($resources,$ext,''); $j->{'resources'}=$resources; OAR::API::add_nodes_uris($nodes,$ext,''); $j->{'nodes'}=$nodes; $j = OAR::API::struct_job($j,$STRUCTURE); OAR::API::add_job_uris($j,$ext); push(@$detailed_jobs,$j); } $jobs = $detailed_jobs; } } else { $jobs = OAR::API::struct_job_list($jobs,$STRUCTURE); } } OAR::Stat::close_db_connection(); # add pagination informations $jobs = OAR::API::add_pagination($jobs,$total_jobs,$q->path_info,$q->query_string,$ext,$MAX_ITEMS,$offset,$STRUCTURE); print $header; print $HTML_HEADER if ($ext eq "html"); print OAR::API::export($jobs,$ext); last; }; #}}} # #{{{ GET /jobs/ : Details of a job # $URI = qr{^/jobs/(\d+)(\.yaml|\.json|\.html)*$}; OAR::API::GET( $_, $URI ) && do { $_->path_info =~ m/$URI/; my $jobid = $1; my $ext=OAR::API::set_ext($q,$2); (my $header, my $type)=OAR::API::set_output_format($ext,"GET, POST, DELETE"); # Must be authenticated if ( not $authenticated_user =~ /(\w+)/ ) { OAR::API::ERROR( 401, "Permission denied", "A suitable authentication must be done before looking at jobs" ); last; } $authenticated_user = $1; $ENV{OARDO_USER} = $authenticated_user; OAR::Stat::open_db_connection or OAR::API::ERROR(500, "Cannot connect to the database", "Cannot connect to the database" ); my $job = OAR::Stat::get_specific_jobs([$jobid]); if (@$job == 0 ) { OAR::API::ERROR( 404, "Job not found", "Job not found" ); last; } $job=OAR::Stat::get_job_data(@$job[0],1); my $result = OAR::API::struct_job($job,$STRUCTURE); OAR::API::add_job_uris($result,$ext); OAR::Stat::close_db_connection; print $header; if ($ext eq "html") { OAR::API::job_html_header($job); }; print OAR::API::export($result,$ext); last; }; #}}} # #{{{ GET /jobs//[resources|nodes] : Resources or nodes assigned or scheduled to a job # $URI = qr{^/jobs/(\d+)/(resources|nodes)(\.yaml|\.json|\.html)*$}; OAR::API::GET( $_, $URI ) && do { $_->path_info =~ m/$URI/; my $jobid = $1; my $item = $2; my $ext=OAR::API::set_ext($q,$3); (my $header, my $type)=OAR::API::set_output_format($ext); OAR::Stat::open_db_connection or OAR::API::ERROR(500, "Cannot connect to the database", "Cannot connect to the database" ); my $job = OAR::Stat::get_specific_jobs([$jobid]); my $resources=OAR::Stat::get_job_resources(@$job[0]); if ($item eq "resources") { $resources = OAR::API::struct_job_resources($resources,$STRUCTURE); OAR::API::add_resources_uris($resources,$ext,''); }else{ $resources = OAR::API::struct_job_nodes($resources,$STRUCTURE); OAR::API::add_nodes_uris($resources,$ext,''); } $resources = OAR::API::add_pagination($resources,@$resources,$q->path_info,undef,$ext,0,0,$STRUCTURE); print $header; print $HTML_HEADER if ($ext eq "html"); print OAR::API::export($resources,$ext); last; }; #}}} # #{{{ POST /jobs/[array/]/checkpoints|deletions|holds|rholds|resumptions|resubmissions/new : Actions on a job (checkpoint, hold, resume,...) # $URI = qr{^/jobs/(array/|)(\d+)/(checkpoints|deletions|holds|rholds|resumptions|resubmissions)+/new(\.yaml|\.json|\.html)*$}; OAR::API::POST( $_, $URI ) && do { $_->path_info =~ m/$URI/; my $array = $1; my $jobid = $2; my $action = $3; my $ext=OAR::API::set_ext($q,$4); (my $header, my $type)=OAR::API::set_output_format($ext,"GET, POST"); # Must be authenticated if ( not $authenticated_user =~ /(\w+)/ ) { OAR::API::ERROR( 401, "Permission denied", "A suitable authentication must be done before modifying jobs" ); last; } $authenticated_user = $1; $ENV{OARDO_BECOME_USER} = $authenticated_user; if ($array eq "array/") { $array="--array"; } # Delete (alternative way to DELETE request, for html forms) my $cmd; my $status; if ($action eq "deletions" ) { $cmd = "$OARDODO_CMD $OARDEL_CMD $array $jobid"; $status = "Delete request registered"; } # Checkpoint elsif ( $action eq "checkpoints" ) { $cmd = "$OARDODO_CMD $OARDEL_CMD $array -c $jobid"; $status = "Checkpoint request registered"; } # Hold elsif ( $action eq "holds" ) { $cmd = "$OARDODO_CMD $OARHOLD_CMD $array $jobid"; $status = "Hold request registered"; } # Hold a running job elsif ( $action eq "rholds" ) { $cmd = "$OARDODO_CMD $OARHOLD_CMD $array -r $jobid"; $status = "Hold request registered"; } # Resume elsif ( $action eq "resumptions" ) { $cmd = "$OARDODO_CMD $OARRESUME_CMD $array $jobid"; $status = "Resume request registered"; } # Resubmit elsif ( $action eq "resubmissions" ) { $cmd = "$OARDODO_CMD $OARSUB_CMD $array --resubmit $jobid"; $status = "Resubmit request registered"; } # Impossible to get here! else { OAR::API::ERROR(400,"Bad query","Could not understand ". $action ." method"); last; } my $cmdRes = OAR::API::send_cmd($cmd,"Oar"); # Resubmit case (it is a oarsub and we have to catch the new job_id) if ($action eq "resubmissions" ) { if ( $? != 0 ) { my $err = $? >> 8; OAR::API::ERROR( 500, "Oar server error", "Oarsub command exited with status $err: $cmdRes\nCmd:\n$cmd" ); } elsif ( $cmdRes =~ m/.*JOB_ID\s*=\s*(\d+).*/m ) { print $q->header( -status => 201, -type => "$type" ); print $HTML_HEADER if ($ext eq "html"); print OAR::API::export( { 'id' => int($1), 'links' => [ { 'rel' => 'self' , 'href' => OAR::API::htmlize_uri(OAR::API::make_uri("jobs/$1",$ext,0),$ext) }, { 'rel' => 'parent', 'href' => OAR::API::htmlize_uri(OAR::API::make_uri("jobs/$jobid",$ext,0),$ext) } ], 'status' => "submitted", 'cmd_output' => "$cmdRes", 'api_timestamp' => time() } , $ext ); }else { OAR::API::ERROR( 500, "Parse error", "Job submitted but the id could not be parsed.\nCmd:\n$cmd" ); } # Other cases }else{ print $q->header( -status => 202, -type => "$type" ); print $HTML_HEADER if ($ext eq "html"); print OAR::API::export( { 'id' => "$jobid", 'status' => "$status", 'cmd_output' => "$cmdRes", 'api_timestamp' => time(), 'links' => [ { 'rel' => 'self' , 'href' => OAR::API::htmlize_uri(OAR::API::make_uri("jobs/$jobid",$ext,0),$ext) } ] } , $ext ); } last; }; #}}} # #{{{ POST /jobs/ : Update of a job (delete, checkpoint, ...) # Should not be used unless for delete from an http browser # (better to use the URI above) # $URI = qr{^/jobs/(\d+)(\.yaml|\.json|\.html)*$}; OAR::API::POST( $_, $URI ) && do { $_->path_info =~ m/$URI/; my $jobid = $1; my $ext=OAR::API::set_ext($q,$2); (my $header, my $type)=OAR::API::set_output_format($ext,"GET, POST"); # Must be authenticated if ( not $authenticated_user =~ /(\w+)/ ) { OAR::API::ERROR( 401, "Permission denied", "A suitable authentication must be done before modifying jobs" ); last; } $authenticated_user = $1; $ENV{OARDO_BECOME_USER} = $authenticated_user; # Check and get the submitted data # From encoded data my $job; if ($q->param('POSTDATA')) { $job = OAR::API::check_job_update( $q->param('POSTDATA'), $q->content_type ); } # From html form else { $job = OAR::API::check_job_update( $q->Vars, $q->content_type ); } # Delete (alternative way to DELETE request, for html forms) my $cmd; my $status; if ( $job->{method} eq "delete" ) { $cmd = "$OARDODO_CMD $OARDEL_CMD $jobid"; $status = "Delete request registered"; } # Checkpoint elsif ( $job->{method} eq "checkpoint" ) { $cmd = "$OARDODO_CMD $OARDEL_CMD -c $jobid"; $status = "Checkpoint request registered"; } # Hold elsif ( $job->{method} eq "hold" ) { $cmd = "$OARDODO_CMD $OARHOLD_CMD $jobid"; $status = "Hold request registered"; } # Resume elsif ( $job->{method} eq "resume" ) { $cmd = "$OARDODO_CMD $OARRESUME_CMD $jobid"; $status = "Resume request registered"; } else { OAR::API::ERROR(400,"Bad query","Could not understand ". $job->{method} ." method"); last; } my $cmdRes = OAR::API::send_cmd($cmd,"Oar"); print $q->header( -status => 202, -type => "$type" ); print $HTML_HEADER if ($ext eq "html"); print OAR::API::export( { 'id' => "$jobid", 'status' => "$status", 'cmd_output' => "$cmdRes", 'api_timestamp' => time(), 'links' => [ { 'rel' => 'self' , 'href' => OAR::API::htmlize_uri(OAR::API::make_uri("jobs/$jobid",$ext,0),$ext) } ] } , $ext ); last; }; #}}} # #{{{ POST /jobs//signals/ : Signal sending # $URI = qr{^/jobs/(\d+)/signals/(\d+)(\.yaml|\.json|\.html)*$}; OAR::API::POST( $_, $URI ) && do { $_->path_info =~ m/$URI/; my $jobid = $1; my $signal = $2; my $ext=OAR::API::set_ext($q,$3); (my $header, my $type)=OAR::API::set_output_format($ext,"GET, POST"); # Must be authenticated if ( not $authenticated_user =~ /(\w+)/ ) { OAR::API::ERROR( 401, "Permission denied", "A suitable authentication must be done before modifying jobs" ); last; } $authenticated_user = $1; $ENV{OARDO_BECOME_USER} = $authenticated_user; my $cmd = "$OARDODO_CMD $OARDEL_CMD -s $signal $jobid"; my $status = "Signal sending request registered"; my $cmdRes = OAR::API::send_cmd($cmd,"Oar"); print $q->header( -status => 202, -type => "$type" ); print $HTML_HEADER if ($ext eq "html"); print OAR::API::export( { 'id' => "$jobid", 'status' => "$status", 'cmd_output' => "$cmdRes", 'api_timestamp' => time() } , $ext ); last; }; #}}} # #{{{ POST /jobs : A new job (oarsub wrapper) # $URI = qr{^/jobs\.*(yaml|json|html)*$}; OAR::API::POST( $_, $URI ) && do { $_->path_info =~ m/$URI/; my $ext=OAR::API::set_ext($q,$1); (my $header, my $type)=OAR::API::set_output_format($ext,"GET, POST"); # Must be authenticated if ( not $authenticated_user =~ /(\w+)/ ) { OAR::API::ERROR( 401, "Permission denied", "A suitable authentication must be done before posting jobs" ); last; } $authenticated_user = $1; $ENV{OARDO_BECOME_USER} = $authenticated_user; # Check and get the submitted job # From encoded data my $job; if ($q->param('POSTDATA')) { $job = OAR::API::check_job( $q->param('POSTDATA'), $q->content_type ); } # From html form else { $job = OAR::API::check_job( $q->Vars, $q->content_type ); } # Make the query (the hash is converted into a list of long options) my $oarcmd = "$OARSUB_CMD "; my $workdir = "~$authenticated_user"; my $command = ""; my $script = ""; my $param_file = ""; my $tmpfilename = ""; my $tmpparamfilename = ""; foreach my $option ( keys( %{$job} ) ) { if ($option eq "script_path") { $job->{script_path} =~ s/(\\*)"/$1$1\\"/g; $command = " \"$job->{script_path}\""; } elsif ($option eq "command") { # Escapes double quotes $job->{command} =~ s/(\\*)"/$1$1\\"/g; $command = " \"$job->{command}\""; } elsif ($option eq "script") { $script = $job->{script}; } elsif ($option eq "param_file") { $param_file = $job->{param_file}; } elsif ($option eq "workdir") { $workdir = $job->{workdir}; } elsif ($option eq "resources") { $oarcmd .= " --resource=$job->{resources}"; } elsif (ref($job->{$option}) eq "ARRAY") { foreach my $elem (@{$job->{$option}}) { $oarcmd .= " --$option"; # Escapes double quotes $elem =~ s/(\\*)"/$1$1\\"/g; $oarcmd .= "=\"$elem\"" if $elem ne ""; } } else { $oarcmd .= " --$option"; # Escapes double quotes $job->{$option}=~ s/(\\*)"/$1$1\\"/g; $oarcmd .= "=\"$job->{$option}\"" if $job->{$option} ne ""; } } $oarcmd .= $command; # Escapes double quotes (one more time, for oardodo) $oarcmd =~ s/(\\*)"/$1$1\\"/g; my $cmd; # If a parameters file is provided, we create a temporary file # and write the parameters inside. if ($param_file ne "") { my $TMP; ($TMP, $tmpparamfilename) = tempfile( "oarapi.paramfile.XXXXX", DIR => $TMPDIR, UNLINK => 1 ); print $TMP $param_file; $oarcmd .= " --array-param-file=$tmpparamfilename"; } # If a script is provided, we create a file into the workdir and write # the script inside. if ($script ne "") { my $TMP; ($TMP, $tmpfilename) = tempfile( "oarapi.subscript.XXXXX", DIR => $TMPDIR, UNLINK => 1 ); print $TMP $script; # This is probably the most annoying thing about this trick: # the tmp file (owned by oar at this stage) has to be readable by the user. # So, the user should be warned that his script may be public. # We could make it owned by the user, with a OARDODO call, but it has a # performance cost. chmod 0755, $tmpfilename; $oarcmd .= " ./". basename($tmpfilename); $cmd = "$OARDODO_CMD bash --noprofile --norc -c \"cp $tmpfilename $workdir/ && cd $workdir && $oarcmd\""; }else{ $cmd = "$OARDODO_CMD bash --noprofile --norc -c \"cd $workdir && $oarcmd\""; } # Escapes some special characters (especially security fix with backquote) $cmd =~ s/(\\*)(`|\$)/$1$1\\$2/g; my $cmdRes = `$cmd 2>&1`; unlink $tmpfilename; unlink $tmpparamfilename; if ( $? != 0 ) { my $err = $? >> 8; # Error codes corresponding to an error into the user's request if ( $err == 6 || $err == 4 || $err == 5 || $err == 7 || $err == 17 || $err == 16 || $err == 6 || $err == 8 || $err == 10 ) { OAR::API::ERROR( 400, "Bad query", "Oarsub command exited with status $err: $cmdRes\nCmd:\n$oarcmd" ); }else{ OAR::API::ERROR( 500, "Oar server error", "Oarsub command exited with status $err: $cmdRes\nCmd:\n$oarcmd" ); } } elsif ( $cmdRes =~ m/.*JOB_ID\s*=\s*(\d+).*/m ) { my $uri=OAR::API::htmlize_uri(OAR::API::make_uri("jobs/$1",$ext,0),$ext); my $abs_uri=OAR::API::make_uri("jobs/$1",$ext,1); print $q->header( -status => 201, -type => "$type" , -location => $abs_uri ); print $HTML_HEADER if ($ext eq "html"); print OAR::API::export( { 'id' => int($1), 'links' => [ { 'href' => $uri, 'rel' => "self"} ], 'api_timestamp' => time(), 'cmd_output' => "$cmdRes", } , $ext ); } else { OAR::API::ERROR( 500, "Parse error", "Job submitted but the id could not be parsed.\nCmd:\n$oarcmd" ); } last; }; #}}} # #{{{ DELETE /jobs/ : Delete a job (oardel wrapper) # $URI = qr{^/jobs/(\d+)(\.yaml|\.json|\.html)*$}; OAR::API::DELETE( $_, $URI ) && do { $_->path_info =~ m/$URI/; my $jobid = $1; my $ext=OAR::API::set_ext($q,$2); (my $header, my $type)=OAR::API::set_output_format($ext); # Must be authenticated if ( not $authenticated_user =~ /(\w+)/ ) { OAR::API::ERROR( 401, "Permission denied", "A suitable authentication must be done before deleting jobs" ); last; } $authenticated_user = $1; $ENV{OARDO_BECOME_USER} = $authenticated_user; my $cmd = "$OARDODO_CMD $OARDEL_CMD $jobid"; my $cmdRes = OAR::API::send_cmd($cmd,"Oardel"); print $q->header( -status => 202, -type => "$type" ); print $HTML_HEADER if ($ext eq "html"); print OAR::API::export( { 'id' => "$jobid", 'status' => "Delete request registered", 'oardel_output' => "$cmdRes", 'api_timestamp' => time() } , $ext ); last; }; #}}} # #{{{ /jobs/stagein and stageout (desktop computing) # $URI = qr{^/jobs/(\d+)/stagein(.tar\.gz|.tgz)$}; OAR::API::GET( $_, $URI ) && do { $_->path_info =~ m/$URI/; my $ext = OAR::API::set_ext($q,$2); (my $header, my $type)=OAR::API::set_output_format($ext); OAR::API::jobStageIn($1); last; }; $URI = qr{^/jobs/(\d+)/stagein(.tar\.gz|.tgz)$}; OAR::API::HEAD( $_, $URI ) && do { $_->path_info =~ m/$URI/; my $ext = OAR::API::set_ext($q,$2); (my $header, my $type)=OAR::API::set_output_format($ext); OAR::API::jobStageInHead($1); last; }; $URI = qr{^/jobs/(\d+)/stageout(.tar\.gz|.tgz)$}; OAR::API::POST( $_, $URI ) && do { $_->path_info =~ m/$URI/; my $ext = OAR::API::set_ext($q,$2); (my $header, my $type)=OAR::API::set_output_format($ext,"GET, POST"); my $fh = $q->upload('myfile'); if (defined $fh) { my $io_handle = $fh->handle; my $buffer; open (OUTFILE, '>>', "$stageout_dir/$1.tgz"); while (my $bytesread = $io_handle->read($buffer, 1024)) { print OUTFILE $buffer; } } print $q->header( -status => 200, -type => "application/x-tar" ); last; }; #}}} # #{{{ POST /jobs//state : changes the state of a job # $URI = qr{^/jobs/(\d+)/state(.*)$}; OAR::API::POST( $_, $URI ) && do { $_->path_info =~ m/$URI/; my $ext = OAR::API::set_ext($q,$2); (my $header, my $type)=OAR::API::set_output_format($ext,"GET, POST"); print $q->header( -status => 200, -type => $type ); my $json = decode_json $q->param('POSTDATA'); my $state = $json->{'state'}; if ($state eq 'running'){ OAR::API::runJob($1); } elsif($state eq 'terminated'){ OAR::API::terminateJob($1); } elsif($state eq 'error'){ OAR::API::errorJob($1); } else { die "unknown state" } last; }; #}}} ########################################### # Resources ########################################### # #{{{ GET /resources/(full|) : List of resources or details of a resource # $URI = qr{^/resources(/full|/[0-9]+)*\.*(yaml|json|html)*$}; OAR::API::GET( $_, $URI ) && do { $_->path_info =~ m/$URI/; my $ext=OAR::API::set_ext($q,$2); my $header, my $type; if(defined($1)) { if ($1 ne "/full") { ($header, $type)=OAR::API::set_output_format($ext,"GET, DELETE"); }else{ ($header, $type)=OAR::API::set_output_format($ext,"GET"); } }else{ ($header, $type)=OAR::API::set_output_format($ext,"GET, POST"); } # will the resources need be paged or not # by default resources results are paged my $paged = 1; my $compact = 0; # GET limit from uri parameter if (defined($q->param('limit'))) { $MAX_ITEMS = $q->param('limit'); } # set offset / GET offset from uri parameter my $offset = 0; if (defined($q->param('offset'))) { $offset = $q->param('offset'); } my $resources; OAR::Nodes::open_db_connection or OAR::API::ERROR(500, "Cannot connect to the database", "Cannot connect to the database" ); if (defined($1)) { if ($1 eq "/full") { # get specified intervals of resources $resources = OAR::Nodes::get_requested_resources($MAX_ITEMS,$offset); } elsif ($1 =~ /\/([0-9]+)/) { # get the resources infos my $resource = OAR::Nodes::get_resource_infos($1); if (defined($resource)) { $resources = [OAR::Nodes::get_resource_infos($1)]; } else { # resource does not exist #$resources = OAR::API::struct_empty($STRUCTURE); OAR::API::ERROR( 404, "Resource not found", "Resource not found" ); } # do not need to paging resource detail $paged = 0; $compact = 1; } else { OAR::API::ERROR(500,"Error 666!","Error 666"); } } else { # get specified intervals of resources $resources = OAR::Nodes::get_requested_resources($MAX_ITEMS,$offset); $resources = OAR::API::filter_resource_list($resources); } OAR::API::fix_resource_ids($resources); OAR::API::add_resources_uris($resources,$ext,''); $resources = OAR::API::struct_resource_list($resources,$STRUCTURE,$compact); # test if resources need to be paged if ($paged == 1) { # get the total number of resources my $total_resources = OAR::Nodes::count_all_resources(); # add pagination informations $resources = OAR::API::add_pagination($resources,$total_resources,$q->path_info,$q->query_string,$ext,$MAX_ITEMS,$offset,$STRUCTURE); } OAR::Nodes::close_db_connection; print $header; print $HTML_HEADER if ($ext eq "html"); print OAR::API::export($resources,$ext); last; }; #}}} # #{{{ GET /resources/nodes/ : List the resources of a node # $URI = qr{^/resources/nodes/([\w\.-]+?)(\.yaml|\.json|\.html)*$}; OAR::API::GET( $_, $URI ) && do { $_->path_info =~ m/$URI/; my $ext=OAR::API::set_ext($q,$2); (my $header, my $type)=OAR::API::set_output_format($ext); OAR::Nodes::open_db_connection or OAR::API::ERROR(500, "Cannot connect to the database", "Cannot connect to the database" ); my $resources = OAR::Nodes::get_resources_for_host($1); $resources = OAR::API::filter_resource_list($resources); OAR::Nodes::close_db_connection; OAR::API::add_resources_uris($resources,$ext,''); $resources = OAR::API::struct_resource_list($resources,$STRUCTURE,0); $resources = OAR::API::add_pagination($resources,@$resources,$q->path_info,undef,$ext,0,0,$STRUCTURE); print $header; print $HTML_HEADER if ($ext eq "html"); print OAR::API::export($resources,$ext); last; }; #}}} # #{{{ GET /resources/jobs : (NOT YET IMPLEMENTED) Jobs running on all resources # # TODO: should give an array of all resources plus a job array per resource #}}} # #{{{ GET /resources/()/jobs : Jobs running on a resource # $URI = qr{^/resources(/[0-9]+)+/jobs(\.yaml|\.json|\.html)*$}; OAR::API::GET( $_, $URI ) && do { $_->path_info =~ m/$URI/; my $ext=OAR::API::set_ext($q,$2); (my $header, my $type)=OAR::API::set_output_format($ext); OAR::Nodes::open_db_connection or OAR::API::ERROR(500, "Cannot connect to the database", "Cannot connect to the database" ); my $jobs; if ($1 =~ /\/([0-9]+)/) { my $job_array=OAR::Nodes::get_jobs_running_on_resource($1); foreach my $job_id (@$job_array) { push(@$jobs,{id =>int($job_id)}); } OAR::API::add_jobs_on_resource_uris($jobs,$ext); } OAR::Nodes::close_db_connection; $jobs = OAR::API::add_pagination($jobs,@$jobs,$q->path_info,undef,$ext,0,0,$STRUCTURE); print $header; print $HTML_HEADER if ($ext eq "html"); print OAR::API::export($jobs,$ext); last; }; #}}} # #{{{ GET /resources/nodes//jobs : Jobs running on a node # $URI = qr{^/resources/nodes/([-\.\w]+)/jobs(.*)$}; OAR::API::GET( $_, $URI ) && do { $_->path_info =~ m/$URI/; my $ext = OAR::API::set_ext($q,$2); (my $header, my $type)=OAR::API::set_output_format($ext); OAR::Nodes::open_db_connection or OAR::API::ERROR(500, "Cannot connect to the database", "Cannot connect to the database" ); my $jobs; my $job_array; #OAR::Nodes::heartbeat($1); <-- what the hell is that???? # a "?state=" filter is possible, but prevent Terminated and Error state # because this result is not paginated and output may be too big if (defined($q->param('state')) && $q->param('state') eq "toKill"){ # This "toKill" state is virtual and implemented for the desktop # computing agent purpose. $job_array=OAR::Nodes::get_jobs_running_on_node($1); foreach my $job_id (@$job_array) { push(@$jobs,{id =>int($job_id)}) if OAR::Nodes::is_job_tokill($job_id); } } elsif (defined($q->param('state')) && $q->param('state') ne "Terminated" && $q->param('state') ne "Error" ) { $job_array=OAR::Nodes::get_jobs_on_node($1,$q->param('state')); foreach my $job_id (@$job_array) { push(@$jobs,{id =>int($job_id)}); } }else{ $job_array=OAR::Nodes::get_jobs_running_on_node($1); foreach my $job_id (@$job_array) { push(@$jobs,{id =>int($job_id)}); } } OAR::API::add_jobs_on_resource_uris($jobs,$ext); OAR::Nodes::close_db_connection; $jobs = OAR::API::add_pagination($jobs,@$jobs,$q->path_info,undef,$ext,0,0,$STRUCTURE); print $header; print $HTML_HEADER if ($ext eq "html"); print OAR::API::export($jobs,$ext); last; }; #}}} # #{{{ POST /resources : Create new resources # $URI = qr{^/resources(\.yaml|\.json|\.html)*$}; OAR::API::POST( $_, $URI ) && do { $_->path_info =~ m/$URI/; my $ext=OAR::API::set_ext($q,$1); (my $header)=OAR::API::set_output_format($ext,"GET, POST"); # Must be administrator (oar user) if ( not $authenticated_user =~ /(\w+)/ ) { OAR::API::ERROR( 401, "Permission denied", "A suitable authentication must be done before creating new resources" ); last; } if ( not $authenticated_user eq "oar" ) { OAR::API::ERROR( 401, "Permission denied", "Only the oar user can create new resources" ); last; } $ENV{OARDO_BECOME_USER} = "oar"; # Check and get the submited resource # From encoded data my $resources; if ($q->param('POSTDATA')) { $resources = OAR::API::check_resources( $q->param('POSTDATA'), $q->content_type ); } # From html form else { $resources = OAR::API::check_resources( $q->Vars, $q->content_type ); } my $dbh = OAR::IO::connect() or OAR::API::ERROR(500, "Cannot connect to the database", "Cannot connect to the database" ); my @ids=OAR::IO::add_resources($dbh,$resources) or OAR::API::ERROR(500, "Could not create asked resources", "Could not create asked resources" ); if ($ids[0] =~ /^Error.*/) { OAR::API::ERROR(500,"SQL query failed into resources creation",$ids[0]); } my $result=[]; foreach my $id (@ids) { push(@$result,{ id => $id, links => [ { rel => "self", href => "resources/$id" } ] }); } $result = OAR::API::add_pagination($result,@ids,$q->path_info,$q->query_string,$ext,0,0,$STRUCTURE); print $header; print $HTML_HEADER if ($ext eq "html"); print OAR::API::export( $result , $ext ); OAR::Tools::notify_tcp_socket($remote_host,$remote_port,"ChState"); OAR::Tools::notify_tcp_socket($remote_host,$remote_port,"Term"); OAR::IO::disconnect($dbh); last; }; #}}} # #{{{ POST /resources//state : Change the state of a resource # $URI = qr{^/resources/(\d+)/state(\.yaml|\.json|\.html)*$}; OAR::API::POST( $_, $URI ) && do { $_->path_info =~ m/$URI/; my $id=$1; my $ext=OAR::API::set_ext($q,$2); (my $header)=OAR::API::set_output_format($ext,"GET, POST"); # Must be administrator (oar user) if ( not $authenticated_user =~ /(\w+)/ ) { OAR::API::ERROR( 401, "Permission denied", "A suitable authentication must be done before creating new resources" ); last; } if ( not $authenticated_user eq "oar" ) { OAR::API::ERROR( 401, "Permission denied", "Only the oar user can create new resources" ); last; } $ENV{OARDO_BECOME_USER} = "oar"; # Check and get the submited resource # From encoded data my $resource; if ($q->param('POSTDATA')) { $resource = OAR::API::check_resource_state( $q->param('POSTDATA'), $q->content_type ); } # From html form else { $resource = OAR::API::check_resource_state( $q->Vars, $q->content_type ); } my $dbh = OAR::IO::connect() or OAR::API::ERROR(500, "Cannot connect to the database", "Cannot connect to the database" ); OAR::IO::set_resource_state($dbh,$id,$resource->{state},"NO"); print $header; print $HTML_HEADER if ($ext eq "html"); print OAR::API::export( { 'status' => "Change state request registered", 'id' => "$id", 'api_timestamp' => time(), 'uri' => OAR::API::htmlize_uri(OAR::API::make_uri("resources/$id",$ext,0),$ext) } , $ext ); OAR::Tools::notify_tcp_socket($remote_host,$remote_port,"ChState"); OAR::Tools::notify_tcp_socket($remote_host,$remote_port,"Term"); OAR::IO::disconnect($dbh); last; }; #}}} # #{{{ DELETE /resources/(|/path_info =~ m/$URI/; my $id; my $node; my $cpuset; if ($2) { $node=$1; $id=0; $cpuset=$2; $cpuset =~ s,^/,, ;} else { $node=""; $id=$1; $cpuset=""; } ; my $ext=OAR::API::set_ext($q,$3); (my $header)=OAR::API::set_output_format($ext); # Must be administrator (oar user) if ( not $authenticated_user =~ /(\w+)/ ) { OAR::API::ERROR( 401, "Permission denied", "A suitable authentication must be done before deleting new resources" ); last; } if ( not $authenticated_user eq "oar" ) { OAR::API::ERROR( 401, "Permission denied", "Only the oar user can delete resources" ); last; } $ENV{OARDO_BECOME_USER} = "oar"; my $base = OAR::IO::connect() or OAR::API::ERROR(500, "Cannot connect to the database", "Cannot connect to the database" ); # Check if the resource exists my $query; my $Resource; if ($id == 0) { $query="WHERE network_address = \"$node\" AND cpuset = $cpuset"; } else { $query="WHERE resource_id=$id"; } my $sth = $base->prepare("SELECT resource_id FROM resources $query"); $sth->execute(); my @res = $sth->fetchrow_array(); if ($res[0]) { $Resource=$res[0];} else { OAR::API::ERROR(404,"Not found","Corresponding resource could not be found ($id,$node,$cpuset)"); last; } # Resource deletion # !!! This is a dirty cut/paste of oarremoveresource code !!! my $resource_ref = OAR::IO::get_resource_info($base,$Resource); if (defined($resource_ref->{state}) && ($resource_ref->{state} eq "Dead")){ my $sth = $base->prepare(" SELECT jobs.job_id, jobs.assigned_moldable_job FROM assigned_resources, jobs WHERE assigned_resources.resource_id = $Resource AND assigned_resources.moldable_job_id = jobs.assigned_moldable_job "); $sth->execute(); my @jobList; while (my @ref = $sth->fetchrow_array()) { push(@jobList, [$ref[0], $ref[1]]); } $sth->finish(); foreach my $i (@jobList){ $base->do("DELETE from event_logs WHERE job_id = $i->[0]"); $base->do("DELETE from frag_jobs WHERE frag_id_job = $i->[0]"); $base->do("DELETE from jobs WHERE job_id = $i->[0]"); $base->do("DELETE from assigned_resources WHERE moldable_job_id = $i->[1]"); } $base->do("DELETE from assigned_resources WHERE resource_id = $Resource"); $base->do("DELETE from resource_logs WHERE resource_id = $Resource"); $base->do("DELETE from resources WHERE resource_id = $Resource"); #print("Resource $Resource removed.\n"); print $header; print $HTML_HEADER if ($ext eq "html"); print OAR::API::export( { 'status' => "deleted",'api_timestamp' => time() } , $ext ); }else{ OAR::API::ERROR(403,"Forbidden","The resource $Resource must be in the Dead status"); last; } last; }; #}}} # #{{{ POST /resources/generate : Generate resources ('oaradmin re -a -Y' wrapping) # $URI = qr{^/resources/generate(\.yaml|\.json|\.html)*$}; OAR::API::POST( $_, $URI ) && do { $_->path_info =~ m/$URI/; my $ext = OAR::API::set_ext($q,$1); (my $header) = OAR::API::set_output_format($ext,"GET, POST"); # Must be administrator (oar user) if ( not $authenticated_user =~ /(\w+)/ ) { OAR::API::ERROR( 401, "Permission denied", "A suitable authentication must be done before generating resources" ); last; } if ( not $authenticated_user eq "oar" ) { OAR::API::ERROR( 401, "Permission denied", "Only the oar user can generate resources" ); last; } $ENV{OARDO_BECOME_USER} = "oar"; # Check and get the submited resource description # From encoded data my $description; # command generation my $cmd; # ressources properties my $cmd_properties=""; if ($q->param('POSTDATA')) { $description = OAR::API::check_resource_description( $q->param('POSTDATA'), $q->content_type ); # getting properties if (defined($description->{properties})) { foreach my $property ( keys %{$description->{properties}} ) { $cmd_properties .= " -p ".$property."=".$description->{properties}->{$property} } } } # From html form else { $description = OAR::API::check_resource_description( $q->Vars, $q->content_type ); # getting properties if (defined($description->{properties})) { my @properties = split(/,/,$description->{properties}); foreach my $property (@properties) { $cmd_properties .= " -p $property"; } } } my $auto_offset=""; if (defined($description->{auto_offset}) && "$description->{auto_offset}" eq "1") { $auto_offset="--auto-offset "; } # command with arguments $cmd = "PATH=\$PATH:/usr/sbin:/usr/local/sbin $OARADMIN_CMD resources -a -Y $auto_offset".$description->{resources}.$cmd_properties; # execute the command my $cmdRes = OAR::API::send_cmd($cmd,"Oar"); my $data = OAR::API::import_data($cmdRes,"yaml"); OAR::API::struct_resource_list_fix_ints($data); $data = OAR::API::add_pagination($data,@$data,$q->path_info,undef,$ext,0,0,$STRUCTURE); print $header; if ($ext eq "html") { print $HTML_HEADER; api_uri::resources_commit_button($data->{"items"}); } print OAR::API::export($data,$ext); last; }; #}}} ########################################### # Admission rules ########################################### # #{{{ GET /admission_rules : List of all admissions rules # $URI = qr{^/admission_rules\.*(yaml|json|html)*$}; OAR::API::GET( $_, $URI ) && do { $_->path_info =~ m/$URI/; my $ext = OAR::API::set_ext($q,$1); (my $header, my $type) = OAR::API::set_output_format($ext); # GET limit from uri parameter if (defined($q->param('limit'))) { $MAX_ITEMS = $q->param('limit'); } # set offset / GET offset from uri parameter my $offset = 0; if (defined($q->param('offset'))) { $offset = $q->param('offset'); } OAR::Stat::open_db_connection or OAR::API::ERROR(500, "Cannot connect to the database", "Cannot connect to the database" ); # get specified intervals of admission rules my $admissions_rules = OAR::Stat::get_requested_admission_rules($MAX_ITEMS,$offset); OAR::API::add_admission_rules_uris($admissions_rules,$ext); $admissions_rules = OAR::API::struct_admission_rule_list($admissions_rules,$STRUCTURE); # get the total number of admissions rules my $total_rules = OAR::Stat::count_all_admission_rules(); OAR::Stat::close_db_connection(); # add pagination informations $admissions_rules = OAR::API::add_pagination($admissions_rules,$total_rules,$q->path_info,$q->query_string,$ext,$MAX_ITEMS,$offset,$STRUCTURE); print $header; print $HTML_HEADER if ($ext eq "html"); print OAR::API::export($admissions_rules,$ext); last; }; #}}} # #{{{ GET /admission_rules/ : Details of an admission rule # $URI = qr{^/admission_rules/(\d+)(\.yaml|\.json|\.html)*$}; OAR::API::GET( $_, $URI ) && do { $_->path_info =~ m/$URI/; my $rule_id = $1; my $ext = OAR::API::set_ext($q,$2); (my $header, my $type)=OAR::API::set_output_format($ext); OAR::Stat::open_db_connection or OAR::API::ERROR(500, "Cannot connect to the database", "Cannot connect to the database" ); my $admission_rule = OAR::Stat::get_specific_admission_rule($rule_id); OAR::API::add_admission_rule_uris($admission_rule,$ext); $admission_rule = OAR::API::struct_admission_rule($admission_rule,$STRUCTURE); OAR::Stat::close_db_connection; print $header; print $HTML_HEADER if ($ext eq "html"); print OAR::API::export($admission_rule,$ext); last; }; #}}} # #{{{ POST /admission_rules : Create a new admission rule # $URI = qr{^/admission_rules(\.yaml|\.json|\.html)*$}; (OAR::API::POST( $_, $URI ) || OAR::API::PUT( $_, $URI )) && do { $_->path_info =~ m/$URI/; my $ext = OAR::API::set_ext($q,$1); (my $header) = OAR::API::set_output_format($ext,"GET, POST"); # Must be administrator (oar user) if ( not $authenticated_user =~ /(\w+)/ ) { OAR::API::ERROR( 401, "Permission denied", "A suitable authentication must be done before creating new admission rules" ); last; } if ( not $authenticated_user eq "oar" ) { OAR::API::ERROR( 401, "Permission denied", "Only the oar user can create new admission rules" ); last; } $ENV{OARDO_BECOME_USER} = "oar"; # Check and get the submited admission rule # From encoded data my $admission_rule; if ($q->param('POSTDATA')) { $admission_rule = OAR::API::check_admission_rule( $q->param('POSTDATA'), $q->content_type ); } elsif ($q->param('PUTDATA')) { $admission_rule = OAR::API::check_admission_rule( $q->param('PUTDATA'), $q->content_type ); } # From html form else { $admission_rule = OAR::API::check_admission_rule( $q->Vars, $q->content_type ); } OAR::Stat::open_db_connection or OAR::API::ERROR(500, "Cannot connect to the database", "Cannot connect to the database" ); my $id = OAR::Stat::add_admission_rule($admission_rule->{rule}); if ( $id && $id > 0) { print $header; print $HTML_HEADER if ($ext eq "html"); print OAR::API::export( { 'id' => "$id", 'rule' => OAR::API::nl2br($admission_rule->{rule}), 'api_timestamp' => time(), 'uri' => OAR::API::htmlize_uri(OAR::API::make_uri("admission_rules/$id",$ext,0),$ext) } , $ext ); OAR::Stat::close_db_connection; } else { OAR::API::ERROR( 500, "Admission rule not created", "Could not create the new admission rule" ); } last; }; #}}} # #{{{ DELETE /admission_rules/ : Delete an admission rule # $URI = qr{^/admission_rules/(\d+)(\.yaml|\.json|\.html)*$}; OAR::API::DELETE( $_, $URI ) && do { $_->path_info =~ m/$URI/; my $rule_id = $1; my $ext = OAR::API::set_ext($q,$2); (my $header, my $type)=OAR::API::set_output_format($ext); # Must be administrator (oar user) if ( not $authenticated_user =~ /(\w+)/ ) { OAR::API::ERROR( 401, "Permission denied", "A suitable authentication must be done before deleting an admission rule" ); last; } if ( not $authenticated_user eq "oar" ) { OAR::API::ERROR( 401, "Permission denied", "Only the oar user can delete admission rules" ); last; } $authenticated_user = $1; $ENV{OARDO_BECOME_USER} = $authenticated_user; OAR::Stat::open_db_connection or OAR::API::ERROR(500, "Cannot connect to the database", "Cannot connect to the database" ); my $admission_rule = OAR::Stat::get_specific_admission_rule($rule_id); print $header; if (defined($admission_rule)) { OAR::Stat::delete_specific_admission_rule($rule_id); print $HTML_HEADER if ($ext eq "html"); print OAR::API::export( { 'id' => "$admission_rule->{id}", 'rule' => "$admission_rule->{rule}", 'status' => "deleted", 'api_timestamp' => time() } , $ext ); OAR::Stat::close_db_connection; } else { OAR::API::ERROR(404,"Not found","Corresponding admission rule could not be found"); } last; }; #}}} # #{{{ POST /admission_rules/?method=delete : Delete an admission rule # Should not be used unless for delete from an http browser # (better to use the URI above) # $URI = qr{^/admission_rules/(\d+)(\.yaml|\.json|\.html)*$}; OAR::API::POST( $_, $URI ) && do { $_->path_info =~ m/$URI/; my $rule_id = $1; my $ext = OAR::API::set_ext($q,$2); (my $header, my $type) = OAR::API::set_output_format($ext,"GET, POST"); # Must be authenticated if ( not $authenticated_user =~ /(\w+)/ ) { OAR::API::ERROR( 401, "Permission denied", "A suitable authentication must be done before deleting an admission rule" ); last; } $authenticated_user = $1; $ENV{OARDO_BECOME_USER} = $authenticated_user; OAR::Stat::open_db_connection or OAR::API::ERROR(500, "Cannot connect to the database", "Cannot connect to the database" ); # Check and get the submitted data # From encoded data my $admission_rule; if ($q->param('POSTDATA')) { $admission_rule = OAR::API::check_admission_rule_update( $q->param('POSTDATA'), $q->content_type ); } # From html form else { $admission_rule = OAR::API::check_admission_rule_update( $q->Vars, $q->content_type ); } # Delete (alternative way to DELETE request, for html forms) print $header; if ($admission_rule->{method} eq "delete" ) { OAR::Stat::delete_specific_admission_rule($rule_id); print $HTML_HEADER if ($ext eq "html"); print OAR::API::export( { 'id' => "$rule_id", 'status' => "deleted", 'api_timestamp' => time() } , $ext ); OAR::Stat::close_db_connection; } else { OAR::API::ERROR(400,"Bad query","Could not understand ". $admission_rule->{method} ." method"); } last; }; #}}} ########################################### # Config file edition ########################################### # #{{{ GET /config : List of all the configured variables # $URI = qr{^/config\.*(yaml|json|html)*$}; OAR::API::GET( $_, $URI ) && do { $_->path_info =~ m/$URI/; my $ext = OAR::API::set_ext($q,$1); (my $header, my $type) = OAR::API::set_output_format($ext); # Must be administrator (oar user) if ( not $authenticated_user =~ /(\w+)/ ) { OAR::API::ERROR( 401, "Permission denied", "A suitable authentication must be done before getting configuration parameters" ); last; } if ( not $authenticated_user eq "oar" ) { OAR::API::ERROR( 401, "Permission denied", "Only the oar user can get configuration parameters" ); last; } $ENV{OARDO_BECOME_USER} = "oar"; # get all configured parameters my $list_params = get_conf_list(); # parameters hash result my $parameters; if ( !defined $list_params || keys %$list_params == 0 ) { $parameters = OAR::API::struct_empty($STRUCTURE); } else { foreach my $param (keys %$list_params) { $parameters->{$param}->{value} = $list_params->{$param}; } OAR::API::add_config_parameters_uris($parameters,$ext); $parameters = OAR::API::struct_config_parameters_list($parameters,$STRUCTURE); } print $header; print $HTML_HEADER if ($ext eq "html"); print OAR::API::export($parameters,$ext); last; }; #}}} # #{{{ GET /config/ : Get a configuration variable value # $URI = qr{^/config/(\w+)\.(yaml|json|html)*$}; OAR::API::GET( $_, $URI ) && do { $_->path_info =~ m/$URI/; my $variable = $1; my $ext = OAR::API::set_ext($q,$2); (my $header, my $type) = OAR::API::set_output_format($ext); # Must be administrator (oar user) if ( not $authenticated_user =~ /(\w+)/ ) { OAR::API::ERROR( 401, "Permission denied", "A suitable authentication must be done before getting configuration parameters" ); last; } if ( not $authenticated_user eq "oar" ) { OAR::API::ERROR( 401, "Permission denied", "Only the oar user can get configuration parameters" ); last; } $ENV{OARDO_BECOME_USER} = "oar"; # result parameter my $parameter; if (is_conf($variable)) { $parameter->{id} = $variable; $parameter->{value} = get_conf($variable); OAR::API::add_config_parameter_uris($parameter,$ext); $parameter = OAR::API::struct_config_parameter($parameter,$STRUCTURE); } else { $parameter->{id} = OAR::API::struct_empty($STRUCTURE); } print $header; print $HTML_HEADER if ($ext eq "html"); print OAR::API::export($parameter,$ext); last; }; #}}} # #{{{ POST /config/ : Change the value of a configuration parameter # $URI = qr{^/config/(\w+)\.(yaml|json|html)*$}; OAR::API::POST( $_, $URI ) && do { $_->path_info =~ m/$URI/; my $variable = $1; my $ext = OAR::API::set_ext($q,$2); (my $header, my $type) = OAR::API::set_output_format($ext,"GET, POST"); print $header; print $HTML_HEADER if ($ext eq "html"); # Must be administrator (oar user) if ( not $authenticated_user =~ /(\w+)/ ) { OAR::API::ERROR( 401, "Permission denied", "A suitable authentication must be done before changing configuration parameters" ); last; } if ( not $authenticated_user eq "oar" ) { OAR::API::ERROR( 401, "Permission denied", "Only the oar user can make changes on configuration parameters" ); last; } $ENV{OARDO_BECOME_USER} = "oar"; # configuration parameter my $parameter; if ($q->param('POSTDATA')) { $parameter = OAR::API::check_configuration_variable( $q->param('POSTDATA'), $q->content_type ); } # From html form else { $parameter = OAR::API::check_configuration_variable( $q->Vars, $q->content_type ); } my $result; if (is_conf($variable)) { set_value($variable, $parameter->{value}); $result->{$variable} = $parameter; } else { $result->{$variable} = OAR::API::struct_empty($STRUCTURE); } print OAR::API::export($result,$ext); last; }; #}}} ########################################### # Desktop computing specific ########################################### # #{{{ GET /desktop/agents : Desktop computing agent sign in # $URI = qr{^/desktop/agents(.*)$}; OAR::API::GET( $_, $URI ) && do { my $db = OAR::IO::connect() or die "cannot connect to the data base\n"; OAR::IO::lock_table($db,["event_logs"]); my $result = OAR::IO::get_last_event_from_type($db, "NEW_VIRTUAL_HOSTNAME"); if ($result) { $result = $result->{'description'}; $result++; OAR::API::sign_in($result,$remote_host,$remote_port,$expiry,$allow_create_node); OAR::IO::add_new_event($db,"NEW_VIRTUAL_HOSTNAME",0,$result); $result = {'hostname' => $result}; } else { OAR::API::sign_in('vnode1',$remote_host,$remote_port,$expiry,$allow_create_node); OAR::IO::add_new_event($db,"NEW_VIRTUAL_HOSTNAME",0,'vnode1'); $result = {'hostname' => 'vnode1'}; } OAR::IO::unlock_table($db); # TODO: reject if DESKTOP_COMPUTING_ALLOW_CREATE_NODE="0" print $q->header( -status => 200, -type => "application/json" ); print OAR::API::export($result,'json'); last; }; #}}} ########################################### # Media (files) download/upload ########################################### # #{{{ GET /media/ : Get a file # $URI = qr{^/media/(.*)$}; OAR::API::GET( $_, $URI ) && do { $_->path_info =~ m/$URI/; my $filename=$1; # Must be authenticated if ( not $authenticated_user =~ /(\w+)/ ) { OAR::API::ERROR( 401, "Permission denied", "A suitable authentication must be done before getting files" ); last; } $authenticated_user = $1; $ENV{OARDO_BECOME_USER} = $authenticated_user; # Security escaping $filename =~ s/(\\*)(`|\$)/$1$1\\$2/g; # Get the filename and replace "~" by the home directory my $file="/".$filename; my @user_infos=getpwnam($authenticated_user); $file =~ s|/~/|$user_infos[7]/|; # Check file existency if (system("$OARDODO_CMD","test","-f","$file") != 0) { OAR::API::ERROR(404, "Not found", "File not found: $file"); last; } # Check file readability if (system("$OARDODO_CMD","test","-r","$file") != 0) { OAR::API::ERROR(403, "Forbidden","File could not be read: $file" ); last; } # Output the file print $q->header( -status => 200, -type => "application/octet-stream" ); print `$OARDODO_CMD cat $file`; last; }; #}}} # #{{{ POST /media/ : Upload a file and create underlying directories # $URI = qr{^/media/(.*)$}; OAR::API::POST( $_, $URI ) && do { $_->path_info =~ m/$URI/; my $filename=$1; # Must be authenticated if ( not $authenticated_user =~ /(\w+)/ ) { OAR::API::ERROR( 401, "Permission denied", "A suitable authentication must be done before getting files" ); last; } $authenticated_user = $1; $ENV{OARDO_BECOME_USER} = $authenticated_user; # Security escaping $filename =~ s/(\\*)(`|\$)/$1$1\\$2/g; # Get the filename and replace "~" by the home directory my $file="/".$filename; my @user_infos=getpwnam($authenticated_user); $file =~ s|/~/|$user_infos[7]/|; # Create the directories if necessary my $path=dirname($file); if (system("$OARDODO_CMD","mkdir","-p",$path) != 0) { OAR::API::ERROR(500, "mkpath error", "Problem while creating path: $path"); last; } # Upload the file if any #my $fh = $q->upload('file'); #if (defined $fh) { # my $io_handle = $fh->handle; # my $buffer; # open (OUTFILE, "|", "$OARDODO_CMD bash --noprofile --norc -c \"cat > $file\""); # while (my $bytesread = $io_handle->read($buffer, 1024)) { # print OUTFILE $buffer; # } # close(OUTFILE); if ($q->param('POSTDATA')) { if (system("$OARDODO_CMD","touch",$file) != 0) { OAR::API::ERROR(500, "write error", "Error creating file: $file"); close(OUTFILE); last; } open (OUTFILE, "|$OARDODO_CMD bash --noprofile --norc -c \"cat > $file\""); print OUTFILE $q->param('POSTDATA'); close(OUTFILE); }else{ # If no file is given, then create an empty one `$OARDODO_CMD touch $file`; } print $q->header( -status => 201, -type => "application/octet-stream" , -location => "/media/$file" ); last; }; #}}} # #{{{ DELETE /media/ : Delete a file or a directory recursively # $URI = qr{^/media/(.*)$}; OAR::API::DELETE( $_, $URI ) && do { $_->path_info =~ m/$URI/; my $filename=$1; # Must be authenticated if ( not $authenticated_user =~ /(\w+)/ ) { OAR::API::ERROR( 401, "Permission denied", "A suitable authentication must be done before getting files" ); last; } $authenticated_user = $1; $ENV{OARDO_BECOME_USER} = $authenticated_user; # Security escaping $filename =~ s/(\\*)(`|\$)/$1$1\\$2/g; # Get the filename and replace "~" by the home directory my $file="/".$filename; my @user_infos=getpwnam($authenticated_user); $file =~ s|/~/|$user_infos[7]/|; # Check file existency if (system("$OARDODO_CMD","test","-e","$file") != 0) { OAR::API::ERROR(404, "Not found", "File not found: $file"); last; } # Check file readability if (system("$OARDODO_CMD","test","-w","$file") != 0) { OAR::API::ERROR(403, "Forbidden","File or directory is not writeable: $file" ); last; } # Delete the file print $q->header( -status => 204, -type => "application/octet-stream" ); print `$OARDODO_CMD rm -rf $file`; last; }; #}}} ########################################### # SQL queries ########################################### # #{{{ GET /select_all?query=) : Allows select SQL queries into the OAR database (ro) # $URI = qr{^/select_all\.*(yaml|json|html)*$}; OAR::API::GET( $_, $URI ) && do { $_->path_info =~ m/$URI/; my $ext=OAR::API::set_ext($q,$1); my $header, my $type; ($header, $type)=OAR::API::set_output_format($ext,"GET"); # Must be authenticated if ( not $authenticated_user =~ /(\w+)/ ) { OAR::API::ERROR( 401, "Permission denied", "A suitable authentication must be done before looking at jobs" ); last; } $authenticated_user = $1; $ENV{OARDO_USER} = $authenticated_user; my $query; # GET the query parameter from the uri if (defined($q->param('query'))) { $query = $q->param('query'); }else{ OAR::API::ERROR(400,"Bad query","The 'query' parameter is mandatory"); } # GET limit from uri parameter my $limit; if (defined($q->param('limit'))) { $limit = $q->param('limit'); }else{ $limit=$MAX_ITEMS; } # set offset / GET offset from uri parameter my $offset = 0; if (defined($q->param('offset'))) { $offset = $q->param('offset'); } # Do the query # The query should not contain the "SELECT part". Example: # query="FROM events,jobs WHERE ..." my $dbh = OAR::IO::connect_ro() or OAR::API::ERROR(500, "Cannot connect to the database", "Cannot connect to the database" ); my $count = OAR::IO::sql_count($dbh,$query) or OAR::API::ERROR(500, "SQL error", "SQL error" # <- add here the sql error output ); my $result = OAR::IO::sql_select($dbh,$query,$limit,$offset) or OAR::API::ERROR(500, "SQL error", "SQL error" # <- add here the sql error output ); #$result = OAR::API::format_select_result($result); $result = OAR::API::add_pagination($result,$count,$q->path_info,$q->query_string,$ext,$limit,$offset,$STRUCTURE); print $header; print $HTML_HEADER if ($ext eq "html"); print OAR::API::export($result,$ext); last; }; #}}} ########################################### # Html stuff ########################################### # #{{{ GET /index : Welcome page (html only) # $URI = qr{^/index\.html$}; OAR::API::GET( $_, $URI ) && do { print $q->header( -status => 200, -type => "text/html" ); print $HTML_HEADER; print "Welcome on the oar API\n"; last; }; #}}} # #{{{ GET /jobs/form : Html form for job posting # $URI = qr{^/jobs/form.html$}; OAR::API::GET( $_, $URI ) && do { (my $header, my $type)=OAR::API::set_output_format("html"); print $header; print $HTML_HEADER; my $POSTFORM=""; my $file; if (is_conf("API_HTML_POSTFORM")){ $file=get_conf("API_HTML_POSTFORM"); } else { $file="/etc/oar/api_html_postform.pl"; } open(FILE,$file); my(@lines) = ; eval join("\n",@lines); close(FILE); print $POSTFORM; last; }; #}}} # #{{{ GET /admission_rules/form : Html form for admission rules submission # $URI = qr{^/admission_rules/form.html$}; OAR::API::GET( $_, $URI ) && do { (my $header, my $type)=OAR::API::set_output_format("html"); print $header; print $HTML_HEADER; my $POSTFORM=""; my $file = "/etc/oar/api_html_postform_rule.pl"; open(FILE,$file); my(@lines) = ; eval join("\n",@lines); close(FILE); print $POSTFORM; last; }; #}}} # #{{{ GET /resources/form : Html form for resources generation # $URI = qr{^/resources/form.html$}; OAR::API::GET( $_, $URI ) && do { (my $header, my $type)=OAR::API::set_output_format("html"); print $header; print $HTML_HEADER; my $POSTFORM=""; my $file = "/etc/oar/api_html_postform_resources.pl"; open(FILE,$file); my(@lines) = ; eval join("\n",@lines); close(FILE); print $POSTFORM; last; }; #}}} # ########################################### # Anything else -> 404 ########################################### # OAR::API::ERROR( 404, "Not found", "No way to handle your request " . $q->path_info ); } } # End of fastcgi loop ./oar-2.5.2/sources/api/livecd_snapshot2.png0000644000175000017500000020012511757171206016772 0ustar plbplbPNG  IHDRɊBX pHYsq́ IDATx}/WSGUѬմE"ځ(33 Wj@$ljmdWYmD6؊DwvLV2DK2F D(Xn`[bttJ=%QoߏZ^W """"+W """""sPjx[CDDDDDc8ø9^Vw}0.NDDDDr/Hl[Լ<= `7O|((=_}.oCilMU;W9wr[Z_X_X_}}kk yٝ%`q7zH+% 7 nLֆ7ؾlf]ߌϽ)JJ Zd-H晖mRѫ~7 n| g[c7舲ڿL!ֆÈYEǾs7Mw涴?vѦo;{'` ,\O|gDDԓ_u_yNʳnՈWQ{.˺-aG> tj34oFG7t' hJp?X'l۶/Ƿ?7ߴLD3""K_*5gK_ʻv9̾Nsoo" . `c9;+jCHu/* +|pႽc>o1{Zb7 np#>pgo[w?QkWZJ>npܘ \ _V`Z; F.\ ~;y;Q#+e93Tm6w~NG=[t/-xGӅ np܈7%KR ioG9&^c4 npc"7t%}P|#ЂfgW8xb+QxntwWĠ %cw[jeN%Mu6⋓C Yb@DD=s'f#{쮓H -r%;1ݚAxmA6YND7޾.RXfEDD[8W#"\MC ߝ.@Zz]pč["+ Dd[wp7={7 n𼖾}ߵw,},9`b@DDDD7Q jҳĩ;#~{S]@DֿLFWQ{~ $fWGaŀ$<ǻ_Ec%#_]@V&D42;jD4w9ɀ1qHHi΄Є4csIHͬ 0ی 0쏞LjS,|{S4:#mxkaM*BvmǮw T~"J-_tDYb:Z^.a9f9-iO2ٝdaǻt[G4g; 1[maw-' 1 o]V+ @6'b޻73>ߑ">'7@O=vWA5Qrщqw ?`ռ@}͕{bN@8DS+ss^{ PM- eflqoX⻝}.ܼt۞xt /?{ &g3 Ab] AzizӃw/][hRhv{Gײַ86Uo?T? फ~dk{,y gOLs/V7DY  Us'ژ7zX>=~(m0@"笠1},RW7%KmpʕZz;G}+W~t6|y"""Hc_`ò *3?=nY3{qk޽G7EG<-UުC_ KDDDDS0븂Ae[G}Կ_7 fMM'MDDDDif ^zic =OyY%1WŒnh3'BZi!Y8+eٸεVC ]xV'o@.BDDD4z c2rnC(޻72+sڲeKEFaLEF 6^[ ;fu{_LwADDD42>Mї ls 7/A /|wm h\vD( ^V`ros""Ht| x( %'(#7ӏM'+@؛܀(݅Ͽ"4u je\q 01@V`cn@DD4_&@)eqs8ԱB5M #82ŊjqgW>&""~ 0m ` C ˟ywʼI 31mn_nX6:>MbدT:BO\ [A'[UPI oޤXy=`ܭ """gO>3]6O&0Ƭs ${qk^7OwM~iJLy/:!| ~bpc K `gD'~@%<+@i%gԀ~w& }g'*TO&2B4hl'nn L Obݹ'+ x}hTv?cI @[CP}CV c>vLRNqH4{#/T!b  )R>@qe 7/^ 0+t]p梁\*_U=PەPn`QBm Y*ǻYS}&y8,CDNsB !D{T r$љ(_?Ws=m!֕: U("YA7^[DG!$OaV0E ecW1S1S=NYb1O'""뜖4Y2'/{g YA0O.0+`O>;z}#FxSż~e ]3ĹA 0QIq.ӃPYכ/tI˖! kVWZqY#nzӪNfJDDD Eܦ(_7ǎ{weg_(>p"0 Qb0 <i̾ e?1"""J_<ċHD1k@}d-ҹ(E׬GcCNh[*(bNyV0(E3`Ͻw ÄgOTmJ"""J>)1"Ƹ^Gɧ{;wt9(2+AS'޵7?q """,/݂CKV'N>{7Մq~Ntw~;?g̬'|*rb{qM6gQ~anևl~_;7 pSϝ_18܄Չ0,:lZRjr!./+{ؖ-[I YA@lٲʕ+W\DW\WhMmKVp҃so-[tj؞/%_XOc 'v|Yvn"Lh&ah?+%):1;w,:Bd Tdvݏ{9#3O{Z;""""venNo[Y1DN'_%>׆|?##"""Pt%jN!Q <8N#""" 3Cfdbg]ӕ*LkVQڪl)q3Uhs$i΀i (s >Dih4%e0 "WkoX |TE h G )VPWQ|Ԥwv!aH) ]abAh9  ?+@J*6&"""r ՅqݯŊU"jU !k5k C߬q M)465%G޻"BA!_z**6v""""?N.1tbr̤>eʆa4t0 oZi5ZmY=/g佦ڙN嵼-@B HWŀ #z1 aB#zQlNK>ʀ@4)HЏYѤm>Q_7Q1xt Z? ~U 1?L@@S C5CQŵtdE]Q6xcI_tCg z9kk+" 3?kBDBPP(j+ ֢)MhHiIX%eXPׂhR+DDDD&:i"*ȉV8 Q!]E@(l4[*z!%uC76! *A? <2~ V=c""""+dk4 l“J(jŭ^xl^AEЍ͚jYQAik^|OE{mjz# f>T \܀hsފ C?ٵi@G (|FJlQ zVlTޭfy-_CÚPPX]eOCp *&WR׬w9P3*Yg;oݹ6-T/%+R"W-КKk0֢YS1 """ h~ QcGq`?t !孝f@h0+݇4Gli3ko/U ~\U@Jꘕ(,+@z430oj×4|Z._74 jTE-_[^ڹ=WaBPW/'0 ehpoˮ srpV9oֺ7X@¢ (GB*DgHUe˖^(s+vPP߬Ǹ KúTf@ 5+@pV""""" H6^NYU RBSE B鑑YNOx}^sw'"""H 2+L3(ݿEj TrZiq${*@@!TY9~ \D=@9p9pϥ|?+E"_}eTE >Nny}kΨ!pPPD1ݒ1DADDD4"#XkV`A ;؏Ь׍*}f !xihY+ """.0Sh+R>A2R1mOhKK _3DDDDUsXt\ł039iuYGY[0_Q PR uewMog~n/,2h (Bګ Yzn@DDDD"0 p=é& bE6/:[-嵼-5ô_~충PZ η DDDDzXqa!85u5iIMJ_*Jْ }W'~|bv$CsqjYbЏ~VK"DDDD4&G aCg_?k/U\ZGdJsWVW~RybXw4@qk:+YA~D^z1W=-M"Al(-YyRomްZZ,͖5ijʹw/եl_0tϝ39g$_S8a$c0%ADDDDCms >w c"'`Y7>aV'l@6d-Zw3[1t@DDM &tM(™'+"rځ<35asȁt< IDAT4N/d58ނ0ȎUϵ'X&ӂFXhGf+YQ$ @ 7/G!B Snݥ o9BZIz"4C6Lo /HBh|5'0w4O<T:4V/Y+ {ຂ{Q 2aIeת tAJ~)1g Q6gdђ_VTVпtwdN LiyLϬD&{!fo1Ѥ 0\@]{^S@@(PBE>{tjSJH!0!kF&rǭ6H}ZAdzf hf#gvξ^q^>iRcyV Qk~GR6nHBJCRJsWrٴd^ӊ3y!D|5q_2/Z 3;Vv"fO,9ŽӳH?O7YW dRΞ>~n4w{þ6DD4]Δj1y?T T!r"Ӡ)g @(*B0Bzs77ޮ"hBЂ -,fuv~ZO)z$({nDDDq%+)n4 H)- 2M(k,CK5(bgYM -+ _j6 1e}{w""QcV@ȸްPȕ9ZV*c h$;g)U 3 V,FG > 0VKsЁ]_ܿ @zDDDQ!l%|Pag04P.uݐ i,Ԭ8Q JBP*/ 8+QL=F$SQxL a LkbsZNT/U6^_+] R¸l6KZݐ2 PKJ%!/W^\fJafK$wT A_4+mˁ2?S~B%uV9_Xj~0t!4:k_SB|f<,vIJW"fVx:s;q⪘=4ѷCO)8}h({ G,e?=HC̤]陥[hVG(JBiҒ7t0tf|`zvvCQTKèB4{db Gw&_?=;J]$0詝v&@bDD4)yf#m5 OVȃ7e}VyC7 )uWBa9%ć~iZ&$ )J3%(jZz 0U>2b$DD$"msڜNҁ/+./ii Z w(]ǔΉkFlV a㭉n|~mMpЫ5B`V3+YPe@7~ M;7YSxw"""&@Q% R4QsaŠDĈ~DDDShjDTKdb@q,@DD4& +@k5.ϫW])ys 8 U;!DDD\g:rgL3-oſ}#7h=ghz"""D0T ኁi;+I,F6${"CY _P~mH|""""3LdVrlŠOODDD#3SYT*t1>l$fa~\xX= ""&&+|Kw=h=hO;cGL0h2D Gr?fHߞc[cDϞv!Θ *s&c Ѻg6Ϟ#o3IT1{ڸ;yMTV%O\>f$;kPgODDD$ 3+Up?JYA1#/o53)P|䔈/)kȺc(2'O}VV O  0K[Ve L׼YACT /3kfoѮh2QJ) z z`~ $e+Š;ό?g""^qb)ŒmQ[d@"f;be(J`<:4@xWO K bgPΐe>DDD#5=6jL*! :=Ҵ < PE8肽s^ gV0 fY2gO j}sB _,욞=zύQ@DD4,ό8YAb sǿ0okw0rٷj+oT,o5='`o{'h"qM&?+ɢQ<Õn*⼸Z !_yIE֤WjEF,P>G?<¹+U JD={&C6dfn}=eFY.ܸ_޹PRM @K+ÔR4 ձRQ=}ݣNпɋY!Y!i H<[swi]VU˩P iBPPFTZ8szMo+?_yM}LoYJ5t U#{mFƩ* e{[ѹP9rbڼ8{֨]Ү+4O*7V_^]3܍֞+CTڃnY23+'wG SlKh3t{]~[V^ DNTHHI = Q׵ `)y'8? ) ""apb: Cp-MEA8.zl}0`~-|u!3V[bA,)-h(Ҝum♟ԤbrӑnO̬c(pӃcqd'@AY z"`2; VިJ i3߮H )4o4MxwٜIH-rŽw-b]Rq ҂w{gD;,7Pxr/zC2"")Q7 |-v&[%M ՚4?ސ0 Ӕd*rs:iIX%M9tm~F-?}_3ꦦ$.-$ Պ;ܡ+g❈y"L|}N61#"""J$ +}Fnj| @Ei6%!UZ;ڦŭ*z/%-@+/T^5νk0-6M(X+5%c bj GaϊW=wDDDD= e`B9h V]+?Źi\ @  c,Ei !"M@DMYg˟)柌iA6MK.MѬDDDDD)֥GPPsV G?'"R+(5WJᚶWj%@@u:f? y!kL)*Sw{R6@JhB5龩\jmlidb@DDD4By+סݲ %{[Uҵ(i" ߭pMTJ Y.k!eT/F1"'`W60fA/YRR19(єsO}qLgѠYw$8+EN~4U@Ӑׄ6 PX{V=/E@b'9jYRR1\i0Y?}>7(mhB@:Y"\߄P*r9Wj^`9$`3,+@Vg%""""'(S4>bfLi%XIW PCB7Q~:م":/kR)ear':QX*2~ET!u=/lQF(Wz )r"AJY$M)H ERv$ fD5ɷujJDDDD40 o' j{d1-Yr5gXP@XХ @S3 B W$%Q0n/(-GUO A\h;mgh6Ò9UˡB˩ ,\IYFnVϛg_ՍP,~KzI1j/HDDDDdQFޏȓ r (^X-sŜԢ&RA;Gõr] EBA*Z^HӐ"DR (ƢӤYRU1DDDD4ƽT֚7pYVaV!@;{o+kϋ5B&P_բ>c7MÐłB9BBh9(X\ѹcOYRU1 """vIBޞ}`=!0+X>ҵby}y|NvV`R `{)5ِŭA\-DNBJaH;+r\ ,_bev.""""j9 ?rJCDg$L1!QҵZ߫_7ެtS?-ނ5UQ︫TM@NX3+Iuŀ=f=jiqdD NV`q(8j-4yvŕjպ8v/}q.{Ֆn˗P|Pbz fR]1 """i[HKn{d b"@^(,ϕϼP=o]{W+F'օj ].CPVc`8([&a^jY;+6as!V~nTRJ|_?Ӈ]*7ͨުW.gEMH/+z âfDDDDeiC/+-Tx(/: cS Q,cY+\kIU|d:(jɒl_y⿝aY6ƀ˷DXZk3+h ֍FݞlT`R"k;oZ,ZOjLXfsi0-Q]Dgao)5 ~{V`[X_;oB RS?Y5,9W.+ٝsM皖\2VVq1VL""""6"UY 6fOIB_un)jyg4kQDh0ϾR:U(ސu`E+d)Gb@DDDDcO7%Fwo nXY9_x\~~NhTBb!/}zCXߐ뵫Ng"zf^b@DDDD^:5gΞ"fάWW~\T,iRaVGJOY6.~VV hXg#OVu@b [k@M\P{[IT5` G@VDHHcSt BEd{a9qss0bpDDDDaA`Ϭӳ'IV/K:DDDD)V ;_sLbV -pV@`"_BI< Kb@DDDD)0i=Z<\rXrA`V1 """ H&?+@*-[ DDDDzGDVU 8+QǞ#2-DVbΐ}^sw'""": |b0pk#xQ ~D/:/;}G Br4йeHǙIX """1g#peb03QDZ"D6ӿU3شpuoAc&?ѳ]3J';?<@]ӟĿ;Mik~u3bDe=Z\1?u~tG{L;y""3!:Kw1cG^* kf1.smc@DD4=X/k,_ݴF'n8db@DDDrGA[Hax]'I,3YSZ1G,_ܧaO] YadRğ4B F@DDy]VA,WW89v~[; BiS"""B Y |il 0v37DDDD/pGH 83knnEMV3ٺ7fye X>}?+W>N-NiJDDD^ F LaV))CDWprɴzZhw憣{:QLX{ji_5o>]nxBf+Y @P12NsD[";+8a0wyr :edASh~Pr'"pEiÄ >9˳'ry?\Y`s>EE-_4wCXC :7f]{|)Mpb0YR(ylh(\rvr-S 0'cevn`?W ;ʳb!󖅥֢}: P IDAT/Q+*%eb@eox%"" Cgj|G{xGO99Z [JZn4P4;oYXXX,3d8mQZX1^IK"<+hEOh+oWVj8<}Bqlج*A^ ; <>!+ ebY^;v$`~"n| ®2}J;{@ߑji{F%5w뗟ٍ^=w5XR䴧4#<)|U66ju(ly-s#W?^((VTV DlW\;ѭ>n3x,S97{w""qVLfrA?? ;?cMcȷ.n-.ocq\,[:iҭ;u@]I8>+ǼڕӛD޶ޝh.0݊$ H y#K)w,n7kK.?~q){_>Owx蛵K5/(alBq充֮< 8+QLGzoKdo+.1ZiݣQ); 0ެ+̤yL6Q󹽏{L6oʿ{^]k6; Y4[˷.-8W)ڃs9+QtwsF2tqSWGDDԹH^?R1odCzmgi3i>"+h5[ꆬmk˷--޼\G5|e||0{ݓ]IQ]LŒT}_l]Kzw>ayHmb{dΦސrS/J取*Jj0` įLPnYَܽ߭<0aїPþRH7;{wg3ċ;,4+LOV0C;>oA0ǿd%(Q=ZBaHi>Z.}\yR}ZU 17Ev/a HӉ8g'z@z {O>I"""%k4JrϾ;=kS~|Ar¬aKrtbq^#9,+TU (f%""8@; ^czOpT>xaZj vvKg^<2NV"_+Y9 &RyaDDD/,D΂e(m-E~׾]66_|=e yO,߶Ǭ[A;7YSxw"""ʚ ʽp ˗J_:zkyxpD׬c+/^S06u 1Mt|(DC2mP(6ު\x1껖w y談߻[Wsn/+k;rׯIZmkGF*""YjY!X_~|Zm5+ת}n~C7)ϒc (U#AY""0=j?+ ?.FY!껗^ZY=zQOBa, ?:aͨW9أzGb rdb7os`Ew4kswDDDDq1+!Y,hWkoU\ŋw-|{ޅcm}m7 kFe#O5!ܖ1rjt$?Wco<Q $ϴ rٳzʻ;>董Ϝ0 )*"+@V+4;ܿuDg35_锠aDDDӴ It͚ˋ\rŇ<\yk ^\yNlc9?*ΖN?w:,7X=KUY1+dŀ""!` d\qEJ/N6dV;zhSRyb4Rѐ3#a57޷;m"" Ñ}Ǯyw? [\'z~1^(+pѥ/V+ӵ7i/n-z?s9`ڦ!zӫc{B,ݺ2Yj dY'Hu:$3ǁpwq`gt;QkFO(>HG RZ_XzEE|I)V+Uk}[Op]dx3+.YQrv| &J E3h-۳ `0?h툿 ""AlDjzx\{cX( E %&\1S甦>xLDVV <yd و8򎈈g7r-x#?fdGpʕq|? طnG&+@f*8"}(gt%:KdD2Y =NĿ31*""" ~Ee~b\s+gcjeb^ȁ4,YzHO;"""] h='\ !!bOj0P/BCIu9+QF8@jL%LKL4_C'gVHyVيQJE=h03πIo5D>mۼrA8/  ڡrҢrڃYYYYEn.P+)PA+\,S,[[6$}ʰ,,VV6Ƒpj[~-E%QP(AZ uFiG!V+K'VICI 6S9 ]p~捚1(B!P-Pp;*Yof%H/Ҥ&sT*a*B!viOq| b7? Ԋ[5]jB&sv$iscBlXQ}=!B%HR'3UnK0 { B!TZHpƒ lj[MRZꠉUx"7!F!rbr˷OWRh]XRr4c!Bm(|ڢB!$LZ.teep9sOEi ?B!Օ 3. 6 E O1!BH@efe]gЂ\4cB! Po $HY^SX9U(|w3(.φn&z gtdB!1jH.ɤi:ˠih _B!YrBj hZT4cw&B!PmОVeRrl|4cÁC!B2("NA"j2x؂B!}Vkr~uP4cB!T5Hz'@nhC*H_Wky 6BZIXq̣zR LT ef;=!B[p,6] ȼpJBHu_5wb´ )6{ު[k#q+BՌƙ9V[(v hv`2yK ^ @{GﺺW){d^ -=̍ n=se5 N6+AF~{|if,ouBH|+{Z-5BGn:\̮P܊h0*M̓R?yGkA#5>HŖ͚ޒ{l?.on-,oƌ_/#B2m $45] j@TI)ױVPШRqFt,1sxYW6wB[¨̫"4lfC<pI6GVu_ЂC_2x rWsG!TJs}QY%,i`*&gʕ.?Q" i5 #L;BZ7% "n]QF9M /]'#*(UGADn&}fpII|,xrI~B]f^ܕBmQY ԔyIӜ-Z 6h'*X_E]2ׇI$J$xx>LœϮϒR+HmD-IB;j]"r f`z9QŇIY"ᄬr*ۨH(FjZ=7{J/B!Tʷ UA)^^*>BHot"ra` UUU]xAQOBV#=+3*[5;B!҈ Rc) "B` !Bs J 7#.* JH]B6o2S Tk$0Z3,{FU$fd) `BH1oN"*ob#2+h"`{*R6[2[乷ܘy'=U!T]ZH%([x.(n+,B[tFM?%/7@qSeP IDAT| ~+ ^Ƈ嶰oe J`7`lO)8ږdnT1 T4U]x%U7P'}_LYO v}D#mC0U>k*5?(BH_ag~TKxUN|K<'"㯰2iM~ 6 *UԖ>X(<ڐٚ#COd7欬&l2˺jX|wD')UB5OϭB}-~fNfuf,;>/ !P 2__"GS`GJEz;^H|B[MVNj30 n߳b(;JPj[泥ʷZe>b'k]]=<>E%xWؠ6TJ͎@mMY#O@tT%ŝ"Yn/HVՕy&{/rC2s_| m\p10@qY me"SD6 R)8}ߣr,=S)H<O="e5\o)"iWzzVUY{jk!BU{ *~27B!NzGAZQ{IFCEk!B5F< > B!M5c^%C;@uB wΗf)~wNH1 HH?HP׫]I!B*ȷs嶿E. Oe/ec3 I!B*Է J$U7eeAbGrGEsB\wo vUᲚq"! TGX" BHCssݺ͗, QB!DeX/t)k.(}r]2_5Uͺ:\ !Bj kVfM敍/%m?!Ef$H0^YB!\ $ L]zbUFК)NQᄔrHk>GT ex5Vl4xBBA/cRy~2 MM_vt8kS2l8AuZha 5_AB(8vK$N%@@gGT4UFwZ,_<΅Hd[ny%?&uDvvk{=x5X ,yzxԺxQOgi(s1*E0T+9!$½(l= od9.e #'*s ׂr( [nUuW#TNipYcj4YL_v ĕfKw JFdr7zf?^"M1(Bi6 ~DMXXCqˌ @D|TPjWzJt:<4f/+L l7~q" r޼;pq<+K]h4~@[mW/twm]M6 BfSyoS^__?~gw4a}~߮n<J}WJOQ؃xmO[Z_K;yej;i޶p/m?{qq3z޻i;LV6_'kn1r1 ~~`cvF {̪dQ 0~uӵ%mvl{>R p!<6Iӿ%bR0H2wx'HֳQ7r6=i65B77=S)s7z"^{V*x'Ed(zd(}wi ><~2_x^w>r.zE*I$s|)vuw[ ߉&XpQW\x/>qr/w˧f`nee{2֪#?ƅ{ۿۉ$@VPu!uDs֨Z L11*PE;ȍ aE.O^3 $AnwK:̴_OQOyr_w2 oG(L1}#QrːUeKD"0q0<8 iY l\;ӧ(o!IӀ^#w]dJs41|v@"4]lMo~ϟW;98gkЬ$z"ӕ͗)ZU w?Ƶ) P!qz~l!Ϣ cqj>o殚~/|+Yz"eEEҦP\I֬E>g!tD)}sNϩ`ZZZ|o 8?paXla=-<)Y[Ώ|9  915SƆuh~>yq7knqɏ/@`;;MyYϗ-뿼ϢrfVV0 )'S:V")SxvB[uBF,Y- |O)Sn6WJ*5BU`0' /PchGqC/{"uw⸵%pqzvwII֑s9SQA;[=_@xn]݇|4V.7TLuȅ)Xpjө׻wʺ__~YO+BɔNAKQ,R7(nnA0&H yg,R]/uquY|S7HSHh r1yWGD2H& @[k+g3do!_6GuI&@[Rfq?ϒ#_j"?.p1`~Q"3 kEpGH G"{f!x2SPR#?ƍ~P-#t)U VX 7+4/`)kLӹiwϛ=CJFht)r ~񮶟v`ݽ@wWrqx<*WzgWnZ6sңa}0ޡ-1/r/:x flS`B\ÿմiyNhYLŔZbd\󁟴{ҋ[Bl(&,.JbuxDghzs7EwVV5"Hp *-wc pPU_>J뵞va{ *À͏zMqnp z&9ڟcoXfqgp86n"à64VT+Xsl!Ul q!ũ@dA;FшYxuo@|U%dWR {W0GeT,6 G+^*3b j ANFC^( VșpʞLQ!(EB JwB(W5L" B!Fy``P U!"b&!A,B!(ЅWDH{A!P.K4  B(S0H@]k!B!!B#  B!܄!B!TR pU"B!* P`S#C^sN]]ÿ!~8T [kk)``BLfϕJHpg)=SHX$PC-_`?ouuw~#Uh4 mCǃ$u->J&@46g Rn}ݶ_3`c)2HcӇw}g: >r?gml9F6|kN !AeMRs?Z|;;=>24HusT#AtD?IBꡉ~1"yP,.MÞCÃ3ݥ?_ Z7HAМeO [D+.P`'{LÞ3@7KD8]|T0h97?k99Cs/th#:"&?3?iJ=8ߧ3~:BG0qfn~]Idb168.Ł %ܷ :YRT8||tyoFɄ^o0i6Qr(Dx~L4y@^3AF̲`3;HQ1I6O23foǑuqi3ޖѡ~K@D432~8lj4яhNq: x|̯GΎN}^4M;77g~5+6@/@tqDY[b $ml_ABe% "XqAP $b.t+dzdo-QV>BOL/)s~Tl\xtG>g_r2L:h c$IzΥ@_O±u~6EwϿXtrr :o: >w`~ F)QɄ'pqv/L_5$;4obٺuN\7LFyFMjusLkC'!r'/9,K_nlz6"2?L,d9HgG.,T'7\RYIp/KIwRÎhND:s VשBHGS0 Ygnw))襸sh_oߑɉ#n\_r4|( ӡ߇).f50ςcH0+AHqYo8Vz#/sM܎~[ݖ!UOvbUO]oK@?ilLGt_ F&=·7p4*[ H?ZZl|JZtyg6`Ot0]dv3_WDLbB-OrC6^sDc0Pц HD?~h [LVNr '-40?MG~q3 tr~mgMaz?&~;'0uG71(@QrN8tE@$l)<>۰ldǢG,'{ gÝŇ[!g$2dYoёNRQ㗦&'vy>L29\:'kqN`5)041 2 ɺblFtcʭfYtToSݏMĥ~o0B\8&Z;B*0>JC@I@ԛOUrў=Lݡwl5vPo} $U1`eCb1nM---MfM̻P]~~͢ #^8'VG!l-|fTO jPh)rdRkh_M Sf>FGfz\7o.'?69q"wǻYI1D~BAD=A8N_OK `ၬ vOX G6蛺|=u!`Z;r4gO,ϑ;%秠=RI[zᐠ"߲#FYݯz=˲=gV&δ<۲@fc=IrP q&ISq&^qRjMpidq%I\R Kpq %DĘy)gwF?p՚u7~;[}ޱ޻3rRV.^6o5L&THTp>IzNSdiA)1A`c.θL}Sñw-'oK^**wGqăh32g#"$=2}r'Ǚ!Dn#7cƺmu͖YK?/xa=I.ҤJ?5g;h}cYNIk.][^^~ml1MmdݶCmorVdH.7gMuꌏ=.XbK'_6>fSML !1L-)gU<_Y5 LmuMȝĸ ?^Mq%DzHZT`@ǐ~7"))((z)R!kvЦ$&s5AZw; 7>%ķsa)on7ggӾɩ̏H& $((zR_Ojũj~^+$$9&Lr?JI.ά,Ŗc7]kwu 9]nWȍnAȍnW!;6}os[~`}/2f"z$l Ba&&/M2B-'bxͧcg7Ṱ6Ҵ>~i=:3;uy\yC8G *Zh=KQc-OZ:/Cx/ :8_q,YjY=\|q& G_x-|om6['{ _s+"W3ՅBjf;_:""nΤS/fUq3k[#v<`?m(8_#wK@"lO[vp\_<5>?|,!DqbX˳-ld_o__o_g nX'ߔ{=(b9 $Kq.'z'_=%XDL8Z1+4IQfRI 8.ŭ O șe `srL[ߡJ[˾C3Nϡ힃}3m}nz\?&gΏ}h~̲=;c>;4QgGS_Z[ޘ9;#LS}Gk.{BGDc9|Go '{ܟ7(^臣V5lHysO9q#|"ǭϯ%2~i<3@`1qxߓX|:c ΗHF5^RVla~w`Qė9vIito| | %k6 IDATB'[i.L1h`qNk퓽 =l}&oδqB5}/xqAYOM}XӒ+@ډHAJ$Ůpk}ADҜQM u|f4v+GyV|}7'cg{gF1Hg1O|:ue~v龜y67ۄK@,޹76Ds㪡.pa!I5XLx\`Xm x|S"kMUd>=5*2["r?{GQ臣m f;_:"˼ngR֩˗`eM]I<NN77K2w̃i㓭GXռV称m@ɥ$BH Kd"h{N"Q- x $W0RC\T̻=Lй*P1/ԕΗb_'gڱv2|p4M]X,W*;<5k"4pj~+p| H( ;3޾C3}G3}'~hό:f'y& :FUIOQJZjp)0R18 d=A6oF+ѵW`FlO-_Vck7 #,Fi~@we&yY7Ёl GGbvʲGXw<>P W4|hCEЭ `"zNMyG?I[_X|4?<{cfovwu .2Y|H,ngR֩˗`eSO|c$h 0qq$AY +??9'ЁKkSaOO3 ]ǨoHfyVT=H$&~UO؞OQ'B}g(Q:0}9,'#z,4YN9uuuצ ȩ#B|\{HtZ3u&8kn778 vkh68([l9 )$gɹWoO:6=z,<9?whH0paL x4U"q 7:ɥ2hҭLQ@rn 16wB:*h2oc./z$wfWK~^-<מVL:-cb_5S:e'g~o:0%H>E_ =o$I$I<1뚟BH$b_Ƭ?f=|z"o8_^Kuz|$ [kW9#$HJH\"?1='YΗĂL*8uR˭lWr]ڔ&BPЙ]!Τ,IfҥLG_g6s0ԼǶȝHVxdezfeeoL[,C7B,˲,;=#КXMy1 39%FEMO5MSW&wi9lji$ƯGMF΍?'\|W;:R~ˬ&BLO4XҙKA(:7N3~BOL*.?`_ŝ9>4>nqcC}iakw+ $VIuO[tKYenA28G;ߝC\CoY w9vIk:Ҵe;sbg~g0b9 (ਃ1B:v @DM8Hҁ@],[&f"gdГ8>D2O)ݤ/oմ{{@?m?+`f>408`0{=00~tӮ] ;.WMS[m?B)(_.m?k޷zSjf;_: &ș{[|)'^". b6$̐{ͶRl7$~#Ӡ;l,pa3aW.T'܉d"p.= wkwrhiSel  L&"gj3L iISJnN_;_d1߇&.ML6_[&L"8c|?*C# ;ׯ^o>.kg:_XM/h"1JM'F[(ixވ@[`B-]%ͱp%v&ɂ T=A,'$`IAHI?XxdiS(B?[z:xjh[^_z|v4v|vtM?1k>?I_[zǻ?`ofጼlgd'8`dAZ@G#? xO6h 8z)n\p_+\{oW[LfAo*}Q*&ud`gA_} zhc֠hEM"3] "YG6|%-~O1 !~a(*S7ŀﴯ ye!87bcSzAϑ' $qI/^+6S$mY]jl.S udQAubj& S"e. @5'Kq6~!:{,p?Jk~>IG3=NN !pG#4gia#@Gpd>sΟ eI>koL[aIO*bga!`06_ .1G">g>8KXej w@ yy [ޘ)1]Ś->6W̺Y{ ,AS1I_>ߑKG.f{F uM00}#ܲJ.q\ ű.&-Q:򸑨'ҝdFkW!*4L5$]ɻMۚGѺeD")v'ҷ@j}}yr\\pIrB3˰_RIAZ]\wZs_nޔ'JOOGF831UfKgB@ON9XWԵΎN3FMsCiR;2dk t``ڙP;ƝБ\ D2dLc`hAQ)O㓖Ľy7sY82%S";/F|)WV*] FaG}ӇMQxI-wxQ^;RxZ+YS;j?/|_bH6] w#?l *D-ђwd%wEe)b8k4-O$BK8¸Uo6PՠW@ +IS|z5򳽶_tsz60Fdvp$i{>;G !JEVO[NQ`_{ w{ ǂ_$c8j!Fn>P ` qZfwA-l5*B!J!$ FP00.ȷ.GG!Blo'ھ#Y B!>9E޻J +Q Bh _.~dD C2&܈քSB!"Ivt8$ڳv\HqmB\/ Ahh-c!T:$B!238ൠ -E F~x!B ʬz2trO* B!=bԵ)fȺñ'ߒV EXdғB!B  r/׭3wct" )%wjf$Qn416@!BWxÑi ] O?(fo%^0#}ď2HO cJ"  @ɐzUVOZW!TΎt` kvw(u܌۲[` P:;:;O4r\1Cӓ-׮~/*]2Q*(@S'TBKr|HwAAjw6&ע*!$]IwizR l?uh\ߠ76*TƭM@|VDG9@^4L $aomaF>'X~t`ṹ]_]:ӿ/M2 *Uυy_+U}Q2OHaүxyum D~_z]EiPT՗6tq`RXS4`ΟDɬCry2-(,f?la{1Ҁdy>qE Hf9n42?g4H; ;ӧdz8? WFwLߘS l\x` +v_)oĘZZ[ZH O'HQy*g)Iz&.Rů'MK$g[~"mhfwƟ~ۤ{;ML /O*T"N;?h4xOv7>?0X!J!E*7A4*_GK*&XKJ0ҺR#tD4؞JpL:EQr<},ǩd2io+^^\NMcS)1G_I_ |4hڳqt{#1N%RٯræW{T`ĉ)L+=io~-g"Ɨkg>ox<[*Xҿ}}zz^'zx,w\jRmy W5=/!+kĥZ T \"I _{3!限V`SFg힓n2ai}޺vV?rFq= yow!r7@c':pzz=YjGqG/єvjpjө׻Ou=6`W{S%U6}-~K(J7@y Óx b2l~ۤ{; D+ WJ/k,F@ė^W -!T5kSR&-e!|h0P}}xN7B~r`wq؇bdBO`uuGΝ~eTK/_-fn L{OX:J &5 !ܖ |VD)'*Ȃ^~˒˲,|4*+P'+*@d!< yLC-O2|VDGv`K/"!Be`l0[e,܉ I "6(mS`xRҝDj w(Ԯu תWs'yUh$1VWǁ0~}q~:͖7oA|34V'e&]Jr. 8G`CZe)5Lq88xӏ:1Ad dF @yK޸S!BRyhi5ZXLOm:ʈ.[frKIKï߈妙Z ȭׅj GE+{NBP"Et" +(_wA᷁2oWWC$c''36|\$WwJ.rN^/Rl/.#]ӖuM>˲^Ig>`'Hr{nz$H/_dB;Hqyc bQ KnߴhҫrѺt)gUgzh| _ixrʪׅ;S`!j 2aTt :*{˂* Wh)VSl"M~~n))d2nUv"r륮9j|XaD͙-͓g#*D\YJ`Ҝ: ;n-j!uij52E$BU 9*{ʩx6F21 L~ /g/L+h;g8Sp@/[fz3tx1RWQ¸S/¸S_c"vf̺1M\[m!Rjy.܎NFciOj|8vv!72Ygr˲ʥ>vX" ieX%K:o<\.Iz>'$J@b7e {6O@IqccS'yL"xO%~ ~7(f_9}67@WWAY!,.󾦅s=S;hD鎜+9^5M7G2fj|Y,: RW$ 7F͛|Íz9 3 gN?d8r7*>[Hw\m=+U*-mτ?buİ;9fr3)ؼrWSjj#k2"|~gl1|`"kMxdy^~>S*$ykr҂ChJ @iAhHqT˲d4;yamPgg($_"4kxyҶJQ|߫ q72wdyudEoX$i_z49Ǒy3E.woΕ6H #" l%vj3A  oQhk_}J>B;2~J7] G k;^1?S9O߫/?%7/~+>ΟdW{Lح l!JȜGnj ^ZhzXI_!MbES9! (po'$@ UgEN{4cir$tx>zimAr'-B-2!y)V/#81"cgXڬ|m3ǧ.1=2v'~LaeYWiqzy:ߊ$~+mhpDכKwHJ{h2x bb _;3T*;_2M5o ![s/e|ҟ+$q0Tl/t^H$‚0oQ6Lid=_M$KOǓc!$z?c߁Wp}̒7V5[H4X+6P<0M"Wu s;x5Q%7W?_]^&Z-i~D>ʚef rb(^]Mt:xY~Wq6w8\ea!6npʍqMkܽIܦoҚk6?o WÁ-D&KW ϑCW“Ƶ:F!7|iLH cvf_.I vo>~-\Q~7kl&AB7_/Gy4ԚLo-P^d&#1 UHx֡tv>1@44*j +?+(<9T' L?/}Lz|^nyt]@IA$0MqUPw6N@h8sMEͱĵ ° ,8V} * ozƩt+r]@0+Q"Ԟ ȚɔT9u f z 6PkE藊fR [HϮx1@FytkԚGS\D_V@0(?5P7@qlRYAA n TO6 Hh *N !DP ~ sD;=Kv 2vzuQk| RΎWl^)6j ث6K `ں porUYu(-߸f R:+uRP"_4f(-PƏj}YϮo@Q]A6xeA %,QGTWCcBe=eADYf TLN2e=(-L6n%DM'j--qt!vPN$ynϖWsݣ @T= P4Hu!ﳂ*H$;2)A Rr Ȑj41to8 M[4"*еֱlk鰄6u,˲Kh!yI,s8z=#xe;0Lid?}h!d|ȲiI.Q a9˵76>n2@V&\:ѥht)j?hol mlmћͨ6y%B̧3p'WJ;v(˹OuwFMlvۉoV̻ͽf:xJܸp_<_Z\ҰnWw P2yfZ ;b,k*A 9 %e+jo%w0 #]zO{?62m[(q5J璟7풜bO?ZLحX;wia9k5錹,~h4b4܆u 3vɊ/&`tGV#wz Yoc/%љ}I듭g-K!9ezdmPn;q'ҰۜϵjyE_\j(1ȫ҂\ *}^̝MCD 5iaK | [f@;knnv8zִ4ΰm/l3Ұۜ׵bO*'Ij%1r4e\wm#ҿiIa]N$g@qWťW&;ioKea =WA4,6s*&]V]@j$1(OVPZa"~흓&K&/LJE?o aF mmZV:~i#diX6shVQmپsLNZK<{Lf>+!d-O0^ B?3{nnj:u`Q~życёQN? >u:uU, ˲J.kl}1ȡhCSo85.~?3DO|41wZqLf<3'Fa C8IkzϮYm~=gJ\I1KòJ:H>Jރ%ʢ),J WPdaVYzІaTy2MCj6fZm||%r+i>fiXUs,(}j1@i ʋ&2x|0~'<>uK61([_iXQstLONJ]Oo3 M5WEP9!JR1p0 @u L} BV H}jK job?ڲe!+CVUjk 2*4Df4u JY(2'e''UYƳ=+PAcgJ7q)YՓєT\J7_@YUIPWP7F P{p=t^APZPeM5Ę(-Efj/-("j =(-=j h-`6bYl;7t: (݄$]7\@tG7j +H9;YAy)8S' aƣ%tW\uy-n UYFqYdԜؾK ؊dɶ^x ;c-B !nE6RUʷm?0Pb%1~dPPC?"&ťY#ُW|K)^nWΊNL}%c׎_ǜxgL |"f7+}W ^ՖGZĿ[iYr!Ȼ#Ͼ%9/TN欀BDXFm1VM@94fVpLy])iVۛ砈H՞=wֶ&-sz$=fzL|zjn7z^4mCvNZF4@92i 0%EHZKx_/cJp ! DQmݖ ."\LO(iP -݊4Ի{ږEO/oZ\o$v iS>:;~{'=N~[.3x{eGm^+g{m ][~ԝ%w{J0a_;<1@VP)5^A%+(B[+߬zG ]^^nll?nm;knnv8zִ4ΰ4c<+ R~rڬ`3MUIƲ_H$"^KD";s_|{Y\>59~dy~T [GDɆfG g~ B}t7F f06,0̶mۤR ?P+IsFwjٗrˑ?/Ѓ@/2K@C4.(j?QB|>>Gנ[YYIl$D8{+R+}_m6heou Lg_ݞADZtSkٞ~WI A˷_ZZ_aqQũX,6zzTRyu m6ٲ~#%bئ|i4Keũ f[L?lass5):Hgs[@%dD$&1P+ |1ʲAe''Ms$A\&n\7^ zn;ggt{s¤| k处BZTLI:K&VsidZԚ\0Ԕ}3sWzcb[Wzϭ= vt^_.mz-KBBRr;B !fު0AA-wt:9_cXZu\kvGNp8bqq-IugGg+΃NU݇nUFjXA8d9VR"d9}3rw<[ ط@++ $4jlVPs=VicSx<4eozcH@1Љb#<"rOw㞷1##sH$"%gϝ}czd~OC$i\1c( G𯮮6i}m`6= \,//t:G:r' nź{{3g(5F96psZzQtSkT|qh7Xժ[r EX͊/>W />hyAԢL$_}?6L S2֡r۹O%32TE7}SŠټFTFIN ,-4DY=+EfH |jR-&fDJ1+@ZLzTf)x4h ?{?4U>UΨ K j-g@DVm҂ͣ?+P4D rxLZ"+(X +pTPxH JYTF+}I*ci臬>.JTLTПPWrv$0*fW&PhCҲC|EД0YTXJ7Iɠ*'*8cr "kt)urYr~9X>s,ۤnҔleΌo rv2Hk)&{g<7=,̒yJXN}f $A{W.]H$=Ne9_'mz<˲\냔W.'[Dz,kc̘@ y ͇7xЛC|p>YȊLd|YZ\ZZ\ajvvv1?Kp _ *YrYJ p9 3lI:NJ}Vӳi9k1z_k4בp$P?vn#<>zjq۹cK壧F?r\eG!+|v_Y@V3;kv.'G'p8;\֭Y?~kyyQ^[__/HMDv~e~Zv759~dy~TqGw>r;"ڰ<I3juj*>7M9 PhXc 5Ї| AH>$G---ɯqT,==ӝ , Bn*rjʒR$yMNZp3Z"QE/RO[pwp}67^_RsY^r@ ^  z-KBB6q-ğuhL'9z_݊nz_uuiʶ6Ц[ [v01ۘ}vqyӝg-6# HhC'tz]Ɲ:nںuagvuUlӉ&'BY %JaVrYy_v#X-#sg_ێ=2<2lg+7!ss'&BBewu!q7ZҖ;<,;vn,~';7Ʋt[x<>Ḵl6zr_[j + \kj1Wzy_#!.T:jTdZ''M@}D P_'+(Lb h'.7T,HyfFSق! LCT19 HB,8.%HwvUg%p=tjb;SYAT!+r_Y)8r4E 6}P=.W!QHw&@vwB*NNJc_Ac@)(+ %x1TJ Boc+ v +䤠JcgJ7B#(j6YAđD"HDWsM=X`:PSp.tjGoŅZ A_$lkl6ͽ{i7KO1@Mj h-g/ĿAF 8?BB [Dzu"O$B !scYtXB 2 vs'>yX,qzf+vG z11Pk1YRD0I_R-(p9&oqQށb7c? ׅ6A{t)]:ۄ|7ћQA{Sm Tu5ތ,VKp>%sc$e^G;P(XI|:{_>~#*,+ J$~'EȻ#>FB"nϠaw8e{qioy/8Bak,:ԴSzd4FU j8g[6Ke˅?Ov|#=VkRɲgL}v蜝~|d?r듭g-K!x@ js,8Ѿ|ޒ5yen;'_oGnGKXʹrcczfK9i>w>v8##lcԠ3cPDK JYAq ح|IvXנ/߉g@נ[YYIloؾ8ُqWťW&;ioIjA"R\QZy 9yaRdd$607; ^  oX#3 #3vζ6LR#T* @PZ rS]׍ulܧ>c%L^ 6L9rjl!I}6b~L \twuy^mvnK3c=o-ꑽǠ6K h R PZv}m]f⣉ﯟhb?ی8~ܹ > x;smva>[BFGF-;qԯ3'Fa C8N @. Yr~J^@/Tβ,6L]ʲ/S3n}YNxxޑ},G6ͭ洫T*u;U d+tX0}8pv ذe-XNy2%$ geYFGFI{^r<ϲW?zycraٿEZiۜ}V3JG*$',E‘5͊u; ?0Pb%1~L'`h>ާU4IGտ]myE+r(CI$aAywا1ab$J:OdLЧdjliAz􏙢u{=>1߉O{x=i S#-}b(\/26hΓ$0Ge3cb. ^ >u.q|(T֝krڡׇ8Hӝg-6# _c-'L6f}}=|d}ƺw^fc!|;;vR!i`0 nǙ}9aCcqFxYֳ]@ "g4?QIۄ=">7?i>IyqBh!tBOGOܹS:BÆ_/WWW~4ڐ|Ӥfj_G"wFml9E"v+#柙Ϟ>, T|qhtKrLP I(>VT2R0+є +(qX .%כmڰV.X2z^zeTy)ٿEs 6;C`:>UD4(dՂa)XsZ9{}/#T >J,AJ +zvU X[dU' zf+w^>[l8" +쵨JղtX,[%S-MY ҿ]YP (88r#0+PI =O55gHHTSDr#+mYA + uY^dߢ P2+ Y桯4+ZBcAcVlimhkW;U $Ad4? ?{?{E4 _U LJe.@!W5K髁c LR"dEVIPcVmqRdYϮ҂R@VP dP~D :O5[ZtV]ya2XJ+QPTa]2)`Fw  5$כI*+e(h>BQwAU4[jarL 0 ;߮ -feYa -l#t-d鰰u,[ǚ͡kkcǩ뙭^w+|+(V(P ǍПTcV</iN$.cĎv|/x7ћQA{Sm ׅڬѥhfueZ_ŵ6~ +OgcOaح!f>I:T@Y|.+H@u ReV"rڀ VhTW@-Ԍ›[ca@;U $A8Pn (;\J7IɠJ+Q(FcAd4@pBx [%$.2? * o c.ؒ+ fmR AQ M]( X56΍$0L*ڀmȼ}PZi+³S^pbq'$ aƣ%tW\uR#Z;x=+v:J* ΔL^x ;tk.J .@V ~p]P nLn|IZaaX5C'b^g2zawQu#m㸤AM!d|b\Z"5uqbb2v߉Hd]NnlIKj_-Ϭm`c݊eV²k'mPh7sʪĀ K JYA1 !σ;!} mOYYKͨbHmBJbәp8峣8~PkO wFͭfBXZZj+++^O?7+_-B]R;&%VJ$)\:ѥht)j?hol #ttGKKV#?EAڞjGoF7m mpW܊ h ,80D_R(dp`:?H Wlj|s=%^qG1˲#ҹ> zvaw,//Kn;91yͶFʷ)E_mr+ǀ@p5B땗F466wԨ)O;;:ggſ[lsIaB(O{64ɺȗ<"|S/%?:=}ʭ&,-(ji Pl_A4e囕Qh~AˍzfggMM 1klMn ܧ8Kv} o|{ɗwu-E_mr+,1HĔȵH)#T]Z@)t uptwK u FZ-qS^A^4>Y8_pz%xu>kmM>אtBATL5 =N%}"\(*1K+(K %Xl-57 ^  È1|:?O^݊/٥s >>s¤|IsGH>Zo .j[u nn;gc`muKSx<O] 1$m8O,ywDsL:fY_rɵyrI|&;ĸ-Һ O]|mPA .j[ڳM7}ywxYcX0=(;`r6"B1iUz AIQ "r]('0SVSe p WP QԻJ9ii(uY( (=ՄWproP4aYhVPgW›0 HJ HehzFdJN(XJVpoç<Ѫ:+(UB*P d%/_VYj+ T^d  R#Ġ)PuVD ћ LX9FI2Wa42d$cb@Y4V-E'ĥ+-(ץYA9avxtPFDtjUADI7(j02,z3scnʦYKy ÔP} Ũ#q$z"H$h4 v=5h -s7i4=UuVP" AӿbYymlcgOC u%Ѐ=4Ecj +H$DhJq4O6P2ʳ"<0 xsgn @MxqˬQZT?qgP* Fm Ày?*=+@,n"LţvMZSFF)(6M[Xb6¬P8g&i('rT)c cc^YLE5g(-P 7@e. M ɐFn@b=+ eL m4+PX݇yR\Y 9R }r5 + IY!(LWfD6%Rʾvo` <RS=Ƃ(+vRIyy a  !dI›o!P%ՑwGӁH$OAIK w~ߤ⎞cn:qՎvSOYnܸ!#HR*QlimPZ _}t幹{^11I ccu߉KX 5ӳ}{ *ReϮql)~!++EVPjgsO97Bi] _~}LsWI 4tbli)=. Hc}5ZAbƚYZ[UBHWc˱Ύ4PPNA(zs߸qCנl7~ݗ=Tf0uw\ccgF{.-1@iA!+(mY4G]^]=ԛ羋_}!_zƠ|c߮/\p챖M$ f[ZPS&-+(L+Qe>?tЗ B_.t4\s_ݰt q;|E(buWQ%J hc_n/B‘ȉ#RC,? Gꑣ΃ԃI]?Z ϻ%Ҋ+( }i6@iСeG\鴦泧FUwi4csATchivGyJǕ@54D@ SKK8ڝL@.˛2'mTce'' q) +(wBoWPJ`v/$AVD+Y0>E $BP ?ΔE}Š0+@1Ԅ*hVPr1u# "9LC CXƘZ)W| 9($H(Pi9I(,QuVPip=}ƚK^9@_A +(XYP#%+ yh$HI]d@tYhl\LVP^4" JV !+ c*?7JU*LN %>~b sv?rP"Af ~ ]y}Vc듭fuYApIPBV=5&BHFO يl^}i^Ѿ Yќ=wβB}AGE !S Yo8Hjϝ-1i+HCu;([}V0@̀vygG2uONpwd-7D+'ɐ;0L]@JT^ł `@YҝW"[ۧ(ЖH5Ym7CwTJȈb}/w*\";-DqbR:utNX[h/<;[5ł @R=JmYA +(m7P #5&CVPzcMWZ*@YA1hjt> ߺs'Ֆ|PTcJůXꋿtնNK8QQPF.fv/sbXPCd.(X} ɥŴE`5܃}Gą{ V;oOhi7˳{C_.H>9bc#J4 2l姪14dPuſGNzc !yw?"ssW}_ȱAq}k SIќxkD/m>+ M (MVLDIN&͆7X΅_/HYu3ǡ23ϔ .+* !˷7^6[ۆլH$ӕj)TY)WBatltd\R־>&omVG@Gۻ80㳒H)) (Bgɍ;#!9G,3 2Mwi.b "@EItnn=3Sŗ qiEnwӏozo%c*zS祵cD.[VͭiT "Tp1ãɋk+|_[mm6[K 3H[*"px Qf d0PXpX ~8)hT{c@*FiW 3Z8=  ES+X;SL!ULՇl/6xQ"ӢDLrO?:[vT#Ğ g1 \o.0 / kA sY܆8ek;~@+1^(L<4mC) ~'p+ȲzA*0U6%w 8ã%)Oũ,d_!8PK_C ~*Ы4 ւcsZTcR`Hl!ѺsK*t]`3&_;[H*&˲xL6hԸH[*B*p`OG@,tSQ "8kMͦ`hDw?cEph9&|GuJ&k^RL8 ;dN_si>z4@def''K Mh+.{[6r}Ffo_mU{L' =~7=h*Yb,[*u,q^^odZB?o{w#|k"Z2nb7DɿLjkKIENDB`./oar-2.5.2/sources/api/livecd_snapshot1.png0000644000175000017500000020516011757171206016775 0ustar plbplbPNG  IHDRɊBX pHYsq́ IDATxyEa;r%ûuu^$&{SG:Dy%^p #g  Xu I#vK!^i P|ޞӽ`ATW=U|!B!d߳ڵk6B!2NgՆfB!Bixߜ"8 ?,pB!B*6>ΞhR7<~o}|nB!RqW'wRg{խtx^eY`CBFൔDmM,ʵ-B!˲`C̋ mr^H/¼t0"'70n5 !GOz>q2Mws B!9x݊O NS$䍿u[o?_Rz v4f"!dԗ=}ѓ[G{bob0ބ/p^o[KݽnK$+xS[ZrE [7,7>W^ V%aĀB2ss>s"<8s_?7{I,BHvG3`7!KwuKSN_ LwS,X^MX&BgUBIˑ9/xnn'UC6oLQs ޢw3X3K!$G>s79g|B.eT9JYɽ)~a0zg+DfJ;BZBHyXB~߿ _ 3-OYpڵAfigNB![sSxg|W183B!2NLwJt+_1B!{_ۄz#T)2ʣ^ ț^h6r gyz|'>97kT%`aa!{!B!哋1*DU !Q+U\UJ/ B!R>1n!$psxzgWW#En5-@BJ; fi9yr Jғ"aMTRmFh^a=R}|neS54wea&?@Xۂock `GEB*{Kww"{qkƥ\U%Z.,i*)!{O>v9N^Gim j ڦHVG{7P"zp`w`뵋Ft2@N^V}\mO}mŰRkm^X&U!$=Bݼ ϰŶіcM=]8) E!dߢk.#u*M:j(/8}Kpqu} Sma뵋r3_??|n_;taXU|BH4EӏhkF5|jĊ :Jb _>dQ0ypݛ#LQD+Wٟj&!  j$O/y*}@o 'k, %EO^ c3cvrʊ*M}`"k5+ͦm&BFp:HH (O/vq Zdk/>^v_Lm_xBB!B'7)Ѱ4n Y}Αv)#s6Xĉ?ygCߞ?ϝIGD!B>; /Us}/NGH={!nT4!BٟamVSWsWbU`MF_B!XDmF9 zUJD!B2RK?XN|xgʮ:R!BHЍSkHU`A Q+G?7R O ٭ɥB!Sf^*xN|r33k~j@B!QSi{o?S VNO-$%jMU % \;_,y[A!|w?>=/Z>%0GUBm@u踁6#UAIDdA߫ y\UL +6?X/!BNS4??y=gN %N![ڢ)e8`2vRU\4,\ FQ! y %b7=qާ,l !R8˒.A}q %8BQ4R91:09%4WK%@!T'>QMjmF(ݯoϥ<%\`~#Ը_B! kg ?XWUPR,AU>rђ-RLzXVҕB!1<wm`!LX~A3O'TKE! ;k|Ruڂ ,*=uK_,ǘ.S%F!r,$" {ltΰ*28 .U ^68':g^[yV B!Y4'' B.TOb/d4ak{kw0UBF*k!BOuJ 8YThu\=w|um1"Uϟ?rs=YA!ۂ||I >++yW$ v+{5UjbR.,]BT~"(v Ҵ3߶z?>T#/|G=VGJqDrsaau\RSt[ΩN,CJde3 !}Mml˟$ŕ[klmo SDs r$⼨Vq~UBjIf )UAQt3Yt/N{3M1JUӥ1pQ׃quܚX3o*_iJドE6  =f &pAH~:t+[_UBHEɦ ~V$Ϊ"As:#;9}QHJ㕉Fž "ک] ;qd{>Q'~3o2wB!!P:D(UOx!Yy/Ρƹakfr=Vө k΁9 -K~hjdKwSTLI!_4u!ݤXAUUgfz6>gSXGAQV'%%bXMl9 !EFmH4`S*@~ޟ6/h.j2w{{,EB"ÞQ}Ag:$ğKu:-*Iy)9VˀqyVPQ%Ǟ⢯:UB)A Mᾙ[p򫔵G~k~_bҮ՞DUH;J:eF:)N]fDoTf3 !27*@s |%RɰO#bHpuCcmP+;<,,8c]uJ BHzCҭWTq4^'B!\;*dddTb{O֢uBH-z/ͅɛ&Q s$vDPe '<фB)ļMȍ<#X Mh҂j{efW}kB܉'#I׿80e ER_yd@_nAg΃ >US9帆֭_4['ԶB!&ݦdhV)yF 7c"}kwGrߺ( eRIB!@+zpxJH UYkVhE k̻ࠝ} *ԩA"}OF= ׫ܺTjUX8^x&*3ߑ0fɫ*R.D!nOUYg)̇:i|8O"Caѵh^h6ߊ2R!X|@a?6`8"ؕIud!KJiR²%l)%m]ZV Ǿb"9`ϛ')~V)M ]̂&={"ĈT`cOW!2_umr"wU`#96-)-\<4+kҶ`KKJiCaK)hl^~s\؆_D1j3Mc,MsBst_G,M?s77'S$Ftr2B!D.jBv!BHq@;r$`R3Ao>.|]f!4іTh1W8 X a** KĀB!@D_| o0BOU 1?,@,h9vI)+Eh(@ȢأgH1-h*Q%k"B!a}ɀw`0P5WmM#[%M*,Ճ-6q-4M4 X?>X;̱ Ry= DC-n/1cA^ޔEB!d_-*DG3@ 3xhAѸ{gȇ+hݥng3C;28@(*_)sG B!lأB!BJCړ ʩ>Z8-]2:ѿP r<2wz,.l~vjB8Y!b1!S|HB!| Y09CC$m3jAY4ڋ o,.-{oYп2{oKm=SӚM@oϔ Xg-B!T W**ڎJU|&n c{3T΁weuزf_JS:%0]t0f5 \} RC¢c@!R'.xp1Dk:ĨhJ=aSr˙6 tܸվD#dWC8) %`ϬJ PD>3!vGtB!RA2:Y PwM7Z7MUiMˇXTδc@f%DSxF1ͨ@A^Ĵ3Gq>fB!Rdۮ9MY `ـwP͖ ͵g/-.ZP%M!FUYB!ʓY;C2/h,]6JL"BMG?XE1 B!$wb6X@F9y H߮[ !DKb R! !v74G4(!oIGgof.{e^vfݻW vujtڢy$' R1 Bчκq hݥL6!`K4 M 4BfYag<#72 @d%zG*wH7?x 3R,\uU9n t8d} !)q=ġET(_4y%4MuFm,:׉ kL8rΚ0;2ݔ`f26d"W{)`'/X|i>[ɷRe$4Ʉ⧄bU wHϮ*60Cy Uph΁  7*|ݣlb/G<ѺCg"_*"4%_u5:#J܋*{2!dQVa )t#.hSJ7Xxk@Lv3iKng[7wK $%O IDATB*ԩ`ED5L U)kL,WKz^YтMע0S#-J4H/i8eF=Utz)"oviQX/!W]06,gLJ\9uELUnwU`;˕zXZv>ʃܔc(!@Sȃ 20)9Jo3)k[QDX|%$*3Ef:e'Q~sJ8.)Z4ۮjv;7o%BZs^uZtrF"g ͠B"TA+}x/.-7{olOdh<ͭ-gd`7P!Y*@)o=JZQ??I .WHJ:1C rt5{'P\ṣ!)ZT~rՕR/s {/!('*>AV !;W,\.uFh[3ò^t=pU1ͥ=y/0{l>+v0fHg΄f'G(hUvgѤJ6FWLNV^Q+* 8LyAL5 |^o F- vݡn] bd.s}npeKp4"U*Auqr~j/,z`Iѿ*hv0"BwseFW]{dCǤRaBw\4ҼV݃]zD_۾n^9(,g hx*~Sa72Gubˌ.G럾DrNYfE\eFXtvYjڙԁ&I|+͕h[t)ѝ9ڼɄ2g&N Gۯn_b`57-oW_^ա%Wp'mx[&Lu'b@I:qok!kJYWt ,B.z41$&(;~dl{ĥcRЪ0; 04S\!J;Klg+gDC{x'B,~ҩ<1Vn0?o s%`c34݃݃ClK=V*PO xSVަݟ e"Hq5!:O2{qvJ BY_I/o%lW{XN} 7ttե\|^DbDzëv %deJudFzNb|Y+A݇6C}`xS'9TX4dzNj%Bm>5Z){:ܬ-M&{ZɞwH27 j,(̧ Tq3r ߛL90Ғ!4cpuAJtrmlHiv;({A,ޣԥc,EJAfլ(մRqRhvg!v!TӔL?#=4 9XVn;‚wtiphRERB{+:=BҐ>dITs) i[ee{j6@yvHLP𓳠#諝Y6V=4BҐTxW䨤r'+Jd',8CU"L,ȌkiׇG#dC?\u); Ԓ0gtTA?^򦷍&  4stc3KJH!Y0GcSJ9@36HX#E$NM & g"~]ĹZ7cq/yB!>*(+.V9xg=objHےIL>i,ߴb08㎤&Ðſ|glٲmm!D:1jJRYR%b1P$\/zumk(sF,3)iT>"Mg|X)zGN(9Nj!ak]|읽"!Ut͠dtEab֎*UG!!vT!OԪî ]Æh 햁F:\ FF!h3@߿ ȕ/o$AE CeP*@._bF8^<)=a:,"gN+Y'[i(bͶgƊv{xabE=Bmr'*>й-slBJiKXef=pWhw hzEh #deOÊ̙t^Zt] /!ʡ(/SPUHӌ"NzG[˘B !zTmA+.3@JiӁFrvD%ĕ_ݮDӈQ%b1!-XEQKۋ0I4NE{#pߧ)芔hvr5:ͮ6^9.G;cNBH-EO#|Pa7e46<r,&QDCݥb`,ܟTEJ=q>%Pr3uѡT"RtCΫ5NP:$?jx``! ;-2Ͷ!6>aDjog7뚎0ߵ;`hJghvoX.uGcyWƁݥ. c7f@DAO#1aNʫчt] 55&*<a֤$+*u6r"Ou:Aj.BH5*[Tl< Vnnܵ!&|d@h@B#s{%CYc)/Nr@'jöW+4U굏o@ו%F},{x$ۮ4Ii|La]Yu1Io{lbaٯd%'+K G4/EӁl_.BQ8b{_PAtH *p 0lpup}l &l :3 H@tZblH[h<2Msdwp0/]B,i0 gEci:4GDjȞKa#.bSܲ&Ǥe lN]tBHvy ِÓ| 葫 wpg{ん02M)GGBӽrlb${Nl ]\i3%R(gU"M<'^ {ޣ!9!jfIvH}CU,B [Z5i6!'O7vMa; )JT7 .og-KBסHHmZPO*pUPu,@_)S#Z$iX2N >h˿3 ɍCbQU- * &ۨyP tNt.,,3(3-x:͌&WE&c}TTknAY$bqe1]Ҁ\5>}.D9A2TTĠ7}_`YҕU'TO^_y Hũ"[ Pc႐l:*@s j=^ Z]VfaER_ԥj^ \ }K^*؁_V\'_ LjApL zӠ`EIQگYfҧSFEw2c+ &zˉ=nK KÖagGYWS};u*?G"da!pyg1I4;s!pQṴ}ߕwEQmmtEaNv.3$(f,H$cP*@|^L ޗ! >؊"DM/3E`Vr0Leb|Ï+8e/3C{IiƄԤDyxٚ9韸"n8Nq`tEIo:e&WrTߗAJE*@s kؤe4 &58T&"I/X_"vNf %jRiЊR|6WS[/!5Bj *NTA [IuSñBbiF#p52Vdg'ͬ]Ӥo\+RfI{@HBͤ=Al]&QR;Wx.GYth; Ø#u3#sof Uߴػ~-wE|k`_BQv|bnuM;ݾ MG"߈U1щѵh{s$lagJ'Oj;5OaXdsI#\̰v öR;":"& cw+?c\)o!BDLG.X3מŬ2cyd@_nAg΃ >UOSlcmĈAef ]9e63"}43N}.X;৾^Jdfv.\LKxZEAagG}W&*oD4 "/!kphTlyZ#1|~h>QlD# 3g +P ͟8#s5͐ڰH^+iYU%QL聭uR˕VgFu-oCR#m{ЯSSOqdMIh@91M%Y*@ܟ=2ɗͬ騦fIzxڳJselɽT?Zm#uT„A.aB]N˥˵Tk8%8~ I䥤9ʙ]TBJ; fŞi9y}t;+k9Ep&aMTRI䲏AD3sl yބY|7ͬS@ 9c([KQ`6.:2[K E\K9 3&̤٬S6M)F!3XQ&gNAG,`\\94}SweWw:0rW?8h TeAߒ9eގptNriB'(a'fy8)9^+ v[W w`u|k{KMU %:^*/asBdW={s5:hfzOj_?Y],h> 85)Ē%D %rH}TpK kVV:ORz>cVt̽"*oHKWtq"̓ԛ$$U\@ //vB%BYkGЇƇggL (b}O7M`(EQlj*b~aiPj)G\oH-%-ZUxHqTZ>vrNmovOkl^ۻ!uSe-D˕5{tc?+zWf75M(K}u2g`F&9h‚2[uXR=\9*C"eGI$HJB)x</(TA}kOuMU2~ڑ w;@9Il6Fs.W5U c~<#~f"rz? (&UNe}xwqptAtG$u]I RB*HEņoaBob%* Y4ϨBݯ@ ݕcW3%m@%DȀ75!l|| @ٯ}/p>m_\ F}}N!.L'i2V%ʂ0!U`?Qu6=2Ƨ ,UbhܱNKwc7>">")GcKee݌^|K ;zw=w>xIl6qVGnOlB#yY T vH63dZQ]稲hC^V!ddT %E_XPy1x:o >|Uqh p]lj40k×\rjYڣ PUbqCV-l11=JCgV,̠sƸ.o0zla$jKO2F=cI@f"چAS@!AP2P;5*Pmv&ulzRZliZdcL4EK m %btoj~hd-h–N ygzkѯ=e]3}3fVtְڣ;$ 9 IDATh O}vf !@c *(l6п2X `4@Sڐ;M4o(i r{K?6_`XhHbKl"88l"1J0+>]$L+üDM?DIkw3{}n}1:k~'ѿ6 #GD!n{9 P5Gj 6߸$}7`-j[ H!aXvGZ!-@" vdg^ln|rٚ\2 9 )%4 p?vNUA0C-C@cAc"!M"›b삫 U*1 B!U{*ԋt˗ zUa 骺 Mтhy` CNN!;dτ0Uzc@!R_/G ֥T^ ֘X|+Y\햀 =Xadb2p[h!f  !BH [$O u(X}4ј>o D8j 1nAMm..DȔ%ҒwϯhR1Ѐ0(CUp!BHnx֩M1؋`DJDm!!60LSnl_1`-,v&HJHk&`n )PΈ>5YDŽB!$+8f?[ht3e@[Vhh5>=!9i]|ũA`-Ѽ$MU|w>H!BT*h%r%:F0Vc@w8'_MrPl&D BfCh jт x5I_ Y&B! WVMN.t:*Eآ{PwܵlF-4-!B&FϭBgtX);B!B@9Ȅh4!@>qkL U!B*Dvw6˛ɟ럑 JU`+2 `MC#!-1vUJ)Pus Dzs)Z !%7aJGrePv*c@!RCN%d/v'g%) z HXbi[4D 7EJ¯ \}s/} 1hM!йy}`0hɣu;#h@ #B!GԨPꊪ8r#z }{.8T.wu}hdy8tW{fhbVT $/B!2ٝPm%^T(V 4_ H+׿[uݽ} =sW^%p@n|[_҆TǀB!H6NHCP^U``Ҷe,^!CߗRbʷG'tڊ}}wnQr8nB!d_ci1~ew ܑ.:C,")Dgix7%__{\&@*ۛGt<S51`݈ىʜHU!B#d_2!=(0P+U`wԔ#GA 9C!R6 (Q)L9ʅ0[P{"IW"g8"^"TTrX7]ܚB!5$f_{lNlP\\U8k av/#p!BF( } bXA)F |wM9c?"B 71B2xNHʤgnx|*4^f9J30)ن6<=0 w>&B+)f CUK C"M~{'wgޅ[O~S<L\y1؊&.?lNB!KYh2hic q#Upꉓi8ѕ[V'4tUlU(9T!Bީ1ԌW/*X7_ZZZ:s_O K)[+.whi/cUnnOJ@ҨpSB!_<L)b4M?}lC+sm k[X[^vw|lj`9_KbP#B!} aV$x˟t#h /L?|E|򡏭uKicc !zg33)?U=1`@B!DvWS/|CG:{wGxGvYZwk Ml6vwAkw,.^U0C@*;bܲ 7ӗWUmf &I!2OǍ_ g h~Yx)bmovhVaͶ>tO\b=GPS}1pv%stLcI!RE pA*px> ~߾qcƳ|VJylg}eG>wlk{[QK;fhkkkÓ!*@|e 2zS^i܂B!U$yXt|JU@xOzZJyͣo<}i)#wG=my'y EM 0wbg㮍c7\T*r$؊!BH(2Wޏ<+>q)962?u3Oɱ<|Ǿ?7/l09t,zAı{aUkOE r`@B!"Kihvv?cyۿm0X^N \y0_v}|s#9LAQ B!r?:yK @sV}Q4͏ڍ{wKʜ:E`g]R6xA(~B!OyeWvD(1;_=|LJya6H `CJ_uV&Tj*!B4w@v;CwvF}xFH$U滣37nX4wFP>Ei rB!D|=۶ ~X,nڻrNë;7/8{=-ͭBOg:u_vD` #p8Hs!/(`,RifUB4tR᝿>䱍{w&UA/=>tI9yY9;FB!Q5*}#96G0:{6/>hŃK~3ޙ1ȥ숁O0<2rzS4sz3>;Vqxa"$B!TC* zc^"\rụOٓG?vڥui'#?fvo7Ή !v-w(u;'v(7gt戜MRtTntEB!4 NfFDyI-q{O~ ݃Yc勧x4 h*ԪDj+-ӛ'B "BPh6p駗'ßۧvB<8s;{I,BJkGg|F-"0!8?՝cA8B!Fsqd6j,O$UՄthß:rÆϮ|xOru;/Kwc.|oK0=D;?șzQ)!=ŴQJs海0UZ}},\:񕏬Ca{Hw?~p9vd’JU9ck隓SԮOc+!N#\uLWfRp'wU.6t;/^xf?*SxT(y3=;L?:M~ʌ8ue"| "7?g,B!tZ Gr2S_<q:??0 #,ϑP(?b.΃s%2)Le̱e*{ TB!Ԃ6:ȣF;f-ZMrc콺otng|&߽:Kb6=G^˕rcB!Ȑ?5)pLwE'N`08}Ρ/*F|S|;[cbP~,Y4@+_|FN!YqgJP _y9G޿_ueiiI9?tZv9שM/|>mD՞9VIɈ!BHx Ct*OMN4TsW,FJy+g.|9iOf 3_>VnYp '%{oU|'7ĄB~FzpukhE*V`s0yO|,7I)wٴ^x{G<ݛ+wϿzjtd )Ai+O!\PC;6So[<C&U|d|?zD>o#׉Qկ}j[VgMqJg>?/LURAAѧ BH4^?Ow?tpQSRJ\ʳ|cjxԽ{!g:x<?{ܻY\*Qptӑjװr&}F0B!{`!lܗ!ŹyE`v&"[q`F׶zg>Z]`A¹04e7栢렢뢦⢦8rQrPJHJXqkIŋ-- 袮nĠqĠ1<ƋQ7D(7~4gO(kkg&~=fx^6`NjoYP/Urꪹzj~AAB:VAel$fsK>S[@~۠qWK^i"C*|,E)@J"[@A)~=LgV*Xϙa٥8e/L\ySh  rJοUC*ZtVd[r}& D3gΞ&p*,Ov%BAA6cL_Jj[-SŦfW.JZ96d%0aqέmZ|ٵYPq5,Ge'V~#WQ>K&غJC Lc%YkA* ]u`l$IADScPRDH.HMsʶ.,[  &Q:@LE `'D .r @3mH~wVMn 9 IDATI:^LNժVs+_X\Áߜ@y7AA-i}eeXck9iJqEW[)r2AAE4ъ VPp jAX@dZ0ʍUPs<΍K}  ~bȭ=܃pH6YX{-9͗­s1  Hu0g]n10Ǥ "Oϕj`d7?Q3̲[ %ujT@V6EGAA2" [B6+kl ؉#*?f/J1=%2DuR6aJ;KER_5 l=V& zҼغ9hAM1jEM9XxVI)Rs" R{d2DS*.yPV- 8K*bW"zs lȴ=\,A:(kdQj).@&ڗܣXb1Q6} AAL ߮+Bj襌kt+z 8}#2e>+GJKRIEUʩM4A141A @i*{ Z_^]:ct- 1  H0/! ZT) O73n4,_VU0\  +h!_=*cـ.*nW"S(^h]  RQHz%SA]7mo@EP I+1@A4Ӧ,J6NEj1pAA@fM/JiAY,XkO $T aTzXdipP[չRqRK%ۖzt&UoKS"IsW"[\z9t!d,d? m])INr/[ّQoQv ZC *ޒHLfCNqb4Fr5m62[c@'PZ-X6*ҤEcjUR(⺺7̅ޫש2Z/r7]$%B4nZJC7JUFLv  \ͼWACas^MԶE=$.] ՃD鯨yYײ Wd*˅9<)YCAAjR)Fّ g 0x 3b  ^3:Mۉ{bw1=s)BA$: t*eU@69r?h  T3jl:ZVvSZ6 Gs VyAAVAw+RH`B2gyQ_nsÔpAAjO*Ϊa r%XC$E,;I姊/LC AAbvJ6 @~?M%2g^aQ  HM ڠ[JR**ؠrJZ~ JDuAggE@:-T+D?(g}^  &bʯg BAc{; AA1יD/p   h   bj(Q]]1*o)  R<*wZ  R24 zhAc;"Rp_E*Jwwi'P/E)+\z^@C"Audkd3dR{e6"> Uăѐ{30 *E_7\μ=h  J~XP%[i<pcHgD^?qVCAhD7<Ry)B@cq٢"Jl5DSjyQ"74EHA&NˆԭSL2K:`s\4tvXquGaaz:ДBRSo$5EjTʔaJ\#)?{d!RL)U+;m!Hm#R/B(a @"[_=_6RIy?*ʬQ-E4[zGX_J 5"sB3AATaet5 QGAA* 3 !  b:gTI@AA1ݔ@:!/R $/ȩ0;+AAL,&J4QYSRD)ʩ8h; A Y߳R  &R_bmWISz 7  R E< LjMXR0/[AA*‚2 T,H ̆b(  R-T֮DϏ4!/>6("  ӳgW.]f- ȵoӑt ]"I3*J8cAA8<*sӲMukY5RťH+,J\x|gͷ\ U+R sa)<8kTH#OkX d ɮĹ͞?w.M9p\Q})PzDDwi1Vtz+h~nX-0=JZ6si4| e1׈>rh dgb9 YiJQ$G,!ϒ[JYTQ8J8;=v^xnrtVO)DkaىNt3R[Ay(b3ΦYB("_UyV.=Ɯ #Uj+FuLoB!eјSZ,Dꐝ:m=Oy/+b6&Ϟs"z_z;rnBc~y; V ݧ{2>\Zw4M5;6uO7n?[n6>rnwFm(3Wg]Na6~yz N>)^[.7{?48rdL݌ݸL*5N/H$ߟO/vH= <udX6rz½hV@E0-, 28溉}t飶mG] >]w7pB |G_?r9UjB:`צ!BˇqVoFM1ӊ HuaP\Z+E9;E)9Q˴.i Udp*Sgçv_ FGeioo(:7:Ѵ7 /vG.ȹEydgq1WE^b]^D]^2Tj%Ge뙽::ϏRtncdZo=&aM x9w#Pltow 8F !6^&>^NĢf+EY`_0 sRZS;zm*wgC'WwY R0w%剉K{0:XuЕS"*.J昅sâ:>Nh%$ 󴛲@|qpAۚ$S~577^ȯ‘K/ [Ó]B*J?36ndt!oK H`WX~p۝Ѱ(_F=8y Y%ӿz*E~;~߸yyMc[IiMhLYb9Z>T:-AC6Gח0ꪹ40D_:+ %9R3oRikUl@Nb(@P*DnN9ߠ@3Ӝ+tBDݑsȹbw(JQm赨z[ZFOGbl-4rMf b KMO7Agh݂ H-q"#jf}D!k)}gdrJᶼ{M_d)a ŁqO#BLUZ\Ir[)eͫJh)KN-GR:1QB>."h4+Ұkf3آ1Ls;vؙKRTTEY-:y93@&4[Ҝ*D+,PSTtTW3ZeGI͠>o-eUDkVUP"x' "nS @[P ەEULSLZbtA>m IDATAjDP`P]@BE@AP30؄-T?7k"NE  HDžPR@I1Q5 zc5.XDHͻZBиRA&  XTǢ]K)>כ]{ w:lby@*<AA!0c%8DʮgW88>CA5ҝy Mn`#ۺNbP:W{4'.ҵ[$]P DI qyiAcTAADm^!aHYY|J<[7F"DrdɌ_JKlҡ+ƽqUv/!e#QY-Mxl* dsʶQ MD>aywu،2>o]lE&yds`{h|߶Vήџ^Rhi.wwϝ 8gҩP!ݻR`]{DX(]ssr|b6v}LAd)Q6Jb[/}u3gH}cMJlR L6R-HYvL4&4!ʦGl=5$ۺJW&RIxeYf# 9ۚ]-zCmawvj>1v#x.KsmtǛ]UپIL3YHN#vv{O?>pls uU`( EYX,`Ņ0Y$  Cz t@ZQAU`TD/+ H\W uL ,u {X+o/{g6ਧ<{\}b L&%D냳 7g//bx>v(o$s'C'\Y#g# _Ptjaq1X(Gv;XrAt50pnwC\SO'e&xtmv&Qk0R훩D*^ai%eFǭ"~ME1 %}>0+ ^Ts;HEإ;lQ_T3&²lXvnw4L6ӝHlV $KigUb.۱WO &ǂQ>o5 .b$iW,g?3I+$fǃs rr^SՌ,CNq=ovawPVmQ-KmW+f8{*i[̎OnY.~'}w䭑rKҭ8Jڟ D./%pmE Ơv=ƜVNJ)|%gU_,OFlGce;HMM^?0}m# )M!FǞj6weS)gZ!̗dsd71=Ccq; b0j`b|0Ӷrg烉7{`t4l̈́H8o|k%͖ aYkٛH,#URd2GÚ^^g;^6?AF[ޑ>P&c L&$5;=nv%g]O/T*Uַ8?genT3`!l.`@63.N^nkX)LF;#skC~qț{hڬ y]f2{m.;R@QVlxsIzw13 rҼsO؎d@و`앮S#hG<>ĩޖSc?&O'>INMsz)6|;r6wbhTEfaw89S+ ELr%rr0y"f` rϫ=^i:<t4VRsd2kdV3F.Fۖ,el69>l 0+IgmPLvx>^jut/3FxɈދ,FڤCmp$Qε ]n;:8 ?r7?K2RtK]A-qxlA*6?|!/uet$ܛ8EQVEAl. ,˦P06S-96*dM$(DWӣ=/vll6c?\U N5yI[\z 6Ôtt*] %FE9sEA>| _n*U(̬^\"_Q.r3 lNc"mf.F;K 1$GH.CH!)B2l.ÒGz5Z7I><3:;z'?hZ‘kk;ZBk}7R+Vzg:Y_5wNtm=l"KQ66єt!kS QvgtV nHck**7GT:4j%T:508cM8Jٮ?O_K۝և TMșQGCuMl,K_rG=bZwi l6B5B ]7W{ml6k ôl>l{@kK iGTZ6j{c7T`uuu#o8upeIYDšMYQ͡C_YVǣɍ<#,{oDDNgCu 7\OJǿYPjZVxHnECKQyE0S@"RwQOǻIVk1ºI5\ Nff6%O§±Xk͇OC'BSS¯fX ,K{+[mVn&#, L0 [ ;kϩ!-nj֓f%g#oXNa& OӇ63Ewژ8`I%)veƃ,IV%W+q.C[;C;-#Ṯ@ծծ@k]{CCsG^1zUm0pw66=ְ8]4MQD9JRѩ0 zW7z]'Fz=n b 1owqG{C\  0n>rr7c3 ڡh bU?@^hazb\藈1ץ]/tQ/9.t*/_C1 H?y >^-}|V_kk7́ҎrgG|K_T`Y\^ZjSɖg$ RmΊj^)aZ~ږ+7yolc?j?ھϷ/h<xT7vY g-D/ ̵Bh\G W"z]>'rgƏHvҽ5~fVf9JBئZ̿eY6fد̷hº`$yub,yujxgG<;7?>qz,yz 1 ( ;i:J$SާmI?G3v:RAO{ {w/u1o w=j>発8Sd̄癵J&Ocqǣ=ɍ_|~CcGZ(*E:NL VGE_[ʪeSI yN6 kfWX =K2Z[e|r=@FA SE8ud0 t(i` Ch]eos]H$_2#"Z?Itͭ)>2~+}lwFL=t$Skp8+0i#vMuIro="UYZ!h9 Woġ3KNoD~ާ#{?haHͥ6r6ss嶩SW ͵섁0p'L. ݑS==b4v(ud@\zd͇POSek0ȰCub#cS=Wv[Wmn78TT䘁5E7v h4sgmxyF(6!;Im,ؘ5+VU{\ks3 ͻ NodesX/{_M-[#Jizݱփ2b+գ"pX%H:d;+FFGoMٌ`f2b@¿?yhf ZcOe)ʚa3OLi_eyۀx]ey]VTLgY~lep,;4\~5}qT8Y;~>BR_f;/NF+4 (QћcBXTWIՄzCM筰\9KY\5*!ᔼV4T ]ⷣMO`RdOm|AYhɄόk?430pj60xx~ratpá1c]ԤyEoH4Eش5q?JBSOĵDzt;1Xܺׯ6l0o4} Dsôm'_J*E]`Xr9 v/nQ'hf|p:(s7LEG>طٙ{ rg}ؚ*oiS.@JhL%H:d;+TA}o( %kmdUuߟ&/26S&:1Z!d:ҭOl@$˵EoW]̗֑.FtGQbۚE?N'L+B[a mM*dsi _cPWW7{y6T (d]T!5j Ɔt"¯JC|D2^yxbfq,NB!4< >ySoɕ@DZ~ԇGcGcSD[C&ŶB$83_B {4YS Xnz$Nv9_Y$%*h~BZIˡk]Nk 6qOu+Ϭm@M+B̛L7{M| m*SD jOLLϫ=G7V]sL&,G\Hs=WJOsk/R_2Lka##;JG{rr[ȯvS$R=Sɦ4N6igE5{3L&A?_aޯhQB6m/~}7).h Yfbn#t.u, ̣-?ǮAx'ٹWW٫n;ozj4fٹYisfiT*55=eb_4&vɦm<< t6o&.N$>K:;}7?PBi-KWZúV#5CœUr Um)%9lyDGڅ/PN g.xj@M6_<-N&Hhb:&xD?dRI7ulU1rl>t`;{MCL\hswǒVVQYT|<L\gR+$B3 LǛ L9iJQޡ.$O4_2i4{Fw4O^F_ӻk˕cΎcHZ`mB1G6'+)$F# t$YiݑZqO'/q:۬Gh&llj1D/eoˎߠm/Ecyo>284hwؿ >8VIad;'{^qf;^04k㽜![1s9Lz! [QPZeǻгn< <1⍡]a*aL!렩 a'!d-򁹓( ,:;vvq`q ?<[ɓ=MN%_ npC( XOS,Cۜ}2Ι,Cr,ۄkdfVu}JbcA*ʰX۵mxfD 1tʾ9l6Je}a{:5vjӿ IDAT`P_~ 9:[.aM/9r}K[JV+Y,dVСGH?`Wb!Wc{=t=Ůȑ,{%~A[- V&L  CXvMϲ] ;mHP@m8aNұҶfȭЗv76[W> P{W- UYV7 ϵyilpljcmã.bTE4ǃg#6`]{ӗ0 %'24Nx#th*牖46kvmB~f䀬fXfRM#4]O[ox?,xzH '@9N q[# kJp 9@k;_Pڜw~d5б8"Sϟ,xz<{lY94~i(blY i#t H⤛":4d)QYz45L]f&z^^ 9@9W=q\vM7 X*; h=̳;i@R+)6NcENFtނI{^Ͱfrp!hڟD_{-q$ M@ӠH ORB[W[ыuEu4XVUB!,mt=eճ` hrieVYJ s&0}#Nu_AVIp&ɁPGCKcE)6[' AT1%NN/Q_q+DCћ0ZOY){cM[(MIC6glO@`(u)b7XMBI}E?I: =o&~{QY!AJ^=~"HQ0 k$7 AiC6>Ǩ!AR(> "U ?GzX 8G   ŀ]%̗l^BwRJiTAAd0_CIf(9D$EAAA*5 <)*%؉AA-aPWWwҘ"i%X|Hpo"Dٚ|AALD0JZ(pK"u"V? H1[vj=M'~;)_,g_WXb/mvѴof֞ud'Zk6+bDNU҆EGKJtRa ,Tv Fvy2X`>d^_C>a/8rTy}svDE\rZ Og.M%>Nzdp0r6$9GIQЛ:M"^o0/æ>N@V"f|ZwG#/S7c7nJZ\LN}oK߉c G:¿ @ t!Ϋ[+^I]E~05UoTn~ԛ 3aS7tōO4rz493GJ~uoN ,}{]rdk! @0riPxPTu)8Hc bU?@^hazb\W]aE vvem6u?8;A_dJiMl=q~z\ *$L:ZI;A)Ԗx}SF tjPG8n4Ar)bRT~zѼ5lBU~ߖe;9ٳ$n%wJ" p(J47^|Sᑷ~ccɄ?y#LӣGuDUg_o#O{l t*p?ilAp=!R-P@&MFnye׀:;/7ٳ۳'z=zqNߚ+-kZß aBȁ5/DR!AO%hu/FoY@"QA#8 x]do/^QuT>Ah PQA4өG{\u, foY@#u3rz|FԦK)髵4}4HMbxhTeD10*0k?*:F-`NK r+SDuM#Xu2+G;Bt1zPuG$"Pph\*T}(-}@ib rq 6(}l wƐ PZ GS0= 4:klV 5K />4]@\i4:c V3RrZuDP^m&A"f2a1aPQ j҂uH& $H#6֧D`PK4ƨ(kIBHdb'6еWx7#t*QvŅĕDO;^}Ԇ҆qfN>Pʽw/{;'(^O%BGF``ZM)L@}L 0$PZ` l,;p Pp"5=>aj:iz.WP:Y ͪ٭+?*iD-RxkcCd'glia\Y ! CƀF @i@hk: *(:rB`P;e4E@aWgzWtlyir?B"5{"x%sM[7>HC/{8~Ϳ 7c ~3ͻvu8XjSȹϨpSqxznc. Tz*{ ;}," a*&FkyGt rK ա8Y.5w2=GĩEO~b9V[^Μf#:oΓGDOXͮp8BC:τ2W^r|z=4|HQ>^'f;:7}㾎} so|HfhO )o{qA4CK:GoGUXX#ҊT BqWPj=y2̲bEoz-s,l'd=aY"3n]fP,Dk9=;{;! >Mf+e^s/Ff]F'FֵI}neš1ƥx{98Nytd-x Cc Fh~Vt}/\A͉ʡ@+_d&pk܎{o0~\z6H|V(s8,^VD祯H祯|ۡRgcq^9"$RS/D\;;OqőO!g{Ě_gEgQv8hy5&>8h!* 7Icär+|oWTq>_jcrwĕىJqܥ՜_s]ŚîP⣤5Cd2;ώK uAy߀\۟ Ge,KĆܻ&gOω~G.} rt;@DB=e5Q؁#ݡHKBK|MNqdNGnwW86.uDar ϳt[%(u *=/}ً~&_dĹ=>? eo'Ρ=! szuđmEA[ :6Ͻ:xT~>ھn'J 'x͚@J *DCp%$_U5eY2y}wug )$'~/HkKяGϞ$GB~[kK_% b"%=\JDK' W렪#l*񤒢?cOk TW5/.蒨 ( 3 ;n|I kWh#viSGH]%SC{ͼK:_Y s~#yԟz qILwO*οqTW&yV:Y y_lN!:;Zϊ> OÄ"h7єtT?Fһz@D BnGd^q՗M+C?$ dMЏɓ,/#9FRD"RWI4U|?>68ƒDϜyܣū"Aīb;{qx/ Sfw‰RRTž"ս*}d3s9y3~癢wB =zS{=ZtffD"x"0Ӏ>el fb$ݺ IDATM.\)B;r.bd\#d[s3#HZNF~: ֨̓⥿eibYlS5}_$cbB&}(78Uv7CmB"5{"x%sM[7>&mX=v%!bb"E#=|=tw[͑עKW}xN[SnԜBnb!,b꿗'>|LP7cgej"1: _7 !׌/x"|bO-0d&R&0:oꓞ&| >)hߤ MSCbը\QAmR[PϞ̾w֪u]6 d fP8M`3axi-h=7JĶ[85ck@bajYpls !qtv r V%R`$"(QxFɨ`IC]BE5r\5m,TFK ֡KG:]rl6oӃ_h@R|r;4t>* tWQ -&2933nBxη Lt!jHfT<,y{oεŲ,˲ށ\?WIDyӖg1>[ޛdY՗#^}}v|TJTy%0r4E6b_Dsѥ#gW?G`굩eWD?H$?L$c&oNf=){|,wm?ܫR.]zgWhT~|$oͽ%x289Kqltl??wG =;ُq]WGE@,. &*Яu*Z!ғL:۝d=q מ9uF|e2VG"I x,*ЏWf IwBHD0WIN8”;$'3CȅHB<=,YO8g^ f;`: j6.W[pl_hccԢEv|Vqs|8ov#+e2BIǽi=yr$#bH]MNaah",~Ϯߺ1p84F?_}NvNs/BqΆl6[3ރ^}[iT@bZN" M G?8y-I6t;۾zR~B!vք; !vX7?YNRn.`67Iݞdom淫I(2y`9 *a٨Jߺs ,m_)!+Fd>B LxWA 6Ԧ-gg_f2熫@)-1s`Z]ZKZQ?n˗~G,o齅vBo_1BH'L\5ے,p8_-a;v^ 'Fd9#(>3m`P\)K }B/NJj<-0؇/ ,;쳷?fk+#zf KK&>K ?7L֑ήix5 bjpT=qsñK1r{`oBWHLȄ*4OXXcmmL=Vֹyib:oc+z~=t>Y8;6bݹu=uC<:::Zw~mz}ߪf>s{6c'OM7|K@Kۘw~'ch ]@])+09ZsS9&^Ā< l  T|iC{ow_?_[Ə(Sq2lG[T`,DrE LEQ.N!d {&l" = tckSMw45 F>XMC˲]84FXTI{v̓1# X> h&XM10WPкJ)-<@i *31QZ GӃ̬^Zסpv7F}r4QM߅Eȟ5f:V;ZgGƪQu jID6, E 7Ly v6 \00*@]=7h36O%SD^t. F9 "\A T$* $( LQxT@nJ l LJFĄ#DbKzFA[#n9rto|j^͹vCw.Xl{W'ƏN,!##{+qնlv8A)W`B$L6غ~5ĀĹl䯺sʕ-.q%1~,x L<;X0Ğ{|ru?5xUslַ>{4}NBϪ2SFST`\1ae&6O *C)n)>*J|=<9I}/*q+7?VhCs|{ ;69fMw酅h))0(ywwkI%)( j3*fG\!\KeմU赳f% vp-) גCIEy _$.}X`y !+~Lដv_ /~8}:]u-T|bw>1 V; k`s+*T==~RS ,-P]'4 ֲzT`҂B̸vէ-cv;o}7ae^uW~sc# ["dMBּuda' Cpt:`@0#?c<5 **T֑c}#|e|NMz1*@%L18>6q[b C̊_eį2̆^y;uV;ƦyχS҄!8',K>1w]㡧,J>w`굩wfS_MsٲRqxb`ʼnjNΌYx"4En\~>Pf u3?8>tO;x3{O_K^*ط_)Ѐ!dY!̬_N"2uJ TRp<͖=d⳸&2?yl3u5ւgV86;/o_gۘ?92I{<zʈiͪTQU! Ik !W U`1,QW}G<[G%V]~ `L}vV;_3ט6P+(h,P2ٙX|L5s4)&QΨkLy v&Uq"YCٸGmTPگJcZ!*+)]҂!WcU ,4h@QYiqR@TT(6Z!Fyf~4.N @3CiX,*.˪rv hTzg Pp,g\Qu%cF~4Ff+-0ETh`Y0HβQ&GA'W3S!M1 )SEqUg)l\nID4 1) تQ8@ jQP"2?_W=FiMq 0 qsPXTIDP%D#a٬u쌢*(-)*t]QelP ]"*/rDkFP-hFzSCFv r z ʦ H͆ XpL]HԺ *Zsl&* ZY: P7 QU9E*R` 4ETP= E.TIƀqp=83p" Yu@}T04 L:@!WPQT@h[CiimPQQ;+Wc DrXLE ,0UN.*(@T@?F:ma@r ,C$G~,CT# ̎ΤA!UY@]TDy,<2FT5R|ae`ƍ L.@U@RdEӜz.WP:Y FH@(|4fiA[QnMX1(-DGD!cc9FcV h.Ϻ)îcaqR H. <@Q^B' zXP@]v5 "Xpܠtx?*$^ZF H?ȟ~#4QQ7 FiA]`q<(-(_Fh(BGiAPZ @GRd(Ԙ3Q6kQA ZWtVIT@*yQW=t0\xQuD`n HA֋уrãͨ:_S/-?*wӕn>]tG񘄐|bY^Jե{( .0WflVǧ玦a с##˱X,. ZZ` iJ_BiJ \z!ϲl烝eY.M<9ٳi }X\CTP Zry}홛ew[c.//g2L7%<ϳ,۳'u5Ui@/ka`҂20W Z aA/˲Cpnƃk6j8.'x<͎ޯ -3W 3`<BxO_K+np̼!6'9#&w6WK͊iCڢKKEmz\A->Ք|J[н{5 j@ :jdqpc.N|oqRۺ $>Kg;!mtowOE1x"vvmTڐg'x"(=%55T*%^}#>*kx7Ą^M6N'Mw4E/F'#ˁ=@@0nnnrll[liJʙg4tvH]AiAՠaLyx'S'NNj.8 ꢂGP#jBy]3*)S!E:>AT~̂Gha# *GT+QL%2Ei"*5]P?Ke1lAf20WPкh 0N)|(Tƀev>hDtG#.˲<χN=8 PZPgY@ @]Y^^bѿ寻 UR PZP=G4}H$zN$ M<˲gnT|/ ! pGnm| ~38:1yrgg254Ԗ 6ߨ7b[GEv+پ]}/_EcKKKbvBfߝKm=#y}L&y&$,I]M} TD(PĄFÖ(ΔM֓}O[3x"b;r!B~gwwo] _{zYj8t:$Ǘٱy5JX41QZPsv'0MMMywy/?oݑ?EAXd'=v)}--R5eʶ qD e_q6boȸf2Uv-i#c(kQգ֓STJŹ渍\@%Jd8Y(L 2T~5.00]0vqR ancllڢ@5Tk( YQAŌhp)@`2 A[TD4ƀ`r!"F28*0UtZ!T-h+(F^PƌQ1شJp*(*7?*u'eLt3D"f3nw"mg&p,oY  ۨv_BA8(ں E ÄN86rsIApuX}X|S\6 BBC=;{*,@w% > '5,? nv%۷opߠhlii)|>\lyB컳z=XZZ 8t3Ln7 >FraﰴqdDP''Eq.BH6@BiA (hS̍EYEWaxoNϾ;;kkHώ2ae)ܶ3 mmmFcuT*%?z1iDC`c.ʝ]?t31C=37I/x"|ڌ[@ sv'0MMM oݑ?EAXd'Z&pGR$ɶ6ahJӹO i~5 zbR| --Яii>={~]rEHcb>4GqAPc *]*>LV+Ry\?s?}w6w?tmW\qu|^hlii)|>::ֹ>{{ыZCV<׺i$(cRyT@cQu邺ADd#䜺',"!{zvz87Js?< &02qan[ݟX,x]l-J{",gbt~ss}C;L]M޻h9o Y4{a늧Y @Ó=A'm 7Mꢂ3eTP9ga]ٹm?NJlT,!8B|lL&i)N~",+ĹwΑu$^8ң孧y{aӷlT!+fsۢx~q @#|l 場$*?={~]r%Ιwgυ^بbf+jmm]YY~#v>}ٷ?ij3>6.n#Gonon+E#хh?dTYRmҟ.lҨt?L&÷bFm{/Q tfبȳ<EqթҭށշoOEQ >2ǔ؇['L8p L^ӽ=q|Bj.ݖ_3KT"i?wӬChcɌգp &3HOm}Q}ittB/ !$0 w6w:?^pssmoY!C?z(O޳n]@ыѦ;ɲ9/⇬x]4qըa1zhŠRԖ-[PS.>.*(`@\ bO.^ݳP!QM1Ic@TBHkKks[s=33Mq*<+꘥UWM1/]Ɣeb& 7إ(w9Y%Vwf'@ qZ^tt(Q7r BەPGx /Y1.*0Vޛsu q{!t &׻r/N+ Px1P#x28˗V>{`]E,=8tru]sD aBCq植 H]].AY.^v,`ꞑ =,qc|O rt h hЬIJbii)rCZry}˙L&^ >Frap#^EՎiu QWmW,>1p%|!geY!;Ai컳XeY޶_ٻ7߮۹@e*{W8#y'<.ܘ3L؇Gz/zvh= VUSK *lBHhAPR5RB [mںR{s<Μ=3oh X@ 0UTP%v Q'nѳ<6;:2*^υƩSGp 5ݛ6dp2z1tGSbt28)m  fۺ[gׄ;;]5L&=e` -çar?B4oOcƤǏK?oN7K (=WtA(Tl,Cu LN'D% K 0.]IkQ z1 X=*rPx1$34S5.WP:J \,`11XPv%TL`.M J Pp J{Yϸ݉DBq>d3&>t6Tbcqcc r,џ{aqFn9ix]Dzl烝BۏagY6p,P옊b3. ^!rq9Jbb5*L"*v+پ]}}}ǏϮdF)1#su35$$/>ӾѾܻw;smUx{1cR|1;/5劫*m+bL&KbQ1l6+$5hZ% jQEYEB>8q;(n0 #oy^6%a9#7s:uuŏJc.W!~fnc]/x"|TьuQA%KG:ATE+Oɫsv'0MMM;ss ~ ب(}-- hBq8L1 tGS:.bvS(Č4:}nܒD/ !$0 w6w:?^pssm F񘊝 NF/Fh^N'ǏkkkslΟ>:ۚv{Э k#4tҥ BەRԖ-[\ ǷtQNPZ ]s0ʍ/hGkc+ }qFkKks[s=33Mq">邂1@"*0 qAR(3S_ keDT``/QD'~6*W QhX6*h\N",NџF^IP?ȃc9,Nj[Byzd:KG:ATM:(. M1G.C H'] :I n#ǬgKѽTDrDЇ 4Eh:eTFvs!=E WPe(CL"f4*w>97s5f)B("$ЂOCKI l,Gu@bt (ץbb>bFdh`@NS đ^M&1V0ٯ5'LD. J BaT҂D#T:/}l6[ r!f3 H]MI}88/Fg x`O``@m4YCZ7@N}/__ W\|c>BɩB4BtT:00䌞5 }F+i .quA62 m|}.EDBv'ܞ!㸍{sXWs!ݽ9,`xȅ'7񡳡=}V[\(aY9v7˲Ǎrd/[<Ϗ~kF* 5/ a^ Z+ʿg|.%Iismu|rf&'#nğ]73q%x9p!yC˙L&cy2g~ffyyuش.)ٕlȟ"%z{gh_^^;.1OVU~bxnB'퐶w}ҿ.;fD"x"˛O-CT`ڮ[`P_l+1G#QǽFn#gyAqJYYYijjZYYi){#[lc(͢(J{.//sGn"`yCmmm~/G/7.--u^]R<&>8qkZ;&Xyvw[**D?IRkkk4u8v][]ыQWōj>ChcgqBW9'_B"1:t;5G"= _=UEqK k2h 4&Jad^T@I_K}-}m]ƹsv'0MMMTqZP^ԏh +;Kǜ[XXmltDda;tf>0LmEBHbT.6؊mHsv[ 50lr9>PZ@\@nn` `oWz={~]r[ Uڊ$@"3u5|ssasdQb~J5\/cu:(ldvBH'zVS``ZS+ : e$q2DOA.*'DQ 捹d2| /fѱ{z{'RCy/1P\8(yn8fc}IS6ۚO}͹Gݽ#uQ\;Pb~n/Ͼ]}cI]MjvB#>AA<1Pl#Xk9{3gϰ6݆6:Ӊ`>N"B` Y0nv:mv}db鎦dpcNf瑞wORͶuZFt92 zk$$QDjٿeїFϭYg|b|g[pMh\?`[8{41[%q%17;Gznk˷lWkP`q>} gגE/cc'-W@,]ZGSTM͵5ҸFP ,>d8 =*hl9<4 *ȃQ(@X_ WQAY&yDP#=$Tu& BjШ4AinEŸ +XG!c z@'); m-h舑|jvR91 ߯u+cwPWQrjAeֶf2{@ |G&no(>Xi IDATATLk ( '[=*n4Fd$>FхmMԯ1 ֎? jIDub[gt!b $|aҿ[[xm!ξunԼ"QQ (C߼|&Dd:TFuZTdf '$!,էQ߳wO*6J jQ[7ETPt|$y-@KX~W,. <>XAP>* tww94n+Z/U|zs 5Wz"f鸯][;`tۜ^H:PȄ ¨'W(CDWw?(P3:ƿ(F]J FښI:oS)mm!jH'` u$ Uꨀ`ڋ.M?,xhO` q>DJ I0 POs%bdƀ VA_[#`6PaT@ h hd$BH)m2bHH@Й.Vpښe) fFX#]8!O?6uԓk 1$"70-* -hq< | hS -h N+)qވ bhD[m.G֮9VY2cF'_G?=ccLfGH&Ԑ^g5oq`NXF|HTLk!@K5ihl#ǂFf72{QfFھ[ߞ~G۶8ȣOs v!w7!1G{U=Ԓ!*x/רsTA~B'n#pifv=ۻ6N6uŹ>l4R(-0J\ yW"[Z{##ε_ ;ml\j`FVf9 _pPD.*pwwWs!ӿOtuBUEZMqZڱ!H^Kv6n_2ӟ׫PRA mcSGf,-Yswf-I]ĕjE3|Kk{ ȋ :55: Ғ.PRے5-CT Fb&dq|loyJ{wz_T:iwgo <>?HBN ?wr * j0Tm,hzJ =Gl6֮sxzG&6Bg{p7H^Kn޼lt fƂ-NZ׻Xh eV/xc:̾gCЬXTPeSh t2DeӠrVP8Fbċ@C*c0| *-3ȬUS]Z`ƹ+z8h,ȃOeTwm`i zz(P4!LѥC/b\4MK$C(J.GGmϱ_^\"[ 8;|[OybR#!xվv<E`.+ͽP,(_{T*%`*^W4ne*T  j\7vj,h`]USwsrr?U Lk`D]9]`̋?P%,^>Ty3n҅M4HClHWG?&Lтav@k0Ʊ]n~np/pD1#IENDB`./oar-2.5.2/sources/api/lib/0000755000175000017500000000000011757171206013563 5ustar plbplb./oar-2.5.2/sources/api/lib/OAR/0000755000175000017500000000000011757171206014204 5ustar plbplb./oar-2.5.2/sources/api/lib/OAR/API.pm0000644000175000017500000016021711757171206015162 0ustar plbplb#!/usr/bin/perl -w package OAR::API; require Exporter; my $VERSION="1.0.1alpha1"; use strict; #use OAR::Conf qw(init_conf dump_conf get_conf is_conf); use CGI qw/:standard/; our $ABSOLUTE_URIS; our $q; our $DEBUG_MODE; our $extension; ############################################################################## # INIT ############################################################################## # Try to load XML module my $XMLenabled = 1; unless ( eval "use XML::Simple qw(XMLout);1" ) { $XMLenabled = 0; } # Try to load YAML module my $YAMLenabled = 1; unless ( eval "use YAML;1" ) { $YAMLenabled = 0; } # Try to load JSON module my $JSONenabled = 1; unless ( eval "use JSON;1" ) { $JSONenabled = 0; } # Try to load URI (LWP) module my $URIenabled = 1; unless ( eval "use URI;1" ) { $URIenabled = 0; } # Declared later with REST functions sub ERROR($$$); ############################################################################## # Output text formating ############################################################################## # Inserts html line breaks in a string sub nl2br { my $t = shift or return; $t =~ s/(\r\n)/
/g; return $t; } ############################################################################## # Data conversion functions ############################################################################## # Load YAML data into a hashref sub import_yaml($) { my $data = shift; check_yaml(); # Try to load the data and exit if there's an error my $hashref = eval { YAML::Load($data) }; if ($@) { ERROR 400, 'YAML data not understood', $@; exit 0; } return $hashref; } # Load JSON data into a hashref sub import_json($) { my $data = shift; check_json(); # Try to load the data and exit if there's an error my $hashref = eval { JSON::decode_json($data) }; if ($@) { ERROR 400, 'JSON data not understood', $@; exit 0; } return $hashref; } # Load Dumper data into a hashref sub import_dumper($) { my $data = shift; my $hash = eval($data); if ($@) { ERROR 400, 'Dumper data not understood', $@ . $data; exit 0; } return $hash; } # Load HTML data into a hashref sub import_html_form($) { my $data = shift; return $data; } # Load data into a hashref sub import_data($$) { (my $data, my $format) = @_; if ($format eq "yaml") { import_yaml($data); } elsif ($format eq "dumper") { import_dumper($data); } elsif ($format eq "json") { import_json($data); } else { ERROR 400, "Unknown $format format", $@; exit 0; } } # Export a hash into YAML sub export_yaml($) { my $hashref = shift; check_yaml(); return YAML::Dump($hashref) } # Export a hash into JSON sub export_json($) { my $hashref = shift; check_json(); return JSON->new->pretty(1)->encode($hashref); } # Export a hash into HTML (YAML in fact, as it is human readable) sub export_html($) { my $hashref = shift; check_yaml(); return "
\n". YAML::Dump($hashref) ."\n
"; } # Export data to the specified content_type sub export($$) { my $data = shift; my $format = shift; if ( $format eq 'yaml' ) { return export_yaml($data); }elsif ( $format eq 'json' ) { return export_json($data)."\n"; }elsif ( $format eq 'html' ) { return export_html($data)."\n"; }elsif ( $format eq 'tgz' ) { return export_yaml($data)."\n"; }else { ERROR 406, "Unknown $format format", "The $format format is not known."; exit 0; } } ############################################################################## # URI generation functions # For each structure having an uri, also add an api_timestamp giving the date # at which the entry has been generated by the api. ############################################################################## # Return the url (absolute if the third argument is 1). The .html # extension is added if the second argument is equal to "html". sub make_uri($$$) { my $path = shift; my $ext = shift; my $absolute = shift; # deprecated, left here for compatibility if ($ext eq "html") { $path.=".html"; } if (our $ABSOLUTE_URIS == 1) { return $q->url(-absolute => 1)."/".$path; } else { if ($URIenabled) { my $base = URI->new($q->url().$q->path_info); my $goal = URI->new($q->url()."/".$path); return "".$goal->rel($base); } else { ERROR (500, "LWP URI module not enabled", "I cannot make relative uris without LWP URI module!" ); exit 0; } } } # Return an html href of an uri if the type is "html" sub htmlize_uri($$) { my $uri=shift; my $type=shift; if ($type eq "html") { return "
$uri"; } else { return $uri; } } # Get the api uri base in relative sub get_api_uri_relative_base() { if ($URIenabled) { my $base = $q->path_info; $base =~ s/\.html$// ; $base =~ s/\/$// ; $base = "http://bidon".$base; my $goal = "http://bidon"; return URI->new($goal)->rel($base); } else { ERROR (500, "LWP URI module not enabled", "I cannot make uris without LWP URI module!" ); exit 0; } } # Add uri to a job sub add_job_uris($$) { my $job = shift; my $ext = shift; my $self=OAR::API::make_uri("jobs/".$job->{id},$ext,0); $self=OAR::API::htmlize_uri($self,$ext); my $resources=OAR::API::make_uri("jobs/".$job->{id}."/resources",$ext,0); $resources=OAR::API::htmlize_uri($resources,$ext); my $links; push (@$links, { href => $self, rel => "self" }); push (@$links, { href => $resources, rel => "collection", title => "resources" }); $job->{links}=$links; $job->{api_timestamp}=time(); # Don't know why this function breaks the type of the id, so: $job->{"id"}=int($job->{"id"}); } # Add uris to a oar job list sub add_joblist_uris($$) { my $jobs = shift; my $ext = shift; foreach my $job (@$jobs) { if (defined($job->{Job_Id}) && !defined($job->{job_id})) { $job->{job_id}=$job->{Job_Id}; } add_job_uris($job,$ext); } } # Add uris to a oar job list for oargrid sub add_joblist_griduris($$$) { my $jobs = shift; my $ext = shift; my $site = shift; foreach my $job ( keys( %{$jobs} ) ) { $jobs->{$job}->{uri}=OAR::API::make_uri("sites/$site/jobs/$job",$ext,0); $jobs->{$job}->{uri}=OAR::API::htmlize_uri($jobs->{$job}->{uri},$ext); $jobs->{$job}->{api_timestamp}=time(); } } # Add uris to a list of jobs of a resource sub add_jobs_on_resource_uris($$) { my $jobs = shift, my $ext = shift; foreach my $job (@$jobs) { add_job_uris($job,$ext); } } # Add uris to a resources list sub add_resources_uris($$$) { my $resources = shift; my $ext = shift; my $prefix = shift; foreach my $resource (@$resources) { my $links; my $node; if (defined($resource->{network_address})) { $node=OAR::API::make_uri($prefix."resources/nodes/".$resource->{network_address},$ext,0); $node=OAR::API::htmlize_uri($node,$ext); push (@$links, { href => $node, title => "node", rel => "member" }); } my $self=OAR::API::make_uri($prefix."resources/".$resource->{id},$ext,0); my $jobs=OAR::API::make_uri($prefix."resources/".$resource->{id}."/jobs",$ext,0); $self=OAR::API::htmlize_uri($self,$ext); $jobs=OAR::API::htmlize_uri($jobs,$ext); push (@$links, { href => $self, rel => "self" }); push (@$links, { href => $jobs, title => "jobs" , rel => "collection"}); $resource->{links}=$links; $resource->{api_timestamp}=time(); $resource->{id}=int($resource->{id}); #why the hell do I need to do that?? } } # Add uris to a list of nodes sub add_nodes_uris($$$) { my $nodes = shift; my $ext = shift; my $prefix = shift; foreach my $node (@$nodes) { my $links; my $self=OAR::API::make_uri($prefix."resources/nodes/".$node->{network_address},$ext,0); $self=OAR::API::htmlize_uri($self,$ext); push (@$links, { href => $self, rel => "self" }); $node->{links}=$links; $node->{api_timestamp}=time(); } } # Add uris to resources of a job # OBSOLETE! sub add_job_resources_uris($$$) { my $resources = shift; my $ext = shift; my $prefix = shift; foreach my $assigned_resource (@{$resources->{assigned_resources}}) { $assigned_resource->{resource_uri}=OAR::API::make_uri($prefix."resources/".$assigned_resource->{id},$ext,0); $assigned_resource->{resource_uri}=htmlize_uri($assigned_resource->{resource_uri},$ext); } foreach my $reserved_resource (@{$resources->{reserved_resources}}) { $reserved_resource->{resource_uri}=OAR::API::make_uri($prefix."resources/".$reserved_resource->{id},$ext,0); $reserved_resource->{resource_uri}=htmlize_uri($reserved_resource->{resource_uri},$ext); } foreach my $assigned_node (@{$resources->{assigned_nodes}}) { $assigned_node->{node_uri}=OAR::API::make_uri($prefix."resources/nodes/".$assigned_node->{node},$ext,0); $assigned_node->{node_uri}=htmlize_uri($assigned_node->{node_uri},$ext); } $resources->{job_uri}=OAR::API::make_uri($prefix."jobs/".$resources->{job_id},$ext,0); $resources->{job_uri}=htmlize_uri($resources->{job_uri},$ext); $resources->{api_timestamp}=time(); } # Add uris to a grid sites list sub add_sites_uris($$) { my $sites = shift; my $ext = shift; foreach my $site ( keys( %{$sites} ) ) { $sites->{$site}->{uri}=OAR::API::htmlize_uri( OAR::API::make_uri("sites/$site",$ext,0), $ext ); $sites->{$site}->{resources_uri}=OAR::API::htmlize_uri( OAR::API::make_uri("sites/$site/resources",$ext,0), $ext ); $sites->{$site}->{timezone_uri}=OAR::API::htmlize_uri( OAR::API::make_uri("sites/$site/timezone",$ext,0), $ext ); $sites->{$site}->{api_timestamp}=time(); } } # Add uris to a grid job list sub add_gridjobs_uris($$) { my $jobs = shift; my $ext = shift; foreach my $job ( keys( %{$jobs} ) ) { $jobs->{$job}->{uri}=OAR::API::htmlize_uri( OAR::API::make_uri("grid/jobs/$job",$ext,0), $ext ); $jobs->{$job}->{nodes_uri}=OAR::API::htmlize_uri( OAR::API::make_uri("grid/jobs/$job/resources/nodes",$ext,0), $ext ); $jobs->{$job}->{api_timestamp}=time(); } } # Add uris to a grid job sub add_gridjob_uris($$) { my $job = shift; my $ext = shift; # Timestamp $job->{api_timestamp}=time(); # List of resources $job->{resources_uri}=OAR::API::htmlize_uri( OAR::API::make_uri("grid/jobs/". $job->{id} ."/resources",$ext,0), $ext ); # List of resources without details (nodes only) $job->{nodes_uri}=OAR::API::htmlize_uri( OAR::API::make_uri("grid/jobs/". $job->{id} ."/resources/nodes",$ext,0), $ext ); # Link to the batch job on the corresponding cluster foreach my $cluster (keys %{$job->{clusterJobs}}) { foreach my $cluster_job (keys %{$job->{clusterJobs}->{$cluster}}) { $job->{clusterJobs}->{$cluster}->{$cluster_job}->{uri}=OAR::API::htmlize_uri( OAR::API::make_uri("sites/$cluster/jobs/" .$job->{clusterJobs}->{$cluster}->{$cluster_job}->{batchId},$ext,0), $ext ); } } # Ssh keys $job->{ssh_private_key_uri}=OAR::API::htmlize_uri( OAR::API::make_uri("grid/jobs/".$job->{id}."/keys/private",$ext,0), $ext ); $job->{ssh_public_key_uri}=OAR::API::htmlize_uri( OAR::API::make_uri("grid/jobs/".$job->{id}."/keys/public",$ext,0), $ext ); } # Add uris to a single admission rule sub add_admission_rule_uris($$) { my $admission_rule = shift; my $ext = shift; $admission_rule->{uri} = OAR::API::make_uri("admission_rules/".$admission_rule->{id},$ext,0); $admission_rule->{uri} = htmlize_uri($admission_rule->{uri},$ext); $admission_rule->{api_timestamp} = time(); } # Add uris to an admission rules list sub add_admission_rules_uris($$) { my $admission_rules = shift; my $ext = shift; foreach my $admission_rule (@$admission_rules) { $admission_rule->{uri} = OAR::API::make_uri("admission_rules/".$admission_rule->{id},$ext,0); $admission_rule->{uri} = htmlize_uri($admission_rule->{uri},$ext); $admission_rule->{api_timestamp} = time(); } } # Add uris to a single config parameter sub add_config_parameter_uris($$) { my $parameter = shift; my $ext = shift; $parameter->{uri} = OAR::API::make_uri("config/".$parameter->{id},$ext,0); $parameter->{uri} = htmlize_uri($parameter->{uri},$ext); $parameter->{api_timestamp} = time(); } # Add uris to a config parameters list sub add_config_parameters_uris($$) { my $parameters = shift; my $ext = shift; foreach my $name (keys %$parameters) { $parameters->{$name}->{uri} = OAR::API::make_uri("config/".$name,$ext,0); $parameters->{$name}->{uri} = htmlize_uri($parameters->{$name}->{uri},$ext); $parameters->{$name}->{api_timestamp} = time(); } } ############################################################################## # Data structure functions # (functions for shaping data depending on $STRUCTURE) ############################################################################## # EMPTY DATA sub struct_empty($) { my $structure = shift; if ($structure eq 'oar') { return {}; } elsif ($structure eq 'simple') { return []; } } # OAR JOB sub fix_job_integers($) { my $job = shift; foreach my $key ("resubmit_job_id","Job_Id","array_index","array_id","startTime","stopTime","submissionTime","scheduledStart") { $job->{$key}=int($job->{$key}) if defined($job->{$key}); } foreach my $event (@{$job->{"events"}}) { $event->{'job_id'}=int($event->{'job_id'}); $event->{'event_id'}=int($event->{'event_id'}); $event->{'date'}=int($event->{'date'}); } } sub struct_job($$) { my $job = shift; my $structure = shift; my $result; if ($structure eq 'oar') { return $job; } elsif ($structure eq 'simple') { if ($job->{(keys(%{$job}))[0]} eq "HASH") { $job=$job->{(keys(%{$job}))[0]}; } fix_job_integers($job); $job->{id}=$job->{Job_Id}; delete $job->{Job_Id}; $job->{start_time}=$job->{startTime}; delete $job->{startTime}; $job->{stop_time}=$job->{stopTime}; delete $job->{stopTime}; $job->{scheduled_start}=$job->{scheduledStart}; delete $job->{scheduledStart}; $job->{submission_time}=$job->{submissionTime}; delete $job->{submissionTime}; $job->{type}=$job->{jobType}; delete $job->{jobType}; $job->{launching_directory}=$job->{launchingDirectory}; delete $job->{launchingDirectory}; delete $job->{job_user}; delete $job->{job_uid}; delete $job->{reserved_resources}; delete $job->{assigned_resources}; delete $job->{assigned_network_address}; return $job; } } sub struct_job_list_hash_to_array($) { my $jobs=shift; my $array=[]; foreach my $j ( sort { $a <=> $b } keys (%{$jobs}) ) { if (defined($jobs->{$j}->{Job_Id})) { $jobs->{$j}->{id}=int($jobs->{$j}->{Job_Id}); push (@$array,$jobs->{$j}); } else { $jobs->{$j}->{Job_Id} = int($j); $jobs->{$j}->{id} = int($j); push (@$array,$jobs->{$j}); } } return $array; } # OAR JOB LIST sub struct_job_list($$) { my $jobs = shift; my $structure = shift; my $result; foreach my $job (@$jobs) { my $hashref = { id => int($job->{job_id}), state => $job->{state}, owner => $job->{job_user}, name => $job->{job_name}, queue => $job->{queue_name}, submission => $job->{submission_time}, api_timestamp => int($job->{api_timestamp}), links => $job->{links} }; if ($structure eq 'oar') { $result->{$job->{job_id}} = $hashref; } elsif ($structure eq 'simple') { push (@$result,$hashref); } } return $result; } # OAR JOB LIST WITH DETAILS # TODO: need to append "resources" and "nodes" as /jobs/XXX/resources sub struct_job_list_details($$) { my $jobs = shift; my $structure = shift; my $result; if ($structure eq 'oar') { foreach my $job (@$jobs) { $result->{$job->{job_id}} = int($job); } } elsif ($structure eq 'simple') { foreach my $job (@$jobs) { $job=struct_job($job,$structure); push (@$result,$job); } } return $result; } # OAR RESOURCES OF A JOB sub struct_job_resources($$) { my $resources=shift; my $structure=shift; my $result=[]; foreach my $r (@{$resources->{assigned_resources}}) { push(@$result,{'id' => int($r), 'status' => 'assigned'}); } if (ref($resources->{reserved_resources}) eq "HASH") { foreach my $r (keys(%{$resources->{reserved_resources}})) { push(@$result,{'id' => int($r), 'status' => 'reserved'}); } } if (ref($resources->{scheduled_resources}) eq "HASH") { foreach my $r (keys(%{$resources->{scheduled_resources}})) { push(@$result,{'id' => int($r), 'status' => 'scheduled'}); } } return $result; } sub struct_job_nodes($$) { my $resources=shift; my $structure=shift; my $result=[]; foreach my $n (@{$resources->{assigned_hostnames}}) { push(@$result,{'network_address' => $n, 'id' => 0}); } return $result; } # OAR RESOURCES sub filter_resource_list($) { my $resources = shift; my $filtered_resources; foreach my $resource (@$resources) { push(@$filtered_resources,{ id => int($resource->{resource_id}), state => $resource->{state}, available_upto => int($resource->{available_upto}), network_address => $resource->{network_address} }); } return $filtered_resources; } sub struct_resource_list_hash_to_array($) { my $resources=shift; my $array=[]; foreach my $r ( keys (%{$resources}) ){ if (defined($resources->{$r}->{resource_id})) { push (@$array,$resources->{$r}); #oarnodes -s case }else{ foreach my $id ( keys (%{$resources->{$r}})) { push (@$array,{ 'state' => $resources->{$r}->{$id}, 'id' => int($id), 'network_address' => $r}); } } } return $array; } # Replace resource_id by id sub fix_resource_id($) { my $resource=shift; if (defined($resource->{resource_id}) && !defined($resource->{id})) { $resource->{id}=int($resource->{resource_id}); delete $resource->{resource_id}; } } # Replace resource_id by id into a resources list sub fix_resource_ids($) { my $resources=shift; foreach my $resource (@$resources) { fix_resource_id($resource); } } sub struct_resource_list_fix_ints($) { my $resources = shift; foreach my $resource (@$resources) { if (defined($resource->{resource_id})) { $resource->{id}=int($resource->{resource_id}); } if (defined($resource->{id})) { $resource->{id}=int($resource->{id}); } if (defined($resource->{available_upto})) { $resource->{available_upto}=int($resource->{available_upto}); } if (defined($resource->{cpuset})) { $resource->{cpuset}=int($resource->{cpuset}); } } } sub struct_resource_list($$$) { my $resources = shift; my $structure = shift; my $compact = shift; # If true, replace a 1 element array by its element my $result; struct_resource_list_fix_ints($resources); if ($structure eq 'simple') { if (scalar @$resources == 1 && $compact == 1) { return @$resources[0]; } else { return $resources ; } } elsif ($structure eq 'oar') { foreach my $resource (@$resources) { $result->{$resource->{id}}=int($resource); } return $result; } } sub get_list_nodes($) { my $expression = shift; my $pattern = qr{/(node|nodes)=(.*?)(/|$)}; my $result; if ($expression =~ /$pattern/) { my $prefix = $1; my $value = $2; if ($value =~ /\{(.+)\}/) { for (my $i=1; $i<=$1; $i++) { push(@$result, $prefix.$i); } } else { my @params = split(/,/,$value); foreach my $param (@params) { if ($param =~ /\[(\d+)-(\d+)\]/) { for (my $i=$1; $i<=$2; $i++) { push(@$result, $prefix.$i); } } else { push(@$result, $param); } } } } return $result; } # GRID SITE LIST sub struct_sites_list($$) { my $sites = shift; my $structure = shift; my $result; my $uri; foreach my $s ( keys( %{$sites} ) ) { if ($structure eq "simple") { push(@$result,{ site => $s, uri => $sites->{$s}->{uri}, api_timestamp => $sites->{$s}->{api_timestamp} });} else { $result->{$s}->{uri} = $sites->{$s}->{uri}; $result->{$s}->{api_timestamp} = $sites->{$s}->{api_timestamp}; } } return $result; } # GRID SITE sub struct_site($$) { my $site = shift; my $structure = shift; if ($structure eq "simple") { my $s=(keys( %{$site}))[0]; $site->{$s}->{site}=$s; return $site->{$s}; } else { return $site; } } # GRID JOB sub struct_gridjob($$) { my $job = shift; my $structure = shift; my @cluster_jobs; foreach my $cluster (keys %{$job->{clusterJobs}}) { foreach my $cluster_job (keys %{$job->{clusterJobs}->{$cluster}}) { # Cleaning delete $job->{clusterJobs}->{$cluster}->{$cluster_job}->{weight}; delete $job->{clusterJobs}->{$cluster}->{$cluster_job}->{nodes}; delete $job->{clusterJobs}->{$cluster}->{$cluster_job}->{env}; delete $job->{clusterJobs}->{$cluster}->{$cluster_job}->{name}; delete $job->{clusterJobs}->{$cluster}->{$cluster_job}->{queue}; delete $job->{clusterJobs}->{$cluster}->{$cluster_job}->{part}; # For the simple data structure push (@cluster_jobs, { 'cluster' => $cluster, 'id' => $job->{clusterJobs}->{$cluster}->{$cluster_job}->{batchId}, 'properties' => $job->{clusterJobs}->{$cluster}->{$cluster_job}->{properties}, 'rdef' => $job->{clusterJobs}->{$cluster}->{$cluster_job}->{rdef}, 'uri' => $job->{clusterJobs}->{$cluster}->{$cluster_job}->{uri}, 'api_timestamp' => $job->{clusterJobs}->{$cluster}->{$cluster_job}->{api_timestamp} }) } } if ($structure eq "simple") { delete $job->{clusterJobs}; $job->{cluster_jobs}=\@cluster_jobs; } return $job; } # GRID JOB LIST sub struct_gridjobs_list($$) { my $jobs = shift; my $structure = shift; my $result; foreach my $job ( keys( %{$jobs} ) ) { my $hashref = { nodes => $jobs->{$job}->{nodes}, uri => $jobs->{$job}->{uri}, api_timestamp => $jobs->{$job}->{api_timestamp}, }; if ($structure eq 'oar') { $result->{$job} = $hashref; } elsif ($structure eq 'simple') { $hashref->{id}=$job; push (@$result,$hashref); } } return $result; } # GRID JOB RESOURCES sub struct_gridjob_resources($$) { my $resources = shift; my $structure = shift; my $result; if ($structure eq "simple") { foreach my $resource ( keys( %{$resources} ) ) { push (@$result,{ site => $resource, jobs => $resources->{$resource} }); } return $result; } else { return $resources; } } # LIST OF NODES FOR A GRID JOB sub struct_gridjob_nodes($$) { my $resources = shift; my $structure = shift; my @result; foreach my $site ( keys( %{$resources} ) ) { foreach my $job ( keys( %{$resources->{$site}} ) ) { my $nodes=$resources->{$site}->{$job}->{nodes}; foreach my $node (@$nodes) { @result=(@result,$node); } } } return \@result; } # SINGLE ADMISSION RULE sub struct_admission_rule($$) { my $admission_rule = shift; my $structure = shift; my $result; my $current_rule_link = { href => $admission_rule->{uri}, rel => "self" }; my $hashref = { rule => nl2br($admission_rule->{rule}), links => $current_rule_link }; if ($structure eq 'simple') { $hashref->{id} = int($admission_rule->{id}); push (@$result,$hashref); } elsif ($structure eq 'oar') { $result->{$admission_rule ->{id}} = $hashref; } return $result; } # LIST OF ADMISSION RULES sub struct_admission_rule_list($$) { my $admission_rules = shift; my $structure = shift; my $result; foreach my $admission_rule (@$admission_rules) { my $current_rule_link = { href => $admission_rule->{uri}, rel => "self" }; my $hashref = { rule => nl2br($admission_rule->{rule}), links => $current_rule_link }; if ($structure eq 'oar') { $result->{$admission_rule ->{id}} = $hashref; } elsif ($structure eq 'simple') { $hashref->{id} = $admission_rule->{id}; push (@$result,$hashref); } }; return $result; } # CONFIG PARAMETERS sub struct_config_parameter($$) { my $parameter = shift; my $structure = shift; my $result; my $current_rule_link = { href => $parameter->{uri}, rel => "self" }; my $hashref = { id => int($parameter->{id}), value => $parameter->{value}, links => $current_rule_link }; if ($structure eq 'oar') { $result->{$parameter->{id}} = $hashref; } elsif ($structure eq 'simple') { $hashref->{id} = $parameter->{id}; push (@$result,$hashref); } return $result; } # LIST OF CONFIG PARAMETERS sub struct_config_parameters_list($$) { my $parameters = shift; my $structure = shift; my $result; foreach my $param ( keys( %{$parameters} ) ) { my $current_rule_link = { href => $parameters->{$param}->{uri}, rel => "self" }; my $hashref = { value => $parameters->{$param}->{value}, links => $current_rule_link }; if ($structure eq 'oar') { $result->{$param} = $hashref; } elsif ($structure eq 'simple') { $hashref->{id} = $param; push (@$result,$hashref); } } return $result; } ############################################################################## # Content type functions ############################################################################## # Get a suitable extension depending on the content-type sub get_ext($) { my $content_type = shift; # content_type may be of the form "application/json; charset=UTF-8" ($content_type)=split(/\s*;\s*/,$content_type); if ($content_type eq "text/yaml") { return "yaml"; } elsif ($content_type eq "text/html") { return "html"; } elsif ($content_type eq "application/octet-stream") { return "yaml"; } elsif ($content_type eq "application/json") { return "json"; } elsif ($content_type eq "application/x-gzip") { return "tgz"; } #elsif ($content_type eq "application/x-www-form-urlencoded") { return "json"; } else { return "UNKNOWN_TYPE"; } } # Get a suitable content-type depending on the extension sub get_content_type($) { my $format = shift; if ( $format eq "yaml" ) { return "text/yaml"; } elsif ( $format eq "html" ) { return "text/html"; } elsif ( $format eq "json" ) { return "application/json"; } elsif ( $format eq "tgz" || $format eq "tar.gz" ) { return "application/x-gzip"; } else { return "UNKNOWN_TYPE"; } } # Set oar output option and header depending on the format given # Also add the Allow (GET[,POST]) header variable if a second argument is given sub set_output_format { my $format=shift; my $allow=shift || "GET"; my $type = get_content_type($format); my $header=$q->header( -status => 200, -type => "$type", -allow => "$allow" ); return ($header,$type); } # Return the extension (second parameter) if defined, or the # corresponding one if the content_type if set. sub set_ext($$) { my $q=shift; my $ext=shift; if (defined($ext) && $ext ne "") { $ext =~ s/^\.*//; return $ext; } else { if (defined($q->http('Accept'))) { if (get_ext($q->http('Accept')) ne "UNKNOWN_TYPE") { return get_ext($q->http('Accept')); } elsif (defined($q->content_type)) { if (get_ext($q->content_type) ne "UNKNOWN_TYPE") { return get_ext($q->content_type); } else { ERROR 406, 'Invalid content type ', "Valid types are text/yaml, application/json or text/html"; } } else { ERROR 406, 'Invalid content type required ' .$q->http('Accept'), "Valid types are text/yaml, application/json or text/html"; } } elsif (defined($q->content_type)) { if (get_ext($q->content_type) ne "UNKNOWN_TYPE") { return get_ext($q->content_type); } else { ERROR 406, 'Invalid content type ' .$q->content_type, "Valid types are text/yaml, application/json or text/html"; } } else { ERROR 406, 'Invalid content type ', "Valid types are text/yaml, application/json or text/html"; } } } ############################################################################## # REST Functions ############################################################################## sub HEAD($$); sub GET($$); sub POST($$); sub DELETE($$); sub PUT($$); sub ERROR($$$); sub HEAD($$) { ( my $q, my $path ) = @_; if ( $q->request_method eq 'HEAD' && $q->path_info =~ /$path/ ) { return 1; } else { return 0; } } sub GET($$) { ( my $q, my $path ) = @_; if ( $q->request_method eq 'GET' && $q->path_info =~ /$path/ ) { return 1; } else { return 0; } } sub POST($$) { my ( $q, $path ) = @_; if ( $q->request_method eq 'POST' && $q->path_info =~ $path ) { return 1; } else { return 0; } } sub DELETE($$) { my ( $q, $path ) = @_; if ( $q->request_method eq 'DELETE' && $q->path_info =~ $path ) { return 1; } else { return 0; } } sub PUT($$) { my ( $q, $path ) = @_; if ( $q->request_method eq 'PUT' && $q->path_info =~ $path ) { return 1; } else { return 0; } } sub ERROR($$$) { ( my $status, my $title, my $message ) = @_; if ($DEBUG_MODE) { $title = "ERROR $status - " . $title ; $status = "200"; } # This is to prevent a loop as the export function may call ERROR! if (!defined($extension) || get_content_type($extension) eq "UNKNOW_TYPE") { if ($JSONenabled) { $extension = "json" ; } elsif ($YAMLenabled) { $extension = "yaml"; } else { $extension = "html"; } } elsif($extension eq "json" && !$JSONenabled) { $extension = "html"; } elsif($extension eq "yaml" && !$YAMLenabled) { $extension = "html"; } elsif($extension eq "xml" && !$XMLenabled) { $extension = "html"; } $status=$status+0; # To convert the status to an integer print $q->header( -status => $status, -type => get_content_type($extension) ); if ($extension eq "html") { print $q->title($title) ."\n"; print $q->h1($title) ."\n"; print $q->p("
\n". $message ."\n
"); } else { my $error = { code => $status, message => $message, title => $title }; print export($error,$extension); #exit 0; local $^W = 0; next FCGI; } } ############################################################################## # Posted resources ############################################################################## # Check the consistency of a posted job and load it into a hashref sub check_job($$) { my $data = shift; my $content_type = shift; my $job; # content_type may be of the form "application/json; charset=UTF-8" ($content_type)=split(/\s*;\s*/,$content_type); # If the data comes in the YAML format if ( $content_type eq 'text/yaml' ) { $job=import_yaml($data); } # If the data comes in the JSON format elsif ( $content_type eq 'application/json') { $job=import_json($data); } # If the data comes from an html form elsif ( $content_type eq 'application/x-www-form-urlencoded' ) { $job=import_html_form($data); } # We expect the data to be in YAML or JSON format else { ERROR 406, 'Job description must be in YAML or JSON', "The correct format for a job request is text/yaml or application/json. " . $content_type; exit 0; } # Job must have a "script" or script_path field unless there's a reservation unless ( $job->{reservation} or $job->{script} or $job->{script_path} or $job->{command}) { ERROR 400, 'Missing Required Field', 'A job must have a script, a command (script_path) or must be a reservation!'; exit 0; } # Clean options with an empty parameter that is normaly required foreach my $option ("resource", "name", "property", "script", "script_path", "type", "reservation", "directory", "project", "stagein", "connect", "resources", "array", "array-param-file", "queue", "checkpoint", "signal", "anterior", "notify", "resubmit", "import-job-key-from-file", "import-job-key-inline", "export-job-key-to-file", "stdout", "stderr", "stagein", "stagein-md5sum", "command", "script" ) { parameter_option($job,$option) } # Manage toggle options (no parameter) toggle_option($job,"use-job-key"); toggle_option($job,"scanscript"); toggle_option($job,"hold"); # Ignore some nonsense (for the API) options foreach my $option ("dumper","xml","yaml","json","help","version") { delete($job->{"$option"}); } # Return an error for some forbidden options if ($job->{"interactive"}) { ERROR 400, 'The API cannot manage interactive jobs', 'The API cannot manage interactive jobs'; exit 0; } return $job; } # Check the consistency of a job update and load it into a hashref sub check_job_update($$) { my $data = shift; my $content_type = shift; my $job; # content_type may be of the form "application/json; charset=UTF-8" ($content_type)=split(/\s*;\s*/,$content_type); # If the data comes in the YAML format if ( $content_type eq 'text/yaml' ) { $job=import_yaml($data); } # If the data comes in the JSON format elsif ( $content_type eq 'application/json' ) { $job=import_json($data); } # If the data comes from an html form elsif ( $content_type eq 'application/x-www-form-urlencoded' ) { $job=import_html_form($data); } # We expect the data to be in YAML or JSON format else { ERROR 406, 'Job description must be in YAML or JSON', "The correct format for a job request is text/yaml or application/json. " . $content_type; exit 0; } # Job must have a "method" field unless ( $job->{method} ) { ERROR 400, 'Missing Required Field', 'A job update must have a "method" field!'; exit 0; } return $job; } # Check the consistency of a posted oar resource and load it into a hashref sub check_resources($$) { my $data = shift; my $content_type = shift; my $resources; # content_type may be of the form "application/json; charset=UTF-8" ($content_type)=split(/\s*;\s*/,$content_type); # If the data comes in the YAML format if ( $content_type eq 'text/yaml' ) { $resources=import_yaml($data); } # If the data comes in the JSON format elsif ( $content_type eq 'application/json' ) { $resources=import_json($data); } # If the data comes from an html form elsif ( $content_type eq 'application/x-www-form-urlencoded' ) { $resources=import_html_form($data); $resources=import_yaml($resources->{"yaml_array"}); } # We expect the data to be in YAML or JSON format else { ERROR 406, 'Resource description must be in YAML or JSON', "The correct format for a resource request is text/yaml or application/json. " . $content_type; exit 0; } my $resources_array; if ( ref($resources) eq "HASH") { $resources_array = [ $resources ] ; } elsif ( ref($resources) eq "ARRAY") { $resources_array = $resources ; } else { ERROR 406, 'Bad type', 'resource must be an array or a hash!'; exit 0; } foreach my $r (@$resources_array) { # Resource must have a "hostname" or "network_address" field unless ( $r->{hostname} or $r->{network_address} ) { ERROR 400, 'Missing Required Field', 'A resource must have a hosname field or a network_address property!'; exit 0; } # Fill network_address with $hostname if provided if ( ! $r->{network_address} && $r->{hostname} ) { $r->{network_address}=$r->{hostname}; delete $r->{hostname}; } # Check for system properties foreach my $property ( keys %{$r} ) { if (OAR::Tools::check_resource_system_property($property) == 1){ ERROR 403, "Forbidden", "The property \"$property\" is a system one and can't be assigned by the admin"; exit 0; } } } return $resources_array; } # Check the consistency of a posted oar resource change state request sub check_resource_state($$) { my $data = shift; my $content_type = shift; my $resource; # content_type may be of the form "application/json; charset=UTF-8" ($content_type)=split(/\s*;\s*/,$content_type); # If the data comes in the YAML format if ( $content_type eq 'text/yaml' ) { $resource=import_yaml($data); } # If the data comes in the JSON format elsif ( $content_type eq 'application/json' ) { $resource=import_json($data); } # If the data comes from an html form elsif ( $content_type eq 'application/x-www-form-urlencoded' ) { $resource=import_html_form($data); } # We expect the data to be in YAML or JSON format else { ERROR 406, 'Job description must be in YAML or JSON', "The correct format for a job request is text/yaml or application/json. " . $content_type; exit 0; } # Resource must have a "state" field unless ( $resource->{state} ) { ERROR 400, 'Missing Required Field', 'A state change request must have a "state" field!'; exit 0; } # State must be "Alive, Absent or Dead" my $r=$resource->{state}; unless ( $r eq "Alive" || $r eq "Absent" || $r eq "Dead") { ERROR 400, 'Bad state', 'State mut be Alive, Absent or Dead!'; exit 0; } return $resource; } # Check the consistency of a posted request resource generation and load it into a hashref sub check_resource_description($$) { my $data = shift; my $content_type = shift; my $description; # content_type may be of the form "application/json; charset=UTF-8" ($content_type)=split(/\s*;\s*/,$content_type); # If the data comes in the YAML format if ( $content_type eq 'text/yaml' ) { $description = import_yaml($data); } # If the data comes in the JSON format elsif ( $content_type eq 'application/json' ) { $description = import_json($data); } # If the data comes from an html form elsif ( $content_type eq 'application/x-www-form-urlencoded' ) { $description = import_html_form($data); } # We expect the data to be in YAML or JSON format else { ERROR 406, 'Resource description must be in YAML or JSON', "The correct format for resource description is text/yaml or application/json. " . $content_type; exit 0; } # Resource description must have a "expression" field unless ( $description->{resources} ) { ERROR 400, 'Missing Required Field', 'Resources generation description must have a resources field'; exit 0; } # "properties" field must be a HASH #if (defined($description->{properties})) { # unless ( ref($description->{properties}) eq "HASH" ) { # ERROR 400, 'Missing Type Field', # 'The field properties must be a HASH type'; # exit 0; # } #} return $description; } # Check the consistency of a posted grid job and load it into a hashref sub check_grid_job($$) { my $data = shift; my $content_type = shift; my $job; # content_type may be of the form "application/json; charset=UTF-8" ($content_type)=split(/\s*;\s*/,$content_type); # If the data comes in the YAML format if ( $content_type eq 'text/yaml' ) { $job=import_yaml($data); } # If the data comes in the JSON format elsif ( $content_type eq 'application/json' ) { $job=import_json($data); } # If the data comes from an html form elsif ( $content_type eq 'application/x-www-form-urlencoded' ) { $job=import_html_form($data); } # We expect the data to be in YAML or JSON format else { ERROR 406, 'Job description must be in YAML or JSON', "The correct format for a job request is text/yaml or application/json. " . $content_type; exit 0; } # Job must have a "resources" or "file" field unless ( $job->{resources} or $job->{file} ) { ERROR 400, 'Missing Required Field', 'A grid job must have a resources or file field!'; exit 0; } # Clean options with an empty parameter that is normaly required parameter_option($job,"walltime"); parameter_option($job,"queue"); parameter_option($job,"identity_file"); parameter_option($job,"timeout"); parameter_option($job,"program"); parameter_option($job,"type"); parameter_option($job,"start_date"); parameter_option($job,"directory"); # Manage toggle options (no parameter) toggle_option($job,"FORCE"); toggle_option($job,"verbose"); return $job; } # Check the consistency of a posted oar admission rule and load it into a hashref sub check_admission_rule($$) { my $data = shift; my $content_type = shift; my $admission_rule; # content_type may be of the form "application/json; charset=UTF-8" ($content_type)=split(/\s*;\s*/,$content_type); # If the data comes in the YAML format if ( $content_type eq 'text/yaml' ) { $admission_rule = import_yaml($data); } # If the data comes in the JSON format elsif ( $content_type eq 'application/json' ) { $admission_rule = import_json($data); } # If the data comes from an html form elsif ( $content_type eq 'application/x-www-form-urlencoded' ) { $admission_rule = import_html_form($data); } # We expect the data to be in YAML or JSON format else { ERROR 406, 'Admission rule description must be in YAML or JSON', "The correct format for a job request is text/yaml or application/json. " . $content_type; exit 0; } # Admission rule must have a "rule" field unless ( $admission_rule->{rule}) { ERROR 400, 'Missing Required Field', 'An admission rule must have a rule field'; exit 0; } return $admission_rule; } # Check the consistency of a posted oar admission rule for update and load it into a hashref sub check_admission_rule_update($$) { my $data = shift; my $content_type = shift; my $admission_rule; # content_type may be of the form "application/json; charset=UTF-8" ($content_type)=split(/\s*;\s*/,$content_type); # If the data comes in the YAML format if ( $content_type eq 'text/yaml' ) { $admission_rule = import_yaml($data); } # If the data comes in the JSON format elsif ( $content_type eq 'application/json' ) { $admission_rule = import_json($data); } # If the data comes from an html form elsif ( $content_type eq 'application/x-www-form-urlencoded' ) { $admission_rule = import_html_form($data); } # We expect the data to be in YAML or JSON format else { ERROR 406, 'Admission rule description must be in YAML or JSON', "The correct format for a job request is text/yaml or application/json. " . $content_type; exit 0; } # Admission rule must have a "method" field unless ( $admission_rule->{method} ) { ERROR 400, 'Missing Required Field', 'An admission rule update must have a "method" field!'; exit 0; } return $admission_rule; } # Check the consistency of a posted configuration variable and load it into a hashref sub check_configuration_variable($$) { my $data = shift; my $content_type = shift; my $parameter; # content_type may be of the form "application/json; charset=UTF-8" ($content_type)=split(/\s*;\s*/,$content_type); # If the data comes in the YAML format if ( $content_type eq 'text/yaml' ) { $parameter = import_yaml($data); } # If the data comes in the JSON format elsif ( $content_type eq 'application/json' ) { $parameter = import_json($data); } # If the data comes from an html form elsif ( $content_type eq 'application/x-www-form-urlencoded' ) { $parameter = import_html_form($data); } # We expect the data to be in YAML or JSON format else { ERROR 406, 'Configuration variable description must be in YAML or JSON', "The correct format for a job request is text/yaml or application/json. " . $content_type; exit 0; } # Parameter must have a "value" field unless ( $parameter->{value}) { ERROR 400, 'Missing Required Field', 'Configuration variable must have a value field'; exit 0; } return $parameter; } ############################################################################## # Other functions ############################################################################## # APILIB Version sub get_version() { return $VERSION; } # Return the cgi handler sub get_cgi_handler() { return $q; } # Check if YAML is enabled or exits with an error sub check_yaml() { unless ($YAMLenabled) { ERROR 400, 'YAML not enabled', 'YAML perl module not loaded!'; exit 0; } } # Check if JSON is enabled or exits with an error sub check_json() { unless ($JSONenabled) { ERROR 400, 'JSON not enabled', 'JSON perl module not loaded!'; exit 0; } } # Clean a hash from a key having an empty value (for options with parameter) sub parameter_option($$) { my $hash = shift; my $key = shift; if ((defined($hash->{"$key"}) && ($hash->{"$key"} eq "")) || not defined($hash->{"$key"})) { delete($hash->{"$key"}) } } # Remove a toggle option if value is 0 sub toggle_option($$) { my $job = shift; my $option = shift; if (defined($job->{$option})) { if ($job->{$option} eq "0" ) { delete($job->{$option}); } else { $job->{$option}="" ; }; } } # Send a command and returns the output or exit with an error sub send_cmd($$) { my $cmd=shift; my $error_name=shift; my $cmdRes = `$cmd 2>&1`; my $err = $?; if ( $err != 0 ) { #$err = $err >> 8; ERROR( 400, "$error_name error", "$error_name command exited with status $err: $cmdRes. (Command was: $cmd)." ); exit 0; } else { return $cmdRes; } } # Get a ssh key file sub get_key($$$) { my $file=shift; my $key_type=shift; my $OARDODO_CMD=shift; if ($key_type ne "private") { $file = $file.".pub"; } my $cmdRes = OAR::API::send_cmd("$OARDODO_CMD cat $file","Cat keyfile"); if ($key_type eq "private" && ! $cmdRes =~ m/.*BEGIN.*KEY/ ) { OAR::API::ERROR( 400, "Error reading file", "The keyfile is unreadable or incorrect" ); } else { return $cmdRes; } } # add_pagination # add pagination to a set of record # parameters : record,total size,uri path_info,uri query_string,extension,max_items,offset,structure # return value : / sub add_pagination($$$$$$$$) { my $record = shift; my $total = shift; my $path = shift; my $params = shift; my $ext = shift; my $limit = shift; my $offset = shift; my $STRUCTURE = shift; my $offset_separation_char = "&"; # remove leading / into path if any $path =~ s/^\///; if(defined($params) && $params ne "") { # replacing all ';' char by '&' in query string $params =~ s/;/&/g; $params =~ s/offset=(.*?)(&|$)//g; $params =~ s/&$//g; # completing path with query string or separating char if ($params ne "") { $path .= "?".$params; } else { $offset_separation_char = "?"; } } else { # no parameters was passed, the separating char # must be '?' $offset_separation_char = "?"; } # current, next and previous uri my $current_uri; my $next_uri; my $previous_uri; if (!defined $record || $total <= 0) { # return an empty structure return { items => [], total => 0, offset => 0, links => [] }; } else { # setting current uri if ($limit != 0) { $current_uri = $path.$offset_separation_char."offset=".$offset; }else{ $current_uri = $path; } # setting next uri if ($limit != 0 && ($offset + $limit < $total)) { # next items list uri $next_uri = $path.$offset_separation_char."offset=".($offset + $limit); } # setting previous uri if ($limit != 0 && ($offset - $limit >= 0)) { # previous items list uri $previous_uri = $path.$offset_separation_char."offset=".($offset - $limit); } # uris are setting into hasmaps my $links; $current_uri = OAR::API::htmlize_uri(OAR::API::make_uri($current_uri,"",0),$ext); $current_uri = { href => $current_uri, rel => "self" }; push (@$links,$current_uri); if (defined($next_uri)) { $next_uri = OAR::API::htmlize_uri(OAR::API::make_uri($next_uri,"",0),$ext); $next_uri = { href => $next_uri, rel => "next" }; push (@$links,$next_uri); } if (defined($previous_uri)) { $previous_uri = OAR::API::htmlize_uri(OAR::API::make_uri($previous_uri,"",0),$ext); $previous_uri = { href => $previous_uri , rel => "previous" }; push (@$links,$previous_uri); } my $result = { items => $record, total => int($total), offset => int($offset), links => $links, api_timestamp => time() }; return $result; } } ######################################################################## # HTML functions ######################################################################## # our $HTML_HEADER; our $apiuri; sub job_html_header($) { my $job=shift; my $jobid=$job->{id}; my $hold="holds"; if ($job->{state} eq "Running") { $hold="rholds";} print $HTML_HEADER; print "\n\n\n"; print "
Job $jobid actions:\n"; print "
\n"; print "
\n"; print "\n"; print "\n"; print "
\n"; print "
\n"; print "\n"; print "\n"; print "
\n"; print "
\n"; print "\n"; print "\n"; print "
\n"; print "
\n"; print "\n"; print "\n"; print "
\n"; print "
\n"; print "\n"; print "\n"; print "
\n"; } sub resources_commit_button($) { my $resources=shift; my $yaml_array=OAR::API::export_yaml($resources); print "
\n"; print "\n"; print "\n"; print "

"; } #### Functions for desktop computing (Thiago Presa) sub message($) { my $msg = shift; warn $msg; } sub jobStageIn($) { my $jobid = shift; my $base = OAR::IO::connect() or die "cannot connect to the data base\n"; my $stagein = OAR::IO::get_job_stagein($base,$jobid); OAR::IO::disconnect($base); if ($stagein->{'method'} eq "FILE") { open F,"< ".$stagein->{'location'} or die "Can't open stagein ".$stagein->{'location'}.": $!"; print $q->header( -status => 200, -type => "application/x-gzip" ); print ; close F; } else { print $q->header( -status => 404, -type => "application/json" ); die "Stagein method ".$stagein->{'method'}." not yet implemented.\n"; } } sub jobStageInHead($) { my $jobid = shift; my $base = OAR::IO::connect() or die "cannot connect to the data base\n"; my $stagein = OAR::IO::get_job_stagein($base,$jobid); OAR::IO::disconnect($base); if ($stagein->{'method'} eq "FILE") { open F,"< ".$stagein->{'location'} or die "Can't open stagein ".$stagein->{'location'}.": $!"; print $q->header( -status => 200, -type => "application/x-gzip" ); close F; } else { print $q->header( -status => 404, -type => "application/json" ); die "Stagein method ".$stagein->{'method'}." not yet implemented.\n"; } } sub terminateJob($) { my $jobid = shift; my $base = OAR::IO::connect() or die "cannot connect to the data base\n"; OAR::IO::lock_table($base,["jobs","job_state_logs","resources","assigned_resources","event_logs","challenges","moldable_job_descriptions","job_types","job_dependencies","job_resource_groups","job_resource_descriptions"]); OAR::IO::set_job_state($base,$jobid,"Terminated"); OAR::IO::set_finish_date($base,$jobid); OAR::IO::set_job_message($base,$jobid,"ALL is GOOD"); OAR::IO::unlock_table($base); OAR::IO::disconnect($base); } sub runJob($) { my $jobid = shift; my $base = OAR::IO::connect() or die "cannot connect to the data base\n"; OAR::IO::lock_table($base,["jobs","job_state_logs","resources","assigned_resources","event_logs","challenges","moldable_job_descriptions","job_types","job_dependencies","job_resource_groups","job_resource_descriptions"]); #OAR::IO::set_running_date($base,$jobid); OAR::IO::set_job_state($base,$jobid,"Running"); OAR::IO::unlock_table($base); OAR::IO::disconnect($base); } sub errorJob($) { my $jobid = shift; my $base = OAR::IO::connect() or die "cannot connect to the data base\n"; OAR::IO::lock_table($base,["jobs","job_state_logs","resources","assigned_resources","event_logs","challenges","moldable_job_descriptions","job_types","job_dependencies","job_resource_groups","job_resource_descriptions"]); OAR::IO::set_running_date($base,$jobid); OAR::IO::set_job_state($base,$jobid,"Error"); OAR::IO::unlock_table($base); OAR::IO::disconnect($base); } sub sign_in($$$$$) { my $hostname = shift; my $remote_host = shift; my $remote_port = shift; my $expiry = shift; my $allow_create_node = shift; my $do_notify; my $base = OAR::IO::connect() or die "cannot connect to the data base\n"; my $is_desktop_computing = OAR::IO::is_node_desktop_computing($base,$hostname); if (defined $is_desktop_computing and $is_desktop_computing eq 'YES'){ OAR::IO::lock_table($base,["resources"]); if (OAR::IO::set_node_nextState_if_necessary($base,$hostname,"Alive") > 0){ $do_notify=1; } OAR::IO::set_node_expiryDate($base,$hostname, iolib::get_date($base) + $expiry); OAR::IO::unlock_table($base); } elsif ($allow_create_node) { my $resource = OAR::IO::add_resource($base, $hostname, "Alive"); OAR::IO::set_resource_property($base,$resource,"desktop_computing","YES"); OAR::IO::set_resource_nextState($base,$resource,"Alive"); OAR::IO::set_node_expiryDate($base,$hostname, iolib::get_date($base) + $expiry); $do_notify=1; } if ($do_notify) { OAR::Tools::notify_tcp_socket($remote_host,$remote_port,"ChState"); } OAR::IO::disconnect($base); } return 1; ./oar-2.5.2/sources/api/features/0000755000175000017500000000000011757171206014633 5ustar plbplb./oar-2.5.2/sources/api/features/step_definitions/0000755000175000017500000000000011757171206020201 5ustar plbplb./oar-2.5.2/sources/api/features/step_definitions/oar_steps.rb0000644000175000017500000000076711757171206022537 0ustar plbplbrequire 'oarrestapi_lib' When /^I submit a "([^"]*)" command job$/ do |command| APIURI="http://kameleon:kameleon@192.168.56.101/oarapi-priv" @oar_server = OarApi.new(APIURI) job = { 'resource' => "/nodes=1/core=1" , 'script' => command } @oar_server.submit_job(job) end Then /^the job status should be "([^"]*)"$/ do |status| @oar_server.jobstatus['status'].to_s.should == status end Then /^the job list should not be empty$/ do @oar_server.jobarray['items'].should_not be_empty end ./oar-2.5.2/sources/api/features/oarrestapi_lib.rb0000644000175000017500000003573711757171206020176 0ustar plbplbrequire 'rubygems' require 'rest_client' require 'json' require 'pp' ####################################################################### #Coded By Narayanan K - GSOC Testsuites project - RESTful API Library ####################################################################### class OarApi attr_accessor :jobhash, :statushash, :specificjobdetails, :oarv, :oartz, :jobarray, :deletehash, :apiuri, :value attr_reader :deletestatus,:jobstatus,:api,:chkpointstatus,:holdjob,:rholdjob,:signalreturn,:resumejob,:resources,:resourcedetails,:resstatus,:specificres,:noderesources def initialize(apiuri,get_uri="") @apiuri = apiuri @api = RestClient::Resource.new @apiuri @jobarray = [] end ######################################################################## # # GET REST OAR API # # Purpose: Function to get objects from the api # # Result: We use the JSON format # ######################################################################## def get(api,uri) begin return JSON.parse(api[uri].get(:accept => 'application/json')) rescue => e if e.respond_to?('http_code') puts "ERROR #{e.http_code}:\n #{e.response.body}" else puts "Parse error:" puts e.inspect end exit 1 end end ######################################################################## # # POST REST OAR API # # Purpose: Function to create/delete/hold/resume objects through the api # # Result: We use the JSON format. # ######################################################################## def post(api,uri,j) begin j=j.to_json return JSON.parse(api[uri].post( j,:content_type => 'application/json')) rescue => e if e.respond_to?('http_code') puts "ERROR #{e.http_code}:\n #{e.response.body}" else puts "Parse error:" puts e.inspect end exit 1 end end ######################################################################## # # DELETE REST OAR API # # Purpose: Function to Delete objects through the api # # Result: We use the JSON format. # ######################################################################## def delete(api, uri) begin return JSON.parse(api[uri].delete(:content_type => 'application/json')) rescue => e if e.respond_to?('http_code') puts "ERROR #{e.http_code}:\n #{e.response.body}" else puts "Parse error:" puts e.inspect end exit 1 end end ######################################################################## # # GENERIC FUNCTIONS TO GET REST OBJECTS # ######################################################################## def get_hash(uri) @value = get(@api, uri) if !@value.is_a?(Hash) raise "Error: GET #{uri} should return a hash" end end ######################################################################## # # Method: oar_version # # Usecase01: Gives version info & Timezone about OAR and OAR API/Server. # # Input: Nil # # Result: GETs the Version details(hash)and stores in Hash oarv # ######################################################################## def oar_version @oarv = get(@api, '/version') if !@oarv.is_a?(Hash) or @oarv.empty? raise 'Error: In return value of GET /version API' end end ######################################################################## # # Method: oar_timezone # # Usecase02: Gives the timezone of the OAR API server. # # Input: Nil # # Result: GETs the Timezone details(hash)and stores in Hash oart # ######################################################################## def oar_timezone @oartz = get(@api, '/timezone') if !@oartz.is_a?(Hash) or @oartz.empty? raise 'Error: In return value of GET /timezone API' end end ######################################################################## # # Method: full_job_details # # Usecase03: List the current jobs & some details like assigned resources # # Input: Nil # # Result: GETs details of current jobs(array of hashes)& stores in jobhash # ######################################################################## def full_job_details @jobarray = get(@api,'/jobs/details') if !@jobarray.is_a?(Hash) raise 'Error: In return value of GET /jobs/details API' end end ######################################################################## # # Method: run_job_details # # Usecase04: List currently running jobs # # Input: Nil # # Result: GETs details of running jobs(array of hashes)& stores in jobhash # ######################################################################## def run_job_details @jobarray = get(@api,'/jobs') if !@jobarray.is_a?(Hash) raise 'Error: In return value of GET /jobs API' end end ######################################################################## # # Method: specific_job_details(jobid) # # Usecase05: Get Details of a specific job # # Input: jobid # # Result: GETs details of specific job & stores in hash specificjobdetails # ######################################################################## def specific_job_details(jobid) @specificjobdetails = get(@api, "/jobs/#{jobid}") if !@specificjobdetails.is_a?(Hash) or @specificjobdetails.empty? raise 'Error: In return value of GET /jobs/ API' end end ######################################################################## # # Method: dump_job_table # # Usecase06: Dump the jobs table (only current jobs) # # Input: None # # Result: Dumps details of current jobs into array of hash - jobhash # ######################################################################## def dump_job_table @jobarray = get(@api,'/jobs/table') if !@jobarray.is_a?(Hash) raise 'Error: In return value of GET /jobs/table API' end end ######################################################################## # # Method: submit_job(jhash) # # Usecase07: Submits job # # Input: jhash containing details of resources,jobscript in hash form # # Result: Returns the submitted job Details in Hash and stores in jobstatus # ######################################################################## def submit_job(jhash) @jobstatus = post(@api, '/jobs', jhash) if !@jobstatus.is_a?(Hash) or @jobstatus.empty? raise 'Error: In return value of POST /jobs API' end end ######################################################################## # # Method: del_job(jobid) # # Usecase08: Delete job - POST /jobs/id/deletions/new # # Input: jobid # # Result: Returns the deleted job Details in Hash and stores in deletestatus # ######################################################################## def del_job(jobid) @deletestatus = post(@api,"/jobs/#{jobid}/deletions/new", '') if !@deletestatus.is_a?(Hash) or @deletestatus.empty? raise 'Error: In return value of POST /jobs//deletions/new API' end end def del_array_job(jobid) @deletestatus = post(@api,"/jobs/array/#{jobid}/deletions/new", '') if !@deletestatus.is_a?(Hash) or @deletestatus.empty? raise 'Error: In return value of POST /jobs/array//deletions/new API' end end ######################################################################## # # Method: send_checkpoint(jobid) # # Usecase09: Send checkpoint signal to a job # # Input: jobid # # Result: Returns details of checkpointed job in hash - chkpointstatus # ######################################################################## def send_checkpoint(jobid) @chkpointstatus = post(@api,"/jobs/#{jobid}/checkpoints/new", '') if !@chkpointstatus.is_a?(Hash) or @chkpointstatus.empty? raise 'Error: In return value of POST /jobs//checkpoints/new API' end end ######################################################################## # # Method: hold_waiting_job(jobid) # # Usecase10: Hold a Waiting job # # Input: jobid # # Result: Returns details of holded job in hash - holdjob # ######################################################################## def hold_waiting_job(jobid) @holdjob = post(@api,"/jobs/#{jobid}/holds/new", '') if !@holdjob.is_a?(Hash) or @holdjob.empty? raise 'Error: In return value of POST /jobs//holds/new API' end end ######################################################################## # # Method: hold_running_job(jobid) # # Usecase11: Hold a Running job # # Input: jobid # # Result: Returns details of holded job in hash - rholdjob # ######################################################################## def hold_running_job(jobid) @rholdjob = post(@api,"/jobs/#{jobid}/rholds/new", '') if !@rholdjob.is_a?(Hash) or @rholdjob.empty? raise 'Error: In return value of POST /jobs//rholds/new API' end end ######################################################################## # # Method: resume_hold_job(jobid) # # Usecase12: Resume a Holded job # # Input: jobid # # Result: Returns details of resumed job in hash - resumejob # ######################################################################## def resume_hold_job(jobid) @resumejob = post(@api,"/jobs/#{jobid}/resumption/new", '') if !@resumejob.is_a?(Hash) or @resumejob.empty? raise 'Error: In return value of POST /jobs//resumption/new API' end end ######################################################################## # # Method: send_signal_job(jobid, signo) # # Usecase13: Send signal to a job with signalno. # # Input: jobid, signal number # # Result: Returns details of signalled job in hash - signalreturn # ######################################################################## def send_signal_job(jobid, signo) @signalreturn = post(@api,"/jobs/#{jobid}/signal/#{signo}", '') if !@signalreturn.is_a?(Hash) or @signalreturn.empty? raise 'Error: In return value of POST /jobs//signal/ API' end end ######################################################################## # # Method: update_job(jobid, actionhash) # # Usecase14: Update a job # # Input: jobid, actionhash # # Result: Returns details of updated job in hash - updatehash # ######################################################################## def update_job(jobid, actionhash) @updatehash = post(@api, "/jobs/#{jobid}",actionhash) if !@updatehash.is_a?(Hash) or @updatehash.empty? raise 'Error: In return value of POST /jobs// API' end end ######################################################################## # # Method: resource_list_state # # Usecase15: Get list of Resources and state # # Input: None # # Result: Returns details of resources & states in array of hashes - resources # ######################################################################## def resource_list_state @resources = get(@api, '/resources') if !@resources.is_a?(Hash) raise 'Error: In return value of GET /resources API' end end ######################################################################## # # Method: list_resource_details # # Usecase16: Get list of all the resources and all their details # # Input: None # # Result: Returns details of resource list in array of hashes - resourcedetails # ######################################################################## def list_resource_details @resourcedetails = get(@api, '/resources/full') if !@resourcedetails.is_a?(Hash) raise 'Error: In return value of GET /resources/full API' end end ######################################################################## # # Method: specific_resource_details(jobid) # # Usecase17: Get details of resources identified by an ID # # Input: jobid # # Result: Returns details of specific resource in array of hashes - specificres # ######################################################################## def specific_resource_details(jobid) @specificres = get(@api, "/jobs/#{jobid}/resources") if !@specificres.is_a?(Hash) or @specificres.empty? raise 'Error: In return value of GET /jobs//resources API' end end ######################################################################## # # Method: resource_of_nodes(netaddr) # # Usecase18: Get details about the resources belonging to the node identified by network address # # Input: netaddr # # Result: Returns details of resource of nodes - noderesources # ######################################################################## def resource_of_nodes(netaddr) @noderesources = get(@api,"/resources/nodes/#{netaddr}") if !@noderesources.is_a?(Hash) or @noderesources.empty? raise 'Error: In return value of GET /resources/nodes/ API' end end ######################################################################## # # Method: create_resource(rhash) # # Usecase19: Create Resource # # Input: rhash # # Result: Returns details of created resources - resstatus # ######################################################################## def create_resource(rhash) @resstatus = post(@api,'/resources', rhash) if !@resstatus.is_a?(Hash) or @resstatus.empty? raise 'Error: In return value of POST /resources API' end end ######################################################################## # # Method: statechange_resource(jobid, hasharray) # # Usecase20: Change the state of resources of a job # # Input: jobid, hasharray # # Result: Returns details of created resources in hash- statushash # ######################################################################## def statechange_resource(jobid, hasharray) @statushash = post(@api, '/resources/#{jobid}/state', hasharray) if !@statushash.is_a?(Hash) or @statushash.empty? raise 'Error: In return value of POST /resources//state API' end end ######################################################################## # # Method: delete_job(jobid) # # Usecase21: Delete or kill a job. # # Input: jobid # # Result: Returns details of deleted job in hash- deletehash # ######################################################################## def delete_job(jobid) @deletehash = delete(@api,"/jobs/#{jobid}") if !@deletehash.is_a?(Hash) or @deletehash.empty? raise 'Error: In return value of DELETE /jobs/ API' end end ######################################################################## # # Method: delete_resource(resid) # # Usecase22: Delete the resource identified by id # # Input: resid # # Result: Returns details of deleted resources in hash- deletehash # ######################################################################## def delete_resource(resid) @deletehash = delete(@api,"/resources/#{resid}") if !@deletehash.is_a?(Hash) or @deletehash.empty? raise 'Error: In return value of DELETE /resources/ API' end end ######################################################################## # # Method: delete_resource_cpuset(node, cpuid) # # Usecase23: Delete the resource corresponding to cpuset id on node node. # # Input: node, cpuid # # Result: Returns details of deleted resources in hash- deletehash # ######################################################################## def delete_resource_cpuset(node, cpuid) @deletehash = delete(@api,"/resources/#{node}/#{cpuid}") if !@deletehash.is_a?(Hash) or @deletehash.empty? raise 'Error: In return value of DELETE /resources// API' end end def get_link_href(rel) @value['links'].each do |link| if link.is_a?(Hash) && link['rel'] == rel return link['href'] end end raise "#{rel} link not found!" end def get_link_href_from_array(array,rel) array.each do |link| if link.is_a?(Hash) && link['rel'] == rel return link['href'] end end raise "#{rel} link not found!" end end ./oar-2.5.2/sources/api/features/job_submit.feature0000644000175000017500000000041211757171206020342 0ustar plbplbFeature: Submit jobs As a user I want to submit a job In order to get computation done Scenario: command job submission successful When I submit a "date" command job Then the job status should be "submitted" And the job list should not be empty ./oar-2.5.2/sources/api/chandler.rb0000755000175000017500000000446411757171206015135 0ustar plbplb#!/usr/bin/ruby # "Chandler" # Simple example ruby script using the OAR RESTFUL API # It prints a colored textmode status of the cluster require 'rest_client' require 'json' require 'pp' # Custom variables APIURI="http://localhost/oarapi" NODENAME_REGEX="(.*)" COLS=2 # Function to get objects from the api # We use the JSON format and the 'simple' data structure def get(api,uri) begin return JSON.parse(api[uri+'?structure=simple'].get(:accept => 'application/json')) rescue => e if e.respond_to?('http_code') puts "ERROR #{e.http_code}:\n #{e.response.body}" else puts "Parse error:" puts e.inspect end exit 1 end end # Instanciate an api connection api = RestClient::Resource.new APIURI # Print a waiting message puts printf ("Please, wait while querying OAR API...\n\033[1A") # Get the resources resources = get(api,'/resources') # Get the running jobs jobs = get(api,'jobs/details') # Erase the waiting message printf ("\033[2K") # Construct a has of used resources used_resources={} jobs['items'].each do |job| job['resources'].each do |r| used_resources[r['id']]=1 end end # Print summary puts "#{jobs['items'].length} jobs, #{resources['items'].length} resources, #{used_resources.length} used" # For each node col=0 resources['items'].collect{|r| r['network_address']}.uniq.each do |node| resources['items'].select{|r| r['network_address']==node}.each do |resource| if resource['state'] == "Dead" printf("\033[41m\033[30mD\033[0m") elsif resource['state'] == "Absent" if resource['available_upto'].to_i > Time.new().to_i printf("\033[46m \033[0m") else printf("\033[41m\033[30mA\033[0m") end elsif resource['state'] == "Suspected" printf("\033[41m\033[30mS\033[0m") elsif resource['state'] == "Alive" #jobs=get(api,resource['jobs_uri']) #if jobs.nil? if used_resources[resource['id']].nil? printf("\033[42m \033[0m") else printf("\033[47m\033[30mJ\033[0m") end end end node=~/#{NODENAME_REGEX}/ print " ",$1,"\t" col+=1 if col >= COLS col = 0 puts end end printf("\n\n\033[42m \033[0m=Free \033[46m \033[0m=Standby \033[47m\033[30mJ\033[0m=Job \033[41m\033[30mS\033[0m=Suspected \033[41m\033[30mA\033[0m=Absent \033[41m\033[30mD\033[0m=Dead\n\n") ./oar-2.5.2/sources/api/api_html_postform_rule.pl0000644000175000017500000000044111757171206020126 0ustar plbplb$POSTFORM="

Admission rule submission
Rule
" ./oar-2.5.2/sources/api/api_html_postform_resources.pl0000644000175000017500000000071011757171206021170 0ustar plbplb$POSTFORM="
Resources generation
Resources
Properties (commas separated)
" ./oar-2.5.2/sources/api/api_html_postform.pl0000644000175000017500000000152611757171206017104 0ustar plbplb$POSTFORM="
Job submission
Resources
Name
Properties
Program to run
Types
Reservation dates
Directory
" ./oar-2.5.2/sources/api/api_html_header.pl0000644000175000017500000000106711757171206016463 0ustar plbplb$HTML_HEADER = " OAR REST API
RESOURCES    GENERATE RESOURCES    JOBS    JOB SUBMISSION    ADMISSION RULES    RULE SUBMISSION    CONFIGURATION   
" ./oar-2.5.2/sources/api/apache2.conf.in0000644000175000017500000000511611757171206015577 0ustar plbplb# Example Apache2 configuration for the OAR API # Aliases to the API. # Be aware that the oarapi directory should only be readable by the httpd # daemon and that the cgi inside are sgid oar. Any change to this permissions # may cause your system to be vulnerable. ScriptAlias /oarapi %%CGIDIR%%/oarapi/oarapi.cgi ScriptAlias /oarapi-debug %%CGIDIR%%/oarapi/oarapi-debug.cgi # FastCGI server FastCgiServer %%CGIDIR%%/oarapi/oarapi.cgi # Authentication configuration for access to the API Options ExecCGI -MultiViews FollowSymLinks # FastCGI handler AddHandler fcgid-script .cgi # Deny access by default, except from localhost Order deny,allow Deny from all #Allow from trusted.host.mydomain Allow from localhost Allow from localhost.localdomain # Pidentd may be useful for testing without a login/passwd or when you # fully trust some hosts (ie users have no way to fake their login name). # Ident trust may be disabled into the api itself. IdentityCheck On # Set the X-REMOTE_IDENT http header value to REMOTE_IDENT env value RequestHeader set X_REMOTE_IDENT %{REMOTE_IDENT}e # or For https: #RequestHeader set X_REMOTE_IDENT %{REMOTE_IDENT}s # Or if it doesn't work, enable mod_rewrite and try this: RewriteEngine On RewriteCond %{REMOTE_IDENT} (.*) RewriteRule .* - [E=MY_REMOTE_IDENT:%1] RequestHeader add X-REMOTE_IDENT %{MY_REMOTE_IDENT}e # Ldap auth, except for some local hosts that we trust with pident. # In this example, we suppose that the users have an ldap account # common to the authentication used by the oar server. AuthType basic AuthBasicProvider ldap AuthName "Authorized Users Only" AuthzLDAPAuthoritative off AuthLDAPURL "ldaps://myldaphost.mydomain/dc=mydomain,dc=fr?uid?sub?(objectClass=posixAccount)" Require valid-user Satisfy any ./oar-2.5.2/sources/api/TODO0000644000175000017500000000123411757171206013505 0ustar plbplb- What about a fast-cgi version? - Remplacer les oardel par des appels à l'iolib (optimisation) - Implementer timeout dans la fonction send_cmd (a tester sur le oarsub) - Finir les formulaires et le parsing des options (check_gridjob dans apilib), debugger --FORCE - Dans l'api oargrid, définir une structure de données que l'on peut utiliser à la place de la syntaxe cluster:rdef=... (mais bien conserver cette syntaxe aussi) ?? - job keys dans OAR API (pouvoir importer la cle depuis l'api) Bugs: - Oargrid ne prends pas en compte les options multiples (exemple: -t besteffort -t timesharing), même si l'API le gère ("type"=>["timesharing", "besteffort"]) ./oar-2.5.2/sources/api/INSTALL0000644000175000017500000000327011757171206014050 0ustar plbplbInstallation of the OAR RESTful user API ======================================== Configuring OAR --------------- For the moment, the API needs the user tools to be installed on the same host ('make user-install' or oar-user package). A suitable /etc/oar/oar.conf should be present. For the API to work, you should have the oarstat/oarnodes/oarsub commands to work (on the same host you installed the API) Installing the API files ------------------------ - From sources: Download OAR and do "make api-install" as the root user. - From Debian packages: apt-get install oar-api - From RPM packages: yum install oar-api Configuring apache ------------------ The api provides a default configuration file (/etc/oar/apache-api.conf) that is using a identd user identification enabled only from localhost. Edit the /etc/oar/apache-api.conf file and customize it to reflect the authentication mechanism you want to use. For ident, you may have to install a "identd" daemon on your distrib. The steps may be: - Install and run a identd daemon on your server - Activate the ident auth mechanism into apache (a2enmod ident) - Activate the headers apache module (a2enmod headers) - Customize apache-api.conf to allow the hosts you trust for ident YAML, JSON, XML --------------- You need at least one of the YAML or JSON perl module to be installed on the host running the API. Test ---- You may test the API with a simple wget: wget -O - http://localhost:/oarapi/resources.html It should give you the list of resources in the yaml format but enclosed in an html page. To test if the authentication works, you need to post a new job. See the example.txt file that gives you example queries with a ruby rest client. ./oar-2.5.2/sources/api/DOC0000644000175000017500000000006611757171206013347 0ustar plbplbSee into ../docs/documentation/OAR-DOCUMENTATION-API* ./oar-2.5.2/setup/0000755000175000017500000000000011757171206011721 5ustar plbplb./oar-2.5.2/setup/www-conf.sh.in0000644000175000017500000000123511757171206014432 0ustar plbplb www_conf_setup() { mkdir -p ${OARCONFDIR}/apache2 || true # Check for an old configuration file. if [ -f ${OARCONFDIR}/apache.conf ]; then mv ${OARCONFDIR}/apache.conf ${OARCONFDIR}/apache2/oar-web-status.conf fi install_conffile \ ${EXAMPLEDIR}/apache.conf \ ${OARCONFDIR}/apache2/oar-web-status.conf \ 0644 \ ${WWWUSER} # install the apache2 configuration file if [ -d ${APACHECONFDIR}/conf.d ]; then if [ ! -e ${APACHECONFDIR}/conf.d/oar-web-status.conf ]; then ln -s ${OARCONFDIR}/apache2/oar-web-status.conf ${APACHECONFDIR}/conf.d/oar-web-status.conf fi fi } ./oar-2.5.2/setup/user.sh.in0000644000175000017500000000103411757171206013636 0ustar plbplb user_setup() { set_rights ${BINDIR}/oarnodes 6755 ${OARDO_DEFAULTUSER} ${OARDO_DEFAULTGROUP} set_rights ${BINDIR}/oardel 6755 ${OARDO_DEFAULTUSER} ${OARDO_DEFAULTGROUP} set_rights ${BINDIR}/oarstat 6755 ${OARDO_DEFAULTUSER} ${OARDO_DEFAULTGROUP} set_rights ${BINDIR}/oarsub 6755 ${OARDO_DEFAULTUSER} ${OARDO_DEFAULTGROUP} set_rights ${BINDIR}/oarhold 6755 ${OARDO_DEFAULTUSER} ${OARDO_DEFAULTGROUP} set_rights ${BINDIR}/oarresume 6755 ${OARDO_DEFAULTUSER} ${OARDO_DEFAULTGROUP} } ./oar-2.5.2/setup/tools.sh.in0000644000175000017500000000014711757171206014024 0ustar plbplb tools_setup() { set_rights ${SBINDIR}/oaradmin 6754 ${OARDO_DEFAULTUSER} ${OARDO_DEFAULTGROUP} } ./oar-2.5.2/setup/templates/0000755000175000017500000000000011757171206013717 5ustar plbplb./oar-2.5.2/setup/templates/header.sh.in0000644000175000017500000000566211757171206016121 0ustar plbplb#! /bin/sh set -e # # Variables defined during the oar installation process # PREFIX="%%PREFIX%%" BINDIR="%%BINDIR%%" CGIDIR="%%CGIDIR%%" DOCDIR="%%DOCDIR%%" EXAMPLEDIR="%%EXAMPLEDIR%%" ETCDIR="%%ETCDIR%%" OARCONFDIR="%%OARCONFDIR%%" OARDIR="%%OARDIR%%" SHAREDIR="%%SHAREDIR%%" PERLLIBDIR="%%PERLLIBDIR%%" RUNDIR="%%RUNDIR%%" LOGDIR="%%LOGDIR%%" MANDIR="%%MANDIR%%" SBINDIR="%%SBINDIR%%" VARLIBDIR="%%VARLIBDIR%%" OARHOMEDIR="%%OARHOMEDIR%%" ROOTUSER="%%ROOTUSER%%" ROOTGROUP="%%ROOTGROUP%%" OARDO_DEFAULTUSER="%%OARDO_DEFAULTUSER%%" OARDO_DEFAULTGROUP="%%OARDO_DEFAULTGROUP%%" OARUSER="%%OARUSER%%" OAROWNER="%%OAROWNER%%" OAROWNERGROUP="%%OAROWNERGROUP%%" WWWUSER="%%WWWUSER%%" APACHECONFDIR="%%APACHECONFDIR%%" WWW_ROOTDIR="%%WWW_ROOTDIR%%" WWWDIR="%%WWWDIR%%" XAUTHCMDPATH="%%XAUTHCMDPATH%%" OARSHCMD="%%OARSHCMD%%" INITDIR="%%INITDIR%%" DEFAULTDIR="%%DEFAULTDIR%%" SETUP_TYPE="%%SETUP_TYPE%%" TARGET_DIST="%%TARGET_DIST%%" # # shared functions for oar setup files. # install_conffile() { case "${SETUP_TYPE}" in "deb") install_deb_conffile $* ;; "rpm") install_rpm_conffile $* ;; "tgz"|*) install_if_not_exist $* ;; esac } install_deb_conffile() { local src dst rights owner tmpfile src=$1 dst=$2 rights=$3 owner=$4 # Decompress the file, if compressed tmpfile= if [ ! -f "$src" ] && [ -f "${src}.gz" ]; then tmpfile=$(tempfile) zcat ${src}.gz > $tmpfile src=$tmpfile fi ucf --debconf-ok $src $dst if [ -n "$rights" ]; then chmod $rights $dst fi if [ -n "$owner" ]; then chown $owner $dst fi if [ -f "$tmpfile" ]; then rm -f "$tmpfile" fi } install_rpm_conffile() { # I've not found ucf or equivalent to install config file during postinst # in the rpm world. So the config file are nstalled manually in the spec # file. local src dst rights owner tmpfile src=$1 dst=$2 rights=$3 owner=$4 if [ -n "$rights" ]; then chmod $rights $dst fi if [ -n "$owner" ]; then chown $owner $dst fi } install_if_not_exist() { local src dst rights owner src=$1 dst=$2 rights=$3 owner=$4 # Decompress the file, if compressed tmpfile= if [ ! -f "$src" ] && [ -f "${src}.gz" ]; then tmpfile=$(tempfile) zcat ${src}.gz > $tmpfile src=$tmpfile fi if [ -f "$dst" ]; then : else install $src $dst if [ -n "$rights" ]; then chmod $rights $dst fi if [ -n "$owner" ]; then chown $owner $dst fi fi if [ -f "$tmpfile" ]; then rm -f "$tmpfile" fi } set_rights() { file=$1 perms=$2 owner=$3 group=$4 [ -n "$owner" ] && chown $owner $file [ -n "$group" ] && chgrp $group $file [ -n "$perms" ] && chmod $perms $file } ./oar-2.5.2/setup/server.sh.in0000644000175000017500000000514511757171206014175 0ustar plbplb setup_oar_ssh() { if [ ! -e ${OARHOMEDIR}/.ssh ]; then mkdir -p ${OARHOMEDIR}/.ssh ssh-keygen -t rsa -q -f ${OARHOMEDIR}/.ssh/id_rsa -N '' || true echo -n 'environment="OAR_KEY=1" ' > ${OARHOMEDIR}/.ssh/authorized_keys || true cat ${OARHOMEDIR}/.ssh/id_rsa.pub >> ${OARHOMEDIR}/.ssh/authorized_keys || true cat <<-EOF > ${OARHOMEDIR}/.ssh/config || true Host * ForwardX11 no StrictHostKeyChecking no PasswordAuthentication no AddressFamily inet EOF chown ${OAROWNER}:${OAROWNERGROUP} ${OARHOMEDIR}/.ssh -R || true fi } create_phoenix_home() { mkdir -p ${OARHOMEDIR}/phoenix chown ${OAROWNER}:${OAROWNERGROUP} ${OARHOMEDIR}/phoenix } server_setup() { install_conffile \ ${EXAMPLEDIR}/job_resource_manager.pl \ ${OARCONFDIR}/job_resource_manager.pl \ 0644 install_conffile \ ${EXAMPLEDIR}/job_resource_manager_cgroups.pl \ ${OARCONFDIR}/job_resource_manager_cgroups.pl \ 0644 install_conffile \ ${EXAMPLEDIR}/suspend_resume_manager.pl \ ${OARCONFDIR}/suspend_resume_manager.pl \ 0644 install_conffile \ ${EXAMPLEDIR}/oarmonitor_sensor.pl \ ${OARCONFDIR}/oarmonitor_sensor.pl \ 0644 install_conffile \ ${EXAMPLEDIR}/wake_up_nodes.sh \ ${OARCONFDIR}/wake_up_nodes.sh install_conffile \ ${EXAMPLEDIR}/shut_down_nodes.sh \ ${OARCONFDIR}/shut_down_nodes.sh install_conffile \ ${EXAMPLEDIR}/server_prologue \ ${OARCONFDIR}/server_prologue install_conffile \ ${EXAMPLEDIR}/server_epilogue \ ${OARCONFDIR}/server_epilogue setup_oar_ssh create_phoenix_home set_rights ${SBINDIR}/oar_resources_init 6754 ${OARDO_DEFAULTUSER} ${OARDO_DEFAULTGROUP} set_rights ${SBINDIR}/Almighty 6754 ${OARDO_DEFAULTUSER} ${OARDO_DEFAULTGROUP} set_rights ${SBINDIR}/oarnotify 6754 ${OARDO_DEFAULTUSER} ${OARDO_DEFAULTGROUP} set_rights ${SBINDIR}/oarremoveresource 6754 ${OARDO_DEFAULTUSER} ${OARDO_DEFAULTGROUP} set_rights ${SBINDIR}/oaraccounting 6754 ${OARDO_DEFAULTUSER} ${OARDO_DEFAULTGROUP} set_rights ${SBINDIR}/oarproperty 6754 ${OARDO_DEFAULTUSER} ${OARDO_DEFAULTGROUP} set_rights ${SBINDIR}/oarmonitor 6754 ${OARDO_DEFAULTUSER} ${OARDO_DEFAULTGROUP} set_rights ${SBINDIR}/oar_resources_init 6754 ${OARDO_DEFAULTUSER} ${OARDO_DEFAULTGROUP} set_rights ${SBINDIR}/oar_checkdb 6754 ${OARDO_DEFAULTUSER} ${OARDO_DEFAULTGROUP} set_rights ${SBINDIR}/oar_phoenix 6754 ${OARDO_DEFAULTUSER} ${OARDO_DEFAULTGROUP} } ./oar-2.5.2/setup/node.sh.in0000644000175000017500000000356011757171206013613 0ustar plbplbinstall_ssh_host_keys() { if [ ! -r ${OARCONFDIR}/oar_ssh_host_rsa_key ]; then rm -f ${OARCONFDIR}/oar_ssh_host_rsa_key.pub if [ -e "${ETCDIR}/ssh/ssh_host_rsa_key" ]; then install_if_not_exist \ ${ETCDIR}/ssh/ssh_host_rsa_key \ ${OARCONFDIR}/oar_ssh_host_rsa_key \ 0600 install_if_not_exist \ ${ETCDIR}/ssh/ssh_host_rsa_key.pub \ ${OARCONFDIR}/oar_ssh_host_rsa_key.pub fi fi if [ ! -r ${OARCONFDIR}/oar_ssh_host_dsa_key ]; then rm -f ${OARCONFDIR}/oar_ssh_host_dsa_key.pub if [ -e "${ETCDIR}/ssh/ssh_host_dsa_key" ]; then install_if_not_exist \ ${ETCDIR}/ssh/ssh_host_dsa_key \ ${OARCONFDIR}/oar_ssh_host_dsa_key \ 0600 install_if_not_exist \ ${ETCDIR}/ssh/ssh_host_dsa_key.pub \ ${OARCONFDIR}/oar_ssh_host_dsa_key.pub fi fi if [ ! "-d ${VARLIBDIR}/oar/.ssh/id_rsa" ] && [ ! "-d ${VARLIBDIR}/oar/.ssh/id_dsa" ]; then echo " ########################################################################## # node # ######## # You need to install the ssh keys (private and public) of the oar user # into ${VARLIBDIR}/oar/.ssh. # A common way is to copy the entire .ssh directory from the server on # all the nodes of your cluster. ########################################################################## " fi } node_setup() { install_conffile \ ${EXAMPLEDIR}/epilogue \ ${OARCONFDIR}/epilogue install_conffile \ ${EXAMPLEDIR}/prologue \ ${OARCONFDIR}/prologue install_conffile \ ${EXAMPLEDIR}/sshd_config \ ${OARCONFDIR}/sshd_config \ 0600 ${OAROWNER} install_ssh_host_keys } ./oar-2.5.2/setup/monika.sh.in0000644000175000017500000000022511757171206014137 0ustar plbplb monika_setup() { install_conffile \ ${EXAMPLEDIR}/monika.conf \ ${OARCONFDIR}/monika.conf \ 0600 \ ${WWWUSER} } ./oar-2.5.2/setup/logrotate.d/0000755000175000017500000000000011757171206014143 5ustar plbplb./oar-2.5.2/setup/logrotate.d/oar-common.in0000644000175000017500000000014611757171206016543 0ustar plbplb%%LOGDIR%%/oar.log { rotate 7 daily compress delaycompress copytruncate missingok notifempty } ./oar-2.5.2/setup/init.d/0000755000175000017500000000000011757171206013106 5ustar plbplb./oar-2.5.2/setup/init.d/oar-server.in0000644000175000017500000000734611757171206015535 0ustar plbplb#! /bin/sh # # oar-server OAR Server # # # --/--/----: Based on startup scripts from Bruno Bzeznik # 02/09/2011: Merge RPM and Debian scripts by Philippe Le Brouster # # chkconfig: 2345 90 10 # description: This script starts or stops the OAR resource manager # processname: Almighty # config: /etc/oar/oar.conf # pidfile: /var/run/oar-server.pid ### BEGIN INIT INFO # Provides: oar-server # Required-Start: $network $local_fs $remote_fs $all # Required-Stop: $network $local_fs $remote_fs # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: OAR server # Description: This script starts or stops the OAR resource manager ### END INIT INFO # Author: Bruno Bzeznik # LANG=C export LANG PATH=%%SBINDIR%%:%%BINDIR%%:/sbin:/usr/sbin:/bin:/usr/bin:$PATH NAME=oar-server DESC="OAR resource manager (server)" DAEMON=%%SBINDIR%%/oar-server DAEMON_NAME=Almighty DAEMON_ARGS="" PIDFILE=%%RUNDIR%%/$NAME.pid SCRIPTNAME=%%INITDIR%%/$NAME NOLSB= [ -f /lib/lsb/init-functions ] || NOLSB=yes if [ -f /etc/debian_version ]; then system=debian elif [ -f /etc/redhat-release ]; then system=redhat elif [ -f /etc/SuSE-release ]; then system=suse elif [ -f /etc/gentoo-release ]; then system=gentoo fi # Exit if the package is not installed [ -x "$DAEMON" ] || exit 0 # Read configuration variable file if it is present [ -r %%DEFAULTDIR%%/$NAME ] && . %%DEFAULTDIR%%/$NAME if [ -z "$NOLSB" ]; then . /lib/lsb/init-functions fail_msg() { echo "" log_failure_msg "$@" } warn_msg() { log_warning_msg "$@" } succ_msg() { log_success_msg "$@" } begin_msg() { echo -n "$@:" } else echo "This system doesn't provide the LSB functions. Failing" exit 2 fi do_start() { begin_msg "Starting $DESC" CHECK_STRING=`oar_checkdb > /dev/null 2>&1` if [ "$?" -ne "0" ] then MSG="Database is not ready! Maybe not initiated or no DBMS running? You must have a running MySQL or Postgres server. To init the DB, Check the DB_* variables in /etc/oar/oar.conf and use the script 'oar-database'" fail_msg "$MSG" exit 2 fi if pidofproc -p $PIDFILE > /dev/null; then fail_msg "already running" exit 1 fi start_daemon -p "$PIDFILE" "$DAEMON" $DAEMON_ARGS RET=$? if [ "$RET" != 0 ]; then fail_msg "Unable to start" else succ_msg # redhat world [ -d /var/lock/subsys/ ] && touch /var/lock/subsys/$NAME fi } # # Function that stops the daemon/service # do_stop() { begin_msg "Stopping $DESC" # Return # 0 if daemon has been stopped # 1 if daemon was already stopped # 2 if daemon could not be stopped # other if a failure occurred killproc -p $PIDFILE RETVAL="$?" if [ "$RETVAL" = 2 ]; then fail_msg "Unable to stop $DESC" exit 2 fi # Sarko is often frozed when the database is unreachable killproc sarko # Extermination... killproc $DAEMON_NAME if [ "$?" = 2 ]; then fail_msg "Unable to stop $DESC" exit 2 fi # Many daemons don't delete their pidfiles when they exit. rm -f $PIDFILE # redhat world [ -d /var/lock/subsys/ ] && rm -f /var/lock/subsys/$NAME succ_msg return 0 } case "$1" in start) do_start ;; stop) do_stop ;; reload|restart|force-reload) if do_stop; then sleep 1 do_start fi ;; *) echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2 exit 1 ;; esac : ./oar-2.5.2/setup/init.d/oar-node.in0000644000175000017500000000675411757171206015156 0ustar plbplb#! /bin/sh # # oar-node OAR Dedicated Node # # # --/--/----: Based on startup scripts from Bruno Bzeznik # 02/09/2011: Merge RPM and Debian scripts by Philippe Le Brouster # # chkconfig: 2345 99 01 # description: OAR Dedicated Node # ### BEGIN INIT INFO # Provides: oar-node # Required-Start: $all # Required-Stop: $all # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: OAR Dedicated Node (launch its own sshd) ### END INIT INFO LANG=C export LANG PATH=%%SBINDIR%%:%%BINDIR%%:/sbin:/bin:/usr/sbin:/usr/bin:$PATH NAME=oar-node DESC=oar-node OAR_SSHD_CONF="%%OARCONFDIR%%/sshd_config" SSHD_OPTS="-f $OAR_SSHD_CONF -o PidFile=%%OARHOMEDIR%%/oar_sshd.pid" NOLSB= [ -f /lib/lsb/init-functions ] || NOLSB=yes if [ -f /etc/debian_version ]; then system=debian elif [ -f /etc/redhat-release ]; then system=redhat elif [ -f /etc/SuSE-release ]; then system=suse elif [ -f /etc/gentoo-release ]; then system=gentoo fi start_oar_node() { echo " * Edit start_oar_node function in %%DEFAULTDIR%%/oar-node if you want" echo " to perform a specific action (e.g. to switch the node to Alive)" } stop_oar_node() { echo " * Edit stop_oar_node function in %%DEFAULTDIR%%/oar-node if you want" echo " to perform a specific action (e.g. to switch the node to Absent)" } [ -r %%DEFAULTDIR%%/oar-node ] && . %%DEFAULTDIR%%/oar-node if [ -z "$NOLSB" ]; then . /lib/lsb/init-functions fail_msg() { echo "" log_failure_msg "$@" } warn_msg() { log_warning_msg "$@" } succ_msg() { log_success_msg "$@" } begin_msg() { echo -n "$@: " } else echo "This system doesn't provide the LSB functions. Failing" exit 2 fi failure() { fail_msg "$1" exit 0 } do_start() { begin_msg "Starting $DESC" if [ -f "$OAR_SSHD_CONF" ] ; then if start_daemon -p "%%OARHOMEDIR%%/oar_sshd.pid" -n "-20" /usr/sbin/sshd $SSHD_OPTS; then # redhat world [ -d /var/lock/subsys/ ] && touch /var/lock/subsys/$NAME succ_msg "OAR dedicated SSH server started." else fail_msg "Failed to start OAR dedicated SSH server." exit 2 fi fi begin_msg "Executing The $DESC startup actions" if start_oar_node; then succ_msg "The $DESC startup actions has been executed sucessfully." else fail_msg "Failed to execute the $DESC startup actions." exit 2 fi } do_stop() { begin_msg "Executing the $DESC shutdown actions" if stop_oar_node; then succ_msg "This $DESC shutdown actions has been executed sucessfully." else fail_msg "Failed to execute the $DESC shutdown actions." fi begin_msg "Stopping $DESC " if [ -f "$OAR_SSHD_CONF" ] ; then if killproc -p "%%OARHOMEDIR%%/oar_sshd.pid"; then # redhat world [ -d /var/lock/subsys/ ] && rm -f /var/lock/subsys/$NAME succ_msg "OAR dedicated SSH server stopped." else fail_msg "Failed to stop OAR dedicated SSH server." exit 2 fi fi } case "$1" in start) do_start ;; stop) do_stop ;; reload|force-reload|restart) if do_stop; then do_start fi ;; *) N=%%INITDIR%%/$NAME echo "Usage: $N {start|stop|reload|force-reload|restart}" exit 1 ;; esac exit 0 ./oar-2.5.2/setup/init.d/oar-desktop-computing-agent.in0000644000175000017500000000512611757171206020771 0ustar plbplb#! /bin/sh # # desktop-computing-agent # # --/--/----: Based on startup scripts from Bruno Bzeznik # 02/09/2011: Merge RPM and Debian scripts by Philippe Le Brouster # # chkconfig: 2345 90 10 # description: This script starts or stops the desktop computing agent # processname: oar-agent-daemon # pidfile: %%OARHOMEDIR%%/oar_agent.pid ### BEGIN INIT INFO # Provides: oar-desktop-computing-agent # Required-Start: $network $local_fs $remote_fs # Required-Stop: $network $local_fs $remote_fs # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: OAR desktop computing agent daemon starting ### END INIT INFO PATH=%%SBINDIR%%:%%BINDIR%%:/sbin:/bin:/usr/sbin:/usr/bin NAME=oar-desktop-computing-agent DESC=desktop-computing-agent DAEMON=%%SBINDIR%%/oar-agent-daemon DAEMON_NAME=oar-agent-daemon PIDFILE=%%OARHOMEDIR%%/oar_agent.pid SCRIPTNAME=%%INITDIR%%/$NAME NOLSB= [ -f /lib/lsb/init-functions ] || NOLSB=yes if [ -f /etc/debian_version ]; then system=debian elif [ -f /etc/redhat-release ]; then system=redhat elif [ -f /etc/SuSE-release ]; then system=suse elif [ -f /etc/gentoo-release ]; then system=gentoo fi # Exit if the package is not installed [ -x "$DAEMON" ] || exit 0 if [ -z "$NOLSB" ]; then . /lib/lsb/init-functions fail_msg() { echo "" log_failure_msg "$@" } warn_msg() { log_warning_msg "$@" } succ_msg() { log_success_msg "$@" } begin_msg() { echo -n "$@:" } else echo "This system doesn't provide the LSB functions. Failing" exit 2 fi do_start() { begin_msg "Starting $DESC" if pidofproc -p $PIDFILE > /dev/null; then fail_msg " already running" exit 1 fi start_daemon -p "$PIDFILE" -n "-20" "$DAEMON" start RET=$? if [ "$RET" != 0 ]; then fail_msg "Unable to start" else succ_msg fi } do_stop() { begin_msg "Stopping $DESC" killproc -p $PIDFILE RETVAL="$?" if [ "$RETVAL" = 2 ]; then fail_msg "Unable to stop $DESC" exit 2 fi killproc $DAEMON_NAME if [ "$?" = 2 ]; then fail_msg "Unable to stop $DESC" exit 2 fi rm -f $PIDFILE succ_msg exit "$RETVAL" } case "$1" in start) do_start ;; stop) do_stop ;; reload|restart|force-reload) if do_stop; then sleep 1 do_start fi ;; *) echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2 exit 1 ;; esac exit 0 ./oar-2.5.2/setup/draw-gantt.sh.in0000644000175000017500000000144311757171206014734 0ustar plbplb draw_gantt_setup() { install_conffile \ ${EXAMPLEDIR}/drawgantt.conf \ ${OARCONFDIR}/drawgantt.conf \ 0600 \ ${WWWUSER} if [ ! -d "${OARHOMEDIR}/drawgantt-files" ]; then mkdir -p ${OARHOMEDIR}/drawgantt-files set_rights ${OARHOMEDIR}/drawgantt-files 0755 fi if [ ! -e "${OARHOMEDIR}/drawgantt-files/js" ]; then ln -sf ${WWWDIR}/drawgantt-files/js ${OARHOMEDIR}/drawgantt-files/js fi if [ ! -e "${OARHOMEDIR}/drawgantt-files/Icons" ]; then ln -sf ${WWWDIR}/drawgantt-files/Icons ${OARHOMEDIR}/drawgantt-files/Icons fi if [ ! -d "${OARHOMEDIR}/drawgantt-files/cache" ]; then mkdir -p ${OARHOMEDIR}/drawgantt-files/cache set_rights ${OARHOMEDIR}/drawgantt-files/cache 0755 ${WWWUSER} fi } ./oar-2.5.2/setup/desktop-computing-cgi.sh.in0000644000175000017500000000042411757171206017076 0ustar plbplb desktop_computing_cgi_setup() { set_rights ${SBINDIR}/oarcache 6754 ${OARDO_DEFAULTUSER} ${OARDO_DEFAULTGROUP} set_rights ${OARDIR}/oarres 6755 ${OARDO_DEFAULTUSER} ${OARDO_DEFAULTGROUP} set_rights ${CGIDIR}/oar-cgi 6754 ${OARDO_DEFAULTUSER} ${WWWUSER} } ./oar-2.5.2/setup/default/0000755000175000017500000000000011757171206013345 5ustar plbplb./oar-2.5.2/setup/default/oar-server.in0000644000175000017500000000070711757171206015766 0ustar plbplb# Defaults for oar-server initscript # sourced by %%INITDIR%%/oar-server # Uncomment the following lines if you don't want oar-server to be automatically # started at boot time #OAR_SERVER_NO_START_STOP=1 # start/stop options check if [ -n "$OAR_SERVER_NO_START_STOP" -a -z "$OAR_SERVER_FORCE_START_STOP" ]; then echo "oar-server disabled from %%DEFAULTDIR%%/oar-server" exit 0 fi # Additional options that are passed to the Daemon. DAEMON_OPTS="" ./oar-2.5.2/setup/default/oar-node.in0000644000175000017500000000156411757171206015407 0ustar plbplb# Fix maximum number of open files to 8192 for all jobs # # ulimit -n 8192 # If you want to change the default umask # # umask 0702 # umask 0002 # hostname used in the oar database to identify this node # # OAR_NODE_NAME=$(hostname -f) # OAR server hostname # # OARSERVER="myserver" # This function is called when oar-node service is started. # You can change this to use another method to switch the node into the Alive state # # start_oar_node() { # test -n "$OARSERVER" || exit 0 # su - oar -c "/usr/bin/ssh $OARSERVER oarnodesetting -s Alive -h $OAR_NODE_NAME" # } # This function is called when oar-node service is stopped. # You can change this to use another method to switch the node into the Absent state # # stop_oar_node() { # test -n "$OARSERVER" || exit 0 # su - oar -c "/usr/bin/ssh $OARSERVER oarnodesetting -s Absent -h $OAR_NODE_NAME" # } ./oar-2.5.2/setup/default/oar-node.exemple1.in0000644000175000017500000000200711757171206017117 0ustar plbplb# Fix maximum number of open files to 8192 for all jobs # # ulimit -n 8192 # If you want to change the default umask # # umask 0702 # umask 0002 # Another example of initialization method: # This method uses a specific ssh key binded on the server to a specific # script(see comments in the "oarnodesetting_ssh" script in OAR installation # directory) # # OARSERVER="oar-server" # MODSLEEP=8 # MINSLEEP=2 # MAXRETRY=30 # # start_oar_node() { # test -n "$OARSERVER" || exit 0 # echo " * Set the ressources of this node to Alive" # local retry=0 # local sleep=0 # until ssh -T -oStrictHostKeyChecking=no -oPasswordAuthentication=no -i %%OARHOMEDIR%%/.ssh/oarnodesetting_ssh.key oar@$OARSERVER # do # if [ $((retry+=sleep)) -gt $MAXRETRY ]; then # echo "Failed." # return 1 # fi # ((sleep = $RANDOM % $MODSLEEP + $MINSLEEP)) # echo "Retrying in $sleep seconds..." # sleep $sleep # done # return 0 # } # # stop_oar_node() { # : # } ./oar-2.5.2/setup/database.sh.in0000644000175000017500000000035711757171206014433 0ustar plbplb database_setup() { if ! oar-database --check >/dev/null 2>/dev/null; then echo "Currently, the oar database isn't setup automatically. Please install the database manually (using the ${SBINDIR}/oar-database script)." fi } ./oar-2.5.2/setup/cron.hourly/0000755000175000017500000000000011757171206014203 5ustar plbplb./oar-2.5.2/setup/cron.hourly/oar-desktop-computing-cgi.in0000644000175000017500000000011111757171206021517 0ustar plbplb#!/bin/bash # OAR stagein cache directory clean-up %%SBINDIR%%/oarcache ./oar-2.5.2/setup/cron.d/0000755000175000017500000000000011757171206013104 5ustar plbplb./oar-2.5.2/setup/cron.d/oar-server.in0000644000175000017500000000036111757171206015521 0ustar plbplb# # cron.d/oar-server -- schedules OAR accounting data mining # # $Id: oar-server.cron.d 1235 2008-03-18 10:03:12Z neyron $ # By default, run every hours 0 * * * * %%ROOTUSER%% [ -x %%SBINDIR%%/oaraccounting ] && %%SBINDIR%%/oaraccounting ./oar-2.5.2/setup/cron.d/oar-node.in0000644000175000017500000000047411757171206015145 0ustar plbplb# # cron.d/oar-node -- schedules periodic checks of the local node by running # every scripts of the /etc/oar/check.d directory, if any. # # $Id: oar-node.cron.d 1235 2008-03-18 10:03:12Z neyron $ # By default, run every hours 0 * * * * %%ROOTUSER%% [ -x %%OARDIR%%/oarnodecheckrun ] && %%OARDIR%%/oarnodecheckrun ./oar-2.5.2/setup/common.sh.in0000644000175000017500000001064311757171206014156 0ustar plbplbcreate_oar_group() { if ! getent group | grep -q "^${OAROWNERGROUP}:"; then echo -n "Adding group ${OAROWNERGROUP}.." case "$TARGET_DIST" in "debian") addgroup --quiet --system ${OAROWNERGROUP} 2>/dev/null || true ;; *) groupadd --system ${OAROWNERGROUP} 2>/dev/null >/dev/null || true ;; esac echo "..done" fi } create_oar_home() { test -d "${OARHOMEDIR}" || mkdir -p ${OARHOMEDIR} } create_oar_user() { # Create oar user if ! getent passwd | grep -q "^${OAROWNER}:"; then echo -n "Adding system user ${OAROWNER}.." case "$TARGET_DIST" in "debian") adduser --quiet \ --system \ --ingroup ${OAROWNERGROUP} \ --shell /bin/bash \ --no-create-home \ --disabled-password \ ${OAROWNER} 2>/dev/null || true ;; *) useradd --system \ --gid ${OAROWNERGROUP} \ --shell /bin/bash \ --password "*" \ ${OAROWNER} >/dev/null 2>&1 ;; esac echo "..done" fi # Adjust the password entry usermod -d ${OARHOMEDIR} \ -g ${OAROWNERGROUP} \ ${OAROWNER} # Adjust the file and directory permissions case "$SETUP_TYPE" in "deb") if ! dpkg-statoverride --list ${OARHOMEDIR} >/dev/null; then chown $OAROWNER:$OAROWNERGROUP $OARHOMEDIR chmod u=rwx,g=rxs,o= $OARHOMEDIR fi ;; *) chown $OAROWNER:$OAROWNERGROUP $OARHOMEDIR chmod u=rwx,g=rxs,o= $OARHOMEDIR ;; esac # set OAR shell if [ "`getent passwd ${OAROWNER} |cut -f7 -d:`" != "${OARDIR}/oarsh_shell" ]; then chsh -s ${OARDIR}/oarsh_shell ${OAROWNER} fi # Fix the bash profile cat > ${OARHOMEDIR}/.bash_oar <> ${OARHOMEDIR}/.bash_profile echo "[ -f ${OARHOMEDIR}/.bash_oar ] && . ${OARHOMEDIR}/.bash_oar" >> ${OARHOMEDIR}/.bash_profile fi if ! grep -q "${OARHOMEDIR}/.bash_oar" ${OARHOMEDIR}/.bashrc; then echo '' >> ${OARHOMEDIR}/.bashrc echo "[ -f ${OARHOMEDIR}/.bash_oar ] && . ${OARHOMEDIR}/.bash_oar" >> ${OARHOMEDIR}/.bashrc fi set_rights ${OARHOMEDIR} 0755 ${OARUSER} ${OAROWNERGROUP} chown ${OAROWNER}:${OAROWNERGROUP} ${OARHOMEDIR}/.bash_oar chown ${OAROWNER}:${OAROWNERGROUP} ${OARHOMEDIR}/.bash_profile chown ${OAROWNER}:${OAROWNERGROUP} ${OARHOMEDIR}/.bashrc } install_run_dir() { # Install run dir install -o ${OAROWNER} -m 755 -d ${RUNDIR}/oar } create_log_file() { # Log file touch ${LOGDIR}/oar.log && chown ${OAROWNER}:${ROOTGROUP} ${LOGDIR}/oar.log && chmod 0644 ${LOGDIR}/oar.log || true } common_setup() { # Update before 2.5.2: Fix a bug causing an empty ${OARHOMEDIR}/.bash_oar if [ -e ${OARHOMEDIR}/.bash_oar ] && [ -z "$(cat ${OARHOMEDIR}/.bash_oar)" ]; then rm -f ${OARHOMEDIR}/.bash_oar fi create_oar_group create_oar_home create_oar_user install_run_dir create_log_file mkdir -p ${OARCONFDIR}/ install_conffile \ ${EXAMPLEDIR}/oar.conf \ ${OARCONFDIR}/oar.conf \ 0600 ${OAROWNER}:${ROOTGROUP} install_conffile \ ${EXAMPLEDIR}/oarnodesetting_ssh \ ${OARCONFDIR}/oarnodesetting_ssh \ 0755 install_conffile \ ${EXAMPLEDIR}/update_cpuset_id.sh \ ${OARCONFDIR}/update_cpuset_id.sh \ 0755 set_rights ${OARDIR}/oarsh_oardo 6755 ${OARDO_DEFAULTUSER} ${OARDO_DEFAULTGROUP} set_rights ${SBINDIR}/oarnodesetting 6754 ${OARDO_DEFAULTUSER} ${OARDO_DEFAULTGROUP} set_rights ${OARDIR}/oardodo/oardodo 6754 ${ROOTUSER} ${OAROWNERGROUP} set_rights ${OARDIR}/oardodo 0755 ${ROOTUSER} ${OAROWNERGROUP} } ./oar-2.5.2/setup/api.sh.in0000644000175000017500000000351611757171206013440 0ustar plbplb api_setup() { mkdir -p ${OARCONFDIR}/apache2 || true # Check for an old configuration file if [ -f ${OARCONFDIR}/apache-api.conf ]; then mv ${OARCONFDIR}/apache-api.conf ${OARCONFDIR}/apache2/oar-restful-api.conf fi install_conffile \ ${EXAMPLEDIR}/apache2.conf \ ${OARCONFDIR}/apache2/oar-restful-api.conf \ 0600 ${WWWUSER} install_conffile \ ${EXAMPLEDIR}/api_html_header.pl \ ${OARCONFDIR}/api_html_header.pl \ 0600 ${OAROWNER} install_conffile \ ${EXAMPLEDIR}/api_html_postform.pl \ ${OARCONFDIR}/api_html_postform.pl \ 0644 ${OAROWNER} install_conffile \ ${EXAMPLEDIR}/api_html_postform_resources.pl \ ${OARCONFDIR}/api_html_postform_resources.pl \ 0644 ${OAROWNER} install_conffile \ ${EXAMPLEDIR}/api_html_postform_rule.pl \ ${OARCONFDIR}/api_html_postform_rule.pl \ 0644 ${OAROWNER} # install the apache2 configuration file if [ -d ${APACHECONFDIR}/conf.d ]; then # Check if an old configuration file already exists if [ -L ${APACHECONFDIR}/conf.d/oar-api.conf ]; then rm ${APACHECONFDIR}/conf.d/oar-api.conf elif [ -f ${APACHECONFDIR}/conf.d/oar-api.conf ]; then mv ${APACHECONFDIR}/conf.d/oar-api.conf ${APACHECONFDIR}/conf.d/oar-restful-api.conf; fi if [ ! -e ${APACHECONFDIR}/conf.d/oar-restful-api.conf ]; then ln -s ${OARCONFDIR}/apache2/oar-restful-api.conf ${APACHECONFDIR}/conf.d/oar-restful-api.conf fi fi set_rights ${CGIDIR}/oarapi 0750 ${OAROWNER} ${WWWUSER} set_rights ${CGIDIR}/oarapi/oarapi.cgi 6755 ${OARDO_DEFAULTUSER} ${OARDO_DEFAULTGROUP} set_rights ${CGIDIR}/oarapi/oarapi-debug.cgi 6755 ${OARDO_DEFAULTUSER} ${OARDO_DEFAULTGROUP} } ./oar-2.5.2/misc/0000755000175000017500000000000011757171206011514 5ustar plbplb./oar-2.5.2/misc/pkg_building/0000755000175000017500000000000011757171206014152 5ustar plbplb./oar-2.5.2/misc/pkg_building/git-build-package.sh0000755000175000017500000001311211757171206017760 0ustar plbplb#!/bin/bash # $Id: $ # build a package (debian or rpm) using a git-svn repository set -e usage() { cat < Build OAR package from the given branch (ex branch name: 'trunk-work', '2.2-test') Options: -s snapshot version (for generating a beta or not released) -u unsigned packages -h print this message and exit EOF exit 1 } check_branch() { if [ "`git branch |egrep \" $1\$\"`" = "" ]; then echo "Branch $1 not found!" echo "Maybe, you gave me the name of a remote branch?" echo "You should work into a local branch tracking the corresponding remote one." echo "You can create it this way:" echo " cd git" echo " git branch $1-work $1" echo " cd .." exit 1 fi } get_oar_version() { # Sets up the $OARVersion variable VER_CMD=`egrep -o -m 1 "OARVersion = \"(.*)\"" tools/oarversion.pm |sed "s/ //g"` eval $VER_CMD } get_snapshot_id() { SNAPSHOT_ID=`git log --abbrev-commit --pretty=oneline HEAD^..HEAD |sed 's/ /./'|cut -d. -f1` } remove_upstream_branch() { git branch -D upstream exit 1 } trap remove_upstream_branch QUIT ERR SNAPSHOT=n while getopts "shu" options; do case $options in s) SNAPSHOT=y ;; u) UNSIGNED="-us" ;; *) usage ;; esac done shift $(($OPTIND - 1)) PACKAGE_TYPE=$1 BRANCH_NAME=$2 if [ -z $BRANCH_NAME ] || [ -z $PACKAGE_TYPE ]; then usage fi if [ ! -r "git/.git" ]; then echo "No git repository found!" echo "You must have a git repository of OAR into the ./git directory" echo "You can init this local git repository with the following:" echo " # The following action maybe very long!" echo " git svn clone https://scm.gforge.inria.fr/svn/oar --trunk=trunk --branches=branches --tags=tags git" echo " cd git" echo " git branch trunk-work trunk" echo " git branch 2.3-work 2.3" echo " git checkout trunk-work" echo " cd .." exit 1 fi OARPWD=$(pwd) cd git if [ "`git status |grep 'working directory clean'`" = "" ]; then echo "You have uncommited local changes. check with 'git status'." exit 1 fi ##################### # DEBIAN PACKAGING ##################### if [ "$PACKAGE_TYPE" = "deb" ] then check_branch $BRANCH_NAME git branch upstream $BRANCH_NAME git checkout $BRANCH_NAME get_oar_version #REVISION=$(git-svn info Makefile| grep "^Revision:" | cut -d ' ' -f 2) if [ "$SNAPSHOT" = "y" ]; then git-dch --since=HEAD^ --snapshot --debian-branch=$BRANCH_NAME git add debian/changelog WHAT="snapshot" else #git-dch --since=HEAD^ --release --debian-branch=$BRANCH_NAME --new-version=$OARVersion git-dch --since=HEAD^ --release --debian-branch=$BRANCH_NAME git add debian/changelog WHAT="release" fi #OARVERSION=`egrep -o -m 1 "\((.*\))" debian/changelog|sed "s/[()]//g"` #if [ "$OARVERSION" != "" ]; then # perl -pi -e "s/OARVersion =.*$/OARVersion =\"$OARVERSION\";/" tools/oarversion.pm # git add tools/oarversion.pm #else # echo "Problem getting the generated version!" # exit 1 #fi git commit -m "New $WHAT automaticaly created by git-build-package.sh" git-buildpackage --git-debian-branch=$BRANCH_NAME --git-export-dir=../build-area/ -rfakeroot $UNSIGNED -uc git branch -D upstream echo echo "Your packages have been built into ./build-area/*$OARVERSION* !" ##################### # RPM PACKAGING ##################### elif [ "$PACKAGE_TYPE" = "rpm" ] then mkdir -p ../build-area check_branch $BRANCH_NAME git checkout $BRANCH_NAME get_oar_version if [ "$OARVersion" != "" ] ; then if [ "$SNAPSHOT" = "y" ]; then get_snapshot_id FullVersion="$OARVersion~git-$SNAPSHOT_ID" else FullVersion="$OARVersion" fi git archive --format=tar HEAD rpm |tar xvf - -C ../build-area mkdir -p ../build-area/rpm/BUILD ../build-area/rpm/RPMS ../build-area/rpm/SRPMS git archive --format=tar --prefix=oar-$OARVersion/ HEAD > "../build-area/rpm/SOURCES/oar-$OARVersion.tar" mkdir -p /tmp/oar-$OARVersion/tools tar xf ../build-area/rpm/SOURCES/oar-$OARVersion.tar -C /tmp oar-$OARVersion/tools/oarversion.pm perl -pi -e "s/OARVersion =.*$/OARVersion =\"$FullVersion\";/" /tmp/oar-$OARVersion/tools/oarversion.pm tar uf ../build-area/rpm/SOURCES/oar-$OARVersion.tar -C /tmp oar-$OARVersion/tools/oarversion.pm gzip ../build-area/rpm/SOURCES/oar-$OARVersion.tar rm -rf /tmp/oar-$OARVersion cd ../build-area/rpm if [ -f /etc/debian_version ]; then echo "WARNING: building under Debian, so removing dependencies!" perl -pi -e 's/(BuildRequires.*)/#$1/' SPECS/oar.spec fi perl -pi -e "s/^%define version .*/%define version $OARVersion/" SPECS/oar.spec if [ "$SNAPSHOT" = "y" ]; then perl -pi -e "s/^%define release (.*)/%define release \$1git$SNAPSHOT_ID/" SPECS/oar.spec fi rpmbuild -ba SPECS/oar.spec echo "RPM packages done into build-area/rpm/RPMS" else echo "Could not get the version from tools/oarversion.pm!" exit 1 fi ##################### # TARBALL ##################### elif [ "$PACKAGE_TYPE" = "tgz" ] then check_branch $BRANCH_NAME git checkout $BRANCH_NAME get_oar_version if [ "$OARVersion" != "" ] ; then if [ "$SNAPSHOT" = "y" ]; then get_snapshot_id OARVersion="$OARVersion~git-$SNAPSHOT_ID" fi git archive --format=tar --prefix=oar-$OARVersion/ HEAD | gzip >../oar-$OARVersion.tar.gz echo "oar-$OARVersion.tar.gz archive created" else echo "Could not get the version from tools/oarversion.pm!" exit 1 fi else echo "Unknown $1 package type!" exit 1 fi ./oar-2.5.2/misc/pkg_building/debian_keyring.txt0000644000175000017500000000044211757171206017665 0ustar plbplb# Here is how to make the keyring: gpg -a --output oar.asc --export 21E44652 gpg --no-default-keyring --keyring oar.gpg --import oar.asc rm oar.asc # oarmaster.gpg correspond to the OAR Archive Automatic Signing Key # Key fingerprint = 0F5A B5E3 9D21 FB89 344F D980 640C B53E D90D 0568 ./oar-2.5.2/misc/pkg_building/build-package.sh0000755000175000017500000001474411757171206017213 0ustar plbplb#!/bin/bash # build a package (debian) using a git repository set -e check_branch() { if [ "`git branch |egrep \" $1\$\"`" = "" ]; then echo "Branch $1 not found!" echo "Maybe, you gave me the name of a remote branch?" echo "You should work into a local branch tracking the corresponding remote one." echo "You can create it this way:" echo " cd " echo " git branch $1-work $1" echo " cd .." exit 1 fi } get_oar_version() { # Sets up the $OARVersion variable perl -e "require '$OAR_VERSION_FILE'; print $OAR_VERSION_FUNC" | sed -e "s/ .*//" } get_snapshot_version() { OARVERSION=$(perl -e "require '$OAR_VERSION_FILE'; print $OAR_VERSION_FUNC" | sed -e "s/ .*//") REVISION=$(git log --oneline $OARVERSION..$BRANCH_NAME -- | wc -l) SNAPSHOT_ID=`git log --abbrev-commit --pretty=oneline --max-count=1 $BRANCH_NAME |sed 's/ /./'|cut -d. -f1` if [ "$BRANCH_NAME" != "2.5" ] && [ "$BRANCH_NAME" != "2.4" ]; then PREFIX=${BRANCH_NAME//[^a-zA-Z0-9.]/} else PREFIX=dev fi echo "${OARVERSION}+${PREFIX}${REVISION}.${SNAPSHOT_ID}" } usage() { cat < [ | ] Build OAR tarball from the given branch (ex branch name: 'trunk-work', '2.2-test') Options: -s snapshot version (for generating a beta or not released) -r release version (for publishing) -m merge only (only for debian) -h print this message and exit -q quiet (only write relevant information for automation to stdout) EOF exit 1 } log_info() { if [ "$QUIET" != "yes" ]; then echo "$*" fi } gen_tarball() { ##################### # TARBALL ##################### TMPDIR=$(mktemp -d /tmp/oar-tarball.XXXXXX) git archive --format=tar --prefix=oar/ $BRANCH_NAME | tar x -C $TMPDIR SNAPSHOTVERSION=$(get_snapshot_version) OARVERSION=$(get_oar_version) cd $TMPDIR/oar if [ "$ACTION" = "snapshot" ]; then VERSION=$SNAPSHOTVERSION sed -e "s/$OARVERSION/$VERSION/" -i $TMPDIR/oar/$OAR_VERSION_FILE else VERSION=$OARVERSION fi if [ -z "$VERSION" ]; then echo "An error occuring when retrieving the oar version. Version is empty." exit 1 fi cd $OARPWD mv $TMPDIR/oar $TMPDIR/oar-$VERSION BUILD_AREA=$OARPWD/../build-area/$VERSION if [ -e "$BUILD_AREA" ]; then log_info "The build area $BUILD_AREA exist. Cleaning..." rm -rf $BUILD_AREA fi mkdir -p $BUILD_AREA TARBALL=$BUILD_AREA/oar-$VERSION.tar.gz tar czf $TARBALL -C $TMPDIR . [ -d "$TMPDIR" ] && rm -rf "$TMPDIR" echo "$BUILD_AREA/oar-$VERSION.tar.gz" } gen_deb() { current_branch=$(git status | head -n 1 | sed -e "s/.* //") if [ "$current_branch" != "$DEBIAN_BRANCH_NAME" ]; then echo "you're not on the branch '$DEBIAN_BRANCH_NAME'" echo "git chechout $DEBIAN_BRANCH_NAME" exit 1 fi git-import-orig $TARBALL dch -v $VERSION-0 "Snapshot version $VERSION" debcommit -a if [ "$ACTION" = "release" ] || [ "$ACTION" = "snapshot" ]; then mkdir -p $BUILD_AREA/debian/ git-buildpackage --git-export-dir=$BUILD_AREA/debian/ echo "Your packages have been built into $BUILD_AREA/debian" fi } gen_rpm() { mkdir -p $BUILD_AREA/rpm/{BUILD,RPMS,SRPMS} git archive --format=tar $RPM_BRANCH_NAME | tar xf - -C $BUILD_AREA ln -s $TARBALL $BUILD_AREA/rpm/SOURCES/ cd $BUILD_AREA/rpm if [ -f /etc/debian_version ]; then echo "WARNING: building under Debian, so removing dependencies!" perl -pi -e 's/(BuildRequires.*)/#$1/' SPECS/oar.spec fi perl -pi -e "s/^%define version .*/%define version $VERSION/" SPECS/oar.spec perl -pi -e "s/^%define release (.*)/%define release 0/" SPECS/oar.spec if [ "$ACTION" = "release" ] || [ "$ACTION" = "snapshot" ]; then rpmbuild -ba SPECS/oar.spec echo "RPM packages done into $BUILD_AREA/rpm/RPMS" fi } ACTION= QUIET=no while getopts "qrshm" options; do case $options in q) QUIET=yes ;; s) ACTION=snapshot ;; r) ACTION=release ;; m) ACTION=merge-only ;; *) usage ;; esac done shift $(($OPTIND - 1)) if [ -z "$ACTION" ]; then usage fi TARGET=$1 BRANCH_NAME=$2 PACKAGE_BRANCH_NAME=$3 if [ -z "$BRANCH_NAME" ]; then usage fi if [ -z "$PACKAGE_BRANCH_NAME" ]; then DEBIAN_BRANCH_NAME=debian/$BRANCH_NAME RPM_BRANCH_NAME=rpm/$BRANCH_NAME else if [ "$TARGET" = "all" ]; then echo "In case of 'all', you can't specify debian_branch_name and/or rpm_branch_name (FIXME)" exit 1 fi DEBIAN_BRANCH_NAME=$PACKAGE_BRANCH_NAME RPM_BRANCH_NAME=$PACKAGE_BRANCH_NAME fi if [ ! -d .git ]; then echo "No git repository found in the current directory" fi OARPWD=$(pwd) mkdir -p ../build-area if [ "`git status |grep 'working directory clean'`" = "" ]; then echo "You have uncommited local changes. check with 'git status'." exit 1 fi CURRENT_BRANCH=$(git status | head -n 1 | sed -e 's/.* //') git checkout $BRANCH_NAME >/dev/null 2>&1 if [ -f debian/control ]; then echo "You seem to use a debian branch as a source branch". exit 1; fi if [ -d rpm ]; then echo "You seem to use a rpm branch as a source branch". exit 1 fi if [ -e sources/core/common-libs/lib/OAR/Version.pm ]; then OAR_VERSION_FILE=sources/core/common-libs/lib/OAR/Version.pm OAR_VERSION_FUNC="print OAR::Version::get_version()" elif [ -e Tools/oarversion.pm ]; then OAR_VERSION_FILE=Tools/oarversion.pm OAR_VERSION_FUNC="print oarversion::get_version()" else echo "The branch $BRANCH_NAME seems to not be OAR" exit 1 fi case $TARGET in tgz) check_branch $BRANCH_NAME >/dev/null 2>&1 gen_tarball ;; deb) check_branch $BRANCH_NAME check_branch $DEBIAN_BRANCH_NAME gen_tarball git checkout $DEBIAN_BRANCH_NAME >/dev/null 2>&1 gen_deb ;; rpm) check_branch $BRANCH_NAME check_branch $RPM_BRANCH_NAME gen_tarball #git checkout $RPM_BRANCH_NAME gen_rpm ;; all) check_branch $BRANCH_NAME check_branch $DEBIAN_BRANCH_NAME check_branch $RPM_BRANCH_NAME gen_tarball git checkout $DEBIAN_BRANCH_NAME >/dev/null 2>&1 gen_deb #git checkout $RPM_BRANCH_NAME gen_rpm ;; *) usage ;; esac git checkout $CURRENT_BRANCH >/dev/null 2>&1 ./oar-2.5.2/misc/make_tarball0000755000175000017500000000616011757171206014063 0ustar plbplb#!/bin/bash # build a package (debian) using a git repository set -e usage() { N=$(basename $0) cat <] -r [-u ] [-t ] $N [-q] [-d ] [-f] Options: -d target directory -h print this message and exit -q quiet (only write relevant information for automation to stdout) -u specify the git uri to use -b specify the git treeish to use -r use a remote repository -f make the tarball even if there are uncommited changes (only for local repository) EOF exit 1 } log_info() { if [ "$QUIET" != "yes" ]; then echo "$*" >&2 fi } check_if_uncommited_changes() { if [ "$FORCE" != "yes" ] && [ -d ".git" ] && [ "$(git status |grep 'working directory clean')" = "" ]; then echo "You have uncommited local changes. check with 'git status'." echo "(or force the generation with '-f')" exit 1 fi } set_oar_version_file() { local dir version dir="$1" version="$2" if [ -e "$dir/sources/core/common-libs/lib/OAR/Version.pm" ]; then # 2.5.x OAR_VERSION_FILE="$dir/sources/core/common-libs/lib/OAR/Version.pm" elif [ -e "$dir/Tools/oarversion.pm" ]; then # 2.4.x OAR_VERSION_FILE="$dir/Tools/oarversion.pm" else echo "The branch $OARPWD seems to not be a OAR source folder" exit 1 fi sed -e "s/^my \$OARVersion =.*/my \$OARVersion = \"$version\";/" -i "$OAR_VERSION_FILE" } QUIET=no TARGET_DIRECTORY=../tarballs REMOTE=no FORCE=no GIT_URI=git://scm.gforge.inria.fr/oar/oar.git GIT_TREEISH=2.5 while getopts "hqrfu:b:d:" options; do case $options in q) QUIET=yes ;; r) REMOTE=yes ;; f) FORCE=yes ;; u) GIT_URI="$OPTARG";; b) GIT_TREEISH="$OPTARG" ;; d) TARGET_DIRECTORY="$OPTARG";; *) usage ;; esac done shift $(($OPTIND - 1)) if [ -z "$TARGET_DIRECTORY" ]; then log_info "you must provide a non-empty target directory" exit 1 fi cur_dir=$(pwd) if [ "$REMOTE" != "yes" ]; then git_dir=$(pwd) if [ ! -d "$git_dir/.git" ]; then echo "$git_dir is not a working git directory. Fail." exit 1; fi check_if_uncommited_changes log_info "Using the Git repository '$(pwd)'" fi TMPDIR=$(mktemp -d) if [ "$REMOTE" = "yes" ]; then git_dir="$TMPDIR/git" mkdir -p "$git_dir" log_info "Using the Git repository '$GIT_URI'" git clone -q $GIT_URI "$git_dir" fi cd "$git_dir" log_info "Using the Git tree-ish '$GIT_TREEISH'" last_tag=$(git describe $GIT_TREEISH --abbrev=0) version=$(git describe $GIT_TREEISH | sed -e "s/^$last_tag-\([[:digit:]]\+\)-/$last_tag+\1./") tarball_dir="$TMPDIR/tarball" tarball_prefix=oar-$version tarball_srcdir="$tarball_dir/$tarball_prefix" mkdir "$tarball_dir" git archive --format tar --prefix "$tarball_prefix/" "$GIT_TREEISH" | tar xf - -C "$tarball_dir" --exclude .gitignore set_oar_version_file "$tarball_srcdir" "$version" cd "$cur_dir" mkdir -p "$TARGET_DIRECTORY" tar czf "$TARGET_DIRECTORY/$tarball_prefix.tar.gz" -C "$tarball_dir" . echo "$TARGET_DIRECTORY/$tarball_prefix.tar.gz" [ -d "$TMPDIR" ] && rm -rf "$TMPDIR" ./oar-2.5.2/misc/make_live_cd_scripts/0000755000175000017500000000000011757171206015665 5ustar plbplb./oar-2.5.2/misc/make_live_cd_scripts/make-debian-live-cd.sh0000755000175000017500000001746211757171206021714 0ustar plbplb#!/bin/bash set -e DEFAULT_DISTRO_BASE="oar-live-cd_base.tgz" DOWNLOAD_URL="http://oar.imag.fr/live" usage() { cat < [] -c: compress the squashfs Example workflow: rm -rf build-area ./git-build-package.sh -s deb trunk sudo $0 ./build-area sudo kvm -m 512 -boot d -cdrom /var/tmp/OAR_Debian_Live-2.4.0-1\~3.gbp842e67.iso EOF exit 1 } REPOSITORY=$1 DISTRO_BASE=$2 COMPRESS=n while getopts "sh" options; do case $options in s) COMPRESS=y ; shift;; *) usage ;; esac done if [ -z $REPOSITORY ] then usage fi if [ "$USER" != "root" ] then echo "This script must be run as root!" exit 1 else echo " Warning: this script will use a chroot to set up a system image with all the oar packages inside. It means that it may reload some services of your host. It's not dangerous, but you may have to restart apache, ssh, or a oar server/node already running on your host. So, don't use this script from a production host! You have been warned! Also be aware that we're going to work into /var/tmp and that we need about 1.5 gigas. Do a CTRL-C or ENTER to continue... " read nothing fi if [ -z $DISTRO_BASE ] then if [ -s $DEFAULT_DISTRO_BASE ] then DISTRO_BASE=$DEFAULT_DISTRO_BASE else echo "Could not find the $DEFAULT_DISTRO_BASE into the current directory" echo -n "Would you like me to get it from the oar web server? " read input if [ "$input" = "o" -o "$input" = "O" -o "$input" = "y" -o "$input" = "Y" ] then echo "Downloading $DOWNLOAD_URL/$DEFAULT_DISTRO_BASE ..." wget -q $DOWNLOAD_URL/$DEFAULT_DISTRO_BASE DISTRO_BASE=$DEFAULT_DISTRO_BASE else echo "Ok, you can retry and specify another distro base as a second argument." exit 0 fi fi fi if [ "`which dpkg-scanpackages`" = "" ] then echo "This script needs dpkg-scanpackages!" exit 1 fi if [ -d $REPOSITORY ] then TMPDIR=`mktemp -p /var/tmp -d` if [ "`echo $TMPDIR|grep \"/var/tmp\"`" = "" ] then echo "Error creating tmp directory into /var/tmp!" exit 1 fi echo "**** EXTRACTING BASE $DISTRO_BASE... ****" tar zxf $DISTRO_BASE -C $TMPDIR DISTRO_DIR=$TMPDIR/oar-live-cd if [ -d $DISTRO_DIR/var/debs ] then echo echo "**** MAKING LOCAL REPOSITORY... ****" cp -a $REPOSITORY/* $DISTRO_DIR/var/debs cd $DISTRO_DIR/var dpkg-scanpackages debs /dev/null | gzip > debs/Packages.gz echo echo "**** INSTALLING OAR... ****" echo "deb file:/var debs/" > $DISTRO_DIR/etc/apt/sources.list chroot $DISTRO_DIR apt-get update -q chroot $DISTRO_DIR bash -c "DEBIAN_FRONTEND=noninteractive apt-get -q install -y --force-yes oar-server oar-user oar-node oar-doc oar-admin oar-web-status" chroot $DISTRO_DIR bash -c "DEBIAN_FRONTEND=noninteractive apt-get -q install -y --force-yes oar-api" || true echo "deb http://ftp.fr.debian.org/debian lenny main" >> $DISTRO_DIR/etc/apt/sources.list echo "deb http://security.debian.org/ lenny/updates main" >> $DISTRO_DIR/etc/apt/sources.list echo "deb http://oar.imag.fr/debian/unstable/2.4/ ./" >> $DISTRO_DIR/etc/apt/sources.list echo echo "**** CUSTOMIZING THE SYSTEM... ****" # If a mysql server is already running, it will fail, so we # must stop it. RC=0 /etc/init.d/mysql status > /dev/null || RC=$? if [ "$RC" = "0" ] then /etc/init.d/mysql stop MYSQL_STARTED=1 fi # Network config echo "127.0.0.1 localhost oar node1 node2" > $DISTRO_DIR/etc/hosts echo "oar" > $DISTRO_DIR/etc/hostname echo "auto lo" > $DISTRO_DIR/etc/network/interfaces echo "iface lo inet loopback" >> $DISTRO_DIR/etc/network/interfaces echo "auto eth0" >> $DISTRO_DIR/etc/network/interfaces echo "iface eth0 inet dhcp" >> $DISTRO_DIR/etc/network/interfaces echo "ServerName localhost" > $DISTRO_DIR/etc/apache2/conf.d/servername chroot $DISTRO_DIR bash -c "a2enmod ident" chroot $DISTRO_DIR bash -c "a2enmod headers" chroot $DISTRO_DIR bash -c "a2enmod rewrite" # Keyboard config cat > $DISTRO_DIR/etc/rc2.d/S99ask_keyboard < /dev/null echo \$keyboard > /etc/keymap echo -n 'Start an X server? ([y]/n): ' read i if [ "\$i" = "o" -o "\$i" = "O" -o "\$i" = "y" -o "\$i" = "Y" -o "\$i" = "" ] then echo "startx" > /home/baygon/.profile fi EOS chmod 755 $DISTRO_DIR/etc/rc2.d/S99ask_keyboard # Path for the ruby rest client echo "export PATH=/var/lib/gems/1.8/bin:\$PATH" >> $DISTRO_DIR/etc/profile # Creation of the "baygon" user automaticaly logged in on tty1 chroot $DISTRO_DIR bash -c "useradd -m baygon" perl -pi -e 's/getty.*tty1/mingetty --noclear --autologin baygon tty1/' $DISTRO_DIR/etc/inittab # Allow baygon to become root with "su" chroot $DISTRO_DIR bash -c "groupadd wheel" chroot $DISTRO_DIR bash -c "usermod -a -G wheel baygon" chroot $DISTRO_DIR bash -c "usermod -p\\\$1\\\$c0MqzZRB\\\$4DtoKo75Jy0fLm3jGlDTg0 baygon" echo "auth sufficient pam_wheel.so trust" > $DISTRO_DIR/etc/pam.d/su.new cat $DISTRO_DIR/etc/pam.d/su >> $DISTRO_DIR/etc/pam.d/su.new mv $DISTRO_DIR/etc/pam.d/su $DISTRO_DIR/etc/pam.d/su.orig mv -f $DISTRO_DIR/etc/pam.d/su.new $DISTRO_DIR/etc/pam.d/su # User baygon starts a windowmaker X env at login echo "setxkbmap \`cat /etc/keymap\`" >> $DISTRO_DIR/home/baygon/.xinitrc echo "wmaker" >> $DISTRO_DIR/home/baygon/.xinitrc # Home configuration for baygon user if [ -f $DISTRO_DIR/root/home-config.tgz ] then tar zxf $DISTRO_DIR/root/home-config.tgz -C $DISTRO_DIR/home/baygon chroot $DISTRO_DIR bash -c "chown -R baygon /home/baygon" rm -f $DISTRO_DIR/home/baygon/.mozilla/*/*/sessionstore.js fi # Xorg config #echo "xrandr --output default --mode 1024x768" > $DISTRO_DIR/home/baygon/.xinitrc # It makes X crash... weird :-( perl -pi -e 's/Section "Screen"/Section "Screen"\n\tSubsection "Display"\n\t\tModes "1024x768"\n\tEndsubsection/' $DISTRO_DIR/etc/X11/xorg.conf echo echo "**** INITIALISING OAR... ****" # OAR key configuration perl -pi -e 's/^/environment="OAR_KEY=1" /' $DISTRO_DIR/var/lib/oar/.ssh/authorized_keys # Database init chroot $DISTRO_DIR bash << EOF /etc/init.d/mysql start /etc/init.d/oar-server stop || true /etc/init.d/oar-node stop || true mysql < /root/init.sql mysql oar < /usr/lib/oar/mysql_structure.sql mysql oar < /usr/lib/oar/mysql_default_admission_rules.sql mysql oar < /usr/lib/oar/default_data.sql || true oarproperty -a core oarproperty -a cpu oarnodesetting -a -h node1 -p cpu=0 =p core=0 oarnodesetting -a -h node1 -p cpu=0 =p core=1 oarnodesetting -a -h node1 -p cpu=1 =p core=0 oarnodesetting -a -h node1 -p cpu=1 =p core=1 oarnodesetting -a -h node2 -p cpu=0 =p core=0 oarnodesetting -a -h node2 -p cpu=0 =p core=1 oarnodesetting -a -h node2 -p cpu=1 =p core=0 oarnodesetting -a -h node2 -p cpu=1 =p core=1 sleep 2 /etc/init.d/mysql stop EOF if [ "$MYSQL_STARTED" = "1" ] then /etc/init.d/mysql start fi echo echo "**** MAKING ISO IMAGE...****" rm -rf $DISTRO_DIR/tmp/* chroot $DISTRO_DIR /root/linux-live/build echo echo "**** MOVING ISO IMAGE...****" VERSION=`chroot $DISTRO_DIR dpkg -l oar-common|grep oar-common|awk '{print $3}'` mv -f $DISTRO_DIR/tmp/OARDebiantestimage.iso /var/tmp/OAR_Debian_Live-$VERSION.iso echo echo "**** CLEANING... ****" rm -rf /var/tmp/`basename $TMPDIR` echo echo "DONE! HAVE FUN WITH:" echo "**** /var/tmp/OAR_Debian_Live-$VERSION.iso ****" echo exit 0 else echo "$DISTRO_DIR/var/debs not found!" exit 1 fi else echo "$REPOSITORY repository directory not found!" exit 1 fi ./oar-2.5.2/misc/make_live_cd_scripts/make-centos-live-cd.sh0000755000175000017500000002764211757171206021766 0ustar plbplb#!/bin/bash set -e DEFAULT_DISTRO_BASE="centos-oar-base-image.tgz" DOWNLOAD_URL="http://oar.imag.fr/live" #KERNEL=ftp://ftp.slax.org/Linux-Live/kernels/2.6.27.7/linux-2.6.27.7-i486-1.tgz KERNEL=$DOWNLOAD_URL/linux-2.6.27.7-i486-1.tgz HOME_URL=$DOWNLOAD_URL/home-config.tgz function kill_handle { umount $DISTRO_DIR/proc } function err_handle { echo "An error occured. You may have to manually clean $DISTRO_DIR." echo "Exiting..." umount $DISTRO_DIR/proc } trap kill_handle SIGINT SIGTERM SIGQUIT trap err_handle ERR usage() { cat < [] -c: compress the squashfs Example workflow: rm -rf build-area ./git-build-package.sh -s rpm trunk-work sudo $0 ./build-area/rpm/RPMS sudo kvm -m 512 -boot d -cdrom /var/tmp/OAR_Centos_Live-2.4.0-1\~3.gbp842e67.iso EOF exit 1 } REPOSITORY=$1 DISTRO_BASE=$2 COMPRESS=n while getopts "sh" options; do case $options in s) COMPRESS=y ; shift;; *) usage ;; esac done if [ -z $REPOSITORY ] then usage fi if [ "$USER" != "root" ] then echo "This script must be run as root!" exit 1 else echo " Warning: this script will use a chroot to set up a system image with all the oar packages inside. It means that it may reload some services of your host. It's not dangerous, but you may have to restart apache, ssh, or a oar server/node already running on your host. So, don't use this script from a production host! You have been warned! Also be aware that we're going to work into /var/tmp and that we need about 1.5 gigas. Do a CTRL-C or ENTER to continue... " read nothing fi if [ -z $DISTRO_BASE ] then if [ -s $DEFAULT_DISTRO_BASE ] then DISTRO_BASE=$DEFAULT_DISTRO_BASE else echo "Could not find the $DEFAULT_DISTRO_BASE into the current directory" echo -n "Would you like me to get it from the oar web server? " read input if [ "$input" = "o" -o "$input" = "O" -o "$input" = "y" -o "$input" = "Y" ] then echo "Downloading $DOWNLOAD_URL/$DEFAULT_DISTRO_BASE ..." wget -q $DOWNLOAD_URL/$DEFAULT_DISTRO_BASE DISTRO_BASE=$DEFAULT_DISTRO_BASE else echo "Ok, you can retry and specify another distro base as a second argument." exit 0 fi fi fi if [ "`which createrepo`" = "" ] then echo "This script needs createrepo!" exit 1 fi if [ -d $REPOSITORY ] then TMPDIR=`mktemp -p /var/tmp -d` if [ "`echo $TMPDIR|grep \"/var/tmp\"`" = "" ] then echo "Error creating tmp directory into /var/tmp!" exit 1 fi echo "**** EXTRACTING BASE $DISTRO_BASE... ****" tar zxf $DISTRO_BASE -C $TMPDIR DISTRO_DIR=$TMPDIR/centos-oar-appliance-32 if [ -e $DISTRO_DIR/etc/redhat-release ] then echo echo "**** MAKING LOCAL REPOSITORY... ****" wget -q -c $DOWNLOAD_URL/windowmaker-0.91.0-1.2.el4.rf.i386.rpm wget -q -c $DOWNLOAD_URL/webcore-fonts-3.0-1.noarch.rpm wget -q -c $DOWNLOAD_URL/ruby-DBI-0.2.0-1.i386.rpm wget -q -c $DOWNLOAD_URL/ruby-GD-0.7.4-1.i386.rpm cp -a $REPOSITORY/* $DISTRO_DIR/var/rpms cp -a windowmaker-*rpm $DISTRO_DIR/var/rpms cp -a webcore-fonts-*rpm $DISTRO_DIR/var/rpms cp -a ruby-DBI-*rpm $DISTRO_DIR/var/rpms cp -a ruby-GD-*rpm $DISTRO_DIR/var/rpms cd $DISTRO_DIR/var/rpms createrepo . cd ../../.. echo echo "**** Mounting /proc, creating devices...****" chroot $DISTRO_DIR bash -c "mount -t proc none /proc" chroot $DISTRO_DIR bash -c "MAKEDEV /dev" cp /etc/resolv.conf $DISTRO_DIR/etc/ echo echo "**** Configuring yum...****" # Configuring yum for oar local repository echo "[oar-local-repo] name=OAR-LOCAL-REPO baseurl=file:///var/rpms gpgcheck=0 enabled=1" > $DISTRO_DIR/etc/yum.repos.d/oar.repo # Configuring yum for extra packages from rpmforge echo "[rpmforge] name = Red Hat Enterprise $releasever - RPMforge.net - dag #baseurl = http://apt.sw.be/redhat/el5/en/$basearch/dag mirrorlist = http://apt.sw.be/redhat/el5/en/mirrors-rpmforge #mirrorlist = file:///etc/yum.repos.d/mirrors-rpmforge enabled = 1 protect = 0 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-rpmforge-dag gpgcheck = 0" > $DISTRO_DIR/etc/yum.repos.d/rpmforge.repo echo echo "**** Downloading kernel...****" wget -q -c $KERNEL echo echo "**** Installing kernel...****" #chroot $DISTRO_DIR bash -c "yum install -q -y kernel" tar zxf `basename $KERNEL` -C $DISTRO_DIR echo echo "**** Installing ssh...****" chroot $DISTRO_DIR bash -c "yum install -q -y openssh-server" chroot $DISTRO_DIR bash -c "/etc/init.d/sshd start; /etc/init.d/sshd stop >/dev/null 2>&1" || true echo "**** Installing libs required by linux-live... ****" echo chroot $DISTRO_DIR bash -c "yum install -q -y glibc.i686 zlib.i386 libstdc++.i386 mkisofs" echo echo "**** INSTALLING dependencies... ****" chroot $DISTRO_DIR bash -c "yum install -q -y perl-DBI.i386" chroot $DISTRO_DIR bash -c "yum install -q -y kbd" chroot $DISTRO_DIR bash -c "yum install -q -y httpd" chroot $DISTRO_DIR bash -c "chkconfig httpd on" chroot $DISTRO_DIR bash -c "yum install -q -y mysql-server.i386 perl-DBD-MySQL.i386 --exclude=perl-DBD-mysql" chroot $DISTRO_DIR bash -c "chkconfig mysqld on" chroot $DISTRO_DIR bash -c "yum install -q -y xorg-x11-server-Xorg xorg-x11-xinit xterm" chroot $DISTRO_DIR bash -c "ln -s /usr/bin/xterm /usr/bin/x-terminal-emulator" chroot $DISTRO_DIR bash -c "yum install -q -y windowmaker firefox" chroot $DISTRO_DIR bash -c "yum install -q -y webcore-fonts" #chroot $DISTRO_DIR bash -c "yum install -q -y selinux-policy" chroot $DISTRO_DIR bash -c "yum install -q -y ruby" chroot $DISTRO_DIR bash -c "yum install -q -y lsb" chroot $DISTRO_DIR bash -c "yum install -q -y perl-URI" chroot $DISTRO_DIR bash -c "yum install -q -y oidentd" # I don't know why ruby-GD i386 is not seen by yum :-( chroot $DISTRO_DIR bash -c "rpm -U /var/rpms/ruby-GD-*i386*" chroot $DISTRO_DIR bash -c "rpm -U /var/rpms/ruby-DBI-*i386* --nodeps" perl -pi -e "s/SELINUX=.*/SELINUX=disabled/" $DISTRO_DIR/etc/selinux/config echo echo "**** INSTALLING OAR... ****" chroot $DISTRO_DIR bash -c "yum install -q -y oar-server oar-user oar-node oar-doc oar-admin oar-web-status" chroot $DISTRO_DIR bash -c "yum install -q -y oar-api" || true echo echo "**** CUSTOMIZING THE SYSTEM... ****" # Configuring yum for OAR online repository echo "[oar] name=OAR baseurl=http://oar.imag.fr/RPMS/unstable/2.4 gpgcheck=0 enabled=1" >> $DISTRO_DIR/etc/yum.repos.d/oar.repo # If a mysql server is already running, it will fail, so we # must stop it. We suppose here that we are under Debian... RC=0 /etc/init.d/mysql status > /dev/null || RC=$? if [ "$RC" = "0" ] then /etc/init.d/mysql stop MYSQL_STARTED=1 fi # Network config echo "127.0.0.1 localhost oar node1 node2" > $DISTRO_DIR/etc/hosts echo "oar" > $DISTRO_DIR/etc/hostname echo "DEVICE=eth0" > $DISTRO_DIR/etc/sysconfig/network-scripts/ifcfg-eth0 echo "ONBOOT=yes" >> $DISTRO_DIR/etc/sysconfig/network-scripts/ifcfg-eth0 echo "BOOTPROTO=dhcp" >> $DISTRO_DIR/etc/sysconfig/network-scripts/ifcfg-eth0 echo "NETWORKING=yes" > $DISTRO_DIR/etc/sysconfig/network echo "HOSTNAME=oar" >> $DISTRO_DIR/etc/sysconfig/network # Apache Config echo "ServerName localhost" > $DISTRO_DIR/etc/httpd/conf.d/servername echo "LoadModule ident_module /etc/httpd/modules/mod_ident.so" > $DISTRO_DIR/etc/httpd/conf.d/ident.conf echo "LoadModule headers_module /etc/httpd/modules/mod_headers.so" > $DISTRO_DIR/etc/httpd/conf.d/headers.conf echo "LoadModule rewrite_module /etc/httpd/modules/mod_rewrite.so" > $DISTRO_DIR/etc/httpd/conf.d/rewrite.conf # Keyboard config cat > $DISTRO_DIR/etc/rc3.d/S99ask_keyboard < /dev/null echo \$keyboard > /etc/keymap echo -n 'Start an X server? ([y]/n): ' read i if [ "\$i" = "o" -o "\$i" = "O" -o "\$i" = "y" -o "\$i" = "Y" -o "\$i" = "" ] then echo "startx" >> /home/baygon/.bash_profile fi EOS chmod 755 $DISTRO_DIR/etc/rc3.d/S99ask_keyboard # Path for the ruby rest client echo "export PATH=/var/lib/gems/1.8/bin:\$PATH" >> $DISTRO_DIR/etc/profile # Creation of the "baygon" user automaticaly logged in on tty1 chroot $DISTRO_DIR bash -c "useradd -m baygon" perl -pi -e 's/mingetty.*tty1/mingetty --noclear --autologin baygon tty1/' $DISTRO_DIR/etc/inittab # Allow baygon to become root with "su" chroot $DISTRO_DIR bash -c "groupadd wheel" || true chroot $DISTRO_DIR bash -c "usermod -a -G wheel baygon" chroot $DISTRO_DIR bash -c "usermod -p\\\$1\\\$c0MqzZRB\\\$4DtoKo75Jy0fLm3jGlDTg0 baygon" echo "auth sufficient pam_wheel.so trust" > $DISTRO_DIR/etc/pam.d/su.new cat $DISTRO_DIR/etc/pam.d/su >> $DISTRO_DIR/etc/pam.d/su.new mv $DISTRO_DIR/etc/pam.d/su $DISTRO_DIR/etc/pam.d/su.orig mv -f $DISTRO_DIR/etc/pam.d/su.new $DISTRO_DIR/etc/pam.d/su # User baygon starts a windowmaker X env at login echo "setxkbmap \`cat /etc/keymap\`" >> $DISTRO_DIR/home/baygon/.xinitrc echo "wmaker" >> $DISTRO_DIR/home/baygon/.xinitrc # Home configuration for baygon user wget -q -c $HOME_URL tar zxf `basename $HOME_URL` -C $DISTRO_DIR/home/baygon chroot $DISTRO_DIR bash -c "chown -R baygon /home/baygon" rm -f $DISTRO_DIR/home/baygon/.mozilla/*/*/sessionstore.js # Xorg config #echo "xrandr --output default --mode 1024x768" > $DISTRO_DIR/home/baygon/.xinitrc # It makes X crash... weird :-( perl -pi -e 's/Section "Screen"/Section "Screen"\n\tSubsection "Display"\n\t\tModes "1024x768"\n\tEndsubsection/' $DISTRO_DIR/etc/X11/xorg.conf echo echo "**** INITIALISING OAR... ****" # OAR key configuration perl -pi -e 's/^/environment="OAR_KEY=1" /' $DISTRO_DIR/var/lib/oar/.ssh/authorized_keys # Database init chroot $DISTRO_DIR bash << EOF /etc/init.d/mysqld start /etc/init.d/oar-server stop || true /etc/init.d/oar-node stop || true mysql < /root/init.sql mysql oar < /usr/lib/oar/mysql_structure.sql mysql oar < /usr/lib/oar/mysql_default_admission_rules.sql mysql oar < /usr/lib/oar/default_data.sql || true oarproperty -a core oarproperty -a cpu oarnodesetting -a -h node1 -p cpu=0 =p core=0 oarnodesetting -a -h node1 -p cpu=0 =p core=1 oarnodesetting -a -h node1 -p cpu=1 =p core=0 oarnodesetting -a -h node1 -p cpu=1 =p core=1 oarnodesetting -a -h node2 -p cpu=0 =p core=0 oarnodesetting -a -h node2 -p cpu=0 =p core=1 oarnodesetting -a -h node2 -p cpu=1 =p core=0 oarnodesetting -a -h node2 -p cpu=1 =p core=1 sleep 2 /etc/init.d/mysqld stop EOF if [ "$MYSQL_STARTED" = "1" ] then /etc/init.d/mysql start fi echo echo "**** MAKING ISO IMAGE...****" chroot $DISTRO_DIR yum clean all umount $DISTRO_DIR/proc rm -rf $DISTRO_DIR/tmp/* #chroot $DISTRO_DIR bash -c "cd /boot && ln -s vmlinuz-* ./vmlinuz" #chroot $DISTRO_DIR bash -c "cd /boot && ln -s initrd-* ./initrd" #K_VERSION=`chroot $DISTRO_DIR bash -c "rpm -q kernel --qf '%{VERSION}-%{RELEASE}'"` K_VERSION=`/bin/ls $DISTRO_DIR/lib/modules|sort -n|tail -1` perl -pi -e "s/^KERNEL=.*/KERNEL=$K_VERSION/" $DISTRO_DIR/root/linux-live/.config chroot $DISTRO_DIR depmod -a $K_VERSION chroot $DISTRO_DIR /root/linux-live/build echo echo "**** MOVING ISO IMAGE...****" VERSION=`chroot $DISTRO_DIR rpm -q oar-common|sed "s/^oar-common-//"` mv -f $DISTRO_DIR/tmp/OARDebiantestimage.iso /var/tmp/OAR_Centos_Live-$VERSION.iso echo echo "**** CLEANING... ****" rm -rf /var/tmp/`basename $TMPDIR` echo echo "DONE! HAVE FUN WITH:" echo "**** /var/tmp/OAR_Centos_Live-$VERSION.iso ****" echo exit 0 else echo "$DISTRO_DIR/etc/redhat-release not found!" exit 1 fi else echo "$REPOSITORY repository directory not found!" exit 1 fi ./oar-2.5.2/misc/make_live_cd_scripts/README0000644000175000017500000000020711757171206016544 0ustar plbplbObsolete stuff. make_live_cd_scripts are historical base of kameleon and are kept here for memory. To remove when kameleon is stable. ./oar-2.5.2/misc/make_deb_source0000755000175000017500000001337411757171206014561 0ustar plbplb#!/bin/bash # # OAR # # build a debian source package from the git repository # # set -e QUIET=no TARGET_DIRECTORY=../build-area REMOTE=no FORCE=no GIT_URI=git://scm.gforge.inria.fr/oar/oar.git GIT_TREEISH=debian/2.5 GIT_UPSTREAM_TREEISH=debian-upstream/2.5 UPSTREAM_TARBALL= usage() { N=$(basename $0) cat <] [-m ] [-b ] [-t ] -r [-u ] $N [-q] [-d ] [-m ] [-b ] [-t ] [-f] Options: -d target directory (by default '$TARGET_DIRECTORY') -h print this message and exit -q quiet (only write relevant information for automation to stdout) -u specify the git uri to use (by default '$GIT_URI') -b specify the git treeish of the debian branch (by default '$GIT_TREEISH') -t specify the git treeish of the debian upstream branch (by default '$GIT_UPSTREAM_TREEISH') -r use a remote repository -f make the tarball even if there are uncommited changes (only for local repository) -m merge the given upstream tarball into to debian package (this can fail, if quilt fails) EOF exit 1 } log_info() { if [ "$QUIET" != "yes" ]; then echo -e "$*" >&2 fi } log_exit() { ret=$1 shift log_info $* [ -d "$TMPDIR" ] && rm -rf "$TMPDIR" exit $ret } check_if_uncommited_changes() { if [ "$FORCE" != "yes" ] && [ -d ".git" ] && [ "$(git status |grep 'working directory clean')" = "" ]; then echo "You have uncommited local changes. check with 'git status'." echo "(or force the generation with '-f')" exit 1 fi } while getopts "hqrfu:b:t:m:d:" options; do case $options in q) QUIET=yes ;; r) REMOTE=yes ;; f) FORCE=yes ;; u) GIT_URI="$OPTARG";; b) GIT_TREEISH="$OPTARG" ;; t) GIT_UPSTREAM_TREEISH="$OPTARG";; m) UPSTREAM_TARBALL="$OPTARG" ;; d) TARGET_DIRECTORY="$OPTARG";; *) usage ;; esac done shift $(($OPTIND - 1)) [ -z "$TARGET_DIRECTORY" ] && log_exit 1 "you must provide an existing target directory" target_directory=$(readlink -e "$TARGET_DIRECTORY") if [ -n "$UPSTREAM_TARBALL" ]; then UPSTREAM_TARBALL=$(readlink -e "$UPSTREAM_TARBALL") [ ! -f "$UPSTREAM_TARBALL" ] && log_exit 1 "The file $UPSTREAM_TARBALL doesn't exist" fi cur_dir=$(pwd) if [ "$REMOTE" != "yes" ]; then git_dir=$(pwd) if [ ! -d "$git_dir/.git" ]; then echo "$git_dir is not a working git directory. Fail." exit 1; fi check_if_uncommited_changes log_info "Using the Git repository '$(pwd)'" fi TMPDIR=$(mktemp -d) if [ "$REMOTE" = "yes" ]; then git_dir="$TMPDIR/git" mkdir -p "$git_dir" log_info "Using the Git repository '$GIT_URI'" git clone -q $GIT_URI "$git_dir" fi cd "$git_dir" log_info "Using the Git tree-ish '$GIT_TREEISH'" last_tag=$(git describe $GIT_TREEISH --match "debian/*" --abbrev=0) last_tag_hash=$(git log --oneline -n 1 "$last_tag" | cut -d' ' -f 1) git_treeish_hash=$(git log --oneline -n 1 "$GIT_TREEISH" | cut -d' ' -f 1) is_snapshot= if [ "$last_tag_hash" = "$git_treeish_hash" ] && [ -n "$(git tag -l $GIT_TREEISH)" ]; then is_snapshot=no log_info "Generating a released package" else is_snapshot=yes log_info "Generating a snapshot package" fi upstream_version_tag=$(git describe $GIT_TREEISH --abbrev=0 --match="$GIT_UPSTREAM_TREEISH*") upstream_version=$(echo "$upstream_version_tag" | sed -e "s%.*/%%") if [ "$is_snapshot" = "yes" ]; then debian_revision_tag=$(git describe $GIT_TREEISH --abbrev=0 --match="debian/$upstream_version*" 2>/dev/null || true) if [ -n "$debian_revision_tag" ]; then debian_revision=$(echo "$debian_revision_tag" | sed -e "s%.*/.*-%%") else debian_revision="0" fi debian_revision_number=$(git log --oneline --ancestry-path --no-merges $upstream_version_tag..$GIT_TREEISH | wc -l) debian_revision_hash=$(git log --oneline -n 1 $GIT_TREEISH | cut -d' ' -f 1) debian_revision="$debian_revision+$debian_revision_number.g$debian_revision_hash" else debian_revision=$(echo "$GIT_TREEISH" | sed -e "s/.*-//") fi version="$upstream_version-$debian_revision" deb_dir="$TMPDIR/deb" deb_prefix=oar-$version deb_srcdir="$deb_dir/$deb_prefix" pristine_prefix=oar_$upstream_version upstream_tarball=$deb_dir/$pristine_prefix.orig.tar.gz mkdir "$deb_dir" git archive --format tar --prefix "$deb_prefix/" "$GIT_TREEISH" | tar xf - -C "$deb_dir" pristine-tar checkout $upstream_tarball cd "$deb_srcdir" log_info "Generating the source package" if [ "$is_snapshot" = "yes" ]; then dch -v "$version" "" -b >/dev/null 2>/dev/null dch -r "" >/dev/null 2>/dev/null fi dpkg-buildpackage -S -us -uc > /dev/null log_info "Generating the snapshot package with the merged upstream" if [ -n "$UPSTREAM_TARBALL" ]; then upstream_tarball=$UPSTREAM_TARBALL upstream_version=$(echo "$upstream_tarball" | sed -e "s#.*/oar[_-]\(.*\)\(\.orig\)\?\.tar\.gz#\1#") version="$upstream_version-$debian_revision" uupdate $upstream_tarball -u -v $upstream_version > /dev/null cd $deb_dir/oar-$upstream_version dch -v "$version" "" >/dev/null 2>/dev/null dch -r "" >/dev/null 2>/dev/null if ! dpkg-buildpackage -S -us -uc >/dev/null; then log_exit 1 "Unable to generate the new source. Fail" fi fi deb_dscfile="$deb_dir/oar_$version.dsc" deb_changefile="$deb_dir/oar_${version}_source.changes" deb_tarball="$deb_dir/oar_${version}.debian.tar.gz" mkdir -p $target_directory/oar_$version cp $upstream_tarball \ $deb_dscfile \ $deb_changefile \ $deb_tarball \ $target_directory/oar_$version cd $cur_dir echo "$target_directory/oar_$version" log_exit 0 "\n### SUCCESS\n\nThe debian source package are in '$TARGET_DIRECTORY/oar_$version'" ./oar-2.5.2/misc/lustre_g5kutils/0000755000175000017500000000000011757171206014661 5ustar plbplb./oar-2.5.2/misc/lustre_g5kutils/start_lustre.bash0000755000175000017500000000075011757171206020260 0ustar plbplb#!/bin/bash set -e kadeploy3 -f $OAR_FILE_NODES -e debian-lustre -k MDS=`cat $OAR_FILE_NODES|sort -u|head -1` cat $OAR_FILE_NODES |sort -u|tail -n +2|head -n -1 > OSS_FILE CLIENT=`cat $OAR_FILE_NODES|sort -u|tail -1` ssh root@$MDS lustre_mds_init sleep 3 taktuk -s -c "ssh -l root" -f OSS_FILE b e [ lustre_oss_init $MDS ] ssh root@$CLIENT "lustre_client_init $MDS" sleep 3 ssh root@$CLIENT "iozone -l 1 -u 3 -R -r 1024k -s 10m -F /mnt/lustre/test1 /mnt/lustre/test2 /mnt/lustre/test3" ./oar-2.5.2/misc/lustre_g5kutils/lustre_oss_init.bash0000644000175000017500000000053011757171206020743 0ustar plbplb#!/bin/bash set -e if [ "$1" = "" ] then echo "usage: $0 " exit 1 fi TMP_PART=`df /tmp|tail -1|awk '{print $1}'` umount $TMP_PART pvcreate $TMP_PART vgcreate vg00 $TMP_PART lvcreate --name vg00/ost --size 10G mkfs.lustre --fsname lustre --ost --mgsnode=$1@tcp0 /dev/vg00/ost mkdir /mnt/ost mount -t lustre /dev/vg00/ost /mnt/ost ./oar-2.5.2/misc/lustre_g5kutils/lustre_mds_init.bash0000644000175000017500000000040311757171206020721 0ustar plbplb#!/bin/bash set -e TMP_PART=`df /tmp|tail -1|awk '{print $1}'` umount $TMP_PART pvcreate $TMP_PART vgcreate vg00 $TMP_PART lvcreate --name vg00/mdt --size 1G mkfs.lustre --fsname lustre --mdt --mgs /dev/vg00/mdt mkdir /mdt mount -t lustre /dev/vg00/mdt /mdt ./oar-2.5.2/misc/lustre_g5kutils/lustre_client_init.bash0000644000175000017500000000024611757171206021421 0ustar plbplb#!/bin/bash set -e if [ "$1" = "" ] then echo "usage: $0 " exit 1 fi modprobe lustre mkdir -p /mnt/lustre mount -t lustre $1@tcp0:/lustre /mnt/lustre ./oar-2.5.2/misc/lustre_g5kutils/debian/0000755000175000017500000000000011757171206016103 5ustar plbplb./oar-2.5.2/misc/lustre_g5kutils/debian/rules0000755000175000017500000000536611757171206017175 0ustar plbplb#!/usr/bin/make -f # -*- makefile -*- # Sample debian/rules that uses debhelper. # # This file was originally written by Joey Hess and Craig Small. # As a special exception, when this file is copied by dh-make into a # dh-make output file, you may use that output file without restriction. # This special exception was added by Craig Small in version 0.37 of dh-make. # # Modified to make a template file for a multi-binary package with separated # build-arch and build-indep targets by Bill Allombert 2001 # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 # This has to be exported to make some magic below work. export DH_OPTIONS configure: configure-stamp configure-stamp: dh_testdir # Add here commands to configure the package. touch configure-stamp #Architecture build: build-arch build-indep build-arch: build-arch-stamp build-arch-stamp: configure-stamp # Add here commands to compile the arch part of the package. #$(MAKE) touch $@ build-indep: build-indep-stamp build-indep-stamp: configure-stamp # Add here commands to compile the indep part of the package. #$(MAKE) doc touch $@ clean: dh_testdir dh_testroot rm -f build-arch-stamp build-indep-stamp configure-stamp # Add here commands to clean up after the build process. # $(MAKE) clean DESTDIR=$(CURDIR)/debian/oargrid dh_clean install: install-indep install-arch install-indep: dh_testdir dh_testroot dh_clean -k -i dh_installdirs -i # Add here commands to install the indep part of the package into # debian/-doc. $(MAKE) DESTDIR=$(CURDIR)/debian/lustre-g5kutils PREFIX=usr MANDIR=usr/share/man install dh_install -i install-arch: dh_testdir dh_testroot dh_clean -k -s dh_installdirs -s # Add here commands to install the arch part of the package into # debian/tmp. dh_install -s # Must not depend on anything. This is to be called by # binary-arch/binary-indep # in another 'make' thread. binary-common: dh_testdir dh_testroot dh_installchangelogs dh_installdocs dh_installexamples # dh_installmenu dh_installdebconf # dh_installlogrotate # dh_installemacsen # dh_installpam # dh_installmime # dh_python # dh_installinit # dh_installcron # dh_installinfo dh_installman dh_link dh_strip dh_compress dh_fixperms # dh_perl dh_makeshlibs dh_installdeb dh_shlibdeps dh_gencontrol dh_md5sums dh_builddeb # Build architecture independant packages using the common target. binary-indep: build-indep install-indep $(MAKE) -f debian/rules DH_OPTIONS=-i binary-common # Build architecture dependant packages using the common target. binary-arch: build-arch install-arch # $(MAKE) -f debian/rules DH_OPTIONS=-s binary-common binary: binary-indep .PHONY: build clean binary-indep binary-arch binary install install-indep install-arch configure ./oar-2.5.2/misc/lustre_g5kutils/debian/docs0000644000175000017500000000000011757171206016744 0ustar plbplb./oar-2.5.2/misc/lustre_g5kutils/debian/dirs0000644000175000017500000000001011757171206016756 0ustar plbplbusr/bin ./oar-2.5.2/misc/lustre_g5kutils/debian/copyright0000644000175000017500000000121611757171206020036 0ustar plbplbThis package was debianized by Bruno Bzeznik on Thu, 21 Oct 2010 16:06:54 +0200 It contains only scripts Upstream Authors: Bruno Bzeznik < bruno.bzeznik@imag.fr > Copyright: Copyright LIG (http://www.liglab.fr/) 2010 Licence: These scripts are 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, or (at your option) any later version. The Debian packaging is (C) 2010, Bruno Bzeznik and is licensed under the GPL, see `/usr/share/common-licenses/GPL'. ./oar-2.5.2/misc/lustre_g5kutils/debian/control0000644000175000017500000000110011757171206017476 0ustar plbplbSource: lustre-g5kutils Section: admin Priority: extra Maintainer: Bruno Bzeznik Build-Depends: debhelper (>= 7) Standards-Version: 3.7.3 Homepage: http://oar.imag.fr/ Package: lustre-g5kutils Architecture: all Depends: ${shlibs:Depends}, ${misc:Depends}, lustre-utils Description: Lustre tools for g5k This package contains scripts to make a grid5000 experiment involving Lustre clients and servers. - lustre_mds_init to initialize a metadata server - lustre_oss_init to initilize a storage resource - lustre_client_init to initilize a client ./oar-2.5.2/misc/lustre_g5kutils/debian/compat0000644000175000017500000000000211757171206017301 0ustar plbplb7 ./oar-2.5.2/misc/lustre_g5kutils/debian/changelog0000644000175000017500000000044411757171206017757 0ustar plbplblustre-g5kutils (1.0-2) unstable; urgency=low * Scripts are working -- Bruno Bzeznik Thu, 21 Oct 2010 18:01:40 +0200 lustre-g5kutils (1.0-1) unstable; urgency=low * Initial release -- Bruno Bzeznik Thu, 21 Oct 2010 16:06:54 +0200 ./oar-2.5.2/misc/lustre_g5kutils/debian/README.Debian0000644000175000017500000000030411757171206020141 0ustar plbplbSimple package to install lustre scripts for g5k experiment ----------------------------------------------------------- -- Bruno Bzeznik Thu, 21 Oct 2010 16:06:54 +0200 ./oar-2.5.2/misc/lustre_g5kutils/Makefile0000644000175000017500000000167111757171206016326 0ustar plbplb#!/usr/bin/make SHELL=/bin/bash DESTDIR= PREFIX=usr/local MANDIR=$(PREFIX)/man BINDIR=$(PREFIX)/bin SBINDIR=$(PREFIX)/sbin install: mkdir -p $(DESTDIR)/$(SBINDIR) install -m 755 lustre_mds_init.bash $(DESTDIR)/$(SBINDIR)/lustre_mds_init install -m 755 lustre_oss_init.bash $(DESTDIR)/$(SBINDIR)/lustre_oss_init install -m 755 lustre_client_init.bash $(DESTDIR)/$(SBINDIR)/lustre_client_init uninstall: -rm -f $(DESTDIR)/$(SBINDIR)/lustre_mds_init \ $(DESTDIR)/$(SBINDIR)/lustre_oss_init \ $(DESTDIR)/$(SBINDIR)/lustre_client_init deb: debuild -rfakeroot -us -uc clean: -rm -f build-arch-stamp build-indep-stamp configure-stamp debian/files debian/lustre-g5kutils.debhelper.log debian/lustre-g5kutils.substvars rm -rf debian/lustre-g5kutils update-repo: scp ../lustre-g5kutils*.deb intra-id.id:/www-id/sites/oar/www/debian/lustre/dists/unstable/main/binary-amd64 ssh intra-id.id "cd /www-id/sites/oar/www/debian/lustre && ./update.sh" ./oar-2.5.2/misc/kameleon/0000755000175000017500000000000011757171206013307 5ustar plbplb./oar-2.5.2/misc/kameleon/steps/0000755000175000017500000000000011757171206014445 5ustar plbplb./oar-2.5.2/misc/kameleon/steps/oar-2.5/0000755000175000017500000000000011757171206015530 5ustar plbplb./oar-2.5.2/misc/kameleon/steps/oar-2.5/target/0000755000175000017500000000000011757171206017016 5ustar plbplb./oar-2.5.2/misc/kameleon/steps/oar-2.5/target/g5k/0000755000175000017500000000000011757171206017504 5ustar plbplb./oar-2.5.2/misc/kameleon/steps/oar-2.5/target/g5k/oar_debian_install.yaml0000644000175000017500000000070311757171206024201 0ustar plbplboar_debian_install: - disable_node_autostart: - exec_chroot: update-rc.d oar-node disable - disable_server_autostart: - exec_chroot: update-rc.d oar-server disable - exec_chroot: update-rc.d mysql disable - disable_frontend_autostart: - exec_chroot: update-rc.d apache2 disable - exec_chroot: update-rc.d oidentd disable - install_oar_appliance_setup_scripts: - exec_chroot: cp -a $$oar_git_directory/misc/appliance_setup /root/ ./oar-2.5.2/misc/kameleon/steps/oar-2.5/target/debian/0000755000175000017500000000000011757171206020240 5ustar plbplb./oar-2.5.2/misc/kameleon/steps/oar-2.5/target/debian/oar_debian_preinstall.yaml0000644000175000017500000000041511757171206025444 0ustar plbplboar_debian_preinstall: - debian_fix_mime_support: - exec_chroot: apt-get -f install -y --force-yes mime-support || true - exec_appliance: rm -rf $$chroot/var/lib/dpkg/info/mime-support.* || true - exec_chroot: apt-get -f install -y --force-yes mime-support ./oar-2.5.2/misc/kameleon/steps/oar-2.5/target/debian/oar_debian_config_server.yaml0000644000175000017500000000070011757171206026117 0ustar plbplboar_debian_config_server: - configure_mysql: - exec_chroot: /etc/init.d/mysql start - exec_on_clean: chroot $$chroot bash -c "/etc/init.d/mysql stop || true" - configure_initd: - exec_chroot: cp $$oar_prefix_install/share/doc/oar-server/examples/init.d/oar-server /etc/init.d/ - exec_chroot: cp $$oar_prefix_install/share/doc/oar-server/examples/default/oar-server /etc/default/ - exec_chroot: update-rc.d oar-server defaults ./oar-2.5.2/misc/kameleon/steps/oar-2.5/target/debian/oar_debian_config_node.yaml0000644000175000017500000000044511757171206025544 0ustar plbplboar_debian_config_node: - configure_initd: - exec_chroot: cp $$oar_prefix_install/share/doc/oar-node/examples/init.d/oar-node /etc/init.d/ - exec_chroot: cp $$oar_prefix_install/share/doc/oar-node/examples/default/oar-node /etc/default/ - exec_chroot: update-rc.d oar-node defaults ./oar-2.5.2/misc/kameleon/steps/oar-2.5/target/debian/oar_debian_config_frontend.yaml0000644000175000017500000000045311757171206026435 0ustar plbplboar_debian_config_frontend: - configure_apache2: - exec_chroot: a2enmod ident - exec_chroot: a2enmod headers - exec_chroot: a2enmod rewrite - configure_basic_auth_api-priv: - exec_chroot: ln -s /etc/oar/apache2/oar-restful-api-priv.conf /etc/apache2/conf.d/oar-restful-api-priv.conf ./oar-2.5.2/misc/kameleon/steps/oar-2.5/target/debian/oar_debian_config_devel.yaml0000644000175000017500000000031111757171206025706 0ustar plbplboar_debian_config_devel: - hosts_check_startup: - append_file: - /etc/default/oar-server - | cat /etc/hosts | grep node1 2>/dev/null || echo '127.0.0.2 node1 node2' >> /etc/hosts ./oar-2.5.2/misc/kameleon/steps/oar-2.5/target/centos/0000755000175000017500000000000011757171206020311 5ustar plbplb./oar-2.5.2/misc/kameleon/steps/oar-2.5/target/centos/oar_centos_preinstall.yaml0000644000175000017500000000026711757171206025573 0ustar plbplboar_centos_preinstall: - fix_sudo: - exec_chroot: groupadd sudo || true - fix_sshkeys: - exec_chroot: service sshd start || true - exec_chroot: service sshd stop || true ./oar-2.5.2/misc/kameleon/steps/oar-2.5/target/centos/oar_centos_config_server.yaml0000644000175000017500000000101011757171206026234 0ustar plbplboar_centos_config_server: - configure_mysql: - exec_chroot: service mysqld start - exec_chroot: chkconfig mysqld on - exec_on_clean: chroot $$chroot bash -c "service mysqld stop || true" - configure_initd: - exec_chroot: cp $$oar_prefix_install/share/doc/oar-server/examples/init.d/oar-server /etc/rc.d/init.d/ - exec_chroot: cp $$oar_prefix_install/share/doc/oar-server/examples/default/oar-server /etc/sysconfig/ - exec_chroot: chkconfig --add oar-server - exec_chroot: chkconfig oar-server on ./oar-2.5.2/misc/kameleon/steps/oar-2.5/target/centos/oar_centos_config_node.yaml0000644000175000017500000000051611757171206025665 0ustar plbplboar_centos_config_node: - configure_initd: - exec_chroot: cp $$oar_prefix_install/share/doc/oar-node/examples/init.d/oar-node /etc/rc.d/init.d/ - exec_chroot: cp $$oar_prefix_install/share/doc/oar-node/examples/default/oar-node /etc/sysconfig/ - exec_chroot: chkconfig --add oar-node - exec_chroot: chkconfig oar-node on ./oar-2.5.2/misc/kameleon/steps/oar-2.5/target/centos/oar_centos_config_frontend.yaml0000644000175000017500000000053611757171206026561 0ustar plbplboar_centos_config_frontend: - configure_apache2: - write_file: - /etc/httpd/conf.d/identd.conf - | LoadModule ident_module modules/mod_ident.so - exec_chroot: chkconfig httpd on - configure_basic_auth_api-priv: - exec_chroot: ln -sf /etc/oar/apache2/oar-restful-api-priv.conf /etc/httpd/conf.d/oar-restful-api-priv.conf ./oar-2.5.2/misc/kameleon/steps/oar-2.5/target/centos/oar_centos_config_devel.yaml0000644000175000017500000000031411757171206026033 0ustar plbplboar_centos_config_devel: - hosts_check_startup: - append_file: - /etc/sysconfig/oar-server - | cat /etc/hosts | grep node1 2>/dev/null || echo '127.0.0.2 node1 node2' >> /etc/hosts ./oar-2.5.2/misc/kameleon/steps/oar-2.5/taktuk_install.yaml0000644000175000017500000000117411757171206021450 0ustar plbplbtaktuk_install: - extract_tgz: - exec_chroot: mkdir -p /root/install/taktuk - exec_chroot: tar xzf $$oar_git_directory/third_party/tarball/taktuk-3.7.3.tar.gz -C /root/install/taktuk - exec_chroot: chown -R root:root /root/install/taktuk - exec_chroot: chmod -R o-w /root/install/taktuk - exec_chroot: sh -c "cd /root/install/taktuk/taktuk-3.7.3/ && ./configure" - exec_chroot: sh -c "cd /root/install/taktuk/taktuk-3.7.3/ && make" - exec_chroot: sh -c "cd /root/install/taktuk/taktuk-3.7.3/ && make install" - exec_chroot: ln -s /usr/local/bin/taktuk /usr/bin/taktuk ./oar-2.5.2/misc/kameleon/steps/oar-2.5/source/0000755000175000017500000000000011757171206017030 5ustar plbplb./oar-2.5.2/misc/kameleon/steps/oar-2.5/source/oar_rpm_install.yaml0000644000175000017500000000066311757171206023106 0ustar plbplboar_rpm_install: - fetch_repository: - write_file: - /etc/yum.repos.d/oar.repo - | [oar] name=OAR baseurl=$$oar_rpm_repository gpgcheck=0 enabled=1 - server: - exec_chroot: yum -y install oar-server oar-server-mysql oar-doc - frontend: - exec_chroot: yum -y install oar-api oar-user oar-user-mysql oar-web-status oar-admin - node: - exec_chroot: yum -y install oar-node ./oar-2.5.2/misc/kameleon/steps/oar-2.5/source/oar_git_install.yaml0000644000175000017500000000265011757171206023071 0ustar plbplboar_git_install: - fetch_repository: - exec_chroot: mkdir -p $$oar_git_directory - exec_chroot: git init $$oar_git_directory - exec_appliance: git --git-dir $$chroot/$$oar_git_directory/.git --work-tree $$chroot/$$oar_git_directory fetch --depth 0 $$oar_git_repository $$oar_git_treeish - exec_appliance: git --git-dir $$chroot/$$oar_git_directory/.git --work-tree $$chroot/$$oar_git_directory checkout FETCH_HEAD - exec_chroot: make -C $$oar_git_directory clean - node: - exec_chroot: make -C $$oar_git_directory PREFIX=$$oar_prefix_install build - exec_chroot: make -C $$oar_git_directory PREFIX=$$oar_prefix_install node-install - exec_chroot: make -C $$oar_git_directory PREFIX=$$oar_prefix_install node-setup - frontend: - exec_chroot: make -C $$oar_git_directory PREFIX=$$oar_prefix_install build - exec_chroot: make -C $$oar_git_directory PREFIX=$$oar_prefix_install user-install draw-gantt-install monika-install www-conf-install api-install tools-install - exec_chroot: make -C $$oar_git_directory PREFIX=$$oar_prefix_install user-setup draw-gantt-setup monika-setup www-conf-setup api-setup tools-setup - server: - exec_chroot: make -C $$oar_git_directory PREFIX=$$oar_prefix_install build - exec_chroot: make -C $$oar_git_directory PREFIX=$$oar_prefix_install server-install doc-install - exec_chroot: make -C $$oar_git_directory PREFIX=$$oar_prefix_install server-setup doc-setup ./oar-2.5.2/misc/kameleon/steps/oar-2.5/source/oar_deb_install.yaml0000644000175000017500000000144211757171206023036 0ustar plbplboar_deb_install: - fetch_repository: - append_file: - /etc/apt/sources.list - | $$oar_deb_repository - exec_chroot: bash -c "curl http://oar-ftp.imag.fr/oar/oarmaster.asc | apt-key add -" - exec_chroot: apt-get update - server: - exec_chroot: apt-get install -y --force-yes oar-server oar-server-mysql oar-doc - node: - exec_chroot: apt-get install -y --force-yes oar-node - frontend: - exec_chroot: apt-get install -y --force-yes oar-user oar-user-mysql oar-node oar-doc oar-admin oar-web-status - exec_chroot: apt-get install -y --force-yes oar-api - exec_chroot: apt-get install -y --force-yes libapache2-mod-fastcgi - exec_chroot: a2enmod ident - exec_chroot: a2enmod headers - exec_chroot: a2enmod rewrite - exec_chroot: a2enmod fastcgi ./oar-2.5.2/misc/kameleon/steps/oar-2.5/oar_precheck.yaml0000644000175000017500000000024411757171206021041 0ustar plbplboar_precheck: - mysql: - exec_current: ps awux |grep mysql|grep -v grep >/dev/null && echo -e "\n*** YO SHOULD STOP YOUR MYSQL SERVER! ***\n" && exit 1 || true ./oar-2.5.2/misc/kameleon/steps/oar-2.5/oar_config_system.yaml0000644000175000017500000000255111757171206022131 0ustar plbplboar_config_system: - adduser: - exec_chroot: useradd -m -p '$1$f/W7v.Ze$LMPAjbXywS/f1dHzrmXpg/' -s /bin/bash kameleon - exec_chroot: chown kameleon:kameleon /home/kameleon - sudo: - append_file: - /etc/sudoers - | %sudo ALL=NOPASSWD: ALL - exec_chroot: usermod -G sudo kameleon - motd_config: - append_file: - /etc/motd.tail - | ************************** WELCOME TO THE OAR APPLIANCE ************************ We created 2 fake nodes pointing to localhost. You can, for example, directly: $ su - kameleon $ oarsub -I Or check the API: $ wget -O - http://localhost/oarapi/resources.yaml Check the API more deeply, submitting a job as the "kameleon" user: $ curl -i -X POST http://kameleon:kameleon@localhost/oarapi-priv/jobs.json \\ -H'Content-Type: application/json' \\ -d '{\"resource\":\"/nodes=1,walltime=00:10:00\", \"command\":\"sleep 600\"}' If you installed from the sources, then you can launch the tests with: $ cd ~kameleon/oar_install/tests/rspec && make Notes: - if you want to change the keyboard mapping, use: 'loadkeys <2_letters_country_code>' - root password is: kameleon ******************************************************************************** ./oar-2.5.2/misc/kameleon/steps/oar-2.5/oar_config_server.yaml0000644000175000017500000000223711757171206022114 0ustar plbplboar_config_server: - configure_oar_log_level: - exec_chroot: sed -e 's/^LOG_LEVEL\=\"2\"/LOG_LEVEL\=\"3\"/' -i /etc/oar/oar.conf - configure_taktuk: - exec_chroot: sed -e 's/^#\(TAKTUK_CMD\=\"\/usr\/bin\/taktuk \-t 30 \-s\".*\)/\1/' -i /etc/oar/oar.conf - exec_chroot: sed -e 's/^#\(PINGCHECKER_TAKTUK_ARG_COMMAND\=\"broadcast exec timeout 5 kill 9 \[ true \]\".*\)/\1/' -i /etc/oar/oar.conf - configure_cpuset: - exec_chroot: sed -e 's/^#\(JOB_RESOURCE_MANAGER_PROPERTY_DB_FIELD\=\"cpuset\".*\)/\1/' -i /etc/oar/oar.conf - exec_chroot: sed -e 's/^#\(JOB_RESOURCE_MANAGER_FILE\=\"\/etc\/oar\/job_resource_manager\.pl\".*\)/\1/' -i /etc/oar/oar.conf - exec_chroot: sed -e 's/^#\(CPUSET_PATH\=\"\/oar\".*\)/\1/' -i /etc/oar/oar.conf - configure_database: - exec_chroot: sed -e 's/^\(DB_BASE_PASSWD\)=.*/\1="oar"/' -i /etc/oar/oar.conf - exec_chroot: sed -e 's/^\(DB_BASE_LOGIN\)=.*/\1="oar"/' -i /etc/oar/oar.conf - exec_chroot: sed -e 's/^\(DB_BASE_PASSWD_RO\)=.*/\1="oar_ro"/' -i /etc/oar/oar.conf - exec_chroot: sed -e 's/^\(DB_BASE_LOGIN_RO\)=.*/\1="oar_ro"/' -i /etc/oar/oar.conf - exec_chroot: oar-database --create --db-admin-user root ./oar-2.5.2/misc/kameleon/steps/oar-2.5/oar_config_node.yaml0000644000175000017500000000015711757171206021532 0ustar plbplboar_config_node: - configure_nothing: - exec_chroot: echo "There is nothing to do to configure the node" ./oar-2.5.2/misc/kameleon/steps/oar-2.5/oar_config_frontend.yaml0000644000175000017500000000266511757171206022432 0ustar plbplboar_config_frontend: - configure_drawgantt: - exec_chroot: sed -e "s/^\(DB_BASE_LOGIN_RO.*\)oar.*/\1oar_ro/" -i /etc/oar/drawgantt.conf - exec_chroot: sed -e "s/^\(DB_BASE_PASSWD_RO.*\)oar.*/\1oar_ro/" -i /etc/oar/drawgantt.conf - configure_monika: - exec_chroot: sed -e 's/^\(username\) ?\=.*/\1 = oar_ro/' -i /etc/oar/monika.conf - exec_chroot: sed -e 's/^\(password\) ?\=.*/\1 = oar_ro/' -i /etc/oar/monika.conf - configure_open_api: - exec_chroot: perl -pi -e "s/Deny from all/#Deny from all/" /etc/oar/apache2/oar-restful-api.conf - configure_basic_auth_api-priv: - write_file: - /etc/oar/apache2/oar-restful-api-priv.conf - | ScriptAlias /oarapi-priv $$oar_prefix_install/lib/cgi-bin/oarapi/oarapi.cgi ScriptAlias /oarapi-priv-debug $$oar_prefix_install/lib/cgi-bin/oarapi/oarapi.cgi Options ExecCGI -MultiViews FollowSymLinks AuthType basic AuthUserfile /etc/oar/api-users AuthName \"OAR API authentication\" Require valid-user #RequestHeader set X_REMOTE_IDENT %{REMOTE_USER}e RewriteEngine On RewriteCond %{REMOTE_USER} (.*) RewriteRule .* - [E=MY_REMOTE_IDENT:%1] RequestHeader add X-REMOTE_IDENT %{MY_REMOTE_IDENT}e - exec_chroot: htpasswd -b -c /etc/oar/api-users kameleon kameleon - exec_chroot: htpasswd -b /etc/oar/api-users oar kameleon ./oar-2.5.2/misc/kameleon/steps/oar-2.5/oar_config_devel.yaml0000644000175000017500000000164011757171206021702 0ustar plbplboar_config_devel: - update_hostfile: - append_file: - /etc/hosts - | 127.0.0.2 node1 node2 - create_resources: - exec_chroot: oarproperty -a core - exec_chroot: oarproperty -a cpu - exec_chroot: oarnodesetting -a -h node1 -p cpu=0 -p core=0 -p cpuset=0 - exec_chroot: oarnodesetting -a -h node1 -p cpu=0 -p core=1 -p cpuset=0 - exec_chroot: oarnodesetting -a -h node1 -p cpu=1 -p core=2 -p cpuset=0 - exec_chroot: oarnodesetting -a -h node1 -p cpu=1 -p core=3 -p cpuset=0 - exec_chroot: oarnodesetting -a -h node2 -p cpu=2 -p core=4 -p cpuset=0 - exec_chroot: oarnodesetting -a -h node2 -p cpu=2 -p core=5 -p cpuset=0 - exec_chroot: oarnodesetting -a -h node2 -p cpu=3 -p core=6 -p cpuset=0 - exec_chroot: oarnodesetting -a -h node2 -p cpu=3 -p core=7 -p cpuset=0 - modify_job_resource_manager: - exec_chroot: sed -e 's/#exit/exit/' -i /etc/oar/job_resource_manager.pl ./oar-2.5.2/misc/kameleon/steps/oar-2.5/oar_clean.yaml0000644000175000017500000000131711757171206020341 0ustar plbplboar_clean: - shutdown_processes: - exec_chroot: /etc/init.d/oar-server stop || true - exec_chroot: service oar-server stop || true - exec_chroot: /etc/init.d/mysql stop || true - exec_chroot: service mysqld stop || true - exec_chroot: /etc/init.d/oidentd stop || true - exec_chroot: service oidentd stop || true - exec_chroot: /etc/init.d/apache2 stop || true - exec_chroot: service httpd stop || true - exec_chroot: service dbus stop || true - exec_chroot: /etc/init.d/dbus stop || true - exec_chroot: service ntp stop || true - exec_chroot: /etc/init.d/ntp stop || true - exec_chroot: service openbsd-inetd stop || true - exec_chroot: /etc/init.d/openbsd-inetd stop || true ./oar-2.5.2/misc/kameleon/recipes/0000755000175000017500000000000011757171206014741 5ustar plbplb./oar-2.5.2/misc/kameleon/recipes/oar_slurm.yaml0000644000175000017500000000531111757171206017630 0ustar plbplb# ### debian.yaml Kameleon recipe ### # This recipe can be used to create any Debian appliance. It has been # tested succesfully to generate etch, lenny and squeeze as of April 2010. # The steps and variables beginning with "oar" can be activated to make # a OAR client/server demo/test appliance. # By default, it makes a tgz image and a qcow2 directly bootable into KVM. # global: # # Where Kameleon stores tmp files and appliances workdir_base: /var/tmp/kameleon # # Debian specific distrib: debian debian_version_name: squeeze distrib_repository: http://ftp.fr.debian.org/debian/ output_environment_file_system_type: ext4 # # Architecture arch: i386 kernel_arch: "686" # # Extra packages to install on the minimal base system # If you're building a OAR appliance, use this package list: extra_packages: "vim less bzip2 openssh-server rsync gnupg locales debian-keyring ruby libyaml-perl libjson-perl mysql-server mysql-client sudo libdbi-perl libdbd-mysql-perl perl-suid taktuk pidentd console-tools mingetty curl nfs-kernel-server nfs-common munge libmunge2 libmunge-dev build-essential libreadline6-dev gcc python gawk" # # Network configuration network_hostname: kameleon # Following variables are used by the "network_config_static" step #network_eth0_ip: 129.88.70.251 #network_eth0_mask: 255.255.255.248 #network_gateway: 129.88.70.249 #network_dns: 129.88.30.10 #network_domain: imag.fr # # If you're building a Xen appliance, it may help: #xen_domu_modules: /lib/modules/2.6.32-4-xen-amd64 # # You can start with a "checkpoint_resume" step if you want # to start form a pre-built image. The image can be made # with the "checkpoint" step. checkpoint_file: /var/tmp/kameleon_checkpoint.tgz # # OAR specific oar_repository: "deb http://oar.imag.fr/debian/2.5 unstable main" slurm_repository: "http://downloads.sourceforge.net/project/slurm/under_development" slurm_version: "slurm-2.2.0-0.pre10" steps: - debian_check_deps - check_deps: - rsync - building_appliance - building_kvm_images - oar-2.5/oar_precheck # - checkpoint_resume - bootstrap - system_config - root_passwd - mount_proc - software_install: - extra_packages - kernel_install # - checkpoint - oar-2.5/oar_debian_install - oar-2.5/oar_system_config - oar-2.5/oar_config - oar-2.5/oar_devel - oar-2.5/oar_clean # - checkpoint - slurm/slurm_install - autologin - strip - umount_proc # - xen_domu # - oar-2.5/oar_build_tgz - build_appliance: - clean_udev - create_raw_image - create_nbd_device - mkfs - mount_image - copy_system_tree # - grub_197_workaround - install_grub - umount_image - save_as_raw # - save_as_vmdk # - save_as_qcow2 # - save_as_vdi # - save_as_iso - clean ./oar-2.5.2/misc/kameleon/recipes/g5k/0000755000175000017500000000000011757171206015427 5ustar plbplb./oar-2.5.2/misc/kameleon/recipes/g5k/oar_git2debian.yaml0000755000175000017500000000710611757171206021173 0ustar plbplb# ### debian.yaml Kameleon recipe ### # This recipe can be used to create any Debian appliance. It has been # tested succesfully to generate etch, lenny and squeeze as of April 2010. # The steps and variables beginning with "oar" can be activated to make # a OAR client/server demo/test appliance. # By default, it makes a tgz image and a qcow2 directly bootable into KVM. # global: # # Where Kameleon stores tmp files and appliances workdir_base: /var/tmp/kameleon # # Debian specific distrib: debian debian_version_name: squeeze distrib_repository: http://ftp.fr.debian.org/debian output_environment_file_system_type: ext4 # # Architecture arch: amd64 kernel_arch: "amd64" # # Extra packages to install on the minimal base system #extra_packages: "vim less bzip2 openssh-server rsync gnupg locales debian-keyring console-tools mingetty" # If you're building a OAR appliance, use this package list: extra_packages: "vim less bzip2 openssh-server rsync gnupg locales debian-keyring ruby libyaml-perl libjson-perl mysql-server mysql-client sudo libdbi-perl libdbd-mysql-perl perl-suid taktuk oidentd console-tools mingetty curl git-core make python-docutils ocaml-findlib libmysql-ocaml libmysql-ocaml-dev apache2-mpm-prefork libjson-perl libwww-perl ntp libdbd-mysql-ruby libgd-ruby1.8 perl libdbi-perl libdbd-mysql-perl libtie-ixhash-perl libappconfig-perl libsort-naturally-perl libsort-versions-perl openjdk-6-jre libcgi-fast-perl libdevel-nytprof-perl" # # Network configuration network_hostname: oar # Following variables are used by the "network_config_static" step #network_eth0_ip: 129.88.70.251 #network_eth0_mask: 255.255.255.248 #network_gateway: 129.88.70.249 #network_dns: 129.88.30.10 #network_domain: imag.fr # # If you're building a Xen appliance, it may help: #xen_domu_modules: /lib/modules/2.6.32-4-xen-amd64 # # You can start with a "checkpoint_resume" step if you want # to start form a pre-built image. The image can be made # with the "checkpoint" step. #checkpoint_file: /var/tmp/kameleon_checkpoint.tgz # # OAR specific oar_git_repository: git://scm.gforge.inria.fr/oar/oar.git oar_git_treeish: "2.5" oar_git_directory: /home/kameleon/oar_install # OAR specific (you probably don't need to change those) oar_prefix_install: /usr/local steps: - chroot_in_tmpfs - debian_check_deps - check_deps: - rsync - building_appliance - building_kvm_images - oar-2.5/oar_precheck - bootstrap_if_needed - system_config # - network_config_static - root_passwd - mount_proc - mount_dev - software_install: - extra_packages - kernel_install - oar-2.5/target/debian/oar_debian_preinstall - oar-2.5/source/oar_git_install: - fetch_repository - node - server - frontend - oar-2.5/target/debian/oar_debian_config_node - oar-2.5/target/debian/oar_debian_config_server - oar-2.5/target/debian/oar_debian_config_frontend - oar-2.5/oar_config_node - oar-2.5/oar_config_server - oar-2.5/oar_config_frontend - oar-2.5/oar_config_system - oar-2.5/target/g5k/oar_debian_install: - disable_node_autostart - disable_server_autostart - disable_frontend_autostart - install_oar_appliance_setup_scripts - oar-2.5/oar_clean # - kameleon/kameleon_debian_install - autologin - strip # - xen_domu # - oar/oar_build_tgz - build_appliance_kpartx: - clean_udev # - save_as_tgz - create_raw_image - attach_kpartx_device - mkfs - mount_image - copy_system_tree # - grub_197_workaround - install_grub - umount_image - save_as_raw # - save_as_vmdk - save_as_qcow2 # - save_as_vdi # - save_as_iso - save_as_g5k - clean ./oar-2.5.2/misc/kameleon/recipes/devel/0000755000175000017500000000000011757171206016040 5ustar plbplb./oar-2.5.2/misc/kameleon/recipes/devel/oar_rpm2centos.yaml0000644000175000017500000000743711757171206021674 0ustar plbplb# ### debian.yaml Kameleon recipe ### # This recipe can be used to create any Debian appliance. It has been # tested succesfully to generate etch, lenny and squeeze as of April 2010. # The steps and variables beginning with "oar" can be activated to make # a OAR client/server demo/test appliance. # By default, it makes a tgz image and a qcow2 directly bootable into KVM. # global: # # Where Kameleon stores tmp files and appliances workdir_base: /var/tmp/kameleon # # Centos specific distrib: rpm rpm_distrib_name: centos-6 output_environment_file_system_type: ext4 # # Architecture arch: amd64 # # Extra packages to install on the minimal base system #extra_packages: "vim less bzip2 openssh-server rsync gnupg locales debian-keyring console-tools mingetty" extra_packages: "redhat-lsb vim-enhanced less bzip2 openssh-server rsync gnupg perl man gcc make tar python-docutils git oidentd mysql-server mysql perl-DBD-MySQL Perl Perl-base openssh Perl-DBI perl-Sort-Versions sudo httpd perl-Time-HiRes perl-Term-UI ruby-mysql" # If you're building a OAR appliance, use this package list: #extra_packages: "vim less bzip2 openssh-server rsync gnupg locales debian-keyring ruby libyaml-perl libjson-perl mysql-server mysql-client sudo libdbi-perl libdbd-mysql-perl taktuk oidentd console-tools mingetty curl git-core make python-docutils ocaml-findlib libmysql-ocaml libmysql-ocaml-dev apache2-mpm-prefork libjson-perl libwww-perl ntp libdbd-mysql-ruby libgd-ruby1.8 perl libdbi-perl libdbd-mysql-perl libtie-ixhash-perl libappconfig-perl libsort-naturally-perl libsort-versions-perl openjdk-6-jre libcgi-fast-perl" # # Network configuration network_hostname: oar # Following variables are used by the "network_config_static" step #network_eth0_ip: 129.88.70.251 #network_eth0_mask: 255.255.255.248 #network_gateway: 129.88.70.249 #network_dns: 129.88.30.10 #network_domain: imag.fr # # If you're building a Xen appliance, it may help: #xen_domu_modules: /lib/modules/2.6.32-4-xen-amd64 # # You can start with a "checkpoint_resume" step if you want # to start form a pre-built image. The image can be made # with the "checkpoint" step. #checkpoint_file: /var/tmp/kameleon_checkpoint.tgz # # OAR specific oar_rpm_repository: http://oar-ftp.imag.fr/oar/2.5/rpm/beta/ # Needed for taktul install from the third_party folder. oar_git_repository: git://scm.gforge.inria.fr/oar/oar.git oar_git_treeish: "2.5" oar_git_directory: /home/kameleon/oar_install # OAR specific (you probably don't need to change those) oar_prefix_install: /usr steps: - rpm_check_deps - check_deps: - rsync - building_appliance - building_kvm_images - oar-2.5/oar_precheck # - checkpoint_resume - bootstrap2 - system_config # - network_config_static - root_passwd - mount_proc - mount_dev - software_install: - epel_repository - extra_packages - kernel_install # - checkpoint - oar-2.5/target/debian/oar_centos_preinstall - oar-2.5/source/oar_rpm_install: - fetch_repository - node - server - frontend - oar-2.5/target/centos/oar_centos_config_node - oar-2.5/target/centos/oar_centos_config_server - oar-2.5/target/centos/oar_centos_config_frontend - oar-2.5/oar_config_node - oar-2.5/oar_config_server - oar-2.5/oar_config_frontend - oar-2.5/oar_config_system - oar-2.5/oar_config_devel - oar-2.5/oar_install_from_git: # Install taktuk - get_git_snapshot # Install taktuk - oar-2.5/taktuk_install # Install taktuk - oar-2.5/oar_clean - autologin - strip # - xen_domu - build_appliance: - clean_udev # - save_as_tgz - create_raw_image - create_nbd_device - mkfs - mount_image - copy_system_tree # - grub_197_workaround - install_grub - umount_image - save_as_raw # - save_as_vmdk - save_as_qcow2 # - save_as_vdi # - save_as_iso - clean ./oar-2.5.2/misc/kameleon/recipes/devel/oar_git2debian.yaml0000755000175000017500000000650511757171206021606 0ustar plbplb# ### debian.yaml Kameleon recipe ### # This recipe can be used to create any Debian appliance. It has been # tested succesfully to generate etch, lenny and squeeze as of April 2010. # The steps and variables beginning with "oar" can be activated to make # a OAR client/server demo/test appliance. # By default, it makes a tgz image and a qcow2 directly bootable into KVM. # global: # # Where Kameleon stores tmp files and appliances workdir_base: /var/tmp/kameleon # # Debian specific distrib: debian debian_version_name: squeeze distrib_repository: http://ftp.fr.debian.org/debian output_environment_file_system_type: ext4 # # Architecture arch: amd64 kernel_arch: "amd64" # # Extra packages to install on the minimal base system #extra_packages: "vim less bzip2 openssh-server rsync gnupg locales debian-keyring console-tools mingetty" # If you're building a OAR appliance, use this package list: extra_packages: "vim less bzip2 openssh-server rsync gnupg locales debian-keyring ruby libyaml-perl libjson-perl mysql-server mysql-client sudo libdbi-perl libdbd-mysql-perl taktuk oidentd console-tools mingetty curl git-core make python-docutils ocaml-findlib libmysql-ocaml libmysql-ocaml-dev apache2-mpm-prefork libjson-perl libwww-perl ntp libdbd-mysql-ruby libgd-ruby1.8 perl libdbi-perl libdbd-mysql-perl libtie-ixhash-perl libappconfig-perl libsort-naturally-perl libsort-versions-perl openjdk-6-jre libcgi-fast-perl" # # Network configuration network_hostname: oar # Following variables are used by the "network_config_static" step #network_eth0_ip: 129.88.70.251 #network_eth0_mask: 255.255.255.248 #network_gateway: 129.88.70.249 #network_dns: 129.88.30.10 #network_domain: imag.fr # # If you're building a Xen appliance, it may help: #xen_domu_modules: /lib/modules/2.6.32-4-xen-amd64 # # You can start with a "checkpoint_resume" step if you want # to start form a pre-built image. The image can be made # with the "checkpoint" step. #checkpoint_file: /var/tmp/kameleon_checkpoint.tgz # # OAR specific oar_git_repository: git://scm.gforge.inria.fr/oar/oar.git oar_git_treeish: "2.5" oar_git_directory: /home/kameleon/oar_install # OAR specific (you probably don't need to change those) oar_prefix_install: /usr/local steps: - debian_check_deps - check_deps: - rsync - building_appliance - building_kvm_images - oar-2.5/oar_precheck # - checkpoint_resume - bootstrap - system_config # - network_config_static - root_passwd - mount_proc - mount_dev - software_install: - extra_packages - kernel_install # - checkpoint - oar-2.5/target/debian/oar_debian_preinstall - oar-2.5/source/oar_git_install: - fetch_repository - node - server - frontend - oar-2.5/target/debian/oar_debian_config_server - oar-2.5/target/debian/oar_debian_config_frontend - oar-2.5/target/debian/oar_debian_config_node - oar-2.5/oar_config_node - oar-2.5/oar_config_server - oar-2.5/oar_config_frontend - oar-2.5/oar_config_system - oar-2.5/oar_config_devel - oar-2.5/oar_clean - autologin - strip # - xen_domu - build_appliance: - clean_udev # - save_as_tgz - create_raw_image - create_nbd_device - mkfs - mount_image - copy_system_tree # - grub_197_workaround - install_grub - umount_image - save_as_raw # - save_as_vmdk - save_as_qcow2 # - save_as_vdi # - save_as_iso - clean ./oar-2.5.2/misc/kameleon/recipes/devel/oar_git2centos.yaml0000755000175000017500000000674411757171206021664 0ustar plbplb# ### debian.yaml Kameleon recipe ### # This recipe can be used to create any Debian appliance. It has been # tested succesfully to generate etch, lenny and squeeze as of April 2010. # The steps and variables beginning with "oar" can be activated to make # a OAR client/server demo/test appliance. # By default, it makes a tgz image and a qcow2 directly bootable into KVM. # global: # # Where Kameleon stores tmp files and appliances workdir_base: /var/tmp/kameleon # # Centos specific distrib: rpm rpm_distrib_name: centos-6 output_environment_file_system_type: ext4 # # Architecture arch: amd64 # # Extra packages to install on the minimal base system #extra_packages: "vim less bzip2 openssh-server rsync gnupg locales debian-keyring console-tools mingetty" extra_packages: "redhat-lsb vim-enhanced less bzip2 openssh-server rsync gnupg perl man gcc make tar python-docutils git oidentd mysql-server mysql perl-DBD-MySQL Perl Perl-base openssh Perl-DBI perl-Sort-Versions sudo httpd perl-Time-HiRes perl-Term-UI ruby-mysql" # If you're building a OAR appliance, use this package list: #extra_packages: "vim less bzip2 openssh-server rsync gnupg locales debian-keyring ruby libyaml-perl libjson-perl mysql-server mysql-client sudo libdbi-perl libdbd-mysql-perl taktuk oidentd console-tools mingetty curl git-core make python-docutils ocaml-findlib libmysql-ocaml libmysql-ocaml-dev apache2-mpm-prefork libjson-perl libwww-perl ntp libdbd-mysql-ruby libgd-ruby1.8 perl libdbi-perl libdbd-mysql-perl libtie-ixhash-perl libappconfig-perl libsort-naturally-perl libsort-versions-perl openjdk-6-jre libcgi-fast-perl" # # Network configuration network_hostname: oar # Following variables are used by the "network_config_static" step #network_eth0_ip: 129.88.70.251 #network_eth0_mask: 255.255.255.248 #network_gateway: 129.88.70.249 #network_dns: 129.88.30.10 #network_domain: imag.fr # # If you're building a Xen appliance, it may help: #xen_domu_modules: /lib/modules/2.6.32-4-xen-amd64 # # You can start with a "checkpoint_resume" step if you want # to start form a pre-built image. The image can be made # with the "checkpoint" step. #checkpoint_file: /var/tmp/kameleon_checkpoint.tgz # # OAR specific oar_git_repository: git://scm.gforge.inria.fr/oar/oar.git oar_git_treeish: "2.5" oar_git_directory: /home/kameleon/oar_install # OAR specific (you probably don't need to change those) oar_prefix_install: /usr/local steps: - rpm_check_deps - check_deps: - rsync - building_appliance - building_kvm_images - oar-2.5/oar_precheck # - checkpoint_resume - bootstrap2 - system_config # - network_config_static - root_passwd - mount_proc - mount_dev - software_install: - epel_repository - extra_packages - kernel_install # - checkpoint - oar-2.5/target/centos/oar_centos_preinstall - oar-2.5/source/oar_git_install: - fetch_repository - node - server - frontend - oar-2.5/target/centos/oar_centos_config_server - oar-2.5/target/centos/oar_centos_config_frontend - oar-2.5/oar_config_node - oar-2.5/oar_config_server - oar-2.5/oar_config_frontend - oar-2.5/oar_config_system - oar-2.5/oar_config_devel - oar-2.5/oar_clean - autologin - strip # - xen_domu - build_appliance: - clean_udev # - save_as_tgz - create_raw_image - create_nbd_device - mkfs - mount_image - copy_system_tree # - grub_197_workaround - install_grub - umount_image - save_as_raw # - save_as_vmdk - save_as_qcow2 # - save_as_vdi # - save_as_iso - clean ./oar-2.5.2/misc/kameleon/recipes/devel/oar_deb2debian.yaml0000644000175000017500000000602411757171206021546 0ustar plbplb# ### debian.yaml Kameleon recipe ### # This recipe can be used to create any Debian appliance. It has been # tested succesfully to generate etch, lenny and squeeze as of April 2010. # The steps and variables beginning with "oar" can be activated to make # a OAR client/server demo/test appliance. # By default, it makes a tgz image and a qcow2 directly bootable into KVM. # global: # # Where Kameleon stores tmp files and appliances workdir_base: /var/tmp/kameleon # # Debian specific distrib: debian debian_version_name: sid distrib_repository: http://ftp.fr.debian.org/debian output_environment_file_system_type: ext4 # # Architecture arch: amd64 kernel_arch: "amd64" # # Extra packages to install on the minimal base system #extra_packages: "vim less bzip2 openssh-server rsync gnupg locales debian-keyring console-tools mingetty" # If you're building a OAR appliance, use this package list: extra_packages: "vim less bzip2 openssh-server rsync gnupg locales debian-keyring ruby libyaml-perl libjson-perl mysql-server mysql-client sudo libdbi-perl libdbd-mysql-perl taktuk pidentd console-tools mingetty curl librestclient-ruby librspec-ruby libjson-ruby" # # Network configuration network_hostname: oar # Following variables are used by the "network_config_static" step #network_eth0_ip: 129.88.70.251 #network_eth0_mask: 255.255.255.248 #network_gateway: 129.88.70.249 #network_dns: 129.88.30.10 #network_domain: imag.fr # # If you're building a Xen appliance, it may help: #xen_domu_modules: /lib/modules/2.6.32-4-xen-amd64 # # You can start with a "checkpoint_resume" step if you want # to start form a pre-built image. The image can be made # with the "checkpoint" step. #checkpoint_file: /var/tmp/kameleon_checkpoint.tgz # # OAR specific oar_deb_repository: "deb http://oar.imag.fr/mirror/oar/2.5/debian squeeze-unstable main" # OAR specific (you probably don't need to change those) oar_prefix_install: /usr steps: - debian_check_deps - check_deps: - rsync - building_appliance - building_kvm_images - oar-2.5/oar_precheck # - checkpoint_resume - bootstrap - system_config # - network_config_static - root_passwd - mount_proc - mount_dev - software_install: - extra_packages - kernel_install # - checkpoint - oar-2.5/target/debian/oar_debian_preinstall - oar-2.5/source/oar_deb_install: - fetch_repository - node - server - frontend - oar-2.5/target/debian/oar_debian_config_node - oar-2.5/target/debian/oar_debian_config_server - oar-2.5/target/debian/oar_debian_config_frontend - oar-2.5/oar_config_node - oar-2.5/oar_config_server - oar-2.5/oar_config_frontend - oar-2.5/oar_config_system - oar-2.5/oar_config_devel - oar-2.5/oar_clean - autologin - strip # - xen_domu - build_appliance: - clean_udev # - save_as_tgz - create_raw_image - create_nbd_device - mkfs - mount_image - copy_system_tree # - grub_197_workaround - install_grub - umount_image - save_as_raw # - save_as_vmdk - save_as_qcow2 # - save_as_vdi # - save_as_iso - clean ./oar-2.5.2/misc/fault_tolerance/0000755000175000017500000000000011757171206014663 5ustar plbplb./oar-2.5.2/misc/fault_tolerance/script-bench-mysql.sh0000755000175000017500000001767311757171206020764 0ustar plbplb#!/bin/bash echo "Installer en premier les module assistant !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" #echo "Lancer les tests sur cette machine ? (y) or (n)" ok="y" #echo 'Is it the master machine ? (y) or (n)' rep="y" if [ "$rep" = "y" ] then backup="genepi-32.grenoble.grid5000.fr" elif [ "$rep" = "n" ] then master="genepi-32.grenoble.grid5000.fr" else echo 'erreur' exit 1 fi eth="eth1" #####-----Test sans DRBD if [ "$ok" = y ]; then mkdir benchtest i=5000 while [ $i -le 640000 ]; do for j in $(seq 0 9) do sysbench --test=oltp --oltp-table-size=200000 --mysql-user=root prepare sysbench --test=oltp --num-threads=16 --max-requests=$i --mysql-user=root run > benchtest/max-requests-$i--iteration-$j sysbench --test=oltp --mysql-user=root cleanup done i=$((i*2)) done fi sleep 10 DRBD="drbd8-utils" MODULEASSITANT="module-assistant" BD="mysql" ####--Partionnement---##### #Changer le /dev/loop/0 LOOPBACK=/dev/loop/0 #taille en MegaBytes TAILLE="2000" #---------------------------Initialisation--------------------------------------------------------------------# #On remet la conf de mysql comme au debut if [ -e /etc/mysql/my.cnf.backup ]; then cp /etc/mysql/my.cnf.backup /etc/mysql/my.cnf fi #-----------------------Installation on debian--------------------------------------------------- #apt-get update #apt-get -y install $DRBD $MODULEASSITANT #module-assistant auto-install drbd8 #-----------------------Arret des services--------------------------------------------------- if [ "$BD" = "mysql" ]; then /etc/init.d/$BD stop elif [ "$BD" = "postgresql" ]; then /etc/init.d/$BD"-"$PGVERSION stop else exit 1 fi #attention, peut beuger si le systeme n'est pas en anglais if [ "$rep" = "y" ]; then iplocal=$(ifconfig $eth | grep "inet " | cut -d : -f2 | cut -d " " -f1) ipdist=$(resolveip $backup -s) elif [ "$rep" = "n" ]; then iplocal=$(resolveip $master -s) ipdist=$(ifconfig $eth | grep "inet " | cut -d : -f2 | cut -d " " -f1) else exit 1 fi #-----------------------Variable pour configuration de DRBD--------------------------------------------------- if [ "$BD" = "mysql" ]; then mysqldirold=$(cat /etc/$BD/my.cnf | grep datadir | cut -d "=" -f2) elif [ "$BD" = "postgresql" ]; then postgresdirold=$(cat /etc/$BD/$PGVERSION/main/postgresql.conf | grep data_directory | cut -d "'" -f2) else exit 1 fi #-----------------------Configuration de DRBD--------------------------------------------------- echo 'global { usage-count no; }' > /etc/drbd.conf echo 'resource mysql {' >> /etc/drbd.conf echo ' # Trois protocoles sont disponibles :' >> /etc/drbd.conf echo ' # En protocole A, l acquittement d ecriture (sur le maître)' >> /etc/drbd.conf echo ' # est envoyé dès que les données ont été transmises au' >> /etc/drbd.conf echo ' # sous volume physique et envoyé à l esclave.' >> /etc/drbd.conf echo ' # En protocole B, l acquittement d ecriture (sur le maître)' >> /etc/drbd.conf echo ' # est envoyé dès que les données ont été transmises au' >> /etc/drbd.conf echo ' # sous volume physique et reçues par l esclave.' >> /etc/drbd.conf echo ' # En protocole C, l acquittement d écriture (sur le maître)' >> /etc/drbd.conf echo ' # est envoyé dès que les données ont été transmises au' >> /etc/drbd.conf echo ' # sous volume physique ET au sous volume physique de' >> /etc/drbd.conf echo ' # l esclave.' >> /etc/drbd.conf echo ' protocol C;' >> /etc/drbd.conf echo ' startup {' >> /etc/drbd.conf echo ' # Au démarrage du noeud, on attend (cela bloque le' >> /etc/drbd.conf echo ' # démarrage) le noeud distant pendant 2 minutes.' >> /etc/drbd.conf echo ' wfc-timeout 120;' >> /etc/drbd.conf echo ' }' >> /etc/drbd.conf echo ' # Si une erreur d entrée sortie est rencontrée avec le sous' >> /etc/drbd.conf echo ' # volume physique, on freeze la machine et' >> /etc/drbd.conf echo ' # Heartbeat basculera l exploitation sur l autre machine' >> /etc/drbd.conf echo ' disk {' >> /etc/drbd.conf echo ' on-io-error detach;' >> /etc/drbd.conf echo ' }' >> /etc/drbd.conf echo ' syncer {' >> /etc/drbd.conf echo ' rate 700000K;' >> /etc/drbd.conf echo ' # On ne limite pas la vitesse de synchronisation.' >> /etc/drbd.conf echo ' al-extents 257;' >> /etc/drbd.conf echo ' # al-extent définit la taille de la « hot-area » : DRBD' >> /etc/drbd.conf echo ' # stocke en permanence dans les meta-data les zones' >> /etc/drbd.conf echo ' # actives du volume physique. En cas de crash ces zones' >> /etc/drbd.conf echo ' # seront ainsi resynchronisées dès le retour du' >> /etc/drbd.conf echo ' # noeud. Chaque extent définit une zone de 4Mo. Plus al-' >> /etc/drbd.conf echo ' # extents est grand, plus la resynchronisation après un' >> /etc/drbd.conf echo ' # crash sera longue, mais il y aura moins d écritures de' >> /etc/drbd.conf echo ' # meta-data.' >> /etc/drbd.conf echo ' # Avec 257 on a donc 1giga de « hot aera ».' >> /etc/drbd.conf echo ' }' >> /etc/drbd.conf if [ "$rep" = "y" ]; then master=$(uname -n) elif [ "$rep" = "n" ]; then backup=$(uname -n) else exit 1 fi echo " on $master {" >> /etc/drbd.conf echo ' device /dev/drbd0;' >> /etc/drbd.conf echo " disk $LOOPBACK;" >> /etc/drbd.conf echo " address $iplocal:7788;" >> /etc/drbd.conf echo ' meta-disk internal;' >> /etc/drbd.conf echo ' }' >> /etc/drbd.conf echo " on $backup {" >> /etc/drbd.conf echo ' device /dev/drbd0;' >> /etc/drbd.conf echo " disk $LOOPBACK;" >> /etc/drbd.conf echo " address $ipdist:7788;" >> /etc/drbd.conf echo ' meta-disk internal;' >> /etc/drbd.conf echo ' }' >> /etc/drbd.conf echo '}' >> /etc/drbd.conf #-----------------------Creation de la partition contenant les donnees BDD--------------------------------------------------- modprobe drbd dd if=/dev/zero of=/image.img bs=1M count=$TAILLE losetup $LOOPBACK /image.img mkfs -t ext2 $LOOPBACK shred -zvf -n 1 $LOOPBACK if [ ! -e /dev/drbd0 ]; then mknod /dev/drbd0 b 147 0 fi #Creation des metadata drbdadm create-md all drbdadm up all if [ ! -e /mnt/drbddata ]; then mkdir /mnt/drbddata fi #a faire que sur le master if [ "$rep" = "y" ]; then #Le master lance la synchro drbdadm -- --overwrite-data-of-peer primary all mkfs -t ext2 /dev/drbd0 mount /dev/drbd0 /mnt/drbddata if [ "$BD" = "mysql" ]; then cp -r $mysqldirold /mnt/drbddata chown -R mysql:mysql /mnt/drbddata/mysql elif [ "$BD" = "postgresql" ]; then cp -r $postgresdirold /mnt/drbddata chown -R postgres:postgres /mnt/drbddata/main else exit 1 fi fi #-----------------------On change le repertoire dans la BDD--------------------------------------------------- if [ "$BD" = "mysql" ]; then mysqldiroldn=$(echo $mysqldirold | sed 's/\//\\\//g') #On fait une sauvegarde du fichier cp /etc/mysql/my.cnf /etc/mysql/my.cnf.backup sed -e "s/$mysqldiroldn/\/mnt\/drbddata\/mysql/g" /etc/mysql/my.cnf > /etc/mysql/my.cnf.tmp && mv -f /etc/mysql/my.cnf.tmp /etc/mysql/my.cnf elif [ "$BD" = "postgresql" ]; then postgresdiroldn=$(echo $postgresdirold | sed 's/\//\\\//g') #On fait une sauvegarde du fichier cp /etc/$BD/$PGVERSION/main/postgresql.conf /etc/$BD/$PGVERSION/main/postgresql.conf.backup sed -e "s/$postgresdiroldn/\/mnt\/drbddata\/main/g" /etc/$BD/$PGVERSION/main/postgresql.conf > /etc/$BD/$PGVERSION/main/postgresql.conf.tmp && mv -f /etc/$BD/$PGVERSION/main/postgresql.conf.tmp /etc/$BD/$PGVERSION/main/postgresql.conf else exit 1 fi if [ "$rep" = "y" ]; then /etc/init.d/mysql start fi sleep 300 #-----------------------TEST AVEC DRBD------# if [ "$ok" = y ]; then i=5000 while [ $i -le 640000 ]; do for j in $(seq 0 9) do sysbench --test=oltp --oltp-table-size=200000 --mysql-user=root prepare sysbench --test=oltp --num-threads=16 --max-requests=$i --mysql-user=root run > benchtest/max-requests-$i--iteration-$j--DRBD sysbench --test=oltp --mysql-user=root cleanup done i=$((i*2)) done fi #-----------------------Fin du script--------------------------------------------------- ./oar-2.5.2/misc/fault_tolerance/benchmark-results/0000755000175000017500000000000011757171206020314 5ustar plbplb./oar-2.5.2/misc/fault_tolerance/benchmark-results/description plateforme.odt0000755000175000017500000002531011757171206025472 0ustar plbplbPKXE:^2 ''mimetypeapplication/vnd.oasis.opendocument.textPKXE:Configurations2/statusbar/PKXE:'Configurations2/accelerator/current.xmlPKPKXE:Configurations2/floater/PKXE:Configurations2/popupmenu/PKXE:Configurations2/progressbar/PKXE:Configurations2/menubar/PKXE:Configurations2/toolbar/PKXE:Configurations2/images/Bitmaps/PKXE: content.xmlYn6+-vLb5AP@t݀( E$ǮПhcdd.Edv6{>\^g -TTzEB|gy=R)In,tG+"3"G"'֊eOް%ZU6-]<wW} HZ17zcMEf?Ly[ =!~8R7D%O1Δz_c3q /dU-+b9CMwm&] Ş\ ,?noZY__EU,iB4۠e ^$DvAxY0.H\%KSM"o X%{Mq{s/H[04إ\i[fRJX]0E\uN$5i 81I &*G63rg2δ6oRnc&$fjzi۩9Fل2q5l&ujTFfK6sCšGp c\wN8\0BTPK,ŸWlZQ5- i4>Vs\h`]-4ET~n.l|UQX5|p I aTmnBfneY5=VМC8㪄l77 `b=qJt{a|=%RSNF $+W@Y[Udm?M3 :Ĥę>QS j:;>SltD:>QC/<%u~ L]Oy (:'* Nx5v]#*L$W+WAx+&T o\QhF9q\ԩBi5ė{SNϳ_m̓Ul]+/2,* y%+aw kSA (8L6ayff&Ȭ9u7}U@ M΂ R?im-o~ZmAᢹ($b sF/^} lYIZ ͺ9_}~G(b]ݙx`fDubp$.,f [ۃv!oObPKv iPKXE: styles.xmlZM6W*ڛl˻InvEɥI{ hP@Rk;3(Q~pg̐Ծy{*ͤv-m*UMΗRPqKP w1_@{V}*POϪe4Kf{i4JHPר!)3r}q!1_KMG^+gΛyπ?)&bc?B_n4xK(̉hi̤{ǀ#Bz#=9_ȿyXHGmhWq,>|h :'%Yԃ`P&n >/2;zDAv6ki #'Jd}l۱=9ꇊ@尉)M<-{ <6HC(' PP<9;z'7">5[}II] ɾڸh;q1>78/~S;J; O 5;Z%ȉmԫUhQh@YCPK`GxL!PKXE:'P]]meta.xml OpenOffice.org/2.4$Linux OpenOffice.org_project/680m17$Build-9310Joris Bremond2009-06-22T10:35:50Joris Bremond2009-06-22T10:42:493PT7M0SPKXE:Thumbnails/thumbnail.pngWWT۶f *pn$F MQ Bۈ$jHW"=t (*AH$ $~qý/wdz^cα,b[K;(&&&0v/bāz\11U}H?:<,c"g1m9}WBӺMހ,ߦk.wһX~ qiҎAS/=Yjoix|njZxm'/xqSȝQ2I3JjCl`)u$?R㌧5q gI`!fA pR[p}`b]-u/F9zp juIIzsm/ s=I(qZRވ?=Z5=Lfk4 HF?Xɼ;t- N3{vDo4Ўr{}Lcf>##26G}YIؽ}E:MMwEKNĩVHFMر_M&#l;RO \B&.&":ήhvnNiv/}do.ov T0aAV/ #xL |XsC )zق~H 3(E`tX3”w7g=($V~(=1/$-|gu#;Jbnb*HݑT| B)Z+ل5<q<;g+Dv\wwh>IOf;2Ѳ"CsHEMuBģW_)"+WBuSХi\ͅ^ݺ aS7d5:ֿp{O` ucFN{*|,?\-0REB>:-{+ĥs )[/ʅwi;xz#Z~ǂ,Ix isJ0o[ "Z`ic9~'F<f\|vS)$J jmHwt5)@;ʬp<pBygҝ(9](sӗZ l#{z r |8滕nY*"s3 J \8BWior@fSVD%=xFI3 N}h D2Q"A&[s̓yši\ ׼>%rq|^Fya]1?& 5e 6|dfK>ܚ'Zt-U.5 U,# >0>zPӖ4oYcTwn =]Fug9>Pl3xwMyDA !Jϯf."ez$[(47p '2+Mv؁=D\<.H*Yqk05/^@e)o״zļJL&"fa*‚2TQw& =AIvY:n~>5 kb.c8>K 9@$^9tMw=o˙؜jG&ex[@U.e{qA>9'ɋd#dvlQGPe5]5A}'`$da7Ssgwg҃S*"ehnl$`|t﫶&Ӹﲼx1T)_UUNu_VƏ2*ZRl~%?t[W칷SOE*~I6hHv>SpPPY%/f#1rϽu4fO^ 든7ZTRX^E㤶ΥZ$,Aoߜr(na:TrI_Db-L/:s'!DH8!deܪCL"hfWYҒ,_!Vn0S[}S=GvEǶ0tFp=QM$4.e 0jYuW_5547)JaP XfU&=PKV/r /PKXE: settings.xmlY[s:~?"wL!H3o^@'#}W$`R-M߷+|]F(oӚw2٭8Tnp:4B h0.u#{}%J6iE&h` rvu#U=Y .o1qZ],STY^oKS>WU*D|Ud7dƤkjw&4g^s͛\AS"5#רyEE}l~cV1xMu[R{05Eb+WrxhE/굫r>~~qqy`LFs\ ! DLzM8LGW.4cO{D,pX'XJCx70UE5m.Ȓ]wVHŢDZI%SÝ%R_`.1\:<VhUسځzaGуj:+3Q Q}00ƌRabёr@c`i60|/= BDP8SB[8d׹X"Pߢج g$*p ]J@ieU-6Fm;cC9 NvىwDɀŠdK |s销ER?,:⼳(4۔O?ailqʫir $cY:J6p0?y1j_-䳦[k3$"GW K&հp,D)8`ǘA&]ߖtܒyX5;fwlF~ew}3_n+.p]~bt?}͙$202IJޑxi e@ ᓢ#V)c;Q&F;D[X{>i$ ['뾴U ࢭia{?[BLyj KXܗVJGMsL sD^Mli3A<}32Ԡ?h.-vS CYlSE#cU6)1̮x?͇Hu$f2ဃSaX]1=6|e30Rp_`!`[ n'Gge,dӭ=`@HMexN~U1 g bY^mލci -0:{ 7(!/'ַꮯ?PK"xgPKXE:META-INF/manifest.xmlKj0@=VU1q-&fW6X; F#h[S0Oͣ)k7vc^aaӠHѵHS"Z^%ۯɴ|.Ax.25| h;7GWsh,.dLB%Mync Y'@,`(Uq:bbqW`<0RO G?Fr7=^ ޛbpmaD-*긓_PrS4I7ZOHNzbK|0Hc-2xd7!ɧa87|"sϩ]PK5b9>JPKXE:^2 ''mimetypePKXE:MConfigurations2/statusbar/PKXE:'Configurations2/accelerator/current.xmlPKXE:Configurations2/floater/PKXE:Configurations2/popupmenu/PKXE:JConfigurations2/progressbar/PKXE:Configurations2/menubar/PKXE:Configurations2/toolbar/PKXE:Configurations2/images/Bitmaps/PKXE:v i -content.xmlPKXE:`GxL! styles.xmlPKXE:'P]] meta.xmlPKXE:V/r /AThumbnails/thumbnail.pngPKXE:"xg , settings.xmlPKXE:5b9>JC%META-INF/manifest.xmlPK&./oar-2.5.2/misc/fault_tolerance/benchmark-results/benchtest-deuxieme-version.tar.gz0000644000175000017500000152160511757171206026722 0ustar plbplbAJ=̽(* *"BS9 A$ *(A$]TQTLH  ~U=KT6}]#;=3}psZ'wjxzrZz]{$wL-95-sZNizsϬ$ @$ 0W Ꚗ*UJv_'}yL[dW˴ D RғS[V*lxgMc\#?B\Iپ&&?gH Աk 鏧&j6IAzrǤn:t|[ϛ~礚w&MMџ.mRKIn[>kZJvI{=!oî:??iISOzsw_6R];NNMXRi ˛fgW i|t vLN/6HEImSSSZwϹ DIg>i哒`R6P}%%JMNJMNIdJ$N?h*-9l+rg:uߢN=5IjwڪSZ6f][tjtJۤ6;t)ュzt1әwz 虷!:I靓/_ƑM:jc:%{y}Mz)mf&^:5E.ɩ;&X/RǢ(ٙN=S$wVo<Ptq;?De$I]wGނ6^Y%ڪwߡsՂ(<$w%g.`+qIeRz"5R<9 ıG9/=D>i];d3)=dz sqs-S̛75I'BSΞQ%BbLPg~Mb>~ # yDvL;V]Yx,mzT[xޱtIKJT6ta=*Գ|]kn*iYo5IeyWLKo6[s_Q>n*NA)gO՗@^͛o01.D}qY!'H_ݳ5. f$,_G ' ȃPwtA AI(sQjPqE _D$A5#ggz\?b&."AHc?~L_D? 21?!c/r!T@%A21215@9gJ8Nt @dt 0]" w" PY& E@DŽKP0@E "+r j(/8?F7iXq \8?+TSlP 4&_FTFH 6%Xz 1՞sOb DahءU} HY 4A}!ҡ`.8BO|"S-n0E<ƘzN!*jA:{X͹< Ym5Lm?C yRZf_P4BݐU 7THk8"qX!'T_][]av?(\k(qS+fG%`S'#iܥy'ݱ߯$@A=fD z?3>CH ??"@ԂZ,*[վ!ŧc%go3@K%4_C@z SuVj[-b A1C@r.Ft" -" iSOŊ܂B2R/$ da?ˉ@HN np Pp& QvN09, @ .9 @NiG\}ZmBə$ *6G} |2PutBȃFԃ0pcM_Ce0تD6R?aK?$=m \BO8E k~O7SP3?, ??%sOA,jטDgK=lt487gQAc?LWbiEiC,`1?d~ ?䄆NY E Ee B̹ǰ G&|Q_OX걀G1؏h6Uukc?T{gdcb?v@-9O_F copqY!'PI ,dS@"hSDeBi ԗ_) Dw @ $!FJptHQmĀ#k pIx3T.?=L8R P1"@z5j/@B\V /_ @ -_eH,<0V4H_{N q<T8LX "#lD PLmm(~П$0PGB:7K p .X <9-iCXD *kv"\?h@4zP0)G W:Qr(P64ƚh2<ɲ .w@@O:adIvS_]lpK /BW-22Hl|ƊYQ濤}snzƬ`ƳpOP`0=nGM =ΎĘY?%Ɖk1xaDZ? xs ?= &#G*-JYsD2WGRI4˩:)VdҪfd(H '};̉3IJG* 1.a?.+;#_$-b!C#*H;sn4{ό q@ QzDD*, b'  "ZzO zԂxkk <h'.+#65~1U[o+#?~I @ GEOCv)BXD]J?hRW?k }w? ]Z!'Hi(H@1ۆU !STt,8ki uY) l!Py4vԣaK? 4AP`yzrd?PXB ??m) *Y ?:X-[Wtu JG6tizt&G5 P[F8eigZ'K_?*4Gl1\V 95GNS!Cc` @2fagt WƮf}ňt`?h$8p%A%QG@_K@ŞHBOϑVz?}@ϐ t<":wI g$sӻ_Cnς d(W\xW8=GQjXWP@{z9`?%\V 9CBϗhC&G-GRgQ7sR#%P(-%xaS)4a?BOJeNCEDbu\~בV/'QS,^iqY!'P#g'YhzN3 | J=ܔ";'d4̈ )DՆ(si36@B6/4u$;Ji42q ?%O\V 9ԢՄ[Ω.["]uB~yp6B*폯m *XzHhDDzRd=#8GN9#tW2XO]Fc_\V 9Ug6 ,"]BXWIcpCE ]GPWꟿQ'q?L C6PQ08H(@?!d9?Uύ:r}̏qa_t_>eC@XT_YTY`1y00mo* N1  P#CNX0!"$`?7?iD2P?&."v1?qY!'NWe@@y8V7ԾQ-ES@0AN H8UǣPWod=X&US$ D im@}m__G@k2gF qY!'A؏E_,J r9z;NR?';u%]q>GlzP`s?g&ˎL?O͏a BO ?edZX)\ AT&m1|?Dz/<oCo\V 9'E0 (0,&t欭 "8oln^L@vp $6ƚG@v)HNJ  sE&iY@O=dTStΩGFʷAOP$uY $G) 88o<h=] 1p"zՌ=. Ĝxx ??91H+_x2hKt11BS_KĘoBej ;jCC4'%#)f ʞ/9  ?9AfO-)?A@P6H~yppx =beB(6@ RO`=x^( 3C]Di_/v!@IESBOϩ?kQ"wCG-;D?'pPbS| @Y͚ХlmcOHx^rz#Wx1.1BOϙh-kn2jZ$ɟFR ,dtXG邃eaO l{jcOp #f?T9[GRROr+Z/`ED}C ?_DܖE EbbOƧ?7 Hql bۤ^d" < @LGNjm=s -0 @_HBK}?uBOϩE @a1P_̂siF:pX 9DID[ِ"{jPW.4@L˰ ' ) Jrk]l_hI.?d@ ODD a6!F=M/o.!ecD,p}N;a#e8eOLc"<@% 0M X ē$X' x-"S1H0; XA/!%2 B KaTህ&<NP8S?Q@U  J:lv0gJhu~ O{׽l$FZ"¬M_ÿA?zre/rV,_e+yp(u (i<$86orn P_)gDXXr ?hRs 99Kq~S={ry@gߚjLÿA?FBO6 Nolx?_Z -{ԩ/_~ D(6)DwecӖaX(>CA4;\1p?.+Dqi-t-5a' ?O!cߛSXm==P+KE$cMCf 1? ?_j װf#R'. ߍWPIsF/c mVO=xں18޿ `\FNn{Sˈ t ?.+l*ZӎZG #DqAd30ZEaʂfP=L0(h3@bև- jB@f31#C J'0 tAWsJH Gbn ?NWsR `53z8`)X`;,Kh3ֶDŽ 3M!ߚHl;3Ԉ.#.L?naN|K}H_ ,(ʠ9k]zj2wo?6UE=Az3ғڠϴ =l_b1{?-Ɨ?D%5 #W Kӿss1A8N%(hxmL ڿPKDX%FMbDܤmR/q~Qr;>O  XWmeaec?3m,l|Nl]\P/.+чP0 ` '/ih+gGmKR! ݑ)葀0#ҦV8rupD T+|3}5c}rC ??03/cǣw3i iD7whQxl@DJTs'Bz=shUxv(KQ@]F E BOdJnZI-<tT} AHw7%qO?uPQ&ȟ$u#Q_]B9͏a*v?+'<!?*'#zډpF3mA0 6 `*ǥfU@@+!w@xI eKGVP@ϝ6Xpp/.+SG[mW/OG#YpQ9 GgNF 'KFlTDݴu@B|"=`ZP\?3_t qY!'$DPHm&N0pbqB,DqI© "g`BFX/bD6g>!ȶX"NA eY=/p4HfR1#򟾖P` a?.+RqdQЬs5Lah' u=N "D0ugv04.& ,ƚ\`=AA6p!B@ǝ8[j|%`^w1Xcs s*'JH}}`( H 0tD4#.ӿ5Bv uV$Ӊ0F.Tkci3 ײ*da/23~̤eGWLTk +^d? BO?S-⿚E!%\f/]p_R#}Iq1o>/ppO'' r3B{=Pb`,?gsYT>C`,Ԣ`1G%w 85]@£"TQDPPYSp1Cү6WOs$rT52~#?uqyOCcB ??e@- Y8h`7||Dm2:7Zϩ )=fǚHc:O60YB00ڿg2޳ VcFZ:0h\?.+ȗ/@ B-=&yշ2ciVS¬ PT241e0 FJC@)m$a&Yȃ*`X&""" {R2ˆU \V 9G?A*C;S?_GAwѿ5cD 8?< ;?lcGa5_#o'O$7b d-CZ!'P# kWhB,Tш?ENHgќ}?A\c7t|_Bw WaȮ-0@<#uF/wS_FnIeRƚa_Fc X @! mM5G}JT:|z($8 ?{,? ] U\DO-+Z ũ;shA rU _ӳd7:AڿZ 99THf}C?vbdM*J8 l`65c"&W @w&-=h6@z?JgL="Y )=HI1K #Љ Nֿ7K},Cp'+׭& *2R.5gr 'B,W?@gpSU MSg'/@ Vt&f3TЄN<A7ZHI9࿌ f 洢L6Q!˶~e?.Yo.SF,fSd \9GLpm{쿿K>?( *f$`'2[WOu7`-vlz\lC,(q@(G/H8utCg"TFu;6Wۤ^B%"``PD bzgB9IP̈-d+yG Y4 XԽop/>+/KqSoX@? SU6a7.? Wɜ]JT4bS?j% =a#pRpkhO2LP_]B\uO/sļ ?\V 95OʵͣF< `1&-LԾ!*t@F̚0'`dt' =ɭhm=]96ԅlFʎnAug&s.s^d_eCdNas'oȢ%)=ghziڿJy>y_ڴ?ZZ.h?X,pi4E{ؚ/9n4>徊HS2j8-Ӿ~BOϩ?+ijCj,lAD:j3?r3R@Q8Q?(4`e02R ,UR@b+$(u_?2Qc-eâ/1?eŻڡE+6$-EFLBR;;w#b@BǜmJ*#50@dORfPK(ڰ`㗽|W7V}nS#v훔rEc}4ddk2:ϡֻ~J5][?nMp=}[Z*;\UWI/QSuw~%Տ_vr]>NfN>ξ2zWǚ޵7}k2<98sX23(ѧV嗗&=bSdxu?kf2 빶Pvxǂg߸wrWgM'9}U?oX M}o6~z#  ՝ij=s@kG:tysvZ-y.ӰNIz_f}VW',,^}W}7et~|R]nwU Un|Q%'4emGn5踅W~ؾhn}:>1?f?3f4zWnWn~tq7>C7vJ_/>qNuj7R+?q.D?7cjNz7/#߷.{K_#ݾ+;wKgNj}U}Wٗw]qj~ou}yKr}캽ȜO9f XCx~rdJ tk0/ouunS{>{vVKsɕmVgdhmjURm/sǽү_ev Sʿt5Wz;8i5NO7+߼CIEXM^Y!mQZrovg}upQ}`n?rdH;:/;eݱ2.)>a"boF̼:홧Wc zdw2 CÿOv/w jd-{j#TX]}== gBo\&qwxLoJXw?|ۥWkpךsvlڵE6|bںV/ncG6kp6v^r#G>V ҞޯݝiMe;]gʟoT~Hڔr foזזz烙szC2yw~xOe WxÏڰG=+W_gQ9E_|j;%ڹcK>3ˍͿE^U}Ƶ]gǏy5O,U_1V)J=J買O~챩}dk|d U3+Ӫyːkn)0°o7ΜZZJVZ7BxV{87W*3mCgn>V3S:9nܦa'k8g1T_33uQƽw;)ʿ7wdkiv՜pZG]5X[&7=X6y6B7W|w$?8U=-yWцNX <{AE32'ƻNmË/2~ڛ?qkvu߆{>ѫqK|Ckճ ^fO.IfZ՝bUͽaoG*mG5?r' )72O^ygSw'ײv_EȦy =hפdK(p}׏nCCkP ?}߅diScʬ&2lɼw䛾O_V3s_e.~?.]royuΙ2ڝs_sZ۽K{_Zm~& /TKҒo^oکc7pF{8ud8gڱ'Nmw㶍/wӼȆF=nMZ=?:hO}_wĂˎ[bզvo9 +>p7_헯'(V' UuלN=jU+H4SWL*2vTj݌NybOjs?o_ܸ2;]ojЁRua%u?+cwo}xU{Wi/xm>hTџۥ,Хd<s(sU~T] |7/ڔ뎣.WuK \K7}O/l۹V|فW Yg:}W2{G_z'z.]xmSkˣ(yƣۖ[ce6*c/&ǟmyaѲ) R] xM&K{S ˊ;|t>wD{^Xf{;3Iu(ڳܫ{jXzȆ˷R#gkQ;9Ĩ?מbЩ4׷%|2E7/ʴZ-n2vfϪ:)4s?ccթWꕥdoQߺ;k bwGw8l?~S`kM`ԉ/-^tۿQ3J5lɛlv?nSw;tpߞ6dJ~jrǾjɻ1v7;jmAgvMk^ѢW\_On3q*_Ut'4+n;=*ݡnº:vW;W'7Q&{쵝=6ӄ֫/')ֺBN_}2yy7byy;w:r٥_lֽR~G.umS :^Y' >w*_zmʇvnCj\wÆԼwu5ߓo~r߈cTmk=wEe/uxmˑ7gG߮]\Xwxmksa_oZׇyg{yGx~ON'c>KNg@JQz%S{O:䭎?|Շ;WX2뱵S)yg_!{6+n^Q_-S>:0宗 ]m̕}gvoݪ]e4x5 ~O_tSy{ZYվvzoSN{_X]w78kK͗{ܿȟOlk̀mastU 汵>'u\uՂtw5Ej|] y6y3,s/u"=_Ԭ<>NEzjtxZo<Зޭ٩ڷgo=Oj>8>e VdnS-{/WM{w25٩o?Q^`82ꇕ멿yFFmߗz=i~W~ҬE3^z W/8ߴW_?'&:8/i}ퟛ9OVꝴ,oyiyKߵs\t˻hY=s5հj?wY>9zj/.~+);zkV}Mi2CU=>F◞=F՗_,S'?_+~QG3Ҫ?Hz%fDY_忳Aw?kIR?2v?Uhef .DCjs<96+lNڠ@bFz/}o<~zŃ2_u~KvP?qX',٩ txmo:n8h⁅{)kگ_'MԮvݾ Yyd1:p/Wkڸ=)uWl4VFZ&>퓒o4"n-rVݒ^ZM{vC-=w\y워;6]]pVO}?-]yjKwXRi[/Pv+N8pۥck_7~-_ؗ͟ʯ*wCxeFӖ_[͗{֨KGtYp3NwuZm7\u^]4sm֖8yX[^civ ~//}s_O*ջ-TJK^zc/x=?:I ?|sw膌G{}ytXAn ^p7:s}JY?,t}ÌBopB_Zul=|<OZ\(p7:Ѳ}wjG39ϣoh8?;{>>N>6硫[}4Vs؇ /Ȩ2o_1\u{m*tq鱟e.ؿ}J_[9^-J\W/toѷASWz %MyʹḅO;1~5P=쉶ԇ#uO}8mQf7u5{͖.n6;ΨT񶒥 oRܩ(ٰ'/ǯe/dʽ^S HA}_[׷:[?²5.fhQo" Eֲ֕ n7 D*݄H՜J(T~y71MzNi~D`{{UfV*FzjߗvG,~Et.p|H$8$h%&-+*hlAgE})v r c gҕ{@?I!kQ:!*ܭX%v{'@!͉*ؙ<,E  yA߃(UNU 1 eM5oy}9Uʼ>)se+;'"qRe 8Μ 0ZvTNU8=AvQL2.nv{;~lfknwcf[]__D|[ 4~f)@E튇^Sp7 _ǖ_Q,ͧd0G,⬢j)R~:|%ֈ (]WCۋX3?1@,'jiJAe諧? [L:82 Mh*x_֯U>VGSou`nX`IG~4ȳȍqVW(a#x8񻖵ڣ~HϺ7PC:D-+1CzȵkI{KW/ReĽiz ľyAxh3"_-++tOtt!F M`Y.t·9)rv'Yغ)v<#|s  .Wh5Ru YTGiGJOh͋i;Ur4*&%#.E7TpJǒ~7KQcؽ`شVӤWtxSU7d`SG؃iw^D{j<"SQ2 ЊbPIrk&tZO"a="k[s˒-fВddnLC*\\NTL%A߅cC^dl*O[&Y&M8,njʠK۹%˗94;ʕ$EgEدseu>x';Ht,QbH&۔?#@# {hE|T>Z>\ٍI(!< o _yBDQrB&'|!TkzJrk#$,$^mmI+ Me@4mar+a$m!{ Y=F?ob.WF?UcbLO5Mǁ̱^k'}_mj%L/&nQv}Q*I/g"b#` ,ddV^v=1]$@0芛l@|t'Y^b_* )@ٻa $~ESGx1Rtd-?ّfN]t*_+ d_#s4iYFX"58r㏓Tc4VqB U~I, 01R.P-$ž&HJJ~fcM%8LNTq iA\~{-j3ʕH8\d>C{;1E`z֎&IDVrh6$QM62I&9KQ|@c',(+Yfͩ*U5`1 ҎO𽧳$4z# #PB['P~8+I..e9׍#%URJ|Ke$Z﹬ʔ.`˟،sOH+4HDJfW@(փ<w8JVB)ͤX1hInP"O(U͂cf  ܕZe<(KӌǓ)H&v.l\=ҥID[l>AdϲLhq0S kIqUʥÌ}]@|a>L,F(k4cA!@b $nc]4$Um% E\_Ćx*;4!8#8X, Z-bpPk}M;\l[p$ ~~rh3 q#Lg?G\hM~4op2];$?)U[XZV]m}ɴȶΙ:Y_ˉvg\sqX]NAjs9O1&* ^ >Ġrȇ!C-#OV"*w%9].O]-;h=KŷKf'XKXCKABV[]e8lmJ+Zy~Tm8Zu[#Z\aƐa WȪ:[߼*:FZX la֊_¬\(;A?9Q%,؏q~1i`w#ZY?$!F"(65DZ1BMRԿ_d_SIw7r CӇxL1xh%DCWꉈ[&'⭘NCOngaTRh&@7n7oz,*Ec|uEg(~zh=f61]2|4j`P&}M7'<O?&ȭ0Bx+Dʅ~Fe[H@?A@W'q ΁xz;>DjPWpCњ~Jyނi^JSdGM}Qf8sW\%JrtN;8+Ɛ^!y;NSj{X]ǻ_.'71CCKʾ}Y9F|dMvtpiyp#xXnuxǸVLA*u48b<>^t<(]WΗҕihpRYW]ӕ{03ONF*W/~_1-̂ t)wv^_KMˇcCVW[=4}0 34,<E9W18BtOo`Λ. \-u"ݴWYů`ـvXחb?e*`xxe\MFquHzRkևil)yUzXT2Pk-(L|96 j#Ճ4oQ XpcH GBa۶舾xC\_  o-N c`xٜL%,( ІHrXO NP%QW'ˡzVYKJwHmu=*S*{a ?EOP:dy!O;J/٠50-EؖHNitRcn{ۮ/r~δLJ W(`&(Q Zy+c2bQLVm18OtDiU_$Q_UEiKeIP{vAYfh*/;~8mgaJ'nV= E}lb'tP-E1tvԧ|MHVI2dfyٯ!'*.u^zZ=߲&d<,dهVy w)mLH~v ޺QΗ.l{`+zX%Bi Pt%dOQ>m"Ll?`.2_f /-;wORNŤ ><6X]:f f4|,eXq/; ~'C_\#Iw2°N4ZeM=;:xz頕PtF[N!gl3@ҿ2 dxڥ2 ןR=loaC'U3!&#MpG+&Hr1'yFlFjY̯ ~68'~28ݻ?)89kͯOAD%ݏ'23%jOI$[ pH~bN 3I2bߜ0nuSyׯ_\ur%)rmrr% 6Pl'a7o쇚X{&-_%ǞWnΠ]#'sϋ0QjJBΖZ٢+=Aцs6U,Oj2o*/Gh16M2sUˊ;Wo.>ū,]s {)>=-%׳2JmefOȋbŲ u疗yT[9=(On(8j 3^1DnI򟦋8+ooǡ6xUHEy0AxGd%ʇ^HVyICEQ91Q|f/ @Aݼ}أD>3Mjk!A DX uoEfNiTdVKz`憅8n؇:YĕGXm T|Ms//Si,Cdu蒋1;ʭtDsqJIՀ:]/p0,|^O;4:@ Gẍ8wZkK#tuϐ7LFvHV b bcN&v5{g̮N_Jb`#ebxZyb!PC V>s+F\ؼ*3iEe˻S!Q:Jez>9 cߥF¾tZzһw"\AmrˢfD⥚#t˚R9=EP.%p^Kլ*贜@$TNkn } Hl֭F hVK*=uYFbu.& UKԆT> U6^!Yְ&\nn=+W"z*0͈!Q o>üj/.f%v/onVVDh zxѢM1)fUiނ9**i%[ܷށ+ZzU &oH$?1B&~Ӈ,/~*#W@k}{Tc|*@^%T{L}Q,Seqa>+} ћ@Zz<̼qU03DѲ F{~Jڰ*+Oă40zFOeR_]+Ť:nᗡoPu>bBwUWghh\W?RfvHoj~u++,TEI٪Zu%ŕns;/Ś@WV~]eVvM<<ϻ"xnq)W(g,-8~}X\QWP0 "jg%~1O~uXLѹ˨B2o3$gD{O7Ui>*ҍ>|m| |=\ǡO)|]9J<鎍*^Ǔ\dwwǔ[r4?V\mhIc'B1u˕>윞+_ߠP,nޚE|:d7kUy3cS,@Wd@__{dPb>,FQ1h@9'lL@=i;A; ڣl=ղtB&孓Dae!ɟ[ {_^p7@]ؒqS=BjO$i+eƹ9λ?/Bg#(z;c +}N^PŮ8%v1CӤ{CЗwcA(9)I/ ?z/z;:G}jç?bjg}G@ݺ8l*LJLoj~&0|,}YVyIe Rnf4=&BwHc=Z>aO8oOfedi,E͛>0˸shދp[$#ٙDVy01HxNᷚe/#?KUK.K4Hgl]Q{~(:c1l?4kq~q3E]Yj_WA-_kE Ħ@lEQʍ{-Ko"ҤM"TZǻqXa~+ h_Y_y䂗g5⋘ W|C(>s)h?~}! Aq8Rpxwb61sLiXXwc:E +{zAAg(|/5 /)ƗMj6Q~m*;ԥ2qԑwԛBoa!f:;!΢.RNg)%C`?.@aje;4楉c C:1]$f0_ƥ8\Lw3ֲ=1`. Vl"ڿQ̽~x#*: ̝'Y/ AhOBw1)}a&ro a37"xhTRA&B@ 5b^Ɖ/H^S |dD l%u PbAHnj; +.IޮS? f3F(Tl3F7ubDy)WRDq3A3AN4ԅYԷf7Mm׺ |E /?W>y+LnK9)ؠ},0)vN3N,ݨ:}pT]#~N -Oڒm)lS_F(VH Im \C;>_\.u =Gtuk-BRd4a0ȚQ_WfePG{i\þ N -Nia ETT@3ae)$ 6Ya"!@_O\ @e)qsڼߏ!"[Z"-c\EۅʖK o0M_W-Lg^8p-ظ _*dJ}E1?E4[ Xp:Ng\6.ۥA87O/1oGw{[qn鉜t$p- +4Hʡr/1#k~Fp`Τjij^/m}l n &pdzMz|op7 qH׀6# gN{8OLv& at/)] 2B?@| 2:Oc(waD*}Bi"D-c _( rԧC;zN.GQS|i5d?'Q^9X.)!o6/5& *_#iد苚bG5w=Tgg0^@P/'abYs x Xr5a֪x>",suF +A:M:m6{\}ގj=4Z6jlw0j,p]\9uӦ8ܐ1ZYG,_ћ)bmT\pq 058L<$jт^&J .֣xQ nWyȤvqqz}R\=1P!:S#ALԊAqM0'ٰ=qJ)0h1en6Ƨzl}4WeidˬQ't)R JD:u8Uft/bѺ7-9KLGN*"R_Z rvҮIB%d!g&p*_`@%{;7|KWNOY״7kf ܊]yL z2#^K m g2L_OYJ,JT mD%HV?!1"|!hz0L_-c4 i4ӌi &ԋOs: 8݂ӏs:J:MQić+ a+N484漗T/p x>iq=M,+wG|qR!!cOb0@Mzf8cÜo}<ޞqsHiNSR 16mesn9&1N\ߜ/9! =2L "P(/ mEmoZ$gV,CL;RӸ/0 RG˄n龍,kjhQ^@v-Q㆟^g:Z8;U3x/^|./ 55 ߒ(_u~UgO'_%P۽Wxl|7?/AJ_N/U*3|sR9LW]y-o޼,r•=Dټwti%;{(T'3u9/2ǩH.L[̭ΜsUw*QRQ V|etd†gFp- ׻&Az OIGaIyŹ:MMheJ̼@}͔{W&;7V;3 (7x(+C9 ȽrEhZ7_XxvO\ }OEM,"|L4pW= N>]yt.?Kp,jElq%1DŻQrsjV_}4j4>PC,M_܃Co'aRqWip75QACAggE񐈸:VQ6jT<Ȱ#nmuhsߞPAgWVF< ,:!Kg{"}bDGіgH(VkAn!Ona*uk&H~5{sA~ND+<ɧ"ʉ "u'4OMV#zP ݻNfv{EXjiD$6C,(̩)Q#v:rp|rqtT=n/{F'lV2i٫qB; L,=E̞c"^Nt{r:8mtNiG2 Į6z.G'bj F̳``pö–5퉝Ґ膝_Qk0HƇL!8_ T):uZ/^E3nɺ.o^GL3fja%)oLqhO&Jv'?oD>KB8ԉ k ^j5ϣNyNb.6wiwa/xJnOs;6\ݐV֨z/hF 6U'>G"{`r#Uϭ5b{;z3k9}]^s4v^ÌŒ|ibM{vC(g:R-3Nm~dʽ{C灤8k^@WxGihH2R 'bE_@^.!0h*P)~~}!|߿y! kx]D(MX(V=//pj6m)\Ά&W5ZOcgFtzZHNN $N"hcWvt 1T^Y,<-lmh˙Cz`pK|uKbsB =#!ғr#@pcb$t&o}ϖsp|W( o`˹9?9գұG8Twhs2#kCżlzu~oOep?:(yC8ūEAQrJ.rkGT%p_"?>s:JP "Z. p譪M0n!Q,Vfu}uˏCPYa5e)&"0G1 4Q#LU| 7V쌚K; m*U>^HNc|P[SG{_s;*2깕t=NV#ˀ;o͝: 9tїyv/ڀovNw":qBeSN?*ЉC N(wu+iY*CH4yꑾрN7HtjT+T4g2#eo"Nsz:Gpz"*`>o74cN,m>]l!jW]  8 ),)Ϝѩ?.[YWD C`ڷ # @>խA#LgRk*Xvw~(oHb0N #~fHU7l²72!UFFG]/nY9Ä&cj ?r3®@lq!^SFxf4 VntςTt\VA$<ϻyԒGۺ} g|5U,|&YԓCRjdxAl볡/`>^֤:A!*R~ՆpǪ-$~xD>^-<(wIU4Ё'&8Cꜛ0ŽyyW_8Rxm|zf*%Ah]ܗyOY\Y.~.|C)\f,F^]?! /Lu+ۋE+o"jJpɃ Ć|=qԭ5VKpsk7>+_閥Nl$WugW\v#ImJ|?[5UFwx7qĎ&͛ T";S\UG7{@ŕa{s9ӶW1]wqi@ieWpi\Dk{R{mܧPvÄ~- e#@ܿ{ڊ+/(Y<Ώ]~D,}`?5R[ᄟ sFoǍ9[K@ǦxѬ*U3xp=^ً<|A^6& ; U;z^N>=h+Xm5F-uHABzxC*O9atH i 6m}MR-5qMך=L!Vt𜥞=c}d3$ i(7Q^ hш4UmEmT\U ^J62_Ru9Mj< U׉Dl਺=;6ޞnցcmo:,*ⓞYY>]_ɳ?GY=*dRDA8%*Ғ<DR/JWڳuy? 6S9U9BH2Jt!4yǒNx ^|g\Z5*IKoy ȥ wF+,&^-&[[+T~vU&fZEέ _7B;JPiUr+pC|I h xYvu7*,*}J4P/5}` pvTsXMK;,"|M"\sčzJ8^LЋС^r@3h&HIX'S WpIt^]7=#u?a0Bmo5 CӜwȺ&RS7yNֲ䯭j+~_vHN b+S{M+ϯb/}pN>J\]SA|F:8@K !6O}VE1 W%~x⿡Z#Y?qqޖ Uwoe("0XbVb(5((*43 RYZfZi[ifeuO.Y ^˹Yz~{QιϾ^{]:| cIE}M(νPjPl>ę[Ssi+mq?m:.e@&~_[9Teyn*{^'bDވ}Nz|7R >]=p#quB.#(ԛ9}xrOrq0Dfլ7;& ô=b+c-Snx F>?xF{cӾauքa_Q0)zp $>ϰȺZdRI[-qhX?"]W,틐gObSi U~PNRnUԵofUq&W).TgK~<C dݔl]bYI`j TOxku| 8kvLK-g8՟$XßqiX#>\^9?jmA3 EA3~UQ0u5$:ܓTſ OB&~ |:p ڒ5Փt$վ [;JD4z*t±z}Sm@"t= Г`B NLS87adr년qZRU׫βJ~`|tE̽[~44_ urm+E=z颵IMՎTj΃ ak0ɗovrI-Qo.ڐ!ETnN/C\4#DnБN&7 vj/=x >uW0 OZx+/.ChKwQA}z&Z"SӭgD$J=.﫤3"[a4-`w+fUn Fa?}fEwß37H[zH|$8zқN%"_dycoEL{3u{U.x,ϧ$Yߥi(Cl%lJɐ MHRecx K]zz)EAQo"(G6#HeTi>}4yZ"8|M]#w D=uO 7ЋmLjAdօB| 8mHykmH(*3Q<WʇMe錧biEcUvhtjFQ% 퐑-p" GCjUB.U⥧sr>%:ukkO@t^Hn߰1Q^PC"} ;b|Y +8Zq"Ƹ#H<; gi+wckKİ|HѨP |Әso!>^x5_T3֎(>84v^15B@ _|~Yix = ٵ[.^P";0%9=HlP'#]j%Ycj"Zq zŪ)3GbVTFm3Mfldd1FE9aȝގd8lvXax0 G⮮W^7ҵUN   5u"ˋPԣ/ F/llP-)PVx\UƅW6jU?Eu(h'6{5}RZwZ+ U(ɓ0Ͼ%=`^b.YHwA^E)XvjP5 rx m,5V@=fL{WM3aڻqTcTŃYdNo#Z$'snԀ:^Y0I`Lg}Ȑ 8I\!ш mP,&4Th§at{q;{I=w]š#zYݩ~u6'c0` ^㻴D[^)c粄WF(ΟVN`wnÿG\%m3F@mE)BK=V׺ΌjX{qlmCPqф%wѦP,GxYa ]՜5/)KPjڗbDt.1] d"Aoktu-6?ݜ_)p jf)uU$ ['QHbzPȘK ^Xi I4d%AIBNO[b:D]5=z]n}!Ե.53F): ރɫ˃b`|V|SMo][s0B$g'=Od[j5q6>fPnxCzU7~赹##N8\ŋ)\ W fJ ה8=K~-N'D~b+=FF]- (cxLlq(gs;~LٔxN'Nj>U ga.smQ"@E ^K#pmml7hF) H˩i'P~&ٴp5ܦ# `r "}g3lKhR&8'ȁfxj~lf1ir #o( L^SD2WGN!ٗ@yZ􄋽j:? U h:ssU_HSTtxgkء3|GIoHRE(MrXIқC'm<>9$V(yNfQZdc'}:L NwvY,l`~NIbS/I:@YOBsu88G'p{tDĝ]:œ%Wh>`H7SaQ?fB>okdҝ~\^K9MyR剂Ogx!4W剶ڀm!)7KyHZpMٔ' )iC'^r~8C[ ?Pm*C_݆C~hCwٔz32CI^#OP5`49[:SE=ko e [{?S*̴:"ḟD?d>ˇ7VF'Yߡqsq7lߩnO[ot8Nq+C\t@ 794^ к:Waϧ%7C|]n ̟>cjgmuX{}\BcnLy0O<` oB⺺YEj.ҙB,3324q1 11.< edPݝxZɎ۴7RŇxH1oc1 _Cϱ]`vne(Q >nTtɚa8q.g q j:gރj> C;\_B+?cZrj1'nݾC9͓Z#͓ {%ͧȡl~ s+g/ ?"{D6w#=Ǣ,O$Z_|fxQ#)ʆ)Hr.>V'2rM@Cd&̕qjυc v+ `w9DaލNE]dfԇ!UT X~j@Ox6һV < {3ѹz3,A(򸤩=Mh$=rDLoifLcmYP@COo\eR쐆,;tS(DƟQG\xEt3Ig~bi[$a{̅`VEyX`Kއ@ J|y$W9}-~]<;dKM|uteso-?T7$dmZ2'8WLR'vۈL LĦW;2ࣶ(6ļ< ~K|Dvb2yBX6c Nr퍓p0P>FÓT~ؤP_hArnabw"8BdFPW G8I')Ή G G?whp&*}IpK&z_(8?F:9J,^Gʢɕإ&(~/}ϣ]i&-zv][4TqqĥD:'҆&{o'h=Pف f[ V&|qAa c+| 5w3_ xv M#ZN,uiƆ?$ rܟ 7h϶s] xؼ G]B6m-N'ɻI-9$VT.,M i'*hO^䊌Y>u={@!ľC.t:dۭs}? 'vdߣ>ۤ[P4 ۝}?:?oyHbX{}ݠޑN`_kbipA4cwևDXSYX]6dn~+uVUd6: ~LeOB,+ X |)Yp덂le4NTp=)رJx0 IQ$ra2֏`Vd Q@aׄjN\nơYhҚUg$5b>iT;;< ~CT {s>UyE ɰʹYHj_Wubvqϛ ~s/ĠQpp`l)Z;)= u ǝUy4<*;O& V) M8n.Աp Cm¦p9%K0'^$"/<wW~ 2l8k9j;ƒ<(|x8 c鴿yN;.Ŕ{H) ,.;v ABvHQ$ZlH` x#K1a^|7OZ3u_V7);Dٞ|"]=n+Snx+E {;d Xb˙fu/WLmAs˝&PKS (->h<-T$xE}9%ľg*P.Dbl 2!7l9 R%okZ B@l+shi|&KX2Эx!X#0D;F;$͖-f8qѵ["z1/:(oIuszH( ǐQeM*)ueڀ[{Z@@cX_}_ħj]-X El@Fmp&Oa}j ˕6O!]` ۳IsZb~hYZ7mC`j~1Tg ^wZL佤`^yO0@ޗA{VKZj)O" J)ީc Ihuo4ژj'o݋[$\X Z{la9 Eˬj{Rtd-w ՇW[ ߺ1M> ՍjQ㫛ZOn=vx=oVy]v^{OuXdw7p{8SoRVLl |<"DI)2b-ڌ#ς>`+X<{q?]G_,p ]XpMP#9"T2oa #n2$0gy!syMהx W;"v+:S4*Ne$X}:n+7s܌ ThL-2=<,!t(!m!֓,bzETw֓,Y2Bj} ! cנj,ēOBy4.DuSc~sPy ۞ ϓӭ7^4[![MZ6u!S1CDiюb,nL!SsTf X.aa~]*= pZO>$%O7GI<]滉aZE4`5 4)jM(S eS[c4䲉|2D=|fL35 =Ǹўq"ܭ Ѭ̆u . / e *dgB'6nź`2o&z쥦+ICЏH;KMϋk}XH,a4 !5?v P{Fc6R- `25v ,vA)z"ѽ0VrVڿc5r8-onxgfWOMA$]Ħ`ZufKVwճ1nVo!.mEoj@8W i#K7gNߢXqjANПu%> nzq[@&aK5jzZpk^M׀ɡna#ָ w+*FJqHB *Rx~Q,t?Zހ;fP9{:h?2Um=@8 7#Eϲ]?-Atlڵ;ɏŋҶ|6nX-|$ϘlK^S :h'KPJvGZץ$);4z#Uʛ)[44I>(i|=}!)3QX2 Iaf̚Fܬt+dͿ]qI ̋\j;deݹ?bwhs?71w\izΝ1wx5}:+u|-o[bAŝ8AӚ+z7@W؂j2}ڍb-A_8םrX𸞿ܩ˿P_=uIIAn=`K IɏA7%){2i֣@9'3S<ߘKi1m%t"*xj;7Yk1#V"1O1/߻# y҃b t{!|}y=4[E:ꑫTMކw9?s )۷b?xi.(sz˯ NJ9P!>V=Ȼ@ZfEbhHҲ/ G)Х70x #5DQ3> 7zEu2yH~LX,i"Ux{Wj(7* =z)%{7b]؂9tq&`⁕dv[1QeG$w8h^h :E$>-F[ _;V(k@[6K̚UfVQ@쯒^xYf"닊:Fw-RHfE:`s#ڟfNba`oġFon }6ԯ =oW}v$kh?gk $nؖW9Nu"9(,\^,FD|K?^D59'vN!KIᔤ3OIgs|Թ9~9\Rk[7#kt?WճC}Èd)CMHͶe1;CٌhXRe3bRFwW懧$lAde([wd([GlAvwnA#M‹эM'ksFA &CI|;ߎ&TmA<e~S@KȞAv *GC'"u mhvfo;;w6ĊEqi|%=HϠ˗Gu;M-IC}.{ڴVG2ai$w@,n6\ @tnf9Ct wBMa'RiW0 zgT}u 魱$/>Y(<ֽ3^]-zn"\E-rK~[/tJ)|ݿ˜[ fND׬*uRBKls\ek%XKu{å` ۖi Nu"b-uW]jcI)hSsڏ; .X=06E pf[&@h;bz#NP1BȟGjm2dy9|d ^IdtnȌN$2:1Xv@F'(Ys&cdQ|T+6~ʻx,9ά = @2*l,(j@W ]yQ@g<);a~1xSƟ(eawgoOo $m+H=W!_"HZ*}Qb2?VY@8If:OKM%$Yzh9LN'Qi ¤Ɏ:Vd@ %w#(n5Kx37݉dObI_)5rH<3Dh37f2wAXlN QuסWX De /cl Xdג}0/Gw݇bQ^E(ojٞմ\MGU"_[ tt'G8dԺަ= f |L0צY>P<)0]Z'DMqyPX|;Dל َܵo1&[ &<v{lSoݪr]q*cHzk?ɟߡ>#C >}4Do膄.4VZ[P4vyku+l@ Y0 ?i3/-~zFtIzo:u<(*I+dQ&rāz7z_d%'rqq=KOaZ\G{7o9V/oVW&$?ғN z:cd dmlnGs(I| BxL~9']<b;_k_|g$Xm T_80'w,DU))ɍ 1_p(T)ت4[IG,NbwQ\7=KmVu:b1)N2PfBi 8i3Th1;af.bb6X=^h"ZGxnLFuqSc8yos?_svNDjK^g'qa܌|&]^B>JTxމ@u|ۏI V4kPGCVG[ e!Nz@%HX7C*Cq K-i_Dyd"o;b3-!$0ZWg4~ZBIpEe8~ PY;Be!nbH-J@'*}@('  vbnWf{+rCb҅=Nnb!%RpiEcoƊ>2FDzRjgQ7;(tx5 5Э:;(M^?vIәNA?*p3Z39t1 vvCE{W4vvTJງY9Sӯ鄲Tr!+:5b^3%"<$dۜt b^Y* xk'tw?ܫ`A}T%/I@0* xE$# }AcQS>HSD y'r.}..s^_ 25.8ϳ/&ޞ(}ͅS$eTlf_9u>ȂA8ܽΣןQfI $3WWo6"f9+$|sm/Aeޠ??{">£XW}£dqi?@r;goEvPQq.M)!c#bZ0N0Pq![*mI>]!<.O#@ { &}; )zg+K>y%sd?WSbl>$*ķ.APR ]7>`P@>abvTGp@|*U<> =zO'{ow; xo0Nٗ/ F^~  h@Ft 꿀A ]qZUUرW*l/kZ'g6V z`J= 6o/y50Ap.h_>jN$b^ "ƫ`PsFa"@FXzl-d)ti%31|!?|C9Rv?yv>пS.(+CB0mx9B1R \(-%HNE>DiQ,Ų_A!p i}I L kJ1<'(aNRD*KJs 9^'j-xR+[V~s@"tmN0ډU3 貀GG'LO2=7iY^ 4A׈O12k7NՠwY YȆF7V@fS0{0I;xlh ?>MgL|> 8<ah٠-lЖ4P!.*JS=p7(DՐ[% UFH?J0LNj z፦[=i hnJ6D뿋csus9ݒ8990qw+qG~=u|ۃS=BѤ"]vqv92"Ag =z. '_geKmȫlqS@|3KFޭG7m֞3Hc-;7/pY-Dr" > f# G.k9y9_ Rn ZJJ^ß=."Gv1UxT T1c[EZ.AO$xT? apzVGkraڴ?C[Vjr58m%:<&-Pov.o)>[6D{ܢ}IwaF",?JJG~zc\pL]C{ ]+#V[%5ȝ"P#7FL̄9@g\#[Qyވ@:v_! FlV$́q 154U/Qz=U P178gm xn.!F\7cۅn-" 'whfn5OXL:|\Xj.3"hUr[ڿSRkK.CwS0IB: 1Kjlq6r<|6:YsVcXem7dkmM'vMl7y&uksaIصJ J+颶 *ޤ_~)wkξj {%NOާ_S<_;ةR¿tu䏿_>Vsk4IJ@fM/6yK8H꾄Mf6(۵(M:.p;qds/EPhYcxIW}x @-,=ua NI:/V3vĹa`G/w9ų!S,F&{f*:ANOQѢi䦉 Sw 4(!U՝nBx`qtQ #/[COG, JA lh Ujf¦qo} _QYP &?V3,Ձrk-fG:#CΎEda6 ?pr$e_^eCTcҦkqpMA Ȕ(g Ot;>HXd(HOJY[vu8,~b#m?`+1=zal喘^^{\iEEYW~P"JKt&bewNJ;t7mHx_xgFN5ǼV~X7hb%V/dR`vۛ,υ,_bE|{rVҨbSx@sm}V.+L~ N!?a $mp31Gm; !ĎXvG W~6[ P2P~AQ%xN鞛ڙSU8 R;@LsfҞ;J%_6YjȔ.0ھF"G7bAF/M+.Q0RNCL&2e3Լ._A2~0d?":Y:#% kԊvQ ]  8^2`7h_|>"]oB[{-cѯAyqaaiÄ`|6rZwkmeKuH?*ʚ8~+Yj9QyTsxdPB8I09|T>bʥ-Ũ8:TD_ͫQ3aZ6 q MkD"a 3N{W"|gu1c-hIEL~.FQ }xx8XQ\kE%ɕ|KT]ްK᱖y-4\4'zJ|մ</AZNj8F%>lMS\Ӫ#WZ:]fd3"́ Wi=%K|@0(lV"JSlGt $u,\JUx`=I$ՀML#ЫNDA'߷?#{kܠ OjrYuŸ2\ N3f{Xܖ[. 7#|QWk齟 vg@w`w@Ь={ ]{ ܻ_ dmX{'o;8tD1e.tA7LoŔIs7d l^ հ)`lʞ!CTBuQCYqCM}m&LLL޿a6UzCZ/<0D 軇D1 ΰؒ fl~AҮzߓNS 'ݱP>^2 $ytB-y4*e{NeBPq졬^ :qif?Z ľkɽf褙#O"C~p܍edrN+ cY =%߅%N#]_゘~X̠!JJ@SV]J.(pRo gH}S@i4L{E<ΔISSTܢu\I%U\1jXboòfad8Xj%:F0 x*y1LnnmI^uټNl+W M_KT9 _ eͤHzP=䣏XX_)ME窬Unơ9̈-)ɥAG.ə#E#ZsLdVʟqۨl-i66h*dNi~ Y)zQbq'QE^W7;=iFj//}F+]ZYHrK)lT:W~ύy):eηx<ӴM([j]]X+}˯VLoKGZ#{K!X:whN1jp \H 9xECxfU'5ۉ5 C6 AeH'Lz Θ~\js-gPeO4n4vja)p,2O)eG)O!Ac4uCRs RjFج ;94/!ReІAxU%%%K~FHx r7flJAv+tB^ {\o"};zw_NuĠ(zM@:}%t@I,٢5UzS dDDvXeH 9]o]V灇Sh\yAS;zJ?jM] )qv_X8e4SI0JJ:K]X%R TYi9T6Eio5ҩeTlٹ1SbFFRGFb)Y9P#7:0iFOFtƒđ!]m|gT~mkxL[l ]3-BaFLwz7UC``9 vvnʹ?gǗ݀'a>LأEc(Fĝ {*GRO|h ɘ#'DDo5o+Ū"RIJ8R])f1."q)mTEEߏ̋uu n`GSTqum\Vc/gp骻Yu~CŘ}bN {A\:J?d|&9S9Fju#k:e C\5 Y:ꧥ~R΀Nx1S(i\4gz'{d Y04ف:Y1=O :? ʱMaKsХs؝Z GCVdAi`_Bhg@E! 3jʏg(ٓY&7<`L7\ ""rr{uQ0!p@u yDfSfjE{ș /_32n9ZPUe7H $ vOd ˳Sc0ٿޜ'yI( Liy^bn-^L;|n Vr{<oǺB7n^{^ <Ɋ$LI.C} $>Вq`0ާKZwnRjͲkWz34)xk'~!޿Sκ0@ (9U`>߻ֱxg?+P[)H4݄?kpBxI~Hg75hQ:Zo=cw/h44|yHkv(Z8z<0w6$+eN#ё5-2-ɣFe'N:rr|e?}u,RŕjudxXp:('^'EI]ɕ;Ejg1E_)ufY=75!5f?Eyr>OQ´I⎱c`OvBrʧ?8%`WV[<9vsK>oЊ0:{繯*+Ѿ_#75w\Ηj!ηNjL[݋ "(-LX=l6dż tvB> = B14h%D{{9tVG(0}sK'1~s;kSVF,͇6i}VTd$ .#Nf7ևPC$8О9~ӱj,>19Ëv)p!ý QKo|[=I?%ص=kg-s늓,$XE݅$ʓq#,^g-'oo1qǫ02ґ䌆P^TD_E#!P TSۑ9 Űl,_{l WC+Z$gBچ+2V[lf&U.E:&xRx[ToC Bt G> R^px!0fg= :2 zğ)u4s -.a~:uyַZUį ;םMmqH.dZadX gRmi]ߪˏ)S_c˯,fL%!#mb /XAxÑ4e_h-PÏ1At(c C͛h1T 6޺U=% RL[*Q.-jMPFPɄ,;ubZy_iZfTzVSYn}eӁZ]HfdDj?aQ݇NW²4O/tJFn5HbBǤBѧЈH.dO $МHl:_m JQd OWr$9A;RcXkAG~0ldFHfj 04ˢ}|c\3(;A4uGvb[~LЊ2 h~Wg㊒ ܈vt#p:R`G^&a-_7ɦCMg:`-yTؚ{Žey_`,e~#?tOX3QE,+cYi`o'3v0mJ +zmPEeNf+0\!s(k=^JI<:1+TY&l*!^ؙl  ! X3aTpȑwkBL(†av?cl >Dc`bTL]1SC'W%Jlhw6N!4GenXr$O ȉ3$xm? SޡJG+&S,(6$Ӝ0S>vK8TC+s!AH65{>'ϯp! xetȠMQikW2~J{#IoxG q2: }'?QJ|E}2A m$ M cW^dU5@O[y>Jh=b*!WwB{?Q8m&N-&] >4?Jow savQ ూ;|gIrvξ G99jSrZ9mcz8q.{=#hvws]m,NNېS{:+p/KJI8֫؝Hf@o)Y >]T-HN?۾^/!j]!o!Wo>6oٛ?L?󶙶KwOw2ώm>"Aw;)eI)!&Uydi< : EcC%GMJt;?cHٯw3 9K3:\6XJ9 v)?whSIQw,99P7]91H%e3$C;0t+Rgd)szji'wMg*Z .d!^c2!g[?a*c]_AARorgH`Х9DAw*hc_lsyCCEb6MqA,kok_#M'z4ie93]r%R{>1;Y4m"D1P]?$@Iϼ).wUW`)ˁݗEa0ӦBP8Qd? Pp@-:WԼonÀq,EqD kЅP*x_'\k^hH?05"坴Ɵ7mAڭg'0B2.wo#]|#v a|C4ʙS.OFan C?*T㜅Z20 RCֺ>WHнns! lgK~GIŭȎw#<>o- D)R<#"뱋MScA~qf TF}v׺Vҁ$@Qxۓ e- ;V8ؚSS^A^ vGߡ:o9zs<͠_ݛ2qV_e=dwuEp+Jn+/Ϳt/o#{Wķ";Άuht.Q1FzFa#>ԤսQ*<1uc|/~ \4R=?zGvZO'F@FT+犫'/0Qp4u`X6il5w82&syZ(KOjk=>{Rݺ<Ai'#C̺E{)!aspS|xȺlq@Pz~KW w-5L4fDp:Ţge% Y(e9~j%߆TKy@H3򕍬'1H3|]q)B1WQQu0d)\^:fÔl ^'rJU}<*pK+wʇO4V-\QxFs ݤ|'J~Io3g "W,<- lKmckw-Ug;*WO.)J-zg-M@M=i˯$q/m5tK=Cq7e(a-z 4<8hrMݐСGmƇ̐"Yr^Bbᥩ';&}Q6ga-OmF" LX~X+*+7]Pw9rx!`4H)෴iV/IΆ,4_ ;_t4Xx7bkŨ2U-`Ooo]I*YU[uR9 YxYS" !M~d4 }(73.WԈW6dglI-O?av8)wUhk~ +Y"Ek/솊xfjsӞfx؈! 1j8z`&J} |JZ \rvoq͜wZ[ҶC|㗷XٲQhY=+GV+wlJ OoMн?4~T7VU%gk⟭GE17r mWjYXmxkoDuХ21[9o _J~ >d$2+8/T N3,k`PD:FMW9뮼O\f,r3fDSs ?;͍\ 0 krFHp%L,irCe zԹ!!]dQ$#Zm>5:FwgQ`ꩂ< Yֻ-u+jՓj0[jtWD3zuoK5TaXUi\x63 Lb'qj^uU"AuHKuTWIӜ _x0{q\=8頬p~WiX*o 4hoc_Y4_cf,NGnh1Cc~P>n9*mnƛ6FפΑY++BOBP*~ȔIGS4{6Ba1SD)Z.Z{7lɯ F3l럘=X?zCi95p lo!lhAP6:? l`}i)p5,N 30EzJ_[s ,SrɄ$~{}>ZP|{]Rhay-Er~ј*H1ZaD`?9d(Cp3{/ъT x0r+Lfu]yLRwg|?U*}b*NG?< <^pP>Z§<25 L+Vf VxO%^> hF/J=OҠ>B`6HխQ=qoLݡikF31W}ۗ2VOUw8rt)y}`a垅Dh EZAsٝw\-.KP- %XmL KO|]'0 >N~ K{Exb5= ;FSEG%Sc:;p}Ŏemoݩ;>cb޻1:Kcnz<Ͳ6&0BwCeϒ_SϏȱQ x9SJ 86;/5]A=f[]S  pYFMo; 4( i5yS-8[u}ȝ1o@PNi;FFȮF:m:y|R2,<uSx_ x6j pOR+ [Dh_΂o ?:ͼ 0oͶn$$0:TC(棑p=5v~JUA9"q.zʯetЃ2v3>Ȼ]jt65_+"v3"_"W}v^z^} =i_'Og'7w#x}~Gش!:dӑW$cԡVձdomVecO;<@ aنJ X?kהf:d:Pl-@&DEv[s֜xҷ)-y3:|? :Wuu,E߳4'RqR~LD(14eޅMgd-q'R9}9HULsqv.m84l۸ 5.k֖5)5z2rj(zug1 섻ޫDkZQ]4pM}ʦ?HTQTP̬bQ'_>=Ml%*bۤ~d5)@ n;Il꭯?y7F|)e`2Վk.񜿌vfXP y -B 8Zp@b,o:łh⊵,3.?@sv u#_{Z Ŝ45aV9dt ~`>Oqie&3 naߟٔe_ZsO'4/9]=n&2-j}m~N˘c7qs$Yhkqɟ-#R9aSnTPъ*sӐQ j`׬#S)JNc~c H8` v4? 7:rK¯eQ|QE O5Ϻ}FcBUTFڜiM\w9ˑH:o6?הՄ ʜ֤\qJV#Nw^R> sv˲$Gq˃Wx#g;/5<S@O$`$6a`ibyPiꖦHg3 ]/7G_ꖺgOg8ɛG'?8dX}(<ٱoq9C=z9<x?VJ2ݜ_0/a< tUH?Irw>ЄB pUY21$ni;JD+9F;K- Sx9K2{X<7"cV`OU^Bmr85"WHMD*';'O'+,T{Y@ҴR,8FX=_; ;z>qÒE~!9ޏ'3M y?.k?Zњ>$?J%j&x/.ae[mA;\yӑL9SRCƔ 7pyE8Ozz DicDzi)!3o;iCN`2 NHè]Zy'$|=|Ԓ'5m &'PLb;(}^A;H|(ar{rN60h3jI:Dq~FeL|-oeէD8vP0'?|iէϗm#%>PӇѭܠ7|d#d)|'~)@O?.nDIvp' v 'ϮdNȟ]7 >㓊K_&~LW?6Ie?T3yL홞8lS&i[F'_)Z>#i >A]0S\zy#?=$haȴiYaHO`_Xװ0JobLH KT !:MWq&^PkxRt¾p'6FhP(~-xͅ'\C;ݢ^\/y$엹g ö{­US,FOuH6kIk_:g4C*f'[8VRȤbrI pg&;}޷G)epJ$*L׿epcjRoyUf6UmڨPKdJKG&&k]'[% !=!\at6]3ť RlźFM{Fϳ OhIo:F ~s@nIEyMPkdP9};ir sY)1 vh;'G԰n{( ek:7d،3%Ԇ^6#,P;vV6 V U_¾UL^b#}6H}|"].#GkcB3w繖)_ Dhl)Fޚ !sr~Y1ܠщQ ϘyDرyW:7׎8p◪LL>rZGUX/w{qPd+A]wLyψqiU_켏D=c z03#,w1 "u 1=rHJ̐`MHxPq/n}Sڗ=z] [Y8pZr j~1d4TW@7ӱv!mqqmq`b!̮#3#Jx#?WX)_RPF&8D8]HM ?+?%S·,vWna[2o:>ֶ0[tz3hm<ˤ!gtwyFKWENPeŅrq-UVXWe4}boB-dҾi\Lvu!movzUdH ׌Bst1oˢ4 7MPQ)Jg#pA,:DăS6?I'+`ƩŕG ^n{d~M{57>`A[sޕ ;7FpnjJ+ȗ*p7s8* uHໃ?e2m8JKAoG#o t dgW1Ygl"aX|ԋAe+Ӿ=Bca]FJBqcIԝ1F,3iY ;JvA ySb1_Y"ppR4b vnc6:$\7<1.7Qozf4 ָGL 4z&i: L`9&+Qd,Ik+ c*_Y$2]~@JEVdԓ zk7B'*B~z2x6C3PJeV;ulQ91vveƄ7l*XyAEGCBj`V6\%xŋKb׀ >._:p35 Gy T_TK Mk9 %/VqY5I{U`zV̈N;d~V @jALKpg`DEo.x/m( Rc)RS dp݋8L'A.P7ԝLkTX,FU*e83o1u&Xuh1VZBșMTFI諴[8Pܑ*BM@ȕ'Z&X&Z&Fǝ,ޅ`Ouߒ9vɣtLlPS_5&.A=[A<b[o ߮\sK\)(}^/= 7H.Ǝo?3I_M]H®DD6{4`&}Q/\ E3ڗ!!b#tC1h(׬:i6mTrKz6`A6_;TV xp*<64 bzƏrqᚅP Sl3Hԣr]үlƅN -B!.29 Go&(,_e#( c7#pdG9 s>eg̜ϙ9_0rSްoi/tlliҳЄeRS^a M(|tuUءR/ߑ{bq@KIO_;!8V:]&-n4}GQ\vfLAD0׬P 7`mBsbE~ f! Ŭqs@CYM;3Ra#\y* 6~?H KqeELxf;=$d n*>K^e/[qbg-cvŊ_#<b9iڔ? + 5S"R(@P]j?V "փobPIk3eciI*dHJJL`!#%qM\PH "'be6Cf,sz&IE`y NL]6"OVi1)wm#1H4+s#OmQ!9?i(}k%) e|Y4(|5LJӳ >Q]q EQ0-:Ƣ&xT%,❪E睪S= QP!#[pg48q蚊:*Ȱ]]Ʊ{6Ǿ9ao@qz8Fv~%=X͒>,~VQ?0FtQ$:*8= #k@"!~sj  ehQ; (nSm?v`c\2!>#@<ۨ}!`SGW@l^2g&1h >s DN5F!""k݈K@-#ɘ!ۨeBrΤV_i5Wv5ڪq Zv`jA SfRn8.?pnIγo ;:P#>$( J'p!2[ˏ&%e\ⵥ 8;db'-#Rvljc5 cB[ce1XK6^&oIZn;m^hN:ѾFk ~7k7'1i9v ,e6leo'.;݀yMJ} t O8=(X6:"C|&3?6~rlklp< ddM.Ҿ@D˘<5g:;DۇO!`/4׳(P57ƙtLq 6vpSvW?3GygbP7Mq㷐!ʤfZ<, cvEgX/P3IK>"GU5Y3OIOmuxY$H:g)/@𬣸dh5}O6-HˊAi{*aA?HH3t\}ǝ1LA y(!9 fa|TvT1n?I{GFA(*Fx| lb5>L8<|DGd u4J/~yHeJGEvD-Q𻆻ӟBX/s#?| iJ۝J_zJx/-9 ABV&m%ʗUՔ3#lg1_‰/QMxcQ8Ǭ/ Lw(~E?2W$Ƿ ᨒa h"61L0~W1qȆq-C-5|W$Ym`a'`[P\; g2f]GB%9PTzЬ%;xƾXM_3I~%6]p;0䧚ę-5+-qՕrL]IAʠud:yR~+W PF{;],*2=.X]7`F~^`Lkж&hКw`f)30`"&sZgHf={v.:lP p} ".r%* &'F$KG#0b?VrnN7~ZoLq57mfbMz,Z#,2Y|Kc.2|[ v8c.^QGn o kbf~-hsrew<=1mݠ̇h@z͡}tpF *2k8J#YQihAMDZ_lFC4ԏKm0¸ 5̲^'+j/L)CN%PyQ7?qP-š W7]Y) qI=_ r 'Yaқa!> T1 +l"F2hlM/Vs7MQRDz6sq!Y,Dz7*vVy*GUPʆ\:ރSjN (ĵTZ\ - O7lA` a`<\~%^ޗ#ۅD'%/ P9Smd]b`GrLJ0+E$/2xN.c٥2`c J1<TPWNm*_Pu$`1DzR.F W俐 ?mi7`lXVh?0hg6t0Q{[eyxgy t~0nIJCcl 2<-.t"4:ƜT mhlHIs1߃򺵎f˔Y@/v<`V!3ුQx\3P̌Dέ$zH$$~udncM Ml[ Tg@q+۴Oj\+9%`= gR K#xGB8D'%!IlQ6N JHvK2?@1^Ǖ$-$mPd rѾrp3.>W%WJO0pČ܀δ3Jprq˲-w!Ca6Q+dg%v66w$CKi^CjUyEz2[]h#eXgkcZu} G|9aVKJ?Q<(-ƨqKM iGuoT؄()_̤+|X>"*R2)t |RȼruKᇥ*тB.%H0$raܲ8:Ğ+* c%W@@0i 㐂5nH TzD$.@ cV{I\pe*\ˆF1"G! <~'EEN=5ݚMď(L*nK|a]p؞,mVͫemdu0nG XRhmig/4͉Rn9Ә,'mlLiyZE8cMBc 6钋@0GESVQfH}j︼$lFM{NC\ s`n8&87vr?CI>,0Ǥ|)v.&w9ָ.%ޥ0G:%G4PW HJ>Ikԩq @z0| =R>^;&ݒ<0dѧ<w)ނ݋xXg2~˶buzsq@(SBlaJ q!L(f h>&=]%7$ YO| "= FFQ1!JyLca b)/P4hFcM Eٵ5]/ r9,1/cilQh5I vZOrKK-#0 IEE]X'֠g&f @b|@BWy͟B:ވ  -@h%@܃&Hb9'qJ ee~uP^9'4\:N-o OrcB*ݶ8bYI*p$h{!Ԁ5qrqN'ş}$m%?)/u_`7mGH'-3`+7mN߅ȜW\qZ|z] aL=ܦ  B/`[cYm8@⃸}i޾L(Lo+F6h),8/)Y [[[XA9s`9ǝ'ݙ~ |ӲӋ gYoZH9Q5i] *_tEv64'!Մ]媕IZ.$-)9Λ 5hf3ռ8b%:! r ~"lZF& 3oR/E!# [QUD 05Q@ŬEEѺ[%@n䗌1 ՘!#cǍR̓שҪpnfYZK|jjjmT?)l#=@XJ*TnHc%iyqe> tb1,+%W̯QJ)`WDvJg9} QT畭ZFv _Ou\ٿ\ܒӌ+T%/tGϟߠ%@)ݪRů{gE-AUC%& )} , d?ܭi/B^ع1EЙ=#J(Br_FLнGz, &z \i OsD2HA~uFIar jx)  3H'4g; y1mqA W T[e693T: ?|Ar^0%VRzNKY^!>/;`dm8)k?7>2lF5хrqEE80GE8@[Sy;MJoG˗b~J@6i6ki)5TYcL`dw[12},z)Yp??ON WSk)Z+8V axh2ҲAhLqZ8SfʈV?\dZϹ NxG J-N:ۿRk]86Jd7.sKrɝٔYv>цL)[3}"rKQX?b],y;W]|S9F2WYvI!ܜs}sq4sv  0A<;\|ҙ7/yfN~ʳ9 $$g^> MI!C -^Nt{_鶙b3^yBm$9rI լX)a!~/+g?hʠy6F@+C-,m08~yO\=.TZ8Drx]9>Aʠnxh ";P\^h? 2'a0fg=bO-Bq)Ա.e Wr)aSp(Zpd^q*h$-- pm1ïߒrq2D czA]t?z4"2lBzMq' x<{o}or O؉X[*YQs!%rL$`FA]ӄ0 G ԩRZmѪ`5}jC%` uܱz%I+qÇ/1 LػQ?v^D{DO B[žG 1y} .VOLT\r1َ/.|\|Rb"u)nֱZ4{?8el ڮL6DžiCx?iUɏ{^733I-VѳN=^}Ұ X*n15|coYJ1w\{ GR|((;I w3Mt>95@Xu-UD)ʇ cqAh=>Ar~簄n}݅7Py~ y/cj[|RxU\j1xF8q1 < r,Np0sP `b⚅ lܘ=O83i jg]inY{]-Pj AIedm2B8&rT>}Lk>}1-D C4 1z~'ѣ 5Va2]LDY 1 _p:-|6͇P_Qy5窢q'əhC8#"\7o>GyDU"9D@5pzy@O:@g\3wgW ܌G! 8^f`{ZEܸY9],{hJ ԭ "a0U1>y0^GUlKw՝pz^%?7b\.)C3\cMs#b*W?}׹*q4ć.3yg + $*fȂYXkҺ\Gkl,xODLʻ'WYM}{4^swӂj|<%Av]x0cȀe֋D<'adD I۪c?=q #!uQMl*Gx}Chj+/+Ѕ?_Vgj 2C=1<@G穀3TZ;0UVs뎹fARn X-yg 3~KSK+_;ǖЮoDvO˙9O]<-cZ# (|bѱTX BiUd^Y%>|?~]6jyk_n%~#X)In_ߧ "?E_if5T#37%2}=_;ZK\\%y@Hnn `*yJ򮭝 RZm!4=V #M ⠭'$sD9ЃwgwW*w8Ğ|ql0u! a*)xxw08=1/б_mc<)f+Q |?>ĩ=_M}pm] zqS`AuS ަ8rn6s̞%ZB :[kY(=!Pavb}_q+uXa=RcL+D깟rpzvtO&!܇jEtY/h&mP]Ѧ4=RcCW|ؙ2r7V5男4׵PӨTWMV$8W`!j^տ0{xye8krh㗉Oue86o>Hͮ۷~Q/I A⠃F~ r{uŶ V ص.$凋J굮0ΜzkTa%4ʩ~^3:] guOl q@9]{o~371 J8@4p[7C4r9 Q  = UXjv!qcdѐZ;\'UNhCxL2zk*XX&WE{8ku†Qś LXi8=x{!a^uh}2b1 G1'del1JSgZj(t||݅y*->"ۛW">b7L#si"(l٬,dh%i?+? CQuk:S<~c!^YWJT0uo*&{/\4Y}_e AleKד7ٻΝjp:_,ۿ2uuU^zg Xm C6ڿWCkfg&4 (_̗Dz?{Lq"han9㞹|~@9?Aއ9 3d/އǧ_AD nStP!B"K 9 h*W1vae Fd_ZsmKDVY Gh5?hlQCqn>m<+z\78Vm9=t hmA"f`0,C:7=D *b:^ y|O?&(2{KLK (Ƣ~o@,dRxzg@ PJY~;~r4ȸXg'm24 yZ m^̯3FAKP!Z=:f8]`i5Ą>w>QN$Xì팍]Tn6\ >αq; j  YDLI`O^LAK}5H*gx1ܻ1nJO'2zo҂Qto^ˢ蕌ܷR&!3(w13+LE+l &bΠKnKrwr %o_&`%;b3gG4!35QFN3 Th9d&qU=')2v+:xz/Rٗ6~>)N$\諲$c0MhkLsZ ΏzY\s"1[H; JVH #<2 ?kAɽ݃;@hpܽgRV @ D&#X.{Ck~i†f4eŴL担C<اoVy @g2h&r_V:};{02 v [>|!!@0=RD9/4M8ihG{apw%;Wƶn<| E%Y7y44ψIs*> r ~O\qQ]<-&η<'(r8o'btlGz3Nay !䷒9/%?:P^9ߛfMշpR՛oA~Q4Λ^ԟ]¯G::11BZxI;ǚ߾g4EM7h}Xڝ0 BG@}x4-ᴀ h6Yb+7D#}&j nw.| [#*1-q5N/~EZ#~* lW}J`h[tjh6|IT%0Gy|*͛~8zͧrĕİ|E }|xcbPd!Y_y(̻j9bw)D)WqMdw2խ -fc1U Qr]z*-Ԋ*)&nFz :3&+#i|#@.G -zꅒپ:DJ\)|^fh1J#,: ~W v:DϲO[}i2t<ӾS0,Q$&axe28vI7VGtޤ+g1z4Eha$E赌܉(bu%zjYbStg#ns u̷^b^R1cN Yl?C_><ȦNp-d_JL1:=t$fkom'}/}1XJ.h˯wbV*=g!x¥0ɂ.j+d? ɊgQLJ(!,ۇgri\]bg+sq<ra\! ^ׁNN폠tc8\<0L(5AN`PG=uE +|qnGҹEWS)w Kc.ZoI5-zJ8>X$6OH6 ^UD3G'QOZҭvi9ω*M-:@*o(_u7)&hjOn7hX8‰MK~W KQ'b Q `W9=jM ]tch龗.vaFc*W)`.duM`Sbpz~x+O"6م]Ls`7f-gbL_ƿRg26n6Nl׿R1:}CʠJe\ɥuZ(s <Ϊ4~22y2q`u) =p ۘ<:u=m7U:0hc{URRï#*LZk fg*pw18s#fu'E_ghR\zkmnŢ+ېn\ڣC&0K;z ?zOe^{40ٽ,zLvu /i#Vէqqbu=LiP&pQV"I F)|TEC%!ل--(qkլ,LRliy&L}9yfp_?s>{7>SaSRBJCvyA46%Ɉ,ף%<\f-%jO+1*I `U5c?VeV5TŲ=et&38m ~8D.' ߍC ; >ߩBz15&p6`;\Fsx _1&YUʱW]I>9nEݘs~A^?XH9[_饜vZev,H/zv7޶jhv?$n7'EJ)fq 2~oA pJܿBe}i͚tL$s*-KPFrq +t{KŵmeG~8YD 'W )r 7aNYH/7DPn}sܰݎ70lꞢ)d6v#@ܿcTs^s3X3䱨}ͪCwg rF`A C,-X;[#<{JM#ï~7@-i7z~c%{rf#IU ]#if?pY^ P+i^7#춱gm \<8-܋ u $4j̗dB]&t䀇 cMMaUX vZSARSG_#5oeUFS#hV[<W{[uW=V2&v`7' bA0nz-o6}9dQ! {#!8LLH!g;?QRA+Kpy[Bדw d,ȻwћԹTjV*x=.ڪ@+)kIKQմg3:[gR=Ue4W ;uj.넖k/+f6VWKP@-YFeA E` kJK9&Rw Ag*IAB\VqYG;BnPiH^%O+1 |j|jGHEUBw7Qh!n[q-phAy^ ҜLG3:wrKRWGw enOzܹQ>c<)>>hZss!̪71-1KioQ&&> Hč XK#g9Ix(pZ">q9zݐG/ilK)I(9]) Ä&·y6O/wO)EPm9|VQ3o6l.0rK}tMI9EO4EwzK_6R~I٦ȕ?ªfUt/k<֞Lv7Zzf9Ln}'`cyF_$ /' 6ZXDE촕38*d .w<ܷܭcS9?ŏYP9?[N뽰W \ &T0"6 v8#4˚j&7ͽy.WT26rc"PslnN--,j5k uYWJe-ڗ#;e"bG-=@mi:bpPNϧJWjTթa^1ypeGpQO=b;?nz&̪+<_F1C[z%?+O=鳼W[&װ{O!zr7PtpzaFa,k|2/ސ ҳM>`hO ݤ G< ,$xcE}F^K>`^~Ir2$9*ºPG6QN$*G=Y a$}qBojRG !'D)`:ZF#n)ZfP#Nx͏(CS<ќK$ÍDtWE1#=n;n +8PynyLVUd[G 겆,b;Hcj! C}gz`fwv[l&!^}XQ!}Mpj>ظ6$s=(9Tm7aҪ"ၛ$%]LPۀ:z8zA"^?*Tޖ5xO#{b[&n&0 = oj@ܦƖN ]߲,HmM<" k 6<' љ#<+xdmlz[7 -?#+jOt5D?_Q$T=;t6x\/f\oEjC!=_/ 2Pۃ1&X{M/ nadj_}4A_] b&Q߇ hcL8 P>bWH \k0'+[ME<Ɣ$喱'ط~b_ޛ+:!C?^j֚(\jaH[оf'1|>>;|\#xyIv-N/hxnW`#r$9/&: nH?KKb.mq$)/AkONAr}b 7.,|=@wZVe2@F-_$C~9G쿐ceὣ$:ůzR6MWy`ޙM"wUBhaWD$ׯ^ַ*I:׶2QCP-ʺ' tݬ爢ͬEͳ-TX|}NJ ]yݗƆؐg5vC.'@Qbc8d3& }*J1C ~^ B/6VoK4zLɺOLk im|ٽiPUs}g)!Nٽ3߱{ӌky,QeaO(ʭqZ׳?}5loTkZ-}}&Jw?yއcC 4Oзq;M8yLo&i)N'QE{ =lvU1#zܱ3`Lu$Gh 渣߮[m ZPjpB]ó&@R+%(UR"RcO)uY"j,g4T! SOk2V<!SX= G~M*w4ϰ 2LH b]u[Y J|M[&:]({g4rmT5[قӘ? n iRmNk~ q\`X6m>%6m$U?r,FԳzAMzl~s_]7:/o y@KߨLT!`͎Pj@Ow)ƏjJ>X{һwf@:jI_u (O4:}w1.`hkM*pxgL+ Eroǿ2'Jq/Q+Ӛ舓(A意3nИx7GDp)͘(>(vLO(O-9hܳH&n!K|lnl%g+WhCEI\D$SL݃dZ5.L7aMCDh3Ttt;~dH;l;q\6sU-G&{? &dW7ҭσ.z ;͑)}}qE@ ̨u mxeDfmHC34/ܿa62چOߝE99E9L//>u'وͪ):;C ~^Yh(nZ]pS*bV>T/*%yA.g+ `4FN ikv1[h/Ay,ŃqViC>m}$⽛Q41(WoZ$4'g@|yDXؐZ| /YkzFlr{ޠXƶT >96BO+L6aݨ0QיWD&x41Wd0Ql lb:Ҕ3ZL.-IB 8ʾB㡾=L,ʧ_ǁv ze<}лoN2jL8Qzh(ݺZ'y'l| a7߳h YRrd`ӟ+K歨tF%ZI;8h3DLIg[~d،:CZvJ@!f@09L/ٔ69_`h}"[YcF&VOJ *1@aXiePX WPvqۼUJg[sI0F50Ʋ1c]y |bb "2n U?jk2jhO}ܺ}PKa]YS6첎&B!g?EPv@b_pwew ~4K;v0~Hf5Qg#p&80?SpiPfLEpc31Yʓ'(;YʃV[1T,34M)|}&=yg؉좌!Ax싎U6F9Ž N77ͬnbm`M#?Eȿzy5m$@d5w_ h`v/6^PJ16FK^ BQ]C{p`  9qx!nԔ@|n֛ėP3Jjg `,PMݮ{5V[yG/}2-w׻1:Bg?fUE74Q\szNf8NDI,6 D,2wXї71},я;)D8:D,A,_eY7C 8m>?Ҳ8n5,g,9#ĥL>va5 .aw-~T43o-8v>_vZǿ9Й+8kpX"MM  ciMmިB!x 96y.f]oB.kNpf'ɻc xK;(U05j7K'%t潼g>"C'`^!V_fN0|^x>oY!(n.|hNoSL_HO(X}~[3|_u g?/ǽLMR{5A roR٠Xs/{m~W 4^I#Q`mKHㄜC_̗]}e_LbQu"6k y ;R'Q=҄M) M)~)tH]~{[)?f'kwkuʁ0۾r Xۋr"J@̪ Y>;z8EDo/ٮ,ukv5r"v5d1ʟ矬 jCZ|E_AxZW;^k\Ln(MAWJ}0Hb,kh~ <AhՐW`C  bluk3|o[wJ4ilOc_$fކ_zO  -@ v+'[("0B]MIWp#G(+O#HDWj*0{-bW}C?,u;[{dHu{H+xag rOzkouIܿ~8>891O NRE8WTE{2gÏZս'`TUsL?T/D[wRS@We-f}t ] awE?c&2m3 S3Y>GB"{$ *-Vxh|yCU?7.IAL\dw)Ye*.kRC*J@ :a&@;6?,F y/C+ 6Q,^z(Üw Xrfb쾟.a1d!3L'K8?'Wyj|J: cW`NfGTL>FL_5׮. (=3PJ UVvUrމQop%m L|7vkAS0{`C+o;ϕKq52sN<*R:slǰS*KZ=xnI۸R%= >|m1hmx Jm6"xۄf 3E.T>eRCm_o1-KєO#->yh}^dJdXN eCj󏍱r[U;Ibq řýB R."g 3 Si3+p$tBEl"s|wo:T!|S2ofuKj5߇qwꊭ4߾'Mؿ0 xN%!4YYJ|ꄩ|>J'.znEc"f]D0Ӈjqv"":i?ĕ ~ fA:nNg="bӭ S7@»ڡ_1Άzv*,WN~' `/GI 6> 1^?Ktu%^wyhɇ,A#M|Q6 IhRm0f=<;G.٠Y6;dSn OC\g Mk&YwlYh+}hX"w;O.DKQ/cOLA?#|+6v0jLWpcqmPT+]1'&BD?L;f8f(Cp~!ŭ%=YSٴ\f*NavVMidJ6gJ?RuJAQChDh2FnW`=L(+xaQj>Պ9qAs _]0h4`fpxIU00Dt1Cz kTǞXEl^l6LDzD[4az﫯!ꥰZ~.>y8勳i׾Cu w6_Rvĸ,GAvxvmfUW϶WߎVC-{Ĺ:5u Lc첞Ne =&5YOh .KHD[Nÿ[֤t`" %lh']QWөaM&by4O w[c[evFm7Uq&ȶW*خ&OΟ|/X=-m#>AP?ʩc_0-tal8vc2Iѣe7b%gme lwr$s&kz{ x,Kx˼:PO c%8+ۡ[)Iv($i^ǀZ0f$70 tYƳI_Ac@m>^2hVI_I_;[45 ~~cۓМޱĺq+Mg?P|[Yo}Q,rYUk!KLD$\*PbZo9g4)?&8Oݕ܇[پwܗk.i"Sԙ.yC>چݍg<0!VZD_(PwEERU3AJ҃7:*WbP+.D1TbT' Q7^1ٿYE0٘y`T^<|Vx6X!KeV=A6Ǝ57i6nm8i>5|7k/ > G>`!T ՛>F^_1PpDtpo+0.o.W^;K=C}&zYywR [8.*ަr0at)v RyKm{ fC_e_)豆Qr(EayCvXC;ХcX[nboG{ʝG+Rѣ} kۺN+QP.s/F5e_:XkZS~ Zq xD@HX[v>6%c{6z451;U6Cvhy97Vxh„4h`RhA;Cֲ+6qu*] ]e[mǚ-q/Ps(.~2%@W~^hk9el5@/ LygL&v&c D]iWL ws[ *} =}YÑusj s 85Q`2Sy()8-e$QL WLy~:s\gy|eENܫ 4+ M`BtHiWAf2Wp+owQH#Fx%0,x Ham>۹|SSݘ#v֑Ww@=-#[XW~[u>ڠ%u3S# X`/ѯ@>=Ż6O > 3NtP{ƭ4 g4qRgCچR^"vF6#X~k,NůN, d$xpCތxqm5،x}c3c3}c3*c3*~ CNij pc D)uU5[?lA&܇IFD=>t2oXDA=uJԷ#Y ﴎ >g ~He) VEä>v^=-Z*؇-&p8|ƨƦZI~JE\&VGD0bޯ%6R&Kx_|x{@0`A73lHAhKmA5vŧ%+^mվ_wi|ӎRmɾ{vEL05F vam-;{t_:|Q$J(Y/LV9uŲ$.o eObYZ'5}?es>c8g:LZӳ>z2z;18iGb+fc@y;Dh~:cB,'&=lS  t=%0@(P[B z¬ĺ=l:ݖ&˪@V\-hp^N$N{3@#N8N[]d f,w̾}By3iN}o@~{Å<"ktS&pґo='f;GS xv"'!AؤNu ញ"@?Dk levI $v)S?"k'I?*^N LT2\{|ͭi%__J4lZ-Smqc%F"yW$ow?/5 ^u"!>a aY;uˇ/_&~G,qa*#L\3vbèVz[h,b3?(T <dTrՀ;H-9GgqbG(0?d/wZͩ{iz4Lm|G+D0V+a^jqj"}E՞,=#S?Nķle!9β\ _L꿅H;':mqoinȵLf(3ܓo+myXjliW4 ȩ9[fhq[h8͚ waԟ!.MlTW?wꥰ{O̙jt靨r;~'#`qHx_6J"'8go?,'.Tuvc<ȋSD"l>or-#K/goV0(|t{W JxKl9RxM> 'R6l<{v`F?Cb=cسޢ? ^,/}:ѿVZ50J ^͂ DJׇJ;Khx˃ҧ<˿{pnC0|0Fhi6T=rt^#/d\?m5>2f_V X)~d[Y]i]AYD\D-t=g |>=!Haܗ?_?> vb(Jyqz{{%OY1^M~^M9xD^;¦#M,nQsZc!n쾍ٽ8>И(ka]qr!?о>*q퟾B`C[_B{8u:DiW FCh?&B=~ ũ뙋zj?_(Yw^P'+,ib!Wo[u|ׯlN$!DN2kylJmg-/7{==im_gimBgng~^1 %Hf:&2XpcG=CpyW Emr!?l$LNN!g3^nКŴi>ILbJpsY,%>FG~ QlHDfC&XԸK㵔_ j_!C1,eGVUoe]Z۵905͵q[FKްR=. bz,[Rt(xrG jG pH+^4s~(Rn,tZUpA/)`ύ,42-e) ),K5\>Ips.xT7"e FR)-YKQ^S@bX+ Fј@MD>0=rP~Y=0y⮃2uW1o+nJ, X9g˩Q}= pM7Ky'V2[CI4A74? n&ki{3]4@Ȋ< q+bpʪciYx5`e25px_oB3@̿߀z~1y<eax#0`P۵ JArk*1.6k#;r41noVP$w'گLjusmF1|~sgb?c#M]1|t}QI=ayQ.O \np91cϳ~E#xD7xNmi)blFUx^QU A'VЀFcce6 H+]+m7WTGP3"]pN_#6G"l|ו/Wx r/5m#2ȬnhmyA}$C%L䁯*kx0%،1Xfו{%6OzVE8?ޒ3qS87_߱wY//Y~7Z,1֗Vb3~v矏z-NB˚9/(u<~ްPcWE\ `Q?wYTQ?.|6ZoQlGو 's]'1?$:3S._Eu=w P]UR˓Ofb{#tN<ߖĻ06;1ފFCCk#S o>%b{d,ud#{J5aE\\&6$Y,d>̃| Na'Rgg ⚨ĒNrrwթ\O0[t(.=|>$^IGYU$:f r}zI7=%~熬g;RߴxDQyTs V?{dִ\/H~ּF(Q){:l~LnbQ.xx~Eo.?<+f>F=ݏiΣ]7WU׉KO?U cQ ?ZfTZEi i] enD4^5DH~! ^9r"+> %3c}b^n5cK._t0:A4=ٳiX%ΏhKm<@V~ yGINX*-QNt~H~D~,{KR~X]~ufG[uj:ar>@,JL<V\8a{'#>ǩ݉fgX_DZlFV{F%&q}zVZD{fڠLh*fE# Ѽ.]`q.q^>䏭,yH ;؏ Ȩ-l=܏o }72& n$o6E;[}bd'I!'b|7Iimq{̇"V LKg󛷥YPqTNF/TPTm3@߮Zߎ_Kazߎ+UݪM\?8FPppd7?/ɹ9)8Z QZBl[} 'o!nCOXQ,NNGop^ܩ'w|Rj|*O99Z^,ϙbGԤJeRa\#ݪ Xf<%wiZQ~$Դ<+%$mA0dQ!249E{wh>K˵ShiurvtiY4f&[=-d|λt|C9L켫wd'ջ}/+u]NŘH(ωc)^^>/+M'nihRdQseA~v5xU^ETԠ^iy(fQqd^ӊhBiGPY=!*g_==?`yul@~WC\zFgJ>y;x`R;kGBA'5i@!R:`pJ^ 4 i@ HFQ==;3BfVEQR=oJva~^֎#+`ɲצdëK%kX^`-|A%xbqIX3(MvTp"ŠޖvѝB:G+Ӓ2hC' ^CꛬwDSշ>o~o]5owoy꿇+mutTU^=3*ѿ#{i}aߒkgiV/uBqf& NCHeA@&^]V$1=+>EAa9> ٹnE,xJ)iis5#~n1{ T M]Yp< X@~ZN|A "!3""hGɐP;s? fBO:$<dS3 5HvQ(eZDf/gI+(g: KO/(N:_#CXTW"ֿ1#|(Jո9ԩ<rRs!gkEz]i׽FcQw ߀+3+|&<|BɎM .=?NCDz;wHԬl4g,5M$ ӽ㉌ZMDWqg^(ԨnϨnB5GrX-ttENf:lǔ-_ܢԡѴ4"NT$mNGzP?ZT :haZ{@3AATgD]5>n2xĕQӬ({DC,J)bCbkMcfDyڤRܰ@Q'Lwͼ`W -j1Fz@JghKA 2ˑc+KD#,c]ꖋv\ƤFQZf|5g=t$J^\<^ą`t CC6Cu9}L5\gb,W_]^t$r8̂ߣ5G> eO({^*n-?@ν+W?|KI(K+Garɔf Z䙓F5ըZ9G)2vu%'o볾2Q9 RT=*'yQ> /,ޖDf "}@!GxP%^φןŵH->ƥKqy'ݦ~,[3;R '4-[J0UOˡ~5/$Tr%dRpc*S< =K_D^R&xR(T|[HIa)Ò b쯱>IɽGރ fAe|{I-C AB6e0=dIbz*L+1$xHuZLDg1i|EC$4Ws!@yi0 Wʬ(e46Lܻ1⽅j VL,쁥9&-fBLϘ^ 1}bccb&+ccRb bhJ10Qv7u+.\Y&J~4s~toJ-IԷR}&]_BsL?*=-ߟ}}]}?̮|ϩJɕ~w;BW_QSjdO$j~_LJWF@vR됏# 0)t}Agÿq jT151v4h [4iޕA 4@RBvtɔ+WMFyi_CRO58R{T8V:jEL3N_v%cEo~׋ɟX\c"i(ø.*#G LW6ͧЩYYQ\OԈ)2nipBm M|NU4$Re**[UPOqМK.<3b^_ -FT '})3?o0Eir| J.zs"eyK2J_%8v\_X"cWL+e_;t7TԓP ԼDttIYN`4|6lz&5 GoDt 4%J;+8<ыmC $aT2zDL-,:|7"]}rUU5?\$BlUU?٪ƫ~])^WLƯ~RU3~מTWWM)B=Щ8|]S\"a0F79dǫ8*e^xwtg|Hwq6]aIFUǵڸbV V3F<Ŀ cS%CĽBW[(έԲ4]M<5@&ڋ R=59ZD*_cIӹiBGQ5;zwg~xǿ8{'D.*B\ؘQȳ/]ew_Y4. gri=<-U Ā!Ir:r}+iՁ*k*+c4q{S.y4TUC]5P!R"WM5a:tv-eIh0?)8<^ hh` ^:sg5 O--?K3G/L@z!onfYx ҟc~!WOaYxȮ\|uEQpDݪEDhRE/gqw4;h-9;0 ,.!?'+ Wjz%ět 3"O &gyiX`k~dMKuQTӭOy6eb+oo~8#8QGF_C~;?mZvnq.Ub"zA%{-E'Lz}t̋OGt b1 3]]]`HX3mgϰH.3WީwUc9X{C]B4)b$1&V`Î ^PDbGEEvN=3sSpzGwx[@6x ~kFvc4{#](>tsP?"/_j4<F_njHa?d~~rhg?w|؏0B_R:i_BzbNKބ 3Mb( #$epAHDQ NKI OMnq5(JDAy6G"3/&$!Hb;4Ĉ!E Co/Kc*?8ϕ/pv')mnDJē_ߟb0 Cآ "hܸUD$֗t/6o>$+ /zA6 JpgM0p#8ygE}&:xгĩ7Xo&22|b K/,!MRj=CCX/u;@C R;EFkH.5x)38rfV; Hg7(0Oj1Gl5U ƨ IHDtMw¡ˆcL|7"D 7Qmӷ]<ì߬7|w{ڙ'P*D+! cǘN.RsS(a5 EbV|27 qVB{~2?$¯QkP5aЬ7, Y{?pz0q M4QH8 pby0\]BQ#~%ov`p8<oiPA @؃E"a|dyq?!eOqơEOŁy`@F^NRujlД@: pMP`f/3W Z$VohN`:I$|`Ż$0;TS')|>e~_N`?m@LLT8 jzN2s<[|O`n @xNRai|2gTm R'0@[ӮXp qr0r`nm%(0VXp$ڀx #x >X }x-@$@[$vU"QUHLӖHೞDR H"D@AY;ЮDO$ b@>C :hg, d | m`h@ 5xx O"bD_-UUZnQ[0(/zl,]|y9t3({A-ehKhH>KA sPlfdf6[!]%CG2a{: E*e2S)K1[Q(]]R^ >I+PK \/fKy }\OgPʥ{t#n. T.= 2(ZQ\n(90)&_}0xV7g !N*` > j>2}GY2(]5Ny }F;t퇀Q }.~2|պ+:1i_֍ xwlr- dP,Z9usQ"]`PT !@z1!a<{%F.;0(-c }y or~qȠ$`n }ɔ H/C_'E& xpe+7unp55Nc J̾VdmZ^ >GՆ?0({#⑥r,lG@-P>>j,LEཏ'r/=.jGZPTR{o| N`> 'tm>Aԧۂt#_A”K@'# ]Kn=C>w 0(3Űy-|_),8O.jX- eg+ɮgʆ yr$Y-p˹%?#z ?odxgt Nҕ2T CR*  >Q [n~TAC5 / Grc ']| CԺ'  tŪEu gG2(;_arx8ҷt?iNj2aգqֿw#EP'D3(mHlEym"] A!O@tȿHB~6ʥC"bP!}O![ r2 J>S SdD+x?*Aa`r%LtwQ//W2]yOB" ph#cܾ5 k:LsT|TNrrܫ5^UI}.e0-7rfxRrYeos (g OQ_(waP+p݂B$cWh#24?}~_1(Z C  *^_~xs@?*)O1(WyE|?̀O>?ضze:Q/, 15]RcB@9FOqr5ot9\1Tuon ǷזFɖqSSwd*9 QnjiOQa = *A5==%;ˬ5f\PN#J@^=HH;? ytM deΞىI,DA]mbn>RP[%[r |CSp}w?)L'x, wurԦ?i?sxO116s7v/c!HhJ?g"e cVlRA: >VD~ L[C<{3(\V׈yzLT.O@ :pH . `>w1P'>ЂF(&0 O]vaN&#G~>"߂DopV4ȉ)^PS`+0R6Txn#Y)";YbȍBE#bT@D= Ka 5R MAn5tw#an>f[ZoaLE܁'<צ őWE$^bXS$3ʪj19"*C~yxL:X02!Vyůi!TV_WmRl}S~STwD)J$XP TG pQ R &_(a$,SeC4IVD" :6LupI[ pKo< K!,w5*{y INKH'x)<3r\,FcO!ji+ uiߣhq<59"Y%60qtr2r&8RuW|M dDb:4c]&7,>1>&+1>TXÐֺ'IcqXv16":tPvt%5!Ӑ]YvIšY .)i#u|?LlD{"h;tDՌ;d %S )*ſ V|Y@l8?*EtŴ%G݃_CAN"x CR0"ITEE]ƣ`eL HJdˠ# 8qԛoH!gv1>r葠(cA=RXK"'Jf8;Sv`|wzX0I)$(_ 9y'ů>Pω }7GI*H2,(G/nGܿ,)& ʗ $#2Aqzu`&=?awp@ F:n7]YI8*g(/gZuB"MVPo6֐"U[S@@gS psH!dHrpEstzWڰ 2Μh臿/nCWä!=#O 2}!OĩG*ׁ@[05  7PuxiJϋ56DwLp]ݠOr~eM25YUYML ?jyƾ_v#G6Zk6kG50￷0F(BG#BMEwp3#cX =2+LIH!aς_x xpKP옰(PD (th% 6B O  {6!=$&ȿĝޚ(A$`rwX r\ "!=W+z{G'Ŏ(' (ݛ}$[_wf rp1m޻5ĝRǟw_Awh3y֪=E/FN.Q<渀i^}\_Q;8[o`lO#o3KTV+t=όfk/~v{mpEtKbn{jXa'q~ Eܜ92oIDŽGg8vz룠Z'3h҄/L9B{;Xa=|ێ_~#Kϟ/oQĄCC?ܹT}u !]?oom5ITvxymxP )%l֎8seMeoJ?oe5OV;ݥs#cۥd[ 3'3l~R֛mNv|e&XnzoīÖϻ"m` Q+ sgLΚ.uP̪ #o 6[qwr\$F3A_?\QӂT:ﻲ%M+Km߻,ϝwp&*fkz6NDI=>mNbz'7{Y53ͫ.1C. 1p4vA.g/8(k?/Ӹ'5B۸yv3]d"M٣=%3R%.p$PСE}v9~J bU~")Q;Ϋ}ӺR0K WqHa|I`,%suQӀI)] Ui92-9RXm'dV;ښxSO,B'X%rh4odeY֟7-=tni:ڍ?7[.ZhtN*(:nHӚ sVLZhOpKp.Lc|=2KC]\aא; 5wޮ}>էEUYPR{&揃9V(CVܭ\j-pzH,-O np<|oDt8^A.uQcץ=yߥ©p(ayWك{nu>wzc*Ie3\hvpس&N`?HEh5ySG'ԍt8s\©7^OQ|r{~Px%X8{7t#o什]S=AIx46cv#mw;',lU6ǻ͡sxë.M?{0Aec)+%u4.PZ+KR'72[-[Wz;4ȾUϖ;ծe!|8|iS}/~_;1w}U{֥qu%->2.q UA4~qXͅ>vI1SRKn^6KC?/)tH#'7<~< cɣU ehoi'ߕXM}1ݓ=EBc|Pˁuaf=)&%8۶hKۼplOnk O]JԢW$yIKk nz:g%qnL}=aBr]זUVXr82`1v->xhxʜڒk{M)\۩_v۽|_W('X Jq2NSX~˱Uj\ʱCv6!wwO=Ie>] f#ki sVzBc'3 UUnbX~kaF m7y3x067E%W>9fҐK6{s{8qvZץ,6vԤˀK^MjNc'bdrnd &LQBuon$U0iۀU6L8*{ê۟;[QzMwJg}$qm<~rj4)dϊ\>,[\1['k~WŴ;)\?bSZ//8t7[ w&Б!yJr߽ta1j(kv~wvoY}>l]byxR*]AEU/ uz@MsZn$(MtYL]3kj[Vݨ7nϾԴ\#*o:#̢duO^RE?|nqJTm= ,|I'?G[wr7VŬU^*N3>xsǎ /wCo)( kղc7?Y64[!̃%S37~zR1*i\3eВVWKwfNg~TmVgkITԏu]=Uƭ{0gHբ(}Fi'? o"Ca_N47nYO]n>p ':e%3[ 5#1}Y2Kn,xa|ݛ%7JK:gvrsBe|~Y\ҕ.Լ-L.MR5qy= 3\p۫T6azVd2[l;ΨROiA}oL1*O{{VUF+]fFڤֶiGXp8ˎ0Mr]'yQFlfh`!;v~mn&Z;nm~WkOΚmV|3=M0;b؞./>TE*w>4UV:Ř=iNʢ#--muƽuý;ww^6TyZ mJݳZʎ ^wq('شݔ~E OoeBqUM@ڀ{]z-cMl)=wvKʅ|nv?=^C`̇c);^bxgv{y hmR1C޹nfb/}Z:koUy7=IU||~[ӨX{-7_ؗ]1LHթ s.0D/ׁyOL|͑$/=qM>)W4O0$/icI%i߿u}N̿oc#K#ssKK# S8pՁ21o 9<=s $xbn y8{;3T*is",-lVd gKUI~BiߐpFkEP "=͍I` MP뛃V$Efx,,%lbV< KDP6 Y#A>↋7<.t)ƈD|Cä$:'n /QB<W:$pzxMR8(9tHB FAU1/0#FxWl(EP _>`4>}\\p#őH:!Ԅ FgGH%{:K, y lQ 0O X\Fmw`GidB,$A.N0+]g4,\," \EXR0MWdT"!](` 0ǍQqW"Ơѱ<1ɉ?)GBOẋV7sjV( K5d@Ba@8+ l(K%F$sp \LW&.p` 'TWc̅Vp q=- R "J>_Kܢ`4,͎[psYD%1D`$.P ,t``q2AЪR@O3*(LP$sTgة(A!]8tΟDkgSqiħHr0F8|<< >Hd&dN pDP.}%#~Ph&`[Ƭ -hA"+D-5.4FCk'12aEP)":@٠p00|Ԃp\dw @ 7<"9JXqh9[3ɂ 6蘟8פU"ADdAYP>:yc^xtacWחJuwƆȃI pV}# wUT[+Q-D8 ZEb!A~'{  ` hEӝ74L oinl;^-Կ-?|X]A]ޝϾVs~^aIyy[5p;)KzoV?u >8p Xh7囎ٽZYk͕P^mƓwz)YCmءV0dwڑ䵣$T-dn줲gфJӅw[Y=hwjs7_Q%Z;Iޜgm6":Z|>wym'k]}uSϔږzP\lfU|7K3~QBq\'njTF5u޽ ZCt]wH3ecAs+N6:9SVe16)X9R~@w?U< 31}twTqlu’[A8Sbd{5մYY֟Ψ&y׎v%ZP^uwxŹnC+mkp=w|9mW(g;au}v^p)8TU>5O 0c,KZWzwGZQMV~f.|0xg)i9L?i9u$g2Tv:5E?s[+M47 tbӉ63m/dNvUW\8bKݫ,d9DNEa;|J;S$)?H}פwtl:|w/@:IcFЙ%>dmgT9U~Yد!.)e樸kv6hL{bV٩˕=;10r_F=hYsQcܶ0ygiO{h~긥}K\3﹣.u_ϝpkKfXsf aŠ{[Ď }o%w'-GQt7=o;9NΝos.y̽qúZIэU Qx? |S )s&lkv\,08#>?#sSᅤe?eB|1d-=O*V?6/#}wXq_$u!!̂!pI8AHّB\hRM5ؑQ `<( 4,Z|80j8rD ygiBRN͖ "^$DX"fbًi1fldclnclgO3r?斖7z:OSEpcyx`0Q?0xj͍(QܷBB ;^TGxAn]J#iA-wGCaO Wx <Ȏ 8QG_)`c~PG=`;T 8bGq4%'oOry\4-@t/bS鿁x@fYptk$GTAs[ƊLDl.[: (//(f@n*~9Xx(H6:+NDc5h0B:tE*W6oPܜNx$ `;u=}콆 ~TkG1iDa_&)aNnnx޾Cܰ޾D {(ls>˟t a .d1}}A}L.7?G{7O?D ݘ~$A>!Ļyx1h`$A.HhyԠcxx2}]A@  !1 cǜ >3rx,fīN3/4 {\0$ ^bN.L?# ^zxÙ+p 6\}$*d i>_='3QrY|a zWL34ieb @^U°~+ȺݟϵSj|a%͹ܡWk ]pnUyV!ӆ~m3LKlMs,, 17rš>i֍Q۝zd]~^mV:|¬\Yԩ|Ӗ[/1Ohw;dfnMgݠ[`^rm"ϘKZLPr[5ŷO-*E gNѩ0.zIi61(tGqzTZ~8`Ʊ׎̩JnX>s$ޖik YS"F>'cԊ^1;go.+gM[rϼs9{u{ Rտ1'&/79n5sY xq:X-Yiu'Z(Ez%ngfd~%iZd pOOӒ_)QcgЂhY_RS3IfobѪyٞNQPz;qcmsyw(|Yo\7#nu>k}1ul I9*ъ -bVl^;}EF>Zi)wofXԤGcψ<<չCffIdw_;aieUY)47Z̕.%"uՑ}3:3}SKgma ,_<]U$C#>'1z2ќLU.O8c Dtms!\okX}ZpVw;1q}˲yo`uwn>Bxm}}ke/_Y:}pjmʞQ.<3_XʫWo:#eb@a!(wU!tp`Wm2[*Fo9ݾp|9&yO_ lsFwg܌cGϻ*Ψa-]aX#5*:3tl7ZQ׉[dp]-kŦE}Zi]5謗ã{-~~y>fVVWs/G~)Nj/.783#bӿirS5TVu[v6nR UZ[8=l#iW; P|mμHD_NoS\М,aΤs:/^=KՖ+GUp;%^=u٠"ZPm3sw;geկ3FY]SKm}rjn#/YUBB݇.Jolx`CE/+,gՋ ='<_pױwi9ݴS 'ia*?=&#_5}d}\/ۅC|.ܮEolkwm>I|mJsy7^<2e}=(+oþ$"Wk$LJωuex!zYoڎO>K=T?y㏞/8`-ѻIN8P1Z8ר[e˷}lBN͋N4fA;>7Rӧn$e[*8<;]ƝlW7 ߢWuT+Q{R[?z/}SbTqΨe5mJ$vO[h'=_Ƙ4݈Ԁ϶~'CoU=(Ztmڞ}bHf?Ä"@7onib.6Gs`PZR ~F!bfomk*#@5 Y\ KBZBqt4 *+>z.A d_`xίStb]+ IzbNH( |\*&r/Ft@V7Ɗّ8BؚPBT.'Wfb1ccCk3f1B!"4  R9QX0D0,t l^R#bx&TgE8\:/# M+W&" ؘői=3Cf"-`)0O^LWoO"FxTR ANӐɚT2 )F!Jbq Hv\P>3bAݸv;UlfM,iMH}}1#_aZ0NWC1aDpAM*R=aPFp"֛?? fu%A}1FB'&WWn6p[V2'A@;CQ 8dFBYfxL6!"R9MKJF~@a'M@q?mNa&VI}]dTkB UŽ-Pq&T nb$uId8:qpJ89dmJ`cÍ}<̑H ]]PȦXK~(P1 C8%% [ ٚcUȮ Jj6Ҳ×om <@~0CKQ;`D-[+ G_8J2'F% i`Bhf#xb>44< O(n(lv.yg!ki pDm@<`dvq}Seu &<aOd tjqA.©?n6AHi#` ">j@L @BVfG]L1pY"6zтх?Z;,'DimٝIW?ix.龣Of  iلe~B1܌r!TfiKkA9B^M_iï'`2 dpF̑}ʏDcȠcŎQQhGwZHcO" Ф |p+ qkXt<*`/ p H7h L@ 74b0q$ic:delvߣD[PVq ^$'>tMTJX}طdG3H *@*;ج E^iPMl {.! pGMh/4МZ%zhxa9-B= IRh]d!T'0AR$,Gq"0%MD%C*p (J&d lɉTq4RΉ![*24<6Ƣ8蔉sE(C4[dZKCctq0xTYZPvfT*蝝QXִXW%uq4-eJG8<6I&P&>B/z5qy\!1 }n q } ? `Sp !qi\ēg´FˍZM:UDv;bO!@!cGcG}pz/JmP!@{T_CN(v2_PMaTBo@DFQREl_v5O? 39o&Ff tsm?oL3 M*[y@uu 'lI~M]Hߋ&;Br\ 'PyI4{hR?=ug' HLSGT__߰ FFpѤgGwsw '$|dzR~RzKIl:܀T%yrl+޽&:/]rB$e?_Ϳ?o sryYk?45mӬ۬WN꿿I]|N`~֬,^7* pcWf7O?q}F/ȹ%rxxȤ*K xq9L9fLSWN@b_E,G“ޣ8ABt/I:F~|MYDX p!/S &qo+\~wqtpƄdH1hghjI~ƿ.>Čk~SⓖkPJY/j\/ G3qB[HiS#}\҈_o oȄ؟ݟ-ݎkko@?Ȫ ?cafi?|:B4MBBĈHrJ|{foHb ˋ"ƣ w Y0>lCW/dR |'50h F'AyY}H5,˃%g''7< l*"CWI&?3ffIYlLg72! IIB%Ev䟋?5;"b/.ߟ7!vǛSD? \&LNua[u.MBl͆r̎Z]kֳu Yoy-4⎍c/yY[]l]/^$4 syNA˹c3“28s>ln{&q(m᫳ɌGNCSYPLn"Nzh^+pW-}PÂ79N0tM9jZ,d,r?Sﱙa v?hglg۞̡vw"`|ˎ-xV&K:8hyVco 4+^u3T ǵxewZSHh(@[&T'ԁhm{]>? 7$g3m݌=VVƝc~t_}`X/|9oΞ`8ulSUk_~8aa崾;8dӓ1_#V6W+SUkfyR=/ėUla>dEMZ]>|[;Zc(yeM./{`@]ϓ< ~Mrߢ10:-ܖwn3Ν fĭmWѽ|8jk[{p pIWBǓp6Qץ'h{gKJ?lAgVg|Ϲh)Kg[\ӟ2lO[ѳ"^_iRtckڌvMƅsSyfr{ߜ,lQ~- Qz^Qϐы5(?ln׫[+^pWf(\;.)&q@r㤖^L9=,mW[/ntva).ۓyձoU8 Q(W^~=r~7m _u3eعY)/FJҬW$-(bwXǝ=vR>uizosdȀ1'U2YJO5]V߼1g{궸>q6_je+|hLCG>z#$ڻϬS_ y{yVOgi%ۖx޽?|ްs]@0b.o.1`,:{ hDm`0m5fqVd*z߾Ã͞V?2ZlNkAW_!UAo{c{|s$ð/;(=avw4yU^:8>Ϗz13zΛL+6ڍ?̯o_zSmC{# m۶m۶m۶m۶mdRlߧVeolrXYo=?;hɑ[ť{fDv 7Y *Q⑨V ĒE}iƾNyg^>obj"օsWz̃pTSEm@a2#9ב NdT 44Zm]3pK8<ӮWCI"W $?w \Wb"Vkl ܛf;K/FK$_>,O1PaHnLuGl)u<.{Vmͨa"BrWǷ #C;t{xjݚݤ[+Tts$dS!~A90>thkәӤEDznBo2®N-]RZLgAF߀paPoB䪏 @nøIpOFfjRoeb! 2@A̸PLW] .a ѸŲqEZY64 l򳣠&8Fq jx};frX̶we.`3wChG'^s \ApvgRAgԽUSЃVIof w?F#iQ2K׈~- 6*#[b.G* Fsx'}1hٙ.Jl^ `D_UPFUC]I\t-,gky9*> ۾o鰆ZsnƱ^GǿM`!P!z8 (>ߟvޗ#W |,v|=̽&Kn4:zEԪ8scZmڔ1uhp55d!|=?)CF حޮD3sgwi՘˫ &)AODX:|l#$5t\9뎞y"Q2+TN8bW|͠S?tG:ψ=^w/rkqp784l]_bqY4V~>zo'zOoni4V͏?M~Rx.TÉ.rS<5W[?mIĴ+W63{%ї/\"gurGjˈEƇ6q!~Bd'y0>e[ wgw]3*j&qڀnÝꬃOxyKCD tr}S3~ؕk fmbN:k+/GM%YZ0R\bJD`5{ bj"J!O4p9(+luX썬K%g~w<%M)׍= ~`/cH Sŭf[᭕vrêVٽomyؾ sd?+V̨h\}9~ ny*u:XV&;dt5EgbU: jvJbݽ^t96 C-~gr4nXV(=^SiJmOg8HQTH`@((@ PP.?Z0Pu tpQ6&S5*9/9? phK!L0h<F(`7~E8BSev܈!@! cnJ(CK#I8p0h#+H (@GeJ?@N0!xāf%'cv` yKgЃq4aDKC|6A>e< Gt~L¾C @~4p &/ &W ~]wn'd`p)*\`/Lm e.L韛1cs @ @_# 3+f^~6- y<:2-Ò |W?W"+Bǀl::n@$ɪiڂ-bLпj1=]3!t#B h?mbS^Z #T&0?Q7q+/e|ltݨJ]]Vju]5EIC <W94i9k{rXh#eMzG +'U δGl;cfz)d^7LgZS418UgHζ:G'ڢ!C#->Dc|'P{+|x蹱*о}Ablt=05?yk#l$W^4>Wx,ρNlDbd)ֈk5x (wmJeNߛS7nZ\f,*h%%6ñlT}j&4aB7(Z)5T6Ѵζ&aA_L7Z5]H(3lxFCk;D3)MJ|L.~,:b%n~Ù$|*Z3 4r6X4\ʪ2,$ބ˚!+e ْ0qi͠5EsxɩDB.a9Pr]kkYcGgmu mssUz$ĥAqn˛S~=a,3t*Ѵ{7 X>*j7TrRq]SW$eq l[͘ ;v/KsCOSDsrɪ]7I6kzc$wܷy!6be:cJōX6Zb[l$_t23 T >Mu.c{?ocN+COgny;" $u㱜<b\(HFu~.zդG]|#fTHrUyc)Њ}#A1Fs\V냿[LDPAsOq UduUI! /b!ĉ:Bw5HMdVg{JfَVݳpr+lBx 1gxVoV݆" 3Q^ӁGQ^{}/WwMcӼ@ e.HKF./B4H[ R%~pvTUellmdxoҘ?5+<2N06 #]o-Jm @ү1Au-{!1哪&LBG]Di8&԰u̼zsB~ʶ}T07;;F7ϱ/@-teHEol)Hiiوn=$5KCxt'/Gڌ(ɇ:t{;:tbW7=N]z)a~%YM ? <\a QQldد0Ailo䨼ܿ6UHwaj6o4K|-<"^[<<N72#O'ܜc*W ߝչ lA` Fo<qnnVp{|{i}ap`{ul)=Ƹele+줼n?Wo ڂl{k * abJ9L}^izÀ&׿Nzq-+&4/Ն߆zrS2szᇹuL=—M.m>ulDLNo?ϟs2=ʀw>rOqFɻWaQy!TS>k/*,'͎wO/}f͎wkkarv$WCݯ)! O4}PN,xJqntͷ2%M8 o/8T8sF$^N4ȞDR >՗r^Q Xh&f_8Rʮi4*8(I J6n{za&{YY|V[bNWVv`rnpe yTSPFw3flycbz;7 V.j1]O6цFgoҔWjv1AOпkf4w<%yN9:S&e͛-ﺼb4/v-4$#*_nK cjj1ۙ x\{_[rǡ̓vruN_&R&dDi_fw1[pgǾ5wW 2 S"Sݯ-F {"F?J2mN"ոhjvJoEC٬ZRT;]u_F6qv{~n)RzKUN?]^nm]GAa"Y+h.2z^ulOៗYv>_XQcF[5շ]4ߟ .[hCn^mmOi6q q?Q7`l!' &Ǧbn%3dQk(_P`ܗplk4[S܃Z05V\u\Ԥ=iZ x |S*+*uuʀ@;ssZnmNr43L$, X Eڲ(몍lsRt;yۊdE 1p]t>3Q#'l^]P%Y>o(65D nLw0EkS/K`-I*&eL9xr0V|j֊~tpp[I#ͱ#cyacX7Y>X\LƳ$K9D\Vo9:gooP=t6r}a`k* ń]{Akmΰ\&.j*mfv!bQKg%7Z5槮WʫėSn,h.niHsY[kxZ־cܹ+ъrˎzt܍< կ;^ 7JyG^M/Que{ jKlS|&FI"hZ%BV b?ŕI*nXp)Zu×/PSQAk/s8lHCr}Xꄊ/W8Jz ^>&]6K=־u>O1[y-M3\-FtOi7nψk/uOZ-N?C[Rx`c\&Qۍ'T&|]s.v3Λ=[D2 :wy6$[iPCZmpUpݭV ɉ֚4Ԅsvdoso,do(]//b "?ɆDn_gSF?*0Ģ2Ƃ k䏚:!\*d3@"C C䅂@*MߩtU%kjM5ֆnä~Z.HTЧ_:7_W1t//L\/$2y RRz@C[/od3bDрyE.ڠ)Q)N|zq0˿`mJ|i3^u2Y̷J<ޕߧBRE؅"tw"/-=~!|A| 9ReN=f{ڛO3ur3K@s@SN8«= Kz+dRRB&2 03wKFKMJ"E5_K,*>쓘40y'JF£p@gy#9㣔[y6+xEF{`#n hl 3C;K3<{g˩K!á|ڳ 4-s(72-Î('pYY_-F'ט@ -|)H[ aՍ'r4(sށ_`^OAOBp?j>Y*fT[IR2'=$Nf&7>У#C~|^Uh9˃r ¤#o=D,|y;BwqꇟrrR̪/5iT Di[ٓb69A!|>R&3\8:) q<&; SSfm[2/V/ugIaŽ)FULr& Rv:zX^M+r7 NjӺfMmlp0F~xfNď9e3Z%1c"0Cq(Pg g[pK4P;6 BXXq8;n($H-. 5h:hbŒI F8s)vv)>S'_*垪aq` V||lrG?|:+F: oya-F3W",71>E'Ci+MP`2FStQ зegS(si#|D^o@g%ΈX8â[r;h/d2ZB8 &rbڔL~h7jr2ٔI{wm=B%nV2CNl VdOxtS|= }89:fz*tW'73ٵojI>>**+YRh ؜8ݬ%xSw BaXgp?///^~G--@3`,]Y9jsƛ|/<%G.yn8ƭ\FGh!]M2'L\`֮*qrQp@+8Ek,RVڄn mj_(J-*E/n'c- Tw>VͷȳlWcd#i_-gp_+Ditk-Tx]B96Ykբ `: kfY僥mk8KEe +6eQm8~U&]8GgLP)?Ç̝J_{ɷq8k͙Ff [8x Oހ^li \OoXΤr}>Ʋ@iE i&GHb<@5E 2c8h}ѝuK{ ()S̤Jm~ހ)}9 ցHq򘁕uhE *vч er8Q#~/c]T{jxˍ#ug?K;ne^ŵ+pOpÊ#ug:1'$ tq zQoIk*S2ɼ.EMp;uوhXpχ {8~޶/S{\ ~lob}ӵGdΒKCN~5u_D`vGۺE>}.O0]C 螜 R_7*Yi9/ hFuq&0F$a PD!rB?ß$Ԏ%2D0/VA9jh8]=c1 Nʦ4b+j)᪔}w+ϰ8ǐs[ǚ ;";ʶQ29ü9m=^{*[3gS'&zK8#U2Xi]ZJv1bbɶߊ;9Sy}EWW(D<I"wtIMY@8ix9p(%s9h_a)ŠHqg~c֠@bVmv5uq2%3p'q 7c@8T %>lpǁ07)x/ Y Q.Tº*4wd"?vڤSz1+}(SH,'`uG|ańBDa~[0apíԷ0g%i خ$&t:p>xk^OpopL)s@WZ굶=Wi ,v 7s]vܬ'>j ]Bah؁ 4p` d<5 #Z>Kg{L6oKЧmpR< g{eiUWB1HhAi~J4Gv(-!6{n:9c6H.π=kdqj6[o} }Ba KA~Co#" Ye3wlE8?Ikg0 D{G]~I7vD%OKnx7eQW 8z5e$P",ꌼ ;]z $޳P/`v.M!nSlr;)Zΰ_S Z׼a>XP)- p^Ш>[H׮v>$PqŚW i>AŵxOIn)lEۚ:o]鷲>>d_2C &|/0r-nCВclHg{4'4hCGmaV iGk9zI%T8nRI}fz{]Mw"BX+ kFYh2B\xHRwt4j=[#ct!?(җ@pvzK}pg.4(rc>Th۲bR~A UW6=BD$ݲ45/]fLjh+澅<ERuׁsFx,4c^9>d-&D =FLs5;WG:E~dvDdAY~7Q$mX`Fu%fɱ(PIY#ύ S 4k9}\[Q烔:v7xHE쇁;?YiZ&XQiې03X\ ܣvzlx9lvȨWl#̩g-'JfLjX񥘂B Y::zNb,jS0`1*A/}:8hiWhh3m3χ %fTOe! h#_v]6VP&LLP=oHޠ>MbV{9T\, N=S3zƇ)qnrH_rkll?>ҙ<>W6ڮ/>F?P%?w4>s_9E'צ/=ݑ<6?mC}3BC_"6T Ѿ)91W!x=2>gzهkccTd\;z}lO-MwN@1g;袢yHSx{|~_]y N FLO}FR/w󣃃uѡ7l=?X+iF[`4״nԅh]ˊSG^-ZgU($uO\(ӭ5 b;(>7t>\x`0bϹ۱uV¶v+C%0MP86ĘcDM2j:QnZK5M](ny*rB7sؑ"&ʼn+ޔ_k.K97?n'FswȂ3t&w)ep- 6MvƐ{VƳ' ^Xp>Տ O So"rwtlTnQB?Y|T&B4S(4ڗ ^VUNa8 6<ݩ a k@nӫ--~"z*Sm?W=igزAɞMNF"LzPW%!Q2(0b[u7Fdd>FZZa͗{:+ZdtS'H\oV/>mk襹jcxb&#};+|Ȝ`]i/{{8!I&-E8΍0URL4FȬc=vG k+־ )RyQMwhm~ 7 N +_CIUL*T fV.@2傑!6b h E 92NiG:8#~ThXQ)u{MyRt-97ZORl|0&#٤RhELY~}ZzAD^(l)@gGP}2<ePE5eg xU Ͻdn~,0;k䷇5b'F1HE#K+OP5˱yU7OwvCj(HnJ, |l8 ]r MBI|G*gd;<ܓgД,N$A&p@' >0I;֏G˖͢x&vCBR5nuC\Ӈn݋ zdlWzo+`+ y8}f/Fo& j~j )/zlO=-܇V_8BW\,kp4B CᑊF/Nw+~ԟ6N#3< PZEXV|gnJ?6K2ygI`ٟlHW #{SC{8v[K{Д֓&pT2*0VAvȝ{pu0YE^כ+oS R9ͅ'17^#ÃMLt /RB$@Ӹ:8-ICXM0".Szx;*[y( HcpMT_fniZY Mi&_J 8"V:Fz@3{Y<0˜J?o!GZwZ!izHW7}.:5c%νS4@)N4kIIaBnԨmb)jPk0cP-&#cnG46OK0H3Yf2cfOqtl = 鯗8eg󅘙YQ_P3e(|soȗBjTQ.LKUC}HYUw|fMwdyk y_E%+gn҆1uw¦3-뫗:AtՍFJk_#lm . 6?Wc&w=Q+eڥiӳojMSO8bɱ o!*NbqJ;8_JX ~ǂ.R밺z߷ɟyՊТLBT#&2^i51zۏV?I^ b򂜯:-*СxkZiq ih[ O"٭>0np `Hu.*T|^K2ɏ ߤ ;͇VQ]]g0m5c KŠijd+E.O]&M#Ylt%59SJ5872!6u=> .keT|E`A-0@@q92y֐Łj G?;kd3K7 6ro j{8AL)@㴊i J3kq (kꪲ [1#R9~txmd ^:Lc>=, v^\߄vO ?%-70&t*X*|kRi'Q+MM Q168"Ƣ֪T0w!3?:P :mv (dBgTbL9pK}ir49S*x`gESInT}'8n䌤hC1E_٤G` Inne%n K4JZ!C8܏޿$OYQff3x&W+,G fm,ڙlhut;?۷>=nS ǀU,=r~_-*5lnc[s . !L½mzcUB8,SڎR,^/IW*r2m᩼al,($6 wg );,0gXOc{\SzǙޒ&Yp5wW3 ?*\M9]78!ࣴi&wXYts?W0cP5 j \=W{wL/I6CFI_09NYS}֢Z | ~&[ww_vrZ^ yLT+|ic]+/a5 LvgԬE}hF +X&]!pBSI8 .o!)oa8z8IN x˨4yZfp/+;V6MW9 (zusrR C.X0%=Zz2hg58t1,+/ VC,D%<-sǹJY"Ta'T v{k >JQ/JيArNX8Mձ&m)ӛxJZ"ZE{ }`c=y9b(聯Zx<,хT;ۛkH^zNR>PR200`J8+_1@/)j)t\Xkt͛LuyR@ ~F4Np‰l1p--I&X5Y p!9h"#(<1F4D;>Hl͉ $T!Xz0#"PI`yfd@0?NjW=ʊMT$ENVyV}y1͈2*Ҷj fI0x0|Atԅs B })G]1_D=@!R4r/tdovt͐d{qќp^SXǍlnzAp>p:7sߟG>yb))^tM{[rqVcFyi -khuM) [Y*뼉`rӛI%fB3k4ߙ*@(t%'1G*/.CYCKHRr9KL V6ʥm x2 -,6ܼ/Z`2K%9HxYm]2>*wqNUPMz(sji EAJҸcx4*[qCbXѵP[ ு{0 D CFi+밟>l+;A9YeZ$+]wQ`pY}oQ,*D2X?.li/o2<޿_Ym_@$`becfc"`c~ȿ{n3#+#ߺ6Yoqp?rp1Ftza쿶'ݩ/S5/7[{ n$8B/~M_sXj]yx^p[*p:p8 U(6j@R2\_<lnKo^{y2lH_l]npwgy~|^uU0*jI~LW>z'b"I2ejN޶'qb9ԟN( {rW$T_4}͹tk2S/R;{*OaZX4H2n&8)\/2 gmGH.,zcSfW P&2V`(I KqB~mş'Q5ՃvS.30mexƣ+1g1ʯF "i6Wɑ4I'[s/Z'A+Fy1 22o%:yi(3 ԶW[d o,1yVn~jD`\Үr  &JPƟj8}hĨ&pdxAM`@ kj fW?B{hH9 1uSămTZ~2[yxx R[]Җ; !H%-5wH<б1qOԷut>#DY0ʂGVM&iܴ,yjFp 'EhK uAjZeG2½)T}T h#_c eSx @MYpޓa\ gL6 K8SQsB f?gEH8`KbD!dq E}`f`|$tߘ&͞N3IX*73icYG㺼j@!ׄs9A$:yԇꦞk024UZ^(&_v5Mtbz/<"r\TC`2ZI[Y=-A Ϩ5&~I-@Θ!^0o,_?-!Dwżlց Ѐ@m J\C;ЁUm&MΦ`_g)L7(=kCGл:IfDgT/$)b_#)|Fbh'cϖo]ӂRåqMuݶM`z E5Jk5]Bsf"qmƱNɉ{,88FJ?Ϸ,%V U[CPz7,{] 7 r2oޕQ%zq4K/(Iװ?^' dOTM /ޫ́z N(cow˞JӨ/\ҕ>(`Sh[&}U7>t`L{HɸE6fٹt/.r/dR_ҺJH? h]Jh I ˪X qXCݹI0idňF{~ dSà h6`Eln~Y2<h=0mv+#P AYHOXvFf3MևsO%Xp8=2.2eG@t3~?Z+H jHE6 :@|[+%BUIzS PDoα؄VNdJN _ZUȈ(!՝J^E4/XA#xu8s:5dՏ:ª3] 5kLwla6rT9X4$9!cMqdmgD>C)/_n Oe?[7j@< QitSO^H;;A M9"܍+Hc e Jzq5n`dp_ E3.bdhٶ}<'/~7aL l%5Z<Ç?vf(sO1arv"dbGL\=v,Γ_evrLu0-D aP1yY]F}!;g2i"R Hnq P9 XLU'_Li]H"},@3&>N{,cƟH԰=~PԎ'!D^'(}~=.nƤ7>#yぱp88.ԪUʂ{' 'Y>/pF6D`h!$QN yx#ދ$"ROGSm"J},W05b?51v+ZuF>E&QuM,J&bgV_=Ԏ{̩R\H%W5,ϵƚ}}Պ?BT=$'X/Ɣ{%{;2S@?ws V'As  e\g"erъ[ ֌&ŒKGϳf\2ůkt!C /Wtw w[GRtUzCBfLu_X;Cb>;o `zv(J~.[:0LT9>78c8K> < L>%Yty{6L!M"3Ek4 3.\qH? 4ź=IyY.5zgrեz9sƳ1+)S l|657?mep77 J UrZ Е(B,dP/vFm@ ?4ZҾd=Uٝ@^ɃYxbe35 vs[Fo51'ʟ Yv/+ׂ㕀aZ[w& {ٱzOy^X#c[5#40 Y`hfQ($ UiaY8\jltb1es.#-w9?EF^8T)owOsán,nO}&wמq=f!Ϊu#+zu7 xH%4 Ӫ[=O_OO|.%L(f 霆:#F{ 2.;h!|݀9EgC ~(bY8=ϖfȆxL9쾷8'\ɱ8[/N"f4T}Af9۾SI{-Z2u)| ,+~=`/R sݧػwZQFj'2S؛ "={~sln[\5֑LvD(0Dxmg2$j k64͟& 6B&{Ę\Lq_G+_E `MQ59񬍄rkW@& :Х" vR̦J&Qe׀׻XKJy-p3:9 ` ˎ >! IK"( ٸYh@{w|3K a제iEΉCYOBeSZ%$k8" Cq1n|ֹ7Pe,oO`/_%KAx䣌xYFQ)sV2ȉR "| "<-r+ϊN%;•Nx$fjQ٣˗Ns ڒj2L+ExytI<]⤃V+nMHcLMrR%$|[ABao.5j*SF2xkĔyMyҵ$6_zEʴ3DUi3W/KpFj&"g ͺ⎕$(/i'Be.B\z,m+佁pq{Y/NH8EmkNKJ "^ |rlp_M'IP7xeJ>דz`*~EBFRE{7nөKoa5E0M:}|Qk* w@w @: hqَ    G4ZA5;NP#Oma002t*mq4z7-5F2#M:V8|G+mu7e{N5AqDP<_A'w}L)̇ѽÃYH$AiCGHi$ze3 NV<YG$Ǟ58l?C_DK92bQ&ɭ'3sVYpqjPwc'f-]SGoA z672B_.ܨ})R߽ 7u{1Y / 8e3Jy?S39Q?:VGJg3$Qijo"1+UjtF찭$iǢkPjeP&<㭝GݙUW67KԎZfw=߮;`_no9 .C,ޛ #.~:ȓs9X9v{k+5qaF KeLEW'l@G Lu)Tf iYM3F0$1Vq NfwII W-fi6m*Lu~T$Lb8]C7(j}TW.K4$lZ)UFmw;f`Zt)4bc `KJm'# C)OR\h\ +ZPMςxۄ psm )A4_Cr7U A, k-Ԫ-\8UMպ$HtPVzA!B\cM}Ist2nWGr2] .̉K)U>ht;7J9_# Hd&"] AN\&Nl#ݚ0YSrx&3`w8ֹjox0lͫRI>oO$gf1JB%.͓Ckb6wMI۸jRNgD:5h8!>IsqnSpgpP㋇YI4IQ4)r=ݞ|0y1\40ÿ~J(5$>y'u"3=)MJ0̈c{OAέ 9;OIb2vV4s0FN(~4 v9RMMg㛖p5 u WF=?(92ޥKd\ "9bѽH PajEط|-A8m@}kHW 4_$&$:膴46.bC ѳp}3&Tlp =CBqN4R jNV\SbIY4'W*W`Y2XxqM mr>qы #u&w P?@wDa>I}:Wi] ;|MӊIER=?wɔ3dY_|o*]#r8Jrc''mnX@]7MJe7/7&߻~EyKѮ1Dlu8`BxM#4ܸHwr{E .ZTbd,.$V%Tۙh&ס0xR gKQ-lZ1iK6&,k'I@idV %aS@O؝ : > +uND:F1 cyqH>e|ߣT'chHWysTDvf 캦&D3z9Gsdr F2':uHTj8jDH]4䫘[kC2nTo՟@1-(O&4ܜ)V2$:=ج*=ae \f0LL$!}h8$'R TsTE*ISW4$]l.BCҿC))"僞K~B =jyaaՁ9 q HVA0y &Ǩ`0,"UD&Y7a_9ynzHA'dlXƾ)to }n4a|ëV&x+t&+c-%a،3lڦ?描 h?2yK|ÞsK\co:q{#ڒ'b: :M::O>9/'v=p(3LOSd65rN `1!訃H: nID>hs Яsx$Cϐo7}b8^$$UO<6_+sIL'4`hA0׳fr_V[ZPV/ z2fC(@Yz7zuuALѾߊ<Gߔq?5UW^^/ )+vwaj4#7dT^w!` ds.!?r YE.!? 6`c2j07BɁ x $p4 PJMחE|rZ8y;y֍Y9qJUA*R kUP3ıVevx3=;Kn^;_vlKP?t)JUYSuYȑ+߰jj}8]O]/c#f[|~mXvm-( j1KkP԰s![Wtj9elN5@.gsƂZLЎ%VS.3f 9M `1JhRR7diH ھ37[ Ka% pop VjBQ6^ZZVB-)E$q WD%U,(rtX-ͤ@HEAk9Rv3FbД-.vnKb#FTF4E,3m49@!i]ĎPQ8XP%R й&1$szr =I+5[?'g=Tx(b #_hD]ju| \^ՙU5: &w`x_oM!u+ kկOy+BUsWFwYY+;jpP \|Zƫ6 (x2SznR x$f7H8HPe2+=,X:FhBA/\yZ (nz`a 8=Bّ{aKyA)Ի ;܆JyC"3k1'Xe/:]JB_=8YN ?IH`[Y.g1lw48rkT4.l wξews!Z\F6XYTz,m+Z{˄l,$SP,'4v&pcKp^ɎHFbAKK{ @V] zudn~?Z-RIKds{pOhӓqA}L;8ge5ƈ nNaLaO^2c$-T6=Q̘K]h5irM%r2Jh4xpQWe~I%zN"+jVar X#4g??)gkG4Cv\FL!-du^x0^&r[lI?N0<%7|r2fYj;{lrgFAo13-|t(ڋ[/, f1wRp*.m^(0C@d:; 3mpvR5h ԑQH_ᩚGZ*idyإrP$zywB- J3}4~p |6‹lS'Nj8Tv۵[sZ&{1-Ŗ|qStg [XW7b cIo\nX[Vױ \d.Ngf =Ϯ-4z\׊ lKF2wPe"ܞ ;\\ ",m EPT)k.]D@G`비/(soGU Zv9Is:؟I@٠ӘHO hS0mD:x$vxӾ>;qV'*jFZhܪs_O OՃØIԀvAٯbA$aJ`Ч}]>AlPuqj~_(I.2YB& dgĚe9ؒC9t (]gd1 nnyW{F7:=}{+``Jj3 6s=u}/IƨBS> H~%Ӱ-cg yL<;MQڃ?~̐f*aWuL:DAwޯ ]B.xu|$yWO' kgP /]WkW}Un"/KQMbg o$™c^-u?dT1Hwtt'9Oa y=RVm~G/Q@IFL5}+(-bp<;3mbc}I,D+MxQaon3}8'z~;I,;R'<E`6?ss6lk]`0u"+P ; K)E$NY < 8OnH#+ ;lxš&Y-apop@eVտN.A# 3kYӫ`"y5CiaBUhF=1B'?!`*#E8x5nB)@6M ʥlqgtriϨ{;?˧դv*6/Fq--7v|%t4S Ⱦ 6WEf߶?t_ Uenz0`]i_+}l !CWe$̕Wu_VwXfcu7?lan6}0AltL?233ѱ#:`ch)gߕim7XGXeMF(9Z="2<⮗ҌzA>YJhqeNֵ,Էͧdkgְ?|bOZإNGAXu$f}ɯk٦Ꝭ㽶캍]iCc3zFnPcE/ob~ & Fm4l9㇓:v/]م0Ϲ'-cVЪ-sZ7D2Y o.Aٽrj{L}$;AbrAbcBEgPӪ[B){}U8~0i_haDZ0PIaFȽ%;4v [6.ߪo)> S $~/Uaݣ3GNm7#A K#$zV\jfKF-LlzK״<1uNȠX;KMm#3s#7 ǻ1Wf#9*;%N00;2ܯG}{-sfM`j"ͳgidž"\ =DbBWo259!{rՋuyO "ufRzjC։ޮѼȀk7%E-Fい 81S5)lA:j5l>V /_F8ï iFk.^?\][g,~X{=(I6yPb? [a t8 ihށ0 =;~EIjC4g*zXC#bAq10|$#J8e1 dN畵y)xIIL,pE(>rNDyf6g\ ]57o0T+*kϵR Vg WYR h<^n|0?9샫Î[u t{'VQG(TwwQ {y@@"oC=y{1&L{ǢL^- 4ru :G>@hq/βLE83jl > `9Q=Sq#S٨OI %?h;&F;ae;=_6:.&g0C@j !^6$!Bp@}IxX9&a }Sgqs#+Cy9I܇>%2:S#C@(.(CJ;ǓIMgA+K*b 'Gn|Ec <% @ °RP/;|3^89A}S0,rF ۠ ڣt?XdGzw;TI"w6HlT"s[H "ɍݕ'Ơ\T |CWF b[-3.>LЮA@d O7AmB%Wou~1JC7۔M2?T<M_-)ሰ:OeZ/n@F ez#TJ.Q*ivi~,<%;DrK\u>Z:xuq="#%sl?8&lęޙJ?_ܢF]y~-LDbx|Q67lAO@vVQu"%ɫߋP*`Pn'h!9%nqţ9sN66;vAaS0 w"F@lBhV ZPY.6~VyϡJ1;zHpƭ3\<)u_uy%j=o%l V(ZzZ[oW D-Ef'΀:A0p \lYMÊ*iAK .@qɭVP˙BؗӬ\C[ԜS[a9f%e$HrAWT=Pi \Ԥ-֊X2B.1G2]V{iS.C_ēPYR1}TN8 GYɾfJ:0CW:?lʣYz Vܒ7rJ.2 #tvSU!Q22oxRwtaB|O49XƇH)$F+I fsOT$"+bVswue/l9 :??栛 EN(X(G_1 5*ysT-F KGi ‘MC.87~ĝo腋:[CW͚;IS]>t'0LlP֜]|jǖ(QuԫV}`3a砇A:l`χ l,OOcg3rg`0|:Kbtu01RH=j ܈V(ZrkfFT줍`nW G`.)St0tLO[]4  qӻ{ ڂ`R|R`_\Wa{i}13 fb+MO箭'gdG]TX ÓK5F9g d*$$ΚN H*HR,g䷥PAbEr -pD{Qr2 kFݓVb U$i)v5xA$`b؜l++k&_/\ eqx6\3@"ڛ&TY DӐDK&N&߭=mo@!0I"!YȚX1k5HӈL$$@[TBHgonjϾNb5\@ؖ١p GZh]222괃pl߲AiD Kʨs_5$UAL`' IQ%h+ȶ!l2|8i_`4l}'d4GN)_X}\z g_r XA,'1Y3!4<)ʡJxօ%͂-:gWck7f/|rDn̅X ۨ.7y:z2diffdQ\%8w:$Qˬ v; m)k)HPd71m=B(KeH|Tx+Am|oK/KCL @0H(6-h@Ik0~΍03P`5SI#~U'T?^x@Z)xz㒎Qʼnmr[^O}Y~8 M3" ne/6juy4J9޳78Xġ=H" -\\x]\\B*Wz~ a2w1?a8m0J')Wlh#tkeZN$9E ]惘^R.Y`JW1K<شm7QOLmfG \Rkcf{nXx{' Z r2e97{|QY**).':WU`դ,FIvc` enn,fUD6Wջr ?b B<վ#%>FH_I@a{Eͼcd9N^xLBjs`+>0x LlD…@ '|;Lu(k],{a6β&dpV|\p+K7ǘIvSa6XW7)(ptMV)ân%Uί ')W/a.#@}Cwd6ښMH&bt]TAS8 D#g֥t#oByp.P"ůU7φk3IG.` {k_5]*,ɢvo]U?#pܥ} !ojSULX@͆FuSTۑcP[J#Xx (g'(mV[&Bq\NܑCоSM#vZWgXᢟ!^lq]c򿀞*w~l@`qԠ8MVD'.mp!QZf[qHDžh.nĠ9Hŝon171c]%}HMz"5`BU~oJ>_G-?ng6}Vt"KqaP, )U+n:8C{{tvu~XҨ .'v,*-U/7=;)}0g۟V|>=+]/t.̙;~ԙCh 9]̝ޝ͍; d_Z,c8fCdC֪fE2J6d e}Ʃ2󟖱 Xre,?926gOL̬gfu-XصC ul *fwMb^A*ٿ{pq |TFߵQ]=8:˜Q,i$P cJUaAs' 0H݌` !5jep\6_vgѴWtW- nHbpuwOz4a>jǶDUk042T  cZmf;N&v ; {z'gpbQ0_{4]ⵥ`+;AYqpD#4(:L C)ٓm}s.<ϖi ۓx`g/ZQZz$$ۻB e1.6B [ue@=̹89XAWJfEBj*VM4 ^q+8>K0B<#ky*zE醞L9X_@CЄe^WSL~H߃q= EξjD%ɭa7?|V+ @<&L "$1W6ɛdx73KIM*Mvamq0h`Q~eGQVޠch ks`o#WW'v'7%).bqVY`ЌUr%ʜ :شvi-_1VC{Tɺå;+mУGp%4KB&:VAyAٛ,(70D&wAm0"6s -NktA oL[m,p6?ֳ@T] w[Tx&34%P_O 8 L~ae:SUL-uDXOaG K0 :3CN?㹘הxK*SD*wUT37LóPep~J5]~۷Q?ó NɩמP%gRi*ƻvs&U|07CjЈlR.݇@`&;C:钡3IqLֈ 4c \|_~aIzoTstŠCAݰ뗇ݰt! 8"0jLz<]O% |Y(gqI >3:SUxDzN /< Od<Mk#]4mLQAOw"AG`SwZh WJ%iY32XdL dYK ܖISO6*xϋ> !b5!+i!KWA^"w 6^'~$?6A<.]ۗ[rVicR43^;m',8ZQVi@_} Z-c2-cQ=Υ%pun,}{mwef5  -3T7I CiH"8rL ˆ ?-_=EO9 sLr]0iuk:^ dڕU7*OXWSI .!zخ8 څ;VqmHo%[%E@~[4K:bfDvҊJE:/-AKdefMNFrfm((纫pod+GͣՏVw{ko̢#*%YӰ߲2^\n#n VW-9T镩>nӕĕ֪w~;9iU̖:]̿S33e b߲ZnK]uwd#4P "% m\eR5u;_௃#ΐ֏B": FV $~}#'owgZ$\n4{~`ósr\DV;>,vo.1٪I8E8k Ćvm?йhlpF 4orL?Yw9P6&{\f:VԱ3' :VvGli <&۽>hJAȒš!=݊H| ϞcQɎ %=i%`U,Qڦ2/$ dbVjsvj5%1z.˙eEϷ5w5t=VU WKm6WV`hҙ?`ҨN?dF)* n ~p){.F' O&Fc_6NЁ~wH'*e+'A.<Y$o9*W*O`7zr[-pɳ+Z|KeyKٵ'sxPʘ 'on[֡LsC+s(4ƌu&; Bө*".VU'o0_\ߛG'顰BP}˩kʿ]xrU<9䓌 mXvy9<OipfƵu'X`Ga؀l*4}n[_?At)|u驼ȓaj^'pȞH޺ TI!T&R=֝0l|rJ)`QD/ӾCǛ.ym1\noQ qO 9a\^7^( YUfK$a1 E1[˭-jPFn[z5 C<Ƃ՟xh\*a8S_ b g2IuS;ЖB@_u57 0!~Yĝ/N_j`z D$>m"5,^\wlאA=Z #jbA۹NqD6g j,jhFU i]/37qdF2 $E,EC1~*rjz|DՑsIQB~Bb(d܏5`]V9*/(3<n5&E)RT3fSkcڈ!ĎJk_ Q R&TEaǀ+PRk\Irx ٝ"v7#[`K2K5n8.aJ lEG"B"yՑPq JǑ$N|@ο@R<0%=H?!A*B5&J{Q>[.;χdO80$N*۸ɏӦ1‰㯤ShUXX8 Όs_N rZ8icR܍lQaJxDXÒx/1?ǒ>4K' 5bط`l& xG1$M0Qؚ^".՛:nd7=C !~+I.@_8cO%%VjlZn"])qt;!êk>JLtq:ɒJ+f>¦65EB{duTi<cMu aҤףt-'!D$$p ڲmqwwwwpwwww I $WY-csvOUhP wEIXf@PNUdmqD;{AɁqkSi&hZ  p.g m%d5xVg|w dX̶CᕸO2\˔yl;dRQ4ͪ o Jo dN?x__AHhen9_vl-4+pI6~ꡈ&&}c+ƵR6Ix?n$v!an½"Qyɒc8 _t8#5Ps!{`?9KO*gCq]! v ~+=-hԂfl>,`o|5vhHq 7p|M8 mjEbi" M&kT,baw)?ޭnضmΪ_ϕA\ffz,&y|^4)=\}q958ϗ6NxTaPrK5&y{M:(½?Π|JN'T^.5$J"'u+M6(f6kD;wQhhXejUle#=(9YLfP'3Jk`lnb!#, WraFgJHNMtye&W8V ;3C/)=n<3S \bX|p}.\\gqJ@nv G !Dщ)ki897oA\,\lk;u]06!;e2wSR֒NKI ݳf鋇)T<{gGk5PMۭzCE-ygSjMGB C+s;FLvc&)e}6^"ܦzeP 5:w1[9NmZRaﮭ3k 7!.C4:ݘ/g331+w\ ~|w1#giN30Dma.ہ&^Iº$yt%): O`2l,Զ:;:֝@m3r71,)w񥓅~=+GAhwĄMIȷًM ?2(>^mjA~ҠI$Ra!h8d'Da R6;ANs `,+Wd. =̋}cGRq =? i As1S 3lBtO$n6^3E-⺂x"UM!>7M$zG,n-qq<W7oS_sftV͎:;9FJ׭_,gYq6l2}l." ~~Ā%jGtыR~aq)7WIBAK2kn5,h;17eh2a`ӏ*e`m~3ҳh[}ÃM{" Fw ~{|iJ*Vx{JuNhYǿ9}BCĞCA,I<`¢dx6(OԚx(Yh턌L>a>|;G9SM$󮬍BU\!RcKh97,<9WlMt[Q >!}OR PT7j`ךJT[[0\,ܛ Hjfc,=&3<P")$volXj{re)IlKQ lMHNn1Wzctkyld+\#%?c  0uJm:ޥ#f zE%ZwVE_-z>O_ {gS1aB0K'vsvA,) Dz~ P|HOXB$t,z2DV6M Ub Ҁ1ْU!y#דl9Rv9@A\k}MPf:zDuS98X-ji’Ŏu`%J!A'C Fn¢1$&9U6( MNb$iN[$*! n3ԋVYuc<4sTq%=ehR7*4y88צ0;T|*ɑ:9F}*W {G9r{52;EF_HS ڢ<4hxzM$$y9oy/*"}d^qSԱ,g 욜NʙV3z2El\6?>M  K;jL*1ؘܲEN5"n;Xex􏭑Ww*3]x@.<wq#w])ll1{!+C]LHM]UBDO7\H?"c DADBU]6cgaqbbcM{G_!\ `.5 EqIy?&Ydyߌ g '.wbac' ] bwC'7Q)$ 'ׯp~-l@ oa?by+ 457'dʯx? \sᆇnX%A?0]E {_qK 66NKl"bް0Ȭ?L,Af%t+; Lzl)/k0{{zo Q}4~xvU52 SVG.B-'Eq}wi ?4tdn35uyuzJײZ@:wU` ɋr25jXtSMѪ951uAMTXÙ vc2=nKy^`d I`[~2MܐHKYR!@1pϵ$Ǒ>9=bI|+}Яôx9 װ0s|&s:>:[^!]_N*Ȳ+V' AAvΆ6*p#dOW#>˄RESJѴНw3Bނ9(&7zk%1籛"@L76}9%}-Gqk'B"wdzqTQS猨HrBMSvpܦhʸ,IbehZq6p'm./ QCDy$O8-ݽTgL)je( [5Io{49CAh ,څ8""#J8A2Rktb g޽ҫOYǭ<*iNS*rf 4@.Rg`.  P. +QNހ G}q mᰔ*#ʏRh4:)bpO*wi `ѐa#)-o?w?r$N>}|@n<YR!/QB:>^&<49B:F \zE\#? nRLkH:.jU"CĢOd~q9yP %eVI6N@[Is>0iJAShܨޝf/>U"z1Xĕӄ<hL !? ,ϰi.k7V')[Snkgj+FşY|)-6:j+5HP$' DK;Vg`Si˪=Qp{|AnT9ͧI=i6ic|G2DxKNV驸 ڗ',ؑn=os}R m* $~h_:׋77kQI0.Ktռ`/wC;8trfa#a=16XZdd,h}l*@Cצ:!wT%f9\w:'$k|L`w3sw3+͹̦|5<ϐ@=Ɣy_$7AS=V}tȸ#mۅ0ghX[5֎@̄p&:͊EJxow__ET%?djhgr>xXQ@J_”*h*8,(x\}) S[}?( kv$:P B ?fJtI۔TEU:кu/^n{I ~y\ ;quwwDIFmˢ^TbØюx<PhVn q6g(ǂ+PۦN=KFVץQaԤ{/_ %ٹ~<+ޒ;9 NqUɬDyR)<9~G%iw?.qt}GNFS)U 7 xPUim% ME^+[n'tqS\?\ P BWerb#4>.\EKi3L*hFs!"\*|WejqFѠC8*rc @2]xW39Vs =nJt :*-~[oQL&?U@Zz DV-D@Wi6z1M\xK>y$_#}Ԃߜfhs=63\u#{3z-6wOBD㔎KK'Ow{lg {XY C$opcFd98y@*ֺ=y{: 2pvp+WHʸQɓ$b=Zl(k-s tEuFGՕ jp *UB NtMCRML L4α+LYU~4Vf:fQJ6%R(-] ~g΂"<*sE"܃ΖDz^*%cRҮ22 [z]&A'٪͖]FfniN2R_[խEBi0ٷ]]%#ߨьib+$&yyリgo^L|\)e$x~rEt8l53Sq*KK6UH *3Wθ12Ѹ;a^sƫ 67 F0>kBlMY.)SzULF7H_[eo \I- ]=,o&Qp>T :'xe+x==nMRBq׉jo6vKLk ;䄄}5C~ p ] +:tr&6-;[L ;ߕ3y#y2'?&'\=98c?>@?> Y&aw$ЯB, .2 {muZ,Z_3P,DZsqpprqXF  ݅`8ٹYx,Kbgabac-,-qfڟs<N)-y?Epqq[afar,'_&_7}7P>U'# 'p^aK ' e? " deCs1b``\@~lD ;g¸8{@"*_- S'?o=q8Yx?cIA%7hlƒLO8~7UDߏ[WU<,\n8}:?kss4'X :;oSg'C۝iQ/빵(D-}ֽL6$7 īdlkj-0yvƐH^́`2ipv\QĥRiۼƀ.}cJ5^ Aj\qvvȍNql˜G4E !q Ër/B:ok'n{ɿ8+倫rbq+g5+q~%c#2we"f|; 02VA\:u8v|lvzXٚdpmcVX]嬬[pd&(BPgĂ~=|O%އTaȂY(O=SeytS7Oo[ +'6׷E]z|- 'R*~;8 nȘɤѴIe7L RDtIC!UQK$VJƮ61 ઊ%%f[df^aSɲ}wt$Tzfh; v`/$߷fT q rD3!djAbV/Pߒ]@]ŧ5ÑmQgN'tW23\W}\~o/A$3{}$k  m7W 7Ur6$@nEҦPdXf/$Mb<'TG)b&ލ[쪰ög+D{*h['^4&FR4BG >Hԏ|^1jmhMTp61NE*{#yԱ{Jq(dv) B DD0GhV%fLsAlf+AcMk}#`'ԖPdPoLɫ܅Ź㝘l-_H#y an3)B ij*' XRo4WZރ` v$wnf\xpda=WvWۋv` 87FGreIXi&$y0%0\XӲ "Z)Xz]Q<qiHl_F\'nz1t3 xK(0}.6$#%X`y,z4T%ʼnnǽ<8e@1\bxj7<լ65#C5+P<'UE \Ӯ,d32ɮL{^EKr[\VմZ`sSXϲ0x -|Rf)]~_^F(OWRQNLYETٞr*R񫍗i)4^@!cEK~k|zK/Wi׻vԩX1 N"}"=N4&T,5M}ꭜ]u|lnDA:R:jkDmjJ"7ޘ3wWɘv--"h+H09c(a)M~|o{0D،Yy`!hnC>/ʅ$Jo8˴;"friH삿)a@b$dCL K*:&$ɒc(52ک²fK QT}DQMtV!g)1T粪B9l;?3aW&A &] Y6@[&ҵ 6ZˣdME E;s\f{J'^VÍ!]r ~^Xqu]SS؀ʳ.uQ5:¼:6W5er8$t/sQ4AĴe7ZB2 =^L8@Tv_\Ks \B~qik F}܎AЖ;mRS)5媾3&<Ɏ8}BgY! lmhչ6q/KI9I(wthw!:k#*v}Ld ۩Q9Lzص K-,+YfG-E%;@m"x~9 jKIAv,Ɛݍ @ɺn)' Kn! oEmz3oRMBEG<2~YL3te5©zz)7ݹiZ!+XIG(,96vk5>!$/Ԁ"38}doO l0+ٹtdZLj8& ˧ Wfõ%NT#G#<&?37qA _I_p{O?~2bߙ:m~چQ4MnOj-F2?-h5~0s̬9YAɋ Nn@#c:H#@sb;qՠ9Rw߿JH-Ge;Մ&Ud tAim1u͛ '%k'vg[`1}ʹ A#*hވ)<,[]M46'lȥcIЦ\6#c$ȤjIIebzprqўaYsO.cj͌u`^eD>(aFE:FRB1iv\iVwyѼTn!=_ŰFBި$a.VxJ^p{|%LpVX:3[-tȕW) %HG>?tGhuo0 ?hS.ja*Y#Gqa,F|'&$9#\=|6qoywֈrQjv0M:`s2Pqvp8` ߰÷b'_i}cpSDQYJuX¨nR^`Մkpמ 8u̻&ԐE\r 6dwxFK =7q&K 6H:*A%U0%2PGC{FؼuO29?C@%{ KC#%l^`͡?+H" ^nϴS& G-ElZ:#Hz @z:S,*ɭ@$b,*gv,! RR[Ž$[QN$~MFC37;bI{A,_$@$JvuŐ4u /(rqmupSzʽ=p0Q 0i୛ #GA 5R BYC,09đ^ҙGmPU^U+KN[L> fC1ʵ7lyEs;hM,::jYRķ&JLUb_bHh/~]4G%8'r1c>F̨Rs.~w_oZtPe`iؗcQU +Dx@b#gVEҷyqІy6DP'VUD`)!Â]"smk":q16eD9G io Z?نbyGWb|e]L?%J$VlӛnNor-/OJ7Ȅ>xU(yT>Ki}MBA[[̈;: iHAƓ(|ui5OU%λ}슒 sSgcUu, nSe }KhG֗1&WM^Xxf .cvUS|prT6gD-evdY[IɎ[H#bå>U%W5' W}ڢSm#Nsֲՙs}$VTtt>RC&z#~$ێW?dMKH,ٻ UPTЛ%^+O˭i'8qP8ȫMX>C[p `, 8w츺խ\C)t7vv8kPt 6L J'?b}awt8)@}k-NFzG]ݹ->SSR(UnG@PN!ԇ nzsLy>0uU> ^֧X*Qگu1νjĔP9S;.O1ʦ_s,I| Ox,KF)L[2?|l" ȖAmpB/2NQvaktnevϏQnw\>ζF7^]Q !l.6}l#]X$]7 u_]9_Xc D"FG> Xљ ]007, ?OP8=1e > Ǔ?Ƈ+C,FLtHrh԰i <ҬQrsȽXǷc$r3x G Kwc~⦜?r=xö6˖ʁʜz,3z nPs Mtk1)ҎZT-o I+6eZ! |Cn.-vgͱ>op+h/q>NYVB$1鍧v+IHQ=3:Ig,BU1;q>-煿?_ B[OwjHx,Sjٸ8@gLOEd@CU?,ݜyYYd1swfypr`5qucuptzcghd߭3n,_5B01iklًuW΋5qQy~d|6 fxGV<L;A!]˗fLEP1E0ڠ; h%IE^AҏԢhpx}=6~ GWJfE)N]>7|>3w~Ndx$yqJ`S6/ :WKSB2+< *JEԁd4wx)Nc򸐦lE_'`#&Sd <-Ϧ c(L(-IX?Aw>uÚ w{SB[UlKV2'f$F>JdY2'oJsԸ<;.8x'D &Vr@f z{t!ڛ˕Ϩ@3QL-k3xؒNu`*pe,av8-.vezh$DּTk6|BicL|ys-"ˆXvxR><εR~MWG k2gKˇ~1B꽙z׻z|=M$X4y r# "Dklp2WJZz`ֱ4S,]0̿gChI.RA} P;QO' }"Z3BJn r(R#}xipCypMX7U hA;[:.jؠ~!e=Yd#MbJS#w}u\Ow9I:ᝓN[VOsb' V:hTofixl8U^ dKh-5qMO쏮̌+M`l@r6kNbe,j~~mM4TԷ9noDdTvs=!`τF}o7Xk5ΓI%q 4MkEG yrt+8i˽ɇ&C 00rahjy'dBM{mdG ~rs9Es~^XwrNiK\}yQ9ֳ#{{ ǣOVZ';&ʛu|ū?ۚ)ԲSU -\dEC+2|j2Fp"Q@wr|)GC]Gl"bh(Xc.4;ԡi9hf|A'\F@ t!Z/|L*Jڱ@X̵!|uaZ 2f2m:" <j"7U)+y9OqKƖn|a3#~*uZ O(΂V )EH`ʌ mP xóg("8xj[ec'oR4*_?@Eq"JB1&wf>:^*}㳹Vdz7Qi`>` 37hmFJ1 .|^!)%h+5 x8XTQ>J*33n~^74t0,{X?,6;"KH4\}\@hҊ@4T* 5Z6_=oo@š0ʰ:2U.h-lj:G?|{.K,h833悕&dsSIqc'~JA'0r$6yr* zT9a/'. oqSkG;C=i&6ͣkf>7fXu!E8?~`L|vY 5F M^:B{cG4LFQHzfѽ/,\_VTu$[&۸&O}E$Ёkܧr,&{' <0`6ФNK*e`Gu%S"haq]> pr arƜM ͈J34B1xA._q?fTlmd8bo`)3XȨ0r}lvdgg :4yo>ɏM%c+ǹL Og7}35" xE/ԬFBӔkF͇BZq pV&b車5[%"g >]SD[e=REbb&֝"k1?p1KElM \{ ѱF2J>4}gYy0` 7SׁNu. &pc <Uu¾:bƀIS'XzosrO}^9xf81ChM&wZWaC AMxEU|8s'y|LP"a2|<.uG 4}oay˔y-p9Rp8gxFl;@άSg()0[/R,j/(MyyqH324^S"ϙdp&3CNTq <Ѷ\ M"T €6H6/+#0ʚ;,nڙ?>HA:[Ӡ^{l_ U?l_7<y1Z/y8h uÞ]Q-ʤe:3t5 /L!撍1RӢT}WLt𺻜h%q-SMڏKEHT /ǂ/D@k nm(!L_.hE6GU<޸"~Y{xQ67V!f]+BAQ*)Du8LCw~~z=08#w:yPBQSYYfJZfwqtyer; ~MTO#fZqsFKGLF4y_k/ p4)]$9*uh\ c5Myxy96 8Wc 'Z-^vKXN^I'匥)x;a꫸Mc}O,M왋ac`Gs_g-2'5(y`]СoƊ%,`A2,gzGʧBjI &+Xu)bv|f061ИlJ>#}r'j k $[?'쿿7Ւ /!Uruԕɑ͉L]LLC\ќWS2u 'o]~@@ C,WyfOՔ0g*/kiW١b:42uXeZ9^EٍKZ=@X%Xۧo<,=6imoSӌwj|(2 Z*=/- Co_ѯ<淅{655*:;~]jp|q15G|D}һ(t&8˺e|6{v~ʶws'K,cRMyiC!!;nxZzԶm ҫ+и-,%D^On{cy`abvRhjUz&4Q/;#=dTx:/cxj6ʆ;‚P5OdG.8֕us>Y: <0I\Aer)ivc}Zmj05cG6kQc ;NP} q b3mu+&1":1%lDifW;qetsq:xpVxY:w=ЕQ' LRޭT:JM~Tnx2;M\ vxK.°i]LYѴ8B GeOf<~fԮ!$v @&ʕyS-jFNfkD:\0a:tk<*Ddj\}etW-ƎWCIHMr̭X7j<<,δ\cR'[d a9((8S.Y+lct,h1$yh6&17ht` Kfo{;uz0gp}!wwv]`se5[M^wv]fO=g`t )K.YڤCA CMP 7IOAE*a%:A=_ǥ)tQX_Σq@|qi9risk& lhJ%H[[kl-*t!pAuuxC> CB-yӐYОr16)38\J:H]1Mj.8":}xzЧ &v >v52X߈ mCgE{PKBwo/ֺl`@xO1r*C:!P!Ł:U =̠T)IEH6Fu$T&?Ҏpq|s&V43&@J1XIہ%u@Hc6-4g#-+xCʟe݌ˬ#m:򾹿2X> 5ARZ ˱"vH9qWBʇT*rMޒxsql-eO繱H ^xyve{Bw#(6hkՁ'~kfPgu{QcUQ},cx o l#5n;mf{_A"v = i&t &hǤja滧_sӝJ?a[lc;?XD{ݐISnXd,UcV(ÖC)x|B(i晝;I51ɫinݍ{T +N!OUK%Dgɯx1쪴*X(o; 3?WԾBom 2,'o? ))>ln<ΖwZ[uOz\ܿ?Y9X@O߯oiv.6 ; ? vπd_Vi1W]g~J@suveNo)f1]zӥsYnwe6…XbtM3›bB˛dFH zDp,MAF2VzNb>&mª-ڭvunD[ksYn+o&>}h53PyO!adӖ*] z%?(G]W@O^.4aFrᄏڿ2?][ nƬtW `)ndH !0%J3ASAnmcjXqWw`ߣQi0bTk'M1ϝ `rPU+}~1I9b`Vd=^GGAɝ4)6{7UY5ki7O/L_ɡev\3Rx۹iᔧ',#) 8[q̣VM|o*Nݟf#\f09?δܐF05#nkH̨6hZ34)_~A$$@vX|`GbQU)Eha?ρdn`A%zSi0EчD!)zg(쓴@Eh(I'9.F\{K>h]+8j]%"SZ>3ngOCDcPdU^D b$S^,{ZvV>nK3j }m./_`ڏ:DL= `=)R{ڐ÷'r0̙k5yБ}ù& *RW3jCɆFRH?Fael&ۛC3b5&z0:7QYr v{IXg\HL ^…AnSԥMJqéIC˖=አty{f=xs&SJbGUj#މ gv3*AMt4-위/w(vx;K{HWl)Bc|(%.?^oG9]^WϷ,s;(6*n<\fW~BJpu!<|~2 ᘈu g'1eiSlQl瑊#֬wϡ.L0_8)>1ykpN0#41<-`Z} Y0QYP$eBOvw%b=bw!gWaMK70 ~ +ψOaCs~5e58DXZZtȻZv^?TfkOFGdj/OГ3c-!ʴ˭Ss٫Wλ4E mlݬmνCtÆa+LNT!+x+ : 7sOpq949y<9h{ }1&. g@w"3-R&ACgU(QvF{bLZz zpPhu{kY6wb{ р :e$ ˅c?B]|:큾K"?oȑEdJӥZf~D{GC)8|7QU@cG @T ܀ jXRaQ AcwuIGj@c1C{Տ#䁌Ɵ +_@^a sEn=r.+9+)ոnDw*wRHyP'LVF}..I05D t|}]1(n8Y1!pC.""S2w( HI%hM (0v˳`Q13|m)9z *4z6u4=0Zg4n12CCbi.V#َAZgZS&ʫPq%Fk2s8+]|BRBub\#W6:>6\Mt2us~+ V8ҁjx Ű@?Á,U#-TʈQf/^{b4 '4[i²5ҵ%sDB,Q^L nz$A-EAԌΪ5Rʅ_ [\SSSR?O5ZSH:-pjw焴66dl;Y).lC>Rkg<#Z;*Z{qy+Nt|.cZYxw iIբg4:~=u]ip*GY_̅`u2!+-䤔z.lP@ U9JJ]3$`/M(};W6͵p AC\ߔ U TEes/y a4cRRtPYVw.!^`آNSIRoC9C^b";Ew~ty!ìK;MwS'>-P<A\#tstu+?YK6s|rL;Ga9-?xt3G3v֭zoH#uόUwJk9~è#<Z4G쨊 ɨSTKW%9br}hJ]Q%7&j ZͽfӒ-ov,OT q4Uzz`[˾zlݣ<ΆETb%bϵz5dd+ė_#ÖrXLZ#7ʍac1pftz ΚWpT_r x*ŒhhMy 2! K+i^cBf4?7wE?LM805XgQwm8Xx0(~u/>){'S{?2"bI8ف,\6urX87G//3i /& dЀ'KY7zjeA'oYs|2&Xxg ǟп} ] qo_}(#"dqHޗT':P̳ [瓟$d[R`%\3Л~DdL橺T̯&۞ j]B-4ޅQ{jS|A HZnNR84[c0;SSV0iA=E)`e7;;(,4ݕW5$󶄦~U.5 CvlmH$'?>p ѹ,'8A%ĘjܝFM{ؾh 9\&(,BB7 -|,SI%]{lA;SR;M@_h7uPF=Xi.WnY@-6)kuJL²DYɁxCۨY CkE뎎g3)¡xkrOd"1R%Wb:Zoa:[FVŭ4tB&I ϑ}*h`5үztc"5:JȪ&D)dN<8Z#z]GUE6U\sh`Y~Li$AE"e7;pΟH\~v!xm< V"7 H?qK$ُC(!ƌ*GԧdJ[B7b~`U9͸G8:X(J8 U |( rx3bdD HOmbz4̝m%niY@!J%ëcz|qR2 bD/yBCoFh:CAs`(X /. i6fzh(X3N]?$J%0CY|ⴋY 4܌ Azz~qJ2h\)13݋T ?UgWQ=ho [v٭UڌyuOul 9y|P&*4B$q6ưSJB#rvmB8!콶q F7ݻQq~ʺNlN(4_޼n.yQ?F<"D9~pCI)Dh:-f2>6ep̡ps^a9bSnÞWS̤@>õVmv7}bȫXtf=1}-Fh\z{k/ﶍJNW0dN;Q8;9y:p'e՜+n_fa+$gwƪӜZ^&F`"[?;g],)aIHzCm4h" >(н@Ez7K#y.HNՏ影8d!6G^od˓8QD[&0u, 4Xl$/ulڪ ӤŜZG/ݟ"/ )Z U1m>ЄE)i{cq$9.xK ^Hn ^lOnwA`Cd˜xuȌxu)q–K6E߄phsgEtA*"?'%Ċ`^{ ]_kdCx0hȥO6r[d1x,ODdQ, 0d ǍlWJs!;e3rJ]AYZȮƛS>Y6VD魢\oi$=13SRj%5(*2ւ1h+~CF?*VN[Yܧ4`,\.RqbRgh &"3,e&.:p+_Z>~w?3d_%e]T-{m0vy|T =vZqGRRu֎hĭ貶7k@4ەlPU91T~QQi9$dK+Mm(0L|Qa+Y'1\/&:t] 1a5mD' =g jZdJ_gTVS%LMK]*-%F+@|*֝2oϰkȼW7izXdUfԬՙOBOڙ|:}WωsǡȚBjo&ȡ0v lFry[57&J8Yveݙe䬁g`clć{4N@@e<`)RO ݁!,'Of `}`Klڤژsp}L} Xv4 xG}`Ht x` 2`Hl + unI%r1dhS/8E?DM 'W@}VavFʵÚFr;=HU5bqyv[1BddoˌdFO~Lu|jkS\Td`[s" s\ڗX ꫂ\H1ōA5)\ip35 Ig< X>cmaDCi1@bYp:*m|EB6U$F%p3E"n5cЛk&H+"4ߧ ^WcJ?-"\ZY7= 2Xd'1ud\#dSl@u'g]}wB@G(P:E GIqq ^ẓ/S$82:Ngmñ :``oq _ssLJN  I'mwyXRU]j`5M?{W";>d;pi0mn 8%a t9:XGj/*!,1~PVfkskzEO!V (0oZLnKb"'ZWn F7ϲ%h*;GJ?2sGW 3ԧ8 F±qEhNգ'6=jHjګ*л adnHxw%}zQ]?.v_G&wދ~K@Gk#A.OwΦ'ց4qA 2WeV27cAIpjm{Ag| 4Lʻ&rxjUxb]cV *[VYs]G3q9RI8AT q}Q+<XJ"e~R~MrL˳f-sD/ǒtV]ל~#SsO2oX2%H/X7u{Mv>iPv\ ZPiNG>osIt6Ճ){z)]t9U<ƯbpK38D'i:ﳁZQUG&Z3-?JDZ`>栥|xKTಢ?){$.jæ3r\54N*b$tw!ni3j{b)Q!0^25+T^aGyNݺqFr]vݛsB9R" ݄d}Vb4E]1c*DE4= Baܸʱ{/c,r/89mZ<[3dh,B8,'ݾr3hh/Ѧu;J5 0q((V݌5C۠ji>$%.gI:O)ҡ)oSL!S ֺӬsm{+\bX~g@ [߳$;rQ_a̹M# ocrD&mn}{CaWȆCcԐ4/C1ynLN(qIΐCI{\Nٕ(*|`'@W>mj3Jz9dzNx[S2srİm҅j7Z(ҢTraqG5 gSۊaWz$3#4âB~wj1޻+brȌ_U@R(ʒ<sC7^0rc 8f$7%-y1wDDd/Shr J.eE['][f9Zi#6cےzgxL(?Fq'ѵ=)YҐהsiZe+k>ih4Q~>? TDlKUGJt9ZϲW B)&tyRغw|mh;,_yr.J`3MHd")&N:`i<ɽX$x2P +3ky>hj ̢S+ag|L0*fԡbܢ7n5qUVdx^lwu7j㋝.2wC,A"2Fo!.X3 |dHQ)uzUЩ'oVhvD,]Pgwn+c[ 7/!JZ_nہ~\%X5ӌ`Lz+ 2 h{8xvZwji7G³.X:l"+1ՑcdM ew([x}q;q5b6@ltթ(vgUR^4ZDo(u  1v-efܔ)j4.{HN|V^Ƣ1<˼ E =b]*Uw)rf.xZ\ !ҜPzp2 y\2:W_S&qmRhiIp>yP-uV5%xlĻ!5aƤ1$!p8Opψ#>1tD &P~8 *mk1giR(Q>oAߑKNM[U{Z7Mi=W`Mm)NT!DA)^(ko磳쓏QZ:v x~ǯъ"V ww:S?k/g?sl|w@@5B.c3jz>Z*]3 *buXˣFL^@]Ch+ X~IEfyY( L$:= @d(Y9NQSV|&d B0~U0P1s {S$ J$Z}|(ܸT}ıFIf9g wI4㪆G麽e ,-Qѷq7` Bh/T3i{~:U%3Uۆh~! AbW[oӿ_b⾢PgQ)4>/F4-2B;VyS~/=!$q-,VI|"F^gd᰹0]:+u|>XtNā)JJ+ѹ'n9ggj̠hnJs?7A|V^K#,C@#[*,m .d;S1-H'}w rﺍVZ>`G)y#C@.K]`#\Hȃs[\Cf ]X0]-FCr4; ,m$б 8J 7y7$<,rڷYPfΙk41zΙOi" ] ݬ\^E<\xGCAapc2D ROq fd`u,p+?{''`L_ ȑX'Nz=Ҫ#@C']SgfԏK 2]%Ӳ=X_Zk|x! KLbe{ IV %^s&,=q bl#@lh-m ~}<85gf" {(wWr;*sB$1[_5<|\ɽ.t\#u/RW߲z N s}Ԉ+7?sMNvԖMQ8RB~^9ft\1s]qsӯuF;oW4f%G$}L~%b&*K8Q?rdhAyv! 3+p dFƛ\/<,鴱ɰyjB4:$~ ,;Dg2$<X>riz蜹Xx}T`pޡtL(wukjsnh aFn"O&B ]sHCgWfS;iQ Oq_\ۋEmp\Gs5lfg4?lD9\wg7L|jai!/a7L_M3o$ow_=h٘.k03FLWӲpO8HOOWfzZFߒ``{%G"/h\l}+=-]֌ } 3-ßiI4oVr0X2gE[cW02"P v'Ֆ `ڊ#3d8P,^:UY}*_X;z.=sGGƿr#(!#SAJ/GLʯ;OmB3лsrHg"Ÿ>LϔvH/_V*Se/Xid-J@慗SK4v^nvXjnNu^X#00Gj|8cdxWa JNf ds8ұ!D0e+(ʤ|,N~|%P0 ۽n׏Ehul=jcuތ*ՋQt3e)K 9m)I#tB N2Ǟ e!Ç%Bɓe1!brwDAsQC0 K"-ֵmJ _LFJ{ۑC)fu;E*,+b*F;rIu;PM*BS UuznBR%XH'0eHK084D׮ea e/وk2!S,4/sh b*X$Ix^\_jKAռAQ_bOAYgMMTџQf̄-mn];.YIһA5){5C0#- ~}>|[j*~@X3s@PNۿOn X_6ngO~0׻]ɿ/m&: ٣~qfN56s$&ڿGL`1=F߀Tbymeqg'AGi>W aɮI1˱P5VsT|VG#s&;邽t&bdUX8sm ?GPr< Hnzbl:MjSb)4#CFLi2a(CAB3N/eHɆY ޔ9Ŕ5QM *M|1u{0|z"nr$1S4UjP0Q_9L1Je,o&wY{]n}O p MV0霉Zm&QT/1ז(kV⧠3h˜ylJ&F'C{.RxTyXW'YdΌ2=0(m,NXDi8,`oV1wip+F^/Ԕj~nF9WJ"g6֚G{*ھrw5% %Pfٌ-\R5&*)XC'jk@3!Q.:mfFPI5%b=ݶƩw /"o;`ӹDynbC߹y)xSZ3 :2y/+L;l5*كK'ϴV1Mb/m@N?uSvuQ$G=G ~E&ZoYt_ĥ=.-#>օp%9` p+PWϵV36`[*˨(g#f"**vl<9lP M٧M<(zz7J;:)ϝ%nPT~1t+B7³-~Mrl|5"ǧ쾓zPV fzeA,Dmv2He4 3bmHa{t[^"Ds76Bv91SI1)B Ksk7\z{(OEӖ900!,|ot=^F<| }Pxn> kt6)%Fbe៯|>a_m]رS|k}X_uX+4,\aNv6ZVf|&VZz6gbe`K$\~.VʎN& gYs$OYf`mF6>M<xmSZ푍7 oF,DWݓם_F6O91i\U*->O {yDAzuxOYLi3H(yA4W'Ty(i ش_Y*glG6h-z+*v֖#y3 :uw^;|8  p⚈fA%Q1 zl2' )RkWK\h^r$۫0vz7ywps.b>2QF~BAQgz7Xa^œW?W?^۫>w? F)2Anx0,p5*@^i[;6h%L ғkCg.[MX+ Mt:}~rj_(ZJ 7_;d1H +*+?@ O1c u:le:r% _шG5 A)a0Qݠb< [FC#`J^.NX sw˼Uv )'axi! w_Ά!!op2)N/J"Q։ F •KF}zߏ*Q=2U IE*t!OPL{ s~ T W(2qV˙hDOMzK ~zQ13"'4f/i C7)hyY dE;C&sA 5Lg#ڃe_PNjs"N7> u+k3va]4 +bu+u%վdqJS3oE hRNފ)@ݻR% 9BQ媋-5? %0H Gwra畣bnJsmno&z3HuL=DJw5ݹgأӍq=\FB5C$ 94#u\/ ڐuJ/٭WW۫$R,um<X|圼u7~FfX[P?"YfԕoLV<[L*\Ⱥ2HX^ӫw67E9F65>q/OWbƮXNюfF[R+7m. $̤_mZ3JeKz7\3ב5]$%-mV˷8EP`:=nŶKP<'C׹D6v UFL;]EwcODWI)U82(fnl(ӉmƣoF2ytdp1f޸<|AT[\xN{NLLD* KR> BGw˒M#*JgQZmNuo?݇=m2`'(QEtRqp3=t7pJ}ټ SQh=`ZǙ3d^˵^X46(҉w-h]_^OG-i; !q% oңwpRZMocU )Ub. ՓUX&=g " =a @dd?3D>LmtVI5O%+0Œ,v+q Lk4 XJI$MLqh "|g0?DpQ ;>{}1\}8''ˋ @y KlC`F쩘B47eZN~?\ -&FlFm5S\8$>ckuTC: p擳dwGV#.= M7{Z#>xy$KV".ūSquxz?J΁΁6庎8hȭ.]6bVPeRB+M]jgȸNp2=x!D3jA %!QEѝoC6R=Upl3Zn P*/rIo\JtFv8mwjƗe0@KIy<-0+DV{7'fszt憭e*/+VJԠg:t2 ij=PW~_@ZJ 8J f9 #~?RzvȿZj.~ǣGp`bo~_?*=J /?8hP$+z\( [{oyK4Yj kNkΎbcfWΐi#ߡ!-3+F",۟krt%[@hi7脍)Ls*k gU׍,ɟ.~S]_׫=eѷ9$NLisTTG>$j(ۻ,A"3DM.Лy5 4}ػe!?)4]/1f ̓sւ<Ѡnz%XD8sغAmU:~8H7B'eVu4"Oj3WS:{w, wީ)^6UI$.F+8hgWDcy Q;wR'IK d/+]+ 2![?TݲȇlwdI*g.$Z3NyɴN ȞLwUzaMhgиQ1_G<t ffEGS<9e<*/m6qCUu:w]g?ey(%Ŷ"BW;tv y j7yZE-_؄Ѫ'ޒ??uu3x}O$c6(Uk#<H,f10]岃Β0OQxɭ ~*u_[ 3_{8sNVW#Vx'XކNT\'M`XJ[lfd 4tUp vBBL?UiđAT2C(7nr &(#4aG mGʯ#,5V(m˼wi^!5f ᫛,ByAz`@=rLf~KCHU'#!0fe& :0lqRx& 34 M9b{ck\{UڷVq{(z"jByf&IZ\SS æod]ɩhZ)Q<# 2CM/k%_kr9ŝqD8ހu5<+nodA[^[m6?K\M ]=NI<K#/2w`1x/>ґ=m4g).2jzC ڔ?s J)"iNY8*n)G3Pe.Q(t֐t=vUN +@_he"-WXqsp5X[u ԉTkv)II?5@#aB40C+wamNpAbl˘ @܅*ӂ1Nv9-3z/EHO6?w `vw]-*`2Rvڋ@},8D\#9hR<W8apǚU=mSn;^P^Fl n28wܵz^ ~_ bS͎ a+0XJ߉#ijPF$Ǜz3͘c䕼=0Sd m4o*FMޝZ/?K9V 8G,567f|2{̦sbpC.0A44n\%uַmƩ5AϬ_V A~X*{(ၜS]yxFPaܰ*TYu;j&Q Ldz8!q1th ɉ!u Ŋ+ =fc|7Zzt5o%٩~Eґ^/{,V"raO}^u#RLg23+y l]7pQʛʞL\R9sO3jkkEh6x5a],CqqO'>2_W?++ ['- tXw_42& a:og=vP%Z[[\o_~z@$D}ؿǤ-Wjz|/ʅ$mw ? .E:>MyQn4dl $LUo'.8C{;=6|3M`HMѷT=|H!kzB t-yVgũYdqn mV!Q~6l{(43S5R>@#ڌ$o~4?p1/Eh1qTXpdY7K;(Z* BB];ikYpnܮqLˊy`9%UDo-`ށw0IVv`^{ZcF'C=#зІۧ˼"nyvi2'oFFc? nj%c% a^nl:<1 ;W\(?ý=owA3 lZA#޿+҉~ AnU9YO}zcU}B j⡮Tit~W{C kOn2@)K bfL3ڀpFUS s=/Me y3~㽁j@FvZ {B&(/40l8K a"X0 UM+i3| vcc;vufStc ~`\ a ϓ{R*Mo534)JWX #yҊT+!yiy\sˬaֽ`>uq{N mZ+w( 4{&)x,ְ/|.>Q0Nxm\3V  '| lJxz1>|몟aUOD ۞l$_X?W!uYAY2v|FlA[-NfSRjcΥjeOo [؀c|5@8j7BuB/a`qmw3q{h]yVs eL?hr<,uC85T!kDwiqmWp ƝTw%` ?s׵b$hmPgWcœO jb?d0:> dShJ"29>4ѠƩ'Y%cP#ҹ̷xK~Tm4P;h/pdL+ϩY2>xU@[ŸSkQMe^XBN{m icW2b7%,$d=-g\yu֕klş?.qvUZ9,7%}8)CM]OvALLF1 R]XP^M0>->!)0{׻A,dE-AH 7T~ffr0hhFW9TǓZ."} jQwbRAPQÏSMaTF55Wvk 59g+d@Sn?G +PAbg*|^T,ct%bƢjQfM 7y6)_v+ɟVahD .HQnI 'mо f}=ϝ @G!!EKC78GWȵ7Tէ>-:awPKU:tpEW A˵|(]%x&+ܭɮ)H +2tLfW$Q1Wf!PRǟd1!Fsԟ@oq<$d[ln7q2ؽ߯ZYCãx?s7'(ZF~wNB2l\CDN\-uGM>gӁ"c=HP%X`6Rx2Eg0-?%]:4H~ .OjDi[kaVׂӺʢebty`v; 6?.\e-VLQq5OӁ! )O[ZWg\ Fxv-8u}6Lϓ ŤCBvڽ%6(zR77+8ntNw | e+-RƐj¢x< DnY`kg&>I'PCQ.S'[=kS1xAPt2Xȣ!pb-`ƉK;d̝Eœ$:fdӘ[.ɽԑi[6ԑ ' A_]cVovvr?MՎj?uELLӰ\u͘s1br!OTȍDg84ZħS=|ު-1t7M}"N-{feKۘUdhB0=|d0&B10~wZ?T /G0|<4N( `G:Ihae ^kjd! @V@])6 B(Q]g o l&˦?OhDP4=\&4>|4Yiq jxT0SE0UGNG\Ww|yoIuq3mgmtD ɽSAϘ H}LW*b.Ǹ^֯J t ȈI᷇| c e hg#fm)C@LN% lu}~YɕOgE_pX6AY\7K!W$29Y`t+癩IH) #~O4_(- qQ蟱~N?&;ʊ~RCê.q1?y@o.kQW0GcsOQ yLb{DZu_S_+NV!L XY%z~qCA+ Lonnn+t}kJ;yL">!0<6¼Jc \3A-=VJݠk84֌owJ0Q7 w+¤ 9ˍO1vO`"9CY^|a" df R_ @\T3y d3ăT_[gvA4jځ ~Hs#eelqd1=HR9b@_$ޛNJ,?%ɲ! U] c2оY;f`93!~12.:CɫSI_C57^'۩@5@2IaWOۮCo(8tS-FګMX?iDPֱWka p@oQըbr,Y<q/ade>"B}%v%Aд9eCɼ="b rQg.*IVumFT`Yvf'e.!`4.;E ޟIdCbYqoGhmBTW2{Œuk=xsY ֥pyǾ1(@sZq[|"#?97^8 o૨9;ŷRG'lK)Rr>gؽ[`y 'La*fϫkJƅ o U*^BpH꧄wyfi&hp=^|OT]TPY'n$5?l[V0@0yHl {HL;6]od脥%\Y1c[΄,_@%>*' lSjߧVGgl'px@i|k0fɠ"!+8HF8MZFb0$/5oƔ o H++NZ9A_$3dg0tV4#4>@xަp8 Rk͆(ADLG̝i1.C1^%ilD%*uSjq-@!E\]˖ma F('Ⱦ*U(W{0Ta/q0?Ԍ $qQWՏda!HSש=Бx]wz.!4 Qȝd糟NNt+T]jw]toE~:x7:㗽i3`OJ #@fsOwr3_0ˏd2ý!q`og%p -'C{=.#mU^G=|8tb;}a7b4Pc1Z"JAmKK1Пmi.sN7aVh$gUZ*H?4i#l=A*Y UQkt:5xfu1-wzzlMI5?`[ ݤd$E7UW| jQ d X.+/u 5祆4Cƞ;QVlysTș&s_-)ԶS@7^r[2J[h-nBbm;,~߽/*$gd=f-oru.ևu wgI&86_#9K#m()yc*8ŨО5xg2ȶ5~7ek?͖h{LKvIɀWA=unGdVG|+o~Yp?gk׷%:V|wߍ V|)Bo HSqR ;dO8 fZ6F?29h?U?Xe`a6v߾ .ƍM_e-*NgPS-]i E>qSY@jȷN_ nD98 ի>}毎T5̦} iEǛBW huQ6$K'سm?Ú/Uh-fJl]MfW_*̎MD.]@֥GoCV<cٯZv *eIbE&jLa5=91L@`-́^ ME=zjFN}]QY,>TV2O(}񟿺{ p!Dk>(&_yO.1_amn(p:wD %b HklX!W&ihzDZV ZTIj*huW:m֐!j5nhZ AE %{JP@КV%jJ! &=EQ3v/of0Ѯ Zz[?􄧊`43"ƱJե3T"Һšl N"r+(-($+ vr ѝ Gpsơ)ByaKlyh)ǁ#$[ӜܺXKV؎P3?q2ǁFuf,k)NYSm|dЩD)<>n'Ȝ 1h-m/k[H(֭"[5=f0\ymyyЃAfYbK3^tG\ },mЅzQq<*6|Ksp@>Ms(o<7Wv~yh5cO8 Yfn4kM 9=d8ac}G)<Ӕx;(i|FU րee3Z}G*u>Cfu;(2#VV%zmFZM ɪ{X^ l"2BLTعLZ8)'.D4l R0z"F/0yF  3\\&̅y켴}6B9GNHEeb AU&*N6&:26ÐH7Iou+616+hY r%l8jc sQ"o$`oe ۫!JډUA%|!hOm zR7I%mo59;݅YM Nh Ik$ZO`Y>o au|18xT`(S`"BX;.։徟Q;T .  L@*Ʈθ Mާ$ _.'_j^,5eHmѱBg{v2A=8Mv\ዡخq闸)%_?,zAH p6"cW֋z oTs_KcL@z(܁-Fr3L&0{G98h8hݡivC{EoKr.&>}MoOYM -ci9X8&32`ULl?%$&!s[ 5j yhjǡnp&-7S<scXz9h!&/bOEc)ʠ VM@`xt ܵfKPT0+k 6c˲2 <l-KV%[le?<̮(28 # !|ھ% )w#2g($5ΐ-%r D 5+աshrx,h'&dȕ,Xŭ+g4 _X6~ )/WG>J؏\D3qQeQ5u3*ZkS:^oz p?DM3n7ym[\: M\~َY׌LqDEX#OR.撒 ]ӖN.5{F Ȇ5 $]á|XuᾇT:wem.B{fu `J| օ\gR;#!=58Ϻ؋rqdT,ִ0p: }C,k7#E t,tO+F69 hõjDKlfJx{ŎSwh:% }b(;+$IgidK:ْF ^3L߉b N&91? QښdAj`\5eGc1oe f  X_7-/;x00TT[S*w'`9) d~WeB12Ӭ"17(jͿA`:Md#Qhl @Sr#C](48oC%I/B=9:시=@ 6!/nX]rTcoeZw'Y(q&0/J"X?ɜs6 Юfy^&q"%3 gg2pCI꼷c x4!,y$65[Œjaa*Kx}4LD,<Ȯ?RZib2U1)$R}^ȷKLr6RNސys iEa+Z]))PKnmK׺<c 㤓+qH~Vm^!o ]__TVSYQ"ȑgiO,PfqZ {Ab,0AN JXXDfIì?X7 w8G,nCyص_VH]UB[Ǘm%u^D|LD˛"oYDX=vOz R3(SEkLtȰ@5=WËoLq%z?7zvly!DFS'k|8,A'3*;Kן*#Njػ\ۛlLH+8>=r4/1#Lfe.lo522狦nV uuswW' J vjs1#FZ}<Tqأ f1;&baҙ+p r =7jYS*Q!H~f=Ӟz͌ bV,l/=)$A"޿:`3\g4hi * xq ,d|VL P*j6q)a?a'aɠuϧV}E'0,gmV7?Ud3oTVVt+KYP}U-!!>_BHgW<[ :~ׁ OD? I00gjcg+ n7N W, 붬) ?2{Y02s?Y01ӳ]Y8hNl0229hQr OE Iп99ld,yF߿UFe &T"އ+ie^9}7́v%ǫl:~_Mʈ́zf]~.!kI<%%%׻#IMy6@11d`,FYF^dJEyUm;E݉XDKknA/2w;F0ל{ƻoE7ppړKp1𴨈|&c3W;`d>3$9pr@m+֥+KR5o}N~7aop[FZw/T㯶"=n-%oDNS{g85C e< I^H39K)l,={K{Pw^;^nPʘw&2=="uh ۨ-p̪ᡸB~Gg:s4Jr-p76"HU}/4T/rdQЄӭ]{KbFy]e,jL%.,G!eelsm9I?#'&Iϙ(do{>1 "QwϹa=G}Lrހ2#A]T7ܒkB5>NdY<2"sgކwnv]_!q!>k6x!(9(ˏltN3X%ҷR[#*޻˄z qF~!כ|ٕLu[[oE Ƕ+k:=qq4O:mwB= 3؈Ffq}'%K*߿'*C{c7'衒SEp 3|$&DO4Q/*B˺V䝽I?vvL47Qڽ<"Pjh=8ωG&a@ xJܞak1ojOMAH`{:vN9bWz3-!)!>H4fK'OOM.dѩ\om{Yb0\2w૜6Þp"bdNB!i](']7y^Q8 l|<4֊yN)  jemD c㫖fOJ?u!ءk҆f~u/oLGų?)= goum7) ӭ r3F[?ĥ p,Œ5Ƃm#]n=~C^c{ k8ˁ#@^XwsvVޮ%Ʋ$&>knj v.CMLJM[$Ϸ;T Xq EA`Qƛv`:FjO,?ՊmG{A>e˗w7'JT !4%&?gWό9u~B+F|e?g)-%Ix 4Q|> O0&Vbd)hE0'Rcʼ$*R}6V!W~P휂ȑGˡ|Ί.*_0̗3}߸&u};*3mWƐ8MpٟP+IXv$q$ikH jq=~5jK$TDqXJ{i%2av6O-iQ~2d0>̧ 5ςlޯ- 4scXx -`lw)=ϟ}Z=9. FɇjeuEwVHZDAl*}9"I513[A+ql(2tw/&3B8$-B?8X'6YE0N{v2,y# 2JWO\WR69Qp ƪ+Ls,0W醒d:[StJUdD"èlG !ibĒ)CriCLa}whZ(ݧI;X2B)qJvj^f^͠b񭫀Jot#(!u‡)i 6 ڴ7EF*:(]s{ӈ.=dէA?xޮx=oi+52dL =]''VǿSD@Wq_&\ގSQTn/򘠣R- gS傤D@9GAdX#idNӏ3C?1>WP fWf`?IGjC\+vS(cU ).9ȈZ]`'I$(116\½' #aE< M\R&En9T*籤W'*)f1ҒJe G!1z녉A9m1DiʰC?Xs Գ2˙ZgФKɕ8$9е5`m76~+ Nfdwhep_+| Y}SahXa !KȔn4s5GtM߾&Rakqel4BYRH/Lo\$vf?F|}d||t4|Lu'B ~ܹy`H6W.-rXr@^ By;4OdxB' aNe^($gyᮑpKvXR5<;ϩ߆ugd2LKc)hf'$'>i KLJ1Sa ϩ*,R+_lp,BNQS3AMCn8 W$m#u$W`GQU?'UL_*SF;ny*Q# 1yk}A|.<`<.@+2pK_[q=N`*Q#'!"Tz'^[{9QtѢ`Ћ#;̿/+罼R:^z\z\%w,9 ʖc]s$ơ-˻k'>3?]cdRLhY٘XYiu (JٞgZ#{Z^l-lM9ZYؚ9Y;㿮1.vfE( Ub6Xa婖 $_RIR\u!R"ݍ8i}Qkf{Wi/-Mie+00߰Yo7 כm?evg2F2PZ9׍J~vuրw[2-Vv,4k% b}qO1ܠ.?4QR/ =w9i{,d_a,ݯu$vEln#X^q}]ZJOMC#JrCaFr s*Ļ;v=ϒ̢7~nAY qNqv e j(gQt,EuC\`< 07_KQJqMe7pȻ)SNNؾʄXbռG: h-H(j`n>W0 `^ pd@0eVd?+MũA- iT 43:]+tr,Bjd)#+rMω%C % pnL#nB\ Km)YEu;) &if6[]tSb0}@9 d5\G,5 nH, =ޗ5` x#j_c\س̫xCT97IGbyZ|1% @x4Pj(RoR:F`[23 ,Edš]gQJXiE;ЦTa[x*Hu6A]r&'yba{q3^S*>>r ,ṱ.GI+E̾TW&ӳNJJ@:VaH]l/!$W,?obl)3}/;ӟu/&/56F`b }jVllj%vjQ Y,Чc뉇5u' (}˻=-אOIH)*cs}|w?/, ִeEY7U#>"W֨<ؾKIdT^旵˨<Wzh\U'guwݑuJsGDv9UW%1U05WⰦk^Q_Jg*pg\=61ꂕwhBF.Cq@a%Yh!iϏ 9LO|'ꤶRB=@b Ĉn69ƫL!A"O@ȠtNW6\q5dS]gq,'|pp2nP>،Цrzb9OZeњlp" 's#l1CL~Ma-첗`ul ]5bAQس"5*(X/w#e WO_(ТF Xh 'HN)_*NزgD; }ĉ/'( g[}畎O*&ᄷ [dX{Y<_Y2l^5F9c&7i2)HbH|1OK1Y|@]'DfKp;O!>@#"VVT-n{GQo ڱ-CT3wZ`VT֢ p1ZШv;bk(tne9}m K BLЗC^N@H 2b5HE[m;9v ̑*¶hp'F֗/p[0$fX<^+a`pUp8UF'zO䧙89[$@eL5&exBe/<ȁ]\n/p %z6[Q*消{5.>/%!AA Cn!|z|",̰fKm\TG_Ѕ'ezc0D>A :;"t@^ 97(DJaNlԢP[U]xEahؐZ$@S$Z]ZĒ{@1$l@>;o V⠸J #_ѨY@\Sb+Y_`Ko |_F`~Y.6K4WmCt`a= vKb!,#)d*yHvURC"^剻l24+K$:{rd~Ihas/XPr|O:%`>@/!݂c5Uo"Y»wiZnOO D>n0|3mt%_ѨWYjHG 8<8)v0 9{ @Ag"$9ϡt/RCLȣFl'D9UH=`qk&Ru]ҔIdx`'Y߹htp;$YzֺFNDŻ*U8S&Kh)#QpM N9v[Jl( <,jm#\BtL%[Ә@>oڷ_|tJ?"*DPԯPo2e^0Sz.HlaQu|5a1^5&39i= 9$43{sϬYf͚i! |r8w@b*~bt7Πy% P͜JslsCO 'zIJծJ9E,ETU>(%˒9C>A"Ds+lom&'"Ր_Lđ2l'ʷ4VeTmC2 wGqRO-6:1؊'/_b&qsGqYu)149̋6C,f!IvJg+rώm6`B46?6!!?gF{^կ.'`q{HJrgCl-j퟽WJpibMdmC}5IU86^A]Spqbo;1 vMWMףe6?)!ϹU\N4MOv({M{}mLΔZ2ݯw4aI_T>4gQn}bjuɕ,(A)tb3YB]ި߶']TDcCd r=]mYUVbgܻL-<r_-sҘz"rBm>X)^Rʞ{!DnQV'`JLj^svFQ<>ĮYb7z>Cjy mG?&#uVe墈 ~sBd|sfwm֫) ~/! 03.y]@3 f?/U`63su=] hgկFwu3 xU 5&Vf߯ ac00 e. ~!YyUW+;3l̬l+L@0 a` aWqmG{U)PfV (kgnndn@vGt݁dz6SǃA !@ 1C٘G@"w s#[obB!~> SDcG4'0mv6%W8'`b<~d|iJ@e}Q*d&M#>U#d¥x:BX`fDC{>T(30LwqQh{@lOMiGӅ2bU_SH9}T(,%ǥМ IT f2:8>_LNp񢓢XXlYsZ#xMFn`]Co'CFAúKAOݮZĐy]|՗b$ i\ J>ʙ)pV)c#d;Оf0 /]lTsmG ~Zg7і/DtEBkVrEaKh ׆^5K>l4~`'A 1(xcj& sa_dr^Ώx}d,s( 2>1zh+MxΝgL˾BgrHn]n^IA_r2SjCW?F.k9s,yfsd{PlI$ˇ1t̢ 'OikJ=34`HP=&0UR)zYMb䔝'4?Q*q%ܦШk]-+#jMpxssI+Qx[@j ^YmS6qioY*Nh1yG.=vo:Nɨ$F$Bqdi3& eF6\wVzqrn}/h@\'}vZ>HXϱv)Rsp2LUXnlRdgwfS,|~},'!0WZ0I }-QAZS2A6Apxp(;Н@>NG3#/&$9xjDUvoeDPQ13cB~V$ۚuѓY]>1G,mS1ް&S[z6gl-S ~(4Q=}\8t_A+WOUeg%95+9T1O]mF66E&3\LAh\Ժ'Tы!~|ݣU-rM i<޳X6D{Ix_q. =قg-5;T_ sU&?iN!"D"bA$%'{d܏Fǩ QqjM')۞Oґ!P lv+^D ;i".ST4 !5EGh| FЅ#,*lR}ڣz.I2n; X[*=Ӳ')i*BlFO*[~yKƤZ^jXU/S⠢v=K2\GHνo1A#HSڼC 35=XI9QI~ȋ0ͩA!ŜA5)UAKa{ Q.#4ygt&I[ǐǓ!&l69?8 fn $3lyGS}h kD>b(=@;I5hɬ2BfZܰ]z'Z66X5m<5yjt.1޼׮6R'r(=FPŽy'i_.'\/M"hG@*%kh0@-KVcf*{R]-lXh<\{U9f\:S܊6fߣ8xJOF k& C|G[&0,#-MfGپm0/V2Il9Vadv)qr Ak$,"qϳ'NH3G )mey$nK98΄~axt"ɚ[A(w2#{yzmEq}OVmq3:04QtWy`͏0Kr Svрg1y?鴌 n t|ڌv-!X~A5eƗ Ӭx9R^VɴCGMtjC ;,Hil 7Mp> }~] P0LZcw9)[׀ú/k~Odme@oK&%ۧ~2d5پhcB/8=XVҫJ>~=;W7b*{Bf #\F)OQHSv.|U8W]9&5 .}ϸavǸ1lļWn#t5 eqn1Ǖ?|.q]Bͻ!^K]cѹjV?DX  #s6bZ ?Cq02GR&X+vYI{9N;=GRн>r5G$0bSr0+|{,(ѽwzl2F>>@;3*ǃdP \N8&z V<ݠrxdZsWMoq"1{LD30yKO.K"]>Oޑpt/k +Gqħfbr {lb|1lYDc$9@Ht2Kr$ՅNN;$+f|EfKwC{eٗeSOqy? i۽&" 4ZK+J]WdKȬ~+ye%1RMeRɜ0 .ے>DpOt4 Qa:!+! %Ap(q`}(hOI2.<sDJFe3!{W:h*un5{Av454d=|he`fgE/4_oE>ʗ5NBMsG %8WONPc`ݟGQps,8?>>!#`t<Xf|މ&PtVV9ob8xF]::#th cڰJcb~"{\}<ڇ|(y *74ƒ)豦JG&pm?x%J9`YA,3,R|(Ķ_m@ wlO>©m߭Ts6lEfCP0>X\lbߢeX RkCisW*fqiTQmKR "3yMGfv<%iyy:~^5i`tBEa(T,CMPgsfߍI g @*Isxl%5q_Cf2ힹ43{MxX?*}:JRp=RJ V=-`gER廥΋" j $[ZzIwϘ<8LZmr twhy\[R#CݷbOG֍_y# SUˏur[deS<3oB>wY_*LS(Aw8)?K@"y5Ӂ+VMڹZƳB }h]IR OM>4n%Nph|-eVt84'*,4#>UɈm dC0r˭<ύ2=zq=мҸ;O8<%ԋ JIH5x3c,^$St9~f `/eP%hF{G}W螁|x+A(<Վ5i^|?FTû-\υ2VGqguYqҺjk߼KC<36"T$EHm~Nv'u!R˿ XI"OJKzZ*O>F>BϧSʤ+{3Mn=jp.荱}}@mhx GVt Ält#$'85 =A^*Lԟ*]_+̊~ԛ`G;?+GבlXM*QD)jq#MFJHkJ,saR CW o$Ph:9/WU1֍ 8/`w),*IOT&O4&G5{Vi⣤U8q"kL%,P재]Oʈw4~A YjS;á |Q諘Zo39O^ZpᵫQ5Vݪ7scbxY aXM/2S ,V@V 8\zFʖyHvs"oPY7!~vl(Jgw7v`+z)@Wb  Z Lǜӣc"ZӲɽ+V.G'~I .wlpCD8Z8kh,%*w߼rm@Ӑ5DR2*reS4{MQ,>L` /(pr{Rt(C bIPmu'UZmUA>hWR7;yn:C2E_{DKEbŸÍ]Et_FS¶_`%~(wJ%Vge} 1?]nŊ(q 0f%7j<`ZDT K{.*p;Jͱ$!h}۴0Ppy#Yq3\2\fXw%)qZU .-K7jX9F,} qy jσ2a].lhFIHBKޛ`$ h&oGj9 6US)Kwqpf\laޢZZvܛM)3V}M"zqh_AzCE ݼΐ_|Ot]y;d`_99|Rˍ_2/9 B"ـU]d g 0 P6 `LJ0ϤRG[ׄR'UA_%B >Cw Ѣ>/9 :Оd٫DUM0YAgąԊ%3Za^AYm^)A! \hm1͚/'X+zRKN45hpUϑ@(VV.ެG[!rLu\x!)3b3|t>@l/D~|a*if6jY±oB$>#ܙGJҞe~;vƬl&~xDZ~$lBvQ=lzYݬOG#-NO@MxPHS?DO !UJmv؍_0@lw(v=g=)qFUPc(LC񆄿ӈkwҨPNG ~*IFϽ(ժ+CA90{|Eђ2xIkOzH$.YzҮ$-., uS˗#]QM22c$i0{X 8H@JO+p4KCJG1ڪ隸w..;[ k|6Tg5吉lId4YOJstEݔ!f":\G/Q[kl1^ԒJ|xm g@<[9齁FNJn<Y4D|kِOT"gc',L)Ub,p=apT2G_(;rsvj$Yt PX_KO=FMcY&k5ҟ5w53OMN.6 O;ct7 ,k‡z6$돱 I8,;܅+8BӧiLc8Q]ry?\?98o-W 7l7n5Ci}xm)\@ef^[~kTDsCDBn bAAdv034v%A/ b-쀫a) \$h׎O 0mڦe( PUӲ]5uh@ REXhoĻot@B83z!EO4; sB8E Cydמ^d=vK,rp< plӼ+bh.$+d`q\\p>ϥ axwY&5TjsMP2u:ǨwkRHt۬-8j_W6o fL2'L;e} EISs(2ۡhft/ޮ F()Vl_^@^Nnr4w&89}%yJXz IeHM›~*ECᵎr! _ueR خor<~ H/v>щk*7ggw¶=M&udG<3`c*Jz~uɛ @.N 85UBKU`v|U% ;tCʷȚ᪮ݪ( ڱý\E˵{Bʊҁ&đǽ21?4k?6xcp?<3lꉗ]yhzq[-Oc!{ /W1E;/Y*$a#CGh1d|~"-ܯ+`oB(^Rjږ=]Ù>X< Mi^G`a.)i1Cdeu~GX?cN|5n"oT vUڀ*zkĵF탈E!bmVJ*FmȘ+aop#$Z. `6 hX*~e<)L#dv1{_)Sԕ^)e!Q0w,lw5JكLKlg3?IF'AdUś%RQz`Ó/][O$M|V=0֍82u')tqI>@ ubVY"1=j05d^x|Ž!Q#(‡M2R*ރOo;GF=!զ&?x1<̱$mc^ڦKЬkԋI6^sT26C}Hs]HI0~AJacz>[f{cSAk|MX8D߹W"ޠ?>'?>!2**Jwn9$. P̨M"רc7Na#3#2,j+ŝuEo/Bg9(ü|ib7[p[8=v89c>d)*]fC[y1iԀFP4\ԾMxy0N=&WiJ䦠a,%B5|e/u uV> ynHf?aY9;:÷߇49HD8*3ȝn)z0%}. >eʷ`mٌnZ&Uߚs Wo#-BnWb6Gk/U/k1wض2ھ{+f|ZR . wwQ0؍pzqzazuЛ f966zSR>+W.rM-/V#ў:QصVYVS L]*UXFS,>h/J0zU_7F^Z3( 1N,AR9EE*gN|.,T;4A;Ѫ'PhFSN[_ND `7Ϧo R I'=]?ϒ!λiYڃu 4y z[0'm׼H s8N]5és-jSP(M_G#SlZNgqEx|(qW8)A\ WTa֔% չ^_C|=AX}s.hNkul;Ȓ%k*ؕ98rJW2$ ˽3ZymHI 'zVa%!,?Y̒(Քߣ[;svT[\-U0B>?^Ȅ q0-t,n/c7ށ'b R4` @9n{\~h$ZxE%TI (N|jYwI Um#)a@-87KrRajW{ FVt,9n׽cV06`IЂ6{AnmI)BU[9[BcU7l1Fr Iqlm70ڻ$ϳwxF |$"dbԟǻDdxIWDs `Ƥ>GwT;mGj!mc! #prCpW$a2@, ǟpseM7g*M=WذKݿ"w,A"6k37Uv6R_1<S^ӎwm<=f-xɲens*V4!׿wܦy"7WTY_{.DF)f*DV(Vʜb|HڃqܷiyoAhYPԖqɟ$!E7.PU2㇢W?RAj/~CftU5UD®E纛LPD铄DŽc.V1v><$|Prv}!%:5*rylw?/=?6G~cQDZߨoq7=zzzsܜ7g|sf9CnЛ3|SϥzK Zլ ~Ŋ }ߒA̐v66hͷCmM@?-~][޲#n ٮd(M+ N757\74.nk i뽒[&j/sI;xKצg@O^x=c=6'i A`0%+rK{p5dn4v0خnz[ӣ ו[#o Ѝ{kd;ՀpMacrë3zp5;Hܖ ~`S[k?GF?پ JNPo=C?ZswXh%G|E` 35tU_%j92C(j ^a=r&|Hϧ5 O_Ûr~=_G맯ܔ+#*_nA~j|Wn 2еgM^߻rJ^sY{~]~_g^; ]~7G6֫MPNe\ܔ o@~׭yJ5#Wy`T5knx-_uz} g*zUse W@0֟[_W?*3O>gEJ`wܿrA? Kju}_Z+?9 $Dz@z^W5*r~z˯3ȅMM? <C_ i4Ä%guyI`C?<}XӃ)jlJ]lt_,~\s^ggֿίn͟WE_/eS)σ&Zss9CR 00? ?emZ"6#]unܮ](X!Ыv}WM ]bglv(ZA?]o.!0蟨]oRgYu<~J`еl3?]; Mfz⪱gگu}s&Ҝ ?mj?AWߍ>0972w%NSI,$.[8,qnvi)<;hiv+1[ >DԐyRJybCnNBn(hcm(D+QnƔ$ɧ΢o6q=6t_-}wlU*0WBYHx;X CYI)gxA9/Y[O $U]r]xu Aa6U)s|]mcW]5t1Z!C]C/~Ҷ_sW߳$oi!|Rpcg1u _cnVᅤ$ 2z%|i]MA *tm2>5g؋x{5G"rW R=(*KrWs29p֟i^Rՠ.IpaO7/K8T4n Tab.[ .%ZD|;UU1`K!b#,Q'ba0K;6jzdwtDg]q#kQ6 }#pe(!ִ :5 Nj%WUR_Ӿu' SRߵy[gSndKn~>-]ǫsd|] g7HiQ/,^htNFHAҐ 9Yv,$fs"}uvvݏK[:yE} o*/Zl5ee 'ӊyc:ޚ--$V7<4MqK F_YԜ,QNɌBSeH$#zRZ¶ȊHjH_|:[0KfPy78(E6bVɵ|Y-{eXxi_ՐL(Oq|Y)dTCyw&ɽ0%O;p`q.9|[R$9](SnB|g^`qZ]阥{I#RQ# qڐeO5ݶol?)F}~ll;ư?9eX…w΋;|C,hk|Ĕ]q2 aX/kq3Po膤H,Yڧa6 r(LY"fY ,BhCp Ϻ#J `wuӂ@ WҍfS|H^zNE}|Q. :l 'S8$ج6Sxo"ڝ0ɗ|霷qĚpr~6PSȷTC% *Pn14'N1@ݚafRc&=h2>BӁC9?! gn+ݣ dB2cA5ObaF^ -M"O64(qEx4-FJt3@PNkm"eL$c\EV W`U<&=y3nl({c%6$0% Z) -S{9;! {GwCӬ$q\CcY;L8F΍SUVyl;`F &nGlw} %](xX㰏_q~W ŵev-^[1?7%~Gg2x(9<}c4Yb{:Ɠcgm>WkDuddDz|Nwy3qs3lOt;, X+_}ٙG*N)|a䥠>D'Kl qvB _yx >8 .$N/99b]ߣmz*YC?:4Ml}(&XA/B\nZlWXs-l9M%\{G0@O[x[S>=mnp5Y을(=;R{>vCqigwCƶFn#ɑa1=?@;^?Fޚ:FH baGk5bPKJԯzI\Pgl4jݝXx)|e ㎾4,c嫮?E2cPhx ]!)Cp1fzډZ"ˑ:%F1J-(Le8LiB2I}Hr-Z-$ ήE2JS iXb<"ߍ<x/^p8 lzy.9y&I2ڛ=nbCh_~qXnZmF}p'_J/E?uɕ##g\NvV_drJTw=ՓU3ٖ1cI˿V@;vz Kofxrep#S2m>jG9H rrk2U} Y [.NsP'@Dŏ* LD'A.W z/a!;0'eh0D~y:aD"a.𫺢1t ׃ㅖe۶Ƶ2h\{Jу R m*%-K 6Cְh*^W\hBA璋#Dwݱն.=8# ,A5pgP n#xLݻ+@kd˳f̞6#"f8TaE!)Tϐ+޿ʤW%-7I7aL>E̜LF&G.A6 {_ bX0MCr+UQh3z9 fZ?v&,~. '\|aYYetFI-SqrIPQt6q\Ɍr s&+/S] "fpH~9 @M-VG4$jMc]\-1AjQ-N ؒT]%HQ9`ÂkclvؔǙv\[ۇ` RB!OL@haĞxI|'o۰Zs]HdԵ|n9¥ N"WZd%+n?S2k)Y#aАcР|7D H}ӌixKc՜q…&N rZ,ߒ\/!A}2JÆ|}X=.Di3MRqAByfN\sk:F*$MD5(8~bAi%uh^)K+ Z*(ۡm5uXgٵBϕ7KʝO HGa Z#(|14!t!207q^S vvZ}VK˫>%Lf*.e^| Pד'=|LT@̏$v^&3̝%T:Fn)DŽH?M#[|'k@~,4QޖMR[buPX59MQ)F<%˔vGi~W"aVYvM އxZrRIV< YI6A{<I8>\q?LK&+\R5KxwzEhs=g/0&NyDn G a@H}|)ogkOŷԅϾl x#; :k)9 );";DjŎSB)*I#Hb:4i*.rXC 2hQw?wm2!6=Vl`ܾ"K9)$i-C+zf= ,$Vڵp{Jҧ;~~ֱ1!=޵܉`"Xbfx2V'͐lV NN θbhoh`Qh?^լi1ll޽J#? MuMmYr@27|~0lw}j:A4}BUrh9pبB[LC, ظBOD8ZUQ"..J󀐆Kix e\ÃT 47$T GS9hT&|fOSvJcRMЈ({Kkd+ 0ȥ-|H z~vD3UjV:~c=v_ (zc`b\Le ]t}uBa_2?'IkF ¹u3_٥FŞNm#2-we.9:QQeCZD4'6MbF hP$WrbDe>O s,Q >#N7r5 wQ-Q'a2&@E5x +cRD0% -ZgO^;%-ҟEs@|uh]֖U|AϘ2s=,I.aO؀{} gX.y`MoÖ5̓)\h"_յ|[  Q;zїuD6ѩ]΅U;2G?=ACOgNiR)nƃ' j5ڿ<}@TEg.`$Z'F Id r:>j!Z`m*hA|ޔ}$syK^}7_vt]\bu!3T6kjHfNdH_\Ge?Fᠩ!X#H8![~aoһ,y֏2\\ЍlY*ۣ{S9忬RvҜwZ<6.jhlhXlDlsդa*R;ᰡǕw%qz?aц#2}, P:fؤ2Q/;e84|c_ES.':.z'k_s1r^:d>QzʲlL-l hzPLm֍+i='V,}4c!O*T }\$咟^SY٣S+0Ya3K+Vil ?ЎI};<8Z?(,ÐHuq)\<[EkG;Iv◈.u5V@1?,#Bsɏnoa  <;:犉P%v)7 y6-9)اbioW Vq|_~nǫn1IO#/FQ,K,ߣVm-`H˜pnãҒ 9 ]\; VC@e=ixXrp^xU60#n)ihVۋp -2z#E^>,tx=PGӕ( >hXJc<+,Z_و-Hp :?X\wWMU89( ꌏ{N")WحtS H@A̾L&߾2mb/b8*.nӦn!cN_CuJ\2ǖQ !>(|tjʃTz!2)=v?V3#fR0pҴDyo:+Y^,%:.i!mK'TϬ~1 %RRXr~[?U8YN,X3h~ƝN7lD|͍jfkhF֥ߊ( UE^l8#5?]6엠r8s~!`$sW"2{>'06T9E iw{pOׂDNkij Ύѧ0Mo:ˇ1!#K֙vCQ!k!RQd71`9D,$tSAnQ}mnTFUSae1A + W @2&z_F*ږzFWa4\1lMt LVl (<_am}mEEEEEEEEEEEEEEœ%>!9 )9%,S=}_Wׯ`Գ6xbega3կ4k[;{G'__.ltұ_mKh?!@ء `ecbr $MpR{/d%B,"<|)|j/!?ֶ^ra<| 9:tR̥&Ҹ!!ēHkm<čpO\EpVz./Y_r bB01ɨw7-.=ojB& aA[;4¡WBk% s(|ڎ"t3Ag8LQp1ؤw5׬|kr|¯Y\b?ſ~}BR 5fc7a6܇l %%cBc-;QiK|zxg3Dc>D*Qa-QS:cgrA1F~䡴Įk`/o p؞yoƛ׸ _b_c2X@r뼇i R!%j+ٍv7\;o{yEjW\Ԅ`rW_ln39]ڥ&VcNxm6O^Nlx)J>kyE撲cma;E\)b e 6ZiiPj_LltcRN_ ^o-G}-VuPq`}!li.\u)71I4:]#č|Zӓȃ˃3h擈%#2ʠh'!+6]+t{e9);"[+1H.qXgkJJS 4(!yWJg[trɗxcҁb{rf;/!~yLqGdܽuqW՘}7x,-Θ  rf0O*z2=kQ,Cyn\8EBL23blU!PˏzJH}W)O 0f/$v0d|!SY^[QcjOUJ2' h&q^ S弝>*Z͕ ٭oW0oX}Tp g=(}_6#]OZK)!ѧ"9PTY[ZD DE5Zsd%yNW`u̸=C }_ޓ8hxyoN7?J(Y>;yiNÅhA!l^!kp5 h`@ڧiޢxaҒYN.I g4ڡn\u[q焐yUleHer4N'jN?lWD~}ZsV:c̓ n47⟅OpmUPr=yA^w2JO@/=z*ׯ(9:'^4Q$IERz1W^G` GQk:H3ESmJYLjS'+;!^ŜS@ZI " c]c_Um!Dj{v/5ȝdQaKL1@W_1,%k$sb娗bt2Q-jfPpr9@1mW÷9R_=>^qH%N'Rf ~5YXNHfsYSoM]|ѨqCYw^qa#*?Y /{}_'x3m%HmOTa Z{ JpGo ,G̤c͋( ܊p!㟫 ET РT~HۊҍGn]BQŮ8P"fQzY=h~٥#G)OHb4BO 6NX_w*'O1rlo1qa0r˱C qcճ < wxXu6G Eid,-#bi?oaڔ9VpHK36yD`2]d>9V"Dm vT!fU,58 dUj#r fEJSUopnH~N&c^S˜{.-Gޛ5}EF(+;w\*6cј$j5H{/nI?94Feӵz~%YR繊6Zُ[$ߤM,zHkʿk>syYyg9لĻ,%Z ^:׭E>tLhߐHX(Ylح{ED u1l4$/%Mn^Ɏ?xRޭ^vn5cxMZ&jybĆF -N^7<$@ 䩘8G\a슘1Mgو{CV#/c$e -[EQ 2(<쨔&a uRDݍj Q#bZsPnUڂ ;G:zLTBC̃*jt,]Fy&C;(*kvoǨB+'f4k9 i̮РW|"?">t?gX35_| {:ZYpf lꪃ7ZEajQx_0+^;cyPLξGb63SBKDǬ|n#$d4z0vVxc|=eUk>[ao?0*b 8,,Uo<"aj2 rN?Aү:~wNvgW#vC>{JZ{.qKһzKa=/H)j;xs>mvgg*UlRl)uyIٻd ;iM%O1'I[ܛl6VI' (ʵڽt;Gp%+4+ɑZ?wKJvȎP``M%A+Wvˀ7ˣz9MC5Gj/3Zcv(ۻz ЋAN褍i>o &0K$\bXjU w-wQȂZaSmu ^̅GscJPU jď{Y?ZMH8pv}?V(Ã͚Җ6+?wd4>ΥD1;Dfhubi9x9E% ddmwJݻa^bU_kCdjҒᶺy}AgI%%GcȪY:FjqǃAT}ӧ/5I'>?C=oWsڮe"$xrW{Y{U SDD5ٙGh'r?:yK?sZI15sZ%_5q#rydD8;ϙ=SuwAػc˨G%m(ԙ)su~'mφpC}gϦpMWS2I{4V}q>ǵ0 tfkիaTRd]iC׫PSl 3K/8iAՍB^#Ծ}8hOBpNp<ݢf7X/TKF2iT^qW~.KwI%@آpY}^b~<RC _݉6_%\>̼!b.RjÍ.V:OXLJ{H!uqK,}uBSއ{(m!JX.ZiZyJl`%Hj~ WqHJQĂ+WWp%Ѩp>C!foqy |0|E{bK4GA$묥h*CԎZ.^b7 9 =q NGuatG3rqǚ_;CM;>آU\c͹yȢ mNm>٣/ [[_Q=pJ 7aOOHϐV8-d!R2`q?ng_ضu9Y2^n0-KMC D+L=fS/6Ӣ^fUjhyktzW壭1GIzuS8d`vיI,txCQ*C&%Yy2ϔkD# JσL3(Hb0ǔ<2xjC ?WCK7ro-S9~Nj>?X ghGqbҍ"QjCW&c(A9H%vJrUAV_`t!{@6%woa62'\];38j%ĺ t?]`2^wzG8W#뤆8XC3i-q R)s/^~~g7`bWo3Tw2MH~v}U'y\9~FC VKq#i\1C@! p.BJn:J'x_t+SJq SYa@i~zg"O YYzy ]}aڡ:b M6̄-N^;+l~3Zt\tP/Ơj7Hؙn'B)24rV;:CM8*Wл![oLMi3|ՕbGOy}:8  &QO;8>G@CkGw#rM0njNQZ w;u}R!ǥ"5VW죇3*b>^ca莱ՐƮަu8R 1kBtFQ0v=OH1Ug/9l-=Gi%r(ɇO3z6u߻coP . N.}daݜo4NBWa⃈ ۏMISR lehޓ@*EPfiuiQ#Rc}8"@HPpSyۯO(bM@`-W .:stjD4zD˽31Z>҂qqzKg7v. {=x~pXYͤQ]9[ RlLL_sl)M.<~?LQ{ˇ~>m[k> |,>ِ<ݰ-~ʼn*Ըp%Ci eO,o@K~ZoCju-ע"߁4)#%Ègb%K15q#.P)xB ApnJo`ٕG@W$91V+X Q3rkڙ>y+("AhD'Ĥao'7N\?]PWiOXĢujHF=|cƣ5i`0=H]h1(~0jھW˜ixٞL) o 6*u>Q/\{pm^5pԤ<#ſw, &Nѩ};InRpT;.a=BH*Qh~D˃Sa/1lw,"8Heh2#^ mOvےt%_Io|ǢY=$VV<!̘  53;Cl/:iOep &%x,\bYڵT]B-tQ_=J~wZmkdYVƣ|ںEGKZ&S(BBMaR]yT3Rd6[SiibLn{j}zFsL:Q]:jn|%g[3c{.ڗn/1RvbGg f;ͯ2i(A+)_ۗ,5ʙ`nl%U(/̈́6S"6! FtZ?PDW1%&Ab( u#uW!@gua"Jൄ IĨOv ɴ*oLC,b2+Yi̳y" cg=tXcVL LZ}Far'q֙ݗZS,ŧ?08IƺNl\KLQܳN`!SM; *tGǂ3by MpcCq>~mӄ,h=xDfX0cWCԑ)Õ1L+͂ȓzkv]brۊlk@u=R:QL,zY%."R *ƨLj([bU>* O?b%h?p{I)yWC& Ń |0qArY'M ?~ql"F,ӝuOľ x]IOGl健+0w _ǩXO:rQ.PԆlG0`nߏeM9)*st7% F>HGW~^i_GsWI[R$糡{"%-G!}<rkk J?bW+Q[¾x ^:뜆yޠ mxt-zkNhNT[l /#Wӓ}%ƻA["T\%J' Cy\U͸lqE( d=ؾ)ֻoD5ΣN}i@2Wm? N=e~m>lVT{lbcڹ7awJ^?9|NG+tU9OloueA˺J.N,Lܵ `VtT$scGUU-#̠Rhlև %26*2-!m{ }kE$k,Mm۶m߶m۶m۶m=3sVdTdUȈ̈'WRMHridEɇ9URm7b@LP!V`m4lY^iq3 ?_J6E$m B.Rc4ѐw欲<>K͎1nl;43L싡S/Cc~zw!lH=Xxn#b@=T{a3d ](bBcn#Pb@ PQya p(pwI A0b(@ GR{ã>1!@JDj0[ ĿK} n^Hj?rzq5eF(q@`y>}z*ĄuprowaP`Eap/Gyۗ{`?\k? dn қ ݼ)0l0dG00}ޠs3 'N`[+f^tt 4vVav@h|9gMHr >W۫P`\CI8[4v^Pt_h7n5mYS*lZڸ+hbo *p:|,-K"Е5 ^v\"f^yדͯO F鿵vSݳ^cE>r(L^\ח!+Ǡ^,*Qun=V6XLDsd7_׮3[Fch w'I蛐Ǟ5%T?BƝux6B1hS-h:U k.N/ߊN쉬q6;drdpNVuYM6_|LC/ /c~B3 0ѣ_0Z W؁gY0 L3~8=\RJ~4bJ׸TXsd-ŢUAfil-]8d1lihob(!?lj$zރp@ E%gUʰR0D5ҮZ"RAk uIKZU+N xpX0gK0c"K\~{6@ͱetKYGfxy͒dVcՑ *ApMgx[7okSgVl!g&65 Al?Ŭxc̾&5G}!4J:|_NӟZ4:+z)_~( X3m*tBF/G9čjZڅ.5)O5ֆIY,Xؘec"j6Q֞ni baksk{Fקr}/Q^[w64@0<=\V\Tv`h?ɠ\bY818|#C2Cc`zz7F ++);0rlY~%0vSSp>ts ѷX .&O_#0'zмT}J\Xr~s01 ;?BoǿZmm]|ǿKLVƖ|/-p5d~Ys7%~e+֎ ( +H%E`"2=Z<(tm+#!*^M+/Ύ(I3۶ox8nJ}'?|}MI./ ?:2>| ٛkb -8ot 4X G~GgDE`S8@'ESD/ EK{yv=[h|O5_K*?sxjqq{&s]f\fgygxeO][#WXֱ035c/c70-{zt+tQk6-:'/w4:7mC.!4<:Z/o$;7X |]Wo$kl{g9o /r\%wqtg ͧ6Gf/ydCc~#׬oOhlƋO) o3vT˯3Z/h`8haʋc/ε_oċo_/o+#_j &/)5'@o"ӧ_/7ocMl37wz:wY|dZy)4^R%oh>k/U{4/h-1֠P6XW1v_šs@gIZR9`ss,a*0.W14;2(iGT#OG؅YѠV!AZVKjY`=MGp: MސY(raؼp-!vvFK dF^`۶"b+iOu׎Ads^ 'z}r<,B|_O8I~=}(CHWF>:F7U}(n~$UڴO_\ފNq5q8~)zj/ӏ&?9E5za$wwf9@{\ydo""IzG#c5@wle>b r ~@"v+ZxA `ՊJCqۤ(4,l5$ěʿvTMr ѝ$EʍC\oȱC9&a$)q74݊~3L uWSJ46iu$Lj2\6#b_mtPNѸٜBeV.o#\VJ~τO 2)lRUn\>"xw[|MJGc@ͷvNW_DOlm2c7U%nܲ+${m)>$m]S/ܬ P/C|*-6G}I!aBk9;[(y0!և욇 `xY۱&_`YTu6pJHn)n;*\:;-*ȱT? _&. g/=7$~1@Rښ]>,?s%葧7Ӻ m ]N 횂M4M~P9q>AA$PIlvNUu@xDhMJ.(~Vw'^At?3*thkS-ƨP}ܨ nw㏣@viq7Ӑo~FӲ q ɮYi"wg&TG@ӵDӷ'6\"xb#좌l_986~Di[oU&":0:KF^`mq$f"V` +dސ ӇчܻP!g2u-6" z!59x5'ngaXL]z`AL^Q6N-0gKx)ـ<&JMQhU\`2k5UL$ӡznp|v;-O;4Sc'Vı^cu0M:zkbOGO?09fBb΍6Dۑ@WZƚAѴeGN5Z|ҚrCS@2 InET KkErg 譗 ђ2Ѯao LH|kW|*Mgl)oy;N3_!D`>?$ N=; ҕv򎃔# 7S 4pIwTk3e5!' \Jj˙8@@^{$%`!$LZskaΥ )[ MEMs?&_Ț<$ZL5mم%iUM1#eUuyO[$Ȍ_C72u|vU۝yN+>\~?Yl vB""ay2~矶\֪3mFȡD0jDs739e7%]|c׊詯䛺>PHZ>p1МͅOoO:Mvg xG1pX&Q@;/%-,3Je-=`7 &VGSi0W"7Z( ۶kHgP%@O-q;v\$5 U񛝳w-riTy3|MݥS fhIWȽHNb}D~eyazZ<#Bp??p`^Dd&z4lc5`H)t!Ja`z![KE?֦@=vb$%"1L</Rvbi; #׌xOh"o曲.]° $PgٗVLV(ZrOl,3B[qx>k*f._6yM'[ԁl,?/ 'q| >OiA4EjNy(C%n$H `mbx%IQ) r{Ycs7h7D "3ӁgGxE-U# ºZaQqTEYd!f:J 3qB\UZ'895/]E`,„gqkb1etHE[ul,d۴`[/3?Z7$dQZZ׾6vb1?i^.ǰ`7}}iۅd~4qv0A'y=6wG]F~‘btWWȕYz&K՘E1|msk)3gAtB[}H-Y!PldЮzSYleHIi o-EZ\NniAfk 6=**Q9̽ ͼӞ׋RՃʄq- v) 4ydN)#xgtBk%iC7dь}:5] 4XFv!J[8ZS^:^T]߆tvB&ZJ7#[Ǎ| 1fR>[L *b׷ ;E}TLޅ:V .ЊYK;@|&6 "tK 9F{PHmn7,s*hQ~c'ljS s=?#6z9 8%w-T>ʔv|mJ`rc(x*mF p5o{.>*E9of|f-0ޕg#;2ymf3Ty{[#'kUCj/KWIfY솭mZTVpYά!u|4MovSJX:hL}h%M_6[Ty ȐB%_W4R_sKJ>|{ȫSa/\*Pk)>.)Voˊ̺3v̧^t8?qjN$ߜsO66 ["L%BH 0"fN[)SQ&FZMhogk=JR:Q þo MZ٧r"do2,C* du5 _mq䒰"w,[QV$DK.C*Nu*>;=E]j9390xVBc3[v4)e4' a?N>vBmh/^\3lGEwd-m&\P R r/n;IN&eQ6O$~tzy!Y nL.}KR]&H]`y.X6Lk!u=?Nxk1)hTXԩt• WB5Uy|$F/;6 ֛_ AERcNanIhQ<$5*qPO/r5sC^%U{H!(i^ CofwnV(IXa؃<dل$ S$q˳6N5LLb1*JDEng595eHAJ5+%L#06NUӺ?J3xcJ7wsf;!cs`NԴ3[@e*ItCY$j2$M$c|ݵV(5U xɃi+qCPY>4לQI=:u7 QTGJ\{rM2ȴbNޖ K\R=>! l[$4m{ۼDu)HB$``CoڶnGFx& /]}U:!b6la`^VZ!C>n`-R EvĻ("ch[+SpܕmZyWXr K ɇRM {PjY3{X %Ƞ0bRsC#IIcU53Uw]TR[a+'OgHESvg]G[rHG38+h`7IqfE3 SJٞXEӥ#vwMu8` ,N81:4b*&t٤!.ꔘ}lHɭNpb+"a-h9Vˢij"b= 3Py@T GgvP./:xR1,Ya/ - yKq+Ђ7x:o'>4HʜT{h`W6i:\)1BAft6kxqyyZ=& \\"7l?c]SkӛX~f7jG V-ЉK;.~v(S7̯Xn%>஀}; [Zl׌}jrX$&X^A@7op -!~HyxoyL & J,Ea"[Q2i._Pe?+:s"VQ Ht|MHЦlK|N\У~p[ٲ45u.jw*QCwHH:{?e mt uCD4zo eIsHk\fG罖ٸ@Ju"}ْG|Mmr^|΁MPFLtYJG(!7 lB 7F3kA z3OkҠ.q 9t'z?4v9Vx:TLV+$v9U\+-( Dm*e=?Yl *TIRu3^܉3PNݚ)WN㘑@PDpT?#1ҝ0Jk+:u[l;CsPCZN0RwWb8x; 笇hLdE[H-+H6p`)PP9FRmaxh:辴k\q(+B(m*j6BFkK ʥ#pM2d3!b]46f:.IZ{ V&AULoYO l)FWkf DrYŃfCv. 'x\j8uYY/4LܛhD $P<%;3(K0Bk ڱ|?!Fא5Erӏ>>ڂ$ԆAx6^1 }3;Wy!lG Te} B^:W(Y9B'΋ Cv%=/V7QI5^1Qkh:7[*{x표ᑄWCF$4Cv V,$ O /^1ZPW;i=:QYYJq=&hLZۧSa<,@:әL{W;Q.-C"Phz!i}ܺNU"jA#ZG[I]xJ0eRwuʡ{u>r9_O7 ʢg娡z\D,&0I7(E$F0nDqRfAWƘ_`פ^.C\ͩ5|4k#c\eWƪmҁ 4HO?<Ƶ.ӳmN#ߒ*H2D~fO\#M%rMܚ;H(Z< Hv#Go,VDsW6D?mܫ0FýCSqG_xu 54`Hͻ܅ˏdhm7AOr;D b`t %K=5V͊!KC /mx/XyD;-^n8~~|h76m[6ho,r>gÍZlD-gH)Z=or(VVj …eJxɋ%$ n;rML72+0헵zX !21,ο&-`< Ia>SJ%Ghfm0ai6m@RJ * Qz7_>(zcܑ/sw@٦.U{EXLESks~0fM} Q5X<~D qRzsoY nhTEʷfH"bOY*pD8GFHL?Ygû3*Wg$ cj(Y(JWK$?x`3^UfM%ͪmI}z/6Һ;) G'2\Tw9ͺwY#JrH M)VGms]>";XW-֑*RYm8YXlϕ?NϊVu\oTކJ6%MuX;s*w9HvEeT>T fkE2C6\(nz.a;;P\!'WO:irs{ڻHA,07'ˆqjnI 9A-Yu \9% !p֗S yF+6enu @ 2-voƘհ8ǒƊ9e5]إɳopy9(m&.} !(L!Z7Cf=?t`e=qy:NLrZB3:. 7hKZ1ೂ4#,I,rfQqx\ ϙI̚ 2:JH]oEè(YLXV91n~X 6|ݴwn87Et9չ@t> B+T)4 +"E gdP ch:C\IML?Y=F@<\lW҉HT *ɸ>_f5IҽUŬ͘@Y®/C08PW$&gJڐT}Z}٢ S k?w8޾ SW>jQ Xx)Uq=Y??ņ8X?Z}a cJ~ d kb4Hq~*7&[T-#hЉ ,ͥYJ%}倳#O,%˹D{DPd]ϝ҇|PȾ4 з-;6I>UԈ$.~l$ 7(c{H\́[dBkNN$Lh)ME11j-&Ƒz'x"ypXԕG=TEdHL')vu*ݽV+o- RHl1%je=P keGx @[ sriX2j-:j E6ZN@q`X=q"(Cʦ]_~(M5UZWC&g0ɽxbz7sT+Fz֦@(@#K&Έ"9!jHH;Ƞ:(k.jb";K~icOxS_ih'99SFLB$zQ匹[ (χ8=F$vj596~6W z%8}EXFJm-fM/X*{wEl59{#NfRr mkޖ:2%9#e:"GQ01p7MwW~Dc =4jJL6X ȍphѤzq69G r%6JLh>qD~pܹ΍GY5LQ;9HVR@.dw|ٹ)vRv}dvYgќ'Dkw'I`Kt.6l4ݣ$dY頭-»jwر?*7 .UX},S<&%{[p0,'ykRh9"(8|R]DԤ̠Ʋ})1كXE+LW36&WME]tl hX3,K:ρ^EQ'VRFDZozZ:*6Չ+ 9q'_\9 KW Zen hE8 3EVT d.((؛YcYhc]a_%%bL.qǰ}Yj;UmGWw"D[E͟@~ [Yt``懣)\ `\}G"|Jlo $T%ظ1LQ-Q'ڲ6JyQ2"_5eB *gʈSEXّ}9o(9b:au`@pDo/_2z¡{ʠu|IY,J)PZplM:͑5-BM5%ŭq$s-JC$#nC"}qt<3K ~27&fvXSs&5PmDΊFM,z:m&[8iNp2Z#SXPXNabG| u@\BF G#y&л ]~0$ތHx/Eg ~2fWUc1ȉ48 [fC܌q :Ȫ?= H-?-8l0ϥ'x`M}+Sh6&~z5ݸUuW~z=ɨX456 %DԠrvMUQ_%1 a *&C:b.ZPL&HtO?zP//WSO&J\;-mtBDv&g $3u6h;CA9'-˷0VRo0д{%ag|Υ0ϫvS3%/mUc\We}Mdn\|v>f"QjiIԀP 9@!(5q3 =Ho%.F h!q@p9V$"WP-,O6S0&Ks hM 2XMuCZTsx̞1PA_,[bKbu$~?Q71=!6xlM=6sp)JK[Og>Mj6"ps }h| Lթm$ouss {=B|z(~2;\}?Z{9 B2p?Q$aws43uDz%7E$fޞ#Zh=Eu?YeCwy6ߍ9b-C̆s!?#%+MH;5eO@e6K (@7V =2`A#",':dWYђ AKtl'i};WƵj4ͅ)m+8*})XV]KV9f $k 6Zƃ,fTջ,/`(.̲~H ,aagSUK:\wF_8+K)"+ua:Gzyyp}3(J"&3пLIg}QyGY/Dl 'ͯRTB2EVssrh0YwCm7GԿdѩii8ђΐ]}4X#[kkݬebl`Egfde`O?7 )ܲέٺelmhhafv56NcˮZGk1^g3 㿪efe3 !&`acoLvW^olBnnmc00::I@,8l@f~N_?msgN:W5{,o/ }I6hymxBjb3%fp!CyxxRyrȨiIiCA;>q~o,!7]6r|0nPptǶ@F[rxcPpnMR/npT$X_\й3tVK{~r]cq}-'zmw$F7 !˵lw.uWL: uWJ^ `M.w귡ҬhvKM|~;װ&3k Mbrzr\*4׺>a LSӠ4* ǽ@^N5g 0wn n v  n엀 vW\!T; kA?|~b@nO?S?x?.uB-i%4KrxtS@wtGw~?u_ _~M[pWj|ezuȟZp>5pczu,wz3tAͧ_h^u6?gUS+FZW&&=@>k5uLH`a`ߵZtCzWKvi†[f\YFp{y6'gs&F،ktU8|mRD]w,a4M`C[NWEcaqR&n^2=bQPVY/L=VEBoLM hAB\~"lf$h.B6$[u ݼfD **ARG&8O y0,E&J{|)-*0ĸՐljU!Wp2=Ц\BҌ8: :ɪ om H obR 6aMȬh pZZIDt=\xgh2;- (VjEB/&ytRh*㜸W 5Ϊӱ;ѓ5(?|e_wĚDU#]Y'\씎zJ: p`'Dm '&8ՄbAϖ(# Jԇ`#K)Ni{mmԊQd֞ЪZ׫LƆ<(H\0d m]E#Yc1@H^_hiW }Dg(J+ϩF~W \/>iZ "?c֐F"1&(l>*BY?h,?J0&H$EmY"7K;AW$|OulH߿t:c99*8֥5iބ|qw%PO;٬VMw>`n)-$ck΢XJ`^]NSh\ wh#4YtN9vb?*l} 7>7lKW#TB:x¡#B_I7˱=\:3NZ׶y.Aq=>id)Hu> 8+ɄJxGq4PkS$AC- f_Pt(}ݕuWBY k??FkS3 `1>xi{S⓻{M]S/i3г]yL%xV/-}Y} QݶUd^lr~mzWfW;WTOtRf%נuEn-]Ӈa" '\͟2P!LYo"|S sv$$3#{m,[;sr/hs65h(oۺƯIlwF!mPßXYCu M='3"'1XGΪ΀E/yԥ2v>pD2L^5uewQ U,1p;qX& )V/Zp@;'{l}] G3O@2ر˱K^l? Ut^Mv^ KEJ#=u9B %[0@MUh,tn?9kenD7ߘ IJ q_Uf덲GIUOIU^)_fkty^iC4L MwouhzT.'`0/P#D&ȶP偪$ <;!`/'HRJ}h|wM')ZtO\oiѐE0xU&5K)bzOLuBz`Եc%j PZF5֡S޼g.d[D\粅z4ߘŏ{oޥ,A (whfrWǫo(q61r݄Q9pg=`5lq ~D|p' ![aI r*gǣM\*$1{3 ^z| RvD4&O#z..lJtsmOŴgG=O=? ]C2+xG@{WO;:B.?ükM !1jU| (̤0gv*angzpG\WdQ5G>ePda̴{V)CKVi1WҎRć=3pBUpW;fث5Gk/#'@j3T!]0XD_؟Bp&8~l\fngP 1@ jAR O6emf[ ~sAPXPB (fOL%mVr1ѻUel3JڅL OD@N<0y?1p8;|ECgūHo-G,cj=/_iOEzJCD^󭾧Զx@(^%GjdXػ9*ri)ٟrA_e/b́0$*u0A"=Xb<S[EPwu_"{7cj4|% tKN}ls!#6y;k 0/.݋|flKٱq{09j5ϥNomʽԮ "/7wˋWza%kkU V GK@ lNޥUj1BA@yNEC5{JLFO޺|co>f 73W)y傼-MZLn#K9Iִu6>u,yg+C)/ 7D'8'q+PW OCSSq`z.^p[1| L>Àˡ{x] )Jne&W~yPsj[>,-s72i ,Id~2/FYV&\'o4R FǟͿ7L"Fzz%"e(ǀ>=r2NS'ZvDߘȆwk D9L^ÕO\`% J9Hn`W{n̷Q2{EGxbC v@h׷fOY:P3ձ[1EOBpa B|ԥ95.т ']p]Gagi~~t a CZ0#C vwX>Dq`(It~08B9-+RXs`WY q5?g]g [m[PtG鬏*ë{5!ɑ ~FءIۧxl˯l89EHɎ3LLx IZY}Ef?\-ګ=ktWpn=ۚKkv 'vg|nZ2 Em2<[V@UmMLUoY\w:ƹ;ZxYHh'd'[-Ȕa58Ϛ=Q@{Y ]X ȇ&+V@e:QWvR]w?՛Mwa#Hr ok>b[W\2QVw*‹Ǝ+1)LQo Aƴ6e MEU<1|! ؕ efjbUa2 }sŪ׵x-8(/ Mhyۗ^iNJFym*g`h,!VWc {֞=|ty۪xGfk`O)aa/~3єt< T\Z _A?Sus[5aDA[52U^:3]#*s$͹+z?\_ཱྀ ={$IT&r|iQ~ Tg%>SrɅ1kͼ* sU(CnHQt}ej>2P>-B8q#UrSlNEM2V b@2+#qP )GhͲM+2KNAګ[@]<Э_탨+[* [.eO8;XK`ke q M#3;֩ݕJtL5jI5BldG@ *x(xD mk:lzj/!v'hʐϲg H ਇDG8F/|'(lj g/7}& -[]R\ )+W,p3(?=9ޔzIcOX/ ;荍K^&nM#XڛAYlŒsS9_iL4?ojiͯl"O ݖ|GCrxl\R  ҳ@Dm7xb`),E%%-ʪt[x^q!qC4 ܻQpl֘=TQ2eXl}M(%dʥSkd5J&y C1O=ѱ1A_ݟ١wOŸ 45_nR1Y~0 k% ?s/& *soyˈD.Io9Fʽ=]0 %='_pD=Q޴6"'W\JDв lI艦bn԰lmz8έl͏^砮ِnX["RX((qOuGs D;ٯ"Svr+j"gP>fp/81[ҩRz7(ʷK4 l8I8VCJUNН|,q[LP aKZWv6Rti X`2l~mN 87hJi~_:̄^kUX]j24.+Í! e;Er|H5y:[^9IT7E<42dhFLX иj{U}}F!? 3TLoHx ءm?X@C&0'<`yU9W}D1oTHCx1rRT7nZ\+PqIl/^H&PW&ls*1a33J%bc7|_&v wrM qȌ9O NQRRĬ|RoL7J[꽇WʍZ4~ `U )z&e@DY@firln6^3Z srϛPL3}3ljh>j+.`KHRH]\ ǿg=Cq dcoP']+Vvdg 7_t ,Aб2~ :Vvv6Abwg pbgs` 8.%D+ ؁A"IQ^n}{z陥7iYy}]F tC(O .(4 v ܗLKO@L D 4n_W"/ξؖC@6Ώup (' QF45)wDLpx%Vfz As-l Eeuyr?5/u_IO (T7ң_w;{hy~֗ fGޏuVZŠN/W>}eߵ> ½o0 AG<9lo[Q0uߜ > 0ɱYX0o@&XnloAA|'c|-1v!@x0<@9b0@z=)ktr1T9? uBn_eHy Ko2gY/ ?B y;L_2{WqA7EgA6?a6E |lPT]~h}57 fĨZ6ȡY~`smE/?oLZ?6:>>5S_Pܯ˝_O7&Gc9DGY~a1;wNTu|sBnjE HQlg W=JG0C #$+T#_km e`I.ke5N0.ԵDAg+3irCe~%0װpT >ۓT^$3F3IhZ8SjY1ljO ةpDpnz7Ol+ +JX|6LQN) b`*E ]Ftҹ\^&P:۸c]EtGN7EZxQԣ(k#خ8ݱDw&E &N縜z/Н&Ysnzl[Q,e85hOC*vQ/vnwfPŠ6sZ3^nxNfmxV3R0;=J@z2 Wq>7az|Hi]pm2dK3' 'Č<@29ο! @ft-LAM^f _gIT]j!J17> K=buqy&-$O}x\kF+SUcH g{&? 2ffT9SxD`pQ @ieARy%dJߛA^06Lz#qe!ewcդ-'WU閚6Lz82> rMH}efQiYd mCQ( Bi-.5]=8V払fK@Ʊu%}$NgG7B #@TpQm~(fP|X v} w nNTj_ZE4V?Ňd:v29iFm'CSKRE*39|S$]ȷ,uZ֝ۥ*9g2U'uNVx ~Wo*Ia3k[ 5=. 6"K: ݑ'v|@܁{>s)ئˢq׺coTL&]zH:JT;Bo$icǪ[pc2,vF)M*BbQX`"\m MzDQp-)8NyG{k8|{f SeqiFj;:J;{*o$C񱢎TQIjD1F٫0hpi{fO1TAMfRwZ`T5hSGIN F9Xgm kП9Pf6ɗ!gk..RN|?Vrs/ґiB u~r^`>4w秠빯]%A,5h#nE^ 𹈊 ~h!H&sl$bFOHJj-6sf5g_K}Tٹ,BZnn{*p) l#yG-̑Wݧ1/ eJM[<o21oܪJ BԞFh=芩;!9`,"xTLjA _1QQnЌhv=) oyN*AMhTr^ tp<37 65L?)M9oS#~Ժ,t!ymRJ؃on{] <n+u֣FMs̆&UbL L:js!#2DzC k4~Zr5e%.Hn9 (i[ʭd]w*' 4\%ͰŲhPuut6(%hOXӤN$u;L"a2a$OT֎j6ŽQqLyop!RLMI_Jڽuj_Ց}Ysg,o oRr{E3V{/D|rrJ@xG,;*H1J%(ښFw{nx(39gWhk\0+/)*xȺC@}#؆j0ܢZw2,DG>&qO(.e^>Jbi{π٢еLӮ ?fH` `Vq/ Yo1)mA 2pzfLZb!OxcuMo:?e V H`/ԯ)lj\u%g2JIvv<rcnn?y (xIx@YbAD8~۶m۶m۶m۶m۶mιsg/~SrWcUg-|܄yo wyё93 {i.m#[;I^"Z=5WrD'f>nD ѯǟ&<&.GqbSgnMrѹT vxJ)j੐DC~H yT*IL7mz\י }S<lwPM6kzhbtMB`6@.\~?A#v13wJoba-'7 84gp=wU'fsjvv"Fvܳ.HC>w_*#g:Fh{4͝sBwLZ,ae^?{ya/?8w17۩v`eH(]r6Fc[L1UdMY )O-!iA3X_@^6=ͼh_8>m67Y:Ujx^UrN#'`| zb9 R+7t+L_L-yֳU^;]h'2-r]i3*1h25Ļ=LdQ~ ,-=BOws:)Me+s*\#e䶛,luZ DNbQ)C j3fyRU%,9`SW wʢm2wMsW/Ͱ E t *܋"rHto"Pqtl|z %L_*,GkZDvhqRg++bTAq5k{%9jIҽ-v)U|޺YCR X(GC$=&iP-AN4bActV K)Wgߗk",_tSj) UM$=ƘWk?/CItqd3 *C-@qruW \I;ŬT-h"p+T1T"J:ou^)K٪e!AѰ(MnN51+0!ш nk֥e]w#.$x'2yOޅ^:R 5F ;s/n/ ܝrre֚AU'8IVuBV3&$b. I|0jX:CzV/%r8HeMԦ,;X"~y =K7MT59\ɺI2>pȿu9%v[LFZjFը棎sQ[x evP쇜(E ^h oi|| (L9رb cɱ!¾4H J4 @kٍ9=+rӹOܟJGfZT&Y2&,軛螅I@/T{sFfUc#m2͢ ~_aqH<gQ%Q @]U!LLyifiiԑCY;Av 6MϤTf6Rx\+|in88XQn !?'BtzMs~T8 84,"›2&}4=[^MSg0Kj,fLx}H_Revf|(pS%Andd~X3%y(v7]H廜)QagV- 6@JyKݹ78@R'Q_PR80A$AښqޢiÅDz 4 ce`&}G`&G0HXhq+cf"/ Cw*STV4'Le<=&s=N?GG\u#Ioh`q2c ȗDҺD=C2NFZٞ+rj8CGsA4)^:sG|}!(%6-mB!.|m9%F\1ϒ%rz'/̖Z=e~Wn2Tiwh‰*X[1N?az+i>]3v7ŏ BQ}pW;4xXs}r0+'Aw}\p9Aэ2,~}XC =qK!`p<B?+Wm ? EAFYXpWX<@4-2 X u&_abzht5k_8]bDߌ^3yԿȘ}CvŻAzW,OU82}a@.Ͻ-a @A7!'ZxW-|k~h"aIJQFC|SHb3}4~~ޏY0 qpy bCE4yˆ!:@udH cq^&t7@A}BDݡ`/|BS-'|QH<ӼXXY\|@9(3b+1nN4_#Yo@` q{Kٻ @51G=&>~j1}/Ȱb ed)4!cG)_<';UIE:FH'%b}\ºب&bգt,z<tSIÞǺ>wQ4YRl04 J5Ugg}y#HsrruSMA5kzwnK}!1`)Ha{ȄLMCz*wS%cq6n-Hܠ$Y?$C08kUv/>)}Y;{Dή*H=MH\Jv~s9ѦJ6a[=CJ&Z*<ʠ@md6&A.alv#G@U[p}౤[/!6cmK} o3`[J[>_sdrַzwx2ĉ Ee<4;A:Q{91?:)sNSlӽf+P2-*̰qX-5n"&em]^uRNq.$Wj?\pT(j z<9>nfD'_Sg fciBAܲx(غ*Wk#"o0R]EԔD9>x/H6,T(ZK $G6̞g=5ƣF@QPL(;tfpvؼe m&sz}}yn׭p!3:Z"O4{UGPpT0FkCaH-_.E+ P2*MNC+z_R FSB|&&|VPz30/322ge??#+ag?; ll,A O*(+B Mgz<ԣnjӣto}j݄LX+ 1*A/1+%'!cq"`ws|~rm L G!;!ˆ .،Lw>n"R#AVKT /OIBȰ 3VB$cnQ2%\7?^~tOF^OY#d(`O Pv|y;c%RI8Y}Ac(&Tb䳦 DEcR92XS$d(3GvleIt =E^cnxXHPKLR)Lx¸"c&[3.@ HFrCci.ch)c9ZOMĢ 9x @̖G XCsy*{ȃU,2ړ.Cu+# si)@si>pԒp$> 4`aZSvbp|rP&$uj/b\0(*$-Yg|;*9f'l4hvbzu0~oHt[ufe%fhSnz xƲQԔ3WIʋn4]ݶ/] T-):=c[?F'FӴ~*Ehz۵(EG$㍩-bmG%u)=5Io|NBN3#ֺ:vvش햧lܥԟHlm)Zg=Klcmvԗk(7o\Yۋ f>?Z`zřm4JCT_Hl1fnD0Ml'FN8̴x=RՔQnL+w* %G9P0BU;&r=f[rVY:۔N+/@?C?l 1s[`cT;tabmmLgdhd]>|O\ؿzrfڑ8IDZHq*qŔXh366nkj3Ik*y :fs01tzN&UG5Ůw?cNcutLL8&8).@8Hxqpm@hRrL/wT}s m#& O6)_IӿVjb w!q YmD҄@̨?Ƞ`SxMĢϤ~87RDb*iBaЊCo ܊-Ő'19ܸbBS&aF迢m?*T|bHMĤ@;fQ1ܘG Qq@'cdAHxAaYԐ@ALCڰ PYG!Q[DxDZ ?@}ҡP&~I  <|ÎYBES4ߊ ٸnDSHd׶KA 3 ̐P%wGp2tv1:2LYN;Tnsod/i|0t~.DY.&h7Ճ\|K~Ed%8|+0E%}󾇯20L:+E)|bsF'봓yÁJH'6c>Ys%){y!)tK+UIe-@V7eʯ*.NTSy>I`)xx5b퉯2ݗ{]yUcbjK4?53CK'r6vzv:Qn.[-W.jN6bYW[ذskf[96#JB8va8yĚR徯'i",HtarՖ̮x=L^ch}Lr4u7nnDOXߴ<Nj gQ"wf5x<7^3ٵt+-WGe_ӷ;D!A'l31ZxY"}x9~` $^l*FGv4s,!XY[x}9I#ROGngz o0hNWliv,sbMd\XV?#iuw!jV2e^~ZBgWډ-c)^<hԲ,o|Ո>uFB5 ra\ֻ:4a@`e}tǾ~ Ȋzn˰+J!'<;At™MOO(|sXحT}@-LȪM>*½bgjZI Ggxn.gk aurYk_*Kgf DYSoN nܜM79 .>*^O/bT^8N$MJa1c72Ѹ>e}!lONK{QlJ,Iᢡ%V4Ӏ;{;ٽ̺¾DÕr8 #;)`wh # 'ȑv#M=".7Y.7ʍlh}'rF^=D L9; H@ar"62{ dddd"l0DGm`y| Z/PihkG 6q9~!)'B$ha r<nPeW7:8NBFV:YU]?~<"o5t0,)mulU}-J`$ZYeoE/i|u_q[ O)Wau:=~-y[*0ٸjƟ~:n l3# Aᙲ?mLyr"9HVeu ~ W%Gcj䤤)uhnk݆oeҡu cD^[oAiSi<#: I係J[W?لO6x}/񓙝?[MԳY@@;;yo '\>9S~O:V*;=I}sx6}  `aEqQgǧ >le~AA\KLFo0<9j_AiΒ~/] |ue7oiWǩ]J'iG 즸1iV:oMӕoϭSK!Et/t2ƒh/o`yo)vկ͗ ::?cn/~ޢ>`fm_?zW2S?ryP8]m6EXP?g Ug&dFi,&yHu<)[޶ś_.X` !S/T<,3g/\ohI(NV#$T  x3\hKŠV˥f&n#Tzh¼fZz+v7s+)vs%3ƪVׯQQGz&}{ysWT!AO`LF*+<3^%# ɞQp\Ꮾi?Vy ]Bh?WRhpOWf~4 @%UѲݭ$ \EuEښuͅ#hGѯ i3r0 CP9tC;qz;>Q)^E܄_M&WR/pg^~Waf d  PC* Z3Lsy!!ׄ;cV 9,9h)ڰ]\ӛ И^(Z#i/"nZI),|ۏW9jw)7ךQyX R@6ڗkKj&#ᬘ{*n(*B ?rp{hꮽvc(Z2!0ZWGx5'Z'[s;?͢'/ :vD@gƨosJ[TSbR^ZIm?hx﷪$Ρ1C5!D\ @d?uMό\o7TעV=B23PdpCz v+8Eu"EpRoh$`TYm`>+:]Yൃ uDsGU) ZBHBJ/Ne)k 7%[MrY+Lf 76ޖ}WZ; gc.K6C[lWڏ_x畟Ð݈|dz wR5 rQ =.6Xj+՜#ž !ꐏlor6S8`Qڒ&Vu7P2}ߡ nW<7GuN8[TTmIm Z`km{43@¼^_^p5z.f:u=Pst.lޯ-P"E5Ix^Bun㹂1'S, h <B[xҟ8*(vv $)og<ҕK%$-b2(%8/ۭYmb^^0B3]/0GpY "Ao|0hϣRhB"h}Ԅ'`w<ȴ'3~ja0 ЉvjFоw:ڐS!o󉃝s8Ýtgsbа_IjжHQšF,Tj^%iaNk5{Dx=Iy3AG_{ LG˹@IlLJgӵd/UoW/#2s\QnU=?DA!*(IvFFBXi7]cD}CqfaŻ4<˃@Z=\< q$bZu˝;XU_1c˟1,AύKa~5.R!EѺ`KDZ]#ې΁\/}9/ b/u[n&d}@-HA;SIl2ө9_Fe] iJa3rvuG# mx$8opPS_Ҟ֫!s` ͉I" !KQ)gzRwϨ'^(M'7Afĺ@[' _KnRA48,P̻ 2#$JQMrH}3l]O~X2/ :㯞Uz}1w˔`{OםR5+= L-Riw+׺#$!|&4珇VIf %מA;dLf~K|L8 KV76>5>3 Toߤ lw6^\ۗ#:C>ݩ7BX0l+T6L8_ _c͵M% ymrb 7)ake' Lc+PACd B)^oxe%`pEb#"Wz;|9(؏QގBm!O~,i327I4rNVI_X#S!5pFMcڻ4kqTzOþ_q8A ^,XdrDS0AW 9*уQ;MگC-/z5[􀯏|EsN%5٭;si~M#F#^RN^T̨Pum`j/Oư̼ Xݴ8W9|c"iv\$ȫKf-qzߺfFلQ8)_bŴ\,= A׭Y/ 3-ۢh]E>Ҡ\cg2tjWcw&jsA_ߪ{pID.t!9!Mj،eWR VyslZDQUٱ\Mlp&˖<ޫnʑƠh!A߉b߸CNASHGM l]W ^I_n fJ7##=Ѱ*LM@4Q5c6͎fW);] ^4>bnVn|JTtUR. ؽT)k88j>e3~e& S㵈_v^p]J w(ؔpBQ{+Y2f^ODߖ,(T4N=$P8Jɜ'zx aB"3tawۈp1Hd\}cMrtɈnnu-R=oB]*JyG0uN&e=Y_J"|l/eЂ뉙['^v8ܟ@Yvtc> nwtz+2G gL]|lإC zZ Q% bw%1qI`Rv'u?dO"kC{zC`n[BN_dԎ'OU 2U=0PGb k=QfHayKBp[ӫxVp.yll@kb^>$(C*OO= PBzxoGs֨?^iHz,E)08{D&hNw%zWL)$Kޥ ih߃"R,uD\jl4ve6"a' h];g`EQʬ/4L$XE"^1MxBkp@WM 8CYr{F'/SsŽŵ6AV^t8cG߷2Dl]Yh4dtbHu |nitտr,@C%, 3utO OQ<::k: VT#{N'[VE& a,'B3 g`+z=vڵZw\\_昦&b{~j/ȸxvNYUf"O$\-)1{%Uno1$`鍒ˬleYKQ׿"MrmZ/c{>e ä:? # uc?_HFwaѐFK۟Cs)QKc6%]>*ɯ > jX8i,Iqf}XE`nn7,C{^ix պˊE&AǠSZ:Dh7l;Y. D11ZΜ-*/9vעDotbf1v 3zjiЖSh2;=Ht|y8\c##hD!S:|jfv_e.$j6ТVPisc‹$(션,ciH :,fCL6WEi_[$GQLMP(}WF$mŠ9Ʌm]Cŏ{\~Ǣ9iFRL9S_ |0͓dӁN^:[[?DWZ0[AB7qH̒W؜;"2^\T}gk(Ah܏ar=W%Zd]-L?EI^u'zy軭Ϟ~8c`Zo3lO.YVmNX r_t5aY ]r楺'iv&&~c=jvpVm.Z;& =>D0Bif{L{_^:kb' YyV_yo|@ ϤY۝d3n4Mĸ}?b&o_lUTV7TRlL(aHk }4VsSPŲhwE}g.ͷ㖴^+nSc-vWZ$J9[֪aYOIOhnhcsi`$v [?XC(0Fxg V)WmC!jZ@*j V4IDynP 7JT¤*C~|Ky:&`] Sӕe 7,9ϨF=CV6&M``Qo/VNOmqfŢ# "B5R&EVOjcX\U1$,"n]|XwhHNߕcmRf+EaBEn4ݦ:E<^=oulMʕ a6)orV4\sdz,#c`e}c)vK"kTyNxr+htxrU_˿O.mdPܥC+pR (+_O I1`|;޾arzv m mpAk:kD @ՓC@Ԓs,(<Ǣo;XX q-h"2I ey5c1KY$[2 ڨLZk~kk>&66S9q_V9K~<ƃ@L_r ~Wb,3 fȺ125|uK r%^;4hxڌm' ]+Ng<'9Em._/cKtd~j}CCO;r.Һ <+GLҥ<KW$r{8 E6(6z=BV̄ZvB@,|1[\29˥=abN :nZk0,J*HxAK!Фߩ{Z%aK |^LyG(8;콡^ {EHj7GvhR9zٛ1qS[=MJ.d8c7Wi%7u(@>Uľn+~d gdHi60gP6:!#x)rb1sFUf$c"kxCPRj#WPdXtHDG�#Pl,{&}d |ךrk((R%՛|on\ʼn&ڸJf K/~$o!BFՕN$U!έKۗL=ųņ>.j2[PT.RP#)<7-:V' >hSt؛Y96ZcwdS~:|(Gz:2GirS]uޞ\*W pW=ŧe7_Q4ڸm oBKRmEPMY |,\[_6gZ.k}wEhX~g X8CC>'c% :Hbn`Qn&] ^ ! !Kgnx v~=S4ߐ د/GjꮝwDa^5`\\J~7ЛO5ũSw2q. YQ7ʽi~ bNnDyћ Bݣa  nōgC0ܳHv@bUXWB=,u0 ]kC1V* yc;#^O;xq h-IEޒ$g,/αpƂZS$mԵW7 zH(8\'/rOW~"H_;#cJ.5ŐJܶdj>K+s&Zfuq=s E-.P/K"FM` cG(t#KNoֱC mP8-Bv}234Nj 9䆦 ܅(ͬR|nrۿpA?>{~~0R^a&}[ NBqYp_7i݁e)D|'s\Wx25g_2x Ì#5YJBү .n C5C'BJǠJjOt-Q܉ZYN!IIUE ' S [Д}#<_2e: h-EÁ7bՍ0{2}A˱\5c^ Qb^^"5pȆMvS]#8i /cdWEX RZS:}I.Đ$QMS׹B9R'"X*]?֕T%6? rƆ,783sk=dx8To{ыKG)+Ӕkaֆhܵ'԰;6߀>.+ؽ4'+c+9/3T 0H9"ZP‖Fb]f&6SO+MX#x C9F`B]5bq4jwnp'OJ^ Tbs+J! 7ܿ6y@;X5^6-tAlǏ.Xb*DwK>BY=,_Q]^Й_*{ |횖񗛿*p!=Pw3*I58 ڡǵq 3$". sO#bSqSS{Ӛv! ޷(";VWic= ZW? B xaC^SΉgՅo^Z$}6W~zʡ:S 3@'7),.x5Ezl?6:GCLy&;JV!'4xBVg? >qdwH%XJeSn[]7L !7(Sέ%BlO msVC@i]'cc4ȅkh4~v\Xݳ =d%38ƾӕKu"0,rtJ2Sg,qa %G(1O?*[EYh%:7{"%T0KK[cWYM5xlzbs}+kWt%nds+SD.Vt(E,6lgE [x}+BYԽCx>c5e=5 B'( xC?tYR@qdB—'gY|5Kܐ:= re{:bXOMW*^ 'w^2p.d4>lcçjU B4I;4*'02ʧ)$n!dHbJdhʖ;-ـ߶1󯚝9r;ePɻqA-oHP ^zQ5 5382}Uk}.#?SvAQ4G5K&kֺi龞C&5kX\D^-G9)qF82c}CPPKHG/Mزy^fCfN"1懬2⩫=7ajC ''yDP`e ]7Lx63rdɃ?>`v Oj,x1G>+|SKV)rNpc"cX|I9?z+ HC<_/waf>!YLűbr>< xsCYgd4?9NV:)|OWƄpk7F_y{nXaSڰ>z jaٞ=+9Ife~Si{ƅ202̮c`F!X7A3J"FФ*AT뛜 l +JL`Z Xwg]$պف$ #]$hh]h*~>;&zB*#~L!ԺO7Һ ӽGV.4_{Jn7SŽq~+M9 oƍJ(~Xzk ]L0o DePV%_j'jϥeEb)-GK4?X@RGf,Cf~@\2s] ? 2M8=:{{\ɹ߹t L>ӵ r&CBi7hޞ,[8*Ӑ}ghNe\LX_S|~}j4naj:'^oU7/ )plh),ڈHp7^]@-9GA#ef!^ސ5Dse!N&QIEA6e7ORlkc6^(:C諗P ֚'1XWJT'so msxmMJV ᢼڔfoްyY 8e `UMd-U4'Kwu.:&~&[bG3_v9dZ(X3=W$s_'g2`M}:_? Xd%q 5Z$YHsfVx&4oaMTCXLN󆲐k:eԆÔK& l0R`-ppwd׺uåa5w6#(C1NĂj^Z_4Wk3tfCUu=Q-i.W7fzsHyhl"+h쪼?qs?t\R/˩f*}&\*r cZ] 2Uw;lOۅ} آo+W)n}]Kjhکr3De;RS*ij$8ûg+ ʵ hTwjp Aؠj)cjfժҌ}t:7:uβϹS:m\ (=GCbH(KwB"t$ :P]?WG ݳBLݾnɩ,_J håI]&P^7i)y`4;\PnzjN WшM7zF+3rQ^Xٗ͂G?%LcI^QR" V;.QVhitp1z)ԯe*o bۊ몜s$?4ޟV6E0[ߍOXfjϬ9j+*9#]gN'4Y#V |[Hbg+UJi4xP=\w W6+83o5L)}|OFT:@B#b⅒)vȹ65j@+M<8Fvt4@aˇFUH~{1-Z(4<:y9.‘@v;ݟZ +&t!S`Hf\rtʓ4+e;C({U>NزH }@G`Cca\7iQn6vRL] `9)n (xR`8 "1ENf|0HqB:Hnێ]Ǚ/ӓQy a;4jiL #*luaYQnjؾ%}8T`ɋ }ه;Czu;~lBq9qv4bz!OyPpv~h~sGVI`hv{AD|:zIV hLs]|hM0>woPpJ wwL2??Y!_9,и0c(_s 3P>s|npMu ~xx|/x%ѡQ!Ml: ҢAD$`{aMx/'%v61r'"Ͽ^&@ 4^Д1;)ъ(~9$@-{Q,p }4~te6_uspƨȁ$"2 ^g K_\m_:b㵙 . $ByXlb Zrs];MCSR}zx 7h~@{Oet;Ydƽ.a:#h1gk u7&**"(.Wkj E6EV ;YQ_5U!څ SQ;hjA^gOf-P(:ݑhkJtFKODꗄxSnMeZz5pqd2ċY.eh?fH,~Z ^ms?JI^v*:aZo5Vl:Njt 3̻ ?y+zؼ"Z \ =Q6vf#en2buu~7cjmņyn9n `9mwKB?3JJfp/(3{~usZ9膸3&)0UXlVCBT-%Oœ=50PV(.> e|1uY~fvjWj-"fIiZ Yxi\uL<|=(&}>r"NcbեP1ֈ}?'3q2pʶ=NQT*;zV״9;R~NcUbZ}up>)*g"uK!t{ #eR¹OR;n2bfJ*q7o|#'kE,H{Rˢ 4aT!E|n.r bb=@c: {~E%ryΊfx4& ӹ[uut+Z %UH3n1m_ nȠ}d3 zڏ;)yowל($Xk/bcd֪%FѴAqY-fN\3sN3 Rҩ6y,)Yc 莜Lm9^Ȇe1g C:Ɵ畻=}WH Vpddģz8O\#6PFH$3ImlƑJR FPjC'K@lV ;TVW1>x\եۊpyw_\)#N4ɂ{Gs4x_%I{fvGP'xZHfjS'ۅ۬~?zG8SAV`b_ڐ€=:P.}{bjU@Ix3ӌ.u{[K&v\R( H")[Ԑ>nWQ:,v00&C-$p^OCJt9hْaM7bYD<]k/\`C{¬'ȯn ܐn0ܨwT(:2YfyJ+Wӫ=u-{kjҽ.h}TΆp^Γش7%f-A&}'ijj#Ol~AՓXNzL7/qWfY հ$tdRP\lY~9hHV寺+4+>~לxM ѾnDzV1semDB|V.l,.v= {b=,j R DЄ1_G~Hq3}[R"4`xAY:p}sޫRpKy 'n>8SgqT6cN!0Ax2ZN VBgPC-4˥s A;Iz YWJ3PV0+`"8滴TY1)}nio,kaq=p5s18`~pPtXWqȯ:b~ p6 *\8z菼|Zl ɝV^\! ɀk5iQJ'-&?nH#^8Wa/11snMXg8~x8TIBM5\"65bBi>5I[UZ!WJ ps5[1e_G#WGW ȳ3q%Փ =u߈^ל_k.njHЖAY'W!`sұXuy71L'J%qb4{,o} IHWM0yRW~-3h&u]jܗjX;} ~f `nu[tI,gh>ba5+S2qZO[WJ +0'>,002rSsm8L,,GڿW-Q  *Jf`egl,ׅ$% x"tEx0 xPNN)IAA sb̢l/ z$/H'L wH.u?rV!5EJA8{wql@B5ڿ~6q 3~u*@G3 O~oс,A%J(齐nA >vq>7eFd!֩ 1( ևp8:;* X@ BWWC^杁@H1MЅ4 m|dt yl!;/߄A$t=##@繋s!'&L{Vl#h"^LDc?%/ U P((÷Loi v&e =}$VS??Q+D?g`[˟7GNuZVsѲ]`f ^ zv^`@%Acܛƾ73<x tyˈ1sks:_.$}W/EX ]gy{SRj׭!&\ f/v3Hщ cF<C 1 tp)c^LH'j眐XrN⤂\ aVq}-rDȯ*PB"eQW1>,?y\tGε^-Mqrf0CwQ.4 Wzt4!goj#"jOI7 I1M?J%f_J7{)s+%p}ɘAꕉ hٍx.Ux[,Vix7P[aSWRuB`{CpU3 0ea0~6,LwFs 5XYܟ ju>&z4J7(r{1_4TBVӿ_Cy;G$qr^5\%E GO9V_͜Rjp QW+fЬ'ATbTK3R˔c\}=A '^?d|t$gN&H] -OӅ >7VI>9߶npJY ~Ku&\I1:ΛpiOn TS ^moV^[y!0֓3D _]mZo}T6L:ձ8gGJ$mm#nr=Y6 [%>Lڀ>aQW*Yv,!҈^w =\M,k%$X6ݜZ\%QmXMN|2sjȣ~dy}MtJ"cDf&!`9<f7pӇ΂?"yHWd[xK.gHc;ong~o?W?(ۊ͛թneн\zVX)](J“L#F\;4ysXt>sv\ckW&q|h<`#+\ F$: g*`&~)V N:]R솟jaS! տLٯO1"ڀN &T5 usJ@1+!usV:C04= E(H[Knk%:tZg?AXq)*ϩ32e0}핈]5h!kSR8!ވ<ФɳQ tW>~EV3썠'm3:TĹ3U#V$mkܸWi&Ж<j+FgjM&V Jȶ[8)*}Qf}Pe:C5d7f`+R[/`09շͿ%~Ct;Htr~ꅂh.yH*L}4"h^ʲn{P2r47VE(7SSa\8Sn}S~L9%.`wXVHBH 0d@vr43xJXn_="JCܢ >XQ }+P>@Og$;~`5=)SS³^La`X{3Z4$ˣ0z"T= W`'(VT7AL°[C/讉W?3 gX0 &Ul2 Y_qlan7Il%1fubI L(5ɢiO=18oP&$k[jW[!yF]|הM@+)reЭGt_'Znza[넆kcT^GG&vjȓlo,T^B|xug>fߟ94CS:-ZnSᨓg$JC}n)uC a=4/8Z c@H2ɍͧ:*ߋ\0#ϼ` =v=5 &iO`i-4y#b~F* 48ptCɈn`B};tT]sQTjqǜF4KNOv>ǭf;bq%K0Gݑ|w&s yqu;8'ͧha/jmy?+<2pLepySze̗gt[['P3jGu!囘B&m6}q 9RwŤʠDGMt>$95IǝXk]BpJ6eSfOe^u[w&^kJ3ia|hڕjEDl_M_V{9̮~^j.q@rqvMZFX-s_9Vk'[[rbke/#ȡWB~î-(ڠտY>ɿVvrD1\Zj%{s*Σg>>8`}pMvG'f7"">_F[y@ ks`3^~Jf??ɛ"R) OœWM]m6œIځ9Yc)g/pڝ?m)[9V_w:I3Jm|EI3jXS]bI#5;ul.y1)$kK`K~nͮ}ޯw9hވI1kUJiϻV ڙX).Q{>|nK)NnfboގF|xokCƭ?f 4{'xžv7pi^0yTpfmO\8qᑪW[( o 35>tı-3H Mj5ۊ.7(sR캥ws c |۹xޚ<aJ}"}>G1|+}j}qɰsѠI.hi6I;9-K"mޝїM+7v~tF]6 Ԟpur=7~vTٓgoIZC+v@/vģgwj鞌KN+ӯ&+uȮSF!R2^oXOR}-j=|_y?]f1͆;/>v jA5\nAr?ZG[?4+Jˤ#?O)imX[%Y-|:*C̋l:G3|m뷑j$Ylrʶl wos/EFx>]ؖ:{̯⅚CWvͬm)oK \ڕJףm?n[и} ;}^-el<[A*tj,vQ_XMkj7vvMW|5%vRaAnKӯ;ڄ)9:f.gt~w{kԂ6٠}}wtJo?g~=gf?Pɴہ %Me9qӛ2~ese;>=s77bStR'X_6/űR? s~~zo:}g{+ov[@Or˪9o}5t}{15ꚺ'wK\zW^~h?UL>έ2Tgwxm|{ ݱ''ݧo m3YY˾.֬c6/O]/ Uvn/Ϊ+v{t޾{c_Ϝn]a̋/l䶽ZU::u2*v>y֎5(}썝~˕>{JFayimTT=\ĢO=<_:W'.-?qwl~yen-vw= RlNʸǞK~uCK%McZx& 8$9ΩmzuՕgeӜ/ۏ5>]~i 珄{}~/}d3w:xf {n\w)&wSճ3eߎ~lRv$Ιt+ǻ\+J [k0S*O{֋쒼ܾ)L=-2-Q}͉jܐC=m_\P-"GjlI4-92gVٷ8ao›O{6nxy}7f{.}W|=}| xo;YʞԱٙ۵c7WR;?*'\']SGIG4{kZzW$=ûStsS׹ߙxɛִHt]K+yxش:qޗvѯ\ﱋ=sU諁i} 2=ZKoK]u%F<-ѾuJz}s؊~wZ_kϻƖ(a+:]}`;ƶMβ5v;kF܎,Lز׻99ݾ.Sj甀߿u|7 WG6YO<~ZG'OWT>/?]?.pTyNsC.kPl^sttYSis5׮3el=MMk-]cڻq&^*o=;*$m3->=k՞:.?fEDMssI'=?MYWY}Eؼd*vv?[r/&H?N 6YWM{3Q{mHQ}NYxY>;^nmAceg\IM;ƽzsֹFwZib",y4MiYgVr=Y/${}YM܏=5?uMҕM۫[1a&}^WkEe\_0y2=^}>#yN%/ٜ9~ݱa!Kf?.\n?/P*`A\6$;`nUې]8zɦ_֥ɍԃMP-ҩG'TWEg`ip_:Z'xDV+ݵW/O suBjK9{=:xkD }z=^SsKwݷWu'DZ!ϥs/<>j9]uv}0#A'Y)Zgol%ߝoh{N7\u%Q>V$:D&+njPitbǝ>5J?Sgڡ,^j!mf+}=^i+7LQD&Ξ-<߹Usٝ4ogqt*$Kje0ޱ/>w3i`+#[`wLc?]ڰ~=!ԭܖN pRl7}ߚN%Xc繙>3gG/~yYnIrn9o;ׂ^ v-0ƻ*a/~$d}x@{a6Af6Y~_w]q "m*7^^}ܔV3ZLsoų|xï'<'B6%TOyv@y;*9zuq\<ڧZYZ־d#vG isɻjQc*TU8ʕju״ȥudž{BSߕG+4gG=Gs5)`VɎoհ^u-it~r_Km;VPsgK~]va-O>ӡEev޳vjҰ傫>:vv(ީl1"*mG=[2s՞E.g2xFnm;*jAP-2@).jUյX+  ;U:ޱNzA"$ծzv;k=5rA7&W6l<_+ !{c4r˕D6(teV <,esZYk WN~a^!J]_@ &`p2Z; ӳZx_w5ԫh3fQk! *ti`vr>#e~ ٱIZlNR[zqjuuOf}kGU<^X!J\zۓ'/YS+Q~^eSWl8Ӽ'n7屹Sr9[b`ZdB_:;\C{Ls N٩he1cݷNͭ[{DGZ ώ ȫs_k^9{]ǽwݙ]Kl^;MYӾyR7sݺDǁKu3oӨ.\}W'vk 4 ˙߭ŋ̛ty.YaCN͈QEK7^wY1/;sմge^yx=7RL^q^(Z>jg'=`zW;^mto,t˶Ź[;&w}z[[WXlʑNs]_L?<=o7\e2dtoˮfybnߘIv; ֯uw5ۏmܜμ₣u);BsK\-ӷ ΥԞ=Iq5.Τ_W}N-9_8:?n.q`NT=9ZMGjEw;nKS4Gw؞v^:ٿsvV46h,^E%K.֭.V \ͻty6.l~yR̭K՝vZW`c/4bu[*O8(g^j/`{~жM}5vh^}VprG.%x:i1eoȺ/\PNvrc;TNr18h}a{NsLݱyZ^-Y[ݼBڪ۪]~oPK~,[mS9[rѰM9ǔ{x=y-ii}"*|=2rW?.|lG2}gmC` |f.[~Bi#ʶ9,_5QuWS4wcf;OR]Y2lo3תT.YzjSr4I5앞s2%n_}QjnL~2,vfK&mЇtk`8p1oC~ mbX׋?zS>^]*)+WkMRO-*poU*}b2AL#]˹ŦBF:8IN2AێqHZt暮߫;&Gd:2ރwK>wV\܁-]8 ڝ=+vS"TlPY5bij'fLZcB#>{^;*ZkVpܐDGBH ڶQ %}|TSw0t~nכ̗.տ]B;]u\/i?=>}O+,w Tĩ{Cu^nT>?x35SO7]z-Vƍ[uشs^l0q@M"涝v}oxJS%ceKٖPw]kFg !]jo|*]|B$jyJ"<ִܵ*U;iW/^FBi$jfzF7}+?o]osԻj965)>'~IسKX݁vOPfvT$\038ލym=k2U%8;Os\6<+fbxTVe▨7[\R[i8HSZM';5/-2UΞ?->2+wo3xÆx>ˡ3¿owW0lͤ'eW97t!w{"IU*o=A*-}e^킽f^y/Nf}Z8~`+?h|Wl1!ln2d~cQ廲6ÿ8_ZϡaG][k?2u~ʁlpIc\ָtr$k̀Uÿ|JHR߶mMJkFQdY $K<,.9ֵߖ:֯Cv7qm٨A%-iZ4yvfɖӇu?uv2sZ aʬݣč=SQǿYYUMٍ#X5ڣ+Տ-%QB+Xid͵: |B'*>nVyϙc=]탺W.mtɋ,Oj眻Ek~`q6}ÍBR}ֶJ:ð__J!gH͞QRڒQeeOz<ҏxW^22ӕΎ0$' 4RYY*;ߞ:u%ך#Na*6U%SNeu#,o v}ldS}NYX09Y!QeCKv:S\ievؔ +}X.Ndu>ZUMsJ5*vAaBJppp׹Hw"iTV=n-{҃[Zۡs*usrȍWXP#uœ,FtXhT|i限1qIZuwȬ``<ͮqs,]mdMG+Y^Fw,x[XdöHϾKlV;52Xۭ6G"+vIU>'WԨ|%fc)E&̣\ӺիKTݶD2˭[zvIţ3 ||+iU9Zivem(L@ͧF[*p+_宬,s/]j.sݪ F8S-qBKwloM ytkjE58YH&ϫUΕ=pH&ߣIleֶuvD+^{wYrw徇 d={f.o<Y;;vN%oYgӚѵ?R#3 eRږM7vtg+u=:WlujuȮbaT T^a&z~9!?g,{1lS bzM7ozՋይUViWs׺->ſzSO}bB?'lCl C +׸Ւ`]?qEdssmbeY̕|N}y{#kN.A ~ā=?&kkr'[X`|ꢉ/$L;\Yַ1Ra1U,[eʴ{us7/ůwѵJ5d~Rf}FyqF5\RVlJ/!ջZUva-~""{qig{I@xV;xoL/z (ue5ow*f^9KFb{^9v[g0ihZ|۾.VߪVu7lŕF[}\fBEVaٳ_{pٯ2Ia7^ҋZֵώv 6C3WZ1խiϾ(m.eN4>N,Gٯ\6mWfwOv% Rءw [ơ%jG/ZO*ڐX=uIG͓Ky9fa{ (9da˳2hM|ˮ56l2bطy^)V_+=r[CPת.L 9YvvZ?k.ۗ::{Tz}feC޺~d2pINն~NS~A/HJ-'[Ӵ\N xF ݱ~Iu4R^kĨuZ'Iж i:'8-HM%ӾriQLzIմ1ZQTT3 |_T'h޾1Ɩ*01BK6O#NN j#Mpbx]*$$ؿב0IKGomC @HDBɒQJYَQl2T`:с$ErqKdJTd<%u 蕌2E2Mr$ FU&(t} hM ~xmTI#)YALR Ed%˂mJRrRxj%\8m>+,,}2< f'G@,9)2T)e`ʴ,*2)`I`zJ5 ^ '("kh6MnT2snTZ# <֥h*cYY,|ԁFK;5**& :J*u@-e`CLTv?dmmtU`rdeWR(~K*4E-0d,p<7Tbt 5\%9F,zѴ1x.->Nhw.;q 0CvBTڲf %+*\@[2o!#_ 4㐏-\Wz:~>lS&!Q1Ѹ+j$:Cpа =3 ,#\Pܺ777j^yymȿQpXPDF^$z\PMM1z?;M2ŏ65#GFGB ~"kac5-ȘlIJ&;DXLm "RK(=kң{lԫtjL5F-$((\yO<` pkX$A4x,a'NȒG`%4kd%Ze}X*`ck {fMV[lx_=umaK!1i/T*?-)X;vB#ftpyZ˼I\dsME72޼\Hr!-bSraiƬ ]H4abkJr } 袙X/W iZ5yu"Hvy-ZIZ(s:YZd Jio*TERh1HpzHHՒhcմ.&Axi DژF=LI˜8(~SIWqсCyWc$<)-D 2F+edHkhNF3$Zex M-3)Y"U:&A3{ZƨJ @,s4)t B(dOJrV9͡>7$IXēl x p? 7@-;%]KR*EOƅ_ P?05>G?.pg>._.u565aEv6m28D 1q?7(BP)6t l:|, Y4 PzJ / -M9hp7=fTu(R N#e-=(0TL^GZ-5`H Fju9N΃J u=..W'Z'sJkTpCL&PDfn0bRaIR Y#ddhiɇRd*GiZG2**<$,d{+ |etQq3“$Qp,ZТe|-g>pTxh( !h1Ҁ "~9 8 "XNSY8J isDa)4)0hZ5дZj5`pQz9:ȬO`H:PPbhH}04L\@ z,ˠSe V8NJȱ"4̯?\(W(Z]%ҦR mp^Bv0,M̕?"q˙T:1"s0xB,n$@X bu&N,lv TD݇w)j`N:*@<%@kP*H8,{>s6DE4 y--ORXM*&n ?}x@6qZ $!nTFSo: T\6x@Ig?qFiJF%!USZg -&,CLSHQ y2JS'+Zx ;T 6SNM۩*u )r@=\CF*7Vga; dsG"ĠY% !U7|!ψ2`aKdJ'LHg< RW* }B#Rł -˄'AZA a=M@ ^0ZfQJ ¹qJ_x4Ui@d:l2\:QmP~$Т.N弔$pɯ(f @23dkdHilM7"wx6(C*fM*5qpFDAc(^0'|0ЛEc3X_V2Djy<)rڊB DZwf M\ "A.Yc0H'"Lq3 C%s+'Qn2)Jn V- ῕ޥmI@JSHW,𨐎!1NQ!ёC kΎ=bNɀֽC;:*4*#Nޥ{[VCS8!4d4t|@ $D<wȵv,~M @Z(~ti ĿO?ѐDtpr'I/O7/g4 pkË jV_D|ø_sqq\OW~;)Zpx{Oԫ᧧+ Fvv1,8<]-^@Ps }F;&"& +$+:&ehJə5$&XSq<|ɴ6JJ%nS*qڌDǭ09҄>~pӐ˞@o*B`=NBT<3 [gRU abo(*&A)D*Ҽ&nE@> VmF aȖ&@3Q}jO_dRepG4I02U@d`R qF!DpW,E DR> ҳsq4ޅѩe2'\dq"q>idt| I+he(ԆFN+^30RWl(bg ўP7RD(`:`.B![sA' 'W78q<`xAA#Aa.$Gs,{mQv.i.%5,EC];>B, r^B dF/ 5x_"[+ .&Q^ /Ce\2-' bDAdx`{ sgax9!6ęr]R$}G`( _ iZU؇GSrG8`쮎w"%g((yrAK#RUji$.7M:)Gg^#\n+hi@ wcI9IOjW >W0~Pd́{6HJ'SЂ >ë`%9 r9b6ApL8X-W`ȗ^heF.9'J+W/%x44Ùu ;] v^YRׄdV}$L^2##{"NE4J>z 3ZѠ J7R$A"&mIgy$.G@_{FuɍX)@^4atJJ?X,/WBw$<7ۡnaW9?ލ[4~ؗQ5Ƭ EŨ(Xy>,KCla ]!f\$^(_1.C[nhXEh UwqDm\L)p8!QyDNެ^d|d zߖ+d8O:NhK]\ԑ :+,S ) 0NezYВ7V ii\#[FG.Abo GcT *Pjpir|#I@$?67DIP:.ɐXU 9rpf gJFsTDg"-@ w,6R;e|rJ+W'be1""ՂtQ &5%L|(Wao[F7xF0[ FA"4QBf:PJQ*a ~ݰ[ڄ )7^|'=Yb!7jSq(71ZFqE0[o~5 R(w 93)ġz98YH] KGy\5 (/U/f4 " Dka\2 3n!=&wȀN!(!:0|Đ(0 ; ̋ϋJV|ISEQrF%LIR.>PE:LBtѳcpv2~by4 }Ju8rV.`NBa`@c|x[.26^ X BDN )+XqN\^4KB,Jb_( WR>ped,a5P&p^^{:A:Du 7hBT-VJ!@;#`8;7&y8|352V# d%cB8r$Hg#40qE "W|ql䗊''^= FING/rWq=3y#_>oOȹ; lpb? Qƹ a X堢{GnC GX#hIj4O:茉6*6ekXm:@^CUCV%lu6`!ۣiY:'#8XWufA KK@8ZD,P* G^siޠ\b jG'-Q!h wǐ3N!ҋ`y7i0 H{ Xh12Nqi2e9p+RŁXvsxK %Ġ\MᱜG.G@'FW0Z>I$]q Dq/h؆Moι9e}o5!GI1*RgYi[I0qI}Za J`zx@3! ʫSCPB'a.̝|eFTʲ<}@ 2 c`ssuxĤC>LNʞ(r F#+FW*4dV 2r_H0ʿ'29 "Dà "zوv(hAPw$C`UeF3_k / 0:S=>(lJ J c֨,UF#L_獻^PjA:?ڌ4GRTHOw"0b&| g$gTp$#i51@ 0&,Y:_suN]Hq ,d\V&uK` A;D`XdF]!D7҈)5i+62є+ HjlOIȔ erP%>O#0#3o081.a y)l,@WXR AtQ&GrI^0Jm*O9aȠ#G9V'a ftܥpH1ESPW& Qݜ8w[i0M +m6FX"#f-nBi8$J-h U@oK"/".Nx3"=R@B$bu7ar6 pt5hr{93ަ"mXX UHĩw@\gs1muk>!gk b$)e5!(/4U{ޡd&ZsUmX]QiC/[(pvQ{.K4I`ǥ%u@ ߎ%"R) J a.Y/i@"ł?/ QtGZ|b-}@C\/q-.U`*6fN,(8'ar3s!\+?rQ XE5`8oRk$>LkFu h2 YS$9]+DI(>.8lRes n$IkP|fCUN(`atD, _ip&!=AD0CB ^P%\Η!$B|aBHaˇ˝xim63$WI:9Eq> 0J̸f +ָ͂WK?t `nrE`Z~$V*P 4$=Kv[A!J \I5pBn.p#WR aDSg wᛧPU:"[JV-^3xdC7Nqd,t$`2] 即QLh`~FN@-M7Z~@|厨6!U$BBt8ѐswi&[/L>Bͥh-H!^B ܷh8@zw2r@; j3g?|b wNӢF$KnB4TAP4IL"(V_={q(q% EMehӼ ^ą3(j"`oJy;j.l~ۃ :N$y1BͱtT#i)l.f Ο$>͑m0us̜|OTE*?%A'ËKip˕2.q*q#ZhKP]X>\,_= lC.NXKs ! LVBIje>POFWWBiU|&>S7\3)$ԖGHb|X aM8qhii_ P BҮV+ Yca'A;] h)-@K9 ! 6- ׿Z(Gxٙ=$y1|"iwgw+bTKziW1>I JbP\BYЎr?H;/V_H\+%WիWׯ^OYPRIlZT,;_V5oykCvnbm˼rbJ܃U>M 'Y Kf{ XJL+A2BeH %`;`OD4@VH1qI*y ),/Dp~u aZB.QtJ=c+uZ]OʀrǼ]Ĭ1Ǵ ƣyϱ} ؕw\#ԛ\D(YlGS#{DHDLղD(=7!;7hq0@J{ c2~ WN(5LڥIvKSRZSX?Iqkh9{R,N)`@&lFUfw1,4Лx_/ͣG;ތ2`wuMkU4,(=cPFQ2=h`A)%R~aE''$+0=T"> xEvU1{#Kƃz~IdnDABkke։i#t2ѸrB9*rz`!"YL.%?vwOxc4f.t~1m{L`(uB8n,#T!3 Zpl9y"9n^}ˁ }KMD;gjz A;1aNOF:穘s{obA3ܓ责5qG Txzut][rov*qq0:J܎ =r)0o!rVD2q^(#؎UQU\u]^ds tf$ƣie(lFx\MY/^֘;E3܋-Tj YIHšrrHj˺2F3:^5b_dD^̧#+nl]>3ZkC#d 7Ny)[?GwD~A~AY{DM:Xlq8,hV2f*j k Z6s bNr:@G .]=rF7?|#G.}{UbexwX1~KM3E\76VH9g7)KY]WKn5JN \m@+RnؽO WL`2[^Ǽ8}pLo=y8+LE Z-4;Tqe}Km5aly碓>: E ŸiL{:ԺbHnRtDi+IrpL??Y@h룒z&ִڦ¨m;+=Zandg[@c #KF`;kXT-К h@ : \)&ͰO:v{7 FPCp]1@&mwb\Ur[yJ- [|%bkM"l&r; `y: L(f׮rQrkCtDn[v,+j}w:ZKT@љRՌnmHM(2w#/AS6^© 5 n*9W\*ͷNn͈а%'$}\L`$Rꀸv yU̩tB0w]J5tw7V3uM 9F ^ w+BGp Ap*r&010v#(ZP Jό8?a ļCTmkAc 7$]"3D,U=Fȶ[bŘFDsX"[Jdkz@NC-gs f54$G;,,a [&g- :66rU2IET|v$e!:Fj+q(CJqN?'B? PRm$VLȓ@J抸0r(샜pf2˾(Lt-mzّ6~ ŝI -wW|['dDݏ;:=#R^KWD\E\Jktw^#O<2КOa 1X(=cHОt"K_ ԕ%yY`S]fHazxRӊPNA`$jTL$*F*=37hcf^>)xiV$05ӻMIud,Te(|G_د"rg ת*԰y ǎ І_Y Li?{lyGCL 9fttFIp]]a0}mmFJ|s̎6aUgNb%r'%{Kq|N|1d I^~#fD澆TA2ؚj.@B@tʵfFz#!ʼn #3`W8 / TXhpl]?Q {2 &|fHJEg&Rɤa4|co!`SBZ.Lo$t(o]X`z&dQtr֩z]g}\&pq @ :80Z4aɅi^^q%Kwl5 v>^ov̤}b L}-\Y jTI) BjSt̏e9>WpN2b*[>24&\u-ef͸3`Q{̏(e&%L6˚Ñ6c&}loc%'+7JwJJ-b۫Zm i^9bo(DŮ5JO1y8?q3%h-.~-%}L6k&]91SA:69ji^06?91!Aۖ#ƣNMqN B~|&7$MNZOp0]7γӚNrP\$tvLJe*8V- #p k6$nDYl(PDKbMs(K")Xc')"T UQA.khU'i`v}ZuJQ##f5ogH^ɩT{eH3oJQ ƥQ^tL L|P5RuX.gmd\ t(.`O4ڣ ъ+4]I])=*vctݱciWKz\3Ǘ̙tw+;=0exQ| 83-/zμ|nzE|p%{:XKsvArZhHlrQI Iu-4ȡ/Z7BrѭskѹP7x嵌2[H.Ethb0&H"a b1vej?߷ :A]hYgpmO0IvpuTi74SWWk ُ| ZHy$8#JP^]i'W (2{MguyBϊ8h=;xvOKg݆}%mprDž)"PӠX$긑RmRPk:+]:pa*%n= XЬrܑ !Cl\y9Y92?%1t:D0x==zp_ 1L8bsb2NwP>@ # J OIaC8QPKn([K}VBBhg$7=)X=SbkXYM,ˠ[=p8 ս{ԾjM^tؒ2Ա6p^I>gamb Nq+ Ž)3_ob=9pސ=,06$Bj1Kpx8)g]\(A=l>L7-|8Dt V2~aF \y2['JN:1J 56;e rsTo㴌J(a"_UԞ <ހ8qxm@$;LgQժaˏ0J- ;lsj`2/PZ$Lff[Ҵ .d?Nd.瑩aڴbsقPr`R L01WÊ׈KYp4YNM1q| T/u-v"RG 5yRpd) ՗u #PɎ:r҇Օ/&&Y| rd~Fa U)LX=9יWP 9 U9Ϩ6h3a s+[")[*Up9fLD9˦ ĂaϖbP9P1ctY] I"I9`Lg@Q0M#YadLlcEnrsppE5|R6V[jNh ̧zRW'LpHS(#iczD<(粘Ԅ3j1逘@szL`#3y5*cϨJ{lL+AuC='`L:6Tȹ*F9"mK)Y9co#?p~78w [:<`x0ݫP0YlH$УP=S`_8Bn;W%U l8 ɅZQE1lѐy1`ŝ6smaQJºԟ5Dx(;1Ga,wp8w,4-2F6ljq Bz SHxZvޥ}I˖iKxx@I,{sy22B2 O\+c`֡DO0!3̀f>bp,­'Q5WM7`:F]v`6VMwNߞF!Q*dCq(D#H0ZȀ׽T8ҩ1O98D*3dQ*ƍ \(h{A+p['9XH@})տӠ: {vb{p:ÌB*]ώ}t:e e;ݫJ-NR--0sio;A7ռ17Z &Vs\Xh>,(33Ati ͤTє`U[54PGy͘ ].:mib$ ޖ([ ӳ[X70&q] m!M{ %x55"cN\*3#e'C3$ۗC98?4C9zr<]!. 3F *kL/ɇR #μA^Ď=gvN/ɥO|LeԱ;w 2cSQ?7EsڷWEo{픣n]:|陛E~-mw-nRJMrOM:6&(mlkQj Asb FRj K \Q&uB0#nU7T("f;RRoC3dRH)`Ύ!Mœ0Q/\aT]9޷<ab-}]uC,K kW%cb5'f* 37nP­Jk0['w Li: L;']$Je%vX^աgEFoGḷ60TygǨ+ (wԩkҟ+ >kFxUU Ndˌ{#᪎P,BȶEɴ P3#$HLy3DO͆h0(dLZGhT[`3 ut_ɷP&_T?V%B= I+Q,%7 ֖11&H<8 * YhFXc\@!oebb8RNxFTuq798$($=R<Җe BEpS9Tєe+BI%4D;ʉ* `m01 h%F ӕTe(   \|sJy7}NcUAmlmUXzF?G`L[+:;%჋RًRxRf|X`bZFؔEejo{*@/!y5dv»Pwћ g|s`" ɐ{b e`A'z*[]mTdbwhޕZ  IAv*q(ca*"tiP^ଊ; +JX}1TIqi)B4Nfkd!ZCtVW!S rƚLNl '?'^+\5I;%4;}gHN9Ze}@rdYMppIX2ǤŴ-k0iNcǔd0]蘬ZkH/"sL`dHc~D i_t֌r%wo5ڨA!|>(#\abnGCtUAVQ݋rk͚It_M br73@Co K>NWLHA|nYy|R9l#bLݬkN,+#]C7^YŌH'8+\Ziz9!DKta3=0ѣ֡A:tp Ϗ;65kc݄Snp $(oM7EےaTD5;11k|*)!K8t6Vjg2@աNvE_8YiPiA^Jt =Ѡ*؀OFB3>b"\'Υ=1ƞ3技 [d+hoa^+نofGaQ+Ip;0CjNl&%O^'/Pq!j'\;yQ54_T2A:PP#n5lAjVWw Dةea6[,h!ͷyC!ZթQrDo:MpfeMwd~ XCi5<(!$WwIZG!5#0h4,9y@m f,2!ם5hU{y'}c7G&unY{I:LyqԅuEhTD /el w$8VgAWLQYĐ aN!9vB82ͣYPdS"3G^nk͹WO= Õj?~=TfG 覂uEӰ} jvIHjY~f1 7Zp:Zz)3s$I N2mk!}?U}:T  qM (Ҟolu]>~|$ð=Qe B泥mr> eLOtMs+Vvػ{ Z5?'IL,+-݇4Y٣"F$Rb6 {^ ӋY?8dEyHfoT@%C6yly+x7$ cE[:eHufafdqJ[FѢ!NPaQ8e?=T(1h7T78ٳiVRt! hN™pfnBҡqxy9U]FMU)-* ֥(!aUiEhfk lv+dˢ_׽W^M@!8rKw?tZ|P3\4 y|I 6#[?zdvu!!#֟ɊAOg-Yj@5 [QF`GrP(u 'KS!G\a\yC,Nwtnr\;f5,U :/hn^@`Lb[6y#Co$.Fdx#74j8w@ˊQ<3@1D=5:Or02A=hOCamj-t~V#Wt^H.4!N*s^őHBUe2C _ڡ ->_H؃<wZܝ΀Rnfs=J3@b)RbǨT%%+2^`JRjFMKd*=)*UNݐ0eūH> mzVJL͑׳Xg9 Z%@gQG!:-fq8]D I>i>3C RFS3>O$$t`qC`·̷K'xN8&ݸ7F9LD!T~Ta&f{9oP74Ay6 EIy:ÛSErFF J=Lɰ7`2yBjZ)cyT8 oo:i A,ڳA1mnCƻyN1Q쌌bqQ0KQDA؟FQ ~߯yS%o|V=7ֲ}MZv!Ôċ\;<ń\+dq$g:\%LLb.=bZ-PWe#p!ΜM,8QS$ox0cHۓ<=q+yk4焼v-I9]wy&f~- պ;bܧS"+V '*Gu PvHbFŞcCD)>>J%ysB1ٚ0k\H }2t[5U!2ax!$DwJPdbXv-|KEN^>q<AD|C=7T"*g3BXcML$؅LnwFT,#DʁpJ5{UǫQFΏc#[\5S^іXP~a2'J@tXAu_1SNݳ8;xf_nA&D'[Ԙ V̑Я/AWkT^9 p5Ӏ95>ǰ6:( vڬKCի֚gƌ7<-4vս9͸-`hz ]j0\OR„#ӐX,)El:#Sҧm]Qmm`>W"L842$X*_#DV,[y.%űwM-VP^j{.6`w=q.oWBI]nr`;vVbwcNBd5g))/\#f09m4&0ߙ=pr$q2NّoNrك)u+iRY'/>ʉ.5 {V[;RqTg#Kx{:7V5t;-3AӏtIVo.z&u+rM1gtȋUFMhz-)Mw'') 5 #4,Q 1 Ӛ"*5/! CMLxxLl %BȍU{`D`Q(`G`$3 v_aCSp1nF@WՋA1$ @=VMaܟ [eFe6 \fQTfy s)AK)2Xo5SܰÚ*&'P0NHy>8F)TDxd~BY҂cLo*bh{XKVGC ?m2 ;SH(W\Y(b'@!@Z@{i@:%? 9=x 1ğ,ZpC[1ap Q>_D [D?R׉Zg٪&x=kyBk|0nTg |\bAht)?K@;h`Klh(-YDM`@8KZU֖\sMd"HJ0Nk{>k^mĝ^(dXkh[ "f#aX%*c£lHQ{XP݇Ri̥[1-@ay^"oP98S 9~llPfKkci̐P!hH//TN^FEA2D!:KX =$] &@~#!q&ۨ v,BEBhu7ܣKcUZ# 6>JʈpּVuC̆<; eݒz5>@ ~_ajV Fs,g;b}p x*Ē{Z(X(@Ě/&(Rp@mtֽU&qh$& Z[0\p1{ca1P0AW71OptmI%zCcb AU8Ĥχ=,&9EbF!#v`#}5CXf*+ V!vF Xv׭h-2Fաq/vp–S9x"8-q(9;Lj8yY6{&/Vd`_i)CM0E֫A t\\7->[8{Qz-߅[L\ pӲNF`vG;Lr 7hG28e9 gG`QuJǑҩXz:xBZƀs-[ṕ-S:b\O&b"6t([.%v5p̬dą~F~rkW`Rw੄~~ƆA-*Y$#& lɏ80/1Xxĸ3rь0; hl`nWwVݩKݛިF~q3"AM8dW¼k9t&_:ēkPHFVI7T`" d9I{h/EA聊>`\3G{*qyGC/ډni_uM@!o(VT!PN< RpGG3`;[П{.\cTŎz2m0[dv{3?%5\r.3`'Uʜ5ibUk[_(kI~A _iйq0%d^J6 Q6WB!3+F;("-^50) I䪾0[&0/REtZ lJѹ]bP1EvŔڶ=ANYAULT3ːB)gI=̺pč(JSA+bVS<=%3w%3Yڎi/!l;0gK ]ORzAi5 K̓],k"Dsu!$J2wd@9en.喉[cRSY{[&SV ʖz{)vxCy{[Nn>>Sq=~X?⥑| \O֢qO#Sl^M: ]Wۜ\;L{1@l=, 5F*ْ tUrǕ{f]4u),o!x UVL1C]%ʨ SJ.B.(OdkMC~4°: MϾ!J EvJ1Ԯ1n48=b U49 81ap,xtKc/-=3, JπS%jSBef)d=I'/'”{m^9:S!?Lzӧ&\eIa4u4%d6 Lv;]u-e' ʡSQ %jҫp|AqLL(AF}%Рe,FoI`3IlbsPÿM;~q^-c 6!{M0V+ȄցFvLy^wH 8]_ʸ厉dGR/T [#˨LGH"iK|@q J?}ELxl_RX<vژ:Wb&hY2산hS"EWP;B یsۑimKV ʢxI-'xbOhh3$9.Xl!xAu'hBh,sR\~2/ϥ?IYmhQe;l*?` bx X~4-0Vt6M5H',J6luUI=|HcUEħdRgf JؿϮ{!}MR ҷуʆ\ *XOEvFUB Ba:m[?2Oޟdq,BHϒ^_~Tc$mYh<>D2pxn;E9_ ~q)GOac}2G>= uW( jWɊiE p yti~!Dd$5 VOf X`7?+ςP F`WcRvPtWE%@9XP5p,z 4v.rp(f wӔHI60!ȕ)Ļ5$U4h;;OnWT~eS[ g8w`Jc͜!v"'{s & -֔3<5O8N(HԓssYH8rD&-7p;3"iKɐ}œۦ`hԩAK)I n)s;*Q 趢~-*>kba Yr >­\ x$lmMȤ/i:#M(E'~4pǨv ڲ$ W_t!A }Ƚȶv~ڥi$; X姭7 |*wTR~;')) LlSk0; \' )liN(M͐xU ELihNxF7+]t݇/툵eE .ݕӲxkiL9T)Jv!`X|b`עhSQI2a=h2A9 ]6D(AȥDGMn C ;\ʕ%LIU<3\=1=N‡VBf zQݖAχiKֻ)p̂q@.i5dZoX LUf0LI{2Eץt Z-î $v1B9)#Cdܘ#Cd:$-1K8A}@,g"-^Ƚ$&T!A @XN:ٽ?N2˝ dqhvbc)G˙yU"KG!%֩SL[8r"X!X <“$J c'΂Z׶YP?v0 ԸGXovv=ỌP`d$wUa@ f7'c}.&%2W:s8uwIHsV̄Qz@qgp5"3{–K9!!hig*Q [4:i[ JB rKHB98P$b{"3ܲ MoaT1i lwK"9ȇJ[/\a\קBh1㶻 (hEkپ9bq刍pڱk.GX1!`R"*OX}c1)띞,L&"2:"ǨÕ9{U'9 4 "EB2iX!X|Ʋ=,8A + l(*NeX]jtZT'P(#C2 88BȶxDqu QIdUhV#hPvC褰ueq<4LX`ifZůݙTnr.akffj271 p,R>ǧ` .̔čx<3\*fӹ<~(/Q6XLRSUDЧyd "VAc*pe7|/`{U]\BYpWĦsوwZ\"\IC6c_Z`J1ho"UTSPUfbz͠j˅ٷ)O~5I|D6aKX_B;po,[Q6$1 ,zvu Q*}xPI_[;_Nܝ fd)Nf&sAu"Ek DžL>7?.LL&T"ѶRn/_|>W*/,B׮ djk6҂ēD~ut^:`)/R&G.SYQ @g"ڮ^͏*蓐\B`"sBZyD- S`:E[&DsV;llCy 6+QW6IX!I X80kAȮ9Sg@3%&N wM~ Z!M$^:8HJcʾQ>g<@BBr>=$t)]^ X*sF88%%SܸyI\tKXK//%X>9(]]BHWrD3~ =+.;2` *䧋:)7a C<4:{ K]o'Cz]‹oі[Chin:bO`@ *1uSYy+ VN4alG1.͏%ڵ Ҧc"D?]rů]jj-d_>N>Ĺ2 (X{o0/Jkr+M؞= =$L{*tkz_k{墭&NvBa2?Y,_OV< xy%.Sb$_SMǵPe$]7h8beHV^U]=ײͮ  [XUM~Gv#/H:{UE[V ]Q *"hq(D3tUE$ʖ "ٿlJ #GxE \yQABVwWl"Z)<+)Sޯp] c9l Xiƕ3TϱMO' a%zz-X?](>QϥhZKb↉ځ^1q JaCgmyU D B+Ix7|2P /-q`F:~%~5AU"ZʼJ8b蒤EhFpl䡃ɓx$7OSd7)t)AuӹL)N,TJ?(F~N sfb\_,#Bט 0VWld\jSr@mH.a6C8lrRHj :(4p lZBTy:&N:PnW@Lɩ UT: Dg TԀ$P,&L!--1+AH,41TWW} -쩄WF9؛,S(ZeV5a8բrJ\jma KHNkt*D/=Wگև'iNAOʙA4Sb0vC2hlnLiFHG /$/!zy{HqyRBд;tZN1׍Lw+v|O)Ih3%MqUC,t'R,股bP+FNdr \2.TCUSn6NlU|{ l9ڛR O* }#@#&,/2FY+IeG"*.|STzfՐAi" E6\TtB3/XV%/RT*z=\j^M5KoCПW;Gi= I6?+FXpȼلDϘ-v0gRe_P. 7yefR?m-V#и/jZIKZRF'}$0jH#GgשQ'qBH@e?xcK4{B"`q0MULw ]I|zrr.b¤0ou`ú%ު ~9+Q Yp&K? ug3?bgtŃJ%WY}M]~JO^YJJ kBx: zU0hזyհV1Wd^&5LEDM&mVQ:eUOd-vg9dY44Ȱd9{)FFNK`⎝+c=VS9{Uҷ8E\9lɑ\ մ:(Ƽ|':CpJV^~]|Մ ROo6pGipH!\IdȸX;C>Wu*(;OU;aԣrl]^ζ :Ui)ߨݚ(Q]h9}˺41\tb.KPo z!cQ> k5`kKb9dRs,\n(i@$G*8y]$MX*|O로+jwćN$ p.HEҰ4>jWx 1kbi)[/I9]D]xY M/֦ e:m1P@W'uNR^ֽUCrQ 0+Wⰶibm"؄^ks: o%%Cqi#b9.funv~R @ܥp, DaD INL&s3S 7%Y (fŦ 1:}/2&VRajč'O Ew€ECIwp}_9L%~}'p<`,px-WR @ P)@ʗ]E ZPQ6T %ܶѝVżFY!ZzƟۓ6%tqc26 V,d<2S8kUL6NQ3\&+|XQ;.0 3'XB> Wэ&#,Jݶ.3p M d'oFA5kOR j$whCk.nKs&wNږKƻR+tg=[吩,oD1fHKPU'Z3$ m9-,;;pTm?kT[(}=fI9*u괟* c%✑c=3cS=x=)K<5T꽣F@>(=k2}QgR-: MttC:ﳢt# $JnaD(d _%z)H-O>ӫYC9=r ?9U0+4&ae]b.7VG}pSVg.9I J"$*ѹYhtCIO2UAx6n&hUg)ԵH4SH堥J'0 F (6h̾Iq `,һ_iسzJ +~-eTm(,U ¼uBp^hՀVA\rTٔFcRP!n XD$ֆu WzHaP (d_,:o=y m愉Ĝ*x,% ìSB`P]@ l(k?ѹ#vtFc|6{ [ND}j;jȹ#KKr T!7;?U,|閣ZzcA1lלLYiׂ54hcmaX t &fSlڹJ3[Nc NA~Kѹ)YlJ[68b&w}XA]ђ՟=r,Eޓ9Oΐt3nȼ9:6OP.Y#>? VpE ^c!V®=Ы\ 8IsyVX~wwW?ât6~\=;%景̸Ʋw xaqE!4JQq csFNGJS5NqJŝtf@05_5i[,|i;@i,X8rfQWi$ɝ3f_vޕ]I&٤;X(lvSǃ>GV&acW}6ָC=pz^t\hLiէAGw aew]{ wQbt`Nْ 5L T!b;ȗL6mDL07Z}Փt/P!;-^R$>2 ߖɴW4;ɉw -i֚AӥNUC*Cn]VV::߽ FJTWJ;TǓ]=n+=>ڎŮ~Q-R.DlQbƥSwIJJ濇zFeUvrDz~f(CD YdK6˚#yFF.2w Cмy;|_:רPJ}I/93%{]룤tu)u]-/YkZΞk靅ĽgeZs+Bk,aJ"?Ky{~|j|rrjj4qD¿)Zau]z]j϶>;}'>I^̯|;FgcX?uyݥyM_4ͷ_c<|>{_}g^oFOx^tO_Q|catՉ=?sw׃Cܣw>z}op8ҽ|?_oy'>x/_xo|-ǯ17exݳ/}?z|{s6r/x>zͻ\;wo nyx]}.E㽗\|0rM[v+T~^o6Sro:q/v}c+>{×_姽;o^e𓍿:|ſ>]wW\{ &ص/xϣUvû|<7,o~棯?;z;p?ܕu޿?{M{n}?.|@?Koogoa~_o<3o>~_'t~wߴ>O^﫮|=EW| ޓ+?=ﳿW廞s׃'x]ث.s|{w[/|z?zjGx_WʇFnp~4ry[oo/7I~eywm\/9y7=[_x%G;?/?/O\[{zޑV_ϼ9k{#kϼj;}m-?{O']:٩妅?\|֯;W_k71y[@(|{{<'_-?O?y׶z@'/[q޿^ƟN]Kuk/Xog,sGo}ˎ8o^=| x_F6>x~+(3{,G~K?p4Mzqipyk}{^v7;t{njiOw?WK[߼>?Y=ѸN|nz]7uϜ=;-}яг~ O=ď{?eΧOw}͞ǿ.~=~?.jl];]x_q/|_O#?6uwϩҗi#|;?[zsO>vǿ;?n^\;x*f/}JN?}ݍ*ɷ_ƿ?뚱;~w=?_{;}}F;ܰzL|ӗ>w{=gw=o==?/}'7|o[<#]+׍ɧ<]tw7<p~|O}{ wW_7~__Ag~]㯺50Kp9+U(Nh^oyGV_^Xo^|}i~7杮?wdc{~gG_ ӓٕ~'>yމNo3a7N&m;ض&m۶~VmV=vUw* KATƙ7^|0KB".#Z4-s  (P"=Wv-v~&z m*CEƛ7q9=ç,DN7PnJפ,luQrZTrIeCV<yߙ.MP1)L.`j1J˭7[ݗy6FT*B @%L.>b8>=cZ)cw(L5pU`!fʧ:5˲sE8DJ$>N~oO|KUِH`8ٻ;w`\zڟ d+1n*1[Ced*&">+~ZI_ ޼<-A)PkMF19A$J~΅xrIwd؇f"D;iC0cg#$ٹ☓F0h{ߐXo ǝ㛇\E?Ĭ5>7saoxQX#7a%ܡc®!dYHx3>eG)ߝv^4s vuNƲXCfזLZ>.j~~Xw|r"[ boJSU&|{iKJ'uXTQ%VR`sE@iTK.FPDP$U A~gp{rg?+s=swsGAԓyWR.eI-wK5SQP iU؆jBlrs㡆.#/*F Ǘ>;NKj2WU5Pz/DD>ϒC_x-|61B<]rQY 5fY~K.xlȦi3Ŷ(.0 jϘs&Ǽ/tt!Mf=1ޱ2ܣ,X= MVԞ9(a9^'}F7NfخI(҅XLi_ Jy\Z W3JPżJq4W~O`cI} C9W_n3ռ*$mI+hy96k{i6T(*_o|;Z^x~@>UoNu.'E寓g9ؑ-S̚qO;0`IW_ +Tq48x崓է*ޠ&6O'_8dc;O퓇cK][8#O`[vҕ2Qy[]nNaҭ37.u(pc@=|cyx@m?nh8kaQV6;dvmV؊8)s 7ݐz18,[RRM"oqde7+{xT.}zPO/C_W߯_[1qZ]V?jbMB6OVQעIWD|ј3aF}'*U->=NM]S0/!g!F e 63m5|\viG->c&;!̞E1D\ʞ}g ¹>e4¿l=U~o-j)({FOƃKOvW&aDNz)99]h3KRq6.Ktc2|[3+~4~8,VҠr;KQ(^F߫QLq%ͼ1e[E-o"2B*.Z7U*ɠmG݆+%OfsG҆7ǰG-:3!$h6)cO1ֵ"N%۩o_&Z߼~CIH=˜ ,NTOm\dggtcݪ>HJwi1|y2ٲ)!jMjEL<ŝ a1#z&k]޿%i>,uՖW%e8o}a]#!0[%3 [Oa+KY钋%v-rcK=|lŦݺw_=ynV@^W= "_ 9Urt k#ul\e!PK:..E@̪].<{"Ǣ5.KqӇt#?5uLDҫ2jfIE] 'ɫ)x LjփJ-*7;0s.Kd5K/R3W;y0Uҵ_+njeiC~BexW ;Vn$I2TEIJ_ҤJ``Qic'I#(qQ:M9w\^V6Vn}S"p}`ihp1I|ؤ.ԭZK#*I:|ST/܁Kf{Q9GΌr|rNݺsk΁ gQU4sVW*ݻDC$fYx)5{V$u _eZ5][".Pȉ|oNN3-xF{ܵ!'tCWϗx{}| so!o&I=kΟ΍59y*Aq s8Voy-ÓTֲz=7\&ɬę'.'bC(OzMb4e~|ldi]>3j4gN$~T,^|7R=_m.Mu)4yl nn-(@i^DĀӲlJE /ۥ0ۮ[/g/ShscP/ 3RR@7*O9aeT5 x77HA/a*籲tRc|l(QC/P{ѨӳnAVbYִtc6bׅ\Cg_Mh+R0ljӣw vY.'ׯVWJ[K~Q8oCbM.>[F>cTZOj%NoPp}8}PwtD0+VQ陔%;{]y?  jWr٤:MMkhqyq2}=C[*MR5΃^RLv|ؼsw̓|$Fxtu?܉;Yo;9 mmӉV.;ēck~N[t<}9#]WBC_|A*cFMQ&P2&D*nϾ=RqpGiwvZEcvjWTo)9r#t*uy|;#X/aTpűuRN%Ld&0CJU^a#ϊi--h%\}fsSgJy =ɧ>:8ݙd&vť7]3|y7[ ]⟧MG0YufVY~O7^ôec^eYEbx,jh #Mm2:w1cyjM3S'Jf_ǫBZIi{lg){/ V¶ +Tg=FvJ7פ^l2[&4ϾI̻@S[6c:~ O{&6kZX|iat`4ÝبR] 'wbv뎛; P߰3f ?ˣF#ŕ7}'’fʷVW^-(0>F:^o/ޑr3ߒ(wk,.]h&$KR]S)xg.GcAS`7JIjR9J9 #>G.V+:AوYWS]CJMú/7.sto9*07^ V2?wNJtA^XK&}y9 Y}2 *s'(XJڛa-yܩ6 \lpUdW3C |j9̰'Mo!Iuwu\Y2fο}͕W[yuAS}ZOlrFEeoXDd5,J\ ^Gx <-c ӆ֬#&Z|:(siģE51glr-CR<ahqRGow{]76W>mKun͛}ϖrmf*)Y.xEޠN4ȽɿaٙRƼ<)ÒX'l {]6զe{' nK! oDH=e UObVJ_$'BUyןE}!7C2.}vΜy*W%4Ac'g;'zNjn~zƭ|EAv4:[4̗LOSL1o2xaФ#cv3f{=tGw3z͹A/sa^ c4δo9eMtFv8Ƅ#zQ&cê/0f֝ *&.?Vr 7xN}݆{~Vxm+kWD iq-'+Ai>3U )!J; 'tm)b )ӣtdA8Mg`g*d2$U >̹ MR~ͅ{دYaSU)6x[\SpyhtɺoS;ce&$Еuw|ylOWi)N=nMQ U~G4Mw꺺 6 8Ɨ\NjtÛׅٙ ~zWQ.dR H46dnKT<x$Ln<.z&ˣ [7l9{QR}uq>Mu>AZ,ٍVg{je]gzDvDij^cΓ:IUgRv](pBEC6}4{ћuկؔ}3<R9 jd7>U`{ʫ)8B"5ͻzn&LH3*xZ)~R!ˑ>iucJߥ|֐>vOegK .' W'pvD5$VFw8y=<U9|:Pyci_.Bș؋,҆>#8|&_Dzyc 5{hil(xGSXM'a`7bFW붿}Xנ*.|H\[[>l7t(c ͛#4pv(ժ06 oĮڡh4r湺KlȎ7o`_l395- :Ҧ7%bz}ZaΤoEϕ׎ }2NZ3 Œ]^Fzoʣζ:)7\٤e|_;dʳJ}敟AzA<"+ͽ˒\*JHݼ9:!6+jQYx%cBwj*F;ɇuTzW/Z>(ޑTWy眙Xߑ'sli6tGjtܜN9Lȩ[_YIewo;J5{B)c( K )ڛepDžkɭOlMz(aH0͹Mhlg3Z+ʗE sl ~ή~\x!lF~L[vP2n)w,9M*ycWM>CSop8ZTN8ܵh0ۯRΘqK..4#W7ܭ6b}͝cahW6FBg/ɴ|0oJ6|ãV!VGO]hQ r5Tu8Wb«BBfWv=-,'TR͟v#~vk=}<_st5,1*!|#C`AѪZj. 7 V\J}J!n|/I {~`xE"' vQȥg==W<=T9g县Jri(nf:~b/u@x(_Ao-[Ҽ*]=Cs|kײߌtOM"r]Ӕ|y^5{p/Bej!{)Bb>&Ԣ: ֘&4Dok卉y Cyc; /y{PDkH}I#AW60f YS{IJ6k?QpRQ9牨co_ / 'aOMVx$D}鬥6 X=(m=%P*Wz\oeVbfczMkҳJe0]<|❋cSP)%Íw)#sW$t{. ,6U{O?:m)*@] {U.Y79+_ExvWDGw|l?בw >\SǶ1EUVH W<K۵?V<w*0y=W ~7VãJ-zs5!JQojny #+`^^vȳŬ^U0^9YIP,4MW˶࿬d_tgȞxC_~څq3|/vo~Wy"K%96'O%hp9ݞtW+5"~辰+e.z;rG]Pu|:EQ%Z[(kۛ,!= 0cDtV5&gr4E3vd|n;-բ9}%*n~|鹥V-9mZK*J0cTr|*?8jvF.S -:T_&ؤy/7ȫU,g4ټ3MzSɴF%=ӱeST='62q.}c9cѠ ^bdT֞8KL`6ѫWW}6cN$#e Q^gZ۲o }O)BSSPWڞ=VSyKSoX#*|qs%[_ oqR?Y@)Zs|I~3Ǖ_f3vNlI6ضLLn8_Vv;{@$PibmnjG&6s]* T`sxUrWw^͓-L"RlZ] KfթդS9dTȯmuv[gBe1gJ>P]oIYʨO[xB2wfOxXq ,({LUTs6E">$4X8 qQAl>#rF2KGͧZv }W Y;+rv7zY-79֍e_np:ôǚA'cϻR,^21=]..]YmvhWy*;2 wF+I nQ^",?"ߣaX Ήh7-} <Ө#jo%G0pM?BU>̦rMTAkTBv-M|JS'+rK7g լ3M:$1ЭQ^'i14Lo*iR{Ά<xV[N ES;OGd>Gh.S(V h;YV]ȗv&S`.31gY|:ތTzy)=[snRMQ֔iG44WYpvٖhoIѼ%Z/c)7vl={/3\1|7e+n9̣s_N4TW'N<{KrG}sOINZ̅]IXy)'ٯvDP)P/ܶ7oֹ}*PI4g1C/d)vx4`)XXc9WӧEnW(jnݽs۠VvsWOc'-9̴I=v}- Ɨz隁 J`94,`vE'j3,JRPVJji O+zb U}cG7e-8ߪ_".KhQ9/v]k :m%U%-Abn'~ln}%wd4ov?*سK+UmN=/r!{gZ] k}+ ҇-1;=Ɍx,q塆uYIфaE󌴢.HsN5JmEw 6)}.8 '<hټ4.s9[`MV٦53:lcB/1Pe~hFy$2)^.Q>R9dNlN;#WFizkp+ :I) 7'ܯ&Y;{k jgOz>Ri<=m6*]d:naA7=WuPx{v˴DTK~Ê;HJrV.vJbdfRq^tkH1xj9LYs%.8xמR$ :EYD o0.hT)s~\-|.OYSf$[٤U^z+=]M7VTEI45ϖoIݰ4=_8}nbAK\6ھhFpmχ' dE/d+  MKE&^EULkLꛗNѨ Puô̫lF0FGO. ѾL[0ǤV!]ʆ|;†잖g9mX>)))ƼS_KO{愰Sw8ìl\B_2ǟa\ 4#:|[FQ͕3tEU2%1K}a kX?ۙBl5UM><7fb\v]l&sޱT'``?sgߞvC36wqEXGw'hnqkmJ1F+jr?x߆6ܻ=T I@5lodwQ^y BKN플,mvN_5,3i$-&Y9Ǜ#IIp{/OK4P,yާdA063UF0ߜc+VjWa_*n h|M^l~XoK#tYOJRnV^}量]،r?bzE2{idz`͗mwtt~5Ogy?nV| <6ы`go{ԑ:yei];.U&˼MͿ'KsE̤퇏윦z[d+ɥ{ 676Nzǧ?{|S8Dt栳 :x0r3N+AM2R+mG,K;$7Ocj6upQ-8ՙɓi||]EV۸gsCLVnƦg?a3/j>|ECn' 'U蔶z3O&b?ǠXf.WNoh]PRB jE>&dC'6>vΣG~7S؞tc}4.#畍+_l,KMKAK]RI[G7Q;gQ$ǀ6Ģ0@,Ab7H  ?ߗ+t*v?߯d`8 a0Gw[QwWKS K wKGꍇ~՞V&+ p%f`n.V"07['k Z ''X4 ً.ݺUp4t9[m? :zTKmus;tN?jTp0 [7wW[3s@?]7 E.K7% ڥ ^[ZNAIe7t0¯}\MLWL=ܝ:à.fDlmsJKٺl3;þ?|Zߥؖ 8Yiiwm!i :KWh/ Q +t:m #߿ ]>D8wwv7uGbJV=`n𽫻lno-!'PlU(ğD@ U!AO?~\ q[z[y8B*PkQۿl_O]?njo |osaG!MU~.G[A[zZV$>n!(L]\\0Mszm~2:CV?VK.Ytbn{Q 0)>߷>Jm b8 R##xQQY iYQCZdc!AGX:Z:P(R 18FaI?Ɠ PX<#? @4 d 4bޚD C!4"H? GH?ttpMc[;?N U H? hBI#p ? dx?>Z?|XlUyaxB k,Ɠ@HUM E~<9*CaG ( j5GcT Qx8ő|+ZCq 8l4ɶ?4"mPBX@D@>0 dcuu "I,U%x, 9,! ܟ?X x MBYkX4Фd=ǂФz Aw!@6AHӃz1½Dah2#hu"?ǒǐ`!XCVя{C?h?Hk`p,OJLIX8b_S?B~$@JĒU?GD 7F~cqO4 _7?W9)k DOG?jJݣ rs=QG| ??z@4OfߪsX\S(5KJA(o531Râ?eW ~pz   Hv?z/Y_~k4RGpbI CDWv7Q^O~?  b"W x"PCRLI`SxiRM,VęHS_$p?4???_7?K!?'@A?z}@BBY?x4Y?H"O>O±H@ "%!z5 C'=D}籈HjG~ @ñbB!ϏXYC?S?z׬u W8&%:E4 Կ P@rI9c~d8qd7B+D@DR"@q5 %$C.'dN#H p2b'RPho@H2GdoI6`Ih#8 Db?$2X5#y>j m ȊJ!y}:# xA4t5@$ ?@?op1&1FX4!P x~|@Hs @H@ = CtCN?zO}oc_HЦ!ŏ(ED G&@~? XCJ 3 Dzc0$$~o;Od@X8  0X0G5]PG` 4$ǁHRJC]H81(8Hr 4OF5 !_X Rw>_? 1,j/Y$@p(g M Xt%h4'GOz_7_Ǒb'i!D ]h'@j >bW;>` !HIP0fh^c3~XD?_88{ ?@2Ek ?ĒP@p c$$T#P(4$C \14jLh)(4|!AGp2 `?@<A4C! g!!c(8¯1Cehz/Y/C`$jqx!H?dAxw>?7!lG ®fY > kpm X~k A$@(Hr~$4 #{ 8mgv%'tzIVJ;jbU4 C _ ?*t #?fk{8'fӇ.h˞Ua-/8ea{QC?CYi4/5\Kp[%YښoOwG P9 Ǹ [,==a!`}d]@ WTXZ7=/N H*U(6k-UKݙ!?o\<:c~ߔ YDر+ 'Jr;F3?qX~]˺p"80&ɽ:fv4_k>?*{d_k~(:ƅ?#߀<$A>?O򍷧CWL[-ј@ת<͋5? vO?O1o%|o8c_I0SKXȊp7/Kb ?ZǓKx91ܒȧNB޿CuC?c,@>)o./Ǘj<0az V{hzl\o@!]@fާ n@c%1<}y4QZMY%t8 ;kԛ W%qS{Ml5#3w<Sk ޅ6hsj(I$4 `^5a! q$7)k[H~I[ P}p$2@,I@0v_/=$6?Wο5GޟrvtmH;\'Vῃ;_7 7  vuG`J4Τ;SBCk?u؊rLZvTM`GoXpE@/q @}~X]8y//C@G@w՛8 Q4@96im< A&碴#uOjTcu$yz漘 À] $(C0F%Pb{IlFϪ_gGsJv@Mw O-' ! Ѿk>M\{z? y'oGjI 7+ʟ-:mzu?{$6?S]1 ۓ* D/ˍ<!@;쿚G\'-zLޓ@?ԓHqm"s6Bl@TLK|C8$6o9k(+g q b qӉlHpzS$/ֺծr.qH<p( /G:Q Ps!oj%">g3Ƅ$6?mO15RvX3 /vշ ?@df.ޫ?@C)YsO_]݋R'@pßl=Ÿ$cB??t]X\ zlޏZh>v?\+9(QR_kucԀ?nQ`&Nr5e@%5pm<)6o9 `8s)R+_Ux߹}i ssO%^1j%P8/jh'[a/ O H@& SPT{./Ϛ3yf_;s!GPV(¹@@:+Y *|K6ŘzH8(mTLv<#p=1$m `*P2bI^6B}/e,TsTG0ئJg*T\k?퍴T5+PZ?Iso(ʸ}:m3,m:1B8@6 ll3J;B[ /e_mQT:`UxO{&E?T7݇?`76<%(4R]DaCuZ6_Kb @oowd/ a]6Oh9@?M)}_އ$bR:ٓ2}7U;ǡšWăϒ 4_)-?WW'?H>?p]~].p@U/qs4J9Xȥ?AW#2V ҿm03V@πg?u//ϠaVonqq cߞȑyPCĺV\R+}[F[c˜R?%yMujzچD vj)ozݚF\~ /?Kb cO-1*fIꙂ`hyS3P\gI_7,%?x?R [=|sXkgԺr=~쿒7\rwi#7S }?L?Jnj@wHt l=%G?AO --?HXݽ,#?xkqEl4G !?݂]eUP" "ZVyJ)i 0X`Go# vZM#@}ٛP hpN`cm$g +n%iY);F<3 wg u@P{q4)B4"aˬמz,jOP'H?~_!zl*)?/%{g;ƄIlɡSr?GWGI3`dRaOƽc(1%hK]~_o!_-5ޏBWnn_=/ϙ{ع؁r^o<~hQgBզ9a=QWsNo'qqs$/D9K3K)-)3{wIlCW՞OkG./|yD -=Ppj3Ӽ?71zhIö2" *pq"ozz+W??e?m%Y?p@r!TJLZ/>:)z V9&%Njc< ȭ4FYH w2Ja#x@XPR@ldk<_u|PCH{(/8]YP6c\0 `]o qdRF,;dbga{BSxt #;Ƅy9?OhSد?7zw8\O!, C?OiԲzj: b?i / Mrԇ%/b>%.k;~./lIWsL=P< ތ$&!?.\4%ח)> gO⺟k$@o͖ޟS jJ6xgO@l%5SH!G/$qoا_XWϩSmLR hrHk&_m8.2m%Y?n@YkXGvʹaU ʐz(h@+F^:X+,$X> VB 0 QIkGoL9M]T &7%Z bǘH/gG`Nj/~O9bs?M?2k {hryi G.p9(_2Lǁ!G}?<ߟ?5ePgP;vp 3Q 6_PKJ;lCz6B U$#gZ7E8`Q'`k q# vPj, 0/stj@[s]*?|ϡ?ʺ3Qk_ =seӆ[:J$-‘φV ljn_n$sd< pj=O@{WcR?^i q" ֮ *g%ő@(m @Md@[??g8(2Vkr/!dr;3$A P"@UW-L7U__ե?;gأlGdRՔ8M*X(6gJUU7<#qrQN߰ѶڗaUwx9j!4L_WĦοFhV +q%{Kb ??Kc8jU d`IylHh?$*NQyF~0ZK"K L#?8$_]4)hI>x+okb HnձHɳOTa8w0{7~v?z@/g=2{;ן8_/TY=X]4yPJWiR;ߡ3'UX !?vwkw 4s s QOn?0l"63_;SS]ж'6k*}eNj%TSS /7_ÊPнOKb }FF5<ų@\3#Rї H1Ӌs3F8NJMq@R!jzp\ǜP>t ;xXnͨ0of4#OSIlpTOYnB5[xR+ϗ*v"%v>58(p8 W!&(:NSR_o?r/t;X$6?/UY]K}\ȎP{93CAI"7:JEh1_0xc 9$~Wxh9@+JJ$6?7;ӴQ[A(5PE3wj\fF*yJ76KuZ;*G[B[~l}]˸=i*<7&1 @SY:HXv3 X~RJ3 @Ot?.*8o :VaJw=okb Ta zAͱ@TuGD4U p+ 0 H:q8&#Z ,xJghM]6)q '@1Qp<4PWfJnߓ1?n$6?#?*ۂUv7cCB&R NF:Rk1@'@ B&~@WtaX`_n"zJv4 gSƑL{RM O .;m_nDBi'KvCޚ?7kuH`)hG ! \?h NAS@r"ʓ֧w^JC, -T:/E'#P?v7` WvX&/7vY+<Ͽ¬f#ck+iGjG*WO҅Na0ʯ'YcE nZȕ0Iw'`qC_@%_tT\ rҽ5X2$M\?P)x Jw3O_ڿ~lMl9G,?3sbO.;zWc PϭCpN"4 \f<3!j&QXj[MF"rh&|p- K&b6__߫s||}vZ(}`i8*Y'dl2ItYC<r% n;$%;& ?C Ȏ4@N)@A LH K"!T~g.Q=*DZ:&^0hi Rs%(^ `5`>slݔH5?}awwL[vp4DA:^+38Ъk%:P^ 9y$ZAT! 2? xsn[)\)WjxT{Lk #&X?_tZIԔ $|;q7˞ yoȼtS ps8q'`[RD6_n$s!q5"/O\_ׇg;:j92:3,Njv϶}VEhv'9G  ] A? i-zA //Ϯ?@sNa{G@8l?|5TS I>XMN_$}5 4@ܒ+x Wa`hWg_^Ml/{" c_Kށ3:1Eûw+*p~I-/_>u-?YeM܁]_#P8Ripp&'Õ3(UCuLYMlo"O0W_2`L#k!| ?UF?Yw/vW@ÿ ?[Y o \?}sJi=N a.#4T4Z,çc bmw!{[*u^w5YWi_|Ov/$6?gqC>Md5,iZJ{/ O>^*G vN þX׊TK:Jv}Sw/7m7?_?ONd+s#O//{RgF𾀷q ;zJ#ݓx0ob]? ~ I՟?TMxWwIl_vz&?1/> J/khK;P ֝?pƙgTGy`ObXr8g$6?I/Z #k Z9z+ڀ{ \MV@PVf)4c4O՞:Z(˙qhƻ% $kd6A@)ky/ADG XV'(&~!pp03g@eaCMf@ cum9@ ?0dFO`[u3w[$:0t RU1(RngI@멭v#|= h*X1/5oTGH Ɲrh$dz/Y1#?+b FԪRvXh!8 U"9Pχ?')}w_;YϭVly8VOu,]8+ܛ`ZQHդ C>")? ?z'A_ôPF Iy X;м/e; P?i]Ahw)q85jK8Yޢo(XjxnOw }Ϟ__ ̽ @a:T*%HAP B:8Ylcz{(Zܻ# 8Ԟ@V!䕉%Z ]0**<8c.8 Kb 6\ >7,aWܶ7 iDX DCC*vYȟ]t pHrdςz^cدEl?9n;AT,0q{M7Nj=y ?a@=?>JS4j6}?n"6?7YL`uw ex?>t1uV[DN?ڟ>:'@SP\[7@B @̩@'1' .fNb&[~WϠG%rX*G<??{/L/+9 OsyyW;>'BQpCpltx&:j演ۼ}x)t.C $hJV {e%OfY?/v'OjC#\S{ "3յ *yF_ Ww^6h^49(_om^]?ۯ+oߧ'Wcv,sS;|/oJjj?$?96 2LCJ/?925K3u{=_ײ?,Mz)K?WgÁm oht.:>H1+_A\@>6X$|?E?%,r-?@H .?(~6ARC'ãYȱ 4;^CsVf*+9ȂKY'X R? )~ <-)[=[T a?o%!߻c8?~K_m@?tյ)[W[MzYqlZHc ?ż+Y+֍Wg`*񏟏  [l]# P@HlKQS8JY:^k*.i$jO_K0a`N"wI5:~UH% .a$6?ig>sZ(NjD ;] ZjCe0Fund@n[`_@g5z ` mlTwIUs{Wsu }`k͌!a> 0! >QWٲ&Ğa3VWݲү-qֶNa]aV¸¬qYe f-+*[WU00l g}6?hSm2{-y9EK6EB<_KJ 9.D@ }:?@߂>=#h;=c9]~}ZbЅ݈6C?NtkoK{ ztyOTF<7B;&] @t t*h|%f*耾pz'zZ?KKN"n. AKbC΁$ }: EMÓ1|п@S(8oCSgZBBB瞊2CAO.D=ޏ &9 4x !h#C>hc+tn?_]1\-:h50s@gC' 2:ϟ6>ڭ(!!Ch:ng1Jlp|||c*t74 :B߁Ag9>wnh t#\7Ѓ NОj3ogSWD31Y@t54cG=1 ^ =)(z+p>e4{Cs΂:>f$֝}\m ľm^C|ЯO?r;t: 3+Bq.eCW(0,AKk 00/<-nDD(#ko"n+{&]OGCnv5c@F[3ԮW:vW{p'ٟ  )M(xPFFz:*eBZ2Bj`@(P(@(X,S|BBB11˅BLr3qBF3 FaG1b f g1˳322zx'xәo.7Sp))8` vv0Kiu^W{p=­ ^.\!T(xPF(X(H(X*/#)*( 󄂙BAPpPpPpP0A(/ *z( *z( 33w;LA5S|TP\g 3e~,?Sp)g 3Lx?S;S))|\c Vg>Sc 12?2_252^`)8e ,d )xG!SpI!SPz"[)8=)qSpIS0:).)))xRSp)a ^TeR,r3Q. ;S0ic NHc ,22Ǐ` !)#)<)8r(SӘ1Z 07 ` vId .<Џ)8/Sɽz2vg fu;N^W~JxcStdwZv'9AF"Iv=fC}Qմ;ӭۡH״0%^Րpf$Q/q'Yn-~0zqcܽ\( ii=ppk;\f/IIkvpvO=t6 =^QӆǬoMէeN:M գDEc<MɈ&.ɰ45z0*(Կh5;THa7cmuo~mb75\8AOq9]V]kǦ ?k&$G+Cf!}M\Zx%b~iyqBKeIYu"PSm)_6~i&2`pHQRfٰ;}HY]qx7w%zC4-HDCzQ-iE7QZH}%#Z R|'c>+CK \DʧTJTLRPUck+蜠5Z wa_  DQjgm_Z#=:;7eVB Cif5Z94l|+KYy~| ˥xVtO٨1{G$Gez-LX@_q_qעYqڵ@KEI@Bf3/ 2r8R52͕AљqX vkdOHc Nݨ@d4K2zjɂA`RZ h5r`bF|W0@Hg70x%=$AE%A,BH"'$:iڀҀ*Zy^p'D4ܭH܀(P$3h+M)WRi ,,),+,*A(bs[/*+$ST3*VV,Mv;)$o ߽M4ۃH\!t46i!]JA?Pv5bl4 e`{) H}p.G:Rki x^uj UX^SFBO>!!,Ik-+pi~DI,rSp=կZie%2d~Zxr^ :ׄ !}tQch( @34wtPKw7`aPK\: content.xmlWQo6~߯04`o2-)-v_Xa uI$GR_#)RR<~xȃaU 3 bQf[j)N wM y֏Ur.u V wPK}/ PK\: styles.xmlZ[o6~߯0Tlo,xM 6 ŀkAKVCmI,n<.>_a[5_BH:Aw捕$TSkByLF5yp:i*4f$aq h'3 8(&i0w9wOcFLY 4R8(!yp<8ZQ؀o4Jگ3: i4˟^ÞVY}:5Ƶǁ?RBI Ɠe64[dw̦Q8D54?g#1Td#$ .Oq?"coAlg?򡤒_$'@ JrCc%Uivlp. gAB"apNw@:0O@E0ca7숻J1pC5c 8yc"R+ 6k-]R*B,BKȊn4tq@ ]NbgM?N~6((zY|mhFqփ SvM  +L[#MA 9ov02ʪuen glbtb" -lzy;j0wKph[3H0/tC'Ar<5l;iO6ZqJS^e];Le6G,/(s+{Z$Xu!2Z*&RlQ~a$Zk)þA*`UeT)VW&^UMX>GX4}Zd멦S \VtZ>flNœ4ҽz SJG㲭UobT&IheF^&>%?}99CK~%&+PJUU`+b&K՜b&2#ٮٷ89\;N:hRS{BO FGOw?n+Uߎ7?hp”1%;\wuczĹ4ك6ε3V4TwFvE%YC'e qUZBPx-G{dYn w>Q8`oEsOCGš'uyyº9OXu{-f H=JBh܇;H AՋNPyƵ2羅GȊd ԣGZ}f#mBp{OҺ!ciۡnx09n< zhC닢u(@ίo.7Y['js lTŴdAp.ti~[Nq$95[.fPt꬏&3Z+Ɋ?a]0_{yhRÞI/ddA0F́ep𖰮 Oi X"ͪpFK Whu5u~ -A7[ZQVǀy# lk(qWg.XBڽe;3Kp^yېb 8B % #+O; *qLߑ b (9[ LP5ǵZ _NBYQ 7`GR^4 S" $Q\rU=+aEJ"=%(N(Ar)*#71F$w1EZL2.llxכ= ,5:kYS$EYW5_ጲko=rKιoM s|Ƕ w9KRZ(Č~"\öjIC(P@AiiG(D ]ޖtaTEv&0g xq_2niFvf s"q1ɕũHnkKcla^a$վ~T7aG;Ҳԝ>QHn>$Tg U9xШi67ѥ0hl }ח[zbP)Im *Pz~*[kZHm*̞ k:mg<9 0pn &Ó9!;>Ncv@"%Qd\VNvek'n$D9Cy@8**R˗\i_ -\ƽ 3>B\UtLO]n ]S-`J5E]l;B$#; Xdy/ْf7[T1J#vᦈvGzĐzLj Emn׹}%*kV2f/]D+s3&&UBqa^ <1%ty &n%5#9Td_|ͱ±78MC:({w;UI)5jNCs!>!94*kCpN?Uݞ$+c8# cG 3;CL-u7/DKD}s _á8._O,ݖ5v=}G)Q"c52)yb{Zc(ਪ̧Eg0ĎC?մU2RۡGUy.]DLp"OmE RphI_dl_Q<dQ'rE"#;=Oyv"_k2Hak[$2/Wq=2Ȏ4 g5R;:9țь lCJIخ!Kh1x@oOƳ b;MX˧ͤ1 0B]iTJS"ьGfFvz y4>]k"ġ Ѓ):H)2g8{. OMwPK!$z/PK\:Object 1/styles.xmlr } Mڙ'h :?} VI}.4M4peI".B9xЩx:BU I o46 ,KM9$@DҖkbU">M\ ;ط Ll+3;طKEPٲsO}PyM\A̠!rt1#㘌T׭`q]G dgIWV홫QCMFuT؅:zD2|D|Dz}v[a4}˳GQ Õ8 ۺ 2sv+F~ŊwVHmYʊnifR'.BlEˏ[n(3~vX7ԲpqķWVPKp:iPK\:Object 1/meta.xmlAO0 ,6d/z6bfH[{l7N6ZgrGRH+Cu.^_+n2>FU[$8JN-R.zKKgEV!#Vsr36>s}a$b[C)VumfҀ N (^[jb+1)h—sV[X"yXw5l]#-=bt%<Rx,Hw4U&i3ȃ~PKXPK\:lU99meta.xml OpenOffice.org/3.0$Linux OpenOffice.org_project/300m15$Build-9379Joris Bremond2009-06-23T10:54:302009-07-28T13:39:094PT01H34M49SPK\:Thumbnails/thumbnail.pngXgT~C^{AMj@&)@& HG :Wi CI.^=߽g?ߟw͚={>{~@W*M U}]@)巒TU~ ܙxueoYhG}P^X;iL4rSpVJ'c 17eVڵsϝYleV[&㞪*Q~U{6#!aN eYޖ'0<M,Bt:bE?EhXP1? ccƓ;֧Eu9͑&Xu1ˉ1_ʮ8(uZeRg!b[ ٙRKna摘8uS,:2/&z'D(^WI;0PuETě7i%faἷ-8\83_I|=1I x4zWQD3;wKZ@0'" w[}K珼D4K*Ի w;T] Ov8R*yʬZU;G+P505 ,I!keSY*Tm anV[4P͹pPm,7N|)Z beքuE 7=FZIՎ X=$<°̟hT'_Y#hnºI0m׻[Zj(6d[$=ErqSr YXkͅ?M m皞QNXX%4-xe>ia-?qYdȔ"}䂧$9<:_rֳG0C]9jvN z+S/:p۲ Ī£TQ|}RHg՜CcszG*+×%.9!'%(in} ޳_CYmA<S bHgUDb8dWSޒ]X|F5uBĘM(mk]/pb+Œq|pBu 85[ԎLmhpˎJ]NplP͝eUA5-x#4iϲ*mz4]ZԨIpiDP6󴁺aAǁ-*/.]Bb_xH7KQᠰ5V`̛V,4+j ;"U불<^ݯxKc iw--^Ns-t5,;c6 ~ K:DMNVB>քBxiwS|ƞr GHqhDʅ b ֱ_ VWlhd#F9.DG k#6ײ^<on 3 3'i]]UI/\cRDZ-yl },Y]߉,CzCYvGƀC3_%ڴ{+ W.xMXuH Zj]`vhvooLqRb": ۔@5YZ>-KѻK}_^ {"hiIgeaLJK2%I0FV͂w|o_PN#OULЛDw3|w;3avH C{- /N譗Sa4%k-s IVteN<\V $cQ}XZB}vVl hBoC;N6 <\TZ3#?Ro -h}궝غp8snE_oo3z{d<}ܔpr/ 攆_cgiaQV$@ZpimL/XNRḁH +.$Qxa)RwSgnggdi{~r*aFo^BϚ+%6x%H,DN2P-GH p MwWl}=rrTxύ5W[}[HEjƧTR͝r?lj1Ǡi=XAOdڒInږR$!^Y|ԏ3M@ Uq8j/IJ|Һn$2T5!G^,j$x85 RlmI_f:1D_1Vja ?"W fBg>]1݄9G! _LNCo880|/#T zAc 氪K-T U{S nTx-`[Mx*ՅwGꑼUa[d4+@{*&TZEX-˕[0Π@"Бw#sE^&ma3a2Fb/GdSd_f.PiҮpܐB*;li&Tu,&7r ܶ5q%-4@WXMM7Y!gm\q&]fA\9{lrp_#,>Ke5ųuv_ZBy{s}֨Ea@sB艀2$م;/ ahlZ`poMWb`[-BTzWZ+ֲ+D? 1Iz_B_*?(ŠGOn?1LHc;W#2 tZs +ͦΘ۰K,jb" YMP.R1rt3"Vji.aizc_Oڡm잿u|n57&@~.wZ˚EONQ󫫫zv4D9񾪊/U!ZP&Wvqv^j!_k2u]*(>NV>Gkzamߟ{~z_aޘeJo4^Ňkl|VMt܋Ox~>)QA0e2`(IeT鸕ms .&[L?b2oЏ>Sl) iWB'.i?9H?dT%zL#fE[OQtਯIj}&Oc :\iCfHoKwQ)P&mvDҷ;7$|NU0xGa2$QY 6;1,(֛L(\*öGY\y_",70 >djͳeM5yw.VΎݝ-/^tϠ,"?gRahVV(vqnD o PK^  PK\:^2 ''mimetypePK\:MConfigurations2/statusbar/PK\:'Configurations2/accelerator/current.xmlPK\:Configurations2/floater/PK\:Configurations2/popupmenu/PK\:JConfigurations2/progressbar/PK\:Configurations2/menubar/PK\:Configurations2/toolbar/PK\:Configurations2/images/Bitmaps/PK\:w7`a-ObjectReplacements/Object 1PK\:}/ content.xmlPK\:08 " styles.xmlPK\:!$z/Object 1/content.xmlPK\:p:i&Object 1/styles.xmlPK\:Xz(Object 1/meta.xmlPK\:lU99)meta.xmlPK\:?.Thumbnails/thumbnail.pngPK\:n ?settings.xmlPK\:^  ADMETA-INF/manifest.xmlPK2F./oar-2.5.2/misc/fault_tolerance/benchmark-results/analyse-deuxieme-version.ods0000755000175000017500000006152511757171206025761 0ustar plbplbPKU:l9..mimetypeapplication/vnd.oasis.opendocument.spreadsheetPKU:Configurations2/statusbar/PKU:'Configurations2/accelerator/current.xmlPKPKU:Configurations2/floater/PKU:Configurations2/popupmenu/PKU:Configurations2/progressbar/PKU:Configurations2/menubar/PKU:Configurations2/toolbar/PKU:Configurations2/images/Bitmaps/PKU:ObjectReplacements/Object 1 xTO(E@܄{CH!vA@0)<Z_ V~R?*JKZ_TVkJj?sMvdM;s339sw7ĐC)d;-DWPI|v,qD {,εnȐbo#9, M^A>^*WL[݈x:a䠵U]6~D-_+.^0?47Wrܡ.Mg V7h/#cL[P_i/aV6 iVm:կc/.}ts=)A}z1~4> .t9m}:УkgAA>]C>aSЍsnE{^o;[ߔ{1šB@ 2P1e!эDAoN?_Bo.ź=nA!"o 1hއ2AC7N`5=Љn  _u:m }u{&t;l":غs>H]Ld >:1| |:y &Sz?t!\%chKì9jܓyhګ~xëO!֓ =+K ~`P' .M(*L N N .L   ?2d ~(PB %|Q(PGB o  ^# X(R(xP\(8_()L N & ㅂGLw   %*L("`PpP){?SB[ BAP0](8B(AS2B˄g +e >T(\,L ~V|))X-L -f >S\ _)b P  XDs>*SpPLlBBL+LL}y)nSГ4)l`R6S*)xlLLYLG2N h^W?W_pN$ zL 4$`PpPpPpP0N(x,<d Z) >d >d 2ww;LAEE˃L SpE)Xd YA Spd)HAZ) <|<?1` ~5 0 LA[)8>` ~g g >g g n3kL"?S0g *j( (c WfS2)8)N)Sp)Sps)SR`J)SH SJe%L+LCO1[Ӌ,` nXLYSf b ! Y;SS0.)xSSpJ*S𶳙#g0&3_:)8mSa SpdIL3'2$1|T{cɣN h&W?/^'"_>A,@c|b_3FKVT~O-_-X][.r^ǓipQbzR,L dqTtt\`&r6lnQ!m;&I~2 1Gp@"X6o8bpά;l_@LcAN%֬/]]ոҒjY[\k_wZ-yS}߉;[M]HYhHV&hf!hې"]+ɥH6سH> NP.ZB}Bq摩DeuuMaeMKMc]MeUU E4TzmYɒ J0YG]^V@4::śz#ܺ^ott 3*,F{Ŀ7mZ=yEѢ›Q=ߑzWW--ߘo@Q^ }W݃OnA-8VWEQyo)~WlfydoKoWkP*^/yU РHͰW=\`rq@N Ywd7Ůj5Z" АľtѤG-iѾW,;S|LNՓ i}' =/h~71!a[T9x~'aoeM1oVи?.}{zC\-o?!em6o.:ՒM\vKzh8os1If0/gTG*7uo/o%Xˢ.׿}yv;;r4\.;*7uY [ގOst/e^'һ<8#Fd*U/9;i:2:2izz*^C5{?ïuF~`#Yk$em:%-6(BtDQqDYbp(K|ߎ(CV .#FWS(dmӆ[ TxiLr}z &'A$ep!\$A,"!!ZźEBOX,xv8%N1Ɗ̥C} ℣:+p&X)HASc҆*aTј*3@! .6rIrדTj!w̆b'b! ,.J%H8x6X =]o#hb:㑫c:K2FD2H "9q,QJr-g `1 ScFo9)~:y)ЁriTGD!ƈ(ihm"DT<&eL)9n筧m$]V*-^ãתuGToY+n "=q$XXB Z,NO!K4L1!3׍#6:R9zr9!Kԓ vJj#ߚ%wtinq筧m$]$i3湧<.a $qn88QH' RJ+M'ԑQ[jsql~͊ۇ1 E*":̕ tDq8UQA#=eo#)Ќv \QZ[1yxCRP0Pa 7"bi6hI $B@G}Z@G#]Hh9c1Υ!2=iIXX(S9{(`FsXCмIC:cG%0Fg+OУ#uFΨѱu,8nɌiiX73ZЃE`{5~84ʽD: >Bp 4݂kq(S› unS%'4&@VkM(|;3TK<],D?}qT f,3T;}~,mLt9%݌S (|9 #:+^aV `V M=/.cGa1=##>PS\*gfWӏe 0څHa5`DSY"%> b6Ga}5u<'j'AeAX0ÜTg0%X"䠞kl3rvmAP/tmNr`OM'cq'+0N{;bI"ȍ>rc"\5qJH'x[5Qjl MxSg@ ;AsKN8(tD(\34c cX|2q%R0UL&:dzK_k]$Jg&]L9T)=nD%p 01/20sQLDQphW\Z<AR2M84ޅEsQ0@QOO ҚǦbD{S>H~` >eCP"FA0cU0cRXsjZ90ՉD) &Dt6L4_l+& $â*FMN%4=aI3AކDHHbhʘ 42t_<ƅT/JDGTphP-oˢ]zEX TQU_ĆFȡ伽`!{jlV0 T0c>!]:6_ F N:NH E1B^& `M|Axz:ROض'=h`'DQ`PLE} 3=2DG/e2Vs12ow$.(E{qgJiGwhTNt4#V0M{4G6i ξ:eiI%L|e6ciﱁ]6хc*iD8ɖFN47A8;) pع*XOlxZpD#o Tި~r÷J&i%{E~Ʌvcl62Qut.1fe|ASj$M*6J٫"k~뾸pA&|,I7$4`GS 0mqT\-:Y>&.3'8#&l,s[w&ݦJYB2bup#Y1YX@:Wd6' Ŭx#VrIxp9(8OXєG 0/](eŸ:b0aVIwĹA#//-6T|l29ߛ꠷TTfX=,brz=[^Ѳ[53cr^7Q" R`_>(Ih#-qNwC0eN:a鳹RddwK\*#lT؞R2 Iy;E V[W틃 UcQ{~{kP,ժ.AnR(Iv B0/MOzW۝}q$Y;qAU(Ifk@/e[gd7hWJmsuԗɒkj#}_F>vW(_d\  NjvE^(E e*+>r*>|@*%gbGT2S꠷جyuXJN8s? 6LJ2)]$BVF2#PZ#ɄR["Q(N՚N%'n'~􉗚X:(1F4`J^VgK:L]ԘJrB)Q3a [](/[ X5X1K8h?=|I8h!1˥@.m*fr mh(IxԈtA:^/SZG7BIId۔LRFX=%B}b ĬL&b(I%[Ykf|>RQGL`GCIt $ĄOȃKy脯咮3x?A_ G+*CᦢԺ/Lܛ\s6W8)&=(l"IQR+13Hw0 , 5qq܀WReULHe>7 PEGHͯs闰HXI^2= b_?p>7۔wuY@lQm dSF7;.8<`T ˻m%(&l|kWP;j~;ܸ$hf|^=ٱa8dQÐ{O&״7!vor0{?~j.iuU]rzskZN6b ?PKþPKU: styles.xmlYێ6}W $@iIލvޗ CR坖( % $eWK:$EYdrik<39$}μ=bGHƿ|wӔdITGFƅ\ε_bűrUȕW$3Z+3m1ΦpZ{5Xc;x;}dn['k,p6OT{PQ+z=ŻS\pf\dA\.7̠8 d͢as4RQ["&SeDu9Qۦ}6Y]:3TRJڶ9V ^CIW":v-'OӢ&Tm` wׁ}n<ǘ <" pQDZWoG-07nK9u) JHih z#>g\kc\r g}ZQF "(LYDIU cA㡽 ߪZ)!"C1x1bOH+V.s|QLԄJr‘\4`P'J,p&p[G(RBil,O]PAW Y3MiyU(z?w`#btChƒ^ZRM^(8 ;" C 5?.gX3e#Aab:n(?ȫrZ.UEfJPQx&ts !Bv<<כ ^T{ObFHf4+UpJ"F@y5 (?9]1\r>-5GQ1R'[ ήw1މ1|̩#ӏ{a)%;ܩ@촥낻/+E.۱ȠTwtE nR Mܯ!ձ|05c77{\$o8 on3(4(qbUHtpNEGq~Z(2SJo)W{Xȱl\Sydۜ~sk~)o#Ă7wwwycRqpzFw^S wYj:j[m"7^s|4GC̾%%{ĺ?GX!iH&FB\Rk@p% h~oWA/usC|uX=[4A沲Y.@I/^ěPKXQePKU:Object 1/content.xmln6}BPQ`FENv[mDc_(ӎȝh-6o<MoTOoT%j7ry)?>x^%hOBsRvSQT47S*s)/+Tz XOfYsToż%DKQ=UHXّB,!?/O9 :W9ÁmhWF*GV{aװ?/@`&ƑV3d %Zy`޴:2H}ʩ߀#H!R$-):P }-DDeqV"viPV z=؃n{~ vR; Fo\Ժ=óIq*~c] Mv[d둑Ojwfjv)X-ݑ'BLQf oyp3[kLX( |˷=ZEKL=H1x5N7͡\27+WWhWCY:(Nl`ޔ$?; Nqrd,#=^,uV+q۬DޯDYV2y XkU5y jm:ϩ;'8ҡ!.l[kdW*eWJz[VqN[G̟#eLQLc)3Ch_(tH˟f{/F:ĒApjV~JxA/ߨMX;,T ۶; 87Dzdk;Mİ30.*)LPJ.=ǐ\,Xo!8pOMa[cgooMoynshȩ^dXSA]A{rK\6GTs pd:V:I]᱕s(BQZ r*.߃吣2&)1)?@R^cvvw63M,d1.,;g0"Ah^˚bP W/Q .4q4x\ j<z/.m\Y? >\aO4?~GÏaxvOft?(c|*6$l'k-( \l۲jNq (1ws:* 3^݊^V3H HTV7c]Nf7A햋Xnw?} +Q"C521yc{xcb4%QUNi421U mlIGUy*]Ld< B7 yӥ00 G;DRdG3C_F_QԐHd<]"#8 Fah5d"}id,H d>3i{$2.ia jLC&җt!Sul.c:ao c7 HeHc~*A8tpfn==MW̝g44v4t0{{3pˌ穛4hI$ZkjFMf K̎Ё쩎&)^Cwgꍅ~:PK/PKU:Object 1/styles.xmlr @ *ͭ_~A4Lu}4Qx]vYN穓ȵJgpŠ-[ F0Nj`CǕM$ Y7K4hEa72=W74Xámdcݹá]kb兝k ʓi)VbB}jmO0venq^[lAKO sɗ`Yo ǣ讎?cW}u|.}EzCr/z}џ~{;-,JU{dr<)g4 Gmב  oPK;LPKU:Object 1/meta.xml1o0 deM ԡS,\!s!n٦D~߽|NCS_haʄb i. Ux{}ߥ|:d5H>leLt8EENyEZFԚVS|3Ԇ>3qU}G6b[L&uAK}Uj2cu%f\zg)eWiN³͏-m^ uCp+?P{xǍ| OpenOffice.org/2.4$Linux OpenOffice.org_project/680m17$Build-9310Joris Bremond2009-06-16T15:27:22Joris Bremond2009-06-23T12:46:55Joris Bremond2009-06-19T10:21:1843PT4H59M12SPKU:Thumbnails/thumbnail.pngXg8[&$HB Ah#Dw!:]$ ђ=^(Q{w|9=u{}?5{^Ͻֺ Nz24>%8has:@8" lm)nO{_CT@xU۬9#?TPtH?&fq_Dz-j9[^;i#evvkvvvSxr'FYR񻯪T!]Ȣ %P_b%TDkY蝓ҧ#Э[]>7ST44&666;}\zintt~N6,;ܱ)pEf۬L֍7c-抲8~m22Ƅ1"_+Rn|xulau۠rܗe'>Nd):G%bN:s5$ZVZ~3}Y[p98|r|8S-Cx8=JQ3nZf]H;mm̶LZ%Ȟ4K 4>m80>[5]N3$ahS0Jɑ;%'XƝQRǛqSC҆C GF3Orv'}G('~&[X}[f4.ug8[4J*u"Pzy= =p:EG3RdpWY$8 S:Š)ܵ C齐b:}w/9nVY?˔ {rv!hcE-N8?K>vۍ`qp>٣R-3h$Ĭpc"f~Nv>9_af $9|H|0ŋ*ȳڍK0ңՉøhyO7xk@I|ƌ*^wI96{' 2 !bּ_ 9S#qf.{;ܽrK:k 8ƃӹ}{%ĴA"yp9vf.;t:SLCzͅ(2~Ō]p+(#vO\iak2PͻbmArgC NϋtwV?;VS#:\آzŠй_-E˗Z_@ՙM'e]oLz>Rg+ 3} tzfcw  s1?+}H"nƫ;oWEu[{J%vx!J3ӗZZLx+0E^N3 ۍ_M6lYAuts0컴hde(=l(ɷy) %tVSqc]яL6*8Mv;#j?CȞ~ĩSlF>%*,ߞ SpU=nL.+mؾWLKRr Z3O]})/0"F'З(5I73kQZrÆ{=P7OX;(O 08__7Зkjd{g!;f!s%5Z,/ D} wBjF6̚N>C*u}H̖B9y&n&bCZjir~w_3?fUk6!+5،$iG1P^;V~h$St(h-̻g2Hڐ]dĻ{#[>b~L'4@^ÏizyR],`]¶Vy+ylطCb-0j򰈆0Caii"=(4rSSVqQ' QlfVGoV9:9y$ZJnm];GmaukIZ (9u?ū@>5{'I `J C3BP[Q ȃtqR y i}7թTn1*?'Z PJP>s@6{`FĖcdUG`L56c'm C,)A- _F^ Uj(%W'6#3$l0Vuhc1TۥJV Id+J 1 }\1{2,p\T{<$cKK̅䛚 KJ`K_ӥ7LeXc\xiY2 jJtň"C~tFef]L SF >Bq sӪw:ܷUr6䮥hCXˊ&g>27;>8Xn?2Mun(QeҒEH"gD+0k]r9=A8gI5 0չau-v3C39)A;Š N#CX#Yb$m<}IĤ`te"ֈ;I԰"P2(ZUG2.Vi1*3s#~"|M o1"ڟa| b/NT;Cfm2QeWB@C;YԬ%q-O鞷+DiY.(z͈F:2dqH hdsUCRGNWi2(F0F?A0v>?xl|7iOJTjdaͪT U$֝M/h~j<fuk4VJ08|$PWSn }d31Qd;}ޞ698{i:aKxs,292$'RiO-(s$ 1crY˟{.pUT0DD%j[Fd;{>YHl/dZWz1bzi`mʙC@IXQX1/Jj-}aW 5 IW#_,/j.(bG ©V}]vB @@b#;ϯCpK xMz'(VդM->ʡ3L0)rřGwY1N r(>3 ^f뺂0h_+Zst " B7R;Mg=idXBc߼9? >HMzɯ,ޙ.Pz0-~8$U_MDL VƩܓ9/"3H"k$LO 3k[ZZ2fA{% =1,^F;NO',Qg7'??b_HQvw UD z\&M_@{!4,W̘?}ET6Q%p2sWh>-uʹA|{"kB9+&=.evsdJOPh-1-4xA6pKW<4[5Wf$:J /zNƍMW"Չti/p-mUȷ^ݨ+Ckm-^W'S6 i[k7#S'f5H"V2m0us]q2Ƚ$Zn|}eyڭj $.?Z|^Bݰ@}0ׁlÂϧ%$5UnIzK#&OB_D-FJ@ 1)MsYY) v0|Ovm UZyZ_I瞲Z{`V GނVQ4@s8yO'ZlD$67G3˯ҺH[% q̷b|^Uq[< C{R2@Csi!is/|$ǘ2Vq0J.<`F>9^-aI#j1[N:9_ؤOvwӖkOc *EhIVR}IEܒk*qAm51ߤ\ug=wj"Y:ɋP`$ u-ϘJKԄHLzu epcy39z +6.2/dr*ʡqpgIwU wn7~zʥ 3 hȯ(jItu( Mk5_DQc?dgq[=`j4k mz6hie)fkt $MHMʲQD3j蚖r<)H뺶9q==rd戰NJ.$yԏ<7l3&%mdQ'9pB(9 .ӂ@ e衹O„m%i_\<:*%J/PK~PKU: settings.xmlS:_Ң;"/-m#͚&$_S>bmsgGDwNON9I=|2#Jv*'AdZU5}~F68VߴuL+9m=u*P $AGq&~(K 4l1 `29nHk^KkS6|lF1Kżȗd,h^ F5b=S<. dt?Ggw8:2Q )1Voz`^ױtN^u/k8O+#T mu3hhWF#q931V 3[Kunxk{oy h4l^û/8ٞI[U[ZQq2˻펇Mjhzltf11ljKhVY}xϜh78G?4TZ՜x1W M,8.`QiR!1< "IǏC3eu8ED&ߠAw[hlQ?- ѹu7 @™A wXMFdcYMG @lPQSܥǗP; o i7PKJ]PKU:META-INF/manifest.xmln0 } Çl= nТaНF TI̺YDj3=)k~_huZ-F0GfQuö 4HQc`DjX6֡ #n~o8{~[İSPa[sZIx0]Uyֈ\ }кr8Jsɚ^ ?B8 |%jٱ . u 7\r /W^}֚N,evʀ$xY;3 ۑ9<ٖPKï PKU:l9..mimetypePKU:TConfigurations2/statusbar/PKU:'Configurations2/accelerator/current.xmlPKU:Configurations2/floater/PKU:Configurations2/popupmenu/PKU:QConfigurations2/progressbar/PKU:Configurations2/menubar/PKU:Configurations2/toolbar/PKU:Configurations2/images/Bitmaps/PKU:+]rx~A`4ObjectReplacements/Object 1PKU:þ content.xmlPKU:XQe 'styles.xmlPKU:/{.Object 1/content.xmlPKU:;L6Object 1/styles.xmlPKU:^y]X8Object 1/meta.xmlPKU:Xll9meta.xmlPKU:~$>Thumbnails/thumbnail.pngPKU:J] PWsettings.xmlPKU:ï S\META-INF/manifest.xmlPKF^./oar-2.5.2/misc/fault_tolerance/HAinstal-Debian.sh0000755000175000017500000013457011757171206020117 0ustar plbplb#!/bin/bash #@Author: Joris Bremond #Script for Debian #Requirements : this script must be started with admin (root) right #Require kernel headers (linux-headers) #-----------------------Variables--------------------------------------------------- ####--Package---##### DRBD="drbd8-utils" HEARTBEAT2="heartbeat-2" #HEARTBEAT2GUI="heartbeat-2-gui" MODULEASSITANT="module-assistant" ####--BDD---##### # "mysql" or "postgresql" BD="mysql" ##only for postgres PGVERSION="8.3" ###----Nodes name-----------#### MASTER_OAR="genepi-6.grenoble.grid5000.fr" MASTER_DB="genepi-21.grenoble.grid5000.fr" BACKUP_OAR="genepi-29.grenoble.grid5000.fr" BACKUP_DB="genepi-31.grenoble.grid5000.fr" #-----Nodes properties-----#### #Interface ETH="eth1" #OAR-server Virtual IP (ex : ) IP="172.16.16.220" #Database Virtual IP (ex : 172.16.16.221) IP_DB="172.16.16.221" #enter CIDR netmask (ex : 24) MASK="24" ####------------------Heartbeat----------------##### ##Password sha PASSWORD="oarteam" ##UDP Port HBPORT="694" ####-------------------DRBD--------------------##### ##---Device-------# #--low-level storage for the drbd data (partition)---# #It could be a loopback device, like /dev/loop0 DISKPARTITION=/dev/loop0 #Do you want create a partition in a file ? ("y" or "n") --> The DISKPARTTION Must be a loopback interface #Non tested with real partition ("n") USEFILEPARTITION="y" # -----If we have choose yes before -----: #-PATH File image-# IMAGE=/image.img #Size in MegaBytes SIZE="200" #---------------------------# #----DRBD partition alias---# DRBDPARTITION=/dev/drbd0 #----DRBD mount point---# DRBDDATA=/mnt/drbddata #---FileSystem type (ext2,ext3)---# FSTYPE="ext3" #----LoopBack----# ##DRBD communication Port DBRDPORT="7788" ####-------------------CGI script--------------------##### #enable cgi script for monitor heartbeat ? require Apache #"y" or "n" CGI="y" #---------------------------Initialisation--------------------------------------------------------------------# HOSTNAME=$(uname -n) #High Availability configuration : "2nodes" or "4nodes" if [ "$MASTER_OAR" = "$MASTER_DB" ]; then CONF="2nodes" else CONF="4nodes" fi case $HOSTNAME in $MASTER_OAR) ISMASTER="y" if [ "$CONF" = "2nodes" ]; then ISDATABASE="y" else ISDATABASE="n" fi ;; $MASTER_DB) ISMASTER="y" ISDATABASE="y" ;; $BACKUP_OAR) ISMASTER="n" if [ "$CONF" = "2nodes" ]; then ISDATABASE="y" else ISDATABASE="n" fi ;; $BACKUP_DB) ISMASTER="n" ISDATABASE="y" ;; *) echo "Error : nodes not match with current machine hostname (uname -n)" echo "Verify nodes name in the script configuration" exit 1 ;; esac echo "You are now ready to configure High Availabity with $CONF configuration" echo "Press enter to continue ..." read #Restore mysql configuration if [ -e /etc/mysql/my.cnf.backup ]; then cp /etc/mysql/my.cnf.backup /etc/mysql/my.cnf fi ########-----------------------------------useful Function ----------------------############ #Exist commande test exists() { if which $1 &> /dev/null; then return 0 else echo "$1 command not found." echo "Please install $1 before start the script" exit 1 fi } ######----------------------------------require test----------------------------------------######### exists apt-get exists losetup exists mkfs exists shred exists dd exists mknod exists tune2fs exists update-rc.d #-----------------------Installation on debian--------------------------------------------------- apt-get update if [ "$ISDATABASE" = "y" ]; then apt-get -y install $DRBD $MODULEASSITANT #installation test exists module-assistant module-assistant auto-install drbd8 fi apt-get -y install $HEARTBEAT2 host exists host #-----------------------Stop services--------------------------------------------------- if [ "$ISDATABASE" = "y" ]; then if [ "$BD" = "mysql" ]; then /etc/init.d/$BD stop elif [ "$BD" = "postgresql" ]; then /etc/init.d/$BD"-"$PGVERSION stop else exit 1 fi fi if [ "$ISDATABASE" = "n" ]||[ "$CONF" = "2nodes" ]; then /etc/init.d/oar-server stop fi #-----------------------Delete old heartbeat configurations--------------------------------------------------- if [ -f /var/lib/heartbeat/crm/cib.xml ]; then rm /var/lib/heartbeat/crm/cib.xml fi if [ -f /var/lib/heartbeat/crm/cib.xml.sig ]; then rm /var/lib/heartbeat/crm/cib.xml.sig fi ##############-----------------------Heartbeat Configurations---------------------------------------------------################ #-----------------------/etc/ha.d/authkeys--------------------------------------------------- echo 'auth 1' > /etc/ha.d/authkeys echo "1 sha1 $PASSWORD" >> /etc/ha.d/authkeys chmod 0600 /etc/ha.d/authkeys #-----------------------/etc/ha.d/ha.cf--------------------------------------------------- echo '#logfacility local7 ' > /etc/ha.d/ha.cf echo 'logfile /var/log/ha-log' >> /etc/ha.d/ha.cf echo 'debugfile /var/log/ha-debug' >> /etc/ha.d/ha.cf echo '#use_logd on' >> /etc/ha.d/ha.cf echo "udpport $HBPORT" >> /etc/ha.d/ha.cf echo 'keepalive 1 # 1 second' >> /etc/ha.d/ha.cf echo 'deadtime 10' >> /etc/ha.d/ha.cf echo 'initdead 80' >> /etc/ha.d/ha.cf if [ "$ISMASTER" = "y" ]; then if [ "$ISDATABASE" = "y" ]; then ipmaster_oar=$(host $MASTER_OAR | cut -f3) ipmaster_db=$(host `uname -n` | cut -f3) #don't use ifconfig beacause can beug if it is a non english system ipbackup_oar=$(host $BACKUP_OAR | cut -f3) ipbackup_db=$(host $BACKUP_DB | cut -f3) elif [ "$ISDATABASE" = "n" ]; then ipmaster_oar=$(host `uname -n` | cut -f3) ipmaster_db=$(host $MASTER_DB | cut -f3) ipbackup_oar=$(host $BACKUP_OAR | cut -f3) ipbackup_db=$(host $BACKUP_DB | cut -f3) fi elif [ "$ISMASTER" = "n" ]; then if [ "$ISDATABASE" = "y" ]; then ipmaster_oar=$(host $MASTER_OAR | cut -f3) ipmaster_db=$(host $MASTER_DB | cut -f3) ipbackup_oar=$(host $BACKUP_OAR | cut -f3) ipbackup_db=$(host `uname -n` | cut -f3) elif [ "$ISDATABASE" = "n" ]; then ipmaster_oar=$(host $MASTER_OAR | cut -f3) ipmaster_db=$(host $MASTER_DB | cut -f3) ipbackup_oar=$(host `uname -n` | cut -f3) ipbackup_db=$(host $BACKUP_DB | cut -f3) fi else exit 1 fi if [ "$CONF" = "2nodes" ]; then echo "ucast $ETH $ipmaster_oar" >> /etc/ha.d/ha.cf echo "ucast $ETH $ipbackup_oar" >> /etc/ha.d/ha.cf #experimental echo "ucast eth0_rename 192.168.0.1" >> /etc/ha.d/ha.cf echo "ucast eth0_rename 192.168.0.2" >> /etc/ha.d/ha.cf echo "node $MASTER_OAR" >> /etc/ha.d/ha.cf echo "node $BACKUP_OAR" >> /etc/ha.d/ha.cf else echo "ucast $ETH $ipmaster_oar" >> /etc/ha.d/ha.cf echo "ucast $ETH $ipmaster_db" >> /etc/ha.d/ha.cf echo "ucast $ETH $ipbackup_oar" >> /etc/ha.d/ha.cf echo "ucast $ETH $ipbackup_db" >> /etc/ha.d/ha.cf echo "node $MASTER_OAR" >> /etc/ha.d/ha.cf echo "node $MASTER_DB" >> /etc/ha.d/ha.cf echo "node $BACKUP_OAR" >> /etc/ha.d/ha.cf echo "node $BACKUP_DB" >> /etc/ha.d/ha.cf fi echo 'crm yes' >> /etc/ha.d/ha.cf #Don't work with release two of heartbeat, to be deleted #echo 'auto_failback on' >> /etc/ha.d/ha.cf #-----------------------Rename Database service for HA ---------------------------- if [ "$BD" = "mysql" ]; then cp /etc/init.d/$BD /etc/init.d/"$BD"ha elif [ "$BD" = "postgresql" ]; then cp /etc/init.d/$BD"-"$PGVERSION /etc/init.d/$BD"-""$PGVERSION"ha else exit 1 fi #-----------------------Create file cib.xml, configure ressources for Heartbeat--------------------------------------------------- if [ "$CONF" = "4nodes" ]; then echo ' ' > /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo " " >> /var/lib/heartbeat/crm/cib.xml echo " " >> /var/lib/heartbeat/crm/cib.xml echo " " >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo " " >> /var/lib/heartbeat/crm/cib.xml echo " " >> /var/lib/heartbeat/crm/cib.xml echo " " >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml if [ "$BD" = "mysql" ]; then echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml elif [ "$BD" = "postgresql" ]; then echo " > /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml else exit 1 fi echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo " " >> /var/lib/heartbeat/crm/cib.xml echo " " >> /var/lib/heartbeat/crm/cib.xml echo " " >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml #echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo " " >> /var/lib/heartbeat/crm/cib.xml echo " " >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo " " >> /var/lib/heartbeat/crm/cib.xml echo " " >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo " " >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo " " >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml else #2 nodes hearbeat configuration echo ' ' > /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo " " >> /var/lib/heartbeat/crm/cib.xml echo " " >> /var/lib/heartbeat/crm/cib.xml echo " " >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo " " >> /var/lib/heartbeat/crm/cib.xml echo " " >> /var/lib/heartbeat/crm/cib.xml echo " " >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml if [ "$BD" = "mysql" ]; then echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml elif [ "$BD" = "postgresql" ]; then echo " > /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml else exit 1 fi echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo " " >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml fi chown hacluster:haclient /var/lib/heartbeat/crm/cib.xml chmod 0600 /var/lib/heartbeat/crm/cib.xml #-----------------------Modify OAR service, LSB compatible--------------------------------------------------- if [ "$ISDATABASE" = "n" ]||[ "$CONF" = "2nodes" ]; then echo '#! /bin/bash' > /etc/init.d/oar-server echo '### BEGIN INIT INFO' >> /etc/init.d/oar-server echo '# Provides: oar-server' >> /etc/init.d/oar-server echo '# Required-Start: $network $local_fs $remote_fs' >> /etc/init.d/oar-server echo '# Required-Stop:' >> /etc/init.d/oar-server echo '# Default-Start: 2 3 4 5' >> /etc/init.d/oar-server echo '# Default-Stop: 0 1 6' >> /etc/init.d/oar-server echo '# Short-Description: OAR server init script' >> /etc/init.d/oar-server echo '### END INIT INFO' >> /etc/init.d/oar-server echo '# $Id: oar-server.init.d 1048 2007-12-18 23:17:08Z capitn $' >> /etc/init.d/oar-server echo 'PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin' >> /etc/init.d/oar-server echo 'DAEMON=/usr/sbin/Almighty' >> /etc/init.d/oar-server echo 'NAME=oar-server' >> /etc/init.d/oar-server echo 'DESC=oar-server' >> /etc/init.d/oar-server echo 'PIDFILE=/var/run/$NAME.pid' >> /etc/init.d/oar-server echo 'test -x $DAEMON || exit 0' >> /etc/init.d/oar-server echo '# Include oar defaults if available' >> /etc/init.d/oar-server echo 'if [ -f /etc/default/oar-server ] ; then' >> /etc/init.d/oar-server echo ' . /etc/default/oar-server' >> /etc/init.d/oar-server echo 'fi' >> /etc/init.d/oar-server echo 'set -e' >> /etc/init.d/oar-server echo 'case "$1" in' >> /etc/init.d/oar-server echo ' start)' >> /etc/init.d/oar-server echo ' echo -n "Starting $DESC: "' >> /etc/init.d/oar-server echo ' start-stop-daemon --start --quiet --pidfile $PIDFILE \' >> /etc/init.d/oar-server echo ' --make-pidfile --background --exec $DAEMON -- $DAEMON_OPTS' >> /etc/init.d/oar-server echo ' echo "$NAME."' >> /etc/init.d/oar-server echo ' ;;' >> /etc/init.d/oar-server echo ' stop)' >> /etc/init.d/oar-server echo ' echo -n "Stopping $DESC: "' >> /etc/init.d/oar-server echo ' start-stop-daemon --stop --quiet --pidfile $PIDFILE && \' >> /etc/init.d/oar-server echo ' rm -f /var/run/$NAME.pid' >> /etc/init.d/oar-server echo ' echo "$NAME."' >> /etc/init.d/oar-server echo ' ;;' >> /etc/init.d/oar-server echo ' status)' >> /etc/init.d/oar-server echo ' if [ -f $PIDFILE ]' >> /etc/init.d/oar-server echo ' then' >> /etc/init.d/oar-server echo ' PID=`head -n 1 $PIDFILE`' >> /etc/init.d/oar-server echo ' if [ `ps -ef | grep -v grep | grep $PID | wc -l` -ge 1 ]' >> /etc/init.d/oar-server echo ' then' >> /etc/init.d/oar-server echo ' echo "OAR is running"' >> /etc/init.d/oar-server echo ' exit 0' >> /etc/init.d/oar-server echo ' else' >> /etc/init.d/oar-server echo ' echo "OAR is not runnig"' >> /etc/init.d/oar-server echo ' exit 2' >> /etc/init.d/oar-server echo ' fi' >> /etc/init.d/oar-server echo ' else' >> /etc/init.d/oar-server echo ' echo "OAR is not runnig"' >> /etc/init.d/oar-server echo ' exit 2' >> /etc/init.d/oar-server echo ' fi' >> /etc/init.d/oar-server echo ' ;;' >> /etc/init.d/oar-server echo ' reload|force-reload|restart)' >> /etc/init.d/oar-server echo ' $0 stop' >> /etc/init.d/oar-server echo ' sleep 1' >> /etc/init.d/oar-server echo ' $0 start' >> /etc/init.d/oar-server echo ' ;;' >> /etc/init.d/oar-server echo ' *)' >> /etc/init.d/oar-server echo ' N=/etc/init.d/$NAME' >> /etc/init.d/oar-server echo ' echo "Usage: $N {start|restart|stop|status}" >&2' >> /etc/init.d/oar-server echo ' exit 1' >> /etc/init.d/oar-server echo ' ;;' >> /etc/init.d/oar-server echo 'esac' >> /etc/init.d/oar-server echo 'exit 0' >> /etc/init.d/oar-server chmod 755 /etc/init.d/oar-server fi #-----------------------OAR configuration with remote database (Virtual IP) --------------------------------------------------- #Detach job from server detach_job=$(cat /etc/oar/oar.conf | grep DETACH_JOB_FROM_SERVER=) sed -e "s/$detach_job/DETACH_JOB_FROM_SERVER=\"1\"/g" /etc/oar/oar.conf > /etc/oar/oar.conf.tmp && mv -f /etc/oar/oar.conf.tmp /etc/oar/oar.conf serveur_hostname=$(cat /etc/oar/oar.conf | grep SERVER_HOSTNAME=) sed -e "s/$serveur_hostname/SERVER_HOSTNAME=\"$IP\"/g" /etc/oar/oar.conf > /etc/oar/oar.conf.tmp && mv -f /etc/oar/oar.conf.tmp /etc/oar/oar.conf if [ "$CONF" = "4nodes" ]; then db_hostname=$(cat /etc/oar/oar.conf | grep DB_HOSTNAME=) sed -e "s/$db_hostname/DB_HOSTNAME=\"$IP_DB\"/g" /etc/oar/oar.conf > /etc/oar/oar.conf.tmp && mv -f /etc/oar/oar.conf.tmp /etc/oar/oar.conf elif [ "$CONF" = "2nodes" ]; then #2nodes configuration, very important, because oarexec must contact oar-server after fail-over db_hostname=$(cat /etc/oar/oar.conf | grep DB_HOSTNAME=) sed -e "s/$db_hostname/DB_HOSTNAME=\"$IP\"/g" /etc/oar/oar.conf > /etc/oar/oar.conf.tmp && mv -f /etc/oar/oar.conf.tmp /etc/oar/oar.conf fi #-----------------------Get database data path for DRBD configuration, and change this path--------------------------------------------------- if [ "$ISDATABASE" = "y" ]; then if [ "$BD" = "mysql" ]; then mysqldirold=$(cat /etc/$BD/my.cnf | grep datadir | cut -d "=" -f2) mysqldiroldn=$(echo $mysqldirold | sed 's/\//\\\//g') #Save mysql old configuration cp /etc/mysql/my.cnf /etc/mysql/my.cnf.backup sed -e "s/$mysqldiroldn/\/mnt\/drbddata\/mysql/g" /etc/mysql/my.cnf > /etc/mysql/my.cnf.tmp && mv -f /etc/mysql/my.cnf.tmp /etc/mysql/my.cnf sed -e "s/bind-address/# bind-address/g" /etc/mysql/my.cnf > /etc/mysql/my.cnf.tmp && mv -f /etc/mysql/my.cnf.tmp /etc/mysql/my.cnf elif [ "$BD" = "postgresql" ]; then postgresdirold=$(cat /etc/$BD/$PGVERSION/main/postgresql.conf | grep data_directory | cut -d "'" -f2) postgresdiroldn=$(echo $postgresdirold | sed 's/\//\\\//g') #Save mysql old configuration cp /etc/$BD/$PGVERSION/main/postgresql.conf /etc/$BD/$PGVERSION/main/postgresql.conf.backup sed -e "s/$postgresdiroldn/\/mnt\/drbddata\/main/g" /etc/$BD/$PGVERSION/main/postgresql.conf > /etc/$BD/$PGVERSION/main/postgresql.conf.tmp && mv -f /etc/$BD/$PGVERSION/main/postgresql.conf.tmp /etc/$BD/$PGVERSION/main/postgresql.conf #to be deleted in the future sed -e "s/#listen_addresses = 'localhost'/listen_addresses = '*'/g" /etc/$BD/$PGVERSION/main/postgresql.conf > /etc/$BD/$PGVERSION/main/postgresql.conf.tmp && mv -f /etc/$BD/$PGVERSION/main/postgresql.conf.tmp /etc/$BD/$PGVERSION/main/postgresql.conf else exit 1 fi fi #-----------------------DRBD Configuration--------------------------------------------------- if [ "$ISDATABASE" = "y" ]; then echo 'global { usage-count no; }' > /etc/drbd.conf echo 'resource mysql {' >> /etc/drbd.conf echo ' # Three protocol :' >> /etc/drbd.conf echo ' # A : write ACK (on master)' >> /etc/drbd.conf echo ' # is send when data was transmitted on master disk and sent to slave' >> /etc/drbd.conf echo ' # B : write ACK (on master)' >> /etc/drbd.conf echo ' # is send when data was transmitted on master disk and received by slave' >> /etc/drbd.conf echo ' # C : write ACK (on master)' >> /etc/drbd.conf echo ' # is send when data was transmitted on master disk and slave disk' >> /etc/drbd.conf echo ' protocol C;' >> /etc/drbd.conf echo ' startup {' >> /etc/drbd.conf echo ' # when the node start, wait 2 minutes others nodes' >> /etc/drbd.conf echo ' wfc-timeout 120;' >> /etc/drbd.conf echo ' }' >> /etc/drbd.conf echo ' # If io-error, freeze node' >> /etc/drbd.conf echo ' disk {' >> /etc/drbd.conf echo ' on-io-error detach;' >> /etc/drbd.conf echo ' }' >> /etc/drbd.conf echo ' syncer {' >> /etc/drbd.conf echo ' rate 700000K;' >> /etc/drbd.conf echo ' # Rate of synchronization. max 700000K' >> /etc/drbd.conf echo ' al-extents 257;' >> /etc/drbd.conf echo ' # al-extent is the size of the « hot-area »' >> /etc/drbd.conf echo ' }' >> /etc/drbd.conf echo " on $MASTER_DB {" >> /etc/drbd.conf echo " device $DRBDPARTITION;" >> /etc/drbd.conf echo " disk $DISKPARTITION;" >> /etc/drbd.conf echo " address $ipmaster_db:$DBRDPORT;" >> /etc/drbd.conf echo ' meta-disk internal;' >> /etc/drbd.conf echo ' }' >> /etc/drbd.conf echo " on $BACKUP_DB {" >> /etc/drbd.conf echo " device $DRBDPARTITION;" >> /etc/drbd.conf echo " disk $DISKPARTITION;" >> /etc/drbd.conf echo " address $ipbackup_db:$DBRDPORT;" >> /etc/drbd.conf echo ' meta-disk internal;' >> /etc/drbd.conf echo ' }' >> /etc/drbd.conf echo ' net {' >> /etc/drbd.conf echo " #cram-hmac-alg \"sha1\"; " >> /etc/drbd.conf echo " #shared-secret \"123456\";" >> /etc/drbd.conf echo " after-sb-0pri discard-younger-primary;" >> /etc/drbd.conf echo " after-sb-1pri consensus;" >> /etc/drbd.conf echo " after-sb-2pri call-pri-lost-after-sb;" >> /etc/drbd.conf echo ' #rr-conflict violently;' >> /etc/drbd.conf echo ' }' >> /etc/drbd.conf echo ' handlers {' >> /etc/drbd.conf echo ' split-brain "echo Splitbraindetected >> /var/log/drbd-log";' >> /etc/drbd.conf echo ' pri-lost-after-sb "echo pri-lost-after-sb >> /var/log/drbd-log";' >> /etc/drbd.conf echo ' }' >> /etc/drbd.conf echo '}' >> /etc/drbd.conf fi #-----------------------Create the device for DRBD Data--------------------------------------------------- if [ "$ISDATABASE" = "y" ]; then modprobe drbd if [ "$USEFILEPARTITION" = "y" ]; then #Create Virtual partition dd if=/dev/zero of=$IMAGE bs=1M count=$SIZE losetup $DISKPARTITION $IMAGE mkfs -t $FSTYPE $DISKPARTITION shred -zvf -n 1 $DISKPARTITION fi if [ ! -e $DRBDPARTITION ]; then mknod $DRBDPARTITION b 147 0 fi #Create metadata drbdadm create-md all drbdadm up all if [ ! -e $DRBDDATA ]; then mkdir $DRBDDATA fi #On the master if [ "$ISMASTER" = "y" ]&&[ "$ISDATABASE" = "y" ]; then #The master launch syncrhonization drbdadm -- --overwrite-data-of-peer primary all mkfs -t $FSTYPE $DRBDPARTITION mount $DRBDPARTITION $DRBDDATA if [ "$BD" = "mysql" ]; then cp -r $mysqldirold $DRBDDATA chown -R mysql:mysql $DRBDDATA/mysql elif [ "$BD" = "postgresql" ]; then cp -r $postgresdirold $DRBDDATA chown -R postgres:postgres $DRBDDATA/main else exit 1 fi #After data copy, put the master in secondary state, heartbeat will choose the primary umount $DRBDDATA drbdadm secondary all #Delete auto fsck tune2fs -c 0 $DISKPARTITION fi fi #-----------------------Remove auto launch--------------------------------------------------- if [ "$ISDATABASE" = "y" ]; then if [ "$BD" = "mysql" ]; then update-rc.d -f $BD remove elif [ "$BD" = "postgresql" ]; then update-rc.d -f $BD"-"$PGVERSION remove else exit 1 fi fi if [ "$ISDATABASE" = "n" ]||[ "$CONF" = "2nodes" ] ; then update-rc.d -f oar-server remove fi #-----------------------Create service for auto associate $LOOPBACK with $IMAGE----------------------------- if [ "$USEFILEPARTITION" = "y" ]; then echo '#!/bin/bash' > /etc/init.d/active-loop echo 'case "$1" in' >> /etc/init.d/active-loop echo ' start|"") ' >> /etc/init.d/active-loop echo ' losetup /dev/loop0 /image.img' >> /etc/init.d/active-loop echo ' echo "Start OK"' >> /etc/init.d/active-loop echo ' ;;' >> /etc/init.d/active-loop echo ' stop)' >> /etc/init.d/active-loop echo ' losetup -d /dev/loop0' >> /etc/init.d/active-loop echo ' echo "Stop OK"' >> /etc/init.d/active-loop echo ' ;;' >> /etc/init.d/active-loop echo ' *)' >> /etc/init.d/active-loop echo ' echo "Usage: active-loop [start|stop]" >&2' >> /etc/init.d/active-loop echo ' exit 3' >> /etc/init.d/active-loop echo ' ;;' >> /etc/init.d/active-loop echo 'esac' >> /etc/init.d/active-loop #change rights chmod +x /etc/init.d/active-loop #Auto start, before DRBD --> 60 update-rc.d active-loop defaults 60 fi #-----------------------Launch heartbeat service--------------------------------------------------- #to be deleted, because can fail with DRBD /etc/init.d/heartbeat start #-----------------------Add cgi script for monitor heartbeat--------------------------------------------------- if [ "$CGI" = "y" ]; then echo '#!/bin/bash' > /usr/lib/cgi-bin/ha-status.cgi echo 'sudo /usr/sbin/crm_mon -w' >> /usr/lib/cgi-bin/ha-status.cgi echo 'www-data ALL=NOPASSWD:/usr/sbin/crm_mon' >> /etc/sudoers chmod 755 /usr/lib/cgi-bin/ha-status.cgi fi #-----------------------End of script--------------------------------------------------- ./oar-2.5.2/misc/fault_tolerance/HAinstal-Centos.sh0000755000175000017500000014261011757171206020162 0ustar plbplb#!/bin/bash #@Author: Joris Bremond #Script for CentOs, tested with heartbeat 2.1.3 #Requirements : this script must be started with admin (root) right #-----------------------Variables--------------------------------------------------- #########to be deleted in the future############## service iptables stop ############################################### ####--Package---##### DRBD="drbd82" DRBDKMOD="kmod-drbd82" HEARTBEAT2="heartbeat" ####--BDD---##### # "mysql" or "postgresql" BD="mysql" ##only for postgres PGVERSION="8.3" ###----Nodes name-----------#### MASTER_OAR="grelon-39" MASTER_DB="grelon-40" BACKUP_OAR="grelon-41" BACKUP_DB="grelon-42" #-----Nodes properties-----#### #Interface ETH="eth0" #OAR-server Virtual IP (ex : ) IP="172.28.54.220" #Database Virtual IP (ex : 172.16.16.221) IP_DB="172.28.54.221" #enter CIDR netmask (ex : 24) MASK="24" ####------------------Heartbeat----------------##### ##Password sha PASSWORD="oarteam" ##UDP Port HBPORT="694" ####-------------------DRBD--------------------##### ##---Device-------# #--low-level storage for the drbd data (partition)---# #It could be a loopback device, like /dev/loop0 DISKPARTITION=/dev/loop0 #Do you want create a partition in a file ? ("y" or "n") --> The DISKPARTITION Must be a loopback interface #Non tested with real partition ("n") USEFILEPARTITION="y" # -----If we have choose yes before -----: #-PATH File image-# IMAGE=/image.img #Size in MegaBytes SIZE="200" #---------------------------# #----DRBD partition alias---# DRBDPARTITION=/dev/drbd0 #----DRBD mount point---# DRBDDATA=/mnt/drbddata #---FileSystem type (ext2,ext3)---# FSTYPE="ext3" #----LoopBack----# ##DRBD communication Port DBRDPORT="7788" ####-------------------CGI script--------------------##### #enable cgi script for monitor heartbeat ? require httpd (apache) #"y" or "n" CGI="y" #---------------------------Initialisation--------------------------------------------------------------------# HOSTNAME=$(uname -n) #High Availability configuration : "2nodes" or "4nodes" if [ "$MASTER_OAR" = "$MASTER_DB" ]; then CONF="2nodes" else CONF="4nodes" fi case $HOSTNAME in $MASTER_OAR) ISMASTER="y" if [ "$CONF" = "2nodes" ]; then ISDATABASE="y" else ISDATABASE="n" fi ;; $MASTER_DB) ISMASTER="y" ISDATABASE="y" ;; $BACKUP_OAR) ISMASTER="n" if [ "$CONF" = "2nodes" ]; then ISDATABASE="y" else ISDATABASE="n" fi ;; $BACKUP_DB) ISMASTER="n" ISDATABASE="y" ;; *) echo "Error : nodes not match with current machine hostname (uname -n)" echo "Verify nodes name in the script configuration" exit 1 ;; esac echo "You are now ready to configure High Availabity with $CONF configuration" echo "Press enter to continue ..." read #Restore mysql configuration if [ -e /etc/mysql/my.cnf.backup ]; then cp /etc/mysql/my.cnf.backup /etc/mysql/my.cnf fi ########-----------------------------------useful Function ----------------------############ #Exist commande test exists() { if which $1 &> /dev/null; then return 0 else echo "$1 command not found." echo "Please install $1 before start the script" exit 1 fi } ######----------------------------------require test----------------------------------------######### exists yum exists losetup exists mkfs exists shred exists dd exists mknod exists tune2fs exists host #-----------------------Installation on debian--------------------------------------------------- if [ "$ISDATABASE" = "y" ]; then yum -y install $DRBD $DRBDKMOD exists drbdadm #DRBD test fi yum -y install $HEARTBEAT2 yum -y install $HEARTBEAT2 #2 times beacause there is a bug ... #-----Installation tests-----# exists crmadmin #Heartbeat test #-----------------------Stop services--------------------------------------------------- if [ "$ISDATABASE" = "y" ]; then if [ "$BD" = "mysql" ]; then /etc/init.d/$BD"d" stop elif [ "$BD" = "postgresql" ]; then echo "not emplemented with postgres" exit 1 /etc/init.d/$BD"-"$PGVERSION stop else exit 1 fi fi if [ "$ISDATABASE" = "n" ]||[ "$CONF" = "2nodes" ]; then /etc/init.d/oar-server stop fi #-----------------------Delete old heartbeat configurations--------------------------------------------------- if [ -f /var/lib/heartbeat/crm/cib.xml ]; then rm /var/lib/heartbeat/crm/cib.xml fi if [ -f /var/lib/heartbeat/crm/cib.xml.sig ]; then rm /var/lib/heartbeat/crm/cib.xml.sig fi ##############-----------------------Heartbeat Configurations---------------------------------------------------################ #-----------------------/etc/ha.d/authkeys--------------------------------------------------- echo 'auth 1' > /etc/ha.d/authkeys echo "1 sha1 $PASSWORD" >> /etc/ha.d/authkeys chmod 0600 /etc/ha.d/authkeys #-----------------------/etc/ha.d/ha.cf--------------------------------------------------- echo '#logfacility local7 ' > /etc/ha.d/ha.cf echo 'logfile /var/log/ha-log' >> /etc/ha.d/ha.cf echo 'debugfile /var/log/ha-debug' >> /etc/ha.d/ha.cf echo '#use_logd on' >> /etc/ha.d/ha.cf echo "udpport $HBPORT" >> /etc/ha.d/ha.cf echo 'keepalive 1 # 1 second' >> /etc/ha.d/ha.cf echo 'deadtime 10' >> /etc/ha.d/ha.cf echo 'initdead 80' >> /etc/ha.d/ha.cf if [ "$ISMASTER" = "y" ]; then if [ "$ISDATABASE" = "y" ]; then ipmaster_oar=$(host $MASTER_OAR | cut -d" " -f4) ipmaster_db=$(host `uname -n` | cut -d" " -f4) #don't use ifconfig beacause can beug if it is a non english system ipbackup_oar=$(host $BACKUP_OAR | cut -d" " -f4) ipbackup_db=$(host $BACKUP_DB | cut -d" " -f4) elif [ "$ISDATABASE" = "n" ]; then ipmaster_oar=$(host `uname -n` | cut -d" " -f4) ipmaster_db=$(host $MASTER_DB | cut -d" " -f4) ipbackup_oar=$(host $BACKUP_OAR | cut -d" " -f4) ipbackup_db=$(host $BACKUP_DB | cut -d" " -f4) fi elif [ "$ISMASTER" = "n" ]; then if [ "$ISDATABASE" = "y" ]; then ipmaster_oar=$(host $MASTER_OAR | cut -d" " -f4) ipmaster_db=$(host $MASTER_DB | cut -d" " -f4) ipbackup_oar=$(host $BACKUP_OAR | cut -d" " -f4) ipbackup_db=$(host `uname -n` | cut -d" " -f4) elif [ "$ISDATABASE" = "n" ]; then ipmaster_oar=$(host $MASTER_OAR | cut -d" " -f4) ipmaster_db=$(host $MASTER_DB | cut -d" " -f4) ipbackup_oar=$(host `uname -n` | cut -d" " -f4) ipbackup_db=$(host $BACKUP_DB | cut -d" " -f4) fi else exit 1 fi if [ "$CONF" = "2nodes" ]; then echo "ucast $ETH $ipmaster_oar" >> /etc/ha.d/ha.cf echo "ucast $ETH $ipbackup_oar" >> /etc/ha.d/ha.cf echo "node $MASTER_OAR" >> /etc/ha.d/ha.cf echo "node $BACKUP_OAR" >> /etc/ha.d/ha.cf else echo "ucast $ETH $ipmaster_oar" >> /etc/ha.d/ha.cf echo "ucast $ETH $ipmaster_db" >> /etc/ha.d/ha.cf echo "ucast $ETH $ipbackup_oar" >> /etc/ha.d/ha.cf echo "ucast $ETH $ipbackup_db" >> /etc/ha.d/ha.cf echo "node $MASTER_OAR" >> /etc/ha.d/ha.cf echo "node $MASTER_DB" >> /etc/ha.d/ha.cf echo "node $BACKUP_OAR" >> /etc/ha.d/ha.cf echo "node $BACKUP_DB" >> /etc/ha.d/ha.cf fi echo 'crm yes' >> /etc/ha.d/ha.cf #Don't work with release two of heartbeat, to be deleted #echo 'auto_failback on' >> /etc/ha.d/ha.cf #-----------------------Create file cib.xml, configure ressources for Heartbeat--------------------------------------------------- if [ "$CONF" = "4nodes" ]; then echo ' ' > /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo " " >> /var/lib/heartbeat/crm/cib.xml echo " " >> /var/lib/heartbeat/crm/cib.xml echo " " >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo " " >> /var/lib/heartbeat/crm/cib.xml echo " " >> /var/lib/heartbeat/crm/cib.xml echo " " >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml if [ "$BD" = "mysql" ]; then echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml elif [ "$BD" = "postgresql" ]; then echo " > /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml else exit 1 fi echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo " " >> /var/lib/heartbeat/crm/cib.xml echo " " >> /var/lib/heartbeat/crm/cib.xml echo " " >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo " " >> /var/lib/heartbeat/crm/cib.xml echo " " >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo " " >> /var/lib/heartbeat/crm/cib.xml echo " " >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo " " >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo " " >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml else #2 nodes hearbeat configuration echo ' ' > /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo " " >> /var/lib/heartbeat/crm/cib.xml echo " " >> /var/lib/heartbeat/crm/cib.xml echo " " >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo " " >> /var/lib/heartbeat/crm/cib.xml echo " " >> /var/lib/heartbeat/crm/cib.xml echo " " >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml if [ "$BD" = "mysql" ]; then echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml elif [ "$BD" = "postgresql" ]; then echo " > /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml else exit 1 fi echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo " " >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml echo ' ' >> /var/lib/heartbeat/crm/cib.xml fi chown hacluster:haclient /var/lib/heartbeat/crm/cib.xml chmod 0600 /var/lib/heartbeat/crm/cib.xml #-----------------------Modify OAR service, LSB compatible--------------------------------------------------- if [ "$ISDATABASE" = "n" ]||[ "$CONF" = "2nodes" ]; then echo '#!/bin/bash' > /etc/init.d/oar-server echo '#' >> /etc/init.d/oar-server echo '# oar-server Start/Stop the oar server daemon.' >> /etc/init.d/oar-server echo '#' >> /etc/init.d/oar-server echo '# chkconfig: 2345 99 01' >> /etc/init.d/oar-server echo '# description: OAR is a resource manager (or batch scheduler) for large computing clusters.' >> /etc/init.d/oar-server echo '# processname: Almighty' >> /etc/init.d/oar-server echo '# config: /etc/oar/oar.conf' >> /etc/init.d/oar-server echo '# pidfile: /var/run/oar-server.pid' >> /etc/init.d/oar-server echo 'RETVAL=0' >> /etc/init.d/oar-server echo 'DAEMON=/usr/sbin/oar-server' >> /etc/init.d/oar-server echo 'DESC=oar-server' >> /etc/init.d/oar-server echo 'PIDFILE=/var/run/oar-server.pid' >> /etc/init.d/oar-server echo 'CONFIG=/etc/oar/oar.conf' >> /etc/init.d/oar-server echo 'test -x $DAEMON || exit 0' >> /etc/init.d/oar-server echo '# Source function library.' >> /etc/init.d/oar-server echo '. /etc/init.d/functions' >> /etc/init.d/oar-server echo '# Set sysconfig settings' >> /etc/init.d/oar-server echo '[ -f /etc/sysconfig/oar-server ] && . /etc/sysconfig/oar-server' >> /etc/init.d/oar-server echo 'check_sql() {' >> /etc/init.d/oar-server echo ' echo -n "Checking oar SQL base: "' >> /etc/init.d/oar-server echo ' if [ -f $CONFIG ] && . $CONFIG ; then' >> /etc/init.d/oar-server echo ' :' >> /etc/init.d/oar-server echo ' else' >> /etc/init.d/oar-server echo ' echo -n "Error loading $CONFIG"' >> /etc/init.d/oar-server echo ' failure' >> /etc/init.d/oar-server echo ' exit 1' >> /etc/init.d/oar-server echo ' fi' >> /etc/init.d/oar-server echo ' if [ "$DB_TYPE" = "mysql" -o "$DB_TYPE" = "Pg" ] ; then' >> /etc/init.d/oar-server echo ' export PERL5LIB="/usr/lib/oar"' >> /etc/init.d/oar-server echo ' export OARCONFFILE="$CONFIG"' >> /etc/init.d/oar-server echo ' perl <> /etc/init.d/oar-server echo ' use oar_iolib;' >> /etc/init.d/oar-server echo ' \$Db_type="$DB_TYPE";' >> /etc/init.d/oar-server echo ' if (OAR::IO::connect_db("$DB_HOSTNAME","$DB_PORT","$DB_BASE_NAME","$DB_BASE_LOGIN","$DB_BASE_PASSWD",0)) { exit 0;echo ok; }' >> /etc/init.d/oar-server echo ' else { exit 1; }' >> /etc/init.d/oar-server echo 'EOS' >> /etc/init.d/oar-server echo ' else' >> /etc/init.d/oar-server echo ' echo -n "Unknown $DB_TYPE database type"' >> /etc/init.d/oar-server echo ' failure' >> /etc/init.d/oar-server echo ' exit 1' >> /etc/init.d/oar-server echo ' fi' >> /etc/init.d/oar-server echo '}' >> /etc/init.d/oar-server echo 'sql_init_error_msg (){' >> /etc/init.d/oar-server echo ' echo' >> /etc/init.d/oar-server echo ' echo "OAR database seems to be unreachable." ' >> /etc/init.d/oar-server echo ' echo "Did you forget to initialize it or to configure the oar.conf file?"' >> /etc/init.d/oar-server echo ' echo "See http://oar.imag.fr/docs/manual.html#configuration-of-the-cluster for more infos"' >> /etc/init.d/oar-server echo ' exit 1' >> /etc/init.d/oar-server echo '}' >> /etc/init.d/oar-server echo 'start() {' >> /etc/init.d/oar-server echo ' echo -n "Starting $DESC: "' >> /etc/init.d/oar-server echo ' daemon $DAEMON $DAEMON_OPTS && success || failure' >> /etc/init.d/oar-server echo ' RETVAL=$?' >> /etc/init.d/oar-server echo ' echo ' >> /etc/init.d/oar-server echo '}' >> /etc/init.d/oar-server echo 'stop() {' >> /etc/init.d/oar-server echo ' echo -n "Stopping $DESC: "' >> /etc/init.d/oar-server echo ' if [ -n "`pidfileofproc $DAEMON`" ]; then' >> /etc/init.d/oar-server echo ' killproc $DAEMON' >> /etc/init.d/oar-server echo ' sleep 1' >> /etc/init.d/oar-server echo ' killall Almighty 2>/dev/null' >> /etc/init.d/oar-server echo ' sleep 1' >> /etc/init.d/oar-server echo ' killall -9 Almighty 2>/dev/null' >> /etc/init.d/oar-server echo ' RETVAL=0' >> /etc/init.d/oar-server echo ' else' >> /etc/init.d/oar-server echo ' failure $"Stopping $DESC"' >> /etc/init.d/oar-server echo ' RETVAL=$?' >> /etc/init.d/oar-server echo ' if [ `ps -ef | grep -v "grep" | grep Almighty | wc -l` -eq 0 ]; then' >> /etc/init.d/oar-server echo ' RETVAL=0' >> /etc/init.d/oar-server echo ' fi' >> /etc/init.d/oar-server echo ' fi' >> /etc/init.d/oar-server echo ' echo ' >> /etc/init.d/oar-server echo '}' >> /etc/init.d/oar-server echo 'case "$1" in' >> /etc/init.d/oar-server echo ' start)' >> /etc/init.d/oar-server echo ' check_sql || sql_init_error_msg' >> /etc/init.d/oar-server echo ' start' >> /etc/init.d/oar-server echo ' ;;' >> /etc/init.d/oar-server echo ' stop)' >> /etc/init.d/oar-server echo ' stop' >> /etc/init.d/oar-server echo ' ;;' >> /etc/init.d/oar-server echo ' restart|force-reload|restart)' >> /etc/init.d/oar-server echo ' stop' >> /etc/init.d/oar-server echo ' sleep 1' >> /etc/init.d/oar-server echo ' start' >> /etc/init.d/oar-server echo ' ;;' >> /etc/init.d/oar-server echo ' status)' >> /etc/init.d/oar-server echo ' status $DAEMON' >> /etc/init.d/oar-server echo ' RETVAL=$?' >> /etc/init.d/oar-server echo ' ;;' >> /etc/init.d/oar-server echo ' *)' >> /etc/init.d/oar-server echo ' echo $"Usage: $0 {start|stop|status|restart}"' >> /etc/init.d/oar-server echo ' RETVAL=3' >> /etc/init.d/oar-server echo 'esac' >> /etc/init.d/oar-server echo 'exit $RETVAL' >> /etc/init.d/oar-server chmod 755 /etc/init.d/oar-server fi #-----------------------Modify mysql service, for return 0 if mysql is stopped and you try to stop it--------------------------------------------------- if [ "$ISDATABASE" = "y" ]; then sed -e "s/stop(){/stop(){\n\tstatus mysqld\n\tif [ \$? -eq 3 ]; then\n\t\taction \$\"Stopping \$prog: \" \/bin\/true\n\t\texit 0\n\tfi\n/g" /etc/init.d/mysqld > /etc/init.d/mysqld.tmp && mv -f /etc/init.d/mysqld.tmp /etc/init.d/mysqld chmod 755 /etc/init.d/mysqld fi #-----------------------OAR configuration with remote database (Virtual IP) --------------------------------------------------- #Detach job from server detach_job=$(cat /etc/oar/oar.conf | grep DETACH_JOB_FROM_SERVER=) sed -e "s/$detach_job/DETACH_JOB_FROM_SERVER=\"1\"/g" /etc/oar/oar.conf > /etc/oar/oar.conf.tmp && mv -f /etc/oar/oar.conf.tmp /etc/oar/oar.conf serveur_hostname=$(cat /etc/oar/oar.conf | grep SERVER_HOSTNAME=) sed -e "s/$serveur_hostname/SERVER_HOSTNAME=\"$IP\"/g" /etc/oar/oar.conf > /etc/oar/oar.conf.tmp && mv -f /etc/oar/oar.conf.tmp /etc/oar/oar.conf if [ "$CONF" = "4nodes" ]; then db_hostname=$(cat /etc/oar/oar.conf | grep DB_HOSTNAME=) sed -e "s/$db_hostname/DB_HOSTNAME=\"$IP_DB\"/g" /etc/oar/oar.conf > /etc/oar/oar.conf.tmp && mv -f /etc/oar/oar.conf.tmp /etc/oar/oar.conf elif [ "$CONF" = "2nodes" ]; then #2nodes configuration, very important, because oarexec must contact oar-server after fail-over db_hostname=$(cat /etc/oar/oar.conf | grep DB_HOSTNAME=) sed -e "s/$db_hostname/DB_HOSTNAME=\"$IP\"/g" /etc/oar/oar.conf > /etc/oar/oar.conf.tmp && mv -f /etc/oar/oar.conf.tmp /etc/oar/oar.conf fi #-----------------------Get database data path for DRBD configuration, and change this path--------------------------------------------------- if [ "$ISDATABASE" = "y" ]; then if [ "$BD" = "mysql" ]; then datadirold=$(cat /etc/my.cnf | grep datadir) datadiroldn=$(echo $datadirold | sed 's/\//\\\//g') mysqldirold=$(echo $datadirold | cut -d "=" -f2) mysqldiroldn=$(echo $mysqldirold | sed 's/\//\\\//g') #Save mysql old configuration cp /etc/my.cnf /etc/my.cnf.backup sed -e "s/$datadiroldn/datadir=\/mnt\/drbddata\/mysql/g" /etc/my.cnf > /etc/my.cnf.tmp && mv -f /etc/my.cnf.tmp /etc/my.cnf sed -e "s/bind-address/# bind-address/g" /etc/my.cnf > /etc/my.cnf.tmp && mv -f /etc/my.cnf.tmp /etc/my.cnf elif [ "$BD" = "postgresql" ]; then postgresdirold=$(cat /etc/$BD/$PGVERSION/main/postgresql.conf | grep data_directory | cut -d "'" -f2) postgresdiroldn=$(echo $postgresdirold | sed 's/\//\\\//g') #Save mysql old configuration cp /etc/$BD/$PGVERSION/main/postgresql.conf /etc/$BD/$PGVERSION/main/postgresql.conf.backup sed -e "s/$postgresdiroldn/\/mnt\/drbddata\/main/g" /etc/$BD/$PGVERSION/main/postgresql.conf > /etc/$BD/$PGVERSION/main/postgresql.conf.tmp && mv -f /etc/$BD/$PGVERSION/main/postgresql.conf.tmp /etc/$BD/$PGVERSION/main/postgresql.conf #to be deleted in the future sed -e "s/#listen_addresses = 'localhost'/listen_addresses = '*'/g" /etc/$BD/$PGVERSION/main/postgresql.conf > /etc/$BD/$PGVERSION/main/postgresql.conf.tmp && mv -f /etc/$BD/$PGVERSION/main/postgresql.conf.tmp /etc/$BD/$PGVERSION/main/postgresql.conf else exit 1 fi fi #-----------------------DRBD Configuration--------------------------------------------------- if [ "$ISDATABASE" = "y" ]; then echo 'global { usage-count no; }' > /etc/drbd.conf echo 'resource mysql {' >> /etc/drbd.conf echo ' # Three protocol :' >> /etc/drbd.conf echo ' # A : write ACK (on master)' >> /etc/drbd.conf echo ' # is send when data was transmitted on master disk and sent to slave' >> /etc/drbd.conf echo ' # B : write ACK (on master)' >> /etc/drbd.conf echo ' # is send when data was transmitted on master disk and received by slave' >> /etc/drbd.conf echo ' # C : write ACK (on master)' >> /etc/drbd.conf echo ' # is send when data was transmitted on master disk and slave disk' >> /etc/drbd.conf echo ' protocol C;' >> /etc/drbd.conf echo ' startup {' >> /etc/drbd.conf echo ' # when the node start, wait 2 minutes others nodes' >> /etc/drbd.conf echo ' wfc-timeout 120;' >> /etc/drbd.conf echo ' }' >> /etc/drbd.conf echo ' # If io-error, freeze node' >> /etc/drbd.conf echo ' disk {' >> /etc/drbd.conf echo ' on-io-error detach;' >> /etc/drbd.conf echo ' }' >> /etc/drbd.conf echo ' syncer {' >> /etc/drbd.conf echo ' rate 700000K;' >> /etc/drbd.conf echo ' # Rate of synchronization. max 700000K' >> /etc/drbd.conf echo ' al-extents 257;' >> /etc/drbd.conf echo ' # al-extent is the size of the « hot-area »' >> /etc/drbd.conf echo ' }' >> /etc/drbd.conf echo " on $MASTER_DB {" >> /etc/drbd.conf echo " device $DRBDPARTITION;" >> /etc/drbd.conf echo " disk $DISKPARTITION;" >> /etc/drbd.conf echo " address $ipmaster_db:$DBRDPORT;" >> /etc/drbd.conf echo ' meta-disk internal;' >> /etc/drbd.conf echo ' }' >> /etc/drbd.conf echo " on $BACKUP_DB {" >> /etc/drbd.conf echo " device $DRBDPARTITION;" >> /etc/drbd.conf echo " disk $DISKPARTITION;" >> /etc/drbd.conf echo " address $ipbackup_db:$DBRDPORT;" >> /etc/drbd.conf echo ' meta-disk internal;' >> /etc/drbd.conf echo ' }' >> /etc/drbd.conf echo ' net {' >> /etc/drbd.conf echo " #cram-hmac-alg \"sha1\"; " >> /etc/drbd.conf echo " #shared-secret \"123456\";" >> /etc/drbd.conf echo " after-sb-0pri discard-younger-primary;" >> /etc/drbd.conf echo " after-sb-1pri consensus;" >> /etc/drbd.conf echo " after-sb-2pri call-pri-lost-after-sb;" >> /etc/drbd.conf echo ' #rr-conflict violently;' >> /etc/drbd.conf echo ' }' >> /etc/drbd.conf echo ' handlers {' >> /etc/drbd.conf echo ' split-brain "echo Splitbraindetected >> /var/log/drbd-log";' >> /etc/drbd.conf echo ' pri-lost-after-sb "echo pri-lost-after-sb >> /var/log/drbd-log";' >> /etc/drbd.conf echo ' }' >> /etc/drbd.conf echo '}' >> /etc/drbd.conf fi #-----------------------Create the device for DRBD Data--------------------------------------------------- if [ "$ISDATABASE" = "y" ]; then modprobe drbd if [ "$USEFILEPARTITION" = "y" ]; then #Create Virtual partition dd if=/dev/zero of=$IMAGE bs=1M count=$SIZE losetup $DISKPARTITION $IMAGE mkfs -t $FSTYPE $DISKPARTITION shred -zvf -n 1 $DISKPARTITION fi if [ ! -e $DRBDPARTITION ]; then mknod $DRBDPARTITION b 147 0 fi #Create metadata drbdadm create-md all service drbd start #drbdadm up all if [ ! -e $DRBDDATA ]; then mkdir $DRBDDATA fi #On the master if [ "$ISMASTER" = "y" ]&&[ "$ISDATABASE" = "y" ]; then #The master launch syncrhonization drbdadm -- --overwrite-data-of-peer primary all mkfs -t $FSTYPE $DRBDPARTITION mount $DRBDPARTITION $DRBDDATA if [ "$BD" = "mysql" ]; then cp -r $mysqldirold $DRBDDATA chown -R mysql:mysql $DRBDDATA/mysql elif [ "$BD" = "postgresql" ]; then cp -r $postgresdirold $DRBDDATA chown -R postgres:postgres $DRBDDATA/main else exit 1 fi #After data copy, put the master in secondary state, heartbeat will choose the primary umount $DRBDDATA drbdadm secondary all #Delete auto fsck tune2fs -c 0 $DISKPARTITION fi fi #-----------------------Remove auto launch--------------------------------------------------- chkconfig --add heartbeat if [ "$ISDATABASE" = "y" ]; then if [ "$BD" = "mysql" ]; then chkconfig $BD"d" off elif [ "$BD" = "postgresql" ]; then #update-rc.d -f $BD"-"$PGVERSION remove echo "not implemented" else exit 1 fi fi if [ "$ISDATABASE" = "n" ]||[ "$CONF" = "2nodes" ] ; then chkconfig oar-server off fi #-----------------------Create service for auto associate $LOOPBACK with $IMAGE----------------------------- if [ "$USEFILEPARTITION" = "y" ]; then echo '#!/bin/bash' > /etc/init.d/active-loop echo '# chkconfig: 2345 60 01' >> /etc/init.d/active-loop echo '# description: Auto associate loopback with the file image.img' >> /etc/init.d/active-loop echo 'case "$1" in' >> /etc/init.d/active-loop echo ' start|"") ' >> /etc/init.d/active-loop echo ' losetup /dev/loop0 /image.img' >> /etc/init.d/active-loop echo ' echo "Start OK"' >> /etc/init.d/active-loop echo ' ;;' >> /etc/init.d/active-loop echo ' stop)' >> /etc/init.d/active-loop echo ' losetup -d /dev/loop0' >> /etc/init.d/active-loop echo ' echo "Stop OK"' >> /etc/init.d/active-loop echo ' ;;' >> /etc/init.d/active-loop echo ' *)' >> /etc/init.d/active-loop echo ' echo "Usage: active-loop [start|stop]" >&2' >> /etc/init.d/active-loop echo ' exit 3' >> /etc/init.d/active-loop echo ' ;;' >> /etc/init.d/active-loop echo 'esac' >> /etc/init.d/active-loop #change rights chmod +x /etc/init.d/active-loop #Auto start, before DRBD --> 60 chkconfig --add active-loop chkconfig --level 2345 active-loop on fi #-----------------------Launch heartbeat service--------------------------------------------------- #to be deleted, because can fail with DRBD #/etc/init.d/heartbeat start #-----------------------Add cgi script for monitor heartbeat--------------------------------------------------- if [ "$CGI" = "y" ]; then yum -y install httpd echo '#!/bin/bash' > /var/www/cgi-bin/ha-status.cgi echo 'sudo /usr/sbin/crm_mon -w' >> /var/www/cgi-bin/ha-status.cgi chmod 755 /var/www/cgi-bin/ha-status.cgi echo 'apache ALL=NOPASSWD:/usr/sbin/crm_mon' >> /etc/sudoers #enable sudo in tty mode tempo=$(cat /etc/sudoers | grep requiretty) sed -e "s/$tempo/#$tempo/g" /etc/sudoers > /etc/sudoers.tmp && mv -f /etc/sudoers.tmp /etc/sudoers service httpd start fi #-----------------------End of script--------------------------------------------------- ./oar-2.5.2/misc/fault_tolerance/HAdocumentation/0000755000175000017500000000000011757171206017745 5ustar plbplb./oar-2.5.2/misc/fault_tolerance/HAdocumentation/schema/0000755000175000017500000000000011757171206021205 5ustar plbplb./oar-2.5.2/misc/fault_tolerance/HAdocumentation/schema/sequence-diagram-without-detach.png0000644000175000017500000003331311757171206030057 0ustar plbplbPNG  IHDR|A+ pHYs+ IDATxy|TL&@ aP\İhT.]wE+jkSp.E*j-*-J +BBefdBBf\5;w97|>s=lۖ$˒T, ٖ_MToj{lSk 1-PW(uJV~IR.թGi6IIGzxN~Y!ln>USԡNJTQtD#VF)V$sUY9+RPQuUNJ?})._SOH /WA6qW` vCoSom߽2Vٶ 6iUv]Wy:^͒mu ڐl*z2<ԺuV~Wo vګsgu}. VVPJ}y?}/P~.?\p jFگqs3^ѷ-X}2_-;׏FXutAS5*Q*«yK/+hkr;Pk!ٶUwUN{ b#jygŸE-7_= }X %Yq/y'|S;U.ɣ E\تߤ+Β$;(Jѷ 4Y$K=Pc A k*[^鄊y<_Rr='zn^XW_]Y闉!N *iȆ@ xM U.ZE1uQ/ͧXz|FVT(PvKҎ/GqweUK/Iֈq(.Y %*J~IݤЁ{x:hEX[]pC[}&I*ֳ?j51 u;^}fj7ZΞ]|b!gi-~Y jy'ƸE`6CմǕTJMwbW=vS|uNO%[i*źǓ wJН1iZQ ,Lx[^iҏwXG?dYVC UWӬYZ^V,\ޫh\/;QsPŌZObczr:J֟iEQVt5buJJ-oj])JfLb}A7k(w[WQ Z`Ik h2u6/Q꘦zJj`y5mIwň tt -ThuV~Dz4VwCA1p[p@~K/7lɵ^mwCAJp[ؠѲ@vm8@l 6op;*;j8~-pd-O5@kALW&T -7HhmhhOlDTW& _Kv桹M%ITA*[LZ&6Dr Jf22"9\"9Q"9`Ϲf&Us[Q>8 22}6_`hiuq !b$dpe UjW&p8@l 6nW\bf|Il 6y^SJj(ڳAnZVT,˒?YOk?W>&R'kH’uz/]]Hi#%TmW;JV T0x \lhlBcӠv~X'qZ]Pl&=FZUV/u/ e oׅ),}M[mߠdreinf%4(6M]̉JMPԅ J6W֡s2/j[z)ο@S_mXV)Na kW>;JӶR f|٠+v,Ӕ1FXɺX<[*֓)v:~%[v߼gw%_4¯K.O-\W~It%)4(6B*S@*Sa|;uU{T=Hu>8QK=tO>z?M@ԡݴxA ؕ7Q'< Q4%)P%5,eV!:׆Ǵ!2aBmVUF]+яdIj7Fd֌7.WO׆ i5(6Uz澧v潣)9W,U 4eܫuzf]-unw5'&OS&m-|ꗠu?F.JĊӀ q<Da|٠ذ:?i\mМKBfܣ>Qf23z;5ЇkURF=y}= %hD}q~F_] D_%K0p_e)s[עeJO(LM4)ιE%ղ'O8Z)g^Lֳ/Oݪ9cАKf=1aW>8Y8'i=K5nf|iْۇ{,h^>Ldpe" qIl 6nW\bp؀<_fpe UjW&p8@l̽[7=Ң-TUq `O1 nB'h4fRM јIwa&+nxP4bA+[xGrTVFLu0 ɽ hI4ap ɁɁ6ɁT@#9WHz^Arh?OVՑ=Rā&U+Z?I1 IoI1 Iop8L*0WHzI1 Io6H{B]:@3q!VڄR~,K%o@C7wm3vdreinf%2ӑk7WHzhCALVjVk*ԥ4Vx?D#/W۶D('G7lCI&\W~IDDUSgTQdK/֏QgUh>_!mDy,yu#|ګc_UK<sFu7EWܭ"p%bmGƛ 髧yƚOo?(V~$)NV.wVRIdyDl4`kj~wʪ\R J\UʲUX Wb&ڐA뷴$[CzpXoG;#7*p%T.MZN=1z7WH_K7h2,dyWgi :lY?Qܦ3Nõ2KOªneTYq X_)t[[Feziԥ/__!ι8 #nM`&2TmO3] B2 +$T8.2ThM~A3 IoEl@qGrt\̤ZoS)Y'}Y~dI]sa#EY^l#~q3RtT^_3bv2j\-&֮ {&$aʬJOI)ȳR]:A,HbXʒDVHHu]:iGDʕ>kF{㩨݀/"MΎ=n!L*5i0i*uJHS`d^T**>ޒG| W< Rg KOK]kF{Հ^ռ7U/soa_!mIR"y#K,"iWm:ֹttpͨc! hȆ{BԍZTRmB;),U|SJY%ER)UJk~4?Rqd'Qc=UPFu6_*d\X y**N~mC}5ǩ}diBu9Sрz53` muVC>b pTp7p> F8@l 60 F8L*u8DopF:uRU.G9՞ j(uӲ`Y,|^#%Clst:u]B1֯lҋ:c:'(QyS+3n!'4: Ykw觏uRNϖm?h?TVL__tʎ㑋TTb+kXtԝ?jqoҿj)X_ҺG4jFlQF=}>P, h;GnװG4sRԡ>sr5uae`|e;(~Rϧ:~PNYb˯@\=ZOwꮹ拹C7ܩIꓢJ?COW ΗTp7VA+4eV.:Vk+ϖs:p_iɖ7/Y/]<2>ұ:{MZj"&j7BĩKv}ީ *Cu~Z'c)Շ!f9꒪T*}J"{;j\_mWXدf9_bś@XX^J9[.RN?_ӆu2T8_Vh:=\Їi23ׯW4c?ޢj7IjKRL٥j4JJTu׼w<%ꁥYݕ{NϬ.Mz.ϯfVnҏ咴|uuѠ}ԡ.|\.R禸En-n˲xGn% : bpTp7p> F8@l 60 F8L*u8Dop 1YS}b 3n!dpe1 "^qID:d̤10Ә}Y׳mc&ܭjxʲIj8 IP'b*>fRr6>ymxÒ9O:q̨ǟq嬗#1lذaO53|]cW?<}^0WRwO @L*Cm >zf箻*2%i+VXtCzyK[ҐK?^{oϗ,7-΢a&+ކ9O~wЍw_>J|\Νqq/5<^o3䌔/s=x^ϟxNF(ٲ}ֱJMom(v)\ncvO;.~č.*vWzz$m+Ӥk9\,OnqNM얨%!4Z6U'k|p{nYIxs.5`IJ2g٪իWxẃtë[BE[6qf222B۸C|^\S5E.ZxeI&UXv}F:¿ ^SYk[:x̤2~.7>e?^p%I?n.B8 ey뗿an]¡P(TV_']qa?V: LހHm{qcIO<'OJqI<~!$f^;~dy{W_=kǏ<@2r]Ϟ5r4+q>dg_օsk馸u3\0G./H\Zhi-7rQ=H Q"3%cm'ucEz\-Q|3*-VF` 22[t-6-}KpVcC|3&-}KЪ|&܍: Lz fRpTp7f22[S-5p fTiZL]0M)R !dpe8L*u3\6 ̤Q0Io3n!dpe2HpA*b`)dpe UjW&p8@l 6nW\bf|Il 6 UjW&p؀<_fpe bf|Il 6nW\bV}cj,,Y{gemi pb{G=w%I??aE xqzOL䍵E^|(Ek_|;B{oM ENCGoKIxO;k&ź K=z#u}i/-/9u~Ww=]t.qU_5_.ٸ2sC{o<ʲ[+dY-sU>:n޳y%YɣpHex7f\ʷ|n);qW3{y @+c~m/unOK7 hsxd w3x \$6 Ͱ9*@%+8@l W32 bw3x \$6p7 Lbp8@l W32 *p5+8@l 6p7 Lbw3x \$6 *p5+8@l W32 bw3x \$6p7 Lbp8@l W32 n,' vcwfgY& L8@owc"dpe fRp؀<_fpe fRphIZS]^ 7VCE1H$ l$wTgrp77O;9 @41-9 @l؀1 T"#hu0$(]rvfY,ywZp]USH6r^Zb;1H?Q)tOŊP 6ɬBcӠv~X'qZ]Pl*+WQfhy8\bXY%rUL?j-˝7ݮQ wc&ik#9Q WS9P5ALVjEX~xg(a^^| hIGw~ Al@(ڠe2F+YW {X<>`tQ.9[9n|4B*S@*SaIDy,yu#GɊs%UJMU T=јqXg#|N[614I k{I$epzZnLef*3S_jOuL~36([N,܍Th:*=NsS(׼w14OZ5E3+Dy4gD(`P%--֐^=%.SII壴l\{'j*4`P%;-]zC$&P5#Z^7TLֈZLR$}pQJ_MYjDU)O8DkVkG5y}qײNR\z #qWl^3p3b,gWgfFs֮]`eܩ_lE졏4p.MًjRT=ClL*!bp8@lݘI8Dl 6n̤"6 ܍TCbL*!bp8@lݘI8Dl 6n̤"6 ܍TCbL*!_K7 {:P!,˔ 65p؀1 f22 b38@l W32 b38@l W32 piG>r_G?K*/^r.ٸ'r#qI޽~5og+V[yg\^œ>a"ջ]aɜ'8fϸrKk‘Վ6lذas . GU:\fG69QǟzԻ_KYh00Z8Ù 쬜 kV}VO/]%'\wsGWK/^sq)}#= ֖[@ϭ9,#ɲN[Fm_ڜ]_8')i>~DpO^wM_]4ER+UG+v>\>s >Nl-㳍>^IRN_%Tke?W_}f[{_~v^';ƻ/?%>]aθW:zzRennWuru4b>rF?:zt޺bǭ!6n$KyhfɊY%Mߙuz:mO=UnۗnZfg?h h:lYeg uh+qS6ڱ¥YK>,66-`1jiGߥOvdW&j?'f;>s1N-cgƥv$)O?~ gEQd/rnysky ~x~g M {b.*vWM`l+%f^sÆ_?G'{L~]ٻ&vKTǡ=Bl a3xܸϭ5ބyA[|]g<{@~^pרS\S;b__..5$#1'v޲'k|p{nYơVԀ%)iȜeV^݅3w nLL }~ٵ)>5uv\ZX'O>З:=~$OCΝS5;VˊٯT?ޞŬ?~.~vS_Yn;ķջnE,u|ߤjǷ|rϖڛŖ] #+e]QpL'>qN>/gK닂cAk=)>eGhu[ h;#;:yh% ]⊻&y;Wn_կ{z=X sB9 Ϻtlʨ,)S\i}| =;s?hs acvؾNsǣ<:fTK9;(IΓPz%eu,YА7{5r}ʵakܻ - byY+wۛl/ gyYvgٶB Aso)۶KlUtyp(|[aàJdZ#P^StGh3 ΁j Rf{Рv48YJK-B7A,I=y>FSs'kR=Y1Pwk>X Y8P #U=GA9rܱo~P_rl؋9RԥXGlO^l/\A?F LQ`uWl=VjhlT_aÀ]H_xv26ڇʼn{ HivTp/W_RoxW[^E[2YW-)p+ǩ_2rP*sʫԗrF :ϪWb&<@mx̷*|sn6;+8y8,|z/)o~!.V!SX->;?`[zez#7~Μ o_H.6R4-HkFȣ'lB&C Wլ;}swȠ~|S|K2|ÏɎkmBU}}kWRUHb ޜ^ x V,b>8}G>ɽsl^RaN@wyY>mQJ#gދ)%)˻<ͽ. si 6UI]H^)%Ul]rPA!<:%mQv!Bϩb /G둔V!\Ι0e[2+%o bQdЉ<}"5%SN~?n^ k6oAXZY<\8ߤލ_ͧ׫^b;s8 %|Rs|?+2 Цب+2~yMv߄TS.qlE5m^އ*ogXon?㺓f NOІCes%Cs\$m|y5,lc2zcunͮ [$14Yv;-c}S~ɇY @ jk!{8ռw7O6զ(ؘln'[Wr׋ ?e\Kg݆ X?,H9F\m<r6ٟMd1,_|D-2dX6#`R.z+B(B\mXo5sNaT? `<:1oou୔5=F3q9_ .WSYO2j7a<y\Z>zRuRS| !$j'3jMgX/dAqCc0`qT>+e8eQJ]U<Z? i-09u9JZ;LGކ֘ꊌ im29Zfrkq& *wH{:arRWB:XjK\P-A*IRƆ$)gRIRioCI%I; IRƆ$)cC3$)|B|!I T>!Cې$`lK+-7(\A26%TqVbbdXnK=AA;J]3u{r8.8+ qGkZ; u!deۓݦPEUIcMe0)qk{eyAOI,oێR9J]M}/ʡ -h^ֿ3gSst~ _SYAioC]#o+tu&OcfiT>͂ 2p9cًVw"P1YWŅ[S5}2j:w I (yolHRpNz2HRu1k&;3$iMA*IjuL*Ijq7 ކ$[ؐ06$D#9I%g>$x5bA r`[Ӆ ^(< IGiNw0\֚-J 5ݸ5.|7[{ Z:*9B|!O>0/skA5,a ,`W( /q-WCuXwPZ3Z8r Ryށ%"M>r[V9JZol ӡzT8Bmb,'@ 4|| ܤmX; 705#y;5`HbLL9KZAz/wH{zf)X`>Hڬ JV?\85tmlF Gnmi@[v\UƆިʤQݡv6 p)p"(⦟%?z}iNW jFGNy;5UlZmh}:0 RȀ/CIGj:NW# J7#[ռuiЪvP-s&B/Rv(b Vl. _v oӜ́׀f<6Ӝntӵ9L׀V/wHF 90J`\u-ġ2bOΆ&(Mua6Lϡ¶5#\ռMZhZL*EVãa0']fBC>6F%Zt/ =X }܌|Uvk@N6m[!5huHE $)cC3m֡)ĕioC3m֡)ĕioC!I ؐ$L*Eup qeې$L*Eup qeې$`lH06$I8Jf*B\6$I8Jf*B\6$I IRΤRY W IRΤRY W IRƆ$)cC3m֡)ĕioC3m֡)ĕioC@Vg7@_Kې$~Gf6$xsΤRY x|R qeېhH*ΤRX,S E^E- I%I]n6$UIh3#ĕL*IRRIRzO∙!I-Zo:tX+Zv?Eu(5o IR3ÙT6PJ'Mrې$*9 IRz͒ؐ$ir8JfJm6$ImH~Gw˲Z`%IIZ~sJ!I T6P ކ$)?JfJې$`lH06$I8JfJې$L*Eu(doC!I ؐv0^g҉4im-w%{ 3V-⭈ňeSvWM'^Ʊ(NRNڌ%)476!ޙ`\"5qΤRY3yk컔'1qmcb\ I{{jJoI$q'( Π_\v 'B=:nf{`L8?Y)wwQRkoC%/fL?|zn;9|Sm|r Sw &߸3ʢp#>XZݸ8C^^+7qaݸ9710+%uŷڋ1 יT6PV 9eɱB۝"q/[_((_꫚`Ռ{İK8t1sk8u""Ij2*daM_NE=rNGF] O|}ȈQ0xz&:~EQTDT&ͤM&Mvҹo_[FEEkz IjnհҏI~yQ5\:On哪m=K}=bܘcԍ1y7dKůgiCgu#*cCڥ`c韬zl_]/2H q ,v2`b ivsy7<9E ГNH7z%`g0:EG<_3m֡-֛+~͜S$%B;ffx-ۖo6:˙rc#^Mee㫪8PS[{pxrq˅ggeͷ_3D=+XأƙTOf̛ΰ^ ,<1~- ` |W6=Y )'?|z*=Ùs d 9ן㯠,z{`;sczFO:Tb1,d1{ IRƆ$)gR)ڬC) {L*Eu(doC!I ؐ$L*Eu(doC3m֡ IRƆ$)n$MumHCRIp&:T82mHIB*!%R8JJt2 Is`Ja۲ E^c}nQ:up qey-Ϲĉ{fw4 .scWm]97WZ=ddfff|g}rׂ}ʯ_xfCG^+>gOU =Jz;)uhlDڻgeה/E62VRDՠF.4Ĺv}OkܔQVi-XKWfg.&o?XM7M¯UGYZ:B/.#2w~$w8K2q+pԅDVُĥ.&w KWCې"8B.lBMb abȂG] 6%z4 5ݸ5.O1h r7=K)OWGAȅp>Էx 3 6SL*E[pL:,m(a" ^yp>:` vr X4x QpX/Bk[굧tp,,7a]20`< a'Ŧk!tfRgYP ,!hX>Tx cU1kzO׀U2!? gi x6yn!l ۶xɫdA6lwX nLsv%sJL0NM φS8+ȃ+X-j{ڲp N`g8Z GMƆԙj-ʤݡV}) :lUACe-$[8]%?`Nڦ%44jY8>J,]Yj4?`qӅ8%)Gӭ&Fl 4Ul!} NgP06R]rJ&5;:2[`oCL0nfˋ.]0z$m66L< x XlaSt0Ē\5< L*E]ga 0 >  ӡV\t ّSCeU -l3IDATԫ5E TJjaJ-T[p|Zu+6N6^0z±0p` ' (n0 jSP Zl@[Zpn+ҟ.2$Cp n񒁗!z6\u~֒vU6$It^l5H-֡)ĕioC3m֡)ĕioC!I ؐ$L*Eup qeې$L*Eup qe:H%I A*IRƆ$)cC`)ĕilH06m!H qe IRƆ$)cC*W!I Px"-ĕilH06$I E[(B\Ɔ$)cC*W!I ؐ$ ?ɔ:D,nKڬz?0 OD90 ;kK06$I E6N!LcCh tE[+ؐ$`lH06$IWQ2 IRƆ-Ui!LcC!I ؐ$`l(B<_E46$IWQ2 IRƆ$)cCh |EZ+ؐ$`l(B<_E46$I IRƆ-Ui!LcCh |EZ+ؐ$`lH06$IWQ2 IRƆ-Ui!LcC!I ؐ$`l(B<_E46$IWQ2 IRƆ7\CotoWuR16|ߗP ?;!?9`vO8~ :16d}/|VPk6Zw[ЭO<{] |EZ+к7pM*}4tY_\3+>z>"0*zЏ/|K߾~|%u*>?מy1p#}ʆ_vM|<_!-i Zwb7ܯ?o,{mw!f_?lbw?.y⥗t7w@7/9w>}o˂.ui^›NߵO^F™n{-|YRw;ok*Wu(׶z,yqAiբW?eD㇖cAiբW߮اY7CGgZ?O*̾cmpN ~j͸Kտ=V/⬢nj 9njxNIT~SYAFf;yRJflh]Y^s!zg ?>6//*x속{nZPZ 3@ݵ_oFՄ)(HQ}ryuq-:†cr *DZMR sY (k^oG7;`PnGkY,oÔ,g-[[ٸS|xn%ukgc9Jjgνj]Lu|CA`מ40qro-C}?W+{qw3_vڏW~#O[V"#L:ԊqˤрX,o]X y]6oePx7פ>>րƠig1#tvT6NRI!_K)̕ilH06m!EZ+ؐ$`lH06$IWQ2 IRƆ-Ui!LcC!I ؐ$`l(B<_E46$IWQ2 IRƆ$)cCh |EZ+ؐ$`l(B<_E46$I IRƆ-Ui!LcCh |EZ+ؐ$`lH06$IWQ2 IRƆ-Ui!LcC!I ؐ$Չ玅xNbJA!%I mv5N!LcC!I ؐ$#qIj*ֺ6$BgNb&T(KalHƆ$ؐ$5>9 E3 t$!IJ#Ur6gRI-kƆ$EMؐ\~M| 3D^]fyl<RP 4zcQqyV?qå/2h$UKFy=t'N/Zbi^|Ɔ$uogAyTTpor9RtTU3=巰ӑ|U_ Ɔ͙TP}1ofs˘( [dEnOvB rk||| ǾLuLr3.06$jN@fbIvg}HzR_r(C N}5SGOSdԍ1y7$7iU&\WMgӓi);rR26$lLq67ĒJzG#gfLOo"n| Iq1 Q6T΃_2~BG\O*Ɔ$uXo5sNaT? ķQ[KM%?%l?j*+_UիJ:.4yiT.8a?ȴ55M6gRzL6df ;Bv>'fP\U/G2 ^ʥ#Sj恬zR5oe&gbizȉ]-x^;AKa%X,طsH>ώ[[- X,mΤRīe]wvScCkQl{6 즬+R)b1ohuA"A*I06mv5 IRƆ͙TR@Ɔ$)cC!I P9J ؐ$`l(ڜI%dlH06$I E3 IRƆ͙TR@Ɔ$)cC!I P9J ؐ$`l(ڜI%dlH06$I E3:>NHPbv80D+A*IRƆ$)cC@,3IRH\Z8RyWR qe6!KƆ$alHR0OcCsNu!LcCpPE_V'ĕilHҚ }16$Ɔ^P!1ȁoM6z,x$R9rajZkF#*e;7txCqȁ>I_S0Hu,ȃhV5og]-TېB&K,]0z oӇ908t06Ӝntӵ9L׀Vj#cC* bṕP+zxkq WUpNw5̆i9TXXضfp䔫3]T - W!p` ' (nqPaA vQIsV;KFBO8FB67#7_ЪPmw+bu.3d:}e IRƆ$)cCh .ĕilH06mkR82 IRƆ$)cCh |EZ+ؐ$`l(B<_E46$I IRƆ-Ui!LcCh |EZ+ؐ$`lH06$IWQ2 IRƆ-Ui!LcC!I ؐ$`l(B<_E46$IWQ2:RĄxAj { IR]fIRې$`l(B<_E46$I IRΤ$`oC s9w~Әnyse=PO╟p9Smw5SRӰ.Ż% ˚U_Yxڑ[<G3a>҇ZQlѣGc NSaFxNcO>?vf~- }/{e;o޶/K?yk9ͷ3r3.{[*Y?䜥; y{7۳}cی}kqgpkνe-~맟qrɍ?i,]zW|=3;oxjG5OܲKƙC?//x y{/߻_W2mHVOߨYY6~[{_o}#7Q{8=7`~߾m]WKv!{Z_1;9W8qxFqe{|U'>xqJ][Y%{ŵf|g}rׂ16P.MkbyM_a;sΐMwWU?첍i=},JunQ/[񡣿;u`vOʒU_p 8 /6{|o~eo6hF )b=F{}r޸cN _baUvQ@׏ɓ'q'sS(|~ScGrءz }ޟ= wUYEsoZfx8P68zt.?8}(gځ`w٫,X@5bl(:|$;x”s'gn<"˦囦w95W7[[xYu򏱮ˊXmg̙;wά?=SgF^wFɒՕ]NnQ^f[+ؐE¹%MoN"GXV׳O߾3~:E'߬qw\z؞'OO"Xn u'H|+IDzs9_iav~oAMݬ,_uCD<̌ E[?A~>quTt2RFOo^|Ͽꚪ_0:hruY>}ʇ_P{}=?be}'UjӾ sn{iꚲ>xѴI㩯^_YmyMUSɚ6;|o/}ұÛO[2g)2z{e_٢2l/:d ?gï};oڑ.,u`v;)[49V`c .%`ҍc%n|⚑ݚ6,q[|5vOoڒAL<ݺ F5P6;C,#pí&zũ5&|.rC2$];}α7źs +/qE[gLJWk; ~ݔ+ĕ 5 efiѸ ̥UbN7taU9?hҿL,W!IkGoalH }!k&0 ^Ml1d#.AQVŒ^`Q\nx'm%y㔧+8[)eJmؐBdL:,m(a" ^yp>:` vr X4x QpX/Bq[굧tp,,7a]20`< a'ŦF_ h |88azbTXS% x ivV ?]VɄ8jk6{>`¶-^*Y ]0.Ӝ} Seކ0NM φS8+ȃ+X-j{ڲp N`g8Z GƆ-LCϵPE͖Ae҈JPNp_æ[5 AA1To@BUmcMm_rJCiTqJ ,K5 yRȀ_sj{4=ݪQhb& r.MZtp6 Up+ c`# %iR՜ ), `4u͖7]`8Hl ly@U908 hK@nra d7%/jxƷelHaa6LϡBt(p=<5.Ȇ&(mv䔫PxUA<}iv-og [>jMmtpZ0%R .9ʌ E[ `p,0f<Ix#TGnj&$FuzÂЖ-:˄ 8 z[deȁp k0 v2Z⧚Zw=ކ$)cC!I ؐ$`lH06$I IRƆ$)cC!I ؐ$`lH06$I IRƆ$)cC!I ؐ$`lH06$I IRƆ$)cC!I ؐ$`lH06$I5{x['IENDB`./oar-2.5.2/misc/fault_tolerance/HAdocumentation/schema/oar_logo_small.png0000644000175000017500000005510511757171206024712 0ustar plbplbPNG  IHDR}nǻ1sRGBbKGD pHYs  tIME;6p : IDATxweU{snrUWUWn HȨ8Ό3ʨ`Όi@GGĀbD@A!$tJ7ǹ }uSZz׻ֆ?tOU~.n?{>˻}4Zs+W _yW]/]nm۾pޜ/l~;͙"BH 3 ܺ7W6  Wtv>x7/_N:IG'[~Egi}-^S9E },4{+tVTJ,|ipc*рPJ!Ca35]Voas [FWo#~|ꣿ?8zȟ ?u,ox,MY 6ysq;ObpJ@)0z!DZ+N];K)rkRbiZ=]8B'[#B [3`7q>.Ww>{{%45{S4M2V !J) H@kݶ 4a0Zk|?!%{r{Ah2 yC׼W9>Ѓ2ݫs~I宱_GxYo\w5rߕ[;zPk)MJhY/Nv)NF N0[ !~ND`>Nj#Y26On&7>}/~r-x_sNX qΒ5Vzs!adP cL6<\z:8ŀ0RA84M|qlf'WuGXxm;:y͝@߅RB>^>c<ٹ#_1g0ꑭߺI~)4o߶_O~ܺm?{>?P)h4pkuQ 40J'.;p44 jj LDkeY!0MbaA2*y^:A4J)0}aHgg'RrL.i3 #}_̛S~6 qɗ;'O>?w\yŻӷSҿv/ PZJ):l4Bb7ԧǩOlF!BZq8@H,j;sn>=ꤷKla&.kU*rXܟz_{7~⽯z󹧜_^r^Cǯ\𮏽dizrb(UR dzr!:1勔ΥPBł ɄċLL\s|Ϗ&JY}Ǐ Ðj$j5R\.G&ai0Qu·tKW|O|/|=;^r0 ?ѥZuJg7N&6>ECF CEuj\Ŀ^vAf_>˔S9\ex زe 7o0"~p8jnU_ousFŊ0M sؼwxvt ϗ1, Loу BE6AKgCn'5+߭!?>۶qIRՅRw?5?n+01.:gLC= ?r+_s[~dX(n6@T'6&ËFa7<%_(BJGxS,;`_AH <0 1Z' Pͯ9& |00M3#b/l^b6SYXv-LB@OOB CV+{WTϿ/Ow~x^i!MѬO}܅.B(*MPzԦQ' |\S:|Ҵ |>y~6%'iѫ~ɧsZqBnj6MrlRJgggWYG @y2í0G|@zgY) ?{>}7O6r@kM7AAN՟|RJ8*ձuLmzarEJsJdsE,: {kRe8]>VkO4M0h`vb|CCCcWau]j,\)7i Q(#mXsAk / {-}j}M}/g_|u=^!s!ټqj)v?w,4slU@h6mL+*J#Uƌ]hh-TC`ݖͭk݋$dpp.Q,! , iwb}_<W6[{g\Wd,Swmj[[OAg\,$|at\!4l.4Bӌ^+Mur+B|#ga;.z=1#tgR xkdsk7Mu, @Y{:/1A㤔4M8w\lZAVc˶)˗wqy᫾'}W|uw\QHCkhLo1~ɩC*x4ST:S>2-4b ]FxxN1ۘžibē dIM!ϓd>ZkLC2x 7nDkPZkJ| fzXg%u=.knmO9mZYuOPwp>|a~MJ.?nߌkW[qUPXP(ceH0-vKOKv Pƪ<+M=lfrrfI6RI>GJa3uE… t!~O?/-/k¿;v9W"SY LSٷ\Hj&4nL#FR'l]iRG?A*TF+QZ3=9A _]Nؠ0 ɗw/gj_}6zZڶJBP4YJ:jSSSd2:;;,5x7?~߻3?넆{W~΁5fNOl~^d9,!TдmCH JY1=Fm|~sZB>a*MͦGrx=҄IҔ5kh6*-S^ôqIe* Jek֭[Yz5ͣ\.j|B 6 dȠ9_~}yә/N+G!'Wغ%'QO Q>mYE}j;>lLwCkh)=H B7`j|+fkgf0xqQm9~z=BzXώda^e /lD'ttt088H__il6ٸq#[lA״uttBjǕ'4z/n[/<7o|}(NzϧF;;{F@H<=.JAe/X.VD> ɾTb;:"K(6v}ϩ n=񘜜P($um;]UW !e0M˲/fg3 XH)f# ۶mcݺu!rT*r\"\.syk{X[hUe1}a˦c݃ 8M,+pjtΥd(4abPisJa7ghVǰc1J+ |&S(&&6M?%" oȲߢHA~:֚F *B!c 0+)X~$Uċ9Ú5kR`%Px֙o߾meߛN; ,˿㈌2YѨEŔ;z$ k*Hib&y6:Mn19לBX9 >>.|[d+GH 4 ҥ1kh], 4×ApҞ%[xzZbp2[6+n݊R!,:Q{qxsCߡn֦mLDG ZK._°,L"mN#$صI<139 N=Vl6WPJl4BdnP.US: T\ٲWJᇊeaHm:=Lȱxqhl8CR! pB>۶mCkM>GDSyBçFNҏNn(ߡgBab;#4%aࣴopU r0] )waesXFTzVZ*NM2<@kle{Z#!fVY,q[khwq ܲ,fgS ×ӿDhM>oOɖ蘳|HvW4⋓JEL>:M͛Y~=B~,h.WW'/׍VǶT (u!W(0AJa!R(wZ-4u039f1I:W̕Ȗ(uP(u`BÊRL!%AXf_Ҡ|Ȝ _aʮn7޵OgrS #ՌLbrr2IC"]] | 4$ap= ;⥧3}Qbdyޢ?}y'=O_ rE6_xN\JiqU<4=) 2BJg/ËJdϠQZ|V*]rE7$hgah )֚#ỿxrfNeY;g@WW Q8Gc@<01 [(jo|)䳒Hh"|g||\,?qoy? wsɹC-[̗E T!@U C'vT-vR<iF_h$Fq:k72* t#&JS]`W@CGevwhJ{uPû6Q.@fMϳu#xn+Wbk>:עةa&5swcˊ]vK/9K߹ndZL(^wza{)tR袳3C `zjliY$ ?ɘHx02xD+IF*AI۱ZM/aR1d2 _FARe8a,%.淏mG0Mk֎=E~PSG#[V9^tHTm C|rR)r9V^p}| !n>䜋ukY,2uVlڴ)J%XtPh߅ڡ}G(p9+#ۺ8K#O{ES a"@8%ThtPa@B&2^0G.f6+ce deF0'_(clH\@jyowVE]@gNFŽLNN&HRyjz.A񵓘4@!k7lz(-l||g{:*F<x-4|K-E쟂 *DǗa±n6NMKAP0g\, 0CaHd]fɡ& !qLus3blR]v JsKgۿ|Lk3 LrL__N+LɉԶo +[$_)KQFaHL+wpuwwM2Ȣ< BL{/j* lnDL5|LELBVɑ"*i!qlb| ~r$.ٰHMCxԽL4Ya =i7iK F-^F#\Өјغ<08mũIuH6~\6Ay^>!\v:4u ϮwAXR~rHFO L\m)#=B:6V&0DQh4Pt RZP}7}wa6~aǵslA}/Z2X:Wn }|ϡT@ r/>= PA^է 6mڄrLXDkMXlh7m峀Og #{;9&2V.OTAJ dsQg9%LSbQ CLˌ@"*Z#FjM* bQIZz.glhT |ӣxNT'SS46z=0m'}iWשT* R~)%'1ȕ5^| d]fj.(|r Mu<7 #QL&TX02gCM`||)&jmzl kZkKAU/\ + BM|0- jCitZ5B B|iNU|&p*M`J {(dE ˑrIJ4Qsi=/ze25Nw\SXx3qXk.^p'["96xQ l>aR(0 cF6ٙ=.8OFEFz̸ eZ* ٿN^2Co<:::`hh&{6~7g>|E\cO{g>~0,SkET3h"!Z(uܶR(-Ն IDAT6&VAkqe P'W?B&WDkEPGLˢ)IBzX24P {5bwㅀ7*z(3ذj&T+euy2"V)tH!_coy9f8i0*dQ(/&2}цݟ~yl!%Z)!D0D:-0ie1 2yLsp+4MiE )mDTR)22VQ|qkESُ0Ybs6}0T,y'/!TzF2{TeYEz{{wZԜtQl2/}֌bhCb.i5 9=5JԫSZʾ⧾ӹk6!k藆vU"4-0M2Q(bf-r!$C& m( 2baD$% PQk2"Z(Ea\7<睼dƾda>޻h`GuVkpfoy4丸=?=kxh;{ibeH+#Ȼ۰7C-!YfBH3Q|BF 5JB]R`P)* )ZUyZ_(b5JBRu$c18[1]SҐ3[kdAϋo.icFSMms#lWjdyJФHϡgw!圗Κ=ha}!%Ji(1 !͈:z`L׾#) * "%đ $Z+t>*pS5m0@>a5&˕QA@cN=6⦛M\M2-$qLyP(xE~t=t}4RJǡ#GTv5[O7rY4!F| }  i؞B(Jhm"w#c*D۠ɵG%I12(l@EȐfe0L !My)aFc`.]oWҰO1-Ŷl9:*i8Pqq }뺸1>r9i܍{0|' ۇuh ?1ha&fm-zxQ`~*T<ޱõBi%A!$\affLaY2ieLӴ0̨>aJJZ@&̢aZ"(;"/_'YI]vF߻hF005P_^ Zg}zkz MjJ$XD..hYdg֌2d8Ķ5G'CxN@![;DJ[` !M! BM&*mJKcvii̘Zt]y;pZ ux;?Öcd\\\kuk֦RvHj8; B VZZвn4JDs+P(-;pV %%Rnn*|6$|\OvZ I|OX48d#Fh4=.ݾzqu~6T:1UP^,ra.|c +d@-*/ vohK"  .hVZoי9HECٔ>+033zЬbA@8ó-q"0YZ'utZX(f\8V >W hiڄT*8M)x~oax5F8Q*f{#1* DPQ @ !<.@!DTbu݄M8AN=j7,߹7#aX$iNL9fbc#`ZOYkLHOrR9ż%\o>aX-%W拠\SLJAw>,K&c& <x5OY3g@jWE]{Sc_466b#Po*˻ϯqݫ{Zg,+7.T8"rXx lγk߮G=BEsntX7CNO(]Zޠj 98?bXxƩR{}}Tn ߟ>wGiPbU fV;w5 ټCOlZl-ٮϱ36`cF~ķhE h~$M;கMfʴv/K?.~n]Ϛ\3/E0˺}"ōw?KUZkt;2>}vƿ{\/DJxő ۏ -٨ạ3kRQߑM ~CXaKI4Tn?^bv3JQ0X~G4ݐ|.Kg3ԣ>_,c7Q&H@1>k86Lc]ŻZR6NPk2n)U*wuP;?V.>W'7ݧ0wgZ:D[`\q]/۶=ý"hęCrg)%7A۰"q+_Kͧ.R ºq]q(3.Z g!8vug-ȣ#ulډ9-b;3SPpN'˓oൃ/_oaza#NA[MjFkx>1a?o~rPƬߍ󡋗NW+hg?|~ L' 2L™7*A pkBɢuVFGGj4M?4?YTp=e+W 8?YK~pcY\lkY4-/_U;zԶjޘ#R*5OFD>]wb&/򲃆'.gw<|ibfFK[xqe~{ҖgEoi-ep~/̘3,&F'/}Vm=>>K*ŏڱ5=u]~,ۯO\o}ڧMUu#{gq:Ka\ώMOܜ S> ֍=<|tANOӇጬ!];H3;Pͳ=G~ t_9fFcǬFwpn_2V792Nu]ßmɭg8x~/+S(Dݤ3ϛfpܚ/J~>=}Vs%#~4]u6\q,@KlՉh3cQ*!ML\MloTV4j/38#C$><́C ٚ^ cEݦ9^Tp_C~Ž$'\'ֵ\xr^u~Xy\?FxGvUq\ 0 `5|84oWtk~|56O<0T3:v_gؼ}k afc^qBFSbC1ћqtR2oggN`o-ovAkqԑTOl6i63@b\YK>t=Gb7߾2^Ah ieh>3`.ٸ]vym🷮Vrh`?uL>Av#2.HO28d2;~NKvPJ 4HKů~}-کZӰ}J^/9v k_-݅z.kUhNme@d%.,/lq]/-/>/)ްE鸟fƘ9~.s3FҤwz S,bK4Q?D19%{,{ 9y>L\N )zGK5:9L)JpxL:3o?_uWZbAR*)c0V ;O'eB@wwN] &Umbȵ~g-7{6҈wضnx:tTMlb:uhgc{^\.G`of1[I;>';9>K Wo7=:H155I \) VP g^Jy{Ň::"m30r ۩l$ӣӀ0 FW0:hyRNMa!Ce,ZFА'WOpa=5n?'!.7sC8=VwvxdM&WJxTBj[pUTRAK"esB۲iG|!&IFl~1(; ;1u%Eȓ/ R2v#4$# oJB&M-3Zp/_}:lƐ$gN͖=n>M0wH!iVpS8q8bZp=WBꮡr1s/z7몛nS_qZ4+ I~\.'1b8=H!?eӡi3BAC`F͂J5X~y{I?Ba>f1pGOV)o>R</d,TTSk4b®~w}5 _Ŷ8ƥOIb{izjxX.+ ɰtG8hɼޕ1mi4]l"KE::Jsʅ,bsf#8eA_((QL)EٌF!48v +kۻ𨪻sgdC ; hEZDqylk~_}ܫVTS< ,IDAT@Q\싁U $dL23w9sMi< LL&{~w~{ߛ;3=4GxD+۱u1B3FԝV,UGʕw*Sښ7.V,=/ Pf #/ph疢^c&&_z &_6 ։jqӁtngsV6 ð{/Bab[30B(0"nޣHCR|m'|(6q`fނƝ(,xs7d &vt DŽ+Y7P짓|1I vd3uE/r( ˀZhX|o%m0N WuT>yZtCWFzzɶ:w_m7K[8=_Fg8}d6nPXX2H4E-hG 7y=.8Sok҇-6eEvUQ FABg9>]S=nli~45ob2Ls9|>=u4jsВ&suas[wǵ=C-TFK$OG='T/X#;,ӯ,SN/*~E7olW(Ԣ)ҪCe>5SHe:A.PΖTw{w\p/gJDnd9?|aAl@ی[Oz꺎D"!q~/*Gᡢ L&@!dMs*ثד#5Es^\|ŏ>YcJiѨ8ZZZz[[YCˆ6hsX3RA_.riP,gj)%ԊH(?Z[[H$؈ۘFχ VXX>[ eڵ*k f4{f73p_ w|uwSOh=޹Cv*(4UYx򅹨( ;2c<6`s?vOnrα3(T*@ i gqG넲Y73ch ##J?:-KXOA迮nW,[mEQ {.Og; jHejLDK[;}H!,S !*^477Ja7ʜ߹=(2m0ހJUeUDlBPXVр*du3/uuz2u(:f0¥DN3IW{oѿ80aҭ̛>nBݶ:G(**iH&f(..!Tnw&N˲h0hT|LnYH C8-TPN(JU:E۞hn@8810sN@ii >m8ZYx}>p5j^G0/}ҡPHz SL }׉1nCܡdvZ`-~횅iv^i00nTyDsL/`߾}8aH9hd6lף;wM ֠f~AϚ]fKjERAD5h{TjI~JƓߝ5t)EVHu 錎n"Jv4cf3zb;&uL0J[!r4d{'Vَns:X3 mؒxh:[2kҗh+?ߨG!'+~P-洘bHDQDY\U\@ڈ  +:pP/.Λ.[+6ѻ&%GlG^Ǥ a 9Os[[,9 [ T%&4h!G7TkC]jL\`̂x8<(f #)59)BR1YU!^<➂Ee\u`@u 8]QTTP(eY^,Ͻ~:YF!Lp`D9IxÅh%y Mz +X )ɋMl42M1]l)PȾs0(P+BB !TE ' -i^ b}Oh>،e+ڂbTWWDmc 9'm!8Ŗ{XBܮ8Vfh30K,Cb1H `.cέ.^=Vơj i0t,Б͈ar0Vx4lCCjkk~x^TW RTҒ<|&c hjw5/䂓f>{z4~!CwLYzλRl9le"><xZ M/#+ fSa6Zte{v.]p\~AJ A?TYDKxcn{ѳ,vލZl޼Vb1TWWO>|Hg=AR)ٳLHϾjzuy>Ni;I]1һfhI+kBo4y[|YBgzh9;CR3Ŗb#%8Av0;R[R$:,ptzfFf: nfQYQ6;>F())AyywpqQVK3chbR)0PZRgarژǾį>zw)5C8`sϼX֛/_ĸ>qƆvԜ gaس˅w4vVW2B6B08<@/RjW, t2e"3 S4͑$^f2Э}ke:nG;ۺ@g>_ljmOŌ?Ig YGWh'^9eO6580fL"eIfYbʞ òLK U3XBHې|~6H_:.@;:luRTUwLw͐+7[V-P*g!m@#@&kwwOA1%P~Ckk+2 JKKP *JXy/~~J#Tsȓ6ٷ];C^\{ !6s,﹩d8]7e=q1b{Xt&,,S3ɮf1'h=h9 `pN?+:)1 8-TUUw\eZ؝QB ż%y %Yl#SE] 0MDyx@qbR@%8 Y G*P\TpBRQ>8'+zڛ_~zȯn?9+pegȶLSE`܉ߞW9zvLvBym77mqņm3\'9 #\u<|4{]dsLrfrg^ޟ&/\0g̋yk `ao >?^o}-(w,<w䙹 F љH ܡO<WfH PVVfi d2^CKci-C\?)& AӲ|ZL OF4 /ؽ>E/TФsJ PDf%T2Ȧ}5⦝_i7^~#u_[SQ3)ȿۻ2rnwoc BXL2*:!6ƙ-3d,0#t5%M?yXjz΅d\Ŧ]>m躮 &6p3M:Q6|pxP x+ IM8~=<97>o_ƻAހ܁Cx(BXY];<uIؔ6s6;1| _me݂HD3c`*P1@6Xq;{"ՑBϷa}`hLfb_hs#m]v0I{G9au]i_y8nfUB;> ?Zp#o8r1[zH+s>Ɣ]̥*gH[K`0pn[3[&JMӄ%ip-fqfY1E83aZ,eLL&}Â%rs&zXҲApLJ\xtveɲ Ī!ZquPZ/?߹3Wc۶֟L @at0 ai caB㰸(XS[Js[tC0th_H |>/< ̍Qelçl-2qV4`y\@)ω}*XNC0 r!F5N)!hШh`XQQ.Xpmg$eſX:[6uj'x+**F8w=;GٽƆ!azDiZ-nq8#Lg6DJ6HE9Hƻxx((Eb"#ѰnC=V}Af ܝRhH‘23h3 DjФ7Pf̀AW1>xSG3">aLV>o<냢5>Z{t(.BU3+L#UH&R=37puEELx^$ϪUI!0Zv俇V]QPm-F?_[[tf55}ۆ;hɤs !M_TIF kP`~_p=;w756'H*nij,5Moi)$YHF!sـY,n9@ "V^pg Y^ѫWx6^ _q5iG`8Y?3axzL` ====s* |IENDB`./oar-2.5.2/misc/fault_tolerance/HAdocumentation/schema/oar_logo_detoure.png0000644000175000017500000012703711757171206025255 0ustar plbplbPNG  IHDR/|%sRGBbKGD pHYs  tIME31&% IDATxwdWu-ιnӓfG9KB 9=8`٘?#F<@HFB$@BhG4IhbJ7s~{oWtH}o=}^{mr;zy] ZZ\[\kqu/u+x,F` U=BPBXyP,~mMͩ鱑+jPk5}VaNrVU YTU5@y1~"0Ƃ)+m5&JR_P/f T7/_UWoylk1Ei#{[us9/={)+/Q+bFJ8zyJI 4J@I\߿gqLؾk[ۅ'y}fUv{O-epq}`^|;W?Kټ/fȗteKOXSW@ ^7??d(/j <;{8$SN3n6?KI&(FGwoO'}(DeY2KPq(  p. D M!8nyf{(.!)@犴Ǯ1+s_ 7|_lSO)YY`#w?;;w7X]zbg vy.l^LSp5^ W󦆇E:CvapB0֟֝t. ܱWw&wG|l~ V/&.Q]9tsD(JAI!(!sO]916bGtE5UUpKpWar]?o'6/yobxJyfa,RgTJ+FĀ txc`10 ?FBhC ֧{W`h>(PUAR\.MӠiE(""PJaA#\ 9W8$v?X[7_x|޹^if"fs;nCpyeY2pq45(˴6[8ZIܤbvS |eKտW .!(f@0 LҔ IҶ p!߲.McV aBuDQ(9Oue4MUU9O\ ;X ٛ~2ئQ\s}x~3;.u̱֧ι^Cؿ߉?YX:w|G@^c A0ffmC9K K;Xq/y>鲋NNfJXx{϶s"",;M=ss0"Nns~IhLBblRhPٛ܄סXdOC+8d1Pϟ= C8VfFJ)P(fuD^yq-=WG:EV??W+ ( bNsBK9Gsf^hMA@-!G4l Mlj;4>2Zim7\k,` Qs(Bрu]GXDP!lvKB`?pxy;ֶKEuv|5B8. #fQP%aݘF{fB]Bq`%rd<4=SP@ąfa BQ) .d. ױ|0ZHPAMPTf>n1==;vnZbppedI"RG; Loȿ_s'g\B^"E;nǞߙ\\A6[`Zzr?Gsz?. ;_Ayp%lJą@}BXL $l ˆJorc @\uywR/&%qLMMl""Ŕ0I5W #߳ ߹qQ4+R:^Ckjz7s÷tiW ӆnf/6<$iP]~!Bc "&t3QCn2˵ ~K<._@>BŶmd28E033]v1\.l6r|TLS)~58}7KٗN_w͋{ [{j/B2 pΠ6{QDWRLKe ^Vlvn,Y% tJ[5R?EMv^+[U&E䜑;~rGo/LjRIA D? UC&߇ᣐ/CUTϘg!R]$1B+5MJ 8)Xv C9|ߙSS^i)C>0LTv;{=@*A80 xۃ|qdٴxiZ׿ i,+}un"J^1y^q0>>vRj MӺS6s=0~v{ غWppˍ{vE3* .8f&v5< P> 0-  bB*jf@DR)mM#[胢(RAEQoѮ**9 _ԯ! CDQu]rt뺞ʙ!Lis&dC'?'QqEI5MK iT.&&&`YJJR&jm|7`ˎ3o8z?XL_MsIɔ0o'P۷S? "g`Xy;'ybL(x-xpE|5m< , B-/EXLiZ!;['2Xr& x\㓍>E뺘d}0,TM0 s~as^`xfNCnv'9V( ,+Mk{At8,vccc`aɒ%)a> _i(UCEt&hkQӘg!FE2hAh=Z(43^c^k, @~r~UӠ(4\RQ(|/;Bӫͻ{O]WT z.}Wu]LNN/躎l6|>L&!l۞`=%0((t]vM_y7_lyr1E"3t>NQ.]bclh6OAׇ>ض}qi.] ~8UVee_O]}%߾; /;_wњ|n۵yaf9 ̌m/cpq pawb) 14ׄ33kA-d0%ع8}@q!l4A nL"ݐ4UGabK=#%c}!nV>~'ϱP͵^lL_@DՂ8uR b1W'B>8طor*Znn5w]Zo[؋%4FݻN!U(#ǀ Y&uZ^{ͩ=>[r}(PBUh|qBI[140<,<1Vc n!1  F[/܇/A̅΋IUvZ5fdd|w]( 0-alۿqfubx+bz-Q4U߮x5lfPeVX@Qu4jhN33 ža膑Z%Rzm !44A|牆ŪRKk8Pش}kV_"6TűtRDQ ݻvB6E__r!#eYXlfff01>?~:\s˦c}}w~1oT,0.M37M;T]ǒU'BB0aJG ̌@6y0OG4]T=#p0q=Kp潣cVA,1pR1F0ʰ(TY';a(<]ѽDkTBDŽʢ4 CCC333Ǯ]Q.*,˚3QJQ*P(011w_0 ]\{Oh ^=ɽ(ô!QCQxp[54'wmð \yA!ie p.`ZY$4 "niK2BJFx tnn#zavq$Ni]]ĝ JV C׹m_F6ij#y]\Qt-y_KQ']QT*l FFFUy$+]vZ Ao|'wڻ.[AO?=V"A;{ѝhAQucS CQ sY6t+Jz  @]>6Mkd(9-p.Io#Z|BМQ%ؼ2 -%Ed';`,-JgLIbʩn{܁UZR$j(=%7dvnJzv$@L"BȔlVaPUj53ufxiQo}wׯ|FU3D}Mo_wg9Ur#&jρE>\ë˗@(  4>RQ1twsɕ 8C" p[p[5PU|A fde.Va UOES/vg1>78q pY8j3!'uB|W9Rq 1>>qP.%ѣ"Ivw>fлVC:XOl}mW7dQ xòA1^s(.aX q| ;_1?!>|.}6Td J( О@p#`a@@-(!S臙ru#8uqZva{q,8{7Yw'zP7by9 UMOOԅ4'9 !`ƼEQ|tjUU˻{Hᣟ:|CW*` qiW-.8`,`Ru9 AhQP lT`q_ Bx029Ї൦6v DfVV 3itttEm5[rZ񅿼lAsoPzGfNK_B 0p@aa`y.0-$Ev ]lB [ 3Q<֭]'H<QȂeY, $LOOu]ٳJR۶9msl>&39e1DQh?ngn;7{5F{kC?0&FAPcvasS -)qN<`Q\\f& MUӨLJ@bM! όq8ν@:ht ֭7(>ʸrjY MW;kyh6޽{j|>|sxڦibٲe`attC\FRA)o/bןVoX\] QnU׮![@q)8 830hј9 9l ȕ`YY膑Rؘid#%%Ơ%V8E pYd>TJJ"6nik^TKBt@Zh4ؿ_iKfsE΢Xd cؽ{7:,YKU eG6LnIhN]Њ19l$BK01 gf?$D;v] \T$*Ž|Bd*3=r ׻bRa5ti+vⴵC<ȓ>qI|_NB*˨Vu(TX1LLL1]\{{9kB쁍w}{^sr4Du1v M7/cc.(U03n}ׂn搯,𪓐j*(HӚ0ʔPB@<. I>?, E!Psi,n6iqg9AafL&zw%Cu.^FXV!kr45sR#t|[ 3MiZBfQl۶ ]I*90 EQ088(0::7?һ/|ۯ/ݣϞo),fXB &tΤX>< +|K0, *p fxMHtN0(~|!9 xpy"(L;ۜg(U.b7("` bmdQJSNt1<[oN=~E8iGrEf]E>@tR.;@P&͙ YBP(P(4oߞ ϫ*FFFy>pj|?~x%>ps?s ;*ë wAb**p[p:ECR)E x@PT5ܒ(6wPsTߌIEQxnk ^cT5bPQe: P lGXAZMRpλz7T1ᤗsKضSN9h IDATt*'U鄖d0D%4MKWoy9.eY0 o/_b8)!e5G~_>\\|pc?wk] TmB8K  B $0D7'7[1a+X>B-DxDPJ9X tӆnd4݀j TuּfA__aW %QJq։#1-f0 nꤾxwAv1ZsdٮBiO:chh(X166˲PVԷv'ÿu7qݟS n\쩭M?!T^ h#pR4r4L4&):tK1JA"_Bp+2cD p|-`~UaaXy+K`f0"JGZMwvFh6i's>?1`s͒lbbbWSƗo'[G^MD{U(1-zE*dÚE6hfvLLS`)HpZuhV6/2EfV$S_Gw-}iy=0 (-49"q=aMN+]2#Bs$ATUM4220 3޽{~ cn\\xmW/Ň\ `'?Ga{t#+ؤ&^ ~kQBL2+#D(1HerYa: 1v2:CpJHD\.EeA[psģ@x@mjSL[;"pp]K]dzgQvL&m7v/?cMzwmC 익PH,']նmchhK˸k.4 J% ¶.¤L&36lywX{_G{gfEBĬ^w8Iejذ U1!! e cR,6gOE쨑 Q$'X$BnB< @]խ3Rz;wL7n"rbFf! ;Ig1NHE y,fi f:QU5arHkw$zСڅ~0틒filVaϞ=4-N>\-cjꭧ7Iկ3h.xr H°9CxkkЌ v!>GIFEp]]ɏN q/d&B3Bzh.РEf, yDAKP5CLT݄MLhEPz,KY\#ŕ}KilN&d& 'IB!=K%$ $Tg?'b=$tʰt3OIG䵓 zoo8snkK6'_gy M߿Hj6dx|hENr22d;JB?|72A݄æco?9|-kq#lWeYI)ر#^TR,UxDžq͏6uǍ}>o cOwų[F }As*3K9LG4ŢfW<>Et{ }A{RrtV=%onwaE\Rh=zIAjjh`rrA^ 'vnA5.UbLXhƺoC8P躎A 1z^xseJ%\| |GϘ?{OK/+8מC0ll&6.=rrϐg0Xm=?N,D @rȖ eɖbujf.VBZb[*ɴ7Bהâ_N%GfW8/mG ^i]$(5q}|QS3lv]nh&8 ntubj ;2ԃ;cimsc~x8a6&}b?!׷D*){(U^/&9yh/#I>|)U =Xݻ[s)en~MlӶG.޶el3Pt0'8 f'G(i硩zLX 8Veъ4vEݒK.R/)h@&xpgJYFa(<c N|kY&>@9GsfOlG28NZP(9O=vY<]uI㟴F'驝$.ʚrn`0lL>(i a:-f6Ct(|Ѹ1ElJ]{\iE' ϒ!8IptD8,J;9 gc9lڴ&ݠ06m/}ēcmNm~sNoϘWv*`NV眧"1V{mhkE{ii˹VVN"%PiYk8ꨣ quLLLsP,.qAȇdGNk`wߏHaQ5T36Ra_B*q?O@ 1}:NB!lIp%QAziiyp,UX襔6d4Ռ%SJ)?BPUZ-bFL 0ڤ*|`>ΈERR0yn9._م\"m HRPNO}kpUUA:sRKgIӝm{$L DHo#*  gn 01 gR)R)j5urL&B΄?IEd*\aq-K4U;C Ƥ6QR3} 9sw&',cN0pfB(p"9|֫Z9(hPbR̖(Ͱ`6T<ҤYC'el#IYB@V &p+,$w)JUTH$b1U{l߹x.Uג)U&]ԛ/D6c}}}K,<σȈAɊ~G}Ų]Q EQԋQdE5+:gӼDNA-&ې mYa8crCU FB?,ݴc U3%1>01G@Vm 2]eBƲNƏs=``IcLGɆa(`ۡZ|^D_^BQs @yV\(8`Nq˓6aP>(fF?8c'_ (U=k R˻zr!'砱 KGU)1!ے8cm# H).ǣ, Jj:4ɶSJR"Et|^A Xp1(oNsTQBwO>j}5 Kq%M6oy`O"tsc۵؈Rf-Kj YfJ2&9j4^gK4N?ײ}I{&?{^MNZy3&[[|,sJ pҜ&ԩ]P*ƪU7S^tcz5!8wpQ.d^+ 2?J9(lҍ)yMU!Ll^F),S0 >Kg\q Tass7yCW|i获5x(vr qw!KTѠ~h!FB$A~zT+E:TU:!j_ $NKiSq'}Z;-J'Prl]º%͋@qw1};Pu2ܷ\qɚW\q1 R0YeӴ+bNkTQc&ZTi}㠪zFQ:Yݻ7r9Js5m·p+"\I7gjxa=|R!ixNY"]Pou&"|bb/nނaYݴy -6s:5_1=SosЄ!$NDLpå{HR1nCzI%(% $u41X`Ԣۍ?`,dK@H1 Hf#V:9k(@ؽ{wQwBsj&苎-#]]Mn^Uxӟ-C롻iWf +έEϬW76'J͘ TJw^t478 !R7+9?3 }[7'1). el_~IG}+];$@G'. %-gt";rr"L3I$*># Ƅp8E ZF&MWRQ #SԿnPZ UT(T( $(8XO>lvH{_oZUE(\rt#QrI]+p2%4L {:$ " C Դ'Q{TsoRl*z#dZ͛Z_} sNR 9[Ð+P"JBw*/b^KxY! HBn |gjĚ nLu7Bh"43 Ͱ)T ͐Q3Yt֤ڄ:Lz} .vط~SRkwiن+.Ysk쫳Ȝh16RXH%EQo¿aϠfqÀFO2pkKW މ$j(WrA|#zO3td+vXE?N 0B () s44Z͌l{:;H*dBSBE>g1kݮ!C@6U$b ͙)F|vC]h؂%k~|[|3.KE,G 044i7r~t&$JdU<8jY?0욷I4s{ƚOޝ4C_V M\n:;,n Cg!yq((*4#=NT0ī!CfKY4}vsGKpxk#[@UԎ@.|);t@>VHpP% +^y'=/Re^s~"꺞g_|(|O?71R IDAT}54ZG%K|  >%^Җbv1{+n|LݮPQv}]rىGe,CڮQ#% 2 ph 2ߠ"X&2rpx (P5*I, JB;6OW#9!irES?xWl8DSn֭Zs&ijb3.fff099bF:"[F)MϏ[vNuRJ:ǝ'xoVC}{/a:IBp(*fi"D6T6G 2 \0,xXKGȳlZLjи 0r/IcIN=>'!עƑ<]чɆ6+WU*UmMLR CCC]w<c\M{޲,na B3P5]2ͱ߾}iTolۺuT7ADQ@Gٳ>5bU 9]elHEֿBHw,1[rLtxvo'F$E{%eY]Fi+7<+p̑ x9@bv檇@akd6 S{ўދN=>.Y=ROK9e;xfQa$6J 74|eQ,;r*} &_&w)3 y;)S1,p\*^^1y+hBbIh4x撔Z#*j'2b1@!M~ 02wiov ]w̥>|o0gΜQ#>Qĺ\6\R.7~* @*B&:Hpg鷣!`=xkuTu/XgeԮ&,f7(⮛*+h2CQq|}b[T*6*@(J'}Ȕ]<=Q͍`ෛ݌il rra<T&岸i.o8sbF.ay`x7=''ς ~)4Ҍ FG.oE# F kn@*61Us3Z^2jUo2ujdR.AU59b r Ye-4FBKiHD 3 Pb`BV,KN⾛)L!A˷1_ml\U,ST(X,&Fo®i섓H^>Qx=0@aь ?Q9~lw*KMU]9E\ r-L~%)S6JezL(wscHlQF"D)"TP{'˺SnA5 |>Ao( -HeRb/}{8T=RpxJYU-S/D8?0`A¹I6i>x' o%; AM6\* .S08A) #gߐc̵uؼt&: I3?Qɕ_FEB0 cdoS;x̒X؅, KqѰy%s*ؐ@e% bDE}}e!mT\`FI6LWKl:PYOj UbKSy{IǹsB+7<1J! .^~d2'A(=qpW 'e5LFe+?z)'D8QA}1SS' I$9 JF) `ݠTKASI08@FHB2TbbTUpjOqKΙ<@-m'?.b$4O[ej߫;Oylxn\ii[:,XJ ^}j'͒1ҷ7`0 Cd0 ~Yt$RoDSƃ_97jL&#}¼@ķaVZ"((["QCU`="ܩJ~%2"cTFǁço?߽PQq (Zkg|T1:&4ʦلdzvm>Cy*brDU3{_ZL@&Pok"OIw~^ґJgi[OSϪM/RTE)Ri)8Ÿjm!orfra\3'c?^h"ߤU 8a"3XT(7Yxyq|a*mN2zAb^Q`Ur-pWi3L#Dn;EUb+^`{8|Bhy;Lg 1ubm)c^PeWSm02j ^T{q>V)4iDdLUyDq3 7dq= EuAJag&-Z MninAg[Zhn"АLļ0,b@pK8u'pԠ CFO{#c}7ェ\j^ 2P5l| ;5#XO6#*ҩ7 hMX9 q HWi4Uvf4*{zy9TĒyx$4 \H%ch'1AW {b(VLߋO&ϛ~߭P۶7MS/Sփ7?Q΃ƃ>wmN!o=-sІZCJa$Gj',t]OP%h[yHj2B.<;s2njV-A2AB0NIŰnL\j6l፽gb[3~L4?$Bv G{Kf׷?xk//|߼u c k!jGuߚ5M l.*(eJŃ/d5psV1˅ !8:Xp2̜2kl6 rŃ7b1]2/;#a[pjL0,֧YpfMbq%wxw4>3 / CYm 9 䨋m۾0iJk#ia1Q&J[8trҝ,2oQF`”Q@Rl"2GQxOnmM0of+Ɋg+^xe7[W*>J~_A%GP1xѧ{| (rb ގeս/(Orn\ŦkpA ~u3夳dP?H4MTNRY씈\Oxm%X؃S$weXz>D{A<u<#j͕m8\XSNkV ݇_n.k}:MT֛$-9̘1C,*]wȖa٤ZV`(sDH쐃U&JbMaD3e.β,,? {c-^JTR~^Qûe 'e)q4Щ6yo;RxVMpQ*p-R(1i+$}Qf۶ -! RDf.&Hgek&5g&3`9Ӄ'.%l7&w]׬7؂};87J M;T>?qkS1m-c]+[͛?q$1ozKc +AA3jYq0[%wGꑸ`.\;a#(zn")8U(^XsG}bn3'>tgSO}! } *U W]ߺ 4wQQ0Uxj( yQ2֔_޵;Ʒ.mBEaSbt&x<6l"s21w1:e_|*6>3ZCb&aF }m͛{rQC'yi4N@Lz. 8Idr`D5lA} 5- U]믘 {er=Q~B 5U'\ 68xNS ,ہi=1"h˦Gnmu}X1w*15&/%|EK0L<\Tӂ=-Kpma38v >ኚc|&213eQ{͛vYK*{Z1? MTpxο_= $WP<r eӎSX1B]M945&jho"[VQfґCOWn*ؼ ^s{ö),F8X"T:B)(`q-c1DzM%.MB< bg '>x V-oa{=+6(k>eUQ&!W k aN_!xmYLh[Q\|2^yl ?… w,Y.xc_/6n;GDAazBQѸL2[,WQc2_UGu܆jQU/NXi̒RWhzJ.hnH59Zu|?W-Rķ~:|=_]̥T1c/V/4!?O_Ë]K{9ߔOa_݃by5wpHV;ȧSἏ*@MN$ҥstn+|= xz$+ TU_ !.jq``66l>'6C}9Jhz:W"onΜ/OYh o<=$lSm"')/m?Ջ&lﯽ!֖vò95m>)M+V:+zc$7_JjWAImULvH{;'FaD2wRT(/{sK ǥ}Cāgh^6ұ}6,3SgJxXۅ 6b})TJԀ5a?'܉ïHll6tcpQE5^rUpB+~#\( %%GXgp,J-P@nh˲(61i{&[Z gOƒ%`lC@ C\;QQrq:jsTbQs{%D-4#!55+Plb t˗/im?=6-9,\pZ$<$ #`_ ![0X|X z(ˀ 54O-7u"l2 ߝߍQ0(Ck،UBF\7R20w2}RXSSy+vөS;qY 2Y;2 YӴ|{ϠXvD[Si(`DQfPa @SVô7v2)w_;"Pe/ 'L #=Vi3qQX~\hr$y^zM^,2D eCN%τ}ۏLru^uoH[/awv'm;0Ma+0L`;0lT:** ˁa:0L gO?Xݴ ô~<)eQߵT1m "ބY4 [A.KߨL5>1PnJ) %MMNMhu$۲%0!2 (E[av_΀2 x-$($2Ec/Ab8sJ3$' IDATY5 EaQ xK9FWG;]*nMX-0 ԉ qgvQb׭[SfzR/+7Xeޓ(5Յlr݁*IkgM{K>{{n\)Șo^8PѭKMƦ'5R8+ EC6{lKce`خm:`jBeU?^\{{{1<oj.^u*!5+kaX ,2)LҀzfq/D<1kVME"ı 3MoDBm ^xhpm;t@r5ƓI,^8 PH=Q0iƕVA0?"zՄcAKfh1U%6*%3iF*Nj: tg!FagNG!_ v\~*$y! /CKS~lgz++ce<V5xFm0sK$%eY(۝8W_Ac6[-@SCRI̙9,B\6 Z7cmۈNZvGog2eڞ_xXp6cI\NX[H)#x7g6Lmn^vIX[Vm$CfUa1/8](&ڵ'jU`׮cرcx^>Hʋ_*o|b}prk!|tl?o"0Pп;R*l͆ըV#I277#9I#]ރAb8.[>] gw#NqI>h?x4+1FGWG˞K`Ư=%~ ZA$UrW?&f)S;T$2IEl)o/bT,]TSG7`ܹKݰܰXkS0sz4AFG!85c/ӧaPk- SG"U󥇧p"MH$bB,]#·^d&&M:8Jw8rBfγ0Ljc@n4dk{蘰95Yҍ= vP*z}hmLƵqieZȨU(Q@|;trFpSy[6mN25;g{{+Z42$KvM<@ckpIlNob=`S%Gdg>laR* o%v&{aoWiwy WԱ<9ׯ[/T zsHY`Rw}G08REKc`cpR\b6~׮ } ̻ @l)%F8%9L@Mdg;WA%ʷ"ԉtdZ,Us?[]Q+y,n|:耦تac~_شLc4EK=鹗>ʻvT߄1| D#ȃuCOoԪ1(u|g0>u>TqpÕSp`^=3RAUEVkJ"n$-JH&> ć<'@3l v-}y\u{*l6 ʉb@%~V)hԪxpH&b>~+4ժϔ1$q7aw3>h"a("Nغ &OM kzG1(saaƔΧNJ Zb3Q.rLSBA*XRm?lEdGk+[n7n?oŁ#g0RŚ˦1wF',f JzU3o7kSpժYzw@7- dSqSRFT ACY!ϋ :3 vemul)(5g6Zqe[+a%b6o;֋MXbYGqtoЄ}mXFZ" q{g})Zeq^xLo-WL٭PU)7jw$Z[<}SGN/-Ji!ri<􇕍CxT Qd|vwt{E04Κ L7˹DnS0qv\u/݉|'JL",6y.̗K1oz ^~-@TU\2:&Fq;<7Ɩј4nl޳w޵1cٿ_:R(fdRO뺨ef.e XK+1$a^a/;{ Ί`ۣ ZdOP vH;ǶL ymxnb-1mRNoƖg~uϘ].4]/ Qt>n̽5"ܦAijޓ'.Դl%aHX!4M$Iѱm~v<W=K۶h5q`ۖ{+uuxYLj+n]*Je-S(Y riT Il^݈L:!HmU\y477#ˉ&Pyζݧk7aTHgP"xs(^f5708X2BV,񠇗$Qp~GD:*٬P]Te0+/hK59x.HLE˶mRqIx]Β u\i>}dp8;Oӡ$9!D<{lX`5a"z]Nbͼժ ^t:=*>i" d8+Whۮ#6T~fͮ7 1HYҍYus-Zt (hB:plC!\A9HdAtQ^8sCjy§]?tXP=]Q,E`b1aϥBa\Rؔ0,UA1xa"ee44,(2OQlq]3aE̲|KQ9$kQ)`V ^y(m` YxWJ8_BW[DB YFyŇZb` U|Dm(R4z!+VOd {B?d+ U4ScV4*S uDžR9cCϩ{2ZeT`V0*p,!"ʢe,͈'nօK0gͯ/|W:oq7Ju8#q83_rV,\ K]2AP7B&uG 28z󹇄7 ^$+;NྛK)?/61UŒxyIv1c7zT c W,Mێ02]+%ؖjQULҹf'E*C,Q#=-_~O^Cvļ_Fjۘ r@O.XMq!ҙM*9W[; MDNc߾=`C!\3HeO$0~1L;x+AsrY>0d4?|sD_fRg>pۡT ʕ;i(˾m2"+?&3QA3[G{ޣPuv(8L #^A$#B" "Ș덻U9F\ n^|Lm":jJ"N]ס,SzNDr<Q5J),γ\)drq2XFE^s.&.YmܰOz~DA01lXYA0TaR`hhm'sg^͐rhr(eG?uVsBEa -uTϔ;~Ɂ3IBSdR h$H%hʧ i4hΧјKFXaK&;?x]3_.ʪt:FCl zMz ӲQ(JG۶ 2OeŤ&ͷZE sle" \>.8&"R( y!Agt:Q2S]dl3FnÃPV~;t/o-v:i#iH5$5 T ccҥ<˲| ק1R];^/C[K$@DeX"`vQ"(u-U(`{`UQ2ށm+O(CbxRmuI}+<]fY>+Az 0eXȃP<ࣸ-N(s2'S ?? ,QU$A>|XLR744h pVv 8JK<%‡ 8qn fatI&& !2r{>a؍aQ:$?V\^;6@ @(e*2uXF cA'L2i&l8z[~vb>-W/[1G6˻#x +O ?Ӄ`gcL""z sHRr9O/'ewpׯV"S{0$I~|S4I6E"]&˄7_bq(0jH'TGlgT|Qdže0%κ`ۂ#kFyH2`P׬k)=`Syk՗?s/֜SϪ^cj###Q"t% +Ô,QET#A]ܹ"yUYtVknkF[[[@يgJ1ظDÖenR3hQ6z綰3wr+ \-ٺVɘFr~~{e&0jm!ʡk&Rh^V+b4fξh6G]8! IDATՖM?ÿN|Sw1+;T*%CoT& b0 ׾*U Db1K֪:H}pPGԢo+˻.$X.fHxj\/o;߰0t$}aY4MT1t(uK4"҅d* U\u(L+W*m7s]@ݳ_My旿Ǟ؄Rru3HmDkXY.dASVKCqLpiчE [;H}ף  FX5s<<\r:p|/)S]Q*PTDb(2ف+>b"\.w$Rl٢%m=ן O?s3| [t\5ML9{&TˍANYEQ >5L_m؉w0( a`xDT i .x.JRm>Q aA8[z(<2] t[ pʀ/pg^ÓϿE{k !^p9n7lD wy* y\Xl/ubX̧d338w4hk<3C߇N(_WʅSX }b"Cc$iX US]{ 0XǗ/oq'ʦ088y nE)ETB__w?zz Bl| z|8R~v6%5) @Tчeǒ( RH3deYQ@ $a\YFX8('ׇ}$K*(ג$$DU(a>̞zClQ7f30 KGM=H\*(V_>9SW<30B>?dA{¿=܌f_u]ࠀ9)a;XBuCřE|p Z3fQؠf0J8uԨL,LHȺF]בf}?`>9Fn fBCy(qF ?l6++?^"f8}N=_jP>+CQ]KfW<.:Vsq j e1kwA4lD+sC`"hzCunxzRr,hI&!vgM#ronۺomSj|xy<̘9 Ҕu_r 微/^?tEAܠ!<-L7ޜGky~2`uo!KX,Qr9a.BFǎW-ő"D2fthx_[B00iҤȉa 0bYNoƒŐmla[9e˗z}?'>} P jHL0 >x@f3uڛܔGKslJD\PÅ2(܇/$T (lAE@p7 |(0 d7؋$aS&eS؎d\@ar);L&qիnܹsGɭVa\>ɝ =oZ¥Su;?_~o˟=M_D"Q1\*a޷oX1RRi. 8{A0ґż3R#g0f:^o؂Q`;C*,cpT? G!~ p9ɯrfhhh@WW;!&ԮQH`ƲmBA3(%3 ;`:Z;?O|DSo ,j@?e?f{/2/{/(THBEqbJ g- 0g)ʼnqYQ񍃕\4XI*UPAa|#%X&+ahR@`2M/:~}+N%}Z*| \Xͥf MA{0֕;O\]]]LI ъɏ~C ٶԺ~?Vز~ ZD$q-v E DT MTzbgz͘J-6^ͮy /5ޱȑ#gMn~,KL NrC"d0CJ)yr+~edY XDJp uS F9[RlO9,_2ǣq'L_Oaz4|;XnblP*Fÿx}\;S'_G?KgY1J^˓7kpx:sd)Q4T5s,jfM#ԛjc1[1uT|+, FSXH3nlqp!TNI6džƥͤPIEOAb(R՟%6e"ʯPNٗ-_P@\LGGǨl%͎}gwxQ~-u;Slc ş|fsjH%C!yɥPs-HYԂ#eL\+{_AK4v kJM(PS/Vջ)xLD+ ,W”UmB4Ǒ+w?|qh ]6]J?LG/x?Cm -vpuVY o uj2X, %*e╟Lg *x[ LrI0u̍gyn\LNd.YŘ7:/~#worLCexs \ דIE qƗ$Z[:;;}gqew? H<)Z~#Ĝwc-'=mWGCF,P:ֲe5ߖg7j7qTmBm[j8&!`tTJ'L6܉Rnԃ깟e%wR0g-1HH++E=%PT`@I<޵q]c/.7MJ$%zY#۩;Bj'QH4M[@#h(PG}h&m:q`$vlY)SORDr}??f̝Kʖm=|'>v;Jq' s)L(ȵ4fֺEgUsvŨ 0j=OqLl<*OݘpRN%-C0 X(( Xcg9Vx嵓ܧS+Q!ឬI$ 0{2 j Ł=hkG/>OMSV{l`@6ޕMMrkSLfG6B}0jR8#hIj\v1)]syȋ VYs{Hp˖X‚=LW-|!r9/4.SK{g{-!|6,iD&dou3?qvw z{{ ˞|/1?㿲RG c0>p#W$7[ިd+3ԽZj:N.qa̼4*;Eqj6*sr3;T$o׹'p1\ n#$WR.W, MH  d2hp8l"Z+x;|04p OkT\*^|ړD!PA,9}GǶOrppǬZw1Tk+;]9W.v.]m0p [Bg3U~;_QҨJp9cm4'8/$؞N 9¨NM:xM0( j JϠ*Å2ZVsb2۰7]C@0{A&j J)|>\WTԶ/R,f?eқy(?B!vX zx)Wr6%+.ݮb;^aU'PItL."&D%&\`Yff-Y`.Gz3o^n r諪|=,C')P+U~|uAF:'0MŶ+PT-瞼ra#a$vN~n MBxW 1o^>YFD{3#EE bm`.P#(3Wkp}1"."I"{6k9O)dp&_֘ ~NL8fX-+L~=Bh)8z4aaT*jfDF?ncәG/b"M%(4M$Ĩ% F+ɥ374slKdƞ}g.Zʕ=G^x(\&1Z$0HxBŘؕ,3x_/+5sykjc!\ FK&wÙg'^43 ^Sͨr4VF7ssŌ sѬRj{oϼs ,Nre[]\k/N惮2u"#+^xnrļ.vs^>gƗ'vО1FwZ y l!/{+d1N|}KXT+X $o¸)"D/ɛ>RJƼM_g#o;wzǻ&I+iShX$PRUj N̄_Je_L[ B>&x-JQV 9Ka1IR(W/-=>w?9ӏ}/ccoVZ1_$` Fl-a9l ^IEC(@BF!0T{$O&RH"}c|~|>[/@^TCPZ׶V5 d8 cIDAT{ q< gsidpHԊ >)2&Oɳs6]A >#kx&]1G1.湬
/OM=[ = ̧]ڽs ]Hafh_GD)y!L^z fRڡ).@bR#"DχQj(O~\X0 ӫ]h۫yuרjՄJly:b-B9?rcL,EUWf."/CBU_knp1FChnzԑf62fϿ'25ȷoNllbk;%mߙ jNNLC{#aJ<&y !]GZCVux"f)B/1 qL`Gne.*̇D,QXڒ:6Pm3녍W$(s2 -@h1{>rήf޷UVagpt2< uBU,kbbC@ ;v T#K?{o''.!60@G?|?>*YwS %g.B_0CVC^ »qK ^<0/Ɔ8@h0t@"o7/!duG՝Hü?3'/a`BRAXXqӸl2)JCh!fM!NQ!pPо(rR^5Za*QU#ŏF^gV_7ơb5<.j{bZ(Gk}p .e03xŤm.x@z-Nrs]6No=^$BHɋjQfշ)PpB G^~ RTՀ;mK黟 VdS{Pd.?}[`onqwϟ{Oϟuڳ6&Ѽ:Dqn>(z a4QR",\ꄏ6O$- NP<ʧԹ^"l# Z|MA6$(iZF3'/b#r6`'1{v/}+`-2 uK]7Ov!4$ᘞ~SO}UK8W}s>w/.c&Ur:wwPjYg_ #Q}>(7a,]{s!$r4huk׻*U {^#pCSq#"R*AX^}ewO!ۋb_ TjֵQb~/ω6"^jwxqR%4x3 ]~RΡkΝ}f·)ٹ}mx ȗZo%(du;eB=^QE4e5-^&d{..26Baaz^i;99]η̈́Z{+C+7Oܸ?Rl&[[@"omTZRT\EUF]m$[Vh4,= BO, o6*ܑY`nRV74Nlټ@R޵=&?8roj1<7}b;&vn5Iڱ5w5w5')L׍)'QP1`oxp2[z>`XY2YRc5H1&w~[n( b166>@׼g^ͦJiUbn ͧ޼ w#Bܲ |@p. !)f(#Jo +ġb]QXX#D1' X,<:6vbtlkc;^>KO|=漜΢kINèձjjwzmejLzuGzmugXphfi;IDR!`e&RmB |]1[+mI'14 80dbtwoڈwA31I'~0t.P\:wI|~?yupx虡N FVKـăݺǣ7J|ntzu*Mgk;ٵLfm{.Z'[mZ`\3rDVC.ɽGm Qֆ10tx@m@1|~(>2#TUA(ש3W13s7"^bh<)aTh8Q<:Czx0.x0" Q)ܣ9!ϯV=}}s  ]XwWc`Ap#\fgsB>7e @PX7´tDaP.)q2yd\ rAls4SyQlLLʳb9:10ӨTC@.콙H$Wz-${z{{]O]ǷZ~Za ިBO(bX,l+r[(KbZ \Zhºb`m{6PH>B8Hۡy'GԔjo+)Ƒ2i`FZ JXHk٪khRw}URl6b!ge(eJ3Z^)z=hЖVQ+Rn2V&[ Bksa.[SP(% 5cH(Ǎ&ޓ\Q>_-AK$zRhzwE׵z4ȄÑ\ *G#ZkIC2[>xv&4Fs=Pg G]5P}2( G#֝~[`: l: l:-:w?ȱ2IENDB`./oar-2.5.2/misc/fault_tolerance/HAdocumentation/schema/oar_logo.png0000644000175000017500000013401711757171206023522 0ustar plbplbPNG  IHDRB̯gAMA7bKGD pHYsHHFk>IDATxwy%~T "ջdU[$w;;=)dI6l6ӼNqb-WI%NRDX_/hSソ?`#EO ss>r}ЊVoPNhxG [ъVhE+Voha+Zъ}h>Z@؊Vm- lE+ZxG [ъVhE+Voha+Zъ}h{Zъ3#88(ᜪ>Uƙ9B( 3tR_wQ8rw')Z@؊pp~%56>`ddh≉rU.JrW}HɕʾYZv2P4>\?(ԣzcV6[LYX.[ik+<}TVW> =Z@؊@pTW:xpG>|l9>|> `nCwYtI0u Bv"gxϲy~*V06Q$qQ38| ͷwxieDh&Ϭ -VmA9w+v^/yclK]<0c.,]Ў=,XP<28(]_qEk4)e(bA ^ڱ:ޱdҧVYĪs>j}Z7Z@؊3>8g9쥗]c׫7?4|5E03Xhw:Xԁ%ې2:K^ܒSkSߺ٦`9?.M":^um΋ޥ[v}||t[gdPivogzCC7mkEdrm B9ۑƧ>yՌ'e֖39ېK);74;y7뱡6*;g^S/`rM/s/Q.toha+Θv•=_y=zͷCT1EvE)tCSS!@;3_y eכam8s`<?ψnօ^l{tFohKZqcppǞ|_xՏ\ُ%*ePUs0P40U(Updx zNB" ?'Y:+W>|~;}w|Ӆ 7s7]~[ފ[qZ" OCk{S3<`Z862$fl}TKXd>'7Eɐ'+| ~M~fsPʰu1|GmC7W_qͿ.>·T2VQ'<'|n@]ͯƲk830s\  1.AƧ(ʌ 9 !6|ҍ ٯ?scPJdD| !4篝~ů2+/7\zhoha+ޔ?wC~r#/lY 8jLEUUt+b 3(P ] DR݊"8uRpR$zd1{;u MyEA.uBUU( ( Lӌ-:>Ǚ|,|ݛ6n9x7~ww]yƿ뿥)tg[ƭ8Q*-vGڬeY8PRV(<5 3`v.a>|뺰mCUT@yLD:(QQEW=~l|fb8K)E(}a ADBF!0 XT*0icѾ4Ùa7e /ė^۷clԕ_唙OD [qJ¶=x<:uf K)ޖ`e 0t<5T >< Z`O/CUU4!6N#NCӴE#0zݭ߾a72gZ8`u]8uA) ɠ e0h/4ks0A|-Kt_\y_438߇3=Z@؊3s^Ja6\"J-.h) Nу DgV&%pΠ)T W}iZCpx#~G/¾ 'R uQ,155J)<!i""2 hHxr|-ܳ6]yBI'-^?{?+\0xCqr V&]MI±:Aϩ­N.2q niz3݆tٶP@UvdO`1VՕ`n(k,|Vl뗏}GZE\FTBX(BP@6˚m;^ϧ}]qZ=n˗nxMbV<ۼϿ|:{ @dTeƑ-t}c(MéL.c9e mG:ߍ\{/.UMr ]70'TgW~?~Y|&\%Ͳ P,avtoP(@A6m/9Ƌ?{Mi&^|>ƭxCs}_ZLaV4|DQ t:o?;/__pwi='Aj?u׃mWͶ*T1F"lvd[L6U /ȐL=59麊1 )(l;iw2B;NoTo|"ܯj1J%A|)~Ç^w ҏ>t{~g$~5he8?6+߹UTOaW+0R8:z vqg#)g`:aqs0R9pƧea xS.ߴ֜m4{M 8 NTI -]Іz~׿w,ibN%=|*Q0t]H$\ru=Tc2?׭yůۛ- lŬ~]̞9GT=M(`arG󖣳w)˂z(J yQC(I(t&W gvyBTU @x|ne !(/}Amr0!}tT*8H__g AZFEODdi]#w*LuR1, G&*oo&qoZ{N͌и3F.e|(yw@PL p*O`+znBw 059 Am()ͬ¬sp*` <)8qv `BhkkN83L~4,Y)e ۧ-_^fI ۶199 ۶8NUR)d2d2e Hn1(UUsT044 "3rm?~lUU;fD+#lEӘ*t׿udźqul ?t[BG74MecDeZG& #$RGA*1s{< 2q݄ |.Vn@ Riw=7 ] ћ Bs>Fj5IwzzzP(`YV4gR`hhR̟??*#/Ŀ}oUwc/- lE(UG{?t: 92\٤x>aE7_zi?`>1z*ggJƭx痾uCL ]Ch@xʥ"2sAAQuu*KcFad;혇%2mHYh^̹$F %!Hgr=ڔo7o6;BCX91EjX+ J#pBWW2̴bl:.\p1TU,]+uoutweM`[a+XoqTNɡ};зhUC@9|?VC4~24B:ߍT\;p1Q*:Lӈ\1rqf ɡ3%'K\L]| : tDJbR rjammmɅ,*=\.mqttoWOwL"Sxy SC zPT 4! j#Ʌ4NeØ Ű`PZBGR,4U /B($A¹ (GaZyXԴ>rc t}o"x/s&|\s"CT*dd`i8ƶmP( eeO_;ϽC'vFEkh @@][w}G--\*Bt֔#T@qb#N1L \y0L3j)%^ScLA!RC _.5' ٹ=|}Xㄋ%'3qB"U… FFFp- lڵs։c/]RS N" )bފQha f|@8|@ #}a"[l!:!R X͓')(*D^ ٚy* -G{·u0 rnvGGG4MeY BEQގBy04e|rumV[&3la+гOuC*r.Tx剣P5X|P*8R0&8]a塐R`eMac<N D1w+2 )+hbwkȪ_$9YYx1$)ûgkV,MKu]AI0ΨLΥ(3WـJ8}izzz"d'|>l6,+~?xc8Ϸ%oغ[> iF YY( R^DeAUۅaQAjq, jTeN\;8|Aw2SpP44p4t3 ]2m=2,ެ9߃*>qۆg+xjj2=Yj |KuRF@2<QP0STTV&C .#µpKc*R.XnX9 t#MUmeChP.(=џ ʺfٖމ|.6Yx1u6jJie&Ӄ6XU7?Q9166EQ_'#q_}Btnޕ_o~_OuY ̮L©Lwrm] JHt3#0((| DiiE*2)Kt]{!QT)rppTe]S(;m_ًkB, el<mmۑ2L1d2`جr-.;::.z{{#@Lwuwgxo(V:oR=C2D rT*%ffÅ1q]]]=5 Zd:桽ormH5-| hf!RjH@E'!o4?| Ɔdz;]|Y4W9RI9r$%A>4v*¢E@) =tvv=e;/[{l7Z@6 qF !8 dۺ7 DQS&2/pƠjGXu*SL 8zNi1(t/Da\Q&4Q2pnJ5T$:q0AmfDg!ڰ!23\Eʼnr7 MMMX,رc|>e 1mukRCajj ǒm\/pYWvgmsq>kDZkEi 2./^2P&(F EB( wQ-:y NqB{W]L.GT(\=?6؄@xb>|܍Hg4 tyK pޚ1O夋\tttao#*iXx1(WmlƝS͝5-C[@6='yK),\ EQTK.l(ð9e1omt Q ͗-DѠʴ E9qb#uQs0J(J:N>Q*Nj򄅄oav]cz},$/=spX-ٌ1VOeR)R)sRAPU5rCF$! ͽعؚ?{?(o- |ǡ=>[#'p=ŅQa058L+||{aZEm{aٙ=UҰ$X./.1NuD8 @6 hSuٰK`4h|v[v.T*㊏8)qH9EQ"8R 86qlZ8Z@0l[JiN#,LѬ+!BB!_Ǿ},hpap?w2?#~e~t_sbOnyK 8@CQE9..­NAUu g!L3% dZTE2UkJrG8@@Z <8E3Cs x@A300ǁH7]#~{MLs3|ݲ,̛7̓yþ}@)6',[WW۞[fE?nvjpAQ *&AAA7RPU P4̂B)((]c B3Dq"P{6GgxUP CQTVF*LC7- UAhZ Sog]]]5kc.UEQp{P=K:i?'٧&Er9Sl:JEM|oJ1 .r͛~h^Ѳ,DCo/Z߼oo l2L$Oj-/-wtz^c쒐-X+;J)F Qs0@f3@dvKpE au+ЬLL+||y!ZU(EUDg*㺾5*FN5sٸ{sYFGiz3ز{7mن(<ϋ,8QR5vH$rp]Q,5x~~毮X3(Z@Re߼s@zNթa0 @MD [9૑zSiE B+VaP=NF)TMnCcl[t*R,br]^ !U6(BƠi4@ϟddB.ٰo= Nt9dٺ(bgi"rHq>l#^upαx5U.| V=?S- |8]QTƏB4;AE ADhB@侸ۊS2hwD9߭QDQjl;![iZaeYkjtQ^9JJ),-jJYduk;)L O0xFiY4)ɓ}N&&&"n2iA2Wtvv3چy"eQ;6VtXPY5b'n܁"8R:` " DQkې9>xbnϭN[jʴ!SE:+;NX Ԍqrq \υڼZV9sd&84mز{wJqn$%M#FuavD4ͨI*q>G.c,:gY\O~%n}_w5s(·P8n)կ|ӥ!)t[U ^@8@fl>J~%3A~SaHz3.YuB Ajq me / v!N0aBuh^lDt.4s]<6 D|>E T*E|~mmu+Jmt wYN !Z@}21RYZ^BԐ."[{ EqS_N³4T|'a!0 BjcXu:irJY-2ȴ~&!sP ۂ LOmR qZuHBu#ST=q`&E߿wL60(rۧ:ۍ%-XGsGx~']R\\}}}xW/~'=h[$_]3S804hEaU3UxNnygCSH:нh+p.&`ZyIv扁 ױrF~2# 9BaTQ-"l`bbW*"}itWx۶ R-#jt:QX2\~(Vd2,+:[\"r['گDRm\3 DXD{{;d /yD|:l]ϞKa-ux*@f]Uk *AЃLXQrRp0a`4-spV9rFŜ$@dPxNc +0w4sW&Vx T8Ibi0M(}?"0'HZmۑ!lTH{}UUAU؁sW/T*Ex$GӴ"2x̀l%\lEo !$*HuT> ]#GkytJG޵ zp۟mX5MS)L+GF ʾ-W[9'aZ`"fHe .ZYOҮ_TfT2#%B!=Z$A5Tf*Uס* t BIj*,H=5 'c(+˘:, q3ty0MNsA\.G buE:  @gg't4m?ގ'_< RxT*#S}םϽs .ݸ lZs, ,1߿a Q>MU+K?7)03Z@x /=v۞݃ 4 0zyBUcjF*-t,sy!0 RLMM_c hoo,W#]_3N³8 o}k_eln"я [l* XuD]  g9??@i.2y,\h4 +l{?%2y qQIVcm.O![^p#.qp뭺NEV݊#cs~\ITN,AD*ůDmȴ!iAS`C9`Y䋇1Qt'z),SkmC)1=}G/XwSEĵgq<[Fu FQ@Co@EQ7,& $DwVKB5PU\EtC +]JNZ\ 10 |&Gae;`i-4>ȵvģRLLjбnE7q W?pR}l1_ ?{N/-:>)} ro|dGp=_WaYhXSU蚅tBgWu`=2ITF1qUngb`2Q89SʅYP_ݼhYs{sNpH~SF`h +\GtNdzEӠ2aEٱذ h5HK)Α`HbXAImhs=A䮦jjy^|$2J\.!QV1$XŇBp΢rW_ Mӌ>YIE3V/Qz50>mRT+% R)XV m]_~ zkfhY#V| ɂ3S p E3aX9XlXP#iN®a.lI.'8Jh0 #OT]sQa}.xe8eY)8 "xYF(+ҙE(*!HY,kC /_FFFߏH(eղ>Sͦ߱wjY>|o~uJ`E72H0-LPơjj"RP͉u}#Qe2B$YCU5bPWiFi8 @Op Yܱ?~`Nx#³,y1#ө/*TU *4)5 N:i%7YgvQ ߭n(wCLhfB7LT&3¡&t#UrHnqq<aa Oř3HsYl\~~>\4Z~&PMn/wlUZT011=0nUdQs;COej!:J񣪂P1(},IҞK:,=#7gQpp2k;c;_qn*ՓQ- ?&6: 7eIZ24!> c0 22Hg"x8>|}ttPV199 #GYu=nloW9(M}3?%q!`.ks7UfAMFM&_nʆ_~%t F(\•&%CW:`( ͆ru7p-#aH>b qlYWVYiӶs(!DFv24UE-kVvJ|- .! A=$0 $UE.2DLX>Ѕ+.XMS> yG rۣ&@M v=a~T*}Z*KMp'{J)2,˪$'qv:+N|AQ@aɪ͢cZD<-[^m/>.g*! 8bUfJ ̞sd\x$ G}pu!׭­NBPRXB">tTA#2?Y$\ BK5I @ `PT`ڛzb,(U^m׬>'ݨeV Rk]T044%hҍ{Ó(廡pńH<n2kV̀:qd$ZU3af3ĉn)m?[@xK/m6u04APFAT] {P{3P .jr^PenzZc˜3T5= E)QaFCntVv<}rlNV'bR&`s1 l$tN6s51:i=p7m[%nߏy]' IRb/\勺~S:Gۭ P ey4J9|?qlEږ>q\+=hSk玫"p`,2pN@C}0Wh8<DeXQuUnaЌc\(RPj4"))~@5 8]B8.8cAUbyݰJ dFϟ(,]h;nj)ͫyu)mۘh= e,er~s:oJEQj(Fl-7@3<Z"" UM1t3QFGν)D( jz0MK,OCJs"SCJLMc8dꘑ ;+F[iXX@ !;5A]-V14^A_gno.H2EШ.t﯁{tjb1Jl6{ÇЍ44̂ѣ)4?4+T*Y]m.Nds A@6p&42@7P-0-+1ĖzfZ 2p*$ќ4emކ/qxh%&iۘd@';<) xﵫN~HY}4:::nU8|@X1\L8xK3<>tfƖ0}#!BLы`<j @ "1HHQT H 8I;@d{DSc` k9Xځj#,?9G;ݪk\j u6@:F'oIUfM/nINRM(jeYuv0"]c+syN]Ɏ74*y>R`̐]ݫ0sؕIƎ2~xKo^·sT‘c9*UpNBE@RHu9&39HԊ4oyU'!/cS~zV}1J#;~)sJ0|8i*, <v$pnڑ/3nW54jT4N#ŀ$99"LSȿ9/J!C/=-/.ގH"mF*Sf1!!+n=T3P+FGW2o~ (T 6N!/-4.ٞdqv6e =)O(ѐ><jUpVD7.[9bO,v+Z-FJdSЭ4Ê":;sX`9UU#~|cu:F`"8ɻG<0O%Rmنt~8p'R`&, TjZX3=P?MÉ\2U?Q0Fq8`W,@Ўf g:)ﵽG um~<"v!CdX(>h!F(,)@HnDQXASЌ44#Ud\ FUafz̆_q`.ZCw`TbO:ދXRHf 9;!?|e\|8J.7+iTȹ7HT*^ u]XL&%he$PfǷщL|$F?1K{Z@Cf0g "E+#A'BT( j>9\&2?BUjYvِ0PP\GȤ (V<wB:<[ܪae<` ~@(&M[s6k\Aaro.q<쓹\2|劇\T yPGP4hF]}(/OAq m|] QCKNuD Abr=}*ͼ6Lby͌U]p eaF)U¡ODsjfl/ր0FT&jfDG"sZ><cƀ]p6(!pUUhqJeI c riNk:IxiiZu"`qO^;8EmptijBH(25Z!:@VqB1U,v*"g )ñJ,cpp0Ju$XHmMU%qPT2d1 4Iet!=0Knt=-TH c,Ƨ>GLN:BƂ9vdfMLRW^;ܽ U3h**|*1MӣG*1s0c6ϧu^_.7dk&B_ۏǶ’m'E"gH LwX 8xbԗ*X·F}0 p*A33ᾆ#UbYQx6\' !{N;z34\!*}PNj(◓R EU:ߠ0Q6GH Bp( >  ?SUjh\  +JC)C D4DdjԸA>@DH=𢓙 57F‚lßˣwvFM t2eu=Q|#X|}yA4LYs<.:9S-iũbh j` @*Bl=)b#802e /RI `.X܇.=,R)zPbxÃE98}ŮIﰰCNT᎑xٵgNW Wۑ9is90UK{4t?YFuלsnsCېu4h #>*A@eMsL@b3w~ Ƞ.fbc/׾ܵJ/'Y T2qeذj֯B. BP(=$wK7TX)o.֝ㅝǰe0JU/F~Xȧ=xbˮ!\bhzJu-W=鰓_}} TUk&kqK0MĶS*i㘌^Hp#m0d5P>G,βu8RfF-4Eqe( ;'W %OjLhFW/d|}ذɉ/X?;TnU'ݏ-9c!ͲV}$d֖ K":96)hq.\-wGR!$+!Dp #aJ4MiR lQ'B(Ƣr(>ה4%xA) $mxb>09% QB|Qz@@Ne[ >hh*t>|亄~w׭/"":F]m0 ^90UKg:Y8's8usr g 6'/9tdC`UTа")E88nn2 }%ͲhlDgr8N_'ף( weg HNd 砾 W0p;ر}Mz-6&ɝkHRM$ zgГq͆|DבKFA._WRYT{zȦR E? }uPN=sɆd|+q o='8ePz69\V;(TV-lRZ7Mh=Rezl+tY Myqp}~<^q&td?HNPxn%4eP"εXs>xe8^{Fyu_b?W Ϗn^Ww+`em3y&!@Us:q@'~ 84TĖ]xvQ8Vp]a.k*`E 0Mݓs`t C*籗`2Ρ*/~\| OZ8?s3h#go+WM(CYarŠsǷ}oϡĮcXĔe', ~5 闏ǰs(TKc4ty19=r*g`ZUubDb2+$^Gd߄͏qkD(l iOCF#L͋Wf8\RBq ^P?<>!7-/e]' gCBӑ\w_%/£[^dz[@sB1z3@ \gFL i/&J#z*n~]I_O.+30:ic 1YrQH+\iU!݈');uröCpݼ~_~ӏoU=`=o p(U. G̦h5>Z@ŷ ƍ`9]k-+2L5&Ndb BD0kۈC~_}LƲ*.ȭK΁׆l9{_gh!Ds*D4T.HN̴|LcEǶ…M[YO*SȠK{Gpʞ=el+q`y;7C |Whuq]a$Wǜcn|=A6J9,#No8˲dMa9Z(QF:լ1p`p[`E_zFyxGe.hU"z ^ %0-O<|>_gZ,U*(A=/{5LSx|uOZ'CJsWq#۰fIiM\ LiQ(N@,a<9Q86_Z+vrQ*JcXx34r"2tH3PH7 [zJb2~匆$0~2ƠzZ"dFٱw(N03PT$ɦK?lرӟ|ދMkE[ETY() E. hH s%vui{^k!^ʷ:pk~I̠U<vp庼0|)K&>~I~FU_ٶ@]7)-#)7:g8TMDZCaÆSvgldGJ<.BwBzeȥ+2fH|,$P_3z)f%RDj(ش\XTMWozz6j`vvi* ,QD"6m.Z9l{ͬ%ehشn !uY2FJ6Ir//7(4l_?T2.,\2oh֋ D hooŧZvhQ.ټ#>7x%9z3L3L+TA1Ɛ(DQi,eH3Taz+XxrFiyt^Ĕ} Pؕ"UOz϶o=q}n  \8|U[v6(g.Ex6DEGC䐿2'|7f#Vc Ų `ZYWghtu^GI&!(a]#L3Hg{|v}Rog`4y3F$K:(Wh-U/c\X *LH})xtd=#ǽs68FŪ(Sh]=PR,fWxeL⿶s;XT f,H%"MVYX.|=\wׄCɂ![v|ۗo=4mެ8Q)ѝ.( l<w`4=]sAg*1<<݆099r ۶8?Ƈ/>ZALj5Xn?|X:R6?Zt}N6!h<<شis|]xiϰȲᾑ%7nãHg!%MBXrF.=ʍ&]5W_B!_^/|AdoiZD N#^qr84ϵ!zxP"FE@&9l821}๗74,xjfl3/,`+Chsg09眺DJHyQJQV#FUpXtQf 2B̶}W5fJcQ[9'=οB MmX>-/+ r*#?I)(_?|fkF$o<<կxR hDn17@wGᥓ}m%gp ,Y="1;.±l,Ԣ5"=_ L) Fxz(D">LAf 췢A>`㚁h2 6,üy{OVslGk0%왏B*[Նky|ap2~/}w+ >=4^uBK tB  j%ɡoqzk4oca_,АQj8(yѠFfoX+j^s,>m_NB. LNbrq>{`uQp& 2S s44 6>}>g_>쇵OSi/gx\):h…@!BX088cǎahh###$*\׍3 %<+l|r$LCJQz~!KNQR\INaBlqCfE`:'>1|fϜsHCUiA2vk#ׄɼk9} 𚜜ѣG̖ػi<'93>VZ?2bBtvvF;8lۆmu٠[ZOYt:y J., HDz)^8i4Y7] ?}xk|XA rl^AõWkm3Z:,-/+=Ս F闎˖!?qntJ?),198](+&4?~1|;*h7_홨?99$a:%]zt=%=rNh,Z@xNJsV?} 2suY$"Nq!YmuJ St]H򱪪4qUUb Nm{@(g, Q#dj"B.fW/o~ibfݱFMc[KRK.ebyx#ųLK@\]}up.*8$ȇ کpKcP +qlg5l^bl\1rWV?xr.$G:2}F"Z@x+_i- I,K+%3NJk#?A 2*ScEf] C"j9'Eޫ`xe~#4ԁH28=h&5\kZ5]A#c1OlK7]lۚnCSK7lI|[GQ'x1p,Y Mm<7 k(Vm\{S&Z@x!*_౏I7j#d'y2dǺfiڍ3E6$Hony?o QA8ȦT| xeΨWp]|>HUE dj5Z&I[.J"ϾF\<۶AA4A8e xP?]>ZHuf1ӊSr.T*MqwK2kamt-?$mR)oK{ď0롛<2߆,:Wm{`^/nd)hL=@رoc¾YY ςذяI\SY%9ɡd&* ,Aa;hΛ7\{)}q?^wSjl,E..E2GTax,uIT*f_qh^Y&eu$xRK N}(f(/Ų_[$p BgYV]'\6\p:zw\rN#~2KN[e^Lmx^lKc\ؓJS l&xUe0cto߻?xp7.87] }g{F@TF-ͺZ@x =0pEWHzQV46' qQ(oQ!/Ǖ!Kx>e >N>/K!YAYSE^I"'>5 "Er}I&Y^Lw:Xrj=Y! ,2?_bjzҥ3[*ym=Qz*B86[7Z,gQkor"F*x4" 2F<$e:CF9EyJ P 2a(xcbBӴ:sp iH h[a\E@ZA7\B:p|织pѪlێ499T<tM@B"|>1 |rq+>9|fy9E㱭GY<;vI``(#>MB@ h-RDN,6P|p+kY$³,]_W*z{rpR)0]af-_/|c#I@x$~/#n!;^n᭢(xz>/=ׯAj\Ϫq$}?"j1Rp0|a"A-;4Ndĥ"u# ~I{-WEP_@x9}5ƛ >Րn uDs.h̄j*ʞ)íN:9(䉚[w4^8U ϲZxxu{U+# 9T*J 2% txf\G zc"oT] "e5 klJFFsc\ŲjD&8*ƕ.b^ f B6.?YLmOVs԰iU?z,#D \w10JVEn][](#ae9o9f_wzJNg]p{[B+/h00 R%xAu/9,}(Y~=s!ULJDhʀ'_x˜6Tdijf3"sűˆFb>L9vqY\ U+V4ʿ?f+]q߲ǜ|ON8S-#]A"|:(*L;ҹ+ JH (tUsOv?w\IqI{?۱U+f)O&8":L*i< =1t\rqn&.:o=E- < CUuz-wSܦF$f99l^.#;eY:ňFF`8ۜ܏fkyL?~p[Th$[\F /i2 "saW90p{ۉ_e ?276.?2z:rMkݐc~M"vrv! !H:m녕)0Sƒ0<J\ D4\p}(LMC~tId,koKw|+~Hf^_)QLU!)qkf/ J%bKJQ&DI6Kjr_ߍ2Wŝ*Fq3=q#0nĵK-"buX4 <,vpY=`~<2~}CԆۚiǣn4}2|GZiNݶmTը8TU>K˲p-IK  !Ӥ12jYtC&尚.i5[W_OqΙgicW]wrOF| ͚ieQWv]EZDzq%4@5 #2MgqH D)f?(B9mhHQFCFCfqL cHRTUAG>B>|m9!%v{et<|?W޲l8\׍ReNy& do߿KEX\=0$AwaXY\ҕF"kHgq6YFg-˪#7"I7˸' b"4:;;ـI_ Iq{YYM$Xƕ(lfQCL'%!i#ʿ7m­X`5XhgRm7<D#} c mOVR:::""k)'90ukéh Y@ ЈAUEpETƏLBt­SohY=K\z͕_?kr;l4'UbPi6шhL(,$ 7J4i9G))@wgf*L:^{-8P(хjTr%Ѳ,B>Ejq(mThbjj*W叄yyr=뢽=饌? p @|n s{.t!Ba.AwKcpJ`ԇfJ9o-+⛾|#g}烿O{_ߐ+&3C@'dE^OhYfK]74D`pقBYKM$Ȓ2&ɿe&hF4%1 )[!'{[;m{5:Pck1OW8nʴ!ُjQ/68T*7^7Nk8- | +^{M7|;_# "n*CGMMME4I4 \.ܚ-kLabW$,}G"@F}!U(XfF'knqHRtfב|=Yiٞ۹%o>ۯ]3l/qh2j4Mx^,U҂;({JE(%. 3~oÛ֪s.·H|/_B[!/]3,f\8:D>vR)$eGmIFu|<}>*JbIBXTh4Le" ƏY&̠^t<ʹFr^& ۙ']q$bT]Fus_SAhV] dCyTA}>7_ff_އVlgemK;qQz{ px᝼o%ѺP(P(DnkT*ajj (Q%8~ 2x}2$PwWQQpp(|6߱)z_|;8F5y 6n{mD#eZ kGGGQ,199a add$**YB!1u3Ơ yno-wЍPEybS{6nٴ;NݕpbBq>ޏ/{=hu 1:: UUd"YLFeƤc b1jw>VB!"Ȣ c?}%| @Q@$76mx*I`o١F*FѬzh͢q\뺑yXoⳟVW#hkkC&Agg',˪+391YYKGd#jE໐&ekéL)>Rzk7:ŗ E BJe_ O|\{&8N5|><_c#/nY~P4Cx)* 814N6j4R3I3^2-3lc((JuN"IAmBd`v꺑9z8ŏ3Əpv!|jwx> PEq809u/JnԔL_'- |Ņxsn|9O=]lC\TUE:dbud#?ٜbV2I@\u_1Kyz~* @0S bqt۷⵾K`ãxoDw ݌xD_)oI4Szͦ0GKWš#0z! ĵDȨX.$><-3@J"_8fu Ϯ1VgqPF9g#F*cD go9.\?}q~Kr}qJYTi2XV q ({Gnߟm:o_؟~߾?(o_6"\ypGN#y>d'L&˲"P`'P> w8U8(* {h,,'VAۉ+VT$q~ >7.kr-F d2C۔|̸$3Qgdqw8tlzr @eqEQX6ݷތ0?I|φn{oT*fE~>䣷پJ'`x}˰bY?h؝M^R~^7tF )9S*:dj 5^ec+G>džkY"BUHHQA8ePZ׸V8n*|-YOE$3ן|}.$kLsjxג~2.9wqcSzP\,rJ%C͛7lm]? BNsp+SXۛV_ݓ>5·hNW~yY)߸zq`a[Z,cFtI JյLNqU kQ'cDŽf5 $C4@E>(%:>XnvI0œ\4b7^#zN3fLͦ#w%.?~~ӀRrP*Բ,d2tttDF:ОPk6 GwuD~aK<7~`Lt!–YX?9~_bbzlf'Fӕ(Ej5k5DFVpb42"NP bBN L(IzP"PSyP32[TxJ{="T\.cb}WBu?<FMͷv>B?ߗ ġا>O{26A:NɩidžߍzSH$uGAA\r\ ˘9B`ͻM@7i $ =A!jךܮfhOb@Ӥ0- Ѱ"ycCf Jl޼%MBAf1zQ1:Ʀ-ې+V&W*s-oO?D5O{>)HHNT–HP.psR@Pb Ybq=fvM&2i0&{f2)3#: q @6Bզ~Wc'MUEPE6Wrl 31s.^y$>zڎ#첢rH&H$HrUdHte u.4M I"۱?A45$N={/v|/?{?+ *5i$( Uxb,+8v`! @ 0 0ljkrNЬەQqP;Umz~KsTRR!(Ujh&/i6Q] (e(UX\.bnnިH0O (zt?݈ǢJT* "EQHz z'cSXwnMީoVQR$I D+[X" 5O0u 8faseF겗sR"-syF=},h(T8~9ŶLeԩSfZ$l"Lʌ oIVi}SQJwg,XfF( vOjZ&M0 ^c+Qd@kRJinر_=x\kOĒ...T*!JUx gƝs 5]7?W3;x,wZ`0ΥxL 'il2E10 &?q:&J;t~{q*1Ļ{166&E. j5WZ,k[S.ף`rSQQQ͐S|#fqUP c˖-+z/1iS G]jXk- (2dj``E<:߾(7|֛wp!,|/=^.l_^͞ q'l2+: S1;6i$2 ILjC". .d(\=yoRLliGiw zI\# 4~Mm{׃d3ϡѴ#Y)PA\ͧ;㱧n:%Y!#L7D=eY8 |?'÷C`;D1;~SB KtUUtG5WMl߮|R C;2oGS{],zA4"HzW {|C>=^`U-jy!թ8t!M/M'g~^eYLK)יi96S_1%Q#Q8R O>}2= oQ_Ee4 tvv"LaހMmR-h t+!::sv=J έs|9!o_.s?M&3ņRj?G*rGuH-áY7\V"bR d8s/}~T+ě6\" 5BO|.#d@xy$:ET$Ee bWVj#'Dy_#&ob'g=dQ: K(IQYε0cq9jl4jT*+JJ'Æ`e#xyLVX^^cL :>}T+D">ovpˉ,(f)-u/L6slz)QAc*v>(F#bx 6iT܁`6[R]V^ ba B pa5k]?p͛G7NSB —(健܎5K5|3M#R^Tحl*/ٙF(ƅ0M@Q2v(t:QF zwP2O SHrX\MӔ%E։0qIHw+hVTj=P~S^Ot DC*݀fġtc._х9Q3lbpYlU-=jMo޷|q躎]7\έB! fwq܍ _qa®\1(eTL(u0Wy4 D#¹K}Ux@<ɄUKSj:(A~#I`#!V{I:C芹e:o^.GSC4 7oMpާ8D͕R< 7#Z98y%5}/2lT{``7x2vmc{':~6n|C},Dx\0;s˅7Ϟ;5bR۶z3Ŗ$M4]Q䗖]XtOkrcEQ2h@%J H##W\U}-::<͹ޛF%Mb~m[c0wZӸ=nDdcRX,RT*YeWs܇zMp~ghb1#7>1Ȇnܼ{Yb p=gygft~S|$Һ3sŎiǑd ׾>JN."UZG1HX[U2ۮЂk]oy,%ט8AB4Pb{$j#\:avY%^o`Sͮ-F,&LӄRWyۓ(zԙP, *@v"p}z)dpQrk㗰nijod#zP+WQ@-[aOx] C R_IɝjмfSԚ ﻼo3"ZqCjJ-)8v᧖쑫"mNRLg1RO<~y&@0B:C,drmbEQ![}'׏=M#?}ë0<N>" U\ct Wrp߾`ƪl(sx(:P)Q+ᰩP ܙ@Ωkp}c3I($寗KC"@$q'=Evsϛ Efʴ_Syދ"h]tE \sY3;<^B<(?J`1%lG&_]sMO}綉M?{kX:5vW '٩g+\KھB=wʛg` pʎK'FQ`5p*'vƣ 28Jq#ЪppX6D!cux$;VPob jYP^ )RM3{Q6̾&tΝLa k!x?ۨPR 9;t OF, MֈtHkv^ժ^EMĩL]^w|׿㓓jN"\CeJVt7kWΓ]4p{: hۡ;%ݝhꨕJ`H;q#R$egS+=FWWWK-iҫ*rso62N-s@T۫εɏ-Ur@MմLi*u"ABRO\#+q55(tѮb̉t== Q8^fF]V._|o䫿z }*gc{Μp(C8Tvv.?7Ds͇Yh|?3hPJj2XuIf؊3Erlm"^RӢ7XZύF%F(hW1B2Np"Ƞikqw#Jc/6fѨSxŵ\_PP@ReQ<;5reB:|ihP5pypY8l @qY9ڐ 3ds F޸{ I pU^w?ws;{~n~g<۸|s<6Ol!U kd*d2*O%T*4 Y%"DMD"DMDQw2Ki*JLU&=MOMU<@a>U*Жew_@i X? W=82zÆQl =3}.3kRi~… -n[uy)?YYQ?/&((ula'֕ Žd`"eL1oD*)qaP\ܾiH߅$רo6.x"OCOmUɩA^\ NղzO 3dki7 Bt=!4F>ۯ5M @to kM#4_{rtlc^?N_ !P/Abdrƍg{)eU#Xfaܦ2 2 hl/^e;ꀐyDf>_D>#Sa DCOo714ԋ4ҩ bQ;a;2c 3g RԨTb'2o2e[Ce2\­+VDw<8il GbogF;4400|kଡ8_ " pQ0fZQ/r#h><]p>(z,tٶQ>]VО{mmԇd@mQc^' c/ZHOyԠtF(/5yIO!>BxM>D(HO_mCOD=;;zzt;wt:;2U~Vl^oTbR B!W({KBwXbRlRMYLb9-/F-UjxyUBb5puiM9TyTq{!n3 cx1Jg:R"ZNwvͧRήtg\gg\2ٽ Y+XU4ͺA-S/{jjGRIf-}er٥JVg, KFir~hԫr2Ztʃ0FaH‹Rt{Dc~LXG"寑4j"ʁ5M; 0R8uv̅BF=\Hdx)FԵ+'ŌY`U2"&阽\cRJIYbh֢r1D:2 >Z.ma4Mc*D"z2ѝ_ p @a<"  G@X0@k`# yD @5a<"  G@X0@k`# yD @5a V`UA `mǛB"zTXtSoftwarexsLOJUMLO JML/Ԯ MIENDB`./oar-2.5.2/misc/fault_tolerance/HAdocumentation/schema/hb_gui-2nodes.png0000644000175000017500000033210011757171206024337 0ustar plbplbPNG  IHDRmNsBIT|dtEXtSoftwaregnome-screenshot> IDATxwxU߶lڦ@ I" El S7 6l(E( I%mcSId'dfΙ;9;whҒUB!B8%mC B!rzT&B!J/)B!B8/=H&B!J/9B!B8/zfmx 3j/w-} VF!B\"PRpO?8Ьyu΍O|ΪSx&Z'bT'qk4QG28քS%n*lJ 'B)ؙ;^Y |}ug.hշZ> #:* nhd ~%?=>iі6}8l>"$Lҟm/ao~ M\,w2ZZva}=3o%W(\m&Ut RH"E)R.R5KK&x|nh]5y-=ez>.w~mYx <"N۫do}SWZ I烻g~ tV?+΃+}ǟ.Ǘ}gXb[9E:8v^m)O.'gI1C0T*"?~~btۨ: sزk3ͺ [AM~}xp6mEr۞̢enot߽76J"E)RHT&ֶ&# 43V*>~:z)O?gή;xu&o״Yq?Ծwwe)\@؄vM#{b"3ޝƤhB#h`;ZwPӬ߿SbY9-&pk?,. mczjED kVNUm-KD-.i+w+io{Zg?40yi n1%`׺rS;ЈǞ’QUT~mB!|N[QW5q'4M=M2tYۨ Eu59[|ɭ)8οWuanAmSxe>3~m}'3ݤOG5ۯ~7}ϓCÀ=Qû]S,Cv))zBZa,oWUZ{%h}]Q|#6x6U&NNi";ƭNĸ,F!Bq)QҦ%>gN O?N5ڲ$t r:K*i+=;뵦pbbc$omx$W|W|:{~p {NmF-ܧBU MӠkHKO:X{{vp҉'sP}_EVA[jVߠ(B!.%Z[EԓkZX/49FFJ>քX?x%_49N ~cB.c7 mc: b40B)7_V ]-;.BrW~+~UUEtk|4- w|!7=nkqJMU]ݾ*ݎxvމ-9IWW&.!SO-R<ߜ͎:|H"E)R\2F&Ln*_uȍ0s7¯NrbJ}}7VuQATtAJOV=3H)Z!3;܅?xWx vk=h#a&&K-gԥ~d._%vr{)S}J Um.bb؟aь٣Vojo}UU45k]Vo}Hnwopj>‚qǺF)RH"E)JѣRܚ˟;pXFtF:F!BqI$8$B!BjxM!B!DC8!B!B'lB!B !B!+mB!BJB!B81=;:!B!lV&B!NJ~cB!BQ mC B!r !B!M!B!$mB!B$iB!B'&IB!B81IڄB!‰IV 67ødcY: !.H9^)lgGB!꓾& 䐝S2:TuvΝUFxΧYؐy^Vs**;vǗ`:$!. II?pJeh4K.!(iKNIT\ dgg[,;صv#f2ݻL6$lbW8+/__3Jp`^& ͎^k0.>xzO8(Ȉ&dd]Y֧MJJ 3+i'%5V'Y FBB9pv'aa2 En~B# =zrwANN#((XM!.U5Nfs' E'yyy:|OSaTYYYxjdUtZ?9|ߝNH#UG^ٓ۷ӵkzMڪ%2-kTIłZ砹dfx̄^/N"V^ &>1Lrrs=9pn͍fM#!9̬mNbrb9^ &9%tA] .f3ɕQ{<}?.a};RvkxntgҥL&; !%FI/v{˄h4V9sAAYyݺV[Mcn(@ ow7ZEKhbٛˉCbJ)kUGlTy9*" $(Lf4}/iiNKm+:mY$ܼ<j9rh]m$ɐ`N~HzFChpH=DTL #G㋯%ϹZ}]nt;'N`Ȑ!$%%O```v !pU9zwwH=O:wgb_5Zvt 3czu#hPUwW~7ii_Cu-;dӈu>@3pwY(+6Y퓓KVvVrm$I?222&Wݣ^JZzګ>fvN$gh4N:ETTa !h ՞bRȬ,oh(5-oao#LvAbb".V"NDM@Nxzxڬj؍41f%7//34Z RQj$u:d>Pr! |hVoj!'$t7M>}˻| 8[`РA4nVhBQv[+g!~p{KۓWLf7:\oΓMߝߏ,5uz]AH:}G[<'I>ޤ&-3_߳zmpPIN#80 x=$=-#EQ}R*|0{Us$I'^Jr#z=ddTΠ`,3_P7Pv9ZiU=l,6 4n~}>4:t|[ :4Vݛ4iR?( !0T=䁰[`0N~~֭ o]@.{U<_m[.xd|߰׷6hЪTiD)u7}XH(a 3NFfF]-KUqV&8 @\ = B}(zpg~?T>Ghє{vU8ˈѸq9rh'-=Aغs3#00b9emQC!IUd#DV2_FF&II痛WfU::̸7+,^*hU ɛ5hdӦ,X'NΜ+ /RSILI"4brQc8v:Vۙ<=@ !A!r[!ąn ɭt+U\p5|n<ᒞi`aaa 4ʺ1  FKAxh(yyNM%?l6s21ܼ\ILI"#mJSÓLNqefgFNxcX8xHA.Njaȋ$N&5#FxS!2'S@qձ=#"6=k0~1+cXz-x<1<$ACX,ުH7mf@~L~ླG@LT3:@^~~ $&']n}e^Gw`Zњ<6r\nON(HN; #F^l6IxtW^s]f3M+_rرJ?(i.huZV+:ltz=>>QfSh4(A'N`R\5;kќOOM#юʯfeeosG-^Olt I tz8F z=Ai\EWDBoЂn͎jή ϟ fȋHD Ujl/`3W?Ҥnz%6ֽ YCb'AJɵU$:F , z=!ddfɄSW؊tzL&"+__Bqsu\}z1 j ti1bL# 5?$=->{G"4$WW Y!D8-8(8q$5=\7Ri3ϡ۱[*}+<F#=Nʂ&ֵ9s> S2 ;zzzCBr"fp Ѵh(BVis뇗ɫ^L<=<kۅJqWDȳEB8gV>>x*+Byhƍ-;u@ ~)]uw.#/?$22ݽN- Iդ}Νa pgd2a2=B!Ҥ$3}*;)Ly[^ hVI!??̒\z{^p1CCB!.*t&B!;mC B!r !B!M!B!$mB!B$iB!B'&IB!B81IڄB!‰wmcB!BQ }nn66B!h4^,]Blt9B!qu7|J.!DM>&z4dLB! GC @$B!B%U. !B!*I$gB!BS !غ>`8B8ct'&!8OG!B!XrMqcB!.1pt4d܂dEa=my9ӤH"JiS˘vPDEӸi4sw'|:}z kMq."ET.0O9 ڹ]FOBR|1e-H/|Ì?tU5g{̒B!Eɕ2_0Gyu~3^T@#\շ=Mݍ}î1|wd; x6mF^wLv|}xݛI _G͈hڌ6C烍%1H",E %ތz)mGf4`4<B XygaKnڌ1VNů9[rXrVsWLٔJ>>u6P)R!D9mZ<:a-׼![G.ƕر3{y;i i]ku< ~w;Nl7D?p /bxXFi \0Iuѽ/rQ|*B\4~x;&@Lt1ƌk _Uqg$M4XA4=B\"vlYGh& v :4D-*v;q:T)h4VGaٱa#[ySnWϏMwBQY? f 3PZ-M*Sõ̶hC6fo]wt]@ 4 Y>!a;W?Yٙu/.2}O}ykfòP|]q=q!ZO̰loٻ3^ݡVx=nGxbtţ㖮kLޡl mqo}UCKUż+f_v4qj %P_M)~1yRZK.p{f⃝1@q"]l?70uk3멢3i޿ğύeezpԓF^3^'EKqoV}7_{ ï'ͦ#}tx寰TqkqKwktOmpĢ1HZ.F~W0$4;NfRH0 1+M ߹ Jl=,~.XQ#Es?Q;VW@BжmXE)iPcs5hd|{`s ,݉>_~xVVǿu'kC#q>>[’8f8P_r2] VOk,d'M؍y?n. zB$3~׸?&o6VԾM<6/]a::,pq~~d~M*c]j>d3N~#15e'~L>=އ?N/Wܔ=܋hmͭ/DӿWVq̈v6 ~+*oRK~^pw&TRvORHEURHqR',9 o|˫":m',9WI.GDuJ6>Dќ7OcqL_If\6c ocЌߟH"{gֿdͤIѯ&A~Q#0lUVbFDv O1{X2#kk!&P ,8>÷Iq;|3cx&:/PP}հv$m. ӈ=LuqQjU_LZoNx{jٓ罘5ާ3Q!iY'}ώ{ >:?N{3=,=~ßӞ̂q9kɨqWPRao Q2 ;ɭ{z.+}?;@㦗e5=w ȭl%O~'57_=~S;ޤۙjk۟`;=?&XLJL,\W>eg=an׎19{^/'b=| ٟ=pŃ201B}x, ,҉Cu^OѷG:||Q=}+=|#{wҼ7hSn^1WQ1o\vm/(Yyc79_}57??y*?Cͼ=qpW'朷@-7bPa pR7=S+p^,}eSh%s;=rSq$G^g7Wj}j<WebpmTT)(W rn#OG511{{jcZIҦRHⴥ]^d6xRHRQ)>FՁ+db@ #Q#na'V|ȖW5VC#4?-/^tLQ`cӀgy^ I.L?τ~x%%}u"c!*Mcѻ7Ǭ-Jj`u'US/{w=&|v!oظ/.yrf14.̢9/2wp͒i,[W1ynac(N],y#O- ߹~É>YJXt&4X*m%g?b/ԫuWQ,ֽ oiĝ3Oy~@:='+^R#H"E)RԦ.ϣϵYsBcB% =7֮1esRt F(zxeXτ˻8?FD|2ɲS#57\xWF=[͑&<֔C$@n޷fO^͜uz?׶qLoy_ܽ%{ƅǪ_;'-\Dƅӟ{ ,*wvE̓ĕ &ܕ5q] kD2tat֜ƁEcȞt~#$PAk_+[UOMbD01!>WFdnlH:45hJ*>^bfOsW8ӼPFwD-52{.f X*2B!DlWm(*:l2`*݂ͦbW٬ݑmVltWUmVUVbY K4 JzvUZjnbIZCwCY,M\1oB;l( #6Mq%jkۯoƤpe9fW=O_dZhICc݋;}`fX#K%n-*ڒmwFr,"T[^3H/vАnVsm<~Ӄ=0|p'R[E,Ԯh#o̠eX(׸Ev%eea,"_/ЄXd _lIS6V}] 747K-;8n>Ŏ'fW]U(u;6va!BQ>Cbv4RWj_.W8`M̖T/E{<ݶL/~Fd.EU|`ޛؾ; >OOзWIqQ;K%ZDzEzW/bb/T:*C)L0^R͆#( cG*f^9'٭6GvkIV+vpV++y/ɸ9ݎFF,ۧ1G :㊰b+IV@-VEp۬6lfSŃt*Im6Kyl/Ryp2CZdSQZtJvS@Wn6=݇3;~;p ) ٤̴F6T}]AU[wbh[/?tHc?}9ytYB!٨y҆m˸ t~3w F6U8"듸|x0C{{AS=i?80D 38YiV܂zN$Ж&Z6@KYXyB[r̀VkeS9bQ o7*@-`>Ebw%uڒ-Gw~Y%h*_ 4֙v9Q.g-wtHNUW5k?ULmQrЖǟ]Ƀ:8u',t8K>NZT|Vԍ+c>;iyj;nƳmT5CH+kaվ.Qj!Ͳm [nGC bLۻ/B!7M]ܙ b۳9Ln(Ш-|4s-YWjGKe~r6Ste͋ˤi~ƪqu!qIU0vbk]v$ YUƒ1&cߛQ.5s!_C! Us%ތ~/#1}vr_ƶ~{a_z>5eq +i1{L߱DUQNcʬE|2.ɸRuu󍔧A0k{#糏 Hat7GuᲧ_#;v0*^fb]k՞ ] Þ};~b;opiWNx}[ů)nn뾭ʀL'G=iǵc{6ci?u7_=VvkRV}#%KPkF9nnRͽJ:k힡$.׏9 sl /ŸBQA@x-vx^ƕ-KVFzѷywxI|`挜 Dg{z6M: JuO=ϲ3?-B!l *Υנg":҅?rPS^z\<ዙ{YCR-둯3~ }>cœQuӋ7"U)Ϡ+ư|RWfZ+ YV.B!.*9{<]c~|;bOŝ#:|Z-p$B!h(yu;j܈677p=C -LzV:)l0]nV:V)yp }<ў{n=VoL|g"DpjqO[M B''tRM_)7c+Ҷֆ]+ ~h6:]Fh`6ڷbyehE6DbQ \VΚϴ"珜igqp8akC^+ t2mi6оbu ~BB\.り:]],Z' MH͹qx$Bg}WھenPG ކ-n.u\0eL^ 5ɱT-O;Ͳ_e\lAx4[³<G-eLrUQ\}eKzk5_ĝ:Ⱥ@VVgxxצ#d_.O0#L/qe/[rĺK(DCШ٫J骕m2WJ׸G'".:ʿt֖ Ү(lT†c NͶ%'i#sY%aqԇWo6mo.Nld:V$i}"yXJ[FГHm:f ]jQJ[^üPޙXcn9ZiuS_fDKUv? -G߬g+mf1?Wp_\Nq^\\n0. E~uqERV/0N=O+EA|F[z]5kp؛Ʈg5oƽm{hE ZvG  ײsI"3ZQ$M[1+Z e4q7봝սRo\{sQ1[vӆ㦁FZrf:=4K}pD-'O*D;3ay繋]x'ν}ٳ/LTuUY gEEE-cy/kI=Mj-:o_\%[_ds$@h J!*3^; @Dfb:ؔ)8ZM)C#SYN}8lFܬ6;M o>ITK[T.&f/.*}_vԚh `I9UYT|&5:9l>$:Bb=؛DOn|49MB|g+~Q6- y%Ƴh 2>[t^! Qش`N;8QPA4YfY.?2̫f[,kd\ڴ,5\r-5eYf"|߯³k?`[Ӯ<`k1ijjxDDnPתKPpwǔti%"-ܴJHJj՜7pLi棏A״{EƑnYZt@v[TT#mwo]4a@Վ)\jRi5贀2*)9aB[-K J͖Еt;Bl ]Š/B2ӯ'Z\j8J({՚5ΪBzʵ> !d{NB!ݭᑷVFuGԯz9jHVEx$) J:$RQw<N)BQBwl΍7|'vk0u8%=lBc=FLU޹B!]N5#M$`B!BB(FV-%`B-loLygA!⎻#s ʙd@6!]v{B!r[Zf-/B!PA[zFfyC!B! Aۈ_g>B!*=yYvB!BQI&4.]ZYB!$hBT#F(,!BqU"hS%o(JygE!B!JEaaXqY"B!R6EQXsɆCtyT(V~[o哾iB!DUtm(<$4V\vEEI0b46aڋXr7(;T!D~2M!U]lɆF՘bv=IeqxQ=n 3?p̹B!Bwm8bɎ芪1bƪ&\\ϵ3tw^DЦås'/g yz^dKh0gːo=i ш#,yOCOgh4Ң(>۝ҧ7Fw G.%tKI!BQIܖ-,,ڵkV+—6VTE!$Ӫ~WTL((XT3.m&1 MCSG7bGW,X7 ӌ?`K]O}‘k?ì`fNĊQ薽˶T5xk2;B&Lf;71+6XPέ`ԭNYοkb&=DU%]E !*-A[pp0ԭ[Pnɻ=88s+o5܀/ZwƊ +fPTGAH@@nnnEm߳x4ct}rJz>ߪgک)>4 _PJzƐoxoϴq'+h\?;1j 䇗w ^=|G"櫱$ xt'ˡCiB!DUtۆGl2~SOZXXÇgٲe EN\aQx,auª1a% (j&QW¸x} زhkǤqx"4{t~wUFzB$q?=ٿ;]evÜYFݘy2$t~yA^ܙ/AyB!%Xܣ>1mf+p+kkjL4s]wO%!f:UĢf$wVԧw,z|z']qƇp[ZVT];%>>>I`H.=vV{L!Q<;q S*~eЧZ| &3{i߀ z;.g,F ~{ Ʀsh=5ׯ-]θ5o6sl! Մٚ ]< w3Orz$DѷQi,8 '?hc;hvDݘ\Z4oZ'cj,oiL -s/QuƮEO2n3ױoYRT]2M!": ^ȄlX`[hiLM %; Ok復Wy+"3aӟ)4MXO Ӆԇ)MCNOʄnؑε &f+q[oؖ$#;%CN8`,8q)W( j8Х̱MW'>)8y^Jei<鸅E4K}ތ̏Ӝ=gdϵvF0/t̲dO3$&xG& ra뫣~N?x'LdYČk…o >%s q9mBqwqo[?^g~j~ :ּQvԮo7Wfm3K}:Ok}Yσ9f_;;s҄֫|>.M=]>mk\hwyߙwu2a w\9G>Jm(n7 6u0.^8]5fDtn׆{5 o># M;3L|||6^+}|؛siJ8qu iI^FM- زhϸ)]CnІ>}d=G_=85Նۉ::_('0V]Gh\JȚlԙnOh\h \!#L]<<J$B!;hj˜yW-q$cxhZ^Saw2IW~H= :4 g.g^.סܚYp5cnikU/ՙWpƍG-˜wX_.em=?7jjFŜa+un%, ҽ< edJٹTzHVM*:T2*N1e4ylY˟U͚퉯ucFa`?t!*|[VhBzә^MQȜ6!8IqW.lv&L ݬ8n:󠶧p%V;ւS;3<Ёqԣ׈|mmwjr&:i9r2{&*r'T:ikWٸ*vN^>Դ(Y4=2r6f:F$YN4AM?X -SZL2-:"dNB܅>i8c{TPAՙ$]kJGC5)%"KaO-[fឮ5ٚTSSI, dEѢڭVįYsu} =y_D\E"*I!oֿDj?DR糽JB!(?*~8_/ƒCV08ziڒ--#3ٰ=]7o` F3T[f>o`w@Z5 c֕'YXڹ[(UNLm1WDB!bpVZ}GKY}#ї1s( !zX.<-37Cۅ!jbqT&o1WWW ؄-]TH !HoMLogH:si2߸usfvPqTg O FOM% ~2+\RJni|uZ9=g){T`'ɬ->L\M-9e>>/s'Y 'js|6e>}x+Wr!w^JEQEAK&BrʖЕt;Bl ]Š/V։BTs[BWVޞ !BTNywnK; .%юB!(aJB!BT !B!D&AXtiygA!⎓MQirB!$h+EQXR/B!BT)IQa! !B! ( wN#pN>*wı/xSÃKbm<4Sw|@G\S*kr jO#cq [aݦv݂noǡ7{!BT*CQd8@*|Վ(ii@z7ݔ2whhh#zoHcV|?c}t\fg 3b4zӮ>qeݞBOoi,8OZ4QrfN[,ˌ ^s+OMՁ]+!BvU:hS/m( "BH6UPQPQf]LacpuuEV,s{Ɇv}"տmy[2Wv6a79\aް''<0s|* cp8&?ުOvj'uz8q3=V0(2P0xh 1X/7{ŕ9;O'q:ư:,K=X.nSM 藺#7Ҍl{gm|51l׃]Z2n'raVt0~ 'b(t-p#hn4blԚ^uk;kp[ށn S9+2n7ͫ69E0wqU7%=Fy>`ڍHvev^\ڴ^NƋk$Maܽ_>N+vS 2nБB!lЦ( _BtjX s6ǿh+&bBQM w" ڔ ,3Cת^}x!xfT\?;1j 䇗w ^=|G"9n1K='|O}|.ug)Ln}ov?Y~zq_x*qlt>@R\MA[qe ^V|RzPV=gNM ͤS%n3g;П/fr6z-KA) YCfmiԪ?f=zaYo;:a39aK?$5tu0ւd2˸$󕳺VԜi;ؕvavb,u!\ŵ xM [Їt^8]9?v~O#BV^)l 좍WW]`Qx#ҽoqŰzAX5&p^O6SA4MиZ9=vþ)pO\ zz1lՠS&Ҭva;t͕çIUsu Z ˄-[К_Eܿߢw71pH{#qqe΢ئ9{L j0*ςvCaiw|Ew}:\9rs=<(eoH4~ w 3,#s6ȽdZBN|]ΊvʾvI3Ӎ@v՟kjpP3H7pCh!B)U.h b-y/Vf8SIrOzPDU-XH\d^R5w]T+scO)47Gjvo1[X3\aY=IGܼ .mFds\MYѲvG&$F]hv 6ZH`}SvnKBqT,8q)W( j8Х̱MW'>)8y^JæȠk|5}K$4<0i [_̟s:.ȭ|,?a*o5廉?ɶSZ9ٟ?e+^aȫa]7~/HR|RBAoFoInij2Z .ӾCLI/xجS2Tχye'UԌh~%19;E2?0˶>`䈯,Op<>  IDATW̌:/ ;}{2'f"K-}Qi*jZ4k?#j8NpKG8sLBw/2oaƟf?YDm?GhTnq!BTU*hw 9y1W$zWv4eT ݤ*`J̓ 1w^~3ǵAA+?XǗ#kicSg=>]ruc۝dhgi oP}s.XH}P(sQ]}_{2zp k g ]p `8~M{3tq: bs |S<0)X@ZuŶV r. = =ڟBXXx &z}R{`R3)u RNǩUm14kLqDK]wJD[atH3_V.1=z?606O'4b^'*ɋwDU,(I*^cی{n, T*V.~?4C!TkGmn߹:`S* T !-+VOd5HNMj*g8UƀM%%{Vl=BܥDW>%P4ޭOPRQc ǞvRCN+dų$yϽ7@!&66܈V8떬i~o_}I3oh]%ee./vii'[Ν^oϥ-8B!_}g"kO% Ի؛vxju/|͡)aM\.bEI;B!DU٠ n6;g-&B!eU7p~t ؄*6!BғȄ+z^6!*żn><$v9ˣI3u7 d|;&GѠ4216yw& 3|~BQ!HV EQXBm܇FW;ƢJuݼḮ@#Fcoޜr's{wIcV7]t,~Yק]Y}&)ʺ=ӘYqҵFi**̜_C!Bӗw*-pQ5fhiGOi zUKpQ8bOxΤfy~ 2al݁ŵY9^7iL5Va'_Y,aj4&m˺`^.G_B>:6Ka,k{Ҷ#BqTQHvGWT3VՄE5qzqB{,w.461|)_-C2b} zNZg@?ÂimīEo=9';>LЉ>KHhl :Њ֭[gp#hj|nnNeS456Xk,~-1e{l7˗rthīMz܊2Eޚy5g,޵19עu+VOzL=Qm})eߞD&<"a#0iwoVf2yt]ܖ  [8+F[nsoAscĬv^;FY-hFN޽rhWKg(翛LiѮ^NB!lA[pp0ԭ[Pnɻ=8Jş!D[ 27`sbŠ+&đ+p:}'͠MI<3;~؇'Yig'FH$Э4Fw^otbtۥ:5ɭ#j8kW.} 0h8^'e%a=o3p L?Yr(-7MCx3 l&7?mײYo;1d<ƛF3m@sϡ^աkC>&CZOMjdx@_&lÍ2x J`5l*Y4"gփ)!XMFYB_#ݮr5,ל(o_+Gn',MIƏq7Vpa]o|edݱLUȉ︢Y.]ٷNB!lAZ$^;p<1Ż丟:ZPnɤkJ_6VT];[ wSh Ԭ1U⯂GmS۪@^U*]\p!B{Ty fΜY6~`0[3ٚBbI^`D(?6?`3xh&T1zs/Qu!wxGHA9ϾKϖF7u yvPgo$z~>+sQ8^j4;n(Lh޴fvW8q,O=\d+6롤)Mo.Cj,o3%Dr&s 'Fo45m-\q5hɈcl_^~OzRZuTO4nE;B!DUi6}% +h9N\}(j54ti5s,G~߉O;Drz gׄ={32_Mzuw7>z} h :>LWG1%:r+_&O=Ǜw dnbOm$fG԰%Ff'Ka#4WLF$N)e)Ga z3~3?Nw{OsFfETa ]/8fD/!-7^#G|ťgy84]dfԱ|1eƧѥ <6]jq쫟JSQӢYQj!# &>>g^U,^+}|ǰx'uAcA _ΒlAQeaRKe%ˈ?ZV+2'ak@J4fN}׀j$]툥:ށm ݦ]R-, ؿ[̧=ؖw$C)sQ]}_IS_$W6@'Zo݁1e4SL=S|S<0)/Jw1nH} =n.3w aQ439_]z&aYz2)X+׹RNǩ5z4z3v,ݶ+w?B!(旕A=դrG(Bbb"QQQ> =7R VM*:@Y)[!?GFzx|y= tΫܢ;Z *Y!Nil ]Iמܾs/tU i6BTms[BWVᑷ^͍D?Drj V+h4`U!=SL2l*)߳b.%wf)qn}@2W2U8NBQzUi!o6 7j0)*5%DZW_gL:/?j=v"BҪ yLdi䱁z{׎OC[o94e1sEU,(9i'B!(*́ۆx'zes؄B!ܜn.X&BQzp#psuuEK&DU !BD'$XB!BTDU~xB!BTd !B!D&AB!BT` !* Y=R!UmBJCVB!DU$A[9P%o(JygE!BQIv)°20,D7!B!D$hEai$ARXsfJz}i-UFV\d{F؝V*VUܽ<4+ kr jO#cّjAj"fnS! #A( CH2Mhy>jX^DD耜?Y?3!, ?Z'\>F6?$YJ^ihi@z7,UVV)۲ dEGyn&<9NRInc ,9YO~LƍSu{> q?ŧ1,0G#?k Lhu2o]BF_ rdAZF՘Av=;ç12=z[DL~~^i$>A?y P`j櫜)Sfd.~x8[M$W㙐Yp5K^ i<nwRҖ<{ܩS~s&5$Ȱ0M[;ж*+fk)ڻY9^7iL5Va'_Y,aj4&m˺`^.G_B>:6Ka4W/UTUS.Eaq4#iUcFŌU5aQMkg>a:4' -]~׿ȏb[XgF~;t#08@K}t,Cʱ\{>r)&'\f=iVɴxoo HSc#z|)OJh+{ro9,FZfsYm/ؖ\ú~6|NfzѲ ]džxs2^injB~ =G$,ɎOStmkm:Њ֭[gp#hj|nnYf[mkIe"m>Ø=#F#^mԃ-EV,*PE]s-Z"aiV/ZOiע? mw|/RЄG_$3lF#Gl'k[X !D9P/m( "BH6UPQPQf]Lacpuu-:<xưu=sYdžYI;Òp/.=֎Y--{mݕs+?u+SVڸI6Ppro _3u,"nV63'q:ưߍF`UƮpL4US%8V,s{Ɇv}"տ oiԟvs.>HEKFܫg(.dv 䕝Mvvob#W7l, ȓǟ\)9x$;?hq0X龨QbCYBp5ϮRmPe;mgs8k>woVf2ytϓ]z#hn4blԚ^7RvZ>ЌN1{5LЮx2˸=~<q ^FT|Jz>ߪgک)>4 _PZEƣKzh&CZO<t0ւd2˸$ +L!DE"2 EN\aQx,auª1a% (j&QW¸x}Jjw~7oB5_C~ݶm[728sF_MBqwuUF`>2aJ$NG3SYG< vL^ zo{˜dyS`1i=_^ZWN$YnL4!:haJ$Vӄy~HG;u-{[IWyn71g!ne|wBCi&]ź{uWE4O/i%OZ$^;p<1Ż丟:ZPnɤkJ_2lr9}kSM-p_s -̷m˙Ь9~ݬ)kE_ҫe[]^a7ohu䛣u9Z0~m$gӶf2~}txtj9z120RئA6UZW-W/B~S.J +rk+jEյcξu r6*:Ovo1[AŢ؞=(_/ /}@*k=dJJ2ߔ&%MѬe3 FBr{U⯂Gm/j1 T-ks7IWBN<5:Ã-[xvI:e ?a9^ 5~>t7wQ/aC' , 5+xpi.)$f_YIIol)[(<Šo؏Wsy+.렬t ~szF7u  6X۔ə"#hIMFP:V3HKaQN qVg{ڴը דr)ɧ^><[cF~ඌpNflRueRʅME5oˍ<:zuė(? IDAT\Ww#$^+ځ(غRgo$z~>.u*8.P7SĨu〦fsM89B#;q, dTpklK$|eλN#S1dfDEDF?G߳9-7^#G|ťgy84]dfԱ|1eƧѥ YOWӂ3-I򂕭j2ty=]nݥͷJ%^σ2g]0L O yX#O7vYfl'Qσ2a3?{WUps%\p ȉ(iȴRfjiYjY9i9ˑi\Vp(`=2.C@}ޯWtyκs9nXv-ZsgFxs֭;M(ke'K>~ً:> rMnn䧿MPщŠVm$/ 5*qOw8]82=bN.Q2RGwKYɉ;31 nHACs{ dxXt -=.¹@~?֕|Vp}h{Js w}EIܩ}.zfh{csۼv_*̧UOXrxƢZGډދV1fーmcOO+ ШAN뉞4=#s}XX<-r.,F3fM|E #^3΄Ѥ5#i?6Mh {cw.w~aʘ7η$M̊_L.GLڷfxz(4]P ֫,09N`@l65|4ΟJ52VPt Vopi%֟QJgQU= Ppa onC=(4HV֋aR]/0uDʏ{i4cM[؍c}?Vv%ƍ37Xֈ9jJ@R՘2U su\>ɜ^ح|'KZU|l`Ztw컀 YRAUK5kéJv[qne:6 _~yg{ 3- 5I4(]d*w^N 1=+-־ډ&bНLzjmśR+PU 2d,ot{- BiXbM$+_~5bwDz}Bgm\*dž7DПߏΦC,?Ʋ'thi;AzP( 1;9p5Uʼnՙk/]IF=5Ң{I4\iFRb|əEO3 U-b=. d3VK* в>Fxv,rP$ ]^eK܍R@'TK)B3a`L/NbܝtNoAɋ &|v"*rD(s([8Ooq)ڌdn2jdЯz3 9xxx@ՇF- 8Tk~ IliH [^R?˟Ƒ*Dn遬7mx4X eK\K({֓lwwQNW;l [M$O1zd)$ IP"aAc5zd w' E3¥'F|D& J5PZ82(ػ; % AAPEJ0WHKNV2uR1/.5;2AD&  +C*,1z   B&6A+V(AAʜHAxlAAxHHR~CAAǀHʘ$I,92,92Y$n   H$meH$B];d’SMde7|]C_eA{W:/1*0}r]Y>Eբ|)P1mJ+vMPw,&fE.̧`zcSԔ:O9Y?sa_e퓥{ '5~h?x ~Jg!?zLb($"7MYtѮR>'5{1 N[J4gcDJ8'0KjKd`fI)3|LsLش7tr0s,נoR Lj`W]Yi ̿5Ukc D47׿cHF_Ԫs"vUUʩ,_zĿKPO| L11 {S[E%>Z4{1&e۾j6ʄ- ;#Nm&*I] I9˥|һ%uW7sloWHL~f hq~6=fb2wPՄի < ,{ jjjжߗ(sA+BcL gg]/IC|<1ث5]qkڝ"(phWƿ<F%7ho2ul- E_z(pfs9Z$[=䥠xJ4W?t鹶g־jGMkl!0WS5ȈٓiŧK>=;9mmҏR;XHOy)IK&9M!#!#aQ5tՎήlzB~_mf>orC0>}[{c!c#55,f .du\1rJgH; c}MѿOsl2uFne`ў9; Z65_`'䇯936 lp";#z;gʑ9Dv .13|QW;܃{GOUyM|$c4Wa C|j||Xgt{3f ɒC )Pq>/f̦|ӗFM"gj*`In5kRM?ND_ ͯ 䣠>m6o 4J5Vf&1mVX?sǭiM]ts+3xdz4?S>ts'=gZճ+N#l ϥ=(+^ pg7sgѷu=]&.jܓ)zpjdwؼm ?ӏGfwbreThm!.*N/Jql BE"K%M[sGuIKKcidiNРZk F1$rwմ hf#6f5hZ;S G)H1J{ڿ՜J{Xwz h5`^/6m$=DxrVw3HE]{)sXVYU#` wnKVgAI"A"5{u/3ǭ)M_.,:x~79t7P7-iiϥ ފ_ 1ㄥ3.Fldlc2my55{FUw_J1VV].n 3ô*i=oոm]Z@U)YD*`4+w_5YszҪyK:O+c~%k/UuŹn 1E1e%lx:¨QߡXv|"iP52ȲBcOx.]LM(UkJr/Y]du]\]}Edˆ Gvgݏ9Y)䝚 @ cvA|b-cU$Z M.v!? 'C,cu)sM("_dM)B6"1V8OL\ޠ7d+AʻgM۟9ʟ9.f9'Y[x;榕0C:ZO*C*rǢ4YzXǵ=5Hf\OTdږ3zb=[YO*mjr(A3԰#\I/OA}I.>#uDx8M(Ӧ$Ǧo&]#*|F8y5xA㶴@B94f8a y1)INFҠnGr.,Fs疸c1DiK@vSRӍJ^υl3Xƃ3ˮrϴ}+q#Rz򬇢S2?\&hD=xבr5pܪL֝FsCjż [nkTjR^eg-$dRB7x,EN\Z9%-3\>ک+üX=n[ fOZMu2kteD'=ړ׸qq7s|MHAxeџq}lpf4"# ǚꊮt&:NxT(cN5*R6caMEKqM‚Ax7"#?[]LYsP,])y*Ӌ.3~%4UFN cӬϚ!%o?wԙu&VssZYm^і_3}K0W/ gDKOUfA'hY j;;;Frݠ0Pt7Ӆ#(U2N2_E|m;l\n(S+]@Hxv]r#^h3g+cR|ef86W|Gxi9qGKgcQF~ GDE+3^Xζ1'ݕhyPVUDO9>,~,H%/,F3fM|E #^3΄&-oǨ1I)1TmEkxG0pgr>|*״İ9u%i2`Vbr9 `Ҿ5chDžCJ^H+xC=@S&F}Ür' 0sd6NN} ~Y}>T%՚` X+\Y ؾ~»#՝w,I&$$͗㴛glm0*P)/ LhןSۡU泣^ymK /ϥc =a.vzl5y2Q~ϳmav\nO˴XЅ|{ܴ~i)Iݶv~~tvZxwd϶.i)Iy'-=5ubydqjcqIeHMޥ6Y"Mubx'1|d0O[#u.y7ۙwDz l~$+T: oxN5GXD2͖W]q[ J\+t dOz1-a_s7J$S.E%$彖%k|:V`r Ș|_>ĸ;5nك(MDUˉ(QbQpzw 摥"{S-qqt-wAAx摂 #E&Q qXvEYuHAAA(1թdO܆>;M$l<}AAOd(+qCVMJܧMADVQD&  Bi#AAA*0   T`"iAAAD&cC) H$m <6葂  =Fя,p üDζKTFݗx`~zQPע*'%)Pf".k?eGQ洵)'3)eU| ݒZg\!Ӽqj"swO'IyËN\^>OJk5VOR@MM긻Ѹp`g="yx׿2c;sN_̬A>4pܰ+>:9!(sVKLX4'3&.4EC~_+Jm8 1-mw7' X d~`b4rwŭiw&oȻɣ1^^^spޠ}޷˜ױ0} }5OkVsl)2;\uo=Ƃ7Hϵ]mXɱlUn~$65H~6:A(g"i+$SAJҠiNӸz;dtHHd=Ey ]#-® C_;ZKઽ N,seFGob B/3 ST+G<=!OQ^ Jsn@*>mُpv]ۄfz./߈fl?w&N/ !|Ǽ]Af? lSOg⦃ݳI+x<.=(:6PX8NG8/cQl/+=( &] ;9{%3{`;06t h]^L/r$` ~>׺,аq䠼z-aG: 2f0_@ G\I@m-/XDPtMLNđ=_ېڡk&qX% /~m0YIHTVfm}ݨmsBW$O&,H 'lС FtcD$p7[m,癴 z?1Yu@N8X$kIOxϷ+ⴄwy 5צSu"5˹sXwcӭ>m9wk3ˆP+,pF{9jtS'Ų 3kG3ϣ NS{ gW-GK)t{&QmV\ %nς ?oz8x彯xF_L.onjj[f8ux&*W7!Z4ϻA%ey%Si[Mw:; YkIleq>I7w%|ʫM\ո'Sf:TɴVy~U̠\с!?b8e`H/QNG^vN T$O[ d%l79Fv\ƊS9g5o `Awӵ-z:̺W;$Pݑe=SEIe ">'v_~p#| =fmM ꅂb*nF5_@Ug,7htק:K~77&`"umu ͊_ 1ㄥsύuEB]`L-f3oOPި _#IvȱL>4ЍcG".n 3ô*i=oոm]ER@Uwz(1Öw}[ܪZvl &iulJK錃uXrgSKql6(8? !U͘j+}ٛN{fٴϾəcbczҙlU~cVDS,?d@"x,J3 Ū7u\ѣY*ne{'f'*v۔l?lLO TjU $슘Zc 0#w-`ޘHtz(78}o=A6RnԿU;6| nXQRl^iI+HM߄FFTr_\y1|XG>cJ!sqEf 4A}W %bw 9˚ 1kw6Y`ȃIIOr2ږu+? wa1;č)'L[^F[;*nTʕ(uwz.TdAĊ5Lל\vE/#[ANg=524A+Gp&A1`忎V EgeJwNq(^p_'HڊA$0`şBӐiT2M}\rD̯@wePƠF1l b@BGɵ8Pu$˷qƧ-Vo"$EFN c g*P;4*;o!!_,ƣ`A(&c2;f"/`uDA^'FWFt۸)=yw3gׄ^E|,CB*#`8qo<o,09_G +&q(k8[O4' NZI* @}RwG;)X+$MHH/#i7`T$R*P)e,K _K.;zh\5!,Sσo(e* g|ù:\i+E 3iRػm=9Ȟm3]RO[zjmśR+b1A 2׻MJ}* wȻDά#kesMPtmHk2?e.&9-淀z BWȞb,[ nJ:Iҿ]6J 2O[dL/NbܝtNoAɋ &|v"*rD(s([8=;ADVbe5븋jw S`nL+@ P1e̡pƔFRڞ2 m  '2R١VE&q& P\"(e"YAA4摂   HAAA*0   T`"i!FAi$6AbHAAF"i+$ݒZ}+㛹wO+IyËN_W?3k \87ʄ-϶|n.V!(sVKLX4'3&.4EC~_+Jm8 фsLXwL՚F5myy^V8y4jhaT{y ,s^P%m<1Gӯ%.Z-MC^ 4<Ksս1 {_G =v9[iC?ajJ1{2M#ײTbɧg'DzWC7EHZq!̈́Duu-֙b,zRhL&.|3vvrJ(Gge|{)w:`)b+m_r[oUhI}|uYȁYaAy׃IZ2u>ne`j!/Z-Z^t{p#744z#jĹcQp.{*U rg%!QQ\[-u A D\JH$~<<#9FMC6aD½C>X >MquG[g3PU7$gI0FeV/ Y0lqbisfУ5Kyl-F Ji|kQ63})h[ 7aW㯫&O7u}], 9ѸΞ|4N䝴I7wxsf0u]\=2n<ְ\J3Ҟnj(M=_Qm .u_V](ƃW7!Z4ϻA%ey%Si[Mw:; YkIleq>I7w%|ʫM\ո'Sf:TɴVy~U̠\с!?b8elB\T%^Cr8rsB"}J +a18 N&4V= {d-""6<90L/9փiVKeej7ʽ Cde~nhyXٚמ5yKZ%s%k/ cm>+.sŹn 񳷘aX4tdـTC'IT.&lx^\DوjS[s1[yN?1g9vJZE&j -2lyUX,*4Jr/9&eSLR@y,cu(so(lCQq0kJn&d![d R=lgQ̱w?j6ߪ[}17ul2י֊Ȅ|*C*rǢ4YzXǵ=5Hf\!xanS)BɤigZ4AQ//:0谖oF?*>yg^CO1XC\7Ni\Td/ofEv?ұĩ i,,Adԙ,*%. fڬw1aۻ+Vg ˧8BjU $슘Zc 0#w-`ޘHtz(78}o=A6RsKa뱘VRuFO[̖?;z+lcӜ!ݬ9V뽩1eٌDK6>تIB!LNҒ*Vrx\>G 1kw6Y`ȃIIOr2ږu+? wa16mynqXxQ),^υl3XƃϪˑ?čKO˳Oqp#8f0_G@gpjr|\t2Y B;8Y ZK8]xR{'בhǰeA2LeOܟo=`ݸ*|V #n>87ֱy&Oe% g̿ւO7y ka9:JOoc\< DV $Ws-dL5mꓠb\sĥ&b~;_& џq}lpf4"# ǚꊎ Kh A!ܾ]"U!gT a.! IDAT0wpTeՕq&H oEs/.X&MO[[.DHƖqA.ک+üX=n[ fOZMnE]+#:m֞ƍ3kB 3/ Ǧ3yq£BٿpsB?Qc|o*-^fl: ;)}1u`ʚGr`ч̾g=OP^v1+2rZfDx )|Μd3Cjꈶ2[z?NN?#Zڔ|z2 Ue%4UlȾ)Ny8-ՔCM~_+h~cG6yi[6Ӭ{59RV5>ҧpHw[RpY3f_ˬ7ݔfcҭl:_Jڸ5h[_K,0 hn3&T("i+Zn#qJsǟ!E%" Ws3,)d U'yߙ[iXeL|ZYfۻ"64oގŹxރ'}W͇OV{XZN˳Mhn2 .kZN\peT6 |F|s(+j'z/Z?y6~u } :Tp=Ӟy$[e/c( ʤ5#iʗxu,JGw.~rMK So&Pb&[3}b_ ZDG`rV^i8ƒ_?磟%Fm.!Lgcg.D!%RJR˷ՅjiURj6ZoUJעnT*jkJJ"%$d3G$LYzV?0_EǕwB#4tƆ#;s6;1eʰ_J[Ul(6¿\5_Mz5<ӧ72it.f6Srv,@~LS~BÀN#Lmo䝡dΎm˒CC:i =s4; `6TE5\\F??X lِr{?I tk$l宒eSms==}FVބM!$4ˠ:S z̯NbdAos/:Ndp W;1W-]gߠWmizztF?̰iE t|='E|VWwtU\j\cycd>SWUgt;{N0e2rvl57?1{V8Wl}t4V_etuӤi$(%7OiJ&D)cڄ:8TZ3$͹VG2fy fMaCܑ+._$F*:)'1]z+:):Hccu0B%qB!BH2( N!(|lɢS$qJfLr},M0 Ʋ?*ʶ͋t|46vvfɻx/1)eUXÙ=~ .kgƳЛ5-w=N!ȏ$meDQ`{uFUh()'Y&w܄FӦuQ'ٴj<q\fuZuJUԚmY_ߓ2{KTNhq~ƭ|^$qim~p|Ïq"3:VcƍmcokUÅ⮑k-O=P5&,higW%aSCK,L`csQݎY[x$h{kY[0ͼdToMEnT ATkٵǻjz[oίz짿/-A4=s,iyXF_Aaԫs~4U/UTU)$| v숪1b¢1F\č>$߻m,y;0gYc?z-y|Kͮ6]>\̻O]Daʾ5SߏWueDo3thki|^Xq8t ~XL4z2q2X,/B}Y|וٷ5V`G֏+z'ܴ8ypw1PXVdu&<2>=;²gzmk^/Wj$}|<=oOgm[{/x5ƻ~ߘk^X+XWoҋ/ә9D;M Ӗxxq;kkGL֧\̒0'-Ћ>z/N ?_Z7z/ɲ'jBc/&kFObb|8V*#g|Mdx/ُ--vvvx/>o&ty}s;X]KfGǜ_&r9Tkя)3Vzjdg+Xo5C?IV5g $%QNgOvN!DE"7/J +a>Zzwp345EcXP0 Q| N"4dxz6q370#aBy+a7@вMh1,f_nO|A(<>á9 @gӓZ<NҊS*bC&ܚ_@WZjqr^Ӑ nGhہvUq+Xa1Zw-?&k%u[ܺ{Ε&kpYUZm|ρa~ylt;Xoo׬A;Cdmc]༦1oؤ3Ml|eOQ * m}oo9ߝ|*.s9#<$i+-ּX4F[s*>%'`wۡQ1fATAN6Og^̛CqujoM퐔lj3?;c3cVl1f]=B7uN~sfFZ1zۣL3YMNMy/ZWlI MgbucKͽ~CawDQ yUR+Um1Y~ɜ *f%Yy^˲I>Mult;ܬ3QaK!_1ֆgqljBW547힘:qsʭb#3BCVŠ( _fs\f̪% ;{r~Ξ,IgDq.%V~q5IS o5]*\:x69 ' օFi8Ib>CN5iB&MhҸ>5mo~EлYFOu}N|Wvm%iZp}Gp ?ca:u̲)i:PRRocJYR爳 `&m1aDebvߍ wa1 Žxk] Zl&kz6L+~n* ?m;\kynS6نX=fבz&5mToJP}#m;EVP!vF`;v%,"IڊAQz ܟW"8{bI+uaD¯qGILK[;lӓq;xs'0gD6KݛQO{MaQPIZN㋙pᑱ\͛9xnγ8}pfrc3cicyn)yhc*?45=f~EL||1CG +/2=4Dɝ|9i:h\\mB{t,^+|GT:Q-1S/׌H4Ơ)hu˧b%l<`oObzH,|g_73W'obuj g'Y I*yOjcиyzrOI`gZPίA|k.WYR:ֆt)~j?r4FBCK;Iˈ}c<mt4_,V逍;-{fplx?+[z^Q10룄 æ2msQ\Yt~l[7n0=5m!صw{mu.l YÀ!rB-#-ub[*Y\z7778. 19 4bwwI؄(%VipT"V㜿cgD7C\4:#7j"RZc,{bRY]sΤ!ʕ\K {֟quj0**^Y"zL[dI9f G^ك)-y@WˉjX4n1~B I[eO1ugfj_I+& 4tƆ#;s6;1eʰ_J[UlBT~I܉8{zM!BQb2dO܆<0M6!;M!$(EY+z^6!JYe&BQ\U2IքB!IG !B!D&IB!BT` !B!D&IҐ#BQI&4dH!BTEU"iSSQCB!B"6EQX, M!BQI(,;DۣF}$NMJJƴ !*g6EQXLZyVף8dqXwB0nkޅgSٶAiB!DUtO&mY [zjLX0D+Ϯ$O$;nglߺ/_ӿ˴%`2Y;!B!]t%m$| v숪1b¢1F\č>n:4l6H_f<>z֯QJB!ݕ-445jXZEcיºm]\[@N寧3+>4iB!DUt׺G|r rǝ&lGKv\`qX0,nEcBf2P "r |I4_~k>啖*fg?i1{{Nt2|nbRT"4sjd{uNax9=p*#1jYIbB!BT wuL[^[IX=Zaso%]8KO&&ڭQf`V3HU+~;=Sڄ-z}6e4Z!0T]+%... :4c̺O7v~bWbք!=h(UU,9( Zı !B!]${6s'l;jdIB:Ξ\߁'&KQK U_\MRdc+Z–n_|ž='γ D~UMm.u~Ӿ`o!~=j4"oAƻдQuZj8BJBꭺijĥDyGsGwc"1mB!dȬm֬YEN 3i5sN_C(j:%jvu) roĥ%1- {{l64yp LÙaΞ%:b;_x_ҥoiw[uz2?NwrȆy)l jɕ?FngW;|7ٱakzsqk/gb*DE$cڄBQݕHD\\E~^Օ^#Y{*(ИS͗ks$cPӍj%l7?_LZܧ0s,8xY&jްY I*yOjcxg*ߞ~ACY8h3y>GqnJQm~ike,3Gtug>|tQXB!fêj]nXޱHQ㉌d\B-g,dtZ :-t}L&lB!HOMf[*:vgmhݡ [C0`뤧&繜jHKu<\[憿?$&'adNaQ!-C q1mB!*M9Q(fHPq^IGiB!DUT6șk8$$q<׽ !B!DEW)3gOO !B!9N[ېI&D cڄBQU '+qsuuEK&=Nƴ !*Y$kB!B{Y)B!U$mB!BQI&B!$mBJCfB!DU$IҐ#BQIV4|*w(B!B*B6+)¢`gfQh$nB!B2!IEa)$O7#Yw$nB1&BHB(`lвh %$;n ~tp1>نFzt]Eƍޝu  i܇l3eVvwZ֤+Iy Vy|CpH,B[lC÷CqДVlTsM!h*tJ5 -RUA*`m£#*FTT̪W~'-= #quuEY_־"uep~ 3ؑf[%k1l~ʼna?&[7y/tk[e"jZ\'mQш-_ӓE6BTDyisM! BZ !!!ᎿAAAV׭( _ &:94l6?h X0a9~m79w噴)6$:/ ozx⭏ 5rνuM/yrG jLX0 Yz49xQk7߮8N5Q15M5!B*t2dwBCC2d˗//Vv}ne`ivX4F,d`&E Z(ϧ '5 v]~z -둵BtnZmhvgn`uz1Ε&kpYeɹ&"_%]#s)IlLz'l^,фQ*=& 5ZF+2v̗r0ޅ:PRRo]ijĥdD* ;N.rjRm!ט69ׄ(Z6]L)Rt .ĭvdAc|+ˆ1UCvN2 =R}Jf͚U 2^C7^?XҩfJ]g?nb9+F\QӒ}ޫ':`儏XW,Olds ¯ӓq;xs'0gD6K`MP{NT55u3>㸚>ӾOL\;L`N/7!ϵfNѣuʅc=닪'1m\BJϤcZ {12]֤U}Ojķ+v^PQϜ{ω7{צj1ƂB9gHq$+0v ݨ}3DzݞS1nf(M Od!<{S6L⊜z\]];D]>J#* &\HTUT ͨ+a\O.\xǃ{j>7L |$}b'!:tCX8h3y>_&Qt>GƑtjۙ痦¯`Whɻ;v˽ygfiDV 5!2t]!*ؔjOd-ߪ{&CW9Ϻ+pz\teqT / pf#Zҿete6$tnzўO߮vނƊ?7j+9Q J:(\8::Pz=nnnƱD붐h&VN &b&l7j1}tkl1fw crG[ư։?fr6 n0oy"E .~".olBTf;3w$j]͗3%7޸KwjyKuaI^TۑjzhTaWXm+W);T}H"+bw =THA(s8ǣbR ?ͅT^P峒쉛4\]]%aZlY]$WGfWc5xg^|0f{$w\UЪt]@9*M_OWWھ\K_&0ygouMZW|Ҟ3/Y7gT4?m]ZK1kÁn(؀(9l ^?tQϲg}m784V-V[w肇w\)(zI؄BQ,l YowZw֐5 :2O-F=sź1,wDr:-dY$YB!_ڞ/TޮMfU-Aܣ$KB! w=!;mBK!B'ieVa|%ƗwBT@UzH!B!$iBT˖-+B!ʜ$mBJCB!DU$I[9PSQr~B!“)¢`gfQh$nB!BIVEa)$O7#YwJ7<˻yY4,*Œil~L}`0P1Tm^\3Cy<5L~ O)ĒS`evYS98TR:U{ƙU߷Yw*JZYc,?ƉX-r7>U BFVD? B՘AV]9p7MaX;]_W<塻]7??4oLʒ4䗏bqc[^w.Ľ͂66w7)mM yʅ(, B]-<;jLF̪#7qoG:nZ m7C+t=zC5]>\̻OFy?H"u~Ny 1Ki< J.ZsK&MoёgߛssS4&*Dqף?0c{ 3Ѵf=L̷GF]/[_)K,k/cJhܬ #fsPR˳Sߐ+.:!7H~6m,LR( _1(,&0-<:bDEAE8zw0WWלwnҸ>Ȑs&ⲪW[CRnzNĞ#njnw* ?dO(N6`0#=$EQ?-WXэĩk3b=Ճxbey3_Ӹξկz;|a?&[7y/t+g-Nܝn~ %]Oކf'¶2k=;²gzmk^/Wj$}o&ty}s;X]KfGG}IExn{<ղ>Zc'qUA# ֆ=GЏx/8msҿ$r> $%QNgOvN!DE"cJ +a>Zzwp345EcXP0 Q| K297| #jµ waC `chY2w![N tx V=*[ozD! գ؅(6ntըe?+˭'5 vhl:Ϛ"4dxmhvgn`nU VX':-C]}>PuSϭ'\iҼ׎%YubX.ݶfH?=`헜VIuVOMqͺHݹ\;~v('lHVLY [y^mhT|8KO&0MkC%U5b&|$lD%lYz5'_Ɵ+˳O Z}!i;F*PW7y\m[Ӂ}ch;$]5OI@޸;p{PburlZL{)5DZ]XQq~g ZPuwh=󸶘7wV1+yr4;Rng[}GVbc^yI :Jn֙ڊxX,5GH!*Ցc> ب>9{Մnjhnp!1=1u⮃;璕[ƊI! jU `6EokƬ1Yұ'WwɒD|FR9|mW٘>`K':Vl\c5̀-:-lk) (gK|e; c ÇuQ-t'NXgǯiSH&Jrl5[@ hܖFTF_m VyaRʥwiw]Xvm%ֽ_\qo#T˕h 4j҄&MФq}jf[@΁eIl}}r&suXߎYo΅̶.sS6نX=fבz&5mToJP}#m;EVP!vF`;v%,"IڊAQʹ3n IDATz ܟW"8{bI+uaD¯qGILK[Ą-^o7IG(t\Tǻ0N]Zb 7A]8'"SUhj{3iϰ)" *Qk`މb,D9meMՓanrG+'62﹀N(}xď;9c>&`^ ,_J1<{1a3g/9{>|==V6Z7NEcD2pp"ua\mi8us\f38>0}_JKQڑk1L1qQ\8'|sͣV1sLޚsY1~m ^Jǫ,Th~1Y F;P9Ȏf0lJf`ӀF.3'wD2ssg!HV zWWWz3'QsJG8}}R$rH3%J2Wj!њg/j0amN<KxܯMF~(:)Wٗ|IkڒNv8qjHZQ|l9O.\xǃ{j>7L Jz PH:Kyj׌ (JG3-eK]*KC.ZbV[G{:̠%iw}ǠAo߾8887!@$(e !f.:Vb֫W//^ñgϞK&dB!D=2/ ==aÆrJv튝$nBQH&BTt:Z--[ٳIKK^ $$mJ!8ACD p[##fYPfHskYLLQSq(` ȸ(  ~C3glmmQ*cii8Ad#Axn#h$I"226mG}6j-ZoҪU+<==133+ADM爘=R&**N:Mՙ?>QF/ 2666.]KKKJ3AY %@$ODEAxtޝ&M0zh>3ϟO||b72Ɋ6n-F9_e^3WZ/Y̌i+ȱ,H9WhZ^r<* +k_!AT_2zh)S VVV2sLڶmjgk3̌բ2&$GCymφ;Ƨ BnDHĒb5: D]V<Ȓ<޸ɱlqHWɳ!,?y-W9-}h]i3N PZQuU/P̘Xxϡ`zw)jJ'Cl8օ{3gFa̘1ԨQ;;;J%J֬YC͚5:u*mڴ:>ޓklsDt釯{Z)؛p`|7L>nh-gϵ} ?m#]k -cewu'3?58uJh,-\1 `fIcRq?>>?Vu^kDRQT>mnnNV:یFB MbX_S2HDJYt'˫ŲeI=_PǪc 4T5?/AxI=w$I, $le>ڮ2^uQ~t诱`l{~G`:hΰԉ\Ï'UhGRyrXF I?0$I,  NsZ"CFBF 9{wQ4ή`0UP`nyT_2}[kZ5neԄ}ѿOqlRƶ&{{>{x2}tw3#PE6=n|.Dche蹴ls.:Z>]y`ӿ̓*&rXELOQ~> 4*k߶encAx+SIĢ%2phF!:;ĭstr'kjTq+<.L^xjh+yФjʌ^]2Ff[F{XWv$_,nmgAKzw]TӺ@?L #UWW8{6q6˲Q}҉1g\Tz}5sFU@Tcemf kR7A4SedMݞtkKJwfkU˟O)sC2s Uիc3l5&?(i*pg'g_ҭ;.^-;`r~ƈ&eQaS}_V긺a5:ү^Lǖ}.B9Zϙܯ1n=hLUc9ZEɳ:^M9LZM[7w=({d ze%V.]{-EőZLВ%6AxRd4nq oFQ`IP))), 5¨a$Hr*p-\ `@fO{vm\{˜9p&KǴj߷?n)?2`&nw˴ۻ¥բ2#TV w<Wgiݻѻ>nܘ?e֚C? Yٿ[Nb5ZJeLeF9BJE*IFMߞ. *T~9[!D(iu4ū _1q’Ù-DF͘v%ʹdj*u}5p5E֮s^xr`V/YiuƥIu2j+c9OX{i:Ѱ^w \mP B )a8SC s):'k~!,1lJ_$\J"6)&hʠNիJPz lעdNN˭߶eWVwqt]7;i+d?Ha{38(Ud4#159GkvٽX{[pl=ٰ&m{WFtr@Vfodc e>/( |rWdو˜[IwL&o2A i5(ӫ;y*j9o3P~JQNkMMOAgZ$I,:٨5 1#)88rb?6 D^ƃ`N[GIuC7ؐ8t My;A]#eyk ctudrqZzx၇ e5 4Zor= zJ?#նTI?yDoa?iȯKЅћ" iSZR &=&]#AqFеGRƒY`裏IIh[W-T.h7Br,P0m}A{XiqXxQ*[k&kC}N,F\ttͽKuA|δ|1S9ԩ{9CA)LCIW_uKdtPd)j7b^t1; /"h+Ih4OCg. Hr 1RvTF> rntpP 6CB$a/sw̷β}R4 ,;m]J:p3~t5lOưbʯ\IS8'sS%L^nԎ\¡T6m1;/u<63Mlj#I2rR.\2_US;cOW̋%F?cڰ!L)EFS`圦|&8QW?,3C٧fclo2I,TC#R*x$1nuA_QW8cf\pͱy~,]Bt~vbS4`wѳUaM8&o,?mO5)zz< DBP6MR JqOw8]8ez\H<RNhԚIoSGܳ7CdomMz3@"[!rDєOaYJs w|ˀ7Iܩ!f-eY[fjfPڍR7ke!40!>j3(W#hw^ɄvD֢q4<:ʪtO3ka~?/ x*Qi-ȴ o0t_wٙp6#Fm8oc([ W9?1pg2˾,hi9y)2`Vz19y0n>?|!xtEcyZ.f˝o6LB5 ?G|2֟Jż5ӿVl,Pn\ߧ9坫t,I&44Mf㴓Wlm0*Q)R2Ei "_E6h\5! v⟯Bokj*ZbklϹĞ(k/c B-~ofq $%w:lcѽPߧ9nkHIJq9A^n^'n]'^VZOOOޮ6 eHNF4 1yL3G&!d=k'n4`G ݋&(T: 8N$DXD_<w\qk9zU'Ax27:3apNJ:I6OdHAҘ<d|p_>wRkZ }(|y"/KˑBQp:t F[enub ]ӎE /M'I%HJ8F:ΨbP/O˘gAhI<n;,h:\4AAA(21 p$`& PxEe4Pբ&O n   I4ڊ$I,>H,\[h⣁oIhkH9_^0̘\K9WhZ^r<* Ky\i,"|D9ă ŨU゙ѵZUr0фh6uH?opAȍhIX@4^GԊY793nT+BPov&`IK7Z.b33M{yxϡ`zw)jJ'CZzI(h_,>ޓklsDt釯{Z):p̴>Tw\7Dig.!(4V曌_yt'3Ƈ6ԬEB߰#J%y0фs8ϘLXoL܈@ֈ<'J+űKۺ"3wdTCb!91QQiݱ,%EstWާү?>=I/~cߖݘϨYgI~%%<'rӦ …pv lB7=m@3w΅qhbyF =ÿlsZ2{G0ֿOsޔ6[F՘ Gw-crg%8X1I1e3ptjUv܋ٛ"<ĆvaS>%۱5,GR&msf>g ɹL*"yXpͣ2=̗ۂ^;ՊmC&ykh+զͰG#fj{Ӭb"gEZt]Xexl〹 B/m[; Ag2$I,:@X,A>lM aDC:gM'1xzzbooGM&uW/<3 xmNO wv2 %E VF?X ~upu,gڿg3Po{3$ٿ"7 X0d̴iSRó%60د&^}1Cmy+9!,cڊ vcx;7`0$5{xHd+"tr5iSbU5w/q’9ҍ9\#cQuF+qcִi_7xkZ+@iO^G?z'Z.V]Xn!bPju]< ԩ1] {$Gwǎi(Q JE*7&=]"U-|p7_tBPTaH>gc[_ùd5b]ׇoOPQ ]#QvȲ]5(( 8LE\`8wW.i*j9Vj\T.v^;x9O8 V wFz>͖ip4A-N+Xsql!bik{t(\o BLPgo!e4" rjQC}.EbOQ\#dRe2 \Ubj-MƘ,ϋ{(Uusr ]r~n8jsmlgу{f4^SbhmTY|כ8W+kXhY#us+XWJU4J @ΥKSTg=P{| : e,K,.f<äs8L\ߠ7dʃAydM9 }?s]Ji0)Z?lsԟcxLQ/rx!bTrqe*dJ)0>+K\Fy_1d%lR+8>Q0=~ p')WOkzi5]x^㵘HĢC수Zc S0#w-`ޘ@tn<ԽuPw:T*X̝F([>ږj~9G6-'M=_X2ݻPUcʺi -h&"0;mJKXؤǛw%sij5tQ綔 h,q0ǤW-ի~*b,9l7߱rT֌ZKU<< (>>_2* DROf0ޒ}8c* 4u>e￱F~WQa4=[=?𽹄$?tiB$N4t?$ S(enGEj#'&*,q _oKrglW}b1I~L]_l2+u23ԍ}j 10&STrM>t|7"KASV5Ou ?f!xưbʯ\IS8'3H agyNNeЀUG`Vw5/㘼9hdB=J}+5N)G4 +\Lo񦄙wDWe./M)HGoٌ :53$k>Prΰ`j4bW+&Uڄ0h:m~6r]0h{99R+|܊waP1hoɪyb [hOz64Ϗ|ߜU}2 y?/K]Gʔsĭs)s$)KRjzQf i~nq JwުMO_}3['~mkn,OT!Qo6ͽ: zhlz\H&ps[ƴzF[!jwct;9K$ID$J:C1H`@i-44&kq N\2UckJƬ udܣ+=^?3{C+y՛ZM;%Xɲy \ʛ*Sw$F,kvr趍i<⻎u1a:&zɝ^çp~_eQB1s=ɗI9b.ۆӬ|U7]aٗZ:d{n !oNElB K-s 3.|T Wns+#ǔ揻+wQ|.g6>Z&涭]`ōZItNepKغ܌Vɤ2YG7Ĉ¨d'eĸќ9nǯҖ=e\8gA&vgSG/XL8zSCU>QCS"y\Z=W|?拚g>t8'PUu "H3 >Z#9iT./.}S޹JI\$hBCCti61N;y"R6K)zh=)I'ߴ'XA _E6h\5! v⟯Bo,*ZbklϹ\k/c B-~ofq $%w:lcѽPߧ9nkHIzZ\ Wx!edDgz ڥ#㸸&Oc-C l:O=3=YKcKۢ&K7a@;Veq#eI?YJlXWZ)!6omIeO3|]6t2r4V2DmːD$EmʱY 'c.+ٿTlFx}g4UO߿^lW]v4r{-10mu"uփ|ceOj9[tm?K-!#v:z arFXN gy߼²dQ ڴ_YzB:} *^Fgg5_ΪEubu7xajjc-q igeHNlnqo=CZ)ggLBz;Oh"YsMPtRmqHi<y.$9r7OSd_a9n+y CT,jc.3yNAv6 {ؚ~~8KݜT[ǀE8x2͖*󊅂{9^g^^iFR*gn:%%Sz"Vt1g!iN,M9Όy= Hjһs=_]Vp/B>;7d @kA x˗uXcw^9 k/[gSG3KF㈷uƱ8eTDy+`s=Jxz-yl-/VIw,)nL6-++SY>c5*=ߟG'wtkb#cQ$p+NYWm+¦hU,(:,0mwQvĜ]YK3"p6_;(wL3UQ BmIqzRIѬ*3Xw<˓2%f#񆧂6YfUQn۲w;]dWG롩M[=Rh0p#5\Jc5,ݥ͜iiEAVl@ZmjTԴkP7AzOrsg=U`Sw;=_ H~rۙ(Ut>pu!eMNhJnN.k]p Cwl0f>Ogҭt=4*9S>- 4ln n5:jn%ѶKԨSBGT聤˖~. O0_ذ霊LoEnYONeP{=^5HfY+2ЦDi[{u )LxWfh;݌*6g7Rd.mi۵Ӗ|_嗝 9͡M6l{4.=!?gvK>ə ]XT !*]'/Gq BvT8 ;;;`'luIA%n%ɵRqsRj<)HkNÀLي:0b9ܖ-HwBBKaA(R?5:ʂ1ǖIؖy5Ӹ*3b١4_{9KuZ$]8Ą͝Grn1k>kkŮX@X\^>:O 0ߞ9 I xgW#0I$ Z-l yvf|1BK‚A s)hd=R*0XA)BϑO;iIaػt J.AA^0k!pim SeSf'Ɣi &m  3xv_a3G)   6A˗//AAh  2 I˂'"I%AAAAV$Ib"SOHh  'h+F$h q4rmME(>_ 7)m[x @y\i,(sӖ $g/I5c9"M:C G=~vMSmYHDnȂ7X| ! hZ*Ų {pјqAF|d44g]YLj:Dn2jQ%P4Fz03{ϤeOoBvE-o{:9FϏm(ccDo+$MnU5e+c D亡MZ,;^Tu=*NJe- /S"$Ibip q!rl#(0:NNu+-Lٛ߇*Z-}hZߞƑ{@Ҷ4L= d8d9Kk?ƿKP/-'L1PYE[M>Yt{1&aP?ihkcHҲQtIS[ɛ7FD9m B#ͿY]PU[e\]7v% =kщg񥺋m~tuysY}<<ƯZyeamYQV[&=aG>K_acJ8[|7'5ZLɄ&΍[lȹsɳkNWÞ[(=|39[/b9Yafh8{g*ϓ,V'~Aj*QkY:G~ƶwv{ @5x2ɱl\+zdZ- !ͭAJh=$SAJĒ4U)2:d$d$ wuNC;px˦'t\>{7iπOORطe6~73jYQSѿ?uc6|#aQ6~G>űKۺ"`Q}F3fn "xP4'?Fslܿ x#z=ʑ9NM<#gYc\^[ΎMFҲ hFι0M,qǭ)؞_6}ЏQ-#cz+m|'1aAZH 9)&$JkqN 1]h>ǂv1{3Mf4,ۄ휹|Ssd_^0!6T/َe9Zr0nCõ69p|?h0O`R1òOaLD!V|m2P]E[6m-$8:)_ 5[U#㥘ڡ>*&rXELeUQ~> 4*k߶encAx+SIĢ%2phF!:;ĭstr'yviSRó%60د&^}1CdM_ۅJ:ٷp$7c6Ϳb@?jzxϰj2`Iݯ6n+޸' i\m "1m63گ+k3w dXrlqkZۓn`iPi cJ8{ɔ8r^I>5]fNWzcxf٤|g=`[PB1d,2K5rŋ67 IDAT}<p\\n1uѤ,*pj3q󘘫Lt\ݰrW/qKc>fL[L4`&_˱L-עJ|nmgAKzw]TӺpjg҂lںA#SG./ri2Thm!.*"dx|8VsBY"KE`1qGu%A$8{T/BT "ɩ;str5ju] ԩ1(\ {$={ULÿXw:0wFgqc: Yk fB* yVU"fe`#6>:!qg'QċW'Fֻs4f(Tx"55%OGZyB ~&~Po@\-So',99-M.ҌiWRM{Lb]ׇoOPQ _#Kۻբ2#Tu ]箽0^[E-VKeܡԫW6r0-rB?GyXy؆N#ُмۧ/V0 A!f([0^N 1*tTsϥ`@)kL*l@@!+p*\Lm(P2 P('Y~?FhȪ໸\Ź ]^"-ٲ6vv?=9kvOL>L dd!fRk[YKRa؏ FRx9$t e,e~EQ'|FdU]BWLy1H9̲i6}gYӫ;y`nZs*j9o3P~J`7߱rT֌ZKU<<rgZJpԽˡ&ϏGo7uB6.[Y3㟽ȸHwNr(i1/b_h$It3pI$9ɘB)s;*T#^?t7QIgKNz[:8lXի ef$̃^]r~ n&84۷9cx:hP;|.[z+ר5I\(dA(w46⪍&Ialrxjv g)l' ۘZn A]Zml kƍ ;9j:U^3/Ujþ0AX>VOun&8QW?,3C٧fclo2I,TC#R*x$1nuA_QW8cf\pͱy~,No0i +ʕd9%~"E's-GWzTYt~g4p{Ƿ z̝mR‘W1#ƴ[tM)~Sg*cGM64e*w%#j3(W#hw^ɄvD֢q4<:ʪtO3ka~?/ x*[!iba<`Hm3I9b.ۆ,6up5zc]aٗ-PeKK"f #qc>˧GWۘѾ"P+|ݿg n&v;vuO_vScaF׿l/x2ҾQmET6E #6oКebo\;b;ò<8`ּbBTos-h4Dtt44Iʵt!ߦqOtܳTvapYX9<햣c 'L:{3eԿHG7#Y1epS̭Dz-=*?ƛO\=|t=3HdTR&n3G!;lP^U"+ȹ<ߦ/+H6΁Q#\v rK࿣_b6.)-yu[ 1VishuQU"+;!@GVC%{3WRexB! dx"\9w*gf~6͘?ź\^׍^q !DI$iI4McΦ4Fпs3g [fNEn}cqv&:m|ѳ!=Q%umf.c?Ynf.e~}LCM\Llq O^/R~'ĻYy/ EbvPxAɥ ! c [i71!q' Zʶib_hѧĉnn7*_şMx5q7q%WG }' pb$?塖Du9;v$M6ը'ɋ<8U|<ʫۯE# B5-*N^^vc^Ջ:-)iD/G$fK8\~~ L{ f3-\+h{ksűwfnax$lWc G "aޅt؏ߛoEæ˴JvXʿ(=eޏa f3"3B*F ii2YN::Ξ3?DH3q[c?̢W|Nj#%neis"OLqi v]H8g_ŪXȈkd˪IY{6ֲeL,3e)G1'?hr;9|#\'gB;c.e^Jv ;-4&eg.n/CyS,t录sx&?_rJ^ߕTC{(-Fcr-!` v:7jk˘˗Pܸ>ﭧk`Fv2qR~Z"}{T#^%8Oi .CnϽw# o=ezKxƣԡ⻌Ľ2oP]nK{7tؘت" 8S? R~n 8znM0vD5imrq-O.j+p'N?2-z3aJ/aVܳL,Yy>P8,P3Rs{Yf_N5!( [*[hށlfΦx$oY6Պũh8tg/ޑN RwqR"!mh͞#98t's,GJHay79`0bnYO {Ń(^X &3];>W;:`;+bLDxYHSo84ߍi)=e qM~qV./`𡦇yN^^AnPRQ4tؒJ[_O|VZ J{IXȋ'h<{_beEV&$G[=[#p!s0sm58Pzq mt'5w|ǃ|h?rYv{vs6); [r]e q-W_b]yEǖ^~YCiۯ_[-[_)\nwEڠЊU콬>aD6ӠQ#5jD0OG>8X/6DqdiYpc_ ˿nߗ; ]oB6Փ@/8w=e#\Lub0;إmMbv߈wi1uIufǯ6l{ͶߓqzE^Uz;JڹH{;w-85imhY?o)f⸵2l"QIT 7&EoA. dbښq/vY_g$Iu4uCgvq,{nEsZq ̷19$d?|  b1|1V1m[ߟ'c07zNwx1MO(׵s'MfFl!np eϗYishuYH n!*ܡˡhv?#YzV ωe\%Rq-.i9%/1MTgܔ -Wo+z(A*ޕB I[Mz3L6!DuaTف\C%րqS+ 祢U6 !D'D2q[Ag%aB!Bi EOMiB!~QT [@@FQ6!*XU&Bq$` !B!* B!B[$mB!Bq P e=kTS'B!P60Y>5uYt#cR4&o(ϟ/!کI,-\OGQw['y SL[BN(! {$qJM!QL4+ˉQ:Ѡ]Y&‰kpZmYّ !B!DTͤz@k?r{#BIʏ&}#n}3i,# zU-B!zG:5'ÒGhhXv02p-`OhdXr.?=o ;1͘c3r6?'od$tImT/tC&> zϟ_!!BtUiaIEwdAwx6(Np$z ٘L>n^8,jA-{@vjkw&4dNvpsFέ n;ݼdNB!Ӧ;VL~8@ACMP[j `Ew@w^磧k|x Oݎ-hQ~[8sv1wXw1Yb/,~"23I=vt5 IDAT= GӳuDa/^D3h5;L%lf9ҧ u:s2?f_~@-ݣu1P﹒!gV!BQmذ@6lz-?/t >`/ X PY-(wP ZU:>ߏWn5_ՑwfƌzE<=[/kŘ~ģ²BF_CԸ%lu'[VeL0L/]h7B*,Ÿ|& ]mD7d֭r>jۗH| Qaa4l('u!\FI1zF9mB!nȸ8,XI۰a `eKؠ0Gp/I'7ZgM@=/8HIyPjt+5p:)c#HNW}0׌Ęs]q !B!* _h6eʔr&l! zH;@A'ZUk6ƞ1k M _cheBٮܪ}nքGYw ҖlOI?џ0|,q6߱p_>bPAw^;1l' :Ӡ'E##w{-1N!B,םǢyfuL BF7MY=B6u'lQChrv >Not/g`rn &5֥hy z=2Wmcؾfo?sNً=Cj>~ULɘ`?ɳIٹr%M`#7ܔVl:Nż6=}u׊SJ sڄ*[Z~"n0PJU.{]Vz6uVΩUo1= Q ƇIWl,]bհw>4o8RRR')*x iޓTR s8NRr wxᾮW|S3CGA{g2o"_̝=fP9S ,:׹ޝ3 ?bnjΧ'q K6 C^3eόkO~{]E [_#N!n>&ʷ +d$ oɪ|4U ſ?g`q(z rZ*nvlrLy mfTLzVF2pf:sԑ'G+be9 _ٱ\k!~*&zx9vpgDw BQMXrYl[sGlg~X Ú[~]囿l`&?{ ^'`j<hb具rΉsۓ08i'SXl5 x)|3'Y 1 VQ4s1 6p+02/5Y'v*p/fуQ|Yr@Vs_j/mvN2% 8E{QfOZ h)_>gfTjjs- s9kuEqb0B._!BΝfbo*8UּXah";2}ʴG|Ogٱ [W_yTYC|?asBZ{s0>ן͖nL@߱&n{,Ϧk7Cv#\j[8s\\XcTd䭯'mcU]EQPP̗@ g"sڄb [!市-͝sзF;waٍykUS+Zr!V om Yv|&%C]|\TUsu+.^uFOc0܊CB!tm"[;qS#K6'ng361(ߍ62 K[lnޜ6UOc:-`#\xڮvfSM]S\*1TTnj"<<BT0&ǑcA#ظ‹ ޹*]CZ`2a߸q䤁ÿ3u?>:` ֈP,`d;K]ul kj^6&SKA~aI"a~sP{"5 xNrx7 yeC;dȪf͚<9mBQyM~5Uxs9vt`Pt};ςxmlMgZu:+311ptuG+l0Yd4ԟ. }qc<:g^R`=ƶ 9Z i=CaZ޻Ӌe^Lx!>j ^VET !B:iBtW>OVn([ز 5rB!jWvUFӕ 0Y>5uYt#cR4&oȳ߄,ϗ!BqI""DI,-\OGQw['y SL[BN(! {$qRH&Bz'mDϨah.|xլKPXRm5DyC6`B!Bz'm3dj4%^@JR~47 >u+7ҒYpOGZOի']!BOr KA` `w] u?kaזFda oLZsF$.FzѠ[z4񑁝J !:.N;K*&j6# Aq#c`dAwaIPj:tj ƾC\e!D9ɜ6!BTGշMw;<@qb% PP @-(wΜ]֝afuV/gMdk}kll6ٺ'/46}јfu[r/W,؉Wp`R,f3f/Ւ̂Q:vۏ0S$]~Ɣq40޴㖞qphz(!s܇E!Bq[:i۰a.lذ[Z@见}^ `ZP 0G 3ularTo<7C>`֍,o-t]mD7d֭rcMv3];{%|=!\"sڄBQI+a@ZFu”{ ä6$7 K7&18CdI7#xԢb]r*ݙQ=V9‰}ՇNҦW+9_gSF,ŚGӭN&k~/.YB!I\M2 ywh^+Ҏ'PGu4DpaZB@הܚp(^4igD*aOU=e; 1 ԙ5=1( ui} *ΒwMaq# KMlw,ܗ!B!B$mp)q:uj L49HO; t7T309Qkwdc=dn b1|1V1m[ߟ'c0ԾQY>9f$[gkNApSGX4:yKcF?Cl:Ǖ&=Yi0yR68IZZ";WBt Q2M!QzX8RRR*=p=ٹ[N%`?$%,g۹;x[&:ڶWsm3{/_\DKܻ#Y7G Sa֛/2eSp+~#;S6̸4G`X,-^Y#vH|zod˘0pߵsMS ,:׹ޝ3 e%qcɜ6!BTGEs;b;^cD:C2ULv+#>r"_~ TA !geEtzo7^ab0 k^n !7[U7\ZEGQ _3!B!9$i;/Ǫ  (㡐/OV sڄBQIvMw`(QQU+F !O !:r (6/5FDpxgp]4iZ !B!)i;/,,E?^uӀ<0xP !B!)Iڊ1cp3 /Exxxe%8O !:WY&<@e!(iB!Du$=mB!Bq ,e-w^L~*7>!B!D"I[ CXSwM7:&E#0( :mQa![8sD(E}|XTvԗӒY=YЪcB!Bܢ$i+/dX npC:e~ ] K>X:Ғ5&]JT/tC&>2h*{'Hf.|X=;6!BTGӎÒɤ ; xD ٘Ln^8,_OΊnESk7w Pٱ\=_N8 Ie9mB!$i+/݁b jP@U@-/G)ί>mh`K{طp4=[G`61G4#~|ɚȲ،l&uO^2;tb43c6ק2RYr=6}хm̾\f`'^=PID͘V#YegLG3M{0n \Iy{͘øO>\|al׊'7:~\$wOE0j[2'yHZN_;F)]B!Պ$mP sB@? Ղbp>%;.}w$Lle5D[_we\t äL~ߛ!_@WqF7^zH3q[c?̢W|Į6_aV~9f;'×.j艛HJI!et^&Ki?.-4&egJsbɰ'x?+̖/791|ڛK4v[B>to6`yw!BjHR|(l4 Zn4JhN7VPl|͠xoWx>>jd?E1VD!<=>܍p7pwrټ#ȰH?cC˴T3 m@d9裟2oO^)x|0!*,e..&8AP i_ ATAt2#x|jQf=yGe<'B< m_n>1v&BHRW@_N34o3X&`={4_px@?W͈e].tF7|ǚftvKz&;H=ƿ\ zR" C&(ߏwzon- ?D_ENf 9.,nj‹ >wힶ$+w{#*E[r1w- kxh<'x#Ȝ6!BTG)g ImHn4oLJcpDɒoTGVPENgSF,ŚGӭN&k~/./9kEXɨIDr CAl4K_Qܧu\eǼxb:+qEqh{B!"! zH;@A'ZUk6ƞ1k M _SF?IĦ}Ard+y1[e݁܋Ҷ=ݟ& F=$0xPgĠhd$N֥1;KN2Kr% !Bȟڎ@`Awz;}A'==v[0*9LNΕ+9lPFvg^Rطn19U#=Wc&c$%bڈ8P?Ox]RW=ŠM;צ'\2j$e&Nlֹb+0>;IrBRQ(7xLĵO@woٷL号rgaځ(oΕ\aId=;|={ٳI៟iB!DuT)ĵ(*x iޓTR s8NRr wx堺H[9o{;yݹ< @:OYoȔg{Lxe}38&H d4(Gj)xf\{#0,LJÖw/$~ĎɝOO AlSwgNwͣ}Ǎá% ,xxf y{G&:Ng)č#Y:MHQ뚻~4oW;޻j9mB!wv&$~e'cM?/_dR[?R#PY IDAT.U7N(t=֯dP .~]ʲpD!y^]-v˹#3?,[̃a-v?!Df?w>z"۱b)NՍ(:`MWK'nˮ/Y~9[-/c^Y. !Bqk9m,Ǫ  (㡐_EG;:.߿Lg-ƕ:%m1U59l($sڄBQIBs8 8QT0*:Ud*犑`kRo&atspk(fSu:7%sڄBQIVjm^0j]")#ຌiҴB!BT12!B!D$Iza}kғF@ǤhQ 7*Lިg Q͟?_H !کI,-\OGQw['y SL[BN(! {$qRH&Bz'mDϨah.|xլKPXRm5DyC6`ȅB!DNڬg> hJx3(g, 79"ho7|XsC^o~B!BJQ}Wtj`9N% a0d;ஃ[5ɰxk]sR^+1ff37s"sڄBQU͎Òɤ ; xD ٘Ln^8,഻TExs,„O?,|3bnwVdKoPB|2M!QMtÊ(V0(` j Pb@ ULf[NhT~k|2ulEfm,~l2uG)ί>mh`Kיena317IV|k"^}؆=xMfLP|3ohzl6chF-1W!BQ1oҦP sB@? ՂbpPj񎴵|KðGh^tJNCx؜/Ρq 'Y7G<[GxMl^8Âs^X3u/aZleF,9u!ݳQ5n ɖUs- ,)B!-&mŖ-xfAۍf]ö-7r -o)8)jSs{]'=?jѷ+<I5b9^΅i?E1VD!<=>܍p:Bx2M!Q]=R5_=}IN:Ah3hhchzzatg)z.%mecܲ.$Pd!ã}.(FDvtb%E&gaLXݮw+^6QuȜ6!BTGշ {EŢD1ug'wE;w /!ېexr)r1`Y\yyyL({y9ȺwLG;DYVpDc[QV q0Ƃ;:{q1|՝L?N^V\; n893yMZln=t%"""""ҩ4=eTx]DDac1m7!"""""r(ikT5۶00 7h[|@kDDD;R(dY, i1RD,iHI[էVi]R5_`O OSSٟSm?%Q]DQ(++ .ZW)=G}t"GgwODDD+di"7mrr ^CScgb{cBΟL9'?uD]αɻiןd~C' +Oe<1xf>B= ˣOPq+ڦt=lfϚ… =;W"'IGPő[GpN{D ],x1N9eW!#ϼ˩ |w{ly^MI vXᠳxK(n{rR<,}Vyq_&E1c΋<[{䷿gTpL>ӧ+ab~'sǧ2Hn;+i$&WGvgϓJ7'FB8>}ZG8ftj(TZ S/}#6DA8zny7/XzpX>&vêp3dҒHWx[Ӣ#7K#*a -x_sLzr#7&UGr~?)L&P’~rν3;g']v]XBWθߝ4g1ef)r V=AU-*,^˛׳9R̳ɻm[\S=[AΛSoKr~ CW7mA>7/|ge\I^,;dYT܎~LuYwJYrcXСyX=>.n=7ss3 > t Lݓi|SղWX "kWh!S4LČ:~M5-հˆ Y1[DDDuɧp a:\Sd3:~>5@{? guXL ?ʠ(W=j8g\tf533]=?_y-93:Wҁyfo;vOxq}%)5l.ZADɫd :Q&9dt|)<+~M%Y/?9=l1\*XbmMV #9;G p7v١sl7~k~/mzbW~ʼU0i-?9}oc{i$5o n*KԻC@P+>JHI3"{"a{:debi5"""" ^6Xp%>rm-wQ˴lɊp?gf/N^lqJ^[pg⢝+v߉_Ϛ?SgC>|A0 6~o;z{YѺux@t 6:)?NNڼT=*?/BPTu_+R)xX{71}:eikg_DDDӦgl8U[L+=L p gկ<(T`j'VXv$޻Nr~?6'1>{1é~&zs)V#73`tt[گ&mVj)'== jZpCfU]NzZ"5o]lQC9·ԞzVp KOѳxlu"""^BrZgwAکvy7i3p|mo@=[e5 0yk0`8Znx{Iˀ=vuP ]y&<{5# s9n *፽wW r{9mH,fm?y!׃Ou/waGڂY/wX˜|>æ.n`g9yr~fؾ￯ŭ"""R" "wM !X=A;|o|j\x`jqdvغ3Oo>Nۏރc_ggx=Zi~h[?ˉ wVoL9OMIC~ugMF,rǝ-N7YߤSn8pp!?:MT:{qĔNK%{xN*-rgv8NSPץc.N紉Hw}6 O&dܵ(󃕅̥$(|> <JD[Y_.rɾo`5Ăֱqsk^9xX}]hG zm AJpeFv,6GdPG"(a\|T%F0X7zar1`Y\yyyL({yھNDDDDI;3} e%[mEa[1`QVVNL 9sg;9eyݯXr_s.9^J 8.Y7ϯ7P]^u/_0gisͩay<ߥl<HDOso 6k_?9<%;Iѧzk!"-К64&"""""҅)i”taJDDDDDD0%m""""""]6.LIHMDDDDD S&"""""҅)i”taJDDDD24쮈HWc`[Tى閒2Yz={瀱C:HuvmQo)i9Ȇϒ>e͊,#"]i$qؘ#vMDDD dܑtv7D5m""""""]6.LIHMDDDDD S&"""""҅)i”taJDDDDDD0%m""""""]; """"mB!eatvgA6`&. vb JDDDD< Q_WK|j,t1M8v vcDIHRTHbr*L4e_YŦl_)I1xǴ6..dYTU2dx>/ dd0 S1i5Mtq`Yvȅ8 kvŴ&JDDDDDD0%m""""""]6.LRѵBv=EOq4FDwC4]Z׈(l` 6ɀ:sMDDDD>勥ƘRj'?_ʝzd 4}͟-`KQh!vHӟ|& _lǓ پ~;>Ǟk%m""""PVX362=0!H|/bu܉ O5XTCb8d䝱 -4{~َ_烘ޤBmcX+i,Oŵ1LCǟn0}AB6`RhےWj!GF7-]#OCi* leoÐApW"&? )6멢tT7Θ z u`MT-0\D1xx&`նrAŷ XWcxk fĔQ;:;fg,c52wjJ38@َѺy0S3qd2ލ߱`+0ˆIȀA}vJ,!?eDg0e-5X7ak%m""""Xx @6IDAT !+) KWosl >x*^ĢϾ""~*Y6Ú$,1kVSn!!d0IuУp@X!Oi>Lbp:jؾb+3țxqj K2D!+W=XFLf{H|/& X38r`WCPbBA솗Ddn9[Ib^<_&s^eKLOX!qoj`F%鮢oxDY\$XzS\EDDDYl-߯/VtT.f՜c͊;MEAʍ]ngG e8o$lV[K||m>U[D?h6Dgw68B!w& 'q8B!l rӴ,۲˨(LZ`᩵p`ˆvj*J ݳ?)qb蝗AhVBa~Z#m""""˲3kJ Ί"T`TI//QA &a*j~l Qn5.WXvÞ"rb%5G+fs8۩ ;|͗3߲r[HbĩN.DPm3[6|> ;hkkƣu ;P'dj+[Z~+k!g&_0NIHw&q Msx۪]E+(3a ϶r}b+x Ji(oaW=g)N@X} G=5{c[!B#TV1`t,x[ =j8 'o9|1Q=gE@lةTo̧LzİϜ:yC8l 0Pv ^XkMHf(/`;J¶BV#o SXEfn FC-9Ĺm1qd$`{'^I\ϞW/gݓޙN}!exToc˺elj)((OM) SmMxT=r- wZ@㧚 vjk*HtSÎ 3̏L{Z!JJx n"? a͈ .EڵYj)]ZXkMDDD;}Tn.ĩ$}x͒pz$q}VF`7Y\RN s J^^hFt&Q(Fv\8#')X]ae8‰ICN\au&5ZWGs҇O_ME€#k%>gq>BfVf~§56D`fنn+U֦IAUe>8ÓHE,'}#kR ;޹XʬIA6CI'ed'WPIt0%&XV ˤtr\1`Uizy釟D8Lﶝ 81::#g; Q'#U9>=R*eFDD0;D_'ԋcհW @8qه1km19 #!B~/{Ì}2Hޓ4l쐏Z W=Gǀ"&0f2r6n/_e6Tص;[^+oVCxˋ6 x߲mv3"W E=(jk׶tyu7oZԔR5Į=U`V*w{/䩤ʳ뻞Xˇr+ns6Cc;ιw+ZG[ / R_{^zgb6iM#[[s&7tbpA|Ue("JDDDDwxʊv2/{RSc֮~<Ŵ;m/"""""҅)il4 BaQhi6O1i=Mtqi׃WY׭".>˶vcDkDDDD8MϜ_֢B,K iD&Bbӎ{LtMIHg: >l,R?e[PH1 ik&N{""""ᑝݍnɁcB:bZt7%"W4TDDDDD26.YSY}Vh[.LI>G0I}۳tPr{G^̓'7OC;G76k-X‡NPrO+'xa.?qGCsnB^s_<"""""_vI<W'䩭pΓ̟/̹ytV/<cg0I^+E5{}Ys2{__@ ~KƟyҾ>C[:%Dѫr61ˈpEZ3i n໾W7˟bQC2` nOx72md蓯i:n{/ &Mn`oN5~s:)}m[n]nUs×}/s1̻z^k;zW "/y&"8n~+F{mye96>vq]KA= zXa 3) n~po!ak&7G83iGdֱtY_DDDDDY@d5%0W .౏rǍ5qW2 %^s\pDF89+pP?>:u-\-\TN}o?^. zedpT-[pθc"k\I$Dzs&g2>vߌꤟrՖݎ&=YdBuqk/"&!\U-Lo~gbL>=X_Ch~).2#ciroȷ'ݫڢ%}ŐVjnr.͍~dioHڎɨnmWv*jg\͋׎$zW7yEțH4Sv&m7at%)jcU嶍m垏ezSz떵=Ӂ#N|ٞ5HynsO02ǝ̓s/!/hm2t3K8kqW:?<^p'1RF_u'ONo{{N6Ͼ`dOט9ov+ib8w9o|Z |Μ>ovU/~s>Wu!J>gXۚ9ڂva, x&nl$ =z":ޟ1-'"""""-iQY[%HZx>ʲ_+iDMkj*Z|MDDDDD EDDDDDdKdk5&"""""r5%R#EDDDDD8J,IENDB`./oar-2.5.2/misc/fault_tolerance/HAdocumentation/schema/hb_gui-2nodes-connect.png0000644000175000017500000012362111757171206025774 0ustar plbplbPNG  IHDRmNsBIT|dtEXtSoftwaregnome-screenshot> IDATxgxU{@*,%B/ )w/(vbT *XEtBHH#};)yd3g'<{Μ6UHljhT6l""""""JDDDDDD$,f3!ܙ|iqɿT5RZ]*y,ۗJl8FFUV\y6v[P_}'NZ[fE^4N*^LfM<g`qjC, >ޞƈzMɚwïhzٳH}t[9\ki+L /|:!z:vrOZͻ\ȉ]z׹Lml'0rpÙУ^Ą)|(g=܃ޣf_)fN~x&.ue<_ŀ!VBrĊ{~rg~-׽zGɲN6} >׽òyq~NuU=ߝ_bm<3u м5]q[zf4vS`bRzu)K2(̽_WzwMW+NЕVE/c벿Ni&<.n7+S+oWʑzz/=?}i]x.&}{]t"K{8kВ/iVQDDDD|O[Iw($ZpM,xGaYݚ x.mXh !]݊Zsv^=hmvgf[\qYf~4L|88lp:2nG`5L. ~:C=m,U[c([/n--_XI}WYUWh?^ߖ3^{+sLUIDK,7nӿ]|~z>FiL";lMŷ/UvzQٟT8t;̟9#WWqGY99{+?~a3oRM_焪bԅ.n*fj$!>pґrhZ9N|6BWR'&e^Vl%'4&^fIJ5?v5X2+6;o7kt7m+W+҂\t\&2V|цM|grbnѓ6[^xy2NgI[ [mX2k#*?~ڄv/&7G>_ 4r3K<Ϧ곪,֚GzsM]< eӫ讴0YtcZv:QǨhJ"fGnڧ`?۸Kw_E/ÈĘ z$>g; -`ꃯ1|.W->\v5r‡aUl۝55Y>9[G0ƂuLJ`o(-QV-gsJ0ڟF?rж,Ax͹ط=*=~džx9;w6EX>6' ||7f66͊?]gUY5_^}'λ pWEoZtl)cJc)6LkyVL޸S-3|^>=$]qb^yz:M웃-!<~&= ]Ŵl/nbl+XB[8)^28'/3^ !O@ș|8󊨙M1rMx59d<nH >qc/pm7X86ӟok}7=# ߪ8+*>Jcz<0t{o>/\%ӫ䌧'q^)Ww:kEDDDQIآ[HU͖69俈%R6eT&"""""&"""""&"""""l[֯DDDDDDpMDDDDDCٲ3R;W} """"""S&"""""x0%m""""""LIS&"""""x0%mGa_ n^a48 g}! vp8,u_DD꒭: 䐝S2V5TU֭_k+]?gxjyt8/KͳMLV[CЦ4߿CiSظi>>>.!X,.V-e^RS-y2u͙aa\+̆ dGrnrwVNhڔ 2eGTD$Mywx Za4!!4 wk׮FHmVH@@@/&$n5Vբ{GӦ]NkBfeo۶NrؕHvN6i_xۼiŽmҤI'pDpPPj(Ajzqmw( G9_+O(\.>SS*]ӱc:JDD4$I"u {VSp搔D`G5'(051Md ɦIPp0\صuDbn^QCZꕧsRRp{Z|^[+/qt""R?io6e4#<]vN$%m/yg ":2V`-.V7lss yTzmYZvnw`[֬Y OMO% :Il6+V̬#Z?*2#OHJî]Hړjs. 2_~zPw"G!-#LLlܺ Z52I?nͦkۮֻJIvn96DzINnwmEg#Hn^Gρ{CvNN<2î@='镚`5&N:?]vsLJ;w{zRDDK'mNҪ\.22HMKc޼U.7jfQ"Hu>6ˡٱ:dzې&!kbϟv gZ '1i7v&u:4Ms.jRl\.I17- (4Mv'cP?'K$&wMlw_I'g̘1dff×_~Idd$:Y6ƪI://-[\rVV!G5d{JJ J9E?mۺgN&h| },ᅵxb:M*%"WUBѽϯFqp[ VmS=IGNnn'->-yy$$/k_֏aؓ9uvDGDwo9xxp8HIxʆ?9KjV4Ywӗ~OcLT&"U+ike?V:sAAay~}Or^|Qi9%!Y!xhrRdd6:̬/#$iU4)餦KtkKn^I){Ϊٖ®IIu6dtT UIoXh]U,3sYYYDDT iڔMCM>2++ ZyMĝؑ/Qߏ4 [j{CֵԊ@lVNÉQndY qNr2__BCBuv q$%'U\]$iZi՜쭛*].[Ji@z];JhM_Vգ|I<;'=gO:t(ZK_4H5G\.ʖ!kpW~:27~s8zS?d^ 0*W+/+/ 'Xۯ 2-X7o(p˘,_~7 Ss%R? mJZF{&gwJR񳶇.Gy"*E#Eauv[^NpֹT8pSkcǎ ͗]""ⱎ˄llV^V/N'V/+YXm6BCFs0ȩŮ2G!}"qt~g8<,6ZMK nB\Փ[o#GjFvq$%'m-a 7lDEDUqDGD71-=oЂpt`u7 coZ*1XK떭5!*Ŋɮ]Xp!{$ѫg|l"={tPEDYK `󶑗n G#M\K^>f}.~}'~WbzYIhN;a˵kT$u<5۟A3hM}DGD+)jIp0[gW,^^4 mJ(km? ԏf%.]pxyyEjzGy,\/BFz'{<:?? YDD%mQe'~'ן](G;VaD3Ot57#3N&o]6yT=wO>N{sݪ%߸NߋS}g'2xx֬a͚GRJ2G`@@[ IT?GNuW3mD{DLpi,{ju̺?|UܱsFzLCaa!NèMD瓾oKPm͛""rLiIȱΫ)i`JDDDDDD<6MDDDDDă)i`JDDDDDD'w3ݼcŴqg^e׭I]Ʉ]NgݻS6fȍﳥLEEH-8{dǫ +r+K{Or++] 3^aǏ_&]cZA˗p7* 0‡/%\-/ԫڪxf)Q59Xҍ dчe&v 'Wvݪ`@~cb$|ʝ-=pb|*w mKEE~HͫxH.Qx],G+nSk xMK2'OpEN[K.tu&w@ƒ?yL֏ e qnmHei6GAr\zѭ<8sSXmS[õo^?1Qp"g\0>Gx`"""")}QP0w!F@on#ĢtM/tPeo3q҇ېdC~CI-ǁmEXnp҆Qw/<R*( g2ӺA } IDATٙB "Ҹ^V?d?+ \d&w,BGAq"Wu˷uAûhyaQ\C=y 'Y&Rw""ǯCc!K>_+1ofs˺q-oqdm )/`ZoRTlw2K~vcZ{qnDyv "er6u^sg\̛.皗cXh[!`1Xfq:pˇt~_¼oWo&pr^)"""Go߫R'p-]9WEӝWم'0H[LÎiݞioܿ2+8b଑az״yGҶ8t{a.U}:t 42qAxFv -ڄ>a(W yCI^"""r;g[Xi9zq53k8ZZfӲd-OXfeot~e2׸v]ݜ3 3m+Y㸬i#=s2NF! ?ӫЕ [ (+.g }e=t̴Y8j:/zBı}^^)$& q|6\a"JGs`7n7Me<5q9cEA4mq"^?对nFe{tpZ$^xr"6w6i|^M:2/|x^M Ewgo2B;ޘNc=!gn\{Ü5|q/X|I7&'mOt/{b2c5KDDDjYeUH=YB IA^N}""R0͞ŀg;0u4D~A̛=6Ok4. D!|VHcEDDDDD<ءGx}sMDDDDDD<"`I۪g"""" ɧS!14ivHwIHm""""""LI4N} """R甴Hq5w""""uΣ60x{D èPDDDDDD&ma0mxv9V0mx%n"""""(ydfO g}c㷁i'T9f賟roe,^g/yۉ{%UoqkNs'(뗈Կ2IۢEhڴiˢEkgaV9AYAbb`b2 }aPl6 [b-,{Ukmku/>֛' or;9'9=lxYVy/]|rw>8:pS?b"W=Gx"DzCiTb2l9Xv@11A3䝅<ɇN#1ĵSĕcy%,F|ŕ̷_?;W9+\:% 983|ou ۙ{c[ :Kl:YfzSW]5E?gc*c_\E~"zR&igTR@@g&>>;2 7ϖEeF,M|Kd?ƉB ٽ:cѩS'Ï"i&pI,~ĎU:,6?ϓ'[⁛gxbDDݬ?cѭCOFmӟnWŃz۲%q.aC;6TQFgl99rƯkB9]d i22cO[=ZmY9L[e/<9=кY zI{o̷q OqYt=SӖy{UxF=e# лZyK\dž^\\@k=6XJUL\κp`ڇc[OZDU{ڼhs0-e֬r[?diAGNXQ$th>tof],b8cE+s m)]H^:oRE+9vT:zdIvEIud뾿0 wA H3u߾I[Ef~6Nct;.aiEI! 6䙘y[WXk'_t~}t_4W cIم^d&y>)*]Q=m ̗ zŷ:ڎ?r05RgkB6-=gl-Nszu k?aOr[4L[ٱG^,]=*H6^1!ILL< fʨ؛NUlyF 9qvK; /4'a > 3ٱ-og00I ㎩#xIxvm8}O:-D=xo}$ME~l85Ta}ch&g'nz}xΫh X9g껌kq#WɽA|RٵF1z,=aO}2WW3qt=xRUlSDDD)fM7{LTv0HMMeÆ |dZHXH0nKV/K(pmMDƘn!p柼|R`}G#"u /yg1`_<;z "R5 ͞U/׶lөS'8=]\ۄ|߶JDR?&"""rl &nčYi.so^HT3m""""Dz:ˎLf_osH{:[[Q1N3~Y H{Lܮ>Q%l"RmzMDDDzɔJPl66=&"""QeKJDDDDDDV#EDDDDDx0%m""""""LI4=RDDD#%m"`hHiEfo/aa960h<+h7iP0 -@* #oO-7tإNϴHct&ma0}x2|gSxjq*뙾73رq)Jg hsd1HYzMDDDc2i+I2}Gt<ʼn'XJm3Tf ? Ve""""""esIaX:Lߕtiqbm2 'eYyu%IA.O~ֶY+qp,j;'??c/_ٟv;n#8@vf?rv (ڟͨ^mteuﰥFIDDDDDZI-ZDӦM]-ZtX7  0 /O G d_H\̈́b*܇W>ٳgٳ88?>>6 7ϖEeF,M|Kd?ƉB ٽ:cѩS'+M&pFo=is s~6~6&G$EI+G^Ə>…}hզ+x[[ɻܗ@ #&:VN⌋0? ciƨֺG3sLCZ-ZW_̙3(a2zKO/OAAӗ'O:Gm)č E&lE=w,,3pJ,9/[Tc,wd%AJ 0Me%;Su۱ȤMdw!wcG ٞ[693m%nG%~ST/ܖB:F&ٺ<@R:Fāt2IO(F9K\ˆvWpml-Ni|S6囘[b[l/^={.f70lݕyywc!k_tCY1w.(:E1L4FGx 8um6/+`GhKz2 cxY/(4k(a+tQ&"""""Dz=RDDDDD1P&"""""x0%m"`hHiH#EDD1Rv 1 CFBI[5EXE㕸HPV a0m2}V7f9~xBN܌]ˠWҀjͼ<0ao'QgoؕKs*\sOk{1S7gj[Oொ>=|~ A罈4(Jڪ`ӗ'z6?A뿞KpW퇝LjAX*YHBv8_ 1{j~y,;vLtJo]i,rqv6:AVG; ]_L3sj֭|AGRci;ϰ\I|]{=嶬ќ~ǩxVxf/"""AI[%JLiq 'a[nj% ͫ>C"Vs+o]}/lc_ʢ+d&ptkbC +?9s3gs]nw0癌 /{y51`i[bm{ }Z7#$[yYom:?ɹsXٖ+F,>_`9:e`DDD(iaX:Lߕtiqbm2 'eYyuI+m1/^qmvFr{r] 3Wre G.(7tE{})!>τs#`nzn<7>23}N;x7Ǣkd_ɮ賭kRy~S˅?oԃ̫j?8O+$%[[Ii`UYُ]J|Ǣs5(~OQL1e&6_DDDj]N rF4 KǓ鳂Q0)e:YW0~3l|/JۮġLeJ'O ;ow&?~eْ22/J'>|[X81>{^_zݸOۯUP>?M|qe)&{UsqLg|߲.a;so⣩W2~_^嬜wsdצuT~c]g7_Q~=T^oj[%ŕc-<~|֙ n޺ozEE;|vjGEf`V-Kq Ҕ3+"ɆD3v5bti;xmJΫj?UuY=( O༗|7j7_46i3 7ϖEeF,M|Kd?ƉB ٽ:cѩS'Mڌs W0UQ%(Jㆩpa8Z;o5Nf#vu ^?qIͰMqom|Zo'+dNiW/~}c^J cW!I[U\"xC19>^6|s3(=78o{bij>Umف[խף9[砌s?0lCg3ayDnFD<ʻ|9k>x-z /N"lBCkLqo ^*{쾽ΫίĜ} ו<'fL>ZB*p8=oW՛_UyN o[yHfRa퇷Y@ӄOEDD4$aKpN'17.eỂ}1q`.L\仲ؔݦꄭZL7S "it;:83NְK 1R(VQXEiU[Ժag JE) "k$Ȓd3G@@8?2ssEY6S#~^x̊*.I2I  " > 15pOۤ#g8.Ñ2O_Y(srRO :[Љ$I )`GM#ǍỹhW4;S^A;/6ʹ9\U|(énP[|l4ys)qDMDD: 鑦ioF3g+8^V)nb#88<J2Vʲ}Q֔9ǦRӗ+\pBGHcSÿMv-@}>8;-Efh͚֬&Ry "c_+_Of7^uyqzNW{,J;0sJVfEѢI#ɮe^}~8xΥͧ.k?d[=Vw ;ݧ/hRҭ$ ¨тR U|A+\W򸳜iR3 |d-+|Nuh8}?o4RsЁ\w$gnJrA%"""I%m {3[aG*LˍsM|ds={ؘ?6}EfZr y-7&'`ZGϹg`ˍO_oWA> ϣ A$?8ك|yN,xCOiC7>Wd\5mvKozsȏh}͝|&px/~~L(͸GrY8~Rp:Љ\߼-RWՋFL~tȝ;Vߗ2\6Z3mu ~wg*Zg("""1{CJW&4X. 4"--_!\jGE3 6"$l~=>7s//'lc\2|DŽ*[xQ|6;9)dX̯m tN|~7gOC="yrU=dl:t%84"G&"QHXfM 6(m!99[%fߵaς=*Y)7q0uؿhۗ_7(90\u^v9cOrmryX|Nw/ w~|v3Cb{]"}7af kD́K?ωP=OFPTyJ P@O&"""r2MȀHqPdгJDDDDDpkNv|O)aU5m""""N h6KTu^&"""rdMDDDDD.E?=RDDDDDRMDDDDDMDDDDDMD )"""HIT=RDDD`&,{4: Jd&o-MFjZlqsVh+.?uŌş?A+9>$?S:o6K^MOcET L2>y_eظ_\q.q.5mg}EDDDΝMr?Ҷn*݀q.+&.})OpT"u?I=9!mXjGCf=nk~@PP ]qߟ WO1 ;V#B0ɘ^Ǥ9ǃ Jea=~r,&bo:6EYe"Ͽ$-4l khwƒ*kÁ/Fmf/~WzF.w?͝ά'o'iInycE^yZ5~Htв'#gs?1+hݬ!IY{ʮgz-mh۶mO~nsL|$OO}:ՀnoS~az:ޅ՘+|9e/;;r~(ϙ9m~e!N{_rݓsqD7{Ѷ e=;ˈ럌2ʠGsYƇǏg0J рޞϧ~cFO/p&*~*:~? { """ri&&׹6u¢  a\礧Ae?2ʜe,a?zGF|,݇ǖ4|闼z!{?Me]vy.F>tG~[&|m:9bҚHzU#1m83ٸ'9bIeo3E,r<7N޻^aXioʎpdڃm!n~f KY6e 8 hrjЖƳ,칵7xֿmA1Gl:vy&%<^z|FM~6f%8u2ihI|y?nGgx:~/`64^ N؂el'|ÃRL apz' %99r6s&.=/6 HhՋǟ}I˟0'튦$$#pxwQG4AWa>:;b/sq9)O5[u~Mv W2&,̀˟b'8VuR&^d롕dKgNJ3$ḥB,$)P{ۋY-WOaF& :' G,U-m8*8αͧ5dM|l7f]hy7' {?}G#'{2n1Q);s;/O\w{NDDDDpIфm[ZտQJlZ ^M:W`QeyRgkJ 65Ċ6X>,{{^]5[cl<^`mq˷̊*.I2I :x1}_S4 ll?)9'':Ͱq}4šИŽlz4ٛ{ܘ77>Fq0dqx|dlee>4e> )76?aso˷;Mr/ekIW$٧LכZܣ'X}5[%qwK[&͚ѬY35M<9mᦆ'&sǫގsړ`mgQڱW2+MjyMv-t\?`Cs.m> pY{!ҽk>}Eۖn&VF&~fN5⛌^"ǝ_U~O-ap8h  \u?dl9y0-7MDp4ac؜Ek-. xܘY9侔|,3!c) q~k$ dgّks-0gney)->idoyg^1\O M/aø~0+nseq:z=tÇ+wk\^iǶ/]Ƽ[Y8~/nlXn?TO|J_We3ُZlaطI?zB^ |bV?}`?pWj]IF̌5l۴|51OjsU=mRb{VdamgsbU5oU;ωp8W҃Lf}.^E$qtalݥ9%leէ2G}Ҩ65rWma-.1;qr#ȥ>䇆to˸g<*wr`;|knO:V{V*h9 udy$\IH7*["$DTSZ6҆8n~}{FVY3HY#C%/ ~n (vF34w8HM;bu~՗q<?>W t5/RzUM8@DDD S'XRR7qUrQIVViiiL R;*Qf`=!瘰gt9}8_lc\2|DŽ*/j/f9{zND~E̛5P%f!+92B"7kj`MѶPb]Kna>,(.٣ E2X7}~ASU5m='"""r evȋfPjZDq^vJpy_/js`DDDDUMq맔\ª6s̄c[tt4C %:i9WNP&""""")"""""r)S&"""""r S&"""""r S&"Նv@MD )"""( 64ygYա_|f&o-MFjZJ*"""""UZ&miv9AɄeuMݫ(kyX{k29zhGŕy we><1~K1CLɋ{ CF'it|s'2{%x)JH;П Y>+>qd{d jc$vfV799؛-~dXNxyoH?lPw {ti,GݎtcӶ|?9pk@J"jFQ<@VXEDDDDPmFڎ&l{ҪRv`s2&ENjZԹ,ˋbo>[sVR 7&HyDҒV.'xrmLHaꪙ[~;d]_.XW/Kï(e;!29̵s7*ŪMߌfΞWp8xR<>7>Fq0dqx|dlee>4e> )768dxw!|;n qI"˳9(wOɌ %k&St'΢cmۿYQhR-ap8d> hhl˵yq맔i@T3[tt4C /ִH Y5%#EDDDDD6KXW+|(Ɇܲς!68kY񉈈TB5Nڬd-o ʠĪ IDATrNäfL?=!4r:K_=5d'O,eWf&^劰LFlܓΜl͸ُYb ?EOgVq;]Lʹ I[jj*f͢v̚5S~?zl.|J@E?b-0 FI!H_S#=6zM:Z0s9AMhت'_Iygm4AWa>:;by{q$2%!Jn; Ag-_yK=6aᑴ'Җxv'g}2NSy5N EkDDD$]鑩L<2xbɓ.aīF#bkFk7jYxm,<f$bLؚ5F#6]1]Q#cش-IAiVы)!Gp۸ŏ ԉ3/ +ā]K֒q ޮl:ErdڝDNSy-N GkDDD$]5m%nJ0 IqPV)qښR?8M" Ȥ!\;(%qc v<}YGϱ mɐk0awrUY[~tTeLvq6S"""""r>]HOƎ[Ʉ{0D2Hiq(6[clަflEspV\Atkw 5)l)9Iړ`mgQZ#W2+Mjש(wOɌ %k&S66|gN<+XږuIPIM~qpQv<=O؎rFa׉N/Y=[YYD;}g:p׋#i,76Isc?ƮS'cpvmKC^ q?ض7L3XY=g[̺$:Qz2/e,.iؖ4m㋥0(:џӴz HК6 D=mdffvpf EֽX3~ڕp!>̌:HJPvz0ĸbܠ("t=<3WٝTi@d̞:ҕ :s`·o #!!LkDDD$UGծ][n HkDDD$"FDDDDDD~~1#m+|(Ɇܲς!68kY񉈈H@R҆UmwYXiԌ'~GjVwTI&iO,(ڿ8$kl227:;cT6MJ(a@ICؽq4i}-_1Xj7 &[{|¡NGpVu""""" ;is$oP% @0p Wb=v's1Q !A} &_5o7 .""""R> v]PDz1P#x=` J& v][+Y7hB .xccbB$Ѥ{zN6 D;-؋i-80|ptF`-؋lըlHOR:JҚ6 D;fyn5`kF[ŀ[)JqDbyKVz_&]\Χ9;Y.\. ä Ɣd\.wY_hЙ'JI{*D WGYQItѷ3Ƹ /.Zdx"6OFe1$}^Znnf1!{ J`0앫ۻo3E,r<7N޻^a`To}}^%+0 [3zcXGjU&k&}ÓNXʮL27'RQx4}ʺ|D]f}0d|FNgY>w"ûK.p6{ $b/ffя9xK,|vQR=U驑/xs>/=K+аUO>">O0 ٖ iVZ'Җxv'g]qTMcteFIc-_o+ƛG"].a\]ۻTnִH 褍IƮ f8fA܇6> !3'bkր*]1]Q#cش-=HѣmT9xZ2ەMtH7Lş&QY]G/GP;"Mo?2S'nϼ7*%"~њ6 Da@H":,͡R5pqDgA&IWCH˵O_ƙJeLvK*߰|Α:B[2d;sX\q*X'm=[jtwn816oSlf3lx9s+F E5t'΢Ÿ?3deV- 4 -|6D̓mx_WndoD>~ GͻdWo5)S`'m(l:Q%k>G`±|`"++h[:⽹;ذv-k@Fxw]8@Z~myw0≮/ױ+sM?AlKض}Xm_xlxQz2/e,Yz=XE&,K鬞3-f]c^+RZ&"""l1ln^^?sGf $kO(;X>םi7SH}5:o U@M>){78c/掸إ<0JA|=_>:VC1Hˇ.7~gd v xn7׿^M\XZ&"""Ș=u!+uWu,UoxM}ٻu=;PlܹKyrU=dl:t%84"G&"QHXfM2 9"42 aAuDDDDDD.%mGmDeYaD"i@GŎff"RyZ&"""HQ'6ϊb嗐&aʎ&4[DDDDD$@i}1 m@X&EsWU(%mIHH <>1:,9BkDDD$izIj׮-RaH9MDDDFDDDDDD.ai;W P %eGCpmpj ,Yۂ{*r2(0OHլ,yCl*&MI 8YP9{7CqHHoVK5`rX@z*N&ob"BDk"COqW_EDDDDJ> v]PDz1P#x=` J& v][f6хٕ73e&"""(6ނXA8 !B:BHCq=+( o^yxx0Sa/gZV?{zAogK/P"ִH ܤby8Ck v`R Unu"s7S?Gs՗ѢUG{U}3%FwƸ ڧ#M\ VJr3\Z_zvR67VSѫ}". Wb+7{EDDDDܤͰcC().<X  0`+,޻!q՟YGl<]|Fe4dt!T^|)˦ >,(ʊht[ҘG?X_!t4<]F'it|s'2{[t[njCkDDD$H!23*_DDDDD" ^'j:d,_8/lQdeeaQHF.YnY-^KڦUyk(}_d_Z:*~b ˽OƾM^ 525yto3g,mzuIu]T!i@kڎ2ln^^?sGf $kO(;,Gsyx߁^{Scrq$<2^f<)}emtyn&oǎa1>m;]~`'<πvS`Ap˹A;> p6 ӿ#EҚ6 Dڈ:|V-5 cWv4 ޢeU'"""""J#mG'pÏQEXl7("ߏnDDDDD$@)i;NBBNFᏑPaZ&"""H#ORvmn喪CDʡ5m"""4&"""""r HQR(͇l(-,8k؜U$%mXeZ{WA,I͘xBwfezgHU4iHH ͂\ٻCJZ(*&#s#S?3FNeoJD6 Dn8`L!vb[e97'1(tGhUG.""""""7"q$oP% @߃@gpx9zL>x33#.w`^Z/EDDDD$6 ;.(^([ @A`{j]P ;ˮEyathrJN7XQu!w]LDvKla4އ-"4!T. zO-؋i-80|ptF`-؋lw]7gIkݏMo/u""4j:⟤Vu"GhMiX^7`xp{m-bfl8C"%`y+.Ň}Q|6m,^׉-б۝xk.G!az:ޅ՘+|9[;Ye#{ ä ƔwY_hЙ'JI{*D WGYQp3Ƹ /.Zdx"6OFe1$}^zPDDDD$nf1!{ J`0o4~q#}irQQu±y3s<]sF}%_I{+lpShErX07>>}KJڄYw=1W`ǣh *5d'O,eWf&^劓[+^ߙOYoˬsO,~5Ndxx{)""""rZ#]%{0 0~;ܑ&EHbFHFŨQUFKOQZ-EVhQW1jA$d;K8'==+qޛiIgIcuNMJ&KN{hjh_V_뇪E?A[6JNYk&/ ߓo/p?v뾌3"y_捷:V3lL˘ɍi}9qa?7<7'v,kX0 IDATm)%mVIۦķIiU=G̈́Ҫ^Ұv>VFvIUҜ/׮^WVyM%rZŝ EF y|ռv>XY^̻X6Rż|^9g'fc ?9m\d9$=ӥ&LRuR==RQ^&w̨7K=g, U6ϣޘaZq&˦Z~-xRM*KpK;Hʍ ߬1U? ǜv?”, ˥;ڒ Xmت!GTO]ʍI =tl՘ULZV}1^lO3hS?+/B|\pėϧnrI'_[CEǞ_Pln.yk[y?庇K:+^_?)g?_ }xCVxI櫦΋?+fne?;9~+9jҪ&9`AsewȊk!'.ydCt|vZOx_7$I*Z{˷~v&֫~_# 6`yTߕ!Dz 3uS*OKgL;sR^zxrʑF|'db ii~RYY,Dv횓O:)Օ(NH׮] X9=r+R{e= `\,i(0P` D@6m& Lh(0P` D@6m& Lh(0P` D@6m& Lh(0P` D@6m& Lh(0P` D@6m& Lh(0P` D@6m& Lh(0g溽6W~VL#WYzoiq *=N}GaքIy-пg§Z}=i2Of@4dm={gK7 Mhn;|5kg3 +%G.:/NmVmqzn} òMգ7ȑ7r\#sϧCy2lZ c{fNvT*YDWڭL7{ޟh?5/.WEm:~l9pߜt͹yg?LqDn)7ery=r ;o;!S8[8f½y$uy{?KsTdy?r̖[Ʀ1/6o>_^={g+gv۹òf3btB~؅X4<]s=t*V.F<;i34|{N>,g?n5Zfa;=]7藃L}7;S.ʺܛ;_HXCg9huvon˕W'|'W [@5ˣg}+& ҰYM*SNR)IC>|W>}za2>YJXKL=i˜ HRٽscWVN{wysw3&??>Qmfl嫓*]H|VƛtؗUZOͩoAyqOjjHE,h[KZ_;1_ꨜ_ dYfռ>UV _g!]E&ӆ0}grvZb/31O|0v|\S*{4Jz\ۇsK7Mmӎ rʕ弿]Wqb_KmKH[2J䘣6ȗuYѣqQU7L+GISfnͿ&N9%WHeK9S^#?هڧ˪UyCȏ2f䤩eA>z`nqY5=Ekò_OʗwC=/"]WUg\C8|i/ˇh5VXUtM+C:w]gY3myΛzjcޕ>wHvKydgQ79Pd D@6m& Lh(0P` D@6m& Lh(0P` D@6m& Lh(0P` D@6m& Lh(0P` D@6m& Lh(0P` D@6m& Lh(0P` D@6m& Lh(0P` D@6m& Lh(0P` D@6m& Lh(0P` D@6m& Lh(0P` D@6m& Lh(0P` D@6m& Lh(0P` D@6m& Lh(0P` D@6m& Lh(0P` D@6m& Lh(0P` D@6m& Lh(0P` D@6m& Lh(0P` D@6m& Lh(0P` D@6m& Lh(0P` D@6m& Lh(0P` D@6m& Lh(0P` D@6m& Lh(0P` D@6m& Lh(0P` D@6m& Lh(0P` D@6m& Lh(0KXcccQ z꩙={nN<Č;VD,A3#;駟q 7Kղ,y/m۶͏~t!6X u떇~8lMjjjr 'C)Jzhh>={C=>5559ꨣR[[+X$KACCCӦM|N %`)Oeeeӷo}}ӦM|i׮p`D, Jl6[>M6w}Ӷm[|D,nI;nA֭[gРAn=a)hhhH}}}S.gnw9ꨣr}eԩsMG`)yz䜧Iδfԩ'ON9e=sBf_Lcc|;v m-gdjݬz2Y'Z{w% ih_>'܈4662Iy!OH@Kg@ m-hF68=ihQHrD@m@m-L-I$wZhhQ hY P`L6uYq ihI6E6TRD@Kl@ m-̑6%6kڀ&ZjZhhA3n)ڀ#Zm@ m--%ZHEEeSS$SyJcCò 9H@ h>y/wd+q靿P F|։6Rۡcjkw//ge0*NTWWm۶z(k Lh(0P` D@6m& Lh(0P`U3_g^!G Lج#Ng~2rb(mwoBܩSl#%bV٧unn{+sITZ"׵W]nD/־MCC}>lyzHv|m>w#R)RbٕR2}ڔ%3B9jfocqmz$=3d X͊TTT 3[.'r0˱63檚~ ٬YesϿZ{Ͽ q~,S<׳\n̰q}yϹǢ7pu=<<XkEYzGcm9?qǟ_w1sq,AsE[C}}O:ϣy+fS7Ǵd s/0s~V,5TUZXоMVs/1˯enWZƏ(c> [@*Ͻ)Hi-K\0qb'3p [ c[s߈dSJI^Wxuh^ynH'UUsϟ4CyS`Q5dL81?>lثg:_4gjY.ڵY?orƏ(+\.)_ϊhRy m|RܚF$ 5]ys͟8,=Hۖ[n-bƴ}=jYVQQ99 %`=y6R) 6OoViռ3YgЙ2iBd⸏4;g y!KpDlVmfy'ӸG {%ٷ(UdN}x[UԴM6m]xͶi~@sU%ɣ;R4WѿvGUmU5ae=hפsTmrғH捶r@~~it;<:bz }[=6nx'uF]޻|ws\k]qJѳwo8 WA; ˟e5ϙI~9w7s?&u5,qM^u|s-yc1/{'[uCWFsK) |*|%yvcs; <3mw?O'L$Z=dw?>2\fwz[͙$=7qϠۧƦio>3o992]ӫgtwZLiz}cG zN|g!q:1/d|OS/ybN?ݯkڔnred/ygHϓ5$5YGlAwx3?0[agN+^d:{fN޽wYo|{?=|*G=[^߯IDAT7[;[uVXm*>='^7_{G>uدҬ2갫Ŀ侓;/}VY)Ow{~-9o\qɹql=wk2?Ο~zmm/#vNЗ>X_79Kv!<:iA'v F'F9S': m揲gNjf'XfGaU%#̕TeON51ǞwLzqsyqs+zboRK֗CkS0o69dͻe#~#V{*7>aF>K]=?ݹWzPMT/`Mods=7\#k5'լW5=_.ɬ¼q۟2l]r&< #3\:UtHu,/ y,+Y3v]I6„4l$Y*#]zZޞ2H§OTޜvY.gkI> ;,4,&bޭ#ם}-XMӣ%M'դk[y+1˭?iCO{%7Nj 7RdvUuι#CߞzELbޥ?^.\Q۵{Ob IT9*-EaoӾkfz̿I1wCV YOUmt,M{c|\v3b\a,ƌN2U >_o^¥pr*W0]oL=gGY[ҷVo2,wƅL_Z|aN`HeERnlkEz^Y<웆L=maGο%𞔧 5G3|ٴvk)ϐȸ^g/.Jz׺kyfڥ{;?rf95PF1_do4>L{9O?rk~/a3xۺzr3oW%\akC6Yv9vPm9\|ϿƈwƟ_Ker)?}'Cq{n|tdڧ˪UyCȏ2f䔛X_U- okS)O{3.`IÄҋC3ѩ+O{^ CɍI[CrsswL/慗^cqi\'NKN;鎌Oju9ssC7oom)scwsͭ_忸2WcF%i5[ /Qj*y͇g =c*].82=['ւ+d<8Ml8`|oRR1۞qM~~YiIUzgRj1Gf۟t:{씯^xIӳg}yBm,=YO=}5kRA< }!?61pp\k;oV'iȨ-rv+sbC&\Y5&IUsa~7si_uS+&_˅WkZbE<nѶ|`)s.^o@zac7pP&ע `y ۄ1ZX X]"ڑ6S" }0_IENDB`./oar-2.5.2/misc/fault_tolerance/HAdocumentation/schema/drbd-test.png0000644000175000017500000007745211757171206023622 0ustar plbplbPNG  IHDR Q!sBITOtEXtSoftwaregnome-screenshot> IDATxwTWlX]ib/ر+j=jl1vK̫&Ě15;]A#* *Hps;we<3+3 tY&> Md}@4d5͍9uj0Mgt״Tj]J#_pS+h#<gUpf&Cf1 (n*\'n9{ګ9W5u,;њ/kqdžy~D8hUiThCZw2H;{s~njсug"[YN&Ayy犕Sܷ8g\Hޠ)SmQL$4ksޝe9]Uƌ֜P0JK{ׯamR&0Ba?גӻ5Il!sp~a_*''k=Toӥ>nd[u"z~wEf>_BlVt^ţ>סѓ^_'e[cT5ZM[EIKzxd svM ]ݰu,w,g% gWP|~<ųg{VךyU#''nVYV]vBf nV~2g~^!)j-T""iYYSjڹbW'8DžV,V]w7B+0^vU]jws0Oy\NjdSOUwWN;~~}jt݁^3,dP.jZvۉBğ]ιF7ps=Z3L5?o~Bcýj^{BD҄#{ule{N||]FB:g}txn?Z.'otWe 6<_Zvfz!G~~i&&5RGfwr7EVfZLFzw^ maw|yB![5(/ꋨsfݡ8vJ![vMl?}߿ԕ-Bskm'n|Q3r'CyOdO"#####ݻz`3/n3ؿBĝ}0٣j?clnjB{y^iau[7pgڇ m=lӚ,^0́|FrƢfF4j&(egg~[Uc94իf/uB`g]&kp!ikjN`ݣ#Z ZrL?bEם8_ !D=f=٦⹦͟ { Q]B]:9mOhio"D:aUyoo!X0*ZڕHM=!rN  JGn>6â#X Q^ЊM+6ێNJ!D# y1"2jtV ![oUC|ާôdZX3{+$=rB'?4QDiv.ne"%K I_]I,dF_[-dl㼦?͊zmКWuqV !s|Rc嗛5)mӼo,RPl{eڠA~RmcY]?6,d"O] &2 ~_'%O;EV>mkYϮ;#=J%\ذ?Ƶr*u2uȧm*=_t'6z})iߪ5Vc$O0ho^0exOvҭDg.{Mve34BFM^+m [ {(*Sȋl鋉J(P7ibº } ʅBnYR:e!*h) BU ! O qnˎ;B|1ݾyʶ/3!.x[^; lYu_N{>.3M^Sͫ~1.mK\cu~MrgBX6 {~nRO335+Ň}n}\+s/|YE*ņ_ֺO>wil+ @~Am,7[~=-=ʫ+n:۰7ڵߋ/N6~|gO¥^QߎnAs*:rvol_]_rѳgb%gVaagzYU FxRrsۂBx!̢yz&m2#Mډao8aY<ş66)*^=2?~.oN(JmGVүO@yň]s\=ע uQ`m[SO~xF EŬ^#3+TJ܋x5oGu^o__[Xo\Dw= XxoX`ݺRmUn 8kܮb~1E,S~]Uj{xr'J#*y3_EBE$QҪ(3xâ)e*"Q"M"[2#7|LwD\gv|UD%ZظWܳƌ5:(V|T4myq[L2!LLj7]cB8._PGlہO8]L!2m{YzZ[i>~3SM, Z˴# Bi^Il?e\,ĥtSK/pm.ldݡmM9nRS%KfW]%ߖ/ھEQ_`Cr{ly)mKVt\?{y_&ߔE_ܰ+Hs䉇TXi੸{*֭L&!\o(kM:ˣ? إ7sŇ.<{}ʻJ&!>zd:!SfQܹBnl☊{&|$ M,+W\r^Qԯs%/,2u[B!TN*Zi^ذ垱-fW}A`o'* !qlTu|b~:ºYߚ3`dBh\W]!O6,,/b6ٮvʝmtQ_ T.htQ\YeEq1A#7t=jBXfXc~W%XoKb-vaG}jc̆9?0T:W;yQs͘O;>}ucOPJ%3 sǥזcq93kyw!sשV4;va]c|LUu*m&u~e$bFl9r4e;֮ ٻn{[-X..S)DzHF,}:4[!zoD}!EjgڽB2NʢZMX?B/֭4=a/Z 2<~ucNBl&m^Q`w~.dܛ|!,|D:aSߧ~qeuBcICL$3Ӻpܚ)楛Vu0BmPZYU<ڷpA9}YL3~e?[Nzь/MG2/c-T^٬-sx̠t_wrT,ߵ!S 6E5z㥰Op>iUE3T(6U 4d D_A +W]e@D>d>@[Q, ݓup⿟{OhM  S?%uɳD> Md}@4i"DHY&> Md}@4i"DHY&> Md}@@?fդG'`aIBb/_ï³!D:@~6Iv-B}N7zwTF94.' !4|%wg}iA:sP%?G^{2ҽQёmjXL)/\g=i. /ߢE=wej]}$O:ԿG&yGFۺQ ![ۑZ56/.ˊ:@>;M_oxB/@t VWPqnԩvq(ݼ̿[Dڨg.BQ_ ˱{xRƓ:tgϦ0SfO z7DYGQR KZ*3 X0wj2fag:Qq23^x !nF|Ef09.oxWd}@4i"DHY&> Md}@4i"DHY&> Md}@4i"DHY&> Md}@4i"DHY&> Md}@4i"DHY&> Md}@4i"DHY&> Md}@4i"DHY&> Md}@4i"DHY&> Md}@4i"DHY&> Md}@@?fդCUu2Na]/ww F706';k#/i")FOMzA!47W ;8d_G, /3-ZYzcLye`9GG=ګa1p͞ 쿧1>/;m=hQU{mԏ˓GFۺQ ![ۑZ@ދ&tըeEYװPo)S B#@^~=箟_c>{R'jƲ6#5B6*K!Pt>f𵀀; L3y,&"孬_ꐛB _պ {~q5Ak4VYGQRk4+OxoGF}.J sʸlR&1gpoglクE.k]$,!>ڎ?0yyΞ)ϯؐoٷrM=ӄo]UҪoj2)jYY5/9o}Ӊ]V>@bj86fB e;έdPݝSܿY;dFvZ)1١I,ld9w,zxقG Md}@4E@!9];> Md}l:/@~GHY 9]@$HY&>@YY&> Md}Cr> Md}@Y2d}@4 <}4i"d <ȕ4i"D[i"Dxi"DP4 w#DAhAGHYHY&> Md}D>> Md}@d}@4 xi"DH <4偬طͬIB!tNTݳg+!u=z.<mB@~6Iv-B<{r-~r!ϳCW܊b<݈!&:bD! {y_2ӂuJ~{/ߵGՂrS&Gwd㪽S i{02/;m=hQEf-\TMKWlmVD%BnoGj6 oџ6n0g5+b"Hَ7xdt79]{JkKL=ׯR]A裑!TBڥR( fo,y"-oe}m,cZԶ~ݻb!ɏo {fU'35oBfdWd|κ>H%I@˃xd}@)u> Md}@/i!D DHY&>&> Md}@ ߣEHYo4@4i"|HY&> Md}_#D4i"d}@4@>C > Md}HY&>7hA>CHY4 !Dx/i"DRG+> Md}@@hA>FHYH <4i"Y&> Md}@B IDAT@hTi"ɡB"DBY&> Md} !4)i"DRAY&> Md} 4oY_8oYWBb/_ï³c#F7ܝMmF94.'9bt= /3-ZYzǺ#WհR^fI{ O;rOcd{=hY_nQEz/FFۺQ ![,RSENR6 # ! L7G@P#VWt>B%QϬ]:ئ)4Y!ei```9]{xҒhVԚzC{;O;4;$%o [lTe̳׼JYtMf0@C@[=<Y&>ȳrqi"Dy <ېi"#hY&> Md}?> Md}@ ף d}@4@F4i"\ 4i"܊ s4@Ģ>id}@4@C4iR|L]}oyk6lձY) <@X:tZφM|ksQ[6:"ʹ| ɳTჲ>_URکPѢ&d} '}PWZ!'}|p9 :675;{eAwsh0w~IgxD-ZuȄLd} B!v+UV>Jʺ@~C2MO^߽;Jh +4[zܹe:S,qkKYcu|E} }PF|h檶(jV&؝[E 1$\Y>AS~qnƐQ,惮͍ׯNq&&&rB!MLLrܦ-3Fu%g O 15{̯z'C&zxί:Pf耲vv_t{ج+ H <@vD'$ht :MD}V 2-<\Nߖ+O\\[zYd]i2!Y_f¶:wqe.*Ϻd6%y!!wɪZdGaiu u cQ~X׏=A//rN&bYCnDiY%AT!W(dBa 묪 H <"Yߪ֜d])PZuON_}xm|"מq̆aE7|#72B]/.R( 5ou).Jâ>D7uz0|(Ld} %&gI;f|iuGpvU=˖-[!MaIBb/_ï³c# DZ2 վKI߶?bZ?Ua˗ϮnxrhF94.'9eL&)`w ΪZލLi: :P G=:a1\ w:[7U RaQef^>O2MOz܆gm=ŗ̢Bm݊BYȭ@ӎDj0xk!qj15p.ᝧ^N|EZR>`D!tִpT;tvm^ruj77Wgg_lol:*3ߛ{yMcog*8pﺍ_oWަYWG}K|V|U ``t4;}sdCCP3zaY7zsȨ Jh.{=h9rU˫wc[t׎IfB$_6w/.:sN&S/ Yv)`fP{ L3X|mXL\s%?QkY}nͭr:ػن6#\#×,-⯬ԅ~eY5_9?}9k[FU=ڵVGO_⾤7򸉵Gƀ^)UV}StҾ\x3 왹66Yj2ϺҌ3uZ"m7 ")lצ5\j4+|jMc!=qYS.^?eAaNMPuo~eT")"n;mx#Qwڑ;[ϸwBI5Q9du (c'FV+RһúݚF{OQu}sEӞˏWlruYu4+A:5c 3 ب8gy 2F;{rfYZ`><d|O@̵-|\;8ig^ 6̭=m' }u)S&sW:O e|};lC&ȧhrZf~=jv\{?ʂ6j7Ѽ}B&~ſ& VShI0E} WD>{,d?bרZ^eE#K {k!qj15p袮&'٥|aBivvusss-Zۼц+.nn. r/dǻ'MX{yRY9%&<]ަݪp})f|Ld}ӢwNR3r ?._Jz㵄}ȝWn޺}ÿjB7o wHu_'Uՙǯ߼uŕuN|$$\!FQpmN*SbZǯX+d}ϾÜWlh%.}7`m!|?|@"hC4Wg4Xsų]oe;MY|d%גb@Vr:]&],lCQݧCFw37G57ŰIjWekzyyU<4Ͼ:7l\˳B) 1i8j_RԳݚp%Itkf3N7`Ԥn:f|񭪖wVǯǢ=mB7[S"R=U\BsV#Gh5۽fF}5(jO-\¦utfY h`Nu{Hbo95B}|goQ0ANat(yCAUBҥO ORNipgo747*;i&$Wgry3Cb#ηY| ϣ'/R:֪dTeUJ4|@i+%۪'l\Ż*f\ae{($U[I'T\ntXqnX^h5>tM1TqU*vmo֢~cs;jW'eYg55젓ݫHma|5fSF]Ѫo^Az7WjrI!0)j@o/?izڗ{׆jU)^Bf0|~bc,y9]@O I ¯*pjOpKa?1ݬL!72'72*]4ewiI-f:dxL܇'14?*K5W]o&EOEjbMme"[՚sڜ$E} ks*޽rm.;|кBaY\,orx.C.>BğWdB j~>]!qjck2\ڛa _vvusuqv)0vsXkZ8]\K:V6l!jG/܋767Ivf|.Ey❶?;ɀ3 xI{wyv_ޖ_w/z4l/&^> Gm:"vkÕ$!ӭ]8O߀QU퓄ƷZǻB/7I>3[Uwwc_}Z 4 vEJ]]O9kþ;\8q0o͝}(_ 7 yƱ$Vs B}[.nqTׄޣDy @[\yrJL̥=Q>M7/pN^Y*;{ݯJ!":sH܆7RP1&8<哆[9^}{KIJtkV f\͒n6j?mc.o<2KW>_(כֿq{ǘn5Jvl}$nR̚ipgo7,{RL#QvOJwڑ;[ϸE! Ʌ\̐nmOmje9{njBg>50ܧUac~n_ mo K;\2\K;q8aN*# u|e l ӥ۸"KV26!QTzWuY:sN&")lm#]܇7ʛE{>֍ZϘe̱ ڻrûÓӭA߽sz'{]OɅheOw-^P>Wg]fsAuO+0&9[i}ޑs{ۙ$?\zE2ݶr\S"E&<-6rBX/3B#r52#(x*&Bm_k9@hͼo!PgrʶD'[_NeJ3-2YoJ~.xzv؛TկR:!y2̠ BlL8#=&Y߼de}֟}dB m,1*ZVQ\ilv9ސyZWNYih٦Tbxh=2NN|9 wo=W7"4dr΃͗_'n: aR_4+?ŲaOGA^+MY}WѼءpZ kWGu[(j*[mF 6mMPU{ry[aX!qK+9DWs|}>Miu\E}(*TI%n* TVeT(p图d|hBe\]\;0n頴 +{C!1\jL=rsŪW;v3V8JŮ ^=X IDATZoabasg_-\RkTkA'OWy#퓄IfSF]Ѫo^Az7Wjr7_H3kGXYR*NE*VN(R!/Vp^{׆jU)U ں)aU3DȴsYgWsD̑^d[=7zѠ>AN޵f󡌿KK*tl1% ;x2u]S>YS4 jfXPPPA\2RIgTs_(5}奩f)hK+)  sѐޙf'pi?mQJUΛ\)ua\<9<b\za l9-lD|v%@w/cЦ8t#vS2iuwjVr~OVn}ާO?bm`3oϙ_-eʗammw֨N-떽|ez퇬lf`wGzV5oߏTc{{ro[Côou'P&/hy{5uڼl8Bk:2mRS`s~cw5Mx{xP*kE'+]9ܰu`{;vϿS Zqfx'@˾7`4qP'gxs +XٿUA;mOƮߤ6WVg^ > W{si@aqNQ0gi~~_}˃ gL{x6'tz1<Ӻ~j_-(>GXN@M.#CP -wnߢv.q^?)ez6W[VnZcgBs>X춍FhT#P8E}@h}4 , GLr@h}4 @OcQ0>`h} DXL &Z!XL&Z&Z0M>fE}Di0o,L[_nM==~]+aPzu[Yu6I ȉE})3wt3#rwFsB:"]㊱G S\~zg;*޿Uٸc{}B>ڃn~JZ_w)N ]ŮF%9zX !Gk11ٯ$;jE} h=YN/y;76+Ⱥ# I^y,888ۃi ?)5\hje!jw|v~I$脰Q5+Uvv2Cd}lW0qj{`tã؎S B{n-^ּC_oB=i5?2`R/&E}9%"`ɻ/Cc{0w> Gː8hA>`h}E}bQAX ZZebQ_n}&E}Ydž4hh;[h87rCR:LM#w/c>4PB_VMoZ<8li[]o5mmd:ҭQ=g"=b3ıe6nѭ-+A7m[agʷ`чշg"=&;\J?T(,ˊ>UjWBW~|3{UN+g׊NVY%@9}yݫkxbWa=eX7maf{ƨ~v_>#> hw4qP'gBV4$I2,888 أh}Y)MFϔ(/+%PB@/7[syX)P }U9G6(7eС/X G c~\( P}!>rŢZY"emP&Z!eЇby#d>2â|P8Z9!Ї)Ģ> ЇIB_>} Z Z aBh}E} aZh}ЗB&x}E!8>@>HXԗB}`>L@#ЇI(]|0u>ЗBfЇy(-,l B_& ,4@q#Їy(V|0{m}m=P!DlԺg}VMr@) ЇSh9jTI!tFsB:"=c`}Y)hQЇ9Pdg4ef|! }_bв_3&ЗBB(xGsnTBX:{KJވ$s?|=<gAˊ,Cƀ)n2@ˊ,CZ""eFP@h} (>C @Ah} ЗB(ZrCC/B_n}h}F @}E!h}rCPދ>+@Y̓d1Fhc{?sLh}Q{Nx4:pEkw]>s@ K+\45p U-yBxV)%M}ݮlYaaw :3O^X7imR数ᗗ;vUͽ'MlPM<Ɩ;GLQs|°eҒꍙ=ce!fkd^_v MI^dksdu整"=9zfq&|T${ F`QSwdHOcX 5(?9f|y"b9<sEZ`}"􁢢З-03l@qЗ-B(>lE%37fCJ 0u,$Z`XΗ9B(a>D/s>Pxo.2GB_}`Z}9wB@&З L/g>` >$rFFq%c9_}P,B_}X(/s/ R@ o( G賜>ZT>Pxo.@!}#a] {lWBZ o,)1Z9w>k7Cb Svv!A=wq"̤ WZI׋BbmMϦztYq#KxYz?7zOw֨aslՙ̸X#c3:{n9"K׀)u\޻| ZM:ѭɼl;X-:~ς77m_/dBMBI)}ƴ&ZJeaWWR7.q/1z!w"5ƕ^me[=mZCcL*8_ hW]ҿ8{Ȧѓ]7(hwuooZt^Cw8cN*ψ.ȊJ$cȂ= W)Fl@ p=uO;mY 'JR?S{n/>mg y:zG^"2*c`]`l }#[S۲};J(}G+vBJv};i%|e-|@h}@`9_AؠϦ@h}@qc9_Y޷CJG¾bro0>Q٠χM;9ρW|SD v|,c9_A^>`h}@ r``h}@oGA؝ l𘅱PtGhU׳Я.>aPzu[Yu6I\ە8iGA8i B!tqgaF_!7H @,+HT>'_v[T"éC :!nT}J]qκ!s^sPc"8jBD(kKB"O {sy !SVVw~tE]y +-y{ojڮuwvjٮyi'G=޴[2O\0:ŵ~Zsrq^oq!|be]kKU+4vM;H%Vv)*_A=iʗ-Fu}0K.)2xk%_8/$ƠMqh?UmvZpeTBɭNBc{7; 9 gB|C +hkk$F׾t@kzZY#0|:[b9_)|ύPWhk!!c"MMh82I qeS2c[ky?zA+Gޕ/ش(h}PkE'+]9?63[Zm챐+ꍋoJ[A/XP zkCTz39e6ig49(T>c@Q|Ph}P*_]T>RGQ2W|A(u>$]Rd#p~YK6,}FlHP]~ E}3nAV40BS`<>|2΄9v[asWtr slՙwL\֪칬uه!72, APA]YS.?1-GBpˢFEKamܝ =uqE@%]JM}ݮlYaak~WM:. 7*Ա?r%U!XT> %A%;U;f}MS},l>6(h[c_`[מױ]{tptҰ@h}0KRF™_k615Ğv~~Uȼn6qōS~1tR_cZxR>9kzf!PX/yO0΃{Oog qg; Qgϩy0c_e!PZX7)ºIk G_>bzx{O8fc[_Ldw>txr B1%:4'yAN_vbFO^2[ #~0dSzζOz6>WK%I`dSBed_PL ύė'}/ 8YR]:Ev|Y){mo`h}(4e($>D@!/+- OV/mǧ 'Zepo彶Zґv@q7n+F0W,t$&⺏fRIK3SSS柵rH}ާ,:b~ϻcЦ8t:3t_>إ%`,[_+l?de7kUO?гrF[uF[]#5\׭q/{cc.ZXrԨU;}e/]f.BXdu8CIDATyAߛ]\,{`e]:`<>cI<77|3ڽFn;_)NANV* r6z!]R_j*>4Z}޶ʤ'Vo~U'yAN+o}~i˦XuQ%|Y)A@SId1Yppp@@Gp/+Y^Ȏƀѱ@XLgyMEY&J _3~DIJ=7j%DHR?S{{1"y=@8)V>>V즉OMd=xنRų.9Okw3 Z>@fXSsܵpyCoTйO|7D;؊~=p/Wŗk];^W򙞰(G `ll/{S"S{z9>:(=bgǢbӪ5l\aYVfф=4|W{PZ@#&!>UO{]sGޛ悠ۇ 'x{KَT)eW|h>GSZoX7QAzwW|1v#Vl'6 ZSN~:Wѷ]q %g[&f=7Λm Wxӗ~;s{0%ٷ@q e'c@ 1`t(le/{V1h}z٢䆸6'+}lHP]~gPLB7.jA~e*c_5{ė}ynmb–k=g;vig7KzqGOˤߩ)Oߪ<=@1TdSWa=e_%Hɼ%5tޤ.#\OvLZWvw;(z'UXg~%;5-#pʥanMz4!g[޳l)Mmچ>9>o c>79ϾB߉{@ɣeQLZ!D !niu&FeWk.\@^Q֟ E.BXjѧ Ubb׮\'sfs(/T{FGru"Z 6"]Z\B?no\g٪asOFXuzR=@vh}t~eĆn]BuUw.\j;QuE=u{F4vrfkLX?Ș%ϖzifVKEݴFd1+;e*eQo}&,gJ ;"5J<76o=ZS+^hr!WY/kW1dC<6y~ > Tzۦ +X BM{%۱vؤѴUJG /ck4bn#-`ӼUKg?&D"7޸K#^5ȫ q;.KLx[IiR 6 4-|U?^Χ5QӜ4rی>lxi;i Z}3ֵ,Ƞ +d:z˷b軸COzt;M7}$JpFy0 s;ebxy;|/ZE"#u7Iky} f59l`)Y~3g~|ǛݯZAkG=+Fh`fJoU0bĨ?TgWqKvWlQ1כ/֥]m !k%^PȒYlzLrY5׸"i'uVZM7zY\kT+oڛܞg%=8j%uZ~iU^p.}-\+oT p P!^xoŶ_mۻBzdci%?x+ 0֗lWcIAt5Z\F]aB{ߌ wLY{V߯y݁f,DMԛ+GnP96|7"&|φSR?Kw\rzA>z孥XMf[;.G[3/Z{tpkF?G7_й~RsF(˸i++szQwdig&wjo}m'?1uykC1MO}u]8kH kEUp 0~o.άZ?wyNzNWxu٢at&GJ8fw F\1AU*φ:%EUe0jEƘ޴`h}4z망N !aPzu[Yu6I*`֒KZ!ijNL]Fy=y{7%!BѦS3ʌKs`z{yyy1˗Bޭg=ۯk,.oȻ^=oߎ㾿Bƌ+Ǘ#A$f+N?|ʊvw~մܿ sHA)nZ_M /KҳNL]A/6Hސvms>e q ?<"I3V'?zOO' /,i۠DIU/IhmŇ y?$!˔CO߷7Gi%Cٹ|,̌= IAL|]_v+xu=X"#}_bв rKgϥĩ^xm*[=ML]FrM*jm^w;e Ӕ4{r˗Rd%I22I]/!!}iZcdƛ,juk^Ōe5 a⭯Ojނ}g:vȨnTBX:{KS1?Rv۶S6_6Qf2uJe-.ʵэ;{2c~r˟M`h}4iDL&Z0M>`h}4iDL&Z0M>`h}4iDI{q^ #5<{ڟS[&JWȸSo5}{mdFQ#/9(f=CdPwߓ +K_x6|k%mF4[oOz_fvNz#BU{ușǒOMIR6$m7KP0ZP* ˔]fh0@2IXUeq>݋Ӷk^8R1}p{c3fD3|g.GO!%lۗc3 cſۊ);ɖ }W߫ңRY;oHʒOGpB$E_<<9uq\׳հϖܺI]VCd!_ӯo}O}VMe}ڼ/$KB'?cSΥ=y&}ӺܰQMN N^4|OoӯmzQ-9'Y޹>r#};N˒t-#7nӨ^V7\Js~zΝ~W|v~8!CNǧaWvnB1u?;W6_rҖ_wV|B]u*^ufVp N.?yŲ(YM~$49ҌȂjWGZ$/H}{*,lhk.%=1_\ !Dr 4$0xu;wK3&6glSK?B溾7fkٰӻX0=I?cr\~ޞK󏵭ot<>ǚtڕS?C'jwWAq7t>4cCMoaB{ieX}*91뢾 qXو[O?_nػɭogHI'7nv۶S6кKqjmH?_/vvF SwUS>1/y|ifp_tQ !D,t^kS?O?$h R;wz j4ת.u좶HsZV$IXVطm%Jw'Ϧv̐TVUB~ YEl2*zݪ[\Mki/nlT¦ }tQr-1K~3Jشoך=$٦O^Mo/9a9Ͼ3s? IKrQ3|c!/Oh7𘲞2ohl!,sxv񰍾{͡XwyAfQ);]]7{tk*-*ReSTNSk]^Bw-}e;!R*k2 BzBYRNgٞ }'Ϙ}*㇎>ol=ǿ{pL))o ֬S46YKPY<!}rx2Bd觧Z*ۨg;Sƪ)ޮG+W+ Q5ムo2`,O>?Z3q_o8q/CJdϖJm_ݬTޭ+4)#?^K}x55'?O{ӇxŅ^{tw;6zBtJ-yӅ!O㝜oYm̪WwמÓm}[mأeԼY[iKwg؎S B{n-jd)sro*I$R;+>/LYE}(Eeo_/k0MֺhҦe[|m/]s ptqРS,l_xsc=¥Y'>U3su;Zt:6k:[xҳ*}V7A޾_,]Z _Ѧ\\գ }{b/7ojʺ S]/o5,ҐzװqB~tnP@ùNz7/X aYK3#.xTzҪ}a[ ޯ%s|}z]izҘyΪE&o;ߖ<yk :R M%I, cA>7紐=28Ä>k^2@ b>CmχVL !,*vz5tө.e̫>.ĺ>`h}4iD97kIENDB`./oar-2.5.2/misc/fault_tolerance/HAdocumentation/schema/drbd-s-p.png0000644000175000017500000006176411757171206023341 0ustar plbplbPNG  IHDR u:sBITOtEXtSoftwaregnome-screenshot> IDATxy\EOݭo3d,LJ>{Ae (¦ `pyD#KDy?T$?'=L&vd@S眪:U]n~CQedmy_8m~sT3(b.d!` 9Q@Û[xwc^e}FZ.^jØ9^_Z@  vԚ22N't?ӫ|5B XnkPj]I572)֦,3\@ NKkrr@,s~$zQMl'xvoI!u@sfv'Y8It@<ۜ/t p ;ua9R@n%jnzBoRsTRQK)ǤyX#'t#Rt(%DS빳~ x(!FMoI?Х5WZoض窈a֊:wר`79v_@SV.& t:t MϽtB=WckicSw{l)@ $W ls ;'#[;_޸wa[[Vp}-pl% xA%_`o=gK&?ie]q Ƹ0|c,agߧZ5;|˕fm_J=6̫>f/4je#MJYbQ.]~rOt/Pwy3ҲnCI'ZvvMXVcX vgw1;xE#W3U-QBri$?tsl_ɣj-7xE[w TXK!ᴅq|_?,aWgPU3K)vawv#@,slJsYt YZu sUQ1R+ y@\b&3xg+ąGrhVQϾs8v_s_ޕ!bD7LQ̢Z3򓈦ȧ;VZ>Krev83;d2QױQ;$lVաì+)g7)h5;:9^j}h eDυl^AC  C#E:_0m 4cgm94o?6:shDȯUC/gXKagE\50@.^|ؤ@?P̅k3$ _C_j\?Ȥ37bEףvQUahL. |4D3g>l/5sg?-%4oFo"F~xgfJ^;mS=gp>Ƈ䬩EEA i׷8 .a tbr@8}1ߋR G/aP*BPz˷o'Yba'; Ij/8y_2?Oؒ=D1[6l>?^xe^1ZpSXʖr{ ,˾/0 =PqA\%P{G r1wSIVr@8mṯ }vajO--6GvTAVDmIѭ2]EZXTF>{  N}qslh;]ؖ=ɷo-\kUj~{:E鸖0(jm04fRKo<"?Tⷔ,;# 5:)U֒TrqD Ϋf #=O:' ڃG$@ J'ژώqoo"}Yuó=b,ώs_d&]蟽Jlz"ZB/7kmbEga|fV{aP R ŵ)V .{o'{ <@Bdm6hbs͠~pL4hi)TjbJ ]»:w8 Ysʍ}W1:w{n9G&@ FsXϵ=oiѕw$GIQ~վXc`#elJ,:yc6aBh1}\q8 Лi<qz[aK'ofpC8('EMv](/g oᡵGQ,@ ]&x(kʩVL+T.@ @ @ @ PefR @4w߬m{GyPz;g urJ'TWmBח1mNՌugͯm;l\]׶Mw֗7=~WgS ڶ#5aF41w2:7@&G@8Ys#H-K86҉x z?WfB1_nᨱzDdOP5H,{C_avvK;5*ϦAԂ|myT Sr^kSNFfW$;(Q@8C`Bf/lz0@:8Mp6M#B,8"8mD@έʹnwI~qCvk1`vU2-pC@8w.1O W = E"3`NK}7Fu$@mg:jgܝ`g"~3;7ߚ\eaԹcT,FҧD;\﨩"u'^]XS^o̩VጀN{k+jn$ [;_޸wa[[V `=k]}b5{Zzߒx7BGJnnQy[c\t,` %mk$h k[d1="A?,W;Kah}tCk78KOC"gjNxE 4HI169U RY#ZZ=,m,럛w:}"j sUimҝە1_ z83N,]8.cdnD `7Atc!CY۪yc95͋c";3P_h5^ Jʚ`ʁ£ܞ38CT ѨO"f.\)&o(47M^akBՃR{׵8*렍xP*P0bY;lAN\[7^P l#3~32IC~ȕbћȠ>ډYgL\nUǀ@8`$Lqd-*ξ郞K-.->ha/Tፇrl x{ad{70 ~6~EH=XĜt6 ͭBيH<޷[yޞ߹)'˛X'Y6}c_ -aPz`@~ Y)hTU1`c_t&&=z0qA\%as}ˈ3tC8QnK~(z<Պք_;x]ܯO' C~ш@ @ @ @ oQcs4/xðOWZj׮k}W똓Ti,Q#E-3kVYo_6m{{*'R"̮b>f@((|j.LA<'lO?10?7?{@zIrNS8)} ]f 6!5o7z]K%esSuu:МF}lAx8˘.iznkDLa}jy~΂*jW*9>aC`zKK]8~Gr4uu{r~iA. %wLVzu[s]D^E2_DnƏ@;SK#hU/aǬޡ?GF7?Rk:Q.R]^6sc5@C4+|Obዕ@C9Rn+*ƱӜ[#cK0<~Zāɒs4`ESwrң{P;(&c(r綨m0%) #4ww[?#k&88yK&/ˠ5u(rF`9HY?Cjϒ#fYzA0C׀<x:⟋U>Ϛq!;tٝB;NL[c_DܼU}_6#r>}2y:GzrxG$sm؇&^&v#=܏j]:jo ԇk3-:;g8"#fŞrʶ --U7hz)C[SuJG~Za3ʎV-z@\2S$NZсq_=|:6:rtc ׵,|n: wSkdk[Gtnlp96:(ơd{ s9OP5KG9dԝ!pY:O AUMG>4PJ8EuGnP WӅoRzg5ts0;U{/@&;o"ΩYk?EQ^  ]7gZv'FTl`t>)rA)R c\ K. E}?~`Ƈ!k9#\Xͱeeٕ6;!gݬ):cqj'K3(3jO"Yۑ[wմ~U{9ʅLoh}tCk78KbVC|S-oNo!u?5'"D r!t(f٭.]iE-"zK)vagՆ `1!ugfU="zѴڶ֚7&mk Cԯy-.gh?ª='~PUW%@ %/|[cNUVD9a/8!Ű$\,W}˘,zJAwwWI-cyZy.~Zq+?O#&gsDZŁq"}+%upĢgE\{3ghDPj=wSقcB`/EBuieMTGaq9pm POG|+F DcL1C=5G MYzf Lyb <+ٯY͜lRHD NEO>. IDAT}S\5 &T=(E7vu} 7pTAj2IC~W1տx=9Dcgm xFӼx*]1h=b =uG\k^a(U5则R1ch?_y5wz[f\XNPj| )1VLhvzIhC?kd^f Exgf`_x-7Yϊ`9`1@T4gqHVcKWQ1p\R+xu|#aPG.gX}ѯ'AV聾E?>,?%l*}l}ZQ3Җyk8PjL&{s*ߘLunv0<3Չzh6;%Mr2\ÃNOۢ i`;d ~FKZYĪ?h2;~ƒ?IN Uۯ`$4*]c+Uimo{T NAz }'iIG:~X/߫a}q5+1+S뫲ߏuծ 8 (74:.> v홽f츥ѵCJmrO[p=z.CܭS^Bt_"8lL#C5(zu~fo(ɮ"5]}`bn̟&kY[Q/&>UNuzʸY~We Y7i;T0>V%wEYOgǹ/Rr{9A%VwʲKS:3Ünt|"aY8&t5i^yOK 1_,5ЬH7f8f?SQ݊Ǖ#&r$ _i˩1f =+5zn\}xNž.=4DZ\=GH&wG̨0$eXۦh04CjPX1ni M]]ϏGblVQ;:]B5ҢJa  r{cPl& >96p ؞(p;qf%mwUsKMн u }c$ycGˢvx^f'+arC[;n,M]/"n>+mrFX_ߝWQ=<#!w?:HG5 gq1sJ]f+8km^_cM^tzޘqBh')BnQ瓃߄7k?ƨoM 6RƆh_*Fviti=9eGvŪzb3ty/?є &%ԛ E4kW&})(7s;Y;q0#=?Ji~@Ӫ^vDYN+|`H ȮQJڋwEJ: N3߸!hnsi{B3?70XQ368I _ɮbNF ɘ͍(k|虛JfD>q*_:Eg7VKaN1$vX]TS_ݧf(fVyMYګ#)cdm4$LԚi֕;E@swxl%) Y>MZ3ɲZ~lƁ[]!@80[s0WߴP5KG9Qzł(/J$k.&B8}wOvW&bj-?)\@3yO9 \,n: oO\XГpv1qկ}_7`Q39{W+r8VPyEV'¢6]cVb*Ϧ"FMoI?ХӡmOKa. 4TW/ٚ=^;{6>6NR玱S#gZD&~&PVZjyO@@~Q.ΛZo һľzXF_L^@S4KڎܱCzNQݜSdoװӡcnMۡy~,E|X{,}A†>rF_UCױ\ 얎6E>(eY,j +6%xr/N~R_K=1;xE#W3U-]nݳd<}eKG4wqK¶l߷L;#-k6tumWڮ+"a.LrcK7ǖi|;Ǵ\3ɚ\ Ϧ"ʥO.Jc.L 㝱q{L<fl(%0LvM.5W̋: XC5m]-"T\ ݶ#}ZO_W 9[~X l$CV+oeq 3T_ߏ]C0oޞ ¨P|9.rz/ .ʅs}+(fQ-IDS@vJƲБd F8<^ӱQ;$lVաì+) ƻG=:$Yt_ٔfK)vawD|&r^G!)a`Unjd2X*p級 )^o"64U}f;rvyåS>ݩ_%5 &]Wvj*} Sߎ83\m9Zn?v0tlV𘤲W({FWTmw:},WhvY-)cp0~Gw`(5VJ3@w` uІvhE\}ffi}OeC})=z%(K+kv–/kmrk ,6:b3F# (j\8&ZӼ\sM u} ֒,cFڪ]o:xm𻞬u}I\Mfz-ĥ. .hṯoFo"\,,xD^'囂Uc@E 5@<5o_r`P~6H\bGޮQh{fgE\AxM(hڮIwm*N7~:ʝ}&Y^g`KДu @xp B%\jv`QouZ@TkQrN~h'eIDϑk"ec7EuYݿ'!=!)Ķc */e| XPuS6/_ʸAfEm'c0JSFj@ՓP^ 0P=[#I TN ј7J Jγ!DvRzY6:\ƹ+X׶L].d/IgG%[0 mޡ{R.B*jtR%+߸'iv[M=b{+ AmA5e]i|ح1sIu1O%(_vRaDi;ؾ]o`fi8ENfߵ8٫wz;;$HnU.Q0ߏ]E1oa6Hpgt7h}#< y]U_lOhXۦh04CK<[:b7jCSS&?+}3;#Yƺ04 )ʯ5+zb3t&rtEc/tBpxܓ0F)RER1NrUS>*@Vg3{Q=3j2#Iw2j'aZco3q=M739=oLs6@L_tqg7T_tYm7}_wU C+GhcԷ)cCWbq(zgG=I3o*S~1ߏ]o@ 9k^cO-V}ӏ:MjY]Jb@P!)!\Pj\zh|1џ`G~ r%4?ە;8`jѕiU>f/@ @ @ @ 8@k^cΔ=lgpz׶ͯmtmo8m;;ӌH\9-/0$p1=eh\nO>^`VpO-׉AokĿ24r~.^1N@4qo0 ܽ3މ}^8}#d҈&]C78CGl'^8oK%f_]' ?WԯG`ܓV.Gdnd9HY?Cjϒ#f1P&.37ot}eifNM&/]FN%09X.`hϟMU_Ӊ򶑩r?:uﺿ)2.bz&\>!`3f'ܞP:aSӯOH'\Lhα5z Դͯy><kߜ2pj6[~21 `sz2?t#sm5hϛ1Y?=Ͽvnտg4r^^Ljkf{Y yGͯmﳲuN+C M1sv޷bZq'䗗0wk׼6ѽdfͫu5?leoAѨݞMKޘS6vǜj9>cf]EmZC7^[6?F>}jr͇So֮kq_2 챻5s|k_67Z=~p5Uͩ2/X[u]r6g6̯mk =[ϊo)Z8q J/LF\a('9{6wޒI<fObS['zUCי׆A\: u}n)oA7N69Q\-56H̏˧ִͯ]7[>*NEUߜ[iN}|{ `=k]}WjF\F!w B1.<+Xv1Nsen-+y짘Dt'jO;Pݞ׍r{ 6~[Ze^4^]=*CzRmHuQ)9BfȕqUK|Tv֍sd!@4wqK¶l߷/et-U{r- pr2 IҟEtfQcYHm-*ot {J]5r @n9!ZT0Д P̵MÃQ ;-ԷvƮޙ|Vf1z;mҝە1_Jޕ]ނݝURhFP־EjZkޘķ?P<.ܕV65>Tu^` 嗓U^G!>)Էb}sQ̢ khf.g2*gY'=7Pߑ}-xvy1of]Vʼ%D-nA٬IeoF`YAlPQ.X(6KH02ad~xSg*K+Ɯ0W.ĻۅޞV|[CZ/n/$7Fg>c3F# \S蕠.HS̅k*18* @j|_1Wtw1 M|NC[} qSV<<ӡ!}{@6߾"hz,\ޙчnًF62*s'>Į\&I/"='8/0(A:S"]*C#`}c_]튠VrNpX}J‰kϪljV:~*[(7.^72w]`}{!&9 j` H2h`e_kmr‰sc|HOG{|N ) ;k[5D4/rcF\sE F*@ɘ lZCOG|<lPE۽bg A3`i&ݵM@sF>6ڿy{؟Klw4:myU1`c_p ps>h;0{ $qf IDAT-ڮ :rva'; Ij/46ocAZA >5x7't(UԪGc_ f.gX}ѯ'AV ̮*Hs~IF  ALDϥl]rÉ@8wxN~H;;/BU6)}q'JZPz(v^~k g;HFME 7nK?x[3 ҂Eb~9ʶw6٥Fb4wLhR/j +SbQ.*~HW1<qM{.+.) Oǵ@QSm y5^,AT>?:' ZzRGүۛte'Z$jtRYaZ]4j=YMfl\cUݺ=Va%qkmфY~}|,2q.Hscw%&P(_(74:.> v홽 5qHva{PȾ.+S{?%n,b՟GAxEv!%xԶv9:s <7ٵZ&v7^O,n )@_D5\aߌxJXOy~?jU.d((r]4-<FG⏟U'=̈́u\SFkxP>%_bYC֍dX5^S@u/P{Y .G04[~|?K#ew9w*/^K[>;}?*1#}Y٥wuk_hPQ*&>UNuzk)F@NFXۦh04C-W<hoU]p@ mdٳ|4uĊRO5c!Q5Ң)2Įɽba}}w^lUc1W19`ǁ+-Fv_[ZBiccWrqt~I.b&?+}3;a#FЕXo04 )ʯѭ8\C}`z}7{-R˵_e/@ y'?N:n @ c@ @ @ @ @ ?|0s̚#crzsx?==,p=GҎT8~Gr4uu{Ss/ĢTD_F #H ;Sfyo αSpk㸎"% Mk =H}M*q19-/0$p1]~}-RP]G!0u=%NWϭmLAXEpJ0pb7G ˝MiQ֜@GPQ$g27:3M]gn*>/!.6ei4UR~/^?O'txG$sԚSzx~RVPڳdbȣYM^A4{dQr;tٝB;NL[09X.`h?BO*Ť~I~tTo㤨μuUuuuOtυ;"!F#~4$Y*55oPc1٘#DԼ^g7rupaafz?R=53/:sN:ujQ􃙏A_Βsq?|0FOR3Ro@>~WyxoOIuOLD2G&\V^~ { 7fʌzERkFF"TU}DDZҴF d{t0eswecNhv *,fXnנU?K3aZe+'pD,CYsg39z\wpF/lHX~zZ6VXFӚ\h5oM4N)ZiS=RjAmR?z{ia_@ Ii(JnKN@2`1֖N>EϪҲdT;CNjuN*t?g -H*Sn˸/"2-`]cwU[Ts-TWd}$cAӊqW޻$]qJa`l}R7<&U~˼Ə#п*ye3N7"Żbw^s8f`D2m \doz١lWM`C#n..ҫzb}4pGL>rײMD=(%*&Zvޅ3Z;b) M/x<3L, ?D3#`W/~2S%Vm/#L.arfGBsZxG뙠5t͞O- ȕabۑȪ2:'quWdMG]֝{M5XugicWa Z3 HvF8N4ZҸk^[wOFz|cJ`__!௤-gM G=wԖ5>8ޏEI/Ģ0IȇM{=:I{hPO{L5avZFy{q]ƏNVޫ0۳gI]dS5Aa!!1@z$F3;T#DL3RLWք孲|K(+~xC5-7#@>{w@)]#鮮ȏ#VY[+]9^ - 0 រ#B@P]7g2/2C}?$Xj"_r/Y wcAOesaRSV] EVZa'[CN4믝L=ՓQ}vP(CI%ا ID\tήۉ]` #_@8(,wbRZ:8"=fbI|˰||]V vyZM#Ə:#=G2F+j(l?7]#Sџ~9ߕVwD 3F~g7TG`L6.x .}iʙvۼRk;<< jqZc,@:|UBƦcC5!Oi6G 8%=cGܞz{Z~.?Rf Mxީ߉o=Uu 44sH@FfB#}='WDՊ^PRd\0& `Ph~hkUP+`|& tIrzރy(0K49$,Rs_9䄳l~Xʈ ^R,|YGF>#==Gz7:\Fk_8jhkbxY~z-Ȱgk<^*H!wWNzSnFMڣV{9O}QNiqJ]J (%efpO]%VP^z gx8b3mD&ɳWK >vfZ}XYx̙w|h^=WMK)9,[G%n R'M@kTu:l'ҭVA3DgŤu>3{(w] &$]x2fěXrah/'IU> AܺfvZ:tQYR>Nbeع_> ũVރjg8y6l%)Gx]k {l_ v"_Mָ|nJl+L]Ci/v$F2N`jO`MK-RN 횂 ̪p]}dtq%>е&.#je;hoOć7gs&>[WNx_9M\oc?S[/Xxx~=yr~8]N[cS$: 2k?D?ڄ4DvjyD5H6X:k'uKh~o$ iO_CL?^NJan,n*JC1̋$;=m04#Owr屋#m|*.1;{Nh^ڳ' x/E/ seCw/h\Kt0Nؘe\0Nߩ9!iZix^"?ߑ~'..tqOx bvR_ʘc)<6w?wjNYohʿ眾O$rO^h\\.'MhruyllYxhIc}:"oMcF2KCl>UA77l UƊ7> àB|`^rIlhnK9>uš|ŽvraMW ҵɇC0D7}9]٭- Hy?O~3 Mj9Őoٓe@sC-VS~P6L ޙˁPYsPzUG粌nο9BICP_Xnd@?^7{GOϲFꦏ2m*L?HqgV|xbc&WfP%uD;6vtv2 CNnF׿v-?;\xz[kk ;`Zji.AUO$3'7P]K'4 <hib{D+Nťc 0h%WIaOOX6'|W6섦''>m7?a㽾ԵߣSZp=_:AN? ]~١FrG;4)Z)vYV/,h^ZFzo/lHXccr7tUkKtOn#aϪҲdT;CNj8hˮ|{#yST[%>k!{!iq-{SLkHaޭ̨._{pw#&lkY߷,uӚda?L}׵oZeKushP~q$zwLmXs6N4ȍYiٻTӉ;{ZwQQuOKEM~?@ )+S=gXaMY+s꽧ޟX_X nkj%{ej)djbbtz="ԛnP{93N9~h9ˏ gtl-qF2i[:XZʛX6XĖ1gz- x,? ISߞ 1XM*N"fV,-Wl(<(T6MR_#7;)2=+wdvݏu|с)/co$ W?ޖ&qE/IA,s"zE*\{pȥ}_:5 oVL+|I3*hQa:0hrHX澒\{zݏ3V0} KPžA-{ysc[,.Cyci={|h^ϧT=Dw?0W:ުA=gӅRd\0&0iUE|CG?/c|QH9'L)=1_$-w/DaF727ZDZIO  Ń&!C?jbYMTcq<؝-` :\2Hui tf`ؓ> #"zHF+ͥH:$+<1=79A@"oYJVpm;Tt6 ߿^. KMzeY.;QylqwuGN͵j!K,k(7#TM+á8%/ cotYobK?=)r.cI,h8BΫ}4ODe7 <}Z AܺfvZ:tY,&cIDAT d> \cjUA}$>![kKi]He_2<:]KN`+şLb8xJM4uuP{Y*7x/4;>S kbg%bg0|Mm i> )+t:RN 횂 ̪p]}dtq%>е&.@&7wsp/ӱkfk&"?Hm*MF ֙>+:XrN `_kP-;mq_Wr>]-(!3/t)q9.ʷC;O"-5;c|8~󒋋؃^а WE/ sD2f4/1g@Ƭ..>~CxKR8"?ߑ~ssq9Wx!$5B"m#"c\wj?O"PeJ+Œ z'/ ..Yohʿ玏>e'@{] ?qqqqqqqqqqqqqqqq$IUIENDB`./oar-2.5.2/misc/fault_tolerance/HAdocumentation/schema/architecture-schema-4-node.png0000755000175000017500000022516111757171206026731 0ustar plbplbPNG  IHDRz2sRGBgAMA a cHRMz&u0`:pQ<IDATx^~mm@ Z8-n- `!X@ Bݕww{ߛS۽{]9ّ̜9gL/E@PE@P7RE m,^F[m-2{5_۫((>(@XzoYJ7_{~/=Wϰ(TuVP2Z" q]fN'1|U(G@SH+(~ жm?7\;bPYfFtGO@CP2dJP C==nϞ28=rp~KժU[h*"2'"XLrV~Xu{H"tT]vM,:0E@2'OC"md\N֩թr磵vsv"ʜZKR0[jIMY}/ZBBE@8ʜ"AZ=E@(sUciC*ID@S{UۤlcF?kL@w7_H !-V2' "odyӳ uⰧz㍚^P"+r"߶UocEs'p]|E}5'lA[+@iPTܵTE@(lv_|x+\6=Z'9}"k9ź@`nF8k]9/Y05{KL[0{umBQ攺a V,(s(AsQL=).rM7,_<{T&=[eN+ZQE 09f(!Y(ժp8nE3W,qMseNueN?"x0ϱҿ_2tP?A+΃2 >}|A>С2ď1m"Pʜ K+@wlf+Ga5͝6rDC})8 )uTR̩J=iUZ6?{ڕ֮Z4w?xxϯ9xi|(s(F@_M+iūW,3YU֔QjKfʜ"= r@PTE /9 h؞]n՚K׭Y#PI'o?~Oʜt0*#ʜ!"PJiT'z Ӛ芼,ߴqu7uLɣ:+s*ekي@P*CƜ l9nsnʊMpkxܴi.]8U}9oLiPT|"@V4qD_AD7[W|oܸaÆu7cC9w"H9%[Q@r0̩o{k 4n=oU+Z`}uÙ X0Es'a;}2 m".ʜ[KS0~ݶۼ9͘8`֔ f[r  %tKrc["JӢ9+2;A+P椣AP"Zw?/f?q؁7G$iQg[U d:Lfb9E˵r@P)GԦYݽvOZӢy.|lyNVk]GxD@G1E@+s3쭫ڳÇ0۵򴉃9 sZhl[dΜòʜ>-EHʜק"E Q4Wc=9-=a%˗Zv[o[;dnϚ2$׭{5P1@X(s i-GPeN)|E XӚ+nc>t=w{;Q&͟9f_~G[lw -?-u͘1[#aF~@P>)Ü~|ݺwͧoC<5gM0osoz'b;(n:E 9&VzOޞ m{YS7^8v9wsZ0mڸ^oeNaeN1@~:t(9zGrU9W{7_͟5vmZ~x;w߭>T=j;u\ f{UKO9Nb-GEqZ")͎]v=ӣP(!uZfcG6W^|3NBwQ!8_gs)S~x{`NUV2h-ҠAXTU+XPA(ʜ@_έ#6MЫ?湧k~ꉕwگsg/7V8!?~FnQ#U(s m-K9nciϟkaNǏ:7_eNa92P+ʜ܌)*kW/_0{ܸ!?MF_Hj>!#)d8E(sr3SU*Ӭc*Vq7}OeNਹ2!Pf,bN >y[\ZJ9}-;͜N%߽z9S [)&2bgK2EeNnF@.4iT7i!C|뉿n:seNK2⯥eNnʜ~1[+͚2xp<ʜt4ʜ{jժn*i IGLPӎj'eNnzfZ:~h gN0ÇvSwC|M74ʜt $eNSmQlP䦫㏀9[:jm2nH\7iPmtNzZjw}ٳ\G@uN:Bb2vV; (srӋVcy˕wP>u:|=v fLׯ{mmዧ|e]6f7u4nP%MA9ESJiA@2}G{}a}w絻/9z\`}Sݪ[^p駟Ngq2'XiH!)RݡIʜaN~uEg|=5Y-W ݔػ|Ӽa[p\6>8w{6u?<עE 74"̩P4}DPjeNnz!ɻUuן]5WSu?+Ӹ޽~|л5ի禆ʜOeN%@zP95կYߎ_^v'x6'ث|OȽrټ_"5_xA:zj74P:}(s*-ZzPa0^{K>!#)d8E(s0EItG:Ͻ|ɜɣ:wnn͋wv=.Mn>>G050P&Z"?(s< 4hy't2׽9u6SAPO(s p-NP?cp!WRKwo/=idǎn=qa-_|E 7O2! J2஥* :ሽⲋҠ.{YGo0yux}A`jVa")L,E@uAƍs"oYYٱu|F2_npP `+ʜ"ϕeN%->)۷/nL%%pAKʜ|Q3 eNc%*A@4#)ͽ+sui㍀2x־89>]29 z-XPc (sJsǺʜb}Zx#)/eNO eN%^ V9H3ʜn2XwV>(swiC@Sq%C@SɠׂeN:Ҍ24~ۮ)ݧ7ʜ2nݺ'gƍcgu}^JR攒N^39%OEA@SfWN>r~(fcݢ2(-ʜtx(%C@SV4 aFd+s o-79f2Bߎu qcɐ$"%,ρ_d֬YGg?̩Б/9yM*9ʜJZ"̩Pぬȿ|rgʜl9%C\<.MZ ^xXzDNeN44JSzYQ9eeN~NFd/< w3h BOCn*"lo!$I–(B2&WKS;GeN:>!)+sS2f5<% !k&@rcTDIY$?Ei#aラ)aZʜ<"eN@G2(!(sŜr Ld%RVdcNlH m+?s"+| t({j2'OCG@S@kZ9Ϝ\r1'Z:'B+CÕ9yMʜ Z"̩Hds0]\SZ /s͖)3̩@4yTPzeNE2'"{ .9ᖄ eӒ#b} soqق\d9 >^*9 y-Wp[>+O2X6 \U-$LAI&XyfMBP.BPT(b>"(sHGh5҈2479X)ʜbqZ$ ) m2's%F@S;@O3ʜveN:b2vV; (sJB/j"+r\PTӌ24]")NʜЋ(s>Wb94#)ͽmWc (sii2$+ʜ"ϕeN%->(sJskە9)ʜbqZ$ ) m2's%F@S;@O3ʜveN:b2vV; (sJB/j"+r\PTӌ24]")NʜЋ(s>Wb94#)ͽmWc (sii2$+ʜ"ϕeN%->(sJskە9)ʜbqZ$ ) m2's%F@S;@O3ʜveN:b2vV; (sJB/j"+r\PTӌ24]")NʜЋ(s>Wb9x!xbNK/=#8b]v}ϻ۱:udjږٳg{5jp3ISr<0bfP'A 2+ܷoߒ&eNbm`>ƌc4/\;ٮ]\5jqh"Ko`Ew}Yjky›oi`2eX#@dqnի[7f͚XǡNo1{Q[m7|bZpPZJ mf.PpF@XE^ӧO5O@xG ,UT`wy6*E gt=hz>mƹ{a͛7z(.![c=իueNi$fG"d͐s9O?{Az"CE2>{wYW^xHg![~=z4w}&=H j޼` Ѐo+kLLFGF!j,|*h<Ŷ1)؆wAy2G\԰J$ 3 *eN+t33 $sp,ǕW/E4I"ASHkD$aH/D˵f?~}Jn Ŀ6I: ["I| 'ɟ|O%f]|\nVQF̘W%^A@0묖׀ƍ3¯)QLF5[F,S% 2U&uˤŬaKf,So2I˯pe2'*e{2 ɤVZ7i)i|i4mZuP%= k3@AZS$(\y啌/&L poO1MS02LOcuXGi~dFy 24+ALUH=M&M1<%N+x\ꩧBŘw7L4UP^IgXHG atٳ=h}m"m͕׺HYl^mZ\s"O'V/iCJ<"dodydd|oEfV\pB͒ify)S]@E;z">I.d!IƂ\$akYKI59@lfiT˼c>lI$j+ª(r%(q z˸j71N9ƭϲ՟m Д<#9X*ٚƜd%mgg#5nD(nh4yڧIl9EWS' +,oLH gSL< ڵkd@pcE}ifbE-[FCndKʜJ~‹fnȈ>kXQkU_z7BN`4 thp.y}H]P5=̷WċSq|l;"e|S15-ֹ"k1u6£.dSLˇzH7@0f7%AjX7+Z 4h!UWvy%bUK# $q2=[5C!vC_J8 G`oivTFnQ!2մL!8x#%j I@T?ð-BsHK,3S+(P"V#L($xGuQDr]4DeNV2G ƔxײfH嬒1_ư K8>FQL 4FҦr b~VvɘGɄDxy.:ˣ2E*j? pE mC-TjcSQwzF6=%Νapv_F|@x1@qcy-dEI\c_czi[͘W%S2vJZ+ԛoĺ!ߛĉSr9JU5P:$_Gt2SҺ#Ь=r/Uf5A\}9ng-;o~iޏm6 IK pi8_8QY cch C| 3/3l ̓ks?nx%6>POK@S'pFiLKQӎj| k`V<Ǘ ng-kVaks 7]TKF>o'tR怗~]QJĠixhw.6(sMWiE ~q<-OժU"C%mWT #?S,i2t6$fXlV4Eu-HxV;1q'Z!)fՍ#xzK) $+.s4@g$G˰gc&tZ!)a͉?pCeac-u.4 @pBiVgg)E 9&V F.C! c%P+aB+0(QmZٸ")=PҾ ԝԁu SM+)02%+/"V&(sJjjJz&tN% *0'*Jp%G>9MBڄV<57E +ʜt`( 2DyDA8uą!" z!bhBAV(b^(}dݕ zfKAE$1@e7C\b8mV/eNc9 (t3[X#{q .>@HjT)K2EC̖ _DJ{K%].MeNӈت#;>Gw dx@&Rr)]C1R/QnFF@rmzfKb} {xH&5{4e(s d-" !czogѾȁt)qf2I>D@K4Y o@اR D[lD*-ՓURBmbTv i(sQ8 "uώgYb&d7M`HajӺDS2 P<,tUT==VAU I:Y%PV.5" eNpӧ҂K䁾Aw! '!zfKPJ2b!O)M_ԣBo-HSj^YDTS@@l mhI(sr&I97NRYߡf`~:3W'NvxuP2j).Rx ˜قEIY/ˁt} rtLr!Idž"?u(GU'R?ҹGL"G4"P(ʜ EL'9WAw3k{$,}6ʁtb1]m8SP+M|jԨ75-3[+h(GT "eN:B U聾!=͓ daW#X(eN^MK3[tcT.cUu K=$8=*sJO_kKs" N= 0cJr*|܀io(s>XdSFS̖<Nh 95"P(ʜ EL' Y^ż1_8(1o7'M\:G4A1(s*=}6QwD/@!^w}sR( 7}* -Z$4&m3[́t>Ky@@$ f. èߝ1ޥ9YEC;4D9G+1M\(ʜ EL'JAKw3[wnB=%ؕHj9%g]9` fAB~n9K`+t™`NJ7 MӻG@{4e}L%=#  Hc+lBdԾ7}=ʜc);xB$1)nCl$dՉz(koS#ʜ!A@/e&%;EOV/fh4/=}eNi ~~Bڣ،υa ^w^+~h@ eN:6R˫FyId' 5buDVCS"U?`mJjIȈ-P% {Di/Cy%+DD@# I$3 (bHİb "gSeN: Y@$wsFrf ()O߳ A@ǖ$# $7RۖقXBB7Q&zB@z-FUҪ_E BS"xE=.Q&4 V 9seNi$(̲[]n3[d|dwXp*zt]P*Mde}c4:gSAvzxZ]ލiۭ)p}y3BF>a!`J(]gV@NyʜR>|T YIOJOZמR"QW̞̿ub 9eNi8.HL6U%m$ٯnD ,-%m(sJ['YMr6Lf$VjRd=||}9^Mé .}LÂ>Ebt^\Qu!c9R)x['k&3[ M̕{36!B.TK ʜRIn&lVbKr76sf fCd_5`GLP\ Pgx>}C<%rf KUaգJVM~䵞TmaXyf JP\xPUe-ǎ:2@@Sj!! j CJedہ{C0$.xYqҕ9%g.9S-,٢ҕKSCI"GI@(s X6X =7X5 Hӡʜ@Ue*sJxǽyH=Y4pܻ2?3[N *"]*)s{&]vOoqh&c"abq$}/9ūP[+UgnxIBwư }gy!m>d"IGE聾­e ܁t!NNa‹K{m\u"IFp:ldSQ3[ >h䩠G4"eN:BC@$^Z"~%Sn?1ՁEG9f\{IHB͙-L+8zt]1UE(sJUwzoɠO}H`'kg OV Gle%1&P\"%P;  / ꓞ "A<b-Rl0vG0i<Ɩ2ʜt\9KU 极CNNVIp̖. G(8:|D@`jVv0%ȫ:|ڔ*eْ9DjCSZA@}W΍ˤOߴ2`Nu2'|u{ud'~uzf < CZ\P-epȕHzo)!}e@GlEqP B@SApibWY"uSu& CpHSec=%U^_d^!@EeN)6ũU͙9.$g~ztJ P#U9!}Q@ 1 fjl6v ^?=(sJO_RT⺍% H*0o5DfJ@pjiC@Sz<ʁXfX4,:Ns^иقO}"̂ƀ&.eNŠ=WGC$@Lϙ-zt])UETpv^o[lŞ{?OM#=buEvQCxg;3hc>4?LL!%DQƆʕ+"#</>x#ϩDkr FC eE}733pLFs%DIԨzKw}-oABni&!@U8+jt&*l馛`Q1#iN2:%`159Ŵ"Q#F{챩Iʜ Z}Ii aU|3k&X<>{u$Mѕҥ -zGI3H;ʜ>ou]h3aN,ZEΝ5%n?s E2" q]Du߫Ň{/)LaX :iO(s t91̨LH6ee8&^zyIdx*UW[4kPaG$ĮPeN2]UT3&{^oFY{M/(sr\}='ГURo.adʜ2DfvMg{܈nL×Mrg(s*r49Ud"qI̖e [ފڰ='To޼y5G@SAfW]9|Hs&}/7e5+;7>6D3؞s2bVkz&0<>E2'F:ڄ%ΒP6v= Zy֮Yj"aO(_(s I{>]ګ{;~S֯]zCٓNhg̩k׮Ň%A $sg66\]ױcG6\sUG Zʥs7nX_ q(sWCz-7_lފ%sW-[q5Msh|/sb۰} eNWt==VI(sYfl9@wykŁ͙۰~5+fM<7~ɽ-UJeN> Of mڴiE^q#z};s2gߠAғU\d!0'd@gӜqx"̩[/._2gm׭YvY l1waݿT5AsZㆶeͪQ3_jD=Q}Μ=N]D&,9Yԩ#L|ݤ4A $Ӕv~Hˣ*tNo<3oS6[_Nnt",]8c҈c4կ2'YG90a7)n}7[ͤ]h1m3vP 96(>atY(sr\@:G" L$,2CӰ2efUI$׭]b72o%+s1ÜFiZz[CUK=U7vAS~:id%xEP՗U'DXIGg_GdN#|7z`'o…۳HAgi߹GNmNʜbb(sic4wH4O7%Fe77fׂYO؞2'9 2\.TkUGDKœDTaA{6Aǣ Kc8m|_ fuomy sBi4s3Ǯ^fPBmXk+8ӆuč3; kUˁ\VPdc잫V9֤A+_rUZV!W[^y t89|\2m^<+aNfN4w/b#M_jn녳'TW$ٯɇ4:g}m^{tKtNœ:ixT>LBU\[X`8ؒB:44ɇ; eNV1f"6mVDoRs2&י-r{i&Gi4~NMN߭_oaNIe'B 7wwT5-3&U甘B]is{Uvz[iIKlln℞}wIAgs mBtNFd"-)cY=u3Nb?nՖ #s=Z<*a P(\ȡNM*Օ9%fPCWfz?p 5j<ԭ[}IݷDd{saH x+]fiySΉr\Zq\Ά9oI.D 6#'0JnʜX*7nRJC!9Yb=堃rt6u9}{u?!>i9VU^U/ܦ@d:-TW攈@h/Vk3^>p.v5\D?ZoyI&UZxeN>taNpTW==w+Ȳųzvmv?mxۮ~|lgL9YnvI:,{Wisn֯֡Te=L#}A΢Hm:cʜjԨA?p\0'FDj֬YV^7|/_cd~g͟kژ߾{QJu|9g[ŰqCΊ`beN>taNcbikj=v߽;Θ< qtLS(ɇ 2bSiS s/+=oCZ>%%_crß<7b Eh2'MؒU Xeu~]WeݫVc}z ëlNqʜam{fOl.훿L}zAt]ߞ̞ 3wtg9ŗ6qªf4„/֋cErOFD'(W>CȤ8aiiWR}㞲MT1ree[5+ ZXmvT}va뮹O͌TQ@ϭ+K2'όJF7fW}N:*{V>Go/!̉x+s‘9qp[:u2W?Gy$x]0,-W!+8 "1_ O2ʜ:LHʜ05TX'y_}U q-IeNGeF%8nLsl^zOm׿vAnUzKHun@v`N7Gk:waݿ>mj-]>߱SsO<$ӌ @@S`J_έc#e'OݩMG9?U_ !inI#;fV0oڽ. X+XZ=Z:7}c'O:M6 /}oVVzV`l?LBgeN/E %>OVhql3|6e6!kժ6mԒʜ<;9ɹu]f)zv_>gyJ*&RQFFpʜ|\̉WfI '/[ȜfMyE,qC~t+sCMQXYeQ5Abt* F7̉P#s*v/+R299#s"f捦*Vq:,מ< V$ HY@獋 (ׯ|%]Z>st\P䚍ڄ{Ә176Z>Ok7g-2|BÿGoRu)TK: Nb[H#3eN>9]ys]fLyϛ9vݚc(VCړ9z[oYg% &wy_ n jo`]Vp݀gõ?^> V3I mЧ.n6 isml㰶6_a}p9ޤ2'x;szN8?6*,HW$֨2SYӫ/<|g\LzvK 5*}_kO5a_Fgø\F4E4_|Ө7G6Ya=v Ț%_+F|cOַzncˇ7OOhY>O]ۿC8ޤ2JpS=~{6ϙRoBE:5"99rV7_|uiۡ4Ccn S|4߲p\{0'm<ӭ.'5{M?3~xO|˿gمdw:BOK 3)-ԓ{M-覊M?>_+?247̉O<4lźki%Tl}q',_ߞʫgKJћ`uD/H4o>\7S A2'Dr(s\~N_}̼gZ-^n!Cړ9m‹,!'l#_Y-[ݻokM{wQ~^6G1*@:0ٺܵ~)KgSTxü#ZಹTh_.ԙpq/VW<곪Gou1'bܢ>RG*"7/z?B9\9q.4eL<7vxϯz=GkOuCsի'<[~mlÅp/Ƌ6߿[sX6Y-=q]YXYYKhk/fnvx2kO=wKN,wHټ7 4ٶI&z`=ܓM0sBnv*9 D:O)sC$2'PŜ87=kSۭɇ# ǵxN jʰUUY}r*Ue+N+[rRcU6貙GM߿lՇo\q=f&C 5lZfó,?l*[yzғ[SܙG8l[/wO,@ʕ9Ygt^YΜ|sQG:lieKO)[rrzǗ̓6]6{b< {ibѰOsMû3?QN,FWw*ohF^zx '2'P6鉇n~?743&9 I12',oIQS=n&t+[lM86UfhӨS,3"b [DquOIV;K\ TmœjM#m5罹>!q.!!nrt3u_ث}Q~,F2DrO+se5<`Gi>ߚUˠ>E6So_pm2'0 ̕uw΁e BWle(~^ Ulsa DTR%=G֒5sfb;l+S.^m ?t+֧/8zs:_vmGk]8ģ9z~(H^>:=cRZqH+shÜ_~ֶ'&fi@Eކ9u'jT̮{nȑ>4LpSIV;VA|\5&U6TlZѻ E#_ٵNqoғU|d-yZp{GOZ6i߽lQl@I Irq&95i҄Kӻ5}ֽ{W_#,R9ü 5r.& eN>lS?9#.7i;(yf>y3mͪڽWEÙpkCS.䚕׌yն{*eî=a~A[ћ`:!eΩO{uHx>g| ŋtr@zȕW^٣GBd eN>@leNx{W/[˟wSs gb[c{\?Szu湚|³p\{|aNcϯm)qka SO=E1L8e]f.7c*q&91_wuuN0!= 2 n=# v.Rx| s?\L'G@[uxԬnv *Oٿ˷C{7}Gs]z-Wtl-sߞuvЋ^p\{bN!I91R_4 WСѦJ7̉_tEֺjZHrxC+$<_@y*Rl=tQ /8aui*xN([=կ4{82u?5?;O|ßSFw_r N=?|څ͡K:|p\{eNeq.?zҦLhc/Aћl:k<'8\q Dcbo^x.~=C ywoq8]ԯ_8O2' 2Qܣncƽ7Uew|vJ/fW\ҳU͂~ W0ˇ# ǵwhFPr0U\u-(PȘ7̉Ͻ;iD/MFm*w^_\?{CҋNG˿;~o&s"Й˻Ck+o$jҚO8=A0'Gjl=.PSYN>{]v3NӱH?}z#xM!^g$P'3R7$F?(f>A=1=7RvcWϓFv^rq:ޝ'314܀?_.bNCc}è'W ˾HћlS8;X*|ZӯjOj?HzN<)'eN{ޫfw<󔣟{έ?_'s⼔𭇂q HdfQN%3Ζ+h.3niלBȕ *q&9 лw\7XZ}W&g:`*e÷Mwꟿa 7T=$=ts{RS} R诸?|mP0 k. lO&sWMG}/{Z5[7 Ǧ@GoSr \2?ཷJu>ӳHuS6Dko2Vi[B]tO=vן1v`U>#>ǵ'4Į+*#3ZDLqfN 67ـ;xWkv?RnpހSʜB#&.Ph[Hi0P; מ@:Ll'1 ) Ba1/q&9 xpD~BĻ1, NN:>q9􍤔T4sHg[2)P/+t9L_d2ћx+DlITd4h") kOɜSOZDٙU\kgGq&9A876F+ 9EEq 9{VU_,iFw۹Į~93.hъeN]KM kO@̩5Y>ir v<4ћxtuBbđ6$9Xמ f9Y ww)WA@K)7I#k8x(/[/ɕɹW^$#,?OGl?%v5o\~I J'd́2'#]6jDztE9J<"] ꪫs5&<sE*ҽ5o/rPrf9=<G"ʜ ĭ6q* +ï?Sc&-BӜH$]8Î%$2@䐙&CGRxybhܐ}q`k_IuǸScykQpOE93=2@Ʈ0땕ЗdKdGh))9`dVC-";2I~Ys(Q49+wH98`#g9=0'5 mFnzr j~%֔qœJoƶGg,2k^"o{M5ά(]׺^볬/d(:!GQ\oI2vo--(2'+_PȸfR"™AI",PbSӬF5D}m̶? th0V'#VBdI|Ϊ+~pSHϺˢ6H7](o$VX锣H/Q^"3 FVgfg[e@U惔7r^*^^‘!EK*{̄Y,Y\7d^_32aiVh0i&5.O~`5 ZȟGE)J'Vc{auFBÜJ("hf#aK\$ LP8♓HάrUYIFVXFCD3EyI t6E62ټnX=OD9o^jm'lV+6WNcN2ryVq2&wbˣސq fLF|LUe~jF"4]HYsz(Ced"DQgSS7#ݬJ|,Wء$ve5fљ$kxfN,ȟܯS>bJO8zgN3D*3EeYVO#)"H7/ˮjr6Lˍq#KH/QBDZyd.#OUqHE[-x뎣7%̉=xK"}(s l-*8= ˃ -ѱhGܟ`W 0\:ޔ0'<$'mS (s V4=8=6C b<.%=B%&M s:SveH-mi(s*@}<8=V感'H OV)4؞` PD,q:ޔ0'"LeNb*zgؿu]w]Gq3uAefZ+E<裦\L& ǵ0'. wwҋ ->nMLwM s;w6a~(2/Ngf4SDڵM#FWii(s/FPK/e32c~ާ7_lquyY![L33Y3S~'XoviQOhunٲsWk`p\{9gWs J#[̞zꩂWbn"OsgND?ef 2._d &ӧO̡QFt`hg}z_=&G3O)FivRʜ!o5az _1#~2r޼yԍG9/1BN3#< ڔ`$1?6V`wxћ$ >/A [Bhm64a^PA zlkE䨶ۋ9#/JBiaN2axny]K ǻlɓ?¤!|/mOI)ȳhXRB.b9%9AP`L4Tۆuc,kXɍ w s'.Fr s2հLC, fqS)ɖk1'02%s">4OCX3' H{R2PL[Dr;8K|#&9vpk63U <{DÄěG$.y 3Dnm9ٌ RayٶѦI 6n< ! {)OK愍i}ntfRs)t7D[d^1WeYi4yLbKp|m"BmʜWk|˟f!mHzn,F/0*r>kjY@ALg:4'sb sȍ\#e$l'j![~|o~2 lL5H)e =ߛ4RgM%ϓ<(:JY{0H:N,ь'IB%V =l,g۲7įUn X69o#mYVm{1֪My|rYDo{ӹ۰aC_FF3a e59o$ƀWyai(&%<*ʜ&YϚ5+ f?[laQj^  4tH)c/ <<^xG})+aN8bbF6O8MXLCG$qBY@ʁtB&*s.^ꅍ!‚$VRqf;]Fq#AᔕLثNfN,?l7i|Gl[)^S Yʜl 9M'|^uN869gIMq感|hY3d[ hxqNJR Ozi9$G E@ Z'(sʤg6|I·)E9$s T-;:"hxx3'݄,0H Wj6@x6m8vRf?#PǵGSΉ`+i֭E A7qvS? qK]A4rY6Vl3Ub 2|J~T/fNԞvbb|N Fy_ͩs<1B8  Bo!ǵGS%m[j jnBș*(p{pGoəaGP%᠁xgZX߁*V8+ ;:(5Zb!. {}3P wG& o bF_=fKn"a"8=ʜ2rW80BMsOtñ)=gGq916XYŋ9+¥ bJ 򞌖(#;lK e+".+c2x''Č9ђ{ [|bƙf $yUTد֚:,r (TUM y2lqQf!9Aܤ,4BLCzks>O;\RZ)*|+Y[I_ ŤT۔s=D!B<7%!ads 26r[&-fJ|6&[ ace묰8=ʜ);ވHƿP(.KTaU+>D8z#œdq:KQ1`968[38eN9믿.O|0[\ JPǦ<;H1'3b'Ȝ4{g)><4ldbd{g1/{@ sĘ9I ы€Rم$1'5_WBm;s Mv}w֘@eN.5Iv8C\>LQz%9Ddaې_.PW^y%'9FB"6؞y(KҠ@ Izbڵ1QohMAf=r(<PJ2= O#%@*.˚@_ԄUPB%'8XmaaqQ9 }M?~BX y\6G (:')B4L̯ʕ+RLވ`BhM3E% 'of/H &,j3sh2MyTqk\11J OS(/(;"D wYhʓ9ވ3'3yQ % r*]rdt t7X4L0NoAȜLעDn1V1L'lRBݠ/&7ZB#3AwrL!5f%88kڣ=;0I09j`KCgG7.:X_"hCZSpJQc9L=1܈}i̜l0h)C a]/l CPd!++&Gmڣ̩\ RƐ+1c0[hᡬ4?8zȜBgF; R:^zw$ L^ < Ye>B%@iiH-"i2}IhEKs!Ȑ3b!Uj}}LdpeΒbAsh*c0P 6;k2'g $:lyD+gm;yPPqqgNrGNgcIiy+baʛ?UekM@shMSb$g˜rak !"Jf\Xf֙ M7_O3ZV)gʕI%\R5́t.]eN < mNh?I.* Z~RRMs*Y,e\$yK%*+n6bcOϺ%b_*/ob@@:fSU7/unĝ2q|#Ũȅ:u]͗{S#SQԪD ZV)BcQ== '%m4ǵGgv"N<I`;aT']4ގk2b<.6# J4O¢:𮘚Yћ*$/BZ/"5 `9_k% $!)\SZ%iF 7,kڣ̩x!>O\¢'aEŗGoژPS9nO(nީ$QTP~zeNKdݙ(B뇀EE s\{9EJ&ԓq|,.Y9ޔ0' [(~*[m[&eNA)pXPGRJ^o| @ mUqQY߉=;ҫ GoR6 I,v5 כxѧ2E<(َ-O(x|x6s\{9O [98$Z|,.Y90'=>KYŲq/CܤDP#ݤ4qA@S=sU]"TYw"&Ц0u}Q;V Idv2Sڣ̩xad&[gJf]-ћHo60̣EJKeNh>)~ 9e-17X(_8O$p\{9CGV^M dF\e{?/HbJLճ7F gCYņ{')^,9LBs+N JznQ29ek2'\DTiܸq$ȓ($6(\b8zϜ_(JLj.VD0w;3'FEeN鈒Um-MDB(Y.X$˓UqQr%#>iСMQGix'G$sw%[ ˿&EZ Mk9ź|ɵmKyK6\BiꣂqQr&{/ŋyV) ņ;79-9sbv'.MM~ -#$MW9ŵB7BJi/$bF mrdǵGSAg9:xl m$NTS$Goi;{o< $Xdq'h7tuǣ2=Gnȁ]8H!b'k*QFKfceN996csI)!p~K(Ig8zC`N"U+z9;搊*0Ad/m|lf%(s |$f'^ gBIHIV>PeFxs61zeN.i86M2eF$IВ 9!4D$l+}tW*F(&۳ގ(sR{i`N6 x[=Q?#ӯ^ (tstBeNnP5>@ 78A[[ 2^BOBXCQ7iʜ1]eNwYژS.@R%*z{Hׂ2"ٳQ5թS| $H)C\WA qz9_<)Eܕb}GkNm}Gʜ|3"Y)s #9 SRun{ ˠfNV :‚ڣ:< o߾,T; ÖȖ z6K.xG{dx {2%[ "KTT$|\S›^^ 1S-ID;ṗ {Cxu\{9Zիw駣s6 3~;\CqgNƳ/(:eN%̀V~[V %Ł]{@p4:=ʜ2Lwo|ql09 jеI\pY68iN`),r$|ʜĠKT49 1\JZ1|mJ)ؒMY塪k2'`M1Mr c=.&z:K/}Ȅ1 z0J12f@E+s f?pD.:G+&xUbqQd]QenT 4e3G/SHHhwy睏:(wG~t+yXS3eN>Y,>R=:'#eЦReN@t( ' |"S2zaVW3, @νxɅ_@,GS"5ur';\6Eè 3U"e&6An꟰4"D?f8F/W r50 C?fKF@k4aTy0<H Jb+srdQy1 sLbݦ;092'=.>BRxgmW7V[| VEK|V"xT@(s ؊lvn[KMPd&v\{|aNf9d3Cĉ6?i/cEA񉬉َp8wK ʒ+ynMsb. Tnݺ ̅g'T<.̉7ES#i$Է !U=(=ǵ_gQɱ o63 fj=5J_v9T1'yEh _¿E1HS}4= =}PSc@&Qh|<=$v\{BcNbDCFKD"G.٬ְ%~G67'уr{sx:T1ћ6Zgb` y|Y~>^Z9L}p2)UW/32s\8*#vH1bBRhh/eށ^~57̉9w}}9` oW~pȑ#2ʜB8K?\Z9.v8=gN@͏>h;%\cMGV*q8z9y83b'Κ< ݵs6)} u*s s3^|IBJXT5dad8np\{bǜ 3Ux`Ah#9I(9lΑp1wʜbj?ߏ&A޽}6מx1'X/g,~"9DE5 [ &C99f捾`2q*sr#OpZ:w:;}DI^~V? ^ BN)(39[泧 vs{?g/jE&_9%e&'AqsB?+_V)b@<]Y~rrpZ G5z^!?8z9pWiǖ=qGyj*jr#4Mh(sje.n휩C 7^;vCiKq srp_ }ǿnأW{)E@ֹjg̝6_vh;?X꼠Jd&t\{ϜdwXIVz mJJΉ|A]wuFŗ3 &PWS|[镗džp\iݺu?N?PxuvnN;TOT`],%;=gNX[Ё4b3Q5qI~ :G׹Il[tʜl‚m=SY8g|%~? eN`߇*UAvnɭןe޽-Q@:89=QfN,O}p|B=XzL RJs-WM6E_}Օ#Z4wb҅6nXl]>\&Mʜmc?stW}ӧJa*/R)Lǵ'IBY]kٖ@4"~Q(D)':g_U`9眃WM^`uW\ɇˬp#+(DlL8zSΜ:w 7jjC% z/~5+LKwi2`Ey!+s*i:WMa,^>fN׎&MڔԭkOԘ*2M/a,[=g !X)P 3reNf'XvU+͚<_wM~xYSKu(s*lv_|[Z|ų:g7œ́t1=Yp\{"Ŝ` \U&ZBAhT)q֤P+'m8zSȜ~:WW^7gE3¼Q2oڴpݿӦvֵ9!Q lZZ͙9va+ݴqySt0F [sJiqs`P:!t$$ q!]r=B3iu1z"u~~5q9}Ǹ>}3Ǜj7mH?Gڽ\DʜQs?ɣBs/bׯX:wd ۦŅ9=3e{ ؜qsBj褌J Z VJ~@kDo-:C~fk8zSŜ6nKyZ` W,<ӐuDK[dʜ!/[n ͛džk1u1aN@fZ:=%gNb!ʥA(rh\H38[ʜXYWVa,2cqڽo 0'NS袋kH*n&S*vwoߞyyh<_ #O>ier;>iZ*%M 4W}bT2 S7 Eb]fo˰p)s"ĥ4@9Dƭ["4 Z%d! (ZVZX@qdN|ࣗ֡s4uLH%sݐ!7 e3q'@.=o9Ӓwu1RFl9}bD$R@70ٖ-$*lȜ8Yĕ\6ǵs6Y=¡EEx6V [2^V|V訊wћNtWm?]?6i>3& >!ڴiÆ k37+8it)aNxns9 *>`XKAdʜs,[ qsW%Tx/px?@ A^#1kkJH/z&71gz@c'c9̨֠c%3JP:%80?&_[8ޮUdInNNF纓!ޤI:H8QGժU{ 2Dq]719dNwvileNLSՂ8LEXVk7DYi*bl=s曅6d75sRP,Ɯܯ|RH)]WWRs k7[16bƮ'Y1zeg_Ss[ٿ [U;X9jn9Yr[׻wo91B(3r_[Iq-75i9dNl8÷j˭cLtNœlHft7lh-+g/EXO6L-s =Wo& Pg8Qtս޻6[_se[4B/]8Ϛ2$ϝ8]pD#ɓgj֬i.b4ʜ>}Nۿ6hd1uX2O5Žϛ1jY!=2)mNV%#IjsN 3Ӂ;˧i2}$V)AXE0!sœ0ͧa[u3Nأ5_y8nܸnʕ+"mu\ٳ[[V 4kɃ1͸4]pU5vX|k3;qC|@&ynVz^|پ[aNVS7Z\v溳Üg}Ա@Gj$A6/[@iϞ[GOx% 3z:" *" Wx72Q9aIs+Z#9cl9q57}i[n0߮cO9}`ĆM,nr?kgyd{1`)<<=IڻD%mc͚k.d{i'h`BT;: 90DHLq +(#yʦdv\{|aNcsEb0¡GZ'IYΩLJ.q]/??m}±W!>eк}'Ta{nqތ_ FwN_A]wuBtuWFo?ɖ7aN,K'@-R.cVrUm^1qDGBtZp9.۶Rsvg5yp;`NE&J9YgqI8be8 %CkO ςMonk=[[G{?'.¬ZaӉB(˪smc5r1y睞yCڌ3f5vœ0n$n|ps}^}Uv?2.{JXMO˘9!;~VkH0Ls/Z'ArAv?dܛMcFo6::'X|&RGrf8 #hhoABq)9ɦ* 4E}*@ZERj9ɻhjy2blJ0fPkѢsڴꉇלּ{ۻ_zQF̞`+Ӵ|a7Gsh_ZPOt_ݫu-_]=黧;4|.QɊʕ+ 9묳y\D~"ړŋz M>#xl%^֨Qح`ea+˘9#.'O6lL}gǟ'V[nchsk`Nh_@dGIw%_+8=s 3m s*3+n 改 ^B*~*Ϭmf<'\5֟C~Qyw9{쑇vЮgeXoTP8}nn(3'z㎓w} e\'tF| 9z ab͞/ $8D8:D@u<`+.گC|ؠǥH1'rJ06,~Imǵs^=J/҃󩅎Hd-zL@;aVu^*io+7+ssx=;4m?vq?14{({҈)Ցk6?뫯s= "o9pDOHD.z駟4iRY͚53J2f9܂y3ڶ/cv^v_~^nSiz9<F7i XEDQ fY Ȃʜ`KADIHaKPq˜2:OY)aTxR*9K^aNc[ ׬Y5u^nӺ+/=qک'f9͚2xN.1'# AX"2?/Oc yY΂eNll q.k&O1Pfr+s?%<8un`~Sy_~ K}! %|#} 8>cǎ˚E;hP!oR)3D%`936kOs֩xzAg~238cszn\(o"Uʆ8-yqqϜIq0}L>sjೞw|{AoGbß0YSΙ6U+M8q=o`NDr0m!wuhi0(]:t8c ( Ϝ,*[,ٳXV$֡|;Mo{jO}JMɣ:EaT$ q& S^CZl ǵ'+s4ú$D.'\.--9s),H^hy~82DV%$Y @.X:mW i~\]Z?' >'+ İU~}HQOZb OXdm3J=ot2'b u ^AeWkO&s'aOaÊ Vd6i!qƔ98(O2|Ay;o<}qG~}sb`آ; ̩O>SORo/+"ПvDz+9by˻u֪UW^y%9 ew20Cw@ɚ-W˘UCˊ9YM|9یZ$(m44qdNA|X?[Mm <:F=_ӑC蜄9nhW .^Bn8N+V_~ݺu~)9 mPĆ'1O?5Ƕs9Mݓ*7 \fSw ʜ9rDl0@ ~xdt\{2RJ?p,^dlGt/O>dj V.΄P*q{BR]}奙מ8 m{ 77ip owDӠA,3TY  R g%ڵkQ5!cSŜP0e 2IOaN[ݷӗg^zՎ;Oagz?M |g.~R~Ӧ ͞ p}W2sDH&)sq 'Ü<>g¥Is ʜz-q?33w3{gԩ E{ő!Ŝ0us>\L݄'zo s@ë3x<f]{hdq2;I]a9xN!l5|H̿D?Bsp>ɲ7qk&+s4et74qx{ݡ~toowR aY4p!@9`mڴK/e2`\0'ɟ΄yR5l0|~2 ^pR=#:/nk/\@6z*jD?]=ǹS ~ ^;+n_zoV:&,{tJNRydxvC&JQ>sw`.>p9z3̉^@aU| E_%ɤolϚ?y^kJY 6d\F/j9 sKW?axۡXoۤGkKC`N)ldxo {뱗XH# !^9҃ Ė9T_7gB 5~x eeNF\mяx/9ƜCܱ`YTCb?t%~A0'&w_WyJ߈m'4Wì߯v?WoU>~]>Vj_vyY횿7G^f5+ID(p1Dt7|=|bטdOɊNJ|IcrpI>,?ybM]0'77̉sWc<3ny4ΕaN"W_}hwv{.s"05DO 2/({8+5&++sbͮ*K"̜$#~~~A3nv+x`NPu68lͧƹ3uHy'jxMB> A_ǨXb4|O&P.cK>į|Iq( C)!O\&?̩}L<#:+Ӑwzr9GOCgv=}{mBfN"Y>`,(8̦sW\׈W^P"s]پ3jBS[^V%TDbz"*f(rh΄gF@xR慟."d"/\穀[ԣ=h s2 xEoq>h<xY2'G;7 O3˞S .Urɜچ7M|fܕR .y98@4WVzF/c8 2'ۗ'I\O86> ސi#@:a9Cŧ~hv->?{uX7c= s:o4>ti}ֿNq_}̼Wr|XW{=[ɜKC(f+? D\W:u\@z 6s4.M<\O?-9Y!ђeNGc!.xY D-iǎ.A#eIo s &{͐[}?;_j=dFAw6O?'Cʼ xvg KO)U}l% i̵Erс`tğ$JV_ƺlOt0'1idO1O5/r9}C{5mأ鸡m63ZoaN5~i|G?hHo^C?w׃nXk?#m̩Cn8#thqgnѿ}]-Gn;tc>dM?xitI^;1us 8]|l$ӵ=Ax?cNv)MlAſԭmPC6uy/˻_>FdeB!_QޣަֹnRsӹ>(Eo\[AE>Zࣗ@l/Zmn}w\K>tl _rW0Mz>k]1r)gё=X2nzakBb/]s!x{k#,T\o-(^p&.LL93'K_zʜ<̍?}gu_}WA 5uۘS30a0u]f݆KVYLSl/_*5m49{C 9guz5-opn|ߴ7߷noT/oPo^\#0X#m۹o޶5k޼ 65꙲R˜'qӵ/x[aK*вr;jȟ֯ٻ1'YN:ó7S&4#p3ώErX R0sXe>^nn3Zd=Φ \ӱGzߝ׉mo;asbnh s*Tu*MO7=sGnz(-U؊c4 KS[ѡr[:#˟;z]-ێ8yǽ4Ksf7{} g[uy]}0bN;-~t6zͦk*ktV_͂T/}DقC9Zx,tqrx͟V^c^ѹK*[7cǒ{,U GlT 9W}돟\p_^/߫l/ʗ֯-_އxz"uNFZmӟ@|`NCs=ECT;C_4 =}_{?.>sFfSwzzÜ1Snu;^uV̲e'?lQe.Y%w-t,deۭ;l]}fْU6GL,PNχ#=R,s"UMRJ3͛xY4q`xYxRgVwZ7X9s?g͟YMʜ< ͸<sTiCgQ-|/=lIe +wtF_-}!%YYPolXbsLZuFJ۹sU6{-y ٕ6$b:=|O^9w/w *S9 e^)aNn3.eZ\"~U$[]Ǝ:p@Bc?>r"8LOZ7œ=f>6{U^Է|o_vX^lV 9U,zˎ)[~jғX脲ЦcX\gYN;l>ejm קJ݊*:w](6Iz@ö0zp*N,b_ҧca=ͼnV[8`*U( `NC}ѵ OK&|vv=+Ii\XQuy溈(.MgoԍnUt?Py.ޱ=-^pg?(Ŝ~ Oݲc+,8muz釔\6Jj 0QGnząǗ?vsb~ܹ,SlSDϦ91`w|\w?{i4k.MicN(p /3'i .Zmjle(׬)2"O9s3ʧ;?\7iPO;6~m}䋮rم2y S=eN.c X:Xαw^3Ĺ"9Ά(J|bӯ M3w/8ET(l}ѥ[S(χdǡ'VXlAe c_ybE.xOCwwEN\6蠝&bUʜ]>~~P7`;4z]gfҿky\p:+p=kա{ŞCՔ9wpWzYc!/(|IsJtmt]X{ݹs[(YŜƌrzĥ'lSVgͦ|N.=e~[$ʜl0J?pmͿΟ9uc'_zn8?NLm5z7=w=_MFk֯Yqrǭ^zϋ{zBC5mR?'?+e]zNnݺ4q6!*~V}8jU!ݿg5ury'9e$aU!ŜJsGDW#b)`ٺꑇ76_iX)8>O>k߹~[o!%>˟[nݟvsx}QfYE\!.9:Te]8ٖ4hI>[۷baxXmag#Pd:]:PtYG ZJyB@S~^lP08vDHZ6xN[|yG; ye y3!Rp&LLœнE/`GJv6RfN 3Ƕ0plz0H% QնnNSw;4|<d?97ܷ^?Z/{Lv9z݂ζa>V^ eNZ*~#)<=V i$9AX̉dovaN,Xu0aȋoav/spL᎕yO[)׬0{*LM8<Թű tVߋ!9Si9q s*ql̈́[i0%Ғ1'U' {Ν;z G 5Zxuuy{|8[o{L͎=TU^Sy(sJ4sXsBgcNdz衬(gⳘn9*i ;?9Eׁpsټj\dʜ^5!)jq9QCc_(aNbSk=wh1G@Sy(s|0I|Í8=>=w/U ZHrPbEeN][f[-so&9թSD6Y"c "2tV!(s:UeNqNsYwY9N\s $xNn;)!+^1;+ğ B/E (s{"@ˇ2uyĚ V~Q#H3ߙFKN=};Kccoee~qi4O*A")BVob͜XzVbBc\E*74<'qّ.\Y 2Boz-6- 7bq// }Rȋ2'k/Bĝ9YCW&9$œ䵟˦hIc3ÙH`rքXiԇLnDsd6D梭RsGD֢J˒q&geg<)*Ye2:m L\GʼSc_$,+eN~/֚_HslI$g.;NV*@q!SN&KY'bj yngUtIB(l/UvI 0ͨW$WPKs\+KȬȥ3!Y52GmFʜ^5!k@h9%٤i¬2.-sud$ Umk3'۞S2FOz ?ٔ[Vtܰd3:leVu^7ʜlM0GDaNSS#$d$4hPEi'f%6l:[I|)BY_|}$lɲN!U>Lqʜ^5!)SI(ȼ<̉e"kԨp)@dNIP>P4$XA*Ocbc(Kh+1D ?m lI#J%8n"2ZPUޔ2'%YK>S)˶hH@Lߋ!9)2= \"k7xU#)BVo92'}$ WȪ\7#&9Xk~Btgu嗇Xo=#')'*)dh")BVoduqz0KԲ|G PEYk9Xk~FjժSLI5o|J:QS Vhb4P9-)Yt#xUII'qoʕO:#rpG{G1[mUGTR@JdnJ:l]C7n\RЋ@:u?(d̘1:E LR ieG1foIBO{+"`C %n=%)8SN&))Yt)0']=ym@R!ETSnJ:QS8IK eNA)QB %n .)DeN1P9 f ,7rS҉ʜNZo(s JH)YtuII'*sh")Px5sETd-ᔛNTpR|C@oPjF@Hɢ%KJ:QCGs eN«+B %n ܔt2pʜ|R3R@J(A]R҉ʜ:c(s ^\()YtKo8妤93P"%RF r뒒NTE@Sj@Hɢ[*x)7%)ᤥ2'ߠԌ(!E7J_t2'(ʜ7gϞ=f̘ל@JݸwS9%{'uʜbשW~7wqDz &hC@ %nHt21- ʜVgZh03 NRZrHɢESJ:QCFs eNC!C=Ӆ0uQ-[4hХ^* a!U>Y$,IHI'*sJ0N`9ESa׿7ޘfP)jOpjE70KUJJ:4̩q^{mVˍ5ʜ"}w_!Fѣgd0A$Y5/^viF %n06tbiS nvS9EL~wI'{VkرUVm۶WY )Yt#u`HI'*s li )\}0}GA뮻֬Ysn ~'W'#>dэOxiJ:QϔeN%M6t7'^**&zJ4)Yt_YNTa)sxtꩧ+W_}5K.裏>lذ')ʧJ*իW/@J]f>)DeN~Z(s@Q?Snݾ}GÇG^{=cnݺ|Ÿ?p:=@J⁊r)DeNQZ,(s sZ:uԺukaNzi5j8{^| >Ÿ.2=%]_L SQMI'*sPʜ? 8ĕ;YfnVSN9g;.]u͙3'/-jE8qy*%).R9e(im5\ӣG@0 wxAq;a„/\yxqA,cʔt28TיBoLs03"" ~ : ( #@@A@ְ̈́%J –`=at:u>up[ozr[׿;q,7rv w\0 чկ~_|ﭷފOqo3Jv!S17ly$f)Or!(OlW:B٬@߾}; 5k׿ ޡ[7o([VL`(n߳r<@?__:S~~\~$'$e( njĠB9azաC{Ó6lشi-[+{]z='*JP Gg^HNAO (csbGFFI0;=8 {饗ˎWPDV$UHNꤴWJrrH;g'wXRl9>sLpx~KXXɓ'KްV'G]:W ECg)*$'Sg $^Y1vڂ>9995Bl)))|>]`EoۥK\2vX㎺@7UFMZIS[݊q>;ژ0 Mq741*W^Mwޝ؁Q9 \ oIEŮu~lVd~ӘpcXnk`ݦ4@a?~<ۂɋL0ꇥ5WZErSꀋ)P$UCNX ةN͞ ^%y۾ 4>}\XyEQzI^HNwZ'̂Bΰ# '̅hTsijHpERH>2*A"Oǚ݂<wچ7Lڶp5jM,PWUT9 MsUkER䬴cm)>'yZY{u]xklr1L\Hs(O@u"+*$'uRZ+%99?4rUv/|{ƍyf^t)V]MpFu[CE595W$UHNI9D$'Z@N"d\\\j$aq33PV1|)Hs$ ɩ>y%'QmLcm&BquHsh.>"BrrqK9vX 8r\SCEC Q$UHNA1VHN|S@!y (Z!I򙮌$'[MrrG4?jHB+-Y 9]GRyi͉P$UHNLU 99i3=R9tU+ERZi)p:dΣHshNf9',8OHNO3="99JTasUI%9Y*ei-K@ IWIN7Li)~hJT!9VZ+@r \;$9٥()H:ufGTdځnR9- I ky@rrG4?jHB+-Y 9]GRyi͉98Zg<B"HNδL_X+ER}N=VIs"AINiY0ECR8"BrR8t '99#ECgZR$UHNm ή#INv)m<4Daio (*$'* 4ə):SЪ"Br dmWvvIrKisQ94' Ku6 AӀLHNŁR$Uc(@r"AINiY0ECR8"BrR8t '99#ECgZR$UHNm ή#INv)m<4Daio (*$'* 4ə):SЪ"Br dmWvvIrKisQ94' Ku6 AӀLHNŁR$Uc(@r"AINiY0ECR8"rjٲeLLK$ n$'T\&5]}ER%8GWwzq$'"͡3Z)*$JK6pHNkgב$'6wECs7I_HNwLi)~hJT!9VZ+@r \;$9٥(dsfy:(@rr~kHɁ*IQ$EVK,Ӳ`4p ERp+v$'NrrG4?jHB+-Y 9]GRyi͉P$UHNLU 99i3=R9tU+ERZi)p:dΣHshNf9Gi_gzDrr/">'$EY- JrTN˂)ZI9إo8ə):SЪ"Br dmWvvIrKisQ94' K{S@T!91UQ|INHЙVISh%k$H]J;"͡9QX}N6m$'ə8VJpׁirHNwLL_X+ERc(@r"AINiY0ECR8"BrR8t '99#ECgZR$UHNm ή#INv)m<4Daio (*$'* 4ə):SЪ"Br dmWvvIrKisQ94' Kfy:(@rr~kHɁ*IQ$EVK,Ӳ`4p ERp+v$'NrrG4?jHB+-Y 9]GRyi͉P$UHNLU 99i3=R9tU+ERZi)p:dΣHshNf9͂tP4 99#b;JTasUI%9Y*ei-K@ IWFi8*UIDATIN7Li)~hJT!9VZ+@r \;$9٥()H(@rr$'gzHsLCVJpȩRJ%%%m+@rr$'gzHsLCVJp馛sJAZHN)#IHsXqAT _=#ɬbW9GLwET!9;yuQl 99#ECgZR$UHNm ή#INv)m<4Daio (*$'* )##C+ԓn"ٿ57nZ^[ 9U@<+SRRZڵ_~BBgb@)**SNlӲDs e - $:Ir(.Cȩu-[|޳ٻ9n&MhтOӠAj֬彎ɫQC^O>laÆzvgb/@$Dqڛx饗nS&]v n/r ߘXoPޮ;qqqzknK]%gۺz/ Vx^)@rI b}of˷ZVu7e?ifؿv ű!{ڵkWx4sҰAݘ> ڑ ֠>WoI,Q{S7%}˚^k:u8qw| DwHNe /`0PܺvG 9>@Z89I w<3r=Hg;-,z^3rJKZY/,nytSq$w$#DqzqZ֭) ǎ1#ڵm}9ɠ)t/Gg]9-?O/a;m=;Uo^/Kc` 8qBwB%IS$'*ZLAkzŢ]}LDxZy]ۓ.~ 7Uiu/~~m=o԰^Mylqtڵ1}ݝ5SO|5c"!Ie$'*Z.3{ǏG7gN\r}ŕʋګeШs0QS%gOfG`ne~r<)#ePz̔=$;~$Xe>:bXqa}ok\x`뺩؁TBjڴI%oܨ>$իy}[z5Y4Mݜ[jUL[/`sIJX0?fM $'$Q\B_.Sfs}ܘooSn'\T_KS$s }Xi1 f< F[ܙW!O>a5sLytc\.+WmVm-Yr,{Hv1e>7 d*UqP@L㣽9[O89❷*yȥ KKNٸyyĆpSل@, &Q -Ӆ?=w-7.>f0ɔG7ƵkՌ&(5EOK _HNqGa,ѩqeEg\+&νګ;)?v;oUV?l^1z$S N;>xcG7%op^9wg;zY"2fSpjUwdn+W.?{|ָUc$9MIN$9RVѴi+W.)5O7o|9͙6쩟|^Zt!'.__ ܙ},S@ ,SINU# ę{.-9[>q{[ptG~njN씅$' 4~x%;*ia;`ؕů/a7|tŒcyYkv2TA_t8I$'YʪW#l,=m(?a\Ag~ v\oj#)s㬝)pտuM4^b @4jڛdGmѽEL+w9dot;Uxx _$M,eՈ'{2W;KT,c;v(:BsDr29ز`zpOHۣkk>ǣ .b@`N"B4rڙ_K>`}byT^=77waVӁ]HNP$'Ynd)F\~r[WH8z`[S3g0yFt@})$'y")na?E w۪;nљt2{<=*'{ $' 4wwGyN?|ݗ+/z7u*tƸ( I$'YʪWSܼ[n9W>RANw'S'_Aex=s͟Frd W1|yI ОyX-7k}\}ן$7%9r$KY5j}NS 9 xt,?zB `}="]xAr)h].moft$ +f9S|^ԙcGvF ;$ AN)k|qntݗmwgB<}ᝰ}UҒV$⦰$'Ynd)F\<'ֽv1]\md>j7oy]hds[8ڏEEEMaı9aj0P ]=/8Z' +kWdda }ভc?ǯ^ܧw͚~ʿnBaB kL9rټݺ4s ~ڴnYb,IP/$' qG(,INU#'9a%LLz:Xh̴OwyڸUI:@Nb c  ܿ/;a}jԈ0,섻}Ye8|JZ2Xrޜm+}ƃXR 3G5qHNLJJ_vXC3ѷOWhܨjS^:4>v)㕜JGρ"y᧋qoHNV䕜JX S7r:qr}!9Yek␜dY_`Y ܗt.^`o|$'S9iiINF<"Ӝ%;l4gii'9.bac'fNի3 qY@*nAdxr}!9,C*~9$k=IjANGr m[w{v¬ SDm^1zbSajkUEN*:9${S9y{}/;߾q ]56i%CѮkTڌB dErq=iٿ~o?:e{XznrwINۧcXd 4 rAS $'45-6r ɩ⾸)I$'YʪדZlѾg<2}r&mw^Nr0}< }'²@{ ‚O^S;G*葁֮qw{:z/L5> IN$9RVI|4Ã9;)2hq9ZgUyͯ6.T\ (yN@NbK>[ _ܩ%TWu,I$'YʪדWn2]+eѶę$'2ȓ,^Sx?=ywDSE<=u|? ?3 9UWKre#Ij:C|ε^wP~s qK qqhtHN1 q`GT{\q,I$'Yʪדeŗvln;ɒ$$ **؍FOwD1JGT/X=}!9[$9r$KY5j$:";u1/\ \yX. kePQ~Q9 WNK_S'1.:$L,eՈSn~折߲vӶYk&{S翷zNKSIwd%/)ˠcн;V{ws%r=bZ?ЫGG_SjMGT'n+@r(Ijiʸ&=R^;#^ztft4vm%YnB7lM% iSA 8ҫAAN5Ŀ?}1UC5 rڸjԨA :OZ3ѫ/X6˿)~?|9rR+{$'Y֓d)F\٩N.ڀi1aٌuSABf%3۵mUN85t*> z *<fݹV>."rxsXY{D_ ;1e]PZxxg` VZ={,..v2 deIji|s ÀZ /uٰr?I5Ox?e5*>Cv'e聟u]/=o? P;Iaxs˖-322{#j}NG_|}KW#͗c 0ˤ1|q>Z&Q9WFre=Ijiؠ_mu X:7_뷕_~#v&ǀ~_4 ki!5r9 v_;od$=z`۩#,3/֯_i cC#KXx=Ƿ _¼CY2oʷ`-Ze]A_ɩ|+Ar L7%ЯJv$ܣy9[ߥc 騡_dm_zdOre8PL|*#0٧ x姝oL  sxSA[wp@Yz7iʹ>_UW0lJU3 WMr$'YʪZ3;'r{\U~9#{~c࣒s'W][_`le,Lٳg7ou3AVOy?5_gޏC ^ƿx??/OmЪa7(<<[onslt"L_^l_:J꺙XL߾,5vWnHN|ğ +:* 5#E %[4o^=brdzs[VYp}zT7D6fgg+ yg_1E(jDZ5>}!ߟ'˖eІ~_vbUZ_TTTf/lGS|p;f7__[,qmk}qG4,INU/.%|aut3cȤQ_ᝒś Oۗom9]ƞd=V'mZ|a#R s~-I6jcq.lY| O72fؗtڕ,+%92$KY%b msnO_;Ű=Ykž|f 1 % E+vzڻC &okK-´'|⇨0O_jX?1 z>'L;h |\z4`TZ _~~ȮmIڼ2A&NMreI{]4]tMt:00[Rʕ+sQ=LC| jdEr,R*@$'Yړd)˸T P*@I$'Y2.T P)@r=IKT x diOr,R*@$'Yړd)˸T P*@I$'Y2.T P)@r=I۹sVZ.1''qԥpNm,Pwކk4-Ss"e2y ;Y;YX> 9r$KYWE sjj*Ŀ.Qh8*p5 Tllr 0TQh&^p5Ѡ Q(Ve,, d9Hr r–]Y0D{Lr' Μ9o%28Q[\Z(I1P`GnsE 9UD=_ǒd)긢@؍a< *$j=UGht %%ƛ$' rA^oYCӧCmx F2iJxI~Ts-.(#z#8i G^( M_Qe}p deMVtu ~1;=97Zc7(S!HHDۀ+\%W1;hXǠf<ۇ8>}rh.-'׾UHte B|e+~>ar!o“Ks?Ere1Iu'1vF G+Y< 9g](ihLr3&Y =9vT& *V&āzr2v'* 9I\"E'=IPLGЀLH|Ѽ"Ddߣ.FreI^F%Њ>hGxm&\k8ZOriMPXi$4tCD;?Gb a&&`?uvq=~#!f'jx~%V5I +],8/ dI''m@Nzr$+~ݰ ҷژ{QoŹĘx# Kvw1_N Ϗ`/뀝4O-WGr$'Yʺ:\4y5(IkG;gfZJmOƆ>'BL1uYIuI'@b>^}N9ᅟKmI6JINL'9Rqf%+#& 9ANf@ӆ%eh)D7ڲa_Ѩ@iQbƳ'OLhG|${ĢQsmՅ\1Ory/Bv}#$0HSU%zv )r&9U\CHNuu\jB ܪX=H;Sj+ʼ8I0z `2, %I@W#k X)q՘;TT n+@Qc @#næYV@?8EϨPV©stp. 1It2!xQԩSW^yO bR*C<W`˚&Mdddx,!~Q _ rR* $''*UX"SKg rdޡCTZCԨRjժ=wm^+Q{9r9r1-*@RPBW ==~u-ÞKM|;+R*]C޵ KFSjժ0u&/LK]LjbŊ/^ikM@ ѩR>}6a=ےcFhZ RSS,ӢT  %YE*0UV'ܻ}󗞝R_d#?TeT Q@Vv`Zu)֩;wnLE0,&.(@rAtfIY?ަu+Yov2 qDXw*]*@S sX4uC{7}=slA-L!r̉ P_)@XX*[dXʕ6Yo'0'S2kMÂS .13 ,{sgO>|`Ξ:y敓g vڐ C ~T 1&kXމG9̩݁Nٱqf0drFLPե(bܹ]3aPlf?Ü153AP0+TS ൩x|[߳Xz穓s -c΄!O], !ۈ%>SSO? Οa9"g>aXy*`!fT P`.{r9 ܼ8m 8{Ng sg{ZIJˈ5*@rPlfEC;C_+Uf ۹@#vfO?{&<$`Gߵ+'=CqTC\@jָ]seqmTCG1' ' aը ՟S*в9ŜSة}xs s )'`h5{.{xJT a Hs 1iؑ]' 暫k_mѼXr!D:,@ B<aC`Q@0P`dӦu{﹫Z2M CÇ_]uUz>'& ٖkH'a(WkAakayPƵKsomP1?K%S;K/AOKKe ׊!Х'PoP=wS~+5cT U>T|ySt/]~8Ceʔ,i]b *@aW+U ;#רRfFv]lRP_7g`hr٣[?j3Ŋ/lq5 a(aL͊R{Kٱ>eޠ+gZs}xtorТIgl۷x%%%9U3C@0 ӮpWL^.ߥ>GTw~gV;7~¿|-a2N/͢}5X,֧Owܩ`ؑS ##x~pŜ5gЎoYꚫz`ޯ>yw꘮0`BÚF: iY%5Hsuf!ڍ>QkJ< zvme}Sy6g/j5j5>1>I|a7bAO@̗K[3!,:تj֬5>Xl*qC7Gy 0`CwO_=Ze_ # pLRj2-* D%KG x| _= /0~RH$D)O"2baq)vΐ|C <:/!5(E &-'$u) e4)("`S\fd*@a(@dU  >dDL`JQGQp 'DT|#68j1O*@aȋVa>L!Z, Ix$9dLCWG:T اa>m2HÐ iCB<2:C)q;%yXI*@t(@!P*Y0D2-ZIL 2d MIkF \%u3+ P!^T ĥ~B62c\/YY^ LH3L\W>hHX3Xp)JLa(Xdm Dy\A`͛4ԋS|, Y6RFA2mH% %%qǥbT xTG bQ*V^T 8a& Ѩ0aȈZ K ՛QU0g/ L,$![AV0dnT3 lfR@P # \0ӫ uKKK`JT Xa \pOnpŽw\H<;`dիWdBH$F Uᩀ] קjժ>p-)COǡhBzer3\fWcT !^T50b\b~.nc-x]_LЧLcNk(3 a(aL͊zF5j`n9`c?HML fAa(FenfXmjڴ)?VC1b֯a4 0)l, |,?ӧp3=fz O<.avT֬Y/[ weu{!7?@8z"k|o?uРAj"_)ǏUVa El4\,%!?Xe᰷~Ga 6(X?=84.Q!a 8 4)Wh%~0aa>|7R0-{4AR?wa[>.;OX Lz`H( hW}IVc5;Hu(@rEvf|>LhR=&S0"0gH!{/5@DHRRr@2 [x~(gT(/Tk췉͊ߢXC*`!;eډ/0C 7KseSG=GGGKgĄ!e2 K<6!zR VCʾ-Ŏxmf%jSc5 ёP-( _x"Q% ZBUFtA*@L+@2-#R `<ށ ?8r|E8aL֬BvLR?#30 Px ţR XGߗ'^|.K%TAYd2:!%d Ȍ%9dzY_h+5ЉYvm[=SP2:@jj*fըQ.cpER|_ *5d T x>N#]I, ߑ.V)@JICM!ga ~^RQę`rl;1Wjժ v7TZCԨ@4* o훘Ͼےљ{?s0@xA1v1TCv4@ pK{a<Ј;AaN4^@,Y[*@U^&Nb+@IIIM4#L0l4μ<a!ƿ`nWs ,va*`!dJT  N#1{I#5=5|>0(^+b!ƿ`nW[р0aȌjCTI#5{$U)-vԩ cX@#^>0(vr*b^T DQ0˃ RV1X켇{-Lŭz cܕ9f9laxKBjl @X@ecIP"YuM0^lE0&h]Yp`BpLN:.9dx(wP/*@UX&*_K2($Gs`2e(%aT x\ Q?)(P|֌ ĥa(. P2e8UM Va f;ɨC$K/œ92BO-&,T !^T WkdR-j>b*T*T%w*[C yB,X/Uc` -žI2dexV[ P' 96Q{u?" /o1 kBYCfc<* @B8*4ʀN28dS#T*CV)t@(A18Y J:Y6vNT (@EA0i :jP8C~nUjCЫ@Fd)U[cdUc T Q0G%T"6T IK˔ Ps XT d`W3dF]S֎ PT@B¸Hcdz9VI}=cFl ث=Cԩ,i`"F /Hd4.S*;C3 LR@v&ĘrP^t!3% Y*C1 JTe[E?n0mPpɐ&KuڸUZ1*kC6 OlQ$o f͚R6raT oCր XFdy`U_ ,1G )P+@X<*Z/_ץn!̧8pqыQ ٯ1s>Q~R.)k10 īHHUěM+C2eȐW%Y`*@ ]Y+*GL|1ѹBf2˚5+^jf.ƢT ` zpǽs_QS PҵKU0̝ ]@tW\L 8ay͙#pY?L[h ٢ 6,LIQ*!DfTC `e;XnCs,O R*`!eTs `^l0leص"cEId&K,W0dL xT=ZC~7D-5ӧ(@DF&B<G61URRcn%ɑP*`Zi C ^=-ؓ [u"Lcp^;_HCzTb*KpŴpiw7p@2wKܩa+7`L^ f }U+6\NX؄P0ff%M00L1=H8d$T xP)A1`w\VN+yL,XB)@J(sW@rn}qK@*TcaşKQ+@ YP@lɱ q_LrUPP-z%dC6Njx=fwvxm\ *JP_*@Xh*(YͻO XP^9dS#~W0w fIT\f _S)@X`*CH6&A!;%d-a_bi Ɖ{ dV]L!/[e EӁD0 <i_Vʃ HIAEx}PzPO(@X*VU" `My+w%A,T0dL X&LJYB̭搙xB'BP nk#TeB=s)^!T ~Ck Pȸbe+CfXn6TiCN+@tp}eK%^\05a`CIQްL2! ajŤR0-{4 9+6/su+pݽӧO7cQDV0gݽGC^)700E.+@r̞ `CiTR8edT0Fg= sxX,gq_?ewTr33*Va)_ɫr0s0KmL0 iVV piE2~t4 *,;]3*6"/[D47Ud&C0"3U`Y\O-/~KMlW0d̀ hժxa8ːY c3#)@XT+׌ㆄϜ{[`5kʛ^X^ .13P"`YhK-`2| y(,R%`:hvg}sW#Erڐ^2E*V#b!ė$W&y`, pK[3߄Pwy B*kRwr3_+@^SHD!$fK› X~yF,3 љ$xqCUŤ~s؍%Xł29* Cn!n0 f2d}Q` XaZ= ʫG h9G&cQ-T0dL \lҤl0my5N\吙ǣa( M +5{+ӛM񨀟0aa.YG[d[E ]Yq|]w7|3d @J*Ylo,+x8d}?,u@O;wnpU(Qb%T ٙ0)ʕTc z뭘"4ZToޅf\ٲe!ixZ+R3k,Sa 4#`::`kkB+텉7l @B  {I?6~!2w'cCfݮam 0;xU^@P0Q Iӓ0%ނi'6+$2[7' Y)-G,>ӯaHV 'd{U`]ɰ k+O}!Gdf&*\:M!5 I"M^9ۧ?7Pmh5vU aS˒Xy2ЩaHP =9V\!5* ic 2:֙0# (>R􉲭"֞XnCD *0F'ouDDP@ "D{ Cdž` 0$=^e̎TSM"zFG &JVChp Ƀo_va+@r,@\ `Q  ]|a$ ɐ,@9O82a(;tLCD *q`H)eyW<1; !ŋ]q\0lf  `\̪V w!ߙTW`k0˞>&!ًT@bK!I! YĄ!"62H:;M*G+ -}#npP0QJdBCG_7xxb9<aa@4]}RJ? =|=i0LCx_nTG۷m$_>)'Za*%6:!z޲(RV-"E^T9Cdž`ր0dA/drtSxOQqI"u[kM# W_}q8Kq*@S@E9M[iKb/hݲ g7E/^`kEwnذ!:+ OI 6}qt^1;aذrViƒ3s X2;&_l߰pe |٨!"j54X@4Y aȪCfx=z-&HD<ҤzmMQfX۪b3C  ;W?ظzm͝g "+dW/JiL`kE2ՠG0`wHaƔŊᱱwZ$ y($*Vt1vDHڦ;d70LC{R!0sdp4ioZivHmXMd I܊:}Zm9n " =a,[M0YΜ9Cic넑zȑ~)U֌an-K$0=On:sKeD4k1%X`kEpz>lߜ鄑KcU(@%y؈M"mʃ{;y8QFJtf1ߊ~C`:A?6Wgq/~!::K^ޮTN::`F U;vDay,}ܯgkL!^s k RW8CD ׊0 bٛ}섑׉{/?o[| uu+@2S1yU*WڰfqNG8wS>y`Ą! 6bt aȒ:sX\ڶi0: GX67k,iDCik>e,|<|ɹ8| e:qx 376+0=bŊU< Ƅ`kE2_){뫖wF^GhƉë>0a(NFNU^ÉX,#yɓm0o޸vF0 C{*?¿`: Cdž`ր0F %O7{ÝΞB?e٣[ќ0dڎ" ec?מGtΌcΟIY}ㄡI&p ek26]4/b aP!c?wƁX8 :yUR_>W!Y0dfR +ad?tS'?w#{Xh-NJJ ^J CD ׊0d; aCݩ˰ݾXRƈrA?s 70dXv& ١4gzl8?w왌i6nhaȘ M?6!Cvl\6\!y :u s<Ϟ9u KN] ڲj*a(I Yv^ Cۼj%&%:&ڼR?<0d:& 9 r,40vn_u ӧ;^ X+"acaP{%cRA)s0b8 ڻ}%fm_?0d.& 9&uČl[?^pܹ3gN0pfb:|`۞m)H̄!cC0k@2Ц?vlZxQ-^&  C.RO<\^) G*0 ,/C;t;fz U !=C0lf CZC7p5W_լq5 I?|hf.#4r?|3MQ:a!+0fP._nz~3$0mۊ};d6œGd3,ևH[gaPW`׵++uϤѽ3$0oÚ_y)җNa7E CB` G!,ǜ:uu\~/衽ϐCӔ9)D: Cv0LCھfyC+Wx⊗^zagH!% E CB` GE3I֬^%޻v{Vv)':ˣ\Mf Cdž`ր0d+0rL2~&tԻ*MIr3tC0W CK2gh&n0~ێs' zۭ|n"g':rNK33a((Z zLsc@ӧ=}$}lݘUP~0d& 9.ydžy`ϛ:Ka1c˯_V-lۼ8iڲ$Ī!cC0k@2h74~\^)l#w`~~BΝ+Y"OtÞ] ?}~. C633:o׃WУ7|g ރ(+{Cꆥ>F,,Y`=سs}:1O@m CD ׊0dɫahYYoH6xUR}^kE? *h^tx1;anh"& 9,x0txJ*V|}P&z9m޾}$oڎuX\N{1OM& Y&ݽiܰy螻ʕJ]6oz+YTB脏-1;a8 !'u{<~ЂYcnTΡu-7@w3vR|7& Acǎ)@?R[r05  wj8{9e;_z-+>~T@10& 2 CN~)x[u+ұ+z͏{(wn[hDml='acݳ)ҲeK^Sg}G 0Rlٲ9sFSG]6!C]~Yvy=:xx[nGLnצx+wb7=0EX`cRG(ZJÇ=uFϗ,~տ' ?x֬>tџ߿{ӷܲjꦔI'[2z͢NY}MdO qi=kdÛN䁍zk}}X3kfaR _~ [7z@>?xҝK>[,~^Zt< C[Q' {7zt8|$ׯ8xgST^_:BxO%qDgDTRpdQL}{Y9eF(w*(7$ Yk04hР9r|Mz*T(tΐ2qB9C(0(J"*xu__ЕgRRM7n;w6mE 0[S3N>sԮW.U;4Nо-xSN!9Lf:& 9 r,{ٞwJv=zk'zƍ˓ 3dcٳC}QPoґ`Hq(?E!Ň`.IDxOB[,lJRz`zDoךK nO7X\vIC0sΐv4&aȴtE CjH97[4zx?=# V/& eNJMń!6` )7"d2*xߣ|#!Ÿ> !琄y`.!=CڸRw٦ou衽vv>܁ڐl LIXFWWOU UkÞ@іb:5қ_)_/_z8 *\Pgv>;ߞR&]8)gF8D3qZvq3{qZ糓2 q⅍28,ԓ0diG!<|8~]vT섗׺m3t5>xoPoρu8 ˎ!;T5f9CO-xC*Ē"~S%s^va~ s{_X/Q&e ۺbWOFyɈ=/²b0dEG 臋-C0nz.ΜOb۲ߐ?(?v|7!Y0d&COԷSʢ xr6*Bz+Z֜Zݧ|1dEسFq7@k|ܛ|Q C0kƤ۽tqGվtֺпu. B~{q~s/vdX@7",ߍX# 5(~qN8m P~x@Jٲo #UߪQ/j5d8R|:Q`(u͌'ӜCtΜ>qkuʼsȷ}0t|x닍_lQB"͊^h^ /,~BGy+wqfGэx'VuR# 5(0Nۚ|:Xh?ܭ[ko^E?pa-ZKlgI`%2ƕH;c貓gќ?D;H7^>aȐ9m.b.VB.7upx=uއɛ`,ܼ B=sY>yTgr, fٛ?KZ,eٟ@I8pD oi}0d!G!=-KCNxfɒP, }? C`PƛOuQo\ǦzNlN9}c mFO]G9)%P9 =G2dQ`f=fo},K3sҿNJg9R,ˡ'/z$\YpO"cg~ɳ-+gĴ[X͟w7:alx:㨦:1M #e;»O\0d|& ůa)(04wR !'zO,ӜX#6/)eײbzn@ᒄ!C` [V,zՉY=H,Of9T8Yz$ˎlȑHG \ضjgc1Oٳ{ % j A}ܔ CNxʄ_Cov^EV #GxPW /57gVzu1Os\<ƬU~Qe:)eWZs}+5{~vs_VoBahhF/*QVx6]_ZV;d '(IB{˲,;epMɲϜ>ʤU  iă1M1]Y6T*v) % rdFu˛'WfƅNtS& 5=ίKW:w-x@7MZӴMSìJYޗjo]?+d͛W0_+B*0ZbOϗ'w7$OyĒx)0U?NfFKJY}U,l7a}54ECc7^|ٻufWM0tiܹsۚ@$!PH;,--2)bqm-?ӈBB#>ئuV+ . C?vp' {xocv;6 i:j1j5c|6h9?J?nυ\l_K鐴ઁ}.JyPؐ!CrX=LyºeFh8'?N90w۸#{gO;ǑOl?vǭ^{aʘ^a;41ݢй< Z`єCa`s fe,^v%  qxF}?Ss74 C?XӃ,X`e 30+WmEO Z?놎O̰&4SF>r"g66eG2(04z-n 7iTCoŜNϚNxـ!M'4ig8tRCU`x ţ5q՞5Jpj'GmY4ݛ?T^+ҮE9C{Xw9MO`M&`.??8bЅm2Y S0maP'^Z_楧˾ :at=gQtߢCF}M#95!M'UZ~]wAmŒE/ĉNpΘ6oS&Pk E&{hx`m#'lQ2q'8rDȃ0d+l.7C }~0* 3k:N8Ǐh~=Cg ůa)h`hՂ3,R(I#ϱ;)]rkU\4gj9 Cٕ0 t^-C:LU.WEj_F[[Kfv҈~9s|V|e47aȐ LrW?s0aHڡCլ˿}a■'4'a1ah'%}z40v^Uõ} \!F7L8iK2j\oYϚ5@CuZ1g\4VƳHOiZo^g o9 Cٕ0 t^-C&:!Cx)czxkW_,֡x`H ܚ0dnE! ||?n|vguJ=5W_ti)aЉO$ 9fWP0"5" L?}v i聱lcʤNxCLJ,+GtʕOGú&-پGz_qO$ 9fWP0!x \Zdre_H)[7=~H{ذb"`H ؂0dG$ 9yGGz/'yf':kNPl- A FF88a覍7,O8~H @F' 9*"ꫯrY6O䁍|гg?yB2$ W P\DؑҽeNx}i?}}]/j }7Yq\gv_x`H!Yկz֬Y~w҈.K=щ139k\f5g--ꇡ'߬Zfӕy~aD0jDkj_e~֙6[h'~8e7<õlֲNnKV|rVW0_+BMaqJ<[SۯfdjOD='I ̗_~)/>qF<+[0`{M\Jon_2or*ی C@Ղ0dmGѴ\wl`OJ'nXq};1֡CAco硇~Fnc*ox~1j]5{JuZkməCzTJlG_U+oC;oմ>0? }|ƭD'/UyR?ݛko|D6=5ieP0"5" lұa.K/tWbݭCy>U7kLy۫~O]+=NJRryZ¥+:bR] RtM6Xw +V7o^;Q۞TBqCzKi)P0!x ٴcFJ[ 0l駟>˧M>wNg t{y1 V\P0"5" a,U0dL, W PFyCcL*CÈ`Ԉ0 W0_+a W ;,*_~  aЯaHV ICEU0 W0_+1C`kDbE+4iR Ϗ-۶mz| i~MNNƗ_~15R2^&S(J?!9sHس+RgaR1k e41b&lEJa"U9f"sᜡ҄+f- E'1l{~)5f&R"%S CiP:.ڏ'+atbv58NQ>,R=!+֓E0>!4+q;4*);j!UI$JkcI Is(| 6EX&z*ZA4UԂ-[HECDˆnw [|`mJݡI7n9Y*X)eU:XZ ?VDbfaR:RDa-(@e [xz gW3CBB-nZrjPa<Ҽq *(#5$%<kxY)jrGʅ= @`zV?F\ Z6A"~P㼈%z<<K>%0nVD6,H5Oza{3#=J:LUlaoRl~eKYTy>~{JhjH%Y/C0 <4a׽r. ONH重C$G{ @ BQ*ðCJ^!*M1lQC_YڧO07Xӷ*Ս_ݫ υ: 2"RAs+ϙ!ŷ.=̣f&}/Xݣͤӈ46 &-`'eSG)bfaRJ30${%l,QR/]C(d~Hh)t@N^SG\6r  8o }iBP0i.iuH&tw4\KB~ʕaTJr&` -ڧSJn!z,RWђ2$R/L7XW+FB>GEjW҄+f 4w_+t ۛKKDru$u&5^H3 \P0$)Ck'cT+LPM9C$QDҔP-܃pښc+6>= CQrI(@VѣL,Gm):A8?v >aI3= WJ8 eAFPuǡ\gnK$ D6 K)V'(>Bi9+4_y6`Ljg5~E͉0sfl!i,Lr7URP[^aHRM^کґ (S:vt9NFItn܌~;ѬYQQ(:ĬTN%ė%, IyB$, ۷rEp1 \7aMN1: iʩ) Ðx¶ >ʺkZ u޳Aa'1CHP+U9P1vJYC/.41CJLJk@&GEoTX+G0DÐܘ:}eܛf^1sۭzҁ.<=NLR MAQT!t$. Ĭ0~ ", ̕V] yIŕ ; 'qXK&V@N҅qaBhFհ4ŰFfD)U *I|ŨQ`H.J[~mQR蓻#u`H^(fxՆM3B) W%0$%B3v® ^u z!)LA ƩI1yD$}s!d4vO}Q] JQ垥# %R賿5z³@e1,SIȕF4dʊ'B?sBriw$)[Q= 5z$ey oҗOa,uǤ) ;!]ӊ4=H蟊W?I%Ay!H#P\6Xlk ;%0+_R4LiYʜeMajRCȑɝIG$JLCVwŒKPD&N݀#z0$F$PT_+E}(y1*tJ*@D+iJ,)R4:è?k:pH(wj(<=gH*)Ɔ"j(2)$T'|,Ld>DZ':+YkC)jrHAӪ S 困"]iA\tCD*RBzBib32$LOC4 9PIG$_u'4t r$McfaRz:nґeBW깕]E@GY k @lԦbPsh%`H)gG<!Cs^XޢtEO0 2"aM,!QOk2|b~ꓘ@aAZS^>94VhR ?!=8N *c| Bx aMCvUp#C=4tН5kԩSG|^zmLD ?WXQš6mɗUV9a!z,_ 3?[X`ҥ# Cִ{MܬYŞ3gaHEC3aHh!<Λ7oԨQx|>ɓ.\rc7rHC2- ͟?4~ĉ]ohǏ?w`h̙GVz]b &W0R… -Z-rʅ=P[π!cܸq CzOP<Ŏ!4PXLxP.TchfgΜAK[jlر؆0aHPZdIrr2qm۶5͚Xۏux5aHC_h)ҥK:tpwcEl_/2h\ri]w=e4P' %4qx!=; -_eUTD"<.!4"Jw/h" 鱠UaCV)>H0b <`Yl~7 W9?ۀ!FOD,<43XZ`zIU' yh!=m< Ex?-!Vgg+05''a CzlgmzjSCfϞ!Do3 !`!  CÇ/S?:k`& X3<#5\w߅z? ɗH*ʯA֋%c?ꫯFRxM͚5.cv+4ac.6N14.:, a﷛n!#0z 89s攖2D=LZZV1`[ bƸV!eS \>d JJhp CFuJJ ]~VCpȜ!5 _c6<{C"q#:uŋzax Lf>JӦM+4GRl l›Gfvh]C¸VFQ?[b! ůa)`ꪫBKÔUk4LCKC`l;a/ ,fB9L{?Q0dac2:~혦B)HBcK0dN7c/o `;'h&AkD0x{u/#իU Ba^JV _aS yCrę a(N>ԂDp` @5kB YIەrJ 3>KNMm_Df10Ufx:Uf͚]&ē,a(l )F_- ?i[>} ( *VCR>&w"xS}}&UXQ:[5W ڢEn  J;(*qc.e9썥VZnlhNJC3îx43wmI 9# a.;9\K#\61TY,aȕAct ]4BXD!M 1 a(ݴ_yT0b$klC.ZPք!Cry%0`+EIr,aΉ0 0_+$ y!b7 K\7k塐!0M`C!M!Zy($a; ;a^0 0_+$ y!oKEHNNqФq(_"6]T&PUJdM(@2!QM{JK !Q?Ÿli Co*@@݃_dI|W\qp*o l*0գaHJ6& qHJ[իE͙3RJx-tgF& T!%陕J:p P=sV:po!ϒ6%d Cq h}t +H ChZuRưҬ7I Co*@+C5&P㍭0zr>d% ů) !?&)X7e,6 ațw{*! ){!Zĩez5dLKgWs0 R )~#eZ2ڮ"2] IaȦJ`Hfp uyi a0ԢE k!эsM1,aȴtvED#QV{BWɜ!e.CjP/]ee,aȦIPF!+S6ˋ)"N tjoN d.Y9l33xJ;>C6$j҄!b7 '(rjM!Zy($w1CD0z/Ar @үBc Û κQY}d*)*a0介z QalR0dv%K" um1]*!WO C>C!],.ЧaHN e![d/QaȾ)S (>&  |vɲT@!}:1- lվD C!.L\T0̚0k0D%R} P(@EV% 컺2pQ3kϮag,K)@ҧC٢aYK0DbTEC.Ϭ C>C!],.ЧaHN e![d/QaȾ)S (>&  |vɲT@!}:1- lվD C!.L\T0̚0k0D%{Ʌ ʚ5kX~C,")@ٵA" MKKY&[ȗ/Z(@r@dfA 5@" Rhժ_/Tzvzg2eRSSRC,CC!_\#G̑#pO? mtlܸA?W^٨Qt_Ԉ[ 3( pga0K #Xl٫*a0hޯKh!f\!f%J` lWcر^=|W%O}2 3S"o uE+MժU1Rֻwo̡riFHTB~YB; ١*ԩaHP^ F" yZ\!a0KV34bxz0H`hΜ9Wf[/-Z_xV$<+ƣa(7NCq ttakx~k׮?k/ǚiӦa=S.{mۆ}#-( Qܹ1av h) C>3,a0K[ʕk;YbW_ݣGgyvڣG~70<6l]w%*U XHC  *@VOS# #/FƍcS<TG- Lz1;|kG>C/ ((\0~eIjwyO>`U:+imn&þy/W~;4rڟa)&YD0m%i.U0^&s 7> H`ai `~{X/F^%Xb;͵;C:7Cs Y,L+ܧOjժΈ($¶qJJ ^{-[lݺ3p/_}XbD@QָAU(%q3:[ aB1H0dZei鬊^ [n֭͛C^_>#PG9 rmIjeʔ{aȨ!0 0dL/Uhi鬍aU`ФI6uQW/~^z% ͞=T+yVY[N(@2z1*zx&0V1C9b*Aʕ+/YdÆ Xֺuk|}۷o?8w܄ n69q' =aȨb' nc Ӌ0dZ/#b"{'@_јֻvZKrd"1vvɑK_{]QZZZ'aw& d!C> (0f5++{-t.[0KoH'%%˗W%π`(6'1S8p [ .؈إ[LJyy-akQN9]3:y7ƫ9DŽa@`(:6 cs%fL-`6֥=,ۨQ#7-~ <}rM[~CzGˎKZ-ҪU+N# qeښ&aVyO0SSY,;W֭۹J9lߢ›eD 0duHZ!Gd.Pt-3O_r`Zٹw˗|];a⓮U0|9-T|fo#XK^XULTY,6|o?Ya}e]ʼjeML= C;ag&# E2>^wׯZppφгK˗ S/م(!#MiW ȁ;wlǽWixGإGNp1#ˡWsb5aߋHCboUx-JO'OݿsӢcgNJX,GOߺiE@0N>zLl~8ܭQX,Gf~g(yte۹`f~Wb4^' nc sXIG>5٭s*3@L?|3NUxXh8a(NڵrÊ \RhIIc$<歟G;ub϶څ0bzVNr^r$ )0ji.^~O1Ϟ?u\ɞ'ܹq敓7$' uQD0`h떌}#\Ni `&|7˳ؾmM+&,1PʼA'a: 1 +V䏉Q`C{7?C\&BrU~0 0΅GӧE?{QJe\}ükf_c(0zuKݱ \Νi#ݻŀPiI]=0[q}!]9ӥgvM`-KA[VM# EQ!}Bz?&;{؅0.y-akQД]!EsLg*zP+m[]>777k(8a 2%|H9C Y\ڵL] .i=@U )v)#VBPLbJ!Y!xpvVN3cG۶uԍ11HNog Ύ-+0du/0`jW^4cЦ5 9{䯿Zba' 3gFC[띔#6Y 4-7s U"-.A=b0S" y"!0tɢތdСj2.}Tsd֯R/6ZcbfR#)0cۺzM0aa1O-!nvӕ_v5VV(6,(i @FZ6wbMÁQoka(K C0"BaʯabU^iP'`hq='=CFDcdW]o'M6PG4Ot~7zM9~x/L.!P<`#E P1wf眩C>3ahs9 CFDc0Stw@n}Wu$ Q0ُ]*U( v\2gewSfz03N '`WM9C͸1P ad.!؈0υJ\+ϔ0 ^84yf''P63"%;riP$`{6 a0du1"aEdM C+np0aO&3s]=N( E2^z,}~ $aȴBa(] B" D]HrQ|3Yz׭q`|0rS\`hgQ t`v뷎zs662o(v9rhم0D2w}0s' )0tրG=C+ýЩgY6T34sB{h߮pBVU̝;wZZB&``34n+ kQЈ>_yTB222Pj C>3aH2\4WKCO.)0ѣլYwYD`hΤ^nU8qg.߆hߎո Mަ~wA:t0Z /04z`{@Aہ(.x]k%iPT]:3G|f5C'aU{nu[dr@ 7NCݿm2esUpMB*E ?:r@G c5֢ݱ o0d^47__IA 1; 6o\G S쒾۾e?WC41BʅDϐag&# =Ce_zf/mЛE4y T!4pk a<:![ŋ?Ϯ /W`H l?~È;5状kڳ}%ށ:CFԯ_;6W ݝ#.khJ ş ޷c~,.S1 azx&0Vz5Y˯[W j}Mח/ޭk7xgအS$ġm+//_#G3 C(xLKOyLRRpKmŒE~l϶i k0P 3w)!Q`Ј} ٧ Kl?wJv×SEC.Qc\Lք\߿ho 9c|"aФݾ콼ݟ[׮k& MWF9 k;ynjI'DT,ڊF=:6;{_ׁjT)=?m]~x¯{7/d!x`}mZK7H.,'@B|Kݳ=%]tmnSL^[J /.U.☁fwj2Fd"ߏ߼쫥q傾my<}G] w~KꯣKc϶Q2Wgyx(/WS 9,xDAx0yh+.iiZ|oYj꘮0 35՜XۦYCtX5 x/w^٧1]釕~Gٿ]l 5ۯ{VyQ5V%ihǚ=;ܳ)mkrX۲Ϸ_( O, q+-lNmVh\dY+rn-v;vm^ǖ!CFs= $60'AlN`x~y3dZ,oD$ yKA-E at&_ _.WBbU rEbvΫ. ţ+q Cn>Sy P*!Gd2j:aT CJ\+ϔ0d^;ƤT 8ā0dEr@dfAGP<03% ׎1(@rDf+3! YiYP*@Q0z% "L_:B[d 5 |޽[ScǎU):jǬ]$ ~]\,&3Y|jk(  ˊPիWW'k4댅RLRQ4~MsXc./ l!,aH?fa_F`JXcaCp_ Cz #ѣGoɡ! ȣXqCaH~R!vD(=u @QCN(քjnbbX2:G(MROt?! j!,a}<*? tE1-jQ,7i`2Idiq~ A,JtC%iʡ`C ?N\pLϐv"wY(F5 j{A&Z-wP)Ԡ28ȮPFm55F=jq<Iňbnu\awV& d!ۡB(]'yV*Ki²]H 0 +w\T"(P|<0fx,Bd_P+G4.ڢ84l*PW5 0dP0ܷ 2X!͍@O!!]a2ZgeLQ$+Ԏ=)xpp@,SJTeսbb!Y3aa(B߾`G?7Wjp?JQ]:**tk>CK'k4ӹGMMI,=,ihDO3F*%ag&KXX\*@XOC>3K@)@ C>3agcqH<C9ag&# `,.aw6' d!ťT  !03T $!ٜ03|f0 PS0;|f2 R*x |gsLFX\*@OlNC>3K@)@ C>3agcqH<C9ag&# `,.aw6' d!ťT  !03T $!ٜ03|f0 PS0;|f2 R*x |gsLFX\*@OlNC>3K@)@ C>3agcqH<C9ag&# `,.aw6' d!ťT  !03T $!ٜ03|f0 PS0;|f2 R*x |gsLFX\*@OlNCMxP*@SSS}vwI|fw}=FMZj᳻Kb0gT $(T PV0gT $(T PV0gT $(Oի 9 _>4|ӥKuȰ ~'ǎSEJMT|5cl*@EPX6)Wjy %%K|A" /a MMI dSe,T0HRT9.M~=Z~|iٲeh O&"5h̉ P*@Xp* ² ?PLҐMuA+oRP*iC6 G@$vA˖- ? ZE {vMp݀!NLBvH Y{_: P(@!X *W ZW$)%AW$_bȤ% rS*!%gT X Ą!'iʳ2a!eЄg" bm PyP+`JGTr`Ufya(WFW0̑ J0$_oAH#e1aHV?Pr2T ؠaQ$H$PL| P2IUC`/SZ')XW*@L*@2)Q* D!eUW$|]"%(!#s64T !^T ĥ@XvGF,/'j2M75baS#d(#SS0x6g hޞ!"_5b1RfDy`4%_ԼL $03+IS p`ff#ҋT1EEga_ԊD8:f2UT P*@b+@CP*@V0`jT P*@@lC5b*@T l\V P*@a(F AT X cAIENDB`./oar-2.5.2/misc/fault_tolerance/HAdocumentation/schema/DRBD.png0000755000175000017500000005037011757171206022436 0ustar plbplbPNG  IHDRzY^sRGBgAMA a cHRMz&u0`:pQ<PvIDATx^} Uuv-RZPUz#RHlb+!^@nKJQD .[i FMU$ xXZR@"wH'y;s=s=Ϭo}}=sh{>|>^?>J Hv=}ף|];wZ~O|P;c@z"'o:P_~.('&ˌp@;ض֟Hwݟ a  }x۝;]xӛޔk*OXèpx]tB=!{dU"|E()# (ڦF`S"]@⚝Kkp"E$qe[,؛$?GW=IF '@UǧT߽CsxB@}e;PDL! KSeS$SF Y.mtVPȴ{+UV'E]n'_>;?t]~5;7^pZXB/|}C!&3 ೟=O?98훈<@%KHDkj;<^\Op_=ԓ L+`3?$hϮ牕y .Te2[n3pP:͗?w팼 c @r`? rZ A6('E1Q~DH~}S]6 ^$8!; 'WK٠H$B2euM .ȡ:8!DW 1Vc@'f|TYJ^c薁 SIӒҹ[+9wF˯HՒ]/jf:Nj4낅 s(Mrf,g(0cm -w=O6n][ntL¶ 3G4 W3l-Y{q9>_27zMuÿ4n)LN\8Lya#5cTnjzte`*Xn$<9izqP3)t<O׾6BƩ?ۺt;N}(ҭpR0X׎؞|L=6-5nᗊSv^z3: &"wA:6[͙ޕV1Rvk[L&SZŝ&MYfd:g*nƌs5]ԓ&ʙ\̥[e<ѿ;M5Tt[nǯNq3 b2T=Sͦ֒T#u= M_=ukт/nsݝ9rcVrtayEjns'67ܳHrMэzI7Ý+Yqݺ --Mmֻ WW[}Gt;b=2% -K`8دnι\v+Fē>+ʪ]칕?;;w-q:[8b^uXΥۑO٭ ?tڭz%0rBH!v7ZU,nH\Z D ?MߐxݣFFfLsA9 Ơk 3lڲ OX|emFUvsvif|Onvk)8y{da6B>⭭3O&) %#h7$9nm"zbv[.Kɭ2K#s efIkh|$~up~ _Nvk?T8{ ٦ŀ6'?t?m۶C%7DN2Cۋ/X"ǢȱPRl}DUF%aHt۔]t}JHjN&C"eʤPRl}Ct+LLwtfpms*!DtjVh hvXtJ͵HhOZ'C[ݦ zEgtR7A`tydzQGq8`kCsOE,ݜ;=9.,z2=Obgza3:4tЎͶt;,uPF sܟ I!?U%hUL AО_dS HLpia5ǛCWbD3e->xђ3t!uRtJ͵on;7h3\Ӄ7-Mɘ;8jEg](zaWt Ie10k2ȀyEvigv)_% `h< וuyNt׊- ƶZn3dgz 1oeaJ=glm}6[l8/6~-j^&="ߢ?Eg6Gzv8L ų\a!\,WzBme,l0'Sӓ]5*s[z;ٲpq! n-#dJ%dLS b إ[f,le;|Ν{Ɖ&ztS EYbtw}0u6ix3f]tJ͵HG<^Q,?%CXl*n懝2=K{^Fk usevN --yuY:!@n 6_ %0ٌA 5'd5-K.BZQT'p:MeE423{ OK%ua眲tKn:_m()6׾!Pr6wă 5a>**unFقuf\$ss`^eMhm.fה0eN´ݖu?^dmP8JFKd`ݽ4x6WՍq DgEC-p0^K4hyhk/m>C%7D)Odki=qm-+ոq<悲r~}.X. ~ mZ dC^61c;F⹚7nCIvzk 2BݐzW!C^F y8]Q4%<9 nQux,o"޷*v(Bl:|m()6׾!?[0T[آms*!6[Ef$GܢPRl}D\3];vHtm1nPV-ZF`tȴ9d}D\VDu5 PDKms*!MYu!0$ܭ""lf/ %Gۆ2UPjg.6t{Ht֢0E@t}JHRr!2C[x=tA <+?۶m6M6RN-TvQyo۫..h3f"pYgqnCC(tgang>=옺dn;Vc9栃:DD__?iO?S#inE+2*(Onذ]M.X6,:PF^M}.<Ɋ+?XmEif%K9@@m(nc]7ғ̚5+05mmx{W4_.GBm(*퓾\D> mgcdՓ;j'VvniVVg6U=9nc#ZYUUOY7 En4Yzr5:k% sZ@zrgPĢ[m5鬉[IV=apbmhyfkUOnh"EhԀ LOzr0G)b Wҭ'@CDԬ)J`l(m;8g9Ct FYE2l%YĢ2n;rv}E %r*D۩Ƀ-[F =cdݺuMV Zc?9sD(Q݊n+Utob%0"ۈ`%ZLjnEj\n1vΜMtT8Vt[AM:n"x*^t[&nD7lѭ6&5)Et$qdn$|vY2 - ݶfNg<#יU'PG59l'I9sfH;C:k1[mocѢEt{vLtпo(w3eg݊ntQCz-Ql<&Xt͸֓UIf0*ݖHgɪ$w9v6.^=YFĢ[mt{Jr!!^=Y$V91JroKmgCՓUIl$N,ݖHǿdU; e'RvJl` 6yLG7JroWmCcdUs+ %]mNEɪ$GF68mT8zzE f4݊n;PSJr l6ݶ wdysTIx N/ݖHJr#ގۡnER=3c1B=yڵwBVt;Yr l'ۿm( eYk?֥RlA`%L?LE E9[="uJ_|H%[D ѭ:nE۽I:>dor/Mnׯ_fL*݊nPQzml٢bgР|ph~L*݊nƘ~VvDvKULZuJt+[!{6!ňzvѭVt6&AnE[ijMn5w[?݊nEnX֔NU9vɒ%ku/^]d9ֻeOmcǎu3ҟ8mD[msqڲg|>!@NkEvzv?9gIon+ &W\cdi<%_t+ݶzǖ[iY?t~d-mt-V8q33=\(iѭ}F{q{9Vt+mwy-sV%Ÿ?~_ZdO x~bΝZ$趝[._zf`!*-Ra2,{Pn5݊nE]dt̷x~%Ϡ̪x~3e5*Mneϛ"5幨lEad9+̀Օcm{,SѭVt-ªU̮+,g ~xttDVW'ۄ͋:ѝSL|,4 ݺlW[Vt+:5^ yVV1g f6 mky[Lf59(.8tqO62n.}*݊nE!yɗwCs6˵-EG"nNnI7wnZuݺs<2j<&݊nEf\;iNEbL_]#PEٹ[OlDݾ=!m:vsT~fT@0ܭhOsrilbs-*&Ο?ӦMᤓz4I`<`'V1ֈ1}FN5" Mnߐw(?{{b{ >wW|n/e݊nÆm;{apv ]~Cc&Y?m=Bnnۙ&ﱢ[ѭݹۦp]mtm a݊nEv`7eǔ&D]t>Vt+mֺ{/6!24KpѭVt6F+Mn5w[?^݊nE.mtVt+UvVﻭvs]_t+j6Q1Y퓷b}=Vt+Uvˉn[jb!$*J~̅dn[Ϟ* %]M)F8ܳ+Mn։IE[ѭn5w;M}lI6^ 7|wCbr(5-$|{#Mޓ$A|衇nݺX̝;wDۊrʫb5[lن w]Ht}ƍxM_ aɁye$FMt+hԳgTlـ4zr,#)2ĩJ2M)JJe[܀4zr,#݆jQ(pO$T&q~gOҀ,|UUe4vm=-Zts-k׮M\P+ )mj#k嗎Vt[$l2\jkvGRZ*U؉M_DR%d=Qv&/[mnEJ"ݦmVt[$[mnE3݊nK=Vt[$[ѭVtʧ%ZjJiJi$4w;Y?݆4e-RrNoR݊nKDdKDdULV19OULULV1YdKCnCX6#ULV1\&~!ݖ**&**&br(brnݖ*6UL.w:cl!ݖu|Vt[$*&\$|Y݊n'xRO/+%Z6Hn^/t3^nEEmM,m\uU)=\#msnC5>[PK֚ ܷn:s#I]ct F!8Ƴf{{9\m(FDw,"i$*9iI")5[Oƞq݊n'+XTTB*.(iI:)#[OV%9fDRgʒ ܳdUs&iIJrK݊nK+%gɪ$nsp$4[OV%YtJES渥w5E$5RJVt[Nh`wJLh$ n=Y nTPN0X,ݭ',-t4RJ6S'OnY*K6pg=Y $bMZ=YdѭvJ2$JĎΝ-Xw4#br(ݲlz*yۖ-[F믷kicՈt/MZ֟￿ciED=oAn馻=QkUAb2H[DɤqlĞ$% ) %zkgt2ņzD6.Q@tυU KEJGe+mإ^t[;k5%ݦYCWɯߒ3ׯЇѭB(;nkq;dJ=)UFז!O2Z7+*٭_t[;kn<ԫxt++쑴[ԧA:KG ݦW+ zDX?z9^Emt(0U4}TeF=n5.UEt[;k5%J*>ǬL=C+kpgC(ÝP6AXvOhQ PE5!Vz^QCnsv}+t=4HG`TtuzY㨈tkG/'ԫ(CF0?mҥ̙'xk>kyA)QGu 'Byg"9/Pr\4GI~򔏧=6|܏ǯ%Kd rnkgCX1E1wAŻgK.) 8.Dl[ ǵh>~ yW8w]9vC] ~r7^9"O, ]E~ݒbٳ]xᅀ'|ٱPfn/Zm=qԔt(l.XM/R'͹ŦN҉Q_~4 *dhsOreD5g…T}'*UVYNTt?1Nn8/'||-ʘkP:*UU\9@8b᠐n]9@/^zt붧 zD&!o`D/ 27t ҭ+ߕАzszI`-g3lnuh68gss?'Wu씏.e̞CPns}Y5x&!!id9;KvTWZN@oCH?F̏9 8%RQBtX;"lB\ŝ@7Yi<; m ǝ5H\}Vc]1+TnjMn3MWz۲cܱmwUko^ZPѦF bck)'뾱V~,e)]Zϴ(F@niLCa gǗeaBjQd9^Í^gD5!Smt}x Q 2hBh@gfPq6;dG#Bױ˅\cV΀<֚pd,tlc~ \k\z7wAv^vҥ1\{tk'L-PF|wrb~ZP(zݘpThv0yO6"Ttkz%ǝ5n'07!zgm/u^CWUn[vZM=,f6Yՙ)d,ۺ >JB ]eIs$Qڏy&1.V" 5\؟ ݹ[.$atbt#Eゟ@6-+~Ytۄ>ޙ՘Nb.fsEnd=*A \NKbe(0AeH\lr< 'WWԧ1GDjMژفnE:mtl [ 併$l(׊\P(-&O[:2bYWm΄3W!ε׋轣& An볧'ٹ[: lN6'WڭPwY)V\E=gU"j8bDC &szY)sUbB\:a٭?E45"PfR%nkgC"ҭz4V:6zhQBJEQ6^NWQv]%J na~eƆ@騄ҭƥJ AnUYs}F`"Nm=qԔt2ƿP6AXv 5;B5"Q8oaq;$"J+f^EsaԧA:KG ݦW+GEX?z9^Emt(0UY1RFPY)F,VI栌,ݖZUG^r; PIM3 J2qW=E|qk=Yt+l n%y4XU nLUO%dXɢ[ds+ɣId< ftVUOxqדEFUǑX%yA֫$3VG\O݊n']wDV"Pj6Rh/6"UE٫bћv) nCCmo t v0)x`) Vt[JT"PADApigTRRѭ趢In+LtW)x8H(%A݊n+"PADApigTRRѭ趢In+LtW)x8H(%A݊n+"PADApigTRRѭ趢In+LtW)x8H(%A݊n+"PADApigTRRѭ趢In+LtW)x8H(%A݊n+"PADApigTRRѭ趢In+LtW)x8H(%A݊n+"PADApigTRRѭ趢In+LtW)x8H(%A݊n+"PADApigTRRѭ趢In+LtW)x8H(%A݊n+"PADApigTRRѭ趢In+LtW)x8H(%A݊n+"PADApigTRRѭ趢In+LtW)x8H(%A݊n+"PADApigTRRѭ趢In+LtW)x8H(%A݊n+"PADApigTRRѭ趢In+LtW)x8H(%A݊n+"PADApigTRRѭ趢In+LtW)x8H(%A݊n+"PADApigTRRѭ趢In+lt mڵ?3?ٱeܹ6m /XE,ZOO{FOю+W._|]>Nz/}3ٱu{L~yix ^4>h֜6%_qOmQk^OMF@" ˿ q=[\JvO-QFIa&P-)eԠ!B@t`n@UtSRt;%:|LnMmnBV6M\55;DM TEMڎs0dM,lb3gnc9桇J/tdEKԕ>7ov{e~dkdVчS@~p?ApS4ḏnGCYj]GAZ~]䆝^ziX#D—m9K 82"N[=5)KsW;v]wݕȬb:{^hcWƝ^StKFXDr&-ּGO66g/udVӱTF`ݺ$ ]L`ŊFâ۩P9Ut.̒G^?bFf9u&-&bOn캓)(XGt@=)rdEtۜBr)ݒh˺s^x[^b8Չ@/[ k*&O7JO-d=Q!}V[znQ75S(UF[tkmOqde-ԀxTïʯ,fPne2TxF$=UCAnYҎZWbFڕNqo}̽{_> 5 Dȵ+W9 ;%mPB\-d%6~| _z~d\a:ZS{O;4?z_>{`3Hy[Fjk9@@UE'{# -Wv[{%/yɒ%K-[W޼y_j >u\TV\ym_'>񉗽eKW9=Gx\'(-{D`z"p;w|z衯zի^ڳ+\t[+tSwz+]=閟o`w̙Ć8j+ݺ{nN:=RaV'^t@2 !"N, @@=|ƯAgȏCW?[[ i…~MlT4~7C +rS,V߬Y֮]_oo@_m-֑7ኅR+PgUnַ N +^ 6)l%<ۓO>_:f7-=?a'~Ť:"Q}nE=mLW-$y-0FD4:N~mjQlo['hb8wȷ=[ѯUīygt:ꨛn `yݻ=y_l_?es9VG \ul7h|F x,F=؏m&1^It-@D4:N~gLK8' S,l뿎g3soٳכoko~|3~7~!6ʈHHW#tLĠD_a 䠥]]rż,J ?s?w 7qgv+:_btzM"=3gJ__&!;d[%EcD $'hQRwQ#ņh3D))ޡO={__M]G!b{tuvMlW[Ee(cBe8Lnm.Hp%,R??-=OOKD}6۷Y6֭!/?oWްhV>]Ũk9ODw#D mvqi+nb'W'WҭWCK*mV3 A{XA?o1n{2FݺbXM hz~Re3;'MѭWCx;߉{ `)nQI}5o۞ DX+sz .p=ԓXۡ4B^ MSHv饗B8OC(="5H-_g3/[]qn=O4- }UL| )앯|u]w]9SC==_痎G{۸xJڠHn/~JHOt8]*vNt?80Gpk31B58@ գ9m=t(HnQ{񊱼4^EC)믿ȸ`d"mH!΋$۷?<ĖQ7J0EnPF@tW=iO1SK[b%sǎ?8 ?ex n~?ƶf1?-趶ֆN3 }-ƅUQ|Eb(b=cxA4q-^y\BKf[ HkogkA!<oذnw)cB@tktK(puY_ƛnlڇ@bg>z-ӈ7,U$3B <wyd8[{^/첃>SNBUX,A-|V&vp! >A ˹[ݒnq'n-y{ރg3q}>hЍ"!99K.孷/}KJι[n=ƒ lh1G1gϥ[,?C?ě}pvcGG e/zы:([x~p!&t1bDq{;Q=ϥ[c}'qu=O%MH+<Mhq74){⑐]XwDӁC qSrY}ܗB }\jUnv mЃ}QHU0(V-|6wrl+ݶs[ܯ=9ynΘ?z*2==?V/c?n׮]{Aﰜz6~8/mdҭmx.&t֭[1Zeܨ! 疥[I[d {ҫ ܃LvçE@tkxΙ3e/{*6wҭy~T9X -Ǯ+P;<~tK1Q?`\9V&#!ݢp. D ı Tvb2B\4mOB[o5UĴ{Rf5&ZXֲn8 ? S}PWx5SJ60%ޔz/5hq- wX[ ?aĵ !_ڜuGYN8_Ln'x~ܡ;c9mr=?>S n?ajG~Gx^Q.gz!m-{ 7~Lخ;̃D ,/mxRܓ$ B@!"*b=0k'{~E2#]KM3b-/ ^p?ք /$۶mѢ΀kފ6p^i:#@`V1'=Mp\K4-0OB/";y{|ȶMW\g?`%u]g(xU.qb†٭!&W! @;x1o `,BmyG 72BP[n}Zo~~k(nc%7x]=M/yK[ `B`??|} ַN WK*eo|Is]fܠ[nx… )7 oꄀCG/~9Lz~<tHkQn4(B$-Aız440+="rwxy~ԊAAszDP,@Iƃ7o@Hg}x\6z !0nܧ?"} inq>Xnyt"mRz(G*hƁnq;+^ z>sxh0: ! :ۡ/@@t;ARB`n>B@ I]B@##} /lBdw؁=Y0)=Ojʧ !8xK+[ uE}B@E[yN岺Vj I EhVɤ[ULK[PB!-96y׬)[2B4*Gtkhb',m*"B@n_y"nQ"mC)B@! :G@tB@!0~Dc]B@t+& 7Ԇ7IENDB`./oar-2.5.2/misc/fault_tolerance/HAdocumentation/HA-DOCUMENTATION.tex0000644000175000017500000021153311757171206022773 0ustar plbplb\documentclass[a4paper,10pt]{report} \usepackage[utf8]{inputenc} \usepackage{graphicx} \usepackage[margin=2.5cm]{geometry} % ---- inclusion de codes \usepackage{listings} \usepackage{color} \definecolor{lbcolor}{rgb}{0.95,0.95,0.95} \lstset{frame=single, breaklines=true,showspaces=false,showstringspaces=false,breakautoindent=true, flexiblecolumns=true, keepspaces=true, backgroundcolor=\color{lbcolor}, basicstyle=\ttfamily,basicstyle=\scriptsize, keywordstyle=\color{red}, commentstyle=\color{vert}, stringstyle=\color{blue}, identifierstyle=\ttfamily} \usepackage[colorlinks=true,linkcolor=black,urlcolor=blue,pdfstartview=FitH]{hyperref} \usepackage{float} \usepackage{subfigure} % Title Page \title{High Availability Documentation on OAR - Admin Guide\\ \includegraphics[scale=0.7]{schema/oar_logo_detoure.png}} \author{Joris Brémond - \texttt{joris.bremond@gmail.com}\\ \\ Laboratoire d'Informatique de Grenoble\\ Bat. ENSIMAG - antenne de Montbonnot\\ ZIRST 51, avenue Jean Kuntzmann\\ 38330 MONTBONNOT SAINT MARTIN\\ \\ Authors: LIG laboratory\\ Organization: LIG laboratory\\ Status: Testing\\ Copyright: licenced under the GNU GENERAL PUBLIC LICENSE\\ } \begin{document} \maketitle \tableofcontents \begin{abstract} This documentation explains how to configure High Availability on OAR. This solution provides fault tolerance on both OAR server and database (mysql or postgresql). With this High Availability solution, OAR is protected from computer crash, services crash, network failure, etc. \end{abstract} \chapter{Introduction} %schema \begin{figure} \begin{center} \includegraphics[scale=0.5]{schema/architecture-schema-2-node.png} \end{center} \caption{Architecture schema 2 nodes} \label{2nodesschema} \end{figure} \begin{figure} \begin{center} \includegraphics[scale=0.5]{schema/architecture-schema-4-node.png} \end{center} \caption{Architecture schema 4 nodes} \label{4nodesschema} \end{figure} \begin{figure} \begin{center} \includegraphics[scale=0.5]{schema/DRBD.png} \end{center} \caption{DRBD Architecture} \label{drbd} \end{figure} This documentation explains how to configure a high availability cluster with OAR. You can do this configuration automatically with the batch script (see \ref{script}). We will see two different configurations : \begin{description} \item[]- oar-server daemon and database on the same machine. So you have two servers, the master (oar-server, database) and the backup. See Figure \ref{2nodesschema}. \item[]- oar-server daemon and database on different machines. You have four servers, the master OAR-server, the master database, the backup OAR-server, and the backup database. See Figure \ref{4nodesschema}. \end{description} \section{Requirements} This documentation does not describe how to configure and install OAR. So before installing OAR High Availability solution, install OAR with the database of your choice (mysql or postgresql). OAR-server and database must be also installed on the backup(s) server(s), with the same daemon configuration (oar.conf, my.conf or pg.conf). Nevertheless, it's not necessary that you initialize oAR resources on the backup server because its data are incorporated in database and DRBD will share it. So we will have the same configurations in both nodes. Before continue, please stop oar-server and database daemon. \section{General mechanism} To provide OAR High Availability, we use different tools : \begin{description} \item[]- The first one is \textbf{Heartbeat} version 2. Heartbeat is a daemon that provides fault tolerance. It manages and monitors services, like Virtual IP, filesystem, etc. When Heartbeat detects problems in resource, it can migrate it on the other server. It can manage groups of resources and define rules and priority on each server. \item[]- \textbf{DRBD}. DRBD refers to block devices designed as a building block to get high availability clusters. This is done by mirroring a whole block device via an assigned network. DRBD can be understood as network based raid-1. In OAR's high availability solution, we will use DRBD to duplicate the database.\\ See Figure \ref{drbd}. \item[]- \textbf{VirtualIP} : This package is installed with heartbeat, and managed by it. It provides a virtual IP address. Thereby, clients permanently know the elected server. When heartbeat activates resources on the node, the virtualIP package sends a gratuitous ARP. After that, any nodes on the network have in ARP table an entry with : \begin{lstlisting} IP address MAC address primary node \end{lstlisting} \end{description} \chapter{Installation} Note : before beginning installation on CentOS, verify if the repository "extra" is uncommented, in \textit{/etc/yum.repo.d/CentOS-Base.repo}. \section{DRBD} In this guide, we use DRBD-8. \begin{description} \item[]- On debian : The package name is drbd8-utils. It's easier to use module-assistant to install it: \begin{lstlisting} apt-get install drbd8-utils module-assistant module-assistant auto-install drbd8 \end{lstlisting} Module Assistant require kernel header. You can install it with : \begin{lstlisting} apt-get install linux-headers-`uname -r` \end{lstlisting} \item[]- On CentOS : The package name is drbd82 and kmod-drbd82 for kernel module : \begin{lstlisting} yum -y install drbd82 kmod-drbd82 \end{lstlisting} \end{description} Now DRBD is operational. \section{Heartbeat-2} Heartbeat has two versions. We will use the version 2 because we can monitor each resource and detect service crash. \begin{description} \item[]- On debian: The package name is heartbeat-2. \begin{lstlisting} apt-get install heartbeat-2 \end{lstlisting} You can also install the GUI package on your computer for heartbeat remote configuration. \begin{lstlisting} apt-get install heartbeat-2-gui \end{lstlisting} \item[]- On CentOS: The package name is heartbeat. \begin{lstlisting} yum -y install heartbeat \end{lstlisting} Verify if heartbeat is completly installed. If not, retry the previous command. \end{description} \chapter{Configuration} In the next section, the example will be illustrated with nodes names which are: \begin{description} \item[]- 2 nodes configuration : \begin{itemize} \item \textbf{node1} (master) \item \textbf{node2} (backup) \end{itemize} \item[]- 4 nodes configuration : \begin{itemize} \item \textbf{node1} (master oar) \item \textbf{node2} (master database) \item \textbf{node3} (backup oar) \item \textbf{node4} (backup database) \end{itemize} \end{description} \section{System configuration} \label{sysconf} It's really recommended that you have two different networks between each Heartbeat server. Indeed, heartbeat uses the network for check its neighbor. With two networks, we can for example avoid split-brain problem (See \ref{splitbrain}). We can use one Ethernet network and one serial network, ore two Ethernet networks. With a solution like this, you can easily cut the main network without interfere with heartbeat functioning.\\ For Heartbeat configuration, you need one or two network address (Virtual IP). If you are in 2 nodes configuration (OAR-server and database on the same nodes), you need one IP address, and two IP if you are in 4 nodes configuration. Two Virtual IP to access the oar-server and the database. This virtual IP must be free and accessible on the network.\\ Now, it is heartbeat which launches services like oar, mysql. So you must remove auto launch : \begin{description} \item[]- On debian: \begin{lstlisting} update-rc.d -f oar-server remove update-rc.d -f remove \end{lstlisting} \item[]- On CentOS: \begin{lstlisting} chkconfig oar-server off chkconfig off \end{lstlisting} \end{description} Where corresponds to mysql or postgres.\\ DRBD needs a low-level storage for write data. It could be an entire disk, a partition, or a virtual partition (in a file). So before configuring DRBD, create a free partition.\\ If you want to create a partition in a file (/image.img), you can follow this commands : \begin{lstlisting} SIZE=200 #Size in Mega-bytes dd if=/dev/zero of=/image.img bs=1M count=$SIZE losetup /dev/loop/0 /image.img mkfs -t ext3 /dev/loop/0 shred -zvf -n 1 /dev/loop/0 \end{lstlisting} Furthermore, you need to create a script to auto associate loopback device with the image file at startup, and before DRBD launching. \begin{description} \item[]- On debian: \begin{lstlisting} #!/bin/sh case "$1" in start|"") losetup /dev/loop0 /image.img echo "Start OK" ;; stop) losetup -d /dev/loop0 echo "Stop OK" ;; *) echo "Usage: active-loop [start|stop]" >&2 exit 3 ;; esac \end{lstlisting} Change right and add to startup (60 is before DRBD) : \begin{lstlisting} chmod +x /etc/init.d/active-loop update-rc.d active-loop defaults 60 \end{lstlisting} \item[]- On CentOS: \begin{lstlisting} #!/bin/sh # chkconfig: 2345 60 01 # description: Auto associate loopback with the file image.img case "$1" in start|"") losetup /dev/loop0 /image.img echo "Start OK" ;; stop) losetup -d /dev/loop0 echo "Stop OK" ;; *) echo "Usage: active-loop [start|stop]" >&2 exit 3 ;; esac \end{lstlisting} Change right and add to startup (60 is before DRBD) : \begin{lstlisting} chmod +x /etc/init.d/active-loop chkconfig --add active-loop chkconfig --level 2345 active-loop on \end{lstlisting} \end{description} \textbf{Important} : You must verify if the UDP port 694(heartbeat) and TCP 7788(DRBD) is open. \section{Heartbeat Configuration} There are three files to configure heartbeat-2 : \subsection{Authentication file : \textit{/etc/ha.d/authkeys}} The authkeys configuration file contains information for Heartbeat to use when authenticating cluster members. It cannot be readable or writable by anyone other than root. For sha authentication, write : \begin{lstlisting} auth 1 1 sha1 MyPassword \end{lstlisting} You can also use \textbf{md5} or \textbf{crc}.\\ Set file right: \begin{lstlisting} chmod 0600 /etc/ha.d/authkeys \end{lstlisting} \subsection{Daemon configuration file : \textit{/etc/ha.d/ha.cf}} The ha.cf file is one of the most important files. It lists the cluster nodes, the communications topology, and which features of the configuration are enabled. \begin{itemize} \item An example with 2 nodes configuration and two Ethernet interfaces : \begin{lstlisting} logfile /var/log/ha-log debugfile /var/log/ha-debug #use_logd on udpport 694 keepalive 1 # 1 second deadtime 10 initdead 80 ucast eth1 IPNODE1 ucast eth1 IPNODE2 ucast eth0_rename IPNODE1 ucast eth0_rename IPNODE2 node node1 node node2 crm yes \end{lstlisting} \item For 4 nodes configurations : \begin{lstlisting} logfile /var/log/ha-log debugfile /var/log/ha-debug #use_logd on udpport 694 keepalive 1 # 1 second deadtime 10 initdead 80 ucast eth1 IPNODE1 ucast eth1 IPNODE2 ucast eth1 IPNODE3 ucast eth1 IPNODE4 ucast eth0_rename IPNODE1 ucast eth0_rename IPNODE2 ucast eth0_rename IPNODE3 ucast eth0_rename IPNODE4 node node1 node node2 node node3 node node4 crm yes \end{lstlisting} \end{itemize} \textbf{\underline{details:}}\\ \textbf{keepalive} : x second between each beat\\ \textbf{deadtime} : after x second without heartbeat, the nodes is declared dead\\ \textbf{initdead} : set the time that it takes to declare a cluster node dead when Heartbeat is first started\\ \textbf{ucast}: [interface] [ip node1] network for send/receive heartbeat. You can also use multicast(mcast) or broadcast(bcast) address\\ \textbf{node} : the node names\\ \textbf{crm} : yes for use heartbeat version 2 (very recommended)\\ \subsection{Resource description file : \textit{/var/lib/heartbeat/crm/cib.xml}} This file specifies the services managed by heartbeat. You can find three sections : \begin{itemize} \item The first one is the cluster property. It describes general cluster behavior. You can specify different options : \begin{itemize} \item \textbf{symmetric-cluster} : Is resource can run on any node by default ? \item \textbf{no-quorum-policy} : Action to do when cluster detects quorum \item \textbf{default-resource-stickiness} : How much does resource prefers to stay where it is \item \textbf{default-resource-failure-stickiness} : How many failures before migration \item \textbf{stonith-enabled} : If true, heartbeat kill the node which have resource failure. You must create a STONITH device before active it. \item \textbf{stonith-action} : Action to do : reboot or power off \item \textbf{startup-fencing} : STONITH unseen nodes if true (default) \item \textbf{stop-orphan-resources} : If true, heartbeat should deleted resource be stopped \item \textbf{stop-orphan-actions} : If true, should deleted action be canceled \item \textbf{remove-after-stop} : Remove resources from heartbeat after they are stopped, default = false \item \textbf{short-resource-names} : true \item \textbf{transition-idle-timeout} : Provides the default global timeout for actions. Any action which has a defined timeout automatically uses the action-specific timeout \item \textbf{default-action-timeout} : How long heartbeat wait for actions to complete \item \textbf{is-managed-default} : Heartbeat can start or stop resources as required \item \textbf{cluster-delay} : Round trip delay over the network \item \textbf{pe-error-series-max} : The number of PE (Policy Engine) inputs resulting in errors to save. -1 = unlimited \item \textbf{pe-warn-series-max} : The number of PE inputs resulting in Warnings to save \item \textbf{pe-input-series-max} : The number of other PE inputs to save \end{itemize} \item The second part is the resources properties. You can define groups of resources, and configure them. \begin{itemize} \item \textbf{Group definition} : you can create group with the directive : \begin{lstlisting}[language=xml] \end{lstlisting} Now, you can add resources in this group. \item \textbf{Resource definition} : There are two different types of resource : OCF or LSB.\\ OCF example : \begin{lstlisting}[language=xml] \end{lstlisting} LSB example : \begin{lstlisting}[language=xml] \end{lstlisting} OCF (Open Cluster Framework) Resource Agents is provided by heartbeat. You can find resources like IPaddr2 (for virtual IP), DRBDdisk for DRBD, etc. You can also add resource with LSB standard (Linux Standards Base). Generally, it is the main type of resource. The location of the resource you can add is on \textit{/etc/ha.d/resource.d} and \textit{/etc/init.d/}. When you defined the resource, the type is the name of the resource. Example, if you want to add mysql (/etc/init.d/mysql), you must write : type="mysql". For "id", you can enter the name of your choice. \item \textbf{Resource operations} : Some operations can be added on resources. In our case, you can add monitoring resource. Example : \begin{lstlisting}[language=xml] \end{lstlisting} When the timeout is over, heartbeat detects resource crash. \item \textbf{Resources attribute} : Sometimes, resources require additional parameters. You can enter them with the attribute directive : \begin{lstlisting}[language=xml] \end{lstlisting} \end{itemize} \item The last part is resources constraints. You can add constraints on resource or resource group, like location constraint. In our case, you can specify node priority, for example if you want for the resources to run in priority on the master. \begin{lstlisting}[language=xml] \end{lstlisting} \item Example of heartbeat configuration cib.xml file : see appendix \ref{cibexample}\\ Tips : The mysql OCF script integrated in heartbeat doesn't work for me, so to avoid to use it, rename the mysql script (just for Debian) in mysqlha for example, and specify it's a LSB script for heartbeat : \begin{lstlisting} cp /etc/init.d/mysql /etc/init.d/mysqlha \end{lstlisting} \end{itemize} Now, you must set rights on the cib.xml file, and give it to its owner : \begin{lstlisting} chown hacluster:haclient /var/lib/heartbeat/crm/cib.xml chmod 0600 /var/lib/heartbeat/crm/cib.xml \end{lstlisting} \subsection{OAR Service modifications} \begin{description} \item[]- On debian:\\ The OAR service (/etc/init.d/oar-server) is currently not compatible with LSB standard. It doesn't have the status function. So heartbeat can't monitor it. To add status function on oar-server, edit \textit{/etc/init.d/oar-server} file, and add : \begin{lstlisting}[language=bash] status) if [ -f $PIDFILE ] then PID=`head -n 1 $PIDFILE` if [ `ps -ef | grep -v grep | grep $PID | wc -l` -ge 1 ] then echo "OAR is running" exit 0 else echo "OAR is not runnig" exit 2 fi else echo "OAR is not runnig" exit 2 fi ;; \end{lstlisting} \item[]- On CentOS:\\ The return value in CentOS is different than Debian. For example, if you stop oar-server while running, and the action success, oar service returns 0. But if it is already stopped, Debian returns 0 but not CentOS. So heartbeat failed in monitor mode, because it doesn't have the right return value. You must modify the "stop" option : \begin{lstlisting}[language=bash] stop() { echo -n "Stopping $DESC: " if [ -n "`pidfileofproc $DAEMON`" ]; then killproc $DAEMON sleep 1 killall Almighty 2>/dev/null sleep 1 killall -9 Almighty 2>/dev/null RETVAL=0 else failure $"Stopping $DESC" RETVAL=$? if [ `ps -ef | grep -v "grep" | grep Almighty | wc -l` -eq 0 ]; then RETVAL=0 fi fi echo } \end{lstlisting} \end{description} \section{DRBD Configuration} In DRBD configuration file, you can specify a lots of parameters, which describe resources, synchronization type, synchronization rate, etc. For a complete example, see \ref{drbdexample}. \subsection{synchronization type} DRBD has three synchronization types : \begin{itemize} \item \textbf{Protocol A} : an ACK (on master) is send when data was transmitted on master disk and sent to slave. \item \textbf{Protocol B} : an ACK (on master) is send when data was transmitted on master disk and received by slave. \item \textbf{Protocol C} : an ACK (on master) is send when data was transmitted on master disk and slave disk.\\ \textbf{Very recommended !} \end{itemize} \subsection{Startup action} When DRBD is launched, you can do actions. For example, inform it to wait x seconds : \begin{lstlisting} startup { wfc-timeout x; } \end{lstlisting} \subsection{Disk action} The directive ``disk'' can add action to do for disk. It's recommended to add this one : \begin{lstlisting} disk { on-io-error detach; } \end{lstlisting} With the detach option, the node drops its backing device on I/O error, and continues in diskless mode. \subsection{Synchronization settings} You can precise the rate of synchronization, the hot-area size : \begin{lstlisting} syncer { rate 700000K; al-extents 257; } \end{lstlisting} 700000K is the maximum value accepted by DRBD.\\ al-extents corresponds to 4 Mbytes, so with 257, we have 1 GBytes. \subsection{Host section} \label{hostsection} One resource must have two host sections. A host section describes on each node which DRBD partition and disk is used. You must also specify neighbor ip and port : \begin{lstlisting} resource mysql { on node1 { device DRBDPARTITION; disk DISKPARTITION; address node2:DBRDPORT; meta-disk internal; } on node2 { device DRBDPARTITION; disk DISKPARTITION; address node1:DBRDPORT; meta-disk internal; } } \end{lstlisting} \subsection{Handlers} DRBD provides an handler which can execute actions on events. For the problem of split brain (see \ref{splitbrain}), you can for example write in log : \begin{lstlisting} handlers { split-brain "echo Splitbraindetected >> /var/log/drbd-log" pri-lost-after-sb "echo pri-lost-after-sb >> /var/log/drbd-log" } net { #cram-hmac-alg \"sha1\"; #shared-secret \"123456\"; after-sb-0pri discard-younger-primary; after-sb-1pri consensus; after-sb-2pri call-pri-lost-after-sb; #rr-conflict violently; } \end{lstlisting} In the net directive, you can specify different policies when you detect split brain.\\ \textbf{after-sb-0pri} : Split brain has just been detected, but at this time the resource is not in the Primary role on any host. \begin{itemize} \item disconnect : Do not recover automatically, call split-brain handler script, drop the connection and continue in disconnected mode. \item discard-younger-primary : Discard and roll back the modifications made on the host which assumed the Primary role last. \item discard-least-changes : Discard and roll back the modifications on the host where fewer changes occurred. \item discard-zero-changes : If there is any host on which no changes occurred at all, simply apply all modifications made on the other and continue. \end{itemize} \textbf{after-sb-1pri} : Split brain has just been detected, and at this time the resource is in the Primary role on one host. \begin{itemize} \item disconnect \item consensus : Apply the same recovery policies as specified in after-sb-0pri. If a split brain victim can be selected after applying these policies, automatically resolve. Otherwise, behave exactly as if disconnect were specified. \item call-pri-lost-after-sb : Apply the recovery policies as specified in after-sb-0pri. If a split brain victim can be selected after applying these policies, invoke the pri-lost-after-sb handler on the victim node. \item discard-secondary : Whichever host is currently in the Secondary role, make that host the split brain victim. \end{itemize} \textbf{after-sb-2pri} : Split brain has just been detected, and at this time the resource is in the Primary role on both hosts. This option accepts the same keywords as after-sb-1pri except discard-secondary and consensus. \subsection{Launch DRBD} Before launching DRBD between the two host, you must prepare the device. The first things to do, is to load DRBD module : \begin{lstlisting} modprobe drbd \end{lstlisting} After that, you must verify if the drbd device exists. Look at /dev/drbd0. If this device doesn't exist, create it with : \begin{lstlisting} mknod /dev/drbd0 b 147 0 \end{lstlisting} Now, it is time to create meta data and launch DRBD : \begin{lstlisting} drbdadm create-md all drbdadm up all \end{lstlisting} To mount DRBD device, you must create a folder, /mnt/drbdata for example : \begin{lstlisting} mkdir /mnt/drbddata \end{lstlisting} DRBD is practically operational. You have to define a master, node1 in our example, so enter this command only on this node : \begin{lstlisting} drbdadm -- --overwrite-data-of-peer primary all mkfs -t ext3 /dev/drbd0 mount /dev/drbd0 /dev/drbddata \end{lstlisting} The ``all'' option means that node1 is the master of all resources defined in drbd.conf.\\ The mkfs command prepares file system on drbd partition, and mounts this partition.\\ Now DRBD is operational, all data put in \textbf{/mnt/drbddata} will be synchronized. \section{Database Configuration} Now, it is time to configure the database. The datadir will be changed into the mount point of disk partition defined in DRBD (see \ref{hostsection}). \subsection{Mysql} To change datadir in mysql, open the file \textit{/etc/mysql/my.cnf} for Debian or \textit{/etc/my.cnf} for CentOS, search the primitive \textbf{datadir}, and replace the value by the partition defined in DRBD. For example, you can enter : \begin{lstlisting} datadir = /mnt/drbddata \end{lstlisting} You must copy all old mysql data in new datadir, and change owner : \begin{lstlisting} cp -r /var/lib/mysql /mnt/drbddata chown -R mysql:mysql /mnt/drbddata/mysql \end{lstlisting} If you want remote mysql access, don't forget to comment the \textbf{bind-address} in the my.cnf.\\ In CentOS, you must correct a problem, like for oar-server, with the return value. At the begining of the "stop" option in \textit{/etc/init.d/mysqld}, add : \begin{lstlisting} stop(){ status mysqld if [ $? -eq 3 ]; then action $"Stopping $prog: " /bin/true exit 0 fi ... \end{lstlisting} \subsection{Postgresql} It is almost the same in Postgres. Open the file \textit{/etc/postgres/'version'/main/postgresql.conf}, and search data\_directory and change value in : \begin{lstlisting} data_directory = 'mnt/drbddata/main' \end{lstlisting} Like mysql, if you want remote access, change \textbf{listen\_addresses = 'localhost'} in \textbf{listen\_addresses = '*'}. Don't forget to add rules for your network in the hba.conf file.\\ You must copy all old postgres data in new datadir, and change owner : \begin{lstlisting} cp -r /var/lib/postgresql/'version'/main /mnt/drbddata chown -R postgres:postgres /mnt/drbddata/main \end{lstlisting} \section{OAR Configuration} \subsection{Server configuration} OAR server must be configured to work fine with High Availability. The first step is to configure database hostname and server hostname on oar-server. \begin{itemize} \item In 2 nodes configuration, one virtual IP is necessary. With this IP, client can access to oar-server and database because it is on the same server. So in \textit{/etc/oar/oar.conf}, modify : \begin{lstlisting} SERVER_HOSTNAME=VIRTUALIP DB_HOSTNAME=VIRTUALIP \end{lstlisting} \item In 4 nodes configuration, two virtual IP are necessary. The first one is for oar-server, and the second is for database. So in \textit{/etc/oar/oar.conf}, modify : \begin{lstlisting} SERVER_HOSTNAME=VIRTUALIP_1 DB_HOSTNAME=VIRTUALIP_2 \end{lstlisting} \end{itemize} The second step is to configure the \textbf{DETACH\_JOB\_FROM\_SERVER} option. Indeed, with this option, OAR doesn't keep connection between oar-server and nodes. So after oar-server crash, the backup oar-server is contacted when the job is ended, because oarexec reconnects to the new server, thanks to VIRTUALIP\_1.\\ In /etc/oar/oar.conf, you must enter : \begin{lstlisting} DETACH_JOB_FROM_SERVER="1" \end{lstlisting} \begin{figure}[!h] \centering \subfigure[Scenario with DETACH=0]{\label{fig:withoutdetach}\includegraphics[scale=0.5]{schema/sequence-diagram-without-detach.png}} \subfigure[Scenario with DETACH=1]{\label{fig:withdetach}\includegraphics[scale=0.5]{schema/sequence-diagram-with-detach.png}} \caption{Detail of DETACH option} \label{fig:detach} \end{figure} \subsection{Frontend configuration} \label{frontendconf} It is approximately the same configuration on frontend :\\ \begin{itemize} \item In 2 nodes configurations : \begin{lstlisting} SERVER_HOSTNAME=VIRTUALIP DB_HOSTNAME=VIRTUALIP \end{lstlisting} \item In 4 nodes configurations : \begin{lstlisting} SERVER_HOSTNAME=VIRTUALIP_1 DB_HOSTNAME=VIRTUALIP_2 \end{lstlisting} \end{itemize} \subsection{Node configuration} \label{nodeconf} If you use Start/Stop of nodes using ssh keys (see http://wiki-oar.imag.fr/index.php/Configuration\_tips section 1.5) and you want to do oarnodesetting form oar-server, you have just to add the virtual IP from OAR-server in \textit{/etc/default/oar-nodes} file. \begin{lstlisting} OARREMOTE=VIRTUALIP \end{lstlisting} \section{Administration commands} \subsection{Heartbeat administration} Before reading further, heartbeat and drbd must be configured and started. For launch heartbeat, run : \begin{lstlisting} /etc/init.d/heartbeat start \end{lstlisting} \subsubsection{Heartbeat-gui} Heartbeat provides a graphic interface to configure resources. You can install the package heartbeat2-gui on a remote computer, with the command(on debian) : \begin{lstlisting} apt-get install heartbeat2-gui \end{lstlisting} on CentOS : \begin{lstlisting} yum install heartbeat-gui \end{lstlisting} Note for grid5000 users : you must create an ssh tunnel on port 5560. Example : \begin{lstlisting} ssh -L 5560:machine.grid5000.fr:5560 login@access.site.grid5000.fr \end{lstlisting} When you launch heartbeat-gui, it asks for login and password. On the heartbeat server, define a new password for \textbf{hacluster} user : \begin{lstlisting} passwd hacluster \end{lstlisting} Now you can connect with user hacluster and his password.\\ When you are connected, you can see your configured nodes, and resources if you defined them in the cib.xml file. The figure \ref{hb-gui-connect} shows a heartbeat cluster without resources.\\ To add resource, right click on \textbf{Resources}, and choose \textbf{Add New Item}, \textbf{Native}. Now select the resource, add parameter if you want, and click \textbf{Add}.\\ You can add group with the same operations, but select \textbf{Group} in place of \textbf{Native}. Select \textbf{Ordered} for launched resources with order.\\ It is also possible to add location constraint with heartbeat-gui. Select \textbf{Location}, and the group or resource to apply this constraint.\\ To launche a resource, right click on this resource, and select \textbf{start}.\\ If a resource failed to start for example, you must cleanup the resource. You can do this by right clicking on the resource, and select \textbf{Cleanup resource}.\\ To test your configurations, you can standby one node, and see if the resource migrates. To Standby a node, right click on it and select \textbf{Standby}. The figure \ref{hb-gui-2nodes} is an example with 2 nodes configuration and mysql. \begin{figure}[H] \centering \includegraphics[scale=0.4]{schema/hb_gui-2nodes-connect.png} \caption{heartbeat-gui connection} \label{hb-gui-connect} \end{figure} \begin{figure}[H] \centering \includegraphics[scale=0.4]{schema/hb_gui-2nodes.png} \caption{heartbeat-gui 2 nodes example} \label{hb-gui-2nodes} \end{figure} \subsubsection{Heartbeat command-line} \label{hb-command} Different commands are available : \begin{enumerate} \item crmadmin - provides node related details \item cibadmin - allows the current configuration to be queried and modified \\ You can change the cib.xml file with these commands : \begin{lstlisting} cibadmin -Q > cibbackup.xml \end{lstlisting} Apply change on cibbackup.xml,and update cib.xml with : \begin{lstlisting} cibadmin -R -x cibbackup.xml \end{lstlisting} \item crm\_verify - checks if configuration is valid \item crm\_mon - provides the current cluster status in text or HTML , see \ref{hb-monitor} \item crm\_resource - queries and modifies all things related to cluster resources/services\\ Scenario : The resource OAR is crashed and doesn't restart. Heartbeat migrates it on the second node. When you have corrected the problem on the first node, you must cleanup the resource with the command : \begin{lstlisting} crm_resource -C -r oar-server -H host \end{lstlisting} \item crm\_standby - controls a node's standby status (ability to run resources) \\ Set node in standby mode : \begin{lstlisting} crm_standby -U host -v true \end{lstlisting} \item cl\_status - provides low-level node-centric connectivity information. \end{enumerate} \subsection{DRBD administration} \label{drbd-admn} \begin{itemize} \item attach and connect all DRBD resources : \begin{lstlisting} drbdadm up all \end{lstlisting} \item detach and disconnect all DRBD resources : \begin{lstlisting} drbdadm down all \end{lstlisting} \item Put node in primary mode : \begin{lstlisting} drbdadm primary all \end{lstlisting} \item Put node in secondary mode : \begin{lstlisting} drbdadm secondary all \end{lstlisting} \item After a slipt-brain (see \ref{splitbrain}), you must invalidate data on one node, and reconnect it. The command \textit{cat /proc/drbd} indicates standalone mode. If the node you want invalidate data is primary, you must standby it before (with heartbeat), and when it is secondary, you can type : \begin{lstlisting} drbdadm invalidate all drbdadm connect all \end{lstlisting} \end{itemize} \section{Monitoring utils} \subsection{Heartbeat monitoring} \label{hb-monitor} Heartbeat-2 provides different solutions to monitor resources and know nodes status. \begin{itemize} \item Command line solution : On any heartbeat nodes, you can enter : \begin{lstlisting} crm_mon -i 1 \end{lstlisting} You can see nodes status, and where resources are launched, follow failed actions. \item Web solution : you can monitor resource with a cgi script. To install it, create a file in \textbf{cgi-bin} path, \textit{/usr/lib/cgi-bin/ha-status.cgi} on Debian or \textit{/var/www/cgi-bin/ha-status.cgi} on CentOS: \begin{lstlisting} #!/bin/bash sudo /usr/sbin/crm_mon -w \end{lstlisting} Add apache (www-data) in the sudoers file (for Debian) : \begin{lstlisting} echo 'www-data ALL=NOPASSWD:/usr/sbin/crm_mon' >> /etc/sudoers \end{lstlisting} For CentOS, add the user apache in the sudoers file and comment the line \textit{Defaults requiretty} for enable sudo in tty mode: \begin{lstlisting} echo 'apache ALL=NOPASSWD:/usr/sbin/crm_mon' >> /etc/sudoers \end{lstlisting} Set right : \begin{lstlisting} chmod 755 .../ha-status.cgi \end{lstlisting} Now you can access to heartbeat status with http://x.x.x.x/cgi-bin/ha-status.cgi \item Logs : you can access to heartbeat log in \textit{/var/log/ha-log} \end{itemize} \subsection{DRBD monitoring} To know DRBD status, you can type :\\ \begin{lstlisting} cat /proc/drbd \end{lstlisting} You can know who is the primary/secondary, if DRBD is connected, uptodate, have detected split-brain. If you have a running configuration, you may have : \begin{figure}[!h] \begin{center} \includegraphics[scale=0.5]{schema/drbd-s-p.png} \end{center} \caption{DRBD secondary primary} \label{drbdstatus} \end{figure} \chapter{known problems} \section{Quorum} The quorum calculation process is required to select no more than one primary subcluster at a time. You can enable or disable this option in heartbeat, it depends on the configuration.\\ A common method for defining quorum is to say that a subcluster of size n in a cluster of m total nodes has quorum if it has a plurality of nodes in its partition. That is, it has n members where n \textgreater INT(m/2).\\ But this solution in heartbeat creates problems in 4 nodes configuration. Indeed, when 2 nodes are down, the two others can't launch resources because they haven't quorum. In this case, you can disable quorum in heartbeat, in the cib.xml file : \begin{lstlisting}[language=xml] \end{lstlisting} \section{Split brain} \label{splitbrain} When a node thinks the other is unavailable, and vice versa, heartbeat launches resources on two nodes. In our configurations, DRBD becomes primary on two nodes, and has two version of data. This problem may appear when the communication between the two heartbeat servers is loosed. To limit this problem, you can connect two links between each heartbeat server. \section{File System Check} A fail over must be fastest as possible. So it could be very embarrassing if you have a file system check during the fail-over. You can deactivate it with : \begin{lstlisting} tune2fs -c 0 DISKPARTITION \end{lstlisting} \chapter{FAQ} \begin{itemize} \item Be careful with heartbeat version. The heartbeat-gui package must have the same version that heartbeat daemon. \item If you have install heartbeat on an environment, and save this environment after, it is possible you have problems. Indeed, heartbeat create an ID after primary start, and you must have different heartbeat ID on each nodes. \end{itemize} \chapter{High Availability auto installation} \label{script} You can install High Availability on OAR with the HAinstal bash script. This script must be launched on each node you want to make fault tolerant, with root rights. For example, on 2 nodes if oar-server and database are on the same server (the master with oar-server and database, and the backup), and on 4 nodes (master oar-server, master database, backup oar-server, backup database) if it different. Like previously, you must have a working installation (oar, database) : OAR must access the database, etc.. This script doesn't configure frontend and nodes, so you must refer to \ref{frontendconf} and \ref{nodeconf}. After script, you must start heartbeat : \textit{/etc/init.d/heartbeat start} \section{Prerequisites} \begin{itemize} \item Root rights \item Oar and database already installed and ready to work (without High availability) \item Linux-headers (only on Debian): \begin{lstlisting} apt-get install linux-headers-`uname -r` \end{lstlisting} \item For CentOS : access to the depot "extra" \item One or two free IP addresses (for virtual IP) \item A free partition, or selected option USEFILEPARTITION \end{itemize} \section{Parameters} \begin{itemize} \item Package name :\\ \begin{lstlisting} DRBD="drbd8-utils" HEARTBEAT2="heartbeat-2" MODULEASSITANT="module-assistant" \end{lstlisting} These variables contains debian package name of DRBD, heartbeat-2, and module assistant. MODULEASSISTANT is only used on the Debian script. \item BDD :\\ \begin{lstlisting} # "mysql" or "postgresql" BD="postgresql" or BD="mysql" ##only for postgres PGVERSION="8.3" \end{lstlisting} The BD variable must be mysql or postgresql. If you choose postgresql, you must enter the version, for script access into service directory, like \textit{/etc/postgresql/8.3/main/postgresql.conf}. \item Nodes name :\\ \begin{lstlisting} MASTER_OAR="genepi-7.grenoble.grid5000.fr" MASTER_DB="genepi-7.grenoble.grid5000.fr" BACKUP_OAR="genepi-9.grenoble.grid5000.fr" BACKUP_DB="genepi-9.grenoble.grid5000.fr" \end{lstlisting} The name must be the result of \textbf{uname -n} command, for each node. \item Nodes properties :\\ \begin{lstlisting} #Interface ETH="eth1" #OAR-server Virtual IP (ex : ) IP="172.16.16.220" #Database Virtual IP (ex : 172.16.16.221) IP_DB="172.16.16.221" #enter CIDR netmask (ex : 24) MASK="24" \end{lstlisting} The ETH variable is the network interface used for heartbeat. IP and IP\_DB are the two virtual IP. If you are in 2 nodes configuration, IP\_DB is ignored. \item Heartbeat configuration :\\ \begin{lstlisting} ##Password sha PASSWORD="oarteam" ##UDP Port HBPORT="694" \end{lstlisting} The password is used for heartbeat connection. The default heartbeat port is UDP 694. \item DRBD configuration \begin{enumerate} \item PARTITION :\\ \begin{lstlisting} #--low-level storage for the drbd data (partition)---# #It could be a loopback device, like /dev/loop0 DISKPARTITION=/dev/loop0 #Do you want create a partition in a file ? ("y" or "n") --> The DISKPARTTION Must be a loopback interface USEFILEPARTITION="y" \end{lstlisting} If you want use partition in a file (like describe in \ref{sysconf}), write "y". If you have choose "y" : \begin{lstlisting} #-PATH File image-# IMAGE=/image.img #Size in MegaBytes SIZE="200" \end{lstlisting} \item DRBD device :\\ \begin{lstlisting} #----DRBD partition alias---# DRBDPARTITION=/dev/drbd0 #----DRBD mount point---# DRBDDATA=/mnt/drbddata #---FileSystem type (ext2,ext3)---# FSTYPE="ext3" \end{lstlisting} The mount point is where you want to mount shared data with DRBD. \item DRBD port :\\ \begin{lstlisting} ##DRBD communication Port DBRDPORT="7788" \end{lstlisting} \end{enumerate} \item CGI script :\\ If you want to use CGI script to monitor heartbeat, say "y", else "n" \begin{lstlisting} CGI="y" \end{lstlisting} \end{itemize} \section{How does it work} \begin{itemize} \item Initialization: \\ Thanks to "uname -n" command, script checks if you are in 2 nodes configurations or not, if the node is the master, backup, etc. \item Functions:\\ This part contains useful functions. The function exists is used to verify if a command used by the script is present. For example, the script tests if the command "losetup" is available. \item Prerequisites test:\\ Test with the function exists, view above. \item Installation:\\ The installation part installs heartbeat version 2 and DRBD 8 if the node is the database server. \item Stop service:\\ Before modify service configuration, it is recommended to stop it. \item Heartbeat configuration:\\ In this part the script configures heartbeat. It copies configurations in three files : \textit{/etc/ha.d/authkeys}, \textit{/etc/ha.d/ha.cf}, \textit{/var/lib/heartbeat/crm/cib.xml}. The configuration is different if you are in 2 or 4 nodes configuration. So the script contains different scenarios. \item OAR service modification:\\ There are different modifications to do for OAR service. For Debian, the init script don't have the status function. For CentOS, the script must change the return value for better heartbeat compatibility. \item OAR configuration:\\ Now, the script configures OAR with the detach mode, put the virtual IP address for database and oar-server hostname. \item Database configuration:\\ For database, the script defines the data directory. \item DRBD configuration:\\ Copy DRBD configuration file \textit{/etc/drbd.conf}. After that, it loads drbd module, creates the image file if you have set USEFILEPARTITION to "y", copies old mysql data into the DRBD device, and puts the two nodes in secondary mode. \item Remove auto-launch services:\\ After installation, only heartbeat must manage services like oar-server, database, etc. So the system mustn't start services automatically. \item Automatic association of loopback device with image:\\ If you have set USEFILEPARTITION to "y", the system must associate the file image.img to the loopback interface, before DRBD starts. So the script creates a startup script which realizes this action automatically. \item Add cgi script:\\ If you want to monitor heartbeat over HTTP, the script creates a CGI script, accessible via http//x.x.x.x/cgi-bin/ha-status.cgi \end{itemize} \chapter{Test} \section{DRBD performance test} To test DRBD performance, different benchmarks have been realized with mysql for test DRBD performance. These tests use DRBD in synchronous mode (protocol C in drbd.conf).\\ \textbf{Test environments :} \begin{enumerate} \item Master DRBD was genepi-31.grenoble.grid5000.fr. The test without DRBD was also on this node. \item The secondary DRBD server was genepi-32.grenoble.grid5000.fr \item Filesystem with and without DRBD was ext2 \item The synchronization rate (in drbd.conf) between this two node was 740Mo/s (Max) \item Network bandwidth : 940 Mbytes/s between genepi-31 and genepi-32 \end{enumerate} The tests are done with sysbench, with 16 threads.\\ Three different scenarios were tested : \begin{enumerate} \item without DRBD, mysql data mounted on the local system filesystem \item with DRBD, mysql data mounted on DRBD filesystem \item with DRBD and saturated network \end{enumerate} \bigskip \underline{\textbf{Measured value :}}\\ \begin{tabular}{|c|c|c|c|} \hline Number of queries&Time without DRBD(s)&Time With DRBD(s)&With DRBD-saturated network (s)\\ \hline 5000 & 3.67 & 3.66 & 8.75 (+138.69\%)\\ \hline 10000 & 7.51 & 7.8 (+3.94\%)& 18.95 (+152.38\%)\\ \hline 20000 & 14.95 & 16.23 (+8.55\%)& 41.37 (+176.67\%)\\ \hline 40000 & 31.43 & 32.82 (+4.4\%) & 82.86 (+163.61\%)\\ \hline 80000 & 61.62 & 66.77 (+8.35\%)& 169.64 (+175.28\%)\\ \hline 160000 & 124.32 & 134.04 (+7.81\%)& 342.23 (+175.28\%)\\ \hline 320000 & 247.99 & 264.56 (+6.68\%)& 682.21 (+175.1\%)\\ \hline 640000 & 496.6 & 536.77 (+8.09\%)& 1375.12 (+176.91\%)\\ \hline \end{tabular} \begin{figure}[H] \begin{center} \includegraphics[scale=0.5]{schema/drbd-test.png} \end{center} \caption{DRBD benchmark} \label{drbdbench} \end{figure} You can see that DRBD offers good performance in general. It can be used on OAR without hesitation. \section{Global system test on Debian} \subsection{2 nodes - Postgres} \begin{itemize} \item Migrate resources by setting nodes in standby mode : Test OK, heartbeat launches resources on the backup, and when the primary comes back, resources are migrate on the primary. \item Power off/power on the master server (with IPMI, kareboot) : Test OK, resources migrate after a time, about 60s, which corresponds at cluster delay. When the master node is anew alive, resources migrate on the master node. \item Power off/power on the backup server (with IPMI, kareboot), without resources : Test OK, the server is down. Heartbeat lets its resources on the master. \item \textit{Killall -9 Almighty} on primary : Test OK, the master relaunches oar-server. \item \textit{killall -9 postgres} : Test OK, the master stop oar-server, launches postgres and then launches oar-server. \item \textit{Killall -9 Almighty} and comment the line in /etc/init.d/oar-server to start the daemon : Test OK, heartbeat doesn't succeed to restart oar-server, so it migrates resources on the backup. But after uncommenting the line on master, you must cleanup resource otherwise heartbeat doesn't want to restart resource. See \ref{hb-command}. \item Add rule in iptable to drop all communications : heartbeat and DRBD (iptables -A OUTPUT -p tcp --dport 7788 -j DROP, ...). Heartbeat launches resources on the two nodes. DRBD are primary on the two nodes and work with standalone mode. When the network comes back, heartbeat launches resources on the master, but DRBD stays in standalone mode. This is the split brain problem. It can be solved manually (See \ref{drbd-admn}). Admin can enter his mail in the drbd split brain handler for know when drbd is in standalone mode. Test OK, but heartbeat needs admin intervention. \item Heartbeat, in my configuration, have two links. The test now is two disconnect one of the two links : Test OK, heartbeat continues to run. In the log (\textit{/var/log/ha-log}), you can see Link node eth1 dead. \end{itemize} \textbf{Software test} : 2 servers (with postgres), 1 frontend, 1 node (resource). All commands are typed from the frontend. \begin{itemize} \item execute \textit{oarnodes}, standby the master, and retry oarnodes : Test OK \item Reserve resource (oarsub -I), and after first node connection, standby the server, and disconnect, WITHOUT detach : Test failed, the job is not ended, and stays alive. \item Reserve resource (oarsub -I), and after first node connection, standby the server, and disconnect, WITH detach : Test OK ! The job continues to run, and when the user quits, the new oar-server successes to kill the job, properly ! If the job is ended before the backup is up, the job is not deleted immediately, but after a few time. In the job events, we can see : SWITCH\_INTO\_TERMINATE\_STATE:[bipbip 4] Ask to change the job state. After that, the job is killed. In oarexec code, function quit\_oarexec loops until the daemon has informed the server. \item Reserve resource (oarsub -I), reserve a new resource again. The second user waits because there is just one resource. Standby the server. The first job continues to run, the second waits. When the first job is ended, the second job begins ! It's very interesting, the job start its scheduling on the master oar server, and it is authorized by the backup oar server. \item Execute oarstat, standby the master node, and execute oarstat : Test OK \end{itemize} \subsection{2 nodes - Mysql} \begin{itemize} \item Migrate resources by setting nodes in standby mode : Test OK, heartbeat launches resources on the backup, and when the primary comes back, resources are migrated on the primary. \item Power off/power on the master server (with IPMI, kareboot) : Test OK. \item Power off/power on the backup server (with IPMI, kareboot), without resources : Test OK. \item \textit{Killall -9 Almighty} on primary : Test OK. \item \textit{/etc/init.d/mysql stop} : Test OK. \item \textit{Killall -9 Almighty} and comment the line in /etc/init.d/oar-server for start the daemon : Test OK, but admin has to cleanup resource otherwise it can't migrate it on the failed node. See \ref{hb-command}. \item Add rule in iptable to drop all communications : heartbeat and DRBD (iptables -A OUTPUT -p tcp --dport 7788 -j DROP, ...). Heartbeat launches resources on the two nodes. DRBD are primary on the two nodes and work with standalone mode. When the network comes back, heartbeat launches resources on the master, but DRBD stays in standalone mode. This is the split brain problem. It can be solved manually (See \ref{drbd-admn}). Admin can enter his mail in the drbd split brain handler to know when drbd is in standalone mode. Test OK, but heartbeat needs admin intervention. \item Heartbeat, in my configuration, have two links. The test now is two disconnect one of the two links : Test OK, heartbeat continues to run. In the log (\textit{/var/log/ha-log}), you can see Link node eth1 dead. \end{itemize} \textbf{Software test} : 2 servers (with mysql), 1 frontend, 1 node (resource). All commands are typed from the frontend. \begin{itemize} \item execute \textit{oarnodes}, standby the master, and retry oarnodes : Test OK, oarnodes says : try to connect... until the backup is alive. \item Reserve resource (oarsub -I), and after first node connection, standby the server, and disconnect, WITHOUT detach : Test failed, the job is still running... \item Reserve resource (oarsub -I), and after first node connection, standby the server, and disconnect, WITH detach : Test OK ! The job continues to run, and when the user quits, the new oar-server success to kill the job, properly ! If the job is ended before the backup is up, the job is not deleted immediately, but after a few time. In the job events, we can see : SWITCH\_INTO\_TERMINATE\_STATE:[bipbip 4] Ask to change the job state. After that, the job is killed. In oarexec code, function quit\_oarexec loops until the daemon has informed the server. \item Reserve resource (oarsub -I), reserve a new resource again. The second user waits because there is only one resource. Standby the server. The first job continues to run, the second always waits. When the first job is ended, the second job begins ! It's very interesting, the job start it's scheduling on the master oar server, and it is authorized by the backup oar server. \item Execute oarstat, standby the master node, and execute oarstat : Test OK, the commands work, it print the same results. If we launch the command during the fail-over, the command inform users that it can't connect to the database, wait few seconds... \end{itemize} \subsection{4 nodes - Postgres} \begin{itemize} \item Migration test (master oar-server) : Test OK \item Migration test (master Postgres) : Test OK \item Power off/power on the master postgres server (IPMI kareboot) : Test OK, migration after a timer \item Power off/power on the master oar server (IPMI kareboot) : Test OK \item Power off/power on the backup oar server (IPMI kareboot), with resources : Test OK \item Power off/power on the backup postgres server (IPMI kareboot), without resources : Test OK \item \textit{killall -9 Almighty} on oar-server : Test OK \item \textit{killall -9 postgres} : Test OK \end{itemize} \textbf{Software test} : 4 servers (with postgres), 1 frontend, 1 node (resource). All commands are typed from the frontend. \begin{itemize} \item execute oarnodes, standby the oar master, and retry oarnodes : Test OK \item execute oarnodes, standby the postgresql master, and retry oarnodes : Test OK \item execute oarstat, standby the oar master, and retry oarnodes : Test OK \item execute oarstat, standby the postgres master, and retry oarnodes : Test OK \item Reserve resource (oarsub -I), and after first node connection, standby the oar-server, and disconnect, WITHOUT detach : Test failed. The job is not ended. \item Reserve resource (oarsub -I), and after first node connection, standby the oar-server, and disconnect, WITH detach : Test OK, the job is deleted when the user exits. \item Reserve resource (oarsub -I), and after first node connection, standby the postgres server, and disconnect, WITH detach : Test OK, the job is deleted when the user exits. \item Reserve resource (oarsub -I), reserve a new resource again. The second user waits because there is only one resource. Standby the oar-server. The first job continues to run, the second always waits. When the first job is ended, the second job begins ! It's very interesting, the job start it's scheduling on the master oar server, and it is authorized by the backup oar server. Test OK. \end{itemize} \subsection{4 nodes - Mysql} \begin{itemize} \item Migration test (master oar-server) : Test OK \item Migration test (master Mysql) : Test OK \item Power off/power on the master oar server (IPMI kareboot) : Test OK. After node reboot, resources returns on the master. \item Power off/power on the master mysql server (IPMI kareboot) : Test OK. \item Power off/power on both master mysql and oar server (IPMI kareboot) : Test OK. Don't forget to disable quorum policy ! \item Power off/power on both master mysql and oar server + backup DB (IPMI kareboot) : Test OK, oar services continue to run to backup. the database is launched when the first DB node comes back. \item Power off/power on backup mysql (IPMI kareboot) : Test OK. After node reboot, standby the primary to check if everything is OK. \item Power off/power on 4 nodes : Test OK. \item \textit{killall -9 Almighty} on oar-server : Test OK, resource is relaunched on the same server. \item \textit{killall -9 mysqld} : Test OK \item \textit{killall -9 mysqld} + unmount mysql partition (/mnt/drbddata) : Test OK, heartbeat detects filesystem error, relaunches it, and restarts mysql. \item \textit{drbdadmn disconnect all} : Test failed, heartbeat doesn't detect problems. \end{itemize} \textbf{Software test} : 4 servers (with mysql), 1 frontend, 1 node (resource). All commands are typed from the frontend.\\ Same results as Postgres with 4 nodes configurations. \appendix \chapter{DRBD configuration example} \label{drbdexample} \begin{lstlisting} global { usage-count no; } resource mysql { protocol C; startup { wfc-timeout 120; } disk { on-io-error detach; } syncer { rate 700000K; al-extents 257; } on node1{ device DRBDPARTITION; disk DISKPARTITION; address node2:DBRDPORT; meta-disk internal; } on node2{ device DRBDPARTITION; disk DISKPARTITION; address node1:DBRDPORT; meta-disk internal; } net { after-sb-0pri discard-younger-primary; after-sb-1pri consensus; after-sb-2pri call-pri-lost-after-sb; } handlers { split-brain "echo Splitbraindetected >> /var/log/drbd-log"; pri-lost-after-sb "echo pri-lost-after-sb >> /var/log/drbd-log"; } } \end{lstlisting} \chapter{Heartbeat configuration example on Debian} \label{cibexample} \section {High Availability on OAR with 2 nodes configuration, postgresql database} \begin{lstlisting}[language=xml] \end{lstlisting} \section {High Availability on OAR with 4 nodes configuration} \begin{lstlisting}[language=xml] \end{lstlisting} \end{document} ./oar-2.5.2/misc/db_converter.rb0000644000175000017500000004167711757171206014534 0ustar plbplb#!/usr/bin/ruby # $Id$ # db_converter is a simple oar converter from 1.6 table schema to 2.0 # ##################################################### # # USAGE # ##################################################### # # 0) MAKE A DUMP OF DATABASES # 1) adapt configuration below # 2) run: ruby ./db_converter.rb # # Note: don't care to message "/usr/lib/ruby/1.8/dbi/sql.rb:60: warning: 2 digits year is used" is not important # # author: auguste@imag.fr # # requirements: # ruby1.8 (or greater) # libdbi-ruby # libdbd-mysql-ruby ##################################################### # # CONFIGURATION # ##################################################### $cluster_size = nil $cluster_regexp = nil #base source oar-1.6 #$oar_db_1 = 'oar-1-6-grillon-grelon' #$oar_db_1 = 'oar-1-6-nancy' $oar_db_1 = 'oar-1-6-toulouse' $host_1 = 'localhost' $login_1 = 'root' $passwd_1 = '' #base source oar-2 #$oar_db_2 = 'oar-2-nancy' $oar_db_2 = 'oar2test' $host_2 = 'localhost' $login_2 = 'root' $passwd_2 = '' ### ### NANCY ### $cluster = ['violette'] #cluster propertie (cluster field in resource table) $nb_cpu = [2] #number of cpu by node $nb_core = [1] #number of core by cpu ### ### NANCY ### #$cluster = ['grillon','grelon'] #cluster propertie (cluster field in resource table) #$cluster_regexp= ['^grillon.*','^grelon.*'] #$nb_cpu = [2,2] #number of cpu by node #$nb_core = [1,2] #number of core by cpu ### ### Cluster with contiguous set nodes contigus nodes can use $cluster_size directly ### #$cluster = ['grillon','grelon'] #cluster propertie (cluster field in resource table) #$cluster_size = [47,120] #$nb_cpu = [2,2] #number of cpu by node #$nb_core = [1,2] #number of core by cpu ### ### Grenoble/Icluster2 ### #$cluster = ['icluster2'] #$cluster_size= nil #$nb_cpu = [2] #number of cpu by node #$nb_core = [1] ### ### Others: ... ### ############################################################################################## $scaling_weight_factor = nil #$scaling_weight_factor equal to (nb_core * nb_cpu) / maxweight $res_id = nil #intial index for resource_id $cpu = nil #initial index for cpu field $core = nil #initial index for core field $job_id_offset = nil #job_id_offset is add to oar_1.6's job_id to give oar_2's job_id one $event_id_offset = nil $empty = true #if true flush modified oar.v2 tables before convertion #$empty = true # MUST BE SET TO false (for development/testing purpose) $resource_cluster = {} ##################################################### # # NOTES: # # Add core in resources table en update cpuset accordingly # All resources must be free in V1.6 database (for simplication purpose). # # TABLES v2 modification # modified # ******** # assigned_resources # jobs # job_resource_descriptions # job_resource_groups # job_types # job_state_logs # moldable_job_descriptions # resources # resource_logs # frag_jobs # event_logs (QUESTION: Est-ce que tout les events v1.6 sont inclus ds l'ensemble des events v2.0 !!) # event_log_hostnames # # not addressed # ************* # accounting # admission_rules # challenges # files # gantt_jobs_predictions # gantt_jobs_predictions_visu # gantt_jobs_resources # gantt_jobs_resources_visu # job_dependencies # # TABLES 1.6 # # used # ************* # # event_log # event_log_hosts # fragJobs # jobs # jobState_log # nodes # nodeState_log # processJobs # processJobs_log # not used # ************* # accounting # admissionRules # files # ganttJobsNodes # ganttJobsNodes_visu # ganttJobsPrediction # ganttJobsPrediction_visu # nodeProperties # nodeProperties_log # queue ############################################# require 'dbi' require 'time' require 'optparse' require 'pp' def to_unix_time(time) year, month, day, hour, minute, sec = time.to_s.split(/ |:|-/) unix_sec = Time.local(year, month, day, hour, minute, sec).to_i return unix_sec end def hmstos(hms) h,m,s = hms.to_s.split(/:/) return 3600*h.to_i + 60*m.to_i + s.to_i end def base_connect(dbname_host,login,passwd) return DBI.connect("dbi:Mysql:#{dbname_host}", login,passwd) end def list_resources1(dbh) puts "Get resources (nodes) information from oar 1.6 db" q = "SELECT * FROM nodes" return dbh.select_all(q) end def determine_scaling_weight_factor(dbh) if ($scaling_weight_factor.nil?) $scaling_weight_factor = [] q = "SELECT MAX(maxWeight) FROM `nodes`" max_w = dbh.select_all(q).first.first.to_i $nb_core.length.times do |i| $scaling_weight_factor[i] = ($nb_core[i] * $nb_cpu[i]) / max_w puts "Cluster: #{$cluster[i]} scaling_weight_factor:#{$scaling_weight_factor[i]} = cpu: #{$nb_cpu[i]} * core:#{$nb_core[i]} maxWeight: #{max_w} " end end end def get_all_job_id1(dbh) q = "SELECT idJob FROM jobs" res = dbh.execute(q) all_job_id1 = [] res.each do |id| all_job_id1 << id.first end res.finish return all_job_id1 end def get_job_info1(dbh,job_id) q = "SELECT * FROM jobs WHERE idJob=#{job_id}" puts q row = nil begin row = dbh.select_one(q) rescue pp row puts "yop" end return row end def get_job_info1_mod(dbh,job_id) q = "SELECT `idJob`, `jobType`, `infoType`, `state`, `reservation`, `message`, `user`, `nbNodes`, `weight`, `command`, `bpid`, `queueName`, `maxTime`, `properties`, `launchingDirectory`, `submissionTime`, `idFile`, `accounted`, `checkpoint`, `autoCheckpointed` FROM jobs WHERE idJob=#{job_id}" sth = dbh.execute(q) row = sth.fetch_hash sth.finish begin res = dbh.select_one("SELECT `startTime` FROM jobs WHERE idJob=#{job_id}") row['startTime'] = res['startTime'] rescue # puts "Warning startTime = 0000-00-00 00:00:00 for job: #{job_id} ?: " + $! row['startTime'] = nil end begin res = dbh.select_one("SELECT `stopTime` FROM jobs WHERE idJob=#{job_id}") row['stopTime'] = res['stopTime'] rescue # puts "Warning stopTime = 0000-00-00 00:00:00 for job: #{job_id} ? : " + $! row['stopTime'] = nil end return row end def get_resources_job1(dbh,job_id) # puts "Get resources (nodes) affected to #{job_id} jobfrom oar 1.6 db" q = "SELECT hostname FROM processJobs_log WHERE idJob=#{job_id}" res = dbh.execute(q) resources = [] res.each do |r| resources << r.first end res.finish return resources end def get_resources_log1(dbh) puts "Get resources (nodes) log information from oar 1.6 db" q = "SELECT * FROM nodeState_log" return dbh.select_all(q) end def add_core_cluster_fields2(dbh2) puts "Add core and cluster fields to resource table on oar2 db" begin dbh2.do("ALTER TABLE `resources` ADD `core` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `cpu`") rescue puts "Core field exits in resource table ? :" + $! end begin dbh2.do("ALTER TABLE `resources` ADD `cluster` VARCHAR(50) AFTER `suspended_jobs`") rescue puts "Cluster field exits field in resource table ? :" + $! end end def insert_resources2(dbh,resources) puts "Insert resources" if ($res_id.nil?) q = "SELECT MAX(resource_id) FROM `resources`" max_resource_id= dbh.select_all(q).first.first.to_i if (max_resource_id == 0) $res_id = 1 $core = 1 $cpu = 1 else q = "SELECT `cpu`, `core` FROM `resources` WHERE resource_id=#{max_resource_id}" cpu, core = dbh.select_all(q).first $res_id = max_resource_id + 1 $core = core + 1 $cpu = cpu + 1 end end if ($cluster_size.nil?) $cluster_size=[resources.length] end r_id = $res_id resources_conv = {} resource_index_begin =0 $cluster.each_with_index do |cluster,index_cluster| resources[resource_index_begin..(resource_index_begin + $cluster_size[index_cluster]-1) ].each do |res| resources_conv[res.first] = [] $resource_cluster[res.first] = index_cluster i = 0 $nb_cpu[index_cluster].times do |cp| $nb_core[index_cluster].times do |co| begin dbh.do("INSERT INTO `resources` ( `resource_id` , `network_address` , `cluster`, `cpu` , `core` , `cpuset`) VALUES ('#{r_id}', '#{res.first}', '#{cluster}', '#{$cpu}','#{$core}','#{i}')") rescue puts "Unable to INSERT resource: " + $! exit end resources_conv[res.first] << r_id $core += 1 i += 1 r_id += 1 end $cpu += 1 end end resource_index_begin = resource_index_begin + $cluster_size[index_cluster] end p resources_conv return resources_conv end def insert_resource_logs2(dbh,resources_log1,res_conv) puts "Insert resources log" resources_log1.each do |res_log| p res_log.first node = res_conv[res_log.first] p node node.each do |res_id| date_stop = "0" begin date_stop = to_unix_time(res_log['dateStop']) if res_log['dateStop'].class != NilClass dbh.do("INSERT INTO `resource_logs` (`resource_id` , `attribute` , `value` , `date_start` , `date_stop` , `finaud_decision` ) VALUES ('#{res_id}','state','#{res_log['changeState']}', '#{to_unix_time(res_log['dateStart'])}', '#{date_stop}','#{res_log['finaudDecision']}')") rescue puts "WARNING: Failed to insert resource logs: " + $! end end end end def determine_job_id_offset(dbh) if ($job_id_offset.nil?) q = "SELECT MAX(job_id) FROM `jobs`" $job_id_offset = dbh.select_all(q).first.first.to_i end puts "job_id_offset = #{$job_id_offset}" end def determine_event_id_offset(dbh) if ($event_id_offset.nil?) q = "SELECT MAX(event_id) FROM `event_logs`" $event_id_offset = dbh.select_all(q).first.first.to_i end puts "event_id_offset = #{$event_id_offset}" end def insert_job2(dbh,job,res_conv, assigned_resources) job_id2 = job['idJob'].to_i + $job_id_offset begin dbh.do("INSERT INTO `moldable_job_descriptions` ( `moldable_id` , `moldable_job_id` , `moldable_walltime` , `moldable_index` ) VALUES ( '#{job_id2}', '#{job_id2}', '#{hmstos(job['maxTime'])}', 'LOG')") rescue puts "Failed to insert moldable job descriptions: " + $! exit end begin dbh.do("INSERT INTO `job_resource_groups` ( `res_group_id` , `res_group_moldable_id` , `res_group_property` , `res_group_index` ) VALUES (?, ?, ?, ?)", job_id2, job_id2 , "type = 'default'" ,'LOG') rescue puts "Failed to insert job resource groups: " + $! exit end message = "" start_time = 0 stop_time = 0 begin message = job['message'] if job['message'].class != NilClass start_time = to_unix_time(job['startTime']) if job['startTime'].class != NilClass stop_time = to_unix_time(job['stopTime']) if job['stopTime'].class != NilClass dbh.do("INSERT INTO `jobs` ( `job_id` , `job_name`, `job_type` , `info_type` , `state` , `reservation` , `message` , `job_user` , `command`, `queue_name` , `properties` , `launching_directory` , `submission_time` , `start_time` , `stop_time` , `file_id` , `accounted` , `assigned_moldable_job` , `checkpoint`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", job_id2, 'converted' , job['jobType'], job['infoType'], job['state'], job['reservation'], message, job['user'], job['command'], job['queueName'], job['properties'], job['launchingDirectory'], to_unix_time(job['submissionTime']), start_time, stop_time, job['idFile'], job['accounted'], job_id2, job['checkpoint'] ) rescue puts "Failed to insert job: " + $! exit end if (job['queueName'] == "deploy" || job['queueName'] == "besteffort") begin dbh.do("INSERT INTO `job_types` ( `job_id` , `type` , `types_index` ) VALUES ('#{job_id2}', '#{job['queueName']}', 'LOG')") rescue puts "Failed to insert job types: " + $! exit end end # job_state_logs #insert assigned resources nb_res = 0 assigned_resources.each do |node| if ($resource_cluster[node].nil?) puts "WARNING: node: #{node} is unkwon" else # puts $scaling_weight_factor[$resource_cluster[node]],"node",node,$resource_cluster[node],"yop",job['weight'] ($scaling_weight_factor[$resource_cluster[node]]*job['weight'].to_i).times do |i| begin dbh.do("INSERT INTO `assigned_resources` ( `moldable_job_id` , `resource_id` , `assigned_resource_index` ) VALUES ('#{job_id2}', '#{res_conv[node][i]}', 'LOG')") nb_res = nb_res + 1 #puts "job_id: #{job_id2} res_id:#{res_conv[node][i]} i:#{i}" rescue puts "WARNINIG:Failed to insert assigned resources: " + $! end end end end begin dbh.do("INSERT INTO `job_resource_descriptions` ( `res_job_group_id` , `res_job_resource_type` , `res_job_value` , `res_job_order`, `res_job_index`) VALUES ('#{job_id2}', 'core', '#{nb_res}', '0', 'LOG')") rescue puts "WARNING:Failed to insert job resource descriptions: " + $! end end def convert_job_state_logs(dbh1,dbh2) puts "Convert job state logs" sth = dbh1.execute("SELECT * FROM jobState_log") sth.each do |row| job_id2 = row['jobId'].to_i + $job_id_offset date_stop = "0" begin date_stop = to_unix_time(row['dateStop']) if row['dateStop'].class != NilClass dbh2.do(" INSERT INTO `job_state_logs` (`job_id` , `job_state` , `date_start` , `date_stop` ) VALUES ('#{job_id2}','#{row['jobState']}','#{to_unix_time(row['dateStart'])}','#{date_stop}')") rescue puts "WARNING:Unable to INSERT job state logs: " + $! end end sth.finish end def convert_frag_jobs(dbh1,dbh2) puts "Convert frag jobs" sth = dbh1.execute("SELECT * FROM fragJobs") sth.each do |row| job_id2 = row['fragIdJob'].to_i + $job_id_offset begin dbh2.do("INSERT INTO `frag_jobs` ( `frag_id_job` , `frag_date` , `frag_state` ) VALUES ('#{job_id2}','#{to_unix_time(row['fragDate'])}','#{row['fragState']}')") rescue puts "WARNING:Unable to INSERT frag jobs: " + $! end end sth.finish end def convert_event_logs(dbh1,dbh2) puts "Convert event_logs" sth = dbh1.execute("SELECT * FROM event_log") sth.each do |row| job_id2 = row['idJob'].to_i + $job_id_offset event_id2 = row['idEvent'].to_i + $event_id_offset begin dbh2.do("INSERT INTO `event_logs` ( `event_id` , `type` , `job_id` , `date` , `description` , `to_check` ) VALUES (?, ?, ?, ?, ?, ?)", event_id2, row['type'], job_id2, to_unix_time(row['date']), row['description'], row['toCheck'] ) rescue puts "WARNING: Unable to INSERT event logs: " + $! end end sth.finish end def convert_event_log_hostnames(dbh1,dbh2) puts "Convert event log hostnames" sth = dbh1.execute("SELECT * FROM event_log_hosts") sth.each do |row| event_id2 = row['idEvent'].to_i + $event_id_offset begin dbh2.do("INSERT INTO `event_log_hostnames` ( `event_id` , `hostname` ) VALUES ('#{event_id2}','#{row['hostname']}')") rescue puts "WARNING: Unable to INSERT event log hostnames: " + $! end end sth.finish end def reorder_resources(resources) puts "reorder resources" $cluster_size = [] ordered_resources = [] $cluster_regexp.each_with_index do |str_regexp,i| regexp = Regexp.new(str_regexp) nb_nodes = 0 resources.each do |res| if (regexp =~ res.first) ordered_resources << res nb_nodes = nb_nodes + 1 end end $cluster_size[i] = nb_nodes puts "cluster #{$cluster[i]} has #{$cluster_size[i]} nodes" end return ordered_resources end def empty_db(dbh,name) puts "\nEMPTY some tables of #{name} database (oar v2.0) !!" puts puts "resources, resource_logs, moldable_job_descriptions, job_resource_descriptions, job_resource_groups, jobs, job_types, assigned_resources, job_state_logs, frag_jobs, event_logs, event_log_hostnames" sleep 5 dbh.do("TRUNCATE TABLE `resources`") dbh.do("TRUNCATE TABLE `resource_logs`") dbh.do("TRUNCATE TABLE `moldable_job_descriptions`") dbh.do("TRUNCATE TABLE `job_resource_descriptions`") dbh.do("TRUNCATE TABLE `job_resource_groups`") dbh.do("TRUNCATE TABLE `jobs`") dbh.do("TRUNCATE TABLE `job_types`") dbh.do("TRUNCATE TABLE `assigned_resources`") dbh.do("TRUNCATE TABLE `job_state_logs`") dbh.do("TRUNCATE TABLE `frag_jobs`") dbh.do("TRUNCATE TABLE `event_logs`") dbh.do("TRUNCATE TABLE `event_log_hostnames`") end ############################################# puts "\ndb_converter oar v1.6 to v2.0:" puts " See the begin of this script file for configuration details" puts dbh1 = base_connect("#{$oar_db_1}:#{$host_1}",$login_1,$passwd_1) dbh2 = base_connect("#{$oar_db_2}:#{$host_2}",$login_2,$passwd_2) empty_db(dbh2,$oar_db_2) if $empty # Get resources resources = list_resources1(dbh1) resources = reorder_resources(resources) if !$cluster_regexp.nil? determine_scaling_weight_factor(dbh1) # Add core and cluster fields add_core_cluster_fields2(dbh2) # Insert resources res_conv2 = insert_resources2(dbh2,resources) insert_resource_logs2(dbh2,get_resources_log1(dbh1),res_conv2) determine_job_id_offset(dbh2) #get_all_job_id1 all_job_id1 = get_all_job_id1(dbh1) nb_jobs = all_job_id1.size puts "Convert jobs..." all_job_id1.each_with_index do |job_id,i| # job_info = all_job_id1.first #puts "#{i} jobs processed" if (i % 100) == 0 STDERR.print sprintf(" %d / %d jobs processed\r",i+1,nb_jobs) if (((i % 10) == 0) || (i+1 == nb_jobs)) job_info = get_job_info1_mod(dbh1,job_id) assigned_resources = get_resources_job1(dbh1,job_id) insert_job2(dbh2,job_info, res_conv2, assigned_resources) end puts convert_job_state_logs(dbh1,dbh2) convert_frag_jobs(dbh1,dbh2) determine_event_id_offset(dbh2) convert_event_logs(dbh1,dbh2) convert_event_log_hostnames(dbh1,dbh2) puts "\nConvertion is terminated" puts ./oar-2.5.2/misc/apt_keyring/0000755000175000017500000000000011757171206014030 5ustar plbplb./oar-2.5.2/misc/apt_keyring/oarmaster.gpg0000644000175000017500000000610611757171206016527 0ustar plbplb-----BEGIN PGP PUBLIC KEY BLOCK----- Version: GnuPG v1.4.10 (GNU/Linux) mQINBE2IcXUBEADlMaMuFeNzDGlVM03EJaxgCi9kQLLSP3UW/u+PpT190JbKy6f5 41mnI4Z5vvs/PwLlpzxVjrwo/k9aJqbkiZhxrS3KmTWdAneG56AitM6V6Jx+jq3z 1AGu/W4HDNxocUnZEsm0IRoyHL9gz4n8B8C5Iohoc4mbvU0CrKarym6sUDAbALEQ 2fKNXSZPKYe6gkdapJke4lzvNrCILOk7Gwxt9lmi9QGpORB04YQ4mZW6x5nyCTxS 7OXjgo7EWxLHwNancDI40MgvbXDC2urNxrRgpsQhlK5btwnniCI0v9R7mj5pOjn2 QguvicRptaXXVWk6aaBDuSyA5R6ZSFFKYqZ6OA6IUHtHhcWULXmGCqp6jr3vshiC 5aqRNTmvV161CQKla/xMEHLyPURN3NSv/OIGtGAMgkh+FX5uvI/+JWT3fjruMBnq TcvlXvBX9+PiZcnn0OTn/mESVYM4ItP9tzpp5YlhSJsBoU09oMPHm/+ZKcR8uHY4 70x/ZvHBxIoa6msjkLEgme59qMv0SbFYFGTdWYseHXJdFDGPDk9ECIHgqKjrm5Aq mI7utRzeYb0z/NyaE87WAcydBg96J5JdUt/XST7VxrrCcUjYwHLN1xHXtTubTy6a i4ffoAea15NgxkEc31vcHn0NIiPlScgolKOmiHRdAV3TzKk78oRAQLYClQARAQAB tDxPQVIgQXJjaGl2ZSBBdXRvbWF0aWMgU2lnbmluZyBLZXkgKDIwMTEpIDxvYXJt YXN0ZXJAaW1hZy5mcj6JAjcEEwEIACEFAk2IcXUCGwMFCwkIBwMFFQoJCAsFFgID AQACHgECF4AACgkQZAy1PtkNBWhrqBAAnjTxsdhNXPwDQz/tqf4t63Ug4G8rkfBD QEqp3lue6xowV5n92I77XFM/lJH7zD8ktwWv0pOSHm+Fm3nGvvRtif7VNw6pHUtc Nr8jvA8OWqaBM65ZiRQxAFZL+X2bi6o03rSaDz6w6wOp8YW7jglFNp2zduqLbwda ToOEDTcgFirqEXPEPPCY4gsMp0AbBQUaILJJcYSSvU2oWEx7X+Coc3mtYA1vyNtT 77RhkhkQFKsHHXHs3BF6jtYSBJ1D8GuzJu1DclAyOADxA3dBPhM4/6fTWuQ787uL +33lDBFPzQkKco2u5MtFr4Hi/EysPpr6Ish6V8DOq2isl1mq/4ZLcGv5pDlKHitC LodmKDTayFKBkq1qJTmcBzVTP0g19TE3XIfUzjRXKyo9qrVH52g4EpQy3Dt2FCuK 6RVxzXWLNdFyZLFNXLRJoEabVAkJsXFZxp2THitN7HDpHtn801uWq7MnGLv0tsmZ E76IOX9jt9MGijRr89bUEApVnpoKhu+ZgzW2HJZPtybyuzC1UeMi9PXynBinISi0 byg9qhQxk+C1xgv+39lBOTLY7DL11T+K8BmmxJgKFHzU2VM6P0m/EtecE97UyG+P uq6aYBOIMA26UEJt1lPWvvf2dERtSNMFbdwlVEG3lb2CLeeApDR6u79Cc1p7vjdO WIc2ldCmpba5Ag0ETYhxdQEQANfdNXi6zHLUm+wL8OJNheSvzC56vvkARcv73r+t pepHrL8FcqxktuK5rZrbESxuzqZN3IJH/HON/27cdb/ZNLeQqZhWg1jHt68XBvH1 razVs+6OtoyjbdiQnQoVT3gO5HSWOT0x6NJdTGDwqTf6eCTh52zcBOJ0VZBbXPI2 v0C3tsK8C2Yja/JhPFcawaQEAxQ/e6hv0/CpmVojfot+zGMIJ2CiKqZGYGO8Qc7l EdsN5sjcLr+uubon7hbZdsli4YTWHHLwWlRQyEzLCjVSyreb+T0MPKZ5xy/Co744 tWi00K4ToMhN5YmKNdgdlJhpr7o9DveJ8Ym0uw9i8kmhPa95wx+32M4N8oT0josU zp1eum5MafcEqPrX/jygHPGSuXN/dsZEx7zLqEW2g8QY0h2OUcQ0lu+5LsuekuB1 /jTI3o9bokE9Ab4YKHamQF8w57t2FzoLrU9RuhoFRbj7ZV0d99pICL8W5+I+YJQ4 xlMqWiqepyS3yelFfNIUEG2ExdVdayNLEbZ+xgGKEyoD7OCnrDwtbM3dXRpPsnaT 9IKC1ZcFBiwm2vNsHzEoEJg5BMNmFMMvwE17m8EpF3gy3J9ut8ircPuqeyCmlxmP Lej5stbBumFH3gKKvlE+jBi/RaouM4agbtxZTkohghzi5MDEBdGdGvjpI+apw8ZT uLwbABEBAAGJAh8EGAEIAAkFAk2IcXUCGwwACgkQZAy1PtkNBWgK7A/9EqmDpvJp /rUVxZi1O8GHp/teW18uRUuk9PI6vxgzC6JX7j4q/Lc2zIrNQ/G3hcERQuPIT5K7 wj5t1WkwLuRljoIz+J5fWNkXd/RfcyXmMQT4FJ6fFkheKL3EFZiZF6/ge88DVty6 bUhT7GZr/Ry1Ne9Nf+52xIQwv1WUfVtyh68pwCdFNuZtZmn0NasxwosjQqsH4Yl7 z1FP++1Adsj77X2qIdmd95iS8ic66BSW/XmptUOV84kEZD7CpAzXPJProYek4bX7 8nsfu9NDXws/EXzw/GCe+aBcn8Qtwf1hX5+POHGi4Z2kcWbHERGAUatxcAUsbqQD FvhmMZfdDBHdVdagtKSGaBn9t36IBA6Rdmn7/7nsKtjpy7tYzob24ympBWNF8+95 +dGwf6IYTEpLbo2AEh2ZNzAHOTJUXOXK4CZNeqT4k35PEfvvl0GLrbn+EiBzj/Lw NpbGfZo+wT15yk9sNtjr41i9brLwl6KyhMXEDpn5w6vKMm9aLCocP+5Mh8RLZTOt l4w7QIVKbXzAfODFLb3mGD27e0br8DL50oJXj/V6pflDXSixS+8563fTfKdXpviz SVhfZVV5CSc93rpHOOWZrwTsirKYd9M6cchRYUYfg5astjeVA+CmOJ67gip6YgVO KHONJnOAH3vckqTcCzu85YND1e9cyjdZC+o= =G6T8 -----END PGP PUBLIC KEY BLOCK----- ./oar-2.5.2/misc/apt_keyring/oar.gpg0000644000175000017500000001074611757171206015320 0ustar plbplbC7&.D݋y9dh‹i kLj"T8E[OtbL "eY$|J7ILr!PDs.6_ bЩ.Ed J5mo~=|=ka(F^AW{JD*VcER*kxY+(ď )9,b$)|*}N:R^uq/YC^ T(ݍO|wAK/I]Td(fVu-de/U{Hs$6Wre GcWsdcG{+AsqqsSۆRxhC5lj󆚄I2D(lT8hե_A!ۦ {qV|Yk2Bruno Bzeznik (UJF - IMAG) t4 hkp://pgp.mit.eduG  (!FR+YGZ*7!c?=J[M }d4Ф~s2^D  (!FRzxzzk{k:^y+רKd ؈#LFEu 4&65ˀ @,5Ovj0T0p 躨s^!ܰFE H b7;?O7 !\)5!!fjWkia8K q1 Evhkp://pgp.mit.edu (!FR$n{{$4X >E, d?VܢxG;Bruno Bzeznik (Academie de Grenoble) I0 G  (!FR"zbh7S<ʰL ܑjE˝{G|`߰q1 Evhkp://pgp.mit.edu (!FRAei2FCT Y2Btr+ \{#Qu w?Ә-$@zFC TwTA TJSbVqAܯtylzNxfP=FC Qنda2\!}'٤Xh2s=:AmgdFC# niҠ.o]%1HY6@XemB_%Kp>FC!  `7ՍnHE,t5tyjoa ‹ċ8FC =-,Qyiz60sVFEOvP)H\%fTS!w9>NM_&FC Q T"TcOpnLkTY.2CJlLSRi~rZ:#FCJ~ dk^OMPYTӿaš 5BШ L¯-%1^C  (!FR 8z/z J[{~ Y,m@QFEu 4&6 v[]| ] ٬ ǓIr,(Bruno Bzeznik (Perso) q1 Evhkp://pgp.mit.edu (!FRzHrDru5 3mUL}f7m}V6o^lx^D  (!FRҘjTpB4K;q]"Fsw -gsFEu 4&6 wi?Y~<E' L?\$P|Uo̰1Bruno Bzeznik (Association SLIS) q1 Evhkp://pgp.mit.edu (!FRޔ+4w dg`جD8RJH|ڍFC wCٟΜu7Uo,~uoĠ̮Xa°FC[ Y2Bt졫{1//uQ`œ۴G4FC TwTAI( ;:ԏӄzbӇ+2To zv.FC QنdaMvm i9Cu"6`i} 9OdyiZFC& ni=`  `㊇v'2bh ][lAFC!"  `7a+UO+yQ 3jٯȣ$ϵΞ(JFC =-6^31F<MMٷ/ ӹN[ͰFC ħfj({~J(cQK;$vf%sڻy<>TFC r=p7+!nq";%;I<8o}FC e T"TcNPSQޟ@  diHGZަFCJ dk$4EЄ8@{|!  rQ0ׂ$̛#FCT Y2Btr+ \{#Qu w?Ә-$@z^C  (!FRۇPc/Yֻ\RB`=`DC7FEu 4&6D-GQ߬:~YRޏ'B!M17DgGU3?,x%Bruno Bzeznik b"KY   (!FR8%wgSE=3G1AM܊~ZU7E C>`qC>n:Ǫgqm7XH͌fc Z9bu<>@:!jjJG@h [ /BE&{\cJc! ɵf֫rWOH;Ru*wF] $l..0$5ˎ!5sz$":AwMI@CcHDcʭ&OE[ Ic`W_-(k.YumCnȵgO1CCX,-O{⪟X?KcZh&%B۹ܩUC M"R(k0(9s0h^D@Y:ZǢQ0I31]4%+%_.|ASsnԟgG8ƋV7:xB# 8 , ONʥA_f KTߒFz*|z3cvKkWFI C (!FRW ʖ帉@1M>p* 'i~3aVI./oar-2.5.2/misc/appliance_setup/0000755000175000017500000000000011757171206014670 5ustar plbplb./oar-2.5.2/misc/appliance_setup/resources/0000755000175000017500000000000011757171206016702 5ustar plbplb./oar-2.5.2/misc/appliance_setup/resources/oar_resources_setup_as_normal0000777000175000017500000000000011757171206031610 2oar_resources_setup_as_ustar plbplb./oar-2.5.2/misc/appliance_setup/resources/oar_resources_setup_as_dummy0000777000175000017500000000000011757171206031453 2oar_resources_setup_as_ustar plbplb./oar-2.5.2/misc/appliance_setup/resources/oar_resources_setup_as_0000755000175000017500000000504411757171206023550 0ustar plbplb#! /bin/sh #set -x # The type of host. Could be : # - server # - frontend # - node SCRIPT_PREFIX=oar_resources_setup_as_ SCRIPT_BASENAME=$(basename $0) TYPE=${SCRIPT_BASENAME##$SCRIPT_PREFIX} THIS_HOSTNAME=$1 shift # The first node is the server SERVER_HOSTNAME=$1 shift # The second node is the frontend FRONTEND_HOSTNAME=$1 shift # the other nodes are compute nodes. NODE_HOSTNAME_LISTFILE=$1 shift CORE_FACTOR=$1 shift CPU_FACTOR=$1 shift openssh_cmd() { host=$1 shift cmd=$* su - oar -c "ssh -p 6667 $host $cmd" } # # Set oar with "normal ressources" # normal_resources_setup() { if [ ! -f "$NODE_HOSTNAME_LISTFILE" ]; then echo "Unable to read the node file list"; fi cat "$NODE_HOSTNAME_LISTFILE" > /tmp/compute_nodes oar_resources_init /tmp/compute_nodes << EOF yes yes EOF sh /tmp/oar_resources_init.txt } dummy_resources_setup() { if [ ! -f "$NODE_HOSTNAME_LISTFILE" ]; then echo "Unable to read the node file list"; fi tot_cpt_cpu=0 tot_cpt_core=0 tmp_res=$(mktemp) chown oar:oar $tmp_res PATH=/usr/local/sbin:/usr/sbin:$PATH oarproperty -a cpu oarproperty -a core oarproperty -c -a host oarproperty -a cpuset oarproperty -a mem echo -n "[" > $tmp_res for HOST in `cat $NODE_HOSTNAME_LISTFILE|sort -u` ; do CPUS=$(openssh_cmd $HOST lscpu -p | tail -n+5 | cut -d',' -f 3 | uniq | wc -l) TOTAL_CORES=$(openssh_cmd $HOST lscpu -p | tail -n+5 | cut -d',' -f 2 | uniq | wc -l) CORES=$(($TOTAL_CORES / $CPUS)) EMUL_CPUS=$(($CPUS * $CPU_FACTOR)) EMUL_CORES=$(($CORES * $CORE_FACTOR)) for cpt_cpu in $(seq `expr $tot_cpt_cpu` 1 `expr $EMUL_CPUS + $tot_cpt_cpu - 1`); do for cpt_core in $(seq `expr $tot_cpt_core` 1 `expr $EMUL_CORES + $tot_cpt_core - 1`); do echo -n "{\"hostname\":\"$HOST\", \"cpu\":\"$cpt_cpu\", \"core\":\"$cpt_core\", \"cpuset\":\"$(expr $cpt_core % $TOTAL_CORES)\"}," >> $tmp_res tot_cpt_core=`expr $tot_cpt_core + 1` done ; tot_cpt_cpu=`expr $tot_cpt_cpu + 1` done ; done sed -e 's/,$/]/' -i $tmp_res su - oar -c "curl -x '' -i http://$FRONTEND_HOSTNAME/oarapi/resources -H'Content-Type: application/json' -d @$tmp_res" > /dev/null } case $TYPE in normal) normal_resources_setup ;; dummy) dummy_resources_setup ;; *) echo "$0: the type $TYPE is invalid. It could be currently only 'normal'. Failing." exit 1 ;; esac ./oar-2.5.2/misc/appliance_setup/perf/0000755000175000017500000000000011757171206015624 5ustar plbplb./oar-2.5.2/misc/appliance_setup/perf/setup_nytprof0000755000175000017500000000102411757171206020470 0ustar plbplb#! /bin/sh export > /root/env.log mkdir -p /tmp/oar_profiling/ chmod 777 /tmp/oar_profiling/ cat >> /etc/default/oar-server << EOF NYTPROF=log=/tmp/oar_profiling/nytlog:trace=2:addpid=1:file=/tmp/oar_profiling/nytprof PERL5OPT=-d:NYTProf EOF cat >> /etc/environment << EOF NYTPROF=log=/tmp/oar_profiling/nytlog:trace=2:addpid=1:file=/tmp/oar_profiling/nytprof PERL5OPT=-d:NYTProf EOF cat >> /etc/bash.bashrc << EOF NYTPROF=log=/tmp/oar_profiling/nytlog:trace=2:addpid=1:file=/tmp/oar_profiling/nytprof PERL5OPT=-d:NYTProf EOF ./oar-2.5.2/misc/appliance_setup/hosts/0000755000175000017500000000000011757171206016030 5ustar plbplb./oar-2.5.2/misc/appliance_setup/hosts/oar_host_setup_as_server0000777000175000017500000000000011757171206026662 2oar_host_setup_as_ustar plbplb./oar-2.5.2/misc/appliance_setup/hosts/oar_host_setup_as_frontend0000777000175000017500000000000011757171206027173 2oar_host_setup_as_ustar plbplb./oar-2.5.2/misc/appliance_setup/hosts/oar_host_setup_as_compute0000777000175000017500000000000011757171206027030 2oar_host_setup_as_ustar plbplb./oar-2.5.2/misc/appliance_setup/hosts/oar_host_setup_as_0000755000175000017500000000520411757171206021637 0ustar plbplb#! /bin/sh #set -x # The type of host. Could be : # - server # - frontend # - node SCRIPT_PREFIX=oar_host_setup_as_ SCRIPT_BASENAME=$(basename $0) TYPE=${SCRIPT_BASENAME##$SCRIPT_PREFIX} THIS_HOSTNAME=$1 shift # The first node is the server SERVER_HOSTNAME=$1 shift # The second node is the frontend FRONTEND_HOSTNAME=$1 shift # the other nodes are compute nodes. NODE_HOSTNAME_LISTFILE=$1 shift configure_database() { sed -e "s/^DB_HOSTNAME=.*/DB_HOSTNAME=\"$SERVER_HOSTNAME\"/" -i /etc/oar/oar.conf sed -e "s/^SERVER_HOSTNAME=.*/SERVER_HOSTNAME=\"$SERVER_HOSTNAME\"/" -i /etc/oar/oar.conf } configure_hostname() { echo "$THIS_HOSTNAME" > /etc/hostname hostname $THIS_HOSTNAME IPV4=$(ip -o -f inet addr show scope global | awk '{print $4;};' | sed -e "s%/.*%%") echo "$IPV4 $THIS_HOSTNAME $(hostname -s)" >> /etc/hosts } configure_user () { adduser --disabled-password --gecos "user" user mkdir /home/user/.ssh touch /home/user/.ssh/authorized_keys chown -R user:user /home/user/.ssh chmod 700 /home/user/.ssh chmod 600 /home/user/.ssh/authorized_keys adduser user sudo cat /root/.ssh/authorized_keys >> /home/user/.ssh/authorized_keys } common_setup() { configure_hostname configure_user } server_setup() { service oar-server stop service mysql stop configure_database sed -e "s/^\(bind-address.*\)/#\1/" -i /etc/mysql/my.cnf update-rc.d oar-server enable update-rc.d mysql enable service mysql start sleep 3 oar-database --drop --db-admin-user root --db-host localhost || true oar-database --create --db-admin-user root --db-host localhost || true service oar-server start } frontend_setup() { service apache2 stop service oidentd stop configure_database sed -e "s/^hostname.*/hostname = $SERVER_HOSTNAME/" -i /etc/oar/monika.conf sed -e "s/^DB_HOSTNAME.*/DB_HOSTNAME: $SERVER_HOSTNAME/" -i /etc/oar/drawgantt.conf update-rc.d oidentd enable update-rc.d apache2 enable service oidentd start service apache2 start } node_setup() { service oar-node stop update-rc.d oar-node enable service oar-node start } case $TYPE in server) common_setup server_setup ;; frontend) common_setup frontend_setup ;; compute|node) TYPE=node common_setup node_setup ;; demo) common_setup server_setup frontend_setup node_setup ;; *) echo "$0: The type is not valid (could be server|frontend|node|demo)" exit 1 ;; esac touch /root/oar-$TYPE ./oar-2.5.2/docs/0000755000175000017500000000000011757171206011511 5ustar plbplb./oar-2.5.2/docs/schemas/0000755000175000017500000000000011757171206013134 5ustar plbplb./oar-2.5.2/docs/schemas/virtual_cluster_bind_views.txt0000644000175000017500000000205411757171206021336 0ustar plbplb# Bind configuration for OAR virtual cluster ### # Bind config sample view "subnet-1" { match-clients { 10.1.1.0/24; }; zone "oar.vpn" { type master; file "db.oar.vpn.subnet-1"; } } view "subnet-2" { match-clients { 10.1.2.0/24; }; zone "oar.vpn" { type master; file "db.oar.vpn.subnet-2"; } } view "subnet-3" { match-clients { 10.1.3.0/24; }; zone "oar.vpn" { type master; file "db.oar.vpn.subnet-3"; } } # Zone sample: db.oar.vpn.subnet-1 $ORIGIN . oar.vpn IN SOA ... $ORIGIN oar.vpn vnode-1-1 A 195.220.82.8 vnode-1-2 A 195.220.82.9 vnode-1-3 A 195.220.82.10 vnode-2-1 A 10.1.2.1 vnode-2-2 A 10.1.2.2 vnode-2-3 A 10.1.2.3 vnode-3-1 A 10.1.3.1 vnode-3-2 A 10.1.3.2 vnode-3-3 A 10.1.3.3 # Zone sample: db.oar.vpn.subnet-2 $ORIGIN . oar.vpn IN SOA ... $ORIGIN oar.vpn vnode-1-1 A 10.1.1.1 vnode-1-2 A 10.1.1.2 vnode-1-3 A 10.1.1.3 vnode-2-1 A 129.89.30.23 vnode-2-2 A 129.89.30.24 vnode-2-3 A 129.89.30.25 vnode-3-1 A 10.1.3.1 vnode-3-2 A 10.1.3.2 vnode-3-3 A 10.1.3.3 ./oar-2.5.2/docs/schemas/virtual_cluster_bind_views.dia0000644000175000017500000001305211757171206021254 0ustar plbplb]ms_G Bwƛ].Mes.Je^(RER{}fC l`{cQ?hyw?ζݯakm/,~@—${y~-W|jޮSb6]Rϳr6gӅW]ӂw8nW^z<=3OVP t֌00R'tyIRQ^҄@C4dX7<[.?˧~Cד"< PieLl~i[/$9T̈́m>|p*gj>S=_Jņ֊m%Ul[AwAIA 46 L4Nq|h3[ݗt/{<ʛ}RvytS7M_>N0Wd6!rzApB*yZ> z )`t!dt2M'*jc $Zm eINAxxeQ{bc=:8Nu*)Sx (mJD$4 O8ߥ1IL2|,ϭ =kpiQ! $n]q4pLɉN+3A^ztɥ\xI I` c]!@L'ӛ֊ B3@̭dV)$)*(wi θ0=eOaz}MWZD/ I䴞ڸh}sD'ѻzpKRZ$YHAA*;2`}Ҟ%Tۍqi?D4$NH @BS}Tk^Y{F_lc I$Ok*P}sxJ~0TŃxQue~\ؙ-p ?]L2s}\:!ʩPi!!VA YO5 Y~8,tӓ}Vf&)lmnAbV*%%DV繾9f[yD<ȩQZŶW ^m6^92sp0yy(;8kgY1p`#IVIlT-4ڵd/h%)#*);h@"c<_n+tZ$2]qiHOx31p*:^'y)(Ԩga) Pnjm$??LB,@n*.$2:p&۷d&^I;t\P#\{I%)h1h6owcf|LYͷG2rť<ς0AgQke^`NhZ\jB@jL]$ۛ 6m2tN6rɽyw%.pzEhkbr288uES_LRW<UUӶq<+:x#!1vP%aQtפ]ae" .}ژj\3eΣ U6ZbdJ5:*غb2:~\Y P ǪU$biI^;&AYM4NgMwwƒZ7U;(FVJ[o`.܌Fi!Z&Y}o'_ZhtcBBiuP*V`M/ WNr[,zjl!2 h+1JA(j\r=gN6TjMm$?JmֵN'm$08lƝP SN~G Flv>8ũ"M/|_٬>(JgH\h[|r%;Xcrn@F02E} ljM+^ep\R1W%Խ)hx06FtAîTM?7o a$Ek{}g]4&j/ȱ #~Qh4~z*;{ & AY7cmvh٭1XJ 6Ԥ mgvQ ꬯`5|(l=ʬ1Xr4XDVN_Ƚ"1XR:Ekwߘb1TmXi c+B 0 BxI g֭>*1.&1y=PF[kWPd j}>['6?NY 'Ԫ^N)(RAiT(ɣ;xW)57_>.goP/l~Q@FB6:s_ù.].tP. I K .$]fKJ(%h 9 3H+$ u$>\uu&piYVAҷW֠vzAسDV2~ ́˝n4am4r\%DCfwIuЂ*'+ [PGM4r_#OPS:jqZy7PGGjLV,ViЊ)rQ6m;I0l @kvA+h'ZigF!+Β <9asb!UA&jT;TQN53R Z'Ăl,nJy$FЂTq<>vU |H|#;QqP5.rz^UjP> E(a Sƪn$5z[@U^UujPIsU"+1ekMUyTթf!BU k6z$Uqk*d(lUu|{p:m=tŒ*2$&1q\ W=Uzf"\q+Q1K.q( ՠN,$*Ug!•P P$ a7+~ƉU%fs쪯Uzf"\uQYa-x'(.C i@+;RzªP5Dj_Y rFOVu.$0N#L)ٱZqFЪN=3\ U~)'9社=g-Ļiu&QIcUD8|2HQ{Oe@LjjM1sܶCv\=}>aBP0>faINn^օ@+Ph!1—=@uS;SheYÎZJAZh.˪PTh}%=jQwg}5~N,͚Re)X,H9#Ր\oxj2cfU*O?_{qRsɣ%NqqZI>1&.)m E8,ҍVġB;6Zn+W;R\ZۦB]=~5K}$ ?,fˋ4gupM̼ͭu>?ΌIć$PI $hVDZI4[Lr#λ/۲V+ʕ[b! ІsZ |IQ7BEwO!GR$Pi$ޕaHfP9ru>;c(P~x;@|*^ nQ .QvFT=#U/r@תo.ΝpI(|/XKPGy%EI3w6FuSz$ ,s1#`x!ؾ< aFV0RVG1ʒQVHpOYTt~VFIbj$K9mùA[^Z磓t)d+yQ8RMeieIf}kaV"PmMx| 1iR3[Q'dN晉9sCbvx&C.Q1EQ %*<dzg~{#چgگz1#gۂwl$ 4:q~iUbE - dTĦ^;Fcx0-loι3]PI:)vp[yJ#Q̄wX}%+Sz7dhY~;,]Z۠ot6[ Pφ5ӟy \!СCIo%$c#)%3>6B Fj}6BlXO/xVZT./oar-2.5.2/docs/schemas/scheduling.png0000644000175000017500000003640111757171206015773 0ustar plbplbPNG  IHDRp^nsBITO IDATx}pufy0y<B(찡m۶]s5///]]]UUU~?''gݺuرccǎ\tqDVvxjkk U[[Ђ ]nD۞UU?.~Sx0S.~uT(UU?*Çׯ^Z[dɒSNM4i>#k֬>}/tǏ_]]}ٟWq '_d'@ T\@Aa>,`S\?j|C&'h3~,Vi/Gu>cdLg[HPj"'Q+ReJQ5Qʩ ۴clWtҥcƌѾnBPF|R2B RӝH&J9ՐڴI rM7-ZaBEc'صc,թULօOۚ(T 1& (~aBƓ D" Xw*9OQHV|RmHnkSu(hjǸ&(օDD=P8E*ReJ(my9Uv k*-lll,((0=$kDdu TERXSX=TLw&J9 _ns dLvgTERXSX=TLw&J9UaΌ k*R]]aBƓ P]PCM)]]]===RP$M6]qR"ïVMsxx~/ uIo&EhD4u~0uf$e4p}ZUPxSRPƷ$ l5Y5hg5@uT2jrKy~)"PEcB(MLHSyd*b낤$}/S=VAsرǏWXkTk4(++V)*lӮ.l߾}۷/VjbS|X˩OpC6nY)Y%}d7K6$Y+uICͅ  >j-*?!vư&}d7K֬ߺ>-8qow˖-Æ +,,t>BLj]?9d._JVQ}$?Kv֬ߺܧ~KjGppup g)A%PO$Hu U>.ڴֹʩpM p UUUaWLudR#,K Yko]޴iӊΒk k#2pZ!WUx$IPX$/U:?7AUC&COSkm ۔(JeeQl* G$dW|Ԫ[JVa gX2֬ߺ'666Κ5{:!B23/4a„5kִk?^ pה%I/JsIeF89\郢(-JOO=zCOSҐjmZ{+,*lӮpvڥ(ʮ]%  !vGݐᆄm:ppH8KvǒuCš[w'Oj GC6?~|̘1˗/wz믿>##CB[(O,ɯ8~'J_yu𕄮!z{ӧO2ɓ2+xo<OMMMNNNqq͛}Ѽ~[[G{uff)SZ[[:twܑ>a޽֭[}W'xB[g)//߿ȑ#?sn(y}eeeYFinx<֭+--^w-\oynX%CjjjMT___SSSUUtժU}ݻ2221u&N8o޼榦C51cƊ+Z[[wܩݝ[|}SWWv>}5k]7EYlى'۷KWؔ(}ݻ{{{ØCp_\\q<} Eĩ#(Ң!bV(++ۿPu|FMJKK7lؠʢ8 ֔s7JJJO8p@AirX<@3dxq;A_o3ޣ>NKKSM^ǣkl>޳g >䓕+W~ X~ĉ}>>:(Jww] !(IP\99r[0)_W gM5j'Nذav/СCawN[Aڴ---:,FfϞ=k֬{޽SLQK.njW=N_vuu~ ̙3̙Μ93P1uԪ'N477ϛ7O[sĈmmm gϖl*}8<1_}|߫('OVk'O^b[!f~9qE)㞞UV >W^QU/yz dnƍC|Æ {뭷TUpܹsρ]VT؍3fdddh󫪺{#G|zJ*lJ򰉇>DcDQawAg=yd~~; $544o@1dx ;xOϺۦK~}6u>b/ =B5T ǎ;vlffҥK¢r2mZ=@IO7j|l)\hWOIݮgaaE***-[h|ͻئҧvuF}ofM#籈u= .ME*h3ꡦ-FRS8"…v[nDc{#kX & U= hT(qǦ…v)A >vySav]r;tZԹΨa6$IxȀ'3 \MԷ^Ѩ*_Q8"@ ;z߯e]筥OjN3urrr](VXq/Ydܸq~__O?1bDG PGuӨЄwjC֚¢rjaKw G$_3­kj]3.g?{W>ռ?OUUѣ%Hƣ+? *xx,,Ҋ+vw>7+/O2'ۼ[$!8Sy=U>WD҃L *@#PA/IS|Ԧ(^`Z858T\L (54!}}wP̸1p T\@@*. Pp T\@@*. Pp T\@@*. Pp T\@@*. Pp T\@@*. Pp T\@@*. PxTH ܽN%P). Ip饗nٲkmQsETDo&c{(wBਊTꄍ$P5 P@jlN z0u)~ٶm5\ӿ^xA[UUUsrr֭[֖;wnҤIF;vlرK.՟kY;NئAeeeֆ4L h; ]tQEEECCCÕrÇ)$ߦ>:0/})o߾/aDB cƌ|衇Lߚbz,lS~MyvO׬^G?Qkkb+#$yᡘԈ$Ύ\W3}UV}7۷ow˗/߿~ȑ&}Çׯ^Z[dɒSNM4i… x---.\K65;w|w֭[2;N ,7nܙ3gFp 㫫Ϟ=ϧ|a /(۶mtPuuM7ZHmʯ)AktEQ~뭷_! QbP0F.Nat=N}ʷdM3=6(ڵk`|atI3h ^owwcz|QU;@i]- 4F䆄|7*ksoӡ5³gfgg:u*;;ٳ#6הAk  ePUI#G<.]ѣO7oɓ7|bŊQFi}G~=zܹs/_UUՅ ~MMMvd Ҷ`mۖ=.))?amʹollTz;::TUmnn6ue6(j^^OLk~kΝ3Β| ɷ9 7PUUUQQ<"|],lS~MyvO?tШQ>C2Tܤnذѣ8pNKK>O{\VV~qcccaa%%%H)EQ;n 6q9uvvŋ~ǎ;w܃>7blP{0gΜ;wȑ#3f˿ŋ۵&W^y-^ԙ;v+lS?8Ӛ'Onoo_`ɓCmPΝ3]~>~cWUuŊ]t#<<")SHbak̏ÄOW5IDAT믯::^+@Mꫯ~ fcڃ{7++^ѣG/YD_7yyyoMٸqv6(j}}رcƍgPAa?ܜ'|RoG~>ԬZ*33kbk.EQv<"G^=.) 7 ?hiӦӧk2Bv3&faA%UUn?~ cSv4?,))165p;vh?xW\jZZٳgsDKKvw.hS!},?qܣClEcr˲e)zH5|ԩSKT{{̙3̙Μ93|Ξ={֬Y_uww޽{L5XUUuĉyik1aْMH۷o'MuwyI_~eEQ~\uU#Far6G $ fT\@ 6䘐Ha&A%Z$5"[()O}CtꌟGd.AOЗHI~5 _ tIET=Tgm*vO.]9f;iЫ]nCV TPT%k&L%HjLC [ +}Fئu,}niѢEڒ0]7Pc$*Iլ},Y3YetF%Hj+QXӭ6>UE[xq߯-ڥ]UD$y/O}&)')%FZ˗)/l)aFOE.ڥ]UD$C^2]%Xd,_Tgm >UE[XPP`z|KXuhd;ݐ;W ɚrss?Su&MϜ9ӕ5;::~ggʕ+ں׭[ѱv[oU_-\~V^Q{޽.\8w\zzzЖMSg!+ᆦO>wǏj'r74 2C{{ڵkM{mj.p?S^BmAli|@L|YMa6",}(Juuuzz._m|KijZ%P%ZUG;Qa[5!&9rXw;|LSĺR6mdr Bjb5_|1uT ޜ8~._\@G/GTn3BSN5'<~f)-F 8 db8#2Ks$@5Wя~ڪH:t}^pP+df풰]W!6勤*©.x<. %J9UaBv{y6e"U."vOWwzKr;:'N|7nٲeذa. ˩dnI)_$UXN]Qk|vqo3QfIHVBB;.f5vO߼ysyy#GuFwqן}zҤIO?]trwʷ)_$UXNpsMgƙv^D%k;ir!PaV#,i J}}>K/l%rtJ)_$UX:BIS)$yLYrКs?$PS aN:thԨQ~uAN6;,))?~Cʩ2B)yETcn Uإ8,*lSH8˷YrDPء0N aN?xWM;9*8qbccY5ʩTxo3"©kS)$_WD%*H cEìFX6m>}Xӧ{zzN<%rLݴH)yETcU8u…. %J9UaB™v^D%*iz/Ǐf.|p T\@#?eCHYC#PYh1Fإ @|K_PQQqEUTT444˭>C*xjkk U[[qlll7n\ff?JX,Sئ]JkyTaMMq\؅vI@ d7u}d7EUTT,[̡ md 7̙3G^pZ3Bܹw [nUl8.^xرzv2m UZǨn]omʗ i dO+vaLnD<Ț5kO/;w"q 3> j-aǂ}CnJZt(iօ ڦ|J 7$Bvɮ}d7k-ir!P!@5V|O֊ҧ|PUtR7iWڦCLkmʷ)_R~vOKvOnaD%j P!@+>P>uA cAAVxcWڦCLnyP.sZk OKvSgF} ,?~VsڵzZ3BV:&LXfM{{VKbB­;p\ecwL?#]*?u(?*v\4̪qcǦ7ѣ JBUenj|r]Skr3[R~ +S V~]:FFP9'+,P;!vuua;}!CN<KBw>PM6]qw.\7ep@e38 P3 3aZ@ B &JLTF:!M%1Qfl)M[RWbZ009b,P̊ f~,U4 S 5K."@J!PX$H ;QJ T;TCqu @!PCxjjjrrr7o裏 ===K,)((̜2eJkkvxkCq&Lhnnnk֭Ç߿yy?(JWWWUUy'՞y9?놢(w_VVVQQњ5khꆰ)dznݺ~8~qB Msss 6mZSSS}}}MMMUUUV}gwnjjȨ.TUկ&N8o޼榦C51cƊ+Z[[wܹk.EQ/_o߾={m;vO뮻f͚e EQ-[vĉ/b6(G}{0oEQZZZTU0>| eee766@:tÆ G՗ 8hM9w(ޕ55hz`7A[:̌t ߦQoo5TWWٳsy|ʕ+?_~ĉ>O oavuvvz^EQuZiDa'$n?WGȺ=_ٳv[5j͛O8a^nqqCFQQQ]]󵵵i[ZZ@!PuY$̞={֬Y_uww޽{L(%\bմzZM:/RU[Q3gΙ3'9sfvcԩUUU'Nhnn7o#jkkfϞ-@n-'bqOOϪU҆+ZSSsz z'qơC|aÆ[^paܹ999k׮U_v}ƌ?ݻGʞz))Æ @J5𚟻O)[|D[ f qQR' p;IlŔH*)D~ƧQ@J!P԰1uRj_W a'S@ "HA|S-.BtHq\ޯ<B pfk)LW̘i#U7~pBX5x3 qw@*. Pp T\@@*. Pp T\@@*. Pp T\@@*. Pp T\@@*. Pp T\@@*. Px?];Fzzz,YRPP9eʔVEQ:;;ᆲ5k֘z.oHM /9`̚5C[feevC 2444رc߿Ȑ!i-u֕OQhݽ:wIjjjMT___SSoB8&===ܹs tK՜ Ⱥdvt 'jO~OñҢTU8p[4*--ݰaѣG%v)++ۿPUՒj K 7%K/,))~?8pݐu[l:u&LXh~3UU瞭[ߟYpT~CCSL?5=]돝w<+W۷Ϻ]cn;y8 y~! @}+**x<1P˽^oWW]O81//o7oc׈z^_~z^owwBWW"M {bZS;_+b](.kmm9}tnn`o_M넴@u~,y?/ =q9g"! ѣPRR+9sٳrwym'O*'t+Tz{{|͢"a;vSSʯa fK/5^^zP<] ltm^RUǫW0aisγ*3ɦu{_YOCz,ys)--+,Y'Ȝ4"qaHλ;ޞ^WW4bĈڶٳg;0s9s3g̟?_:u_~]vwwٳg͚_wwwݻwʔ)ZUUU'Nhnn7o@-߽{ȑ#}>_YYSO=8^!]paܹ999k׮uXhƍC|Æ {뭷5ӳjժÇ+Ϙ1###wY=ikk6=ՙ3g C6. UU7Pjjj.b<ɦ{Ԏ)&Ɵ X8󮳛"s;!<h|'~ĺ#=u[kֹ &|kϞ=EEElF)jp Wu/bze)|\aXE(`Dc%@e!8ZcїBAa[bK2SwiF3Hno8~dCAÒW|MIpNn6s=T~^$ |*@4=^"/!HMw!qF`3 ؾo d (ROU (Hqe bIENDB`./oar-2.5.2/docs/schemas/scheduling.dia0000644000175000017500000000350311757171206015741 0ustar plbplb\ݎH)*N? Ndf/bgM % [m{ϳ/[QLVF>(s0 qxf~EOa[}hWu$U1́͢Q0'Ex'\\Ѫ%"eˎDU/ !-hSyw~t8}|{75Yߜ(R6=s1Fe`حzMu Ѧ*fڡX"3*l Um_"]+I1PrrffE8EΒOXEcs0`q+oat8([`}B©#(?SrSCl}<ٺ)/jz hB`E$WGS"H:g|bNC8z5+T4vZabE*!n%]Ubn~D:F}_>SO|I &mt͸uvI\I}bcq_Vs v 3c[%'/}]8x]KKy,!Y`'[6ە S( <;@݃a0Ckq,;61%; k0nri*Vuwqv ^.Aվ!6It+ V؋U隢Y$%rwitT) Ip;Ē2?I{ \m8@fhXѐ6m*7zQn'S>Hj=na7zR"9u^Wi-PkZIq8jPqU,[\I'?2s?|D=R./oar-2.5.2/docs/schemas/runner.png0000644000175000017500000007265211757171206015167 0ustar plbplbPNG  IHDR ,ƹsBITO IDATx\Tu3@ 03f=bWTjGiY{-KRq[]\Qi[Z'9?v39 ?ps>s>̛t,n/ 3$xHSA! t:DNFH:t9 ! yd5 IeYGfff>&p[߾}?s߾}ҿot+V0 k׮M<922;8ucUEEEiiiz[ < IW^-**(sĉSN+_&$$\res?ߵkי3g>Su"g^BBBBBBl6ۏ~O?h4t:Ǜ(u:ݕ+WZZZl6?\WWw7^|Y̙3)))mhb"(ɞ];#ImmmGݸqFɸ8I4&^x1---44h4:$~ixt-33w),,VU/vbyIIImmm,N #K,ihh(++tÅ1bիWΟ?_n._}Z-PPP\c gv$I ,x̙n];v;G8K,a{F}y{Nadv0S]/tz._H'yyyǎuO|u?1@L@vzֆ  VxAYYw`=@I:o߾ݐC@nz 06B$O;-ʁ.?~Hꉟ C3}^#I=J+$t:|:(--MItڵɓ'GFFqNR8q"???"""//ĉu gnc{Ξ=;r>}9ٳ_WQQQݜ5*ttNttג[l-KqqFu݊+fsBBL>|xTTԯ~+~6IvzK|$I|ugpݾ:Y!w2ڴBrOn?|׮]gΜO%I/pʕɓ'ϛ7O)swM4i̙u goUy1ի?ϟNl/_=z 4*Xj '[kGydɒ% eee4$:IN8qԩW^y@2;---kp݅n*IҖ-[gy3n_Nv;h<]Ig)Ξ=+իW755EFFjԩ={2555敉555}ը]WS8QtaԥKVVVZVOQ׮$ʕ+,[PץkoGa.l罴~qɲjZĮ҉Nv~~!iJhhhhhhHH#dY޸qcvvvhh"n!!!mmm,l6R$׉Bɏ K~ד'O߿^_|F]Xq鞄$)u^rJLLL]]uMZN9$ $4ũLJJJcc[k׮.:)vM&ӹsdaes4rtCpfWlٲ%..NI®f}KDHyvvrss}ّ#GjZZMьv; 0u[|||yyvɓ'?Nzǔ}Z/֮S8_^뮻7xcm|ח?tPKK˵k"""4Jjt7x_h%7.lgz)??ٲe Ik G4bԒ%K$~a^Z\\L||MYήͱSN >n1bӧmU[[\' .,%+++,,lС۶mh넛̙3?#""~_joGa.lgz$IߵH3Lwصwri3 ~ֈ6o7f6l4h[Ya:o߾/$q<I$B! @@01 ]W$I$B! @ @H $I$B! @ @H $Ky t,{ zF0`ɸ_"$ACGZs`G $U ׹CNB! @ @WqXrq$#I1hcNc$)4t:'_7#I$B! p; $B = $ (xdY&Qvq3I IH a$ @ @H $I$B! @ @H $I$B! @ @H $I$B! @ @H $I$B! @ @H $I$B! @ @H $I$B! @ @H $I$B! @ @H $I$B! @ @H $I$B! @ @H $I$Bt:[!)А$Gt#BeHt/B_r7\0݅FHt#B}ܡ AFE$$BSܡ A% ]2)Y5gh# D |;!xOxOpzn0O1!I~4V]F#!Q #!q6 kG!.І!]$%D$ ɋޤl x;!xuHal3z(@8q.\; *$ }l'I@qLGp36= I|*.5ܳ UYYi0JKK;:oRRґ#Gܽk4Ϝ9ӍJ !$io#w}_NJJtBrܹܹS$\UU=u.\6B$$@NPnٳg^OJlӧwocBW%%%ǏW^;崂Ϳg~2A݅f$ڵkq߾}999[nUƥK6l„ NGDŽ3J~^"ܹsM&h|XcgϞPRVVU[[+ѣGN* O7c Xw@>}{zgǎ;dȐ)S$]zG5 3g/X4[lhɓ#wn>Bˑfl|Iiܶ 9IZmۦ6lXyySWWPΝ8qԨQ*;+wWSksWkn k͡!ɩI;w߿nw`0nҤI6ޑN  A/ߌd2͚5I枸zkСv5;5bOW@j<: 9ڿŋ7lpcǎɲbٔ2׮]Xh~ԛ=gfeeyRRiQM>h4vccB(fluuu$\R9Цn;vHT^^w^u饗Z[[***ku!{?OnX0@A[ތ"//oyyyϟJ7| ''gٲe ۴iӎ;6m]ω]}$;WPgwnXBB·~s'ݝ$4ze%ǏϏMKK[j _=S:nĉ~z9s愄8kXPPOH+++m6۠A=:N>ݷh7cz!ɧܹ>;vիW7lbŊ;w8q"//oGvڽcIT{;vlmm֭[͛7x{Itb߾} Zpatt2BOkSƆ.\RMjϞ= . ʚ6mZ'.# ]nvCGHiO]F/]4`bX֭[/(/$_?z?]FaIRRb9񽚚7^Yt_%"Cz뭎+#8p`CC]F H+rQQܹso0222Rm۶ V^^Tիe944T{ƹs:N5jTee ϝ;'\ի5V#I>M?z?]FoXy;vgyjzu{ ]}׸7NF9&kksEkwwΛ7/O ]J[ ݅$ 0q:Iٸ1 #IAƿ66نcǎٳgFoMHH0ND֭/҇~~a$mȐ!=؂ ѣG 2ī1~УIHg7[׾77fC!$h4:9>̓kߛ!aXΝ;7~;wJg7|s'8p@NL&/~NT]_NJJh Nޡqc6Ƀ{qc6 I]Gmmٳg;؏>o֭[~nlfӧO~J.|f}הfnb+**;極A$׸^"o߾[%W^ӟ'?ƍc6-ZX'|r7>|X3$$n馛nuF1 Id4.]:lذ &KӧOW=~{Or'm7EEE#FX,ホGhߘm˖-f۾};7fC@=;k[xڵk666n޼Zew)oY֔~n(el6[bbm6nܘ&rkk Vkcccyy,|[ڌ?X˗/gffJK,lNHH,++ʪeѣbʂf̘o5VUK.EEE]|Y咒:VXXx-(q\Çk`x'v{CCCrr޽{];+|aV0hchؾ}{zzرcWX!~j2Z[[Vhܶm,;w߿nw`078~:1##h4&$$g?kmme?OIIQg駗,YW_ڵK)㮘}[.~ IDATKKKeY~wulnn ݄$w+n08L2eʕ+;Է>làGA}M$l7&pBjjgZZڅ $IZjդI'N󪩩X,{o|z뭊+Vl۶󒛫\/bҸ(l6?_$I_ ոɓ$)>>^yyg@$I:]T$:fBBOmhhذaChh'|"IRcc$I맞6h=Ѐz!uhLII1cݻ }]j#+̙3gΜI&2DXɴi~ߜ8q⫯/w/322"##5z$Irj_v{TKrss[ZZ/_.Iƍ &_h4=zt?zhtt rss_z֦ Ųu lذuq_jUuuj#׋<(b }JKK{=^~[z3뗔ԯ_3g^:,,lӦM;v숋KKK۴iZgffM|>iqYYYZXXF1 W(++{?۽i˖-^na"!y?vm 8'85ew?@$Bᄐ=FpEHMFBPݑ#$Al6WUUu :HHpɲ| I,s5LFqO5kVmm1c,KYY$I=\rr^9p$INe?<{$--My.Hy[6 k1ǻ}ǎg/F~`4f*e֭[_z?p$eچ c-X=ztYYِ!CZnH9#I@6mРAFѵԩScccCCC.\XYY={.\5mڴj> Bhrrr fϞm2f͚Z歷:thjjjffn~՗.]0`bX,֭sJQ 8q@s̙3gNMMͤIgϞx/^_fddEFF:0))b9r;Hh:TQQ!rlllddn$)>>ɓJtIV\ڪLWʲ\TTb+**;極!$ի>`HMMMOO9s$I ,xF-[rsswy^̨پ}͛wa6yu/65&^A6G$I $I$BaN^$B! @ @H:Ӓ|~sI@УI@'T{ #!$IBB ¼ɲ`z @"$]Iܽxw?\8' @ @H $Ip #I$u3n@``$ @ @H $I$B! @ cIORpH#I ᖀC1 @H $I$B! @ @H $I]=g%I="$u0D<DN |!{81@ $u"kn0 \FIqa$! @ זNk/1t&$q7Dy|>} ,:1eG2d<)G_hhs=N?Q[[{ك:oii9r'xHK.^xٲe7nܲeˌ3]?Гt8б0s=srr8L4EEE#FX,W&ʲ;ShѢf744,^w :tu֮].wvuܹ:Fqh4.[믿馛z}AAR\;G]_ 4d2M>}k׮uwL%#ڝfr-DO{ʲ\VVU[[+ѣGkkk>ܿY;`1cȲz &l%KlÇ'$$TVV*5Ϙ1MXS222>cY/]t}͝;Wnm۶qƔ6ЯJXmYYYTT:"''7Xпk0 CCW_}ӟҥKǏ?t萰\;G])11qժUʻaaak֬Q:MEΆFugCJ, (ӧLrJY꫔]vso/^jZ}ҥyJJnW=K,Qj޷o2QX 3q)e~j2Z[[VhܶmZ1$)ۼykqII{/Ƣ L&~]K %hʲvZ ? ʵsK|۶m4h:}СJHrEQG$sz뭡Cfffׯ+---$̞=d2͚5I#Gڵk׮]G6lXyy]FU]]}ҥX,ŲnݺB٬VD =O<ĸq3VZ5iҤ';rdZL+Wvlj/^T'\߿ԩ-R^?%%%)/铘Z¾rawn/&''MIIQ^hlG,K'Tڳ۷o߾UUU,lnFe#C=qꜜe˖ɲk͘1ͿoϟeZwܙDǚ:rt$IGILLLLLill]F׿655 7nT8q",,LB.]7zrM][neǎ)S+kSo‘$ΆFAO$rΑtIV\^/tС Yccc###1QFx};/_+rQQQKKnp=YXnٮ_FEE%%%_h4=zT9zhtt _xqճf*,,NttСCSN} .)a_v1bD]]/IҺu>Z#>K- ^lЁmyyy;^רի>`HMMMOO9s$Iz,wyzԨQ$m޼yǎf9>>gZT'? 7`4׬YnݺիWϘ1_~IIIIII9szO~Axwy)((xfϞ}=k,ZsBW:E"""JJJ M&ƍǎn)=FF<ݾ6tžH~]vnHo2d˕!<>qC8m<>L"@n&---˗ݻwɲ\\\|Ŝo]3iꮢ1_uwNǞz꩏>V HSLillX,_dYw[9t8vy| <|_cuۀ+NhPa?S[7&N`ؠ d4Ϝ9VtaIIIGq} t`p4jV?Bl_)gt;R`SvWu'ͦkL>w\0.v$͚5v̘1L}DGGgggoݺU$I[l8p`tt)ǏϏMKK>V{.99Y8p@-l4.]:lذ{wܹ&h4<6Mө?~ڵkׯ_ϻjܸq111fyѢEN慎ۄb4FaX ܝ}CU:<|72L}jv4&&]aY ԩS]VUUe4k233,Yb>PYYXEl[nq3fhkk+++ʪeѣ땒ƍSu}]~ٳ%%%mhmm8p`aajmll,//e911J嵻u1 O-2j8G dFgtCIob… iii.\ОE=Y׷TWW_tib[1h,⭷:thjjjffn~Zl6KSPP0{l4k֬&톥(/yǷjjj,K.}#""ZZZ?dYv:@8ZSa3\k>w©Ʃsmig>HnNp=-Dr8&/4hd>}ӹ6w,l6۷oX,[yyy+VpWXȧzΝ;3335'\ľ}[UU%˲fjjj\Yln QKxm7xcڴi5ܹ355թaܭZLo֞nhDZ;A\.pw/ pp,=UCk666n޼Y}pkOp:Ax*i!wk=MMMVR.u-x?ǟ8m޼yǎf9>>gZ Iw[!r{W긞]迻58vw%:R˷{;߫smiN<<]#F{%IZnÇ;Wu,$\|榽$$CN(׳ ww_x~ZAxcmo߹sL<9{ViVS)QRRRXXh26n8v^{CP]}GNG>Đ!C/_>j(gazÇLB?x$ݻwɲ\\\|Ŝo(1: BBBdG|ʔ)k>}ݢɇ|'> ׀Pn-h`o뮻>#˗ggg'--^pBBBRt:_ib…>n 7yyyUUU khѢ{Wy=xoYfGt#G 8بp§~3[%h^> 'y׿^zg?+}vzon>?ꫯ.W)rСTLFFFIIIV,˗/_Z|yCCCmmu{ knɱ ,ryyyRRR||dYVz$IPw^}U.((xꩧ47^F'$~}]#:B>chgׯ_TTwܱ~e`xW>ouԊ+b٢_˲|Ivر111ӟԚ_ynmܸq:6>::zӦMm7nT8q",,L ޒ]nWRRTcӧϘ1uꫯrm(,ݻ7&&QߓO>9}竕(#C7'Tmw/^pu1!M=ץm2WOn̒Ç9rڵk{c=N?vݻ?$ܹ>;vիW7l3jԨ]vI܄Wyk׮#Gʲ|=;v֭o۽{n޼ٵBdžݻn7ε/ǏNIOOvءk='Nlll>%Jqʕ={ :]I#""~_]vM'MOիW+ikk[f͔)SN?YӠvWG?ѭΓF\~lIpS U,II0ά,3;Ҳk׮QFiF%j~G'*]G$>lG_.yNWucI?B|#cϹ{Vӳ$%79r]v5zaÆ+!I㹛fYytOtud׮]{9|[[[/f\j644 bŊ۶m;:7=u&33ɓ| 5?VƎ+IҪU&M>qUV F WCIP'`z{ݦS^S</^aÆӧO;v,,,Ls9x#G8P\\,IҨQ5jÆ 'N6lXRRb9񽚚7T;BBB8$$$$$$ot͛7OelÆ ׯw}JWNP>mG_Ѿ;p \pM!8GURRr7;\|yNNNxx'V^h?ú;wJ锓2JS>7|[Jvf]xqճf*,,XZZIJw}EEE'Olmm=tК5k 殰F?pIIɇ~K~zxQe ѣ6lhiګPԳ!/:!۫CPnn;vѢEzFepiC=^?|Ȑ!111qqq>rr] / 5n,?~{1 wy7|TB'W"""njn:w7pC_VFF$I:h4;ֱ6GNM8SgW=c,;wnڴiIIIO>r혰CU^?VzU /s=mg}/%IҾM" C/>w܆+#>-.brI|[6@zs8QW&s)G<:Bgp՝W B! @xoB KJJ:rk $檪(^~夤$=gqul6[XX/} L>w݅$͚5v̘1L}DGGgggoݺU$I[l8p`tt)ǏϏMKK[jBx璓z}NN΁FqҥÆ {Νk2FO>mt:]CCRrk׮Ug\~}FFF\\?چqĘE9nh,**1bbQZ3Ы4@D/nɤ>\jv4&&]aY ԩS]VUUe4k233,Yb>PYYXEl[nq3fhkk+++ʪeѣʓ땒ƍ[f:}wgfdd8uV\Ç+`0<v!99y޽=P67\1/",,쩧t'N?Y bbb222ƌs={444,\0444++kڴi,bԩ .TLJ$I3gNHH^l 26,***99駟.))q|k/bxxxddaÄ5h˓O>z<$,7p…Tϴ .hϢ[ZZ/]4`bX֭[w4[o :455533n_~]-c6%I)((={d5kVSSvRRR?[555XxEDDDKKv= 8q~Y!!!ӧO+,dX4.b//322"##e̙3gΜI&?!!!6MyڵkϞ=HLLt|_~'O^Oő. HĐҲ|rI6n_O0]a\YZZZv{EEűcǜ .!66V4+WTN6rrС Yccc###vN馛v!IRyy޽{˿MMM555˖-:uS_z֦έKzz! ~ ,xF-[SZZ{g}>ׯ6o޼c3XVE <8//z޵W>!555==}̙$999˖-s;v!CLr;5rӦM;v숋KKK۴iS֥C==J'G'iFAW67\1 @HB W@H $!1N` DH"$!1a BNBII ?Р #I$h#!pBHB!'W$r\tsQ)9$$N¼ o 1\ B N#"$?gm @H $I$B! @ @H $I$0o7_݄n#˲I`$ w 0b$ @ @HNs7IIIGq}ݽ&Lvڞ\/_.نu#I_~9))C檪j,˹sz1%B$ 0H1}tV J^CHo;puOUUոqbbbfEU?j*]XTT4b2|If͚U[[;fRVVT۞={233O!ɉ޺u2uq,ϝ;d2>}]T1K.6l؄ ܭg%^z饗O.bY&h__,'&&>|XyW}}ر111ӟw ë:|yɲ㏇o߾iYW^ƍ'е14l?Ǎ gs̉7 ٧N8o6hmm8p`aajmll,//Ef\df;|pBBBee,' {ed2}wjnnNJJ*..eOShh5kVkJJomKKKcbb+++ʪeѣ wmXvmccFc Ì3dY.N3Aoh4ZVY/]uew ':Љ݆+(;Q_ In,Ν;o'yJJZ駟^d,)S\Rv{l߾oT^fՉcǎ]bpq_}UJJʮ]Z[[Aۧ.N3A[oTwy{јw>mUwnnn~### &,gϞ feeM6Ty'{wĝ?|Z쒤!,E Owu^页#7jX*ΨpיN+N[zCLC|:z.CpR"7糟lyϏWsδ5̛7-cj -Z}u .\rJ>oq.;::a0bccY{UFh4$INR__@tt4I$I?^/|z!((H؉VJ̟VH$ʨ(Vk:\-ǙP(:=ch7o>{,agϞݼy=K*H999m6zyW cq/dVjBaF~Ycbun2i!##СC?CccK` fK*SСC 1١`:##㫯/oq%'' 444`JYcv۷oY]R!x<á(ʞZ fYi$Ʉ۷ӟԞi I.ʫ;tgvQAym7Fb, ڵk555QQQ׮]VF(ݻwONNZ7oL}T*wر|GXð.gϞsEDDXnhh(33qD"JonO3m,=iӦM6͢.ӽ{RSS\P(iH6U],fH&kؐ'K@O!wc588rJ$ׯ__papp}5ΣCDqq1IAݾ};))ZBh׮] &kmrry[[>66V lݺu͚5gΜ'N۹^ybT*B8^RRrź^y啐D矛'OJRO8ؘ8[EQ999|>| ={"""ϟ_^YWWh">o^Nc@p}OGFFI~|z&i֭@=ZȐ`3kI5$ٞFBڵK V1 ^jNj*//0,++kpppʕ$IVWW$<<\ ŋcbbBCC?kɼ{FFFr\\fylk^XC .O wpϛ8𪫫B4 2`0tvvD@ ŝuvvNNN'NPUQQׇq|ݺu?퍉Q*ɼvR9< [v/xGFFܹSTWj;$y<^^^GGN3~Zm9g> S$\-I$I&)5ZV"0FEEiZzY, ,7vHu?~|D&QcW5=O#'AgA;H&Iܹsݻmmmeee$IF9tfǥB$=|SVD"zY 3vH&>h+I:IR JKKLkM]q6|C$ȐP{޹@ijkkCBBBCC333BݯjHH+VB%%%BJF577/]t׮]_qѣ8YvHƵS.Z(%%\.Wј4b[f^>9Oeָ <\K5FV__h"f/>yq_r`yw&hFFF"""NfI̐&=CS?4G---yyyB3|dH*nY$|}d?ޝ!JƗ48`N>:v>ɻ3$Lf &J@q:  Q$$H!Op,C8< $H駟֬YsM .\g$//&! \Z仏)Rfzg5y`0/[cOFe0DX|` $+((()&I"⣏>ZtiZZ2a---r<888>>ƍ'0G! \|de}wdK~MMM#Yk׮}ʕ{d̨ .? #sn$6mpV^<{˖-§.]Ĕɡ3oڵ/+W˛+d8WTTع A>tjT3g8wﲼfY/&IU?sر֧'a& ͗ZD"aGEEiZ,=|F ;O|uֹ% 0Z8aiB`SvaHdZDN|w^֭7n8q5 ?{{{f-֭[ pv0eFNjI&]XPb î^zm6ɞO#͛7gIJJىa`tt5k(,,LNN&Ir߾}Wbݽj*O>IDATU^^6oQWWM6\rk.@@DBBZ6i޽{###\\.okk7|#|֭[ͳ+W$Y]]mOK (..&I ۷o'%%q\0Qo>7FbaUU~+eM&K0ALLq[zj2ByҥϏv2nǪNv1_no#A=X111_}BL&;rButt.zӧO#p߱cEQ:.22J CggH$ o۶ɓ'&aJ^'N("$$|[*pܹ:..npp!t=zR6 K,avbDxxxYYB?oL ܿ8qFNp^n>ގ 0lDbbYSɾvG܃ؓ$!NMM!IR[[rÆ V֊b;w,(( a|ѭJK)))N2Ctww#bq]]&&&Y gHl)t;s _"$<*'I"1f$Y7kKxǏTSN} |z!((H[[700M$IϟgnMߎ0L.fgg qf.^X"d2?~l-xV+J Rsفaaanͣ=`f+$* x. bmϛ7,LNmn}Baŋle rrrrrr4ʲocbbg-0f ~W'Ou=M`udrGi$;jժ?;v,77W.Yȱ! F(ݻw[dQ```EEEII ݳgϹsl<%X&]vS*8K$T}vb "555!!!55 ((HTرcG]byo޼ Y~g¸~ٳfΞ=kgYp-wX_GSl$gs0-'I ȓz$Ī\CFl$ Y#?\bEll,SZZZ^x T*q쪥E.߸qvxl<}Xȸs{ :[D:4aaa!R9<qEQ!!!63i9"La1h6zyÆ 7o\`EQ&q^^~}iiimmX,fJܹQ,MMM6 #G222Lc\_WId^EQ?4?͛7[UUT*eV:uFxBCLsg|8!DfFO:544OQǏlBPP^h4$IZ{\^WNNNn۶M.fgglڴѣvb^X`AyH,̴V}D\\3| &q0G "555!!!55;+ˋ =\v&444**ڵk6JVVVB>{ɡL%T*ݾ}1$&&oذaƍVZ1t֖d1.gϞsEDDK/a2Gܳ3  ̺maaa-mGBIYr8bppF322Ly4H 2eYZZZbcc򂃃1sp .}8qw܀Aqwܬdxtȓ[|8*``$Bs/3SȠz=ͥÇMbV:ME̢E~o9<ZtiZZfL̑jii7n0.sŘ>% [o e{tUWW "ݻG/3eJ`_d )c0d2YAA`D&7h\.Bk׮ukV[[pႵ`:;;M"gV"p߱cEQ:.22i||<,,g@@ӧғjz嗕J%m۞=zɼ*0! ec?kZDj7\`A#0Ix<''''''GѬ_,;;@ocbbgy$ o=H…|Ӽ.| $I1#>i_/>rz$炋ĬBywuxpNQjoj f+x =x+IkpJ$2*b{c $~WGIp:-Go3 9IENDB`./oar-2.5.2/docs/schemas/runner.dia0000644000175000017500000000541211757171206015126 0ustar plbplb]s:_⣾ɝvok73mcۊCrҼ߾G_d9T$;_E}q6e}2w=ͣgJg9J*$.ճ&ıhyYi4cQ4>ͳE:T8Kܺ[_T\gy4eEwm' =g氳yV@[$;?Wh*՛Os)/:bw2(6/?Iy3N(>cy/ٍN.9-f.Q>Nц>9p*r1vݳ,u![,NZGeSΒ{q `kLBv՘+u"9/@mӅۺpwa4GŬƨgVYF-uLǛh:6DmL2؏:eSj'C#X#^U!(+rw=Ϭ`}$u@ϠIPr4#a#EoJg4FQZtjqәymv?֤<|ULvSk/ AWSU>BDZ1W֝o??Xf()wʢXq"0dCgC=C{L:A:(H(#Qhx )dDjV&Z=W>W1e~]f 0){~=߭!uͩ:C0J:5Ej+ԃ< &uo1D>/`^"{5XQƗFUEӠaⴑYf<p_,(X(#_Y0>Vn#Qڧ%!PHӅZ誂5K{kR?Ko׿Y?~wEN~_Y6MTyB(Ҵ邒j—(P 5!#I}ڊTM{.'+SYY(00s?]Ga>peFOc2=c #c)Fnc#.έDD.r_ +d!imz PE^HY&El7ܵDc[[\ZK&ު;b]3Sd~Y2$];|( >\b<rX$N MtH#rK\>ռp{L*j1-"V =EO_Kh#cy/TV*[(j/ShqHHC2֪=iݓj>s NZip-m_@Ny5.\ӥOoRC5+zEIr\NfTJnv(> E된m >Q'ZD,4fW}s ./oar-2.5.2/docs/schemas/oar_logo_small.png0000644000175000017500000005510511757171206016641 0ustar plbplbPNG  IHDR}nǻ1sRGBbKGD pHYs  tIME;6p : IDATxweU{snrUWUWn HȨ8Ό3ʨ`Όi@GGĀbD@A!$tJ7ǹ }uSZz׻ֆ?tOU~.n?{>˻}4Zs+W _yW]/]nm۾pޜ/l~;͙"BH 3 ܺ7W6  Wtv>x7/_N:IG'[~Egi}-^S9E },4{+tVTJ,|ipc*рPJ!Ca35]Voas [FWo#~|ꣿ?8zȟ ?u,ox,MY 6ysq;ObpJ@)0z!DZ+N];K)rkRbiZ=]8B'[#B [3`7q>.Ww>{{%45{S4M2V !J) H@kݶ 4a0Zk|?!%{r{Ah2 yC׼W9>Ѓ2ݫs~I宱_GxYo\w5rߕ[;zPk)MJhY/Nv)NF N0[ !~ND`>Nj#Y26On&7>}/~r-x_sNX qΒ5Vzs!adP cL6<\z:8ŀ0RA84M|qlf'WuGXxm;:y͝@߅RB>^>c<ٹ#_1g0ꑭߺI~)4o߶_O~ܺm?{>?P)h4pkuQ 40J'.;p44 jj LDkeY!0MbaA2*y^:A4J)0}aHgg'RrL.i3 #}_̛S~6 qɗ;'O>?w\yŻӷSҿv/ PZJ):l4Bb7ԧǩOlF!BZq8@H,j;sn>=ꤷKla&.kU*rXܟz_{7~⽯z󹧜_^r^Cǯ\𮏽dizrb(UR dzr!:1勔ΥPBł ɄċLL\s|Ϗ&JY}Ǐ Ðj$j5R\.G&ai0Qu·tKW|O|/|=;^r0 ?ѥZuJg7N&6>ECF CEuj\Ŀ^vAf_>˔S9\ex زe 7o0"~p8jnU_ousFŊ0M sؼwxvt ϗ1, Loу BE6AKgCn'5+߭!?>۶qIRՅRw?5?n+01.:gLC= ?r+_s[~dX(n6@T'6&ËFa7<%_(BJGxS,;`_AH <0 1Z' Pͯ9& |00M3#b/l^b6SYXv-LB@OOB CV+{WTϿ/Ow~x^i!MѬO}܅.B(*MPzԦQ' |\S:|Ҵ |>y~6%'iѫ~ɧsZqBnj6MrlRJgggWYG @y2í0G|@zgY) ?{>}7O6r@kM7AAN՟|RJ8*ձuLmzarEJsJdsE,: {kRe8]>VkO4M0h`vb|CCCcWau]j,\)7i Q(#mXsAk / {-}j}M}/g_|u=^!s!ټqj)v?w,4slU@h6mL+*J#Uƌ]hh-TC`ݖͭk݋$dpp.Q,! , iwb}_<W6[{g\Wd,Swmj[[OAg\,$|at\!4l.4Bӌ^+Mur+B|#ga;.z=1#tgR xkdsk7Mu, @Y{:/1A㤔4M8w\lZAVc˶)˗wqy᫾'}W|uw\QHCkhLo1~ɩC*x4ST:S>2-4b ]FxxN1ۘžibē dIM!ϓd>ZkLC2x 7nDkPZkJ| fzXg%u=.knmO9mZYuOPwp>|a~MJ.?nߌkW[qUPXP(ceH0-vKOKv Pƪ<+M=lfrrfI6RI>GJa3uE… t!~O?/-/k¿;v9W"SY LSٷ\Hj&4nL#FR'l]iRG?A*TF+QZ3=9A _]Nؠ0 ɗw/gj_}6zZڶJBP4YJ:jSSSd2:;;,5x7?~߻3?넆{W~΁5fNOl~^d9,!TдmCH JY1=Fm|~sZB>a*MͦGrx=҄IҔ5kh6*-S^ôqIe* Jek֭[Yz5ͣ\.j|B 6 dȠ9_~}yә/N+G!'Wغ%'QO Q>mYE}j;>lLwCkh)=H B7`j|+fkgf0xqQm9~z=BzXώda^e /lD'ttt088H__il6ٸq#[lA״uttBjǕ'4z/n[/<7o|}(NzϧF;;{F@H<=.JAe/X.VD> ɾTb;:"K(6v}ϩ n=񘜜P($um;]UW !e0M˲/fg3 XH)f# ۶mcݺu!rT*r\"\.syk{X[hUe1}a˦c݃ 8M,+pjtΥd(4abPisJa7ghVǰc1J+ |&S(&&6M?%" oȲߢHA~:֚F *B!c 0+)X~$Uċ9Ú5kR`%Px֙o߾meߛN; ,˿㈌2YѨEŔ;z$ k*Hib&y6:Mn19לBX9 >>.|[d+GH 4 ҥ1kh], 4×ApҞ%[xzZbp2[6+n݊R!,:Q{qxsCߡn֦mLDG ZK._°,L"mN#$صI<139 N=Vl6WPJl4BdnP.US: T\ٲWJᇊeaHm:=Lȱxqhl8CR! pB>۶mCkM>GDSyBçFNҏNn(ߡgBab;#4%aࣴopU r0] )waesXFTzVZ*NM2<@kle{Z#!fVY,q[khwq ܲ,fgS ×ӿDhM>oOɖ蘳|HvW4⋓JEL>:M͛Y~=B~,h.WW'/׍VǶT (u!W(0AJa!R(wZ-4u039f1I:W̕Ȗ(uP(u`BÊRL!%AXf_Ҡ|Ȝ _aʮn7޵OgrS #ՌLbrr2IC"]] | 4$ap= ;⥧3}Qbdyޢ?}y'=O_ rE6_xN\JiqU<4=) 2BJg/ËJdϠQZ|V*]rE7$hgah )֚#ỿxrfNeY;g@WW Q8Gc@<01 [(jo|)䳒Hh"|g||\,?qoy? wsɹC-[̗E T!@U C'vT-vR<iF_h$Fq:k72* t#&JS]`W@CGevwhJ{uPû6Q.@fMϳu#xn+Wbk>:עةa&5swcˊ]vK/9K߹ndZL(^wza{)tR袳3C `zjliY$ ?ɘHx02xD+IF*AI۱ZM/aR1d2 _FARe8a,%.淏mG0Mk֎=E~PSG#[V9^tHTm C|rR)r9V^p}| !n>䜋ukY,2uVlڴ)J%XtPh߅ڡ}G(p9+#ۺ8K#O{ES a"@8%ThtPa@B&2^0G.f6+ce deF0'_(clH\@jyowVE]@gNFŽLNN&HRyjz.A񵓘4@!k7lz(-l||g{:*F<x-4|K-E쟂 *DǗa±n6NMKAP0g\, 0CaHd]fɡ& !qLus3blR]v JsKgۿ|Lk3 LrL__N+LɉԶo +[$_)KQFaHL+wpuwwM2Ȣ< BL{/j* lnDL5|LELBVɑ"*i!qlb| ~r$.ٰHMCxԽL4Ya =i7iK F-^F#\Өјغ<08mũIuH6~\6Ay^>!\v:4u ϮwAXR~rHFO L\m)#=B:6V&0DQh4Pt RZP}7}wa6~aǵslA}/Z2X:Wn }|ϡT@ r/>= PA^է 6mڄrLXDkMXlh7m峀Og #{;9&2V.OTAJ dsQg9%LSbQ CLˌ@"*Z#FjM* bQIZz.glhT |ӣxNT'SS46z=0m'}iWשT* R~)%'1ȕ5^| d]fj.(|r Mu<7 #QL&TX02gCM`||)&jmzl kZkKAU/\ + BM|0- jCitZ5B B|iNU|&p*M`J {(dE ˑrIJ4Qsi=/ze25Nw\SXx3qXk.^p'["96xQ l>aR(0 cF6ٙ=.8OFEFz̸ eZ* ٿN^2Co<:::`hh&{6~7g>|E\cO{g>~0,SkET3h"!Z(uܶR(-Ն IDAT6&VAkqe P'W?B&WDkEPGLˢ)IBzX24P {5bwㅀ7*z(3ذj&T+euy2"V)tH!_coy9f8i0*dQ(/&2}цݟ~yl!%Z)!D0D:-0ie1 2yLsp+4MiE )mDTR)22VQ|qkESُ0Ybs6}0T,y'/!TzF2{TeYEz{{wZԜtQl2/}֌bhCb.i5 9=5JԫSZʾ⧾ӹk6!k藆vU"4-0M2Q(bf-r!$C& m( 2baD$% PQk2"Z(Ea\7<睼dƾda>޻h`GuVkpfoy4丸=?=kxh;{ibeH+#Ȼ۰7C-!YfBH3Q|BF 5JB]R`P)* )ZUyZ_(b5JBRu$c18[1]SҐ3[kdAϋo.icFSMms#lWjdyJФHϡgw!圗Κ=ha}!%Ji(1 !͈:z`L׾#) * "%đ $Z+t>*pS5m0@>a5&˕QA@cN=6⦛M\M2-$qLyP(xE~t=t}4RJǡ#GTv5[O7rY4!F| }  i؞B(Jhm"w#c*D۠ɵG%I12(l@EȐfe0L !My)aFc`.]oWҰO1-Ŷl9:*i8Pqq }뺸1>r9i܍{0|' ۇuh ?1ha&fm-zxQ`~*T<ޱõBi%A!$\affLaY2ieLӴ0̨>aJJZ@&̢aZ"(;"/_'YI]vF߻hF005P_^ Zg}zkz MjJ$XD..hYdg֌2d8Ķ5G'CxN@![;DJ[` !M! BM&*mJKcvii̘Zt]y;pZ ux;?Öcd\\\kuk֦RvHj8; B VZZвn4JDs+P(-;pV %%Rnn*|6$|\OvZ I|OX48d#Fh4=.ݾzqu~6T:1UP^,ra.|c +d@-*/ vohK"  .hVZoי9HECٔ>+033zЬbA@8ó-q"0YZ'utZX(f\8V >W hiڄT*8M)x~oax5F8Q*f{#1* DPQ @ !<.@!DTbu݄M8AN=j7,߹7#aX$iNL9fbc#`ZOYkLHOrR9ż%\o>aX-%W拠\SLJAw>,K&c& <x5OY3g@jWE]{Sc_466b#Po*˻ϯqݫ{Zg,+7.T8"rXx lγk߮G=BEsntX7CNO(]Zޠj 98?bXxƩR{}}Tn ߟ>wGiPbU fV;w5 ټCOlZl-ٮϱ36`cF~ķhE h~$M;கMfʴv/K?.~n]Ϛ\3/E0˺}"ōw?KUZkt;2>}vƿ{\/DJxő ۏ -٨ạ3kRQߑM ~CXaKI4Tn?^bv3JQ0X~G4ݐ|.Kg3ԣ>_,c7Q&H@1>k86Lc]ŻZR6NPk2n)U*wuP;?V.>W'7ݧ0wgZ:D[`\q]/۶=ý"hęCrg)%7A۰"q+_Kͧ.R ºq]q(3.Z g!8vug-ȣ#ulډ9-b;3SPpN'˓oൃ/_oaza#NA[MjFkx>1a?o~rPƬߍ󡋗NW+hg?|~ L' 2L™7*A pkBɢuVFGGj4M?4?YTp=e+W 8?YK~pcY\lkY4-/_U;zԶjޘ#R*5OFD>]wb&/򲃆'.gw<|ibfFK[xqe~{ҖgEoi-ep~/̘3,&F'/}Vm=>>K*ŏڱ5=u]~,ۯO\o}ڧMUu#{gq:Ka\ώMOܜ S> ֍=<|tANOӇጬ!];H3;Pͳ=G~ t_9fFcǬFwpn_2V792Nu]ßmɭg8x~/+S(Dݤ3ϛfpܚ/J~>=}Vs%#~4]u6\q,@KlՉh3cQ*!ML\MloTV4j/38#C$><́C ٚ^ cEݦ9^Tp_C~Ž$'\'ֵ\xr^u~Xy\?FxGvUq\ 0 `5|84oWtk~|56O<0T3:v_gؼ}k afc^qBFSbC1ћqtR2oggN`o-ovAkqԑTOl6i63@b\YK>t=Gb7߾2^Ah ieh>3`.ٸ]vym🷮Vrh`?uL>Av#2.HO28d2;~NKvPJ 4HKů~}-کZӰ}J^/9v k_-݅z.kUhNme@d%.,/lq]/-/>/)ްE鸟fƘ9~.s3FҤwz S,bK4Q?D19%{,{ 9y>L\N )zGK5:9L)JpxL:3o?_uWZbAR*)c0V ;O'eB@wwN] &Umbȵ~g-7{6҈wضnx:tTMlb:uhgc{^\.G`of1[I;>';9>K Wo7=:H155I \) VP g^Jy{Ň::"m30r ۩l$ӣӀ0 FW0:hyRNMa!Ce,ZFА'WOpa=5n?'!.7sC8=VwvxdM&WJxTBj[pUTRAK"esB۲iG|!&IFl~1(; ;1u%Eȓ/ R2v#4$# oJB&M-3Zp/_}:lƐ$gN͖=n>M0wH!iVpS8q8bZp=WBꮡr1s/z7몛nS_qZ4+ I~\.'1b8=H!?eӡi3BAC`F͂J5X~y{I?Ba>f1pGOV)o>R</d,TTSk4b®~w}5 _Ŷ8ƥOIb{izjxX.+ ɰtG8hɼޕ1mi4]l"KE::Jsʅ,bsf#8eA_((QL)EٌF!48v +kۻ𨪻sgdC ; hEZDqylk~_}ܫVTS< ,IDAT@Q\싁U $dL23w9sMi< LL&{~w~{ߛ;3=4GxD+۱u1B3FԝV,UGʕw*Sښ7.V,=/ Pf #/ph疢^c&&_z &_6 ։jqӁtngsV6 ð{/Bab[30B(0"nޣHCR|m'|(6q`fނƝ(,xs7d &vt DŽ+Y7P짓|1I vd3uE/r( ˀZhX|o%m0N WuT>yZtCWFzzɶ:w_m7K[8=_Fg8}d6nPXX2H4E-hG 7y=.8Sok҇-6eEvUQ FABg9>]S=nli~45ob2Ls9|>=u4jsВ&suas[wǵ=C-TFK$OG='T/X#;,ӯ,SN/*~E7olW(Ԣ)ҪCe>5SHe:A.PΖTw{w\p/gJDnd9?|aAl@ی[Oz꺎D"!q~/*Gᡢ L&@!dMs*ثד#5Es^\|ŏ>YcJiѨ8ZZZz[[YCˆ6hsX3RA_.riP,gj)%ԊH(?Z[[H$؈ۘFχ VXX>[ eڵ*k f4{f73p_ w|uwSOh=޹Cv*(4UYx򅹨( ;2c<6`s?vOnrα3(T*@ i gqG넲Y73ch ##J?:-KXOA迮nW,[mEQ {.Og; jHejLDK[;}H!,S !*^477Ja7ʜ߹=(2m0ހJUeUDlBPXVр*du3/uuz2u(:f0¥DN3IW{oѿ80aҭ̛>nBݶ:G(**iH&f(..!Tnw&N˲h0hT|LnYH C8-TPN(JU:E۞hn@8810sN@ii >m8ZYx}>p5j^G0/}ҡPHz SL }׉1nCܡdvZ`-~횅iv^i00nTyDsL/`߾}8aH9hd6lף;wM ֠f~AϚ]fKjERAD5h{TjI~JƓߝ5t)EVHu 錎n"Jv4cf3zb;&uL0J[!r4d{'Vَns:X3 mؒxh:[2kҗh+?ߨG!'+~P-洘bHDQDY\U\@ڈ  +:pP/.Λ.[+6ѻ&%GlG^Ǥ a 9Os[[,9 [ T%&4h!G7TkC]jL\`̂x8<(f #)59)BR1YU!^<➂Ee\u`@u 8]QTTP(eY^,Ͻ~:YF!Lp`D9IxÅh%y Mz +X )ɋMl42M1]l)PȾs0(P+BB !TE ' -i^ b}Oh>،e+ڂbTWWDmc 9'm!8Ŗ{XBܮ8Vfh30K,Cb1H `.cέ.^=Vơj i0t,Б͈ar0Vx4lCCjkk~x^TW RTҒ<|&c hjw5/䂓f>{z4~!CwLYzλRl9le"><xZ M/#+ fSa6Zte{v.]p\~AJ A?TYDKxcn{ѳ,vލZl޼Vb1TWWO>|Hg=AR)ٳLHϾjzuy>Ni;I]1һfhI+kBo4y[|YBgzh9;CR3Ŗb#%8Av0;R[R$:,ptzfFf: nfQYQ6;>F())AyywpqQVK3chbR)0PZRgarژǾį>zw)5C8`sϼX֛/_ĸ>qƆvԜ gaس˅w4vVW2B6B08<@/RjW, t2e"3 S4͑$^f2Э}ke:nG;ۺ@g>_ljmOŌ?Ig YGWh'^9eO6580fL"eIfYbʞ òLK U3XBHې|~6H_:.@;:luRTUwLw͐+7[V-P*g!m@#@&kwwOA1%P~Ckk+2 JKKP *JXy/~~J#Tsȓ6ٷ];C^\{ !6s,﹩d8]7e=q1b{Xt&,,S3ɮf1'h=h9 `pN?+:)1 8-TUUw\eZ؝QB ż%y %Yl#SE] 0MDyx@qbR@%8 Y G*P\TpBRQ>8'+zڛ_~zȯn?9+pegȶLSE`܉ߞW9zvLvBym77mqņm3\'9 #\u<|4{]dsLrfrg^ޟ&/\0g̋yk `ao >?^o}-(w,<w䙹 F љH ܡO<WfH PVVfi d2^CKci-C\?)& AӲ|ZL OF4 /ؽ>E/TФsJ PDf%T2Ȧ}5⦝_i7^~#u_[SQ3)ȿۻ2rnwoc BXL2*:!6ƙ-3d,0#t5%M?yXjz΅d\Ŧ]>m躮 &6p3M:Q6|pxP x+ IM8~=<97>o_ƻAހ܁Cx(BXY];<uIؔ6s6;1| _me݂HD3c`*P1@6Xq;{"ՑBϷa}`hLfb_hs#m]v0I{G9au]i_y8nfUB;> ?Zp#o8r1[zH+s>Ɣ]̥*gH[K`0pn[3[&JMӄ%ip-fqfY1E83aZ,eLL&}Â%rs&zXҲApLJ\xtveɲ Ī!ZquPZ/?߹3Wc۶֟L @at0 ai caB㰸(XS[Js[tC0th_H |>/< ̍Qelçl-2qV4`y\@)ω}*XNC0 r!F5N)!hШh`XQQ.Xpmg$eſX:[6uj'x+**F8w=;GٽƆ!azDiZ-nq8#Lg6DJ6HE9Hƻxx((Eb"#ѰnC=V}Af ܝRhH‘23h3 DjФ7Pf̀AW1>xSG3">aLV>o<냢5>Z{t(.BU3+L#UH&R=37puEELx^$ϪUI!0Zv俇V]QPm-F?_[[tf55}ۆ;hɤs !M_TIF kP`~_p=;w756'H*nij,5Moi)$YHF!sـY,n9@ "V^pg Y^ѫWx6^ _q5iG`8Y?3axzL` ====s* |IENDB`./oar-2.5.2/docs/schemas/oar_logo_detoure.png0000644000175000017500000012703711757171206017204 0ustar plbplbPNG  IHDR/|%sRGBbKGD pHYs  tIME31&% IDATxwdWu-ιnӓfG9KB 9=8`٘?#F<@HFB$@BhG4IhbJ7s~{oWtH}o=}^{mr;zy] ZZ\[\kqu/u+x,F` U=BPBXyP,~mMͩ鱑+jPk5}VaNrVU YTU5@y1~"0Ƃ)+m5&JR_P/f T7/_UWoylk1Ei#{[us9/={)+/Q+bFJ8zyJI 4J@I\߿gqLؾk[ۅ'y}fUv{O-epq}`^|;W?Kټ/fȗteKOXSW@ ^7??d(/j <;{8$SN3n6?KI&(FGwoO'}(DeY2KPq(  p. D M!8nyf{(.!)@犴Ǯ1+s_ 7|_lSO)YY`#w?;;w7X]zbg vy.l^LSp5^ W󦆇E:CvapB0֟֝t. ܱWw&wG|l~ V/&.Q]9tsD(JAI!(!sO]916bGtE5UUpKpWar]?o'6/yobxJyfa,RgTJ+FĀ txc`10 ?FBhC ֧{W`h>(PUAR\.MӠiE(""PJaA#\ 9W8$v?X[7_x|޹^if"fs;nCpyeY2pq45(˴6[8ZIܤbvS |eKտW .!(f@0 LҔ IҶ p!߲.McV aBuDQ(9Oue4MUU9O\ ;X ٛ~2ئQ\s}x~3;.u̱֧ι^Cؿ߉?YX:w|G@^c A0ffmC9K K;Xq/y>鲋NNfJXx{϶s"",;M=ss0"Nns~IhLBblRhPٛ܄סXdOC+8d1Pϟ= C8VfFJ)P(fuD^yq-=WG:EV??W+ ( bNsBK9Gsf^hMA@-!G4l Mlj;4>2Zim7\k,` Qs(Bрu]GXDP!lvKB`?pxy;ֶKEuv|5B8. #fQP%aݘF{fB]Bq`%rd<4=SP@ąfa BQ) .d. ױ|0ZHPAMPTf>n1==;vnZbppedI"RG; Loȿ_s'g\B^"E;nǞߙ\\A6[`Zzr?Gsz?. ;_Ayp%lJą@}BXL $l ˆJorc @\uywR/&%qLMMl""Ŕ0I5W #߳ ߹qQ4+R:^Ckjz7s÷tiW ӆnf/6<$iP]~!Bc "&t3QCn2˵ ~K<._@>BŶmd28E033]v1\.l6r|TLS)~58}7KٗN_w͋{ [{j/B2 pΠ6{QDWRLKe ^Vlvn,Y% tJ[5R?EMv^+[U&E䜑;~rGo/LjRIA D? UC&߇ᣐ/CUTϘg!R]$1B+5MJ 8)Xv C9|ߙSS^i)C>0LTv;{=@*A80 xۃ|qdٴxiZ׿ i,+}un"J^1y^q0>>vRj MӺS6s=0~v{ غWppˍ{vE3* .8f&v5< P> 0-  bB*jf@DR)mM#[胢(RAEQoѮ**9 _ԯ! CDQu]rt뺞ʙ!Lis&dC'?'QqEI5MK iT.&&&`YJJR&jm|7`ˎ3o8z?XL_MsIɔ0o'P۷S? "g`Xy;'ybL(x-xpE|5m< , B-/EXLiZ!;['2Xr& x\㓍>E뺘d}0,TM0 s~as^`xfNCnv'9V( ,+Mk{At8,vccc`aɒ%)a> _i(UCEt&hkQӘg!FE2hAh=Z(43^c^k, @~r~UӠ(4\RQ(|/;Bӫͻ{O]WT z.}Wu]LNN/躎l6|>L&!l۞`=%0((t]vM_y7_lyr1E"3t>NQ.]bclh6OAׇ>ض}qi.] ~8UVee_O]}%߾; /;_wњ|n۵yaf9 ̌m/cpq pawb) 14ׄ33kA-d0%ع8}@q!l4A nL"ݐ4UGabK=#%c}!nV>~'ϱP͵^lL_@DՂ8uR b1W'B>8طor*Znn5w]Zo[؋%4FݻN!U(#ǀ Y&uZ^{ͩ=>[r}(PBUh|qBI[140<,<1Vc n!1  F[/܇/A̅΋IUvZ5fdd|w]( 0-alۿqfubx+bz-Q4U߮x5lfPeVX@Qu4jhN33 ža膑Z%Rzm !44A|牆ŪRKk8Pش}kV_"6TűtRDQ ݻvB6E__r!#eYXlfff01>?~:\s˦c}}w~1oT,0.M37M;T]ǒU'BB0aJG ̌@6y0OG4]T=#p0q=Kp潣cVA,1pR1F0ʰ(TY';a(<]ѽDkTBDŽʢ4 CCC333Ǯ]Q.*,˚3QJQ*P(011w_0 ]\{Oh ^=ɽ(ô!QCQxp[54'wmð \yA!ie p.`ZY$4 "niK2BJFx tnn#zavq$Ni]]ĝ JV C׹m_F6ij#y]\Qt-y_KQ']QT*l FFFUy$+]vZ Ao|'wڻ.[AO?=V"A;{ѝhAQucS CQ sY6t+Jz  @]>6Mkd(9-p.Io#Z|BМQ%ؼ2 -%Ed';`,-JgLIbʩn{܁UZR$j(=%7dvnJzv$@L"BȔlVaPUj53ufxiQo}wׯ|FU3D}Mo_wg9Ur#&jρE>\ë˗@(  4>RQ1twsɕ 8C" p[p[5PU|A fde.Va UOES/vg1>78q pY8j3!'uB|W9Rq 1>>qP.%ѣ"Ivw>fлVC:XOl}mW7dQ xòA1^s(.aX q| ;_1?!>|.}6Td J( О@p#`a@@-(!S臙ru#8uqZva{q,8{7Yw'zP7by9 UMOOԅ4'9 !`ƼEQ|tjUU˻{Hᣟ:|CW*` qiW-.8`,`Ru9 AhQP lT`q_ Bx029Ї൦6v DfVV 3itttEm5[rZ񅿼lAsoPzGfNK_B 0p@aa`y.0-$Ev ]lB [ 3Q<֭]'H<QȂeY, $LOOu]ٳJR۶9msl>&39e1DQh?ngn;7{5F{kC?0&FAPcvasS -)qN<`Q\\f& MUӨLJ@bM! όq8ν@:ht ֭7(>ʸrjY MW;kyh6޽{j|>|sxڦibٲe`attC\FRA)o/bןVoX\] QnU׮![@q)8 830hј9 9l ȕ`YY膑Rؘid#%%Ơ%V8E pYd>TJJ"6nik^TKBt@Zh4ؿ_iKfsE΢Xd cؽ{7:,YKU eG6LnIhN]Њ19l$BK01 gf?$D;v] \T$*Ž|Bd*3=r ׻bRa5ti+vⴵC<ȓ>qI|_NB*˨Vu(TX1LLL1]\{{9kB쁍w}{^sr4Du1v M7/cc.(U03n}ׂn搯,𪓐j*(HӚ0ʔPB@<. I>?, E!Psi,n6iqg9AafL&zw%Cu.^FXV!kr45sR#t|[ 3MiZBfQl۶ ]I*90 EQ088(0::7?һ/|ۯ/ݣϞo),fXB &tΤX>< +|K0, *p fxMHtN0(~|!9 xpy"(L;ۜg(U.b7("` bmdQJSNt1<[oN=~E8iGrEf]E>@tR.;@P&͙ YBP(P(4oߞ ϫ*FFFy>pj|?~x%>ps?s ;*ë wAb**p[p:ECR)E x@PT5ܒ(6wPsTߌIEQxnk ^cT5bPQe: P lGXAZMRpλz7T1ᤗsKضSN9h IDATt*'U鄖d0D%4MKWoy9.eY0 o/_b8)!e5G~_>\\|pc?wk] TmB8K  B $0D7'7[1a+X>B-DxDPJ9X tӆnd4݀j TuּfA__aW %QJq։#1-f0 nꤾxwAv1ZsdٮBiO:chh(X166˲PVԷv'ÿu7qݟS n\쩭M?!T^ h#pR4r4L4&):tK1JA"_Bp+2cD p|-`~UaaXy+K`f0"JGZMwvFh6i's>?1`s͒lbbbWSƗo'[G^MD{U(1-zE*dÚE6hfvLLS`)HpZuhV6/2EfV$S_Gw-}iy=0 (-49"q=aMN+]2#Bs$ATUM4220 3޽{~ cn\\xmW/Ň\ `'?Ga{t#+ؤ&^ ~kQBL2+#D(1HerYa: 1v2:CpJHD\.EeA[psģ@x@mjSL[;"pp]K]dzgQvL&m7v/?cMzwmC 익PH,']նmchhK˸k.4 J% ¶.¤L&36lywX{_G{gfEBĬ^w8Iejذ U1!! e cR,6gOE쨑 Q$'X$BnB< @]խ3Rz;wL7n"rbFf! ;Ig1NHE y,fi f:QU5arHkw$zСڅ~0틒filVaϞ=4-N>\-cjꭧ7Iկ3h.xr H°9CxkkЌ v!>GIFEp]]ɏN q/d&B3Bzh.РEf, yDAKP5CLT݄MLhEPz,KY\#ŕ}KilN&d& 'IB!=K%$ $Tg?'b=$tʰt3OIG䵓 zoo8snkK6'_gy M߿Hj6dx|hENr22d;JB?|72A݄æco?9|-kq#lWeYI)ر#^TR,UxDžq͏6uǍ}>o cOwų[F }As*3K9LG4ŢfW<>Et{ }A{RrtV=%onwaE\Rh=zIAjjh`rrA^ 'vnA5.UbLXhƺoC8P躎A 1z^xseJ%\| |GϘ?{OK/+8מC0ll&6.=rrϐg0Xm=?N,D @rȖ eɖbujf.VBZb[*ɴ7Bהâ_N%GfW8/mG ^i]$(5q}|QS3lv]nh&8 ntubj ;2ԃ;cimsc~x8a6&}b?!׷D*){(U^/&9yh/#I>|)U =Xݻ[s)en~MlӶG.޶el3Pt0'8 f'G(i硩zLX 8Veъ4vEݒK.R/)h@&xpgJYFa(<c N|kY&>@9GsfOlG28NZP(9O=vY<]uI㟴F'驝$.ʚrn`0lL>(i a:-f6Ct(|Ѹ1ElJ]{\iE' ϒ!8IptD8,J;9 gc9lڴ&ݠ06m/}ēcmNm~sNoϘWv*`NV眧"1V{mhkE{ii˹VVN"%PiYk8ꨣ quLLLsP,.qAȇdGNk`wߏHaQ5T36Ra_B*q?O@ 1}:NB!lIp%QAziiyp,UX襔6d4Ռ%SJ)?BPUZ-bFL 0ڤ*|`>ΈERR0yn9._م\"m HRPNO}kpUUA:sRKgIӝm{$L DHo#*  gn 01 gR)R)j5urL&B΄?IEd*\aq-K4U;C Ƥ6QR3} 9sw&',cN0pfB(p"9|֫Z9(hPbR̖(Ͱ`6T<ҤYC'el#IYB@V &p+,$w)JUTH$b1U{l߹x.Uג)U&]ԛ/D6c}}}K,<σȈAɊ~G}Ų]Q EQԋQdE5+:gӼDNA-&ې mYa8crCU FB?,ݴc U3%1>01G@Vm 2]eBƲNƏs=``IcLGɆa(`ۡZ|^D_^BQs @yV\(8`Nq˓6aP>(fF?8c'_ (U=k R˻zr!'砱 KGU)1!ے8cm# H).ǣ, Jj:4ɶSJR"Et|^A Xp1(oNsTQBwO>j}5 Kq%M6oy`O"tsc۵؈Rf-Kj YfJ2&9j4^gK4N?ײ}I{&?{^MNZy3&[[|,sJ pҜ&ԩ]P*ƪU7S^tcz5!8wpQ.d^+ 2?J9(lҍ)yMU!Ll^F),S0 >Kg\q Tass7yCW|i获5x(vr qw!KTѠ~h!FB$A~zT+E:TU:!j_ $NKiSq'}Z;-J'Prl]º%͋@qw1};Pu2ܷ\qɚW\q1 R0YeӴ+bNkTQc&ZTi}㠪zFQ:Yݻ7r9Js5m·p+"\I7gjxa=|R!ixNY"]Pou&"|bb/nނaYݴy -6s:5_1=SosЄ!$NDLpå{HR1nCzI%(% $u41X`Ԣۍ?`,dK@H1 Hf#V:9k(@ؽ{wQwBsj&苎-#]]Mn^Uxӟ-C롻iWf +έEϬW76'J͘ TJw^t478 !R7+9?3 }[7'1). el_~IG}+];$@G'. %-gt";rr"L3I$*># Ƅp8E ZF&MWRQ #SԿnPZ UT(T( $(8XO>lvH{_oZUE(\rt#QrI]+p2%4L {:$ " C Դ'Q{TsoRl*z#dZ͛Z_} sNR 9[Ð+P"JBw*/b^KxY! HBn |gjĚ nLu7Bh"43 Ͱ)T ͐Q3Yt֤ڄ:Lz} .vط~SRkwiن+.Ysk쫳Ȝh16RXH%EQo¿aϠfqÀFO2pkKW މ$j(WrA|#zO3td+vXE?N 0B () s44Z͌l{:;H*dBSBE>g1kݮ!C@6U$b ͙)F|vC]h؂%k~|[|3.KE,G 044i7r~t&$JdU<8jY?0욷I4s{ƚOޝ4C_V M\n:;,n Cg!yq((*4#=NT0ī!CfKY4}vsGKpxk#[@UԎ@.|);t@>VHpP% +^y'=/Re^s~"꺞g_|(|O?71R IDAT}54ZG%K|  >%^Җbv1{+n|LݮPQv}]rىGe,CڮQ#% 2 ph 2ߠ"X&2rpx (P5*I, JB;6OW#9!irES?xWl8DSn֭Zs&ijb3.fff099bF:"[F)MϏ[vNuRJ:ǝ'xoVC}{/a:IBp(*fi"D6T6G 2 \0,xXKGȳlZLjи 0r/IcIN=>'!עƑ<]чɆ6+WU*UmMLR CCC]w<c\M{޲,na B3P5]2ͱ߾}iTolۺuT7ADQ@Gٳ>5bU 9]elHEֿBHw,1[rLtxvo'F$E{%eY]Fi+7<+p̑ x9@bv檇@akd6 S{ўދN=>.Y=ROK9e;xfQa$6J 74|eQ,;r*} &_&w)3 y;)S1,p\*^^1y+hBbIh4x撔Z#*j'2b1@!M~ 02wiov ]w̥>|o0gΜQ#>Qĺ\6\R.7~* @*B&:Hpg鷣!`=xkuTu/XgeԮ&,f7(⮛*+h2CQq|}b[T*6*@(J'}Ȕ]<=Q͍`ෛ݌il rra<T&岸i.o8sbF.ay`x7=''ς ~)4Ҍ FG.oE# F kn@*61Us3Z^2jUo2ujdR.AU59b r Ye-4FBKiHD 3 Pb`BV,KN⾛)L!A˷1_ml\U,ST(X,&Fo®i섓H^>Qx=0@aь ?Q9~lw*KMU]9E\ r-L~%)S6JezL(wscHlQF"D)"TP{'˺SnA5 |>Ao( -HeRb/}{8T=RpxJYU-S/D8?0`A¹I6i>x' o%; AM6\* .S08A) #gߐc̵uؼt&: I3?Qɕ_FEB0 cdoS;x̒X؅, KqѰy%s*ؐ@e% bDE}}e!mT\`FI6LWKl:PYOj UbKSy{IǹsB+7<1J! .^~d2'A(=qpW 'e5LFe+?z)'D8QA}1SS' I$9 JF) `ݠTKASI08@FHB2TbbTUpjOqKΙ<@-m'?.b$4O[ej߫;Oylxn\ii[:,XJ ^}j'͒1ҷ7`0 Cd0 ~Yt$RoDSƃ_97jL&#}¼@ķaVZ"((["QCU`="ܩJ~%2"cTFǁço?߽PQq (Zkg|T1:&4ʦلdzvm>Cy*brDU3{_ZL@&Pok"OIw~^ґJgi[OSϪM/RTE)Ri)8Ÿjm!orfra\3'c?^h"ߤU 8a"3XT(7Yxyq|a*mN2zAb^Q`Ur-pWi3L#Dn;EUb+^`{8|Bhy;Lg 1ubm)c^PeWSm02j ^T{q>V)4iDdLUyDq3 7dq= EuAJag&-Z MninAg[Zhn"АLļ0,b@pK8u'pԠ CFO{#c}7ェ\j^ 2P5l| ;5#XO6#*ҩ7 hMX9 q HWi4Uvf4*{zy9TĒyx$4 \H%ch'1AW {b(VLߋO&ϛ~߭P۶7MS/Sփ7?Q΃ƃ>wmN!o=-sІZCJa$Gj',t]OP%h[yHj2B.<;s2njV-A2AB0NIŰnL\j6l፽gb[3~L4?$Bv G{Kf׷?xk//|߼u c k!jGuߚ5M l.*(eJŃ/d5psV1˅ !8:Xp2̜2kl6 rŃ7b1]2/;#a[pjL0,֧YpfMbq%wxw4>3 / CYm 9 䨋m۾0iJk#ia1Q&J[8trҝ,2oQF`”Q@Rl"2GQxOnmM0of+Ɋg+^xe7[W*>J~_A%GP1xѧ{| (rb ގeս/(Orn\ŦkpA ~u3夳dP?H4MTNRY씈\Oxm%X؃S$weXz>D{A<u<#j͕m8\XSNkV ݇_n.k}:MT֛$-9̘1C,*]wȖa٤ZV`(sDH쐃U&JbMaD3e.β,,? {c-^JTR~^Qûe 'e)q4Щ6yo;RxVMpQ*p-R(1i+$}Qf۶ -! RDf.&Hgek&5g&3`9Ӄ'.%l7&w]׬7؂};87J M;T>?qkS1m-c]+[͛?q$1ozKc +AA3jYq0[%wGꑸ`.\;a#(zn")8U(^XsG}bn3'>tgSO}! } *U W]ߺ 4wQQ0Uxj( yQ2֔_޵;Ʒ.mBEaSbt&x<6l"s21w1:e_|*6>3ZCb&aF }m͛{rQC'yi4N@Lz. 8Idr`D5lA} 5- U]믘 {er=Q~B 5U'\ 68xNS ,ہi=1"h˦Gnmu}X1w*15&/%|EK0L<\Tӂ=-Kpma38v >ኚc|&213eQ{͛vYK*{Z1? MTpxο_= $WP<r eӎSX1B]M945&jho"[VQfґCOWn*ؼ ^s{ö),F8X"T:B)(`q-c1DzM%.MB< bg '>x V-oa{=+6(k>eUQ&!W k aN_!xmYLh[Q\|2^yl ?… w,Y.xc_/6n;GDAazBQѸL2[,WQc2_UGu܆jQU/NXi̒RWhzJ.hnH59Zu|?W-Rķ~:|=_]̥T1c/V/4!?O_Ë]K{9ߔOa_݃by5wpHV;ȧSἏ*@MN$ҥstn+|= xz$+ TU_ !.jq``66l>'6C}9Jhz:W"onΜ/OYh o<=$lSm"')/m?Ջ&lﯽ!֖vò95m>)M+V:+zc$7_JjWAImULvH{;'FaD2wRT(/{sK ǥ}Cāgh^6ұ}6,3SgJxXۅ 6b})TJԀ5a?'܉ïHll6tcpQE5^rUpB+~#\( %%GXgp,J-P@nh˲(61i{&[Z gOƒ%`lC@ C\;QQrq:jsTbQs{%D-4#!55+Plb t˗/im?=6-9,\pZ$<$ #`_ ![0X|X z(ˀ 54O-7u"l2 ߝߍQ0(Ck،UBF\7R20w2}RXSSy+vөS;qY 2Y;2 YӴ|{ϠXvD[Si(`DQfPa @SVô7v2)w_;"Pe/ 'L #=Vi3qQX~\hr$y^zM^,2D eCN%τ}ۏLru^uoH[/awv'm;0Ma+0L`;0lT:** ˁa:0L gO?Xݴ ô~<)eQߵT1m "ބY4 [A.KߨL5>1PnJ) %MMNMhu$۲%0!2 (E[av_΀2 x-$($2Ec/Ab8sJ3$' IDATY5 EaQ xK9FWG;]*nMX-0 ԉ qgvQb׭[SfzR/+7Xeޓ(5Յlr݁*IkgM{K>{{n\)Șo^8PѭKMƦ'5R8+ EC6{lKce`خm:`jBeU?^\{{{1<oj.^u*!5+kaX ,2)LҀzfq/D<1kVME"ı 3MoDBm ^xhpm;t@r5ƓI,^8 PH=Q0iƕVA0?"zՄcAKfh1U%6*%3iF*Nj: tg!FagNG!_ v\~*$y! /CKS~lgz++ce<V5xFm0sK$%eY(۝8W_Ac6[-@SCRI̙9,B\6 Z7cmۈNZvGog2eڞ_xXp6cI\NX[H)#x7g6Lmn^vIX[Vm$CfUa1/8](&ڵ'jU`׮cرcx^>Hʋ_*o|b}prk!|tl?o"0Pп;R*l͆ըV#I277#9I#]ރAb8.[>] gw#NqI>h?x4+1FGWG˞K`Ư=%~ ZA$UrW?&f)S;T$2IEl)o/bT,]TSG7`ܹKݰܰXkS0sz4AFG!85c/ӧaPk- SG"U󥇧p"MH$bB,]#·^d&&M:8Jw8rBfγ0Ljc@n4dk{蘰95Yҍ= vP*z}hmLƵqieZȨU(Q@|;trFpSy[6mN25;g{{+Z42$KvM<@ckpIlNob=`S%Gdg>laR* o%v&{aoWiwy WԱ<9ׯ[/T zsHY`Rw}G08REKc`cpR\b6~׮ } ̻ @l)%F8%9L@Mdg;WA%ʷ"ԉtdZ,Us?[]Q+y,n|:耦تac~_شLc4EK=鹗>ʻvT߄1| D#ȃuCOoԪ1(u|g0>u>TqpÕSp`^=3RAUEVkJ"n$-JH&> ć<'@3l v-}y\u{*l6 ʉb@%~V)hԪxpH&b>~+4ժϔ1$q7aw3>h"a("Nغ &OM kzG1(saaƔΧNJ Zb3Q.rLSBA*XRm?lEdGk+[n7n?oŁ#g0RŚ˦1wF',f JzU3o7kSpժYzw@7- dSqSRFT ACY!ϋ :3 vemul)(5g6Zqe[+a%b6o;֋MXbYGqtoЄ}mXFZ" q{g})Zeq^xLo-WL٭PU)7jw$Z[<}SGN/-Ji!ri<􇕍CxT Qd|vwt{E04Κ L7˹DnS0qv\u/݉|'JL",6y.̗K1oz ^~-@TU\2:&Fq;<7Ɩј4nl޳w޵1cٿ_:R(fdRO뺨ef.e XK+1$a^a/;{ Ί`ۣ ZdOP vH;ǶL ymxnb-1mRNoƖg~uϘ].4]/ Qt>n̽5"ܦAijޓ'.Դl%aHX!4M$Iѱm~v<W=K۶h5q`ۖ{+uuxYLj+n]*Je-S(Y riT Il^݈L:!HmU\y477#ˉ&Pyζݧk7aTHgP"xs(^f5708X2BV,񠇗$Qp~GD:*٬P]Te0+/hK59x.HLE˶mRqIx]Β u\i>}dp8;Oӡ$9!D<{lX`5a"z]Nbͼժ ^t:=*>i" d8+Whۮ#6T~fͮ7 1HYҍYus-Zt (hB:plC!\A9HdAtQ^8sCjy§]?tXP=]Q,E`b1aϥBa\Rؔ0,UA1xa"ee44,(2OQlq]3aE̲|KQ9$kQ)`V ^y(m` YxWJ8_BW[DB YFyŇZb` U|Dm(R4z!+VOd {B?d+ U4ScV4*S uDžR9cCϩ{2ZeT`V0*p,!"ʢe,͈'nօK0gͯ/|W:oq7Ju8#q83_rV,\ K]2AP7B&uG 28z󹇄7 ^$+;NྛK)?/61UŒxyIv1c7zT c W,Mێ02]+%ؖjQULҹf'E*C,Q#=-_~O^Cvļ_Fjۘ r@O.XMq!ҙM*9W[; MDNc߾=`C!\3HeO$0~1L;x+AsrY>0d4?|sD_fRg>pۡT ʕ;i(˾m2"+?&3QA3[G{ޣPuv(8L #^A$#B" "Ș덻U9F\ n^|Lm":jJ"N]ס,SzNDr<Q5J),γ\)drq2XFE^s.&.YmܰOz~DA01lXYA0TaR`hhm'sg^͐rhr(eG?uVsBEa -uTϔ;~Ɂ3IBSdR h$H%hʧ i4hΧјKFXaK&;?x]3_.ʪt:FCl zMz ӲQ(JG۶ 2OeŤ&ͷZE sle" \>.8&"R( y!Agt:Q2S]dl3FnÃPV~;t/o-v:i#iH5$5 T ccҥ<˲| ק1R];^/C[K$@DeX"`vQ"(u-U(`{`UQ2ށm+O(CbxRmuI}+<]fY>+Az 0eXȃP<ࣸ-N(s2'S ?? ,QU$A>|XLR744h pVv 8JK<%‡ 8qn fatI&& !2r{>a؍aQ:$?V\^;6@ @(e*2uXF cA'L2i&l8z[~vb>-W/[1G6˻#x +O ?Ӄ`gcL""z sHRr9O/'ewpׯV"S{0$I~|S4I6E"]&˄7_bq(0jH'TGlgT|Qdže0%κ`ۂ#kFyH2`P׬k)=`Syk՗?s/֜SϪ^cj###Q"t% +Ô,QET#A]ܹ"yUYtVknkF[[[@يgJ1ظDÖenR3hQ6z綰3wr+ \-ٺVɘFr~~{e&0jm!ʡk&Rh^V+b4fξh6G]8! IDATՖM?ÿN|Sw1+;T*%CoT& b0 ׾*U Db1K֪:H}pPGԢo+˻.$X.fHxj\/o;߰0t$}aY4MT1t(uK4"҅d* U\u(L+W*m7s]@ݳ_My旿Ǟ؄Rru3HmDkXY.dASVKCqLpiчE [;H}ף  FX5s<<\r:p|/)S]Q*PTDb(2ف+>b"\.w$Rl٢%m=ן O?s3| [t\5ML9{&TˍANYEQ >5L_m؉w0( a`xDT i .x.JRm>Q aA8[z(<2] t[ pʀ/pg^ÓϿE{k !^p9n7lD wy* y\Xl/ubX̧d338w4hk<3C߇N(_WʅSX }b"Cc$iX US]{ 0XǗ/oq'ʦ088y nE)ETB__w?zz Bl| z|8R~v6%5) @Tчeǒ( RH3deYQ@ $a\YFX8('ׇ}$K*(ג$$DU(a>̞zClQ7f30 KGM=H\*(V_>9SW<30B>?dA{¿=܌f_u]ࠀ9)a;XBuCřE|p Z3fQؠf0J8uԨL,LHȺF]בf}?`>9Fn fBCy(qF ?l6++?^"f8}N=_jP>+CQ]KfW<.:Vsq j e1kwA4lD+sC`"hzCunxzRr,hI&!vgM#ronۺomSj|xy<̘9 Ҕu_r 微/^?tEAܠ!<-L7ޜGky~2`uo!KX,Qr9a.BFǎW-ő"D2fthx_[B00iҤȉa 0bYNoƒŐmla[9e˗z}?'>} P jHL0 >x@f3uڛܔGKslJD\PÅ2(܇/$T (lAE@p7 |(0 d7؋$aS&eS؎d\@ar);L&qիnܹsGɭVa\>ɝ =oZ¥Su;?_~o˟=M_D"Q1\*a޷oX1RRi. 8{A0ґż3R#g0f:^o؂Q`;C*,cpT? G!~ p9ɯrfhhh@WW;!&ԮQH`ƲmBA3(%3 ;`:Z;?O|DSo ,j@?e?f{/2/{/(THBEqbJ g- 0g)ʼnqYQ񍃕\4XI*UPAa|#%X&+ahR@`2M/:~}+N%}Z*| \Xͥf MA{0֕;O\]]]LI ъɏ~C ٶԺ~?Vز~ ZD$q-v E DT MTzbgz͘J-6^ͮy /5ޱȑ#gMn~,KL NrC"d0CJ)yr+~edY XDJp uS F9[RlO9,_2ǣq'L_Oaz4|;XnblP*Fÿx}\;S'_G?KgY1J^˓7kpx:sd)Q4T5s,jfM#ԛjc1[1uT|+, FSXH3nlqp!TNI6džƥͤPIEOAb(R՟%6e"ʯPNٗ-_P@\LGGǨl%͎}gwxQ~-u;Slc ş|fsjH%C!yɥPs-HYԂ#eL\+{_AK4v kJM(PS/Vջ)xLD+ ,W”UmB4Ǒ+w?|qh ]6]J?LG/x?Cm -vpuVY o uj2X, %*e╟Lg *x[ LrI0u̍gyn\LNd.YŘ7:/~#worLCexs \ דIE qƗ$Z[:;;}gqew? H<)Z~#Ĝwc-'=mWGCF,P:ֲe5ߖg7j7qTmBm[j8&!`tTJ'L6܉Rnԃ깟e%wR0g-1HH++E=%PT`@I<޵q]c/.7MJ$%zY#۩;Bj'QH4M[@#h(PG}h&m:q`$vlY)SORDr}??f̝Kʖm=|'>v;Jq' s)L(ȵ4fֺEgUsvŨ 0j=OqLl<*OݘpRN%-C0 X(( Xcg9Vx嵓ܧS+Q!ឬI$ 0{2 j Ł=hkG/>OMSV{l`@6ޕMMrkSLfG6B}0jR8#hIj\v1)]syȋ VYs{Hp˖X‚=LW-|!r9/4.SK{g{-!|6,iD&dou3?qvw z{{ ˞|/1?㿲RG c0>p#W$7[ިd+3ԽZj:N.qa̼4*;Eqj6*sr3;T$o׹'p1\ n#$WR.W, MH  d2hp8l"Z+x;|04p OkT\*^|ړD!PA,9}GǶOrppǬZw1Tk+;]9W.v.]m0p [Bg3U~;_QҨJp9cm4'8/$؞N 9¨NM:xM0( j JϠ*Å2ZVsb2۰7]C@0{A&j J)|>\WTԶ/R,f?eқy(?B!vX zx)Wr6%+.ݮb;^aU'PItL."&D%&\`Yff-Y`.Gz3o^n r諪|=,C')P+U~|uAF:'0MŶ+PT-瞼ra#a$vN~n MBxW 1o^>YFD{3#EE bm`.P#(3Wkp}1"."I"{6k9O)dp&_֘ ~NL8fX-+L~=Bh)8z4aaT*jfDF?ncәG/b"M%(4M$Ĩ% F+ɥ374slKdƞ}g.Zʕ=G^x(\&1Z$0HxBŘؕ,3x_/+5sykjc!\ FK&wÙg'^43 ^Sͨr4VF7ssŌ sѬRj{oϼs ,Nre[]\k/N惮2u"#+^xnrļ.vs^>gƗ'vО1FwZ y l!/{+d1N|}KXT+X $o¸)"D/ɛ>RJƼM_g#o;wzǻ&I+iShX$PRUj N̄_Je_L[ B>&x-JQV 9Ka1IR(W/-=>w?9ӏ}/ccoVZ1_$` Fl-a9l ^IEC(@BF!0T{$O&RH"}c|~|>[/@^TCPZ׶V5 d8 cIDAT{ q< gsidpHԊ >)2&Oɳs6]A >#kx&]1G1.湬
/OM=[ = ̧]ڽs ]Hafh_GD)y!L^z fRڡ).@bR#"DχQj(O~\X0 ӫ]h۫yuרjՄJly:b-B9?rcL,EUWf."/CBU_knp1FChnzԑf62fϿ'25ȷoNllbk;%mߙ jNNLC{#aJ<&y !]GZCVux"f)B/1 qL`Gne.*̇D,QXڒ:6Pm3녍W$(s2 -@h1{>rήf޷UVagpt2< uBU,kbbC@ ;v T#K?{o''.!60@G?|?>*YwS %g.B_0CVC^ »qK ^<0/Ɔ8@h0t@"o7/!duG՝Hü?3'/a`BRAXXqӸl2)JCh!fM!NQ!pPо(rR^5Za*QU#ŏF^gV_7ơb5<.j{bZ(Gk}p .e03xŤm.x@z-Nrs]6No=^$BHɋjQfշ)PpB G^~ RTՀ;mK黟 VdS{Pd.?}[`onqwϟ{Oϟuڳ6&Ѽ:Dqn>(z a4QR",\ꄏ6O$- NP<ʧԹ^"l# Z|MA6$(iZF3'/b#r6`'1{v/}+`-2 uK]7Ov!4$ᘞ~SO}UK8W}s>w/.c&Ur:wwPjYg_ #Q}>(7a,]{s!$r4huk׻*U {^#pCSq#"R*AX^}ewO!ۋb_ TjֵQb~/ω6"^jwxqR%4x3 ]~RΡkΝ}f·)ٹ}mx ȗZo%(du;eB=^QE4e5-^&d{..26Baaz^i;99]η̈́Z{+C+7Oܸ?Rl&[[@"omTZRT\EUF]m$[Vh4,= BO, o6*ܑY`nRV74Nlټ@R޵=&?8roj1<7}b;&vn5Iڱ5w5w5')L׍)'QP1`oxp2[z>`XY2YRc5H1&w~[n( b166>@׼g^ͦJiUbn ͧ޼ w#Bܲ |@p. !)f(#Jo +ġb]QXX#D1' X,<:6vbtlkc;^>KO|=漜΢kINèձjjwzmejLzuGzmugXphfi;IDR!`e&RmB |]1[+mI'14 80dbtwoڈwA31I'~0t.P\:wI|~?yupx虡N FVKـăݺǣ7J|ntzu*Mgk;ٵLfm{.Z'[mZ`\3rDVC.ɽGm Qֆ10tx@m@1|~(>2#TUA(ש3W13s7"^bh<)aTh8Q<:Czx0.x0" Q)ܣ9!ϯV=}}s  ]XwWc`Ap#\fgsB>7e @PX7´tDaP.)q2yd\ rAls4SyQlLLʳb9:10ӨTC@.콙H$Wz-${z{{]O]ǷZ~Za ިBO(bX,l+r[(KbZ \Zhºb`m{6PH>B8Hۡy'GԔjo+)Ƒ2i`FZ JXHk٪khRw}URl6b!ge(eJ3Z^)z=hЖVQ+Rn2V&[ Bksa.[SP(% 5cH(Ǎ&ޓ\Q>_-AK$zRhzwE׵z4ȄÑ\ *G#ZkIC2[>xv&4Fs=Pg G]5P}2( G#֝~[`: l: l:-:w?ȱ2IENDB`./oar-2.5.2/docs/schemas/oar_logo.png0000644000175000017500000013401711757171206015451 0ustar plbplbPNG  IHDRB̯gAMA7bKGD pHYsHHFk>IDATxwy%~T "ջdU[$w;;=)dI6l6ӼNqb-WI%NRDX_/hSソ?`#EO ss>r}ЊVoPNhxG [ъVhE+Voha+Zъ}h>Z@؊Vm- lE+ZxG [ъVhE+Voha+Zъ}h{Zъ3#88(ᜪ>Uƙ9B( 3tR_wQ8rw')Z@؊pp~%56>`ddh≉rU.JrW}HɕʾYZv2P4>\?(ԣzcV6[LYX.[ik+<}TVW> =Z@؊@pTW:xpG>|l9>|> `nCwYtI0u Bv"gxϲy~*V06Q$qQ38| ͷwxieDh&Ϭ -VmA9w+v^/yclK]<0c.,]Ў=,XP<28(]_qEk4)e(bA ^ڱ:ޱdҧVYĪs>j}Z7Z@؊3>8g9쥗]c׫7?4|5E03Xhw:Xԁ%ې2:K^ܒSkSߺ٦`9?.M":^um΋ޥ[v}||t[gdPivogzCC7mkEdrm B9ۑƧ>yՌ'e֖39ېK);74;y7뱡6*;g^S/`rM/s/Q.toha+Θv•=_y=zͷCT1EvE)tCSS!@;3_y eכam8s`<?ψnօ^l{tFohKZqcppǞ|_xՏ\ُ%*ePUs0P40U(Updx zNB" ?'Y:+W>|~;}w|Ӆ 7s7]~[ފ[qZ" OCk{S3<`Z862$fl}TKXd>'7Eɐ'+| ~M~fsPʰu1|GmC7W_qͿ.>·T2VQ'<'|n@]ͯƲk830s\  1.AƧ(ʌ 9 !6|ҍ ٯ?scPJdD| !4篝~ů2+/7\zhoha+ޔ?wC~r#/lY 8jLEUUt+b 3(P ] DR݊"8uRpR$zd1{;u MyEA.uBUU( ( Lӌ-:>Ǚ|,|ݛ6n9x7~ww]yƿ뿥)tg[ƭ8Q*-vGڬeY8PRV(<5 3`v.a>|뺰mCUT@yLD:(QQEW=~l|fb8K)E(}a ADBF!0 XT*0icѾ4Ùa7e /ė^۷clԕ_唙OD [qJ¶=x<:uf K)ޖ`e 0t<5T >< Z`O/CUU4!6N#NCӴE#0zݭ߾a72gZ8`u]8uA) ɠ e0h/4ks0A|-Kt_\y_438߇3=Z@؊3s^Ja6\"J-.h) Nу DgV&%pΠ)T W}iZCpx#~G/¾ 'R uQ,155J)<!i""2 hHxr|-ܳ6]yBI'-^?{?+\0xCqr V&]MI±:Aϩ­N.2q niz3݆tٶP@UvdO`1VՕ`n(k,|Vl뗏}GZE\FTBX(BP@6˚m;^ϧ}]qZ=n˗nxMbV<ۼϿ|:{ @dTeƑ-t}c(MéL.c9e mG:ߍ\{/.UMr ]70'TgW~?~Y|&\%Ͳ P,avtoP(@A6m/9Ƌ?{Mi&^|>ƭxCs}_ZLaV4|DQ t:o?;/__pwi='Aj?u׃mWͶ*T1F"lvd[L6U /ȐL=59麊1 )(l;iw2B;NoTo|"ܯj1J%A|)~Ç^w ҏ>t{~g$~5he8?6+߹UTOaW+0R8:z vqg#)g`:aqs0R9pƧea xS.ߴ֜m4{M 8 NTI -]Іz~׿w,ibN%=|*Q0t]H$\ru=Tc2?׭yůۛ- lŬ~]̞9GT=M(`arG󖣳w)˂z(J yQC(I(t&W gvyBTU @x|ne !(/}Amr0!}tT*8H__g AZFEODdi]#w*LuR1, G&*oo&qoZ{N͌и3F.e|(yw@PL p*O`+znBw 059 Am()ͬ¬sp*` <)8qv `BhkkN83L~4,Y)e ۧ-_^fI ۶199 ۶8NUR)d2d2e Hn1(UUsT044 "3rm?~lUU;fD+#lEӘ*t׿udźqul ?t[BG74MecDeZG& #$RGA*1s{< 2q݄ |.Vn@ Riw=7 ] ћ Bs>Fj5IwzzzP(`YV4gR`hhR̟??*#/Ŀ}oUwc/- lE(UG{?t: 92\٤x>aE7_zi?`>1z*ggJƭx痾uCL ]Ch@xʥ"2sAAQuu*KcFad;혇%2mHYh^̹$F %!Hgr=ڔo7o6;BCX91EjX+ J#pBWW2̴bl:.\p1TU,]+uoutweM`[a+XoqTNɡ};зhUC@9|?VC4~24B:ߍT\;p1Q*:Lӈ\1rqf ɡ3%'K\L]| : tDJbR rjammmɅ,*=\.mqttoWOwL"Sxy SC zPT 4! j#Ʌ4NeØ Ű`PZBGR,4U /B($A¹ (GaZyXԴ>rc t}o"x/s&|\s"CT*dd`i8ƶmP( eeO_;ϽC'vFEkh @@][w}G--\*Bt֔#T@qb#N1L \y0L3j)%^ScLA!RC _.5' ٹ=|}Xㄋ%'3qB"U… FFFp- lڵs։c/]RS N" )bފQha f|@8|@ #}a"[l!:!R X͓')(*D^ ٚy* -G{·u0 rnvGGG4MeY BEQގBy04e|rumV[&3la+гOuC*r.Tx剣P5X|P*8R0&8]a塐R`eMac<N D1w+2 )+hbwkȪ_$9YYx1$)ûgkV,MKu]AI0ΨLΥ(3WـJ8}izzz"d'|>l6,+~?xc8Ϸ%oغ[> iF YY( R^DeAUۅaQAjq, jTeN\;8|Aw2SpP44p4t3 ]2m=2,ެ9߃*>qۆg+xjj2=Yj |KuRF@2<QP0STTV&C .#µpKc*R.XnX9 t#MUmeChP.(=џ ʺfٖމ|.6Yx1u6jJie&Ӄ6XU7?Q9166EQ_'#q_}Btnޕ_o~_OuY ̮L©Lwrm] JHt3#0((| DiiE*2)Kt]{!QT)rppTe]S(;m_ًkB, el<mmۑ2L1d2`جr-.;::.z{{#@Lwuwgxo(V:oR=C2D rT*%ffÅ1q]]]=5 Zd:桽ormH5-| hf!RjH@E'!o4?| Ɔdz;]|Y4W9RI9r$%A>4v*¢E@) =tvv=e;/[{l7Z@6 qF !8 dۺ7 DQS&2/pƠjGXu*SL 8zNi1(t/Da\Q&4Q2pnJ5T$:q0AmfDg!ڰ!23\Eʼnr7 MMMX,رc|>e 1mukRCajj ǒm\/pYWvgmsq>kDZkEi 2./^2P&(F EB( wQ-:y NqB{W]L.GT(\=?6؄@xb>|܍Hg4 tyK pޚ1O夋\tttao#*iXx1(WmlƝS͝5-C[@6='yK),\ EQTK.l(ð9e1omt Q ͗-DѠʴ E9qb#uQs0J(J:N>Q*Nj򄅄oav]cz},$/=spX-ٌ1VOeR)R)sRAPU5rCF$! ͽعؚ?{?(o- |ǡ=>[#'p=ŅQa058L+||{aZEm{aٙ=UҰ$X./.1NuD8 @6 hSuٰK`4h|v[v.T*㊏8)qH9EQ"8R 86qlZ8Z@0l[JiN#,LѬ+!BB!_Ǿ},hpap?w2?#~e~t_sbOnyK 8@CQE9..­NAUu g!L3% dZTE2UkJrG8@@Z <8E3Cs x@A300ǁH7]#~{MLs3|ݲ,̛7̓yþ}@)6',[WW۞[fE?nvjpAQ *&AAA7RPU P4̂B)((]c B3Dq"P{6GgxUP CQTVF*LC7- UAhZ Sog]]]5kc.UEQp{P=K:i?'٧&Er9Sl:JEM|oJ1 .r͛~h^Ѳ,DCo/Z߼oo l2L$Oj-/-wtz^c쒐-X+;J)F Qs0@f3@dvKpE au+ЬLL+||y!ZU(EUDg*㺾5*FN5sٸ{sYFGiz3ز{7mن(<ϋ,8QR5vH$rp]Q,5x~~毮X3(Z@Re߼s@zNթa0 @MD [9૑zSiE B+VaP=NF)TMnCcl[t*R,br]^ !U6(BƠi4@ϟddB.ٰo= Nt9dٺ(bgi"rHq>l#^upαx5U.| V=?S- |8]QTƏB4;AE ADhB@侸ۊS2hwD9߭QDQjl;![iZaeYkjtQ^9JJ),-jJYduk;)L O0xFiY4)ɓ}N&&&"n2iA2Wtvv3چy"eQ;6VtXPY5b'n܁"8R:` " DQkې9>xbnϭN[jʴ!SE:+;NX Ԍqrq \υڼZV9sd&84mز{wJqn$%M#FuavD4ͨI*q>G.c,:gY\O~%n}_w5s(·P8n)կ|ӥ!)t[U ^@8@fl>J~%3A~SaHz3.YuB Ajq me / v!N0aBuh^lDt.4s]<6 D|>E T*E|~mmu+Jmt wYN !Z@}21RYZ^BԐ."[{ EqS_N³4T|'a!0 BjcXu:irJY-2ȴ~&!sP ۂ LOmR qZuHBu#ST=q`&E߿wL60(rۧ:ۍ%-XGsGx~']R\\}}}xW/~'=h[$_]3S804hEaU3UxNnygCSH:нh+p.&`ZyIv扁 ױrF~2# 9BaTQ-"l`bbW*"}itWx۶ R-#jt:QX2\~(Vd2,+:[\"r['گDRm\3 DXD{{;d /yD|:l]ϞKa-ux*@f]Uk *AЃLXQrRp0a`4-spV9rFŜ$@dPxNc +0w4sW&Vx T8Ibi0M(}?"0'HZmۑ!lTH{}UUAU؁sW/T*Ex$GӴ"2x̀l%\lEo !$*HuT> ]#GkytJG޵ zp۟mX5MS)L+GF ʾ-W[9'aZ`"fHe .ZYOҮ_TfT2#%B!=Z$A5Tf*Uס* t BIj*,H=5 'c(+˘:, q3ty0MNsA\.G buE:  @gg't4m?ގ'_< RxT*#S}םϽs .ݸ lZs, ,1߿a Q>MU+K?7)03Z@x /=v۞݃ 4 0zyBUcjF*-t,sy!0 RLMM_c hoo,W#]_3N³8 o}k_eln"я [l* XuD]  g9??@i.2y,\h4 +l{?%2y qQIVcm.O![^p#.qp뭺NEV݊#cs~\ITN,AD*ůDmȴ!iAS`C9`Y䋇1Qt'z),SkmC)1=}G/XwSEĵgq<[Fu FQ@Co@EQ7,& $DwVKB5PU\EtC +]JNZ\ 10 |&Gae;`i-4>ȵvģRLLjбnE7q W?pR}l1_ ?{N/-:>)} ro|dGp=_WaYhXSU蚅tBgWu`=2ITF1qUngb`2Q89SʅYP_ݼhYs{sNpH~SF`h +\GtNdzEӠ2aEٱذ h5HK)Α`HbXAImhs=A䮦jjy^|$2J\.!QV1$XŇBp΢rW_ Mӌ>YIE3V/Qz50>mRT+% R)XV m]_~ zkfhY#V| ɂ3S p E3aX9XlXP#iN®a.lI.'8Jh0 #OT]sQa}.xe8eY)8 "xYF(+ҙE(*!HY,kC /_FFFߏH(eղ>Sͦ߱wjY>|o~uJ`E72H0-LPơjj"RP͉u}#Qe2B$YCU5bPWiFi8 @Op Yܱ?~`Nx#³,y1#ө/*TU *4)5 N:i%7YgvQ ߭n(wCLhfB7LT&3¡&t#UrHnqq<aa Oř3HsYl\~~>\4Z~&PMn/wlUZT011=0nUdQs;COej!:J񣪂P1(},IҞK:,=#7gQpp2k;c;_qn*ՓQ- ?&6: 7eIZ24!> c0 22Hg"x8>|}ttPV199 #GYu=nloW9(M}3?%q!`.ks7UfAMFM&_nʆ_~%t F(\•&%CW:`( ͆ru7p-#aH>b qlYWVYiӶs(!DFv24UE-kVvJ|- .! A=$0 $UE.2DLX>Ѕ+.XMS> yG rۣ&@M v=a~T*}Z*KMp'{J)2,˪$'qv:+N|AQ@aɪ͢cZD<-[^m/>.g*! 8bUfJ ̞sd\x$ G}pu!׭­NBPRXB">tTA#2?Y$\ BK5I @ `PT`ڛzb,(U^m׬>'ݨeV Rk]T044%hҍ{Ó(廡pńH<n2kV̀:qd$ZU3af3ĉn)m?[@xK/m6u04APFAT] {P{3P .jr^PenzZc˜3T5= E)QaFCntVv<}rlNV'bR&`s1 l$tN6s51:i=p7m[%nߏy]' IRb/\勺~S:Gۭ P ey4J9|?qlEږ>q\+=hSk玫"p`,2pN@C}0Wh8<DeXQuUnaЌc\(RPj4"))~@5 8]B8.8cAUbyݰJ dFϟ(,]h;nj)ͫyu)mۘh= e,er~s:oJEQj(Fl-7@3<Z"" UM1t3QFGν)D( jz0MK,OCJs"SCJLMc8dꘑ ;+F[iXX@ !;5A]-V14^A_gno.H2EШ.t﯁{tjb1Jl6{ÇЍ44̂ѣ)4?4+T*Y]m.Nds A@6p&42@7P-0-+1ĖzfZ 2p*$ќ4emކ/qxh%&iۘd@';<) xﵫN~HY}4:::nU8|@X1\L8xK3<>tfƖ0}#!BLы`<j @ "1HHQT H 8I;@d{DSc` k9Xځj#,?9G;ݪk\j u6@:F'oIUfM/nINRM(jeYuv0"]c+syN]Ɏ74*y>R`̐]ݫ0sؕIƎ2~xKo^·sT‘c9*UpNBE@RHu9&39HԊ4oyU'!/cS~zV}1J#;~)sJ0|8i*, <v$pnڑ/3nW54jT4N#ŀ$99"LSȿ9/J!C/=-/.ގH"mF*Sf1!!+n=T3P+FGW2o~ (T 6N!/-4.ٞdqv6e =)O(ѐ><jUpVD7.[9bO,v+Z-FJdSЭ4Ê":;sX`9UU#~|cu:F`"8ɻG<0O%Rmنt~8p'R`&, TjZX3=P?MÉ\2U?Q0Fq8`W,@Ўf g:)ﵽG um~<"v!CdX(>h!F(,)@HnDQXASЌ44#Ud\ FUafz̆_q`.ZCw`TbO:ދXRHf 9;!?|e\|8J.7+iTȹ7HT*^ u]XL&%he$PfǷщL|$F?1K{Z@Cf0g "E+#A'BT( j>9\&2?BUjYvِ0PP\GȤ (V<wB:<[ܪae<` ~@(&M[s6k\Aaro.q<쓹\2|劇\T yPGP4hF]}(/OAq m|] QCKNuD Abr=}*ͼ6Lby͌U]p eaF)U¡ODsjfl/ր0FT&jfDG"sZ><cƀ]p6(!pUUhqJeI c riNk:IxiiZu"`qO^;8EmptijBH(25Z!:@VqB1U,v*"g )ñJ,cpp0Ju$XHmMU%qPT2d1 4Iet!=0Knt=-TH c,Ƨ>GLN:BƂ9vdfMLRW^;ܽ U3h**|*1MӣG*1s0c6ϧu^_.7dk&B_ۏǶ’m'E"gH LwX 8xbԗ*X·F}0 p*A33ᾆ#UbYQx6\' !{N;z34\!*}PNj(◓R EU:ߠ0Q6GH Bp( >  ?SUjh\  +JC)C D4DdjԸA>@DH=𢓙 57F‚lßˣwvFM t2eu=Q|#X|}yA4LYs<.:9S-iũbh j` @*Bl=)b#802e /RI `.X܇.=,R)zPbxÃE98}ŮIﰰCNT᎑xٵgNW Wۑ9is90UK{4t?YFuלsnsCېu4h #>*A@eMsL@b3w~ Ƞ.fbc/׾ܵJ/'Y T2qeذj֯B. BP(=$wK7TX)o.֝ㅝǰe0JU/F~Xȧ=xbˮ!\bhzJu-W=鰓_}} TUk&kqK0MĶS*i㘌^Hp#m0d5P>G,βu8RfF-4Eqe( ;'W %OjLhFW/d|}ذɉ/X?;TnU'ݏ-9c!ͲV}$d֖ K":96)hq.\-wGR!$+!Dp #aJ4MiR lQ'B(Ƣr(>ה4%xA) $mxb>09% QB|Qz@@Ne[ >hh*t>|亄~w׭/"":F]m0 ^90UKg:Y8's8usr g 6'/9tdC`UTа")E88nn2 }%ͲhlDgr8N_'ף( weg HNd 砾 W0p;ر}Mz-6&ɝkHRM$ zgГq͆|DבKFA._WRYT{zȦR E? }uPN=sɆd|+q o='8ePz69\V;(TV-lRZ7Mh=Rezl+tY Myqp}~<^q&td?HNPxn%4eP"εXs>xe8^{Fyu_b?W Ϗn^Ww+`em3y&!@Us:q@'~ 84TĖ]xvQ8Vp]a.k*`E 0Mݓs`t C*籗`2Ρ*/~\| OZ8?s3h#go+WM(CYarŠsǷ}oϡĮcXĔe', ~5 闏ǰs(TKc4ty19=r*g`ZUubDb2+$^Gd߄͏qkD(l iOCF#L͋Wf8\RBq ^P?<>!7-/e]' gCBӑ\w_%/£[^dz[@sB1z3@ \gFL i/&J#z*n~]I_O.+30:ic 1YrQH+\iU!݈');uröCpݼ~_~ӏoU=`=o p(U. G̦h5>Z@ŷ ƍ`9]k-+2L5&Ndb BD0kۈC~_}LƲ*.ȭK΁׆l9{_gh!Ds*D4T.HN̴|LcEǶ…M[YO*SȠK{Gpʞ=el+q`y;7C |Whuq]a$Wǜcn|=A6J9,#No8˲dMa9Z(QF:լ1p`p[`E_zFyxGe.hU"z ^ %0-O<|>_gZ,U*(A=/{5LSx|uOZ'CJsWq#۰fIiM\ LiQ(N@,a<9Q86_Z+vrQ*JcXx34r"2tH3PH7 [zJb2~匆$0~2ƠzZ"dFٱw(N03PT$ɦK?lرӟ|ދMkE[ETY() E. hH s%vui{^k!^ʷ:pk~I̠U<vp庼0|)K&>~I~FU_ٶ@]7)-#)7:g8TMDZCaÆSvgldGJ<.BwBzeȥ+2fH|,$P_3z)f%RDj(ش\XTMWozz6j`vvi* ,QD"6m.Z9l{ͬ%ehشn !uY2FJ6Ir//7(4l_?T2.,\2oh֋ D hooŧZvhQ.ټ#>7x%9z3L3L+TA1Ɛ(DQi,eH3Taz+XxrFiyt^Ĕ} Pؕ"UOz϶o=q}n  \8|U[v6(g.Ex6DEGC䐿2'|7f#Vc Ų `ZYWghtu^GI&!(a]#L3Hg{|v}Rog`4y3F$K:(Wh-U/c\X *LH})xtd=#ǽs68FŪ(Sh]=PR,fWxeL⿶s;XT f,H%"MVYX.|=\wׄCɂ![v|ۗo=4mެ8Q)ѝ.( l<w`4=]sAg*1<<݆099r ۶8?Ƈ/>ZALj5Xn?|X:R6?Zt}N6!h<<شis|]xiϰȲᾑ%7nãHg!%MBXrF.=ʍ&]5W_B!_^/|AdoiZD N#^qr84ϵ!zxP"FE@&9l821}๗74,xjfl3/,`+Chsg09眺DJHyQJQV#FUpXtQf 2B̶}W5fJcQ[9'=οB MmX>-/+ r*#?I)(_?|fkF$o<<կxR hDn17@wGᥓ}m%gp ,Y="1;.±l,Ԣ5"=_ L) Fxz(D">LAf 췢A>`㚁h2 6,üy{OVslGk0%왏B*[Նky|ap2~/}w+ >=4^uBK tB  j%ɡoqzk4oca_,АQj8(yѠFfoX+j^s,>m_NB. LNbrq>{`uQp& 2S s44 6>}>g_>쇵OSi/gx\):h…@!BX088cǎahh###$*\׍3 %<+l|r$LCJQz~!KNQR\INaBlqCfE`:'>1|fϜsHCUiA2vk#ׄɼk9} 𚜜ѣG̖ػi<'93>VZ?2bBtvvF;8lۆmu٠[ZOYt:y J., HDz)^8i4Y7] ?}xk|XA rl^AõWkm3Z:,-/+=Ս F闎˖!?qntJ?),198](+&4?~1|;*h7_홨?99$a:%]zt=%=rNh,Z@xNJsV?} 2suY$"Nq!YmuJ St]H򱪪4qUUb Nm{@(g, Q#dj"B.fW/o~ibfݱFMc[KRK.ebyx#ųLK@\]}up.*8$ȇ کpKcP +qlg5l^bl\1rWV?xr.$G:2}F"Z@x+_i- I,K+%3NJk#?A 2*ScEf] C"j9'Eޫ`xe~#4ԁH28=h&5\kZ5]A#c1OlK7]lۚnCSK7lI|[GQ'x1p,Y Mm<7 k(Vm\{S&Z@x!*_౏I7j#d'y2dǺfiڍ3E6$Hony?o QA8ȦT| xeΨWp]|>HUE dj5Z&I[.J"ϾF\<۶AA4A8e xP?]>ZHuf1ӊSr.T*MqwK2kamt-?$mR)oK{ď0롛<2߆,:Wm{`^/nd)hL=@رoc¾YY ςذяI\SY%9ɡd&* ,Aa;hΛ7\{)}q?^wSjl,E..E2GTax,uIT*f_qh^Y&eu$xRK N}(f(/Ų_[$p BgYV]'\6\p:zw\rN#~2KN[e^Lmx^lKc\ؓJS l&xUe0cto߻?xp7.87] }g{F@TF-ͺZ@x =0pEWHzQV46' qQ(oQ!/Ǖ!Kx>e >N>/K!YAYSE^I"'>5 "Er}I&Y^Lw:Xrj=Y! ,2?_bjzҥ3[*ym=Qz*B86[7Z,gQkor"F*x4" 2F<$e:CF9EyJ P 2a(xcbBӴ:sp iH h[a\E@ZA7\B:p|织pѪlێ499T<tM@B"|>1 |rq+>9|fy9E㱭GY<;vI``(#>MB@ h-RDN,6P|p+kY$³,]_W*z{rpR)0]af-_/|c#I@x$~/#n!;^n᭢(xz>/=ׯAj\Ϫq$}?"j1Rp0|a"A-;4Ndĥ"u# ~I{-WEP_@x9}5ƛ >Րn uDs.h̄j*ʞ)íN:9(䉚[w4^8U ϲZxxu{U+# 9T*J 2% txf\G zc"oT] "e5 klJFFsc\ŲjD&8*ƕ.b^ f B6.?YLmOVs԰iU?z,#D \w10JVEn][](#ae9o9f_wzJNg]p{[B+/h00 R%xAu/9,}(Y~=s!ULJDhʀ'_x˜6Tdijf3"sűˆFb>L9vqY\ U+V4ʿ?f+]q߲ǜ|ON8S-#]A"|:(*L;ҹ+ JH (tUsOv?w\IqI{?۱U+f)O&8":L*i< =1t\rqn&.:o=E- < CUuz-wSܦF$f99l^.#;eY:ňFF`8ۜ܏fkyL?~p[Th$[\F /i2 "saW90p{ۉ_e ?276.?2z:rMkݐc~M"vrv! !H:m녕)0Sƒ0<J\ D4\p}(LMC~tId,koKw|+~Hf^_)QLU!)qkf/ J%bKJQ&DI6Kjr_ߍ2Wŝ*Fq3=q#0nĵK-"buX4 <,vpY=`~<2~}CԆۚiǣn4}2|GZiNݶmTը8TU>K˲p-IK  !Ӥ12jYtC&尚.i5[W_OqΙgicW]wrOF| ͚ieQWv]EZDzq%4@5 #2MgqH D)f?(B9mhHQFCFCfqL cHRTUAG>B>|m9!%v{et<|?W޲l8\׍ReNy& do߿KEX\=0$AwaXY\ҕF"kHgq6YFg-˪#7"I7˸' b"4:;;ـI_ Iq{YYM$Xƕ(lfQCL'%!i#ʿ7m­X`5XhgRm7<D#} c mOVR:::""k)'90ukéh Y@ ЈAUEpETƏLBt­SohY=K\z͕_?kr;l4'UbPi6шhL(,$ 7J4i9G))@wgf*L:^{-8P(хjTr%Ѳ,B>Ejq(mThbjj*W叄yyr=뢽=饌? p @|n s{.t!Ba.AwKcpJ`ԇfJ9o-+⛾|#g}烿O{_ߐ+&3C@'dE^OhYfK]74D`pقBYKM$Ȓ2&ɿe&hF4%1 )[!'{[;m{5:Pck1OW8nʴ!ُjQ/68T*7^7Nk8- | +^{M7|;_# "n*CGMMME4I4 \.ܚ-kLabW$,}G"@F}!U(XfF'knqHRtfב|=Yiٞ۹%o>ۯ]3l/qh2j4Mx^,U҂;({JE(%. 3~oÛ֪s.·H|/_B[!/]3,f\8:D>vR)$eGmIFu|<}>*JbIBXTh4Le" ƏY&̠^t<ʹFr^& ۙ']q$bT]Fus_SAhV] dCyTA}>7_ff_އVlgemK;qQz{ px᝼o%ѺP(P(DnkT*ajj (Q%8~ 2x}2$PwWQQpp(|6߱)z_|;8F5y 6n{mD#eZ kGGGQ,199a add$**YB!1u3Ơ yno-wЍPEybS{6nٴ;NݕpbBq>ޏ/{=hu 1:: UUd"YLFeƤc b1jw>VB!"Ȣ c?}%| @Q@$76mx*I`o١F*FѬzh͢q\뺑yXoⳟVW#hkkC&Agg',˪+391YYKGd#jE໐&ekéL)>Rzk7:ŗ E BJe_ O|\{&8N5|><_c#/nY~P4Cx)* 814N6j4R3I3^2-3lc((JuN"IAmBd`v꺑9z8ŏ3Əpv!|jwx> PEq809u/JnԔL_'- |Ņxsn|9O=]lC\TUE:dbud#?ٜbV2I@\u_1Kyz~* @0S bqt۷⵾K`ãxoDw ݌xD_)oI4Szͦ0GKWš#0z! ĵDȨX.$><-3@J"_8fu Ϯ1VgqPF9g#F*cD go9.\?}q~Kr}qJYTi2XV q ({Gnߟm:o_؟~߾?(o_6"\ypGN#y>d'L&˲"P`'P> w8U8(* {h,,'VAۉ+VT$q~ >7.kr-F d2C۔|̸$3Qgdqw8tlzr @eqEQX6ݷތ0?I|φn{oT*fE~>䣷پJ'`x}˰bY?h؝M^R~^7tF )9S*:dj 5^ec+G>džkY"BUHHQA8ePZ׸V8n*|-YOE$3ן|}.$kLsjxג~2.9wqcSzP\,rJ%C͛7lm]? BNsp+SXۛV_ݓ>5·hNW~yY)߸zq`a[Z,cFtI JյLNqU kQ'cDŽf5 $C4@E>(%:>XnvI0œ\4b7^#zN3fLͦ#w%.?~~ӀRrP*Բ,d2tttDF:ОPk6 GwuD~aK<7~`Lt!–YX?9~_bbzlf'Fӕ(Ej5k5DFVpb42"NP bBN L(IzP"PSyP32[TxJ{="T\.cb}WBu?<FMͷv>B?ߗ ġا>O{26A:NɩidžߍzSH$uGAA\r\ ˘9B`ͻM@7i $ =A!jךܮfhOb@Ӥ0- Ѱ"ycCf Jl޼%MBAf1zQ1:Ʀ-ې+V&W*s-oO?D5O{>)HHNT–HP.psR@Pb Ybq=fvM&2i0&{f2)3#: q @6Bզ~Wc'MUEPE6Wrl 31s.^y$>zڎ#첢rH&H$HrUdHte u.4M I"۱?A45$N={/v|/?{?+ *5i$( Uxb,+8v`! @ 0 0ljkrNЬەQqP;Umz~KsTRR!(Ujh&/i6Q] (e(UX\.bnnިH0O (zt?݈ǢJT* "EQHz z'cSXwnMީoVQR$I D+[X" 5O0u 8faseF겗sR"-syF=},h(T8~9ŶLeԩSfZ$l"Lʌ oIVi}SQJwg,XfF( vOjZ&M0 ^c+Qd@kRJinر_=x\kOĒ...T*!JUx gƝs 5]7?W3;x,wZ`0ΥxL 'il2E10 &?q:&J;t~{q*1Ļ{166&E. j5WZ,k[S.ף`rSQQQ͐S|#fqUP c˖-+z/1iS G]jXk- (2dj``E<:߾(7|֛wp!,|/=^.l_^͞ q'l2+: S1;6i$2 ILjC". .d(\=yoRLliGiw zI\# 4~Mm{׃d3ϡѴ#Y)PA\ͧ;㱧n:%Y!#L7D=eY8 |?'÷C`;D1;~SB KtUUtG5WMl߮|R C;2oGS{],zA4"HzW {|C>=^`U-jy!թ8t!M/M'g~^eYLK)יi96S_1%Q#Q8R O>}2= oQ_Ee4 tvv"LaހMmR-h t+!::sv=J έs|9!o_.s?M&3ņRj?G*rGuH-áY7\V"bR d8s/}~T+ě6\" 5BO|.#d@xy$:ET$Ee bWVj#'Dy_#&ob'g=dQ: K(IQYε0cq9jl4jT*+JJ'Æ`e#xyLVX^^cL :>}T+D">ovpˉ,(f)-u/L6slz)QAc*v>(F#bx 6iT܁`6[R]V^ ba B pa5k]?p͛G7NSB —(健܎5K5|3M#R^Tحl*/ٙF(ƅ0M@Q2v(t:QF zwP2O SHrX\MӔ%E։0qIHw+hVTj=P~S^Ot DC*݀fġtc._х9Q3lbpYlU-=jMo޷|q躎]7\έB! fwq܍ _qa®\1(eTL(u0Wy4 D#¹K}Ux@<ɄUKSj:(A~#I`#!V{I:C芹e:o^.GSC4 7oMpާ8D͕R< 7#Z98y%5}/2lT{``7x2vmc{':~6n|C},Dx\0;s˅7Ϟ;5bR۶z3Ŗ$M4]Q䗖]XtOkrcEQ2h@%J H##W\U}-::<͹ޛF%Mb~m[c0wZӸ=nDdcRX,RT*YeWs܇zMp~ghb1#7>1Ȇnܼ{Yb p=gygft~S|$Һ3sŎiǑd ׾>JN."UZG1HX[U2ۮЂk]oy,%ט8AB4Pb{$j#\:avY%^o`Sͮ-F,&LӄRWyۓ(zԙP, *@v"p}z)dpQrk㗰nijod#zP+WQ@-[aOx] C R_IɝjмfSԚ ﻼo3"ZqCjJ-)8v᧖쑫"mNRLg1RO<~y&@0B:C,drmbEQ![}'׏=M#?}ë0<N>" U\ct Wrp߾`ƪl(sx(:P)Q+ᰩP ܙ@Ωkp}c3I($寗KC"@$q'=Evsϛ Efʴ_Syދ"h]tE \sY3;<^B<(?J`1%lG&_]sMO}綉M?{kX:5vW '٩g+\KھB=wʛg` pʎK'FQ`5p*'vƣ 28Jq#ЪppX6D!cux$;VPob jYP^ )RM3{Q6̾&tΝLa k!x?ۨPR 9;t OF, MֈtHkv^ժ^EMĩL]^w|׿㓓jN"\CeJVt7kWΓ]4p{: hۡ;%ݝhꨕJ`H;q#R$egS+=FWWWK-iҫ*rso62N-s@T۫εɏ-Ur@MմLi*u"ABRO\#+q55(tѮb̉t== Q8^fF]V._|o䫿z }*gc{Μp(C8Tvv.?7Ds͇Yh|?3hPJj2XuIf؊3Erlm"^RӢ7XZύF%F(hW1B2Np"Ƞikqw#Jc/6fѨSxŵ\_PP@ReQ<;5reB:|ihP5pypY8l @qY9ڐ 3ds F޸{ I pU^w?ws;{~n~g<۸|s<6Ol!U kd*d2*O%T*4 Y%"DMD"DMDQw2Ki*JLU&=MOMU<@a>U*Жew_@i X? W=82zÆQl =3}.3kRi~… -n[uy)?YYQ?/&((ula'֕ Žd`"eL1oD*)qaP\ܾiH߅$רo6.x"OCOmUɩA^\ NղzO 3dki7 Bt=!4F>ۯ5M @to kM#4_{rtlc^?N_ !P/Abdrƍg{)eU#Xfaܦ2 2 hl/^e;ꀐyDf>_D>#Sa DCOo714ԋ4ҩ bQ;a;2c 3g RԨTb'2o2e[Ce2\­+VDw<8il GbogF;4400|kଡ8_ " pQ0fZQ/r#h><]p>(z,tٶQ>]VО{mmԇd@mQc^' c/ZHOyԠtF(/5yIO!>BxM>D(HO_mCOD=;;zzt;wt:;2U~Vl^oTbR B!W({KBwXbRlRMYLb9-/F-UjxyUBb5puiM9TyTq{!n3 cx1Jg:R"ZNwvͧRήtg\gg\2ٽ Y+XU4ͺA-S/{jjGRIf-}er٥JVg, KFir~hԫr2Ztʃ0FaH‹Rt{Dc~LXG"寑4j"ʁ5M; 0R8uv̅BF=\Hdx)FԵ+'ŌY`U2"&阽\cRJIYbh֢r1D:2 >Z.ma4Mc*D"z2ѝ_ p @a<"  G@X0@k`# yD @5a<"  G@X0@k`# yD @5a V`UA `mǛB"zTXtSoftwarexsLOJUMLO JML/Ԯ MIENDB`./oar-2.5.2/docs/schemas/job_execution.png0000644000175000017500000017107311757171206016510 0ustar plbplbPNG  IHDR sBITO IDATxy|T3<%Nq@0P$-(KH J"XYP-U#T[ZwT}!. CBLd9srg2ŋ̝|9fƢi@X,,%QX,^bY@#͍Hׁь47qX_bBU HsMD'H[J i.L4&D "͍b `inp# Hs`BķsD!\#щ47a!mDF>Cь47)]D9\3]{s`B0!\i.L4&D " Hs`B0!\i.L4&D " Hs`B0!\i.L4&D " Hs`B0PhdXBBx4-!`686O/Dùp(} Hs.y隚8fdYoysNvI'-qE4r3"ͅ?nX ҚF>ƅ@o7cJt*HY?yqڊPG!E ?^ ۻ,|*,?^ƹ%Oteצ<q45O*>)lŹsTQї;E_U~/ ->O O{}拎Db/}Dt&z>@{_їBe{_l {oK<4D/ea8 h M[8\Pw@7iU5ڭ/i׮Q1OK-@VߨT?p=Z__k*m ENѠokojNi&mWF>[| E/ҲiMsj?i-amWfo>:zXǯK]mGZ]VzRivF8x(ߖ%|' / P=Ů[8|Y޺Y-ϗ^}vA ]m#[;ECǵonMJOz Ϲ)BͽOBdkm_勽=~E_Ϊn=3Ḡ o\ӴpiAqI>Ng [1NӠRH)7>tֺ?,H s}|+.h}f-~u_w@,' [!Ԃo{oKLHs6ɮZ.] KG.]\2++cǎ_|iy֬Yiii'N /]4333&Fo;:rP__wtҥK.SNWկ:wlƌSUUi8ollOMMMLL|ꩧ1Fbk,>8(&C\#f&><$Mlxqs* 'ŷG/;u'Uz-c 8 yɒzt;t}i#=Ůb,v{6#[_g^(f"x}yrA?ʼnZQ(S^j}ɷ=ʷ`&H xdO>E].cǎɓǏ:ubcQQݻ?ʸB}]>쳖!Bz#G߿߾}%%%sUG8vӧWUUUVV???͟?޽:TZZcZu8]'z?l3CŜ"yH-*$Zs|Xppfudee [KIIIB.]466J7 !mֿ!Dee^XQQaXʺwܬ{H{cǎ} !ۗH=tYYYUUU܃|뭷\^:FrBEئ塎B4 BWӃ~ӦM?~(СlKKbosv)kۭV)..Nb:j}Ϟ=ow9u[__^^T ͅҮbqzŜPM dbw߭D[[?~܇޻uw555577뙮!:?>x`ZZu]w>}ZW֭}0">qZסB4 +Eq.^쮻awuԩS8ԴgϞ'v9|7_~mUUմiӤ8l6СChΔ)SSNm^3#B445;Xzb͚57oҥsrr|h5jT||I{!1cƥ^c=ާO>}t /0rÇ? j.L4&D " Hs`B0!>7aMH@쥑200!\i.L4&D " Hs`B0!\i.L4&D XжlSO+EYr %Oov á`a{t@4*xMlCh+"ԡ8 $ l߹#<QEP4I.]ĖTvZ =/d ~Ȑ0}źZSv1nUӚz8corbW`G\4Md/|˽BD}zO/:N}5_>C }|U!="{~$zUyJV OI:|6]t!~8zƵ6lw)v6];zrб ?.3Dig ņTH5 "~>KܳQ66Z(4mbtht']hqчSn0h/rzzE#p{i$Rڮbޤ}tH֗ NiZuO١߯=ߝкZZ\[>6? MkJ~hj]Z<ÃIv\m'~@{Zokov>oÃy<e"0EEqvAޤUh]J^LͥYm1GZ}J{-՞١Mڮb-oM7%m]9ںv:KY(zTNQQѝb}5*U(=G!222}!щd4NHՎjom}'[}\,׾=ں]]c6*STh_>=qVK_^L=Ԭfx.ֶدɖmVzUE0MI[PӺnmP1-^:@OͪGFݣ)ڇqY#͡YSnp1CYYСCCBL>#Sڮbky-&LkѽEwo=bƨ6݇Eޚjpoѷ鈾bϑ֧d7>tֺQJ]c6H€ljlP4kp?/9 !~sqBĐ,18Nj`-^۾բŵAŴfգaգ-HB0.#k䯾ǣo;pF "BhV__}mܸQq/_fY,%K,YQwƍ+- }cC7 ♝++3;eۣE5OtsV?])= ~7i B,7 o}#+_Ń#mĵbPwDDa Eiyգ!9ϼ0.#k䯾L?Aӯ=Б#G߿o߾svg9g9ƌφ$lVo[0]rT/Mq;|"vo}!0\3?+Wvx{hh7b f&⃃b֗AkqA_}P\\C}ӽ{Jv}ǎ/dV,=VsaXP>ܴM pL4a-Q\7-Hs`B|n.}e& @j.Lfm\]d'kv@0!\i.L4&D " Hs`B0!\i.L4&D " Hs`B0!\i.L4W^ڵb1XxI`2MBKOa)3337l0l0-{xLZ1Dkѓg477{u547ИI@xD =g;K.]t:uj}}^fҥ11?ڷwޝ@("b 5MlH!M 7.5V>9G;v۷b߾}999{+V(**ڼy]Wѹ" oHJg&p)TllnZBFb/**roJt :w?@dᦅXZdzzzBBmfŏs/^ D3ebinywsСyP형{: }`>| we}B߿?''B 61;m a5@"E{wsСyPMEEE](K[BۗoT\;ÎŊfB̚5+..nȐ!eee]8J^~GQw6ˇ7w\G#O?~fK;?1hР:{K\jUrrrjj믿訴t;v>|xiiC]uU:u9rCS'mHWsI0l|<>L -`wAEEYSSǯʙ3g[%|0m>C=~xCCcYN87c {u6lْj*\ƮRR:L${܎4x0!P~(_ݿS NݼX/kz͓'OرchٚC.! r9Ϸv|orgΜYf͈#B <ڼys~BbC 4MKp&t֭O>j@;Y,uօ: qӂg!ɑ͘{& {)QOܴpM$ Ex1kkqHs.9͗r̷GիWwY ˴X~mEoÅ&v"nu7 B~i,T :SQeQ{oE](gd2휥Hd3hq5qn(HDv|e]f~KcAVQQqD(\ܽ p͝;ɓ (,,Է^:%%%--m֭N:eggmvt1++kڵFOS?Z,6sssm6[NNNqqdzz̙3Νoˋcgy1I;ݻ޽{{ӧ:$#<ٳpSTSJ:52رco[jرc%SSS.]9qDΝO>()t\0\+݅k!7|.iZÔvm.HNp "of(!Be};uT__'ڵKxNs+**4Wmۖ^KK{;Ώ7md۫2ѣGmmQnwXޯ_[n[oUO#j54Çna,siDUTTx;IV3OMH]RK˝;wnsD6M}Eu0\&Y.]uU?񏹹vw]rrr.]-ZoܲeKjjjrrq 89Lm.HNp m~ 5>heee\rɼyn善?֯_q-[: 566lf? xz[ok:f׬Y@y~: ?j% p!2bĈm۶u1Աb[!=mƆcډ:0M 0!\i.6?}SmG/^sF36!TF0vC&@lڴ)=={@pn ߃6shJ_?k$~c4f;I'ĬYi␉ |҂/h-==^47Myyo1Hwi/moHViiii_\ѦTЖ/Rx;!a2ر @ӴHɮʲ;uꔝ]VV>{x+ хK_WNIIIKKۺu:///..n%%%FcߖJ߶m\бcǬkzޔimrSHS'mӄX,ŋ3!!a3gfggϝ;W/V\\krrr"Sڻ4x]NbїxС?n|532!G:th||ٳμ4N=qWO!<6:3\+WW?~?3f 6ԩSC )((plojj:~W^9sLF~5ť⒒'xP2k֬'N͘1`>f!6ǦT4?N>?HKK{}nݺtx\QQ]7VTT8_-Eکq=jkkmֿ{Jwضm[bbb[,&# xn!i47==}ӦMvڽ# 1{;\$;/EA\M;OzjjcBcW̼K>TosD{7>!6Ǧ!! @%&&FG^ti}}%KFخo\lW_\%%%}2yyys̩)))2e}8'LgϞjfYYYo] 1>55n/X%#ޥ|QfZQQQ||pHMMM7ݔvZ#Ͷ'x}~ɓ'9s޹sq֬Yiii'N˛1c.j ,muŸAfdڿ꫑#G[ꫯ>}H_OJJB{k۱cG@nvݽ{ӧOJ:bf?hyWs@srrP͍FZKHA.-uˑ'Á`&g{o*3څs]4x%6pW óMDt i.L4%oo[\\krrr]<^ziMMW,ܯ^8WWRq۶m\pAǎ֮]ci꼼92tٳg+zQ/~/IJ2Eul#F1R4etuVӽ#O3oV 8f78BM6j[zMӪTtmKѣ֥@jjjEEfʖ:x IH{WyFmNޥ&'';j"6xҤI]v7n\=F]gXlt!Ѓp\vڟ:KҒVZtB4Mׯ-r뭷*z )givEuuw^KF#GhFw^b{OЀKJJOOSSSv wy=ܣ=U۷,Yb&LسgOCCCuufS.7gΜ)Ssss-[V__tRy˄HI{o|B) =zҥK,5j!I;bԩeee]w]zltlٲZb|Ⱦu>}B999ӟƏÈB{n|WT̼g{ݘ1c-ZTWW|ruGw,qnn1-γ,eHqڋ=IIIѝ`͓'OVT߲eKjjjrrUTaÆ4hޥkjjn馄̵kKKK bf͚% \lr!"]Jt#mJCw+)):th vaEƗɓ)))ǎS{;wld !Daaf?X@1K$"x}ɽVv72!mܸ1..ư:H{7+*f5q*]#{]yy_7|;43gδlC qkĢyEs;nq\,e17nܸe˖PV'O۷cǼgƁ'j߰iHe׬Y@𽂂3gάYfĈ"6l#Fp KII֭[>}^}PM f- `B0!\ʬ{E]?)qGf_Iڒ{Yc07\ IO>߃֠>"bOHЦ.:HI}*|i.L4m۶]p;vZv:///..n%%%FŲzꔔ[:6Bwեg4$=Uwl|92tٳg{;F!Dqqqnnf)..VjD"jVTT 6,>>~޼yc߻wiCCCzzzeeqeggw);;Lё IOO9sfvvܹslllOMMMLLtv\8 889d|wGO^3Ku![MmJӱޑ6!]:Ǒ6.p_a)C/ӞuV~)S;Sh„ f͚u^xa.Hsy^]ڑ6ե\KfEcS7iҤ3g>fؽ{޾}{}}O?(4t5L_[[[XXQ_=33s…_|nwlڵkYYiNJJJrtw MvjuDSҎ.R]:6+:rMӎ9CPWW(4t5qر.;vj=yd^^s=UG87ّT={VCFjfdd߿߽C?)ӝWF)# =Y.T6m) xұG4~t4yB4wY7:wS5 ?HHHXrjZ֘WkR=68K;2ަKu۬xl|bbb5Mkjjx7hZ\Pņ̌"N#ipIII=\ll/_UU7ꎤSa5VkSSc?)ӝWF)#  ޞ\HN) x~ݏ#Oq?:*Fgnc6u{swmۖ?ѣGmm{wӖEyOե,{2QQ]t9]jkVUUHSZQQɮi&^]]mҚ"N#vM4k׮ƍѣѣHgrrc*#Q=zo]KOjLs5o&YgSh9Dq T\ۓ'j?6ئ:6{+=qytTĀ?Al?n@M0aϞ= 6Mߘ7gΜ)SHRRҧ~C{=.ӅtR}]~}]]ݒ%Kե!.[/tFTݾ`G$O83f̢E/_.9v؊S]wuv$sK._dɨQґt5'O|XёTw]tYh"NIK?qsuF8OSgp9( zwx҅ScmJCwIG8?kX4Fpic l37'O۷cBpoXܴ}^}PM օPEmw@oXܴ"͍x?EjR0!-c~iKHs-?=B_qHsSnI/8D@t" /99S(AXqL6`b|X/)t R@jnNVsWs"J.vF0@$"?ݫ@# :D4\?›V i.|U<,uM0$Z\S\lg]@ *狵+Wر_;p*iccc~~~jjjbbSO=O'$$vmv]]g՟?;v옕/577Ϛ5+--->>~ĉ555mHsCfǎɓ'Ǐ?uTKyV:{>tPiic;SgϞCUVVΛ7S7|#}!ߟsav={9s^x)++k۶mBTVV^x0  Hsɫ4=Tp[__xSSjB455566J;O,XteٲecǎBtqbHM ~l[nt~!Aqqqzz"66Vxq_G>3%uw555577n0~ 9oӫSLJKKO:u;=zѣI^xŋkkk. 7_666jԤo뮻Nz={L80in1cƥ^)i~xիgϞ~VVVrr>*Xf͛t2|ظq ?|QFO4im7ןB{hԯ\f mBɑFϬF@DT/jٻ&<%nAy0qHtCg1v$'dB:tMwOƁw^d6saL<@Bh:=4C M֙m 1²lm-MT*ɒequ߽uKT*q4 `"͍=7.p*CѢLWq47䳛:NHsb$d qC()O] gsHW/xܨGj蝩@A;|לu-|@Ox#8HsNTx_w 4w?V;+ Qbpq\L3}ǝ !\0N`!ӝHsDD{`"7\0qi.t4LtdD @C @b'\0Q_`=1F{L4 u1x{MKKd<60!1c?u/ A'+1ިL!/>h3"FaӦM}6 oc6-a!svoz\@h:;;U ȦM|u HsƍSRR.\!]`0H5 h{fgg[,ÇהJjii?I󛛛  X l66oݺ~zٜ(ɓ'E;$`ɩ...޲ez@!RQ[[[YYd*))ikkȄHw5s]v ʟw.+N78U+9R+\xzzzjDE -ڰah6Ͷcǎrׯ_?vG}w{X,VRͺsΝ={ҥK]]]RaeeeQQQ___AAAUUaz}s544554fc/--_|O> 'NhooT⪋F,h>ǩO~iHaCD?v%')EpfմX,E@vFJM¼;w~gn;h{͵(eff6cƌ .fggKёґbHS7nH&%% )1(CCC*5Cq "~NJ.+N78 ) ;R8&1>â(z<4Bhs-O*\.vmt[S˽xgϞݻw˅.+NtE$N_ #xPlDd2IwE$Buud*((P^0Jm*n]A>%%E.|̙8o޼GbNsժUyyy*z*+++==A*lIIIEEE/_V(5Βb\1RѾ}233322^z%ҎSɓ'>J:d6^yU'Y}~|rVVիW^T6z1>EdeNTlٲeqXq+Vwzœ'O...rƍSRRSo0''x˖-Nb΢[ʡ`صk̙3SSSwڥ4Bá>Kí[֯_o6322UBR:B1@ \1P^^^__cǎ}GRammbZr嶶6ͶcǎjѢE===R>Ҧ"^|;  ɿ#/~7iiiBZxcA8pe&M҄/_'TSqEc[J.x<===uuz=\CCCyyo(ܰaCaa, 'NhooTFhѢ 6ȟb;wٳg/]եR#C ߁؈#vؕTVf^^Ν;?3-vQ233Fz{{EQtFQ*v8(J'TT)}DϿt\ҿ˿<裢(UzW744̫翣dfCΖ+Hl67u ߤ$gNbM".h kܦb 4(.3f\pA/ŐhBxKvpLb|RYNZ3fHMMݳgTh4FcBB|'$$ xB6 _bHGXQ322 Cv]bAwEG;wheg^}$+} _Ҕb(XSq S㫷ʈx<}!![ ;R8&1>iY---ӧ7l6wvv)zȈzG!Sݻ04ŋ ,˅Bo'N}In'++KlTsQ)4W(ִX,74Wi*9 !BT=\1bŊ֛7o L&jnڴtlիWl^VVr7ئӧOb;x뭷~IYJH%%%/}Q\.vmۦ̫$+*--mllt\w^d\.655=}Bk.]ahh襗^ Cy"BŎϾ{!; OȾ}233322L%NřWIVEVXXTTTtK5Q IDATeBMMd)gsObMñp人:@ 4ߩjTPPso)}=Qev?SYYY *!)f*![D?ٕoXciΡCbHݺud2 :]6{W))]};4C^ W_}j:{}ݱ"ׯ<@c&X-?u Qg0f믿Ɔ;c=uYf8p J]J8L4|‚ ¥'\" ׂf$BNIi.l"%QKᘅQPb` B+ v{qqɓvmmmeee&-z! wlrad``j&''/Xf鿹0;:: SRR6n(oQKK'Mܬһ,)ػ"Ő-[++,[L[֯_o6322 mi]"bzE k /*,**+((RټO>dBfرF*X,VRYYUUhѢ7o*vT^^^__cǎ}G*]%{Wҋ/488/Ԭ;wٳg/]եґ>Wz) x F̰+1>]>fgg;Q;::t144ҦJAEvF077nחޚ.cP(//oΝ}8(Y \֭[/^Ϫל1cƅ gIϼ*T=!{Yc| 2 o[n%$$ O<׿uee燇}iSPKH'&&J -ɏ]cǎSN]v4KAC{Wh9s>sgxk&&&JuYRW\'\ K222\"BwwwFFVk֬پ}SAj IQnn-%w`\|*̒WZ}vRF-1ʙWlӧO---mllt\w^dʆ.l6m۶<I-ZBRdZ7mt:m6ի4MqeeeMMM.KbVVXźɤґ,P]bHgϞ=sLss駟,//p8NsRٳ?44{n|v(g^M H)D`l+1>])))rVXXTTTte ۗK/toi)t٨\jUjjj^^^ssJk+d2I'hU:z̙8o޼G8K!)H1^{L~ꩧC欬,v 쎐f>;Oӂ T=%=B"OLq.=i.t4:D "@HsCСX@U@Lp6:\¯eb!\i.t4:D "+܍4WtGY\47~>&8ܸ}S8-rHsS2Si.t47yT.4:dXoH@!͍8Mj`q47*%q::bE M%FcD4Din$aVS0^fX-MN11@EQ)g[ "472&Z+#o%?h9D8/O"HsG."4wT& xyo%, Ǖ0Q"E憏#k ldK=2]DinxȜ.F4wTTN^Nv"(F|L\Gk׮u\R /fۭ?1u5uԣGJ.^#LKvwwK[ٳ'??ҤIΞ=Ryhhhթ/pmmbIIIYr锚mllKH`"XCC|#Y͛7wtt\p6m˖-R[[[ꫮ[?]]]6_~e˞~or~oo׮]{G׮]Ry˖-mmm'Ojן9sO>JNNO><&"1X8f`'m 7ӧOgϞ-KJJ Å f͚%… JJJ._Lm2ַnݺSypp0??[J*LOOW#+̙3ϟŀܰHƿJ{1r+WJׯrʕ+W֭[㏇Аd2L_}| nHW\3\zի?O֮]_zR(%%e֭rV599y6;]v͜9355u׮]ަϹ4e&Monn:> ?@#!bBB` v8}Epo*ƩMئ85N bHf;vl۶mSNu3抅WXvi222֬Yե$&&ܴi3<(xOAX7aR?N>=mڴg~^C[nE ҥG~P)792w\$5C Fhn&-}ݿY!SV3RSS;|EK:;X,S>|8Pٛ>(n@+$P>k:-hY?ZPW(c8{zz抢e6{{{Is1X7aR?&O^Fco=NsEYYY?bm'9xPOskZ,g^O.]2!)5;22_Ғ;Ǯһ7kĉSNK~Xqɼ'D=@+$P8u*zJrhV38}ݥKx۶mc|mT} r=ӡ{FVW\q:ַ5_ "r޽{ɒ%rTڦ2n[z ҥKK%VuӦMNf^:ԐBm333WXź;im&=IHHHHHP%˘QNH(|WuQ/{<'Np\W^}^_[N /!3n0emxWlIIIEEE/_+ԘL&;AW}efffdd_BSp8.\\WW'$t:WZܬ8!)b}}}JJo=gΜy=z4hw>+]wEots{ϻ<{>R%>h7LKK cߋUԲ∴{o…&M~/"P0Rv[II$HJ cC`BJ,"@Ы̭[L&k׮͞=[5qf 8ǻ-E 9>Nj{}ݱ"ׯ,̟?ҤIr͜-[xnߥ!) X l60%%eƍAqƔ; 7o i@#j:PCe;Nqi\]NNN]]ݧ~*G Ք)Sy䑦;w ?pe&M恎PE6ljHlSqG7WUWWw=ggѢENRPv0i]`[Ją YA+ %''z{{EQtFQ*۹sg}vqoRRz sssv(}}}RavvEQ:hRkRMa6yGEQ_b5-)qH恂-]… b}$^`Z^Qxu=ab5kl߾}ppp``@ 8pF6$\),###RaVV֕+WAڅTիYYYRɴifM-[Akfffq;N>Ҿ)#axxX{Ǐ?3AFuw/233O>^rf۽m6+Vhmmyd`H>VuӦMNf^Z*,++kjjr\A{illt\MMM>\XRR_G oseeecܦӾwEHE~~ɓ'_jݺuEg޿ݻnh :K1=`W".])))Ǿ}efffddKE o9s͛wQg*_t:WZ,L&$ԘL&r;3<88mG$Buud*((tsTqH恂/TG*oHnDСCf9++W^]hx={f|3[,Ԑ߿w9tPITkks`awnjZc1\wL&`^]bm>GS@HsC!\i.t;-Sܲ 068 l.1Bc!\i.t4:D "@HsC!\i.t4:D "@HsC!\i.t(1`u'bC@'Hs$bJpɑJBEÑyHvi.ƈi'͕wwZX  Qyf4"EԑF.!!Et㎞cBxBV'Oԑ"HF "\59m0*Hs&nm_xᅜ5kָn1///!!A(=====}ڵ.˻gV^ /xFO Z,+W:NP?~&M߷o_ ]w%'(u] cn0=?~ںuT|Ȉ 7opm6ۖ-[7W|v˖-mmm'OT̙3|IWWWrrrMMbt:?^eC)xQLr1mFF(Tb+pg̘!vδiΟ?/=ϧO.Qyv_~Tx+)]oܹsRIgggNNbyyypeDqC?+Vx76( }o\} IŠw3 h4 xoݺe0|c5&&&@u)LJJi\gEQEqdd`0Hg}|۶m~Դl2 ۥ{֬Y.]2e A7է\e&է aJz֖_!''GsEٜK.ʕ===҃S^t Kyb ,x\k׮ղ)S/_~a) bl`Hs`+FpyW\rʺuq =O?]QQrʠϮ\gzիWׯ_/W{v588hH?O֮]_z .kx'\V}@aN7|-ց b(͆گ{MKKc={#N/:?<5==xbHkSeʔ)K,9vXXV4x9~ǩ}}Fi%GY ՝8qb\1G DnU/( I\2UTT<}Q:y:4 q%z ^\\?"Őwb+vh)x {fgg[,ÇĩBקJZjvvvlݺU>*Si1,\#G$}qB 4"XuneeeQQQ___AAAUUTXZZZSS߿|'|һO8P[[kXz{{VkeeT_ljjlll|BŚuuuΝ;{KFw6k*~_VV?Leׯ;vLl|UWWw=]qB³hѢSNTxx ,ZhÆ **Β<@ˈwJ%+G l;vԨĩB D͠57lPXXs͠SlaJﯮ#$Pxiv0UFp8DQyvhh(99ٻv v]žL|֭/~gkΘ1… Gw6)zJ<(*ܹ>s3c-]TH'DqD:{ -+(Nfz{{U6?uj1cFjj={T"4Zƨ|BMs{zzI8>F,)*%ce=Pe]8R*+D434705.QHPk{DPG._B '++S `郃͖ZkN>/> -+(Nfk\Ŗ ͧ`TzԔ`ǃҥKEQ4.Kil:;;fI)l|T\#]SӿwH=SeD6͵X,bgs S^ G9"C\ Jor~]r pGy$--d2-]Tz2 yyy aj+W:β?V&|;"Oiiiccڽ{%KBe6nm7Z6mr:6mRٳgϜ9K_ Tp8͛76kRVV/|Ŋ7o0L* jOTI--WZntMٳ?44{J˦{Rffӧ{Q Ie}D'YwHO\!-]ahh襗^ c@ÔÃ>`.LoSqDPx^m*nQIػqңO+Dq}*Xparrr]]fMMdޔ8ػ('OEv~bڜ>}~8{lAΟ?_RR]app0??`0iӦI---A{:;;ބ /c츥 2vvٳ^^֭[&ixxxl OIIɖ-[/^rrOpDQ !11Q '99֭[jjj?H6 ###>owgGFF:T[[{ƍ9srops񫪪gSNz_[n86x1\ *~iꫯ/^X, ?;>22"}éS^tH_qZWWĦ&Wp Ν;{;gΜv\-/Зϼ @xŐ@8"{ bH1p, X l667W”7ʛkhS'(w;NfgggQQQJJ֭[Xa* 4^0f+q+Dq1(ƢU\K?ϲQy}jHw,@G Q\ hR]]ݹsΞ={ҥ.>&̫#(k N.2l!bGGGvvϳCCCk4hG*ls>vWjpX,Pɽ7nHAEQ?077nחޚAgIA{G:Qikq5-ܑx?>LŚFglyqAWW.Zŵ4cƌ .(vrRHh>C₾T 92w\'߿5v#BO:eZg̘g~>(TBRh4NҚ8,(T}DOŚF8x]!᥹Sq-FkBGq:/ o\+{VVVggU999tJs;R, Ζ ~6;;;fw0OsGFFx郃[8!fY. #YxS'*͘Xbttti6[[[sss\héS9sfڴiwygOOTϞ8qbѢEuuu*5_f O`> ;(\@W%i55nJHH11y: `L@ݻ7;;b>|X*Z ,lcREE /_>eʔGyiΝQ|vd2IEEE)))[nU?g0rrrl٢ҦTy׮]3gLMM];ve&MonnuDbMfGGGaaaJJƍUnO;NcqƔBá<&2\6ͶcǎbZJ3 G?ё#G:::]hѩSBmSc>缥Қ˗?R  {zzn޼{7PiSr'Nrk_7MZZ ׯ_?vm jYUUh"-TYYYTTWPPPUU SqΝ;wK.uuuyqbx<===-ڰaC":> +6RannnE/333j*nڴgrSy+ǓDY %''K-EQ:'ڍ7Tڔ*l_tI.۹sg}v(bM6YhQiӻfvvJc6LI1cƅ qBbQQ8xI'HsFј q(*vuu^4'+++zQvȑsFMHHEhs(ߵTQUU]x):cƌ={:QwŚmj%rJSqF ڵJH&.Zu:қHLbX,V_gDk֬پ}\-}f}ʕ0Wl33g|ᇿ 8pңikfeeIݭ{FF\3##CSqsss4̫Wfff<4xLVuӦMNf^:VaTVVկ֭['_g%Orf޶m\t҆^z). ;x뭷~9+Z[[o޼900`23"EeeeMMM.ZJKK].ݻ,Y^9Sq+**ܼysAJljj*++ #I  IDATQb޵kfϞ}X8 E(&x1@,UUU]~W_}b ]Iu_/%Y1HEvvԩSg͚u(u`b?+84/\"@HsC!n(Pkq6|M"2q`0xi.sd4 t0qOBR.3LL~xT.`# )[N&,\@8 i.'n9 HsC~xT.`#"T.(h;0p6:D "@HsC!\b@n|}3`"l.t(1D?Ə9 xmbBq _0l.Os'y\L4qFKK 4%@\RtIsT2]\ı@.i.bK wqQ0fPM1Z"Q6f&ZyYeZ|4]!V3wvMKZui˼̖֬,RDPQd`8ovazc:>uZ`e$~\@ =wޣG޻wS5IswرzhӦM6mz衇>3_oڵ))){NIIy׮]+H%$$6B7ܻw9s\tHswѩSONzQg}v _ą_lYMMMqqq~fΜ\@FGGO7nh4}zwq/~񋨨zȑ#jeffȼ<$--`0HcǎիWbbbAATh2BCCǍWZZR@E ȚiMMMddt <8l0G?]jӧO{Jƌb&M/O2EBBB{/''xذak׮MOO;b7nCX2^||C b]د_͛7O:l6M|̙3kkkËCCC a7ДԩSNs\^ss\A222-ZTQQԴ|rh45k5O^TTh0BɴlٲҹsԔpm.GoY}}bͧzjȐ!cƌIHH۷T?cƌիWkRannK}p8:2'|ժU{oɼsC1~L3<-_6ݢt۷nj~;Z̀Ԍ9믿0`zMλ| SFvV$5kBU=9 p Mrf[vښo|lҥaaa7tӅ Ӭt:fRRREE71bĩS䧭qqqUUU%%%iii!%%D#GcǎիWbbbAA(.֭[cbbbcc}d27TMŚ:ξ .$%%-]TAA˓{\^^n(T Iq~7l0hР 68(==__V]f[[[ffhSS񌋋[dIrr+T Ld޽ӧO7GkWn4S3A[nYdTh'N|'fzz={RSSM6|Bfdd^|R9A()))--]vmvvT[[[k2TtTӾŋO8UAA&LP__?~ŋT Iq$>rHYYus777=s*!Z'OzbgZb \rE7$$DP49ŒvBgּ λ܈,oـ0"fM>Nn X,/3GNtmmmQQQ7o~W.^(޽{Gԙ_Ǐ7L[lQ9#G?z>((HFGk*$ZTy=MŐM}::} zM^oX9kŚ ?_obivs%Fڦic]>P\Qq%A6LSOM:5''g„ FQy=3͍Ȭdܸqv:ݻsrrz~555I)G-ŋ{kFFFWVT s3<3{gyFf~UȾf7t~%cǎ/O86HsҒ?e'ky Ϙ1C*lii1fy͚5H ӋZ[[ CWWd2-[TZZ:w\LKKoiiQ xnܸqqi*)vt/?Ujfdd,Zi*)x:O;|뭷:λ-'@Ĭ2 dgg -aɒ%a6Pzͺhϵ۶mܴiu~憅Ʌ;v9rdpp1c>NpA鯺MMMgOHH(((PiMbeeeǏ7 ҖJIII!!!&L8_*=MŐ'N1HŎn6_״4fGX~J5h?_XRSS#dTּ:0>&P;~:uΝ;}%@ĬHs3=*ב5'm`4/t{;-@HsASևwzGv;:y_h3{O=&\@fzoWMŘʒ&//OSmmkBi.ǽ ǏoO?t˖-RMꫯ60--M%xÇ9RVV~xTw2$Av~{ͽ|:ަ}!9T Hb-=o~?o~_ u܎W_ bdn Q-=_~(GEEtիWCCC֊h6z3YegLL\A*0*M޽w(iiiJReee8:\%$Uپ_=!!aݺu|lViQ!):?!O Q.brrr{{u%$$)=lH&sPȿ-ܚ^AAAkݩӽ{5J[-}qĺur^"}>}ӧA%x9wlWd~NJ?~d2LJoٲEM8CR,t~6Cr~S򏪫.!|)1k2 hs7w͎:萟޽l6766Z?Xuh9N\266Vo933399Y=xlW IYg*үQH6T IЅٴ¦ Q4~CI8$Gjj3{| ---??b&iٲeMMMsε_O[ZZFl^f\hѢ˗%Լ7N}O>=*בG}Ç: oC,\@ϢN3/c_祻y="1pp|4:aΒ#΍E\hC'gS{]6:gnOC`^k\GOz*  8B jDQLK3[~x-@ӶnK%&)44tܸq*^p!))),,lҥrݭ^RRf0RRRJJJ¶Liq:bW޽{'''K. L!C:uJSN :T%x 6 4(<<|Æ dHϑbcǎիWbbbAA5blj:PdNgW {)9r F6 =tڵRINNNlllmmdR9v'Nimm ASSSM6|pժUO>ys窪Ttĺ IVVք Ǐxbbr-K,Qi0-->0--M%xÇ9RVV~xTw2$H˗/8pСC*+t~Ch ۯ%ŕ|m:?Gh!_ u86P[[+lRa~EQRi-&&BEiիWCCCaZ#W +**FJSw~'bZZ;ClYYz<W pHu}7fY@Ś.Lq!)Α󇫳d{#AV,t{TO|T)ǡKe^AAA:.bqp;j(^o^X,*apqɁ S[[ۧOUsUB99R<&)>>><<|˖-**t~Iq?\AoDq%;{7_GX ;Hs_"[ D2?8p`ss3J?TC~{nh}? inttnTXYY7ߜJx6劇鬳H=HgZd]SqﴎAoDq%;|7_G2.4ks,:WtdZlYSSSiiܹsUj練XVL𨨨/B~b4f5kŒEUTT455-_-gҲqɓ'Ra~~)S[HKK{ s:?GO^TTh0Z)VaSusU8sϑ ;?G 7߾v"7/84{Əo0rrr9<777,,Lm۶M6ɅfGX~JΟiiiiRRRHHȄ Ο?/W6 m [;v ǎKW Oq*)cǎ#G3?T9\S¦9F uSŕ||)b /> m(G>v? 5,jkk3 (Xg~#RЌw}wᾎqHsh&VߟD5/cz<\!)$?UDŽhA29rnA i Ǖlq%d絡H5ٍa{t\kܙnڧO_rhky-x6qGmb 6r2??/ ūڍe{;t۷np!*7mz#0/6].^}ݧ|˟O͟c9\ p!$mz#ͧ\ Pi;ٲ&;W^Raccd 7n\iiJMB?w''';99\u֘Q#F8u5..C_tҰ0+ǤQt…Kʅˑ[BIIIZZ`HII))) 233FcddbG!)"EfTu:݆  al-Y$99yŊ}?K5***fLHsU~Cӹ Wɨddd^| srrbcckkkM&SVVJMBWVVVք ǏxbtڵَLOO߳gt߾}]w]\\^[nYdT8 /^}ɓΝ:ŎCRG)++ۿx6W^~_]/Av4R͉'>o3@Ks xKbחaÆ fBs_UBBºu,ׯ\(rxOcbb***DQpBLL\VE٬yoZՙL͛7;ꨫu8+**FT8 OSNpG!]z544Tz_\\l)vؗ9:MټroHHzػuX/{%B~wOS^֥epё#G9O?ntK-jwKTǏ7L[l z^ ~8QMB9ә5WXPRRRTT'P>꧟~4hPkk( .ܾ}N;Ҏ;F}8ShCl }]e@?`0&ܶm[TTTddMB#DGGGDD_^:fCRNKTfS[:~gS%K \6 ކ^2%]H]ZSN5k/믿oh}NKbHwţbӧO7L3g5jW_}ꉓ? pIm@{T#7{l$88*..εΝ;w ^YYY-|ذa}]z3t~8>>!.:Ztۡr~NzΣ_piCûy9$ɔosj@&L=6m Hz }<> /&)44tܸq֕7l0hР 6H%qqqK,INN^bu6t[n}’4"K!sԦ}.\HJJ [t(;W^*!u#GCg? !)R3==__LOOj233Fcddɽ{NNN.//WX1$Ŏ9tЍ7x7>|X#'wee V\i#Š5\wIĴ*3bĈSNO[[[⪪4q!yi.jժU{G1c@-m[[[k2>|ȑ#eeeJV^~_ݦM%%%k׮ΖJRSSM6|Bئb/8qbMMB{:tH=t8t"8{漼{+ZjӧOH'HJJbŠI8 ®]{Lzz={4q!yܲF(}ZޅudXl ]Ϋ)V I#EUUUbDDD\xQ/o3R\ O###˥+EyW_}5,,L%Ni.և7eLz<瞳<711O>k{~^i&gۏgJJ+r=^YY`3fБox:܎ùeOzkjjjjjΝ{vPb\ZU~RnnnXXZSSٳ {AR'(m#m۶-***22RJ\ɞb?`0䨏;F I 1/ pB2}ݟN[UHo\N l֭} E/~8,##_W('''66d2eeeYp#G߿Q/ ӦM?J/8qbMMzNҦUV>}ɓΝz?ޝoSQ'HJJ9MA!h͑U x:bӻKF&MtAvڕMl:R ^qxf^҅Loe~~^nMZQf^ӄu}7f111(^p!&&F VTTƮiݾ`L\rE7$$D~X__eTYY(իWCCCU>w'̺ME6*ئb06Qll;RO)V?]g4WǏL-[t񠠠vQ-uj_|*:H (Oܑ{5J[ObH޸bzb)T]Mg2Qr4 #6+.GS\!9?*;3呑 7+}.'E y\ ###xttteeh$VVVv5N^"ŋ;Ms;8p`ssz_*vm6\(zm*8p?XMNg*bG.\s7 ;bⲱ)Ex*.0%Ç|NöoP1xec}^*=:zW_#hA~S tE|ӧO/**jmmmll4 ]=<555//eƍ'O˥)StaÆW^ݸqc;2L˖-kjj*--;wnW{oii1fy͚55[ZZ?+6322-ZTQQԴ|rLLLO o}W^ݴi +rlhCR.-0G 뒔W^y{q5Xܝnm&>҅@[ jǎ#G 3ḟ~KKKBBB&LpyBvv`n8c4_|EmuQSSٳ Rm۶H)Sl!''Gq6Gy$:::""b*}>}*bG7tShhU俧;dGlQ\6Q\!)S]% .ܹsghh3)8Ŋehߣ w{=z'Z! C{{ 6ׁ7xcΝ{u }sh}ݡΰ:xtǻ;|p_GZx˗_z[oױ)K/L&_ULJ廣:ΑݺN3/WСvu,~`0zw*.Z@?¯y˘ &G @ܘ@`\hi.44D #hW],\hj \hi.44D "@HsA \hi.44D "@} ~EAt:]^^^BBBPP 999aaa3gljjJKK+,,=xwީXML?g'~4e.\(..JKKWX!Bzz>zŪ#FdffJ?'Nttt_8q*444;;glkkX,yyyAXjթSꫳgϖDQu @t:AX2G o6l ?CJJMܜxENW^^>`<11pĈ TUU]7pCPPPEEų>ZBBG}4|pϟ7*\\l6z %44O?ꫯ\"ѡ[nHHTE{ɹrJaaȑ#U nii @薸gJϜ9+Œ3~򎎎ܹs]JaA|ǥ\G/!&\{W ŋ-9s W^5 ٳ ,Pե%~2~hi.Cͭ* 2 f-c@ 7v.Ӓ)d7?D I=6Hg~=[YgsEH{*Q'@49/<)2vs0mzjHs*\`Hs6x i.\𡞳א)ܕ/z- I<xi.Ř1c?.=nmmjkk?~|رrÇ>X))V*7l@侬dpi.ŬY^u'N8!=}~~z/ğrxر3gN4 ;wܶm vm ,>}u6Oݥ|6:xС ?z(V\;=Yi.\߾C 9x… ?oq֭z3gA:uYt_7|@Z,^/b mkkIO+x,Gbbbn欬[oU[o8qVUU}gϖnQXXXUUuIΞ=+=())s{xi.ˬY80i$A&Mt o/Z3g7 \yҥK.=cfr{xi.˴i"""AEFFu]ҏ^yx2[@W%''=:111::z.Tpo;\]2ןyh.uU{gk OIDAT:_W 4@r[s}_p-<=mK/i.)r;Hsܾ覞אlZtz^5ͩP>#re@mx߂!_CN3@iiSKn.C{5\@ nn.@ۿ}vZ3ebHsayinn@Z 4n/V$Rd{4pH 2O E  "@HsA \hi.44D "@HsA \hi.44D "@HsA \hi.44D "@HsA \hi.44D "@HsA \hi.44D ut:Cp(c7n.O4\hi.44D "@HsA \hi.44D "@HsA \hi.44D "@HsA \hi.44D "t:y9z\t\Y\<4,Q])pi.%۷li9ڲe+!\4[r(\hi.%۷liР`_x?|̇1 !Hsq!hi.4Qó:G_;Ç= LΚhcE \h9C%dqhwZFEyĸ rn"h i.4B;$:\h9'0Fǵ n ͅF tl(\<i.{Q[G@ "Esצ#NG Cвذ3g655 }ٸyf}CEDDDDD,XEWΝ;7<<_~>Z+=U GݫWm۶D "砢(2ʁɭ>` i.,77/vʁ}ى'DQ۷>(22rرhZGGYس[9HVkxOWVVIGK?C||ܚ̓~(8~ 폲~F^^^.O1cx/Yf{H?|͚5~iDDD~~~zz.,%C=w\߾}UN>6N$g@*gۿsY,v)Ϟ=+=())stx\\\̙3Rs㚚l7޻t?`g۷ԩS_~_~;r\G*%C@%Yp ~'RTT4sL<33ҥK.]zf͚{Gxŋ-Z$>s:33S|oذ|…ظ۶6Q-Λ7綾^ڶmۼyOY1.7\=oWNp{{{nnCBBFoJG[Nܹs^*f󠹹y޼y}ӧϼy󚛛EQrٳCCCcccy"QO8_*88xF}#F_wu?T">#11111Ccso3ȼ"MN^ӧOqgΜqKkIqm6y!.Z|ǧMp|@_o!\>lذ}^Kx(H/Nv.Z@sD0n.w0@D @Y@_Pi.sli.4L׽O@#E܅O44Z@6 * qC1h{4\hW/t9.@Kͅ֐?@HsA6,r@HsY\%44G)D xs 44D "@HsA \hi.44D "@HsA \hi.44D "@HsA \hi.44D "@HsA \hi.44D "@HsA \hi.44D "@HsA \hi.44D "@HsA \hi.Y:N9 |L!>%HsE^b}V.F x-[r\ Hso߸e+"^b}V.F ұ Or@F jR ^4=7@_;4=MFBO8G!EO$=a['=|Q9g5'bYzl'?o5vsуH^_ `7= G*Ō{:{q%(Q FB]t@7~ʣYIo998?$ {~1ؼǘghm@O@-33h4FFFA=qqq3RMO ߿ѽzJLLܶm 999aaa3gljjj@aϜ9s]w`0~/^zٲeKbbb^~_xС ?^*cDQ4hВ%KM/'&&1B믿X'ÐQlċ/t(0""kl\eZLti.4L.88%88ŢAX,R~k֬O#""CBBێN'ZS C:DAA>쯾ʕ+R}eћ h3?sMٳg%%%qqqfqMM`ܸqޥKy ;wbK3*!H3f< A~Hse͝;CYYY}}c=&fff^tҥK=جY믿~Æ . oME"… ,XO?Y,3g зoaHի`0={; qvy]E'5j_4H*LNN=ztbbbttի—^zwߍSRR»d2gg=@)aC80$e'[úyjb}J|>ivx<)m˲wL)u]w:뺪ww ˍ .&%s NY͆#ho7q3L9<('ӣ9 Â@uʳ1,su \`uE+\7PFEH¦I 9p g!TwKo=p%r-3rH5X^qF5Be@p!ԨBFKhA.X$Ba fwVIENDB`./oar-2.5.2/docs/schemas/job_execution.dia0000644000175000017500000001075211757171206016455 0ustar plbplb]n(FH]&;tnѝEȒ!əɟ>Oߣ/CJrkt#'aD(V 91MEQx{MHOc{ГLU-|x,ۣI{vSb2|N" 4Mc&%"|Y;7"}8o2nksY4?qw(N1'EQQMc~飤lav~j(G;>gWM4 lHˊ楷OŔ)k"yV >ˢ78LFVWBًHYQݺ~-Bx<[L0|;(Uyo |+Aˡc y{TDoG:r/Ż:xu!ۅ̝^Iug`&  5+{}/Ϧ]43i2Z%V~I҂SN梭\<峓tzptx66Y4A18|;zϰO%0z,P$s m1`>r|ncPT\Ç97vQFiȨg&5]aqki 0I50ZC v&` }!Ro!s#V2R鮩GN GN\QgbcMaJfWa%0!V{/$@-mғ +/"(p]:LRebW+bx̨ *> ; iwl]!>^Kq}EMY( Cجnvr#zAZD)sc~/[5ضF7( bCu5Ӈ+rpU膗s5="%JFH4b3蜄W}fOO}t9^R3HN,14W(cuz/rNt){P_"uPa6a:/}&xw 4/H#Dտ 5|-5#G''EOu.()sQ%Q!fM΋܂oM\'UVRىk ^ii1Wmϔ1OYb.}J1&t`7ʟudسǤUa .i)j2 1^ ,|;k>QNa/Ln Fy|'eP3x4gY0_b@q&igP?gi "2]jl]! ScLQ^^ 9t]&ר"az8c6 J)T&݁Zj'T&'jdLC'6rI{iw mMWf|>oӍd_P vS~!EXXy/u$ Vʡ49|Ğ#3޳ DĚ]][Ex_l(xKW/-ء6J$I wK:sk% '&*"/A{BBB 9Jܗ̭a&r *҉kP>T9]tjGJR6@S~ʱ9#Q@^n_iP`@P|y؄Hgf= :Hp*CCvrw0Y JA 6* !)»vq n=@5fVl2^WBF,KWpHWmsN:lF*c|뿏Wy ?ߚD M>sحbD g35 \ٞT$.!|W1M!}Yv l|3CiV1/ĹXg@Sܣʨ98*O)?IS]u5B[&%(0EQ@5ODu%RD 2O)8>jࠢ!p0 _+с)dݝ+U{e"z{ǰ>YÈk|8>] Mc茜QAAAr8ľpTkCt]ݳG*Fbv\/ZCvt=ZhS^#]owWL]./oar-2.5.2/docs/schemas/interactive_oarsub_scheme.svg0000644000175000017500000011064711757171206021102 0ustar plbplb image/svg+xml OARSUB -I Time Frontal node Computing node OAR server LAME bipbip give shell to user job launched ACK oarsub -I oarexecuser.sh oarexec and set environnment variables exit End of oarsub END oarexecuser.sh notify oarexec : END close shell oarexec init finished Enf of oarexec ./oar-2.5.2/docs/schemas/interactive_oarsub_scheme.png0000644000175000017500000111073611757171206021067 0ustar plbplbPNG  IHDR ,?3sBIT|dtEXtSoftwarewww.inkscape.org< IDATxyxI aGvTDA\ wVwںjkZֶvvYqVkԵhqֵ*l""{@Ifsgzyb9 ͜#""""""""Ru6.m @gKmgu^Wg!"u}- t:KzG6 qe{"""""""""""""3 ,mfعz>ᅇi53klÁAt'=QDDDDDDDDDDD*DDDDDDDD 38 MRB LnpdHfvpVV}r'fp`ѐ;/&[Ìr-m;cCQff8ݧʓ'Jtw(T0{@SV}\Vz 0 ^^53;F7ȐyDDDDDDDDD"?DHlp$p 028e9kf7)vL$COYYDw6U>Y59`'>Dғhluf Ѫ4vYk#ɾ^zq|H*H+lt!0)}/reƒ6f]w DDDDDDDDD$O‚H3}8NKRm JL`w_Wyț?ݽ.82D72Ran6w> f6x.A>JwNw*OK?ДV;Owqpfv/yDDDDDDDDD*""""""""3gfS.pݗb + ff_3ڬ} HlLf/J,mU4R=P~`$0 ffǙYQ.2cfUW^1[l<qlI]{ Y3;4@""""""""""mDDDDDDDD*u6_;e'?]dyarnO`@Y^ݖTfQqaff]`tyD$n2lÈT: ,T3xNI.fvd&X =pL }%;/yU9*O̷k"Ҽm!if#"""""""""RG330u2e%W̬'pPJlucff&)3 <u)N*N""""""""""HAY?^d`$nYɑ:0̶Mdk7Kd$.3 <lu)af6. """"""""""F """"""""df[;f%CYɉWArN_^3> ef[w="":H%iui3;XӮӁmˀ.3^\Kl5w27Wl4eʧ9̾R>d;pf%l@2`.(]' G}ʘQDk\gf=YX)3; uiqY^Euw_f BtoZNڙٗBګ+@tA!e8d3pmefm̬Ѱz)ǀ'><[jp`0,TCpGw&0""""""""""E0ÀI5?$}&0 \bfmcI3*&|Lta{(:DC"PTfffH)?Fq KǗJ+@`'} Dի8:t:חHX)3.f@4 -%]R:XL)Ovepw-`ߵrDׯ0~D"o*̬;ѰҶ%ŪN78)""""""""""mF/H@f+M4sp?|VL, _ wt]T$Z% ٠zff-p7PaG=08x&L"yAwOtD]M'X13Ha'1>طU}_ .Bp_.1nX\b* 1wFW>EG{E`}>tϺoO}K` RÌc䒻* /  ؾ ~~""""""""""mDDDDDDDDr:zjYssnw_> fE~y%7hu86vY+)(ݙX `Vf0@.Rϻzౌ#䖻{= 5PԸl`@-W Y{U31k%E>p 2>z%5>a^F,r`sY#wNc̬6@6G """"""""9cf?I_J+@|$PCzIUtiXGGȘ痔 Ю88@DJJlw3@pWsU [vNHDDDDDDDDDHqJw\/|u1ze̺,_.}LH`_`fvA@ue) -nz9X3;80@>?@,}5@/ɋ#N1767)fmlxZ)w \nfb*q OswDDݯ'\JB """"""""""1h`ADDDDDDD$'J+XQ#aʔG f}`B̺[b;ѿ8B x+avYew2T)֏03 DDDDDDDDDD H~ +}r?> ĔO -uY+eKhmLJJ\7""ANzX3յ}rZI4PdqWW ܽ>5B}bJ\ |G+,l>@TҪM"H[|8 蜰tYrݯ¢`f/,W4?ĘuR&UHfx,1%XݟDD C%"""""""""Ҧh`ADDDDDDD$cf֏0|g'sB,Y>f?fϿ֯WB;,TWw BDDDDDDDDD u2sv)aa*D,]iw_3\ xIV}򙒰X?Dz&,Tw BDDDDDDDDD X""""}ލ6KuYmMǹJDDD$u&$l8=@=ъff;2Ǿ1˛]=0]c,FϳD)[aJXTuB """RVfV %j=!Fnmp Sj*oYwqN4 JaW@% 3K׶ث7mĨ2pf6X'A$I$XX,י\ca<""""""""""mDDD$Ufցh@aхOc7D/s `=@40xZ """? H.3{w(T]Nw5)y&Xȷ9$:"J1ADDDDDDDDDDZ*"""Rylk3M%C}DC 17у5I/'f3;6JXDDD5lC`mnsC)_%"Hl,04fU-=<f2sgMJDd3tIb^,""""""""""mVX ̬=p(p** i}6Cٍ.zѡQ[݅άXTU,ۉzPEu,a!l~,dyof4Nݚ6g{/iqX;(a3~$K lkf{rVZ7Yv 4v&Il8j׶uzq~e_O/`w\#\<5unfv;HY5g67R!n"B`,P8AM ݻ*'o`a`痀̬?vY ,<CYD]r [;d=~D7oIz#6mf˓SDi`ADDDF0c#}Hcw9/»e VKWvTu^<%Ƈn0#O*"""5/O sfG̬ RV.f\9Zkm5rHqY_-pB)"wo t&i}p +Y~2VZڤ0̴R XO1<݁'|~;uKEsYɲ*rC9xk>jf52aA*${ts`/`@ڤ+s16?H>잰iwgl`/f'D ))Tpj-@X' iȠm OiX-8~ϦYf ""i`ADDD0VU ѷ2xd?*^`09`#˖ȭʉ|6Á٤H:𐻯rL%BI&Ĭ)PIX. CK:xLrGfv @鵚O#"""""RnMl~D 1$cV.dˀ`w_J4VS&B"KX̬p&/ETS~ρ|5:,[(jr3`f3'"""ŐART6 H̬'pP[>;'FD4 f6!$/`>mfyIGiXZ];w(P$ ܽ=M$lXzyu[Y4>WiA6s<5m(0Ciif`fƏ j3gns  Eߡ.7̗N\|[JW0w?3"""og}p=,:Ĭ7`$PDMJABpfv-cl?*va/5I)U`3pD*F7hoI$"""""rޙ5#lHzB#dy%Tsju$~^]-J[S۬y3[4HcXi̬p;crP_p^_c}<޸V,M4r 39ggYWDDD$3D;?"K%qfVؒ ,LY72p&$l pH6qCIɄco@E$#{v^BEDDDD$ܽ'6 R>3;Wk9[W+D;j!)u.kY>{ri4 ""҆:]6}#Ž?-0-x^r]Gzyf_|j 'fveQDDDeJ/˧=O l45tY`Dc,wB>ъI\ Gb84@}pf}e8]?-̞ ^xk2ɳf6;޵D+ܺaOJp^}}L5XI!yo- &\oA,7?Hz4 ""ƘY` vW]} g1ĿɃ;xf'Lາ.3+؃_jw_Q<^ҋ 2%a1kZ]wY1?kf빻행\C-Svv*m 񕛻]\"YNuw_u)?w`BZ`_|1]v' . IDATs UD`8]DDD3[ش16w$>潖EL>䳼pK2'"""JzQARTfVpĘuˀCi$ ,T_~64ffUDbwOP^gfG q^^ 3{ uyz8:;zaM$-[~;."fNkA:i`ADD0Z6 +l?}`a9/kXNd"S}C 8>d"""1 ,'ƀ7di6 k|{X'F4*3DNB33=*IZ%l^!Z_>3p" }~aDDDDD$,wAӫ%lH)q} 7p_^b7Ozy\lkyζ&yΜlkyζ&yΜl)ge˖1sDDDڎ_cؖrΗcpaa#ZTtcoh>#kfx3{(KE$%/WÈH2^E{VmK~)SpgO6ؿ{sy&ܹ\LcJ~kfYeLtLX?+H5d I??B\$ ,L4uFc\Fp vD_6w|lQ4ׁ+lcTYm?q4)`֥.zzs9{mن .mw_c߼\hXyUyUy͕f*j*j գϽ< 7|33fhpL>}5k՟h`ADD™0 fw&>[$hv>=8 [mdODDDʫCzܰvg2 Y{/ fM3{Xmy3;?+؀juF׹RV 8$<1l cfSn U}cDDDDDd5w7ᄕ [cH8ƦM\_7d=\{oܝxc^sw^sw^s;;;qK./fwڕO>;ZU;;\E\E\ikr#=~!'OlN1b Xuʭ~ H2ہoN;Ry E`,0yh'N%n-\pKvDDDLB\ .@G+,tY[pfv[lwCLp˳|~|8depdHN3KxZTNB1t8˗sW}3g_SS׿N=*E1;;\E\Eab^hwuSLaj׮ÇgРAX꫁uk;]Y/ 4B>mae\+wlfǸ5Yf% ~2(Ęuu@V??N" D%{OPy!f6 88hm5o/#Ra:&7Ui8acV&N螠gYV曹 x039>lڠ.kkk4{5WQ{k!97mf{Wi8=FUךڷ~|;\} j6h#:,wO녆󚫨+yUy͕!z]ӧO[n_ngϞ5={s`A """`Z 3#ٰbIvm֜|[k}2&I/ȯt ˹~@7{wȿ Q{r9* = \a5s_Y =#C{fomFa͚ofj-V)P? w;PO\svD㈆v"nm8nuMy.%KVwޜzqkSxaQsw^sw^s;;;D>"JYx'ܹ3GPXbf;: 9>DAЙSAgX@^gfK7`֮0+,2Wry1kV4l*i~~fDw&@yK 33`3`k;J)m$Svt#w_H]]ݪ5555mB}i`ADD|Xg;}قm1 N99W_fKDDDRd 4H#a}YVX0~1˧n7g,] |ݗeA.D+-q!2dkm.ع=✷5Iꚪ]bW]u1cƪڵ㨣OW^MBìkջ8\E\E\I{Q^\ydL”)SXt >!CPUU0B[9ࠁJshwy&Z|З9q.>\]*IG$3wPA*U!AK$ _5Gc*]dJ 퀳fv:P^/m]6v)mkG|e'POi~ ̶K̾76HP_ \L版Cv׀=Pu.]tW^y%uuu0`v˅+yUyUFh[(g̘ɓy饗ӽ{wj+z ,Hk ~zH uԱGw_U>IVCL$OiuY^?oN +3 ,4?1/b9FW; )l֞/5U3|~s饗hѢU{׾58jjj qaQrw^sw^s;;Iϑ\?6w\|;vȘ1c0`@g_`fUȕWS˺뇿޲Zć|;,CUйa'%ART ˱a@טp!t0vO3S#ZmP¶?J$@m#l7w7P?7c0"""""-݁ ( lEmkϕi,]?f/uؑsqѥKܽ =.,F*j*jJ򗫨+-]p!׿x衇Xb}ڵcԨQ <3kvР!HeV_>vtS$Xc1W) э̞pe("""z3aFfY?4)ARWK0kcW_"ӼdfND3{ _ff4Y'ʑWJ~nG "v3ziie@q_=wx뭷V㤓NO>kThEz5WQ{5WQ{5W5\E\ytRx>/^걪*СCnr'ݷc0;~^: ,$2W|=gՍe˓PDDDy`PހUO/H50]b' 70~qƹ"3;pjv٣Bh cq|ܚ;3?g}=ܓN;6hU<_hX\E\ikk0B\Ecq=G5x|5V4~?B᠁J1;.Kcz0B F=$|"""R_|ldXh}y:D@4p"fqf Pn0԰qwaf$h̶w{sl_:8XӖ&}(,w;A """"w%YKk[sj>OGff'&cnuww̮'l}8kt.ARSX(w3VJ=RH%qM/ZYY?O,_|s'sPUUՠG[󚫨󚫨+F_k9s&wy'/b;wf_~Bi`ADD24-mtBHi9·ftkc&ffWDDD$NX?6p I8~N31:HzlS \ KZB$+ 5>?P3H|u_X ,H=ދ/c[YrF?K._,\pnݺq 'pGRSSӠ_^/4kk4{5WQ{5WCskk^c'lp\MM [o5}Uk|'6iAH ED GPDQyAA#E>QJ=$!R !Oʹ=fOݳZ{k~Y뾯u0Əߚ\D """+ ʣgZ=*YEc7 H4e2S'l4pPĘW3T~mXWׁ#L1 zI}3JfR*[',= IDATEa!]Di6""""R"} pp1p*q27Hdl{3ロouֵ8p ^x!_~9ÇBCeNf+P{3;RdvҚZy^xᅬK׏C93kj@OlXMVۗ ڰ ""R*Eʠ|4Fb8[x?qbۏ~hÂHIpf*Z;g ۝A{ {L]:s0 R2>oHڹfv!=q^6,C}Ր\R~7BrO,-JG#0%zDDDt1dp9nN&7@477#p׳dɇ{83N;ivh ^iWZCi}+١J2;^ <}(o8C)//yAO٘^6,-mPV޻ƀaڰլ T?T DDD$ hN5!^Вw^{ D/O0]|%Jĕ|( L=F{ԥ39w\Es55&%%7H}sd6*݋y el>Ι1cW_}53g:?e|kEg+١Jkvr95#{5;^i\ss3f'`˖uQ 8u~;֖6,Zij\dGCͺIV>mtqim 7+ӁbiRf61oqt錙fv'>Wٷ53sz3{|WN K,ubel-$WQ7,4A{0w|&I4jΜ9sy'z|_Z!-4TvJ2;^iW.fbf+]p!ӧOgڵYwqG&MСCsXqnFw^ """"k¦9fx"<:ؒJѻlXh semwoPODDDu`=0&By 3۰q?He<!)D۰04"l"<ڰ.["d oDDDD$}pM rk~ojlw{=n|{_W9BCeWZCdvr95#=P{%jfkL>{/ÙGfGrkvoz>6:;3'TV~g}С\r%\p 8ۼ1P{5;^iNW!k5;^iWZ{33f0sL[sG~u@ONbAYmiÂHi7\:-VV4Wn=޿>pe;t-ȴf+P{5[dvҚjfի_|^z)ReeeL0C99qoPeL!浥 """a> 9ꫜC>ѾshBw8Mmb6,wo6_"Č$0xZΉyX$Іظfg<#|T齆t* 4G!:*Y=űa/nI-w:; qNٱ+vfon?뮻E>|O~+w .ýn@5;^iWڌ^f+P{5xxg;{ĉ߿ '7Іf1ټKzGDKC5hֳ՝CDDDJͯk(M6{ܽ%fl+ɧ+\Bx} 021 3;_S_v p&9nr1vxmXpZ3{/ψ`"D[ ,lL 51dHdy9&EW(c_xj^}լ1&M+_ /F\Ee+١Jsvx#;^iW̝;~͛;s10xneNuFDDDJǛYԴxlvl"ǯrK5onՆy]@DDD 1;&cbӁw/gCd\mX5) QIFF D|7,af[q*3IM7$.87U=eZI78I,-6=sQ$QXZHl,phĘEqtxd9`Ps-qqݼy3z+_ŶmZ9/| y晔g-\˷S Ӛjf+82CP{%jfƍ1cgiuQ>;ͭyQ63'ʱ浧 """%kodsʧt"6,rC]ݭ"""~Ӏ'#f|̾Phl1D_w_CNf6TjOW<^6,-,̋cf",J} gfvόWNd-+1ՑܝD'6GɟI2NqN,c0.αuuuu]zl޼xEE\p\p鋋K#;^IgǑjf+١J2;^<̜9u3@~8C8CZ3lgNhڿ[ADDDJi8ɍqfخ_h\ntmTNǛR.bxM^Sf 0%bfGPY9C"FU?ިKԳ<4KH' 'xwn?ek%0IxBaK-oiv9?'ݽ)2bx1 Ƀ.XƔҸ\677ss7ׯg}6^z)#G1&jf+82\eWZU__++B}}}q3c}eĉׯ5#onӆ8P x;wv=0 {dEyb1{ `SõaADDM11ǐӕ#}W$ J~C.%TOԧ,kf[1vl_8-wK= |%lP! /|8.;3$p\Ęzd s]K˘bK"/ ]wg?N+`]vBþj$Ctvi}\ c+̬Yx稩nqc Ϝ絧 """%+ۏ=?8|c !k o^mfY^ibNǛ͔B,6"qg^QG9^wl6+!j10 "K'M'ٍo$'S/0qg#̈J.%#y\ |5FU`7 )ܽ6Ɂ|̿z|Y{97?I߫5\ /u#+d43jf+NcvWZCo3ϰiSfnjÔ)S6lX( sɈkAYfІRt;,/,%֋ fT5w%oPMM$7 " HNs~F^3_1]>u"j)J< |x*B) !>'Nwus30(bƌz~MWN\ F߉ob2;z$ 醻7s˘[h]w>h}ݗ/}KuQ]EIg+١*5B~zWZCdv:Ozuч ώ;f|a^{ڰ ""Rb]3#E' ΀`eefkяWy/ݽWb"""̾B<2M~M Ygf#[nf#D_N\'|2OI;1սH91]mfcӳ Y!VCNwNv0~߰pp-qOYcԕSSZ6YLsfA82]}0B$ZXJ%.΋όӱrDDDDw?̆s_1Jz֭[۹;m=>b>sgҿ.CZhX١J2;^Fȯ=١JkvWZCeyuC~8#8c]ԟϜknmX)I=|vU }a.{"ѷ7,4 9kgn-b"""V_&Ő%`}bKT"!]5{߹^a`z\EGc`|6<,\|*R=4 , vblq<2z2?TifR2d<\C̎rDDDD3=lL1w^qr 6lh=>h =\?|***FH K!;^i͎+k~KkvҚj$CP{?_[[/̬Y.efp}ѭOs."ӆP,ŷݿè}FHeĝ4Rj닉HjZ3;xx2ifvω!/vfVcnh#"~m\-^efxц$D}Yfvwĸpp<)zL[f+(pH qOĐ+~\!ƀ_YOxىsc$hfkDrv؉Onq|PceCq7{걬SO=K/QFe-8Kac(sB\#מP{5;^iW١MvCC3gW_>k2e XԟϜ4k!mX)Y}̢ {Lx ~ ׼)T*Y"%s߮80/ Hjkfv%pOLwz)3;x?߹-=D9[rt?oXhf7뀳k+, _ffwrUEkfv<c>v""""1qlTƤ1'keΜ{wq| _`wina!,4 );^i͎ګ+;WZCz5773o<^|E㎜x 2$'7ʼBuhODDDJd@?@#p<&NyACMiL3YJVDkawߒEEDD$ޖS@6"37wSn^l'z9e1us/#-yο H8pόSoIffK^"1a\1e/5i"F](3ߍ.B̓tn.)Gӎ$&nNDDD$wSr1nܛo5\Ì3|\qpyoTz/f+쨽 qP_+١J2;^iW./7n:?bN8ƌ:>E)yц f`-;fe|cwDSi3h/z:nb.c<xL/26׀8bkfFsm\hw/y37٫OY3iI9𩖷-f({`Jk{w:?'9c:kf?v=MUPr-0<عdHDf688h/]""""wE+aR.?.I㘸u7fɒ%p <#YsO/rGoTȥG14fǑwid+١J2;^iW+Wcժ2ADDDJ7٧1iifp7'd'3;̢A \b9p/K 3Qp,-~߰#?WGZD}fvcFoy2'}nJo1rgn̼%԰ޯhnI"f{S/,"""%\2i|ly̞A{~ 3H.'cڕ$xV-wl7=E{XІy|OY8#FJfיfx,w d~"Nqewb{<1MY۟vp- 5`:WE 6AaIdqdĮ]WkCO.v>]DDDD$mm9%/uc yj>gСC99묳0`@kV jHkOcf+P{5;^i@ee%/2 ,[VVƁt$'e^1:t҆R 2w2%M2jޅ+}(D#kӻ}l2mjEZ=iq`f"2 SZ;dy̆˭mx]yۃݬ\2=_} w8vr5q SN7;%e'3-oF`1)|[ T-ou@2_/d'2_3wg{]w> \@-ol!0?nն5K``3{ہAd>IJ&wEDDDD'QG:B^_W?`ڵ Ygg?YښB4f+jvi}iP{5;^If+١|]]:gϦ){]{ '@~ ?9:aADDpfv&E9ƎBPC5;4JkvҚj$CP{Enjjbټl۶-ܸq8餓hIb~O7Yq """}3).ۏ?izn_l1ϨK7'5$YB-{5`+ydfr*t鄻7jfETL_t.>!iw_W4r-ÇqڰOY8&1vjO~vw7ϓ٤uA ORJZLwEDDDD/~J=%/rF\9q]+13fke̙YǏ>h.2cY!.4,fvfǑ׮ϥe+١JkvWRo+BUU=`waN:$F] }aADDqx2OZc1/n;פl]˞ FݨjjV1edY 9} w/ւ17d6Jr<,O# wEz']D7ݧڬ6H߀=-J mL.0gM?p]vx`&jqds4CdvҚjfګ˖-㥗^bVTT0ev}I,o,kձ҆>lZxk񗹓Ɨ妟ӡWM =ش$a~FRۨ\?.qǃH{ 8} d p?YYpI:1IeyοІdD}?kCIb"se.+vfRDpEW;ߘnUa9tcrv{=nFz衬\r &M4+ą!gWi}}]١JkvWZCo5kx饗X2{W?΄  8?9}u^{K邻gf{n?:ͭʪ_p.o*Z͢p,Ho= DDDxE` x4R:Pe>s+Xܩ/Ws/""")3;.pЯux>!>"""qs#_06:qill7 7tWn=^^^Ωg>5Ҹ qP_{)f+١J2;^iWZCzfϞ͜9shl.'8*FV\mƖ-[OX=30pHs yr;GpiQB XBii|#x{oX7tvbH}.pM \L%&wksޏ6+dqfWSyFL1}IC%QpMv(CC-N" `\b࿀Z^sI]?ٜvd+HH> 4Gܽ77<[IDDD$}'<ᮧ5&y饗kxW~\|^-k1vn!k/P{5;^iW١Jkv ,^_ʬ1C OdvKd}.Yq]+Ɏ!knxBDDD3; 8$ph̑| *x8mu,ױ9<[<%M`mU= p2.v)rz}}~!}`O:0dOgr2ۼmfW[d>p{ЙAq^HfVLl&5Qo}`f Ӏ(&2O p(rXY%ah歚Te5X,r.rDcfnswuSQ~2`&z>1]g\wug}9Sаs qP_KW١JkvҚj$CJ+xذaC֘0yd89XdoN;4Oɸݿ """ҁOgyZ@ 0+9΅I3Mlay{M,e!mD~߽ my{ ˓v-w$(p, |$n%sW "#:WF<& v%6L%+4XY+,Qbc~f_L5kb NS73''}a""""1qd~x`PCdvx.""qj?_vƐ0(1X;ok{df88̦]<-hV?Vvm>$;ž&z>1!el޼'u]Yw1bwSNǼ{>jFZ_{)J2;^iWZCdvҚjBdoݺ3gdɒsf~ǔ)S(///B^577SWWGMMMF\2?[~/ """%3 \L{p e+L8N%4۱ ԰#lfY`2Wec4S:*YekG֦|@2>&"""3 mj\?p$`2m[V}+ [*Zfu@ """"Ǹ&z>ku:~M>]A8393!""""""""""""7HZFSS> XbE~q's2bć uai^dYjҚj$CP{5;^If+zܹ͛sill:7zhN9FUV)khh'o>aADD3OgGCnl {qB[!60l*RúNLY泚٬n7)c r:8$61d4 < W^TDDDDDDDDDDDDJ~Cw=M|(׈ә6m:7yd?|ƍsfPBjjid+١J2;^iWZCdvSS-be`vȐ!L:vۭ y(BHUU۶uXmw[hÂHcfF) g~?q/;ʏaIg0p_q0ve6FO^zjf-լڬseby eGs!goԲ`1O'Y۹Nm;>walK\s ?|:.޻Jq!dZCdvҚjf+P{5;^^l3gΤ28c0aBP ykjj?uȫ """}>AfY.=)++cذ60>2x2{:=]kk7lay݆+{q<;0 21 a,C ӹ4Q6UJJe3ulf#yƖ;Lg)g@󒶕,Il`ʊ'&=j EDDDDDDDDDDD$t>30qd|(-nOSsr衇~ͨi]jvWZCP{%jf+իWoaÆeeeqL8.o,kz^!:477SSSCuuuVvwϾp7aADDDPdp0g̈]oq7b2{;0F^U6:XIXFwq۶BJ^GOSfyTgo_& 2h0!ach#;s8١p/* L~l. Lsk'""""""""""""Ir29M$

E """)af/W{v6f;Rw6iVsT~[CZHlf9Oq=oroW6?t¶ܽxLcS}>kL6z*>ص=7١JkvҚj$CP{5;^Ifv+3g[n:^VV>1&NHYYY0 y8Css3555TUUZ ݫ4ɛ6,Ɛٹx90N;qиO!7fiL?Xc] Y\[Z""""""""""""alsYsm IDAT䂽/]n~8yKq!dZCdvҚjf+P{5;^i^nsaM0? eq~!ylݺ6s]}~ADDDef#ڟ`0qg8vdBYڨ YL[,祮<\ XKDDDDDDDDDDDDzNal^r9GFϯ[[nロFsc,RXX*١J2;^iWZCP{%jf=uV̙ʕ+;c=8:th0KZ~mQ׳e˖o[<|t'mX 9p9~(3F]vgWw:;)""""""""""""er׹\ǑQg??)UUU+**8ә:uj]^;ŊiWZCdvҚjf+P{5;׹̛7e˖u3fN9vqǂ.gLJ㼆nJ]]]~LDDDbf'?7h & 'Ec(_9ڨ j7OrUx~\ͅo'""""""""""""]qۀo0.3Jr^{YfMrN>dN?t***bv).L2;^iWZCdvҚjf+8z׳pBy :S{8׊s^} ]w'HaADD$f6 (k{n6w{ؤnsQARfN;lsa\sYc䢾8wm=gfs1}ٌ5W^p{%jf+١J2;^iWZ/h_x1o6 Y A:Wka^ss3TUU|H6, "sڞ+//gᴝ )(6nlXMZB.1z_8Jv>PKDDDDDDDDDDDDZ?A7hGܝ{7x#ܡʧ?ivuXbŴf+P{5;^iWZCdvҚyQ[[u:'ү_;:U]FHHBր 2(:"EfQDǙ>f;˽su{;⠸!P u{tuZOU>U~>t9眮|>9*u20::J4uQ]]>rg煬oEbY?ϵk9/~jhʚǞ0]BeetSWc.BY""""""""""""K1tf?h!G\<}~w}G?r_n7p7n;_5_*~ZWk]ۯu2_*n{3uwwsFSmٲ/Ek/$Ə5.u 133~ZŻnxV^ ;B˲Ӿ9DDDeYT'\&[iZRR`/fMTL1ʷxaƘE(KDDDDDDDDDDDd2,X#.W=A>ruuuk.<̂Vb#d)sr׺5_*enUZWk]5ٳg)֬Y_Ncccz?7C U$CCCLNN:W\q_پ}{_k-ˊDDDeYaNn*"o̙'6 /)M|)Rc"%"""""""""""cY9r|__:SS[V.5\sRk]ۯuknU~\sEtFFFx9rHʹv*:;;ɾʡR_733 W\-2竖e}([@)i`ADDdX Ue'U3Xt '|pvc셯HDDDDDDDDDDDdi19pw\9*8_җPxmm-W]u]vp5˵үZW)sr׺5_*~Zb"GNLL/rsuuu\qr)o_ȵ|sb1q|OW^<7H loe\A`Y5sXݴ%gx]" >¯r0d""""""""""""Rc%c@- :Wc>9>I8 r%p5PWW(.~\sRk]ۯuknU~k1s{fffxٿ?X袋ضmea?X1DQѨ?q>QSS~Z8l,'[P)h`ADD,E}¶'u9Fnrx`g 4 |c~H劈T4cLX#.W-z?c޽{.o}[iooWOU\sRk]ۯukn|9m۷)gcY sΣbXcrٶ(CCCP(>^Z[[3Gr -eYe ( ,eYu7w$ޱ~`<гL,g&08nF'nO'l7ƌ.d""""""""""""K1wɕc{'䓟$O>#f˖-ڵUVVo]׺5_*~ZWk]nnȑ#⋌mٲ/H$_B+qv|OvZ PB[-\A^H XU8'x:?ʺA묑nkY`611o1q1c>Ƙ@%. :s˧?i;ΟtIڵSO=uRֹrRk]ۯuknUZW)s.7Q /@4MYf _~9---eќ_N5s\ 211^tENMM v-zM+4W,chha{￟o`0XPBkX\A^,jeMV;x_8#@.v zAX}:~|?c6s6 !UVݭOPoBl&244mYqws]wDϻ绁S,˚R/ ""|aj!0[s+{4 *p %߫zDDDDDDDDDDDD/RİmN{|ddÇ300Vvm۷{'eX]GZ@uHFeO~ٴu  %*N$O,{c$peYc~em""""""""""""K1j1rd=e-[wmny5L< u/3c~˳{kY.z~u./֊Drd|\Y(:RwzĹΑ^W9Lcn#cm[pm-{+͋cNԒ\SI׍3009_wukˑc!J~~9s˺|#K\BqyVxGvf/́|rMNN8vC=,PBkO-(KXs/Vl}`0ӣb +)@:7vYR,F`{\͘4.&]1~IugkskϢ؉lץ~-.ks˵6g˱kek/f(!}JHy~r,ZY _5|  h|sk 4wcʲ׽)DDDdYۀ7ͽD"ܾ 4fj=ǟ+e"QuM˂H1AEpg6m;-ԅcM5|vY cmNao j_kZǚ*J7?ִVUHúp1<[qm(!k3Zmb2&~*Xcbb\*>ϳuVC1 Mk`ADDg ޸ik\]8rb,M Xhd%빒.6 n - \cR]o1~{cM\w%ߛk Ma*bfx %TPB^qެOq&e] ,uSSS1::3%\B. :(f1[;H,\=zݺuc 壞N9Pе;x7/( ,\bYƘ_yVHyo1;̐k`!~cM\w%ߛk+XS%& TѼ b6fL4-GqEHd6ΣߍEx{w6q|]F333C?CCC$Zf gxXġb|XɓeYA^WWWsɊ[XfW׏vkw)!j r'idE].<b98-GL1Kn@ehhMww7H./_)B0tKͶKXS%]ǚ*Jon:Pfx9[k2PtAnȑ{7Z~ (ub 8'> nvɗn4eYv8j`ADD$6ν8efεnsu10YޓŖe]hgT/da!Ӷmx ~ӟ2338gX|y\n+ekZǚ*J7?TuoalJ(,\<+"GLzg롦c+]gv-j [*ktl&G,{"~??%`@1-aVJ%@ """y8½s0-]]? RU'Rutmlj^ h`ADDDDDDDDDDD$Յ\o5sѬq W^y%;w,N&]1~IugknBumBbs6sJp狡rscJ2~>NK./| ر/S. %}DDD\K]W1411XDJY*vr;n<Jp.*UL mgaa={sP]]=7N:$v7w??`.vPͪ&]1~Iug׹v9 IJȜcI%$>?ʚ#ϵƟ/a.r,[k'?d׹:f#S=6==Moo/$:xꪫC>SmY֤Wi`ADDĽs/֭f!6QDJB2x46puh`ADDDDDDDDDDD$YQdOiL s}=cX!QMM o~yxYvmumk+XS%]X3m{&0ېa{6O|5Α_\^khO#]CB-G6keZG~WX_Lcccjp&~w:2MB5n0+fff>Xbw7t@b@C5pW 5 ""ޟ} r]AV]ÅsVZu1懞("""""""""""RފX(mێNc n-k>̚5kƖKCk!1~{cM\-X=/mjp= )JHw.ZZ]>]t V鞋|^5&A[[[ֆZ_]Zk|b mhh?Lmm^pXeY׫g{11;FbW1dtX<Ƿ h`ADDDDDDDDDDD<3x"qc LNNcW2{Ygſ>zhr[o~o~{'uLi\9TE4lk(aʖ#sqϋS IDATpx~^9?j\ k /si!1۶TUUO|S)sj;H|qIǭy01i=" .Hu,cc]k.,k1'.%[@> \m`p|loo===##ZcMKXS%]XV3]ܺ-7%C sQZrkt-Y/ZYsw(!{u\&`bLLLj8B?m^UN ###qڵ|3_qe2 ,`YV'pp8` S^W&YYB 'sB'=*ODDDDDDDDDDD\-G&6sͽNimm{rVX5?3nsoMKXS%]u.ynw1!X"a`AC ymtmke2nQY5:b);F,òm<⽛b&''9~8$:?Ϲr][=DDDr{zUy%grd2 O"jI ~=W',ŲOc<)PDDDDDDDDDDD<-daakʕp-#dlnoW˗g!ybc?֤3Ə53pV3[M Qw֒Jڠ2.鞋۸u$>se(!{mt󭝌{nmۖ6\vk9Μ~Xh4{ƍ<\{HVƘeY#DDD,+ܚxǞ.l_> Xn*,X$/tZ.I4Qy""""""""""""娨O0ͷ7\;wd߾}LNNcq 7|+W:ٿ =hiiqWN ~cM7LU]@EDDX3f-6^wB q&|k`A|ُM[e#װJXmz{{bZ#wy'wy' H^ȂwJ_>c ,q?H POQtzHX88剈EXH7Tn: ~_011?8?xʵWK/P? Ԟ5w8fi ׉D΍c}{N5-iϏ ᒝ{;ܜ|,zܜr#Xי3&# Օrorm^u+Vrݕto&jN,B털lҝKVYy!Zk%=rJvP{ehᖭ7A ;N%q!r]744|{xeYk˘gFHv$quAI?CuH)4v6igܡݖe=jg""""""""""""mm;.0ߠqFV^OWl$벬4:7KxG%VҹZihhȘkllk9N#OwTHy4$~]Ƨ`2~ړk͉k[MyבoF311c!YgŃ>ȅ:N`E """9Ŏ,(Q˒wX$'`cF9E|`k7\d`'XwδÂH$B$q*$۸"$i [Nmk#S̑ZOH[:qUXծ`6Aǧ',[Ot:p>v!䥏=ot5եK;lwbO bR{)XUwyrvX5r4'4ԗPB*n(!X 8֮]_ם ʕgFHfb[ӛ3?88$Iu~͝/$Ə5Urݕ|oQSeoOMl \樄l9Yk1RG9^+{!]\樭t';s}cr-"nnqxB """dYV5kƂ`3$L ^_* ǿr1qmaQryOāW'"""""""""""RVu`JX5Ѹ2bojlp`*g30 8:?{?A)opF:6>Doo/M1,S.?8><2Ph-7_vXcviNXM(/髝qU5ut1~{cM~;q:wWHi =K %d^+0rȕ[j`C?===UWWswp]w PA)i`ADD.^IA񺍍˶v/ ,4A#;]|h=p&X}R[Ƥaa)6̋Rv})Z뫊ef-M'ޗ~ƋI.Wuo'D"Yk)u[KCm#/#MM?e-ߜm501xWlubTc丿X,FvYڝڳ?Wu(o/cXS%]ǚMor4gkOoBC 6PB!OK#W\ԫ2Ftww355_Ӳ,wrjժ PAC^%S磈Hz$Ѵd,tu'[Z_*js~/UQz򾶙5Xuܢkǀ:g`x+޽{4X`x_?C:b_y~6ʄ5ҭYӲֱ@Gַбj jZL Y.>pRkL>FMpQs%_Jǚ*J7?ִu:Zu v®9Y4-8Jwn16/Z9yVsv#W~/ȋ2]7::Jww7ccc/2xmV;-] ۚ**@iwxa 87+6NYX^B's/s,Qy"""""""""""" lHrr7W,&?޳Sj8zm P,8ʚ !~*X0_1uX`(]WԫOt033N `ddͬXš_|qN,^] oXRO4'1eO256?- +wdFǚTwyd B0XJsbʑmܒJH|.9rh( {BctwwFn۶x7M qeJx ,$,kau3kXٴ輡L$ikv`%Hel8ọ̆a]y ͖ecƽQDDDDDDDDDDD,5V,ڙvX[#B5@mGJ?CSSpMXWzb767/g|h:\G;~g9T5Mt4rx5ǚu\Niiiflz_&^gtϷGZ_ss'`jV8ИͲM)Ϯ'k;'Yޙ}dn-/bp9&]1믟R9 y_C ɕCC 4Ǐ{ժU{シ7嗰9ܹ-ebki`ADD$ե/6WP]Ij ,ؚXVs^X+eg7:c-OzTH9x!I˲\7ismEP1?) LF cŌ]l\U1ƺjԭr,V19yp8vݾA\2,0OX@Ogw0hx v=^.<&XLxʎ5-ҕ$>P`&e"5nWJRz[0U I Jp^ 9Ҽ2\Λg`!1۶顧&>яrSSS,'LT//$i(.nCOS}m{RT՜|#U`(݄mvܡ`5p&XWdX(F\qn[\\1eWOZ9ܑiѱ1jOe3}`=Cd}mSWWg>'"^k-[v1CΦɩ5hq"OY}>\ߙ/IjPcs!lM"q`0`Bsyǎ4#BBLu] kIsf~m""""""""""""dYִ1(lusLMMaկJ0-bXے ,A^t\sr@U|8`0B9)~MK!<豽ToJ/i`av`aмr8<_!*upct:Hcg<0~gl=`094|bX.?t]=B %,۵ᣡ/ǽW50wlrrG28Ωg?YrŠLr~4 ""t{PQ, u05=W_755jL?& h5;SE] q0 6+8W|*-˪5ƌ{\_, nw92g)c}cWW 5;8@e_+̇Z/^lK4,`j1=ݛ?4B+SF^745noߡO:&aϴ$U)šĶR?lLٍ9B1sg -[1u)w@(ׁE5-=' ,89j6z<\ 1X[ C=^; >(򺦅5ɇ:N7 """"""""""", 좐MܐZ`cpiwG tpoa;L/9`E:hc M8wz0 LH]N޶7+.0BSW#npxW<F}}]J΁05xci1Le9)R!1~iߛk=7+H`M;1Ű8 %HyJ?F9rk￟חt *u! i`ADD1Q䡺y=u0:3 IDATdY{˃T՜2-xu,{`NTa33whn`޲.cL5ط9v1| ,$-TBgarr@ӎKOI0_PGpޗ l% f^efz +_ªfڪߓ1Q:.6/XQf`9B{3HJD3R9D1?anEW\HM~Li w{ɋ?ִ͏5-{K\s vJH׼6 h(A 066Ƒ#GFs<w; םIJWsO """N''h]i@k 6|bw}}=t; jw|3RG TQ zyaP-DDDDDDDDDDDDdI,kmBrܧ&eRK384B+Rvgf6=ʺ057YVu@1tN$lKxvUP882Epe)S~F]STU3?`bgI5S]!mΌ/N U(]&7uR7?TuWUm#ǿ5>$oѣGS֭[ǧ?i:V,q߯;;xHZW """N(kkba<4XXߣHYY׽[J BFf,һDDDDDDDDDDDD|￑e`!|)G,#2<;,ą(1 he݂RCAĘr}M:;N{NDDDDDDDDDDD,| QvQp˂1Um<?~ǰΝ;kپ};}}}<^5|n2(M!+^DDDJ09<cQ Qsb` drI͎}HgSM#r$HsHE,c7weqHN'gsǸYbַ8s8t ܜqm7-Tk+XS%]ǚ|W?Z!l9qJHwN`qFK$>mX<]ZM%(kڲRנXMX(ğB!+B0̌ [J Lq}M M a ĀZ˲1W*"""""""""""k70w'y5Аik({Ykmm宻⮻_ؘ%l1~IugkRSTYƏi}}=R?;F<.c1 r)e:< 8XqJa!D~TUzlE$Z vX1\WP,)ej,1ﲐi`aSSSl߾P(8ns9'u4u[W.?֤3&\]`7ALC NH7`62.ubP,+iAh`ADDiL1R , q(u8綨>/EfV3Ѓ`) UӘB /{SHYx+ 4$7e37cL~k]~{cM\w%[8{m %42eXL|^~kd 9PX?Y/'ϓDDDzf(/cQ ,GC^a4YUpxOG#˲F1~3dmt> sUUUTWW399ݻF46:N'9O<>UZkRǚTwyB6 `w^X` %RnM07U0dddCPYvAYR~8 ^ŅS'0EpP()X =]0 ?Аzh^7U˲0|'K9m(grrX,]w7k8p/}Kuuuwppe.5 Wnݕ|o~{cMnY;X sמdI:/c||n:::0011C]XsCKi J= Q Geg ( ,8&>Bj5 jON8lf WivXMݲ1>Wgrq]LrJ1}G$q7}{hf"Hf[TuWJ͏5e1&f'vWPͰcCޝIz5iaٖlY2^1 I^.q6Hr Bp 1N.[Xr;qb x l1$6k5rU3K-SvtUwgFzugyy;k^̙/q] >ݠɰÐ)$I,,= $勏XɥXPVWpӺǸtL290/܎uP$I$I$Ir>5/xSqh`Ν<'|'{oFnf?w'? m_?􆍳ԔˋwR8{_[{Z[r[Xس) my_sp눟zuOMMM ' |Mk"ϧ2^_ $.pvDeBXeY;=aA#X8} cxWa`A$I$I$ma!b5/H]]]*1rWÇxgyͻM;v/xŶq~m)i=ί-=q1LnJAyo 8yofWszK NPx}/o!d{ْX$iuBO/nҚ "q]`aX0p#?`Ώ%"@/G5Tn|hy"I$I$I.!_1~#jr]ke"Mz0"]vqM7,sssLNN{nvyB=y6ƚYSż`Jiaԟ0/~i%0 IzOsj&l߾ aeRWpӅQk0x',,I$I$IqB1~+t j^c$ ޽{/ ;a+\HJM{rIqO5e􊗿 L$mߦ[!k?2y ,H3k0ήy<B`nZc`AY&~^K]z$I$I$IBcon.S34D0,h6.}kKqO[q8{2TgS+d`A$I$I$i17&0 _ڂYw;kRS}gjOu\t?!b&X$i,tf^cdGd;Cuysk9n7X$I$I$I 4煣 f/זyR8;Vs.$uVHst +I6{`)pvz}<=udx>{K݆ؾ rhc`v$I$I$Iz!rGl4x68_lVm)i=ί-O]&M]H[\VheM!6I6{n`O=ƍה7`ikOX/_SlS4qg[X8=٣$I$I$I!juB[/M&=qw[_X{wîޅePgz>=x~xc#4$I=w>}{犛& , 9aa"N ,lM-} v(Y"VŖTw7>+0Ut?$I$I$I,pKq?;v'O}ɺrwyRSmݷxIDC =x~ ``A^5FБ/]b77\L큥s=.d{s`Ef9uK_M s8 E$I$I$I4B? -԰ڥ%jؕ$I#jXAJ@ {(m!lР#Zc|?'ٗ/.g)_Y ?S}OBuy=l s{8>pV>َk$I$I$IB? d$}7o6z@ {(, $cTᫀb8X}+ΣK_ܧ-,k7^Gִky/?˳\+kta>ϯn|>$I$I$I2 !lw}o;x;O1ns1xrRر#@39!?X$!|6y+ygk=fmHNĩ=aAY] 2󐄋q;mrڇWYb`A$I$I$)/tC o{x޶nEU??.{(G3RS^O[ꪬ躢k׶k5}Qte}kgmg~ݫm5*3>;t 0 I19C~s+yñ7VvڳW`au'Xڛum,Z找pxVY_BߤI$I$IBc<.  ex_;{sYjS^ye֕ YğK|櫯{;/g,n~|b?ɃNX8+Vk?[ 3cs%>*K§6 I$I$I4b6=h: )7hh.B^WB,D ,(uXVB1W$v/[_| )N03;w2v\kx_.._7R^vvssaMVXqx[yrra 9Pqi}J$I$I$B~"x?>u["PPg]S׼$ך늮~>Y[qUk/w:4RS[w ̙)K%z !;6IA>M #I^0?Bo0)Yfky/,_;c SXks/"+xN)WXf3s>IdeВ\ *Ӝ'GX#I$I$IB1 OM ˸mq;!P(YJɺ<W־Ukek2ܐZ@!o}S!5fI`B8:t#-``AjH~"ƸZǞ$i1BGvv^н]I DX\Z2 nr^? IDAT{.ܟّ 8} ecÖ>|т =Z`$I$I$I!cJW_i j 6cMF˺2{R_^~>zڳҳ9[Q]2UuРȺ R95I< |WMZ/PI[N4.QL0\A'pοW/:_<8 LB\)}} BuTkΐ!i! $$PQko) &pria5!`[o lYf!wqfsI _FgcOW$I$I$I}1~s ,JRtָi Ml>^Fwy[֌215p(kSo2{f 9@wi1 $(ƸB8|ʌKOs|L ,`S,2v=L2uṍ',!S c*Ky.zYLI$I$I$(hoWkE(!(d&yguZu]kg~̨jn5({vN]cM<)׵5.=2gwo !<4t#-g`A ns xב5tm>h9.SŶ;u*.&ƥNXn~oQgGǺVuAI$I$I>c|.{ JsaJ2PזڍyW9֏'K"}˘|ѷ`CjRZ0C Yj !,4 ,HTS !^ ӫ88x%o v\V; G4} 9 '< QFA:As8$I$I$I !c|/kU{Π>u$T1^uץX5Y ZǠ>ڷ9@Gs7xꂁ>u^)ʪ)m? !|iFƈIj}? !| x UuN ,,--1ᡁ蜲0LU;pNe9ʽdVaPѾePA$I$I$%Bힶ?))NPƜyMZ[unYuuVQ_֚"벮#o,F9ʜaxH*RVϺ K%tPUOS(.BP9/ ,HT ppO :t ,..r\C{:``aКܷ:''(<L,V1 I$I$I*!ib M}꒸?SR T1QW켵)ZS~kg^Yz5}lEU~__Q֥TzH!k]a2{TȜ,Ac$I 1gtL}jW>;/ɝ2_nHdsCf9L1V$I$I$BcuHu&A$Dz]uM>oa;㤩`FUs &Խm>T&Nޓi3c$I 1.>|O.>9w!=W'*ٞ"03P)w7%($I$I$IN!'b~xM:X(!@j5M;j=M!ߺ~k#o<}_dn־R:^OY3NkMajB^m){V  $䱵wN->9 [2 2~xi GFB$I$I$il1~ _֧. ǵ&og=٣/{EW9gм:3j˞W6k3ĩ 7]r]3DZf *6tb`At< m;ae0-)U+ l -}3sp_qY$I$I$IJH!cv_-ʼxR)AumR䚬\?J,-2/6I5QվR&bm^cVs w_k  $0Ov' [ Kf0DF;ia x =IA$I$I$ik !~2^:؅U8ZMYw]Uԗu֗'o<}˘W8rm6R =/vV$IJD1^ pn̰=Nn\.j၅o,һYejs __٦`CkR9 !u U!?kw_*# $rùo S{a\Rj0VX\P?1Sƾ$I$I$I~BOz\hWYF+6U WyꑷOe;+TUH-LіOa}H6i 6ՎC]{ 9!LRO$IJ?1rlq؝+/0]t7$I$I$IҨBO8D!BU3˺Q{VY[E}3StްyTuB [ }~ !UzԅO\eMUzR(wB8i21 IRBbG"egbivpI> ',JWj؎$I$I$I4PAc?G'mD:զTj=J({*kMuY_vAӿUA/uƔO](ګ7P.^>+g`A|3,'xM_)ygJFpB\$I$I$IB ۣ42f6uJB.4Y[wE֕u Ceg~Yz]t Zݻh?O]UU9Bֺ{ y !ʹ1$IXxp®X.{{J/g8.-c7$I$I$IRB?cioQwBU3˞[V]])gYĺkO~ڻ9Yv8s MR5pB [?~`a%TIs:Fntp2Ԍxlm!1eK$I$I$I*[ 1wu>.2 yg=7BE\_ʼn uk]ѵy3J,=;[]a6d(]Ӧ&O|4f.3 Ti ,H;swîEڒ6J$I$I$I} .|=*+M Y6֎Tf[WtmWe*:&M keiԅ&֦P[tmNRLU: $%&D$p9k)vMp 2섅U_AavH9;$I$I$IB M1?|?Gi y_=[wfd]omgXQ{U294 "|w$I$I$I}_1 Gޣm=uam])ȺB egXQ_欼Rx\9ԅrצjH8֖0wx!M1 IR8A'`fa.,/%" 9aaX*aK$I$I$IRBt lQ yJBOS>˚Te][fA}F5_ޣΪjzk:Qq:up¨j;!Ù$IJӅ v^{#[) `}JM?$I$I$I.EWq.d[QYgejFZ;GF闧w3ʘ?M F֓b35S2 ,HcLϜǸ ^'/cJ  SF$I$I$Ij^x oQ{ U[fd]W2鑵ߨ=-ڿa>$FԵf ƿBx,SC%$I麃n`̙3=Wi8wp+5(2̀ɢ$I$I$I$9s1[ /ѣ(?hBp @B٧@Qg]u#o=ڿyYz8s^嬲z7L(ހB3I~xwk6IuΜ9 s.{,LҹRکㄅ Ew#I$I$I$Qa CcNxu=j/|ԩ Ԧ;K}uMZǠ>ڷ̫r-GN^h [9ԐBC 霦h$IJׅ4P.a|)r9=Ua',̬?aa P>W–$I$I$IV !3c/L'>ފ"ϲf9e˳z3ר=GQ֬2PDTj PG֥tB*B UPB8Z$Iv][eRo^ -j LЄ9:~ ,H$I$I$Ik ob\x#pyZ@@ UЖ !pB<_TUjsSB E4րB(!:!Pf`ADCw8qg=~+m+ӥTM0ˉ080_–$I$I$IB,-ؾMۻ֍:YPvQ]ƌ2J!M ceH!Pt}.T]wMB CjO; 5 ,H;Μ9É=Tj`` a,xV ,',H$I$I$I? x|] 7^jkojFuۣ^wFj߃ٷ^m:u!5m7c`A$I$I$ITko\x3pC W^(oսG_tOEd]W<1WY?*|R }T*7L(SP[@mnRxѕIw!S3=^ -,{z+p|C0H $I$I$IB?L\֣U $FsMuuydYoe*cjc("@({sd0jHڇBx,sSm $IJ2>PA``r{'p.X]fʳ2 09pvH$I$I$I!?<5YU6z 3j}eVjMj֦!ܐj`u)՗X(GB $%,ƸB'9ǹ+mœJ%#T',$I$I$I~?Ƹ z[{_8Rf9U*cme'KQ{_֜"I!)3[R & KUX[It gϞwTXؾ|9ΗJҠf8SCX$I$I$I*BX>սh\ /P*ePt?Yg.Cz4'kϢΩbn)_U5{R\O]hϺ)v8GBIU$IAM3;#j!}%\v#z1A$=aa8;)ow$I$I$I !'8|O_xa tBUkR9aԙycPQz WoeʢH-HPD%@B'Y C#y!\! ,H7=$'~XK+pG)~aVXCsCڝ1-I$I$I$I?n1ᅯ&6ԯ[ 鵮ɵy֗cX"=-{YU/8m'/xB=R 5 >A'‘\r0 IR;J7pIZ E8wq`rlCC͆$I$I$I½^|+7=/|\ Y\u]k/G>Y{雥wYsʘ?Hj6J5Q׾Sf& E4kVOR_!\ͥX$nnXXXࡳ}0Zx5ap|Cg3 I$I$I$%.p wbہߣ~q 0TyQf-?2gfڬ߫6PFR5\;?>B3Ws$$IjOtF镵}GS~ }) ԅ"=ڰ5!? !"U$Iq+W/01 WWplC%1eI$I$I$IRBGވ1^ |C /g MFSkۧ )j;},k{ԅt&PX>ŀg ((u$Ijw>I^־)pNXY} ,x$I$I$I4Bǀto'0 LlXGQZ7꼢3/O)?υm9=!Cۥ2Ȣ}V1'Si b@s!RX$= X> L4ꕝVj-rnGG$I$I$IR{Nнcë k+u]EUgyT17\l}rf.k ŀC# a`A1Nn`f4O{U# W|b7Wf8>',,=aA$I$I$IBuoëpYa&B E[ǰ^2xI E/H)(R^R?u~m 6Tjnb@o!b`Avn`cn,p D8%fYZO|I$I$I$Ikވ1'0|50aͦ>UPd^k#o Ywe Ϸb"PAu{`Cŀ!呆I-a`Avx;w(|- nv/ӏ6-aS/$I$I$IB瀏wowbk=֭߶\?(gE2=Yӟ~B p S. !&I6`;V<<f}ɦj`Tn$ {~z@B=I$I$I$I:Btow -5XW!~eeҳh˚Ui:0Κ SrB =*>ut 6$I-c !+,K^áau靌Hdkz0(X=I$I$I$IB\|hbMW*`Ob#o ӻhE+~x.d^wp_Cό4Pc$Ij[> n~h{ppf18*kf9-'I$I$I$IZ_B _W{_w B:kXQ{_"{ԄI-8R;uhgM0BᩑJ[IVy`^v_ /A"<1nfs`D/+I$I$I$Ib{]$ևnvmX۳'1d][a}gޣ̪bn?u\lB R=u^5N & ' xGRO$Ij << WhKc4r,23nX8c//I$I$I$IEa{m6६1cu 2 5J*Y/ NTmRױpBMS &/4X:$Ij|7>~\]`j/,kz'aL+n>alNJI$I$I$IBXvo.^Wo*֏ҧ~z흵Y)BT(AuYjqN8 .i  $X8',@甅6p'Ya)S,7>ђ$I$I$InbStB kC /X߳o(O~ڻhW.W6MV=7C3t kOOx$`II9`|35쾢ٍ_gy6S*rrC3 /I$I$I$Ij)ϻ b;`8h ٣OMPe!K2g2=Q m?yP)6<BX-eCje`A(wiO&Kaw>,p,/Ƈe_$I$I$I4.}{ 8'2|9pY>}gpB](dU"{&VB`z a~O>p?pH8 $Tq>AG? +armZb#d\,rn Gٛ$I$I$I$;w?ҽ}ds1ƫ|"KwNJ)d G@A.Wq)>W* |Oy $I$I$I$;~>^z"\ON) kopx'&<BX*ow%Ijoc|]쟹˞\5{o9o_ϕ>$dN(9>Xayβ4O8X+0c\._I$I$I$IEw{ 8ZoNأUPy/v Dt&ȫR{/OO= <B/ ,H~? tq/q/u8׽=cKfM\E\\yu'.g'e23VYacsd3?;gL3,3eL+H$I$I$IR (@IqOvB S Ӆ&ߛO^8K0 Fe`A1 !|_ { ig>C|!vɞ={ؽ{7wfϮ}웺Kb l]a2.Mڴ'8gps9q9N\&,1SV"^v$I^|+ ?+Р-ntS5Q$I$I$I԰.6\M JWFҥ/'ŠE{9!(@3S[$)I If`{;ԧnma/m'wxHfPam( ',H$I$I$I}G/uo=`ϼ{#LP"K1PG_d}a!2KR ,H4b!)h8oM@:tNmxrėtBuۧk'I$I$I$IJX!'.2佧h[EO+q2TIJI= ,dBob߯?FF>V6:Q֤l! $I{2V+/$I$I$I$mU!7IRg`A- 8<p) ҷfij$I$I$I$I$I[\q 8޽mBF'NxamG [gKd =.Q$I$I$I$I$ $i2 ku ;}~m%+_{<6rݛ$I$I$I$I$*m0P$%{- ہ>o Fz̀$I$I$I$I$IX$II1ݛ$I$I$I$I$Ij7 I$I$I$I$I$IƏ',H$I$IRIB.٫$Yb4I$I$I*fr!ˁ\^$II[>cl$IW!>$IC}}GUm'>=RIz4]q]ZֶvE*H $@H%33l&dZܿҼ1{go QYvPػ~Q8uuDDDDDDDC">\|eATWrl]`k;GO>#'h8 `@DDD6(chM,nZjEDDDDDDD(ѻp)zRe23B[-p H5(K[:`+ L~Ο?Zպ 6 'X: ޾Τ""""""Tq9ff\ԺuU(-2 a0lz -;%n"""":LX """""""""*Jb7{l^ǩ*_>'{k[z^ATIA:N_u{:Mdsycؤ{>fmL6/QwR[S;]_yj5R!%l]1z7 XXZ0ZQW[_O ]QĄ"""""""""w#[cB&I⋸C3s9&lMebI\! ~w&f-}C'!ԅ[P]Yly+p`˻8{g|[0rI'h8s'[ DDD-0a 2p1> 'XH2Je"w|1-pp6[`ҦC0rI$W@.w"""""""w|=frsڹBnn46Ե{LYQ6|y?) hdÃ%"""nOL .|WADDDDDDté,/Ռm;p(O=`&7GEY>*K"+,R@V -ȹޞ^.]Q(ɿ>H8ctH !6l{] T*vNA>2|#8|W (5*EEI..s;pJes*M=d {7}G?kw@;wmås;$Ta×퇹QP_SHC"f 1vXj!doȩ;V6 ;= uC%]؏c;ŝm\UʥHT桲5%uv fPx5٩(/FYq6d2pt鍠ҫK2JDq^*/075U%Z 6ppߐ:UUWʥHe$uPػB14c(οTL{C4ff~٩gQ^X87 Ai(kTQtj[7O0d.zx/ᶾbVu+2P[Sumw} h9DGGLsl&Imj3Ԅ#ˌlK;1 *"""""""([YWͫI;s?~F\Hz>&,Qr IDATsX/XU*%.DmAѵHK8Z-k%طyDq@ΕvsA[1'`kw$v|W8}'U*.DmƙC?!6?oCn9{ũ+./Αs;U|'xOyqw"A\6o⃁nǸYOCc+cfrsOEUa1 {BIBA%Ř0G%RtADDDDDDDD) \ɓ(q3PWSaykJl+& u.dEΪ+!I:WvJBXZ+`ipՕ%PUsB|5UePIVְu8Q[]3,,,J4UޭS e5umWC0a]MSػ6wyI.6} ڝK["C㛰w(/.צ07G%ryCU(|nS]YM_=C:͗s%k?]XJ.1VSUbhk1}b^Dn 'g/pr{zKꦒ!k$L .J~P\NXYn.2sz-*p& w'/zN^ǒ-=T6 Vğފ3oBnnat "qb7P1U8s'D]13c/ҦC#"""(/{n=Xs?%MˌÕK:֨sZ1fe【3t}#u˄;hp( Ɔ1D]Mr3.jT壮u0lNpv󃳇<}LReY>\:C5jkq1j GmFq^*R)\#"|U sk%†1xNxwI`$(܄_: 5rni9%k[G$VVVb,,m:tV@~%ei)<pOL946~A&#t,|jdœ!)vʊ/7f!+kAIrdEUyFqO K;{7tO0IQ!GIOº\lhmaZǩVQ[]k_FJ!,~'fg%(Kc/-=ԭ\:W.ov46ȶ~ 6]aޑ몱ː|aǖfn}//p9f6|D[CXx5xXvw-S7|~J?V>k CX!3$zf}VL """A&cL~>߾#1dRߺ'|ctBLX9cJ/DmKV(]aʲ<ɶ.VfY % #d2 X y;BP'}urW3xJeY>V;[2j\R̡U;;jŋ k^F}\=q QϟGJa1^!XjI+t~/*Ln19 9Ws5=N^c̿X}qį>{FLX """"Օň\'6:9ݕc"VjTW٩p' 7/yA&|߾4ƖzV= Q[z&07 g!0ln6^8ze!/3Gɷ&lzK%`۪PQz9w=5a!+w9a!X@  ɧNJPsY8ZByq6A5YK&= '| I'ە(-@iᵄ$3/z]#jwrF>NNX8kğ٦16n 7aׅk>N}MìGC} GL#AJV(/g&}nzdfk˱sZ[TJg8i عe)Llv^(/ 66נ8/RqJ8`Ƣb; D^=SPU^ _ܧsEJ)Y֡?$c`NjPW+K%%+4IOލo$$7\5[COʧ JVhR]Y_=V7Vtuv,%^Qz+޹9i/Fˌl3YA& w̔ :HC}-~N Vl[zbV75Ue u>*^,0zcv1ZtnJ^͹x؅_[$٧|fpWWwr$۳t~  8~|oA1(;|PN P#>E?@tne&Ki\R)׷7_&+2Z2_bgI Q[SsGVK8LyJ 37ME5&+JوY:u9,_֢`IAVs ~v \T.yK>a4 "jj3YAx`w$ r.M|-+ߛ#\?V>mZll_^o՚ |a?kRCzb>qlydrZ*\͈kvdE㋗U /G}O_uzfeauDZj[ƃU AߐѫPW d&F @ WZWػa{1pw[|8.EnA@qp n0P_(\ڌW3.bO` BDDDDןDž =>;~ᒇj a 7FP5ܼwdE:~=Šsw֕X\>/nnn,J1XɝHO25U%رEbTR[M9~T玐M* k[Vkl= nt\  p6gt?= w>JkGC†/^]* -m&ɑf׺p|חFf*Jp ZIA:kQEyxp(ͣx7=.pӂ :Z¢(+ªWv;$1~oUUrX+o 8{l[A^;gT'WG0ƈ `C0j#2%DjVqb7X;s5}VǷW_vߑ&s + t/MWk>A4 n?D6%qp{:ais[͞NVaφ6<sDF76^8?}~ >s[>ec6Jed? |GI)>f,sѼWQ_G)8 zwEĘݨ,hUrE=*)H/-lb w樭.C]Mɪ.V[uDcCgkQ[]O2Im?=ss + 9gi>= 7-|16HCGNoů<5\nKkj˴̛ ,z|†m)+/#YZtAkyHN}׿f?0ByqS$3OaWwi3$mg ++~I Ws˸koH 788􆥕-/!7|s"A'̨v}EppGذp +*Qۅ׀EX|<~9Sg ۿ֕TJd_3zW מ1 >[{W(PV$j~zn2 .amcBEg`NwsxGd~6NBq~\>.,,YW[m?=^ShOZB$|D1sEUE st~0 ??~zRQz>5YFgͫ/n*/D$SՌjVgGɫIIA:vϊ6ID&,Qr!7c?ܲcD|Js>춴{?AЀy{ck猻Y^Tm gD]Ml ǔ){'O533ؙYq!T6̡1yMnj=O濂|IwuECY:Ğq5d :,4z>일;$#boK4`R^MƱ]_Hƭm-p]ϬO.N4Xdi {Ãlҵ }B:'M[KtQ+קM{KIZ?b"F߁^ոVV*p!j3FA1tI681zv}E`m>v!ܼapֈQlDan"2㑚p15:^ꪡ?4yA |k+\}pjʐwёGr\&yFZ\vح {'O-QVD? #-$,gʥc1DeEYrd|;u:>-!|@JaAi \ CR8?w!jw(+8NlĖ/A= V)G5ϘwT<nB`Jf #$bmx݉ce8[2nm`pEvJ!:Ye%عea+8=X/;[&aoUϟ_v W3R6Huv.|2apxŃՃJ{ɘoi GPZzC&,טs8qYqv} 3\gZ–*/OJ[z;JYQ~-,m0frHdC}-|/y^^;Ĕy/|ښrlagw?vZ̾C>#n|uIw#v<D[$|VWcwqJ\wUD[{\`aiaIɽoP_.co+kǚQQ+7ƌo`Ĕ$f!{ֿޜD;80YA&3ðbgQՈ;;l"g'`φq=⢺D7"&,A~+y(^ Pm{)YA$32cӜF|3|W3. 5xeY./)Y7/yk?]1PC&gM0$LC2cѿw"K;uJXPT8ɸL&ҧV딬?t,f.\%N &{ %oc|6v})"`naOfsFъҫ;,0:^""""ٔFdn arq~* ~-x;^9#hT$lsl}KXp7ḛWN䚲l>A#p ۬|'' >aji GtOؽ5axkI76 G 9FEJalIյ;`ֲ n^6"-$xEpM{hߵ_h8 ]ccJX=da#ڿS]Y_}T1n1f-}v.mfffp YO`䴇;BGЫ; pT#?t,'p(3 DDDDD-3Y᱗]7С`|O_cW$ۏ/}08ƖoZ:&^IJ;Bwk)>-mޓ;p ~ilKx9ppլ\|uG?-1o ~\Vjl?W x7&{6Y"y~oA6ҫqY[`GV{+jJP'p]^<w?ֶZse׿.}t^ ;׽*)LU[[Yag~~mN) 8HH҅;%am_fFM{[NWfh1fV" t\d2 5&cNjrFc}?`y7ƨ8L&"u5_k,l IDATFRI"gβ0k;zudhO |-mמŒ+F݌V֚#ʋQ刞iakn;t~ʊC&m".+`Aszň)IO^эIkU'S[h}P\Օ%H-"JgCAN^H%8O\ pp)zdw?"2 aq7 1vN_! 8s+am1R6b7uCvxd6bd<;-!LKjx}J {.K+[ &J=t 7`R!%cٗت"hQLͰfffzƂǾDx5r݅Z?Jzu*B b\6Wz]S6ޒ(f.~µui3OgA vAw*&rdq8r׿akW,}Gb:[Xac|_ɔy/JƔ\xQrLx-AI@84z$Es%VLa/d4FL_cD.Ռ9>糶u'th%Q1az C`]]Miu;Ű>D 3s5cE.K *:I 1^}NJeujf*$bU {nڻ"pdc?lvh$cT-pSNF]ZnҺ|GVVτu1]7,llx챍PTz`J u=_>I &\UU )J=5KvEa_P촖n1~5xW@ !V=~;t(jWH`艙K R9khf)\}v'®DDDDDujV:V i (Kr?ܼOnnwޜuђL!RsY>F"cKrpDtQw[?Ʈu'aw `/c:BUnٍuj ɘg8v5Mڝ.5Qsܨq QUQ<*6KGoZB0q3%r vL7o,z~:FuM'MhOᓗmC'cP,BFwz>G=8]:ӨHw#^^TCy®cɞwS38L&YzǢZpojȄ070F!/+Bgҥhi7~gBfP,v.jZJ\~挞hD3kMnDKDD7I̼N;||v9ɘ٨ >aVFw \BOWV w%J̋MQZ ՔjZxhmuyGi{3Xs +%=ȹ#7"^xrXMU) _R!yɸWi}yDZ Z2/F;cRK.>эC[R@g.QL[Hiyt\6 vD2 [k ص2<&pvNKv`afֱuR. ix7&A@&3Zm9QSU p 2:a皗T64x7IdMŃ,˗R-=k$F$8| cAS:9XjDE {³Osay8+T ǯXBD\yʐ- 5ORZ"(]6cEI0'Vd4l>†ƥs;#Y8W*tzY =X`И8%@zNډC*b#Z_Vػ]'pr ,q#HOMam#=קro$w Ḷdb"IƬnޡ_1E¾_Bb,_p0,Qڔ\z!|"_Qz׽^g0@ K+Q-"?^ZttkUwRirB]} :N$d ($boyh3FcL&!|B_$ XUV㻿E]-x[wk?Q2ZB]M(m]{UU:75D qkN>)Q^Qwq7&ffr807Icyݨlua67 ں\Jǝ*;>b"b @6`m4gKN>2aiV8 bd |u'npălϗ!3tTJdAV88BE31wŃ7mל2&KJ #|z\yI.HSVBR k@DDDDD]A|ݗɑi=4=ZUR1C2O`Q;]DMT4J*uҮ&zy)JV0QYCj:quܿ#n@DD7['`:A6A"Gu8cɸG z%*4Y#"X u^ӶI۸S l\}d21!iJfeTɳCϯR)vsnu1 K46">brt \o]Y:`1+޽UR>+,b |\-/1a30v;.tf2yw36ΟUkau:oOzef &NHĴ=SC-כZ0^+×%J*hl5ܦ(|x}Fno|ceC#!"]0anЕey&,_ݨG}lyXiPг66*UEP؋J{MtN@{fueQq@ dLXʲ_$FDDDD7. KkxG +d[ny;M$ 9nz W.7`ǺUUQ^ 2OO&=}psyȶ2x ԘD>> t/œ#5Krt: 7+aS۬~g.(`k^ =nB:ln}E]1vo 7{\A2t議ѭJ`@RME/P8K TW(约bm+~hlylFבҩU3AB/[333 V ZkYu]sFe#'/өt(Dtc%cW3.djݨ. ^ uB2wjE dܹZ*j{(`im'ItJ@H bN08mOanC&,d&BHD&,d$F&E)&r}',^i_uj*^zugǤ!3 &#q&D"TWy|Ń}ack'Mvǽj\=DĸŒ$߇С$_͈C^fd|E:F L&0rڃ>yFmC:.l%ZڍNZjѢVjݔLOT{QPT&)|xQ8JLUTV}]od`k*IX̺2эĴ=ށC%ce()0jzeI,̼ L6jFORs=wΕXɘ d2򏐌_͈3:L8rf҅ƆDDDDD&Q% M-87DƱMN.܂.j=ꦇ(H:Kѻ "ݸyŰb{1۬u!j3Rkn+WZix{{6FK(A|N333au'""""" -?{NDF,/d%P6K۪~#+3JDits\2ll4*FKoɘhS`w&z/&dD^b1UҢT])-4Ƅ""! .tnQ&/y3斒,/;^>?*Jr +lƆ:$.o/G=~( nQSUDqXXctTK%"""8Zia&vy &kзQC-po T*U'G?333 Ôy/wO?{_uuq}HHHH $@{ 2DEnVZUJm먶u@D){ ag~&w |׹D~W*eFe<>i@ҩe pwlZ};e[.U9w$&+m|婬\r23n 凉,2z0^F sl*))]\zw]C%H]}ƅ !IGEFvW̛Pn_,KRz>*QQ  hdʤv>ooЪMUaAXڴmG,?6w.NRiy0;g-w@Uj%K+]jw[t8%ϻ+|z7>(U~;$zP}hI!aaawnֵͧs<{Nש"Ғ"ZI-kW]\M]VAݼzJK[e@P^zlOlNcus{9͝QnE_o2Dѵ\.(zkז%~$_TJ躥-@v q{΍\\T[$m߸ \彇< 艵Vv`}.?:bw hbE|bCGl9*+}EIޣsAZv:9woUdhvH *mn.Jn0uSU|fl݉t{2My Taή+Koޯ eoy7A3ra.,Gs1A˱?gBYYǝO(oj=:<:y~I:{Ps;u\`tNI_Y]4k;@eE9WjM (Ij[(H|9I8M]ktGMv*s^]ʶb~ϼs;>f&=KRTڮqONjns6,LG& &r2v:-khx}~eWW|' qNT=L*@\ ɇ26듗 1S?xT7w=vPXX嗔~5ީv}e:νW=y3 O_GՊئ8beeAtצ˅Mtė$i**=1F]]xuꦨC/+k뺹~ d?\?dӫC:^ Z2Jkڦsx]2s}+BgW~S_tg"EO- 'zJKK|Mai'hϪlu@p1.}ުͻ;YwFjZϫdֻzٱ*-uVw4V=lWQRJs nG [oyN'<џtpFGUU/O}DsvW+7iGɍzՇ.;Vk}eϵpk>$IGmgŚ.19]8ݾBp뎯nћO^]!\ f|W_÷y߼GDRAclҤ߿ռ,Omܴ:L+pٶ±p PS1P4Hk/eS? | âqvp&G۹-kD}#w\SR\i>EtsA8QxxkaO桝zs7k'kJKKyp^0R|֋ Ii1vO +%5hdf荿_^UWLWqQc9Zxu๛TۑqO>z>}UM+g蹇ꭧּoW*?7%EگkWBOQL߾‚:^*rg*-ߧi߯׭D*YÇ+uw9y~xs̽:H^IMJ۞OZxr}UuY˦jc\EæծHG<^ĕ:[@K҆SW_רk\eܩ^r2ܷV~IS=Ubfus{=)t)9aa{OMVZRV;[8b箌tU{=n~W.pśWpl·z=fU&b[:.5وQtmu' KI-9_GTILljLW^zk(yM?goPFܥq]F QG2qڽuW}ݾRku p~>M~XkelUzsU?Dο6rFRmՏ3t99_u9 C._k~wYY2Tm N~aW?-kf(ql˚ڲf$)2""k)?Ee 68YrWs7cZZV-T![VV¸{{UNj(2*Zc}Ob]V̛ʷMTYYI '6Zu|orp3JU76,V{簰pGDTX*<"RWem-m~c~\؄J+.*%5Gys^TX]z/j1a*QnJj\m9)|Gvͣ qҾJnNw=)cm^Zt\9l]6,V)M:ԩϥKhu‚ رqK+l߶0vM=yb};m<o|QHVj*+-QZ^^?՚쥤:'/ U[z x~Om;#iV(XOѪiɬww ם9. vWc֖iь׵ljrZw@ \9mjү~T ^{|c˦kԸ'ղו^)=tx'xݮ%6hJs?PîzQ.iԗ4]'ԩO_ql׃Լ@6\[W9Gj Z3s/6pF~q4B9z4ʇqh_??uNr^Ң`uxa~߫[h[\T_Ѵ;i׉GHoW]7Aتu0* 3J kYV-v&`PㄅқQL\flzSЩ]q V!U/&t{&W7Hk1=҉EyE R=݆kWee*XAP/~d1_zeiۿ=V].P_7Uq ) WN>eMfJOHi-{Vמm˵eE+.>ٷ;8v.ܧ+M mc4xԽcMz,c&}oլ5k7@i-{QӮ]%С-Z>c?Fsw+ڳu8x[Z6}u}ZvBRZv\r ZdUs#ͫg:>f/ަ/Ć*++UN>ٶQ([W`s8QZjԬk풯\~iAwNgȁnc֖iyUӴm mt2ܬ.m6;}ںn jY]7Ys4㓿iIB^Bv8[Om[>F=l^ZZuKhݒ)~O {u;Y IKg~Oqf[ѶԴMSeڲfuY_>s<U[%U>c2v8= V41;獮7viՂIڱqA;HRjzgu5Jݮ?$%7sO/'x];g+5hN-d׎ cߦ,gNWi-;/@?Ly^ Joܼs V1 3)n&靼n,ѵy Loڶ OީjRfwiwZ4 m\9-RԦ0vR҂ԠnZ2-vm^܈hu=Z}οYGE:_,8jи5ݾJfM8i"c”֢Zu@m^ZB45m-ze}ѩ]tȫKKK[yK4Hki;]2YyYїaa|==c3'=w>^uB8]5o71bb?֔Ҽ/Uy~U_4MմM?]&S7U-㭖L;ώ]kk'k92*Fy?g/PuR{\qO*"}4tӃio}nܨ %&7;\M8q )Wլmǯ(Q[W7`fMtUu硚=^!|a|y'vDfͿ\pw?us*BDFsvGhm/CUK)M:7x׆4Ȩ(XT*!ntN}/W""?vֶu?(Hr)?簢k+6޺|`9FrcP:&5c_qjcj%i1SƎڹGdS~aՊ&T/kzP#Ρۀkm@V/WJZj}ϖe!*-) txdPn~E(6AvjشK:bQ7t`QN>E+>$6Rz'S/EwԨqOJ2M?*7)/砌T+omT/%?q=IfMy!mZ]@5jU#>}샎x./}hZB\z W{u൚Jn\9];7/VZ6vCa7AYկ]y_U^_5rjbMmyWnupM ]_\?{tL{E|oQ1jkdG@JLN#/ ˿U^!jԬzue@>&1BS?w&z=~M[Wc}_L l#y&(ߑ{}JnV_޿1jcFZq_Qæ]tsnWZ6#ٶ\w}Iv n-6,V%Ņ'cLU׫rRM;fk޷w>QB']^{8Ơ\j^x{}c7%㉮#(;__uzW}u맫}6:5l9Yg. 8ADyqӨ԰i5l%ԩjnϫjV [~֡NUBRc%$5u!R:hӞm+vWZ+޺̫RZ]jc[˞}׹kxG~citG4k7@dw=(""JC|qlZp흯@[Җ5cB޺'ѵԹutZ JN-Mƕ3yLeUۈZj2TmiZST<ޱeZ1ol^WmcGFh;7~ھaσ: պj|O_'Own٤O8=u2޺TWLӶ󔓙ܬ9IcjUlB5iKMW6|MaW?˾Ѻ_+cjf?Bxxb㓕ؠZw݆+I;Ǥw򻿮9ix0_aa4_뜋~ ?yu(cQIq$)N}KmN}/Wc_?(cӡ(zV/\Mus.JHjVW!U~Gctt&GDDcKձϧc IDATlfPaA sDEzY_xd׹tp[9)Iꥶt></QޗZ>Wee%Ol'dW~aG<՟;֓sctx)ԀPoճ~ں~rd1KR)jntf("7"b4 8Z>cz|эȨIlz)-ԥUk"Oc_NP!7wrmZ=SWC}⬰pEU֪ߨRtR58ۘ`mх1^Zp1oTvW[f$up8v,cyS^ZL!xdHWҤxlVr:[vUN~fWN~(.>YɊKHVmߍ3KiI۬{7Pfe GLbT+|7ͺ^Jj[Dw+{tg R8ժfJnF ),, cBeܡ9\EՊSTtb~jk%7jI$/?"IOzǫvoYjN{eH|`"iƱ^'t TXxjEDž4|+8dzc![Ueۂ,EF(<"ҫrOeҩzמ]et*--Qa~kLJ:\iIO풒"(2*FQ1>xvnQU:jS+s(rԗp[v-,WNqA Fjp+*Ѭ//Uޗ(E/%7n𓧴jeڰ9gߡy"}(X)cm6M+gH##>JIMa=yNkVRFp`bg_TҒֆ:1$ڭ՜4هt,` (XAG: @Qt,` (XAG: @Qt,` (XAG: @Qt,` (XAG: @QtN1')BCkP@u`ԻUR 5(X5(Xi3Jҝ.~Ǯp`tI]OD uC: @Qt,` "BcL:Ij )ER}2$m4Z_y4AR{Iɒ$EI:"|ZkWWgURR/I %5()KFk ^-GW}-Tp*WZk^1ΓDRc?H%i9Z pG1%#iUo&kA?Q%.tT/Zk7=~V!QlIKj- Ugץ?KBckC2F7IwHFRJNnyK߬@Qь1) a^64J(cw.s4L;(*YO%7Ƽ!\r9W B҇zVяx4IKgKzKҍ^IscR|*2'{tIt1Vk7@x;11En&4sb~:ZTƘbcy/*: /M%C߿6ƌ ~f@d&i+WT()sjI36(tЋ<$iƘ` ni*}I /iFUZ*i͒&y@AIU|HzsIW,j'IJptI7HJc#ޒ$Ic.II'Xkm<"Cm$;<1E>ts%GՒ%1Ta+1Pwwh+*IJu:+b3T}4”'UU!=cLLD:B]Y־]6STIS1Iz[R EJzZ[C>>9ZDkmGocVyC 7ƼdCn.Iv˹ %}d`RyqCE$ 4L%c)גJ|3Œ>_񤣏RIK1ʋXNT[E+WST51f]JԭX"k|IC$t9Yڱٕ+xCI%t9|/}ypbے{(V81 $թˑ4bJZ{}m{nv+VpXP4\ Zkk.IK*s9|1&~[k7x;>Hˡ1}YkKh%6Z5?˛™cU\t PۊK%kE^q9POkǒVVגtN/t֭8\blvXң6֮sq1EI 5ɵ"\gI%>Kwn]~N -^\bFOQIIJ$}ow9>%3`.aƘ=}oCkfI] ?TcL]cPcL_cL1&*9@up[eU_TkmA~* $cLkk!Xj`.6ƌfbvQ}|!1Xkv)dG ak9(i ~#i6& `P#c%Z-ɯcL{Oj GoqCRmX]?A1WAI*/OIoYk78ΙnEX+|."81%aI:Wy.j86'8(X<ėoO}4cLcIH^R@:*Ђ큮o-4<#.JzXƘ 4Uk@=>DBcR%bUQԠ†w`PS$z ckm1&_Rc!I|`ruAB$ God1%}+IӭAt/}pRƘNE6?XFKJ>c<:ڵG5PKj 5 )? 1Q&J<C>(3Bқ.󢉑>I1KvS0r: ΑT&)B9 *9dXkK/] 5E7EpiLI12Ic\IkMs_tډ&cINPIl)nIc[kUSpQ1ȹӇT^Y-ivN2eƘ\pf-tT' 5a ;RE]b{%.#`ݗSZ;K,I24t!KJy7I3 *t{*NRK<3}W&_ҲW-b4Ɯ'ijFт7$uDcAy.j86:; 5@;6$HaLc:}~+HR]?ۅvd$i$lU-8GU3nPk:kޣE Ϩ|Wz!NLRSe'c2UEQ *@P)v{we}{6Xk =XkWJZ) c0Iɽp^U] ,C%Mc{F{%]+IƘxI +|Fb Goba Ej-j 51fcᆪ%ʳ_&*Hl0,d 5%l` dA!Qu3=}N}ڋ]sf]̆SN5_1}{~J/E5cDXb{cKzUyCt҇5̼k2sj^D,ua:J4j%#"ѦP:K$I$I$I$IӕI( 3so>ɱMy}5+[m88맺/.nrD ӁS²Of^hr8).3ח . _d~88XrؐG+I$I$I$I$M=$I[<x7 7߿{UYn2baFmUc=\Ο=%Ҵ)כ}FD̢(6TN(Uͬ10 ^*#b=ջ6eJ$I$I$I$I‚$i|x=0K#]inScwus4mAc{""b>܉\3Aψ 3G;/mr3i7]IR5F&3)>SˈHRueQE$I$I$I$I,,HFFfn9~{O;"^ӛ56ؽ"bQfH ^3vk\&c^:k&b JY("<ɩK2>҈#r4+,BCüzS(M-"P]jX V/I$I$I$I$V$I-_Xx[D23/di`Fm†cˀ7I2su:"uZc&&6W7pUaoόe{:O{}M&5(wTW͍TƎ-19w;hSjdfk$I$I$I$I I)x;7 efN 76\s2"~JCD,xѸÇd+$N/;8"f'2`y8"> +̼w0OVxGD,T*9ǀ[79|3J]̭VU"Zƿ^Rg!wiSjرaki%I$I$I$I44,,HFыg6_'"x?3+3M}'"3xzD|x32]<53%42pK9Z;4ۥaL(ϭJF&K k$I$I$I$IꔅIHm#l%x}f^w;6\ |c67(74'|AD}{'tnSKʛ͒ "R<ˁ_յm|{D|x&k x/pߠ{ [';GAyQlŲR)ņAQYܥrbVڔ5$I$I$I$I.zO#$"`ꙙycq$Vqw`% 7|33wT`97W<3T{O nO2`g>ӆ@?s,(-I=gx߉)'V(r(37x "N0E8vЦ@cÎ%$I$I$I$I,,#$IE_hhVr8qP9[P$I$I$I$i 5$I2sr.4; #Yh)"SQh76O$I$I$I$I#‚$I$ P"b楆W3j+eDl԰XjM,I$I$I$I4Nd3h+:zffXsI$IXD,yAeb[.5(6XBI$I$I$I44aA$I42s3IռXB]K'ԙw,-ǝ&ENZ0Xfn5$I$I$I$I,,H$If5U"bե'֙wȜPWM=T8Zl\kZI$I$I$I4%YX$I$Mk E9Zy/5 ,dpV9Z)56e:K$I$I$IcaA$I42srs[hhVrX ̨10ܺUEzƿ^+I$I$I$I‚$I$Idr3U.4[ݩYr$"6R]jX +>%I$I$I$IxÄ$I$I](-ED+h^jz50dEdpy.5֖;pH$I$I$I> I$IR23 Q܈8RXPcar[91b ]u$I$I$I$iXX$I$i-'U"b ե'ԙwȜX&E.Z0~dZJ$I$I$I4 XX$I$ivgU"b!wiliy"h)"ҦPR!I$I$I$Iб I$IҐuh)"Q]hˁ10ܦUD:ƿޘG+I$I$I$IRYX$I$ie>h)"fi]j{Qca2U9 T3P}q%I$I$I$I꜅I$Iԑ<\"b&Eip2E,6DFK kuy$I$I$I$Ad3h+:zffXsI$IVplYd`b4/4s,3,$I$I$I$iZ)$I$i x†rjnDDua؂#e8jRDl. ǔ2sgi%I$I$I$IӎI$I4e` puռXLRCu2KqvդER&3՚V$I$I$I4eXX$I$IC#3w;kEڗN֙w,nW"bK _o.wᐤ ̆Ç2 @L3 *"x<3 1p83s H$I$I$ $I$iersi_j8XDY]j-q5p>)w$I$I$I0 I$Iԣ<-ELBծ ')nW{3)>vOΈHBcʢT\E4#_e$I$I$I‚$I$I$)oDA`9K cWjGf{=XpGyM 0yKüOFq#=@FF]l>|23wNND="QR/oִp2Ys9?tBD\ <2_C{xOWDK^'&fgrLp٫fw(k3)pA2<x%XΗ$I$I$iÂ$I$Iԁ P"bwiz90d.pF94/4?!3v2ssDؕÈpI 1Djc'p y#⑙yU- \xWD)~ ;ĝ+"~UD,E#D<bI$I$I4YX$I$I(3*GKWQ]j83d6pZ9 T2`}qK5 Akʊe\6"y}߂ " \JQf>ez1./ *"uD""(vJxHK,.dK&I$I$IgaA$I$I<R"b %#62"6Ѻ𛑙ztƜ|6"~\G&}p|Ya-Iz`3EIN#%MXMQZRcN,>e_nQxCQNh> <~{kC~ nEH "bmfzB/+l>\(%ߛͧwiY%I$I$IR,,H$I$ISXy3rռ h]h?כzhEYdpʉ[i]jbJ |&".>'9d#qw>k2m(vxF-I;*W6+7Ohr1h>Bɘ}iD xp&//e3v{}x9fExVĆKw;tdMy%I$I$IR,,H$I$ICuS9'Һ0#SդIRCS'WDą3q?^<03o593ˁ <-"ޗ{Ν1_$3'F7BfnSdk&gw')v\o1W!U?eM x:~$I$I$i* I$I$lx{K9)jr~Ax'OZU7OsV+"Nœ|1N2 qk&[ {03on8[X@~bG !{9x3n.1xs3s$I$I$Iy~oɱ|ǵ񅅻Es=5I$I$I4i,,H$I$Ie.h)"Ѻ0L`Qy' qFg) pFk\gPŔ~|MX&bGcn7F$I$IIcaA$I$Ij_xW -"^1@4 _t{s5Zۛ*Q̌EeYh2 $I$I$I‚$I$I$ 6_qq\9;S/MD> 0~0[2s:J$I$I $I$I4 qnх) U}ZS@7V˶ht83{]&"> <#b#Xacfw^I$I$I I$I$IA>[KfF~jnoU?not{3`m׎_t.Z7T:/+N)G;G"b6d $I$I$ 1 $I$I48OthA[ozv^iMNmu.5Ut[g]^;~FOujro_SD_51XYs͈JgC{$I$I$I$I$I;pA(%pG _wgDĢgÚKhv^.׻ 3 3ܙӕܮɩ~~M''q^pR9n;9bz+I$I$I $I$I4O_OQN4378O8Jkܷɱ#{Xw53PC"bp&˺=/={Sf}\k-.GR1"bX$I$I9 $I$I4"bWk(vP*pIf4?< or|X <ɩ^{ءa>gcz>:RkNE 3Q)"S*͙5e$I$IfaA$I$I&?P"b&&V/g fzŎ>zԋk(vZ'59ޞ5\vDf c ޕ$I$I I$I$I4D('\\'Eħ3{^eyCue \`ucSX[} f': " ɩ5l̼("~<=jXX{rnrDlr[SfI$I$I $I$I4I"SrW3sO#w2v#w69Kw>]&G?-[o"bmf< pf^էL`Dl̯>Gr?dᾧ+e4fZȱ Ǐ%uAKqv#b'vX$I$I‚$I$I$M9ˮhAZGqpǫEQ(p//.ye]a;4kzÈxzf~8xt7A.̛[d?mfRݍ#b6dM MzfƼ-yo(GKgņUI@ԓzP۶{i_nXlI$I$IdaA$I$I&ǿl3W-(|53֞jt|Bz`3 w^iL^NYeq-E&`ppRÌk}$3|Scq)m7 C-IYzq=z?/Q(n2Z[5md~-X+i_lXELgRDܙl#$$I$I4,,H$I$IR"\Jq#% wwl9 q{x<܁EfxJ2{_n9:}ԓ̼("~0f!gXf'y_M@ IDATyXSJ1\'冕zR9ť:{8"6Yac/I$I$I-YX$I$IEYʗ;qtdf(̽ ?bd} փ̼:"p.O)y8B/̷+O<x+xBf~v#b) c-Vʹ/76dZK$I$I,,H$I$IRM"b&7k( ÃM52sSD<x9Ŏ 'tpBxmf~/" x );"s; yMzG)no{e: l~nnD@gņUt^Ŏ9v#yaOM%I$I$M]DDGE휙7G$I$IZ;ro_4[HgP<=) G3ss ,m8|Cfk?:3?0T)À)>YA`-}z)_D"^MN}qא7R b/t|9 gf'MDeyOΦn \ |ӟ_MOgņUVcv *$I$I,,#$I$I$8؝eN M,5 %"s2s砳iaŵ'˝4D"bZVˀI:R];$I$I$MY I$I$IT20nA觩VP;"b& +e>prTtXn6J$I$I*I$I$I$(3Q)"bGvņcsI=rf 3P-%I$Ii‚$I$I$I423M帺8ņ#g&G?v2"YaCӌ$I$I4,,H$I$I$ISXfn׶ذ X\SQ3c2Nm'GlrSSfI$I$iRXX$I$I$iDEsPh̼b$"3w͍ytVlX,1_[R۷ܰ$I$IR,,H$I$I4"Nowo5I&id>W/63I=rRnnbD6YakffM%I$I߰ I$I$I҈? 9I %iJ̃rTN +9[ 2H-%I$I4,,H$I$I4zXq{'+QܾWU͍Ҿذy-SΑDg再eE$I$I,,H$I$I4n\W'! l)5Gb:+6Փz̠\W継 2s_]%I$I45XX$I$I$i`I F:+6zu.(8 8tVnXk,I$IYX$I$I$i4دdXñDRds9Ьذ XFqczk71"vܰ$I$I $I$I$MqAs`23gC`d~rTY :+7f֓z,,GO`Kfԟy$I$I‚$I$I$Ijƛ7%<-GA#Cb99sΡHgMyĒ$I$IC‚$I$I$I$ 3v#b) cc~=G,rs$"6 2`-%I$I $I$I$I4dV`+vs#:+6N)Ayi79"Ya}f)$I$I@YX$I$I$IHZ̝Nvs#b>VKk&)$iO\Kذ XXO3 8t{Æ3w *SD/p&^ ?3Ln*I$  ꈅI$I$I$I[D̂}IVyD@ P5˂Uw.u͊UQ1taUQ *y0 394էNuU~ysswu~~f'v)L#m# *R3GD<ZC |$I&j1 I$I$I$ID ݂ c_L#m5 tH*JK88%3W0q?KE`mfDDĎ"6h5J:3o,.l- $I}3Z ,H$I$I܍bsgMV%I("=0ض2G٭u 8\%"> U+WfpppO`uB}72_?}G,l9f"?~Xr`.-ÿ_79:#C~uRw8?zr[W~[_^L|k{Ye]ULoOb=ؗcTsq׀N51a[%7\`PLc`AX$I$I%"V' >|_|J$)]v;V`5 f榆TMd= 4̼tUi6SQ{}{3>=Ӂ'RM(>߯꣖Ӏ ;3_6Slo̼W "$ҋ WdYuO滛rf}[s.pezXk_D˸aue.EL q9 HT2Z ,H$I$I =w8})D(~pdK^1"ay߉3A-Z|p@S|(BDU^=9":~.'G9yI GÀVLxI5vx}us)N6\G@=4X$I$I$ie~#3/j$I)P!壣 D`.qݻ7Q?ܰvX"brs28Op/pgo}tSPV.R3|c^6wTT>VQvX٭ +EQ{[Q|@ދ"P|1"ה3[j8-"[:QK01xFfXcE!9d3qs"\X`q9GuZϳ):h $WQtءi@$HgI$I$IS V{$I42s puܘ SٖbMTڌ̭Cxz::x;_הF?>#EyQdD'pPS"J`i̼:8893UXvG L| Iq\f~fDUa[wks E׋g1c$IZbU +\$I$I$E?%3=H$I$"S/ذ }Ce-wۮӽ̼:*"Bqap"̼yH4w.;Ww_vyŠ}2Hĺ紜#)Ȋ3: xZC]f^[OcxR5@w=[n7]#ov8*SkKSS<v[\]ZnWXcGӿ}zƭ="^ΕXP-$I$I$i/Q܍ $IRMu *s%zk2sSCuvs(> WS?^3WD|8b3=3 S2u BmL^d n_lXiƍ- ^gXD @,|b'F}*Z;$37Ex05 wiKD 3Ϗ+pE)" 2`:P1Ì$ $I$I$:I$I"3Rlzuc˻[/{a -w-l ]=Vagc#+i砊od.EY}bl>}XuOXs$82s]D< 8w|>":q3̼U=avBy32#I3I$I$IF$I$ \y5nG"v6v+MOTnP[f{zM+q`11pLjsf#b Eg؞]1We}7c?/f>~^Gě7LsꕙyQz.qvz}.Wo0Hb`A$I$I$U1 I$i2ݎl؅7yDρQܕNonw{="b)0LIozݔߪEXMU%vǵLuЖ3=𠊱a>: Iq ,H$I$I4znt9__U]-^ǕoG?UYF2sKD< CWem`Eԫ"kgc~D옙+j($iF1 I$I$I9xBcB$I$i2s]׻y=";=ذKyT. '̛I.OE~#=~ZxfS] $q$I$I$IQyI$IR2s#bv ; wx8 x{fv킡jX\ڋیkwBD l~x,3̓3&z Xfbo|&t+2Sg^6ND,dz~X1v@Dq+V1=̼xN"bAz.=1(U?j $I$I$I$I$Xt=]۟ ̃2yvfnjQ#b.;S.Ɠm8Xή )cbNZ Wq}pɳ+G@ۏ*wz$i$I$I$I$I$it r&N^왙{f 3\pmͷ*ƶǚϡt.T1wus|//11C2ʖWUQ9"S?CT;bl>.Df`A$I$I$I$IFS.&8“3CF+S'W!"6wTvW络~]Pq4ɜ;ly+pRNS\xoP1Z`f <e*S#bn,8bj7-]+/U*";HR ,H$I$I$I$I%!MQba|Df53[M5dSwNe |XT1̬0ޯ+z^{z9:uxHE1+۝9\bktU-+EĮG; $I$I$I$I4,hWv}3̼ԿSe2QD> EeXJ\@ 깸.3wVϓ:]8"$XL\x#E8qY 388|uWI/IS.24DăskGfr9$I$I$I$I&!"S~ ||Rf;pppO`g`rz#EGgt&"Pl~$eKqUSv{,!3ϯy{S%3ғ<+~axe9Q14XCfǵS<B\l7Moŕ=߹9)=@1̼buHT0Z ,H$I$I$I$I3[D<8aWgQ̫^$IfyM I$I$I$I$I+Ɋ -pT%I$I$I$I$I$b1/`)8"]`y$I ,H$I$I4ʍ+;dڦk$I$ q/wu #Ia`A$I$I{w.Ǿ|&37Y$I$i2M I4$I$I$IR#^ ;0||4"`]$I$I$i0 I$I$I҈'n3EA$I$I$/$I$I$I=GtHD,J$I$I$IҬe`A$I$Is.B$I$I$Ie`A$I$IS ^$I$I$I ,H$I$I4B""A$I$I$ $I$I$I$I$I$i ,H$I$I4Z I$I$II$I$IFI$I$I4% ,H$I$I4Z ,H$I$I)a`A$I$I$I$I$I$ I$I$I$I$I$I4p$I$I$I=Y㘭CB$I$I$j.@$I$I$M̼5".릢I$I$i"b p@`fc`A$I$I}:.S]$2 $I$I$̼88 &$p ̼$I$IxzLY$5]$I$I$IzpD|X̍ %I$I11X ,/+Zdf^Tf;9eUH ,H$I$I42Jʦ$I$|1XAsj,xaf~iK$iH ,H$I$I$I$In'"~`EC,i9ňx}fiב$IX$I$I$I$IY,"Q2@خb; 0 I4CX$I$I$I$I "K`M;$;e_.D$IX$I$I$I$I)-tX ii>I$I$I$I$I$!")v3𽦋$IR=$I$I$I$I$;r`Av;NM!iK :II$I$I$I$INDVˀMԫ6mI3…mVOe!4 ,H$I$I$I$I"buDۭMū̫JLrՁ-S\$4 $I$I$I$ILDlCo])Vp E`5}}ff#J-~<W3/GFI$I$I$I$I;[׃vA@*`ufLFYf>"  3/o2I=$I$I$I$I,r`Ao s`\fnjZI̼:$iX$I$I$I$If,~ǃaMԫ fǃ&34R$IFI$I$I$I$im `q#jn~`33V$I$I$I$I$I4"bzzؾb5̼R%I0 I$I$I$I$ "XB/h^-uT 2F$I $I$I$I$IY1XF+&U߶ksMfniZI$i3 I$I$I$IY!"R?t0b W}lZI$iX$I$I$iDEA==2V%I$"bzzؾb57[׃ՙR%I$e`A$I$I/j> "|0376P$If` Us W}Kzn2@[V$IPX$I$I$iDľ G6EI$iFy2w[fSQ$I;[׃A޺ ͔*I$iX$I$I$i,q$IXDA&U޺[V$Izd`A$I$IR' I2wys_q2ЏﭘJ$j$I$r `aj | D TX٧M;;+ISo$I$i$E>C9pv^f^Vu_WL%3zb.0HIl{ ,}3VM֝6Rj),my|:36Y,b$I4$!I$IFFD,"Ћ[" =yNsWߝ "pNJoo$ite斈8x\$IyzxXBjDAff6Ri/ࢊS@aI$i X$I$I^yW/vs'O̧w96{xS+/$I=> [g)GY#"+)VU]7Sve? $I2 I$If !]Nmw{x>y}ݵ$IK#1Җd/(Ki%"XDo]4Qzzz*3onT])6/k& c>$IҐX$I$IVD<n~\T>;E.w^fVxEwV YG2c)6V cf^le$ ^yo)c_SRl_M̼jշ\Q1u>0møyKDlkZD=#*($I4 "I$IDD,ؠ.u't8s2se <rjpgڇ̵q=ejN,{t`A3֐$̼$I3FD,+%8,AHjDfn]010ɪ >$IҐX$I$IO(^~6pꐯ0va'du5?/^̥xWvYb%E:vX(x:k77t$I$MkCcoHX)U3fn`N-c3i%"{Rt$l̪`XQq%տ_e* Qg`A$I$ Ë̽1">qሸE aۊ雁eFٍq*E=$3׵9+/̬[$I$ [ٙˁm)Vpu=X]vw4:\Vyvf~ xK(G+<_CDc{ [/珛Z[҈_oY,6s_B$iX$I$IsҵCtU&3/(I26{h/8~=pdA$I."p!Nj+]5úzܗ"'Z{e N;x^R[Xh56Ժ;P0enC xUD|xEٵ9 ,|MfOa 73Ϟr$it$I$I؍""bD<8bj+Adڇƻ.P$I 8R W}LʷeFU_ʻ˿{]_Fcl$_u>ȏED,X@:&Oex{_RCXY"3O8$i X$I$I֩˜;Rk?iP-&6'df}Wc_"btZZؗޜ_p]$If؎wlX MVA_XD8,MTm^gYM!}[]F+gF"P(s? #[3>pϷ&7m#!IjI$I4hyܿFć Qi+ی>FDVq77 *I4+E̼:$ `F @o]V+fx9oNI|o5LҤ>;Nqc--o2s`]{:<Vtu3hVX |vx~"\1GjO4f&\_$Ite`A$I$ ZU/m[ 8a@mғ; 8*3sUIY%" $fFCD̡ผvf ͍T;b ܋(Ʈ.i"b_m ?l9nmŹnnFBDl {`&vѹ5".>|lPֈx &~pDqWMam6P|o88737ոDU`aiDDffſz8*3Dā W'_+ν3z#◙5Ky#0b9 c2xˀZvA Vo3^$IQ"b ޱe"b#p&_f!pjD&3ˉ1vS7 / rBD|!3/R+̯t:lGTwG2 I$ I$I4H;woĸRmmf}9 +ی\1v +E$4qcix+hZ9]zz*k mU"b_@nۘ $3OnTޡ2Vɱiw<3JX;k0ؽDıqHH " xT%AḐ+%TXfMUj770 k+zxW)blƼfIW9p^ 8bNh`ƿhPU`2ոv¡k}#fࣙy] E-LY{_qjLDHqVVGKOL=+"N?t8qc̟Z8$I4e ,H$IA9q 2FI ϛwm3z wD9_K&"wm C,G\DlKۋ*Lp=t<Ve ͔!wsto`<'[*6My=(:qRiNN "WVzBNw#rmz.:s3T!/f`x ;zm^\*^?nlp^UQw-^$I ,H$IAyH؅;}xz˱Dd楓v &0/p~D<53e$I;?~=C)J3ND,ˁm)V6fJ7^\ \E9=_,@]buOQZ<)3wkQc~3#陹z2s}DlL>(:\ir pT^C\7!ye2X-SYc*>9HffDJqt,ܽb)ֲ%"~ $ͥv1̈_*8}9+ ;"\333IG/NE+َաFc1!Fik I0Gl`Az(4Iʎo__pZ,1Dm`~!mJQ-!-7D`ׂ{7 +EDe;}_\qIx~KhXvsEXjl")pcԽ`lTF*̬c6*8Tn o (Tn#uƧ3 {),/&طoWϪ433pinBIZ*:bBR׃)լ"mPoXW~4 除 LWovwLmXa6&Ң vx%wZS-uQs^Am`amV0x"$ C ,lLq`aq;,iV Vl}53333b,Y7`졈 qRS^ 1g/O4=""bPK:("S6"-v}D,lUc݀8.7HJA+XG):?"m6Z\"ʿ+ q :t=gw9P#,lEzS1pfDAnKhDIo>H Rʝ={ $M"-2( L;:S*\abK3Gڶշ3"{:MwH#bbŻ7m^o,T<,POJ:,u:PIu:Ct܂W$RŠ̨3 )@.\Q`Ƙo4S#?lP/2aAҊ`u`@nXHsH]uL31Ds-H v1sD' sfϣy`^x,Iu?ic^ot]7N頦2SQPbޖu&Fn9:5CԚY <#whfץ31D W^ǒs"Vff68`fffffff* ,+srD/黤}v_D\Tb1X%|vB /򚙙HZج^(4wí#7} ,HBA+>G)`.0 :l~o~'?hg7xJңܡF ;Y JRDDsֈ ,WG^?6PvC h}5):.n'Z'v2aD<"i1kw78^灊 : i%୤v7p9s]m!-tf]gK36qME ~v3K,T$pg{@D,oCזkĪ&4zΥbznOdN<碓 |޵ji%XJ$eu{33kǦ 33s`:=ŋ4D_IU M820)7"m;1,i([ 1O>gE^7] ikuovѥ9m@$FP`s#jf$iM`o٤"-, \\t{$pn|>p>pi!ZP;nM aMO#qr7=msމRX( }ZD<(i7ǁMJ^ڊ7H:,".qH ,< Lɍq%"J)wNhL}offff69`fffffffإ`lND!i5Rg :uiI?E^5ʂß^ZNIZ-"Bxo@@3G% \pgWIzWDff#]AF5 zOEa6Vp9AUl׃9AǪf6Ix3;ws xMIz}fO_' gEQhBS#qr D#m4ЦN>7=I? } lGk1OΒ$"irG%=Ei-\HˬůH?Ӫ/iJDCmj#lTs`ڒHs%nEN#:2V\<ؗ$]ի^[Ϫ3>G_%m YnڈuAh@d^{}}v[=896XOR.pPld3$C \箏H F7N>FwmH +7 +TDDHzpiAt}q`! ۙ/V+@cD,4?c{ۅqIk#ʛBudK 1r fQI?;ii-"K:Nro%W1UCs{3333Q́3333333kזnqvuÁS!"H:"dw0I/'UuAh@XڝZǫGXXr$@!Ү{ (`07"Lf+Yqiq裭,͂ _vw"S}}$ ja5Bȃ}@dnDzZHNjjA/ ߎZ)&"cC0ͼVާN^:oBN?C POD<&Ϧ)XDzi$"Jܡ %; I_v%;4o'FCىTطjxgg>F5̬] F]"ZIg*8'pwt# iyd-!iJDwwiA>7oqˈ^k ]c iHԭNRg%Ƥ Dv.!k՗""$5~r`lطαXٸLҺRpi}񨤯M>|wIړb:5|Jү;^X]ڤNߍ#"Ⲇ\r/j+?P|3 4 ila  g]Fu.߁é DDݱl@$lN=;",MqW*$8b4؈(Zxz [I"=Iے}wf>[P˾!}W93ws-ff fffffff֎zܕknKrD49"nrg3m"p@v?"ڒGY,+tRXboǕI9-qEy~^ff6E=#7"=~~Dff=,Y;^x]Hzk"bZ8p95iqݬ/ pRD,i Nv>66J L'$EոBUD%wN=\RpaoR',;:FYeoFZP==Wg{RG6lIǐOn\6^,O/9= DĕMmj_~8dƖߗ8_ҦCo􅈸޹6$lpYG~'lGR|+?I-lX(PIӚ$=-[ߖu %Fcy+EY0w?{)eaź"t.33{"Iג:?&hf6(,Y; 7h/)锈haD3RpafZ|L f62TuA(@X uЪ ~P=4 w5sܐN4 ؓe`ye,3XVQoVm dϦvu^/537wj]W\*i2u" IgM~&i >MKZqba*B T,G t|y{SQ'm1hX,+hqu5=Gj`!o,z-LM^!lv2c?4s :XFS~/t 3/YK$M.8t]/$JzdӤ]u_{o^Ik)^X=i+̬7{<|P Rf`@uH$mDz ;3^cyAa6YH߯WoF\hQN&n`A҉+ 5p{% i2=fjn 0͊Jڂڟ3s#IkK|u3 uDyKŒN;? ."-P}ί7gޠ M; @ %"II+9oݟM#9<͓{Ϥu""En֍AS%Mp] ,Td"!i%`cR7 mi'f%liL7333333o'wL~`[$}.",;QRҎIx66nSǁDzd;əYe =NAؠsVq3igؓ%Mex`DM., .OwHyYJB]I v;9E5$|E-ugiQmW>~.|jMZOn|KIDEj /c4"v"oů% ~lID?ꜳy>uA'bNc/,Tk7$ D52ϛF pP=LEugIoyH!I?333333̬UI#4ܡqڜw8YtAh@p[JZ,|0.6e4!iku_؅Nw˫HUI 7VJX <@ 8_CZh~wۇaf'd~/Ύv&û |8wM &?/"T[.HjtV.T끫I!j_H_X0vi nXظ`E8.ZsЁ~W`m?Xx͚ }-C4҉5[0P]Y=ӒD'盙uʁ3333333kUQ`a_ a%mfV%Y]!>(sE-eO4؍^XIqEO;~/GZpj೒NhSZ6t'P1=?-Z/.!phDݩ<"OvWe Uϗ?p+ßS=< %y4Z`J="ΐtXA~ Ha*O5 LkpIvv"x[bz/#.`皙T,Y+T0 :fcEU2 1JXfgq;p;%I+V/l؃K~R`"iEw;jy[mPR?m2Ct?10?"uRDb5YX0333333V0^7U]˄]FJ`fDĝW/aYӯ~u8."n4xp2N'K1"Pہ#ޒ!CΤOK ](6˃.ǧ?SXئ͹><_4lGD l۸ZJ'<Z'Ecs.Tt #]}8yz\Q KXwk1i!uHSx뛙YM3333333kq,BYl.0xK(|XYEo6fYx%ݝxVi㙤M ਈRs#b p_21;qE@γD/sﶋK!t0_@[#b7s;ϵ3OU瞂XPm>k9oG5e?.^;m,VFDgJ/ܼm,W333333ǁ3333333+-".1Sr] -Y@ Hq7p*p,Y)NtqD2m|9>(P-"JE$}oM}kI ̫3~{IZg7eg0|tIs>".Rh/#bQϯ3S4<܃yw#iQAZ/ $ /!X"iRD(i& +68[ҮfY$Ѝ'Z+ -0!wcN/e["bz%Fē J'a5Cdit:8P[Y8`fffffff6 4 Eb-C4|P= TN3Q%==~=TI l!|`?ܹ ,s{Cv-8t|DHz p1K]ҝͮK%}|4^߭FۚtO~EĥM7x+Ia{*}E.A`JD\llx 3IZ8(TLa0hмA.fff6{\xAꆍN8p&OeWIZP0<۷/ȍP*QToHAw;ꁈ#op"*nj;~B9LdC  qgo*8<I.#c{P͠ӯVu{i׼۵wB;fffffxUc{Y8`fffffff6TuA(@XwAJfwA033niX҃x,w܂DC,/iňx}|;rc+v?ù;y`XFS;G7%m;4D#.$}eKxGD_$Lnp0)nWjuoZ1UĈXye HIJcczSYGVhp}X03333333I+3azy`fffD¤:ZT0, 񡂱WED|Իt%EKIOW͍&Gz,*iRD N}?[n|s\IED5"BI+8'pC#uxNv= *X'%OZﯾ"l13333331́3333333.<|P `=E,|P4333_hi7:e]-i%y>fX`f?z&\$[rux p I)j% d7$ x5^oYxIh4UW;mfffffffc fffffff6 4 U Ec`fffֹ{$->Ԋc՞KkWvPóO+y~Q`aI+FmpF`rn+;:i^L69ͮC L[#{%J~/0iQJ̕7SW{JxB`+w  "kffffffff# fffffff6 4 Eia̬S:XrhpڪE:e$ ,Dݒ;4DҞqgKڅؕ9n`ALY7I.6 -Iwe ͱ8YҥYe7EN&A sns"׍٘xU]f.01oPEYsD}ƹCw8z?v{sVI[9i`a:🖋Fg7K$$M$-߶Ќ&~ڰb=v5J:bdg/m "n7>4Qm1p;܈;Ђxs,YIHZS&&0hH]H!Ez*j ;KZX/3(Pt$Mos686E=_^Nk|\٤ӭ I݁QXt O/Gĭ-CR6xk^ JoHZ=BYNfکZuEl]ax0"T6:Iڛ}>G?Z?`ޗ=9 x1V..ܥvnT࡙K~ԂL#ux_) ,H|g:{H倹a6hX:Ɔ-Nqff̬Pauc0X4( Tr뱟X033.sGf`! CoE-?HIE_ph4qVvݚ7spBci9ɡafffff uw,#Vy]oMT.6 ƺ`+"IܡIzKDƴ&ui;I-OIx}.Q.,i'G _+UtH*㟈WҋI#Ew4ܤrK;Y?\Lq`a>bf69`ffffff6 4 E .u, !<5::5ƚ ,|M1Dv'ug{gRXea[ईCun?nN̝j?2 IDAT%I x\`rSۀ+ˀFMYE Ƶ9`?wX03#F.eqAGAؠ`?"h^+;7e]Z\K#fHzp.D#%jT !i)1X Zp4г?=I-H'O'Y!=7!" 03qȁ333333P&&0hOR"| fffw;3rJ$pz(dIR -*`:Wn7"$-8N`a 0)p -"7e7333333uS8cug,Y)َ]٠ee0XAZ,T/|0l̻m4Mzʁ331("F>Y8 K;^/V3WGjOVޏ~9KdG#"ڨlLr`~0`VD|q%Da a ~P 8&ieFBi,{ض_U8PRہ331 ".^T_k\xS SۨN,_ⴿ_:TlOz3x# 733333333GX03333B6$_Y} 2k)  4Je]$uAb +q`c`bn܋{ưwe +Tղ8\ҧ7;[^  \W5v033333333b,0>Bz9 q`2K$-N B uCx("N""$#-1m -w qzKpj4333333339`fffffH (?Żׂlܨ,i N!7 ]ld~s`lI:8x!f WP3333333339I{vC|q%".SI6UuA(@XvtAh>]ltW0揹-i5`"0XU  fffff㐤kH-qyF B Vs "iO,_|՘Y_EÃFIGp/{R La aD q,t@H!ǭIS|<*"fN+ ]fffffffffffff68`ffff6H  |Xu*\/|sZJZ[>s9IGVkpץ볱yu; 0IkXi~ѽ. PШA9d@uGE}M&+Kqgyp?L^ \-$xaR8kB#,A6JX袪.kpAZ ̥ya4F]w[Ϣ$JD\٥k,Z$aDeF̺ˁ33331D6G0]g̪P&&`* .}!i"iA1k$m ?{wnY 2 cED@DD h'A>?QvEV[EhGYD S!02Vwɩw:}%<}gjGI&yxWջ$3t2h[29pi7&y[FAs2x*x]SI^TUӦ)NLr<~OUq42T-2|Tw+. `XDU䧓<#[[XX7a)K3E`8wVU5=gwkWZt%UuUm5)hG%)/ޙUId3dTUK9Ua7Hƪ>s֋gI~'&3>uU+I~3SUudR~i`$?n<1U_0M1a.v``ŪČ Zq),)h e+s:.ɏ[U=p~\U=e$LukW'60qEUQx9kYWU]gRU& g{ℂqxcǟ=*S$ya5~1fϟ1H)?+fcf½h5Xu$ɩ3pIS,E`V*?+=W\$l4 |SVj;5_g3~6)5o|% ;963/gX$J$&9,w$$iSԲ$Gܯϝq`O|…I=_z{FM UuDozˬu JoZ|pFϷ rh u$ځIdRxF` U Irvk|5S7wTݱ^ ,I3ɭo4w/&p&Ԃ/{uF k-^61o` f$} I~7߻{|UfwezRUߓ[g%yЬa9Lz95{un7a}sֱrB2^#Q`hͅK D`vUux%yz[j y[:SjH>0;O佯9? $L$orɭƏLU/I39&l9gMM5~UM:U~[:IW'I;aSI/p ?uXt5~~ȤZf O%`/c<%%\l@8'){iѻkOrukǮ`BUf@%99%5KW/8acIoJc=Y}&91m֭??79\חgԤ|sKo߇,㽽-{:uPF9,f19?My ateUIdF}&G|`C_%$[oWlr֮NUɁC% d6Xk_lߨ33KzݚX%9nwXAFM?ՕI^.0}Mwd5,ԁc%WNO$//y 2(~RƴgQ[݃:0ɉI~=wINN$^mUKqH_;Ŧ Uu$!ٸOJw/LId3rI.}MVթI~)o{aI~!ӧҿ~MCY<gw'/)̞iOXucQMy>OMX$fE{!,ف{,o.vjG$9v{c ,tEI^3~noUժ}IUo65_TO&9jX?T'ycpI䤪z@wnvaΞ!c2}#$$/]0n~nU붟XU_未 {~?kM=aa?1g- 5?uG X)3($H`aӧ&vw|ZuZ'[iUݲ>  U}IU=6)I/$^ia;%}F%yQUF{XT!I?O$l=\Ug%$/}Ug&ɻanIVXxsDM  u}U7.vݿߓ0 |zmAUuUëUuǪgѝ>4[$$gV|F:4KgXCIgFhK3%yx*,ڐ}eFwc{.X/ϻ$l-Zd|}` 2J;ZbC&l/ }qwݷOH 7>ҡ5h~kU]$g'HyUIN͵?c3'Uu5WnINogv_vu%b;$Z}"0p{Yŝ7~>t 2}"XݻƁUہ24EXqc#3jgFpVU3"!m$OK2ybWW,/>$\Mr>u!}yw?'Ò\9T[Uge_ ,>v:p{9o3j:zZ6:)XՂc,r`$_8Eϯ7Uu$) C,>Ӻ9}~ OUBKiH_(d0$o$Kr\:ẇ$ʘXD$Iϻ$+3Ե,[XT=USUznU^.=(-|}~YO{41] C9 zܪb%ͮ/N%yMUM5(q )`X}\w_ݯt IϨ8lI-z-X UuLnu>kw?= I4_OYfO/MyV"? l=:YK0fqU=>$_h뒼$$~>2ɉ/;Դ{:nќ?͹CTgVL/I'['yނ5]KUݻ 5˯xu)s uNr$H$Lr^|V ,hH_^ݏ߉|BFQ0]IgUm}UM>#nv8aqs76WwU˓|>_%#{fFwƾ4`H9Sg{#$77}:MU}Fp͞&yTkUzUQI%k9=3') ,~wu75=4|r/wLnr}PU5UzU?NKi+X l!MyԤijx l=h0t갪za$$-Zle`aUUsֳ<y؂jNYUovaw_QpUuYu#[Ns}w'O>8Oe45AI7M*Y&ɓ2n2D`vFk~jw>q1~ns`oZ?6aݗ& &,^6v|U๳Xy(L)/('2ki# Գ;wI$כ&[[UwYYs~aN$gT ^UuOCv)/(DTqM{j״׬siveI.x˼ Y?|Gߕ<,/'e K6LcхUuP~jwob]U$[u:??O=k:a>YIj$y};MxyI^]I>IiiC{~E hן]2$߻@=1I2OIr$?8wdF'$:+@OnkO6dBtMm:~KOO5k9.XXFǙ;Dw9a IDATtsČ) G={o0j0tWiW.Tlȃv[w [^-<$C?>QInݏv(0v C)8@3te1筻1iRU\TSLU_2b2Ϛܪy I<Ӈ"$Iw_ݿI62KIn=̬/u ~<ɓ<=d4z}~EwZR6 $I~IwhoJr$mnH[\"btwg)/O򞁭;Mq{YF#~9B2Inn$i=6U3{w=W&`q 4/}"gvۓցI^\U/AUu8$/tI0;kx\>5m2V3^~EwgǓ jIt^ݿOA.tw'ƍMrr&CW&yVk˚fpZ[YUWmp`jP2)p$gmr=NO.P k46ZJ9yԄY -p3כeԐI$f4!B:GSS4^B2Nq$ OXXk0xEWT͓|GFoMrhF7!9 gM $8~nQxU*6T6v΂ف3j& ,۲i}eU]d\ MRUכ߳g)ssBO^tTS#6Ps~w_^U?$&'cUա4oX֧kc euy ]gNXEf,tjFX۲/0g-P$9x`}ɞM'TiJ $ƄB}NU-_&yȂveFQ(b>&\=_u-;ѼM `-]4aMz/UՍ{Cz3guA[s5yuԄg-U3hKwO$`_ ,|fw_PUMI~9ɱssyFvySX2G6Ojֿv CSw_䲁W-{2{zsS[J򒪺Ѥkk|$l$_u3^ֹINfr`eښv_I.N$_\t׺ ~qwNVaء#`/X`;j>亏MX߂$̨֮|3CF^g,ք$9~ZU$d'5|>y4HrR7rgܫ7kNr9Myޛ>QL`h E$%tڧ)tVmf-%# i=Q(ϒ.;,ɳvs/K}qF ֛uBg=$S=%K$wלsI06}u\OuKuЪ `R1ZTUwSLU>ɷLxiLz?%yiUWNYuMobSֳLsOXH$:%C񯇾>f,$8$U?=)!MB&y^VM`$gS4oH. 믜I$$_U,PU%$w~uw,kSܪ:1ɟ$y Fݟ]$x xe7c MkݙߝQzTOZKU3ɣ>($ߟUu x@Fؾ<ezo׭C=JrJ'zI߷.~Dw?;d LX`:?ɱ֦sodnϯOv9n8)kIߜIm;%9>IIre&C=4)qL/rPw9ɛǓ$5ɭ3I>}3X`kB2j|S֑Io茪$IrӁww˦el$IrZo^~$5I~8xvݗ%sIF7di,.&p|q`$GTѻ7UzEFA[ \Q'PG2^<0f';_9)$$TU$_I$?ᩆ%yhwt:&XLI]Iސ^'E'`;X($:1kh2~l$wRrq |aMW$yFU=/Ƀ<$m$&P&yew1G _MU$'HuU+-`X`Z8$ުkNr9kyM'6]In8EMXIRU7MrNw_vV]%K ,$Iw'fX$$/Nr~at}Fa`Oa1ajiݺIYUKr$ftS|bXw_خXح/h?/<U{*v(vW]N!N"EI.Xu;՝־UP lWXWC ,T4`:h"$ǍwOr$xד~ K>ɇxwWv1`AOw$y.oX`Y`ᯓ{U< l7.k~}rU`UDX:1+$I%y`Uj+عN~zlY, 1l!?tXN`X:`X`U,%[V l+o`ﶬ VޑuU_'&O$|KVZ$ݝѤwUl? ,K',t ,K',t ,K',t ,UU`':h2UÓ<2QU_w{VXcFU2Oa$9.ɳC ,<%Ɂ& ,woY,{ٲ*XO`X:`XN`X:7ػj˪@`m-c˪@`m埓Xl7lq-;Fw9ɓ2I.L$׬6{5{&yOewTU0 UG`X:`XN`X:`XN`X:`XN`mF]u;A.{%uI^ݗ}IVyga`IU@$ >L+V1+>VWY]w1bX]EL((+*Q}\qE *Id@daޑ[ުy^s=瞮>󓤙$I$I$I$I$IxxTC#⥙n83gְ' I$I$I$I$I$ +<" $h$I$I$I$I$I4<9IҀX$I$I$I$I$It2ù&l$ $I$I$I$I$I$i ,H$I$I$I$I$I3 I$I$I$I$I$I$I$I$I$I$I$I8 $I$I$I$I$I$i ,H$I$I$I$I$I3 I$I$I$I$I6N,$I$I$I$I$I$I4ù&l$ $I$I$I$I$IVnӾDNDf: $I$I$I$I$I622 QͫfJfÞ$I$I$I$I$I4H>pp3pUfn$i1 I$I$I$I$Ii'3g{4$I$I$I$I$I$Ic`A$I$I$I$I$I$ I$I$I$I$I$I4p$I$I$i0{$I$I$I$I$I$ ^XGEqND$I$I$I~k$I$I$ 6\7I$I$I$I`I$I$Im*ږN,$I$I$I3 I$I$Iõ $I$I$I4`$I$I$i*,X$I$I$IҔg`A$I$I$I$I$IÞ$I$I$p.b`Pg$I$I$I$ @d) "~åd <I$I$iڊd=Þ$I$I$I+,H$I$I$.3$I$I$I4X$I$I$i({Sf0ԉI$I$I$``A$I$IҌKO.fO6ID;oi-``A$I$IAD ,$i1 I$I$iFW\`H=qV'83$I$I4),pn $$I$I +7ޛh=DQ]!7O?usfEB@zoGqP&3?2 -^޶:h$I$If~$iP ,H$I$I"VsˀWDķRf|#iU-J_;戸"P0a {PKoswQ*P<~k(*Z\?ozi굱tg1yNcÃH$I$I$a`A$I$IҴWSg{'3ψXпCe-T,T-덌 +| 1[`:o/^CULѮ:IBcI$I$Ihְ' I$I$IH`{ +lgpIU{fE#b#"^Rq̼8p v>\StdΪ5BUTG$I$I$I=0 I$I$i&8m}XDVVZVkux##`>qxc*"Gc#WD,iө$I$I$IaO@$IRw§OKj#3W e’$IHDl<dMBҋ IDAT&cfMq6PU` ."~<ԡk/"bSe=A\v#Z"`W`Nk+3kilaU}D\e;D]#'z]@Y]iX(*;y+$I$I$i&1 I$MbqS4+fۗG{o,"3GI4YTUWX T5۴Od`bϭ7U"QD<|ũ U}př\[.DDwu EÀ0Gz.0GR$[̟לTm"bK`K}(>&I$Ifjsw d`A$INҽG{7XNr$ivh7iwtOgq9w˩èXVx> ki(3<2ԈXJ3Nۀc{_"Hqku*3mCD|س{Wՙ$7]*,XMC$I$i|Eu`!$ $Ind7BN6vw$I~vhA3sYDj95Ϥ:c@uuہu'O8ul&y^D|2hwM罏QVp- ҏvd $I$I4kc"bO%ſ]J&I$iribX\U ;ɈXIaXI48 + |""MQY]W[E|[V',A$I$I$i\X$I&̼9"ؖbqc10whT.wѬ\;K4%,UѶx:"Z+xJ);)*JmqѪ/"g] ƫ*x9Z3^eL*`~éXmszp6p>Ed-cKcOq{f~<&~2Y$I$I$  $IwY"b>ZC y6yq#n b$gl?;+Ӵ?"0MSq$I$I$I$I4M PoḌ^aزmefբ Ϥ:J]{3ՕZEv1+g=+".3?=o lTP<]Ce8Fncdq(^c[D )UUGnģtlƆxwI$I$I&% $I(wSysiv?k8+Z"b-ex1yIҌs-ppKۓ0"PXpxЁ;d歭G|<3{VUg. Ĉx.̼d_ڸ23{ bEkeC`x՟%0Zf4"rꠈ8830Uaaݻ<`qpԜwD<x4{C(#s 5oˀs2sm%I$I$Ije`A$IR_,G- ;[l=k8)Z"b aU$iJ:xuK"c lW>J`NmWjsN[)>˵.q֔Dc+N \xlO w"ON-~1U/4--^4 ,,1wUf1FSC4O=j/'fe 'I$I$I ,H$Ir?G-B CjjPDzVb򒤡9 XK,"f} %"sVpD"PpZfM#b>c_PR^<c GFk{ Bu ,ސ/u8xzͱxUD7AI$I$I4b`A$IҔT.*M , ឪN(41;GS2N5; OH$I$I``A$IҌS{{y#vhn`kCD,YaaIw`E۳oE̞Bqp\Kh6؟ReՃWW\wrf֭zMv5r1TyiD q~qAm? UD\6c>p  TUAjpRpos#Eē2~)^#DJ5y#I$I$I$I$P.6ixRf^^ xP]w^Gfu=޲uqWj -h;?XC?s.T=&{oVXhQek+xp@Mw'o\)3cCW7"查/ :v$I$I$$I$Mr0DD_a@C0{ǺMaYEۘkj-IS1!]pAD|xJQ}xl΢~MIu`B7V973ۅq#cw?|&̼886"حKׇg=j7yڦq[i;+;V%.:U!,Fěc%gdg] cYy ƿ]):1d,I$I$i3 I$IS@f&Be ({V,SUP<]j͈XI2s I̼%"~˖A^MXx{&B)V-@?]3#P` G~U^ܯMS#⑙ywT>۸*I<ƽ׳ya8- L`E?1mN]f~q2dE9Y:5EU)~=s{`]WEgmu+I$I$ISI$IʰnX@{T)䲐Pw/n󗤶2x6p +d#joŦh|`%`T,+8Ҷ |j^f^ \o>JB'vr<^MwoXc6}y tPUMbYo뽆6" >x.F3lf*Rz(CKvlp)7Nok0V̥ܶ{̢p^g$I$IH$I:̕ o۷iiءjTM u;F*ڝ{1yI_f_M+3Sq͍ [\x"rjG \=\rz1ix%嘛%-^L{\:x#S}13h2`f^3S#buqQ.$10HGpm7e}{^]xS][p&(ޛZxZePXDx3pnn<`{Ԉx~fV0VlSCx ء||?J$IRNGf. $I$R. SyR.kvj0uyʣXK^{UH$ amuʈ_<ؖbX893;_Kf^ZW?W;o4"N~ \ \O@{Pl]Nء̼fUUYGȓ(vm\aN , s[Eۏ̼ iV#{pc0Qa<R~TGĉ:?OjE5-()!8,3Xcm؅"^0%I$IӃ:; 4a ,H$I4 LQa-/EĞ5T{pG @9K$ITWJs&l$ $I$My%"=X0k8;G-a4UESRb=)dEey 3s&j17//'fuu'W:BU`ax~&ԇ~ )1ݮ71hU=D}o,GU>ҳ؛Ewe//'~ p,T8NuXr*ۗK$I$Ib ,H$I4e:%"f_ء4fۗG-bb iݨ+3ocP oq]Q8}~^KjZ^o?X̼]r+88PIUa.i~QK /P#~aTWE~mocf hZØ<*vs.un)yq23pL˩Xi9l^m32M1 C{$I$I$I$I3Xf}]{ ;T]S˪El4 ;,7x18S#b +N/B&37O8+NQ\ŭXZ!}:w-=wmÐ yWD|x]˩G2>xTXV`a Gڴǘ?Q&S>QD@~=u "`0J\H =zG xYfi ]E}=X$I$IҔa`A$I$5R.Fk*|這&"No9B69"5xZ%I$I$M3$I$I҄+oZL^KൟðwV3y+ۄU hz<<[)vͿK%TG`>e+N]5Gؤj̮O3suD|xi˩-GGħS(.Ϩ8uC_gl`ဈ>]*Y$I$If`A$I$M)傧eq}ea!10Uإfߌ4̝'rPuxEIi;O=( p =}=Pg: _G?3&<.%M{?6.c\(~~V,s:ÿT{EŃsS1x,mEi}Kf Y8 Kv%3W;7I$I$ISI$I4caqCfa%0SզB{:vwdm.s+l(e̫G|{@4 IDAT;RTD'yG|1}Dllϸ4"^|?~'Q,ݾ/"b;ē('t#0PH]އyOmu=MV S}x.-FCcCD/[y{>m5P'ɵ I5{a@B$I>l=9IE$I$IR2s%bQj-baE`vUt 5;Ef7ϥ5` |"31Yj̻)t!l3>lyޙyvDӥB퍫9;n9(+M-U-uj ǭ9;"| ا#?w>Yd׊Ό;)>9hywVUUtmI3sC$I$s9wDNDf: $I$I 3W-wntXL;kx.wѬraUz3{Q W 7P,bA)vu̐~3aX=oT{ޙq8 `QQ,T{r0] sˀGπe'3W3v ۴[Cf^^أns)>/ORcyPE|j%=58 `,I$I\y8?I ,H$I$Mrb^xW1q#n bDqIyHRf^M^PLئi%<'"v>@ີRBS.<}*Ǹ36kD4TT&Uϝ HM3c]U:#3o0"r3mk3q2"Ku8!q.]~ WVj7:&~3fڝ#o4t$I$iٙ~󐤙 $I$ITZ'siv?k8pXK^{UH$M̼xUDbcg`  ??ָb+:#~D /li{eDޙ03rqdfVsglXyHE܈23n8fk%Mz( YD#pb 8xc[p5eX}U?z{ $Iİ$ +,H$I[>̰ù%Co a;YK\"b;3 ߿̮#b[D]3*#;\rHS᎕kTxS=3^yu1M"5pӫ3sEU^r:ږ_tC-df@$I$I $I$IT2QKD̡{ݱ`pv(Zʝ̛VbzcYi < BfwD8%;G?i3/"I31"b{#K#V("(Z@D}9ݭ@_2҈8![Q|Mg龱mQ\O֩ tUUMFۦmXUK$I$I$MB$I$I4md:eMir l_D"-+Mr~y)mQZ="N sO"P]ua\exp˩g_j0{(VUffD=pEhXGKۮ[~V-h3 I$I$/ ,H$I$IYajbwmivXnA2ֈ8xV.o̼#"ةXcW/ 5 YZڞ_:HD8\h`\£9 I$I$# $I$IRśwG-1"Ф"`V_`bgBn6v% Lf|\D <ؙ"lcm]g" ?'93e>wN3k0"^LX3a * +z[ۡۥU9F{XD쐙t3 I$I$# $I$I7-&%"#Y<֕m*\3ĝե!3CfpK2sMP,CR>> NQ,ߧ!q ͪ&"U>?vQfRTn=;#A[2&(/.n%mhroI$I$IӓI$I$i0 ,L\ ~_oDBüL_1<_cDMÚAL^R<="r'J`f抖!yn?"(A| KgE.}_ 'f%Ƌ]CST>$I$I$99h (]2q$I$IҤii~Ff~w`Ḛù%Co aUØ4DNV, (>oܐ {f]{k3!$I$I}+u\:HL4{$I$IEV!mcD p k٦MP_E.Z"bC cCyiv mI3Na) !q8r[4j */T}}%I$I/p8p`VD|=3o$if1 I$I4DĖ_6 ط<_ޚ%m;L,\VDl ;D_`6}y(87 +鴈X\cNoغ_eD|e_;FϺ},D`JZaI}/[=QBʎEYU,0o;I5VwxiD\ÃŒ~A Qx3pt Fuks1Q /b'ppcD,I$6nDB`!XKjtE>fŶy%-Yg7333333333Ջ ffffffffIخeL`ຊ LJ] #o"X&CYXJ\@37WiyI݋%]I ("pzY?=W4Va6:ۦuOa1y3333333333X03333333N.cc=xqZ7M:59 vD,o;vSIP| oT |XiS]N4/['<(du`F#"b}JBI76>M-M(!1y3333333333,Y'2]%V3<0xpQ%=xO"q^#bFI+v?#Xm}j=3 x3'3rI:Z4^ > uOFGGi>QlHLô>LF`bEBݭBbfffffffŔ/l$'bfs`l5 q̀MI?,%ϥX9'eS0+B1Z]IZ|ݶW<RWvoVhq) :fD H KZ8VH7ఈ6TJȽ-Y""b pg"iM :Mad`bE#4:>O:7333333..9vHNlu*Jң7k6Wۈjv;WKsBy xcyIӁ/88TyT -l1m,H8iqW8kVne㬴+ƛcQUlHDPC-xec$`bERcfffffff֓ṘX03333333[H| 8Nٙ}x{2ϗM&J:xmMFrEW32?+9g)p)p"'o^O*u._֬s^n(0`InϹY*s-Ai1+ OC};ƙx HvPFVKG}j-"t4ooV?,B$bT*49tCTOfqa!c#_fw;"*>깒 L(EVC x

a=&V VIj4ؤLd6M$}?$ǰCٸR~=3ՙ ffffffffC2ǐ ʯR&i*)p%ËS2 #ffyL$"J%Ho79V{.`I+΍JؖE?tR:-[nQ ,Hz>O_7D _ќbZc/aXu+J2:333333333+*(Ḽ7𜈸xXҝ r+qQ}ˁ82<0ҫ/cJ[’^ <5ss:cEčd[Qr]]& ʈX6È\Jږeǚn&7CJ/y{_qq7&Dhì`^R{4 ;aJ,7uЬüXڏ;`fffffffffc ffffffffg/.{mݰBX߇xXw{{Cw4O lڶ`A^TDSCWn2fD\!鋤BV$&XZۨp"pPU3ϕ݁;xA<23~_v0;/[__4 ;B.3-k[>ȿ53333333[5wyD1V%0\62ne.]t` "&\L`x/e_>].y7(O]xN L-PB=yv+B$#c/i$2Q ?+im&eacuO.cE&3333333330V s{%2wwwZf==eȚ}#X8x~f?le 8A#.I{ienu3Fѕ]I]r[c#⎶< 8x-0>_c:z>R2N^,i7-5xMD\Yf6NEB`!)OM}m'JZDs333333333΁3333333U6}82D#j{Cb*N[\7?9=C`'D>\ř}gi쿲r`vx  ?"-:'\ \*i IDAT[&m7Fm}$h1r~.inq[IP:l % iCb[]P#A )8Wi/ ܒ;AҁH?iNf6!Euϕ4ffZ6>M-`W~a) 4e,rox(_v4-FOq.C#rI rZm+QP3^Ez ZW+/+jLwXphDĵvh?IG=ɽVwok78Eq2#bqy H=N$ 9tFDd =uG 7TpjQjw[-&<0ӷ1ظj"Pw牙V 7Om۷I 3zҪ9cB1o쫽"n;I/#I;EzwK:8bA%w"ߝ#gyh 5:WT1 mn:`D"- t6(k~JRo<yyK Ln78G] `ff.m>|~ps}^*ۀF5kf@D,,Z$IyaVc0}INMIzARءfffffffff8`fffffffjxy۾mI_:]I3+WV _yI_^v03333333X03333333[uؓ|틊 I7IUs#()о}53FcF"I!PI/:QrlSHA378#n74C }U\1\=剙}HKs<#by1mԲ ֝Ck3K;Iت$0AwA*lj.!v@*>f38 xG:'Y-Nڟ޲vmK881"{'l+wH@ -40Q6>Ldu-1 Y8`fffffffKz9p:+ۺP_OJ9W]Y|-Rm;x ,l2I/9X=VDğ%= M<+YV0a`X ^{O.m!R-[.urGiT{mm3 뮰騥8]ScV |86"fp(p{698 }c=`;R"$fhfBn>뗌1EI0UXQ<>TL^KC/a\ƾ :%ay}nS1ifffffffp`lHzp m(p@YahN>A ):JҥquA$ |"4""$xWursfV&HZV >I-+|FU}/r,+vRq.4I!g}tS"wCn7 "ԾF/#n$x?kL'es) +j)ঈD涓IL MWl|+-u-3hvH_YjЬü̬Y.SIID\623[8`fffffff)VVv>gY`AqR I?>١N?TKO WqvӁ%=j&c]E  ikcZetwf֛"0n{4 ;B.§=V"2b G%] o<-lrh10%wduZ,-XF*2mea{IA)]2k_\xP҅ ͡b`A4kqk1?6Ct7=ȡM 6Zgtx>~ +V񏶹0E~OZ!gu50rgEZa*1#$1<`+:>w8g Rb*p>I:i-0<"Չ@E{iv!IfzwJBʎu<23333^BkvWtV_,Bp[yIS++)LgeUDRI/lvx*iHTX+ܟbګ_i?bKXp;fZv1!Q}O"ycw߈&֭݇}_ =Ot{}:-<,qJc=:y@hw+wrtB(V;{0F~Iz/r!pDD\@,C mw^\[|XC6~VtiZ8xZ۾I ?]|^@c>7_K""2 ;OƃX,YIa.ͻݰE40t3333AyYcÁ3]DTO^ 5#⟒^ |bb+s"]ΧGcR1MM̡u9N+ zd.=ʭpgQLXL Zuhk>ý]v"Pw̳.DEQ;2~֘C~ $[qPп)X. zz es{"bYy'z[FBsssׅ%טO9 i/xʋLg{;% K]XF RkIxkU{YeEHAC-gMk6Q6xӊmӺ'JZLCYNffaff,ٸ1"XXZq;<k#6IX/LJ89.,;EK]DWe}ff_#遒2hQ~_51𺽎Iت|6:&w~g֜G/=i_D\,`C/tXA)s: ]숨Zܝ>&-9b%n`Z~(/[^l ?_Mvh#ଢ }g"K7 v? S2prҶo)$ImR,\wלGd-# ,l\ܲ! &{gT#UbρKz p*0f/| ~R5i7)RGIH$rmHA9@ނH1${} 9ƌ fW(Z$MyaZoc q"i ExVt!13C/#ў٪́33333333[mEb+J*݉qaʲHWgfV jw|A(`C\]O)N>>KzZ=X!qaűVR*rOfTIqbdx`aјHsU{& Iَ@ZM1:~kkΡs3=dX({:'IwO1L?">"tIo9?P) iǼxs۾kH!ecI5).ZKy1I/*B=p(,͖Ik?=gЊ;,pV5)3pl6voc2 (Haˁ$WVE,j(fmASMFDNTKm.3EH3I?Rq[xpi1O>mve4&uXh2y@Pk eޙnaSUyMlqC"V8t }N0e Wo^9ԅIm_|n(3DD  I0H>6]Iڴ5;Hm[qf鳸 9IHIGjy/bJ-PBX6>L"7{4; Dp $5F>03333[%8`ffffffff6~ؓk8_D5uNIziu\amI(Z1 e"kI#ڈ_qA9 p۾ HD`!"t!û !X(Vrufudb5ǐO$aR"Jzm>I6!i/T`&:1\&v{(oW{S1lvIO!}>9u->}v=X fo=sY-E#.]N]IZU6_lHZFzG.6뿈X&cX(abVҗEC6A3333U fffffffffTQ[ි&",`{. iLDIҳ_lcQ3"!Ћ%O e\;"֜GO"In;IߍFr>Bc"P8|`!-܍R 昆D`ᾆsɹ1[ex`rq.JE}Y r], u=s]ť+Kz {.C6nD\K*: ĶL>mN$:&Gy:HJZt,tky)[M޺]qrDRIo40a~H v3iIFDN>fffff8`ffffffff %4 6Ŷ 8_"b&_4DXR;$-eN3!X, ;45~ԶoSIOt(;eiDڽޏ~wظd?zUtc/!e-{^Q-%7#D.v5h"hvIXj.G ]\ _)Q?٘8kf6&8`ffffffff * -BI!ᅰUY'kۭ_lA~UI p@ۡ $͉;{ku _;s q[RRWdh;~iCtX(}C=٪2R]زu DBII뭪lN'6 ၅!:PѵY%mXuYIO]rg{"x+)$9B#UB>,4"Hz9p u =fc.<"i.齥vED\]s6>swJNzWވNkR"Iv赃 :<礎9eP&o(75v31r%=13[9`fffffffff}7^`RWmmRP\n Oe[\n>F*bzL? V'cg;o"3pms?;,@@|ߙ9҈}C WX\VԜsQ(s[c_6ΥS ~큮IkB#+(Vd?JyŬ$\1=򯥥ϴCtuD`9IEqeqC>K[ [eh[M 6JyVA(~svDbxfE~*"bӁC*v/'EDff_g,,N*DқǴ>;",C#yݣ.o[8"L*f_>&oH7I 6% mK ?< ^ܟ\dOCQ>+s߁%M/VL xB漣^XzRPQV>Bݕs6%q?Kܻ#T t/4PSm]#" >`ݶCU <:U ms\K߲rHdGO+ {k]q'DUIIDshu' `h5NWEUO~woQ?ŜgEЇj;/[_|=tvƫ `'vkܚ1 C |Zqתv(33s)p1+ጆ]/͆xN۾A`offfffffffx88X|s`+` Ra:-۽#L/-(i!]B eNJOz:nݯڭ|"SW433!EǦHz/?Fxlr`Vyn)1!",iw8R~YK*n+Fă.vh9ӎeM` s{Mkz%C7~wXunnm'e+_ØeMpދwbxOs_^ iFFSwۀEĝ&Br`! nv2Vϕm6߆rlvND\d.i"p!Xx\࢈8x볢ppx^8"n#=/We#*#7e@c"b!Ik,06]l=Q"uv[0"bөedI58o43"6ff6JX0333333333%q=Q}erE(^NE&;:<|ˆ(+o7Ƕ3no_+]~fc画? na\`Jg̾+"=̅V-;w9ha%U -I iu鶈\\w 㿔|sXtx)Pb۬_m G88׫cQZ "@hL|EBÔ>LFǴb۴<~FhXh7_ﯱX0333333333eI"޽A!""ZZg_j۷9m z^MۈX V`Cn:MwX8׭( ̭f<:,,`50ft ,HOz4̈E"JB}Ŗ@ՑtGykQ?&o;P8[%|-"5<ƀ8wv mmCҪI+MZF 6V˾Av\IxTۡq/zJ6Ogx𮒶y0) T8θ#5|>%03.<8!7fmWw&[$lvhÌB!g1<w/sD+ VW&cy>db%;Iivևl\lHZB^]H*7?9g5,ƠnW>!!*CN daYNNpV> ,LZw0IoZYaXv̠ǃfqt'qedeN/D]-s  8,~pT۾ ?0n|RNJREj ${r1CIﳭM|ݞH/$=v>NA7ʁ"b pg"iM : ݇L ~u"Hat-ˀƞL&Ig__Zfffffc ffffffffffxTxُ՟#C*,^TU EM @si{R׍VI:9".:PQmFļ ÌTpbN͋2axsrU,4dh4l_cV眆c ɽ?ۘ-K:C-3 >ۀˋLv"d৒v?ָ @zѤB7!ǡ _~}v=A)gj)B e>LF$=QR}O0apV#ug%3333U ffffffffffq5t%|N.9~iDO""$yۡ5IzfDm@v_8 c˘v:f/͠CQve:&[]_](oYBvch:|x\ SuHe\TpOuHZ'iX^aCr%v~_qa_"д]߬8fXS'F |vˢSpX٘U)Z$Aoag}k> ~e<:uk8vHaH𵈨:43333X03333333331/"Iؘ;8 m'rchsH'GDX+%p☱y,*!be3n񺹿U-|DhrIVȗ6ppi C lRMI?Nex k'(M~A!gS|}Ѥ0 rIӪ^D' 3ˈVT;n&;WWIO+zޱ #%}8*"F:.T "n V2ZEfUz슕}7=~wXX zO_,!+f,*/P;3װlNV)f"\G5z#k*'Gšk? ] ˁS[-!=o&}~L <ָ} 3gJ:xKD\p,ߟ繤 TgX-{,(̬Mj)KfҬL{?Ye]+6/?}DL!@$lKQYOX03333333333kHƊnw"W8SLDCsȯHpDa/%*z'C7`B谰X;#N7$`C }ia==̧6BMP#P#s蒈)K$L ]#r)i"⦲A$rAsᚡ \"iND]_'6$MQ#Gs2-"ȼ{^M6&8։5jIW_N%;՗:b"}oq7ppAD{sX؂an-b.ttRpwSR̍GJ}u[[&=+41M&z=㒈z54Rϡ9ˀk-;[?N /n40֤ϻ{jwEGS| -91)~";~fsAͥeN/^7hs`,#"xʊ>:I{V ߱NmP2 *Votwc j ڶ'ug4,*}w݋ZMv/H 𶈸\:i,u71QT|+&ߓ̠~aVV5จ赋U i6.।@'!OcQ+i"{9^X9[fI:~ ,sh#Hzi!(wRwkNBzwğ NH ӿ/iZ]db{".ߐϹk^Z1kl.i9RXJpݦ`c iOk5N x>Q sߣMolQN ; /I&Yx>iሣH} M%Al*V/V|-3+?881"ixY8:,`Asl!if"b%Gڇ 8`̡ }q~Y}^ܐ4 x5pFD<\ύI+[څT$sB~ESVh9p29J}{Iښ $Ҫ775]VmX@U+m7 ;LmlBZ`IGZEQ*:- [\,> _VtJ?^Ôv􊊝$<GH:'"ί3IC]&b1~ <ε"?Ez&BeE5AEXx鏲a\Y /Osߙ78IHZf4b[Bz<>Rʽ׭W㲹18ezznG?jzIL 8XHz_{"iʓS3$֘@>$^`_`.7O)qy/53 I{ht<66IWV>jц+:I̙t.=.52)搵J3".H)Kd͚ %iNBD,JŭuKzbDT^̡{WYP(?8uc1΅گfJڷs`G:Ӆx>uTto% A '=SX>5Rwn_bHA="v23Wu!q~ݾOyD 8TqRe6&8`ffffffffffHgx1SEbܜ6KxB9^ rh73zu]sGڙ ,*if}I`}XAIsH lYe^S2l[z1?aX 6.fH PQQ9'5sBQPD$) *pE@$Hg}8ݧ33yUk0sڋ~uD.5U^VvD؍ӻr=/Q{oCĖw,)֣tX|s3pp$k)IzlĽxkCd߃ώe>/V8=̼ư)J׋V{.p&ݒy{]O)0>TJGEp})ݲG \M)٦#ʿIB #!/V885}3+s@)~L?Hf1"FyZ2-mDĊbB=2s,b_Qo)"b `GE+7q.GS#sg?SY{O.{R N]\Ӽ&N)9>ވl.~N)x/J^p-ϼ{3`s{)t[ࠟoM,?\+)y77aDD$3!IY I$I$I:iVKP~w/ۘl>TyKD| E֙y1 k^76]M=~vXc_/|wqe DDl|WY@@B?6F#?篍<5"~BIY,(z_m9MϦv̾ M݁Hn=d#tՐHY!s <6"G ;IX%iq_Yֈ%i$(1uY j}ݦZ6Lg;3˪ ="쓙O8%BD<[͋k DoC2:8pHaYN RuJě:Q)!̬}o8&׻*Q^;yScHJ!=1"#(@xJrTOWfLRtf)~U;)0x4JaTogoҿڹ2}⹕y"͔ⒽwU p*cWR t/8ҡ]qoJC۞<"v^xs}~Sp pLD|3a[ްyI)yOe3(E._k F%̼߱g7߈aۯM|k:$Nu s9c)EH-ɂI$I$I4/d+G8,J G zCf^?..a/am_,3fζI2W ]Imduk(D,DWy^IDR谠-uIVu49jLqzm ["Ѡk*V*3O(I&^c/$(5%nG xw,<`[E G)ʙ=neeGW6 cn4mtxn٫eg<3">.[Fm滥[$I$I$i6x eP*wB|LߛzNСrϊ 5󪠣Be!c UFDol#bc`>JYuuGJrrf+P<xeZ1Mj; "bX'"6sSelWp.LJQJ<SD͈؈Yvݾl{fO2̼y\Gf)E[U2#^kј瓤y'3ő3<>3f32s|if>#3)Kg䟁R:/̃,VhU[74÷^5Qd'+v)>s`D xJef$v?^ J4߉`zB9ڜ3"T=YĞ/r` $I$I$Iͪj綶$N]#7^T['F_ff-16&=%YcWJG-f6O{ vAkDMI֛jg*cU]ક̾E9m+G[+z"bE*.ls|Kc{FĪGQ ֮w]s+.Z'.y >7Y!LOڅ&.T!64ɡ/K2.rrDO"#)P>Mynܭr줺yH|9KV.CC3 dzDj% 3OfEDK)~jX)$+gc`%ӿ|a9>C.9Վx5ћ(ڈ8<3LPl=3gP!F=cED܍-ב&O y̻Ә^HQ|x?w7 #ޙ9lWbI͵}9(3ωٴ𣑣%&[$I$I$igyʓ/Qqj;9"b5꫇wJd")!zO<)+]LYV`5JƔ{Vڮ[@1j&=V Ør,"VazB3]qX@%%;":s^*ͣM.K^0GL/X""k UԶ`3+o[0Uf[0=eL\4̼eG}){p•%si _5EV}q^R,pĈswʽ!EĪyp*cos3c(ŜM['"6̳L7EnILJDCd癴Y |uj&]PK~v|xΩrǕX9W8+cgPRrpZ;lwL\aSKߪ8vnDe+5rIJ â$>IZEq9ef3CyZZ=2vZfwҬ`_[vV"bxRD="?c#┈8߃z8yc?N_iw^mxtI]ZǑ31ųDi Ύ#!>oDM}|] [1g( zpfD~72VVqNiX8$I$I$I4 ݗ4qwڈj`W 8ӓ|k-Ǩ /[MD!3k+NTf^߳#b]C8dBf1TO3{D? "=Ӂ5[{JuMJjezb|y[%AՀ}>s7QVz%nN5YI6[&$i)C)ś,`$?i?;8__Jϧ܃@P{,;±d[yw>Vٯhx&< x׮ K:\At-Yw[؇UWtR{UD\y˴u>[G==ye8gFgDgV6o'?~noP +84oX I$I$Ijj_GYAsSʊS~Ẁ唂 ) =Nh:@/Mwv)Iko."̧7^AY /9|: ^_;V0\D _~ &4Y[׋m*/S1sQqK6שVtgzzewΤVhP[5uڪkDD|Hew=p$MB{_W-q1]KZWl=MDl{aûX/aΙ GӿXy]ܵۏnߏ/݀=q%Aχ(9WygY*}expsDNq~jsIK $I$I$I4+5t=}/?4HyMDT]LY%uʶOoj{1)d6Qz}73\C'd[i<0Ǿ27`6ivF-XuEQ֦ŰVc[?*c#^+$I$"G)mq{#gygWK WnV 2 L4_ۺhc4쁙y\D|xC]^g9:|eӨ , 鼱4W5>ա$^yĒ3-v0Wokd۶-#+@z9&'>uڸuA2eU^XswD򞼼;v?gS橭;he(&}5"IpEͨnN(dNH]"tk+c3&G\p=p"w= ksގS*0oEĚ3fit-jW_f4!)"|غ/^^p|z]dςeH pF7D{ON=,H$I$I$I ܛ\֔$/OURǙ ?À=1VSel}`/m'i; gQ;&3s%a;S jJ<{djf.-ЙNͣ֍ۋ:%>ffFOUkD|P'I("bS֎ g"ƴbfIf71Pr{ԊQKb᣺F2)"W9k;^ P_Efl^H~9; lO_ÙU%o"2f`߈,$NԿ֬Al."'i,X$I$I$Iz4g7qw4ptDlEY~#/Wwtr[`f $&3"_4AD fn] -(7%3vWn+Xyp`>3 iibT )E4 if 9LVx[D8ňIZV©C<^\Ǖ]g"7SwxCf;8&voy#ADAdUئY*cNZ\EY-3օlIs} ]dMƶ6h`{ }g8|=xQf^(3ߖXaT+ 3S®[pAD̫\ƈ%yGP J]AfMSee: z9Z\Re2 Քnl{~ ym,2/|.l 89 PZ̫%I$I$IeUf^fVZXaOe1x#pYDߌ}#b%1*su)8%χZtq 5m_l9鶯$R 2΂S bx>3Ag51|Gs~~ \_-;"Iˢ Z{>yZ?3ϝ4W.=dLs湰1͏&#;[j|ysH?<^VԺpFe|ؾj"bK3sCx̼a"vs2ࡔ;5/o jjwS ykfIb>i! '$I$I$IyŏmW_I2x)m 3'[3d*3slIC)ԓ Cf^ bjSPtxR͋h<+XxDD5)vja;dKWWfٙ<<3nVY}f{d.[PG}ٚGb IZE}}*vm2+,~[[Jm|SۑǏxe+]9Rt< xWe /=WfgeӠ؊30wGsZ>;У*c;GĊ#%2LM{Y I$I$I$̼!3/faÁM/?yeˁd ;CSORwD}6zv23_B?Tuz {}uQx`cLf"3MYwkg8l${

2v3C?~[31Y)"zWS6p[y[O׍Lr/S"~_S^^91[WD\QSV1g2 #pXuquѳZWD6蹑4 $I$I$I$ yIfXg''Ph~75pbjbR/njAwc)XhV@f^II*prD|so>EI`gnyxTeӗzĺg:zr#[;2<(3w/)pˀv7I4ƙ;3['& 3 ^􌈸0s6+o]̼zU6ZAm{SEG9bwv*a~*"V1s , "8^D|0"^bR/cб#ೕM=zOD+[WD=">ϛi3ʦ_G#G3;k []!">C+c 3/7RC#Q҆s+t`D,GDNw|u]7%$I$I$I42ꦀOYKƭBS(^OS:fa\"3^PKV)~+pD4bD 8 geo1 _ޙ?/d)I͌I@$-K2ߛ{7FW#bAs4+xve{7+ fu&(`꾵wW6- #qemBAǬ"̼ũޭ-m3W9mm"DĈ:pv-9Xfo>LcAQH_3͈x 狈؃R N3ܪ➔.6 w9@s/`7$L3jB $IR[{Ol_̓+|zqUfߠ$ZRa #J2}$YZʊBm"fJ6x*"wv+3otwK3f8~QDJYݽE`y}sDME%_6E+TvyHx_#b_] RkE.5\5tl@D\ \M)i 3pQڮy,%/RtWR^P ۑC3%I$I$I$ITfȯdꔂ2s|xfgXJ:#)+~k+^v,u D]Z8a֬:X/ܒ$-U2Jb-vo ̼c(/>H?9$`ש+Wy-+ [<)Vʦk)]IZX#3tx%Ab)/-v_+gQm} fj”6?{3?L{gS^3YeX"9Wrf $I$I$Ik2̜1+3o2]f<3p[SV}!:)xP+|,3!jЭnF3#b~DUD#"~ \| !8-"j;Qh+IҬI %Q!9xFf=Y%<~֙9lsnJB#i?oA)LYr;ߦy_\RټODܯm#$wGL)Mt|n3$+:bqю:r?$Ŀg 2);Q@y À4A3cORI I$I$I$IZd_ZspXDl <xPXt=p*%y!ݥxV>1\ C,/O,oЛ8~ |33;wC">)EܓBf^:er?W8~i` JGQ/t8pHfA1<|pBÚDZ{w۸S]L4OU(+wy:g҈sJF QĩePD웙]T$I$I6qH9 $I$I$IR[!I$Io G3&IwN:I$I$IĸkVX$I$I4AyΈ$3tLǂI$I$I4td$I$I&3ϝt 6̒$I$I$I$I$I$i,X$I$I$ImaA$I$Ie $I$I$I$I$I$i,X$I$I$Im aA$I$I$-,X$I$I$Im ScB$I$I$-N:I$I$I$I$I$i"bC2 $I $I$I$IlÂ$I$I\DzS"ڄBer@$I$I$-1rH$I$I3c MI`A$I$I$I$I$IK=l{œE!I`A$I$I$6L{$I$I` ضŜE!I`A$I$I$vssc$I$Id$!?t%I$I$IR['u̼vV"$I$I$I $I$I$<gCNX$I$I$Ig$I$I$IZ I$I$I2l$I$I$IKC"<nB$I$I\5-̜h`yłI$I$IZf^ov{w]wAQU-Eb *R@PZ+ZDZv8Pj/7N*@VH@B"sη=d糟$/Iw$=QTF'9MGU/4e,`>䷪}I͑7*Jr0)X'ر)ɷ%y׸9,+`W$L6q~gI-cX)wqMUg]In}-kms+}KUh3q$ϩ3F)0#WI`,sS5+\T y=,Ɂ\U-*Xn Nx,0_U=xu9uz|&?_Фak7~I~<[c-IUu4U`WOsxVy~BG yfP{^=t٣Ɗ`eֿh, =g̫V0 j+XUU;XNSZ1Ik|mNR`a`$l<$ɿ3`,r$'m|#E#lgV0%98'`28{X3yU`̧E0gwbgoI^1f3X ]wy[nMre_' XCn{{38u:2Xݷ%yqU/$9+%}qp `[U?$>\ݷN,`W$oXNSC|q*Xz gWV3X`Kwx~Kr3X`n$SG=T_`mNjTےM,I=ukc`1X =9uI5ɁO??]{ud VU&$'w~Y$K$ߖԪ$[-X/e0<$Wq0L#I0Z'ymtQ`KUEI&>8~zژ:X)BT'%yk~0w+dy#5` }*`g8h TW$_yxUբz`'8*0XvVfeK`#} y0Xj+\U$$9#ɫcVl E2X؟Mo=QUN/`ژ:QUߕc w8/#l3c ,$3f]4V qǣU`m7 vggVKwx$=b3X`,/*rdpےVw0Mzڜ:ז/X#I.W'$%o `,t-I~ou1u2zXS >;w}`KwI ʧE`w\X !9pg3>?ZK`[w4HrˌkK8Ugg W$$$?׌[ޚm_o%V=u+.HrW_pKtv AU$OLr}rwv͓T ClL? 3X =9u쥪L$$9-ɫVOwwWe©W19v$#o `m,w\&VU'TSw6V>2taI7YIN%yiw_7mT I6+gUi`UuB§ IDAT$Α<*ɯyG3Xufȑ߰cI$e;qvh,swyS{Če,\:wG`[;Lm/h;{[o-}-km7 `^ TU?$ߓ$II֐Jww`"S+woX5e9`^y yfP{^m*Xz "ysV3XS޸ے1CXn qMKrԳ/%iXFS+$?_P NzML$Ӗl t_UIΙ+7t/ taJb+V,=`w%- n09O'k%8U2ڜ:X-zNn}:ɡ{QU/0 `lL{LB|]{MS O$`,<`مU`R3N`,{`s ۜ:SU;ɹIH'd ;zĪz}wTM,XUu~+$G~$gGݩG&yrG}ޞ}x6f=6G :b3X<39$T/ug-`$qvh;{cdHs#QU~p%OU TDLǓAInLrYt)֍7,C<,əsܻ[$uI~Kw=!pUU +`0-`.Uu ɹr$߼`,$wXS "4 E2X5e̫w3ng8{h,=`^yeI?I[XbS+c7 &7U3|_'%T\݇'`,0HwߒW%͜lkc`_}XY =gofk7 `%,`[Lq;ʸas!I.N$'Uk??mz`.I.𕛓|dA9{$$%9)SFU0e1Xx瀻Wn`LLrm#5`$nLv3G`Ip$? 9ޣU0w݇nU} $9$/6k`ؕpN,`s ZU4uisVKU}c䔪z]Wu-`,0:9OMrqU4e$'pcX)ȱc;qSg( `-mL{ر^U;`,]|f{ǏVi3ζ{ b,:ᣅ,?`.}k,?`w $ZT !.Os}OwZd uMI^-|"K_TꔪTU_1ugs`tVՇ$glsI^_ T'yr[^ݯ0 %c TO'yJiwm8#?Ǫ}}4eCI1C֝+}KߞD+$5bvޘ wFnXkSR~NJI{,7,0NoWHFAw9ɳI$II֐Nw_䚩;c9`,{`s ggoq*Xz qY'tꪪNrR[[`U$ߟۓܒI^ݷORnp s\w|9LNL#I.>$ U=A*ؘ:X-Uu$/ȝ Irz]Utk>ݟ3V`'S6BSI䒅GK!+E`w\X !  PU_QUWĩ{Jg,fg$yr>WwtU!z9{+ɳꫦXO 'JR<$ E/`aU`,c3F`V00`,TS0X `MmNjM$g'GW$Nr1X5WU'&y~<݇)kcێv!nc`|aX~(6y#lM3^?Z WޅT0q*vl&$[֚0;ܽ5dNquU젻oH$Ir[O$yetwOn6Vܜ9^ݷ-oLzIw`]y0>䗓|.^E/+L`JSI޻q%y^wը6VOwߜU$$9pOu&k} rژ: wy1X`kf0ZK`!.y'1CXn $#c6`ut<IK$:iK`ަ hc`1X ؗĪjs`5UU%yDSȑ_IX{UuNI-IT$yywz2b VU'$gwU$ݷ_{&Ew(ɷWӻij%yaO,TS0X0D T:;ٜ:X) )IU;}ńYwRw8;pS`~z{ͭ?,ضfTUUh^/OUuB%99V,0Ztv,`32K`0` ,N`Eϸ6 `X`Zh1/؏ ,a`˒|y%gͫ>fU^>*;<0ȼ`ڹ}Inw`?sMrVJr|'yuY,3I^UUMkI6ڝ$.>H:&ɉILp%l;݋}@U!XzBwe`Jw-أK.H~͂Jؖv,`# lUu\1$'yxU]c%l[ O~q{έ د\wͭ د*s̭ 'l8` ,N`p IU]U',.}KU50$yEwz|=5Wdlw:,0ZdV;1UՕSy[MUIw;yE$ Lr$8r$?J?o_@=ۖ0iLrͪo] VUdMa%?zx|$;Yv$̤1IXaaI~/_eIj] /JXt>'rXa{UA] 5 ,S%ڔIrM+Ya߿)U[ESUHe_YD=l)7qM7 `X`ȵ|ﲱQ[Nmz`?c5ئtX`U-l׺XؿJriV$ lN93~wU K>1zŬOD=[V?/ Y[0YMEDHrڒo&>w1%\tl=$O]plMM$5lb-,@ww7V[&}ʂ` aZwS.4ɇ7/+`VNr5'S LCI +,93ɓ_W\tOH$ͨ®$&9Yd}$UuT+wW] v$0AUIzIIrZӓ|4kI<0uIw\$ LrI:$QKXvpU]mB{M]INZt\U)ɏ%?I^_\leKuk`PUwH8 IDAT)_r`.I~7|q|zoI>eS&Mwf{/q. \v+IlM#+$\K+xxwhl=eS$}؞v,g%tfB2J$ӜL$%yeUwX!INC-`{XXI~5Amn;~c?Ugzz\YU2*XأInʲ3GHr kMΪ%v?VIx`$?7uQ;W;bnUdmL$7$'$^vIwhٹG$yEUݦ/Z~:ɇlUuƄ$'e O_Lr~J',[$znDQ, #OKr'Ew??<$}AwI^o LW/Ls,3p#)$G_[鼪z`_P'Oso( 'w_8˅˦J.Ig`UuUu$X~07 VUJSr>/3~~kIv-:(IUuڑeIc6lHu^%I.]6vq't{<}`FIO; {UwhFXl=g%yë'I20$^SUu˪zXU=蚖`]s& mT$wzٻI^5{WU=$GlyIIr]IV.]UIiIN_޽R`CUսto9dYUU%%wwu3TRJr>::=`;$zgHwW~wueU<;wa]Ugd`X)԰6ȟ%ҁzHwbAVw\1$䇫 Դԛ2XVιmM`ήӪ!Gu}̬E&Iw&,}oFO0U͓<#Ӈ2^{|&- }_{ܫ}WGKU$Nr$|.KE,+syu$??ݽk`{zIC6$eK:8L 9t9^1BUȁ] $WZe-UjI>XU7NrJw#`;[)^}AU=+VXkյ^mz.`I>>&K3 /Lp8݄75s-L`VKo9Uul%y|a?sjc]:c$Svo(I ϵ 30vil=O]ǹ/O_URw:tj/9;8ɵDUuQސpj`s K^ ~F`ξ50vPlU$b鳪ͮ}f:4uDUuaސQM\u`촹WX3VnOϳT-?:7SvoHrFw_ 5/ :,l`T7\uԽ3 lLr~[U7%e󏯪7w6@$34WU;&5# Y\UggB׆s}& .Uu$&=>7M!ߓIJûEGU7ɣ>$Iw_\U?CbJ򢪺ew/ɠC<*0 UՁY=а|ȍ%7bmto8=6h`{N3InQ-:9ɧ3[p+`}iU+,9 ,ݛw`$Qw'yIݗWU/jf1bކ~pWŒ?FOU2zWXeu$7b}Wՙ{iIݻ7n`?QU?7y$JL'y^w5- :,ޜ+̽v ,˓<-՗VUl$ɱӏ-{%ϲvs7՜;06@/I1QU]U3V仦X7Mb$Ma'Vwӧ8c:,ޖٻ3w_[u%UIdԱI&2}U3# li '9K=Uu$oIr I~>ɪq`|(\ϮKr$wI - -G` $JreߛUc}Z/^U?Iq&j'#W;OM򦌞x!I^QUo򞇏%euTFo }aL߽$o|Ռ)_^Ugdr$u^10::zX I3ek$Egii$៓BU]%OgTm$mpI}nw},ݗ&mϯ&yÖM_V&牪I~"Ƀz<9U$Ϛsg3K|S6ϼ)~3&1KܭBUu$?>IN$]U&L$$Ȗ0$gͻX/X;&ymfn L!㧅Τ?SU2p㒼~dSJ׫wWO%ye&UtYmNܤUUIt2 8 mC\{|L4έڵaz62F𲵄/fWCçmU$XeuRFίQ+Kf`U䏒<2AHrqU֌!4o7Uu$wNr$G&$os/XX?QU7O$ONru\$I6\AUeuoXQ޸˦ IkPUu$N I~ {(0xә3WՎ6 N 61yKr1QU])7dMf6Y>t`$:RanV>'yݗ{:LgQgI%m=g!I;><v[ZΌ{=\6 `;*ɕQg~ui$GdXw{?{xVIn-`*!1QUwH2/NXϓ`=O!?v|tF]MrrwO`?ج޵aы7 !=$Qwau.nKaw?isUSLr uqz}w/o'?-I;&YNr$'f,r>eޞx꧒>&CzajO^a}*1chrLn8-əݽ{`ۇB+vDCçyJV:jy}/%g‰c6\w8=ӞSUGfyIޘINIr@F]n.I3pM[Uݵ/-x!Q\#/MrQ|5W2z?#}o v1yLaaz>j0񦱓D㧷NڰICO^nWU}+N(ЛP7{ 㟟g|J>Yk=U$\6u:d7@=1ͺڙ5+I^R蠪v$od&yz'LYm䷓\}`ɡ4ޖ䟒Cw7íuXXP3nǭ0} `}QFO4L߽$l|Ռ<4NOr\LxzUM $X$(;rjc}`I~H\ULI~pْGWR픪$k!udO?Kl}pcs|ա,?9$_f}U %9xËfm^[irUYk÷Ǻ `ߵϥ/k_w%քn[WN+ ݥK6UՑIܥ}}ܪo䊁I S,Ɲ-m%גoj*VV1iW3]!I>zƯ9CIgW'=~#ɱv'Y {t9U$o]6u:ZczbV+\Q KJr$7{!gMbb珪$(P\뒪zm/,{>3X}DwS0imTQ.p|F `c$6,}_0~tϱM<}eIg}$gƌ,$wu]2U##N@]2|`F^NI򄁩K<.+WTU7Hr$Hr|'P0muUuPf<8׺a[CcX-3]Xt؈>;I>?imUUFE'umc)6^{|LTUe .܄RI>9:ֺi聱S{:IqJ'tU7d$o$yav/3II60prB2遅$Mr̲KHwc$䯪ꄌ#7WI^ǧR9l hInp'FOuwom8Xo3zĭ쨪wdpf1RS63g'tKrxf:4uDUuAސxj/ff7Moscۨ ء+ԕb''KFLJ43AU3k:xc'\wC~4aI~*46㟞y+񟯙U)kYѸc\+MqʗI^X8:+l}JU䷓dITUSٷ9?<ϹmM`8IjGFi7Q6v~ypxMϔ2r,OgͱnZ ,lT+I}AU%*KHrzCK5<~hfJpp=wz._M;,/$$](iɨ;qUnUuhdT%z$U[Ir8 {TV㐌seCIޔ$/-*mH`tݽ;yDUu@c3]t4Ln8-eP3>ɯ,:qx~~:k+~҉sgq U;OYUf8@pzwF2D~ϞpȱUur1Ͽ>.~|=?c<^}ԭ;ɿZXU0%K$tkh NU}*K{WVի+I>..H`r?DfMggAI9>sz$w[6ziwd_)YUw^-,ש㒼!aK*ɋ|p+']F^w[ IDATeI+_dUa`X|OSRU{~ z%IцWx:ބq,^Il$OKzqu6Uu@F˽\y H21'|ۓܷ/qW;g)%SUu)C. c6M}*Ө;$yKO'24ְ+ umXz|GUQMĝI7:G'{KrQw?`'!ڤ3.$7'?p|1{o(ɱ}ל'$ B]6IXk8зZa뱹b0SYVUwO 3zOW%ymwc{$Cy|w?g׬S,ܳ2|: M})-g ,| {s~'~꧒l=b;e`%}4#UuPF]W~`ka`Q:>]aϟ; ͵WrUuFpzw_ogcMWF]RU''xO%9/ILr팺:\Szsi cށdaES^c'dBn IݧUC3z^kW9uXe1hM}qvdZ`~kU=$Kϩ'yiLu`Jo;xtwW9ka$ŵ|^gMXP2;Kw4UX#}Lw$_f}U])wo8.A_5c;\m|LTUe uP3lxU3\}e⒧Ofh,_I_6zwwO9IVI>0;PӚtyU$lUu`w_6pW|bڝ׷l&WVէ ɉȨݓ<5yh3$:  ؇w`I7Ź ,̙~+m0r&wmzlV4s`k꒬hX:uIgyi6m zrw4]6uTtW&]nP~RwzښƇ:)$gVu6(tǪI7z׉=K{IYUCOwXqV ,T1I:0ke$97.){\3'ްz;4#3ꂺԞc-lq \AwLD窪6Nӽ$WIm\k|LTUgB׆}& I>+êI=u~VLz7='ɟ$(yVo%yYw_Ī:(=NOn^UHrtwgƺ30vָ^UuVMPn@=[Fww&ymU;β>绍]-,R*h5XbI[Ė?l1&Xfb7V,{AԈ!Q@@K]}gΙ9g;wf~ys{\f=8Q  w<]?&ӯfƆO.!33".ݱO*+,H$I3"~.WMe<$I$IҤKIpXA CT sXZZB T [r]1<UT'_oz] \|ff4]OZ̛#q%W~ Jq;1ਖ਼yMC>^7L$OvA1g'"roMnKSS"xj Ϥ]U'wFuE"M mƽWͶc#beM[4',t{q%I$IΡ>aa$ILX$I$I* Kk\="P%80TGև("nerM|^(3޹="s2sK\H5{% OZ "TtՊ'r'g}'<$.Sf?Y ~ /9f<8?"NFp2p1'"[z@ ܵfR଑"b3%yE[_eQCL}Q?$I$I'˶G2ߖ$ $I$I2s7F1jxUF>\]8Fq3f>Ĭi.3`yTI%d7j0YkgwZ9z!cTI732kvopW0wU3 # T;~xކẮr69ǴYiG+"b+,H$I4/.ﶒN&,H$I$iسs2Qy7>j#t.+{6a5dN.Ǹ("N^UH'S2 w1Pکg0OH{f;;33"^ gǮEY̼iJb#dN9aa*,;fnth3};{wxGbJ?&r"I$IҬVJ18$Iӗ $I$IDKkSMòGWs #~^WR+x'h)݁1z&|3s2 K[Wim}xc_;v "_o'"Nv_ucT$C}+]i5OqҰ@kP{ $I$IG&,H$I$I-j /, m7֗ 2s7҆W?=\_VO^|."@5IqCD<6p.O,mCD ^C'Oqgr]UپKЦBfҦJeoh@OiN$I$I3XD܇2s݀CYDŽI$I$im55E4'6yGb6vEzUoXlKeTU [*88 8*1`jbwf]ʒwxTi1U߿xRDef3A"b.]?o8z 6` gLypuiݨF>uo:$I$I{xɆ=!" *&ILX$I$IfJRUmGbWob⫴Ty5pwF:Ư؏5 ?*m*{MD Xԣ*ah88m ϧ~b|QXt'gfF%:v2U&S-K$I$"N P͙}RD|=3$iv2aA$I$I%2VV6#b 7 {c%al<=L&9ONF[DuW^THxI$I4FB >2aA$I$IROdMM_a*{f?-.QDF T M7J "SM_H53!3w $=&thiOgo]. M?Z܎ `I$I+s$&$I$IM&ҦTްX艶}Vʯ[-J`mfnC̚JtL`y.d'S%§č&u]n̝T,ƕ7D׀uzID|`@UFƒ$I$I&,H$I$I23&n.is_aJ\UgFq++8lC̚E27#b%QUˀS3c8\[m>̛n⺽Fƒ$I$I&,H$I$IוvqSఒv! ťݩM爸&l7GRC\ |88+3wMuw;13Iऎ]uVD̥v‚$I$I$Ms&,H$I$IJUKnX ,~گ#th013w!fMsyETv{dww#8"^ |xϿ\'^W6*8QDxRK$I$I&τI$I$Izoh97"QMm0DYZڝtMްX; \ׇ]Wmux*Eg 3ꑝJj,)]TiKg&fI$I$I3$I$I$Jk &0D5^Z͈H T f<MqD<8XY@J#"[nԂ1ߚ.bSfnoqXǃ$I$I$LX$I$IiL½FUFnۯQWGv3sweTvd̟EmoimMK8HE]-ecH$Iqpo?Mico4LX$I$I.3o-QD,}մ| UWǴ;"3~Ն2329`pqƻ xXD<x+pPmPp0&Ew35ҷ'3z!eyGf-$I$I$I-5h_a2sQn:;#brCcCY_LfΉ9݁ߥ\/\o֗+3sOmB $I$u%{ NfVbyp SKf#$I$I$I=WKi0r߂Gbp`i"b7̛$4[;' oI$Iz("r<NX̝J$ $I$I*o~ݦD,}UGb>pPi"6ZVo֔r3] 1T $I$M^4;%QHf$I$I$I3Jfn65ӮzjA)QDl9ad[f+*3 WihI$I$I,a‚$I$I$i l,풦%a7= 5-@వ1k)u]8p$I$I$iV1aA$I$ILl^_/G6 C /pXi"ZVodm}Y$I$I $I$I$MRff"b.UUĆk~ަsDL T!fI$I$If $I$I$I"jB6#bvV{FXRڑm:Gf6x\;$I$I$Ic‚$I$I$ITfn,QDƫ0,}aYiGvC$I$Ii˄I$I$IeKk h_a#KKj‡$I$IRv*_$I$I$If\WZXHsՆ{Vvw62sw$I$I3\ffD|x]Ξx$Iӛ $I$I$\SZXD EZUZӮXO5^-I$IOtl%pԇ#I¿88E3>#I$I$I EbWo&R ]88%I$IRD 337 L4VLX$I$I$M7gUC k3s$I$I "b^ftiޠ$I$I$I̼MXFsՆ>jKkiYXS$I$i I $I$I$iV(o~զD,}U.8F Mykb$I$IY?K$I$I$M&ҦKnX %p,XZJCՆ1K$I>CeycȄI$I$I.efJDV.a*aN?bJkҲzUö>,I$IJ"by@Y&K㋈Zt=<3s8$I$I$*%a%7 Q%C@Ui0r_f68%I$I+D1za3󦩏Jf'+,H$I$I$Ms=E\`Wmm9&8ӒҎh9"ne}Y$Iϑ< Ԇ#I $I$I$I{F1D>j;hWa .3w!fI$I{ ISƄI$I$IYL`F %9,}ayiwi7#b#7֗I$Ifwʢ$ I$I$Iu5}h_aدQ`EiGv kv!nI$I$I3 $I$I$I̼ FVʈҎiwD9a }[$I$I4`‚$I$I$I \]ZؗEZ\6vE:UoXl2A$I$I9LX$I$I$IҌ[+Kk.aq^Ǭߚ PZ;#bd7M=X$I$IRk&,H$I$I$iV[[+􏈥OpX,}*("hWamfԇ%I$IY˄I$I$I$ieMe0fO JkPacI~$I$I4)I$I$I$Pfn65.aܾ.}CJkhY*aKb$IysqX6ƾ]S$v&,H$I$I$I l,풦%a7VsXZZB ڇ%I$'".#{F3|1p*.J㋈Zt=<3s8$I$I$Ij!"P%-Wa LpNne$IY&"離8Zu=1!IҬeI$I$I$i/ٳEM#b.UC C@#v_i7Paއ%I$gtTx_\ 3'QU/i$ijYaAXaA$I$I$I"bvVS?EuN$I|;38u/" $VVX$I$I$I4)eǍ5Qke IDATբoF&%7JpՏ%I$ |ƯwTFf^?$i63aA$I$I$IҔ(+__ZX@sՆKz/.-gDl]p}[$I$Id‚$I$I$Ii)3ז("ҾzQ`eiwmwIphSa-I$IτI$I$I$I{\SZؗVz9jm33{$I$Ig‚$I$I$IY)3W("ӾzQKuW3"ѮzÚ%I$If!$I$I$I$̼FZ<50fCfnC̒$I$I3 $I$I$Iy3p3py #U*QDle$I$IҴc‚$I$I$I$Me崯ް }("ѲzT$I@1@$Iӟ$I$I$I(37KFD3~Ն+} \ ;("ҲzU>,I$;#{冩G4 I$I$I$I{L`Cilsްӏ"EĭLnf>,I7OX |hc$Mc&,H$I$I$In'3wJIpXE CT яԦsDL {$Ifs'y9Of5J4D488E3>#I$I$I$iT m7 1a:*8P$I곈8}J8؈$IӍ jńI$I$I$I]aeTcD 98%I$IRt$I$I$I$Ci"bUB%Z#,/.-fDl]5Տ%I$IRLX$I$I$I$U2s;p]i"b'7 >j(wGƯ0q}fCܒ$I""23$F&,H$I$I$If̼ Fv ?UVvL#b=768M$if#G_mLfFDעyeÑ$I$I$I#"}Duްd$IEB}T#^;>*I I$I$I$IReE~4WmOco(1^B3ss#$IINV S$^&,H$I$I$I4d--mGRWoX,}*("bCcCfԇRD<88U%iVz8 Ҭر| ♭LX$I$I$I$i+o~ݦD,}![O KkѲzU-}Y)>>X*HfÔE!iЎ2/l$I$I$I$IE2s oDv*8K8F5N$3G[ 8%"\?wf& h8]6]O/IJVڭTKifn$ILX$I$I$I$I23]Կ$8` #WsXZZB TԪCP%2">W06AP%bM1~Lo]3gL$ocgш\GD pzf^>%IF1aA$I$I$I$DIpX_M#b$6VS%CD?bw*QDB T ĭ]`#>"N>cXPO :( [J0@D%1eDl7NAR7v2YH2aA$I$I$I$i26EBWo>j,-_a_5Cj5%3<@Mwہ}Pfn@L}ύ:FS"'yE'M؏睊 Df;$I$I$I$I@zMi"b_WoX ,}*ۢЦzZ`cfs}', [ jee]gz?ӱ iSQeHT55]HD6:OX|hcŸ́I$I$I$Ii(3PjbtD,}!`aV1ZngD]r'OnKi^is뀻wlMD/3o@L}FU5]Vߔ&?'Pݿ\3$i2aA$I$I$I$i/P&^QZXBsՆ zyJk;&8ԇg̼%"ԃQUk8)"ޖ?23#J# Q%u꣚2\ \;^ʞn[&B#]S]_NtI3<x !`U}s/oMPcI1;fը"nT8Hp[-TЮ?.ݓ91p1~0z p&p^gv[tD}@{;nb'C=yQ%mw?# T?J=g‚$I$I$I$I,T&G M2 MT>j&ԦsDler֮'NSқa<x\Dx;N_urDgo"Y匾<x!"N8(E>Ȉf$GTkQ-*p KjK1[ʎoqȉ9">g=oM˩ؕunݙu0m(v_Ae{־#酒Zm=ˀccp*ƯBB`KDxXa_LX$I$I$I$IRLi0 bn9".aLu)d~?%࢈xD}:TGZxG3E2ڈO;=&,Db /NP~7" ख़y$c8ѓ'b>$Eě2.SD>~ c-R&^(_EijL8"G:+'4)?Jgfd2!12w>[ޞep<$+UhN"PfJ@}u*Y=ӉT;@|/JlJ0nJvyx/U4:*a9=ۘ |ӾT3"aym1$I$I$I$Iz.37&LڟvLlb&f!pEVZVoJpj* 8 JPZD ^ǮG?gA5E{G\3ĸ.Y #٪P61q,^&= ?e0gw}~?kꜙ_cq['x|_"@J;sw?pnD<*31A"O7s[e~ڊ#o 0yTNr;_e槺QUhc{$'{9f"ETtݒ05g$I$I$I$I4P~?"P&au;m&GQM l590"nj֣8"aaV}`/n ϯ[ۖZy?ˁ#:vP3SOIVpq.~CvcOؿxH[EuYGU6fK^գ8d~3wj굫ćw|K#=}jߎds+3:AqNfb "8*vf#t[KXOz_ߋ'S5t\H~ܿ(z GHs?DuJ B{6ݨ^NODDfV3NVQbZkOq}} uY^b9J( _kb /pTM6&,H$I$I$I$iFT7TU$8 QMT<Ǜ[q3-7P%8lck {RV'GćweUC=߉o|2:a4aNV6doe殑;"_S_-@̝ggVxX Wx" ^5]Ng'H <`>J5U`"b!ՄgRS;c^|dۨ&#ygDZ'o8v)pzDܻ)A-3E?J)Eݛ>%4xVJ)eR=x/!࢚kx FFOTYux6c'+lZAO:?TJZZyn|43aDoo^+"?dfVx7dn̅G5yӻ3-s]O/Ijv=/3?f|D?9FO8)^q'F1>, ij=*AΡzϽѓy;3&ǎ5RxjR/&g]T .n1I5[}~8/fWǍzkfM;]?Wc! No̿m]81Tl(f渕"i ]E1E5eM ~0y%Y$Nk?SݏvOp }3m-+l~a>HәY„b‚$I$I$I$IR*UoX ,Lfޖt 0N‡&jʦ̼eLJ߯0zmy}yfjT;]G51Xՙ 稒Fi9aVN?_>YwhJX8xdfnD|Z }jᙹuciwu-*^81Oȏ&7VֈߨmǩS7J б es þY$+d#=_tZ@tP@Y$ <}2 +*ɦu@ffI88tĮT--zpod#7Fajƹx&N\Z['+dx n9ڏjċae!d+^I%+&\p32:Ya7#2R1xT%+GlT$+T*WtZU $I$I$I$IG=3 2df;3&3_Oe3s)$C&=jہgR}ժ̚YGRC\fO}{V_Sxk^>l)|Nf{5vf^>#P+mNe['+eYTG8="P*s|ʡD]Gz]fox.3w~ &x'1T2 Y*WEEРR9]ύ;OuVx`Ͷ^/:?ɱ6d3I?v xQfNՅ_f.M6+cÄI$I$I$I$Ie-T@[Mz>)a~8"> =3V[|43_5 K"s;v=3"ޒ"~y1voU.':'R#~*bkd$ֹrDOuԫ5VOt2Y%iޘuUs"snu-x.̍=g& m ge:aIOͮ^/&O3RyOйTU~Ou2U]kͶ%]W3aA$I$I$I$IRߔo~զD,]bjIe'lFGAߛ!܌J!_乓~la0{`޽RI{I=>]Xa3ldUe[]ς<`J԰МmҽfNkO[gڪL+\N^xjoHIIn?uAoڣV̌)?YU?wkq󑾫ߑIu^~$Iɪz,Y^I^rp$wTm^ `.`H '(?vac+붾{m/Mrʊ[ii\:킪:&Ϗ H$笧~=Js Y*&9qƴ<^[e_3f]Oߑ$/_NU~?<}3<5&~ьyc$ɯ&`%Ƕ6?{I1#<4ZC_U5ɜ ,VUd-OI.I};ꚇeƼsʸdo+W^$ǭ$O`;m#}ec촟SHkw$O1{-O>ε4p61ok YwwOs$J !; )c$~Fj+$2i%yH#3|\{vzOj`ǜW'9aX$INt;p0$? 'g؜EI޽E5n7֙z7& p~H_eOBUu$42tH!IVU7[m^Cu⑾/ƞBC;}yw.rɾ$/1fZ`aG} $g$ɕS.$Oߪ`7. IޚYI!>o{Kc$/zHU=l뙇F2[tOݭY$Uut/PUf睰0m$prI>5r٭:o^orV g5WPM/Ir$ߗJLr)K\䲑)Qw_*)Se  TU>ҷ/Ɇ6W$?d볓UkYgj$_:2 ?+Vzt5&yZofxoI~'-TTóU.0NKYog]o2$飯nIm_ZUZ rh5^F /TZ=v ;\mgqgy/0~U X Xw5/-WTS'U'2l_%< ݄K$sWՓ-.{'$*kk3lؿI^SU?22l}NM2겮'o6/$RU哠ΪI5ɷ'\sT?M^;Wi>@Iޟd{& ﯩoKtM$7 toKrI]˵;U$OX1?+ꛒ ㋓|gNrR@kFOɚK$oSgd.yhVՇ2Ot? a' ?e}In6B /I;c<>C2<]kv9"B.= ~jRzEbnUP PU$yucgL"F7%yswd+jc:iItd&m=~m5\1ڌPI[7d`ص;tU^#F!y߻3ɓF)Ϗ-O$p2o$XUg/' W:.é+O>I6{dʾ <d^^c|פr`2S--w'oN5_2tV:,C@ag7y8'é*ͯ$i7%o=&䏓<1}~VwUX6Ps]:\Du’~uot͸6ӻ{_ a͜If,XgU>o ]任}e'yA&y~O?'F<1$_oO+3ϙԲ m31I>%.KlŮL^pzϽ)#{G'ys<$_^LrIf3"MI:]^~XG'y&k6 Iɵ2l`$?^MWŖ$6$y޾Xp9AM-ϭzTn#$1O誺sXd U'9?OPOU3ɷdج}S#Uw}?2i!$KVIN]qkaB[+Fw7Lf>&{I%$HJ$XImd }o䇓Ԋ+ 3:5yMq~I>ݓuLU}{ProĜ:-SG_uL gIN^'kyw~[voLrYZּKo_}qwFXSWNNrB|6Fw_?*ɽIrNw_oI$d2dTU^2ex=>e0$fxޗXO0cܟ!}xq9\K`u>τ[$9"C +(zn=r=$'?+$Ge,gvfOpaX`MXYGl0w ,s'̝0w ,s'̝0w ,T!UuzPUv`[|k#']gVu;,= Uu$O a$9-ɳ `X`7yZCFI-`OX`7یoY,{ؖU0 ,s'̝0w ,\4c-v1[V *oNr` ,ktyI $yc_뷳6{k PUH5L}wrVUUGv緻 J K`;`XN`;`XN`;`XN`;vuU[Or$Uyw_- Uud$9zY>,\Hr ??x֫8Ts Sog,横IZv드0}Uzj&9duqWw뷯*G`5X݄6>a)m`;'$b<q IDAT]ߪ>.q$$9us7Oœ6UU]'5L>?]h2sWG&y֖w ,I>+o쯪%99CxayaܲbN̘Uua ??,\mg=  [F`II̍ >>l*v󮫪s3ݗ,\it%l!?t'MUUeE{۟IXhI \ߦͩC2V;E{;<ɗLTU8a}B`WXv>7l5wդuƜ 2㤆I;{p0XZ}u2,$7[dJr¤{Ƽk܌yw_rn ݗ%,GͩJr~Z I-١IhҦ+2%̰r0 I\0i6'9)Yp#aҦK3㤆vu uXX3'm:<Nɰq鎞͘s.Ȍ&I0KrڔkN``$l89,8i1ڪ:'Oj8;Ŗ ,%y7le!{ /ۨ/MriOSU~|ZpCfҦ+2%̰jI3a+?[_Uݽ5p$y޾Xp9:4i Nk85-]8a붭Bأ&koHrN$y}w1 Uudpq98a.l殪{X`M`頻cz$lW52ݗm[SUMmka `g\ߧͩ}IN`éI[t%MUUJrNw_j-SUN? |ϊJ9 u '}UaINNMr|;2ɗNTUuqnfX~zR;]h<|}{sGaSGΞúI}u3&m:*pr I9]UX$u-䕹q!I%~Bg-pM+<>}3$W&|ss[?6s?`gX`˓|lFUU%9.'4, W^{ƼUu^{`(w$%/TUI$LreCWWՇ| ɟ%y.}i#} ,Tm,׭I"Ӗ/NWU٧5|ۗT3<)y&Ys\8937O6fLxxr3yv Uu$,v(v.& W 6zw^]SUfz$gw cf$uw$SU|y;B Uu ,T!I:+$ ,uI>9iSM6M 5,}~RCY.p$, 0d$Ԑ!مV 2wVU=>ɫî$?UUv/mVI䮓ڮMrMJ/I>Hrˑyl,Chsg>u/=G;ۓ|:I#^uvAkz>cHUUI5a3%wEfrCsV!SU|e<xfL %sp˜NMx$?仒Kޕ7Yw_{)GGN6T28;;ú{u~ Tաʏo]5BgPC`ÕV!TUǓ~_'d0r6XۡI~937r}7OoTCuEIUu$12mg[],& [5U.tif&z*E$M$װΩIޛ䤑zNK$_xI׮wK Ir$O]1}9ԵiU$$tm( aow"SǞ\UuLIkqe֤82Vݜ WU3=$e>?~D5$oHrU^$G$9jƼ'92Sp;&y*s&9k\YIpyH{%6*A/IrI6*iay;9ɉIYpɻ6k3kO>-а[`'9eSVXݯ䦡pj_Wg3R-4;{$N?cUJ8y e3Ưs3~06#7#}5]D`$OͫC遆OoUB"'׌y5|OvSik17 ,aH'yfw_s;I>\U䷒|PUì-C2&yW]29kK]N`ɓϙMWU3mrUe2Μyu\U,-{+![w_a̧(Oxyaeazwٳ\[UKai<2$oĚk2ɭGߒYO&yptU=OhLnXxqO ʹၪ:4ÉJ+5eaIyllӫkr`YI׸lZU}y&[CI^k|jҦgvarfxSΌ@Cn6|v{XCuHի]TUUIG, %RI#Corѹq)˟F LI$M$/ ǥ`u FM'I89-- lONTUudfnzwÒnҦ+2;pN`f5X& }n =(ɉ#6o$$5,qb Z }iK|h^Δ߈K$_h$ӽ۞䫒ykػFw>NTUuTfዬw8"ɗLTUjl{97m?ck3ҷ@x$i+ U=_WvZH`C5}-H$M=-ɗf՜Is#3BUdwzf=< [F`u&OؤM59xVaEֻK"/5>'nئ)#}G}gYOK.Sk]}wחUO$S?e &9! 45ԾV?UuQ-gM{Ij˪@`ػ$&yUu\II]dĭ&.&UřhXjx8 6X8mkVաI82%I>8Ε蘵zOn[C+;jI>5~SNR˦'fɧM=KX*$%is١S;iw15nQU5gnrlcgd-˪$7_1"KXsa3ӓw~U?VLDV?}b5?!s7.N sݝIDTվ$'dza)pB}9>ɽf;PUdzaZ1dl{ و)3OYrӺZu[}< ]㍹i`I^uN#Colty6q$?{wnWY}{'$BB@'ġ֑:QNuDZmPρ="2, (kd!'I$I4Ef^OaW-$I!$I$I42jj0GDShhe^اwi8'#b=֦͡ӭ-W""Q|/ Pt=h" w8..Eck"ҶvsaDxfšOegq$C7u +d8 8OEc3.ZQ1wp9Eb.>+3=2xDܐ|y3"Gf!xpO&NORG>/I$Iv2yvD|23*I?$I$I!3bs:WuE<`)Va7(kVHdȆ󶗛N qefniTmY ,d pCO7=xy]\Eĝ37}ֱ?R˺:̭q"E&?%p%LYQ#"S|/<"FĹ.khiߴY?FVXPS3$I$i'VgIڙX$I$IXrܔ@Ck,efQM5\FD:"b-͡U6k ,lx? bM_cz`##qz~+j7ha%37GķǴz(P?"HuGv`X"bp\šY33go;=3bs]K*;87"Nm-NjE{2WsܵO)DĮ݄;kK9\hN$I$IF$I$IvJZ&ZcQ;&3z5k4Ve%4mZ2uDuL7Dw l Owa*鶆bo0=0 ׼seM[3="Ng AUTO3w=,Ya5NOvp$I$I$u``A$I$IjPn4S9jE"0C]56jQ+"6! #Vfسu3q*ܶC>Ofypp׊×VY~@FU@kߨX++| 3WXK?]3~ftXTfnwl;|pJU/[X:DrE{g&)u<-"~<#3cM$I$I $I$I!M9p 5BrԊ62GZZH{Uq7Ϡظ<̼X|+@הhEAD|/S] 3q1EHe p?KFA@f~ 2A'dkL9dG1x`LYMG @b#{h49ݺx~f~ "AU cuUE$I$IƖI$I$ieMt^D,9Q;&vnSZqB FZ33[3DFU=Ե*"| v#3? x8l꿎vsi}\UW^U ޺# #RL ,@qO[fBK9CZ{5X( lgFUHI{gϩxjf8o-28UGL?DDl/v8g]? MK$I$I2 I$I$AQ+"9(546KFZs[/ |2"Vefc$3/wn;x_f: pn $I$IvD$I$IXfn،ۦ"b wLY;6jVY\2a?yqkkD\ bC >}M6wx7Ua^;# c:g735`u73Qth9К %R1w0.q>>OL& v ]$#dRވ_aSImUᅟ/})I$I$I3$I$I M畛 m.M'EUZcMyw6ypՀk|*"~|xPK\ $3?g C9|A#;_QnTBfߥ2xoj]èOuCZnGCY$"]Ga%şBӮ=Zw}.߿u[1|Y7 +H|g]X* IDATa#Uim<L7DL8E"bEdž(.JYs=Epp$I$I$ *J$I$IŤם,> <,揸q9pNF:C m2qgB؋c{D]ws2=o'pBšwV,3Ov5.$|^mo3 fD WV<13frVUu):;k$I$I$1 I$I$'+"b>p͡yzWjX;̼xX=[):<3i*0.ouXpΜPӻ%A}XWK(:5lEQL|Twϊư; @{\UG݇XL,ܷ_Ap6Y xZqp&]R-3͍WsJ?q\M ݈{Q Rt-[`η2sݰ;꺗Lwl8g[D>pp6$I$I $I$Iv'K7Za<|-x!M'Et5P61(6nِ~0şA*GiUO:lSz& U?xKG.p8E0`uk T_<+3kB_ۦ{sT^h󸊹 Ed2"SۡD2sJ8:wr/i~|&"b>ߋw/G{Dľ Q="~ByӃtSz4uYAw|?'I$I$Mf`A$I$IX)70_RZ@C׻1+prԊh40la23?[8Fcg6ucXœ4s p̼ۗ:"3}nLydš 3sMå/plfAsT]>ki9b8'į}DL;("旝Oq ܭ'𞈸O} }7'Ϫ8t=2_Öx CzW7Tv`i]S"#2~C ,t#KUp%V̳GX$I$I  $I$IvJy#rԊ=h4nwL,nWZq͡FZ)p[u;Ef^pڛ2s0;6ЈX8ܪb 8 w%P^7S_gc3'~[Da"P̼myL +@=s+eM m x:ɲ#WT,qزJGxSšgO@. o;ڈ8G.&"#Vf[z\&jK~.: 2n˿$I$in~IhX$I$Iyœ^nm 41px9jE5t5Dv>`7,93= x^ykk(oLM:bnt8>WIq#WP$BRxƈN3ӀT\'AW[)6^L)xba[..djG}(,T#)|xFSo,rV% r@š2I-xM[M߯견N>z"X`+e$I."o4RC 4s ,H$I$IK~t^DGsaX0zsI@CkcOs2Z;3t'{cOI_nͿ>"{\*j_7"AWXM=]__EO$99pۊ7P~}" ĊC[dxCO^ 7>*3N><}]-n{rԊ446'X;XI᥈X|937%I$Vh#ſbFI$I$IAX_wNT4) f dٱ)԰ X;7ˏRtX6>/Y |("S"!O[17SǏvCLUvGa@3(3ϋӀg +  ہwNObjȈ8,3/i0" /G^/(&"3 WW1؆n 3DZJgCxKe>Bf$I'6K ,HҌ1 I$I$IsLy]9~Yw^ḌؼYhh¬ A:@[GZ .3,^Yy>mOvgduM DOQejXT1WbJD _M"b1?kϕ2cb(%K\#I$i,i8vBd`A$I$IvT5Yy @CkO5akw`N 64sxE43ϘzDij/0= W#'?}ͻY~.ʸbX7vq}S|.TYUps\>ϫ[$"P|5ZXd暈xpri/̯vD7z]L,ᚓU^ n3>{%̀Qm`o#5kouwrT^?poI$II$I$IsӁkE& U;]iu̼z5/LҫRD PlX? \I>9-7n6bn7m&"͙˥P}UܢXK"3o..GIB |]3ODgfns+:rl+gԩdDvnX \ 6 x4om_{*;efmPJ$I$igd`A$I$I@˩ޠz MVaQ;&.G2P1nkGZEr5zbHeMoԜ\<93k;>EaI+"˙nؔ[Mڣ,\T1_ 5-" pJgnG̿(^U ]֩Ybg{Dܓ u ,t)YG͟ȎXx)N6H$I$p ,H$I$IzR>rԊ4Z]ccWrԊi40)VۿQ~xL2|gD|,p>9"^vh/༈8)3?Zw}D, 0QƐ pڛ#s}yK;NFz>s$3B#yNH$I$ $I$I?VDNs51UO8ED\GPEicPE3gVbfhqDx|A]^C]|23=85"^EyLll>`_E0}z! 7UX̵qEݓ=8m)" S`NcGAI{? υc+.)lQ:,.̸حSESty[ڷ[7&g"bayAm(܅ߋw*;YI$I$t ,H$I$IfUj:́ܮwL^Zq ֱFG~r < xg%7/fܜ bρeS?I[v܋c)~;,@ѽms9ۇ*"q^ ;͝ƀW )^}+ꆪɈܹS~]*mp9{_u+2 ;p<+"N½ 37M*"<x4gz#pxdf^C$I$Ic$I$I$iRlRhhQ;&.G&[DzC (|}:3XJkeDr^gA 235"Tq|B/|遅](/O};Q9WI3 Bw>(kٕOo^>b;_\n,v3ؽ\2=p[37{֨Z̼3Jn 'S"a3ٽH$I$I$I$IX̫ Ή> <,K~8Z{)GSf?_xAf}#b7̼ " S|se`yQD CG=?5|,38!ȊÿWnfnվ)"^vQOp^ܳm@&ukSMfprLbqƶϗӀ725+Eǁ>PnU\T "ρoR89"Nq?I$I~|8wFf6[IҐX$I$I$i@ "bFɦP `Q;&v [T-19PjX5Ms]^[p?auWhy p{Ǟ<1" ̬ [Om? 1c(F \qr@f~)" "Q ( [VDl!DڑV;^>  7 ="NO8eWh=i~%pC93񽶨n60xrdz >˻*vW׏Y O^ ݨ<;K (qtfv2p{r2"p|DĻNN$IF(3/?uH$I$I$IsLfn.-G؍PC؞vl,+G`uf^7jwyr>"/7R괞4߭ U8.3/l>3vEC 13s(>,T婣U5UuJ{>0 RmFpOI$I9$I$I$I;rCQ+"hh%wL,n[Zq=B ni;=" !-]9"Kš2]5O`}BMfֈ7z?>"<3e pIX (XB%">S#3ۡ yyD SqQX$I$I; $I$I$̼"bO FYC9jEĵ4i̼p l/?^P ">_q2ֶCex?`A#~yU .8 3T΢j#b1S 큆I%,Ѯ.p* {O>^U_uր^}}Gt?I$I9%2sk "CwO8,3/q9$I$IYPhlX8[54L6l 稈88]9.~;b^93_G?/{dן _d^kGD 8m!& pLwymk9c;,ameZ-mӛ]s?<;8tUfVu$I$I+vX$I$I$I=)7soz-"b? PÁn[#ɈP*`mfnmsGK3]+"L6~ ysD\ۡ>a7 ܦmg3vD,@: );=Ui 0`wF+B~:ŸGm3IýzyEQJ{e#$I$IҜ`u $I$IQjXA&]n;p%ָӓwv7""6}f^;gT 4_y}:?L`X2s]n\TqG!|~\q͙ "N[q̬ܬS|vm{mnTf>z7RtYl,А$I$I"I$I$I4k WuE| 7ڪwxhd"b-͡u#zf^\fHK^١"eRtJO)J4̋#3SgPGt3Rf^Nl;tBD0Ei+@"bzZOWPXg$I$Iu$I$I$IҜWn^]]΋](6Zc?oTxl54Ve:2' LDN2qoCN rtcUSMe IDAT,i7UmFW pߤuu2=xE'n}ս$I$I3 I$I$IFfnVVD,3Zç_wU9jE&(2w2aqv$z3wf^]qC"yN7EnՙncsBUc=Sۡn#^_$I$i2 I$I$Iv:哿/+GؕPC^wL,-G`u#uEħG'ZOxDġ*|?"~ 'Eǔ_MRs c㪮\m=l{tD,̵]z kV`aq#$I$IҬ3 I$I$IT#3o.)GXLs5vecb7h`"ذn-e.QXQ-uK q~fn/]ѭ  G}jf^?6oB&$ divx߼raя#$I$IҬ3 I$I$I4rSQ+"91;prԊki4cuN[w^9f~ CČ{R;̥š>PW\_vD ,l֌w݀}jX$I$Ic$I$I$I ߕVDMPÁQ;&*N t5P61ool/*nhFǂSXCD< 3^WfUW(_;*׎{PJ$I$ ,H$I$I$9y p "b? X0z>K9i5x}/Zf^<#"^<x4p4.\4*YN&ճ`w`ϊC#ЅaV͟t ,W!3oq^}4v$I$I$I$I$IvPX\PwND?Va90%Z muZclm~ yp pJ=pgM{):k_gճU+D=0lN=">qRfn,?¨L+I$ =(/wf^?%IN$I$I$I֕uE< qOׄyrp޶XKsassQ9梺+g s1DkM^K jY <`t )X>}z@G|_I$i/6i l$I;# $I$I$I|r؅b#~]5gcM7׫ɖXC}a측ᘋj UM36C'JUlL#`>54J$I8a% qNfI$I$I$u-3Rlm ( uwQ+"6QjC kFZxxņXf*V19k2s 烈SXWDXXsM/By5Fx_I$IcIM4 ,H$I$I$iїVD,s@`Q;&VDDsa5Eak2{&E|fu%娓q%S }t騪 >m|^U2}%I${a`AfI$I$I$͚\ZŹ֯]cc767!@la 6%4m$e{IEU2}W 37Eħz:s69QS$IUZX$ $I$I$I2&hhţwL,n_Z@GZ$ЭEatQvXVqhL, <9X(;Ǽo3þ$I$I\e`A$I$I$Ic#3.*G؋@Ck~Q;&,N t53sHq p8ڏU]W1w 3h;tokzX~&;,"Q#$I$IҜe`A$I$I$I;̼鼈ؗ@Ck,1O9pNFz &3ܹ%3X5 (ޣA[&P&az`aw uEěj8m$I$I ,H$I$I$I52jjuDDQhh: `rp^Fĕ4VWf涑V<2s py9jE>t5,3$0hǂs[:"b1%5$~$I$Ij?J$I$I$I(7<_U_՝(6Zc0oUuZܭme)԰"0sUfn6ԝSvkXAt2Bf^pjwnDģ6/ø$I$IҎ$I$I$I42s;?;/"Slo 5`i7;Gy[#b ֯׏spY8xaNeXUq蠈x;EWLעn݁Ǖ%| xEJ$I$ $I$I$Iۘ _|r (战`h4V ZfnԆoX;{v aĭWW(BBޟ ,I$I9+jmB$igg`A$I$I$IOS9jEB >wL,)G`ƑVc{=pE Xᄥ=^7fMƹ+$I4/cgd!3 I$I$I$ \VZ{1+prԊh40laAY೭Gb& ?a^ : W$I҄{"3=H+|;,3/q9$I$I$I%@Ck,F:ՙyӬU8EÀP/ 6/̟ ymI$I}(Mѝ'ٙyͬ&I; ꊁI$I$I$""9КmjC!@l4k {IcŤ_/vi|;#SO2s /I$IC1X$I$I$I(D>&Vch=֯dYp&$I$I]3X$I$I$IljXN\K`͡UZ; H$I$N{JI$I$I$Is2s=EgΉRhhz0%8wk8o{D>2sH+$I$IҜcu $I$I$I1b#~]5fqXC}5֧?Ė$I$IvX$I$I$ISmW՝˩4~ Tor4kh5̫GX$I$I$I$I$I$ṶrԊLƁwL,n]Zq3V2ڑV+I$IF$I$I$I$i"bW;5Lk]Q+"n!yH$I$IIX$I$I$I7VD,sa(ەVD\GPEᦑV+I$I4f ,H$I$I$I7/G؃@Ck6zhhg榑V+I$I0 I$I$I$I;̼]9jE4Z1w9tRD9԰ X[FZ$I$I,3 I$I$I$Ic,3~t^DK}j8X0z~k9WQhhdі+I$I4$I$I$I$I?Nem`nDEQ 10jlI1^ci:"6l(@TA`f\Ϲ_ϳ{s{EfK}e<lio{Dua&3bI$IEfq5@Zۻlj'#FZV3s`˕$IҐoqnסU!I2 I$I$I$I0dMM>2|&,-}Zk44ښ>Њ%I$I0x!I$I$I$IҘn,/Ez'Պ 4#p@$I$i  $I$I$I$2sՊ]hhh]Y[jE-44eV$i6P׶a"IsI$I$I$I4]jE44-dbݪSDuauC$i|xr͹ Y$u$I$I$I$IXzV"b/ZYػll'#b-C +) [[$IҝMu`CEQנY "oayՀˑ$I$I$I$kwjh br`]ՙ}TEJ  8535$i1X$I$I$I$%R ?0DeNmjZV7^$IR"(`Efnu-4XP[ ,H$I$I$I4]Ḍ-m)0oDeN-* +(&%Y$I$I$I$I$I*3SL_ \X/"˨44>@ Yo!ppjE3T( 7ZI$I ,H$I$I$I$ Xfn+[XDl 44{ 8l"b- e[X$I$M$ $I$I$I$Ic"37הVDDn S1z'Պjرa@$IY$I$I$I$I,WVDL@C:z'.Qeihhs(I$I$I$I$I$I҄ۀV+"vuq|A;!v/[u!,wݐ$IY$I$I$I$I-[ؓ@`A;!*=[ɈXK}Ve+I$I3 I$I$I$Id%E޴44\8z'@-mhjXmX$I0 I$I$I$Iu:u}"1.ḥxۢ߶XC}nhŒ$Iw #"oayՀˑ$I$I$I$IsDD>hQLW44vqXkA$IR+瀈x>ZOZI$I$I$ITwXUE|`C Rz[ܥllFx*аX7 VI$Icpxif^8bDk2Q#I$I$I$If\_Zv CV+"ngPEa@$I4t"/"^/xqBuF[ `w)[+#b%C +2*IN-<*$Ig`a $I$I$I$IRƎI"b@C# CV+"ngP23oh$IRpǰ'"~ 3:X [F]$I$I$I$I#"QS{ t- رa*> kN_/f=4ܴxϨⳆ$I$I$I$I \]Z ;GVD `æV+IY栈8?ZJ̟Ix'7)37z]D <Cxav^SwdW\4zĦ_wI$I$I4  ˨j@7"8wBIs;,Hp9(3WFķF] paI k:v( g io[4RG0/FQ$I$I$Il"bwf5,dbϲݣUXKP `Ufnh$I: sr#|HR".C3-Cf88m$I$I$IX\ڪ_DI}j8Xe;E446r%I/殯P ̃8uN{Ֆr $I$I$IReoZ}44 X8z'@PpGZVk2s@+4ζ]I N2sSD873%ID}XZ_bEI$I$I$IsEf,>h\l7" _~[#b5ohŒFgCj]7B$i30-g#$I^D %+2!$I$I$I$B9!~M~^/"Q b-*[+["bj*԰"3 VIw6Ձ- I ,m7_}%I$8gY"$I$I$I)&ʯl/"#Pb%O]V+"n!Ê̼eJ{LBLv̕MәyȪ9iRfn}%iN̿u8vaI$I$I$ieVՊE2Zdb'లՊۘ!@lpEċ F]LG#tq~Ic`A ,,=%I4PqW!I$I$I$i,efՊ^ 44wB YZq+- e[ Zi3?) qM=p$9.38^|w$iRYqְ$I$׺ IDATI$I,3o,[ؙցFuNvV"f5Pp@̌wR,h·G[$i63 (҃ ,|nI$j[^$I$I$I9\rf="vuqnA;!(ѭ:E:ZرcÖV3?VDxkf*$Z9_C+IG]$I$I$I$5[ߖVDA}ajaXfv}2"nua:3\Ni:@96C/P4+XySDC27C$ ?Me抾  ,n~2]p"뀯ߞDO@_|nfnbEMVt-"*3su D8i`)ߡx.lP PqϲH*֕\|痞WE/N=3T]</\ Uf^= ܏k`w`{9F%eT^>;lŽ{ߛ6P;q+I$I$I$[[(ޫU_*{{!( ,ӢXC}'PX o"g3sk$BaMq"pnיy#I}SLmvrfMNMNd5gr)/"|x$ͮ> mDX`m j:.3g7)u j3x1 .J]Gs3:Ggg4~gf{7< ×ffmn\_ <3_2  5yz;$I$I$IEDRhhq~ua{NTE"tmtx~,(ILॆS=ɟ[ p|I NǀAD x;CuJQ ̪ED8/]<xjDx$6P<1^ŏ?;(v^WE*"|[;kƸp*J8 xph㌥2&L!#//Ef4$I$I$Iԥ~E:p`g`Sb~ DD#P2skUgF{#%I̭`+=3oRD,X1] P)&ywEn?HqVjO+> @D< {0ZhCTh (I$I$I1)vX\T/"SYhh}ij!ppjEީa%EV+(D$?>\y۠$.4'l`[tgJuX&` b!ԯ҈Z7:p3sZ x )"J3.- ?vT]-[XHEınJ; ԅ@ |+"56-KylOp>WdWQDD,>EuX!u}>6wzQ("Q )Ujy7v|G49"T.تt-cxw._'$I$I$ImeU.(@C# )v?U `íue檈b.G'>6"|σ$Of^ sïN?e杶ozb#N&G=(U8y طM4]x"1>x=Cgnt쟁k1ψ ,a>?on!cI[_~[8MD;cHc3Pc "d|ƕ)VovrfrZU7 <33W0^QyV5}xXũϛ)9GgGvcf.mq~ "9 ҰxJf7P=~ p\f^A=?d>?d{c"}VxZf~{ShXq:Gfmr LJ2vrn2VN^7_%D/xhfp<+3Fh:|m'm$࿛_Gy/he \c&ye;5I$I$I$I툈%44nqB@Cxf> G "GZ@~%I;,٧)Vqy37u3v^; UN Y$ϢͯYE2w@YB}z$I$I$IUxEj臨 44΃wBQ E:ZVʹ}kwus&Nْ$3㰗e8$甭_>ѝZ]qGwpߗTxN;a)\ |{j};a&/taqeՍP||K;a&Ohb6=o) @me^ढ़JW>3cM4L/Nt_fOi$I$I$IR{2spYj 44B; wZ-@C̭-gWI_xED'̼KƐUYN X 2=wDW0"Yq3][svqm_E^3+N Nj_AucEbx="^ICGW V1N=^**b&vt Ņ3ۇ$I$I$I+w_\ڪ_x@CEwoݢ@Cc].|׳̼#"V `(qD,ޑA4F ,ʗa$ F]^C`0}(&4?K*ۺ03ψ+#N=YX atT ӟ'FNv胳Xx@=pvVqzo}U S= $I$I$I2&&DDP'\hhe87q&vEjZV7df3B"EI2\#I}@dmq:pR^$I]ABB2sSD\q/0'B{4O'"{{8iL,.qN\UqՊ\/]Wq찈xbfø =^;uʑ$I$I$If}˺~1XJ}cxZ4Vd{^Ӎsgsߖϱv!À{_zW a Iw7q^^?UL,uU+B2s}D\iR@DW;Lsz{B̏{[$I$I$IX]E| V=-.[;( 7t`fOgo̟0k'Fę)Iꌁ9kcF+OKlQ1E7VkX]*NZc:plűU: `;/SݗbWfPe]VDHp4J oO{%3E4 4wkY}63W8R5{? ,H$I$I$I)^kL_D,Qhh`LVDl4O?,< xRD|"8MO3*IǓS0W2~$Ijۺ\ql9~i\҇1R~3sCD\tj^8m0}8gvkOj5x&QwGĵ7?Iߏ21CE_YXPmD\+(&I憺ɺ0>ыI*1 TB8yZD@=ˎ,KUaٷHUX$I$I$If̼}jE.44.W<ZD\jffEh1Fs>3,§'|^Iҝխ0coUW840ԇn~GM߶Sl^4~oGQe2m .$I$I$IPV+"vuxX2z_~oKƀ|"I_u+\F5UW8ugc8Eă\zy[gED[G(VxIf*A>G$I$I$I\/"ul\px;pZfnqM) ,\gOಋ3׃I4&ڇGXxڣ{[(v h6qgz^_ql-̼>Wc\95^zEq-.|$"~ =e[4$I$I$I 7S̯M~F[,d9"I{ Iڱ IsOd݃ak6 c]+NͺBfnLhnQ5NR.]Pufو#'ܥ⒅{4tű^_ٳzf($I$I$IPfXWu}.Rhh.y|>Ҋ$i3vE>m 6r$Ic_4?8DZd消8Ա}Wkc8E^TZ=Ng6롔>-3/'/>G}l8[U28ꏭ֮$I$I$IO_Ḍ-m)#km#H( QfnFodA$I/5".t}y(\0q-7]}#bafnan*õc+37Tty*P=Տ@P0F^Ɣ$I$I$Iʲ]T/"P k霚]KU 3g/x/.T43Wv8ܡc/37TsK[qةR=2|ұ؝J>I$I$I$I{5:z]9 X#K L'|>3J%qMj7w3A8" 3/z׃FD,8.̼ 8ԟFı]Z'8Pqnv(y[RUuc9ǖcNI]^`u5$I$I$I4>":8 x{ÇnǙy˓:q*OImT !?tB |"N`kuQ8zF8xuũM'fc'uRCM@4Ρ]*N];åW*2B=curADܝ~ʒ$I$I$IQІuf3?3p2p5C_bI ,my-pN3a#IK'VD$"^`wWh88+"vK||Y$I$I$I$ZhG]G[(>/of>53?Ṷ|yf#3]$IC`hY<]$c#o02s\o`Ws{"b߀M9|+З{#*N8,"Nƈ({Txw?jgz(#⹙yyT`ߊӗohM5)M߈g[԰?^9S#G[\~>3i∻2se|xiFĮ{3sKe{wDlWS.S|܅w^xe;7 "LbFę^_ᖋ_^^emGS|?z8ՏmK%$I$I$I$ipDBv#̼cDH:R.% { ?+GU$id消8>ŤfGojsS>N x~ŹE֮}m}d𸲵k< 2x?xZF;{ pL^ۀge\ (J9ÿ%Is\f0313iXA&u/IҘ*'W:xdfo A9v1f=37f2mtq6#2?Nl2͙k8e> | qx^v̟Pŗ !3/tNwf~UI$I$I$IplǼ  `̼wfMf^1(w IDAT=J*"lf>kH0Do-:D끅M?  f=yoρgUtI{_ۧ\{83=O1x?WtF]tߐnOtgͻtDCOvj3?r<"I$I$I$I]E1e;_AKf7 JD x|f~}HfrgQ/q?Ƿ pXFY8)]j`efAJ`ufn%"Gq^BRXO#](Y._(vZ1{޴{6qQI$I$I$I."x_w3&IԵ8xʸL4$I$I$I$I$I틈?&\v~3$If捣C$I$I$I$I$u&".>;vQyfnNuI`h2 I$I$I$I$I,aہ(wP~[_$iX$I$I$I$I$I[^<;vP8/3iUc`A$I$I$I$I$iEπ2s$MQ I$I$I$I$I$I&;,t " 53I$I$I$I$I$IռQ0˼F{ $I$I$I$I$IR!"{IaA$I$I$I$I$I%"N_R̼xI $I$I$I$I$Iq ʰB"bT%Isӂ .L23:"$I$I$I$I$Iԕ*/|iHܵ0ԅ$I$I$I$I$If{80 ,Hи$I$I$I$I$I&IKV$$I$I$I$I$I$I? $I$I$I$I$I$ ,H$I$I$I$I$I3 I$I$I$I$I$I$I$I$I$I$I$I; $I$I$I$I$I$ ,H$I$I$I$I$iܶU!Ib 76njTYm>"I$I$I$I$IΜ,Tch|#݀EkLYH~{DK4:ffJ1COvj/;Xe1d' |!3a- rW_נDf^7Ӂod# +I$I$I$I$`luqf<}*"v΢>73oSHRDl |-2W} I$:8{Jz MX83"ޕWuPD, b{88 3jhEˁÁi'/P<ڧ$I$I$I$I$X:O" &x.p>c$ ؓ6~A>Eay|#b5GV""ޔiDij7 hc}"b^L$I$I$I$I$ϾFuS߁3q]n5$Ij ̬5)5.pEDl^Dges+#3u^$I$I$I$I42`f-i\Ut]?32$Iz)@ EĩXaP$)"~LY ID>*EqS00;$I$I$I$IV;Vbg؁#=l$i<`ar;w6 "^ |Xt0!=me{Ȁ "V>0"N$I$I$I$IWef4~oGQ||oW$I,X"b?!pZD[?lHO8tЛF42`A]а$I$I$I$I$m7 3o*V(c{03;tu'Su3syG/{g6u2%I$I$I$I$I@D,H)B2n/q 𥊮 3sǺT?𕊮e;/9m6p>oWxV)c$Wq’S)ؐN&hX 3A}] lD@[jNY~BfG.V̹FaM!I$I$I$I$ibwxyÕ)&?iE)+@T7Fľm<-({ {pSD|=N֤ѷb؎ P}ȵ6!\+@q+.#sǔ'R jx_?-"6:nԺl8 PV8,zID,b(6{xA`a;"3Oú_n9v*շ1 lu9Eua]+ꊈu0<8* Խ5$I$I$I$I@\źJI'"v~ Xآ54޺o [,| 8+ύ n\Ҹ7&k!#]F koPpcq$q 3Fԋ@J$I$I$I$IxS>]P$$Y]VǁD>O  Os:$"̪"b'U9=,f= sÛgN-hWE&&M)n*Ș ̤8GNk~F> @q,\5i_|+xD)3sF^Yo ༈43mN6hBf^ZgDF߫wPL{oy[˳ˀ 7n-AQ1kSEw,Xf{gS}{<`]d ̜GvDTn,4Ew{KIy{DLq=Xv2~%I$I$I$I$i q,_WeyFu*:4";6*s4/ bk(Q'Q p pffer#U+~("̟x}7B tu׈X8b'e-v8Tݣ=✎J8$ )櫜R&W*h=*qNY'Cy$3?UU,gs۬XxHooVP<"gC^8gleʘffEc1"VI~f^UP}SyǦ]CnPwDGQ|~Z=잙OyDBZ08|Ϯ*V2␪aPxuō3m^sjb23jNPs1,L^'h s ؟QTֱ~[]sܷWj߾H$I$I$I$IbXWՓq3Ufy(œ{l(aۛ"3)"{uEt |:6]3(OcS|n~\ѽ Bnf~ˊ[zqPf~<3b*ߓS>,_8(k]{DĴK|oz8:34؆*f/Lyn{H-^U4Jf ЩWNI$I$I$I$IZpeyQ^c]ѵNEfⶆV/k`{?pK}!=yזEw]L?5GVξg{3ZږHi\ C<҄b?ڨ"\ޯXڨ{OЪruf={,X$I$I$I$I,_fRfCqCܦK=kkNT?Ms: U>saEۦ {qfĈ`ߊʂdfVtmRed##b3Vo1LQD,]ubf>\7Vei]cn5>Gh,X|nj- EQn1^7EW~a5nF}B$I$I$I$IxrOfUzm7gf9qe]DD:.OWtmKwnnh۸u~hzXshovye[eӏ̺yP2_g1U 3O0ZO~SOoM+k, 㺣YIyGD?ʰ2ª<5W;v<34^Y0Zڍ5 <̜c,Lqʼ*KE{̫SԘʪ $I$I$I$I<{M4A)V"83sZmŽX_ Ѵ{?JEğ +:u +  0n1k<ք?yyhzB$I$I$I$I&vSO6~:1_kܝSD,\Lb;;W~ȵѩ->W+ l\,o@ &dž(sl=e_a",I$I$I$I$Ije l{gS9T|^fΩ@DRw=7 \\=ͤxцgFFK)Σ_k9OH R3v$I$I$I$I!M{ω`a}"IDAT1X=dz(uNhxufv1fkݒB,mEbc }p.,0hl-.&)@$]3B$I$I$I$I҂]Z ls<9EIJT'za!"^ ^ѵw aت˚blUW}nwGխ :v 4 i0YjX$I$I$I$IV +_/n~Kk]cc'^Duaͣ٦ذRY("k("b;]3* :ܪ,>߀9 :i,X ܰ I$I$I$I$I |k-_7@eS@U1@ U̩]sݢpEEt`AR%"VΨzx[fEAKߊ:خ,/4̇~jбHfԌ@$I$I$I$I7U񯍈i=kE2sVK9mk+nXmfDJ$} 4 -gR803obF:زYT}~ʊ#D4I<)*'$I$I$I$I$ ^-mt`Dlfr#-m8#b}`5mnb( ,̻#-]c3_Èaug-m^MQ0#'eQè2󖈸xnK"|n hE44,Hx̋$I$I$I$I$i ,~hD6_b?Z?'g}] cӌ֤Z"beNjび[ږ7p "^ [u|""XbRQfV}M[ia kWDt'"2iX ;iƄ- I$I$I$I$IR2QՁ#:]/" lYC9h}r> u^D oh{ADla | Xyv.Φ6E#b[&"b2ֱswdU2/A]+g ΊD[k1X,"1􄊶"b3 f̠EZZ}C$I$I$I$I_hXDXwJc l9x}߈8"2D %I4'"Tg"զk^֏"5<̼ryqf> ڦxXk&")6`z)ߡ5.X/"vf4h ;i2q7jް I$I$I$I$iU#C}X;Yqu!3oC~b"b#CPX"To?l,!̻3*?U/> ,^1Ug̜{?bDbIfVv2 * ^ \AqRb+7^ ,U'6x͖ŹP|m,^?|ޭҸbԜk0"G$I$I$I$I$997"\DuR^嫮[m2sV!'oKjɲmw7o 9x+mEߢ{j?;{2svDl Nq3B Wʛ1ΡHofzyYDҵp<1?-c*ب|]f Ik5ο' aʰ&~"I$I$I$I$I|x=pa*3on`q%3vj`[fu0"~33z]f>RѤ9y7rC< "krf> |uCtW09_kK)o˃'#7@x (f~ 03kҠDfb=n.pc33s~Ӊpd̃{\k5ð C}O!_j.^Us^$I$I$I$I$E"tb럙7"b 9M8E{{;~ǰ&:E]kfK¤ P)rSnLJg,}RzpY {6"X|-Q pSCNPxÂԬ_1`"I$I$I$I$I̜QFY5n 3bg?^g1._3I-nn$R ;iAc5$I$I$I$I$I$  R2&ʚc8$I$I$I$I$I44,H;-I$I$I$I$I$I SsT$I$I$I$I$I`AjXf> |)_S8$I$I$I$I$I4,H5+nDĊa!I$I$I$I$I$ib`A̜ ᴣ#ⴈX1Gf,$I$I$I$I$I&. >p>JBUD;p̠c$I$I$I$I$I4yX ;;p}D"bjDW7瞒$I$I$I$I$I ,H};u8u*-EQfS1Ez |x9M!I$I$I$I$I$I ;i_GyQq py p/0+3jby;jznއ$I$I$I$I$I$u‚i28 dvwDۜ ,,,_[$I$I$I$I$I‚i@2x 8XeV,_$I$I$I$I$I4nLv҂$3vr($I$I$I$I$IW,H/~=X$I$I$I$I$I$_,X 3o><cg ]z_Շ? ,^cܬ>=_P\w m=(Ɛ>RlLmxǀKdu /I$I$I$I$I$IDf;ImDDkk6▆끿xݖs$I$I$I$I$I`U>KIENDB`./oar-2.5.2/docs/schemas/hulot_wakeup_process.png0000644000175000017500000011006511757171206020112 0ustar plbplbPNG  IHDRzbKGD IDATxy|L?DlB6$"%H׾o5R%D)U!VVZӺb+e!iᗹI2ɉx̣sy99#Biٓ'Opi\pqqqȐ:facc-[Cر#ED^'N… ݻw-[[*Fn[n=zCh޼9allzIjldee᯿BLL Ν;x L:gφycKDru|'tW^C޽Qn]ӑ}zj\|0p@lܸ#"_c…̄)&N &YfR#PammիWcȑ՚.Uضm0yddggEX~=>cQ- ]0{l<|VVV#"R!%%#GDDD0~x,_ 6:騻w_89r$6nj>]"+V 00BL2VBELZZƌ#G^zصk &u,"ӧ`oo;w[nRǢZbϞ=0a={ѣ! ]"* 6`ʔ)S֭[I&Ij1!_@߾}EDTcܾ}]vÇѡC:tVVVRǢZ&>>}EBBz-DFF¢Jb? !mۆѣGK+V.]wψ {oFqaKjd{!22U=*뙈tNZZF",Y.(AAA1b222兂#IJ1cۊ]$%[[[ϟٳt{,vHcHIIA~gIHL&͛ѢE DEE!$$DHDDڼy39kkkvS "Upԯ_֭É'l[4777!&&?B5֙3gеkW4hJ={PiӦ gd%GvH#+WDQQXRֹsg1=¦MCD$5k -- ,tFB111xF<{ K.Ejjj#"Zv“'OU-.QEu]vE||<~wb:z(jdjWj/]}]45`#GT.3,YM4jժGDTeoO.vP_V#SNv*q"u YjgϰvZ|x1{4hPe$"ҶT;wرq4hԩCuZ%"r9RSSaeekkk['e} & fffAnn:2L姢pwwe[/ 1n8) kkk| ꫯT,/uٸq#a``mʕ+-m;Su //| akk+W*_<%xݒJ.X?FqIҥFD'N=zhaue2֬Yƍ+ǩ  ԫW8s ЬY3yڵkj>~VVVHOOWc ==]m5;g[XXm۶HOO˗._,vHd͛ >>qqq{.ϟ"!T /FFFN:k}u:u ׮]Cbb"RRR`EhsU/Z7n˗/MCrI={:t(|||4[U:v0|r$&&ի^EkРLLLLEnH0,::;HAmpQTT@stxyy8z(""" >\󔥥%>Wi&xyyBm5?gkp%LD*?~?ѫW/?QOvJ>ɓ'oУGEaɝ1g|ppp(sSn||<\]]==z޽{gggoh޼9 %%[Frr2qƈ@f^\Z.UY4Y_&!-- l!??_mCUꎝN\߿_]QFviԝԝ*U!Zj%֭['zU<|  bKD*z:u\kzҌm۶Ilذ̓fvv7n055bܸq";;[ѾtRabbrۻw͛7UV_8u+Vkkkadd$ƌ#rrr텅bIԭ[W+.Bϟӧ KKKaaa!V^rTeyYi;ѩ˫j;v999[ kkk_V*OI׮]+m,z%mڴi譪bۥ;'TUU!6o,oV<|Xk򧇈H^zxqgfE[:u`Ѹqoߎ͛O?DDjիWSL۷\rÆ ;#.صkOZx)=7xW|q{Mtԩ^cKDjҥ zi.?,jPdggSqPMHD LG1uT%S*z%c;rVY42sLg}M.cժUӃqH",vH#F۶mqu|WR!*ٳ///4o\8DDRGnn2f ڴi#::\WWdd$_)VMP3T50`V?).iD&aÆ dX`.\ u$Wر۶mo`iM^^֯_WWWe%ܡCVy3{l+\"^]nY7V>77&L\~BCCzgΜAXX5km׮]8G7YǕ+Wm 5鉌 'TfФ]esrr0f_~e% ; -[(-Sk^ӱ%r333ñcЧO$''GXp!rrrFHZZƏQF!''PNi٣qkbbj]oT… FQQ`޼yxwbJ}?~HOOG"""OoPAvM?>>} ׮]Ù3g4Xooo,^8uΟ?@k^ " *((/puuaaaRG#!+ѰaC@[JH!DPP B5Du'ڷo//jK"))Ii^FD\\b:66V+#tVVV5QDLLb:99YبVv5͠Iۋ;w(KW־qbݺu޽{*ש wV;| 8KDVN,XQQQ޽;n߾c&M޽{qS@Q ۷cԨQ̙3cxxx֭[;v1j[[[@rrrlQFJ)))pqqQL %%Ei (T ʝҲ>榸*{y*AvMMNNbٹY_o>;v mڴAӦMqСJگV{#ZH|8r6n܈7 akk SSS؂4 $'';v,f͚VZIqssáCAUmllWWW@BB=TNViiiڊWn666,_}8x EhU4kL%"֭uT߿x"ݻDkJ__o&}C Q|'(I???|ӧS077Gll,7o:7|'''ܺu ˖-?q[ƪU0k,ϯ"3g͛sĈ?>\]]!PIk^(.i5|}} |,'|}}/ԈOk000:k>L&!D=wٲe:ubdwXtV1k,kU* +V@Ϟ=4o+v7oތcɒ%ܹswފB-Y'N#LMM1}t믕sȐ!6lܹ͛cǎJOO˗aaamjo鈈$ӲeKܺu #GT:)88@PPq=ѵkW>}NB.]e$ RG)m۶aر3f ´#G0p@TΝ;[nx[III'"""]0zhw}'qJ3f@zz:1sL|RGfKD$E)MsaĈ077ݻK35k7|/@q5ͩSpi4k |gKD$s?⻨2 [l.U LϟcΜ9Rǡ*2}trddd`Ν7 !|$9s& 00JqDD(}{f^^GwH+ѠAٳG:+֯_K.E5jTl.Q5+533SGwH+,--~'xĉ'::śL&롯_5?bN>?.iͧ~AA.cСxԑp} 2yyycKDTJΘ1Cm%""d +.^?RǢZ,99ѵkWWXUJm-Z.i%'OGHNN:Bԩ_z Tܨ%"&Fuqtpuu_m'N:";wDv퐐:Yo.Q5yb ,\Qb-Z?֯_kkjNHDDΝC޽^zaܸqRG#vm 4FBff&Ǝ'NAղ}DDs)sTʕ+1~xԭ[Qm`eepY&&&غu+4iHtȥK0n8lvލ[ȨrT=Ӄ<<6mRi&xyy¢ܯѠATy/u_7MyDGG:zkvqDFF"==^^^߿?=cL:III(**ӧO}YneKURKqGvvb:--zcKDDjٲ%|}}1m4bT1HMM;wPPPk׮)ʈ#pMC4X^e?CpvvVߤIk?Ϟ=CHHLLLжm[E[y_ԯ_GbbJ۷UV\c֭j*dgg#)) UjDDDiӦ!99Yi޲e`ccWWWQFXt}ɒ%z-t]it={0tP 2Æ 1Ν;vTxǬYЮ]2һ~zL<ԶI&iN~~~>}:fϞ4w}>@NTn}qQoe˥8n޼ ЩS'ѣ3Q 彆f ņ 0i$Q%#00 :|OU#G`%""""""bt]""""""9,vH%""""""bt]""""""9,vH%""""""/u""&OUVI*h:Q' uk>:bKDTC$$$H*ɓ'RG "6,|ubHB_|f͚%u @ٳGDL>} <<26QYr%ٳg.XIaÆhذ1\\\@Z1uTWTTPY_5fΜ ==އ^o|QڰaBBBS20a`ff*'Xr 5鉌 KƍC^^VVVW_}wxxx" ԫW8s ЬY3m۶vy8;;c˖-*=<<6m4h_ee2?~ ++++=~666HOO1 &DDDDTͱqF=.3o<>>>ŋSN*}Æ ɓ'c*G}M6)ߴi`aaQc@U*$ @AAARG"((HRGJs"00ԶF8tllWLۋx;99trrQdqqqADll+'&NX=RLgee:O___<7֭+sݹsGXYY!999J$$$(e|[B/QFra@pd$tR;v gΜy-%%E{...HIIQL'''I&J%%%% 2 2 HMMUdM4Arr=|PhР⹑Q 4γo>;v mڴAӦMqС2B.2  -spssCVo>޽{ѱcG8::j%""""I`y廜666HLLTL'$$Z1mkk^9B(EEE*㯿٩ ?QT<۷!˱n:KKŶmېWBB”[\???]vZ_1 &DDDD$-[ӦMS???"55ӧO}Ĉ\.Gjj*ܹ\vMi'1c xyyiߘ1c0m4ܿO<3>~xR17oD~~>J%С7iڵÏ?2G޽3m۶8#QUcKDDDD6m%l2hԨ.]h_d ,,,䄷z ݻwWZ? ;wFϞ=add/// :TeΝ;hР [p!Zh֭[Eq)o1cc2"2d ccc̝;;v(cɥM4I퍪ӧOy+r I&" Hj`" AAARǡ w 8#DDDDDD{XaKDDDDDD:.DDDDDDsXї:JHH5jvahҤ u]""""""-g};v`ƌ6lZ u/c&"""""Ңݻww)-&}ЦMOՔ`KDDDDD%[nȑ#:YYYΝ;0YbH .\___!ʽ~)\Rr9-[roSױ%"""""$! _-+1g4ip)XUqJsiDFF^]r%1x`m۶5,v*i߾}Zk+Ƚt8~[k,vd2z͛75^H!ܹsaaaKKKV;iDDZȮ"wvu gTqAS[#G˗%NRSSsNl۶MF'mڴرckkri&:u 7n;|ZNDzzr9V\+Wx᩹9쐔mODEEx"˧\a`` @+++ahh(<<<ijg|իW !BCC[hӦ|r=*Zj%֭+͛ -ZQ^=ѷo_R(+*⧟~bJ?~\=ZA{<} !/"""SN[ " @(TC=Q{wCCC"mypdӧO7ߠM6˗#** 033ÿ/̝;6l2B^pѰR;y$Ξ= sss|WŋK]7n/~L:u7|ϟc…ǎ;*ee(kmۆ={uݻabbʲ={DϞ={"$$'NѡC23ݸqڵSLQ^""lmmq])S`̘1o_C.Zn &ʶt~nKىD!NNN"&&FlrrQZT ^iOKKSLgee }}237nX[NܻwOGv~Pzٳj-ͩSCJBQN mSq^DѧOH;v mڴAӦMq!oabbLcccܹ3;mDDTd26m;v`Ȁ߿!QTT}}}dgg+Ҵ}8x r9֭[ @}$%%O>U{ٲ(O{HJJĉg888`̘1PZ6""cƌ݋ɓ'#)) 6lPy 3j .]RLGEEUV*!"M<<<`nn^~4hÇڦ-]""6ܹ3v___Ν;(((kXJnVBvv6#F͛7!(pikJaZP,qaĠuWZf̙hӦ p!|GWھ#7os2DDDDDDZ`c֬Ypuu}ef͚aΜ9ҥKQN 6}"GGG\+WӧO/nȟoz,v/Sft]""""""9,vH%""""""bt]""""""9,vH%""""""bt]""""""9,vHKj"<}ϗ:2 066qJbnܸcǎɓy&QXX(u,Ѽyst={DNNjHz,vjLa֭VjӃLLLPn]RM#'O$$%%ĉXx1퍉'ADDDT%rrrrJ]?j {FѲeK4iuԑ8)dIIIӧgbҥ/ŋQFR$""ZךB+Zl ٳg=z4.]ׯcl777@!uT"""eX"@_[nz*oߎw}Wx|rܹsƍCff&M#==]xDDDT%%+V@__W-[JtPÆ w_>]RG#""Z.Q-PXX?{N83f@&It\޽-[֭rԱ&11p=XSLaeeHtYHT4nOFqm 4yyyRǪcǎlݺ.\8bHܹ~-p![RGZG+.\#Uʱc0tPtNNÂaKÒ1e~:Hj3+++022† !u ).t1i$O>eKDDT%agӧO1rH5J8Dpwwʕ+SNEAAĉBwΝ;Y0,vtT\\vsss^Z8D ?ڵK8+-[:uX0,vtԪUPTTsZ8Jj]kzםbtwŊBHH=Un1DDD5 ]"]v&Mz,v튎;͛8}qTҤ-Ƃ`Kߏ, 6 fff*就y`Jirssamm \^Fd2+*~Ǝ رcGn>>ƍemۦx~ 7U_f 7n =W$FEEk׮"00022'2224O]&M֭[~~~>aee |WJ^V<K.n{pwwe˖R4666hٲ%RRRpM׫)t%""]"s]<~h ** gϢw8{, )) ' P'OٳgCG6|r$&&ի8qҺÃ8ܽ{NܿӦMSZ۷}S . ::EEEJ>~a>}:`刊Btt4RRR`hhsj_1U}]aaaسgĶm7hEƍ|2q}#ŋ###N5^6m$RF[/p@۷\M>]YF!D.]ĹsDNB,_\L>]l?DZZb:++K+E||b:66ViF8v{{{!B!ؼyB?X m022ĤIDQQV-((033ϟJT " @(TC=A$6lذ\;۶mϑ?F^^mۦ4%GPPPNNNF&M...J릤(sqqAJJ G?;w֭[?=zP'ή5j}Zz5ƌ+OJJr_[[[ňӇ F`L2666W^P}رchӦ 6mCre(m辌#DDDՏ.)Դ\_N::t耐ԯ__^^HLLTL|(JKHHP. \\\gb(((дiSM+ IDATݾUO?wp}º˕ˢWfܾ}[u쐐PvNKKtXn%011dffk=mB ^""b5b 5 111U1c>CݻwǢET3bC.#55~~~JCjj*RSS1}txzz* 5 0rHL:UjYAAA;wPPPk׮i_IѣsN :㏑njiӦx f̘hkݺ5VZl$%%W)9bcc5 87oD~~>J|<-ƂUDT.Nzzzbȑ֭[e.a +55Uԭ[WDQn]|ϳ伜- /ڳŸqㄩ055ƍي˗/ }}}E/\2CѢE _!(,,˗/{XT>p8j${Քyh{ RA[ YQ7Pifeff%KMMZn``ET#_֏A\ֳֳg|]' D߾}ŶmیRO_'O6lYw?Qۯs΢SNwߕ233EhhP(" @Yxb3 " ӧؿ7uV@DGG^jѭ[7@ :Tw 6m>#l߾ݒqIvvvBbƍgh4\xɶR|gرc***`gg0,Xx_'S{?#˗/9Qתb777?#222BTTTHσ$nnnpqqA@@zc;IO[vQDyF;#uƍCxx8|M 8PZ֮k]FO>[lAVV%2Uii)/ߏUV#GԩS1~x.Ygl[ϖn Br.v l2|Gґ۞={bĈ???8;;bLuu瑞Ç8pΝxk6[WW%jիի^jm ^DDDfAZVXB EƊ_&7o~A<3^=z}UslݺUzoĚ5kDUU>}@+iSjjjĬYw-<<>4"""Bgdh4T*9s2.)n՚#6mBjjM?2='Nhqc.4W={V _Y':~zT*1bQVVf㨖5%ݺuMi:;;[IDII4]^^|82331x`Dr/?Ï?׮];ɺw?dNbg$...i Bqq2]t^;;;79Oնjt 6 JJJtܹsG:hNnb#>jqqqJ*bR6d.lٳѿ9s_~qck׮iZ|GɓӤ,tg/^֭[Eɝ 9}gn  %%]S(6mZ-/_.w|ȑ#"wfeKDDd[ח^]w%w&"2j(<8{,9"w+6n܈3gHr $%%x fJBv !e_5!d^z eK.3gZ-^~eB=Z3eKDDd[333R=z;M=h Ɖ'4jv܉>Jk֬;I,tl:UhDs*0m4111YN}o߾pttD`` >C͛HJJׯZ~=P>}Zj?`ŊFo`={V޼yٳcܸqpssRDXXju}fdd[nXzQcm/_ooo^@uu>ЩS'N;єJ% 8::ƍDw'N`B`ʕٳܑLTh"DDD6ɓ2͛K.!77999x"G}!xhX`_GRҥKL :>|iii(--EDDbbbdh4T*9s2z =G`̙R!z<pAV!!!={ ,, ֭CllcmG"++ * x7[ݧ%<_~E$8p {=!+?;݁N>pTTT;O.wk\,tl}'3gpm굯ɑ޾޽Xf?nk ϟN/_˅BDnnNm]vH!˖-bƍB!&N(RSS\\xzz]v?Icm1߫ݺuۧ\VZ%XuV@DGG,^Xvvvuuu&B۷Ou]xꩧDmmܑBՊ/1bH!DJJ B6?D/((t*ŵ!6jbIAAA(..6;wǀp=`R[aa!z- si;w^;;;CJEEE:J7jh 9[n?pq9aÆvvvpqqAIIN_.L"4vM=p=z@QQI[/ҥK2'i7x+W̙3< ._,s*jjjjcڵkۡP(f:tc=ֳyDȶؗ\\\db rG3~ """b_3;xo +22RFUV#66QQQRnoҤI8wjkk!) OA"++KC&Mxh4jƞx oÌ3uJJ*[n8r6mڄV՘U5 f͚h}gecǎӧ1~xॗ^B޽vfoVFdJl۶ <FsGzz:XUs6n܈_|-W_5%K0c n&Lŋ9s`qFK?Sxg|gR[bb"z-5 .]BHH͛gX-Z_~pssÜ9sw^ѣ1o<ٳ1zhiM6!>>>,0g|嗷ÇcȑjHNNnX^ :}ō70a,\Ш}G-׭[7ر… ?b̙ߏGy}A^'''#EYYT*Ο?cǎ!==]:#$$:^DDDt2&MDЖ~~'@ :l}ZUdff3f///7rrrQQQb޽͛L[Kbb RRRB67#3A$6yd+44k֬UGٳAYY*++H6ѣzCbpvv;6z3YF; QB*hcbFȌ#G߿=V==Lb Νk\""""""b &w #DDDDD$'fPUUc1حE||<<==ѩS';R[UUMwww#&&UUUR>cǎ۷/;O>z#BCCժu1n8AT",, jZ#00PZR{uu5y+Vhv^V\ݻ޾o14Cߘ e247o")) ^^^pvvFTT_n>inJL2Eʽ|ѷ_}2؝?>Ξ=SNAR@j7o.]\ŋHNNY8rJKK1c`޽8x ^ & &&U넇#..j A||NFZZJKK~rr24 T*Μ9C5?BH'N@ff&ט14oL2ҥKL Fm̾'99eeepp1 _0VnDvvvm"''G~~~4QRR"M79OPj˅N/_nv}???3}q1CSP&CcϟCcd(1w:Zr6f_STTi:00e헖j(--mUDDD?;Әv&ۼR|xyy<] M83f@aa!PVVfc|||t1̱Z;S3s׮]QPP ,hlsR(/_|4}…Vg2dРArY6l`DDDd=555xgzBDar;e̜9z*f͚%EFFJיj"**ʬQYY R R Je5&MB||<4 j5LZc051O>111˃VEVV1yxx ;;չ~(,,u֏ٳQRR5B@NbDDDdw}'۹s'Μ9#c*;/{߿?/-YFpp0|}}xb6ƦM1C 1iESN@~0|p7~hLdh̉:t(FgggDGG#""͙34x\7nķ~ www 2#GYѢEpuu?A1PDDD$'~ >( ^"+íY"[qy3S[رc6l~Qۓe˖!%%r!l2$%%!11)))r!+k\8pwjjj0~x޽w}783.5kJKKQTTٳg駟;a+t_}QRR#DVbڼ@ ={.\(w$"""+t%>Bغu+\]]DDDDwc z,x.Q RcKd=,v-Нfں>^AdN-)t%z;5֚B ^"˻c]FoooTWWoت*xyyAn""uϟdz>.]cǎxW_˦'n=Duxᇑ*ͻr RSSQRR"o0x`xzz !-ň#裏"''׮]ڵkm6̜n=Ds0uT|tzz:ꐞ.O0uT#ngg#00 ӧjʕ+ѽ{wm@nݰzj͛7///8;;#** ׯ_7?""?>+HGvz!#Ƽ} Bǎѷo_;v | z%'geej|7nnnP* Z߁b߁ۇ}1/7׏wSAtD+|P IDAT[/e;88jkkebycǎEFF -- ?8_1v&?|0PZZHmh4PT8s :yp%"''/^Drr2`8z(3gΔ>x9rO<މ':{AXX֭[XҥKL F^UTTeNBv1;_|!BBBB}/^,\]][~ :Ә>N]֗4]mmÕ+WPXX(˩cDnǎ8q"&L۷lDRR-[$&&leː=ŀl͛7?̙3mJMM ƏݻwGAEd Xj6oތsʝ| fNDDd[o%wjwy& ])͟9PVVI&_B;MSiӰzjZ ?бcGsٌӧOcǘ1cCDD6lXl1n޼o'OD||<>8ZSS#][ߩS'l۶.??7nvڅwyܹlF||<={6^  AA0gG}൅eU/ϟ:`ɒ%Dd3>SǴiCDDDfKwAfK:R;`L>DMMd3gV^ '''5BBN,[ !!! /Т?~<]ɓ'駟;Y/ ]")v]]]cc֭5k ^\v aaaB~d GB|ׯk8::b՘2e jkkFdu.]#<'N{ػw/\\\EDDD2fBȼn+v`ȑػw/\]]e <*وGhh(Μ9޽{㧟~BnEDDD2FB,v`ԨQ駟гgO#bժUؽ{7:w,w<"""1(xYYb.lذ=z4T*f̘]b̘1xwpaY#/Qܼyyyy0o< 4AAAx뭷P]]?;.(s,t,Ka=ߏ'ObÆ ؾ};ۇ},{{54TWW_~~~~2$#""?~(|A%""iMB:L.v999!,, aaa:PWW`DұcG8;;ڙ,tns%M2eKd]i ]"cKDDDDJ ^D`KDDDDdM ]"]"""";Ukx|A`K$%""""2GxSN,tdbctBH,v,`r >SݑXgٳO9ѝ."//O?|aP,vHSvN<w޽{w^ ,@DD1H,v FW^y;w"11O=:v(s:"qpp@HHBBBsAbϞ=x뭷pq<33f 6l???nDDDm̱c;w L8. ~i?mеkWݻ8pȊX!~-{1GEVVN,C`̘1زeܱJX?~<0sLܱlZ.]k.Ŕ)SuVc%""jN|XHDd!,vl… QWW7|r!jdo)s"DDD6ܹsسgu-er[NmQm!)MO8~qX٨7B88::G ,K`sڵk!v1w\eNCDbiZ|P(.wi4x{{Zg~DDtUUhװlt0ގe'N3kTTŤ. 矡VK8F?Tiޕ+Wi7|SFM1777q%""A?#`Ĉ2'1ԩS駟J騫Czz4O>ԩSq Jaaa:>? ߆GF%GGGR{ee%L`imjUUU6mTUU鬳rJtjny&gggDEEM6f?Guu5yi?XBSز#G:$s"27DDD600`IL7vXdddHEkZZq 믿bرG\\j5?]YaÇ#-- @LLԖ2\pYYY8vX4yҥKENN.^dN8Lՙ4}.] dffNNNHHHh~04dh4T*9sB%ylUgΜ9  ""ILLDJJׯ ~Wm bʕB! &Ő!CB,]T6^yy~TicKy˗uS(Ҵ˓sss췹}}}ENN4-t/,,l?}З; @?^.**z?C: sǖ]xQݻw7{{رc7#DDD6.]ȜeN͛7xqTWWc͘:u*[86 ε-ѹsg鵳3Z4]TT@...FPP4be|}}ML|܅ݻtަghEEEѣN{C-cnhDDD6"s?O>C=x衇vZ(J}ĉ1c QWW2a4}WTt~~Un ֵkWH? !%"""xp5_j|iii(--EDDbbbL>Znj3*֭[wΝ;cذa 6 :::u29ɓ`\~GpAV!!!׻NkwDDf!EbqUmM}999tvvGnn4#u&MKEEEۨL˗rP(L>L͗'<==EeeBJ)BV!Dmm {rt]YFMِrwS3Ro{رc/Gv,$MX:=zy]TT$Mw)>>>ܹZu||4 `gg'''h4|z3}Ν;_} عs'{ 0svޭw6l\]]agg];""s`KDDd%BZ7T*4///4\… v튂Y!Mɔ|UUUؼy3T*O ^իWV^9s(<HMMF5k=ĉ1c eeem{GDwDDDVPRR{¦-@vvμHNZXDEE'>>fBtt6}t //ZYYY3x 1%߶mC!00Pg~=0p@|zڵkXv-\]]ڢ0i$;wBnj?TVVBTBTBRV75wDDaKDDd[lAvv6bcc9s`:w]d `bz:t(@t .1tP5 ΈFDDc)֭[W_}ɶW^y[Gwccc1wz < \\\>Ljkj?lڴ pqq#0dZǚ;"؉~N$%%aٲeHIIAbbeؽ{7,XL"&&JRgyիpwwro}|I;{1[Dd%"";;;7'OĮ]H/QG~~۷o{ٳ1w\LKD͹p֟) Dd~,vZiƍظqcpttDaa!b Ԙ)krG "3bKDDB;wylKիq4/,,yaoo|||'T~ŋuVhZ888`ʔ)x7hT-]?3[ܺV5 mS{a7#jxdȊ*r_z%ȵrZ V&.[O "bKDDdyyyXhEnÆ CppշݞNnS"]"""+HKK͛e/r޽m]"""+Fbb"rrrqFY ]8y$~ayvvvX~=P>}Zj´iwww<?<\\\+VMMIDAT$%% ΈǸqR0jLom~k+W{͒x{}i7vJL2E'˗/o2! ٷo GGG?Y>%"2]"""+P(HIIȭWTT]6HKKCii)"""#͛7.]Bnn.rrrpE$''Kh4PT8s :ҥKL zs#..j A||lm&C7TB[C)'N@ff&.א1.2\pYYY8v3yd,Xׯ_ѣGqqS"""""9zEW*Fgqei\( iWHO: ?jKEEEۤm6k~kB6g{gLї^Ś5kwKɃ"""A~JPP<=zHPy С@ Ͷ;88H;88@ֿHHHSP^^~:Lo}hhӇyloρ}b6,ZpwwǪUls㣇'LDDt8ptR|xyyI>>>: _@׮]QPP *0xĉ1c eee:m6ahCPB|94}…V 55k֬{5X݁ñ}v։UՈETT>i$C@V#..Ngӧ#&&yyyjY)P*P*PTALo&7co6***PXXӧ9EEEa())AII5-5i$;wBmoj.gEZZ~7Yd `bREЩS'_~>|:t(FgggDGG|ѦM1C i7f2~csulhcƍo!C`ȑ&oN-+ѧO 4󩧞3<$$$>ښڧDD5DDD6ĉo-?Ο?1c ??_(-kv'%""C%%%Х5kJKKQTTٳg駟;mXIѫW/Xp![wNnx3 iD7GvaKDDDM⩧'z ˖-;DDD&xl]b}]TVVO"jXiیos~o{ꅇ~v2[D6%"""v%22r "%""j1n8AT",, jZjkLSoHH+wtLD D6c3n 2&&jF%/}EmBy1椑 ^P(R(hmϫ{9O_9(py$%%aʔ)HKKl6lٲ9s&N8!())ATTOSN9j믡RRPXX~򌬒5_KJ}CCCػw/j5BBBⲯRqdF }+B@ii)4 BCCQPP&yW̷nBjj*L$\tɭ'cׇ|gtuĿ93zɅ]"""4;׬Y;vHIIAIIɘ6娨@WWQXX߿& F<_%ģGP]] шGm߾}hkkC}}=z=~Y!9vގ`޽e{HNJt: vh4'!!AO>}{{{ETTc0cJұ+uuuBkB\\sZz~bccG{EmmcŋB8틻q~R2.3=nz^IC/R*9>>^9sF<{l±R5O666V v}}?00 an@՞B""ԲePQQwbҥZ-vލjjB8zRfC`` n#((HreUTJim188V] nPwy ΎOPn{K/R*8|0Z-T*{YFعYmllʕ+a0$|(۞oc&""A^V/| Z[[1<'Z ")O.N>YVQPP M&M& ju;ϑ#GDXX>DZZP*"11Q;wnL}hhH;vL$&& *^겯rl\wǏjZ|'yH\+WT*żyDYY[5O[ooظqzQzVIJzz jODtTWWC!--q\dW[[իW;nBff&#{`~ٳgzhmDDD>*??p/'!-‹/sNdee9j[]x"شi'DWv|˗/444 ..ۑǼ+?9~|gp<:lƬY``4peGEDD`۶mlصkz'PTTł.ݻ[lDh.ܹsa6q a\+//'|"66ۑ-.1c?ؼy3ȿ#778t'DWvڵkoaRXV|Ǩʕ+QVVMVn,_555@YY"##oX,dggۘ3gZ-")T*ܼyIII>/_o" 裏pmDGG֭[8%""qqqjX`?~Eʕ+ގEӮ_tt:$''CbΜ9ގED'DDD~$::ذazzzUVɓ'ގFS ٌ,TVV"))ш3DDD~ڵk(..F[[㫯§~ RxDo\|׮]nGTTN8<( oG$w]"""?+={OFGG <<+V@FF03M*===x?^* "l߾^NKD.$`믿⧟~Ÿ HD\`` VXM6aݺu v$"|SwIENDB`./oar-2.5.2/docs/schemas/hulot_wakeup_process.dia0000644000175000017500000000512611757171206020064 0ustar plbplb][o6~ϯ<@ dNE-bfR 䥿}IIN|Ǟ̌#~~Gfa_O 'g<ד>^L~p< 7?4Xz8Gד{zzzs$Q;]M>\xf@\y6: oWZyqTד`i&x>)JY%DדwwgrU6sNMBݦ*Tݴo>RviA.,4&aϤMixoJk+n<ޡ ]>OT80((k=.\t\pav: By$ U+'bu_ vo.:y(k/6zi8-V¹<֟Gj1H0փ5`' ȅ qʦ9~_J fc  FTrw!XqaqtisȀm}ӹܦny곚9!;Zib c?AŧuIorgurAS#ڔ:uInOݤc.U_NĜܽ2iqn2Gž8.6iߖ3'h/VA:97~ ;#an#6mlnh2y:7alByml(C%q\4uo{3):ɧ3^O-kHFeUO0'1W-LfM̅BJ^أz[K:u"jBY3%YjC}:֮] A@@^~e >ZGvXv->sܸqNNN:u*fϞ_4PMDDdxzzwŋ/GGGK#;{-B~~>ߢyZV0TcĈc=oQ;}4y?:h]V)h۶mGnn.&O? ԤP?~Dzz:zSNi]Vžj""28}4:vl̘1 ,к$"bĈؼy3Ojj"""]v?cʕtZEG޽Aa֭ZT#pϟ?m۶ҥKJqrrm۶aӦMZT#hҤ p >^rC5;!C,>ŋua^0Xk~AJC5GT]|8233իE=.syO'''mڏ̽v{hԨ.\hԾܭu;,V0j"""+$$$5a֬Yr Ν;_x7@MD^6f̞=ؿMYv8uzKY7o<8q111HMM3fΜX$&&"))qY{˾}pAܼyC ĉs{7Dzz:c޽Fm___8;;#99999VmCe DDDdў={ê0uLmk{IHHPNjٶJK˗2K۞;wNy ~~~@9{8%%E~~~ofSuŚוYYY뭮,vF/>>ިn[)WHRRېmSMDDdC{GK~Ujj*6m%%Ey-[*05j4eիW"à~...(,,^[۳ڥ,p㗻kPMDDd.Ozׯ\ooo$&&*/^ ڴ:`֭HOOҥK(Xܶj?CeLmoݽR5k#cƌgggiذ,X@U=w3T6M9ڵkXĠm۶ZST~T| FQFaڵZc8J=z{;#ٳƕ7TYx衇СC;vLrTWMyyyAVV\b4C /TYChժ?Ci]N@]5]7nO<@]l`b֬YWBd^vv6̙2e?j"""< ?ui]Q͛?zB.].qL5;|Æ CFF>}ĉZF5Ν;ѡCL>xױsNxxxh]Z1DDDoEDD]tAxx8zx@!66n:>}ЦM,[ ]vոšbժUXx1Ν;,wwwGHH6lWWW +۸z*,Сf̘~jq*СCظq#ك3gh]mzˆ#кukKb&""GZZ.ʹr f͚K'4nS z "" Ѿ}{˨ ;wŋxw.f&"""Mǣe˖7oF:TpD;id%K4SMDDDW,..xzz"11T4;? $$쭦j4 6 ڵ 8xbdddhYMH^ & ЬY3ٓT0TQѺ~{za&""Jww/Ѻ]j"""Tz [M HDDD>,<<<6ڵ+ -ZTruDePMDDD* W[V|1D?Tb&""""RH%j"""""Tb&""""RH%j"""""Tb&""""RH%j"""""Tb&""""RH%j"""""Tb&""""RH%j"""""Tb&""""RH%j"""""Tb&""""RH%j"""""Tb&""""RH%j"""""Tb&""""RH%j"""""Tb&""""RH%j"""""Tb&""""RH%j"""""Tb&""""RH%j"""""Tb&""""RH%j"""""Tb&""""RH%j"""""Tb&""""RH%j"""""Tb&""""RH%j"""""Tb&""""RIuDDDD/2~gˠ29r$|IC5U)~%PkN4PMDDD{ci]Qdd$.\ub&"""͵k׮FrVw111?eh*PMDDDDC5J DDDDD*1TPMDDDDC5J DDDDD*1TPMDDDDC5J DDDDD*1TPMDDDDC5J DDDDD*1TPMDDDDC5J DDDDD*1TPMDDDDC5Qtʗ;q9smT,D3gDzP~}DFFBD*@DݻcԨQZdҰxb- G}4\r%ߏ8>}×_~PMDDDT֭3f(=EEEDÆ Ç#33z L\xbVZʲO?AAAprrB۶moZΝ; '''/0[|pwwG:uЯ_?%8J>>ܹszjj"""t-,X@=7oN83gw_:z(bbbP\\,۷o<7obȐ!8qbۏ3gFff&ߏ#GXK.HKKCjj*BBB8J}+V`С|2k={_$>>F_Qn]9 :|Idd$ϟ{o~PPQoat:~ztsM9pF$_Ğj""" "(..X@rr2Zl hԨQtw60jpqqAaaaoٲQQQ CfͰm6NWWW\vlŗÇx'믿FVVܹ5k֠G2d d7pqff&!""" ҨQ#}KRRۊzR_vyvYI?JFJ72bȐbuŚ,J+6m3Fvmݻw˘1cnݺ2p@o%77b"":u={(#]tTU2???t֭äI0qD\p8u<F۷i|I&{#F3gPPP16UzԩS.q4kưað}v={mڴQvL>aaaHHHm0tPԮ]bg͚+Wʕ+5kƍǞj"""맟~vIQQ̛7OQBCCeÆ ̙#nnnFmH۶mEK``,_{ׯ_/!!!UVk׮Rk2Tׯ__$ @,Yb~Y\ Z)..3fxzz̙3Xx"bBŚ?Tb&""""RH%j"""""Tb&""""RH%j"""""TҫٸGELL Ξ=4dddWmDB ANh]Cuqq1vڅkb۶msNEET|||cǢM6ZCDDD՜ա://V… qſ7ѥKk-[DÆ YaEaa!n߾K.!>>ƙ3gxb,^ݺuCdd$uDDDTMYw؁.\ի{9 4nnnZ QEr 6n܈+W8pzO>!!!ZGDDDՌ sss/bpvcǎ!** #F`jӦMC\\6l؀ٳaaaXrQ5Sjvy\...Xr%ߏ:Tf}DVZG\\^yI&H򈈈0ѭ[7?~͛7DZc /@Uv}D ˖-æM> Çg&"""3ӧΞ=0ܹ5Ң6J7l0ؼy3&O˗k]Ul8pG\\,K* hҤ Zl]{Һ4d'"66͛7GTTTt vB=b aĉZUeܾ}8|0p%ܹsZF @pp0:wGyZV݋ =4Y-55ΝCTT,YƍO? '''KJ`WZ7ӓj;b͚56lNݻ#88X4SPPWFTT .*ǕѧO;-[`ܹO>xGЪU+WJUM~~>n߾D={ ۇ(DEEӧOK/ڵkk].U \~-[ĵkװe$4YQQكO>۶mEX|9q˫?>{=Z _]O?-DLO?i]Nڱc4kLNAɏ?(ZFϗm۶ɀD i޼ڵKl͚5&$ @k]١ԩt̙vs7{i]f "rMqwwڵk_uMVwj"Tף4֭ҭ[7Kꫯ cbb.'NH.]0m4׺,̚5KyO0AܹuIdNJdѢE(duY务Z^;v,+ GHH8'Oj]N2l2ԩS~)Ѷm[K*]v8p,Yڵkcӧ233.*̙^UV/PPjBDD///l߾ BNN֥ׯ2224DC""aaa@bcc+%LuR5mSUE к !m۶Wi?zƍ رdddh]Y'''ٶmP  +^uK*?=z@JJI&طo42\ܣGK;8vW\ [.֭'"77hNg;wDhh(/BYWTTH4l...>|џ:u:>S m۶os=WWW4j .z߶ lΩɚ1z4oqqqs׬Y|͛+u;닖-["99~LG: IDATػw/gjIPPv???ݻfҺ{ܹs>,0{ljT֭aԪU f©S.LԽ{իW#11ݻw_uk@UZ8ay>}_RSS%%%E#U4d͒+.] &(yի\|Yn߾-ƍ^zɨpIJJ;wٳC3fȀkT}R:l9X:s/,˗/˗/ܾ}[DD>yW:6~x _~f5TݻwNڵkˉ'.cǎt:ٷo1c{L .HDnݺUa gΜF ݻdggC ~a M4QCAо}{ ǏҾ~mM}# x5VItR|=ٳ㔔6j㬬,c___9wQ}wdn߶i Ֆ?Hxx̟?_?gyFnjձT 9sf5{PTT$'Oj]YjkHmӦ >dgg#99&M*:u*Ґ)S`zOOOǛ߈#p@DPXX4i&N .N2jߒ#F ""HKKԩS޷-uZbZs9KG߾}ѣGFW_}}X*(jvڅϣGxᇵ.H׮]裏ܹsҴ;w"==Cƍ>7==3Z>dǹhذ!ӫ܌G "UyUƏ+m*yQo1;֭[vQ/Vowi۶z ˗ګ-Ǐwwwqww5gvY~^VZ~H͛'(at3f8;;KÆ eFۖ:-aZs9KGllzeQjjz߬>f}.$<<\ea5ynYm_mQuD:bĈro^?x`ٴiRV-IOOW[N\.U}/^Tzs"[{K)=֚jȞBunnrVY~1T[}uZufffI^^^o-M6\Dモ*m&~P[;+V(/aaaF7Q͕qƉx{{#?xxx #""͚5ӧO+]zӧYfS ~\3tw{G}$"7K%<<\ɩJ2ƤI䫯2z_~)&Mj2m4S>J2,3䢒v!ZGGG T.VPPDhjAMֵTsQsQcǎ:5_Zo>Ks[Ծ̵i<瘷Ҹcǎs=j!99IIIU 0'N@ZZݻ7}`$L)SÉ'T8;;c̙Vg`?WƦM7nĚ5kdo#..HLL]!="b4ƌٳg#33Ǒ#GڟC=8vMU (MM RmzSdO=Ն[5e 蹳v}Ukޖ,͵n.[۴t-1ok=k@.\Xڻw0SLŋHnСCҥK7oL2EynUk*Ғ_ִo699\e˖;vh5S͖l$KmH&M7ސ&MHJJ:-?ݟ-S2k, n/oC]=X3TYɞB |eq[>\WRijiuKuڦh)Z9+W /i{ #GiX < _rss%$$hH-w"}s7mT"##YWfmmCD䭷F7A L,l5Ne%Ce]Y?[5Õ u$&MyHEE(= asTNϯj*EUyOՔP}!e_޽- iyS!ij{kB,m_rh)Z9ED~gyGp]ƨY6TߺuKF-pU6`Pm%kC=a6VC77ߔuZՆj"kTD6&\3TSu6T={֦pu6`mꂂDDD âEuGtXb6mڵk#44 V^`eSNŋx':u_~FX|e,u96(9ϱ7.]Z)XxqŋxUqa|8~8233ѠAL>*tR_*->Hû`ǎe3Zw}ׯ'L-H3NNNҥ tbܹ֭s`ȑJ]зo_9rݻwveپCuAAX  ڴ7ou@-yҤI i*;wZ VTT$M4%KHӦMi-jӖ-[f1L{ϗiӦ}'(rrr^xArrrdҤIqrrVZɁdժUҢE qtt0e… 2h qssڵkK߾}%55ը+V(6aaa͕qƉx{{[ʣ_~6i%Pajfڴiy&RRR0}t44NNN 2BR=e97Ҷ77}i۔VW^y˗/ip[ի1cƘP-扶1%o}T3|Gr}9R233+d?UVވÇ[FpIJJ;wٳC&k0}t߿JJJG^{5g~yի\|Yn߾-ƍCٿꫯDDdժUҿ{y믿d͒+.] &X]OYcIojx@#ْ!SLQFݦ,@~a_֮]+ўI||u>>><___1v8++2^j˨ׯ;w85|@^|EM먬k{6`&=\_2T4Y@cSRRۻ}\pA?䈗\xhw'$$_.]*/_}K򒬝e̥޾}{ٱcu[nU. 4lP\\\dرFӒ_^BBBDKVd׮]VSXuzBuWu 1bf5Yl2 ֥v.Ƅj˦Mׯ_ZjIzzlݺu2xr_EܞP-"ҽ{w ?֥ڽ{={jZGVVxxx,DUI۶m9rDRJU`]SH Pqܸq믕LJBqq1:,[z5ƍy //=\]]ѨQ#,\hm޼9YF>..͛7xltXx1Qֽ/'燏?uskc++!{j*1c4cǎEvv*ٵkbccѶm[tQrJU 5eȇA-C * hĉ+D޽qA_~I 0j&77>}~}ѴeX9.]HVV%Q5%;vҿ)..ֺ$"##ԭ[W:u9T]rEZj%GjUz c ӺJ&reILLGG{榶%TȘ1cY6l( ,iXJ ~3[[իr;#"ecמ狫8::*qIjj 8p=#ҬY*=u]qq5Jr Qe8{ yz Ү]ED%"oֺ*)**JH^.ʘ{n~Sźq<@.خ e„ @jժ%fͪT]VRN&?ӦMJ\MTO=y jBT]7=zƕT-[b޽?: &&F벨 _޽{W_kϱ`888`Μ9h߾= ˥K0x`=wqgԯ__ʕaYGGG+N-x-t%lܸxꩧ.Bx衇p9t Һ4Bܹ3gsHLL?C!44TҬ0c 8p> ~wt ݻYH:u /"uVx{{cڵXjUͺx]tA.]gtA":xWu9.00 {f͚a…qՑ] yX`t:f͚Oʤcǎ8qVXرzB@@{9YǏ{ӧeL:xrJԮ]oΞ=QFi]*U m2///={,ۻ?㯯J9 )3Qay%6AJy 6u9]ELWfL6ҴprL4 +sK&P(N9_/s>@[kܸqrqqх f?S֭Ç(;;;j9r(www[vR^^sԩSJKKSZZեKkڴiS:bNIII/jggguաT;wΝDdk*..Nu96a ՒCiʔ)JNNbemMUTThȐ!r>3-Z%LNNvܩVD?)TUU&5k,-^X6(,,ԱcǔZrppkРA."T2m޼_ISWWɓ'+--McǎUjj*S땖T>}ZΝSyyB;qww5zh*44~I_[ՕEH%gZn\\\xb[DCC͛4yxx(11@vvv8q&NhVSSc]Dp7.qvvu :n^4imۦEiҥ֧'AmmfϞdѣuYճgO[tyŅ jǎҥbbbg+Y^^ƌdC))) uY?Z'X'%%Y|8k۷oO>ѰaÔ-;=aʕ+:ywY3f5fW 5TKyVbb :uJ[lі-[$Innn򒣣ڵh5*++UUU˗/79wӵl2ذB 222TUUk͚5zm]ZeXr[ݧ9- wiΜ93gt!+++Kh:, *<s]pA/ 4H{챞^Ֆ>/^Ԋ+z믿jXsNũH Ґ!Cu,XIQQQlRRRzjKׯ}g%mݺU[nUaa߽ihh{nUUUiԩڵk4hWz2me%%%Fqq1qDcժU%˗jc͆1s&Fe /`?~ܨ1*++˗oV{FDDFUU$#66Xz+X1i$n8pyNJKKƍ &W\1_n̝;XpΣk٩Vݺ=|@޽!___IRnnBCCUPP 韕r555rpp^555ׯJKKn۷+##C>>>y=tOs߾}ӧO>Mk^WXX(//fӯ_?9rD~~~*..$ӧUhH=榲f۔WII}Ζ{ݾ}T_UrttbLd9x=@ 0@?C[R݁a /""k0o<lܸqJJJjM^t%/Z ?No,YB566ڵk:9ã< >\ʴm6EFFk}wxzz@aX΃P 6lؠ8%$$իu233+VTZ|f͚kUս{w]t_CfϞ+WLZbſQ}} hjϛAY`"##۷oon<WǏ׏?YK,i6oެ^zG>>>ҦM{wʕ+ࠐG}gyF=4vfO6MrppК5kw^wVK/MmA/q^of̘y"LYv[cT&Ij$B5`0P DL"T&Ij$B5`0P DL"T&Ij$B5`0P DL"T&Ij$B5`0P DL"T&Ij$B5`0P DL"T&Ij$B5`0P DL"T&Ij$B5`0P DL"T&Ij$B5`0P DL"T&Ij$B5`0P DL"T&ٺ=,>}Z%%%jllѣGk׮ TS۵uV>ͳ>˗wߕ}b:kzh2_^ٳg<oQF-se+==Mc!U}5m4UVVzjEDDŋK@/h„ з~p{褐㖕)**pnܸ~)''GfjQ՞={L}a9sU}p&O\ڵ:))I d}=ϟp}رG6#GZbFgڵ$EFF8ٳgu }i̙mCk׮=/#Xyu-IٳW>_~] -~"+0%33S>΍7tQ8q  :T?Xnnn*//oq{V`ʈ#4bĈvYYcUWWbhԩ4tPk?P=`VAEtHeeeUSSS̙3JNNn%),,쑍=yVg:DEFFZWLrZScwMZ܇jt8͛7]-[fzy*PKTt钼[էFAAAm~pGYYYruumU?B5:;V׻wo߿cJ>}I&Opp233%B5:=z(%%E))) Qn$ݥK:qℼ<?UTTٳ***aT``zH?SWIENDB`./oar-2.5.2/docs/schemas/hulot_shutdown_process.dia0000644000175000017500000000414711757171206020445 0ustar plbplb]]oF}ϯ@^O|3nR>nMCdm xK 2.sϙsϫyPI8qT4 Z\O{1{?>rQj'K?\]=>>)t0XT]煡we&7݀iϞ+zZ'ly+u=yO$^G$*q'΃^OeUN;5m{ 5Kih~RJ]i`L}ɤ-*5FoJko8ޡ+/YQ'̝#8@쒍_N .9-\ljN@!gq*/QuVs/4]ZǯM ?qmw~}XTzyy w? CP ҽ5Oo'c;Jb:UJ7۵hiY]}O vzO*)Exsd;w*xQ:qRduݵR{xJnUg<njFv2CERߩ֫Ÿ QRPxni?.^nlɜ i S(Q2rMq3&ܙ\R4$7m8T24rSeg t&K@p93 62h䦑*;MgM+7Q 2R%erEAX$14RSu_Q߿fXG53XWf†(\z)r2%pHl^#&?ZWZ$*/ )[+;yZgJ|@6`a[rY ] v Bޮc*S']{?~ 2CU}ٛdu/4%hEx4}m!G ΦL\nq֋ ț@փ4R:lv~hT(ݶn9곚ӍЍBv<ZZ2|EШcO͗Tbܼ+"K!,"Qc;qLWzqaC7Q#8ӇLmE.Nt;tsd-&6@)SY)5Fd-F]m欣\xԅ6et%'ԥtumTL'&sXšvM9k~ fU>6쌄BRǽiڠȅiX5o6@hFޞ:yEyinYM1qp^zBKpZT\^ ˥{7ԓʮ2J` QdJDhVzci#d8J(PsƜPPKAs9,("_T:VT 6K bSN71oAE+]XcV]-Lb iCn !mAߤ:T{z:߅zBO8*lC5k; ;ȢD+DRX{1QakPP(/]C ia+8FرoJ6+p]]% \oA@U'[&^RG/UV6 ^"[Ƃ> |# YXB׾8΅V6H%bu,0?0Hugw% +W.ްxa$s$HlŠ@j+\@;UɅuHjM ɲBK6_"s$՞8%6q_F&ek/V%> N]~| 0tJ>x5-+_G5~E^r!Q%C_%ln`Vc0xGw/}Az{D^ؙ:?9Nj./oar-2.5.2/docs/schemas/hulot_general_commands_process.png0000644000175000017500000011740511757171206022121 0ustar plbplbPNG  IHDRGmPbKGD IDATxy\?0,"$. .(j:j*Vͣu*2sxN9GB:ZjlJZv X\ 000, xȹ}]oU"" """"E.B<B˖-km\JeW<4k֬DDDdރh//Z ~ ;wZޥK N(ժ-[`ʔ):Vi+I%"""0 Zuqta(JKKdq'0 .\?O{ၸ8;wT~ ARz_kd"44RgϞœO> _]c_v ~~~صk ///4oWknB|q[lϛѶm[S]GVv̞tbV˒˗/gE-#77T7''<鉑#G⧟~uz&L^^H?~W\An3ٴi,Y7oʕ+xMYYYurJdddeÙ3g믿"==ݮn:|'ڵk1c _|nݺAD`4ѴiScbbpk^xsBxx]kqI^ z*Ǝ^xThܸq<pQ\r{Mc=8p rrrpe<xwx扈z^7xXAAYm+""|򉄆JQQzSLԿc'ҵkW)--5_p4 'TqΜ9z…2rH5O>9rD4;w9":t3fȋ/h֏N3;~~~ NhsDDD wB͛7vܼyuG׮]Ì3T*T*1zhxxxX믿Ɵ*#FViii֭;;aaas Υ%K PeggvBmOKKl6 Sy6mʕ+M}T*xxxI&_~%tXf܉&̵kcv죏>ȑ#FBͱk.ܼy"e˖%߿JcW DϞ=+N<gF6mLU[V~ m۶5k駟⡇2zEt:Mm]vNJ+0eŹ56LB޽{cѢE b͚5NԫW/j ;.]y!66`_ީS';(((ӧM3vvvv΃`9I-Oʕ+Xr%.\ӧWenjOt˗#>>T^ڽ{w˃`c0c Ӈy|7(..ƕ+WptQqDDDo*ɑF;v?TfiӦh4ŋ< ۷o3ȑ#r}DEE޽{_._lwfLu*]}'rJeU[c7o\m&:uwww%--WMsɑ#Gh4dƍ)))ҳgOqss{Gƍ'/^:w""D%gQx""""uLB1 )==ӧOGrrC!""" RK/tv(DDDdPjʞ5_ѠDDDT70 %"""Z$$^psssr$DDDd Pj^^^N,aJDDDDI(5He;N,aJ R=ɑ%LB1 {B Y$$puuur$DDDd PjDR Y$[n||| Y$jPj @V;9"""I(5H&M89"""I(:& t:#!"""KR{СC+8qǏ؈HPbccѣG<8vպ {1 4!!!!Y$~6mڄ;gٳgQPP{ 8=z_~yɑ5n*){ Q=cѦ ą ]iTͭ$""rR'"ڵ+~7u׭[1cBTDDDd /SR0w\t邧z"""""[J Bii)"##X'%%FŨH wBApuuٳ?0%""CJ ^GHH.\Pl޽xYPj04 ^}J☀1 m۶ŵkҡCеkW'GFDDDq'oooL6cJDDTq'WYf<'>22QEjg@T1c ܼy (QŝPj.\Rn١LBIDDDDT똄QNv: ?o߆^wvXu+|||мysDFFk׮ rvXDD0 %:ٳXv-+9rF!kQQQ5j?!**9pz-l۶͔x 66F&MhiUZZ[n8y$~W\xTޯ_?̙3Æ JrbD1 %";}4^z%l۶ GALL \\x8q6oތ$dddze˖bD4LBȩ1}t!((?3pF`ܹ̄Z_W#FI(9EII ^|E|I&a…urd ^… %%%|<|?[EKFF7ήTe+5T;2`Ν; y7jާ~*ErugCD \LBZ,ZH/feڵ4ŶՑ9~uYRR"۷o֭[+-1JKffuFF93G[L6MkIJJx~N>-:u ;vSN=km۴i#'N2G]_{װ&P[kȘ+W#Xv5JȜ9s 5p2 Ȑ'J=W֬Yc*SV`ZyzjǬ%ӱcG v1,Y" uuuk`uR<ҳgO1ҥK9wYYfƍEDdƍ2sLcZ6͵**^"Olcϸⰵi0$44T?`3.rh4ҴiSz!Q&奧KfL۵k's'ĉf;df;ao{,((Sʮ]떯o4%..N=jV$++Nh9Ve'TDɓ'ѣ͎I``Y EEE6cm֭v}+_n_{װ&vBEޑ1+ >1R8q~١Q(бcJfft:ٰaz ҽ{w9| Ζ &ʫ# raӧٽ~xϗ|AķPbbbU~nnffϞ-Ç<˓8{B-ͱI}OJJC:t$%%ٜo 0@233D ^5$TiKTPkIEՊZV+6m2Fyw%,,L$,,LM$.j4]ɑxzzʐ!C̙365fy2uTŵX?))N'ȤI9Vrݻwm۶m۶Mzas>ږk&ZZ-:t5YaM\7Vkc^~]Oz͘n(oPQJII?z_!;͜9/FBB^}UgCD PPr[<5tڦ[);_e珈9G]kgk{ @TTS ǔGDTݸJD5*''HjVM0;gu߾}{w-ODTQ4mɑ# "gCD P"QnXR]Aj|W͛>}:BBB憀<ؽ{}5 5DLB֭[͎I&5>O> ={@!##cƌ?1bJDuٳh"c-œ9s̎">>͛7z)e]Њpi5 M6˗/ʿ{,Z!!!puu=܃ѣGc޽:3f=,^,+V]vh4ڵ+9bWP\\ &I&hٲ%y-*KRwE֭9=._݋WG5`:tF^^<==Mj|0S#Gb|23f^x?=o;_9998Y޽{~\~<^xbor rrroaϞ=UYB0U8K򉨑r :Zr5JDD "f""m۶'O^_tIZhQ/kn߾-seܹ-ҦMyڵk:իWWvܪU+2Ȱ+r{yg#" >1jn޼7nWJ!!!Xx1^}UTvIIIez9p̙_o߮Ԧ<AFF/^\|Z NW,[ӑj5 ())v},UEsNtX~LDtRhZh4!99Tn+kO){ၨ(,Xm=c"447n6*l2e ɓ'}2֞9:tJU)ŋWjcT*lٲšXkk4pB@ ** k׮k.ݜw[iZ(ƥOl|*;^z%7/2+ MEeѣGcԩш7o*&b* Xt)g6nU7V-[4}*?wBWVy,":6]kO``YF$++Kƌc*KHHX9tzΖǛ-- l\X_~^zɼyo{4 }vW[Vkx6lɥKdȐ!WsO>}h4ҥK~.>94kmϟ/{t!ƍk.ݜw[HfͬfK[Z<^|2l0 iiiO.G_-:NN:%O>Yj>ެӧOKN`0HǎԩSvZ6mȉ'fu$t֭Ւ:Gʂ m۶&;v>TS|WҾ}{quu6moV{nyG_\]]%88X}Yt钩^_~Y_,Yb5~Gb.**qƉ4o\~m]ZmʲeρQ] Ќ 8qC|}}e͚52Z-A-?˗>p7I=tj]~%KHBBBq]]]`0X9*ŪOvvSFtEΝ;gn֬YqFٸq̜9XkVMsʹvdݭ'+Hhh|VcCl#k0x&5$tvIzzb =q.X~~xxx۞1 dԩk.źFGov:+α*;""?L}8pK~~K|| 4f&#ׯ_~nnffϞ-Ç<˓8{B-ͱI}OJJC:t$%%ٜo 0@233D =n=Y_PsdkL&2դ_^Zjji&Shw}WM$99TnOj2FH?٥QTҺuk)..vv8DԀՋ$tر):N6l`v^BBt]>,Ae„ HBz>|Xcv%>>^%??_eРA67-,,~]sss7;6{l>|I^^)jiUMB'$55xRR :ԬСC%))|l}7d)%%%U_8Y!7o{G`Yb]1Vf=Iҹtt̝;w iѢ\vX ܹe˖9;"jE~zjVEʦMLeFQ}] 777 dS0]h4)7#OOO!Cș3gl%D:uTdvLĉG|||dҤIfhVx*޽l۶ͬl۶mңGնD^{5iժjСNcuٳgά,Bx)i߾[6c({Pkm}5JOpvXDԈP"rUVaڴit֭V\ݻ;;/;;&Mw}V/o7!I(9Ց#G0vX?~...xg0o<;;ŋHHHeˠp")) vvhD1 %"Xd ~m\~...۷/q 0:}4كڵ @HHkJD5LJ|}}ܸqRlݺU=Y~Ȇ XYnt:ٹsQRRR"$""2qD֭ȺuDDsNχ~(ci֬xeRXX(+VJ+ũ- ,ǏKqq@PQ"͛7q 1۶m֭[1|pU*}ePRRbvh4Zl˖-osh4/ʕ+pss͛7U)ZmWT_kqVG#GĖ-[pI !!qz0 %"c>,YU2e ̎駟ƽދcǎ4C 7?$%%aժUGxo# NُR},_Ǟx//s=ŋAD5㉈:4+r\FAnn1x֟sfLDT_UN5k$44TjDFFW_}%""%%Ó'OJôfMJ8p@"""C̙csܲ?HHH,_ܮx*7???EK``iVfVYsʕҤIJm_yӸeQZ;d ODT7oބ|}}qƍJ|v|1X|9N:%K8;OT@DDT"&&;wtv(DtU`99Ƈ~0 ^^^N~bJDDDDI(9_T*̞=f%r,^Ҿ}{<1)33S&N(ݺuYnDEEɪUPC"r =z{{Hll[Nt:ܹSí@>C),,;vY4ntt^Z eŊ'(- ,ǏKqq͵TGJq"uGbbիcǎVT2aYrUVɳ>k5rqoԏҸJlڠϷSNV2&DDU&' _YRjz۷EV-j1DRY  Ufmyf1bKӦM^RQܹ3:uꄴJ#Fm۰uV >  JSSSSѯ_??C EF /QcJDDDvT.LB=:&DDUpm@&M Q$j#ՠCe˖ȩ:bJDT^uaJDT0ꔈg@uP"{9;":L""缻:9"I(Q Q$jP""+~]${\x֭?&DDVt -Bǎ駟h4*=<^z%ǧ$"Yy!##< :t?"bI&uЩS'9҉Q}*)7)URZZ BBB``06o$dc999\/Ӈ (JDdDDDVٳ/AV#>>j?L@ĝP"";zhZ9sb}Я_Z~N(4 ̙clL@P""#44̎ի""JDwww̞=Ù9;DD***IBnݜQP""yzz⭷nAA~jnݺ! LBȩJJJ{a…vT*G} @NRftiٳ7oΝ;a0ɓ'_OΝ;W[DTt =$&==cƌѣG₱c">>QQQx"/^e˖AAAHNNI>X-}Qwbbbwٕ2 %"Xv-^|Et:t+W}SNaԩعs'T*͛w\v iii-wpQ~Eպ?:SN֒Pl߾K,Z?O<(--uvhDTi4,]8r|A\zj&DT/_YfAR_>;;FERW^֭[dL6aQ=={؝2 %Z}vL:* III2eCj]v ˗/ǒ%KsAAAv'LBVß'F̟?ƍsvHwذa\\\0w\~5{Q&DT+^~ex衇0w\gC5 0 xa0s$LBٳ_|5kիW{?7|]vѣG|rgCD DI(ոz-Ęj+-[JE!Q`-eJD5СCسg1~xgCV F… n:gCD R"ZIJS,99R}{۪T*"447n4F,\!!!h4ڵk&M²eꫯ~"wΝܹ34 """~z;v@Ra˖-DLL *~QQ^dff:VzSL0ydz* ‚ ̾/yQŋWjW~֯ﱊqI@^^pE'ise;pϪcjb?ҥKjhfv>9SeM>V'{WIIINK(Jwk""Ao.K޽%==]zdddȸqDDDˠAGʈ#D+(k֬"ʒ1cƘ>\}Q\>3:>^ &yyyr%2dW_~^zɼy,eҧO1~tRh|~U}UK/EDdnzٳmQDd&{+Fkq++?i֬$66V:$z^ef:S?jqqqW:47r(!!!RvKKKҩSꪠ iٲ}Z:u$A:v(N_~ELsҺukkq\,gppdff^gddHppY?nܞxM&_$%%I||Y?XkkkhΝ2zh4h̙3G ""w~)ٿ9&''KllsRJBnj3 m׮Y- h4M_|QȊ+n$q+~]?3f̨$O2%KHBB1{ږtIZ-%%%w#G؜CFFL8Qz!fS٬Ydƍ""qF9sID>SYfL< IDATj 8ҼY{R>]]]M}IFjD$˕΋R<ҳgO1ҥK9wY;ug>Z*t"W^yɍ7$::ZJKKmαiӦaz[;Vև_|a?Z-xoե1;- ͕ADPbbb""oȀ$33SJJJ$++뮒бcJfft:ٰa$:tYݡCJRRSF)ݻwW;-9{l>|I^^)zaӧ]*"o> fmjkk⽠xxx͛ѣG%88쒧=p@|ϗx4hPKHHݻÇ`0HvvL0RJzDGG >t@nconYG7$51*}y>v3OusI_.MJJRVD:uܹ^VZZ:T]ԇ׋VZ-ZV6m$""ݻwm۶ݶmCDD+ ⸶޹XW>u:L8Q|||G&M$f;EFF.:ljkkٳtOgVVxyyIaaCsPTE&MUߑ$QR$jmN%DDlRŋJPT:P_YeĉһwoqsspcfΜ9ҤI:.Yt|G"rQQQ!sεsK߿_:t`y""rASf24_ڵk'voXZ7xWYjZtx{x+QZ7y)ů4vUvkz}Uߑ8'89[]SS<;ښ7nHVRTM]giӦ0Y.;exll[Nt:ܹS/^X dĉV ~ʎ;Lܹ,_\n߾-}]I~dժURXX(ҡCիWKaaXԿҼerq\bH)GUDRSS%((֎;Biݔe+cG⬮FQczu&PGYV%*ʬD[JB+ޚTaaܹܞX۷MJR۷oki^7o#FHxx4mT^{5Xxׇ~(m۶oo +VP_oooO(e-v?Ү];)SX}Z;𐐐垻C)O:U/K@@-M-{5sLeܹUޏEmUmi#njgBwDDw^GNNnݺ???gcƯH77Jg*.طoڶm D>}peն\M֭q%\xZZ?-- :uBXX? ۷oGBBN:pjsDD ˳| Dvvv"c"00IIIbJDdN|n޼Z8w>Ib|07o/ 66V#0qD\v ׮]Ä 0rH\###a,&DD6Xr%n ???}Q;k, BPP7oJɓYDѸ1 s~16ꎋǣ6>DDզ&兠 6l9FVTTM^jsJDK2gTTT\p{+"R\\,v풆 *ef͒~X`0HFF3FDD O>,""'Nՙ.DD7n,k׮ʒQF4Hx aÆɦML,\P|I}hV 3P`T޽[۲ѣ)톇Wj|mֺƯǘ%ZljX;͗2־ה.[Lԩ|^dL2Eنan޼yҹsg9z 9s<&[;FfڕJU{BEݖY*jü>`y.l{䡇Ra#9͗;<y7[ڷo/f@sԖ1v[dWoDD޽{塇ҌZWۿZm3V;2rH #GZmǞ^&vZ%IR1oKPRR"v풖-[*eZSN{5իdffJ߾} s%77פܹs&*gϞT Qd0e˖r}k}Z-ZLsFFhd5mW_[Ț5k$!!Khkֵ6~U=,:N;}g>|ON*?*/ՎK}К&$^^^ry6t";vPw EU:[ȇ%[T+Q0nwժUұcGx!44T9hSjsXXXBe6Ζ[J``\zUsԖ޽[ǨYfrQո̍1B?(Fm\?WL{9ٵý{ ٻw_mCvkl8rњ5klޑ&&&J||߷1}tWnMNNLNN`t,|򉈈DFFJJJIݔҥlٲE]kc`k_efaaJ A2~J///i߾̚5){bK<˭VkmzYvAeϟ?Rn][߿/h[P}gm_tI(gGb)˒2y$((H<==%((Hyx@o}3~Q1 yL(٧[iilٲEڷo8}I(JDզiӦq_&M8;C˖-qehѢw=ui|%[`` лwolƁg|Oh;һ]tx]- t,qd\Ǎ7pm&5Lnn.ɑ+U]KBkjZ< 1Xȸz=֭[&ݹRÃ3]QDTmZn x#!{\t lQС ==ɑ=˸\h+JLL)zkDdI(U=zs@~uUDDD8; "_LB"&&عs!deeѣh۶-:w|_hWOOODDD/PW,2EvN>=z ؿ?С֭)SܥQ!Qz/vn d~27K/4)E"B,YcƌQW,%tig+\,Zƍ ޽[SeÆ &v}vȐp)++3/͚5 @/jYTT$qqq'~~~3SթSGݨRbm,jt KZݎF۶me֭JY}r[O۶m۷Os ȹ~g[Hvv]:c%&&Fڶm+2}tYdnZ&MYGWyZLac<:N,//3Uɴ־… Y\\,v2}֬YKZZ Ȑ1cƈHÇHy#Au[7kJAAdeeɨQL $O>jV_F̈́8pdggիW& IMMݻ_XEDK 2IiXm&M4Q٧FVj2E)aÆ y^i;KKK嫯ݻwK~~ K֜j޾V]Ke]t;vHAAPYjKbb"P*`־O<)RZZZ-_%%%k.iٲR֪U+9u׮]^zIffW_ه`;wgddH-L֯(==]"R>Ν;W{7Н;w*I}e=r{QIIDDD_/9G}$I&r ׷5 xv> @-[&""3g^/z^ΝkrmejsiRk\~!i׮x{{ԩSm&$ ]6?̛7d-BCCJNS.[ZWDرc-ǎڇ .]7xC6o,""7oɓ'H~d""$}o[֦bt)OX+7nXfW+sww6mȿoQWli}j)^t:xxx޽{5ǂ+33S?3DT{1 u[XItt8q3oߖxϔ-[Sk*)g q&GF@K " ?v}T֦dee) =uəf[ǭXiza%99b?ֳLk_iWqܜ9sIjjj=r+WHPPѣGW&Dd+&N`kpիIH"oȌ3O>)%%%uGIѣ%33S eƍk֬0`PP׮]e]kc`k_ڷ)SdРA-zO? =zP'E|rco}eK*b>5WVV&GҸqcTRpႴo^Hn$//m1 %"[1 u[.YƮK%&&J||ߋ7}tN':ttRj%88Xt:'|"""bR7%%Et"""[ln~YXX(ҠAiР^^^/jceKgϞ@3gslXWj홷O)#!CԫWO-o :uk׮Q{LBV$MDDDTZZX$%%bcc;@;9{`ヒo(..F>}e4hMKKCNǏߥh^,3 RǕդxkR+Wʕ+˗CXr% û'oߎzo&JKK}'DDU3DTϟkL٢E ;CExx8Ž;zjDDDD&M`޼y7WvvHgB2i$ɓyFԅ1 %""{Q$ILD]P"""g1u]LBH&I(󘈺&DDDT+0u-LB`":=#-- /zݻww}_|mڴ>"&DDDtONJ+l۹sg=z_92I(Q;w<UFᥗ^_jpx"*:lnnnv-ܼ^UY\5.W"GaJDN87778q©qDD\+AZ^S5GrLB\={={8;""`JDكcǚ$FPn]L2r- .DƍѨQ#$%%K_|ن왥j,m755U?!! ,s!tر#:9>OF=`߿h"@t+8ţF-W^y=`̘1׬?y2~5~-BBBPn]L6R>;w 6HaaٳGڶm+""חٳgKzziƢ5jQX'ү_?)..֬ߩS'YhjX=Q_֎+sjE-N[_˨Qlذj]ULBw!;vN|S~٪-W@IdED,Y"[www nnnXjKk;lbUdt:]ǭ8}vmۊL>]3K}ՊGZ<""Gru:iX\\\$T_jEZj&DDUӕᮤڳg̙9sK۷ƍQPP 6(ՖoE&Mqmܹ䁔f͚~.\`s,m7$$~! iӦ*deC=;v"^kرcN>m۶a޼yV4jQƍàA0n8j իa0LoҤ ݋B\jjeihciũ_{ sbLDTcݺuKf=[΄iF?.""Ǐ6mڈȡC]v-SNU-Wc^n-QQQJ  ˖-ӌGmQرce"R~^Ь*mDҨQ#YhI=Q_jÉ$88XeڴiJ}8ko}-<\n"|rrr[nkݺ5.]/UVaHHHS9_5zh$''cÆ 5jSc8c9)@OUѽI(9π >DDDDD$wk;յUrU/r6&DDU ^SU2w-jeԚqxreLB@;OD P""Pss! _(]VY99G<爧Ɖ(%"` ]acʊ+$%%)/?72;KDD|msīΟ#^ _V\LBn%=w~(111)C ׋$<<\>IKKoϜ#^wj:/9Yu/#G_|(sݻxǑmu#^׎sbNLj4ܥ3=wHvҥKҾ}{e[jtM9ic^g/pxgBsU7PPPooou8w<爯\ax5;޹8m'QТ"$DI(UW=Fc$""""r8&DDDDpLBᘄ$"*[.rrr???ͺsέh"rUVcJDTE^^^bu.]Z(LBq-gAD:tj%&DDhڴi%$""""r8&DDDDpLBᘄUQzyyyNaJDTE:]%%%NaJDDDD$HTy]p#!"0 %"_)))6':t(_^\LB,G)gϞSO=;w"S:1b""&wrQ-__~&˼`0frv4&&;vptDD5 P""{լpiiAQLODd3gZO3%"τaРAصk2www]vP"";pssX6b&DD6bJDdΝ;cȐ!7tBDDD5P"";Y::tP:)""I(:uꄧzJ憷zˉ<|0 ѱcG@߾}_89""gBC5jwqr4DD5τ8qiiiבgO?={:;^GzТE k׮hҤ""I(2۷~!vڅk׮9;:uˆ#0j(j!`JD.k׮ży󐕕P]vhڴ)իu:9Zu-8u>7n<<<0d$$$K.Nj;&Dt۷ok/}Q<8p >'GW裏vZ\rSOރ#$ڊI(9۷ 60gtɑݛ3fƍhРL^7ED(LB).^ԩShР/^gyFuJL{nܸ & 99o`ܹ{"r(&DpgΜATT._p| tvXի1~x ;Vb"JD$ݻ7Ξ=G}۷oG V_bСń `gDDP"r<8tu놯 >>>ꫯ0` |x1f̙8tsN&.O>Xr% ..gΜqrDDTL(9ӧ z饗rJ{qv8DtP"r)S`0`ҤIL@]>I(U; %jp4o 6mڄ~={7|psvDt[x1D'Of3gHMMEddfCJdMUVeL(U|4k %%%r _T,^*h"ͺ)))qPdD5[ݺu矣G%0 %jÆ ÓO>O> Ѽys4m@$sΎ 9z(wf" ^'jHV?O?)Z3h ܹLgƍq 0(t<UC~a'GBٳ'NAgEnn. :;,bzpssS>Tu6m`JYYYy !!!Xn`Xt6lقW^yE{ANv!99٤|pssÎ;IRPP<}mhŒm/[zzzʕ+SN}a~FHHfϞR`0W^^^G\\ ]X5,#S-@vT ӡC@zz#!wa͚5LD{Tn:\xJxb*̙;wbǎöm_.\[ȑ#'Ob՚sR?3SZlirfNLKK&MHyѸqc_5/%88X$((H>s۵6˔)SdȐ!""2p@y7_R ;uəgR>W<jk6lz={1mAU2p@0` ::׼R+b7nƌ,=z4&Nk׮ڵk0a};Gm+Zjߏ+Vhu7=&:teeeʓP!˗z!;vL)/**رcQ^=<׿eB???K(,,ɓJݵk*IYV{=l9=-ZPWBB>`ȑ͵="rZ}` `x5ku9DܦO.Cl16[$''Kppt: O>DDD"##%%%ŤnJJtEDDl"?Vݮ1/]tIED$++K|||$??J1Kڷo/f͒bPbccAҠA?~%j^1fDP ͚5l6m)S˗`G!/_۷o̙3EAIvv\zUM֟4i 8P)߿r H\\,]TDD~g]DD,Y"Jxi_1۱c4mTnݪү_?^Əٞ-x&R;jTψ2 %jS$TDd„ {#<"=zٳe„ J]$͛缼|zقI(e- }(gL"qckZjjb?y;qqq??_QTTkך&\FAIIիP>m,00>}W_lذk֬ٳ1n8|wXzjoԼyJ͟?=tb_~.Xm="r9s 55UYfr EXLBɡ\=1WUSnݺuŋ[)<+o?ޤ7)?w>@͛7nݺ4h~ml۶ mڴQ^E}-_5аaCf͚om@DN1rHDFFT ۷Q\\\͑9P"rY=|Me޽{#>>߫O??XjD'N4)1b&N՜0aFGGG3N x 5WӢE |ӧJJJK/ u8uf͚yă>hs}???T_@.J=z4~w裏GrѰaCnaaaJFfߏ y?J8rH\ziҼys۷~O6 ={D߾}QFaС6G%&&ffv]}V1#{Ԫ61=nRQp<#ٳ'gCvHKKCNǏKIIALL  ;wsssûヒ?xw1ydMyصkWo{㴥둓[nw %""HJJ ))١hz%Dd&DD7ٳѴiSer'"""_(.\ƍQFJV?55:t@ݺu\B=}4z///c1z_=t:vOOOtd777L6 C=de2KjU뗚asΙ ž>|X)Sv-Zt:.m{<ũF~UKqv9࣏>BϞ=4_ EDȝ'n'$''lܸ KJJҬY3%K$??_vm2:ue˖I^^\Risβa),,={H۶mүƐIJJ|ILL:w,KOO)ն_~;vXBHRRRvmnǎeժU/˗/WS֯__fϞ-&lXcZljVqnܸQFm۶*[nT&bJDՆIhUIK,֭[777at:e|N34XߞXEg3o>e4R?!Cxyyy&3Yn߾]bbbm۶+ӧO׌A-N5jKmY\\,AAAKHHUiD&5Poooٽ{+mU~XXXBe.]Ȏ;ƞWL6e֭(W^iRJKKeo^֮]+""dm~ڇݻwKXXxzzJpplذ|׮]@o.ei/͚541%AAAr))Q_MIDATIOOvUچN:ʜ%%%JyQQʼnok\JBgΜ)z^z̝;jҡVᅲvډL2EED%ުǩXED8 !!!!!!!rAZJu릜ɴg~ii׮\tIڷooҞ==t萲ݩS*˵Ak ˖-ӌA-N5j+VHzL)"r iܸrƵ* $\?6%.\Xں"v% 6Tf͚%?` 3f ׯ2'$&&e7n,k׮ʒQF4Hx aÆɦML,\P|I}Xk)_˖-:u(,Y"SL; IMMݻ[o'$$%;;[^*QuFCI | 1 j񥥥eD^p^SZZ*/_bJ.Z"yIJmIBKJJd׮]ҲeKUV7K]&zL۷\~]2w\9x暔;wN¤XBCCٳ*!!!J lR9bZc`/#Ç~ԩS"R~HOO-Z([h!猌 xXKB_}UٺuYF"51 kӦ?qv8wͽگ{tEU0 %f풺e޼y&lY*ǏWt:rX2c[;f+]t???ol޼YDD6o,'Ox{RR۷Rm[RQaa͛7aÆ#z^nݺ%;vL2CEJ񨱖9sFv*eee.?ݗ=*f,zRSStY+%Dڐ=5!8q" |80|p^6m~m ̙3ӦM܆җԩP̝;<4h~?ѽ{ws={͚5S>?ʻ=w-ڴi͛#>>ڵC-*ձvիWwG|}} ݫܰ|rٽnzSNŭ[/"bccR9s{g"8qM6k׮& Nnݰi&zÆ ^c= 1J_ DGGcxǫQqq1~G7cƌQ='Nĵkp5L0>c?Xt)^{*ߨQ#zW!ǹy&U Ǟx 6\7Zf]+JLLx)Ot/:N:t cErrN`ODD$22RRRRLꦤH.]DDd˖-@>cZ[RѥK?^DDǧS`{///+,,XiР4hx[QcVjuԎz~jL:̙#dԩx9zԆn"VA݁^zo#8;ѣ 6`ԨQRRRAaΝަ둓[nT >>ؼy3P"r&DPsO>DEEouvH_ܹsqF:;,"%Cyxx 99Ců>} 11a:7o`ԩS} 찈aJD兏? /cСt钳C>SDDD %%M6Ş={O8;,"eSxxx'|&M`֭ر#MlgwOڷo{1<䓸|2{1ݻC#ZOo_+W`0 Aũ>=+l޼Ow#!fLBe\| ,ڵkq eyzжm[4i~~~pww8"777g<ŋqE)e]tA||3=#XjKIDD6JKKCN?FZZ?"tE)_Sbòe˜m"W0|p$''3x&FK,<쳕Ht}֬YW_}!*&O;5"Gz?`D`JDdlذnnnX_Lj#pB;u*@:mD֪U+ĻDIDD6Xnn߾>}(_(1 (@UV8s Xj6m`޽͛1j(:;,R$ YPON\뚈k׮LDk&DDV۷hٲ% Ywܸq_>݋tEHD1Ya| 饗^N}+^رc!"X`##" >&DD~l۶ ^^^xMrss@yikwww_7npXDdkcJD8OСCi:_AAAЄY6c ̘1a4&°)(Kpe.ܨ1ԥ67mкP5\pZ$ژڸQji DSh ~hg^YA{s{o'ν)%"j`̜9Sb!11صkW|JJJ 777DGGĸMQdeeqqq&޽ &`6k4TTToh4clEQX8qfW7ހ;s᳢ٜ(̄"##q%~gΜJhnn.4 T* rNN hZo{.](xyy!33n|:Ζʕ+xWko^EQ0vX`СHIIn[qu^""2zYfG u(N&---E ,e}DFFV^/e2fӦMRWW'7Kll?~nniii}v ZV͛gӧry RTT$r6ly3| 률@bbbL%$$ӧeR^^.O<1'z^>,j漖-e۶mvϗl-OkF)hnm^RZZjfn)Oky͛@BBBO/P"TWWJ777ٷFH``C[ZZ$,,LNt-rUz׋J2dgԨQ2h )--[aa$''˘1cdg˓Pqqq(*ʤ67i]Xf"԰z^܌N>- &,z^:dRX?vX知ZGJCClٲ$a $::8*hk---r)ӧcclm^wwwp(#FWZ;ζVf-\E[+y`"g`JD$T Gk%V[[+~~~@ֺ)Bϟ?/*rcZȸq+UքKeeDDD֬Y#~~~'^*&LOOOUCKO%77$eSåDD,h4uVڼ""K.5oOIIX8t[^|YFe\46o{PG{֎b ОAmDDÜ9s2  ͛7gظq#RSS .֭[e,X;EQ,> 11WXGRDDpL566<<<5OFFT*QSSӾ$KD]E(z8{,И'O<==5WHH}]455 _HԽXQuVRSS;Xt)`۶mhhhxD,BW3~7sѢE]2g||}sF%K@b׮]Xv-v܉lڴn$$%%9ܟ$"rjGee% d,>}|ˤh0qDƍv;qq!򋊊BRRjM|'PV@DΉE(9+)))hiip-DEE!!!x///dddᅦFŋQ]]mGDpA()SիXx9^OSL׃9D `|r2)b7F!,,̸jymaƌ>+Vzޒ$DEEEEE&"""CD2QOI&"'VQQ!C)///t:455Dss3S/L>N<Ǐw/˄ȩ:|sOaa*?gԨQPN#"zٰ%"׿qY|G6}6׹zj AѣGHMMn"P"rzСC_[oe7N`` ͛gqۈ#pNؽ{7bbbJDNbk/b̴5k,&mtVW<}}}e"ٳgԩSPs皴=fp~.\Fc+DXk{:|sP[YYY&׭[Fmo7|Ӥ %"g"^߾}߾};h6l&NԩS;G8p N<4cWBY%"9rsP?c3pwwΝ;a%"Ň駟NEuj~ǏasuDDDDx9P""""r,B˱%"""."P"UEԩSa m`_VViӦ>>>6mJKK("..YYY{~m ?~%K@ 9s&N<ى{ODs%^yyy6\~&MŒ3PUU*̘1'OM={jܹ ō71܌SN׮]Ç~'sBWQ>D||222LWZ)))blܸ)z\v wƾ}pHLL… Zjlذ9"+Dedd&m8qt:>|I_sB\r6g;+WDqq1"##_ػw o"Ļ㉈q%nB3LIENDB`./oar-2.5.2/docs/schemas/hulot_general_commands_process.dia0000644000175000017500000000557511757171206022076 0ustar plbplb]S۸~ T-2K靥ν/':ۯ$I Il*3ʼnRΧ#_cJe(M:O%4A_ίow~ 7ȂA"GoA|34q4cOnyyAyEI$N/_tմ]?̻ ΫsM{#}e*,/)7znt&hII?3mƺQ2xD^5}e'[ l%Xz~b2Xbq|;/ o..\4,ς(_i@ͳgbmb nyUP}qz ǝA^0~oho*GX-;(k_h Eo!"*~LP0mJz6{jۭ:1[sBʦvY$ak{^hɚLl;>@]/E)Q2=  )hR 㻒Y_YjY8[zR 0 X*\`)!GA fʞ!dpy_%Ja,ɸ|TO>śO_|KKae/GJ (Y/eQ*"a }Cͤ<ڔO**ĥGBgK|y;Q|O.Ho\|TP,pR&ײMGV_ HKA2TSLKczkdXE);y4I;k/'ޅf橕:] Tp.~…HVpFʆKA:ˆWR-#8WttbM:) j:2 Ġ>-P.'q;Ҹb)1H'XZ#X,L !a"HX2ipV06i=@LVנW#%nB ?. ;ױ\g7{:`z6TP,3 Ð.jnU$ER@[ I!\J&?˜~~t(st4(ةn,$5&`3H@OLΈ ,Z@Pg>zrgPU}pO] s #%q*5VG^m$T īdi$DŰ=֒ѿ{mʬձW ^Qs3M*m@rj#{!m-u};㮭qR[uFꪼ5hdŝ|޻;  u ip-X3?ݳÿO?X:%_?#m0}.jVi^́X|<MvM҇VsI8E&V^>w ָ*3YGam0V> Y5E<}lZwn”բ:Nj#'UB Hm5"MU[J9MU};>}[n)~$vV[Y #6Ay1*- ޫxgҒX8uaR-^?KVXXؕ>ͲE$>T CN[ 49ɢ+@W?zfhwƕK  chm1[4Ay2 פ雺riK$bD`iuti.5EʅzB)yɇ l ` ]rJmх֋8Q۝@d{P./oar-2.5.2/docs/schemas/hulot_checking_process.png0000644000175000017500000011072311757171206020372 0ustar plbplbPNG  IHDR;bKGD IDATxy\T?0 ;.E,7YzEjWVJY䒒$ jZ.ܫ(5q Ma 9aķȄDDDDDHDDL&|_Dk<{fL&_p5kVéj7DDUbmDDTyf@Dcccxyy!66VM&A&iӦЬ:u*LLL4 .-yf(l B+HHH;BDT%Zkر?~<0~x3FM|cڴi۷Gvvf֭[^W!Dآ:w 777O(ͭZQmaDTkɓ'033Cff&lmmVj*,_wAAAd2OZ ByOj~~>4B8r^#G?&L#F׷QY"ܰi&deeaÆ #,, 矵 BPa![vq޽2s^î]%"z ,f Ú5k`cc6l̚5 C AFp…*kwĉhժUߚ?>>;'" $ QX8DDKQ.DDTd2ͱcDD/,Q bDDUcf`%""""b Y""""2X,f`%" BVKb1KDTICJ¯*u":,KJ:Qb,Q%=yϗ: Qb|||RO(DDuY"JR*Q,%""""ŞY"JJKKCjj*'@D$U-ӑI *[[[A&Ib,,%"TJNc,Q%͕:Q1DD;;;X[[KNc,,%""==](DDuY"JHJJJB6mBDT%"15: Q1DDDDd3KDT yyyHMMӧOBDT%"3g@RO>RG!"XU\.BDTq,,UZFjj*222BDT%"*ÇK\{nT*DDDTY"Rܻw]vũSZ_PP={`ժU5Y"2 lllp[7o˗ѪU+ -MMM|5BTSNfyԨQ裏РAjcŊHJJL<!!!R%"SjE18q/^9ƍJ'w޽{CRG"puuիW5r055ųgϴMNNF j:"Qdlff&{[Z4_ɓb qssØ1c0iҤbjZDD P~}>HDDDXr%/_)~ڴi[nLjg1m4̞=JOOO:QTHLLjAgΜرc### >AAA:U,_7oFnn.~zRG:j˖-xwtn_t)j0L1l2̙3j[RǢ0{l|dRG:&//Z۷mk֬^ R)A2"KB`ԩ B~~>Νu=+V@.cѢE5jrssFuB@```>DDɓ'c͚5P*ضmD0p@_HTBRx>V֭[MD$}ƍZ`TL=|r!0a ֫ ///^z|\HKK_+W̙3믿<}뭷[n'%":zWzyy!..|˝;wмys888 ))R 1sLܻw2 o6ƌ޽{Jxڵkرc6l؀7oիWe˖҆#"zU^xm۶7bccCơCpAK8Tiii;v, yxbo8Y픗p|ᇸwͱf3FhDD/Ef38pUD~8 ǟc޽W~G߿l5R(׿˗/wEVVƎɓ'#??_xDDWoDQ)| ׯCx",u:ضm̰f%"W #<|RѐdTsss#++ &&&RG$%%SNHJJ[oݻwC]B~{|ԑ*Lozf?~GARU!KA.zqCRRz}<sss|Xnԑ*LoG@ OAYOIHHQ^=^Ï?X^Fzz:O-Z* ѣG5TVϡ?P| WWW޽JRHcǎؼy3d2f̘#UKKJRf͚T)׮]/z쉫Wɓ'믱cIsUİaàVq1ʕ+… Vi8Ѿʕ+T*}vH{'|"u!33S4o\R!"*7陭 Xv-F Z]>1޽k׮ի}6Ν>w\<|7nKp1 ?.\affٳgk=zÆ +3Ta„ mܿ(sEzz:n޼xDEEUt &.]P!ְr:,dbccqA8::"88X89-[$NCDTRVEUEl qի+W&Mh4i"]͛7 dѠAͲBj̬)))LP(t)}|&M5׮]5`ѺukT*6mxVEee1c믿: @xzz ٳR!"*ZY>{LԩSŶrYVZ`IۋP(e.44h ޽[%>>>¢su^CQbgϞfLd) Y!,--ECWH|7xBDT.jA!l޼&L(a ƍDԯ__ܰaCEF!))IvovRɓ'Ν;(((@zzzmРnݺY.|{y(L777Z 'OB'""߿K}jСCadd;w"ZY&M)Sh6lf\0m4 >\}ĈÇL>]I&a„ ~:uxbxΝ;Ξ R R7nh-Çc̙x=zTlmYн{wܹG~~>/cǎ}lmmqʕ .防a+sޣGPyիWHIIŋCDTZ[)S?G +W^Aƍ>pB١yDݵx ~~~8z(h߾ܱe.防atY$T>>>PãDDHog]N:N>} 11Q(ժ_~8pߏ}J˃9o|}&"*Kf̘T$''c̙8pԑINNZFÆ ku![OHq~'''#\\\ [[[|RG:&-- dXO<8 QXRӦMÇSTjL֭[BjLOOѢE CRa8zh۪- 7.q"%jP(eknnk۷o,,,%[QÆ ZƱcǐ+W .sc1KD5k,Ynɒ%m?ׇ9KWV&i&&&bRD޽~ K,A- C}j5;;;,_\+ڵkmVkJ2g0vXXXXaÆ/.YWtYdXr%5k##'Ao~rQ2x`͟?bccq $%%im?~8 ???9K sÇq \tI37oeu.8{,.\"QQO)..NRG!o RG!QGTZn0`B^zPBѼysYNNN 4(Vi233f955U֭[ R)ŴiǏ54mT\rEg>"M4׮],_rE:J&]sܹSuDdHg*q2ToZYYI*_`툋;Sl;wM3aÆZCJ]2 xf--ZYYY8t2331tP>C˖-uCRi777׌.OdhB\yRӸqJOD-pnJ~:UV'055ԩS;`ڴi055-OFJ (Cbɸs ^lB2 nnnXjNt 6č74E.++KRsFzS* //W^:ǏÇPTppp:URpp0juq&M &C||֣mmmqcT*T*q н{wܹG~~>/cǎ}aʔ)HJJBZZf̘Qk*+#>}^^^XlpL4""%NB5S)88>>>x7`nni}Z C@@,,,[52o<ر055EΝ;wjO///lUy…Cݻkwa޽A.]УGJ6 ]I`Ν6l~A8d@N^SL:B׮]SNI* >>mڴ'⤎CDT*ׯ~J]vA.cȐ!R!""WŬ9 ,lܸQ8d ~'$''W^hذqU1 <X&i>@T||giӦIj^^^ӧ/|R!=7 >>:z-Q ӻbxLvccc,[ ϟ:k׮ᣏ>Qݣl֭\ :RG"=aÆӧ7nv*u$"""^0|tݻ7ϒF~~>FXӚnbvBӦMqi 0u={Ç"""`aa!u,"""дiS;v 5ѣGѭ[b-#99o~GXYYAVc֬YXr%G ި_> 6ãG*uk׮xSH >} ,, ˖-ѣGqQM63acc##TիCll, 6̙31i$օ,--1zh<x7ǏҥK޽q'!"*Lf plٲ읭%ѫW/9}􁱱}^ 777߿?>dJ0|,X'|"u*F!99CÆ CDT*.f*((@\\.^k׮ѣGHOOpqqA6mжm[(ơCwrr޽{UqΝ;:_ٳgCph׮?@Dbddooox{{Kj{W͛71|p8p-[(] Ν˗.u$*͛G8 Qp Ik_ ooo$$$SN8}4˖-8 %%%aaa055ŨQCDT.,f>,rɓݻ7|8͛KjlD;޿?[l{DD___O it rD=D9r$oooo>}JRXDDƞY5ڶm tڵǩSиqcoܹ3_^)뮞={?ZƠA8U acc;v%"ÞYUnݺ}п\xطo:w\C 6!FpXYYa޽졕H~~>&N0 22? "2H왥ZB6mSN_~xx tPCd26l<}I^h IDATo6V\հd 9~'Ddϟ/ufbbaÆLjݻaddnݺI֓4hrssqI:tOFNRW !e <;4i_~,43:믿ӑ1c`ݺu011:V/`„ sLMM1a̜9b8x .\3gn:888H尘%p >֭v^ b͚5̄|}}ѯ_?t[1 Njj*N>cǎa׮]}6Cq/^~Ij%?/^DWWW8pZ:VC|ذa4 ZlzB„O4ܾ}>Ԭ/B3yd 8FF,f{.XcϞ=:VRPP~ @VK١SNѣ GGG#U D/Ȁ?"""`jj7bĈRǪ򐘘dffJGc̙aff&ug#Qb W_}L ?L&: [[[#-- 666R!"8pr+WDHH0o<;͕:鉼<t3KTH 6 O>LQK_DDb1KT.]B~ggg8pR" %"f@T^^^8{,^}U\v :uBTTԱ<DԸqc8q @JJ z쉭[JNc1KTؽ{7f̘gϞw V3D8f[L25j֯_SSScQ Y""b%Lu@~~> rf.""޽{#::Bǎq5cQ5XZZJ3KT޽PTؽ{7u&u,&鰵 ͛R"JcK`1KTE2331zhٳX~=F%u,/~!/^,u,"`׮],h HTE,,,?"00+W;#Wb Q׳gO#Q9dgg#::?3;w5P,fVXL2 .Dbb"8Ah"K &&&طo ZUI&Bxx8zGIطoի)hsssEb[頖' '9‚ր71Q5ٳgر#N8!u,zISsYYYI7 Zb5lǎ?~z 6m:p%3!77cǎży(T""=‚0%!FFFXt)֮] \ 999RG#"Â%a'Nā`cc;vp""=Âְ%@љѱcG$$$H ZbH"[֚s8~Ա pj.ڎa$԰aC?~GO?^zaݺu3fѨO> ]zz::v R_>RRRo>,[ s̩T,f$fff;w"88_~%Ǝk׮>L&:QyJY,XfI*wYܾ}[^ĉ~z|8q}`1KjȐ!RGq%_Y깒pZ_s ! \c֭G}ٳg+V[RvZ۷{R,1OeBa)ɰ|r` ,3sE+** t5k `ȑU*.UY6:FմiS"66(>`Í7p%a…eSRYn݋{BTVY~C~z̘1ׯ Eaذa,T8 )hSRRU'TeNBBtɓ/fylݺ+V@F`kk˗Wy;سgK2 2 ƚi ^]YWh׮ EW^zAR@W 888+OIץ+%nݺ/Aظqcs9xxx fSTY?^2 D۶mKmGu?TG*/pj֭[ݻUm۶=:Zw)@!ĢE<_999Ye\.Jlů֗N g}Y"//ODEEFiJ%ߦM"rrr ֬Y#Ddd/Ķmۄؾ}&se~%z4ih׮PT߻ͯ+O֭EXXk׮z=ni˺˗ 1iҤR燐ut)m.-DpppbٚQX !Xְ,f V(t[hҤHLL,m]KyIKK;vt5kֈ͛ ##rߟϺW("33RΠ~V^}()N+_WB!BdffzPee]onnncJ*:a1[{pQ-ШQ#or`ڶmdggCyz…bZ_@rr2T~)S*3gbڵ횘͛jG}վ;6lPӞIi?KWѣGx~wͯ͜+oߎlkÇN^]/R ,Ju:οG'$+bl(3[=5*{fP*Zo !ĺu넅Ect1uTaoo/ŋK̇"=q/~H*RSS+sVڊ%Kh;c }+Kе"]s/tܴihժP(MٳRu剉BTٳgkև [[[RDHHHҮ"c]U뤪gV*bfT &L1KDŬ0ZC.a1KT ^ D IO#2Powۗ)b<&""2d,f T\\\$<<+WDVVV5"""Y,f grJ5&!""Y"="ɴ&&bҥR j*uҁ,I"** t5k `ȑf dm>}jYĐ!Ch" 6 ׋b.Fvv6̤R̛7f͒:R)uzlڨ}:}]qǏUV6tL,--q9tG%""KTKg8aoo/U՚YO8;; +++1o<=YYYX!Bid%(鯃@bmrMLP(4y/_W)Tؓ8x`ѹsg@؈CxƞY""Yѣqu<{ ;8|0rrr&߿?"""}aٲem۶mCvv6o@۶ml!PPPpwwdž J7oZWyuQ(Ewqq tss5 _|8ѷo_V """}b$oW^ħ~ 6 6 M4d2˗/lKHHJҥK5-[b i7b033Ӭ+]k}i筈˗/V\KBRu寊,u,U]Qc1KD$qɓ'۷/֮]+u,""R%" SSSlݺsE~~>{=r""['$:M&O?fDlٲR# R u bȑ#1rHcP%5 ͚5àAgt GÆ F{cꘘ֭Q Y"ҩ[n8s ϣCؿ?<==FkٲjDO>_XVYrHqvvӧ1p@DEEK.лwoIO:"z9OpuQի#G`ԨQx)_g:\DTd2:tq!++ }'e0XQbOPPP{ϗ:Q~zbM&aؼy3LMMrJ <RG#"2x2m%% 5j9J}[n{Ա  ڊc1KD3gY3A\\Ա  ڊa1KD3gΠ[nHJJ"##EDdXЖY"z)* Қ믿:Vz)O!".,hˇ,™>S`ʔ)zQbA[6&&*!0w\lٲE3Ӂ222FDdXЖO#*5rH4o~~~@nƍKHom۶ jZh7n୷Bbb"4iRY"r3.\@oooܹ\İaÐǏY"&Z™3g0h 8q]v?>}HH8::ѱgDcfڨT*>|cƌAFF Pg:#گj,,fZ`Æ Z3L: f:ذa!©,--CH"77_>d# _54U'DT g:ضmJ%BBB b~h߾=vڥY_85\.8ػw/:tQFSN5TÍ7p%ܾ}<:DTc#Gڵ+Sƍ1n8\pCEz]ؽ{7ڵk???;wAAA0ٺu+VXF˗/l[z5իJХK'OƢ]vP(%>|شiSy ?-hѢ)d06679RjrbeLYf 8w\)ⶒֽk1bZj#F?&&[1Zn2:7QDD5?nnnhڴ:No5jgѯOJܺuKUڮ\.yyyk֬YYY"22R !xWExxB!ڴi#BBBDNNNv ooo믿gرbݺuX~7n<%5jTjr?ɅDhhaaaӳk(Ҳb"++K R󗕇*SqqqRG |u$?~,w.KKK?KI &+ZK/Z*R/eiҤHLL, Z, B}LYYb;^^^~">><[l۷ [nBfѼysaddu^]ʩ^VkYYYB!qPb?uPK닣GjfΜk"##׺וSÇN+Ӷmې۷ť^޼yJ+&Oxx&Oi"NRUDD>s!1yrرcK]z]u 3SNJ,^X1!!A RջWxyyizN_U4%݅\."::PgVL "z;0vXwعsgS_Iڵk@^^^mM6ŵkנT*%HVsn߾͛nݒ:N!$NW6m8xzzJGrf@Dza8vׯHÆ +qG}T Y""}bFN777\x:t祎1gitrr¸q$JDu{eJbJV7xwE!u,>D,;;;DFFjtrJc>cvvv3ftaj`'l{/#44VVV/X@Y"Kظq#  SL:ڴi}2좲" ׇ5,Y"uRIq)9"-Dj5_֭[c„ ذaO(tZl ܹsG/5,,,ЬY3QFRG*yƍt̰pB#dx{{KH?I9/QQ)))"$$D¢\OW_bرU6oou3[VZ%T*}B˗/Ν; ccc,N8!… m۶B.8D_j'''Mׯ '''^SWγg wwwT*EPPh! Ӫ/l jٳg ]}u?uKe)i}EKիW '''ueB!4&.m uWѵi8Ϭ6.\KbP3pss 4hsssNU'O 33pmlܸ7nDƍ1qDL<*J$''ѱm III8qFcԨQqq?W^Ř1cw߅V;/_?Kٳ<_1qD_=zlBsI9Ǎ@17o;~x̜9غu+}]:[xN]_>߿RYZ;%l;/udΜ93gVZi>~.̙32d޽sWѵ?Utu4u0`7BRɓ'H!ujpr]SW;TWtE~jEk֬͛7fc8y$T*6oެjƳg0{lZ 2 /ƬYu۷Ѽys8::֭[՜tiӦ OOOHQŠApI4k ,d%`jj !!!d={6[cUY"'NıcдiSFJXQJIIik׮.=W^HNNFPPq*,U#%%C q2 7n56oތ?RGZN DTm \R8CF駟B>L8FTT^uW^AxxԑXHRb7|lL8M4:bҤIhҤ """AJ_Ni&k!CGZZ9#GTsB,Ulڴ FFF:uq x!Mv܉DtAAAHLL,uKKKܺu /^D qF{G CBBТE ( d2zLYf 8w !!]t \\\pI@ll,ڵkim"ɰzjԫW* ׯĠu066F֭8w<<<`ff5C'Q8~ u&u*7nɩ0C31i$!KիbҤޝEqe64"ш 4 '1Hhd2׸m4`f&#!(8&ODbDM4A͆44MWu7r>ã]֭soxT ~ (,,t^ bbbbccߟOqqqTZZJ)))EDDIb2Ͼq(*))Ǐ+W^k}77ֈYXXf  6X:f___@ٵZb2 WJmuH˗/dddI_6˅GǓ9::ڵk r8e2XĎ+jZrObOkɬ.̀1 N> ӟdH9 ¯|[:W_}ݻw7|-[Gm~:peTVV\]]q bϞ=B_|IIIAbb"l"CV#!!}")) jD*J|FS+&[[[ܸqCgqZۼ88}}} l.Vry4c%S*߿oP"""-^VYKmH=z\.'___:|0ĐR$f*3VVVAvX Pzz:PPP) Y .P`` YYY՚}7ZbߦsZZT*&JEgϞ%"t!BAWon<w1ݻwѡC<s玥af0n8;ǎ)uѭ[7ܼyB>21k ~ x5X(,,8::Z8ҞV*X83 'zW\\ p$\hC,gefߚs5d2>C˥dF=p! 7nlmmRGϟ;wXphGRRR[[[ >>^d‚}BV fT_kS`…P*P*X`ڱU(PT .Yn輈S֭[kWsԾƯט>///n߾ ol֕+W]v>>x{{#11QboعהkO?ڶ}4mmi1$=7 q6C7/Q#}kN(۰a <233IիWiDDhhԨQt9""|2?4ڷoO'ZM׮])S蔏7^y3f M0Jb.]PnnիԥKkʒ,7%ŋӑ#Gh߾}ӎq4?R^cRRR^#"QFիiDTg~H|cV3MNNdعkO,̚zj5͝;ɉ͛GjZWdY]2""C)۷ ˗/yؾZ{F||ԝ JKKammSO/x7 00qjw!77>}:`ժU8p ^}U|'8wnj30uT"!!Axw~3֦\.GYYCyyym۶)g,SL7|}"99]vGS#5 * .\@Ϟ=q ƍxg>:::"&&&M5~R1ꏽ!+++xxx`xW 3v.5!KzB)+V@vv6"Œ3_)D@@s]_nc^͠W3"2mƌC/_6{f-ZDPֵkW)njj߾Ό j:y$y{{SYYٓRSS Cbmvܙ]&|66;3+ii˴`aS]jؾƯט!/"Z^z%"";v,Y̙cRϝ;G;vxxiFP(LW>K\;kXJ}լgh_Cwss5ݹsgɶSffi|lگ!-v7o1h (J̙3'niӦFnt޴rA 4}Pwر8x fΜ R &@TbȑXSNҥK,Y_|2/^\soٲe6ltG۷>Cט!cǎҥK{ƌUVСC&q8y$ƌGo؆ 0YCGsKc]{OC ٺ}6<==Ϟ}vjrNcekkk"3Z5NT}~ТEǵkRΝI._.Cm'oooM~) 0;Sرc4p@"":|0O>DԾHKR ?>lmmח6lP]bŔxG3֭[@ڵkԶm[*))1ׯ_'OOO 7c~~>=dggGvvv4zhyhLԘصg?5o3o77Znnn&ewR]63;`KXb߿KO?5xeː+jϘ1~~~غu+]voݫ3c箠"cd&]0sҚZM-ٜfƳfO}%\NsNљْ={69::#͞=JJJ_ФϥsqFZWllY?xf1V,3ع Ghh(cTk޽={Ic17Z: X r1\٘;w.Z5t`1c[laܹ3Rxf1c5˗/P"Sfh9e1cM9 -'1c15{fL&U?U}e2ѣG:tH(ƍ[[[T*|GcΝ:<|0.\(ڇ>>>)?~8d2>}ZSV fT_kS`…P*P*X`4N]LBJ0r5 cce(V+W v2zS ooo$&& e5ϕXM=۴c˗V$dnD7n0XؾDDgQv턲 6)334 ]zONDDFEΝ#"˗/Iш}~Rt52eNqW^1cф :u""">JT_FP;v,ݾ} hZF .O?Mo+Cv*u֍Es 6rssiĈt]>x{{ӦMٳTXXSGT^^N{ׯӅ HR }h4ԵkW:>Jb.]PnnիԥKkʒ,IjbvM9lrr:Wn7r2|q2dHcmZ[[ mU'}r`|DՉ~y+X)!!`)_ʊzA?CDP m7vNaM_zz:gy`9'b,%22Ȕ*3f ]|٢"Zh}BY׮]ufڪK‘)jj߾NBjN>`?3L\IWssoz'EccM @O8e5SY -?d2DGGcfkoo͛7cx`Μ9ƵkPYY6m]2\|Yx4h?H "~\SLL -Z]v-uܙr9՚3Ԇx&\N駟рرc:u;F$"ÇODkl LT,--`rrr"'''?>lmmח6l@ 2%ׯ'=9s%֞~[RT,͟?Ν; ,cܽ{RSSMd3Dzk0X=Xr%lقXba& @ff&222_<<< ƍ-!cU:t(xYfڥMQs98y', 3կ˗/ {t8UskN6e 5ڍIǴ5ߟ>dD5~5e) “O>SN0$$$0yFV& :uŋVdZ[nΜ9doOC^s2krӧOGEE}KÌ@TT`ƌϟիW[$CAAc0Z Nfc f…ᅬwZ:&a߾}ȑ# +d2t͛7O?g-_.^~A.ך)5svvvXj]}^zzBzzdPzN666ӧ ޽ ߿b7ԗUV* Νl'22Lbѣ&O\#""о}{`޽5XLƕ̾+]<LW߾}}SSSѯ_?xm./h'cӜ9s͜9ҡ0;%6j59^NҿRJII!///"" H*--5Q^(66JJJ(::Z(kXT*ݻJJJ(&&$5k޽޽{i:F:uE}YFJbbbbcc(++Kgq@Ÿu?**JJJb5C9镹Wl]S,""&OLDD&MHxLwMu!CTcRm_H&ѣG-3`„ &L`9,Xf~IW.#Ʉ&_RRBDDBXJT&m|K!Ӏ襗^ަEdee :PffN̉f:ɬ6lѣ4~x"GGGZv8>f͂Fƍb~҄GkZy&0ЧO״4KTUUwŴiӠhj*Yk8e5'x_}F_~AAAX~=,Z?bȑXv-{aӦMYXkkZyd1֨' IDAT7DUUk޽PԩSҥ K,Yҡ1cd1謭;xg?bºupTVVZ:fѣG8z(^uxxx <<UUUx7CZ:V111ptt4XVkvYk%t֫O>HKK͛7ԩSX~=֯_gggpvvU~B׮]MK)--EQQn޼W"++ W^hV\nݺY8ҖIC׭[3g|iku1cu,cFѣG㏑/"--#''' >*^~e888X:$VCAAD1f>NfcMF=oÇWÇT|ݻ7ƍg־III‹/??zq( 888>>>\n2 ;vADؼy3Νt̛7ƞ={0x`v ={ܹs4h$ۑdXz5}xyyŋ9spyt{ŰaDm۶᧟~ny!"Wrz–-[0j(z5fj}ϝ;3g"??/<hFWeΝ;: v噵)S5PM/M@QQQTRRBǏ'WWW"2mY(33Sh;h֭TTTDDd㍽'^=R*3XzpmB0XΚ5ˬ95--//LDdmm-?rv )h4:xċ!Yrp2Zm2˫0Z{!**Jgہp5 ErPՈw6֎}")) jDd_|IIIAbb"lX|9QTTdv[[[ܸqN}1F,v___$$$cXk,cUyPXX"j]ك[۷oǞ={`۶mh׮d2eulll0glݺɓ' W\/\*Jݚ1=&&7o _^-Z k>qqq2eJCd#44!!!ظqabCEZZ2ZH,TUUaݺu&cmm :1,YXPXX{OΧ~ mil-..c'V!** ݓSUU~"b1V8ex ֳgOp9r.\hc'/::Z3|8-c 8e1f~O?`1?c1ƚ-Nfc1X,cU˫ﲪp$1'V@c5_2Ƙڴi(++p$1x5$,]jafjРA3gNOPJKK혌1 d5 ~!>|h0X3bٲe1X#d5)7o`oa -c2̲&7dҥK(,,D\\'1 `foسgd2/_m۶h:<c gfc-y4C۶m['N .n30NnݰsNxqbpssÕ+Wt.\Je8S_Gl޼...~I[ZJJ f͚̞;w~~~êUn|@^^<<<5gcc>}/ݻ mbDDD}pqq޽{.\ 5CW^A^.9999xgakk ooo|Hxxx@.ti#F, bƌӧcѢE/^~ i(nCmy* vvv ~b5s`u.־TVT*Ν;'l2y۷ojj*gtqyk,;1X5sn3`,yAQQ988ЯJTTTDDDzX*))hWb̚5vMh޽4{l JKKN:Q@O>C5EEEQII ?~\]]( vEŴgqRh޽TRRB111'98*--""" ,*++ERFE-QF ̈H*--g1/y#v^믹r՞\}8cbbbcc_8_fZ4yd""4iEFFrFDžf,k8ef2{@O<%MJJҹ'8))5iK/LDL@С{x %].))1)5ԎNHKOѣ4~x"GGGZvd<*{ϟ?OFr*..ɬX΋T 87:ku.5bqּmll$T*)//J%ݻw8ܿ:vht\Z~2˷0Z%[[[/h )))!<<\@_s  ɓ'1i$|  }'?ϸ}cwYRP8x6F||_A_|IIIAbb"lb4C#F,Zsbܸq;wX}J>u'N{1~:/R:/bqR Z ]bKW;n ¸I}l F &`̘1h׮x^~g$1<3C͙ٚ7י3g 2LѣeddQFFуLJ ^Z.LJnݺE¶דR$RI6m28KO=0_oVsժUdee%X;iiiRښT*={Vrrrr((( E8yxxЮ]ۛo)%xf͚ESN%")Sտp 3}Z111T*Ņ"##u /v%v^Ĥ7) )_sK1A\}jŊP(LJӉHhdoooy!":q'NۤaӦM5W)VOTÇx?&&˗/GQQIk֧ɬ5y!J%qM>XCbȐ!8s 49sC i蛶*=zζt85g:u*{M^{5Ǜ4~-[nyf#D׸uEXnΜ9>}48lOOOO3Pry$MաC,B,k ,2?t_-?ƚ Ȥ5%YPlΚmohF2%u\cUd5sŲe#,]c挶WZZ(L>]~PPRSS!p)>ٳgcŊwza_Rem۶b Gcqp2˚\L2vvv:u%XLsNt2d $?sHJJ€pQ7xvvv3f ~w˨L<vvvx׍.ePЈt\c,k6]QԺ)ӥbe.ePXXhHc`سg͛oSӡ{yyaĉ 1Z9Nfc ?!| W2Ƙq2klY͘1={D1mڴ1ƘddOqk;Μ9,梸ؤ%QYY*rW"PYYi0KqrrB۶mJ!Ccǎ1'&Ν;/=ctd2bɘ6m,c;NfcpA\]]1tPpvvpMۣGPRRW";;iii}6'NDHHzeHcp2B]>***T*1k,L6 }<&"??a(JKcYƘE;w&MB~~>rJ,_\x,_EEEرcPRR.] >>Cth1X8e5x̚5 ѳgOK*ܺu 3gĩS 9sX:,3^1֨كiӦA`ٲe8}4'[nHMMŚ5kPQQya֭1gfcO>ɓQUU-[`ٲeU۽{7.\*8pSNtH1f6Nfc"++ DII 6n܈Kڵ ,@6m~Y:$3 'W^^~˘;w.cX o&{=xyy!## !1ƘYX۾};._DDDX:gӦM4h]pKcfYXzQTT4hCbdddA~~>1l,cAEEEѣG9s&'MX@@V}vKc&YX;ܹl^_ {{{F?H1ֺ...Y[:X˕_/'@Ν?| ꫯJ֏]):ZC{.R2WscggP1pBk '&'3|0'xaED:_18,cI9s&>#szz:.l1sLd2DGG{E߾}q% f͂=|I9vii)͛ggg8;;7@ii)gϞrPw߯\bK!ƿ}vtX}t;v*++:m۶4iέc5/?cMʸqwΝ;gٳgTr5##ƍ3W_}g_7xC(?w"???K}_ŵkא[n'`?3/^,$OƘ1cM}o=t'''^@TT,Y w}o߆V^mR{Z1ƍ %KhDD4tPJOOg}hɒ%Bݚ?!|...&\.|ܹ3]vM|U(77WsDDH'N$"pz'hϞ=DDkѣGLi_/3'777:wN; cǎs7|@;w41q!Y~#___@}w6RtM1K~XIDAT̜9f‚ p}<3wʰ~{ 7@DDݑ 6H\3ƚ>???4`$?kNGa?O(++ԩSヒvBZ6l@ǎѳgOnnn׿%=oI&G6ڗ憯  2#F@۶m1eʔZk2ÚTwy憓"pL&Ç~Xd2X[[G8tPVUU7PTGΝ;uya,\P)))-|||S~qd2$%%!77}uZrss兜%~dgg <( T*R(h4Xp!J%J%,XFcV<ν~\5;lݺkH*ncok<h4ҥ Wj4Zkּ ݫZsBQRR۷ocʕ:vvvţG# /o(//bС('DVVBCCq h4dffbĉ&X4 -[: 6mtHgl8Ii^|!&qil"?#ܼyN#88Xd$%%8y$ ""GT5dڴiXb=zcǎرc:QQQxWsNx{{˫Vw^ <޵ڗ2vX|Wtkkk$&&N:?3<?FRR~mlݺuqrssqUaݺuO-0""B(+//ǁDoG*ncL1k KݺuKXr1MЊI -r 3*++k7/QEE}gԵkW[n-;whذaK#F07mڴΞ=K:eyyyOԻwo~:]pT*'FC]v닾z׈hԨQzj=z4M0Ξ=kv !++t"|ҥKe#Fq-^9BDDP!G,,>ܹ3~Y#\N~~~:ߣ I&'M4Ў;Ņڵk',-￧}u%$)00RSSu뷕M&ӧOKqs|74>~-T*R(rJR@W&{{{ӧvՏݻ f,N%u\v }63[5d@ƍ3~Klѿ ]۶m-[l3e_W޽u֡TQQQ^M.]"BA.]2ڇWRpp0 8iBʕ+СCDDt!ZbU'n1ݻFQߦ)}<<sk׮:3nbmBD)SԾ}{ofRtI2ٳ0 \cc`j_jziժUK/رci͚54gΜ:ŠlpC ל4%1`|i א۴i3_XXH B2>C8mcfښJJJHɁUǬrr9׊3**ʊL&mCN͟5"vG.TFbQ?~V|ӦMõkPVV˗/ ?SLePPP z{{#>>jqqq7+N8RٳGL.7}IJ>@aHX|9QTTdmmmq }ERRj5kŎ[_#EBBp\c~ۍW/$ 11[l1aNߛ|\Cq2#,Hh5۷_C-"_#]:w,܇o06SǓ7rO? @ǎө{18p >|'|"z\cc`j_ju|""vmV߯1h֖|}}iÆ :3LNNNDu)яM*n:bא';;;ѣG͛7%3gf---JNDDiiiRښT*UM3&&J%Pdd_}o\pLדR$RI6m){7IPlɡ a/6>C V^m4~XkeeeEk.8 /됈hdoooq )gfdD5c$<< ƍ-3nصkv܉7b1cǎa7n-E5q[bZJ?c1Kd5+f$P([C rI\FD?­\fHBv`f?T: DOH2x* ^uuub8::zw=TGD"QRR":CYH2jVnDX,_  " eDD?ǕY"CWWjkkDPXX(:~ Jhkkko3 "_3 "Iuvvb s'fffF.:ø2KD B0 x}}asppɄgh4N""0䚛rNt:qqq!:ruu~<==att,}:\%HӰlACC^/Dg}iDp& >r\tQF8Q$I#Cakk Ft֗tyy ͆H$V]TVV""Q֔c{{X F׋VD"455s%O,eUuu5^/z{{q}} ÁAފN߻0quuՊ@ J%:_0KDYWZZ΢ tP+d2)z#??pP*%"8ưt:l6XVX,t:JP*)<677L>LNNFt&oar 永l^^j5***PVVo߾ ]///H&H$HR_+//V~f(<>>v#}7r9Z-:::`ZӃ"YDD44IENDB`./oar-2.5.2/docs/schemas/hulot_checking_process.dia0000644000175000017500000000557011757171206020346 0ustar plbplb]mo8_!H7]tl[Mvd(6*K>IN/ۏqĶD7ni A|Dj8pfHI\,䨇:(>}@~{( 1?,I^^΋b%H3G3qB{A0_(,^}7,,::H‰>Ꝇ/,%^U.7L4 .~zu5깧i8֧4W GMl4#S)PO{L]*7'ouQIXo-{Fbf(e':FTH2 >veۅ4͊,i0Pl7ɇalDfk?"}8_g{DC-Ѩ8|u]UWj4˞>JΪŷc5F1iϢej:>Vւqx߿~ a]\_Iz=,~(df =C FGvg-6Tfޚ7" }T/v]5szv87HHHT>c@R.3~d`f]u#-U1wz^` )1Q juy T !!Jf`i6CM3oVDD 1?O/K8yJ&Y8fysz?jL0H?(E(EG)#`THcˡ(Y@(-ny@-io[EaxGqplbbiR,C-g$ÅI FCW_} ]Dpw1*mz[*גMnӊ{2}Lwd ߺq-# Hۣ8imO~g]'87"$"ߣˆ*]S+..7V]kB+$¡.B oPa1uiHO]V9a) 3e386SmW]=:H!9 5a!@]0] 9/=p) 5}.`uM?@a.\I>P}mѨw+_Q_3;3SCc.+sKJ=R Bu0BU/IB+ e.4dX\ҝ-󧰜dD|`L Z6Iy*DY*Fo,kXYQ}OrGL?W5'ó_qago_}ӹ9av;w7JW1ޤDK@RrnXkI)j#9TyTN ?{s={Xz-[`N>*7"5F}a$d5!h+f29@3=m7$xH ElDשYpD^4AYG-՛(M(>e#nXV`$ ڼ?Kԫh8J="RL+.&K$Z#aL} .{'m\-aR9eR+%b&Ƨ[S֋(nBeFF=`Bc@3y~nZ͋)8M#\I45)'4!cs)N&mjBeUDV GQ3ڹ4A 7ّMD;ea #NAB">@Ym8,7Iv4-䬁}e>f [Ih0?C] ذP@\+=+n$-'mL\^{xc D\S04{4-?op[gbXp2v5&CKW,l0K":AG9 MRw:Vqx{ՅgE*]./oar-2.5.2/docs/schemas/hierarchical_resources.svg0000644000175000017500000013031411757171206020367 0ustar plbplb image/svg+xml NODES Tree example of a heterogeneous cluster CPU SW1 SWITCH N1 You can configure your own hierarchy with the property names that you want 6 5 4 3 2 1 C5 C4 C3 C2 C1 N2 SW2 N3 N4 N5 C10 C9 C8 C7 C6 Resource propertyhierarchy 7 8 9 10 11 12 CORE oarsub -l /switch=2/nodes=1/cpu=1/core=2 This command reserves 2 cores on a cpu on a node on 2 different switchs (so 2 computers) 13 oarsub -l /switch=1 This command reserves 1 switch entirely ./oar-2.5.2/docs/schemas/hierarchical_resources.png0000644000175000017500000230336511757171206020366 0ustar plbplbPNG  IHDR !xdsBIT|d pHYs.!.![tEXtSoftwarewww.inkscape.org< IDATxgtT+IM:& D@TTRPz#  ʡI.BO@/'SvZ{ff?̵4e4[3RL4tP G3gNf_ &M$IӢTh̼ eiF|}}'ݹs/pZ``"""̙/5l"p4F8 0iN8pa#p4F8 0iN8pa#p4F8 0iygt@ll@LLSm۶)<< xz9ݐ8pa#p4O>q\`YJ*e248?ni{L>^|E=쳖`=ip 'Oɓ'gQFzՠA ܼySqqqs̩|eD߃gv|ٳSS///?^C%XSΝӭתU+]@磏>Ұat{yyiƌի˳FFFx↵XgW^ю;2ۮ];-YĮnݺ:A+VŋUjUPʕuرtK,Qv  /h׮]QV\遢uѮHƍwY2/E[7nF4a?> t,Uϟi;>}OTVMʔ> ztlݑo9s*R@@iͪ~7U^`$:tHkւ ,Ibiذ%4niĉ;wK}RSS5flҩ`ڽcǎUV2ܭݻU~}i֭[jժL5k֨N:裏\ꓐ={W^qNIIW;P0M[GΤ۳gիgw{_4I S]sĩSO:LOg5ߥe˖*ܒݏRRR\?g5mT.\pW||:u^ .TΝr/uYƍSjj˽~իWY-<<\[v(gocƌjѢ"##ڮ h֬Y̳zjl-{L>]~K=Ə[4p[~?uziDpBK{C$mVW\q<{K.U%a&%%E/{:q}]^{oVe@] e/ٳ>/;> ,'x°4^^^jٲl!d+;wn SzgdӲ@RZZuׯ[A'O^MII+N|fRSSթS':uޒl2-[̲~۶mlm۶GzЕ+WԦML٩022 ᆆ_^wܱZ+V0\oРyKd;˗6裏ԬY3[2ŋ0aiOeʔQ``UW^?oWxH" Q``J(!EEE)22R7o֞={, }嗚1cFǕ(QB!!!*[+$;wN۶mڵkm3zhuAKdW_6m`+WN Ç_+66ִ Կ͛7Y/^{ϴhݺu^N:jڴʖ-+???9sFҥKm/k޽:JNNְa kSnTjU.]Z~.fy*44TAAANveʙ3{9լYSŊ.\0-^wGY`>|%JL2ɓ'u̙?{Y޼yqFooowd͑#GP{͛ڴiZh5ܾ}[6l0nڒk tZ'(,,̴#F (`x̴i?o߾?ꫯ4p@UP&OlZĉo*W\JLLԪUgi͚5."3uT/yyyiذaz?t)Sh…8pL >\Z440:uahYfӧBCC׏눈?3vX]zԩ#F($$$5w-YD?\\\jڵk?SO=ezO={Vo.]jXOLLԘ1c78=kVHNN6%z9s4h`xرc5aM:մ 4j(UXѲyMzY?^և~hAAA2e7onX?{Fsؿ~u`t\5J׿AiƍիN8a7))I~g{wm﯉'k׮3ѬY4x`ɓջwogDDDh֋)޽{7TRKJJҼy4v?9dȐt}O?zΜ9xb?4Ν;k˖-*SijNw_ᄒ5k֮]vJ.9s;Ǐ?yh۶mj۶`t^ի~w-_\*Uy?ϝ3F;vO?m3%K g͛7OGqjάra9s/k<6mdLGyDSLь3iUj3 {/yj٦A>HѦlܸ6olLwoFƍ91cbG,[/ַ~w}0vOƍeժU.8qBg6?:rkL~O;wT% E:rƌ.&ɬ[n:qJ,i, 1\_reXbzɒ%MLv,Yxzʔ)׫J*v U`AŋkϞ=JLL4-X$+WN-/e:t~h}Xhڶmk7o6}$iіF&w}||n:v7o^ xO?9r84BBB~1"P4m4ĉ5vXz;8pa-55UݽÃ;>Zz>}h„ y… .x9rhڵѣ]_?zi6^5jFmZ Ӽye߿3fsv[P!_4rfFaשSGWVRe˖?S]vͩYt=&I;w֪Ul%s8#44pԩS9,Ʈi.V4c zjTHzFEEO?5Ν[i8@͟?߰ g۶m5ҳ>\F7o^xo߾?=zcx6{?~\VrԩSOc߾}Z|i{?5|}}{9zuMZ^4l0f4iz)?lzMw/RsvAhsDWL:U.ߠAժU˰G[ IDAT/())ɮ>w6ݍӊ$UV*RHDK!CX++3^'&&jN͛ kf;Yp͙3g[k֛ocsʥɓ';|K*55հ믿p?3fݻwgx۷ Bkm|aCʟ?x뭷LkVhԩ%}l6h KT-Yİ֭[7ȑÒ4o\KǏ[rҽ{w{<*TaАU{5lP+W6m޼ٲ˫aÆ.ѣậP҃BŋWHHӳS۷7gҥKAj R k0 UZU :fHEGG[vAz,s)%$$Y,ҠAKws2?4v;s=XuףaÆʑ#aͪpZƍ-#O8ǏWRRabŊ*/_˗/ߪ,TZUK69{ھ}eX#F8 npꫯ_ 2b+b`qEٲe vlp˳9,4duiVD%P&Ijղ0˝dluVr wq Yu YY'O+WΰQpEl;^'f,XPŊsi.#fZ@@}Qz+V8,Ж7o^=D8 nm۶ W׮]]cN+PRo#y5\y^:uLkW*U=QW^իW kfAggEhaJ*Yr g=_0;Ylyxx 8e$iwV ׭fb|||A|Τ!2#|i=vܱc_P/@[-lOzRAR\k,oҥKnݺC5jMcV?LkfgwyK DDD-Zl׮]˗ k,:f 8VvgMMw~sg͕+Wtzgeh7]n޼R__Ҹqc[OIIњ5ks޽۰ֺukv p;iiizui}Nqk][Qb^u9xjРAjݺ:w{NYͨPBW\\CsYWXђ0{%iZ~$9cΝ;JNNv]|||TxqK{)SppU3nLF=to:3e=kN;ϑglFW?NN>>>jѢ,YrJuѮ>WVjjjuj9Wׯ{hZQTT];zկ_?f1!ٳg5}tz:ÞCӧOשS2<6))I˖-Ӳe&Mcǎj׮2m|۬'4)wܖ1z-ye+PJZ󓇇=v2?aսjg#؜`NzGB???yzz2\4{>Ç[2=\s{3Oό70^bzݺuUpag\E8 ٳ~ rVX)Sѣ 8PO<֭,V1`}}}G-[4 %ILLԚ5kf[ڵӈ#TJg͓'S=msx#fx.ϡʕp݊*qkbZ"gΜuV%wy`ðիWk.=6ONN֚5k k[lNodB k׮:x dz\bbz-9| [;6edk޼fΜun߾ (((H;v4k4.lYJn2i{aռf84pVF˳siv)\իgX[reo߾]ц5ipӐ4yd1{jѢEv;gϞ;wKT-\PժUܹs:pZ𰤏=;KH]ee8͊ʪ{5+if;4ښ^.Vˌa9,(G8MJ ͞5jvZ%%%VXa3ϸU R`A!/^f?ԏrx.w裏o߾ڵk;ÇTRwmݺ1111Ϛhjqqq=ifsVs,Z!>>^iii4{V)bg̍7^.xӝ19Ykܸk}6l`ثu֖pN߾}aX[h͠~k„ :s&L 6Όhʕ+/{#aNC8-,,,'lKLLԅ ,i?sײ՟e/|=|}}ռysÚi7n4Up 4˫QF۷okܹv)Zzxx%wÇk׮]\qׯ٧dɒSN9=ӧO Ncn~gX3Rf*[ŋ3eG*{,XP9r08q"qOgJ.mu\QD Xz-׷PB͔ޙ/Xbw {.]Z3m۶]t1eY8m߾}aŋo3\pg=l;+W47fAHwzY˝if3n:ˮe%%%֬9M6nhIsўpi;9s4\rhh+ ~yQF2e.^{5w} {b-[۷o;=+[i-]B# GݸqC79Q O>\ڼy%}VjX۰aCgk d$~zu___.]ڒkX!*\amڵ]gU4Qኈ0,̗/{pB&{0 d !!!7nswQ_fi"ոqc,YҰvEvy' cbbtRsg3 neʫWimժU.uϺu딚jX_}ʗ/o~q95~G{d&M߹sDzQk6 U|Y;*iSO=%OO79qΜ9c5V^m#GլYӒkXӛP^Æ a?{Y8-:::f^J*n=!!ALCѭ[/ՁxxxsΦsfأSNٳg;5"w܆y^UHk9si-44>fׯѣuԩS.:t0M2% ';i˖-Y<ꫯ\b ]tɰ6mژf͚r۷F) kHwtVK=3v7g0Npd{ڊ+/())ɰcN#k֖-[8WRE5j0mذ!v ׮]3\/P<== r!믆EnݺvzLk7ntt4iKg5jbŊ]ve=oY4h%;=V^z̘1ôt7ٳgΝ;.>}k[>s={hΝ-Z(GvyTP!7|Ç;=uʕ+rJZ5Tx p4ʕ+VZ[ni0`]:tH  fx~۶mMk|K'BCCawB )oٳg{nimԨQJNNijժ{cޯT}NQ/˛S…ՠAիW_;;,,L?ipڢEtE裏Lk M.ʕ+g۷gM4Q>>Сi}:vUci۷}3ӵo>KMMU=L^x2s93%%E]v5 a֨QCիS[N}a]l}gnڴI|Cq%uѥ^f+T kݭ͛nݺe;N۳.۴iSᅢEwަ3gjȐ!.xKRJO?*WO>Į3r}dž~Z>>>U>}LSLѦM+**Pre=s-[۷o;7p* N*VΝ;kΜ9.]Ν;9rFi9C Q\ k׮]SftiիzլY3:uʒ-99Y]tqxg?t'ߊ2Eݕ7o^ZlluN&MҎ;LV3|rw{S=L믿 *PϺu 3 :Ti&--MfRJLClQqqqY6Ghh(P@O-Z4o޼ {7N7O>^,Xu___gr{A9uZJ5kԄ  jРAH5lP?CVr:'<ݑ#Gx/5x`ŋ4cfʗ/ `Z߰aڴicԃ&Md30YbEu9<]_͛+""°iyeڴi6}ՠAAIIIoTzuάְaCsΩzڶm[Ѻuk-ԲeK¯]PxLkskܸq6;w*U9s(&&Ʈ9iӦWvuVγ_Wr4e={jС=>֭;{iFKW Ӯ]T^==zٳg\tIo-ZVZ*Z);wҥKtN>7 ϟנA4h *UJ͚5SժUUH-ZTyQLL޽[?={B 945sLYRRFɓ'륗^RJTL]rE'NO?ӧOgx۽aÆM6͛7ZjjРThQ]zUgϞՖ-[Lw7n.]\m?.^hz̞={Ԯ];)RD*SJ.-___EEE)22R۶m֭[LsִiK/=7p9sp `q ֭6n(8yy˖-|RZ4`,_n5Pc)SK."|'Nɓrtncbͱ{nJ0V__ .g,^XӦMC5!!!h֬nݻwucƌQhѴ:`ʔ)rquuU>eDDDDDDDDDDDDDDDDDDDDDQѷo_ⴝ;w?e˖ſƍ#..NY<<< Ԏgaar]AS^=lڴ ={g#iӦ!..˗/v*Z5kMp:::ذa%.cϞ=pqq%m۶i\.]ХKIQF8q$jԨuy~/Ǝ+VիWZͣVZCTT6}27@\ ~Tf@@1s nNDDDDDDDDDDDDDDDDDDDQѼys-[ѹq!t98+WFHHZjdM]zTxbhB~~~uv%QV@gK k;,XVb>{{{I*I&i;/,]ضmFSü4h ,رcՎ5n8KU*VФIJw֬YСd_wAxxZqllleIWѸqc޽ݻwGffdqh4?uHLLj.:tʕ+EYFɀ;QSaIWnX(W\ѷfΝxYۣFiL&Ý;ws~qqq򂣣2ƛ6mFi!#""""iݻw5^lYG iӨE޽1|͛7+T...}6W4>}:4h w;}}}899 NjLLL~zO۾};*WYf!;;[X[ƶmPX1Ir1g\zǎSi}}}M6UL84h%i``իn݊f͚aرHII$fff9r$_a̘1x-[r)S`ƌfjժ+W wQ+)V\ ooogeee˖U)FrpaTPA>A]zGXX۵k;t7?~իW4;;;W~ݏ]%0Ȗ7*_ vVDDDDDDŋ8;s],Z!!!xEWVi`֭ 5>sL7ƍnԨӈS}o߾sHHHP89իWhѢj禧www u.222q) FFFj|tY ӀŁӧOÇQD c#$$D4СC1b+V @~$ϫܹ3nݺv'=SC߿wAVԎóg0{lX[[K7?,]sQVZU r899ҥK߿1*WK.KaZ'''?5kTzZj\2jժpf͚CCC.\CP~} ?jBXXۇKr-Zvc4RŽh x)LW lcc"""""""}cƌAZa ӈHyFJsttĴi\]]5zUb…^zt!/wݻwc۶m8wd2B={O?rטMiӦXb9C!$$^R86m &(T*www>>->L,]?#ϟK.ޘ9s`\=Pj\>\PDQ ũSn:"##C}bŊeooGضmvލ8/Y$<<<лwo4ozҤIر#&LÇT޽{cҤI[E}JU@SSSlذCիk._ӦMnݺ82°tR,^111rP&N~:? ;m6\~]ѨQ#޽;,-Uoo*Uo֬1U OOOdeeڵkDbb"!x=,ɱα8$AZ=`YDmܸ/^vy0`4h4+ҥ 9Ty:O|]]] :ӿ3i"::111E\\amm kkkhРԩ!k׮x) ʔ)&MI&033H.\p.\@dd$ KKKUTG`}޽{ ³gx\rh޼9:tVEaɓ'Ftt4`ffKKK,YՃ+/y{0&&2yXdIԭ[ 6C䥪۷w޹ԩW3:t.]BLL ޼y%JB pttDNP|yM^ $''֤lٲwww899iUTs۷dee!44gΜ۷L5kDΝE;gĉ_gIII5lll쌆 f͚000jߒ 5nllxj!+*wt@>}F.h׮ڵkT\r>|8T¢觯mۢm۶NE.ggg:rBBBǛ7o4RʇlBZwXFDDDDDDD;~ʅiDYFFsZ i_BB޽{kBc'""""j;""""ɛ7opY9|Ά x]Vdܷ=QߵVڵ Kſ;SNH۷ovjDDDDDvN#""""GOfXF 'ǎpDDDDDDDDʺu]&:o``ʕ+jժX"sw?9,N#""""GUVE9""""""""of͚ c>fDDDDDDDDDDDcqQ>IOOÇ<==9""""""""okԨ .$3"""""""""""j;""""EHHt};p]9___i;۷ ۣnݺ Q5|#;;UVkذ!6lؠV|U;I#<<޽5ndd^zi!#""""""""""ӈױ~nݺs6DDDDD[ҥQtibdggΙIDDDTuM+W9"""""""""" """"L4 2Lpo߾ ѷŋŊL/FDDDDA2 ső#GWڵksVDDDDDDDDDߖTqiӈ$OpMl۶ ?#""""""""8H;XFDDDD$CbΝJciik(#""""""""Xqq>gBDDDDDDDDDD}vDDDDD߫ѣG\izYYYN4"""""-(UF4TGk׮ """"|\pO>(Y$J*[[[nuv#$$ϟNj/888uhӦ )r>|gΜAll,PD +W5kD^Ԋ/$?8s >}$XYYRJT4%sy\zӹ"EvvvZ <<QQQDjj*ʖ- {{{ۣEVS-4222pU\xC||<%K~jԨZl m/g"66(Y$PbEeʔZ~8tÿx/Uԧ-[-My9=ŵ?<ʗ/OOO,YR۩~.]Bll,abb4lÇW)paܾ}{ׯQdOk 7o+<|“'O8XXXlٲhܸ1ڷoҥKk%71޽Ñ#GO/_x^gnnnhڴS$==8uÑ O:vuB&ɓy槟iLĻPvmkڹ,++|YoQ^=4kLkSa3g|z_C||c8qyVVV?777\~S^~SN… x␐CCOUV 5kքӖ\aXg8qgϞō7t={DÆ &iQr%""""̛7:tvDDDDD;vԩS[h=zL={ 33S0f`` ڷotqZRRmۆܸq#ѻwo 6 իWWXx56oތuynoccooo 6 *Uʇ 5Ν;uօ/ g;vLpV)?s>>>*P]vaݸyBB ¨Q$az˖-8w\֭[k׮Ư_'ѣd1W^ŋ#FvBVVv&L!C0m4In{9ΝkK,5 L2۶mCFFF#GtttйsgL4 W;Omxէynocc~aС~vV^۷#555]]]닞={Ν:u ;v5.tɓ'1t<֩S*$۷v©S{F.]0|oٳg4i+w?~<ڵkiӦ% C@@v؁7o}ڵ ooo /^|Ç{Bz&MXF@@BCC-w#GSN2dU>f̘\CEZr9s8q{.Nڵkn:| 0%JPX:wƍ.]`Μ9afBtttÇF|=[7oF\\\W\>>>8p $ٳغukJ*aر1gXqOZjaرӧ$*"::7n|pgl.^V^Xp! V <֭5>n8bl9s&>|pVZa׮] YcƌAZZ4h o CzrZ݋ &`ܹOZ\$}}\cƌQi)2ddL=VRi *`޽*u?*Wnݺa޽UDXX͛(\&DOOGƌ315/ݻwG```3gb_[FRj344… 1bs'))  ¾}T {QhAVZtOxJ+W3OժUq޽\ぁ_}#FuaH" Xbƌ# y-kעgϞJ(L纑?/FFFX`FxZ0Ν;k =k֬QiebΝpssS;k 67iOb,,, ={TӧVZnU2 ԩSɏ*,_cǎŻw޷hѢXn$P=ѣG=|QQQO>r/пOPzqơJ*jӤk~SZkeʔԩS1p@;Y+ҨQ#+CRSS%[?ˆQƅ ME0vX+WNpgϪU`,. .\@Fr[YY*y&~g85zhjGDDDDb.DDDXXX*ZS# 0E:)QjԨ///[=ba7N&aذa9rBi9("##>>>U*L>`ҤIxdÆ SXX0|pO*r9ԪUK4{.ի-[(oJD;dff_YYY4iY.L۳gVnݺ]|,\֭[ }bbbPNɺlܹ]v-k݃ׯ+33C ԩS%Ȭpڻw/BCC%m[Q矘9sZ1/رC۷iӦxڱ1pB]~ cǎh޼$?_y޿]Jv|T4nw$ѡCI'd2t{ټyl2ѣpqq;wԎc֬Yd&_XXvVa̵k0|p ÇU=*I+Wʑs>Isҥرcҽ{ЬY3h;|3ԪU q)9sڵCbbSѪ° Cnݔz ӈiDDDDDDDDDDDطo͛'yܬ,C킢aРA@Ν!YL8vƌ#iLM8}4|||T~ \rE}J(]p.;;&LP94ϟ?:::*ǗRPP~WØ>}qn݊C?~$BBB|r㈉F޽%) Š+$Jz9k7=zƍ4޼y.] ))IҸfB``1cZ-ǎ1p@; :T"]vI3..;wkxܸq8uR(r$v?zZp_?dݓ~7\|YXb,rnzF/Y7nHlud2G#wJ{wʕ| $4Hbƌz9<==)IϽ{]v-t7o޼H[n[#/^Rv^sΕ-Z$I'K.aʔ)dч뫱"tQhi=h$7ydI>sdee{xY6yd?jǙ3g'AFΝ;5޾}Ν;kC7)~+t3Rˏu688X#ǏXcqk;""""""""""n֬Y;:::(S ʗ/KKK#""޽ 捀*T-޿H;wro:u*z <#f޼yؽ{w9::}ppp-'NrйsgX[[&dffcǎ7oWWW/_DDD+ƟXL4I"E`ڴi6l pez ttt$1H"pssCڵQX1+V HHH/pi={6t'N֭[kٳ%8Wn]TV HNNƃpq7n?yC=3g][Pvm+W߿ǏqQe*]ԩS ]6ŋ#22<ƍ]/^,0Ν={九Z'N̙3rY묬J[K.ܮf͚pwwG `aaOΝ;طoߧ=!۷'FFF5!x CCC{"E&?nݺpuuEɒ%QX1XZZ͛7x5dq/1fϞO Ε+W...ppp ={sΉncǎ1cʕ+r^߿GN+iӦhԨʔ)=ɓ' Dtt/^m۶<)Z»w `ffg)իWzO@ [[[!22n[}ۇ tE1rHkd888|Çsd2.]R۩-v *t҈Ǐw^whԨYYYӧOd%J7Q\9$''ɓ'8z`͛75V`ݳg<aÆh֬ʔ)ŋ#>>Ϟ=CPPT/8yyڶmjժlٲ֍/^ݻ8z^<}4Bjm...`aa$&&">>QQQ8wΟ?Bjj*>| RlaЪU+Y!7n@@@輅*T;;;|>T<4"""",!!!+Vj*O_y?lo+=S?Y"""|ዿMբE b6L0^޽eE5nmm-8qɓ'beeev)d.\<^rrXbٳlѼ>}*ҥ8Wg"d2SSS5k& -Zȍ3n8ԔE@6uTׯEclllD޽Tn߿U\Y4Ν;믿իRr4j8&&&!CΜ9#{]'%%d&&&r[nU)?L&4hy#tWvmX111~M&d2CC\[9sF0~BBlr;99sr`<333YVr-[V,/==]pB|Őe˖Ty沫Wʍُ?(7UQSRSSe%J%lݺu*)F/*ԴI&}C˒ulmm\}||QNns6luE-d<ٲÇFVK.____'{h,ٞ={dŋ#ymذA0^Æ jҤܟI2edwef/11Q6ah*U>|t~UT7dʕ+˖.]*KLL+%%E6w\Yɒ%E.aii)[z۷ϟ/322gРAJ,rlݺUnlCCCْ%Kd1n߾-k֬Y}+V(Z`ܠ lذa(ٲ#GF(ߚ5kc5nX֠A\7ݻwOֺukyV\Y}:+&]U6?O__s?:#<<W344ĬYk5kj*4n 7{lܸqCn7|~aݺuؼy3U&-6n܈ӧ=whѢ/@a޼y8|07n,ϟM6n߾׷4~1?ܹscǎEPPܮV͓,Wu v$>uK.QNq*WPL8Qte˖ߖ/_.S+Ν;ۋnSlYY .{3fՅ߿`t yÇhݺvׯׯLYc6lufϞPlRGGGmڴիWQ^=؁*畒3gΗ+WW\ٳQxqн{wܸqCH&O\Kppnݻwݻw%+%͛ ns=lٲEsu}0~x?rmڴIn'0y]Y[[ѣ5j.,ժUÿ^z}1g !>|{.+V 'N+PlYtttкuk\~ZO/aaayzzƍׯk!!!_Dc?|wQ97???B͛7R8j¹s#ܹsU| `5jbΜ9FT5|$$$988ŇBpA,YRp>((7oT*Ǯ]Up.""VR(h!۰a0{?J1r n߾-M_֭wʔ)h޼s8?|L0A[<L_mmm'N^=<}իWW(-["88X,+s=tuu~4mTXغu+ZhŸT#㵕X1%=f͚) pvv~dkkYw^ǯ|XΝ;[pN__v>bhhUV]vX|y;qiTZUX8xSaׯ M6 ]tQ95ϟ[n)ujҁp yWWW\tI *` U.22тs˖-}Ȋ"J.իWŋ]q4.[L\Eq.^N:)2}DDDDmbqDʕ+[9sv#:t(&LRQFs=dee M<V:GXzw-IXd19pqʖ-;vܹS ,9s&RRReٳGp'OV:˗/Ǿ}`kkrغuh>aÆw oEϜ9KTE")Ӿ_)ڵ+D궥iӦM})S0h YFmٲsشih1S/^T¬W^z*ׯv߇5PS QF)O/RБ 422ÇUQtصk*eǎׯ;v(^^^9rӧOqecSNJwڍ7D,ZJl֬;wS*+DFpaZlذA#7^Ӭ߯TAhsss߿_ۖ&;'''l۶Mn?r"qr>_*+b?>vN:%:xbTPA?#~7۷ŋJ, \"+++3Fc899M^wRJa޽r;6+e˖x\Ο?/8Krz)PmwMd;vxM$yl""""v8H"7nT{,Kʔ)V7===тt(+55AAAsNNNj,T|gϞU9T!87eoa)00Px7FΝ^~-+?^tn„ (V9nnnB*̰Q鼬ShWϋ>vRST)tUp)|rwwG>}Twٲe(RÇ.JJJ*h066}xB$98qJظqha@DDh˷ZjjFtM Ejjd4kkk>񁙙"1AAA?I&)ܽGH&MDsUy/b/f^{U9KKKlڴIΗb䭕-[JqgΜJ* egg JOODZcJ(ٳg=LҾ?~s ہ6/+W/"8뙺/_.Q5ke˖sgΜ}H(Ѣ/777 6LsXZZbƌswG%|rwwWfBŊ粳R܂-[TݣGp5;wJ@j}^sJVFa*ǶlUĠADDDDDD$ۿ'""""""""""nnnh֬qN<8 tc6lOOO9enwҥKE&Mnvttvy (cܺuKxs`xbNU=zҹhگ*85 IDAT~EтBu7EUi=z乍ʔ)R={ "&&FH%K}VzM;x 233-[ZױcG I]$TX$&& pN^'RIVCy^sB䭳+W,0l^tuu1aiDDDDDDDDDDD]Jb7*UJ  8+C,O'''/8X-:t$]p*W,UKKKKl7$M>@>qzSNl166ϩSոqcѹp PڶmV Bc6ujl95Yk]pphW>}Z#Gcq ? ݿ?QMѢE,-Z9޽{p$B!CH>(~u@-x#FHr oooO͛H:O>8ZjjWWǑPQ}}}:f BVeee,87dɎ#EFFիWGY:[8...W˗Kʯ5PSڵkV'hڴSbH>(IP|M}QVRDRLZj%KGZnS؍o߾U˗ص{ӦMakkr~9ĊbUqI IYC켻|$mllTlYkAebccqy9)i>ܼySh|*Rh!eFF=1 nbݭ w~nmٲ%\\\9RXYE[8DDDDaqݡvׯ"tM9 ѪU+۷oӧ!8׽{wrh0EԔMXbGQtҢ]:uhsBOOO#Ӥ('::Z)TUbEDrrJ1Ǐŋ ۷.\_ܮqƒ\ bRפIc;gĺI?..N9-*YGy7)QJbzQlYIΚ 颅 -Z8~b0(obk`a&ڧ %$$(GL|XZZD;HiÇ)8'DTbUr~&M[޻w/=BY;)>gll,xVV$׌ReBD$vMSNʪȹ+v>UPʕS'O6m*ڑ\0+|`Ma% i%M5w|$֭+YvM^#5nXDDDD}cqׯ/II5j$y*MI}S2P 69oAT-377ǔ)SDǏx̛7Ot tdaa!8KɎabb"MeʔV⧥;GӦM% |bll,8ib:Pֺ޴,6l(zSvx~,nCvv2RuQbix&P{X.֨Zhcc#{L&}oTæ"bV&&&SZ}Nb,߱&>kh+RZN[AƣG<)"a[+{.֮]hvڒ?P{;v MV>;JM"""">8H ptt$MURwM "]nxSb7kݻwEoIWСCEgϞEE;u*;e@jjdpvv& J,R,(addJ* imMykk!{.(E;(( KLQ`{!Fk5(bCX EEmx2ϰevY~9;Ν2,>wncT&k_,#77 NtĠs6\Xb֭!=V Evk֬\yՍӧݗu r/S-4u'вeKbae˱q)zcvX?'̟?JFqXw한 ;OR4B)G"B!00$B!B!Bj)kkkfiU>-VgL ^lllS{ ڵҥK1{lWAyV8B!ȃ!B!B!9P.==c\RcccQTT9ōBv}"es>|8֯_(/ q%;vøy&k,+Wq+׬,^w@nݪH !Bl!B!B!@h2親TߓqMxxx(eEIIIRCCC@__3EAA>|(TseggXqi׺T8&.І (>022 Lꪶ|&͋BݿE̕"ͭ0V\Ϲ]KKK܄Iם{I]]:::FMpvvƹs0rH<{L8W^E׮]1o<2Cu 0`N:q!--M8OŋrJdgr$zB!RPq!B!B!:r| R&{®]#,]ǏG@@kUVpppE'O"ByCǏǍ7b HX~w̳5鳋B!TOTF!B!B!cccׯlXBboe%g#7JUG,cܹ8`Ǿ~:9q98;;\\yyy4 _X׺o1UMZ`kT[}v-[0}tQ>uo_ܢheW Q ʜ+sssb MޘN+vmźA||aS$m5MqdggGAppoaԩ ʔRAWWW;ĉx ?G… Rm߾ŕ+@O%KRR@Oe%B!BW2i|]3^|Yjʂ˗#++K}Ν޽{CMMMGDDpnwssOEʕz 2hUV0loo\ka۷o?֛7oΣćuaJY^UeQWWglm}OXsTNNJJJ-̔:2drTjj* AםXT"8;;|)#L~>F#G"55¾}p-o>̚5KI?_rrr$.1&N'"11}#RRRp 5JIq"B!p !B!B!" !TEeEt5]M(N{)~w1mmmf۷q!_PPhα~Ab h[||>}ʹ]˜(٬ߍ7nWގwጡ=zLm8U _Y7nXa]@88xU```u z,UŹMU=Orr2򔜍j`͍c͕0Zs*B$$$6%f¯\Oe9 u.V,9ݻ0_s޽*)"0%fX "M2wA׮] EQQb !B)CiB!B!B F-8Ǯ_l`nn9Jy*J۶m9JFz7oÇ9nj`xyy[nyn*WEnҺ qd@N IDAT^Z0ܬY3A 4TZUy(ݢE D"A 9,1ݻ'Hڮ]vۋqM%gXIm7W ]͊,>+ӼysIII Q:ǀ{ɫe˖STU'I7YBB/'&& G(fff8ro7{5 XM*Nֆ:XM=lmmqqfWߢE <[;B!R!B!B!Q]V<}̱ϝ;y/[f 3?oʜCff&v333c~ 3KE.-z׺ Y[TT\8ܪU+JXsׯ/1TenuW݊dXŜBva'/^@rr`ǩ.9&C X젣û/8/_;2.\;tuu9T455ѲeK1UzOmll{e]O֖(8MSY=ҦMJDj9נA,^9.9'=&*ͳB!(B!B!B!*7%%IIIJΆUq={N(WÆ ajj9lsY;wsQF ׯ_c2*̨W1? H())$|Xs;w\YšGdquf1B5֭[WNqaaaĩLMMѰaCα%gS9V_*'&_BΕWBަMBM{yPHH\y ]gqsL(|BKMMy'k׮qISo``=>>^>1b/##CټWV k97~x昤UH4B!hTF!B!B!\3ZtS\\cǎ)9c"&1w\e*,Zj9_燔ra0jhhSK޾}+Ȃt8 Rm۶e9ҽ2:M>y#,, cjkkCcrVܹs(--$뺋@BB ǨmX4B`]O=a-aaa(,,0dSW\$`kk9VU48u3Qzկ_FFFcs{$y:̳B!(B!B!B!*cǎ۳gr{n%g|]tܞGHСCb-[b„ ;88`ɜW\8 0ɓ! ҒVQ\\\caԩd:+jQcg~nn.o%gO EHOOXkӧO r E;w(ƀ8߿Ly}hΝr(úb1+qj{ L̙3C 9?x@>B`ggǹ=??_ə׳gO؉'5i҄sܽ{c([~y6|w$B!4B!B!B!De1>xaaaJΈ\]]9Ǯ\G)9#5jsLȅB),,ĢE|Җ.]\ȵgH1vY;}(55UD:{X,+ÇknT$tڕsʕ+*Sfrǿs9:w } UĚ򐑑!w|*?؟L*(=)**ߠAcB̕E(cLy`E0dpٳłpttDӦM9䌸)zc]Xp'Nt'h Gњ4i|spm%gkrVs(wttDf8Ti%B!D8B`ffk!!B!B!;v,slΜ9re̘1̱ٳg+1k֬W\3m6$$$pۛf̘9VRRKkYLL kVk'Orضms*,1g%g\۳g׫so^^~]+-Hڵk*Ԕ9&dgѣGCM{ɪU)Nlllо}{α7nݕ̎tر#8ǎ=xs\nQ غu`ǪMX pB%gm۶Ddd\8ׯ_sb eee͛2ذa+=W3#G.l2%gs.66Vdee~SAI9=қ7odΉ k-,,TyB!E4B˗/9!B!BH55`ԭ[s,<<WrF܆ mmmαSN!((H)_qH}+V`YsA9qUr^RR`b}˗dޟȏب2̎;:::Ruá9̙3JHyԩ777α,رC 8tssJuWNq&-V5|3gDnnaؕ+W;NFгgOαׯ_vk:M[N清X~=s\B^H̳&L@ii9={vz]wxǬ?QQQJ̆M_ї͛7gErǎ*ݝ7nc}gTÇC$qѣG3Ti%B!DQ8B!B!BQ!ZZZ>|8s|̙x=w;__n] 897 ))I>r ,X@9r$P||T磻ヰ0Yf F.]*addsJ|gb۷KX,ƤI'D2wAVVX~$ #uvm믑,q?O2dsl̅|ʮWVofТE V"eɑ)իB؆,OLLԩSlYfҥKkkk1;uݾ};_A>-ZJ|s?#l2}:7c f׻˗/cRKMMŤIGRcvy-Ə"AYTT+Wȑ#UvvvN?~<{<#1]OGe=SWWnޞ={E2!!K,z?qmSAAA{vGGGA% +++tޝsL,c`UwNgggw_RnjƍMܐ!C{ѣGdsvvv_z˗/˜q-~DxSNI@@oWYfɛ\,Y\̟www r$ 49RRR)c2;bܸqR/ݼy3.^rTU;vܞŋKŋ2G$cbb9w^qiUVذaޞsS.Dzwi֬Xii)Fg r,]m۶Ehh 1ֺuk|szxx8BQFL~ .ļy$*|!\\\*\ΐ->s F%}qeо}{,ZF?`ҥ̱׏Yx$ǏW^8qTm۶evn?Rum̘1hРT1E\rrr?H+;;ނܺu ;1Lbll̼Q6l :竬O[[ٹŋR z tλr ڶm ___k}wmּFIu/y}V>VL6c)Bn$<<};v쀓z쉀6l6sΝ;ԩf͚8bСCׯZn={UPڽ{wiڵ+ǜ[fxzzv ?~<4i"S}A:u?&M|hCFF6oތvh/;"/_b޼yٳ%qphh(:vʴzjhtV˻X,ƍ70aK}WQ̼W1bf͚|@΋/b 899}&$$`鰶ҥK?*#֭[#,,5"~Txzz2`mm-[ ?I Eγd%B!D豑B!B!B Æ 0b_믿ФIxzz022BZZRSSK\~QQQRuDb hb;v;ТEǫW/^ڵku]6/(k_テannsss"55ŋ+]$' Nf>>>hذ1E"֬Y}r/]G^ڵkmۜAu033ëWr. 355w},#[hQ¤;vİaЬY3!>>Ǟ={ ޸:u]DLw1_\ײeKGs]DDnݺU-c޼y5k5wA޽ѱcGxxxiӦSpm?.l֬ojMQF1;`…رc 4j999GTT<,駟*->!қ0av؁k׮1_o}gQTTT3yPZڵoՔv{EN*=ަMp%k׮O>_>`oo ԫWݻwqU| ~'\p9c„ Xx1 f͚aÆHLLÇqJ;Ν;"66Vcǎt*kW>}`С-Ɯ9s0o[hԩ#W5k0 ֬Yt|.l4hƌ#hL"-[Hx֭Sʢ\i>|xUtjjj8t6mwF6m[USظq1 Niʔ) VxyyaΝ*UL\ʺ8)ƍ'H={b͚5rſ SSS`$uY qi/pxxxTu*|}}1h `jj*?>զMJ \U?h IDATݻWuѤINE!_ W^oNeʺTKK$)̳B!!5ŋ1e888@KK Յ=<<<믿"33͛71sL 0[F:u CCCl}ˑRթv|||ЩS'@SSzzz022B׮]qcfffԩSX`zkkk@GGڨ_>ZjZ d'qY|hӦ tuu W^xpa3=z􀽽=tuuMMM ?9,:DDD`ԩhժ١gϞذaҔ!B!lقQF)82﫦f>Ǚ3gPn]?y$97n,Ȣ;2 rJcm۶ ח;'hժ+H,"=KKK߿_ɲ_~J9*166ƿ cccAJE9s&:w,H, Z$J"88Sd7SаaCdmذa WUH} ^pۻwo_^Иf޽{)8}4˷eggsViP~}={rőDu8τ'NYǒF$aͧo߾Xv`/^^z'OY)ިQ-Rz wMUyfmV8vvv→MY**( a%B!DHtWJ֮_;W^عs'?~\5qqq8<;XXXGT͛7xm۶mС:v숍7"((+/*ƃpY,Y666'X7zB_xx8???DFF"''|ݻwpݻwؿ? VŋT[,׈`4m:tѣG#>~ze…Gfзo_رwݛ7opE|R\"##1i$<˗/#..HOOGTT~13f .]${ w]tox=.>>5k,--_2!2?f^!!!BX={61d?B!B!5<۷PXp!'WMMM`ݺu(gbb+W_<ڷow*@SN8}4\]]9KJJ0|˗/lʕ+ mۆx!88Xnn͛7GPPq|'1cȑ#f%<---bڵ ~g/ǖW-K[[۷oȬzӧ.b122ɓ'YL$Ӻukܽ{TH|GGGMnݺ ی3p94nX7n]&xlE000իW9}t(hjܸqѣGY'O2ָ}`>ռys_Jy(000@hh(.\ [XXK.9!+dq)A'555_rO %tQ߇,X(,7o۷o]иV]]];vLBy+++ ZsN]]6mºu?b׮]?:̳B!!֪Uₛ7oJ|K.x 9;(fϞ7oSNĴʕ+\פIDDD0I!""_}UOuEHH+ZBDD I,,,p)ٳGjjjpss?"##)W<{{{\|cǰrjmaÆ 5kѣ|@7n܀vppp4K.ƍꫯ.ӲeKGx0qDcԩ;fͳ׮]SyB!!8BQɴ͛7ѽ{wDGG+I8ڳgO#$1{l0ÊJKK1rH:aYv;y$._˗/ O>e=~GFTT}ŋpvvF||O\|9,Y"Ӿ>DqMHH$cǎ cAAA2R7n 55sl 9&!B!D"|}}9d9`4hРvgggΝCXX<#Gonn`ѣ_6ot<o߾xKKKx{{cҥBrTI&aȐ!8z(<+WH t#Fȑ#%^x+_}PZZ۷oӧx ޼y\ynj3pA:tqqqodd777=^^^r-6m^zUa{-d)S O<}4i)S`ĉ?AKK &L㑐O"..S,--ѵkW;羚 B^8{,%WMM Z°a0j(4iD<:vy%ͿgVfffxߺu 8x _J QF ~UfΝ;%1eL0Aד)Μ9???lݺҹ =f͚Yt7c Ҝ_͛7ƍzj"00k|m۶0vXtR$H9s`ԨQسgv-퍉'O>Rlݺ5+ԃUlllpUZ RRRx_ommٳgcԩ Zd <.Mqc}vlڴ ϟG`` $q 555t 3gBH}}}̝;sEVV_{$ZǎUbGOͳABB!5ի1 \h={@pp\8`$!!A?8Z ,+Ƨcǎ۷s> GtĦM/B@@ԹJ rCߜ9snݺ 'M?bԝ.\777RRRs@,&&&033#:w,BIi#88=BJJJyjjjy6lpvvFÆ $OEIJJBpp0G044 ի{{{8;;cǎzQPQQΞ=3gHMMEqq1ԩF?|*gL4ڵkHHH@ff&agg&Mwh۶"SWÇs]z`aa'''tܹu޽?bccDdggְF^8HE8~8wh߾=\]]ѥKjѱ6JNN./?MLL>*vᅬŜ~h۶-:w-ZU%%%8s ޽D$%%!-- fffs#zY-3gp>4NEYxYwW^AOO ...prr6%%%8{,._\~MnݺhӦ ѫW/w cILL,?bccL^zWѵkWC)_ZNN[~J,!B!Lz ! ܹsu9lܸf$N8  !K]H!LhIII߿?"""QO4%55WHaݻY(HbժU]]]U 8ՑwTF!B!D!455ѿ΅DGG 4Mc0`u!LMM1qDL8SQw=ztUQc899q'O4Tһ "uu}Cx)ƍ&6lXUbU 'H=zGU ' =Dٳ'zYթJ]]]6TV=xNEe}&)1!B!B!BQ% k޼!B!B!Fij͛7XjUcǎŪUШQ#αիWcѢEXn3MT8_~gϘ 4֭[_T333CV0`]&LS8?_-H999󫰽iӦXt)>shkkWĺu렯/ڴi3f`Ĉ|)н{w̟?aaa9 ++ +V͛%CϞ=˗+lwrr’%K 5ML?˗3cbbcc[XX~Wc8q"qq_Kyyy}РAشilmm+5 +W?<ʮ@g 2 C! 66ݴ",nmui ?m@LaF(! aa<Pǘ U[VUgnJ7|_o}{Wݻ(Ks^:v[N?naÆБ}YhQɵګi F<@4eʔ0.W^yeŴ5z.dqj^z)ӦM+kM\o:(,%ɠAr饗欳zgᄍO#HŴR?w}93+̪UZgKq_o3LL8d1-If?{ޒ_sǎ'x"rJXWnrgs?ew~{Zbԩ띦WSSo_F]]].\}^p<-2bĈ%ך+֚[)͕ꪫJ80|pPii@th?r)4~뭷#V|ŋK=:zk Ҫ=?#'|rɵ_=\sMsĤIrgmޫW^K3uwqO_"{J/[,wq&w^~G߾2ՑG[n%[m&?vhSs*cƌiБ466 _Bds?kQN:>}[&ݻn5{z<ЖXmv7fʕ%.lvs\뮻pM޻~4hPׯ_J!C_jɵo1e}^؂ 6zUW]޺MhݺuwM*,>6q14ϷiMtO*O|"uuum~Fs駗\馛ڼ)N߿N<ĒkӧO/?e׿uk_|qY>ϵ#9J&O>V\;Sڄ ZTRCCC~N(>YktrZG|N8mݖիWzkǏo~u-o;T; tUb+9|MMMyKqeިQJ7Wk}ݷ{TҀJ/ZikZ͕ӆZX{i/΃>J–$;csVti@[de٫O>%QN{}^1 /wu6QI}e{FMMMvy\9mNMMMYQnݺu+E9{moJ5W4ۘ[nS#_~y>OW; Ut Æ +^(%N,~N [lY-[ 5zy睹3a„3&;S 4;Zrr~l~FCCCO^rm=heѣGY*wӚ+5gy饗JM0չ=}ՕeѣGgԩ9˲OjhJUw%WXQg?_r~-l6g[ 8|[ Y[ouz٦=Z'7\/,`p¬ZZ{W./^~N4)\r_2k֬lv-ڧc9f@{w}s_UW]ɓ'KSSS3<3'ҽ{vL @Gt ,֖>45^.K.mv~|Mu3֗,YRyz8%^{|3M7nϨO}}}Y~ҷo2$v;SYY*-̰a2{ n|Sj>͝vǦW^ :Z9v!_|q.̞=;M~:fʒ%Kx$ó6d{sꩧfĈUQ(BJ>MsUV'XVr}ݗN;-sigZ,r}]>I:_ۚL81W\qk'OnQ9mҥJM0#oeذa9SsꩧV; PcH:jGh;:i7pC=؊ʩwegc崎^&NXr;̪Un-%ה́r@urZCCCիWl襗^9l #+ec'87n\F˗/wr-%glvmuvN"g%Ki2`/}Mnnʕя~T)a+u:>}c-Y2[r '4{oSSS4 4*Zzu#thկ2uԍ^3zz꩙8qbsѣE{/Xi岱_Sg[Դ|mmmN:餲訔rܹsX4G}}}NZrhY`S ͝$ӧO`i̘1z7so0܉j|p R|r@%6_׬Xbyey3'O\#Fd#RNQF2eJt͕ujڂ OeJiz Nc=ںiǏo\r@~%OSV8M[oohh(7tSiIr 7T0I5qĒk3g̴iJ^3a„vQ)TYs5%UW]U$ǀJ/Y,/>{fv(v뭷fٲe 5WN{衇pfOٳgƎ۞:$4*;3tВk/x 'AԦ_~fKF?˖-w z~5jɓKwGo߾QNݻ7[:ZxqU8Q5WN[dIOަ/׷ijf.rAs]wu;KM0=#tXiYg7(3g̘co,0dȐ9W\lIhcV\~Դ5=f.W*s^{Y\lotPz 曚J^nevjXr@qe5 /dر7o^bŊ\zg}rwyWsz}sy;,1}c{'s=36d-5t5iĉs'63dԨQ~Iϛ7/]tQ~\pYrF}-آ}|͜vi+כ4hPUK޽o|z+w\=oQGO~Yti[vZ&Mj&Lh$r@o;ݻwov7>L81^{mz뭍7s\y93/4a}L޽K555峟l>eܹ%/tA6X?˚>dnv}W1c'?I|f5kVtA7n\ܩ7.={|::nZ{/K.u'Oɓӷoߌ=:Æ Ԕ9sdΜ9yW2s %/@~_4{͕W^:~xFÇgys7(y;쐏׿^Ummm~K}}}=c'???{gFm6˖-ٳ3cƌ/c)^ƍwQ\ve9s9rdgfhoiT^rM7?i PֽwO}Sy;)b={̍7ޘC9ML81_}uVdCmmm~?y_=>;|5iҤf&LP$r@w駟ΤIRSSӦF/y??~˔}/v[>ϥuݻw.p ׯ_;%>;?x>Mޣ6&Lc=۷ov>cƌivM9 Zއ>'dme{Ɖ'k-آ{'IOnr7f֬YSO=u=FaO?=~x n'|rvm  {T?g}vooʕ+~Yg)Y8p`zݪl3~ ު}jvwߝgy&?Os5d޼y.ON;s9vm[܉'Z+,%j65MMMMK/ͧ?>rȼ˛3gf̙ykeܹӧOgw^zU;jXtiμk={v~ 80Ç+uuuՎYU>}z^}տ 4( .쒃:K({wM9眓gPŒX3=x^zeժUiȑ#3rjǨ}{_cthݻwϾ}ݷQ:W^yd1-I&LP4Smt47|snݺ㏯pI9 ~xX4r:ϟ)S\;+RNX 7ܐk+RNXǵ^[r~ԨQq+RNW^y%wqG5O9 o.ח\;3*cSNH2mڴ\s5%<Nб)|0&LHcccs9‰:nP)^{mV^$ihhȌ3Oo;;gyf%ct ifs[oժ{>OfȐ!vW^җTr@3/eՎ!)p .v K9 8r-֭[tXiӫW|+_ɔ)SV[U;@8`1iҤ\Ԥ>|x<7. bBC9 lWv.|h54ZM9 VSNՔh54ZM9 VSNՔh54Z[jjJVXֆskFMMңGҳڱ+4GCC2~2{v2gyZ^2[jUe6hP2th1 )I]]e@(zK&/̘uKhNi~˖m\yǃ'=nPEm͘1cW;]Ԕ,ZT3ZvOMM2pv%#Gc~ޅrד'H|x}٢|ymŋ1cFå;kÇ'fE9 `sP-=D2o^)-KM+F)ݻ'n|ym퓞=+.E9 z)SGM~:Yک䥗QJMMN;%G{ޓU6/r@WfUDԔ̝[\GdזW'/r@g5{e瞫r Ny4bDڶV>'UY45%?\}rhTk;֟/md={$F%yORSS+4!2(wW;QU=Kf$__9f44TYfX׀EImd׽*{Щ)t4V%^n!Yډ6]ɐ!СѯbY̭G2;KkVJ,)ƢEEVSSerwTK&[5wOsmYm @)t\sMrɲeNq55ɖ[&nl]6|ܻw^mb\[54wJ/]Z|?teɓOc]yOQV[mNFޕr@5͞\ye$/\4뫫KF(gF9[]]]]2xp1Z(͟zsfqk]6fL.)TZSSrɏ~pCqT {n뮻'VV;槶64;\namMmun/'^5 I9$9b_)4J???Nf̨Nnݒw, hN6M]]2|x1tiS֌ydg`A2yr1зEQmMim]O$]\w]ە}v~'XvKzlcb[7L^z/W^FccSя^{A_F9 /E%]N0 96fLQL̳\K٧jlL^}tqmѢD$ &C1}kOV;@l24r{⋓Ki{=]vQFmjk/GdɆ_,l UbO+ÓISNI=6ի:4ry_E2p`&vXҳg= 5p`2zt1ֵzu2ks%>[|ܞz?.F߾ '$'L Ztr@[͘|/~GmUQF;ℴylnݒw,ƺVLע6}zɪUɹ1˗'__yũjRtrZ,W}Jin|QHgπԫWX1y k>,]ZА{o1d݋''V;!@)n9gYʻo]]r%4d睋1q> IDAT__6}z2gNrkM?8pĢ6n\ҧOTr@k̙w^ߔwmIN;-yC˻7tlSc];hQsEQ駓JfϮ^$;7b[&MJ޺*L9 ~_, 3P[}trakmEq!;(=d:mZjUuXtS1jjNN9(gu2TrySJ< ?'mUyV[[|Pk kfU>WSSCWw.Jj'qDRWWLL9 9 e%Fre?9䬳~ھPܳgY-ZjO?,_^\3f$_^ SN82JY 9;۾W~E!쳋о"9b$Icc EYGK/\ k)FϞ1EI#*̔/)#/Զ}MsI (O6jk]v)ie/ʧwߝJ岬Zzk1>O.ƨQVW&+4?Nj%ukM]Rmǵݒ#A#~['55U/N8[|'ЇRڠAGmmQ ;_,NUEySqȑE1SK1PMɛsų%u%Y9u ھ( lס$_j$EI䢋K.)>TG|k[-vکX`A2eJr]Ƀ'U̙w[qOL\`d޳k_?,}5i|ի~õn%;[m'U˖%pr [$_rQ$:!C|+W* k V.ǒ%ɵ&=:9CI*E/-[F[Zn;$=%;6|RSN6o/rJ2mڦ1qb/%/P}z%S䩧]w%/TMM(|QT;'5I[ɼ?m޳ɂɺLC1dC?xk`ufF9 |=drqɂvɅ&GQ\@S[[^6ztr̙kjO}ʟ FL>b,ZsOQT{dŊhjJN-%$CtRrСGdM/^W.vmղN^#Ov @{RN6?/̛{K?..sbSjUEQmʔM}-,H~b&cƬ=Ur-1Z{(l`FvunMɴ닱 'V;tIie֬_o#G&^t ={&GU駋]w%3fT6KccQ{䢋-,NS;t+6Mo&$K_Mdyqի$?{Fr>lI9 |̞]flG|[i-QSO1;/yբvɣ& 3o^r5ŨK>xjT5h3OM¢׾֯vJJy7%4j.C9 <̟<|=_%-.`ɇ?\K{M3dfihHx_Z2lSՎ?>4y3$ިv&--^댞uISSД4ym\dɢU|{[zrI!M+ާ ]r-^pB2mZ+7@9 LPᇋ}%Rt4/ߗKNQ9k=A=׼YƿԔ,};Y(,I.O[{Ɍۓ3N;̛E9 VNN9%y7dH?{X{#$3gE{MNM~yV^dmڤI1$W6tvbZMMQ2۪oed>ɖogس#ɋcP[m2tMos;A!_d߭$KV%Gg'/.jލ 'M>֟w@k)]m%]ֺ{ H~c4c+9b$ /%K}8嬒VN] X6n\1v٥ynU-ӧg}? m^d]Êƛ}'& SNz$k}iΜ쳓ӯ_%~b$˗'=Ts+gɒ/FRvqɐ!6tפdj')Sz_>[sZNWy%Sï'oo^_tG]r45%pQPk>}0{PN}+>_=$O>44T>̙W6eKz|&:d咤quP_֬5' 55$k>njlfokVkӽ.ٲwQ:ih}}nGHn{1YI>o;!tYi@O-gOFnLm]%˖%

<{GK˟ 4kyk_kݻ'^2fLeN(FSS2}S՞~(U[o%Z$6,;69ugP,{=YcɒגYSSv}W$X~ jd@QC%78A혯O>Bc!Y|ɡ_&jIܳT{EH-Nٳ+FSՎ>8i [Dug&oQ:hHf.IڲI:CF$;N~L2ki키GilE(]?̜<2/@G4hP2~|1'g~vңGrakjV'@GԘ,x!L2gZho-vB6ܷ ;ɝ/'wܺ{'kdǣ#t i@W-L.8Q`sU[o1韒 ڽ-[V\o}w1d֖vء:1YbQ@[SD3-lq:]Nu$cwLj^l}MuO婤{r5|K-?67-hL͐!)1>=ybӣI]rйcھ}o}+kL-LN>Iʼnj>XjUur56&?^//~e#HN6`5wzmL^ԿUDtD% KFK*@$-oLӲ{f%ɱ_kh(W}}r-M٦lv٥眓vQ[SV{ℳjhhHy]t떌vI}uyϨ˒,!ل@X³d ,eK} Kh-$ltI^bǽW*UG?F%k$hFu]53|̭8F9$IW}%l|X qޑ鎲SNrm]D =qgYi-Bb[אX5{kBOWjm=ØSۛ$I`$I$I$ \.lڔQ}Ko? ?;/=TU/&j{fÒ%'z-'I$IX+l^"LG 2UzD0sB  [wqmퟗW./CKg[W z"+IN$I$I4Pg?}8)pUn ͎\oMM?v(, xX //sI$I[+Uo saxA* e䇀Y 2io^,5mI4PN$I$I40uߟکSۏ$)yӦ&hm+aի!74O(. /LWl$I$ i;^ ]&9!x,^9RʜK.<{ xa+I͙$I$I?L~mATg[d_LvlJJa{$I$IC^XqTmt7'ʉOc8nYΤFS@5a}&IR3&I$I$iy%x֞~:\yez$NI \qE(]BPmxluu#K.I,H$=J$IԨ 0$tE#0Ɩ$hcȢOR/Kے[$Ip$I$I'?I~G?jH@BbfM⋰r%46fjxP#Fpt+K$I4` ߆uXkzɉRÄL)m8 Vݯ|&!mI I$I$IXHny煒$ (qF~0V5_~VXY u(rTbX3ۣ$I$I:Qaxnx;p`}fz(/ehe0r I*7 ͅ-Knw=$I4I$I$ICwIRfhheBX” ^axPߋ/. (I$IPg9,6,4'1))U"hF22JäRYڕ[%cߗ$IYp$I$I'I^}.,HLa!,Z .M֭X,=643τ0 nd.q2ۣ$I$I]V|/-a9#`VF֢I_/~T{$)KN$I$I4p<<ӝ u$ICWqq{]tQx\W/j6d>io˖wmf%j_K$IӟCz0iv&I揁7‘׾x\$IB$I$I$ 4'K@kMo/$LՌO֬adM-7яqB.V?rr2ۣ$I$IIҊQpLˋ$GnΝOo~mXp{ߗ$IYp$I$I[7u*,X^$IpS^z llq{}(+ .H= 3ۣ$I$I٨Bip(X0fH^KR9o"q;ĒՒoN$ Y$I$I$ ;w«&oOo//p`k۷gxPpa]t\x!gGI$I2?Biy9pH? S$ . Btn/)ߗ$IYp$I$I[ޖ^$ICèQַطd]2_\SS>^駇Z<6qbf{$I$?`SMO(-y#aLIkH.LJ./_:H I$I$I^x!u ǧI4v,\sM(ݻ;lq\[ ۦOAK. .yB[$I O|KK R|Ikz9{tvo5'}IE I$I$I,Ine IlP&-_ KleK$<./χEBweGI$IzcãîWSpD8mt&ihZ4 ~um1W樂'I4I$I$Iٯ-KnELÇxXmZhmlqãFSaE`\I$I?cTg 1R{lIYcpK+oK,a8M$I$IR[_楿IQ^_ QX2V["A,WöfOI$Iv/ c@ ?'ǖ4iOn~mK#< X%IR0&I$I$)-YܺSN"IRoy--aҥ- ͲEU]mLwUmĈdOI$IR ,)Ē$P(΃ݯ=Z/.%IR0&I$I$)N$ ̚öݻ;նlI~8@N~zjggGI$IT}hmNq Iꭋ' snK?I$I$In۷a8M4Lk*X,Ֆ.k5WVX"]wm#Gv -\t5I$IR+_%߀棩;4I}U^}ݯ= V|O$) I$I$In/ܺ#`"IRUT QX21Ym hhl;tz(jg16kVf{$I$e^cm4BcMk(MR*]:%p3_7A$ޞ$I0i$I$Iۆ ɭ[ }HBijjKBuuf{<^k+,_; Fs΁s =Ǝl$IK"9WNF4挀 ݯݳV+}IA$I$I$e{[wiC 7u6j+W͛lr lM⡵ٙ;BWq4 sv -XS$IsL1 !P4Q`gMkm$}I!$I$I$ediFIRPX|y"ukMW`ݺPw燀Z Gy9hbZTcJROE"p?8C0lTz$) I$I$I^ P]HFH?$ U(̞*>]:LWV#I\): 7cF"vpP\%I$iy ?թ9^NOSbHV[wO%IR?3&I$I$){%;5mpNIpš LW۸jjPiuvǐ$I:~CݯMF4g7MSRp־mbZZ[$I$I$I+pڨQC$u/9sBa!ի>}v5L[~ð, >N? 2۫$I$eUoAksߏNWNNSFݯmm'>7$}Iԏ I$I$I^ɆFLo$wKCAaC"|9ؑO^|1T\^zj"v!6lX$IL߅'? Rs̹#a PIR:yvں_gp?3ߗ$Ip$I$Ie8M%yB]a[eez5Zk@UUf $I4I$I$I֍>$IR\*nT֮؝X ֭ %ϘqYg1S$IRk!$ P洴&]ckZ39.v|xض3[o+CK۞w97-I4I$I$I٫5_IԿ&L7DZlCk6)flPݗ6qbgäIQ$Iz$,Z~(\8 8 ;L]sZ$IlC@u62Fz1A}+jj4k<&h >xekE0mpTxe4$ޭ;K4$I$Il8)}H쓗*X6{wzLցcO5wnI$IR+MƗ5aӂCͰ(l:nMM Zxa9ʼn8{͔,ohhwcMGpSY@5<_q{[  CM/HazZZ[C$i@3&I$I$){99M$UE\rIݻAU´zX$T\^^vk)I$irp$| J(VojLwth^=aۄXXp0`Vn>~2ۗ?7%IRN$I$I&Ia„PDZlqܜ>ܜHl/+;1vP^^%I$eX+<_ħ98}4e&(4pnGB k )0<. 掄SGڃɭpuPT޾$IJi$I$I$IRFaP^56† n]kaƁ󾣦{.T{&k%QI$IמpW~ %pl2X].v$$Iz"ɓC-^ؾwo"쭝;C=Hb[n.̝1v0mZڔ$Iԅ5qrYpghk[CI^FØ>s|n[Q4ӝ]n*rCD v7]M;=o}=|i{pVs8c J3gLY޾$IJ!i$I$IW7Io$I0n\/Ol8]mZص+s=VK Y{O UTdWI$i k~co %NP[B ڡ!pq2.|6:ƶ fDlP︯ v6ᵽMaY6w U o o,__\3 $Bm1ߛDJI`8M$I$IR*+KnC$)]F. WSk۷C,>{>T{&X;/N I$g^\S v%9pˍPkNk>jZBM,S/e;\%IRN$I$ILHںfC:#=!B$ eepyaV[6l͛)s}Νy$-7́;֦N %I$\a_8?< }M!|H"K 'Ϧ`QA4+QUر ZXVnfh߮FP甆Mcat^p$X$^޾$IJi$I$IWALvuv.i$i*.  ['jÆ0Up6ji S֮{Il/-O?1VQ^%I`p{g0~.LtXD7@/Hҕ(!p6(Ϧ Mȇ&T0ac۶7ګ!z6?mFu 'E"p)pВD-}n[yEݯ$) I$I$Inӧ'N۹3,I4T̙*6n AÇ3k_’%ڛ81T 8$I!Dpٔp_)>[YV"#'3&f58,B_ҦvTx\ٜ64[Bs-]zk ޳[ y Ϲa24Ǻ_{-aח$I}`8M$I$IRv3&I>hx5}:,^~S^2k_’%> meD#0"_``Q/' qP WäTׇ?OIU g6&f7G!M__$4I$I$I-pΝC$i()*Cw`bZvfhlL}U] /hM<6~|FZ$I:U vڮfcR@k- abӪT hZ4S xmjA'ezd, A?VAUK^_6}~Fy ':?O'I^2&I$I$)%NۻZ[!''H$ eFZ(-x DXm el4X8cX->i*SFM rQ:ܹG$IRj5Ã/(ʅiSW6; uGB0yLB16f0/A.7| rӐPUomLn}[ yeP:.HK$I$I$ehLM_c'J$eB8P:Ck7BccfLX l v>mmI$g_1w hMK D!h)9E08*<-X"k'*၃zAm+4(].kɭjD JB%I$I$eӓ ڕ^$I7#G:ĶX,\h}`öXp-jkWBy$IN]CKCeSMCxcmGCm: umٸc!BS _Jrѡv7ƒ7`g/Ax |(Mq ,ww uM=g+oOm/$4I$I$Iomߞ>$I(L+E a BUVfT`P=q_iiٴLt+ILi8 X4u*̬H]_j<<;>p9GAf  y q$'0\~& Kq@4?L hKr .ًSۋ$I;VI$I$Io֭֬Z>$IԿ BWYŃk70`Q[ Hik'fWI$%cx)PH+li8q٦哙PgÔBf1i(g'6Çzk53+iַmˠ$Ie8M$I$IR[0uVQ/I4*.;OcG7Xm㾒0YP\~%I{/| b{~Nπ@3]m=)in4%մSCVh%)$gr#/K}_o [ƪ9DH2B$I$I…ݜqh8y^~!(IbSW$76¦M}-ڱM],]H&OzZT$IRRbȿcTMT4"vdNVcЊЊNCV4(́s'L;No5=g^$Ii$I$I_I q,_+ I$)N=5T{--aZmm}{'oذΧ͙5ILh> c1--aYmkXVմwؾk[bma90TaNCN\cGϞ-!zS>~<{2L5H$i$I$IO.r%]G$I[n.L; !͛CmM[;r- ^$&%Bk3fta2ӯ$I`V~vbih(oW_>&L.E081mbA&i?mX|X<"L/+c[+m9 ^$IJ4I$I$IâEp]ݯ["H$ipuYZkՙ4}׺cljƎ3CP-~?n\I$)y^t 8 #F!mJQZ3`Fa( |_+ %9o%ɉe-mpjxl8$\:VCɭ<9yEnN$I$I40,Zܺm¤#I'7OٴxX}xm0N[ط/ϟcXӡ$If[s @3 1,clƱ0ڤBf9IY-#!/ zj[+s`Jaz7w Ս=g ꯦI`8M$I$I0{6vvJ$I$ŧ}vikikqGš5ĉf̀ѣ_ILik/%D;܎᳙tg7Uǧ%=MߠT)΃[INs{k08!IR7 I$I$I8?zu$I-O[;^iko5'`gN_Z1>6u*ϒ$IP_V.WeOr"|\ `ra Hn {$G]kߩT2 ۘsfmTLKa#$uw$I$IE X^$I:ٴCXmVؼ9\@6qrr`΃k3gByy+Itذ!Qk׆67B,(LΎ'NjJ9OT—Ǧ&Öð`r |r Rۋ$I0&I$I$iX(uV= I(77:^|v';&{+*Mm$I6u k߾Lw70D8OACSs3P3؃ |r3\Qo(Mm?<|[_û~>$I4I$I$I…֓~_!?z$ICW֚C@}h-~O[W^ u<6ڌPRJ,iжm(!t6&F̓10:/FۖcLRˋ͆ = nZBI /R 9Z I~Z35T$$I$I$ %%p|yk֭} &LHo$IR&u=m26oNL۲v:'U77믇̘1jl&@%I.+%IH?{ߛ$I,Cwމkj:cmCMu5Z399!)S-I>uu{{;=D`d. WMp$e|yܸ&3ca̱p~5l|nj{$ i$ y5558pr!)++S^^׳qFjjj#0n8ƍѣFJ$I!{s[ZK_eaX8sUU<|ܜJ l0ƎH?O$IRWSCvZ߹3>&|=O_4 oI1dUlޡ;;Zr(\= ٚ2[8P-D$a9Ǩ O=&6lfߡW6ä<ƌc\<,Mǧ§$ uxp(ؗs^c$ IC4TVV#coߐO2sgrWpEۻ>+++ijjtرcD":ngbrii)R|]UU=z4999'}}hkk;a{AA]\dMȑ#/yGxyغu+.äIXx1o{xӛDQQQ_9?8r1b^1L$Ir? .n.~&= ;`֬8X /ȳϲSƏ͂sY0g{.-\HA/n;\[KCW7AN. +*4şQ龑smlK)).NI$Hnn"LՙΧ&$ũkquuvmD0n\דצLQgIR浵{lee~zYuu;8c;S΁~6WWʓ/khCGU䒛jVtXj}ko\z^/ܲZt10 ˹rƌ)jx25#S {##gצIҐi,Σ>ʖ-I;FЇ>SNe֭>$Vee%7e*ox]wI?n{tkܹs{^|E?.;HuY,_hZ9)''ՕW^?s.B5܏{9.?oڵ|k_?i-**-oy '8{uu]۲e ӦMOK_;#pUW%)[m |,]n"3ǒzUgO5פջ瑩W.).Mϵo|#\a=}u?1sN:ݻ7wK){=>!bc }:~[[+7lehB?Ї[oI$eLSSbZ<>vh;~] 3ݥ$3!\VYupw_i0uj^yY_ellL+d_ 1^=Ե8ŅQ޴h8o1ǭ(sIԶP-UB/'uJ%+|з %ICOaaas}w!iH׿g>>c>|{w{w#F[nyu tN[bEJi+W<+VZZZXn]f͚m0MUVV}N뉣Gr_믿oSN9%EJ$IR8Xw_/} 0z;`ߡC}>V]}=y)~S/W_-7YI|$ܰ!ᴕן| ){Y^Z`JXǞ{',948IA#??Յzځt9>a(Cu&1cN`3&$LkkDd}C铟Mjp;mTT50}a4=:R;I~wvhb*~tvnzna o8Q v}N ) X 5 ̚?_vۧ--m|ÇG'7׏Ni|q&|5Hg}=|g6) ˃wBCKri:w n} &'IҐa8MҠG>qq,_ tܷrJnƔ]8=~.'{uE=|8p mkk0_[$I xAU֬~8j ĝw?-Ǯ=r~K/y{ENd݄zjE7[DA~>PUŞ.~?gNinie<mkb7$Iȑ:Ucc"v|mΰ_!o_W^|MAk' m4t _oP$'BgihӦhύ6=]0. Q_ߵOkWZ]W;;o1]V~ tް0YOu>ۼVJSke۞8cNIiJ⁧Rz-C[l |'C٥p+ɿWjT41=W&jS&IR/N4h544wx_^Ȝ,IfСCڵSs}_ޜ@X,Ʒ-.]}DŽ!|$IA(78|81D2%X ~?wD8 5_[뗽ܼ[̝^HA~Ʀ׮ܐک݅`GY$%]Nxqe޻6{8waQ]3CHPأILkT'ݔMMƔMTSLojƨPDas;3w(_ãs= 3}>umxyuߕ ^O8d<=TZg+5R'|OODD1FDM7`Z BTTjoRyYAA9 )p:7|3z)n:3}&N}b4`&~Ӿ߀E|}ԃ/`ZLIR-%%l0z|=NSOm84""""= @ll;͎o`A?㾾^1.YYdr}~[5t{Sݟ0Wqok+5QpK9/^etg!/u˙|̜9:%ps-j0Ӂ0k,U/DDDDDD}ΠA? <]w;weo7%Kz La8;EFr{tOvkD4""""a@zzLJHm-PQTy1qZ]MeDDzv68HǮt>sڧm.41zذ w?ӬmvliF;r&s)Y8͛|3'G`lvpO 668k,VfWi@M3Pڧ `UEDDiDTUUa…8't&M 0!!!o>]?3=R,tLj幽=###}˗W=d2OFFFl6۶mƍfXTNDDDDDD/ĉ…@7{5 x䑎O&~;UtI'I#*"Á*WU͛ ̄Ε_oL,_J8WTpZg6gTtWS+ON39t!"""!k6v%".$HЬ{,>L[lz uL#kpm9?a@ C>8}bġ8PՊ*+5֣3)=NR3d2tϽא4Yi0b&7Ymv\|G)6<ھ_j X]Lѡ'v`&_? O]DD/1FD?*9Ç]w݅yGn V֭;#~---.קES43!6gjjj~]N=To/c8ܺ?DGG +Tut,X^z)XЀ?< M; u?\r 3o|)$_w;}6|hǖ-x﫯ڊhtڜ X~q1Λ>{w'9.C ۧ}Q;>d]:?-yЎEG~oОV_kо$`t!R/jP[y~}a.;7ZU@f5xuE]寧N+jEgF|Nλuγ;2=ہ)brC`i;}Fg;.'U!-xj\3/֭@V0pVtGLN?piU[<_FDD iDԯX,,YDq%\%K PI羾2e Lz/^z%'srpuawžJ5: yT@Ucbگx.XZoffuo6ap /xDDDD;uQDDD @x`(lD]!6ёu+*r.g ^+*r;T'i>&qIшݻN#"""oL& !CQ\킈txxǷJ |G_4{9e0p@}j"RQP"f:1V4,Ą/.3oWCP AbU:^6T(66;694!+jv;s%2_\:u|^ӯ*DYN*,k7ƌ7 ϵفO շP,`@צW{Oc+E"}رc{l<#Xp!^aÆa˖-c[lfэf;_l>]x^^i=s??kNӶbnع>}F:9d u燻sny Mks32;\]^Í^O:f UV:t{/.ƌɓOS䄄Ѧ #!!С"6[aZnDDttL|}{P)ٵ@Ö0F_&ccikrk3pS{5t$ UXqJC۱cOsO~Ӯ:/Os04*Os0t;_T3)WU ؤoM#c7;կ.3'?Tڈc8 ͆uIyy|t|hp`nn0܌;v ##zn*eԨQƍnmyyy>>>1b}Q466Jǣb Ӻ3gӧVp<1?=Z#À{6IZi?ՙĸ8h~$La8 zx.׳s23_\ND6p׆I9du1hbc;>dOZoVMD_@p0t#$D],,c-飱*1=.4~ A|&!ltٽuu>7L>>>F _NZNC#;HuyҗOr24.dj]ҽ-h2عOڜ T'O GZ?Jk\׈"ujΌ>:|fZT/ڛ E#;c9K,ӈؽ{t줓N^!*(d& mW8p=_'l6(tM^gɊ]܈)0lp@7.xaO˥cSnjb% OMmmcynD۱ :)< 9Chk"rlu~dEeSZlwk>{ gU; gz%ﵭI fsz=ZcSDŽz2ow(>clA(n J1>'ĥ0KCJ`fLɂui@G4o Z ү6""N#~V:JC\\*'dLn :uj8M)Lp퓎tM q W͙3ǫjs))9k[|3pWSKҒXs~-%%l0M KIN#""""CXXOW@jt 7;Xۚ tĿw_e]ZvjqmvlivN3Cר%֩7tNᩎ?N `~y6;Nt70,)&Ϸ$ӈPJQ ntOYP3ֹgll,}#͆UVIǯjz"""""""JHV?ߵ_|\{-,7P 7ɻR n)l--Ep3]~<レ4KDDDDDDDDn0X5R}ٙ@~uH)dlb%())*k1lq$`[gw 4"7i7ob% 6 ~~Vͮv2źDڰm6׉4GׯuY0;BDDDDDD^5o/@|{]lfa/=1Wih;iq4"""""""# }mk%CS*tr5쥧Cc]ysZvz >%.)Mtl@8i Q7sy̤$dmkZOjӈ ^ڋ달,ؖ-[`4bH0U0ZVVFǿ]֦c8ц cgu+!"""""" _}mM ` a!7BW+|ۓd]ol|?Q{8mp|<VJA3-ub8jv??mH`\>yPXKZ6w<_ FK@sK yE/VNf]id& <9->&1*OM%""""""""~Az}3_vä`阥ՎjQ pi ɂbpZ Of@4O/_=,J1j|^o- , M6o SZ Qqqds>bEN@RRl+--EsiױHaLNDDDDD  (!d> <>iP˖͕+XsJŴv271tuQ` V76bwyp]ӈG `P HC\?<bE)4v2Ӳۂ[lرWM]j۰I86(I{O_{ڗ5֡8H$T ]Wb8 LlBO@7zu_{9'Jm6.׿p㏣ы KIq͝@LJDӬmmؾs^UGDDDDDDDDX |5SIaC'KvvwwHҒ$>N6/v|F09a!uSـ-%3ܺ $֛i_>v]868vMӝp`8DU&\ F+z%ӈ_q}axgPWWĢ D4Y8Mv]˞JsNsTVV&c8;,]E|7\|YN<[8LWQU[녪|L&d Ƕ](,&i{HSϱsm׷׌rHGt΋+qF|5V/T%g0R3տWf;+-Fբ{hٯSo =3 NFE`_\ \cЍU,['NS)Ssr32s>%%9GՊBa2pZXHקZiyJN#"""""""W}Ϗ NI֯"MA~Uq^Um~a_MǙS#pƔ RGfa]^uKEe-1T0fRgE ߈I(}ArZ)͕m?mn\a!& 6&09J<;915XW?S_mDD+0FDJxx8n<777? ѣGc̙1cƍI nW_}pn&Mrzp/.z1kpoim?@GG''cҨQeg W^QpڶR;\ӀkpZB7dAd|3KYCQa4]7[xP͎UkjM#b̜LJנg7 춠bs. @Nf.4Yiks@:6VW2&`@+R_ GW8/;vlذ<&M8,\_}Z[OqR1Y;Q`0Hi{n)ӂX *1PЉS7\Z[PR^'_~9O<~;>-WyR8Mm'3ټ{gfÖ؈4|H7cXEjJRG4>1ڭ;xBD Z; 4R  ɂbJNw-ة OP3 +xcNzsZ0࢑Xqm>5Qw o@^믿Yf!..r 4'(툈~y/W??-ȉӯ"/2HET[ihj{_`M;s&\qov{@$a1Q-߈dtP]C;p<ފVַ ǔ:y\.ѡ8~8X@@M gm *bk([_]DDԣN#~iXb k+6m|] F,AHg(o2"3p# d& :7SEc8HG{ lL?!R˙1y~-6q25CVBLHM$ɺFd+몦[Rh7Ӗ~t6I1ɋL yP(߷ %~Dxlf}j""p[3fg}ϵnjj#<1cƠ#4L9rpLCY'Պ۷;\wN)s64G--- S&t)p65W_ _K"i@ڊ/Ǩo=,U?Y+ )ud̔9 x0# pQo4:+߽2 )jC1*ZXA7w Vo܁@WVԉ;u% e*IMGpcjpZum|vtS"1!7ċ@ЩO(Q:1ap0wa@M>5Qa8O?9sG[\\qaٲe,؈;w*ݾ};VǓgӢ1P𠙚nm9H ~,c8wK/F/ǚO]rsbSUV ,eQPRr,c2axjuhN#"""""""/B<_=D=섑H\|VG{ʊS./SoUx~J.gڱ@uYgN0bcMM49=N齨LF{+"@rXzs8 iX vp@g0FD^\\VZ+V`ԨQl_O?{)ɜu2Ŝ ;;[~v]5nȑ0{L8:9s(wT)sz(Gq, PonnFHHo`v 6_DDDDDD3sÇկkm'SNѯ>&Li& ˖᫵kaiusNt4qȸ$" 8dO @Z,6jqyO5 ;W=.ޘݢ̀޻}2sP|H3 Iu 1gX}V^Θf\p(,,D`kAvNS"t6bΛc:UVVdY41g~N{0d裏2FDDDDD}ԩ5$$d7<0}~IL7ƍC}c#V>[~=+n;@ɗ_"*"¥{''#faY'3\]jEaY4VPR"p*9$ _zzcB1uL(uCq}=T;ـwĤQ9.w 7:֐Wsh_D:d:;? Rb #k4&~a=N]W)?l{Sׄt285VIzԶ=؁Op$f`ou\)`#Q_<@DOEEE+7|Z|0j(ڳg}QkcJa0@^Q(lOib0'455y""""""qoݪ IDATCj[_S a!!Яboů}Ejq /\hH`82$-ڔr+ߞS?`pgcIH gEq,,f&w<ϭz%`8llNJw5sbpIGb2Uۦk)g.bAN/. մi#`ӦMصk|Idhx8]A*++CCCp ՆӲ`2+Д{1 N#""""":%'hew |>ui?_=ֽ.^.7l{,Clݱd]P&yֆ;w:\u=nPl,;\W )9~|hU,:xQc2`Ҩ{ vw,7VV]ΔȂ]-Zc]κSNA|c*Z~~z"vʊ%O䏧oO^6fշO 6f}j"""`8H 997toߎ{Oլ+ٌ7|=e4ݎᘬÙڐX@@^)uN 2CTTtыQP1B: ࣏E11ş~_xC8]na?`,,VXVV9)ٔi@%%ޗ(lDԗEcX8#:]cK>8 2Y'ҽ-hn9\p^ot}:| 1&w27NEhTHNa@cSڧOMDD;ӈF̛77oo{i[캖f٪؈I9V[[JW~ u?|>u̩SGs:wUhkowi@[,(n8MnGAI>F#_'"""""""[I# CO͜ C]G똬(p&#겦MN14>&ve| |tKq^ c4 .5!}j"""]]o""ñ|r,X@q^aa!6nJ.Y2Q,11ٶo-[nXphX5"""""":F?uv;À%܃[/\q^Um-7psz?ғU-UTWJpͮr445 T?iT!PGi)P"߈HW)8@8&V& eW}3Y5ўJrN3[l[)Z| x4y9:;uJe {vΟu+<4"" F#.]8g.{ȑ±emmmضm\QLl(&0L54""""""""&NԾgw ?__f!9j^;CcaiȡCa*plx0;v $[1;/9H$S#7SWيC;fYi0_ ,[ϬQpFwL"w5^ (++;ZQQ,\RRRp]DuN Ajj}5 *aa7Sj_K!^(9!|t@:41V؎]4 رMDS y+ OXAp%R isU:al0]3z?3]DDDp .";A#c݃ap`vlݿ@p:"++K:V^^Q?ZL}vz|b]JvWQQ*:1Θ1cc+!""""">! S@ԓO00:1 ԹU<-) ow-VSSLvzõVeGNRRM2iDDDDDDDDS]i@B"|4PwS@? ?=& E 1N{8K9f#;hkjwiO]։+SM^d8G tsv.eѿVDDト鸫PԄD!35DӺpƎ+c8+ٳ}!<_bo69`0bPPRțp+]ӔuC_'ӈܴwlF0]ӈ+kps |e#9D!35%{vC_'i q3{Cmu<2\hҷo3#pm늿>^6zӈ\pvkkJKKx:iFQvKߏM6y""""""3|}>?_:no[v) z76bWy9[KKNKONFN#"""""""҇yR 2@zaM0,bd@_ %f9`3C/6w~τV*55`ciOݶV{k.₵m[""6i.J]՜v;l@s56` 4z}պJKKCp/Q'NĀ~!""""">X Bml6ۀ5kKvXdXU zuv/+޵ BLF#J.ACB0$!}O5!"jk"B])~ٽl͎?N3C-kP@#2 l)!|}Eeh3>,T `SDDD.ZСC13i,N3c:zh& f͒/^MMM^ x-˴kknX^8ttLpZ4ڣi:IDDDDDDDt̳4kQ?L<_=DW:*u!^54UNm\n,^,*;pLz֮]+k̘m]CpU@i>u)?cRR`4r,Uw/faX,'#í=eN#"""""""6GYCDh&#߽s$AoC~YƱf~=V[wX:`4էP'wN4<FY@}j"""ՎXPPP`;H'LW}}=;8N>|8||ۧ+"""0x`=p /Zk׮ս?s,{+SػwM囟nzϥc5* QtRL::'Xk=io NC!,{, |ORNK з!A g&`]DDDF_Yv-rssK/.M555[?}B_+Wtf2V^Z|\w#(HHee%̙&b tIW""""""cBB;Nۺr`Jl.,Ęy+]Mf3{A9. +Qx4=d|:Hb@Aus}CGlaƸ[qhkt-y` vN[ `Vق[QY 4 +銛# !='yI]|@1NӁ1 =hoէ&""Rp;MMM0uTYcX,7ov%3}tDGGȑ#a4nnv}?sٳG={= V"!!AZ2w/yN{ۺuu)STX ׺͛[l͚*Voԛ˖y ?t_l66ckt4ONG%gY6)F:a D6l)%oՙWUmHhCݺE,91w?fҩ~W6~9o60_i4t ƷTG(0NkY!+.ߐZ^F˵|rKW\q.R~ZSNUNNltm*~< y:^DD:vT7o:,JJJt]wiԩ`ӕ\/۵k:uiӦiРA^۶mGRRTaf\Mz.<'YǨqZ׏VJs*;ǡ;\6OEf7oHCoXԗ.m3$eSڴkVBZ6y e*UR돢w $<0ݜVӇ$VtsM.nM_~piƍ~$5d]uU~{>|8͔Ӥa5kx 8PzGNS>c?!!/"mJ}$wt_2cNe璤K;Ow\{ߞOm+6In&iVlM[̕re#lyضڶۇD^='Y5I k8M:7N&N~=pz.ҬRSwtRZkjN:iԩ~^ᯤ$5A7k„ ~viŊ?/ :w>PJJ&ҬaC0nߟS6۽mO$-l /}=<J)2@0hX/Jg46xKi2|Rtu}u?4tlpF}I!s ܆4<\()@~CJRVsfk$1 ԩSG>vޭs}h)));v,Yd }<0)W6l7]t2224tPnŋСCm6~}n@GzM)w%iK:ۢ!C9_Eg)Wi6U ZR>`o(k] %Gkk{'kDKcװ5:QZ%p7M5kDv f|m&5ΕV>nMiOtt  6hӦMz衇ԡC*?oڵ5vXڵKacsZJJ6m14ծ][3gԦMt 7(n$$$kѦM4sL5n؏M!K?CmK:7ܠ]˖am R vulc}yW;̲6t^s{R[_:I;G5i'U}YB]5O֓#)6Kcs$u򜭚*!Kat8k')ҏ5v=>ܹ'`k׮]~>QVڵkvZmٲE Yg;O{Vnorrrl2Zvm&ylDDկ_?]r%ݻwki&\#Y:5׸qҨQF>6)Ik;wrUi;g3P]߻ L^Tjx~4VΟK\}@k7kӶ"9]_OaI}f;#I=&*1aV*k-wBYa4[H1 ͫM#\*)) 󕕕l=zTeee]۹ SqqmۦfAjРի]!NvsvL׮*zќe檸TII:JMNVIP6avb# E%.:R\8T\RjRj'&E(2U@l.{.&)%2UVaZm_IX xeJLLTbbZhQUƪs]}V:p@Zu7f'MRzFu@͓_ay[c?+QyzL3U/^4wa^L3)2~PCp [i""^u>}ԚN)9̲Ni  Ӻ%[#ܴN.m1{$ 15Q\RvkҜ9t@ִ͓f]mY[=YM|;f|iYjSGaCߎ{6)9fuF6HJͲ9R.j-1O5}a8 jͥߗjpQ-=@ )Y6.hjm1\(U]•&]Qq K{ @Mw)…RT1tҖ-@x҇iI*f.HGvsGeұ=0p@G;W̏),C:rĺ^Of>X%DRh]jXN??(xTo]/c ]wct]Ri5z|kZ'BاrQ6v)ғo91BNߎٰA=ښ>?fHtF#k`OrSHk$ԗo1߽"’:NWv̒%ҬY@xekZzRB} \MYv:v'a4+Hzvܔ)'X !gǷͲvt6'Ņ z+pVwme44+$]Ajd~̖-4HK'd~-)mb]//\ؚ~WnskWfv-ݏQz bC~u׫5˝VKjcm*.ïa4H#թc~LQtґ#@hYڼ,iNohm#'QI7te:-Zm^5I+eٚfZ.RtY~[R7cS *֪[Rw޿_.Ժ^=_>g5 [OJhyv:-Zen[AK? Ѐzv0Q,vI+&Xi=W6ͷc6lFBO>i;fYOE+21SX_o۪sԪsKZem'uJFRbYvұ0pCa|;fi,k 5-i`*&ڦZy4έZi['_J{Ͳ(ԽY>{>&N8Q߷cL>Ě> Jeٚ ["gtWwcWm+-s{k72ܚa5 ~[c)6,)} 00!tIǸȑҖ-@pʇi'gks{բu.m7"Unw_7+7)osfe }E=líGмXܪqH]\Gjcm^Lt4G k@`8 `.)IZD[";#|9WXbK=ee.}_3K$INMHW ^ |'e*$z]1|K\3MzFy˭޿Cs6sKH[-ZD0~9C*d xp7-ZHo%EGt]Riu||ٚv [ӀPwS{ņ>BXsזxeB7&M} ]lnxh{j Hձ N,!J:Y4_Z3>NW/itߎٰA=ښ>>yRƻf(tcPtu|̜EynL;VpV5{bD3?ln~xzV5wҶBR>XϦR8Ś)Cjb8 P9CHo,Y"5}\+_cR\}X&P>Cn/[BpH Z./@mn]M4ݭs[ب05Ips#fePEZo5i{q};'uPV(mz,4 \u#RVk4n''z,]&3ƚpwSOո2-lvI+ͲJ-bm& ׭~Rr[Biʳۥ^:u2?,߸e]/TޖJͲlMBڸ֊svHP9QZNn1A I͌Ө,l염lտٚjI]̲yg[BiUKz]^=c.Psx.B%,i6ˬ6OOiVB^J6EեϛݾV}K}2u{-n2KeGKZKLnZ9QrYZBik\ZP6?I,jg1á(kđc]w9]WEM\mcЪjH itiz~t 8wzt $ڴH\4'H ֿjmA=W_ߚ>߽l=i@<'-b..FUD%%Fx,]},@moq-a7[5Ճe. gjl0,)>]tgo4 $}onj#mnEZVkl$^}X⩗އڷ[_O"#lsvks0ݐyI]X+EF xnxh\TR/-\$ImjeH޲5qԳy|-?ߺN շ|ck.6ƮZ+!..W=NYg_mDkEE };Kz`ʳLSzY6.><_}:޺oŹ瞫SO=k"#7ސv7;fnlf_ @)8,mYjMZZ8u*->f6#5t1:VXojmaTRQi[>xFwPse5G["EJOvx_/e'^NT٠A4oф5.wiwnSQkeGUZOVtCcx4"τ Ͳ+&0Z{rAgzn0M}]K]ս4io/M:pӝ-dmf9۶ms=?#X;*(W?wL6K/I΋$Y@v"f}?TzíiԵu=݂eGܛԍZf @#Y?ͮcyNPF34CY5vJM!O+%^WiҲRnWcZ1 QC>w}N;Mn%kM`ڳqɟ;iѥJ/_a1I/J4zrltU@%Zmm(5} TDCGۥ'RZ =_;tN.m t *zSο!C=^uS599f4}`ԽtDZ3Ep H٫ <$QZ6(9q_c= i~Ln0}}I W߽,yW5 ewr^#oh>g'U`7Qq3KW)-jR?(>Pl#އB[[LXJ,vV#)prt$>Nlɒ?o UE}Nvפf~ߐ~<򈥵P 9ҏeb֩7N٧/7{u?5Qw[4lO. ڤj((i{YJ-nVMeIfHցb"LH_=omb |} yY7& `:uޒbʿQ/,ұ_ `2 uUWi*.)ZQl=%M۬/>XI/KMԼZ+2"|l2vK~$E|Z55PYsgh0Pv55M$ӿ>Jr*xpg&[.G\NGGERRRӥSuDIvw%߮+xmެ[ǍSz߾zoC0,e:ֳ؟U=Cn윱-ԬaQӒr륫{8M5:]X=5pvF7g{rtfMHNogƱ>3&T[ -nSy(+{is`ch\JJåݺJ^z}7߇ ;^7 @Я{ᴣ9})_gwI `QNV頿ܘ^|V{uhp$Kt= R:RكKvY #lr?nSmP#/ɓ'egKjwX@C+jY* E%%۵zݺ5Ҿ̲-Rߘi̴#4RkDל@{x,]SH}2N`ߟUgmއقK$Mqzd9>j^> ~u= H1VI*ޚ&1xF{%]}ƌ~~D0ԺLAQ.V}qcTҦ7ͳ]9]{9]WC=`ڦ׬ӖMbxZ8JMdzګf-S!ns eH}Z5 R"Ejdpix$ _69JN P:o*.>](iBEK#FH?YϞFfۼF,~\l+51@ oXܙi@Ai\汚 R{x%}&xJb|h 2D>_nU|^$[Z ǷD[կc]NYEґ pg[Ebi0f%xw]wiժU^sޖj/ }yՊcÆ39vL}~,iNcmUnsFз?V|peӂ'[늾E%.]rV26νvP4<{cD D٥nM̲bgA4as}sT,33S_~JKKf/R33fHZ/ٳ+Wl6u_lw2D{ﷸ@%w̲u㥆\֫k0ZnFד-]dVi[ڋKJݺmzHzbyq4u}5"̲kI5wh@pg))}r? iTMII.r8pk}zeCRRT }VZmnuE߾F]:yN`RIYK=kź]^s6xk&EZ_*DIU13KW %nZieuen]yvQyQFl/SmZCJQ ͲGgYioN+ 9NrKd-^XIIIӥG5?)wgxT5tYF{ꂡCu7rQv@*.q÷÷79|c6hc&ۥ9c[+ӌ[޻C,')ߜf: cX=JvYvd impq;*Xl!ԩS5w\9ݮym۶' &]t332HߣQ>cN:T-n?J7eՕ"{&ƭ^s}N}CQs.lM^7H64Fy˭Y7;OFI[/5Vd8xlUk@5c8 DEDx0x_d9ilNr>3 6(;vX]if^|QjÅ,y>DGEiSO邳2oھ]FeZ @ogEFo}tT{_'J/Oh%;WP3y[{\urjhui{oJ혹AuTJRRLPoNs:4fϞ=+p8fJ=ޟn]WO`]f *%%%k(8m|r2 :T}\ڵo+!ǻh-a{iLt ޱQ~m88߇!7r1RvN٧v;9Qc0~U{c4Fz?[Si>|]z8NRDl:ij¿J1*- 9JN|07i$͛7k.""BW-|?ID4oTۇf̐~sUD-{yҾQoewܡЏAVב"#'7OԊkRd-B_j(cfcjzF n365ٟL8K1ON{K7~u=1 Ns94bٲe9rQW>}*M9sNtRm&%W֭]Ҳ2c4IRߺ|vpyOr~ҽ [|m : Ps 4}Ts !I{W^nz͍w~Nzf]]Հ4@4Y&􄙔vءڞ\{~.5)M\VZ>k5on*]aifFR8k0rI׏ܡCG֫ty / 9]n}Ep^;,n*Mn!Ox@wm4p{GwHUk6t.l *%797xk4׀5{ii̙;R˖ykw_>=[65/S]{r\7a-=,9.|L-2kKx=1YJRt_KWsdzzi|+EVM}nzxgHv6镓Q ~:޺.P P)))f/J /7Nq9 xGpB(jj'ݻKg?xP7=YÆdl5_(ʒ%1rWڿA:,ۡ@0u} ?Peʴaʾ`rk~úFƊof'g'IcZ3JfI6[WJ1*) i.9 _-YD=QR^+3ft)ߗ>в:Vk٤Ϟu,ZyV |<9ͺ|2dN_5w_hef1b{xw\w=xk#va0E4;ӼLR4i6,H"RÛ5gZ4@ߋke6%%%!%##C]w6nAzW^bb̏;VʲڦkYj>~1[Q/m3O$)K@0AI\XMB5@qj󖤥MxgޝVfQ}6zŎ"ˇ"3JlDeX4ˮ'eﲴ iJ1N+ 4iժUK@Hр5{gkڴih%cGix|Nd-XԪ>5K ߣyWta!cTVd4 (I{bm?._wz3}cpV`htVɗH7Njibu9[4@oN+ 4oie%'̘)\.:mٲkaÆZp=1֯ z6ϯ^-͟o]Ҷ>9S)j3G}V ,)N 7G4өЪ߃[~7޹҇ϵSRـstm[UX|܀ZKcYb~6qԶ>{)p9R~k@0[K~p4LvXXnn}?7W*(EDHRLL|?!AJIRS˿#5#9^ G=lD)5><ڲX=k.nӼZv2 [_d:,զEcS;9gh)_1_l2IMOү4YUj#um }=[/"cu+ i*%>>^r8s%9yRѣG5`zꥧz* <ҁfӥsϕڷzy=.Nz9ab+@; _,3_qp@s:˷UxMUKSGjXjԨ?RS^~X~qNlMSI[]Eo0ѕmPV#Y#'YiUЩuV f(t$ QU';6/ cX3E5\NX4@%''ȑ#38m8Qr7UNtNSW_}~'f͚i ԑ̑.,tJFI^"]ؽ<.nyM?( 5CڵKھc6i߾!|ËC]-7Wڹę?5i"n-5m5]En@hش,!1EGz͝Vw4ٺWhCSKWhl~ڷgs2 Γ\RBiҒ.aqR4iA٢lit{Pi&i%7<~sZi)))@PGyiѢEWG曥3ҍ7Z+zz}Dcp9NYۭ!=(]c:L\_~F۵|@ WTۯƖm+iSѶ-ւ]췏=㟧2ǟujv{AL|"#)>}XbWi!e7˶#E2x TE˳kF ~ݓ5gQV^BCiխuX|;<2}w&ޤt 6PVA]D RDl(?Ă  P;  "*"&$3,dޙ̙L&Ε@s6&L{w=֯$'|j'>)WՂd@QpCI)zԜ&""A3.d 6D9-=MN_4}7 lN#atCAծwҺz6[y/; ҶrIhX_{r,?H9MDDf:]̕q<ӼߴQsȟ,[=zۗ;DP$.hvÓO{ ֮lߒ3~lhuU ("""Ov5v iv!Xج>6tZ^ l}:N9ک `4ܚaWU+Q\aңS'\CP\ ܣ檫~oΣYRe9SjYYv< v/؟KD$DԜ&""A3Ai>@+=ϺpE)t'N^34iM69 . ~^zɬ_O[FuJƌ[u{3wx.n, DDD$[ ZN IDATh+VS؉kM:1+ "NM\[9TSj:H(>iE{|p[8o}T5EhS(t^TǾCyuTQh[9Ru8x0V3gι4olSPhS&0] \09H""4 䴌c6'1yN>i&""""""Œ%KݻQԩ͉lsuYQpP%z c=N^歷hިQ҉HTټf϶; ;M`Tj(Wʖ3?˔ hpO7y=j5:c&m^;`uaqc( N7U3&n'o ޴^ ^_3 $?\{yޙ~(ϚNd̓r4zo45pRGdӺrf>K.)ڜ"WꖅGd7  5HL2"grکiٮ<Ԝ&""""""7ߌ[{3τ!Jނ֭Ð!g ! W3y ;4J3X9~ڷ7W {Q\ټ{~k?NY4S' DDDHٽtCچ J& 5mjM@PmbbjU첿='vsڎj[,鴚,cbi5Kժe5]p\x!T^%bV넆jv 'rr<^XIaH%&41Nof-:@{X3%3Xj<]|ESlQ=Vz=0 j8VD9MDDfڜ掤ig419-999:ۉw,Yo]ҥ>}ztM1fδ&GxrP\a6qݼ~k=J~$ժt"""N/k ;S&SGjjt6+6kVCʕ> 60ӓL>7 ucbrZjN}O}Y:N0<-4anR%IKE{+J+ Zj(Nmc޼CۡbcYI/}mH`EӿJQM?%4= 7dԦ\{m1pM*9 Ȭv.?HHNN19͝.*QNDDDDDD$:&L[p8:ݺݤIuV҉HYL/MŊVceY/4)@N7q?|ؚrqHOL:? %ty}ꯟg)N눋+$hFFk'N={FM܁f-3gZ?K WXG&j-nV>F&""ڇ:ߺ57$`m[ۜm;_+1L/ܿ~3oL'6S;X'R}U|ic/5'E`YC$"R@jNON4ϙӲrª4f=}12d׿lNTHڵw17\ap8?x0._~~\ݭODͪUÐPDDD ]Z̘|7NhjD;ՌV^sWUWY6vVnr8?7Ϳ!,Hhr8dI0}^CnX;uwǎٛ=|>X:ƍe-[ZytyaTrP=<"2;rߺ8Cr&OT7棏ӱZ׆e_c1KD$XjNONˈཞ󚜖8"""""""ak.nvؑaHUF3:.ɤÝ'_~޽4UEDDʕVC_[DpB۶Ъ\r .ۗKLe ;H8Ɗ{کcظ G/- YIX ' _i"ᒞm?v5hިdRI07*IŲĉDvΝ{6.6)-jf_7SM"Ԝ&""A3mreD4iܛ49MDDDDDDQVV:t~k7n;#ԤhU,:thV<< noBtḳ6-vqun|7i˗CB ۚVE/o5?mk}"RD(aMwk£ êU>v oN^7xekuYNkzY]9M$\xn;+Ss3G2 i W}w8ӧO Kz>go7,"bUm!;Ӯ4""4)F.w$MNhr?Fb~N'~!u CPysρe_B/?/Ԩ~gOpD`(?ah8xоJHn~A.H1-[B֦(ǎo&M .r|3Ъ?|,E64pظ#[t u(ѢK}3k)*> 05f]k|p}x5DrI5(hV{b,co ^)F4)^qaÆqڜ(Ucx3on;־<,!>鯼U]dT|zٓZ(""Rtlf5m om_SA|<\=7u*tes{""ѦfMa*HO+?o5%%?c5?pUз/,^ljK'@Mm&-qE0PP6 ̷f֢YWWט$65TVAj|5jW`DL8L\0 25XD"DD@L|^Y89B&H۶m;w񿰡K./ "T.p&Ͷ)lI xU.;y,r)Mc Ir1:7sȐy9󦠾}7JPYm1XyDD4)FH19͝{sZJ_Azz:۷ȑ#~k;<&LT73DJ`oM\{&#+d"""u;oy Ӣ5]{5!MD$\Ka 8|jH~UV _]`(hx~-|-6t@ f?+[Wl,KũENZ`CVAͰ 6Ε tbȺo婧6Ek8I~"YPUDD Ĵi&""""""{aժU~*TiHJJ CWԲeKbر߰QJ"˥~EDD"ŽЯ՘hQ_<(] Zs*h!Hs:Y3x#ػ6lѣx3̮]cG+GzKd-jk@0cI .\**w(I)ʧ[3kad+ 8qI !ѷo ^yÉClg-!m?SRM7P̂#" 5HONˈ'^՜Z4)ʔ)S駟RV0*"ohڼ~h8ELҥ;~}`\8t>̚rYi<\}uq)nj\N.ouu- $8peE[ \cj< u00>xcǞcܠ6|N{lsH3жyw`Hi""R i69-i4)fΜɓO>iT;j(*{Eo fǏ /؛'OIaބ 48lYqk߾d_`#"""!t<u0e xe՜&""""""gƍ~xwY{x衇W /~/OTz5kO[ϣPf&&m 'kh~{ȑp9>7AqpUָ|y5]G6E$p~: {֕.G#ǢօOlA4=U%)w]ɓk{c=^:$DI5,nl<""Ԝ&""b<9-I;&H9q۷qob`رaHU ? *Ub ԮVͨ9s{@I җ_Zƌ48pMx1,\񡹶DJgO[ؽ^z .ؾn CGlzY uH[0ylI=KArq\аd5ںpy0pZz.]P]T棏g 4asz<=X. sͱ/!5H7e.^ٮߜUs5>;3R 9 ZؐokZI1PJOH*U+O EDDBkfkCfBtԩp饡MU#%u+<<{=f :pR@4YmPZ烻ʾC~k{tHJ¥]ףm؞Ŷ=!@0h玩>S>n"}քキ;XKN^h]bjEm#"bBi""R )))Fu"Ҝt:INNW$om^ٳgRE!_~a}6߼S =T"""Qhf8x+_jJۼ_SDDڵof5Uذz$0uf*SڱBk/EIaH%tKK[3)?w} 1픫.HN6kPx*23ՠ& kׯ fGD5H6s#`rDjNѣoߞ4-Z`̘1aHu/7E8iz?E/LȠW_9Hp:ҥ^RV֭VSZbb)""P5M|N2ZG-oCӨ]\b^۸}9D}o]IѬ]7Jp0 YH bsпf3@˖)̙se?9spu+HOs).PhĔBPs䴌_=5HQzҥ [l[{Yg1e,J90v,~QaoؐǍtRFόsۜJDD[ n&MOÖ-V@w ZlD7~=<N?뮃^\7ZegYfg%CY5೹G֥g!۷=kaᯱ wA-$!gZ\Ro9r+X^'r7}8  7HԜ&""bܜ N?q9MDDDDDD_KLL?raH4ԙ6 ~վ<M2)dTW^aI)aNض`r:kWذ^~*U MF[@wPV[Sڼ\7lX7MA$Vn;:hD]J C*)LU*q9%򭙵:KTT;ġB ?<*V4kP+8vL jJ{m/HԜ&""@BB:wF?q>9-ەkMJvlG}Ĉ#jǍE]dsbd ]۬糦dg)\~|k$^ylN%""RloY_+W;?C0`l5k溇!֤+BshSW,"Lz,EMJ!Dv-_vK&;w)Meii 8DĴ:R|ToT'hz9 $MRf?ox0yDDr4)0=crZi&""""""ltͨGk^Q" IDAT׮6'*fJ^3ߺ&O/Oꢋ+$ěQȰa2T"""qƴ:5f̀/ƍCODD$afxm8\wfkRСZbgj}Ym e"sY-ou-Sxk0$HѮi8$!^`-_6"иqI,86j[$ZƁEP Wb,\W׼~=8>^O?;ӧ!Hں^}rr%1z ֬nY<{kO ~M>ڷo-L)oRɾ"_fC~URq8J"ƥ疢L|kf-,Mr wO~nx|\s9ΧfDUҹNǓu~eH?H_tG""4)0.ϋ;pz49MDDDDDDnv᷶v|'Ɔ!Y1ːlVc؛'B]|4b1/AAp0$  W:7`5 l5N̲e0s&\pA?<0 zEQ8۬J)dobdӎ,f:Jz qק9 pZS?6 _kPu$瓚jvn]W^ݻ]6't9o#"r5H6t2 w{9MDDDDDD(۷/ ,[WD MFUU=g^?u*[g_֡uk&F^ܹaH&""RUi0cu.HkK»Bޜ9)SWDʪOmVLSDBŝ~[8[}չaH%]קLo'Ô~K )`\VD|/aTiS&W\۳lN&Qb "k>&ۗGD jN3mrgS|>9\kԜ&""""""fҤI15qD=\ <͛z0|y"Xmygq4x x 6ֺO?A&'""*VW_ի}]ѣСD?0s:Jf)&vw'Nz6mN$EAojS{ÔƐD_O o6nάX1o=͓uq啿zɋ"hPW4 ,y;<""9MDDBtژ;Ӽ^/>8;4MNHvٳgںutB1t($޽0iq"]ncT~FY.7> -9voiӂFÆl"""E9X}͛Z?hMQij@)5Ȉ[8r 5YFH ""R` ]c6'ɛsEWOԜ&""""""ࡇb~뒓>}&*U?10/Oh׮<ߧQmzf&͒+mN%""bs`cN-gW\?lM@+Y29o]'CI}ؗCy=,Zvo]F%֧FIQq$ɷfV4rn]L*ӚBʔehaЈpP6W_rd5JCs3ؗID55H7e/(g9MDDDDDD"رc?~:KFJէffK/ٛЭOmT{2=ǯkڜJDD$|>kcAZ;‚0r$$&4HtZ׬v킿'wy66PN<C.d K|# uTo~ n},>ujT{qt-[lN%""RGœ9_4|),l"""a:kZҬ򧞂E V~lV P-~:Vuܵ Gi""R`))(CaONyNi"""""""Eؿچ {p$aKcVr%|y.]j=JٸcͩDDDu@> ͚ҥ1W!VKE bߙFuD0$fxɿk֢0O8t^2;r|\Q !7cGna%f$(KݲpIuC`ʽbI4ED4rwn!""""""jwK.[INNC* XӦгyѐi_"'7;twwۜJDD$?w {w’%P^hs'mз/ϦMХ ,_lvZYV7Hie5'KD-ѕkz2xk/sj02sǍ6,/7ݴٜLB:P!?E#"ŎH7ey'3x=jNK/s:|ӂ`:0| =&xy7x8? o5@\Hlɓѵkz-fʔ6'"/.nihmH`jV}DXQsXQ99-ǕkDDDDDD$ϟO~j}Yڵkgs") ``I`^%Nt\Q.&o7GjF>nm |dR,W4BgSsX\\Iy3 9=srDDDDDDm߾[o̷z+O=s;zo9; $Z9} <^= ]ο*~.K0NƝT7x|u:~wIצ6T-e^b/|t;("rDD$$L srDDDDDDpeddpM7qؠYfL81 $d`(9s`2111|4b7\yQiݽ;HnxQ93sxHN}63 v1w.}7D5A)N)N"Z)>wu u(S*& $k׏᧕w xu7< <~sX {T ]bW_c0z}t붞qڜL'؜a<;ȾL"R,9MDDB"%%' iwiYi֨9MDDDDDD{eŊ~ʗ/ϴi(YdRIHk]g^?lx+)qL5k.̨~ʹу'N؜LDD= z,ܤ$2 y,K`r)׮.]&@ RY^:\_{᳸YjD]Kkf-:;7¸01acx.:BIxź\DDDDDDngfF#Fu6'[sOvV5jN;mڴ.]zu]'fc")Ҝ1$Ǜo%'O䦛n1/BƫA)z3ϘO ;wڗJ.YcrAÆF+7> Y w9aaݺ f ""T K=^}5yy!>n뷅{TK!D+'S"1%ݳpjXz|iM鳭 Gk7y-T<$-aʿXCjN(*rjN|tڕkT9:SΆ#SD$'3gxկoT?履اlH:p6o瞃ނIL>xƍ#B+/?ՖKZ!wZ<-.HÐH'W]L?; o;̸4QI9X!z p6Gds*)jvu;g0{=yD$*9MDDB"%%Ũ0&y|X;8dSs駟fڴi~2e 5jC* x%o%KSOIᛷޢajQEo_srlN&""Qq>ر#k{<$| (k5cD SӚW  ȗ+W&!FӤ$ڵc9:o|{:@Ss}TU411f֌ߨaW5I&H4 .W!LN;55 ǥi"""""">ӧOgȐ!F/2-[9VC1X,zzj޿?}?EDTF4|x\"""bNz>{ۿ7M"psվԨos")nڵ92<6xx r S@!!w]UxFƚ5nCMrѩ!Tt#f=yD$9MDDB¼9I&N1vZ.|[Tvޝ? P f7ۛZ"'LlSAMDDΆG+;/&yu'Gpᅁ;cvM-lͫZ"bq[8|wTVeÐJ:5[31ߚYrY{2 nY fпft\O>iL\YoN׮xԠ&w6>yag""i˜&RFHh;vn'OKyJ ]j*[n&\!!'p睮_z5<{~y7D4 ?x~!u~_@E"peT;gsS6F AՂ'IpuY!!f?8zm!#9Z1Уkפ$np5DSs䴔K19洬8#uB0*޵Iru×_6&,̬}ѢtԠ&yyRݵkNn=jN*Ԗ?]i7irXO: IDAT>aa˔)ShӦʼnXnhҬ6;ƍ6S*y2FffkYJDDJ3`L׮ %KkWk2H`ԩ#_j 9hu ;ӬI,~">"'zrg8׽ }3;I(BoQ`WI-BѠ%(>R|uMbb""Ԗ.= 7l"% M]:C8׮9L &XjN0mJO9cqr8.i""""""b7r=2ĸV'=^m]/ѰfM>H3gbo<0k/CK"H1k0j-^ RjЬ|%c^{(K8?dn`&j} Ю}l'_ pl |ު fMMR|kWoiJTYSeM$'AMǍ^׮Iڝ۠vj5DDRsEᦟ99t򛈈H^N:M7DJJ{ k_{׏YY|\Ӻuj4"Í꟟6O8[ksϹv̝4_⋮_Z cÇfVۨ,hjl8 +5 V[+cKtue`skVI6|mSJ0_ ]n9} ۠w}lWGϘED- sZz):#+4MN[oe޽NkT§~6 jVo|yD Y:e l2kDDٷ-wcy'rl]7a̟5ɰ?;">\r6?̬=VƵ ߿qT *fb~v]Xʚ50IҼy˗7ߵkr59}Z jr?1qsaz{8Ś\"R9MDDƤ+R49.gDDDDDD˝օ`ʖ-TR"T #FOO[Nj\޴)_&F*oODw?CwmM&)9~M[}ּ~ǐ[]#?ӝày , N&[ $9[m$lhy5nΪUͩPsN6pdɤD {2jM.)1Ԝ&""ncL#i;9՜&""""""x0aQ;C-N$%ΈMj&a$kx-[7*?=u~6掯ATE |rnKNpb4/ mJ{*SZBX9f l@8|4I!"_cRO;>w4q3';ijN;v7|3iiiNk;wرc=JJo+0u8pZJh׎O^}?p|d-X|5}\DDD|Ӯ] <# 2[s">(5wpZJmT"7xn/kZZpK?~mm"SJjՂY5jMڷjPT{@kץ]rZ4qԳ',YyVs[nNkWܹs3h7q" O]֯6ޱ#"xϾDDc6ls.]`,"""⛞y r횤ƶ>p:բLkAC_oRu#DR~m+lwdcLa,BJ:u̞#ܙUWg2/5׮Lz!'yx 5ۘONls#;wrZfz/<GDDDDDDJCfu,ZE ϼ~Dˢx^]ދ/b7hPv8=bW@2ܑ#0ldf_S6|1GDDD|Ыk#FNISD#ܥxNʗ l ފ}wop֜X? 6˸/x VjFaF{qUٱy#JpSsv |2߷+"Ei""6i9洬49MDDDDDDLL:ifc֬Y4lk"DDծ_W[ :yYl2$4H1WtSQQ1aÇWvw6i28JP!ܵ8f:>xbt8Xh ݑ#NNjCl[ df4mj|tڷ_϶m)'B8 hYBx2H\"R9MDDxr'YyLVƅ|Ԝ&""""""ά]!C>Duʗ'04)dq=7[QmFf&7~8X7`z??;7wr+aBhҵ~:׽o~]8#c2rmN%g;y_״@*Iix0tߜ;5ݜ<8s&)h`#G2a7R|P0J~-f+sH4q(D3R<לwfDDDDDDp:DϞ=p~_Ϙ1cXd㐰&獒+-rX̵7=,[֔.3#yx;n`ÆdIR&6`ׯMIwI%"ņDDm'8_zQ%5H~ѣGuZ[NfϞݮ^BCᩧ'Ol75<|#F)ii\7x0?ndq*qS']2z=`ADDD$_eNBsaYj*| [l#⣾\}?p~nt?sT3r%M^8Y襮,]z(UʟnUW 8u*N6,N&%JtpnZׯudgA@E qTNNs59-+iɲxi]dd$-"2RVK?TnV?,X`m/6{3dQ 6p ~T""Rd99i\ؠw0uuDDDķN;7&)VP2!P=(Dڡns*}U*ZJ|ǚpx|08_i׮=yIEp?.mW=8s&Ν7)%@ :uDپ}%#""""""o0k,u6ٳgSNC 1cLotUO Ȩj&'e6mLXּre?)UDDDܨQ#x=0'5-*<ှ,CzrS'5zlJ{{J׫ +z$XÇٸ1}q"+4ԏ/hLBBisu#+W8(094([ܛKD.)5xgy'|ŋl2&NH-hԨ G侦i|rinw9<~!]w*T`$&&b /^LNعsG3?\_Fcƌ믷8޽s78~̱6{ޯQmٳtߟv6ΦMAAp!ZIDDD|WS]؝caP'DsN Uu&"MOWY!7:i|Ӆ \{FFgr@?V§QwK@i""^e{1*WLBBs%55m64E2.]sZZZϧW^rwd233Q{az-3HG^r~nϞ=5jROg̀d[}1=q4W?gq*qIr2k8hܺL""""<75zr? `a)JUD ބ&11޽{S|yϚ5k|﨨(ONNgA˪洬,+Gll,={d޼yF ~~|.""""""E7ɓ'6lؐYfai#XjϜY, &ɀ[n1=z$ZJDD=,:d^ 0tuyDDDDl6CW,`vD^Ert}E#i훣TNgpFXt 眊 O&P=$ϒn vYΝs ܹ Ez=p4( [@L?ÜJI}Þp{Ǩ9MDa\9yJjԨ>ݻ uHx)g ~a8=;9-''Gnx8wK)c62[DDDDDDcÆ N뢣Yp!H%>k?l6SG_F}SK@by}Ŋ0suyDDDD.᡿$۠發@DJ:p׍ړ#iڍQpCSUmWKJxff~ nSl|A=+oTW-|qIх_c|xl\"1jN".\رcp ~<3ԬY3fTeۍ6䥧zpr洬t&[ÇSJ*L‰;ߟ;SșDDDDDDuǏgΜ9N;w.5j@*i:@.f))ۖ6ck>L{q}/"rɜ> cǚ0{6.xs[ժs>1u X؀ffE|̈́je׎QH$^ő6䃐]b0D:o4~R\&cxz gTC>,N&%Nh.T5.ٽ`noH9l"b95x_J*QbE>"￟ӧO+6iJO\sZœ;9m۶m<Ԯ]-[ꫯrBuo8z"""""">_5O^~e6mJ3f ;v(r΋=n]ODDDDDD۵k~,}aH%Z=j32୷#v :oIW#DD֬/04K]fY,MKvUhZ7+|l턽iYn(|~j DBBL<Ζ-y.bjҤZ ^Ũȡߙ2ũDjY7Ƞ¯q$|0^6}9ȥ4/gN8a\U@O:|0ǏaÆhтI&㾦S2R=3=9-ӎ;oW\Aj9r$7n,Tݏ }Ai""""""L9mPҬY3y=~';/^ {XG髯ҭ];;wrM${_"">/%ƌ1>2*"""b օ]MM"8f/;S4Czm`vw즟5([J;IL4)k0jTQmN  X/f勶Ω]06rO6q;5x.y*n[{_~+ҽ{wϟOFFOyˋ3]hN;s .;wbŊ :kגSlTjLkQvg&"""""Y999}N SٲeYp!!!!H&?օ6u87CY0aڴ1߸};]lrDD w߅J#"""bj46SV)1s:o©R!wDROතMN-WlE^Ip*T,fҤ"G3fG/0Xpk=3̩?p7qADDJJJQ}r>o'4|Osr*e!sޙ,^ŋCOhHiY\4Eb6xb̙Cbb"E{CTl*ֻ: /nz<ù;tOq?駟RJOِDhn |d w;eVxO'<4 ED|Яݻ[GDDDTiX7J۠6vr͟gC;XF]<<&?s$:Rd%'3a!d}P\9zŋ ݘQ::WK>;RE63)g~_~""""""?~ڨ_}'axD*0hyĉeA|[mبn`R,N&"23sҥs?O _]IܝJDHMwp𝤤9-HaAHwOcZ U]m4$$xFo=mɽ7=X&Le\{5jD O (Zl0&|3ϻ'DD]?ci2DU'@<{zÎlui9998Nje_ȷnر̙3 )HhdyjUw͠}Yj{'aQݻaA<; 'sC @*O> y|LDXӦѢ~}q!LsӦn6:MeZGDDDTv&}_k vqܕJ6v?[v:m$#@")QQ> MNـ6Vm(X5 йs ~~OLLL4Ĕ)m}} PR5*{ IDATZNS+XOh!"4/ѦM4ib\|ϱ׊]GsC XQLNsd$3 Z\M:C+Yzפ9=yoԩuٳtޝsoiӦ SL@*Ce¿e^inW*AAAH&G҆l+VXŔ*7oMf;׬ף;EDlX0y;eMDDDXz_WG̀=H P:f_N2H%%·-0zOMwh +Abh5]P{m[҈/ ~=4yAܮ5)X)j0E 03f]'gM14/rwf\\D%*N?<??IO95䴌BLźW|wN*ͱ0n'"<6j)[6)Or`< ODDDDDD\SO_: >#..D\ #GOuy|Th}j/^>?N]ފ/?EDDD,{%^-+wM/lI#@Ky {S`؝5g !=kBBBӚDnk˜97kP{s6uI"FpSpϚۗF#f~eQsWowۿ?mL#Hxhn~ ՚_nw^'.nN 1COWtfc?f'<,4Zˉ\l6iβdgB7o/Qɓ-N$RC@Ŋf;wAS|2,]WdT?믹'pAMDuYYmCYGDDDUk^s^6|^8i@"Г N.kCޣ/Wm[ac3Av]zuϚEЬY[4CiUyhֆ{GmdeAM CZ!:egZTp8otSs0`K|ElTژR /hu:!5ZNPXmv?*ֹ9-R?5fS6%Mڀxp)&NaV* 0_|6 z)^h隳uQ~W?;|woYG ?sl WbeA׮OO[ ))jt^ l˜9Ǹdf 8a£mZP5/fky-0|״a#$,qZr*TmDDt,|&eeepU=#oVʕ+W?IJJ⦛nk׎'z {/ԪeV{|y|X|\g̠<-bsYJDċ= S?84lh]WyL)2<\:E9hᲉSgpә1:q@-^d& E_/#aeX0l񤄄HOw|_uƄw} zBzdpE%x-t \ E_ODƖhDSvxx[DDĹ)S0x`u{zfF*Odf:OVo!r}JLƅ^gN䑿7egqp7҂H C!G^,LS_I9mY].x=< )xSΦ[n,[벋UTu)Y΅޽j˔Ks%طqI}2qHSx_?4\ok3:wW,@x2Áth35d/Cy}"%?sZ;rև'8~0%UU7;&%eR8oL\ۃWZuDݺŰ`A#k"Ke{a(g1 btAE_O\TB}%,,̸}?BLlUiKjMAaֆ 5y1 ~7 6[mTQe*ԘÉ[9<oҤDDDDDD,6bƴ`,X4)yn 65=y|cJx}Fn?ilFũDDJ}rMDDDxY=ެ1 m4AJSA&p=H1CFikcUfAHx*աj+\l\G^bVq% U\Uj"\<A¹YGvzP늈ٳgaiٲʼnD,` /׿.sG ~|Ĕ*eT?~L{w*4.˖5!:ڬAmt붉ՠ&.* w6 .? 7[ӳr/5`w2'v"b֘m)[ )m~.e*H:@JWR 1<3'o<+U})=DDDDDD$o֭FÆ ;8ڵ3=>AM,դN>Rf>?mO3ܬ("K~ V0mW 3լYy קց -rq7dg.ΧLUՂ=Jg0dqM ,k - @V\%$8IL4V"Ye|&}w.]~Y=/Q- u! {`no8"4/ժU+5kf\o߿=.W*[QF J_/,Bc%4"?{'q]՚_2tkn|'$pDDDDDD`Ǐo&5ƟW^yD,K浳gIIѴ_iӈ3;ɓ?sũDDJIk{+..2۠c|ޏu<,;m^/RnpZw׽I `z֪7Λ-"6-]zCiDr5mΊ͈5ks\}$%eZL-+оJ$8^{ȄCD\4/-+^EzSJC"D``(ae(O+TьRan'T,Q唫ThKr=uƠA~OLzŁVZ????$XvLtyDƍ 6;{k1ilS?6/[GDDDU߿jBtJ8{~SDxX}ũDDJSk~U.ROfvto4YH1 <l|<&:pN~=p.hkuM`tUm Ƒ`JMϫ[7UQ7&ӡu>=S$_1!pGC* j#!3x9z%""A޽]f <5nܛRe>>de%3f୷29s&M485m jV 7Iujӆ&hT?瘵hũDD}?- FYGDDDUOsfMAu1ʴil:D,11CiDfVnNjLo]} :nq2zգᡖps (Z,X2Ll W'Rs0`K;'ISGvrpkbǓD""""""dF#GV%K`Nkuk׎O_}?9997z4-Yd""4{Y.6٠cUTl;ihE.e4u7vfhX$KfG ܶ5֠Y@T]ݓ*[6-" Y҈SժZ՜Z̞G W]D dA8~El9igܓQ˨9MD˵lْ͛ggeۚYrg"2R||Ȑ!+"""""NGGddd8MHH^@*KV-Z&M6 :0g8 r8k$)F6l~26daQY(4a^qH6w%g*dsCuarz8wȠG9ZjG~O? f+Wjzܙ^zgQAgH&"RLLj^ EDDH'ڀNU]e(l{xݩ<>u~v\RΧK t. Bjt0%J;ILL@Uʕh(̨~߾4j}M!0_^Mp{x}!<<ܸ̱}uTVf:'x߾})qEDDh"|CiQ+ƒ)}363+^>J5H&"rm kךVd>-TDDDS~xROAo}fnsxIKwp]9oFzfpE5@*tZUs|L|pҥD,T\ +V4Y3Ӿ~ħԌ{m+W-OS5D5p5WN(:t=DDDDDDțoɌ3l6>իT"O@dYekyuѣ223y0'$\6r$ZEDDDU)ݫpGyM?Dqټ4Nm"yG9hF)fP;mJF.֬XqLqҥX[}~qh:l`ӦdO۠mE +,É(R9MDG 0]zEiONNo|UVhéDDDDDDիyGj~iwnq"btixQZ頢Ka-0qHڴtn2YJDٶ V66~  Y+̦?Z{Ѽqueصջπ6{ܿ |<%%Ux(H&\qE)'2W֭;oq29p}-Ԣ~!;=DJ=m-Zp9*;+̲.I>{Cz8:pr ؿ馛=zRC)cVWgy$OxF”4<6n8%0}yd]WeEڡp])~Ne/֠BYqzn_`R;7͝VF5$τ1W_5C(LW~'U!-ku/B{MV:|Ln~v_>Bi"">d.o^9rpB9sb6}c111ÉDDDDDDSjj*7t'N8?~؜}"+sLhz%5_?!x_n89˗€qoyduJnuU642r]M Wn&H /;LVUaS+x 66f ݲt)1ǒ%M9ƨ,t@VPͬi2_G7Ôaq=DJ5޽{ac;9`̞_~!$H'EY~Ӻ(.\`4dĘ}ҥoy$_ )å&'u@6nnq*FiÇCHyDDDD\Ǚ׻cjڟ:FCpoP-Q&4煡L(o=@qaB#cz&"CӧKl^9ݢ4la v;<xW_}ٳg;̙3Zjy H1fڹn)[H:)oTT""""""gٲe!:N;Koy9`맍YVu&5 ܀[ ^oeg1~SH ʕ-0efXրKXfDvzO]W׍<Dppsb񂤿NswwرcUnDDDDDDd[}l߾h۶mCÆ "f3f+e988``hB?w$7#"2L 2R,۵+б>DDDDJu}J5r @redkҜ8zZЊH+^ (@t oSu긢}{23*!2F,] 3fe|-? 89aBv4ugFT8FDdBCC΅ 箤6֧I"/fnjooaBDDDDDd}aBO?}܈TL.oۇ8:8`'޽גwD̔܌vŲܚFDDD&j=vM,[2&rX pi$o'G>[UWIqy7|G`Es`[kɼXPP2_R&+M0gN}^Œ%ƷvEm/`J'&iQlf8FDd:t.]\m4ٖW"88zhv̘11c lܾ- qrtĶ>CPnBKqqxz$ddL"Pz=mXF `P}ϫ5 :}er8Y.5~Ӝ\|~CHcYˀbyu&1o/B`hnO#kG1_]矗t4@zk ˱V`q gu#RӈiǾ}zMݸ3޽;ڶmr+""""""ې"[`sX| l/ zSXRn쌝_~~] _!!˓܌O@OCDDDKdlJ@7ؘb@*Ypq`MP-#T^L;püSި\MtR&3o$6"*noÇkLw~ĕp8Ȏ1+WgߏGY{ #^܈6 5 W\1Y&v777٘3OO];r07WW+Y(K En~fDD 'st&OۅH\yaP5Ib_rv!WÈYq(.1~_Nߟ"ˡ5$&\3 neY4ׯMgDkȒf|ixNFD5&v]M{ 8j~DDppsbzdKD?8Q r#""""""۰`ݻh۷oGݺuUhEdUߞVRZ%),:pMB͈Ge_xWOn""""%N~䧉e{-wUe$<7myM~/ד|O]KFdV9Z *iw^T *{kgnOgԆ|fάj ,&7"F>+]VXzI4"";(wn7 rJjc rp~/dz|ʔ)pvvV۹s'.\(]x1u&9 ܻ')၈oA6m'ϝÀ0InFD$Q@,;e.DDDDJN}.ץ,/=hݝҍj` %/]2m;&n!}f"#YX'n-Ew'GvӀuZ~CiDDv}xDŽz] .X-eI]sttDHHʍ_ll,Ǝ `4;i$Lx`ԩSGVDDDDDD-33D^;5?UhEd'fŲ;vCU\V@&MNa̙(j%7#"zl)?&""" F,Lnj x:p{Oa}FANThEf>z6ὤ:`Pu"!AAeoҼp!nqYckZIl@m[4 z*XMO`Zgr\_zXz'""1+WO@CY;ܚaaa*!"""""~:#F@\\Ѭvى5ћWC&V \  ;gC7~:BDDDwmku*pQt|hQ(RBThDfi2N5ac%N*L`Ld$u9&li ggUncܸ+8F*sv6&nͽ n7o7"3pFRtNX&[zH[j=z܊Ⱥ͝;4suuΝ;QV-Zٙٳ77iirIjV#VqzBaܹs@ԶoXE cG]Dݻ D:h#Ioyv7[Uℍs1(3vjռHnݼU&t3dHub?h6lKК2|KT^mt ^aۋ rw?{mV 7Q{ĝЦOr#""""""yf,ZH(l2"B_ IE hNֽ:5\ThEvs8i;eD:\\зo23er@l€nnb[ðaQ\Qq&}>|| ~,+6{="Sq8ڵ?.봸xbF%+&wzK9""""""{ &eOq-Dd\dm!~8z5֬)_{7&ԈH?pBDp.DDDDn\)urZ4xX.W$B6G3`Ĭ8d|}L-EQԨ8h~Io[䧙8FDDR=-J ¼b"Q}/2TnEDDDDDd0h ܊@DXkW_n""""Qg3Rs 4vHn9 yFs]xThDk02V\_g``|mr 5vSP؀ھ}xT.M4+Wz*B6m¬>܊Zj*pX[ӈR8 ˺:TMvors;S6>YS͝[e/GlYNܢedS f4~v]Jr+"['N圜v!"""dkZz/*$r9I+0gW,hIT60:Pd`Z*Ӛ ~ TysX6Dڵ2__gǎeg#'G+QS:0X,pv{ pEhh||^g#˺s;ZXXԚ5kêU ܹt[YYrPk WOJBW,QQN{izu]DR~z:?N4 r)N#z`8(1B+*+LLP8"*VGP z:w#G:zu=NOGVԨv> 00󝨜8FDDѶm[<yN'WKlT2flD֭܈/2ePv֬Y9rFDTqIJRytl˖P~_cњ5[])*~E,;h.DDDD":<|>N4ѪT9huw*4r9~(X4'-gndv=zӳS*!RW^8z4j%}F#=064]7:` @Qy{4""E+a)lGFIa/ЦLGG |Ȃܹs BQ\?PVDHo 8 x& ;[n2۵K.XqVDd7Ξ}8&gv!"""qn-pX xOjqs<,Xrh[5+/KhӁYqV`# 0^$z)3s`t:'D֞8v,~~Bsrѻw+܌ Ύ@p?w=d%Q[S/-'-I%6H/3&Mr#""""""R\\!C 55hqزe oBT6ƌ6HCӭcGk}Gaٶm[]8qB,׮gvk8Y%x Fy: [+Y-^sZ*4`nW8 XMN/.(j338{vD"kռ@b11+ os@*@`c`H фwb%7ϚQi,7c""H5jsb.ԦbŸy]/Q+sDXX~'9///J**""lO۸-}N@5j -## ߋp8JզM>>)za),,ĠAp]-ZテFQd<@`p aYAAX??fDĩS*4#" DGe}Vn""""c  ^_hZ!8V4S&ZRܳ}Z*4",O.#@5gw ի~02id?WwѣЩS%|jjzBllfDF8j|7ۓɼ8FDDѣG+:'rImQ\X?ARO8*""""""!!!ߌ漽ʕ+ЊLּ90bX6'شInb /`B%%=iذ7hN {:z8dNTЫW23d@6xq,-FQoH~_ l Z"4""*Shh||˼%|^1F)S܈:|ذaќ6n܈͛ЊM|1M2_͝+-,* aa8[ݚֹ3P.DDDDe?0nkU gE7\4w5S&98?G50Y/n>ܚTO@{2\mYPPٛ?K믹*! N8tuʧO(kfDZWBJ.Ͻ/@Wl^d8FDDejݺ5z)Aœ (fAzns H%?# e{=5K([PXNř%e IDAT""% ۃ,W#IJ ʮޜ4{WTl@߅y!~Ŋ4ɢd`moxyJ``UtYJ{]ZwgdKnF$n%`Zgs/ֿ<0/;N#""BBBcAb$7.|iӦ؆:$$$`tFC [oB+"2~xuZ>f >xUl^A&Oo/JnEDVн.DDDDnT ) &N{3?MFs=:U)uThD g{ۉ ִ;7v/3R"}g0-?'$7# Lk5A@q{]p5l0T"vGMOFH})#K=ְaC܈ȼo>=^AAtwl۶-֭[g&"n "6oۇ;q"L"CzUr+":.=P3U~"""-pX&Ng~Ēw8a' x#Rn*?o`5`Wۓk.KTjCdY{w[<-CP>)G`tss@ʅ((7773F91VHj#ǃ,$DC[\]DDDDDd"##7ߘ^~e\ph;KS=mZPj띩SƄ B̜4 oܐ܊JTXCKn"""{*# v+v1&Oʮ]Vu`~ގ4}M``[z4_ع-./(a 0~3O"4f@zM>$""!!!! !/6旝~ ѻK=ꊗ_~YFDDDDDDm68piii~>۶m3sttĖ-[ow&׮0pX63شIn3 23wD\ML[pSOADDD(>e]&\i tb9nMKZ#g@fhvZx H+VdZV٣޽}ZH}sv`1PPc{w?%2(?/i7[% iժu&7uxbF-)BV[c,]aa!V+4TV[Bُ?O?t,YnO Ϛ#Fe獵 y=J]܉BDG|@DDDd6Y/gGy}d*Ћܭo!s_遰k@4id6Uf""UhBd4X9Mt}kܖ܌HP_]D̑Rl$""annn3fsb<#Gnx S`߾}o9sl00zh\|hzصkw%" ױ#bٴ4`N}HXp! SRwIK39{Muۃ4^D#[4 =7wӍo?:F>k|T0;SvްJD֩E 4lVf&22C6DO^P^7`„+XR"Uj(NseշqL\Kw|f"&GXߒ>={6ZY>ks~> Wx^?KvK`h j,Ӊl $""EZlݻ zbOtEp嗾<44...*""""""2m43{l{O蹾KSQ?"2ݻ_wrje"<ףPR\Ml=ʁӧ;iiشɏED*(,IJO=% s;4οu,Z,͕D}'$""BCCcOA/x7.eg"10yd_^^?Bhlܸ/]1c ?ӦM3'Yų+WZaV8;c_'_v CBg-ݺ/Mz"Rɍo mL*@V =N,m(jyb8hu_9>6L[7 ;~@D{IN'z}VK##K;[!>_8?{v.LWHε(;`vNNϒoDDؐ!C[S[^F .X:-RAFʅR Wʭo޽x#Ϙ1III‹/\cҥ&$"+ӧbT`^}HU..WեPK|Es}_݅kp= \@Qz{^""""dGG<ߧ!, rm/= HJ-2CkЈ nzr:MtDef"#3ACdx>\| T=@0c*ӈH1777;V91GKjc;H8y"""""۶m+xzz:&z97.0]6v WW~MdWnOu!չ%KT@P ԩ/c`ܰuAD*~],צDDDD{L;>jTi-[v65߾"erڍ_Ϣ\]mz ,w#::O6Daɒf`Z0g n%oC%il)Ⱥq8L(x!7%Q`S!R7k S ""h~+o"22...رcԩcrO"R<^IU"R4ZҮ?/5m䒺[EN`(UW $9g' 6^`1RW|9k[HE%(LDD Mԩ~X&-Jƫ H]j+;e`P@Ӊ&MDDjkѢz!7=Rb#q2 R܈H={c#ڣXk׮lݺyK,OlUThEB"3Mw C2M~Fi""UjCd ]UlLcƻ9JJ8F&~Al ܹ Y Q('D"7=YRzMݸ3޽;Cq"""""$mqssî]PF )ODVH~[ $$`wӱi~Sq8,EIp`x[=G"XlJQ#f@^hIuqh  iiR`2]Jxꩲ7DFfԆ6<\UbߟEqxH&Og`\;M ruzU+R""*͛gϞyA+%6z ӧOW8|oѩS')MDVleKrPW^zj ލÐ^S MHD2n=pIwC ;E, lur8͖,Q uwXT@v*Sm =wdUyd89DDDDDdn*q^x)MD6`pY3lL p>TԯWD _I}|" 9:-ZCDDDkL@[( t#OEQ5MpYw1iz\$7"a[`g[Ndwڵ_ٛ4##=%?uC:GÇ35`O=OCiDDTn;vsb{w[H/BBBTCDDDDD$KZZ=*ߏf͚aѸt钔 "+xBFU#9"OeOU۷r e_FDDDd6lusTpZ O=H:#g@FhxwZ|$e|?C2o0ut6rs9,Cd쀪UǏgg#'v"op(2(@=pᮤIW抉t\=ۗK=>x`Ԯ][.DDDDDDرZ*t:;iC s=Y`_,=+Y6MЊR7 b)MDRSrADDDK*o\h^U^&9Knt?-=h&_[l-0Pb?gX :] ,bIgԆtX Gt@bK)Gf&Ԩ9hnl!4""2f͚W^yA+/9w軣҃H ۶mSy v܉N:UV={6N8w$kNNܹynO-["r2TL)MDD7q8dy)pXA h*OEݜ4|"<eQc8@kcFZ$~N `U i~ 4h ǎvm_A>QHK+܌ nNض?Ou"4""2PE'VC{W[Wpʏoݺ5w.Z޽Ǐ/_ƢEгgOT^//^}ʕ+(*R;YcŇ~FX۶f<8h!\aQٺKDի'٧4|W?;[|pXiV^F FxvhM~)"8-tu"'.3R"ղu]y; wKnFT_w`X+@;V,ӈljժ Rp ğӧODDDDDDjڱcGo.͛ꫯG˖- --BJ7/[& Y ^۷c… Rc-(*D"ܜFDDDr| (z:SѢ]N<0xؤ2j~ H`is7ZP-Y@2߼YUjCd6ulj@pp<66zE!UM"sk P^ l<Ȕ׉, ӈl\\\0n8E].J{J=VR%5Jin_|pE74!"+7~<'=spAnPƢkp0B}9yyҞnz:6/ o8FDDDR"iibYnN#"""s:HTp nk OƷi:Wۅt^++WpYѮ-7h}[.̗ԣefNF^NFDNW?֭-$&Gs~>ݔdC]-%ZO 8l.4T ֋'WCSpW zw~ý_J=޷o_4oܬIDDDDDTmVŋSO=Uuf-ׅ-[0t(9nߎ Dv@dJK~"""@l|@Cy},řj84d8FDDf7h T^]8u{!/.w|DDDDDD)%%?S=FqpUL>օ,ɀDG׮Cfp6ߏ|ÇZnrӸW5bz .V """2؇@vXj"8%kTvۅЙl%܏{qƉu\=r?IR!0?A9Aeҿ/^7DV3 @goW(䛭()1OdÞi ԫ,?!U8ٹ`ܸqΉ9¬2Rխ[?Y"}0ֻ uNš5kPKD̜)5!%&xs͛Kt]O_lP5pZr{LN~&Uvǒn"R*'˷+\VPQ-/{^U}鴄B' ]Q@tlXguEP)*ґR$:IIHo2:^#k(TyLMn^hlY%9@zЎYN?o2N*p5m=m07Tʯ L ,1tPle[IV*3S."/;S;0\9Sٳg5kԿomݺU=zpxQ(URb}Pi]ڶ)[7w)+C*'![,Tg C%\ ҵY%Vٹ%kA嗔}֮z |~JrdMPiFk)+ }Ç9.j \^W]U(Lv6;aKuJ[G+}=0鶖cҿYnp͛7W^`kךOrJ9BBB4dR=8J=<ss)O[.Hu .mV.,kb_?yc=P Hʓ&7ۤ9mZu ]nf2V^=P˖uEӋt ?]|Vں !&N< ϴZǘLOða^3E%EgqQAJ}RZ*u$sqٽ:tkʔ)u}1cf|j2))>p.mj맹suw߾ ?M9S>4ӏSR$-z<jԨJ]<+j֬>@[lQ-!=YD4>Dx| ]PnMA?HrZf,Y_8wtc@el %1ؤ[I$Iٚ*K=<4_H_m(6nyY;cGXp&Ufft4l+Uvvn'}ã&ï1;s"<(u} ,|С'nW:R_СvZILL֭[-fӠA~5J {L 7.^,?nmX&>.N_zI ˖i=Jhۺ3m 6WNV`V_fYRjYJ>/se2k ?ūhRPhKйzp=]{.ޱ|齣UɭTP>e+ }ӟ6[ђ%gzug4q"?JeHgc8 `Cfv +;R_3fL ʭi;vԆ 4ydժe(Sd4zYXSkr ѿ}VVГF5?Aܰ iV>'>_-X}<͜d\dԅicoN$#&)7?Qmߛ]}z\V]/_]?Ie &MDp<{G) -3~Pq6͚F[(_oߥSǭioU^5R5b8 `f͚k5ݮ]?Lн2ROJ}f͚{*t]Tgv5###5amٲEݺusL,N\vt^q%\FTTDK6k;gc8 Tԉ0d]OSd1{XGI 90rE__YE 7+(dixC [չs˗U>Dm>=`|AAܥ9bsS.SfY)Qve I)z}>v@= P?XdQ|ҿl#N>eHHՁgz`Ӕ)hذzF"wE3>z@eGTXhw+XRc|O,7`s2O+av\w/5ͦÇ;t=tܚֱcGmذA~jժ@ƎT1Ν+%}'OU5uhrMx93Aze3}VV6c'Bra\,ΑQ.7̻G5ﻳĮ{LPրk#+N!),`&xor3˖еWNGN'٤[j̘Fb򗽥.Z%K=3.>M sn~ii%,zȡ'ْbLج#K}nRӦ/0gΜJ_#22R~l٢nݺ9 3H}fiOXhF},Y/qqkƒ%JNKΖJ J5x8 [Zyڎ  b`3T\R] ڶ o&UUۨ=DS++xΝk(::LEӶo?~~Q^^IPߏXJJz_G/Z^^}^{-I%ZUfJEyK0p~X6N';g2ΞV}}ԨQow^/~f~1B|=f3GbȧwB?^[t=?pVC~xU<^~NBͬVIg߲%IS85ٜ4b>qN^5P7Wh#@zyiil;ԻwTիӕgppl23fsPQoL=gv._{TI:t|h߾͝kx( IjIҺw %5k믿 v~Y=(z۽ך4i>}@e]~ڸq>S88n]i`l~>D@@[;Z>@ڵs?5KN&23r G%JkǛ築C$jKd %%79"9P^9lxsqjgx\_sfYO[Jl8U>ĕrgJV۔RxbbB*w/T/85dȯz$5n klU)x$.3tP{NUIqaܬt9_E9>j(67]/QQQ裏yfuՂVࠧB <=[J7?u3ݧZ'}JNK;ZOC5kZ%ͲQU KfxRE6ҙtmvay_ǁcep(G4+Һ>zRy-[v z0'Ӏ?7̷O|R|;dIٿ߁Z_jϔV>_r\]q>'3Y 痙H;;JτjРAuOk.ݻ8o4d۷OÇW@!6z,#}}U}6Θ[{gt'pAiҞKA~=iRT>s%k9\E\&<gɵQAikli&908x]~y7/Ok'OWzcxf+5һ[~!6l$9h]X*Kpp2}}0_GvsggEFrִN:iӦM4ibb8Qzi)d37tkN>@;Zw]ajNn 4onm;Jŏ㣤~}9f!eխ q.BCl=W tQ /'IlɛRCx> 9ߟZ~~Ə?֭rֲ0 G-vX6dGc&5Ͳ%eN?,.ۙi'uhǼwԨQO7gΜr3QQQ8q~Gu7x,+͜iix-[jڻp_Íi4pZ6c)yY6&Cy%Tl(]Tjv}X}]֪ӯJX&}{MQ0tcu}[˖jGo J/c3&i ^_'R@uaˮ uSsI}.ep6mn vvT/ە|g:ԷuM:thMH;w/z@@~a߿_Æ Vpg OK)/> -4u`nw m:Plf9i jhHͲqee;O>|pL' q8.Hz%<_/TzOi3v WddهD-]zFQntݻupǍf8 Cմi(0КUjLK ՠԾY(OZ}` NСCY~ %n+{Il=zt*kkZΝi&}'va+fͤ{5˞=+}}Uӄӡ5UILnq3dfJ))fYIyfj-$Jm[,Yaur'ԸOmؙe}`RZyRM7dK[db6Xe[֏?0Xjkf]8IT 2_>p:.׿թS8kgS(ťcbb4pJuOTpZtt>cmڴI;wvC+pgl'x~Tlx:Nvlc>e\͑11 V;7E/tز/\]->|P0k}l^ck+t;iРA7n{~cr$)?7[v.R~v 6L!!!N b۶mJHH2dƍǦ4ޯU+i'NH˖Ib}/x8j>>=[Lgf~A>5K/醖ebk0n8aSԩ}xz\Vm|Lt-(oYNDoщۥ\%&溭Ot4bK}t\<"sdR5kV%ז*?yBi.OM4э7h]0I%E:~`Ҏ\jjذZꫯO>D6mb0 Uڵցo1ޫe4wᵔgnlxr dK˞4_V[[*3˲5ͥu`hve0}zsӜD#?ՒneYbb5}% !8*,kyF4I*.ג\v?Iּq[xn3tP{}ԣJ>QFUx[wڿ~adhͳ'[>'8(HnM{.7PxI;ӧ؜qRqlHԷ}<ݻ[lb0ƅNkO6*zonZkj:aQ7,[%@z;>@%\{m~kHN**5Pƍ7{h۶sn1mi;{KVZŘeSI{XNpm맺usϥ?J}EURjY4p1iڷPxm$\=kJq %ҵpAώ5KuӡΑW"o8*IO4U /Dݺis5eIw5lA{Np`XAA;]oO3 zt IDAT6AAA4hCi*.+1c8x wWy),PA6Mzҏ_~-[}LJ1 a*ҕ =j}u=K ZZLVvLtNJf!Sq (Ȧ3("½?b8 (޽9h͚m/O+/.4RR ]$.Bja=Q:>p n5d8pTSIQZxg (5mj]Z:x>yug:4p1,0\ GNKͲU߆/KLv%\k1֖*'Pr&mSM\>uW[6fڷ?Dw׳)*~Lp\Eo2Z], >4n7tJ_cԨQNhCRmÇ.N:p5؜*cR^lM%>.Dx,,|.tm[{CVe[UH RKˀyxjUZ5Paa<8nJS=]~BU]邌"}qw׀7I,{t`uPy|Tݭުu+~UW]6wI_T$Mfm1**;EZyG){/NIg ͲwJ! ǸҒ5NO_lT!.D2]wc':UV ԬYmGcbLQl@}?\NL/_oH@PBiп\YWqqa4egs~իGnKգs7&ǵ%7}*=i3lذf4rHGxivYi@,0\ q6~D:g5Xj}Kצ;:= jN*aiibk8aX bA #0Ц;;jӦu睱 -4'4_ Z2˦.Ws7~ֻwo5oōv_/]~Yvi^k?b8 *)?fo%]0,>8f IֶJtmF_?6D'дqMζ4.f6٤7Y0K.0x]5{v%$tO4Tx%?WyKvTkް*4 Գ>[?nhSY%&N?  I[[rX5t}T7:Eڸ3t[-^V7_-UOӥ3g̲[[xJyH5xT`5o@;,]M60VՍ= @uJf?בZ`G7P~1NVttSumXmt֯;@{.=;*y~K.RYNxVZCjy' "ըa+)?>/r H@ Žwb 6I=evi[ց96˦K XpZ6mnAԱ}bYfmjˮ,-*7gI舘_յ]] *MJ3Z_V@ٶ5iwߍױc=԰ŇW&Uvj߲mb4)X4]}T 4bSY6,@Z>g mOL-iݴzoT%G2§'̳lM 0Ц/l5*~6gϚ7ѣZ@G] ޢJԹY6Sk\|%OR.fﷶt)-,ۺ]IKֽmT_ͳՖxmː6䠺޾{q{;> ӤI*^ѬY:z^{VHөSoqeC7ʴ>(i;= \#!,רT]YTo"]>?gezXbsUqFkxC-ͻL>i}i@YZZ11 &22HO=H{vU`P3LjkM?"<>(_tRÆf٥K3gA\6JiR Irlkڃu 뺠LEvܐ!I 鹡}Υ|i577ÒU[Uֲ}7^[;!lSZ\diSY62HsmkLwd)#X[Vm fWzR*1̎m((W*5If`nu)>,{rt[kNᇥͲsH_oY4fvIw`kzR@|6e腑eVu렢 f!70Ҷm5oq>&&6ӳyv-܅4""A̲gJY|MIs)ҪW4b$M?%e u-6Th7>@r` iԨ?(4CoP3qr| ۣJ? n @JLrsI^j}V<+eekH6*N0'򈟻EE0DK_%eÃ)SZA236լɟ]ޢ.=v<etMM$DZv,{cԼ}?ҨQ&Mj_a8 P HAcP'Xp AAf\i"k۵,we +-QZyRx ǥ9f T}ꬔUlT.Z6~iJ"umM#%~om\pCJժeg϶ x;ΝoRQYVUVk_R(p<_mqu}3/,, y'^mn+gٞ2 )=Y6!Aں:LӺtp+= K6x'JEf`ix}kD9,{_m)i+TY(,ﱘ6*),,gq>p5cƘggͲx6Jŏ[EK-MvfI4?@h]KAu-K/n.i'nxGIck@i״l)]wYTk7ڳG*))?.ha}?eoMyJ 'c.& em%-4~OR Q_VKHf͓k4F0I|cmFv:ul6kNV`]Ų:^ev6,kLp3fهX~R͛ lRWiY_nTjϞ=ڱcN:s)??_ժUSլY3uQ7vwUx]v)!!A)))JMMUZZBBBpիWO۷W\\_տTtDٯ 83ڲkNTj(:"BZRXwׄ1N}V<+eekH6)v<kԎM&'D{r'!WE:]U EG)^@& =μ\htiDE5 DJKn ]vi}RRD-YfNL͚5էOoܹs|ruo]wݥ={Z1##C˖-s}6MZTH5jHtӧOލ5R=r-ZH-uqEDD}t-.sJΝ;%K ~\<͟?:v-Z8u[_p MPҋ/=uJZZ^'f۶235kP+tSϞժMJ}NTVJߣD+7l[Cl6]޺{UF*>4ۉ)ͤ@x׏HG 76U khm'Z.(3syj s7kհY맜-~w?fhs:x4O%ϸWu5h1v犥ΚeHjժܺutW8ާOV:;si&XB+VжmTR7nTnݜTaĬә3V_ns8)}Tat,}HXX{_̙,wpu֩}z+k߿n6%WWԎ;kРAWvR"==]}U߾}2&IǏׄ ԡCuU'OVAA٧bA$'nSttz쩗_~Y[l)s0 Bҭe7lW}pku_zO*<&IEEzg4t^Ė*wbcLa>fI3=h>&_Vltv5f\_CM?W~0Mg/{rXc^YQt}خ>;+*7ƍ(\g/\ piࠬ,͞=5*^W_^wKqF^U~=󊏏w߭K??~\W^y/_n=6oެ!C(11Ѳ{5g;w5ʕEiъWf4b͟?_gH.9a<|˔y]Lo(뫕+ewӔ'Y裚%zp2Ν\ [Zy:R}3׆C1d#)횶05\Yu׆Yvnz(ϚHͲ#ASO:e֓͘qZII ']rҶvc @xiTRR#Gg]wuZ`%wb͞=[:uҐ!CJ#11QݻwnӇ/|Wׯ&Lx뮓ͲKW9xVo;8}(fδ`vN|y,$njmoY$g2Bu}l*x,_WܿG>K7IzUwHHa^}55H=;Y׮T5,C$0qFݻ5*dԨQ裏,Onn+VX~/+vM ;7E帻IYYŚ=;5 8 _nm?pTД)S]a?&N uwh,l*%%E7|z-wWqڲe Uddl6ٴig{!JYvי*KJt3(+R [ŋQR"CΝ;ذaC;ӦM3&??yyyHMMō7pU9rn*׵}_Ldv7hР\.\hvK/sN:uN`#<<fB^^֮]?gϞ-W&"""""%,[ }t:%2dڵkwQ4ƍxqHk(f `СާBe@԰aHLM_,2 7^B-lb85q[o4޿ \)dž^Nű%ٹ%X+ Hhz" CǐgѹWC& lܛ9_ÕgF#|v}ˆ.g]p\rlC<^~Y~=St: D;ĉH E3fCfܩŞ*!<Z:_@gB؜FDT .^{5$'?YfرcBBBQF0`=7\7n܈5k`_kF.]*Æ |r8qBkf̘VZwfYZ-^z%E"+[wssCLLLcɘW*a׮]xc?9W^՘g PfMfΜ ]CI>}:?;wm۶e~d„ ls~-e"{޽{i&V*`0X;Bxxx_~Xh.];w~gxDDC&/زE,dw 7Z;Y|}ye  ܘg/k[EEId.^[۩YHGN._d]7/ZܵFEGdMV0}|mC+ԘgZg#D_`ĮC7>'&TE Ӵ5~i!!n5џ%&b6IUDj"8947 8Z8U ӈ}_RR%KF#OnvMΝsNV:`߾}pw }=,XRQZ߾}q1pv6?`4!!TJf]۷o ;vE7d\x>\]]-DDDDD&~oݸiʧe˖1c~'aǎ>}:x kG#"]cM)l۶lSVq>6g'J=׫c``=wR v.U:4J"""oqǀ<xb:EcZ0Pq?6w6am;R ΃'=C&`H6j\i,?⧟J=>cF]Dd42 AULZNs#(as_ I$,mڴ ϟ{{{cժUt^矛]W_!;;"S3ΝXvj*8qBd֑ .Ht:z-E憹sr)O>a${qY,\{DD<xi8 dl"+Gz_cZZ6mJQ9}ai:DEղBY=ܙr R3Ц'+ as7777Nv 8p@Db>c_ 5_|E 6L~TkvdٳUL#Gh>EgذaV9 :T0fW_-GYLdd =}s"zts뀢\eTlN#"ԦMmV~zdffȼe˖ϙ3GkWV /l_ׯ5~Jiԗ [޽Ia0b""R++.oW6 ٤a$7XyiKVpwu%NI pZ6ٯK[k"5=cO __Pu6ׯg:HvăBYOӹ2ٜfQwOĠf!"iF&6ܸkJ= Q Mc)__:r+έW6K4"r۰իWF^~~>֭['[ի:thӧE-ᅦF Xbs͜^NչNDDDDM6h׮dmÆ 6w YС@- l٢lICzDd-&6=霝ѱE ɚdBfVʉHqׯe6mCDDDzX7F^h#ʙvHl,x7p%MX9E4S|'Y~MN3 e}<[Z)Ͱ`0g3,9>ǜ=iDD0n8Il4={<o(v;vl=66s*Vݺuֳo>'#CF3|}8sZJ4DD8gg̽\\l9.:(Y=r"][~sغ-3*VDxWHLdS >Y|@YjX/8K֢ci|dkEU/A-˒40!Hvn>b#+e7&yOȚڵF׹'C6Ⓧp҈rvS>}l-''R%̜9Z͛7F=rt:G'""""1vX8,(* `}<̽`0lܨbLiG9cGes2b_ΉZYuXOC{ޞܲi13$: I"GҼ':wmڔT畜\,L7xp6B""_U5`_ͰjzbkO.LƲב,!"*={"44TrJㆤ\{#""TLWo>Xek<۷ oof|#ADDDDa$*&!""L$.%8|X,d䦧bŶm*Q_JzlGz٩46ٗ_/<4W&9,~ uI?؁6OxL=iv_e@ZA]78Yl"!7ͨ+UNS1ҿ+xgDHq\ʉh/8=-.p6FATTd-557oV9?.[DULc9ܹsW1MZ#GFN8b"""""e= XꇑF<=ZY}&q0:6V4KHjPW$˗&Dzx͛+,#; X9()fS 7S/E# 0yFޑr9-xúntM(63 P6 =&T4//xY˗q%+^9M /IdΟ[׶-$ɍlX3xXzw๦FXve-ZY!Uf_dӨ+5/JֺR9;NT],D6 #GJ7=dzTNT>_}zSӈl3~}ݖ/O@aQDD2ܜ悇\3؜FDTxVfC޽{q]שSdkO>IA֭e8yk׮ۑVZ֬Y,۾iADDDDT| )d$u]f!6nT1ssquZ C=HcGesY| pesT6Y@6"{I{B^nr?OBQzTN qZ)l͖`Ѣҿ{[BKdk4`iEؼY3[Zdzp`nC^J{I)NV6csQzǍF#bb7ʕ+&M7n,[zI,k׮D9siھ}pz yY&=վ=HVmߎGQXT$YڦiHqɱ9>\,(D6jz*G/ x;TLvH)=pired }9b N$8%ȁtƍ'ޯ^h ))Eqj2Z˶ ^&1%5jHvMXF۶mekC^^iצM8;˟c޼y"""""RFBDD h֞= ܾhMQÆI> {F9ͱܻ$ nY,DDDTy)WO.ׄmj*.}hrnGm IDAT@-ة|ZGz((T_F`?n Afkӝ5-zp_ADU^/=!&36^paIL,_ =W HDݺGzZO?#.vv,FabٓS/#(0 :kpшOOxA<v*&Lݹs{!ƍ5V ] H`2p-)]vr۷}DDDDDb >wɒ%0mDI""IrzZ4i`8i?V3lܨrc/99<).(h@,DDDT9Ey@C ^t=xFOFD ϚQM?H9%:)YrŒɂalw9,7iʶdIK4ΘQOADAh4!&vmKE˖GwP,:elG @WhE8*6Ui`0TLݻwek!!!puIja&ŕ>ֹV-III*Q\#޽-Zȑ#~L$""""Ng0}װ!Эڭ[NUJ@)?{TNU۷}VqHʉHQN9IDDDֳg.x^| 09M<|: n(w1yhd@tM)|2͇N4P;[l5@j rAғ3Ą J{ !*DnȐTѶ7ѵIKO\#U/xp}ˋx*yܹ3=Zi&ZjIMMkSiYYYχ*nݺx@J?hĢE*}7ippGp3MGFb7Lٴ `2p# CmN]DDDT9wViX[x L 4pW.gJN{&^-+$3oL\Yqh2#5'/15cZz{^8#vPYl^_ [f20+*m͚$ܔݝ DE hQAoѫ)?"" m ؍[ہfC@/$ rJL6M,⮬k'''nݺ*եOy**85xf6UAz 5WG}%6W^Vn$y.\.YssuŸTNDzySl-ӈlWQ.ɀS oh=(f_xry~XdslJ;/ױB*VlKm{pDU ;ؼُˢUWGP  KՖ,I{SM&࣏z lDDTQQ$`w4QZg{wkxz:Y;hl x4?!#"1cCf0Tb9K$srrikhRMeMWSNEΝ+EEEؿ?x lM49spI $""""^/xAAVXr""R/0t}Sa8;9a{o*'<шJ>Ow`eڴQ>U7T$5 zS.=z*Y,V P@g bpOK)E娽 aFlLS™lu]|Yl&I<(]iS2.\(}j_wٞ-=ѱ}d ζCg/{1.'U Wvىq0lN#"$ooo9Rv8qB,YYY5k6ulG6WVXXGhzjWTWbh߾=v튽{Z !e=5#!""L$.?سG,d5ƍ*&QwK5V7dɎ ֹ3MaDDD6֯o_pk\{>ؔ"~LMu`H<(KOUWuj孭0A'Xi=FO)NDEՒ *&6~i:cs/rei4l 1{v^.ƍx?<#ᵗc1pӈ,@4}@ 5 j1לf\...5{ ޽{Q9>}GxŞFrY?~\DDD>}:unڤlIMGm%k[~*'tR~SAТaC*N[׭9b s QbPY k Ӯ L 6&DYnāNN ?t*iO>)ݬm[ mϞ4?^0jDDTcԀdaI~cHHk׺`&0|YCX IIݱzust"=L&G;mq"F,iDDOqƒUV!//Olyr=6st:ٚ=yʣI&8r-+:uUVYy*V#!""hbkOS6$iEXui,gg!# puq{r"R\QpݕBDDDk6zC|}P Ⱥڜׁr|v}[ʫyCwt6teH7 084w_L=ju'^ /{#9QU!7ͨȄUN_~XzjF̜Y i||1bOgI{jpubzs';v5P v5oիsr,$:=-wL,,$**JLlذA <9Y$"XGXX,~osc0(iL%7=ЄSUNLzO%֎8N NMk*92rӌHqP&~5CiSnDdaMO;&\O>i:u?Q&OmQ1ImFl- csL4Ia)::Z {ʔ-''7D桹 +r?3̟?bGDDDDTrPܜ#[s4KyZ-ZQS:u0g̙3.]† gf̙6m"""hDDDDD₂пlٲTFBDD*3xu쵻w9 o47J܉g͂ do_kG<, b"RbuS6ύ}ׇK7Tie_]2P9 x4=V/IG{sF<[ ި_=Eq8vN~_CbE:!HUBv p#WlmW6Ν}м'.\(j$,Z^^Nd D`Zx[())=`4xZX*.Y_6~ۘ؃|"idQ 0AA7,YEoibpf;Kkڴ)Ν_iiiضm򗿠nݺz ̝;WDDDDDz& *!""ݷ0_԰ag{2[|gNvm ]zE)FDDd; z :-0)3OˀQ x'yqޖUSW`T9Q䃣W]7{HN/V): |{89)H^_K[JKruk/Fp"GP &Ytܺe~=xb\&POZ 6Kȸl4"" rvvĉ%kݻli暹U~!jRSK3/// 0_|޽Gbĉpvxb\rEDDDDD_0K*~ Yb뒒ħC8x0t2 7^|]Oxdx=TS1IuBDDd3v2Hl޺ \-fѿx@%EEJ>] ? `~{l='ψ?P/9#侀eC. "m`W%߁QA|,NM#r$z}mM& :Z7JM7?bҍۥ%FqlN#"(ٚ`Pի˟b洼<5o<ڲ4ٚ=yԩS',]W^EDDDF#.]B2"""""yΘ4idصkʉHq&'eM Ǡ:yWcӰʖnތufS{aIN[ǩiDDD]*>'0ryI$߻g߇-T9mjNmUǣb؜FD#G[/0Zff&KVPXQXX([ Q1c>}:qcTJCDDDD$OnzZQQ/_r""R\>দ~R6 $'dk%`ǯȼy~g߈BmULDVq:__E eQٮׇ]cd_"=,cS~ I֖lJA;ˆ@7+:IvZpjZSOY,DvN`d醑Blۖu?<R/pwVv"GUzؖ-HI)R9eiѴ!o*JM=(y9NDOOO5JvE\nݺpu>4silek^]E =/l̙3*!""""sFb0c` lN F#ryOG;hw_}UDd5'O 8Ȫ3 ϋwqF4}4=7S.N.0@˿D%9;i0qI}8SD㌶M=%kEHr9#؜ΩiD"jAV \ҥRyy9WPOzfB#/OP9ը"x&_ٟSH\&~7O@d}ߋHaRsS.=zp%W|u|Vd-:6Y4U]29ICi=:]rpsbܸ*'""GM׬IDn6OÕk&{r`]9HA-{we ':ßP67S.Fz `#ekY9-S~:Zu_-v+_l]W)DڵF6^!<eLP:m{n}Pq_ֳ_j:1fClN#"Rس>:uH֖.]b˿0`lmΝHII5]EEck9mʕf'w 6L4+77WViDDDDD֠a$DDde#F~}[(ddcΝ8x$l}fx4򐍸qiDDDw 8T|}ry۷9rlRDV^h@]i32Re&i@5nXg7}Z륧`$QHիxn#23qtn61WgQg?w>œ0yd|ᇥjؾ}; bk2gϖ⫯»kk/ɄO?T޼ys*1|ٺV@F#233%kV9޽{j˖-3+PPP={^P>ٜb?қ *X YY?{6F X'9c*Iu5j(J3y"j{7WY?''ſC[ -xVN NŔDm;SӪYԸqA9KG2⋵-r.]|d)))E8|XzY˖ n~nY<bcFGrϒ}nAוWݦ+N] 0|$^ 7=h߾=N8!Y1k,xz*&k˖-rl}ҤI][ _}n޼)[@ ~X_i<4r"""""Z-&O>T-11۶mCxx"gn-{޽lNb3`]jGϞŅ{n|l}oqH9:%Sӈr Z 7yۭ|#0P"ܧ l0(?CQľvٜvnb191lX VN,U㏇8w."י3G{QwӒSkcڴ`EKD׷o5ԮJti> *ek:p] ݀tע'b؜'Zk " OKv؁x3*jĉ4,^ ֜0~xEoIW^Ŝ9s̮yWTJ<([ DDDDL& r""RԫKɚT5##ekXEtfƸhaӺwW6V-MM W<[\/V;cpOSٸ"ڤd#r8~A 꺪NNNkl"OF"4<9HP;RUNTq&|dsh/8a; @P*iDD*`ҥرc.#uHJJu`Æ 8|l}РAUKc˓]qϲvکHLXXz)Y۹s'!PޡiH58h{4""DFFO ITE矗'%%aҤIn\\Njv̙3-zMdffbРAru̓V븿R׭[ů5k@DDDDTjFBDDV%B6KbСtlٿ,.)ٳ+=)B`Ub&MBDDDK\|3N]F`%DpN|89<5]$kK7ز[b2/Kwmb;':9 ӈ*CLnNM-#Aw!zǎT _7߼)[ !4T숟&=ٹ3e IODdc1vXڵkp1_sƌpvvڵ _|Ůg41~xˮڵ+vjk*֭[xꩧN6m`*;wyشi/-[gs*sXB4DD#֝?IdY =jŶm޼oѳge3&O3;[dGN[׭9%0݃co޹\>Aҋu sj0iHd-1{h)0&W4UfsۂioLT+UNCDL-xq k&Y:ͻ+WSS :NM.mQ6`sdk^z/]3{lٳ2Lx믿]7J_KIoЪU+5xtW_}S#)) Æ ĉa?r j%""""777HxGDDV6t(`@pzZR6z4Zگ'Nŋe6ӦYdN[4"""u] \*~`#?v!Y *B#3.5"|v,?cd݅Y%ehiDU+Zr"rdÇW3K).6GS\C,&O]3ut١恀'(^nڷo֭[z{'K@AA f6/cƌ)s ۨQУG _GIXx16m^{x7ѽ{wَ˗QFX`rrr,/_QX(wfY^v""RKOݷO(d_##+ FɺV} \t:ų ._[[yYUIT\{S`\JG"4Lل`W.MCCm}˛uuZTIT`p1ʲR̄4pYFF?|vKZh6nh۽{x=:Ծ}{uAڴi~']VV2ڣGVnnnmvڥ]_ڣG͜9SH~M:USNU||N)_*IjNa4#G>>H}Uaam\ .… ku:,. Trrr*-^X/qW't 2DoF0hp78aGGZwXK˖eq 8hbiѿgui[/2g״`x%p*- ̧ jwYBGS먆}\-Mӎ@pB}왪 (wٺ3@6oi=߻v:ѫk4kdr9Ųi:^)\yC]bimۺu9W~hڴiJHHp9%++K&N`.21"5j9rd+2hYni&g 2$˾^*+ys jܸn ޽͛GyDqqs{Jzt)nS~~6nܨ1c(+?8pf̘UVSffݯS4hΝnοoMAr HUUvAX9dH+ Ғ%fYp^ Ҿfx$ IOn6"j\"#-^V ߱~UiW%yufZ^pim4Ncpȑ@dz=1p#U5{4eq:㌌:j֯_#-^C>뵘 md>a*> tM O<ёw7Yf:tז/׋/Zvm׫PӃׯTxְCvk˔9 Z׆5k}̲ ଊR󧷕$:V'TQ?HY>#m6%pTCI|?]DwUXf2~{NqӰliX.ٯ?+;i63ޙ#F4_~ذl -[|2e>hz**Q>i&%eK J~*}Œ' Zr裏~׫kпiF6lp֊+4c -ZHK.Վ;~5@:{ݻ;}4p@()78-#äz|HqQnplGsG%}e_<*}|YItR$yҵ\Y-TKWjڼBETZvsqlxN:='UuQy֗H^>Fb0TZח_k}}T۶Uh*|便+;;QM$}z:: 5jNWZUg]&NrS%''jƀ%_ҥtoSJJ^o!\y<wq:tWJ~%'' 2Nkkb[po4 9G1ueҘj\~L4s}.GHշWT JIt&i si;WGP3Tt_+W~N |\7)llHi4iy҉ ]ņ RV5l}I4 IDATP|ժdH 1yIYvdgj[7)+,vbw>_{\HTX\)3gꏷު!nmۤͲ8XUR {,[~V<(ҹ>4cmҵv~2]D6͓ rKa8 6<3̲ ȷk [5pL5i"ulb՗J̲fl$Y/TfT?ι>Wpl޼|UOr# fh)V ' ֠Af%K$.Q?83½2@3NĪ}ۤ|<lE4ny~Ptjs}鶕  ~4dR4v # Ԗp'-Zl(-3ǶoR pa,ۥ]> F U ;8 @hW؜@H̟5k d]r~ Vf 'e.QӭO>ic8 ݻͲ Pndz:Z'bSm۶-d=ut{iƽojŃ(4m٢W~[KW u:Sg.Y eB]'z,Ybk|A_a-e8'lIw3wo(]Ĺ>*1PWp(LOhmn{0pTV_oq@ȭ^}@6WRYh}w//Ѿ}a"%A:Yvſ>a >lٲEs&M@ I̙3գGz,X@FҊ+}ۙg>@qqqԱAqskn|@m.q:}߭]s'm 37ި[]ͭZNgaw(ݚhYo_kVzi#u"?J%=$w?/5lC6{t2DnI}M5ӷGk4 }i.{qp8!QڪI6.l|m99=VYԘ1裛%Iyg]ջwZ!t͖V!t%OJKKտM8`$ڵK]v\?w޿L?X7|+_JN6{@W$餦RzJ lTsk.ɉuk~5&I>Ey Q+3_.گ~`$=o8i@T+* IE^xaTD~^dzԾ}xιC IRYY@"ұR&;% 0j}lz]j8?t˗;%[\Sfδ͍zM5ĉZ}eÆ #SgRaqa7g^ˍȾ}73`8 {5liTn6|"II# W)m},džsUɿ4[5oyˍb!_Ӏt-kuﰏKpo9W~>#\PXXK/=%xYfٕoK=p޽{OZfp@^3<O, GQ0x?;S':ԥVbB'KMV c-h,^|Nr1R0ufR#o[$I֖e֛%: _gQ ht4,z=:.޽Ӕj}ux=҉|,F {1߿2s '3tOnnm&//υ&GJk].h/jgae&9)IO.5 z@02D[̔|Ϻ(J8 Zee8  ^i [Ӵh4 ι>1i4&.4/7~.@ .E]@LϷN;PWϫ,̮](j8比޳  jرC?m=^W:vnݺYf}ifF=붹ۆW]h'$&$葛nM9S΅FQb2%%Iz9X19gi!5LtO Jao[ipc'}6nJW>;c=2Oe3ԑJӀeKET[~^{JuI63:iԢb]j9Xb8 Qց,3=zyR춧>p 1V#<**W]Rв=R'yrtA.5N>YJNv Tlb4iz}4P!^Iwsڣ}[.:R#`hi/oC(2ܜヾlfoDp)裭f,TA iI{9%DN@TڼyƏo;v m~n8M\hpV륒gDZm[nQJ t FUn6E>{ mEf@,YeO>:묳\j_۷I'dlTRÆ@@Z>aM=fӭ B#ᔮ]ugn)U.4`>% P7ifYG>ࡍ6i'>*%-3]NQ. \jT;I^]~neWԫw(l;4\ Թ6prMZONݥ6ֆC&;%DN@Yn&L`{]hsxv8ߥ6:J={eWpKxn^2z;tQx\%$ћnRB6lS]jV**sqq)8X;-`]HbcT&=<] )y"B'lMl3/$ƙKRc@ߜwh|ᴞ=SmII4(2re-nv.vK,Qg̘1eN)tviB#rrr1>Hc܈ScuԱuk]wE_xAJJ\h-2u.59OgL|lzņr_@Ͱ եg,.YzXo@qcf/>Z2JDʠ/+ե6ugfٞj:&p{i*Wo;օ6Gֶm[٪3sLPN3rl6hoѥF#6Iҽ\F **#/R@),!]jn\'#Mf< W>)Ҟ֗垙q.5;FӦ=Q3~i`n[%yi睗tmS @'HGge!LWGU>o +Ү"<ԂP ^pc#qsZRWÆYol.*{U @n߿Csg˗/ԩS-3'[~vf\hsMHڙect{ڍ>@ѣ۴ \R+$aԮE Lϧ>K"wIee9G>DkUͲMKG7vO]Z},nkl嚳z G(KխD!2SQԄwv(esrc{-o&YM&Ԧn 63q"˔ 7I/l1q>ս{wYkӦzlURRR#@1pZŋmsO/J/)1QtmnhɪU.4 $I/i)h}!ZI>?m˛IZ?~kZwffnB0<?^9 {7Dִ왪Ν[ffڣm|.5BTH:.mtD jt뛫^x͵|Si~l0Sqlsg(<4IYgW.`0??K"pZ O46Lf;Z'p@ٲuHDiT3gV|AM2S/ɫKt3jSO>`f>o}H5`s. VjZHN#_AM4T#i w}m梋.RΝ]hc /=|R@2N[JtKy_Ҧ-3 z/qŸ[o|pf|e"/-\he8 F-?Nͭ8纠t ,3geQ&oC 0+ i^x''2j.5H4.}..wA ̙YfYftSZjᅏ>Hq/D]$\eiv=+wԶ˜4zOnr鶹۟zJU~ ܪUҁfYr)ylbԫ}";'ԗ~.XW.]M2,3}^cװ9 zӢ/ r[>U ]jD}~8yQN4|}c@}\p"ִ+BGu m/77qϧӧPgn̲+V8%LS*,3M22t׺汛oVB|efzi4%Kr;JM:h5pfR`K[_p&lA s?l(ח[fXOtmR#g%{tʪ&˥F1$`+ۜv 1a lOC5u6+yvqIi駟ꫯ$&&{uQ]xᅶQ I961[g{9K͏>=t Jkk|n$ݱuk]?/-uQ3ND`PYtO$,e84QQ5g6Ҵ] S\6~s;=  rrR]jsTf#oY2ZJ)fe;%  lMꪫԦML-t)Xff͚{PgLӖ/wG#+?߅Fg^s5lh)سGK”pZ~ ڭ~_ڵ,{\Q>.42׷0>_EPNmeإFh2IgN̬ӬoxN R8 V>*L %$xt%[o9d=m7RZgXϟoINN]wR͵|s Θm*9%^~m-[m;WlF4Ijܨ:jmIeVh~ P;sƙgvGjg}t 4Ew>ۣEև/<3C.5r 8pfL_8 XwT󭷦IR^1&I#F4L݅&*iUzOܵ^͛Шvj{?//ϥ6:ӦԴY`x+-)mV]hHp%s2OwڊJ6:.D-_eۦIpfM1<~4]PkozW$:Fj`ybm+pQ 0J27ff&C} ԵkkӦr!*4'6|`k?}W\k.mݺ55DդIta DzK˖-ԯ__wqKjyӧ̙s'|b5j(',Mn۰A:nfSJrEXޜ&IIz䦛4-s͘.Tݎ=֥fab\߾ ִͭ~lMSE@fIwR55{ƌ:Snևx#U|G#d1Swv9ZD bټg򁨒o<0''.1nqz瞶BTdP _I8! mƍ뮻4uTUTD Pqqqٳ>legg+77W顮> 7QQZUTTw+N0N[..[~ _FT+ s<,=kZGA:n>{e~@4+)Veͬ'>=RQYv0?_lkZ MB!Yzm y5 L՞9 GreeWNlvO8W_ݡnk;l 5[V2xdRDPm?&O5iO\sZnz*Ե)S[ftmԨn :T^S yyy.ԙNr9#n~qUVYNmӼn1¥F#7IV냯rQ((n52@- +Ͳ}[ٯSvw5TT{iI^]~nKBM$էef }8إFQu.^e9-"E[~Is{23t9֗hS^t,N*˜ \otJJJ4f̘EUUƌc["n#rӦM~"1ǘls_~ε=?^R uO;6w۸q[3&RSOt *7O7sO$(K ͲgfHq w>+.-xCHWzjKBg^ZB4B~>=gφ.q׈&LBDnMr}h IDAT9A Ru W۷O_~ek'Nڵk-377Rkxeey洂gR@E@3舙zbtEPZ{,43])7IqWϞZ?skݺ2o_fhRis%7;ߩ1ݻ|_M:*Mս-:췳9 /oe˖=zKs믗?b&//4$19mwi{:es`ϊQ` ^%}㥗7mێ)t>b3-^l`@ZY6=Y:ϓ%ISv:֓Kݻ`OefHc/̲N NoJEm16؜| Թs}ڄΈM-ӂAizv.BT>}[ wC,8تWe'zjԨQ_%%%zGmsw}nVVh={v/PNfN Glm^6̥V&Izms͘eW(.5l3MfٞOXǂ->iNYiao6qI9O#inm>E rY R;ppZ  Vrͫ`-fŸ-h'0UahMOObkNW^yKkxUU~mjtsڦMR lL9S/=(>.΅FXܜ&I>[=?2tOE>rY4jff8ԣ]"ſwJOM3~g#.YOjRJW ɲKorQ)H \p$-7]@ j볿99i. F?dZf6m*[o~QpiʲRl:pVʷ_^o|_<?,['?nuXO.B#w 2D񖙼<jM)foOڶ>uLy)ܹAB#D+ǣ'o69g \rTYiz%~P}{7ܜfLa}"Ŕf $S*0*FRͦWޥ*?2H:`xlgÖ//Qyyz&Fd„.4AT,gvc֧[8 쨾`0~ @lY2IߜVq޸qT\l}kjNt饗:S'1_h׮]ʲ^cGG6H-[:^.=ZP`ILH8"#I:wӻnm8٧Qr_f.]ظ:%)h8]"%fYʪ&2ZovͲώ!cJM]!,,4˵IZdpgE$m;bwi߾*2C7Q4xa$Aw#QI IG_Gn *Iʧ@Uo {n=쳶1c(..΅F͵|뭷r ֎9,n=[5Wms7_v:nB#ĂnE6'.5rҥf~@4 TI^1f֓3t"|kZG:EYhwj܈Co{q-U\𒤋H6-›eL.pX|Md5m;^.zUYY@os?TCRt߽#~|%\10Ҹqn1+/PtZ0ppZePW =cڿͶ.]qE!C(>G^^Km֩Ynzg{Ա[|R#*IM33uW(:9m>Jpi2,iTߪҾmfٓ37 rRgVOe}PV,8wڷL|2om~dTelgzi?>z8M&LBD$IsY1i#"+ iM  \ZQ~lNBkǎzmsR@J gЋyyvˌ3j:vsWIKn:P^{w9t8iT—tjlHJoh$ՏsjԄwwYf*Nرc,3}YgRIOOga3gv66{֯wK-ٻW~ĉ\&jnxjiBgFj2+ [~9|a3lNBc̘1̜vi8p;%&&Zf\jNra5jAYfΝm۶PcQ0vӣXfڷl[.ܥF&i=zSO>n߰xYo_g{mv(mDJwOhdzr0%ƣ.`kRbk풯opRD؜[$<+/,beRtKJgi#5N+/5|#%_S0?oeb4}w6'"~+''ǥF#77`0iӦPcc۴5msOzl~u[nQ|\efڵoԨ,]jD/g{pG1=l Կޱ蕘N ΰ>[Jo}n;؊i,{q]ff&Cz. _W\vvAftv%FjE,6g:- *iٜT(ܴ|r*ǣcǺ({JJJPcGm+ $WQ??m^ti(9:m6tϫ̅FuPڸ,p~iff V|v,I*K3 _՘ia/}矖t6}a̋y.4 [y Ӏo9W/^IRv0efѢZԥF 3$KY2.[#5ڜVFh/6UfsZzzu{=d=duͥF%--Mgyeoі-[\jfr{J6DԤIZgL׫gåFowJm2cn=Ԩ/6˥J'lɚYf^͝I-$ip/{Oz=vɖ.5 sedJ )-S1_#F3aP q2~_*+rO-00RpڜtZ0/*&_/[`f̘az3fKS&`0h}bI63;ж=KkG߱ j&+=]w^umɉ ,Yb;G[jxxbԵ]"ɔf6۸ͧk:Y!dS&|/ Ƅ}R־C.v oN.mo{/'?,5h`=<;UUşg;To:ۥ`$--Mqqջ46BPAU77Msmfذaܹ m׹瞫$L^^Km5mxjO\t3ϨLFZO.5K/Uk-usϹԨLsѤ|ݻf.M$H:8L25|^y{Qd࿮CbN[ڐ3 5biH'si!)7b 4}HRt)!K_sK-00x^&6rqR? {#̙3GfͲsQjذ>lL~~6oyxOP=V*ĺUƯVQъV==VKժuEEP%,a ddf~ !Hx{&gz]1f/fޟ]%IceҥK/tQr0&GڅN0R$kyam'\:婋3;9 LNkj#5M"9 p]weC=ԅnb_ii׃***\HN GAʐ(M_x?Hǚ@ _?Kab#-$)+K:@X4Ӯk.%<Ѯ2 &M?XBGaXȮw-_Hk|R,J ՒӦdb {;|eXuduKŠ-7)p0 i$.[Σj ;M|39= j.u#Wڿ]Y1SBNN-onɳwؤ]{۷ ć|]p5UUUZvKBr(?lstvq 953u~Alf;viDq Bٵi@ltNKGM|9,']k,׹ R٢% :mܦ`0Fnr Z'1DC0wmԧO:/C QNrv8m`uzxyABG@xt5X7T}c YZ\zu'GW kjIMۭr<* 'X1ûQb)M~=jCs:E gYV"nOA 4ioVQA~*--5TTT d=-ZEi8>! IDATЋoe[gHbAnt5>]՛bdevu>H͞_(2#Ij HnqdX6x(vtxcM:-YRG1-KN Kq~.)aS,'٩Ϝ76ټ٧co5bXB)cMx\pZ8[ݻZ HsWq'7K[[ ^o64?KÌmr3l Na@|3r4_fRVX3}z a?ع`8 `-4o [}N#9 pSss&Ol7nvBG+77WC q5kָZafcO?_|XGø%9YY7xy}z 9زE[@X]]nt8.?M:Kd{A&ΫY$.:*/yi76N,H^Fjnj{.޽3]&~wG8׿_RGH%OG0N>Fh [>Ը vFF:uVg@R1cqXP/](k***\Ȯ[?X7fppQ.tHNxW 8±&hԩ.uKJ^HuU)1#igbmnRbQ s8IzZ.Fi56z1&lH?LLSYl/NX ghS]P%TՃ&ދmN"cڴiZncM^tM7QСCk>c^ڥbl8mʕܹƺcǪWwR欓NҐ30X.hKՕDŢ'rYb>Yql4qNMG 'tXOhyKEZ]e ɀ4 6TU9I wLw>…wűkc$=4=ކHNk%Ӛ>+,,t% )466w;Pnn %Rc il6-g9o_rDoUiΏI~fKHZrJrfJՖߡ,JtJ˅C %BsDǷ;k/KMz'pzmjZf4> hJù~FFc6LNzBP#V[<[ FiVNk׮!{c 9m-jINk5 i@{T]]Xshر.u8~ӟ*??߱4A=-ww!9G~i}ovQuG5x,w(_|!ՙRRN|?;S$KjOY!,Jrz ׎,2 |^Mk;ĥW, Mf99]Li+;1P Vnݜ<y< B9rJkތl/I[XHNs:-}r447u]rĒaÆ9,YDK&Ex8կ|Xw)hgDMoQk۸QSgp]._%8%;ċ[ ̗sRTvQK%4]~f?wZiϮ2pK0(-\H^vXYF lLFjZZWX3.]D:vmy0..Ow D\jHb+V4ٱK$2u.t.\|~d{p $񙜶[ {W&gG5 g۶mz"eҤI`!D?|u4110o3teA[JtͶAw?KIZĮ8}45j:}:umXӿ]~.u4l0ǚe˖iŊ.u02 Ĵk=<ΉE]j/~o$E^NV2=st7 YcWKrΚg/]Jl?*6՞E*Hl?w;d'KO.p#$KO{e ^jy@B0"9 pAUUfw),P~9.uxƌq$ i! IJ=7D7P4@HBjlW q.Zimdt=#ƺɓ'+-@UZZj!= bmrV˝C`"=+ƺ{oY]:uHpp;:Tqc?Я-ҀOμ<#W$'kZúGkw^vdRY9K/TA=g^٬:KEkW)S:kd{',w.% ,RTvÅnNՕC<}~KEКFiY]핽v.OߧKJNk ]xU`AK!1X3l~A BiA4_#i@TWW3s=Tk>S}g.upnW]ӟxy-ZX+4]\ O?X7~T"pv8$r=| vGR7%I^|~7ɐxZ}&'C>=2]-5'?^a80`W;r}I4wG;7$쒜~cƘqHOCHՑ#ᤊy6GP~mh4~ߩѱO4S!4 FئupZm]|Qc]ycު40撕+5sBp0@Xd3<,4 5[.d3$@!zh~4?pt|LcͲU `qKE?(d9VYo*:DeeہH'pZ{]xaz6(/ߨߜrs i8iVO{ć3uGX+1ZgWKjDTUpKgا]qEOǚ;3'Rs(2I8[ކZ h5kc8-33Syyynu$)SɱSOyRG#33S]tc'`0O=eu`o!LF:ƍz';ඩi( Q4I+aW;DAZRboRy1#{*=vvP-ں٥: sR*+k^RBjZG3e Bpw $4OnxPxM{Ϛ5k4c cݽfTii4QN^4k:Qc;\BMjcO< )g-#5 g(j265-EPǢ`P\]=hJUÝ |ƥ:fɐ"0 1s usvXpۭY uvW^uPAw{%KNgfK I ٖiwp^wXs]_L:¾\?XxeY,yFjQFZ;n{sԑ <4cFK YGwʛ4@H:wt]穏brZ+AhٱW i@/_zX7eI^1bcʕ+tR:}@v:57XwqoױqR [Iٺ曍u{9}fM瓖/e8 }JiWKjnoovjj1ԭX5myf( w9:ݐFh5-1me[ll$.Ӏ詬tNKӀy.u.u|r=`r:4@ 5],i{ JNknj#Em} 2D%,Rc il &Ξ׮uIMM#ޮCk~s+&s]FtsLg/H͖CV{Fx225-+UXaO/ձ^]\&cKotAZ`W{U@G1 pB'&ݑuаa||Go|.X?Gyґ4@BE39m0iMON³xb͝;ױ&%%EsK%'dh յ#9m-Xw#8x$EOjj7Xӿ@Dc<gH}#KhKs͉[v&[^U|}p"R0fzi#|#INB7qDcͥ^#<҅nYgݝwe8 v8-T[Ϙte@;_?]sƺu^O[J_mWKrYyRϼ/7^X(ڮl@f,:o:yk=zkaۛ/njynҤ$ UUeLBƌm1څNLZBr ބ:)n Z ['5tw}WcMzz~߸Zbb5khѢE.uKZ]]iOz㏍u6eU$ņ7ި\ǚo6lCO>-]:@$yӮԴݞ -):oظŧyo:3S?=ͿM>i9EtI)>eZ[. 2*+JJ8_sUR)Zѥw!9 g4D3=m@`"mu]ƚJsPii4"ۡ0?mx4ᡇu~?Ԑ^H6wmW_m'qK.#5 }deHI` Z*+k5%%i y[Dyʛ;uI;6!iHN p`6{l-_ܱ@Ǐw#8ԫ Xmiᴭ۷kh|.@'s1u$ŦSوƺ;}T^ Upm[<ۮ +ċ˴ Ģ?ܮUk=5Et#tFɴ_lR aRup57mUs XUUݺeCs\t!9:.5 ngջm(6ix 7 i{D ~mP7BSss&Ol7nvBGQF9֬[NUUU.uai{LmsǍ )7ݤE__?̜O?ժ|騣B$"iJAeLΑa6ǜXuhRQ>Y:d䒯 (bd̜ "y8sh(8G14bp :橏Fr`0iMmI$&ӧOך5kk _ҥRcMyy Z>Lr`lg/q5h,ʍ'$Ů^ݻ붫6i}Sx $-SRS=#K+弩E!*A5[k/Ԑ3ƖB'^0HJKul/99]@GOӷJJNѣ{(//ͱf֬Y'Eg2w?9mS$ɷ4}z2en„ ԉαSNQ>}k***xnn>5~ԧƏBcLƏ>͟WZ&I >cW{h7)/#ċ}ul/˓/֨ox͈JK]X3.گGcKlw\c砫{G裏vN4v~}^}5 Ii$P|$Z>njf iM6M_cM^t7BQF9|7RGmL-zKoTV} 3crK}mٚrMƺ̙_~lӎ?>H_-mήvi-f;2O:8' ,㜘*]s"WĶ];߰ߞqx25-#E߿fNNs TVKJtI EKYyj:A\0 9 BֹM6ߝm4m~go:$ JKK5.td?F*IxgtF{n0fp <0ǚf_=>,}A ;2ҤH$}]Z`W;蝏vhWǚN.P>lLYdL܍ ^jW;ԝJu~xtIwwMA߬͛}5H$Y\;F}7J;\@` Cr?Zyg8@_K!\'|ǚ ٚ"/jKڹb~^ìYZcH5MKM&!̘uԥqu~[oOYs@$/O:vt@jJ̵=PLl?bbѴ 4I~t:A3S^XI,𦣼u瀚 $|˥F$PU圜V\LjZ4HW]˱)CNc8 G#=m`ƶONc7M8QYYRRR4j(ǚNK@۱CVۜI{>Ω޴I6׏mLu>O>Yrnԩ ? عJr ~I\xrTHQټYsXӻ(CCRGWnXPo)i:kJJNe;}z; Y'Ewʛ4@X;,@`ppZip@2xGTSSXs̥^ƚr:$rAmN{}V]CuYSn)#LINTǮ3~r H@͐e8BfiϮvxO7HNm6=ƺI&)==݅JJJt8̙3G4@NZT7~oTav͏g): Ŏ.2x .viKvzH 4s]]~t&/sRSkG ܰ9=s-ګbd{ Z;o4wy8-77MGR7p2fi k:$.h q(Ӛk۬!9 ԩSͰp+\!%%EGvւ \Ti`PqQ\ҞhM7)/'DZf]uN \<'5{jqH[|;lMb)Yy?;Ks\r#ݍix{n/@mJ,;KGF`dJN;NJ'4&YΝ7={|>wiDpZiA \eeqeӦMzGu'OVj*Mii܅N uUt[0AiivArZ|]Tcg4mں))Ҁo Dx]]l/ `g6JMӆvl/˴ 4I;aDԧG ؤޱ7J[uii$2/E^#Zqqgǯ=99M|z:BlN)+a 'eSg#[q*9ͳ{UWWX3h cbSqqX3gY Ziu ᇍ:K甔3auYz9lohd;=Hz;K_eW;nz85[j5 zu00!~ >^n`li9 ږ,X2fL/c͌vHLS_$QXi%1 *GriyDU]]?Oƺ{ǘe,ܰa}]$poMS&aYzbi'/'GxqI+MEuDK$H::gd{+Դ4y΋ Q4&'kAJv{H@0r UZw3[N>@Xlֆ M.ucp +333Ի[PA-Ypo[566:֜tI6lK!JKK5.tI*5_?̚e,ՕWogc#4Ck%M0iiSvu=^%^ئI0bѴ9H׎tNE|KIoHM7 5DJ *?K}8}eeiA\1v?(Oy3[]n&*9ksGBbۺuX7eA$x:蠃kΝ+rWr@hBN69ٻHw^}4XaIڥ>T=0nI8 Q-jVI|hWKjNAI $X`-yslNBzD2ҝ&5;*1 yˡsS :vW ʐLjZl^J3$"ϘQR797]HNīPG 69-2[i %|i{Wiz'u{ ƚr:$d1,闯j+8PWM!5J:=0nRS~$ͥ:I[b .ّ%^\oW*FffU漾gaxclic)gZ^Fti옓Ӹ…ΩiT\LrZ*+3SMNzZRN#9 Br79m@c>HNɓ'ٱs駟RG?^|cs='RGD,,΋JSRR+Ŵv nS^&Wk,HܮԴE$.R~ZdAf,yQaEݕ9c8wopOz.]ûKEs\.i):N.uP ]];<y HD~ Ui%N NNkl;Җ4$>LO=eޅzʔ).t7jjjo $pfI,^ʡCuҀbicߔnRnҵ' TKğ%i)sPRxFhhXL))uyO&))kAiZώj$5}:99͝>DeN80_99ĪT]z+[6j\1:9-ov-ԁ.Wu_LNm<8 L4I.JC QqqK-4I*//wH2ᴉ^"?7WէG3X?XN0p`: Ғgj&#I霾ܢ0C: d,ӧk))gbŘ*u5:`As}sx݂AiBᴒ.up6L^B')]HNīp<Nkŋ5w\ǚRԱ~9i|e{Jiۯ-4ɜT5]㼶ei;`_>!^{gr%X2r XѠZ žN褣sy;K!&XrS ty7Gg{&3iHf'N4.n9r9:Li7oֿ/$ J-8+жtsƺ$JD-dz2Ӥ# Rr1PbͶ~on~6IUjh`GGs-?5EH3]N4?H iq*ӂ-AZ'5=FrUUU^z%ǚTM<ٱ4&I.tIbǎ}> #mx׿VVffGvCrZuto͒&H!Hy;ҧ}$Js# B6k~΋ .ꮌtՙ'v?vykvM>kw 8vi@;TU6vn2ta\ă+t9 %h q*^ӂL<;ڬa8 ꮻ2\ve:#]2h va5͓s#Hp۶i_[|?uV rRSN0͓^>ox%c_tL/CޮvX"2gSRFpĪvY2 H4bIepigLl+W6fFM#9 r%9-`0`ur'cdi$wyGocMzz&MRG&Szږ-[?/KN{@WoMOK&ttGA$%z:עn Iːe#RIi)҅# B%uZ:Uû+;y/u\=3:srDp'U0_zkO'=-i&1Wuc8mXҠ4R&Nh3fB76p$ $[7_\r<o @\d~8gq#bVZ];/(=ǝMHސЭ ]XSYsq}'JczI V|Ý%% œ!C{wgݨ#!Dyț4@JNN[<{G3dĻ^{McMffÀԿǚy$oSI2-+Eo1"-!\Qk tEw!]̕- ^ūMlvkj* QQt]t@XA{MMk=sr;}ruH'pZڅNu$Xc^xo >9[`۫d/?tѝwRG4I*//wHpnGTGAGmDZ$ڼمĚoi{x",dy0rX3msAJt"AIO3lxuT﷫4K1,v*䴒6Gck|s֭4@25uSFr`0oMmArI0ĉu7pz6ąrQG#tyRGZ Moyەc .N$4|Kmm瞈@ZbcLI lXU-k:RGWð/ca[tAIbʺuUW;?-.f8-^ӂAI$m/4,.o}wzmJkf=iH&ZtcM~~nv:B1K@ZV][ +߯I&nu.4&I.t *lNGFCyvV7.m`PjcIGi„ i TMHMeUZڡfp:yyߢ.ux3}TXXK/T ,P бSѡv><;:zo }-75}H&ktA#иqohZt~__~ӧuE$fMBi=uǵF#r>tXĸqXHE^AP-K};5昲F iYVi[s_kIM |A-ZK R?zt3gHX$RQQ͛7[l3a-^(m^mW{L/&}屫NjZz-^f;0 apŐB8_U̚_vJ\$)/M߽ϜN@XNCoq1i.==EW\˱ί9s6\gpzxo.SNtG;@8]-a+Aᴦ}$79^/hz^uyސ_?WZZZzZz{u&LPN\kϟ/roʢB]v@ӎtqao n,LMKK$%Ii?#E:c7lE٤:: ׽?H[T :XSYv`Ck$%24I>څNi7i|>֬Y@@~[￿n-[5NE69-T ;iZ8 ::ͤK.JM-wxTe{&Lzz$*XPz AA+Xuam*bMDz =df?1sޙ9rkK29Y3DDDd?K,ŋ%k"""0vX:"g oVn\nʍovF;"d`.//ɚ?SSDDDd 6|=ՂGO֯' 7&Ct I5ekKƺ4##o]*5B"գNJݐZC҃3gTT%:4""dB>}дiSƢL /_6m 00öm$mKzifMw%>VXX-[`رhܸqѤ|DDDD 1o<ٺ)SGY @BB ׯcx];""eWȖ0nٺs"3SDDD*:#'ֶ8i@QX>)k I/7z/͵oZI9CI7`2Nx8ȤFE15͕ .f6˗ [7)o99sSyyyشiz___DGGcɒ%)lSrZifl*V:֭BCCѧO|G8w0m4ODDDD?DjzaԨQ*uD΢Yfh'3eq5""QSNE&SHJdwVSBB9sPbl̞=9C_yy8hBpal/d%kS^O"5o%.\/;1SӈȎdwUz"n@rNs%?_ ?˗dS4""R҅ T`={0zhYf:u*RRRlNS69\Mr*Taa!nz;Exױ}vUfĈ_*"""""חM>z^ȥoQ""~|uungfq|im" f͒-裏\ƈHY1ptXmpC~R`GXm0@-GX(y}iRG* C[nM;^ N "pwKMjuxpɚ *uD{iDDd:u|l ~lFrr2͛FJ{[5eʥ[th~bu^)SDDDD##C:I&S#r6riB'DD/>>&?P9ڶ9Z,5 4iRU;#""r'%*8 H^HIM5d=_ ).W`W__~\?CwDDDdori:s8 .jlU:!U194l0xxض+\q~:nlC~Y@vX-w%&J *;vLt4/"vD.Ԩ𥸧d\:h~ԨQN͘1:N5i:v;""*O>9M>\AbpZ6ᨈ%uJ[2:"Up8Ըqc <ئcyyVѦ8t|fQCݖdrZ贻n)MzǾnc7}PMoxxU8NcɊGDDDDcɑiٲ% RG uV!"r>?![@[hDɖH-))bus{Ν;2~x"""r @7bmjZX.tW0iio2_hyo8^^'۴kIyЙCDDDlDE15itxQ V'63G)o9)S@G{VkG=_>G_@-2J&yc5M0ߜ1f:/_D4t'mhhջm SY)ZW^Ad|.4,ZHn֬Y6S;N:!"rNŋ%k<F`r3˗j5+ OLL`DDbӀT:-aB[F5*T5-9шH""AϻTjɝ*}{w U'3}ӈ*M&%f {,xuZ<{MGW6 :f,|m{lr& ENG:OJ>֮B:-ZzPGh0HW_}5kr3""""+W?{뭷l $VXX-[ 0a$kjx_piNy-~yܹ<ܹs팈1g~mWK$WPj@_o-^#3 䊡a@m="" $&J ѡY3_'tU:!4""}'GIaUh:7x;/^ -}3[gkn tz;FxG! >0"yW}}}êވ̛7Ov|ЧO:"W0p@ٚ:!"r V^-[7@-H5kfu믿.{9siEcDDD7Q0i7!BV/4o7 aXږ[K2K9={Ӣ9քtZw F#~]prӈMfq&c.5?5i 8c<=z5tyUzLlW]G=R^}+[.m;huJDHD D4y5>ps8ws#<}?~l[oB7J6l:H|W#""f2/[w?bF2KcrbrL4 5kJgdee{""rN׋E5ۈ%Ӟ)V}d͋}R>kkiDD ON."9F=Z YR7T]<0`W`vnl?6Qۣ{6sJT1j6|E!FS}Cm? Fo+7/5MţmS2nF}OX1txf:ڦ;hyoE6}[7(7kyҢl%J+Ǐ[""""Cg;Nw ]vU#r%a*uCDؖ,YC-j4Q%"RhrpZ@@f͚%{>gΜlIߊնOq 34Xmonh.[3*d_cb7*IOxp"""Go_lVttJPuY4UNHqWsr(""r:ӧOǶm۬>XVCGb4|Z!ua2P\ dd*?Mވh :Tfmuk:VӎN=Z _~w*b8Դ7xO%''cusQrEĄ $wLHH?bWDD';;ӧO{@GK/4nlI&H49Y3ɗGEĉ֔bҤIXz5U3?%yb9hX!:r!#9x$[t| -B ʭ|*SC`Dے}^Ds%_h.x" z#%ҚMґU 95[iDD.GO>iӱ'~]ԥ:OBhVh궁_p0h4`e%$`ڍtw?">ڭ˧U|R+x׭?DDDDD$a̙(+PK.xT\M%Ym6 ..#"rQ3gDzzd/w*{iDί&\"9 t:yҟYw&Qu;^.Wgo6Jc-1ȑ05^J_pPȡ%&Hެ/܄F &VRb—_ >&WiN#"r!";2[RVZ߿'YjZPQj4hF /TiSoֺ{wOo*Z|k =hj6l ݭn& Rr'&L@pp𹉈?~_}lSӨbcc%_/..ƦMTꆈ8q~lD*{V-{DNLɤT)U v(YO6ݻtƍ鎈zM gXj{̑rkdMH녈~."y{H'EG35͝ )uٲ4C^4""'|}C*uDjx$$XޤkIL*(.gKDTRR5> d۽{7V^-|N""jqw _>I КiR4HV}|[%|}[<p%""rd.RFA:"EϦ19H,=⻔8G~Xh5S:M;"} 7:/_m jABF/t>Z w[VSY?s4iyĉ""""`ݺu5gV#reu?,Yw!7Wz>""Wi&l߾]_| 99LNs`ϋ5o.|x/lݤIPZ*@Z[/V4gV &`Td 8I :]ɋ`hAI"Rӈ&1Q3ب(GHٚKDe6V&\LѾ}{*wqG~\- hCp=!- 9mk7\j5Bh ޾2&&Ӎz%Y8?V\HW_}վ'""""6}tم111h׮J|7nT""PZZq=@(^IDӬNsGrgϞŇ~hyTu|X]0`u(JWU%bGGҿU4 +DDDn>>Zi4ew3`@ HXq eevZG1ӈ\дi*Qf؏ؿ!w#5Nf\;s,'WL<埈#11[nj5kJ;MrIHHP""#99YF`ɘF719͉&5kfiի^{MnΜ9LX!""RSa XmkIVյsɑB'JL  )-<{""']DR|0:7p7~~8PR|MJ Ϧ19o߾d,>J;0|jZA&N%~a15-<<cǎUC""""raӧO2dZlB7.ԩG}TfQ#"ꕚsֽܪڊiDOt844ijԐ^9sX}n"""LM{MB$Ez35͑r RG~r/l?DDDT%KDG 9HٚK )B499FD4Xz7trM&\jZvIXovʔ)U9""""ri v!Y0c :"w+zii)6l\pFD&M<1sgr8nbrDR4iSO3g}8{V0H-,Z=aKX@9e '(Y3O8|>'M<)S~D!~-[іw:t(ҩHс*uCc 4m*uk *uDՂiDDhBC-P@W.4Kw5k7;$""""&!ѤI!wVbBBJU{/9s&k;)ӈhjZݺbʌ5Jii)&Ol\=(V*\VpZ@UN5PK?r}ia|rW<9 IDATE~sdk8ΆNO3X2UnȮ8FDDj2e|H sSqbʵTHMznX;m4x{{;wUMdd$:v(Y#K$5If˦WlcǎN4Iv'i +5on%t:ϟ/[DCDDdW=@ awpH7ɑӅHLӈt:L֭ۍ? #˓Q.ɕ+buMTR ,M߱v*_J Ҁj[Pg`0 j<퇄bKšx {J[5H~6 KـiDIO7 9Yh![tU:!MN7RaÆ^zEOLe tf3LƲ;i8rIsuň#n\lƛo)[7vXDFFZjRGDD7o xyy޻T3"vivH9~1dٺ'`8H'67?[*\vwTЛ_$a[&_=쑂V2:U~ʲkKDDDT5rik (H:/zH!LN#""%yzzbĉs"yϗoə&JnGqo&T쎈\YBB>,Y/x@Tu _^nqCgxдi;_MN+,3r5rXLNsPbu rs΅d͙3gzDDD69&l^VR`1H^*K'ލt@RRɒYiD 4>"Dn8M? sՔ0` :" Np)nĈj|cwy0W#10`6;&cń  ..NEF̜9S^Cx8wL&u 05 *uCDqơD&""ӦM+E49/zUi7NSl[ol\*@|<#1][(]FJ;\KD}|Ut$= ӑe"""G(=v}~̖G\\lҥϱ1hyțiDDn&L敝Vۿ)'e6`42.…Z9s&t:x\"""""Q+Wɓ'%k1n8:"j֬\DFw^#"?`Æ uo64ӜPFPZ*_j'OF 3gݮIDD$6X> (<ɧLMf.MLP,&.X&"E?R"'b6{JEG3Q D˖қblۖTg\FDDj=z@z廻nJMOGlָqc :TE ̚5KnV#;bcc%_/++úuTꆈH9Fu= V+WdcY#003f̐p9]Hȱbupe{q+әIqE%&oLA=L-%Woy:ʤ/,YrKɓɩFnQQ*uCbp4ь/Unʄ?O`ro@e7Grhr2w&_)YO?!-MpqJ%2d~a/Ze Y&կoKt:̟?_믿ƞ={~}"""/l/`5(x/Ћ \dɚ5/8)(L|O8P Gז-]X+zH͛ 9H/t.سGpӈH%I GSN|j;,Ξ=ZFDDDDD/^K.IDFFbر*uDTQllF֭S""{7)[`F_l\ӜpiЯ_?]&[4R!'Z#GFJn4g];7Pj$VonȽկ_CؔÛq鰝;r7RJosمkg~X4""""󟲋۵k`tUfΝ*,BDT ~w\Rn 2At(9KNN֭Z {U#""r 9Kj[q +뀎ABDFvd͐^a={lQu|d2a͚5*uCDdٌxٺ{cƌieҋ=09ɜ;'Vbrp#ٶSN5fǏ222PXXhs:ANZyTnY:}nLg5bj EG _ٲѣ,^|YDDON.[N{S[z C͚ҟQ^}F:"brUM瞳s";;~S cv#"""1w\KDEEO>*uDd~Kf"!!AntR'pB6|NcrsvUy8 }]a~ ׯf̘']s""1F`Z jދ)s")˓i"Y@k u %k6/]lf0QQ*uC@jIf鼤4&Q5:u*Zf3WlNjZau$֩S']4r 送bߏZFIfWĮMDd.!rp桸Xz$::HnUE,]zUNjN#""gѲeK<6+䦝sGl2dd4jHK[6=͞=2 Z{1tEl+lƚ5kTꆈH̜9sp5^w}p85&խ 4ol/x%kN:O>k0HJJDDNnLebj(ۋ.&URjM5~xgJ[R\ _" OO8}vF#Gg{=Z l;WRG$LfjpMfqf}Ti;^CSӞ|I5ȼtb-=͘1Cvf߾}ѡC:">}[&!!AnՔbǎU>`믿.[!CTzN#r1?dgվ3f 5k&YS\\,9iҤ ?ۈȅ V t iApuwSRϧLM#GAe%4kK֘Lf|)ӈ 09&.N~KSU脄 19iƲ_vFfһRR+MMѣl\؁n:FٳgG5{ Tꈈ\ɓ'1~*'G}8ydFEWX]YYկENO{i`z &F^yzzbu_}'Yj*,XkIII6GDDNT,VS`pI#o@&ʭ=Vz&$킩`͗?ݗ!;@܉|rw yxhо}Jݐ+5k%k֮MC^QHgLN#""GұcG<6{(|@n؍TVKǶ#1\LDDDD4}tYbbbжm[:"Xf3V^]k$''U:9#GСC/m>GZZf͚%[7l0}о}{5#""""{nlݺUF -'rT{ 6?++ zBQQ "tiPZj"S"''G& oM緈id&9/l/6xwe~Wlܸ׷n݊.]c9FDfzmH u U7Qb0cÏYXXF~Q樘6Hz͢.!Uudk/b?2V]@KDޥR#DlNRreÇoftU:!!iF4""੧?lӱ~%YvH; IDAT9wJpȾzBFajٕȦC A˖-UHٳd޽{b bbbp)x{{!9[i)))裏>dM_) Z:tAd&N;)!_|y8FDf /!۷yTzSRzce»OPd[%<#m>D0G:zǎL9ca7%%N#""BH'FEd-Z":Z{i׮>MbӈșM6M⻝8G~XhԴT$UֿkNȅ$kt:f̘RGDʉYz;v,~G^x"rnӀA$xԴiSk6W)2=09́\ V뀩i۲G%%%a `դ#""7qO Xmpe{qt^25^JJo\j0cd:ӵi351 nMvF- |uiyyF4""]sdkDɧ-[44""r@={< SeM`(ܱLw-ȹvBFYn\fqqqhҤ )W^a7!!sOoiD%''/Y_~{%:L&SiDm:::T^aÆxWef̘`„ Cɑ_LCDD.`jhl/nC:`\(֍ir+9 \,A1IP3lCdCzUG"Ifo{$h.,ÆE@^bti*JKrrЭ!:UxkN#""#1Q:78X'lJ$jР]/Ra2qrMw4""rTfOؿ!;2L0JM˹ߗZ8p ^5#""""d4q*g۶mصkd^ӫ|-"G^zI۷gϞ=`[o:ӈѣG+|رc3fEEE0al]׮]ѧO$:4PTRW:9U:m۷Ԫl?U4uTI/:сXjIDDN6ab*WZ>@Se{q#n}(O<4 ?fz=Q$15 <SCRlL~ v.ӈKNNF۟I'Uye۟9={:tQ!Cҿ]T;I9s83<#0eQVZh5G`2rx3rTh49s#t^y*("2t6j(ԫWJ!r$5W|=;;ݺu+msoD|9b+Vŋ%?~5Kt:ܟ$'m;wVw+cE݊sڶ (*}%e{`6p(.ml/. ivUj8o߱tqiYjK GԴ7"!| ?SGOHfOľ}y^ps1N?hԖn' 8z@&::Pn]E,]zUNHprZq8*h0uT _)K_>SvԴܫ82F˖-U쎈UJJ vލe˖| 6`߾}5>>>2e rD={dMBBB;w<~'39ȽT6ؼy.\wyGcǎUypZZ-n=7Ď+V&&ӆD~0O1{w@X^V-gOe{W^yM6y9FDN/+N #6r8͞J*{?T'N 儕3KLrEx^g^DUoD*BǎAhJ石p ϝ+O_ps ֡^==WO#7׺ic2'Ѽy".;H&r8s`ԯ/ VՊiDD bccѼysJn_MUdX fjK?WN7T=""""rP)))'";;fO|}}ѻwoɚ̙F .D۶mq t:h|INJJJ⫯ڄ P$v3gVyz19e zv|9rY?@1c?>\A5TT}8灃j_|a<==1rH7))$""szX]`C^]FM|>fJJ+_wL8[2*ߵe35-(\`:\>VGז|l,h?^y ӈϰa0Khr/>*oh4cÆtt'S*cA "%]ZZl:p8Kקj5:ar90V+`a[œ83ݝԴ.ȾS/X;tP<""""rϟaڴiV æ]llliCk!//cA#9zeeexdɒ__%o3gcD82Z5i[cNxit!,YflN~>hկ|%|k׆n[xm\r}AYY~7L:?&Mdk09 5s$L`Q77R@I͹F |MHFv`(3ct|Iy{{񑾇\,%7鲲0h1 vB6UDtFnUs+WJ0rIԩ o\i-4kJ1gN 6܍?|~$7\A /7 11Gf|&M\4Uf2`tiDD FIUrgxw`2:bi71 rp=3Wnح4裏fcF#f̘![p[#rx=z􀿿dMBBQQQؿyzZ$"'pede/4L=z4yL&Ӷm[hrNsK+碒,ݰ ‡_}#Od ed`ݎxyLm8{B݋7Jw=0вT2dvv6:͛7?bbb{ A48+%''Lz!*9kǀb>>(ܽ])@*9n_oDg`l1 2uC'Rb/F/XApI]m2Ѧ^$&F29xxh0dHr_*_D{ХA,[ʟUX…b|5<1ԫ;O?KJhS.#U28/m'1Q:9-*i&M|ЩF{Jᒣ4]^NäI0zhBZ~ڽ-:TC1f 6|p4iD=f6/~С+V@RRdMpp0ƍW6郯Қnݺ(-->/ӈ܋\ _M8+WÇek.\|Ncjِ=0_ΟǏ?{UɤOzHIH,CATT@ʊElXPH)PI(! i3.)frΜ9>Ydf9h{hhդ \A>ł={p(#[w={p$F\kޠ'$_F͊={j|P^*`򂯯/|}}jq̙*ʩǏGRR"'""Y+Vg"| 4{ۋ ?w*B#|LpjZиq6?Iic85>UV+a%lp}0ڷ@N?T.*`˖HO/֭W/qTq@!#x/V ?>Jؓ'U6mj"QF-Y0 gjEm?Ve[󕘈=*N>]yQ%'V],3,pJ pb(T^s=ekЯ_?ر111WZZ_~'OTnTDnȐ!UT+0F6  <;wY97%ގ|Ӧ*c֭Xuкee0rdI۷ӊm? P`239MԸʼ# pAp1 d) Q#n@/"'c%6kV&Z Z4$#__z:tVV VX?_{6SiNN#""a=^u?ʦzG@Q m>5j{jڱ߿Gzxx`̙ܙ3gPZZZѭ[7VZ|}Yb29|7n?  ,, AAA B`` QPP,deeܹsٳga6Wtuqr39͖)ST9UR4-t7}:uBɄ˗gvV%NC>lhժoߎ>}\FDDn/@5u0X]PWGjTR*y *}uH1")qV+yGb< 4iHEE=z bϞh}4"5|x8N=2 L?S|)tqߟǝw_V[;y6=.̪fs׾{ eeV g;1"“N#""a^^^dS\"yV+he.̽@=bF-br3Jp5\@9 Qw |m#4l荮] 4n샸8#Z 5Z8|'O:4"We+ozMTȑ1n _&O>KuѹZ{*m_^nש uz7IMEnn)Ycز2&O3Q60ODDT-ǏGu+G>58_¼~3f8?""""r 6kN>:`ʕB̚5q3g΄wE&e˖!$$DVFQA9"IGӦMѵkW8r䈤#:9Ҩ~=/wh.kvk۞@N#d2a͚5=z}0FD䦎KR֥9$ ɥDg/!mO>Z/WUvE|"m] PpAD.lE@ZZeKCcxxhpb)l>§s|r')o0 4"W7jƍ}м9HH{iiWХtk^{|=lӆ4F] | xSx..294""i.#8p"7Op"##|ru];T/:<wn$@r247/V[Nر#oߎ{Guyzfs< M""KPP&L`ױYGp&;i%7qW/fE> """"r]V'OJshll,.DJ1Xt)9?'Caa!?t v~0 `8#s9S~N!N%~vm6oad8ȍUN)6U pjܔV?Έ't.W IDAT'z dh4 p=ξ}&G||t8":9]vg ubЪbE+\fO0pr;'_""3</!OOoc;Fqr>>>EGGᅦp:8p`-[ 22Ҿ78ʭ ~>>;_MصKGy{PHH~' 6!c8ȍ^+V zۋ3[ *cdk?"'L0IU+֬NN#r))9p`(||t;!Q5$$LF->m 3vOny]xUH& #u왃qدwtj|Դ8 kz)s7:""""\FF,矘5kzɄmbڴiX`V\]v!33EEEW"33{ƍ~Y"Wǎ;8F$"۷oh4xW7ؽY;n 5odɩi۷]^$Eaƌ+##Ų::5OM.%zq!bi:0 uj~nrQʀ/ dreS$%9>Xp{1K@/%%a"xi/!9r]m^^KPRbѡn>\hsmNNs_ QL2]czmSJ]ѝPrE<___<@DDDDnEpʰ}v_رcѷo_j ^?bccѼys /z:FJӼxb<5_L4w7_?ၡsmֽhO>^/DٌGʶ>9HI>&VpZN)I0`KOT\Ae&!rar Z⁵kC #mOّBd'qA8R:ִ/&OhFzY&EӦ0kV&fxwj$** cƌSW#;wI)?58M'OF` ?QNl6d2)Q>nac8H EGGc֭Y*'w݅HsoǎF8 Ocǎի 9<(DD 7Ru {KNsʯ_̊G;'ebu ^@޷fM3ȷyN#rGɎ1"Z-R2jgj.%%:]<=-f!6r= Q=v諸[iVb.Դ8+\+WI&Iwn""""r[JpCJJ -[//ew[#',Yp:(Nkݺ5vڅ͛Kv =FɲQRAbݻۋtiiie}ӈbu~8.%yFy{!6 !UӡB噚vM}|yxz:Rӈ܇~GMh#W|r< ZqFT'z~4wpXll,RRR:6xF,V ,>}WaԩSe܇3LNI&>{c"G? F>ErrrpysĈؼyA[po@XP@t$9Zz:pJi@n5q]wIC$_:DsK͗j`rR<8ml-<> թckU͘3֭lI7Z''g[ez4IbaY3WS*+]-Wj2O|DDDDN{1c vwţ>*y899rjF믿ӧsC uw׽{eY=%5uXݝwApyDD~g >˗/l]NN#"rq2bj-̂Qߓ9brڨ!5!7dk.vܵЁC?^f+f+t:^ !rݺӧeYSa@VVb }A|}u='97__j=D~SZaǯĉ"ddXRN#""I$&&bСXhQ=b\N~zvbR++6m>jɓ';Vży+QM7we;iDQ4___|߿|'4ͨe =퉆:vx{{cɒ%xg0gId8ʼnNMuř- V p=q؎< Wph2}4 YY%x YSTd'rZ))5+Su: sJ)x/ӆ C>;Cp5L~ONNs_IDD1c4vlZgj>5r}j[I%ʊ ʕcĉT(**RܞL#7w\kNF>ENAZZ4@'Ɛ^`!h6MJ7hZ;;w.t?$zU={VΈH׊śJf/4G*)ǖ}ۉskeXGT⥗0n\(*DDf={!< DRKHrعv>r=Ӧࡇj) fщrN#""4jأ;!/]Z֛Yq!CӧO]!""""P~~~Xj>EO$%~~Gv*?](oq ѪtSR85\#io&гs|XNsWtLDD9s]Y,eػ?j\zcjZq%J ˕b„ vGDDDDꔙsbӦM֭M$,YD iDj"==]G7",,Ls*xM7j2J}ᴚtD4h o/No߾_jC$ꈈNHp"i/b8ޑK{`ͼz K6/|X2Z -jNLp5Jiz I"{;&֥ c:D,r^z7B&Ǒ\n7o}um!js}jZdC8gI3g΄v """"iu֭[qw:Dкukp{@~~jZ̞= .E99*г];K^-+R8zT.:~'pgԼysرFS&99E^+V^l^Z'٩^MKݕGU&n4IQ  3wۋQ˛@3iDgذ0 =>hP(8\#&Z̟__cժfT '/R{9}jZE̥EJ#""0n8"""""rd8aÆ_s9رc%hm4rq"wo>1e׶I4i0O4 ;&Vר}8hlݺzxӈ\ԑubuj :"4K$E +0? NcI5k!F  +fL))Nb#[BxX$&t[t'Vh __b=prM""\֭ѣGk'(-75_cY?<X$"""jsT8M6_)iΜ9رdqr{:m۶o߾+*$'Ψ%Yv&ᴆ aŊvmp * )VTŤ$tm%MNhE6ꘜK2x  d&o/鉵k!$C599=%MN#:w6Imƍ}0uj'ּ/t m`6srb8d!6=siX`jڡ\Z\422=@?DDDDD̔z† ]I<<Sn #6@^P7Vh i&n0FzFhh̓<˥䚞j ߽cDEivCN#""Yt :uQU -P_r喩iΦ#+}a0lBDDDDTaÆ!55>>>لcɒ%Ll"&䴇z6l@HH$M􎛖ohT~5^#%9YNG)vuSLw}///zՊÇIZQNm{S*Vۅ?Dg$BKe<Bu֭xq#5o4"xkJHfV]&D ƏɎgprM""ʊ w*=jb.15(?-)ڵkc̘1=υ dW_}HZlyxNN#r_%%%8tPtxw;kn X]ÂЫ}{ ;RKGD 7"44T2wDDD:N.1H>Y0ЀT9ZIiz`l0 ۼ?L^jw`,XPƗ1N#r_jn3}Hn҇Ӣ=u%_kxwص]prb8dӣGnFU7q4?WTXK/9ǃGDDDDr䜚K/ރSQHF'xFk0F<2+0i$ !NN*(\VpױcbuOM6رcׯo4""},x}LN&~j:9XZ]BZ%jm4 D_ -Ш#k>yH)rLN$0IGCqrM"" aw}jRVԴ\u!,r9rd&""""!Z>^|E&rUotb }}lBBmۆ{GŽ$ N㓏5MIN 5l(o.&..u ѩi^z O^Չ" @3CJ( ߉0xI(sUv2Lm 3{yxh0l.$AB"Dŏ?6Eݺ*+b"L.4""U߾}qwT]dƾ sPZtE9i}*<_^vDDDDD4,Z&Lt]"WwߡN:vo4%:k׮رc4h qG:X9 V.]`ݍͻ"pZFv*7c8ȅY+VhUUtjh]o%=UԃRV,ڊ0b}!NN#ro#FC޽!CGD/]dc$#]hVj H4""ӊ /!}7lZa1w&E98맰Z#F44XClM"wK˫7i89}39Gźu$CG40Ԏ#95M:V#}^z aXEDDs p|XmvbumKcHk?> OrX-{? 펠~={Vu4"}\J}׈ED>mv0{v<"" ETztic ~~Xx}]nhhF}%7XJ 65-^J_Sq t:bFDDDD̔d`Oٳ$-Z?1:XuizsŇ~^/cW5$N4UտZ-F+S7*tXiUz_`k׮ɓ uEDD2ӀDӮ/v6 U|X~=$eTDWXp&)C~hDpӈߨQ Lz$' H1=S'z(RnlɄ zNNs_ 4 fΜ)RXKEWזULM3;^z6lX&""""ubrZtt4lق֭[׼!"1b~iz } x(|~rԴlL5j׸_""""R\}]w݅[N:vE:~-bccm2FgGرcБDD'1z)l允tyybu՘lG7ؾ};ڶm 4""8W6Q4U|)DprOFct!U;Pd3bfBN#RQguz}{=Nk> H9}TݻJ.'/$""IIIpa%G`1j5#'7=SuCDDDDDUȰػ6mBHf Wpp0-[owd8} M8WbzHb"#ѥeKuwWvߥ\"V,on(447nq!!"]/6WqlrJjILNx8?nHV Z Z`d!g׬//oOϪS-."5um_JIq.`4? F=H9|}c4j#麼UFg]XA,)[]"͚5?)Z%""""76h Z 6DB͚5*kF!"G,1o<̝;:]I@!_0_?IjDiA*SCF~-t+DDT#|O.TIPڇzK~qP {,\'=i̕tb—_6V[FӜFX,%El+76~|ڵAR^@V5EDA599}&9Ԙ1cUuQ%6߆s7W}NM#""""dffVGy~- .ݰa0eʔJiDkuaܸq t$ N#zr*ZTXjTe:hO*sF IDATUE4&UwYE}P"V;= s+>iD1rdvo_EG{ڜ z(O̚UWN#Vh i64ŏDDPU>XxC!9'㇩NMkѢq`""""Hu'͜9}|HrfBϞ=+iDٳx4h;wk׮ u%;n܀z{c`~D߾|)5NN#""%VE@zXm'Mťksig/rU}t@r(h„(<|lc8H=BǎG^#r5Zqqj{IwI_]4\Qf3'+ޱ"""{GVuQ%ӊ+ޅ^i[DDDDD74 ̙D2toPn!"װo߾[ܻwol߾ u$Jyp*6NTB4ʉNM"Y T: X=/ OT2+:xW^G:iDRYS!Cl)CD*e˖疯sr{9SO!55 v$!iEխU+Dz (76FDDjwxX]B R' |_[?f^=DT+2??LB>tp  / F` E 1wnGcҤ5ZCV^!""EL0An $)DDDD$*˗pPGDԤI,\1FO>o6t:75\'iZT0!N#""5'&tU3e&y{!!7ON"$4+>V ZV|&qrD*p)&MŴ]vѣ4Mt*dq:+"Aa7p{† 0vX[fL Fw˟:P7n4""R{@y{qV.% ӜBI`:߼vw*J$h xZѨEjjS4nӈh[h^n@n/!fM7.՜VW_5D˖vi4""R?UU?éiDDDD$JWV-lٲ۷w\CDt^{ {F!"9,\;vT yq ~\Z5irϽڷGQ`8,sX]bRsxy{!! ~| uUi2Ph.o/Ndc͚fSp Z~4lXT<]Ao̘Ӡ7,i A<[e4""RTpp0Ə_ƍ;Ν;cW!"PÆ +QXw4MP@vzɡf2gO XZi+' xۜ׶-Z ^{qZ 5n-©i}Mϯ_P35\O&O !*2tP|2vDDDDDjvs8Xf [3FDbJJzy 4_?$'+݆ 2x&n%5G P$"UD/N?y{!"rbGG%P>6n\:tPvFK/ rrb8~X6>>_~%4^'""""ydff|AHJbu}Kө:hte:ӈe:5-_ V$o/DrJ]yb3ADQXr"#=U"yb,F= l4wp9S /=w&""""edd`ԩNt;DDDNN u:n%'iADDpZJWS<@[okri yxZC|ᴹs`|I}tXe '/ӈ)DGG/g5Lj#M^^f̘_JBDDDӈϧ}9Z XZ'k(ꭾ(V-o/DDDn6`@ U"xxhdIc4hP\#DD4 hl޼AAAXjRSS\rrSLQ """rW 9/f}9Z4gt l,VYoJ?j95H5F{IJC$+IUMq|Isrb8JvЮ];q㐖ɄqGZ""""""""reEEbu 9^iY}9Z`8?ۋ3Zw(|r4"tIN`iDDDjEd'f̈AT.6ֈ&e/ ogh4С7n`>i9Mp:XiDDnDi* ^"ꉆ:ODD&#GF`( r-W ޺эpb8DiAD剆f5DDD8G66@^ 4"j~*V;4L^j*Q ފkzN#"""""""""""r":'.9'X^t _~͎< Ts2P&8_L#u46<Ɵ-NNsW 94OOy NBdlcBBBp"99z]wl߾~0rR"lݺCzz:Ξ=|d2`iѢ:vАHAAҐSNɁz길8Eʐ˗cʕ{(I&HNNF߾}ѺukYB幹شi~'8|0f|}}ѭ[7$''#%%JL'+*S:DRvĒ <{VH$w.]`peg~{6 tPG.@trwc8rHuV999}~z|F=pc5~m8y$zY5D}h۶CEDp3prXml8"`Xmg`ke?]Š-qBqI1F$w1!K 7^,Ăfu./UaV zaP e?+wpZY+WbXܮqc`if??tIr;|< HM޽By{^sqd;իsx?"€^p+HH>'O.sW`6Nqx{УG A߾>VV 2 }6>sԫQ"0~|L&Νylc,:&"€>}=-x[BEص+K=8, ,8?>ݻVYۡCy$#Fh֪_v׬Y'N}VG}ƟcbbazDDDDDDt,ཱུJw!Y^5k`شiS?3g"""BʻvlقkbݺuHOO>77AAqA`Xl2|X~= {}ŀ+y/wa׮]JoפI 4O="!X"%?Ŀo:uFkթS?^^uh/KVYhw-; 7ٳ'ɚ7ƫؽƱcǐ I?_ݻwwȹ{w>qüwxP  NSTRjdϳpj!4pŘ),^w5ǓDގ&Ȱ 93o.خ1k|ߧq?k{bsޡn NШb'҆ 0{I_ok!xxh0vl${.QQ\)odwObݺ7Ѣ ?ǿu_@Y4`PJ7w )̘q73c֒/r>˖`ضfeux(/_+7xGFÝ\=233%]711/2j״iӦ᭷BYY;;ď?H׮Pt4p pQ1嗱$]7,(3~O&B|(3pm 2v];v`̘1K⟏iٲ%}lٲ2FD$+&ٮ n-?ΦHCa=qa[~G__ǯInt/<1mڢUr ;Hd$k8/phDqիscqDBiK/ajO`8Μ)O%ْk4j1aBMAh4a}DVGx.M3իfã6߻d{# B/%D@z.J"EQ?RmJ$! ҉-Ih!{Ov/=d.<$;;>ju0}z!V++9li'+!B!BD믿yӀ;\5‰'GVVvލc5kj1nB0~xAi>c4h6lJ݊h߾=w{LObر֭`BG0a'x0 0bHM届ܽ{W`DDD]vy(/h8@,۴ mG<bZ'  vQ4͛ѩS'тipt?h RGau}Tf۶0 ]#x0 SJ1qC26yת9%`6.L"25&MA>7E5*;_$pBt4kvY` h"yǎe:V= x"ºv-fM'J;RЬ%CKN.Av: 8 zn_~y-/ Lr,ZM\… څʰ}{ Fyn}sY Ք5k1eJNT:6vl(F!B!Q}HNN:BΝ;+… ?)) &MB~s7n}ݻ7.^(x]OF׮]&Դ4t6mXpяU С! b0fƧWk+#1N8KĦ_xFo8Bh7n!>JJJ0~xL<eeeBğ緝u9b *wj Vn~f)Daqp }1Vn~";sd<exkذATΝA6WqI\lJ%M%kx \q.ǀQGZ/=PP 5?>'~MkE >^EfԘ6-cA1S޽ocj=x0' ?RT*5>N3<)E.װ~}R/bԨhlߞTZ40sf R޻0sL B!B!D4s?Ş ̙3CEQb>39Rs ĭ[xmiQBiϋD%juԔ,8$11;vĖ-[$;&yyy#G?|u&=:o;; 'ן9u 5͈CXdǮPp];<ԥ\F$<*n-)*ROvMU))QaGzt8r|].|X#>p ռ:5w7*{{ZA׮Nh^^Sѣ #:c2;P\ҫ3k&~!vsڣ];{xx[cʔj~t矟#qߏAffկᖖrءeK[XXT]Ř=%B!B!tq̟?nnn9r$x{{CV#''<@dd$N:Pd~-g]\\kkkɓ'*^\jBԩ,̚9F@`?%- ɐSSpx])Į]]d2`pL`fg11س'|\=))$|-=xP#ywz6[oK?>>J(r#)y8w.e TGffYaOR 0y5ya"lؐu뒐`^^_~i*XD:v૯޾V- Vl}>{y)QQFNN9+BddNɓY(-7#'Ndȑ mڷww5@6v^'8w.3f!*O0c7ancj(F!B!A%$$`ԨQP)УG߿?QV-k6muVXR}裏xoc"((>>>M||<Ο?_ǏGQQ%3988߿?4h:uHKK_;wU~M:}=N ƍm۶*رc1n8jJ6ppp@zп,Z÷~͛7o 4h7xoڷoyݎF 8tϟ[nqnrJ^j**lD>[rc B6m4n,agck9c"%#?څwDv^L+(*+=nPޱhedR <Ue2f͚O?mkk!C`Ȑ!ŢEsN!fJLLV6B1Ym5ߨtb0vc@7[V tAnhV6f5CZX#@ 0$rnz[{] =_s+Rjf~ޟ {L-i quq >58 TƎck5 88(k~\pa=>JkŠ1zt- ͝ZmKׁ?c ILL!&N0 {;W >ccc//%:ur̙SMo8OC|~X{;ssvuBdd >!/j̟ु2w<B!B+'NDFC0f̘jSL b…tRkҤ z쉞={b޽Ԥ, :eeU8f|ᇼ: խ[uň#=D߽{w̘1ݻw<==1tP :aaa0abb_dee!883ffBc|Un4iҤhР֮]yaԩ8|6C |mj{0avnXd ԩmQ8F`e;v,_.> xh[ CBx- !!~q~L}bbv-4.+V@tt46nnnغu+zq5j;v`̘18q">^9B Ҁ |M3IAMN~&3Ğ+W'ckᣕ^ yMedIbwgܝ1o/0M@[\@Wqk@RR f̈rmҼR>0o^]|A,HצT@.0C}3;bbϞTUD7_~k׸d'_AyB7tÛo!$$ӧUyNpy'1ejh =J_?Ɩ-=Pw:MMn>\Szcx,_ϻ.mг ztƺuIؿ?Mcg_gn|qm|Qm}(2,[懄bء[Cw XTOI!B!"rnӢE \zU`1gDEEᥗ^i_8::⭷O?xܹsVB޽aeE,7g$$$pn{bͼi%Kݻx7-U;⯿}j ~… x9"@>6mZq&NK.iL{^ڵq!ر66گ#!!۶m:oߎA1)//ѣGu:F|-N֭$DաۧU0yXg8ZnX[%f˖JחTщ{aҥۼK~N!22}dB,̀Zko=sK@Gg*<7'-kAuIdv_QɰۆX80yr90vbκJKXPsM/GGG>|ӧOtBxNcuip⋁{Emjcsv~bEw"0dҧ#itdyz>B0NƛA_1C{ƄFbNt}tDf/fάMZ|Pw\w(i.VBnJKU>#.\@˖]ճDX=Z.dv9Czy)hQ=K3}zJKaD;;3=={ vrR`߾XO oa }AZ[=>@weejl>&;>6v8{eԫg)qmpr`B!CX._@jkص9F獵1v+`f䶴cbaI: B!B!D7nDrr2s7uAy1c -- /^ĢEꫯLV$D*3g΄\4zjcر :2l0DEEo߾:쌍7rn|Bh" ,_\chaaahذ(.̞=9^4ssmn܈rvFubUMpr&U+7ox֭݅ IO[bccgB@HHh ur^3geB8 m?yMl̀u2&[:la(Ǯę&СKӚ{.7^ؠ>1ej<ϐ@p#]WtnaaѤ>^a޽]M}&B!B݋$ b7n  z2 ׯG=D?!jŜ6l@@@54o'M{0|pQjc׮]C1߰ !5lժU7o$兣Gѱ0X[[W^a {@ӔJak~ع쉂f@S ֖8f }JVc٦Mo}:vx&daq___̘1Cz6l؀N:Iv)+S#7WlӦ̮gѶ-^/=L~bb q8;˶X$L9s}k \wܤ϶ly,?.]Lmۚ}{\Tg5/f] %c.iB!B/S(?d!$$ޒcq \p9ޥKҢE @؂'eee"B/99ssΒtyI&زedҶm[Өs+*渷;ƿd8;8ڵ raɓA'qLtNۻw/ ~)ͥiaa Jvg7QQ]L[5@8J/"{C߾. ΜBÆߓ}.6 BwBBBp!\|ju{{{C>}{ff&\d2lll WWW2ɩo{^DD4B!d߾}P4OBP(O%rر9ީS'I;+=AӶm2iYY !!!!̱;k׮V_+ <рo4%MdU3Ǽ1A®ib =wwTz|PX!@L;&̞=#cmK43!!YaHO'\ʎoq5N+(@tt!k.M4㺧kZm 1nWhd@5z5&|F4B1"/^Ċ+p!`\ݻ{\.ñpB4h@z}Oj!:wg HX~D4$ư C4;z ˜|SibXkTp8رcnr"B!yz(m] _~7 E*<]iu1izu3pE@3yJI)ŕ+%F?P8#GnݜUYu|"?d` IDATÇQoo% r Nkڔ< ÇәcmPW%ggL׷99""1M @! 7*8P`T*رM6ĉ(Pw} 4h;vB!';;sKX !D\Aڵk[nV_'h9377G^$Fdg' N+.淝jR0Ng#"pNCtȑ tN;vsm۶pwwB!z 5{ҋ@v%sAI)][Z eLlV =M %qs =uh3Pл~wGZNN9s4ښ(J CAAƎK 綽s,;rP@p4lP؃Q4Iys6q,5 S j pZJu(<<9f|bzA]$Ϯ VZ7^d/Ӳ5z 7V a{I…# 9`?d_;tpiu,d_wM%_CeiiPYug%1[rr e9H u+$#5+Tѣu_ _pZi \H1!7[nH"5 3gdN<)W^EΝ9P'B!r s,00VCVJJ  ٙ0 Zm6ixyyM؃Q8MWnb2}]Vo+=>q`8st$<9;r_EE#t}K!B194u.ڢK.:+99`dddÇqS˗/kk׮,Z"trmX#ifPw߭j\ q+{>///4o\j!H*>ejrc ڙvgո͢ӮeRG’Sp^0а[w=wi#449֫aw#Ua<_zIs@,6ٺ/qoo%<<[q(wwF+cBll/H6aMZ v}z8ʹz+Bعs'LR#>s O :FRPP!C ::ZU !BSdd$s,00PJ!z*s,((JRjObb"sՕ4 : ___C(f(sRS,/}8u#G Td\],S>,جY3 +JٳgѣG033.]q5]*!Ho4sU [l\]v Nz @"3&*dhL )dhȣcLrj)% =p4YـDkfWhBHHNÆVWO;M WWW^1 ~.X.j5{w &Mz))O͟ QDb ڗJFBB]UE? E"kn׾?]p!7n`ܸqP>;;; 6 *|||P(DDDٳ8rrsy 1m4޽K!B1 HKKcD?B יcZmff-ZH\ !ݽ{9֤I +?o4)JOR8Mt1f.mTsaLOƎ Wv))"X昱9s&ߏ'OT?~m&&aW;3F%-mk @#z>J+dVIJXWBƎ#i_*n+Bvދ]1lЦ Fwݝa͂ | {w763LuM#( V~}2&N\O?%LA(&gjzj+/{_NsvkԈNBJM-c v}=}J4B!"77o6sEm'OamhӦ }`߾}Xt).cƌĉ9Crr2nݺ޽{UwϞ=xwę@D!B^5 uԑB.Laqmc"Bs[3Z1c >>^ ,y|i:eMO$&bױcХ @EF̅¤+t뻘昱\~GFEE!** ˖- 0{l(9!H n,ϰI;ZXpZc_TR#iƱ~'/~L݇w^AN_˹`I<&3}` V7aj<0@/1= p!""fWDB #$aa!^Xq7I1ç)ҥ?_lAA!(-U!>^SϯAϕrs+cUվ5ƲÖjYp>gHi~;8(𽙫9rT*?e4B ܹs h) ,]x4MJ%F[naӦMpq%aoook׎_{᫯B\\=zUﯿZ !BިQ#dB.!2QY[̱KX !G [oamll0w\qηsҰWWI4+? UVW977~q+Ӭ~)pڵ.BW/kbᴂ 2߶N{&GhiI)e(<3ߏe:g0 UX) 8dTb`.<'E8pgAfR.]L(1X\0̚U6NǙ3< GFFb>WnlQOehP\ӄ }pMtЁsgr~N!B3前x?$mԨ-[0:v(a%T_BBsԮEEEXn7o~?oooqN37& S!wNKNM/VzS@ڷjUumwu --9fjMn޼.]ի5] !+</;qk7@9}e~Odj c?wh}86BEi!_KH 1F&5̙lhtkkv}unZH+V8VTB>7uuDEhCk]+/! @ݺJA;ё+]]wp++\cp= udgk~ +c$1e4;Ο?ڵk vLbܹzӍǎCǎq ۨj/:B!ӒB.k}biCRZb.($|8|ݖ!>|qBAiϘ᫯ ͛'agg 6}P51ܷo!BD5VY&Āqb4$+Wd >4LcV`Æ :gڴiXzq9iZ h:=+ ?[upZBPPF{>\++ Lff&aÄ#(v֡xZ'dpoo8&8*pt g *oj,.l/wzgi/`4;;?υ~/E4bh޵ӚCښyy(c῜{cCZpu5Gzz͛(-UBE/j ""s 鏌 P_wU."$J c ,(`71!ŋsܹ5̝;W^m۶Ő!C㱱͕"B!5ݔ;-bh&*2'L a5T_nn._zƍ'ɱ?~/FLL &AŭÈi q=ck.sΝ^j"R3nj Ю];̞=ݻwRddd ::GɓkāDBD T#uL,v%_KeLJRln[k`YwvS$h^8x ZƯ-eڷzgq2-k.ۮ y sOh76BCٟ"a%6䐄۶5ÁhXmm0gN\riْ9p@G}%1< L\,-p!=9pBi Ǻuf믿Zj1#""$B!FBLED~S=9Ç3-Z$a5h:mffKKK ?J2 %|cBB:Xq){XC4xh(kQHiM }ݻp\ @ƍsss8;;iӦӧ֮]$]UڴiU !De>ෝ 40] Zv&P\=:rLZ<o4-j{ZAW\ o*+*Q4?/**n- ;V B m !] Ǝe'Ҡ""`4:}b ;;t`wQݹ3YYNU).VaӦ'UnW^N4}a쯻y/e紪j ߌ]UAEDWOBcEEE aСhРd dGGGKX !B1ɍ ӧOgw}"BC6x|g_>V\ U7T[+1pZaq1ۺnNN5P9q]l2ܿ/@!naɈEΝ9MLLĶmt-BjNn2jۋ[>:˳RkЊ4Y=.6| K%G\nד CWnˀ '$j++9rGi NeiS(d8Ĵp$ERcDc;ܻW+92II.jFe A}]oڵIHNI4a KC!u8MdWT9hwbBCC/$F?jՊ9 B!N#VBM_l2<|9b !D{t槨sAǎ)A4ꜦR9V4pڦ_EZVIpGg٫2'P#ܿsթ =Ze@Rrm`bUj<(RJUAiebHׯĞ#p:=e[ŰYJ8:$J0@堠@bF;A!qE讄#dnw\\^{-3g!1QPYi ;wEض@!fe%ǀnV% .MJ*ҥ񼶥pzݵ6紡 YXy tN3!Ç3ѩS' O\]ܸ}B!Dwff˸&>BK)G]&Ѿ}{ +"D{tpt)))#  <󾴬 _WzSLpZ\ EE(9BV;[[['T >:u079!Ho4 (ryRpT;]|<,sjSfdAf?1y/ ;O0;{wTV5iPwX~~~8WT1|x422xmPFSCko L&m'3OZ!DOj=z9>` _NNN1 B!+/a%]pufl1j(f)R˗K\!3%KJ޽0۷}6mZ徢0tPĚMɷsuҊ fr^E_l=t~N5P U߹XR?_qf0zp1Lv~__^e`bXLFk̟+V<dRKKv:R7oOHMe/ Lnn9zx8]v^w (&$Pfi4}r@inݺTx%Fq$B!pMn,((B._ҹl2\r9d4iDŠ uX\\\[_ƍѡC[osΡC믿tRqN0J8B !sn Tdd2^=~޹#n-oz7x9~I !Q4ӨkZ%ULt-Fbc9ƿ1\41i ]Ӭ̀ĭEB\(-\WII%>݂W}0V`۲-Nx ɯb˖K?L>ŏ?6BDD<|W俌9jn.W_̙ln}?o*ڕ6mJVVrf‚!Ÿ], IDATB;&+>)F!DpM+"a5B!TF 1Eŋ:u‡~(aELZk!,, _5d K$$$_nNӚ5j5,$4W^~Q0ᴻwŭC 6661S&h2m4ݻwMޘbD* mM,v B9y;_Z}çsN; ta?1y<_+:9ZvG\̱=8_ gVVTq=="" ( r9ƍMp˰WhܦB#5'3h:wv&)߅]ĉ1<9z݀8͋͟ ͝JP c \Ꜧ]!pZ~8'И,bvve!BҤ=B wN+..ƨQPVVq[l\NuLZc֭|:Α@C蜦VtJd23*2q~~3p s4k׮hԨ1Jk׮I\! P;I <(-RKmKJd2=:Y[_hfZnǒSK$5o8;;h!T h#4NՋފ}Ⱦ3Çذ!xΜ:X~]8ر|_uxv%&Z33W72 99,lܘHO%wW1c罱29!HuM1{{{ +!B1=\M!BpvfO0sO>Att4s|ժU Bbaa[[[)\yL>94aJ4ѹp-`9[Uz|`׮h7(EcdӸ޿@nnD/L2#""$Bo;Sv \́:M BZsOˁ>@&qfG1i4Y8L TGE'Ojd@&Esh0K(/_w 9ϸ_VRTacnCF4BSw9nܸ3Ǩs!B&7HX !D\粱wV:v{0vX +"DXtrJԯyAYY<(T*ѡ2`psqp`fdHXv4uM9I\ sڝ;!sss㣪>f2B!@ DdD頂>ARtA.E54! %BM:)Ӟ;7{o/s 3s˜qofH''!bHwiBۗq6K"--m۶"ΆB!0Uha#!lM0EEE_5kSFg&LGGGm q QմFifOoOUӚ5ϭojsIhh(ciѢd2݋8V!cQɵr>UNc*5s3Fę-\!͙e ՚XUOx+ϼI)kՊk iM,G+*Cz2J*ReWlъ =2JSoK:"ր [</ ٪nw=K'$?wM SP!1Us(F!Fmgz_Oƃ_tAB!Xq6Chr09XVZ2O8/3"DB;wVQQ_Z `rZiтi7rsw۴=`6w[sg"4_:iõr.q 8Zaڴ`Y`i :;ӽ*(f+<aP@s^H2t,Wh(9DZX\d[[K1`?r뮔X]Fv6}.k8; w@JJDOd~~6Pܺ}Z+E  k۷kPؕOLV]Fu9BH)))al q&mƍ111"̈́B!2C&ARlu]b̸ ͛yVٳ[n/R" qӵkW?~\g[ff&YqqƵr- @hPc[nA* Q۰m\j5>'B"ѽb֧O6 'N&\x@4n_wb///oBH#P崿J/ڑfʲVӼ$Pt/u߉%V?+"_\|j#GJ++!.0vvR޺@׮."ϊ;B۷uWit\{cGg|#mWV];G"jBySxݛ \#1r78s u(,Av GǎN}m|,͛[=c޽: )V_ӧ*S4i !Xp3زe c{Vw7%B!dhӦ tgggS8ѦMT9cƌA-C#\-[A"HƶwBTVNm>g4(mZ *,Zoժ ffFu}`ǯW2j(QQ]ՀU6@vv6 J BLit ƱrZ0q!,UWT(*Ux5%K{u_k;wMhoȺP4#vϙg4b~>[: ll/śT6p*Jp` dgw8M*C:t`~|C 25jNxE/~_pmd#ήp5c-*-16OB1Bl4Z ( L:uE!B劋cl;z3!4F222D̙k׮1/^sS;;݋q)gd<==x 6 "BBۏ?/lYNU(LHսء~UQR!&*F \X *aßaGz8ʵT iμTͰZ`dUb&\PX=u5U*-DgӘ `aDbY:vd=` >RMfci8ѣBF>C16qq}*L_HkUc>Vf.ibvߴpZMM qE~'NiFB!-qh4gC1{رcjX:/=>>sqFO&V1ݷo1^ӸVNcI&B̭̃ojsL&Ctt4c;)+ӽUBH#'I8Y5hPJ ²".w/ bULRsv0R%%`-zT*A,K}8cLLzz%cX=DRR)JB|b:O5891+\Suʪ5`b237@͕ [R,PpvP!i\78is9!ղl'N ::'N`׵kWmVYB!X.]0#%%EB ձcGƶ TTT &0a͐rBa;W߿_ę&++ r Yï%R K(q6,kǭ_ZI߾}8- h..."φB!~րToJXͫ-P-WΖC2k,*@YCU,waYNХJn9f0҅y!淐:)T5vy Ooejз34UD8;Ύ: W9d {n "Ƽ2WN+ֺ5, #Nst4?ƕ+WX9s;vᅬHn{Yf@!B &&>!Zbl7˄ c#00P"-//׮]q6ƩݝG,i6GH$X+U*$=+ɋ*!C?~Tʍ7+S4BI(ق?S9ڙBR1J\>M$eG*))c>UN3R9v ;#0xc[AAkpS-C>u`9w QRIޞB/y֘_b9tH9K&[7\Z xL>>6)p!!pyİիFK"++q1rHgO!B~#H0x`]v1.#ʦN[j<>~x$fW^^v)lSi)#Q4zyCx8cÇE 1 \+]T"lԩۿ+gc|.\[`BP[?gaaLVNp!vh0mnr\5mT9)=]\h"g# Ο]A ={Bb^=v.H+0& QVwuJ+ex^R%R6щiR<sEsjjRDE׻~ZNogЁ*BLR{#eN\\\vڦ!B6llsv؁ѣG<+BHC%&&bʕ:p)Nj<ٳCBBj*gDr9 ۷l_z5ݭ[x "P8MTCEͮ8w"bhSؽ;Jx?uζpgx,NH`g0/,h4ŋ@>@R#F`|m6|hݺ33lq *B1Z9Bi5]1ȟ /Ti_1>xy*C ?==} ͰhB֐&r PsXj-:; ?&fe%5k 1{vs8v)cOWP b^ĪU:߯͏0aȳҏ-V\uR5l 2R_?J->" fJf^׏6V2VÆyb׮bm/W")ҿbcpIG%*5._lt̙2 (F!F֖!du5E͉56o  !B0`P\aڒ%K/C*;V D"ꘄ^z%%%:?C?~\YGfN.ɰe8:Ҏ꫌ᴲ2|xE1Ϟ=ɼCk0|x1v,+hulhdzl3Q5oSgۧf[t x<'-i0apZҥKi&QtuT=Rݻ;v(l!rڟ]TL6ԓ1VWŧ~U:'z-Qg8aOCM*\aZ3:: ҫ0**T׿c▢Ω!sX=Bqb\oBj}Q>^}2o*]V\ә T9Mx~~6 G|^io7BP33! 1|nZ}&04ߺvu7~:TفR<r7e%Ba UU{#eo>$&&6T!B,\.رc۳?8#`ݸt钨cbr9ƌ~ P ٳg#33}ѢEԩ"Ĥ 0;ݮXGbFT:r9A &g <|99"Xf *Z11c|۲e 0T/wyGݻ^x+MaNB))ǭR9"/6ebͮ"<(qF{+sۯ+sCg_ո^ʼn4ݯ=k᭘w\,: 犕+ PV&'%1/28ЃijR~n 0Ѕr@ Νc>[TիU:lp/=̯QB?~LJjJ>:54ӆ #G2SR*psX^7>ĝ;U6 nm6wN%rP8BUNUV8|0 S!Bh&Mb¥KꫯBш2!d/g":r֭[;x VZޫW/̛71S#ĤXYYa„ O>ҥKE/zpy111i#F$*[? oئV1vXqjl >\hZ DDDwN 1Ts vƂ! Q&`^`YW{_6/<ƚEgSXJ!vSҸ(@0z ;#"J0q"G*|ahY(۶ ~i!,!\BLĉA/Yrq§U=: j5:{{+1TR#% شZajllnʚ5k1dHh &'s o̘f"΄bd?yrP_/'7EKlAA}VǾqCө3 >p!!WWYT9\ٳgSOB!EDD`СnzÕ+W0p@( "ƢsΌǎEKff&A*3EEE!WWWl޼R)=%;uq˗/ݻDzeG >W+V`lڵ+rG4޼

u }}&,X 8D`\+=x1ƍ@TL>]9dffw(.ڵk޿gC!<~­ӸVNrvfhKr1o 3=ŘY~~&5:7?x\|y?TdUuK%@7˪0sfB+c/ŋx?nRsycbZ'7c^{;eeU箢Rwu G<- Geg'o3?U(xk1Tq]A^gt?|+I0e s]Ћe˗+1}-/:4J85uWUX^ P8BIʒ+`ʔ)ؽ{70sL !B Xg]'6~ZZƭ IDAT'O8.!.!s4 ,V\Rq ag~_҅ۄw"L^ E0,ZRb ޯW{|l"˕E9́Ls!o8]n=#ȱiȘ Z@RKhQ`㧧WOt<~90[8mǎ"^7+V0WvU?[o弛Uq Q>xP>}=qQUƨQYPt ??gE -XX>O1p @b _\Yj l?dž _~N#N{M%11~-?#q5>_"B2dbbb .ܹsHHH0ƪUp!q_n1is΅+UUUx555xWps!?asYf1o@ 5qÆ!U+vFWAjFjZ|z5^;*ykQVO;^yhԩ}[oa[V+W()a^KAAxb̹HHH@U=z ,,!D4 ;U\e@ -4Čh|/Ti;yqUj-8?3xA^`xİ.Z{R6MEᴞU|Ψ~V]~絵}VBӧtY[Kѧe{週c}X,YУzd^YQ;nԼ~ d~^6>់ gb̙sk/CTcNjjboqwXeѯsc3smkbW4ݻ06Q[yhҶ4GN##ŜھuKbh߾=Əgܸqx饗лwoDFFY3qS*v3c B!_3cFرc1uT4KRL4 cǎEm- !zX=77?<ݻ6l@>}P]ٳg3cժU!&~)k;w &&;ve̔t[n5=H|WP*->Ǐg퓚hٳݻwCɓ Q[[1c0d2lݺNN !SNEYTTT`ԨQAs*(()Sн{wPK1=+[B4{(d/$ u$/sз4#}1!w1/2?s梞"΃g0c6LN4s[p a`D_dzWV+1aB6>ͭŔ)7е-6DRR)cMcr浊ٴbc/`͚BT&OfMpAZ$$ztE6 :.iZ-GnnPe˖CXX*vgS~!@Baî Jٹ92 *ϕfͬ1jwjN,` $B1=!!!JhzhpDDD4b*e!Bқocǎ \~ݺuCTT&Ncӓtl߾}L>k֬iq<<<8=}A#G/ >}_|&LYŵȯ>|˗cݺux3|dff2'&&'O4x &...E"`۶m[pÆ شi 'bРAς֭[gՍI 0{l,Y=zxDFF2V*++ٳgqlܸzyѲeK^ιr ʹsqUdݹȹs2:t_Ĉaok_ԅ ؞-? U5o\ԥ'OÇ2->,YΝéSXO?ŧ~={bĈhѢTVVHKKݻfk`X`:^^^pwwGUU?~\HIITP"`Æ fBpi\NkGaK\ή==&v-Ş{7X˙+UZK%yu){i݋7j`}ouбw ?0:9VMkeZ2 eK8bb.=0q/L> )[>Ōll@b&NűcOMOE[+8 쟋Uؾ}QUhX̞}55Ojr/BUa,ZC=1dqCT8y V`!9sж-50E5ljOq&g.U"6:tp¤I=\]٫]U`"|#^O1c=Wp!&sgm-E]]Acx{[c4?|eZt| _}ʸ)E56kٳښiblllмysly&x࠻FR4$ƶiB1u~-222p]}]YfFhZ#''/_Fi).~yzzbHHHJboE\\&N~! :ٳرc>lPu%={RM.]\͛7cȐ!:7G#J{b޽pvvӹ Dff&/i}*++k.ڵ `oo///xzz * %%%())AAA? ի4p=-~ t=UzZ.^ĩ1mb!Y34k_//ף 7rsq Tl@Ě-\&Nѽ{w/͙3gpg0__s9W%C *I@..6pB^a.gWcU/h)^H1Eyl3.Wط {''+" 6BӧJdfV#3j*'o뾧ATe6HOč9ׯWsym *JNUiUEZbn};Ч rG`-娨P:?_~z)ѩ3F8Mi޼ K9dt G__}HQ^Baa._DAE gݻ{D55Q]ƽ{u(,Z-f 2T*֭ልZ7.UҥJ5g ;5HODY{` pa |#INN  ѳ+ڵsRAFF5Nx E [L/M!HEFF2Ӳŝc<[Ȑ[4ډB!GA^PXXgT*޽)Ц5kbze˖w… p၀ۣ>ӧ;B1h |ט2e 1PQQk׮ڵk?,,B@~~>uT~wHl-wv իocX7rsq#7 TJgΝӎ~.<'ЫW/ܻwOԱ':>F7'Bs4 LX5-p%T|;`;w⸰Fwkpn7~k\M;K?`Gϗrp2gVN*z#?۳LJ4"/r]~Riqh)5|T)6 VV $`ǎB||0Z96,T8Yv"p҅[lCa" $''#<<\1{K. ߭bbb̺!54 gg>^Hp\&=?,kak>xe &tZէ'~3n֭kToc߾vmN#ys[8@q; -Z`0 mѢj CN΢mǣ",Im[8oamҴ"#pH4Ž?/w?:4Cz~\SG߂Bر#cٳgNY /sy nގ] !b&ڴi .nѣGe>%H7o*PRԩ~ط/ ..¿mlظ1 kքB.o| ^dYYIЯ_Ӈi*vp#use# DD8pr291w.mjL\\d8y2G7eNqRG2 r;wF` 8…tT?:??,""o.X*q2kycP8BTΝ!eIWVСC"!0|8-5Gv8&NP_'±~}X[[ G6_K猟n'JNSDB1R 'lWXXc[NN`2UM-[ 6.!BHSʕ+Ν;v\''',^wԩSy;.!D1c -- ݺum۶Ş={a4=XmXz5A۷gϞEttt~UV(((uۜ3gč7зo_^i'owwسr%x;7Y{b@3'N;;7o… y97DGGСCHNN6(ݶm[⧟~ /[{X|9!!ph#ܫTIPpgcߛ@dp .kK;"#JA~6H= cg#WyFf2=ũ dڴǹsvm(y|mk={p\,by~'%2 HUkk)/ŋѽ;VXy9Vq1aa8pϧin\#Fx豲`?ܾ ڷwĐ!+! Wa:G3GG+l'cо=(dV} ^yE))sFƩS1pu oBXbb"RRRt%%%NN>4mnl۷o3 6.!BHSCJJ N>+V )) \+Att4 ӧ>44+*"H={7n'|w6Xx1Ə++av8$'J1}tL8=֮]W68vvvHHHQ0f̘F0m4L6 )))صkcgOf0|p{VgNF慄$=ǎa֭%=jAǰJ#6/)/;#  cGm.1 pZ^^777,Zs޽{q>|O#JѥK$&&"11L&СC1tPTUU!99qmպD"AN0d$&&}!4{3qh Ў{5y`|iת6r ;:cs'2*9phml?TV =\}ܰdFZS}a=~V4 ?7?ڵHOXlmHHpèQ3`Ԉ] oPOA$TEG;X;W+4DE9`0/Lli'Oa;Wa-Zw xAh'7n(b}c>Ihz%z IDATxM5~-m;ܹSk kW1.". 1zʕ89R+ pS7sW":vt /<{M g\g ?{堐}^Vv F׉HZ[${'ŋٶ`,YDUuu5tデ 2̙3j*m˗/ǻ+ȸB˝GM= }25CPĉ8}4^LO(t)Uȸ@vN-*P ^#8`@w89V $U`8 95PzO"5}1+F r oR:^8,}*%(,áCHKիUuK aww9||k; `oObjk5HN~'ːQkתQ\=8ag'm??k!C<$nhXCJp` nRX'OJwwg~1d""D#Z8vWV!#ԡ7kk)5>>Cx"2R˗+qS\X˗Q=gϝ$#6]`Homkk*5-ř3ȨBff5JKP*]S98X>> EB³]!DK9rrj?Jj]0bx(T]2q6QE6p!Xl@^^EVBLk 4Y2s]vv6x3** :ےѻwo$b~(F̙RR|a;!?Jr ^BTUUAP/Э[7F җRFU j%|HC^[i̜ $'7z4u!BhPϡZ͂WOl/o?!i H`cm +)UZh40y=^xyQSճp!|TZiAP(԰B*uKVkQS5j!_)Z{R RC~j 66Fzj4ڪBi%%%Xz5Ν+|RRRPTTC6"""i'N=V\\,mRG!br9r3^CѣzS!ή^?VF#eF[yyh;mJ 55p\F_Kn' 1:* O< BT܂iR `oXẌV4=x`C4ٙ022$(a$pt4\EvgkKשEAB1r b˗%??Æ CMM(㉥m۶m}m۶piB!B! rG4Ν>lǎ 2V+p 11p'N{`بtDjjjSObipg'Q [ַ Xkn+<!B! P8BM^RR#FC3ݻw (**t/Bٳgqh4Xr%cAxB!B! f 5 )PH#f¯uو5 _1kv>XDÃ[Dž 1 7n7GϞ=_7![?3Ժ7 +O*#k8>'!B!D B ?0?JII3ݻGNN`c4tb/m={ XB!B!HP8"fxy~^v۸Ν;{h ireeemJSL^1P8P{aA1n9n$ӜiB!!D,\AAA}͛aÆ) ̝;DiiigA&1O4 /"=z:!B!B13 R'r3_$lJY۳ssyK_eoYYk8HIv. !?֭i6VG3k8 h8VNkn#<!B! P8BL=֬Y߾}B6xJXlj!59AAA6mcB@||<6mcgggK.HKKc:>!B!B1Quwbi&J=!D'{?wbm?;ZlϼyP'XJ!VWɭhjQ4B,C@F!B!P8BL?YfWPP#$$K,̭["44ƍC^^36- .c{ee%ƏCСCP*Ƃ СC\zܹs`м !B!4LVV-[ƺ!FO5Q4VPw^{Mo;viEocz֏ifoŐJٿz3D!bq<ȄGSz\qH|:i\gʰSp0@ngR3PSt,#xB!B!/fBӲeːs훓>-B`` ~#P^^ҥKx7n^^^3g,Xoؿ?ѫW/x+!B! Bطok׮qF|7z|[[[hѢ1Vvލ˗/[ZZ{6jO?>6U|<~ ,\QXj\\\XhZL:EBWuifZ9M4J"λr%w*IgFz UN#B!.(F!&N*bvq999?> m۶EJJ >3sȑ#qu˼B!B7"̈́bqT*ndf\ 9C"2ɓlF5̕H#Zq΅___,[Lo,N!A+G\+R?{w]U}9$ (C*TjAZЪ:VWkj- jj/NR( 2''9A$drZ]߭o}ٯ<ڬJ$-v}~?sᴤ iЖ@HG/>#{kO߾}uj{--//OMhKiii8q/^_ѣG?uꩧjz'խ[76@W_s=Wz^ $S8BwhlK+ 3rk8wN矿L眳T˗[6l:=:]tѧ:%Z} 2?7k ',?WB 魐  {LXLo^u-ZH/UW٬,>g}ԣG :TGV߾}l:HsiԻwo/=ܣwyGO?-[uioǕ}G{キ4zh=ZmA7^_+Wϟx s9Mp$^ZrmDm[@&cGݩnC}kpcwʕҒ%ɓ'kРAo纺:]r%z7xnhiY!\}p^-2_;x@~g?fx^{ܖQ1Ćvwq"ӂ㏫t T_s'XխfM x_[4lgӦm=4iR]oG?Z v|>t{(>BT$Ҙ15g^ڬ;驧U^wAH?mdddhĈ1bD{*))):tO .@_n|ܸq۷8v &p_M8 @۳mNK i{EDWz_ g8M3F7oZn]3f͝1ÇK$GR^Ta5pZDO_nϹsָqm  )2CzApZvV~ԯmیG;Vy̜SF8-U5JR\$XB睷t`$u+5~|wK XtELƄx`.?[$K/]K0M? kԨv pWL5yZwL '.]d0Mjkk_F@>JOWWsY#4nhUUe .0>e{DVZtvg 7ܠ>8_JׯwPvIfZǥ,yԟpZؘ뗣}X~?n5wK;F5ljHzpa?o ͞I/?m{oUӧ^pTW7V4{Y}G8 osop%--Mݻw!4E1Ӷ- Mkv/^7]"nw-\tP^ ',/Nh4 %x9-i;:cAW|dH),=nRqg}s]v*++l ݣ؆| )[h~2ȟ!r995MiIg-iZnn&s1ر:,-[ג$Oy_:)zjGX,KGVΝuG^kڞm8@3rr쑟:8rMsR,.H*{sVҍ7`#Tg Mli4_N+'S=eNKMI=gWVZ/,{М)BLɋ8. |bgdu[o[oZ=:C裏j̴il@9 H6z:͞=[[nռy4j(]~Z۲m#hҜ&IWw:|}u-m!ᴲ27I3И1cs{>(7mIҜ:yw>x!h͖ᴽ 5@M'x@/n</K;l1cƨBS:)uVUVz_m,X@<#F>mNK iEDTTW{wNֹ.>4ϙD"yaGEH~Rnvӧ ߯\ ƏG[ wڶK=iB o4߫CGѕ}spFP^ۄm8*2δG8iں:64ip3|]k8qqW_\|@7d{̜C8-)ضuIP;ڧTi>+))1L:&zy|Վ647HZINǍ3L6M[-V`gHIEdtMݻqkF؈js{"kTY~U8MFz_F^]h4kS\L@"l|-@ Rʈ#ԭ[7ϙiӦ%@Xi4hsi?LEUʍGK99vӧ Vvv|Aƍu58@ZBviN5-t-4 }̬[lOi{N ೴4q3W;h# !H!=fۜW6AeNN4Iax_xBU55,iPYi6b]pƹ\  6o1cbpZnڷO~NϯsmfنӺeڔ#L3QsAcƮ9'DM']>vXLiiMh+**3Ntp>fxMe p|0"LjkaO@qԞVVV_|6@ddd[n34 xМz$ ]dC9؞ %IW_};0o[m+å1iE,iK}>{$i-6Ai4@} Ae0NJJJ3684m6 #һ Yit1ޞ3?L|F0j;H6 ^ZZL4kBUU.2G[:pZ{;]a+]5*~5d`역e{z6EBٽ  EsZtNƕ~i3?U]]h# zzyp6EsZUrrmb.8w)/#G}K=>Hzv*ٳ5m4r6vH(mcBCHv IҘiKїkmF :J!y M'ccǎ<^UU3g:i@ jȼUq9iUQ}at25IO^3-Ҝ>pQu&o7;} 0nƹ+R[l!/i7v<7 @ۘ;w г}F{$iӜmMFkZИ(9- ! R/j?묳3SNu M8eۜ]ك*hᴜl]m= i7k֭@ر&Md[~&N`#Iβ9-+D眶i;;6x@IT|5pscvs8vh3/ra* S8oU]]mh rȼli6DR2:%Y8M.;Vr/}e<-XF!7j\,&͚.N8Ag}qGܹsl نӲCԜ2v 0)hԧ_x 8̻=mrUTY_DdNw@BY2B(QC8 R/j/))<^WW3f8S8M= @hN 5IRA^~qƹ?}fOww߭Ν;{$ M0+QWWa7sM1i}y.E}_R} lp$.znN9 @2]#>@8 yJMhfԩp2o4yWEud I睧,ϙe+V8(l^|Q2<ݺuӝwiFVmĚӖXuϒrBȃ$iޢJi):8>qhӼz6$m9 YE"Mm t@ EݻwWqq믿oF@tNm8 bs$ݥ.>4ϙD"yaGm8Zz%wA\x:cswq,Y`#Iɶ9-+$眶;;6orrNɏ[ܞ̺lm`m8洠17Jqn JJJ<744詧r YYYڵ 4_A핆x'C^},y99i\tC0fWk:(fOw΃>lϙX, &5:ӚԛpZ${pucǿ` ܡ UX}t7}]M4Fm84ا_֨.206v /4̚[`H$!-ԌiK.:4i?:J]v׿z͘1C>~9 @7%#|%I%uu4Li:ڤey̚[+h ќ8W]P0|A]zFӟuE&Le]Ν;Z<|zgY` f@i6lP,SFFh K܊m8-- GU8-ɛ$?6;SQU鷗\p5.~;Ұa@~ 1B:urvh$&i[XنzӚ&啍Zsf qsӴ3Ns4$s5D%#T9!xE^7ZznFtI^É{O_}U{8䓕9C8 h[4I6  Hܜ/^AA.ݻ;0-Yj@Y~mlLR%pZX$rm:u ku6Zn.p kn `墋.һk=ierw]tim`#lwqܹ6oLiin6[֫W/4mBiTQg; Zs$]?nJ&NGtᩧ*mdeIÆI/d1C 4a=z=~3T߾}m4-lM%es!;&Ic+=asLRA9 vDmQwSe˖(&INA{i7w离m%##C~&O̊+#t^zŋ=zj-B؜p_}Ub*=:n2#Gڅ>D wowBतh4hi„ z77zPq61)i޴Eޭi4J /ԯY9ebN9-73tvCP3:U!4'VQ}3Ҝ֜&I%%%ƙRЫW/4 xliii_1__tq=`6LȰ1]h_o;wyh4@ia2N뜟>f;ڦ> [ey̜Shnێ4H:4!iִזbM웓,T\\ݻ{L6MxF@y'MؾnӜHpZNMy^{9hr~MGPntQvӧ nl8q֯_`#5؆~iݜs04mbpV&1 =yVsMĔ[8pZ \H^vAMN :\hM{X&Xᴦt$063Yvz-GfjN[n, E:,4iT4Iеo}ۄȑvs'}󍿻 2335e-[tW: SQiNnШn'x h=783k.i|oO8 9ϒ*rD FiNKMMW~>l'OFUU<tsZk~*))_ϙR;/S8-o16'洠064&I8CM[4;…`?f!2bt/~f/СC5~xMJq v(v̏)7MAstXcBiaFp yn%t+--MwyoWng\KǷ1Um] j6Os@r k뫯jv駟}ݧt>)&I_54a}ou >{Cs8-';[Wsn_=n2pZku, $}yv i03gC/~ -]T3 N vWq+w>I]3,ZbޢJ㩩ґ,#=E'$_Oٙ9Q9i79 ?KvBrD RRRTXXآMmU.N|Thr47nԫh liGZEuG㊳6^~]kRGȑvssH-vf{qnժUo`#N4ZInMjzf;tP`FxOy厶WVZԪKp.Ѵx$)V4$07i4uTᖛ|ϙիW;0&#n.H*N6GA^.;,z6!5j\,&͚.O?]cƌ1w}Z`:KpԜ6dPηOVT39&Bli4&L3M;kO4U[ɧMFmC8Mf-sITZ9 i*9pJK[bu7Xޥ$;iZ|;sf֬Y w4VEUXi衳O>sQzQ7 M~Θ.TM2EtMM.RG[wQiNAsZX_}3 ~"v*-sӜHW]P0'OWZ"V[iٵ9pi- +=sNomDG^ǷmN#8QjN\|ZiBƶ=m,~^Zuu^y=6FpZ@SL:p9%$s5D%#TM&UWl!8ߥ94$:xpiimp2n:G%F@ 8č~?N5jtc9(dli[Hs 妛nR޽s\s6n`#m:KVu͔2v TB!N383simv~{p?@rt-j9mhq. +>hϙ^zI[lq>p$^&@Du|;pMHS^Yi Ss$]?nqfғO6!ӿԭPփ>h۸qZhspf"s騀7NaYsܷMԦI!T64YE2=v%pJӒ,=H(qgXmJN@2())<38"_`a7GsZTTW{HOWVf.>Ctܐ!3啕TGHJ}{sq ZdĈ s=^}UжcvsDZYg-|@;H::sUUcBi9 wT+ jVmaVJ5ޜ .Ҹrg(-BRGөS'yNGhN *akMƏ7;؆֮{]:wuvjKUScP 96NbbpڐnM1zeVGۄig*EdSZ8NXҥK7Ԝ%i~{uz̙3G֭s>z1 hN@#Ԁ }͉z(}uUIN9XmE4$(]^PP?sf޼yZrp!Wvs49-4I~8Z`yP8s=洚"͝ {ӎUNiii2e<*++ue9 @5؆zW5vspa*?p$ݞj}6yOiUӜxa ќdӱcG3sf…Z|05I_` :"2mN#8ލaoNSsfzm?;hOC+vaꪫsg֓O>`#fݜKlzd83x@ii4֫!v6P}DdIidc؆Bt'@{hi8-v:s7jhN@pgJKKl 4 44gT+/B8-3#Cמq)Sl"QGN~8_Ree\$xn.|1XRќyc/[-c#:)w0j^[6ƥZXkiXC}|ڦ;}Ahr4ƕx:um(,,TNN 4a7G8-p*Etq:X)x\z9Ah:쳍s?|MhFpZ@/ZEsZT^k7x?g8MF{.WmuM6ќIP.Zk[uU}ؤ5v;.5N9 EgJKKl 4 5Z^,AsWk;p2G@.v3f B{1~H$4a9 5pZ@[g|7qqfN>xos,64"G`75pZk[uUќW }khN@9R]v)-- m8)zjGy&ɆQAuJG)Vة~qY9Zh(^ʛѵkWu]ƹ˗ns  1.[NIӡ}:8ƽ:wne\ᴀ#Ԁ jxG֚Vm4}wH|/DsZJJ ]mVE;IOO駟9rJ͟?F@8y9 @ڔ5M:E(&IW{KGȑvsл 4bwܡK:FVӪ$-Eʷl,F2ӎ8R8nǿ6-5М#֚pZ]2xaV64>t@*))1L:&@x֬Yh "2 (N pZ$i҅?L<?hS:`3*;;s^Ǐk5tiDhKR'2s![`pp$͜S`9-L4f>ɇ. l};*.]S[ɇMZ&s:i57&"uQ 6yꩧ[~ӷ~hBsZ(UTWgN_]t 7Z76 ^b9@3zn8p+6Xu5->\V繃?ֿOF;>}"^wDBFFLTK uW8 'pZ,o h<Ƽ:Cuܐ!3啕TGQGhOCo[fnhu8-mu9--4߰`Ƞ$i5uq:p5wHvolXmҜ j\i!C=sS M,^zy'ȽӞ:lyqU:&:t~cYihCcƌ駟n4i͛`#Mjss ;}-S6f-sL5$FRL. *L ڧTb ƹȳ UTHo.ݻ;0-YDl R[^|SKeCzjLw8|z}]kvIlgj L@صODG `>_-ҚVR}'qiFv b iE8-E4OU[78цp$͚[`,${pEZN&- iM_Ksfرkkk:.S8M= h+45=fL<ۿKIIxaWFm{4MDm 7`3gy3.[IRZӂl"p=wMi. m85gHVlbi;&߹9fi c@@Мczohh3emnP^b1wA$ :T&L0Εw+ u&T4I< ᴬ7$s媩;(  -洝$PbfZ@\^{QFyμ[Zv`"qj@)9.9 u)(yև:( z]YwqwnTey `9-/egjat9tum\mn/pZS*E4@)#egs.i )]8!VN-jAsx<דO>h iSiN]s1ўfc8rJx6.Wp6N y i?a2 c rΜSf nNw ;iki]iK%i&iN5X8S93uTGD8 p#rA%iPUSxӚ}ŷG˖9(l^{=i?u)^-XFBgm8-=Ƅ,n206m2tġгNkmsZA 7)H>c-]F@JOO!,l"W]4?5KFtvss)S(##sZ^zBe8@25 o=_Kͷ1ϙH,a9 @Ҳ/Q!hPo;*9 aPRRb:uMIMMU=l2k69Cs@pD8UXX9SZZh xzyp U+VQV@\aGB os^v>O`299sswO#IvOjU];pھG: c|~iiH-5€pͺ^HL]qH(}ƺ- K5$aɮ3eee6-&ў vMsʭ=mΝzW,mC?iNOZM=ќ{niӒ]|=da9lpR`k\fϞ~F@60 N EKiY826vBuqo\i8itO!xH7Iik+i߈vXGs@EIr IRYYM ќ<.Mօ8z孷,mKvr :wWkHREl@YN~Dr]WJklT}3;$dnv@jq67դ `%oNkwV~ 8/Bk N!sB4dg .pmL ȠARsb{:qFM>F@Ș;)wӰ9pZ`?>r>360a\յqnL/'[fS!gRqi`4@R Um`}K4K`<.l }4`9m<@;qW^.$IŮ36)-4_3 Y$~˗[( 09 )Խ{w}ݮs+Wmfa# $L0 N K{uҁ}r,m9> ZIm|ʰpZ۹dDS.҆LX}MJwAsUII _jizh)iHR<.HivtI{=da8@9Ӑb^xN>d׹;CRaXcZӒ1qs8MfMќVN$%jWw@Ǜ5UV>JZ6s=5lSZZji߲ԳgOi@xVT`pe"A{UָNk^prg{u}f|>TG_:444K. 1LNhJH5߫hN MZqfPi8||q&7?li#KqV|u7-\P<JapNeծݎF8-t g*mhi#2-&9-odi-Z8pd\68NI$hޜV[b>D%%%b1=3plN$sP;>F8ͮS(åYTq|,7]]]w :7c _F@;%wQo$'EK̈hđC'kΎ'Ҝi3f̴9+ڧTH$SOYbǿKY@(^wټ=44i度gj'ќz-I?UYYia#)WoxN/ I,V;Ac i_+ziޮoWsoN`- @W]V6iaJ<Sq4N>luq65Ih}Ըa4יJKUQz4! t}έ[N_2+(Y:dR1C i4k P4I']nm4i+((Yg8dZF?NM6mv$-gi2j`}ޑ޹SJ'#ZO:s磏u.Li4M?uWVZea#)aN'?w;2iի[yN9-ITr;\X-8SHZ2mcu[=Y"Bi4O6ɓ'ОtG8 F~ʒz0ݼ]ʚ;Y$=}sx 8q69s,mC4!"|A8K.LӸ-r|<#AK$ޞ?e{ȡpS* gjIK&U_̓MZJ(W{6Fs)z,L8 鮸rK}iD4à=펇Rܭ#Li@?pp ss#f@ .҉ ]™@Ғ tUm`$͕D"xpZcmpZԡC|u43pږ-[`i`ڜF8͗\,m2QM2u)_]$eggk̙qm۶M?-mXİ9-(=&Ir'k괽qfPi-/7F:μN54=6Nxp iɄkl4%u"wkiSZ7N8A}qy-m[8-Hh#4F/-m~0izw8z孷,m3ÆNwqK]JKK/[a84j4¢U3Ӽ1a\1}ueܜY{unk\$k׮mi{$J4oNk%E4չ8~z͟?F$iڵ6%C^1>4;ruΥm{fsswqz:w嗫%< Zҟc p-9ֵ(SϵMzq Iy;,l}&&@JKsBx@L iI5UhN-⻚jky4`JYIILiiM!H6mv[kD8ͦ=W]:urd~|4tD>֩S'{ョsk֬M7da#Ӽ]ԙe1ChMJql ;6)%AG.tÙ~ikzG4=PڊViNӶnݪorKpږ-R<(4U|i}b|"+K4lvBowuq&iڴib|$b؜i4ջU(3v4/Muf4iO3:2H&HRp j@8 _ќ%o uhNn\[8MJKK-lO~9 h>^X(\`/FmޟKpm]:uҥ:wCYg 3#ׯn6׹>@sӂr߃:ӂnRhT5f/]gfni0mNv @hBPn8S/m iN-k}hN!C#t?Yiڝi{69͟dg;1'(W=6МW9ӎ:8O CHJ$"uBeUr{L(TМpD}}~m=zWU__omk똍ix_7V:m9-Hwc=_|Q[lEZ7xĭ=Rs̱ ӀMi6y-*/76>=z&9qÖ6 洊 vDQ͜9SYY-0555,mqsZ@ 9ͷicrC&wK vX&VLonk_0MpB⢋.q;O~:uC=T't}QUV_mmkf}'i[n_}WϞ=5j(]tE4iF /PW^yN $+n4I*++ /nM6) 4 Bs ,mM2EQ=5Kk)YT$x…A?k'|FOD iA˴9p/ZS5vhm)c;)' Y fRt ~0 `&}]™G}T{Zj|MM2E:u_*//o?xI y!}{_iiG[5vXC?hJ+//?OSN}Q7xCш#g̙/r q_6B9pT8>^XErP~:gtcY/7#C9uk֭[-l@ҟ+3mx'_rkMC;Zԝ IDAT:qT̫ w1 Ɋ6]9 ܛoqB`3DB+W-ܢ8@z_{od]l洭[T\pvoY/V-CpB(77W<ܖ-[FD ӂ0IHiK3::fs-m:;>^Y%!qs/*WfSڈ4vcZazN# ٴiy9R:3 /MO2joT]8DmNK$z_rW;w]=-ڢjt{s-++ } B$R 42B8W۠5qxǙ:֭֬vx≚2eܟ'6R̸95ڍۤA|Ҍ[s!V.LW:3kno`ܜ|H{,-ON{}O>TWW_g|1B{mso8{sZ"ƺՖݶmvK#GM7v4'zh>@2uq9μkF?/aΝ4Ms6i27mNW\iyy60u}rh:4sSMMMzuUW[n0`:}駭'לE K^"PysZ]洂eggk֎vǪ{6oKzO~_I=4ydsY~.-Gͭ_/56z U8>NsZ2D'8R|)Kafs0]t~;׹>Lz+Wb.ϳC 6qs{kg!rg&^ ~!u?#Hhպu!k׮^D4Y7aϞܞ5|QRx1/&psԩ3w>\/u>HH/>@;߿nV׹`a# rMsZ`-v JkZ*DY'8xчUںq&LJޜZN>Eҡ^N) >ju?piK5&D"iMUlذA6mj"þU˞հgaU_C떿Ǚy ݸ[lX,m6lh9piAsZpL:D5pٖ̚6Jlfwsx Z#FpU^^na#aUPNN kFǙ1CʄqX<=H Vx_dܵXB8 *;;[ӧOOckvnкo{5ۿթWoV#NQVnZwu^Ӛ HnNklَvlɽ:dԩ!jʪR"Qtgx#EEEǨ-j\&ܚb6l`ip!fs| iD4]xaipǛͽ.2224sLed8ttWX (jNGpZ@>4xskDsZ*zlrf i8-t@s7"6uT+?zǐ<>tyN4(K}vGqZi7ֶo8ͩm_ʭk~[}WUיz&@깅$iڵ6Tќ@ӂ34o_Ǚ_|L(*+ u5׸͞=[<󌅍"$a I>td舃:X{+3,ܩƦ~a^TPT^3cTNB6 N 4f} As}AK_PjC8 .??_\sdo8_~-+'"}K+R3t̤_+^ȳ.WhxM2mvҥ |M-#3GzKkqo:T43WT[I+>N8xm\nGIILYYMskO#=lnjO׀J:ik/u3-lbo6'ҧz SO=UC= X}ޠ+3* N:x_jlr;ž}r4<ǙYsp6GF8 B+TQ]Z?|oz|:>R:P4զ??#3G=>VCNN\?jձۀVg]P~d%]]bf QǪaбJ$b75Hm|6zߋ4駟Fiim"B_3 ќrF< ={:_DoRd9J{{Q׮]g.[ P԰9-( ixi㑈4z07r .ik絖$jBsZgS*=ܾRiꪫ &[~w߲fKfd*9R2:qzÿ1U.|D'M:J - ;}N 1`tM{oFNnGxmnGk֎ae1'kjCCm&|Q'MV ݎ'|>F@;>N8 @ќ4WA^~΅=-+K3lp{]VXnFBלf$ ퟫ.2-m'ǻX=oM,2:2k i?ձcM_~29{wEQwVc(8p Pvn"ƷJ^pu7ooݮNkǷ13fQiwӦuڣsv}鉘ݭi>{[>{_m())q= 9&BChNK iv+̟+WZ(ELo6d /':wwh6<1lN JY96vhgu57d JޜZNܹ⊤>v狴n>DGAcT0wUID<ڬ[22WK]zmxhִN;4;M`O4s'{3O==pچ [8ʹ9pZќl]t9CI!pZ}XK0AӦMKcS=۠J8-P֬׆-3cF4*ys{Vi&Ǚ@1}My`?\t#箅Ei"?Oݞ֪oz 'pZnN23o~rv>kiUà={bv>X7t… XHҟ\ض"/si2qs8-OvZx@^#@t]\rIR~\m6}L4sV<\WVΞ';tSAjH$h"Ѩr _v٬WhFG"Pa]XjvngkYg#G&@+ΔZHp$]&@}kRy0f-1ɛ>=zo~q&7vۼ"r:8:hWչQvpѨѨzP#Hm_jMkM>jpQY4队q:μN5%5mNv ;~ӧLǮ5mYn쑑Nz'led(SOev$$lMبo~wI&裏n%+vE"wy36mқoii#"8N}pi)ڜF8-..ֹ8ب'KaRȻ̙.%FҕW^:s^"܀|:aVߐЇ+o0vyE﬊ia`М̚i7)]`7--ON1c%soOKWD|WkZNW][[Q6К@i\E8-0|\F1Cфq4I5oM,0mN#pI}욥uv(nMݹQ+>췿m 2zF}#Fgy544X-V^^ni > i@sZx >M7qNIKǛ-^,m.%:vZp 6ڑqskixY 47GG 83knXisqO3IqAFfO`7\xp_ û9jm+o֚Xu+^׶ul1D_v@+))q||zW-mGsvgN+/\-*55N Ms_* ޤǥ~ &sqŋ[h'kN3* N-Z|4\uiiDO],m iRtht_>9m߰7 D[N;4?0̬-lⱨ B8 BoT4K}"^u/2{ժW}-"nf8#5x`Ǚ_|Q5."@P֭[G80@ȼ 頃f]On ZӂǤ=W]}mR,6nv^z鷿G};F@;ClpZP>ZAk758ΌB8-pm5wmU;YƌHM[矷ef6K=.eSN ':_RVi\e0"44F3wn]AW4Y#i`N~%1}Mx=[>zY;7l1FiM+syΔY-&Ik׮ l|ߋK@fi)SY]8itԩ3w>b1 XvG>󌷻E"=qӥ^ji+`?716<%!!u 5as8-OvZ#WG=9 7T!֜vQ0mBʜvl'ee:;͚69 i?yR5W~_ִ/iU-222tM7^ mиZ'Ov|^_-mӥKu|rK񽘆֭*+"NHD3.ui8-{]1c:gx }嗖6!?bp9iUԸМ\>em3gZ&LzKڰ]8t饗Εk(= IDATW^4oh7&H˪\ghN ;̚&1}I r;& ,hBP K!@7qƹL}U[ x"xִjcN6M%%%755g `48h?vPY]:SXŷAvgWn3K>X,:tx\z9owR;PEIUm=.bN ʕ^in:Q.Y:_l:ΔZä-&@p2oE4* f6|`UFQ!ߡyO,mdIfx!ַo_~s| rm|:axqgihDg|[WjGe٦m4 GPvsoNE8 ~3Vjv7F>WkZc}ٱhnn7H[\:3g}]}g6gN[e6rNqkNqT296;;>KlO3mN{4HCg} < y{T|1F֚jG?RϞ=-DIILYYM;wﮜi3B@8͗ih\xgyU}[ȒN3s<.=)F5sLe/"bxOP5VcSBK|lTjlvRYSx|K=zIgb~#6gOiPgv  dϫ'|FasZ< hN+kT[\/5fᴠTGtty?hNK =i=\zCwkwփeִZVgjurtA\.裏F84!}8HɥQU@ V6N ]ۨmli#KN;lni*owRg?hmݺF4oh7 )hi i6q\+_1:4= L@F[9_[I+5IXS/yF-/6u]g{CW&O:SZZjab HJf4d{ v!~p=tǙF?YȒSO5<)xun˖-Gԋ6G|kK8eixaK8MfaaviȋJ4q (APvszX-@;5`}|*VwkZwlukU.]-I4ͩz\\VVfi{4GͭX5gi4]xguG.uң4|OpBo2ec=6!jNupo5Mn:|@̬-mNiqo#i,33S3fpl kG=R"5v>_ꪾl1ZPPkdee[g}w}FpiT8>^XP`i4Cu '8T,9L5kyK=zpKUWWga#K]`?l֨/;ΌqqLjMќLiTt#"x7.R\H8 ~ ٞǛ<*≸bݭiUٟ***vvSz6ZaڜseQz֚&IcN ܯ=/@]@~\!i.;;[ӧOwlʭjq &wgz[N:? ?~z8SOD(EN;g䮰Ği5ejmyׯΝ;몫!{%%%3eee6ճgOeee9N6#fEᴴvر:%0/_ji# ;Nlǽ뗿;CF@3Qx@s $Amz_cX6LWxS,WiiDp^' > NH.ruu}#_u(㉄MiMK{_4;c p ݟƌ;#>iU l6HWnH/#A %kN_6hԑfU";E].77bNNӊ4NMsL24XAi|]!"""""iDDtӧO㊿m7 sY_ 1S,͛#"""Jqr}Nnp>.IӧUm0=ֽhhdPMpҥBdG|}}pBٺ/b֬YtDNU( pZQefxhRoD5Y 4NN3,4ADtD2*>w&?9? 4""M5[uKRqO w2fS-SӮX(+.vxȐib :!R\8-77999uCDqc3g1FwHK>t?ءQG* u]()#r Id-Z[j95pQ6zy x .e4Ҋ4G5Lfzᓅ8qXnl <9+DDDDDDb8ʙ9s6=ΩiGqj>3Ү9""""ĉ+-** m۶Y~=6|+piDi J$pŒ$:6|XիuBdgz-V- N7IM.[r-:9Ӵ NpA<vT1ekA'6 up:"D=}TpSV-L:ժۂc[HrSӮhQVRP^zn䦧`ڵuCpӈj م܂މӜCe.طOT<ޛ-S"{7|Sȑ#x5舜4dDi%Fy@ƶct8mjw[[?TpYF؀7751 ӈBO>S|ݨrԴk齫+?><==kdqwZbF)vpwޠpQ>.4 792XUã'ֽxh4Hveu!3IIIׯlK/Çk9%ifc{NN310y@|K+?!=`P@ɚ-"'ϤQGVr 1JDd@%s iDDTz{N*ܑV)`*-*W[~}_9DGGKҨ#"eN:5gϞը"r8_de99iOHd͛q1:Rbueeg Zp!|||$kJJJ0uTIӌ4qx CGѭNst$o/-_5J6ߗH/ԨRiDDTYf^2>=bejŌkGp5>а;"""""1~xKJJ_k $o4"poo <\iU[qf9r4__:!{R:+YcXgzZ@DXҥBd5jg}Vn۶m裏4舜h8M6#N+f8Mm;tiŋ58A.3ylkuc%ӈpUAHLLgGf^;RFiWp4e)Lej.$%%i" Hƍ&+Wj N#8I4upbF𔹨٪ G'Vw/p١3guֲufBzzSqcʼDiy<"NSu4R=JO۰Lf;ЫQ|dA"*ɨ%iDD$iΜ9I+g}NO0oYu.XqDDDDD"&&FfӦMЦ!"1FD qrjNԫU I2-ٌW>XT+-S";EUz Fvv6MQW4\˵ȭD'1D:i]ZIqTʵ2]~Ҟniy$"""""C ƍ#>>ުڻY)ܑmOM+|ˊLݱ沒rHHHкE""""i1~xM& !RVxx UDigΨۇc8̺^QY.iԑ:v]0uQ(xW_aڵtDNCxrm(F4i]g$k[3, YXI4V""{dT|a8d͝;WD{6 ԴA4Ō">sVO#""""=5+WԨ"eMNve DDb8Mwj ٣1v@ɚRtFLfRMn/Dv^@eyj9G%N45ȗ(ۅ4#%k9B8 KdK7G|t -Z`ԨQVUȾtLᎬS~jeMejGDDDD^:lݾ};4H9r4ӈ*qA1NN#9s_/kvYWTX|lݹs0w\ :""N39 OL&qww :๰3ljBpu$R&L ybQ7D ={VN!5hxz/ގ3b8jC{/,Ļ˗kԑ|}DYdg:t(ƍ'[ 55U~iAcޢӸS-)σ[GGt9a24Xo7-]n/Dvvxd8_]ȡ8`8MtrZ ij8|9y&ɚ6 9w7 .޼;W ^H?rki'9aieo{FTiDDDDDDc8DlzZe%WoHmS&\>^B#^ IDATZNM#""""Rϐ!C YpQxx ǐy4n,Viɓޘp0kWo\bf&>:RQ\xeAd\\\GKa>u s2oJrS.OnVڵC_Hwn<^^^9rdÇo>:"R'D'9nNee5uCF 2=O>AɤA7*jhJv2¼yd~g,YDq^|(“$Q.95YtG^~4Eieyr6UL|j4""2kbߏo(ߐۧҙݸp k_x-[#""""rJǏ42ӈ!* = hl5Fٷ/5l(Ysy;:Rӧ-[Tm͚5 e&+̙3qe :""8=(“ӌ#;τ' %kNsjzIޞU&;}Os“BDd>Kj .9ӈΝ;TU|/FQ囪&S)iWSN qclpZ iJ۱Oz╛ :93kz7"{K֬|Mn BDd@%s iDDdaÆM6E9J U֥oLMÍi}ҵkW_~H{nƏ/[id$ DigΨۇ j n BPQ789q?}hFiYi%8*t7tԴRd܁ǶUX˩iDDDD$ٯ=z^z5V+Ւ1ԫWO54qrZ5i"Vi4}F!48Xfӂ{j]T^ lӧ1| :"":9llNNӅ>OnȞʄ`/v4=4""Cc]"r6 F͛KUrQZ,}5kYLOM˩|jZϞ=ѻwoU """"bܸq5ؼyF5 MDiiidreָӨ^^x")I܀Cj7nΟW"9r$F![ocϞ=tDC4f E)np=#Ozb#iDDDDDD>"""-`޼y"8* ]ȘR\<+2NlSӈs1߿9q%##_}'yǏYb)#,,Lvӈ& i~Kzh8Klڹ)6Ndf`ru{!2{5& SLdҨ+2MDDDDU1c7Df_TJoj߹s':viӦ䦧]z??r~aOTFD^ 3a#Nը2-{8pFDtz޽Bd> eϟӜJBlh8A%>4ps~YFqsQmW""""'9ӈf.aTVH妦T>5mB_ϟǸqO?ʜ#1LXnhٳ0ak҃30al͊+:vVV/H) U 'Yqc4up"9F6 ^SƌX,xyb:RС˖ AbѢE+((C=QWdh“mC1^ӌw1Ԧ)U&:Q Yp*8m\2 gC d%"gpgϖ/d;y'acjZ͸|fwFDDDd:wNIIAbb"0k,VYfСUPՍ?^\|dzX,xGqF`84#NX,8F?NNh8up" BC+Yc2mqqbu.?n/DO{qU-cH /B4V n. vǘDZꇪ^]:@ tl_0p4""Cc]"r6 "OVpƝ{ |jק]ˊqF\9{ΦHY&MkQX,l߾ bccsn*yܴ4L<:t@50x`ؾ}&$U\8 V\)tbL0}Ϳc8.[s9 :!"i-(WNf{oܰtZ\tr#Oҷ/ \L^ ~lp˘9s"kn۶]ȁzl||<%Fy@h2 &>uKdV)i-#}_͖5o䃆a^573FDDDDD N#""L:jՒ.R{{ɪuV?+6|pkΪuHݻPbd޽[ŋXt)~7Lb皮HHH%eDFFNo+Y?N#NiD4+Épr)qD YS\R7.yח,AqIǩ2oo_?ڵklu!2{イuK,?lz ,@jj!{hCP%vFCB&<}4 ɅӺ?rL1%o?pg.kM%NsxLCd^= JV5Ƴ0m1dfZwrxou{J+nNNx8u j4""RU+8ȾtwMC|Դ?,©iDDDDv*99Y\&2%7=|M>}]vŖ-[isD"PfMɚgj 9H/2*p)i|jChȩSxp1jR'VWTZn/Dk!44T@QQܘb1N ON3n/oL ݪQmKh; RegGʵ2ɚ6 Qņ Yg4(C5 DD԰ڶ݅:|.`$t4i' ‹]RŚ6Eǎǐ#}ά3P '"p)F5**!b1a6:5bSFVZUDDDDQFO IIIzereXj:w#GTx;'$o4"/ 0)4RR( C&._nf3}#a:ujGHW~[6]q;wZ} W69K3HrnuL'?}e쓟Zn>?im =Y\dDa(eը_ps_cذ`мN|Nsgv wHJ[/d 5=дi*Vwx4\XD>w8NxN#""Ea4=_Ui=|sj}?ʯr 7""""?ժUѣuWUXXw.Y?-l0l0ҥKޏ4iD6 qP%4;}Z6EnAuBb)5._<^ESط &VCd0Ǐǐ!Cd^u8p߲e 6nr ?^cnV ɭ@ɂG԰XeD!N&ޒ5<]0kd/rW`ݔ]K/0kdQ^$ah4sd&}\\lƧf :7tRRr {\p}yzz &N)x4dfʇ9R >zڋ֭w_}}0ztMADӈHqӦMC`􇏕}g sԴs kѢE cu]ȑv1YM0AR|W0Lx뭷мys|e8v86ݟh8!79-TEڶE%krrpժ*wѣ74Ƞ>TYZZ)SlRSS앃MN 3vF1h><K/NҪp"Y9 ĂwTm_6Цpk i%\Q"|?,Pvwڕѣ=¶#1񏛡Ǝ n'iwN;yfl>yO(ODxv[>vĈr~D1NNӈ-+DD <gZ綩iEH;.) }Yc~bbbIIIM1c.Y믣Yf1ce6pi)<<\v[iV¼y?4+qrZIobXdW˄*"2=eP\R"t.`Ѓ]3֠pb ^xA.55qOcǎw)))U4g{NN3H5Wv.| /? %뻴ELX&*ߟeH خt~5睄͆MFDd:u @Ӧ?NGN!$dҰcG6JJ̲%OsM& ÿuBݺ"**R;CGDimڔ?'1{=Qc ُ^:[ Aۗ;!,l;"#SwF.1ȯ* *x +;V>=MKrSrqi hڴ1H_...HLLeuo߾MլY}9vXtrN#-MN@ii_V O<^f JJJ`2nRW fqc3g ,[֪ʆ$}o!3[7kL&e|7S~tՋ6hX'mȀM:͛7OWŋ='9'Viwj%\{j)%e{iW˰uO.% j܃{> {"c .? ze⩧Nk= ܊6mv `+ڵۅ;/t 4h=p;eg`ܓs/"22Evڌpy>m1|%?_,y:u<ѯ_*?"[\RI *CYuKv1M0i*-ę}&UWWW̟?ߪ>H{zM/8q"ləL&YiiiVc„ vti%pb W OOOG޽ozeSDJrssCll,vaEvk Ʉz gV*aOrVV+2u͖i&Eқ qLO{aؽ_X!?Q3 LEi2u{!2 777,ZHvxnn.yɚ .`J%9C1ߋ4uPh8A;5 Vx.. W{0sY4<ga˰"oL ^S=HҤI*pQ'OJOyT-81iRmhC.xj'N?w32J_ĉpe,~xOdeYu[V̙" ~C3f̙?_mOM炜tYԴ$44qƈ|]Bq EEE@xx8bƍpq\|%%/2CҥKK//P/HKr48{lMaa!^z%4k &ʩdWвeKt SLIe;v@1c 1BvѰX#7b'=r$Vo(|܂ki3L WW~MF틦w%Ysyذ۲0|4:xu)VjPTn?DԦM̘1Cnڵ/+-77F<{O#.ךh8Slʩ4 7x xiq:XzRr~+a8Y [|e?7hk8M4a 5GNS0l~y={1Bp/mz@R' wD)nDj4VMS|uPRiwizjj׮]\< G7Wxi?6ky\y'""""$''k^6mpwkǜ9sp9Z ?8:qƨU5je˖0`&OsbC%ompU՜9sO?Yu_pڹs*d2aѢEܹs}6NM#{ łŋ#** | =4ѫ$矸ѭ[7۷jk8L )9_oo IROGx|Oϯ|W5u$|qh>^^mHLLl-g / 66Vd QU GN7c5jH_5pZnn..]-[bԩpB}ٛ-Z}+WGAݺucê]ZZUVO>hڴ)sKc„ p.fTM1ާm?0'b,[e|7c?`Ѓرo<رbVС_GzCˡDZ_nDqL[iib˖ A`…u5k?bذa70K 8.,_pZiwjw 7Z5hKny\?<̬Jd4:izvŸqfimGBض-[޶X0vAԯ?ctaHiGׄ6Oll0WIOvnܘΝCvh竻ܹb<)4hFġCtp*,, 'OlD審iWF*+) J/O[.+((qWٷ&NZ~)5kw+N4h:u]vaȑ/b޽BA5i7i%%%Xv-ɓ'ȑ#/&&JH+III9;;׿мys˖-ٳgY7oٳxlڴI'ӸXӪaz͵aODy߿?,X7HerZɄ ["q~כ0ujִѐ#pwǓ#[E(-+w3'02Ji=``~22Ƞ/tQ>۶mCQQ_n Ja8́84_L%f<&4 rZ.<,=zAV;3r8ld`\JRЯ(-.1VF1OOWm}2.//WsssABߗH?eۿٻ7SuGbӦ,ɝU5X"Ç@;sq|m5;iS^H^8C^̞=1*Ue|O`aejZAydjڔ)SamDDDDd|3`ԮpkעSNҵk'9s[` 8t͛yv4h;"<S|]w6WWsCԄã/U2ONc8(u Dd/c!!-=z[@.\(Fn ۶eckؾ=><=]1~<ϓԐ\˖]e!Cj 8$$'…uYoS}k'O2yyxx];t~|L|{w.n-[q8Mо0FDD뮻KVi6 3mBb6b15-F~ֹrܹsm3p@"C\}?$89q#|k׮ŋXd ,YzAhРjժ3gO%kٳgꉴ!C`͚5uiiiX|9/_.|쨨*SpB>}* ݪSSQ`lFx~b:h֭ɱxyቤ$ymɺbfV@liD odɺ'o XKv&@5p2vFT~H=4 zY'7߄W?IGЫ?U-]Տ_|ʵ_p`̆YFbbm<)ɚRY5k*)6@{:45j;"5]^8{XXaIIp)YK4i⋣:oK !6f>)yqR RSs7?I^^<<\0ai&Ν WWv|S8ۃ>0t'""""c%0bUpvkk ,X [c2-[`ٲexqA{ի$RFi"!!A6wnѧ48Q`iTS%&,iJ4o%rIIIׯ*NeNN3_0J~EW o^ OpìWsC>}(jtۃI>B-_jz;F0Oᇺ憇u[SPT{W]CXh(jFXh(|}< '/YYs0?Yݍv39 tয়`L&6mڄ7*~윜>|͛7Wؤ!&F)d8NµY 隤.o/W|f$ڏ;/oG|Q^$ر0m1KLdH ɵZKmp丒kcӦ,M BLSEnqŸq5xqn=W ^^i駟~~zyWp,L6 GDDDDv&!!_[gҤI {hN:Xz5r3id+>;Yf?rH鶾5䴛7o0?;կժ_oo=k.ff/ýzUY+55U㒖s._-M 4`т@$>@6pr3wȑ! v㵛R))i蚨&:F&ՆG\Gni]v2dU=ۗ8;(̽T6___ fx>=mD& [VnhO}jWrsNeff;#ѣ}ddd6i'1vph}j"#j`LSS6`i-CJJoñk95<:.. 20j6I@{@4l {@]&i Ħpb.ݫ*~UH=ɊSNR$nҤI?z VZ65&MWWvS'Yex޺۶Y3toNI;AеM[d4+,R^ti&bȐ!8p 뇞={nݺY&ӧ?Y Ns.D2Mr'w7ԯ:CsYRRmAJυ""҅43fF6vDDDDD ǏWXd=WWW\M4Q}-H)zLOիkJxbbkN4ۋxcHϞ k&-\>;GQq1r:Tvu!ȑ#{nnZVn2Lؽ{m-Dw)w!)d8" VS;zg>@ ʼnd@ͮ1C D&Fvر5U;>9Aj7]0aB-ՎOdF]hZDtt "#}4]1FDDAݥ,aƌ uEDDDD*99Y 2lk"00PuN# >m۶tMȈFkݺ5ZjzvZ5gϪۋG o/V}ǀ]uʼnY,n/D:DJJ >[)55U&*7P袚` *@?7?/B}8$Sh?]qz5o^ :h9,DINV/3|xz?!݁kV-OՎO5*d]pFiO>$MѸqc4\TTVXWW>-M6G___3Frj bu fp(ꊇ9ixyaOL֮P&@TX-ixb|' CNN*Wf.7:wfGU_M Wjrh8MH9Znx>< ];hQMc'&r""'*;rlN$#;&||$yz">4{pfرt@L>]ᮈ^,^: P7㥗^RFJ>|f=zM7D* 憄MֲK1vjЧsgMѷ/E'ܑCo{~MNħ= "t)ɉp3  :5~4SxaÆ9{ ٯnݺ-߮Z ͛+6ZZzyw7'rtS C\hrekQ;$0D7QtZ-ޝ5 q\s n>hN:a޽ݻCϻ}vd䎝|41&^<شZgŎBt K_JheG&Zf<#yyyi܂&MЪ7&rG Pk?CH!"BߥGG* 沛:0FDDNj1ewRSS\?ظS0":ue4RӺvDTPPJwfSP X`T8}ZzT&GPe ۓ{jX?vND7WCñf̘1Zcv޽\N+UNc49/Ѯk7'*zwNRu("a4j1lXhw!^ȑDjԤڴQ[e!PI^ KE۶mO?Uٺ=-- III UCJ3f 2i!C(՛4rÇ*ի'6 ePFɨLu4 IDAT Z>/FӦᑑ#=vNAUV+V([ j/ኟڵk8x!c4?%Me TJsZb7fMrWypPCR&xVӅҳmƍ2Bzuy9j"S*XjjpNM6!33zAAAw-":u*X0BDDDDңGƊ/`gu3gze< RW-DjA5 ŷ.%Elɓ֡RY tOص+cbd+O=2鲝M@oOH都{SNkǎ\̅h2vNk7'ed+O%Y"s9Mt\ `!r=]#)ɾMyu4p~>_^#GFK4`(]իQYYlٲݻwGVV^|E̘1% jÇ+\)AaŨ'FiCiӦٹsgnɭ(D'N([pڟd?ǍSdʪ*]vh~p劲@ll,6o,[q)۷oWt~R/.Di7NR/ξEÑ$S5ΌZ,})6iN-;Qo)"ףazYcO?A#"eȱýЯ_svDے0FDD.];v숍7bxǝXnh BPPUhh(>sz|HIJtOڵ9[FF[Ӷm[4jHT4tlQaPڸ^=As~QL!E>w""#Cl\y9B^^^׿K]vNS)j\ .ib8MR/niXD/?/sE$鉲g"uJMJS7z m>X\=!C"'`HD^^4t;Я_(6n$. 4l۷q;_ƍ,Di42-^ 0l04iDu&\D@!ONR|oCJظ'CZ-x}dH"xGd|asʦ~} !Al짟*[ 6 vBfdСCn*$o ځ4kckMN@X0𣏕m}d .t{:܉"Ui 9S:] 5_ Ch6?ozF0yWJ@T_/&8 nfp`ȵon]o@5D P/~̕ n& *`0H9r$fSb6=4raÆqvөS'5"j޼9Zli<ƚUU ]zu?5;x,=1f ^~IspZ>bnԈ6F.z-@}v"1 ѪÚVxP#9W7')T yKhDo>ׁ^Nsy: fgX\߈} // Z5}%@) rrmύ V~(/U+x#"""""""U ŀ$;rw}צpi(ZViݺu"USSru`s+ ,6IekQ)ood5Vxv#=ySB1B*RP7">DZTlܸqPOj v!<@*q28`g2pJ-ܟc_}&r q9F[U AA Gb1hPMaGDrFÇ۶^~~B9^hоnʑddT0n_Mf^pBƍM6+V 2RlWTȑ^4jȮ9v*S5DގAAA8pE'N([eunuX!,>9WVnP"kٲ%vލ=i*dvi+;I'UcsLXǨa7ľ2:vNs'DƲ:Fm:M)X :cSR)Hjrs#׫bƝޭlV0FDDDDDDDշo_DD7;x|'B3ȑ:XkDfff?|pطmc4Iv^WE)c5DaάYjkwN޽] [l-D*> ?… 8v|;.&94):y5x$.NI@wL1&zc8M4e GFF(ĺ $'20MhԨhԲuN#"""""""RFޱX"//I#tZN#G>|xub ƈJ㩈|+q3f@te݆IIݩ}YYXCW˕.]"4kD .6ek!r=6mڄZϱ}v+"Źk4?vNWb]o?u -%ebɜkY68AwN,{,s3zt:594r-"7\Ul7zs4CւA,Opֽ{w:rx?1 iZ<ӵznn- 8!!!6?/)) T&9M=zyrsѨl7N^jO4Klʕ@etݻ={w!sE(h4eː;KA\n#ڇYi7'rvNWijbDZZ5͍T""RH'##bֈeht8--II>) y lJv0P^l-vۉ6[FӦM;jΝk5p9CNN6lhv@5D#FyFbi'Of.ս GAVmxF̙/m#.dR޽ƕ+[ ĺuOy41i4p;YU/NSF,[ *{mqC"l~YMj"i-Z[ $[nΆ mNhb3=Ri6g*_ 0d'WCtR$&&Z>Z{/ya06=a=FvO;[Z$0FDDDDDDDNߟ)"TWS[4?`J;YS/Q8WCҗRg_dcDb8MUl 1h,DjD{9J(矁,cX"TPPǦHuN#"""""""RT:uлwo=٥4o-ݿN#g{Z׮]=]ի'64wTP 2ӱcy*\LzWX|xlݺ&Lc+"وvNS[8Ms Ӭ'O'"@4H$I>xczk wN,`fH x,?Ar]zdg[jk`$$a:yӈ7i$e 2ӧO1jnvOpyQF(`0`Ĉ NPkٰ!bb>p5D^~WU9 ڴB-/4˧*[ 3 xW}41i4_iVY6<#;ta@ÓvRijh؉H, '&K~!VPpk7" }9"hCă_(V0Vt0;^ iDDDDDDDu:<ٌ30x?șt:P4///txtPտ<]J8vNHԄd9"뺵k5o??T9Mz|y#F`׮]hҤT䦝Dz1fM;SL4MytPpty&1 ٠Af~~4A#r^Bsg(ED*'A;a 'UWg\EDDDDDDDDnA`ѢEh֬FTcڵk___UD: j3zhT&9M699B!uk{MY~iЧظg~P"РAܹ/(**rpUT+WpYS`8CvB9O!*yZ,})~Fzm"e8MD<3B~>>Z yc 2X RK$5ՈNF^!H4Bk(>h)0FDDDDDDDDn+V@XXit"Ӻvj\СCgOhh(TdӥMIH1f7BH` oX͚5B!|}}h",Xw/na4휦S٪|_I79&wO!c#\ 0%Z#k4iԼ?Zް`]oc@܉mm8"ǻo#U[wg Wp]tRv"+SaСsrr`0X%'ChRDzTLӡ=H/BUDҶIl|mu|a*%|pxl۶ III=pJwNSB/i ը^Z,T`"ÒAU1V<3B>:wBJͿgDI?is34y#"-tN([ ޽;f͚2vOHKKspED@X~~+qF#PXvOQvh4jĵly=i oWôn{Ax40 vNS.~2jR/όG5"W4M w"ќ\ȑQ-q3{=:#FD:" 5Ky4(-Ѭ+R1vN#""""""""rTg@5jimڴ?޽;z<55;vtBE*gKmNR7 i\[T|:Z++cxc"D;-+&aeu_t"rK_Jz"z{ZEFz h P/gBd#ЧO(ay(NHF!FDDDDDDDDDt:Nz]vuB5DCXƮiTظ)QmFs^!]dl~=ԍRI U"$hXl5B4 |Ilܸ>>>_]$xר,&L`8&mAp"yI>?-ǂE[ pUXMbO\Hxo2&##QQ492IhbvN#"""""""""r;FB;# T`4m*6ߔMdu~?'Ƣ_.Nv_f3.{ZZظks=ػw/rsLS[6GpY;^p?=NXp _;A䞒vɓwzAtAdQ`EvN#"""""""""r?zi:N5ԯ_oPDf c8MȠݡg񹹷3ԋCbL Ϋ-ֹظGvt"RTTTv3$0 QF4V0 ;xmj2wNˉy{k1bKN#"+̂4^+s9԰8ӈH-ƭ_/蘈ȝs402FD2Dp5%:/pZu܁_iDnDiBD`@0s vN#"""""""""""Zi@liDbcwP""W$9Mh8ӈ*WSc4""""""""""""R;;jZlADL4"N4 /yhghug#""Ifȑ#(--իW:u ::ɈqvơCw^>}q|}}n)))FlƁ?ܹ%' IDATs8< ""AJՙf?~ıcPZZ+Wd2!<<aaa@r̙39s1yyyҥ*r#'j@'8}fC5i8$խsӧc~?{W^EeUIIhըB?q5c4"^z%>|i6mO~>&߁̵k8prV p/DF=BqUn2ލ "rJ|XbV^BhĀо}{~O>UPZZZsÇwފ]^^e˖F_hhC]oٲgΜqnUn]."""""STTŋ㫯–-[j>}`РAɁ 6`ƍصkwfF-["33cǎEJJ%rX`1ZڈW=כ?`ҥ9WAVi>? g(9C{ư޽ѢAyhLl@YDX[lq~aӔӌ Df益xM}wZm?dᑨ*g6w]+.ᓯqU"0/ t ƃFO P =[ᄏ  Lf|lZbulV {=n]֮-ѣeVǧN3ؽc"|eTUYc5*4*-֭%rg4xy|U16o.Aaa:A‘>r?&{\źuE8q1: a|elP cTVo}"113f@~~>t帪 fϞcǎBxѢE L6 CUPW\AnnlEFFGٳ'233+ܖ سHKFse(FsӾ* lW0akYG2wRV!Nc4""b <#8y,;v cƌ?̜9{bݽߏ1c`Ϟ=v?`ذaӧ.\x*Tօ ?^ǘ1c0}tCjDDDDɓXv-֮] 6.]8uoRɄ%K`̙駟ɓży' *?xݻw_~plsVUU7|w}#Fmn"W0w\\vei(_={7iN;g<Sbن X6wn'Pۿ۷3FD<0f6S?>k%Vf嫂׳;2K礠G@˃D/*TO?]ڵXfV㹊O?ѣU_G^0wn*Ə&dN~ }t^~yNa"<֠#Lf,]z3gÏ? }E1b.o҅ b񨬔uЬY3,^έ*,\|N~%ׯcXv-֭['KhVf/-voiiiꫯ$z.^(k0Veeeő#G0eEAhW\k2_˖b [~fDIE5u<ѢX8mED ݫ$X^ N'Ns4c촣ϪB<~GZ+%ÿ>8/{U?ckѧsD/j ˗n]O,wvI6Yrr~IfLp&L'cuD1w)lhyPWme̙#Or&lAK\-vo=H$WN/ ys \wj徻i_˰paCxygJvN#"XΝCVVvܩ6l؀mbٲehժ,sN>f͒e.K\7Ę1c;nܸiӦȑ#Xp!zjѶn݊ru8IRRR\EqcÆ hذ"g61uT)ݜQ͛gDi+[Kg 5pK_> [*qj \=sZԣKi+\:7A.h%װxMUT1_ho/9$f޼Ә:˨m.#?'iz_C#dS|e˰ʕ*Ws=Z=ذ9IGp{|/o٥(5;.]={:$Ǐ#-- ~s͘1!ߪ0n8Yjv{C IƝd©S̀j ~P'"w`|%9Mp(--EFF~G =]s̛73gδyAAA6?l6m~+Xr%^ygADDD*B:u 7a6ӧO;Z[p!.^2XшHNNhիWq9r}/^*?TVV"''CllSjʔ)ׯׯRFhݺ5___??,Y{8/!Cs\NCFжm[i111F`` ]bشi6oތJbxH>xz<44EEENl [vUd31&Fa2P0u*._Zp @J|<Atx8JJKq?=ͻvQQZkشq;v@b|_ׯ_GQ]}tM3; w΢^4E|m+ظ }tXlG:nژs8rz)F<~a:Pm2c%+ }o> zͪWr+^[o:I?̝{ Luwb_me^] ѳgu(+sM4 , .)t-22BѦM| __ΟCױdSZ}ॷ/"g@f4"K||bs^b0 ] Q;5o!L͝ӊTa痬O(> ˿{4AX#$WT1<3ݵBQ%}1D`DxpdAvIXjk2^>/7h8s5\Ɓ_Y/`Lˤ|e!^]}M?tWJɉ /Գ:F/8s/p]JeK 'خz!2Ҁ>}B>}B)v -҂q3֢?d[WV㡇\cCp̜ӭ[0k`_~YÇ~}OH6h _N'*ʀƠn]Za0h#HK €q]Y oqӦ%Q l#*DDƏ2c~al߾V[{dqpX`1ڵm ݪK.ضmjmo o8Z||<>C̟?j2Ʉw}ׁ7sAhh!7;wb6~˗[ncf3^}U;$Wo{Ÿqn>d,]˗u"G[d ~׻V*ȱf?׀tl9Oض "1_?O.8!WS*.0Pp̟_Gvꫩ0 ~~8ʫYҴy\g'Ws瞲<"04-g'cϞv8w. |FEd0-%ňb(,Lƍ-1iR(wjР6n܈hӠAlٲb̜9Ӯ(ᡇN#"""OF0qDYEEEXv-׿٥0 ={6lقv5-Z@1 :gff&{1DII$Rlo޼9넊Ark6tH셿߷va遲ISrv"lu[^D ISod"\^ W9MvZm`l Xz.\Ҍ>:/y|֬dmZ ^ʕPTTiSDΰresl ''U+x_~^/@{ϛ0%_^^زڵX5B`^r̊:WVQQ F:u ݗ*~ >c1FD$T}ŬY9w6m7 11Voi&}mX[VǼ8^揳ȱt״aC4v11e&,P,x\dqɉixdt }-~}JJ,kYY6ٺu22,o]YiƧ2T@Uf-QS$Y39.(yƍ;w甃VŌ3ٳg!"""R/ضm.^KG\o Į]Rt ƍ^۶mBCC'y|߾}{KLLÝP};tH:TUWO><̙s`E6 ܩlDӜ̞pkBU{.y,GHHjD(s)aՖcWǮ'_RVF{9 !ւbO>_<ֺx,]pQMa^ IDATeuص-ZíƎc{ڶme?'tCC0xpÇXJCA ڿ/}+^G||< >|0VZ%ysΘ8q_Fll 111N#"""ehj P F[II -[ȹ鐑!yyka3g8r:tϺӈ\iNt(:*6:4VU-np<`+HN?,9A<4H̖*c4kWP;zKbcY~a8Ckn ;cǎ'Ljlj*cqHZ$xluk;-Z%aw[^E&~vCõEDDT3Mqu"SEwh?+߽ULl\puXp V\)y{v`5[z䱌 VuM6ǭ,RNvN#""""TF_^^^ƍ(/Y@w'7{ĉP sgq y p]Zvp5+6ӈe>}HnA29Kl\77pXI`Zy50zs9ƕk_ϡAz{&,>~ڌS"OTW JvTܹ =[!9}%>/yy ſt5^q72 $x]F"`Æ z??wQJQQXYxjLc=;]Z|Ze"KΝ;'y,%%Ł}GUm}ϤWRH( WP )AAұ{-)RD_((қE@B +׫Ϝɜ9ˬ}V`9e~(ԥ RBk\rp%X-)sۦsM+N{G*8-\٦]Ry}vNZQݛNAǪvO!]Pl)=4 >(ӜNLL.rsC蚦dSRAP &Ff[:Uu7"q%8HNo[LLw һt:ͻ=E\\)0"""""ϗ400PloX&M4̄H?ŏ.t1c2"ԭ +iDk(,^=_sa߾}1^zJ㌜Piei[נ0UuDfK޸6gvDJC-JMs:QQ9˜6u8""BI_i :|8]kλIiqZxs!rY""3ݻw2; ?3fcӧ-5kej кuk --M2~IGӜ䤧OfXFDDDD|ݻ'sJA\ӡUVgCd\LL uֵBFvO۲3g^,z\[qq 7WWT EXh(ʕCʕq5#ٮsW"Ua&fXb#@Fo]lTXDDNa~[Xg)NikxMʞ$]Tfks*9-Qv'@8Kݽ},Nc4-.WOu?ubqɉY B;%_@aⴰ!O$""3M8,, aaafcg cO<񄆙<;v쐌>}ڦܹ#h قǏ c, SongDdܼyPPPP3gZ!z)iT`{۝;a V*_|z m{jdszUSR+7oʗGYkuvwBCxc_6|NDNfX5Ie %Jv=-%).N3]#oO=#%7Ei*U o.8zulҎ% r]@JKn%[+Gvm5~ Bwa,&&W9~4E֡h:p N^QD\lZUHv9nǣ\(h7r$=Bڷwh9d[K^RJݸaXFDd&b){(Nv0&W$fIjnC'O cr9"ݻW2͛k٫;wbܹ gDd܂ [rYfY!*!"R9Dz sct$_Q\]ySh;bz2;&Y|/bܹCf@VE]/L IJJ©S$chժ9 bUtOCC{#$U2;HHQL۟Ntk#ʗjN*#(N+&+ b7E\Mtʕw;3NH'9"U-4Έȶ ĉQ8yR`NhA8(>"iߙSDDfxdeI1%$$`x&M4̆m߾}HKK5i^JȩmذCqn3f̀4mIMMŲeJckps.?}ڲ8b,ZGB̽X"ӪoAѣq̄¢"|s'}Ǐ"J;8Hu{^t.."i~-*i9KiW%bx`qw 'Tc'9DRҋ_^aDq1g<Gb4(xx{Gsr;8DOEjjdI_x{WZZ!}VuqaΜgeAƅ[odΝ;˜ݾ}[AXXf͚Xrr2G5Uan*`t97MZQ!9BC33P\l?ȲO80TLlCqe7oj߰eKp] ZkUXaƛ6mP˝/^:en֮]=zDDDDDٳطod F8#%c<@JJ nݺ#GsU\| ;’%KJ^Z5'Rùs焛k4laaag䤔9H4"vu~ /l[Z^BvNܼb|>>pz=P6 ޞz$HS'ͪȜ8Qg@8u)};FŒ]I|F_tӥ^ee81omaW(WA6#!|NhJM%KjAHw3\Ey逊,!%%٘N8WL/Wf-4h~Wᘀ9RìȚΝ;7 /ِ3f >)Sک x=$$/2"Y@ٳǞ9ⴿqsuEDh۴)jW CFV''ݻDHO\ݻ7"$W{ p ;'g͛vr@ dY\T5j{|"'k.a]4l.(3^e:Ǟu1tu  )选aʏt[#Rh[~IŇRmӌ1kӜJ~~1<3͙ed3!""r*gc+h }\˗O[;EIwQ,!&iu,N#"2Cvvl֋ӲdZ8MN.oK G[;6oODDDDD0 8q"]h"""SOi;SNEVW_}/:#+jNYqډK/Y>Сys~YIx{zʎ?mGE>2[@^~>^[J͔z#}{7~ɘ7FO>aaG\Bܼ/7o#Gd7q+6w(R% 27YFŝd'}'+y/.ln>/~HDN^'+6􆬫~u/u(pxZnCZ76@i1,9|u%:YPDD 0! EES͚{w,!*`7,XPC_7yD!77W^owsdD[J8.?B?,Abb"nݺH%4i&M2;?"""""WƱcDŽ?:.h Mbܹ%^ń )Үǝ9dd-aݜ9x??ԯQvũK0`ĉ %s 1^ʊҌw "222>>>x'4Ie 6_7i"{ų`l;?#4̨ DGDo gǂK=ڟ>̇TVX(Wm4\ȹ]2E\D\ٳcX `']=es1iDDf[$f.%v4KEGG#::y.446mmJMM̙3]k׮fD"//6l Э[7,_UVvZDX~=bbbJ>~xZ!#RD"agOcc:4on<=۷/_~/TU684%W&e׍n׼Aٸ]ƎŹ+W$$bxGJ;uHGaN:AtٺXe|_ÁaƂ;ؼ/ ,>ϗDŽ@kP`aq0 (_>X5s:QG@i,N#""HK+Čׅ.]AfDd^}iӮaٲX\Y;;гʨ-qqjZw}iދ1k/w|5۷zN4o 11Q2k9ݻwaÆضmS!b8wwwL< b*esACC.Lcu`_Cf_F6S5kųf-LSP2شp!c[|CٸMv%u]L\ +JW79Ⱥ'qwJ WfǠHĉinA"ɜbLv=ovNc4""5oy cu:kh}~=]ҥ 7WS8o\:d5/P+r֙}8{,pl߾}b a|PȈl7nv*DM6ի%^>|8¬Oe-{N9n0ob"f{ñPptL N]dҜFyz*4"U8Fd+@씶R-d=0$OeH\nd=ѶfXCIs:>WS:LbS:BDD_RlBѼ\yyJ*U򀗗tx(,XpǂjD+,NesQ 3OfR:rgeeiIIr2 kf͚X`z7o ҇0&"2"""""y=z4 OXxY-_>n޼Yu$$$իصk<(ۡ#F@ZдiSKMdٳK0}t+dC&x㲳ڵ|Nl֘1fD޸!믣k IDAT4lYrwsS4Q|RcD^c7hP%sf"##qtG5kF ޮ"]Kt%AN9mʼXVPZ7E>(_ >^zO.ĭ<94& |$~>9Pg=+ZGdc`0kϱt yE&]mb¢Wrq"7+ӈYFF!^xp7,YRKۤ4ֵk nj#gglؑ~HOо}کjN@F,N#"o^^^#w|[)Ns!G.DDDDD_sGʕ+aFdQjUXjO?4^{5$''cΜ9XtH-??C ӧcdرΝ+z߾}Qn]+dD&kZu_eq\]x%/]`FTD8EǬ1cJ} o-_,N# Bi7b˦aI[~M-LW* "vMy'`8\]=BvnAXI /0oa SKq7n%_ǣX 2ʮ6сӜ\AX~_*rs8ȹM۷śX\YnZ||\Ъ?ZǻVņ x; v.M]MV6? (ز()""3w"//OlL'W\giJ6[X^^^y}h9`̟?gΜAʕ^ hѣ>#gΜq&d޽;p­ZIa={OPTkVⴶMb.r7Ed*Z6}9ش78y1[uJp XF`\Jl<=Bɼ R1][VXr󜏊RwNcqY8\AvG!,Ei4WúB|}a|;UѸeoWEÚ^",X+@diK߬2Jc ~6D2Pn9^__ϖ&ADD`FDRRd\VW3#/>>.󩴴B|fT\ zҲ(+*""3xݿo_feˊ[޽k6CCC-+*Tݻc}6F!_PP#G뉈Ȩ b޽ɓA# 4i~[4̆8qW&M{VȈ l/X6'ѢaCa,!9YLș+N[*jP0jq;ib׮]@e9PmMJnlے#] x*s]wM(Â>r n:א.4Ufn n4$ZS<`qeѢ;س'EJQ&sDNV-/VUaKF 9xGi񊊈Lr]|jrE^wA^vMsssCPPo?֮]ӧ DEE᫯0+"""""¹s 4v"2ĉ+;un߾qF̖,Y":ٱ>}bc-h(S&Qt6O^FԯQC1(&DDʰ88eq_jӰy_06sL/:4%Y`:ҼmP 9]8-lӜ"!A9tu?""Yxx>=ٿq*GСtṘM(l1ڲ}TfM?^2vU1Mjՠ`xa0ƍkWtt0Vreֻ!W\֭[%>F//uv""""""СC/www"^ ñ|rѣGQre"gT@TT Ǐ/W\nܸ!RJ +q @; {/#FX>' 7WW|4J@xM+ ?ddgviFTj7on2ARV`9Pu_ؾӒR q`p^^OЉӈG~~>~ɘ vqFJtB&;+N|]Qh>>4i0rJ !"""""5 5 III1VBŊ5̊hs43!"4ol?*RFԭ*OLșլ,yXi'DzQ+/R9z([F@@'휦) :P*'3_jmoKdț2E~iNF ['KMqd kn G"1Q\jU,KooXzfD&y6iӈ!k֭fcf͚ c)))8uꔆwA@=ș8q"<۷7n8#"""""R… w^a|ܸq۷k 9$Nvl.,1e|}5ΆY-⴬T=VFv몾9Ȣv%u]L:RqMjlqQX,,N#q>Jxe8)ծ-.NtI4YS.DDd{/ݻSc+gC4̈qI_Og,o*4j\JWTDD*hӦ0?kׯa|f#+""BX f AAAxg%c_|Ν;_]S.\aFDn"^XFD]}g\XaHKVƲU.t[W8MPfrua&T*EELޑ* WUwug_o;#(*6ߥ;Vml9Z uěuo#""w|f7:]0#"ǖ#. 8{%4xl>`DDGXTT44xꩧ={hKqql!رcիW*퉈C"??_2 62"UHq&D|})qpeqP'.\ʰ84ԨxKjBӦ[no<'2Y\\Ο?/+[,"""4Έ+@ٸ,vIiV}y a ((Twxq9g#%P2Q]&QŋC:^!"rT9922nn:lP>>ܐH &}Mq6&Kc,9OD:w dgKHokr={? ;v]5kZ<۷ YֹsgTZn*޽{ѽ{w#"""""M2~f͚iCk`3!gK/YǕ+Wb Kڴiz.Ne˔ݸx=N^(խVMLم+@$]ySDI3l%wL"'{na쩧^V[^8G*NSv`e[iE ToYWFxS2~\>Fa4a 9iED!6d常<"P]{DDjڴk|Y\ˈԒV"A'`.NL2 ThtJwTnݺa͒%K`Us7P(,,Y-^\\cc޼yXrвeK`*NaÆᣏ>]iDDDDDvܹ3MaFD%&&"Ub8i}Zd]v Ӻt[?;f|i;"ƛ7ha6D@DuH_~]n#4l׮]}YL*N)lfie|R T-NK~@a!4W=QI*ӜVD~9I2vP)k/g#1@xl""rL[&cN1}z% 3"r|Ή l8͔ig)O1^QdĈXRR>s yȠ{HHz)a|͚5ZiI:~8>,>fw`駟޽{3f0kBg7ȱ=zT+[ּEDDxeerr[-oqdEGu5ΈMc磢T9MOOOxVV nÇX8f̙ WWj"&&F㌈Șs_k׮Eiѣ֮]+ƍk9•W[4-Pyӳ0|>t -,,SjmXƍ׫ӈ,ivqRþjЬ0Ld6.^vfU[E8nARZ4ֳgŖzcQX(իl%""YxxZ^X9o c= oo]Tdv465 `q*Tc ÇU3y뭷;g'N??qeɓ'K=ȋ/;w 4@^T=% ٝH{999:t(iaÆ w}Woٲ%\\l:ٿ5]WY棑S.8vjsffg㙉 ӸvmlHcb3cXQysr`Za|h[R]eYFXrr2N:%gg[8-AӔn`gi~..vf#*&;I>5ߖے B<^ w{LN)ƢEwAvB%""SC/#/OQ6499EG͛~}a',vR;tCopMp7ހCΌ WNZ#>l#R׭[a&DDDDDd̔)S)0#Gw}4h@2d%" Z.]v F|2J$8r 08DxMI6ۇS.jk")5U2O=Uy TDlϞ=(.^H׵kWNWi<lY_nN L ^zmSl]tL.&|xKk%sS^ 似b̛'!re*WD۶eLlԩpyMΫksxWz~ЃU IDATŋx+cƌ1͖_ʻDD*X">CL4I8ʕ+hҤ VZ}̣Gbܸq|sM6 _}nܐn-\\\C֭[5kVQTTI&?ץKӧTǰ޽{L2HOO/z*~wj mٲ˗/;uӧk٫D <}/^J*=gRR#GcpT^@Xk|ٳ;ud4`0|Fڴ ukÇ`0Č "qbL*"Ksuq˃˖ <7u*No܈@::}K;owOٸpuKv%u]LHi9M8M~!X%I2V\ z 7dMں5 ÇG"#Ct DÆ>HM S hJjaqL8ׯlj'c0`?|BBEGaڵ0ԹO?={1 xq5,X@S?%&&bضm</^x^[/[iDDDDDVv=;V ڵks!)?b֭4hNM<`?S֭[cǍ//RfKDd+{)d Сö ؁ov@ѡystlMChPEEHHNƍwشw/nܽgBw%0x0W+Ufl,O͋)zsSHXAWR\qH ݻw ݺu0liz8f~SWoJƣbre[U傕-^i5 DjSc L{JkJ`TV\P .W\L\;jV4.]FϞQ7Na.QQ0{v ֮jcTw n,N#"^o͚5CZȊ+W_W^=z4wHNNv؁ 6`˖-(y[Z=z v6/cܸqxW.hTT,X5k 7W͟? 409wk1b8o… fv$QѣGHKvBRnJ+Wv_5kl]tAжm[ :ܹs8tK\tq|}}1qD'"zԿ,Z v go_ew~6f[ޞ+զa:vPw#5k$㿞8cݜ9hݸ¢"'L39EnzKtlBHLT6JuMΝ;Kի, şvEb;NPBrР̛Z z܌ÿf`!s 7鬬8'3P;,.x_xy|'bD,&?|\;S\ƥ0cư'_,DMHhxz1cFe̘q]2Rm`2={R0aBnz0PpLD~1]4{A.a `˖FݻwH6)5Ф1SEFBݺc/V\,gޝh|yR+2ƎW^B1ڕAJ qG@+t: 7q#4ۗbZGϞQukǗo<6rWTDDRZ5Y}5@7o͛QLTRGHH .ŋ((ȪO>Çqq?>/^FzQʗ/w~:.]be-H}Y+jk߾=Tdl߾}tQi 0@XDΟ?;*E7oLv 8qś ʖ-e"$$HMMErr2␕eK.EXX2'" `֬Xxiòyِ 6T /?T۷f0hvEJP6 wqu߶ q pwiO3H]v cݻw02ʿqqu:b c&b75|Y7x~rna4=qJpse cΪ8Yw7T@h+|\p/1qyHTqǯUBx9^xiܤIXtСlYt D>ps,9;epśor """?\\tpw!'Gwņ lkh PXtMXFDdQ{ҥK1a?f^zYOOOرm۶w0*((3gp̙RZju֙55t: 6 gϖ]iDDDDo!..ά9qAEcݍ""RIQQ߿/>`aÆaȑ*dED}ܽk|l|O. ÍDF*Acr]r?`E ƄR9~}xxg3N :ĮiDDDDS +Z5\0zZY. PzJ_ƢESJ&Oo֬9ñg+'݊^mǶm,eĈۑ-џ5je˖Y; "r&( IcuX;a={Zdg:t{&lg{®DJdddرc1///o^Hw0-є>j7Ve5ë[8XnUZ7ŪW+fqg`WQyw/>/=7`?7B`KU>X:4"" 4)Ɔ ` N:Pvmk׮>euy撱||wgDDDDDDD[n8r 9#FMOҲXe >ޤ ^5jX8DX{h׬s6oΛIsߏBXǎiC;MCa ?c3- ӊ-}22F?bC]U]s1uc_ZQ8,6 >\t7/O6_zX^aS""""*77V?nTRl)gWiDDׯ?}8~8 ժUñcЧOUN^z {EPP[\kj YJe :Kضm-~,"z㏕_HH\>ҥU+޸ U 9spdZdq 8oOOl_TfaҥRe>I7n(DڵK޽ba ;9Bqqv^] S}Pw\]My>7=+렌DqY®iR?KNS[U+֪={ 8M̈!ʹc0fLTv\So6 [b 6KGrr*6nsA=TOJpp0~'lذӧOGIeԭ[K.EΝUж <SNa㈎FZeƍ8z(>3lڴ ݦM|yLw5kĻ+u't8`|l^`im6YzhV>>GqMpwGѫcG<ߧ|쇿/ZWf?P9z=fZQ+ծm<Df̈́e8RuN)EЂN,Um̅wp^Y!j8ʗeQنN|>ěŃV8J*S4Ř1WaRѵk 6nl@.%5~|EřLڲ}z(P")hQ-n5|=|y,nQeޠ 7L^ &[TNQ>7rXx4I&aرXz5/_ .<7t!C`РAi9tPWg}/:[ɓѿ:Hիg@hh(&NsIO:e8qζDz8R';""""?UPAx/Kv h۶-ڶmcӦMqJqwwG۶m1e<3ȖԬY cZ novtv{ׯ͛HLMEzf&2Р Fhp0֬'۴aA5w77|] SÙHE?ѹeKxBkrs[mĢ9[;2iY'wF΁du<~{ImÓm1JhRd[}X% ?{wW]5I{Ж-PeqA EzQ/\**rPDXDEB[n.}Kl6$&Ҟ$s23x|9sf|ڦI|S[>\Z#OՏo~qCU!;N̘1sL꺔6Լwqw7kYƲe.:n2ԥMw zh/=Y%pc;zkrsc=䐶'k5׌W+xM<&-n>q8SO0 E]ڶ5g =X2Owl-se뼬aG$u.3g:K.D"A,o߾ ?֏\$I czͫ`޼J.fn:A>Gy ϴi=9 s5,cBjk[N$I,e8M$IRy8Cvl8 կ b%E{3z$)]f y'GE^NdX ? N2Ғ2%{w|?;J$I5XS~Mp+{i$I$I$IRF;Z݌p,,z$uo7 %uqBvYۆ 2Q;N$II-xm}֕Ms~MMib8M$I$I$I'0~|O? <]=^ 7o$uua-mQ:$ܼ%YU"ܼ^!I$iH߀77m]aZ?6&I$I$I!/d nUIR6v!Ibtw˶B}BpaquH.vN$IFXNyEGdP4u4I$I$I$ 7_S\$뫨7 7E2ijͫOy0GVD[R6f4I$I!4]a,:55ei$I$I$Ij|N8!%K˃$+@"yyyp1#I`psn(;ܼy AR+ivN$I¶yގ=S_S0&I$I$I`l?᪫.$u]/nCIIHR&{-HF[KT* 75;Ijp$IykdkǍqN^k $I$I$I>CYm[KA׵Ɛ JR^ 7-]%+NwG[KTvNRvF[Qm8M$IR5$౥7:M0i$I$I$Ij3τ+hۚg/H$IR׳v-Zn'F[$e澵9Z2k4I+wjm-$Ij}.}O NӚN$I$I$IRm[FS/ 7MI48L8#EYN^m2#ˠ>n$I$j:m.jX {mP$I$I$I1pP\ܶu=7MM7c!Nj%uC7oK lhEUp)hkf^4^vN$IԤ ~X>vL2e0i$I$I$I=n=$u ˗âExbHRw"0KVG[ڶc8M$I s# |6ei$I$I$IJN :kw=DR. l״x>hkLWO 7WfbFIW/׵}cR_$It+:Xvoa8֖% I$I$I$)u>1nڶ?_*$e㎃!CE2ij\ oo(Ā;glIbu-\j#K~I$IRvxobrl># 3SW[1&I$I$IԺb۾?o|꼐XRa͚ps/0Z$)M<b!/zauD!-") V}B515I$I|7᧯mk9pԖ I$I$I$)ݶ{1ش)5I>a?m-]6D[OqsHJY򥰽k(%I$)smG-PpGOjb$I$I$Io|򕶯{u8(jh 7Ӡ4z$)vYyIl68ܼwj`aUH|Սep{H}QSahAk$IyVl IDAT5W/+kDk'/i$I$I$I7å}ƍAK}Mß Μm--n렶1z0s0Ӥ. 8-xrk֟>z禶.I$I!tF<<ݑ|i. >05u$I$I$I,VuuAoD"uIlg׫~zHR熿kwmcP6C>>v4D[LWg;۷B/$I$r<nx}mvIkVX >~H͞]iI$I$I$)Z8p[?{6\vH]-%e_ŋǠ0z$)E y[U6¯G[hͭnho&(I$)5$`vx=B`ZؙՌ9}SK$I$I$)zpm_A[ocS_r=~ !IYG?<޽k`ط_u9Wnh >^&%eDĀ&I$%ޫvc}%$"Πᔛ`B;o؍SI$I$I$uBxqooރ3S_̰l<\Ӧ#I舫Å^X}ᴢ8;^Ս0k\3"$;[+`Mm_.IJ$P ADq/x>@<t̉7cozAn՜V5ܦ$kH|=iH_ד ɦv|C֏w7gGk]'~\1va8M$I$I$I'7n &O+ m^ }DS ܹk~%Il08X羽6T, p\9<I?/$u@U#=xpc^))$,k>6Ά`4?ͼM1.79Pt́] ryݜcu]ᶧcM}ȹԝ5́==vmk{[X0XXndv5ḯaCnA*~%I$I$IĉÆ m_ կYg>Iѩ_hs‚sg1>'I]ő._=(R IJ7 k-\[}]祦nik;--|c\?ڋ%E*`Gmͥ*[fu  v5 Mᆪo 4}5ymxkףZXk{9kyu|=m؋:}M&yq Xceb8pWWJ$I$I$IGS9'նmß %u\Ư=UxMOEh_RЍQ{΀c`}{+, G_W*]?~)nK^e#]xhS 'YyLRfoMl7!چtWf`*h$^pgA&zI$I$I$A'k/ܮ.(mm^k錶x1$}b)I]M,ӯ?}9'>Z:Nd=` ϭKWÍcKR8oougF{*LM]:\-5 4t&IR0#0SpyУo I$I$I$){᠃bٹnx7uJڽJxx ;ן {HRWpgoB]nWÑ#+= ~C|f8Od> p{Hc r"V6մњx&IRg4LTJ7.t9$I$I$I58ؾ}{w\r \vFIAlxyWwIR6+G\n_W”PiY)5.w 70')U7pzOwM2IR" [w~0|:&IP8~ʎw?e$I$I$IR8ԠYghl-8(uGAwg :#>q?>}/I{`G 8gJo_x÷WZ"u$<X숞9qp&)UvBZw$I٩/gA71 $I$I$IY^~f΄o>V>|Kпjd/h=tGK?jӦ- I.%pw7Z8| .T^ 7of8JJOl[WÚt2:/b_i4KzGp\Sa4Ig9y0@qç vHKi$I$I$I7? }]t]ZLCKꮞ,^q!KRfm5-A;|g$I 6|ZF:ryE&0&I$I$I™g~{pAHo*ed-jpQpp)0lX+?|ߛwafEKͯlo,&7TRJ=[NwtH]6W^,s!t̅x:o9y|Þi'D4A.牦ZmMc]0$I]G<r 'rr 5rwy\=Ð7T$I$I$Iن =(\q]jkWzHO*emZ=: $k1 | ^n`|ˉTaTȯo%ukǫoٯw.h\b4)4&aC%D[SάU,=PYaiKqAVwS K+Izڊ;L 6tn.]!T>.| 4; a8M$I$I$Is` c7 .>CjꚒIx 曙-'Li u󮷒So>\̻7[j౷+fM_p.([Pju5ԅONo6niRR+UBC|x CK5z?AQ`B%V_<6Tj^[Zm5xd%>:XG&<j / xE i$I$I$Iz~3gwl5 ~ۖZ))UJ+s]SԽ# aڴ0PE.I٤8u濺YYlht_pށ&d6Imo6EA>Y9GnrXCyZ:ml(K)&SsSvS4Iw!N$I$I$IR9HXn}~pppAH4%JK$́ ;Y0z haC;_HRF80uy.=a`quMۍ&ʥI00ڤlW߬Y렲15{cApX=%F}Vn h˶fs ᭺:$Iji$I$I$INppyA^p]AHm ֯_RmƖ0ڋ/BEEzͅSOOɓ!''5Iv/N!7½o,}6m %p$mmR6Z_tI{hhHݾg~YdVMVmuGL%A9 yIT3&I$I$I6iRORҩ;Y` 'a4: k-K]  N8zNwE&cwnƪڹ+¬ pk¿-{&B, IQ_ ^-K:g,Lcv[k:;P]-[Bhh~*Iԙ I$I$I$)bpя7jCvؓx`!φR/T'X6=r/rsa vpAG1g N濶􆃇D[W*}{,߆Ej2YC YoUvƱpZmIwE{k( /&B$Ip$I$I$ICkwC]]j^ n~8 6yrj TΝmŊtWÆ;LI:N3 |1ǖ^08RcaY5iK5/KOy&eFx`lJq礲Bhh H* hʃ@LTMǞY$I I$I$I$>~ӖڬYP+kk AH3$5{Y;ڜ9yyp-Lo=hMVn~}}tD[[`|xyGuoU̅pǾAFʖTo6.Ły2|؍Pth0 6gT$IR;Ē='dEǹu^VVʕ+۽$I$)l=TH.tW!I$Y n'u!֊Ӄĉ_]GM rKwի]Q<`<$In;6-f 8oRt5Eas=Lns}`7"P^!VqH%aV+\.w]I  hCN$I٨V7۳s$I$I$I2N:`lhhH55C vPhn,s1~4ɶ˃#l z$W~ |n? ízi㢭-`C]u}O,&oko^ . wo+!r `-!aI7*IUN$I$I$IR1z4}7|o~ڐo?8,9FL{(s0o?0dHӃh={"IR&?};)JvG7Cu>ÊI|Æ}`0X1 9S%Ii$I$I$I~ƌ_%vИ 4+++!=TUy-aJ}=#SqAH zO5_8:RpА .| w@U#/l6|ȷ)}ᦱ0$7G rxi-$Jn!O Ɛ)E$IXD$I$I$I׸q_7Cj0k*|sJ٫^{ h B"eeASa Ia\f5[ 911*Rpd1$xÂJ84Os-mA(ႁpb(MJ6ކѽG, hNQGB%Ip$I$I$I4~ю=[$)L\ԶuO-85"p@Zg[۾ XYTtHVX{M) ]00&)=6U?V ИU Ka̱Am-I.p$I$I$I @vA8?{-[:w2 <$|Kpyδٲ%5wɬ0ZI ̘H=:I3a|xm[IJã+ G SayM/ .y( Ͱrgho!J*xsSjgɃƟçAZhG Hv34okۺ?>2,oxq* Iu5< 3jJxb QèO҇[=-Irz SoIgA~Ij$IRg8M$I$I$I f …AHmlضc.[_{yf^u୷?cGл7pz*|2 ˢ%I]YB^m6WiK``>}2}{C{Fu/rx| wAPb%)n Bi+R=l,_'~}$IRjŒ=}'Yсq.rv'I$I ,[=*R K$I55p˩ٳO8\8|0 5{v׷Кhjb1:5r ~8x$)C%ȥ0箶 .J(xߘ߆t|czÿqEK{vxi&:}O W'U]#, Ͻk*RwS$IBjkkun4I$I$I$3 wCcc,/;Y҉'ŒAw-A"kҥ-!Lppp1pQЯ_$)X,=Ӷ[y`|HKܶO,l)lo^n/!ޙMlWvA:KQ>58M*~Ix{+_ @}~"p5}%I]9M$I$I$)C=,\x!^=srC`}!TS+WŠ|yp\}j;Ɂ)S Zsp$)%07m_ QՕi]\1. ~N ୪h *`Jg:88kXEJ$r,ol?̸;-Ini$I$I$IR: ,K.?!5{66K/ 7ƏlqcK9bl{fͅh Gz*IR+OtP[۶M&/+Nj=$3$<n__{;&+ƾ=` 8SW20K;`{CPV >K6*a =S>$InN$I$I$IRo_x1Ok.`㡇 jaI` () t ش 6on6롪k]sgӃgIx|o>Ksa0<]1 NZsvt|%+0wNv&`nEF{~;,OaσsյP**m)~>-i =E>$InN$I$I$IrUp9 mc_Fk8B-y `k9~zy ڦMeKPOWQPFGGtW%IRzsXho ?  K}}Qٷ0n\ )ZX ѽpLo(nYXZ^*.Msp 8/H:UU=і߳'v9P2($Ivp$I$I$I#ڗ /ܹH@yy0NQ~8{lF;0(,LwU$e<xAJ IDAT0޶oHKa 64Kpfmxq{jmL3ȋ]N ^^ޕQ;aQ5,j)//gNѐհ 6V':C0`7$Ivϟ^H$I$I$I#_VJwEjV\ ӧtF6 ]$I-' O|۾NJmp۫0u0<zfCz]-fJ}-18WTz{WJv Uâ*LcY 87\8]u5&`sMBk`K $%`8?!_$I b!I$I$I$uX f΄΃[nW^IwUϘ1p!pp1 z%IjWCOŽ5m_L«pl5r㩯3 g;+ac]joHsۃp`1  SJY)V0jʀ ZkAp@ig_)%a{-TŽ:Qm*##\K$)N$I$I$I:[n.~0^x~#xQh̰_ѣYsOtW%IRR6| ??Ӿ=Z8e,405F%76 >=n~~^4$̫ ]@<{ACzԞP`$uG ZM"ݕޤb8?\8+Nw5Rz%PFCj3S 䙐MU$I$I$I$INӧwwy]Uv*+k 5MwU$u%sA=߾/3^3*%9ph||{ZvI$᭪`^1`\ؿآ8(?2YMº`ozr',D'cxA+R6hH@u=T7@M}!8lsG" lj]^{ybϯ7Ś<ܱ/|sܱ\ $vu0Z+i >o d$Ul7!m 2^ 'Ku'ǭ5Au>pA Ȗ$IboSObŊvA<.yYY+Wl~$IpS"n\%I$iy7;7o鮪s &L}my\Vx$I{ec7SCQݏ.Wv1jA0~y#:]IaGc5|6Eە3+j6IwEJD6VÖj(o 5w53@ھAT$I١gOj$I$I$Inh`| ym-zKXmX{Y ZN$Wqo/#_w|Ƿ~ژ>+? ƜpFMsؤ!ړP V80x (,MwE$IRN$I$I$IIAzh0UW﮶alu{8*yy0x0 2}!0p )B.)|r6L:  l[=`C0!CЫ{G^0`,b^K]՞%D0&{S!C{tG;WЭNOΖ Vېԙ za̱082bv$IRb8M$I$I$Iv=zUUURۺ5{|p x_?y$I" 3 xMP[}V>}>PD ̇*xh#~U^1`\>7/U)aY9, K@En/QGA1Щ,$I2'1$I$I$I:8#GIԝ±߀W DjN$a`b0FBYip]ph~4,BjmL& S{)%Px x StxT_ 'Qv ?~>$IR⿀%I$I$I$I$uw/ IX_ϕ-řuBjAG6 hH) \!{.HZX%0iBkD8 zeI$N$I$I$I$IԹL/< ' [E^ka`^04Cq̇|ȋGWKXck#Q/nnnWvN;3\N &h M K ZkL?ؐV?츛9o~Vsx  ={# ZHCh$IN$I$I$I$I΁}On}vn=w6ҭ؝ kh$/8& *cmDphܥٮ9=b@p"p\$LIkF̃0.Ø|(buP^hq[ӻkmX\LBhybϯsYט _C,WXy[P!A@M$IRN$I$I$I$I>9p50bM0wܖzjԤ4! m65Mm=ԕF.@=P{'e<(E}c?/xn0r89?p9y wX$IN$I$I$I$I~79wiob&zZ$de\vIM rH3:}AAM᳒tW)I$3&I$I$I$I$)sز,=P.ݕe\`pNZEӨX4S*m7B>:k~ܣ뒢ρaژZa$I I$I$I$I$IL7‰߁%OԖ<NMu1M^%Q!ƚƞi 5VI]Y,aC`0t 䙺$I I$I$I$I$Il\xV0* [Iwe][ )OaS $UI$IRN$I$I$I$I=zccV=@Cm$?L8=#C<'I$IRd I$I$I$I$I>>:uznT[TmNoB;%ֻ,I$IR1&I$I$I$I$)C`Mjê`QRC0 6x+JwU$I$I$I$I$I$uM&jCzk=ޑޑaJ&><ɽiGȚXl߿X;wd^^ ݈BNǻyscq$I$Ie8M$I$I$I$I !.F$I$)yI$I$I$I$I$I$IRN$I$I$I$I$I$I$4I$I$I$Iٻ8of22FQBD367eiQ)kQ(l,%dK6 c9g1|<揹u`iHNFr404i$L#9 `iHNFr404i$L#9 `iHNFr404i$L#9 `iHNFr404i$L#9 `iHNFr404i$=##p|nR~܎؃4G{KE vƙ*n{TI(\v^$=E5x܎5j(HN{X+rҀ&R~܎؋4/, j)TјSԲ3$p?xyJjK-KIץ܎RR"RrRd:7>R,]&%rZ<V/wcAr yHJv~7{HNFr404i$L#9 `iHNFr404i$L#9 `iHNFr404i$L#9 `iHNFr404i$L#9 `iHNFr404i$L#9 `iHNFr4&q ðU\׮]j*I&_υ礦<?v16@"9 Ζ-[4j(믹 =4@HLLTPPΟ?o>""B 48222ԲeKV?u)9aaazWۡHN䊂 7s=g~ڻwϟqL2EWZWNu!GY-Ҁr; B-[Z;|>̙3=zպ|iʛ]%/N>~vURRRTX1N;/^ׯ_?C-vhp0`mqr;4ءFV??[=ֱcGϯ:Ci\W_S||E+H߯_nnRJ SNU\\պy*$$D*URٲecu9OOϜ \a|9##GrKMM~伸8XB4poJHHz k׮YRSSs!w[~&''B4.lٲz5x` 0@~,Y%KXPƌ+--MV{1͞=[O> ޖ7^S``պ7jƌ.zWlO6M.٣[ꫯHLpON:777f͚JKK6lڴi#???7zhEGG[۷\uVuU j{͚5JII(p]դI4vXr\/_d2eB4"9 pW^x }u4h,Xt?۶mӔ)SZp۽{rW<1\rNp/)]J.aC=!ʛpӻkN?SiȰZg۩>w/Z-jǑkxzzꫯY/+>>'N{Z VhhmnqqqVp$wf͚{V뢢4f=qƎkPB:uC ׮]Z^`; 4]gҤI*Qպ/B;v0K/u|ʖ-kM#===Cvܮdɒ8qzeQhrssyZW_}ա8'O󊉉UdIOUTQʕjHEGG… x._ K%KTʕUJ.]:HKKӑ#G~*X|||˫^zrwN2 Cvґ#G2?+WxGyDw~^wmٙ3gt%)RDzgcviӦoLL#???*00PʡCtȑc{LuԱ^~=z4*99Y~~~SժUSOݑ;رcڿ-|e;|A5h@jGѣ111*XJ.:uA4 C'NЁtQ]zUqqqJJJwye TV}ĉ/p PFTZ5NMM֭[u)]xQ/^ԥK/_>-ZTŋWŊUjU?3 ۮʕ+nݺ 0ɓ'((P@Tntvޭ(:u*s/s[]X\/99Y[lܷ(..N%J<.xꩧ߉}رc:}9N裏f͚b7nh׮]<HJJRɒ%U|yuY vCi߾}j71o_˖-:u(_|.~UӮ]2?hyyyL2 PݺumNx8p@g… *X|}}㣇~XuֽƮ߯'Nd7/S6l5jv0!==by[vmUXtRSSUxʕ++00;OJJҎ;2ӧO+O˗/+66V7nPѢE3)7-ZƆ;/##C۶mӱc2?cccUD+VTs;TܧN>yp*!!AŊ&=CW ,Z8x`5(%&&f;VREk׾op7{F ={*""Bk׮۽{&M7|3v^_jL]ŋxbXBW^vr)$$D>jժew_YILLT>}͜9S rI?={TJJEĉs4Ubbbh"XBׯWbb]UVjݺ5ktrr~w[N֭Ӯ]j׺ŋWPPtΝ;tdݺu (\~mhM>]ӧOWLLLmWXQ:txjZxV\UVҥKv͛UV bڷo[-]Tv"Ey Q.]\vS޽{5~xr}/_֬YW_ĉY]lYuIFRM6ydEFFZGEE\~ȶ޽{EV [[.KغufΜ *666e֭^xA]tQn[rfϞmN͚55d㉌ɓ-yv޳(=ʧ~۷[wUm۶(߸q´hѢ,͚5X~-]T˗/ IŊSppBCCզMS}^ .(ϟ?fΜ#7|eddo߾V?N:SNn:-\P˗/ٳg]O[VǎmnK/Y=|##GZ 2ԍرc-ʋ+ڢ… ={f̘#Glwذa裏#'%$$h޼yZt֮]kdնm[uU>S}WaR7o^;|3dEGG[#7ǫo߾VOnqErr,X)Sh۶mY]xqjJcƌQ y}2eٓ岥JR6moᶎuUdd/^˗._pa5o\;wvyh||ϟ0mݺf-Zʕ+$7+###oW잞Wڶm}ǏkԨQ TxxE˗5g͘1C_%66VVXkڵkv1ۏ])99Y}LB{zmԫWቚc}'V&M$էOeddR'O͜9qaÆ\O]Ey6mԭ[7۷+,,LӍ7l[fMSWܹ3|vy///+$$D;vtٍ7o:Vj„ [nմiӴ`%$$l7_| VϞ=վ};PvZ-[L˗/ɓ'][ZRhhZj4l0rwww͝;עիPXXXkZb̙sK]ZZunje|VHɣ/2x ;w΢QFzrϬ3;p4m4͛ʗ/.]hĈ9-[dm߾]IIIv[hQ=Сwҝ;w?(/]cǎiڴi5k._l]wwwhĈ ;cǎiĉZ`AQF6l6mjwؽ{{-_\G,XPoطo_6iƍkv ֋/}Qll^|EufͲIJJҼy4eܹ3˶K(֭[뭷rhB'OjĈݻ겳g϶z%+wev3rJcԩ}׆̿ef%a_e,X?OOOٶѯ_?K2 f*͛7O< 5}K2ʕ+N.#'#G4<==$K6~mŋvn/?oxyy9$r]M6j?O?k3fn:怀c.NIKK3f̘a<Nn 2g8p.]d 0ț7ñTPXzKޣ+WZX/0 (`:ޒ%KcҥK~sM!!!7ovj׮k_{mM0;f={onԯ_ߩaPPw^ӯ?6c]dIj*}޽ۡ6OYfN}65rz[>6o ðyL?jgVetw1g9tPVg,Zp{n 2ĸ~1|[;{ѰaCc?ܹsF=ΝkyQQQ{n^zNmڴ){:?zK7 c7:wl5ȎszWE.]>}X}"ϿΝ;UJgpႚ7onuTX駟Ojǎvc4#knܸ޽{멧vF_^5jиqjUVj)[|Mijժz{TjU^ک~WU^]o]ߋ+WUV;v0\ҥKj޼:ud )HKKӧ~G}ѰaCׯwM6eFFFZ}r=~7^#>S5o,YIIIѸqԹs;vx`5n8˙]ڵkСFxjP fדsM8Q~mĉլY3]~:wlz[-z.]j:ܸqC-Z׻wo[[l޼9qm۶tU31ry/9&5nܸ͘Tnڱc|I[Ρ׬YzS"##UZ5+NbŊ6g4eddhС޽Cc{zl36lؠ5js G}TN۷OO?yQ~լY׫jժ\k=zPڵM_~EիW_Egչsg 2$kkZl#}B|||8w}Y}gN2qDթSGSLF9qvݑk4tE5mTFF&O:<Ξ=>sheΜ9jժҶdddh̘1Ts{}:.;CufS3xbƥ:u5jиͱݶm:tN8ejĈڷoSmݔ'F.k^ӨQ#=CVrjnqƦR+00PSq-_\+W暰\=cǎ٬|gǒ6mڤ *2F3bccVZ:uKڴժU+ג4vX=s. 2DժUZ?ۼa(%%E/Aدڮ`'%%]v9st_~O>qI{FR߾}MvͮsQ˖-uC짞zJ+Vp0ԯ_?sQd9gɒ%jܸ:ۡd1c{9n>S-r&$5j%>#烺EݟԦMƚZ/cWgٳ]v1chĈ.iY}ZO?ƍbccdu}1Ֆaz;8sTzꤏ;ҥK 4\7R(((:{Zne˖^D[/11Q۶m3klNOnϚ^xAoӝjgҥzg]ӝֵkW;ܹsmns[BBڶm?e ФIԯ_>Yf:uU'k6oެ:u9և$驧Şiԩj׮N>hxwK/!C8Zl}YS~68'nZh՛lԳgOW'wg^JJC7'$$hVi1?ANoOcbbԨQ#8qEIVR&MtAy5jȡcֽ{w\SXr:t萣c؋-R&Mcjs&M?qpmw^{]~]ݺuʕ+]76iDsuٹxZZz%Ys թStIn7:ڹsKTΝk{rwwwiruխ[W .t7xC_}Sm؏? ]p!c/_W_}բdol޼Y.}k׮7nKuũȑ#{1v|8pcSO79t Ѕ9uVʓ'+fW$}G:t]P1& X4_|֭dddo߾vߤ'Oy{{<]XXz ;tsjn,v5l}NUhQMNNVǎMrobbddCsn;_R%nv3fӝnŋ+""e |ϟ&sR\\7ouTP!5kV=w]|TD yzzڽѣGվ}r?M2rH 8ԍE{*3g_,Y9sZ֍ЫWֹs\V&[ o͛7KʎmЌ9ܼyyn~`wMOVnݜ~M111z]֢EbX]UxqnN$w̙35o<*[joݺuӧO{>}Zwh IDAT~lw[Ŋ]ˮ^Z-[ԍ7n^ڵkm(QBʟ?s.;wN 6# k>@Gv&D^{5Ƌsky64ywfkF߾}!C4a:+*Qݿ 淪v5G}ɓ']g`9r%/[fRj׮,X`:EZŋաCno5a/Pʖ-y{{۽OϟO?e^ڥYqss1k׮cǎwiEw޽{[=v/^Ҿ̙cNJJ:wlXo޼yMݓ0avǃURE%J(tCǝ9@~9c&Wŋ*&&Fm۶5=)SLqq L?i5kW_ɓ-.\Cj̙e$%K(!C؜HB\&MԻwoOnnnJJJӧd͚5f$۪VڶmkWLn\ZD iFm۶U``|||29RRRtm۶M7o֪U\Spa5mTO=UjUy{{r9!!APdd.\M6loU֭sϹ4NI7nNԸqciFjՒ+K.鯿ʼpg͸8wM˖-\CM6U۶m$ ]rE{_U6lpjM6^drԫW/uQ=<== .h ʕ+m&Gl߾]7=lv̙3fXׯ__mڴQ%Jի:y~'͙3biii8p]WF,?EGG[OO>d֯_?e\xgϞLRԧOC+WٳgpB͜9S޽{ߵOI-)))֭՛ݪUvکRJN>SNiڸqyJJZh  ֳ>f͚Aɼ|uܹS/Vxxͧe~'jܸZlw ///uoѠA\Wxx֭[dɒvl릚5kw <|)55UgϞ?,p#""TJ 673gδbŊM6jذUdI]|YQQQڳg\Y0 =sYT?~W^YJ* QƍGJҍ7t^Zs_eAiӦv'F$0Աc,ρ<<<ԩS'h"srsrxᶞƍH7΢@j֬7o?Q||N:?Sw R:uիTR|)))믿Q˗/yNhͽh;̟?jzѢEժU+5nX*U\(2ylo+RZlm۪^z|t%ܹS7o֚5kl>{5튎ڵkv 6L!!!vOԩS'OIOOWDDˎU? 6Xˍ#Xܬ-[(((8jt+VޠeKҥkܸqIØԨQn7ܹ<1cƘjћRSSեKqպuknZ+V˧siZp-[fs[j*}رCqIƍ+WVΝլY3UR%݌ h՚?ͤׯk׮ڳgpOz'ԧOuAKμY5!!AfϞ LDZjY_9*##CzzbJԾ}{UREԙ3gUViڵ6W+VGU޽վ}{-[V >lذAZf>~7 4kM0d}BCCգGծ];30 >}:slСCVیWvta'KNNVfͲRHYf_J*u۵dL?~5jgyơ8)RDjҤIqfllo߾[34ib1i+bu3fSIJ(sssS^Z͝;W&Lplݤ*UW/,'Ou֪[+WZ|"""lN𖘘AtR I&Uj֬bɤ$>|XZdI۝ui9@_,jѢ:u\rssٳgqF͙3'ˉֆ֭[CCx"6m_:wvءsj6>|ڷoԤzj׮x :mbb8Kj֬Y6~'M8Qoq]~=ˉͫ&MO>Q,ƫ ǵgڵK˖-sؖ[}Bst3mOqaߢGQvԴiSWIII֯,=Z:urD\7AY|I.]@zzΜ9-[hݺu7o}n\\z_~Ů jԨa[Vhjr'e銂JQ`Xb=ƒDƒ{^b-T%ĊH~/s߾2؅9:ܹڶmg3'׏Z[!!!޽{?#$/M6!C@6m>@333!** <ׯ7o :[QFRAӦMMXXu=K"N:Pиqc uBڵ#YYY3رc_YJ`ѼAVrUbjժA.]uA'O7R4it/۾dNW^D>Lἆ`0 Yp8r|r+W_ 5`0 \r>S9sg7o~nƍ T**Ã;uA9Z;w.gee*[,/~$&&֖ƽ{!#GV7|%''{ nɜ֭[_;a8Zuܙ۱c&q7n֭~7GGG+UVe.]e˖ܭ[ ʌtWRqO<_cKTRhҥ cQr~_{폅eeeu%bŊoΝw$Yŋ>*]6w92#Fٳ?Q;$̥B DB_Ӈ3(';;3ga۶mD9 61Μ9ClK,llrqq+ nܸaP[1o}M&w8+[,[Q6h"""q} c xuVD,kٲe.^(ZFZmٳG췷+7G,Z䕙ݼySדҥKyߚ5krw5('--3f rZVp D3{lQѣӰaCm۶ >>>O2e>;yfN,Xoǂ7{n2322~wzjIիQܹs%[p!GJō7{ YѣygϞ3=hɓ's2+W7n 300[rC^^xuZ!qULKKKdɒssyeଭyaÆq_==z͘1o/ݻwƮM6 .\;v͛7O5jQ(9|6 _Θ1CSN ?EצMeQZ5KFFD 0tҜZUVtR111fڱcj߾qJp&L޽{gP P]pjՒߤ$ ]bEn\N8ױcJ+m0^~]F̙#hOΫ,Q#Ç'#Yj֬]ṗro޼+OJJ<==`ii/Ǚ3gxew1ӓp5jtM2w-?yd}eĉ\F >kz%?9OKHHտ7oej=}-7{l2Ҹׯ9-Edd${8pJo߾EK.҆ToN엣޽oԨwU2~R_~僌6mp!!!x$=zĵj ퟍ %CA۶mG~zoHHwEgI&lէ.\PҽฏФ ^p~-Z[[sw7>c``hE.}u… %=+o޼>sw!r9˕+m߾ݠ̬,_C/^,ڵ#%c0 ÜE91 T8x Aӹu֡iӦvҸJ*rW.󜍍 *s1^zŹE].\ppɓ's ߿/mQ:V$sNW_ 6C]~gI5ZPBFVVvZƍqӦMChÇCrr2נATfҥ|`iz!ڸϠȑdҥKvaCܿ꜖/88XԘGٳRɉ;y$SLA-t:t4a*m`usiݺ5AF^?޵HFD>>>gqd矼dea{mۊԩSd`AbtL$Y^z%{35h@x^/^k֬tNsj@ioo_8|{]}FyİqFO>¨syyyvnIMMVaooϝ8qBv֮]+|iyjԨƊ ae5Dɺr *ˢdt\ cӦM>hB6 {t{`99-XTVVNj֬YTL9-hOrL1y2d*M6tќQJ6wP)}-JnBBWzuTfEsN7f.;;[*>>Q*\r\RR(,_ƍE[j4ۢq\5p,j> ۸qDœyzz`98E6wNE>cmmMP sZ!Cҥt:nZRΝu֍v횤jyu?$sZI&˧G 9p@QcSP=R rt{ʊ[n$ׯGu7,O?%ʨ^:w355[t sZ?___vB *HG8???n֭TO}rfŌG-oym9_xxlRyO666͛7ד1p@[a1{VX NʔT*wIߏoEF(kn|%lѹDsڒ%K$~ o@$7BF)HŋZ}9GsZJ$e8`f;w%Kբllld^T4!ie˖uC 4yJNCb}!i4nFnnnAQzzzJ4k*̙*_n$ }*o͢F@ӡ~4u)9yzz eOVVu=zei;ȖJt*Qj o9-ڶ8q7PDE9Mբ~blHxk׮Mի`^"BTWcs0]Z!TsZժUEgh΅/уDci (wNS`0fŋёXb '͜9*T +WuWOOOArӣG>|88Xv$ED?u* oUTQW9XYYQgaa+VׯÝ;w-t#L:XC5jh?ݻwW79@zz:W^0tPIr]\\ $$_~=hZII)S֯_X7oޔ=eÆ %Kŋ%9r$4kLEzٳe9~8,K!::X7drVZOP^=]֬Y#Inaa^@IKK͛7,--a֭`gg'I3QFĺwݻɩX"Bxx iii϶h7oW&H.gIrbaa6m'''I믠Ru2zְe(VWb9q~oDDDٳgu3g΄O?TYfA͚5u:,_\̙3֭KM}%@>bŊzɰgQH1hZXd Z|rhܸ61.aÈ۷o } ^:ԩSGl9+W쥧˗ ~yyN̜i)1qDh߾k;w :) Lw57{]vׯ%V-[¸q$] ,@ׯ_/[D*W K.E1]Y1BV^cj*(Ukg#>|Xva$-_\ܽb Zjj*~Zj[)ӽ{w(Q^yff&l۶M;w: J+V4n?@WLc-ZD^xOޞ7̙3 ~tҰj*Pś}o}Q={'}Wп_ -Z -[s?;;;A} IDATJ!!!*YfF!I%j4t zI=ѣGpEYw z0h rΨ-Z$ٶO>/gTbPФIb3g1f͚ X19F"t:z'KKKؼy3qm-suy^`0 Fa91 ìpuuy扺f͚0i$Ar DDDZn ;vv~~g'mذe/J[RRRu_|4mT_BҥKpBݻ7ZcJru8w[{faee iӦЭ[7b]TT=zT̝;%_ߩS't4\t ߿O>}d\ϟ/βe$3Y&9Rŋôiӈu|J4bB[.T^]7oޠ@_J7 p!%\ׯXo۶ ucƌJ*ɒ79Ɯ]x[hA4pཉ,_///zsY[L4 U&Z* \rTVVVQ`TdKKKc_٥K탧O:t X96l(ˣ׋ @4͏뒔Ǝ kז|?6q`0 s91 >|͵YFr'2eQu^Rœ9s2tԉX.4cPU&Ka}ߟXg hKKK{?*޲ۘ1cZG+3!lmmЇ&۷o'[[[.[~zdg*4iDrƬ<~9B9s&$:+FFF³gϨ!,CC ..NL>i1YӶn݊M:Uќ8 [l%\0aBAw _e DPg=%Zz{{'QvJJ \v͠իWu4Dǎ+[h_ȖӡCbyA݃x4;өEpԩс8>>nܸAJR ((.e9L8,w .҆105ck.HKK+h40`ri!99X"ѹpr333ҥK:S1(*(Uf'ԨQXg󾒌1B# >NTrJbpwww*z\ƏO, EM-ZƶBQTP\\}φ0\|X޾}{1h=GAQsbT%Kμ[[[ྠa^yVVd۷CFF^5*?h{ݻEqWjU*$335iժy+t/\@f᷏-[V5^^^P\9 ^z2bk-[R5k$=8}40DvZј;mmmG(Eٲe۷sTPc1dddxWvƠN:3AŌ?.9+f ߡCjgŋ298_r8CŊ ^_29TqQo1СC=zPsX1>#~^tIT7ի9AAA`kkKjiZz%tM6wwwb} 5cQ9wyG!E*U ڵk'+V 4jZP{{{OuBioooQ PTгgObÇݺu/ ;W Y i$4`h%>0[Æ yL4T*N366PR%o= e`c?xyyI eN+]41SNѣGSc=t:HII H:怃rFqMo߾r׮]#uܙJWurQ|Hvd0 1o`04SN=TYt)1e8ƍ7PJ|!M0ШQ#{CLHOOG222Q ++J4 Ԯ],i ))|aو3hذ!UYB tcp=b|<ˋʗ//ʙ(TdaƈV)oܸA- 6X:abܻw*XN\\L{ޱsm:j*.(QdI1J̡0fnҥKDǎy :>(fqT Aq $36w;w?";;w_NW=xu_oZ҆15v9ыj54[] AAA lN2DvUHKK+7dPtE:l9#4uRػ q Bu8/_&;jQtغTKTPj6J2""Bل-5G,˵Rxd2 WZ 943s7DCW^ѣGEz-8pX'FV3,1,u {43D5KKKbf+m۶h|iJ+Z1) @ga;:uDEEuYx1NCTݻ&Ti`g1tIĠ'֥Y!1=S[ຌ`0 i04`-hmzꉒwxWF Q KII1r%byZGIVVÆ  @Ŋt`ee`kkR游-YcA;aϏFSIBB@Шmb(Wlwj5ȶ's1 _FF___*mSSoK,UVE F>m۶,;aus0666ѡ=3H5'՗/_ʗ/+;))@U4DS Ս} A/==}ߘs{hk֬qAfʔ)`iiip_4Z6θCٲea h.IJhw=Q:6nH߿?1 MzO>4ErNKKuf"fd`i...ĬR(J:-t ˽=y1=5 V‘dS?{ ޾}KSRWJcg9ٳgM39 QT)43X֭[!33Sz-{booA JjpEX|95 RJ1VVVc9~ؠYKG!ed%8Ak6uZbbAg]uk֬ *ĉe1-ңYXXg4/lٲLڵU;+V 41~#5u}h֬1C4U&*vH9N3ԳsST֭KISU*w܁ 6ĉUV e˖{{{Az[mPtP`0h)0 a*~Xq*@NdĪURmC,Tׯß ۶mC咘H T(LaeR '7oޠu... ^ҫ#xbc 0 OOOFXUT18/JxxxPuw{ oRzl0EÉ[ly 2Vji&bEak<eQablϏz̄AX!cn3Ϥq?1}ժUEk˗/3yxx@ H,--lL= &̧|ktpppҎ?f۷Yҳ @gTj{)lذ6n܈ ͵uATzoj5X[[a)c7j N:WaO>f5  [*VKkDᐚWj#Zsfol39_IL~c{j[[[5oCe%0g$[GlZ+-Q(wVOT^Ο?W9§uևL"tW{f! >vܩW~x`-p[nPD 9]T%Τh߇?lb} ߝO@ C: C$$$g+/^Wsu47b駟~w:vAAAТE 4:F;{n !!!T20`ذa0m4>} _=֔JׯO|_| ITF%F7Ǐ];rH܄u^y~7ƍaƍhP]:/a0 ! `0 R7Nussxqqq0ydQBEXHMS|~{c͍&w) Ϗ1Ic+itn:~CSiCrػwo?~7ohd=z5F2d(9Ʋ5^|QeM 71F&Cφի!Kbb"DDD@:u$: 7ov,44&OLɓvN{L~PZRSSyS؆b0g_hBk_ @`{Ϗ)Ç':m߾-Ze`F5kք]J*UZRSS!<<7;; ?G %i;FL~c낻wB߾}뇩JU Z'wJkZ2eꁧP_cǎ'1x2| +WNa633mƍ3(֭[h6we fǒ (M—$e,xe{{{Xn|gzuKXz5^,,, Zl mڴM:LiKbƍ0yd3j;4u2y1%#2sƼ(hݺ5<|( 5iׯNΝ;zVVV܂FsL2WNrXKIIk׮kZpM622.ȟYTPI2߮gVCU]w6;;lB4hPOݻwM6U]a ʸ |l4iرCr ؿ?taڵ&~a|Lzz:t9R])2 6XcN#/O"!!ڶm /_t=`0 (`0E, ,--Ѻ,&W:FZjAf͠RJnnnPX1[[[Ti2|~k% 6 X%cdP"FQT$7*U>*ʂ-[kmF|VmmmO>(36c-IFժUggg=Ұ0]GeW^%)S|||v}UR7o~~~a_g]tAlL NF793|pXl^!&&ʖ-^˃$Ylpdgg`H'&ʔ)B >*~:qPT)Vw`0 3l]'6LSퟕܯɽW IDATJvt=3Mʗ/['Omذϟ^{ax5N<$%%A۶mylT*f͚xxx;/^~ [ׯ_d0qML6={B۶maΜ9fNvQQQ0rHXx1_Lg58 7oj''c  fMPP«W>*OJJ{ !fyrvvN:gTO3аaCһo/^'O$r\ceep)=+ƍ>JuNѣ*3oooh޼9;,YØ&=z4l߾]R `s`0"55z[| sj GGGO:999 `ر$I>tEGGGx^yA>?|rc %K\b ŋ˭e˖EA6lL2E|Æ is׮]%SJkN(Q^|Wn? %$$[f͚ʲŋz2({<,, &NWFe}a/2pvif{\|9ܽ{Xgee#FQEAVqmJHZVZz(Z6mD\Ĺ 'Rzuprrx ׯ_(jpy=s7@cǎo+#ѬY3Z3J *T+WV5jP-1c1RRRg$r;`2 {lllDZNÇ6o +ꐈϚ4i*UǢٳ!::XWX13f L07=^UV40J(s΅~ {SNWpm_>QG !]R~4dK˞={EVCaʔ)PzuѲ`0 yV `?J:s):B988QSio߆7UKqs,Xᇒc)?E 9XfcNA"4FDD͛7! @;wk׈<+8hFm415^A0:C4i>WsZxx8}Ёh~紳gqO14\J*mٲ?#7FAĜӌaܸqz!!!sچ 宮о}{ݣJ}>rN >_|s\-r,,,>j?ElOݶm[Xb½1=h;8qgiɽJ {T*=zTv֭щիWpQ һ&..:Dǵ/_A+_<:tHv[_˗5 :_gΜP %H$&&Bn <<\948|7D}%X.K>Sp FtN;uDEE^]hh(<}(AMeoN4k֌(?|RӧQ[B7qn ;w-7/lLb0 Q4-  xbrxZg*J!VFU*HT*zڵ\td0pG|~ .Le)j`=::ITTUyĜ鰃,U7+tؑX6c垞кukIXNG9㛫Mi5V8sM^u\R-ݡbŊd{nݺhf\ qI4Z͛e;d[49}S_~́݃+Wt:4 M>2rf͈ROZ**47Ltp93E )ٳg 4?IIIAMo뇒5Af:a͛7Ő5ߟXɶlYYYzгgO+r߿2225 ۷OcЂ/9!j5ԭ[8tñc`„ c8?~=e!&&,1 )|TRGo`so@@G`*03`g1.\ 4hЀrcH6#\3 0/s`0ff:!JQC:BA;z8ܾ}[|̑#GǏ...N ܹsGlxն222:SRJ%F?KJJ CԼr!ZsiC37ǃp`$#lؼy3 N1V@ sz(j` γ f\,ҡ%_4;Wֵk׈ǥK5g0Nl_پ}{h׮6޾}KE0ݺu#֑N<2dŞh'7Y.ͥjժWw ,Yy3l]Psyb򜜜%=Z||<~XǧOBFF-۴6lĵ;f8߽{w(^8ͮ9H,)!/&)Aom۶h"x!ܼyv{ wU !=zSNcCBBRRR`Lm,mR13*Ng6L`LO>h M:ҥKaÆzyRp5RӰuN58 |`0 91 K*UQ7zœT*ڏۣڴ ;z=iiibVʼwAdd½ ur>dggFڵARh$Ă=4#ݙ<ʕ+2)DQCPlY7o9r^zE#omE{'t5͢?س3HJJl/_Dɪ[.طhZpg XT*[Q,RgӦMȏ;+VXWN,}g6 ̨2F`FգAԬY')) """ @ސs7y<7J `uj2&0yjdMTX,--uJgh.EkE}m ԬYj׮W ۶m,""%?0c=z";Gd<==u/_V7 W^wwwg8={(3ؘҥKwDGŋܹRSS>kiif9k[S9]X+V ԩC3K"u]G à{J= `0 x1 ԫWqն0%aժUQeO~F;Ӥq/_Q͛7oJ4FTڸt9E({RPŦR@ F%bŊĺ'ORkԔ$NzsiCvp {cZXXXu 1&M@J$kmm-x=`5ޭ[>R[c^R8'eh4H=~:z 7+f\/%R'baaˋ܉9j׭[|sWbIvv63@V߽{Dػw/QAjhҤ . ֭[ B8{9tO,3V3g(ݻh))`{bWNmml]͛7e%11ͮArlʋ3lWߦ4@KО+*1S[CqnݺuV4ѩScx?9лwob96((7[!j׮Qʗ//HFQC"5z4׫!}ɓ'ŋڐ"Ǥpv`0 91 )SXG3X\\1@he~2r1֭[2R QSzSPn UN#ؠ5 )}wQ.;!FN^ޘÆ #:t۷o :TVju޷o5MN7VZh59\?5j>V-ܠrʒd}޽ ZQO 6w^~^~poLVKt[|E rVXPTZ+Q֎;ꬭo߾u ҥK .N!""8CSiii1 ZjE=poLT8}45y rnRl;/J('LO : !69sF/cjQښXܯ_?p5sdee-[`clTTh# .@LL TxrQٙ-s]:i|}8̋˚5kCCCƍu#srg{&u:,^Xޘɨ1eBBܹS%e˖"C%:%?~?$^ۥKptt4jh;HF?͚5d]Z5rXXܾ}/jsRP]Ov?/͛} ߞB b_OVV޽J111N@L A>^f; 888}dbb":%`\JA%Kݻr Je6lM윏8R,?fY(k-R'SfMb9iߡ$Xy-$&&i9^\ z.45"ꖒ>d !^ :tlm{MjδGb3[nIW~팪I&DǏ!**KR /oի:ac svvvS`0 )04`0gu:qrJbtYb9=G v`j ,,,`ĺ@Z%K$}ced`V Ӷm['֭YZ6֭[x`C>3Tiw4{l\r:ҥKePvڡt͛Gŀ"S`=z@ 77ƭXB|˗Kj??aٳge<(ґ I`MLL A_ hf777hذ$'F۷o ,XjCiXX:7mڔEhgaÆPBbݒ%K !!A<h梻wʖato "ЦMrV 6mYfU:u gϞիkѢ *hΟM4 A*v׋'zr!_!9jz![>M:uZj,VBԕ_^(4`=5d^:v숞c̝; XT0aÆ7okۢE 4C8j8XPNb݆  **J)GŊ3ԔAql9y)̿6nذ8C Fvጊ ?ƦDt_6aaa?ϖ˕+'y݇I4hr̕<~2 !`0 Fznϟ/c˖-pub]ǎM,Lr9Yʂ3gJ^)+T>n8*b(]4Z'WsetPǚ%K+WQªo߾ĺºud̚5Xgee%!;;;ݻ7.<<6n(YGC{%ÇoAaoocǎ%EEE3iѳgOb>""6mڄFъܰaCԱî^f111?H_.&FBO.9|Μ9[bСC%[[[mq-L@ ȏѩ͔h4hve˖l1si=w9sy1kD%pvv&3C`>}EQ$m$$$o&KƣGPb6mC|ڸB׮]uᲳuŊ>}ȒE>[1ct 3OaĺOO?$llVIe˖^7Çױit߿_d3oX cƌ)˱GJȵj_ʒABc1h bSNAHH<x9CI[r%X(GAs6nܘ9;sᅭ;88X 9r\|Y\.H>HY 6&]礦zBa?  sNc0 #0h b]rr2|7eyM֏9Rz˵Zh3g΄+/|ĺ/_رc=p(S m&YnZZ90n8bNCH/1czJ)S@||jժ˱!rʡh40ydbGl93ylqL4 ͌%ԹIiL:Uf񐚚J۷/+VLw}'W>>>5p̞=ۨcs1yPRi+;\xq޽V$Bo.Ynvv61!ݻCJu͇qXcv^n7oބX{S`:3g:?>\vM~~~Ǐ t:fnݺt|XXXgE0~xP/vH/3ELLsN.FHt$6&8p aԩE>x6]xRRR `0 sNc0 #cƌ!nr"F믢eAPPDEE+W:ZaԩSA GELcŊ_A j5y9e6lt:<}>|KKKhҤ nݺupIANe?G#޻wZjE%_rr2\xQgԩ>G$I1oڵkJL}j׮:*:u$"22ڶm++zBƏeeeA~իrV`8;;8>}_E͇a„ TƵR߿?r~A7K(!:f%m۶ĺ{‘#Gv=kW{L-3** :vvE f\Or{`Yvm4IM0- yT|9= @U.-0pX`$ƍ{V3yd4{ڙ3gK.T^~-;9bee>J IDATZ\3t`s"ik֬h5Ql{{{^FQPTҲe0|p4{ףGuM=N;wSSSC~8tRJhUVO{ tUGpp0ya͚5hڵ+z!6C+ MA=`߾}?*6>}Z3!CϽ5YϞ=u2C<~h>o߾ψQߣӦMyQm366&OLttς SNX NgڷoO)`ciVVYF<ؘ;4h!BBB`r~=d`j=9r$:g8p@Z)++ zڲ-[<({ڵLaؾ};L:UuBӦM!GTK;;;jqNƤOZ_~KJSa?PGC`0 +9`0|*FOϟ?vڡJgJ֭mtQ 0}t3f g.hԨ! C)I~dd$9|||?dMyඟ?۷ӧO !%Jݸq7n N$?>>fϞ ^^^"UV.<<ڵk'8Jrvv6̜9=9r$P_uo߾y`޼yqZnݺQ뫯/jLy ޽`/^@ǎQ sb֬YPNb]jj* Ljp ۷/͛eeRQ 2R )Rqpp@X<KHH>}Ν;̟?u[8 N`ŊйsgP^tu:ۗ;?׮]VZ),4bhԨfȏK &Me0qDhӦ .++ F9yFrs`PR%XrFWΘ1Μ9#XVRR 2P8:TP.\;v Zh!hN"::.YdlԨDVgfUдiSAmԨ~V ݻw!A0|px=*xa۶mh&WBݺuډ+WB5wpYо}{:~g5k%KW(J5k֠ cǎߗLag&MkNj >|ڶm:e֯__RF_~)))giGHdggñcǠwPzuغu{|N h׮c9A83~uF=XCAA>}u:[ӧʕ+W`ܸqBIr @a={,^qpBXK͛묽{!8[VyQ KaÆZ|ԨQ{n۷777A}Rmlٲ0g~0~xg9_Ν;É'Ϭ?>^>kZ}V635QNN|A3f[Ujժ4-I*|M``B # &yW7,dٲv5Zjʔ)&W2!9 # z-ȑ#lDIOOըQ#mذ^|B\O?-'''ejԨ>'%%ESLQݺu5z[^x"5TdtcZꫯj6;vLSLQll"""_0ZvQEEEiȐ!vV>}Z}ׯǠ޽{ӳPp~&Ծ}{u]-*p%g? 6LfT^= 6dƍհaC=ZF֢EF͊%jժ0aBWW5k_^;vT``K.r[oUԌo%ŋ^z={ܴk. 6LuԹNEc--7ounn>#wQT-\P/ԱcG͙3L%K7ѦMb*eL\\\3~׊ҴitE_1c5joGM)ݻhŋնm[&'"Ԝ9s^xg:y"8q]|Y=:uիWޜ]ENGFŎ +QK[P }(_f͚z7j`3gl2 Q֭W_@mۚ\ʕ+Էo|W믿7ߨ^zwLѠA5oܢ1K.&˷l٢h=Zzu9s:u`}&[+BWiҍ~r+=sÍmݺU[VӦMճgO+5jƌsǿYfreO-['Tllv\!!!E͒gto(44T:uҳ>Ç|-j+$W_}U&ޜy9rD?"##յkW >SdIk׮&'-^zI-Z0Y駟qƚ={q Ν˛lcҥ&ӧuV8cY̙3ѐ!CtQkƌjذ rG5b-kݺu+J{OiԮ][>Ѳ\=ܹs[W^Տ?F1$((H<@b,g,==]Txx:w7yHItTp>}ڶmk2hΝի|||ԢE K:v6lPgQQQ3fL Mv^|Y(ըQC:uN8Tl֬ƏiӦ9RR%͞=[mڴ1w5}'OﯦMOVFFҴm6>QJNISq3F'NTݺu(--M  ;voߞoO?~I ???9::*##CgΜݻuBp~[K.զM_x1oǖ-[f͚ Ѕ t mٲ&GGGM6dB%O>JMM-09k۶mڶm[lڴFI&-7\/͛'%%)..N...jܸd0#GRN]ÖoQ޽M6 ;wN#Gȑ#hjժrvvֹstYeddhϞ=ŞVOovH%9ɓcrPcԿjժWjժJNNֆ L߬nWre7dGFvv&L &v 222}`3g5wE>L}59pnٲeZlj֬M*((H...:q>\uX,6mhƍ{ˀHs~@~|}}5o]|Y֭+q:x뭷c͝;>۷o?.;;;կ__ZVׯ+33SJIIΝ; zgd04m4h^{5o^aaa u mܸQηޠ \>Ǜ\!33So~m*$$D:wo oʕ5c 9;;[,nM6Mw}- -ҢE͛zruuɓ'uan2t|'+Hڵ5c =c&uY1B#FPjjժ]eddhbA7Yk.ڵ+:4iRD]dMey[訷~wlM4IӧOWݺuUvmEFF`0(--MիNRZ5͜9Sw_I{kgڵk>>zW,I)>*J-[48yyye˖ RժUǏkÆ &ɆYZj9r-9e̘1_t[Gz+ٳOrR{cV]ڔƍSΝ3bbb̮4 3m45iҤL'N;;;͞=[N얛={hϞ=]鳳_|͛7ߑsʕ|%K(00Raʕ+kժUuV]~ccݵxb} Өƍkz7(;p8`vOœ:w'|R3f̰hVZjӦMرcPN^Z:tw%.xUZ8a_Է~[`޽{Mw+W/77be-I UVnnn뮻LN{%&:5l0}&Ҿ}Zm|jݺuNNWwyڡ`Uk-{,pcUVYޛ5XQ+88X+W,sjÆ jҤUprr?ի[>ooo-X̀lk\\\?Tg,Ybo777-^Usʀ{O/VSO=+((HL&['qKjر~G.[p=hͪ_C)hBajQnZھ}:w\zP81c-Zd=FCaaf2EK,g}&oobҳgOݻX (]=z޽{տuڶm~AӦMecff͕Sdɒr7(/44T۷o?YK'Oָq,^wIرcIܥ5988X;v=Avi$-_\۷/rӖ-[CY,W^?,2% @3gԷ~["I\nnn>d%fn 6hڴi}k>}$ggg{5jhڵzW-mpssҥK7XtPn͚5լY3W7H+88=KWŹtgPP֯_w}WܻT\\&-ZSO='`nmgggWW.,aÆߋ4sm۶xxxh7nEV۷[Yiر믋9wttԨQ/RJ/<<\;wYm=::ZSLرc-^wIի5rHMBXT& e_?Wn]mٲE}Pd( OOO-[LSڵ-^xxF oÆ n$־}lvB#GjѢEx4i>b=V\Y3f_|Qܴ`M0ق~8gҺ֩SGw /Pd8ggg >\7oh_|_ֶmۊ<^–dIAZg04|,V=ZpFee&NywOO 6}f,SO=?So; j;v0c^z0a/_^"RJzWt! MNNNСf̘cǎ`0WBB^zKkC IDAT%/ ZlK~(g㣩SԩS5k-Z(88XNNN PLLzꥡCZjzyG]O6zK$LwE ߿ԩS'-_\cƌ) ׎z饗ta}J5 j޼{=ܹS[n-2( ҵjRVJ-WWW3Fv?,wvvVnݴsN?cЊ+4k,իkĈJdFyWN-_\5kf$z7uŕP7iSMIN멧RBB>5jԨȓ٩I&׿~M{ѫjKf̘$$$DÆ ӎ;ԢEк5jTwڵ,XÇ+)}^}Uw}]RԬYS3fЮ]ԧObMj奇~XSNɓ'ߖIUx}w5j6mT*+d$k:p{9'J׽ޫ5ko-$~~S\\ u`Pe 2Vprr?%&&jĈfngoo-ZhĈڵkl٢{DLJ$-]T#FPϞ=5jãT<==գG?E_~EӧO7;9N:5j~w8٩wڷo EGGL Sk֬oҭ[7:tH{ѣծ];9gggGVw}"UxmܸQ{'|+&&FaaaZje?o4*U׶mۊ=Qa_ԁvZ=Z=5jyyy5o޼TT'j֭z͞HU>٣{ϘզM矛՞>Hׯj¿%ԞS>[-`0裏>R||~~~yv2Ѷq{ֶm۴m6?Olٳgxb-^XIIIJKKSjj.]*UOjРt{キTW/]x-\P۷oWjjd0O>eb@LqڵK6mڤ4:uJNիW"www+**JQQQU-{5-_\ڰaN|}}K$=JHHceԱcG'55U-ŋuA̙3rwwWHHBBBج.99Y˗/תUt1>}ZOٳg%ݘC>>>[4h (66"+)Zӕ+WsQ6rH : Qpe-_\ .TBBB3ԹswC=;ޘ,_^ .ԦMTeeez Q͚5ս{wuҥUD'N?+Vĉyk׮GSttvl+V(>>^Gɻwfdd(''':u;[lY"Zb YFGUzz._,)((HիڴiS!C… f}|4h yjݺuWQ-Oϟ^z-;tP}~`;uV-_\7o֩S /9::C V T~}5iD6=VZ:|.\hru4w\>|XǏױctFc=vڕڵk?ky}5gϞUʕjBBBxʜ9sԧO;7nX;v0zLFF[mܸ1o"wwwըQCjӦ [Xǎӊ+j*?~o;;[ڮիwKUqH"""tС;0@"x[NJOO׵k}VZzGK|EF͙3g+Vy׵tUR- -ZhbEvvvޭ-[?ԟٳpႮ]&GGGUREUTQXX6mMm۶f'f6ݻWK.U||ti?^yהիsҥ ,[Pzzu;&OZ!.\KjѢE:|p^Յ QNuEڵ+4S^5kh…ڲeK^|y}Taaaݻ:vX!&˪[jٲeZzui]xQyפ`uM~YW^{Mrc7o4l6vPsΩJ*2ԩS%z&ɑ]_e0d߿_ypס±o[nުU+[ @}Qӌ=%9͘ߥeӦMjٲѲ_U͛7/`LY"͵eVV+TP\SJDŽ /߱E)))BTFnn.φ吭m=9oX-11N`~BAVXa41-(($ޣU~OӦM3]v6&+W?/,[fҴdeO?t)GzϳMePVb.+qu_}u!1͆y(l-ĴkJ0u޽M&Iްk(,w6nt{&MJ9TdԊ+1 ]~ݻ"x2335{leO=T@9o\ٖ>*HNr?P||Ѳ.]r4&Mdt? @4ydeqqqrss+*J.]c#( &ۗr4@CrcW^OZt ulffF0-]~5kf6mڤGyDׯ_7Z>dȐRҍiF#""rTϞ={ԳgO;wh9g͊+ԯ_?Qfoo[!*|rv._9shΜ9ruuUfy{{CϟWzz?ui۶m&ծ][|A)N<˗罾rk.-[q:uRLLLiPamذA{}>|XVҁL7l0 q` Νӂ ^_~]G/lӍR=J+LʜD[.Ktam߾]k֬1y\\\jժU iP_Zv֮][zWQ vޭ~ڬc\\\4a„7}嗚1cY{ׯ_ EP8q3)S0aشiXooo}%P1тD:ui&խ[ڡ=zín/BvvvB{Լysk@O(WHNrA>6lؠ5jX;@?kTTIjժeP*zHcǎv;CU\\[oUNNN_jժ(?^}Cm"##auڡTX:tϟ/{{{k@ᡩSjȑ(V͚5Sf4vX%''kƍߔCرcr-xxxGjٲڷoVZJ?p4ikvHd0TzuĨwի] 6ԽޫF rjժ뮻tgC-vXt9R =yCBBTYW^{mb,24HN4HN4HN4HN4HN4HN4HN4HN4HN4HN4HN4HN4HN4HN4HN4HN4HN4HN4HN4HN4HN4HN4HN4HN4HN49^:zTtI͵v4叝"H֎@U+W/SJLΜvT&EFJ;KݻK+[;*P1N&M&NXGFƍ[J3gJUJ/(T(UYYRH{$ؚ3g#:YY֎ '9y{otf^lh+Wn{~6b$] u$mhH`o\`Hܦ'H]HjHPz''ڑ6uv@q]+Mb((Q/KCZ; XСR߾#zj彶W޽K5SNiժUlkܸԩSq@q͙3jӦت򝜶rt%ddH+VH={Z;|V^ѱԓ)ݫ>}ܲmر$(Slz |UFFҔ-___ڡ ݭSN$P.4;;Z5) @T1TtׯK))7&5ŋ+lrڢEtڊ3֩SG͛7P\mذA-͛uV]x} 5jvکk׮jժANN/~ԮRݺSUi~o+۪2͈#eРA$بG?׬Yt|ю;c}Ǻ4zhĔR<)iR -yFu+xp+''q~I6cn$1 ]~eggUV{'|_|`JRVV98H]|<0Kٹrr˗K> x饗4o޼"'ݔ^zIPd(ie01nL*j믿v (d1vͭG*Ud1`0[)쬶m۪SNU䤔_^fRBBc{Gj*@YDrP_cvءMޱrttDXVצM]ra؄  :T&LЛo[rFSV 3X;@хjܹ?3ML;dggwG7|'OT!9 GGG_޽{#`0o8XoޱZdiP-X@~\\\\ǰauիW'4PAeOpwwW\\b ?k;q߯DeddWjݺ^k׮i֭JHHPff.^ʕ+MPXXMwGѡC yxx(<<\:*99YUPPj׮0 III:xuEEFF^z ]Vbb8RJ QllTR?9u߯#GԩS Wz!w:pt5+&&F5j԰xܹJLLԮ]L9::W~~~62OIݻD_QQQ_~711Q{URRTfMAf mMjjt1%''MkVeddh׮]:yN<){{{F)ѕ+Wyf%&&ٳt\]]榠 EDD(44DΝĉ:u<==ڵknݺ?)ڻw=cǎŋ Rllj֬YjqX‘#G'O*--M TDDD}믿*%%Eʒ<<L9;;Wf0foѪU+y{{y}v?~\ɺv횂Tzu5jm7۾}:cǎU WDDDטݻwѣ:yΜ9jժ)00PQQQ%n(9e۱c>C-X@999w{yy)..N#G,RϾ}4bĈ[ũk׮fճvZղet|uvvVVto߾>(vIII6mϟ={N͚5#<q={VSLѬYk.u]Ӏ|wӧ7yKF~0n8T^]7yyy8{,\t YYY2733C۶m(;v`Ϟ=sׯAa̘1Ǐ1w\1[߾}扔l۶ >>>1i$x{{3ӧ+=O_~-bǎŞ={dfS ///322燍7˓y~ݻwRk@1)) 999ZxѾ}{}^x͛7cx5]]]cРA8p ttt5rH$''|R .]Z9//'N7^)_re|Rn;سgvލ }J*8p ƍҥK F۷oǺup-GJ, {{{899O>066V}e͛o>zywwwdرbBЊ+bʕb\|/ŋu+W={JB E`` ?˗/'''L<jҠs=ѣRm۶-\nnnnbzaҰm6ZJ|ԱcGL<;wVK!CYvژ7o֯_k׮ٶnݪеPصkك˗/K6660`FkkkAm=z͟?__6m—/_8usXt)ի'UG"00W\aa߿VE¡Cl-{x=V\-[ --StRn&q]lٲB||<CCC899*k;;;۶mڵkŞ~e˖>}:z /݋/^0DhӦ <<<.xEAhQ&gΜQV:::5jTg t (j`      B6-Z2] HL4yr]4nܘcJ2 ΝIB 8pZjˇ˗/^̶|rL6M :t(G4(Xp!OA!;;V¼y $144Đ!Cf^Oưa{TRh۶-/k׮8}-77"9r$m&Hɓ'fzK*)3c ,[Lvǚ5k0i$޽;ӧO޽;nݺ%CCClذCU[[na8xظWcƍ7oV#%Kɓ1c aÆ 6m.ײeKܹSlٲbQFT.]Z^X 6 O>UدRJW̏DFFUj<,,,p!+Ö-[0yd" I6m;wvڼ۳\l > UN>|Xܹ7nDdd$3f ,YUa|gڵk+%劼 ۷/Ϟ=CǎªUx 9[ {k%XTT,S.9VZaÆnobBvZ:ӧS%!1f~+W`Æ 8z(sy4k 6mRz̛7Yf'NU(FFFXf FOYYY7oVX6mRZ0>} ߭[78p`>u҅Frrrzj̙3{-Zhi B>FFFb/IFLAAAAAAJѣw(8U>}BӦM Ӏ|AJtt Rh'oa/Pܼy3.`tڕ0 ^|LJR 0 6_r%6n9;*ik||]v2i^ vvvx 0fUȿM4Qj]Bnn. #G5j$(P_Ê Ӏ]tAllPcǎK0 _߯YNNNǠ:x)ի:ĠCpuuUIfT`i&1Kzz::w{ 6+\\\x"##]t4?l(&M•+WxE= E6 +m,>|Je;|0S)t8::j%0Ĉؽ{uB+trt_fMNǏɓ'JG,! ///l߾]àAdU޾}˱*UJ :+èQpK-<[df.]E)^Yd 6lؠ_o>333ѧOğѣGĉ`*A\t =zP0//_=bbb~zz:y ݁W'NTWE"33Sp{ Tϵk0h A9  |TIjj*t邈mR˗1tPk< :TК8++ ...?Gt޽]633z\~F]g4HFFu릕  ӶAAAAAAA<| 3^ƍ cǎ1>>>UJ1cƈ"M4)xf@XX3co0sL^"{uaee֭[Zj(^8/^ҥKL1/_pi -- Æ M6EZ`aa ,,9/}f͂?vfV WCCCԩSxׯ_ ovZaɒ%A}AAA8p 'N0ѭ[7|Kpp0ϟ;~!q24nrDTP۷oأ9*ѷo_ *03zU&!!/׭[777Q> T5Z)RG.]0d/^TH̠MV<UG2Eс-''N~RRG`WdIjJf@\zUfll3 ,Ȼ}H$ϏX500q4&`Wsrl5B&M4+%Μ9#/If.z3W޽f+29999,|Յ?6mʫܝ;w8 )`agguq[lY5o߾,ttt.]*'8fnݺ17JR\9>|#HNΟ?kkkϏ3JGGGܺu []6mY9Bsss:uЬM6{ttt{nIɓ'3˗/f/_>ҥK+Tcǎ֭[9}2o< >\y=zQ8vuaSμgii#GbŊb`` {aÆqW^͛793gUn bǎ{bb"s$""yXfB;27G9UsΡD sGA)o9      (r >m۶u$nRKb|ck޼Siiiؿ?>rHWV`f7J%IRfѣGxJ)W>}pvkBOOOȱ)Yf1bVVMe˖)+T/$fffL"e,ttti&fv(-Z` ]BCC喽~:gvCCCfe˖?x۷ol9r$zZn ٳga-Z._X+WJ,)9spG-Ȋ(,Yw9V;+TF ~Μ9Ü0 ޽)K@eLLLxexiܸ1&Mı 33Sz$2d~wX &H$CrϐxBjcjjJ[8rGchhM6?ߩWSXy fV>E3g###\TXZZ*|Xs.\dT?9ŋqƂTzڵS|! CQ"--t̙,tttm6uΆ`lmm1sLgR(WT)NA}HFAAAAAA"cРAUIII߀wM 777V0UVκfP"U,tttxŋKͼѽ{w^AdQ$˖,Ν+p%*w#tdzBvٙݻwɉwV"ѣG96f̘T:ae?SV"w&L NLLlll Am+»wp1<==yebb_|ӧֲeKtUP#<{,xH\\'K[ҥYDahh)!3d⛭qq+Wָx%_><<<&k3|pނI&qL" ZeeeqW?y%0~x\VB!{2dx3vX+VL̖A~(Qye!tMSYsP5E$aƌX[[378t萪*?~#跰т59vF12{lY^zsrssS>hwwwZRg=U0g$HF8YƲsrrriӦ)1wX\###΍U󊾾>Nʫ̏?߿6Z*w.fsLym   "-C|жkA1nF-   r/j،%^9s&GlRXA||xR3hNX gިZ*'8-;;QQQ4a ~!uxd_W*UʌPdI@0--MfrG֭(߾}{Aԉd6KEsǦ,+55?^m"kjjrEaÆ)q"|`dd ,nݺ5>*X( dT5YQsׯޫYsӕA]&:t$^Vr|)233yըQ#A>hJpsB׆|`&M(}/qio߾q+E":t >Hļ#B!9Kh`U1dLjMHV.y믿 /llllB m  P-_ 4">}Bl|<ain2%JL]*jJ!  /gEFC\>~/_PҲڵQA ԬYSP9VjժdeeؒѠAL>&MZ+X~g$D#u/8 ^:YK2R%仰vJdIPUӲ[n˗HJJBRR y,AiB,YR,k ]VpYuQV-AX;~*-[Az 7>|@ڵ1o<=ZP9ZjEߑIIIʂu({͚́5xܻwOy%d'NKKKǏl+WYH^Ӵ42,ɓ'ѠAZJ,T<לc֭toRѰaC굎%ʕ+'&+?~uK 611AŊis0?aaaUVX}r[D}666bهsrrKph`` Wi" u =Pܽ{HJJBJJ rssgdd}66 YԨQCpڵkšC? l`υT~D⴬,ܿnׯ ~|#hRSS]AT}a'O 33FFFcUe9{.gU<*U 111 AEA@yr͚LZ 27qm܋Fߩicۣ~SGC^J')5g_],AԴ4\s)R_`]+.]kUF0x Q/^']LLnTzűtC%`s7n \}Q,~S۷ǀ]QZ^DGhcɒ%96DUQD 5$==se>}&&&jCQ޾}˱ Ƈ>plu|TV-8qBn;XKh_eWsss@]U~!=>|uaϞ=JM @V&ZR&| Ǧ8L2DRR:TР'O}A||< WƚU_~طo=99'Nܹsѽ{w;wVJSҲ W`T<& $b4 ?#%$/0ۏU(G`/VVLJʩS~M||HFCG8w-}+V qZÇ9r$N#'1%BBp68n;%VPЈfpŋC.]AxۏcVJ i?zGhfsG<%cT\U̚5 ۷og044D֭gggTZUm>IL"plʳڑ]V!"$k.|]FFN jEzzrB7ee˖I&zJ\opI Y ,?}RRR{n޽&&&h׮Э[7f`,XWPP5jīE+NSfh$%%y*3H(zzۛlEeǗ*OvvƲ T^:֬Y#w\3f Ѯ];(570LQΝ;brl4\*zxxUn?~Į]RJݝ103ߟ-,,pak3os9<~-tӥK?^nQlYڵktS.u[֨BE6mT&L4#4m54ueU@>|@FT*L?M}RWWWH$Rj'd ~d-xg{;ͩA8|8V4Y-B)B 7#Ǒ 0 @YgϪ9/٣ ,1c XYY)\.22m۶Ŏ;]>,3h*+e-l۶ ϟЧOܹQQQHOOGBB޾}O"::ZӵhcLZ۷ǝ;w0qD.]ZroFf }Յ3cC26pJ]4__ ![Jԩy+VĪU|r8q8<222dB>}pUlْy nnn ש(-[Dɒ%2_\xiii,45֭cŨ^Bl-[DBB݋ݻw#44TnWСCܿ4ɒ%Kpa-҂G\XugQZ'==]vhӦMSy[͚5Sc񁿿?pudgg,gtSk5Z搜}q,D",[ 'NuiCР-i(\a{vkN`bb"ևTgle(@^^`%k<ȃ.2e Zj%iWDAEAX;ܛ?Af/[&MPB%5kHÆjo C%N_&LL AB̙ͅ0_IՅWСC < :/^TG<pse|RVbE\pwdU1Xc]Hޏ 4cccׯBI__...pqqAFF.^C!00|!AcqkXܼP|_U '''ܹW;w...bfdd… bbŊN,--9633BqMK(///xyyӧO8~8:/J`ʔ)غuN_űW^{RX5%f`B1~$%%8tN>-U,#F̙3jֻ-Z[Q$NӶ*)!8v؆@N wEYO\dffHp|/ϫyyyj[uIŊ ź P AA1XAMՃ!5 5#k d\ض -ggX($ٳ5-{N575E54>ADa5GD"4o@ *>'%W;eE$ӆYv-n݋7 ҡ [LրAht7oqetܙyn\\|}} +͛7jkOZ޽SNVBŋ9E7`%N{Ru~A ĄNNNǏqi;xGac\{ng i.\MΘÚG 5-S <==qi| ۶mCŊǏ0Ob! هu5~޾}`iiÈQvmgϞŃ Q~_\fdd͛b6kkkL2w])))r3SmA҈U e7EaXYoٲeDMEYOhOyUȳY 9  x[|% †όxaVti-xCB2pqAѰV- ttĺY0oC$jXEFr@/4uAh8ͶjU ~fBPX U£GUP|JH˱)un?zsGFRT.A]]]ǎ;RRR9z(~ժU9J*ql?VNVyV;DbЫW/Au=}T.iʕ+slʌ>h-;X"G#oZH6ҥ tݻwYYYb9r,1ңG6ܹ3'+ɓ'+.%Xc ۤQB-** yyy‚%<==ѯ_?̜9>>>bdzpI :T~GHJJD"ܹuը?`u>|6uP;fffK۷HII) SSS={Yfq9z(i6o _\޾}stqqﺞ={*x-idffݻwRE`AeLLLxAm<'ku&b+.]K[0PR%γntt4sQD|If``e2ϯ\2nܸ!f{ W.}!DA$ 6<{(WW? &~6J/US٩SX%SScc=y2nc9Yٽ[U2yDT I%]KbܹvG*)_ sH`f `ΨQ޵ ֩mBϕU]Kѣ!S2%J`2w`جo߰UKD!K.CCC9ITůʱ=|Pm@Cp^@H! 7AqժUĂNׯϱ);&Mpl׮]ӂ'qss̙39vl0߱ÇxxS155Elqqqb&//'N;GOO]vU%Krv?\$YMMMffwwwu9s&zQֹPO!..Nfbbjժ :::똓 N/c̙L!:(3ߤpVpEVvH֘Vu{ihcœ'Oe+ MMM>K(L5jԀ-&&iiid%-LH (GuT}a]K[[[ yUgN!eYυj}EAhAOX~D"^BQx040//<{a$GdƢCf\22xobcqU&7fS T4 pA^]K eJԶ{R|AOs$A e` \ كcƠ/@WWWI%N+[BB S[EpsryxXG6l:@nzzz#GFFg~߾}pG+pGJJ4ޙR}{"cBIJ4(u AiJD6K@] )Fͷ͒!d&pܟ^}`^k={~C|4T檫Wx'дZ2^O{AV 2rv`СǡMXSWTkRʍqĉZJAKK UJKKqinnN n46L,֬cǎ%(tϑ#GO6&&&2q>?R'ON$߭AAA05JJJ w {]޹sf122v:::2x5A-:М ;>~"@5~A{=Q@7=La~޾}[ `zz:"""X~@ ~8PO)QSʉ!@ $%$lTD< -E֯0YK! 652J-w >\]+V`H^PlԨ9RMY&՜4ŋǢVV͙ Y QHNVս{)~Z7s8OA''''FM) cȐ!{ e!gA͏:tHf:tχ?~"""% [8bud{wYkQFQ>xXlKttt`iiY,&&D'N9r$EKܽ{u_uA̪ݻw7nűqr=CPZZ*ћ&ɞ={XJn&(|SNժ"''GE)催w333iŔznnyOWSO[&j&AvOQQQdݎN.v tk@~5Aׯ@7 ,vA__Jُ?zF?~ϟS=ܹs߿ $8(ݼzqdee @ ?DF  £PF󁕈Bmr4`dEگ fų^\ &>RNT **hQ-uC"+JU & j̍!GP Y o:̦-,ik˳dN#~/^كxĈ#(e'N@bb"~6m$* 3f̠m۶ 111u` ;ݻ*e***q.o``Pg͚%6AM]B}|m͛7g=sj^TaÆ!??J9A@@LMMnL4R{BL[#55U?[nX4i} e[1aJҥKh6111\=pQ$$$g۶m[2eee e_C EEEXzL &P {خcccqYJcm~n$NTR_~Ŏ;XK+rvvʾB955hY '''J֭[Ygׯ_O)ҥ Zh=l˖-fEEE__;;;4k֬Jٷo0|$B@`#N$=@ p=尽Dd!J]WɜJ xPI 4|i28O8Љ BdϞ=B9K=}۲eKQ ##qQpQ,+++1|̙w^͍RޥK17Poyzz2n[Ҥ:UJKK1sL.]ʹ3f %{Tqq1ƍ<{. Z>>>BqʼHM2tbϟcҥ9a3QB…  53gRʢi'l(++I[ ??? +suJYMcܸqa%Z9>ҪU+X[[Sʗ-[&ࡦG#Fߊx.vI)wrrd ON pS -=}4}[(i]}rp|5ЫW/LرqNJJ &OL))͐!Chٷoߎp\}3gNfffFɆv}ZRaiàA(徾1cJJJXrq%tӧOGQQu߿ :uk׮~>\BEܙif4')}A!""U_'N@^YfQ/_e˖xF}+WR())a7ob…5prrX|HmаJYYY,X>شi.J0tPgϢsR^^'۷5^b ܼyRzj8mĄR\h0;;vvvHJJqq|rะ5WVVٳg:777J_ޮ]]cۘ <:wsδD>>>2޿6.\\\jlollL|Q@(HJKt4VPut`m1WGaq1FGCb"򐕛 hCK]:ܶ-ϗ,yyMLD/tHIlw\ IDATIAUI 43a/?JJ'_#+/\.ʹL[ZFS-:n:%h9 #a1[]E&4S$W{o㑓y9ڡXGaq1b㑔TCEI A+CCh֙}w>}BHNZjr HLMEbj*|AcwS#:]˗HJKCfN2qbm-[uc}Bn@ssUZV?_[a!T6-Z̬Υeex(d䠨瞫u#{.B"5# ))r+1 OӳNȀ T.0jL,cQ{NZf&|ڊј >FyU_~Ă (gD ܹsӺwNP4inJ"dl޼US-5j===祡2w\9sSj ˗/ԩS$=br,--g߾}h۶-%sܾ}p-xzzbȐ!G#** !!!cŋ0` GGG(2صk(Ykڶm+8&iŠ+dQ%*tٳgS*֭,XyA__}vv6h8;;*[lA||}gϦ\/\Ҧ6ٲe ._*hժ֭[#G[{.]x츸89zzzpqq3Zno߾aشiEР0[PVXCQ>>>#oߎ-ZT+++1sLʽ@2n8\tMt @RRpܹem۶Errre 577ǢEy*999ٳ'fϞ 777\>}x%8466ϟwdee ~~~~ddd`ѢEMLL(F~8;;Sqqqӧaii IIɟu߿ǹstҟFCx!cabbYfǧJ`iic5w0o_5G]]X|,//ǩW{Ϟʞ=@ll?rEt\^Gԣ!xJz:v e5k4ded`ߧHEJz:99p8ԣ 1 a{Ϩ Tngc%EE"ߩ|E^RiڱjSb\sG.\jƮ>]ba IȀ;y>V !!n(>7q\GEQ`ggVx8\^Nzml7x>; 5`f9;)#[DX;}.^D.hhciѣV ?zm>I45yc ǯ^s8u* y$WGGv&V],N]+j; ] ZiML //1y#. v[:b p>~L)752BV(~(oagc..qӎ٭C3phagϐL)q ^xfE<._}ù7p44w"#)NqnݻcСei)QQw \w>QSe1?1 v"R[}4Tѩm7n`w` nGD>>HRRN:AKK ?Ƌ/׍7"22NI8DÇn֬P@pYDx \Aۖ-qÃS4ΆcJqڛ8㑀QST\u{bV)r!Vx8nc֭֡N>}>K)f-[LD;FUU:oaq]\{qr&iEx=tBӚVXݻB^=‚͛ѳsg,0CzypVԖ <\[3cuֲS)gb7:{ [BMYMXs'i&n*2q}R_q/ OqRxكGU'-3!! As==,sq{{Hp\E\R/_GI!mڄm m\_,ںRngcC&&uZY*-=;=bccUUU¯CdѣG...B\~]$cDRR9kΜ9$S4n7n܀5m`rDDDP ɓߔ:.;w0:k׮ !C޽#FЊWJJJp]ܽ{W|Jv,&ULQ3f bccN+//ǽ{p޽QSSõkנ"3-zzzXv-VXA+..ƃj H`twO&ڵkѣ%;nO<'OfCE67o,Ǿ}_z5lmmYUVaŴ߾}ÿ˳}n0w\L0ոFUUW^5Z%!!q6 6`ԨQ6޳}vܼy_ik[ ~G6n܈x9sR\aAAA@lذ=}ߧ|ۻ?`5&۶mÏ?K[z@P;NMn/)9y2>x0/^qB,ݶM2p۲6lQVz„a/tNjnΪt?it}Z 4ԠI)/)-Et\@cz|&*=Sp:-|NOG?WWF/~fd` ͢' ڵm yNN"R#ZuvYq:fW&W6 >ǎZq\ލg3l%7ɉ0:Iii?m))o?~ոqiIΆӢEAŋrx>  Ӫ KggpAdvɫVa5it|7mŸS:B}׾P9b6n+aZuJJKq%:n[e0Ã02= H@OнS tuijlxns~V´|=<`:l"hܲ4rA}÷>yc0i7n t+.ļƏ :i)@hѢ\&Mrrr AZ-;99aݐpE@@Q0hѣG!+@ 4---\~:" QpwwvFFFx"E|Zh׮nݺCCC'))Y+s.]Z`2c ;8V) >^0p n?X% G7=SdS!ݜ*%).,(*.3ӧq} ²I6 _bͮ]6o@T<&ǏG>>uMaq1̙%B aʕ0 ?J`;}:㬜l s毋8_}C)SrnKpeسոqB!A<]aa'BS xL=)kv…Bˤ6S׬DDEac܆8mþ}?m235';O3E#IY< jp0i$x AOO۶m& 3#֙,,,S=zLq3ncbbS  9sׯg۾}{ܿ_ ㆆ޼y+W ,jܸ1͛>`ذa<СCGRRCdd$cʔ)x ,P{?񳏏\ח8q!&&ÇCCCx{{#)) T---GEE‘#Gꍓ~CEx%V\)ܜ@gϞe->|8߿ǔ)SvڵkS#--{gϞ5–-[pU²}vvx4h^~ +++1YVDHHƏϸ 5k'p qѴiS<{ ֭HܦMТE ܿm۶et6l؀ӧO r8c 0nb' wׯ_Ljkk#44ba0j(={=m˗p0k,x͚5x|---DFFޞq#((H-[ѣGKKK8qBYt  ׊P/y%\׮iƈ~=o >E͞SL[]\:Χ8}y9yy"Bt4  _Ii)܄n߆Ǟ=BK"I 7oh] #<˖ի,Z.^zT?( ה)"2i6o LiΜ]5[ᘿiv{"EGG6j 8q"z8vڕb1͛Suޝo[[[ׯy&_۷oݻw|-[1w\FSNСCqIܹ@6Rd۷/űUP˲ePXXM6:P~,֨{Fll,o;v )<Y[[cΜ91bD{ʊ2~˖-kWMMҮk׮άY^ b UVaРAؾ};N:4:v%Kɩʜ)-&MB^~[aԩSFxx855Álll`oo3r =rrrpu\~wA\\߱Zn!C`ܹs'o+>y|/"  £GpY ..222033C~0bXZZk׮xA9nN:aر4iX7iii30.3bÆ 4i;@VWWC 3z)К={ѣGq }EmeϮ@=6oaccúyaذa8|0N튊Ah@ 85|=z*>~(1c=pEEŶ PϝԌ t5 4 KQcr3F/kDh%o/s8e ̘k7t. ٓRn^-L3z4vXڋwbȜ9r-${DGٙRDU,/_c/RWN%'С"}Vsھ[Em?e 6p\Rnվ=;zΣFi Y׮ۇ,HD1(XZ{Q<:8|>L^J,m`a_1cYLbc&KކBVFfÇ[%'ay~ :HJu„+p44ŋhe` q2rrIa:vD:R[p&, #-~zFۭ >9"}ww?//ϝܿƁg)®wƺ{1'co+*liת^;''Bnp-[,eA(-+P1#ŹsҪUFK]iweDzSÆttlfީc"Fu+FU*(C͇ Ǎ;]nj*\OWֺ=^c M'N)cca=ar~y|-ücYȼV \m B^l;r6# .bE8ƺ7c?mزsw4GGM3)IIȂJ`$PON @(& ϟ?#??%%%PUULMMr-222Tddd@NN011ǏLhjjBOO͛7~⣼QQQHHH@ff&ա}}zh^JKKahh͛ ,HFbb"ҐIII(++C]]fff"C T磴jjj? HMMEjj*%%%m۶b$''HKKCff&5jeeem۶gУ˗/x WHIIAMM ZZZ077(A0BQQ033%-- 3~ HYk4$''#-- 鐓455ann.L)وFFFp תS'$''#??:::hڴ)ZhQg x9|4k h֬Y}%''rrr $~Рw޸uiii#>})))UVbw#::iii(//G-ТE V EEECrr2RSSMu ./^ )) Ѐ> Ⱦ_k~==q+IIIAll,͛9NYY>}sƍCYY066& "''J$e'x1#1N3mmnzzG|r2>|1j-[ ]۵UY˖TSCUU4VP@N~>ҳ(mqX;V0ᠱV:l q\utl&͛CYQGV^+ ~Y0j LMѢiS!.) QQ5f燁ְ"E1^0pǏx:ic}}(7n 9d#+7\1r6r%x߾e-N㕑c6ic[@_GM %%Lbs`<҂">9&??8]638[8BΩ5_P9سFaZ#9971Aۖ- FP;%+ ^!=;ո͋oi{Α35QU>ikC]E99HDt\Dlؿڧ|S5TaؠQCx%+7vf1Z_s8iʹTK HDRZDo_0Zo$rt)'bLJk9 .) 2ڗꢕ!t54 ))Tz_q#w&:f"ںVk @9ܹ*SQQAǎEаp8h׮ډoCCYY"t+VkEEEE뉂 &At4mT,l"f͚Yf6/),/555455IKݻ)NskeeUkcVrrrh۶-ڶm[צ48$$$`aa (Rr[)+#)) ##:K @iq={VM!zQ,&aCw OZiYx{_*`СKXo9ѩJJKsl=t_8Ef#}h,^0с Gݼ]'O:;sy|w,-}cmiYyлww252i3igáo_GrxpX&:'BM551Ϝaշ2ۏQ)pqpwalX߸Q-q}Jym1cH0,///ǝHx>{&, EKVKJBlb"m]'SSȈ0rߚz0e~p8putmnU?~܍ܿox.0"S@H^ >VM~~8}|paNlrXmk nlJJKu{")-Jì57#D&MAI^AY}D}/cm{哥ۨY31Q U}ٳ5C(O))<ܳ'\0gO9EW=8y2gé\Dj{ D] K33HIJk/SS^ݤ \0e¥\WBe9v ))|hmUӧicc^ӎ|۳^B&&pqp@?.de+}лwѣ|6JKIaر?dH k}}G%%p߽5vD3oQk@ @ @ @iZ˱ח5SG>wwιʹcr O 7߿^w3=|(jvp8aXZJ ,-W߇%<]" GFNN }>ՔѭCt Əu~8r6a|E/HIJ9}L߼{ A_c/R2v"miiLwr ݧO=|?GwM^S""*Rوplèi))Xo1{6]x=AZg11iLEguqZ3mm4!N`@bl} kw[Ѵ*c-FSJ 9Oטٔ)8%%$1{6Њ9zw]`?ھ_..}LI`UaVۋt,:l]MM *o߰ȑ*e**8aIFVFc0woL\_.>9?GNXURZ 5D&L E QgefR {O{͠= uib!?dbu0BBX+(jklrs_S7k'6oF?++?(M"N5N^V<<0v *kvڮC RO}_wl076y$ݻc֭ ŋX2y2̄ȞK`x։zD @ @ @ "N#r5[˗3Ϯ]q='M5hΝӚbҥTg5>~gxXGqt44pϏ0D&Cׯc |!%) 1O8~Ju'Ik \ݻgV4ƽ#Guh|-!5޽÷BJ9Sgt|=~錅iՑh;;8YtӱMZqڳc#15R.@A^VA>>iZSW7l`$L@]E֮Ek} *p3F!KX7g Ey2[(A7oWuocaZe^h=޹<csߺ.j߾ވ\t:-@ @ @ @퐨k x=m;?{:>$aZv66<|8m]h²v,0m"&>F g^9oZ4mʺ]?ah :m˨޽Y9#/qLhg4JBڎm0rephh̶mB *\ss6u;9YY;ŭpaM#0dC E׹M4٦V] *UDzaz,Sb<'lsqt+75|Rȱ6^qmZHW,-,_PXD>BOlB!B!B!Bi4o$!={Nq Mm?tIT5z4s{ۈONxN+Ñ#%IIOgnDWs*4d2awstK5IEFZWK`nsrg&?KKz#1EFD 1Ux}ӄU?<NNV͜snǏ"rOdM̶D qG̗PWKVsy>}6]Lm/VN IDATp\3W`6tZBȱ l%Wڜ9jj2i1`]#ߝ=Ǐs0_?Q Atu>72XC;B/?'лÇcok*pY oqfdKm \Kmv̶'NHMvy߽lĈ} #B!B!B!B1>JN#&JzоE nrD\qCR|94NIӆ8..cu2g:̶n!.)ID&&"QFᔴ4vchlӶ+LPYÆlTN {muk{oei֭F:_R%n»11:%t:nžf eF۲Q#ٓ>9RRRVbBVVPvmf[Dt4";wl[ƫ՜uśx]zu [r|[kknEem*Q~u[PiDP (p=77ٱ#x¹oAA 7F7Х{T8Yp%,#S̶!!{.HNp^,_Pl3v0uPS.qT*>{n{@n+!B!B!B!TdFJKJ¥0f[__|Æq9#9:i~[d mU$S9BřlNěےcj?x+L"fn7fH^X dPޞ|=7;7w8f<9Mhq`annh^$%}Prl"ܕ+q]R.Q\v}TzݬdgIm>ChuҽJ% T:W5 WCJ%rp&q'Uu9F{WobC^ө 35UO#FJ<ğB 'NK.CBlڻ7::Joceq1 e~@,qroחZ`&.#8<\cMŕ7mJR{B!B!B!RQr)|H7Yb@βc@ ]*1MmU%EDt,_u^SGrdtIr|"?9&Z[3ߑ"iE1YoZU dVpqtziN2)WBe|I0*(,˹4'xxHNM1r_&v_?qGZrwש_VEHTѷ,^=j؞_PP=]y/S2$GZ\;iíF\XXT;ѧC&XL.ѽfMYDzssN#R%ޕ8%`wqۇ&B!B!B!B*:JN#%%_umVc8Uf̶QQCznnj>-!;;v".2}mZӦ6}T#ұ)Ԩ7WWcɻplJb+JJkEU6^r[ER{7o*U #4d/VڵS32ʬNDtݶmI+?PcQ*'给1]EwzK59WP,R)[,Vjd2)ZquwgnyQ+ƽFLN~ʕ(r=sYYZr&"'Z<6B:NJz:3zooJ˼MZw۷%PNZr"qJd!/])6K ,6="B!B!B!aY d&TG&Md;NWgiiONF5ggَ d҇qqCy$nEEɲLE^=2:jZ4_yyk$..Ƃߗ}"6z{CPhL8:rKJB\RxQ %ڵƪ+UQ!+'6KF{M4%/9 ggJJ۰Aj*X~=Gv~빹qj)K93+KujdT?~Oc]#llДO')W Tȵ OP{ARqg]\.5&΃̄Uy6Y7iGYanfNZйsmr%5S\d*b‰9qBThׯ㷿O~ԭYӀ="B!B!;^zF !B!xq$w8L[m}}mB< WHDdm9)7a1,ANnlz_ҍ>cepWA'r6r*"v!oXNn.&/]]U]*Nf[YIdT7cU:%] M4˔,4iU{/yQO?ݚ)z˼jLiN+3?:o6҅xkJUfȘn-ey@Z˸F4m54#L6j57)/t 3WNnRֹ  %c< ZλD˼NJz:&&J%;:޳c??NEZe1 rN+c !B!B("##;vKB!*j5wPE]Żu:gϐgiiH̄JSPX.f"rY,QOU5lȬuRMX*PF }#QV1rM""0- o˖ef޸HHv6w?VrIuUbX?shD`BpP+ %&Q*d&y%/{7N^mxXsWnD~Av}MxɷbX[3kAӧ6MQQ B*Krkb-e=l?P\]ameFoY`rVM+Rvm}D["a/Wœ Ԭm؄汴YbD߾XigNtP\1wo0-5*36xXHB!:9w>=Ǐ5ڄ[Jz:0tY0а^=ѱbwBc')Iwtj))̶闟+OgSxg̬,TsvR320vBb՜gWB!B!B!b(9$4surbN.ե:ZzIJ+yV,irV(2_?ش 1e~nL [֡YÆשח_s*zJN::^gD߾hո =2 ѱx]ת ~C\RR}>Yy~>>߹3^:tBY"3+ m陙 <ݬᡱj|xwoqyIq~V$ɔNN8rU b&neX:m|4Yn 3OL*ÀkB*U8>^e$ڝԘB?؅ %C1u}N٤۱v@5зS' Z Vrwݺ&Α&<3+9MJFBĺLl幒`$CKttpVD.E|ʪmۆm80fҥ̱u/ޙB!B!B!BHyDi%I v66S`V>⬖+>lܹxk Q]sҟ~BSoo~*k611QXc8-5Pi €ijl7O}j5޺n~@F0R]\4T*B#"zFF2'WȚh$ UeƩëV}}+daNCRX#C5zގC$<{]kS^5V}x]ZE.>3/\lc?|w]ꊡ}`aTx7خS gLT*\#fLϛ)|$D*b"[c mcê '5K.S&bѣ۩{D!B!B!B!`'/ɳKLv&MyЈ+ _+sse]ZttDXdoQt[r%9^V#XL/^\f +vrj*ilEJzvs334d"1ʼn☺|9?~m_3g깹 d&w4T2<&nVF%;;cc]h4`~?xPS g^^SSrZyPBpxv]?yy$FJpr-tUL}KWPXsr-J%X]B!B!B!B^U-N*$$xB(.&63Z[?].׮ A.]ꇩZ:m:l ≄ 11x}H|'4d=,?HȋlFDhlepi _1h/Zdcݸwm ϋcT:h&rc;/mԮ^]5_V̶w[K^-Ld߹3 ~/֫Gۘ m8y2k!@*u?//?_i`N%KZѱc ^7k9ED0ty_xHouBߒ%cƠ}!B!B!B!=;Њٹ/+;[t?[crၥӦOǎy*J"D^#GX/HxL8*,[+KK8P^>}|y1ieBJciEź;,-M8xOaeiwzsN{_bRan^nEEiJJO~ܹ6N6^[ESΎ9qw$žggJzIKpkT_/d<ilj*9X\7ɑܧ҄ڰ̐IAL̥̊VƩgj--\[6 jZ͘7w suL.L-Scnf!!XieFX:}ގO!B!B!B!%0х5 ~x?'}1X1cΫ×ɚڰGFaa8ǎa3H9iMVchո1|4SoM>ɤİX+)ll0o89.\cpYP˜  E[vmTX=/?J$݌b&hԈfr#W9VTSvK",`Lp(SJ؅*F̟ϝP(y28:CH}iS؄ƵY99Wu6oa}m񒕜T~[ )CO@@}?coE7u2ߢg YWȞijn߿Ԍ $&=B5"d(pH3;s9Z5n,:B-21oJ%{OSONJ޾je=ך6Emz3*^&_B!B!B!B~J^2t4)J<ɝl=a`5KRX}; ssٱ#y8y"?}Ni_P-mtZſ*١8i0k&waei;c@}HZO%Kp_D[P7·hܾ]"9Tfei  bߩSqKUNkÍ]׉t}i.ȑѮAb_Sdk naHYsLD[_r5Fm6jlcɓqb1o7PE=%$5|CJ3E.bu󻹏EELN^⹄ 7N..3`o.ELl,۹s%r1 B!B!B!ByPryIh"*>Ȑ07ڹJ|5{~yMD OPIqAaa8.m 1 K4D||`*ZLNk',VV߹3wSz^ Zx` o6d^Wݾ Q̷Aqc=Crj*T$0+(q/9"&K?mo/f0XXа^=X_se/x RHYS$^[B~~hU3gԥKz2S32a,2@%rc]#͔J)oLWU1&C>skHQAϻ_VYLN+(,4~XxEuCb~?x;ᶿյ+GB!B!B!R(P IDAT&r<|TcT*nLm&\ cn$Tll0o_ظOİ>}gZU(,#NtN=9ɫt@ '":4O%;;0C@eY:Yd4Vu*5$Jzznn+W${Ǐ Re<s碠nceV QոH}irUJWB Bt<;I))~1a\~S(&.)IRlST*ѣ];l[ai0^Sfe&JN9B(9͔<.%,oR<^t;=9T|(4v24;-aR=xS/綻ŋ #B!B!B!B)(9TёvCY$!yy6W''}23q}f[% suW+Z|gkkmܞ]F1 1O^))jH5]]e=5A0NrZq5kb7`ʕ=+:v3ooЈU*B#"4>K>+RjUTsv^<эVȘY'@oڊ_͞F?H$21<*0{|![j?5)/?,-h1zXPmmpD\޵ p?߽kM崲*?//ܼ<<0*'Ĵ:z[ S}׀=2݄_CUB=&d:֊}+u.߸!)j퍋۷s͛P泭kdcOO(9qT ]ݵĔSXⓓeEL k TzukyYx i*| 㶙һDC%WqJytnC=f mkB!B!B!BHEiz;ʚⶋӠ^++4Sfc Y7]DϳOHnil{fPF[dWgɝ;)Z'o[PPXh!!Xq#MnB!B!B!BH&O&0|<Jׯ#'72\C=\]S:èt>$Z1Xͼ^dX>f i7497JYjIF2` m߫WZӦĺrFժ܊>}s{Qk++ Y֘«,UQܾܼ>6kkkTRE~ 55%rIddd ++ĻY;;;TTIB䑝Ra%USR899҄ʛ GB^EyyyP{Oeii)B!SAivJAAܣcj f5pw/~ VDH[&,-94Z5n,9C[0{bh?џ SLNkPJeTE9?KMms* /4W'',#P/鰩ZU1mmQ ?h;x6P(l$cwVlڄ!!#FG/$2+ȕxP^98K>F^~>.\l+*1(J4SYUd}c 5l߯=1%Cy3Æ-BfBH?//qq&8(msb=xկ]zmfқ؆ ƫT Nݥ0,GnV~{.2b¯> MP>)bh(wLhޢׯ#/?_IIOgKt!~qEF2F UOr5Y7pSW)8x^$fce%9)ITr+_}lIk8\H~c(q+4n=xsAKLxE&7B 6`رh۶-um۶:t(6n܈(cw:|0<==1i$:tVthݺ5֭[fb!oϞ=%(1@={ C'''TV ѣ>3={yE !)|vZjB!FJhӤ \m{O%׿ⶽYJY=ӹOEj5v;&9NERёV3Vϓ'%VfV=+9ΎÇm|~Uccz~ˋY+)% - zyilv7QA۪l`kmͭZƫ橋!zq-qHő9ϝBfvZ+KK}U )ohݚ!RлCYYv9¬ך6&h9~\Eʫg5]ĸyܦ \8:~\כ\};u:N:u?~$*.&6Vit[5n ssɱуtyC)xkWf[Ll,NGǏ1urn{MWWx{D1eXt)6lMbؼy3Cddd &&صk&NOOOznv܉ g?mcǎ:Wn#Rqj,[Ķ҉D^ؽ{7իcРAXjN:;w %% qN6mD䄐 qQoZZ֮]kB!;JN#%(Jܙٖ.XLƩf`nf>IӞ$$ >9YR>}huxTj^R˗u%%| {HڿPp\bMMJpܮY=<8!xŬPϪV^*) 8pVOqrw_Vv yuM]?涯5 == أX]\UY+*k++jߞ$!%VONƮGmv66ڶVqzkǭPΝ:IJeefe͛e;Ou@F$673ݙmi%C_&רTכ7:''9 ]"oWLl,b4˕?Gq؁TY#F5ggnwµkߊVU֋8_o">w.2?g+ lYT\p_>>3DDDСCh޼9>3)ObҤI(aa P ѐ͛7"gIʷ?Ŀ}%bDbbСFQŕ%K.<|1&L֭[ѣGz)! O``E|%fdff7B!莒ӈ]p۾߹S/6'w@-Pf Wggn j,GryɧurgBͨ(y@-ZU1N]11:?q|ZLVF`n?i_)j5XӾY997~Z~\䬚*G"ŗMQY1Q:%&󑛗VR1/"?eD`O1t)3~h7ЈT~oN܄^Ŗu{+*Jp };uM[\E*1cJŷAyy1rrs1qb}Rc#e~׿DYMzOib"3Rkdǖ-i9 &;75]5LD[??f۾StNhjٟ݉]nܶl\^Bi{ tիھK44RN50*Ie1|HVlmoְ!V̘aBLUc{⣏>5櫤;܇$9?FHy"0'k̶ԭԮX-ѺIɱ?_~] hzf[=77ԭYSD$<>bZ BT|}%s+ ݎ]PD;+T0b|efr?۲epur2`4jHySR% ٓ>cJגBZ  r|[oښի:-RdIUׄ.KNbԂ%b/8йsTyE#֥m[YA {RĜc' OP6#zO׭Vݻי U*[FtU'Nޏ۳NTutC{F5mx{,nA1>kY:uйMn>]yv6.\Ƚׯ][EL6[ 3.) ƏG³gٳh

۷7`XTK l1 N'ӧZÐYp-g>;DѹJ?x0}58p8?,bW`#>o`2gY 4<"VzMKn+Xv1\]]İ ;wQ}ƍ믹IB U*lܻ sti8*e>Z/P\m& lس NtHHrVVX6}:=)%o0GƢߴi1oSSoє)ܶ' =y2RR7L.MĭLk ֬YGs>:%T ûzqo߿v#F&/'~# S?gͽ!`L矂B _mތ1 jTh!5^Pym:z SDs㓓֭ RXzO~0K^~>>7)l3Z_~79sZuSjXZX` IDATŌjf5J0ٵܼ}tJ:زbFAaah?jTo߿Ncܕ+xcx߾hhǏ=]4m,'Q ϳljxmp5J/IJ.F&/]O֮5OJчVZ QM`Lhaa>gΜaVz!mۦ$R 5h@o3OlҤ~"--Ķƍ K Uj\W-Rڵ 999/lffGJ*?ÇfjJ^ܺuk;w9ラ/_BQ5g!B1eY_NLJIAω A#ԯ])鈌s_9rƈpRETߚxzeF t11h-||D?yBBvv܊bd4tXݸ88pqtD6m}|P՜aanB qIIr&; Niո,gD w<_@zo;N033çOq+* ;D9;Ñ#$߷|Z_oقFӦU r_mތOƞ;pkIXL"^$?sϜ?z{Y+6m=ѧC3%%q|<]ぁͲh GDGӡFu^&&&"":;N)0T5%v66r, ;ܼ<|X/۩tzj *WFrZⓒkwKW!B*U_`„ <[oƜc;VnX0yeC!۲eK?w ...FͫcȐ!Xf j}clr ޽+*Bʃ|G/{bܸqF!B!ڣ4UZ5|={6X!0t>>?~N1,ݻ+KK7֩O*)%{Ǟ_nS(lQO*pHU*/q#'^b 8Qc{hDF *Z|6y$1$=#Gȑ lUl( ,2Ery{k6,S6}0nrݼyeރs1>v "DƼaԩXn]"X-DH-:[dY8~FuŘ fJ%ԀL>={$yke$&$`X}}#;7'5PZ[#'/OT2CXn1 uT(WhB\}H1]"#_^o>syK#Oxmg$ &$`ŦMZrwnj)9a-z`oxɄ Xqgg㷿--[bСaN-v޽u^-s <;!ڴiJh<`*=00yyy|+ʋQUL@!;w ((ĶA7Çs:ogg5k֠O>mgΜ4BHSNj Wx֭F!B _bԡC1NO/::oNOT mjTԟFVNL1bt2N۩F/[hC/=+(,z\ ++9G$b%RԪV W7Bo^b`֭%[2m>ۻ{zB6[73iCx%SM Ʉ ܣ^b׮^Y sik8U-+V@PhB1дaBGYqlx֩#*1KMc>#n͚{ZXD.mbd~eanVΡ XBTe`Mς+9+b,6Mo ]};w.:j؍==UT'x!hjܸĴ"3gؖk׮I*a7333ZB!޽{5=yHIL+һwo4d,zV !*}oB!"%2_Pffo^8?~l=VWptp%^EөU+|5{Wǎ/eRGZmRq~xԪ%KeӧC䕗nil/OI۷hSM[kkx ~N %Kd};ozGXU`kDM&xU-+zZ[Yモյm[=ZX6Ċ3d䄓62߳..sM&|[kk4]fybC͛'[id>kht%֪3hTV(غblu9SqZJ%\)uBtH\xg&J^^"##q%ܿ"3e۷-~kްݻ Bll,jTadff<޽ˬg(Ϟ=q$&&'557oĥK\{BDGGҥKDFFQaLyyyx]ڵkFaj C`` nܸ!|{'OҥK}6R*!33˗qbaC8x`?׭[et[n!00AAAu>}*1XRRR`<~z?`,bJcp\pׯ_Ç:JJJBhh(\OZOnϞ=CHH"##%ccchC!44HKK3+,ܻw/^DDDQriii/bj=Bxx8Ο?+WΝ;HNNݺu8f!B!Jڒ`iac'ٳ9W+WʲrcOO0}:%073eDUb35R+dإ ~ K\M?><1\p"c6m9U֡1x&g&cW'\}9_9ܜy 9 ,Y"j}5u$,قPAETP"YERj(XmZG[\(*XP  EY=!MH@W_>=$ㄜ<>GM^RR L@x{#1%%ڵa lسGmWIEݻ8Mg>>8uYԨ+ࠂf!+'GU͚8JW.%.܉ e;b!2Wcs n]IҰ*rtT¼ T/P ij/y3oر*)?4QNlތɿ=Gmw 33 ٳ(azؿjԭt[?`Νe2YiÆB$\xAAA8psҝ #Fٙ~>gϞ[KK &M{NNN6oތuÇbBmmmtƍy<Յ%xz٧Ow^[W^$]JD>}TRT$ab1[[[ׯ_s*<~[BOʂ5jA"m=zynll,6m$}K*((ٳgk.~(ضm0zhEDDvڅG2Zh#Gb]6Wͭ[bÇIED o>X{~۶m1rHL8:r.sܻwΝ;EXYHOO͛+WHM,t=zzMDزeu@)͉'CP(m<~GuRy=Э[7rC8qߒCFF\#ڷo/y%=z8pbbb8-ԭ[m۶'zLjرcXl.]9^0bL:J+996mquf2P(5Zh 2T4\zU,&9Hl߾gϞ dhh;;;o]vE׮]ӊӻwƑ#G8ǷP(D1|p 6 J.]4e,ʣDe}aaa8pN:żo Rz333 8qskkku>|8 իOvv6m&kܸ1ױ|rDDD ##8&M2WPP`]o;1p@̘1nnn^ۑUbC9w~wrz-,,0i$L<<dڹs2779~ob͛7bÆ )gϞ~e%H߽{>.]®]/^pSѣGI;ªUp c1gCpp0ۇKM633=:w]SN|W000;f:'*!B!FiD.E ڵ\妍3\<`پ=W??|2)\.[Op+4}H`hLJ6|'MSH kD;kkٸQ6l۶xIiBl'LoD}1aDӧ*iSOWx{gxWD Yƈv*q鏗4hbk{cˁ= ̓Ԫam *5Qa=TCB_;ӸjU_6nĢ=Aٽuk/BPV-] "bFegacÜ+KM@ @SQm/Yk>QEyv6l^^ҽ!T|O>&<~Lj#n:Xn8}tuuuŒ"##1f1D8r9^za֭U(/Xqus9998wΝ;f͚!44T餛\L2E,6h f˓'O8e9s\\\0h Y'O,cM;;;bbb0aܸq%&&bѢEXt)fΜ~MᤄsޣN>'')$V0L2Ef5k׮ڵkXp!/^ӧ+< Ɵ)(NN}6 $>'^xvڅ&r}vH}… pvJeK.ҥK媌T;v >>>puuŒ%K眶6-Z$ٴiSN`i̙ѰaCƼybO9LN.k;SN;9-==s$<}FժU1uT̜95jԐk_,ϟ?'.]$97oļy_a '#fΜ@Ց $8pݺuի:gUѣϤsJcudff~:OaXfpƎL.RPP80w\haqbC4zjb $,鈌Ddd${ 6 >>>̤҄cҤIVzDBTTΝ+V` +y3fLqrZNNƎ`~:]" @ٳ6m);;vž={?c _;ww͛'C``wjj*~WcÆ c1j___<-1WwrZpp0֭['ҥ 39mǎزeԶY/ݻ0aB$HII+b L8+V`sՍ7'RSS1xR_ci[jt.]Knݺ3gO.LWWm۶>_ZE B! JN#r3n[ƪmrr?}k[dž 5x0&R&U1cRѩ6 3fpp t\y- jGjUbј=z4ɓ8 ETÄ3~' 5ädbۛSuАV[[[f[%HРn?޽r%kn_ѮE i:f%VTk4I99ի8t4ϝ5͠Jx BJM.RwׯdVl֨9jٌUD TPCG[ !CѸ!32D{%~׸{ AH" ^ITY m*._$TUrsr*s_G9&jHUcc汩U0QE*݊ ڶ;vՀ]4!C0a2<D4i7l;&qޫ66r!0y\YޫXOW6c?v v^^prdeTjUTVtdejĉpl X[6F/OOJ,FH[M{umܪ~6L+֩;v`ʕ'$D4m,@GWWcvvxǘHDUt/+o[[y,;tjax%e_-ڶŸo@h`|?}}ѷS'ݹ/}l₩~az)ݏ"ujbU=T]\!!haQ{zDclؾ ѣ^jvӧʻ"ݻ1fc ""]tATT d2/p :TxkGUޗ~~~QR~~>V\ÇԩSrWg%..={dV+dl?{Qhߙ9s&:p*tEGjU I׮]Cǎqe4Pﯚرcۈ IDATő#GVCsssKN{.>| W5, W\;9-ZbP)رc=z$Ҽ{K,x֭$KRRv튓'Om۶ ٳgҥK),,Ddd$bbbԖvyNLΝ;1a|st_^䴟-R˟>}/paU45Ⳡ$&&פ,ZH;v -- rmSXXooolܸQ}effbʔ)8x BBB`E>~~-NQǏ_~$֜={={DvvvpBD"/.)++  Tޒ&''ǏGLL ֯_>|iVX  77W6oތ#GɓhܸJ"Q8K,Ob۶m 'S%rrr;!B!@iDaص|9x|l,x/^uz:tuQvmԳ@c_5kVSY{II ǿ! ~҂Epi[޽VřRVS1%..ₕs 1%w.ݽO]FKCUQZ5k[sVe>+xݽ/^O252k1IhFeI-kiL/&M¶0rqw(,,D 33֭66fe:075Eu33lnnpoݚUxhעb]#h仂P(Ĩ}1o_crMUoes`٬Y;}OB賗/)/ZZ056U͚h쌯5CmaUfh98;w:?~>YX0oЄQ@YCwǐU.U`ƈ1b$'#("qwS޼AAA`njժyh6p*i2r7 DΞ=!C(<ѫW/DEEdB`hh(F!۷ocذaP3NL޽PbZTtw\YXj߿={J'`&&&믿FFFJKf˃Rӧ1l0(]ƍڵ*4^~޽{ĸqJLG֭q &&"Ni ̕+W0bp;88.VWtޫ/^}2T%%A޸qr.>}NLj9fffӧ1~xUe_yoeNRY:L-D'Or,¬*gV81HxzzVoFĴ"qqq}JL+~W_}=z(ˈ#NL+iÆ ?t$˗/TEUijgУG\t \HիWС\ׯW*1MY$Sr!B)(9VjUOp uU_N_M'uU(ibt~b<|/]\о>AKKKjզƢF ̛0&LǛܼORSc&Y[ښ)/*C۷/, W\+9M2LJz'urm[DEEz;;;XYY޽CJJ ߿>L2s҂q}hׯ_//R!lmmaiiڵkC$۷x!|\=:[˗/e&YZZjBff&^|[n)t.iեVٳE dܹsy?z(;lٲW?Us>n]t'$$H*m65 ]tAVVN:(((#bP˜9sн{w尰0,--aii ####-- / 4B!B;JN#*U&Ѯ@Nꤣ]&U!EBO$cM4?PWGG'M&7o8qM_S+>6 BJLefgcUz,;*'U-qZ7%BB %ծ]˖-СCWGחh ???߼3}t}Ν;Wlx=^ ___\zӆ_KN;tgB96m~w'naa5k`b3w^̚5 ^CbqTؘ1c8|K.XhڔXL>|%K QI|֭1bڿ4666Ƃ 0fԪU X,[ 洱atb:vãxK;wSN-uBCC1p@^%333f(@ ٳ1gΜR$..?Μ9#D2e n޼YۯZ -$%%m۶b1+VNicbĂT%۟3g5k&/,,ӧ1k,N`tt46n(MYΞ=+쯧#G3'w^ܹ|'/ %7nB!u))BHKNJcBd+ R\qot$KU73C#kk BHeSNq...8|03ӦMCΝѣGĹ-[`ʔ)hѢSd@@ 6`ɜ癚nnnӧg%ׯ޽{,88СZ+zYG$Fۣ[nU}$+cmmmԮ][,JlUć"fYfFFFRPPoooNUsssꫯiiio߾ҥ 뇓'O=>{lݻiJNm޼9ߏ =J*h֭֬[ݻw$ƍѣaW__&L@^ЩS'H"8w¯,Wd֬YK,vЦM?۷o{<==GŠA011)̪:g``*O<$ <<۷/u[xxxk֬722BӦMq*>}k׮q%iCiUT%+hii)9/HLL[lR,,,0vX;׮]ϫ%ϙ# s=с+\]]QfMbD"۷Ԥ]ݻwb5kJI􄧧'-u/^ɓ'b1E$I>P&+WrM4Add*d%hΝÄ x{صkD_~Y1Ksm6IC`` v|*9M$b1[[[^Uf{-WgϞaÆ xefbZIf&0- +>.sJbU!""4IkƖ-[uRWT}I7|+Wʬ5e̝;yX5kđ#G!`Ϟ=H$Bݑ:u*/^\qttK-r1fIML+M6qEK.`Pȑ#b1mmm9rDjbZI:::عs'QZ5=zVs:=z&IKK 'NDhhBەԨQ#Nu_#B!<4B!RVN߻ljS BU5 ILP!ʈu452!z7}$N{.'~zԩSG6ܰ`N<22_ݷZj0;v,'J*X|ǀևB qry橺kkU$wFFFشiޮ][@@rrr8}B!v؁5jݎ5,Y‰+󚬭f+0eN"]7JJJJzŻ=Y Jeѥn#x лwo|Nc˖-VIKlH$Çj1n ?uTNʕ+2<$O]]]r$:>U5F*-Fׯ_s;w }}}^߿y[̙3Ν;먆 ׬Yڏ'OѣGMʫM R߽{7'fff&3ŋ3*~\>|8'^TN:Xb\֭:K.ɵ4 䜿?FTTRX rKs/Sv*k<ֳgO}P~;F!BHyBiB*nAω$BBQǬ,ܼk*!|ʪ֦Y3M! Ur!5 P;=H$yUU4'N$Tw`&̜9S W^EJJX̌PFFFZumN"&Mp[:t@ΝbNBZZu owA^,ֿ+tI&ٳ -R(ӓsz)'"HOOvVcUX&x6m8jΝ;χ՗*?? gVT۶m9U p޾}+o4U Pф^{~@2U5k̘1[B,\="IVV3Yޞ[ֶl‰jՊx*ʪӧOJ\ĉpttT*0Zc???ZZZo92o}mu...810%V}Ç ʬ7n@bbXL([<==9ՔE"xmر n{{{fB=== :T}kKSZ598pw?*]v!_bAh///TRW{jkҤIQVeJMj֬ɉj'B!OZB36XL aC B*ժqЧS' Bʗ*zzkdׯ@o!4ݝhjjd0!Eeff20˫`ժUb8^m* %"%%vvvSS˗u렫? IDAT־>zɫ>}QKӧ9nÃjg[PPkff:(V^Z, ҽ{wĞ>} WWW$N,66SmOUsΉ/??SXXWZn ccc888֭[+WHlTѓ.^ȉu իWW{_7oc| $%%:uP8]ɓ' H=xU8n߾=LMMŪЦ 55S*G+3Nܹ3\kB! jLLگ=%B!!TJ_k77MwBM6DSJ%VV~ȫ-uعs'lj{zzk׮j͛7N<ҥ grhe#8艹s ֭ |ZhKHHTyM.J,UIenHê,?)}%eff֭[h֬wEFzzzhbnnn,--h|eU"SL581YYPPYf!00W͛7iBہp̙2I q'LbD^pB(1n8Nr>֬Y~u(_29b266fޫdaݧYYXIuk;#44T,~J&jffprr׋c|ɶ? 1b 3&_&&&۷oB!B%t!B!B!B!aM*m.QSZZZpppL^nڴ)>T$۷ocɜ%GYKKKTpU yM@ܹs999b1vIU?~p7oV5De_Sjՠ'*u4Jsqqqر#b Y ::Z-Z()َ6>~;wCyƚ$*}|ȉ+:ɪi?eIVAm4hЀ׾kү899C˗Ѻukt ^~}$ЊH&VhfCll,cEJNƳgb:::VVVv{L's~-_=U'y{{sb`gg^~XXc/exUT$vJ(K˖-Cxx8'>zhxyyiG81''2ݧ?urfdd(\Ii>/===d]JJ+ѣG۫5䓀w\dԩӧO1tP4k K.e~R5JN#B!%B!B!B!Rdddpb666JɪHOiB!gRTPP4~xܻw_|9ڷo֭Tn_ѥsb6l@ժUQjU&&&0111add###000>q6$rUZUeiڴibkz^SEncҥxaa!?///XXX˗/ǭ[_ݺuQvmg%9::%>{ 2777Ia:ufffcWV%CCC,XXNN݋f͚0`6n܈O꟪HV5x`hтXrr2ѲeKD:֔fV'O⧟~ĝqF 3gҰ>e9St>ͺ横-E122W'{UD1}'ݻWMHܹsq ͛7ǼypYN*ֽB!!B!B!B`XJjSF[[@~g˗/Ǿ}8bgSn_ѱ߿GFF222|?~ǏLdee!++ FNNrrr8S|*B5eff>IJ2lڵkcǖHӧOc޼yprrB O?!55>%+ݹsͩRr;---=JD BjՊW5uk'вeRCaԩW\\\n:^eޟO>nO `͚5 TaիWǠAp)JNR#*:++?Ʒ~I066eeY]A^j+LrgYŇnU|gXӧOJYI^C=GV˗N˗sΨU&N7n(D"'sB!Y(9B!B!B! 5Tɻ婪EFFחC@@z(9Mu*\﹮T#*,YB|S9h"`̙ O\VPPXll5jp*I&ɓtR:4u1fvf̘ubj6sb|+ر#.]$w嬬,8p]tA˖--vc,a͛7ШQ# bA^^XL1JlkVOu^EHh4i>,w7o`˖-h֬Ǐ+݇\Nuo#B!<4B!B!B!B*VdUe [Y=zÆ $"$$̫h]ffr_k͟?GT#+ͧOvZ8::"!!A$ndB kYiϟ?dzgdSw+*U 00ׯ/v޽ٳѩS'a_Y$3bbb0vXbccW__suOWL0?AiG$? P---Px;򲲲4݅2׻wo\zzRѴiS:tHSr!B*/ B!B!B!T@d(eOX +SUi2331`899iWXU<4Z&ٳLzMFJcaa*nݺ[nqsN~Zvر#^z|~V PXXXLFL4c%IV`q'Q*U4PJu_ ///L4 gΜA@@BBBgDEEQQQRJ̌SU0333`ڵسgpeB?stYYYJUb5=[bÉ{xx`XUdee2皬rSZ@'{L٩S'4o\d5 "<<ϟ?.++  CϞ=yujB!u4B!B!B!B*He'tE`رu'>o< "ݢsHffRidM?yqǞ={ʴ:"LLLĒӀωMe&`dd$<4Wi|?%+9kkL١CoMٳļy0o<ܺu  իWn#0fܹsիWWx{-A!B;;@!B!B!BQ k}EikkcǎǣG3?S8wΜ9#WےIbϟ?/>Ґ\@ TINɄ44CCC4mTW4Г}/^ɓ򂮮.7nTy$mmmԪUK,Uf֘2e ;/^`˖-h۶-"1kuj*9)11CE~~XVFzؕgAA޾}T2'yyygV*YOQa3Ft!B*5krbẄ́SY9r ,4h ڵksbn_ѱj'SF NK,|jlVL&Tfoo/ud;E &&F1WWrSI^&&&044Qwww_y&uyNnn.[}[.'ƚjժU f2CVޓLc4y2effoa&[mٲ͚5S{JXtU(gdd 55U,sssmVtx1Yk,^ؿy_o@cϬ1{lܺu q xL+ҒW[B!KńB!B!|YX=? لB!D6'''N޽{JY6+`(((RiJF8^Ȭ+:N,**J=QkpzBTZj믿0rHciJ{h۷oǏrS^ hтyzS14j̄/9aذa8p'=??s~a'p*-ʊw|?7n̙C?rbe}ΫzڶqR+V$RdUr+ӔISf͚qcbbi̙UVq{!IIIF!BHyBiB!B!B!T0ޗ.]§Ox$&&ttt*>`x@8::jW҂Xݻwsnܸ!KeQn]N Ǫuei7͉~+V>|(wgggŢ_VZA ;99/^!U`^mٳG|ω?5_^=,UO.xo+,VY,--QN}Ud"ϫ^zAOOO,s_ePV-f²JTKEOssso>ݻwW->|aÆq  sssEQueVO۴iSm۶{ԫWp[=ŋ9qu~U||< AAAX׮]e.ŧz[``Qub>kLk׮ ?R2"޽Ë/b{!B! %B!B!B!R 2 BRRB8qYfСV>|?3'ngg UVDtt4Μ9P;y {Vq :b4,[LC=Rw}lj;wUO344D*Ubo޼PoWXXtuuabb"Jf֭#vN:۷oXVR9oj͛HīѣGFe_+++Njª:6ϕw̉/]T5k 33S,VZ5eǏ0`3Ye֭jǸq8#11Lg``>}p/V-1Ӹqcf5h =?''')s[ʢeٓsbVILXIЊX:!B!RA!B!B!B!0b1H3gFvv6fϞ͉W 9r$ FFF8x VIUu*,5_hbccnJ({{{tԉ2e B T" ѨQ#ح[Gc!!!AX[[˽=+aL+Mrdb<+! ""w{cǎ_Q o˗/˗/b:::̤eIZ._??yLI&ºooߞw!BQJN#B!B!B!Ô)S80*;b*Y/UFF @ mࠡ)Ģ1o<5kUݭ mΜ9$мhK%rJt%an+rGyg+Q!///:v_|qҥ-\sMڹkrZDoߞqE֭㡇Jz粈AŸq U\yqE\{o999qYgի WP!b&/oK/%w-n,deez\tEIYy,ս{hժUx޽?9/^sN‡TV-zT=ƍs-rgq뭷&_veQvo۶m/۷oԯ_?a38y;w.qzᇣm۶1lذ|MC9$4h:[?NUP)şx>뮋%KĀ"###aw}_|q r!q饗[] 7_|Ex b1zR衇F>}JFqs1ѭ[5jT;3rss{-r & IDAT6~e^z ?W{M?"+k׮ѯ_իW:yyy1|xGcԨQ2Pyڵkw|3Co9zÝuV^G}t|.k'xbկgEϞ=?OO>;tkժ}-9 j*:q7Ɛ!CbΜ9Ɵ{_Wtak[qǐ!COqǙgYh|qGO?ߡw}7zUdk n{ɉM6ŬYs1|hٲe2$.袄rjժU/K۫W0`@lj'Z;5/Z(C2֪U9sfc'xb<3 |I׿NOVVV`q饗F~hѢ2eJ?ŋo9e˖q-ܲr/b;xnnn{'?ڵk4l0b͚5|;wnL0!&Ok׮M-H4`9rd̞=ҥK⦛nnݺEΝ~QVɉ˗̙37ߌ?cv%=z􈌌++y/RBaĘ1cb̘1Qf8ꨣ裏-[F:uJ*`;wn1dȐ"}I'1Թ?m6l7.:*iӦWyj֬Yt!B}Y\tEۜ׷oxm۶quy /СC㏏N:)5jYYYwł b„ 1~XlYnX _>}xgAѵk8#I&QNHKKyܹscر5ꫯNi~qA'|el]̒1o޼馛[nC9$v:tun>ϛ7/fϞ y%QB"w`G~a۷O?=Zh׏5kĒ%Kbʔ)'<g'pBs޽{vaq9D˖-####6l6ZhK6m{l}G6mW^qiE#7774hPL=ׯ_t)vm۶UV,Y$Ν}Y<䓑Fzyq{>ÄmK/4NZ,YwqG׺;c-xdڵ;#ꪄ,XP71u[JKK^z)N>4iRW_~ȝwUիWqű_}U~m z:t^P{>:ƎYYYŞ,}ݨ_~Yrx嗓*}׫W/Ə_f[W^=FrK>̌C#XrešɪUVB-Z$*V;/:th̚5k222㎋={FϞ=f &܎.QըQ#뮸KW^y%Ϋ^zzѳg޽{TVXkݺumRJ׮];a&M$Ffffp ?!&L'Oٳgw}+W,tQ_oq7ǘ1cb1vؘ1cFvaqUWŹ_2^Bciiiqa%΅^SL)4m۶-Q}7\nݤ9ssΉ{/&MqX"rrr"//б-o~޽{q1xX`vdffI'\pAkq7Nɔ)~r kmVX1{XfM 6,FcǎE\ѭ[㤓N*Q޲rğ-GWիpn.]wߍ3gƋ/cƌ &w8v_:h֬Y>Og?cܸqiӦmߺu _~%-5ks=,>:>裸㩧>hs?8K.5k&u İag~8z(((|p׿/D̄:u[ޑGYoVZhԩSqwc=k׮-F_+cmVVVL0!ǝw+V(Uo~kaÆ[[j5M@_1qĘ4iv1Ğ={w/R|g;WB8ꨣgϞѫWBw{ŝ[;/;xbС1f̘xEiڴi7zk._yBGWUTH+_1rȘ3gNOo\n֬Y|W%^֭(`IOx݈$?A2aCDΛ#+VDOgƘ1cb̙1XlYԯ_?5jp@wq~s%SƄ ⫯˗G&MEѩSq;xXhQD5VZѬYh߾}r!QrTG-xchѢXxq]6jժjՊ֭[GM6%*R< .>(O .UVE^^^deeE:um۶ѹshРA#1qĘ6mZ,\0-ZQfhذat!:vXv3gΌiӦ~mZ*"+++֭۷#8%_G- z}/~Qrssc1}1cFĚ5kZjM6#8">r\dI3&ΝϏhذa4nx;'N … c͚5QFΎvENQFevǸqb޼y`ˋFEFK.%.4hPt߮]:ujq˗/cƜ9syqƩZfeÆ 1rȘ:uj,^8b/N8ᄔZV˘8qb,Z(-Z+VիG͚5I&Ѿ}h׮]w,/999GŴibΜ9jժ͍5kF:ue˖ѥKRqǰaö\>bܸq^Kʕ }xϞ[N+(\N+'E>_^(߿22"֮(7g:ScĈ[.w=^y&v -[5*{.=ۺ,+--'?)ޱF1dHĴi?mbe :Mqiﯘ@RBG .LQO?]M 4={0$o~[Ӌw_Fr+W&@yظ1bݺ=IJO֭[Ǘ_~6m'x"'`o޽{GFFF@="dsK(SSLZZZ\y啅vP^{m0{)SĴiӶ\̌>}0Lz,bS~G.hР3gΌ^{-{e._r%Ѱa۳i*D}wSP7"33) UR%BcwuW[`A<[.gddu]DPr{v9-"S".,)(.=)؍o7>&`ouG^^ޖ_|q4k,*:N1p`Ć O>$$ 7PJ0aB,]tXzR~p@ @R >}"|p=PJZVZ:{mۦ:Ux饈֭Sij:?3ό^)i">/6*:@JGGo~s΍ش))\*D4i~ 'Dkd@1;~lӦի# R `OQ)SĦ}H}JR`ϥV "jJu 4i{T`@ҔHrISN ii$M9 )44@ҔHrISN ii$M9 )44@ҔHrISN ii$M9 )44@ҔHrISN ii$M9 )44@ҔHrISN ii$M9 )44@ҔHrISN ii$M9 )44@ҔHrISN ii$M9 )44@ҔHrISN ii$M9 )44@ҔHrISN ii$bڔ< I]YZZDfjNr䳈#"4b#rG:2"k-hvdDzT'C9 bDě=>Iݚ{gqDiqTGTx@Si@Y6+b"4xT@9_G,$b7/xGD^n?@s؛" S}><ɟGl\$~[Ku `o7slĠ3}h"ؾ%F1)612:iȫ_DT?E_SN`V.rtSۈχ:*:/^(((J#2|& kJ6?=lIQN`f*T8qDZuDT(\cæ" bnN% P.ض3wܡ ": "=|{  m׮~Ļ #[,bY(Rz [xumN #jd(i^fT'$+:P i$M9 )44xS+ IDAT@ҔHrISN ii$M9 )44@ҔHrISN ii$M9 )44V1س͚C,Iʈ޽ ў7Ir۵'D^{mYL:a##DC矯I?}K_Kv}i>[]7;aU*iehԨqm_'7 (SC,_0~͕ӒSM_SN`~RV\3+akgKA"6m*)SVcEsqsg{64`+c ^3i67o}?Ցvii.#? ~;ֹsHKKA `qcAR`䓵rƄ.]j ۣ2&M)r\9 `ף2&ON,U;HAb|9zTV!i(o%ggg VzdɆTH4`ti^|enx.Rmi 34Lu Hʕ㨣nݲ[W/#ןX)5-"K;9 ^N4iRBZ9 m%܏yiDDĈ#W_-4(7lذx7\\r[̟??yז rM6i;4 >pU'+rrrv)m۶MibŊx뭷7ߌKzq('PNRn]"":w+777x≔f8vq+V7|3ƏǏiӦE~~EDdee)o͋aÖW_sży#//?4MVf*ǩ։f*l6ksWǷyfM~ԭgFq!բ{QNϸ=o]w1uX,/֬YYU3ck' +79sҥyti^lTիW+DvvFiS-ڶZ=,[XdC겘0!']99^hݺjn]%N9Nh(&$U:(պ9_QzQBԭT-ڶURsb`kG#++#ڴ?YԩfT_Ç/ rb z_?#4[WsΩUH3fă>VPPHO=z(4ֻwS"ڴik׮r?i)LrB^}E 0Ɔ E}8ѻw#;*VL+\k-k-ի7k^qY_=zdio78'a<---z' 6C-gcʍ.͞U<-Ѿ}8:qmjŞ2֭K< 74 ˦p sbɒ EIp@29GQj]\x%WԛhEDDŊi_ԋ?YZ*uP#23K_nKc~Eǎ?9^~ַҿhժJc7|rqZUBڏ[C.Cgdǃ[6;X1\Xu[fX'1u[ް!?fVX,acǚ%.=?rZƕJ\N:uu͚Ur^}E< 2[N;w6ﷷ6fLroZBiq/}1dȒ2dIjU%nE#:jע/ڦM1|=zEqG˸eB9 RwW9|_kL2ڷ/|8mkqg,:ti] |PR%Қ0!'N?}z,_WwTN[dCqLkPI<>`U,]zd;_%q7)(f̟>UB(_'ԹsͤY`}qxUe+ "^}uY|隘=v=E>`U\z%:nP;ۼ[7#uN-sՓ^kw7w7.&c/?gQӮ ĭ~ +Yr{JkTRt-y3gΌY :tHzx _j CqBciG(DS{y {)4HqV)|Tݷ|y^1-{ZwVƠAwm^>j[ѣǴ;>)N;mMqƧ)xc-*Q1g^])ܿRLdvNGA̙SoikWt8;6eΜu;:uUʾԮ]/J>husԤ#cy%*ؽΏWlB)Sv9rdn馸 va1bĈr{޽{:.ZjѥK8c";;;Ho߾D+e'=`oWs?)b "~/lנz8i1{vn'77?~i9gbZ95(ydaҷe;ϤI;hQ94,^yʊiulڴՑv7+|LɊovN7o}zj;۸q+Y;>z"f,^ٻ( QlXAlH{k,o4h{$KbMQc5bNJ9;(+3;gfw-{y!z?P̙33gZn]ޫG!B!!B!r#$'h}'&komIH:]xDA|'گ&J "/ZG">nDVnRjھ}(P1,,9YYERX$$x&$&//OǏ>iii^0n\ZF",YB+}B!Bx:uk׮!55AAA={6ڷo^5B!B!|ร%!B!s3f<ݻʃ]TCǎh"<۷3pd_a8.Ѵ t5j 'GgϝKř3){<?wvvư1Brr"#K|4JaΝe˪2>\ܺ!!鈉)N̪YЩ1UNjŽHMv\ "qhK]6Ѣ[2$۷NvhN,d5af37nd $$ D"!!iiwdiSUPX}k0@UTO"N/#y>31@$oa6ÕW42C߾5Ф 4 m޼ݻNCvvjmmкll`l,Ƌ9r%]i{dlmtʄB!Bʑcy!B!B>RN#B!B؋غ5Ns:vO?9];sǏ1k3;ɓɸt) ;Wh}aݻ:1TgS0s3!~x=C$< ȪU1E>5C>ա'ЧOsxq43+W*ի:֮mf̈́O>?+t;]J߇/5mj͛kWp-V~_ K­[hUVN JǙOpp0_DD6oÎoZuK77pZ& R_BgOd#3beL=#-L ,-+App*6nEPP2HyxN%̱%)gΤ`„H|Jf+_X{+K!B!B!B!F4B!B!-[Y%ؠA5{wsT_Y3OKL~}޼yѸxMٳrٳ:n߶?ڵ*fwd̙iF汉[6Сc܊\GghUUZ?c7Ŷع yPCeyu!(VVcR;8:c̘fVxCDXᴪUpv*h#GN[۪>Mয়lY#l C VZbb0ҽ{Qx9ߺH ӱkW32C>5ЧO gahoر#bKal@H>,}vkx{͛ xK4BHG`` ={ϟC"N:hڴ)*RRRp%\|qqqHKKC~~>`ff4nh޼yy.ÇqY~HJJ1,--ѲeKm۷v~*~!X^zWڴi///_kStt4;hDGGJ*h޼9ZhPV@*w ԬY5 Ag|4k ͛7G~`ii)0\v QQQx)LMMakk}s;PAVVn޼0DDD 99޽jժ gϞpwwHwQ $$W\+XXXN:ڵ+*U_E8&&g333 $!!G/$Ԯ]| F!9rO&~64¹sπe:(¤IQ'!***ڑq*7@aX@UkT9V>>~?\+*hBoh|L[XXV޶oGx8J[1+:A__A-ZTE@b͏祃i2EX`F 77pвhi{$`tZK\ll,/_?yybӦMYXp!l"vecժUطoU8ƍwwwYzjٳOɓѲeK'Jm6lܸ9ry9sN:4i{#۶me݋ݻ<>}4/^W2X~=UKbҤIZ EGGcƌ8r8m4L8fB:ѨQ#~a%j*޽ ]rVBPPc7|///EW+W" 3Wb?~ځTT۷c˖-}VV Æ Ò%K:Vcݺu2mgϞ-9K$_Xf qh޼9޽ﻺ=zqٳ>|L/}HRܹ6m7>_~%-[5j:~ ׯ_|HWWW|=z4j~e۷rJ wmll0`7w=s xb 5J2rrrYnݺa߾}ѣG˜b1}:oX?9sJgggL{.ԩiӦa̙ ./i[d ƏO|WrJDY[lAZZg`A'O` DFf̘va…ݻ~5do2m-ĉ5W* %mzzz{. !B>PN#B!BІ y<ըamۚ /\5k^Nss3Ο. p?Iش o%MmDp0%?Zp IDAT_۷?.]jvUn)n&izؿ?mI$R]69jk={D`͟^l4mퟵjbba}5o^ KN.@LL.66]n8NNUq춮JM"}v8î&Ht 鎎&صZ>&lУѣ 3ܹ65szPPZᴜ 5[~GX ի޼I~Z@@.]SNUV*-ݻw2@\>6l3ja˖-زe ;~469s'NDtغu+mۆqq}`̘18we_ؾ}Z,?Ԓt'`B^pB,Z3]"`ȑj? B۶m$zJW{9zϟ+cر8q: T\R Ǐcǎعs' +?`T o޼}vڵK nYYYmt_f44|78}45w) :TfeyVZOTy/KƧ~p ٳg_U\}0ԩS1k,,\P劏RX``Klذ6l+WСC揌W_}˗/ z~ZZ6n܈l޼WiyϰbĈؿ>R)CL;}4 :;|0gUy_={7,!ݻwؼy3o>Seu\Ǝ@Jsܹ%KpD1D,\P}˗X~=֯_Poߞ'''ζ"8 ?ǔe֣E4zIKKSlN8Eq\\[z>Z̙38s q%ԭ[WeddWixx8:u_~'OVkyGٳǎիJlaL2E:##3gDPP?k ھE9 =}Vfm%^'Hxi֬Y "''G:]v 1b֯_ӊ={… e٣q8ƍL[v(F!S~ B!B!h͑#I֮u@:*ij*My!#CmmLWP۶@{&W+ij9R6y&_[Z Ѷ QnذjUmۚV:|-B8u W p?`{{c4ozFS"89JU Zqd|Xr=Ѥ$ݥK鈍e!0Ip,ccٽzu]+Oi[Ԕ}繁^Bх+WK.Ji%&&_~x* oU9&8P~K`ZiRTi5Ң!8VZll,#SLSgϞE^J[d gT*ӊb+Ν;+ e˖qׯ_/(VcƌQi[އw6oެr0/_wViӦ vq7Ne?~SL+-22;vDT"xEpaGa0 P<0;99ڵƍJ+((e =sL̝;Wp kTqE ___P|rrrЯ_?4࿀8ӊ+0iBH$SGWEp0Da޽*͗ ooo?>fΜylߵTZOsSRRUއ^~-!x*L3Ο{*]aʔ)Z;֪+""...i{&L5k> .ķ~ҹ:88ÇW^ܹs:uRL п̜9Sp0 >>>2Bmk׮4h x;B!bp!B!RF˗lm嗵ڱR%ݷ8:W/v(8X3G;-hڴjhv|ܺ%|*)[F$L^Z{tu5h4^*!2VU+SqiYJmٹU;+yU-Z}QsmU"#B4 ???~#x+̙3;wn,K6mڄ̖[AUJ;v,N>`С*J9r yWybcc1e'H0|pfe-Z$ڿ?Nr?1b yXvZܹ9Vϟcj3fL#G ff&Ѻ̪|ߏ~Ie#,, Wkz1DCʕ+ϒ%KWs*~éRw Baaa ;իto߾UJ5jΟ?/xɓ'ڵkj/g߾}u XDD఻ Аy1Sxzz"1.6qDcޘ1c\> o߾ը^PYcǎaɒ%j?SuH0dAD"#pIҥK6lήE"d$MV`U*B!- !B!Б#?bM\P~[׮=bN;z4 ~~4_FN$ߣ%ݹAU_j2иo򞇇9oGɓV&ܹ3ׯ AAA8gϞŤIb1u놁uAnn.޾}ׯܹsvʃ[ K.9r$alla׮]FAAu몴M>]f96mڠf͚իWqENݻwذa~'DDD`2]]]FA,ڵkr s`4o\ײ~zܼy䱡!|||н{wX[[#::W^Ņ $G"ԩSe>[[[[Yf033CXXBCCqeNѣG8x >s_KӦMѵkWtGj 8y$govv6Lqu7o`2m5/5kի#<<ׯ_GHH30w\|駜pT/ii&&&1b[[[ӧؽ{7>sr vء+֭Sr*j ݺu5rsss9s2dz~YfQ233{̙3y,Yooo"̟?9дiSX[[vAjj*"""pu9sFdbb"zܮ,,,0p@xyyA077۷oqMرs>Űa#W:<<1557nիxgXzutIJYrr2BCCѹsgѣ+/55zbVT233/zꅆ III}6v؁GdNÇ#""CGEE-:v#FaÆ%޽{o޽{~:N:<&8q"gMIIAdd$_ӧO &۶m[D"[t^eaÆՇ6i^^^Qͼ8!gggo^i?JA޽eK_|֭[HJJBTTΝ;g*ʩ̡CpzzzСakk H7oƍz*#==?c"KC޽բпx{{QFDjj*߿sN-ܥK8 NNNQ?~7n $$yL^p! ###20a„ǿ{uԬYTm۶4rJιިWb1?K.qϟ}1k~)Zj5k"??/_D`` 8`رyr̩Z8֭[VU$B!!B!2?cǪ? ؠA0e} k<"8MuhlX;Ѥ]i^UT#ߣ%ͭNxx2-xD22!woߞ}`?ءZ9޽Ltzɑ}wd AphEw|^ 66A3O02,<ֵk5f{QѹhD !ϕ+Wd*@ 46mlE &cƌ_gڵ: ]v n⴯Z SL!;Q2͛߰k.L8iӦ v܉f͚1ׯ.]Ob֭_-jgصk|||dc 9sƹ}L#F`Æ 07ٳ'RRdoc,[ ӧO/iee[[lȐ!0]l4Dua˖-jt]vΝ;acíOOODDDȴ;w$HQTR?#;J4hi  ]VpFݝyݺuɓ#1;~8̙3JժU? 6f_! qQL:!!!hܸ1g맊.--d?vppݻw4qGɓ's5i`Zi...8<=NNNƊ+Ty)*y\TTgz!Ng}V_B!p!B!R! 2uyb.BLUju &U‚=hŋ\ye|`kE}I*<FExZ8=h'22ܪXTOc\\L~@_謴W TUpՅN |DccSRT0X!vV)YZj'h6lPͿmݺ:uR<̘1~ A9udb1 DcƍU^p!T½ِ"۷oGLLL-aj*z < V&|766Ƃ -S^VUgC#F9s/kɜؿ>XVXt FqQQQ]cǎqڄTM[hL3M&XbnnSNq,dVfz +V\t[l>>>9Ώ/ٳխ[s崟>}]Tf&&&8~€9˅ pyN_GE QV-۷Zݳgܨ-K2>\/|l B!!B!210SB) 5WB^mڰ 0 I$R|S-cս™ݻK#Is*7fiMwqgiؐɑ`n5WLH`+{2yЊMM`da}iU+]-,Qjթ(ٷo \V)jaW$2ƍy/Ғy'{i{J$''}rW/^ kkk7nݻ\%^j?ӊ͟?uԑiۿ?T{𾚠`a!{cL<}f2uT4iDX@/JjՊ+>>s͡-mڴDR?@۠A4Z7B!R9|أ!B! c`gBܜzbLM $ё]HcgO0QNj2D͚iNXRڵwv,aP>U3ߺ5$aU7WN+("Sŋo?@F{'"]jeeR7oޔik׮'XoN寚5k|SSS9B___NHm74hZULLL8Q *|w*LLLבVuℳU ھ %_@«N#B8P8B!B)`LRirZDD6oǼyQpxyCnw VKLmӄW^MDcFSn;{{ Gᴊѣ,r;tP!_DD?矇Մzó 9;B,& }(>̂\f=Whʘ>N!,26]d+ ՠTu%''իvUΝ;HKKi366 z;wNuSuC9VVV*fZhi{J}6dUcT䵨kזi$Vzux{{<ҋOXX0`ΪjOm`W=z1p@΀[n>~ůZbm۶zCCC>}S!{o?@ſFP ) DDD q8nݺ* 2DpXmjߩR ZݻwjUQ UVU|SeVؠ N>L ݭ[L:Pf|"##C-ZiӦ!B(F!B!W% &9|렊WU25q"|Klql .^LS/9f8Mہ'?Z ;M߿WwM_/^ju9D3|!,Tϯ$qk(ܸ֦ |'OrX2CC=h ##=fJYUrZt4߬i9;PccN#Z.]TܜHMM*q򋍍ŋu>>!DΝUV-w; YYYqRRRT^b:uRyk_>lllTKhZamm-X0Bv픆^XX鱗޽{^zl}ekQjҖ|uL,&|޽*aoov۷)z|:wb" ĉ`U[aUd_U^=f0X:pڵkssGAv6HG=zyՖ-ZjUٿ ( OsuuE*Uкukmtt4}dee!<<\2UMxmQ;>{G1󣑑گ8ߟ9_jՊ/Q^cYׯZ󱮁Խ>,&]p=zk3g0P4B!!B!2F^eb|,M_/{;! $ 6iff@edaxtƍaٲxH>Dvv EիܐX,B۶WN쳚ZU>^Mbq͛_ʕ1ݠڵ Q!]"} gY&043.+PrZJJbb!-WrڻwbfJn>Bњ:up*! Ul<3SquPMQF:[&XM{ecŠߪNPk-_2EUXTme˖*ϣpiِłM*UX| y oPPLyS7 &ҒYPɃ ),|/**°asց(,ѱcGT RX14V8c)**ɓ'eV7|1rrrd4?ԩ9) =?qpB ::YX,o߾qP)'\EWpׯ fffmۖh;%%/^iswwI&2m/PΦu5PNNN+WOcVLdaUp!BǃiB!BH ^YZ~7oe,-%((`Q䧟bЭ\0!rr7D*Ux?}Ѣ _ìYp ^/.jCe_!L߷ªֲeUc\!??@iCT4tttx=zsi+իHJJiҥjnzO嗫kѤIl۶ D39shu-& fnvW^i*NNN75uwG8m/PLsR իѺΛt,Nء3yќM4JXB!TB!B! b`>33xk$)NP Da\έ9X*VŠy:>F "v0}'UPbU6Grr"#U:t`<[`$|*r0 VAB)Oj J`B[֭ п[i۷o9mńHN^4mTD"8amm-eUSw{577GbbbcuU333N6!XEܜ[X5k?PBAA @{P/IHA"m[Zp/@I3U fdd0tgt^^VX+VM6󃏏F5}~n޼<> mr\cdd///>|-33ϟG^˪6`dQQQaFEysNV!1ydxyy{f4>3rP?ڴiG+^Vů7np*Ip_J<<<ᅲի&MLbgnmfM?]ջh\ X$.TNc'AAAaCpj:}{իW/C~g嗏ʮ%;Y5pqS>}P4.] !<{'Vyqqqغu+ ;;;Z ݸBZfժU554mnc彽VoOk4MLPo]I݁٪ϚPdݻ7֯_/(HKlܸ={Dƍ~z_V8o߾ ǎS8OTT"##eZl [[[˫(e0tPHRܾ} .3v'Nhk5?:XnZ TԠANU>@j999!󜸸8~ZXgyNUeul+,,DAAJTR*56 < {nܸ!fooϩBI!B>l t!_yW,]M>&Pik6]_~(*RB;;;I&2m'N@n.@uUM#BVB!BGNOO1zZND/Pѣxnnذ;Z~==f϶X:;vQ˗.BCQ״bg*z00iЏLa7npu蠽㉁KaҤzX2;wAJjw .O>+WZB?W=t)3Ν r) "0\Bg;`fMԫW_NEJ\ }Mǎywww<|ǎ;*h/^wصk c*sAYf EEEH$UbUr`6RIRL:J^^^8q"z!D=)CCCV)sx1oرo4_DD<==>}>c6ӿ\tIѣpssc>N߿NMצL}bǎعsձcЩS';w666:^O.]J?aaa{qɾ-h?%% n޼ OOϒ6+Tֶm[3j(>zC/>fgz?; Z74 kÀ0c cǎa…&''sBuE֭-:t=zPaS=upp˱tR9sǩSpgϞ׮]ӸH}7''duppPV@ƍ%DxdH$T[c#Flbb'''WG+JÊul۰a ez3֭>eUZU}ʓCh7oʕ+2m666B!A4B!B! mE8GpZ||>BBA Ķm 3 "|%>7KqbvNCzOuѫWup ڒ6r63+p>\­Z}8u^MgoJ"x{׀w Jq\*v~Ç~=GU›|ub򁱸n=ҡݤI+B9sUQӧ+O8i`MO>Yk//˖/VGWLϢoԧ6Sf捚2%YݺEZw9u'۸񬮼~ϥvլi7{ڴvB9J@  hZlۧzj˞dvQݻm{T1o>4{vP-X배03ƥN:剒*:r籕ٱ0( IDAT^h7n~Zk֬ю; @.;wvi=xG,zm c3gtTbэ7ިzKМ9sԬY3u'MT]ۚ4icj͚5ھ}zeޜ9sk׮2 SRRXiN:S^=]H֭$믆cf*;׾={xzz^ըQĮ_HOO:''GϷo #p}"@N۷/ڻS5\irtS x&P*8WWSgڵժUf>e=Ɍ[a纝w8M3zz>{ݓP-3`۵?MY|,h)))NvռyU3~ogk׮sްNS'@11+u?2VTdՖ-iaa;={\4h?\?||s)m'''ƶlRnۓ.7c;}=j3 <'*^ff222l\Ti*110f/w[$$$6mRQQ>3fhڴieVUs5}\&M cׯx}Yx څ  !!!֭1{,wﮍ7jذaw5mT3gnXfZ5o<رG*:VZy.=U1fܼy*z׋땯6lh3vYB46v -Zfyݺu #@W/t|n_ooN{5\ijp7GU%Wך֭k=.Z(ǡO?@م %*(lW33{xvس<y:pIM~ôn]Y:ϗv~+E͚ޮ u7s$f>_ӦwBיsg$)(G'6QZ05_,b lCdW.i׮nf;+/ذ+n]]>>͛^m;g:h+ s۷oo?N:Unlժa~py>ǚm[vvaF FU~fϞN9م?*q3EGGی;v4[gϞ6c6Ҿ;:&..Nu<&;;rȧ R>} wʟپcvءcAAAwz{T^=jsYgӳgkz?.w=Vڴic3V\\pwkա嫃Uٱǻ{ډ'pBI].߿EJp 9<cJ7A[d̑pZNck֬V]lBIzF 0?-qcχpCgUjVmĉBee, y߲e㩩 z[o5yi&矯:] i^c^uJJڴ6ES7 %nݺkurB_G4jHk׶+,,_]nLII.;w]oƌ1 *7n+._[tQ/Yĥ(k׮Ց#Gtgig7hڵP}-Z(%%TV/(=uꜶaFg[wV;װ9͑[E 4=빋V[;]~)[+8x Ν[]C9(.oj(GX, 4H++ bbbԥK-Zf|„ z~0|||ԯ_?DŊ0v޹s̘1CDI^.]\!]<.|z'kҤIλ﯑#GڄB>/BXc=2DӧOsBU]ZZto7Y5kJJJi&:tfTCg9G 2^/5~x5h+PEGEԮ];-]ڵkd^Y0%%%_<έp~sLxk6c˖-󕖖׃!C7ߴ[`֮]/Uj8}jٲ6o|yPoa* ӷ~:|y߾} 77p 11H.>o)癋*J 9Ȫe~~maJ3gp{kEBB$iu1v`ra=u`t{~2bI%ZJMf5jdN;sTe;?^Ç Ld~jР}b8'o^^XZȬL~~V>_(yj<ġNpXna,&&\9x`C8m0aƎ[.0`!'hܸqwh ]^sA͚5 cׯW~~ۮPO>Kp} IÛÇ7tʡ: }g5J~{+;Tݺuucyyy:tMWkAppn6i'N S$鮻ri;iiijԨsy@<͛ҜϽcǎ6ᴼ<}68n }ǦTh̙Am߾]VnG4o\:tիmƟx YFݵy馛dz%$$h۶m.((о}԰aC/VC?n3[o{UvT{miz ߿"@%m >jbeN޸jډKQ/\["Wƻ^52H^@jwپYE=m}zBL>םwFǧ컠֨aލȑj)S%ekto؊MC@Θ1 USu{Erv3erM+k`0֯?k9͙ffsoْ+$)99DpEtN[7tٴiY:|8@cZ_+99YOQbbyÅey?:=Z͚53?9rjjٳn6N%AϞ= éSV5mT.ogĈjӦaW^СCm@9bѢEӧnf_zgΜѸq㔔ߩ0eaaӕi{I&jܸS*77Wvi?wz!?E䙙6mN%&&믿vhcǎUZ _RSS7ߨ{ݺu0`q.&L*G1s?~!4RRRٳgK.6֒Zcǚ |Iw}ڲea/4}GWuzK/FW\͛kĈZx!]|ά_^'N-ܢDM0nVyyy? W^2enVUg~;Ng{V J^@@>cI&z7cΟ?_>4i4}|f͚MFF!0ha~~~|4i{Nׯ7 b_pAK./6mڨcǎO UZ5'N_~jڴwﮇzTs o0~AuY ?`7sN}ᇺU^=7NN*Ke=JJPYv_Va@gfs5/ђ%杉|Խ{CԯݻIo.&I/tB;qUWk4O>9nr#Ք)ںս+&N<>Hro([f5oNiˍǧCWvGGoX½jaexlt&@֬Nh *BFk-^uア0H>lEqz}/xjϞ=0z77TT"88XӦMSZZrsm.p&MI&)88XkVLL t)eee9^\PRRRdnݺ)!!A{/**ƍvP߿ٶhBo/z'ڵkZj:qDE5yd{fcnnƏǫFSdd.ۮ_XX}ge;wΝ;M^zwyk׮]+}WfjթSt ==]<鲨(r-\*iT}c^?lmn2{Ν+T"Tcwo.T?dʕqY͝{J~XuըQ̼ ̓;_?P Aڻ&:^z)ALiߡC7no+>:Ajkp;tr柮+3asgW7n:tU߾1nuvãW3 >E-ZhZ;L}9^ l߾|[wFy&LסCsւY˗($-ϗ((yޕUjj&MRʱ"[9sҴj*u.\6^zwѰaLi֭eswHM>Hm۶5 mZV8p@R;jԨ*5풇zH˗/ׇ~h|޽@xRTe۷ս{w]Ν;ݻwk^ѣGuc,>5mG-ŢѣG]ɓ:ysVFwu~iˣ=>\E=i!I:uTtZڳgiLttf̘a qvDpڥy:vt]ĉu]wݏ\1|pjܸq˭V;cǎyl׊~iڴi8p mnnn )yʫYf]cڵh"r-vs{oԸqcmVׯ7,ݻ7uP͓2GhpAf̘=ڵPdzϕb?0b.9C_URRu;vAzhO=GwSVǺIon~~F0ӞKԧfeeCDj ٮg^ }tTs_|cѨQu=Qܰlӎ)woQ>ugz IDAT @=z@Ukذ̙S ɓ'+==ݥ 8u#{{X謞={ꫯR=74vX r#'UE~4sLթS|}}ճgOTUqԩ%K(!!ۥ:uhѢEJKK|[VÆq7{Wp% .c\n۠ul;/謹;{^ݻoTvvC,O;.7ZwBN9cۘ1ㄒVj:t_Wӧ0]Xj,\f3V.ONVLI+=}DŽ ? p9}9Eo1Ъ st{9RܹSuttm޼S.{ᴔߵUׁ/np)`n6otG={Բe˔Z=zh3fCVvm5Jw'|R-Zh2en6#Tvqqq7oKխէO͛7O6lPƍˡJǘ{,|b-Wٳo>7cs T6IK9^RD`sط/_MPa?ɻu7Rض휞~zf4Q\hQ[u]6hl|&LhHJ/\(ѿ}LF2t];@-Zv8Ft)9y2);}}-?F?n WPP}^nIzx]/ϝ+VzuTu #Au;dgiL~Aj)"~ĪO뭷oO\>%$iϞNe"Io77byCsuLƌ_om\k/Tl _R_Ƅse>)"˩o|.**#]xbX5k̥6nܨQ׮]|ܞ={t!;:ڵk͛ŋkڱclAAAJIIQjj^m֡+JQQ͛iӦi…:z {㣤$uQ}Q=\&IVU˗/ٳgСCpbHb(66V:t}ݧ޽{J[p׮>322sm-[**?YjΝ8Ν;իW+//ׁɱWkTYnݪmߓtMNu+((Ї~ZdФI qoӦM»c֭?.]M6i݆zj߾nFw}w.]jJբsT-Z/˫U!͛7ɓ6c7|Ks]RPP ̙3*))Ym4m4M>]ә37DUc=ܣڵ;a\W]tqK<“kƌZr233 X(%%Ewq:uT:33S۷ok޼bbbHK؞?Y뒒9rD9995Mo7ڌ5nء0ʕ+uocǎ}e5w\:tHgڞ; RVoY~=_b{Ku_=U2 NG8 CwQzݺE(54 R@v<իOʑ#5gNkk: 2䒈?[n WX.nӿc̻LsOR8J>[7PjUSl JtT6o>_~TYP_nϞJ?wkQZZ富ߟ_~Q~~a}6n>}6k8q R j.]u]Qj4Xڽ6o>)S2 a3ݿغ*ᴳgXŶOkid/R,v uo:uWM11:qY-\ s!] +Y!!>ׯJ2/Xaa|]TᴯRzz?*/ %;ԬY4TL/TfmxV2F8#VIN)77WO"##`Zu >}Z XwҶwyŹ"jɓ:{ A^ qy?>>Ulllo+((2pMիM* S]BBB԰7hϟo3ݻ{"T6>.QHOoR,>[(.ε;V35M,?yGo3JcǺ!'"ǚIN^qZ,{5Uj;MM]~óBRŅ<{cwuq pKNV`k#7G__ ^hfjXP3f̙36cz$7𒄄 }es{.u)s anszi$lY1fB4uj3Yx}bL;~|sG ӳg㕓SUNĉBeg);PEE_;.-zFkk䣚2%S=6$WhȐ8ut-==V-Z_]zn?(EG$Wf!!>*QSL1>@fZKDcڷoѰa.ݠA߿x)@=YL\txf\~8+NС|+:_ j(Hw`o ;~x>͘E99]-u"_F֭֝܋ghmn]U\lռyڴ.(E~WթSRRB+q&+{'I^Pk׮$hfpB/T $00Pp>i5kR۶ن Z;x"@B8 BdddN:xbtM\*iGegivc 5JOO'Sx干Z㩩a_ pK˖-x TRxԲetNӸqzjq:v-˗6VSFA^|׾WfԾ}{5h* cZNܙ;Pلkʔ).U pش)OgSSkx':mje9 4iN8piN#p4F8 4iN8piN#p4F8 4iN8piN#p4F8 4ixג ޮzˏouo*R֯et WZ_&|,_kÙ RV찴c !Ĕo]JE8 `_4iL8#Mz}-R{NHl[=^NؗKn+J{$+{>.PEK nv`+ LJU=i|KX]ֿIޮNnvpQԑޮqǛR?z ׻fKޮaH}ߓnJ\w+{4c,Ͽ_Jau] EֿICKAޮ]i.5+m&-~S:U:: #] riǏұ-=R9oW*HMRnR_+P i|%Vt6S?-굲T/@ s&iϲXи5iN8piN#p4F8 4iN8piN#p4F8 4iN8piN#p4_vl@@axn1666۰m*LLlALA܋MNߗ  &N &N{'IDAT &N &N &N &N &N &N &N &N &N &N &N &N &N &N &N &N &N &N 6i=x>jj=  image/svg+xml moldable_job_descriptions moldable_idmoldable_job_idmoldable_walltimemoldable_index files file_idmd5sumlocationmethodcompressionsize gantt_jobs_predictions moldable_job_idstart_time event_log_hostnames event_idhostname job_dependencies job_idjob_id_requiredjob_dependency_index admission_rules idrule event_logs event_idtypejob_iddatedescriptionto_check assigned_resources moldable_job_idresource_idassigned_resource_index frag_jobs frag_id_jobfrag_datefrag_state job_resource_descriptions res_job_group_idres_job_resource_typeres_job_valueres_job_orderres_job_index job_state_logs job_state_log_idjob_idjob_statedate_startdate_stop resources resource_idnetwork_addresstypestatenext_statefinaud_decisionnext_finaud_decisionsuspended_jobsswitchcpubesteffortdeployexpiry_datedesktop_computing 1 N 1 1 1 1 1 1 1 N N N N N N N N N N 1 1 1 1 1 1 1 1 queue_namepriorityscheduler_policystate queues 1 N N gantt_jobs_resources moldable_job_idresource_id job_resource_groups res_group_idres_group_moldable_idres_group_propertyres_group_index 1 N resource_logs resource_log_idresource_idattributevaluedate_startdate_stopfinaud_decision 1 N challenges job_idchallengessh_private_keyssh_public_key 1* 1 1 job_types job_type_idjob_idtypetypes_index N jobs job_idjob_namejob_envcpuset_namejob_typeinfo_typestatereservationmessagejob_userprojectjob_groupcommandexit_codequeue_namepropertieslaunching_directorysubmission_timestart_timestop_timefile_idaccountedcheckpointcheckpoint_signalnotifyassigned_moldable_jobstdout_filestderr_fileresubmit_job_idsuspendedarray_id accounting window_startwindow_stopaccounting_projectaccounting_userqueue_nameconsumption_typeconsumption ./oar-2.5.2/docs/schemas/db_scheme.png0000644000175000017500000335617711757171206015601 0ustar plbplbPNG  IHDR k 4UsBIT|d pHYs."."ݒtEXtSoftwarewww.inkscape.org< IDATxw|$"vHmԎaբC>Z*J7ڵ#!,ID7[N9<[o纮z8|+%%%%Et4zhݼyޥdbػ׾]\\.y ͛*Zlǚm(?Ys?^4kqw0Y rZh@ 8ٻALLKػCW] 4k'N_ {X,/)/{ /{rZ~=ZЬZj={jN{7M~ zСp]< h@ 믿TR%uٵf͚G>>>۷]k /rwڵkFgjP@^ZhdWVZeuֵc5 ;4k@UdIue]< 8Y!(IbQjVK\\._le˖UbVσv ]rň+T"Eر"s)))IThQyyyٹJHH$B vۊ4ŋ}^ѻᆱ db]vocZի5}te9U5ҰaԧO{9>ȓ֯_uβQKN>A驧2NťKԠA͘1#\)))ڼy7ne˖eٳgU^=}6jIR||vڥ[\?9!f=JJJ2sjذʗ/G?,Iڼy:u꤭[p¦rƪW^$uU-ZPÆ jڵ:~$)!!A/-I޽q$5kL=zPժUUti%''+00P:w Ѭ@ 2ĪQ[n9s+f\$mذA%IGo~TJ *~I zG裏4rH͜9SW^yEʔ)nիW… F>抉ѥKtƍ~ݺuK~u +88X,OBB_?@h$իJJJʱu3`+66抈ХKHd… vNJJҵktEډ|t]gؖSw˔)5kqPPMmҤ-Z>ԩ.]7PrL+88X#6\~]gּyt)Y͛7K/~^ԥKj֬Yڶmj֬_|Q R%l^7Ci޼yZ~Ν;g4p@mzk׮ղeˌ>3^/4sLm޼٪Ijժzgoq ͟?_6lѣGה$yxxG;vs=٩ۊ$իWO#G4]ӧ5k,[NǏj>,^:v쨾}G6->>^s… uE]rŪq]^^^j۶:wvæ䤴͞z|S]dɒ*Z+VӍݻ.]+WJn޼iӦiڴi*\5jƍm۶z'f:? )`ݫ~A~~~ٳgX,#Fh֬Y4knLHH כӧOM4Iviƈ'O뭷ҼyI&O>֔YakvZ~iM>]vR޽+WTΝͳuV5JgΜvƎ~۸իӧOK5j;w~СC}] 駟Tplk˖-9r}V?n٤IرCR߾}u,lR+WTbŲ#I׮]K/͛7g9[ ,PU\9wZhQyBCC[oiҥN4ٳULiohfcǎG,YeXtctgǨQ|r'u!ӟ5r$[nZzukY,f̘aT&MhRJcϟ1cƤ{Vڶm-[oCYSLy oVǏvݺuwn .Գ>n\^vZ#pB4qD#^rvn\ժU I_kz?5}f[n믿7|i RV4rHu%9?^WժUM (8l?z˗/cǎ6Oе-^XݺuSdd^~ebZ,+W.!5d-X9a͚5z2l*WiMW3|͹V^~Y5j,Y2ÆE۷M?{6cرz7mŝӨڷom$L#QXX:uaVŭ\N:ö/̙3jѢ,Y.)R$ݜ 6e˖)WBBu떮Q+GGG멧ɓ'M}5iD˖-gxٽիWպukM:5bzڷo7o'˵7oެAkrppPٲeu @^'^v%[s}Y… kܸq:u&NN:e5..N7nTս{wTVf(>>^}х keիZ~Fe!CiРAOӑ4&矒rJ ͛SRR4l0?~<vkޥK4p@%$$֭+V( @.\իWC4|6Y|F$Zٳ Wxx٣cZ5M ҫ*I*R{;vLW^Uhhvޭ1cX=7nԸq]{ҥѡCYFϟWdd"""wt1cM S޽u-O>͛u5i4h՜۷kٮ=x`8qˆ .NGUHHBCCuA?^W߾}M7IRDD:uꤋ/ʗ/iӦQtt"##uY-\P*U2]pA)pԨQ믿$9ecŊ:sn޼cǎiԩVřjr劺t@㚓ƌu) @QQQթS꥗^2uRYڵU\5a>|XъV@@VZ:u]޽{+&&&õ###շo_5訑#GСCWPP/m޼YÆ SRrlܫŋ[Ŷ6{_t*.QDst___1B]v/T]vg{p~6};f%KԒ%KԬY3q-ZP-԰aC꫊$mڴI0]v՜9sfʗ/o*$)SF6lh< A]^}Ui۷UKjV|_MB ~_~}ѣʖ->Ο?/Iz'4oj\;xvi:8y6nh>>>Z|UVjNNN:uZhaS &(**ʈ?t Ni=믍8..N?My-ZdըV>}4~x#NJJoFFgeƍ[֭[̔/IrssҥKUJL :T5k4aI۰3{,uI&Ys&DIjРOQ+5/*SqmҤIVe磏>JרZ*U5߸qveUݻ5jeDzgMT@M:Ո+V f)SX.Otc8`|%/rCƬ;vӧO*T`j}Z;88?ի+>>ވ_y 9B13ׯo|rZL3jժU,L:uTP!Sc>`yUMI*>>^'O4ڵk\ŢF9E拔TV-Ӧ>+;O2~wk׶O:B e{]if8.ZԴiSSK?(#6sYjV'd}ΫVj:ufjVnڴ .l:GN zh¦&A._CѣG[5b/_\=z3fnZo665KwNMZ\\ 3g׼2mZ m۶ehTմg 9ٻԍOJ"mLf'c:$UP*|UCIN miggZSLRRR:ptrrRrL'e|ɓmGhhl\(Q*N6m4QZŇҐ!C2Oݳ6K5k_RtUܐuwE 4Ȧ{Zn^ziŊ.oUZԱcG5nX۷ R|y͛7O<hjРڷou|:~>7Z5D5kL_}|7۷u 6L˖-SfT^=EEEȑ#ڸqΜ9cqvv֯%KffttΝsG͛7WŊUbE*!!A:p/_n{߾}վ}{[o9fۺuD榒%K #O 6?sҮokfc<-)OZ111َ)_Mv/.\hzy~=<<+GFRt65.zyybd{CttUo>۷϶BX/3EI{Gʕst}3ҾN;cǎzwM:UZnSNqY,5iD<^z%/_ iFk׮?o4i?~z衙3gt… kѢEܹΞ=[j֭Y_x醪-Y=zhΜ9xP$[o/^5Rω35XbCUكv}[񱱱6KFƍ63xRj+UrrrgVҥKz +]֬YٳgI&X,ݻw{=լYS?ݞ2Ҽys?~\cƌQR2wyʕZhlUBe½ IDAT8p@#F}z쩣Gk׮Y7}tkzG3s85GGG=ںu/_.''~ 4[RHӀ^jfO )GTzy\\\wfN96sS_suuվ}kʕ#>l}ΣtMrHҗ_~ݻ۔+5[NIiq\!퇿z-s8PTppi˖-ںu@igoN>)Ss=d&))*vuu55x7n>#;vLGQXXbccUtiyyye˖67a;w.5777M0AmۦK.ڵk*R|}}ծ];,Y}U߾}eXӧO+88X UѢEUBy{{jժ*[MѬU888hѢ$]rŦV *fO̲5O@@U\D *ig'00Ը}$F6J-$)<<\n2cާ>!>>^a/?0"6="444_ޫ']p!kH\zU5k|y˕+!ChȐ!'OjÆ u%c܏?=z]v&@fWPEuQ:u=ԩSWD 5k, 7q8ȗR77mP1?ӵbkSVMmUlYƳ7+imS{eHJZjSvmVmy7{ԣ>j:ttnݺV=j֬)GGG#׽]z-9sd-Z.5niOJS% ƍ[+V0=wYnvk5k֔bQzĉ:w111ڼymڴ7mdr{Ovfԯ_*5j|F|!EFFΓWԬYSʕ3m۶Y*<<<԰aC#>]rvv?`ujVdҥK>ZLΝɓ'+)))y={յ]ʙةS*>>ވwnjJd}͞=[QQQƶjJeʔ1ŋkΝz~'ݾ};qΝӟi*TjbUV*]/X W:v(ggg#6mn޼`NPٳuLLFm[yD>}zm|Cz bŊFcj9rDVRݭGe4k0[VjՌҥK1cFsRRR+66jZj;k,]|91W\A 2~ 0@ 2 d{Vxx.I*TFi)))z7t ~:ujcRRR'(99ٸ6h 9::t:t=ÞL-[VO?kҤIϬ oň̙V{$iȑrrr2%KhݺuZ믿%KMh]aaa6s 54hZh~i֭ *hذav AVcX4~xk>Sk̙5'''7Φ111jӦ ?vڴicՐꫯצ\=;wN3X-_\m۶՗_~i.r޸qj~/$&&_״ilڳ>ѣG+:::3<,7jرFzJ2w-͙3Go}СC5x`]v-'NИ1c[n{O:u˳ܓtgc~h\kڴɝ`=zhǎV\MN駟/ٳg׾KmڴIO>x ݾ}[{͛ӝ0tFuI<4i5kڿVZeu2SJ^m1zhZJ'Nt礥]gUƍcܹsFw Yfe˖UV $_~jԨ4iիZjJNNuI|ظ~8p6Ν'|Rڵkvڕ圉'ꩧvJ*iϞ=zo>۷k-cƌQHH~ݻ{ רQC3fP5l3oO?iƌv|յ|rlohҥ*Yd:w;w䶲ej۶mׯ_-3fO><>>>ڲe&NhKw>ۤI}z7M\߯'|2q^^^4tPkh۶m4iMhڴ 1t?ib /hَˈ>3m߾]:t0=[o-[fsEӒ%KL7ըQC/b1j(]+$$Dkj !}W$bjrO6m#zŊ˱ѣGۻf-~mkY EOߞwɝ*T(9 _nRȳj֬={ڻ @>GVJJu9?^7nܰyʕ++11TG˗/wQ%K<==UR4yܿkΝJIIw)GikOVZTV-{hP _s… wIY;vTnr$$$HZnMڵKHy$\^ElŊ:qg&tA~4i"{>Ѭࡱg͚5-P %$$h͚5ںutΝ;PB. yL%TJ֐R=yzj'+yzbZhv7a:ues2&ܯӧOkРA.}Y @k˖-.nݺ~M7nT޽նm[{!d5o\NN pQ^)/ʋ)/֔qҋ֡Cte{ _>jʔ)t钽KSbbb4k,QF.C矗ȓ&sZMy̢&s{t9:tȮ5Yyn޼i2@%JػF|+,,L|( _~E>>>*WK3gϞW_}۷oۻP@[O?˸/4kȷV\I@6nݺ)S裏>#WRRKA.,xsƿkZW꯿wB@@ΝW^yޥG}T#Gw//uJVYԙ3gQgˋnٲEg϶w9X|]@c>|ePY @sE/^%@jsѬD .lz7keʔC gЬeBRR=]v)0001oVLLL.WxY,-ZTcƌw)HKzR ] P-RRR؏垚5._cǎe˖F?_6=όҥKqu}r\]]s*@ACCpÇUV4h]P`$%%e.@{9X'!!!Khm(RRRcկ__ 6w9bƌ$ع oY+ !!!ھ}yΝ;iF ]gѬg*))ZbbR&@AaX`n߾o 8Z-P888X5k%%%)11f-@[rvvjժbŊX*00PrssS%㏫L29'uǏ+ @O3Iw~QkVZ٭_Ic=cf:vѺr$vzsƜqΝK/ٱ"Ȼmp_Ξ=qiݺu}c޽ƌZj)::ZM654jԨL׾}/[N֭3[F @_}Us{1-_\.\P׮]%Iׯ_ӲeK988d4]#Fó{嗵gIRJ~zSBCC_~3>̘bQݺuohьp}'Zd"##sppP˖-駟qƦ׌5eʔ 뿫xj۶Կ> V˖-xz뭷iӦ_Ց#GtM+Wp3g?׺uqGGG5o\tbΛ7oj֬Y8q36mڨSN߿-'N'| 6(&&&...jժ{=iT=zѣG%\jyׯ_WÆ xذaz3{)u͈? 4H}SN??\k׮UTTTǝ۾}l׻ƍ6m&Md|p9#j߾:u_|Qnnnsk$7776kHZ'k֬1e֚5kJZntR#Y 2AgF!xXBk׮դI;󸚶NsJ%d(i ,en2p%Ce!s,oB"TK%4R,4owGz>=}k}֞߯k̘1HOO>"q3tPJ, prrqU4&//O ? :g[n!$$[/33])((+eeeu@~gSM;իWxb"zl6aggW^E۶mEߓ'O0m4Z<|Cƍnݺ3fڵkvFΝ;D6_QXXb:nlܸߣG#?gϞmP<E޲ IDATL0 ˗/ D`` abb"͛k.SYY`c8ydB,JTֺu]Omm-M&l-))O0uTy pttӧQ[[+ϟ ___\v &LXk֬Çnߪ* $$ӧOӧܾ>ĤIx())A Dyy9rtW$%%Tyȑv$6AAA())¢GD @ 'DHHFD@ Gqq1˛{@ @ tx{{cٲe<;vccc 66(++ŋq9;BNNiiiHMMEii)U'%%DvD/""ӧOGUU:!---(** UgƓ'O rgTWWY򐕕XKBBmڴҾ@)999ܹsXhDAAǫWh⠸8 >݃:2331w\J$%%=z@SSϞ=ûwf jn )))G@|5kM%..={]vPVVFNNӑ^l̝;GթS'l|MEEf̘A 꾫Q\\/^=w,;;SL ա!==]$ѡ?γ}022BRR޿O… A@@cᏢֈX,hhhoWXXq!**3t HMMEjj* VVV^|I;Fo۷oCB茌 L2&C^о}{ 77HKKc$$ߑb̝;*;;;$ =U@Ϟ=rcƍt^X@ @ DHN=z40@ |GN:I:H @ HNNիi ;v"(('tRj׮fϞ3gK.`Xl6nܸWWWy@cŋquTVVbƌ:<<<0tPJXfC̟?LbYO>;w puu+YLL %y9hB^zݺusYY]{{{*iNJJ ̙w q䄂X,l߾K. yaa!qej٦M0l0ݛ:h+WbPSS[?>>ϟǥK{LyfAAA@ciB-999lܸK,l6ݻ[[[dee"##쌽{_Es[`֭[N:[;w ~OnִiӰct@~`ǎT`lذwǏÃr3f ֭[###JYXX~0[pB_v<˗8qDe˖фZ pqq$%%嵵… )A^pp0mۆm۶]#rrrhe'''oߞoϟܹs:f@ ܴjՊmȕ@QQQ3yԪUf @grrr:uj%%%TҒ4@=ѵkf,ڶ777V3qTVV̙3TW^۷o3AZ@ @ @ Dfڵ4'ٳgÃ9XXX 44&L@ZZH=z}􁸸:, 'NرcannN%] ׯa`` bС- 2aaa022iQTTEEE;;;;ڸq兖-[h3g΄('pxzzbѢE+(($N:)S|gϢ]v8x  _[[K,Xnnn@ :߿z'~g'ZZZغu}OPIIIxyyAJJZvcxZk׎UQQH/xܺuk9sGʼn)ȑ#`ٌ9s&_V=, v킮.,&&O<ᩛ/_PQF1$$/o)mhbbb+DYYuO>}O>QI@ @ @ dŐmĉѥK94hjjB__* 7ִ2(P/loo99M>;wʉp, +Vz>>><~J+׻ l޼Qo"$$*ϝ;%Ғ*߼ys6VΟ?O+[Q5kDtW\~-[0w=$''Se}}}矍ɓ'K.e>6vX 8*s~@ @ @ °aàuuuL4Cht~@ @ @ ~IbccieN)ac% x͛7"577gTu%PGTT$,Y¨fjYdd$#ѰahY˜>}:P\\LX,gXXX@IIQͅ:FnPPto߾^ff&222hǎ;B^^?8pVVV 4 /_ݻwg~LHKKCII 033CǎEwaa!JJJrNhW#Fugggyyy4JN$$$6t %:F^^[:ƒ/_bALLLm`$''#//PUU.F UU&ׯq=|hӦ ߴT#==(,,D֭Ѻuk(++}m(p)*;;HMMEvv6,4Do_g[6DFF"33PUUŪU&pEEE˗/_BEE;wȑ#x|ݻwGTVVC?hGHVVVGEE<@II ۷G0p@UII u,Y &ȈɸGheZpoTw#GϏ\__vvv1D3>>sϗ044Z~*(]DDܳg&kHKK}KJJb۷ڵ+.]?{)+}ڵk)q7[ƪUcccʑoƌ7o/bbbx:t(֯_=vqq 999<|x=vZ-[&x7gs4>|FMM (--:VVVϼyp7NNNj-'<ڵkiΏܰX,7nnnڵ7˗Xx@^))),[ ΐaΧOb޽vZPQQÆ ޽{ѡC/*l6&j3g<==QPP\~}V+۷/UշG㯪‘#G&tWPP/_hZjΝ;~޼y˖-r-[`ҥ Q?cxmuuu1c lڴ *"$¯ - wR_ 2 eX@ @  ))g40@ \KZ_ f'OٙG  ddduT02@TQwy*!o^USS|oߞVȑ#۷/puu* `ܸq nJDA ܭw.6JxpwwGpp0b۷탒`jjqơ[n 9''V8-&c:~v, M2 Z^М_=zFFFT`^qq1a?~<{СCBi߿ϸ~hh(=z]vaѢE"UѣGvZ8;;];{nl޼zl6퍉'W@@lllk|~TVVbϞ= [LHp̛7p58::?1j(knnܸpܽ{Wd<,_GyAQF[la~___̟?}<@`` ӡ3/^yYFTTib$??cǎ(䤸ׯŋqΝoruuuŦMh-ݻWjjj0fGba$$$ח  :ճ>11"M8w,>|`yƒ 77W}J Ć ɓ'I`sII X?Ys!1sDLHKKk MMMܿϧesN rrr @yعs'PK]] cDEE!??n݂7 }}}t:u222P|@]PիѾ}{?^>_fCZZݺu!ڴi<~>}{yyaȐ!2e Ϻ 555 ]%}};w֭[iѷo_<܆*++1k,`„ z?_BLL 055^~hQ?p]JO>Œ%Kh4551n8CEE@zz:?~\0uTJFTT0zhDEEt ӄZڵC=бcGu۵k6nH[hkkCEEo޼A\\?NquujPw-V6m`ff~ALL ǧOC8880߂;֬YC[&%%nݺ{Gyy9RSSǏRǚ *",,,Xg/_`<{:::055oRϟ?ǐ!C-ooojFFFPQQׯ\1l0;V:hB-111_~h۶-ZhA c$&$@ ?Dk׮)lNVAA!503KPVV.b"ڶ:t8k@ @ _)))I>H)Ɗ+hˌ1~xAII JJJPTTUgڵ8|H"4-ܙEIHH@LL ^;ׯ?~g" <m۶ٳ8z(F!x 9]šڨedd.744DDD|||p uHII={pyxxx`ҤIu ǡC|rtqqw3fb_[["ͽϏX~=MT?ׯ_cܸqCkvv6Ǹz|||`hhHvm~}Xf899VgϞgaffw}:~pU̞=ڿ޿;;;\tQ_+9Μ9# ,,,ܹs@-ۼy˜۷*Bm p%DFF27@ho޼Azz: -- @MMI˗/M6޽;OŸl `o"==`XhӦ uM|HOOGNNѺuk(++shٲ7fŋ ++ UUU6Zx\|͆6LLLl塦۷GѱcoN,fkddd //PSSCѦMoZ$++ ϟ?GNNСC65ƫW"!!6mP ;Cqq1ӑ(((@YY***2#''EEEPRR:՛䘖|ԩ򣪪 Ϟ=ǏQTTDr(V~OӧO(**"ڶm޽{7-))/jo߾"%H঺?~Djj*!..eee(++]vy'M""@ @   >@)ڙ5bѣ={vE~'VZ1k! IDATj'j?WKI,\ .ė/_Cx%-55'Ҙ~4[N%$$0g̙3_~Çӧ<++ ӧOGhh(LMMivwvkh~TUU5HI~֭[#//@ݾu BJJ EYYO >>_edd`ҤIx/#4 ٺu+%|j W^żyhQpyF[ űw^@~~>\Yf18}4x>7޽{G$%%!::cUVѮ -Z:m݃5BCC8nܸ^^^ _ۗo?jjj5juwqqy:P/X3g:1111#F(ԋߏKc``0 :@2Ĭ.]w?{WWW֖, 6ldcc=== 4ŦM̷={hf&L &hٲ%0g\tkjjpBںO:tHH_UUk֬ի|LCdeeq ?ڷoKKKXh ƨϊ |熒]===$&&KkO<}|| sŋxbFc&!,, @#{ݻq5ki=, FFF 2JπuV?>}PnܸqǎCXXZSSٳׯ_K.̙3oך\zË/֓€`nnӧ]v ;33nnncpRkkkF]v-u!##gϞ1jfff8F)0XUUuFMm۶|}}#::֦O>Mff&87ntGȑ#1yd7G.h;w>ogXݻ71uf~'..ׯGhh(OB.EEEL6 7nlt?bǎx 266Ʋe0eۥ .\={[ddd0h XXX`̙իW鉈Z&Nv kkkX[[ȈoR ܹsiװʴcL>*{xxM9QǏpqqϾb`ff }Çi5557oO[ߩb-A;IP?ꃖ7W"oApΉ}Zlcb޽Cnn.<<!$$GEEE8~8郈oz ٳod?llyy9ݻ5k$!!Ƙ>}:߿/gbb"v!ÇHKKCZZQ[[իWcܸq j]Qyy9>--򣬬VO>laذauSLUQQA[T[[KPy/Zfffu_P}Ŋ+mnrrrCO>8`000PPȑ#prrb "M*TUUJ[_EEj4܀@ @ @ pf ӧOL- uq򻋵Z4'Ң"]޽{G+3 hlZP]]*/_Qˣ)>}qqq2%%%!..6wyUܮJ߿/%ȥ3˰(R "[vai~MLLhe 23/>>Q@9{.-hҥi]Νaee= y K.E۶m-2n_E-Zh.&NHO111,_Q+V W\#''G+s^wp?A̘1&o0PiӦAGGQӧOSKIIaӦM5kh ![YY5 ֦Mx,PPP϶m+l6m?b0b R@AXf O~ʯ^[~羄@ X.]aVZaСի$$$h={ #GyΆ%֑1K;Obǎؽ{7~cӦM&ASSSydݺuXljԉ~;tÇcȐ!X0x`L26666ltݒ[Jo¶m.x&o$=_R0,ە@ @ cffF+sDNN.\.w > *Aܽ{Y^ ;DōӏSHR^^3n˝["W`VSnn޼I hE m۶Ō3rmm-޼y~OJ+_ty(**be_}]QQQ fh'??UfXgDQQ HFi]v5zEL UUU+ 8@ p'L(u?Fqq1K.TL@ʴ%Q p ̟?q[}6ݺuqqq<[juuu|±D\\ӧOgTWJJ SLxcƌaT>gllL4D.]hA?-Lg 6662撠Yf5y?ߓo҂---ѡCFmhjYt cʳf"-x i[###DDDÇGӧOӎ7olX,l޼YYYxΜ9X|G}vI?:::Cjj*Kdeeô6Ou "##iŴ4 xxxЎߖӧOq xzzaaaHOOm6 QQQ믿h0D@@?~O>СC47o֖ܹsj nBff&[| nnn66mիWi,--s!""W^Ett4rrr;8;;ӄ8x -UJJ rss{ܺu çO#wxnx)ɓ'|ϟGXXo>+Vwww%IKK_E[l`ǎ|zA{ | wK.>>y˘:u{HIIIL8~~~ a4 ""@ @  Ҵy?~<qwwejmnݺΝfSIJJ҂#w KKKZbbb"iS>|ڵkilmmEʺɓoooQ.]8~%RRRfD[j===qqq899і^Zd|%ݻwi366ckk ?_zyC8V޻wPYYDl22GGGhHIIa͚5exH;ׯ_5sNȠG"'?'UUUWatQsŋxM_|a\W1gQӜTUU~#QT DrvZsV\\6n܈M63f 9Ӹ3j9 N:mmmPՅ[nk~2Q|k #E: poSzj%oy)(,N:E[YY ___ؠ]vq "%4zgrn#?z($BL2=3gϞ!4jjj8vmSϡCh{AϞ=-Ão]gsÆ ٞ{ ZB/uuuxzz 6 6m-B[[DwCaٲeݵOa>|X>|v)$\Suttga„ LLLfsXd p :022!Cp9Z 7X~=^J-366曥cǎ,հƾ}cee#G ϠAh0qDQQQݻ7BBBf^Xv|IMM)<(0ǎCϞ=qu.]J0feeBEUUUsOhhhL.ΫNNNHHH@YY]v Hɯp} qέbFD퇻>?QBCCw~|!!!Xjttt0p@ZB栱ہ/QﺃEEEFpϷ{6\pƊ7X@6dM 9Ӝ9s'N =@ݸx"h"7ʊl1-- =b/S,$*hjV^hYYΞ=KMMM?өS'R刈sgAmWXH6|Z"xfD:iiiaa!m_377pƍmg>>>ʕ+] ,[ *??.]ʖbVWWbs-ZSSC{Ton'UVe˖0Pwzc8"qyu1jǽ{yy1sʕeL>V[x^UUÒH4\+ ]vvv]*7k;pTT]Ą&^U/Hs2ߚ:{ݚ灨A[ohhhL011ɓ' 8bccaffq #::UV9s&v=z4Cv1bHc411cUVV"..> oT>Y)PpaSпc0b ʰ9@5Ғ( SSSQ-- KT C-q=jlذAG:D#ؼyzlܽ/#GTaB TUU#x_!rg I {Ll-[N@ @ өS'ܹsZwȑ 1̈́;h ,_Բl\po޽{חVgA__۶m7-Z^"ll߾-[ N aaa ۦm۶~:z!R_8qlmmQ^^DAtL>dϟ?#>>B멪ƍh׮u77 q)jׯ_3J# &ٌŋ3+((ի4g+nQpmNЧO0 f8q"V\S>pae-7ަcǎmL?DrB֭E2"..N`UNFU֭N:5HGGnaaAЫW/̝;SNȢTuw;III{յ ]{5Ec/1{o"*h (BD w [++9Sf>[j-ޭrB֓'OG|ns߫WJJ֒gii \.]m-Z²ep;ڵ'L*֮]Y~355EzP^}ڵkqqjj*FLnݺK>׳g0w$&&B.vhР!TGڵyw.?8Qr2f,ZիWG PV-xW^ŵkW~'ORDDDDDDDDDDB 8rΝ;Add$`cc:uhڴ)d2Yܾ;w.K??V٧\r:t(LR?W7nիW xXCCC8pׯ_dž s#""B]ʵaԩh߾=O??lbbȐ!\ݻw\..\ ZƏ///*Xn ?DrrrcW]K.*w6mݺ5֮]9gii1c`ؿ?=Yf޽{P&Oo&ۊ*,YիWǏ?(TL&UVX|9WXskcѢE9&>d24j{uG%J`Ν9r$,XӧOvVݺuCnT.ԩRRRpyzlmm1~x 6999ʕ+2ؽ|ƆIK`%W (I\333aQ]xx8%/`UN4N+,J366F6m]=zYfȑ#B)S`5E|58Oy>աL&9YK%mX=(QyM<.H4h to2e ={ zٳsKD9ÇrH۸ ]Ixx7@WW BH+*qyĉ8xynjj*oߎ۷DpwwG Ю];4h z] $^U> ʕ3W D߾}!Uc"P*..NYz{t]>;Ǹ͞=GՓalذ&&&hܸ16l;nݺnV)jk1]VT(%k)&ln)ILNGGwMiii*aΜ98qp2d _⫯ʲCqdbڵk;wX۶i,_~~ vcܸQN88H{_iii7йs,}NCJ_~W?|Mw'OL&ܹ0bķY޾>}<+,Y8ω>Z¥ײeK\rǏc( -Z_y,Z-wvU/-,,0o}d9^V-?O<{U6m*i5;|wHOOݻw(DEE!)) 666-WVbcck:j׮ )) /_ƣGw 666C_,/_Æ ðaàP( ** HHH *T;;;TVMݕK.ҥ ? . ""(U 8_5p5z FFFCfD ʖ-e1Fvjժdիzꅰ0#""_ amm5jnnn8z(LjAbb"acc[[[8::H#Gȑ#P(p]<{LxL$&&VxLD\"ROڵEN:#FH{)IT%gɓ'ޥ֠A8q@gdKw]( IE ▖yJRR ۇΝ;̙32v2Q޿||R}-zKUF *U /I֪[}mɯRϪ+VG_5Aq[Ue+|԰a<ǔr9<<K2?}}}L:5q}lݺh޼9Fݥ 0Y6m "OOO,[\嗒˛ĹsdϞm!|iiiáCHevȑcҥlR 8H!==3g[~z該k-:ڋJKG¿r9j׮-i.\]]UViy\;3fVlYtM!eYRBr\x 䎡dhh/_|Fd^:WT蜚&MI&3ڢDJ y%PfMԬY<6m`B{ժU:th IMp8v?.oA)[,tuu5VoTFAaԸqc!Y V^ 6:NP`ŊcM6tΧOԩSh߾}};&2 ݻwh%Y ֬Y /""ږprrh,:::hѢ? cݻw믿9E5kK5kh,ڴi#<ڐ$7_"9*W,TSr oIϕ\pAh/_ժU˗85k? 9LDyFr:::>>8x :ӧOgI2+w^,ZHfs%?+eEy|u7r$L1cƠSNزe ?eაۡClذ666ZXF`-.3Kܹs=?Tɓjew#DׯvLNNƯ*O{Za1QvEB-߿%\QL]lxsvvFͅ/y\-\0BT\YԾsNcRSS1cƌ\4\.1=**J }-۷o'Oݻwڵk'˗/~ |(?9^S6oތoo͚5ꮚʔlO0ABN\\\Dյnݺ%lh˗/ I96rHѢիWk,4p^ʷE oK,4N9)mРAE*?X݊ DURDmuDEE!55UhKMW<˔)ٳg忐IM0!u۶mChh(Μ93fYfYH0i$ڵ+QhhIV(xFU.qux4Wvon>~+7nPZ }ٞK&gϞػw/"##Jƍ$aĈ>R~lk1Rd0Sk9Yۢ<68::b޼y ijgϰe_'N@ UlAaV1Kpa011uw!p95j~DcGfEmǎ޶,o‰+$%%Iŋ,_80t=DFF m&kfΜ)Zx?iҤl+Ƥ`ĈضmD 6oޜcRbb"`(H|}}UkkkQ+?YYY{B;55{Uv,BG֎.\ܹsB̙3 ZʊX~=%[yCc}}}U5->>x]}>իW mCCC 6,_/DIׯZ O?fcNJÇBT^]HNNFN$%m,kJJJ ;`?gζBYpQᘡ!F/IrJBPH  mmD2eʈ7urBJvU\94333Q͛rޏ˧MӧO˗صk\\\D&N}_E-RRR$W/*ʏ-M%/_^DaPL}sVGRPEOO-[ĬYW^aY6&9rdea;S~V6IR~cؽ{7^|;w`MX\UVi1J`V1sUQ{iek}U֒\bE'4h@h߹sG Oap-eʔA۶va*Q:v_a8T\SLTy-V\qWWWիB% ƤID:v쨥hrww(P(0|l3fCw^L0M4={d,722u~L.]Xt~@-p ̷%!!M4UpQ\xQ/sGe˖Ef̈́:w۷Ӹpߓ'O HtRׯYf8shAׯuVm^שS}{2dH*F!!!8p ~gL&hSCŅ ,:ҥKҥJSa`` 9~MxOnEʯ_ƒ%Kзo_ӧO*Bv~EbΜ9 Qŋسgz''',\0ۤ~/Ϟ=Cfpq^8?M4'O`lloگ[NtM߿www,]O>r["""i&ԯ__ܦs΢^^^4iۇs_OJ옛cŊcsA~!~BwY%-ZTNB6mPR%,\>TYݻwزe ڴi#z,v֭ %,"""D˹Q~SV-IInP( Y*6+JDѣlR8fI%Z<{L -ʏ3wiii4PPXM~F_]ؓtuuQjU3ɉuSI u111Aq 4lP8%YT3RyVZusB|M5eݻWtٳZH{tsB 7+VݻMO1{{hPN1%%Oƾ+;nݺ υ$̝;s΅!*U#>>/_r]suVlRHԋEݡ5kB.HMMƬZ ,\AMe˖űcСCȨ5eL2&&&ӧ$FCClZ*-[H4Zlʾ/_-VנAp x{{ ߏիWǃe1zO>&cڴi6mr9QbE#""!!!HLL;w.:t蠥>/ 6b׮]9nܸ1\R={aaa*>\jѢ}q̘1#IGGGp޽{hݺN8 Çѳg\OKKæM̥r/_"s\|9>}=YÇ}<1i$QU¦^z:ׯ_㢍.#hd~ L8QkF€cUdhѢ> ر.x5 c}}}}*szϜĕ끌LLL u7""B!Ev?~3gRJz Bwj7 -(q BǭZFfff*]CY߰ۈ, o߾ŇrQ?QQ{͉',2 =RB Yv=Ǐ.,!""*r96l777O.200#0k,$$$rXPLl۶ -TVZ%$/&_1uT\xQ 6ƍ1o|∞ϟ`xrWF \t E?KHHPYQr* ..n*J>W ̙#z W]OOSLO?ֺy0!iPƍ3fwxDF=k.j* 2$׺ϋ6(W\ċ|ޒ^W\)jK9m۶ӧdTY`̙ʔ)#j۳gO!Y -[&)YСC+VR1#?#ͪ_,^884jժpww*xk׮s|ŊѴiS_2*.]]mΝ;/")Yk+UT M4 }6-[ɓ'Zj:IBOqǗҰr,[UYtݯ_lf1sfMAϞ=}v0\-uŋEߛQ2ed-)?7L*F޽{'z8$`ee% #5{:o M)cQ=uf۷ǚ5kp9eV%///t֭H}T;/_̗K*=z`ѢEZ]@ZԼyo߾L"""M.cСׯN</"""@ƺ5j{M> :5ongϞ,UuttвeK 2;wѴiSON;WTI7Ùկ__XQf\שSNŋqu|ሉ(/믿F޽q<}x R:up lڴ +nܸ!ZhҤ ,i.QYfBvvv8˗q9;wW^69L& ѣaffV,DVZIHy9fϞٳggKL4ItlȐ!jUںu+uc}жm[Цɓ' Z@FYͳTIbb"ѨQ#h<}TT'7ʛEӭ[7L4Ix˗cS̜9Sto BGGG}~z?>$4L:5yaĉѣ^x1j׮/RdUMJh/XM4gMBB޽}ZnJ* /_m۶aРAَ}6V^yM&9|4iD`np޽,zIGVu۵kWWWa >6lc0غu+zc*QڢSN\E1** ݃fo:t9vQJ3sԨQM*___DRHF+P0YQ.$-3X<&)y2CXZRRrr ^^^P(ELL bbbK3@;c,--}vTR>3%J@Ϟ=sMGRO`…Xp!bbbc$%%055޽{I}w$Um۶O׬Y3Kf޽{XM\UG1rH9qqqx93338::K1(8q"&Nϟ#""033CJڰodlZ۷oamm [[[.]Z <<<+x_Fҥ&L 4[ڵi&d2!** 111000 QDO[OOOx |LnLNN%<v!y̋3պukn>|@PPbbb[ j',988`ȑܹsptt!!!›7o`jj 899yi0mڴO'se˖hٲ%̙Bׯ_#** Յlmm}C A@@ؒn~С뇽{ȨձcG3sQdÇcƍh۶mdF #1cBq[ڴiOOO,X ddB˗/صk:tP 2 ˗/% >aaaY$}۷000|_iÆ ƍdq/1`̟?*ǥCsNt%KVHH ԪU | %Iem۶I&šؽ{wa=ƍ ] {mڴ;T~?cԩc˗/ϱr◗.\5jBc6hC͛d$i6lB+0qDf%cƌv0{lܺu }ŰaòR<@^DAlٲ,\U@_))`+ǜQy?-j7uo0<\DDDDٱc;-Z(O: {{Ē f/Li_j+W`uBz`ĬXd2ʕ+ri;boƌHHH<ʕ.]Z{:TRXtFZF#""*2& @ǁRʼP6(]t%7^cbŊX,077/o~)7NNN.]]"c{#Ld-[bҤI?DڵQ~}y.]*p}dllm۶=sL,Xׯ*V777TXw+W)3`Q`Μ9x9:$p899J*\2tuu'Ox捤E@@Ď+WbÆ ^:jԨ[[[<{ O>Çʜ9U[l^x'O ܹ+DŽL&C\\>|7o * Z۶m1b!!Ν7aÆ]6={[[[FHLLӧO'O'ozf͚  !!prr;Q.Qe: Y:w 777tMkNe۷oqU4hM777TV wKTwss… UM[i&lڴ VVVprr@`` oѸ>}`ZZ{U̘ FHMM\*""c.MPRN*|:*NRRR$ͧoo\+X&&&BU0x-r=*)+WĨQZrr2nܸ7nr̜9^^^*9ccc>}Æ ÁIII*JWWٳ? YRRRp=ܻw/9<&4iŊHJJU0q,[.KØ5k.]IEJdXnvؑc|~Iy8<6m2e`qZ) #G8N:8y$L"y^M244Ν;͛C.9%fBs'1{lܾ} I888`ر8<֬Y+\7bϞ=9V[mԨ.\3w\ܾ}9::bܸqWy]jժ-[믿M)SӦMӧOѭ[7Iqi?ɓaffc_cccxxx`Ϟ=ֆΝ;A-}[XX`ƍ߲ҧ~{.n݊]aÆΗvSSS={[n͵lʕg>DӦM~JLL̵0w\$$$͛C OsdbGY/G`XtyܺuSh-[_}Ay׮]#)YK9sXJ|Aӧ/V|ƍ0|\ܯ[Nr1""""|U,Djժaq} Œ3! U\2nݪE$ͳgPNeΝQfM-DEDDDś7o[YY=L2ҥ)!"*Vkυ4h h:::k2z/sd2fI*ʖ-ݻk|^===4j5ܚ&мys4o<ϥƍX}O]*UBJ462\~Y~symZZ 4kLazL*fʕ+={aތiii8 {셵1ob葢cC UkAիW1w̘13%K㯿. mGm%TT [˗/1z(Z*ܹ $"""Bu6hݺ(Һu&;>dü\.G^l2ghڲeSGo߾Uռys,[L#(.F*W1chtN"""ub$DDDDZspYDFFܹ6mʕШQcDbLL N>)S&JU^ƍ|>KKKDFFb Ō3EBCCx"h/ڶxb4irQCDD,+WҢmV,YׂjѢ;{ɓ'kcaa@DDDDEٳg3d~;9sf#"""BCCqM>|;{=zzzŋ0|0zJt<,, UVǏ_ mۡlٜ޿ybӦBu܏իW_XxIMDDDDDDDDDDDDDTٳY`޼yZ`=zlٲ/56Q~;J.gϞ(1Y3"[]tuu1dP 2x"ѯ!Q|y888XX*Wcǎg9QFcԨx"##ˡbE'*UJhS>i\ v7-O!* aaa{{}U^""""( ̚5ޒ{ G@?ؾ}':ߑ#G0|7Y6rPYww]j%%%O,pUٻw޽@dDDDDDDDDDDDDDDŋ t%Kh}h~1b޾}jժd-*RΝ[c0Y&kAwѱ˗{pSU㓦yh8:..5hsK$jծ]mڴAz`llׯ㯿.WsY :{|'ؽ{ҠoOOOյBCCqU~ 3g|R(QK&ã5jz!. ^?8ׯ@L*U ΰ*uDDTT5l&kAO?v aCD@+WKDDDDXl蘗@,^9רQc56̙38}n݂H:ٳgؽ{5j,RJhժ  HIIƸqcqryno~8y=* ]vswo#Ř1oDcӔ.];vvnܹHNN| 9ݺu˽DqM KKKtEa %K!--Mh>/ȶ#`lli%믡o rGC5Ut8tڷo[7n4ڵk/iѢ͛WTӃ:ȑ#}L&B$DѣGm@DUL*⒆a o>?/[xƭqϨҵ }fBBqmll &:Xn-=zx:GAۡCl>7ߏàA^±;vdk׮BfMl>d8q2= B!g=E!غ6$Q< i """йsDB F2er'0nx1\8+9YkԨ1wBFRcE}}}I ٳѣ%R ڴ||NGXDDDDDDDDDDDDDDDDDDcVP(0(]zUر䱝:uƷߎ]ˈ ʕCz$hӦ 6nHIIݻwQn]q[n_[c۶md-""",>|v LH"W9):x_ekBDDDDDDDDDDyd-"""""B(8mcc#yʗ/h@hh(RSS8TɓDž*YaR`bb"y:כ0HIIA@@ɓ,?ONHOIIBtDDDECRxbrmR-[۷_|Q}*&kAx4%> V܅O"OUlY\z=oj&"""""%¿uuuaaax[[[!Y _+W.1&(Y[ũ5>h'"""ҶUV@rU"""={ĉXt);v@GGGaD:Ƙ ^#i u*1FNY%$$h;"""""Ҡd055(KAZkkl>](:6)_;:rM277WYQ}_<6uiӳgrL""""* p4mTۡ; ĵkrp`tDI#DTr]Va|ТE TREۡgZTdTTI!#¿߼y7obE%Jd31U>Z[FHIIƪw ?iPm˜$qE&k`\Ra(_|DEDDT|@dd677AMPRNrppPk|~111HJJ&5{ """*tܹP.VO^•+Wk߇"$$|rDG)n1D={h; IJ(kkk4hr\8d-"""""B~8~оpjժ-i?EsI+<}NNNg5M.KԪU /^ڗ.]BV$t PPްÇ4DDDD+]]a!ׯS,** >}.]³g$RRRÇ d2$$$E->|80YPj޼~: >]byseGP{--%׾/_­[7vall,<ifXf^#)Y+22Ј ի0<{?i BAܹsػwtuuQj|L>h޼9 5]Qv!!*** {r_qGyu&O"𑝕+WxI>GAiժlmmہpuu뼑R!;##B^5kV3YEEE_vDDDDdl۶ gQa5n8xy SN.<==U?{, 's 6%K|dtC&ᘃ#u.E.a„co߯]1+Wڂ 266\aYJJ*Vmڴ 賗7n0YС#z7n,\ @vP^}+p6l@ZZ0F8qխ[ B۶m1aZꈈkװs\xA#/@s7\ N:b„hԨ1jժoŵkpa?;/TiĮ*(+ u+P+ V {AĊ zQ R>F&@ }s'=3dνgNڵkǏK3tB!B!Bjܿ`hh(0!B*OvRWJ; B!_ƣG(YB!BM6#%%7nx3ˎ;cǎ(H/֮I&8z8enVXv x?999lfZtUMR<Bx= 33W ztuuss*B!B!B!B$)!!A|`B!D|rrӲ^zEZ? OCs6Ǔ=R%,#֭[˔ǍE׮BvBPP fMUlٲU}a5Ln":uC銌ՎLyxj}sex".\8ǔ7n uuu)FD!y6+Y 'jx< 69ASSS͛eee88DNNLRUUk1n8j:::8"gϏiU!B!B!B!BH"yy#B!܈B^^%k,nVrr  tC\t])Z))_X[JMeΝ;w%޼yk:I1B!&''e˖c;+W.#66PTT llv *e{ҹsgֈ$eɓqe?~=Drr2x<jB˖-0fX􇼼<|>kߴk׾RB!B!B!B!Bυ!B! ed|c58544 0B yPPP,W, d4h+r ++ 5j(W?ֹstܥ\maee 9BB!B!B!B!B) %kB!BHJNNf+**BKK*H2u%QK!B!B!B!B! %k$TUUr4?& ufVYBdec  !B0q:RB!B!B!B!B!FZ?UVcժㇶuvi@~͛7Ǜ7!B!Pll,ncʍ7b4B!B!B!B1z%0!B'O ==s}J"B!JS޺x2MӃҶRW^vB!B!B!"Q033vB!ʹs^bէd-B!BD ĚtCɃ> "|K+J"B!** nDXX!#Ã6l=zBWWWb{>Frr ФISXYYASSSb+))߾edeeQNmϟPQQ,,:]vX222ȔȔ?~___20lRx!/_@]]zzzС# ++[peDDD -- 022zhiiqF|' eeϔ"nvv6>}*y111qQQQjԨ++kQVoF^^$$$ 995kքڵk ݢ|.]DXG$'@[[۷ʽ^B!B!B~VHHH`ʹ9Obb"RSSiiW_Y\x]VY}|U}Hھl)8_|j,\effaNNn)/!!IIUJBHbb󹖒R9?;+缕J i~*i%>GTLK\%'}TJϗ{B!BHQObU<ωe(]Xw>pt\F;P<~X ǔ)St2ԨQׂ pRBZZ -M~~>ۋkהxaQFذ]thz MʔO>nݺիX`>|v)bʻwaÆݻwcʛ7ou?~1e7]1b$0o<ܻw>PVVۍ;zPSSsԩX {eIKKʕ+pQ.]={b000^BHɦL٣_dݻw)o޼cƌT2B7, YڡB!B!B!B!B!Q/+W+2|x{_͛~puuÐ!Ci$Ɉ77Wܾ}Ν,[\bILWI޽{aÆa:tPEqٳg";;sϟa1J',\/_Qf2ۼ} DbbBuG鳜cQܺu 'Nk؏?`ԨQCuSRR耋/ر.MLL  ADDDunݺȑcc'˗/I8۷oyL˗B6#bcJٺu+=*y@سeOVA-@J!B!B!B!B!zd_N\/k:ڶmm͐@=an0x}cUȀ ~wC&M<} FKW ///9s%j5off ;;K)^ƺukD-UUU@Fϟks==u ڷo n]#DGG!88?BNN7oѣpRgD-%%%9S<~-~wI²eKD-eee4oM6Eff^.&8 ȚMKAAhРLLLWx̬7h@ܸ[lv8p+QKAAmۚ &&&xK$''c n!qttĸqĚ"]y&gR_aTYJXoN%B!B+}}} <)׮'h~L!Bz]t>1FK9B!B!? JbUe}Y0fh [Fz&۵ yyyPPPa\v'ObH g Uf֬Y>?زe TNZZ811SLf%j 8k:v>{SNefzz[n KiHHehh'NI&EjM[em IDATlʞMGi&PTTڵ`k;222:㌌ DVV7{SN˗/o޼K198DxxSU9ssbu?jjj;Y 8ƍSΈLFuo4oޜYvDFF[>ɔquZ0a?z].x<ɉB!B!B!B!B!R=P/g,Yի׊L)e]rќ2d(,,:Z #Gb8~8>ʭ[-,,0tR~Ne& 4CZLL Ξ=Ô۴ii%UUU8:~۷o8rȺ³0-XDBvvajڢ8~4ӧ]_~Łr n2)((`ﳼa^u:|>A> мys5 {7ܾ} ?FLL bcc99yhjjiӦС#m*mz?gϞ"6699][[`ذh֬D)KFF|$%%!;;ZZ01{ Uܹs)w֭[ŋ/Lhii]vԩS8ssss!<< PQQa]XZZbAPWWp?>d}􅖖QQQ8u$n߾z5k #GB֭++lB!% APlii OpqGLL 23P ~5LZo)8q8n޼h$CKK[#Ю]rgx 󡥥 #֭;,-7@__xyy!$$IIԬ:ugϞ2dh?߿__{9 444sv֭;ڷo/mv.^|jjj06+++ 0o{x{_ݻw'$''AII ::hӦ :VI, !3--8ŵkWqDGG#))ֆi ݻ|Hiqq?)2k>h`̙3G֮]{q{;J LVp+dggCQQy>>>>}bYrPإKW#..N!SSSÒ%KpB@zz:֮][J92B!B!ls3u1 ĉª :uy0gL< 7s8pZ9Ƹ8\/βf-ݻm۶¢вe+Gnn.n݂͛7糞 }퇝;]N(:w>b/ !,Y3ga)6eeeaѢqbυÇؾ}sVtl: #!!QQQx) 5gۗ{ŋ簷͔O8)Y˗/Żw=ϟ+Xb9&OE;/88i>{͛7q֭Æ 8p`;{7n۷ťo=w 8'B8???X)߾}عs֮](8󃛛+WaҤɬO<%K3I ߇mq&rppQQQŞ+\ lR̞mV޽{{Y d-˗/pU88 F%ֺSSSazx{{#<@ Y3v2AϞ9GB*Ϝ9ptt($++ {z0֯w̙3%j < sgz'L4QdL8cĈHOOW/_ebZ¾}YfyX3D-Q>} kkrC6"#qrS{ #X[[k$%%%apss]=zvUfΜ띊%j v*,_~Rpp0,-K9sv*B!BHU~! S600+HGG Ř*M:eWbկ*'''վ2}uW8ֲ[2qM` 6m?~ٯ\I*yRRR_/_}J W49 s3Q0yrXr%/!B!vȀVZifǏpW^b۶EELL x<Ѷm[@ZZ*={}"33gѢqĩRIHHAXIJJJ2daja 鳲0~8\xfff?+Zfcݺu uܾ}>}*3s8ѻwhuօ!455\x(IiVW~-[ ￷annuu >N<~M%È?LFFC 94huuuyO>Þ\| -Ķm;$8qK,f-kذ!,-`ff]]=<}Ǐ… 6 ׯ{CAAS_Iׯ_3WX++k<~7(oƦ?Zh:u ׮]Ý;v4hvZj<;jѢ%333#( qU0~89sVb3y&&&`ذ:tuu1zq&x qQ$%%666V022!5Dv6x <=1deea)yq#** 3fLGNN0v8o[All,uo +0s ԭkssrm(1|0V"S:u0fX4l7@JJ _ 9pff&&NooohRbHLjYp< @v:))z 3T~~>VXuu5fڻϟȑ#aff###x<ܳ#GrIJJ°aCX:ik`̘^&M;v غu /Ũ !B!TJ|C 1<2G*$n†p\\ ±;SuJ[[Sї}IpUtI|t5Q~㝐\\\===yRB!Bp$zLw^{ 1cʚ5DGGցzj7x 6 #F g|}}ƍ3YZ8z~5M62d(f϶# `viӦΝPQQ^)ݻw3eYSNck۶-Npu݉7oOrF-zq ӦMǭ[0y ;1ys&u+)Yf;v,zYf2*0t0g)ֶy+Q[lC6mXZn1cbܸq2e >~,9ȑ#F߾$$EDcѢYww(++31u4\xӧOe_/_5jQ gPPP. 4|ǎ0}t̜9ΝPpP"I{9\]w2eGU9s^6m0u4Y[nݻw:TB!w@@v&Mի<׳gOL23fLǥK|~6-[;w v[lӮk׮2e*sUh.R >KW9 [leXXX`Y<^ {%Kf^mذ!N<| .H#\B!?3khh@G۽ӧOHKKPpn԰aCBDD/KYYRQNKKc &zPTT,!GDZ OYYb K\/dO>*ǒUBMѲ%N}nݺ03بSJJJOQfUcg;nj2Ν;WoMM VYN}O}bĈddd̙Ӑ3g`jBd-[a֬ppX,p<.[dGח)ۧę pׇH%""0}7 X]SLY0$?_777Yvݺu;`k;@Mh?bimm={_;bbRއP&yXΩĸJr)XXX|NEEǎ~޾}s0|e-1;K/++EARRP\xX%%%c-;y:v$2<=c[f l޼;vFܹ͛ QqetڙInܾ}M^,AN>~'O0emm\zV1^N:!CLyҤ%WիWcL911ڄ չsxyx/^ܹL _nݺdO'?EEEHRb3SVWW.&&F~뫫kP o0&FAZ zzzpw?ይEG*؊3!3z%ʲR .EGrgq :fffL ?bD{)1Q,Vuf,.|iҤ  ̔~ ///}TCɓ'UVZfQ&B!sd_X#;%..ŧ`]FV7AI8C_X+TUUrE~ ^b- zzX$qAׯ}^8**JQθ]ƊHx<X˖,Y"h!- ̿vW5@%==]jB!_,+$M6+lƌg)ɺU8 (E83p@4hЀ)Ç͛YM2iiic1ꛋV~c?{AYzQf]!{{{VhMy]ȈT#G)+\ܤٳp׭[7hђ)?ziiib%[[[N@}eRm@  8kҤ +!ƽ{8544s{B IDATfV\ZYYsĉVxF=B!B!%kbڴi<b3u͛7Kׯ_3eMMMcUhٲ822JFڪڷ>2UVVBBB$VZ&&&58>>s_V(}tP%++`!ѫW/у)ƍ B!BCúPRR*"{0)4jԐS/_5R8ͶTMֲNmK9:|:ҷ/%IMMŋq]\~ W\fdfff"55Orn+JXG1e boР˗/* w666%,ܜ^y%kFT5 z¹mU.I@!D7n̩ sKj. _> ,/(V`F^---]^:u&ՙqMQQ^x)綢' {NY4>##=H@ ӧtuuYer`cNz:.i 0~GL2ess2g+WVhB!B7^Ӱ .0e777ΩΝ;UbbbpeN$}}}) 'Tڵ}#J׮p涓QQ***h֬9yys;vf 899./]{sB!BM8FJJ evSNy\t֡oSǐs\@HO\ùE={GuĈ65 4448g[PfMwĊK؋/Y\;v,?E(555$''+ 7eY d6>(4lXzRdkĕ_T>|`&IM!DrLLs*p;a'gd|Y"<$%%"55$O=G,Zvv6N wyZGP]|ǁ}/;BaX@\\}Vmf g͖&wKq!_ 6nĔ6-^>Bx,Y T&<!~1CJ|lr͛x(CjՂ5~]]wrJڹs'F۷(1}/_QjL۷W~77eF#KLLz*=ãZeiiu2gnѣG0fX1zh,_)bǎ(uMb1BJ׮]; 2gϞP0ӧ1l0)GF)Iƍ0x`\!B!D:ttt9-:\a4VYobg)Q&n27Tfeea8qx,YF̐߯Ξ=gT`}G' ڈGQ%v'-E_Seeeγ tc-!HsCOUz.*ܮ2) T(YK3<`{و]3E؄*~}nn.-{PTsJX*v\=oKI֪>,B~=<vv!?H;BOB*gaÆ3弼|L fSSybڵ}yH27SJz W;8Tshqω[B!BHG3kW jݻ'mێ:2$%%kXddd07oyqvڈǶm[Wf򊌌Ć qqV͛7CNz6l@NwU0jӧgg4n<ٹ ٧p_֭͛[x 4ܹqPP`@\r.]BRR"Μ~###,X֭P5q/_cAKK @Mo޼ɓ'ۯ>$[ni "''޽áCwѨQ#{NVƏS x-F͡j7=kpym:t(~ 8:fFüyǏ>}s0??>|ҥ> 4k ؿ? $$I!B!R _;KNN}R{0)ee ţ=fff";䁩ڻcFFFXz*ud;wG %''vm7}Ku ~+۠]rrX:)}+)}{|+}M%MYq/>o߾I:$B!?k_~χŸ**?,&je:X*ܹsYƍSТER۷O ->WWR~Ne˖Wh}eT|R}>7,v;>9>>.B~v܏Y4$f͚Ԭg,ԪUXQ_|AjASS~@ @BBtuuYRW'HNNFFF:HDzE  %%_YYyd| bM$Redd !!jՂ!HĪ!JSSȑÙ>}QFBVVM6Evv6޿_>;鄠М9sq < ^^gu:::hҤ)BB"!!X KJfl\۷ թSEb ggڎV"++ }0# ##uPn]dff 11Φ&~p7Uj#LLC__/^>7Ɣs]lpMsuqJY QQQ Q>|OsdEԸkq|N)zmʔǏE>pu=3gĪUkD/=={ o<|H.//m`ee ~{o"**PV-i={D=`dd$K `{ܵkW >Sی :t/ ;N.+=Ǐ̬͛hF¨Q%PW^wL`8p`bRk.ԪY V9c &8v(Μ9TTT%r]IIطo|}}S1)))SNc8'͞=yܹsg9Sׯ_͕)O6 -[Y>~ `bRϟѣGiӦ2d(N&싾vM4N>}77W;:SUUEϞ=1~tZ}'H%kqeL4`mZnPp23!%$$LҒ x!C"??cJf F%ݷo?~444pY_]X_n""^b{ccc<3qǏՉ"8Wםشi#~u)cb˝ZJy&TQQᔬu%"Nj|ڹs'֮]^/^<ĉEKÇ=g$KJFFε/v*KWםpzN7044,5YŋcY3DYұᎵkװ ->ر[lA]Q58'kűZ[[V][ YSSNݻwDy-֭[Cb`aсS\t%|йsNZ^^gp{ׯ8wΝ;1cb_%kLMMq?<ӧO!((m خ]{?C4ͩ*:vĔk׮ }}}\t{K^^ݺu…m۶@15k֌VIСЮ]{_K.@OO#GܹA]]޽c===m@uX}kj/_SaϞp ~,DFhذ`mm Se_AA{1p kSLCʵ 0{=̱q3߿όNPE8q"Ə YOGGu{DCCzK0{=ƍw\~ /^( I=6hӦMm8;[nղe+fx<M !l2ݻrɓ'K9*B0___rܾ}{NZNĪU+\dd$>} ///`ܿ`o?ܟwiS EGG#8ػ?vET يd'RJ YC}g'ۥȒ5Zʒd_,V96q̜<ϙt9ŋo۷Wُ/^{ 8Xl)WO . 8۶m甝`ǎxS$&&?kE.]M67o< "i>}Ľ{wslڴs _-'O<W^WrJhhH!BHuҤI())!%%Qӡ[>r6ꏉ ݻ8k *Mdd$ܲe7 ׯ9[{qfu=VuenLMMȌ?~*qY~ׯ߿gM 566{`iiq_uk *)<<<{w9kB177ljLߟi@@z?+:ttt!++CSNU NeϟeKN,GNqi zkԄPp.r-Vܜ_G[)++COOYǜ7Ǐ+RmM< .\^^/^TB⊊HKKcߍqcn?~B; Z(27[uC3gvȀ0+S󉋋ɓ?&V[߿Co\Z!11o`` ҥU:^[<__L̚R'۷oc!b}V-[]v\qݧO1d`ݻXlu͛7cҨUVSS0+O>}ZN:ܱcG;;{ItR(X7a?~F 9ם`zwKdt舤HMMC5*L[$)Ν>p0w 335j(@Mpp<P_3i2㑚 iii())BEEU8w^Ǐ[W_ne`,#HOOG^^PfMwTHLLd>+5k* o{ǼG>Eu~-Xm,VCZZ:塬,L o# w[1SVv}(ʨZDfW $%%!==999PRRjԨ񳂏7K,,,ϟ?#>>jjjBzX:9 2 !D|={6/_ŦMժr ,[\]]9ퟚk<4߹s0i҄b #w!Ap[ӧO!..'Ok׮Wb+NDD&MP̛7s|Ӻ|G~8"6m$ 6؁RRR0ed())ƖS\]]|RNyyy8y:ufpR'mãG?wk׮&M?;wawggg׮pwwP*UIvڱc; uurҥ+%svlٲMd(xz Vׯqmt]{aݷ(wХKgf|?>>ǏØ1cE;qqq=Brg4lh#FYhҤ 233+]0l0 ̈́8, IDAT:Шz GcG-Z͚5C폋 ֽڮ]-0qDt֝52.._닓'O۷T4??_̞=DҼy 033>"""ݻw!((oٲׇ}o;x9hڴ ?,[iii?` 8{^EX/͛CFF?B@@/_,^H_a@ 4Fsss4k͛7Xu݊vGA6[.BCC7nd>YYY4i4Z:Xt  &UPPP@TT>|+3璖QFI%zfΜС#\\f ***HLLģGiF̪Po_hTS&t_|'*آ6b~T~⪧Kڵ9kSWWzE\f®]# .ؼy3N*^Bc ELM[fffPTTģGp?.|cz@nn.de0bk-[ ,, AAqi&{NNƎ7ѬY92;w6ej ݺuCf͠ok@۷o!wΫ׬Yahh]]]BNN?8v ѴiS}* SLbЮ]{h))x9=1!!KW0glf ݘ/^# a@ٳЩSgزe3֬Yzz:::9 ^đ# d6d/ !B$iѬ̦0bRh:zrgj2669>| D.q(Ç9rTu [UNN\]wau[njO u 4,%M]]8waѮ];|R}Ob<d|/XkG+cHe044D>ҥKӧO;*"B~Oԩ337"?~ӕݟ#GK"ݻwsniӦrEXkrr2E.\?O(΅ x镺 #Q׻\7Ο?@8I{…~#ef&ĉ3gKudee2e ھ}`893 , qFU9r4+[ݻ<222%D!UZe"/ ܽ{w^SSɺvڡ]vƤIŋ}0id'\p~L'pvvҲ dee9[N:, ͚5ǑܔÁn@>|Hv/^R$P SVpssc=s(={\Ycfa:::8zXpqĩ"Z`X~CIuu xy&XHH\R>W^r3jhƙ3gQ'N|]g6ksUѸq"!D(X7G|-nB!2e k2I7!߆ npO46m.qpҤؼykcl`j) СCbWUUٳgabbbpGFFF;wkaԩhk`구.^Pj?-ZݻÇ/qVZpssgeX;v(~Zj;B!Ǟ={Y`mÓ'O7o!wիܛ6mAڵ+?={ڠ_~L9%%Æ9a5{wܺu ôi ;;͚5㼂ĉ?'aXz޿l" ۷o%3AKe>m{&Lk׮"66 ֭[з+KQ0b2Y;Y0|089 qga=VE)++cԨ"С#+THH>|ȹJJJ¥K:G l/~9̙+c:1wu fgg V9rU>}zs*|Y !;^|Eb !!''+V`̘1 !>~???inؽ{SŸ;wԮ=+@qde0s ƌ98v!FE311ŰaWF:u&LѣGhҤ1ʬp ++kaaQ `E@\###hڴ)3ɶ wgOر8{=eggcE&B!TGM4C3fWp_P8YaŊUӧOg۶`&(`Mؼyꏢ"p ɓ'333ulݺRRRE2źBݺuھ}'ׯbHKK#''pssys[:1cF! y&o2e>/1g\DEEV;~=qݺuqiqnKW>f͚ #==VP 'Nˆ'H[mۊm۶C ^C8}+B!5k 66/^d;s4Μ9 4IWX_ pFd|M@kW <v؁>}sz5dddX<B`Gدh֬ #11K,.\-Xvvej$}En~̘'!))^O4hWJJ ֬Y 0 >2o|ڛ?!֭[ ܿ[{=+,2Ftfyӧ 'wnG!-MXAKS8;z (**m۶L?~۷FuZZYYyx ""?Ⱥ#Gzi;v4Su<>}ºN5j[M<\CSNׯX ;X[Df9=z4lلxE!###~+9x)سg7ȬM.Vպw.szЯ#zի۷!Zhԫ۷^z%.B!1b% BH9ܺuus̘&x>N\eM,ƍK< 6uuu\zJM´s*%Sڵu/);VIk4iɓTx* [v:]ϱ].Õc8ƚS)++ a͚EnjԨO.xR8HaC\rBp Y~իӫLI ͞2e \]w(ߡCGԩSZ[+WY?[lce +CAAin.q'agg9 $4iW^u"VVVƪUkuv: ƩSgL/=5668OT$+++o^@VV&^|YdeU:*ַodmTTX:8|555Y߿CUU~kժ%Mhܸ |xXu !{;wsԬƍGll,tϑ @TԴ||nʕ˸vݻ(dffBZZ5sX[ĠA8g?055e2_N]uj֬{G/bcc!Fi( L1c&IqƱcG;~@VV&TUU[4h0ߺ}8^ ӧطo_mǎ@[[zb022+@˖-X.())u7_<;Ґ!N0` jԨ>}:pnG!D|慮 !@[UUUVV1g\8;۷o@EEEԯ_ݺuS'Nխ[7ܽ{uHKK,֭-1rHdf;aee;w 00ޅ ** @f8p lm{1'M$@Æ K{"` ƪyyyfjf&oO7oɓ'xUũQ=a447N- 5hZzz:BC~j׮ OndM}#&$&&p!B~^^X}>m~z ?p z0 @Cyyy?WMnu1|gc.?nx< ; 8$~pv<jԀysAAׯ ظG[q~}~Lxvvњm1ή`ڏ kk#/OxL\HR$p"V(ҿ~*ؠ'p+7wJ+ƍW@VY9$]T-ZD-TcNQ8)?77%N6mz "6m0+{QNXV Sk(p~-yyyc'8lʩfvz; ''__-'㯬Q ֭CC]?\u)x nXUCh^gE;_7 ƹ.5Qi@ @nnnU߿nUlOiiPK999?egs~RC IDAT g0?ohޢkv܎(9Z![fƍ/S;2e Lf(qf͚ !Tdxz/W ͛XBbqKWW0p ΋V| Px%ގ455. zd%IJb!n Ny] +|NUrU߫MVbb>KEk֭ЏPP ֬YKྲ(! 6mʬ^V]v!Z*8`&--9'OEn)`PU'UEEE+Py(L2-S -R84<U(9AiS= 8BQQ BG$ B~)u~lo$T{aRRRt7! k'$BȯffBXjRmdL,VVEᚙBK}FVVVWs6l۷L}0t0XjժWf-i+X0Z(ﻸ=E9RP~PVQ*QPu: |}EIBȯ~A<Oq+)񷲲ƿV1MA-B!vRRR544XAF\ԩJIIUNR/LShh88#,,*ZjUUUAUUxeZ,`đS$B!B!B!7@NNccc<^c֯_5kC#33~~}6Ǜ7o,z1ٓ-[ j߾ΝN:ZVHHp‹ cr:ݔ B!B!B!z) 8#4hP,G\ں'{bcc#G^I @)VVjժŔ_|hB!B!B!BHEܹ d:򃝝=~$$$ ::Z=b36n5j0e$%%q+W0e6mp{E}xܺuku|ϩ#++SUVXڴ1gϟ?G<|)שS/v߂ -͛ל-k_kfffen2Ȉ)߿܂ޢP􎄐_e"B!jMs@LLL9e +ɷo N߻ws_u"[-M83+KUcNtI˗/`jJ½"Bȯ.shd nRN>7vdg'+t WEݺu+ N>U>r>hڴi9{F!KX!;vlGZZZ+;; СCGx iٸq |x|V{! Իwo^? ՓX Yj3#%%=z0sRRRp̜";rvY=v*>|EÆ9b?|iéPWWuO\~ pnS0|Rر?Յ1޼yx=^{"n߾0m1s#޽ Abb"TUUK='Opm.Q(={NNCEsNVֶWۭ,66x-܊`"۷s!FZB!R uڕU޳g7&M YYYunRJ" 6mܦM٧;))vꬬLN֭[[ݩr={d`:'BH((@ nRsrpcfm-Z4LLL`gg>)#_Ì =!)Q] BoZU`fFFF044B^onܿU%F ;!Ӱ/QfرLI$''CYYx||qIy>''ŝM1}_]˿^;;W?ĉʽTu!++ŋ`ڴ?-Z84N=®];0} jJB!&MI&K`!B=t~yޅJ B.tàA/hڴL$$˗x!> Dnnn!˗/ڵk`aэ9VzPSSCL7| Ob<)**bezec.XXteТ`oo[[[n~y&әؽ{/3bxxxx-{ 5g`ƌh֬yŜ0G6lDHH0^zļys1o\ԫW*** a}f,X'//{W^gѣ;{M6۷o7F&M`o/_ V<vv055.BCC+ܾ} 鐑bMe:#F0^z=޾7͡O!((/_fK]˖-7H {^mڴAF \xC8-LQ!B!TS1x /j޼Νǹ֭[ٳgx=lll0gkMWCcx{B[[ xn޽{EEE4i7ם8xak֬1a4nFF gx\O>N& B!B!B!F:uK.cLf@V͛7p JIIhѢe]455qe ؟ 2spܹծ]'O;Ku <{.uCabQp93e=$%%9sbL<.΢eabbSNy8.8'}S֯߀gϞ"<<wKLWARRRظqxFCJܿeK>QTfMxyCo|0c8~Xu.] "?!B!۴i3Xux<^7ncǎ ܼ-zÚ5 SlիנkW qN@\\,͛ `.dddڟa.W\zu<\]w1} z󑗗'B!B!B!BZn ?;ؽCRR:ҥ {ƶ:um/ܻw񤤤Я#s5kV*FFFu6֮]#G;w^R~غuzѣEk&<<o~Xv Svv ofϟaǎL?gTUHqqYL)/\ HGBJ#Xo*2XUxpaAXvnejԨ˗1IjլYVm qIٳ jթS{Э[7\z*Yr*tk֬ƛ7oSRFm zzzMB!B!B!B/E8SnԨq aƌw.g#33ujBKK ZZZhР!w^jpMǎбc'>"##o߾!;; ڨWO:::F"B ߟꩫk`Ӧ͘3g.p߾E#11ʨ]ڶm ++kԯ_sf̘Ct%//[e6N:7o^#::N:9S DZZ455Qn]HKfdNzK=޽|] pa&=zX$''AMM Zԩz b_GG/^½{wq }qUtttaccK ,{llT6'O': ::iS.LLLп4hPV={2*:8gϞ !!! Մ%{CNN`mmz*D!GZǏ KZ L_adW-$!R%.]/@}e>:'S!TcXx F'O-#11JJJG1`΋a޼L̬ `ҤɰƑ#r2"##9994h{;`QW^(}:2~dhVV8k7oz\v ޽CXgԪ### ޞ/8p[aÆk׮-r(Nn`cc o{wdCSSxQFC۶8 B!B!B!yHHPnWt ,GWBF6hȦԽh|wo3{KLLϔee!,Lڰ Q%!y5Ϡuy#GȑlHIIdgg;;{N6lX}gcc [뙛ܼLmx>ވG||ѸqAMM)\z_FLL ]EׯHCVVuA˖-aa ,֭Vrr2+`Exx8-Z{wKkhhFb-\sR2͔KwFF}Ɣk׮ ׯ˗0CMM FFF :::eq%#66jjjWOhҤI[@ qDE}E||j ]t|(IIIA[[sĔk@]]C)###ٳg9&UڵkE`ݺ%ݍJhBܺSǼ~&TTT*B!R`h9L^)B!HBffZ'33K!B!B)k&VZ >ob9{1.]x+V&+$$3gb3g&LE_rrrcvl?dddOwq1?M"K} ]ʾ/K,B}~UvIZ iu43J;̳93smm۶CGG),,ٳgb IDATy&DEEcҤI?Çŋ]]]!''k׮ׯo)u0Gaa!Μ9 {1yd̟@dNiC+>^۷۶mErrP{4kM6O>R#(.͛/_|]YYsÌ3!###U%=֭/ 32;044X]I߹-[ٳ3g?YN^^ӧ`eRslK.1AuTRݻw:x5׈ 4I[YMT}u+Ç23gauR!Ǐxvrr*m`` F3cذa͛ x5j46l(UkNN6쌜vvyI8~ViӦc9YغF|`߾9r~$%%G#q.]J9"BxEӦмys[ݭ {{y+!B!B!W\aҫW{oW{.{P~}̟;UL0``%!B!B~Msvvv"J*,,5ܸqNN>|ma'77Ny&.\]~ 31};|O>1o%%%IMMĉ %1oNN6/8u Zj%U[iii>TlN`Gѷo?NuBطo/3iE0u\x|o޼ƸqfprrҥK,`j;ܾ}nnR֮]|ymm]p3gڔ:kvv6v܁0;vd۶m͛ 6+ׯ#))ehhm6 {8s4֬YyJww7V’s? bԩx1<=87cpS>*3_pp0 R0~X<|Pbޯ_bǎę3gФIS<8t˜ ̇.455]XXL,\!?KHVM'4xrB!B!B=z77W&x.X%oTjVVWVWW`-B!B!(XW۱U\LUnn.Oֻw~:dffC000"##G1aa`f6ޗ{Ҝx$##vC__x<ÇybΜRvpZhM p=޿!CLpM4lؐf͚jիWFF {HMM/zCOOO8ab2cq1hРbr&PKII mڴ6deyǽ{]1<vDFʬ3BZuԁ `ܻ$\k9vprzQFС45VZ@h#DFFV@*ʹcA*UѰaC~ >Dtt4N={ĺ͛GSTTD֭_ТE ݻwx ?~,6kX`>Ay7qe&ݵk2{~aٲ%LV͡[ 66OBLL мyshjj"%%̧޾}Ay3jjBjkkcӦ͘?jzVV>_^d_v܁ׯ3]aERBHrrr.B!BOZtj*3B!zԔIkhhTbo!B!Bģ`?PHH֯_zn8pWJAA͛ w3c߾ᅤ\zSX!++ `ݻw\Ys.=F(o`-XX#==۶m[Y@֭[q+:v(7)) sիRSS1}Tx{_Ķʓ31L@Wvv6fϞ7n -i֬Y@-mmm8:nHٳqU]O//oNx)ĉ~^zɓ'"<<;w`mb,Y 21 ga„{ŋ@jժa%>FnV`͚NCWF.]ѺukN-Y999hժm6srrzpa۱c;N j?w2QQQpssExxk#Gv NY\N8&mi9SʴqdggCCCvA߾}۷k׮Ann.A0…мysٳLZAA ׮]Ŕ)Vݻ8=??ӦMej`mh׮P?b 'NY%nݺgϞbʕpppMaҪjؿ߅v[!B!B!?=kR/D!BC B!B!(X|R1issslݺ]Dw55uxzzҜ d ֮]#G8999?FFD8.\СC[`IPQQvR† 4ÉгgOvoLZ__ޗE ơŔ)q@P]QtNl۶"4oOn&<~3ҘXڵ[gVZ8w,,,+v233p|&-''//ooAlkhLcƌd>sܹ Ξ=+rg">M6#:: KOOǕ+WX+W[z lmgٗƍn5+ "FÇCBBmm2,,,;VUUIe~hРo n>IIIؾ8̙Xl)))ز8ϗ' D0` .\|psseW^8u:uܹ03 `dd$;o>]|`mm|`}]6B!B!B!B!B~/N{_+)C& QPf,PRRܑjժptCCf˗ pjwbc3 RqI̘1S2 *ҺukLl;sng׮l@֯ddd\\s իnu;XUV k֬\k޽U.bD#33`-@p~[N:=z4;@yڴi+7##Νe Xvm>_o#Q;d&l2&&q7K?sؼ^dVIga{b<%hܹ@ĝ&MfZeuv ޼yͤǎ[.p?5k։ *2m48;;1TAAAb:uّƌ#6PƎW#\PPggg&={$.'##ٌnݺ0Uzu>| @N`sFѸqcbL|)3{ݛ8!B!B!B!B!?VzewB!RPۛիK,׸q >gΜ ر%oL6Sfr7 ǎev-!Z-eipvvf c<}IO`&MrjN:9r\7rrr8XXXf͚Ȩ{NDD^x-[>³gO97niuň#q`Ww"77SڪUve޽"&=v8IE8W\kQVVY[LVAAnĘ1fˍ1[cǎg=u$XFF֭X?ZtiYUV[ͭx1XZmgӶm; 6Lb>55un=DD_.^,̙8e̙pwwC\`-{I[[O弓& w֭2mׯǒ%.L˗b߾cqB!B!?'O0 5mڌYԥn݂z >|@AA,nHNN' uhkA~лwJ|x{{#$>c$ԪU *PQQF˖Zݻ?*^[nHNNBժUڣO~޽ąrܾhLMsٳg hڴrrrX _W+7pEy?~6mv||}///DGG#66qqqPPP UP~}tݻ@5Xe? oo/gXyxGZDٶm[ 77!0011šhҤ1:a ԫW8Ν;Lw>̽Txy]?bEEE,\x ĔOMMmKƍ= 8#mll,n `ҽzF:uD ka̾}Aœqq%M4Fvam=U{gHLL*ڷo#Fkno6mڢ_իW%B!Bt(XJ[Xprk@HH0`vڣUVhLwӧGh#q-˩zڝHA :Xi%o/˘1c9af6 q`@VSΜ0N;Mu y tJ8A)X+)) LаTN[jf˃8CdzH:u঄(111̄02bҤEtxxǸqEMJJb[hڴY"IsT'O@ѼysN7m eUwW}[jk%''7egVVSx ^^d=ĉ/RVVAN;B!B!1{-xc`ddXYM‹/+*VYOJJ pa&بo޽{8|Zh A>}8Qwڵ o߾z-66޲jՂI)))ظquG~~/_ݻww^hkMйs=xsEUUU`!330jhZ-^$:::x!L̺vXuђ>|-[qĿn%]{4o[Э[2ǒM6ر"7\кukϦH> 5kr JJJҥKr%=ׯ_f̝;K,->B!?88l%=tHp?i,[\yӧO7%\Bl}- ֺ>sƍ7׋zGpVZ1c`MPTT8`V_ϝ@ڵz+V,g,8|+>;vzէBv *n ^"ǏCY?s`wbTÇaʕBFE͛7>,Yse~7ϟ$󱱱x1u#gTs\yf=ꎼ<׋~}/@F YiQqqԫWOq'XK v|' Hs} ?lٲıq*ySSScO>qjY3MNN_3E e7^?C˗#))*6XԩI˵/whYJǹ Lz,&ݰt]5U3/++ uu >wo$1nx @( v=hРľB!B!v=aʺ!˗/af6n%رcq?Zfݻwa5iiiHJJ.31b)^|ɩΰg05;vbqRGr҂ūWQfVX38#G`˖m0aT%`۶ ҥE!%4T=`QDqEsssq1<|PUU͕+W`߾R#܎]NN֯_|̟k֬fsáCG8'<<cǎA\\5j$H83!B!B~< "55I׫W222˫3%*KM_>+\R8q[U.ǿf͚Be) ؗ޷oŏ1*UXiqKSRCM`}{{.޽s;>WőxUpq&c„ عsbaaa]؊21hРrKE&Jkޛd mg>??;INiiiܾ< cOB!B!T$X[[1Zh׮=ڴi>(DF>gzckjՂ9 ,[Ճ`  3i>_&&&2o4hHOOG\\,bbb /KȀP~yJ*6h߾>|`x{{3 +cl[()) LVNѮ];n Oo޼Zl,88Çc]둓СàVZACC111ƳgOuQDH b***07@˖-_fٳ0 &y~իWyrcXZ|}J@T;ܺ///uxx8f̘'OK5o=^:v6mڠE BCF7`Ƹ{.fz1H4i}Si`Сv}EQPP(>;w Mm۶""GL~{:t@-uuuPi5«W/ ??_' !C˗/abbڑMYY[6++eoǂQ^}۷<!B!BkA222X ԭ[Ʃqη*]z*u|?KU$|XD~?s$摕-68֭+pUUUK|=ɑv9.,,,{.;ªU+--;kLܿ,6B!/_2H24o޼B!l(XR:!+)E)ZTT}HN6i[ɱ?ۗ0xp٫(IҴiSy)Jc_2]M|WoUrlҞ˓t֕.$MM+R*ׯ}Fhܸ1zì8v]UVe򸺺2edd`aaQYPZ5o$(䴇?ƶm[|!B!B~<ޗжm;yO:2b~2L0YY_tC[2(fС"JSPP#ľ/R /A[JJJ8wy233fj@bb0km!{GN;TZ֯_'O0J !`?<|yfwXӧOLVZʕ -kRJCr=%Wͅ()U޿BλF^QL{RSNNl\<90Ǐ*e)zHџ@7*]3yd&X+==ϟ&x{{3ywi""wz\.U{1//NN~'$$6ڎѹsٳB@!͕իиqJ˗/_``Ǥ-,,lJ !B >SVaa!'.c׮]ژ4i2uGXXM(X+''޽eu͍IL UcŊ_c*{BHd7n܄=M˖-1nx:tpv 8ױl Xʮ<իWȑ]YsNFFjUOQMcǎ2AannE!B!_! NTAS\r\8[T奥)mV'qJc99HLL䜿֭+8Jjf_r;K~srr^F kk+f:%KBVVjc3F=XiW#2ժURpqُ={J̗ W#LZVVBDjJ I,WXX\8}}},AAwq)-&L0rrrnnn055e>0~ɕ{ۿ"C\| [+ֳgO ..af6|>:]x3K,̛7oF@M[غu -Z\ B!BHkذOipĿ(,,D5E =6%%E "0SǩS'q9vD^ХKWm ;.%wo޼kԈ{sVq҇ׯbOII.~B!03={v#66`տ---=z􀮮.X[TT?/xTaÆ=Y3MəsYf˩_D{\[)%s%BHxزe+Ve&BHe`?̈#fj&VX)7X` :9gVzܸqcǎſgNNNvKSif۷|ذa=g^x!q(x=۷ԅ5k}ƍq? fd M4Żwo?>}"Vi~~xv1Z`|ܾ}5jWS1Z?1l))\d0<<Μ9͊ڗE+;v,+iϞ=HSC07?;vlgtU?Ś5 \\BVVBþ}ѣG7$$$lqD.]йsr !T-[ԔIWk. B!ҥ+.\-[I};YϗGallQvmm^E~}hJܤq[5ޕwyk~WX^U/_|W !_E KK ֿݑľ}{m۶1%-/ؾ};rr|VWyY􀰩s +?Y\NxrVl{ZSLe !Rddd0q!B8Uvȏ#G1|Lh )ɓǰzj 9׉ Ť4i)}~`]ԩ3IIIv*.]L|֭1o<թS?~Ν;+W-aoNd۶mTqmп?B~bڵhڴx[c]8p@Յj^-*((n5QV-V<{ >xp!== Ϟ=ŀ CÎ7}/_ &9 ]v[V3`e8~8V^==2y/^3YJh'[ɓx8`յagt| 1333PRRBVV$ꆾ}0cG,ZCC ##*@x{{n@QQw2ѯ_ 0w 6{[.11177W())::u ---DFF fi9\󻐕֭[aj:yQQQXt5*^Y/::8y龻zycmJϝ;Ga*;oAhl<~>>鉷o`ٲ06$Tys]A`ݞ={bΜL_>~3lpi!B!B~ 6m 99oOOOOk;wA`|W^/BBBvIݻpEBUUzժUYdX:~UVcggg˗/^w[򜐕umcGSyV+"ݹ#m~B!WSvm9dڵ DRRPޛ7ok.puuC~˽/wށ~&kaPSS@M?~C~ c͛YJаcytBdgg#>>UV>HIIFա]Ɩ_̱ե"--5jԄrͽ*((@BB !vᅟMNN6>~0))_<,,,DJJ >NGZJU/?/++ PTVfedd )) ժU*IҐk@EE??|| <jjj$@MMwO(X#G0f(af6믿W^ [.veX={N:ǏCq确- Be,X{| A~,_^˗p%Uxk'ƍceι3g!99;w` {1c;CS9 o2HqF05UV ..Æ /!##ϟ?=uؼY.Nf"99vdc>"++ 2>} ,5|>mڴAnnXQ[Yif8~,--222f jժl%%%!..V?v$dddRRR0s Zl$DEE&())K֤It);8MCC(~?ݺuKX>} Obk_dqhժLƂ9lrCsEQDEEk?ȯB֭;ZhQ="B~/_M[cGAqP]O>ի7kQTPPٳmt=0jhNe333*9x 0'NZ[Z?ƿɓ'#EǎF033r dB0auܻw'OիWXiӱq?"`ûw*U@__cIR/dtuܔcǎcD ''>d啗0dddo`[+={*,ǃmV}ҥ˘1גdej3[~n{ŋ@NNYR>:Wܹs[6x<5kc;uM c֬Y2 z6FuT"0pWu?DFF"11 yyPUUE&Mгgo 8\&dgg%o\| jRBfM1x ~GSRSSPF Ԯ];aРAE:IrrrpY&_@7Ӎ7qo޼Ǐ gϞ3Ƭyyy8{ 3R]]}z7oœ0yyjB!JJOt `ѽ{t˗ٳg0wfq*oo/~>4hȪC8v%wJKKC~~>$_~-WEhL7odQQ%XK9kx<4h111?w "B~222ֆ6lllL6/^ ߍ7:::7kiԈBh# "Lj'O0nݺAGV\Z@$q ?~ׯT$77AAwtv킃#o}|̱;O#~ӂK߫u 9k]%KUxٳ7nȑO<ƤIN|ܹswƱcGrP9ӧx1ܹ3tuzpYFyxĿXnعyyy BPPv {{ 6Lb_|sg޽{bW\ƕ+/YrI٭;lD@8 IDATM89큃#9癹|RRYQ'ZFfXh!N:)[n%o((( ЮG?ˬ@ܾݻwam?l{Xǯ۷o;u۶m@UUU^KK ꃣ=BBB ذaP\zGJh: p1HDS,X0q͛ؿ5:ɓnzL8;pssٳgCA@q9Fi5js.LN:[./c}8vЮ]rrrٳ.\==72ݺu&\v 6ĸq1k-Vx 4d /<==q=AozЫWo <]vPڲeѭ[78;;O}҂DXYMz+ȿbQ~}&&%CWWRPP`\~kii! vډǏ VPPC0{nб3կ_gϞq!ܺxy066.C Xc&Ҿm6̤s*ӦM[_<<ԩ}\^^{?&kԨ]]=&HB:*B͚5YZ]]Cl^eeRyOoڴ),D͛>}ׯ d׬&1mt|hhf5Hl,ww7xyy"w͓C ѷo_SάB>|zzsӧO缺Gzvbsu===_flBȟvv+tǎ9}9s֭y!::=1g\Ζwc[0mTΌWX3xyyIصkߓS! ן?7oƃq?W5̄,&mccz &Y"""Bd;>pttzTÇ1 ((( ""Rɲ1ieߣZ;vB!]b۶L:$$Үx<=QQQ< RVV&^,)\CC_JWfUHHH@:u$_}Eҥ+9̤1hw۵kW߿I|׊?;0Zx5`4ii"B~Gm۶ѽ{W9Q-$꾗(9h׮==ysA^;U0wHHH0!pE brA5dh$&&j6mڌ)S֝qݻBլYkw"za"oZx1}4cYBe+ 2CQժU!//TVt/vRSfδ[}|]ykk+Ĵi֝DZ6("슌 y>%@QQuFMX/|<&N'!;;QQQHNNƗ/uA]]\zڵkzM@L'(DqV((Ί{Vܫj[mjjkuuom]jݢ2T(k.I @P#s_ ^n{VO &9ʖ5Fժ6E.k``aÆcذrGCKH$L0]t3#?g#S?aO6o5jbpvGGG/zpD-D}a;w`ؼy3믿ʼ'O%jnxyQz;w۷oS@SHrss(4m666@HU,[ܹ 7!jҤ Uk_s}ԭ[/_?P33~8DVZcapttDZϟ#<<.]ĦMo,|3[%J1xaee/^Ν;؀;w}}N''b/v׳0~gkN:㫯VٯL- 녅=7m(j2>6l22ҡiӾo8::B"8uΝ#$UݹsƍE`|Ǿv ,A:u*`k[ 2 8w,ϟ'wI̙3Gie0"yLzOȗKZjưBWjԺk&&&pqiP"244C'}@qc(H$ۗ؍wg!?FFFU?SG``u.-sss4lبDell/^ըQc&&&v.\8'N ́Ά! 7774h ;w 70|p?~'mUr'`uGhܺu ۷obE>}pDGGcȐAHMM>>4hGG'$'';ظq0@&a)buM?/E[5J֒/+޽ݗ]ddd#?~= o䤰ϐ!CDU6lX1cŋD'zr3=5kѣGpʴV\9?ׯ7ĬY_c֠B/:t(VʝcEի7fiӾ(o^V"]+"#SٲeagWC+?U܉'111055-8 DDD,]155Сðbr ;?z/e4&ϯ;~'k5kqAtrXn  nnnȑ#3W\U-cNذ!@G+++XYYaF7nyI&www`ϑ,?bq^`m!X'aJJKKÎۅP"?^BDD8v:o+znݺ[.5j>|;)˗L5gw9rϛ#Ѯ]; <^+w>+z*_kFBDT4Oj֬jDDDve,Yhې!CqQ|tx{E3f,c޼HG&Eɓx%,,,k.,\ݻ=ڴiiӾcB蓑#''߰BCC1mTmdd9sԩ38qv튆 ׷L3gbq8qۻw/bbb`oo#GbA6m燙3ę3gE7`ej˗XXbbbD+zyyL#"""z9JTѧO/rUAK7 A߾}p +KWT&MW_BŋW:,L8wb̘q*cɝ(#GC*7n`ii)?^z &&&~2 Oŋ*}~񰶮 /^/p7bhB,\Z$ |gB;-- ]vVeggc)lDDDO>_]D_{ѤԦM*Vuk1n߾%3Dׯ$xhNNVZO>?7n\a41vXTZׯʿ򊏏âE ` Ĵhk֬U+|y+WRHʫk׮9rЎP BQ 4Z;;;G`fQ>CݻGH4/`BD-y-ZĊuIt}O1z|j֬_~Y!$d2,_}í[7vju QѬZR4Qcر^ -^XȝdRՕ_l>s Z_~9{l$$$Xjz'z7;*~h'cAhs}pua2pzz:ðrJxΝ;BLO?|B;44`Ŋ|2#lݺ~~Ig廢>S-0x |48qO>?@PV|p 2,km+E+9st8qQQQBXc>/+[OE">>_˜>}ÇѾ};]sYYYxۇ}{ UY]t}|՗pvvȑ ڊDTTrrr]/\|Yh6\목S<ڵk>y-yړ&M.0G*b)mT\$-ZTx^q/;oܸ.Z\j!RSD ꜇Ey\2e=*S &LWʛdkD!;0a" {{jƍ%R Çİ:bРAj]`!<0ap$XZZb 3[a+զM[TZX777auvv1fLV-Innnކ#>Hʕ+r ,4h(ѣGDO>D㘘yBEzeIAE-j'\FڵkK.jA.Ϟ=CDD8jw!jUIѣ`Bs^%YĿG׭e˖;< L&#P<ܺu+ZHc(DFF MUjѣ'.^M$''k-Zj]7NŅ ƍ\ CNA>}j"><<<~xnaajժ}j?wERR"`ll,ժU||ڕ]?&H+L:upqر;vlǍ7===X[[y0` ڶD"AvvjH1ы]{$''?qM<ϟ#%%Z*lmmQN )ť'O"h~Z'U Zi0` V\P+WFvлwawU,ڷF ;'yݺuC׮]q}DEE!::11";;[دjajjSG#44ѹO.[B?ףf*ѠAѾhiذZ {lkk~[ZemmmkR̀([@)ЇFFF2epQ_U_?~X4^RRT*ł ?Bvq8qVV􄛛~+-Dm؈PzZ ZxI^ŊE>}R@QZu}0TӦM< + mD φ N<'O oCu+JEG\{./++++D044ĉ4:'BCCE%%%D+`B>æMTLKOOۄq59g+_AGڻvɓ'ޛ7:jG&käRpM1_w;}{EJ++k8::bIJ/ a(6n jL͚5c|044 Sdd$233 \? ׂ)4A'kݼ𵕕'wcf q Arrhe*ej]y*XiiU;UX!X7233kNDDDDSiu5jb ?~066.xvv50hP 4H=-- FFکV///Z/JѫWo@5D"VUP4T<==E22ҡ" cҤɘ4i(然u]zzz۷ /P/"":tTzM{+VŤ*9;o2P~qSuttE3:q&ܹ8}4.\8w*|^/_ٳJUx (ot/48NT 6n ,Ҙ؈t;wN8ܹpn˗/ܹ([9sA$:|+sgϴ2 R`հa#"ig_%ypvv۷6mرㄪǥKwss:ҐvބL&υ=ًʕh/lz/:"*ʕ+rxCDDc75 j!NWz=F5 Giʕ+QYXXcNسg7?!' mOOOMDDDDDDDDDDDDDTRp5kB_x@ǧ||bqylg.(#|7&&6h%JDE=S;Y+-- qB\9*>BdԨQeʔ)c>8;w[nӧ0{,,^Qυg炪7 :~u(M PqR),--3aaaoo/z-wÆ ǔ)䞃Ν' 0phO>U;Y+66VTR_ޅ_|:u*60YѐXX[WPޛNS( D+Ubaz̒Я_!YK&a۶`L];)ڟCQ^=!Y+-- ߇s`m]~~CuvYDOhɓ|S%䝟Pn=JݺIk{ťA`ee ???#\]] ugϞ>]>u&nܸ_ R]@DDDDDDW&jkH}[bŊ000,&7NTF *\QQQ-c?U-m6d2֛J\DDDDDDDDDDDDDDVZgϞM رt"~JqsttBٲeɓ' z M6UԎv&M+[3g&o]?/^hҤ ҷtƏӧwQ|5*}E![nʸPׯ = z->X8C^9xP+i}JMn8;w 6RٳgތϟѣG][U~OkTv|EGp}Utб؏[R)ze~DDҥhѢ%޽!ۭ N#"""*m҆ uD:}46~tYk$ ""Ǐп?٫WoLS!Y -[N:{?~ׯmWMw^@vv6V^9sSL&W_}Lhs}iNV KII_~WL@S#|};jtIII:u 6l}˗/affVX4QjU!D2e 5q%ʕ+6{E222x"Ò|嗟E?+WN龓&MưaCY_Kत$XZZjԇ>,_QqqiF 혘ʰ_p/xebb>}} <-[&jq8ڶmW7>>ТT!oݺh޼/EdJ@ L""""""*:u޽`mrWg8q<\ ___61^sbբ{`T+դIܸq>D1e  Mq9D"ҥKahho^^^1c&@n`׮5s8::N:EFFqMСq=OYݺaƌjEwJݻB#""""""""""""":`;tgk'O"dɓ<|gXRm|sKgfdǏ>TP@mխa: _ݰtI"˗C䷯WZwo@̑맯 `4dH|)˵]Wƹ >`spH&W/EazFcf+ 2۷oéS'q}$%%A&vv5=zťZbڴ/kSȑ͛qׯahh5ks.%t j-uDDDDj~~ԊG\\,^|x}atD!|tdg %={fֶ͚W_t 2eʠsEewuTJ0Y]7,_ ƌȈJd)l744m5DDD%!O_0&++ Z矗_W)l "ź֎UZdff"11QcDcLXihh եUKܻwOs ?!/8GKֲ4150n1Y+VVV """*$ yYEDu RSSnw:>$^: """"AddC ""R(&&ׯҒZDx!ZjYܹ ,Y++b=\Ν;iu)SbZɩVС#7kuLyb;ѻˋ%#"""!{ 3gԩZZ[凈C;w: """"*DmDs"H7H8%ѳgO.j׮됈ֻw,,,PZ5fZZ׷ZHWDVZaJ)}}}KaQ)LԬY pwwGXEGGÇZ3++K}n߾C&mRJhժ##"" zzz7?H"L2% }Hʗ/KKK]A 333tQc`!Z4+W_CGTʔ)WWW5nxu|dtzzz8|0t"֭ϲD~X!eM`nn1RV#"""" 3Sq7fii ⯿u(DDDD:ӥKs*-i 7:wZݺuؘ?D ^a~~~ӧ됈HǤR)4h'''H$]CDTXR!}PlllDlsW. *5\HJNDD _ݻwu QCFP=d-5k׮HLdgg#$$2Ld26l-GL%%Ef F;vu̘1W^=&kT%d'ժUCft>`QQQظqvKKK[q9ڶm#**J*uQ#)[|_԰7nʏDDDDDDDDD^`{SLѸߋ/rJM<7.jh#}Ҋk5kҥKuOHRp$DDDDDDDDDDDDD&k}$d2Y FBd-""""""""""""d-"""""""""""J """"""""""IZQQQhҤsbĈQFfff ը?bi"557]\v [Ǝ;t!+D tKLL ^xH$Y,0dffLLLPjUGDDD0Y&H2Nvv6bbbϞ= @ٲe+66VիWZCd-""""""*Hll,&Oܿ?߿J02""+;;-Z O? .Y`UVR… H}EDDDDDDeffF: """""w """"""zׅ=˅иqcFDDDD{DHJJJ… `Gaa7FӦM WWW888޽{pvvuxDDDDDD&kQLL 6n -[d3;w &&?cܸqRJ$0Y Rn]$''JT~X"sa4DDDEDDDDDDDDDDDZgjjJhz saDm&kEFFr k<п^z'""""""c;B"`a]! +*Je-G͚5ڵkѣEjfVvv6bbbfBZZFc=\sc!""""""""""""*͘EDDDDDDDDDDH$6[YfHL{[$~ |RR<<lق7G9˗:u2<==eILLDhh(Οw??I:IMMZZZa9 |VB~s5FG9r$f:۷q; wYy޻Rd&ʲk.9$<& ŋٶww7aӦXf-lm5DGGBr֋Eug͚%)YԩSXxBBB<///xyya6;X66DDT0Yb͚pv>$szбcҥK`aawjժ?~ v q=Yf#ʿ};Ę1CCC#񺹹bB|d-{{{a0dT۷vv[w2D~:a'/9̙3X|޾t.88yg,^;ƌ/zm-Z YiڴY'k988?d\vv[3};w@=`g9s>}ݻؽ{ʔ) Jd޾ׯ_6yޫW/|SNƙ3g29aӦؿ 4%''nd~;غu ֭[/$/^{e:gϞXzz+VHDDT͛7۷pssC˖- yd[~&ko>>>J'&OҥKpDDDDDDDD0~,7,\@to<#---|Ut?p=k֬Ř1l;&&6YGֆDˋE̚539A LǏÇ0q$z5wXQjjjwzs dNeVPcǰxRaܜ?uEZ2D$%%ej{ʔɈ2(HMMڵki,kjj">>^9ɓ{̜9JDT` nH"""""":u:ùsg%O>xǡ[7,2 $6lX9sfg%KTT-ٳgaԨYN/!!۶١O^ʾ]rr2lma-9~Μ9dJhʊ_ohh(LKH*O>sgoؼGMnvg{]qqq:u cڻwdT:uVVcy"";k[f(?~MYd-mmm,X@cccIv~_JdR=z>>O}AF:W%j)((`8w<^E@@|A@cjj*,;wnSE[n>w͛~`µkױxԨQ#ގ|q fΜ!]w>cٳ%K\5PPP3p +?xxÆ Eر򒂂 p\~MrgׯBlccM$5[88͛.x }pa4mTTgٲ<ɶmvz3gٳ |oqeĚ5qł2QZDDDDDDرcٳ'jԨ5kdIm -q%$''Kc9yhذ!wF~6q;?&<|z ӦMŎ;Bsʕذa[֭ʗ7>}wܹIQY&NǏulVZJ*044īW F'Nfzy%+޼y۷  Ja쨴hB\6\N]k(UJ/peܹ {{{o҂u7TZ*T14447prr˗/vv[annHTL4Qhm۶h֬9ԩ<~} +} 3fkױp|ܺv}h׮=j׮ u<~nbh۶-tuuΝ;hB1 t+W S>|Hݻw8?\\\#= ""*Zn.]… 7778;;ںGV|go?1sLIDDDDDDDDT|/ػwǟ(Q|ŊDl)S{C&MDJ(&MI&$| &M½{PRRIIIիWkWk۷?S9ׇ>ի)S(gWLhAN:c׮/_˗Əg MMA___RϞ=*춡OsUTA*U`aaApi?ÇˠAq=qrrDN%urr^o~YÆ ѯ_,Y2yEEE;[AL> @pM8|QK+T>>jտ yahӦ&5jFҲF.<1mT}Eի4i2q´iS,Y^z> ĺuacc+:ߣG < 7~Gbi£ycʔkQFcȐ#N7NTÇlR!.Q.]aÆگ[aܸ5kN: -akyؾ}'""*.V^/ -X]vk>ՎijjjXt)FHLLŋ'Q֬Ysڵ[RիW )((`ףGܿ ;8s4z#*+Z tȐ!Y&jenzcǎ"44Tke^k.DEEOe,\(SVF[`߾}޽pnk$kifk/^DTTT7M;u,3#G$s興'NdJz(֖xƌ9&)**bY4GXYhԨ-z_~ER??FHJLLĩS'1jl=zD4Wnccc5j7xzރujpe!ӧo30a" 00@wZDT0Y+ <*Uʹ`.r-꧸\GQXfMMDDTT…BUV瘨 ={nի76n___i=yk1$&&FYs !"""""""lIY`3ZEE 65j#">"""BtB|! 4˗9vhq&2 ҢE QVN5s>1+}Q=M(z^|){F'ׂqqq|2,Z .Yr(AǏr^U wcf||<d\DD?EDDDDDT@ׯչs\7&/ըQCp>;۠$D 6!OQتU9qJ.-Z+66V;| n^(++ CCCj;cSvʖ^++cbb27ZZZSU^]h~~ŋ8im ><⥠`͚5Сϟ?7o~xP䲯^^c֬}Ȍ͛7/ĊXf ƎU8v\zhӦ 4iMY^/_^ˋ TOjdwAlll(Ďd,f,^WO?ŋypbbb*_^~%VSSÒ%Kn~s.a&k___Qlffo}3٪o555$$$^zC3k 4@t#Nhڴڵk[vڒ^xD!VSS 4Q'3)^aXrEdd$ !!^R7n7oȴ ]vOBIe EqJ%3Y%ٳBldTG}*3/_(i/""R;k@r0e[-[`޼yUA&kor .]k! """"""""'YO^Δ@#ӧ/y&$%% #">8yq[YuA~ѰaG~I|}'_ oOv=())L2xMPj޼LLLE[;E~\BB׮]GRiFj|I9Idl$$$wd-"""""qrQ_ZZZWr &V*ر#&M ;±Dܺ[\%\mVV]`eeCCCY!&Fd~_Ixk֬իWϟN:eHDDDDDDDDMRLJJ-Й}z~l9ڵ+֯_7ׯa.{e[,[Jx񢜺rɢ-W?:::BV\>򂂂 i͛С΢d$)jmMo.]Pj5BOOڢۡCHNdȏƈ Htʕ+}.]ZFFFB_r(.]vcxu t".]sU1y̘13LNNtMܤF ]v|2>|P31AՈ:===̙3 ,f͚BY!+I*nx7l}<ѣG1p|鏈? A֬Y{6MLLd37[\p ArtKd=\NBJJZB}BY(w}Z‘?*VMjjjKN񻺺E(-[kj}188X]֖v?`Z(zzLMMѼy w-m>|7o ;vAm>x˖ٳgcy=>Yy ɹҾ>%ݽe˖E%1"""""#$k"11w ޽C*U$89#mTw$ͨFQM8xxx-;wor_ƼyscNQR k1bdB qڵرuv޾}#zzz8)))""ŋc„ _b<0W#GbW^a޽;vlIDDDDDDDD[5dxܼ@```޽w>8#F N8S%kUVM{$Wlۓ$|@Vz u󊍍9aÆ;*,H^6'wz`mM8޾gؒ}4&ׯ$^={ Gh޼AcT$"""""HZ -$$;wsҔ,YB%iLG8I^ ʪy ;;;~ZCL""""""* VVVxӦ8'O*ҶmDq6m,.U/_ԾIriӦWJII3QeʔyDIII'$$Hfٝ#u$^5mٲ MDDVAIK'd%ku֮]|7uTe0nʲ>}ZH'J*g^Bg̘1]{tYޣϘ$#ᵶ6*W,wiԨѢv"$$Y߿m۶;Nr߾}Ú5krܽ{h:Xr?yF;99)2ԪUKxܽmT,^{.^V׸tb?+WHnL:9W r&k:ueƍ󥯤$l߾-r#ĕ*UQjա*ćl17o8j5ke*3nxѸ'PtiQS@@@}ʱqR޽LM̈́KQQPCVZU(vdl "\__gHlذ!%"""""""i%WC̱nDD߇f͚…ٳ;c{õkׄaÆ?}/^e'/Є'\]]qmQSܼv\p.(((`РAXn-24HڮZй4iwޢqF5j44hҥKիWy睅D+ȣWnPzuH[ {ym‹/ܼy߾}>z#z#;pp8=ׯҞݻZjujjX IӧO ;څ(`Q_hXZGQan^ Qd xzzC'%%k5nÊ35V <|ocӦլZl[ٴ ;wg0|ԪU JJx<{.$$$Ƹ~m''ҥt"ʕ+N033)tuupvvݻ(>|$E~#HK42e2`!ZV }}}"44>۷qy䤬=˖-DGGQԩʕ+%r9?%J'tfF.]allh&o߆nݺw^!_ի}cUUUj йU-ZUܾ.=%%%SLyTZׯիaLAA /F2eK]]1o:tH3Xwl+(&&&hѢ%\E;v\mU\7oܹd &b`Y8u4&N /_YeJ*˝5kč7k=v؎(++Y֭z%wDD?3&k3gvXd1^~|V/%}CC#\p FģGNeվ}{m֭[|0$%%|(W<ѩSgzG{شi3f͚W> ::044Bƍѱ%LLL$gi!J]]ӧıcG3|PlYhݻ~ –-[(:&bT'NīW%K7F=E6wӳ{GBB]zzҞ!;\ׯ bbb`dTfffѣ'7n,ի7ׯ/e˖O%0} xxx޼CddȨ0e IDAT433Cv'm"ZEԙ3g000(l/_֭޽;J.]h!JGS:[[[,YGDDDD$ֹ:vqeÇPCEEFFF(S j׮^zEr=Lijj .3prrOT*f͚aҥ\VRR [88+BBB}}T^ D߾}޽vҒRN]668w,n޼ |X(**e˖CrPNX[[ܼK.ka9x BBB}h5cccƛ033W"̙+IpС:tׯ+pwwǻwAJ UVU4m,C"""7dlذO<)>碣-Zi;ZI{JJJE׾P]ʖ-CbСy2Ujլ455ŬY󤟂`bb"W۷G%͘(FjQRlW /?~DPP޽{H?Ֆ~~~CDDRRR`ddիԴ޽{/^ <<III022jԨ'?~DҥajjZjI}ttt```}}}Dyeܼ+))h('xu}n^ZDDDDDDDDDydɒҥkiiiaÆi?3=RyVV-O#""ZE->|QN<)^HH֮]3g <<eM6Xr%,,,rǏΝ;m6|Qf9===o:u TTTscǎKvڅcǎe^ɒ%xLL \/ҥKY@YYM6ĉѫWloر7oO퍪Uʬw…lGGGcӦM8z(,Szu;ƍ̶~6IIIؾ};كgϞeY;w.j۷o믿^tjժ;v,N eeeY'Od78q֭eSQQAaee!CX ""˔)S1eI!9)&`y1Y/_](ҪW&kQ HiӦ!66Vfo߾F=9n߾!CÇ2qZn+WbԂܹs9r$|c٨(8q'N@׮]EIXXӧOePfTT*T i5d 8tALLLKHHxDN<'""""qb̙عs'Μ9S$E޽lbڵسgѮ];IGDDGYիW={6N>cǎ!::ڵk/^8$?~k """iflڴADDDD #vލӧgIWW 4@xx8={ᜇ:v˗/Hr_1cjjj_>LLL~Z(C]]ƍŋ5j(QKEECٲe'$a2ՅRSSE;aiii(dLZ%J@ `bb}D+WW^r 3_ddЇ De3q6oތg,-[ժU ?~,J{Zn7of[Wa{:tiG3UUUԫWZZZ}namm'O*?N: SSSC `bb˗HMM  JRR)Qz033C2e ,,LRDDDDDDDDTYy@*ZEDDDDDDDDDDDDTYfn7i۷oGjՄ𱱱8vf̘!$z cƌӧeެ̙3 EEE[Gp>""&LscsA֭QfͼM0A5|L0A.b8t=O>ƏUV=ubԨQ۷/ \\/lܸQH*}66n܈yejoɘϟ/JRUU UUUxTTM...xkغu+܄cǎظq#֭eСConLDDDDDDDDTeD<""""""""""""!. ?~hǦ>}իW%hiiaĈ8}4ttt׮]áC$555>|&LȔGGG3F8ӧ喐ooo!2e /^,3Q 6l͛7?nTyESSnnnwƏ/JHCCӦMÝ;wDcٶmhǭ_U {{,w^R _aÆ a(c)SɳWnʼn_{k׮˷v!Tϟ/.f͚%o޼% .^(JDk֬ڴic= 2DSSS\jj*=*%Ko&ils̑TΉrg[xÝ 9ᵦ&F-:ƍ':vIut"1+;Æ }5kk˗/o~(Q*V(3,ڵkƍ9ۢxƌΜ9Sg*燨(!ׯtuu%߿H*[V-c%Qv;0Qd-""""""""""""\!655kߗTFB KÇ۷lo (}}}XXX///ԨQ&MBn2]wa􄛛={P|qqqV ^GFFX\]]Eq˖-媟1~xLy&sssukԨUUU$&&3y(\`ffGXSNu 66͚5Ð!C0x`l%""""""""ZDDDDDDDy\MmH(*l1d 2c}2:gePe R &RI)m$ѩn[n|iL֪233EmEs=< uCq֬Y.]ȍ޽{Xp!lmm]bOؿBݱyfx{{޽{BJJ RSSVZXLFFF011)OjJ}Pprr233K.ҥKUKII9sPjUL8QuHqoޤ 66VhfŒXH5;R*k1YdV;A$wBIarWqəܥKժUC>}zj#<<K.dL:Uk>}޽6l%j;DEE)=܌Dm_4dee+P[[k׮-j+|hrWNppp兄y~f͚U*DDDDp؟9sÛLJ+koL"""""""""""""*}L֪jԨ =z$j7o\Ҹ'OH^###Cbcc QF={6_.JVO4kLhߒ[NɁ͘1Ct޽{J_j...O*UUUPL"""""""""8d2\]G ?}$""Z\$#XvqZRRR}vI}׭['jWƕ6}}}Q;g2Ld+y~KKKqjj*>}Z蘈_^Pvm/$!Cvll,f͚etuu Koc ʷqDe˖:bb"lRhY1knؾ}SF UDDDDD$o%k%""""""""""""*L֪abb"ݻիW8&44 ,8q"%qFܽ{>[lA``nҤ $QT>ŋv5PN|LAjj5lllDm77y'Oƻw$͟_| UR3g444޽{o>OOO˗SU&JF”)SDUrKKKɓE}V1b(4iܪYܹ3$x{{KsDmADDDDDDDDTKXYʦ*PRRk_qq7o:*|aHHH(эϟ#33Q,#<~6 УGO*Ȁ">|<=/ϓlm[W^9UT)tLLvppe$Äk-[~-DDe>_|&N-[e˖hժbbbPX}}}A[[[zK.󑜜}vvv۷q 0uTtСx/TYYY8<Ο?===ԭ[VVVBRULL pyu-0l̙+<<<sŒ%K#eҤIذaaaa0ܺu pq5KzhӦ pvvη۷T2e BBBi&ٳgqԭ[ 6DF/_"$$ֲFpssC~?ݻ vvvсd^-Z(p ㏘?pʕ+&b-[ >%=xfܹsQ^=XYYnݺ033CRRpy<\4ÇQR'"""ƭ[~sv#B۷o鉋=xddg044*v8}Μ9OŋVMuZٹKX\p9||aܸi s,Z0߂8x:I&cm}XT)###o߾Ι%]CyQEdOիQJYF?7o[T('333?~7VhVZaӦM2e C<|PnWWW,_\5))) ()?##7V^+f̘={6DU_2d~g~W1" R/̙3?333#}۷oرcŴi`ԯTҕ &L/`={{ɓѮCP!:uJM65bJX[[CWW3gvk'NT(MӧȓuQnn033öm[x"X[naϞ=С#~u»>} K,'!yĝ;A8{/,ZGϗ7%/G;&kQY6q8qBr,|)>>cǎFL2?Amۊy*4:t(.\P`iMsss޽7o.][UVINuņ ں Bpp0/^^zy044,tΟ? 6zrY[[cΝصk 7-["00۷oG߾}ѺukԪUKƎ`|7A͚51vX:u 1.]?~<*W,뇠 ɉZ5 JʎIrtt{p}<} s7n%@bܸqw䵒0vhddd@MM #FͿeoܻw.xakwD=zرx$ ǃػwp}p1DZ2 C<9sC?Ok׮7mܹŋV3f7o# ǎK'"*de9:wvލou-5d2:v[nx|s戋Õ+WxTT h֬Zn ] ԬY-Z}_+KA7dffݻx)bbbThܸqLʾ!99wEpp0BBB5k?GVVNEy ?KvڨU`mm-zKe͟WCzz:aeeΝ;JbL <{ Ю];Q5yhDGG#11ư7n ===ap},_ҥKAD$\|Ytl׮]puuUM@T,ؼy㲳8YVql޼YTY֭'ʕ+r ѱ?onݺUVՅ?^_=o~s'%%n]K___yZZ>|8gSܼy(1HKK.xF)(/^̙?>>ׄV\%m лNJ'Oҥ?JZ?::ӕ#:߹sg1tݷo_xxxx_w!Ezq=ܼ釽{"))IS%yؾ}DǺvNF APPky8Q HoooǩCCh|7 MOOO'N@>}T)/_FǎK9""""""[ӧOsyPADDǏt<dz;͛c„JUo7quRJ] ~B⑉ v;;;cN8  NK=iff& %%лvr}jPP 5j --BWff&j4cƌŊ+훒6ml-޽oyb8yp~r L?}c7^EZ?oddOֶ%^xxΝ;޽$gϾp`ˢd zO>vMt@ddSAF)> 'hԨ8>>-,ꈪo۷}??rխ[m)t}MIV֭[͛DmJ͛ 픔ܿR׃ --Uh+r>QW?O W [@LMM%'Uҷ'}6/^HntqqIDe||| XYYaaax޽{g̘1c0l0DKDDD{7/ggi\@>_`ҤE144DV$USSCnݰmV@ZZ*޽-[JSUF]v ݻ?7&&FK[.077VQreI}-,,ШQcܻwLQrUZZ͚5~1{,>O_ȸ5-" Z@||<.^縕-ZJǑ#G2χakԨN:)eNR7nGPq9;;hDGG}^CC=.\XQQYIE8mmm!6EO>Ezz:455 W =,ZM4A۶m 8t,X--<}'\#G*LHJIIATTKǏ+WŁs\Ei߃;ѧ*.yZL#""""""""UR$Y_Z;|_`5HOO??xwϞ0`@ɛ$+?N:011g^`^IlRb65%Yիgrr>O H"QEdrHOO_}~G:Mԩʘm۶Ms>|L'kر<==/wںu ӧOeIDDDeB΋pwZLH_<͹3X~W˗/JFp 0P';;{ڦ޽B>GzR%ūWs|U\vU5KNDDDD+kQYw3#--Mkge忑sлwo\.]DJJJ>ؾ}o߆`ɒ^R+Bs=<ժ)NjՄdoQviJmFJLOR6TIy "(U4o>abbꐈ޽Ë/xdggC__knDDDd᱑䝖>QCܕ\hV5Z$E>_`޼yI. uuu} =}NX&UffFᝈ :/[&kQy mmm!ѦqعӭXsZXX}Ʀ)w#55W\ƥKp7߿z?+)VLTjUQ(GG?/p>y?F:uX'F5#w!ϟAGGGO1鉓rW[~ա#$5lnnZ'>ODD2&kpvvVuJ&aʕJ ` ͲhРAh֬R,k;TRJDDD/D'&&ڕ+WS$$^TСC~}#X[[ }vvaذ &{~Jv=ѭ[7@nuDDDDT~wzp +++hh-֭;u7݃˗/ < %+J4ETVMԎThjWUS,**RrֻwU@*RETd39=>UZSzuaR<}_V*LyXrtt """*r^KMME\\,%{PONϏ{Oeȑظqpnᇟ8{ѱPTR/~=}zuQJ*pup\DDDDRVd-""""""""*5j$$k{)..qi=GJ]-ٳGD<-p>y~ Ϲi$iiiThC"EDDD7EDDDDDDDD]D˗VM wo'Gh'&&"&&F5n+W^^^HJJ46%%gΜ2 ƞ|XtlܸJ Ft7mڄ/_8ϣװB~.B;993g*DDD1Y j3|y :Uo\╮.ju</^_G)y255> /\jjj}} muuu 6  ]hd2 ONJ[{ŕ+#U\EDDDDDDDD…`hh(==/ѱ nܸQx킽Lwmڴƍ_fnn%[j%:t;wڱs'ݻw%QnNNN {؟;v4RSS}~͚Xp[ u?&wxYϟ?gg'溃 FFJ$>[[[񜇅Wxi] '^7`044ڞpt??B&$$ ڵř3ZS(ӧOȑ#ܹA]]Cnƌ%:6~TZU򚩩 g:tuXy>##f̀ᘥ\\K^rV[u6(?q +W ==DúvZj*ɝ;A1b8o:::yŐ!+sqcccL5V^%3f4.\CɭAvv6olܸ۶m]%"""ʍ?V"""""""";w>Ν ]tA&6%޽KEbb޽7n ?dff8'!Xx- :G¢ a#']]],\D_{Qmذ :uТ={D˖hܸ1\po߾߶@]]]zÇǞ={[hݺ5tt*o> Ӧ}y`mpr-ݻwѭ[w¢?E/ѵTKK+qŊ.T|{usDNmupܼyNDZZtttлCfffصk  <99BΝac4͛7O'""bQ/ ci={DV~r2n* ִi3̞=Gz-[D`` ?~=z`֬YCMsܼyw0F&aڵJO%UVhsS'ѶQfMhii }u-Zț 'ggg;v,sŊM6ٳOgL0ACܼy lٲ3[܆' 11ӧݻ1|4h044BJJ #00>>>8sh74""""@~%*EDDDDDDDDC{:u#S^^^JWő …UWWǎ;ѬgJY[LLLpi w8~q8|,-Zo #88wUh ehѢ:Ç!99kkԨ9*Uh|8zO˗/ٍ={v鯮_~Y'<93pu!~dggŋxb_'ZDDDDDDeؚ5k(|~8jvаaC@T֭۠W|Oر"/d2.\E8<} MQFI֪]6+IU 1iDϩ##USSΝn6m(۷oۢ~ ?&kQEҲeK\r۷oCRRRcСC899Gywpp@ϞsM|_= 666Ez/^²e˰w+"d2|WX`!LLL^RJ8} n'_qƫl#:ҥpBיjiic„ 1c&VZ*5k<=0m4\vUnZja˖hk׮Qx-Zeolm*_SSSL*!\r]T]T+(k:u5Kd^""O䝽Xgl޼ s_ܔd0` -[כ>}*Wŋ!==M*Z*~' 6L5SN8u /_VIڵs@Æ E;{ 6\Rd̙3wN--yohӦ+WwSXz%={p*UJB2}0xDDDDDDDDT5 'v <>ΝiӾ5x{{#<< HM}5M`jj SSSXYEΝ'wvЮ233ׯ#**111xPvmԪUfff޽ԩS*R644š5k1klxzz:^˗/chӦ v L  5J*aݺ>}&9ELL ڣO/`hh(ybeU{ǏynB\\^NBQ еkWIwڵx5re"gii=x$ 000)w{@C}:_~;;;asロ\ꍰ" oLR2d2\ \=|O)U""O~>FtÇpKdggJ*S;w_4ky+UƜ9 Vl&LD׮ݰw9sQQQx-aeeg>1b$jժU"WmBCٳgBtt(eV371lpYR%_ }/解jժA~.6l8*UÆ W_}SNK~:bbb 555ԴLMMѴiS899I!"""򏕵,V+uʕ+knڵRSWWG-ТE ̧l+4Ç.>WN̚5[)딴~JϚ+m.L;;;Q}ΝcQ'ObŊ>ڵ^~۷صknҤ UQZʿׯ_c-B[GGMب0""""*+kIӧO: ""*A!!!x9LMMU U`QQQtnР ,Y,x6u*H5d2Cd-"""""""":S%<\xnԨѨ^ #""0YK#--MaQ v 0 8<|7n@FFܔ)ST٧)>>ϟ?Gvv6Ç שc *laeO^["""""""""mZ\N–-9ndd#11QsBWWWsZ$%%9HH455DF?zHQEկ__$''9ޡCGmk>mm S>=UqQx1ڷoWk8;5k`hhT먊M8;;)uYf*uN""4 ];NUC(u+BOOOsʫbJ+?9+%6?XYtԮ]{UuZÆ yB`TaqqqxⅪ ""$lڴY!Vd-""""""""""""d-%iڴ)0Hs㸪 """""""*d2C"/YxDDDDDDDDDDDDD%#o興DŽ򏕵J>qUq1Yti:"""""*^| ܼy}6^~-| ͛ )WJ)*"""""Ry =٥ )+k.&k$!!!8t萪 """"""0Yt1Y8z=޸q3UKtt4,^hгg/Bӓ'!X|=z ۩0Ν _О:k|Ysx"DF>X[79*} ""*]իWGZp]UBDDDDDJZ埼d-DDDDDDDDDDDDD%Z;wp1/,Y+99YKӦ͘EeBBBwѱ+-:~Y"Z^^>ΎZDD匁Zl [[[j _޽{:<"""""*"LQ#᱾>UKLL \~}"Uff&5аa#t :::6O"h$&&BMMM4Ԫ _|HJJ2TTT͛ҊU=T|||Y9!%%%r\\? >>rrrPQQAmо}{hjjlXDSUUbOII?޼ Abb"222 }}tM6-Uff&ݻgϞ۷oHOO*ѩS'iuᴴ4\vo޼ABB 0W&ԄlWe=˗/X𡭭Mw>TB)vvU= VܫW* !B*TYd-B!B!B!B*%k[lۘӧϜkBBBtbۮsg[Z FFK=H̜9w?o\e˖s{ExngΜFNNNmЩ .]͛3<իW LMEB ###ֶϟʕ+ׯC^`nFd'Oo_G0vXz%.^?lgllKK."T7߿ƍqA"cѢ4hXW233qڵod6l`Ԩ̾'w`ĈL|qѳ~_̙331 :u;BHukiiUH!BHuCj>J"B!B!5Ivv6ེ:zHNN"TTTE+oUUU(((qߑu*AEECBB򠩩 II 9OUB||}*vÇw"** YYY>iii ݻwG׮``` K+//ӧOcb[[[ 2Sߌ >_߂׫^={u,֭[q1o8qo߾aJJJÇ#5t&ݻ7I(q IDAT+V\}$<=y3mի]bh׮]Ʒgn9s5XVV:tȑѿ2q%#))ЬY3}\l,Y̶gYfq7|'OBp __?ɕó-٧O?~gmmm===hhh 55QƋ/)X͛7͛HMMV65k}}}AAAQQ8;;&M,--Ό ӨQc۷mۊ+iii M4:޽ /X6o?\TJ?_?G$Z29 #::isecNXZ<&077I0>} >y`+;waee ԩ#'O8<~X&B!0<}+z:$$$pnbbb}ѵkbqe:>(( _/@~ׯ_G`CPRR&,,,`oŎʕ;3Y ۷o1[jUkE9}Snݺptڟ?Llmm (X3ubbbuu h @nK};77܇?oHKKCz*Aζӧ Ku|B!R!B!Bpq&AvmxbnQAAA;wBB^ ݟ0a߾:u.Sh LLbƌI/^<Ǎ7!++[81{,f.]VE! %k2o߾ee|Mgxzz \e ݻl[p!,,,ѪU+N;p`?rssQNlڴYh6ݻ3ƙI {-֭[+VJLz?AxYz޽lmmɞ7ƾ}XEZZ :w,2=666^?bx@f͚17o.+Ykbe߿V~1mt̞=22N g-[6k׮h߾sVxxx5} fff7lXu2oVlb͛K&Î= زesiVfΜJС#9ZߚIr2Ξ=S+B!y&.}3Jd֥Kaz&ؽ{VX$g<{ ^^С#<uu ^j%޿/tƍnرdϟ?cɒŸ|}ظqlmmfZhB9[LL M*t߂E[h1?\\Xcx1&N>|x֭аa#x{{cy ::ϟ?ÉaaamvI&b͛X|)޾}+/&&!!q/VXѣt2())uB!|&C{B!B!T'… aѢ?o^[bu7CSRR0r|!OII ***+233W{m{a9Box>0qcߵ?~!##Ú |4.]F:OUڼy֬aW{x|l޼ **M6pٳOQF޽8u$y .GRRƏAxصk455~NR]U~HR-[*䄓'O WVVɓX#|~6/^ )77<Gz,;ic.]aM@ݽ{8,>}ܹsƖӧ1<ǎ… MC9ի*leq7^x!4Q Νm۶[NIIIغE \*re333݂&s1]ܹz=N>-YYYxzz1 !j֬Xx@VQ܇+t<^D-aѳ=kшL^^Cc␐׬1/cƸpnZ̘1M Q o޼t|] "4Q?˗/A!GHB!B!BHu$j5j˖-ɓk!/VèQp!22 |*kNk~~>s!>| r?Xg#44۶m[G N.g=._Ĭ{’.\H&4imy <=˗/`llR!, A}FSN! @g sh{1V.S|y/ᚗG01kXEIHH`Ŋ9WdcB!s9RFgh::=߿ZuÇXf5֮]'p@JJU>ę3}ӦM@UUUV '4ffhժ? 88>>>HLJaC##2"%Ɔ ׮]oU" '3!r:JJ ||19|/.]*񭮮#G͚5Gh[1o$&&1d ܿ+K.Ʈ]Xll:M46?"$5b*Ҿy#G뾐 !RUa 7E!B!BǏb-PTTdoذ+ku---8pVVVv1arrrS @qCBAMM &&m1} |B!##'Obѣ'f.tuuѫW/L8/^d͜9o޼4o߁5iM4Av0|pf1# Z>mmmhkksg;ׯ_deec5k.Z5GcѐGǎСCG,[;w传+$&&bARR6lѣYIKKȨ5Z޾Fϟ?֯NNѸqc+((CÎ?;{,mر~~7ںBHUZ˗YUcƸ@]]~=ڙ…;u4N&LȚWxbEf32SȨ5?y\a:r0XJJ 'ӧ`6SBB^3! ̩eQ'ׯ_gKHH`4n#cVnĽzf :B!Bķgnx<灀Xj F [[[ؽ{k#G#55UXNNNpu WױСk#O8q<놡\͛`055Ő!Cחu͐DZ+ 3 #F6lk׮aeݻ7n G,[W^aO||<-[Rq}}444A;)Sg^066/^{eڿz6!RPeڋ!B!BHuվ}۷_ QKZxعs@VQÄ 82yaaa`ѣG MؤZ.vyIVE'#8r(+Q08p̶d>%y-Z,UX۶p!ֶ۷udmm7 +Xܯ]*x1118w,ԩ3rjGq۷ L|VdS'֊o߾Ezz:M~~>^|FFVEQQªϟÇsKZBHM4 -CXYYY+[n!00VZ mohW^g%ԩS>޼v9r$877ӦMMG_[c(Njj*Ƹqnر=uQ64D Y?L ׯY+h?Fdd~>ӧ|1?#<?>>BYZe= OChh(k\\+kx۷ҹ; BaSWܹ%0***9sk۾}{}lU˫ÂkCLjjjŶQW׀D|=zDh˗/ Ϟ=*(Xj5V"BH@j/E!B!B#cct؉Sׯs>,--XXRP&Ymjt֢7F߾}9Gm8&&=sqƌqT% &Oʊ ԯ*l &$߿<[XXp?~:>@nݘǩfڼy qEL:ɓ'GA!5 %k"))TBNNs_YYY*^r WD]|'77wtfm C^=Рu^%//۷oC6F>}|||LNJ*/X7LM̬~Lannss3XX*DXXӿdV\d+|=Btt;^-K>B!5رcTb-[BJJ?~,\abeeewذa#BB^Wa1zh& y'O"[Ppc+999VVq<<4oނ{8x emvaɒebBjiKMMe<񋮮.gfRqKOm*ex8y6oބ͛7!''?33>Ç|2h+VB+lL8q襤\**P^=HINl)Bny+]r=^Y=])Xxj@:xBψ,TUU9xFdddE BެxDLL 7J͛ddd0-֭˩4z3ۂё*^U[>|fffBۦ---ѲeK粰|SS3*RZTIիNJ6O::^/>}ߙX9McZ }%%%1!FUHJAnn. +[uŠ,!nelV,%U9A)))̟cpU\vw-644Æ s.J}ry5qSEEFc_ wrOaEݮ];4hа,U:;?EWxB, X$⶯ osrr III+"!EÆܯ5plq^xupFA媳gb꿄&>}U5Us_M\DD|d}IZNYYǏ+t7^ؼߓy+J2!B*{ZB!B!:'Y+--48qL.|_plڴU 1Ξ=g(w^1x{VM*IIڋ6mJJJ8s,MJ[=(jsg[,Y[Y~梣ѼysEWt5q߳x˪sE\\~>_V!ZOC{"DMMMe+kҚp777&Y+33N@;///qzпS֛߿gE_O2UJgB!U_(YB!B!TG222 /TYؼyѧO?}222DEEa߾طo/F+VBUU\WVET/:$Es]]YYI(++3s&{ښ몬,mqYp 99!!Yx<@QB(YԬ$keff"11U8ɬs*nRXd----'tdNl矋cLM6 vgϞ!>>'M)Q >}(qR~}V ?ge`}&SYYY":*%kկ<˗/4Bj?~V*0>/,7IΊ^}C| IDAT5gUgݻ?xyy $k=yuqaNG!B!BHM؂4luTh}o {.=ǏXb9֭B#eev%q 3MQRV)66sVVV1V*Z$\ע?23`bVsk]v*k۷$?>>9k&fW"֬x׮n߾oߞSd:uSǏŋIa<2`V;}RR6ddduMT<̒O R&{JHHƦPdk"sssi'N<1oV[ ݺugp]>\Vgx}'ݻ"D!JV7vkVިQXxyy2p9&F>GY_Ӣ*+R"s"BHFj/J"B!B!5]-YYY 1ki Μ9Ç9mme)}ttX>{Ν9%jر99*Sv6 mWfBW*M6eoޔb8r;~VtbNjh]ԫW{fL߿~?Ķmѣm<bwl߾qT5F^|{V!RIY7 UcMGǾL|YN> f_i_1w{jeZZZ}B!Tj!B!B):uawf ӧqrrrcٲe+3͛7K+W01X]/rŋX)羵 8&&Ϟ=ԏ:66Y۷PLpww_0aǎΝŴݼyk:!6U(((ٙm87|!ϻw6z* 󐛛+Ν;qe&_X a;J'//wBh[mPyӏ9ZlɊo߆4퓓1nObs"Z j߾:we└-TԷo Xb?Ue„x#}VV<<" (1qFFzG``ѳ=ĉYys*}TT&M(r?!c}v~i2jքUY*0!rrr8ǝ;wX۸-+Vu¸1333qI}x8/aFW uNHH(qAB!zjE!B!Bj:GGG}Alllhܸ +.nhedU1JOO8dW<ڵ1v|>eb ѓ9j={;-iqjkbbCLw5<͚5x)c}PpzHHHBHUdZfEhذ=zҥ ߿3ݻ'ka,Z~e8:tΣ.bcc`|,]u@NNN'$,[mڴƸqnuĭ/_ׯ_cYZZBCCCh{x<&7Vٳg1?WWRRDH 2H >;;wAn]p-U&o/N'۬ `۽{zX`` :tuӧ_Spuu16nP-'sX[$_X֭eUApp0sd !!wghb4hАapuunװ{.L0v@Uo߾ch .]Dff&۷ѳ= ##2!rbll.]MRsIK’cccqiN}]o2!`Ef߾q[Ak³g+f۰aĸJJJ8>߿3q7 ѣ+^|iF!کNRP!B!B5jZ(3-- gW~~ S.9EII 7Iسg7|RlXd!q4aڵ%ݿ?Yv|d'V2ŋba۶!!!3g͛7Ts:t Ο?fffXx M1g\&NHH ~5!VdZF^^PSSg%%%7DNЩS4m{ %%i///*cN_밴@M1`@icc8p`?ϸq򯕁CF`h[[ 6={ظ5u70042jA3qzz:w ƏN߿Hy&&m0`@v>(+8?g999̛7sssqqL:C b+555qI0  zz'޽Zlc֘8q.]lc +Wbvdž Ѷ1ZgO{4lh^zӧL;իנ}_^^>>ahhlt"VZQFbE8s4shbذ5XQ6Vrhjj*\]]Рv=Р! _k--!Bj.WWWVm+U6mdm+z"ϟVYu,8$$e3ƥL߹sg)ݻX\\w())1ŋqEm !B~1Jģ/ !B!BHMt21 t AAA%MLL!oo%taii۷,P!ܼy̪݂X66ѵkW&NHH}wx\hW^G;VXVܹ7Mҍ7`E%ԩŋmdee1&E~}qmccc޽<Çǣc8qxdgg+4 KBB^cELBJJJܹЩﹲsI!6]XFFqU느f{^^BCCiժ;_RR;ݻ x<&LUVu#!!!ק0صkȪZYrr8~8~y\ؾ}'fϞ$#&&111Ǐ+Vbc]v222͛1glI|~*"ڗm۶"Wmj0w\?~ueee?ΟQFVrXARR..ؿ_eB!7FxxAuݰ0 <^B+i͕UQVSSG;WژYЭ[7HKСðr fm͚5+`DD|İaCJ%%%c8%٣u6Bsa+WM6pwǩ݃m6]UIB!5_uH@U"B!B!>JxRPPҥ*ֶm;`kۙI@C hٲ%B#ܸqL_%%%ڵȿ8;;ȑ#82غu\\\svܹsGhR[Æg@UUUqam7o>.\87o !+Ν;kn_*l|ez UTJ~:::q{'>~ g헒B=1u4XXXڵ3eݪUr|BLسg/zTzǤI`ff-[x< xĶm;*59iӦɆY\Ν;@ǎP~}2m;w knݺ|@ppoZ-K^^o('OgOq97ys!u,5kMASS5ϟG|Ě5k!RsPe!B!BHmѱc'\tc ͛7Yʂ _ܸ[b[III8pm˹˛.]2fee>>>"ihhSb/vx^x0~Ĉ㏅b6q89 `'Ν;|_+9Y (x_oرcYIs^X_saqy,!!nKHH`=z4 ߾%"??7:{qtuuqE풒񈉉A\\ ]Y`ԨQ5j x|)))Ҫ]]2A/v?55ulٲ[lׯ_;C]]zzzPTTdwssQVVs*n @Aկ_㑔 ii)hk@[[e:GY4o\g 3338po HOO%L__SL-]7(277/kXNL<'OOEVV6ׯ}}}gnV^C&BHk۶ݏ 3+ED|Tg6WBBo@+ut2iό+::wBTZ&~iG;CGG>>> ŴiגuZWZZELU؎;G2BBB0o\&ɻB!d-J#B!B!5)a׮طoBHKKٳ;Wx>IJJb'L>FFFziӦu6/=z c<CÒ%K%deeq|x{{)''ŋc}(pppY۽qI&n+Wtnݺa- *M2 ǏG BH@Z5@RRXCCRRRbCQQʄBWWRU044aUESSU= Nttt*jVeD&MФI*9\\n߾&&mEN4h(jB MM̛7Gʮ ήbjj7v_岲QkXXX ((6|pHKs[d8;w&lm۶sgneeVtƺuQD-mmm !B/>k/E!B!B6mq&n֬yU^=,\'f̘ݻDRRP k׮7С#:t\|>Dll S ']]]BOO=z9Eq_7ayC`C|߿CII = /0?cL1{̲زe+f͚SN7P}sǎзo*\9,nA}]]]V[Cӷo?V"WaFL<gΜ˗/rr_>1p ܪURNթSFSܽo )) PSStu`kk ===jٲę/p޽;k~Ifff BZ5: R6WBFF;88Th!RQ,,,0&VQQٶSNT3=ڶmVVV}/^_gCJJ 갰} 4bmw /_˗/bڴo_rU_ YZcƸ8v: 4GpǃCAA-Z@N3f@U^. pܾ}>/_55u -[ڵ܄B3T}d-B!B!R(++cNzLyyyξ\'))vڡ]vrVꗆX ggrOӦMѴiS 1wr9Oy9-gwѨQc̛7vu-$,,,`aaQc/P8֡C2B+J֪fyT!<}zzԬ_l?̙ӈMZZc5Ch5۲%zZl֭aRHNNFDDԩNFneFWN6ɦTqvùI[-y~Ā0`f[ff&t܊TwSSSq%&СcWj5779geeAVV\-)) GǾptl)4U !RsQBvEZB!B!ɬ @B!%kUSiiix .]Lѣ. !b TaRgIɄRޞ={gϞU0HP"IWˑ#YƎusW(E!¨V'*!B!BY^xΊ)YB(YDƍg 쳴D.]*PB!5O?޽{B!T{Qe-B!B!Bj4ٳЪQB!šdjHX mQ!Bu;wT0! &&L II*!B!D%kQ4B!B!B{.>;wn˗/̾cݠZ#BHq(YxE&Mй-&O:u"B)I&aΝBjdD#??qqqr 25440j*!!BHŠ*kB!B!R9~r;ޥK{.>}z:ArrrSAA zLB!$S!<<BwK IDAT! 3|||^!egR#R^ӧ '))={BVVGE!R~x<^UTJ"B!B!r|:us8:ƍ^*Oѡ\9w}2~Ռ??J;vdyu{L@Fcg-Z@"ٳGK.5ړ&}a-j\Jkn)ac-7ի$+Z@J ]a- k srrw $~jhs< (`|\ 쬕@q''%p|΄r~_.#G5jFc2L1w\ܹCѣ{߿ޠ%K?TѢEX"aZX @PX1*THW\8$WWZۛtaadbI#>*SQ5kL>>3l|_.)ibŊT-ru鯿Td)EFFʟ? 򖷷հa#իWOy]t#"No߾=ui˖-:}ΜٳgB G˪nzzk3xbIRxxs+V,׮];SWl9լY3s'&&jӦZzvءKQ'|R͚5SɒR=Z9N w! jժe˖Y\ 9p`q쩧kYnv횺t  ڪXMMf\]]5q$o!YԹs'#v?N#G~ޗ%6lXiӾ8=|ڵ{Y?~\VWj„ VK2k͛W;٭ꛬ\\\iZ˗YfjÆ :6<<\ .իWo 0P...)ncX{֯ӰaôgdEEEi߾}Z^y >RV$帬Y Y[-+tȱʖ-[jѢR\K-ZLx :f4wZRRX_G1}TEFF(rvvVFԲe+,YJ~~Eoӧut8KҥUb%}gs8VN]5nXKV``N8}jΜٺx1{vQj;Ykk9~"ܹs.8___uI}78?-[|3Xyٲ.O,oצqZ֘1u̙( bŊ$hoԪUdʖ-k<5js\\,|+^^{$iƍFX8n:#j{%{r*={7[n$ISϞ=\bLJLLԬY2D <ȸIӧHԺW¾3gʔ)c9{#`YȉiZ슰ٷoED6&889JLL͓U<`\zW\ykȮk9kݥ[6͛7|| +Wl*00%Y/_)11bTz'r3dc5J*}d2e>}f_-\@7oޔ$__I=cjذz)U (`ݣyiLثWfxXˊ2>>`|cUDJInwǯ  7`EʜZXKJrN{Oŋ֮c7{>I<4/x1i{ohӧ痋KcN'^^^<<<.)f{Ho>0S{2ڹsvܩ?BBjiӦjҤJ,uL0`Ν7>ȂivE{=z뭾|7n(!!A) #֝;wl@J5b͘1.]q)RkݺuKi+W%׷M ?+?,???cΜI@OvǪ_idϛfm޼I7oҰamv5j|| gJ=wQNfyTfM.죂 `0߽{V\)5s,H+-a-ԍ7$IgVUB,A%jFڷo޻w4h`]v%;-=*)^ylw>㱻{UPѦqYJFXҥKpႊ-o߾;ʕ+_~UgΜʕ+j*m޼Iׯ_OwѢ?V駟~QŊy9A}9ϟӿV޼uuuN H.r| y;VRCŋ\տXqS̙mN_Z֭kӸ+W~TuVϿ5jݦyJHH/MREiӾՐ!CwE-]4 Pnխ[w%$$hΝZ>L+WЎ;~W\o[wӌ>>>Zdʔ)済ZGJ-z>/@nEKǕZXΝ;YX QmV+W4oYdmۦ,ڢd\`o֯!y+߯^ڦݞvءu|eV6;edV_nqcNyCl6[3ec*IKW ,-ZT/Ѿrz|]nݲ:f4eYQrP?,8p žy1߾}9ΜhWZͦqfY{y԰aC-~<<<¨Q-j+WP/h߾5yWz.3@F7$ION߽ElVJ C+>޶b͛HJ{KmYGfXEKvk2˸q,Y1dWL|4sL6]|YEKԓOӓOӣVϞڵkF?͚5K6׭[w=ēU~W!*WQҋRVӿӣj޼CSHM܌c;wTUT h\!!Tܣ1bܹGyD#:uEbŊ) h߻ Cjʖ-g ߞׯkܸie}NV$oիWk~ 7#,uK/dmۦ%Kر$k9J*kŊT;w:x`qŊ|JU\%]shƌzɧcڰaΞ=kI{Ѹqs=￟iÖtcƍĉ1???͟@J2\zUM< ֭[i7LO-ժU+žŊhn̓'J}eZqR-Z4ŋ<->9snܸaXd.}<+ݾ}ۦslٲE;wm/ ᢥJgˊ5zhÆ "ٜ%s[ s*UZN… }6%$$XqqqQHHڵ{U۷XQի Oa-^D3fLל9w3vj>Z֨ն7ƍ5~82X<㣶mwU¾:s&000^rDm۶M۶mӐ!զzUxJ(!___ݾ}[.\кuiڴiڳg1nzVW m%K ̛7O۶mSHH5yPիgءëzر{jm (,lM`jժ/QgnլYC 6gQyYU;v$I7oԈ57a-ad2|Mޔ(!;w;\]]嬉mKR=:+IIol֝;w,WWW=ꬾ}m#3 Wm%{(0C%l6I.ڲ<=P@@~,*&&F3fL׌Ӎcyt)ʔ)ٳX]xUC U~ua>|8sz駟azII|d}d(111 {QlkI/?\tQ?(Y{$ kIgMT|w,^mgggn6x@\t\mȑZ`q_ȑ#աCɓ'Se9ac___9㬋m k嬬q;KrKWc󘫫$~6*W:&S|cnnnjݺ B z^zI Ըqcns]NNN6; 4@˖-x-hذaի͛g\Bg~tX'xRcǎ֑#Gvi"EԴi3kNpd`[_~$ԩS2e{LdWrib<OOO&]pA6a:tΟ ֭[PPPTRezO@EX  3f뒤A: IDATcǪGzG`o/jMFx)N7nر(H\]YQWHjZZ8yfeʔQ2e=~_6b*UJT_@@|+ggg5nD71%$$Ν;V@]dg +)RD>cIҥKgiĈ6';kȮkeSڿ j ~ǎod jegg|ru[ %]&H|LK.I&N7|S2Ȯ/uUd5kpڵk+44~+L:v"6 #(P@ 2ڱ;vlZ+φL;gUZM_}_=YǏϰ󹸸O&d1dH>b2`6mLܙE'K-Zo?Wdd$iԩzTx 슰V6@)SF7o)WWq-_lْasuu%۳E7:xGSVK.Z:.Z'O1Bݻw$kĈ1cFEX @v]ٌ;y*O<.VGX պt &ٳg>1dW!///{&OZ7nȰ@vPFCLK2a-@VsqqѣզMIlСC3dKk![(YK2܈]`.Z:~u Ѷm$IK,Q2tk;kqY_Nj.r"Hmg-~4vX9s2dWhR k 3=3z駍l])b祜ƍi:슰+L:.Z{ QVtN{7Z EHۘ1cv{swt+W֥K,ꫯu.]77nTR2FLǏ۵}v2/OjР+?tqݲe+;V*S$I֮]Fٹ2X3gΔQ슰>x'I vasg^jIJ"ǏWzAe;VX-[A jcE Y쐋9a-bg-ٕ+t6/____խ[W-Z0[nҥKX@nŋ . Y0 '# bg_ׯØ1c믿2D-Z+(::Zcǎ1͛Phh 9-shqrС>p̙cNJ#~=Vf4c`% ZdW$#G>|X@ʂղeKۻKY mW^ULL.VX.;wNk׮ѱc}I*\W%KRFa,Νh6՞>/B۷oSO> rg̘ѣG%{?Q1[n*Udql׮Z|VZ{Z7nr*VyR|.<]v|DDFXZ P}fСu IҘ1cԽ{wa-#" Jr9sfQ4ZN j֬4צMըQՠ$f} 5jP.\xyڵkp4Zt J9r8jz>84ZR+ҥK2,X0_u>KԒBkmڴҘ1: {g/^'ڱ"]dg-`o̙{XUFMժUKڶmvءXIIg~.]^yϫ[v횤BBBTR%=zTn|?֭[jժȀW'Og+if bbbttN>G)\rjٲ$i͚5$y{{AVkJF-[VŊS``<=+2N80Lx[A]v6۵8qx*U:_}FaqhѢQʔ)… Cڵ٤Iܢ_~:u%I~x .\Εa۶m:znc-^nݪg+ѴiߩL2)d׺uzj۷O/G͛QMH"СCZl"""rss VѢESՠA*T(]>zVZ ܹs|9Z-իWzO|K*.$) x-[h%:|Ξ=#ɤ+hZwwڶmguk8]vzPppp}ܹ;w꯿TDDN̙3W:uq&*]:[p׮];`<#zN>W1LZv֬Yŋ2L)błԠA4i*???'IBo%KN~A?9g*o޼ R ԶmY X @vEX8p@8S5sdk׮KڰaqlРA +4l6U'NRY>L;w2a4r}y/>իWcԳg/.g~4i&MJ4xJ$.]ZӦMOW]yK/S._3g4{,IRBB~6mڒlLٲe:gҕ 5ߟzmʓ'OW|h?|6lu<'਼4p@_q>se{?̙m۴yYo֐!5sGEEx=z"##=wi~ :D{ч{5۷WǏʕ+RPرcYfY;wѧ-22RF}Kx#GhƿדUvm7^ժ=y/J~i5h@'ս{w ]qqqM6髯4UÆ Ӝ^ÇkJ\\\?w=t萾sYFїR<ϩS{?ZbFMO?չ'LD;w&}˖-ӲeR\"YXkz뭾F{U6~W 6L'=]v_~ѐ!o?iᎷ~KfYmUN]EEE{ھ}{ڵk5a'1bv+L:6ZGd-u‘ v˖-h)E~P-c&S}7$,'''͝;/ŠzGc|35J3Î;/\{Iu|'''թSG&}4cjРAo4q$ʫƱG꯿ʴf~-B_AԒmieΝ;=zT4}UPP믳쿃d2ԲzԫWO~'Zs b XՇܹSRޝ^7 hɒ6{֭j֬VXnS:p6 M1uW_mo^pA9w֭[gCCCUhd÷kѢZ j/11Qy?;OեKZפIզM+cg8|5 M1u7n~i)a-#" bg-rqF;o޼7nܬqww3AV2V [|63ϦڧRJڵ IIayjA6Lx|Xw3g֭[vOի,Xb-qqqԩδUVSvTJ)'WWEEE)22RǏ/#G:7oޤ6mZd2ԡCG)SFJ֕+W> 7j޽V7nTnUF\<==͛7IJڝf͚|ws߻Dj~a$JTz'V1կ__ŊSPPteEDDhmZhn߾-)ig]_ӪUS\8wեKq_x 鵥fڴo-擤+f͚*T[;vЦM|oF-[CWOŦ\+::Zj֬{*U[t~͛7WQQQƘ#gSppCVr .Z:6ZGDX @vEXˁop:w…}뫎;Y|ű-[믿\}… {͛7kҤׯM^$)((H_=EO<͛7WG K,..Nӧ?f_;k9Z@Irm۶Y;ulo۾}KN:6SFرc+g'''-[hϟ?_ 7fڵk_K2ܻUF2LںuѮZjv+^ ;vgh}@NѣGm٬Cڱ"@vѳg/},tXoS?cֽʔ)E~)'ZuΝ;9T^]j}G/bף>}GY}...Cpґ#G?T] .L3%%ݵj6׮:tܫE0TZw/^\3fP"Ec3g~oSMYaOe2moY+jǾ c3[xxxV$ j/RIlR謭 ?+ 5 "@g.Qc{ y)ûmSr6mڨf͚F{ھ=vd~6={bccر-vW~8hGDDhݺu>YlaÆuX[lщpBvj*\b+:RA]tQvssCifzǍ͛75TFXz뭷jbŊyF̙3=@:qlMk7Zڵwckq+xr7#I/_Nѭ[w=T}c3^?%y4yW:vXsם;w4yWR~--[Lҭ[l`(:o<ըj֬UjժZ];DkǓԩm@f4NNN;vűA٩@vеk7wɒ%cOOO+6Ӯ];͛}ddN:eYzJ;v9ƍ7ە`>蟮:_}E L&,Yl}Q@vFRE{]z~zݾ}hm6yּe(q͚6svvjU˝ˎ;j8GGX hR[p{sw8tcݠ/)>͛(5]3899iѢ4iDM4BIu6mڨM6jĈ*_F~:j2ݻ.]9777yyTBU`Aϝޔs= ?tڵk\^j԰-={v퐐< եK%IwN֧B ;w[k7X֭[ӭ[t:S5kPٳwdû?Xe+׉'te]~]&{h-Ν;92ڶm,/O?<==u IRxx4((HiEZo޼is}v\tl쌰qqql$ٶ;ӽ?ovs˚nnnԩV\+Wh 2=xڵ{YZ֔)S5ΜEPPBС^xj֬i W%J>)%iܸqS4h\xb6ۿōnZ`Sby(IOh޼&N)رcje,YJ5R Tf- h׮g%&&gfI~hqqoS~~~~6uڶm/\timذ>.w-X X[\tQӲet~w ,wS]($((K.71J [֭ߍ& 9BG IDATk 0dgW]J~TTTǟ>}x?~V鏊:f{yykW׮Եk7k۶ SX:ܹIҒ%UL} !11Q'~f (ŋjjigY},7G yW˖-tRI֭[U@;WbŊUˠh?xޔvTuwwה)S+팿-$ĉ>ONT|y5iTM4US]޺f}Z+V̶^}֒ ֊5kVreΜ6#>P}aAAAh/X &a-@nuFXٻ﨨 H (bXEc5v[[DF%W0;vlKT ҋX|= Q緖k>>{ʽas RP $%%ԴQRRRDͥUwQ5V8UjUƗ$l)Z? QQQXl)vCb>|Js5 cI jGKlp~N… /ExEDD+ dggrA#{Y߇DjX;x pe̞= {=joʕ+^ m0I7mFhܸ1BBBcerӽ{;ڵk1g,nnu`kk*Uظ2*Uz cy_222:*_R0#JDDTxUʊAg*DDDDDDDDDD*ok@Dea QF ڡ4rsIq=deeB[[GR0ᶶ\\\%+ !;;{ AhhZn-_]u1U>Z8pհaCI㲳Vhyyyծ]†O9H54hlRK!"RYBB\[]*=~С\V'jw-Z| 4o-, gΜƉ'p|h~ٳu 1{^3ԫWBX?$ڵkp^^ѣXGhkhhgg^EV|TZzz߇YYx9%ONN.4DDDDXY*ŮѣHU5kJ-je."""zXybcX*ADZH._`>}aaapss+rիW(͘RM  g]J|I)lgggQ;<< M6-rLzz:-[Zo%`ԨBX ΝKADa̘1XzuY/9;;{Q !5kf͚97n,_x)6n܀y~õk}7мy^dFFFܹ |"##ao_ڵSԿp-EΝ;'ܶvvLLJaa-]]]ڈH9#;;jjjPWWvQc?GYP>ZDT1U`РAXvplعs+oc_}5H߹s3fLǒ%?ACCCa???;vTh[ZZ{[wׯLMݻw5nDa_+a 9sw(...5C^QR%SRR0z7MD{YhAA!41 k*}/cƌJDU,">>>֭qeaQ"J*`HC5vvvETT 44/^^f͚ضm;ׯ+֭p~-[~C \֫V~~>vޅ3f"77{999I+n׫W_:_Ζ}Ν;+|Uc2%y܅$baa!z$UE˖BMMMef|TxӪU+EVZ5Q*ꪔ`X/D3H%a-"*k kU0زe z聤D@rr2uuuԬY@Aũ?b֭000|G 00/^S'qI>o9_|; ]vb׮r}-[aÆYXX`׮?1p`DGG(X|2,_ :prr9! !!Aڈ?[nAnn.~E=DҔ8*_~%8ɷĆ JmRWWG>}pQd#_}~G W$$\n"++SVa>>ʝRhhhO<5GJ ;;;igϞ<}ׯ7L-r\hMQe--- <佬) k1GDDDDDDD;w@DDDDeDsAssǏA@2o߾۷ocNN*KCC6mB˖ ǒpyƌŋޞg3g jկv+- AZkX8\paa֨QdO* o+WVy,̙sE>\[n ܸq]. *@DDDDw#vnn. '\@88yt鬰/%W\ו>fN4 ĸqcEUs`ݺ$/ f8vnժ%:Ė |}%&MW O@DDsH1id,Y _5J_r={}0`\u_"""*[ʮ00OŠUjmp25V֪p\ v؁]v"$Q(Z5i}ǀQzzzh޼жۏM6bm;OV0yhѲdDM6իqy?  W/L__0a<==%ojj+WcҤL&Cll,EϷ|]EXHOOݯ;`ĈzWw=EĴi= Bll,<˗7ZYYMeV(T*rʨW>:w /Β7t>> p$$$`̘زe ggg8;;Ϟ=L&pE;v\ÇpI,^#:v?=amm 555$%%kزe N|ğ0fX4hΰw@^^RSSqV8_:u!!!w}_dќp\E2d(6o$֮]{cʔo}}}DDDƍXaaaV<(<#F?Ά3؈.allQFK-Zb1ׂyyy7o.]/| /YYTo}~""""R""""""""""/eGa@VVp[]]:::Ʃ㫯W_} DF>Bbbann[[;jժq x{(ɓ'HII)PJS0p@ 8@4Tjjli=JqfXZ qnaff>BTIAʕ+cРA*{S:uz2 qHNNaee%@DDDDv؅1cFԩ,zevv6>Ç_B^^rss`]EVJ/nL4ϟ?<}˖--5ZoT:w"yggg|w?X@)PtVn=X+VH=aa ^cǎwx̛>qA])w-lVưQ1~xG^^۪WR\׆]]ݲ^}$'' ͡psSRZjVZme XXX2$f,"""2ddd?؁ cժleܹʺ...x\U^raWZĬYQnbݽ{w`5Ml]9JRnw˗rU޸q㑙UVWٳ/YICgϞ4^ZZZupssÊ?˝K]|~Z KK.""""Ra-"""""""FhӦMY/*:(Lpuuرc1`#Z77^ч.==o.eQ1"##&V(:v+++(4%Ga!?@\rqxannkkkXYY^zܹ \\\ιiddd 82p =ABBҠkkkTf''G 2 4TiݵkΝzGAwRSS CCCXYYvh׮=ڵk==" B`Q5)SR% DFFw]4440mtWBxx޽Tӧ<<(P;cLa-Z{ccc̚5&LĽ{ 66)))>խ[Wt.)gPSSäIѿ޽ gΜ[HIIA^^ `kk O֭;4iR윯L6/777EVZ}>Ԥ:*>555ooJZ\BB/ m;;2\ Qv1Oe ""RQ^ 'ޱA}лwXVV&5W>CCCowʄMS.ԩo@233ߺ _Ⱥ ۷_ggg^!333(x ###4j5ͭ[Wc 1aD@nn.rrr>.)OV9#"""^æs<__""""""""""""ǰV9sg#*}5ʕzDDDD%mɔoRSS{և]M商FDDDT(ZDDDDDDDDDDDDD%arŋprrDVVw;dYD*DDDD%STSSV ""*0+/U *k1EDDDDDDDDDDDDT*2z DDDDDTZAѪQ#e """2ðQ)eW1%""""""""""*iU1EDDDDDDDDDDDDTzXYqN%T:~Kl>uuu 4#ծ[K R+zDDT LW m--2\ QǰQaX\2weRvx̴SSSQɨW^#88~~~e """" EYe-X91EBe""""FC#b=QY13&d""0OŠ,ZDDDDDDDDDDDDD%a-"""""4n2#ZDDDDDDDDDDDDD%OsDDDDDDDDDDDPVy*JZDDDDDDDDDDDDZDDDDDDDDDDDDDa-""""""""""+kUl k*0ea-񈈈JZDDDDDDDDDDD9eb`e-""""""""""""ð)K0ZDDDDDDDDDDDDD%a-""""""""""" JfY/ޟX̝;[h?m۶-Qy+kU k*t8p@h7eX#ðQaXJMpp0bce]]]tU+!"ܹ pE%ٳHKKӳWDDDDDDDDDT)EZDDDDDDDDDDDDDa-*5kp1ZoXjоwA k͟B7fX ( k%""""""""""""*y#""""""""""" AY4V"""""""""""""*y k}䔅yXybPVYa-""""""""""""ǰQưQaX#ZZDDDDDDDDDDDDDG@D@@]xZjpwwG3?"227LxׇG3TRy?Dυ㹹yxq-vD;wHLLDVV6m^£}^x+W *1 ɠ SSSTRժ٠e˖ lED{FFFJshDC&A~~>PV-gϐ$Vо{.Ν; L_prrBjj?,ffV+++hiiF""""""""" ZDDDDDDDDDDDDDaRRR_#/^;<˗p BDDD}555ѭ[w̘1NJrݼ_ Ց6"""""""""R$.F嗲+<ſQ1cFcʔIJZ?\s|@)6999ػwڷoKs5k֭[! TW|:~-ս IDATVZb jqHHΝ;oùs j@xx8G`o wBΝ|cbPce-""""""""""""Z3c=cjՂ;oL|NWBlllaccmmmDFF8`ذad1===t䅚5khܽk׮)rs& uZ ժU5MxGF\\w-Č3=k񙏬,ᱸOkZ_=Ƴg"-- `bbOOOUVi=DDDD?{ } W>v؁k׮"&&jjj^-ZС```pΤ$l߾ W\ALL4222`ii fj֬ ũSp%' %%;:v섆 ݻ~4 ZWcccRSSC&M0h`t effbMXd1222˗/C=QF AVV/Wg@Jбc'g+!۱l߾Mh999={6ms]:uW:tLt?/NJ?#3S\9vfVܹs |~y9ڵk\''bիiFs_uo^̞= 6668"*Upθ8,_ ;v!<_ٵk'A8}uZG׋&|+lll0o޽Ҿ<|SNEPP}8}4~qNiӦ޽0kϺu%tOغu rrr>qԪU -F֭Uz,DDDaSv/V֪"""""""""""""*= k}fN^r  N @.^T<<*S Lzs69u)쯦ѣP@Zt 6m^W ifhi)ʕT5cLIƌ ++k1 @6mď?.|LСð_MMM["""Evv6# +!Cc۶Ѿ}{b1ؿ_rrrhBbܸqta3/_̙3P///#GPS&:: {酰0|AAJJJk`iܾ}_~IIEҥ?[%yMoΝ;ӧ~i) aX0UΝ;BnzJZXYYaʔܻw*Wx>:"==]8fllAVZpr̗ 8wMӧO5>J$""+kUl k9C $ӧ7~q!Dzpp.lL۹~}pƍŁΝ+WsL<p4i⎮]/AOOO (mV̜9CJ|oJHHUkԣM>CeԪU[-0uddd|<{L5|,yζm}!5"""Ҵp"L8I\33sѱ;@NNF Zbٲ墐UqAE=s7L jŋK/c.]Dpe111HHHړ'O j)bnnc@ׯ6Ξ=G.Ə>;vD~ޤG`„ו=zK'O`ǎ?!N<% jRJرc'<<o]緶ȋ/ 2fIIIU\͕><^_V"""""""""""""*yU΅ URήN $%%СC ­[ሉ^Է:_qBB76e ߼ ᘩqqOWFVV&ѣGѳg/i-ZU*m đ#{ӧ~Wu."""""y5jСÊӸqcAXXpSN+r9:uC *FEE!99&&&r}cbbpݪ'ƌ#1,XOn*,z P]E|[~~W;@PP ڴioGNN2dh5uuu|TKz'---r*Icۄw۶)I\񘈈>, kʱJ*A]]]4::ZR?~~BGRuMoDڵ+F[Rѥ #<<!((.]DFF7<<cƌѣ%oP_V!;;[h/Z4^c"""Y2jj͕R)۔YXjj _K ^'O+ ȑ8r0:|;]vVZx n߾0Xbb{ *YǎЩZl mmbc 8[˗/yjT kI_BysU:Oaiiw"""+kUlʾ%""""""""""""*y kcjjj077G\\ &&FR/_W$WvzJkzUEW nnn3f,^xM6bҟ !!!طo/[>qpQFoטclSH^^Ҋ[o+''W6ݏ[` &''c]ؽ{LLL0<&&&8},Vk.Jll,6oބ͛7K.C/,w*UA06ccc*"9rJ +W.aRY$|DDDDTXY0Uխ[qq8$%%TCCC%{ݠAI㒓,/Q\?7n-y5c;===5{pƍ ZmI!ӧK{ݻD~/> 7wlQPy6m7oMM_Ë kڱE/L&Iꧫ'VSSÒ%?S&MX"""0Q2EDDDDDDDDDDDDT*qtAX+777nČ3#GK:NjEm##iWj vZZ*%mT5jp>}۷WY^Bvv6| %%&&& Q[ݻSN߱SRRKDDDD3qǏæM1b"F۶mѶm[ߋC~~lvډHHHCv&M{Cq=zTlcccQ;::1o.*|4k*\>LEDDDDDDDDDDDDTz:GF>}E?nܸ/_,vܺu 8;Šk$p;''Iǎ5FԞ6dddt,++[xj6 YYYq淰$?H[R|$$s3Q)n!2Q]v?7n+W<__?\vݻwݿz*1AAޣ$ѣՅ~_N\ƍ**JZ圍 1cF:|0֭M9 _]}Ebcc1b0dffJ> %/"=qqJe˖J.||#<<\r={Dnnnܸ~H>Ǜٳg+훟˗c/)oҞ={&T#"""Eʶe>> Ekz RS߾򪙙9|}u[rd2]^}I?xp_%6l ܎E``q8v쨤$""RQaXpᏨT>r0upIIXj%LTZUpsswEDDqnjjjQ\J6mB___hϙ33fLǞ="00/^%''ƪc˖m022BӦMQQQ 7 DFFG~}' az}7oBV-ѱg?G︸8̜9C8fhhOOOՁCuyG`8{9u/77C Ÿ=o@߾db s.#3CP}R]QE֡g%Kŋ2ZMQ;&&ZIOiѨQ#,544DmXGڵ—"7oӧO%m֬ ];qk$"""EYXa<""""""""""""Y YYYÇCNN $$ZDJPn=d2}޽+ RƍYYYشi#6m(wѩ蘓c$@FF͛yBOONN5`ll4D˅J[HHBBBZZZ-Ǐ#455i!8---|5@PXak91:wN:̼5Wҥ8<Ο۷n҂GQ0ƏCáCXbccK\N8{/lذ~~dr}6l`Ȑ@RR}dfZRԷpP*SS3\ &MB@@d/ IytM6ł w(ظ~:8pcǎ[8;;99HNNAhh(7ǏGhMCPϞ=۱}vA޽QfMzzzx)"#aϞ=ضm;ϨQLNNƮ];f֭Wwlmmܽ{;w]; ưaZ*v @RR"<=?̙ߢ}FJJ2_g`uCJPF Q(ݻ'o^˰~:ԯnnׄjƺ?ӧOtSSSlݺ]~L-Z`Ĉpw3tDDDի8}:.]DNN(|DDDDT~QaXceeիK޽{6,-ZZZB_[[[VijժaÆML{BSSff氳i1mt 75mM6DEE!11׃宼_Z*WNЩ ''CBB"`ccKKK?iRSScƌŘ1c{"99fZ @[z%ڷ;&e{TGDDDD555̙3THLLUWRWWMaa Affӧ@}382/wXS7{Le)"@ܨ{_@[gj8u:p+۪XꨊD@\ Y+$p,zss9I.!$}?HMMUhҤ/^?XLu…%\kСCZfZd2= k>Q:::[.֭[dsܼȶ_Ujժ2Ԅ)LMMKz)"ʕC KzDDDDTʌ9 4k׮ݮˣ];O4j,w[jPbEzJXEA-KKK?#GR8HJJRFEA-@nȪZw2e Gt[)|}סeV8tm* 6˫+-Z҂g{|wΝ;EKfۼy ;w? Ų){>мystM{EDDDDZDDDDDDDDDDDDDŇa-"""""bԱcG m I\]]1cBj*yV4A8=z 8u$\ŌW5-ڷVZA[[[޽޽|2^HxxdffDNгg/QܦN &˗_7sļ˗PSS ,,,PZuwPZ5" " ,o<|d\jBϞ=ѣGO!0յkWAnݺ[nw.޽X+WpuuPXcci֭[˗COOf077%ѡCGJDDDiceMYX/QcXu:tTy+\]]Ugkk' k nnnB;33iiiQy[pr'7nhiV}ZZZhҤ 4i">==2,ϰW~4440t0 : MMMzyuWX쓳'OS뒓bZDDDDDDDDDDDDDŇa-""""""L]]@AYV^45 ]UeԩBaF+A-"""XYS2EDDDDDDDDDDDDTJJs ==]hݧWCDDDDV"""""""""""""*>EgĉㅺMo1/mQٴc@Ϟ=_dd$fg댌гg"_# ku?غuKn˫+ZDDDDD$IHb֭ʪ:,--=DHH+VA "555db^ ZDDDDDDD%۷عsX'tةWEDDDDexDDDDDDDDDDDDDa-l 8ڵ+mZ[Q&*VW^I߬Ys̚5M4)爕6V"""""""""*iiiشibbbJz)DDDEBKK #FII/TaX>[U`l\ADDDDD!CbРs.]p|HNNB Pk.%'JYa-""""""""uE\r?S!Q6#P~|sNL:Q0EDDDDDDTBѰaC4lذBDDD9V*XYdddd<<<Ю][Jcu+-k)-JZҲ+V0J>Jl((-JZye-e@Y !VZZ^zU"~ ;GDDDDDDDDDDDDeZDDDDDDDDDDDDDŇa-""""""""""Ϝdb^ ea->DDDDDDDDDDDDDa-"""""""""""2ZDDDDDDDDDDD9V*"""""""""""""*> kax k>>sUQ,PщƼys_m۶%""""""""""""*n kʰ7oOh7nưgFYXӈ ZTl_(.:u\+ܿ"t񂶶v 5553S6QaX͚5q @ժU*?XW>1[nO?-ڧNʺD;: `Y%"""""*: kQbbbccvFFF%33SwޕZdV٦eX)>" EDDDDDDDDDDDDT|""""""""""""XYl`Xh>'5ѣG]\\ Q55^!ea-񈈈 Z8s nݺ/^@WWhܸ14iZ(_޽DTT^x~hҤ)*UvHNNDxxqUVnۏ!..0665ڴi +++ %''#00ለ@TTtuuaddJ*-ZP!::mVbETP!ud2ܿ.]Q }}}Ahٲ%Ϸo"!!AhAKKK?Hhڒ!"""kvk .nR-CCC1(LץUu2 C-,,,ڗ߿t/ЭNW<+sQOoܸ1\]nxϞݨT"~q9>5!!!yo˗46998w.!!!Gzz`mm֭ۢc022uǏÉ/^а"lmm]xAWW)Ç#))@{V%{5N8.]\\% {}!,, *WJ*͚5G6m`l\Eӧq%DEExhiiJprrg{nZw'OWFFFh߾p۷pܿHNNN8ҚlbeMYe- 3n'"""""""""""""0 pl]ts0eT<@~}%dG]0h`4nXi'Oc8|0 )) ӦMő#G0h@!ehh7CZo0z745ŻL&Æ #5=`޼h޼T NǵkWvNʕS:&,)+X͟(bJTXQ|}L>C[0`@^zQF3֖4O?-uȆ ѢJp֭C.]ѤISrY̛7GhI k~{=uNu```I&c„R˗=/^]3ܿG V\f͚h1g,qh߾VTT&L/yqaǏcq萟pŠ3gBʕ1{,YA9sf# ԻFDDDTd2|\]VRD,\ױe64ߡC=Ja(OCѳglٲ;vRiOAll,w0L&Ch|ݽ ϟKгgr Y;OOƪ4ʖZFDDDDDDDDi2dZh)" krׯ_GHHЮWIiP+Lɓ'4V6555^u=}ڿZ۷oǫWsrl;ԩǏ#** ElsG\nW4(LwFJJn]iP+f&ƍ+\q1jjj6mҠVѣex![;pr/i"""lɒrA 000+W.# @sY9;w<ףGcvddd@[[ #<<AAax1 ==ÇǙ3[nR 33Æ jժaÆZ:::ijg'N޽n;88^^](\gddAf͚Dܿ SB5tL8+aBP...ptJ*!22R[H} EDDDDDDDDTvn[.eQ*9,jO0AҸ}`xEQ, PjUX[ 4 .v&\622ر$0apbFF^{)5T\9rDD222Qsر#ԩSի7/YcGݺ555Xaܸ C"""^`` .Etݐ!CϋϹMa X~Ν#l 7o#$wlVuSCCCرM6nQڶmѣ1pW~ 5=LSNyׅv}v.Ϙ5xʤ`Ԩ֠A1o޷ToV3f 6n܀!0vpFm444`ʔraO8)ѧa-""""""""""""S6+n߾%\_t4N[[={*u͛7x蟝',i͗Sll[UU*PYZQQQ6m ޽{W*۷?TA-%W_ ]_I۳gOhkkK۷oAAAe =K.re˖b<:>" jTbEELLL԰V)}&tP:S 4g||>K.`DFF"%%%q^*| j֬/D2L2댌K#&y3={aŊA;vѫWoi͛U*j?kmmGoy666%^]<:011AttqOʠ't600|}W]#bq!OҼ]tc|ٳgwoߎ'OoVk.9sF_ؠcǎ>|85j$~:u {ڋ/|В%K 555bѢEJΜ9Sx]QfϞ kݸq#vލO ===th׮d{֯_ӧ QZ5k:t;TcXKKK9TiJSS_~իU:~DH/jX+V,W;^%f͚3g"!!G6mѹsgJsT^%ږK|j(Dž4;waÆ+L1c>y$Ə(_.]`cc'/"55UreDܹs[TI >۷ʗ//iw.ICΪ\DDDDyaeʎ+W` *H k={ cƌ  qF <K,)A7nܞ>}KbXz5wwwwL:ǏǨQPY0i$\rE\9סCbҥs}:Wff& ,ZK,ѣG#00P2 /_Ycǎ… ^aǎB{͚5ZhժUa]v ڒ0f޽Ǐw(r L2AAA odEll,1{l̟?3fܹsƍÍ7yVZ___3/X9Ra1%%/^ŋn:߶mYiX+<<\Է{Eȑ#qi#44XnUV@iwhsΕd k4k g޽{1zh}Vrr28`Xntuu]Ӳe0|$$(?x5ݻ˗cȐ!زeKۦQ7oވ*ya~RRRlRV {{{4n͚5xA*/߼)pUAh1v8ܹ reddXv N\ KrroOYqEgUخ]]V###8;;KꫮOOO>3}JjjР8zGM4UipY&޽9;4M k}=zOOOA-gffb˖-/ 򒑑ZٕgrKHHEɓ۷ 2ΝC6mؼy3 !l̬Yn:Uccc;Ν;PՕ;[zz:V\>} %%%m۷cС ZرڵSԪP ѺukA-mmm^zz:V^=z( 'O{ Z&&&7n@۶mEUT޽{+ j)+ jikk˽~d2lݺ:tP88p V\Ya8v۶mݻ71m4}TSS077"*U|U%Je˖ބ`Р򂣣R#F ÇUZroҤ) 5k~$С#ZlsԩS8s mvX[K/)x_R S|jo """B)ǬYТE 899!-- 7= ___իW]vIoŊ>EAƍQB|.\@VkȑWjԨQHZ5ϟ?ȑ#!ɠѣGՃ! s]KSΝ GGGXYYAMM >˗1w\Kヵk*\ɓ'rJmjj)SwޢIII{gy?_|=z .q̆7o7oGGG$&&"((U9y$M5kɓpvvk׮Ɯ:uJxURgF PN۷x)m6={6ϵܸq}hذ!̙GGGT^2 !!!8w[!`ٳg_~)&_8phł дiSXXX 99n¶m۰i&cܸqsҥQN :Gڵ;ܾ}[rLNNFEbܹhӦ 4htѣX|^v Ƒ#G ޢ#Fʕ+ŋ5kDZZ8p?p_N8o .TG[haԩ4h,FZZ"""{E|oaR|PWWj3Kퟔk?411'$T]SAT\Y֭FVһׇWWxyuL&Cpp0._K.?EI`O%}?R) yr&544P\9cʚׯ?TT9tcii)FFFyQ%Wl-gQ:VZ^z>~o߆۳cccN:sh-`s?63g~QkR܂g!"""r4+>>6&Mhmmm ???888#f̘!|i&t;vT:Gq~gQQSSCݺu1fkn, NֿQU:K xvzz:/_o׌5j( j)'''+`eR^='Ĝ >>FFƒ޻wORЧv ${V~rȐ `۷KXaøqannQ>vܹ}K[/+++#>>tiHHRSC[[r?fJ騨($$$ JKDDDT sO*!A^]___IOc}}%=?MXx {?x“ܿ:u V[SS?\U4iZe`XڻwCNP~| 2K,0ʕ+hѢy'Og@'NĔ)Sn݊ K/5jԐ7gDi*7GGG 6 j8 r$Jv~n*,xwrJիW,)U\abbolܸQtO?ɓsVVVrGe0`ڵ @V%$I|m۶cNJ (3e ՞ モoϞ=HLLڞhܸq   <<c={ZoQh޼95ko1c`ѢE޽{b *""B\XT8XYM6IcǎJꛜ$jW t5eyׯ_I|hHHTҥ3Ç)엻ҁ*L'$$`Ϟ=a]B[MMM/;8r0bbb$ݰaOhٲ%~7. oTREr1hժ(,RR*T wl2?[.5\xQԖZ.[ڵEmEDa}pFFFyW^}_s?SWWGӦM%ͽ 9 'wƍr}\]]iiiډImH%]vÇdU93֭1GRܥ(xzUz)::#F %Zp!1RIo/˖:tԨX|ҕ@ցv3TZwAݻ2 ⪰_MJ{66 } }qbŊJݻw֬Y-Njz:Z4h^^]ޞQF %W06" sV`'<ut6LgϞPm|,333hkkKCQøz*d2Yks_y0$$$1sLݻ11Y1vXL4 nnnhݺ5\]]ѲeKI,s?lYYYI&jrwƩS$|P֥Lff(ģdŪPyiSSSEr4ڒ󓳊[ B(իW;w.͛ ]vhܸ1<<~6m/8wU@SSC[vT\Yhc˖mB=gΜ+Onݾ_pe;v.a~}3V\ 4077{-[~ҥו+W [#mi0OBKmS|xhee-zիנ[7/޾} ,)- u: g3 {[b"}pa3g.j0i$χ*U/y"""*Y^֭I._kRF&a5XtY}]۷o mHcU^ wd'&&_D8pHchhLT>iٲ%tuuٶlق#FڦHDDDDD+k}^r@P=DUJwR+^'wAQxVyꕨQs~bŊسg* ʕ+r @__֭4̒{D3e>湳)5roرc@Qy$n#t>khh *?_daa!wK[k:v܉/RTL&֭[u+.MMMl_| +^T#Tb̰fZQYР jvv6֭+\\Q=2 咶UVh ѳg4o1d`OWƒG*U0v쇪Lؼy1ի/0Pܠv%*;|<4lXի[ukwtڴi {pvnɓ'" EaҤ h fpuuA^=е7vEdbpMMMl޼E/---̚5Khd2a„۷1ܿx777,\H" xxuutukٹ\PY?JP233ߣVh]z mڴFF*CDDDTի  혘ڵ3qW\Ƶk*^_ݳg7^xo+CItl{c/U8TQy Ev+VA dLAhcVm۶ynGʕ+Fʕ+ctQR8%%1DzeBGGGk1L80ɓx\ *`͚h׮JseSL!>3g~ M 4HagbĈaFF%{t慽{񙊀3u6ѣ9>V׮ݰhB:Aoff&V^իWI1ްW_ ? IDATtvM~ܜ4i2< r2 J:Pll,6m R1ʎȰQSBQ;::Z鈍U=eT'w_8p^^rď?{ԩSx"^*{ڵk#GBll,Ҕn0ljr;""BT@ssoBd '<<0P4**J2L}V^ L4 &MBXX;s… xoLL K<{ ӧOWiM$Z/pvvO?-đ#GWA߾}1id!""͚5n733sG;|\9sF.髧;a̘ ֪UK cǎ(hܸ1lllط_;ԩn?22/,A۶ܹ3aee Khhhxܻw;vl0P]SSSl;z)T=yΟ?QF5kjjjxBBBpM9sׯ_CFFF!V*XYR\9hhhDDD4>22R١Ϟ=Si RA8o޼AZ|^GGG8:: đ#Gyf!%퍛7oUeB%Kj r[hРGo7>X,DAijjrBxGᮂֆPF}HMM*)gR5t-&&֖?}ieeqaܸq۷q ZJ;w.t:uH$Z333X Gŋц)W.*3YZ59rT[XX6zy!455al\իW+?3>>)VaÆ<.'777z!˗/3339Sԩ3︸8x8TP011K/*+Uyży爎F||< add [[ۏ*US*U`B|GLs ԨQC/"""ҥː3gN ޽ whkժ]v^յ1:u #..|5j} 1j]*K֭݅X$''co3gΒʖ3*bPWW5*n;;;l޼v oQB۔ZR裮fP1EDDDDDDDDǃdUGJII;XS.U_r?nB~u hٲ%Zl3f}d}tE_bE &&LJj׮_rD혘IUǢ;TNNNvhhhl7qMxyyʶJqyY̷oUS&{_+juիWx Ye^n?-- OC*UдiS?GGTa .\·B˖&M066F5в  ~ǃgo1b.9"""w-tbe-""""""""O@gI7={ v:::ԩm~6lw͚5dՠJ+EBΝ;Xn#jժ%jzJO׮]E+VH޽{8r.S ڷooD/_e˖I.'''wb֭j[zu4h@h_|[lQ{4dQ{ժUHOO/t=~>Xb3?M6 mLRcvɒ%)p߿/T qEQre+m'͗!""""""REm33s%=URSLŔ)Sx U50xH50̟͛Ym&LĄ -m``fc++k72eʠur3+edd@WWMNSx;ٛ7o9U1R#qQd-""""""""OΝQJDFF?ܹYsrr0o*y4c t]hϟ?;wFU'11QOFFQ\9ܸqCvwwӷo_̜9IIIϯL̞=[ׯ_?XZZ*߰aCd25k0iҤŋ_M2#F .Daggָ~-'t*KI3;vĉ'6rHlܸQAի=w:vի#44C=zurss1{lyFX֡Cwadd$$mڴ +=8{JUoɓ'+퓓 >FR{O||<$o?$$M94ޣ/^ Q|"WD-UD-E *dEN""""RPRfYL"""""""""*tuu1w\Ѳqaʕ aРAسgPn$$$S|PP<==",򂵵JіۣgϞB;55͛7 \7++ ߿¤ϟz?~<]Vh,?ƌIk7n,g-߿?&&&[n8z\r5kXlll)={ѣG#--MoVV,Y/@װ5_~hժЎAFw׋ҥK믿*|GJvhذ!֯__5L?~={ĨQTx5Yz^ ___mW^%kFDD`ҥ۷/PZL&ŋE&L 7oޠؼyL___i=@})))4hޮwrsso SLQZׯ()^^^ ;;;W^8vX[RR\blӦMUbe-"""""ɓ'c[*Z>EIvv6]&E `ffP#3p@}ۄ)///߿͛7;p8qQQQW^ {{˷~a"ݻw#((]tAz[nŋx Tׯ_GHHH!{]}رc5k&w+֭\\\`oo֭[8q2331uTd2l۶ >UvZ1$ ((HLL|gžީY&֭[#G \z͛7Xakkk];w틣Gbޞ,XGM6hҤ ^~-=wͭmL>{Ԏ?WWW...ƭ[pe'qY_rss9sԩSDӦM+\r~~~ud2֮]^ff&<^p@LL .\;w t:uRu4L֢ӧ?~s̘JK%={ B`eҍZT?~wvDDќcӬY37NaGheW\+W \Ä TV֭Qzu,Xرcvء9###m&&&8y$FC CBB4ttmܾ}[R~aJdŀp ay`` _~,4L<V=|>TؿiӦXv{SbE;w=z[/^ŋ<Ξ=aÆ*ݻwOi9m4h0a$%% 322Dwjժ{(M Ҕ… 7:~ݺutRQ"^dd*jNNNسg/b˖-hѢ 5k$'򅇇c˖-6jw.քu-lݺEc~Y7pKDDDD"7n\G||ܷŇpm~hT*(oؼy3<==h"<Xl<==iӦZj«Wѣ~gTPHѶeb׮]8x -[&9N:ѫW/1j(U RSNE޽ ollcݺuXdIQO?Ll2XZZbٲe jyaҤIXp!%)8{,֬Y˗d2Zhv/.]*TzG۷נ)C֭?b߾}=Çcر([,|}}Ew`===5w\<{uU_lٲ1cT^:::6lV^ ^C~5kDpp0s)WJ[]v-pѣy T)S`066V9~ZD1cǎŰaÅvqgK}z)))r=-իWd2 ~ Uhh(⠫ +++ԩS;wF͡6F/D`` [[[ԫWC ŊBbWQlmmq\z{Ǐngg]O?TupXx1^|)bbbXvvvSz衕$ɏ5x`o^c:8TxCj; """o~Xt(6f} ӅwT'}6"""YId-""""""""#o*URSN*U)9<<<>gll`²r-qE&O>'|1TAilʖ-nݺ[n4`ر;vl}%۲eKu’cll.]K.rϩOttt777u{ttte]&<.S B֭5_2eWqK,--_/,Ϻqt_| Gc}aeUj""""")jժf͚hڴ)<dؿ?;;;HLLDHH߿;waddȉ> L""""""""""""*%kQ-d;w. U񋏏GvvC56QqKLLě7oITQbEDDD ""1,--o>4nXxJ;&kBTz(\d-""""""""֭[mRlZlOjl<ܽ{Wc#FOcxʕmڴA^/^9FoooTXQqVLҐ`>033%5jWWW"6lM4$55HPEDDDDDDDDTrY[[~v]MP"z7n,MIR nnnjo͚5K!0Y#G V"""""""""ůoYъd-"""""""""""RNYHH;vLhw΁ @Ŋѭ[7oCwҥKB/Ʒ{nЩS'oC[p:uh- 3/_zZ,î]DVΝcǎHMMTV ;vrDS)刈f IDAT>,L֒@::N%"*m8}XYce-"Rս{0yd[,Z?ܹhҤVΜ9iӦ mwwbI֚;w.]t)UZO>^fV-[7n\\\J}ٳck׮LzfΜ@?d-8qd-""""""""1YKuٳeZKQ`#55BB JJ5B2e#]]]t]a}4\\''G̛7GUz0Yj* GZe˖Qacc0>C\\hW""""L){EDDDDDDDDDDDDYzC1tP899a޽v8DD!VVVhѢtPs66o,njڵkk1"""""NNNyЮX!z(WQd2h;"""""""TWWWܼyiiiTBԮ]3GQ֭[gucXʮH(K{LDDdhhGGGmAլYS!hT*U/QQ1YKEzzzpssvDDDDDD=z"''P~EDDDDDDDD=KJ"~I(90y%%Prbab%%@{dggke&k{~m@DDDDRd-G!** ^:j׮]x!>} LUQFSܾۜ}QQQHHH1 VUff&35kքƶ(DFF"222 QFTIKKݻwTؠA ;w >>(_E||<`kkʕ+j~DFF"!!hݺ5WG!44055=\]]?ݻw8qo_DDP|yԭ[W Cxx8aii)5j(q """*}?h9о~Lޔl|J&/}!W>rcQtQQQwwwlٲp)\ϟKl[.[Snׯcʕ8rDϙaҤIV9ӧXt)oxBy =zPilUbɒ%ضm\,2 -[Ĵiо}"M;v ǎCPPXŊѯ_?xyy Grr9]]]m=իv m۶aӦMVڭѷo_̘1"c-B~${1X.]|o?#]t9sN8z(V\s}0tP̙3&&&jmG]?>vڅ$s:::h߾=fΜ-[L_[n͛7ŠA0}t^~G){GzбcG54{rJ=z [nӧ:vl2---!|E;vLtkڴ):tu]n]yg_~lڴ 'N_ ]]]ㆇ䎗͛+]oĉ2ehqY@ժUDž}!::ZyL `1byaĉB{hӦ 222??@DDz7o()vHNNƺuj*<{Li?333k:uСCQL ""[LL .]W pstDDDڡ "]]]888O>jM.0Y`eCYB %nggX`V\y= 2f¬Y۷oɓp$,ZW\={$'lmܸ3f(&\rW\A֭k.I_!!!ٳ'>s4i{Ɵ1c~WI}?_~7nĦMЭ[7N`` gggɓ O?ѣGi;>w/..k֭֬[zE!JHII)tT 6 wg߿ ,iT/773fիʕ+q9rkVy;={*Lx~O8ӧOcܹ*0x`/**JHE֭ 쟓A?ǝ;wpDDD`} &M[]իWعs'v܉*L$&&"<<\hgggرc:tXE"##J qqqHKKðapDDDo˗듙)zS&"n,;8p_~\X^~:+].46--M޽{ʕ+J{ 3g`ڵ&W^ G~III8p8OOOԪUuルWijժi8""%##C#?k׮k׮޽{3Y=z*=z(/H|=ݻoFTT$rrracc-Zw>g NHHDž{caׯ_ر8z(>a``Ca KNNɓ'Ǐ!..*jժ]vرJ7yݻGFhܸu_~:u !!7ovl]R=N™3GB RJTjժO? rEvv Q|sMժUѲG%"""R+k~EWƊ+ ֭>>z>>>3fLFΞ=s犖j >>>Q,%%;wČ3ܹ &`׮]Ann.ƌ#JԪPv܉f͚޽Xp!Uږ zÇaÆחE`ȑ{.ʕ+'y['OFrr2j* 2D85 'OÔ)SвeK{gϞ_~D&M`OD}_zٳg?:ѰaCţiSN%j`…5j(0<<e?#5j?\Ҷ1|@5z Ͽ6`aਨ(=GUu"++KWWWQ߫Wo߾r`D 綼={x;S o޼~AڵkѮ];RSSq!رky1b\֘1cߢJ*rܽ{6l;߈#kcQvm!wj:Se˖عsudeea߾}5jPڵk={\8<{ ۿ?vZΝ;=z(QAزe \\\_Ɓ0zh!.,, ٳg%_ǝ>}:>} ===̞=ƍC DGGcȑ8v옰Μ9s0b&L >eL6 *UR͛ضmQ'""H]xZүƽ=Dok0t`@ٸ }: -- ={ٳgѤI!Q x[ekҥ;v,33SKvv6Ǝ+ 4K)[,ƌ___/:th;#={… Bf͚@8;;# nnnVI1rHbڵhҤD-hذ!<7M||7׽|?1rHY[[c2dט6mJ)ȸqDI;3f@@@\,%%fX}\7{{{۷G-7nRSS%mիWFFpYQPVp888~^~7ܾ}[hׯ_ΝKwww?uś7o*ddذa}|r?pttkee۷a… {>d8y$:v4 ѣGdCEQžۇ5k(LޞsV^4o\yEEEgϞ̙3$YTݳgO?~\n\=== 0ǎ~͛75+! >S={ 022 AkkkaK'ӧ0771|TPA|J?O?%''WxcvȑXbD-Ob DFFZjc'""ӭ[$NDDDKhh(bbb.+kiԩ޸wqnpske"8Ν;w3ӧ7Ӱj/?|{><<<\NNNlٲuv&-ZִiSiFܡ …BMpaL&CϞ_qƨQ&qqo&$[8qSxcunի$-}}}|y8;; *TӧO;wnȑ&Ï۷uacPvm۷wPaZDQF ,ZHiL&ĉq)a٫W`jjUVH&L ٳg_+燈5kֈر#fΜ)=z(]Gk׊?(%JW)ŋD+3fJc?Sビ'O ?ڟ8q^J8qy&Y3x`Be?.qյ֬Y#jO<*YXz5.^;wsN$.ed26n+++}j֬+W/oիm#77WaÆFlmmvZnZv?k׮ =z`ҤI_c߾}z*`ǎD7$|ٯWYtS3 ֭`Todeex{D^J6v~;kc6lX`ǏaӦe_ٳnho)Xl)/IXkNmN#7{ʕ+DZuʕ~wuuŀ1d|(<|o/vݺuS8˗/1jHEvj?f,_V/] {vȐ?EZ:t HXY`f,\Pi5wڶm cccQooo/_UgggN:ٳGԞ2eJZ|WXbW\GPVBUÇm۶.]hڷo/$kݸqy?~~ݻ1{ By]Vr"âEd?~\ky_DI&&&*t=LoCm߾]rV׮]QN߿8< LBԄ˗/BӳkyЬY34o/^ azzzrǚ22 /FNo޼ӧѿQU>V&֭Çkd֭>}؊=3f ,YW^xG98رCZ 'OpIOk׮ &گiӦpppׯ+U4A}xc"w]%"""""bӷo?^Aaժբe?LmTT g$ܱtRQ{֬٘3gYg-[{\ʕ+vgMW{셁lŊJY+K}K GA׮Jy)DDDD%Za:d-"͐$fٔ:y866ViK. -,,0x`I-[VHޱǎ+y]UעdC򌩣GUXQwocooRe,ggg ˗/&]z~&4ӻwo9ׯ_\!5aM&aܸqexO B =^r*@ݠA+嗿ڔcIx!W-(D9s[UCJN)G`jj*:'''>\pAxɓ'KZOQ߼cm۶ 8::e˖ŋ'"""""""RZDDDDDDZӧگo~Vо|2BBB$o믽affVh ܹ1~|7 ;VtRhcKO ֋*^*TXD1߹s[̩ؼyhe˪5QYcKe yL" ʕԷRJ$egg#55UOdd$v͚5a`` i|W,y]En޼)j;::J^W%&&bϞ=={6Ν;EhҤߒ%KD|R6T`zT6V?FttnѢJvB;<<\x4-((8 IDATH\DGGB;33SrŠ*i꾧R~߼iE?PDWWW. ?+ϟ?nQy7m68p@zXѱ7JQMbX~ܹs8wtttРACСܹOFGGG0i6v~$j{111 G%/^U}O/d^$әM}gݺu_P/11/ŋamm-ZE֭ZDDDT DDDbԐZȞ={p m!(?U}ݪU+5JaQ ұc'/Yڵ pqq<իݺu%kKUy5k044Dzz:[r}.\8/jջ|nae-"銚tIN:}(/zm۶$~~~6>J8::bرj88>>>HIIH,DDDTzrOŊ'CIU*}FPL&CʕUO__rcE䪜޽+,7,/^$ ڪ̒NDDDT(ŪKJ[}}}_V]_WWWYĥ=zн{wt666077(}Q)&UcS_}j)Cϱ.tuug M4App0lقC! @,`…زe ֭[Ν;k!Z"""""""*阬U4"%%ɱ8·1K"Ҫ+"** YS'k坽 g)AJHNNFnnnG}Ç4xf3qy|""""ZD-*;O>%E\xXTXQҺu,_\Cڵk sI+_}kaa!jO0+x%Mx{\:99I^(:**JkUϞ=sq"==]rdI,--Ec„ eM˗/q} gΜm6bϞ=r㘛 c>}5{yǥ2>Ǫ&R46b̘13f RRR>}nMf=z ((bDDDdڵk Bpp:`Z>v """""Ң ݼNޛV,Wjloש (SZ5h 6,xoD0==)))*DDDD&'Pɤ=fч$'O<2 B;&&~~~͛E7nV< 'VٺuuMHH$ wQU޽[T%,{Th޼Ўř3g$iҤ}vyFҺ$'''IU揹8OtT%M6Igaa!J( J&M}}}}]>ˋ=E{;vm}}}s;`)Ț5kDf͚i,TbE 2D޽{ZT 34ZDDDDDDZYxtmoxVggg#&&FqHO_|vvv.x7o5"ժg&6>ʪ0PZD^z?݋O mGGGf͚B;00n*tݻW6T":I´h"VzJRAnn.V^-Z֯_"oaÆǏׯ$PZj%m6Iv%owϞ=w]r/^rUC6mD3Ѿ ?^+n>>>,߮]-wp_oϛyũ}WXB;y$5ڷox1ĈH}ʕC֭1eڵ <@bb"6nܨЈDEDDDDDE.\(r_eQFٳgl))Mz.WׯM#-[zڧO؆vVV7\&k.}􁅅оr ֭[W:!!!XhhٸqԎE&aԨQeӧOGJJu2220m4Q%FBϯă'N`˖-/?ԧQFpuuU{={ ԩS%wΝ;')}3ftRaQ1###9R6322]>LJJ7|S`E___z]`ձ4x{{d?|pTTIh_~sΕdkiǎS8 BbbVTL2-&k,***E ͙3sl,--1vrg"IEׯ_-bccMMM?9s@NөS'ׯ1rHDFF闗~ gU;QRR«W`cc___k͙3pLVVׯ/󚟒޽{Ea޽իWK۷sΰI*DϞ^Cqʕ"}?|~ f9oaÆviO$9裨(tna„ Q~GHϟG޽XoBBlmmq@AAA5o> ֯_kkkbDDD`ӦM077ǀ #&&n*7#9_o߾UVhӦ ;w ((޽?UYt)Ν;'l닶mۢO>055,޽w U1|鼖/_y!%%}Aamm ]]]Ν;EhΝvڕi=qͱ{n7NHz94k ͚5&RSSwŋ>ZFFvB׮] 044"0c kڴ)k.x{{ƻw$jԮ]v*W$qM!???4n_5[nݻB͏?Ç$e[l3${000[HMMEtt4ܹ#\7 Rao޼ƍqF066Fnn.qmfϞ iE.XK.-9bXlpرcpss7n"ck׮Çve^<ڵkvj׮&Mq066/ @GG;wȉbQ_N*y?;vȱٳvQidee=fA86udA3g`ƌi"frx'ߒnml 2˗}`8<W׳b8;ӧO )_rC ֭BKKعee%tбU#cҤɨ_~qU&o\q0Y˶m6ԩS۶m}^^^;hܸqbii'ObȐ!BRMvv6_Ϟ=vb+?cƌ*&92QA57bǏbO6 -*zqttF!$5@ll,bccYxyybv6xDDHRPaFۥlg ޽{[vm9smڴjУG9r#F233q ܸqH_ÇZg„ 066ѣ Ddd*gRTTh?Ooo=[} p <~~~o͛Vnpa>Drr2.]K.+WQF=edyZt)TTTpBw+2!hii 4q=ܻw~ 6E*}T=GXYY!// .pe$%%!33na54hH4p i1YYY0a͛Kܑpw9s\0iD&)ܹ &ORnl;Zl%1fh89 Ƒ## Gzz:V꯸Ge5kpeX[[ﹺ:.\TH<]vE@@[l,Xl\]]K#NqGYYCŝ;wСCgĉpwwy}p1[+Nv7I&U{nDEEVZՅV^]9@A1777ܹLծq9,] 6GVVwv* D=͛7ѣeգG<}+W~ԩ'''>|XnUݻ?m۶& ~~~ؽ{DK555Ç̬ľ222С6n܈.]:wUƭ[0}t055h^%%%:u 3f u&M@EE<]?ubΜ9xIHrttĖ-[(F[mVBDDDDDDD%be-"""""*}_opQ;v@>|(Ux\;vĈ֭[ nܽ{vUⸯ{-כ}UTTpi?7n\_v ׮]ڲe лwl߾3gDNNAYbb"VX.FDjj* -- O*޾;$aJiiuSիWcRر#<<<+ &&PQQ&ڴi +!##CXq իEff&ФIC۷ ӧOS@SSٳ'֭+^fڴi6mZ;vč7!++ 5*qO)))aʔ)2e "##x$&&B^^с.LMM+,!$gIZy]QQΘ5k߿$$$ == 4v*uq/YYY̛7gφBCCw֖jbjj wwwիf͚rrrB2 ŋCBBȧﭦMBIIlmm͠իWHHH@:u}}}hx{{#..PUUtttЪUR7VªUSџ5^EE7oƊ+p%DDD !!jjj0227|zin ٳG1/^ m۶;pyJJJhذ!Zn;Jo 2C *vލݻwG[[3f3"""񈏏ǻwe˖eN$""""""^= """""BpwwرcP8..QW/;lٲE=?N}*|`dd'VZ&&&UoT н{rO===1Bר,kƠA:֠A/mmm̝;Oh).eeeX&Lɓ'p^x@ݺual={Cдi2#)yyy̚匉Ù3g㍀;HHH@ff&䠩 mmm믿ƅ ƅ ㍘ddd@VVhHppp-KIX__6m/,˗/8VkGDDDDd-"" o޼iK[ &`Q5'pr E~~>p211y?a޼߿G~~>4_NNH05zRUF![nvv6!''W ڵv&sr!'ykhh@CC<#"""4Us0Y*HZDDDDDDDDD_$9""""""ReJԪ eN$%%rKGQb'"""j222UU0&kQEsssɓ'vnPN*Hr&}-EfY!kܹsGXEfp|CDUR#Y*+k%1Y>Dze鉗/_"..N久 VQTDDDDDDDDKMMׯ9]!QcUǏȑ# Ë//|?`lܸ#LMMouzzzU#L֢ b>SBB233TkW^ m @DDDT!""pΟ?';'Opeܸq^Brrjժ MXZZw>:L?~ !!!BTT$222P~4hP79llzEҿݻw8w ϟGxx8RSS C߾PRRٳaþ-v,oooU(<:uC\\޾} 8q ;߃qeܾ} HIIAݺu ++kƹsnx-Q#mH4.==nnmsѼys}qUhwFF ˧N'bbbuhѢ pD=lOv [p <|ԩS:::ر 6USL֢ +DDDDDDDDDDTVW\+kU11ј9s^|6mWbHLL,2&44T\aa/d;&gϞƍشwt+WW_}Uj-ع/$%%y>**Jx|i WAKKO'r<::AAApuu&o߁]ĉصko'ˋ ۶?Ѐ#<nnE_h9 .]r/\LL ?~ ooo]={+ѤIR}ňt]dW"KS[n1< YYY"p]97ehDӧ?;}}}^z"ɔ=zW^ŊO?l"6sݻw͛7h@NNGfХK+6]ϟ?ѣGx.0|pl>|4i;v,￑ 011AN$HNNF:u kkk۷ѣG8x \s1bDvQÁ#??PWW:u޽{COO\΃:aQr~F~~c<<<0a8Kƍ۷;ݻw֬8sıwfeeJ5s޽ -_BBw|z)))녗/_H<&((CArrD=<8{֭d"&&Qb\,[|sԩShѢb… c!Px{{c˖-Ν;ѠAL:QFUjԩSYvȑ%kEEE᧟~>#xyyaڵ+6n332ŗӧM{|}}zj̘1˗/GZJ3::˖-Á׽۷йsgTU֒K~ޘEDDDDDDDD_wމ@iqx=RRRQnb}I,TdKLLĐ!pMUeɆ`DZja1Ǐ#""'OE #ZTatc?˗/K?==k׮EPPN:%%%~~~=<:v숛7oJSuoHK.8~8#Z޽=޽[b}%''g1{K{1mmm1mtZ7oB~~>2331y$xx\ ]]]>zzzh@AD9skז"##s済spp֭BEEE8fjjAcذa1b8Y5_~ w)Sb_ gڴi>`I"ZF8zH_Caʔ: a rju?011'ahh( hܹs{xxƆ 7n\D-'''t ڵCff&qE\v 8q m\\zׯ_ TUUkkk!44oɓ'+XZZJެYD-+++ 8:tÇ ===1yd߿9Ǎ'ellѣGSNhԨԩhDFF",, ϟ(KI+zL.61Sk׮72s+.\97z z雚B{EQWWĉߕگ=z111Bή7v[6mZlE7i,Yӧ(ߥK ڍ5˗Pl\t 66 x0gϺb!syŋРA7iѱc{2ޡC1{ *3Ra۶B[QQ .\qclѣ cĉB{˖-Bsڻw/O>صkW_Г'O.2/ѣ-a @A___9 ۷cڴi%pi'`:tH?kkk̜9˗/DzepiNJ7N$QKGG077k~-\\\пrrr0rHBUUUN< 1bv%dȠu֘0a0qDC0|pݻ|o޼)ۻwofhhN:ϟP-Eq_FSQ\e-&}f^DD! ,--2G5-Z!-- "ZϞ=hdTr!rh9s~,ڸ!$V]]]|dIi{iϘ1DbY矄cvvZ}7D4551nxlXPx#//¼y?88qBiHZ)++c,gLRw"3GT 2N*GK.żyDӥKٳG]v8fժU"IԂ ` "|||!ի7n$j}J__h֬p,44T$o߾bˮ9H%ŋ ^reD-q4i~Aˆj>&%k}),XOOOO_uuu7N/^DVV3f,$uc%2%%% E S%;sgmx6cL(*~kNN6nܸ!b̘1jhhhh@:UuqKwҤ"ץK**2zXFFӧϔh܀ծJUV֢jM^^kڳgOSUl;wB /u3f`ӦMxg[/K`c̘1±[b)## svrr²e˄w{жm[~KH+k|L""""" ==AAA D@@@mllpȑ*͛yUFW-,QS:tjf͚8 )::󺺺[P}k˖PRRԩPN֪(iwQ&&&@BBBֶ߽{#{ *++wB|V+5ի'Q_ ۷?BvvDE*ƍajj*8yyyѳH7"E՚+j6mddee-vqqhzaĉظq[qe]vm̜)YСCd|@AYۗ/_qz@߾}ѢER&L]pHV2oooO """""""""EDDDDDU%>>?#IJJȈ\w)PRRn޼ X$+??Y>GTT|I"Z0g\,Z kk֭֬KAЬY3Wͭ"m]]9YKҟ˗/DV+|NUdKAVm5E ҟ[ͨGDcUkڵ+yT}E qnn.޼y#TPPHK.йsj3$'' ]B]]]rrrpttcnݒ(Y^ 9-[ǎcȑ%"""(êU+%UI,hBh=?7lط:tq;<<66C*V-UvRRT㓓 Wc*/IIIwDsQ)p>K5))Qύj.&kQ&M?PСCq%㊊h׮444P~}ԯ_**~JzI[ݺu!##S<,y.m&H[ҋfkkk Ziii~ 7oTTTn݊[۶mC^&M5_qZEEvv6VX7oޔw Q2BUĒ%K`ggÇۼ-[DJJJT ""4hĂ1B7%,--`/HMMōンg]k76oބ~]a@EQQQR.p*|tLtkn]>>@{%+ _LLH[MM{+R*kc-d-Q8 +WO>% ݹsDZjjjBvuP4sI/iV~bb"6lXuTUUe˖aѢEvpxH/^wޘ?>-[&q,DDDDjBvmKU~M$d-N:UjAGG"""""o˃;ڷoƍ˜])Jf``ϟrrr#U^eK555k}xشw[Vx~Ӧ\S IDAT?Tb͛7iGFFJ<6//R EҮY~**Dɫ<>*OIk7555!55v%T prr*BWXXDY(Ri4|*ڤNVǧqIl۶ ϟ?GDDك#F@YYYʕ+)U}WxK_AQQIh9s99ussCFFFE&07Z:7GXvVb~zOu\\񅆆Jܷ@dgKz#8655E:k#O/X?UCL֢x_+ _/_J﫯iK4}HsаLJtmll,؏1vXU+W ./ƍ'Q™VV3%CiӦBrÇ?kN"""""""""??Crd->ݤ|[@^^6T$%%aÆU-Zֶ^-3>>/o222h޼E9~ סk׮06n\]vGG|y&߿gOAoffSN({.̝;ĸlٌINX[o^dcǎ?E5֭#Fڵk[pr}DeK?GDZTcɉ%-yIԷ_~pvvFNN`Ν;w.Jk׮IFUC׮]qE;8p&M*u+W(Ѣd=e˖AGGԾ6~h(++RH֊sS_kիWqU<}o$"7=add{OPW/Nq9\pHHHZ=4nl~޾Iݻ8y «W XZða`hh?7w27jqqqڝ:u0p%DGG#!!ʘ6mz q%xzz",%޼I 44ISvvvPUU蜟={/+Gވ-ZxRRR갳 8u$BCC%%%W/; 8 jHU222UU~LȣO"""ʑϿ j 99:t22嗅PWW/u|DD8oߎ-[aȑ"d=Jz.o5·~+HMMC=z 5*?''γS9"E5FӦM!///d):&##K,x bРABӴ4?$}9sHFU8qׯǘ1cT옼[%ZlUx|ضm;Ç///cI&͚5ӧOqmڵxlݺčbѧ7xήfvAѸ N­[~>}b-{o0mtkgccc<زeȹԮ]k֬-za%pwwׯ..s`ii[ ::سg:~|-c055n$tuu˗agg'xx8xYd ܄388:tH8::ѣGPVVFNNNZÇ/_D^p)hh?###Gׅc2E6l:Ν+y???8::&9;;CYY'Nik&kUUI˗}!//uԁvE&aþE^^̙#TJNN\VV>{ ˖&rLII bO2ƍ/zٳn8HDFF߲e+޽6)**B^nƶmbƌx=um&//G]J]cU󻉔@vv6V\Qlnݺa_˜5fXhii =?-)EE%eZ2ԫWG=pq @kAGD5F>}:f(>??NNN2e Ǝ NJ+_666h lذ'O>\r033޽{ pVכpAo^:tٳaee֭[y&v!;vU:Gpp0*T /_,a ƌKBB?u֡[n8p@BYY&Me#"""0k׮)ն9z "00ׯ Ւ ^'NKVhs:t>|FkkkAdd$ɓx 77ǏǕ+WѲe9a `!"Z>|ж9^G@@?M\kzy]ܹ Zj͛#33_ȘӧI֭t KKK;vw߿XTvU| G988[DF; )ٸq1bĈ >+kL֢򒚚@Z""""""""*U}}/*Y ֭[cɒ_U֪U СCtttDc%jiiiaڴ:ut'P q [YzC9s΃ԩ[145?gDwk3+ ====vbccKwdYY44~=c[%J:jmڴٳ`)E: #6l\Qd-QƏӧOڵk nܼy36o YYY0m4mVd-=z40{liNN WFFg,UVYOµj ǎ÷~LU>_\V ֯_H޾}Э[7ȑ#8r1qZh!)N$GNNkkk'"""(oD3W٥C1c&v؎ .^={vc PSSáCѡCG&MG4i2F!g?8rUxnP״iStӧOǏͭk:tHwhѢE=3gN m,Z3ggcc9X`>vK8гR+ Xr&L(򼕕L~'rquѢ_ЫW/hjjVjDDD$꺹&kQY!((w /^A׮]+%YJ ㈈H?^~]a450wr"** 6XiLMM NFJ2/i` Ɖ'rwʕq,'^;-Cmٲ2 맄444vɿs/Z{ kX@UUU,[صk'Yׯ_y((-(; zZ[ݙbX(d!FFF2F(_TTTPz T^F3!!>_YdhѢEubccE+kժU;={R@]]=zD=m(^RQF Yll,d2Y)[(s.7dd0`   X|y-QİQF!44@)&S344 ~gl۶ ^۷oTP]tA~PbE@ڵ1k,aʕW͚5qڵkоf͚zyyd/֭;wرc8}4^ C۷_/e2,,,pu?Fpp0```&M``ï۷oҥK}6'@EEe˖E `nnggg88DDDDzW e---L,6V ±c[R;vJ!i`I2dm׮]y:r|"usJmʔ9kM6] /zǏB¢"IctuuADD܃D󛉉pq13SL޽{طofMᙼ(e4qNVO{LDDDDDDD;v{0(vzӒhjG=<}OOݕog`\kǬEheȌU迅a-5R5k ͚5SJOLL;SEZPVlebb޽35fyyebb"bbb䶪޽;w.lB~ۣ}d2}Ɲ:e2p>gBB /n2gIǩ`Ҥ9r… ZcǎϺC]ppׯ_'L(]5ЪU+xxxd_KKKtMRݴ}L0Q4)<:tOaۅ TXkQ~?711A=p~@HH0;:::(YPȤv*]4/.~^^ IǕW"ʆ0,\K,AvP~}x5^zGKvԨQhҤI/ ޽7N:7776mڠF?\+12~ }},)G2M11rRjhԴll\FᾌcTiE!"""%ZEZDDDDDDDDDDDDDya-HLLę3gpI틥K={_ٳgzDDDDsŋ)>,,TT.Qd%44mZ WTɒ%"jhCCCG*}+iﬤ?s """ q4""""""""-W֕@4lX_ҥ+V;(ܹsFXXpoFR`l\ 4@&MPtRzO<̙3ԩSѢE޾|||r&&&vF%5pbX( ann>Hoaaӧc>gnZif޲ebӦMhР!"""ܣ#lAxxxgO١---|TFEE~"7f'AAAL&T?k󃈈r˗/55UT(Ka;gIR`ll>BCCc ɓQtS~NHHHZRmCDDDDDDT &Q`bbl笷DDDDEOfD7`ĈdYK$_"˺nģGգrs.ظqP^zzrwb7 ]M6P^zV\\֭[+֬Y V ^qKJvTOסo~Y֋?A%ψ`5Ƣa-"Ltt46/G ~Wz?۶mʟ?AMMMH+ha//^G(9sf76fҢEsDEEzr† ǎ۱db([!"5fر#z/^^=UVUЈS@5kB:BػwO_[~h޽Hw}&Xj

>>X|YnMЩS' wkYۻwܠ]jD7ڵqqg$MӧOXzUu ?.z۷oׯ_r=$GDDDǕZDDDDDDԤID3gVlJEۆmmm}ƢKN 8q<.\8/l37cw}Ԓ%KD+?̟?SNA|||G۶%Jd2 5Zmx6..0ctѶ)S]BSSZʁXdܛ?޽{ 3]peܹK.2 cƌqDDDD3~d H{DDDDDD;ɩ7<ǤIpov(^8ݻW`ӦMHLLרQ4x-ڵkS~Z ]vFEE+V^x5jѣ`ݺ¶;wСU6jժx>|a⥘8q|qܸpw?{"##ѷoo9 M6-qD;u 'ޙ1j(;.׬Y}g灨^:ٳgn݊8-[FFx¯ 1113'y氶ALL ӧ 'MJ*)-yA*:ӧc9ڇFCDDDDDD""""""*kp9a۾}{o^7f4L&u-Z a'uuu$$$dfEh֬"/I)_lڴQ7ܺunՕ4lrԭ[W=7WRSSn89ӧOXn(dMauYѴi3{]xQ ^ص˖-UG_jjj`j2ej#"". kQnXx)W;QAfee++Q9""""""*/Wݘ4irWyVEEz9ԩ3011QI&cѢ(V-6{5Fpʲhb>|jyUUUhgϞ3|||Dϗ*U*Wghh'NYd2=Dɒ%%bJ/z*U¹sacc#i4i-*F 6IzDDDVǰggg <AӦ{8DDDDDDDDDDW""""""*1ko4h0<=+#)) %K9Zh=zfZiӦ e[[;#Ѻu3gNЀ:uꌁe+ lǛ7o|011hDm ҲTR%[K >GҥKzH.*U-[w033WhXf-WWW\~ ?Fbb" PJݡ ٳllld/ּysܸq#nzL2hӦ-  7DGDDD W*:""""""""""""" k塨o䶦|† pq о  ;;\3]vÇm@˖-ޏc7|`mm{e"bBȑcZi>%_õo 6* tM(/X;zrRz?ÆׯH}*b{VST_[Q}]9ss3///qr8ŋׯBG8"""ƒACuREí[p?Z޽Ð!t|gٳ`ƌ:saaa ]n-""Q|&Q0UΝ;|2{8DD6m!bŊqh׮{8DDDyѣׯvMScǎǑn>>1w,X0;vBZP*T(H}o޼Ç*gE([l>pZEZDIJJ/ AHH0+qƣyQ&ٳ믿p,E}0a!5h`t5kք6?]vb=Bݻ]1`@O\\ pd2Lf͚VZ;?~sa5Ǐ0|P>|Di܃ j99PNhjj" wżysフ+WJ* 'hѢLM%KD||<|q}\/_7n,j׮SS髚*Vv̘1v(]4"""p}ZZZRRNZjv: .yM* jaa(]4زe3N< HHHqPJUT^]iQK. jo#FZZZ'./_ݻQZ|1Q0U[>a3g˗/A~(u5lдi34h0FT0K?mt 48wEDDDTqea-"$%%ݻ055;7n b8z(˗/ptgϞBOO6lDmDlmmakke⯿rqIGjO<zzzص5kР!4h9111+W`˖>|D ٿM6ӦMm+[,v wo'<{/^܏96mڌΝ;C&H|b`nnss 8:v/5ؼy#,X(V֭}N(QBxNGG-[D&M0fh=z@r0lĉxR~Qyҥ(]tz5S//[^nJMM 3fcǎ /={ 3gnVjO͛s֍ƶm[Evܕ.ZN19G8k5_ۋܧOtAԌvuBtt~RmRf޽{}s[ _F̰{^H&a-x{ \{{{d$)) +V ۶mKJk{ yWY98qBe˖erC- *`˖m=aƍq4DDʥ*,y ?zꈏEETT 4@Ӧ{X* o߾U>DΝ;SNx9\/ AHH(~ mmmNk4k uX+u<+W2GCDDD+k} Qfj*Iu]]]3UhZ;q&ֱݻ'lB~3m;a}@0]'#""B(7h0q@=l2Cm3{ZEEcǎ˲>`˖9;3jjj4h ׯQJ5jta67Æ օ E!I&VڒΙ3ǎpGVÀbV4551lp,^(EDDDDDDDDD+*`EPWOBgfOCs%JhTRJgF7z^RoߙC """"%ZD99TB3QX{µ̘>>>nܸ!*O4YҸTUU1aDL#LuƍkܪU+a#Ga-^EΒvσ/k 4gϞMׇTY&޽ E||kfZhҤ? V 1115<""J#<<ϟIU*r"***@DDDDD4 kR[>@DDDDJUy7X2EB9[JI{+ZHWL4*JlWVT̰=QA7t_zlkk' #""S~K0cccEZ/.VHH0aff&nܸ.P(QFCvQ^=||>۷o+ҥWhѢ%VP(:uOƇ###TP;vB>^~%ӧ \?fq8:: Ͻ|nn#˗/044ġCnpO<ƹspm"<<JիmZ>u$| T-$+N6FFhժJnݺC͛.*V@]ѹsga^| ]x%|}}eVׯ?5VoooիW/^]6Zj͛ :tX-kʕ+W^hذQӸX\~n݄/bEK4j;vAɰý{w燨(=z􄭭RJ\\^  xBBҥajj {{{t y2lW>,--% ѣG~B2eP%Efv1EDDDDDDDDDD_|RaXbŊ|r~#XXH ]FjXKWWGTfhᱎ$LLL"oS+;|Er)Ծ\r ^KHHHPmdd亦 \9q}__l{q [=~{W^s0EDDDDDu~~7nP^h1~'\pcƌBHH K޾}ᾼc8udϿ~7n\ǪUq&Xd)Uvlٲ=zl-[.\ӧ0ct8vq&4k֭[zz9A]=ׯ_ ɫa;::"22&ɓ鏹/mz~ g=/^ʕ+X|ZjERJYs^rX+0\`ܰׯDu׭[ss `&Go^ԯ_k֬EŊ4wl|ШQcj 6ܹs'MmoTPAxׯ;vL_g޽Õ+WzjlڴŋeW޼y[nb ^:/^ƍH޿v؎ׯ޸}\]]P^=l޼Uo/_֭[p~<|nn31h`̛72F^Obb"/^5kVǏw6oGGGsTv IDAT,] Gcǎ_ɓ8s4f Ç3i^}XRRX7̜9#Ϯ?ݻؿU[ ˆaBo߾hڴY~HxL0>ADDDDDDDDĕP(*5mmm#11߾}ʊ iAAAWʉbjjJɓ'a= Zڕ2hD\?'ׯAؿ__|)0aa-"""ﲰ-.L2Y!ܸq:Ν.YJƏGJ  &&Z69mVL>-zٳ6nܔѶmNrw^ Tƾ}P~5W-Z+WȲޝ;wǏ+жm[gZŋڵ3N:r! NN=ŋLۅٹN8,'W^eY_t+F%QĜ9aYֻ{.Zhws SU͛78|w0h@I}=zϟ?ljJHHi.Icccfjܻww-0Yf5ϟeϟu8~<`qaW*"*D7\(AJ;k7O],+::Sʗ/Ptuu mmmطoЯ__Ɣ_{Nm@~ #F 'OѣHJJ:6mkZj/QLcff޽rʰ@pp;OHddx2*Wx֯_' jGhݺ5ʔ)=/|||quxx\Phe8\V*UЭ#֭LjۣAY&Sر]x1118q<.]555㉎cWyFئn]ԩcxzӧɠ&LRԩҎϦM7omή.dbĕ+WpIaȐ!ٺ͛Q|yӧOƅ p ؽ{0y$lڴY~V[jU\=z/ZE lŋ$6O> sꗶvt];{c̃* 4̴Ǐ&cW?'6m bʿ?3Rҟ7P> 2dڵkC!"* y-[nݺ Ę1&Lɓ@SSSTu=z .]իW!)) 1b8<<.fعs'NLR)Ա5 !!!8zJ,ώo߾a)m]tuPxqa[JHO>߿>}K.eϠ !1r(̝;Ŋ=z811#F ̱Q{ȑл̉°aCqt'N,--qa YZZSpv'̟?+V̗1?x@rlÆMVٳ_>Bر(Q8nݺ%Kbٲpr-jשSgNN=]r|2Zj%w,yyy)4Ԥ-E׮ i ..W;'ҮcMDDdɒhڴ)`9{eL&C6mxe.]^._ AByʔ6mŊs}}Z+WVLPPjjweuz % 7rŊsjY &)._$*T3hѢP{N&&&%;/ʍ7<9̸qr4ر idY/..VtA_ ǀEܿ_oZZZ2+>|xְ4vl֫ ԍ=:vŊM[u=<<***4ir6mL(GFFݻs5e;L#Fo)jժ%<䔕~)ː]qƉϟ܏oz~(ХK066|K>c͚5رcGPZEZDWFaeeW^|}}e5*vK,Ʒo߄rÆPzgРxP^bZl bnJժKW/ SSScccoJ޿<{ƈ#`nn!ϟuVAo[[[4kW^_gbziFɒ%s<&e>^^^RJ捔,XYJ]]W.m۷o鮕x{{B\˴޿P޽ySyw̙3KϮ Ip ۷HJJ/^@||PАa߾C}H8 gCCC5ܒF__ɳ в{DDUUUG6|0\v@ ڦ{˗ !!6g`Ѣh߾;x>~,G3mg=z7o²euq3Vu ? tu 8@t `j*=:q#Gĭ[7ٳrUpp֭[Vm1g6)LFLMMzIF"""/?(I-?VﺺzCR`9=/҆rz^mHF[[ڒWKLLy W":Zz';_s)a߿˗/^"9~%\t)[oi_rʥ | kVܹsXDD8 Y։`ZEGf}&*ڷwCsk,+WKдiS">>wK[Qɓ^zJ*V\6mZ!::l`gg<~wř3w~8st}/ؾ}0s۷PjժAOOϟ?ǏpIDFFFeuGAHH0vΝnݺZ*޼y{ԩIʕ+ygTZ֭/D۷Ѳ=ڴiUr(_<O޽{~~pmafׯ_M!9haaOND\\J(ps;ϣ'""""bǎmWlllϙ3gG\V)Ό"O Wؿ,*T8r GTR={ ,yH"3hҾ~**)ԗqOeʔQ~Ax-ٝNs?/X(2MMMCFFF'=*5!uuu:C%߼y#L(ŏm4a-NTr*yglfX&Oye8 .>XZD,Cbb"\]]m=ģG3m7q$L>CcR ݇} &&yvZl۶۶mMW_& ~V߫ѳgw!%Kmӳg/L49aڵk^z:u bbbpAdiho֒S8t0 7W211npt& WWyBMM +Vw^ I&[٥J*x$gpww*MMMag###L6=G\>}ѻwܿggϞo*N% j.]/@ӓرck=ﲒɱ;w?--+塴ߏBCC{SP0EDDDDD9VF@vg#i"*԰rhܸ1~}2oii ~Gvseyv5u}ЦM,\5:t(v@__A-߷{N=zcР3f $q5nݺ#88K.ۡ5kӦ ?۶O!!B!%z90{lq,1dggcڴ鐑i$$$`aa Y999 7oD@HOOg޼vډe˖7Y<%wTT*!!1jBYbhsVQII3?JwVVﻈU塞R[GQ_˪P񉉉h׮߿lqC(/Nt~aJHHԻ***5ˡknoD{%kB!B!B!D(Q>%[mZ~-##"> HKKvZZڟ$e˖cڴiDKb>}0m=6%.0m3ΜMhc;~=111̀444Ѯ]; :}o[^^K6ܮܼ Cp-\xqΆ:ڴi1c%3h޼y;v,3]{ ӧիWHNN*W_ahkfXEEEuxm۱m[dذpe\ddggC]]:t8ٓNN+1sg&~u#V!4o͛7Dii ttt[h߾= ,}}PVVE@ܻw.h}ȑ0|;id׏^ө !@)))kEE&K&ysqqi ''WwCCCz P\\}nݺ1z5F vtǶm1~X֤uԉՎ<d-CC}&UU23Wz%R$$$kZhkiddT\pmז 66S?aD}V^4B`Ç}Z{'A&QZZ͛7(++P~CeD;1Gv*Ca„>GJJ \̴ ccFGT\|i[ZZĤ'[ݻwmmmXY[,B!B!UՖ #!D4\)LMME_8Pw IDAT0`@VV  A5*IIz")) KKKXZZٷmvLRWRRRM}9_B}ee0n87NXС:tPJJJl?ׯ~U$$$Я_?VVM ``Ps!#55g3bbBHKxxx`ٲe8+0OUYV?ӧԼ<$$$4~ff!++"y8;;CV例/"??bУGOHJJBCCQVV)͛c{ڷrdEEEq[UDD8%S0mSSSxz)++/_z )))4ƍ묶E֭}mN+rͣG9*U=U%L͝Av x9򠨨ؠ6ݻjr)ijdz͛`ll^zbXl)Vi6n <7oj9^1s89hDu4k~quuebٳgXc!B!B!1QeGmZB!Ҕa``X[[.\E!JUU 7on7ҰaYݐ#hUݼ#1i+W`h+SNqs{SŢJŅ jQ@@y :t ؕ+}:_XX_3b&33xXZ U\\}<8fdeзoԦOeGFaaa<B!B"?? MB2VFFQdffJJII:1FT^ :u2k9gϞjmCLZxz“'LV{׮ C”};Xkr"@Ç=k]HLL_mS}c3f7hA1NIv۶TjdzfC]]L۷s˗+NsAYYisI*))96vORRΞc ?~B7o>ddd#q޽9SvQQ8P]YKZY0555ѣGOhkk3;v+TY󡧧aÆڵkB!B ӳ„BHMV\}1 ۷o… )Ț5k͛(..?~Xvܼ Ozw*f sgڵkp _~9c|.FcǼ0`@V_>gzN֭QPP1?nn'2e9%%j1s="##cmڴԩ85\\5ͻT+o>ر(L2 GzU{>++ 3XI#GBΝ>C{n:u[npLi_ݻwTud-X`>ܹ9sC۶ jx nnې^F-q_cG3fwDdSa۶XmNN+50'Oo233hBx{{ai055)Ցd<~|:4G駟/|>~e-"#z"=Bhؾ PVV6^|)+%kF3+WSܹYY:z<=zĝ;:1B!?ŋ"11d->9/''֭AVAA3gve/\<8q 222 3Sȧ\cn^yy8~p_9ӞZyqlpPTT1Æ Cbb"|}}EsfPR$yR11bNEZZZ{H5)D񈑍r.B![(0!|.\www&`߾}Xl Y2eT|8991e,IIIzKlܸuLNNEEEB;:·z'*---\񶈋e#>>Zs.8|(owcBZhB(]V(/lC`iiiᎁsW-s6EEEpu\cKKK8;;YkYŋ3&iPZZZYY9xyM6sӧ}1e$&a+++ ..j~_1Ev:ҥ Þ8|dّ¡CXzdc2;vǎ$$$$ z'k_NN7nƍj~jjjSͫJZZǏ… o01իaU$_;w%k׬i{1EBpݿ%!BHs[lllЩ)!..qqիԙnjZ}ŠAaa22_[n2}.\99y۷5DGGc>vW׮py$%% ]]=k׮sssNo,:װ{u?fWx<aiL4{怒H(..f/_"!!)o={ȸHNNFbb"PTTUUU̬ƅ(++ãG|*HAO t1/ƍ-''O CMM ҥ+yrׯ yV_|B!eظq >|"۪_juW\V͛7v<ߛ t3SK.3PW5x`L<ǎ?0qĤ-.^ q9s?޽E\\,x<.C]]]aÆΎ`{\v ƍ1;ws{̘oꚅe~g|ذa#-ZWⓒ~ yL{„سgo0a"ddd1{,&BHC!B!B!-6n;ϕʰvZ;wN̑5-UUZ#M쵍EE@``}?<t{7QQ̡2Ơqa!-|/3̔>T*++T5&7o?AJ+##AJJP\ :k^BLKEgxC-S=)Pe#OiEF|<iܬ8~~x| `y = ,eeeɓG]?<;8ၯ-*>۩ dd>\ >SR];_|x0j<̗WT ~M]heF|<矜~@5_@+>!yC ~f @YYzwa:T,Xw@ĤզFZ^1pL۷{#$:Ӿ{wdڮ[0js;Μ9X9/^ggֱ׸z*]۷oWK +XpQvJ(((qӫ=߳gO̟p]!.\(pfe6mxӸ_jUTT0qVZ 55VN{cEPP <<"<d|>NwY3*7bcF\R!4|hn[PR>7o޿/֮ܯqqGdSNq`ٲS=_`Nŋ<َY*̃0wwvݑ\+HKKeZ}\]7 M`lxz¥KA8rXcZ/EE;uyfϞ7n/;;NĹsa̙񕕕aUUm~9ʥB!t)UA__ aUVuEo9%kֶ~U7Ju.ޱc'HJJ2DEE9!ƌ~:c¼y5sάvt4ظ֭+:z-XS`.=j/^~͹,E`|N} ![e:;B!BgFBB1bsFpp"B%kzd턟XvШ IDAT۩xsLJJ FxiiiƔT }}}L"--YT/k!))GǺ ~ `Wxѣ'Ag>۹7n`)t<}HJJ´iSj8[󓓓ql?r055>=z "^ yyyX۲ŕ } 77y.** p:KB! eRTm߿jsIT`c3i)~:::.'-- wr8:tWlde堦T[o">>ϯVٶr;%%EswБS?mmmVB\~~~?bG9QB!BHsSq@!B! >C o~aÆ92B!PǤ%KLٳ琐[nArr2E9 +%''U'--#Ga { 8_Y\\,m_3L]0dZr ,^ҒU*>>ý{pAtlڴul„غ7VYqq<==L]r>\b+QKJJ ~; <9e˖̱֭7{/0h Ns=aaaʫH=3|矘;w޽{^^ LB!7&ccF9oՊHmڴH!$'cu(uu (((4y+@UU[?yyyzzzx\kmmmWS9N0@֭[㫯 fΜKUǗ_Sڢ V_$!B>=E!B!Blڴ RRRL{ڵ(++cDB/wTNM>ϨQV|Wժ@||<"""y 6+))a,޽n"gXEsZpBx*r5G)))u&VQQs'--Kbe̱SNbD;N،~7n7o!!̀yH0$%%U;j'&&Ufee99&BUVdd7y[nB@ff㳲V;i;mv: ߿Q8'l:"4T礤$6vhBH5LVJJ +a%%e}[!y ++PZZG0mcc@SSαqqMh&f%&&؄B!M*kB!B!B>7ׯǩSPTTpqqiZŒmE!TX^aԈHaddܠ񅅅~=?ƫW/\YYBXmQ#BJJY-))2mpߡCf;̌XYYYoߞIGTT:vXX.}j#J"B>c&&mon *0MJJkvv6@l_cdee1SrJ&JtFFFv侺!''C"B'D!B!B!-!ϟwww7oe˖92B!|(Y4~ oo/<ݻwBWT&f6G(--emp V[!Pqvff$mXTQB$B< 8Pso޼a-ZWߗ_~ &8p BCf!!ջwoܺuiߺus֟^r> @9MRtƎΝۘ@II b%##Qgv )))xoOUU6Eצ}TYB!g8|0͛w߉9*B!|(Y4zzz"8{^>_xWMK6muV=IC&sׄKEO*{B>ǏskŹS5j-LmNVdoa+lƴwލiӦsZ HJqkN4EHbnnUUUJ֬YǩJ؞={:/^ͮy>/<11q>8 lǰ}-۷L;&&NQIII ͮ>;Ǵ!''B!ijjbX~=ME!0{)"1:u2+QK]]3f'p_x1bc㑑yGp: -bɂ"IjҢnV}}(ZUu"'BHҵk7 ~-mkWׯ{もɴ50vCʙuILLަ ^dee1eJZE=5y!BZ&@ !B!B!AVX oߎt1FD!U"-֩S ݿ; yyZǥq: R}AYYNLLiA5R*!=z>a:*+7!".rrrpvvn9ڷoH4ok֬7Xၬll?(((8077GXIHH`tbآE HV.\ Yǖ,YYYQuĉv077`֬Y(,,ar2o|xyy1ײkNaEBGDDo~ !@TYB!B!xXf -[Ń!B>gEZk:uD-t~]]]V;!!z46//ٜ*$%%Wr5j"SsN+>WF55:dee[4G.uuzB!͛\\\Fлwo]..N:wb907 s.׸>NO?ukWpvyxn6BII 6!ҲPe-B!B!B`رcJ(+ pP BB(YX]v"q11ܒwjGD܇ ڶm(@ll,t&S׮Xhc^_VVE Sc^~jWB!E[2_cb^30@yeQӦ}$=zGu\_ "^ իq%|@6mĦM!--jENNNJGGGz^1_3=f*Upw߅'|(YBL";;޽CNNrrr;'H !l޼%vډ9GZZ۶1m3bB![&gw("''m BGX+WqSRRZb4v_`NKKõkW9KOOǕ+4UXwެǫ-ɥKAHNNf_|ƞ8qs'N`Vo#BH˷b? }^X&kw!##Sg RRR T᳉Skjϕ<O"gpsێUV }U][EE>;v B! P&N&M Zj]]]t}1aL0_}:t%%%n>}:<ϟ+!Bȧ4}t̚YW_;GRRyfr@߾}!B!5{{{; B!Pe-b &*Qtt1ǎ\]IAAǏgipA89uܣGq Nsq޳gFY8OC(..jI[tO<ycV{ =~VJnܸȧLw044<!BZ#Gaذt]P222҂tqauVƍ0gDEE!''JJJ066!C0q:u)VeeeZi0uX$tƷRSSWj*+/NN+)Q`9# ϟëW/R66`g7΁LnPall E,^:_BBV8x{{ʕHJJBII x< `kkiӦCSS`g7 zYDB!̓_ݝ*y=_҂%0n8N B!B|<|qqqՎƲiii߅cذaPTTl! D^^?JJJ ۶m֭[='//(B!EZΝ;3Z?ӧK5oU6 ,/JJJ<9 iӾiq6l8 };gΜSjcxzT!6؜9aLu3,,,`llR]vѣL[II Sr`5عs̄3s8A!χ|7̱BɉxSI[ZIb*))@ \-*%%%VB(/_Qq3gOUUޱ\^󻛍 lll|B%--bkm=cD-Z\w'!:vM6cӦ(..о\6K B5wO.???`Μ9;w.t^Bil鐔Hc߾}|hkkAVVPQTT HHH@CCQ-@KK dff=TUUd yyȳjL o6x<%6?dggAQUU:7,åҪLKSeCKK Zw8"ݻq:=zB{UC!=z4FHB!yy$IGPP/%%%3g6<=kn >|ׯ1yd&CX !hLTy\ kJ"B&%%6m+F gggXXX;,B7ݻ3]v(T̙8y>+W.ƒٳ'fΜ)S+oׯ_Grr29 2ӦMWc{܏@dgg35447`,YW \v7nX$''>x۷/F”)Sʭ[7バ`0`ccGhӦM۷oq]{(L_~3QTTӧgrW\\Ǐʕ˸s޿<'%%={bذpp 55XPP3gNԩx)ݻ7&Mg~Mii)~a)}OСC0x`t <O>Ǐ|mnXh!yVZׯ?BKIIc^]99rf̰7su3D}ХK"<>v:Zq͙;+HKKP7ߌ z6mg}vv0m4ڴ| ;wBϞ`aa\ܿϞE>LSQQΝ? !B!BHb`` O6'G`` WWWhii} !;w Zpp)t\\rW\Ə?'sYYY5k&BC6#<< ѣ8WHǒ%Kp5y OmnMV}߶bv7c=zO?cD:ݻw`#k헛`c˖-ؿ $\O?KIIIطo|||S~+_cؿ~m++Y"ܼn v턻. <⩉/XVV)YprZ^ .zf˗՞ CXX鉶mk8}VRR┬[I<,33^^pV\UB!B!d-bIKKc޽j[j;wW^"#%%Ĥ--w]]]#ڵkFAZZѻwoݻ?I7?{8p¡CѤI2֭?<r*()5.!]-[B\\իRx< 2йsg B!B!_^h+555 <֭'_"77|>3޾}GXt)LLLDJ̄:w>? X׮]Ǜ71x&׮]GϞXclٌt,XIԲDž xܼycǎcsqqˍ'77&g%jIIIaŊ?qM{ XoSzŋFx.^FHH?MvډEKQQ3fCXÇ@OO韟y">>^y\\D:<"# oq05/,,bʳt#+Qaƌq&"#],uwXYY1y!,,ZNϟ?J3% 44NF=XcwWs"7c4&)kY}"22ؽ{/4^( gvE(QkĈpw@L{G 44'O6[B!B!ܮS8}nu8DHػw'sss}c{^,]Drr2^̠Үͱbş'ӲRU֥K+5nQ>|=z@VVj֭tttݻ2J'!iii,\/@PP^"55_|AFФItjjj"Y6 X'p{&HB!B!Rv튀<~iiiԩ4448CѼys_~Ǐqe8;;#++}K.8qF" ShjjĉSB v fΜ777O0lP@QQE_8I&رc;3י3s}61mEEE>}ݺ;w̙37=gTOzz:VZڶt2,]I&1llF#44^<6>+++HII .)) mnaÆfXp!z󅇇,,,p)2ˣw޽;̙ 7+a .ĝ;e.,) www0[ɓh׮=u3Enpvv QXX,X0>UaƌwӧϰVEyW˗g^? öm1q뵢k66@x̜9*[#b ֶŋ ưAPK!B!B!UGZҤ k]h6*Y}xz,`_=&'"Vz-/`O|:!,>KK?6w#D4k@p.ga!}/.U܄B!B!TqKFF077ǖ-[ꊃVXAӧO5jyfHHqSIT<˗W2UBCCWJ*n8r0߭[W?~ԩBZ lݻaYS3|>Ӷ#U*._ׯ?[,JlB\\+W³g銵kfU\\ΕypQyI 8SBl#LRRǏJ*i„ x SuwO[]`*dK- $?dܹ#.;;Gڵ8ϻp"Dp+zl;x ӦM:::044ijgϘm[ر'''A3URRR eeeׯ_Cff&6660.@ȱuV}۷oa-Kϼ0w ())a 8zH.ǃHqǬY+Lfüy0mTfٳUN(hbGNcW^Wݘd+W.tZ..gX UR&Y Ξuʕ8%JKKck۶AE¢±YV{QPPĉTSaB!B!BJ}1B!B!B!j`ii`l߾ 4(郏?Rt"0z ~͚iÇTG[[Վ+?hbNqcKTiid} 5s,Uimێ 뷁 <:uB||<OUȲ?`VTTD֭Eߺ&A'<< ߾}cڝ;@JJxSSS<|#0 S1CCgɾϟ?<6FFFjhhI&HHH!B!(==^^7!ZjpERR$%%1v8\۷d,U`hhKҥȱ~/^@XXl(*6BFPTlN:ܼ7E?˗/p'޽{ Yf4h0iii@`` "#_3cmmǔ(ի'aÆ1EDD>|OK.CAA֭[x1RRR BEE]tE~ԩpǧOOsg_gڝ:u.sx3mhk 닄dff@I11t0ݻ);>UPPPw ޾}>@VVCCF8;jׯHNNܿ>|l޼&&J_\\n½{>? CƂ},,̌Rҳg?ϒ\&!!.Ǔ'lcN1bH+J|}W()5V39/źjiٟy|>]w"11iihРV^uuu _ Wse7h`4lUURR|}ToӦ :t(>ρ!B!B!Rgp%l߾˗/G~~~ƍ}G!UѼ9pMRPgsr/11Z^^rrrCaaо*#!!iZ w"ܷx,.44*UXXo2mOe޷-눈WdeeWm+yU\sK+呙)fKtWWIZ@YY B~J:tyJHǼy?oذZ7̙>_xR֭{GFF Sشi3ڴiSa_~p|QUܮX&MTUU+ux<W^ :={ҥ8v(oԨѐ(1|' =l0deeaypw>+W͛^B%$$իWöm[ѧOlذ -[׬Y8'k$^_fVdkVgbr֢.x\]ϡk׮طo?ZWuZfbccݺO>ooolܸ6lTI6mȴ!++m۶bB$$$ 55Mh?YYYؼyNI.RRhܸ1RSSIDEIxS߿cE8wHsWCe5E]ݿ*Ϭ]Rqk>Y_Y*LX~TLv归_EDO!B59s<)gN`8uLυXÇ7cXliRRR0r:tJedd_x 1ϟ?(sg,z]vl5jÆصkg|…D&ӃT9aaa~F֭0id:ur%իW䔰9s/ybغu p).X0JbC..X…LE tޣFb%5He]_Uaa!nݻwq/J'&&6m SNP-N:Vf͚߽j_U9sWhٲ%6lMs21}fͪ0!&$%%)YB!@޽KKKDDD'88ƍõk8E!\W'(((0Z%W-UJVHY f'6mY3-((CAA 4cǎ){UmETFURs#u\s-#\:|pQUB!_pp0PXX m۶C6mHJJ`ܸ1ZZZ.7o4>BqU,22a8ܹst#FjjjPTlT#..-B+[BZzzz:t@]]A| -D.]*=ܹsD-m066F۶pM1cbb𡬄:yyy : GTT$pЕQFǷܼ$(++cq022n+z;w1j>۷8=<?CRR hڴ5jT̙ [@ZZB!B!_Rf ss2<<|tuukn;v-&NôiSύ^qN<+:]vѣDž8֡CGÇd-555O4ׄ }6ӧ9'k>}ZJJ [(Yկ_g *Gǥ)b>,027T>-,e˖պOB!B!BIMM„ 6cǎ9rd-GG!K^^YU811AYYYk_UQRQ||H_ُ%tܹq111"S؊}{`۶С#\)ҋmtիЧOo];0uR|lٲi+((erabҕcv 6s=zҥ!!Q)48p x-|M4)wܛ7oi4JJJ5+YEH7n\ŝ~aׯ0!B!B!-333~>hݺ5ڷ 6!jZjǏrrr eenՂؕtu[U)EMڿx\0HW/?U֭[#$$ Rrr2TUUϟE&..N%sEyOSSSô8 %DMx?!B!0BNZ|>UXz <==%(PPZH2mm]Μ9j;:.0CKK GKiܒN؋/FFFU]vxxzzbժ՜~0mjڝ~-ٳgY i;w^.úߠvv0a"v܁TA@dddpN]0idN}O0o޼YgZst߫E VR||<^ G۶*;da/v4}?`a޷_|Gѯecyܺud+W.3_+)5СC9Faɓǜ```y޽tuu@P%\?iԴ{Jj%kB<3fp0B).99m\o"B!ꕟWWW /_Dvvv}[nMZRtttp 0I(.447nڵkk?8BLE5j$$%%qV[8t 6m\a'r0``-bm=NNukaiyǬBCb5x"v۶miN z:ۯNNNm4Gm9nݺ x=<<<`mm]txߔ"LMMѸq!C@@k[~~>WYڴizq611i֭>~x4mڔy]CC 6do۷Ҙ5k6=5-((֭:::PVVf-P' +y2YNcիKKKoBBk@׮]9PVVFNWɁt oՍK2ӏXJ:wv}沮c---od-x㳰PRR 8% ::isUDSSrrrA(yN_-T'45Q(YBHC: B!Ǐ =IBUtf֭[+=zf*wܦMYI&&8= R;;{&Y v܉޽أGYc*ׯ#_nNNNVr f̘meINNƱcǘ<z{\pr:(~Wԯ_1UI&3S'agg--2iص?utݼy3\\ΖӹsiCOOOyj=vD߾}!+[O>۷j#*,,,V9ׅc/=#+Y bblݺG=annccnݺgjJ||]f֙3ak;Vb$5!B!pַo&f=p F c˴lن!CaDBGAAA]A~#x.\ Z^^&O'OP]Boiժ?~\_? bbbX`>.]lxXju2`@ٳg2ιqce1C?nnWg8w 6] &Mz𬲾|KKK; #""`k;мy* 6lDM.^زeڶm[d]#uf":tĐ!CgYYYׯ/N>cc###1f ޿lkӦ llF4۷0nX?~rrrB>|VdbbbXzW[߾`llǏBCC`e5硢"M/L:ÖB!߉&d 뜜̛7Js_3gѣq?f[vv6n@^^}X`ժ[EM@E^z/)MffМ(ٿ꒨E%JjZ&M8WJo֌[Q]]yߧʄRcV8OOxzz4Gq\dimqYT>|@&MXbbL 5XI͠d-B!B!|I~.y{߾}h!x<`hhyPWWo `;wbɒ%u!iiimիH>˜!C ԿO©S'mv턯/z}ӧO{Ѭ;K.8vڃ}0>{c044:S' ]1y8qYG022ѦM(**"<<AA/,stRecc 7+`mm+!022>3xxӧO ]RF__8aʔ 2߻}}}iӦ+4ٳg rv؅@$$$g>|,--a``m͛hLB_PKm$Qjjj <'󡨨Sp !B!իMRꐟ/P8]p+{]p333qe\r 6ĪUagg/RBLe("///xQ%!Ϭvi sGE]꒚Hc3;H]]U=K$kes'4id&Y+??..Xd)ϙ3gXj5FRs(YB!BHغu ::-1{:B!MVV<@N ++zgZ>5j{ֶ7mڄ3gꊬ6l؀/_)))۷"}}9:tiQVlٲpv>l{^|Q aٲΝsŘ1 11ǏUqaܼirrrؽ{/FA+#G7^v"ΞuE6mDoժx >۷oTn+++lP53hٲ%ΟѣG1&%%8yP)))89_mJ!BԫWYAEEK.Zn]kc`cc@ܼ%KKǫRL)yOÇDtԉI,"Ǐ"OOXbe}?~S1"Sp"/y%ϪaU{>xllladd$)*:v섎;1p]\$Օ鯫nL$VR(YB!BHps(@nE!$%%ajJeccsB ˖UaB0>۷Pxصk7LMM?3Չʢk$=z[7oײeK=z 9Y z’% ),֬Y)S"#jU졡9sO+_>}c.hjjVi <ľ}{pIտKS^=c011Cfpml߾ ǏC^^^f:4kL$%%ᅲv;vT"RRXd ͛_Vnuŋe'kh#GcNE!Bs5bq!))Yc󉉉믕ܻ̄w׮]Ejj*=?ݻ-x"U*>>:)UɄ,cÆ/0#&&|-Jw%/P++11UWu/SrW=ȒJ%/ϾXmeeeOi_ɓ1<㍾}?8q}]Hj%kB!BҲ?smvu !B k;w!#S+m۵kΝ F,ZƍaQѥKiI7\#T_[ՠ#GaȐ!xALL 4qceO>>W+ nжm;x{pw@FJVիR30df|E1FFF ** >$BQQ**ѣ99 gf2ݢo:  ;>HJ ()5FV`m=;wt~q w?WVVR??{ԚI-[̼NV.PW\_/7=zHOOG Ѹqco߾h޼XMA1mYYYx|V^,4u)&Y̙SL?))iEP߿GBB 9rTGG󖵪ڀZB!ͦM:B!B'7 ~cǎa޼yu!ϟ?cÆ سgO]R#tuu+8MMJUXRRRiw`ffV%W:qqq zo:LRW<ְ㐓+4CkiZjVZUz|Æ 1`@ 0ʱT_}/p4|~bڽzUzުrrϩ_ZZ*.^ȴСOϞ?lχNbccqe-!!nݺq [NNXUK[ILL }]vrmkҤ uZZ'屷|g9su4Pe-RmRann&R5!LN)))(((qDB!B! O^j89sSLYSNbڵ8uTU:vGVX]A/2?bfzWҫWN !B!uK__}}5kVauS\\󵘘k|ɓիL{ǎٳgUر؊LhwXYYAB쿷{7;e|_@aa!?K׾}{t~3cY&LXy"00[7r:t999Lv m8юu /vJvځ߿3m+++(++swϞ=01)?+%%HQK;w<\p9ݻwjڴi9ڠ|ܜJ-[D=qٳ.QUV066ZCwJIYn+QKYY:u*Eܨ4E~x=.]`!Bȯ),, o£GtAEEFFFk$$$=?---aÆCLCfwArrСc7A'@&033+q,\/QJJ2\]ϕ9AѰaCNVϟ?Ν;H|| ***hD͚5%LL1pcx# )nLѿnݺq?y{ !!HII444`ll[1PSSKLLĭ[7w#yPQQF۶зo_<Z#B%Klٲ !۷oGDDK,A۶m8ꕕ%FZDG5Kucg7.e&89`U_3k$XYY1׾~B!Ѐ=9"ZTT.][[: 1rH/JĄB!B!վعs' xxcƌСtuuѴi3###aaa  IDAT ^|ظqо} …hժF5ӂ6deegʕ8}ә5R[LBPzz:Yfԩ4QGhh(^'Bh۶N>UիWNN0d`޽a000@֭k|8/^{cǎ@\z/^`[/VOu۶m?~w1߿_.]cNHNN“'Op/Yڵ<_{cagg###"<< O>ۑʌ²e+8g~$&&bʔI8}F ]VՅ3f,v؎o߾O\vmZyʔ&8HNNfm };d-R-Ù kXd%jB!R/6m*r\nݻ0.Wѣaf]`LÑ#ШQJ/..Ǐe*U<}d۷oa${My qe;l $$$0qmnm=V GZZ|`PWeEB!Bƍ`g7u3E =::-Ѽ6RSS+W.#<<رcp WSSʄO6~_PP337o`ٲUz***-[63nBg.s\ј Pca;w~hkk!99߿GTT秕߀L8UJUUCX͛@XXʕ+Lއ`mm{@YYb葬ddd0aD}?gtbbMU`XDŽ ѺukD^^.BCCq|||+CCCiNB!ТE 3BC lڴ L{ŊÈ!B!B(Ǝcݺ{e},++ %%%Rǖٳ`֬#)ZZZx۷oéS')GI1ƏKAZػ(6?XXPCLTT#v`&uVV, ,TTL؂ )J#¬,b `V}}}, ĩ]6֮]{{{NW^/^]jqcҥL6:: p~f`g_UN:ĉ4i" NԜ0eT̛7,רQc\~f>+PWWܹ0fXp&O73EFcn7r[SSOnlݺIuE^g޼W֮] ^NNñq&sPH~OEd4 ?!B!iӦ2:...Xu;uI&aeصk ##&Mիׅ ;;O݅?}i8tP1++!INIMan涛 /53UE+((XZشi Xulrxz^f Ʊ7n'OA__fii '''1 C f.0cʔIpw?#v[lf‹'NdeLSڷoqcذ!ŋ8*ej+V6;&8ѣ7o|2 6mV6B!ΝǏi}v "DJ:t:07ܽ{^^^ܹs%όB!B!ٳa͛7ngõkWqBCC01N:]PPP_=ѽ{y>>#DDD .3--mTV իWGN0p@g$~*U`E9x!Ð]]]ԨQVV֐gƼ.Y%%|| !''A˖-ѹsȚ/ff?>,BCC7Bǎ0p hh|8sTDZcq-8qO>EBB򠦦Mo~0`Yx<&MQFӧCVVx<tuu7M6E=Q^Rdgz<B߾D#..pYmm-())!-- P~}XYY}ϏTҊ Fdd$>bXZZDl< ** 11ΆW7B ++[9#00C˄!j֬5j'!>>044Y#bHLL@TT55Uhkܼt?҇D^z.B!իW1+L:Ml}>_˗Dll,<<<Ծ[fѢ}gϞ<=/c߾;vSX~S{gtf7oT,|I}B!B~i%S.((@NN%KFFff`f'~XPPJ * GvJʬդy/퇾}1۾|"gr1qDw >/ZT-Z~]܏i\0m ҏSYllZƦֺuC_~ϗ?222ر:v@T`!̶lxrxgaa (hff33rs&P Qީk ;oHJJbl*FSS o{3'OcDgzt زe3݅N: /am1^^^y = +nݺc)hР׷¦MqQ[ZZbI<OFF5peǔ{Xj%`!9swE6}[<("//++kL0]v+۰>@LL ѣGjVIIIغu .^u4hѣiԂ!BŹsm'OvqGokg|_hиqcqLYYY<28t >yo'- ·cZ[} XpZ@܇089 :_KIIa[^^=;;[y%nIgΜFͰ}6Z[,mۦzB!23Q9dchh;Lɓ'766Ɩ-߂rr1zhy<}3fM{ogg5j`ر722L={v)wL\q7odLʩ^zW^L@KKS[Z+::>KB!ҴlRN?x6VXZt^'B!B!W͛71eyyy888T!*???1e'PSSFZBGG)[|߷!+fC͚/!\,mm4mjnݺ zzzLNXhv齆~CoDFFM{ ӧݱxbN}W c.{q&L>`HII~~>Y۔Q~}t m۶IM˳\~ Ç *())1L|>_y%\ubDgή5D}vLB!D>|*gR*M111HH@^zaqLP۷ƽ{͡TJ^zKeP{S~"Pxܐ_t ڷzꉝ@ ԩSXjiicn7۳>}'{D_n F?~_%%%fۗ/_qlڴ>}ի$ܼ)={AKKKhVV݃۷!99p=gBgpŌf,ř3$ۅ k,yCSSU7(('OB`3&L˗ #Bԋ/***sǛWt P  ((_ 5)):l2<} )={wJAHr%jodd$_`lVbCCY"U/_ʟ>? @aF???X[[sCCZZS^`@"X_x{{3ܹ!"5XPϩ YxPh"үDKW"%qˢlƄB!AQQ;vĥKyzzY7o|Xz5ƍUUJ!B =zteOB!xC}vK.cm37oxٞW!C?sNo>Zp#,ժUÑ#GVS!?k<\]gU8]vcRRRԩSGާOcNe#Gq6wwwVyʔM2U`ѭ[WL 0cIjiicIXf5͛7TB fիcfZepvxh̙3+~UTa/wUVe$jĮ_ '%qτ+|Xի@^^^k֬w%B^z z BCCQVٟ !hkkc֬YXx1 11֭+yfB!g#++ʞ!B~byy jBێ=L<|Gbb"jjj000ڶm --mO 3FLYCCC*!<<\*}[)TT Uhjj~w%!ܘ0QYY FF5`ii#FBAAwAZaggWj*iiȌU z'2Xٳg1^iZ~5kCڵl7˗/*jj҆>  q^xDloVl4Q,-=`-B!HG@VVBܼy޽˔edd ǏGmЯ_ Ce+MXX8Zߒ+KzSdd$\r%˒ϵlccFK4!BϤgϞϽ]|...0+B~_ؾ};H͛1uTJB!>ШQ#4k ͛7Gf͠ssʞ!B~bR^@%%%t;wxz*R=GTTTXSs888[jueO(X4Mׯϟ`$' ==](Minn"o߰&&#j!??siC\\SWDMMM+XK O۷t|-@D㝜,Yf?~*[[HԾ~}vO>{NB!@Ĉ@ff&BBB$&'O0C@OOnn{0h@fu3]aaaڵkW<*[ d>Ig%###V9**RLMJkըjժFbbe:f<{HZBEx> "D1|L>+V`YԎB!TCCCI&PTTd YB!WаʞUD!T "?:ruȑȐ}ZZIII$a IDAT䠧hSqrj$j/i}@SNbݺ$ؗW>gl,Z +ߒK#B=zȔ*,X+)) ǏeٵbƌT3331z(xyݔzm>[yyԬXvvv7Ν'QUQZh*pj ?o+**17ooooAfQQQ??zJZcӦL9 O„Bv%U CӦMEcVȟW^"|||eeJ!'bӦM/\ <+" @--mϺRn]UNN6ÿсljEzz:ǓEZ9>\|!55@a:uTؼ>}lז~;!H2ZlYvEB!B!7'[ m>}ms ٯ.'isŘ10ur[vm'OϏSۃNkժn݆y77E߇s\+++2ewwwyB[ll,?.KVl?K2圜,^gD$X1?[lq֯_qĚ2΢E 1:th_!c5kLwأGR^۪ukp'fcGWXB!B!PixrUDf8@JJ Ҥ*WT֕+mvNZ)))?hiiϟ?`-B!45kի`֬@ pvƍnݺ02<$''իWǵk @&F7n YMFF;wȹ4n˖-Ü90f\z |>uȐظq~ 7o..\@ 3f8i8xڷo ѥKg;͛@Ff066ϟAAA8}0``-_cƌb={?bh޼9LLLK<}ꋝ;w 66 2 ڵL7oޠKN9sZh "::~8t>,|e˖x)㣡#GgL;wuk[=VV֨[.jժTٳgu >D^^ i}*2O Zj :t0999عsSGFFFc/b0 YJlݺ FFFLm7]ʩmY͛;vD*%n޼ / *vڥ;id|СJJJpttػwfvp]NcTGǾǔw؎{n3ԨAޔ$&&@KK6pN100ijgf )))/;w%Kc O!R,,,p%ڵx>01.]| ENNfff"3CU3F03kd CAA|3RQTTf++koycذa6l!++8dLLje: !BHESdddD.RDZb޽joaaQ U$@x@[[GIIĪB,呝@[[K}K*55|]CR  T8E222mmm())UX?)PQQ i w PVV,\|jjj*((@\\iK8 99 Є&B!B!"QeOnj}|ĉ=o|+1LMM1m 6lX{** Slݺց޼y#7QժUxR̘y!XvF)T̙pq ^$++kx<&իzFC!##>/r5MXa߾?~,X@[[[EׯycŊ o`=ݺuʕPF m_x˗/c_Ο4_B!ix\3IBFFB~xE!_IժUQzuYcѵkJ!gv(nu6ׯ_~ 'NNj/TTT`kkWיꯠׯ_þ},oKKK1 ̙FLL kڷCy{솧' hkW^9rTo ͛^{>~ CLL ~ еk7 4X*TѣGq-&8PР'Zj,III8v(n޼ ?ˆ|ѣG C̜銜ܹsS^:̙[j'OرLȺIII ///}QQQHKKcx<4nmڴȑJWX>~ ņ }ӧ{п۳s f[츋/Bbb"nݺ|̹6mkk2(a8vnݺׯ_1s n}_EB!B!R5~8ܿV:nݺ]3~]vFxx8UVؿ`%ψ?k_رp)ztڵi}իWx< W_~LyٳAhxx| 8"x<ܹ ;vlcUچ 7k ftŦMѼysXUUSNʕ+vژ2e*l 0XnҤرc;Zhjժ!44/^<ǻw.Wי N/l޼ _pt#n@3M>aaq1f۵kWqLLLP)ԩ"4= DbbS~J!B!BԫWOdk?Ǐq;oZ>|Q#D.Зׯk3g.+P"99#GÇ8gyNHǴiuCl>|Gc__²O[7n`pa̝;OO!0v؎ɓ`.YT)ͅ]+o֬YZ/_b-je }6&N$ 8p`??#GC̾ׯ_c=ⲳqnݺ9sbYbTRNN6VX}1%+z 6oތ={ԔB!BHiRSS" {)!ǡ`-˒Î;ЛHAAn߾۷#T-[Yf{ԬY ۷o{a g^ش²eKY^2228pF7푑"/`HM?c~ *i8g<=/sCVVmĉܮ͛aC3,YYe1//!!! )=/]B!B!B*_zD^/`-B111޽k7q֬Y eee8;ODBBFs

|/Mv]d0Z#Fy>Eƍ2d(ݱcGY㉒'a@->sڵ yӦ< SBZ U}۷Ax5?###MC 8u!$$.]F^zyyy:u؅sOl݇֕&** 6mڔ:Νmn DFF#22o޼˞ppp`S>{,jI&x}?d)Y<>y< ][n#22/_Ƒ#Gall !33UT yaBAS .`vaÆp"x{Axx>͛wpw?Z555FB!B!DTlݺ [nMv퐐PZZZؼy+fϞw`|ZZZՅ}[2M{\;wΝWjD3ڵ|x%##ѣ`wUUUu6 2pt+Q@0xy߿GXGTZ::hF<|X!'''pr.q"Xz V\/_ŋHLLDJJ a``VCڵB!B!B~k( !Sz8p,--Ymm`mmgOKhhh`׮ر]fЬY3`ÆXGQsÇU+[V=kkX[B޽{0~?( ͞fK}8&psۃ^zΨ$//01>}|2l۶@a&={vcٲ{ cǎ8pVSSSCaggɓ'<ӧT-]|ǎc5kU+[jeGu:rr2 n gf͚AII Ax,###DDD(1 zw^D p!}i:tܹ𾹧e 8H|6lXʎfc nn{`hhȪ:˖-Ν;Xd+B!B!%je$Z IDATҮB~uE~044D+{*sǛUC+ب VVVҿ4ܼ)͛VT!BH1/^ֶC j׳g/l߂o߆ѣ@N|O|Ͻ|C۶m9#@ U߂~@/N< |{bENN66pMgɺw.z̿1s ۷HHP^***hTYZ߿ WיbOÐ!CP~RY[[؄)|JlÇ!++)[@"Xj5S˗lG!B!BȏBZ!??111`do5=B!B~ rrrPUUꏸ, B!!. kB0` z5jʎ}9"=|U1ÕӼdeeV.ٗYP:t(3pDȔk5b_|:Ʃn^:Syq_.4W:::h[O{>\۷aEe*Isu6 ϟr6mꔇԩÔ_~.}SPPk[8obb{%ݸWPP@]8-[2S1=!B!B!\,!hڴ :u#FmvgÇ1gl0|>ΝL!B/y iB! V9)) aaa3B~8; '/+@<<|WZ59/ѮvrZZz~5ֶU}IsvPKffll ˒W^"88IIIHOOGnn.Nvv6ȹ S‚ BILL@xx8h)Ç055i}&H+$$baC3hii!18èQYugβ/զ=l w\ݻx-bbb/_@ eff2'eee蔝 (Ŕg+Lsr-RVVrf%[ojjA~B!Βq5ܬYsԫWW\~׮]ED'B^^C ZȥwM/<|?FrrV ]4o]tE˖-%/_p;^~DD|Bff&444 MXXX]hРCJyW\AXXRSS5jG޽]& ߾w 4X4Cttw)E*ӧݻw8w,?d̙sPWWߋ_'CJJ TUU-йsXXXp~ߞTv8`e5O\\n߾Ŕmll`bRx-99Νŝ;whiiA>h߾}/#Nߝ2z-[7PUUE=}o޼S'KDEEAFFzzzA߾$S$66gϞ7bb CCC~Kݻwp-} Qڶm=z ܼ)92S#|Z:(Xq5\~ <QJJJ p [l941B!B!Rx]Ã˗/Y[!կM4֬-4\X"EGG3Ԡĩ044 RΐSZ5WVM`-{عs'޽#~\iR9-)j###;XK  44)xr:u {+_q߽{˴/((@ZZ؇EFF[ƅ < w.( |rr[;KGGSlڴ.x ))I"Q_׿m[*US - )S&=EmaiGB!牊ԩSʕP^=ܺu 'Od(x>~ ҥKYزe3lmz4lذ9~۷o޽{D~7+U<000[B}+ڕ+3 0; .@WWwM{9se?rr߹s;n߾ {m>}|YGFF"''GhǏX`++q(}úukѡC\IZŋ#<< жm[ZqqYe+~gbݱc'LLj#;w~e_@`3ùNgtvA"rP!Gdd$R(ظqudeeImNJJJܹ#B!B!BJU摟$i@mmmfTKl_2`,իWdzg8˃ ;)TڴHv"[Y"DKYŒ%73}-DYSGiNG|߯yܥs>|HcT$&Daa!NNNѣG< X}AVqVZZ3YzMYY mk,'k|ܼysg1:u 2YI߻RSS8Jf^z%FR.W'B!5kP5aI._ U9ƍӧK.Trrw@{sKLLcr/==={79y܆ --S˗/]'9PaՕ*ڲeK~:n߾޽{ N ?F~.x~|>vϟeVNsϞ?}Rݸq]:˜-)//3?Ʃׯ_fjܽ{nЦM̛7 ,L8AAg&;>ϒ-Zb2!ՏVZ8t0^W ::Zd?ƍmѯ_?L0Q߯̌!B!B!B~0yyy44dOdJZ,+&gJ255E^affMM hjjB]]<˶®,^綬IFLLd0&YdUvYdP=J)%NHH<='3j׮ '''hЄ&VpΉrT/_cDŽoe߿-Z# 6!Edd$QTTtMиqcPTTb|2FZnnBz'Op)<~ @Ȉ\xmJĠAhii#99 񈋋Ǎq%Z/^JjԨ ;022BddݻѶm2s)LE=ֶ G\ 99?& ~i+NCC D֭Ѩ _޽{8u*I3 ˗}Kpy.;;;XYYٳw>TdKII!pMT>&Md:t舎;qc[ԪO̙3x۵G_~pv%q %j999{w'ف޽{ Apy$%%a((***رc޾=,-@GG/^@dd$cbo@^^޸v.^{.-[ Iw___())OHMFZH܋aff&RRR(**BSSf֮h !B!B!BȏVZgQǏVRW#4weEߟikii K;16qܖkoo(|`ddT\YXԃ)PeXX(N0#;˗/\v EEEx -O|eBV̅Vb(rrrΎT:u?O<<<=kJHkEE!pT޽vvv'&&bo&QK^^23g΂ `شi# OI|9Y4$???汹Μ bmbb-Z^W,bmsuu֭QV-f  www1?~ĕ+Wt̤$?0y,XEEEVooo1χ$V `%`j nn< nnCm Q'NŋSΜ9ıcܜgii}aH :,Z֭e11oeV`ȑNN=?{2۷l"1YիWXz5kۜ9bΜ_Yۚ7o'`ÆXt ߿/S;v`%jYZZbݺر_-QFcҤ Lrٳ# (܅xغutq=ԩ3wꛕqƲzX~#-d:Bj J"DFjjjPSSc +9sTB6!B!B!B1hhh0  ϟ?˗/ʣt5x'$pohժq1112S؊>wMM2[UUJJ܈mL[[:$<3d-[B]]_|Ajj*if/ss =;wi{{{cƌb.{T &$$|W{B!Bj]]]\ --^ IL{,1o|$&&ȑ\t=zd`}6eD-Qttt0aD*ѣGX _}"Aٳо};؋-Ɣ)>0m\p::uEDpp0v$DE=ӧ0xr\p\ 4С=cY011Pl^?bX|.\f+ħO/r̺ukE0{g.]9洴4XiשSub4lG}[ruk1dPhkkcn_(**BC```9s^~ʹGp>BjJ"1C B!B!B!kkkܺu $rǮ4eee]XJ5YknsI"]'}`m]aÆx8jujG&Y+;;߃J-ddd455Ѭ<:tȬ k놸}VyW ϟHR}LMMQV-dgg*< 5aEC!~y:D& ڴiY|8c8{o|,(P~d/_i[X4wU;p`?3[lV1sss :,q1q"}SNULG.M?f۞=k\ĉ&jر~:ڇįO⸚gOgZ0id̟?[oFFΜ9ʹ=Ej&yb-HKK>VeUVKL*fhh3gaɒׯ_ԩڵoOIIƤIx ?'bb΁!B!B!%''Ν_ԫW{ Cȏ۷Dۻw/jժUBD~:"##YۊPP XM;VR`ILicXm;;rҤI˃6gϞżyE[Zhh(ʗ$IIcƜ ʹoiG"66RƍL~Zֹsg&Y ^77rYU:wf'UAYYiwY{I E͑[bИXƍWBCC9ljjjvvv6){{{\rsM4!B`ر;w999L{14VWW]vӂ;wndddsut֍U%55O:s"WSD5KϞ=efmV7 MOO>ֶ^z+8::27TyϟC>o޺u>Ŵ(H#1Ù3g84>;cŊR˺Yw>e>n`۶L{ѢpvUcnDOO 6ċ/UJ&cQ# BǏaii)qL~~>&TjLrr.9u3g8%B!_׸-TTTw yt $k">>Uyual޼ \RJWС籖eUȪu6=z<ݽ{w ٙVTT{I+Av8_E˖-pmgO[wV]74d'7fffW=9=9%kLmvR͕d`` zJxؾ}k~k׮CYV^+++qRSQ!B!B!B~H_]KK!ߍ:a{nxyyIbrdee1;ֶ܌=Iu֡[o #McF/#>>&&&bb9/ΡC zb| {a<G.[n G >%&&öme#;;[jSesppdޣ߇ܷ%kؠn]]$''!;;17n Y֥K&km߾ oވ^y>* ח W'|5#Yl"HGLL 5jT U hkks[.zG*%~Ֆ=bUÑ#K|Y|,*::ukn 8|>upwT'DJ"B!B!BիW"7hР#!7o>FQRSSv_~Ǐcc޼ IDATK}`ggw=zW׾8|$15k3y'>"۷1;v,Ƴ+qnZh>))̶#=P^r-[^z2IvǎݻXjJ'Į];j՚*_9w( MRRΝ׺drc6m <Ah%h׮Zh)7//ͅ}ʚukѤ*aKII  Fy0… />VׯqXEXu !T ˿c۴i]ViBH01ឬ<ŴiSu~Ê˙ms/X[[ >~Gu+yM !G˕TVMѣBU8 !G#jAqJ-[Bmrrr?G`E"Pedd8yԩyc1B+F1 ڿ, iLӗ8_u200M>RsEEEc䠫˪X%u!IEڵ+2$B%kB!B!B!k)Y=U?l{!=z(q3X5jÇ`0w޿_="+)))c>i~UUUlذ $ $9JgNWHKK x߽{wޱׯ:«2f@_sF^^.A;Q|"䦚$mlu&c?~?P!PQQaaΜ_5_Æ sw77wܿqeDE=Fa!;Ϙ=AAA8~/˗+&iJ'}-[ WfGZKE][NMMi|ZZjjNKjjN%K PB<**ʬC`oo=R4449{~Mz-ѳs-:EHud-B!B!B!!G"//7cǎXd1$KܫR0m4DFFנA޽M6㜬|$oXX(6mڈ3fVZ<2QIռpD*P 1KXx<,;_^\~$%)((O`hhilJJ $:n]3V@eDzx(**BAA&NWÄ>|(YB!B!B!?T7S!D3fbĈ7H3dPjFFƜƵoOa669.PU4:Ssׯo[%ɩ?~9ӡSZj=zy]]],^>>spuܸqqqqHIIAAA> ah(נAtҵW]2mEJbLѩ7cq Ctt4޼y EE%qA077g,\ӧ V(WQ:ܹ @P%NSaQWWs0{Ǐ#rsݴT,iƍddn_di}E_JM0{fşQi4h;vb% Exx8>~T1y#ڢcǎB@PP 00!lrssFZ/333DGGS4kּʎ>}\Ч ,X7`͚7`2]UFX%),,d-33u YYEfjRapxUL$mѡJ ǃY*}񜓵޿|>ÇWs˗/q㡪͛7WNCWz,T6jE*EAAqqq  >])/PRRF^^. 0$,X uQ BVVVVeVc*.߸q|>_b7 ,,,ήȾ%+x}s|Ks}BYY󝙙Ǐ#EJ~D͛?7aggi78kР'Ǒ(8r[V&MtR֭pܽ{|۷oWAHUI`aa֭[a0c/YXtIuH!B!B!?"COO!B!%kF^^***PWW6abbz^'BSV{ݺȨhY턄ʭ\>}UNLLđ#G8ݸqCeŢƪgΜ0ӧO͛oĭ/oJ䩩)HII:NN ())ɜLѱK=С#+Qo׮˓:cӳgOqVVVZ%[EGGcl _߽PRRv&xaBHUZ$''k.2eYGrr *** ##堣S#"B!B!T'q|!B!x<^u@I^^PTTXe[ǔ}EEE BNN=pEǏ`|d|Z044DΝ nxg}\ի=@MM} 8$&&BKK0` d:>} q!%%<tac=zS !RUjРAGB!B!ߏbe-99.I?J#"0 ?x x<9̝;:::RƾŎ;`k#Gdǭ[%T1Kϟ#0$ӶdUԩ,:tD߾Aߣgۏ;bضm+vڬDOv?P't= Ou={Nd1b"ԯw>4v HiطoLDHMAw k=#8UUjB!ׯXh!Z/::ظq<3fuoLu6oߊK.Z5Y+44Ó'QBI.]ŋa?\ ܹ0m;mۂd-ye]$u-#`*h\oX7nc„#||<<™3gzرc'K=ΣGuy'66᧟aŊА:dd<ǎgЅX\={vci;wkhh('ӧOBq$==g/j׮-5bXv |qQ۷1sCXd {qB!S :Q!B!%Ytr^鿘F!俧ifXnNl?d4oVVV055Gzz:l۶}ի'&MD֭ѰaCxoƞ=+f<֭$޿maQzƹs<={̙`oo-'Op ܺի7Ο#M۶mq\z..=vvmQ^=DGG޽{ؼy#\԰j2GѶm[3Bս{ѽ{n<GHHmۊB#;;rJ˗/t6eee / '{aqe>pt~i bc2߽{w m_}Xn-MII w ~vmvL:ϫ,^~tY1VXHOO HZbX`aF}}}ӧLҠ?tɲeYۙK~fg^L=III9/_f_~ ׯ_cڲ|x63gNg***݇ZjIE ڵ 09o|cPPP555 8ں!>~Ǐorrr999 rvо}HJJƻwqN d.Ƚz 'NS!//<.^U\\iӦ000ijgp̊]mV |>||fCSS DӦMaaQ11ozAAA̸WAxxOBBzż4jdK(,#** W^Epy .!  !R^ʬ:ZZҿ&B~>}BӦ:Cc BH񠮮044Dݺu9"BFMBGiӦXh!^*_\"Fڵ 322RRRDo)*9_5kVo222ȑ#1gίPVVF||O]14h0 0{^|'qXj 9obbӧ`0w^b߉'aѢEL5ڹstub^_fD-lٲS2׿we˖2GSSG`Νشi`ii}aLX'5j$5 zXn&N\?~}aM`u&Mܹ`]ŋ|>ڷ;vVrttĘ1cgn2۷m*1Ys+Q˗־}L8 ǎ`ΜBB!\tIv3334h@B!B!B/zz3wmؕyM᯿N8w\7o ==PWW! aaQ=z@׮.`}' 𛈋Cb'::uall nnnPR]kժy_7n 6-ӡ333kgEEEL[汮d!Cu6UU2رc']ӧpܾ} ˃"tuuѶm[錁b˅mܹs= IDAT?Ǒ̄ ѣGy|TRRƪU1dP:䏫W2磪  0GNZe#G[mmͫ(v쥎QVVƺu1lp} s?ׯ?мys 6ŋpvv3nݺ 8[2'OЮ];sR(YT|@BB>|x\hii666RѣGHHHׯY044BznIqqqȀsss())UyLHHH HLLttѤIShjjVy<HIIFnn.tuuaaQn BH\.=zWII vF~~>;HdRTTbCŎՅ!t؁λw/~eL*CĎСC}yhiiUIܥ3b+Yb2dHfmm}е#> 'S899o}Xh1N dVpv-9~`)&L:{  ԩ@V.]wĿuuC^^>Oܹ͗rքB ⒵w^őB!GYY{9[hQB!BFOOQZjVZ1BAEEEx<lmֶ XsWkb$JJJڵ>Z͚5ؿ塠AFZ*d~xyy1Ynn.D~7U!A;;;VWNNNsAnȑe:rǒ_ 5ignnpssgB^^A})5m M6+s lRQ),tTV/kk}V8&3+VWƍط9M ,dmy . yPX1 SL Se|ׯ"K;j ^^8'ɞ} gT͵999?m۶2狢N:{:ԍUrŊ8v,Xe^/\-zM(<;;׮… trrrrhҤ)ƍCJL$;wozM$Ʒq&88/]v" (?.) o)TRB!ݺhݪU+Zx<fϞS㒵.\f˔)S%&jSRRŠ+@{_/E>W^1i~ULII˗/^8qR*ɲe˥^su폅  !! cƌŦMnB^^!XK$QRRˆ#az M*6z&Y ""Mڲe XKK [l(Ɉ#><|p?ΝWa B!8B!hkk`uA!Ri$]/^BWnUQTtҌUh6ֽmcG5Ib^7o -M\_UM; BQ)B @egK+bcbԨQx$S1N}322p; ۛ8x܆M07:N8/^Ć Ȝ|~>|ww7$K훕>-ZVITZfK||ж-!%%Ej_b|bɞ aժ?кuk !BN8[[[t !!!;W'OOSShkk3>ܺ^mZ'N`e9=ɔ}V$kj :uO^^66u߾AaaaT .VrU\<}-Z4[bJjܸ1]^}8;:֖~~^ǑLR_ 2IJIIAtt49'BH(jIݺuH!B!B!lBC%׺$U" @DA5k ?B*ҥEj&J"e"'']]]|K*Xݺu%ΙÇË/m033GvW$&& U.LLL`hh}j)?.xg?~yn M8c/QzpI%`oߞ5jjGFFP!REnoҤ $lB!B!B!? *U>2Kl@ЄB@ZTUUٷÇ1OK,3-ʪU+C9666PQ$$%%  pvQԩӿطXf  `>-Zݺux\QQMJѩ;vё;xyyؽ{455e:2<&q <c 4Xd<ڵ;@L̛6l$Կo~۷`Ĉ>CTSb{8R(QWXl-X}?}Y8wJ]z^wB!|ޔSKWFHeJ#"x\o"ֶoI>+VD|ikii|%R&Rիǹo5rrr&k%$$С8s4EVGYZZrꧣþmiYӸ deeNKHH`o㢢"!R5^|w^B!B!By zh .R ,0 !|(Y>|֬Y#G K.Ĺ-ym3fݻz8n߾ʹpfffB}xSx#0L7%VoEAAAu1kNbFWWWtطcǎay[Wb[E4i"+Qk1b?+W`͚7pnذ . !c(;W5)Y+=](22ӦM-|_|Aaa u?FQQ WJ~D+ef\UWVXswWF}bE.W߆BZ<OugB!B!|$}'m!>B!D f ( ]BtEjwww0_<<C+p>BH3}*8zfk4h@߻wp}mkۄSS:u0fXl5۾jgWy uѩd+-Y[R^ڵr埜)((~ǘ1nu+\$9B!ԪU YYYTƦֿ2;v5|Ɨ2ݩSg 0\s(Z=ĦM2.\W;eN֪)TTjڋ/Eڵy B! &&F>*B!B!BʮqcݽZ|>B47Ko=,$&^GRm$!//)EE Ԯm=N00eAji´5ӂQ zOڵsZaӑjҰauG@hEjN:IZUQ4e_x.2YÇ,_5ZTlرcԛ)k&Ml xY#33wevvvPUU''!!HKKǗ/_.SjٳE=αA^^|>ҭ[79sm`qpu-Z@NNc* ųg}233U8汸שܹz텖P?{ a^"H8 QW՟uԽq]Gպ{oVhEg@E2e@Ay<͹y9% ܼlٲCBB 444DDD- iEGG#==*Sܥ% 2xcbb---dz<`cc#o@@mTRx-׷hKtᾝ] (Kbi)7oބ:v}}bΈJ2 AAJ: ]\`DDEZٰ,PD,_ {vR"r(P|ycJ/^˗/#>> cÆ000@?~hӦ-5/by}?O*scv*F**w@DDD߮5k: ++ ^^ѸqQ}]&z]]U"##E>GEMM x"xB*dP Qo:p%ٟammt_LDEE 5k55jX}- :!"""'N,LLRTf##C<111x}emjj Bŋ)$U|w@awn===pF mllkwy""""""""""J~w,,,f͚bӦ*,_jrWRL|H3g0G˖-dԼyScnIS`2g%}XE DT|FFzϟ njMЯ_?ҥ˸{Ϟ 6p-j|uuuE8)JU֭[mۡTRr?u>N:UdիW mUUU8:6EqYxyO%\uԩrz/-M033+аs&""/WVQLy~~~۴i#222DSJ{FZZj}"##n/bOfua8~b? """~:sO3W>ٯwxٳhd9rc^YQ^,XsӋ9"""""""*.*** YїZ:pW"gqբE={yDDDH$7nЖJ>r l۶UyвOB;::K,QxQ={vWjmmm_'NTa+>͛Xq:** K*>gHIpMM؄ i&%:#z/]~PP`| 8pZqV,B BݺuUZX~f͚YT);/_>VfMk׮3"""""""""""""oZI'@TP'N uttpA;H}ٳ`Q-[V:u~ZTlq(UZl-[⠛qY,37o*Թ#:Cմi᧟~ʣA<2mѳ%""oא!Cqܹs@vR1eT888fO-?~ nnٟ{ׯTqȑ#qVh_>@~PJv!΃p-رiii([,LMp9Ν;8t(˱III3g6\]]4?ll~9RRR ___ܼy}3gqp9nZ<|}G*U JCx{߄3`aaS1t0ܹsG<~.C~5rHpb5kʴo߾%voWR 0h`tZ@+懅|6׃dڎ DKIIATTTHZeھ>E>'}TUUw :v '%%a޼?dsGebziܸ z]c.\ kjjbϞ}XlRfZhkkc׮±'O`ڴB[UUU(QVӦMѳg/5ܹsѲe | V _wvg;Q[~}?Wvmt޽3""",^#.l[R) XigB *w'==nnE֮m---w^Nj~ZQq%M__9#FFFsaLsusϯ|X;#2eShڴirjիWgΜ#֪Uѳg|*UsWzuuu,_λPR%1 1h\fZQs +cN_,RteʔAannYfa̘PWWXBpa}]kԭ[/^F>}jI$4j;w)Y?>7BU@Z߾}1p 87)t\lJ-*/o)D ݻ(GGG]tkNo޼0e<ťKD97i*UJn\ٲ.&&Vdځ(SL}dbsp?99)))2v2؟ IDATy۷/.\HKKq)CDDDC RR>|a3NIqy=DBBTTTJ*u6hذ^ 11;2 Jn8s>x%o+⧟ZifHIф7Ph)@gV\W_^^^}Cll,ߝ>ajj РATlq^ mCݺc.{~ M6Σnݱ (](_6lׯ۸?yVVѠAVy#.}nԴ#(?6jaj#Gg Err2`ddze˖V2ijW[jLlj󈏷駯/~"E~Ѻ\\r> Dbb"222SjՆ#G˄/DVVpѦAѱ""""""""""""""b-bURE(ŃQZu>˔cԨ18|jˠGȍ}6)00ҥ }Dڵq1DGG,T"vqqAÆgdd`鈎wlEիWкuQ}`Æ.`ׯUVbҤɢ R)^&MrADDDNN:yݻ[6x:CCuu`Mnٷ+?~[^wだ r<^ξw[(;Ww>>8|Xq .QBEݔ[탔 <<_ͭʻ2(;GNs^\GӁ\+D.wDԭ 2o6l(~G?:BXrr2VX'Nj֬"Sq!Ic̙FʕahhW^9 kkk722d0nܸJ=Wuփ3:u jjп?%q jV788O Q<<y|..p9x{^{ o}fM< GѿqYYY7nnРzǞ=qQ봵ѨQc 2-ZP:t޽ 0j׮^{_ 冋/ŋ2YYYe˟0`< R .@TTpO>ptl *8yжر<""/K^S)S&1DRhKC+5dPձcO%KbĈK.hԨ1*VHܻsڷDUB^V_~,Ç޽.{k1XjƎ#S$Fnܹs8vp,$$ʍoڴ)6n܄}\ٳ 88>>wިQ`m=F%R)?ϟy{!+o111r711-Hs;t.]#FDll ڵk \=~ի;Gxx8tSWWT*EFFpL*رG`llʘ={6oޜ븚 333pGpZHNNFff+v܅/:QFĉ2-ܽyƍEFF&~;v/JLDDDDDDDDDyKLLD۶m+qUUU|Ŝ}ͤR)!..4i2N8`ĉhҤL?\W^aBրq=FHHΞ=޽sqه;wOZZ+S3f̙x,q7n̵__ 6DnLA>|(WV=p?x,= իW)=!5,,޷u6T\Yرc<../լY >| >| 6R)L__)/ӦM)ԒH$>|Μ9 <{ -իWBٲ@ٲeQR%5?~VS@"e˖J`^^^شiR)ҕ+W)/UV F hӦ0` FyQɨP! e;p`?^t?D551ak/_V3{l̟?"–1cƠJEǨQ#q!c˗Νaoo+>33#F pZx> yS+s-Y+W}Ś5x"ZRhbϞhРӧOaĈxGc,a'''BEF ƾz '67L&$** =ztǃ<#\7x,;vzVVV,? G*s=4o,~ ;wZjZZF7±k… ( +++[nٿ#++KoMԩIKKCcشiaI ~2}ǎiӦr㣢дifP'HpI888ĝE&} Tc0#G8-iiiܹsZ-[Q|I7gMr g⯿ iΜ `ܸضm+ cSRRC{j@f[8kÆ< jjv2Z={RX8v8jժJԩ:uꈊUUUƭ[u]Z( ..444mv ;ݿ/_FfD#T*g7o>>wq\;}cx9s:$Y[ر]hȑcP>Ç@XXhrQB]raMh֬ 222֭QXenn͛{nJСCpU? >,gggjjժaҥ%}XE ܊>'vn7F.S,k'QSSCժՄy fCyIIyB3..P#"""ܽz ݻwJ} Hm۶CÆ򌱶Fݺuq-ᘭ-zg?oDGG#&&bO}Ed\*zEs!)W͛7?9;X;vDDܸ1cƈ\rQ!!!͚5ТE v~3 ϝMssW81t0y}-%QAqg-""""""Bdoog%ӧ.9YXXŘ})S&GE++Vo-DžiذAc)WNZYY;>>܇?^|\;!ʴccDogWZZZb% j׮0;b?VVVTڵUme' YYY}'}~%''ˍkhhZr[.=o8::@?$ mہf͚˗ ###Cĺuʃ[ JK:"""""XJTFF/^y!++Ka)Ο?kkb̎UbI db eکo}>tttDd_TU#Xa2r)տ\rJk]r6m¥Ke THHNYb+'Zb-T -a1?++ ^Ő@v}|Kk900P?֭[y_*ի|細MѫWOHRowk""""""""o@3qM&oߦyfJ3ЪUkU}+[AhiiaF6>55CDDDDDDDD$͛71tPܻw/8cccxxxή2#oREQ-<233$-(Є bbbdN)rYݻb3220iD˹V^-<7odcccpuOaO9(H!sR䔘(*ZaaضmY~Gݺw#""""""""b-*--)ll2K:b!u.]K.ӧrTTTPJUtC }wCϨ{ -+T} m<"""""""""3gbӦMyժUÉ'DVCDHKKv}}o bO]L0%%% "?u\Zh۶*T> +젔޽{)SArrl?.+,k!㔲jb[JyIKK͛7s/hQ)"4(X(***hӦ-ڴi {e8|qqqPWW>,-+Ȩ%"""""""""c5kԩ\\\PtbȎ󢥥D###ŋOHWW/5k,ܹsچطy LsS)?ۜ}zKRJ Es/^(^|sAJ6J1EσO]~wԯ_ժU/pDDDDDDDDDTXEҥKtҰ*SLCbbB大Q JJJ۱j*3k,,X ϕvx% """׏%$$ 11QfOs70)Ch}cbs{/xnhhh"--Y0ll ^200}m}o\3!!ȟ+w3زe611Ellnyii#,"*})EDDn݊ 6122֭[ѭ["Ύ? *ӔKĈP|yQZ@vd IDATSA)_x!^EE?i?[[[[ܿ@.OQQQ0771566?Z111HKKb mm+˴}}}ѣGOUBXXƌ-544qQٳvbҤغu[ADD.DDDDDR-իWQF(_<ϟ/Pwx1 ީ[L辇i׫WOA8ժUD"7D|._yU˗Dt=<\Tl\\]&O޵q2K.~xߢ5k {)Qq5R+ECk؂ PZu,\UV;v...EїJ*t DDDDD c}tbٲe_>0i$\vM*Vwww1[кukMoׯ_c]2ڶmI*U M6AApw?#ƍEcdd$SR~qIs䔙-[6ݹo߾\yyPr7n Ν;bѰa#clٲE|JXxnݺ%;v'!MMM8;ѣ"Ʌb-"""""""""""-%%7n͛1l0ԭ[U~7/)SÇhӦMeMDjР!lmmvXXoߞo%K#))Ih;:6ى (^jL<ׯ .ݴ8uP5jhlڴ@գGOG\\,:uꈎzO>ŭ[p)$&& }ʕ+?W䱳ƍ4///h Zlll`ii{un߾/$hӦ-vcǎ9k̘8y$Pvmx_طoy֯߀`ysƞ=ѨQchjj >>{^*|rtlkL 6T p9W˲eq7Gƍѽ{CDDDDDDDDDZDDDDDDD%䯿YyٲH$%}1i$$''*U˜1c0d]CH?\LB"&LoM/\*WWx- "";dvzOCC7oٳ5Э[)..K,Vا[_1qkլYǔ)[9rGVojjcv):t#Gb!]~g u+xzz <<-[biJk*U 'Oرq x``  ׄvV1r(=~̘1Rj琔_"!!@>+ +͛$DFFm G7otuuaff;*;R)!LL.X?||"..) CCCԪU —hcbbkMgeI냰0DEE!>5~5jԄƻ,]`ll,w4wj033v{_#99KLsԮm+++Q?x" ٯ+UUUX[[ ))oO˗x& 7 affʕ+CCCC C``>}To33SQs$%%!"{\r7>>oFpp }}}XZV@z DppFFFx/<< O#44IIo ---@TŸQDD1!((BlY .[,g(5}6lX???cPWWj*џ J˗/~E59#"""""l&""""""/sJ:"""(6j֬ {{{ۣN:RRDD8,\}{(_~f꼻v\Aƫ ؿɺv-瑂HQ//yGŞ|攝CS9R }}{EEo觼ݭ/^̟`TkkW0yd077:ec)Y3g,l.R3!"""""b-"""""bt-<=zv0áCq}!+K2eʠaÆWTTIqqq8+ի/MII9s!QF/11y…  @LL,R)LLwߣe˖hݺ E:uRhרQUV$%%ĉP:m۶PWW5<7oC__eʔA ?+11O5kB*Uiix$5tC ̘D"G:uYw|@[j- 33׮qqHLL,,,ФIo|/$$G0F077c4offfJODDDECZrx,""""""*Fb߾B[_'x \V^G`֬ߡϟ?رc+PR%رcի\}rJؽ{.]89<۷qbʔ:t/-̙PjU9Ǝׯ_ć=_֯߀Zj9GN7oĜ9;cÅ hBt ,̷(y̚;TW`a d}l„q [dibK{{{{Zϟܹsɓ\8y@~1{;g?v@EEmڴh)Z:++3!"""""""""""""rX%%%??<233iFGFF;X_ c2H$<2s l߾MXӦMѲOFٲec=.l>@߾}p挻B{wfν6l8݅B~ w+Tu {TV SLGɓ'cmxHI\\F,zSNŌ)X 1c-zG]]+WB^=PPP<|GQuDDDDDTz [:%SWW(T:wBZ3KSLnikoa96__=7Eʕ+8{q'NOPh~hժlmHk&MVxtCMM nn޷o߾Eff& 00@XVZ,ܸqCf3"""Y+x=TZ|?{wg%[|C4immm]}U8%Kz*XtǏ$#** 666qW\Q#^( >ׇRT|\vUׯWWWG pm qb߿?jIDDag-"""""""""""""b-""""""Txlա\GZ5?~,|HbA<~wWѶ4aTACCCn^֊W^ q*UuPQTJJm۶C"gϞa7,XP ;wFN++~zDDDTY*JBBTJwIDbJFGGGc@"ڿwgQ!e_CCիWG\\ pQ(55Uxlffmm9aaah7oPPPPbg%e/r*>_822Ӧ}ť؍Ҳr"}M ((AAAe6P7bР/T~߿P-z쁞={MwB""JC";(|rKJJ¢EVrRDc077Wu*bJm۶?XS?\#۫:""JܼRk)zPΩln Z'|R>qxR)"//1UT)ue{^ A9ܺu HIIa kU2ãGT Q@=TQ (1X__Û7o/_Pz(N]]=Qܼys5Z5S୲)~yݰbJ̟.Ǚ3G=hbL2Qّ;kQyĠATT>s`̡TëW[*~ 8s UQCUCDDT)Zb##2H(JLLDnn.45OǸ/Z-yG ?&&Ƣ8''Z㿤)㡧={g^Dž 퍜%矣ICR~HLDDDDD$ *!]]]tݻwWu*DDDDDTΔ)TJNNFvv.|3XZZ*_O"8r/~uPzu$&&>|,hk(EMhii Roߪlljc̘3K… l޼۶m𼈈H迂7!"""""U~L""""""W\)-Zu:ptteKG#=@RRBc ׾}qNNnެ%E T%===ѹ QVg' mWY>DDD/v""""""""""""""zEDDDDDB~śYnٲYwEӱc'lڴQ7o2,SPPI̥,ܹgıNBTS!RTܽ{w?~L/^`KΉ IDAT.yT5jd;)[  cǎ]e )a;kч`%""""""""""""R!v""""""R,l߾q/_BHHۣueON`cc#K?Ç}Qx={v#--M|l޼QmرR?VVVB]v*OEY(*u\]@O83gNZ7lH;EDDDDDDDy7 """""R%kuVv*\]EI?c䢮ӿmI?es Lh۷Nxϥ=33nn,lkSt$u͛/ڶ`|jdee)ӭ[7WEZт2ؾ};rrr|UJ3S|;w // p%1 曩XrK}<'Ď GDDD勝ޟ """""nׯ_Ǽyspn( -۷oҥKXn-0rrr A-/ oSPX(5~8G.]F AAOϽv9&MzZBPPܹ gn 4@LL4sQ&6l  4!!!(,ªU+q!L8 agg5k"33ɸs6_ooo<~ 7Mç͛yy&ڶm> v -Z(ZӦ}7C_^~~ ƍCagWժUCFF?Oĭ[SF||<wLHǡC^X;wF^Q-j׶AZ/p^ߏǏ edv""""""""""""""z,""""""R!֭+RRq!>|!QFᱽڶmFrA@@ڵv픻_-{]֭;ZtVPX+1^]]6mF&MK{% mQQO1gD[ 0p!z㌍߫XKMM a`# bƌnχ/|}}mPWW1LSSwE J-v""""""""""""""z,""""""R! 9s +lVխ[wlڴ 垗=ŋa]R1_b%WzŋZ15MMM/ׯB󪫫c޼h׮,Y01~|ljj={NJ<}yG:u+V,˗u-lڴ;vʕ+:5kD^ajj*ڵQw-Zsйsg v""UߑjjjPA6DDDDY7࿏Q&恼ADDDDDb-""""""sɓ8| Ë) ڷ Ç@ gaa3g qK./_'#Edd$^zR lm C EKP~޽`pY$$#++ zzz[뇱cLv'9\zx5 `bb XXDvѣ'ڴi+hccqn׮];i'O'O#66 HHH@VֿO-[:JDEM:tӧOy?\vϞ=Û7oSS3ԬYhԨzf͚sŊXpp%#1?/_BCC F;(y#N8!?&"""R]]]TZUi}XEDDDDDT hk`ذ6l8®RRzZnݺ5g̚3 ''.5iԨ/_W233[&s NNN¶\A[[G8{u?vkD߾}ѷo_a[NNAǎбc'lhhhGDDDv""U%bccU !CZDDDD˗/q1!СU=y>>>BܻwoԬXb-"""""JHSZi yJ d)<ΕXWxmˣʇyDT:v숃: """"PNtMi,$aB~zkXHHM&vvv,""HM&}TYTG:u*T GF?~<`DDDDDJ}8!^~~~gBD6mڠI&8v"""ꔈ> hܸ1ڵk[[[UCDDDDDDcEDT1fUADDDDDDDDDDDTj,""""""""""""Z,"""""""D޵ ̄򰳳͛7z*̆b-"""""""""""[8U~:::_ ""XժU XuDǠZ5sfCDDDDYKCC!"""""""""""""0X͘fIiTZp񢿪 """bY>$MHqx9bccׯ_5j@ӦM[&GGG#&&o߾ LMMaffڵk˽*M~~>&&&077{h!!!Azz:,,,Pn]Ԯ]LJJ ---XXX_#"..)))Յ9>STZ珎ƽ{###XZZI&z+Bnn.޽8$''۷RSSqm$''011AZЬY[^^(^:J=wrr2BCCsdff VVVhҤYYY E|| cHLL{˖-1uTa'߿&jՂ̹%sױj*={yyy}0l0,XT$Xr%N>/^H]CCmڴ?޽{+=ŋ1|JZj3f fϞ CCC.KpwwbXl=d>^^^|84iR)((ׯK C ܹsQf|Xj<(3_[.z聁I*+t-333gϞٳg1zhlܸ%w1|r III#Gp 0Rǭ_ ,ZR\ll,݋3g,Fttyyx+&88nnn8s <<֘={6֯_/y'%%aڵ?+4gyIMM999ѣ#G" @/_bϞ=8x 6n܈qƕ8ErJ>EEEapMLLsl۶ ֭["w|vv轢{TѾ/_˃7KŅaȑ{nc=z˗c`EFFb ;.99Ν;K<0c [NH7JOTXEDDDDDDDDDDDEDDDDDDDDDWRְ۷oʼnBuٻwܹɓ'۷6mmm4k իWGժU3 &&%퍹s600@ӦMQzu#>>111qtuuCJ𔗗e='Oj`ڵqLL Ə˗/cΝZj֌w}hڴ)6mڄ-Z@S'yyy8tO.=}7o.i޼yBO>0uT舞&Nx@PPBΝ;'ɓ'cɒ%044edd`͚5X|9bbb`EM4i^~ ̚5 C A 555CKKK 7n7C߾}˗_~ t~gkaҤIBl``M6aȐ!Rrss?xXPPcNJ tuuyf 2DxpssS6m7o-[=mر̄6-Z~ LMM?ưaDZUV͛ѧO9 v ;}4n݊ y$''cB-[bhӦhlzz:ϟ/t+((~6mڠI&R.ZWjUxxx`pќo>:U߼GCecc7nٳ9rB-χ76l97o*ijf̙3ej@6m~z<~zzzwRSSxpuuZvڵ+vލׯ\WǎåK^ztw.qs/+++:u ... JƸq֭[ Z@5jO~rr 6n(Ėy&~"555899ƍܻwOyyyoEۖ,YKcزe Wx̙3X`Dǿ~3f **JǏׯKj],Y?S!''\n޼ !ѣev7D޽qA\rEsܳgΟ?/fff8{,Ǝ+zaÆE>}999psstqqqĥK0sL Z@acZUӶm[E ;gΜ[d1uTQw}@B-`Geff:E=ÇK-zpq̙HUXEDDDDDDDDDDDEDDDDDDDDD,SSS4h@-Zٳ֭[:͛kݻwWxmmm7  )EYxŊBⴴ~zpƚ5kDr}g/ǢVZ%Fصkall,*+((Yw333a[XXX,Yv.e q (<u lժV^-%4h &>>>}ĸE%)Eb-"""""""""""bg-"""""""""[X)7.oߊ*B\\^* 6D^J'O~,Y"XqE;H-;wNGP1kN+Z@fgduRkkk7N۶mCnnoڴI႘%Ksv\򐕕򄆆Y[[cԨQ%'HtN+[Ekڴ) ⅘3g΄{eQ᪇G{XB:uJbLlG!RQr333&""""""""ZDDDDDDD!w^ "Eԯ_:::B˖-CV*8aÆx…ضm444J=2BBBDբƍUVzmE;(ժUK'''KW2@cԩSG4&00Px\vmCE]TZZZ -Z'm۶ SzuX[[ YAϛ77n,KvN4jHxsb ?o惫x5N>:ujժ5We}e^9VƜ˗/G2[eQbg-"""""""""/˗#)) _FvvD1g-^:4h\ƍcʔ)ݻD1"Zhxׯo3lllSaaaxqbwwf͚82Us檌9\ODDDDDDDDDZDDDDDDDDD!Î;gϞ)b-555lݺ]vnl `Y&ڴiۣW^ 7n-22RSn]n;vDϞ=˼`$%%E+SZjI-('@a7U ^~wmۊ+{'11Q[ZZ*myS0!!At@ԫWNAARSSX{zXba1ydڶm/ziyᆭlC +++QARe*R+Wv2Y?66VVػEσ/_JaccEa̙¶۷o @aae۶m^zwk}<=bB|?HpWy6Ǝu-uf'O#8fA=z-y'-Ŀ~M4-*;kч,o^MD >.\(򮃶n'N7|q 8q3f@&M₯ZZZ26l0c bHDFF hӦ \]]1jԨ2+H*Qz-  ;c_cUYԪUKB IDATqşsZZJnzz'N~g;Ǿ}㫯ˆ#duºA}^~-'Ock!9sHݻ{aDN0e 4y'kU $%% q^^ ^ 9K/55UX,ϓr޾ι9rƾɑ~6"";ke}|O.*Gnзo_UVE*U.bcc .qqq0`% VS8%?O<)yǏڵkdnilmmtR,]QQQ {) <W\Q"ȾM&233W;Q$:y(J,YjUk׮EHH=z9s戊%RSS?*>w܁45[M6UXND`9::b8p tHڵk abb\NDDDyZ QuB KhhX__͛7WhǏi={6.^*U/\P9֭իWɓ„1 Rx_eV-Z.,Q(22R<氲ⰰ0dee${{{8r~~~c5k&#""^'++ QQQBlnn.#^6.O<|Pᱥeoo/* /ڊ7oTٍ4i]v*ɇHkxر ;v¯nQu:$g^;vvm[UDDDTfzR,^^^OzЧO!~{wWѲeK!~{]m֬ /^Phߵkזz_#GE ߿ׯ+_DD._\TΝ; srrp f 6ⰰ01m۶۷oWx~ 2zXt󻸸8;wΧ444бcG!NLLľ}{^5559}Æ ߽{WIb}O>ENNNرcSժUE^Ω-\%Å8==kݹsO-ڵCeRRTc֭Sh7~7Csuu?#U311K{OnZTsm;wyzjѶ#FHz EE^/HII)q\Y0a(?>222{qƉS=( R%k;k}AxÇNCBB<\xQmmmaff&seQG!00P[jޟӦMZ 6l9>$$5˃:fΜ)6sL*5ϋ/"o8u._~M*::4%%%a &,\pR>/^7M6c1i$Ѷ~Ia% $sۋڵkܹ5kSz-\äI.ʒgϞqqq믑T~׮]z --MyDduC#RMU'@DDDDDD%{8>>>xW^ժUc+tm۶Sx/^q6mPN] Ý;x2PGB>@ݺu1h|B'sHJJGZ}ǏGxx8RRallZjk׮4h0TO|`!ٳp$8qGrrxR|2ӵkW닐$רZѻwoԮmz'==xRRR[T^5ammnݺK}!1޼y# /R攕sÇ_|={Y5c^^~/Qۅ _ 21:uZ8qozz0`BkU3Q9DDTY+x-Rpf̘Kbk0vX$$$@SSjjj%v믿vZ8;;2cNNN0a.1n8u777tAQQQ?~<^z%l+ZDSZ̙3tRGΞ=7n@~вeKTZ!!! ĺu됑]-*O#G_?v믿ܹsadd$u\\|ǎL-ZHêUW_(3xqXf ,sd_8x .^{X4.]СC1~x6pqquIiӦ~>ɓ'ѬY3، L0Gm߼y345eYaϞ=B?CMM ӦMڵk`dggJ*҉JMM {Esp}l۶ Q;i<==Ѯ];w^^^uЯ_?9='Oo+W >>|hŋq-9_}=\ |HޠA4lPzE`Iz 055Cޟ}ڴi1c]1|pl-[D57oѣGΕ+WbB!ÇL4={_t/~>L?#22^^^¶cǎرcRǫcΝT555ܹϞ=:aصkРAԫWx%ݻ[n)g̘18uN<)wa͚5hӦ 5jG! @(tR f̘.ouԁ "իWG РAԬYiiixBBBCWȀ'<==QzuԩSuԁ-wˢB}bĉR$8šuְC۶mQZ5 88X+w}>}ͻ]v߿P֭C&M`nn0 hڴ) RaB-}v M>DV`eeMq@DD"""QMGQFСCmN-- ۷oۅ;'455۷oʕ+\\\0f̘.r:T))ت>oB~}s.)y ??ƹԩK?g0h:u%ѣD]Jk k*.Mbb"F?$VV0x@Q'i pRRRuHw[8pıwK/}ZOh rǥcʔSXXԬɓ'رJ+WPe^^ߏ@QXYYO\\F; 甔T)ZU֖,Y +46##Vĵkװ+ŋ2h7nXk>OXRuB-"mDDCrJ`jjj0yd,_\RSS"$Tkkk?~\fW,ҼysxyyAKKKeQWWǎ;`kk wwwjԨ={SNXߛ+H8{,f̘;w ׵߾};wΝ-黻}aȑX7xzz"##Ϧܹ3\aÆD$&&_ ڵkrǵoFӦM燾} 7-((@xx8å9s`…% 8""BE/::c7n'Nĉ ]VFjժaȑ|111SJ5o^pU :$$$rАeɛյkWٳGM TZhƍXn+#STK-j%^WV- 6%`>ONVV&O? [WNYi%BVFХP9>|#y&6mڈ!wޔ;K.ptl{爎BDDN<ϟ+ÇzJdggCOO4GRsܽ{{ӿիQ;Pe˖ر5r@*Ux{{# ߋΝŁ0j(YDVٹ`\|ǏCzz:Ʃ$)) C je$Q~@CAsC?..?0Z"qԨQwJWWK={I+==Ftt4FXԨQ]hO<'OO7nݽĉcɒy_:gdff ̙#G8jj**W6w\OO+VD.] o6?L0^hZ}}:..uFzzZlv4*vΝy TUUdbEwNLL QFGDDDDDDDD$]VVTT >?-[qDD$փ0|4hÆ _Fpp0=zOB__hԨWAfddݻīWtT\VVVCUcr۷oŋBu!eeea#W|7ŊWl HLLV*JMM˗C$$$ )) fff}kFFΜ9`@WWhݺ5llld|XZZM6011PǏ[J*puuvR,\v 7oDll,ajj sssXXXQF*V 8<eee=aiijժB {n޼/^˗wJ IDATx444 YlذϟԩS+kSVVV&,X >|qq066)0e;v~(NXx>>C#?U^t ,zw2555?~UV-oϞ0kL$%%|}JڹsQYbe-"""""""""[+.UUU4h 4ۜhܸ+m'Nh;::*(ihhUVhժVUUEvЮ];]ޙW/J[[͚5+D%4mM6- гgR[ѨQ#4jHs[ZZo߾EwIGG-Z|DPx6}61kl/)0aĈ+䖷ՍץͻKرL*9sHM];Wb/)GGGr=:y~8p`# [ǎUvJD{R>266!ߗfX0c"G QF:_ SRR￧+Wh7DS>UĔ)EW;ׯw^)*Q OO'BdggOh]t|DDe^?& &553gJJJR7]%""1Y RF ~*|f 94bĈ"N~.ZjСcPv||i3f#s@׵kא"}}}Q'ZZi;ym:::4hqC|/RI{_Ko>,Ieڵu|}qdN QI̙[ (:ĉ?JO:!$%%|c2xDwkϟ?'Bjjj2IDTXYxH,DFFꛞiӦӧ±mں#""|݅ʋl< 11HJJBjjJ*V4@\\,˗PժUHMM{~.]x:u\j7ժUܼDݻwёwHc277OAn߾%n֬X6WɧD&MBOOOX###4l(WZ6l(ox*W +++֪^/^ܹs'/J~n:wRfIkv-Hݹsgc;udffȩx%,-- zhڴ.]߃_~Us򛒒  zDDe.Q0dt0##ΝO?UUU1{d-"""""r.--{^Ǿy#+*UE566FTT 55>>h;ىPJyeee؈k`` "h˒bff555#O$. \LeM).;ŋB;w->ڭZK+k}}233qQ=zʨ\2lmm0$(++ϟ?=FdM֒q{ƌ&%%!++K=u-TRRL---d-HLL,2YReu&&&ͩd[`֮])ynAZ,}N x2'++ 6MGG˗/gH6>ĝ;w Zj333GDDd-)"""pq9s.<< $tuDKLLڙN$%I~ndM*-?φPSSi|P==qK^ƲeKAAA>j֬)Wmbb;k="ZDDDDDDDDDD_555۷NB@@>|X`?%%%ԪU ݺuèQ```PƑ~#6ۓ'vPvm^Xv-M&2<ׯ_͛Ѽysl߾],w ???\p!ߜ]z[n>~GOr.8̙36m*Tjhh(.\u֡f͚?RCPP]vErr2Fݻwιsh"5 K,yaɒ%HMMɓ'8w֯_gggl߾2e 0sLDFFh1qDL>]nׯ_/#Gڵk1c`ٲe&""""1~XD}aذa[^cwU%kikkIccɗ!wJvv6^z%S"ϋѥV+He pJLSSSݬYsѣDsHV*/q}b/L|k%| 0+V,|p.GXXK4"ZDDDDDDDDDD_%%%tQp099qqqG|| >^D)yח|DEE4>:Z-***4h\Ms 2' $7+++k;سg7fEHTUKII J5"be-"""""""""ttt`kkP ...Ғkիm.""*>&kǏw?8kiigϞUj֬ CCC<y&B+Yf!]6*WǏطo?443gĊE_ ر#ԩ+++X[[ϟ?dzgϰw^\rE̟?3fhӦ ޾}+344PjUΝ;8z(N: 'Ih̘1233%$jj -Z@:u`ݻ7nGE1q|Z:u4i%%%\r'O?8brvի9"qW^pvvFahhظq#RRR'N_Hrrr7֭J*AEEEx/?? Ǝ'jܳgE_H%YYG$ǏCTFT"./I'+ШQ#Qc?^!J-kVAUx=ΝΖ z;;;g6wIXYY ْ%*iZ_?OСCB֭[Ʀ!"*6b}NX%>pttoDDT>0Y 9 :TZj7Ej 4nfffYz5`С?>?]٦M 6 ]taÄ$0???믨XyV WWWxyy}[II 8~8 Dŋ1l0z 򔖖///D-ooo,\}[j1c`Æ | .x1tuutR 8P~www7&L K.-4YŋX~VQQ%K0f~~-Fcܸql2DիcڵhժD?GGGx{{cС߿?޽ طoo//|s9s!!!B{ʔ)X`A~rW˗EDDDTJrWՅ]!?s5V}豹~)s 3g*ŸKwډ^zk*fddʕ+ IZ׮!--EnݺU!DEEM66,y-?~jժmӧOc֬rܨKe٨ ܹ/\8/NÆ⪠K[ o[ѯ'vޅT$"(`^DDDDDDDDDDDDDD$\pB$$$-["000_V^Ƙ8qbdŒ5 ˗/Hʭwک sٲe7n\Do^HrDF/_7o 6mʗ۰a/XlܸQuwڕ/Q#uuu,_VVve?{,^z%u H\bŊ|Z >+W)x̞=[hWXǎ+WV-:t*T͟?*]&ܮPΝ+*&M[T_""""MtQfdd`E011ƧdGwY?Ni333Cz߿/A:u%Ϝ9۷NfJLL֭[J3BN>^Qv؎7oޔVX&ў={\777G:uvp=ٳ[.sCKKKhʼnۢEK EYlDeKkJBYY|:p5W8fjj:I>>/aaar~O_~]O>0445VMM }Nj/D4i~ՓhKٳ;ʎ;V,i0~xQ9>244D>}D[XZZ CCC%z~""""*য়~Dzz7nY3e^s.l̜9]jF||<`)m]vC6x\\͛E ƁK3ΐ!^h_|:|}ѿ'vR+IƍYB;$$}߾}gϞQƔ)?KljXfuvex{{IMu~ 6u+O3ϝ;w&L(ў5k&6oKjgѯ!C5K]tڹCeeexy*5}XYxܹsmY`dTQS(֮o޼Axx8goezA]N2{͛7ann^8Aʕ%^7nH[l)j~YHTj޼L;XZZBOOOUk׮-NKKC|rkN:DDDD$_..m7\~mڴ´i&&&۷ÇVdxyyaŊߋOF۶m0aDԪU x޽ #66jjjhժN>CШQcԪU vvvAx^]ãBCyիã>|x5rBjР­[7q^0rc8V'.m˖-G˖wހt䎑#G UTAhh(] իhԨ^ 37n]9߽{Yfbǎիjj033Ç;wʕ8r0?:uZ󫪪b˖-pqio< Cǎ0dprjڵk^Bx!1>>>5 Embb"і|#+++Qm%%%J} zr'k\ ..>Pzum۶hܸ1D?>""""* \:u<|TgQSSW]GOO۶퀧g?b±0hc_ׯ_d-ƁC~}K@zz:֯_{舝;w IIIrn>|X`:bpw(Pllc-4h'UUU%zGhѢeLrд%*K`ʔB[YYNlllͫW|n֭[u(ݽĽ ?~,_?|Zp"#±={vcϞ݅doouu|&MF5 8c!"* iƲ8_}ׁ,--Km-SSS}E];ؽ{wcSzY)S:_A_jhhȴ1|_~->x <(z---[n $$+Wԭ[]vEnXH'''LsJYyի 6^u;̞=+߼c?ѥK,]딅:uĉS={؟oWSSCժ*ϊs \t VnvvU0j( ^Qsn%˛5j(̜9v*eEٹϟ+lб{֭'|nrڵkv!O, l%Kc?m7&%%%xx add$Z4x&O$+U7 8%k1ᝈ# """"OJNNnWT *** Fviiiڵ+.]$q\KK NNN022!*VMMM+W(tU%we%wd_bŮP IDAT"\effxiӦƸqw;wΝ;;w.Zlu֡FrKɓ?mV```Xh.]Vĕ+W#xjjj066FߢWر#CmllD5lpaƍ8gϞ!-- <==alSYfCWWW۷*Gىcnn 6b„! *T:^EES oسg7N:x5kkkn=z^ϢH.%em-on^PUOgFZFV""""""""""""""竿ZD[[[lniժUZ3gܠ- Mʝ8˗f%״iS& }}}v\\RSS!jlzz:^zUd͛7-\ _ lbbWѣGq! &&&_߳gϢ~ѳgbCDDDqrr)If2g uuw:Xa޼B;%%%ߣ5iM;ͭ}+ˌ1XFQ~y*M.zccd-Y4j5շSԩ=z~Tt')m aAWWFƨQ~[֭'jڵv::::ʵ*2:vtGǎ±⼯ #M^:::hРA,,,0~?@&$;XUUbsᶲ2Ke"$2w&"""""""""""""Oʝl/_+vyn:ᶅ9++"DžfXE222nggge˖Y(:.wSvv6PJQcDFkll,633e =W􄧧' 88Nq1Œ4 6 M61Q7AAV[>WnSsDQ?*uyܨ8|n۶s4DD却MXY#E_?FDDDDDTK}a&<{LA.$$B}PXXXi%Dƍ &ɒSQlll$ׯ_ԪU cǎÇq-ԫi_c""""" 8t_]=[FDT֯_ =fFCDT|EDDDDDDD_ """""EꓵZj%>qℂ"]TTD~ƥhy+W((iذD{۶mWF 7o"55U:VN۷Oؽ{ ׯ_矧ƶm~0[!CJ3>-*ͰDaI4>|8N>-uHSS GFFƍݻwCSS1?~|HNNիa`` u\ff&= 4lذ111rSyyyq<==۲ S &`x޽;/_. 6 --MU~gرC2uaiW-66={ŋ uuuF50x`_ttK.E޽˖-ٳgߡCkIM4)r }^444PVmܿ,M 6cǎF}:tpÛ7o7رoGTR...Xh "*^DDDDDDDDD۵kIu렪*ׯcO}t +tLLL ̙S䵪UVEBDDTڴ^Beŋ{.4i///*4&kpppҥK1rHqu1uEڵa``gϞ!,, 7n܀/yf$kѣ1{l9;;c۷/lmm۷oҥK;())e˖ TH`Ϟ=puuEZZ_>Ff͚z갷2޼y8z(Ν; (qTX6l@= ١VZprr,TԚ>}:oߎpQ\wիW۷/֯_zQ H\~gΜ~?̘1ݻwGQJ@Wb8z0ݻw/GDDDDD哞"..u&C_"ТE hڅ$$$-F555E$jժ-666 :uꌶm*8:"ce-""""""""۷{|7nC'22Rb[["#M #;;.]sL4 (̜9S裤Tv8q"ܸq@Ni `PUUEfffO'k9_qqqTX+Vsq@ycǎرc(T۷q|3f`˖-± *> OOO8qB8~i>}ZhJKKÞ={gᘚEn8|0LMM&ٳzUtu$"KUUV})%ksDDDDDDDDD$_,CCC 鉈L*O ϸsN 1e^TTTk.7N|ikkcɒ%":2&L#F'OĥK+{***033CʕQre4lݻwGZyA$&&ٳ8}4nܸ/_˗x-aii kkkTV #GDEKDDDDDDDDDV"""""""""-XC JJOϿ=<)++y捂d())CB@]]]iѢZhQ $www233uu6n75#D͛%oSL)Sc߿v)8MMM̚5Xcİ^z'$$DݠA€0`Xjj*TUU"\Ν;sSSS.i*mG"""""""""їqƸr !!K,9sїK|WNSSSD-ESQQZsSDnms(VVQs29 <8GD_{… BYfQ`DDDDDDDDDDD/p.H2$aڴiBݻw7o#""""*:/'Oa|o+oI=ztiHDDDDDDDDDT$V"""""""""29˖-ׯĉakk[q8::ٹL$""/޽Ttb{.]֭[UVTTT#СCwXGA}ZDDDDDDDDD_& /a֬Y+8 PZ2],ZZZHL"l>}Oտ}믿J9*""""""""""qXY>'xH6 ŋ;0ydԮ][}Y Օdͱxb]}XYHlllp]DFF" oF\\␐---TXhӦ Zj EMDDDDDDDDD$lhԨ^ 8x .]M*82"""/ ,С1iOB{-hڴ#*{]tFhcmۮJ&--  ԩf͚);5k*0""""V""""""" "* m۶B{ԩ8s""""0YXh#!!^x^~h![5MNNV`,_>H (+kї8q 00ǏGїZDDDDDDDD_(=<xifCr_CQ"Ѵic믿aϨQ#q5nܸX ۧkXs%<=}ѣGs{{{\$5dEDDDDDTƎ?ŋK$^HfjhjjbE*τ 㑒ӧd3I 6ʔ+cBG~:nWW7,] E9~x<|U`˗a˖HJJw0ָxredKÇi8OǏ/** <@`` /^mb޼ow֭7>}"##sիSaڵ֭VZ--"ב #888}b߾VZd c B3RN<_Eh7l]]],_ .? ^ s$߹oL>Mܺu  ƢEVX)Htt4͛={vCxhh(.]5k֠AX2O#"±&M̙3 Xx1I6DDDDDDTbǎWQZU;sɗ׉ݽd^Νh%_V^W^G7y@N2krD=I̱矧_)0QKX$''x_ff&||?W%ϋ|}dq +0Q N[;a'5Ydddӳ/VZY._җnԨ7onÇlj*;wƌ!_V^[n)E6mZa]v,y&vGDDT5i055˗/ǫW狕ȓ'a9rp2\\\vvU`ff4DFF"22<*$XX[[Optt ޽kׂ>ȩӷw8u4E <PSSCWjŋhamBcwq5?햴ւ,)}-{'Q,ٙ` 0[]elHDBGN[綨_/Ϲ{>z,Y;&Mr@ll,`AС[#5Ag IDAT5aϞHNN… q*70`ϟ;"(._eVfpMѣGg҄2wbqMܺuJbd1%%% ZhW^"$$Νŗ/_6zH\v[d1_hӦ-z֭[CEE[n1 ݻ0m*zŢ͟_|FBB֮nccnݺ m\1ڵ'OhVVѦM(((ٳ8y"""K,p?6nƐ!CѺuk4l/^DҥKs6n߾>|8tZf<<5ǚ}}}4o<{/^@PP ''S:C[[V"ZV"B!BH]"jBׯKbt]"B(YB!BӧO3.ؾ}FY^?\7CRv|+Jj߾==uu 9}}} >FX$%%ñq-4FQNƍ{7D&)l޽)PSS͛ae՟ծ(1cF13³gOalܺ{}c---x{Y::::h۶`i+AEEUD8sJY\]gǏ8u$+Jjܸ1=:^MMM1v-O1c *-՝xg/aΜdݻ7M-[6cŊ۷eț̫ʰa/_d%ku u"g..S| Vd~g\| @ }|lllJJJL^z;w`o߾dT8\\CAAUW^>}6ok֬F^^0u qN$R}DDZB!BKhpB*fԩ@TT 22f"|WjjXޝ)`4u.kvة!Ԥ{B!B#44yljj*4Q$)))XZZO>ZˇU\q%VŖ-yz…KrVVN<ǏJ*bllٳ簶pK<{)OE2e---\rUJY͚5/mϞ=9]/̟Uaٰ֬l.:uV^#4^zpw`%ǝ;w)))e%j+РAfU&!Rm۶"::);9MkJ%jcΜ>}-:: N,B>D%= NB!B!dddbŊRC8Wl5XP__C"Ԑ:\G__B!Bjċ&M`$Uc֬٨W^u7o#G1匌 >}3g΂Vud4bH/ݸqv#qo U*7mڤ}h"^f55u̚5m]lٲ%U\]]YIe?2eee,\Ku4551`6III,_^6iiiٍgʹxкYYYػwS600ʕ>`_X=9#Rhe-B!B!B~nhݚ4!B*CkQ?J?xBg'B5jhJK6lJ*L;vYx1õzFFF`@(((/UڮT(+@IICBB&:G]R222I[RGՊd/_ (()))ꛘn"=0H%x<(L!u-[D`]7? -[׻w'Zh=ƈ}壆 ~֓>}l3004Sԯ_P"fϞ)FCHII!//jJL99J\a}<.GqA'<A~54,?(\=1]32289Ƌ344P_>9_xXnn.""]BH!B!BJB5֭[4nݺUI|˚5Ӄ"@RR"<==qqDFF2?0p@̛77)_VVN<}"""*##nݺaƌիq ;w>>^Grr2C-зo?L4Ih\U݋W ;;COO#GcIG*((ÇqAll,>||tAOB!ՇV"B!B!۷/ Q.\XEԜ˗/ٳg6mmy JOJJ¡Cpq%PxxCRR1h 8MrK5?Y&kihh|q<޷54pnWMǏG@8qٖɜ WӧKK+(++EZׯ7Yެcb~U'SuttDd}Q'gk%xk;y}suO `yډR+BZYB!B!uIM:B1jH2x<^ɪrss<ǎ@^qtsɓ[7$$cX܊";;[NN6mڈo`ݜ'"'''ڵk,|0h'zJd"RSSK/_ĀVpZl)xY1b8^VSS+cSܹX')) &Gppׯ_jr٧O`Р8~$̪4z_p23[nÈ#+0.ƎZMQQ& 1cF̙sk)77rŷsc>{:{H)..55}._={Uuɱ߫ϟ; X,X^^+VT}V8BZB!B!QLLabo/tuu`e;wax8 s 4_Ffͱo>f۵k7tdɓpy={S8g9[7oOI6eee}ksmxǎ8ptZ=| f̘W=A%,&\'fwRǮkڵ3Av&XW"0.u߿g=xW+ORR;waVgKJJ۷ ^׼Ы'&LXYey޽cWTyʷ1t5j0"B!E+kB!B!B~Trrrؿ 6Ν''GfӧO;v,UDJJ nnd-zd/^V>ZMs˗/غu s7ndm۶ekӦ-858زe+zάuaʪ?kpׯ_NҥCKKKJ3~ 55umbş{7aa'No-) {Azz:S 6ӧ¢#bcco߾FyM6Jׯ֭[W̶-[6d;ZxID⌍q!{G$W] !@ ˗/q,X={2Zlc̿ݻwWٸ+#B~d ֭͛[CQQݻw3oӧOk:ZIJJ ?\éSgX O>Um$`~LTUdXYǪUv؎lTSS <<6 44 .7nPXeqJ+zz#iܸ1VCB*!B!BQ2hB03E&j4hXlll`jjZf}fb'fر#-&&5ڷ7$S(Z(uk[eXtʞ/$~ EB!ZB!B!՘1cʭ#%%JѣoM4a'$$ ''GhJ& %R ܽ(>Dv E!//ϩkMo pYYYb]O755P2RRRLmA{y,++ KK+m;vz+OUz?1=zX 4`L>]u⤤2)TR= e%M0T(3+,BH*J͐rMA!9!dgg8pmq]XZZVCdB~;OZJWWIFvv6|~}'%%…իWz [w٭zYqD8~S~[Wώ;1'ka``Pajj pGoD2ehiiU(HK=^RRR ERs )))3cvLCτu&c B!B!eRRRk̩"""rf9+.%+5X۲3[*9,]tݻrHHƍܾGr857D?~ӧOHKK+5qh||ȹq&Ԅ.BCC!*<mzz:XHKKsn`hhPQIHHlaa ffpE&E5sV;^Skj_5!;zn߾]aT ѡCA `/Ɛ!CiӦڝ9sCBB?ι!011a?~.\ꤘVUVBiw:55ǏĉefT*VeLNN,-- ;G*l)}ǻweV':0Zi& j͚Y\l4Rq6|>3dɟFGRRRp:t##RQZB!BD_t^5GJNn::e^,b IDAThܘ[ Uzr?&;'Q tttXxڗN*Y!#GNIN:{As̹V[[IJIIAJJ HTT}fte}f}?#W{(DJg%CQ_ތV5s*++^zȨ!U&kB!UE[[033)ׯ<_劋f8<]~ nnn"W3y5f͚˗ 8VRRǏ)YR 4zBТE l7nܨjd7':f8ʬwmf2۷og6xGhҤ_rssnZVUEݻglݺ22q"55fg7ٳx&Nnݺ{0:: G|KjЀΖ-888];rc +WI/1ׯGtt'{CQQTd8::ٳ̶Fz::Lŋ4mڬ.\}˗<+WBÆ i w\]gVwBA+kB!B!G`ccS!SWWpKuPPPP;TY%'UlVjj&i-Ԩxx<5j$8ׯbX\nn^O~/~Zc8%++zB}WG$?r|qCKK _*OE +sLB%kB5oͫߏӧ Xl<==yfV,Y1sL,^!|OFj=@Xd1FN:AO14i "!!nݻc}]кu}w x!ـ鍅 `ff===}Ϟ=Ñ#G~ ˗/Ut8{ 0yt`mmmDDpw_3m455+4^]g3hڴƎVZqhܸ ׯ {Ν޽{i?uR}<=M=z4Ѹq4nHIIAT[͛SN vL{[A^^ұ6'O999ڵ ,-:qɓTժq&عs3'Ϟ=+W`ih߾= ѤI|ABB022\Nxx8kF|ίϏY @AAÇYۛT]] b>;O"..YYY-s24\zW^ϑLhiiA[[M4AУGHJJrz=\|˃& 0p@ :Bnܸ___DGG#** ߿G5554mѣGB!*_ŋ %%w++^$c̘Qxk1&},]DčMT n+Lm;G_QVQR_{lJIIA^^t4+gudURP!RI:u­[px)-Z(!ѪU+}pppʕ+KA!瑒{`=6>rss`ׇAHI aFXYY";;cٲ"5'Npւ`ܻ (<ٕWz&+EEE=zB7Uo߾am{Tx̝[v'YI|x*9cǎؾ}\]g 77@_ &x<ǯ$6l؈{TI-n݌/_(\…Z`e퇫 ()>k!ڋV"Bȏnݺu 4m3qRccckŎ;?˗/FHH;555,^ӧO7_g߲e |>V\ٳgY7//6m?#tV7o ((ʕ+Pn W\Sҥ P~tRDDD1h &L'*.##6mƍӋaoذXx1\]]|-ظq#֮]+t(СC2/bʕ.deeaL:SB!)ׇ¿2W>|k$9reddOCh޼y*dߠn;qOQ$%)%ߣ];Kk.qRRʼw &QpuJ{4iҔs>_Rj_nB!2rrr[ 2aaaؿ?%jBOHFFFFF"."-- [q8},TTTظ5N1b$N>CCR Kj֬9? ;UGm"))/b9?lŃu 8HQ7|?} !p@+kB!g'tUh(_~Űa믿 M*))) ,gcذadgg#66:YYYŒ%K&jGL:s̩`nn.4QKX˖-%ǏPfDK,t3iuuu-wbK.ښSV,qO!uY˖-k:BKk޽}\ TruqG2+رcUUU9sD-{Eoٯ2+w#>>׬Y{*y܉"q~~X J%cRQ]+Q\]L}XPP rBHA+kB!UHBB666x!6oloР&M֭[`tBj<;wnmx?& !#233!''ظ5]y.]"$!o7|}/#<<с'2Ic::ps[ĴoٲȾ[2fmҤ1ׯݻ~">򠨨6mbĈ7ndd^ ˊI&1'5559:t ʛ}i)HLL3/۷qnbbbͅ*u^z~deeE;~x7 D\\>}J@RR ;r_=53Pf"]tx{{AAHKKCCC;vU >Ҝ2d5k@̚5#b X̙3_񈏏Grr2SGMMTvڱMM-ct؉UWXN-NEE¢3q!<^BrC^^ 6DÆ>}_~?!G+kB!g"Ĭd`` ix=.\JhlLMMahhyyy<{ AAA8x ]СCPWWի{{{&gϞ!455X <<OF||| ϳW۷/zꅎ;"''AAA:;wVZq~999c SNh׮TTT/| =z(ufѱcGy#޼y/_ɓcwg۷!|>>};wL܎;e˖ ͛7g! XZZBNNgϞĉ4:!2}tܻw δS %_ Љ6a``*&t6>>ӹ|0;qN)T^&lX>o3ήsBE!ITT`,[ :sa/^J277ǖ-[ Oprrbnڴi]ˋ,szoqM|3gV^sYZZ~sNf;,--sA{ %%+V_~)]͍y,0qDzغu+lmmYn:;wgϞJ޽;o^jΝ;'N3 U0xWb;q3!ۇ &עŷqBȏfرx)yDB*%u{L(? ͛ '{7] ̩S'YessJӶm;Vǻ ?éϟI[|TW+W ''gfjjZq@FN:֭[Ox+omanޑUᅲM-L\\& }}}N? Nu}|YʾUĄ;bbbǪW|uM˾1K.V200c~7۷))) ֪;t耑#*%n8tIz[[[;Vxs'hhh?&"^BիW=z0!cm+Up?ww,''+W`m7ׯT,0a"֮@۷(3f}4Gf͘ī;*sO5blPTTY'44.]dʪضXn-;c WȀ4+SUrr9sf1ewgYm...LOOǦMleswwGvv6S=zLVBB&L((Wc=e~DLL R u?5BA6m£GP^=Y?F߾}Xt)?C"??{A``wB!B!D8Q3zݔL!Yp!$%%˭3g0mۢsΜDZc]a?zիYܕ1m46__JS/^̩^bb"ܹÔuuu1{JZ!&&&_%+U1K!B6%%%̙3m18qIII>o3TTTJQQSNC S'299Boڴ&m۶cGGGcѢ_X+w=>}k a^~A&(66֬ ~7+= +fٲ|aÆaZͪd #FڵkrPPP\;666U2{X7u]%bСm[Ƶko߾СCၶmV0jB!R[̛77o``ƌ8y;?& 8G-Æ U%())aݺptt`;={:t|}ǝ;wP8Bm %%GBcѢEpѣPܻ/^TYAF;vK}}%+033Cfcܻwׯ_C^^^[[{pOWWqv܁ѻw#//!!!~:?c[rU}V=0~˗3<A@@o(=Y^=drP>|9 /k)ϟ1e;񐑑x(((ܲe+tԩǹ>={ʔ'@JcBHg4)СCK]p!B!BI!B~6=zUNOOǡCI1+#%%%cڴiعs'|p]m۶ׯz:@YY닌d+))AAAsے~s&''MMMNu?*7mڴJb eߐ0z|=dn MLL,5ܹsw^fׯ}033sL())Uk"B!ߏ$={Lܸq7nNJJ 7o!U{Cv:֪=Z_MM {ٳ8߬Ys,^˖-eEFFb+2U8Yk̘1¾}{ ǁEoݺ :i5bqhrr UOOOǙ3t`llӧ_~Pĉ"W SNCEEX<۷%bbbC<|6nn`ooy`e1\w/D&멨`Ϟ}!ueſ"==0jM:;w0#Fd|,\);;TK9֗.[qE!B!DbƌiUڧ &OR}B! E!9- \j[ c8991/ǏcxxxC !CвeKcᚘVDUUCFFȘk8dUUm۶Ui@a IDAT1efz@`Ր@ΝammaÆаRqB!GAA'OΝ;aRRRʬoaaUIjg$'ayYknرc'6l(V3f̀6~} EkРw3d%Uĺuahh`[Ä bJ&DŽ aaqj^(++7o,|AAEaܸqM!򣰴T@j*.*9Y|Nsr@[ǃ,vyy+;S9RVYlKe"33yyyǃ$|>իD>q/}^VZphi#==yy̪PPPǏ| ^X;;:ӹ GAnn.!)) )))CN.. ^2#?ڎLyy/|!++yyykePwJҒWI‚fBO/*^KIIAJJ |>rxxX 6oV\~;p{m((B^^~~OvU*((:^RSS{ #c - dd#77A٫__|YX;'Ǘ700(?aR9E!TN:ٳP!B~JRbtŅ, 1BH]E+kB!'''*Ϝ9qp$Q6m ooo$$$ŋt&yyyE1Slڔ쯮(zZBBB[<7n̔ EE>gddׯ#..>>>x"&=z.]µkо}@!REFJ"%^{w_I&JFa" [Z EU^JE?{yD%NIL?*z\k& )%&kKN%!+VTg!Q!RM`ccSaB!,,,MTMA!)!BprӦM1eʔRM)&&MI&=7p%0RSS1yd gbEVV &&FSRRX bB||wHJU%FFFppp`Uy,ƴi0m4#$$׮]q=^rr2p}YB!B!T?o"00AX;f_``,XP!B!B!BE!"\ 44/!!SSS,X~~~vttt߼y3gΔjW<$dffr366UXŘvիWVTwQQ\m:HUDAAA.g!6ލ{kFcM]Q,X"MH-A8 N޹f潷R%Qٳg*B qzaz*Ξ= xPP?^1Q6&kRJptt)sttD2ĝdkذ!͛7 ၍7sJ#t܏wR-Z`:Y """"""""*xy7!"""""""""";kVdIxxxҥK`ر}bjРNXмysڵK(/_ڵs촴4^:X|Ǐl٢PE5p{gxyykܲeˢvڸ}6 z?WFD/_)""""5̄`>RSW )) ߿GZZ)A2eiШXRuBT1YKE222p}DGB|*U eP U:Wbb"?~dff5QT)S>|/#!!eZ\rR""""""""""*6|Ǐ`ڴih۶-,--ϧY...9t'ND||<hذacoݺQQQB)NRBSSSH߼y3&M]]]㇇cƠJƍ%͚5 ]ts&MBϞ=رcѲeKk**NU))0-`zC!*"g4P$ꎄH}׵jnpwwÌc 4ЦM+xyØ11p`Pݺu˗Lgg't>>1|0tNN3HJY!##C8nXt}wڅuIFߏF^=аaԯ_6m䪭DDDDDDDDDD;k׺ukRh׮.^P4ڵKK 6į<ǒH$X`nݺ9cѢ:OOO?^؛6mĉEuSN۾dɒhݺP&߻w:u厩j=z􀻻PGݺug{a2uʯ_F˖-P\߿ǖ-[ЫW޾} l۶ )))y> wWXXpq+..N>K,ʕ+ HK~t]v? ]]=;v܋iii8|0N>7}Jϑ_'y&޽C޹^~)O'Ob˖_U'ƝrsN4n/^Cڵ+:t'''8::dɒHNNFhh(p.aPT\EQuVܻw/G}Dx;w`ر2ǘ7o $>"]jjܕU?~`6n(i V bQLMM_wޢ$ÇBYSSJII۱}vNOO2mmmq(QBx8x :v233b X"XZl7s˖-ѯ_?޽[;ukPD ʔ)kDDDDDDDDTpL2YT7nxL>#NJ6m:0~8dff6n܀nf̘k>}"00۶mb҆ i\}l2>044/l@ՅՓŬYahh>Cd&j}%Ks +V5!"*zдi|榢hl6mĪU+aeeƈÝRx&&&񁏏233CnЭ[|%+\]]1I&hҤBm2&?7$\\\9s{˔)#GbԨQ zRxDDDDEdI$<0 Y˫;"ZJckNawǏ!%eBT3nܹ?8P´>ᵮ.gmmm 63fL/Ј+MDD/^TwD_eRADDD_EDDDDDDDDD_%--d֮(0YK))))xPvrrB-[\9t@`hԨq}e`!Y nܸE%kEFF"**J(w ƼysÈ(ݻ7DX`#R~wk_~jg駟r~^pwwW[))Q~66ccc#$k%&*6Ga=+\{"""""/Mо}uADDD$Ri!GBDDDDDDDDDDDDDuD&ɔ%utt꯭-nJq{ZT"9d~!QAZDDDDDDDDDDDDDD%Jd<#16Vlbl,>DF*מǝx e{{8ɓ'k|" `jZZTV6("BU"PNJ$zJ()SF 9d~~~x^",, (Y$J6)мy xxx@OOO7"".BC_+wNyt7n@xx8"##--m.mGh;v̱Cn߿ ܥKW(QÇʕ+B||LLJcŊJx4^zz\c*"9!$$DTUQ|+[-[=|wʝ;wwÇrnw˗طnDDD$ +nݺ4h(11{솿?Ñeˢnݺٳ\\\>r?8}7oa``s]6ڶu񉈈qg-""""""""""amDžB޽ X[[tD Acɒ*kd-%|ڳg!rZq0T^]~ϟ?Sx0dff 55Uf6mΑuğرc;\4}o/OCaʔ=?~+WM6.~q!֭[߈ԩSرc8סCI֚9shqv.3N_AdSNbBIr>~Z'۴i---,\?.!!!8{ ~iVX]oذ .Eaaaƍ1zY2ŋ;w?.xpp0`͚hEZjJADDDC^w""""""""""""""R Pti888ӧ PBԛ7opBYCCjVh^___3VM޽[T][9 }E#11pQ̟Jʳ#R!R)DgjPIEiiiа"Rɓ˫Rի8wSRRr ܹݻBWWWзo={FP=$ %%%aذ!@.] J1axܹ#~YYYXf5ttt0u4b:< d_;ѼysQАYϝd-%u ˖55ky2!CD\\,`Ӧ2d*(<߮];[?SSSA o7oeoo{T=z_&JjҤ)<=J ۷o&ƏI&v:mӦ-ٻ IDATDB͛ƍž}6oވիʍ%&&#F >ߏ s$̵j Ç%fjHRb!8}S,:EDDDDDDDDDDT4T?Zaܸ~.^ &ysOPf- $)nܸׯ_WʕoKLLDh x5kĄ z?0 Ǐ5ٹs'ڶFDDDTQ0Y3hiia^_ > Gq={Ff5ܱo>W[DFFbUp=m5440w|W9 19.]: [R۶L"""" BCCHOOjwuuu<Bzz:tuuvv 0kArw(Ys.γ_jDOCd&k<޿/nP9ZlÇnܸT=""/w"""""""""D*"..޽xH$+OFɒ%abb---AGGG%KVVbcc ?7o 55*{n#== qBٲ_ DLL 455ann.>1Y3_{XG<|Pn[kkk;)ڧf?>}*Mj.Xd)ׯYs6 \\m۶@OOݱ`B!J"""""qvvƵkWϞOޘ?!YNxD":"@||<,--VZ*8qttTݧKJJ_Tn@C+W"NHH@XXʗ/DDDT0@1y@\\,ۇ=DFFŬ^;ޥK~8|Ο?/-ZloooP*θ8l޼ ϟCXXbbbc066]4k -[D:uN:s4:/{hժʱ<'N'NETR ]|^x(WL*̙3 Y#G^z{Cx 233ffe?97 9 .ŋcC۶ߠKpppPsp?^C˖w>vI?~\(/X&&&y޽{o^ܼySGUO>ݻ ,v"dkڵGXX^Gtt4QT)VJvp9;t:۸ ԯ@m#Yll1o|L2gϞŋϑ333X[[E_ _2t" :::ݻ(|X`""_%0r(w.^&MFh޼7n 7j[ (>ك'BXXR}NbRXBʔ1WI>nzzzq_OM*~R4+>> ""/w"""""""""۷GՕ,Y6lPjӁ,[L(w{VYɿkuL<.a!9ACC_` pܹñcvܹݱr<YR)͛-[6C$$ݻwfjL}tBmky&k|>>c{7iFlݺ NN_DD_&kHKKCjjPVÏ+W={ReXrծ]k. !:w0 -[q!== @@bB:uѾ}{k <4L2wpSSe_8\9&|enYn?y>`dM=z(ɓDDD:Yw]߿?G}z8q*TU%k}U|mwyy&"...1mmmhkkAJ8xBBC066;˱n077GZZ:bcO*ɓ'Hc!33Ct7++ ;w@HHً%K:: x{{)?cĈ‚*UsԿy&q/_4MMM uYYYؽ{7> PQѹd.kjjw tgΜ+0 Lrecc#GB=QH* KuADDDD$SϞP;&Nk׮8.Hp5\~ f=zb03+S dee˫Ν_>>066Vw(ji<'/qoa9)[,֯BBϺuke&k}Sn]o511cqq+544osOHNNƌӅ5kVQZCήBmmۊW^ U?cʕˑǏgwL<%lllϿY&Bغukd,Ϣ={>գGODFFbWI]"?[U&5k"ʀ>|J*興8F:u0~>}p<(>Ξ=9_~-Z} %j/T }]ʗ^#EDDTTpg-""""""ڵkڵ+ʗ/ tf±ch]dhW:>>˗/Wc4Ӿ}`WfZUaÅ۷]ddh|Uؿ7 ;(ֿ ƍ*IUzPVVV6m(3gBZyYu2XLGNNNPC\\lQ%??? f͚յz%KdhDD_b˗/q_ڵkcȐw3fBcmV޽e+tLU1˻YfXd(=zڵVOOOx_EiD ϣ  DZ]..5j$Juj1""""Ry;k1YTAKK 8qbǒdn_j\RPߣޟJ!#G; "RcԨQZ F#+\#FP]\\,n߾-6l$Zl5/-Z5[7agg'jSBBBW\AXXETVtD=== >q >Rv!::Z(7i-Z/_-۬Y3Zh-[6 7o|C]^GP?###xyyaWX&k}Ls 6 G/\L,b=y|1}|``IDDDDDiР.:55޽Az3=--M-ZQ0XYY!66A\\\-+ǫ)iժ5̙-WZoccc5FEDDD MTZȑ*11Ý;wSQҷD"ABBگQ`޽Uw(DE1*U((UVCoܸǏG X!100PsgCHR+뵦p]͛791b$|} 1u̜95kDMPV-4jXZ+mllz###_#.<]>8qOܺuKT*.;N%K*0wd-{0 %,X1]]]U:QQ޽{;t%Jȵ}zz:VX.rQrD ~Uk>hi#GO~h޼/0`:nA(_:<<ǎCΝ = {.+++aC~DŽ aaaxeƌ(ȶghjjbY  ƍ //ĝs`HOOWw(DDDET*Ŏ;OOO#Fd,":aΜ98p {ҳfΝ;YᰵUܹsOwBʜoZDDdÆ WwDDDDDŖT*ŭ[p-̘1ݻz|y;TPׯw7ov <"{f͚޾"? ;i֭[Sʖ$uka4ic߾3y ۷|2t&MqR.H+c_ ۛeʔ=TMDDZDDD,[ o߾Uw(DDDEŋQbE)KKK .Dnd/4c :RJ7nBn=czzzoʕQre9 S'mV$'' &M =PPgdd !!*<<\#""DTƊ%keff*<LJP ҧIP/^QTTNNN.Da166AXXI*]S~׈ &kCCC_ׯqe\| _h%J-lmmQ ||F95kf͚ӧ@dd^z sͱeVx{;w 88!!!HKK1lmˡCٳ'*ܪc)VrjܸhWOWR. ~Ec}U ,Z'N³g'IIIB[[4h :w֬YseCb^U4m9؈):+<@8'\gϞ͛7BR`ee+++U@6mмy gEDDDZDDDu@DDT,>ϟG=ppp'OHOOB}HsAɒ%Eׯae%yRRR %YUT }qTq޽={d/#k۳[ IDATXXX(ԗZDD7"""""BTlYtn|H(?~ (WZDDDDDD_/-ESSSDDD;EDDD0.KVV ""RVǃED*h"={V}hӦkʼn!ZlSNޝiU4ibTM6mW(]Fdp<_ZjPnB۶l+WF,--Ѡ] x%6oތ#Gk\3334lW\<~6l#mo0gl~z 00Oq왂d-"""""""""""HqyH{455sMx}!FCDAZ鉃o8p=zPsd_'ZjJoժUSjwH$x-޽{r͚5si׮,--+oѣGOfeea޼h:::B۷aܸ044;ǹsp)oNF-$kىbm~|;~x!Y -ZmH`ˋ9::ã!^wxyy퓕ŋ"-DDr0Ypg-""""""""d8|0$ `̙֭R)һwо}[mWvEz:fi޽ӳ;tttJ?QQXfxwЫ4ioiΉ1}EX5z㦦bѢy0se`l|ߤ &L̑%fСؾ}+#kƳӊ?2 `Br<gӊT`9?uVmZ=ySO@#Wn~\lڐ#ry?ɣH~SP&k4..]#|? 7v>_*yw"""""""""*^fϞݻw }Λ7WsdDDDD_/&kob ByĈ_eT*暬5g:td-"""""*TϟSU:I]c}Qbkk#GbŊ[&MRsdDDDD_/&kQ[<}Tc&$$t<"""/w"""""""""*~M͛7#)) xb 2DQ ߪ; """*EDDDDDDE6T:J#"""Rqg-"""""""" &M?DzePN5G?..)&kQҮ]{<{Ba}Q4vX[_^+WTsTU,bCL""""""""""""Y ƌ0 ܍qauAD%Kb̙)))Xt"""":1YOZDDDT`dd0 C "RСCrJxހRQ!;k};w """qg<ѣ۷077m`jjCC_ $_а$ʗ/^DhDDD 22ޥ֨V_G!..ޥ PjU |,>纮uNs>zHKK*6m }};""G %%jjjAfg+H~~> iii MM nrrrUz.B!B!BMK "B!B!0qD޽_DnnnΩV{ !T^کyyy8} $K6mbذaahРAqeB`` }***1 G:u8{޽{ׯ_[.0w|o߾Rknpqq3^,ZkuLرСx< |6mWpu=w&L AFFΧo^k׮s~--<==w4z%KsΕ~B!B!?dYB!B~=>ٳgM6̝֮;<^K:uZB!C\\[nYj%\]WAAѩ9 !TkyӧO}2/_֭qy<V\ggg}RSSsܽ{,ra1Οk׮ڵkX`!֭[_+CCC1}T}Vd$888ǎG߾D%L;7+bcc1i%^ :.<<ӦMg3u6Nռ|ɓ-C}kNܸqO;#++ apqqAx,,,{_!B!B!Pe-B!B~oGL{ٲrk׮2)XZ"++ |'Ezz:_?qqqԫWG:} nذa:wK$߽{*Y!B"7pr:+} !"--HJJsԒGӦMHֹ`|8;SQQ.tttbB iz,UbWTT[D3IYYY8q;!Cp>Wff&Fw1455ahh*[GѬY3XXX7??cǎ'Yǥ`lB!$5,-'becwdʢ:;FJJ Ns <#B!B!!B!j߾=kߔ0۶möm %%:Fbfff0339-[VE!Ӻuk?JVVJ#}(Xxce`&}}}G& W9+ ```|]v[7=z[\\z$ANNObb"ﷅSj0 .]r~UǏ-++;yxc˖طy.ڵkLUÄ XZ@I5Gׯ߾}RSS9B!B!gZB!ԑ:OR<B!B!T5 ))i7jG|AJWW I&3u4`JF{{{U_LW^Xl,,,qeyvcѠAN]vZl%~qqq,]U] #F#ix<+r ~ \\\ Suq:::ظqf͚ $0O`ٲK!B>7n\DzeKtСWD! U"B!6qDL8A!...wB!B&hԆ %slll*WN 0DhjjVZ o߾AJJ*W>;; jZZUo"%[T׮]+5^RRFFF |Ӹ ^2r`O0eddЦMJIf||}}JA!Bj/oo/⫂kU89bںE!?ZB!$X'dggCCCk֬9>|(((}􁎎бdggԭ[***PVVuzUzXxzz ** ?ѨQ#cuuuseeehkkCRvd|ϣO>1 ]eddX oy<lGpSz>)))Xf5>}I+#;(ML^TT*{{yϩ%'BZ6ԩSXZ8p eee(((0_BWW /VPP&V*?RKNS|9+u򤤪'pB!B!kO\ !BH ׇq !!!Yp>Μ9:_Fhժ%6l999ŋ|$j¨Q%//&Y1h`4l(,,Ǐ7o 8igw7obSTTDѾ}x<</9s0l0N/_j-ZCIIqq_PPcGˋu\II ͛Yf000DRR"bb Q\\3gˬ5B-ѰaC 44x.]VC~JǏ+5k%%%$''(R!B!B!.!B$qtѣaf%=}ډka)|EEE8p`?v܁%F:u>z._L{Q/2iDD\]CIIIhlݺulA8zrrr?'cܸ d Ѷm[k)_ŋCCMM'##նei9 ۶m󄄄ԩ((x?888TTTa&?uAA [}(,,Dqq1-Z;~?,..k׮f>|8v4;Iljj*&L$v͛7accSC+"+@RR{?G~ϟδ5jT!ϒɊӃL |Fjj622\|*k]r>Kմ"nTUXZɏb*˩t !Bȯ X$&&B^^*PQQ>z]]wƂL@I۳gzή]U"44wӧOS"Ӡuu tGǎ9?NDDcڣGa*2ܹF|| nݺaQ?̾( ^ }^֕СCYbbbзo? dCgdWu61 +.\pŃG|ڴiQFĤKEOP>|ݻwx@JJ hݺ5^zM!ګZB!Ğ={Vgn|ڵk޽###󋋋c%̙VJ[|K@SSWXٴaÆXf>WBaa!Ӷ={ |罹k׮r"<<<YYYؼy#N8)|ixÌ{ ef9<@ ĪU%KpAlٲUZjېô'M={ |UTTpU?vM!Bj/`L15kV+"{űcL;!!RRR5"BׯcLh׮] 988`~[I1վ}VݻwBzN!!!mC!a"kI4o2Բ~%7F:uW@@@999ԷM cAAA055b !BHqm޽/_V0deec.L89 럛+x)GcBbccaz\vMƾ}{ѽ{l۶͛71;wNN 77/¥Kn|"0k,]eٷo/]`笟uZ^Xb9ӾwTUU͛7"?7:: ppǺu֘={6˗/cŊeHKKc#^Ǝ@Re$l߾ksO)=-Zbҥ+ !R;Pe-B!ii ĉwu6ZeH8991}Οwņ 6ϔ޼eVT@uHeeel_Nڵ&&@7n@||<xMUE[[W4Ύ?~|Ze ;ѣG?ؽ{@,,,| sv>k׉eddʕo֭+40,lll`jg,B!H!?_LOOQ#V.--y܅ ձdddЩS'gϞ:w̴q=Ncɩo=XK.r_$!BUn߾'p *OqU%e+4PG|0`@xyU>CjNN  0P"cL?ŲeK~Z@~Zf۷VV;w,+KPE^ BަpvvU>|Ν;B!B4gAz8uv>7%,XϏUbbdcD3fXnݺ34 Æ gq*-Z'yͅuUkDনwi7o#Fiv}.''/^pO3ܽ{LҒULcݻwu.B!B!+kĉغu (~kpw]i@?~DDDp^+&LG|…bjmba1 L{~[M*//_PV^l}0|MM G|0\|?'ռ1 pqqadddu6kh۶-߱x > ߒAԭ[#F@v`llx9\¬˗/7n,n߾VZs~/_T ر#͇CPPP@@s uIw޽úuknjO4ɓ'c ^KUV)?gW#F@_#==iW|nݺ˗/Ycq;v8ֵHB!O'&&&🸸_HJJBJJ UTTj'B`ʔ;,ӧOkN,[\dR<^^5oO>PRz?&&7ZՉ6b?عs']۶my||ѭ[wN?0h 4lؐ Sqdeer"B~E֭ +عsslѢE| WTKHH@ff&4iy !B:nnn 88iqm-x 7===L<&XKZZ9Eqq1fϞ ٳ`l܂9֮];9 sØ1[%f[_A^ 7n|P&]\\vɽEEEe*%%%c8~S#pssȑ#ѫWot֭ҁ[!!2222m===ܹc?Ξ111ӧ(XBEPe-B!I9-Trsjj4iV *((@HkƁ%Kbڴegg%F\\Gll,~׭W5U˫لeff0V1+ɪF}{+;~)IIIlls!?s"tu`ڴ)HHH`#>>^^^"KIIU92V\Damf͆ {#ezDCSS=ysqƷR dUj<_B!"mm&x% <^ ?CQQ<==瀚QFc]Lpq1p UO\\sc%Awo?#Yaf͚&&]ЦM ߿/Fyy J0PHOOB9Uf BTH={BUUݪU+ >q-ܿO`եd^ ϟ Xn87kYZ(o+A/>}@QuWT$%=o‚ ϧ@-B!jEEEHNNF~~>Qn*>}TTd_@~~>PN())HNNFqq1455E&*yyyHLLFFӡ YY*gJMMEVV!''iL~~>RSSuu* 4x@II>xyHN PWW}f077G~kMy\v G 55A~1v8fkLL43VLL CAANNyY[96Gƍ1B!׬Y3<}ǏV%<<YYYL{PTT4VRR Ƒ#1NZ }e ϟ Xiԩ)))*<|isvX_VM(.]*$ R{(EofQ\\yVe_kB!-8޽ ooo~@BB>TTTй 1b?ޓZPP78wbb)''M֭+z6i.|gӸv*>|DVB-hkkgOS[B+ʴ׮]} EEE8Ν;_ߧL@$4i1cb cYt ?SN$pLYi? :'Oʕˬ 05YfĤµ gӸ>?g { LLL0h XXXBF;aaapt<wwwV 55ucʔ)06n]kJ PRdb۶,E|^7PW\:  ǏG׮*ά,:u7o@ll,YSԫW055E>}ѵkW?Os>}0nxI5|3$%%!%%҂adԐSd1c;UzMiӦW7**o T^.]ѥKWQQHMMEnn.444 ---@CCҏB!TsܹF OOOmڴA}УGnݦŠU|a]]Ja>㸊Jihh6{QFFqꧪVn\ʀ e`T֫WX툈w̦s%K/EFFƧ|cB!\TYB!D0MMvj *K\\ZFV1w엚[n֭رc;N8vq:Xn->~˗/DTT$\\\ЪUk<|!ǰkN 5++ o޼7op#cN;;wiϝ;`1c^WPPX[Ǒ#pt<\x@hVnn.k=:t(Ν;e˖*}ׯuwXY֭*U#صkd<^;ؿ}<޽{gnV`ϟqI8;ʕhbV//kN7ol3ř@PQv {{{ݷo?l)DFϞӴ™:5 .%$$ <Ǹqcw8dddw. ˗ tqqMKJJB]]|UJHHH? ۷IP\\z{777{9~&1Rc?~)S& ґGNN󛖖&W7n{M\tr|~)(ǘ1cqA1a8ܻνs޽︄TUUξ}{uVrX|O nݺ|Iaii{maaa!p܁+&AQt ///cZZZ8|ؾRZ*gcrRQB!5fXxxxm(UXX_ߧ^;`ΜHI\-kb+[USS|BzU7EDD`ƌ(,,waŊL[NN˗gHHHDpp^߁8{F999&YbJܼyׯK !! ySֺH0}T@\\`o//޽Xx +Iߛ7o0nXV,V^˗ < ? 66OÆ %0J6Ӄ())1y</_Z7o :t(]sGTT BC'022;戻w+(a5+PK\\'O;"22ׯȑ~EEEX|)[^=?/뵲`|׈0k{%!!cǎCUURZTX%+\*}aÆW!BH-ӤI_G||} --S#xE#GbML+W.cu~zĴUU0h@}[`g>{,f̰9ׯ_e>UELL 3fXf3szܹs[Ÿ́z%ϟ@Rߎcjj mmm2VТ ׯ_GLLOX!!BHŚ7oӧѿGAÆ ѰaC;K,ٳ(y ˗-[bY|$$$ mmmt 7nBpp9=aU]:}4OJJ G>}snDDdpxd``3gcq @ǎ1edNS”&S;vvŪ ts˗9;;zy_wg%ɓhٲ"t.]/Dqq1x<̟?z?==k׮e[|V\:CcƌFp+qxf_zz:ӡaA5443ϛ7&L֭!//ꧯo}}cYf޽JN:iӦ!+yW{ ={"::Z㴵݇/^0mEEE:=z:tӘ 'OsCmQYM]]G:gOSh۶-. +7iӦ8~_4mڔ5VVV7Fƍ1p $''R={,ViӦcMS/31c:>y3cԩS{[oFШQ#bР#-\ulΜشi3+ɢ6G.]1a8#44f|P۩ gt o޼aǝ;QWWG~Ǐ1=zbǎ|Zeus/4pٲ"0g^]d]<7NEE.*xqu涄=ƷG#GaʔL۷"Κ5|Ksy`Ϟݬ={b%ǯBlRI߿/,,&w^oN| 9sЊ !B~\QQBCC쌅 [nWZl KKK̞=wٚ^n%));bɒ{>.^*u0Leڕ *ߟkٸ8HIOXvs#ddYژu֝B!gZB!hNfmKOOʕ+кuK7YPeFбc,]S=}gB-Ν;b07;m֬2pv>êU k͟>}رahS01&Ma+O[[{$Ȅ Ѧͷٌƍظ7o&Mb"Ȩ磪 {cm$,,&I06nsС=tuѭ[odvǏ`mrKIŋeKc_?3 :=addNXr=Ip Vbx > }EquA FĮX&MbGc%j %V4"ewr'{go۝ٍιՅ6mZӤIcʗweȐAbddܹaS!E58K빹UP'$$y<]>oܸ1Y:}FFF|վ^0.]+WL}<~\\?͐Y2eݾy檲ڋ[nZ3gfmmR֖]<..NU~}ިC?{;wʝ:u֫*ɓ't_pr] --#Gp>prmؘyPD yqx}LƏ@sH߾pqfV5+B!Df WՖTR㄄TcvZݻ_JQ+֭['̙*듺=?d]5<<"iܸ*o%- !bR!"]-ػwcccp[nv /%%___n޼Mݺuٱc^LLLϕTT)s)eKKK~7zꭳַ^VcFP||sU~vٵkc#Ν;DZcGÇUϛQ~+ʕ+q&\\\Tsi|}/g;!BExx8bѢL4R[6[M2߸0jHKN/yg#kc nu@1Bɍ/'NP-~l9Qb%<4/~_>XҥKu˔Y{ӅY;1((HiiݺИ1ciVPU!I5Q&O 11Nxx-Z%Kj+B011ÃuIdd$'N,iUem0e]O |П*ٳgѫWWMII᧟~T3$vbcϟ\Ǐ.tUT hFFF.]Z9~~~eIb-Z@||zM"#$22B/*dMٲN,Y K`snu&Ą{-)S6E1~..R!!!$$$711D8;`u lœ۷yhk EB|c2Zu:U ʊ05f255x(QpupX1k$88(Mkg%(^`ij4x<}em\]c&&&8878deEjbPgNcӳ;?Hk~$!Bw; ,ر_̺իcժU@B .`/FӦM䭷Sti}dթS'qXM111|wTR&MP'ovvv}*UYatqrr`k[TUNLo4n߾MLL=zk)V/be8 }A{4g{-"OfԨzOkjjBbƟΑӧ˖-sJӧ1ctjԨIƍ[.o՘EfkIHӌF RG]J*@Hܜ%KU7 @}K>]8zrN=cj:'^4v} 88HUvt4ĉ[n鬗XYXX0f}'޸qC_dd$ q̑#GqZALy$XKg܂2eQ!eUBf͚EݺuUM6^,QQQӋ>G.]ʱ~bbb(OOͫdp6o~7Vi׮\~[nѭ[W/_}=b:uJW9Vf+vvv9%|WOTɩu~^jj[/?ƍ>|3S gE2ngzB!9R B!/3 ͹%3%%WV3 :oIMG1pjfrFiiܷP)c&%BBҷ ٮpGEAPz}HN{7roYMqjf@-eP[01pB!yعsGv=To߾۷Xd15k֢m۶'Ǡ&###ΝС9qℲ?-- _^҃o@6mӧA'9V)^Y.]##\]G.RuSll\5kV<>|z>V=ohgΜӜ򃍍 666z͚%ɓ|sRti,YJ(+/gȐxzzRb%ʕ+Q\~gϲ{nN!%%?VD!=ɬ%B!B!x П={vm(S Ŋ֖… +0jEG*R˗`,Z'Oj$~e„6l8ovttd,]+Wx"%%E _:u*zk9msu\40..o%Y j#\%22B ss3--^-%B!ÇYdggMJJl XOkMMMꫯLx>~e̘l/ B!BښGƑ#9|؛ׯFXX(XYY䄓UVcȐ8::9qGy\Z5}VV*TB 0)S&1glZ6+X+3###UNj>|Æ e֭$%%e˖<kUV{*ׯ_k J\p0ݻGRJUeC+~OؘZjSVmFŀطo`}6>cUwJVrr2^URJe&9 ~~~oAA/TsN}K+srʕ+oX_9pRn֬fͦi&a3g~C iР3GA`-!B!xJ(AnЭ; XZZO.憱1;wsʾx,--}Ess zI=biii8H"ԩSW}<<<[vmj׮m\\ZeJ*1ng~vBBVVVS!/ɬ%BAˆ=!B!3JxxRREYy{0yT֮]իy‚~E ʏ>===U Hg4-Z[o 9x,?TRSSS4ir؟8|؛|RJUs̙3 ~NZ>MOOOVʛ7obQz~:U^74+ZTi޽{zÇTvl UN *D\\>>>zbŊp׮̜^N9siܸ陭U։'ۙJD̝;;wz --=4 B!B|yᘚʟ/#### B!^!YK!BiF9}?%KkB!/2ccc-Jdd$!!Nv|||tW\]]@GO;;;숈ȗ>7oA_~h ?c>>>J裏>V-Ļb\ey43p5%P 3=eU^=VݻǼysuwߪ^yzzj9;2mZ.]d?u#=ZU^x1gϞscƌU̟?SN܏WYY~|{yo߮iV)GFF2~8SSS5E>1jH򧟎㭷|W,u{f9e!ZB!B!B!$B!B!Pf qdd$}4u}}/Ӽy3nݺwSX799#GVY&{ٳeذU4i>r+/ЪU ٭ 7Ю])ULE`mmׯ_GmXXͥ^XMԯ__)_xN:(ɲڱ_zxWbqqqaOrZZzdI6--g2v5kox2`@?87(Fڧ30q$Uy)L2322{J911CѩSGRSS[65ji߾-fb}=bɒZY+WÕrJJ ;wbo~>ɓ'5j$iZ:hܸ*oVkܹOd5e-nos:>xpER8p@c9s{.Ոq󚚚`"UiӦrC^KTw!B!B!B$B!/i!B>h k֬Qvׯǐ!CqwwښK.qlW#FdΜ?t8v(^f͚S\9]puuĔP|}/z_T- uܹ;wPL7n ...8;Lhh(Ofu ͛7`iiŋڵ @z&>}zcllL x1~~~J,XH>,G9]k~^*Wܹ0p 7oF˖bŊ8;;Ox._ɓ'9{kFFFjB ;w͚5mvxzzȅ 8u$۷o'55;;;6bm=xƏ+W'%%1{lذ[PbE*V >$ sqa%N%4֭[ٺu+...4j&θR3IIÆ U~:v--Z`Y`oqԭIjj*'Np?U>/F9/Æ g?sر4i;vJ*˙39w.=8N:ٳw/3g IIIL: Vnnnqm?vEll,ZISv 11?#k֬iӦTNŊ)\0qy&44@$:駟i֬ח-Ѯ]{<==qvvŋ>}m۶L"EhѢ7okX`.III={֭[Ѽy VJŊqqq!>>!;2OԩS9~'={iӦ(e{{{,XNNN̚;}C CkDnܸѣGrvJnŊʗHJtOU˗r>baaQ3B!B!$B!B!fV}*((ɓg1bW`"##ٸo*UիRHfjZ*K,4n Q&7nblA,EgS~% p2j׮=63ڵ]vwwwV^C^=,B!!!,Y%Kgonn9طoo.(FFF,Y)S&>+㯿V8~~~/'̝;?8OArr\x/ȑꫯuٙoc?V~Oҳ-_'>Ag܂͛aР+Wpʕ\kbbªU/Xt?((Uxnwd1 cٲ,[4[}SSS~}6ϟ7h0-55{:h0::*`FFF\J*cmØ?|h/_y$X%øq*e*Əd} zl۶ٳW;v`-!BիW0_}5jt)BZB!B!BWE=O&qKk=KK[ڶ{wOu0ҡC]wʖM#( I:biYuR06lpXc&&w"ii)RGPn/ur%{'qj֭ 8aoF׮`ٳy%#{8{5ѯ_[wjec㈻{{TqZs~BC} -D#]ϷQG͋}&cm;h1;̚s*ާKKvp-uZ+̺u6HB@DDn5 #::Pg%aeYxf89wСk6D\eoZ\ uy*(YRq2FGs ԧoߣS IL ʗo{nnzn̘W#Ge˖:4i2>>>\:o… 2d^_t%k(22R^511޾g$B!^eTZM)紒?N͛y'kB!/#ɬ%B!B!xԨќf p쁒@KZi6QƮ]z>Yp,G p%!Gќf;!c2 pFN@Ebbڳw9{u&iTVJj!8[Ǎ8~IݻG:Zm=ZVYKU%KlL>@P:Vf_!GK*Ua`+G#Ё,Z$"k(%݌)ƚ3mZ^"u N~FH?dדks29c 8 3Pe3MW=p7 X̐3t~>%>g pL^ ` oZ$cs,Oω"?kΉكVdYRu;Օ҂ g*<^9ÎsQQ-X$@3?|BacɍfܸQ7ƌI%!!0x w7ިܜkrL^,%kr1*UıcE/ !B|)|011&_B!^FYK!B!B1zY xӀ 3bKz0I|IE^شɚQvu1=6d2f@]gl3-cO2gUndlT]EQ3whJۋȐ2[nTe;#?..[n&=\Q~+^܁5g|IB!BN5}nAOC!#B!B!B& =Sن)! "Ñ#ǔ -ZB!B!BO2k !B!B!..Dd* =sB`-!2TP B!B!J2k !B!B!t,8i%0N!xHB!B!B!ɬ%B!B!X9PpR% )KB`-  ߟX1;%E5III!$$022X1;(QywJJ $**ŋSyYy&~~~bcc+UV1r !!DEES=o՘B 8JDD!!q)5jԤH"==E@@n"<III/[yʕ+_S{x"':!EPTijԨB!B!/.ɬ%B!B!xXZWA OJAmL)W[J gzHN"--1_43+ ,Ē},_|]Y[ ^IXXeٲ<|Pk=5kN-x9ym~w֮]Ǐsvvymێ-Z4~gVZɽ{=_JƏ@ 7?QN-ܹs&OBZZ˖-eE\v-[; ΠAs\ɓ'ef|yy&?۷o#6V}AW$99=¿KL4YU|X<|P2y&=z;ti3ՋСٳ={h<&&&ԬY!Cҵk7LM7lXό S߹sڵkjmh֭8 sMu\]ѷo_>pZz$''طoRJ̟[[[mϜ99v8?ִnݚ>@JqL6U)tà;v#G(A3jhB!B!ɬ%B!DCr _v 懰0 ܫWoZl|wܸKai>B!^N%J?,^.n7nAƆ*URd D`` AAA9hŨQ#s網TRrl٘c6GRjvZsyUqzƍ ǜqP (TeʔDEEmllxg*mgΜ2UV5?`JQ7P^mBܚ={,Yyƌ~ g2?!B!4fIf-!Blj'n׮}H!B*rssͭZW ͳ߯ڟϒ;m۶>˞CgMÇW&M￯1SZZ>>Yv*"X>#lmm?H]4{$''>֭[͛=g#((sssK{}UM`` ÇcʾI&ѭ;=N~ڱ_"##hܸ ͧDIII_O?}11UV?)QW\aƌiL6#[޽(z;vP 7775<ç%%%EץKN} d؏9pk={`jjJohvڭ܃ppp`Ϟ}QHNNNl޼ }عsc'ZӝkשxXf9sȖ!'!!!+W{3t臔/ U6 :uTR޴iY֬Y*ֺ۶m1Rc9 AoU+Vלr 6T 222b,ZDcm`ذʾ۷o1wg6]6J1ӧ`ڴZWpRn۶GU֪@-H?^#F`]? z{z@`Ŋc{{{:tW;!B!B!@ YK!B!B!B!t{탵Ν;*lJڂΝ;<611YyS'QjUϛ'y{2hfoooLk&M0r(̲eK rʹ6رCwۣGU {WV 4{>J966ݻwP֭%00P)2Ν;Ȉo"ʾ ?-[Э[W%‚E̲aJt̚;9UFMF~yߟ[j;`@x7nZ= lݻOeBz]6M4Ӗ5uԩS8?M6Jxx_۸oe;/kP\z}BJJjg:B!ċD2k !B!B!B!DU3!1ڹsiGlC)SFysї ֫=]vSܼySロP,Yf͚)c5k(-T+?ᡔ]?RnˠlpuzʧOʷ*55Vٱc^dP4iؖTRE):}P={_Z`۶mJQ7)_My !2e2+V,^B! G2k !B!B!B!D*V3ݻOϺ 6֭[SB_ ѣG,]cǎ2lp7ocݐ RJ)A6qq"}q5sTXQg;WWWCf޽e޽@ׯcQq1ܮ][lmmK9rk׮BLL qqn<~)?~\U64+V133#)) @=o 8p۶mUլY5kWiJ :+@@̝;G9˖-~Xw͚5`~4'!BtAchLppj_b(RH׽{8AAAŊjjxyyQD<Ⱦ}{9{,XZZRLիW#ի޽ )]4nnnk>iNFBB{F;Ӽy )x"<gΜ! x-Ŋ{S2!B!"^_i`&Nɉzˋ6mڪ.jS7Se;v *T^7x7iժ΋ڸ]PB㔔\ TZ_W'ʖu6hC= `͚Z֭[ ݻ^cܹӧ*KÇ nWϟGZZ3WӾ߳ *ӥO^\rE)lْ%K>'9#::Z)SvM='~ѣG.\8X˻ѤI< 2eƹXLylo_zB?ʍFFF,_BySWWvWMZyɿ!BdB!īlʢ8} VbƌiZvttdt1mʌӹq:{L`ݻw3hЀl###S־.]Nǎٳ2/FV&N?S)7\'&WʇCkp[s&ʕHNrmڴV~nܸ 7n"))oeKQշsVll,cʾ ϕyWϟcǎ޽K.XȈ6m2aUnXɓ'yW~}?q Ǝq[ZZү_&MA}l\7oޜu6ȌY||HH[6m*.:1]t嫯U9Yj`^^^LZ2kol޼ICE᭷3uT\]5g!B!B<k4mڔ+W駟Vwdٴi#F͚ݻ7 DkȈI&fsMn޼abbB ߿?ݺcм "04XLu29'./-,Y tyj~|͚5%KҬYNKK/`_ZڳY ͚5sw{'н{wl?Z'!!qQ6ndEEEѽ;?N6III|XVKgT6n]vtr7Ѿ}_f?iVkֶm[0ދp0`k !B! ZڴiK YOvɓ'𜖖8Vb֬YT^CkeVZ;vh\(%%cǎrQVZ/ U ;Te}H_yW ւZ>>󻫔{sn WjҰ:tVbk[EmU{ZYHLLTֶ`A4lǀ ]vfӦ-ziN3B<g|4!B<[o? z !Ba(ɬ%B?|j.\jժSjUv*'NPe`ˋUoll,;v kݽ2*T 11˗/sIet҉={jD׮]8qℒ5܂iKEU7nŋ=}||h֬v~~w϶[kVjj*GQoX|}}ҥ533jjxzzRtiΟ?ӧJ6lXORR"/wViii|h%PؘSZ5)B@@W^ջ_N=U|1)SjTti*UNNe.L@?wۛ Ǐ3bp_~lZ+V~ԪUN>ɓ'v߇.OekkK ښ3gp\|#zQ۴4Fje=7eKJJWx{VF*UpssȈ+W|9uYif4hq>ܧ_T޾8۷  ((K.v9B7]ٲei۶eʔxDGG@@@ Ν%((H7Q!B!HV&5QFÇ9r0ܾ}KUҥك#GaooOGGGƏb"Hշ{ٳ͈cŜ *GT)S]֋}c |ݷJҒuH|BC ~+VwXWD@E ("*V"bAc {,QcXI]coQlXa (E@"cd`fޙ{mfs|[B?nA[~<<СC@tjjy:l;w.XAcѢ۷ozh>|XߪTZhѧ˺JX1lEDDD_`,^лw,Zzzz>n޼ ŋeK&M(JԪZ-Z }߾}I&b߾2*r1gΜ%`E]\ ::eyiѢ%TUUv&keu̅ >N{"&&Fh헐#LLLcp={sСChb# >+v횐˗ollz  (>>sM,thY0=}}^o->VԠ5v܉  D}kN9s&ߋ^|Elt K.qoJDZƘ7oucD̜96vu/CGG'G׋<\޽]vLgZd1޿ d4hp u\| vʵ)d-ѣG/-Xqqq̘1V4 wBN2NNyzۄJ=~~~0ab!<|V]ĸ4:ӧOѱ ˙@9}VhϚ5[RxwO=1Ν;ez)xzn&v ٵk0IWW2L޹s7a!XwȨx];@FU?"""*YpЎƎs@fͅv@s:pħ ũS'qyz QQQPUUԩۣMTeSܸq+ƒ{ؽ{nݺ0hhhRJi'''[RDDDDV"""ES=0~}*U={ʪp-ԩܹ͑ؕsuHY&Ξrb:ObM2طoR群([,׷y{{+5w>س'c'y QeN~e7 @qa諢3 LE>{,OnKvSL4YnEgn81cF khhsp@Q:zJ.g?ccc,] ¹gϞE6m-Z0YF8qU^/-xn֬9v-ycժӃ:J>NV4x}~6<+Ƹp"ʗ//w~m!{y&IM4Ǵl -[DDDDDDD$1)SSSxxLѣDd/i5j`ɒwn'C?waaaS >iW~hu;>V:zh0)'eW\eƄ qa"00q5l޼ 8e8st Ν;a1Ƽ}ƹukYׯ_G`` >}K,FF66CIkZDDD5{}ttt2Th'%%Wa2 &je1M@g.U ̔/ !C\Pre ^vMY077ܹ7o,6} ZY4lPhamɪJ cժ1bS|y8pXHJjEڧNT֭[uɳYŊ1i$b"jϚ5;1-_vYU^]aV\)gUV+Ljܸq"==ɽ_ݏfG#?bcc"˜0YKIu~֭?$ܹڷ O$KKK[5@>B[KK k)mڴgΜFd7f4ЫW<狉%őuU6)(Z+/kF B!!!J$QUUҥ0bHa[dt-׋+ЪxJY :Lx;߿>>> uuB/00PԳg/ԭXʖ-A 帆\iii|R>~~ ֆJEhNlo IDAT8j=xp_TmVZ[GIgO:|q0i$b_~3g~#[hҤ'_TJ.-l޺uSaCcƌt^zDRߧOBYGe˖A&RVYCS:k۷!**JhwJ>} ={{p N:\ryΕYgQtJŜdhdd$ހ3c90aW/^`1qrϟ?q"mذ ;|ԩʕ+'غsN$%%Nr:9 4Ws[nŢEϓ5Y+%%>>W%D5j<=Ç2N[#GJ>~jj*\:Uq1c&po2*W ;vD-sZz}8)6MRb^OL2VSQQ0L"DDDTt2Th/[TH266+j)))9QVϞ0o|6o#F/?#>>0~hѢ9+iRlݺ/~\W[[6mA۶+֨QZu'OCW%"""(Jbe-"""d j*RbEQ;Xv7o^MMM=.ߌ {-)>ehiiQƸr2*eʺ>#*ݻw"66zzzY|<VZ=maa!9vXXXZxjժ8mmm=͏ڷ7tҟ4wAz-ΝţGK~nbcc.Jrƒ2IꗽZqǨ |EOOO=7o -,,~3=*<<<1`ƌظq#z֭[QJ/*=`3fϞHNN?|˗{hݺ \p ={LXKK{=_ sr]4,, C &Բe$?\:uj-NJP= {BbbM^bȗB&L8AJѩSG ۾!QǪU+!Zn#izC[[[hoڴWFJJJ:ָZ?ĉ dU.'yxLE׮]r<ҥIƘ0axʤI믳v||<Ν;'Eo|||`k%޽þ}{-Z4C@s1:99묟!C&o"""|ٳ[tsAv~ZY Yf 혘lذP̏T,^Pm׮=D_#"""*U5`e-"""INPȞћ7U}酆 66 ac٠qFhܸ4i&Mֶ16m!>4;W IIIrжwqf͚2νe]'xծ][aXsTRWd*jKy& fs֭JLVZZVww7"jt隣rY` ,[ݺu9:u_#Q1cX8ϟۢJsk֬A` IsQZ@ƪ1G#GArPJ:::dzg8(1~}+L!w>`mؾ} QJ ÇsUONuUֽ{w<͛7ef>|66`mm`ܼyO>ض9scysrrƍoNZVo~ї+W: &M͘1 [lF`ff ܻw!)) @F2[DD¤lmmѮ]{9s@FrϑU퇀,Z׫Wff水%ʔ)o"00UߪV&)֢6v4551e/HOOGBBtƦMЩSgQ_} غuz7o"QQQ?'̙35jԀ%*U2E||<"##q] ?KĘ_gϞdM:FMM 'N 9:"""kVs}@˼&\o ŋ[n >]hܓ'OdTیwWO쯑?ٓx޽5W8wމM4Z#55Fb}9CO,ʖCr堮1ŋ9TyDp(Y2)011+@zW?ڵШQ#̙3;G]RR"n޼7ob07iѳgOQSS`…X.]ĥK1}Ν-W#""""""1Y+hDGG۹Vv)ipڵkkڴ)֯EWWWdž ЧO/#** Op D8R4h`ڵkѣG9\..CqXnmsgTl>H{K'!!(2};w:.ԯ__vUUU1{ɓ'q ?/s |~;wn^ c40} : 'Nljq cGec"""""""*,_}?@Jp7^g:u={RX-iT47n߾JWM6Ï?vy|NLMMq쿘>}-540p@̘1eʔ)(43fLmj ZeL l۶{̙3.1t|kiiq!>|!!! EXXO={֮]BDDT/_;vB.]:_qBSScƌFjj*1bp$%%o~&&&8~$;+۷s]eXMM M6#X-/֨^:d|f $""˗Df͚)5fZݻJTݻw2]EDDD_-V""""?}}BӦMERJY">~NEPSSCjj*?ޢ7ozzzvppRLr͛7#)) ?'$$$`ĈE}JꫢG.pt삨(\zAAAFJJ עjժ([d|@ss|GDDD_/_Ďۑp3gޔ.&U!+ٳiMLLZ!Uلz"""/ +k_ŊFdTL cccܾ}[co  +++|]Q-kk\e5444wouo SSNABBYYݹs_ڮ$%jUXQx3/BB%kUX1ǸuJ+W~w)dakk [[[L8 Xnp˖>ԪU+2hii^H x ͛7 V""@&Y䀈0U\Y W:6laͩUd'f>G:::(҂ lll;IjժU˗cBªe0dKsWXFD_OիWrĤ#|Ĉs)Szz:޾}+MMMl"""/ +k埭mS_)޽S(KHf~Dmo \2RRRd,~+oQ#;;,ZhӦ-=z$ڟ ׿ݻ={I9..ϟ\BT\Y؂Ҷm[ڵ8!>>0{,$$$`䟋4 gURRR -Y^B֛7o.9IYYY׾}IcS5mTԾsNW̝;Jʕ7{NۨR*<=#11G$&&hܸqMDD_>MMMի }ŜZػ00Y3,cffN:cDD%ԩSn:DE*/DTѫQ2SAHMM->E||&eW54w'"""/ S}Zj 555\َ;q >EZZ¤{Eԩ} | #*"F|2 qw'A[[;Ϙ7m(ԬY\z-[aϞ}߿޽{Xh!0c"%>'iܖ-d4nǏÆ 1uG"""pAIhذ軳y&uuQ*obEѯw?p͛?tҹ[IIQڶmSNBBBp/q/^رd2 qU+d,6~ط@_$Q ~&&3%""e.]Gvp9Q=QaPUUE:u`mm]b/QQ||biB[__1"kڂIDmmmѠu1ESPLaU۠ +ןKRӅ{FMM ݻwǎ; Ɋ;""*aƏC"))#bϞݨ^:%ڵkxݟ5Y <#F*7lJ 4ib+TԺv횰iӦ^u .@СCG0|0DDDjժmyԠ5<^z ֭[,Y{]jJ:::rGGGu"##%ݻ}{9FOҒ;رXzcQd->}:W>|8Ҕ:Χ3f,K8|L恩S=L^t=\~?\<0w\;ڍ5ݻ+W\ryΗ={mUUU4lh#)""*T~rO&"""R_ƍ0h@89GХ#Sz( fffݻw)‘+5VDDDD_ET߿{-Pſ-X0vv-q1USRRp=,];W\Qx )SFhLL4wq%OnI9ZOȲBٲeS0ٵk^T18˗/Chh=))>>WgΜcժ%buHnnc$HGGB;00}􂯯_bb".\6mpY9fΜǏC]@<###rpq뒗VлwMu={v#..N؞WcsJB ) nMk׮Afطo(0SZZ=zիWsh'OÇx,\0`?pq<}cǎ֭[mUVCZL恺u` u}Afoo/ae- t>+lO>-9ZKZ$?&wwǐ!.E}qZlŋ 7n|Q1wʪ/_.ЈJ4kkk`(WVZƽ{Dߗ)Sx&gTaС#-&M*c===XZZB]]oc Z5/8uPeLKK fffT޽{0|R4F__v킚9o^:adT ׯEcUO &""""""KOO? ϻB IDATZld-Rʞ={r:g?d-y0n܏puu-P>KʕnqAD(11޽O[[5ne˖ELL `=puu7+ F˖-}6j*xzsӧOq<}ܰsR!Q >|S&M@``h_dddQvm X>|wvvvضmh}IWӃ`pw>.]G.STT9={ŋ&%%a dvl333\?4N8ߛ`#0{̙3Kt7o1+lÕ+*V7Pc-[Aqua{ZZ>|(ꫢwqpwӧO+uO쌚5kcѾXܼySX U9>>r`dd8:vC0cL5GApp0BCCu(~*:O׮Bm\Ǩa!(L 9_Æ}kź,--ѵk73zzznܸ1""Rq=sLʕqQ\cݔA]vE1wl:to߾dh۶AHƫR9*ʔUNq~m_~7GT`1*U1+ȑcزe36mژ#JMM mڴŘ1cńׯ/|rI[DEEQQFx2ڵW\UUUZlG.h׮J.***rZQbe-"""""":޽H$E0yBXaߪUVn)FFFXr&MCEo!::Ԅ QR%ۣcN(W JǎQbEƘȑc8p`?vޅ[n!::P:u*bccEmӂ~DDDDDae-""""""""""""ZDDDDDDElԨQJW>ի/oժDF278}ZZZ u:tTz z^z55}>5$""""/ +kTL""""""Cpp0```5jܼCUfܡ&allJ*T/w{x&޽CreabbzCMM#"""""""""@UUEDDDDDDDDDDDD}YJ ڵkef}Va?nm۶E}r3 9Oڵ M[mVlRl۶_αN:|{ƍHIIɱ_OO]0q\ׯի/C6mØ9sО4i2 pVj""""""""""*<ced۶m&OkBU޽ 5Y`ߑ/_Ė-qEm{]r܁&juQaVhh(k׮:oll,o߆{b!ru-M@jj*`ӦJ'kmܸAxe2:v""""""""""*Jr .LpuL86 ԯ_xÇ<ܿ,Y,ڦ:ubEChiFpp0IJرc\fdd x"GUVvIc$wD- *nܹsG0sY*PSSCZQfMPKzϞ=˗#+HKDDDDDDDDDDEׯ_GppsDDDDDDDDDDaQ\D-555̜9 [)==>>WkN=zT|߿ǸqB[[[/A=JJ Ν;ݻwYL4d2e*| 劫WfΜ^zC&I>NA:~vvXv سg7&L;wk3WNNMc~iqqq4h(QVZXh17o!ꛐe˖beBٳиq4n8CRSSuߘ4i_^!.Qae-"/ڵqa Z2Y +K_ׯ_#0О1c&F-7Q X9YX|X|FEnN}^ 9@SSSL{ )))2siB{۶.'%%aBZjhٲU㈈hI'k۵k/y+___ᱚZns*2cLԭ[W~555?AR(hPWWWcƌ)))زeSQ{{{=:>F c 92xxzm[l9%ՅNa-[6cABBvڍ})*JQ.\gx$Uˤg\=H=x<}5A5QvMJDDDDDEH>Y+!!1[e\z_V)~[Fr %~8455{B۸|۵kgaЁJ`мy tԹHcT;w&NT1=gٖ-E-Z,3DO<)߀ΐ>&[zyy9w>2LKDDD 66!""""ae-""""""""""""O֪Y`|$%}ZrU9ΝS(+++8822cJEE?\R_}}}KhGFF߿BSZjDrcooի 7RT]-j/EzOQ\|2a[ڵqiԩS'+<ԄmSׇEGn݄ӧpϟKBKח}6oބ c„={VqCDDDDՐz=TqP,--aff/_Μ9Fl::t%HekeʔA\\\ WQhӦ-*UT WZMr_###C_R )񅥥eA/5j̻Sf |ssB,k|Dzq ۷o1d`QYVv yݻwM#+C͚53gذwUn&MTLDDDDu:s N< y3f,戈?LKKu?> ""@Liii^300޾}ەW^ mMMMBȨ"d2 } \Epp0޿CCCXY5@T@__fffhݺ U~ORWWҥѧ N!!!1cf̘SSS4n͛7GNallY8qɓ'իWGMвeKo˗W.]Zxeղ/p>ɧ>"K/_:::ErOGx葰OXOd2Is!%%Eh߹ kk+!Q*==]X޶o ^ɮI&[uߘ0abIIIرcЮ^:7o!Q˭Vօd:s c{LL 6lpܦM[Э[7=@׮]câ86tQ@JD @TX0Ƃ(XbI,Gc博( V,{ ">' .Kw]\>g<3W#FbÙ3gG73gݻ&GYf>I& 楥Saذ.  ő#G>K`ooYfu6ODDDDDDDD52ڶm[`ddh۷&MDÆ6m,˖-V={۷aQ[6w{z޹W\DT)33b''sHҐ&/+k٢B-]]],XPB-B+ g5pDDD ""BTT"66qqqHMMy{^GFFSb=W^ }zu""/[-PNasg^+))a qKf`i2dh!˂l->qmĒ%ODDDDDDDD;k}NNͱu;k׮p-;;wƝ;իWA2s؊@ܿ/_Lff&.]K." `+VXss?BMp?rjjjqYvKUp7SKfX]]MFdf͜pe@bb"z`Ϟ7P(GZD4X"Ζj߾HIIcoD1?QW@5""""""""""*]EDy355ڵkk׮r{(L #ׁOر@a |K,FVV5ФI4iիWǝ;wpu◁G1nX[˗/ÂELLL`o+++Ǹ{.vahjjBMM 9I[{s/w 6""""""""""*>v""988`?Cz jii sYQbmD Vh~[,Yi IDATcǎϕ#ׯ_ٳg ;w@nYf޳gbXCCƍ㡮!uSw. m۶C&M:"""""""" dCϞ=ѳgO@DD|qcN|||8\s`eԩ =[ߣbŊ Ň 6-Qx>Ϟ=U86EX"j֬Ycc9r@·Ϟ=Eүey-6{>|nݺb!sߪU¢^?~\jCfpU@@VDdeeA-JmDDDT>cɒ8y"""?8}4-Z'Xly~䕙 ?\B B1n1kly˗/CxxԼן҂'~uܼQQQ={+#.\ʕ+-RڥK͛77PXÇ#Ǎ/(**J;w<Ԯ]nwyN98r0/^u~G۶mq(GIIIRMfVff&|d$${?<<oƾ}{1ctL#F-@nX|VX.t(44.cÆ?йsYĻ(4k :T(rk͟@s&5559zZ͛µk/^ܹsR7ݻGtgB4ib۶ Qn]υkSV$v h$%%+gǎ߈|b+C[تUuqqqRoVP kgg/VZn-^aӦ?ѻwa[xx8+={&w6m;L߿С ###q)">aR ""'447UNW\7|$''c~4inqY1))) j̙3rc?~oqS%sM:ԩS ŗǏoxbcc1p`\I;xx ɓj啔_~ $ ,YP+С=?QYpI~ptt;w@Z%k%%%xzVx::: 8fh|Dl"VWSX88}MYruuYʃC \}۶{=z􄪪:ucr <}:vFn) ,k׮ņ ˭w:nas?'Gq 4b[tt4u늽{\m@чf̈́ Jmݺuôix*燴T} ׿ԎODDDK˚ԩWի߿GXXlw޾}Cq\0 HSQ&Mвe+ԭ[x& aa/C HeIOOG>p vnuEZ0СC 7..=ztijjW^ptl[ 7p~RSS9EƍS{_,Yps[[[Ԯ]qYJ$7.](.]ؼ/..`oooDO4h? áC`iY5'O*V//BkB`ʕ/AÆ}/B~~ҥ̿_M[BYY .w}/sttĈ#1k 8ud΃Scg-""gO7̜9CXxWfDLJ *ԱUVVPcCMMMs$0]6^|8[x5mf ɹ[!""""""""Zomhذ> ((Hj2d0„mZfjԫWcr{V-RʵhBL233\v -lׯ?dm߾˗0t޾}/ hݺ%]pAsAeԘ-[`J0k2ytڴ鰶>sڵS>Xn-6mi*DII /cmٳV\%*:~]-11۶ͭ'ڷo9d~ffhu~CDDD_FaFjZ5kֈ w.!'OW+++c߾=z)))C>}_ņ wOТڶm+ԕ+Wpa'N r311#m-R˒eM9P#kkk̛7_mRcK˗/valdd{+D]-Mf%DW/^$_GJJJ>|DaNL} WⲳFFFɵP255+r|VVDz%vhѤ#6mMY/'BY;k9+>|BժUaii XXԀ63={VTtӰa#L6]j>`۶lCCCXZZ5jԀbccC\rY!kB9ѣBCCp nи#7nɓ'j԰įΓWEE3g¨Q? ..@MU`P"矛+L0˗/#lljɓǸ~:"""Dt,"J*nn=ӵ %%FiӦ^=[BSS! ӧOD+7s̅&.yX4)) {aǎprj.577 (EGGcذ1uTؠVZ066FJJ p|?aaaԩSk׆SrG]]ӦMÀo߾۷e>n (=֯_/WhݺMnh`jj*wmmmL8 Çmڴ #F,|JҼy NjӧO7oސ_?тk׮Wklڵ1|p_@=B:uDqaaa8{0655g'LHd!5v""ܼ~:>/,X( >/t+A؆6mE-Zɓ5='$#!!WVVJ=4_C (wg-2qQaS/0QX^k`ƌ%̙ uŊ.ѣ'-Z$XZ,yB+%+))~BV||| 7^z 7^2W6m:7vq;995ұ9133g0s ޽K5WW׀f͚].][[[,^ WVVFp"XZZj2eYjULFY. q9l߾k׮ƳgdƪEpssCz>, g t0?Ekmms炱gXnݻ/za7/\Zl,s|1w;upN:X5kQ^Cb7o`}瀯^% 00Pm׮ }~)W\Y4{}2Bϋꋵ3>>c#::J膕 333zUV5CFv|{!&&99UUUajj333XXF K O¯7iRR%,_sӧT\5mm" O\~ wCBB<*USSSjBdOfͤZXX`ǎ]¹sg$Ʀ6ZM-[*lr,[\TTTx˗/#>>_P14hPZw_{1b]TTT˗/#** _|5!!!{tC*U޽Ν;wܾ}iii FǏ5j(+[X}>vԬP?.#ӳT<ΝwpJ"IۄJu6ܯsW[[III *ٳO|1^7c}:ur ~BֶmDUc-*L|Ŋ#zH4sGX+7555888p(h֬YhkkgϞ%WMM -ZD-K<,&&&0`';^IQR/!zQj}E~CDDD_SNb„񈎖! >H/XHE"ϟ79Oޒ);ONN*ϒ_?޽{vUbrWD *(D.""""ZDD_uq nUTT9/\z=Bڵe!Zj;c.5m}TT ޽ܹsH""""""""""XEDTJ޾}+ Q#;4mڴ gDDDDc 5`mm*UJ*\B4)))I9%m)HFtyWΛS҅ly7ՅӋ=7""""ZDD_)**`ٲ½>?tYQE{4#((Ϟ=u~~NNNbTUUѪUk?~ ݻwo_"͏cQ)~۷=}ڷo=="ϓXEDT  IJeKeMt֭ gDDDD''L6!Wre8,E(Ni#"" d8>KS( [oB֛7opX^`ffV}64iD1c &&Fֱ7YJj3ʕrڵ`nnМiFm}XEDT/_۷o#=]P<?g̙3k%%%Uh/Bg0''8oIkU.~ZyW.5B:nٳgOyAw1W7իW< -SY+++΄ٹ-;w={Mjj=FQN;NnR%yyykժUCLL ƎAhh( CCC9_?~I&K>Xhܼ7nB턅.]-0nxzu|mFFF 8|0N:t d`a߾%Z.ќT~ܼy/_Cvf؍7pqE.]?񌈈<^[ZքBݹsW8;;;TT ޽\z޽CJ ?\ճE*U9$%%)Ju{ B LhDj\^qqqRW_tsph * 55p-5Ɨ.]TX+EDDDDD;k}90z,^ H/w^`N//o̘!.\266Fǎk .+cŊ尰Yuܿɢxeee^Yرc'<<!**ejj*/^ŋACCVVVCrr2/!kb\vv6 Dsj՚Z_^F IDAT;w(ќw܃Y, 0q$xyyTPVV^K$i ;YJXUUUWz*L:EYY| vy+V,/)vޖ-[ E^% `+QΟ?[n cCCCԩSGjl[ آi|R&ׯ_+\pmر@9+!oFֺu+xu0``{NPDDDDD ;k}YƌAjj*6n܀;_0oޯB9 9{vƐ!K@XX/e˖ͭB6l3?~p裴4<|PJJJhܸq!΄RrKhI-װazJD~هo޽wC"""rԭ[WxYj% hԨ1$e%oذ/^;!CD!-M~![VV.]*-hAƍ /=zT`ܧfll,*gh`|U `oo/_|K]...r 98{7@^w9:uVkgytCx{co`k[#̡Gnغ5߿Çҥ¶UbС27Dض-zFfͤoٲ2[9E_ }pf͚K8zk٫/^ /HpAܹv-50>||},5> sR౉ʚED7wk¸COUre̚5ӧ˗a`dd++kTPX6l5kP1MYBDDDDDDDDU`az i׮ڵkW """,K,Fll,ԩԩ#~yPjUx;wbV;w@Najjܿaa/  3ճEZϟg8p`?РACZ| ܽ{ϟ?z m۶m}ԪU o& y<ӧOFj2?^u tQFѱ lm\zyRjaGzzzpww֭[ >>cѪUkԪU x: kg;5T\]]abb(9].^AU?{+++֭GN@X :[n kkkhkk#%%}6.\ǐ,ZwET,[qƣM6D"͛7qX 6o @ O>Ajjj^/""""ZDD$222WGEE5aiYT LLLJ-?׀ZDDDDDDH*Ulrxx ݺu }jjjHOOcdd__895U8+W0`@?Ç6j(_6;o!v՞?Ǐ cee"wXf-pM@zz:. KdeeZZZ?Ѯ]"SYZDD_7$ 6m u1'""""""""/OĹvʕシZ-[©SAVZSzu=z }=/N:r󚘘ȑ1b$5J,KG6m}ť` H(BGG/co^>}?ׯTV =z􄱱qy05{ӧEy`jj]УGOȑ8~8.]AUUzzzptlpwwFb1y,-k…؊ÇHNNѢEKxxx ɓ.(I& ϫif}+W^@[ǎť#>}(DEE"66VTgm]Kjjj8qFsq㑚 UUUh]veMIGG7 ؊xڵ_ݻP7a'""""TY0yrExQ2TƳg銾 <(Wj 3J1{f4ʀ.~e0NKK[|4~"ƍǸq^bLMM3>>cK,ÇPܕo HҠ&K Q< մiS4mڴ)))666E:.s0<= Ҡ&3Ζ (8Qicg-"/իx@_9ҀgrܺU\޸qe=""""""""rZDDDDDD@q CYYTWZy555K)EDDDDDDDDDDDD$γEDT$&{ѣG$P1\\:ںVwU޽K=XYD"""""""""ZDDDDDDDDDDDDDrї{wIr"ݴbo+DDDDDDDDDi^vbZDDDDDD,$D..:e= /U&N|[ """ZYYYp&DDDDDDDDDDDDTޱXef*!9Y}DT4o+""""4Y%%"""""""""""""bg-""""""""""""RXQ~(iWN^;kQnXK >GTҴ}2cb-"""""RVfBBޔ4u3!"""""""""""N2DDDDDDDDDDDDDZDDDDDDDDDDDD2묥/(CYYfCDDť^S """""""""*EDDDDDDDDDDDD*bf͚a֭7Q`g-""""""""""""ROJYJ`oo_V'"Tzu4o޼ADDDDDDDDDT*YUf`ذa033C`` RSSr*DDT***GϞ=XVZ*T\\\p-dfftѨQ#hkkTJ,kQPPtu\3{BBB[2 їL>.w7+>UU!"dΜ_?GGrC@@OaܣGO(++ገlFDDDDDDDDDDDDTYb-"""""/Pnme=reɒŢ͛Rs!5yfYO\QRR(7ղ_JJ 1KIyST5JOO/?EDDDDDDDDDDDD`K3gN(׌H,"""""""r) dkQbg-"/OLL n߾pDDիDT\UV.j׮MA]]=߾x =]"lOK˗2ill 555ss6"##hjjjժ-ZаsH HIIJJ]ՅVqY<|^ 3h۶lll ADDDDDDDDbzus c}}2 Q#>?O>ի߻.7bŊhժ5f͚:u7o :7_`'3߹sak[_Ν ĉ'p=QRRkgL< 4w-|mW5n s%K~С|?,, C"##Cf\ 1klkNf їZDDDDDDDf͚Yfe= """"rKVVVV' Ǚ3gп;233=N8JҘ1#bxAAfZջT$Ν;0aH$i޿}Ƹq1cL}XEDDDDDDDDDDDDTeeeYÇ/*266F.]aff$''#""{.¤]Х<((III]]]8;;˜Nes466 `fV*i!<%BCCqyP*-- FSS3 s.BCCrwWҲ֭ٳD۪Uư}&""""""""XED[r%^~-Ν#G,+W`mwu Wg6BV͚5q㟅:z O!hӦstoddd`1rZ}jժ%cIb?(|EcOPBݻwaIx-mۢeV sb-"""""2SN!88QHLL*M`ll [[[tE۷ʕˈDjj*aoݻEE7op)>}!HHx ...pu팊+aa/pI#**PQQI[.w5,| Y>}7 c%%%t+rXX.](۵kjժkN\pQHNN!o޽{?ǎ… Ab+hiiA_ر#Zh)ូ^ҥK¸cǎ7t ]]]9?AAA@rr2tu`kkΝsb7ׯٳD"l2 ۷o_>}й;k}n߾-[5}T‘#Gflle˖C"I?3gΠ}2L3F(NVRRB@vkN>}; ++ YYY?=V*s$""""""""*oXEDDDDDTF?~ ȑR{9`n_fjR(vv6-[~[L{aaay&6mFQRA$4Xj%Kܼyv턁MO r -ZJ>!!υsAFvXn=ԩS|C OEE/wn޼1x=022š5`|dddqM˗󊏏ܹscvǸx֮],\7.0EqQZϟÔ)S;cҤ(><<wmh7\j; CvMܼySvZDDDDTd-:b-"σD"G c;;2Xa:Zx 9,ܷ:~Xk:taa/#j}Ԩ ];wnξTIDDDDDDDDT(מJ̾}{ѡC;>|Hn߾HbCh|Zy:u nn=!)իWps_+P+xL0'$[C}8p;PlAvޅ~܅Ԅxyy+c1w|Zy:u ]vFDDBy_EN}6˭[Э[W_qp͚t":_$DW  RJ onn---(ݢ2""""""""ZDDDDDDГ'OO?9::?6B^x?TRY 2g mÇ/&NIxp 0a܂)S& cwN{?5e˖SNhNj0aaG;;{lڴ 5jXb>غu V-Ȕɓ'WVvڕ|,Բǎ;o gmm 777GBB<Q.IָwaQ\]_E"PD=j[]{Dc{A%A`)R }?vA}Ów=;Q{i={bʔ)0?|}}6axxyBŊ,k׮߷СУG5IIIůaff&޾)-[*$kUVMfDDDDDAB~V""rOBVff&&Om۶{hݺ 5j/11nnzO""""""""J """""X\;wD܌1klQR~fϞ r:t:t(q5رBf͚8uTVN-[Ğ=NB;337oժNСCG\oO<{ >>rnZUU;wFs4m^z #&&Z_PP\]]VZ2e;ǧBn]p%G IDAT؏?v9j̉'lٲyGGG{:uRf}ڃE<Ĝ9J]EfffFٲex|7^^BDDDDT\XY˧cNHUs6,Y ;;[4ob)33ǏŋD@2eo5j̈́ʕ+GpbbBVVE"dphĘ1cajjZ,s3[SSSqEmcc(O~qi ѩӏ>/^%9U0ѷ#cFƪU###QV^~y9".]#])VSSS(ȮfUn=v^{ QZ`` =׆ZDDDDDDɭ[7E>}K)Zj)OKK ZZZ®%whJӦM OڵEJ%kݺuK.4h bccd/>Yx‰PnF̲ԪU U/͛7011Q8ݻ Zp"OR~J* ZII7""""Hby&k}1lp 6{^^^q:|}}!uv>5kb֬ED"M6N> {nHHH"ݢE }똬EDDDDD<{LԮZjeaBVjCQ;11GfW.~|KEEE#5ӧOE*U u̙-|^55ul۶ ={ HBVLL bbb'Y?W}ש)E QqWY+++3GBDDEM]]͛@-0w<~6Ç>6mİaahhXd=nx +~~~>'׀ZDDDDDDIBBZOOʕ+ _4sEm'= L/AXbc92uᵆN8 fE2vArOLL%kV򓻿k FDDDDTѷ [L?~ GM6RUTT6+RD@@ݰbZR%g|@vs]6tttxD߉DDDDDDDDD-oDDDDDDT$>Tʕ+`$XeffH{]UՏ{ۻνp~r[Q;}ݿZDDߞ=z/_/󝸸x}dIN~/jkkWP#99Yyr?W,FeeehBt^jcDDDDDDDDD$+k}&HJJDGGp4!jO4Z5 cʕEZZ* **c+b׮=8qeŊHMMs>y/[V\qMSS3'xDDDDDV""*x(*էjժ%j?~yu; 22BJcƌ^x!ڵkcc@DDDDDDDD-agRB!Y+""iiPSS/+jתU  ,9+ ..$+W. y8y4+TZ~޿%K~!!!*jWP!vA?xDDDDDyYYs\|  ~pq1[}s&C%%% ڷ!9ԩ%%%!w۶۷ʗ//||<Ǝ|ΩRJٳgQn=εo-[9۶U)4FLL4vލ3f*}K:"""""oEjRK0RE\Eϯ簰/ *9rYǶnsBCd*---73"jNOh8YJ*rzQ^eZ qqqXd1ֵȑqUȮ/lmmahh(?v=1z(,[gΜƍqMCw YPVvpp0___ii~:Zn OOO{ckFFFB?OcG_⋈:Xz)uNXXN>Ç~zذa=->֭͛B5j(׬h߻w_ W=aiiY԰5ΞuA^=9wBJJ 6n$w1f^ܹD"w('UT~~"11 sUQqc+ rVKOO/HWbї%==Νùsʕ9*Ux#,,Lt8$wLKKK'+q{Efoʪ޸q3Zh$ڷo Tf8<{9C cBYSSf8~?&wݺ1b蘡!?~ׯ7JKKƍqzu  11aa³,""""""""o+k}&;v7n܀` аaC/tgM+9dhwCbğe /_K ݵk( e˖2}޽þ}{qֆu<(]r-{k""""EfBDe$􄏏TV p U>X|9UՂ[Jl*V(:7#Qcf?4sC  |.v*'OJRRR5TTT 57ї>zyy  aٲ%b&OÇ 9sfe˖-ԩ6mvB->)Zj%w؎QF\rrǗH$X~O?$VUU\>VbQq`e-"/-'+ !!iii2*++C[[+@K"fTdP6!--iiiHKK]1uj hhh:w jrDPP6RRR+mQB~ ~Kw Tfǘ1{+y|3h ሏGjj̞(Wtu 55uy>CDDDDDDDDD_:&k}F/@׮pc͚y&ޤ VVVU WK.hҤ 11ѣ֭[{'OkسGveyK6gn$$$`ݺ(_^"sEԨQt@jpuN ={ipr 5551f(lpOTYm^9984](t#9ʕ+KOKKĉqeᘖƎg( ̄!!!8wzQѷ|UTArs, >>%Qw$ 66#esGT}7}wbӦ 144Ē%KwHJJ|e˖if`˖_qmx{{ հTUUj9/_GC yFln^[@dd$,Ⱦo&g?K:uo~8vpnH7n#UV-l߾ 4T([f̘wޓuM| [KM67oBDDDICDDDD0eee?T%"/Ci޽ """"""""JHѮ]{\pW\ƭ[)S066FݺУG4k\mAGGGh)ѣ077W蜲ebɒ4i2 T)SƨT*W ;;{cg(WvЦM\x/_͛p}Gh޼9TTTdպu[hi}Lʯ:,vޅL;<\rؽ{ #GΝx 222uw0`@AYYYK|.\87o!"")))PQQ}۷B,-0klmjj*oFE}+)>}аavrZ}Fe˖hٲ%iiiPW/~ J{=444 }---aiiP_kkkX[[jVRWWGzQ^BODDDDXYd-"""""RFCCC$z},QKMMbXYIZHKK+XhjjI"""""""""""ZDDDDDDDDDDDDB'kCTT'* 7ڶmCtDDDDDDDDDDD_%V"""""""""""""E:Y+)) QQQڵ+LLL2B)䝢4RTJg*Mߧ)\zE 0YQdjԨڵkҔST㔦XjKQSbq%R*F>>>%W߾}Cj88 u_J:"""J)Bϟ 파>w!**Jhsػwoy}򑒒"F%ͷ!5OK5/*ш$$$p4DDDZDDDDDDDDDDDDSJjҤ ~W]ZJ…`m *ለ[,87 """""""""""JUVQzJÇ#11d-""""*QEDux RRReʔAJ222_W1jjj4/p5 66**000U]nyp1SSS Ź{ND 022$''#::d_`cBCCCƒ~x%kCWWӇ-jժ%H"<>^g}&H#xaa E||<*VCCCVko233(zµk())uAmDDDDDDDDREDDDDDDD%̬ z)K0o`4DDDDyEDe1bo@ժpMcV\7o<˖@ <+V\/^sTTT0y$ܼ%3g.f̘o G<~H8[1`@! KFرh׮} Dwɷ ڶmM6=azlذ^X5j{Kr2<<<g<6l8LgBޚ5G긇Ǖ<ϟ=<`ex>***s΃~DDDDDDDD2Y+##111HII>ʕ+Wls 22e$&&"66EB\\+aaaPRRg=SήX[[cn$VVVgD"pC.PӧOunݺ!88˖-hC*`~p7i$LMMtRQW^aժUB{Ȑ!hٲ%탻;>UTA>}0yd(BBBwwwܿ{ʕCV舁BEE@ػw/k -[FFF?}ܙ ZDD_ ZZZZ[O۷o .9nRRvDǭQwQ#x{{ W11ѣ\Dڵ522Ž;3f4_==ٿvvvv5aFQ*U>V]}P-JSS;v9RZ666h޼ H~!!q$ 1 IDAT%%M6Y޾}+zF2)))ffU`ffSSS 88|}}/^qU: ߃#-- @v&MȍA]]]XZZ*z)TYԩի#++ wEDD2G>8"DDDDDDDD$_Jz>,GoVzz:,X;w"##Cf8:u Nv܉5k*m۶a޼yqa=z'Oʕ+(%$$?ީSgnnnݺ n«W8p ʔ)p /^Ğ={… DEEQѤIL4 ޥ,886m~7 ^Xl6oތ쓔Xv-8PݥK0yd|Rnd Ebe-"OXX(֯_ݻ֭[ *|u=ٿ^~=ϻ̙3DZձnzjJobb"fΜӧ1cF۷j SL͛0a<;!U@+L6Ehiʖ-+goMCddv2nUH$Xt7ѠAC$''y7:uRE^|)Jׯƍ_ 1`@ <UV[Xh7;iٳgؼy͛/w0pOZj 66иq-Z$J233kбc'߿Ǽys437nxV *%% {JJJPWWGJJ?v/|[lܹsE /J¯Dl۶@s%%%3sT޽{+EXDDDDDzїóycڴ2hР˗ իWqq]vmxz s.hkkc߾ǏSs̙;w<==u4irϗ#F~7r*XYYyp`5>#([,ѱoeaaAU5%7pIj=Č3V,ݻw{UTk7-&6oزWϬ:1cKDDDDDDDD_/:Yk̙RZÆ C^`cc---t&&&}v]N:äIɓadd Vkh׮6m+WV8(,[TTӳgpMl;5jի*TAݻvAn֍1{, ]]];v3gΐnk#GAطo?T"scǎR}||#7Ν{ӧOnɬ,!!!x<==fj4mꀍ7VZODDDT\ege-"/[͚5E N/_Æ om+JlZMM v햛 r111]vMRQQ{ЪB+W޾)lmm .ÇswaÆGimm]:6s)h.V\óDGG&9E-wMiƌ8u$BCCdL""""""""*ٿU\t ***ؼy3mۆmۢB PQQA 0axzzT)S(<ӧOZjڵk߿?d'?E;+.Xo޼)O,j׮ڵkK]zu_9DY׮]-2˾}DT49CV׮]qtQHA^z 礧cؼnٲW\666x-Zƍ ! Ѯ];xxx`B/2~,^CfDf)++͛7ڵk#GODDDDg3h۶5\ޅWEy~$ F5kVK%jၞ={H%~={`e2rԩS0ct1MV8w_~P&|{СHNN]u_?G57..ݻw%쩩 եիf"KKK#s-Z|_k'N`׮cgXZZܼ -1o#(TS^`PaCk 4Hn N]Xiii:tbbcVVVo4CC#Ahh(^~@ɓ'EǢE *:֮]{m66PQQƽ{p "66#GDzzZ拍O? Dll,ѥK4lh KKK$'Ǔ'?&mvyEu033LLL@<~['&&baq& dW}#"""*N*k1Yְb՚rW~~7o6u(P}z1m49R8w^Z@llްa ĶlقȾ7N8 5JII ˗Whl""""?ӧ`077^GDDXBܹʘ4i2fϞ555VZa̘8p`?f̘.,ؽ{NgٳDZMiflll0dP&Cdd$` С4+Lh!CÆسgV&۰?<.\5k&/'NH͝Z ZرcTRF ߿?Ϟ=7Yntuuq]y7n={bʔ)0?|}}6axxyBŊ,k׮-:tzQF 7Νů{7}SeKdjժ)\٬8 27n`n^Ux={<|W$1ܻw{***Xx)&L W~1ĬY3EqԇN333v=ݺuèQ1rܼx=ۋŋWMM {!CLfsM: s3dWN{s""" WQSSS8HOnyڦg^Dxٯs>Ȉ|c?޽ !zѢŢgi_*U o]}СCjZ3gN]o<}QQQHOO*}w_*f2}ׇyY,q}doXyyyƍBΚ5+ϱ2eYXXk׮B;88 SrVʳgᵊ F\yӀPR%VnnnnkeeeSx`bb"o߾-']Rx|"""""Xbh!G]v!EƘ5kvU̞= .ZССʸv|}}qQ]fM:uZ*Q+-[b')U)9UVݻwBCx=d)###L>666y[l=z$lv\zaaa %j=-J11oذ2`O^+kE&k} ꃦM*|nڵa`qB |444Kݻ%9r ԩ*W\ 4s*S 5j$CBB&ꓑ[n zBŪ|`ii)~5=_0vX<{@ѷ/^XrTU豙&No'>>>rnZUU;wFs4m^z #&&Z_PP\]]VZgߢnݺU; 5ĉQl}*SxØ+W. mCCC :43Oj)K.BBո.X(T'"""XY룬JLL,gjjVZ)))a5EVj5xyB۶me={cǎA51|PkLԩ7)[,*W KKK7C3KMJ.Ċ~Eg*j|SSSDEe/JOOGTTLMM<'g%E)88@--- 8;v\v QT}=@sw,J >#F9%赬cqqqR1T^P ##wݻQZ5ovvvhݺ5VZGDDDD_ ΋~ ?fXQcƌShsj+RSSqEmcc(O~qi ѩӏ>/^1rHWUxG߾ 5VVxW܌DIXyI8rp_\t ÇKW,zSxP"]ͪnzx! ""?*N}M"##4@TT^x5k~ܹ-Z5$"""""""ot}a|}}E@Sd%Vcǎ$ zjQ,ݻWhC{Ns(9}Da>|Xj]va׮]{2dW{{""""6ܺuSGPt޽K]o}J\ @v>|(R.P1h@^Ȱxiݺ [V-WE``.yFں@?w]HUuTTYHJJ}6uUVCe~?Emf ۴C555Szfff"((QQxR=)S>7yEDD9 ,, /i9;k 51wVWլU&8)lܡCGԯaР6mڠuhARux򥨭Ȇ2gm*%:7,11egS"""rKZDI@"MJII1':uo&\={5r@:u,fdd=z2ʕ+Xp߿HNNƼyp[!j].D-)ݽ{ZDDDDDDDD%L&kikk SRR-+/^utt.=@Q;VH֊ĩSлwoۅǪ9rkd5۷/ϟ/w>yf[3\t /_FPP_RR.\&}V500Tx EK^y;Ɗ+ر]f?WtttrlPM*<đ#GVE%ke ZYk_PFFFywRJUUUlܸ }Ut ݻ{.@5ЩSg|]'888(h߾}\]+""ׯxYvmT QII [ WlAII GjoBZi>YFDDn(p`ccGVZXCG%$GGG=z\7ʕ+`[*Y7rVVtTiii[nؼhlݺ@תUKV{#$huceeeL4.(EQDDDD$+kT5jʕEǖ-[ou9SSSq=Y[s'?2_|v;v̾5k+Ǐ1z\wڵkMMM=k,̞= GիWsM/kXa P~],^_JJ N:]ӥKs}Wqtt4ڴi[ĹsgEۢqm۶m,kN̚s4::6mD׮]k鉪KE={`pwwǵkޢocaa77lX͛Q9KKKC`` ~}ڷwBNs#""""""""ido!TO>سg̋F .Į]rM:|B(y[n̙3^KJJҥ❓ƌ#yʞu=4m472&L SUU=7l߾'N>?2F)oϞ=QfM<~C,Y-R(Ddddv(J'##wUaJ'GGG\ڗ.]DK0"Zh%%%&D"X/_} ueD5lhwSٳp¶m[իksׯ###CRpɾf*U`dd$lqAGGGRt $=t`.l^[YYII(D%w|&k, Btt,Y,Ta|  000@աX;]|hrذ!MR oo/;v@浊ٳgaZ֚bqذa= 99 ۶ތСh׮}qDdd$֯_'wE_?^nr26nҥKѽ{wx./^%K`ee%Q={ǏΕ'±o&w߉ګWqAiehh  wͭPQ]HwOpu/\\&!--M={On( UV2}߿Ǯ];%mxy]:::kˈ%ߜQQ9w- `mm-]a`ySNJ4#Gh¦u x߿\(Kt>QvSC|;::~~~ţGr$jUPk{5&L/$ b3ի׈Wʼn8qVqRdd$#0*u:6ݗ WGaÆ~7HNNǏqQ.v܅֭[+oD%m %ȑ.sðwZʕm%"""""""2ZY V5k`ر14m۷G&M???x{{W4 ...k׮_bرpssC-ШQ#DDDƍ8{,BBB1زe(qaȑغ5sWϟ033]H2d|=z4͛\7E :DDDڶm={Yf]6=z???!>>^SjU,Y$y۴iիWcԩ±3gŋҥ j׮5k߿GDDn߾ I;u_~ׯ_ǴiСCԨQUVEjՠՇcK\ x-VZEr2e*F&gZʱkk~ծ]m۶*Aߖc e̙ѣp3ő#-[6Ő7CH={v O72l0?~LhoF$XrؠADժJ;sssܿRrŲ& U q2d֭[+?kؽ{1{,$''hVvmfnr7o81ׯCTTdqGDDD+y7QR^}a3E6'UQQA-qm۶?='Wf#upv֭[縎gnн{w4nXR*T;/??:ʬ|]B,Z&Mٳg+#""))B?=¹6(ˢEѫWox{{ ~~~r?G8qT"iM⯿|+\]]777硭...022˜1c X8vX#ѣG%ٳgahhwg""""\|3RS3ohؼy3޽x3gVuM47Q޽+VXf"G+ؾ]ve9s~ps+V@JJ <<>033qΜ3f9_1@ݺp)Ƙ`Q^ TTT`oo{{{Cƨ111x ޼ytXXXRJֺu|Urpps!55Pr_:u 2z셞={ FGG#..z077V|eJȑ#akk7o̵fϞq"Sf!CÇrծ]A&M^0ѣGزe lقPѮJ222BqبQ e7sTRcǎEtt~شiC&Ml2Jt =z@sӧ޿ׯ#)))0vXL6M؉ոqc̝; *;x|}}1r(ցm]hkkŋ xq~DFFر%ݻɩ >D.ի7ڷo5jF hjj">>AիWpE|uѰ,X_~#;znèQc`kkum`xn޼͛78xPn\\WꆧO<|IMM9xx}_{*D۶ms.3ZHZJMME`>ѿo~5k ϯߢUVc!±۷oO^29dlvA&+QvEDDDDDDDDDDDDRd A))ШQ#4jԨqttVVЪU+K<_CCC)tSS ~_~:'FFF۷/++6mڠM6&"""᧟nݺxYn``_ʪΟ+~Ν;Gܒ$% MԱůǏs $<zwL۷7< mmmcUTTpaLNe%?Artl3ga/:'5111Kгg/IFk֮֬]VZjUYz_y3g 7S C5kĔ)?_HKKUq)xsgg]-wD粿Ǝ-[`bbR!ZDDDDDDDDDDDD$EJzXII *U*h蓍7v0aB FCDDDD;kΝÅ HLLajju{hQ́NNDU]sݘ1c x˗/ b)ps;ooo2' IDATFtt4SSSM6C ׮];my׮}~}TUUֶ.wGGGȜM'hi}NR]HOOnnضm+҄ׯ::B ضm;BGGuC^0`A<<=ܹvHLL `ggۣo~+)թc3r7555޽ѰafrZDuuu5'W^!22RtSfUڼZB@@^@dd$bbb333Td+++ :LRMMheUUR<صk޾}k׮!<RRR`nnԭ[O*J222uԭ[WR<Phl62d~:' Ѵi3NNM:u:S5k֒]o/ ԫW*_<ڶmmۊ'''AUUMn"enlllD NG׮]sSQ~o}PDDDD_V""*zJCjIAT2AHM2d`ee$CժU ...UUUT\իWGZ/333LV""*,,aaZ"""""""""*%5sL~իP.yaoo_(sRJY'"""""9sf3fD^ u΂F͚5cI1i ƍ_(}}d-""""""""""""ʊwѣGcђ~mlDDDDDDeQŊ\9XKKzϞ=+9cbb u277u+>m4޽{ Mzh[8::BCC&"""bȫJ-DDDDDDDDDDDD NNN%WOMM::::zLt233CпZDDDDDDDDDDDD$ԩ3/ """/d-V""""""""""""K:""""""""""""NYYjEDDDDDDDDDDDDY1Y(*k1YbQUJOO/H4cQXYP-J;V""2{~~x"AA/[}Eǎ 6m uu3Ee낒 Hk׼gn=w!CuDxDDDDDDDDDT1Y(*k1Y-BeD'lllr###-/==]+EDDDDDDo h׮=~}qps;???Ax>^8w>4nܸHb&""""?ODDeτ͚}g̘13f,ʰv)9V8YٳgHHH(X Dn%1OiTާRx 1"""")$kJsmLݻ+OJJrGDDDDD+k}9Ǖ+W.@苔d *O.P-a~T!Wu/ܹ Ez|yoF݅ =z>| Orr2s DZ***Cc͛s>~qbĈ wFXXhMx-> Ϟ=C.qs]7`c]6j׮5k"!!۷eVˏ'O`„:uFJ`hhXDhh(Z(kg5iD;vTtagg7oMܿ)))wޡGn8} o<ǼysGuaii/^ z!񹆕+W_Γ%""""""bz-48UU7 -YkĉxX0rb׬Y7nիWRJ#EGG% }UʭB ]ħ?ڶm~[QOE Glڴqqq<gϞ-[4d- CcKaaaXj\]]dV2[U(0e(Qaɘ<ǏAEE%tfuʕΝо}}Ϟ=c >>6mFjj*ƍ#JrppUQn0q\z S{]qU,^Ht۷eUc"~8q\sgUx=+Vb&> x}puu͑5i ~uA=z ɇ0fh\|KgΜHJJBj-hԨ|ZZ.6lX/$m޼ ..Qb>M"""""""*fwJKK+P>˚UXrɓ'+ѱsΡk׮0443*'8qBhϟ?ZDDD$?={*/|C ?")) FFFo]Yf^/^[Ճ 7o넚5k:˗/q7 $p<)) ;ѱ%*W,:@ʕRtK!ӧs~~2wmӦ- X":wv9?>]|mmm!""""/ +kQ~%%%ذwɿ:::ؽ{ZO<￸ի/w޿ RR%]I8z̊F^^W ǎ;sݢ#:r諮-[DŊs%**k׮ښ8u dwv7޽B}رB}8pI*U77w ^^WΝ۷aoojj*N"J<>|-[555qbʔ$?y&'cJJJh-Z8xmHHHEǖ-[1c_vm?ݻwųgϟ?lO?MfRR6éSe~`޼ ȒpI 2TGDDDDDDD%H "*iXt)֬YSҡWpu+{X7Y:ǎy&n} [ ---ݻws̆o]r.]’%ѳg/,Xfff2s&OvqÇ2sZsBdd$sgZ[n9󹺺 gu1:uJ[r5RJ*ׯDDDDDbe-""ʯ+bErI('O]g5YKj%OVZsÇOOOڳgxbZ())a޼8z:<<(6p Z4i}%?, MDYY˖-GV-Ms sknx\UTҥKsM*X/ 6,|qq  MC ڵS SRR²e2Q/???&k!}E6o,MDDDT\1lPZ2z@JJ==ϣSd&jeǏCveQ֝Wӱo_ΤxxxZ0d\o!""""+kQ~M8 y˞8BB ڷn,C:l-o8mmm 6#ݩS'***4iq&Mt Hؚ5kCBKfÇs΃5 &TU?1Tlkr)Q~4:N:B;""7$mܸqԩ#z """""""*ZVjԨڶu*X+gnyOJJ‚ cǎN-*AթSm:nݺ044/ҥKB[na1mڏC vXYUC`ܼǏ#11nݾ5?_>VZ ػw/ݻ sEˍA3bH89e=3¹!ǘ5kUZ!(`}1cCݻ[xALDDDDD_y90YҰaU>111S||\LKKǏslU5nL*)䘬 ^ظ)O))) M4eUIcmll`oo[n5oQo+g:upY7o |JJ z+++aݻwqѣgvI!z/ ѩSgIc0t0̚p͛s"X\\= """"""j::aeeU\WL]]ׇ>} سgf̘Zj{,DDD-PQQi?b֭[cܸضm+fϞe̟pq 555sǏ%jկpC С1pDGG"##1y$:tD4%*$kijj K۶m>>DZܱÇǂ<:wvs`\zUh]''|"""""ɻi8*DDD$SUUcq8s ._˗HH{lllʣGJAVRÑHNNڊ d-%YMᱪ* I♉Bg>Q֓'OG]u$He7h`| dcǎеk7i 60_P^»we^USeMMj֒011>ޙJi[}TTTd@rsűcJ82"""̛7..3f,.\ Zn-,mm̙(_5ѪշŋEfJtR$''#)YuhGaÆUDDDDDTɻy(/$US|G^?cҰiF^Jd$''ƚ]T|y鉒mûwoEJ HQQQT4kDu:ĈUV-5~={Fd&=bٲ(_<CܹS)^fM~9=Y'ml)gʈ>}^h?~ """*\VV0v|<<}G0` ֯ 7^~/GDDDDDDD_7^) ^ǎѺuk\rpE\t NNN%}lll$ׇur?yS\%sggIGh֬^ s''Of͚Ɨ&7m]>3fܝ#}ݘEDYf֭lj'J0""""ZJ+oNWR(JZ /Mz---gns?l؈ y1Y[ J hkk #"%˾<H^CrGwq5o?RIhU*0}OPd0̌݌}';cK:dd#XZ Cʄ(i_g:m|ޯWϹ9 7++ ~ڶmN?|y011ѿx :Gq[ ;wBc)xiǬQ q``BE=ŭZY:"""""""XVX!>|dddaFDDD_/n`GK>^Q<_/& IOOa![ ]t)o ""* ɢXKKKR{AJJyr|.iff&^"iVDg+ݻ%+ VVϟCRK."(HV]vŻvBzz޽8!!7o*K€Dq@@@kߢ>%$$ȴ=Fb???-xwX_Ny,, ^`ÇI@T0g+qEIs#۶mVĽ{wgܹxݺu8vsŋp%[XXoυÇ6+Iѣɓرcbb-8' qJЪU"о}4k\###1~x$%%9wYZtlo}zXEDKݺu1qD!Ǝ;0#""""ԩSG\|U2iy8Nx)k׮ AGG#"""";kQyQF tY_ Đ!egRRRݻt ffѫ-||| /#::Zw qTTéS RSS矷ѻwO<~7Tmm*U`cWwMEHHxPP&NF@<=1eT!NMM)ή/6d|ҥKк5FyαifԮ][`kk۷#(s"""p~t|!\y-,KK L7nY9.]z/6?t=w}ooo CCCnZ88Ň]"ADDDDDDDeǦNWbj۶Əw*\?W-?CMMOݻ PWW[},]XGK>ϧ`޼yطoʕ+1n8hjjqfDDDDkݺ(ufϞ#oBBjժVEEUxVLKO~wӲÇV3fleHDDDDDw""dԩӕlm{ffgϞvwtʕ+c]PRRš5:tp{ ,, )XnB<3aG$|())I&PSS?3;.|Wlll0} lo֑#8rzzzhذ=Ǜ7o--s̖<…{:tP8)W>;hkkc6l("##d-\8 Gjиqc$''#00PTj$ϓ>} O55 }{ለW>[,[XXbӦ͘:u 222d& **@DDelo,O0{(++GEE[B-+++jUPRRƍihkk<;w'd Eci /_EeKHH7|}}EZd[Qy'$$ un޼?(m544о}{|r>֢E {ool߾ g/7Ν`ĉ200嬩)=rX+4"""""""*y,'iϘ1ZDPzuO1c"V\Bpppݻ |&M͛7CEEU}XXF.ެYs+9E۱vKxNEE#F(ԈFbPUUٳ ;c:Hj;eTL2U9`߾ -[eVDFFdeeAGG? ;)WVp $%%! }իN:2 ;իWP_PP^GRR2tQ1jԨ!jfs-[ȑx=_hhiiA__&&&=ak۳P󨫫cɘ}*~W~DDDDDDDT]dKT]]?羼+9%>Oqg,иqrhР4hPbsȣSӺ05[ʣ+ADDDDDDDrW])7ze'*9UVŋ1~|nu)))Xx1[ƙl ŋoJ*ɽP]]aÆ '~#Lƍb-[۞066FHH1c&!"""""PZЂ;kQny/NDD;vb7 G~;s Z УGIN-[jE$SU~}tԹl""""" ;kng-"DYY˗/W_}8+"""ڵk 'NF||"#)[۞8y$^o}PQQ>:t舾}0/666A@@u8"""&֔)Sh ]M0{!n֬B9fbb"IZ,""""""""""""]Vhh(233֖733&ttt`ee;BUUXC#== h׮=t ͛7HNNFl;8,}2w_GG6HII@x.11/^@@@á CCC3?nܸpĠrCfЭ[wTVsVt[$&&POII˗_:ЦM[k׮RO2d 8uիWYQEջwݧP}'MT~UV=dHj  +c߾HJJwŊhٲ%6m Kr™3nXr%_NEE1k,*4Gq;wZZZx">YYYpq9e˖rԯ_W;{ŚѪUгgO!~2QyWRb-Ԫ(pu!/Z YʟE ̚5#"""""""O[+RTbb"Fn,mRR֯_ӧOԩ2DR̞= ͷMDD`ʔXxsGGGQtr#""pe!޽7!!C v,_[嗽')8ۦb_p;?Q,;y |Μ9o/^`!Xp =zQ֩Q!m߾ YYYB|BbbbiiiprSN}':nmm ~HNNƓ'O)|I:5tead 7n֭>Chh(y)))Xt (Oʕ+Ѷm۲N 8zؔE|ɯ+DDDDDDDDDDDDVVX.*RVVƪU8N0ׯ_cI8ut+!z*@Æ qįSNٳr@8jJ 6,)J6} @ttX_ǟ$1vΝ;rH233zX ЫW={ O>ܵk7TUa111nFժU{ڴEZ5ڵСCG޽? pr +*ĕ*U¢E1eTO0lPDDD`͚ե'M6]pc""""*3m~FzzfڴeUP\Q~= (?kŽۅXYYGakk+}npy Bn{ K\xvv}V){3f@\\@CCChLv]]"FDDDD#66ݺuɓ-p^"""""Wŝ(G-ڿ?233xAyj尰)Sdb؞=%kyj6{97o^0I,hjjbȐp Ν?s/=())a({sVZ/wUbٲeڵp~;Eѭ[ڵ;Bܖ,Y  |4h )]Qᓷ7%--(2;IF mmHܩ\2,,,aaa KfرؿENDDDDDDog [ IDAT,z ^ q6mиqcI}U/":%a%nG>>y,_,-Z;Ԕ<ۻFzz3VyM?n#[C ӤISqzz:=zPCqj`gg'_ժUA"7qM\~'Nu֣cNeU`Yˉ>Ŧu_'OJvڨR ҄9* Gaw٫D>|Hk׮^z+4?i) J7ZUQ??^/qg-""""""""""""BkƊ:u(߿H*U!11'X #&&pZׯ_Gppeeee󨨨B[[Gr_wbdD~><<}KNNFJʿ)y ׈pI!}[r$.N\\ehhP##qV\\~S?ι$;^^RR'w{sرcl (\xA{Ux߇.v{Ms_(UVV_|_߼HS*UL/+b-EDDDDDDDDDDDD9*ViiiⰰPW^]ru 59wffЭ[7xxx? BEEp+ӅcǎUx(J*ڇb-2mjժ%<pnQǟ(ڞs,""""""""""""XKOO_hE__?b '55o߾bڒBVLL Ξ=!CBVV:$344mOׯabb"LGg^ VURjBLL >iODDDDDDDDDDDogR̄#00AAA!Yxn͚"g»wכ7oS+|)[LL ̙%ă ەaFlܸOȾ_iŊeQ񋏏?q~_`6۷osxȐлwR$Tbf͚J*HKK\+彦m۶?|kٟ7n5dw׏ZDDDeXl)N<{/^*u>b{Syk%&&o3kQUrLUU}ĥK%}.\8X 9DImw-۵k'_QiiiAEEUsva* eeeۏw 5ʾs|:%##{"ĕ*UB6meuMTw _yҰ>IBBBp⅒Jǝ;ps; 7Ӓ(iٳN8"i""""""""**d1RoٲYR-[6. 2UV766*ׯvZ`gOEUR%Ԯm QQQEo1\ ֭[ - CCB <~w~wn^]vL;###K_ŋ _y3|p={v#55~vDzzzIFDDDDDDDDDDIZDD_yf89x̞=]댈!ymvSV2̒([-2+hk' yپ};=*I ͻfjܾ}+cbb0~#҄ccƌ\Vjxuz,}}}WOUtncǎ-9&M<1~qĉyofA&S~-Νs'>>>eFN淪..DoݫK[||(իW<7oSlʣ6r2СCϯL!""*)kDDDD+44Tt/= wvY)Uhj~WaJu~cccyݧDDDDDDDTT.Iշ5k6֮u]zW^6m/_"::Zaƌ ͷcN 6 )1c:fΜ Fx>| j_~W+8::b׮ … dikkٳܹ sQ졤Tz?CCCYf"22C 7o)AXn}c;9MwvP#^Aܼ> 66/_;Uybll[a„s=tիWGf*f͚Xn=F&""""KKKÛ7o89C>4ޓ2͛~ x>DHLLL^ҲDǗcb}W$%%~9QE5EDDTr^z%M"DDDDDDDDDU؝r̙3ke %Sorej՚7o@KK @gϞ2Z8z qVE ԩs9v(VVVƨQE?p355}"sX-phkH{98t0d{ ݻ{_PKSS5*Y`׮=Vx\\PN:8vDDDDDDDDDDDŝFxx(VM2!""""""""Bﬕq>[lŋ,ӦVZ適SqСCG!\2z شi#ΜqC\xkmm|嗘5k6j֬Y+[̙p]x{{x:19EgRt] k.\g̘;b۶mr2ū/c豘0av}M?~ gθ- ѭ[wC.]RRDÆXy8p ZnիW,DbذPfM} JHVVҐزN*_BDDDDDDDDTTY%\3Rwzz:<<ѣG ]СC!ƍ ELL  KtիWϷZZ^~-:VN|[BBbbbXUU$}wkrȡUJ_HM='Qrs244S.\3RWWn󅆆 Edpy?GժUahhΝ*߂deeɓx9ߣFC֭amLIIAddBMMMRX/ƒ+-- 全HVVVܹK^3\taaaHJJBڵaff?S~$%%[x!Zj]te"\DGG#%%]6LMMѹs|O333*ZZZ_ /^|0Y7@6mѩSGH%"""""O[+ L*\ +WBhh("##] Uӄ.77ƍ9nbb 6bx*QQȄ>.(*IڵGv<ιsxرE3<(eijZHc~4G#55^!&& Aڵ8͚5fVn֭릯Sg̬Ξug'..gϞ,Z!"""TfM 4H5-l*® 8""EDipt{5+V,CTT~FFFXt9 ιcŊȳMʕ1rט3gP@1eee|Tܾ}K86w9sV9s>m3F)8y$~h<=v8lm{8O}n8==P޿E!} @z셣G9_ǎ푘߿?wAFF`׮2h֬9֮]kk)55.]ƕ+qs,̝ceff7o?*0O+W|XX(Z K0eMJJ֭[}6W]]]̙3\Ǐßs}姪@bbXШPԩSu)򤢢 aÆ%>WYJOOǡC66=Jdի7?SX{QUU-׍Ş={: " k%ew""4eeeaƌ8x%vaaa7QQ4AIIIprK.6==ѣЪU+6JJJصk7v킷o֮uFСC|ǟ7'QСÄB-gAOǏ_#֭[m88iF\pG%MMHHƎJ)ݺu#GvM'%%{;wۙBse89ϳ//^q,k_Q)))q I$&&4UӐ[I&ԩ_%Ƒ#ykFHHr0^|Q`7o`pssTVMfϞ}'񈈈ĤIůDDDDDDR"##`z^)Ӝ0!ۻPcx?V{Z-*,UĹ+4ʝvbQŵvPpRZ5XZ6#>>^h`|t:ӲeK4jHMMœ'O)_EG_ʕ?sΝW_"++ 0 7n܄\p~!677ǺuEmLLL0h ;wcЧO< סr(u::2}U+x'()ɓ' Z:h޼6@jj*>} //Oa1>`Ҥop}ԨQC*UBӦMabb:u /=z(}F˗idbBmvhٲ%#)) 7o*TLqzLڴi kkkT^>>>`a„ =ĉ:u 222cڰy}x16֭?;~JJ  OOOqmmm4mj&MϞ=S}vBvڡ}Ӄ&^~PݻFTT ;Ŭ7ի___x{{ի@͟Ɨ_PQQ<߆ BOXiS hhh)<=='_x!:v[QEU~7 *ʔ!?,w >1*vY*eQ %YQq ڵ/gQ+"==k׮Ł(4kLQVzVKqqq5kP kRLnݺa L< ǎ). |bUUUݻvڵGvÇCddºjž=&Sr(**֚3gkIr2RRRf͖y/`;̜9+߱1r5jLM֭Xhz)lٌٳ䊁'm4̟@˗/W_!(V.]ロ**lO@___8v֬8tn޼eeeciB᧟z2^)SŚGMpR+S7h< =zA]]Cȓ+ qڵqI4m4> pw?Hs·NjU^}{s˖-e+$UV kef5dddQZYYYx5._M6u:DDD􉲴D.]: ""???xx\0CK ѺuZjX;v FHHRSSQf-ԪU: I8~_"$$!!!ʂv-ԬY zzzh׮llz仪nLL .](m۶Ez% 7Ӹr2ڵk̬ nݺ =<<~9ٳg7𜏏N8O"<<UTFچر#I \toBDDbb ]]=lzBN 5vVV]7Gt[hiՀw/5k:c<~X (0cǎ{ CZZ:3CVV? jՒ;f||<qŭ[c k:thqxxgϞhAGG{-==/_™3gsDV-mԩSzb?]CSzs'Z,9?;-W ]R[AW\Ɲ;"22 @]]zzF>}amm]*s{ [[黗 u 555y]vUfرG˖C`eeU9J w""O0} mp)hL9KxLaWk׮ĉBܨQ#G^:vڍիc}___:+!ܹ?ݻw.??oԩ:8::v[b%,,,IRRRg^r۴lBn]c'NȷXaÆx1*W.VΝɓڵ """..0mt|iy,]'O۶^tJQƑ#bkkkiߝU\7nV VY6{|Alٚ0Wбc{lŊ0`܅ݻ'LEEWբgشiK'~-w1b$FYs\pB~;ƍm8FFF=^x/w܉cQ~$r?1{<% qįhѢbccd{\|NDDDDDDeLvޅ  ==,]- IDAT ""O+̘!"+Qi{ ʕ &44O`Uh׮=֯߀FIݻwظq\\ 11Q ɓ'u!!!X'O.NUȑ"tӧSM[$kX\tIx>;Fahܸ1n݂7o[p|=ԩ3lllɓ'ի2?[nbÆ6m:f͚]`aeדr/?w`eeU(Kd`KL:Ua'GHH} hӦbXZ..~w`׮hڴ)V\Ubь X[ ///޽  ΥRHDDTTݑ;kU\ 48ɣcef7_rwq*2o|=wγCYY{nݺWXvڣu ;0`Ǝu(0gVKfkdg:r۫*-['qwyQ.>>>B\~}L81>::={pexѢ.r4w8z(J޽{[?0Mؽի@\r{iuQ8B.T.Uw:uj ӧd/gn^&~9ի~o 8:Æ d/[[[IsѧL3dn QRRØW"իcȐ!hҤI+qDSSe 00PXW{O^8r(ڷotmw(4GP+dff-z=r0^|И/j_s_ٳgoi;bXf5RRR0Ic߿_#&&FR{o;wK*ǀu򒜜iӾ˗/`ii))Ґq{ロJ*aذy~®];헕;w@II K.+0,̟?qs$$$`ewp)߿C-ppeV(4~PPMwW#رB}̙30a|79zV+7α6ǭ[7ѷooD.] P"""0d`KjS <oT333cƌŋ l憧Os,""rO9Q5wO[ڵPdI^VPPx 4ҾwQGcӦǏFll,jԨ!}ڵm1 YYYHOO8\~7o޽mMMbr!Y ,ԮknBܹKѶm[Q]Z(2ejG_`rSBb%("#++gUbXfy>$~`oF͛2}{/_b---̘1S@qqqy׬YN6nn=bsBEEv]v)ʴX+*ann'oоY3$kjuZ%"::u-Zkn2Ν;U򕔔uЦMkyG+d`ȯ[>P xA1Bھ}LVvз簶n MMM[l]?lj4h }EqmYETl(^XwwDM$j4VT{0 jbAib#0 K1ו+s朝dę3p ͛7[hذaYli@ ٹ-6tNuBo1b8<=U߿(RJׯ?P }Xp~+C9/_"0xzz >~CkJ {z"-- ZZpwwG&MPn=z ~~8rpJII!Clmmx\I1_~Aʕ@;v ȸ?嫨Z5 !>>={vǓ'Om֭;7nzpBĴ4xxLR ZժUC^ѨQcTXu+?V^>|Y*ި1xutK:*ؿiB65uuu|#|I%`-=mmKq""""ʝz셁Er'eNٳѣG޽ڵkt/G(׫Wǎ2~Cr'ȇ B}عsWU$~9eT?s=RĒ%@Onnnܹ3FW^իW@FӚ5kѩSg:`18qʕ۪V M%upa۪V]bW8s>4ѣGO 8-[{jڴc*̙Ç"fjX!}Mf'lllЦM9 nnqo@RR"&O/$'<<Ǐ4441'{@[[[Tٹ-ƌ%K㧟6>1cFTm޼@e_wXٷs.2e* /O25x‰"ljwMV鋮]b@wzy&v؎G}6f""҂Y2lnnW^/9soۜ9sq3G/0D_d}E||vAAA8v(|}oѣGDrrrbcc%0<|PHyGʌRJPWW-#OXXBBBm,{OG03(kEDD_T.Ej֬ {{{a'Oym۶ Eee‌gY '`- XZZI*fZתXwZj!22Pa%uEʕ=""""W>nݖwEd mܸO<xa9$Y'̘1#Ge2N u}GKܻwp\;:tp܏"˗/e>}N,Xo jwKЦMGBBBDWOzzz8t0#LO?mK$)[jG}$UVaР Q[nEDu255HoeU{?)9ضm;ALL;`1MJ$Ԕv]d Ü9{ %:qO}UQf055ś7or)*{{_lLgl+ 2 6v휅"p={@}ypqgggꪬE_>;NKKL+-#|a""""""*4ZZژ={PC43=~XT\=z$kki+HJBB:(K* &oAF Է@Lpssʉ9V; իc$eƌ066{[S4Itذ 2+WF4R&OȵUMMM̛}J*% te*((Huٲe1eLnVPS\)>ƍSbŊ8p`΍fϞ#?~#3/;wVI[[SD٣tD&MyeZ`(l'O=՟u0P+f`)\5^)Shoom w8w>ZnMDD%Lw%%e.*IIIꙛIMMM K.Sx g*MpKULTXkFӦмy ῒIRR"CJ}UslއU>TfMVZ}=<<ǎňP F²+*>rU{5-{?6m^/&:jԨQ~yڧ2<`kG&M 77|=ilcխ+>?Ϟ=C rԻ|hS77w)[,:v]vȸsd~.U.i ƚ5EǺ8 80 $Y=H R-*?{ 9]|UtFdŠEJJ  ,9((ZjʣFĢEгg"YQQY*S7;--mXZJ[TTVy?6mԘj֬)GnzY%/_7lC &MbޕIHfffrje?gW+*w"=kn`RVxx8"""r֭QT)Im+Uڵ<)#-- /_>-=)OchXd28;;c۶2@4h3@RׯlfkEFCXXXk$$$ 0\jtM_[ڶmd6m]]]!Xm?#""*j~cf-""dhX^T;w `L$?кuknmʝ5ܺuK(hN|||jebG]]e˖Ǐo꫊~qKn/}+'[$!Eepފs۷~׮]ŵkk0axՅM~ ?J2A{DDDDDDDbQ1ɿF?.*+sLK. CCCa2MpK;ٳ|%Z?pQQ>+?NؔӧO uf2ep9TzzI%&X+88Xt_Psnnn!*gL(sgϐ.9؋(73kQ&"1#!..N6y~u\faCũh?^T^nx^x!MMqgꙘUAeJBBB$?KJHH,gbb"*d$E?oz*X} z^z/bx!c`|ݻ/}dSzF\_="""""""e1Xy{_dI\PFH dJ+\ )Nك~쥞7K_P0#==>|R7}?1 J˔j̙/@k_YY> hjC։z@EYj'~:55?~R ""* 3X25iT dmt޽DžsPѣ'Ν[[[9@FİaCQLA t"1+WիWǓ'OdO>AGGGY梲5Kg綨W>Zhhb߿/mZZR_&~/EDDDDDDiH%Ο?77_WRڵG߾0r(L63fRGҥKE B"#a߾}pss}}?~<}U?K[غ-Z4>ƛYO8ȋ3FF>tiir bWs͋r S~[cjdd̚,o_?ʞ/e/m@@*Umv SJ)?c(M IDAT (ʴyW*"9 jʗH=^~IDD*s";ejժ(KTP ~ǦMrUv:@Ϟ.SL>M~>(d}Ω3͛\7YkIu["]tt49,կ_By׮HJA^GG'紴4l޼)kРɓ'?҃(,,,@lmklٲBܹ~)3gŗj""""""")Y|Sx %%E6jh 85jP.==֭܏L&رc1rHܼyϟ˗fplڴY>`B|l\zϟիW~3t 3g3$[yHne!k Kٲe`AWEVv"ML 'dW̗LL~L;Jx^eWttz>}d$'' FAf͚ݰaRcʤ{~\^<7JBbb333L*m"66 ^_sޤ~ntt낞S)f""") +g^8|̙͛7o`ܸBYKK ۷o2,]sUV\mff&85Pjs @W߿~mN8___}ӧOa%yf;w .@RRsOg;wȱ0WQ100 N< x: 77\ۅNHgذmB5ѥK\+CCCukd͟=}1y mLCC}Ν;d,O?aUy]v'""""""*(k'CDDP2e*fϞg0+}f-ZE2&ڵ%̡C舾}Ommmkڵ1ڵkz N>-ʕ+к7hҤcT\9Q9$$DrVjjj 244DPPnݺx}rǛ7VRo#::˗>yMɍxBKjӧO z`Ҥɘ7<۽} J)U70q {@LL ܡxduXJWSMMbf-""jʔBÇ`cc &BKKK>n޼ϟox-%%#GGLLm[^R}N8;ef̘{^¾+V$*8q4nVT^|,Y7w3ɩ iϘ9sܺiiiXfD&a{Ĉaؾ} oX^bdaBBԨQ5[?** Ç-UO8x Çb˖_PvIݻп022QcvիW1yD,Y,o<d;}`hXm۶eT$8%/ܰa\@b#O=]6"~JJ O{ ʗ//JDDDDDDJU"""""""|𳚚&L(˗/T Fѣ'֮]4h9aaaWWWlݺ oAҰq9ǯ^KnԗRT{cnn.AXZϙ2pIIIL33YYYʞ/CL8IR/RZ ׷w`ii!Ǐh%IJ 3!]^"׉DDDʼnHjժaբmK.A-qYSRRp=YߠS'_9eKqMܱc'9Jn5j`ٲB>|h8;;TRBy֬YY8|^?.5`k [ٳq"azt޽R **\*l/t ˖eI] ~ 7:w{"55U-[6u> SSS}ZӦM1`PB]>|'''͛h߾ `ll,W2i=|m8fӧrWGFéS0z(ԭ[ .P7$ڶw^89}o9-xu/l^u\Q.]Ƚ$%%رڵ >~(lԩK2iSL)))6m*n_ SSSq]}YV0e'LM4#m,EaOg%Qjj*F)۴_s!\PMVy?DDDDD&aa_\ tuu% PmPmeժݻϟ#))Iٙ_A|xAY~Q/ɫ8UUeV8rP󅣣_lYTZUϟ?K̑۷T6 ={F(ή׮]Mupp(X/du&RSS%`}NSH~}ϟ?C*U%믿Q^| d|6^*oCڵ ϒBOO5jGAAA /_^YWLˣGD%%""*jk1eꊨH,Z{"((n2@MM ߿ϑ.d,5K6lؘX|| Ϧ$Q-Z@9Vbb"&LI&zý{DGӦM͛7D2lٲ: cRSSmVl۶>|qRt--Ϟ=Cd%l<ƆPJe}vʔܹAcDDDDDDDy)qԞ:uǏ[c0Ǐ#))X#Ezzh̪^=DKH""""d_*2˧OqB7n,SRR^}.]} f - 666ʷnݒpIqiӦ(͛[Ѳe+XѤ>|D֭[ʛ7oBrrk׮ɶo <788On'd z0y+?ӱi&IuO eVm[UVVTڵj:NNNҎo e--m4mLRۛ7oOZ?WGI툈CnY8q8#c6͛q>|#PKGGjm{-ƌ-֭ ϱ^U~Y(s;ԩ0vXQ-*#FMǏʕ+@ra=Zb%ׯ_ ׏&L P'''rʉ (PB#۷իWWUÑ#GѼy 4FݻV7FpZYyNOO۷oq-\|@-MMM <˖-/PYc3fLc __ZKƲe1{(7%.ѿUZ_BBB.Ԭ֯_wژ^~- sssfZ>/С0mT}TY ®];͛1p\`Ӧ_V~dLt8p smwUܼyS(O@9%@맟6K.NV6pVZ bŊٸqCŒ3allkAay )Ա._ #tXzM-]{ƤI`hX!vWeѣG Xٰa}P=zD(-[={ DDD`-"]]!`Aja #8 kGʕ%kӦ nÇpQܸqrojj GGGtٽ{W.t54l߾s~o=zڵGٲeE7nΜ9P!<< )Br?S%cѱӓ QFx.D BӬY3|q^j֬kݬDc32RlC&aݺ 0 C@@@ѣGO?A>7n"d'OR߿[lǏEٹ-Ǝ-Zȸ)ܝ7l28q˕+>}bd#2 qb|+6n܀K.!**2G=ssstI&K>vvv}$Y{EDDDDDTDOM }}}:/='OeW ;w@`` ڵTVMT7>>cƌӧd|).5PQ1cΞ=#,z9?c{@F`öm[addcxή. ~}`=9Ǐ6lH  :X(Ϙ12:\]]aÆ5Z'eiݻ 2U*QQpuTִZ)ݧӱz*^JwSBBv팃]m<%%Sz6X[WF= 7̝;U}Ϟv)J/_͛Q~}|IUjժ;w/Rj5k&< l„ 36m*/_!wзo_Q&#Gr4QqdLϟ۷o# @̣K. V%K嫝6N<HNN˗/P!`llǧ31c|XN*Cwm;8.7ns~GJJ  ]]]Ue`=JK]]>ǏajjKK+8R jj,YAAAx-4abb KKK"R*3Jjjj}5޼yO>9ÏJBFw!<< QQPSSCŊaffV熥J˜1c0f!! kjj*)󞾾sFQF2%cDDD "-annJ̡޾011akk͛ -- /_w!`bbEk,%'"""""kt2V " ?gFDDDDDŧ[Xb"ڷoYf~(W^|ػw^ #)00PYaعs4h={Z5XZZ x=^xCsL&#r3""vĆ ѪUkt֕ammss hhh &&?¾}pI K{&~gtYrz7ojժA{" Gbb"Ox-]Z1uel ݄9; ~ǨQѠATVVVVHOOx!n߾ ~/1} }X -[6&l];`ܸpph<~~ؼy31I֭[2]vE^?ɘ4inݺv+W`۶lիSd,2 ׯ3ѵkg1Ǐ߮]bh}q*_<^|wa=r `ǏӧOJ/{{{ܽ{AAAh߾OƍV-[{2~j֮]ԤԫW+VIؽ{N:ѣժURRR@ܾ t+VTxTmݺhٲgо};L8 5Fz7o~G~ΜY2]v"8%QF022B`?uVZ)ʀXJU}V ̬ED!44ǎn߾͚ddaASS3DߤUj՜ BKK yf0+i,,,,×h>]+WʕC5d2T*wq"""""""P₵|}{_5 <|щJ"^nn_&ܹs}1Q#99YvAӦBYMM ZHJJ[QqUQL&(+J~e+֭ԸC`Q&LmڴUѿ?a[2eT2;vc?)Z_ KKK W~1&N @L;vB׮]u?Cضo>۷@Kܚ5kb}TXԱ[0z(aޝ;w0fhAQ󫯯ko ѯ_Sؽ-[6{jԨ1:tp%3gP^2-ZZWeQΜ9CDGGcɒ/+YpAO؊… (pcǎS ĴiS˗/,Ω,0d2_t&LP """"""""""%Ɋ{DDDDDD%:`ݺ (_|ײ@hޗ$zjee0nzz@-kXnƎ+ J*%4Zvvu;v4n7n7nU,--hbN<]諈 3kQI!~"""""""""e""""""sssCǎazܹsO>w # hܸ gěɓ=([[[2d(wnܸPDDETTaddsssKnڵ+c֬Y|@&水B~{ aƌB^] FX~#/^ kx > 333T\ 6&O&%%!44ThkdT!}=qqq 7ICCC{iARjժa} ę3q5<}1HMM`fVVVhΒ35o>>s?GӧO҂16UV>-$jѢo}}}Ij֬%jWJeI팍MDFSSsàAqA\| ?Fll,ӡ KK+izB:v&LϟիאԦC4i{Ɖ'ֆ)ڶmw]6QF\\΀Ѭ p>>{A۶ԤI (R̨Q{z3 Sʌ  IDATѹs 4Xr`XVїNzqqܾ}Ν5xHKK6`eev Ur;vW^ӧh||>k--\;99aȐ!7e#W嗭yIMMž}{lRDDD(WL3'NN/BxxMMMtrt%}WJJ G`ٲyƜ9qax}֬pqUVHc&"""""zTTmÆrfNMMЂAJJɿIFbxR82GkV&DGG ?WT@*%EDD_=E'}BDTPщJ8,\@oWWWJ lެ"mqqqs[H1>}77W\˳ϟz*;v G͘1;vlϵNxx8 '`JQԞ?nݺ",,La49sp"q!\bE4k<DD_H|A(4XS21DDDf?ݻTϏ?2X?&,,Tב|ZIIIݻ'|}}Eۭ:uŋ}>({9:urŋTr;vltuuѤI884n߾[n"88XOahh &wޡ{n@-mmm4j@@@|CDDD`РAhժe1k1o\ 4vvus{^СTY۷OT^z1过(/_uŋDZXt+W0d aᰰ0;GSZv˖-1С#077^>|RSS1ctj?K.AJ줙ٳg!44T(CSN6hP7m y]C%""""4ڵvD KKKXYYAGGϟ?GPP^7o6hƍ/\LL >}7oBpuٳ[xJGGDDZDDDb.aWUּ_ ~͛7 euuugggqYQQQk׮ɓ֭>SSSagWǏAձz`u2-X0{) ?.+V3gBncǡB ;v RSSjDDDDDD"TnzMMBQJ֭["))I%cmXd)>HEEDDDDDDDDDDDD`SN ?cvC ņ qoDDD8϶j3 1bH,YSNb1X2ǔ|puuv„8нFDDDDD6.](\v w(DGGCSSF˖-Ѻ7([lq_#** AAA*g|g RY)S QZ5ؠs.%na ""E.0HDDDDDDDDDDDDm_eVd;ʍ7F͚5%-[,z_&lEǎlۯ_c[!X nܶ(<{L*s׮$6ߡ WX#"""""=z@={()JJU> LWWɪ jذ!>Q( bf-""""""""""""UΌ | *[YY+Z\eii%333hjj"99Y$ylii)L&U=,""""""*&N'0*?)!Q!dr3eR +*+>{KjWR%}bŊB9::Frۢ-*+bE}YUkʼnEIQ8X+..NR;e13R?[5eODDDDDDDDDDDcf-""""""""""""W!*'$$(>!!>)\?YKhhhC}YUkoބ(>$D\_OO/_픩}-1|T{e}ebe*Y$266 D&&& j)Hljj&mQ055 FDDDDDDDDDDDo(VZZZVǏfffE>>6XڵkիW/`ׯu@ժU5X+$5?.2XrC[[/^<￟ٳg8wl}I ڿ?>}$֭[EMJjWT`ee%<^z%ݻTXC#"""""""""""*d2_1e*o6nÆu/zRJIj{=Ǐ e}}}tYRE)1LMMŖ-l[)aH̬EDDDDDDDDDDDDyjzC B֭[3g6RSSٴi:FT˗/RÇ"99Y6h`aE} ʔ)#wډG(q"<<(GDDDDDDDDDDDT0Q+WFFF022B n@DDDDDDDT̾`2e`ݺumїbAAA2?W}ʔܧ)зo޽ ߿^KII[СC{ܼySnaaiӦ-:̞=G('%%aQXr"# #KfjjZ%""""e U_LLLq?-));wdt E'y|J>~\۫Wn mh̬EDDDTv܍8s\q(O=pqg`6o爊ѶcN:uRm޼CRR"NiӦz000ݻw _!^UҌ9 nɓ'd||2,_ VVV07'ܹ ,--iӦ6?~Ļw_|xf͚ 뤦~9?}EqmY`) UAboKbc{7 &QcAQ)JQ|Lvf\3s]Dg9'**Jx/DLL!66ͧ'KDDDDS(]3f̄~q>~(BL&#hbhjj5WZ/`QCFF>|4 vF;L&a/O炃,: 7nªU+>gDDDDD_zzzXdigڱc@nEŋ8p :""""x>} spG!NKK 7n(H_C 6lX'˗/bѦM[?իW4n ФISvܹ Xn-8&&0mt.]N,444D9WX1>شi3\]]a\櫰dz0t0PhU+IQx>(Y$ ?¹sŢXkB ݿ%rgjjZT)}5}n=;w˾vvZZ_- & QtijժhذKNڥKFɒ%%yN斖γ_||<]|^>allrʡY(UTD#>>sQ\Lkkkd>󳴴D-Шfq9͛dddUVE6m`b"ZVn瑑y&BCCss 8::y9|1ʢe^'SN;:y|($$$ȼR?ƅ r:ZjCC֭[r2{fZСC?o?xF011AڵQVO$""""""S&'' _(S ~i~iBCC700( 333TPA킢Caa mllfZ,_/^#22ii鰰@J􂷦&=ڵGv/[ `!2ǏqIݵk7,[<~.{ SnGDDDDZDD_ǏcÆߐk>5kZj χ^:B{0a<~7L:Eh_p1›NNrD 0v88;(1c:9EhhfQ֖-[nQ!Y+WBpwwG-<ǜ.\EΝ*cd2t9s| y9mT=z0_9sf#$$Di?S̝;P>}I&+J՝bEDD`%&ʕ+cΜԩZ?y&MUbA?'GA>|.]5?IN>  ((He&'̙vXvW1u}FMMMIc^r֭ٳg511E֭rbSdGDDDDDD_VEkkkkJIE.A*UPJBs255YQADDDDDDyxݡCIVv/tz#FvmRԂsͭ'$g-[im[+AM4\v 5kRYϰaCpX > ݻwK1zhIc`ʔX~/^$9ho3g.bUZ.[6ZDDDDDDD/^>eW"""""ʛL&Sz;kQa1bDQU ~# ,--kkkݻw Ehhܹ"8Srr  **Q]PR%[ ##!!! ÇqYrPellFmm9n􃯯/^y={uܔą DǫW{{TT ?~~~Bb||< o#jxx"jFpp### Ggm,LMP5:vtk~ډx-N066Ve˖˖ Zݻ0i<#==6l8/_4  ^~/^11Q,;vTYu L0= 8v͛-[m۶yLZZk{@ IDATowBQv/^<<= gA…ŠPn=Z[n?XYY)k]z&Wko Xkɒ QwJ7iTt3 ѹPN %,, pI,\#yK^陈Y+k""*Eu)lsq͚5 ^EXL&Ï?.aawC~ǢE>yfLCC7oUYϟv܁']9Νk6ƌ9s-h~b;5-X0υfϞ//OnntGt͸Gs-R|L0V[ԙ3g< Cy}VY?S UT scbb%K[±{`.>|B,hѢ[xx8:t( GUN]I9QRZi Z2 G.8uRD<~@411e)z$*'Nzbb"^~UYgϞE~}E* 7XnCq$''cĈ8~(L< X] Tddd`ɒX~޽ ׮]¢3$""""?EDֆp‹ϋ8#ADFF%uj 5j3}ݻ8}4v*aݢE lRR.rF),Gjժy<c9h!J>}@GG<ǐ!CU.޽{EyKcǎի\T(z1-Xtrj9v%0x]Fr>EI%]Wn޼4h7nbccs~.Ͼzzz:tƍ/k#Gr~v3j%i|"""""""*>X+*UJ*aprrۻCfVмy}X|EIDDDDDٳ=zL&C6mЭ[wTPHJJFhh(BCCoooDGG)PW~WaU,]U\Ç0P| ٳ'o[[[ܻw7na=#O-[uƍU(jѢ%Zh#--~~q 1{x-Z|aFkcvaW+ccc̞=Ge?)7Ǵi3/~7qƣ| }LLL %efϞ;wj mڴ,--3Þ=ׯ?[DDDDDZDD_. T\EFлwB)|*HUTӧd^c5k&_HrsVlVܼP닔mܸZ888ڷnݔtMND9}8[v^n777Z.mllPlY/7oTɾkWfn_BV;} ,ke%X}(]9ڴi333DEe^>_+B[[[Yt U'ƒ """"""H ~+WСCx=@SS[nSeq*UNj/sg)盍   rE\.Kɓ'"11Q8ְaC~ *UnݺG 118uyÇ͛ &Zj)Sb޼ؾ}p|֭hݺ-Zn-y΂qXdɒj[ncL&*rq$iU_ ,,,|JtYWVmt Æ ѣQvL>MDDDD$їo߾u4L07oB.]ѺuԫWE={bǎ®`?ѣZn&MHᖍX[[Q; P! ?Q/_ߛ;~fddd{ٖEDD*V()rUBBҸQNeZCCC#Ͽ+z 2DUȀPR}2U4mt LMKKKC``6lXgppǶm[&y|L2eʨݻ8} )GH-/߿-Α.udщVVꝇۻw>)SF9*VVVy_D^nJLjũS'1y$TnnݺjADDDDDDDHw""""""/*+-ݼyIEpZZΞ=#mllERqϝ;+jO4 y300#jJؙ3Q~}I9ua᱆ʝӔquu.WѷoϑSqg-"/6M]#쮔݃ݺu֭۠UXM68s NOX0w@s1! ХKt r >~(޽{ƍ*eJ*I#55BP!D7n<$_aj] 3&xuņ %!E(<< K?44@)h9O|sO>|P9$hjj"-- @f1:rwI'/yrCС#̟ 3kLk,""""""LlmmjjL<I2o?֬q&;vD  -5@f:gddnPQw%KTR Rnl۷=^&Qqʕ+7"55 ^^2w @6mD9^zy|Dꪗl.rG@@䢞GQс SIoի/>~ϟ իC[[[rԩ /h[ἃ֮iDDDDDDDT/GDDDDDDW/7;w7Q|ZZ|}byptl1cF#&&PrɹʯHr(LBBE vՙ_UEti:QAQUŝN]v>@)99IIU9ѥK\ Bֶ<4i` 2v?[r\\:RJO>SS3|ͷ8y̝g^\| z-*ڵgL0ۿ"##v5Pnql޼[l5&--Mwメȍ?ODDDDDDE C Ő!C \ĉ֭WՍŘ1?]`aj 3F&@KJ# %SFAQ6DDDDDGZHLL$]S44F9rs3f,<<~vfx̙9sY@% |}}1lpQF%kQrԫWOi|tt "y2R uÇ:tn&y!8пkv0zhX+<*U<0tPK>TVm895U ]%K*si,\رc՚СѰaCDSRRE;˵hRz?B׮$-^;?~Lh[YYRX҂#Spy9\(T ӧAǎ6M9Q[B0i]ʕd~q{_3'krJrNӧO%ǒrVVVBjj*ΝEQP֒%KdɒϜ Q񢭭 7w*JXt ѷoԮ]Kiݻw.*qttұ돵k"99sw//Obҥ(_444 Y︕+WǏ}mزe30`@4m ñkذapth֬YshiiȨ4"""Щ ֭[vڣD2wr*Ǝ_Z1t0㚙aǎӧP}6;v -ZL'==>ĹsgqQuEZմi3 fDյť dܼycǎApp0455all(I󸻯{wE``ڴiCa!(_<ķDEEի8~N~j gϢCvpw_zB.C%K|455ao\ LLLc}=о}"ΌԪU 'O,4(qm".]ׯaÆ7nQ9d2[m~ @Ӧ0|ptS&M%J(|ǏEcgF*U1bH|rͭ7j׮7رHHH=oee^0yWk2;//O\z޽Sd033CӦЩSgmVkժ#::pEOOW7n3ϱjժyƛwBPPyMMMncƌY~Y[[K6m?`m8w?2zpqږtǎ_~vޅ(=c qkժ%+WNae˖M IDAT󻉭[ѣNbY6l8ڶm+i\33sϭ!qYlذ^^ EzzzС#f͚%*Rfhڴ||.W4~OPbUÇ !CX o߾E޽0`@t-וM>W5h@il/ƷmۂkKZsgWZO<?cǎu땮+Tz*rcǎŢE c?V޽{1ctѱ~Aj:::oq(,]?Pi 5R.dYr[l…kedd~"zG9sbР̵FF._M6b*/""""3f Eї}E߾}dRHšL -kҥK{Ν;sΈ[\9b;t:9fҥѩSgt@Jш@tt amm ss[l#""2,-`gg'yG0e444 WWW!8bb .- `ii v>}O|<=G&a!UrrXOOz>L)S"&&fPttT/L˖-.xVdI̙3fFp DDD">>氳wY444PzuT^F@bb`mmk#bUEDDDDDȀ0gl=5j[/_fffHIIAdd$.\8;w޽BƍFJnժ5*T(o>:wv%˱v:vVwDHHlٲx ~~~pw_W^ACCǍ7$رp \v @}aQhѢ%ׯT\拓'O.psG`M?4447w!99 eʔA2}_yt?ϟGzuaff-ݳgO/_AҸ_';xf̘{bРAR *W333|}|}⯿VU24QqP|yêU;}Ytuuakk [[&&&>.h jժZj2&*Vs666e.sss؟<͛7ek STJ*d2TP*T,1K,%KY!""""""ag;޽ w:HIIVZRR%ٳWT̙aÆ=zG)+{Yz;N"e\rY?55Ug„Ғ\ =ٳ'޽HKKÖ-e\6k[lsM_~_cΝùsc/V^%̕XMiӦ xc\m){:uЖd܉X p]w%"DGGc˖-BSv[""R%==QQQB;ZDDDDDDDD>\UÇJONNR8YftҹߵkWaٲy 8%K[s ƍ˗5=y`߾}yr 6 s΃\#iի ==5vV[Yn2e`e TNGG7nBͱb PQ[YYťS:MDDDDTjժU)W^Դw! . ..NhX3(Q._H\x!"""֨^*ZBV!<< _FJJcoߡy?s$&&BWWVرʕ+7oMMM~9`1bΝ;ж7ٳ;SiiiaÆӧ݋K|kժUu]]]@=Pn]@ɒ$ӱ3:tG!<<ለx-*lPZGڵ1} -2nx|S#""`ii%9={{ܹz ^~CCC%:upn"""""""""BLL4_Yf ttt=0+""""""""iEVLL4?"#CK}444P\9ׯp\񈉉000DEE!!!@ VVޤ'O,--Ѻuԯ__|rz)Ο?P@SS33s;u0229;<=zTiWUVRJQAD9{нwYEQRJTRaڴ6mzy5k͚5<;wΝHNN5^NNN@.N)*wCCCUVEժU秪]j׮v1cJ8pgBΝѹsgXV`ADDDDDDDDDE]8p,,, +""""""""bӧ?P8~RG&!22f;v%<'VfPFMQ̨Q#p% Dzz:Wc-J-_ Xv=4hVNpXd1>!!!XxqID K.2Le}~,""""""""",Zqqq2VDDB-N>}FADDDDDDDT؊E֧_hԨ9heddѱcS862U K1"""""""""""""U<Qcjii*UFnУ:>Qa*Z 6L&x{{ +T:uj+!ɩ1|}dRJCBB8p~s LMи"##~~~Ett ==&M ڷ 7o]U;;;~"##޽+N<%،6n w o:P"\ mmN(_Eȑ0r(Zn+WIcȐ!BVbb"~ .dɒ޽{}"##ݼZl 000|  1y$\z-Bٳg lmcŊob1s 8Ç""""""*XҥsDDDDDDDDDDDDDDDDDDTpb-8p6n܀t/kG5$ [[-""""""""""""""""""W :OHLL^>|XqRЭ[w撲VQ;00@!O\U\9| 2222X.su"""""ceeg^uDDDDDDDDDDDDDDDDDD}uZm~r!$$gb۷q]=^lٲ}&V!&&&ZG]ʼ}tNd2 8Hh qw #!%Hh)(;) t<"""""""""""""""""""""""""RY 뇕+W 99@Z 6>$j畽K!jw2dW(b-SS3ta#X'>x!~jϑhJke咝=}LTZU܈ueȐ!D<%=<<dž֭{ .-SR!BԾ}?_yCZZ*wNIm׮]:%""""""""|+VZM)===c5nժU{dhݹsw #00Prl@@]n]'''QΝ;ʋ-wݢNIm!s,ꔈk 74ׯv{;vHݹsݨBL&M- QvQ}EwaQ]o`AQ@ b/kX%X[lw4F&Ѩ EFEQJ#R];S¾Bv>޸wcf͚qrTC 8̞c#"""""""""""""""mxٳDDDDDDDDDT,.YK___8u%K[[ ڧOBrr5jt`ixiן={iӦM4? BɓXj%22֭[ػw+;;C"""""""""""""JJJ;$""""""""/ZKꫯ㬬,t6mɓ'q|}*]Uhc{Zj۷_\2=ܹHKKܹ޽+BC_ ۷C nzظqܺukѶm[>} rcM6S''tU_DDDڲe 6mMI&x]yDDDͿ¢W!yzzb0bpkgKFM|2%++9oM7""x)DDDDDDDDTTTTaO;$""""""""/JyנA믛۷ot~QQ1EפISnn*XW*Ud2C\\G@UU7L&rb˖E=h``ѢB咗/_`QC"QFBB<^x!J#"""""7$''#::ZhKrիW={.\(Ojjp" { JQ%H;ѥKRsZ_=߿?V\UUU@ff& ݻw4hwQN8q=zL宽{7oބu<|P.QRJ,""B%MҥKU`nnn9sfΜ*pd2LRhUtAܾ}~~}11.yf;:ڶhذa 믛?g.nbbG`ʔ)PSS/Ǐ#I$zBǎNT5_JJ ?~,1""輽p16-[^Qi9t v6m0}LԫWDDDDDDDDׯPjUT^:::hذ!ll@MM ԪU%% p]Z5TRE߫WcmmmT^]t=666 Ec266.4鉗/_ 66Ӄ>LMжm[a#ܒx.\@XXP4hΝ;COO AVЪUOSQ?yZh?)|UUCO`!Dhh(bccuڵkCYY^ U.W^.21Yf߿.f'DDDDDDDD_'O_6#,oJо=.\F͞= gΜÐBڹs'./:dk|ѿ?;ѯ_?QLjBh|+W̛7ǎ8ݏ~TTL/d2=֯_w8d֫W/L0 -4%{\u%Kr KDDDDDDDDT*tViٵkp\zuӷL҂e>X,UUUܼL'"""""ŋq۶5lp 6,""""""""l]rCAvvBSRRqCKrppCXXX8W _!cc%%%+wYKHH8\|Yطo ~GBBBB}{_3a*|˗C=v8cDDDDDDDDEƫWva;,QBC_oƋѳg/BWW CXkܽ{(j{{{ׯɝ**YcecԨQT?Vs71VZ8NIII\\\z'O׮]7FF7@xxBCC,VO>ˑ%%%4hajjPܹO tZXtVFF/_&5447ߔcDDDDB[[:::H$R 66Յj#J LV|n222TZjACCT珋CBBj׮ MMR$ĠZj^z7)JCbb"233Z9FCDDDDDDD۸q#%0qBwСҒV~}!""@N &;O@@޿H$J?FIIIsvtt(nx8qℰ\]7*?ؽ{7Ν;ׯ#--M cǎ&߅ѭ[b9s@&7oo12 q%{N 0aիPk׮EHH@WW+Ww^ݻwH$1vB{o޼ߺu+N>ƍc ^n޼ rOK.X ֭[q k׮-Zݻ/_ڵ_| WWW"44TH$AFеkWt VVV]#///xz^ׯkDFFBKK ի@GG&&& m۶.<ȑ{WL85۶mgddɓC͚022D=ѧORKw.<<O{EL6l+o_sss=z9>?ZG޽{]1eܽ{72 =£Gm6L:?s6`ʕ СCCR7n7waQ2^^ةS o*ϛ7o{n޽_}v]]]̙3m6a"##o… 1o<\~{'ʷT*ELL ^Wb…ػw/Fk /vZܹXd߭[@CCVp>(LNZZZ?رnݺSǏEpN N: `mԩSXh<==#x{{a͚ڵV\)$N$==]Ǐw ĸqD m 5>i v"/Ovv6f͚۷{]KK ߿|#Gbݺu8qܘիW^8~8xM8 {Q?^HM]]RT|ܠ[n}: ..Nt^IIInw ׮]{(ٳ'ܹ#wMEENBpy:VVFcǎ]SUU"##伟-Z$W(&L>ȩR III&ܹ9YdeUN"/򊍍СCq/+wUVZ+5 ]l/˜1P?ٳv ;w.興򧪪 uuua/_ʼy]y{{%/1Bƍ/7wVZrʥsI=xABĤ.Ə4oo:Ĥ.Ms!F""""""""V.ZAApq)7{63X4_(..1g8w. ::W !"Zd(QKII _5Q~}"99AAAؽ{PJ&D&Mо}{yd-طo-ZPL!!!uӧTo+W`ԨQjKmڴٳaii ###dggѣGp.]4'b ,X@RRR0p@AMM 3g΄5k }ȍ)ş C%aذ077Gzxɓ'eJJ F/[P~M[ 6X՞200={ ̙?\~M<Ĥ.LLL \lfQQQxΛV|}ƍ/ SLOOOHJJJ*x\񁫫ׇlmmE`gg;;;t cƌAff&R)&M;wUܹ38p.\ }øqD-DM6aܸqu$ 6mM{4h^Aq4hBGGG#::5޽{ѴiSukkkX[[M6>|p~ǎ1ckPi,,,Lt 6,2`˖-rЮ\2лwoQ?[[[ڢm۶2do˱x|%-[&UTTi&QrԩSDǎ1x`xzz SN dž\?---XZZ#Gě7oDu1aeeel ٠Axer2fllѣ]&>yp@y Ll-333SSS"""""""""b)d-UUTݑʌ Dŋ NJJJѦMBnƍϟ?; $ꧬC ^z///tСR)8 ԩ;w۶mB2̘1Ǐ/t~KKK+!llܸQT]0pssC&M 3``]vv(ODܜpS8yfV ۾}(`ԩrZĉ[.zq޽Mʏ˕aVZ囨 tZf1]t 7oڍ7&ɭe˖石o'TRĒ%K{lr3X~Äʅ.];B2SQpb=ؑ.]RX#22Rh4`yƞ=ٳW6 """""""*+Gӧ% 5fjhjjE֭zJصk' ##~~~_>TWRRBq%J&ZYLLZuׇDDDD!J]aLYYǔKC?/qvqq)2ꃹs 111#G%kȑ#d- bVQk\rB{ĈIFF( .T(~'''OT8~8nݪǏ/4QoFHr*Hג<<> [`֬ 8ys>ػw\())ظ6#JMMp5,^5KйsBm׮$(q˜hoYpaܹ\ֳgD?+\jԩXre;**++ ?cΜ9RBBCCTHzœ˗/ mcccW}7LVt޿[qW VVq52O:yխ['<<΋*5JῳjԨΝ#7n@*rW0"""""""*Y9k;wΝ22>zCAmoPQ 4nAAx{{ 5i5j9887o@zz: xK󠔔hjjS4DDDD#22hڴ);eDTV1{]V>ׯq]ظ%"d-""q֭[kX d#GwΤ$aGAhӦ ׯ_d@.aii)j?zHd-sssϛTxpܕv,JwSf͚Ui+cbׯo:'?Ça尰(x>>B`` Q~B+;88ZE||Zߪn(( K=ADDDDDDDT'j?}*UUk%emmMMMqmݻwWCb/L`S޾^ˮ];R迀@n~HKKXZZMT9 HѩS' 7pߏyٿ(9'wW~VuuuK>@N5ѣk*:::$$cذ]GDDDDDDDǏ< 5j_*СCs[~k֬7o;:ɓ]7nܼXK___8}6BCCabb￟'83gn]&''#22fff%3 )ⰰSSSѣ2]oȑ}c___xBh>Iῶ5j$j~ȝC@޼y#мysQ8DaOΝ;c۶m pÇ ,uˢkkk̘1qqԨQC/F"W)晴jQQQ%H:YYY.h/../B&1n\|1w.ƌgY[[VZaeeJ*ɝR ZhQ8Η~U->±T*EϞݱq+N8W}{UʫQF%B[&aӦٳ;|}"))I?==ᇙhǏ+DDDDDDDDDT0Y*$W pqq7onyIΝ#];B9:o\|LkРǑ@EסCQҥKd9?|_*:[[[Q ]I 4jHTUh~ ǣ(,YDtQiСV^+:|pn]7o˗ o/jߺuKubhN3Y@O"""""""-33'N!аa=֭ 5k'8s0w-rnUUUmV|v\viaAI\_hڴЎIJeK1n}Rxo߉o>05 +ݻ7o CCt  ߿/CDDDDDDDDT0Y*$_߫pww;Ο?_| 5P}Zɐv IDATbEU"*HZIII8qRRRpqἍ 4hP\͚5оx"K7]NOϞ=Em۶!99qiiiظq\޽I$1Bh[l)~v]#J*ojA㴴4|`cFh߿ ///رYYY ҥb2dd+4*.]@IIIhoڴqqqQ kx.]yFtY8xpU>ȯV V5k\AII u^9~y \]76r*m:!DUTTJ&} |SSS4oޢ!"a8z(.td2PJ"<fBZZ JJJd ]QRR?' M{{{ԬYS >>^ҿvڐH$n+XaXti֬YH>޸zTlllwSѡCbөSg;w.ϹNE:t(*U$k׮}}}֔H$h۶ЮW^cͱo~ܹk׮p}11jZ7kȑ۷/v؎3g 88H.9 tuuW}пic\PZ"_ 4jh+-Tk*}9d2~AM6066AݺuLDEE;>VVk&MO С#3ŋ탿?QVmQW_URSSFCk3VVQNw­[p?xѱt*UشLpn}8s4&L-Z~066Avv6L^R# ĤCOQj}"""""""""&kQwD_4?#F@xx8/\ .zNNN0007o䮍9լY>>>pvvp޽{w^!#+C?p.** QQQ~U=)o?3233vZ\pp\bV^| 6mT8qƌcǎ=4i6l;R*#!BSNԩSKD_D'vw:233ͱg^ য়c¹ǏΕ;Y I[n=~akvW?wb*L=TTTN)SBYY?XHŋ>DE3 fjDDDDDDDDDDDDDDDDD%wDDT1n7nٳQJƨK.ؼy3zZÇ;'''ɭf͚8s v333ǵh-O?TuKD"K͛CMMC*+Vرc077/1ܰuVH$yRR%:tNBnTUUѯ_??+ zA 1zb٦M?~ !/6lXtD-UUU : ǎz߯_?=zVVV%oԨؾ}U&w-w -Zhɓʼn`mm|TAyf%ZJϐ! ;&z ڵ++kQU `ƌ^|w!-- zzz000LMMѹsg>}::w,: eeǯ#GbԬYSNNN066.ru!!!@CNJRSS644,rC&!&&HLL~i=z4:t Ĕ߃ӧz ___xxxLjT*EZ`jj]C%NFѣzd~]6 .3(9n?OOOLMеk7ZMRUUfڭZ/i^}-mשSg=նm[Vlܸ1>w̙3zϞ=C||<- MХK89uB}*DVll ޿OL&};99^wuDD222+utt%T9py<›7PVV>:vt*={ WX(+K[ رx5^~XTZ:::Aakk 55'+Eת/odd$$B 233qE͛pJӃgQpe}Y00 {b||<\G"66QѱBr]T09 6Vret ݺu+5W2[YY666Q4o޼D$'F%%%BWWsPiJIIXcUTTGGwhii¢aaaڭ[.rNSSSʝ/xUV- 0 Ul]]],"jۗÇ;F" **azp󰶶.V s4i*3q7#(>R)֯_m۶|jJXXXuBuY,[=*2ٳ 3l޼ %GSSڵǂ иqb_R&M'&ny8 6uaʕw1-[={+۷ocq|WR'3fB"kܞ?KAvvvZj  >}nܸ1<<.*\BhwGSUUY *o%HJER}/QᒒpݦMonNDD%+y&I2fXQ{ϞwBe˖rZHǐ!z|>x!z.J+JZZƌg&j@vv6GG{ܸqȹ^Avvpss+2Q RSSq"(R)L3g(0Q ^zQ.ѣ[Z@iժ8pPv ڵũS Mr답kgȯE߿yǵkXfЮU~m;+Q 777]fMݻ,"""""""""""""""""",{~Q!}*De-6B"www|zh"ll%yf8-[U*ѣG*Gи ՟jԅ-m ???!YH*bڴQF tֽySSSѯ_7o5ܼyQQQ8 {lq"++K8WvmFFFY&k{xBߏbe8p`?@[[M4E&M͛HNN9akk :ٳ`)J9]]]ڢukk())& o̙3]WSZl333 $$w%^ ʘ1cfn;wgrЯ_|o\d1DwfMb&"`gg={bر044̷OJJ .] WWWQr״iӠoJDDDDDDDDDDDDDDDDD@j9"""/OH֚0a"&LT*Jر#֬Ycǎpaݻ?VVƀ;8::bINN_ ]RӧO7 M$7o(QĤ.V^Ν}=̙-$'e<<=._+.\osM>|(^xM6+WqΝ@@FF֯_w8o||\26oժU?;,mMUA*E 6T{/XDck71Xc/^0HbGMvE@Ȱ "UuW>ggVuws}vq=F&Ν`Y֦MiӦƕ>InN8 ;;[sHLLof޽{cPU#C`?Jb֬/8yZhg#F z@VVӱzJ:*7+Wļy1~x\&a_7իBE;6eHNN.1U&|YeddvVHϦXK]]#FvU;wФI|<_=z9UTT| ZaŊps(:t@nM6 UUUc< r?N:ov!S'O}GÔ)SQon)))x¨Ta2 ,_RnV . ==]RާNDXXО=9B7o!C#7nkז?nxܺugϞ3f8~} qӧ;SΒ "B&mmmtUsJJJ% }I45+CKKƫTREDDDDDDDDDDDDDDDDDD* Z8~&N$7c---_D&Ν;++++K(2s P9s}6ڵGFZ~044kh(.JJJ#j[XˍGjÇ&k׮k@x紷o~.\۷hbIQsvv N EHH^zE`HHdeXX>}($%%033sS$$$ ሏ`oo55BZZ222cݻwGz`iiYکӧOphkk 666|ǏA^ĤбEDEEÇKIc#FgqePbEۣK֭;Oʡ)rMr̔#V066h4y"#D{`=-&&VRܤIq-Ub׮=+DDDDDDDDDDDDDDDDDDDݫW777ܺu rV={b٨W^抉mpiʍQQQA6m0`zzzm۶ԩSVZa߿? ڵk駟pmJ*ҥ /^u8c 8k֬#Gڛ6mBnƺb޼yBСCh֬RRRn:ݻ+ IDAT2ի%K`yvZٓ}iXXpڵkʳ߭[PѮ];=o""습1z\ OOO4mTsW-=zBTZ544-P;)RRRزe3~Gc,_?|Wk(W uS^]tud r+t~侮9rL iNDDDDDDDDDDDDDDDDDDDTIRlll,:#G`ժU9sBs8p}1fdd~:ݻm۶a̝;{c333qMܼyݛglJJ &L'O;f||>>6l?CIII&&&&2Ǔ ==]wDqqqѣq<`Μ9믿p)Imdeea̙m69sǎy]""kٻG:b>}Jh7iD+g55SS>}<ԭ+5blg 6冋/ϟoW^رc쟃K溧(ff @c޽ܹsdرÇCCB@DDDDDDDDDDDDDDDDDDD[`ll(#<<\](33 ,B `ӦM2amm x2`mm KKKa\Q)))۷/_.󜕕jժǏvʕ+;lذAr%%..=z􀟟p5kք.[[lA1~x*W,L@ g**O9 >/^N:x-^z% 믿¼yyǞ>}:v!::u^ÇPW{|Zѣ'\]OΜqŪUQjUӧ{!~Ԩ ϑHK|%']]}ڵk+T5j4Fܽ{pwϝ;5bɒŖOYZ111~p)66 ;vw#hdeeair̍IzXv+#""""""""""""""""""{3f ڴi#>scYGEFF`ܹs޽[Pqشilmm*822ž={EH[nE D 9rvwŋj-\NNNxfpp0ϟS>nm6888`QfϞ`aҥ?~pjժׯ *^H&&.F潥iQSW-[?p"-[6+?ի"@jauQÇ(۷ҥvmо}{}-_SADDDDDDDDDDDDDDDDDDDu~~~8|0t"S󁥥%v܉_U8۷;ׯ1oxAYfիhذLc֬Yx.\(wܐ̞=[tlڴiq7n,Szzzprr£G|r޽{[nطo,Y"*SSS2y̚5 r/-۷1|Qŋ{;qْNU ؾ}P k.̘1C89fzz:f͚%:qFرCf *`ڴix"455 b-%%%.>,-ZN:BGѣ0AŊ 5'O 6i޼ѣBULLLa/9rp,##O<.JKv<"eI-ÊQ;vbPZ5?֭Ś }dee%@~Ǝ={ 'O"+++M6!%%Ehw W[LLMM>yf$%% ;bP۷o'bСvZ4mT8oQPiY|9|^YY ,zjqUh'OF޽7o=9<K:`ʔ)3&""*eX+gUxtU4qw-___xzzv {"v)96͚5iٲQB>DϟR&I&"}PSSC˖7op|z^ll,&N4o 7;LL< >$""""""""""""""""""u]x/^ȍKNNVQQU>ytQ12V^&''B[]]Β~v]`ܸqƵkNTLЪVe˖gddm m///dddȍ=r䈨=|I8;;K.$""*eX Mo?iv+ڶmiӦ[n 7 y^^wjחUGGGc _QHMMMLZ9E6B@|d=`ݻ^/?9ұcGaq|͑ŕ'iԨ$E-55U޽{c333tUFFFѣX""Z fdd \޽rR w:ѣ|R<ǎ_|ٳgM:5f͚'N,Ϟ=k`Μ9PWV}]bȑ^Ĺs0|7000(p,=*Hwppfԩpr.gΜǏfZr_a̙ ѡC4mH?7o1޾}={vcr߽{AӦMp"ooҮ]_݈ٳFA())/r6oyQ-<e066.񈈈%%%ҥKxxRSSE_Q޾HDm;;"ݻM@}<-p_jՒ<Ƹ>ʒJrl޿ѱ 5k*"痈b^z?do]0aDXZքpZlUxƍrF>} &nݺb-h׮=*V {ׯa '֭`Рy[%6nS|eipIhѢLyzz:>ŕ+WpYyݱ˚#,,8r0/>{B?X^aaa @UU3f@Æ K;"""""" cطo+۷oE5j&ǵ,qs"sG&)r9EΑ8==8d Q;##C&(zz3 eXkAزe3||o Êe┕mxA~ȐBMahiia8q<1b7PSSC6ʂCغu[c8Xlk3 +++(+ 66-^ sܹ444PF !66aaa 122¡C%QRRg{Ç*׷V Xzb-;v |3o޼]v]*TtttPyW 7nRSSc6m-իW֭1ldee!##&MՕz4ɸ{.>}LԊ1+" ssso***/ cEy/܋"PBNHb'eXKYYwŲeKqE2رdFXt ҐȍơCabb"i)S~3g_{<==쫡zWVTbb"|||lllu6Isejj3g߈vBJOOǣGEjjjX 8WRh;;{\vg_KIIw}иqcγEɓ[ҩS'L-[6BCC49$&&b޽xVDDT|\QFnݺ Q[n ~Z_ zUƘs`e/Ҟ9naaa?~KDDDDDDt 0@T={[n0776tuuQre!իW-p;I޹8/+掗M]bbB߿_)kمMG/B(C"7oAn޼9j׮]$N8 M4-E;3f̀bmڴ8}N<Qzum=z}rw Ç^y7oĭ[7*wJ* fcǎ ]׷Ajj իW7A;T9TUUѲe+maQ>5jXغu ~+{n1c&;4:^r300Cٳ7n\Zikk={BneΗ'ÅZ-ARx ;}%$$ܝ:u*J""*!!!Xf N-Zv:DDyB{ݺYEDDDDDDD_ o]4455all ###4l$ٿE %'' Ϟ!44**022B.ƙh msss}„]+U}doݺ5\]]QR%y/R.i|mmmQ߿HZk}ZZZvPP$ sO^GU``B'""*JeXFQFk׮Aݺu/JtcoB;44L/ZԏJr8yd ;kI'jS߿X]|Uy",p"7Fz$*TWWR.**w>2OʕQ^=ԫWXWSSl̊+\裚5k؍&&&011)H:WWWmd &&W>""*oF#GvtN[N\;v $Æ 횕 67n7ݻ_$,JDDDD_Ǐ 4h |^k5mT\>5k&j?{Hͽ;ׇEʝܸ*U 99Z訪Asӧ-/_qGDDϾXիƍQaDFF@VVV)dCDePQQǏѦMR̊ʋ c„ª@w֨S.ԩeee4hN:***gpp0; OD޾!  ]%ٳx>{occ8::JꗑKƙ3gsDEEBWW&&&ܹ3*UgDDDDDDD%ձcGڵ'U5551z=Fb}XGؽ{db*WGc„p<"͛7a%,""""Z<4hg'O.L3g?LѢE >\RT {ٳ"|&>|0Zj1c|Ҹ'OF`ggX… Z>lR؝,++ 6lȷ͛7vx* 9c~`aaQ^:uзo_9s@BO IDAT:vӧOH&>55&LkJ:U"""R/JOW·_\8ܾ›#<S"|̛ {N\8|\\ !-- -dCBB/wSNb y.88^^p9,^SLży%%y{?ڵkvQ'/_85j4֯Yx7o{ 5jXJ.NB{ k @c/N:شi 5jTx7o͛ݻ74yA&.?ǁagg/< ƿ-[6c1xs;rޒW+iyvb/Zf $kb͚ؿe9<<Ν;Pn]ZmJ+`-##k֬ƦM ?X};w`׮۷/֭ zzzS,Y!!! ӧ1c:2220a\t ș￱sNc˖-21Xt)-[&:u*.][B[[[OVV.]]vAGGGnT.]лwoDEEcǎطohs\Rm….9l0,\)))rXv'OČ3;hii!...qKC-k.vĈXb֭ aAbӦMzp===Ѽys̝;?޽{8Ǖ+WJ3u""zї,55ݻw%IKKܹsp!Iظܹs..{ӧ7bbb$瓙%;wN?}z'ЪUk戉A׮Elc8s"!!|3oʷ_bb"CSS=zT(Yxx8___I񾾾8p֯FVhL;Fn1^ngΜΝ;==})iǀ, >>'N@@@B7EǎM.w%лwgaa޽4 ""]3i$:t^^^/^M6ر#֭ ---xyyžjjj_9* Gر|iX[[ ŋ/Hwo8~p8{0n͚5& `*/۶m1x`YfU|8~'Tt,Ldz 3113gгgO$&& 9>]tXKUQGGGT+4ʝcdEBZh !""7n܀xU'sc=zD˖-Q 3xy=n߾ɓ'ȑrsINNȑ#DZ aȐ!EjPUUAHH+;wϟ?/SR$qFdeeASS#GCի{.?&Ç&,,jH#++ ӦM~ С7o[[[$&&c/ݻG! &&~;nncyBVƍѮ]{آBu<~B@@v\ff&v.ڴiI25kv?suuOy֬YBY1`@?<}T8>}I&o7{..\W9Ҫ}JoMB&011GwAjj*ٳg1\p̮H {.XzwQY}[ L::4Ls2dPeLMM ;v@n݄n333q%\tI&^CC[nB(++-͛E0 ooB寤}6l@ff& ==>>>)Ըzzzz*ǃgbϞ=PQQ)py޽{xp,""W^544Ǒ+)~***ضmƌ#\9::˘8qbM'''OpuuDDTXEDT6lkkNb-칸 06MMM&M-*ԪQ6l@۶71h`='N٫]7\\\0rH\<=T4yZZ&F K…<}*m幁=;;{Zjs.0a"  /GW^;wN<-wBCC MMM_3 ,zO>6l8wbg;vjjjXh1OwЯK;vFOOO?:ucǎ+91001cd&XkԨfK 455g^t$^ g^3g.ƌ wsѤIvU2;}W8pzQF ywݻde֩SǎZjg^1b$@"Q""""""$55>>oӃv'eW,,,D'Ou:DDDDTÇXt)\\\BTTTЮ];_x(++c͚56lV\K. SF 0#Fw\%%%,_CŊ+憔<ѿcTZ5cX~=BBB򌵱?={;fN8~8~G/HHHӿ_~KR֭}Fpp0BCC.Е7͚5ý{pI=z^^^ѣGW?(Vwb))eqN>QNDDDTVL4W)0.>>+Wښ8yx*7+++?~M:lܸÇY9,cXl6l J-IaiYS8w<:th'|I{uxx[HgϞ}ԩjժ `ʔE;,_'NOWWVAcgϞbAhh(/q<ZZZ8v87 bccefYV5‰'.֭iWtIƘ1cSZ} ,TSSKq·#Gc%2;eΜ]7D׮_ EEE?D߾}%QR8 jO:-BUca_35""""""baGXK[[544-석;W"""";wBHUret޽Dn݊|\ ȤTv튮]TRSSq5]NTR3""/ri'@DDDDD%;v$Ş>}ZxCJgȐ!ǿ219W B``%%%L>CR!QZ5}ߒ6ͩuuu&N55֫WOxɹ}\,*Eg˖-tRїZDDDDDDQƒnҤ TTߴ }}%Sn]qff& +WH,Y&$ŪWc; WZ[[K^(Wڒ~*ɓ'BҲ&ׯ/޽#_Ν%NجY3.Uݕ_IxeVwV+5"""*/1B_XXXiDDDTr,JNNFTT(Qv甘Xc dffJʕ+EƏ_iI""""""*.f||E_<ȑde!,9gWSS߽YWذg^|!CҲ&:tGGG4nCCB֒dbbP8ٳgo *Xe%&sTs'8)r_/_J{PV.kz%.BT*ODDDq d3---hhhEK:_HH0${TUŷxCOYMDDDDDںupEL0FeXXVZ;v9rB k"33sIq111˗pB$'uuul۶CADDpy={vԩnݺ[hذ]Jҧ;?K~uUV~DI⫂䎏+M zj؅EϡkDDDDDDDe27oWڵ퀮(ooo4h mjoooQaÆr㴴D7o@OO_nlN/_W"""""ׯxb,^UV%???DGG_>lR їl,LDDDDD}E^t q|sCnVϞ=q-TR2$"/w""""""**V$jZ.xb۶Xv\ 777ܼ鎷ozzz}{Glݺ  ,t>Eݻw Ƌ(tT$35OjFўC""""""fXn ޿ ͛(?͛hӦmq7oc/mggzɍ_}|u|ǦMJ̚lrvvFZp%ܼyrtuuѡC899m=FDDTXEDDDDDT::3flΩ~_gϞ^˗/!33of͚Tf%$#T!! ŇڅGժUEmEqp8>xy )8(yWV?7ژ8q6nE86axl۶m:055ٳqqXYY}ŋ={ԩS\'=z;;{QQ6mPFDDDDDT^ժU pvvFzz:"##($&&BKK RΛJri'@DDDDDD37=zX9X[[c„8|n gnr _Arejj UղN5+z'""""""*͛F 7o`Xl)!+-!:455QJFn]vpsnݺ" pM8pP㪩aȑB;33={ aaa8q8Zj7n@WWzzzMDDDDDTz갱#uVZv,""2ZDDDDDD倹9̄'OTjX[[A>rcs,DIs<^b/_"<<\Rlff&<<<j׮]˃ *iiit+WF:u˗/*z ߿#y???DEE 7~FFF000ڞ}BB?*ԈJ:pAzcYYYغu ZjssSt:uDZ]+L8oߒN`=ё?}3s.ػw %%k֬+J-ccc!00@޻,2gDrɓ--- vK usW^ ֭@EEsիڑ2ש,j>>EmXbU^h޼9ܹOOO4mڴ7T9:4GIjݺ \]O^eW 4~MD9 GJJ U9qUxyy!44߿!-[.o޼+W DFF"-- U:w B嚐"00\2tuu 8::J* +W7vԩU&y8 &&&w|8z* WXs)))P$%% K.w(\rBRR all k^KN!!!|2^|dddZjF.]Pzu,+"##qe۷Fܹ3j֬Igv9/_ݻwVѳgO111fffZ۷oQJAOOVVVh۶-*UTXDDT{ն^4`oZv+1&$c]56PbQAA~9aeYr+W̜seufXXqL:T*;厓H$J^ݻ#>%f̘,dee֭rVV ;~ݻݽ;D V744kвe+b&""""""""d-"""""رcېX~wz'>LQo)_:uE7b͚ ޻wnܸAm۶&x*{$Jz*ѶA}q?v,u۷o011U0 Xt J{%|||y+uttSc|,`]abff֯DD_#((`kk[nA*˗/GBB̘SM>qO .-m۶3g֭[hvZ_^"6l'SNES*$HЩS'̟?EιeL6Mh߻wO鄝Cٳ~w-oǎ hذ!Ο?̟?k׮y Mֺ|2ϟg*ikkC9r$Zn]\zӦMˊvvsUԅaڴi8|0rss EyŚٳg矱o>O8۷ڵkQR%,[ .)˗c˖-EV/D-0c 4i\?}tuuq&?ׯG`` _H$pppD֭X?Xz? ʕ+_?~<444 EVqE,_ {UOկffyIuQXDDDDDDDDDT|L""""""LT\}7 7n_;wzU>} V.߯7n ~05U˗/O@ W IDATsqqRSSG>} ]w߾2er/ʫW][C43fmkAv?؟Y6l@FtQVWN:cTt vVNNM]v 4440ag&/rЧOW?:мy 4lP߿M-?)) (rї,++ PzT*ܹsxb>>}Νի1`}ТE Icdggprr*2Ykٲe>芤RǎéSi&%&yp%dee~-[ꟖB]]]aVnn.Looo=vN>M6W㗵#G`HOO/kЦML>]ܹs:tPJ3:Th?yn޼VZNFFN>Ν;3Y R֬Y T|W000D `nn.s8aӦ-x U^P1͛sLj{T +ڵ\c"""""""9s;BŃ/_bԨضm=addT֭|?!n~a8y2 .+ڶm*U*RʰD"ABBnݺm۶! _[Bu;"RVVztǨQժU˗/p} kGuq9;;#$$E㏓5j :: !!!ؼy]&QUUŊ+VRԫWpMcGѨQC4m梇yڶmU2-[`a배'С<=3 AAAؽ{7.^ ?ebUxA fffƒ%ETO?\2'\ -[6mZ#33pdtQF UѣG U+c%y} O.$jI$TT uԁ)={ .Ȍ=z4|||Dlmm{{{hkkv%U5 ݻX޾} ]]]tժU9h￸}Rd̜9SpvvFFFq !>33C zq> &ZS -,++ nnnB%w QNԪU 666Cxx8BCCq"J8p /^zu888Zjy A vD:vla[rРA4jFPP>sEnn箠(D-MMM4j5B*UpmHLLR䀼Ju={%j UV9(ܾ}>T!"ϐD"A*6RŦTE͚5KD" ѧZDDDDDDuuuQF<\zW^***}cdeeȑ#8r踹ɑ鯣]Vzgʔ8q8"##UXv ֮]#7|5kqqi a͚ՈI?D#Hr7>73f@݅mkddI%kbӧ+̞= @޹VG` :̙/?{8<Ο?ϒ)v--bcQ6l؈Ç pm3sg}5fMf͚aͰK$yFMDZ˗ٳwAEEE4nn.,Y zzzqƨTL<%_yxx[nUw=z___ʕ+3gh[׮]e_xwwwܸqC81cƠA\rn߾-ruuƍabb"ꓜ,jo2ZÇʕ+'8wM~^b(Q .!HD}1o݉M6bŊxLΝ;˭7 <<\nJVVZl_ uU.>>x¾nnnX|‡1klkd&܌ݺɯ jٲ; /u8L|8Xd e_~]xmiiY fR租~%>˗/I.333O>BbSJJ O۷+<ǔ4iΝ+%ҽsm{;v{2[. Mƌ3^ kkk0k,XYYaD,_-RKY={ZV֭qA®]u6aܹsGٳBe;Z*.\333*UBv.0!OHHaÆX`Acիz)Lz)>}b!""HLL vZZbbb܇~>'zȶ0? _/#55***055%,,,Qn]t|Msnٲ o޼իWpܾ}ϟ˗HNN:,--aeUU1xbUX"Ξ=???9rׯ_ǫW *Q8p 7o /_& Uw*_o!H0iOԩ3|}}gHOOlmb011U&M>ۋ*:)WA'y^U!gK4jFxx>}X 9@ cRą qQ\pHKK Pnm}􅁁R{ kN:]vڵkx%rrr```GGGD^ٖQ(41mҥ+ؾO:t"-- P_[ 4HHƬW13S W[}Ŗ-[peΝ;1w\Q2ܺuKx 7]& ,P8VXz :tJJŊ1c -]TD`` ڶm+ɓ MzE֭"c@5l """ޘ1q;;;\\Ǹz*z)o߁N:q>\ѡkͧ^zW¶ W҈.ڶmmۉgff@MM VSSÀ0`7rΫ81Yf<o0o|̛7dѧO'=UF~pC>[66UacSƖw NzeVhٲNOO{V {% ڷ;K&΂{=ƕ/_O(JD0am6uժU1x`3qD!Y+==W^ED}W WznE:$j7NR{ȫx9D* 444엖&ztttDհWNNژzX4hkׯ_G?8 JN֭jԨQrm2ZZ߿R/J'k UK}"""^a(Eم??ǘKҟd%6}:EDDDDD)JHQ!ڒZTzזV4|2Z%>S0a0͚5+qjժ%jF8wɓ1k,hkkX]vM:tc &$k@PP'ձcG]rETw077ߺu ^M]I%k]zU6lc{QV[BK.JA\2"##[fM<~@^ҥKYfRl\r D```YS>*}l.mmm#"σ ",YT\Y~AAA"##!J!JEo{\hjj"##J?7nDNN`͚5ػw/h֬YݹsGx 333؈w-ֱK:,--{ Q޾Db(L׹PQQAnn.t=QA!11dggBCCEcRZ_%Lu֡O>h׮4iR&KчJ Epp0,CWW<}X1}Vc``g$_(-˗Ѵigb_?7o ))XhQQQ~y7 a  A={gΜ+WDN:~ ֭+q_)0>[L"*AjEw!""*\nn.k<\9HIIARRttadd R9Nff&?mr044*|N ʃ]DDDT2?~\rh vvvG]PbS#JJ*%: *7o,dTTTGGGxzzXf ݧOƉ'бcGX333<{ +dL277OCCC~R(ߝ8^*޼yJ*󎎎.VVϏ6Dmmme/`ť&MI&6m^xŋcʕJ͛7cرSJNhhhY@DDY/`ffV֡}EDD ݻw+E-ѭ[7tCaBҞ=qO?Jbm۶ nݺm۶ ǤN˗_555ԯ_1tw2f„ШQ# 83l'݋---4oÆ Cv 7..ܽ{WoʔԒ;K.pu(w_I^)\2773uD^\9h @NK%f"""z?%U^=:::%:&M7o,HnM2&&&T0Y G[R)xNNNr "OJJU*-͛7oܸQ"UR~*/oݺg۷o$O͚5%\ P:P*eaЀƍL"""""""ݞ\GӦ*s}Ȼd-d-""RZff&, 6 3Súo޼qq\m@͚5566.]w֯_mO߻wf͚ϟݟ`cUXw,2?]BVnn.'0fhzJzz:N>ӧO{XKNNGTڵQBN8Wq4oaee3gpm>͚51h .]T.0UjӦ ? ÁЫW"_dXRIի(Y-)@nT5R*j!''C* Q>IIIpwիWMՕ?KNz9]\\`aa!]#)))8~w4h{{{}R)f͚ѣGMёY% 11#G5O:z'JԒ b)PQQ%y7o:&&&=z%FnN:I&B޽{hժU pqqAVVR9s(D3z IDATζmDGDDDDDDDDDDDD6&kQVX.JjԨ\ ?NPUmڴ#0a˗>}ܹ &X)m޽~׮]Ò%)֮7)ZXt|||R)<=Nju&о}e 6NNNֆ7o]0j᦭[̥{{{ۋ[۪¾)2y/HMM-Zѣѡ+MUgkٲ|||,%""""""*MMM\r¶7nDLLT'O`ӦMڵ+}v{{{^zpuuݻ#:ytt4~W̚5Kfllf͚ɝK&e˖8y?*]n]L47ȑ#Em,[ ¶/_ѣhԨ --V[nUCFpQl|86m$lZ*֭[qVѧ-%%Vy mcccl' gʕ˸vPbEQ_;;;899!$$pQ~zzz CPPԩʗ///@NN^j6;1a'/V 7o=D")SsRɓJH$Jsc*T䯺K!<<.(_i,9::ׯIII8q"&NTV HNNFdd$u7^*W kkkddd ..$.---ٳGfw=z1Ƣ{PUUE͚5.WŊ+JFgСزe nݺ /~o `͚5ATToh׮)\C@^]˰ųg%Hƍ}v 6LH~z(WTEO|||о}{DDDRSS1|pytttp=u9 o$___XXX(uٸж}}}7hd-OT*EDD;333aTDDDDD%cҥ%: \\\HJJ,,,`aatҊ斷MVV^|/^ >>{'c=z4/_BUU&&&R Eů_O>P,-- M*+\]]!J.|&aii hhhعWѽr ԣG#-- x`jjJ*sppC'kծ]~ׯ Zs$$$ 055؉>HIIUVe|k׮Ō{/<=ǗBDeѣGwᅧwNFDDDDDDDDDHhdf@~||ƍ+,JEm{0]-y<< Z燩S'j{x+tǏ 0m"c:*JWIJ$k)_@Tz >>^QܕɋzHR ssvv$^xŋ m xxC͚5E U\E4.99sjjjݻԩauo̜9Ch\NۃPɢvX=y >_V`ii)>;Dl W׎Eۻwⅶ*ݧTb$""""""""b*5MRQQA}bryU"Zh)w mEU oEk|q#`KKb͓/^m /s&j}A|}}qaæ2aϟ7n.hc8p\\\`hh_bb"&M$T:uaÆ"}dORZ&FϞJ<6""""""""wEDD=ܰaSF |sСCwww١rBRRBCC ˗8UUU] #'"".X'kYXXyߒC0Y ehh$j?}v<[[[4l#GbB]a``pN##c!Y+:: *Uf)TSO>-p%K`Μ9e'}4h nK.e}4o߾|||-(UUUƍ0#^*=ٳhLDDDDDDDDD1Y eee]]]ypR?G?!Y+-- u{T*-J{A9cddccc$$$(~4,_ƍYY0` PaQ>wŋH$5ann!a*55XRD-[[[\:t(Ј [@hh(bccKKKi>xGsx)SS3ԪU...¢Y3ܺuшBbbʗ/CCCzhذ444dƦ fff 322U1-,,^dl1118{,Ð\_"տ}VTAScuttϟ뛑o 㴵Ta<8w,_|066FJ 'J-`hh(SRR0!٬^0` bbHLJJ3=~ a+.._{ͧ&**Jkk2FL""BI8u$ 997oDz~30ݺuԩSصk{..mE8q>wg+^0--(b[.Ξ=  Ehh(UV临49XW͛7?>V\Y֡)ҥKشiS9nܸO:YҥKt#.. BVr`aa-Zcǎ֭TU?LDDHNNau"-X0k++i#?=zTh555'OQshh(Wb}RWGG͛3Qzua-1o\wFwEԪUW^Ŝ9EH$pugz(04 ,w_pp{ѣq<;]صO 22s‘#G[)ѱ̙MLQC Œ%KkL6Gffh̿vڵK;vfϞSd\DDDu6m /y3>.p=OqDDDD5R)&jO#rrrJxzzzW\Fdd$]'O{ԃ:ubrDFFh_FOkTkW+5nN_>zݺu,h(hӦ ϟÇǛ7ok$''ƍѣG&j{IOOGݰtZܻw:;-- Ð!&j@vv6o-[7̙3h޼)vGhh\L6]tR7:q8\\Zȑ#ׇ8y2͚5Ç&j[7]p:Vdd$ڴq];eZDDPϞhJLcy-TUUYd_~.Tݻs>ʨX"zꍝ;}8~۷XoHR|J)i{JYIݻfϞoً1c)k|+ nfffb̙ضm[EDDDDDDɓ'cذarI$CB*3e}A&O/_I&h99 իW^NNƎ##c(755]v;EիkwnݾũS XEDcĈ! t+V)RRRi4>}۷oYѽ{^@ZjUɻg&J1t;vTjՆ444p}\(@FF  _ߝh׮h LLLѢEs8ajj& uv) 46---8::A'~=BK ''6]fdffbذPWW=jժ 555!77ʕ-4P5ȫ#w:u( Կ?}2W\W^ ycĈPQQT*EPPvh_ǎбc'@V-p}-6n\Rellٳgo_YYYXŸqp% ׯ/poQc}-Z4*WԹ=cǎ%jժUvR733cǎm+W@f͋L{g߾}B5YfCSSfrr2ʗ//Ŀfj̜_… Q癐ODDDD_<~ebbRєlz "/HڵoMǜ9mGӧ {j x5?۷o+xA>}bٲ !{CG *`۶ѣt֭[v:*DRR"ݻׯ#(LY277G~}'OU945G>}*T&xѣ{$=zk׮prrBڵ`;vLcǎ jjj7oz o&qJz !!!Btt4?]]]UTAf>O>utt`ll333_JQBpp"##FFưf͚L<ɢl+++T*ŋp 33֭]>R/_ٳg#$$$ 33&&&Z*\\Zܺu/^DLL RSSall {ӧOqQDFF"%%\2:vssL]ŵkW^|077G&MTEGGcyRJ%~DDDDDDDDw?X55G(ʗ/}a8u$ &&k֬/L;3ggn]zuX~u>N7n^wFVx7Fƍ}`hhg$5gS住 Xf fΜUjqǢE#!!Ah7m ;wIx Mlذ fذa=k'`%jmذ=z#QI4hZ̫Z0aZDD %L}Equqѻ{0"6:BTbbhb-˧5{ ł",tcð¬y[f 0ct˗䜸'Nnn.c"77Wb=L6C$Izz:~u:N<)<~,_uB!B!QPPE#$h޽{$ٸqP[El,1QKsp &Ah֭"ZEHLLdmڴOa˖ͬ׬Y'1QKX@ݻڵ3g΄bN~=>ĔeddlrZu~}?CXX8 yBqƍѯ_ܹrMXM޽;8#eVz53fy t+WBϞ?|Ը1y033.Μ9?AUSSSl ?QdktRVy̙ Z=zmۺaӦ&j]p֭Od޽f͚Ym X56>>_Yh!F r /c)S&KիЩSG\rD- En]s_RQIL8S$xxt%Cii)?=Q߽ wwW]D-HKKԩAPmr 1 DZcб'M!B!R##jY[[{w(z))) Ɣ}}޾X455?)߻,VE(** SuÇLMٓS[999;)wk=Fi]|L{w4o޼vJJJ7n<ɓ'9;c= B!BZB$C6 `jjmACCC87W`f.uuq1xS6zcb(++CFF:޾MGQQ!SؤFk&LʜitLΝÇɃ*tuu삮]BUUxԌ=,LMkȗ]tAHHʕ+pvZϑANN#''9ָqc=o"|/^@b$$$c&LLYAAߢe˖ׇ"^i2PZZ*ºukpss+tuuo''?a޽ :::={N}O$xeii֭[HHH@L<~ Yh +`Ȑ!#,,'O‡f`ooccc<~xSԩSرc;GUojj*|}{!==9W8;;1u>|ׯ_cppp;\?(jjjpss ֭()) 著@?~ʄ=#55ÇE chB!B!|Y(U3g*' wwwVpVmvR#N:1dddд7{7~a@?H5Vme-Ӯ][WNknf' :TBMQC<;I!Ҁ۷a-8P~33y(,,a;TNN233--mU)7nƍ' UUUhjjBAANDN{AMMN"Q!dSS(~Rk_IO u?wjClc}|<" vX~!3Y "!_KΚ5 ~mQDl{e`ĈXh$IHH?Q8v(Svqq޽+MZZ>JJβe,,,paX[[31|>M62c${߬D- ̜9 #G$|lڴK,Fqq1`h׮-4SÇhٲs+6m:{fZ Æ [lbb9f͚ YK,ƣG)nHǨQD^zaѢ%bWѣ$7"nZM8ƴiSY gChz*,]>ē'O@!B!R͹?`׽H۷ى9x;>$TS$ִ4†0h &YǏæMѫWotU.S*0fii%YAZo>Qdz۪CGGY(..>0I'!B>oa8pS9sLLL$&9?nnnUtm&M0eJH۷!&&e˖Ko HLLhiib1޽{̢ppp@?bauEܹ/_Fqqӯ%aÆ@~8{ _G^^󙬬,Zjo#F^}ݺu wbAAAj">GP5rիWӧ{۩dCTT^|XNNZoйsgt-[b9N:x5s'7bfϞ#u~~>] . 66 _4E0l0; ֭"..`+Vp?8r0bbbPTTĴ 99yN:%,X96B! %kB! 郣G'8|0_ϑ/]L潭-s*͛7 2jߔ/{*cǎjRԩܖ IYYΝӧYYYLYYYgϞcܸ񰰰D@0@AAVZo3(**B.]w~WJJJXv={P$Wos2e >}FMh2%2eT;o)S0kl۴q@Heo~ ƍ % 4::::5H!B!B#+++8쉳e#<܈#qi$&&"1q/_inݺ;֢Hu`OVĝV9&Mcjj$k|dgvBtAB!רQ#87Svss=ܹ}V?X>~_ɓ'U&k]r׮]aHN~Ç!!!A~YY;¡CG8b}4+V,GpVlذݻp귲8q2 66&̘1cƌ؟۷X]cVU\\kנ٩Myyy4i"N}롢R)#44y^+V{ӧOt"ni7..mcbbBZ=9 !BWiŬE̙C lkZ Qll,ĤI51}j$SUbyIIIbrssy%&j ٳ':uĔ;L".]V.Y0aDV922Rb]vSLСТE˪AӧϨ_ȔD|AAA  ժfB!B!\V{Gk"_vvHJlW/'##ab/((7wCv ո}sԄϩqPSS}..ߑn dB!ӢEKhii1kUkWlqIPNM#%w&%RD[eRM Z<ݻwK.JзoZyos0sY[[ؘ)Wwm˓߄U&**IV1n;vTd ͡YqTvN؟)yyymsx1zD-B!v"B!_f͚aذaرcÇ믿Xϑ/>%Y"% χjcnn^zW[ONNcƌŌәc'NXg潑Gqg r 7nDgϞն4h04~bʢ `4qJJJpl``y'㧟FsQRR”gΜ yyn |}}1x%+ي] ѯ8%B!B᪨ JKKYOO :IM=g(Ν;s",,\D }=*OFɩ!՘|bo`*,,_߹̄Bʢ}8} 44 |>_z|(ok+##՟p m߾ Nb;v'㹹x%bb+ `~`AHN$˵jUb VдiSDJJ {?$hEEEaXh1#F G^^o~B022FZ\x**غu {b_0lp+(~BR !HovS 𥸹B8þ}nǐ!C\ϑ/7|~˖-Ŕ)Sذ{ִiNT5*ׯNگ_֎wU0` +Y+:H/_JOYۜ9/// v쒴nbÁ>}@QQzƄ 9M< gՕ3Z11wD> 5YH&L!B!BW͕zeiii] W/*ϙ35*FFFӨ[ Eh5İV?zlll8. I`enkuOMː~h}r}|!BlLۋ+((`=533Ë/v$$ GGGοrLX[[c۶hٲs888 CvS +++ߛ7obϞLYEEн-lmm8OYNNNطo?tuczzz]vìY3m[0ѥ7lllDdrssq`KJJXIi64ZZΎs]9bqNKK+9sVh֬5k?AxH6󷄢bB**Хvc988`ر֭22w؁ɓ's}?// hԨ֭[~ȿё !|~(YBDƍOZ-/e֟tttj?B3333k׮ xxi&sdK燃aV [j%߇>}SpwwjOǧ;,Fff& X6ӧ:uOOOhiix33suա]ӽ{DDEbmmms{>_=c}SlMXq>C&Y@lV\JaĔY*ߗ x<222;"{⳾+]݊<Ode;I{x)6BHJJ6)CCǧ4! ˫W7p+PXLLl&k SRRBfƋ/r ߿fj1V'rw쓵ˏNRRRG!v_*6Y+""  ˘6⒵22Y*U[dee&j ^^^`w/[[_x'.XdH0gggg09˗/#>>S'",-[t?~P\\o֬Y+RÓU &6YΝ;.M:::  `7Q"mrssÔV˼ѣߑMLL*$[Q\\CI__bB^E!|<==VkY[[ͨZ돐b֬YضmtR5V$uC1۷ocb ظq`oxcS+OPV&H޽{w^ʪ :vо}{;&  %yE˱br*'n qLLL9)BXw[ٻw*/dbbRmV~~>kŸ$8:/;;us>3AZB)++CFFpZ_kBFCDׂG;9YKx"' ~u77woewƢw5 #a ())C>|رH}Yي]%ݫ͝UrttĹsgr\=k?~L6mH1 u#BH`cc###~ _DFwQ򂧧P5Jx[ReVL QXp 9 v gp]cwU\\B֤oMV_ X\wEEŕf?zBegg۸}6=zTD7o ??FG!TMVV|>#F塧GGG[d![D>}m[0똻H=Cii)];޽{3ZCZ(;U+͛(Tf 2}ڍWm&L_CZf;4iRgB!xzzn܈ /Ϟ+PSSC^^Ґ$p2]$8ճbSSSֻu&+iԨPTT4رc?qD@нΞ=(((@tm-Oֺ}; "CCCY7x<޿X899Il#//vs&TUUY'OlقI֊'U_~pBi(YBBu-GMZ_44SLƍXv-ƍ}}z|=z 8w,._&,55'{uB!,:BCCq9?N lذ'NǎMNHO'U9ھ}{]_K*?),,=$3afff5~XS|((..i>!|~vڅ˗/Vz J!PVcx/^@ll,~gZ䂐tmܼynnnU D kkkz::ӧ/ٳga-*/cj*~gx߱߂s8jh,Z`Qܹ m2i۶`aagD\pݺun3 IDAT]jj*H, 5}VgcB!xzz2Zs\]]y<˫ <==mҥK3UNNj۶-$~kknↆ{ybݽ{Uvvv ׺O&1-ZBMM8ڵg ::Zlo@idd$:u|L 222D*9^NxtꍩSǧ;焺#={6&M@ps?92%D?QZZw"4B6-۷U1Kooox{{cUx Bz d> j}z [˫WPPP`}^9o߾:tƍʯ^˗ׯ] 8xTTGSSUNM}%xכBȗ͛ 0!RJJ ~7?mpRaq8x ,-~SX~yL ‰Q\,H:t lll0~do޼':/::SVVL-'i#ğ\\92[lAF`QMƂШQ#N}<|.]˜1OEFFGٳcSLƾ}z \wM&ՖZǏw!R7<=Xkd0f+;;;1ʓBCC1ztŎR))s,.63T*?ߔfqcccT(XEI]evYYY$ 'kEDDD=XXXږ'k]v 'OaddLO]7oBB.P00rdѮ];opssY̳`  Eii)&>&DB!_B!S޲e ={V1id9r '`)'N@ddIJvɓ1|xv؉XvfgOefN}I{TwUA‘Xo(*IK\+**BKK)߿W700d'Ν;; B!VRRL#pרQ#"9 uK.Q*((ӧwY&t~AbXr5ҥKgϞa-xTÇw/k֬Fǎ^"޽ļye ˗/K\>..1\]]'~2m=X;i… p^+??VCC;wdݫܻw/\o>yFdz}8~C`($鴴4|}۷u pEt{ZZZXbehnn-[1GۇK.Y.aBix`ccÔCCCY UYuz8JKK%!\6ihhz٬1D*EE%VYeIJR9r455Y^۫{+u-&9 `'U[׬`۶a/,,˗p?4N6q6DEEW|>ܞ+++sZ8B!By! `yyY8< Dw]>Ϭ$LEEݺutZZZ={ձpxll E7jԈIz5q?y0Efv\9i2gAt5[WW7֪iwCҪUkVU2Ʃ;Ν; @p3=)) FV-Yx;## !СC˫0!^||<򠦦Vߡi0vx8pТE |PD{2hBfǏ@GG666Avv6Yb)..Ǚjjj!kͱk}۷@߾p!cغ׮;rwo-[bܸ1̽tL8`"-USZ|S֭лw/<{ĉ1q`:Y=02]d6`cgϞٳgD;/9N!Ӌy~{v @R9/{{CNNbbb ३)̱!+,xeee7nܸgOmɮ_?a{W{xk[ fffx _[eee9tXlN>%Ƒ#qab군x=z~3(((mMM-hjjBQbqmۂq)ǓB׋v"B!?Ce%gٳGd/I`` |}}E^w<عsz#G0{f=z$K\W*..˗?>*ؘ)?~,>> ǩ֭[YeqIl:::hѢ"A(..8ߐC__)>}JJl۶ T6m~ґ4,,,Y6b=Ǐ x(1i$3}.1c?oW٦jXt }U$ ?AXފ9sV٦@ )Oe%%2 !&'' 999"!l-[ߟ]Ǝ77wlڴϟGq1/cN>JΝqf:G 22c|9999cǎ{tQK777ܹ0"<VFF:._a~W唜X%!!Baݻ7pMYuߟLP圝ԩ Ô׬Y'\z+W85kV..._xuٙ[;ذa=Zn]<''FdM2$|?bݺx ן Wnի{9:ϣ -[63@s ѳgO O9TV{3f,.]8q<6oZ*E{{2Ç?0efi_ydffr>B!_>%kB!R] ҥwvv_BNN?oBIIưY<oJJJ3dff"'=u`dd}}}Γ1x` <JZZ޾}lZZZFknڵʐ <^&ШQ#@X֯Q}}O|}OӧHGI R'O8r!B>----l w=IxG%qIWǢnN&Hֶ)KũD%y [0_x!Uϟ?g6m*&ggDD\PqMCC2u- |݋ûw\ۆΝ;sΘ8qz琔͛xQQQ{!={伋\rrrA!kJ!B't޽x|>_\o~MJJJnZ8;;_>8q޼y#vgO1ctlX1NN⓵|||`hhȔo޼ eKJJ3Ɂ-؏; nh[ijgOY–-09n'^Ufhh|͛Y햖"66=zիq==*wfǮ]{X+i?˗=]vr:bffq#G?ֱ޽Fu&M;27o׷7F vC APl'OBF$1sssL:)bĈ_ի'ΜW B!BXGNЮ]'jU[[[[8::ȨF+++...F-kUdeeae+̤Nj(aggOtZ:QB!Dě{␙J(ʪ LMMňd%xr~.PnݚU>u{jiiښ)Sn;|>GanSkV{m7?bw[&塝!BSNڵ+.\Pߡ/իW#%%%x& ϟq?PUUۧ.]a̱wcѹs'(**^Dqq1y-1wo0?2esssU ˖U,gnڶm[풒W^AXX3;pE4ib* ?fll̼/BKhBZN~]uqPUUe<6F!ְ/M!BH=Yd ddd; TTTGʕ+y&RR_,,,{۷={~9^\\OYZ8xTUU8.oii)?0ѣGDFa%4h01{.AbǏyC$QKKK wDxzz!$2ڵk/Y~~>pܻ'%''GG>>%yyy_#ٳgOq5kJ=&>I&L+++CJ3!::)))"ZӫMFDuMz_>uB!B ;;;B´h !B!Ӷm[&}v!//~Ώz&BQQ dXrE6ox<q`=?po$&&V&;;+W.g92ڤ)GGGJ** _Ç!##]gB^^չw/Uvtt[؄y[yVysNZ.]ٳg©dddļOHB(YB!|= >ϼj䄲2V|>u1ijj"11 1t0XY5XWQQ ކ[8v6aD>}^^^PPPeVXf-9 }}}4jڵg^lΞ=c hrrrb G\}ϜkB8 ZZZ"kiiaذለT}[XXS8y|}TP]a HHHΝ$533c}Woժ5ӮMw STTBp6ڵb]]]uk0CUU֬ǜ9ss&M} hѢ%MkB#rS\ylΝ*+!8xTTT`mm#yB!B5kЊτB! ə)1シ$?3~,ܦg &ڵ&LJ_t /^$MXcȐ!L>>peSRR 1KK+ ^<yyymێ)sw`{7-Zc٬$ڵ;NsM2?|s";;1XIpvĩSظqWo]h}tt4vډzBȧ!>͞Bj͛7˗/Ueee1zzB!). - ܹ:wTRR ӑ mmmC[[IZ;ر8<^&7n SSS&9|%SӖس4jpr'.4IһwHOOGzzTTT`ddf5RI}U(*Rɔsr䐝-#̜S.‹/%%&M /PEճU:^| wA]] 000dXWX=z ACXǸ6wVkzCHNj/::022/?1ô}d!++ o޼ƻwPR000@ƍ?xx< A*΃8|T!Ln`kk[aB!ѣz!''K,ڵ1!B!5OOOܸ!4mT$Ƶ㧠%K_*ݿХK8;;CQQ ѷz  B-Zx ~~еk78;;eVHIy7oҥb***b-PSS4.^ r˫65B֭qqCCCVӧ͛7cpqqAVaaa !++ ^u LŋJ\vXz^z8t N: AVƍ`nn@9s3E[ѣ4i8֭[\رcu=3eJΞ=Ô`011A&mێO!CZIOO۷qmDGG/)YB!_0u* M&uo9m65^phV_A&kþR5D@vk{:(x}B=!&~֭߫Zډ.D]--Ǎ$69+\ʒg^^Uz;(s!$[Bi^xǏHIIp!B!B!R|||дiS{nZ|rܹspQDEEť#:Sr!B!B!|(R#ӧO!B$"w ڊ˱br|]cDB!B&##K[ṉ3gҥKӃ;B!B!B!bP!/bCAAV/))Aff&ZB!B!B/]vENp@HH.]ooz렫 '''8;; NNN077޽{_B!B@Jڵ0! r}G@!#Q!^Ç>}%ƍ+ԩSaii ɓ@i U IIIذa=S:g3g0 B[[E8x؏;wbP\,DFF[AEE%b*C"$$))XKo 4m?B!k0} L> B!BH5.] wwwPTTmRŰm[0kDܿUgƌ_+R#661ؽ{n{{{1y3fLǿX'-- н6nMMMcB!B!B! zǏ,hv۷#)((wB!B>3޽CIII'##==ZBZP!:amm O^ix ٳg+yE<7 6eB!Bȿ֝;3iwБ5kV3~y1}4EZB-ZB^^`…pvvF*ғB!B!ke6 m5'BJ~B)7puuc6(DEE;رq/_ǏF en^ǎG-QPPہɓHB98.nܸ۶mkɬ[uVHKKl޼.T]27of%nz`-B!B!JfnncӦM8bʔ)2B!B! *{ B@ !+7PRRbr|JJJʴSSSqy}}}}۪)JOOӦ`QxqB!B!BL{HOOR1"##oW]ttT|D!B!B?kB)WFFFի7Ӿp<ƱcGv&Makk|9&L`ܹ-'//7oWj޼98k׮vhc !B!B!NʴnݺJ\!#99Nb~W]rr 1{D!B!B?re/Bȿ(jU^^>o >} ߾}c#GT\-ZںTUUxXG!77ik^XXX@YYׯ_+4"]˖-accprr]%'l̈́B!Bh̘0xӮ^]WCH>}:|}}X~=a``P+#B!B!B*kB)w;wF:u 7x "Rx<<<<0<@ttcN~xvpp@j:BM` 3߿WDB!B!B!T^^^ذam֭2e *yeB!B!BH`-B! ˖-Enn.aZVh%@\3233^,E!xǏc+VAg յA!HHH@ff k[n/!** I; `aQu"..qqqHHU5[011QQQER())AOO 'Y͛7HHH@NN6Mйsgk$' ''5`ddSSShiiz="::I(((իssRHHH7ow}~e2DDbbWԴ6lW&Wt=O ##c4lؐV*UpB=w… {J^!B!B!R(XBH^:#G.\$rrrpQӦְQ< WB(++Z5ƛUVNfi444J=B!Lo߾ũSܹ%XԩL`xjZE!(c$>} 033Ǚ3gؼO\<֘xk _-8~"##%v:puu8q^[qܹs[j>f͚[7{88 (?܄'4h..n=z4TUUιo_֯_ǴϞ=ZjyauZp9} u3>W֭[gWR JOO:u^؇M8p  ?ۥ㯿6ɓا^zpssǘ1ZE?|ݻC0y-b߾l+Su//o(++w v0߸q‚n݊;1kn@WWWx,Z.g]QU>}zcƌYhРA5rHY̘1WE!B!B!T. "RaFkСC0a"SHKKc s[[[|ӆnuV;99Z!B!B~^>}B\\@EEyXl)+Gk׮aDo|Yf,Ywaee%NN `k(,,DXXŋ|GFR^~y|gn8pP5o߾1)p?̌UVV7!#ӷFK`Ӧ U9?~fϞT q^ c/^)S$_dd$ϟݻwСkP1prrě7od#6o~ڵ+\ŋpETۃ)߾8w11n'%ޟӧO#00֭Gl%%%,]C `ܹ*B!B!B!rQ! ӢE 4mj ̰=<LUaH!|%O^^.7HL۰aCVٳgZ!B!BY>E@OOo>L> XXCժGBBqUxgΜu3|O>qP~:\]Ūhhhys￟#/(NAƍ9"b czzzAbb+WڵkĎ^z07ϟ?ݻwHNN漖;c\YJUӧO}1ӧΟSWRz-~X` 4iMj { 澔8;;b=ի0OiӦ033Nj @c$ڶmWv]~bUѢE 1avv6&L*a(ZÇ'OϯU]NN._~/_jժ011AvѲe2=۷oqM|)))PRVVMеkWhiiq kddܗ˗/͛71**066=5k^Ilܽ{aaaHJJBFFtQ:uꄦMdΑU߇\ǹ+!BEbb"LFYY uԭjjjُB!!ȥF)S&n{ <؁uNE;wӦMʶb}A[[CdffjժZ!B{444```OqB!g3l2m@;wnCE Eؘ50y$ܼy1c[[O`e?~i[YYaժ5h۶̙p.}Ǐ+Pv:غulmm@X!TJNNƨQqեbɒE/˖@VPfMŠ/of9zXU_֬;v{ׯg#`B{ &޽ׯ_1v\U* <#77G{y_REB<{ ƍETTiqy6ͥce@-,_ά$-- Łoߖ-_~xVV6صk7y),,DPU222U%D+V_~aYՎ*,,X\,AXf-ڷC:-[*21klZ,nkuÇիWaǎФISlܸ͛۔ɑk`ݬjժpp"5h+77:cŕ+A '99 ͙߻wٳB!nΜ8{,@WWoDVJ/_l/_B!俈RB؄rpjժ1VU-pww//^<۷8믿X޽ׯNNNƒ%K>B!5^f~6yB!畖lnnƐ!CYy<:CYYq<@-ݻoX u 79[}%m--->-[|ܼ6Μ9 WW7˗/{nEٴi#ÙvZpR jmccKѦMxzz:O*uobǎL[?Qbkشi3Zf˗/c]kٳ|>z/=+{pwg@-x5 ?:OVV>Rn@q~~>ag Oر=m*3P 0n'GJ!C0'O0o:|0+@zȑ 'B!B!DQE!D.###oW4440|}…gyj֬95k^`լ/\$9nԨkÈ >W];,NJ(K,eUؾ}OWbU֯߈ Hݻ7n ""Bb={v8k^&NRYDUbe"M4ży+byڊ+*{ p5YǬ`Hw[&;@U 0wܹ-e6233ѯ__`G̝;3gB>}_\=99 ׯ:ߌә 믿bɒeX`!WPP/Lײ"119 kf`b.\8777VERQX{*.đ߿B !B! m۶F۶9C! ^!طo233 BvE88 3+((`mxXl*Y055̙8Ĕ)S1{\\^ U2YWYu&>̴7aU+Fpvvƞ=> =y$s[]]mS0rs AGG'NY!Xl)kx0//7n#ѵk7ԯ_V}#,, wABB陖 !wx#==:::QZn [[[6:hiiSSSx111HH֭xi``uuu{]ŇgcNzrc{yAZЮ];4nlŪb)MRdeoN611rss'])M*PVxaa!BBx|iӦ<BQ3~?Fxx8n׮jVڶ`>  jРC\x}|Iʕ@;vlǎۥڵ+C>,X0۷oc'$$?fB!-999|2^ $''_zu=&)SlZZlm3m///,^={Ξ=êj ޽gܹ G}wŀ[L:۸UzuVVrr.\+W[Ԯ]'NHx<:E2m?]4h̹Kڴi#kc}ѻwP^,\X\]'t\~3fLcmoٳgݣJ!k/$}}m۶ nݺPQQK vssEϞꊎ;ASSSs*ETTTP^=cJDZL9}>ݤIDc۶m 1%'*ӧavF9]444>}@;իp֘urzo6W_ cIXFFŎޒT~<嶾#NjVڵ{,<;j񙋳g0v)X}ݻmۤB!DqUT}`ƍؾ}VnUB q#Gzʕ@X`e^2lmmakݺ٣cǎPUUf@`###6皘~5jpS:=y>~g/_*%=QدwMLL[ b/yĄ[]SSS˗k5jP]O'俧K.x~7o͛Ŏ+++3A8s =aD)=Fbݺܾ޽۷A9Wс6n@ʩ2!CQfM,--ѫWo\tUwUDLL4^|ɴ1xSNek$)XKMM غu+ɓ'x94i*uÇ3.B!B!.(XCwݻW2H111~"!#?8 A;rr 6.u*))g.ƎıcGO>!?;jԨVZcA066.;vBǎ;wnHJT ###UVh֬I !'|4nl333ԪU &&&sxsKr7ߊ΁H&PKUUѤIxRSSqqԯ_VK!;լi$⧨t]4!-@~?,X0|"44۶mEj`ooɓHݸBmlXj35--MI%7[s~Eh`NipMPQ}+~U Ԕ 9JJ }G!?3 4Zjӧ/>$ݻwYv+ƍV k׮r >9kǏ(XÇ3`NPUuwm۶1fZseno~D!B~\aa!> @===7oJJ2rrrQfM(*==)))Ԭu]󥭭 mmmSaa!~tT/TUUGB!E!D1k֨!5@@uV/^4J\D{dd,eժuy?M8kZỳ!`hX<&>زESI` ڀ6`!O<'=*}55˖ށBȿ1]SSY_ #<<p3Ɏ9g //<^^4 ksU uuuZNbU%e&g~~>9K3XߏCCxxbp"O6χ+qݻX,X0ulظqTTTXǿ|Gx4 WXB!H@wnn.C}vĺu1lp;v /==Oƹs鉹s} PşMD+U6E^}L?"77$C7gT.cJKΙ?Wp$aCjBJdfff ѣPV߿5.ӧD3V%44DjW,S6UPPhBp떰rǰhbhhx^1±6B!wL?LԒiӦz+#>^X nݺ+ڵaaXrk3"""ի`a"ʜ>|ϙ*|>666:tFsYPUݻׯ_g|СC1r;jԨ𼢾ÇҥR|>н{w52 E@@ӦMC:u6m*-6 vvegݺ c?d/K11ÇCRSSS 4@nЭ[74mjkxΝֵ(L->>se&JNNƞ=qZ_^MM :t@}$v\!::`hh>?~ ̵NUUUtݻsBot!(*+#tGq#tbiiQ!4k aa8m֭7nΝ퐜ؽ{9ga*P>}J#+{T^^.tuuq4Knuuu8kSVV3,,_0wզM4nܘ `;t{A?!!AAW/?eF%K#''i{zòe%500ٳ0lPܿH!WjoܸI箲ЦMik׮Νs6BBY Xx)kjժ1?|2)l_뎍}m~xޒ;vsgٛ*ILJJ yǏvɿ'tttXmEߟ ̿גχ|JZmI&xԩb髫L,SEYxfΜ!o2Iw>|Μ9޽'@-mmmTZUK1b0dee)|B!lTYBB<=s[c}ᅲq+Hp]o\ds./UHBզM[Vɓǰ4a֬Y>>ƌj۶-nFD+XY5waXh!222{1h sLҽ W_6.]߿3mQ/ԢE mێkB!Ɩծeeeu]g̚5v ,,V1iSkܹ#,sx"##YmkkkUj'&&aÆrήA-[d߾}[&,bmwaQWII_X7KfѴ5|>#kKNNFLL,qxoq[+#7eeV[tS#/"n%Ic)((`m-(ǟ[F]YQVVH^-/IL#G6z!B* Gq9w $9zGn߾VVVFv`rss⌔{`ee 3e_L`ӛ7oyfL6]>>sR/LII 3fD޽Ѱa#|DFFXh!rrr0~8Vby"""0z(gR333̘1-[BzǏa޽`0?]6<=Iή3S!޽KM )0+11o޼7kЩSGڵΝc~CW8SFF>|'8z(zbQ0WrͰ}k077;o_sXΝ1jhh5kDtt=z5k &&S6qz}~Vܹ`oo룰UV2m3g66n9!;P!=2\~o޼FAAbbbpie?~,.zVBppqqV-RE! rss UUU&"󌌌=qѱcRUK JSSSV;66V5551t0ٳ0x;::Jjvׯϻ*Tݬ+^yE!T B_II_ݓ9"L0  2kV`-@/gѹDW:=,h^:yf͚-TQ͛7&\֭[HNNbUS'kٲ5_gѓ؝;wV kfLEÇRgvp\ٚ7o#G3?'X(!(dިǮB9w-)=\]ݰ~:&l޽Xvs@?s[OO}B!fggk%''!<<\blVRZj!..ElmmYGJJ RRR`aa;wiSvb'ؠM6puuaŔ)SKNzf%llldkJ6ТE 8p1=zĜ9`N?EɌ` >^,QWW RU-NؠKKKkz=uVf [uZ:#44رN ƾ}Ѿ}>%%%XY5U4iGb?8p#F8mv!җF!B88zw|ZպukΛ!XC F&adT&&F[6,,0?L |Ҙ*X@?f_>_ affz|uЯ__4) yxb_j_ZGT""7Qx<Ԫ}΄B)='6mj%H|XٕIwޅLsR +oFX˗/K'J `ErUUU 6ibٲ?<2SSS1{vlYUӂJ ,)##Hj_Nb?Qb cϞ̦ Y͛74BW҂:QJ\+֨Q *[fM)=144dZr=5d722BϞǐ>TU)#!B~^vvYmjIBBBMMM,wLRu*+|>_b޽s<.h%.X|X-[bvŮ ]~/_`چ8}Ԡ'>+Wa̱\nؿS';V[R,x smVOOc' & ^ے>-o߾T[]vk׮UOO'Of'22ϟW輛7 *GXr5ؼyB FZB*E=g?TTTw&Be>|WWt֭͛7(VmJ \ԪcA< z1u~o߿r.ƍM6L IDATC"r%iKJr!KJJ s[__U8511)%B!#///߫W`m QPPtUD ʕ@VynܸjlROƍY}qjjj0a"޽{8{˯;tPVrBg`-B!9'Ot BqѢEKf~OqB۷ٳ;.](v_ժUabb+++mw`~e}[&+ie +߿ئ ---ں!kbыɸpA<ՁY|4'-bW'BH7X||~9۷X~ZniӦ/fXz̊ AAW_2+))m۶ZPt1HttzP昞>Ν/u=<.LXsIII7OXԭks1m@)S&c$k(@ׯ'O˗` Ga)++cZղ444?p <<xmۊ=~CIIIf6۟ɂXvØ1+(;J@!D\˖YYYؿqbtԑu-СC$=o;~~~;XǤ}^eVÇI,UXOVҹsgԭk"55gΜf١NA!RD PIR]`kkU>}ׯ_ RSSc}.K#F8rWNVǏ>`=65oooNa":::bd4jԈU+;;?W}A\vvѸ ۷oD(++W51 V`ٳg8U/OXOںnn#YBCC%&˛'*~m,.((`!)B!С0t&eoo{{rB)Kʔdkk#ѧO_VFj2 \.0JNN¸q zѽ{n#""бc{5`Arr2 f/,,dm111/ "VQ)B!DACCLL V^w/]&}w!, MK#V^kf07Zj!77>%"88Qq+WBu$ieK.̙3c7oDF am-ZDvv6BC׬zUUUl >|~h x q#<<\jF=/_ıcGcΝåKP^=4nlu"111x xv:bbbe_̱7oCv07ͼ'444X=ϟU6]]]l۶#F g޾}֭[aÆhѢ%VxXE%l(GXh1,(|+(!B*9 @X5ɓ'hݺ5srr2S***h׮35hЀ5hWvXʒ5X,ɕn={jlْZfg&M2o\o߁IUNkN@^^߿]2gee:w N:ia"۷oEyE@SSm &˗/ѣ/>}&zU)I=bɕt,lllt/^Ƚn sU= <B? !B! ]_X֭?(xJJJgϏUaIX`q*DHZNNؼOݻw 7n ..)+oef+46>^B!eoYĔ)@@\zE*Uo0y`G U*1cPG}]Zjس]t^ܹ #@=իWxXaÆcE6lܹ ǃVԪU 7n@aa!ለDDDzEA5k&󙾠QB`eUUUd_:uñcTgxxԊrX|FyʬH+WӧWxQ4\]]y2tܺuȄ DZ5jΝ;3}=~}6lJ#8tB:uΜ9de?v(ի'q DGGщy'Lѣ`n^[ߕ9xzz2ZiFԯ9997Γuܼ6Xl u!>GчA!R)D/@`;w05j}}f\Q۷1v'3ݻXiK $* zaKWc166ǓldU/Ew;v>\u&+X+$$.knn|kݺu SLe$%}a][-)O .ĵkAL2W^ah}С#Zl6mVHcE?~d_ѿ!!B!_ҥKisFlll9>F###NZXqZsTj籊wB! 4O<ԩVpA]?c&W8::!8||rZ CЯ_@]]nn#SV:Kh߾>k-Z)x<wѿY`MMM$!3žb„ӓ_)K_8|Ξ=1T;=z 444SVVF^q]΁ZEbeh۶- ccci .“'OO<=…KhZjz? .BȿkY>#۷ ?ƲeK1iD糮cii5kֱXvvqEAxo_]tF߾|PWWGTTz聠 V,;w}b]GwaQ\m# AQ XbWTXS^{b51hT=]+~92, ]W̜l!.39-:w,yq&0d`,^H4frr2ѹs'QrV^wrʡ{ 'DDDD%%텲|]Z)ʕZ0zY++1ZZ011ɷ^{&dOޮO8kU͛Br N^UT)uΤ$?s]jU > GKqAd|n]tt`hh(y EEEQ$YdM277|r9/ sÍSDDD %%r lm+ys-[+W#FĈ/<ϣf:'Lׯ .ׯ_Chh(`nn5j~B]]]]DD+V? ===ѸqCɩ޼vnjj cРӧ"##0o\a/^_c\{{{d2DGGիW+ecƌFdd~&j֬T׿?""""pqi/^||]]]ݢEK᱓S rDFF"66w܁3qjժ] Ϣp$%% T{CCC3FL g/+ܻwʕxz~JVX ˗G@@?ZY[ѵС׸z-Zcǎ*|q~8 4e+PzB%gȹ{[~ޟ6 '&&J %*Y֭[صkT $9eii ss+GbbXDt]B*T颉R/hGFF`=wРBVdd$???? u >FU_D߰ad >S & ѡEŊPb"뿰MޯL&S 89(UMKK ͛y[7/^ PSb)'''899: "=&M&@TT\,kk[[lABƎ5jbDdddm٫WoDFFb޼HMMEJJ n߾#vsssK{bĉؽ{hPhchh~[o@c֫W5kƒc-ZmGDDD*...ؼyH_VZ͛7hjjd24k "/KBMdz20t/ÇR*A&$$DBB`vsqiU2JHOO˗ѹsgDFFѣE9e27o.j׼ v 3AS֧Fv..r ,Y~[۷o3tn߾dQݛ7oukW;w!J]{ Rv Czzߩߝ.%"Y+%%-[իWU ge)P>>>W^!)))UٍGxMMM vvvx)=-^mvA`` `x6H8_|y.pťM011EDDcǏ|'3>|o߾-JYfv iժ !"gO74j .ĉǑ :oaa^zcq000ǏѤISἩzWWWܸ}~hG갰@˖-ѩ7hٲғ.GF?WS9&IVTȑolfr6V Ě59UlN:a̘066rתU+Qր>?""""UPqWV//OjJVzsqi!$kyyyaIx"##Q:}ת@Z1116([6ATZ43JK~Ν;뒰]Ҳrqs/ZťRq6Lz^z:'$&&ڵkCػw/RRR0jH={6~10)>~0XXH#eh\DDTzd3Qxի; o1k\_ٳgGhQHz;ok-[ƶm[?kL555? \&o~b8MMMcE2WU2dv SSSmܹ3ggDDDDD& ˗TcN>%Ю] nz$&& G2077G IիWǑ#G 4z^z#%%~~~xbcc`l,% eҢ3lيXyaaaւ;VV7oAZZ^~pX XYY_E?&<633C o"""R\9ԬY i$/OZDKuTУ,*r``ݒ'>d2,To޼TKߝ+>^^:-Z|o%+OWWpuuŸqѩSGg.V}<}"]*UEeeVKM@%ğjժI J1[mڵȵo^ٳ#%K~÷w`nn*7]D1cFc HMM0iDB 5n~>}~ViܸqwAmqUa1þ}{ѥ7|!4ի':th;w(\9##=B1axѹif@CDYHDD.jԨ WWW4j*U.޳Ԅ6l6mڢnݺ, VZIJ'juuuۣQ-D-OOO|R(WҮaDDDD%Qěݻw-ZhSRequQ."PvmQQ ;&nrݗׯ_Ij9;ϳM˗xmﭙ<˗/[CCCԪU;Gre;L,:ޮ]RȤM6[kt QV-ɓ'Efuuu_ݺuV~#""|fLWuD cǎ6luuaDLGRZZf?X[SS :B͛~"Tzאdx\uuB( Sv&"YO᱖݇0"""Ӽ V@7 22@f2Kz<\\Z`sڵsM `llw<3f{7)) 6%yvŋ2_?˖-Ϸݱc3\BT^=6..-hB 26n8vO>\x.]5mڬ\hA&>~楰S >zǎC.=XѢ:""""* #rj8 \DȹV&[d& y8\ȯ`R="iiiHSg@d2OժUQ|yU?E%&&s4ipLMM GUaTDDDDOWW9 ;ceMi"Msرk.u` .\e˖*5Fv۷/<<<d.ipuuQzUb% 8(߱444иq9sifPWWGZZ{bٳ'uV`WLTn־RSS1sL,Z ׯ___|u{ܹ[s}>dYoC5+˗!)) +d2\jj*&Mo.344رJSNs4DD͙3ciiirP'L'JW|yܹsOe?ʻvF۶TTpm@͚pB…8pP޿ ZlYQhڴΜ9͛7([,LQ|+o޽GR8rhrvvd2[/$&& \n ss XZZ[[[ϟo~c޽wt;}}}̞=3g‹/nnn[[[>OKDDDDTR8p ?C" 0 T$Q ddd@MMMĕ(tޅƦHgbذ!2W>d \...Bx9R(`eU:b9rpl>Zn ggghii-xyy  &&O<4μy pU|@w^z]pvvF͚7n3jiiauӓ4K !Y+-Zڦlٲ]0# |»wvZ]GZakk [[[XXX"** o߾ſsgE$_N={aٲ ۷G5LMMݟ%aikkc Сn ڵGzPb%ܿ7nDqUPDk`k[*TK^Xf5n޼-[ٹ>RRUlbDDP>X "*>8Uzz:w $"""455URdtuuQFMԨQl޼Ix}DMM UTQv""""*9,z0-ފ6A_7d&&B퍨`VUZjAr蝞#KOAW Ų(NTRSS\֭O?sDDDDDEť/^$:fffի.{ViU3gpxظqz%y ===9r ?|}}d.''luׯ/y, PN|ڵȑռyξ7%?~6FDJJ )) /_>JHHѾfZؿ-{ٳ{u\w@^c h׮ <4I\їD$k+<^V@'"""""ӧNУǷ\( C3UQʵ mTAMM 勒E$tDxb\*U!CgO7/!""/Fݺu᪣*II@gycBp|}Oؽ\Ç>>7o.2{ۣY>Qa֭;ԩ *JŝJ~o c׮=Vp-[D˖-i~vڱc:v;HcV~tr LLLѡhܸ 닫Wbcc?a˖|ĉܦMl޼eʔ-[-[DӦM1~8ٳ[x-uO&6mV$Z?4E֋ZDDDDDDDDDq&011-45KTs6lC "RD q~vL""*]qACbJ2d(pʕ+$'k@&Mb﹞744޽PNmaǯǏWTqÃ)mmm}Nhh('[SSWW/ uj""""""""""""""""""*]f͚їbHKK=z|gGÆ  ___;.:::::tP;$Q\o.*9*Dƌ[T!_mС:cƌVu8_5:{VuHDDDDDDDDTJtZ$zJHHFHAQAHaQ1ػw֬Y#;tZ"DDDDDDDDŝJQ9{U~'RSL٤DGG$hQYٸ}͉+[,LMM*)Zjjj TbblhhT{hkkÇ ˍf$m=e9IO"88q033kױKDFF %%&&J*V<>|@@@zz06.cccX[|I/_""¡vwٳgFRR"ѢE|ۅٳ|D666([lëW4$HHH;B*U!U)kɒXd """"""""*Jtw""*= īꅄ+Br8'(;NqRS1e' ݻw*n޼Usg,j]]}%$$`͚ؿ^xp+_~>|ttt$鉕+W%\먩vpumoUTɷX8p`?޼yNժUѷoCKK+>=| sh۶&Mڵ*J8J:*>(H\_.QAAAJ=YecQ5T """"""OBCCLDZ̟?O蓟sa̘g=?ט;Wl޼ ;wS+Ɛ;wΝ;HLL˳+<=}3gNoƎ;g54cXXVCC3ϺoyWX"O?METTTqU\v7n qL0y{f͚͛7a׮-66V~~]r ĻwOMMſsg1w<)xJŝJ?}}}XZZ"88sU⠠ dddHy W%%|\\x'k)[r{.1###rgI4qSSSCO =z$\?2|MGԪU;xƏ#Qvvv033Gjj*\Iׯhs(S ?HNtի曎8t8::J8Y+W044BHHp-ZK8 {{{VDXX()9 cƌ9333;@[[ =Bhhp5:vcNjժ*n-nZW }6StDDD%Mr\v>>ވ@tt4 aaaMnݺPSS˳#66V(镁\n"9NrzzzLFHHP666P~%Ξ=@DF\.Ct ƒc睊&׺HHHZ8˗/;Fll,LLL0eT$%%S[ZZBS3$쒓? $(+WN>>WZZ.]ׯ!$$BڵѪ+?{0ܹsAAA Bhh(all GGG4n [\9ll,>r1llʣEVgKDDDDﵰpTVxbh N""*Yׯ#GqIt𵤶ї.\@|||U00Çk='''̘1m۶˵.]:۷ma׮*J+WbݺB9((%6Qq)PYЮ]{Q?~8vPFV$6IuWST뎒;De-+S  }h7L\Ç1b0V&a-ظqUHmllo1R8K]Fa<veù&j; 0'N3rOS{ܠA:uMBBv￧PB/os_"##VZE/tYˀSHHh03gP^=;cvfϞ08HvރիH[ۊ# ʾؼyRϿXo|ݰ/i IDATaC8qRd.4k'ND pg3"""UAÆjy&jI&C~}#.\$1x;faÆ Fa 011<<==Ѻu\ 55ׯCvm% 1t;N0QA+ؖ-[c]vSZ`>kbܸ1?~,ȵRhX7ܻw7D- s͙3ѱؽ{r}CaطooZ@^0s|QiW"vJOOWxZDDK=0s a͛7qqt)69 wDegllkǏBYOO~1K޽p$''v؎#Fֶbm""±~bACCk׮%f= Æ G.]l#0cL8q\Hzٸq#ƌ ---Q;w֭ \.yje@@NW(kjjbMy?Xnݺ ǶmۊɓHڡ)S֭ZGׯMn֬9/MX {xx <<\(?mڴͳ/555̝;OBhh߰aáV"֭&Fc͚022ʵ,X}U(\p}%RAMyÇq !Sׯ;p>Cp%:tӧOҥƑ#G $$D86q$4k\ ҥKLڵBQzu< >>>}'OɓE{ҥKp!lcc'@HH\ ^LNw.߿ZjKڵxPvsUl+^'/hР!իhx{{ח TR666//O$%%6vX[[ys+gݻ>sSb%lܸ 0k Qk+9>̜9C#Ga) 9|0&Nh [-[UDDDDXHH@}^~%״SsK*I+E4CC6mrjj*L]`Ѣ8}ٍ ƣyD-Xhq6yׯիh׮-&L;vڵذa= N_}UFϴL<UVڵ3FMµkW ƍAmL}0j""""""/K^v ãGrƍZѿn],7sz'Nnnܸ!*)ZIR>}]#Qǎ$䐗ׯ_ if„d)&nP%QyȐd2 :FddP6m:֯ߠ0Q ׯs.T8|2+M6v0aD_ e6luw#EҠ={<ߦM9rT@+&yRSS=*秫 MMMhf{=zD(7jUT)g_~dDP)֥KW᳴t2}JD6w""rd2lٲ m۶r2.]޽{GcjjjXd)zx;v˜1cdޘqcй76gq俈Sjܹ#<Ԅu@1Lm$սq㺨\F^u5QQJZτ*=|@TRj.5sʚxHDD ~~qQܨQc?!vFFFSg\׬Y }})&&=waذ!BL&Ú5kaaasҥ 7wɳW_A߾rrr2vܡXRbҒTР㸸8\>Dwi 3,뽻-[cX:Ǐ6LҮ͚5W_)Рڵ?{֭"Cx,0} mnj'*{DDDDDDDDTZ|[&k|Y_->}/^D@@""¡ 9WwD6mQNoڴ Pvt\ɹo߾֭>+W.#44q055C͚5ѭ[wܻt2%ذa#ƏCѣG>-.] Ij_'Y RQ9""<$$$(ׯӧBgϞ?Wi3v wɿiiiHHH}J*8/}ڧ݅WrInoff\.,PIeDDD_#&&F([R.I(@.7<)?BLL4000(~cbb;\9"yMSRRBCCQlY))) ʕ3.ݕRSS )..{annIm޿PoQg^a(Uڿ@})N\11QnW2eʠCѡׅ\nM)<}o߾2VaUvrGG|\_"""""""N>|QK cٲᇞطo/N:bccqa=zÇnj3sL^>SKKK84YwP5e޻qg}Nfr&$-[sC*TY??ʽfdy"""**dU"$$4˿Kx:##Ct,.-[6mBǎ_ ;?~L8odd * lok[/g>NKKǏQn+(fU[brZ۶mxcᱦR TdN<~Ξ='O_R2BB ]?/Ϟ=;^o޼QFH֒{DDD_gٳx9޾}+_[[9uRIٽ|w… }ѵ2}}}XYYEڵ6l(9q5vڅ ޽{~`ee֭6l$ipJJ vޅ'NʕˢD]]jB۶0x`Huժ?E0klI0nXܲeK| FFF`O~7/^`˖q!u ѲeK#sX~=-xRRF1v αhŬY3{92'O&^Nn۶ػw/^ ѴiS 8;vʵܤ`vݻ>>>wLMMM89Հz ===`„B[WWWc۷o`|ܣǷpuu\r8sh3 tÇ@%do ޽Þ={p~|H&Lx9?7v8utb8p` x7oB…{53AARq}[6 CwNŕ+N֒H$ػwӓ$kgg-[am:u*R󌉋ѣGqQ5gϑ:wHHh>&?I h=dC'%%bAx"oE 1 ___n6m2=Ɛ!sx>6l؀͛7ٹXVllUTEF 1}tl1Z;c?ڠ̙&$*U3p5 2XW={vcY2ҥKt֬ K.G޻wOt?,[\xvޅgdd͛y&V^ŋ}r dW(1c:< u/))Ix۴i#j֬3g ߿Os+444܌3E Sy;xd-""ՋFHȧ;L""""""""D~az0bٳA8|7n"ُ&(eEۗPŒ)>>N ]eUΝtҢcM6-rYY+>144*t&߿OtׯP*/ H,= ӧO󷖖LMWWW4hPgccge˹2"""L*[4>|qcjJ3h엚PlY %|-gnZeʔZy'OsRr} d'M> 3fLw^UZ/?N%k_իJ*B9wv,JԒu{N<OOO}~D-ccc|J @vm(=OU<==$j+W.cGc- Ν;ԩBZ˗/èQRҞWݻ٭p|H$>}F%5QlٲyH 46xuϟEطoB{11r ""XYdիDeq]]0""""""""K5EmU%hjjIhҤ)L͛7ڵky*FUsljj*BBB^KQモOOOOGʕ ?99YM*jxB)~.Rj\|Ihラkl``PqEDDTPf-s,YYYd˗/… pss+SRR/AS3_Ui666Bm۶O>۷o.m۶%cD4{޽[R__zB6Pvm͛[ys?W;*U ֭[6022D"Ahh(zjܸq]pԭ[wHtaaa1uuu 6 MVVVHOO۷o} .]:͛71555 <?\\\`mmtEPP0N8;$JjǤIP+w`-8k=1h|Q.]۷o*V1cƢzTcB[;Ioܸ nȞk֬8{ ēs|e`ii!CA"O@͚t'N>| M69orr2O2&&&={4h{ HOO8q+W@bb" VT{쿙7ֆ1W>ɓ'G͚5eM+"==ի7իU8yo#G`yի!CVZƫW{.V\!$fffb1Pի_ԻGKhGW8:V]v`׮]gϘ1UVEÆdΝ; ׽!C<{AA8~3cлw{v"22;QXdLMMSd-"" ޽{7h׮ #"""""""T\fs7n@hh(T(!Y ]yk׮-"x۶6Mgsq.iDPr{NTȭV-W.]ZX8}!L25EEbb"EQQ01:͝sN,^,_֮];#$"""_ؔƍdVu 'spe+WPs+(}a>|pm<}T߭[whk,LɮhuZE~V˗ZWlY0FE=Ga֯_ Q%""`Ѣҥ+\ؠE w޽ GD|RߟRȑ#DZހ iiiBT~ .Ȝw̘ѢD-sss_'GKK 7 IDAT`o_nnn3g.Ν;'uNDaÆp֮];w}fffVZSNU1={~G88T~)I7o !bdaa 4nRSS{.L-WjYnDD&L'ӵk7\ 1;;{٣E wWظ+33Æ ͛QT)Enܸ.z\+W7oɓx:uN:hC FZZ$ F[Ȭ9Reaa//o4jX/ޏ?sWhjjSS3J9DDj^U_6DD\!!!?wl޼I3f~͑xTjjjٳNJJ„ U`hh(jK_:IIIӧ:::hݺԾ...+_NYYY7onA[[]vکDn݅vll,Nψ/ʔ)#w%gaw۷34""|~=za߅ .###0tH$B Ua[X(oW}eի7>UڶS%Uo~%SR]ddCAY4XD- ;ʝXU%""R 8Hj4={޽{ .rs(1TR8utD-i?Kѣ8yɓMHWWΟ?'$ G,Y*ڄ<55ׯ/0WrƜ9sDkN>]R) [l˓ĉD?s"##CjdlݺEhkjjIã%fΜU苗 v#JIKK {*i=y$OPAoߙ'QKm۶">>^h7i֭%j夯}bO o߾ŁyNƂ jjjXv}۶m'zO+=ԾD$jIs3}ZDDGGG:uZ›AڮRuQ7JȈ#D_={^ ͑)tQVVΜ9LʽӭŝUTAzvll,~m/?8qhQsΝabb"}їN˗/ Ϸ29RcvZ;jhїGΝ;-##׮]UhLIC.]vJJ Oc'..3gW8խ[WKnàA {^7w4kLw,]Pxis{"\Յc[\жm[}A$&&"..sD8ްa#тRw嬬,;ceH$ *8BUHy \w/uaË\]@dA޾BݱC\}QrU8qo^$''9eQSS…6BQ›Ҋ;B>}4JB=vJJ ={*E E:t7Q^zLSaÆLHMM Ev!w^?j׮-WN81ZZZ;V\+g2]a=xp_4doƑ}r~&L(TCIuoar=IZTd-"gnnCaҤހ}qUDDDDDDDD􉩩/_!:6}4k׶/^PNmL0!YYYٳj/wG,;wcƌihh܍rѢE/wޅݻ"...Oxx-011Ōw,USSï:up$::z _Y*bƌB;++ ƍE}#uLVV|}}ѧOo;Vj,\رѹsx"̙3*ѳgw$&&nwޅhW`""ִi3Q{͚\G›7o1cuuu_-JxYtI߿cǎKxЮSg{9㤤$>|GJJp_~* yAnذAqؾ] r/&600kܮ]+W_07WȒRG9G>|PbM"""R\ʕE=~Hj?D"cHLK?#H__!ϼׯ_rWj5{> rPA ѵkׂ;PA'ܝ;wDÇK[Rп===Gm۶оz>|\^z%nN:rU.];?~c&w8EX-[Vݻwghh'錈$hܥx1YR|y,XPa8ww|Ai)m."""""""uAAAXxpƍh֬ lm'''">>^.wp] 44/ҥK`kk;;[٣|HMMŻwv:DslV`O;^x..wQ+q-<{Ly6֮]333iS FΝ; Avm*UB"ɓ ?lpKέ,#Gzɓ'8p`pN#T !00/^<nOǎdۯ_a5±/aeʔA\\q=^ol,je'(* Z?Pُwv9s,TɈ[ϞbɒHJJxNШQcg 8qЮ[.NMh;wD"D"!qw_fM|oWSR4hJ* e[nEFFp)ڴi+kxjݺ 93f*qEdd\prrED{$$$ ---z[mmmjttԹﱨRQ5Q)Z044,$իtroذ!Μr]ԯߠq)C~faa!j|9)_|>{pYMM +O*U[\\\PlYfx*/r+j׫W_qڵ^+}J~^Y\]QʕшU|/tDDY'k}8[dwqk׮V cǎ%%%ڵR+ÇK:T"""1 +^9^^r/VKII==zٳ?U522† DUI7n<-[ ;YfaطB>zf9dXb9Ο?/˗/6m}֯_TŚУG*[RЯ_ZA`066:֭[yrR62Z={\#ݻw ݖ܋e=WWW֨Q"褤$ yQ9y&xzRh5+WS'T,*9r6~浨mooyCCCDmE­s'*.} s10ߞt'FV omm]ph,o޼-0YK[[;OR,Ɨyy/_utt0iD! .拉%k~-~}Y$4՟lllѣGWc͚5سg7rZj6mڢINZwիc s>vvv2e*vVI7yܺuK۷h۶ t OQprrhLll,ܹ'Oĉ'̝RYVBɓq:JVر-Ǻu̞=GTU۷Xl v!dR%<ɣ[yBGjjjӧ6aD9rXXwml5k֢f͚BR^||<|||0iD ?E w)ً*;u?\ڵ?6۷ocر`kkgQ,5jaw|oooDDDiӦ(WBD y*qhp{IF6l8z [[;hhhƄƍ8~gΜArr2 .WDDD߂˗aѢ?mEgH=*GJ=rğ[[+;$wHE/a2*hT5wUArgqkii%wQ5Yϫߋ%&&b=E/!!A.=""E}z֭[ЀC"##N?~aRf lذ͚5ԯu-|Ç1ܽ{>>>񁉉)\\\skzzB-=N:cǎ5.pwVLl7:vE֭[TRϟ kii{ڷYs,Yׯ_0>|Zy OE,tX|\ԩs$썖<;o|1Bt{GU0jWի #>>a(^^p1lΕ```CC>7߿;%%E62^TQyeUUTjx^HX2R@ <7MM'Yq/T)?=_QKMYA2334^܅cuHQL"""""""""""*&...pqqQ|077W|eV6}QUҥeVU)}nUstt "" 7nUDPP(* $o߾ G?Pf.0KKK]=ztGVV2220d \xFFF;ѿ_!,**W^W]]s0`ӧOe? oDDD%%22Phkiiaݺ&$'БQHZ+v?Qi)e=AWVOO_ S,âħҥK _*U֭ ?[[|S>d-UIDDDDDDDDDDߎ3gp钖 "J̙OKK sðaña7Ο?kJ. ''gj mڴE*UDĉhР!g+W;y6m:.\ ۵k'<=G8G`ƍعs;={&^SS͚5øqd'|-eʔɷo*UCjEwgODHV255C֭6QթS׮eKDFF/tر`ggt> ã%n޼ ȑÈ]71`@4o`kk':Vs1fX 0G۷o0|J`9!6n܌'bo\| ^W__ժUG6mкu/_>>>>HJJ˗@ ;r&w\,󆄄(4>wŽ\9Idff=yRSSSy@c<##-uU{+ oM^y%/a`TPA< _pp&"Rʓr̉8ݼ ooUAD23Y%fϞٳ ::aaBf,p!;P8ƍqj&Ç۷Gll,LMMP &L &շ*uݸq]TUG]&̛7g˗/uu5+g;;;|zc|K.ҥ/#33fɳ[|^ЫW/o^+VȮ ]8cKK+XYYGDDypYMM ,׸ 𮬤U@MMM 8X9I`NNN۷o1|YݻwrK!TV]˗\\cso\Jɝ ,q[XS4OʕۜOQZ"͙SjGD('kWYZDDDDDDDDDDDDDDDTall0666{pYMM }Ua4@JPrTdff33b_,,,`aa0*t/_^D˗/ *8 ((p]<fT>} x!fRRRTIѹƎۅƍlǥc۶bիW=LLLOZDɕ'NϗkC\ t2ePJў5ƹsŋeaa+" @ȳgϔ-sN DRWuL""""""""""""""""" {cǎ VZ޾ #""""*>FFC"8X/ѱcGrFFΝP$\H$3gRuww.gffbݺr;v옐d&gT(NsN^^劥$YYY h]v{ wgh H$Xn\}l>|ڭ[Rz<[ǁU^~RSSv͡Qxڶm+c%JuY9sWZOO"}Z*JCff=z !"""*^..U)))vj0{,QrGA }0`(… 8p`kN<<1XdؠAdڵk kŊ/_Iz)R1pDGGs]̙3S޽{olܸAtO~πժU+_ @TRX+_:D?V>PYYYzPT)}9rp3A288J> ]XY˖0mccҥK0""""""""" 3: ]p?RꜽzRTRdoGDDD_p[կ*x}y IDATp)Q2G1111b8Ξ=9LM0} ̚5S86`@? 2śqii8v.]ʛdhhYfc)±ÇD i8q8Μ9\舞=ݻd'l鎿ފf͚U0u늗/_ *O7ʕXx&NDbb"f̘;wBMM-"z,ب(i9&TԺq:[`Ŋpu-?S0u^Re")))hժ%v؅:u޽rXϞɩXXv->| ; GG<_}=P~3s,\xQQϣyhbWN9/=z(#L>3gN <oœ9sE 9.\5kWsvv.s9sQlo7}[T-L""={GCÆTO?㧟UQ㿕:͕Hu!==ɢʔ)ŋ(2"""Ѽy 4hׯ_yuƀQf-XZZŋx!N:P@ǎpU{N1w܆plo>} 6DժUQ bcc9sQQQXy ;wС±-[аa#ax"00OAddʗ//sޅ 7G]Zjz[nsjkk[T+7 ;SL-^ΝE&Mɓ'z5id,^Hִ*ƍʕ7oյ&mP945'ND&MU\ q&tMx~a7eKw*w СC{k3j׮*U˗sN88a%ΝWl1ijj{~ III@7WNx?ܼy瑖&777ի lقΝ;#--#i nnnZ`ooTDݻwq-p >wѣGc7n3g׽J*#>> ӧ dۯ_YPl˖wTeVn"/0==}~PNDD00̔:>|<ձjj(4ѣGq%e0aDDDDʠիWI 99kdQڵjjT\7nƤIcvثWx*PnzaӦ±ׯ_݅SWW>>ѥK'222p 'Oƒ xB7=}>dܥDsx-nݺX/GDDDDDDS^tbŊL""ZFѨQcUADDDTl`aa5ka)Zsܹs[Od-"""*Vp%L:%MMM 6ӧπ~_b%<<DSS}Ô)Sall\l\t^^a|H\JYfȑ#k׮",, Ճ)jԨ-ZIX?Ʋe1bH?{w)kkkԫW:t.ďG K귰;VZYhWP1Ƙ5k6f͚4ݻw$J*[02?!g߾c UTyFWWSLŔ)S0hjj*&O?GŹsgX w@ZoٲHHH)SqY[[{Mۈxxܼo͛Bys'~ɛ`hhScpu\zFjj ʕ%,--Qܠmh ww<|}zKNsrק5{srrƍdW CD{bKr#/ʓe/}wA""""""ihh@+[2e:(t]s::*藈&Wڵk>>8}_!9R0̝;&&4~Y=z;uvvh޼9|!""9::d-""""""""""""""d-""ɓ'0nXDEE.)) Ǐqt+VTht?wCZZnݺ[naʕ1cFtCݻ'wI=ѤId&k={ 'Ne*7o–-W?#?)ׯ~&""RT~C&pttTuDDDDDDDDDDDDDDD_-uUd-"/?IM*W5ȑWH$rϟGJPtiNii5k&̙-yҥKhCjttqJ$l߾ ;wƇJ*L"N0Z*%Hd^\2"" 1jժI&V-W!..êU+q\zy 6mڠ]My ++ x)֯_ ___a̚5Apw3߰aѵk7@SVKfVVy=y=ztGZZp'OEjUQ-k׮!>>p5L6+WV>!"lmmakkׯ_:"¤#99YlJ?&!"*Vh֬à/TTT$|||vÆ$oXXNWh7i=F"""""""""""""ρW!oq%شi3J.-3006lQ#Ν;ѹs4jXt=z4f͚ʅD\r%ƍ޾"gNر#444T}޾}'+Vl碾^^L""""""""o^1N 11,d@TRSS9M`aa9VTXsKD_?&kQݻ7 mؾ}]F@@"o+Zl0>keʔA߾}yJ¿[*4~˖-^K$v~D!|ݼyS!)۷z*UCD* z;zC"""""""<=_"ȼN]]dDD߼ׯ MLLЭ[wƵkxuq[[[ ..{yDU~ܰe޽-ܕ'ѷ[nx%^|P ߦ1DD_#[[[=#GmwwԫWO)ɓ' ;Qc1*T%--UW#ڷoˆ,*Obe-"Wbb"vjՠ#X ԨQZ󃿿޿37::P*իD(4rʢgϘEDD0a?g"--M!}5k޽{CKKKաWreܸyrT }iݻ'TUW`Q 8w,fffhԨ#"""""""v0Yd N[[(4Zs`Ŋ8~GX^ OJ-Ǐ,dHDD߲eˢ[nѣGqEdffsb5hkkhذCT}n݂ݻw~d-7}45jd-""""""d-"")..NV4J7o̙3 ]%##P ~8wl拏/x""}EVpaܺuI[D$"s~AD_2 \2 U}aff&˖-h.R# d-D":uurKN j(4^a{)ã%TC@BQ>B|&"""""3663UADUIPuDDDDTTZDD/}}}Q;<<\OLLٳ:-[{8%##C #kn88T,|ݜ䨫gsG"^5jã!UYbEc| p={׮]GZd}}}xx(TZ59{莧O|ll%u=(""" ^ѾԔ}0~ѱ; ((Pwؾ}KXn-.^(Q[[aBChϚ5:t疘K`߾s+N&WXTT$7Ih6C+cǎaB{ǎV6 &>=zt;v섙3gIӧnݺ)ZlI&'"""""""""""""R&kLBBB[={QQQB{ΜyݻORSSV`266T%""""9? "}brǁqA̘1G7<<o޼s<66R|L̔:@H*ennXpVX.W4Q,Ws]tÆ /3S"'..NXEcSSISD;FGG˼.22C ˗-!!Ǒ#1{\ >Qx<?B]]҂5k.4CJNNfe$`o%%%ڟ6E>VZh[<琖 lddĜo֭[ yO>kEh׮Ú۷o#, ]]]N:h۶TTT고=ǏCCChѢ +HRdgahh+ANNfffh"{\M6hٲ[^^^7)2ׇS{/*ouKJK.!22PSS :tKKK.O[\r^Dbb"ݢEK4mڔo oԪU JJJU/!򻉊DAAuIeee!Ck#F3!CFFvvvx) 88qqq ¤޽9'1ۅQUUUN۷8+SCvv6vmڴeܹbwB!D!"##Ν`h۶`Æugk<scMʕ+o^fێѾ}o^bJYYyfdvI4hАs_>|+Pk֬$??۷oǚ5ckݺhѢڵsާE! ^^BZYYaݺhժw…Xw ###`]6~߾}-[6(((YWYYmڴҥ`ee%־Ѿ IDATEt"Ə\9r'NO>GyYYY߿fϞ%2P (}7lX''GلD?M*4P (r4j,_J",Z'tk"K.Gdϟ?SX%2P (ͮhџpu클4CII M9sf *sy/_ٰa=&~ zb-??!0PLxx8\]{С(:: ;v@@kpvD>J伂 ۷o{B!B$ԙh!Լ!Cb5j7Ǝ/reJ/W~钒\~={vVll,Ҡs0`} ]tXcgB!!M'sΝ9s7ne2q+**b׮ҪtGYgY ÇN~=]tC߿){3Ek֬ƍϬ[^C]uB>elm  ::AAA B~~/^y拵,ŋƍʪ>,,,W^LJrrzk׮ ̀DzrFFFAFFbc?"66!!!L߫˗/cA8k̺LֳgϘ1HIIW^B711ۖɓ'օO浉=ڵd~UUUEx)?~zMm mmmL6>$Ç;acc kkk˗DDݻTጨHfǃ%4q%޼y'x=S'((zʕylٲ~~G7hv/W/#۷o1s 9r'8"BC_|&Mĭ[w ''yl˗/c2mikB˖-ѴiSx<dNJ1glhkkٹ}H vZڶm#Ɛ!CÇcN:GWԄL2.\ѣGpph  &&O>œ'+cy{nBh%矫U^B! DCZRj׮ &27[233ѭ[=F@jfiР!<< $Yfb>Ғye˖`*OVڶ}իΞ= t5c7nddZXX{a}S⏝B!He""Iݺug233kk7cǎCZ:?lӦM\,X ZGfe<rطo/իE!Hș3gXcƌ 7AHNNºukϼysYZffXvfdd`޼8yPǏE` &fy7obe/ZOPRRGwdaʔɬ]6-||JO70۹X Ԓj ܺu Fׯ_}4̙8p%''cϞ6mAMM\)#b̞=-[&HRϞ=Q7oʼfsWUvD^^TTT9{,fΜ|N?~7SB֭ۀ!C qGLL 6lX/gE!B!Ǩ`-Q*+Bnʕ̪h..ppp5޽{ \pqJoڴ){ҥ/hݺ͛W":: ЩS'۷ǰañi7Hv:u UKΞ=>|mݺ>DJo,YGmP^=ԫg yy9Ç~w"55@E|B!BH͢ZIc~`-A媋-Zhɩn.]allÇsh͛ciiimێs[ԫg\zG!aŬm7nWٹ;9ƍgVVV~&WSS{( x9yH⊉'C-ТE A4 6gohVs,--#GЩS'p%;wnܹs"3zWTT{8} 444.-- 6BCC7oPke8zOܧX%߿gx~`-Z՚B~8u ztaVF@?FSy ,9`-X~<5>`nz0JJJXtfΜ"@NN"""E lEL:ϟg jO!B~NYByyyq:BBBm8RSӐzrz&<?lVx<ԫW 3f6~LVA!xɰm۶Un]SL2˖-弟m۶2g@-Zs2F{ CXXSnԨq}?bРE{ddwӧ@2:uVXl9X (}?j7=zY,20`hhy?UmVһwJv=l߾}* jnn̶={`۶RN̞=Gd77wJ&SPzv"kܧOrS)))ZJ`V\ /^dώâEE͚5[hVǏ {IV68gBhk¬Y1cLτBՀkzBoSWQ! q?ݻzً9sտ.n޼ W0r(O Pz@+++cؿ ٢XZZb֬{B!Dr(XB(Xb9׷İaC~:;wwApp0BC_ gOOOGGG?lNT50`OL,iiao߆qhԨ5j Ѡ4-magg[[kZƦ>.\XPff&!B,MSM>|8ՍƝ;r>}9SaÙ˗/X ++ xu Ԛ7o>nVZX(x<̜9SQFCSS)?|s4kkJ@^^ƍg8}|B:u)caʘ0a"3fd.g2{J(++Ξmȑ]v<-[~N=zԬzӧ/SNNNݻw9w] 'OZlŔ␑!n(=޸0` 8%B!B$3k0C!R|}a֬8uK"TUUGGG"]8{͈+222}\]]W^ݻ AJJ2Ҡ}}[B!BkN"7šw^P>NDi?vZ*ApU`?V@EE{F!=ddd -mddƍs_NN]v*L!?TddC]]jjj|b hkג5!##ZZPUUHHMM 455rssgACC㗟;]PP(**@SS 鷰/:Iׯ_uuuK)**BZZ*233%s\:WB~5E!ckki@mZhݺlي+aQTT9n`o k B!ǡZB)**n@-mmmt]vC:u X4j]qdee~QѯW^^^>7|B>`pU622BLL_(.QRR\fkaڴؾ}z;qy̙3 J3ݻS)I }*}ݺM8C__(((zJ1VEp3V\{~*pn]SkqW[6;k;]]]Vׯ &&q,LMM+*kP\&//LH[BjRll,!00'dgg3G&ر#9Z1?ܼy/_`-r8::Wh޼9kSQQ͛7WYYh{F-8Z1?\t zMѠAtGu(=WXtvEEE1c:SvrrBMNN²euqqAn8p`?Μ9'''L4B ]Xyrss1u FF&MXۖ.]T'M$9W߸qc3>DEE2 ˣu9r p!#(($++ [[;}0QPPYeС'>{8z(S^`!ԩé?/} /Օ=v؁ |ZLZZBǎѡCs Gx5ѷop`2I&1sOoݺ^CJJ S?a9K!?:XWv'B!Bυ@!@hI͚5É$ݗ/_{h絛-:WeSʼn2:u}B.## U&'%%k?m=z .\w0k֬"5j]{w*<|ɐN`=v pˀ$)iz=x000@tt40]Sj*{U Szܗ뭦9{ {IVYR}Aqx<:S %|N+.!(cul{\B(66p=ܿ6' .־RRRhџ8u$+L}o߾4m/_oZZ,Y^»w; {y~߿3f*Ǝ &( [v>mff9XqWUUUhVff u뎿> 2IOOٳgqYL6˖-w\\̿emtЁ/X̙3s."N8dxĘ1cq-L0I|p ܸq= 99n8_z۷x<{޽{aiiz?,X,9k￸pw?Wsu<$U_ߓISVVrr/?XIzqjRS!g.4PKYYY9_vv6f͚͛7qOxx8:wj)++ ,9""]tQAJJJOK~E_uXEEEmXO,^Hd5i۶={+PK\[`˖?jhU=K.bԩ}z%0PKKK >|xWW|{_/))ɓp@-EEEC]j }={GgXYYo޽^wqq1 "4PB~'5YKXd/@ZB!B,ʬE!DWc[[[Γ"##9?5! j+__CCC rBB#""Macc[111HIIV#BaOp}^xU=KKKeee1 6W\ƕ+qW àAзo?ر/; ͚5CII j{NU|}:066uVi?U]^#챒ע:#11kT?==:ŀ V|0'5kbW`dd###tG^ܸq=DUM2ݻU֬z055:v숕+͛7;ctw.v#++uMQ)ڷoW 00P`Ř8q<j5mv-fwwF.]p߿۲eKѡC|]~ OO/Vv0===m۶7n,G+++^z [III1۫۫W/NNNرc_6VZUX/ll߿`u": > cccf,6l I&?~tW0MMpe i|V|#؄B!BoB!5ÇyPPP`IIIOm_JJJc6Nuog۷кg?~))w/8 X/_CXfR}B!DX!?}e׮*?k?C^^mڴş.•+ (ªq$%}ԟ2СCf[XX\\Y}.{q -??u~'lT}%~+NLW_8Bźzzzɱ_~~~@QQu,}o&!0fX΁aØ͛7' *OZZ֭+-//;wt?6nf6)ONN˗/g͟ztttA}scwPXX(nNNϔedd*ڵ,YZ:JJJߖ3 ͨl``_}"7 ̶#G#99xٹ6n "MGCQf-B!B!(®C5B|0_"뇅K{X=;M:~8޽#Ε+Y;ѻw[h<Ɂк{[mu2`-q^wt̔3331mT~b!B?{{{,gyrHȏ ֪N:زe+ l+,,īWPPPGЭ[wfC6ߦsngooYYYUX'4(=_+?ƍsn[U-[bE ?*GSMXXWرPq"~|EFFU|q5Zp^<ׯ_aBH5j4󸤤V#Ȯ];X'P1jرbe'СC)X a 3w}Ej:t`AVVә2o켽7e5DFFVy2\\t>y<,<3!j+Ù-4h({rJOJWW;`@())1'O 4Ttlx{oBHAPP> oXr/^Ed[bZcJJ^zTԸqc3۷HN@dgg)#Oe޿4looEEE[n*}$̜s݊AdQԩ:Eg{[V9Ɩs_Q&W^KN8w+38RSSf͚AJ{X ,,,۷2ZZZq$!d*RD]KB!B!2E7Iwߓ4mێ=]%%%y ظ[k?&&&X/Z233!Cwq̙_{>>{1f(|111ZSN| Dxx={lG||<_]33s=y2}í[J_Q#Y >ٞAVoB!kkk8::̘1ޛ +++ѣG8R7{={ARǏӦM<ѣG=_Rxx8]֮Ui?|MgGy< Ҡ=]q) Yqr31wv{u֕H>3̋Ѐagp KR1D磮~>Os]]]οаJWƍ+=O5F EEEj [5kʺ'̇Xbcc1uf[II 뱠m߂DSSerSǘS=Bx4e"B!B!RׯcB#o`AUرi0fϞN:Ʀ҉edddnСCѺulMС_'''zCNNSjjj8},&N$ƽF7oVam{mܜ{eLLLxаaJȠ];GY/^&B`k׮gk;7xkBbb"<ʘua}"j|)FQP 'B!BuPf-"̯R}q7͛7oݻ3vYt ?Ѷ2EU6m"VU^I q~뚙ĉS#޽ϟ?###::F۶m9Y2V qDDD 55jՂ ڷo` \XYYaFoەB޽ѻwo$''Ejj*}}} U!B3777k۷oeЀbbYtttFq4d$''aժy6l(sB x<Ç \̙38s suBOOiii"`llqއ(PPPͥATӧ7iS]5k6/.=.,,Çqa[lCXۺwwƼynZf[`5^2llle{`Μb=;waРAÜ91wXZZBCC!!!|-+V@$h[nӘ _>}bU[ 11?ư2i$z̶KSS 8!s "~%Xv=Ft > 222Ch+;wziJ̙9sf999N)))㋥K#)鋰nY7n={ܹsJ'z{oXѣ'077oMʼn'лw/( Bz +(S[صk' #ڵ@jmmmt VPW׀:Y M5RBUvv7oQ&99Y``QeeeX[[mgBhh(BCCaFعsweժhɒPTT^JA ?chժ5_ѣ ==vzy ,/_lf{VV!]:?꣮d|1dZ:X~֮]gϞ!0.>}ʷÇѱc{ܸq VVVƍc$#qZW B!B!B!BAТE xyyҥӃ`L>x +IGG~:tG:u>8VZZzzzprrB.prryx9޽w"!!AྕЬY3L6YfHKKrz~s΃*.^l۲e3M`ddĪ+'' b 8'$$#//[[Fgزe3\:ZZZС#M[[nC.]شgΜf&֮~a޼Ԭ~$u6xɓx,Zҥ+N:aѢ4 [lƵk G.]1s,NAJ`cc3Ԕ9Ԯvڬv'Vf1ppp֭[p%,x}x$&&XdddY}(((r=|[.TUU3g™3+$&~2 ѢEKtGTT$}Ō+RΟ?,q7[iϟ?s ֪Wq7Z\;‚t/WTT( dժUrg\d b/YX=H>>jǔƃp3 .aIkPmmv6xԫWJc!"OEB!B!D(ZG!B!gbRvFNN޿@b())v066aMdƹs竴` ** )))5ZZׇ.O2d )Vϟ?#11ӫ CC# &L &rGTi_@i <=ϟ?#55**с666ƍXz5EEŨ]6,,,8Odee1| >YYYFrr PvmAKK*-[QRR($&~AFF:5 \hm`Ĉ1b:uTipZe4hBL} IDATL > YY̼jjjbUX/DDD !!%022B:u ''Ǫu*ǩ;ߏ..pqZڵks)< --͗UBjÇ<=9[[;VYRAU6x(U (V@o /ϜkUKVVV xl f/-?gϞ!::ӹnqq1N<ִiSugEzz,\y:?Zc&77GСlذv8x@5L455ϩ]\\__I077ǜ9s-dyuPII =z43 '"dYB!B!?JMdOsX#99Y~>}cÆ 486RRR=[`}uܙiooc&B!B!3۰a:l.58BLSSy(2Bz۶m式}2 br(~b,_T"v҅y\TT;sjw9)׭k*tq)OwN>_ɢ?:̠III5kDJѧDxxW^]DDDD٪U0uT):*g233۷+:ԩZx@EiI_?{)ї+W@۶0p@zzzg#\oY2'"qƢxI8q┨[{?~Μkժby/l9r8`PWא8/33GbEb*Uf7ׯ_/,zRff&N8___YuA{.yElؼy+:tP`|xx'O,-0l!ڵkcccڶ XWL>@d̛7;wR<{ؒ%^Xv} r98vQʕpv ͅ߼ySNa֬Ȁ}aݺsff&ݰifT^={vcΜΖ=uM= Сں7n/b4hР8MMM,]nnm3g ,XPb{1݃m۶"(TRx"/‹ kb-"̙3ps%Gc[f1B| jZ}O];G!e5jt!3$SDg-iӱ`lܸzvڅ~ vvvN ~H?K!"""""\XGGRCѣ~qzYGD$ΝMG\| sl#GBӦ`llܽ{NDdd$zҥxL?~nܸcǎ ֯_8::7@BBBCӧAll,;znܸmٲgѱnbb"BCٳś7 -Z+W 4 11􃳳 5k !,,׮]ٳ~HHH檫cu_`i9'a۲e =֭ܿ/E]3fe~B888]ppU884jԨUU5aWLT 6bHKKWou"11/_樫k`ݺ puuǿcǎÎۅwW\F7=`ccܿAAu+wccczj^-Z@Æ`aQ 022F||

{ b-غu+f̘QweFJJ ͝;GQpVDDDDDDDTe%f۶ЫW]"QU|[rNĺ6n l@Dbcǎox ++ 'Oɓ' }4mZfĈ8s~~~¶7o͛?ӦP~S%}fx'W mw7ƌqGtt=;;[x?ghhuR]GGLr}f͚XWL:YLؼyؾ}'fϞ%_ׯ4cР7o>1o\{$*$""b-iUDDDTt IIIggg"gDDDD-[ĵk}0o<߿_YQyպuk$&& =zW\AK.ї*>>۶m-5XE_sW^ #""qqqGNNTUUfо}{zzzNHjwfĹs礎SUUر0w?vZdff:յ+~gXZJ1eeeXfǎR;4jiia9rPR4ib7ocػw^~].>mquZ+PxfjjB*W.J40gƾ:Wr2[Vm/ҺuL2;wiQ GǪV W|2 ї\k&DDDgϞ۷ *8"""RZGFFn߾ 8pЬY2KW^9)))aɒ%ԩm>sݻ] *R """n|̘B/ז-ۄyGII vÄ 999Ȁ#"4Vط_#0BBBV[Tlݺ](`Z@}v+n߾gA0޼Ezz &&&prrB&2۹st݅qHMM۱LNY0aΝ;K."** qqqEj`o]t̹?OpMļF%UV;+x;r0DFFF2յk7ԭ[Wk׮]䜪UDtt4^z%*rW^y7oEFF:@_jޏ,bcc#^ԭ[0Ittt0s,̜9 ш*M`` ~χ^Իw8;ĉ˗/ԫWC͚5nnn55uk֬YW<}.]ijgkļBbb"j0)вe+888ȝ{qfDEE! ^hAEEe кuf6mD}6rEDTQ)Xz Qy״iSB,c+###jDDDT;vDǎeۼybGII Ku5jFзo?ۯD} SL-rʴc322QyEh077/r^Wmqg~oCWW Ā[~}ֶU^ hll~_'DD‹Yծm)GEG(: """ӵkW|Wp̙3749vyyyOiΜ9pqq2"""""" DCCvv s*AeEAT%$uq*UPYD6;''EADTXEDDDDDDD_Y%KQgϞWyT888wn¾}п߽+=GEATi *2vc}@aZ O } ;;DNHH@xx8Ktݜ!<< III%6dgg#** /_ĻwJ|sdffJKNNbccV⯷}EEE!::L^"""4(jӦ !v>\yPh"󑕕H ﬕ#9v""*bc`8t """ C EΝ?y?޽þ}p)\@rr𜲲2FBjr}}YwADDhmUUU;vׯ/O>^;ww@Ϟߢe˖RM< ޽{xȐ!R?~,;zhTT G•+% Fpضm+֮]-ZHw EDD[KT Z^b kfiii󃟟VX˗mvE냛7o֭y>|>}Z`|vv6]k׮a۶mطo """*yC(;v=k>|Beصk7… 1tPDDDDDDDDDDDDDDDDT(6,"">#K,ԪQCbȐɑ\TիWcР 455"ږyb9; >\b6,,,+w#tyK,֖!>>A}lݺSLRU=DZ04,,""}FPPP%/={B-I_8~zvu)P%w;wn_~R˳pBQ  33S}[g-033Ä :FFFbʕ ̈R;kI^HHƏ'*.i֬f ͚9@__o߾͛7qb.^ @\\®޽ DۺtqƸq`m]FFFÇp1\BiݺŠA%lٯz7lǏkWQ۷o \~ {Xhα;DEE ۔1ft ޽Ëcǎe͚9Fx)o`ٲedffgΜLJNN L2mڴ-444C~lذ^smlڴc>8H~孳Ԯ]BNxx8֬Y)S(,]EAٳa$&&.] www)83"*o>|(:ѳW`FDDDDDDDDDT\Uk(: r+77Gz/rssݻKEE** RWStDDN?%rrr>W.!"4dff+6n$N;&Mā"88X}>ìY3XSSk׮_wT4G&Æ A\\`Ypu튪UXq&8z Ӄlmm1bH<{zzU=axQ1֮]GǶqjjj]k[ ? {7oy33_ɓ`|cccc[Fp]W/?~ehh(t+K,珅b|qnXKйsg 4Pww7\p@'7I"##666ذaի'zYfh֬6mq ۷lb-"""(Z0o}:<<<qqq߰hїҥ9'!nѢ%KMM-yDDDE@DTС:Ti}^xٳg:tXIDDkYKJpM\rY--}E,Xv>}۷e_!55U-Z\P+VZa|OLq %%7o3DRSS#!ӧB-I,->?3B'Of͚E=zȔ#evUϞ=)իhҤڵ gϑX,W6n܈t!ٳ'.Uxj8|(իWGԩ-[(X߾azܼy@^ǺgϞ#""/G50m4?*Y9vJѦM+W"&&|rL4 5jPpfDDDDDD&..7oRtDDDDD WWtD)+t괿DWn-%(`d$ ZDDT;'mHII 'N#eׯС~LsM6tںuZnnnj7bHX n߾b-""2V^;kӱzjs &@`޼yfB IDATiMi0vRY[GǶhܸLcǎ'9 Ғ۷Bܥ3lmme;|pQAܵkd~ 0Hqkő2#""W0{l!NLL3Z0fԪUK׭[P%DDDDDDDDDDDDDDDDD)*'GzUlyPKgϞ 444d{{{^tIjZ$ԫWOx󪪪hԨ_ ?3. /* jܸ1tuuVQ ˴cQzu!y34RY``(nZ4PESRR*P%(NMM9?"""* gĉ055իWŋ T9c…B;xxx(0#""""""""""""""""DU vTEps)wL_dZOˡ\a-&&F^Bxy-E׮.aA۶ЦM4o͚5cDD(%ӱG͚5KmmId}>+@LL 233ERNU=F:2WEQʆDJ www@zz:,X7*83* e˖ ޽3g΄3#"""""""""""""""XEDDR}9 D'77{O~III5m˗ٳg!99Y؞ӧOSjpuuAߢSN2 j SދpM!~- K:-]d} 1zVG5"""*[Ĉ#[bƌ ".]@YY/Ʒ~ c9spQgFDDDDDDDDDDDDDDDDb-Y:QIKKzzzrͯRJу=H~ 8}%Kbc`׮]صk:w_ ƥb}%JmmIw*z}V%"{POΉʗPE_~l̛7-RpfT[ƕ+Wǎ˗Gjj*^|HDEEB]]U~011Qtz*22!!!} VڨSS#""""""""RcL^@"πYS "*u /ɑmy5їrʢ8::Z36ɵ>gffUV#;;׮].]۷o#;;[4NNpER)|r.O}oR%::חy~TTTQ'Xӧ5k nJT:*B!{K,AxيKKJJSGdecr WWg sϖl_9K]¨Q/A`8<z9#׷edd`uދJSfM 8&L9Ut9P d)X aۅRDD_]^|)/d+`+UJJ 444`nn.׾䥢6mѦ# 11/_ȑ#B1۷o1e$@4?&<SӧO.\gET1^z8} ?5kʼ˗Z޽g__ ҥKiFر 2#x9 ++KϟGsds%7sKϟ?Dzeb-عs76m*WnDDDT*W eeBoЪɢ,䣬XEDT~Մx\|tS*W W׮ذa|}ψ8Gv8,,L, Rdmm eezQn]Q,{B#""tgggtQ>}low &" 8Px or߻W<~AeffbUUUԬY[kW5,pׯѫWO*O e˖ܹ3LMMEĠgopղL䤭 KKB())_/bmm]R?Ɖb-E^MDDy }(JuݵqEƶkN=RФ=ƌ+Qlll KK+! £G$vI}y$&& qӦͤNvȑÅ񱰰PѝkԨ!םԉbHZdEYz{cyaoo~): ς [湩8z5UVرpE|[رعsx, uJKK ܽ{'N@ffյ+nѣ8q݇;wpu*KKKØ1nHHHPDDDD$ƍ+:""/ԩ4HF ɑ\yPK*<ºukdy&VӾ} Ν;DE3`ee% XpAiU&^x!Ь0VŮRFx\ԩ-$$Ni-F{HQZloViP9/@UUUiUxjjjӧ>ǐ"woΜyXh1lll"q\ǎq/]m)";;nBY;w¢5j`]>}˗X2ɗQFN\x% |ݼySѩg ZZZNdbb-""}z͙3л׭B,Zs 2h`Qg{[7WxB3g|ѽע;}? 4(܈*sM4ժU+`ӦM5<==1`RW^9"77W{u։nKDk[[;!>|p|/q.000(V[qPP`]6n\/<’%Keٳqbb"\RQPRRܹsQfMEBDDYRQQI`iiTHү.#kDDìYq)<}@^=A 4@HHq1$&&BYY Ν;eڇ.v WWg1d`lMamm +++dee#!!>͛AtPաCGGGG?_ GǶ,,,`hXqqx%>K. ?TTT h G 6lX___8::7@BBBCٳg9XXZZI]W;޽prnݾFajj;w8yrrr9NNNEk׮\Y۷o) .ݳg/&֭[nݺ!33} 'pvvAf`aQ wիW| ZXb훈?EtOjܸ17nIk >d!""yȻ#ѻwm;WIiy]R)i P\ּysoڴ(q::w\bQܹsN2b4jHi^}455qaT}%Ι9s,-d.+++ŰaCq}akpڵO;Dw.^pqq-tm*l Cxx'd8=gnz [lƖ-oڴ)6oZ9s6<< Ifee}!I{۷CARR ..{޽{ΫW?]]O?_ҊxQGDK߾}p{gB>:w"󾢣$$''8_FxUZ]&1JVobbʕ+#11dNDDD3f`˖-v;}mmm١e˖pppPt:DDDDDDT /*B^(EDT~fÇ^zСCr¢|}efX^zUeeehݺuG^ *0rH#$$ϟ?/=DDMFFnܸQ`6ڶm"200@F`eeeeeECDDDDDDZDD$j qfL<۷7􀦦&૯ȑ|;2GCCcǎȑpu $1⑒j all ccccG'J]^zذa#Çt"ׯc cc _}vr\\\;wn w~&i04### M֑#Gիא;ۋ66r1aD 4+F #4i},,jɽ n VTT^}X-caa!:+:2vmKl߾O<ٳ~ ›7oUF #8::SNVM{(C(4.b:::03)\"""*DAD0p@X+''ycivG \蜔(cccSS?5%"""*C***S!"*3iiiطo_pwwW@FDD9BLL TTT`hhX 9sssķӫ*U(:bǑh޹Aff&SbkKCUUKӅ+ќ222!Kt}""xXEDDr0uuu8:5_>ׯ_b姤&M,*, S\GcԨ5j'%6ԩ:u /,GF5OUUDDDD$?i!xכܹ !66ս{(C֮mm 4@ߠ]vZR 4441WǼ82!]JO`Z__SS"""""""""{.ݻ7nnद[[; 4 1edanЧO_^z;w3~L9m޼ 7owϯ.xbbb}6=[nΑVT ޽ 55"׻r2v%?42iFܺu @ .UaQ^|%Kw>ر#غu =ׯ_ cѱ&N(P___?ሊݨL__-[_޽|~ѣGX!h޼9Kغu+ 11a[.Ǣ!!!ذa=;*:~ӦUuƑ#GN6wΝ;cĈ[n'"MZ *555k׮>C1s,L>aڴ)*t\||ӧOa/lڴE(*Ltt8:tƍcyشicYYYԩmAQǬ+/,vdd$֬Y`ccSd~cpucBCaÆؼyE+GPEDDDDDDDeHQї% w&11յLZ*8yw*e*20&ܹ#{rDDDDDDDDD_ϟgo$jUZBݻ!<<,SYZZ~K,)-,,}-||NE2IMMŰaCdB-I zOYXK`޼B-Ib%ؼySkI-Ԫ\&ٳ.ȑ&L_PKqܺu }n6kL,\@tN[Ҽ7o^O SN~~~Eb:5D۲iF ODDTX,"7 6C Ĉ :u\0)Ǐ> oӦ̹N8..}@ۋ;wn˞Q$|oDDDŋ6===,_E,&N &QFPTʅ?~,N:)2e*|}i(":t:uedd`R;/*SBSI<'''a\FF,<ԩSo-Q>999λ޽ח:g駙޽{e˖ &lkkks0~tMvyܽ{Ts-Lff&~qhiia˖EDŽ Uȶ x IDAT{ji߾=/+UOmwѱ-siTUUtR vUt 0&MykW888/VZcѢPWW8z}'IIIr/ bg-"""""""R4 "/ѝjtIcee%0#3S9`˖͸p|12M˖-E5Ewݻw={&eneK?*-˗B$*nfQ.v!DEE yaee%ECUUU8PC=`i)q,^XTsO***XxIlmТE !{NtK]] 0zJ3gn X`ǝΤ߿({RǮYFxljjŋ\7n"GsDDTqX gkk1c _QaG "ofggc„R/*7a0axɓ'aƌlZE[ϟ?G^ߊ.x/88]tƑ#G-y{]]]\J4~/:Νۅ }+A-1v|(0aLTFJsZs-,,`ii)7n\/䕿T=ekaQKtׯ_+ѤIB}Vd$&&h. 6t022*r(Bf͊gaQK?\⸇",,T;u --"cG'qvv6dKDDjCJWNNXEDDTZjVZ): """2! ,|H;wAjx5 66Vx 6ye~CǎZ¹sЩSgXYY!55wŝ;ԩ^o-+cw&lz Zp-իׯ 8>^x ի`Ԩʒi]v…?COPP:wsssX[[_֭; WΛ+dוtJv3 Z2|~x1ѣG%<.],tΟ?/!77޽͏)?""u/"""""""TyrH:I2`Z5fXh?4nݒ8FWW *V0a",,,0cZhEдibյ+n޼a-.t]_S/}Yqm 7x =x \xT+33Nw$޽{^nn.RRR^|IǑ|jjrխ\*fff`w;vkU%%''}|""XEDDDDDDD_ i!x$7q%=||ѣG AHcdddС#444ǏeZ{Ԩ5j9u uwA` @MM fh޼9֭+#>rʘ>}O7oMMMƨSԊ"""""rXf ""KzN>zzU Y^ISl)))%JK%saN8.fiigJ*EGGcNDszOt /*)^(EDu\U!(( b`cwmvw9gn3gbװDElt ts+sS;`9KDDDDDD_oї {{{TQjԨEƳKל(/̲WFLLNmڴł ?ςΎ$r1۶m|ָŊEťKDZػի;#oeOMg*;tge36V,0Nۢ }Y~DDDDDDDD:]]]8::VZ]6?cǎr022BPP 00cJGUUgZZy111}})eæ&5Uʳˍo}k =z+V̳ݛ7o9/XTT } \ """"""""iuEڵaoo555[n̈X"={HNNann.m@@qB%}uuacc___W]]]H=KUxxRCGGJ?EBBttt7}ZvvvCtرfCDD_BO]`-"""""""Rr[DDDDDDDD_.]`R(P(V'OJkhh(*Hj>HۨQ#uzz:<}*:5j.4F:urjL͸qw^DD\puƈ#agg}}}<~B ر_ 6mڌhLl74h[[[V/2jjժ0OBJOO"""""""/kĬkDDDDDDDDDDDTX/^ر±Wի2שS-V(X˖-R;ˬ_ti9sf+p&@>}all1cF#&&',O1iTؾomllkn̞=Kqc;F)155Ŋ+1lXpp0.\ #0to"X p3Zش_ᅤ83""*T {̬EDT1 NNuT(r,yM\$""԰if,Xr멪b8qhjj*}eVZK.|*ڵk=1t0hjjIjN:a-Zju͛yθ|*J,)yYo_Đ!Cuܹ3=[[\?²e˿̾ٳ[ {{{1mt̟P#"3k(ZdDD_hi_ """"*C0x &5557zq'BBB pp^z Tٹ+ZnSNHLL@ɒ%Qb%ʕϜ9 Æ kH +VĬY|4(Q%JD%PbE4m ZZkn ΣR%;#y >!= +WG``T4iyeĉIn۵k7ԬYS([ZZZIv:>}W^!,,&&([m۶Ba%Es+SܾW.[R%I`ff*jWTiICHKK?ڨ]tGGC$`ll,|/J,FDD}(Z|P ל233#"eC^ѫWG}лw<}X&&&ԩ:uY(.6lVVV gP@nѭ[ &# `ddTS$66ՁY>؛0 7$&&"""***JCDDDC󈈈Hyl!"*\W\Ço߾`qrM6 eʔ~TTvw >RRRvc)111ؿ.^t'455aiiիsggm::: u w#vaÆBSS+_?}pe.z\hݺ5  *X88= XZZZҥ ڶm]]|#֭[-1k֬|_/"""qa BJOOyI/_`}9GEEFڵkdɒ}wbݺxI&MD2eTP&ɸqcrj5k  =ʕ+555ݻwݻ]/ PWWcЬYs888@__/_SnZ! wofj̞k}߻w DjjpZ1cam] x]~[8ŋhB,Z$1&N#Pq&4h0Q|y!,, ޽͛ؽOn޿G>%ؼy ?"""q|D_aaa5MHFi""""""z[_)F-4hP!"""l""*\FFF022[[[aڴczڵ)MؔA6m1h@xx\5f(ܺuZZg{*(;wPٿbܸ9~?YZZNNN<'\.\#o֭ؔPfMի;zuGԯ={v~_`ƌАo\\F! Zh F#hӦ i&t5jԐ9ƞ={p'W&NK.,#z_w±Gߖ2Л> !/ y#Zؼy H԰doyWBg``(>+Wv(`z֧l!"oΝ;GܴiS_rChذ޼ׯ盜UV\ϳ*Zn#y,lڴ9GVV͛7G6mqY@D{ܿ_n1qCa̘1ΣlrXo c9ꦦbժcK.Ð!Csȼ>ZʳE9s)wR>҂ʕ++*ɬSQ Um-y>-Z^A:.]Ν555cǖ-1t$%% qa8;;+u\""""v18E']Knuرc2Fyy=@M tyz^qJlٲyW`iYBTY뢲";E|2Gp]@GFF͛S8fmmRJJm\ yY떖S| }۷ի9UPǏWQZ2!)"_ PY3eIaee-*gҥ ʖRXλ"]]]hhh %%,ׯE#G222O;O>322/MMPX1I砨41 ǏURR$""!/=iii%=yM4a}BDD߮K IDAT?R>ܕ-*(mbŊAUU@~}Μ9YĈʱ 2ew}EZps; }}}IDDDfO""""""""""""""""""e*Z;}yB퍌re,ebb Z\\\=eollReDDDBCC1o\^DDDYi @>"%44Oʻ>8q ]vAXXe~)ml""">qϛ,X7ݺu̙3+W..\ wDMM 1b$tq`aqԨQ]nwFđ#kuʖ-`Q֖ۗ]r֭r먪-ZDnakk+w]9@f'^9skncxϟDž %^%wѣR:ή];nZ|1-[߇cժU8v""ދlٲ~~~صk'@O<} ۷TtږGrrrup}ܿ? ;w].rbbbxJJd2;&N/l"۷opl7\]rʹ/h ѣ;nݺgt}v!aܹ[nCmlGќRRRq- 6D݇dKbs """""""""""""Пrff-"oWŊEeE3U_ASQQAẋ`ddq vUVyUv ]vÎ;$vhjj*ml"""6q~D$ߟ 拎B R*|c$''#$$o-UhZ:::FR___ [еkW?ZZZ9<** )))uuuw`ڴ9tQ|+OCp?ZZWL41GVRP\9/nT# r7aLL r[q,%EJ)+ȑhԨq'/FhᘦJ.,[zzz¹DDDBSS 9;2"""""""""/)))I% qFDDD)ZGDTt $UTTT'OaEr*,, ׮]ʺW̍7cǎUQFիW0s:FÆ`-pu݋I&|||gΜkn);DD .svvʕE$''a;w>|_~X0`v KK'''cϞXv O>%p>}:tnTPׯ{*47=sj9;;cE(Yn@@&MW3p>~ÇUh8s4="`ŊWQQQ8~؟wzK)?e̿>WbZ{a|2 VF^֖kر]tlĈ5j@ÆeܹsDke?4ET3WadE PR*5k&cbb믳*^8Npyx\CϞݕ}}MD=x<ʶXpq?/:t(4 $755X\p^1> ѣGcǎQQQ9sQT)ؖ-y~%ULLL$UfUXb/Yʕ+/f jƌhܸ툈Qpr 'Xžwswr o=%"Jk1QWR%Qy_)mNQZu6mZGf7]6mɩ~>f?W׽G3f4|}}=E:vVZ h4i;vl IJJ ѷoL8AnŋP>t ~=޽{kwŸqc~RUp֭[ڵ /}?=wvQyܸ ֨ǎL͚5Pڶms̕N[[ӦxP7} ,ڴDxy=s'%KbJQ~Z'44݉} _::H'`-"SXl޼ qssùsamm͡CI+Wwihh`-hժo߾A-ѦM[Ԯ]W'qm\tQ󲅅֯_4CZ'N~OݹsM6FfP7o[\ϟME盕 lنv%kim_lmmak[jqMxj׮1v:3Z>GӦMмy 888Zx6ܸqO>/?L CCC9r }͛2w@ҥ3>SSꟈm\ 7t=ᵁz!MjΝUԠ;;{<|EŋJ'44TX CSSz{wQn,* ޽g亦QUREx X[[Kjۭ[w>+"""""""""LZZ(שf""VdTU =:.\C"99 7o|Obbbrűc'лwO!#Ǐq!>|H6661+OƸqsܾ|a``3gaԨpw ^xQ ctZ;v4qqq8y 'O*C0\x:wcN\s ""o w_iiixP)PKŊ#\~/^;vWlrС#tqγnNKĉy^}}}o,,,$%/:::pu݇s2:tǏ%J8DDDmo~AeR̙Xx^|pۏ?*F Qy͚5ظ/ddd?#[>|PO`|<醔+9*UZ`{ΉkNDDDDDDDߧB֒(`-" k֬Ś5k "--USR\oٲgφ;޽0|FFFS}j _dkk;v.]۷(Ҳ,--QV-h"םuƎ PWPhGxvmTUUѭ[w8;wţGǏhD(Q$иqc)SVy/_۶mǂx]>>|>SKKKԬYZ̜9 ÇIESS v;):"""~p~D$=lFަYsEGߗ[a9ؽEt<%%w;wiFUV<'88TQnD>>g 7!"""""""%&&"""jjj077g0SْbŊĤ#<< 044jEFF")) ]XDFF J[D{$$$x_t3" S+m4BMM PW/z  1  DGG#:: 066}d"*P[I}PLY{Ʀ aÆ+afQT) 4 wn[~|UUUc 8:wy0p|aggvhРa%"""A{&~Pl\ Y6m$ RWWG ѡCT###Pt{rBRTb`bbY7>wJcuݻ6mQF)OPP6m 7nxOt]TUUaddˣyhѢ%jԨ!s,)) SNʭZF.]$K;q$TXQfK.#B_fdɒȀݍ7n 11@x8qڶm'۷oc͸|h+g^;vuOw 8~P3g.,,,#GCRRPjj޽; kp.ܹsxS*Oϛ?~voܽ{V Dz<7 ŎǏQX14l?zdbaӧg~?~={}x0:֭>}W_,0##&{N޺ukafGf:u`РHHH̙3lmm1idx"n\Jt r8} "" uLMMѡCG5Zwyž=qܽ{GtoDSSu?)5E6Xkp'""""""908 ;1*>(HZXL(Сprg۰0ƢЮ]{[Pիz!##O<]7='KMMşX`acժ@`ժy -#Xpd`˘>}*8M3>>3g+ ǁq~hKK\皔 e8;;C2'hSNԩ:t-[)))1c:߇uHܽ{+V,kdn̝"`-???Q^z y1c1cFBIII}6.]`Ӧ-B QJJ O={dXx9O,xhn'NBJJ l#<~v֭[Qz7oġCsoȝӺu GبQ#IZ6ʕ+d~ s [n>} /_ԨQӧπC :9ꧦ<=o`ϞpuCC<\떕v)))4h0tttK. :ӧ//.y/_ĺukrnsw.GΝbrYZ*""vľ}Y8q9m[|2|Aɸ~ׯ{`ÆXvZjDDYzD!"""""" DDhwUyA#K#.\$)P ޾}wϔÇ>2jj;v?ׯȑchԨƍ#;==nnnBѱV\Kdᔈ/;W9B暣tuusOMMEllLDGG[7g>,L.]?;c׮=z(PF}ҥ/.9PT)ə [RRu#PK9~8&O DIMME^=sjjsGDEES@- u늧O(4edd`ڴX`@-^z]$ϣGrj\>}-2Tx{e KT}UV֭[B ֣g^'5=¸qѥ(OBBc|8Ξ=k׮ZnJ(Q^zӧOaBw 0PܦM???@}0`@8::BKK s6Ν]9_)S5j4@N0klsE,ܹCt]߿?j׮ SS3zז!00!C嫨P"#?`HLL.~i իʕϟu/vvܺuׯgg^TUUqm0o\M6͈f`` nӦ-J, .4i&III8xPt?ʕxL?>ԩ n݊'0mpttD۶r{_W(bȐ֭;ʗ/SSSDGGgm8vLSʕ+˽nDD)EDT%% 9gADDDDT002\w"kZqÏy3%}FIԔ".?~ܮr055nݺ`(QB'xzYaFFbbbڐ% IDATgrҥ%+887n\T#HQիڇECKKY& ;wXf{焲k:w,WfMԬYNN1`@?aŋXd-Z"<ܹ޽V\%ZSFfШQ#L8AXLMMݻpb};wVxmff'OTR9bŊX"vpIrm7 jժZjti{ x%ձx >Bǐ!;wT(XS?´iEY[[uC^=KVX[XXܠϕ]&Ls;ҲeK O<7z(\.>⧬sի;b۶m([:uN:^:M*߱c҃Sp'֒>555 8˖-Y_r-Zȳ?8p9֨QeuEÆРAC̝+6n(7}44iTf4 g el߾#G𕡡!ɩ>Zl' ==]u "J֠J$!)}.o]E?CRކDIuKγMDD֮]#OJ,)j/ +cccE7FFF^ClYvK,V0{zK;?""""""""RK Y`̘1Xbe@-Y֭+3"##^tlժ9Wv+:sNDEE9OH]]s΃p̙2u-djbnn2&\?qƋ6z0pZYuUV#P++kkk>|TرqqyF>}b޼r[ZZDzʕ+Ʈ].94h0DcJ0 0Pl׮ڵKx޽{KS__jeE]± 9rDnYձe˶ "zDT?0CRDDDDDDDTCDD4o\tCػwo.-O`-Ǩ\gϞ\'''aƌi eA8ɸucǎ0;wVs4xx\SM~…r1reQv2e&v횴100P>udjٝ9sZi!ݻws """"""""GyS(cT3Rݺu_a zgzIB9!!<%AMM-:h`w"::Zfݠ ᵁyEe˖ðasΝaƌy=(ǒ禢'YDѣPñc$ 8|PVSSÔ)Slg``#GܹC&Lˢ=|%ydaa~A(;w6@W^֭BSr32h`YoI޽eֻr劐 ƏZjI˯΁P>zT~@|hwJ}!C#o;tJUn]Qyʔɘ7o>WwyJc͚;vpl֬W^n'Oa=[w$7ѧOoXYY_ׯ?d 1yDᘺ:sYzuGlRhjjaF] ૐ >6#[WZZ,Y ###Cϸ ԭ[Wx -- #GٿR%;BUhu\o""ʞerdϏ7nOȱ)O?k׮ ׯcԨy+Y(+7eʔ^gdd 88XfFusbƌS_.]@CC#z666rffʪL2r@ Zj+J;vxfgпc4/HHHʝ:u5UV#Glyz@FFz#iu@X 2 55{)Swq)*4XXjjj~۵kƽ{<@||fo_۷a(U4ϟ!99sMO•+W$S}l/^Dկ__uO< ''qRϞ[\B8yM6F6-GBBB*T4ׂ+cժ(]eؠti.]  x+r} OºuPti: /^co۷WF߾vdѢ%Ƴgj̙?S$pOOOǻwo[\p>q2r)S湣O?MOpB(^8V\cG#%%(>%\\_.AUUVƸqcE&"""""""66e ߠ@QY^VzY444Pxqapvٲes}?+cccQU͜9'NGdd$&LSnzhԨjժ @SSKAjƤ˞-kH }WOFv ;t&gR|ى˛cݵmmm|xa@޿=~7oҲ]r=Y+7>?.!"""""""UDDTLKB6m3 \ոqc7\]pEkhh`صk'~gzJn=-V₳g8x(dddHWhݺ V7oFtXsSкubŭ ---lܸ ϟBkkk/`ܸpvv.3338a֭ Axx8ËWWWL:gϞ[GOO=zs`nn^Z@/]۷ɓ Cxx^z VݏLMMall~^H̬6FFF)%6l؈1cF#4]ZZZ 7[Pڶm.]>W"A#nYvP&/~nVzz:```PǛ7oDee_KSzYկ_̝;GHۼydM6 uuuѷo?)9W-55iiB966;wPj♬EDJQ.""""""""u<q5"˫zK.֭[m)JlBz+o~޽޽Gӧjk׮ ~7ݻOנ///xyy)6M#0011ш!lmmakk;;;899It ]vڽ{(DGGbŊGJprrcl ZDvNNNx1=zG!>>TmڴPGCy4nx7^Ɛ!CiJFJJL+E&)gik޼`7||r}¡Cl2,^jä9bl,db&& jʗ+ue zm۶?HX[[ u?~ BgO䀹qajZϨD"+דH9L"""""""r 9pމdh֬95k^"X0ZjVZE!+++XYY;Ν EFFFHHH ;;[%1 FGwWgspv|}}˗/!55UTΝܹ#||ifjWW7Hk\DD*W,}xxxϮYR 6\Hm[1aD͛76v++LXػQuHBBBШQ#9)Ǯ~}wԯ7on݂2 iiiصkPf͚hҤc({LM+:::044vz}=#*++Kq."""""""y7u뺊ϟ?WI...˗/%Ǜ7os-Zbq TRUxŋzj6%yǏ_eTėdeejԨQ!I"?$D좠f6|pqpp0N> 8|_ 2X+sʫ/A^Emڴ$C>}t+\tIRݴ4?~L(MjQ̙3ŊEDDDDDDD!|DDDDDDDDT\_$"ԩ;pΝBZHӮ]{do޼ qqqE_-ر{SZU&*'&&ʭWzuݻw\t˅ rXF=z%]|ܹsummm 5kքPu?={VR?X"FعsKrKOOʾp. z%t)+?EddPر#455 ޽>L&È_46uo߾]읯`aXo9Ç}2dhcǎP[[BB.StLѸnh߾=111)XzR׋333###m2%8qoR gJݻbry)WiDDDDDDDDD2e>/"""/uVXz {6n܀N:M6mΞ=tիŋ@GGGT7;;s.1cj|!88~{ͭ^>|(J@ ӧ/֯_', Zn]1z(ܹs5@۶mEcǎ1cfY]\\pI@dd$ǤI؇   W^E׮a۶.P3f233cCcƥn077vھ}:wM?'''l Ο?@SWڵѨQ7;;^^=nztiЯ_ ڶm-Z*sΜ_qe$$$ߏx{/[z êU0wBw#"GZ\$EDDDDDDDDNDDDDDDDDDDDTV[` gϞKNhݺ ֭:uϟ?ӧOx]Erus&J`ڹsM._sؕo~*W-MBB6n܀7 pttcTT  9rDHLiӦTn 6DVpY@ll,ݺu[=ǸsN:TbQ> Ɩ-q5m;wAF`mm7n8~hw׮УGB3f,6l؀ŋz*TT ffkvf722DϞ=yZ:| R%{ܼy/_ӧEF5kv.444w0ct9u666Z׺ukL0> .$k5x{8w^~/]QpssË/p59r^K366… Vsիh׮-ڷڵkHJJFll p\~]4DDR0YʍNwDDDeU.]`gg>+U`Р.88q'NEZZϟ'{!>|Xh>}?{ܒt@B"O(s玈dee>>>jkkcٲɊLT֍=oP]k׮QXifXrU'`yuD|XVV|}{MDT'k)8eI~AD>"MQ׸qc? .-EDɣۣVZ֛4i2`̟&eccB~~  ¹ssImaoѽ{":::#5j$_^ձrj<[fjժE*<4551jh4IwIСϟ HLLTe"۷ɓ1sO+f,;S]>}vIJeٳgEl)  Ǽzcԩ3: 5hK.C͚5%ݬYs\p֯_"Uڵ+ZDXDDԞh!L&+H T`ؠvԩ3$ۮ];nW^ũS'CDGBFF,,,6mڢe˖Օԧ1|| J*I~[]v:+VĒ%^| x18XXXpwwW:jj8~Ν;'|#ܹ Zj%inѢ(u]ݣG{jժb!5!FQR\2 &Mưaðg"22iizBʕI۾}-6A[ IDATCSSkY5Dť\\\sn OOOxzz?---4oޢXm-,,aaa)~ʕѿb%''*lgg;;;cXbW]]by[`Ԩݏ"pqqRh=##z=5j@pww/V;|.+++XYIO+55{uԅCOѡç*7:v섎;HDDԾ}H8ADDDDDDDDDDDDDDT]v"..N(1BmjY+yqpgNDDDDD/^_aN:_ *o""""]FFVX.Я_?5FDDT=YKZ27"""""""y"DDDDD!38::JnS+HuF%W0|*VT[fHZ0?ݻIjORtbLVʑQyd9RTT9,[jU1""""""""""""Ⳳȑ2lll;@OOf͚wDDeMM"""""""R5ED<7>}I& UV3Y UretE}ZYYʕ+_SI_hDDeރ EǴȏHC$: ~D1Czz:@KK fffjÓd93,--}2\xSaCA'keee=E3D!hڴ)\R8]]]Ԯ][a ZD4h .]Y&чgbMr'z戈clx{!%׫ʓ=P!SaQ R{02#!"R^z```$uBDTnуw'""ɘCDDDDDDDDDDDDeʕzgQy윉zED1S{FER5}0d;"r;wVwDDDϜTI|oEDDDDDDT>ٝh>M6vލ3gpd2Ԯ]M6EÆ ": """"""""""""""""R 'keee=΅RD!P #11Q}QBuADD(&y'""""""""""""""""*9jORHJ&r$DDOOOzzz""R$22oƫWQ044]%ԩS*#66aa Ett `aaWW7l'O 66鰰DPj5u&#""*̬"P=LMMWFF8nn`hhұq}"== 666_*T=^ETXX&DD2ŅRDDDDDDDjYADyݼyf… =gbb޽?ԩSannto߾ř3w'N@DDz_>ѳO޽ ?y[~# uŊ矽x:UVàA#z3g?:2 G۶лpvvz*+9 t }g̘^z+|ѣG~~'Pymmm4i[.% c 6m(O:+ c9r6::ԩ#~y%CDDDDDDDDܐ799毿b9̔7o~:9roTשּׁ,8;WGZZZu333qu\>7nM&%$$˗=+BO^~~~?~,gϞbYX~o߉ڵkZ_ՒҥEB`` Yf ϥ(| {/Yh!,FFF:?޽;VXYdׯE1eee?~,IKKeV{ADDDDDDDDDDDDDŧdw%E3DDDDDDDT8Aw"˗/ìYihh u"8 6O1'j+W^xHK{Hu% '[OXZZIIOOhiibŊ c֖{|˖͘4ib5KKKT  pѮ`!!!ڵ ||խwAuv|Dʕ+ZjFFFBB^"$$ IhkaBBRRRrˣ/̙?aŊ[[[ϟ?Ǔ'ODvL!""E&fϞ%: T8côi?*=3 ^zƦiiiغu ,Yp;s1{vK.Tl/H¬Ys`gg'ogW#pY(5nq=z#ԩ ohҤ_ƁkM4{&|͛7I=. $ju s {{{X\\֬Y o߅c/`wo7NJ+ѥKhjj 㫯#00@n^|3`nnk#"""""""""* AAASlr_)GQIDDDDDDDj!8Q3'"y&O$JP=z4V^+J]5o.$=/c̘1rr#pYX[[ 7oބD%_t5j>hhh`ÆMXf]D-޽0r(ؓ'Oj1ܤ4011&j)GHL 4iر~FQTXSLrnZ\|Yqqq033!tM9$I+&&:WFDDrIIIx1Zw""p|駨V,,,wwwcB[n~Ÿ*344TW^=lذ4^ s"""Efl OƘ77uCDDTԞ%8Jqqw"v%*8I?+ƌS`#Ukݺ5Klk-,,1cLI0i+ܼyS嗒,/N:cРAh޼ Kd24hР6VVVWa}0RRRMMMXZZBSSLKKCLL 222`ii ===D*_jj*"##QB\RR^ LLL ToiEjj*lllzo Hl11HNNU dee!..033QHhjjZZj_ODDjYJ ref6Җ\wʕD----4k]vEzajj SSS.~y/9sFww1033{ ɦ$FFFXwqСC9rO]ƒwJϽ)8Udee!;;D={Lx1ʃt|}󈍍Ԅj֬{>~o۶mɓ'3sh۶> iFRo߾ԩSrvx96l؀!,,LFghhVZcȑh֬qrap!/_LSSST^mڴE۶.7-55'Nw@Ϟ=%p EnSNa߾iaggl:t۶m E1~ر>/_5kVӢ1O3fyO?8pP1c&ً۷kHMM긺?Lj_ܷy&\rx3 "ƎFaK%= .ʃ rdd$6lX???QB4o}}_+=;::bs?>>[nŮ];p}a,---4nG߾>dB""*>ς*Z$_JDDDDDDDj!8&g&&ƢrhhR¤ONN|={ӳim8*V(*/]:u\J&MФIdgg ?ΝŋxP/##˗/̚5[1ʞK])OP8::kccc~"" Q):cD1hBR_-łr? '8|}cٲϿиq"=r0݇M}wR֙3gk.ZretÇRFGGcEpp0"##444`jjU}ر#6m mm黶%++ 2+V&tuu $ddd 11Anx 냫WSSS iiNguƾ}XEױiFѮU@_yIbׯ OsyM#l\ฮ.[.[eUjj*z[8pzzXl9222зo;wvÀqq6x5`ᘶ6LLLJT'ݻ||N(i٘$Ci"rDZYYY /#"""""""R5D睈HL[[θ>ŋ`deeI-5*"ehڴv. 8e~կ/ ͛7FVЪU+L< 6s}dS.d2pɓJD|WYWWWII -qssܖHxl۶Msr&J֊kժʓ={qOa/_bڵXn˗B ӧcŊ 䞝8!00駟?k""QGT xHԪ]6ƎZjťtuu/^ƍ@l޼c~G%jihh`رw6n߾wx);^vk֬sC￟WWWԬY s.ƥK6}􅳳>}||9=Tf,z􂕕p<99/_Aؽ{7Μ9-)4ct!Qk֭\]]allwb˖Mܹ_};&$ju)Kԭ [[[;;;vGpBw7o.\Xฎ&e( ->|o IDAT ]]]qsssA1+\rEӭ[PŊ+ѣGQ AlA Tݿ:s*u\r_`95===n-Zyό lٲ gϕDŽ#\rzpqq zW^!)魤K˗ahh?g888޽ǏÃsW_OԮN:Sj֬aÆ7nܨTV?I&=7n}2KTa&彯sU|73g,!i׮۷$5'NIvs@zvZTZM|FШQ#ԫW&MoذZDD=.K@a`~U3'z˗/nݺHIITreQɓE%%N^* 0@('&&." LMMEeEwؤKT^OI֭[+JkXZZJjCY/%%֭ʚٳ1QJܸqCӬٻEEٵKStU] [lAPP^~Xܾ}VPڵk5j¾/\ٳg esss̝;?۷oS !!wŞ={лw p|7o+= $}Olܸl,[TtlEjҤ lqF~8rժU ˗Px#---̜3tttcG[nmVn;;g-JܹGs:::]\\``` <5j#u#!!A86qDO>pqq6d21tPiӦB+~Q|}}1e88쌞={bΝ A~JQYt /+bD 3R]ta#O4iO>i%kNoEoccΝ; ϟ͛7r놅 %QUZ #F|Uh]]]!!LRdO$96 ;z/۷o~㔄dݻW(kjjb„ E366_a7n yq޾::t͛7$ADD6&kvZDDDDDDDT8ADD2 q]nSNϺ ..::vApss-T^v Vx:u+Jƍ&|Ǐ!""KD~t +Çx"4n'Ooqeee~pwonvv6|} ǴФ}Ν;K%~GQ_}5-=NѩSuœ'Oбcܹsii믰z*ᘞf͚%y ""Łp5ܵkWoX`mm7-X@nݼ}{zzUR\QDDDDq;Dž DcAKKKR^TNU*U ވzu'+gϞ.^YnERRrlwcƌK]q#B{v_D r@7xׯzyu@HDD7i߼Jw"""""""$o.s?s"}6l86lX/7n@ÆPn]ڵx 3fbƌEoffSO?MѼy3T쀐wﮐԿ̙3^gSk'OD5pu<= K}ųgϰ`p, Zj G8;;Nj/pF """NNN(- .#Tq#\gϞF111|Pnٲ%,,,$С\\\#ٳgE""""*EeU`',L}J$ֆpW^!##ȝVZyUXQTVԩSs@Qŋ7n,&NƍEh͛AGGeݞLLL.lyw*ģ߿ QߩJR=+y -2Y(:׉֌(EDDDDDDD9""%ɰt_Xf-kܸ1?>}*տo߁WRqB.\-[k Ξ=5k֡[nhذ!\LW^x&L>1~Cw9͛+վVZrļ5j$#BBBp!,--Qd2T;vBǎ[Cdd$222`cc&Ml""UWN8;wx¼{,Ʉuqqqoi&<{gxx8;L87F6mиqcn*T(WIDDDD᱙W$'7NMRo7ּy `o@hhh:o߾šCp!,[ /0){'''@!I"ATAM|s>.jMZDDDDDDDTCDDRd24k͚)T  hhhVZv(K`ee0Wz'jР4hP*c}HԩS**Tݻw/ă㙙x".^C۶mѽ{w 8zzz uN$"!!!l522BLL4)oDDDgspv|}}˗/!55UTΝܹ#||ifjWW7Hk\DD*W,}xxxkL6!!54"""""""*'xØ9QwW^3fIn9rFBzp FDDDDc*?CeQTwS*4+m2{*m[j.he.?|A~x99} \7V4zbbb~tiٳgM e!zQDD/^SҥM}F~I)))9QVVs;w/,U+ɹs𐫫CYrZ7wmqs[D%T%w57xYfI&tmʖ-kuFQFѣZt֯_7fK;ytGs,(ڂCtaS;22R~~~}:uh߾3gTR%&%%U]vյZjVZ׆k!I:uvء6mژ𐓓鳡َ?f $8FuGH6S|gi3k׮s1 9hb (/^Tݺuor6[n]_ 67̖/ۭVZ~]f^UXw3vlnBttsX:^qt6pe06 Ɗl))`53CСvA[l͡ZTZk%לZP5h{E-[ѣuq5k̴/!!A+V(X:u9M`ロ\eff/>7ֱcێol5k:tP'm޼I۷o cǎiŊ6/ڝ;wƛ\ZpWMi64ig;:sq$kDBرcv=fvU|ysJ*e !WWW奉'u+)\->>>z'5ctIh԰aC TL[>n``ڷUVJRRR4nXqLFgΜ1UdW*++K69p`YZէƍnpE͝;G=fddhĈ6QM0^mڴ5KիuVSzjӦ:|$#̔}/]wrrSO=?Ӷ1cFE9ˎ;dbS`0v Pd3 7)yx,\P'ec^F71@WLuQ$)22R|F尘jժeNNNvP$pzKKhI_mQ۶m4u45i긣Gj֬ԩń#FhuL:EIIIӉnZp5}̶î=}x>F̱xxYŽ6mjo^3Lqƪv:j۶mGѠA/Tlo{nu͛/l' >>[CW^$EDT.Զm;a`/_^:yvepԪU+mN >,ݻԮ]{M۶mӆ U1#6ɚ5kf͚F U``+ @III>UV?0%I҈o[YfjӦ֯_/IpzzDݺuSFw_Ǐf_Uҥ5p ڊ}iΜsNoN;wQXX*U={h۶mZ|i]v?q_|qfΜ4IҧNԩS ooieo)~OOO}7aeffJݣm?f͚) ݣm۶jڵfRn]Ks`MŅRH`uEv=f@@]WԫWOstIaaazgJujС2dJ~wwѨM6i:qℶlbʕ+  0@sO={V/ٶ-Z뮻/KԷJHH$eddhZr-W^Uzzƌش-<<\9ի>[7?8?[B5O< y=>~:I;wTll/YDK,ַTR˯d0Z2Qa7hЋJJ%K(**JӧOӬon}͔\[re ƌ9˗ul}osܻjsgRjj$)..N i f$k5 ]w]wtt&&MѣGyfIRJJ}]͚5K۷WݺuUn]yxxŋΝ;vZI7nlة7o͛zV5TF ʕ+:֮]E. ^xA'@` `͛7ƍ5~8͙Y(K\]K7xSM4;$s#???H=X뫾}jÆ:u*Ҧ1ݩW^yU=P}tzᅁڵk~5k7LU&M,&rӦP 4ax=ggg >Bnnn6}Cƌ-[6 }ݧիwFjժU9ussK/ ֫&| Prd-PZ?hB@5(\]]tR >N@k^[αʖ-%K~5l>϶mۚmРA>^^^83Iҙ3giF?~\Qbb|}+J*R4i֭9IQ#H˗Ԇ r ;vL.\UΝM6k[,~ըѿkԨaSLfsԫWϦq~~~fU i? xM=3Zhvޭ_UJ~ U=Tj<WD7W:we4M}ͫխ[|ԩk\u?,ѣGz*ٳG|9Y>>>SVվ}y{{|_~9Y))W$;IP<<P94YdoƅR޸8NPҹiٳF;v4f͚z裏Z/}x I:99)((:w.]sNgc IDATŋT@@VZaÆyn>~k:t萢8jժV5jN:>G} ~AEK#2V{GZ!!˖-{צnnnzAppoi%+Wc=fk áh) K</͈2Vq 7K7a U`J{#!N?$k5H$>\9yN #Ő$''+22BJL`$,6l(Iڱc4'šK%ҥ+..Nz.<<\[l6u{H>>>(,[%IʕW= t~M $___uڭ@@# 27VSO;: PuS;$P4\swwt~bl4cՓ  wo֭[-MZD'O61?Ӯ]$I!E"Yk۶zM6-Z'O6%ծ]&NT$5i҄d-;#> p!G^oJutB[Yf@A` 4|0~.n ZİϟoEM$OOOӶPd-P*),' I&Mԕ+WLiӦG80"@Qd-huw5?;۔+oߧLm77| (&OZ$WW֬ٺz$W me-.F$H;.""VZ6 _ PTRšWV͡d Y hA@"tUĘ@q`_\qtd9:ECF}#%ٳJO *qƷ8(EEE)&&ZVppC)={ܹsJKKUʕUf-8:EFF(<<\V@@4h(gggΓ>%$$C*UTHH#yzze߿_ x񒼼ʫreRJvÚK.̙:sU|yy{{[իWWҥu~ZRTT!__5hP>>>v+33Sԙ3g$կ@UV<yy]S>ZĠz$N7iڴ<+I||-[g1&ܹsL[[MywdbS{rrr5fh-^XWQ`0{ըQ;t^ڲeV\+V("վSO='+www߮][](I5nx:tHO<~ԣG\ǥi„m]w+hSe3fۛm|^{mnkCi Zz/UZ[C QFQK,ٳ}6[((w{A<\ܹSTF~NO /ɓ'sss=ܣ;WC 5wǹs4nXR%$$do0ԤI hB=w<5lTM0^Slllq kذaܹM`pZDc JzXwe]|9a.]hv hu .doӦ߿$n:m޼Y'cSrSmuȑ\Iґ#G4|0M>M5k:ܹ(SJ||MHRFF[z~o^z嘨u_ں/=6Ǔ_FxVDGGkqZw}rQFjƌ9222azmذ^={>/R6q M陸'OO'hi:y2bX{ $Ggu4^ZWVuԾ}{MVUn޼y6-Zc4k.=}=\+%''}?g("Ç[wI9ʦsp J&@ai-5?ʔC*T$]fx;˜2v1S`P``u(<<\.]2Ћ/z~9UGPժU3gNԩSJII19q℺v7bŊv;ٳgͶ+WNM6va>4mT/_y4w\mjРj׮Gȑ#Ç}ԷSy#%%EO=Wk׮5@ը :|O?*>>N (ggXn~_:'''UREuUJJN>s0WIIIlZ 6TP)2*.\`o4u1-z{」 zl=ݻWmڵkZdƎpIR\\^{Uֽ͟fZ>>>1[sO+ OSxx&L/OOOG+I嗟%j^+Wh4e7:v&Ny jUlYHJ*en4lٟ2dz5~8 :###O%j+WNcǎSǎ%2k5kٓsGUS >\իװ?".\ ضmFiҤIVZmW\ѢE 5|0eddH^ll(ȑԧOyyy>}Z ԶmLF.~Z#%78JTT5ks׷BaaaZzZ3UZvΝ;' ͱrw.]Sڿ$i:~Y2#Zn]Z5O o:u}m6d)--ͬ Ǝgy6[2e裏F+ @3J6ϳlٟ?]vm*q72 A5kΝ;ԩHI׫2))) ԩUjU]]]]]UBqo;[A:tzk22ҭh/)++˴gG_յY2e駟QAz~JNNt{GiΨ(iѢ,VϫV,Mݺ=;vHbccjJuVO@1RT)mڴIst( | RRRT\|(< mVa_tE JK98&Mb?4`s;IדH6mڨ^z[Ӽyscpuuu-k׮[o DiSڣGl1Q˗_y0dbřڝ:uu_|IV҆ mg„Ǯ:uDUXQGO<.z)SnٲY۷o7͝;O>>6WJYsU9rСͿ999eKk֬VDIS__|\]]iӦ a_ۜ%I!C^1m۰aZ$I}QFի;:@իk)YKߟcV^T:ut!IҮ];v[qSN:رS[nZxq~gՂ ڃlӸ_9Yk׮]ڳgݿva~ǎԼysS"֊-&k͜YW_9Q222ij|WrLG~OO?UBB$i߾:|p Xhڴz|$~]|YLUڶmN>PۿCrrrBCCm; 'M2VZxڵY;!!4llJ:zݏv2k?lvC]ݻ;88D-ZilTS'Oȵukm۶SdǏŋ*_ik״aFS\r4k_kۮ^j6ŋfvjWF/_XIRZZNSS-ZPhh3uI!!:w||Y;x*_Vyc[~(-[F2W\֭[չsgnD(1,EQ(,\]]S|#߾8x%NȽJ=%%ǜ_Z7>7FQgΜ<7rKyK(ߖWgK8LNN֪Uj*}[4do~Z'k|!gsc ''Cm{sꚷh%$$|Gdd레@QUh+k T0P޻NNNti? z|D={>ںu6mڤ k޽=uҋ:e˖5k={6O㣢-[JOZRVf&ZP,("%%ΡQ9%kq7KkA<^s*k(=#GMy6%jIRDDx6;}M||̫Eݜ$%''^}yMֲ}}'!!!ωD<"##zZZF#FeVر/Tz ~ 529V?@C(1Hg4kl'moh?~ܦ~ 44kGDDo۷ԯq&fs<ǜ9'0}vۖ0o^~BB8&33SnWv)Iݻմilذ^֭ 4СC$]O|ڱcr￟oGh׮]_ir_ts=vzUz"ӧtI!6YfYX⡇ȑ#'IڿK`cN<>xls=]'FpM4a~6W-_E(HTւd5J*fյ֮]#6%(11Q}K.GEE)**J+V,S̷ի:|>c?͘1Ӭ"?~}՗m/_|1UV…?vvEC3rJ`o"X]/|-I5kִWo5TK)_ yYSN׻ᄃrYnݺij*gwvڣW^yU9-Sz|D|3sYf4s*Vkooo :L}w[sI?鮻Z{zklJ@ t́Wz-[p}g+VT\\ٶ[7ovPDpoV7El/Y՞dddh̾|լYБ#u1?~\׮]S TSZ]IO-[ҡCe˪J*jӦ*T{"5111ZzΝ;KڶmgS#ku W||ʕ+'???iV+W\ڶmN8 .(%%E*T*W@F٣+>>^W\Q '???5m*777;uǏWddbbbdUTIUTQ*UTn|ג8mܸIE)>>^UHH#5mTJ3k׮]ٶ8[|ϵkN]3gvt`wߟ)S8: 5F(^}U͞=[[lqt()>P84YZ(h֝\iiNJJb=@6Gc ^xy睚={RRRԢE ժUNM2  ( PlRM4Qtt.\@,7)Wj֬@>*Z|{8MիWW J 2V k%לD>QQQ1z)8ׯz۷״~ZxqǼ T ̜9s2OӦMs1<_J *kZkP]Sٲ$l(~<<+--bM322gjZ=vfyߔ;D\x_ffVWJe(ZC~bIܨQ5ſ}~;CƍutP mw5OBBƌѰa *./UBrhh Yq k5kҥKر8K=z0-®fZfυ` *kD:EDD;v똠6mF~TtA:tpt&k Tٳgk.ܹS'Nh4TR%FW4ܘU|y90A%SMB)`o$kA5`fSVLLվ~!Z6HH7=vuuu`$P0u]nE3(($9R6mrtEZRR.>qZZ~~1ر+V-[T5%ދ/jժZf""N*>>AFc|}}UVmuA:uG̟pڵ~*[Mc###pBmڴIђ$5lP}QppH tT%`0xCR\_lq_RR}!fZf+'k_ɓgmSk.-X*VV߾}-֭[#ڡrMЄ gaڴiLF]vg}rK@IUh CFJ'A{TP5kL UϞ=UL$$$o'}vjȐWu~I!33S7nfjUPrd-j kL8QUVUJJUbE?Ԟ6m=*IԻラmOA+Wk.:viy\5RZZZ֭[hBӵ ~;C*TЛoU`Ոó%ju][߫f͚)%%U;wԲejM8^zAœCF}\(ҍcX(yxXӬY3GP+WNO?KMZf70Kjn}Dծ]۬_XX{Jz֋/XIԩS'4*иmuV͜O>9 5rۚ:u/_VСŚSY "Po׮]Zp]N-ZcDmVӦM74q1fߎ7jr`Pb8VN #%7eݺڷo@DDD8:Xwҥoo7\]KQY bUY~PYKpqqǿ@f~Tj[>^PPdWlj4ҥK9^ oZOe-PrZ8Fƍ|l۝?yUBGZ>>>f:uw/_8&&F6'pFEͯɡv5p͛;:5jaVժsP$Rttc#$*ZwMei-dkN51jժ%oooG@׫W/;: jժY{ǎ$4nجefnl{_shFh (///G@հaCuaZ t&4ٳGK,q`Dus2W_}eӸ˗/kΜ#$D:'pB[YE PX(y>ѣբE G@R\9uAoY{зh;+$}ZfMf͚ (Xodrq$kGc  2e襗^R֭D.j www5nXuo uf͚iΝ8u>d\ǧiڸqMW<=`=;n}s|?JBe085jF#ٳCv$8@o 6-׳,#+WNZO?]QLŊzuZff}5iD?Au+Rϡ'kZP ϫbB J^o4R]yK Su m+fm=}+9G%I$}D%%%+55ŔT\9999YrwwysQ)zy?rSIR5$effJSjʗ//I=eeddHZhQ(6Y`p_d-HzNt=qLttݝ$\q]Z,)#$%&J/kl{$LQ>fVK  e4^c'NMȑ#zG-^;w }kv<ƌS(b{Z(QXICȑ#;t`ϐJ&77ei(`Efݺ(uXc7#Y !X۰ap &&kF CF=T^]o-q]w)@IZLd-'V&L0%ZL Y ۴iSڮc~Jխ[Ϯc(:$kLZtN8a1:d-h`(H@iZUbE~ dd-P&mҤɚ4iP8qL$kY[wm ZLd-5Fp3&kFXĩIPXol*'k  <0d-6J{{!Y eu֪[84Yh4ZF'NP؊ZCC ZPXm$$k2=oNM6VӦp3Ύh4ZF)`O$kkynhJrmҥK <ה)SkfMmCnZvmO0Ɏ(j<ɡQ-H`O8Y ؞F)`O$GرT񵹽{!F084Yh4ZF( A/*%%T{# mIpk@!*'k "v!H*){}7.Hʦ>>y ̆ ?jڴi:x@mH˗ה)[l/(55U#ڜu_z ~b5Y_~1kfZ>رcLqZaaaZcF~ ?jԨQ6?{~퐖/_>}h޼&9Y E`0>Pݺu 9/8J&#Qf)))35-\nʔ4r39[|FMdffjg#Q+M6{H*~{=6%jݐbT3qr$j,--Mﻚ:un4ڻwMٿOn.Za-s;{ɘ$9؉9;P,YD+V$5jH=(oJ:|ÇMz-[ӴiS%IժUQ-TFM;vT?v3[|xAu%x&M5kBz.]m۶JKK׾}{iFm߾]>|Fc߃+,,LGQttʗwSJԨQcuIwyͧIɓ'5r3 As|!խ[OJMMSdd?Ժu}PXk}RIR:uիWOh(??9zoj?}4W/_Ҹqo(++KUԷo_v::yl٢ ~4v`Q" { @D(X@vAڵstݕD4ϛ7'dhyrr4yz晑fڴiGhI={²/o5)SSڵ3#5x9;罥#4t%INNN?$F^;ڶm] pFV4}PZ׮]5lph-_\d45axR.]4w|uYѬY35y$SܹsH@qhMI &[k陣.556mk˗kÆJKK$SV.]t+WNƽ5kBu9Iە ///}NLSшXd4 Ù3gc~z-ZX?pu6mlJ@rvvVka۷_`񚋋}=}7$m޼Ytw諯B{zW:r䈤ٳgUfB uGNnC'kX^^^3fLm|}wfuA'N:ggg= S933SY/ IDATasefS_!!!yQ^{quuSӦMխ[w O< hO{Էzjc1u8UOȵ|8Gĉ\yꩧʇoMz(~Vɳ]ӦM;vT-׬Y3'>Gm۶5b3ruus|=䓅yU~zB m߾]o3όЗ_:~j!IڷoRmWުSNh:ٯyf'O/@Fd-c5lЦvժU֯MV5g]ϦV?B %I]t;4lp͛7i׮Vݜ~R=YWIbGժU/.pe4-ֳI Z8V ljW[׷yDNǵKbcsO*ʚ'k2u Iu6+=k{V~ފ?Ӭ\r|;SGMh55]ߵef}Mcbblĉ(meа@)D-o7+ٳ{w89DDD(>>TvrrR@@[K.2eYݑ#G,X l (Ee@aСYy9V/,%%E/*LLyϬ|w.cV?9<ǎwkCFz I7VvL'Nؔd|2<- N٭[NV4{~ǚHrf, 4ʕ+-ٻkv"PVq(Sfg͛7[mjJf;voFMHH[oȑ#k׮'|bXC_|񹒓'--M?ͬm۶}Yyƌqbt;Zt颩on]R5Ęrs+g᯿̙3rԻ{Uv< Us*::`ّ[ `/!8ٷY7_"#msT$ddH׿NJ|,]RūYYRZX)""?1{>~;d*?@?9$%%%iƍڶm?˗e4fWuS׮]գ}\M㥤h5rղ풮[lڵkuT.^ *Vڵ~UXѦyG"Y (\ӧN:EWsYV$/ʕ+z衇 !"ĎazuȑΞ=~;Aǿ3f5)SÇs\2/[Ln޽>3c۷OѶmoMDD:n*Us֠AOnCNÇK´qq_>,IڳgYVNYVܹC}Y݈h_5͞=K'O2u+c4ߍϣqZchhv_2X=~;$I֗_~gY Z(q"##,9999:bKc~|%SVV233u_s|xS[nZ >U^~_t~j֬k;oսzꩧUVjIV281nhҤ/^{k׮ⶔ_5^xQo9jQ^ƌylm[Rez54} f[n-Z(e4-4& 8: Iwc_qK*ǸcLqDq8$W~vܩ :: "Y )%%E_~YowuzoF%K[L.Z|^j*jJO>Ͽ hР'5}l7޴uC֭~Io+==]...6%fEI m߾fju6}饗ʛ7oo5+O4١{Mt1r֭[Ç3J /mٲɦ9Tc=fSۛ'Ru֦~@Q`CO"Y !(kV~ly֭g:#v<==MmRSS~SFj[:w))) W\U%$$(##ŋ.\Pf 5zܷo߾^:5@mZYڵ$ښ$(He8VxxYF_fMSVffN>͛j*׮].22R+V,7߬e4mwPbʏfm*V(///%$$=Vx=_sTgVNII/PemaMRx-S\\Yf͚=+>|׭[7_bƌ4}4؂d;G7O\rרQCG$edd())I d('kz=8)11Ѭ\z|iܹfuA~Uʕ+ۻ*T`~9-[T.Ϙ}ȥed啬F2YMR f+Xcc:y˗u{ռy8qOFٳ,Y8pww7=ŕ+9"aE(*CPuզ5Zr}jعs}>;{cm۪gϞҥKf/]zU>>>񑯯nvuL(r嗒ᇳkH}fu!!- 3g(99YkرSvuuSzLt߿_YYYmVVƏyd0d4%]?M0u]jҤ=*I:sVJPPPPckذa믿nsݻ5tPS9,,̦d(?^˗/xǎΝ;5c mVfRv7##Cg֌3,p믾Jƍl٢-Z9>J֬ Uzz$iq޴zGNٳ6mjq|=(M:T裏h䷭&x+WgqYfdGj޼yI^sj޽ΗUfzEhjhk[3~K/`0h-4|0#cJby`(H@iZc͞=[z9;/ f޽zue߿_ݻwעE4pBv?g}۷Os,Y}˗>(=5k1cjIYf?4`@iFUg}чJLL4RO|As+ڳgv!IJII+ ~T^EjڴRSSuTõqF_ۑ IDAT^YYFIXÇ7|cZwx7uA=sjذt:tH3f|`zpANcGϐ!C5iDIדڶm`U^]^^MjժG}q;_^g={(88XmڴE۷o',4{C6m /x~4(ZlEu]6G]wݥ};Tʕw^ڵK_|񅒒4hРByJڿl PHH7nnMߵ~z޽[~Z5kwMۧnݺ)%%TWZ5QFjܸoiݺuHMMՠATvm'X,QB 0`5jO111:sΜ9ݻwkfl5?^|Tyfm޼Y-3ROVR*k0g4dHnݚ9\\\,^C…K"4tBCW\rrrrRFFY]W+Ykp$)99Y_uv}KZs[3-[$Q-l 9;;t+ p$k2:uU=ڬל9s6;6kLO=w#F(22REnO%j9R&LY{WFҌ34a+33SO?~wu& z͒{9M4I+^"5nݺ_5~xedd(33S a>.gcm@@l٢M]Zj%IzիW \rz)jܸ}ƚ]}.]4}[MsxyyϿٳG8A&M-K[oM.\h#Qk0aMP|ymٲM3f|>Tiii⋕zw r̕=2 zG~2&kF$k(c*7lP|fΜi`Ы8M2Et)} 1kÆ ˑe [@1W~=krÆl2X=+֭[toLzԩ~u9q9;;륗F+$d֬ ͛uA]rEPժTjUuA= Z|<=3#;vTǏWbb<==UjUu~X-%]?e<{M+p;wNϟ3K8UV~w}\^^^y &*$dVZm۶z奺uk׮zj޼ycJnډ^}T,O"@q7k,M>TpB'h#w!!!ڹs$nݺ:v옃#؛hԧ~j*+WNcǎͳ_~ԢE ^h޼yrÆ M Xy3fVX(IҢE>5jiӦwرZ|>.\#Y+""B׮]34h`PRիW,&?*U#ш9T\YC Ր!C2^ӦMGslj=Y+?|}}ks j_ԩW^yUjg=Y+?ZTy'Ь(h @;vΜ9c*;ק_, d|>lv|LKjъ.@ 19;rrh ]vMY(?I҅  ܉'5kִoJTB%&&;9xY?ɓ'eJ溣GrJDSÇKIXƍӓO>i*Ϝ9S ,PN;*00Pv7&kY{@)Z@ы5+'YKjԨcǎ3zY]+*9r]Ǔ{LaaaZh.99Y6lІ $I V߾}ճgORG$@V,$ @~*VժU? ܒ8alUTk͚5[׮]ڵkON:2eఓr{r [yzz\"ooo_r!鵇LrKjHr}xzzjԩ4^v^۷oя?}HJNN믿իWkǎ𸥘p$%kFHBIXUXQ>>>%7))I111rrr\\\ eѨ .\rR\]] e&55UR*UT|yxm*)n|Hgff,___yyyml„U\Y+VpgW*Uz:ʕ+'B`U^=_?#??GoUbb&???9;^Xk+ZVI=(Ikddիgsgh$gښkz}ߦ.5 r׮]<`>;wܡ7|Sڹsl٢P9s_رc5k, @Qd- tRmܸQ{Qjj隋ڵk޽{+$$$Y/6 nݺ+r֭5bĈ\FYF|l٢Xӵrʩqѣ:uԩSu IR@@&O,e˖iMOMvrrҝwީ{LO?t FRJJΝ;gVGiݺu矷)vG駟|rmڴlC$U^]zաCw^͟?_~Lׯ @Ig};vH,Xwe?7o>3oJOOͣ>!C(q㏵af-V׮]տ 0 -:pf͚C)""Bk...QV`5nج%Kk.I2ݳ$ڵKC :ދ~I .4_~e5mTc-\wXW""XhR$*6(+VLb,D1j4bM[lh(XQDƊ􎀰  z+W̞sqwayγO6ѧOL>]Ix~:N:+W 44dBSSStÇG^$sa߾}B{…033Cvv69[mmma̙ӧ9^k󈏏7n2e *V(Ȁ?/"11QxNMM M6E޽1qDj \wEskjjFE=BODDDATj׮-j&*99Y{~.~-y?~y/G#EޅV SYѣGYZZ 9TR={DϞ=dlܸӦM[/F*UJ-&""""""""""""""T&K1"oߎ  &&F/VZիWo߾ ֭[2BCC uAxxx:ƍpssÃ>`cÆ pssüy qٳq^zXp!1|p7 d2ܸq7n֭[t% ))InKT&wJ~;v;зo_lڴIu~2220uTlٲEglDD6mڄ͛7c޼y9s&]={IPuVHHWFFF/UV,sKHHcp1X[lk7ϟW'##8s Μ9vS<}Tׯ%k=yD4ȑ#Qn]̘16lP8˗/qFl۶ ;v쀋֭ D aؿ?6m[*>ǔ)S/O럖W_~pqq={DЌ L8Q ۿ9s`߾}򂁁Aw9|Joݻwq]Z 7::cƌ?wLDFF ^^^>}:|}}Ѯ];}j SiGfDm___1BثWJޏ*T \gy!!!uI=͛ŞTrehhh`I+5Y+qܸqTrɓClٲ@guPPZn>ʲXYL)S~PVDDF)hF___xPqNԪ\2 гgOZrU޽{www|*T\=MB݃Pucݻ+LTX%ĉptttYff&\]]yf *d2ϟ/T@++?~;%j)zܿzUR&##XtD9Uvׯ;wzz:\\\&jX0rHD-E5==#G˗ 7((Ha @۶mM>S&-- NNNrZƸq7z%h\`` '3oڵիD-hhh`̙4iRd2 :'N{?W\Z±(~V:QɱA5!iի U&.=zHr\GGG*J!5,$$W\Է[nXvmGa.KEBDDDDDDDDDDDDDDTߍмIʚy扒W_x"""?ÇeeeaڴiyyG)<޽{%C+L///3޽mGÇHܸq+*V(;|0%Ǖ!C 11+VO?SN!44t;ј+sUܽ{֭m_fP&yZVVF 444? 7̙#Tr* 2d6wwwxzz3ވBhh(۷o]vVZB,L0ArRzxb?HNNFLL p=,]Tt^0lذB'N>ѣGɓxq5|עv۷pB\xЫW/>|?۷oq]/7o֭[ gϞ=3-[D˖-ѪU+|͛7~^zϟaÆSUuuu{n899iFoĈHJJgV8ӧOl2]BYF.y  ___*VH4j{쑻)u֢OL2E{VjjjXhf̘!gcct}'OƏ'N(̙3 +uU^իWGӦMw!$$FFF>066g}||PBu^zڵ+t"#\xJuppСCѩS'rʰ5[OSNO>իŖ-[0x`s[F֭aeeY>vX<|Xr͇5\\\7o*s)s\صk\\ڰz#** 0uT_Tܯ?>>> + ɓ'J+nQV"R'b۶mHIIZ #G 6l===L>]aFf͔nj3p̙Bܹsq1$$$N<#F`咎#,, 6l@jc{zzb߾}7nvZg/DfT^ 4(%*_Vބ"UZ`zUWWǖ-[>)Ǭ,l۶  ϔ&}}H=VzQ{ŊPWW/srW{oժUhӦPYpvvF*U:ADaÆpww;mmb݇,X RSSQR%lll`ccS}Y[[sY OQa…BǡH&M K^CQ7Fƍ1qDa[I{%e2Y+ZSY$3|լY={TnyŠ3{`J7ȑ#E [7yKEhqZ ]m =SuM&=?P;wjLI-9(cǎXv-fΜ) OOOxzzѻwo 2]t)TޓpvvOa4G!:NݻwK͛c۶m4id8q'NO>رc'''uuu?===Q˗hڴCCC SyԩSG|˭N:عs'233q;w.]ݻwq?ׯ# )K֮] m;;;޽[ QRa4nX.LrTXXd2z|V%p˛竰UrֆecV뇃[wrb||D-HzRjժ۷/<<}}Q$[nhی3IcP^=,\PR(OOOڵKh7jzzz ^->22GZZZ(-͚5b֬Y _pyj꣪kkk=z)))8pڵk'W I~]6̙O8EULMME~DDD$ZDDDDDDDDDDDDDDDDDT4U&)*K,--cݻw888[nFÆ aii4y&nݺ4!?(<*UHB ػw/:uP9ƍúu`oo APSSC||~ʕ+r>cϟ^"-- ;vq~?pc7УGnÕ+Wpex{{{OJ.\ŋ}`eeDEE˗ᅤʕ+;ydlܸo߾,_6mEVOU+ . PWƍ 󃧧' &O˫*Uرc*Խx =ZjF~B\\p-\tIHСoaҥر#lmmaee:u111x%> .muu0QY-ٳg/TV @,߇k׮",, iDf1nx(۷8x.^W^!..5kYf<[.r/^<ٳg7o 66ZZZ000DӦMѭ[wt1k ?-@TT=!!U:ѱc+##Ν'?HTZڵ+\\rZ,22gϞK ELL 45+@ 6#UHD8^~4ԪU u.h߾Qd-V֢K.8}4\]]9sIUݺu!"##+JҔ!||| ___a{p޽4by-22R&عs'ƌ 9IV[l-[ر#> M͂Tfee… pႤ6nGGGתU sܹsIII5mf¢Emdu֭[4h޽+l~:_^,˗Y|9 P,&""XT]E̝;Gh_===_-{gƆ / 0v8}f/_" .oWWWX  ǒ%>{y nܸM6ϛbɒߔ&\̙3')NJJֺF/=C>} ˖-ڵcX~\nO>͛7kNԮ]+%{ys( ܸqhڴn BMDDDDDDDDTj jժnܸӧKlݻcݺuVFMSSÇnnn;J'7UʮEp""""""~e\Vniii5Glmٲ0[DكiӦHwE p!(..\@Ϟ=&jQ\-iޤ$ <+V0Q+P|Wpw_&i~Xd1~K+ .:#88XDe1QnET5j1}tϟ?Gll,all nݺڵkM6 =zm300dFMMM|puu۷qe!&&Y9`nn^+W.WTIr<:::8sЖ/uuu 2C Avv6k$&& +5k֔CQ3FWreI㜝ѳgO\v ϟ(dgguAѾ}"' zBRR###B O>iEb iڒUXϟRn޼9.^={CTT޾} }}}K.pttc߾}F`` ||| #22)))011AڵannGGGX[[K>8r?~?}8::K4T?ƌVSSSèQ쌃"88HHH! aۋ*h֬Ю[chժ.^(?3Ijժ%gii)i\Ŋ?`„ v.]"&&III¹011wo"VF gK.Gxx8`jj ssssYf݉g)Qؾ}pApr&M@Oq! ϟ޽X@5}accSS3̙3,۳gΝ;Gd``Q@QN]$''#((N޽ĉ`jj6mEsm~z<{8###̘1Si,VV/cC[[ 5j(aǎHJJddd\~:::J¨Q~h{Ϟ`oo͛#)) ~~~8{,>P.&Novʕ+Dڵk=zEѩ۷o;v U,Q/k={LiuyaW\֭[cѪ@ٽb\%Ӌ/аaC憥KJ_Pvfle1&qVYʕ+ؼy3ڷoǫ:"""9СC۷Æ SADD 777]Vnq)>P-2^UUC )~*44sΕ۞Nnی3aeeUaM6b9rG-FŊE0a78qℰo߾E\\իנZjqX[VֵkW8RRRO>}̝3V*?;;6ǢE  gQ7>}z ^ 6իוƓד'OкuKcӦ?ѠA={ Wב Y#GT5kVc_v r* >BwpaΝ;& }k! Ə-VPΝ;0s d2v&t\xV^%bEU-Zڵk8Sлwo:::HII)ZDDDDDDDTnDbe-""՚7oݾ}{EBDDDDT:]u""""""3zXw Ns'N@\\:w;v%j9&&&¶K.w IDATMDQF}DI¶W^a}p1277ǩS&j9չ,/yT|o߾Ś5E۶mۮ0Q *⯿6cȐ-;+kE_=K0Q +2Qyd-"Da %^|چprr*5kV}jG>}Dձ`JFԄ(-wزeжŋ+(| ]vIW,Xmm|t B_iÇ#11Qhj ={w~555̙3WT̙x\wEԩssUe2YK JDD=/^ׯ_+퓒~ {mwssAcZDDDDDDD5t0Ԭ_`ƍ:tM>3Q' ^Edd6lttt *Uggg)))~ K~ccDx.^$R)_u,\-.bcc.066.p~MMML0QR,DDDDDDDDDev9$EDTxXp!,Y;;;ԩS^:={G޽{rԩT5:}Ν;Wœ>佣bVLQ]c.ׯ_$""""ׯ/_Zk ix\bb~WAF ^AhժU( @f͚rpt&yg\F}!pW޽0{O%*Kʯo"Bа\|>d2ܺu n*o>}sN7"##Cž 555j QqZ&""nBjj>2 UT'J1OSyU<0>9hҤIn>'"N닫oթSG8dwL~j2Qd-"(}78~xTVX,N&M©SoBhѢM+2d|}}>WjjjEJ#""""*.ʮE:}LpSZD)QD˖-1l0V#"MRFMM=Eψ㔉g̘.i2 +x=O51& LM]Alj*}ZZZGTTT""""r*nܸQ""*ڶm^z: "bd xQ͞=gF\\h@MM ժU:vcccUKDDDDTf0Y>޽_8UBDDT(~~~ ڵkp;x~wOLL,23e:_i[uаP+Wʕ+#%%E|yU*15F"ԄF?>¿pS8qؿ?wMM7+V"R- .H~?*TJMѽ{}^z1DDDDDj] :uZDDTnh۶C!"Ov%qŊxUVRT[ޥ۷JmB|||XؘrرcѴiߗ**}TϏ}z˗/ǃ7}"0Y>JӧO/8~/iADDDDDDi7aÅvJJ MR11_Vbb"}bСҪ(O<mܸB޾-5j$OWW IIIرc{_x GDDDDDDDDTVijL"""""""ҔߵOEfPbł; EDDDDDDD988`׮ݘ0[$&&RSSfjzGWWnnS1o\ap=;vTIVJ G vډ]vBMM Bs7mZTn^'"""""""ʍZTn" @aieTVc+qŘʲ|ble1&UVsU@DD/e"R"""""""*}NN=pE,X'O#w@YVEt ݻww &˖b8YZZ38~9ESttt0yL uuƍ6m%feggشOtHuEeRll,|||ʽV^| V"ju/Mrr2UF٩:"-ZbquX5nX8===ѸKgeU;vDpp0N8+W $$qdAZ`bb sss88tAnPJVSSC߾}ѻwo͛7x5"##)333^Xڴt,`gB4f͚100m;pxzťKdzhذ1t0K455`pqqΝ;q9#33UVU 4Æ C5_kWGQYQDDDDDDDRYէHZy>^w V2 +VDFFF)GFDDDy{\Y]>,--1vXԮ][{1th-Z(8+:d#&kVFШQ#̘1s(ƍKNIE 233U AK.CDDDDDDDDDDDDDDDDDŅDDDDDDDDDDDD1FllЮW^,]~ryz )))---XYY8"*)d2jժ011QqDDDTEDDDDDDDVn&"""""""""""O+Vׯ_COOOͪUrJU07n8\xPN*J=...ؿ#""d-*Wڵk}}}UADDDT.]FBB "$:}^c$""""""T9^ eZl : """bwm&k0V"""""""""kDDDDDDD&uU8* >RʮEp"""""""""""""""""*.*_o"""""""DDDDDDDDDDDDDeWaii)+U`T 7&k'!kDDDDDDDDDDDDDT6l-[TuŪW^Jv9&kQqSDDDDDDDDDDDDDDDDDT\de-uu'ZDDDDDDDDDDDDTbccիjժwD::::"YYY@ _111x-w\WH{I*+332|IFde5!k|  4%C~S=|^H| x@NN3== \wDBB$%%14F}Ԥ#??!UY~~>7(++aÆ5w^^>}III(**ҿ'BYZHB!B!5kB!B!B!TGdd$޽ www>}·oߘ祥a``!CU:Ohh(D渢"FYfSNB{x %%{{{@ϟٳg<@F0`^:t鯠7n?/^0z{ĉ9իsS>p*lxxx 22_~e,ttt0h 2=z4@%""Ǔ'O$9QQQɡe˖|ǏL `Ŋ>>pqq4l ɓS?-- ׯ_acckkk湯_b„ iWXX'Oɓ'y&N>]nP/_Ĺs={kaڵ8rIIIA@@`kk7bڵ|geea Ǐؼy3Ξ= &=xyy5k)X+44gӧO۷x-0o(**kM} ,Zd- **z.28c,0”)bݺhԨ88c.hР+Ve˖BO!B!;H!u[RR@  77 (KKK`޼yγm6ܹ縨(x}]^$O4hRRR*f`ƌ(Vu׮]:lmm|Ugy>>>رcSVRRTQQQHNNƛ7oQFUӬ@-#&& ŧO2331tPV" !22׮]>S/66ƍ\|||@tt!>l۶UyƸsu~_”) ;;@q/mm(**ݻw`<̝kVZAOO~sssYe IhhhI&Ftt"##Ya߿ },|:.%%f͚AS)bbd-((b˖B͟B!B"J E!u"ߏ &@FFyQQQ4mM6 ?ŋ(N+ IDATekk '''gffX[[c\9r$zqٳg|w"++ ^BfpytЁ|ѻwoL1yd|ƂǔquJ022ڶm+Vpq1Æ t>aΝXp!}֬Y34k ĠA}qMLYho޼nNNN<&SCʒ%K;}x9"<<oASSiM6r_CC+W ~pvv /""pq2mtR摙硨zzzpuhxzzѣLjV#Gw0tʕza3x{Mq77W|y߾}Cjj*7}t֍`%%%;l0{%s,;;Æ#..~!CYDEE1{%o:_e޽v`C06{ܹV{.k֬fv"B!𗙙_2?!BHbiYz-Ϟ=Xٳ2dR 6ٳ>@ƍc%]vh׮XN0a-˳vy@ZZZFU|ֵf99j)ӧO>GN<ڕ[`wyw$EEE@<~Ǝ $*,WZc{{{k}i4hРvRRR5q]zF}rB~ʝիa>233m۶!B!]EhBj+Sĉaf66o@۶*SN> [rWaQQQaú1yw#'O`cժӧomİc c+#...0HfΜŔ ~wZ$N8 QQQ׷9-!##{5wkLiiiWvﶫ0sSXj {ff&)AV ?ڵǢEL9&&7n\ !B!B!m۲Q~oLV⥞={V@Ub޼yNM8;JWWy|}T?1118^><ʕ+9Ü9sXǼ9555|DEEŔk5ŋ͛&M{2U~1O-[2#""I!2XI|9!B!Re] ԌK"""7ov .TTTǏ1f͚3;UFNN d#EEE8q8Sn޼9+ئ"Xukgӧ[n R>0P↪jѢ7oΔjp @KK [a7n<ڴi#E1iii 8)GEE'KHMMeWkŬ̱2sB!B!ReffN-6n܈UVaʕ͛!!!IHHכ7oX;t֍8TUUY4*{="r-8kܸ1m`(Ȗ}֮]w.]BvvvE~~>k*eeeο_ۗϏS;aQuܮ%?~DTTS644}FФIS{FuӧOBBbub ijj2[^bӦM8sL-B!BA":!5۷oxO߾}ǩQvZ$& >>)ӇLйsgV3?7kRpp0|”'6m"44@Mya?S=uuuǔxGv/**ׯ_аaCz3erHJJu6xs[B!B"}}}3)W-!B~m_ƞ={憬,ږMTVcp>1"w`j֬veCŋё zÒ%Kb 000@߾}ѠAN}nݺofvz!%%޽{o߾޽;z]INNf6m*T{MMMVWk}sss9i LU~,,,{EEE(**xɟe+ >Æ :ؠm۶4hu###(++yBH]RkZe#ˣER?'֭[x-ܹsXreY\ !B!_ ] f)VLL ͛7iB~`-afffSѣGF!B! AH(ԸpWU=J*)*&<233վ*իW쭕9t6n\_%AL\yӶjIZB!B!ΊejIJJԩS055pもiU [w!-- 6`Æ x- ___ddu&&&prrѣ;;;ٳ/^;w???>AN<$m+ů?Ru5(kf<ۖfϞٳgӧOu{IɰDXXOP*!Q  Ĕ׬Y!B! A!c]z^9UǾAQQ7+###S!UI՟s7V:w S޽+m`˗S1Q~To΋V=!B/_$B]]hѢLMGM6^DPP"'' hҤ aÆC]]s/JЫWo߿gkϟf͚c4h>_ s@\\ ZZZ02ɓ*ȥKN(,,IVGG=zd~ō7xȠiS- >#GBÆ 9߇2 &2p!..))PPPđ#GǷ\x{?'o߾AAA߿?LLLi|eOb!&&(((BMM }п;7DEE!&&qqq~O_~//3f̀_rʲ¾QQQ;'.ŗ/_EϟѪU+z5}oqư%;wolmm1j(ٳFO!Z *p-aРA [ۿq;DFF0mmF-߶۷_޽{6l@xxsxn߾cΜ9Xbe5%.],QЫWoaƌ bՍB@@^]ܹPRRfOLLlx{{ptܹұ-^l,;vz艀̜iqllv`; R~'DffcQP®]o?z$$$jjX[/[vf8ЈS#v؆_</_ڵhԨ-[s*M 0K] 9>|&$$wށس#*;!R0˗5~ʈ]vh׮1k,;w@K.Q!iTvرU^v--r#B![ EEEV96V+a(ɮ&[DD$z~9QyDF.pGQWZ8w@-C4l^_~]!BH]̙|BBB*sL2oVy98pGDrr21o`d4'P<= III7PX?oLe!+ywѼ>zzzzdv?q[cݬcSKK 6mb{Me&KOOǝ;w8p;Yaa!>̩WcQUUe0 >|u/+00C!ZYeZ cǎիYV_|˗/c„ <2Bj˗/MbɓTybArssqyܮ]{tҥFA!BH]BIG[?F^ 1g{N:u*Ø1fСZG\', o߾lܸLС```6m"=='NGff&se˖~(3ɓѣGOtg&Ȭ֋=޸q={PWWGϞжm[hjj"!!cg3g6_:6| z[@YY x5Μ9o߾̩(|իWTvQ5ާh>|8n({n :vy&SQ)Z=}cV-''vvve<ƍcc޼ypqq.A`BK~`-hsڶm\\\PPPذa8_&wr-߿)cVZX|S^Ț!BA":!5CNN:LЕ> 55Jzyy P^/_PXX)9o` )J鉔q# }EEEM*Y YnݺCBBɮ:dMjqjwJ kkWxZ!B~gɘ9ӂO$h߾f'.]rB&M{]ulĈ8|k'nݺa\+\z .` .]<}1g\̜9,^lABBo9:Ͱw>V =ےU>}+PKKK{ءz:t0} XZfvٳg`n>g,ϟ?cp6{%llvfY^)7֮۷9sy5oޜyy>BJJ >>|ul޼y-]{z1Îۙk^0h L8^?A6mYϛ:O{&B6m K\ IDAT@q𽟟_b͚5|Yf>[ٳ#FLV˗/Y>uA~~>233* QQQ;s[~&NիW3;>|۷oD`<]utҤIPTTئ.*zٳ`-;;m…cn޼/r ,+99kX())q'!!LeBkjm R"SV`nnΔCCCqԩZKrssd!B!T] 昙1sssqNu}}}q~~><==*m]ʩIII7)'$|ņ #ZbqǛ7̩)))Mů_bƚǯ.R>|M^^6oCM4 ;Z!B~g9vj׮=< *OVVs0|8Ňe-n3f N<-pرp91<nu$^ E>B|VWX?##+WKX-/^+-deS}cժ՚\U6_M֭Y  Y[nǏsX?6Ǝ- F…Z fQv5!BHy߾}éS'YǶmj źu+]g0h ӗ)? `BWXgt cGFΝ+l7io޼<.ի7knbŊcg8~Xr%HKKc۷@GY픔jjx {yUջwo:OXAAgř3gxb^^^08ޞY!qy<|Ĵi0n8dddQF¹s0`tχ-\???xyy… 5kڷoχ.Oppp@.]ЧOX[[c޽pqq3g`044d]kݵkW|^]hr~~>,Xccclٲnnnptt:w\bx2 6[[[k׮Źs`gg mFQQ֬YúV̙sKGرcׇ9v؁k׮7oÇ1k,cǎطo222]PPWWWAKK ؼy3Ξ=NNNX|9δׯ&L]L!+:qE e"OSSχ 66kydB!BH]1MMM,X{_;JOOLj8v8,u$睴4:O>9,} #::222HOOtf-ذasl9r vY⽠@ܼyϟCӦMq6rAMMɲ/fδ}}(Bw70u4322p]\rp5hhhȡB!C0ec۰>~시cƘqYt)>aXpVV* QQQVv+&|pBNzΝ;3HxPTTӡ<׮&_>N`IXtӧO1h`Vƍ3cbb--mڠ@BVVZ} ӓU= 4-Ʋe;BxxxĴҶ***4kӮ]{<O>Z\5FBիzbfff2k,144޽{QUGG...9r$2338~8?S_\\Ga5!!! T{92 .+W0qDܻw9DEEqLY֭[pv.M ѷcbؽ{7k6m3 q##-[3$$$jRZtf?~Ʉeccٳg Զy.\W7 !B!uBj+}&VVVs 5k]BGGAAA)ijjbԨ8xsbM*,xMx{{MhѢ9bbb/_(? ssBDG8Qzݻ{A[[zՅ$$|eWxMLL 6l>__0ct7o>~ gj~^^ֆ^KhтGXX^zŚ6DDD`g'g:t׮]CD~ }Gdz5s.`1{+99˖-Ŷm[ ===hh4oߐ7oʠO!=z*7N@M_ijZyPJ>}B^^(((`Fˏ5jTiPVVfuLVjjJ%8Ш7d1CӹsWNgdd͛7LkWq_"''UUU˘~nEEE?~B!.Ӄ/.] oooűd]VH%w̛7~~~ikkԩS֭[ ֒œ9sniժ.]I&MbSgnݰb pQ~}8;;Y;xc``ݻwG5:ŋb >|X5piiilٲ/!WZc˖-y&69r$ƌ3g/^`%DEE- BHZ *Iܔl2lڴ ݻwc֭<2B~EE%Ι!B!?Bj$._qիҬaӧBg ӧciiix1KBxx&Mf0`ΝUdt =z w366mڠ۷:u} eavGMXX8v͛s_+++YutMBꎖ-[۸wH4j***޽;X'\<VtHNN444`jj3g̘K?U{1wnq4a>ccLgۊΝ;LCΚ5 LŻy{{#$$񈏏GBB MMMkYVSSñcƇ>SRR٣GՋo滉6bbbX`O///xzz"** 2ڶm ccct҅zݝ)o>ʕ+1m4½oj۷***UGLL {ł ???|6w !""Rwׯ3A`\@ΝHxyyLjGbb"$%% 4n]tA^Xz5V^T˗3>x4lhڴ)tuuaff)))ZO!UD;k*Yt){… aB!B t'''7;ػ9 ##8LLLs.()) wA6mk..P%իf'tt!&&J>Æ prrbn aÆ}={жm[ܹY;#@GYX&8p.q&011a3ىAcUbF%%e̜9K,R۟:nv+ŋ^E=`bRva4!Bꖴ4VYKK!g&M 0?}1]مqUm'Lp0A=sKSSSyxxÃHIaҤ̚1?26Æ GΝ+b֬d!((!"";2}Wy^奦2edd8VV&M ANN$%+N)""x'!B؆ !Cp[DDDЧONןͩߪ~ep^s}cҤIB"͚5Cf+Ӯ]*mذ!F#Za^Zl-[8U.7o˗s[˽޽{Wk\ZZZEۖň#0bĈjE!D2X^shذ!֭[kkk@ff&m{{Zُׯ/HNN TTTбcGYjSQQBBBS,!** eeiZ\$''ݻTTTйs tU!&&ƍ5ЪU+VB!BHt->űr*,Zd //O!)) PWWǀj T< _ABW4hېLҤI$$$U<غu;6oފ@zIIHNN,ա7Fͅ)hM4Aކ՚Ƕm۱ey4nѢEZ7С8tpھ{*T}###<~!!x=>|8e-`hhjk9}eRb۶ضmͧ|DDD0| 7Arr2pUfG޽{wa߾ڵ+ً6mn\]o(fe5cƘqhaaW^67݅w"77FBB&&&X~ݧOoddd ++u :g2#T.::7orssXIHH+WnB!NԄttHA>3)L-V=/0LYxI @lYY{fQ9@V?/y|߫8*W#PvkY_%%5'~-ys_ClBsׯW.Weʗf*׵B!B!G`ʢ`_6oތӧrssiӦvw,X[/µwQQ]],A) XJ`/!QEDFk7DX`F`!"?^; %g>lۥԩ8}Fs)II&G\\`˖͹.ڽBܢElۯ] .@JJ6v>|6o /011v7oyJ{ĉ%p!;w{_+lp7DD(O]bccE_N1z($&&*|_&KšCq)XܾU[DDDDD!ԧ)lsUu?oHCDE[=J8/_N>nXث\kzJ)iY<|~?m*>?A\9c 4Xs|ݻwGt"Ο?g妧cʔpumʕ+g;ffr9ݻ/̙3r.)) cƌVysOeϹBO:::bI""""""""*|Y+H O2 0u;v޽{̨`DEESY R=ԨQmڴEݺ ~%,33֭رc{Ǐq-sػwWX-[T633'NY3ES*UBVиq, WCvBAN<1cF sVZptt̲(OR4aff%7###)3:tHB-kkktM6Cٲe_5j$h"U""""/O"""""""*BS$JQblZ}s&mll՚OJ";wu|UT^C Νp?޽{xuر?c8u lllCCCs5'}}λwD= """""""W$BDED" X.cH2331r~\D677dž ի0\]q/^ n'Lm߾]}޻TҥKx ۮ]^^ۄX*b„xnľ}p1<ũSg`gg' U+**FDff&p| .]ٳKxymTEEEae ǻq#<>ux?մH3iii5gggܾ;w[q<}^^QlYddd(-#""""QMcԯIܹsjAҥK* '^ 9#Lz*8995kkkQ9r۷=|5k֊ݿ?c/^S?%_۷o폲C"YŇ]UT_4,6o___!vuuիѳg,::: ~066ϝ;;ˉJի C!ޢݽv=\r3DEe7=zڇqqqxt .ifJ:tĉDe{L&Sify6mYvFq1/oND:hK;w 8886nD96?W[wQܼy|S]Zv>˺ IDATJiFx-bѢٴ.x+VDOil4}ޝi޼(^f 222T纹THDDD=*U?:u-LcZr\{\(~-Z Dzz:VC nfۯJ6 cعs¶'O׻wDŽITUVNtZt9jԨeʔeˡ+\[Z~066Ʋe>|\ѥKW!{T޽{L2Xfm_zcڴiQ1DDDDDDD^ʕC~BYfs |b$=zGbmmmxzW.EɓۭZR/9rtttxÆ yf̥4y׮];c~ZTL1]]>9}U취ѣvދZjey?33sƞ=kfff@ccc!CbÆB~ܹw>99UVY> 4@߾`nnx\s,mڴq{_]r,YXt%ɿP%pN 6lcVTRGEE* (:tJ0`'/_ViH~_er؀&;uCB"銏Xvom1H,--v:xx~/CM1bHԭ[kׁ9BC_g{_رϟ? k5lc+w #00]v LLL+W.c͚բ+U 98Ep#Cv%hX^sſ+lѣGk011Abb"qm\|'ODdM-?SNbѢh׮=ܾ lllaii ---DEE!0n݊SN }npz=L2gBnѬY3Tl[[[TPlڴW^֮]M6׬w>8q@&Kȑ#7774nܸcMĆ ;"""""uٹsS ""Ҙ"YŇ4-ZU檿&M7)kVK~ƨYf׬Y G{111055ݽP}b̘ |^ڣ\rJō5e }cD"JWh!r_qw׺rիDJDDDDD%֗їvvvNTЫwpppPַkӦ-6lcǎL & .hiiiӦ͛7ӢPlL+++ٳy"z_Q:J9eZтyqVxmaa "%""""" !rUVhժ """bG3YWTTe``lN_kPvm,]r]`ii(<=B*b䟄Ϩj*U Ӧѣ %/l߾7o/gGlmcǎ^͚5׬YsL6W~:up?72,5D;bj\W__{q_ <~a޼_L"ї'k)D!d טNJУGOl޼ QPgu }0&..^)iUNJ5Ǘ&..Nxme$Er۞l-DDDDDDD 'Nŋ~8y$. ,, H$033CŊG.]gZZZ3f,={ܹs BLL4r9ʔ)kkkh]vN8I(Q $%ܯsiùwhҤ)vډGիWHLLT*EJСCG 8UTm۶077⼞ؤ!Cb8rp5}F֭;vJܸq3K.Ν;x ޾}8V1<Ϟ=h\|.]Ax歐amm5kbȐ*ݤIL2Us#_Ǿ}p9<~qqq̄lmuٳW_OriԨJ^+*?hV^R=+g1/׷o?X vޝXUx\wﲕ$,YFǝtuܶ'""""eADDDDDDDyh޼ds1/o1cb̘ޟ֔=o49a<~:uFNJ*aԟ1u*U*Oc}m|m|)T}}[&B"цN])[,&.BGG7OϝsY}HMMRMиq<ɓ`)ޟ&!J4*7nDEX;ZSQPT)!55Pzu\W`9::FxyݻE}Ƣ.Դ2&U"""*W}{DDDDDDDYy!HUkVa)}AYT~RFDDDDDDDD#r,lz!dh9sZSRRp!PܕMEwԔ) W^宸Ǔ Y'"""""""ƊxN봴4/zmmm!x#!!As:U wŁŹXKBqyȲv:디*}YADDDDDTb)=DDDDDDDDDDDDDDDDD.;#ZT4k }LGGlz䏹9Zj s| SؽcT{8^fDѣG1~yfbb"5Iԯ^&>k*9y>Mϧ9{>QaXFvJ$;H[;w ۟{)yׯP}knڴk9U\]U@P=^ ?8`/@822RCN׮]1wlddd6mڄqCOO/~'OӧO #E""""F}M6|P%t_P"|GD%O"ʆ) k2220nX+VTy/_"== UC!VLL `ݐdǾ}>^:ttjz ff&/rPLxy@]KO={-[ {{{WGʕ(ܻwAAAdyo߾BDEE 7s5… kN߿vvvGjB\\xqqqyʿ̘1;wccEvnnb=RUTży1yOµ,h AcʔɚJ苓Z=GQ[a5uHAT2]tYX4~P044p6DDDDDDDDDDDDDDD%Ɗ 455Jto\aW4DE}$lߡ4nCDVSP+;;;M@DDDDDDD$Q7߸i: """4XV>}0}t.JQPeˢm۶NpEk@cZUVE.]pM@DDDDDDDb-B/^7044-Js8/of͚L2jC&ݻx?f[ZT~bbbajjMA___QiX >3g`ݺud*TD<{ r\R= fCDDDDDD_MT233T_v9s"33[nM,lll0bH : $y̙#G [w,_ ǏDQ_ pp+  == Э[w̞=G{ o _YT&M`IhԨJc.X0k׮C*ógOlIHH9u \\\IFHJJt /_x ={vE_{p}˖_Q`۶Xw!^h1ڵk1:k@6mЦMMAEȑ#J@&Kt5j_527Ǐ11+R){M'@DDDDDDDT4ur3QIv =zмy =@@M&?~ϟʕDLׯ_ऊÇ!==J…p <W^GHSeOږPw>{7waq#=?;w/^F4a޼v^ի^MƜ1c&m?=U TPiQ Ѻuk&ᏥS""""""""""ah(ETprիWX" (.N*T@q11oڛ7o"((Hhߧ;4+i͛7d"?>Ƕ6mƌ3g)lw?>,\kРz5kܼ$-z x >/eѣݻ\az۷okHMMUP@j`gW ?Ǐk׮dT9hРAJܽ{o߾AٲDڵQtiMFDDDD*`QoߎC?\"X ]o \.. 24Ƕ .]j.rTBTT W#ԨQ#Ǿ8zkkkѩr DGGx@j~ 43fLܻwnB`T-"""… «)SbʔYURUx=s,\v-ק*2zhۦOޢB=z>KwaذBaÆcڴ000mٲ%FիWaHOOGFFFJpgDDDDTL||ʕ3Ü9qEddd300@=1u077WwDDDDFM'@DWTT޾}o"66VeKZ,"*Xk׮\.ϱݮ];"ڵtt4Tn݅יXzJ֭[!nӦ-՞_^XXX #""5{_*UJmۂm۶ t钯9x}'OQ_=zTZ M< / +V^p`5x ƢEj} H0nx=F ۷_w@DDDDEMddBCCfnn7K$$$`۶hѢ._G)XMoQvMԮ]C~tX ֋ϱll<}+V.6xpXk4h0x}8st}\]1bD?ƛ7o7nժUSODDDDTԪUKx^z`Ѣhܸƍ3f^ aZNiG6՜5k^prrDDDD:uDǒ%B" J1xSةXEDDDDDDD%O"*8ظq3zꁐDGG38{+WơCs,*lػw 'OףڵFFFJtuu|rXe]4hZ&"""ݽ/V^P@PPtիuoмys899|yBŋµUVX|ɓdff1_0&St-88X?::*7BDDDD/RomCaU"DPv 5Ν̙3o^$''gi#gρYR};v /ģGĉڅ7o7???~):LU=zO?Mv555ENO""""lٲXf-<= 66V`_WСڷoW\\ **RCϞT';""""*#GҺ#azщ\DDDDTtX(޽{#,`ffGGG+W.c'$$w+Z::7../+W&&&066-~=x/ cccUC5L&Cxx8^CXXr9Q W4taaax*WA5`llm?\`zo_}kkkBTTaffڵk̬w.$""""*,ʊ-[˗ss 022-```x+Wʕ14xUHz:t 00PP@>J5DDDDDոq\r?<ǏGFFF6Ž{wKмy ,]UH>iii8?O?MV||ZIOOWxD9* @x]T)xxW:vÇTjDDDD,"ʣP,YGβ#6Zj3gNr=ٳgrJܸq]!ڴiSJ*+bϞ=Xj>}]RдiSj{Y7ѱ1;.*4q*GN!˱al޼ Ϟ=2Nʕ1aDxxO=zgϞ3gp5z3Ǝ̙Ә:uY7Abb"-[ o]Յ{_L<Ұy&_xY4h Q>_ƛ7YHBݺ0j(ًDDDDTb߾DݺutjaccMQdl۶U8H#yQcnn͛"&&?;Klŋ~hڴ)lقm۩='. q&M+T_t)u2e/򕏋K|'"""ו*UBҥU[zHԀZDypy1 111 Ȁ.^7C*aÆSٶpa̘1cƌqGgrl޾:Zn-,}RSS^ #C\ "jh :/)˗0G]u\~enm>¶m[ 9ILLGrr BBB av/ѣDްh 4W\V:ߍ7бc{xym۷*lذRXLȑ#y&l߾*ADDDDT);YZDDÇ  $""""*ѻw>.]s|+R1~x\|6KmW7DxN:|y3HDDDD%Krr2*WsnDDDDDڱ4D$y& $ РAd2 64S>HLLDݲjiii| E便aYXpAccY ԩ7oѷo?tuabbdaff333hkk ץRp]D;Q, EZQr,O>|˖-vL}٧`nngggo...03"0~ػwO.>>}DZ666hР DmѣGw"%%]tjUP_uxd2 _V))S&ciB----TT -[DMP\9Qׯ}vJ #eZDD?+V4!7n!"""@KK uȑo^@O;6ߡChB!65-ݻf,'{NZ#/%H8QlmwQZDORQBRjZt1\(IMMŜ9s=۱ 3g΂@ _|)S&pmepttDpQXϞU)݋{fyo1~gg'xиqc0{ҥ",, ZZZ5jF+ `I8|p?V`РvHzԩ<==ѰaCHzY|D~QFݻ _D,^+W6aMBѣ`„uFӧO0~v;EDww{Ѹq fDDDDDőC]lܸ͚}2k0f3@===ܹ+K*4i":+ Ԓ'<XGGS_@WU}eʔ9rXBBB/s_̙j|}ЬYsډq(S-T7gl }|';Cژ; gHDDDDUZDϟ?6ޯ]...yU |Dkiia¿,ӑ.ٝĤI?١nzB|]桥m gOEȱ]E&Nc;{)l{ q-гgUЯ_tO""kkk^cƌt*DDDDTt֭CttJ,Y \.y= d2ē'OkxbܸyI~9sC~ [7^ ]&grȈAG 7Z}R; KQ' Bܷo?O֭;Νׯ_!vׇp֭[лwب4GaquuE2eTjkee%(L>\YW885jRߦMA[[;v|ԫWʕ˱_DDvW qVrSغu oBMr5QQÓ  qJ4 sALL TlkkkCdy!;wF˖-akkʕm`aaLDFF" 6o???-ڴi;x\,&&&prrVev:t###5_ׯP\va={ÇÇqM={ׯ_\.Ǐ?Nї~³p 8zzC/ >>G)􈈈(XEKUVQ)R)d2 ))Iah}PV-sF͚5b-Lpttkݺ5oԛ7oФI#xzD׮ UieTXQx.]:~ݻ"$$HHHȲHƪ[ժv*jORʕ+յJP0444WE^:tt TTІwqQǿ([YVeX[ee֣)" ?yrAg}:} zsWz.]K\]]uNZhE5/_l2>w WV%ImܯykpႤ8#1\sƦڒ4uTϟg( WhEʕ|GhwwwX+33#ǘ;n+wZ9]O$]tI3g~3?7njm*P:x=R&c[ɋ?vƮ؆ڶ}Hm>d9ׯSvծ]jkFQU ~fр4}tؤI믿V TvmEEEiǎ:|쬑#Gꭷ޴c0b-J,i2>}BCC->?***ΝWΏ+==M[lƍkݺr{9 8@JuS̚5SO6>Vϳ .6p>>&P@@ sQzIs1j͚ZfI>p :MX w:___Wy1Ǣ\]7Қ5?ᣚFaSrr-km.\˗qHHKIIя?` ̽k2^{ EەM&(BA_;vСC4?~*VN&MI&N7b-Դڼy$)66V~z'~b2qc2vss1l ܹSoSJLL$>|ĢNg2QBsϽڸqw&}a^sfm޼YQ}W) 8;R>Ϛ?h:9_|1K6iխ[lqN?W>dܠAʖ-k<򵎭U7o̜۷kʔɶN :d'?7km߾ݚi`+cǎȑ#r\ pX (:vknն߲^rEoxu3R>-[/ŋfHKK'|/\`\roذ833S/Wbbbif͚/^޽{-zJMM5I}qzz:v적(66yYYYڶm~-ծ]Ks.t GG/j޼?wwJn˖?Q||gfF(L>MKKO ll\nڴj>}Z'`uWk]ڱcGs=vҮ]RJ (!!A֭[D͞=;vԧNԾ}Hvܩc_T !VCtR޽{t}u6Zܴgn9sFTxq;^C .\me1Ԛ5%]9u|2UTIR%TJJJÇ{n%%%9sp#l-00Pwu4h`s=ީ4l]_}5խ[ώŋinHQ&'LTժUl!::s 1;TV흅d9;KNNNAPH[.]0MԩS:uv}F|2 \/ٳVp1裏տ? 0qwwה)SS]+W觟觟dϔ_Nf`4q'ȐtցrݹWW[ s(ւ=Z3gTٲe y?*٢Ӱ S*Nh'((H^7ZK5NZx&O"ggu 4ЧNVΏ9$U6moذl٪'۩^z*S iӻe6us/IrqqC=?P GGGpmٲU]>#<ϩP ~&NTYM(hգP Cg- f}Ys)WWW 0P?Ν; .WeʔQf(((ȢJ,nݺ[nÇ:{6F11guy\r*WU [rvvvVս{wϹ{w._\3r(9@͚5[o=RÇRJL2j޼[[Nr-3fZ7]]Vb%M.^ҥK+00Pes?~;gK!!TVUڳgb%PFuԑ-Ͽo?:qℒUtEGՉ't\_ZUVUPPUIIIQDDURRKͪX*33SuiOTU?p"#O)..NŊ_)U^]Kdk\b-Vٳ̙3g6G͛7WVO%ϹSSS5w\-YX{1[٠AuI:tPٲe…>޽{=W~XbW֯_iӦ?7)==-ŋh׌_gf{AjgS׮]o2'ΝS-q߾Կνkʕ+5r^OwW[nU~}q=5d+@Pzk;fj.\glBBVX+VPR`9;cZ-^Ԣ˃x"1qq==ۅhD _?Sq)))ZbV\^{]ÆjϟWn?79p 5khY{hWY|^Z%aEzוk܁4{ۅ*_|qqqFW -ZC_1ދn^?^QQQѣy>}z'Ԕ)S-*T~X^zQfcwK3gZ򐙙a򚓓sƵuzAƵl֬Y.֚7oI(TPPQZҲon ri&sZСC \\\2QLL"eQA53fȑo+33丳*VJ*+>>N'O@#'/8H˖W驰0;wNǏ7Y{˖-6l>|fFEE':&TR%/H:tH/_$eff+99I7&/].]жm۲_n]j׮:q$i_4p^W|lUr999j߾gj^-^?~נAM~<<<<ŬҥK޽QժUS5t߿OW\1!ΘYs/^H TFFq///խ[O߳ok~Up۵RJXE =d͙3[׷{.խ[Ϣ<rƸqy(̘0!ErE.[;vn=ez; Xϐ!M }ϯTv…ie/?wFTreM4h`t=&99Y˗/ܹs-oӧ%I=Yo5B*T0OMM՘1,]D}켗/_V=M 5nx=`[k˗5{z~uӧIzvfx&ZOO8_%oMb B-GGGSo|}}MbF$͟?b Su3?/@F Q{֒+` VzzQ#IozNzT^==F9$''k&O>E&|$7RϞԳg/EEEYBgXcǩdɒ_UZ'~b<={ݳź_{ԶF̛o<(g_>|X3fL7%JЗ_~{/[; .()b+/cWWW-]LMޝc|jр|rIҶm۴hB=t^{]zol :45b[j׮ߟ_~ɤ_B+VR׮O+>>N}v͚5S봔-իWW&Me -[ѣǨDy`|㱷:vߗ Zm۷OƸM,>lժS-Qr,:qHoҽ?0{EN\2ejZ׫U+\W_cܹsL: 8(Bk5qD.]: ٳg+99Z׸?Pɒ%cӧO׺'NʱP^իcwGy;wN˖nΝ믿qɒ%5s\.;جY3ⰂbkE>/^K9֭[uAcܥS*VX.gPXNm]2_jyΝc2~v4jԻyyyyM6ĉ&EC[xȑgQ>/4X>>>~ŋcwww=syZL}`QQFF̙m4`@+eHmѢ7ndm[7hK& 1nѢ&kRhNN)$1>y6mӢܬV׊_)c{h]f㓒L:5iDժU3ĉhذ6l&M6_)b-:u;wꮻ?Ҿ}nj=o)TT1wcŋ)9m޼+?_9]_:gs)YOaifq&Mu~XXZ򵎭Uj{io|zzw5 [^j֬i2޽{*[_+ze^ϟ?lE u{e\~N(̲JT|\;#>:k5ydiiq*]7nƍ衇R !yc2Pv2sGqqFƕl1i&ݟNRzu/gff*99Y%K4ƚ?eY$)((H:0>m2~;#7]ߝ,7U^LL811QƸ\r4>..LZVZk_$IK.cUDl 7G;X53Q9u& j֬K饗^҉MѪUj*WΝ׀Z,M!!!J]ŊJJ2=--N\xѤX+))ה+WN;v츥ٳ4_JJΝѮhvZII&;``|T׊>}Z)))Zdzm}vEDD.]@GfڰaKYZ6m2:m]/""Bcƌ7|O?Tw,[]~$fy[[ZZM^^^4ZQ5..t_)999|y8::S\\9;|s[~>d{|T׊6mW卢g+ֺ$y(zrD (w:k]?W?Wڸq6nܨ?]0=v쨞z6lب BL$:uZ|ӧON\b9o|o.\uY}|ƛ~7~8ٳgM 3oϖ Z訞={i̘ђ{h׮]W^rrVXn7mzBCC(B///=p;7^7nRDA;5bRRR+Cg2>yk[cy].Y|ɓ%L.l-ۤ9`7XTgKu֭\]ڵ`<ŋj{e~tw,:k"###M]v{viر6^5xzjݺΝ;'Iڼy\"glРɹ.\oUݺefIRBBN< *Xm5kO8aiii8S.\$9sgV^}ݻճ՗)?qNNN~~TJեKLssqxx|w+ ZQڵk˯v[tF%JhF? ]СCVjjʔ T˖-MIJJҾ}(..^ϟʖ-ZM6P9u֢X fqǟCx<<<ܚ)*˗W5wIRzz;U1aaaWll$i_ի%b-IZl yjW&777]tҥK;#-*Y;]||}}ym߾XK5iD...FΟ?/OOOU-_kWr)߿?s~ilْu,:3gwGY}`ԩ͘1CƸs9uxڴ9dqk]ϯ7onQnRP׊MVXX17o,gӣG/ #::Zԙ3g$I?^=IkbRDHI+:_㼘[y9R۶Ӗ-[~/R5k P̟?!9:tPYYYƍGVZjm2NHtւ-j[+S{G˗(>33Sf-o4ct{ڵk5u 8Ȣ4uP!D;? tڠAy9"wwwe{Ogx"c`~86jHzM:]9iӟ;w.^o^٭=zl٬'OJo߮ƍꭷFGrtt ._~֒%K?iɒe9k5lP?,Y,Iնo԰aCL֛5k$յ-:G0/ƌeժU6SϟhVzkF~Ok֒%k̘PsݣUV髯TjaZ|+>gwdY|h9+0#=cƿ7'MTNԔ)Ӳu۳o=LWEGGBB*꥗[%KA^+tyJRRR$d={«PT=# 7QQ5neʔرըQ#Jn-ݻjլYS|0AM41MMMՄ j)Ʀ[o=B 6T:u 4o{X P;qpԺun:yzzbJ UllN8u֙T|HgΜ$lv/˯չs'JZ6lP;F Ur啚8ٳGDfqھ}NيziZm n $777}y~^X:uJK,65kM6)}ծ[nՎ;ԠA `okPQJJ1vqqYȋS:sppKѽ ѴxtSZ<rssӆ y&%''RQ OwUb,ZcAj׮>*-]:t訧z*_V㏫fjMrqqѼy5y$MӧN͛u!;wN P2*_ԩ#GG\;wSG7UࠇzX=y&EFF*!!A/_V``SV+VO7ڵku %%%TR*[6Hmڴǟ?z啡0`6mڤʗ/͛ ۤISڵgرwٳGJLLޖ-ʕ+[T;k5닛ʧ )SF |_(???e˖4qZµxR%$$hƍ<8+VL~~~Q6lrjԨa\MLXZaNbb=f5G+W\pڵS%D* Pr%Iھ}(l(BҬY3-^ V\K.}Q:kY (8NNN 6[XbjѢZha5lG?&s{zzSN6777jJZ::uNN.]ZO>&sGܶfkw}:˪RB{pӒ>cܨQ|ofk:uʪFkPȑ#l2ٳ("(p*U"{26! ͜1.[ڷoonjPӸigΜQff1޶mիc-++qNǒ *uX NJ4l08qBgϞf:y:uߟx5`;dGUV Y_ ~gobnjP;[d2p.\p󥤤jJZ(BBBb4}ǽԤIUNPc۶mz[=zcF(윜-|9d!b-@g @~͟?Ok׮UFFN:<o6&WX)oo??Īkn(y9,LPpZΟ?k꜕*UR:u:'g޽Z9>cspF89uR\]yޕ+WttUs3VvDȣ,Sg .E `~~~twz:>GV!_|I w*M(i2>s挼<ԩ|Us P``%I˗bun'k+VҜ9sս{7]xQB>}z_UVShhʔ)+&&F[GEZ" .Zn֭[֭[[u^m۪m۶N=z;6;B 2e+WHӵwݻ'[A/jZ`rի>lUllsc;::ɉ[vEZؑƶÇ9{MuQ˗Pxxm15jз~Q-U Ԕ)SUz<]]]զMM8IG\r%}Q(kYVŊUre\R}v-[L=- q&մ:pl٬3g(++KAAA]֭ge6n*'ggg=tW=tW={V7oVttΝe˪R*QMPTQ(sniZ4n8|#FP5),,id;; Bb-@gZTfMuM ,$EDDhݻUw&;:|`+feelnP8Q(uֲV$.I5jvbŊYm pg Rɒ%drNWdI{n1Y+33jk^0ƧNҌ36?8::I&NƍSn Z"\5;kI҈#TD c]񒤏?XTR@pj3/FerС#$5nX?iذõtRرCN 8հaCyxx;PQ(uֲEaÆIΟ?cjĉV_ 93gw*|rppw vs233mU\9c>UHHEpR||N>W%R]/^s1?~B+##CUJ;5d-XiP(gb-Iܹ4h;vH-[۷aÆ6] D͞fΜ8q>>>j޼Zj'xR...s/8H7nȶysb+Q6l3do嗟vZx4h}_Ps-$Y6m$̙3]v^:f[d*Wl$M:E˖-ɓ'r U^ջw <EZ6]Aƍ<`>|֮]ku0[f{A.\36!!A+VЊ+T)EFFf;:|yzzf;jBuʕ\۶m+02dIСԷo_1~׮]Z;-_,s>SNZ+V0?s9rTsYw;tҥK3g&OlmݺU'O+  $ILW+I1vppP׮]dر4h;i͚ꚯu( Q*Ut{@NP(uRVV[6jH:u%I۷o7v;Ezzvez^z=zٍ8nGqwAV?Jf^$ G昩QLGܩ{M3v%SԲkcRG4'i]5p嗡 >gw=sγ?}q|~3f̨z ~6>)[n{qyjVڶm[,Zt^׺9~[SN#82'?)""{lu7Ƨ?o5%Ko~byrL>=N=x;ޑw Z4j_?.;3"".5jT P/֬YwZv}""e^Z{0aB\u1GD͛cŊqꩧ$ߏ~tCtttt,|jQ.{'::^뮻.>OK}<P{Ç9sĜ9s@&kED$II֘7o^zݺu#drozk׫֯JNI%GnlIDAT]'M^O<8 6<O>GY WIWYXti~ o|nk:^~ cwx']O~oϸo6sn J%@ƚU.wqǂ kb̘11}1zu{DD\2f<6=3qSu+W~(֮]7_ر#^y}8 olR,>@{s|Oҽ 800Z4zqEرcGַZDDtttbeq!ٳc?4irnX|y<~ݾ}{ EZ_ҸQTc{{ۺukMr}@ë6Y+K,Kfz_z0uqmŋcÆmӦMqwƝw_|Q>67.\--,Sggg,Zt^r86S\۴iӀ۹s@ë6Y+I={ $N\r%q% Fu 'Ƹ_ƃ>5ikok׮.jtMq 'Xn5'P[Z4<رc/{zسg>חJnJ{Ok:jÆ c^{~ mZ4jĉݻc\7|hiiZo߾x饗^ƍӾ[o=Y͜y\ 6k}ソ-[@}QU$IIѣGw[\S*b]뎎>w޽{W<3y=""=Ю/|---1wY]뎎/@}nhxZ&kd|,}>_$Iw߽]cv+Wm~ؽ{WO7?U߹sg,Yصo7ڵk .-7~^Xѯ=@m(kǙZhkk3s9+gϞkl+Wz邏NJ~֬]_|qs̟??WK/T5q---]o|q7YN;5~_EDS̞=z_:6n돈L49.g9?Uu5k+3K/J%PLID[[[ň#o?"&O>)ƌ7o mmmݦcM49s-Z7߱}_]wbFO? Xx3>w~|ǬYcĉlձnڮق UV3Ό;.}шx}g Z[[w 6ݮ'OzguQ1lذضm[_>W͛{e-^ZZرcGsn׾[lyxW#ϴO=Tm_O?iMI2^\Kqk2ʬ]C~WhW.#S"-ri&.dfo-)cڀ=S״}լl<jH3wG`ӯ5[{6\"I&v99 ;Ǻ.my侳[ -ȧҭP6垥m5r)9~83@V]rȵ9dڴ1`0IVۿ[y'uGd]OV/ wq'~>wZyLn6c0:* Cᯊ4|Z=iW7 cAjIu}2L:ڍzxDX''^ĺ :\:ޚ|ϲ+A<+=y7RJ-jfܡ]QS؃J[[ӓ_D7I3~]z9YtJUZ;'U G"V?N8-Y?U$WxN^É,'I!X RW#R YN+I#L1ǭG4D~^rGiglnݷjO7>s_6v\|!jz͖yi<%,w1FK==.Y.V1+̡]h` 9?Zv} }᷸H>},~a8¢t,r֓.[sj i[[s2$]kԬgƹ<_u jB4.-dHS>^EZ\ Pt, kv3A=;R!5K+t\$[!tq6u[.]H/u u^_k3' =eè/t̬I'A$|Ai|l㾞Kcr"bIg+ 9U}-e %{7.?ڳ]P:iI5—Vn?dyVG,Gpi<DVR IJ@w <-7z5գKsm 띏A#>ωůx C}YHiGÆ xt-A k|׫ܣH1*kǾ:+dԭMns`ӇtQrg O}xWvPr"BH8{Zޓ:ebwq:4b4kpPefd5R8ue;y~i$2vq,lt6OaۭX@|ݰ)G, P;#Z,z yN~c~+}ľ!tdYJwPь j|~9RM+xmo'bѓ@ǜO!u]߉-@u& D޸PEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQY~# cQr鸅8Kx_\XKCYcDNiz{tج],4y=]Y*3xdIcg^? ះ!E0[<$vzگq̓Y?0yp>Etk`R\U{iη}/,0L_RI}kɤA5͕4:54D˷AՊGeՙR:='I46uꡁ#+>rաu<BN]U#sْp 7'.6ek[Rz#ݨOׇu`xقd'H^M%z l%Ѭ1'o4]}=V.<.ʕvi<2HѤU I^wq(,DE"5n_X#6` דIc/}k+ ~htk9 h#o_On5G˫2Fu]{ZO 4i,lCGRW^.-Xm9 $:~}o:}6ṜF.;$oO1l6kC>Ex3ZJ6j):0eaA"#((((((((((((((((((((((((((((((((((((((((((((((((((((([Z4%TGw}r貑-J I};Miz鶂K9,LI*A'ŭY3 )2|8̹t4Ve~Qn hHHQ@6wk)Ò|8#'Ơ8a&dsXs@/͏%=e,m vEKVnS%}uc M۱ʰA͏%֐#HUWmv,p=麕DŽt+vC*V.#˝vul_Ώ6?\^ Ibӭ]# m֥++- {׊[NEva~_&Y&b#n3uミ_ZmKXHЊmujZlmecҥHY,H~6cqk^&iya W;88"::+*hwQGY[`W$3AeF;=V{bD|rp(bGŗPHRK2!x<)ך`̞dh2݂r9hJ$mom9MV_PE`[ӵ2B|;;ןQ@'͏-/u{==?q/*Od:>lUޯbh b6l_ιmF|?++hm6gp*&@o(k͏Fmbh b6D=O\˪&^GDkHm$q8/ eH%A:3 *`>5K>Mqk =e g<~AHfwny}:<qWZ/nm%o2ՂNuƀ8S?E~m^yfSњ%>~uxv^W!Un&;(bL(H9Kxٴw6,H z(ciժ ~?ՏK ۡ(C;{O `Ҽ:KGkw'p?JGL~ 6yoO+\<;܌qP4ZPilXY>i]<*2+׎+4ig&8%zxF< Dw:*ȗ.OB5emr|`ѿ ;+8N8Tn6'zR^=(Y4{YAY#^O] E pU;[֚v拇;Awd)Y+:_dO;.V*k,JHT*Iff<?yJs^-J_;V'Y>i]<*2+׎+ִ;o6 s5"߃x/U0>uU.\]kV\cn[\ 7z¤qN> ޔgs}:{.2ddnywi!VhX#kZXo?|߲Ok,JHT*Iff<?c]Bx4n\M=5=w.Ev-$Q'W ]r|?/>U/uwbж? k ((((((((((((((((((((((((((((((((((((((((((((((((((((+>a[_yM -HC)pT1]چ ƞ"63SYvH4\ĹrqLҬd]>oE1k8mL4 .m/D7jmG6Ο.(O>maQɴy?]Z.!+𪺰`K[OSbf6ۙBgs@֯uKfv>H!90=sFRӓq-Fͅ^ΨFSpN? hfk.6 Ҁ0"Fx^he>)VAyYkVGp]V PiY !sh_NxM>{{!$gi>ڧQ('vP|g%>eHREݵ< ^'6\u%"a.ce+wjmG6nmai[Gmm }P4O؆#*4H!|[\2u;c+_nU*?ҏC=?v(yvX{\8c)M C%̡\V)w+̠<ǩO\)&`)&:g'cx$(UeIObgYǘP.up5?R]KAQ}͡lɓh1'v?(ݪυ"*Bo  o |/<%^(4|'?fCWs\`z _ne ҏA=?:v}M86 uKNMķa6q\j~_ͧldܸ۸ */JM>{{!$gi>8NL[]*{u}Ÿh(r&𞑬#-ƛlhd19h%ٛ+wS|m(n?@<hAwgֶlI "BD cɦY^x_mݡ~ho0| ]S|-(n?@NsxWI|m2s.?_/<[Yj4 <iA[q\dzcu9T+X`2G ?@8ª*V4e6X$lUi1Gn&m1حPx>^OŞo\CHcp@Ȋ ܞҵ }"Y%6O7 EhmS&ӌg^Ey6/åǪjWWB;i-e2>`U2:I4e_YζUɒAʤjå|1ựkzh|X$rqʞⴥFirR&o.fIOkoZKwUd$#k(I㞙ƬMp7`K6D!0A5ie%.ňŢ]Ep˷ l@'kw;3_ =ΗE-?ZvQȐAYX3w9$qGCW?0k';'<{d;KKG^;|_@Q\^--s x#$4w+ uc]><@eմF*Y-o*}ϼlApȨqx_3I m\? s ]./Y]z%ҧ+(\4a@㌻GoAk MDKApBs^X/?ߕQM\Zڢ@o%AH]TULD 8$d3_<{YZCkl8!AhP0R/\G.? qh?x?sǹxn ΗG[#8e=<諧ep?k?J 3r.:I{X?\Ix?sǹ|q)|B ϒ5[|P{oh\2 2eg'?%U⯄oEq =7yg#/>gx.jUI4IW.Dv}a b$Z,kHHin!t!Ir>BGLԿ/?ֿ4eA='K} vaM.wgUg7el(I 01l|Oav6b ?-T8&,RP;f, m\?q_+3G^;|v6zz[kcT`~/%!/}6\omȸA mC۝p$ɕr\eA_rvڇF(dyh|:iE, ]yXrQU  YG)/?/>gx3-UHђ?,s\I`K&m̹8o mmcy1q @r̅T[p57.? qh?x?sǹY = ;"_|Yn]ȟ `q ]<G>/$(Y#k`#$g4[~Wfw9ܥ?U./tf]8ZE4qʲImƿ1/8z'5Wf'_ ^*^Èzn1w9t{h/4 `&y#Uݸ# ה_ "{#?5=l*(~O?EELR<&qV<9.C xI!~Io#=n Vw'uC3|A;,Ey/v_\zJGgsm 嬶1$L9#qu#^+|$$wPņxhx#Gֻ\>JOQ I֦OYГ_Dk(G|KžH_[~»o|"}7U_]z ooŹFG÷pF.w%'(?EeK!QwKݗ"{#;R"Gt$_.cßd0d=߆MaWyBw[?qn{0 I?p)?YRq]ᇒeE礤vw6ZmsKc7WR0AOx~I|u ޜXi>~=k?E|Ajd 5DFdyXĻ )$%`+G'uXuGj[d|;p>oR"\>JOVTExas9)R"\>JOWG^;|gqEp|A;eAqEp|A;eAqEp|A;eAqEp|A;eAqEp|A;eAqEp|A;eAqEp|A;eAqEp|A;eAqEp|A;eAqEp|A;eAqEp|A;eAqEp|A;eAqEp|A;eAqEp|A;eAqEp|A;eAqEp|A;eAqEp|A;eAqEp|A;eAqEp|A;eAqEp|A;eAqEp|A;eAqEp|A;eAqEp|A;eAqEp|A;eAqEp|A;eAqEp|A;eAqEp|A;eAqEp|A;eAqEp|A;eAqEp|A;eAqEp|A;eAqEp|A;eAqEp|A;eAqEp|A;eAqEp|A;eAqEp|A;eAqEp|A;eAqEp|A;eAqEp|A;eAqEq_<>e̫ IYb)kzjU~iQEŠ(((((((((((((((((((((((((((((((,wvgnk/?Be(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((%̡c5]pPg⻫2WŠ((((((((((((((((((((((((((((((((,wvgnk/?Be(((((((((((((_ cY㵈αbO 1> *f;y3%8fS( cPne!rxQEV|C?^}S2|L8sxS'e&> ZrL$ʁ~cVcuP[(kZŦϩ_>! f`2(0bg$X0x$oɣ[ytM@v2O}muoż,2x䍃+w(*DSkYg7N6dhE`Kt@5Yk zfOdZ._#~mvk[֮#lt&He8 AA tQY:wJ}V18Oc09b25[[]. U!B3ᘃ$`,@u?W~qx'(˹Nvtm*8'/Ed\w@ŔO"vc"~*Z.4/.QeinrL8=e8C$嶚G4VKMU]\[l2G]*gNpG4E"yp(sau$Hk -@l[;DAr;fU6f;y3%8fS( cPne!rxQQ2;)a3gPK` =J+: QHј!'Vw\ Ԗ^;kB iw$LI5h?(ogU͵@:( +x5 n+im4TU#Bf Uݴy-]@#k6d>+FXY'1`tP:6`_Erg[Q|lOsg̋ҊZ~NQrukUpN _.w)E!RstEI$,UM=siji7 \C٭6XIR`d'xOnd]Ŷ3)huۿ|©yys[QP^qg$F@qcFrH8M_,4NlZ9\(mENY/>{5MDmryb#s)'EG<$/3X*tn dEr1tb{ͻbLVl>֕62n`G quRX7,~}..( +Ay`B$q(fu mY-eE?-{Ԓ2@Ã$A;@7J_>{cK[K,j)yasc WMe{oZ%լ|*AXU A(z(]CYkgeOwAHdPIbYHT]x٧MJ @e~mI' 8PX0Garzm;S-kWr:KE$mp2p@ (bS+ qZI" 22H<2h7m84whdڥ Td lf s7(_ cY㵈αbO 1> *f;y3%8fS( cPne!rxPg_(eq]hߙ+aEW AEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP\?o滊ܳ5݁2wQEpQEQEQEQEQEQEQEQEQEQE`–^(y'm SHܨ$\vgª- Kq_)4n6f%}㞊({=gޥ6eʒ9r$du|7\:dY8 #q#wu“9(ii2,Ow)D22d 3LzOy*'@cAn,@[ebrtTP M4/kn8qd(dߛeq{R-FYm#8:A$zan<o[q^gd@ݘ J/ Kzo)cuOxȒ R9lVUwn6ǗbyUo@`>jZu͎ekt{9#9"Tvxr3սErW~կ"-, Z)yt4m?1᝘CZ6[[~Ҷsso|~+'ĺU޹]vZ}'Ye3~ԩA$:֢9KDum2K fɻ;ā[eT%c.x}>׾ރHvgzgǫh+r,Q՚W BPEq݂I·Pֿx[E  kQ|e^Z~V۷^!,uVs\vɿEg)hi6:v29^bS!{Vfy;9DKkɭ[,s\~$!Bc(CZ6[[~Ҷsso|~('ĺU޹]vZ}'Ye3~ԩA$:ռ'7 ~M"5enD-;JP "IRMo#;/wKG^7h:u﷠Q@5+CISɴ W+ea#践f-mm|pȥ[U~uqci?lwq&R[p#lEMKEW0\HbNA+2Gː$'u-# [#9 ]6jP5Ϋ9ԢG>)qKEW4ާ4Z咵s3Oy*˰e˰P5aLWOtnbKk9#.Z)}9f&m$4)-7}qү@`IG4].ġWs)bv@ޢ9k ^7v:Nmdgoܡ ?M q}mۏ/:9;dߢ -k3:"I:8#*ʧq#"@6%ߝvN.GM$@ܣ?"۪rZchnb۷~ d82jQEE]ZIoMe,wwiB37V:g-߰n-ݸ Qc?곞:M((yo6djO t3l;n z75O7?i|7{PEP.^WjI'ͶeI)!, eaZ[i~~osEQE`–^(y'm SHܨ$\vgª- Kq_)4n6f%}㞊%̡c5]pPg⻫2WŠ((((((((((((((((((((((((((((((((,wvgnk/?Be((((u]9oůE"[fU.rHVV#`z=sIVd`6!>33[:J冃x^eV{bȡxĨYY:üGmu/eA(ż`d iM}Q\|ŘlmvgeNMWNSL/]Z%u琙+rc\[]2YN:\Co- o2[yXœDI/o/!dPX8ql F|*fݎv@:-7UMJmF ,.]1؎vJȤg@}vE6tGb^Eq@]rǁgQ:qj:!FO66H1-]ZhzQ7KjӒ'd'yM# F9 2+/5_c\,Hrd+u<# J~A ӿ=yZm~ Q})K}9ʮ>mþ@}'uNROGgќ*P]86kx_+G-ok%a+TϨ+kx_+G-ok%a+'5V"(Ϩ+kx_+G-ok%a+'5V"(Ϩ+kx_+G-ok%a+'5V"(Ϩ+kx_+G-ok%a+'5V"(Ϩ+kx_+G-ok%a+'5V"(Ϩ+kx_+G-ok%a+'5V"(Ϩ+kx_+G-ok%a+'5V"(Ϩ+kx_+G-ok%a+'5V"(Ϩ+kx_+G-ok%a+'5V"(Ϩ+kx_+G-ok%a+'5V"(Ϩ+kx_+G-ok%a+'5V"(Ϩ+kx_+G-ok%a+'5V"(Ϩ+kx_+G-ok%a+'o/K;_̷Sha2#ڈ~&x.DQ'٢;UFLz\ %\ܾ6컝o_ [ƿX?kx_+\mQWe [ƿX?kx_+\mg?@v_kA5G~}G_ ׉|#cS{3rA AFQOE}#oY5B q8_!:^=vI>$NzʈsGV.NH#GTWlq#>URI%&GgY?uug)ko4O}ſ?o/#W7'1 GgY?uug)ko4O}ſ?o/#Q_ ,gxrW1w+Ֆk8n$X 10Zm՟?x|-Iӭo&*3/%EN=Mm}ſ?o/#Q_ ,;QYVbGgYo[@M5ko4O}b} iOY]/.Hmđg=:VCn~aZN 4OK꒩w s9 m}ſ?o/#Q_ ,;QYVbGgYo[@M5ko4O}b} YjLoM0qiu`qb%۬߫?1Xv^'Ue41Ӭá% ̹C}$#DG'1 GgY?uug)ko4O}ſ?o/#Q_ ,gOe{ 1|Wy7w7Mx@1[h7/?3Geյ;5,XGi`?6?V.NH#GTWlq#>h7/?۬߫?1GCn~7~-&y' j>>`?6?Vg,}_NqYfE@38' j}׉D:Fj2kT[;d_ ,n`?6?Q۬߫?1M_ _F~-&yOcAa: Yj-+Օ \I`<}ӥhſ?o/#V/ND #Htѿ$*kp<p_ ,n`?6?Q۬߫?1M_ _F~-&yOcAa: '˝Rn#kYuaPF#^ſ?o/#UMMF_-Mb-Zu+]rY99牕iIY% CW3YW+ez+{QEPQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEW5,w`LQ\%Q@Q@2VKyuwZ-UYC[58A$$rBOIߴ[]Y%Eq,_VR\*ۈ|YWfx:C֚"F vm6wRQ].$ʐoG\672wI//^{k FF[h$MF x}Λ1|ه]k:3,2[[#FH2o9>uf?\j^!ekw-I>˽տw"xZ ׶E13*އFhx+uiM E\B1EipĂAe^=.>\7/npʡªvm: ^eڥqo/dZ 9 e&Ɲ4S:24,7Gr`ɂ6 D}(|Ig.=\ofxtbM_x9.7Ku)9^3 Ey}"1Bc^HFزT =VcK9u{E1"Vv!8P7>w`[yݸ[$is[\}-Xp*H\bJ>I{.e& C~F1Fr)QG'r:8nmۙn/fծ,-qXugQy!w:}=Ǒ\>$ݎ2Os@EWxGۚzu`Os\_;r&szZhH2dEH&*O*5I|7F_{ITQ)=ij?jM3A=zſe\.?\՗G?#͔#w+C ( ( ( ( ( ( ( ( ( ( ( ( Uj }쬯FӜzg5oi-Y]le[c0z{$弬gpx pE ؐWU sz)]+0*0Ga"Gb̎0r0s%p(=94±d:( ( ( ( ( ( ( ( ( (xQ/xxQ/xן3sn;QE]=B5uqmᅜ:+u!y/3Ej3Pg ?׿ 3gξ2_Fu G#:uT?}.{g_|?-TiɃJ9={jtk%M:ɣݢca4@($ު|>֭t?v >ouc23UE_O5/kV6\O Ip7;F 䎵:助(ӓtk_϶;5G%ok#TaC?nL4/ MmowjKGyƨH .ieWы {}\zu1Gu$FXa m_϶;5Y> =B;k!?nX<ҵ0?!˟7G MmowjKGyƨH .ieWыoiFMV#9[_VO?Q Q\?0?!˟7G :zUN@v["KrV@ <88MmowjmGa+PXZCfr%aHoL4j+?/[7}?0?!˟7G&GD?Ys5?tbxOoS}_bi׈AdlF)#;_VMLj$~| dێ*1< Q\=Op]%ok#T_϶;5Z~mY%ݣ FrUV#ڭT4ӳ~)6o۴{$}x, dKGyƨ5o ? 0/[7}ޢ8xOF[ӮL=(&6Dr@k7}]Z?/[7}ޢ0/[_ xOHW$7hn0rDFpyA8 +P ~>?V ~>?T(5O3tusk-g̺<,z} u5i#`w@Pg_(eq]hߙ+aEW AEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP\?o滊ܳ5݁2wQEpQEQEq{P˩nbcIo vM͇ZI-JR\ʵ[u{[i —T;/qg-1YIӟz5^1Gth 2:2 >B27\=q%-ZǍ6H Luchzu+Pho/oOq ᔲ8$>[`rF./z@7iͿ6<ǒ&Hūj ]/J$vkourV乍~XHXא/''q hvu{9 DrG UE2'sr72OAUO_Ֆuk [mdqh`H «ixj8ƫImDڎWGv@n$we0;xN%Y--1:hPO %5iW:eHDYh?"T\ 0~kHҴۋm}Hm w*xg C@-igzὟSmcpZI6h8/4B #Hk/kXx-7ׅD>p|A%XAk36ztRskȎcjv[CH2d)]ƕxڎe|Oss4q̲Ppx`3@Mnn2iPYr7-_'F4? 7'Ae{Nѳ|wy QE'v6V*nk%c;{qzOlu<*Xi'$ߺ;0؈̝SB:'~G?Vov] f^0 R %99S'4۸7;kw*JJ2A1w#s|_y__ӿ?¼Ķwmo XDrEҌUG@^yAGRS2oH#J>kƎ?TOf>u6w8+'M4{Gk Ǚ:Ew_&Btw}GuN o |$u:KI]e(rF0t> ;?e. Eٷg^ ;?f?ehje/Rǐi2>Nz~? tWŽj?7G( ctX. E{( cu?<1jvMgg5k$,XX. 'E{( ctŽj?7Gł4WŽj?7G( ctX. E{g>WƓ^jk57,r3bPd֗( ctX. E{( ctŽj?7Gł4Wh_?iOyո ,`mH Ͼzt/Q[X]<Q[տnchl=ךogmp%qiu`~Lc.8 /Q[X]<Q[տnchl=qgspephP>Lcx?/Q[X]<Q[տnchl>~Pm_^5!Ed|skϼ_/Kf֕܊8u'ta.&|۾0rvG)Eh}/?( _Ww _Eh}/?( _QEh}/?( _QEh}/?( _QEh}/?( _QEh}/?( _QEh}/?( _QEh}/?( _QEh}/?( _QJ/_g`͐sֆf9^R U1y1^ 0]^HH偔0>R`AcsV3åA_VKy{#Lfxjǂt{x'ie/0.ߒ\9ٷJO%QV (((((((x[ږwus%ݱq b2x~GW?_^9sEyq<=Ifx-ZGΚׯ_Ri3ub-_i4IIUlb 2p{{V!_D|_MxǏOu5j{ "\!S-bheO"!FBpOJg/ab??{ 2= Я/&B<%BY/KEQ 3h?{GW?_X.\\xsHitwI,bfv1$$_<_A,|}7OkhȈE<֏,GGG/OGW?_^1 3h?fx-G#c# ' kƑ;"W{[UG+g/Oa CmqLGvw¼g/|*ukz֩qΖqU†8€:>'+&ռB7vkJCk/%̡c5]g_֏Q\%Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@p]Yۚ+rv^ˡQEPQEQEÍQ&ok ƭep2#,m㷑BPu]y j-yyqkvg%._ϔ Q-yG! ɀ}F_o.ëZ1H|f(>[9ĜdZ[)5u{kk9HN%jJKWquϔK*>kkK#iSQkmoy"M:Ko1+SI%7y,G-ho[,NLc:%kmaҲKvM?hs/0뷾7M{{FeKkvTh#fV-'άÂ^4{3xg{[Dn'$QQyJH ~Kaݐ֚޵,~{6G$&2G]Zޒד,K 5턦&Qo d%7 ʷ[KL$w߿A4X~MLb7a>\vp q&[GoAe` yigģ;ƟuiYD'V yY'Y>`#FO]Ll.}cx[h:aKi,,%Vv8i>Vp,,.ûDu,c-5@D H8- " ^Jӡ4-2ݝ;xB E 8GkCiKeD Y>f nt 6^kWo/H6hQ@ylj~]G8?_ ѷ5.yf![Iğ+j7j-v-niYV"oA'Kݤ#X= iCԔn$_qJif4k~ Q/*3g]_nj.GRG.bO@H_ _F~-&yKGyƨMmowj' j׉iDp:H꒻*mۀN;dgV%ok#UK}\h7MmowjKGyƨꬺFf:ut:B;Howd`c6h7:zUN@v["KrV@ <88Mmowj' j>h7MmowjKGyƨLF&uDJۉov`'vh7:{xJ[NBv&Kb0VHNHgMmowj' j>h7MmowjKGyƨ[96d4q.+o\pOэ#<1GAT\ag_BOkko-^{'áUHF&pA!𠢊+B(((((((G\7/npʡªvm: SmV+^N[i גجi7f1hvu{9 DrG UE2'sr72OAU>M2;i5Yڳ+][ D#EhbWg!@V,0~FMЅuj]jwIdRv2@#` Lаh~iStJt Ec^ErF . !- .JV]"^,Ak8YmWq[(8?_ ѷ5'v63?Y4v1L?#GW?_@V!_D|_MxK}4kHk>&A?JoWG!mEU[u%`~?|mXkm#Ԝ(rIkȿ%Z?QEGHQEQEQEQEQEQEQEQEv\_noߠW3zEyuznOeuTD@dn$>gQ}4SZQ0Y80\y*fi>Ŝk1!EX݋3Ɩtir%I!h@as\9# RiҥŤr+IIefqD,CHHnSښa>W'{}OW䵒yYRԇ\p;I񦕮O,s#XP@NJzM]^IM;A%Wʑ2U w&֎Y9"|)F l߸Hp77R蚅h!3,Lbup6+'oGx4V'2 K˶# vVlv Eh[lf .~_, r<+dV %\iZ$Q#D¾2>oaZhsIxđibsNȭ鍭ϼkU}wۗ'XW2BOMsyVUvR>|"}R=@ӵIg7Fw3U+I/8`OZ*GX%eV8$uSޮ$)((((((sFS&эT*#_ƪTDzPQEAEPEPEPEPEPEPEP^GB"Vz?rsוTgW:_(eq\?į?f븯G50+(((((((((((((((((((((((((((((((k?s]p]Yۚ?Йt;(J ( (8t^iw/ e nOM2FrIiiifDwM20R0u%a!p0#yMy;ZdFYo"edA5ZKs\)[CL(/z@7iͿ6<ǒ&Hūj ]/J$vkourV乍~XHXא/''q4pZyn[#HmpS|eB'goI{|y K$zź4Qƫ`Z7V6FsQ5_Hj&Vv؇j=3bq',M[k4ƚj[2cp<>gQ:qj:!FO66H1-oeh [KglRq,gMm;,iYogXyAexv( 3ft=B)Amv@U6O- o2';{KӮ,fMrΏN{WI#?e2?I-a=ʝI[jlԈ`FW-H\3eʲ HWwXy3Av:*3F𽦕s euotprGq"iT!@$Aҹhx9^=?z@hԚ{if9`!;=Ni5egqts[R\ɝ2I9$gր-EW[O_\Zsgwn:V펵'v6 RVO")L=:_h7Zbeq)^1z!mþ@z7v9qj.PJ%PiK{C<:F @A$~b0d6L6jlFA {*+꽟9[YtbH+L DJ[1bf5Bј)|Yݍ#HߢJše,k _18:VCn~`V}r-.U4:8{m^.9s}ſ?o/#W<2i%/h7/?۬߫?1X0oF#VNfHBvN>V[@M5b}n4"8Np$xI]| m'3(/?7?uug(m՟ſ?o/#QDG'1 GgY?uug)ko4O}ſ?o/#Q_ ,c?E}Z}:ݖ(*ƿ( 89$[_: 44!u%Fe vǩ' j>>`?6?Q۬߫?1M_ _F~-&yOcAc-D'բQ]Ana'$' ۬߫?1X~:4!Fw}]@=8_ko4O}b} m՟QYVbDG[@M5Xe=~|oh7/?1[}*3}Xi׌%1ZP$ʏ ~Q9Cn~a]x})HNT_2q-| 1[@M5Xeÿuug(m՟ſ?o/#QDG'1 m[Uf1\93|-k/ SU- pNu 4!\j.b Jqcq sDUeh;_}Qœj?"SSWIDG[@M5iv_sHƭj 5!Rȅ8@qZ<=?^0;x#_[#H\ =RWe_-Bp l_ _FG=~Ao"ZK{y%Uyi*p8/$M{uvF&Q#ǪJ쫰6׆רHI'RAH?z?ȾAH?>?SSGE"AH?=_x}^/}~AQ3}ȃs4}~oWWDds5={z_r+Kk MwgÝ"@Kຎ%]2~^'\4k_'Kn0~"6fk}ߔnxq8 NO?7icȭt,œj?"ִ %;ǚ>L0r r}ſ?o/#WGuy~څ]Z6V챀ʥީUjJNMMFޓ%٥2di[͕ݝ3bIl>斎b-̮Od|܏v{SOiVI=sfH;d}֏ 5ZW{6Ҫ3Na2ɸUEu #u11;P؃,DIG\ɈOOiʛC,'$mb]>z nm%qv6hEeLcЁSxvZws~yubeIz sˏǤz\dRD"LD 9#+[~-&yn16S[FD4[x׍o)i!Ed|skO燿Tk[96d4q.+o\pOэ#<1F[@M5O+)c(9?Etko4O}ſ?o/#Qv]# tM#ou4vs\FHK"Giœj?"x}n4"8Np$xI]| m'3+k~-&yWs<=?燿T}ſ?o/#QDG+{Iw9SSVg>蚿4NPY,ḑct @Zh7^'_h oiB4qɪJ Qn@8퓏SG+{Iw*œj?"SSWIDG[@M58a%Nx{5OYT帞PWK˫pDlWF<}ӥvko4OՋk8H?o/Jw0-p3h⿝r)c(9?Etko4O}ſ?o/#Qv]o燿Tou'),l-;2\p(' jɨ}e;[[iBN|ˮK2!9=1Z]Y.I 4ع4mne%+ɦ=ޓ^^&+$k/H3 78 b{q4׷tfXdk9O F"?oQ@?V/?V ~>?V?:}4W/ncI"{A[_Q|9`o@?Q ~>?oQ@?SNou4{kXr"#8<Em_϶;5G? qaMGK&MmowjKGyƫz|SY;4he$'+d`3KGyƨW]MmowjKGyƫzSž4 ad# d䁁qKGyƨH]MmowjKGyƫzuQwm=ڒ>pXN2zF%ok#TkHkz0/[7}ޢ8xOF[ӮL=(&6Dr@k7}$B( 5̰Ί`&pğE wq6sN' ʼd3zu+"5/q,omd Xd`>S~%{rk'o98LwϯP*=jcjֻu>D&dpFߗ0nx$UɼL'< !kخ 8D(W<;xztVҋ4Ӊ !Dm8$O::1Kj(7 (#D^x C»Ү_-NWr9gQ'W?o;:^k_϶;5\爬^$ƶxEcn't=W[|ϔc}lxkVdWo1jXOp7>gD/\&>3=ݵ.."#^H$>u)/,8sN9o3fϛqJorVڭ8ܩ1ay㚢|;/$ }'/coc\kI]E~5E9,w#!2`s4.t#ZҚծdmն4uМ>=͵3RHīs}+o NC;.ʲ1i67uv.Bwʶ$x$&P p[:EY*Bs(IX}sm=hP$+2I{k$Wc1#FgӃzˢZMEe#/*(ډqg' jQEB ( ( ( ( ( ( l4 QK˼4={jMmowjȽoS%oW[e[N.[}\vݣݩ# 1eHd'$m_϶;5F! Q淫1?Q ~>?oQ@??7 M[ ~>?V ~>?b?:}4W/ncI"{A]`#D.E-Q ~>?oQ@?V/SNou4{kXr"#8<EwSQɨMmowjKGyƫzMmowjhzD]:ݳ]ERG=qW] __(eq\?į?f븮̕0+(((((((((((((((((((((((((((((((k?s]p]Yۚ?Йt;(J ( (8|WHeԷ1j1;YFÏ-HPH$ڥIx)yYeZź=qKL[cͳC /u#c_4@!R.e帒Ӗ-^̈ƛd``&KB`Wl=:~]4RiEyԧMpYp%𭿎`mb&oA0 y0rTo9#w^t I|gVddc po$obյx;h5緺Q+ir\tV?,H|e$,k`4{;I=zFj"9o#mG"X9'*=~O&D4%̲=jdVKa-C5cU$6emGl]vc; f 7~^@;xJTu+$n7VV8MT+ʻm6{ X#Եa178ܧDh)v?{5h7xG71-F8&nч`_q@vP+j¯&ZPF2Š#lBJB ,l]/OmxPkp[f]hB[LN$lZEt "HAyfH0n=͒e,U.oi} 2%>b9r܃'=G'y2+jKe$R4rn%DRnPcCKx 5--'[XV!MCrO"/EWxGۚz[Ks]]|]=ָaW~l;b"Ҿ2uOO5$[W)Yڞt/pu-x1H2~=O| nިȫi)(l<F>W{8pJit}ei7NT m-e[kxW.HQz*(o.U5ޛx/e QԂV==# Jj1o#?/Q= :+ٿ7_L sCn5=Nf(\ڤjd=n/O׌x:d ͼ&3­_^/&g)6GW,9&=s Я/&ub-_i4IIUlb 2p{{W_^/&xwSZ6KILП"12}~?{GMn#? ' h#_+/# _MWA_>u|&uxK}5bſ4Nx%daA A^{ x}Z}͍ޣ[\ʞDcr0 gd= Я/&B<%B?X+K x?_?kw_GW?_G!_D|_My,oW%h _M{5B1uÚDK[Ic3I$$B<%BMm5.%$#;Q@dOuWA_>u|&uxK}4/Oב_^/&X+KWA[/ x?D?4\j7bbnU@v #_+/ψ!hm.6I#?<_^/&G/OGW?_^G x?cx/G}^Mn#Э|n )YFlbڬ\ m$*=+k Я/&mu|&uW^J_iӯ1mfY-6{n>GW?_^697ͨ7#aƟWA_>u|&uxK}4/Oב_^/&X+KWA[-oWG!mEU[u%`յGW?_^7qMk4ڎ-%3B|(O 9UX+KWA[B<%B? ' kcx/G,oW%hϫ_ ɭ~?z<_k"-:;lA.Am/O׍xzscw142܌#!r8'X _M{5M<_W"+Y^9#Y!  ޼V.<}kimԷ2_"1#d.zW«QU?UVve-Vo#h^Wޣ|;5 i:yg ċP΁RqUgO3{^?zO"Uz??>L?y ?|;5M>[/UtlWF<}ӥ[n<ΊUz???U(0wq4W«QVl|O5ogmp7V1>L?y?«QQafQ'Whơ_;AF4oil[nO?14sᮍaJ+$ʡLHӊMI›|Ch3H#\~^GG=sVPYZVӭ&rxx$]> Я/&N#,V $I$H chc#z쿰uug+! :[mUS qvzttluch-ZyQ{7Jr[ݦl?a0^Mi4qA $ 8t6ֺ ޝ6iXƑ"4{I 3߭M[Q$Ku (QPZ]s>3aY g$c&1;$6ϴYYT#`{n?N7_ں06RDTL2ù3cp]/ϒf9=)̹[: k1-4[+3.|ٿ̉X.l>$i5j `# iْ6>'ӛF%Km?Nh<G F2e?R=jx4WBW+:o$J]f6Y  X븩9'yUX(7Άyc292y'p18{W[Ev1cotcHVD}і9H+@wY% >c.0$m\=)J[B^OHI $C,Gclmsqzu [8leHJMjBN2:֪ vkel.d<%ж2zUHχ8#iKJ\J̥Bx7$݁ ӯg[ q<2,K#lR1"s*H`:Kn&M!{|H< G0OJہ49VMf4WV}BG P1jt ZZK zqQɖB ?s+KMi mpZHEtvFWrNX}yP:ݬXiy`䝸ϠK KHT#Dcm)l H-)_-IFzCmz/6om,OKmI;F$#񦳨i_gF۳fcq9{C\iYZk(1e;9⡻}'[3gyT=(}}fEfh1 jO^'Tgis;جimI=W*\^$RjƛwI\! &nqcIk}$zvb]B@צi4сE..DZhVҳ1A98Vj1qؘjVF &%IuW6ʲiwR*< 9OK[HA82zʇrv=q(ȸP!FnxjB{-?*6 2bGiIi[KB?qU $$XPҚM0$*A x{R^Ad4q.$,FOµ ' k+zMΒC-N6GlQ$ֵuug+(9η"c["6sHDQtV ~p?/#*m/O>ߦo]U+\~pw Bp# xm՟0&!o!_D|_MxK}4GgY?uug(0& bſ4Nx%daA A[_xK}5 /k?i4h+|l Em`?6?Qa?MA!xK}4/Oӿuug(m՟~/C ' kׂ4 i.nI%L5$W$޷?uug+-$OУGT5FI'Ԛ=?ɿ/l/OGW?_Nm՟QYVbco #_+/_ x?D?4\j7bbnU@v m՟_ hxV FvV.2{d0a?MA#c Я/&B<%BwCn~: {_o!_D|_MT;HoXZBuvЬj[̺!@ ۬߫?1TKIo>s~Na-2(O=*eN)]M?As?W3YW+ez+{QEPQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEW5,w`LQ\%Q@Q@8uk̶jWs"2V;y/ +'Uwחnr[>L%طp"` I'e~<:ZDʶkq峜IH1媅"QiWK 4Xv$Uy9\D ѣ6d658u[HG!\#db9Ē^*^Cq<7qnqU͑ɦ=ޓ^^&+$k/H3 78 b{q4׷tfXdk9O F"7ѼOxb)hŝxcteWR$!K(,xe :meV{^|g[X=à$epGi@mmmXIkT6M:Vpnl^:~5٥U㹈nvQ+wH6w)$q^xwm6K{i-¢"aǒ[Mg&-&䷍,LĀF3ө@tQE'v6?].Fg?ܻ??ɘtOo'ȭהۄѦde[Ȋ&L8UU/kvoTb -Σ%RS{ppiWg9)#K{ZcL1Fv/ۼ*3oGEpQ^ѬAaxݷ :t9@m5wLӭNҾfEEWݝEPEPEPEPEPEPEPECq,.i$ت]D/b|p۸~:v.i;2]Hf[ל5e| GӦEv; j>THc@M/i}˔U3}JSca߇pq\rFy[JITfQE ( ( ( ( ( ( ( ~-&yz/[32??s~-&y|uuFF$#N̾Z85 ~>?b?:}4W/ncI"{A^Y_ko4O}ſ?o/#Q ~>?PDX:4!Fw}]@=8_V/SNou4{kXr"#8<Em}ſ?o/#QDG%ok#T_϶;5@[@M5bx}UH NT(_2i |N `rs%ok#Uk=|om}YG-%9_+ |qm}ſ?o/#QDG%ok#T_϶;5@";xcV[#Hg0%vUrBp lѮ?qڟ]{X#F<^۾;x>܀nJ5݆~ߪ86M"Cu &cVVxդ1$.=i"1~2FkvϙLFcC1(;lxfkg_nQݴ1:4ۯinm]ym])/;_vA#3w#9˥Z-2\]p]7ef*A6H'O@d/mW,_ٍoI޺ƚG ,n #kw+lqCq56/;-F ÀflSj6+OuǑ.Xnu晨C$y4fVE !IXOyu %N9C,u%m0܎0;X&xa3# ,N"gug4`̄O^=|˸ Ñt)= ;KeIu0Dl州کi_, ws" r\W8£Ƕ1+JTkrMkI",0%vR%@,sK~%!FuT'df۳6Ȼ~ezgP/nh0F ,F}NO8c65ޒ$SZFM@rvZks:6#'YI(G!?\t x5XUFsխ ~>?y/VZNu 4!\j.b Jqcq sDXߊtu EId^,rG'=#k7}aDG[@M5Q ~>?b}n4"8Np$xI]| m'3+k~-&y|a> oNE2hhr =wKGyƨ_ _F~-&yKGyƨMmowj' j}׉D:Fj2kT[;d%ok#U {}\zu1Gu$FXa m}ſ?o/#QDG%ok#T_϶;5@[@M5bZN 4OK꒩w s9 m_϶;5XN:=IoaaȈ:pADG[@M5Q ~>?ko4OSCQjvgXKkJ\dBs;y%ok#USCԠ+:Y[[C{|𵳘&%x6fsDqWq_ҿ ?]oZ Z=MDQtV0~pfq+(8;Xm!1*>|sZѺ]Zjɺ22 Gcɮ? пX0փk~Db-:;HՑlA z>OY^ mc v79UPe.0z0{ NUWV+hq.PpJ/Š WG'){zkYEq:x",m# {FU=<JǻbnnNI$W_ҿ ?? пOg2-jdhF&n3Bʃ&ca wryL !(jw*9փuDe]:KHٝjI$OzO/_Qv 3O f:j0MqɁ#{FyOÖvMmjD@O˂[s]o"~J(D7BQX,?RYm)aFH(1霎GkeyFͪ ݐ:>AZփqNl5 rG@v O/_RX~T)gg7j9]$|`rIAZի_ҿ ?? пMb`EEko۶SNtGY @!W'[_o+ YXʢ WG'({/?Jެ7W]?tfcԛixK}59sI˹H5 x[w5\> Em#搉6*@:~^FUNpj#_+/z#_+/ Я/&ȃ?覭a Z#_iĬ#b!r=k Я/&7B<%B? ' hz|"?mB<%B1uÚDK[Ic3I$$#_+/ Я/&7QGW?_X.4io3*ʪ9^QX?xK}4/O`.FGW?_U4=+N€(|JCk/%̡c5]wWuo~dQ\%Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@p]Yۚ+rv^ˡQEPQEQE&?kH$cM|A(d#ptd|eJoܸz7KON[H{2#l!a, uk̶jWs"2V;y/ +'Uwחnr[>L%طp"` @wu}7K'{MUnmYIH<,F2F-[PZzW#^{{%nEcćRBƼy9;4}FLFӆrD#kuc,V082<#;xKKn'Y#.-95\[Ѹ Y7c 1DQ2x;UQh3?/ yᕷ|CpZVd{Ps*fuksMdcd8; C)I 2YxCCyoj$(ڹ@0IS0h'|ZZG+*VZip km~\QG\9b)v@>S.K9,m$m?N.\ƪn9 )q)=9|Gyp4.a< ,)Lě7on;up[WZQKiز-(2ckpJ` A56đC8*j? =߅ۭRWʳd\E1]`6DbȑuV 1%Q@yUŮ.>Jw~n^^qink0%n+"?SӮ|ů)Wm~m%% Њ?#[븏f*M~/Y]Pm㹿)}x2[&G1\Oֻ/' jNu 4!\j.b Jqcq s}jqȿRh??Rh?_ _F~-&yV`#ف AKGVgG5mN5D*Y 2=k_ _F_]xƑG Ӯ+!MF}EZG/T_?"T_?"h7_ _FU>v`¤R¤RDG[@M5ZG]WƓ\߬זpHȁC: eIOi¤R/NH$i7T\܀q'h7o?4ԿE4ԿEoſ?o/#QDG֫0}wt/6q=^][ b5<3+O&?z|-uu'iC%IT\{p6h7o?4ԿE4ԿEoſ?o/#QDG֫0}w`kK{1o{;ka"n-#άˌb% AKGT^'Ue41Ӭá% ̹C}$#DG֫0}wIޥ#(Iޥ#+~-&y' j>[븏g?]/isY;phP>\cx??T_?"?&S6 x)Re[9c' j>[븏f*M~/G*M~/[ko4O}ſ?o/#Q];8SᮍehߔkyKH -#׮x'Z4ԿEKx2iB\T7.8'h㑞@#k~-&yV`#ف AKGQ AKGV[@M5ko4O}jq7 tm#os~Yq",X AKGT0;x#_[#H\ =RWe_-Bp l_ _FU>v`¤R`?6?S~h7_ _F5?խR;QYVb|D> >nqZ@c_n}I' j}׉D:Fj2kT[;d_ Cn~: ߵ' j>h7/?۬߫?1Xo'd>HH$] s 9$d8` ' j׉HAi7%Srr8>>GgY?uug)ko4O}ſ?o/#Q_ ,;QYVbmt[}V1}X:leZ`dŸ~cx[@M5bx}UH NT(_2i |N `rs_ ,n`?6?Q۬߫?1M_ _F~-&yOcAc;z-^դ"YLZpl +Fodqy &t1~Q\xYn4"8MGTWa m'3+F t-3wR.il@vF灜}UCn~: ߵ' j>h7oOcG `?6?V.ߦo]U+\~pw Bp# x_ _F]nWٓHƢ q?gF79'1 ۬߫?1GCn~7~-&y' j>>`?6?V/4['զDӮ+Pm򝰃ӂDX0;x#_[#H\ =RWe_-Bp l>>GgY?uug)ko4O}ſ?o/#Q_ ,;QYVbGgYo[@M5ko4O}b} oF"xVNe8J17BNNI>Cn~a>m"HFn#y5IQ|*-qkk~-&yOcAa: `?6?S~h7_ _FX.;'hojEjA"P[I#'Im`?6?VND #Htѿ$*kp<p[@M5Xeÿuug(m՟ſ?o/#QDG'1 kߟ걏Nc(s%$`6uug+ꬺFf:ut:B;Howd`c6h7/?۬߫?1GCn~7~-&y' j>>{xJV.u LV -8' `s۬߫?1XwW^'Jf40ӯ %*W̶K} 9!}EYgp gr0njGgYNu 4!\j.b Jqcq sDG'1 GgY?uug)ko4O}ſ?o/#Q_ ,cE}ZdM:)" F)8=8 [_:  54:u%vU$)vϨ' j>>`?6?Q۬߫?1M_ _F~-&yOcAa: _h'hQvXԪt$tMl}ſ?o/#V/NH$i7T\܀q'OcAcsQYVbGgYo[@M5ko4O}b} m՟_ hxV FvV.2{d0[@M5bZN 4OK꒩w s9 XeCn~: ߵ' j>h7/?۬߫?1TKIo>s~Na-2(O=*[@M5Teڝ!RRe%ÞxV_r_ >%̡c5]pPgk1/Q\%Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@p]Yۚ+rv^ˡQEPQEQEI>/dWyխRU$>U\-N2A-T-H՜\$P\?٧i%%«͸:%vm7n|C1kmiYO)LhHdbi~oɷq5)airL/h#s.ߗq(^M15綰diY%Y|Dމ`Wcmuӣ2%Y*y4i_+VaSƥ<>jVڒ\ Y\, ~\`[r+`mCĒEiy<0q!HfYA"}xa=({a)[* M1H2yfbsj7R³0*1|A8;ssKO j[repn kZ Eqē.Ur%,>_HOi[I~>S_`>]I8d*GAFQ躣bIuX2/ Yɑ3O_ [=xM;<;ǵ(n Eĉ;=[ZE8j@;&}=캣Iu!YIKr$_$"!!;* []"'(#H#YY#  p\u-Zw<Ȫ%H4H!QS˶<1޼ppI@wRBA@tQE'v6?].Fg?ܻ??ɜ޻pZ4,zD7 n_ ꑬWUdJ`sZZtOo'ȭהx]W~G5+N?#]j?/[7}ޢ0/[7}ޢ8OFӭьz=۩"52CpH=k7}<"?m7}?/[(t4rN~&v w+DDgKGyƨn?)dս@?Q ~>?oQ@=t9>f D$| 0ypq ~>??j ѷu@?Q ~>?oQ@=׊t8&x6La|0823 ~>??i Ѷ@?Q ~>?oQ@>>]_nj.GRG.bO@HMmowjC^oP%ok#T_϶;5[PK}\ß K@%ok#T_϶;5[P%ok#UoiFMV#9]`C@j?/[7}ޢ0/[[_}Vso}:=l.IY$`㸬Oۺ?/[7}ޢ8xOƭ[䳙ɣݢPYG x C»Ү_-NWr9gOxOڰtx7e/~Gw ~>?V`_϶;5Xߊtu EId^,rG'=#C^Q ~>?oQ@?V/?V ~>?V?:}4W/ncI"{A[_Q|9`o@?Q ~>?oQ@?SNou4{kXr"#8<Em_϶;5G? qaMGK&MmowjKGyƫz|SY;4he$'+d`3KGyƨW]MmowjKGyƫzSž4 ad# d䁁qKGyƨH]MmowjKGyƫzuQwm=ڒ>pXN2zF%ok#TkHkz0/[7}ޢ8xOF[ӮL=(&6Dr@k7}]Z?/[7}ޢ0/[_ xOHW$7hn0rDFpyA8 +P ~>?V ~>?T(5O3tusk-g̺<,z} u5i#`w@Pg_(eq]hߙ+aEW AEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP\?o滊ܳ5݁2wQEpQEQEq{P˩nbcIo vM͇ZI-JR\ʵ[u{[i —T;/qg-1YIӟz5^1Gth 2:2 >B27\=q%-ZǍ6H Luchzu+Pho/oOq ᔲ8$>[`rF./z@7iͿ6<ǒ&Hūj ]/J$vkourV乍~XHXא/''q hvu{9 DrG UE2'sr72OAVg?[V_<}+<4V++ltɭ G|I"M[Q >nqZ@c_n}IGgY=?ɿ/7 Я/&B<%BwCn~: {_1qNsHƣ~&!VUQ`B<%B.;'hojEjA"P[I#'Im`?6?Qa?MA!xK}4/Oӿuug(m՟~/F07mÚA47H͌[UK \Gm/O=~|oLgdM+99<{ƯK{_RC? ' h#_+/ _E<_A,GGpGW?_X߃1ᴏÚB$ڋ1KyU8==̿fx-P\xSZ6KILП"!2}~ab??{ 2= Я/&B<%BY/KEQ 3h?{GW?_X0-|[sHh뇎HbVF_<_A/}KOkSȈnFҏ,GGG/OGW?_^1 3h?fx-G#c# ' h#_+/ _E<_A .\\xsHitwI,bfv1$$GW?_^#c}[DĞDGj( ?,_%(4~Ad{?!_D|_MxK}5<_Ag/ab??{ 2=7Ƒ;"W{[UG+ ' kl{ZmOˍb<#{1xfx-G#c# ' h#_+/ _E<_A 鶾xUoiӬ#61mVi.C6r{GW?_^"?GW?_^"?<7ͩ7"aƟfx-G#c# ' h#_+/ _E<_A ߃1ᴏÚB$ڋ1KyU8==k Я/&F'i=ZJf Qsfx-G#c# ' h#_+/ _E<_A 0-|[sHh뇎HbVFGW?_^#}Z}͍ާ[\ʞDCr0 ?,_%(4~Ad{?!_D|_MxK}5<_Ag/ab??{ 2= Я/&| ]x#@,$XcRI%rI=̿fx-PXnmciv,1'ڊ%rxX=n#_+/ Я/&cg/Z/",GGG/O/ው"wÚD5 11 rW;y,_%* ?Sid"#y^(4~Ad{w!_D|_MxK}5<_Ag/ab??{ 2= Я/&hzVxY,-lm:;hV5-] }y,_%+U> Rϝ-Wث pqu'|NW[OMy_*n*2_=wJCk/ο{1_ (J ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ܳ5W5 C+((yMy;ZdFYo"edA5ZKs\)[CL)$/Ւ]]VKUVchVr>P|s8?PRj7"VsIpApfKդ '6 (U4}FLFӆrD#kuc,V082<#;xKKn'Y#.-95\[Ѹ Y7c4ǻtKמQde z&~у!^s_1ao|n&N g<ѤG9~̬[OYOigQ:qj:!FO66H1-C~lRR:fI"JehYDH1 y=({a)[* M1H2yfiǩ7Zt![Yr#&x;r7a/薭5OUevC\`8澜匾# R0E&bMƛz: դZ) ob 6F$4d01xv}:ѼAz.;ؒm2p]V+pe̪ $Vr2dbcib/,_yvqaf_-Ay:~~ÖxcIKm;eT8# =z,F-ֽ q"_EySbKs l %aIR^ˤYI߼QJToQhQ@ylj~]G8?_ ѷ5.yf![Iğ+j7j-v-niYV"oA'Kݤ#X= iCԔn$_qJif4k~ Q/*3g]_nj.GRG.bO@H_ _F~-&yKGyƨMmowj' j׉iDp:H꒻*mۀN;dgV%ok#UK}\h7MmowjKGyƨꬺFf:ut:B;Howd`c6h7:zUN@v["KrV@ <88Mmowj' j>h7MmowjKGyƨLF&uDJۉov`'vh7:{xJ[NBv&Kb0VHNHgMmowj' j>h7MmowjKGyƨ[96d4q.+o\pOэ#<1GAT\a%̡c5]pPgz{8Š((((((((((((((((((((((((((((((((,wvgnk/?Be((((7v!R,$1 Co ۸ԇjufo&T|FX?bѴ?o˸ L-F4Xk4Aq T0xE/5?U[5)uIls1q#R H +[ImPIX.0~]@6֡I"4dUH$3>0Ժl=:~]4RiEyԧMpYp%𭿎`mb&oA0 y0rTo9# ]ťNJ|ou#T_*g=C+U w9tCXu[P}&{5+P:a m3g'nUvu{9 DrG UE2'sr72OAUBm-NRvEX  P9P[mIu}H+vQyeo5--$wxKeLYII'mjrF?hW|ʏy37 \blŏkZ_S<Ÿ",h% Ujn]N\[<.آ+Š(B'&Vz|+Wm:esj0]p qx5GW?_^}_.#7_ׅ(GsG!_D|_Mb~Z?ij..eTN#_+/ Я/&7 w?)#_+/_x?-9E4Zu$v1+#؂\z(B<%B? ' hz#_+/ Я/&ȃ襭 ]x#@,$XcRI%rI=k Я/&7B<%B? ' hn?)dս\?ው"wÚD5 11 rW;m/OGW?_G!_D|_M?j ѷu\=07mÚA47H͌[UK \Gm/OGW?_G!_D|_M?i Ѷ\=׃07ҭÚ@xŌ[YK`$lۏm/OGW?_G!_D|_M-aG;񏉟PuOe7["6sHDQtV ~p?/#*y_m4j66ZG"5' 8/h֏sTQE}QQEQEQEQEQEQEQEQEGj;Wۿ0QEvAEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP^GB"Vz?rsוTgW:_(eq\?į?f븯G50+(((((((((((((((((((((((((((((((k?s]p]Yۚ?Йt;(J ( (8t^iw/ e nOM2FrIiiifDwM20R0u%a!p0#yMy;ZdFYo"edA5ZKs\)[CL(/z@7iͿ6<ǒ&Hūj ]/J$vkourV乍~XHXא/''q4pZyn[#HmpS|eB'goI{|y K$zź4Qƫ`Z7V6FsQ5_Hj&Vv؇j=3bq',bgԡTIm=儖vKJ)@'1gQ:qj:!FO66H1-e޵יr5]MZ433eKV 4+;kj eeed$,b,~rVf[k:uJR81H+rX%%.g/&>?*=-ѹFgR79Dhi;~_'DYn.}I7 \yKr&ާc+8}Iپ[v[߿nϲ}ϛ+4/};FϽ}uָȚ`0GZ%fT\&(J@ҡf1!,7WiiEnm!JEP^_+in|}?}qkKϒ߽[:רWxGۚ*:xiI[>;0 3}1kAħ#}x9{wGmnE[IIFH0a0q"OO5$[W)_99亾4=]oƫIHo%\pL.CHGR }X^~@;&-{/a6>yW6 p=|Vsbyk6(B(še,k _18:VCn~`V}r-.U4:8{m^.9s}ſ?o/#W,2i%/h7/?۬߫?1X0oF#VNfHBvN>V[@M5b}n4"8Np$xI]| m'3(/?7?uug(m՟ſ?o/#QDG'1 GgY?uug)ko4O}ſ?o/#Q_ ,c?E}Z}:ݖ(*ƿ( 89$[_: 44!u%Fe vǩ' j>>`?6?Q۬߫?1M_ _F~-&yOcAc-D'բQ]Ana'$' ۬߫?1X~:4!Fw}]@=8_ko4O}b} m՟QYVbDG[@M5Xe=~|oh7/?1[}*3}Xi׌%1ZP$ʏ ~Q9Cn~a]x})HNT_2q-| 1[@M5Xeÿuug(m՟ſ?o/#QDG'1 m[Uf1\93<[xQk_/3;~O!WҽS[96d4q.+o\pOэ#<1GAT\aQ,iCwke!K[kMRxOgB@#pK6~MH|qOk VVfKeH7e#|._Cv@ iw藗=#J-4&LB>MclîM5-ySH$rX: x.5/IPԖ xZeK߻[mC4K{Q\M" ۏ'$ɮPn!gs&{)*YW UG /=QE'v6?].Fg?ܻ??ɜ޻pZ4,zD7 n_ ꑬWUdJ`sZZtOo'ȭהx]W~G5+N?ZXgX%LSZFe;KMq2Y-rDonX0͒(_!q]?.5K}_Zo!і8f$.es\$i 4&-o~ 3g$.:+s(_ [5F]_nj.GRG.bO@H+_ׅ(GsY%ok#T_϶;5[P%ok#UK}\4%Œ1d;::d'srіP@lR[tbIUl}w WpľnBWʒA80O18cMnl2AO}9-irѕB0y9'9~iX$ƈdɗ$l8Ŀ[M_ uxEWYAEPEPEPEPEPEPEPEPEPEPEPEPEPEP^GB"Vz?rsוTgW:_(eq\?į?f븯G50+(((((((((((((((((((((((((((((((k?s]p]Yۚ?Йt;(J ( (8|WHeԷ1j1;YFÏ-HPH$ڥIx)yYeZź=qKL[cͳC /u#c_4@!R.e帒Ӗ-^̈ƛd``&KB`Wl=:~]4RiEyԧMpYp%𭿎`mb&oA0 y0rTo9#w^t I|gVddc po$obյx;h5緺Q+ir\tV?,H|e$,k`4{;I=zFj"9o#mG"X9'(uo4U.f;fHLHʁ[?j$&L+q{Z?,gaOvd L6w++^Ggjcb1J0)bK,X°ڝsV)o|R4Yʁ#цN& n1iWbgG2wʠ| Z'{VB8TPK3n [< DW:֫ӆ XL1b<78bu-/PkԔ5#lQ#K 6B_4e,Rx!zqWiiV^׭^_HXDCJ;(D+$JiW0iWVOw G$w(VJA0:(Q@ylj~]G/񕴷>>ʾۥXuޭk0I-w&"-+3'T_QO@5BGYb׌+NGs 6? a`E|糌gH,v^f#ztOM&Ux\Ǣڶ??߻5XL2Cy([ar@^ :[Ƕi^wlRJpmע+ (: sCn5=Nf(\ڤjd=n/O'4˻.Y 5 1[$Iڿ7"cqtGgY(6GB<%Bስ ~&]%U_\6ʩ[: m[Uf1\93#~/ol/OGW?_Nm՟QYVbco #_+/_x?-9E4Zu$v1+#؂\zm՟_hx#_OL\3E$V\;aB(0& GW?_G!_D|_M;QYVbGgY=?ɿ/7 Я/&B<%BwCn~: {_1uÚDK[Ic3I$$B<%B~/4 (u,QjU~Qp:rI&uug(0& B<%B? ' i: `?6?Qa?MA#Ƒ;"W{[UG+ ' k-D'բQ]Ana'$' ۬߫?1G_7/OGW?_Nm՟QYVbco vxUoiӬ#61mVi.C6r{GW?_X->t62w02\OqF?1}aJ9űo(D?tX0OY-xukTUf6l g8cU ymþ@moGEbi7Q^Ѭͅ{xn<0ASUm<SӜ-+v6(B(³iri0ij߼ japF1{8s~-&y+z\\CӮ.Trѣx ~>?y~6x>h7[96d4q.+o\pOэ#<1F%ok#UOWۭZnԑK$p2q316h7_ _F7}?/[~-&y|auvFF'N?b:}ǂ5W&pdA1 {@_ko4O}ſ?o/#Q ~>?PDG[@M5Q ~>?b>m"HFn#y5IQ|*-qkk~-&y|>oNF1np = KGyƨ_ _F~-&yKGyƨMmowj1|-uu'iC%IT\{p6h7)'G:=ی5ܬ9GPr"/[~-&y' j?/[7}ŵ?&.f(Pe$獯' jŵN7g6ӬݖȒ䜯>aF86/[~-&y' j?/[7}ź?&S6 x)Re[9c' jźN7ҧӯ݆ɒ،/>SFv/[~-&y' j?/[7}Nu 4!\j.b Jqcq s~?{pXN2zG+j70x;z/h֏sTQE}QQEQEQEQEQEQEQEQEGj;Wۿ0QEvAEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP^GB"Vz?rsוTgW:_(eq\?į?f븯G50+(((((((((((((((((((((((((((((((k?s]p]Yۚ?Йt;(J ( (8 ݯu=-m5K;iE?i ,A/m65!ƥ>=1Z]Y.I 4ع4mne%/%kBG.Q%14F7?6yjBA$*L^Kr*n-m7 ]Rb |m`@#ԼC}'-BR[k6}k+!/ˌ{~El 6xH9/4x'.#1u82 (;DOo 7|.[/mNjMmu)n!\2\z|+o-.m[X[yqHsqiq.CʷYøonPUC;6@P.=gPӯm$x&6W0'j7i:6'6{;I=zFj"9o#mG"X9'*u+\m#UvF؝YRH vq.K^E z~qi#Zj7EDb ) 4GQ$`um.I41)[wDCm)1ϰ$m6{ X#Եa178ܧDh)v?{5ɥ[o iUŭv1Ecoı̸]<1ymu?Y-6'ۍ3\bB6B)yڅ9x4K8fX䙥e rgOS\]gj:W7KPpѳHظKoi} 2%>b9r܃'=G' 袊+jn]N\[<.آ+Š(B'&Vz|+Wm:esj0]p qx5GW?_^}_.#7_ׅ(GsG!_D|_Mb~Z?ij..eTN#_+/ Я/&7 w?)#_+/_x?-9E4Zu$v1+#؂\z(B<%B? ' hz#_+/ Я/&ȃ襭 ]x#@,$XcRI%rI=k Я/&7B<%B? ' hn?)dս\?ው"wÚD5 11 rW;m/OGW?_G!_D|_M?j ѷu\=07mÚA47H͌[UK \Gm/OGW?_G!_D|_M?i Ѷ\=׃07ҭÚ@xŌ[YK`$lۏm/OGW?_G!_D|_M-aG;񏉟PuOe7["6sHDQtV ~p?/#*y_m4j66ZG"5' 8/h֏sTQE}QQEQEQEQEQEQEQEQEGj;Wۿ0QEvAEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP^GB"Vz?rsוTgW:_(eq\?į?f븯G50+(((((((((((((((((((((((((((((((k?s]p]Yۚ?Йt;(J ( (8t^iw/ e nOM2FrIiiifDwM20R0u%a!p0#yMy;ZdFYo"edA5ZKs\)[CL(/z@7iͿ6<ǒ&Hūj ]/J$vkourV乍~XHXא/''q4pZyn[#HmpS|eB'goI{|y K$zź4Qƫ`Z7V6FsQ5_Hj&Vv؇j=3bq',> jkSy?ٿg<ۻk8=7^'Y-^Q6qv@7RAexLtQ-bi]5Mz!fGݾE!7RP0 +FԬ-u%ch]J~`hV%> ]05Xma(#C=/HM&hViTV_$Sf266% гѵ=VmvRk+Hot:kiKrVHB%~{0yf[6>T柫C6gnՑ. b]]ʱ(%y=MKKI!"HSh8$`ȮI,`|.z,OY{YV![Iğ+j7k=86ܗWg ˼~qWw7]b+d2C)-"G۱H-cv~@;5^b3mJ_#yUqn_RU#߉4=KˍyDg}Z _ErtT^{3x<;wt# _EAqMk4ڞ-%3B|(O 9W5Eޯ?>qZ/"Y/KEW'Eޯ?>qZ/"->S-bheO"!FBpOJ樣g԰?r:Y/KEQ 3hg԰?r:Y/KEQ 3hg԰?r:[Mm5?.%$";Q@dOufx-\{z}GY 3h?fx-\{z}GKgBj~\m,D~y+ݙO 3hg԰?r:Y/KEQ 3hg԰?r:T&]O3E2?(ʌmGO 3hg԰?r:Y/KEQ 3hg԰?r:W!mO70$1 m&4O 3hg԰?r:Y/KEQ 3hg԰?r:[kYIi)D6FBx^~Waϯұu=NX%ιh\8UJ*QíAEVx/ C܂(O??E (O?}(?_0_r (C܂(O??E (O?}(?_0_r (C܃Gj_nok+ըr+_QE?C܂(O??E (O?}(?_0_r (C܂(O??E (O?}(?_0_r (C܂(O??E (O?}(?_0_r (C܂(O??E (O?}(?_0_r (C܂(O??E (O?}(?_0_r ?rsחW땧pMZ*JKmgХ +qNduPg_(eq^Z?|zQEPQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEW5,w`LQ\%Q@Q@2VKyuwZ-UYC[58A$$rBOIߴ[]Y%Eq,_VR\*ۈ|YWfx:C֚"F vm6wRQ].$ʐoG\672wI//^{k FF[h$MF x}Λ1|ه]k:3,2[[#FH2o9>uf?\j^!ekw-I>˽տw"xZ ׶E13*އFkɫXz¬AMi!xJ0tiyp[#sqiq.CʷYøonPUC;6@S.oᾼҵ{]2fMZ6n LdI|Q( 9 84z#[r X daBG 82 9LRm=;r\_s ^]Z,v6fh lw/jOV :y'P[[K].匫.1di+-[Kw JV,n) ei3q%Mt;aT%H1 2w=߇-ƓkqjCg r[Ŗ&@Ib@#Ñ˭wZ]pl. -1HaՔ0PH~C׷gË,{"la)@_+<`b4袊+q^Lц}] 뫃N.9{c`0PmTGrIͻuB=j .!%Dbee,6.er7nS; YeӴ6"A{4N D]h ohX~n뷚t ^ (aFɐ[y`%,@p]ǡ5=<(%;[ڢ%Bp+F2I`x^^ XkFGs8kJȹ0߀vn-l'4i.Unl*a*~6bMiZ:e۫7Gq`8$gָ=;Qo;;Q=֐ʫ" tHD~YjZ43mgIo/PD6uQ824袊+Jw~n\yTNImu6v`i_ə:tOo'ȭהON8:`_Jr?w׌sOiqoTvUd 6#B+=c8%4Ggò4п *p+mo XDrEڌUG@]]oƫEK]^\R Mp]V=n]JuH)]wK)b)MfsW[(^zKEG"__'}'%EzhΝ%P@1{SSVREٜp| +Nx{5OYPm _^5LIuk<m;:+Nx{5OG)c(`wqWœj?"ҴNx{5OGk燿Tœj?"'w!Ez tI|O5֡\# qiu`~Lc.8 ?SSQ(o9?E<=?5o LjQ^?/iuy7w7D7Tĭ;O燿T}Fm; +Nx{5OG)c(`wqWj tK-CEP)}x2[&G1\Oּ5Sy<6;X6OϒWq97tQEveࢊ(Ul7wQGv_xka(?|ò[ EQn (_0wpQEe(W;/?QEa}Gj;QڳW1^O9~AEVa}n (_0wpQEe(W;/?QEa}ࢊ(Ul7wQGv_xka(?|ò[ EQn (_0wpQEe(W;/?QEa}ࢊ(Ul7wQGv_xka(?|ò[ EQn (_0wpQEe(W;/?QEa}ࢊ(Ul7wzxZ9˫~C_W~ڵy٦>|;7:_(eq\?į?f븯F{>]|L(((((((((((((((((((((((((((((((((+rq\?o滰?&]((|a^yfNHK3I'mV/'żiVF Y<374meĶ>vH .`䊖 -TgyPq,6.PyrTę}懲KMlXKu{y̭3*B#"1W*})-M4KQJp:;p327~|^:Quj̹/5j8 &!=Pt6WlBoq}\\BUKK2pe|(\G<5vڣjl{uʹaX_|>}QRK{{VoRU!nIX6|>c@և_"%WT5=HrOT1Vx]NK GNK)~~s9 &o0m jɸu +[i"Vt^Lr{^Js9i!|E="FSӖoD7JnrrCǵT4;d[$Xn,bAonҖN#x~`&"jݽ4(tFa I|`"<- *DmFF3PA=Ɲw4nt+)xĞl*>-cfYio.TV2%'h.4+mþ@moGEr:>,X`2Bחn4#,9ݝv?m{Yjmɋ懽}_nGMEd7mmoN{2 ~ Muⴆfp7ۿqǙNGEfN5Œr0?w?6X0]gI=n$h0AX7*vsK=Vcw>h7º힗:L\m:qG-0OQ[W_>#' jNu 4!\j.b Jqcq s ~>?b~)6o۴{$}x, dM' j>h7MmowjKGyƨ_ _F_]xƑG Ӯ+!MF}Em_϶;5X0Nq~ɧ\"4{PLl9f;[@M5ko4O_϶;5G%ok#T}ſ?o/#QDG%ok#T_϶;5@:#@[}#H[MRTf_-pJrlz_ _F_OFӭьz=۩"52CpH=k7}>h7_ _F7}?/[ _ ]xtF~I}RU;.nx  ' jt4rN~&v w+DDgKGyƨ_ _F~-&yKGyƨMmowj1mnO ˤicYCJ/s>I'vF099k~-&ym|SY;4he$'+d`3KGyƨ_ _F~-&yKGyƨMmowj1nO ͤia^AJTm>Anryk~-&ynSž4 ad# d䁁qKGyƨ_ _F~-&yKGyƨMmowj1u_fM#HW꒰f%mr3y_Oj-}\/|pLeA?ZMo:|mp"]vv\/Ŗ9#~?ڍ+:^ޏoOs"QZ((((((((((((((+~C_^]^GB"VzW'Q+ez+2_=whߙƾ&QEpQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEvgnkk?s]y.qEW AEPY>)5" "y|cc׺`⵫'?dCZ/Oݳm$:I$(xbKeKa>Uvl`gusL[~B$ejd?R√ŬZ-EE^dM)@7(?].FףלxGۚgۗ3T_QO@4j?j_ Ӯ"k\}K\Q۠cV :3d+oB.[Yʶ@  \{x8UQP;W(wgrc9̺bj1‘#*ю0HEw;E&2F0x#zk_]ʌ}{E͍FZ(h :FAP#$(Žޭbl[*I~H'^@Q׎F/G*CPH/ook¾}.[OFӯfY,6#ڧ''s[xK}5Wg,5o ?GW?_X߃1ᴏÚB$ڋ1KyU8==3 ' h#_+/ >#]j? ' ků5iMpJ6  ހ;+ Я/&B<%Bޢ ' h#_+/ sv)kz?^..<9K4u$13;ԒI\Oz#_+/ + Я/&B<%B;Ijj*'tVETFMwFU$'=`!w&\rH=>(zmYQZ((((((((((((((+~C_^]^GB"VzW'Q+ez+2_=whߙƾ&QEpQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEvgnkk?s]y.qEW AEPY>)w5O> >x{a۲µMu~'it넎8Գ;{PDfZ[)#Iy5:,%$ _ކsICFdab jfs|:x\H.nldy=E* r7a9Shwz&ӭlU.eykTGI7 o!ꆣD+ec{rZY++B9+xygE 3> JVܿ=Oyazd ͼyUZ5JV(dtŽ7i?O7R~O _Z5JT[u5jۤ 4Ckd'v+on^|֟o%^soYdaɓtm@s?[6mm}#JZ5J? _H4ņ#Y%0WP!M?On [ƿX?kx_+[_M?O ;?&įBj\m,h#{1xkx_+W߃ -^ 1Q'܇@wE+FDŽu5:^, dߟW_ω!գ|*FbVޮ8ܓ_wGhPyGb1ްZ3P_{~/xI-Q3TW%NÜX:~ ZOhK>) 2c}l }V=~RiIB];q+ŷSZ6KILПD6FBx^~Waϯұu={R5 o|q*t σ Nm+, "y2~Ng>owIinqO%mly(\z7(dtŽ7i?O7ZiQ _ 4NLhlEY E,@Z ;?y9Q oQ&'on<y(\z7(duSJ? _H4ņ#Y%0WP!O/x4'(\z7(dtŽ7i?O7G_iw?A^ ;?M?O?A^ ;?M?O?A^ ;?M?Ow_8yQ oQ&'Y]i^i6LV['@,z?&[Sk;9#Y$B p#ַ-їNnNXnvos0;x#_[#H\ =RWe_-Bp lVc_ݻ<Nx{5OG)c+_ _F~-&y;s9?E<=?~-&y' j?q_KxsncI.9(g@ 8?SSUx|-Iӭo&*3/%EN=Mm}ſ?o/#Qv]o燿Tœj?"O' j>h7;i.|-5M>[t$@6q$jyCg=:V)c*ׅND #Htѿ$*kp<p[@M58a%Nx{5OG)c+_ _F~-&yWq|-%=Zogmp$MŤyՁ1D4Nx{5OVnO ˤicYCJ/s>I'vF099k~-&yWs<=?燿T}ſ?o/#QDG+{Iw8xL[Cɸv2&Ѽ |%l~:<=?WW^'Jf40ӯ %*W̶K} 9h7_ _FG=FD4[x׍o)i!Ed|skO燿Tk[96d4q.+o\pOэ#<1F[@M58a%Nx{5OG)c+_ _F~-&yWq#[iյ;{A5D*Y2=kO燿Tk^'okqit#ǪJHSn8푟Q[_ko4Oh⿝s9?E<=?~-&y' j?q_Kœj?"<9D1wZyg ċ3bPdoDX:#@[}#H[MRTf_-pJrlz?q_KW燿Tœj?"O' j>h7;i.7 sо蚦-Z^][ b5<3+_ _F_ ]xtF~I}RU;.nx G=Nx{5OG)c+_ _F~-&yWs<=?~՗[cyA("O)muմԕede[V "'5j6Sml4nqL6 G_xI%{[[9uFl򑴢cn] jɸu +[i"Vt^Lr{^Js9Y4ox ^8o,IneLe B*zlTuRcu+l̕xLr9Ycj^c im͹i.TaxDwe<ڪPifkw.q VƩ-+ʁ! &G *XQYio.TV2%'h.4+oNF1np = KGyƨG>]Zޠ KGyƨMmowj)'G:=ی5ܬ9GPr"/[V`_϶;5G%ok#UEp)Wu4{\0H%ok#TZ.F`_϶;5G%ok#UEp^)TWؚut{2[Gr@8%ok#T].FV`_϶;5G%ok#UEpߊtu EId^,rG'=#k7}5 x[w5@?Q ~>?oQ@??7 M[ ~>?V ~>?b?:}4W/ncI"{A]`#D.E-Q ~>?oQ@?V/SNou4{kXr"#8<EwSQɨMmowjKGyƫzMmowjm|SY;4he$'+d`3m?~cnMmowjKGyƫzE> odS&v B9f;.;5 T>F4OJ|m9\Fw~e瑞?(k_?j!'vuo%ok#T_϶;5[V ~>?b~)6o۴{$}x, du x[w4_϶;5G%ok#UE`_϶;5X0Nq~ɧ\"4{PLl9f;qX>7 M@%ok#T_϶;5[P%ok#T_϶;5[P {}\zu1Gu$FXa m_϶;5G?AR`_϶;5G%ok#UEpN:=IoaaȈ:pA ~>?-5,7}?/[(N7g6ӬݖȒ䜯>aF86/[G_v?67}?/[(N7ҧӯ݆ɒ،/>SFv/[G#w6ҷ7}?/[(S˫mW-EݷhjH% Yc88 _Qk#MmowjKGyƫza> oNE2hhr =wKGyƨ>#]jޠ KGyƨMmowj KGyƫ)@xtcvHG j+ sv)hMmowjKGyƫzMmowj|-> "t{}\߿ɣݸ]Ñu +šM@%ok#T_϶;5[P%ok#USCԠ&I's>DEg^IOj yIpad5:kI\imk&h킬M Dm!U ]76h_S_ʇ?fO+gwYx@-Q@ylj~]G/񕴷>>ʾۥXuޭk0I-w&"-+3'T_QO@5BGYb׌+NGs 6? a`E|糌gH,v^f#ztO<1En=2RTKi?m^2?>wݿ^5[j,G?-ŽobT羞pc˷xK}4/O n? ?KuIV}Jtv?xK}5u /u!}:1ce)#n Y*& ]f7ׄr1Li{>0N? ' h#_+/ u]~jRChs<m՟b__AxK}5?jH9"MJcdlSڷ?uug+[oWڷi .c?d;!8sCn~GW?_G!_D|_M;QYVbGgY=?ɿ/a['y9JQ@cw*zV!_D|_Mc[EH@j7" w(-$䑓$6uug(0& B<%B? ' i: `?6?Qa?MA#o šuf-%bBO}Ҷ ' kE>7c'ՃfQK)00H9'm`?6?Qa?MA!xK}4/Oӿuug(m՟~/F׃07ҭÚ@xŌ[YK`$lۏm/O=֋~J?{GW?_G!_D|_Mx,_%( _EX=g#_+/_x?-9E4Zu$v1+#؂\z/Y/KET?>~eM D7#\ G#c#ۿ ' h#_+/ _E<_A xK}4/O׌Z/"Y/KEQh?^..<9K4u$13;ԒI\Oz#_+/>SXbO"#JZg/ab??{ 2= Я/&B<%BY/KEQ 3h?{obH+F=LBܪ[_xK}56~?=- 1DG٘S-bheO"!FBpOJg/ab??{ 2= Я/&B<%BY/KEQ 3h?{GW?_X.\\xsHitwI,bfv1$$_<_A,|}7OkhȈE<֏,GGG/OGW?_^1 3h?fx-G#c# ' kƑ;"W{[UG+g/Oa CmqLGvw¼g/|*ukz֩qΖqU†8€:>'+&ռB7vkJCk/%̡c5]g_֏Q\%Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@p]Yۚ+rv^ˡQEPQEV/ O, qfv1$Z#_-:6*6 9'u󦍬a9ۉw6݁]Rz:RӬ<9.%F.B /cQi ~γOv<eHYdu$[ aJCR6u弉`)T.PGtFssFoσ+Cy2xN[-Wx%淑R0$[$6ݼ \{Z/ˋV2 if\ H?.Tm^͞C4n+1r P':\oojmJ2-+ރvhռDjFc nIF> bI-p²_HΛ$ 7ZCZn4BH6W'W>Re'ӬUf.`A*fO 6isʲ*嶐UWFU>a.$W7}FwJ88PH zwo{ .B_7Ȱ B0A'ь2(:,6h67C1§,iNC9e7"}RN5 yvԶ,>[Apѡ_1u/3 ƥ5]Kq{mdnvI}E:x |- /q š(Ryu]B[ԈB6eni3vnulCWl/53 xeh7^'okqit#ǪJHSn8푟Q[_V/?ko4O}ſ?o/#Q ~>?P/NH$i7T\܀q'h7)@xtcvHG jMmowj' j>h7MmowjKGyƨ^']"q:hߒ_TN˸`[gk~-&y|-> "t{}\߿ɣݸ]Ñu +k7}>h7_ _F7}?/[ [[oAaꒅ \!ϒIݑNx_ _F[_}Vso}:=l.IY$`k7}>h7_ _F7}?/[ [o3i@iׁj+[n%ϐAہF9_ _F[}*qoM::=l-Y#9 `qdgk7}>h7_ _F7}?/[ ]nWٓHƢ q?gF79Wڋ_A'P~q*ֽS[N.[}\vݣݩ# 1eHd'$y_"s α< QaӸk"x_hG5EW!EPEPEPEPEPEPEPEPڎvqoo~EWiQEQEQEQLUb{ \U>* ېQ/,;}QBnO|P# Pxĉ}v˧[Β|6W!QK3g$g®ֹy6z|yn#lcI^lnUxRh$ķ&fG2Vگv-(Ncy4kbiXP@@ag ]?PjkEQ{{\n?1X(xnx{K,&k4DR>,r+.0,Xdehګݢke'+ZKF9 *C1 $a]blk?N;(/8/j6#.]f,F6{>{c9%Ԫ3qX6Z-.9%E"c 6S2OuM?XaqoaG2#^v"9FWs1.#PSEP^qinkVB%Z(1YAc7NT m-e[kxW.HQz*(}u91P]_1|5o MHy N]xfghƤ]";{"y # IGF5诮F>=M"# -oi k4#P` vrboVg1FE-k$$/ (#裕!$z? 7| _hz-Ƨi׳,KTSXǹB<%BF`Hh#_+/]oWG!mEU[u%`ՙQX?xK}4/O`D.E5xK}5bſ4Nx%daA A@GW?_G!_D|_MoQX?xK}4/O|9`o\?ዯh%]:KjI$I'm/OGW?_G!_D|_M-5,1qNsHƣ~&!VUQ`B<%Bޢ ' h#_+/G_v?6xsH0fjIrhB<%Bޢ ' h#_+/G#w6ҷUsHxk2l̓qB<%Bޢ ' h#_+/_ׅ(Gs^13JlRW~Z?ij..eT+mF [hD\ƤGO^E/j(:B((((((((Gjw;(ӈ(((*h#.*j)J*J i\\e8~Ԧ;@[p$55 _ֿ+"[H# &;*`rNsdIUc};.I9$LqR6ցʂ(FQEQEQEQEQEQEQEQEQEQEQEQEQEQE?!/i.Q#!+O=yYFu~2_=wJCk/_ (J ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ܳ5W5 C+(էa<^u=ѰFrOaZՋ&Foo4uGjYlrI="M3@K{IJ-Ԥ /C9y$!xzM& KLDۋLf]`q$dIʒą :yFP0Y\KkCmĈLb.Hk^ONq)i{e ɉ#m JG!I|Iޠ~"GZ1|>aTѴR%嶹}:AWlj?[+ YL:ίg4l@U.N$k&Ü8UሏJU]֓>uWؗhپI.(d} wo{ .B_7Ȱ B0A'ьI9mzM:&Ofb]dD=̒ (CPL&F4KmE,_&Pٔ&Q $lŶa\: ,W Y| 3>aׅ ]h4i59aW;%LsCEEP^_+in|}?}qkKϒ߽[:רWxGۚ*:xiI[>;0 3}1kAħ#}x9{wGmnE[IIFH0a0q"OO5$[W)_99亾4=]oƫ#_K!Ii<^݌ (Aof YZl!hRmrp8@1ӵ{߷7?? O~f_ZoƗ?? O~f@rDZXxĚd ͼ"3­Z/":*\"Fn& _EAqMk4ڞ-%3B|(O 9W5E}?fx-G,_%+gz?Ⱦu<_A/}KOkSȈnFҹ(pWGY 3h?fx-\{8vE# _E<_AN=;"nmciv,1'ڊ%rxO 3hð}^/g,_%( _ErtQ>G܎'Y&#Ȉ;;W3NJg/G`_r:Y/KEQ 3hð}^/*x_.ȋBF6#~§ _ErtQ>G܎g/Z/":(pWGJ?<7ͩ7"aƟfx-\{8vE# _E<_AN=;"xSZ6KILП"!2}~wơ-u̸BtQWMnh %WQEk?eOWQEXa*%~GkJ'*o>=?gȵ/S?GOUH>u%~GhЗQ_u?#4hKZ(`e ?%~GjrG}n2Ou?#5V9#>_kB_Ə E[̹"T t5gÝ"@Kຎ%]2~^'\?x:+xW۠!ߩHͦYw'~Sh7;'Kœj?"5O%ukyKH -#׮x'][@M5bw^':̚F51%`K 81g9?q_KW燿Tœj?"O' j>h7;i.7 sHƭj 5!Rȅ8@q][@M5b}n4"8Np$xI]| m'3(;i._SSQ s>h7_ _FG=)c(9?Etko4O}ſ?o/#Qv]#ß tM_Nqu,זpHȁC: e Oiœj?"x>m"HFn#y5IQ|*-qkk~-&yWs<=?燿T}ſ?o/#QDG+{Iw8 niOu+ո "+#Sp>ҴNx{5OV-uu'iC%IT\{p6h7;i.7 s?Nx{5O]'[@M5ko4Oh⿝sn/ƺ<{;ka"n-#άɌb% skuMV]#H3::RP|˝7I;01_ko4Oh⿝s9?E<=?~-&y' j?q_KODbjMŝñ7U1+g9?EZ?&S6 x)Re[9c' j?q_Kœj?"SSWIDG[@M58a%5O%ukyKH -#׮x'Z<=?Z׉ί&+EAuIX3}~6n9r6h7;i.7 s?Nx{5O]'[@M5ko4Oh⿝sHƭj 5!Rȅ8@qZ<=?^0;x#_[#H\ =RWe_-Bp l_ _FG=)c(9?Etko4O}ſ?o/#Qv]o燿Tυ&'Sk8n$X@2'ֻ' j}׉D:Fj2kT[;dv]ʿ<=?燿T}ſ?o/#QDG+{Iw9SSVfD4n'đ8A|][@M5bZN 4OK꒩w s9 8a%ܫ s?Nx{5O]'[@M5ko4Oh⿝s9?E[-nIK)NL#+g~-&yhrj2YmNomrө_2̈Axa [RxSO:POk(yҠ}o#y ‚KOje&y#"d3cs"Ec,k /SiF$3Hh± !@|  |=Hn͜ޤ+#.Bukha[W7r](Ȓb]UJ0Y -f]%WՂ׍yvͶvzuzAhgfv]’&t6iX$^Uēy)|%ylJ-F5Ժ^GMVΪEP v A x^}+WԴ8-|=)WiT(I"I 3 | MQ@ylj~]G8?_ ѷ5.yg7%֍4#*޶DQ4a¨$x[zkgAm>u(z֖![Iğ+j7k_ߑJӳ~x?-+ծYT,3n7tr Uz q˧ʺwFmQ_`lQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE[^I/>_?{;5 T>F4OJ|m9\Fw~e瑟|_Kh?o;:#zS0?/MoKGyƨMmowj KGyƫ[N.[}\vݣݩ# 1eHd'$wk#7}?/[(7}tk%M:ɣݢca4@($޻>#]j?/[7}ޢ0/[7}ޢ8OFӭьz=۩"52CpH=k7}<"?m7}?/[(t4rN~&v w+DDgKGyƨn?)dս@?Q ~>?oQ@=t9>f D$| 0ypq ~>??j ѷu@?Q ~>?oQ@=׊t8&x6La|0823 ~>??i Ѷ@?Q ~>?oQ@>>]_nj.GRG.bO@HMmowjC^oP%ok#T_϶;5[PK}\ß K@%ok#T_϶;5[P%ok#UoiFMV#9]`C@j?/[7}ޢ0/[}f{xNB.melGT\cm?~cnW3YW+ez+{%|L(((((((((((((((((((((((((((((((((+rq\?o滰?&]((|SOD5bh>)w5O> >x{a۲€1.uOǣk w$V᣻r%|$$e@Y!W9Pvx·F-/FْhHL[28tfo#'hnw!i4ԟĮNJKșLaa, O"37N7l!iാOe  'FD,HR'%4}НP7((JHr]۷8lH4k `HP۔$cnu~"G-Ƌ<+NHJ1vO$F[:(V@O^W%Y>2ּwWT8(1V8HyrCpX 3is^]Zvhҋqkdtjbf9P߽F @2j&xv]oz\Ro=Dch a"/3J|A40귺.n.~Ԑ ,I2/ς2EG_Xk΂au{9SeP2pvgI#Y6 @Zm-V)ݴW6$d"D`I^Bkx(pY&Zir4%a& $؀n8!hO__]GcRbELŒVv%_ȭ!R% IzaΣFؒ;$c`+bU' :( u`Os^^_+in|}?}qkKϒ߽[:a,4[jvMlDZWfN![Iğ+j7k;SӮ|ů)Wm~m%% Њg M?ﹾY/F/ \img{ [3 \{v8UQP;WyAGuhd%̷1 PF/nH-czn;F5Ui6TQE}iQEQEQEQEQEQEQEU;ZMl:dz>{rjdVI$a&C/rO<:.Vv2^Wkv4./>ĉ wszulxКDCؖ,?˴ s"8JF&xBy7WJ.| (aEPEPEPEPEPEPEPXuXud3Q^EPEPEPEPEP_׿ơ_;AF4oil[nO?15I›|Ch3H#\~^GG=sA,T/$޷/WGW?_G!_D|_M;QYVbGgY}?ɿWGW?_X߃1ᴏÚB$ڋ1KyU8==sQYVbu5 xVZ3Kb3G9cpAa?MA#c Я/&B<%BwCn~: {_o!_D|_MbFqo")Ӯ9#YFB{Cn~bE}ZdM:)" F)8=8 G_7B<%B? ' i: `?6?Qa?MA!xK}4/Oӿuug(m՟~/Fዯh%]:KjI$I'm/O?[|H'աGӭb+R kГӒO5۬߫?1G_7/OGW?_Nm՟QYVbco ~.4io3*ʪ9^GW?_Xo'd>HH$] s 9$d8` GgY=?ɿ/7 Я/&B<%BwCn~: {_õxsH0fjIrhB<%BǵoύXlj`YEk s 0I_: {_o!_D|_MxK}4GgY?uug(0& u /u!}:1ce)#n [_xK}5uߏQNa)r%T~9vuug(0& B<%B? ' i: `?6?Qa?MA#["6sHDQtV ~p?/#*m/O>ߦo]U+\~pw Bp# xm՟~/C ' h#_+/`?6?Q۬߫?1G_7?x?-9E4Zu$v1+#؂\z#_+/hx#_OL\3E$V\;aB+kQYVbco #_+/ Я/&۬߫?1GCn~GW?_X.\\xsHitwI,bfv1$$۬߫?1XoF"xVNe8J17BNNI>a?MA#c Я/&B<%BwCn~: {_o!_D|_Mb['y9JQ@cw*zVCn~b[EH@j7" w(-$䑓$co xK}4/Oӿuug(m՟~/C ' jiG}2Ӭ]cRe UGgY񊧢ZMg}f9 :u nV0y_/F8LyS*qJi Pg_(eq]5hߘŠ((((((((((((((((((((((((((((((((,wvgnk/?Be(((+Mu~'it넎8Գ;{Vby|[MpdaAzœ:yFP0Y\KkCmĈLb.Hk^ONq)i{e ɉ#m JG!I|IޱńgY[ 2,:-0o%r OқP:DDE[C#9#qMh ŅǔnѮIfЩUB(Ŧ[\jSޮEȖ[XV2hH?U$ϫvС)%| (x9mfgT-?m.ohQq$#FY :!*$eul~ehDžMR[ XtSܟ-VvhЯRi5X`K[.o }V=Ծwo`|;GxFKIO:Q.eMlPÝ "\zŭE{s.nl[4.nf [G5t^I+ƓQH-KFL)hHmnmP(?].FףלxGۚgۗ3T_QO@5pZ4,zD7 n_ ꑬWUdJ`s_7Nn /84diyt3\-{a6ʹE)|9U͸w~x?-+ծYT,3n7tr )VZv}袊Ӡ((((((((((((((((((++^|Sx]|@+>`(((((|?x:+xW۠!ߩHͦYw'|?{;5 T>F4OJ|m9\Fw~e瑟?>"?~h7_ _F7}?/[~-&yu_fM#HW꒰f%mr3m_϶;5Xߊtu EId^,rG'= k~-&y' j?/[7}>h7^'okqit#ǪJHSn8푟Q[_V/?ko4O}ſ?o/#Q ~>?P/NH$i7T\܀q'h7)@xtcvHG jMmowj' j>h7MmowjKGyƨ^']"q:hߒ_TN˸`[gk~-&y|-> "t{}\߿ɣݸ]Ñu +k7}>h7_ _F7}?/[ [[oAaꒅ \!ϒIݑNx_ _F[_}Vso}:=l.IY$`k7}>h7_ _F7}?/[ [o3i@iׁj+[n%ϐAہF9_ _F[}*qoM::=l-Y#9 `qdgk7}>h7_ _F7}?/[ ]nWٓHƢ q?gF79_ko4OՋOWۭZnԑK$p2q6/[~-&y' j?/[7}׉iDp:H꒻*mۀN;dgV[@M5b:}ǂ5W&pdA1 {[_PDG[@M5Q ~>?ko4OՋ44!u%Fe vǩKGyƫ)@xtcvHG j_ _F~-&yKGyƨMmowj' j׉HAi7%Srr8Mmowj|-> "t{}\߿ɣݸ]Ñu (k~-&y' j?/[7}>h7&/mmα -:.,ȄvſKGyƪAxY;EӬPYm>edU$shį?f븮W3YWuZ?JQEPQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEW5,w`LQ\%Q@d>֮.< n|rm^q;֬ kؾ?>Ϗ3vx`M<$k+B+k=M77J0#  -MO/ |سqz-lv.go0<1y'an2ѿ-CWOu M٬.nu/+J0RXG'M9gОK8Xl9L0>` qoQEWxGۚzu`Os\_;r&`j?jWrȧEWQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@aa׽ϗ|˯QE{Q@Q@Q@Q@Q@|_Kh?o;:?{o\j.Oh3M68ϛ 6vfS_P}EitV!_D|_MxK}5X:-aG;? ' k["6sHDQtV ~p?/#*wV!_D|_MxK}4X>7 MG!_D|_MbFqo")Ӯ9#YFB{qE`/OGW?_@V!_D|_MxK}4x#D.E-oWbLJ4fNy$&gcI+I[_xK}4E`/OGW?_@? qaMGK&- \i%qߠgUTr+k Я/&7B<%B? ' hW]|n )YFlbڬ\ m$*=+k Я/&7B<%B? ' hH]n/^;,b̲[$m 3`}kk Я/&7B<%B? ' h5o ?p߃1ᴏÚB$ڋ1KyU8==k Я/&7B<%B? ' h>#]jޮů5iMpJ6  ޶ ' hz#_+/ Я/&7 sv)h#_+/_?^..<9K4u$13;ԒI\Oz(B<%B? ' hz|! [ j?Y5xK}5obH+F=LBܪ@GW?_G!_D|_MoV ڿmxK}5SCҴ#;ake i.Bo2dgs(į?f븮W3YWuZ?JQEPQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEW5,w`LQ\%Q@d֭<- dqn3{ ֬_A5ׂ5{xYӮ8Rc`I@i[jOWYlX%L&0谖`T'zϛ'i mֶZm4Z_Je'Zc2g#"NT$)֛Κ6Vւ[X;n$EZ`vAw0rEK_Z|tKN<(h世LIo(RR<*KLn5:=I}Jf0o.eBCΟu\qrE X1|&I݆8mOy2xN[-Wx%淑R0$[$6ݼ \{Z/ˋV2 if\ H;#hy+K P@h'@+}*=oڦEcaPx-X1 D~VbC]hpMr"UxKSԁ7$A#Aq =h1M$2S(n ,&5yKo i.ntM=4aH3E$=&gWY6Q * 'fx5a`Gcqq=BQKdjB._b@+0/ݽ4(tFa I|`"<- *DmFF3@mhyWl(r dˀ*?t,~id畆GP?7%g&KM4\)˽s Gi7o*yZ< q4Ui~q*M_,+#G|(V*nk%c;{qzOluP8?_ ѷ5ǘTtҒM}WGtv`OgBGYb׌+NGs 6? a`Eij?jq,7ڼX\s9_ _F&g[Z5R9SSVfD4[x׍o)i!Ed|sk_ _F]nWٓHƢ q?gF79guNx{5OG)c+_ _F~-&yk>o燿T?&[Sk;9#Y$B p#ֻ' j׉iDp:H꒻*mۀN;dgQg>_W燿Tœj?"O' j>h7[Nx{5OG)c+_ _F~-&yk>#ß tM_Nqu,זpHȁC: e Oiœj?"x>m"HFn#y5IQ|*-qkk~-&yk>o燿Tœj?"O' j>h7[4/&q=֠V$خ$O(y J9?EZ׉HAi7%Srr8_ _Fϸ}n39?E<=?~-&y' j=38>LkCɷF&<"\q<=?V^'Ue41Ӭá% ̹C}$#DGpg7 s?Nx{5O]'[@M5ko4O{Yfq-"=ZqgspdMx@1JtNx{5OVO ͤia^AJTm>Anryk~-&yk>o燿Tœj?"O' j>h7[5O%ukyKH -#׮x'Z<=?Z׉ί&+EAuIX3}~6n9r6h7[Nx{5OG)c+_ _F~-&yk># tM#ou4vs\FHK"Giœj?"x}n4"8Np$xI]| m'3+k~-&yk>/^W~.YtQ~yLթ}#8]cVr[ q&X,Q}/V h7[96d4q.+o\pOэ#<1A_ ,n`?6?Q۬߫?1M_ _F~-&yOcAa:  /k?i4h+|l El}ſ?o/#V/.NH#GTWlq#>OcAcsQYVbGgYo[@M5ko4O}b} m՟QYVbDG[@M5Xe?[|H'աGӭb+R kГӒO5۬߫?1X~:#@[}#H[MRTf_-pJrlz_ _FXwCn~: ߵ' j>h7/?1-ˤN}Z 5ڑZHrHp_: k8H?o/Jw0-p3DG'1 GgY?uug)ko4O}ſ?o/#Q_ ,ccuӬ"s? $GgYõ?&.f(Pe$獯' j>>`?6?Q۬߫?1M_ _F~-&yOcAcE7ң>'ՋxSKl0r '#`?6?V׉7ҙ L45IJ-g O#}ſ?o/#Q_ ,;QYVbGgYo[@M5ko4O}b} ދ~ռOH_QuVhs%1#18#kQYVb_fM#HW꒰f%mr3m}ſ?o/#Q_ ,;QYVbGgYo[@M5ko4O}b} 0oF#VNfHBvN>VCn~a}n4"8Np$xI]| m'3+k~-&yOcAa: `?6?S~h7_ _FXwCn~b?E}Z}:ݖ(*ƿ( 89$[ko4OՋ44!u%Fe vǩXm՟QYVbDG[@M5Xeÿuug+-D'բQ]Ana'$' DX:4!Fw}]@=8'1 ۬߫?1GCn~7~-&y' j>>`?6?U=k?1Ϩ\߹ӬKr̺v1zgJDU495|okkm7uTiԯufD RZKk x4_t嶒DLsU 6lU8Q/,;}QBnO|P#_L1mkfѢW""C2WA<3O^5హ&V~DGBªCQZvx[˴.Ie= gf +|a-4:<"MpcM$GI7oU;p4tC‹y%pDIGeq5`5h^w@.j1Rԯo*nxJʇ Ą`Sjx/5j3,i7e=qƥG\)g%XEqc%7S6VI=j2I,vIFGHIu_LIQE-+̗ 0 |1.h+-K}_iQEaEP_]vKEݦ. qpڣL秨KGyƪ? 7|ןWg,KGyƫ[N.[}\vݣݩ# 1eHd'$wk#C7}?/[(7}tk%M:ɣݢca4@($޻>#]j?/[7}ޢ0/[7}ޢ8OFӭьz=۩"52CpH=k7}<"?m7}?/[(t4rN~&v w+DDgKGyƨn?)dս@?Q ~>?oQ@=t9>f D$| 0ypq ~>??j ѷu@?Q ~>?oQ@=׊t8&x6La|0823 ~>??i Ѷ@?Q ~>?oQ@>>]_nj.GRG.bO@HMmowjC^oP%ok#T_϶;5[PK}\?oQ@?V/?V ~>?V?:}4W/ncI"{A[_Q|9`o@?Q ~>?oQ@?SNou4{kXr"#8<Em_϶;5G? qaMGK&MmowjKGyƫz|SY;4he$'+d`3KGyƨW]MmowjKGyƫzSž4 ad# d䁁qKGyƨH]MmowjKGyƫzuQwm=ڒ>pXN2zF%ok#TkHkz0/[7}ޢ8xOF[ӮL=(&6Dr@k7}]Z?/[7}ޢ0/[_ xOHW$7hn0rDFpyA8 +P ~>?V ~>?T(5O3tusk-g̺<,z} u5i#`w@Pg_(eq]hߙ+aEW AEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP\?o滊ܳ5݁2wQEpQE⟲!}'Gy~[nٞ7c8jMۼ!Zy[\> ݔg$sxv=X[" ե6+'a#*̇ )ʃ>:4h|46̖;@Bfhّã3ya;Cs O&%֤%uuR^Dbc fIyvѼ=keOX"{Ŧ3.pXi82$IbB=/-쉣8پαG)BUCݹdV/ׇo|DD, 7XKżBGܥA#sn5:=I}Jf0o.eBCΟu\qrE X1|&I݆8mo]kawiznȗʷ8v8T8fTKMo^,,>c*ZJhRX?9q qmq3id xdq˦x_ojmQoaXw\*+bT-z>˨,إ9c39yQQ"A #XԼ;.=.o)^7ZX14QlU]%m4np#^Ewbc + >tW?u }a: YyMv@ ‰ٞ7$ds*1u3ZYDSj:O%MV!ڠeLc`c5X[-;kkh4 ~S'σln45-fFT5Ibie_Lo'*$." P7 !5kIK(n'C#E%YcUؿgw7aHpEWxGۚz[Ks]]|]=ָaW~l;b"Ҿ2uOO5$[W)Yڞt/pu-x1H2~=O| nިȫi)(l<F>W{8pJit}ei7NT m-e[kxW.HQz*(o.U Cn~GW?_G!_D|_M;QYVbGgY=?ɿ/a['y9JQ@cw*zV!_D|_Mc[EH@j7" w(-$䑓$6uug(0& B<%B? ' i: `?6?Qa?MA#o šuf-%bBO}Ҷ ' kE>7c'ՃfQK)00H9'm`?6?Qa?MA!xK}4/Oӿuug(m՟~/F׃07ҭÚ@xŌ[YK`$lۏm/O=֋~2&pZp#o}GgY=?ɿ/cqo");lA.A|_ME:ԏ}ZdK ٢+Pm򝰃ӂ+x~*4i'Kn7AEWAEPEPEPEPEP_׿ơ_;AF4oil[nO?15I›|Ch3H#\~^GG=sA,T/$޷/WGW?_G!_D|_M;QYVbGgY}?ɿWGW?_X߃1ᴏÚB$ڋ1KyU8==sQYVbu5 xVZ3Kb3G9cpAa?MA#c Я/&B<%BwCn~: {_o!_D|_MbFqo")Ӯ9#YFB{Cn~bE}ZdM:)" F)8=8 G_7B<%B? ' i: `?6?Qa?MA!xK}4/Oӿuug(m՟~/Fዯh%]:KjI$I'm/O?[|H'աGӭb+R kГӒO5۬߫?1G_7/OGW?_Nm՟QYVbco ~.4io3*ʪ9^GW?_Xo'd>HH$] s 9$d8` GgY=?ɿ/7 Я/&B<%BwCn~: {_õxsH0fjIrhB<%BǵoύXlj`YEk s 0I_: {_o!_D|_MxK}4GgY?uug(0& u /u!}:1ce)#n [_xK}5uߏQNa)r%T~9vuug(0& B<%B? ' i: `?6?Qa?MA#["6sHDQtV ~p?/#*m/O>ߦo]U+\~pw Bp# xm՟~/C ' h#_+/`?6?Q۬߫?1G_7?x?-9E4Zu$v1+#؂\z#_+/hx#_OL\3E$V\;aB+kQYVbco #_+/ Я/&۬߫?1GCn~GW?_X.\\xsHitwI,bfv1$$۬߫?1XoF"xVNe8J17BNNI>a?MA#c Я/&B<%BwCn~: {_o!_D|_Mb['y9JQ@cw*zVCn~b[EH@j7" w(-$䑓$co xK}4/Oӿuug(m՟~/C ' jiG}2Ӭ]cRe UGgY񊧢ZMg}f9 :u nV0y_/F8LyS*qJi Pg_(eq]5hߘŠ((((((((((((((((((((((((((((((((,wvgnk/?Be(((+Mu~'it넎8Գ;{Vby|[MpdaAzœ:yFP0Y\KkCmĈLb.Hk^ONq)i{e ɉ#m JG!I|IޱńgY[ 2,:-0o%r OқP:DDE[C#9#qY[@M5bw^':̚F51%`K 81g9MmowjuuQwm=ڒ>pXN2zFf&[@M5ko4O_϶;5G%ok#T}ſ?o/#V/.NH#GTWlq#>/[_xOF[ӮL=(&6Dr@k~-&y' j?/[7}>h7_ _F7}?/[ _x|-Iӭo&*3/%EN=Mm}ſ?o/#V/S#@r=ԑb!$յ ~>?ko4O}ſ?o/#Q ~>?P/ND #Htѿ$*kp<p[@M5b[:|D'QGq""3AV%ok#T}ſ?o/#QDG%ok#T_϶;5@^'Ue41Ӭá% ̹C}$#DX)Wu4{\0H%ok#T}ſ?o/#QDG%ok#T_϶;5@W^'Jf40ӯ %*W̶K} 9?bZN 4OK꒩w s9 m}ſ?o/#V/SNou4{kXr"#8<Em_϶;5@[@M5ko4O_϶;5G%ok#TkuMV]#H3::RP|˝7I;01_ko4OՋk=|om}YG-%9_+ |qm_϶;5@[@M5ko4O_϶;5G%ok#TuuMm#H :" RR|mķ;p0s;_ko4OՋu=oN-}^!G %_+$|$ 3_϶;5@[@M5ko4O_϶;5G%ok#Tx2iB\T7.8'h㑞@#k~-&yuuQwm=ڒ>pXN2zF%ok#T}ſ?o/#QDG%ok#T_϶;5@0;x#_[#H\ =RWe_-Bp l_ _F_xOF[ӮL=(&6Dr@k7}>h7_ _F7}?/[~-&y|uuFF$#N̾Z85 ~>?b?:}4W/ncI"{A@_ko4O}ſ?o/#Q ~>?PDX:4!Fw}]@=8_V/SNou4{kXr"#8<Em}ſ?o/#QDG%ok#T_϶;5@[@M5Teڝ!RRe%Þx ~>?T(5O3tusk-g̺<,z} P2_=wJCk/G_ (J ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ܳ5W5 C+(}ߑǑaԷaAZ]GJmNVFWlFJL1"r{:cѵ+pZ]ov2,x;AC[O#Mslo$&f-:377;棢O-By Fݒ)#`9;+_i=Oku g.̀"R6Svm;pnR~Ț>Ncr%]9.\mۜ6Ebmxv^$ODBy5[[ɀYK$(KyD}T1 ^Y\,v~wmTG=kԵ=P^}hmu D(Psȍ7z Z燼=]^%帎$ax c+Y4+^#.1Z.u3w(cp$r XOA:ZWMHu.s4A7HݤVUQWk}gO<(QwDy^WcV #^6q۵K[in&KⰗm.FhG "F&xv]oz\Ro=Dch a"/3J_:L71-E'{ҺƖ`BģyUڥtHՏs{%ԡ,[b#Fo;Z?(=CPt]fE>D+lm"&ؼ̬ت(f@ !ي=N{i$+8LZ|U(OKP[%G(EWxGۚzu`Os\_;r&`j?j.x]uEWיQ@OM_-tWÚqu1E$`T$ks Я/&<\GdoV! QB<%Bስ ~&]%U_\6ʩYGW?_G!_D|_MoVASQGW?_X0-|[sHh뇎HbVFQX?xK}4/OGW?_G!_D|_Mß K[?Fqq"YӭI$ƤJ{!_D|_MoQX?xK}4/OSQɫz x?D?4\j7bbnU@v #_+/ + Я/&B<%B-?~cnz{_ao۷4 in4!p =J#_+/ + Я/&B<%B.~?}m+z{a|o[4 ׎,IpH =Z#_+/ + Я/&B<%B5 x[w5\> Em#搉6*@:~^FUNpj#_+/ + Я/&B<%B(-|#\[sHh'x%daA A_3WyQ_FpQ@Q@Q@Q@Q@|_Kh?o;:?{o\j.Oh3M68ϛ 6vfS_P}EitV!_D|_MxK}5X:-aG;? ' k["6sHDQtV ~p?/#*wV!_D|_MxK}4X>7 MG!_D|_MbFqo")Ӯ9#YFB{qE`/OGW?_@V!_D|_MxK}4x#D.E-oWbLJ4fNy$&gcI+I[_xK}4E`/OGW?_@? qaMGK&- \i%qߠgUTr+k Я/&7B<%B? ' hW]|n )YFlbڬ\ m$*=+k Я/&7B<%B? ' hH]n/^;,b̲[$m 3`}kk Я/&7B<%B? ' h5o ?p߃1ᴏÚB$ڋ1KyU8==k Я/&7B<%B? ' h>#]jޮů5iMpJ6  ޶ ' hz#_+/ Я/&7 sv)h#_+/_?^..<9K4u$13;ԒI\Oz(B<%B? ' hz|! [ j?Y5xK}5obH+F=LBܪ@GW?_G!_D|_MoV ڿmxK}5SCҴ#;ake i.Bo2dgs(į?f븮W3YWuZ?JQEPQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEW5,w`LQ\%Q@d֭<- dqn3{ ֬_A5ׂ5{xYӮ8Rc`I@i[jOWYlX%L&0谖`T'zϛ'i mֶZm4Z_Je'Zc2g#"NT$)֛Κ6Vւ[X;n$EZ`vAw0rEK_Z|tKN<(h世LIo(RR<*KLn5:=I}Jf0o.eBCΟu\qrE X1|&I݆8mOy2xN[-Wx%淑R0$[$6ݼ \{Z/ˋV2 if\ H;#hy+K P@h'@+[; W^#EԵ}L5, 1U7جJ03Euëy7{ȉU."OR@ܓ}d/xToF I[Aaed1o 67-Xljw y.m'!le̒AmRFYOU&gWY6Q * 'fx5a`IJ^],xwwA:!R}^;VL!I/XgEH hh KPm/umkGSoJ h!2X/7bU mп_3̞V>-/V![Iğ+j7k=86ܗWg ˼~pyY KH1{v00[Ƕww9ךm Qm)K}9ʮ>mþ@zy4Km[9ҝKCO]_~eQ_C.Y(WYTQ.dt|Q@\lU-bheO7#\ \;Z^2JQ K_X=/_qV"Z^2JWE)*/>UXbOv\V?ix?+\};Z^2JQ K_X=/_qYE]- 1g;;W3NJ K_X=/_qV"Z^2JWE)*>"=Bkqs4Q#.Q ێ K_X=/_qV"Z^2JWE)*"MBss RC.ێLi{ K_X=/_qV"Z^2JWE)*/mWt?gmrß_X/ ?EqP—?ix?+G-/g%a+}Q}zscw142d.GeA>RUM_\Yemxa2#ڽ'oW~-&yA5JM\,=/w]Cn<9wڿ4NSY,ḑc02'ֽ ~-&y|uuFF$#N̾Z853~3/2FgoWh7_ _FS?}^( u|q=榮V$+#Sp>ҽ ~-&y|-uu'iC%IT\{pLgaz_ej?7G( uſ?o/#QDG3~0/9Q_G2c^j~M0=ŤyՁ1D= ~-&ymnO ˤicYCJ/s>I'vF099WW( tj?7][@M5ko4Ojc?S> {K//5FI<,Xg8ȭI›|Ch3H#\~^GG=s+5e46s =RWe]$)vϨ]+xW۠!ߩHͦYw'ygR5Gwn>hcN1*u: `?6?S~h7_ _F/?qwCn~bz-jV>!}EYgp gr0nj' jNu 4!\j.b Jqcq s}b} : `?6?S~h7_ _FXwCn~bE}ZdM:)" F)8=8 [ko4OՋ 54:u%vU$)vϨXm՟QYVbDG[@M5Xeÿuug(m՟ſ?o/#QDG'1 _h'hQvXԪt$tMm`?6?VNH$i7T\܀q'h7/?۬߫?1GCn~7~-&y' j>> hxV FvV.2{d0Cn~aZN 4OK꒩w s9 m}ſ?o/#Q_ ,;QYVbGgYo[@M5ko4O}b} ->t62w02\OqF?1I'vF099k~-&yOcAa: `?6?S~h7_ _FXǺoǍωb^0k@*?sr F;_: uuMm#H :" RR|mķ;p0s;_ko4O}b} m՟QYVbDG[@M5Xe>ߦo]U+\~pw Bp# xm՟=nWٓHƢ q?gF79_ko4O}b} m՟QYVbDG[@M5Xe?4['զDӮ+Pm򝰃ӂ۬߫?1X~0;x#_[#H\ =RWe_-Bp l_ _FXwCn~: ߵ' j>h7/?۬߫?1XoF"xVNe8J17BNNI>[@M5b>m"HFn#y5IQ|*-qh/?7?uug(m՟ſ?o/#QDG'1 GgY.;'hojEjA"P[I#'Il}ſ?o/#V/ND #Htѿ$*kp<p_ ,n`?6?Q۬߫?1M_ _F~-&yOcAa: ODs7t&ܬa._ݢqҭ}ſ?o/#UMMF_-Mb-Zu+]rY99牕iIY% CW3YW+ez+{QEPQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEW5,w`LQ\%Q@by|[MpdaAzڬ}ߑǑaG-u#\IcpY{iY^{Q/,;}QBnO|P#gܾ/o ,-8c%6WƤ`ۭkH!J#)\*{2yH.*ehDžMR[ XtSܟ-VvhЯRoD &Rڌ3Kpee2e ߘΟxQo5Ws5(̮7ƬFm h-֓^M>d׶P8'KsHX_1]gSV]N =>H-c"X%qa#@p.5Gס/5(y%W1ֹƊaFX_1>{\]]w,w Fp0(z(?].FףלxGۚgۗ3nFEo["(FQ0TTAKR53J:=ILkKT_QO@5to iVAg?|__Գ~0/[_OFӭьz=۩"52CpH=ȃ襤y ~>?V ~>?b[:|D'QGq""3AWqX>-5,KGyƨMmowj KGyƫ:zUN@v["KrV@ <88+G_v?6KGyƨMmowj;^)<1@y,E2hh#hQHb#P5Ch3D4fӕgz\Ny|S"^趬B rgYߑ0?k_϶;5G%ok#UEjp?V.>]_nj.GRG.bO@H+_ׅ(Gs@%ok#T_϶;5[P%ok#UK}\?-aG;ޠ KGyƨMmowj)<y4S&v ,ܐzMmowjȃ?覭MmowjKGyƫzMmowj|>oNF1np = |"?mKGyƨMmowj KGyƫ)'G:=ی5ܬ9GPr"n?)d_϶;5G%ok#UE`_϶;5U4=J S".b\n. "#_C]M`.F/į?f븮W3YWuZ?JQEPQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEW5,w`LQ\%Q@dk_o |y_۶g3kZ|SkkV|}y8Fse=b\F@HGwuiw "JIHʀ!B ro΍o$1oZiCi})7qi˱,N$9RXOgKKE"h:6oQPv8qnpxab jfs|:x\H.nldy=E* r7a9@Z燼=]^%帎$ax c+OZד#O;[j;:41hl$Wl!V![a.vr6'ѷ\&t P,-m("tcEfr0 l]ke'+ZKF9 *C1 $. K= m2X.mDo-9HW{8pJit}ei7NT zm^.HQz*(ѿ??߻5\5^D'r!p۰GʣRV=ҝz҂ݼƴ/Vm譯"c+?t}^f-o1d[~_xŢ-/?/}yVEQo1e;/>3ȷ ?-/?Geb[_ߗGEQZ+k"c(ȷ ?WEmd[~_ߗG^#1h"c(v_x}^f-o1d[~_xŢ-/?/}yVEQo1e;/>3ȷ ?-/?Geb[_ߗGEQZ+k"c(ȷ ?WEmd[~_ߗG^#1h"c(v_x}^f-o1d[~_xŢ-/?/}y!_D|_M|o1OCn~a[:VJgP俟7 Я/&| ]x#@,$XcRI%rI=sQYVb|D> >nqZ@c_n}I}?ɿ/l/OGW?_Nm՟QYVbco #_+/_ x?D?4\j7bbnU@v m՟_ hxV FvV.2{d0a?MA#c Я/&B<%BwCn~: {_o!_D|_Mb? }Vݼ9SNtŵY @ۀHUzVCn~bcuӬ"s? $~/F!_D|_MxK}4GgY?uug(0& /bſ4gHbVFH ]<+h:\hfmq6@m0|>2%IWvN>tm&K m"lKOsy{'>o>uv/OGW?_Nm՟QYVb0&#C ' k["6sHDQtV ~p?/#*n`?6?V.ߦo]U+\~pw Bp# x=?ɿ/l/OGW?_Nm՟QYVbco #_+/_x?-9E4Zu$v1+#؂\zm՟_hx#_OL\3E$V\;aB(0& GW?_G!_D|_M;QYVbGgY=?ɿ/7 Я/&B<%BwCn~: {_1uÚDK[Ic3I$$B<%B~/4 (u,QjU~Qp:rI&uug(0& B<%B? ' i: `?6?Qa?MA#Ƒ;"W{[UG+ ' k-D'բQ]Ana'$' ۬߫?1G_7/OGW?_Nm՟QYVbco vxUoiӬ#61mVi.C6r{GW?_X->t62w02\OqF?1s~Na-2(O=*eN)]M?As?W3YW+ez+{QEPQEQEQEQEQEQEQEQEQEQEQEѨBz5Pj>ОE}='QEhOF Q@ѨBz5Pj>ОE}='QEhOF Q@ѨBz5Pj>ОE}='QEhOF Q@ѨBz5Pj>ОE}='QEhOF Q@ѨBz5Pj>ОE}='QEhOF Q@ѫȸ  >go1{QEtnbgGj>ОE0Bz5hOF>ОY#}_nUf6 TdQEshZyFla9ۉo-0m 9"K-> UĥYi4r\K&$))\%&_zP^5VڻK;+4[,p]*Im Pڝ>sv^ LHv炊޺o&r"UxKSԁ7$A#Asckzԁl쭵 c4lJQ38EGa_[[[XhmKkJ#IHb$*0bT(mib%%h?匰V&@sh*A5潅l!gP{I P&8˦?xr L٧YYͥ6ic!3 .^b2>#\ (w }=(Bz5rzƁwxmN)!X^ p6yX$^W62*t%mta$Y5he;PӥIB\_, "qˏ֍_څ}g֢Ky"B4Q^:ҼtzKK_*-2s+ۉ4"$|l1;o=EYng㣌R\.Zk?a?6(^ i͇&VhZVhi͇&(^ i͇&VhZVhi͇&(^ i͇&VhZVhi͇&(^ i͇&VhZVhi͇&(^ i͇&VhZVhi͇&(^ i͇&VhZVhi͇&(^ i͇&VhZVhi͇&(^ i͇&VhZVhi͇&(^ i͇&VhZVhi͇&(^ i͇&O_ѿo/#QEyYϗ71:Ͽ~*yhgZ6:4bՕwP3&+<_FO_ѿo/#QE~*yiZU:4.7jʸfyqg3J( ~*y?FEiz7 j TZojm+g?g>n16O_ѿo/#Q7*jsxSү44x5YX+}gT,Qcuʺn"t:eaSӟ׎J*-V_~*y?FEg0iz7 j_^W:2}6j|,X~?<|7WoQ@_FVjZ_NmeemԮqqg8Ȣ7WoQ@_FO_ѿo/#QEPϊmMtik*ءsgM_WoQ@_FO_ѿo/#QEP)-tiw\\nՕq=M|gO_ѿo/#QE~*y?FEB⨵S'F?i՗V~|cm/iz7 j(O_ѿo/#Q7(L|U.gdֳ_ڲcDǕcݱ?FEiz7 j?U@=5P @JѓMqVSfb{9;_WoQ@_FO_ѿo/#QEPRth~k-+lޥs9E_WoQ@_FO_ѿo/#QE~*yhgZ6:4bՕwP3&(_FO_ѿo/#QE~*yiZU:4.7jʸfyqg3J( ~*y?FEiz7 j4xOCT-[{x⵹iմI-c>hh!vqa}Ob_48d.thOF&O1-}='QEs >ОGѨ'Qj( }=(Bz5hOF>ОGѨ'Qj( }=(Bz5hOF>ОGѨ'Qj( }=(./oar-2.5.2/docs/schemas/capture_drawgantt.jpg0000644000175000017500000044114011757171206017360 0ustar plbplbJFIFC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222E" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( Ŕ拥ybŚzix< >EFOW.Vs9]&H#r㪑@63ZqZ57)TTj׍.ǁ[_hv*-pTW"%fr `xQϥW_^'^Mos- rD硠Z4ZM`Ko,H R}ь\W<1\jjn+[\I E pAh'Cx@5mAQKHّ|hмEtkԻV@@Q֥x?]څ͔3]N!Aqܑ(d~N2sTCkY{jvq:~F|RFߦ@=֊+uo/EŪC?3||g+6^jڍ VgvTR} gں:/;Do+'}^-w63}zD5=29KTT.@uW?tX:/[صYO4p{B$մ-GT=2K[;;QdYC  Exg~#χ<_Pkk9F:10G9|Q/?}`kw&[x#;\UC̀źu4jl::\j&eʦrG܁Qyi}ŭim=춎if@NX+gÑGן}O'w&q+P:5[u$1HB9}TT:i쯒#4>/+z`ޭ61$Ǘ/DLk<_c4ͥ Tbv83pzPQ^kOc߈|ߝFnm%4r";NU?};Vώu[I&j:\4Rڋ$2DvXLjyᵷX$/$0UE$xs^!⟈%3`."7@<ѻA@s;i^$O x/%7F{d% yga>YQ"N o;8,.໵;&A"6  88 ¬W>mLyQ=e!Ո1QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEW1㳺J5}5og{ⷒ7C#,o'洿Vikj0L}%ӗ囩f0y{|2&m[SW-Lzl'9U,HTvA]UhN[->w%-/hp9r3k횕73ԪƊ3rzz (N6Z/x9s@Lv&9FI~{kGZֵW75ޒJD`]><h zޕH]2̬?*uF\ԾZ s]Ŕ) cJ>F|Ϡ@CoO4 V@O^TQ@sw> mn}gN5.OՐ;sZ(Zt~)5alB5E6;u½"Nt OW={(i*Jc8q]w_kXZmcY:]B o+$G9S_m|O֪Z>ֵx|TX,J.G簢9w~=??kլ ͣ&vC)3vjF촘$U݉e*K?x]}1iL<3\Jgy\;@UF \%D$:<>ė>E͹eoѾz8&_JRLѩOV;) $:[Bk/ X˘UPOֵ*ByɌe'h|No9ZIRO|IYɣdZLۖP;׮3k*8$ʝ%cYrI^c|U/fNI\Xu d1kR)ǚn̘Rv=>F \%D$:<>ė>E͹eoѾz8&_JRLѩOV;) $:[Bk/ X˘UPOֵ*ByɌe'h|No9ZIRO|IYɣdZLۖP;׮3k*8$ʝ%c+((((((((((((((((((((((((((((((((((((((((((((((((((( kO+ϟ(&8"B ,@4(ZfaE$7_)PGe`NV"O`(U{}J ID"pܤdy'CmFQ@hVK@wܽYiFY33`u=֦ +n?? ]uffۮ7M[2;7C +n9e3u.5^ı]"VEP&yyw$:jDyA pc8.wl٢cѷ]o.6hBu C}c@3tvwl٢-5;[-F8I1Kv:?]w/zZtqeLLO|^ۮ7Fwl٢̅#iu,Oٛ +/NbXw +"*5;˹!R%& Ǯhjۮ7Fwl٢cѷ]o.6hG:k?ٺ~]o.6h7Ե 5kR@iaR?r <{9ZWvs1fB:Vc w.卷+c@(((d/.MH8c($ g +n?? ]uff‚]^+}OM Plɟq@TV} ńR292:85{] 4Ecmn?? ]lXufۮ7@4V+6fyLw~UjVKșhBÊW_:manPL܏RsOۮ7@4V6wlu{ش wi6yPb)9諙9's@ ]o.3tEaA.uož9#AK-`uB 8s9?ڭt Yͯ\43HV2܂КEVXX±E^a|UbW`nn-Wg>Ug^|*F$>ھw4v{%(/xwl,8rc=_/1P>}ã#U&"H+<]%]^I ,'*5V(⹨4։5M9Ti;?nS¶V.NLWRF18#BEt=6,PBQ63hk\Xeke+f>ר>!|NBI$e|L!QJRkK$IO}tב* O_E|3#k8([!O\dzүіhFr75x#^ ҵIs@bmkൔ? teiDF0#]}iQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@q?_xYfզ 'dq{&jƸ=j. }X e0uH5 H-,bY yF$c*YPqW5]oC߉5'`҈x"\x\=+úL1J) =j]_Ht4F&BȲ4d`)`z I}Ъ]cMdcsA=f<%MݤBD$4ҺmkEZ%-D6pr[fs&B9=jM}"䱜 b˱.Vk:fL!(6pJ,lOִ>^~ou {/ ۚ..uj(?ّUJs֟v.oXin"G*p:1X$wśT~~Ug!5 J念勔;Si;VM[យWSyg2, >4$a,A 0P8Nr9c@Al l$X#L}mYAR9'=q1ӥ C1#QUid_e mM_vcd+xh.o6iKhZ\eVV\>ێMch:Wx}ȿkL.)f*}ºC 6V麌{2$FoLxT1%7F5$r8j3*sٗiefǖG5'M]zTM=֢#ա%b 'mZ0ȟnc3g%c/Iw4{KėvZ٤5}HcVl. $('tNN}%wM7ԕcRh?Ihţaޗ0oLSѦ1X$wśT~~U:n򕅜.$i0=} JV6s9dPOǨ v94oMv𽝭Ǘ5̒;rv! +Σ+jgwstd0c|xSR9 3vԚz>}VVF1 ci6 .xcxSMKYY#+_3Z3L8 urqZip*:"8`AFfUR !prOQ*B5Oj֡ ܙ'  m_$1N º}.:+ۣ%ᐪQm%#&[As4Q#r\q?a@˨ګXwYiA;:w?Hq,c gwy⏲nkz>;Cc|r nϡF-2$<* $6_V=ľU1ejn\j~oSm4 ak8>&KoF5tПd% +y9>5t0 &@9?A O@-t/{<"*N98P ݤl,I>FotdmiFSZ!,yFFᔂ9,Z?Oݵ'Fٌbީ.ltl5^Y亖fPAd< t ^s,.CaYCobFw@<$V wqhq"Fq߿q_pN1@z͟m6v\\% g_&5GyCu%4FYga'(|kƉfHeHAbێ l-^95nEWY1ԭxau]VPvHzA@jz7I݂no3I,Yۜ&M j_NeR+xM3;@UTXNx7iqO 6/c4&U%%UYI8 d.+^_FIɮcDFm AXp+όs~ YQT72NȦ#t[3ޝRPZ%vfuT]6l285>xoFQ6}VYÜ(VIn(݌c8v&kۛvǺ;d=Q0ܿGrgnkuqZ>F+9s;-"/o4ؗ;w $ nxF8bkJMEk GklzhSq(Sj4jbƗNn jeIb: YnŤ"c\iHBh$3&_GL_GL_gxòeE$ba8>ׯO#2Ms8+Cq+TӷקWo$D=>cб#H\3vHN? {}z{tO:#S=*[;ǸX(:A~~ c˿+k:C>ǂ?Z}wRO{_$0ѤkX0>1NsUdz,omKht*B#9UXN8[M 6WMlݒU1:q56wLܓ yCn\wA218];3Mku /el`R]waq. ˰+u;p3À?ZԼ)k$ʼn4dӧ$WjڧƓi-nW";Rܨ%awJ3N Vy$t+]B٭mSFO+-DkJ;v3["M$D>E߻@9=[v ~I\H6܏>St^Ɛg 8>~ЪPP0XPiR=~ekds*fHGEwA*=Lz+n5mSWcIu+کhnT};}-P]Zm{m 0~[p1娈ZT|wXd#h#c=[v ~I\H6܏>St^Ɛg 8>~ЪPP0XPiQ=ku139i'{xDdt_tBtǡ"6V<%v4[IkrݪG+Ӿ:Wݕl׶z)' KZUI'02Q!;վ'j[gᔐ5isu=H| {>/ i &r1] E χ(З:՚TNx*"/=1H յO ]'ZܮDwjQJAwek[5707ފhétbp1娌V'x!oq!dAq}8+UT`8R>vku12[gk k8`*-WsQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Zo_ʝE7ʏQGP|T_ʝE7A S#4-?:oʏSQq*pgnt>P( (OzSH6afUz֢(գKsi@%s\rxP( (jtZZd2զQ+GQ O@TfxV-T:eU 1=hJ*O3u Jə#mڇюw/II%DydJv[Xv (i1Z>d6!k `]9U'E_*9KR(bRI#TP2I'@QU.]:ko*4͐0QS<7^ RT Y0 U؂(J( *ZgP}/w) *G*;%U`J6> IEPEP4 AwyY[Z4) q`G_T:&>6o26ex8Ϫ( *1<-p*U0rU{>R sIT9|;gY0aPr~V4~( %JgVtʤ u \ۇfx.A+ n~f82`;Q@ĒJ6ȕ± U\c}Rnm"3\BrOEPEG I@U ]sI濴,,Ep{FNsP*1<-p*U0rU{>%QU#tu9t^-̦T^9)/$wnei:I-euQG ] cjQU$t8/ReLW^y v>n-PRIm*:owS}EO@Q@A&;G,#5-pK* ȫQEQU$t8/ReLW^y v><uoż,2x䍃+w ((..%HK$Q@$ *G*;%U`J6> qPg_(eq]hߙ+aEW AEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP\?o滊ܳ5݁2wQEpQEQEQEQEQEQEQEQEQEQEq4[U.l.G՚btUFpCEqaEjڨ]b ;H`6|c]QE++kK;:kH$Rc2|s3-@Lq%/r$M%( p6 VP\>^;f:H$EyP̬Z8$pk1|? ͬmY˼b0D9,i9$ (_5;TQdmRoVb#ѴGOVuA."rTvC@.#a!m!k #5gb)%gR0Yerj7~)ӞhI-/#CrF DPK PWhXjjZWm؄Gh<9 1w8!|ByEsyWϿۃ>}Njz(4m2 oqkֶnusvvW6rIr"*VB$Ep vkm].mbui'kpo3crs͌zuQEj[}gE}ib+{\F$O|P łhS;BFWm v~WWR׵3^'DQa-otTe Nݟ*R;(y,.DZNI̛dϰF@]o3,K8P=xSj^~H-VHI\dmd*XwQ“ȱO-1n浨+k6qa{]A3Zl̷#F*!E ǒ[%j!uPk6W-5KhmB/]|$];~\ ~Yu{9MZky`$rYǘrI+@TPY>%4)mX /\ɨqddA+X,a.}I\Wv SLPGJm$Jea:~J᷊|=L״[y:W#*NGƴ4?[Y *t3R*t k1[DJeڊ '|So/4OEV/Mgo3s{QKY~G-go&D_UKT{hϵ[wBlclt+t}ZwA"*IJGP{oM%CFOZLlKiE RFpGSVG+CTmfv=>zTQu4TX_<'g ߨ?qx{|O^#E<'g ߨ?qx{|O^#E<'g ߨ?qx{|O^#E<'g ߨ?qx{|O^,<I dLpT3 l h)=TQt?.!o~uG\uS'zOS.ID^7~qGs3o|?G-Ϟ~($.fz7w:e~ë[ϱ5#j$c$R+]GCbEI4iZrP1ϟ xm|K\JC/$0UE ${׌<07ҮzA4y[UKblOkG^ GG-g?gKTo/4OEWW_7VХ??U[_?.[_?.^<'gs?nߨ?nߨz(vxs?nߨ?nߨz(vxs?nߨ?nߨz(vxs?nߨ?nߨz(vxs?nߨ?nߨz(vxs?nߨ?nߨz(vxs?nߨ?nߨz(vxs?nߨ?nߨz(vxs?nߨ?nߨz(vxs?nߨ?nߨz(vxs?nߨ?nߨz(vxs?nߨ?nߨz(vxs?nߨ?nߨz(vxs?nߨ?nߨz(vxs?nߨ?nߨz(vxs?nߨ?nߨz(vxs?nߨ?nߨz(vxs?nߨnjy7QypzW Sן|6ԦTd:O_(eq\?į?f븯&{)|L(((((((((((((((((((((((((((((((+rq\?o滰?&](((i|_ 'D):LAkr0H;>Zcu;xmM`x3K-ՇyyO5%ءñmnb F[iV[_{֕eQWtHJf=Kih<_j:ݝ{JS*aA^d?gܠy!p Fj$$ $įI8* ےK7fkk c<2hFf&@EQ0g_[lmm^wy7@Πf1 H9j;9'[|aXSAWv2[\}E %`5hHa%Io~`@cVKS[4wBm&$)e`ͅ'JۑXeW[eo(ﮤp - īb.g/;hOtXYK饶t'@wUO8Kݝֳuh3H$SB]1@wF;妩i^hKGBŭ̐8ˁ$ |B˩ׯmx1a"qFd7nW-fw(cPz|OO@Q@ylj~]G/W>{٥ɸ.u=;`m-6Wv`/fN![Iğ+j7k;SѭVGQ-rJx;yj!4Oֵ+[X0Ȉ'*GY4pf4Ԩ(GM|o"sa2k/Q[oq*9/$eu#N#/KU>C2k/G&^(X>.WG֟2JLQC&UR]o77R=ĆF-O}V]Zzt5/Fz(?vVGQu=}Z}k7h/Fz(סnX_7_GGQiAC?׸G6qJ$v?ʫg>bnzg;Fz(0N\R?ЊHUD!XfAK)#r2 s5u=h_tUpa?X" yFcw(cj ;j252A#є½s@v{@E7ڦT%\p)ğ/cm^LicJv ޢv}(uSiˈh}? EcyW|[J]Xi#6ho>_ѣϗzhKEcyWu~K;e4u+w0CNc'/0A o3GA:prlq&6jzFYA+egpK*@w϶= Y|9mCᵎ/ynZ#`b~L\톥sq d,iKJ0Fdf''B\t1N.)mslffxgrBA! .9N,z|M!h| {=4}[Aۢ/Y$E[i#Y!;w1|%X7-K2CqלcjM;xekH$Q9&W&$C!d*_ߥmW-V-iv<N 5JkGje2!H#izGONaK$s]E*_dr8u_:uim%%Nkt%we9TMwɰr{}?JI <&$לP~Qɯ[xK?3/G+o '&erHvg/oq1eX"b@S yIinд Gʂ3A; +2Uo? IJŴCai4iüwg~^g g_@¶f_*VOLRL9Y^D0W].FW%h:D }>ow2۳*x֭׆lTVuu{[2r9ƘxAJJmWQ]5o~gmEa'γ;G"zw?:Cܿ#Sru x[w4Duyk[̈́Zn5b%]vt\7ɕ9rg/H5;j+=;~u^?ӿY?AY^&SXP'γ;Y ŅIV/M^Ԑ!{A(Ə]$'s(((((((((((((((((+Ҿ O_Bֽ+C\m? ΓW3YW+ez+k3E0+(((((((((((((((((((((((((((((k?s]p]Yۚ?Йt;(J ( ( (88YwaAmv֯hn,#IEpJ6VpPL}U,m"6F0YvʁFI|9A}}y/[W[WKAњ5dGf} 4YIt,1][o4<8lPG;AeBT7)F4׳_wy??]0tݎ55oFy>ܺFo*]sr_@F ~lVg< 16aPChKxaVr1Tܸ=oHԵnhHnVT J6$SGVMY.YK""7FbC4"QQXizk3$:L]ǒ^}F؛U?Q.gK9Jm!gg14UlLBq@6.^>ueCK3nυ>tv1;P)]3lEQEWxGۚz\m]|m]}Ҹ nQVK"-LVvх7[ku9POK#zUu$l9SnN|o/[u{?62t?/?;T<94^\-"d*Iǡ%~{`S"Ru{m_rtTQE}IQEr4 #! TpNN#1O5]/$gV%Uf#2C`9ij2ӠKw+d C/8'py}zuouoik{$# N |_ fqi5m7+ @¥fPp\q^mQy4PxWi8[зf '>^3'mu]sة_u!=.Tf_-|zN/u(K$P;f'c_2VJnwɧI(QEWeYMIqbC.T`?w$gda&M;Y00oO\ӭa&T|sgWwz̒Fh4&2Lylܣ9Q%IU~\Y*$bKcc,ԓ9׈|/FաGZY%*ى'?űGMJ/mI$X<]Br /WjKD\<'"Qu-Kvd̳HgH&gl(0(G&_-lG`X2PƠEp\_G-gRZ?5&$1ߏ7vslY;W us<67PfU$˦vaFyݝz+H&WSDC`8*O_z~tO QI D@Q 5rғFQ^I]`vy͒"K#<І' ֧5/FS{6c r#1y6me{Xy###ֺ/lt&Bng!F^_쪸FEx?NӯVzmIlt֟iwDKqJIſ?Q/$-7Uu} L42tJ.vh Goj?)k?-~>͏xz' %> (iڏ Zoj?)k?y_.s1 `}Tγn"#'͆l۔s'U_o+jAׅir [n~ђIۃxZ3úۺ[1"Ƕp=HZ!Ƭtmw02[eﱁH?0<P؇G͏xz' %> (iڏ Zoj?)k?WuG'7_I]nWؓW΢"\qh;7 rA;ڏ ZWme5eV2\ yžsOϺasc-މ&xz' %wBlڏ Z}^} '7_I~Q5}"HEDKa nqkO{QKY~A_R7ƭ =-TvN^>ƴVGQE~`QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE_'/iO^k^_B~!6zQp>%̡c5]pPgf{4_ (J ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ܳ5W5 C+(((W+}}Z{]>[WVG粐M , )?|FT [HO0jSEt&.w@bF|L(!G Yځ]MƝBwQE((~ZX?AoW0̑a=XMzt}3=w xe8##$ksº%r#-B [{ڭ pצ #~'tYj)&i"x&y!D]O&%Zv}*譵V#e`#n`oX+|Oȡׄ-T7s-b$פsFd,m*W;?;~ r֞:u;զYve.cIvI w,@\b4 mn']W,4oM̪rd0N,!0kݙm̐#+71Nʁs_fsiHok4$Z2y#f;A,͝ (ߙX5{hHpIpF}*YKi㙒 x(܅P3(vjl;n:6iӿ?~vi5;[(Z͑dcR gkӿ?>s^ V=X~Go/4OEQ h?a[Wh`o/4OEV-<07nz@4I [YK$\ۇwi#`w@&D_UKUEy|Sk]Ǒ/yq`#$*׌<07ҮzA4y[UKblO[_٪Gme_#{M%CF?7 ' "z07 ' "[%6HtQw"~p?7,'wk# Ѣ/*M%CFޢ07 ' ";_.7[Him%H&gblOj+*k?kZŏ(p(((((((((((((((((J/!?O zZqЧ<t~ntPg_(eq_3_֏/Q\%Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@p]Yۚ+rv^ˡQEPQEQEQEͥx3λGVvmHoQoI2YTF3S\Hm!:-p/;42f;=5h(rHYwaAmv֯hn,#IEpJ6VpPL}U,m"6F0YvʁF ּ!fc\lIϑR 170 e ֿgO8ݻvfߛݷnߛvݼxSig~/f@Ɗ((V1^5fk&Hqc ^^qink0:xiJ uGf*XL=ouaNo'DQ:I / ,V d! cWu՝am$q]G&T2ǽR:ިehŤlI@>iUԔ%:mg(E)˱g=\uCÖ\,m+%ibqI7wZw?-hc m1DSYn='ȧ/m%} j*Lȓ `ǵyg}:W]> _SE,ۙv̊B`: uսվ?3LrJ8% {W48Mm40(.e-O,-#Lx'ujN!{A% `ܫ+Z|#5ƯIȑr2m;զEɿSI"*$:m~ҝe!Y#Bl zU+uI!e/vR|tQT>=N|88|H|{٣b&~m1fFV*7ќp8xzA}&1!ϗ4L0VY'mفzMQ[b_ZZu۹dybfٰclQ~RjHehJjsCLN] D:b~-e͞]F+2K}@И2yrF%W}qg[,E#4dRO@`yiRrz>l88tui=m>#"ymMfVHbOCp7<=e 4oϗ_o88+J ]Kf]3,,1ə +/L*{7͓nی1rITyD$t*ǭIU_CKc>8wx3]: O;ܢ-86R@ޣFHqWnj5?QӮukm7س4Wz]99x7'gfzwMONgWvDuyidoOK4}ſ?Q/$םiʻONgWvm|3`7`7Ӭɒ>QN8Nv> (kꪺ&uw:\J;@_dwd#OWw]DuyhONgWvOoDDG<[AMZaʻߌ|=eiϩns&]v5r6 }*ql}::l-y9bmBk4sۈ6HIa3F nJھf:uG\Bہ_dvc1&'ugv!/3s=;~u^?ӿYſ?Q/$OoDD[}j󿽜w n䃞0OWw]DuyhONgWvOoDDG<[AMZaʻONgWvA  %Ƭ^;I]Dө! ZB W (k;_:oTk_Hi)#Fer|zҎ&]XQQE~`QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE_'i5J/!?O zQp>%̡c5]pPgf{4_ (J ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ܳ5W5 C+(((WÚגz ջut\]o1cPHygpnmm#EDO1=ռIo,y_,e@%|mrlէ{dx^{)Т۠X]$i eK2)t/Se4WBi1Jt+thwɱbpx#`k65~?˴o/c@mˊ׿ƣwZ^y>Kx"EfVX6eeRK`A_6bZO}=ﴷ]`dbvbsp߈mcV~"N4%͵^4o# r͕Q~UeVϖs2 ˀCc$./.!^_ri{XXIRHK2 F}ጭM ;=&F얤<:~mo '%F7&KJݎ(8Š(kVr-ɋoF-1Ÿl 5dޱaFe2G^g!W-xnDvZܔ+gmb~1̦>g٘ۙ3GArm%7msƧVEe @%ArOEgPl|#0*茻$U $ݹưo Et0L<08ujS"%5I_Š((((((((F/w=nNs0y߼usJ_G-gxSK5-Fp 3x5KU^c;8'~%{_u|#FաGZY%*118xG<1ujzDKa:Gw3;lOj0RRق (QE+{TnIe*| ]‹.7Sꚅow;Ɔ)K( @W~ݏUݩ;5;h>g\^ۥNxHٻpf8c[/¾?Ѽ'XAy$Y -疑 >`AlIm{P֭S6չp5<)av*CGUR/oj]M?[e-"1u;䁕 ltx)3yǧA))krJ7p$NpF *m)̪xGyI;XHt3 +nYi S0=~i-̦r)BGLCӠ5EIfvʆ5 IXNz$dcyye'ؓǧjp&cͩB2ONWl%F>Osy Ӯoٲ0o_cy^}_mD&bBҰ <$qmog7W ī-#cH#8VvozХ??GBlgi|/,x>]ܧ8!  Ѣ/*}?ɿ̫yR[]jxUxcV.u .p3 h?aX0UoӬ$7mfY.Kw`r;n{hϵ[R{QKY~ h?aG&D_U9׳`yf͒7al=E\֯ύ5`^(k,?} rAc_`}^Ưo/4OEVv ]xoT,JL$եMyEW&EPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP^_B~!6zZqЧ<t~ntPg_(eq_3_֏/Q\%Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@p]Yۚ+rv^ˡQEPQEQEQEA{y̳i ycװV/qy{m%ݭ FW(dV?1O9`0M啮%\\ᜈxI - I {oKxQ2QMoyCMFVhRwo]m!n/'̸\b-1lF@$"Oom_}K:ݳWJ 5]nWҦj0{e{ mTG栓Y%t`YmGW]Pv Jb|ZCXprzO({iY93/r(8B(_׏֖z|i b\m,$pp03Ԓy 1 (7 5o EtIPzv3:ˋW2h{bt~Us*QV/GFJ*ؿW}ÐE[ؿr({bt~T}]Bobt~Tl_ʏT_ʍQw9 UQ?*>!R?*6/GGp=? FE_CWǶ;O]Y˰o!eH qZ?-[d⫖_)prϥ_"N`elA$ڤȥ7A<+#%I4ٓOCh`Q[&/Ndu;vs0*Ն}22[ѱ\ ~U(Jpq9G.t=WL/ӧxZeoL䏕XE]lm}z^.kyMBX%I'vIm7<q~Q*ͽ%709=Xp'@08]'BQTtkUgkDLpI?(p <:1*p}f̂X F(C`:d|EARкUC.rH1s֪͌>î#N_ڊONgWvDuyjh3$rjnV! Q?:?b~W-ƬDnK2 r0qАNZmEa'γ;G"zw?:rg/H57++*k?jDuyk;ީ2\j㴕I:U b5x5Q_Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@zW Sךץ|덧||JCk/%̡c5]|Z?h&QEpQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEvgnkk?s]y.qEW AEPEPY>!t(˺Ϸ6޿620ʍZ|]cshdPI*#Yt[xBw6>mx/SV-7Y0Y7YDrơ.5;ilEC0{W.۾m;\.Cn$0fM~a\Jb{{bTQ@mp?<= mJ.;5Y>ߔIuV- n\ GJӵ{u-oaVޱ²(lϹK:&&gٛ=1!f!3˕u&Fxmk[wH^FW[RҐYp9j+;EMui6}{5v֗v'GF40eL0+q--CZ>sK6ۘa8h2F `86]eM ΁ZGhHd$᰻rvM}{^zW:u\H<b2o\Տgtm*Xm.剢"12 bupJ *  =djB <٤ ȏ+:+lK6wj(+KF6^\y7S (tݥuvGf&bk;]hH区Me'zuP`ыIw:6|ӃK7FG'FRB-F:q) JO %j֒aFĄ |:r])FJo_DZkӿgqnV3E#[i"mr͟q@:ڗI. ۑ $mcВU=O):ҽLw/k:*( (9O ѐT*U|8''q㑎'ƒ3ثB*!0II1ySG B(M°BkԤE%Ks/R )r{O~E3O~yҎx SxZ)7 7 9=>IVj%{=" T‚9bV:Zޟs6GT]|2}wF ڪ2N砫{-4,qKhVy ?ld z]ֵ~|oH|1]:DF[] a``085m:՞˼pTSl}ſ?Q/$OoDDNR{QKY~ ?so<[AMbv'ĚuPKHA1`cxR]oZ}_lգ)*m}^} doOK4}ſ?Q/$ӿgR(\o<[AMgk'_ qB-%2$z\̻@cp@8CZڏ Z j_ ꑿ5hQ%VImJ(~cbp: 5^ =Fȴe/,rfm $nXdS4ak^FҺ vk2 (5ݚ˨\jvnPƒȱIq ˘ԁ' r|?CKa#Wg`:RyB Y0| oˊ֠(?].FףלxGۚgۗ3n`ѧ8![$F` ZhxoT-^IUD[%Cy5GDy'ڿyMn7w{R B֝I5˻kۖuHX1`Ǫ޺:?o_}^Beq ^WQE}aQEr4oR9Q@%̑}Nbz櫨~i^*G B(M¹[Bs]=s"RQ\ǘQEQE4N3W,98 8\0BW8;'>nWІ}ӏ֏0eF-> s”|G/ 9DtcILUm(>7F(Q@Q@KG- y1L"ɯV;]sKt衋Z$HI8'? FE_CWinFm/0ׄu{-qDc`I=Gr3_ȓ:E|".ծ!<%9JA0n#+q֚-Z2%c g"e% HUHu^}@wy^1Ҭӧߚ _XsgseV @#s= ksº%r#-B [{ڭ pצ # @iUҢ{ehSܦ,kd6w>ZSgk=Yq[l9c/ ɯiwFo4UoƻŒw^0xJ_ӯmVi-w`;>o-zf?i Ѷ>9|?7 ' "_4h0+S_4h0]oW=!EV[E% V! QM%CF?7 ' "z_4h0ީoo="Y#;虝I=ȩ׌h>Oc(G9Š(((((((((((((((((+E\m?)kҾ O_B||JCk/%̡c5]|Z?h&QEpQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEvgnkk?s]y.qEW AEPEPYWO,Zf3}ܧpX :m@ .Z_ N-e;s\iKYwk,@Kɹt1(pR墸,j{YqXx{RPHmf.QvrT?Sq[mYWYAD^\\hJ9_1#b5$jPG=:Ɏh6W鼙>g]|,DdIʼn$1$ĒI$MOT4[` va\ q t(/W>{٥ɸ.u=;רWxGۚ*NRi鶝Qـ")8OF[D{SQ){xDz-dmBvz#ugk[Is}Q TA<7o1i.['RFÐp}kj% N}JiGi`YmGW]Pv Jb|ZCXprzM#]ǫ?Ër?i)LQmslۏl82)Is_O3m袊(ۭ<,ͱʼp 8I?5}Λ"_={_x[ MSL7dUݶFQ`:+!cX\l,'PyUsA5f*?&'? <CZk 1p_:]OoDDX0:#_k_H\=.Tf_-|zi>i>w*?&Go]l}ſ?Q/$OoDDGv}ُ ɹ|9{=* VR&X=g'7_I$SjU *q&ggeȋG٠g=;"/A<#fxG| 9pP숾}*Z(}C"4?Qh?heȋG٠g=;"[ *vN[9v-r2 qZ?ڧI5E'tiJk6A> Q) o?UFrD%A? Mcj3}Dcv5={Z}Dcz}Ɠp3L˰1\ ~U:jͱ;q/xfֵ"J=ӕ`x yj.}麅孕̱ W9s\mt|A-ä DVm$)$85t^攠 EuIkQuQ,5+ŴX611J9#dyOؿhW.ԕf4t%X"6t5g9{yJ/2W9F8UJ+՛VmWv G?;\G% reQQv% r0xZ[]cP:<2%A9`v}k6..OwS?Ʋ 7SԵhzq_ԜeNr׬]xf|o@.5m^9'W-%o3 | 3 xւ&o+nȅ[x*$gF}E{կ7ҕ} tŽ4B-h$ (k[86$s*o\rGs ?:?~ (h'7_IU9Waw<1aoRdՋi+Wu$!x.'į?f븮W3YWuo~faEW AEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP\?o滊ܳ5݁2wQEpQEQEKD$6jIWPpZmGV+95 yl(%7%| !TfM~a\Jb{{bTQ@mp?<= mJ.;5Y>ߔIuV- n\KK_߁xkG[sDUULvKr']wĺìبb~y+l/h!ݸx􋫭%ey()W 휳XxsT[^r\X k{4Q)tcYxM ,'wjϮOg4.mcma@#x1'xv:]FOҴnki;nKK:3(duFܱ@5 ĺ~,O5ޗࠎ6`D #XHV'Mt` !fRQTm}cQj>!.aY{pclrJ];_?YWڞO;lg۳l}vߛΠE!`=>{opXn$9&ѧ.taXZ'ґR1irTbW=QE2V7[\-ێzgzylj~]e*I9R}ޣ3S5 (Q@Q@Q@t/u)Vh ;I8${kgż,2JWR;wgP}Q]6Oo5BWB*r<G"%U 1sJg@gc aKs'zOS.ID^7~qE(#E]"Dt( 8FONH⽖Z>7Ҥ>Ճx#-Kl00xփ<6%n.%HI*O޽oW = šu<-%PNB}Һ0\GYQ3s{QKY~G-go&D_UKU>Mgoj?)k?._o jє]Z[\~p6LFpI y_4h0]oW=!EV[E% {hϵ[R{QKY~ h?aG&D_UХ??Y ֯ީcVUidԪ;f'cW7 ' ";_.7[Him%H&gblOjҍX{Hu&+(((((((((((((((((J/!?O=yzW S׏?T\7:O_(eq\?į?f븯GŠ((((((((((((((((((((((((((((((,wvgnk/?Be((((+dAb$*t7S݁w^Uܕ0܈̉Հ2E1,4j7RJ\[޴*%ʻBS1X|KGiCQԮ4/?W7jU !> Z tg5YKe.Ku$67~k\eA[~/39mJUA#IQ]H.@ZV ]%˂ \i6wu{hm#A!j1#s7*p>t615}t70 %L!T#a}ϼץ]ON{k &60s qpڽMs^[PT]> &5P vIvĠ h(8?_ ѷ52p4\y7E[gz\WyL?#@fˮ:*( (7cPBrK?)7y7(((Dڱ**BF*8MxWc:RHUD!XfAK)#r2 s5b4#1"DPDZ?_^.E2ҍ5hQAaEPEPEP]?db0B'ׁ$S൝( ՔFsGslm!T}/>dXݣ8VU)-]'&C2DV8AqHӦ0;Ëwh,*֏s)0dCF1Ӝ*yy.6@Uro6)I2ĒYz95E>f(ubĊщQOL.>%B7*G*=' t* (~D0W].FWh0ljt\I/aF1F2A^uն>x^Dápxp1хTۗ;kmTz5J?vV"zw?:'γ;]|?87+_ׅ(GsNONgWvu EmV"]EѷjL@%pLS98H'-rSӿY=;~u^9h3$o5@5"zw?:㵝 X[oT.5bJ$IO*GҌhHOuBw<(L(((((((((((((((((+E\m?)kҾ O?^>yQp>%̡c5]pPgf{4_ (J ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ܳ5W5 C+((.rɮkp[,rlK"$3"_BR^ҼQ5\w[+ 1n8_mUY@(uH#:XO6@$I fw) }E^vI~ю.@紿>^]%-w&,JUC"˹bxO_xLE2+4!.Kr5仐uYc+&qXPR|7w.cvot6H\H嘌1N~`NO<3[jM++ -2Vi^5U!RXHu<ںɻJxLR)W1# 鸑,uvitkk7VR )&k$`p~l.@%KzhrE8o0,OpFJҀ`K( !$ kFGRaio3iDQI NCy9| G37 2$ic`ŸRq+tx:O&.yXI)+ ZGȠ <1 /)/s,PCkn!d,$ɑrl[wV%i`QLP_}dB @ QќE@Q@ylj~]G/̶>ʱݥhu=+0an;b"ߟMugk[Is}Q TA<7o1i.['RFÐp}iu=FhVReq(^s:װZu<4g/xO+̊k 1uM. c}6I\~~Ƨc=OoDDG<[AMqQ ߘƧ`SvdoOK5b5wV3}J@wYܓ9?|x_|5>ߍpY-l@s1{)eRsk=h_t}YE*OQk={9aξZh_t6?GfYE*OQk={9aξZh_t6?GfYE*OQk={9aξZh:m߉G7 pL(  ua $L>ݷ9ݎ\۝}h4۝/d47"8U'W_U r®e, /*_u!5^§b誟u!4hE*v<'E*_jmIiH0NX):3CY{DU]/5%tau(Bͼ$3')K7";UFIl{B\ qw殊zץVPrN߱R(W!"2=3wT/N,fBE\.@XW$U8FeQ]_+o'&bm_UgQ]_+o'&b⪺xIJjXbiϏrNwc^W,r[x?3G+o'&b}D]%m##yo%AϨc?&RxQƗ(PeW$97ºPG"4L>62vIԊjJcVu∌-$`qktݥѬ텣o~f<[AMdoOK4G-g?g(qdoOK5jjC9\DWKB.9#p၎H9s{QKY~uj xcVCKbb3O8Sp y_.l}ſ?Q/$OoDDNR{QKY~ϺaqdoOK5|75ƯIȑr29= ioj?)k?w5|7FաGZY%*ى'֔pU#}<(L(((((((((((((((((+E\m?)kҾ O?^>x.'į?f븮W3YWuo~faEW AEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP\?o滊ܳ5݁2wQEpQEQEN]3D $U] V[t:HnJ6Yq|ğX3Iʼ7#g(jw`Gᶀrxk}6;a^^s[ĞTwq,U~TyA=BF/jj~jy*Z(cK}n"8YsꤍutZg5l~٥Zmxmm-ZUDe]!)Y,>]~WvvM+L\Ayru,! W2K"ݱݪ9UuROK?a3orǫ[qػ`q~n屵{ީE:P)# xA8T'K i·e '-d2yJ4otd@2n@lX[> dӭO by(8EqfL}&$ڒHȲCVFY[q jg.̺=+PLnGf;a8_l7p^om!wiE) #(RUqXMjEu-] ȕ6&vj ( u`Os^^qink3uGn]o]Fxo[tU`2OjI5cG<1ujzDKa:Gw3;lOj:?[WL.G8Ng() =zS|o/ۤq51S)9)' ^wUӴ]jwPs2ƥbpƼ7oןe^խGK4S$q}3B6I'x?#t*?xBڽ:֝%[$tS"0Alsx]tyQp>%̡c5]pPgf{4_ (J ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ܳ5W5 C+((;SҼ#6sѹ+3aawGvdb Yr;,r,&$,R HN]c2.T*!e4]A)+]$+oX}NklOMnE),GWbŕgR|7w.cvot6H\H嘌1N~`NO<RI }:Of~ϤM*ݖf-&/*o$ޅZ gkoqxӫfeQϖ4$</ åxm5םH[]#Şe@m [ ,cjm]Ěk=g,$DM;Bř] ײYkڣ޴]iS*. &|E%$.BikذfVV UN\ZDQK'm2kc3K$JJŊf @a 㩠(V1^5fk&Hqc ^^qink0:xiJ uGf*XL=ouaNo'DQ:I / ,V d! cWu՝am$q]G&T2ǽR:ިehŤlI@>iUԔ%:mg(E)˱g=\uCÖ\,m+%ibqI7wZw?-hc m1DSYn='ȧ/m%} .vFM`_#$cdEh[foاiQePD(x,v'&!9?[BsJ,c-$̥EVQEQED9`[x줰QPwr.Blv01t PCqA9E.pwgoҥ+ +QLaEPEP|Ӡcw r{6R@#z6RG=3ʽhhrtb>_|4wyǵy'|aveխ`rslө_2>8=dz_~_{}ߴgv{޹^p\+~'%GR\݊)ŅuV/M^ԑU b5 rE};_u!=.Tf_-|zԝHy7qQc^QpO6??N2,P6OO^*J;z)>T]*HSJ Pk:֧ewf\g%TA*e9 ydsT;^FhepCrrr8xfQ'V={pj#2%(f'wH}yiQ͜k lQe$s8ӿ?~ǨiLlYmu+x c)x)SK:͸dd6;nQT`QV|O c^q/mFI'nF0x9bk*|Vwb{7?ӿY=;~u^o<[AMdoOK5֫;ʻONgWvu EmV"]EѷjL@%pLS98H;doOK5jjC9\DWKB.9#p၎H9{UONgWvDuyidoOK4}ſ?Q/$Duyk;ީ2\j㴕I:U b5-މ&_F!=.Tf] 1 wǡ(k:NouՉEW&EPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP^_B~!6zZqЧ< ΓW3YW+ez+k3E0+(((((((((((((((((((((((((((((k?s]p]Yۚ?Йt;(J ( ( G@rˢZn(`0JŻg%5Ueלj>/\ɨqddA+X,a.}IM/%폅'hi ] PȤ+0rrXl^"LKyy. VXɅeqG-/|O~ylmlViU2sE-<ubeh  + vHu<ںɻJxLR)W1# 鸑v^j0\KH%gHfiw KQp_8pNֲut 1VBu  q&ǃ`#r~9[ &TӧQ70FZs |daFiv@ֶm5t;LU+&-2+8r6ĴD55k ^Kk 5~njVϘ_̐qhogÖ| !p`}LC?,{ͪOԅz1>|B$>\ QܟO˰sva\,k4“D噝q~Mچsga 3LS#D# !{J(+es-n.ivZ1wJ u`Os\ypn)u٦v`Iɜ~J1 ^ %yC5+<7#hZJ '5w]Fxo[tU`2OjKJݎ(8Š( OWZŔđ[y`sq>ls 6xmukR(b^I$`$x,ʽ9FG kϊU^#]ayd_4h0]oW=!EV[E% Wg[W>+WLEVƳI$z.l`^ h?aG&D_UL$fUs_>£n.|0񎛧]]M4NPI8=+M%CF<1u~zDK\$q}3$>3G]s@?7 ' "_4h0S3쏟?Q7Xڷ mu[dHeWʒFrU&D_Uy'-WNMmq_¶j%"#8#qG֦C,'ZYa?Q?V3?cd3d3EXe?ɟg(ɟg+b>0y?Q?V}ba&&Ie1,RHOd$ `*ep;`OCsK3y~;?ɟg(ɟg*d2˾1#:Q>xIa2%ױ3"XLUvGM^#uXdhۀ͸6~b8šO\^=SGׅ7`NE'<Q_{⦇wSQ]ZK d g^;֢ZTD+ٵdy?V*vDo޿Ecj(N?7K_dyˍfK;'h@ @*Bk8 *wG|%yiKkg%]Xrm6/AXҧ Y[ΠPĩ* u?:瞇S:aO)RSM͛jAӴKO o*v3?¦pP|1'ǥ=o<"HV>k3Mn#$ `7 :|/}ox[gq=J`gF_ۘ!H A ZR8T))S H`&S;}ѓU,.gpח.:7P+c1f̓ʰY]3ίEcWws%HK# q:9²ZG6+ooeMSeCֶ\F`۹Fܶ~)V]Jo ;^Jૠιj,|ݞ_ڮ-q#B]]n_F Nt?ڏ Zk_ jNSr%9 `v?7 ' "_ = Bu$-%`.Gmֻ?ɿ-noj?)k?Х??M_4h0 Ѣ/*mo yߌuk<v^nl|c"]kWTuӯDem9 ^1O>ɯiwFo4UoƻŒw^0xJ_ӯmVi-w`;>*K];݈_uoR{QKY~ h?aG&D_Um>Mg oj?)k?._o jє]Z[\~p6LFpI y_4h0]oW=!EV[E% {hϵ[R{QKY~ h?aG&D_UХ??Y ֯ީcVUidԪ;f'cW7 ' ";_.7[Him%H&gblOjҍX{Hu&+(((((((((((((((((J/!?O=yzW S׏?T\7:O_(eq\?į?f븯GŠ((((((((((((((((((((((((((((((,wvgnk/?Be((((+dAb$*t7S݁w^Uܕ0܈̉Հ2E1,4j7RJ\[޴*%ʻBS1X|KGiCQԮ4/?W7jU !> Z wyM͸9ٳݎ~l渭*C]m?Ty3-iO|6$.Q9f* P}NwC&/E3dci0,b2.so5xjkW[ylgimcI\7"Vmr9 iiXZ6O\-ϸ fmg|ʁoUYfojF"[&F]$lLn<)}s0wz:]KoB#%iAM<)svڮk7CNOkYGtq˕R$@LxEP^qinkec^vivn$v0Nǘ(<4KM5 b6GDy'ڿyMkUK}e9GM'4KH7HߖKIX'a/=|-%8rɵW}E/#-?k{j?2ꇇ-XV J/oWSԒ}LCJ ɘ9{]_tTQE}YQEIsb#Ѕs*ӮwU`x:;Fp03ԒpFeaёB}A #ԟV~{w|:>C^xw$/ ik̯ɫ_frQ烆aؑ޻?Ǒ?GTWkԿ.Okx/dUc{ =鬶O'w#>#]jūlU2xK{wSTw#+ pAfO~.֔W-[d1j'Gd>.5?nђW_oo Zƥ(@CHd`=28GlGv]'B >?AK:u|ԣjQYksgUjQ5(Q ?_?(V_(\{ڔ{?&Վ)TNUut21W BmLZs}F3.i'|ⵒ6yUQ$V4gA/ F Hܨ*LS\k:u|8j d) Th<4:*j3P+?+??Z{5H篒=kUJomkF/gV|28#vJ͂J\jNrNt["K`0f@@8p&TF2: Cܿ ?DuyhONgWvZexEMcg A?:?gx&KXv5{RBʴA>6t_+(((((((((((((((((J/!?O zZqs׏?T\7:O_(eq\?į?f븯GŠ((((((((((((((((((((((((((((((,wvgnk/?Be((((+k<\tHI!9uȻPᗴ8t mW.*]0mb [y"W[h|VP oCR+;HΥ,ͣ vIC(i٥,%cw|ĶH3oQ{׹]Dmocːp9/%폅'hi ] PȤ+0rrXl^"LKyy. VXɅeqԟ 0˨:]/M09f# s5]Q-oo.o| y,ĔƑqy=GQuvZVR۴1Db03S hq-&VMoy >  ( u`Os^^_+m}?cquKϒэ뽗zWaMJ*7iu]]ـvE?ɘZ08kmyc.g*IީxdxoT24b]N 4zѾѯ~Qμt= xRz6<Qq!8_<Μo/Qiy=Y[LHbosg{`9N5oj&k˃%B,I99X$OldJNmCZΊ(8Š( ǩqeY\iV{R_i%FJ6V)& yBN &*Pp>nq<8[y3-:f8ݐrz~OLIyc0"[m598~ⶅJqNTOOخ3Iͼ1O>ώW|1s]nA 0<3@9?ˊK9YH d֞yh'xwd5/㇩b4wSKmUVU2s=gj?'[+ 5'$"3V|3Ĺv ގ"%ͦrtWe ƿɨᯋt>IivIl[}b/֟/(O Pt©j.Re}Ev_|k@_m_ھvN$T2+I#9RGP*OE}Rij*%~fK/Qcuh_`>/4}j+* ̬r_YV y?3G֨0}Nky?3G%~fQ`eZ*%~fKZ:ʴUK`>Guh_`>/4}j+*UhlW$g? KY{K< cr^㨮|EZuw1F9uk" N_ߞ?:2ipxAK4:oOkn!"FQ`ʌ9dqa;y#*B+;SG~!+Cy,G'Re/ˆ0r2sdjo . 6@8Ky$xm##yn%AϨS?&RxQƗ(PeW$9&үAA,6r|$ =zֵ~|oH|1]:DF[] a``08Z"rմidoOK4}ſ?Q/$ӿgR ?s\o<[AMbv'ĚuPKHA1`cxR]oZ}_lգ)*m}^} doOK4}ſ?Q/$ӿgR(\o<[AMgk'_ qB-%2$z\̻@cp@8CZڏ Z j_ ꑿ5hQ%VImJ(~cbp: 5Zcu;xm_$_Y=u0MK$JYT7a,xU|K_:۫K ='헑`&P2v2۵j7RJ\[޴*%ʻBS1X|KGiCQԮ4/?W7jU !> XNYG\:u!:j 8c9b?{vxc\Lj-c+ZPꡃ@w``k r^Tn"Ac(s<Ή[xn>uk.˷_&9.c|rHf=Z#jŬ0Gm?Kmb 5g A #[Tխu4JCť4m,r9c@"n3f%[}By<>i+[H4bRUZ8K;.UCeTqgu{,Z+Ke?ٯeʗ|8EBe-J5K}k+?ʁkCDZDmF朜0i\&RQq۪Į\-1W; tTQEWxGۚzu`Os\_;r&szwZ43+zۤ¨xTA o EI#Jr(p&OO5$[W)_7M~jVdh_ӿ)?Ƴ9wmy{rN 18l{z8VS`{GXZ'\K\}]&{8YU1oqsJӪYW94 Q*sґwqX3v?)?JR]?ȑ$Jea:~J᷊|=L״[y:W#*NGƾz"11~Ÿ&GN;vjjo/4OEV/bnzg;9NyV8!]^9uZ8RKty-,VF*$ ƞc2 8?_%H I9#9o{|ZiMX2sIFfQX" yFcw(ch:):>u`)Q@Q@Q@txR%[^9#`\AGzk'~ڎ_?̢+U?տ 5Re"AL~vRF:sӍx}1|-B󝻸9R– ! I?v yV鞟Jr-痐2a_ W ~9ci"*@y LI%9>Ud= fM#ƙ H H$c>)L&){g#U{{*ʼn$\}3?KKvo.(U8TzN@RE( xm|K\JC/$0UE ${׌<07ҮzA4y[UKblOxm?!W?i ѶG[ҏ&D_UKUEtf&D_Ub~0ĺ?.DB/.'ed wu x[w4o/4OEQ h?a[P&D_Ugk0׆K{-qDP I]}exEMcg kCB{7QE9QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE_'/iO^k^_B~"6 ΓW3YW+ez+k3E0+(((((((((((((((((((((((((((((k?s]p]Yۚ?Йt;(J ( ( =+3j0_]0y?6twfHf #.rɮkp[,rlK"$3"_BRPO>"[2Bm%qo) y[FU<0n]o:Zb.' c g𫑒X:VvKNY'EGa @$PӳJXJ;lg߅wsrYQEێ ?h my_>wyM͸9ٳݎ~l9|;xC6VQa֥SK2̄BHYY >aQt}Y^$.$`rF'?0''kӭ#i]:]F{+t<4OPJZPZgTA'n Jm]km-jZ[[䔔3 ;y##HRh^h~)} yWK{ .'Y @DbLA1*{z]}/\0\5,Iq)rGՂ ƺɋVFQk 7$MmghO2yv'(A~P_n4]pmAwmT&0Cgk BR2 t:| fBLW*FպZ(+ec^vivn$v0N'v6 zmTv`"k?SѭVGQ-rJx;{?62}Lr\$-jv::( (8JY;bdp9=I5g/xu4 #! TpNN#1O5]ԨӔS~oīk<4g/xZ/_r:9W>yiZ[}Xmmcu"瓀'񫵅PM="/7 ZS'G.䷸u9aJIr2'ڲh};${?-[d1j'XQ9#1j'U.JQ-ԁvzeqިI҃(7xh?AG**}/_q+;7(\CE—>[ɾ?AG*!YQswJR~U X_ >?AYIrW @GC B(2ӊ[ ʽ$h}Vk¨I -ϧ[z3C88.ʸs=GNքȯ\{s??AG*+oaKWiM >?APGVwoQ =/_p}b󿽓}T4Q)*s}aKWXdks { _ʾo{&\uBY'iy%wsby$]Ue_j{\ؘB\S9s6 J@I倧/eeXJr(''h9".SRc$#VG+6If}\ʲô0 iR@#8`,)g̬c 's^ jӂoԩ8.mb%l9$SS5,rc;i~j2IlT)KVXxs4}~YKZD{x  ;8~Q7^MJWSjOm,R43H0Ee*y92Sܔ:_r8 h$Mc.cFtQ,db/\ɨqddA+X,a.}IM/%폅'hi ] PȤ+0rrXl^"LKyy. VXɅeqG-/|O~ylmlViU2sE-<ubeh  + vHu<ںɻJxLR)W1# 鸑I/tdеH>Ŭߴ1^3\s 2[s(` t=Mֲut 1VBu  q&ǃ`#r~9{ x/NׅuZM2ܗ[V36 7f,N2hkO^L#ZZem-k XG8fCPҵC6 vڦlawast#iilHmthmtk/)$MFXI|6/1iT <§aN.uKY1^[L0C"(">cMQS[{ƒQeCm}a(/>2L! h[/U[sY^TX-|""#lP74;J(+es-n.ivZ1wJ u`Os\ypn)u٦v`Iɜ~J1 ^ %yC5+<7#hZJ '5w]Fxo[tU`2OjKJݎ(8Š(Sƒ3ثB*!0II1y|i<2ؤr6%U`J7#`2WGhQEUx\j:vZMODxf5Z) K{K db2pAk^T`cUӤ3ɿT5P_|5n]>]M4PI8 =+M%CF<1u~zDK\$q}3$*gʝ|k@_O Pu_KTo/4OEQW>S<O Pu[WnT[iE|$g*H_Io/4OEW|Rt_e+f[L(mpJ38h}e#ZYq y?3ZRcy`>/5E_<K5b ^+J:phv3W}>3!%H4\zYLx 1./ ,f\$_qṊ y?3Sp6C!ܻ/μr9뎵qǍH3 [{J>XaqWm7y?3G%~f}7`NE'5-3?y?3G%~f(Ρg_'hOօ}~gP3/4}_'kB>X?y`>/5E_<KuZe+\&cvFGEcM ׈5+yRXe6 w37FPϨt)QĤP?IE3WP%+(B c~VcHYR U'^[FSjZcK %$󰓜8S*iq,'=NqP91*asǶ8W_Ԩ\ǟʲ쬿åͶ뱷!kqLK0#Jɟ: :^Cyjx9ϷҐ,#s4Qbg4p0YX?Oϳ<@vPcy BxIhyDL@Y[h_mΛ$R[!q.]YK#lʌmfjS"V9^;uXˀ%F*wanT][M,A*]ɕ 31*hbc9$TQEWxGۚzX{׸]]<"-]3ӽq  .ve~~gfX&d?j#]jūlU2xK{wSTw#+ pA2xRsJ+KMCT_oo 2Rs_7Zׂ+?/7 ZUKcRK{qu ]$20lrx6ki#.[r\kWGjQ5(Q ?_?(V_(\{ڔ{?J+/s}J=eyj~Hpc:I |!6y㿭C >?AGu^#p4dZ<+y3ߠp |#$nT&CsԎxQWAn+DbxLH<1;1v׋ >?AGu^"գ|J+/s}4ԣjQYksgUjQ5(Q ?_?(V_(\{ڔ{?JM,igt.fѽ>W}s*r80G~qDIng2p 13ᛡ:/Sa^QPOK5 "&8Y Ŕ8BqHbI3JVsA ,#!}x2E> Y")h]YNDj9$9%Q4f$rC`JNVo"LO*X!e6Fq'r`ߥ{9eUJg= UJ{Gje2!H#izGOMI,gEf9 zɋy+XT#[k 8SUS9̢l^@˾O,r{qMpRNbbI,y<"&/Ȥ_ g <:FR@ɽd;`6=3 'g0ݏxo14EL_?VA>k;ӓV2ho&/ȣɋy+_.{gl?!7?i ѶmR\3i~}1-/l|)=յDKHZ0MX%(E!Y˓rŤfTdVhB\Xkw!VL+.㍤oI]FIfzl1cyJ~ԓĖWquMk w{2K+<ˉ%۰K(5iSuWY7iOUe#5$s73\:~qG ΃-k"KyD2$GS,{PX.kW6a)[%Kh% r}CJhFHWn{ey ;gfnUG 4.Qg[<66ڄ\љC)%Y6#xZד-[GF2q!e4+,&,x%K[i5"y.<&34HĤXf`>Fb:ܟO˰sva\,k4“D噝QE'v6W2x*vk%{q{/_l<›Un꺻#툋~15՝am$q]G&T2ǽR:ިehŤlI@>i}_)[!Kģy8{w'Dm5kIAvx0aBqyS9B.|_WSݔ%75 iz8+{s-L9f͸ sKj?M$חKimȅY s{zr6I_'Ȕi^y~&;ܵ}yQ_RqQ@m**dq qV/n ֭[Oݓߐ4[pc 1>kɽcl=ȍvr9\dTLբ$hYkUUKM峦2=I`3 $㎽}fj?'k}LRU+C(((((1m;NS)r7erZ*B8Y#\1}j=odO:U=&To%Kqp8$!T0TC҅V/@DQEjXQEQEQEQEQEW[ʹ* 1 ǧj?u"~5#8ʲI epX=wqI Nd|[['y??ʱ)Ă7"Kn%R7 3`2hFRE ]Ji/֩4f5IO+1"6a&Pq~drkFi B3@(398c4Ջ٤1쑡Ec6t#@1ͳ[9&R\825f\G $sJTV0՜;i(X ?'cѳ?$0Ų4iF_H^gKYbCn d+n}*CX:׊"2`dc~ayLJ)gktݥܿ{6> (h'7_Ioj?)k?Х??X^}97-މ&u__bM_Hg:rV%$}01<`oj?)k?._o jє]Z[\~p6LFpI y>>͏xz' %> (iڏ Zoj?)k?y_.7-މ&_F!=.Tf] 1 wǡ?G-g/H(K$P?118xҎju[gQE QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEW|덧=yzW 9#Es2_=wJCk/|L(((((((((((((((((((((((((((((((+rq\?o滰?&](((Q}կMEtk.#%^[% _c t#BHC\/x" &i WFl-@16 ]/ďZ_M/ Z:ۘ&%% Ҫd]~WvvM+L\Ayru,qu}:LUA5eIx{; k:%ŵvZ7Hk+rKc~n屵{ީE:P)# x-O[M5A]ia3$IB'p_LP巆n-מ|QJX5l|G@BUr6{*_^-pAww^ Hwj|H#7u,g'c[ʆȅG&Bw[VԴ{OPt'1pBj*RM6<6qq*E WHU0$xw^0xJ_ӯmVi-w`;>O1^w#`i_=zW{M%CF?7 ' "z007 ' "[%6HtQw"~p?7,'wk# Ѣ/*M%CFޢ07 ' ";_.7[Him%H&gblOj+*k?kZŏ(p(((((((((((((((((J/!?O zZqЧ<t~ntPg_(eq_3_֏/Q\%Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@p]Yۚ+rv^ˡQEPQEQEW^Uܕ0܈̉Հ2E1,qw^Mw^S9g `\n) $'.b2yaٕl,S@#yHhN2/u?xz{}9@e(+iq8f#HS>_\*>"Zra?,; d$2RV9ܧwKd?.{$JO.&vIF8|gnw+O.ba.=u+ob{I;3K"Y̫]-[XK[M/KԼ|ٕd9X:yg M.T#I)_znxv4gbbӬ #t]=ƱJ De0%?v;TL="-̷R,#ݭ)18m ,Y [ocekRޤ%N2K+<0QtiO 6n5%?8iR,(}pafRB˻y t}Z<ӧܛ7\" cȞP`k5[]B6O6v Q8L3. AT碠(V1^5fk&Hqc ^^qink0:xiJ uGf*XL=ouaNo'DQ:I / ,V d! cWu՝am$q]G&T2ǽR:ިehŤlI@>iUԔ%:mg(E)˱g=\uCÖ\,m+%ibqI7wZw?-hc m1DSYn='ȧ/m%}]͵Diu8-k'7_I_Z|ƯIӮ *3/= y_Xgj3ɿT+57G*&S-މ&xz' %>Wg|@_.#þ"ee.ϖ.9bO@+~ (k>)Gkenf{kfB~ '9=r{jG'K)Oՙ14EL_?Oi>8vC<"&/ȧGppy14EL_?Oi> bi|<"E}ò<y14EGr` !7t#?RKk=.T I#l=*S%G{__Gɋy(bi|/9@eF z~" {vuM}v4<"&/Ȫ\;H )jU$g*=R: bi|<"E}ͽ;!L_?GOSO{8vC<"&/ȧGppy14EL_?Oi> bi|XlB6Ȃƹ'jO=sukeL.(0lCY ?շ%|}Hz%ı Dl(0)kk>wx&Y/(bBw0V;6w#ɮꑧ{}ǏԒv5?AG+'q8 nq:g Fd8GS:קZA >?AYn]dxLyać GN999i3dUx㓪>ejV?Q ò3sOE-y="g}_Beza͂J\jNrNt["K`0f@@8_Õv?:?gx&KXv5{RBʴAſ?Q/$vkuި$"S"Gʌ˰785MgR)8((((((((((((((((((+Ҿ O?^k^_B~"6|JCk/%̡c5]|Z?h&QEpQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEvgnkk?s]y.qEW AEPEP^qx]YtT\-` Vl\D*zGV+95 yl(%7%| !I}ĴV-!k4!bTf.N] }RZb)Y r]c/%܄CY06£H%/5mY"P*Nqg]Ra^ZXQT1?l6BsaXvouꚍ$:[8ml{3 oLqm5&oWT[M@yREW X=[.VoSL^>[G+|y7zIq q,%nf9Iz>gN,5Oi?>CYRΥ_#ԭ~?iegRjV4CGk2$>Je)]|Zt5CGk!ErKO8) ex=z/н_Mi2#9&F*q$9S&bo8\gNn}GB`ٿ/I_s3M$65F0Y&v#?gQUK?5-nUoO&m7vGk2?wW\ k(Υ_ ԭ~?iegRjV4CGk2?w?+v_kp5EԻڕ/?Q^ǨЫW3af$ggZB"=:hQWzTw|ºPG4L>62vIԊjJcVu∌-$`qK>)5.mʼv]xQxnoW = šu<-%PNB}ҼjҝH&g:*b7?gRo/4OEQ h?aG_7Х??X޵~ټ1FSQvUims!%11' )8c Ѣ/*uxb]_oHuyYo! p22{;>Mao3s{QKY~G-go&D_UKT{hϵ[wBlgxZzoZ{IU[R 혜}__4h0ީoo="Y#;虝I=J5a#-<"(L(((((((((((((((((+C\m?_'/iO^>yQp>%̡c5]pPgf{4_ (J ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ܳ5W5 C+((Z_6ϲ]ܤk$ZX9*P0g_0O qC;ծ/1WkM (:Z(+Y%t`YmGW]Pv Jb|ZCXprzO({iY93/r(8B(_׏֖zb-J̶a46A' 09=I'?zdx<;{xu9WܵXZI!Z{\_ kԵ-3ϻ$DH8 @**bcIsIvgS{~ j+U> /MMG~KM}iw6S6  uv)gW|@_O StiRSϙ详?T+57^]#zG|Eoi٬=],]rĞP*}:r*+_ɋy(bi|_PSʟ#"bi|<"((eOQL `'QEny14EL_?S,VqbyLڷ1r$F99N, $3b{kɋy(bi|VO;c[}(>0})X+ɋy(bi|>Ja~c"bi|<"҇?FE<y14EPPʟ#"bi|<"((eOEk14EL_?G??Ȣ"&/ȣJSddQZL_?GOQ22+P* bi|1~xUT("_*Ekv:7{om 68$dVZ0݆Hl;k_W_LKW7xKCᥦ%4ti|7c8+UGӓ3$.h#Ύ=rEf6p&w 1ܫciF{^X^VkwKFG= z+&fpj޺<œI:/Ku|eNd*|TI#6&Dw{}MZ9b{2f[+Bm<^ EDDUx5PXMlbFBF2rFuxrW ˖=ZMaipαisYK;o`z-o AC_j'?Z]OۺH jQoU[]NL ʤ$́q2Z][ơ/4EcP?'/̵a_.FWkZ5{_ڊONgWvDuyjh3$rjnV! Q?:?b~W-ƬDnK2 r0qАNZmEa'γ;G"zw?:rg/H57++*k?jDuyk;ީ2\j㴕I:U b5x5Q_Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@zW Sךץ|덧||JCk/%̡c5]|Z?h&QEpQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEvgnkk?s]y.qEW AEPEP\]׀]׵෽Y$DI ˬfEؿ*@7 yƣkmueR-pQkW%XbݳpqCલPz YFu-9fmHCN)a+S%A~o޽f%F'n;x$>\3i~}1-/l|)=յDKHZ0MX%(E!Y˓rŤfTdVhB\Xkw!VL+.㍤-Wڥ^T XEel@2IXxtj(7Y[a#2)` {˗vmŎfZߺ,C٣Fvǿ 8<%סTԺ4vxsSd Tf2o>a߸ݼ[-Jja&T<0D-\wmx+Kѭ-tӤ9N(0N1,\8`J]&{^-EI`DҒ8c+`c;UIb@Q@ylj~]G/̶>ʱݥhu=+0an;b"ߟMugk[Is}Q TA<7o1i.['RFÐp}iu=FhVReq(^s:;ݢ -6dc+M)Jn5ѝ<[AMb}5}"HFpdHQ| nqks{QKY~|a_~7ƭ >p,ڕ@co혜}Ϻg'7_I-މ&Х??GBlWuG~ (k>)Gkenf{kfB~ '9=sڏ Z_=6,DW-b7aq((((nTMѕu%HVU@ B۞HUr*rN5#!}I1G<}S~| h+!>`ˁ$sZ~ך4hixX+(I`0@_SfЩ!EPXQEQEQEWoHIXK9ÄfsGp+ܵO>K3VѮP'ro*pGW-J~Ewb^uim6myn{}9dbȅU*͜kFn8Ə,(C4_d[FufSwr2͸qiҮ;Q.d|<侳nece8|Gn1'#d7K&0 $-.mM[gYF Ow|gqmqk JA$V+D!Hl%k3N2Y84}ſ?Q/$ׇ TIv$xТy@lz\-WGוϫ\fEא+83gްhƲnIu,VfgA)_,rw|Ѹ8kړK 4LYJrqNz rK:grqR儹 >T?):t8KAXU'7 k\ƌ9YyWی6i$ cAt~&*Sp-}"?痕Lc4X<ھYL v?~;Mq#E)2Leg992SεT7E× 6Ð2:{ʗFz+hb 7ͯhnȅ[p*$gF}Ezկ7ҕ} tŽ4B-h$x.'į?f븮W3YWuo~faEW AEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP\?o滊ܳ5݁2wQEpQEQE|W,rj/k3\Aq*,PJo % KzBz|ğX3Iʼ7#g(jw`Gᶀb~$xo}d^6,(UfS'8݄RܳW].~/kon,(*^F`!@0wn7ligR1˓pK5ڪ VM}NwC&/E3dci0,b2.Y-SXGpnODzdEQ3Y% p[VAowiP꬯o+AKd~஦ ^2j:5yMǐtQEWxGۚzu`Os\_;r&szwZ43+zۤ¨xTA o EI#Jr(p&OO5$[W)_7M~jVdh_ӿ)?Ƴ9wmy{rN 18l{z8VS`{GXZ'\K\}]_\*>"Zra?,; d$2RV9ܧwKd?.{$JO.&vIF8|g_UH{fH䷍T3ר\uV0}aYޛȥK]jfW+2Qfm7VfN7=}Ziz^M Ս[vV̬ $yZҭ4CH V vzT䬁`˒>fK}>{+ Zx:y,͸ YP<7L>*is@ַs!i&&HU>sd\ιfi4 -@uIuhZNNBYKI3#dF * .4JxվŨEa}xm.fHt2X%~P`ǐJ+ih%[˶y2aZFf8X Tlg$ok{m&$O%ǖd2fI,,AS@Q@ybk..Mđu޽B?].FaRtҔOMTMwz5h%2OOt&%^$Yo%jCƮ;]hH区Me'zuP`ыIw:6|Ӄ_[Fz+> U3n%soz0_ *8|oeFz*;!f󿽖n:?Qk;kFz(Ѻ E}f󿽞M7nO*`ʐz1ZxG3/^3b=F_]ukm74W̏A9c<{<[AMpbS81*vU%' [W<ಚXH<FE:\jD ̉*3/= |*w9\sK^_ǟ/=_5Affxr_?6W9R$]9:WJ^ȟoW^}<Fl9%1Tq>:)rWacj-yWw {xV`K'PˆuWmjx  MBm:dY^d #;N>v T׊TރXM|mF fD]=  t%`T7$wrKs=XK^fPOQ?M_:BkM>$hZc$: K"C2vC{@e76 #8h:hU^v]]̥&L* Yϙ$P; t-gD6due9\c!ry`)YfYm'󶒜y)r 9Hi#E>I,LB#N8p:trjU%ikqs*c+ I;1k<Vsɮ_[ƥj-2{rWD8!9@eZhzڔCXx;37G+&f+~3/˭cKx_F̊dq bc`WG,ſ?Q/$כ*+&f"~b^m4D;pvyk'7_I[[_o@ier+\~ѐAݓ9=?_prDZk3t°wfo.> (h'7_ISU,{]]:X;wlmʞ#cZ^_P [c׎IdIl1co^˝fEiGO ٝ(*0OwV'JV1ӯ 8 ̶ $#<T*Nhw׊?7?ӿY=;~u^o<[AMdoOK5pÿӿY]o6jEՈQtmڽ>p&TF2:<[AMbv'ĚuPKHA1`cx>_Õv7?ӿY=;~u^o<[AMdoOK4}j󿽇*;=;~u^,-7Lx%ujN'i#؂ _'7_I~Q5}"HEDKa nqkJ8ΤSubqGQE QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEW|덧ֽ+E\m?)#Es2_=wJCk/|L(((((((((((((((((((((((((((((((+rq\?o滰?&](((Q5\w[+ 1n8_mUY@)|W,rj/k3\Aq*,PJo % KzBKi{cI."ZByhBĩ)ET2) >\-'4׈S"B#^^K U2aYwm%GKK_߁xkG[sDUULvKr']wĺìبb~y+l/h!ݸQ5EHt4qHNAgޙ'äjQϧjq]Gs<MՐ,b8;c:oo1ֿԭ⾓U"g Fo=aUc"l|Đ3ɦxGYӖ[H4伎ilVk{oLfxRG;"U]:/Z|=LmWy1ga+2cAvmR`f>TI+ҴR-rL xr$,q2ڀ 24l smŨLHnnFpk$gxHhHoxmnOڧomf05WdI"rz9u}etgӤ >Kڥ&nSʸh(/̶>ʱݥhu=++v ;{mJTf |_k]~fgs#'#\4GFt3?Q7UxMo.&WL'j($_&D_UbFoo="YӮ8㾉d{W֧5G£n.T~9MKTo/4OEQAGϟs@?m[օtZ$T2I#9RGP*s Ѣ/*▫j&/oa[5FeCosTǸSg[T-,.ǘlŸw?ʃɈsEgea3"n>h&s.=V}bah)$=qJwLߏ?[Q^¶(/?Lߏ?GLߏ?ZcXɏHw xUIc0v N&f"v?Q?Ud;|c׎Gz Jo OE'35e99yFyMSa&/ @&)%q0$עYM$D+ٵd)3NhU*(S `Ὶ_z#$NqtSN!}-kb.ot_4FO?+yYxk[l丳{V$)詞*sHk §{_zWtYH ZıF,6 3z$_&µi4Hю"JRvcͭ>kknp97RFIϋ.u 9hD 0}rqypk7 ' "'T.-IaQx䍃+ #sП*rM+2*7 I.=3Nv4ifZ!X }\Ϯ~$蜿XtO?u */mo{Y[]yىJ|ފ?&z-}u]tO?uQD>35,l^hu{ylHڤ2IԊܺ֯ύ5`^(k,?} rAU \JC/$0UE $z׌<07ҮzA4y[UKblOyUcNyE=JNx:-{7?gRo/4OEQ h?aKC}[yR]oZ}_lգ)*m h?aXߌ<1.᷏zC:Ke=k&0Х??GBl7 Ѣ/*M%CF=?ɿ-;{QKY~A_R7ƭ =-TvN^>Ưo/4OEVv ]xoT,JL$եMyEW&EPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP^_B~!6zZqЧ<t~ntPg_(eq_3_֏/Q\%Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@p]Yۚ+rv^ˡQEPQEQEW-/$Ȃ'IH-nU9G'P nc> ;SҼ#6sѹ+3aawGvdb Yr,i(nfkᵶiVUK)wDcdv]h^7n2qqC}yaٕl,S@#yHhN2/u?xz{}9@e(+iq8f#HS>_\(l˧ZW:$wBUr;8+huY6\2I.V2E#,y­]m {kqjpoiT%@$g#Qָ;3I֢xnxR72“2HT 9p\!ַ'薒DJ)9%%QUT6UNL2MSĞ6U@61B#%eyEІKr֩IIM2g=0,N?(x]44ܴdHHLhF$*nl̡da@<ץ]ON{k &60s qpڽMsU#-m%IkmuceNЩX'T5tTQEWxGۚzX{׸]]<"-]3ӽq  .ve~~gfX&d?j#]jūlU2xK{wSTw#+ pA2xRsJ+KMCT_oo 2Rs_7Zׂ+?/7 ZUKcRK{qu ]$20lrx6ki#.[r\kWGjQ5(Q ?_?(V_(\{ڔ{?J+/s}J=eyj~Hpc:I |!6y㿭C >?AGu^#p4dZ<+y3ߠp |#$nT&CsԎxQWAn+DbxLH<1;1v׋ >?AGu^"գ|J+/s}4ԣjQYksgUjQ5(Q ?_?(V_(\{ڔ{?JX}=ⴷߔh `oPyV>J\wQx]0:֢IQ/O5L&5c#Ӧ 5̶H`NfȘڧbe@'“]|;WO JI%'ph%ak)ơMgTlR[UXv88eiy_Rs2D2ipY["$'_1G=  +[!qUxMTEm/9RJ$,E.$.y c'-dovK,QL ;w)PF gǮsZNWІZf%oJ->x\I/F1F2A^uն>x^Dápxp1b7;kmT{J?:?yQp>%̡c5]pPgf{4_ (J ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ܳ5W5 C+((.rɮkp[,rlK"$3"_BR^ҼQ5\w[+ 1n8_mUY@(uH#:XO6@$I fw) }E^vI~ю.@紿>^]%-w&,JUC"˹bxO_xLE2+4!.Kr5仐uYc+&qXPC+RK/*im"ef g$z=gΐb t_FȲFL)c'H "[N}SQtY$KGFp-y5s_p]躿-6ܯ4a h>Z!,rXkj5:e7k,.,\JauܡG` %Yߋj]kRA$fM?0BFO$CvUGJH }>S{-3Tqii!3^sn%Z(+KF6^\y7S (tݥuvGf&bk;]hH区Me'zuP`ыIw:6|ӃK7FG'FRB-F:q) JO %j֒aFĄ |:r])FJo_DZkӿgqnV3E#[i"mr͟q@:ڗI. ۑ $mcВU=O):ҽLw/k:*( (9O ѐT*U|8''q㑎'o^AZ- l&- iwc }770zل{ߑ s{ҩaERHѢT2:)gLe;I<{y/)@DVXrGAAloЖŪO ӵWX*<| g9IzO b$M5ܫEVQEQEQEQEQEE3cp۴v ֦RWbnT ;o!Srb=IqH3 ,Ghc:z{XȞu{({LJqNIˀB$` ^Ή԰(((((?q;v0A[jXƪ .݃ 1g8&niO5sLHHBU$`1GoLp%oH\9o{#h'V5U[UM1V 2xMQQ~ƾ`$)<8lWv"gquEǢ; ;_ܷT+iy.dʱyQ/@'<&ւ4_գDU^#19xB\3)t' r7?<UtE&%ӟcO@|M@P'5 V70'œ&7ޗ/faQ]' >&gG3#Q2{9f&m{G[wH7D.ۆ PA#=3+|O c^q/mFI'nF0x9ビÚց跷sҁQ#2662pz=HZ>7Ҥ>Ճx#-Kl00x\DWi8*){6> (h'7_Ioj?)k?Х??Y^}97-މ&u__bM_Hg:rV%$}01<`oj?)k?._o jє]Z[\~p6LFpI y>>͏xz' %> (iڏ Zoj?)k?y_.7-މ&_F!=.Tf] 1 wǡ?G-g/H(K$P?118xҎju[gQE QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEW|덧=yzW 9#Es2_=wJCk/|L(((((((((((((((((((((((((((((((+rq\?o滰?&](((Q}կMEtk.#%^[% _c t#BHC\/x" &i WFl-@16 ]/ďZ_M/ Z:ۘ&%% Ҫd]~WvvM+L\Ayru,~RMVD)k5U!?;@-fAmtCv3HC܋4ZOQv'\]:/b%K<*ˑǑXxF}/uѭ\E6iUṲ V (H465v&,qO$eY aS&O Ϊޝ_ķpx.T^.W/! H%+#RHڥ`]*ԢyFXWy%buen+s(((((((((((((((((+Ҿ O_Bֽ+E\m?)?.'į?f븮W3YWuo~faEW AEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP\?o滊ܳ5݁2wQEpQEQEjzWfn`7%f` 7"6"5` l.̑|Ab .Gc\]׀]׵෽Y$DI ˬfEؿ*@7 |+3E6ek~-K'R6򷓴x` O^Nt)Yc)J\N5ϗW#$)JuH#:XO6@$I fw) }E^vI~ю.@m/mn-^9-`* $u:%:Me՞Kz{(]I3%ģ101*$?ZՇ\Y힓lhY.7?0L5}ksk:\XisXHS3+>?Oϳ<@vPcy BxIhyDL@e"}~m!łzw'fi[6"?έ+K }Y]OP[E^RS09TUQSIA-jWڢ:e'D+,2;0A@ o JӮ!X-.h$.2!y%BªSz*?Ej Zh@8[bͶBu4QEW+OqIYn:a+5ʵZQ;e4v4,綣+.xr;[˅`б>d!x,N8Q9=I&KN՟Ź[۟am(`}96~mǶdɎIrZ6ttQE}IQEq9EX24;8F`g'$?ŝ]5Mn͂01mQCʁAmO,`i dAzz瞚u}ԏW_V}Mѽ:U7Ѝ,3*+ cpp~\sIsO[_Q U60F=Sb#qãG{xu sV:ysydhsGxՑcnw9H77S"6!Z{X\lg,'PyXbISRgSd _ɩO_ i֯4.yvſ?Q/$/-|NW$iDKlqUg彭Oy7*&S]j}ſ?Q/$OoDDG*3{Z/O Su<7xwV],g,I{/<[AMy'(MlխcRml(]Vw$ bi|<"E}ò<y14E>=d3ɋy(bi|}{IggOQ<(&/ȣɋy)Q'=;!L_?_F/ _`KW⫻4>%"V"({cp~bQ㳜!껁N7}EW_LKW7xKCᥦ%4ti|7c8+^uim6myn{}9dbȅU*͜kFn8Ə,(C4_d[FufSwr2͸*5$>wTgi6׺Յ{8]nRC^ ə7ɹh fVF\^7I5!}>$/X‚&c8*J}4"cU5]N?0GAsqkz{sn HfeU(^OoDD^p&]ST[I&ؑBHRI=qk :֓})"1Kk?%ƹW+ G3 yU9;xv5mI⍥Ft&B8'=@y$g)5EcPkz=wgUG\6U$'9 lۏr^dcKѝG561Y{q$#5¬s(.Jn=z}b>Yw;/H jG$Z5 ^d9 (k/Wwv?:?b~W-ƬDnK2 r0qАv> (k[86$s*o\rGs ?:?~ (h'7_IU9Waw<1aoRdՋi+Wu$!x.'į?f븮W3YWuo~faEW AEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP\?o滊ܳ5݁2wQEpQEQEfx $IT#Fꭾ2@(MCy~siPӚI"Y4 %c[YGWj>/\ɨqddA+X,a.}IFuZ]}HUh9|vɷ73+B~n`uyKCUmHMNG-/|O~ylmlViU2sE-<ubeh  + v mNnOkjP721Ց{K6Zþ)aW.@,8 o_jV_I[E3w7a6GbH⚗KF&DkcaP1Ma!X(0u0.>{y}{H[d B&r$3B-#÷'HtҴiqW/Efgɂ8bj֞ ú/#fFHhFˀ $o/C뇥61dp||ʂ{4,56u-3#Ho̐Ml +RX6T5-v%I#1KhٓoQ9GfWi6t]y\Ρ\d[ۉ]y <}럨( *@>nC0J|8%'5fosL7xQ<>: W>aGʔ;'=GNwP3gz HG!F_H9J$~f^<3fMao3s{QKY~G-go&D_UKT{hϵ[wBlgxZzoZ{IU[R 혜}__4h0ީoo="Y#;虝I=J5a#-<"(L(((((((((((((((((+C\m?_'/iO^>yQp>%̡c5]pPgf{4_ (J ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ܳ5W5 C+((Z_UP"h18fA>[ICy((((((((((*dkZͼ XVw q G|b}̎&;u33'Q灞T7/?-C, 3.HP$1$#ҥOci-;c=sП|-@oN?KM^Khr3!1ln^qVI;vt!bO`Q̒jvEakq'9q29kXx;37\gu~^ΕV[gB" C2Fޞy=>a]xͅ#( Q&uQ\XlI8.c,%y0@TuibvWʐlI%<ch?^%o_q9?.fC"DPʼnqԕ\3ػ[F@+Kѭ-tӤ9N(0N1,\8`J]&{^-EI`DҒ8c+`c;UIb@Q@ylj~]G/̶>ʱݥhu=+0an;b"ߟMugk[Is}Q TA<7o1i.['RFÐp}iu=FhVReq(^s:]Zſ?Q/$/-|NW$iDKlq?g jУ -T6ى' ?|͏xz' %> (iڏ Zoj?)k?y_.7-މ&z^&]NoWh.;ssq>Х??^EJ{I>s`TErі#{߻vsJR(+&{k^(B(((((((((('7_IkOG-gs6ϙ7oeſ?Q/$/]jibČ?ٲy_>]~7cqqںG-g崝Z]#ڟj-@kO55g'hfttcsBꭸ`3#>-މ&wE״6X 3=9 {OG-gsJRkmեJQdoOK5e5Eta݉4({$3'{QKY~6]WTxYhL򝤌= \)Bn&Zc(ZŪy~ZQ#xs1RjR F˂wHd{cHfq(iQXVr^(-`SС`7`bXF0dԐѤe' *PN89`Z,9ERVA'deSH#ӓv2RMXO6sDo"Um wV'JV1ӯ 8 ̶ $#<|ȐΗ(@WkWTuӯDem9 JSKg*?l}ſ?Q/$OoDDNR{QKY~ ?so<[AMbv'ĚuPKHA1`cxR]oZ}_lգ)*m}^} doOK4}ſ?Q/$ӿgR(\o<[AMgk'_ qB-%2$z\̻@cp@8CZڏ Z j_ ꑿ5hQ%VImJ(~cbp: 5Zcu;xm_$_Y=u0MK$JYT7a,xU|K_:۫K ='헑`&P2v2۵j7RJ\[޴*%ʻBS1X|KGiCQԮ4/?W7jU !> XN}];:4f!y|I>y<^Zo=1ҤK{Y)q02X_( G:77r֩{Ή,]ǐUF\7v<=[Zfm43uqvXTF% p@U` JţiP]Z_DdA$b8GL~i>x#d`s 4m__pYiE!b(Tgg+,}vc>O1ֻ3@5AE2+: ne9) I \f}_tH$HDr l @=.t];P\ΨSf9,w/![X>oHڄ>wrwjb[cBw(8?_ ѷ5'v63?Y4v1Lۘ,igVI+Q&$֩x4"WGQ bPMij?jxnMRTuH2܂;5 ^D_dsb?6b!ce$jF_Cq0}cijTq$Sq"(9lerxO\Z89LT1S8`sx $c䞧44{)TT;t'9OJE=FhxFr-s_sVE-{ikhP $QCI'^u 7aM:A}fب'v![^a͡JC7?i Ѷ|uQ0_4h0 Ѣ/*c Ѣ/*uxb]_oHuyYo! p22{;qX:-aG;?7 ' "_4h0( Ѣ/*xbzfT8fv(@$M"^3X= ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( qЧ5J/!?O zGꋆI+ez+2_=w5hߙQEPQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEW5,w`LQ\%Q@Q@qڞ[/.Y ȍȟX ;$_3Xˑu9du8-cy`6 %bBrv/ʡP) ( MZ!_~87#*7SWӝ-d VXRFb1e3U RCR+;HΥ,ͣ vIC(i٥,%cw|ĶH3oQ{׹]Dmocːp6$R[OF0kl)S B5azJlW/n%$)nH||õ2zou-W$"T{)$뜌%vW-ooqj}2GRsg))VLkXZx8a,m/uy ̪"&ć"BZi: y2In8"PKo2q~b$ Y49dY n9,  f(՞`,$Xc({9U<ى6 |H׃R^nO4-!W'i'ilok{m&$O%ǖd2fI,,AS@Q@ybk..Mđu޽B?].FaRtҔOMTMwz5h%2OOt&%^$Yo%jCƮ;]hH区Me'zuP`ыIw:6|Ӄ_?APQQ3ήiw'd{ÍRwqqdw5;S\p QӰ_>W C'4Ʊ4-9%qXN֮'xu~:DuykF:\jӮDө"6p&TF2:<[AMbv'ĚuPKHA1`cx>_Õv7?ӿY=;~u^o<[AMdoOK4}j󿽇*;=;~u^,-7Lx%ujN'i#؂ _'7_I~Q5}"HEDKa nqkJ8ΤSubqGQE QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEW|덧ֽ+E\m?)#Es2_=wJCk/|L(((((((((((((((((((((((((((((((+rq\?o滰?&](((Q5\w[+ 1n8_mUY@)|W,rj/k3\Aq*,PJo % KzBKi{cI."ZByhBĩ)ET2) >\-'4׈S"B#^^K U2aYwm%GKK_߁xkG[sDUULvKr']wĺìبb~y+l/h!ݸ:nܷkW?}@Aڥ0($sxw 5kBNuC$Ku $^R>hbq>ujVOi3\{N Ɛ¾Fx$:<3C7`4Y[iyeC! _c*N-ofJQ|6'S鷶3\ݘ} +2p9fgc\wF3iahJEKPťmRf {7)\ 4QEW[oOX]nscon:e힕'v6 (SG~L+"8OQ79:5boJ0?yל󎇾OjWrxoTF/Vg 6N$'@js֍= ޶%p3d:/xFzQjH"ܡ,J3ɯOPi4v4/?;T<94^\-"d*Iǡ%~{`h_ӿ)?Ƴ9wmy{rN 18l{z8VS`{ש&Jrc|}Q_RqQ@ckb4%;WbIN;wdaHf0r܅?PrVvZ|л HJta~0@,zt^uO5}lGHi;q;ӤH =:{T%H˜C[v%G)MAQ-  cGܝ9O}VCu;Ilz+qӐ:Wh2rYV=M}>|Z_k]~ngs+'#ZRo4yٲI:G-g/5(u´KjUcbp: ?7 ' "0ׂ5{.pL6$ڷk&3noj?)k?Х??M_4h0 Ѣ/*mo yR"w=Nqf"hoݻ sz&D_Uy'-WNMmq_¶j%"#8#qJU#%eճȗZgETjQEQEQECpҢn(B$꨸rvpJ^,FLHDQ}D; (QEQEQEWҟڏ Z]}E𭅺zޝ{+ĂpO%U@ Gxѣx]|BBlrVz>X=#D>[y{|7߷xGm.[Yq:[ OVmWN/*;r=(" ?(Ot W*h&L#|l'qҼfW7xMsl s:#xdhBI X>PpNG>Ƭpg&{ cӿԚ&Ucx$<jne4jP $^4Ymr0vylHi̡x{ Ưo/4OEVv ]xoT,JL$եMyEW&EPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP^_B~!6zZqЧ<t~ntPg_(eq_3_֏/Q\%Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@p]Yۚ+rv^ˡQEPQEQEW-/$Ȃ'IH-nU9G'P nc> ;SҼ#6sѹ+3aawGvdb Yr,i(nfkᵶiVUK)wDcdv]h^7n2qqC}yaٕl,S@#yHhN2/u?xz{}9@e(+iq8f#HS>_\(l˧ZW:$wBUr;8+MnK[Kw,s I~s7 H.QU|&r}6[aO0rb$y ok{d@fˮ:*( (8?|WOmFVWf1#?{ tI[>yk?TE3M*1Fk<4g/xٵ{h:_O]#ϵ_kOV3K7\ă8T(Ln2n=|o"s?:?b6KXzuèWu$FǕiaAG-r䏚?DuyhONgWvZx/??d_ב|RӠFm?!W?i Ѷ߆^(`$1F2"6 }*ql}::l-y9cOɵN?:?w-r=M5o ?ӿӿY]o6jEՈQtmڽ>p&TF2: Cܿ ?DuyhONgWvZexEMcg A?:?gx&KXv5{RBʴA>6t_+(((((((((((((((((J/!?O zZqs׏?T\7:O_(eq\?į?f븯GŠ((((((((((((((((((((((((((((((,wvgnk/?Be((((+k<\tHI!9uȻPᗴ8t mW.*]0mb [y"W[h|VP oCR+;HΥ,ͣ vIC(i٥,%cw|ĶH3oQ{׹]Dmocːp9/%폅'hi ] PȤ+0rrXl^"LKyy. VXɅeqx@{Ylt>,$tH $G\fm,tquMsQvX!Mqd$m WDtF o"֯n~Ga8 $K>`PH$AWϨ@ UEK"y 1`"~<[AMdoOK4G-g?g ?OoDD^IJ=F/[.uks7ԫ[4 {U998]R"w=Nqf"hoݻ s)Q_z=FqQEAEPEPEPEPEPEPEPEPEPEP]6&_jKp$Q#ƅy I gN=Ms5{U׎jz\Ido8-g)Bbe#hiC`)1*xV_-DAms#p@ܿ\۝䪑e_J;OS'ymI'$ּ~i'JV3(؎L T- %n:G<}kGɓ0ZbC C?qG[җXwFto,PW (,(H89O\+Ϲ FqדZd$<(zf7ĬTIZ}O>e+S2?L˅`3vߚZ)d sJ,Uda tyK8sT.RX-ɓLk?gȟișSsDo"Um V'JV1ӯ 8 ̶ $#<mQ yYo!+ew9>QONH⽲Z>7Ҥ>Ճx#-Kl00ucW?dz)'3c-މ&xz' %wBlڏ Zy_߲xz' %|N5 5}!."+ʡ[cp$Х??X޵~ټ1FSQvUims!%11' )8 ?6> (h'7_Ioj?)k?Х??GQ0߲xz' %|NW$ZJdHQvqg;# jУJ,ڕ@PlukJ8ynM EW&EPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP^_B~"6_'i<_ ΓW3YW+ez+k3E0+(((((((((((((((((((((((((((((k?sE݁2wQEpQEQE33eT,y_+pp^FҊoxY["W$kB8^Ĵz55oS]kYhZ^D=`0h!,Q@wE~"\!W 6y+b5PT#<*ygkך)6Vvm6V1,%X]Ġ t2t!pTT4Q@M=; [_jZTLK"ɔ@iV+ )eZMjx.95 ;[Ӕw0S gsB Q@^-|;Epŭѝm F.ӮQ[EQEWxGۚ( /w~05OO5$[W)E1]Y2gtFah7p/K~ǫ(Š(;AM>Ih͞ ?1?xiC}1Tt MWFpL0EOUgDe"^N J,$w_ s21EkgĐL<08ußk^+I%;5IPIwASQExϛQE?nђQE4{gEGQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@mE5cYxB*(=(6I `+$0e]8rی08%e6)T@L’;(#C`62@pz!A'{TQ^R z3T](X((( ? ٢B+n~?}m(;5}G(Bu x[w4Q@QEo5@4QZ,}PQE~sQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@zW SExEs2_=wQ_3_֏/Q\%Q@Q@Q@Q@Q@Q@Q@Q@Q@./oar-2.5.2/docs/schemas/bipbip.png0000644000175000017500000005341411757171206015116 0ustar plbplbPNG  IHDR3sBITO IDATx{|lJf!aL[-Q.r9@-ZxRq|R9J|L;˅Y^xk׮<@YYTb7jA9lذr_5yqƨQ,Y"WH9rȥK4/ CyyybbիW RQQkךsϵk\&TSyLsssXXXsssDDp}-N:W~ qqqkZ,_5y̚5k֮];qD5Ǐtz>;wʉsQccc߇@1==p0/ :΃<U1\n hpy F1@h?ok*^Q&@=oj^Z~cm%[^Se / +S{`0lܸj&$$|5ׇk?>22r̘1mɕTX]]5tВ0??Ȑ!]tl*555-\0>>>66v͚5@/B׮] V^^.F2bH=NY1xŧXqAAΕW\waii#""^ZZ*KOXsܸqٷoߞs-[vSN]tڵkrqȑ#.]\q ŐWI{i_dA) 5۹ }-Z(##_x\.=ţFZdIkh"N=*͛7EQFQfk EQj[9AXZZ*bUUb WXQTTdo\`ۊ)8#qвȊ+>ijf;W^1xijJfYYj+Oq|||L'1ZjUݻh4=˗%Fh4 ~(ǎݻdڰagϮ͢(:TqAR IqFTe1Df{V^1xi6,,L,lC1BgyTG;wn OQ~Q¤:Q <ǽz(W^Y7F pH%$U S\dbo X=+/ m[8t#?&6O*{%rq: vW^q{f:tH͝;7==ݵ0++_)))5kT8uӧO766VWWGFF9sg}׿\f͚k׎?^*;yիWR IJ*qjA).bZO\yzj1n8\zرcr)8qbۂ9J JӵkW6mX,[[FGG7W,1cdJNN͕ }}ݟ}ʲgy&...&&fʕ@K,\"o|||\\ܛo)8bH-CefftSmAf+((p Ê+I&=ydzzzn'((>E@ϴ_ 8'3fH?80|GSLQlO?ŋf=ЬY>ofԨQ{ƍwܹ~J.AΟ?_XXۤ;xϟN2ܹs={\??|IK/xo/zЯÇ744K]tzJȹsر[sTSSSTTp8RSSf#Gjkk.]j4 0mڴ<T/X5):t(<<`xᇊ;?yKm3l6&!!Aq5oӳϟҥӧ[o߾R>gT[Vך݊d0%%B~'r`:7mۻ<]* ճgK.y_t)%%EѣGAFcXX|muutc ,(//}ĉ(v=**tfffjժFyĉ/ `X&oUnllܰa w>vX@K\1y>Jv\ h㓵 ߳gϾ}bccSRR#?~ 6mԩS^{-;;;==}V'x"&&&99O>s gZ-s=gAXdc=f6PMDDD^^ަML&… o. $12>۪PWkC2fsQQQ޽}s#Q|wp| 4GBڮW|탠}<'d1hۋFGn lFR/N@ ÊckOp_@eatmVUUN0iҤm۶THLL-uf]ڵzW(---&&F\Dmا@S#WAJ?lmZ.\QDA?͜9ۏ,gϖn?Q:'vva{ <Ъ2ō1GIKKX,gϖ"xiLWZb';wׯ_ll|OA~_d2W*A2ͫVȰl/٩qʢEF]I5f̘ql:Qŋ߿[nr-Dq.ޖѳyQ\yvЩj˗/wMG&Ni&`rѣGoܸQ=t&kC#NC*(1*+B~˄~%rX7bNmN";IEEE>}mҘ6f{ZNRRtw>?}iiiNS ғbnDFF666 ^٩}EF|zF>.d2566jY%GS\y N5.\c =`KUB8?C?wE0=>9L[߀pu1Upr}R)(--.yԩW^y/ׯ_sssTT(Aj5;[5eΟ?_В_TtWak&iӦ/..>z7`׳Zc4gɆE-@?/i֌2"## =zԩSKcj֦k\\gmmmݥo͛7755y R kvjEF[.>a2!eT>ŕoUگf 4hܹ*(gko|*d!AirN?;E0O*-n ܱcǼyFnݺ#G .ZZ9zk>t1c 4h̘1׮]3LނBڧyQ+/vWR\-"(7KO^PP0}t`׳Wf}w U*Bhsq>fq|pj+ZtY|B ZG;;;P!kBe{L @۳LmRœ1H.\0a„h/Kfy#GLMMNp8 |ĉm&j>k{U?^hy:4`O:XWWWo`@ <&ĹJMŇŋ>]ڸqcllnj#;'7nܸ|Y58p믿Gܿ~zŪ]vEGG777?CGu'|hѢnjϬO%2m7 ɓ'SSS.]ڭ[Ύ%DŽ~>Sd2 6믿 /R.]>A;w;L"Ñj69R[[tR8`iӦy^ͳU۹Z|ӇT=fՏ~H ]Vдm|~_^^nټO 9.]4}u<*9s***J=N<#W\SHlᓢE=Pq{헍rKza4˩TWWK7,X_={͕NPo=pk N9Qcy*DJUOgϞ}ƦٳG~h4hдiӤ*kׯتs}ڵr}}}=l"+<<|֭+gqqqLLL}}(W^5LUUUzUt ]wOlQN\4,-ѣG<$8p ))tJw~霜Qcbb+Gݼy({;uV>U߿wpzɛ7oV-544ƚ:ަNnw>}G޸q[>(..n>L*wcy 0@:(&M_Z>KMM{hs &+ݐ z@;|N'|^>fl|QMUTTHGH=T @LT&QK䴴4w߹>25-***)))nul2y.]<ÊyEDDCv5ׯKك2C̘1 _qS !?]s=]|hT$SBB5|yyy6m2L .ܾ}̙3gϞ={Ν;WWKZHklٲݻw=:<\*n'1c?Tc6= /l&|{m஻ڰaÈ#4/cG$ )a I S}(ׯ_OOOvO<^W DQ_fm-l,F~ӱhǏ?uuu6xˍ7rʎW\x J ye!,%s|<Z;1S6#REno ?]vM')T!Sl&`܄k3e! (lTxAM:1vrK_HbS01@(r8s \/",/ JrO4D*ԷP@ " Yc"Þ IDAT @se@K]~\Ʈ@V<g6=);y k6Vmt$=a?//q/+W1@c^"zE<y +W1@c^"zE<Ы 6 80"""%%׿uSST޿?Pq=zt^бc]Zb+~ݻwϙ3ǭΛoK/ݻS"?01h۷?CRɥK_2k׮rʲe{]w)vb0AP$wvZPʼn'%}{7dA6o_$1دύ7,KX޿񕕕>lذacyt-~jnkΝ?~gDCСC ,\tĉG駟|ɒ%K:)F@u?? O>=eʔӧKH>wyW^PCq/Kݻw?~ۣG'xbٲenuO?tԨQo:#FpQho~%W1@c^"zE<y +W1@c^"zE<y +W1@c^"zE<y +W1@1 C~ڏ< x+d0t<!(Q@1@(rEo]@cFM/lS1@cP1<Uxg-Bq:79/U%<h/o ߸ - y v߲|vU֜l<h 2"6 WZMNbDQKS3qqqfyРA/_v ҭT߿nݺɓUPd"ŷرcw!nOJJzםNg^^^tttYY(111sinn\|y}=[o;v}Xl=#̛7tի!11177Wŷ~h4nݺյ+Ñp8Μ9PTT$5kxoڴ[ͦ/[n?~I &#}7޺*((0`[DQrѣ7n(bLLɓ'򘘘oUmhh0rW_I}͛7ݻ;8-9p@RR>999(:Α#Gu]?OUj߿wr(ZŮ=t&z1ɓ'o޼\k)qk7***)))mZsڵ}䆮%eee(v}Μ9  9sXvJrڽ3]gggϟ?ҥKӧO_n]TTz? m.@FBB믅/_XsΝ?x߾}qSN+_|E~DgIHH~|WΙd6ٳnnzf͚xcǎ?PKȌFcXX| Nuuxt ,(//%Kڵ˵{Җ͛7KqȨ<| G=ug<(Zt8qŋ <ӏ<[o/{٥K˗755ן8q`0 q(ݻwr:nUVq.@xժFDDmڴd2-\p={V9--mϞ=O=ԧ~*fff3fРAcƌvdR.22rǎ1bĺuFV!<Z__ߣG-[cǎ[(]"Fz„ [niyc3L555EEE#55l69rvҥFqӦM˓*/X ,,L;So߾|bhi驧!22rܹ;vpС_m=|pLĽ3A2eU?Io^J?xi=eee}l6ޓXVc= >c hl&!!Aq5r唔 ׯ_ի\2}5 ,X`ɓ'u]6ٳꭦMѣAHLLͥKRRR=zp{4!!ʕ+˗/^Zф2Ta4c3(b{ oO>q(ݻwr:(Zt8qŋ m6۠AΝ+$+Ɔn?Օ_~ʔ)nfff666nذAݻw;v`aG322n߾;{9sFl0o>A?^XX@y oUUUO}Ν۷jZ,{n+>}zAAӥ?~ 4mڴS=i&ɴp۷S;v,[,..nݣG׶^{-;;;==}v  l.**ݻw q]wmذaĈ7{x>p۷o{Ύ@H8_@(^}Ѻ:GDDtV$lB @ ^/ _ hAE$51@cIF HeO cM @ ԐZ& @*hHbGL ypMbx@BߦǚpE;.7f,co~<y +W1@c^"zE<y +W1@c^"zE<y +W1@c^"zE<y +W1@c^"zE<y +W1@c^"zE<y +W1@c^"zE<y +W1@c^"zE<y +W1@1 C~ڏ< x+d0t<!(Q@1@(r4rM\DQz1lWh #Šw1/f- N= :c< 2-rEB*!C $ gv^z^2]~\ALd-Z g=[#p*- 8;+Wh-lذa]v}Td09 Ç>|xiiik TEѵOÿyV^jUJJdZj{SqtDqO+V(**rabbbii(UUUEy(vh4JV\Ų29>UµOq*H{ Arkܵgz׵O\{s8gOo%*sטxV\ŕo4?_>%&&W_{'L$BMMЄ n*Yrah"NN81tP4`|p…={ڵСCO:8ŋ?#:::99Eܹsܹs(>ի (B)ߍԸ=Ƶb3RSK{-Ǖo+.ʷvzsAÇ^z.]DEEw}_WN:uڴi&_޲eTs={>|*WXo߾p>U,ZhW\yݻ9rDP+>#Ep2w-q*R\ŕo4CV:Ez ݔ A?}iiiNso߾6f{Rc0̙'WY^^nzOgo:^>%&&mv-aJr7pa޽6M;f̘7>… (:ұޮ߿?--37ok֬Y?ܿrrr{+< ۷OǏjiظaAv}رI&v޽O> l޼Y:Ƴ(Vjllt:'Nx O?#<[o%$$jfffveMMM'NR+g:1Zvvvzz3334۴idZp{왙9f̘A3ڵk&ɳaxxo>jX{9ݾs/bʕ lڴ7(,,TgieJJʞ={|ow]tC<=y +_ zJeGlذa]v}:tVSPQ~~!CtbrssUj*.@ thnf+W:;64iҶm:; Ay̢E222ŋKƍξ}ɓ8p+W|G < 7oZhTgݺuk׮[fͺuBŚ˖-;{S.]t58T\\\RRꫯfggK%/9r7N ;\)Xh̙999JMťSHqAj^p9sfȑ}=~fJ? oZI/C]__%ʕ+KKKEQX,rK/4z5{7ߴ*`אAy(vh4zH}%9w>UrkbŊ"ݮeZ]N>OqCŹ>}z[n-ijj2t?.Z~ cšEQt8r*{F`<3FQ`Ϟ=+¹skFá%NŐcsJs}**6?vXVVV޽M&ӆ TW\GS|:ǜ8qbС&iRaLLʕ+ ֧OE5ϰ>}ݻWiӦ~L&ŋ?#:::99v{믿~ߤ $ļ{ N ,KLLKJJ_b„ rs@wwUcΝvZ<))γۙ3gΘ1'P$]E!)/H}%9w\ZOB`PcccUW\:ŁqtkZG1}ӧw՜9sԳ`(ϝ;'PsssEQ|Fx}|̸q֬YаzcJ vW^Qo/Ԕ̚5K*[^^^SS_Ze !?~ڵ rq> %6СCݩSJWWWGFFt).ӡ}qС_J=Sd2 6믿na;IDAT~=_#Gjkk.]j4 0mڴuiӦL_~y˖-**.@ th]/DQTSQQDTTTH-t#22cAHLLnLƲʾ}l6{j jJ7ӳϟO׷-l VTTHזȷ=c0B@K]}{l61c6n(~7pkhZ+߿ZZzl|t;..NޯypYYtamϡ]y8{tt(bLې(hE1µ|zN P m2lb'c$>|k͔5klڴiԨQ~PU 3*\ׇ$|d Tw]tXmJ 3`_8@@pO *a8_1@:-i:-a  ` د󖖖><""bᥥr /еkxL!Ctf溎"xA`0Z*%%d2ZJvSS… ccc׬YҧbsŁZRRҊ%J:!EmҢE222ŋƍFZdJ3gɩ-((FrG}e˖={ԩS.]vZ}*rOHHyfVV֢EZluc\!C\~]P^^x!CTTTx*%%駟Ozwv(rɕ+Wȷ׿Ƚ5WgϞ_~e^n߾}wܸqUq>Ʀ戈!Uhnnv+TTXXꫯ;v֭[ӟKq-%pn4#\qpADQt:t|<O@+m˥ʸ8C}/_sFn$&&{kڧbsE555R*^( qƭYacǎ˥µkN8QԩSO>X]]b9~wy;S__zjp̙>lyyyMMͯk>+zkjjJJJf͚^( +W8p{G~#ݿ|ԩӦM3L/-[\Zhу>~Ɨ+V,]499o߾r~~}fYOrrr_޳gÇ=Z2hQA8>OlٵkWjjj F4 o6|ڀ<y +W1@c^"zE<y +W1@c^"zE<U01?j bUm9yL;:OŎ֍;q=bdee]t=jϲ{ )=!(ΝKKK{:;|2K.6-77W*ʊ:thIIT(Oz[sEAظqjMHHTFWֵÇGDD >T.vvA/..?~|dd1cUjjbso3j'O<67<onիW322vK/ M3s̜ڂ?\*|n޼h"P P\\\RRꫯfggbEeddTUU/Ǎ7Fdɒ.bƍξ}ɓΝRS{/{|VZP,\vo5.]:lذ7n466jBINN^bEQQn KKKEQX,FTl­ 7oEnGҭj-//EjxTfm룢TjvFng aaa <nrBpK<B8k&$$+旫O^B19vXVVV޽M&ӆ Bh4A0 nݶ\=how*k6,,YEeYHeFn޽{FsvFngٳg򗿈-ޕoOf{,???66VTWWmTө\[so>Uo]zU#^z5!!AK3r G;w՞5[;#3jUo߿h4644XQQѡyLBBBYYS>}:22R*zkjjJJJf͚Zb?~\ TFbܸqk֬ihhXzرcrpڵ'NTi2#v+։[ȭQV]wy~E?OV\Y__ku@]jg60 <<Ϥš3fL\999]vUl­޸SRR2lذ/_+dggGFFJWRJeFnoڴbJ*5[;#3-ذao? =_5xe˖iK8 w`MMMܺ͝u;שּׁlC[ۼ pٵkWjjjgG/^\[[>`g@@0 oVgVkϞ=wv,@~% +@a+W(^oƍѺ4]+ѳõy|-ځt2t9>F:zڳgÎɷOP~k;q}jm y8c zt:;: imHڦ֦q]I$MDX iEH$D)a]APR{v`vIP$tc@Rs]a.dX竨ή|bKDz݄rmyKv3!djjd2-..RR=YYYB0 ,8r݅zvǛHvLBHěh=_:JwDZc=!/_یHq\[["N|vvСC/^]ɟwd[VWWB={vرllllmmț7o`ssb ,:Niѱ1;88Ԙ B%tZ3yeOBH4tRo3?l & <Dj4L~{쌉?~|ee=cv֭O>E"|hz)(c.Hb&%(+ݓM>aeOBf9_ |>Jɦ;eV^wTjZR;6(ĒSvxś+uL1%D=!ΐD]yzw1&5|6c411v{ _YYFlp yd0$Xo߾t5) &"T%ZfP: 8M>dOa򦧧vtJIWޝ`/@fvtt,,,9rDhakjj=z@owޞ_~'N|qqq1 iZ!ȲlKKK88)|dǔE۷7B":W) ܹsdL| íW"w%Çoٲ999bKJ2tyzJ}}}fy흝 O>=pF)--} çO62,1e$D˗uuuΝʅdcR^"h4!aVVVdBNׯ_j4H$VLI6 VdQQͥʅ: ﮥ_'pxyyY(Μ9sP($.O$+77n߸qBTJ.cFOe[ZZ0qMMM f9\~] 666?+WP&pܽ{WTJ.cd8tVUU-[[[@AAAEEEuu5۷?^TTdkJJJJKKm6۶m(˳.HStI c2Ͽ\ c@PRBJ: u (P Ip<Lc@ tIENDB`./oar-2.5.2/docs/schemas/bipbip.dia0000644000175000017500000000414611757171206015065 0ustar plbplb\Ys6~ׯ( DwT;^NtM5IH(Rvԇ.H(<uIlZ]߽ GWccF8`jǟ_{ףF5'[ ZDjbf:E>!"(d)J_d DH3 ^q"TȖ^dy4 9UAØk7$ZexZ#k|g]X(?d UHjȟh T_*n>;ZAbKEUY0?a>٦//lSǮy7/-.+.8~Yq,b.8a*r!%Q.Uv,I*vjXFw+&DDW$L }ڮ5g[rQ4]9"7,a=dм6jMDQM:eMP2M MA6}jNL~`g"B`֊\ ΣYRkC,W?Uf+ {q8*Z_,s؎/A)1)F_bO;3G~jfQEQh@:pi"2>iwKnIW' d!aށoHTd4QI J:Yɑ͝SsY&!QRu^~ lI^b`Xn4.;~TM|E 2<}K^40u#>,%PG zjW<Wn N|dnesgh;TrIi=B6jS]߮uH*b͓A9lٝ smiß,:%`S{"*:(ښz5pС3nJ̷q(& Q q@DOPy5Ýw~Cl?n.(L[=KI^gI l^q)wzCKm~2a07LހFv=13 6sA ⻵֢7ȅ}>C*9 N6@a~Cj5kp)-qsT 鱶/HEq@ ~4hD4EF D/7, ܚ Q Nwa#goFS.c1Cmn߈ VU?kcj ]Bìt$ ڎÈ) bD`XdX| ”Yr<:?YXwX%~Yk0ze]cKK^Qc>`j (Pjed] 5PL4&\2V_./`0RTpgG R}F9w&c9JvcYg@'C*S}GVO&sOfתrv/4g"i Ag;Ȼ9=4KUiɣR+5g rWZqt`Ɋqj/ƸqyfokTjϢ9R\ 3A[෩4[gDMrev6+{12FZL̔`6- |C>r$Y/NdM;2 "3O (?s8z"tZ턂7&^p[<})?q˨z">5oz4=wq0$kΘ05FYj\5Bk-/A,&kSYu|T @@?gY*\ӥH9$cqH-RQ$;Q,j ZaX.;FrinK YD#۵"MwQףs,?a./oar-2.5.2/docs/schemas/archi_oar_en.jpeg0000644000175000017500000012617611757171206016431 0ustar plbplbJFIFC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?(3ƣ:^uQԠB~@t \~s~R|Nk2xSD!E8'.@<(7'c@_G#X6$Y]hr2n@HPN02NWY^Gko .4guA 8 F'OjZ[ƩZXdbR'8+51ס*: +51ס*: +51ס*: +51ס*: +51ס*: +51ס*: +51ס*: +51ס*: +51ס*: +Ş#wu&vCr;`p ؠ(((((((((((((((((((((((((((((((((((((((((((fw=5d[[`pAJҬt=.L-MD2I$I$+k|[34if*+VLaqF1QEQEQEUֵf>Pk+@7s#ÐcMQ:FPRviY9+@N0+?0ƿNd{+{i-3 (nss 4?C'T]Tk]oHx Jκf XPЂ:*h.OЩ&>(uGR为d lHJÞL金S!A ?  h*h.tX;fL;08 Zt 4?C'T]] 4?C'T]ZZaQȷ=g`0IP?'T]G SCt?MtP?'T]G SCt?MtP?'T]G SCt?Mo;q¨$jjZޗoyw )6=p*h.OЩ& (qBKY/$o- =:W'? omFSqxOܭy}hQ;0|} u/ }3KUȝn zT*J]gsoqݭj2m<0B5-#&" eluoy4[x`NNv{#[uyڷ ?9A`U1uuz-oDO_x&pʛ#j80zMG+ۼߵ5yVݾs1qלv4;_6: h%eӸda i j21b }:TbTAp>ZE6&KFybey#6,dS w>xcOg\y)3D@0w`754K0*ϵrRk ZkBu6ͣ)P/'v8<正j>-%Zɡ^={`>`UV]eH_ r"3;{}Iiᤳν 5VKj#@1ڀ2<s}4oU-.t7\+7ɪIx{'^׵?y7Fݜgoow: ( ( ( ( ( ( Od2K#P2Iiq{UE+ă&HTwP;U@ OpH.nbLcnB@w)?1dS"|Y7t7HM*[wHUT{ t-CᮥKk+6 K"EU1O=c jD@5I.qh}6u$ tľOEl]ju>FƶvzE+l/UI WFeݎĂjĶTbjĒOyU> jz[ n5Yki,n8Aހ;yzv1q>l^{kox_Ymt;xh"^jQI,4{FD2Iҳᷱ[[^52q-+yT1n0=a?x˧} ٪,X{(z|CaƳogiZ†$p=b "yjy UI.o5}s3ϖRN9=ht/[kwv?cZ¶vYXA=+rO]~,/^eDxg22Ns s6d}Bг1B$9{5𾻢햓\ Cwy=Q&4 *n颞ۑh{rE^((((((((((((((((((((((((((((7t&cO+c $(@Ӧ="xo iI_.' C7 WW֩xTӼAũY WF` f(((O|r`!Ң&}Ży~\Gq@7t2hwSCg}1!wq/! cow"\GswTUROPQ^o.H7@Ey]n#zwFɺ?ԍtW&R7MQ^o⟄|9w_-mvoH:@Ey]n#zwFɺ?ԍtW&R7MQ^o.H7@^ u+8{mඅyy3՛x4&M+[PLXZFɳˮs9`0}vP((((((( <= oޱ%#EX ջ{;[31Yp0zqSOgku,\[C4BF'##֧*:l-qȄ HeV6;oۤY7Wv6qwq rp2OV4دn+O5;~’m H#'Ҭe-^$L!Tw_G4WҺY<;K5h{j (ycocu8|FZKȒE"GFYO :}0)Fiu,rB>@^Z K]MĞhhQH| u0^OjQ@^4۶[4Z9%ƣX^N %ŲH, E"U `0(((((((((((((($R)g('*8Fpq@q:FϝOvUǑ皩Z.eWLxZ,ֹe}䗓Yg9 uYf |jAz簠"@TP0%PEPEPEPEP\ )u[Ite# {47ǀW9]%Uh1qe*mKywCn+r'5m>mtMnOv\-<N+(((((((((((((M{^Bfե26q>9ۜ I`WnZ[B5eR1dg=̝ҞMJTN|BmOLع9LTEҢdLGBY`PA8jWͲP7"R ;uǭ[or(|s晴Fis IwIXjAϕvM1X;#8KRŶ\\"Fae~ g0hޥ[;r ٯ0Ĉ|sm P% oh^Zm^Lf[E\bƽ 6v_=\Jf^0X6*(((((((((((ni{ۚGl!?ƤUӮfX`V̬O~`-E (H[P0GJB«p9݌sP&[u"] BCjmke Pݘ$'RGE*"0EP0(((((6qmbYpA8P(EPx܁6$7Nznd%сVVo$Ow4)8qٰ<~\@/E (((((((((((((((%|Wg7&9 @ы21++c֏-eL824"`9eTqֽ ( ( ( ( ( ( ( ( ( ( ( (eՌzXH9Y!($ Tbr8`ſPinkϏ9~qcoߵڅ)=cb&CxLNa [U%X + 3ܠIU!]C L60c0H?B OErܡfA{G^Svym=@Cu ( `I 6RcITP>TR; K P%&zwV03x/> OLԡwQ% "(UJHpz~>m2FL|2 + .7>p'wߴ5;9,?]ɍi$ WU/c>=sA*H+fWR2!AK[XU: #ŵW7%$%0 nn|C /So5P%P-ƏD!OנQ@?)@?E?(^Ey"_hK[z|C /So5P%P-ƏD!OנQ@?)@?E?(^Ey"_hK[z|C /So5I"EI#"pI5KʛQ{{cbBcwb>e??qoĩn3@&>3%\횹k_G%Ĉhzg*yO]Cm IK#Pw*J:,Q5܀̃d?*hEAQ703ĒRE ~?4Dof#в`ӥFo7itv\F'`p(Uh,db2xbN+Gmoآ@ΰ V ge쀉,|N\3Ukim* R\NH9<lJEQ@Q@Q@Q@Q@Q@Q@e]Aj[:ڇUfS؁6Nzj@br}Pk{d1Iit Չʕ=GP05k>?n{yV Sc,A89BLqUbH+էwxd?Pa(_u;RAH Fʁ^mwmy)pLMU'-fTnOCILdRW6 {|0?ր0so"Kr=4cQEQEQEQEQEQEQEQEQEQEQEyK;>ɪXwja@]H9UVf? wO ռW4x䍃+s~;un7]3dW?S_'h-ݟ$gqZ(((((((((=>3kZťV5˰y>Zڟ'<+kg{]f{cn0Õ@Q<6\K0DF dOk'o~nݿzdk˔H1ϓ^?GZH a =1 arr0qiN[X]bR$(8gU?W q/IZhh,'b Q'&D\7ᗂ=q;>umӴl*PX+WYEyφغߋ/ZŧyaK7\)#$6SMR *G[[U8瑹y1x^Q/T20A㼹KGH~2p[!G?­"[ _^w2Ӯ,yUI8AaҼ|T«4}~w *N+`q@d=z04^#_ \lXUxڤw7zwp]ɝA GRO7VEJ$E `+ԾRu:PW .iͻP;TyEyGĿ [-57F|j9 D!^|ag@v}\BXe 2}TtUQYz'4_6i|C(f0^N VQEQEQEQEmo$6آB'KoIǸ݊p H)*CCa^{w-v((((( #\./0o߼J19NGX,d>$r4VЯ)8 eJuoֺޗe}ڿm,LT2A\sj=[Fws}%K }e ;GҀ3s>X5 -k峴hKJK6СXnɧ45 S_PJD)G]TW*|;G{LV=An$d]V, ;ԶuvSmEͳO1Xd ŋ'Oxᔘ˕˻n$V,T.@[>nu']cC]KV] ߊo^XxJF1ͥ0%ٷ tUt/ \i1^jP-Ea>]Ke$0(=_[BxwBK$KAysuz(K1*wEkz. iϮj>sxnww [<7uuCivFOnr$zZJ51 PcuFw /g4}cKkdւb NQ#'u5b^񅗈4"{]b<Ͷ=a+'sRiyڞ7PU7 鸞I&o?H>ѳ9.ٟ3 :m9j|kXx76MS[W&Q28uuYxO'њMV)^jcXIr}5oQ?h۳HnկĺFpVhQo`PH r [+\]^Cy7rj'HD[Yw OuBT[&ToA| '99>^#];-iA$-*e_yb2F3&xZt}8iKugϟR:;Wv;zE'ÚW۱5o\c\N=oִ;\zf4&̒7~}cARy8#/%E44*Ma}F)He9oWؼ[p<h 1#osxs[M6O;Y"VpF c#Ê?cI$±+r͎4(((*]ZتH>;ΝQE ((((((((((+Ŀ~;PgNPr`v&@_照4yn߉Me>@EPEPEPEPEÞFmkXp}Ҳ ƹv 5|'弗V K/`.ݣ;v{J~+{+1{&4.N -O<6\K0DF dOk{,,/'ֵ%=&^^; ?7=* Eo_&72yVt7Fk}/OGk ĥHPpϰC |i];~偑`G<r0' |EiG{ocA$vq+S<|WQ@çxv$ID4#`ɹ`y]EPEPEPE'>}rox**>5}eyM=f?Ox+SW+ix(u C4}F Cf%\b}`#8,xc_t8t"*>YWsݎa;@_ x;JUckn/ҟFrǷ^gM{=>YQ-UC]/.s+ :+/ĺ7u}Rci쬦ddBq( @~!x>9[k+*ךz>Cˆ HXzw v&]һ (((((?ԼEO&Le8Ãe ӓF|{qڏ-!k=M/jV /t[($ăH]|9VuK(_W*mmQr@=EnW7ž(vX';N+F#`p=tW|BF{ (}s;odBV;d#?,T-fB$a]tVpԲ1-al _Z7W֪C.91s³A*Ktx0=@dQE ( ( ( ( (/UoU\xs×2[Zۿ1u3`b?Ox+SW+ix(u C4}<1GCH 噹y\w=~@\ҴK4hdQ'E̒rI^&>/[R챗ٷxC@CkoQH8Ppb((((((((|):c{#DaL<+r$`r.4|YQճE/b6ǫ@<[cKG ;ɉdTUoO ^5&YIK| 189iT-'MmT/WBl3hM&?/{O<3MՖ t#Ib%I'޺(aEPEPEPEPE|WWYi?n x2[rU~* Pp\wWxfbKyqbwIv+ A8|(ƹoohcm4=(9`[ @jzy_ڭ/w c8FqW'?"nQv:8eB{AW4υ~<߳xfO7֦1׶3z ,l,8- ;!1.I' 8$ƀ8Y2{a[8Cs‡X4[W½XI)virFF:?ԍuP&R7MנQ@o.H7^Ey/?|/gʿko'wF6sQ )>iXiX`A^Ey-:WX)7,y8Qy"i4%GT.Mҽ p*y5V~hߕX9n38=X㼰L $0? \=/^I{ ń[U!O~|=aO[/<:F@PBMzq昺(C ( ( ( ( ( ( ( ( ( (RH4;%̧Bݺ%@ ZOapKŽ{z{rA,L ^}\Ed5[2D ufz{ɩfФZuꉱJ'!}38~) ( ( ( uq![kTc61ݟ0M]jtF, }C@T$H8 Vlm2IU0*T>ƬU[L܁n"x_#ߨ-QU a2.˃X)*zg`qHaEPEPEz}ST.dz[Ђ>t R_kĭrM—xrP5=v0|f\}~iV:oiYۦȢN?$y$rMVcvfml$NI'I'$((+{h"Dd)i6D8V+ \嵗POT~Im 69=@(((((((((~AoonS3BnflI @7bm M<K`vjoiWnɹ+ pw:dt:`Hn5@>\G)101j%YKP4A9`ۜ/cҴh$QE ( ( ( CeCn'u]T%-a#͔sF%XeXrSV{MI<9c+[z];L.hz_, \7ǫ6Mqr|2 ׎۽YV61eN*TbB=4'MѭK,`g.±)l3*QEQEQEQEQEQEQEQEQEVxu6ʪp "g<:S[P plFѵy܀L30!Gy8xO:wwYӵxK*6h3w̹ N+_>agGhv{wRJ$:+GKFž* EJ_B .G$?g߭~i+FC<]O%(|N<6aetiy@|Ď!@Տy6e_;?7gH:}\53uݥ*C#pT GI֯-:'ӥn+-C o^1-]*{"SREbOQ*!wh~%Ō+\Q:/\= ,/n.oI-$Sl&K4I,Neu9 By^!/i==&m -|/83ҽ'igXYc C92(@#jآx5+/z UѱX1Ž_Rxr$чCM|6s&!&H GSk(s@Ey|n4x_j6WzeVpRu;w_v7n1W-]=2ORxlhIbYNz4jz~7[+E #O<[<Ͷ(3I⼏HNҌqjS}[}2o C9#=g>6lĔxfIF3@_I[\ijBH܌SbMOPkT͵\ 2DypFB+.wpAާ~3[O b4%fWla0,*+]p^;K6ڴifE ,R0F5P7Vu`YH*Gn8梶\5C:]ȹ$A#'"[Fܥlc `A= FFpybzVE "8f#@F =y:X,_o|-$|3g[Ik;Q@Q@Q@y5ws{{/O6c3)lt^F;RJe+FG{pwJ<<gJ֛6]#Kh{+(mh*Y)# dz Ԣ(((((((((w%-AG Fc(O3\?2'W<*2p4.u]۠dS Ak%kQE ( ( ( ( eƙrO.OXةֶ+?]?=>wImݻoBg8Ƞ 6_CkiFr(>}W[Pu\:Mhd1H FGP kfڅNp:㴿]5oX%Xè |P4BMf[GVՠLaVHbQI%W?3?j&etDH=S۽ٿWWW!H;7@8ߏÊ|Y/b^7|38m3ޫx,Mrn̂I^Xk<FXv\nj DtA&~ 98|fDWP:c L\ٹfn cw ,ӕT6c~cЁ6VIɈHa}WBI-1(y1jXROM%a%vY%dےuF1s;KơWKG CBTm VCh"wԦF5 X ȃ+'rTOakϤ:шXm;i(I :{w6CΡ\Ky&it 2@:v\qs~V`3d{Aߚ|qhx{ TL.S*`[NsywQ[]GɜʏJcpF;Nk9 gp _jz#9$tQ.ڀrѦI5ު!`u$m [<;^(%խKA Kά0P3pEzyߌg<υ,e4Xm#V^2diF ( ((/Kf!rAb@U,fsT2={TZMѺO2UaC2FGNS(Š((*=۶] ϔqv-Ӥ hk7lgڪ8O$$_[K##0=I> mbANfrԱ$5p(QEQEVDqu}vy/iɱ36 ;bc!=ApBb"8`jJ(aEPEPEPEPEPEP?_I'!$v(Mqbd 0_ϣ& gϥ: lLDJFzY ƺrVo21I!f%X~*H: +Gi,>ЗR kM*ImR\X:Oŏ j>/i-\.v`gq84QEQEQEQEQEQEQEQEQEQU9//വA.H,x$Ƹy*ƙ4؊- 1@;ּ}w\]xQz$~v !B{팵w|mgi Q$e!)%VRv3]敥Xz][Gmgn":(dII4X7Kx K SY$$d'I'((*ɭF#E zޤQ=_y^r'пEP0((ʨU$QXΞUCupu{*Q8qjk齕,33D 3ӪL[) ((((((꺭jzvvY_I8I @+/[&?Y-,P mAvzی m砣/So5P%P-ƏD!OנQ@?)@y>1Co,tBf3>@ԊJ(;Vsᾫ U˨/`?6x8_zZ>y5lа 21px'zd iT-'MmT/WBl3hq2/5ׁ5۽%ۦ72FT|X@ŀ?\j:xk_'ce[s xnKÃ+'km(((*7^Qđ10q9bKyjhEAw{Wk2C%{e@X1C (o&d4=>'ضzaF;zy¯?tzq5Z 9[5QEQEQEQEQEQEQEQEVuˈ<`$x$nl{py5Yګӧ̑ݨ}z`ӭ4'ERQEQEQEQEQEe^%tk56v@DrF@b2ϱ J+Ծ-xJX.mͼ?t`!F1FGj%W¥ӭuzEYah`Ǔ9'hYSqPQ^ gO9a@Ey*|sU>zG |>tep$Ԑsʳ؂zԾ>KQւ;KcIX###@Ωy-3BuX  `FyjxQU_?n*X63ҿ/R还Qs:v8^Oko MYM5`xTO߂Ć!vI8 U_x2k:Ǩ~ҥ:0|r4v`2F}kHyo.m,j\ayOqɼƯ%(p0yJmgºFpPH3bI85U,m:?.$NPNjQEQEQEgk( `]QdT=:Y=F,VuRvꠜn>j^?hPwSy>wm%m߱ m3g+<0-%o/^b$U4,Q!> =myq.dߌx]Goop8*(((((((((t'?WRI3JEʺu,!їK!@!ps (OvCvԤ#?{f͸94߆>ҭ  iɺ\6p &3(5[z} ;XV%-2B3}\(*9[{ PȡԌA8IEss>mͼ?t`!F1FGj16Efifaʜ5PxXSeK:A-7##Q~@׃5Iu\1`D`J(>*xS-~f U\aCN;J7tKh4acuBp9RҸ7kj:N@Li b Pn?1 Es ;l<.-e0Y]=F2qs؎PEPEPEPEPEPEPEPEPEPU5 m|p2P;rG^\m6ꆵiqn,Kr~eyO6~ҴY7.ca`XF {GGS?|Q~_ʰk;=wm6sWQ@?)@?E?(^Ey"_hK[zdcmxST>M&y;/` u4Y5hHU$o9BwZ (+Mdj^WxYBg#$m9S Y3h$#`pÃ*I[{ PȡԌA8p}w^ZzD1 !X I^@c^+qc0t@4e }!n9@@~vj'+yc P/[R챗ٷxC^^w<9GФ5a{dTr&"+q恞EPEPEPEPEPEPEPEPEPEP{8u '\*< q̶7Pbrʑl 9@_;Z)Wr7#A AVIEfGs}i $Nܡd.: ܯt:;y} 3Vfሠ-&RU޲:.ᴞóP3_|edNp̱}@Iv~bsg%gQEη/2hk-?#1Ա̀@RrlT&G㰻5 =G{wX؃0Ju΋t&Mb(C-R@\+3v7ܐç=4ydfDMs6Bw>8_ liw[;[.o<2! $}jЗ:zi7Au-sG3"[v77A0K{masbn eFeX[]J4E<\n/9RT냚9m'p&{bO$y3]٪K𹲱&%FMɽG |Pex4J1s" `9uv"eyF]=đ1"<9+<:x3PiP╛Gғʲ‰v~C9c_^_[<:^6hibU ?wzmx,G㈥[h-e][Dx{&L(Ua46LU2KQFr0 sZsm={s#nx1߭eZ6[;=(u$49ܲ6wtzgr_&{83HSiIFIaE׈g=-属ߋWQ.lwO~=3ϧ֖~czf5 9-;pXmlmm"gP1Am cM^sOjwVQcau3^9<' yX cgLwtQEy?m#ԴbQD`fT1AlA%S^gu 9zsfôI `qEzeQEQEQEW|W^pǨ^Hm\I PH85OH^9?g@Ey-?sΤ+Mdj^WxYBg#$m9cgyawݬ4 H8a~b ( ( ( ((h/*P_/UEP0[;n8w ۑq˦];N\]:oyrI}x~#bvy$$Ib].$ꓵl)mǎE$7Ӭm]sZ58|5[iHBJ^I&vp^o< ⋏iVn.=z1I8*002zNF4_{j|$Ul8#<hW%/ZN#gIXQ2#f ! 5? iz[% ^!w*E 5`?25_i2+r :$b8:5.S%E X:++.!`;:tMjKO#D4Ub;200x|e{WBh4D.T7) 6Ϲ jo{*O(2y.ƲF6Dp kJ}gķzғN &+Fh2 s.ZTXx,lh#i[O#%B$5gmO5 :bG;~ ciב]47:IOy3BQmZ4jz6yJ$ʑC2U]Lp: ZΫ.kinK7Q2I#0wgp5ynXneEyd!у86d-mh+`1s>gt4HfQvW LֽVtcoiI'{1x4Cuo-QRHPF Ab+x2dW٤3B2$ uI9K}%?lզڶz\ 7cN?jlZ&CJ] "`:Š(((((((((($i,m S ƳӖY4V  h^GuBr7"eU+R$}JױFUG^Y'H<,V~?"me![EBGMOp+x!Ybm  <ym/'I5ۛ`L P8'C=((((((((((( sɺO~bg|ٍBkbqHU@={Q^ BqN@A;SRc_9^6NUGCنOAymL31"ǯY@Zh71-TdFw3w6NK" V i :`VqzkZj-ލ+7Dgvp%7Q^omQյf=B&m#.Afc'6?dj:_f?xI%q%pHs@EqgOjoټMckcmB8=Etn6q+i-fYT6*H8r(((yᵷX$/$0UE$xs\ir]M)LdAr9BX8Ҁ:+liڏ}\ݥť%Id۳  @Z7`gT'mҀyD 2 [cHU I9pwo5diI(]Ju)&A*vr v ,no`<[^'EK6ڣB8QJ8oop8*_7ڥ5&S0˞$"( ( (9 gwjڛv6VrK9er88 r|:Լdj5 '#.c#EinX|1.6w0r2ˊB\x CǺ̯3%naSc N8l+5OMtݭ>r+!@>®QECuo-QRHPF Ab<UO5 ^O}\`yx̀;^+}ѴrLJi&mi?`|浸96K+mnkƞZtRyˉnCmh.``ilux_}bqu4E&9;4l⥰j(((nR{6 ~U5PmOCObM* -{(&]o:8!@#E Uoa5F#PIv' $f_Lue\U 3B8BرaguyT7$A0dty$tQ@Š(=w}&FúK3; 0;r@ r۫<[/8KX[*_"cO 0$gw$|.Zk#Rvڽ̤>@*9$ciwCuo-QRHPF Ab3:[zޛ}:v%`$)' gVrzjdž4Ep-8#i#OAY;g sۿejR'~}@Ey*|sU>z?}? ?VzgE4&hdRܻX`g[ԮBS,eAy'$f(((*i5j K%-p 3N=\9.]%ц Ȝ-'A|p2 ጃjZS@HI\gڀ=K~!0Q o?&_ +-D+G-/|z?}? ?VP¬?8(Qo'LfG`l1P;sҖ)ذ<cf3ڮQE^LӮo$mby}Eҋ=2K--cFI7P-<~YMe’Rp˕*Cj|ko O9I\+RUvSb{ wCkoQH8Ppb((5FۘёK.zl]s >kN.[D뎹g3 b}(³˭RP9$.ON?ցѪ~&*袊QEQEQEQEQEQEQEQEQEQEOUlt=.ScM2IrId^_{}rLՇxb($ Iw~e m'(zr E=fݤJWl=2U`@!j:7zXk3#`@xF7)#ׁ!($ qUE81RW?y5ӼA[޴QiZZ)p ߑc9+uuӵ+!Y5v A@adW[K9dJ*0r0Q7@qeH!}+PKR^DLq.Ao(Qui^4}iWd H&McZ8m?Q}4d 8%x $PJ2Z=۔ 2mT6Eèxu>x~^B #X&{L$*2猖!w%gƢ4M^} C?ٷY>b)#|R`,pzu ǰ_^bխs6zc{y*CHRBG |: NJhLr>wFh K`3QEQEQEQEC\RYW3[U$@# cg=V1u (Š(((((((((((nm0YYNxdoQb  Ur,zkHCNz(;^,nB:[̙UF*>?a؟Ұ{6vT6vב-n"Z'  ((xmi"zMGjed?{ 1OcV+T>'dgf"಑E_-71 W#=˷;;udGgUubksN?:mX`"z$jIH((((((?ğ /^ep`)\3(?'Fzkk;X3wcCa@<߈L?o}⎷K7nߺ5gx~Y bw8+' ( @(((Kh0ff;R5坽}MA}#eP|ᡌBH E[454TEUT`:*¯,ZSsPqgVQE gSRx6똛#Yqm?usZE=qUNitQE!Q@Q@Q@Ts  !y$($s/ҭ{鮌\6pO+qpxv@:vd#?w~c=XΡ߈3.aN[$|d߈L?oW[WI#è7uvb6m9<@߈L?o[W€=YZ>q_'İ[FyeV#8tzt#g u꧊ +B;|Q$o V$rC=zWQ@yC.ߘt$Wzse*H۷s9,zec_9^6NUGCنOAqx4Zߋ4V[P(d%(4ShXrqc 5o RNE4 sՏxJ3:^YBK-<Ycy3I03]^u?' Ԗ6k {F`.NKmGoZN` #pb t.E5w_!ϴ.ׂrH냁xhך=NO.bi^2);H#m|G}F=SXy V@TƥAgQZ((((K#%.

Sy?5sj1\HrT/Nʅ@?{.ί"Զ%2>K!@!ps?NxVjn=FWFrzuczkAѮK4 48%@8µ( ( ( ( (0_g\{u=M6K;/nbS뿲)߿vv>@<;v}{J<~[{1e&d~ 4ӷ?w-UO,&ќD+ sCuo-QRHPF Ab#㼰L $0? O;KhvPb]tC0N8GWu7I{th@ *f<'ˍGXo xK,q,ndmxppCerpm[:_kf?vl8qHsGw]36z57),'iQry=@<.H7G]n#G>-t.4վE1;!\ÃֽEvY:/"pc^fU!0R;r+,㳰<h$(dEQEQEQEQEl=P X !V""=ht%;ZL3LLѢ) ((((((((((((((((((x#w#{9 4b!{Q595Eh *k["W\%`Ag\aсjr6aR{x/#oRJa{njT,Um8Sv6z#ޯQE ( ( ( (nn]\E4 S^(ć>i6s=wպ#얬Qf:vԑhlko,!@suI?w O<`n!a$%葨P;% rn; GrrrxU( <_|l1^qh,ӡ5=0PyI"u]9HSp>VP $QE ( ( ( ιњm68V%䵑n8'(܅$ IѢ4Cmr1Pvm#z{Ai蘪/ni>5.FAnfWq0w<1/::Fo,竹hz&V6m^,33S uoż9#`FApA恒V?֊ڔ,Qyq~UcsӒElVGej3)!K$ 3@I9Q<~if?v#t'cץKw].-Y坣YvZ8TbBՂqW?#f9켕U;yrݢ8mێo%zxcE4R!AbT23",s#I9R!x'ɫ6~"o%2 I|HeH  s/5 ^fKɚ)04Q]wruSim|P%a%VJ[c0ŔHz|ChA}4=sQA$&nڨ7:5Zukoi|ek\C .$w *[*k:b8.cubw'cy+—i5l%զ$vr߽^P z%nJ3Ok,>b(]@p2>zZgMb[hnJno*$FX+2b9# s?}wE5{\ڌd۽c`.yھx[=+65FvRHl?4y͏tCP η%'1A.8w=j OWV]6NUY?0$t瑞{L~Ƒy$9c3\3Ƿz$\zg&kgzw](9$F NYFܶIZ6|lIc ~!Pcj>DJ2I; hP]XGVslݿvkp W4 ^xBd-oAV8mк|s4.I"]31m,;!!ǔ_*A(ź$qظy~ޮ֫J҄T1ϡ5?u K:^yb #Zgt[;8?Z- E$fع98FwYdZ1\Ӑl,gEo38Yj2H60 w'J "K=DHҺx#WhdQE ( ( ( ( ( ( ( ( (3#Uӏ'oڭQÀ06!8?i Z+wm%A@$09""Kr=4Ļ n巸9 I]H#W[t5<+nHKcؖ+g) /ٛS5 Ir2i@28KB/Ui=tndԴ1> `6Kdž=7UJmX].]n"F\{'I|T&r2HӜ4[MmK-/W(Z̲lTq(Q@Q@Q@Q@Q@V~'ypo3g=EhQ^ oFԿu]?UvZ:G M$Dj?6=9{?~*9Ẏ miV|rX2s@y[u㜦#瓆r*F<=E-ٺ%l<> < p3zts =z&~uóFduZ(o^9|"%\_u]N]dNF$s^!rwip#rp71Qq+>y] 7f,uzeQEQEQElMP*&;H#:=Ehvfgg74N>c>ENѢ) ((((((((((((((((((((((((((((((((((((+:eK K73V?2CqaAѦKG"+2=A U?3仗YHs'cס (7VEJ$E `+~b1,$ B b(]k:t?#ڛe:#plgޏ쏊:_yxC<Ͽ`N:l ݜc@<G⮙Úm }2KiZa o?&_ O\<zˎ߈L?o[W½K~!0Q o?&_ (b&K ;kUƼXЕ ƃv㟘H*QEQEQEKVM(]qTax珼Hf#WlQ D\ RPZ6q l$pq-gb]Š(0(((((((((((((((((((((((((((((((((((((FȊ+ PER ּlA.)ꝀTP+?-c}wJ\'*=|nfmRpqRCcY-Yǡ`Ƿj<_|l1rCQ^_e}|mc9 #WNX%WLOc7Sc_%H9.}uˎe;hXf9d@7A,yc1;}h= 80<[/&y'Á*+?.*]pp Z5(QEQEQEQEsHNncݡp*áVR  J(A_!yQɾ\|1[sRWi/4=svvLa8vC` (v<$?~WnY_槟%ڭ9dH\A'Ut'Nm9Sq1O8rXg Ȓ--c 0A1$ܓSQE ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (*^A"stvI$mvU*jsj E 7+/.FqG H TUaKoG.d# r'p슿@p(((((((^B%bPf=}RxQ݀e@EA lᓥ8) t gFQMe тoݹ@Þsÿ-Sk~m<o"]OvW引#`9P:*ú/)5$i8e%[8D=g0q2ir v%ϣӎikhtP2vF'* 8$VQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@703ĒRE ~A3$F?x4 Y.zpFOyQxs<, a7P]^4,B۰(h,L/ ?'gYLԪ$ӬƷwѿYUsQ+u߇33LyT}ojiA~ȞzkeVr(>hdZ}R_*a6],蟸 D ixye煋}Y4MTE4qI{Y}wQQ.& jqZ,F=⨻ڟ86uW%ɪ:Mwd;1UFqD̖f=5WnvS}%%D>lU/R[I=<Zt_3)V-'ioq3+9#^47{lP#Nݧ_ H{bGA4$LfYC P1 b$a4j7Y&i%d.\knټx!Fځ6C È[YfC`aŸ] 6 y8ѝOF0r;|YGu`ݏi&Ej] XDw rl=oI՗w|ҕ1X׈՗8В&l@5S~k[ H^(B] <{#?L@P? yӮdrIA!p)\ BBsΘKGzP.9̓\<9) *7{b|֕ v Ia1"ВuXWHRר7pHQ|2X 84#rIq E4ٳN^CE?ŅI_RIuL`,cW3j dT!LDǔm616@a͒/L.RI"I="Dl[HǀLSKT ld3T{nUvܪCQ~\pw&-g[{>";W0JEM`5iQԩ@䓹.mw`uAC:}SLQe.R%O^J^ez"L™ݵjS+9^u4vYTNPJ,l`N,$k#vy׎k"s+>4~<,g2>*?f 7 )_vBҝS}(Vbq~ËtM ;ro"҄K(;0tH9dBPs6c=B5>'RvFCVT'w(%ȐHrQ\qN`~k2Y5IJ2\ƥG"}9&6CL89#H VඑH|ޞApaAr(bmv(p k聗t/fIQyqw. qP(~mユFtȨKqY?^w4g 3Ɗ "'+рtDq%hP.B:R9 Q F>',sԾN~>OUH[{QId]bN7ǂ EX/zPI22ɴlQv  v /{iqMGa&'#r|GқؼHml{/3j%s湄ᄂiNd k& qFtlX$ =ȒYmgJcm59'x rWaNʰ8 \ҵ^ f- -$vuIK6or/ˆ8fCbj՗o2{0iCs'浬Ғ'+\Z,}䒦1u8Y㧴jn)[Ӌ (maϹ3>27[wF &yhjWgD*BB!7yX&Z*ZXU{Fea7CPoHKj r@ |4"Ԫ -HoVVE@*VkU5,Xdr-y}-;@?7.͵joP_ Z\ ZFk8`0\tɁiSRRnݺ劂)R?^zʕ x͛WPP`k6Ν; qss lݺ_| c;+WF+W;ӧ?cr$8}tLLٳի'''{yy=E߷M׵k׹s~駃>|BBڵk,$J@nEEE_}믿իWO>M.]u֜9suֹs-[1xΜ9ڕ+Wʗ/Opž={uVzǏ[O>Ϟ=4hb<}M6/^2dHLL͛7,Xp'N[_m0l;3m۶^zMUܼNcL*ܻws„ G㓗ǍBrss!>oZZ!$##?{sC !;wdh"BHǎKKK!'O&Ԯ]m۶BV\ӧ 7k֬zŘ2eѣG!򋇇nj3n ???nfn޼Ii߾=7MB>~իС!dܹV#v_}UQQQӧ7ntr111lc;7G{w.ZlIׯ_r!Z"p x{{s#uի}a?s^^^>>>~!!a_>wĉ{Yo0>|hunl^n[n]~ѣKKK뒒Xre`` 3###c͚5aY 9SN/5y>|evWTJU˙[aQ~ɣFj֬ن 8pﯼ W:I]fԩS.t tڵgϞs̙;w]g@fFz_tooٲaÆ6//oj2˗СÞ={-WߣG;1 UV{lV\_zyyq jժiӦB !\Ѝ73Lnnn k?~\zuKjժ: sYׯ.Fճ6m*r]v矗-[6n8 vb6ÇΘ1cСlm4,<|G,[TT4f̘ __7|sܹvAx&%%%ӦM􌎎^`7UBB›o9iҤHԾ}۷oQ۷|7nLJJԩSgƌ~s=l'v@/6==2|ˏ̙*?~\re??ɓ'/XC͛7k׮ ֭[Zjݺu.]b&''J*~~~||MBHBBr-V>}f͚UJƍsj1;8)ďDEEEk׮5 {el޼믿rʥKNy9a=zd2Ǝ{ʕ7oܹ$ ۼy׏9ҤIݻ[.wر7oޜ3gN@@IEAx&'N[~}vvˍFy؏̙STT׬Y7x̏ f# y왷o6Ν;a֬Yuׯ9rN?k2ܹcu^rmBB!Owݛofff֭FcHHHbb"kYti͚5=<<1ƍ e됦LRXXM@:thpphYbܹs\kO>6m6Xv!C?^`ܽ{799N:޾/œ9s={f靈kapB2h3h4nnn^^^M3lUk!|k׮ddd G{Yr̙ ޽;IEAx&G| ј1c*U /pX|e~$P0lm(>{i 8p wϞ={ݥK~q;ݝRzǏ ,@kf}0r .\p̙3 .4L\.\x׃C a?޽hСôiΞ=ˍo!ә3g =w!,XFRs Ç޽0LBB[o}uVH`eڻwzyy߿?RYG. }i999of_bŊ6mxxxxxxtӧVo>))?LNN5@GܹsڵQFU^ã$33?Zƍwؑľf͚fsnDAx&ʕۿ?+UZlm(XeP|=zHKKu3<==ٶK.;viQFz1ȑ#===ٶ=`| _@؎<~z *oݺ_,..? ҥKׯԩSʕo޼yuBRRR{qٝ;wk4>u6mTRСCk֬qss?ƤӧWViӦwNMM9s3[I`PffϞ}ȑ-Z|l?_iii+VXhQDDΝ;aÆըQdС۷o_֭7|?|ĉ^kjH9>W`9bGFF&''?yt޽ׯPB:u>裎;4a7nk+Vtwwݻ7tf͚Xooooo N:rG Qƫ:e~?_b <)SDDDxzz֨Q aPƢ},Տ7߃Ǝ[F OOπvq}vҥI&4jԨgϞܹr8e[1U{u$Kuќ9RצVuf.<`YuEˈ*\K_~=þ9xTbkjza** xN:&:a%u& d#¾6~"FiT0T]x^$$?nM`=u U^4F?q - ;WgI*%vDDZVr-An $ZK&ʵD=`PN-XBtJDk"Ҍ lAҭ?ځ*[Jk>: Ljx\`@i k`E@AjY;P_ @}-N:mL4FHij-k)z>0V{nJhKm-k5WPl5NU{}5^法x=а7)9wP_ βϫ5?1} q*g hkGk5 `Vh84C+hr*zߗY*hO aW_0{81k$Oa7!h  n ?Zuk|!*]kڠP)"k)Å;rr;veNbVh" Fq Z& >|Wǎ/B9k͚oP˱FP5<5 9="j r-qF[B_e-C-K8k@5ڭ\rԨQyyy2/yyy۶m,5 Tq͎TI`imZRVN h@-)S4lذ\r>>>=̙3.ӽ{w-ZmrL[^0\Drv=&Hnl-h9b/nݹ1J6 0>xe˖&Lh֬ٳg/^\B+]:Ű%$Oqj֌]鍭"e'cQ\'p rB*0&jpiPK3f̭[9ԤI{o߾ҥ !tժUYfjjԺwާOO>$<䓬Ǐ#Gܰaĉ/__6nxƍ)))ܺ|g3gLMMߓ iӦnݺ >|ƍFܹs&I[lٳgϞ={yfS ,ֺ[.)-[&O|em]t2"jzaxɭ,q\9d-9j]/Z)C{ϟ===?]Æ !6m:|p۶m:s͞=rem/ -s9kTfi=ӊ+6mt…'N8}_~m6ј4}7^~=---55599E%PBpp֭[)**0axYFçNʦCBRRR͛7s̬k׮'&&B.]8qΝ;Ǐ?|pݺC9/ IDATu-g/WX}y+Wܿ@LQm-Tx b V$ȵX{)hmq =%Q\DppѣGhѢ-Z4klܹoI>}zrrrrrrttg͚> 7o޾}{Ŋ6lh2ڵk'rO>SNl9{ѫVڲeK ֭BL&SFFkVJ:ԪUkٲesNLL֭?1hذaUTPl-TxdkK^F[<2Y@hx^ΔYfpctCe-*Hx@}( a57m͐@5gY~-0Bj!ᩊvZƖVYvjWw7hPB-HrڢFj5cWTMy/NhVe m™LƲF8B]PY [ȵ dHXSv2{cC2bK34%3&~•E ''OaZY!β,Vm2!J9iU+ dfM@ȅơ/0β\lxtL*8b8s"Vp㗓3϶u5tj\qV:ϲd m@Bp,r+?O\uR\Yx޾kD1p˦8.t|;c$/h}l[<:4(jRur-U"!,!j .( q2h\VP⩫0P;ZdY!cp@ȵ}RA "30'u 4 qVZ -paO,լp(2ZI[dYQ0m1=@G !;[$OqZYB jR½,լw ׂ" PL}֖U\ *,+'$ P¥cu-7l4X6UTKEYQjrlp5P Z: N<'7A}~)eY;b劣q m+ $t 7mg(X65E^߯ؒ6N8 Zut"ՐB-ٺ/: }9:Z?A B- L23@P AK: `I!@R Jʥ!6kuյQ>,gˉP$YI+ pkBhkC:Ō4/V\Fޕ$8 x_IXddYPp)*{>PNWV_8hΑ4M- 9H< 5{+G7Uq@s%ȵRutKc rJ3˲hfE\ 43?XA_k | |Z2"Ԣ̠+@H`+D#ih-QI%ȵ3WZNjP GV_thv;|u3\ ,=(?T$)# V-"ԂU&@ ȵ{CtniYB3\P q\e:Il} %ZUK ՝@gȵڄ{迄YB' W 5@"ȵ{34nxY\7;\ gHBW74/I FX0jeOjP P+Kώ/J8'g|yU6c gf"W^ye tr\lŽA쉤tfIAڵk7sL)Sk3g93 rxr3#GܵkO8ak)eCyƭ[Ο?.1G[YȵjZɩU pӧW_}v>}B T:9۶mۢ׬Y|\Y=V j]GT.RUzٳg?~]v%0{X^7n |v:_2f̘pweٚi ۺu먨={^;SN֭[dg8{͛GEE%''[.իڵXz5;l)b\3f׏}+VlV x⨨ ;v,&&d2?^x$*[l#.PڧZeR9E ϋzkܸq#GׯwŊ/\gϞ:u2 sҥܢ"BȣGڷon:a6m4jԈ?;ښ688f)((\rjjjiiizz߭[ 6lXiii~~~xxxFFk׮=cƌ .;wr)b\k׮<}a;wLٺ=j"s]rQyyy۶mTP;B-z(6OUA'Ntnn+Te}Obn F2 ͛7}zjռj֬[Nqqرcz--Z;v,e˖X-ʕ+4h`4CCCHvդI]^۷[N;f̘{iӦuܙ2zÇڵˮEo#LMMMHH TZuѢEqqqG~~ǏtRzuBHʕ-'~?o6bŊ*Utŋ %tiM6ÃRRRRz?\jPgL0"@]uk_-ctI 0xK֯_?33_~gϞ>zI˖-!-[O>l6UVڬ޽{s=~ v_nӦM-^uǪQsrrnO?MLLڵܹs˗/ <8vcǎ: U.yk;ԂVVZl5З8K3U f_ZU041`ɓ';6!!{Ӡ^^^ %%%|`mڴɓw޻wɓg͚oK>}YW999o߮]6!dŊ?ZjԨQ*B?3999!!u)F[ZP/Tֺ-8Cz̵lĔR-N|u&$UXK.7ocQtttr߬Y3v+UԼys3gpt>}?a`` I,֬Yok !7nܸqrrQ,Ybk{葖sg̘٫W/a.]:v~q._|ɒ%\9r䧟~:wܙ3gZ-ֱj*{T F@h|kIfD\kEYvY`46m{ԙ3gÇO:M6UT9tК5k3fΎ|nݺs={#GZh榥XbѢE;w߆ VF nCΛ7o߾}[6?';1Ŀ;sYIZ~ma {zчs@RX̬luȑ׷ξ… LYFy GZFW_}uʔ)L Z|UcƌGҥ 75kbcc6l8uTanܸkUX=88ww޵<;vl5<==ڵkO?uҥI&4jԨgϞtooO>Dq>ꂴDW#KtA@54|pWՓa|x1txf`c2ිp-CՁxFj)3Xh ?P&i-̵Ml$RoA6@9axm> jZ3Z{ K3 |ws߈TG=ȃmYvply@qÀxhk5P~f `[53{!nuKLg\{0tet{xꫯ0[#Ab>Vl`SY-I*8 EV9vHjjP z^h> x*ȶv PM}-B-z lgOI[g$`:I}AuP O0*C)R<Vuw/SAE9\m@$KUb6ج\ zpB'XGO"zl.dוr K FK@} lϊZp9T_(W|r."!:KQkQYj \\EJ5H/^\PFœ.먖h u!"&/;ZZu)3*U0Z}.-T2ɮ8pe5( J-TpQe jD] fp, *~V#uJ%~B$&P21Ҙk*Vh=-I^.eͪET1kr`ItUZ]/`8 g+Ѓ؊+PG8nMaM* z j m@~U$`Vw=j@zNy[j Uh@Z +ne^4l!j{6BN$'&r-(ZD[|p<:FJ!悓DZvQN:ӧW^-Qո6 jo@cns7D< |ZkAY|-n(*[[fA=.7ҒkAA\MeX~vp4dUEYVKWmDEō_O:[n8k֬N:B>\vpIքgn޼yTTTrr2njn2Μ9#>>ݺuիW233r/_v~;sd;%?= T}ygeZЀzxxsM4؏!*W\ !ovZ'>|`0L͚5;{,,ݻwwww0a¹smyСgϞM<'66&LpwwSoN>n8822gϞժUk޼yff&!D`B[%!U *X*]ਾϵxbLrrrV7"""'';$$'22[`  ooBn ,hРAժUk׮]ZZc[c޾};**quUyf.Ɗ+rOhz;,ml\C?_ Л7orqF||8W1 ;}ԩS9RFPJC¢.^hBb3/,S$``آ|}-hC|||aaŋ !۷o?v_߿!BѣGO>-rBalˋ5mڴO>aٳg""bǎ;wܻwoHHHPPѣfek~$ IDATq+00ЮU [Z&Ze!݂-ʿߧ 樔l;N3Qu{S_@P_ .goꮃ1fO)Xp)s-Uɬ1>*>AhhxIf- Z/\ { TMhk֚V2K-Z%^@մ:[@5gp3B-[rUD[=u0h:|'r:qF{!A{*[-Z]-Zq3:\T;.p“ 99Z~( 뭇/SNׯwlm۶L2_:<;>i< GCAuDAhK! ڳ`G:k׮ƍ{yym6++K͜93,,uK%$$[ׯ4i%3]`FXRjwG,cs]v:thnnk}] 7`GhoM:sίӧO-Ulvrը +L`-\p BUYU0p`뢭TG[=9tPM9uT\\oݺuGvbCO/ \pcbbbL&1v ü{ջqV8{͛GEE%''[Çk׮4`bnիW۵kzj ۺu먨={,YO>&)55_6WBBBLM?DGGr\5uZ#e3b'@JFuU=ӓC$<8Ru> 4hݻOv`AAAʕSSSKKKnݺ0LQQ!ѣGh۷_n0=z?zOn߿ՠLa*VxaSN\a.]k9V1 VZZ_gϞ1 _,..]3/\z9|eM6)))5bW?:::%%ɓ'Ǐg ޽Ç322.:myPq}@REzݺ urbkm<>\RR2`^z=|`ܹs&M;7d2EFF388Zj͛7f2=ztܹZjZF( dj֬ٳg8tPrOykժ?|p~~ ԩo Q'LpܹC={l^^^>>>ǏQF֭Ϟ=kuYk *sY[ddHR1۞ T_bԮ];---;;_~9IrrrVʍ#<1/XbEhqqqǏOLL 2dӧO-p# #((ۻH߷nݺ~jբ6o5j 4ZjڵKKK?~|(wnM&Saae )H*˵V塍 :rs<ә-@$$EMkժo;w.44qՍ7BCC !nnn\Շ:g^x̙3iiiVlCXhh{rQ8,,,**_n߾=}tiKӧNm۶7n\rÃaJ*l)HjʵfZ$ m=U[qYZ<4vM#Bٳ233׭[_XXxbBۏ;֩S'v111{%?~<##eff׊x#Ǹ]@E峺Y茳yhʢMB@@@FFFӦM}||^y-Z|F1==}ٲe&߸qcJ?-\0>>7 jժ ,P ޛ6m6lK/4-[=<s ;-_?(JC63q+f,kFe#*%HH'&8O`QA+B-%skn"kڱo߾]v]r%00__pss|r\\ܒ%K|W%%%;vׯVZ5nܸ~h֬G}d2M֯_?;+W:tɓ'jٳg&M_}>}|%%%ϟwlebkm U`FM$2*H\KÌF s-hYr/kڴ)@lKIIInnn5kVV~ܹ-[Znt{_){&L:uj~~/xa&MGdllcKݞ2GJǤT$V!bA tKC\K {)_vvvϞ=ϟik䐐^zmذs6l`_ͽ`e`K.swwg_3Z7Z~б%8i'/T|!O֤*O(+ķsI۷ow5--mb^roL>ڵkGݺu+vžfȑ#5j())X):Dqo{r>j ;=X-kRUMkI"oI?7&PY,_gèz 4}l{,@Ӝοkl&umɫ'8k r|Z˜QE(Mp*׺4Ԣ(~mAN2C-$sUxuuM-AB-H@,_FUo/W=T房8ke,D[I Ԃj<9wY-#VPB'g -hnlYŠLvZC- i;Ffy*eyDu)/ķ/*j9h/ezeZZ[\܉duKZwuuۑkii0C!ڂX [ȸh˲|>%hѢ?f쫯Qh|2yT}Uh;GBaլ6lؐa7 K蹚p,5XH9%$f7 DZeI? GZ&m9{pjb;\P'"C_E,E[>Ҥ$\)zk;ȵ*vpO3{^WY_/@U2Yͅ]UQHs8ˮD"#NG I)%hM‹'k5ՠ쥡 Ra/8@td:uQFejVd2M6_~Oɾ}vuʕ_Ϗn9ҥK}kRJVK AZ#؅\ jY6 ҃.!sM4  0IRR>|`0L͚5;{, w~:a„Sd2=zܹsM6U7\̴; Ba$Nr71ZPUr$$$;((ۻ{K.sww/--}e6?~|bbbvvvϞ=ϟ#r2mT8ˀKMb,ۂy\ ⊗`޼y7܊ف>?ӧOO:ȑ#5j())Uत۷ow5---11ִZ-BZ&؅p-rI2siA\ V񅅅/6lۏ;fwnݺ?~<##C ### !˗//**:ZfffQQ?SZZ*0mPPPvv6}b;(Ge@BVoI9s,vHUr-hLOO:t1cTqF>wޙ9sf o۶mzTҦMduÇ_~ãs΃ vܸqz***ڰap@(I')BŊ=FqGIZ(R%T]Lt=zZԻTUN(jhg|aKHaTѳ]@K\J͋UpJR@&4HZ[=_ch( r-E4і IߴCIÅ ̰g%NOͣd= VJηur'E~_HΠ!j.`$W'ѲV 99gӉ&-aV<KK)~-ύ =\W=,2SR* ѶpP ڣxT̵ʠhxp eY.]s!9U !kf2ݻwΜ9cUVVΙ3筷ޒO>`ݻwOMMVVV _fSSSccc?qℳׯXG͘1P1y+qrV%7mtttrrkŊeMLL,^k||ի'O嵵gΜ)!uXD4biӎ]/x)Քq6yfiJ___6]vM-;666m۶T*tҧzwL9uW_}500 hii,k[lIlvѢEĽ[UUUQQ155%gԒ/qƞ={:::O>g1/_kii sUw%NΪƪd?~Qˡ[NIDATN9p[JJ2{:̚5k߾}]]]t:o> ɼk_~yѱz;w>CBYUKKKkkeZ[[L&#{Çy (TJQx'di˗ٳg.>i8'13b tȭottԲf 0Zq[> 'Oljjf;vgΜY2Q \7f[nݺUuE(`زr4k@H ȵ68 w rpPΰ\+Ѹ՟6mN usnߓl C yY\hpn?r9 @ƭH@LQ6J׮m3NFa%$ٹV@PBm oe8Jk%ҭ*$Z%k(p"\+MHf( =1[}tCt&M3=G%wg ~̥Lb5$Ba+mePpFP~ @!C{b2n6<Y'Dk=6C7;4z5pxDޢj3%e\e`(B=be4DkkzsD}εh枚#~n##VFC@?Z )Ӧ.&ePYܸs;_K'Np0Bee =9r$_Z- 1-hX6Sh`26#(h24`̢rZ-Jc\ɬ `#4mxaSki٢dE[CC-ZYk(V&=$miXԨɛ٦smӢY 0~$?AiܢXeّQΨ]B]6D(>Q}*)үom6G[9[%[zi1rcEdQ2t.-Zt̵"^fin c]ަ%̕V]@AvLȧ,6XɂFgYAbn7!h0Zf-hZ71Q*''S,B-tCaOp^BBqͦ$1Zqn*Ng2/JUV}X3P c!?F `4SnfGwI4Ơޓ0_OWyOZ@'")x$CPl,j.\dZ}|1׺!BBb9@,%"׺ir>I6$l٪$MjRa תAMMҤ&er2Dۄ$MjR!תDE4$ !!PP *F6t:@4q_>G1eZ@Ґku<!@j&L /G$Zmc@AH8rvH'R.mVGd)l Z=18Of Z}ч3}A$PVw4nuV#I`ZZиՐσ8*\k ҭ&lQZh8@[Z`9rYQ=G5y+l?` r^!a`rmLpIYؙ\+9db`4rme7o=<ݍNi_Sʮdڇtx2-Hnv` Wa> a)}u$ 6 iv7(lygvVێI|3fygab˺ 'Y|`nTR)YcĸE"WBaj%0(Gc$ѲF*vYS'fI^kvpYW%0%\@x; :$]b(&yPB A)W)Ah7ۚ'A)1ƒq6͏t1\悖~2 [lf;vqEes"tá;'6%q2HaYM#m8<'yGI^1o5#zNl Et/,Gy)J O'd &DD>Ab pCRIE HK8N ϡ{Fh`52yR&b"X7v3قT®tcT:!tBxNtaL"]xpjh[ A!pjx5gIZ93\ 0GSdܻaMĐڭvS@|)B)},s+ѹϠQG vWXۣbtsLf50} _I'EG礍h@ mΰ{ 4៯:%w~F$L 7$DLT0O0gQf:6zvlthΏ;?M2Hr e@%bXTYZ Y-j\)b+⊸`.A^?gY\Eq&~ʐOMupSW|5q1~J ƀ a5;ͪFȦRDpB(!{\Ko-+ۿgxؾJY%9SaU7) F45uĪv4eӸ]fa-WK-=;`+MX`u3;Δvsl6 o^U$K՜~n0tsNЪ.@"b2ߧI{LX@ "B|D@*M:*ZYgfK_$kwb07SAagw!&:<$ qpSˆj?bG~Ml^f~pwBͱxy,<"lщsɀS29$H_"nNGXV*TYvf:+TҶ͟?᪇n.N8[hpx9#A0_bjq۱덬XڧpV:.N^TXQd4M!7l@A W|5M7R:%enD%P DQǗy۾)ﵳ)5~ΤKs Ym(̛F"vlf`nv0FJn;^7aaߜ$S ;!|N!׹7~/}V0CfȤuޱMW(.Jś@j-N^r9:sZdqcѮzջ_q~./oar-2.5.2/docs/schemas/api_libs.png0000644000175000017500000010050511757171206015425 0ustar plbplbPNG  IHDR>3:bKGD IDATxwXWAQDAEŮ`wX&[Xn,M4 5a"Rlr?]w]{SΙLc9B""""""=VHi"""""{L|H1!"Oƀ"EH"pvvƀp˦\r000^HOO϶Ly3,,,PJ!444[en[^Swʏ1À7 "Ҟ|عsg}Wذa̲L;vڴi#w|V֧E9Μ9:u(WWen*r*4]c&"R!"Ң!CHI;>>8|0;w_-͛7K-,,ωB$%%ƍhڴ) ..SLQ]DDDiɓ'ѲeK@rpMX[[ԨQO>4o\fzҥرc033˗/aeee9}+-o涩Zo|4Q4iɦM&Mʒ5&M$ RSn]|Wҭi _U|f/KRJ055Ewe[]t(\0/VZaϞ=9.6l؀zB}Ulm˖-[d533 k<|Paի=l4^7 `eeOOOL4 !!!93""DDNNN >|||˗!MB^Z)Ό ܻwOR2;tPWӦMErrT&==] 6L/__le~eکl;ԝlmˠA]?Ewuv9rd󲃈g ""-155./!!AL?HMLLDLLBabb"MsN:]&''ѴiSiڢETjSz+U$\"ĵkDʕi˖-lܸQ_jU$ǏENi[n;;;o>{کj;ԝlmK.]? DJJsΕ/XYGlEJw)D\\ ]OD!L|$})ӻwoiԩSyCCC1rHR2q i'OժUK_^=i|ppLW^I4h w9Ru)~e"OjjTL2Ƣ:RgdN|Ν;R[47 "ҒE|>T&,, iiippp@DDh׮Tȑ#h߾=tx *T駟d)xo'$$H">>x!>>fgω޿p9ţʿxW-Blܸ;wXcddbQ=loV)R*T@Z_s #">܀HK5j$?zh;vLz߰aCڷo/I=#/_ȑ#/@zz:^~M6 ظq#/_V4ÇrgwAeז'bС8u^~% wzB|rlܸmڴ-q-lݺ]tq4"L|Gzh"y&bbbTZVK.e;&V {'ʶ_6l/7-v144/DFFݻw2O"ύh}}HMMEDDN<3g\ʶ_1771>|ooDDDDD!""]o|H1!"""""ć"""""{L|H1!"""""ć"""""{L|H1!"""""ć"""""{L|H1!"""""ć"""""{L|H1!"""""ć"""""{L|H1!"""""ć"""""{L|H1!"""""ć"""""{L|H1!"""""ć"""""{L|H1!"""""ć"""""{L|H1!"""""ć"""""_|W~_ 2m09a~CDDDCf@$<|Է9a9q<!( @|cBDrquSX̊Ys1( 1wnG`` NṂ(g""""]h&=Dy魀Sal].G6XsC!+L|Ho5)o1!"""%CL|Hob"`CDDDDDz}|ti"""""[CDDDLJH3b"""">>Dć ! &>DDDDDLJHf0!""""">>DDDD:}|4-!"""ACL|Hob"`CDDDDDz}|ti"""""[CJ+"Ny^O^[p7! &>\H]ͪ 5PKmEDܳ/0l887U pia!KYJ%iE\`^<ԭ~ū([6|2(:gT2X:[Tif~IY#mo޾ŔEQusXWquWTl]|Ugv̚W,jnpۇt>>6i<;M~@{cv8-({9茣y ==:g0B`m044!!ܺC#/&W:.eƇ>ʠ)qC`4k~Ę3^t64vo~5Ϯǹ WF܃Gxy6~ߴN:a, BBbRy=L| .jW…Q˽~'n۷[7GQ Ӿ5^$MB` poV+bXnM*ٍ]:XՊи>6E%k֬U@ x͏2d| y/ XWqENpIX.!Cԭ9f<6RwN4͘8jU`ݹ R"8|WB0s9w&dg ?Nfm/V?nk妬*LUH?zm8ڦq>~}򜓷p.bb1u2ܚ6=;mX1G=۾ 013>g2VmX2u:J(bűlu[afj!Ceg6c8X1GL'ԮY OI ~#P+LMLп?ORwN- &baS8L.K>pa)U Bzط~S>X@1++Ŭ?GDK>çNj%7|&Qb%>u^[rJ nIؔ)}3Uz7pˎsyg]xq漑xLJ6B#xp"V͙bVHIMkoW `ld2=aZf(ux2v, wW[@IYOJ'Ɍd*Pwk.\ FkްYǓŧrZ'qeǢPBhԽ3JT=d,}.6ŋb߾M.X܎tSAHKK۲] U\1cb8qISKٲ9o9S꬇"Np_/ap|?}Vb ݡ}|y?漑Y JSj~st8.!}©gꏢlyx|́~X'N8 $Ⱦr'%MH\-W͝PD߾@~R$>y)̈A>{`q~DӪqsgdg dlm`)?֑>^lmSϯ<(Wn~*]޿{><Ƨ~; nnrƋWHKKg9ƬC1n,,Z6F*x-L| 8 s L[n{%̘=O UX1GȝX\{"p|;7W *6izͥ4oGcv<UC6j rsc+R{np-V=#yc)v&7Dǖݫ/.^RѶ_ԯ][m3XV#PZe _4Kfczg#cl b!}fNS)>Ees>Ιf=i6qEqz~L6{BV`]"vjn1^ƭyüby΃¥8i+LMUeDDDDDLJHz-!ʯb\XE\mByGG,m/}&"&! &>*JHLĪa``y'k;In.NyOhI000p(b5<~1obѺIS$U+Z7i78t!"lLJH3/zv,3>==K­Y#uJMb.3-P Qa=:>|g|vVTl_F^QzeTsC8~T.SQYehۃ꭛[ig/_R9U7­Y#XUXǎ{G6eSXUM`ٺ52?Хu(Ny<`Ӿ5Rӆq&۫Љ_'Q՛7 ԑ5l1l BFv0%Xn| vn1y/?gf>9GJ_ ou>2Ê24 AWt\C?eJ1A;w?l\I aݺxy6iKa <9m]Zwi``Tѝ1~lg۬ڭ[].a"0?&I৉M57$$&͝P66Wj^+Wpire rK7+g ̳)V\׮fU$䮽RL +%eo{/ޮRRSaU |ebڢ ={;ɡ,ѳڢL+7k'qi8:w(]:Ωso]Yw7x,;c}d [o3j])) 3SSD߾%vO0Y+f-,Dy `T߹v8y gl[*"666%/"^I\^aW*+ Ml2_cQP!4%WF]p2xUY4 vFFd21<{P43<Tk E\PNuϥyFyYuBDDy}|4Jٖ|!3::ߘKI v$.\ Ɩ+N 2X+!8fo{LrY4AXϴ2J1>Qw{4OыW%o/0H2;""""UjW\vUf|=+7m@bRVl\%͓(jiĤ$XX1 <ꕫiSizQKKoALefWVهhP&C̛XļŔ5b7`x=@GЕu(e_._%M22VEj&2,S^ٌo Ʀ+CF SpMoP-z-[gk 3!wc:pmm[_i.;E͚R_GvU;ۺ:l}z|zv&N*%YǙDDDD QxyTo#j/^v8JA\wh~||vQ+"ff2p"ׇ@}0ns"g1uBm1?lXy& Tם&ݢB[$=DDDD㓷$oFڽO![qi{m:~;LƇVF&>DDDD:CL|How|tLJH3b"""">>Dć ! &>DDDDDLJHf0!""""">>Dfg IDATDDD:}|4-!"""ACL|Hob"`CDDDDDz}|ti _i}}ŗ>}xa}b\X ?9a53ƇH23YGYY?Hәx8~~~!ׯ_jժ1i$mDFڵqUh;իWR bcc1e̙3ۂtRJJ a011vX gΜ9SAQV066ѣGqq|۷GBtKHHZj+W?VeaaSSS;v gΜ͛7ѩS'.\XۡIbbbХK۷fffXz5WH"~ h׮v KKKmEرc|}}ub޽(W"ǎC>}իʗ/t ǁPn]mE?:&qݻwǩSPdI9rM4/p)))@;wIiݺ5._ 777ܺu 8{âĉ_>BCCQV-\|I}6L|OOO\x*UBHH<==qmmETDDZl+Wk׮ƍajjtڷo(j ֭vXT@Oh߾=bccѽ{w;w &>D3ФIq8zâ&((uŹsPL9sÆ vX+++}pak;,*Q>cbb;v`HII!C0uT3 ҔDx{{5j?Τ'8p N>ҥKɓ?HO=z5ÇQD ?~Ii!֯__~ظq#ԓ'OУG\~X~=Rxx8uk׮;wDN鑋/[nDժUqh;,*Q>6tP:tرcڶmXmEzS_18wߣk׮Xp!9<}v4ohӦ .\t|m۶ғqΜ9FѣG1!.\v!:::tիWz ;wļy'OF(B`ԩ8p 0rH矰vhDxGǎq-â|޽{a``ӧc(TիL2򑄄 8{쁑VXmE$y=zꅿ ؾ};u(wz聻w۶mcF׮]{{{۷/^K.v{niFae􈥥%:!C ..={ʕ+'޽jժ!88IO˗/Yfx5k;vh;,qׯ_']\tI,&>3gP\9h;u6l؀y!==5jҴ頴4|w/;AAApuuvh%JѣG1|p$&&8q"$nҤ .]777mE-&>z|M&%^7pM2۷o VX^z!>>^axpahhK_C#-266ƚ5kccc,^]vŻw gϞq1(QBa@!2. `ii gggmcǎhTe ޽C!o]}9׍.8{,wxxx(Y"-qKUD g(}Ddd2L2EiF<{LƊQF)]V8y1̞=[nZ<{L{N }*ļy󄥥ի̸Keʮp1k,Q^=uUDFFHѹslOyso(][*Eabb"vؑ sѨQ#iۯ_^!Q>&L" 1tP38r䈰xⅶC"RKOrr066ζp\\.P+eˊUVgϞe 6 ϟ+5_q}WbŽ{ለagg' NE˷<߿/ʔ)c<DTT4'wQq(YW.L2CCCӦM ˔)#~W.޾}RQ줧?CV;e/D\\4o|'LٲeExxed&b}3N>W.yeRb۶m[;/>x1`eرLn)OxBԯ__b޽HllhӦ LMMΝ;QpxܸqFƍ1{liZBBLMMajjGW5߿/3NQOyyyΝ;HII9/O5tR>|AoAxx8޼yѣG E3|pEjj*n߾}*NEQF!22;F۷/Ǝ(DEEa̘1*Wg[e꾑YnKn uu 'O/4n_.i֣GаaCl۶ _tRi;4#KƩS0`|={ٳc?/4hG'O"JTBԨQCL0AzJ&Kڿpvv\rbʕ2Xϝ;WXXXȌSTST022UVGɱ~yu]~]ԪUK GGG(3_rrŋŊ}PT󅣣066_Ux#,--V^-J(!jժ'Oh;$|@=dcXl FV7"kӦM>|8RRR͛7DaQ&{T^탋"Bjj*Ǝ+WFKZs Aűg4o\ai"ѣGѫW/{M4QxqmU॥aʔ)Xd † PHmF$cƍGrr2ڴi_ŊvX^pp0v튈TX"8sq+Wȑ:}mڴgϢaÆ|ЄEEE]vXx1 ?`ΝLzH' 2'N@ɒ%qQԯ_?W?y@k.4k hٲ%.]Ĥ $>vXzu YA7jժ˨Y&߿ @v֭Ǐ'N@@@"QƍqԬY<'9 !̙///$$$`ذa8|0E뷺h$YP޼ ޽qkڦuJ ڈrݻ79;СCԩ4hʔ)퐈Aa޽Y&\?~ www$&&bɒ%2?MTP0ɣt"OVXfg_.}~x^%" Q0h TRXx-I:t(`ee___$&&f[iXv-PpaԪU !!!2jyIII_d0t055Ec 9ɮP,_e˖EBqk֬3LLLc˖-Xn߾v۲k P/^ ;;;IIIҴÇ 6lؠ2>eӦM۷oc;#f6uTxB`РA6mZ#;Oƅ `mme˖B_M6 _ƣG ewV®]3f`̘1ؾ}{1TSP /_akk+;~8Μ9Сڵk'NH|}}qEږ]{f;EgϞ9oooL>- 8k׮EN+̞=_K{OGDCP KDycؽ!E "MU8.S N>-=hѢW#paee,fPuy8}4*T _Rl/$͈D29Y6簷#** 6662ͳ˼iq%uPiEx\r0auu#^`鯌'QH|xeV"""(trrիWE.8;;իWJ/$~)R9ί"""P|y3 _| HDN)j2'=2i?y6/ȼʗ/ix޽8z(j֬ v"&|QƇ<2ތ=رcѭ[7mE: 'R䄊+2} 'FoX(BDDDDI!ųoL|H1!"""""ć"""""{L|H1!"""""ć"""""{L|H1!"""""ć"""""{L|H1!"""""ć"Vu{Ɠ>Ǻv$<gD)#QaC0!ʧǚƷ^D]t#QxqFcCG0s(4䄆0kh$%%Iß<7k9u_ވWukкN5(SpqtoKGM۹M7ooqrZFveí+Ѭz%x8`oMɏes vo݄kcito7/_ЩQ=v,ޭ!no?oF;RL20mHx8y 7l \%szZV̟*sL5>|g?"mG*X4qD8#ML|EU]px4A_a8sNߺ' X2sL_pSLjqp) 6?[׮6[/eniٕQ׵Kq\lwcNÆ{ЦsW̚0Z4=ja\;bJ/sƑ!s._8'+έ>z o݇)ϛ%Mn ]ߎTh͎2zH7! fL]Y-kU}Q3Iؿҳ+_[ hQgnS7])i6u{ķhѮJ˔no0 UPUs*x( Czvűk'?.V\!˸p+bnoswʺܘ-UmwlwPZOEzmQk' ׯѳ8uO˿8MMW}>c glؽupHG<arz- \ ƠnY,obdOYa+NBVMѡA9v$YF^q([Nz_l9DLT %=bR̨ב2!*%|)ݎؼm7H(SLj<<ΈtH70!#vS(,6'%&MM!ٙ7>!&M,j>_2QגS(,:)3νWGNiPD2;tf IDATRZ1QQX4}L1kh<{i{-MiPoG*X>qL}yqFDL|̷¦-:6 려])|3;iWb̩p)!=:[kCå,ϛJDv-da*Z2GMX+'ܸre5=Qԫ-^F2}FG} \0q/Zt(MiPoG*X>qL}yk8#p*p؉S WWF^^~ڎ|ML}ćD/Itٕc &>DT0!""*Q版 &&>DDDDD\III9nVrBJN5~4OAPߵ:ï_oDEIӫ`ۺ5h]j)887oڎΣ&&3>9p%UL Q$7uVq6ؽuׯ:ѽy#~AFP۱znлwTrɵz<~C+x<?./MfW0t8~ӷɥ̜*Sװ)|1!?FKOy!ܺvU]}'/DGƏ溝֩hK`󾃸p/ >8w6?{6bքѹTćs^L=K-&]??_E"`49%SYsQ̦4ljjבe0s b/Qgǟ䮑jԩhK}S.͐\MDDDaCDrEGC9Giء#^K!W1[GxV(6pqڕ~V>^C:8sJ19-'/S^GT>/T4u{S33RSs3eSϞJOxookn>BBX{ZXu.ĬehdĄiMlLy 6%lj[nD DDDyL|Hv]c)BLTNzHӓabb¦&x fWoeP>RSS @jj4Rزf5s&R~ɌBltzu;4çPuڻ{I2C aӂZP*h-z(T.Wm֪W[(n^@}Y?0Ldz>y0|~?{fp~FAYV=J|__}J꭫o[~}j-kuޠ~KfjysϦj.yiꮇrzooRxT?~bw=i.:owׯ::=X;:6 ,_\Z{F[rnr6ҕڴޮXg%mm:ڼy-[fu8 ¬?WCK {業Ή@w(:/7H|=A@#H|=A@#H|=A@#H|=A@#¬ʆӆ: z6c:hO7x.yZf p@#H|=A@#H|=A@г$iq$ػᆱfYJ>σ-I8?,c%7nTXX6nXfyuy)//ϧhm{ԩS(ժ3LK3(==]6mң>*Soc1ڿ"""tWX]m7|SݻwW_={o[K#F(<<\ӟ4CWjlzGp=Z}tjJLLTu6VݺrnqKRFFf̘8EFFjԩmr[UUUկ~ݻ+5e{15!nͺꪫ$I˗/#^|EI /zJ>w;wj޽TNN~y֮]'Nڿ=ukxӷo_|i(~z;vhl꫽N)((.0rJJٷo_ڪ)w]&11m\۷7SshWSTTl6tjnHMYqqlR/AiL>}tqk'/M6>gffz_>|اݤ$ ի0vXC=sV aaa>4>}(##Yh7͛JYYYx<*..n~{nߒZ{h?$><:󕚚~3f^xVqWjҥ:t\.ݫ H_R>fϞt͛7OW^ꨮ6+V0=z0ݻw7wXkn򗿘4jo|fgeeYx2z2w}MSSChXf plNqLٜjokJOO˵l2m޼pt nurJ 8Pv]=zЬY}vKM6Mqqq~3}>t;}~, `5`r:zwp8o>-\PׯnsAM2E3gTVVN8ٳg'?oa@G{ 8PJLLԼyc6r]qW|| ]{[[nC376ssC͔]m٬sf{Ԓ%Krw7kiofmnM{ٷ978>]O0~x-]T~(3ZhQ- 83qc;q]fʮQ{6ߜٞ׭[b>|X{Sޔil?άͭi5}K;)"f3|pioVXa ۄYo_3kqsNi@3e+lցol}5.8pY3O;N)~jM{M%fjF@Zlա]Ҏ;L~٪ھ}իW=?5.t>co>{:|z-I'Oշo_4zh &,,LUUU $\.v^goټWwZ?O_^|tk-:ʣt:/hi[^}rfW¨n;<]V3f̰: ƑjӸqa'?Wtxyq[ctgΝ;Lg}>&OܢXh@3eh#Gx>\-fnִך}>VٻwNjuzF0~}L_|~7Xm1vƍ ƍ7o,mħ_|Q999r:v֬Yqyu=SIIJJJ?Awu֮]ۢvXhh3e7E3 t5(??_ZzOykgY[^k78Xpn[QYY08<ӵi&= m߿_+,moC@۷o7g6ݻw7&%%\ve&;;g?L:;CpllO|OCXhh3eRWsolXnvc۽fghhִלY7xWIf…Vt9 =4_eM||}~_I2wIII16ͻnf&<< >ܼ{2dfԨQ/s!3}tk"""~3h\:==kԨQfϞ= xbm{Ϩfn?>m_8o2k֬1&**̟?ߔx˫ͪUYgeufz{:gcCC^u}cرc֋h9:Z'UUUe(` 8pa Uo5\cMfrrrLvv袋̵^MnnϺ9s昣Grs83w\uƍ|l߾TTTSN+VE5WSvqSVVfnV3vXc9s5f~uYf˖-p#Gi/6999&''L6\n?5ǎ3%%%??|qok.Bs1STTdV\15vhJ:M#PꇆL6<c?iӦ'p:wj rJSXXhNlC{*I'OhmM9MC]STTl6tja:5+VP^^JKK*66 8S#mΛ7OW]uxT\\2ZWLht>}(##U:Ĺn(CO=233}ГO>)ݶX[la @ΨiJEFF*22RFlh]se@#3K,W_ǏԩSZjU9u(-2ϯw%n3f^xf΅Q~4x` |ƍw(3gjΜ9 7ܠgyƧ|„ 1bRSSճgO۸׬Y &hʔ)… 5{lo7߬s9G#GTZZiN ]suK?P?#h -[Lq>Qӻᆱw؀3L}H|@*//߯{PdUqu6X`JI|@z5qD ۯify_7M6_~ iq_߆j[jԨQĉxicm5v$9N^Z޽ހmnnFKZ`JKKG Җ-[4}t 6L{^#)R}~ifVRRbbbte> {vޭCjƌںu$>eggO>nIvϗnM+VPBBO+W*77W9996lV^Sow}WEEEZpM7xC۷oשS4w\-]xk/^[oUڹs>#oڵku 8p@ѣGn:wء]vHgn4ۇ1M?ݻ}}9:.H7n اj8[W_iϞ=[}F$ 3lJOOgSL`yf-[pFTRRs[y睧+Wj…:p&O})::JMM*={zcbbKHHSw 7rW5k,ѷo_رCC $߿_'O~ɶl*((P=6,%''ܾhJõ~z͙3ǻ_6Vcv5۷kСb oC!55U5l0IRNNFlͦ\w@:u6w3~5cǎ5FK… uq]vէOz_jn|ͺI|'*66V6Ū/Y]rmnumݺU۶mӨQ4h k޲yҔM(4_^ESkGߧi89yOߵ1dee^ݻw3?PӦMӌ3DO?6)3P6-5 IDAT7p@sYYz)MhR6gu97NW\_|Q)))ZdomE[϶8GԎJJJjt@}v-YD)))ڲe/_,m޼Y8H|@>}^|Eu6M7t6nܨk*<<[VYYHEFF*33Mi߾}>-v饗믿1ϟ}"77W+VЂ ,F֭Wo v69rUTT(++KW^yO%KtW:uVZ巍@5+ҥKu!\.ݻOC"""4w\o4rzz_9j WV^^j*-\m5\QFizt%(""j'I|@]v>CCC5d-Yg?իW+&&F&Mm ;C &4:̙35gn3<-ەkJNNֆ ,Fk5f/vjyק Ր?zWo9眣#G*--MFfM0ASLQtt.\ئô'%%i}z_9j &LЈ#={nkt@giժUMZ}@+0ж xy+x͜9S ,Х^z+u9hŊ$#.}b&OuiҤIV6ҚA,:b; $ ХxG?<8z-7.ٳG{>1tF!V}KΨP^htBr!뮻:18:]KaG3GUSAnvZÙ3gr ^{ê #]JgbqFw}ꫯZ4H|@{Gp=Z}_9l>Wl6ӕ1B|I :[޽{KvO?T)))z$IoFpOScgܸqO-Ԓ%K޽{96á/\ JHHҥKb~i5쓑3f(..N:urss}ܴiפћRw߭$.SUUU}XI[Cݎ;k.iZt"dwow}WEEEZpM7xC۷oשS4w\o=[mNGyD+V$-^XzJKKsN}G~ĉ[N:|ݫߧ|ڵ:q8ѣZn]o9kI{>}V\\hذaZzOv-RΝ;w^eff*''G[W[ԁg3ܔ˕[tK.m޼Y˖-:ͦCTQQ9Noyݯ#6MٳwzS;+cz߿z͚5K))):NTQQ`yJJvܩ4I5dW;vА!C$Iɓuknג}***JcٔF8K8ɓ'رcku:\Х챲Y/В-pf5v}iɒ%>I$mݺU۶mӨQ4h kcǎO>gggkToR$Iiiiir=g-i/>|&NXl6o[[s7p@M-@#4i!^z%mܸgرcꫯ*//O=PvWG};$9rĻ|z噙匌 իMclm{7o*eee񨸸U#5>|XIIIn-@#_x@֭guJIIѻᆱ\wyw^ZNSƘzWD>^zIӧOo4 kQ~~=2|,Zb,Xs֒SYYHEFF*33eS[z򔗗UVi…n-@#k5f̘=7Lrrvء%I3gԜ9snA{Чg֜ϑ=z/{v5d-ڼHN߂6Y,֦M~[cMko[[Ce=g3Waao=Vj=sCSJLKԼ)?#_omzKG E/;0ɓn:M4EϚ5KƍӚ5k7|iӦ)##Emͽ dhݤEz7?ֆaKY:눮qmmsCZߩ4To@{w~jݙyؼ[_ׯZwSf +_hy'=+4)YjkFfjq@#voݭwVlb\n[5!G]}W|{VlϜ;(61V}l}~iphُTUVYw>FCjȐ!֭[fRՆ~V(+U9s@Oz3?k_WuE&Nh1CS%%>GuxTx<@cVXPjmjܸ%ƩHwHΊ.?'5۴`:rW Զ?+[SW]@S:brcV7A@#H|=A@#H|=A@#H|=A@#H|=A@#H|=A@#H|=A@#H|=A@#H|=A/`ҵ/k_: 4+>c/~ =zgh"gW|t 񒤒#V H|=A@*Ir\G@K$[ G z$>.I8`]Btt$HH|=A@#%DFFJő+"""$IUUUG@ z$>.fӊ #V %vI8`A@#H|t 2#V %Jnő+z$>GK$X .fI1G@ z$>[<ő+j7(--8`A@#lI-tNgr]-Z.K!!6.yڜ1nHŹ,1LZ1Fv{R\\up=h[9:u*WrV#EGǩ[$uVEEZA@QII N([nwڡ; c]ɃԽ{/S!ɓ9,gwv:ꦔaJHiu8t $>eQAAߧJAG=z hY#GO'i֍h zʊrCAn%'ҀX %%:p`7tBu[Cf::q:UtR6MNᯬӷJ=nFmrr(/aС$<)HAHH?P \N: !Z99SjPII\j@ Q^^aaPEE!TVZXځ3pYAhh-ù2d<"#BBBTVVluhcV@A@TXxR0n**JdGX IPQIny\.j::^EE:u*䧓:}(7\.СQPp\EE$?ɓr8*'t<'r9Խ{vcnNrr媶:$:$ *))Q=***V!!\ hn$$O9J@cH|4ڡC?K ILͣ gauHtx$>2F*.WYYK!!!$@pݲlPaa*+K N@nf8O=wBCjuhAvLN$>v(WEEyMPll7EF+$s@m|;pKT\\ H|QY)nWllwEE%(""J6IPscG2ƣh$>Zt~{(Wv]hD>c7qp*+Hvhc$>ڔTqqEF~ol6nº֯+:!!!2rLG*+K vֵy8<**JUQqa|mHGn^lA1XB:rcT]Q!LN'p&8c1,Seew]HH"")=Baa Thݛ l6p\<:cN?S]]-ZUUjX<wdH:}unnWXXrz&Mޤnv\.cd%I[wDD$:Cl6@5y?߯$?G 9}?օIHj""\^xSffV= Iw9r}o$ m۶a$EDnϔ)Ivmۧ4(mŭn,WZZ.IYszܻ.?F%sJRRg>RϞrNƍn=NTTTR>v{-@"`:UJxחW~[]㽝L.zW۟h }ꮝ$&v…?kq,ytiOzu-W=v =w$/~wIŎ$=+Z&-ZtIҎgocդIcW>h6@~I#nӤIc$ W|X?>ۧiР~:x{%L$}*-Јt㍗qO/飏j>eQZb}z{;G Mr+'@۷m>ҹ̙?nt{ӥtrԌ^kںu|y+..F;D\2ζmK~1я/i۶%>nԩ 䓯jŊ/*,,TJI+ F78lv[ꢣ#5z0tYa]pL: : UQQjut$>tV[ɓO @WC@VvPTmut$>d 'PRR!48]Al1aut$>ԧOBB+#700H|(<7nweuHth$>*//ѧG& cdQTTFr>hMVX?ߡC$@g+<:4lH|4[qqef~)˩ݽ{/ 6F0!bNgJ~\j#5Ɏn׀ѠA#H|B9ݯ“ kH4Wr FH|eeԉ}D.W1$BwsZng)9y .8veQaars(7rsTUUmK<xm6u;KzW5@QQF@@+--TAIPaI婺;9NpIN\\wu[g=G> 3@PYY<$_*--REEnlhIDAT6۷MƜ^>SQd56!)""Rݔpz(!R||O СcTYYbUVTGrUTRՕr:rr:x"#=TN[xn(44TNvePHEDD*""JQQ##c8EGn@G,ֶoIENDB`./oar-2.5.2/docs/schemas/api_libs.dia0000644000175000017500000000505311757171206015400 0ustar plbplb]ko8_!`p!QORݝf~h5%$oKIÖ2QB "s//_]#RYħ}ܾq2 i6;2 3M܁Oę:|n2')%d(t?9 &2]i8Zʉ\Gr}&x/KUIΥNo.WA=ԽS5J\ /!zj$ H~(P}LU*Bg*}SV]}u7e: M,蟨 <*ǁG]_v.:.\z\0;_$i0߄%Id\R폓e&X_yAeHnM!1Th^{ /iVb"'|H8T G6[u!{%Z`T8`l>4 3bc\×t&d?)w?l\h'zB:}[ o:7q5qÃo fpHzUI,$s'}4}\PnM(r9|YDY. yW B?5xLM:,;rF7p2NhKcCMOHY5pY;ЇN>KUr^#\uaAtY0' ,883;FDt7Z;Rer!2@1]R hHmlޑ(p/4JW.F0j1"`T_< xtftE|H=D"cj،YST{xf=$NOqK.& kԠpm"Goaf" qEERTFGˑTMċxx{l@(򌆼4ސօ=Cs˧b^p tmB0Ӏ>!;6Uܛ!컘rxԧ~söz^d-zvh On>j,I&q.Ga+uDH駙ޝtEr39 \#L(_}B9f_ᣧO'wz˴9=w4?t'o:5 FĝܼfW"u{i~oa#i/䉷%_>#"~g?K*stzm0=ئM:wק9=[A<.։ӟQ׋$ͳ{|53׻e 5/±^>Sqqo<:#K8n|,/"4z|_{?N8hG8-o -3=+3בQYi*gH5./oar-2.5.2/docs/schemas/almighty_automaton_villains_part.png0000644000175000017500000001645211757171206022506 0ustar plbplbPNG  IHDR#sBITOIDATx{P=8jFN0-F bf뀏LjlXLSBFӢtHP *XhA4rqnǦ;}?㷿ޗ{S$Ix+P(<]7 Q(|IJv%*2W‡k}3$s.#VVVV C~~>A7nX|yPPFIKK3A(z+::{g!**jΝV5+++$$$$$$;;jv,add$''gZ6335`#nP(yyyAV'''g.]2$I  kGܗ:0Fgy{zz/\@IJe^x>k֬Ԛuuu6޶4--mϞ=6lؼy[ZZZ_3w_t 큁/&QUUU[[,;;^-11144zYQQ1a„9s8n  ކy֢wʻf__ߤIMn޼,aԜׯS͑ܝ%|۷LªKn5:;;骪T*&w^r%5OٳGE+TgL,x0/e˖ŕ$Y[[h"NG ɡ(ajzhh T*RT*-]p_:+88l6wuuQPL:sw -Јއ{lǏO:$Ȣnv=hmm߾};5'""nnnittt[[xT*U__5}ˆ$իWرcO==Sy+Tgޝ3yիW755 $9<pY;zYT*ٳO;Dq%%%hj:>>|Bqȑsj4 c Nq"Tɓ''LP\\lZ/]4s̏?Z{  ;w.&&.mΜ9gϞ0ͿկVX1J-e$8khˋ/Ι3NII9qͣKkjjݽ{WӍ$3׸GDt9odd wR-KXXX?Ufc^%Lf$f0M$kL@ U444.={3ggΜy衇ƩZ|疏u R^^;wj/YZ{]papp… lw7s/.r9UUU=c:.00G---ڷohTTFҸ叾$ol~Ep.|}k=#9$G]o;DR}W})~ @>Hp@|̫? x$a 9y 0I#a;#N}GԩSK.4iԩS322 w7SM:9{vpsBbCae˖M6]TPY /Cgz&cAݳaӧO8<=%e۶m999SQeybV̳_XCF?.F.] DRXXHIpppFFl;v,))IV37X[[;88*!++k۶m9YYYIQ1P T*АH zϡ,XO>-$ɿIII+Ƭp娨(:###/_-_hӦMD4M[[aHogfPZ@ k͛7G?#U5b_i4zaBq?[t{{dT*cdm"J7n>>//ޤ=###((HV'%% ߋ-:y!؅0ǏOJJraskҥ_5`qƍ6mrcmdOƍ]N"ßY/;#+ !0I@$ @ wF #Hxy$q8A o~/-.wɢO n@3BI|~zI$eH"7AE$I b|DQڍ(@F #_ #H]^Aa^a0I0B6D@|"@|%0%J\@|(Ѝ e>F eF, #, #Hυˆ@$bѐGa_ #y 1F@J|:dz#i@>ѷg!ؐG0:F!.m10rMF#!E^QA$ I0rDh }.C" Hp hl!0'H%yy9u#"IN@N{#<{,xA9P#O50kqj$I4⭇0`9$艴!Hp$AI)%fDy.IȍxSʀxrAfFބ:T0V]Ay=KY0#Y7dHHXYlCgw+!YoiI“ |l-o-?s'ԑ'cX#3! 5v]2"0v .ufȑ#jz>55ʕ+eee .tz>==^$999 `0-w1cZ6bFZ=cƌ3_^N2瞳X,tܓH=WJ:Um鄄;wYfĉVN| &[K.͜9?;Fc]] ⊋E27q9fv`/ fc}Ρtԇ#Ȉ{X,KNj;;;t:l9u۷=һzjHp}F"-e*l===&SҒz$A&kBT؏5O=iڮ.kX+Չtbb"k=}ٿ/3gh4ATUUыwF>^!qB̜S^^;wj/Y+VLZ*k R|g.bv`lٲbL6^UYYi]8XO16%9iIUUU=N |GKKKkoo RIII=۷h4T؂?>>>//ް%55U$&&>|^-22qcu`ٶxճỶѷ$I8P(֬Y멎mo{oIx7^zʕ* P<<ÈVVVV C~~>A7nX|yPPFIKK3A(z+::{sN՚mZY;b0223ydV|^9 "//`0>^ҥKFy3l4?sd[gWïjDDDDDīz8x!Z_k$| ݽaÆ_&V2>I&QOw[dI{{۷.a֭g׭[G2/XjU[[[oo/Q*u$a^#~ٲeaaaqqq%%%$I.ZHQEwJɡ(ajzhh T*RT*-]p_:+88l6wuuQPL:sw|ˆ$?6SN%I222fݻwO(Z[[oN͉hii#""Dv&^+JGM߹sDž0"Ir;vؾ}SO=E?@ ՙw3wF ,+**.\]G"F۞>݁zꦦ!$ h4櫯0**̙3۶m#bժU/l66lڵk˗/S+677$&&͛k׮uL&M&=S[g!3kG:{WƘ1ѣfRTg>y$I}Q\\R>};Cޒ|7<֭[-d 2LEd###111 EEE$I+z^199YRݻW/Il`00?@ ՙwNl/bhhhhhK/Ĭe6S|KTczsm@3{_6Q͑[5/n GCF#\C% #'># \&8#!b7jF 3IS,?t_aIENDB`./oar-2.5.2/docs/schemas/almighty_automaton_villains_part.dia0000644000175000017500000000241611757171206022452 0ustar plbplbZn6+e-QKqE)hfUm22(:g1菕zxlKyH@KIo߽Ty<"!:TEO8껻AoQ+XMՕ73J6 OR4FcJQ^;@X6dXa(Su>7SSnҐ+ONer85cXZqƼ6X&b.?_dV4b{:R6U5Z VƑi" p 2Ĝ軜4_^_DuGD!aH1b)'Z *ء:D$BGD4:%ts5\p,j<Of/=+Ȝ&~{_'aWCV<[j.x8:65VX֋v&&*uA{ɿp|Jg*oY~Wn?Xw T]˧$u_D*\&:y;Ģ8pOi )frCpx718c %|K6;5Mk$3A"#USŸWx,ya⏡ކkT[nT%ۘdud^SW1-lg4- &XsZo$9l4]4$&vB@TvKr6¾ gHSl'zW0 4G%݃B7nwv=5{S8el%7\rE%,FO ˥Ll ybv Ŵ%c ߀>+'+nP3+ kLF\gAL/,RF''`~/ٱBV7swX&ЕCgd}+w%fNsqD8 3i w!)Ԧ9humi0A)}k)T~׬P<|G?Ԧh q:a2JjFY"$PɣeT-kZ &tN1( G]͐=l-]{tCHD5r}!ܞ4ܷ m|l{fx ꍶG<25F2L@][*oL9r,6?~^xn><<\ZZGoڴlZ[[ϟh0̙c*F_d ͛`5jkk+55uȑ?{=P(;./-D}m۶ɓ'1ې$Dt64d&==O?I/=6 RVUUUWW,Xv+W477_}պu!֭lkkkll5P'Vxm#i6駟#,Zfє---Nܹs4%!d͚5k֬9v؍7ZZZ%7 ;SP^^ުUfΜIF:Ahn6 L얡bٲeZgϞrnJX>5Pll߿Vxܰ- ap'&&&&&;a/^yfrxxd2EGGϘ1c߾}lVb(ӦM2ewYmhqZ{-XC{#G5jTNN_|AsظqcBBDm7J0[zd2ڵK/q\fl6\.vtO +@~>|"<R.]=aE5 (׿(zʕ+{zz^jժz(Q'E!V%f[o[F/Q'$9WPį$_@I@d(( z@_!MįVW3X- Q Ư(3m u Hk8#KDa (ٮ M_#;XPI*PT@5ֳfWBXRv*B : _RaBD񫜃W !,@((5~dR2)2~#i%1 ,ʋ_ *rR+ȟW,`QROM,%ů_)V@n)&~UW.̂bWU`B JDj&R į,į$_@I ~%A yFĶ*~􊊛 C_Oө>R\Pt V+&)ڪ'0 >V&įr3fĘrBPqqqrrrRRҟ'lf9&&&;;ٳҔ>rpp'?~ /@W st۶m6,hdSYY5o޼"ёY\\HcO)S_@ɓ'ر믿fL4-}ww7]zat%իt9--.755~WR07Bj?wضenm|K/ ܹ-nONN/ZĮ>]qA 1:C|=t:'6l=z/8o<^?00ӳ/%˥(^"8NL0JܶmKw[/u50LFFF{{h)))ihht+ Ex:$(( hwq;v'Lq &;aǏokk^w8tܼq[{h4Ν;"''h4Brss/_nZ].Woo/?p,DA --p0Ndɒ+V\|ڵk+W۰t;w.//Ͱfѕe˖-jz75t7{)??Ϟ=tM`0 mmmnSf%b 4PRJ=q9233zӏ90̍7~1c$%%mݺM/L&Stt3G_xqlllJJ͛il^o2vEx_s;7vƍ #F7.l6fE8p`ڴiQQQSLپ};?gź.k.I3j0R`=' C_@I2=TTo.  eį|tvUP) oqTk(w.@ЅMqqDI`hMUCa+L#x;:)ѿVȪ !ޟy`+pRC+(ѪfOnnį>G xE@8>D(w9 O[%޶ s%r=Ֆg@$Ψ8YW.LE U&KWuz.o^@0S(cV1n}{ ; ZPkIIє%""81x R0Ԫ2~ vO ߂#z#jY^4~EBW@S hGpW*z Ԕugƛ G ů'Ah^p71vfc'^~UV3!lWukO1$uL_ pBl?]p,FaAF%μD[UbQ}s|!̴p?~m>!,l8k?bKCPxPٻѬ\;wu]F6O}:tL#wAa!nCp[wtt$$$g77wܸq?/_0w y7|Ͼ{ϾO]wuQÇJdN9}tK gC@hdH35._ܸqcժUGꊋrܦcE;n#F B⺻ccczA_"4ɕ]e:tz9s?[xOۘ.7sۅԮjS?qܸq$? a|b{t tʽ4fVnZ*..㗬cBfΜ g͚UWW'bٗ'Odzzzؗ/^5s?y@udƯ۶m뮻}]>00pԩGydŊ7'Nq_|xbo2\~ի?A^YY9w\֪U-[v顡ڧz'?իjjj}Q_3@>㩧KMM]l;G"!b3{ #ӥ{p #O>dw}_ݷzk̙111/G?Q|||ll!r{nvÎ~8111&&f̙:K,sz)^P?N>n殮UTT_|_b`2"0Ho%Q= %m"ci|Jkՠݿ.!RSDbg___ff+Wqμp=gׯ_'s=osrrJzw}+W\`}̙3K.mllk;iB@k;ګA_ [>ů6ѣW^S_f"ĪqCgH44AJkՠ 7u%///!!ĉׯ_gƍ.K K=[WlCl^+@ıӻzɓWX @H?N:iӦ[o`0Bط|ílǿH&_ $]VVow]d OqOҪl mwރF.7ί7W+t 4onaȑW_e"nkq+kNăSĚO;[΄wk!:}ZܨQ~a-ayzHLrnίw?bHpV[]ANΐniiygz[o.?Nڵkw[RRs鷎=hѢ{>MMM .ܼy~3mݶgϞ;#:::D% _bCH1rĉu=cNӝ>};i{AĿ~s9#2.o땕K.}_O/ 91NgTTTH J?U?s̑#GfddTTT8>պp¤$p}:twNz;I)))IJJ3fLii)NbbiXJ1G ZU1BL,]tӦM}}}GBZK.nذ_+=ɓ'ϟ?gUUUݻ7(b銀j9} ϭǵ?+(Fz-!_6L3glhh`WըQF_a'x"...55~{ftY&99966Goiϯ,_\KK˼yFӟC,%0cŴ6mFttWbmuI&3tXw"D9{i\.}rM(ѓJ40ﮬ˕?M NIwQ(z>'?o[Cwvv޸qbwgϞ]SS300pǏ?b[yW|'턪Ꞟ B֮]{ʕ?Zn!dݺummm'NۖwttƖ>͛7futtdff/ϝ;w)j̚5[:s_[6r׻\.~b~bL$OX,F*++$zR7 gϞMg͚ud xQc;tW^6mZBB²e˞}3lnر7n|ꩧ2]!pRPwy.v^0ĉ?s)--afv%d2]tzjjj*]r:E)ZZZrssD5VҥK'Np¯|mjHLD>/x쨗^z)77./\pΝ4Gb (}z] N]s8hWs3;$' v'}󺺺ӧlW VYҤ@n:ܔEmG4JOB-ZT\\iيh‚y9f͚5k;vƍ--- Ɲ)(//oժU]]]]]]Nj|]]]eee3gΤF# V47D &vblٲpXB߳gOyy9o{R\( ?ŋD+W]'\CX/$K;( H5 m p8]/MIIa?0<<\VVFo1cƾ}ج{=C*Q܁M5eʔ۷rn?=[{/h7nLHH(@F &v˰>;;[כL]vIW%0.l6fA{R\x \gyh4ƒn=(lr&[gu 4K B4ŋSNt-TKO ̙3jԨoUwޙ5kȑ#LR^^fgggoA?fn2s̘nB/6mZtttFFݻ ƑoeNtwǏ1›VuĥfQ^#Z"S$vC] IDATG}Nm,**o\reNJI|ۿرwpp?9]СI&;vl``%yVV֩Sm6ۓO>Cѷ9[oߦo߿?==vpp>:uD愐nƍw -.Ze4="U45|<_7n\BB£>Ak|:` ˗w}W0 ./x|߿}O>ʢwuѣGٷ>k*9!!E^^\qIV1B.M5Yc2ç_ᇿ?&M⺺qвه9Py4XpШ=L't3u:{"_{"v NJ!}OF>4AuÇO:߷X,?M.9.mƗ>nIgSH5$ҳyD6jNq_ K/~<̛oy̙ 6Е3g?Sfͪ|b|˓'OqqqpE_3mDxv'8X"+ל9s;fۯ_~qzg!7͉'nܸ_,^cVׯ_zG}488h+++ΝKZjղeN>=44T[[?OV^544TSS裏y &C8t^~IzWG L oeCT4 8iFx̾{?ðΝľ[oU{9b~b*//7gutt<É1113g|ٷ܏|iP ZWSpYirKvxx9.xȧuaZ#]P [SXܚ0 ml OV#J*WMD10 lň6u >~lY":˟~ 'RYϨ)BR͇RӧC vYjeJx͸A&Vw6/aP6@j%Z -4?0(=5BGT,Y.@Bof!Dgza{0Z!X AYL%¸ A'(BFDi4mE~tUJw"U0C V *x9r;v?nقUȢG>r "O!0 SNmjjKP8LEY oYP#UB>GE}"2zwt`c{֛a]vd20 n4kjjJJJv;݊Jw` CUUՒ%Kyf%7IU _!  ^w8tۿL&Lt:Ah;wnEEEEEENNh$._j\^G)XՉ|7{챷zK}+,Pr;)wRWbٲeZeRXXXPPt:ϝ;F)5$ٳ{lZ(6c ᣲ@8zd2ڵHN\0pYYd1cƾ}Kvƍ 4752fr56mZTTԔ)SoYh KK*T4Q辄;g+zr;*Mqb1)D@߂ AMg ~] н^bpW)(E ;r@("E %I Ql+Ç-"j Vm|z@dŪ'~ŷ4UTI~@dR,W9#~\%9 "~D6E tįৈ "~\psbC]W/¥ a aAD?f"~"z(T0LIIIRRҘ1cJKK#;h*_L N}?ɓϟϪ-aU=Nm۶ɓ'1-4a_tݻw昘gϊ$ "VපȟD ؘΞ%IOOO[[[ϟh0̙c)=ĉ'NsUTT%.UU aP[[[__r$TUUUWW,X@beYYY]]]}}}GGGlllIIt)mi,h=z_=j(BHool;P  a+Ȝ^w8tۿL&Lt:iTmRC8w܊H]|ju\Uh_lgΜuuuӧOC qacXlp8Vkaa8shljj b !{)//ϧk `hkks2+QeD%[xڵk\rʕk>~46V_KCMl^o2vE8lW2 3<<\VVf2g̘o>~b9 nqƄқUrfrL6-**jʔ)۷o,X` zgFh,))a+)OGjJ: dNV 4%ٙN~D/j?y 0XG @g˶b)n?Bpi{96~Ź@aUj Za@J |U`pJO֧+D!,WeV#~;r ӽ\_ aLaڌ6yK^.įGH.oNň_@M$v5ǯrzVg]7M]+uNKq Ca$))i̘1D_JlFnD?d||_xҭ=&N۶mɓG9w=[?;)V:7No#1==Ra?'&& 9sl6v -uzW{'N8瞫Jʂ/f xBVm*@,^x}}}'O !Ο?vegؒrݺummm'Nb׻\.m)a 7~Yh4ͩLJJʚ7o^QQf,..:ٳYΟ?l^j S`k&Ocǎ]3iҤ&tnzaL&ӥKʫW崴f^T̍bZ]/`{C[66rKty…;wtK`ۓE:+#pTT`nꣅ$3~7Ou2O6lPSS3z_|q޼yz~``@ g_FGG3߅\.KQL^?88Eq:4`ܕm%V ^, ڵk&avXSSSRR`VubѣQFBz{{fsOO߹)̏A3A&qܱc|„ 70aB{{c~OǏokk^w8tܼq[{h4Ν;"''h4Brss/_nZ].Woo/?,D%>}3gr]]k xAnW|ѢE.\btB,YbŊ˗/_vmʕb8swvvl"blٲpXBsF^t/ٳ}#GqO?=f̘[g_L3f۷߿xؔ͛7zd2ڵưy &֫nݸqcBBDnHtrfrL6-**jʔ)۷o,XX\gyh4ƒ*&C"y+|Sw3 d &q{|P3~x|" eO0 >Q+*1x dWaT??~(JQ@OF+WN@0 J@lM|wP8^!p_qtwBXP_ (W݋bpJAƠA:+h%~r A@@ye î _12*Y 3DѱB B1= s 4hqZb!"0 !BAb+0ڊ_q,v 0Ümův0h8xOg %sۑ0 ᧹xO5g B`EGǁѿ8<0++ #Ac د DNߓ0S-PY D L/ApOŘ?En]L1: 3Mǯ:F((  ~@د X42 ( .+qB 3`(]+A(</h-~e)j+jmnJ4|A_t!8h﹝5:|X[Ԉ+@(|=`i%6Mǯ x |_=!Й ``!7f۪xgY+92U8@<"o:N+W\P9Ǝ;l[8]@J*nP1WS4r'uիWwvv^xqы- J#pE muH,į8A>|xƌ111f244T\\?&۽{l>{,!dxx4%%%...//|'Ǐ /Е-st۶m׆],N㷑ؘq ä駭OLL4 syJ4\cΜ9 )))/Buu5? p+ԡ->qA/8C/^~'O~DŽ?ϟohhhkk|2MVUUU]]ӳ`BHYYY]]]}}}GGGlllII Mnݺζ'NxY jkk].[zz4h&zo#bF9IIIYYY+**lŁ46;vwsnJn9GjƉ!qRLٰaCMMѣ_|y^/}|wp\:b(BsM'rz ^, ڵk&avXSSSRR`VubIUUՒ%Ky~ 8W@C^ Ai}wq;v'Lq &;aǏokk^w8tܼq[{h4Ν;"''h4Brss/_nZ].Woo/`,zoc[\.  20 'âE.\0440$,YdŊ/_vʕ+6,,,,((hiiq:Νc3,..lEEEtbٲeZ mF#M^߳gOyyy~~>]o0 C[[۔Y}m1֭[}ǏϞ=6 ZYӽwqG\\\BB>?~wvv<=rHټw^/kV+r-ĤWTTpSMOO[^y[O=T\\\jjeWJ.On)K_}j Ueffӧ9ra7n|*\#iQ. MNF1<<,]DK_:ql⺻o m6[iiѣG^JKde=jHp@]_a,x  N*}Iz*AITPZpĉׯ3 s ;&'˛3_!"pP1~55kV]]dqqq===ˋ/%'OZ,lXbO:iӦ[o`0|awIDATBط^@_EadcW裏v{eeܹs~򓟬^khhGuKlٲӧO .[ltUo=S&YYYw}wɒ%[iiiUUUzHi8jO=K:ۚG񱱱Cutt<É1113g|J|zԩSݶl6GGGgdd޽ݰ?`9rbyWٷ^{4_FJ@y3-a;n:,~_O.@zY0|&.A O8[BX!xզ`}:7~dÙA NjWa7kZ ~ n0 @L5PE&k`pN WCae@ρBX`Z ~ ", 'x%_ !,hS؂W50v3x%_C-Ў0k Sh@W5pUt ^ \yTį!pO'O]Y_C .=!+A@=TC+Aa$ ' ~ 3\)z@DQtrF'@Yd`5|8Pk!2LegJBX' ~%W5Rh}-k!&^W5h|Ek! _@PN!_+h77BX č\įr@;tURܰ+ fJ3K;x8 lT``U0 8 į@q0en< tő\(P7Q'\" (,.!~.JbRZ%įZC@n)w WU'_62ÿi?*:į/8Y8H]!~Q! r 7q{B}@᪦t:*TݫN}KG Gnw~:}́1p/AWXN5t:ݶm&O,h;;[כL]vN\p%0eee&)::zƌ',]lۍ7&$$ДPb\.l6fE8p`ڴiQQQSLپ};?g+,^/k'Ǒ~ !X0ȥB4$RtRakЂ4bdk\jmWHNCHd4,hX#B*7_!˂ h*TcAʓ>t=SJ<[@@$!!~8(KpZ%F) |PE$WA M,Kp/ڡP_A' ) pP4įnPgZ+ ~B8 Ad&_AB(VU @zL2#G4{+ddddddٳ]xo`0=Eggg+1n9+{DOh%f=Oor^_n?c&G E"EHM@>p:O754v? $`@ۏ Fx5t~=ƿ s 0 [s?s̑#GfddTTT8>պp¤$p}:two|'9r<;vfn,A qJ SSStM6=zKOOtRoo ^~ACnݺz΋/=zѢEA!l2Dn>^~e=s̆a_5jԨQx'RSS7oL3q:k֬INN}G[+**2o޼#G?K0Xx1ƦM$p8!XynݺuҤI:I L$0ٳgMrK5mڴF(WAeq+**lb)i5z{{sΝ:uJf*--mmmvK FzOP D !h4AeeeRRRVVDOJ:vwl"K_xsxYAtnza&NӕMMMiii ä577+i&&ҥKtիWSSSrTT,_nONNHB%aZ.]:qą ~W]/FnVDb"2}cGKty…;w9bk`Gq544L:I" șΨj,QŽ!tRFEEBNgllАha_B0.Kq]x199YheMMMIIICCn+iVAuuumڴƭ-[}MkL&322ۍF)3gF+@ehXlp8Vkaat{ٳ۷=)VO r֭>gϞ_"!Q1+>t-h #-ÑۻǏJS0e nƃx R54񶺄gaɄ`Ʋd%A=LgLi㧟~Zt1 ϗ_~f޽O?Wt:z^ uϤ]%WL*dɧL`Q>SI5_= (ӼOPJV.dħB0(OXy=z}}}s2;%+cDk?klzXȯFz?޽{;d7 JVA~"ʄ6S+>L ":|vȯ)LbX_TP( _%#f@߉ C#JI۹]*V'L ( چPe+r:2_ gpppiiȑ#Zɤ~PhuuU[ BpxlllppP+r݋~? [%;$u޸q_~ﵒ@ pB:s۷޽0 vww?~s``@?XBtXDO*Wrˑ&x??!y!|Ɔu GESNi===x… ,Nt9E3!zݻw{<O>TUU/)>^(6N>۷oihh oܸcǎ~M{AA!TtŌ͋(o! #!Hȯ0+ #!W@+ImȯT)j ƾS?ME~_ Fu;wt׮]J"ﯭÒ:WVV;rTUտtvvv7;;k׮Ǐ|ӧOK CPoooMḾB~~޿p8qbcSX 7^ʕ+~R!~i;w( cccZА^\\`PR3go߾}h`ǝ@իž#GH900d2 )3׈$TFGG矟~ [OE)j2t8O3Za8zꩧݻYy\}TUҥK~tvvh#:GJ|ai_~ /`?J l*~&rE?#!Hȯ0+ #!Hȯ0+Ķ@N|(ZHX 0njIENDB`./oar-2.5.2/docs/schemas/almighty_automaton_scheduler_part.dia0000644000175000017500000000450011757171206022603 0ustar plbplb]nS[(6YL(z1mUQ8ʒ!x/<}X%{ے$ˉ40DxH?m;O:Gir#0J&w_y~1$ ܑ_w Iځˁȣ`),$19˚.k.gigAig |0uE/E3D!Fsı o=9 "AJJ$Ml_2hśK04sas/ﴠk8 ,}Ji˪N& Fzh 4It;~yb<5R7Qlu3޼;j9ᄷ(,j`EHgv$aC0]%>Ph<6 ("zmJ7g%׌%ppJqKGU EDkW#fu)IJ#yX K \s>~"֙|: /!0_'{#8rSѤzf`9U\$ py>\"Hf!Vse>TeyFjPA{!;P5G8 8r)`U>0p0\cWQNZP|V>5Ԋ?y"%"&È<CȇB&Jǚ#d)7Z d%<)6HaxL1>V>* M{޳)E y@6C<+)KD$' *+X7 ԕԢzi\٠87'l{kÁ)@P4%P!߯wَl;, g]^F8RSHS䷸zEM">FS=7jywC@D>C;ܶ[G= W/{lei5;} Kj5cX3qs12A냏,9u,0 PÚcEI6P|㍒12)bCndeI"Υ1JbӜkHOP DΒxj 4::1IlCTT"n ;tL,A!W-M80tGljtt tɍg:q4eIj&|.S H$>6(:QU`8}jcT\5㔀ix :IKv:n>E!Nu=Y0DXp "(ߍ|ӽHB0a$8f+Kإ#S  5S֪ L}sTPrs̀NU3H=X\y.(ͳ|HJR2 L&k_ F`ỘsQl . lDdgZN鞢|YZZgxNk3~'o߷R=zI}fa8X$G߿۬nXx'nOӗ|7h./oar-2.5.2/docs/schemas/almighty_automaton_leon_part.png0000644000175000017500000001724611757171206021624 0ustar plbplbPNG  IHDRņsBITO^IDATx}p?p9vb;3&'iw$40 xq7]Qpf>}Ν$UQC` [oc֭[tiss+W_H0!1ׂ~[o%''ٳg$/Ž+G6lؐyn֭KOOh4%ի[f9•^p!++rQo].WVVŋoܸ1uԘ^?qDʭ[5oWpfR&`w6RWqqsjkkVh\lA+Ww^]]WZE-\]]}VSSS[[rxK&TPUUE=rHbbbnnԩS/^dZ|%K:|Q$/64 rDl +##ի理$O\~xڵ4#---Dͦ766oyKm-~Gf͢^Ϝ9?f-`ْUpm0wa槼UΟ?y5w &U Jqa#NjZ6""Jn$ZmOO@ww7ޒEeh?9p &TF$LMMsNOOOoo/IUXJJʭ[n޼ٯ_?Jftvz[&O\RRRRR2eʔ f͚k566\GHU{l0 w/gϞǾ,uv z` ~pJ;syw'Mwuww?={6IoɓVj8q%KHϰxKNHH/ ~m0c §3,$>SYYIMIJJڻwqٳ%V`w׿u/_ ܟմRf bk$Iآ"NzX%;111ԒCt|ρrfl.믳Zׯ_-jT~I &IY ΧG !"'5>T+p/I~ϵ{@`g"q p ] =C iz*${k;2eJrrrdddO^YYIHW: ?EhW^bł _n5̈́ $jx|Ҧ$. P%>jXСCeeeO6ԔaÆ}W_m"HgX{駠)^7?\z5V˗S/v3 3f477˔ :Tכm۶1?^VV6l0ErJNNΆ x[hݴiSvvvTTTvv-[ܾ}b4hЦMXMrZV&M vM0ܝj))) .~na>$Yپ}655 { K/M6uRqŜ{R>p8͛GF8rb;vӧO;SNeddܹUZZjXjjjNӧ333ZSO}ᇏ=r:555̖3tvv644̝;_g&M| rYeN-P&z=vRzٳgsssףFoxK#I{~~w}'rw߿o߾ÇSGyAzVEEe1 -֖na3kD`0)3f'gSѬNfF[ʕ+GM?e߇z f3 tYbZo﷿mIIݻwYӬVkQQQZZVe]X|%gॲ! 1bıcpjoogc2d˗?qIikuUTTdff߿?//o͚5͙3'&&$ɮ.DWpBa+s銊| |Vڳ>[]][QyCdeeeRR.rwS ***sI(Z7oތ&IRѰ"##r!.>No;jrGdXÇ]]]gϞ>}:n nԋaÆ:uptttTVVN4UVV6`#G8NAsƍ-w7eǎYYYgΜ:sLff&=_fggгĴeĉhoo߼yO?MdzzzUU3 V^bRSS麸 ,AT -ާONRXXH=Z 8H?~d2ƍcUٳ'???**ٺ:Ųfw-aŢ,͛mݺl6GFFfggoܸQdkӟTM<>쳴4=T'L`0z}^^ާ~J.,vRǓ7T$R.zNwCTA@`)*+AVnpH RXA% !iTpKBeRXPpIV4VHCZI XJM+t!- QHGr`AFЂ)hpBV YG+T < {H@Z @$̐V!䄴 K6H+o!䁴KH+ i3@`t ?[+H0t?V0 $ 8TXA+ X  ]HXH+ !CW%1 ]+ V % ]KVQ1 1w;,X )w dFOS͊Kj]/)f]G5F*2KiEAf5(^;I( .:$  ,( ½*.*@`eeeCfy۶mY6mΎ޲e =]ڵg1 3fhnnzڤjB8XZZp8.^w^j֎;fӧNS222vI{ K/M6Fҭ/*;vlii)ٳ낂ӳ7|p˗/ӳZ[[M&$XB~R8d)AVHM}Js\oݕC; ( Fa\vfSipsz駏=J=z!C.o/]YlGk2bbb ʆfm<[M eXJJJKn߾bDEE 4hӦMYVW_NIIYpn'×N^{ut ?~to~w1AM),,>p@bbbiixbNN޽{=Vǭb83gdeeRJKKYOd~0--aܹDB)HIq K.o[[[ ˜C;v,.$I={677cuԴ|޿[vZHH+ArWjoog#EΙ3'&&$ɮ.na|,Pݗ"~cO6;wb<^rǎˣ^fѯO8{  Y3,_`4rC3^Ka /̜RYY9lذSN9I&q6mZQQQsssWWɓ'KKKķ{zzzd8{춶6$;;;_~常W^y|eddDFF?[uөonɬ,G .dee6].WVVŋoܸ1uԘ^?qDʭ[5oWpM KBPh6[;wje˖r{]~~ժU'O|/8|ni555H'd!Y@yyy UUU#G$&&N:uMMMV'\d]T":3WR߿BOudd$xq\:tjZ zzzFcww}:SxKm뭘eh>ȸqIwIHH8uԲeΟ?oو7 ~`J} ˏoi>,޹s:P HIIu͛7'UL:sŷ'$$Li0?-..)@f7Y4@{)لHfdp;25% +Da@< <0 >@`lY- ^A`̐Y  XY  B2 |L1+ m6oddձ&2{o߾ ]mK5k߾}?яZ-s^${zz' -ZjڈPX+f'S֫j{zzXDɼN]mK5XK蒰wߵmmm>}477$|Xns=mvʵ5!|}mݺ5)))11q2 #F0 ˗/(8::ioo;wd0`۽?yd,P&̈́Nw]>}}jb}}Q"##G}]x'nޮssS%ٚ%39$[ @`b @1X@<ķv{R ,1ԝ f,3P`Ne@FEE۷  ,=ԉLi޼yUUUGh'xg;{LJy'މ x.]t:|XvKgB:y|0==d2mܸǁ. P'2]#F+sR [kd6O8!̙3ۮ6 Nٗ[cccݻz)N7dȐJv/^q@`b @1X,P ( 7 ΰ@14#J3,P';IENDB`./oar-2.5.2/docs/schemas/almighty_automaton_leon_part.dia0000644000175000017500000000257611757171206021575 0ustar plbplbZr6+8VGΤtᶋ:NG04 d[Y{G;L2ty.{νxB0:Q!T9 &统\{w_Q;h| !㧧'c$!YAah\(ʶ HNf+"r =<_i-+y3F&0ٽ=ΊiJQmW+͢viOU{ X}!AR{\oL\KdQ6ʽWzL.L$bq y@wnͱX-K(4j9t/X3B |LT~i~<sHv27&䌺rRcǞ59tՐ}!Jر!W+QRmDd^l>L I,N+P,^9.' ze} 72uRLwxRHmO*|(^XZwhFܜgBuo9}Wbe3 p6./oar-2.5.2/docs/schemas/almighty_automaton_general.png0000644000175000017500000015760111757171206021256 0ustar plbplbPNG  IHDRLNsBITO IDATxy\T?30 R7M.Yd*߸f)aY)Kn㦙ZK%s)7TPfda|O3ggsμ99lys2$ IdήKo@-`Hj5 =[o17gWOԳŧ.hCrod2(I6⼼oZ-" &9`HE+y̙_~yƍ7nLII9}MxRjeDq6 A$ɖ-[bŊ<*AkkkfffHHwRRR]]A􌎎޶m뒛7oN4]v bj dիW?Hb+Wfgg5kԨQ˗/_jw͍g ~>Z mU]zuhhK/j-QGFm .jdٲeӧOWTwYlYZZivv/\RJeFFAfZdI]]Ϝ9ú$!!ajZRR.^ŋ'OZN:5qDĉ? z=6vڰ-z/޺uKRT@(Hf J$5 \.DEE]zz]VVJdǎ7mt]:%L $IFDDܸqZx5&TYg}wwwNGmiiqww3)--5Eo-ۆƍ2UPmYU ]F-?###K tha$@N>---ٳ\.www f}7}߾}cbb;=c2ǧ (*hbc߿~[YY١CC=dVm Uܹ3̂j\xx۷u:]kk+Iz M6.yg^{R^_SSC!oߦr.... )K6lm卍*xòmh.[šTpC ,7gΜ7nt/&%%s]|$IjxISSBP(nݢzA$%%WTTTTTз*#9-*((hnn>vopBاO5k466Ι3mh.jZZZyyyyyyjj-Pra׭QQQqqq{!IrݱrgϞf]vݽSN7n2lhh1cR Y|G)S̲o߾=zW_6g/\Я_?\G{r,؆Le§->>33&mZm!>z͛^|)""Y&ⓕ8#BW1RSS}]V;{u֙Jiz޺uGmm-6xWKKK/^L s}ggϞA!-[h&M:F`V:}||T*Y @@͛W^^^WWsND84f; |޽_޽w}G}wޘQ՞>}Ctƕ7#s zqԩf7aD缬 .\zgϞ;w:tE|Il/_#Po;vX__O1bDjjSO=E=wٳW dɒcR>|xTKqd.Ξ=o"mrŚ48֕<曛ӯ]vaoo ooolz=s:sJJweeR766pd.t:jrܓ vi86;nxyy >~S12zӦMW(#F8pYۖ=bl===WXQXXf>uuu̙QTbSw?9`3gμ7nݸqcJJӧ]Cu 'O<}tz=2KRRRLLիWkjj6ol%u JHD߾};fn 8}0wtIt6 JE-++ 6Nt1cܽ{^xW>Zӿ۶m7oW^Ս^ظvÇ,'bĉ*JRM0a…|2Y+88ҥK!!![Jճgϊ _iYU_f26 6oޜCb|*NT*[ZZ\ԊVRQQjժSN!˩5-VjtŨd9V5%kmq6AoF`Cosss f,<<:;R׿unݺE(.. f Zdɹs稷7ols-S3,,B.O"4oߦ^ed&8t9`E477;v^p!QSA̙3'%%ƍ:ŋIIIdSSӶmۺtbQZZZyyyyyyjj* 6^'|~h4}'''%%%#f̘A|rjMX/H,+l7P333R5f6_ իW'LgB)4|6)Gz| t5ڵk׭[w}gWl,55,==}ʔ)f}S vu^;AXsήXtttݻu֡CKYOTcVBgejE\DR)Xc 0L,c&AN`Ʈ@8&6ńAHx`cL2@hN 9&N D0xśo A{lÄZw6 H)1dv p!1 IxIx) 9҄\IAl @"Pbr +9"ApC A A!0Bx`9 o A A@x`+r m!pرc}}$\dA )m^X/[nMOOd[߉lR%g˕;(0? ǧͭ RYUUT*卍MMMTnzyEjJ8qV_C:t7o^?:ta۷olw?I]޲eĉM$~>|Rغuz)"RV ̅i "zd"6a !Ef͚1cƨT*J5f̘ 6Pkkk}}}}}}kkkR/j[o5x`B~[n:NR={6**ѣ?5kz) 75xuѳd*aÆ~4`Co<o*7ӕOm6Tq\X{ u֘1cFy-(zF5jԁ鵚Hӗٳg7oNKK3^6eʔݻwsT;O$"$$dÆ {w555O=B8p۷իGTTԎ;82DT.^޶QZ:G|Aǎ?S777uaݺudR.]~w$KKKT*3338q '//oEEE>//o„ Gy.]Μ9|̙Ν;ܹTnr6/gi4?pԨQ)۵kGֶoߞz=˗/[,55ՠꬬ,*[SwjUTTSy 0%A[lFGGS Ν1;;{ڴik7n$''s,dݛiiie-̷sP\\\UUEՊE?&##zTiΜ9ThvW^ycӧ/\1==A!ζA1‘#G(i$͛7򘘘[2s3V>1rfHZSSRH׭nnn^z9sӽ{ussA XSN4=9y 0%khj#Gs,dݛ!!!#>]R벖qu>)Me;lذÇs\VVFb$Q`LO h4aaaEEEͅ ˄GH/&88Xfeeq|g{{/)) ?|k X,((6m\rT-knnuK/d*%rHѹ2DP>>;vO_~?,{ꕗDZuo޿zOOb-T=k-eb-]վ~~~CL4u̟9s z%%% R(OW;yaøb+M- zA۶m7oW^TK3ݻ/|Zܾ}{٬Kz葟y=$IOٳ_=D:jlk)A$e2^駟6lؐ`(##H(˵Z-NGgZyF IDATA,|ҥJٳgEEA5aujPOzNje=9 q26L)AAAw^z%\ot#>>>''|ӦM)))Ky^{TS#"44Tܥz!b :477~;l0uXXح[ GJ r0HoVa9 qm~# $ddd]tqv]@=W ֘3gNJJʍ7t:ŋ|uɷ~ۥKwwN:ihh1cR Y|9YK1eףGjZW_}E/ojj5kUjT5`~)|{ZkH̅gH4h O&+]zu„ 7oHcê&&&SO0nX>4N+\0] a^`2^lϭ[k΂,+ǀ*/V6&iNܕeeeSLHiJ]vݺuhVWPy.s[+G8W u\Yt9{O8!%)B8lH84ݻw֭CK.Hizvܹ<<<ܬXcF5O,1iA Lg[d{Y"J1`haÝ'!AJ ڟg1:& WAf֭[BBBO-MLLT*wsѢEC aNidk֬ԩϚ5k(-->|KKKd---iiiׯ(ȑ#{􌎎O9t944TRi6A|=C$b`ȴ+W8T2dES\\{EmsWoO}ĺ(cǎ?n׮,X0bĈf=:##fʔ)gϦ6ǡz0tb=>Mmx"eps+#؆HA\ yM͍"I?°R$hEE{uuubb͛Mms7Hpp0G3aet$F&"'''::Bҙy"hjjR*tAƇyPd=X7kAXOsu!σXѯ6i!mڳv>goN6/pQQZ1upss3N R~H-[p,4yf\{{{j8 k3ynOւX7k}_`s!k<ە#P1`WPd=X7kAXO ‘B-suQtYл*ApH G3hllY\YYIeee R2S~AJҏ9g!&O1zh&dƽ[օ=YĺXkN͛7O>M/d-=88Γ ݻwVrIhC<X7 d 6ZS+Im@pOk 8wwķ~Ν;܉ׯ_h6l0vXdGR['Μ9s޼y߯{w8J6mŋkkk BV eee)))< w>y$ 2n8ǺLܹ={^}U ncnذAӛhZmVVwJyRl{( fm:c's?hEAg HM `{Έ^< Οf̘ӱcO? *A11Ý;w 1tлwR Zop_>rW^yyy YUWWVTTЕ4u1ܽ{7u~ 7HFFB"LoY7q2n̙ 4HPP˜2۶m ߸q#a?OmEփuӱĊd > `ёtAD I!62<ĚdlM); x@x! چBl AX_.8bd`@\ bA)j" 'UNn#[n.+RNopD8VN _yZܛ?'Pl2qDn%)jpW +%AWŋSNrJkkk=<ؽ{w:w6qo{v)jB9i|V ,)je|a>BpA,Xq…?GRΚ5&KOO_|9o9rhEE{uuubb͛Mi*$Zh?~|ɄzSSR^63((ԨJرU~wV˽~gI%.))it VI֩EZX A !"88gJ[n`ذaׯ_ꫯd2>V7hZmVVwcǎݰaFY~]K6mŋkkk wY4J%Eby0t`N {""##CPPGc78 XpB4h}ㄕ jmm ȓMM^S2[QЈ# '  C7` g"I'pep@xܜ][d&:u?}r޽{!!!I[HH9}ԩÇkׯ uVP)mBx5p!DDVVVۗvQ*N{뭷J>[[[K{'.qFBB׸qT*If ]j]RHɓ'ر~})SPSRRVXuh4OOO֏h۶m{GZm||< ē&MTTBK`軷 ̂+ `CA믿^__m6yQ{E%Xt1c޽[[[ /+$I}۳gf]ңG̛7'I2==}jZV'$$5a-: +JYYYpp0I&00ݻw h4=x`ҥ>(Fcz' 4fBājgY`JAߝ·k7 9$Ij4k\N%z*,44$Ɏ;nڴݻt>K"""ƍk׮5a-4-Z---TK?zqn(Z^PP@DAAw +Í5[t |p@xËzgÃ+2LOYYYEEE~~~6lHHH0^RTT?744Pz\j tt欥V?>>88ҥK!!![JիW/Z=qӧ?A|{9p]I7oޜ={yz)4zf|-L rJpp0f+ bh'D OW~mNJE A䔗oڴ)%%u3]'Lp~g%I2))ҥKZAAAK;Fsƍg}0--mʔ){rXK!MLWzY~~V-(( +**Zvmrr23K/n:~-[ɽ{®\B$5^t=j:uꡇ逸⋬uVP)}c蟀eOmz\(# rZ[[<<̺oҥ{N6nHe0c R|rjRH$ףG=z$ٻwcǎ1Ǹ#Gߟe˖щDS幹}Ok9TJ{Kas!AlG ܓ#`Vb+W^0a͛79`3L66Oa,'G(uX,Z]]]VV>eN$8"f/NZ8lx/ A~EGGw޽[n:tXt)GJWn!tD87`AB&8%EgIHg&pvFrH8A0XOP_N 6'`xc8& ^@ܹŃ)xBrA$j\'`8΂A qf̝p D @3}ht]0Axvda$ΚGSsE8o@ib~+2s:pS@P@F; qG-#G<aaaSN˳yAfgϞ0eʘzsuA#t>LGڮq+D8`Cdmu2-3gΟxL6fdn ݻgS _zM\d >7t9a >}ZT2X"33Ӷ7㙕[kkc~fV̹nQ H9p< 6vH; XfTgӦMACDff&b׮]W(~~~O?tyy9&''_~^^^۷og3p@Beʕ+ݻw߼y3kH͍nܵkWOOϮ]~'̔v4h;*((mUUUpp0STs }Wr \2a???//|| u>{ \cl[T0cpN~AAAj#޽{}ZEEEܹN֣GSN577/X$;;_6k$#"">쳦3f묫J?MjjjZ?dR0D`WO۰I^ހ#q ---_|~[UUC~{fH駟zM2dYs#Irǎ}~zM_0x:ٳgYsxbdd$477GDD\x8 6<+&$$TTT:uɓUUUO>$B 8yUwP((,,4kuJ,X_;w.++˂A[J xRƥ5Vaܸq&LxrsskjjZZZΝ;O/Z{%K'NH}+477_~}֬Yx≣G~|jHļy^}|WϟgE77^{mׯ{ŊUUUMMMONN6ku ƏцΝ;sXvSȎsya1go6QrdMq9q$1~qW<8~)O IDAT@\G݅\RXX8rHR8po۷'tiu]rJ1n޼9&&F.lݺT2c<{Mvژ1c W>}^^^۷8q][\U/$6Oz;N?cMtbepkRh{g]Hs5er I:zdNwe|i KB!J5R$QX#)fh3zBf%=(@ +fC!ǀbQ'ދH~g AQQwi q@5<"ͻppV = tEZmDqnS 9R 8GtQ;H\{h8 B!C#E\0ѬZ7vYC!XAtHpMfAY¿桂M XrFSRcol#9$kB8~8olԾV/9d0ua3x"[qX 5rDD8N9RPDđEbl$q jA1o6 k%09Bo,F2p˱ %GAcPnhm1ƙQPDRޘ7H!ZC q5 lH:B˲wC;r}@xk\@huƹ9_%*md2ٮ]P(~r`1gkp~1 J"c0dpJgI}h3O$?d޽{G555~-kJ 9M@[\?qݠaCNsY r._#Po;vX__ϚAXuϝ؉)cl2ræl9 lq͊N9O0!qG~8grFٵ"ޟÑ?[0Fԗ\9\o֭[}Y<ybsCT딩OZ[[w)Y| >YkuG 8eFDWUtҥ(::^X\\]RݹsgٲeiiiԧϟpJR*A̚5kɒ%uuuǏ?s 뒄jJKex⚚/M2 @* oKK AG%BRӧSN .2eJdd$hZy.]qnݺQ5a-01Ԇ5N9wΝ;/X իWGtfBm"S???j+q7Y"~߶^ERJ`W|B: .ܳ^$I=SOYYYEEE~~~6lHHH0^RTT?744=S.kZwww t:9k)؃ׯ=$Iv5??k׮AkYWae6ƍW4HkA+oL`cڌ.}nBx#ɬ SNO0·$C͞=K"##׮];nܸ$q SX9CDbbԩSIܻw޽{ym9O0z{r\d9D]C@18lLzG8Ҿԉf,dsIIIqNxbRRA=˗[ZZHtK Bu}JRRRzzzEEEEEvRL1o]vڵo&VM\<l?Wk(%ڛƳ9ܒvev&f|ښg$w+{ya%~m.];uqF*Æ3f(ʐ˗SLbP% 2dȐ!Mcm/kYWae6  s-Wk@d0{N$6F}4xHk*Vb+W^0a͛79"j Ym'̈99N"bQ DDSќx榦VWWO2#%./VBAu+.ͬ^,+@2c6nxwsݽ{nݺuaҥ)#4.5˥RԺ y\:Fȝ] r@J),$IRml C ?b?7:6jkHLrf YQ1Rg@\Fr>JY㌰`@ @s!gvI&p< }z Y`w8T# DǂDsZfh'9 ӅS  AJpQ e0] A8sgբBT`r@@ SZMjq MXE&v?}}}}}}g9Y:wP(JellK/tIV226o^0A+~LR-]?|Wi6l0cƌ1cƜ8qtŊc=Ɲͧg(jls *<+nۗyY___za}}}||ի8~ܹsڷo\WgffZ#Np:8F`ۡ-[{ __{o˖-uֽAnnntӯ_?//۷3[搋Y̝6 Yut吐eee{VA29rs=} &ӦM[zɓ ;%[t=<>>4ǏߴiөS==={챗^ziРAAK,9{^4hЛoI9_|[ou=8/;u;tĉݻ6g+!p:LWQ9r]}Ȇ@rl'<$7 8Fr Ab WǠz6s\ bG߫*R[X.l$Iyl A 𻅸9Vx#9gԚ?$IoF|ҙ0GgAlŘLlR x^W&mݺ5((($$dBAJi&` "mKqWL&[lxxxNNΊ+!!!IIIuuuA:t(..3::z۶mKn޼9iҤv)ǫj }||V^MoRX-[DGG{zz_~1UgN BվT[>{TVHb‚ ;w\2##.^-(/5Sq<旱mDPH#i꒒e˖M>]RݹsgٲeiiiԧϟpJR*Ti֬YK,;~3gX$$$̟?_VTX*ŋ_xɓtXKSNUWW?S)))lŋoݺ믿8VB#Vݓ)>$ɪu:.B<w_O;dn=̾L& j ~BDtt#Gbcc PT})++ԩ… LIc1::ZVGFF?~K.A\~[nTMXK!LX'qéQ5qwzYXXصkW Xa)gIYn Kx%\nֆ޺-66ƍ=== 444k̅^”Gycuuu___kk+Ӯ7n0%WTTb+\!ĘCCCf3O?1IVt/O?dxPbn%q㦁>ϓO>_~ϧ(jW^i_ѡP( O?L{(?ʕ+wk?.ޙ 6md6|I6 ykHl~Də3}0 r`[GIkwaq+V^>k,Jp7E=CAV{Ĕ]vegg3fL:)mƍÁN:u̙l7j4!C7n^ &\<߂Z?%~t]cהƻ=?HvKdyh>VIaE\Eij>{֭ڕ+WΝ;W<M"!>y=r|;vΝS*Lɓ?^z(=p9|L 9ڕo6n-9ߍڵk+??yV(o9p]wk. -[Rz)eXDv>o.p8;8 [׶@bONbgΜX,Ν˼uQFSVVW_9?dwvv^v-++-y'$%%8qzysY,g2dϞ=[eeeIII.\X,Ν:t([H>"kkkrm/ɩNOOHOOfk-((PTFIlkk3 J299YZZzE*T(3gάY^^>~ݻwݝhl+h&I$xeR OrI s}J#/ȝ$rG(-)))No,1d2qV|7nիWO>m3%͛92tÇO0m;q۷isv)\3IKKkiiIIIe{zzMzj&%??_777 6gEEEeeeQQQ^^HEyyys]tHά۷o8q>sݺu~_~yƍ:n!ooN.PY%8_Xx1$p3ܸq#s&&M_ߚ8q⧟~J|G@E;Sg7y=a;yV}/]qFtr̙3#GT(Ep &M[ZZ>~[ aűǏO<ٳo>~ϼuO>b<_رCQRyaÆ?Ce<׿u.\`ߒ-}wر۷oLұc4ĉ>6ۤI֮]d68%,vn{;Ug.iP%Lr<$$yGr uС1crQ]x`0ǫjv ){zz h4DbRc|E:SLi.>ógώ 6 ǏE'4M?fڿĉ+v%\ڵkIIIEEE%}ФRn;w&&& >|ǎ:tпۿEDD 0h~wd?'* &[kںsͽ:믿6Ǐg-jkkƍW__OQsڄq*+۷?eR>v󛛛g5U IDAT%%%Bm۶mʔ)L"1x6?/p=EQg"rc {+zWz!C$$$7o#*R&8/_T||W/pqG<Ǐ.]el>P [4M3oזaLlo5x;8w6G.9 ywC Ax> p7fǯ@.W2I KK׸p810---߿ }LKiG O?A bŊ>&O>2eR#<G;?x`J0DW\Ϻ?._e9ݝhl­{ӦMV՛6m(`0(J&[EEEffB9sfEE,{$YF\X&L&+((PTF[*&aN!qKNOOHOO:"b˔dѫVJOOEF_vuuEGGY%ኣ(T$L&&55UR絈;w}7vXرc[Ξ=;k,^|^l0rrrD`'[a:7]c03;&؄5Iκu/ƍuuuܷN:u骪#GPVyyys]t)H'"iMOOOSSӴiV^ͫfN![Ғ:"b[+sÆ %%%YYYx"Ϝ9s>#Ç;6::KFEEEyyyV<1֞;mڴ.a\Nj5ZV߾}[ni4&蔕]VT.[O>Vv<'q \% `v5a:EQUUUܔj[ZZZ-/GGR }q"a F#MFQ׋NioH2u:SfMMNfueR I:y=(oݺe0onEDiX,rf_8bK_ڹ$/%r.D&y֦VijMMMir<$$57ܴϺɒ}G8l>F&œ1ǡCƌ#˅u) 鞞[0ekw{X&d[81Q[n߾=44wQT⡊GW祯b^ ;.>["|yǍG}ݷ~4Mřfk8hJg1q:0T|hr5;&&FnN .,Y/涶6b @QTccV74 Sf}}={tĞwLo<@~~~ZZZTTͥșxHNvr3,X?OӜ9s(ZreNNٳg{zz^4 k֬1L/j/]+(Oa?E]edS'q果+VFsω$9:;;,ƍyy! HB[lܺukff9Dyfm6{2̙S[[WWW`EDAJamב3#33s֭Ge;Y"Dm#F1b^ϧ(*##T*Ճ>ꫯRUXX>c n!999ӧOyNj1;niX/_9pn&iѢEj:!!aLΝ;ZFٶmoBJ&')UV)6bsJ'aaaiii7o޴bˤ\[ntFvqk[oQ<҈TD\q֑A*%%EP0Cd;z?sԩSEh>[RT*˗=YZr|V=8#ŧ~M%{7W̙3yI9 v &< 3[&~E&@ 5!Lr|MrIc*9@r=9Vp U+[ؔ`@,`_xx[u)=39~ís|xĉ.X.o=Ҥw2B.k[U?7jkkK&mڴijzӦME R\YYdT(3gά`kjjRSSU*UAAZ+RN<ȤkVfAAJJMM5܊gh9W'[pN I̷2#""ӫyVQQɓM&ډqvwwgggGEEi4-[tHyyw-HDl?G{čimcLb'KoL&nƚœiii*jڵ8zũSN>]UUuz}ss`addd嵶Ν;wҥLbnniӚī`=PEEEeeeQQQ^^BݚiӦ^W͜vU3\ƃm]w5f&qٲeOpdaX&w$x8x 6Xщvb׮]I )!!ʕ+IL$"޶%;2m '&tyĜz0ɑN$yr\. ~mC3F.s iDZI5vk9WN}_h@rd#|mєW&w$= (JrKXI"){zzx9!]x`0ǫ;v$[doy'-L˴1H\(RZDm@Eڥgrx)qqqf]VVfXEjkki{f}}}Īڭ^[[%(bN|g#x0:Im=<$&&d21#>)eri45x/ɓϟ=22-fLFVko111+!%''߿͛eee"D9vbK,yf33n L6d2YooxN"'t: `0Yd2UVV.^I쌊X,7ndsfffnݺcMVt[e˖έ[fffڛӮ 0f3c/vŦ=qa[(##9Bo޼y֬Y♇o߾zkkt"q$A3++kŊFd2=s"!͛7]]]mmm B$"'N 8c6{^X]EQgΜ#RZٳgtttl۶% bccg̘$+=А!C؜ş~F RrNN1ڭiZ|LsU s0pP19SXXR(Ύ~ya2-ZVvm԰7oZMDjJIIQ(7ZJPpo.Eb,_<22r%%%"!G 7nE-v'sNVhA97d˴1Xw7S+"4SLQ*֭x1~ x:V` ܺukĈd>#ނ~ф1m=W֏۷oӧOv,<9c…Xұ_6).=Y.'N>|[oeo. t{}嗽 T|a D̆!wA]S/>ٳK.-..6L}KI|_3L׿ X xrCgj N8ߓ2^h4x饗"##ccc?d*j&#Gy቉;w$㏿P(fϞi. L&kjjjEPMQTbbbyyQ(0aBmmWZ5wxa lNLL?us?0b&b-ab9CCC- 2 8[OEw\\\9tR(LWL4wik8y&׿y󲲲ċm/E<"}>39p<"33SP̜9IIMMUTlNb"Q[[`P*ɕ"uwwgggGEEi4-[%t:^O*K&mڴijzӦMv.E2,::zժU?H3ǏܶKL$T[[R֮]bYSxpuuuzzzDDDzzzuuS!=$98Z.>O>_~ϧ(jW^i9 "LP( ⧟~bnz(j+WlllllldoVxYzիwuu]~}ɒ%" 0hР@l7]_AܘN>uԪ*fmCigt ڼy3o3%n7 b7n`s둑QQQ ѣƍ 2dțoioN]H X1xbHIzܳK!))㝝̙3ą Zl6\Hlٲ'xl6Λ7OٳgWUUoooߵkט1c(jΜ9Lk"vA0"fFEEَw}7C qBDR6J^dq^"0#2>-.~ܵ'ڥ#!Io; 98yx?H<:th̘1wĐ^Dr\. ~ZErG9f:\Q~ +x`W;vI"}-Q8qu8^< :gyh4>HNl Vb;CFI<,K$fD]qN]1‡>%]VVfXҢjkk_ &řf)v_ 3H]zbErF#H Iп<#^1ھ>Hp'B}9o~[[t~-\d2yF"h45HN~SLksq Hsv߼y,??8o0xO$=$mlQQQeƍlbff֭[;;; & 5k֘LŋTb h2{9Hz[$y]|MP$C={vIIIGGsHE"ZK322lٹyYfٻ0o<ء7شbŊɴgn{9'&&r#+=N'N ^Cw$W^y衇 &&44T))iܸq  rI.E+7o#v8۪כ*LVG.cpN>| ˖5e|`_^{u 1o1~0۪/4ck!<5~G$QwoCgGҸ޻ػ<&9|g g;GC tucawq':\'9XM/>)p/~ٻzkŶj/zw:r<1 IDAT Vx .W32p쨨(Fe&:==="""==IdѫVJOOE+**233 ̙3+**؜:NSx%6.f0JerrreeH2l# k5V!"IIcfݺu~_~yƍ:&1'''--%%%%77ͼaÆ{O$1###//uܹK.esVTTTVV1)ӦMkjj=s8eZkDkز o߾}ĉ>L$N_5?BP.`Ю@ v3fHNIIIǏ|WgΜ)sٲeO 7@3&&閖V+%~aDRtr$!2E:0.$r`ѻ\K5~===Đ^{zzحhQ:4f\έX&1􊈉/^4 jzǎ6)~u%\>srt R#8g73ۄIJ+]ā1\.!!!7Z#R&8I n,C@!5Sh%Err"?###kkki4GGGY,6-<**H'F__x29Ed2o-rF#'H wp0΁<"廡b.Yk6lVdɒ_|l63cbbL&3sL!L+'99y7o,++H444PU__hD#2Rdd$[(!D^t5)XoqfTYYxb$HE:HʸPzN|듒ƍ0h &ԩS 8{M-W^y衇  O?h4RJə>}:LX-*,,3g-/{Gj }]8x|v ̧b:_Dk"/~8pHd+dDr2 U*xΝ;wjZFm6M&ӢEjuBBݻE)h޽=zthhq?.'QeeejjjXXXZZ͛7oIb,_<22r%%%"eu$L$qWUU( 挍!Hk/p./j[uհ|96p cY(C q`b [dÂ@?m՗~ၫF`1ԇ.WG;y;q%0 Z kcv7<ߌfmk 'Ye9# /)Ùϡ"|w߇I8/9z-0H0\9)--߿ 0m5[pV "w?ϡpJ (AwqÇ'Nhw>yE/xe:_SЅ"'ފ*ϕ3vfuRHHH__ z}n &9¢0qmT~߶>/ʯBH?k.\hii:u h51W0S:==="""==yӦMV՛6mSTT4yddWQQQf˖-" R\YYdT(3gάඈǏOLLܽ{^IQTiiNH:k-*((PTFQ$XL&YSSR xPfJOXhԨQ_5oWWWttt]].BK(ZvmAAO>ߛ?X&{ގ˟صx~_s${OXNNNZZZKKKJJJnn.7SN>]UUu67|=ڿj_nݷ~_޸qN|^l0rrr<yyys]tHgee޾}ĉ}DQTEEEeeeQQQ^^HE"R[DQTOOOSSӴiV^-hvaӦMkjj6U$lKOXhΜ9}ÇǎG Wگ9~-ܱc8p̙|PфM<>(,,vhjF#M555:oܸ@׮]#ë(&&閖VѡT*EZD\6|gϞ+""b?p}}=nmmR-[.e)_İÇرG}4q!CܹӱvؑTZZ]76l3U_zu gm*Ak%{TVs۵󲅄4n K(JJ G.،)CBBGiСCcƌ–%.~EVwa3Z*Ċ#( &k']6Nl&"bWPwwVݾ}{hh;CR(S@"e=t: WϜ9cXϝ;}7..訪ZhO?-e} >… … IIIlGh4eee_}ȑ#?C{{wϝ;gXΞ=;dȐ={0o%%%1U;wnСT=z^{`0V6")vnlc^94Lp199ܹs?،i6yeeeM8_U^^hlF+8(&b(bVGmmmTTH"vbQQQ*<בd+ٟW1… cbbz衸 2L8#&(  )+144[W677jbζX)Ks=ǎc-//gկ~UVVƾ矏?&MtaC%''3LR^^ξao wy- \^ޕ^;Fw~g-rᄏULիW?555mmmk֬Y駟~gnܸc1<}t[[s7Vxo+"E$ʉIGlEQXz>*H磏>zꎎa%#'ɐҟW1޽{):tEQsС'OHC, 6`$ihh)([`]]ݒ%KCBBBl.EӴB~ξR_b˔|BV*W?o͛7 ?k'&9aaaiiilwqS>ì,6̙3SNYbY|yddKJJD*2L-R wfwܩj5Ͷmx2+@dѡƍ;~hyeĊ#UV)v.NL$N*%%EPs+$f2ULt-N(L޽{gL$ sYx e=XK1c˿/\( $*H/_~GZK/DǮvLr\nƌ'Nv}v3_%svȑ' /,,Eaaaq1{;qSJZʤI9¦>|XjҫU*M2wfI=p&9B q΀? &Mtuu}?0\bڵMMMf'91b5bǏO<ٳo>~oiRyaÆ?Ce<׿yUw}ǎ}d*--;v,M'NN]DGYzb| t€><{hw 2vڽޫP("""&LoK4cǎ!C0qξu3fjR9}?ZDRoߞ*|Ν;=ZbՇ7"++ڵk===_~?doGBI3Llϟ.qWTT-Ydʕގ(yh"ys O[ccky-|-gJƩgш?6 PQMr4͖-[ĊLB1s̊ &ݷ;9qqJKKu:^?x𠽵~Mlkk3 J299)|yyw-^{ttUӟyډe\[[R֮])""kkSK555*@@kK_+ S_%6Ynݷ~_޸qIkmm;wҥKDv),**˳v"5lz`0N >++'NDj(jÆ %%%YYYH2N^zujjjSSSWWx3OKӦMND]^P!ȿ5kѡT*)rSLӴb 0/1&&閖Vkv W\X,"A2 3Dj')^zhih4JH"b/t:kHW. $~ Zb;\.%:th̘1r\ _ťOr3d2Yl6K<\шIk/Hd^_SSCLq+"RTTTmm-m nW|4@0 :^TvY{rVXa4M&s=$vvvFEEY,7kK./.≉gΜ_`0Yd2UVV.^fu͛w宮6Baoډe<{쒒m۶9PܺukggC .}uH; {*h<x rDY,˗GFF8IܹsVh4PPR)gr3ۿsAɴh"Z{nG:nܸǏIk'IL$vh2eR\n{ŗ􊈈{*%%EP狯8bW. $'Hʸ \]`X~{A֭[#Fhllv f5| }-R|P@nBɱU'o~7OX܈i>( 7 d5 tÇ߿۱Muҗ4w+&X)#8[pO1[;0_Lr Lu\ +I.a$_v</Zk/\^Lu\$Y)䲿|z, Y>7 n0nKr5'?ϡ0p$ݩx{WiiNdR RLNNd^jUzz?ooEZ+**233 ̙3+**>}d2vkTTMߴijMuHmmmZZJZv-7ւwfQN}ȑ#:dթMMM]]]by'6Hz'KZ$6:ޅ9$ K(pc\.EQ4M[,\$TWW4Ңjٜ߰0*t:HtMMNwqqZ+M$Rio*bi4z=7!z]pqbN#k͔'&'s|El5l0-:@P=9xwx1o:Tp&L['v a[0Lu@$`簄׭y@GT 'v ``p\$Lu0Ձ\` :<$aX/>I/`X/$p/$S")ۏa`:BxD(/$LuXlWs'8aHF@y&9vکCzL'4M,Tct LP5oЙ39 :>+7vp==ae,dNLܩ*q/45`,i/wCl_,ܓL&{4Mll^zǏ3z{{zJ?d(ȑ#wygxxxbbΨY} IDAT;)?+ٳgSѱxbZ/b-njmw;i0^xa…uuu/Bvv6naa_|NTQc_d2:uĔ9s<3uuuFb |[[[+**._|6b-vq1p7Oe q`Daaa d2Y__qƳg8p֭s={6//_W{{;S`___hhbEk\46d0L VfΓ6E 7\ˮϝ;',#G,]h4 S_yo&i4Mŝ>}Zx&Z->p][sn"]'=?`HpOO>ׯ_|)Z`իWi!ttt( BO?=Li_reccccc#{답ZL]N _rmZ\X8%x_dJկ_{ʕ+G:uZׯ`hhh`8p஻HLLܵk5#FOJJz뭷9wޝ>bĈ_V]]ݲeT*UttSO=e6ْkAJi> Q-^uoooaa!C}4wQF;ѣĔoÆ ޶mS`{{EJ^ 3vOXNX*.}}w>}b466>Zv„ NdR ѣ󫯾9r~h:aIII.\X,ϟ6lXYYVYYֹs]0..訪ZhO?m:q" пAGY|СCH>s\8ɹr oss3EQ_57_~_W섄?|6X^^{ȑ)S0L{ZbccU'1N^ؤ_/'yƀg}vڵeҥFڼyAE-q|;]v* &[n\\{S8ym ;#ˤ۟~$pg8vC$ az WsP`DjA sT@@$ &9  Lr `( -U>û  g<=c~i$$E@iILr 4@$ &9p z;4x %q&N_LDbf82Lg ߐr>FV nLr?H`~C<:V ٜ`~F|Iy&9s0r qI1<'{He7@Yy9.W#;A S]!Ix}׵O ]8>qJ_Yjۻئ<ױCYdHT+hi&Dl"hjv)+E%X4|U KKK=,ˣVU7@^,^Dyf)CCCMLLkU:Nds922"r 0HAmPYY͛7C Sg[S|̇Y^^wk׮9B< d^0d97T39!ʟ꟒$L&Y0;;`;333999333yyyӒ$Ʃ)d2)N:_̄%e!gTS8sQJDB&IOԅ g0?쳎K.}+=̛jHXR733\j,cMMM)OMM=cݓ$VU))..V ޽/ۏ=o\}?$!ZbؑƈOziii<%8uE)47ʣG ;vUg_X H3\A6s:;w|n{ӦMKVWW;|N]$o={BZZ/_7&V\yСnuy"\v1/jWollܺu|vR ^~Ri#0^:999>>n6/U68H}i}KUV=ONoRz>oƍ}}}]  /$I۷o7UUU'f.RxAjXm{{{AAǏP=qXq(|6mjEEEڳlR\\xNvWWW (f>B@໡n߾ I1p2ҥK7lD[$@̤szfJ80SSS۶m'XkJJJN8;Jۭ]]3|HoQ-鶥%Kl߾v!y<ڼZnJw8eYV/)HYk充]]]_f͢E***ꂂ;vW+lS^~]ׯX"Bns\l6չ\.6 C g^ɓ' C4NwDږ wa8m $544={ܹsJWGssu놇'''5M#r8%Iw8JelllÆ 7on3|3O?SX]B8u!{VQ(@ˁ%R3B`NmYY͛7×e9FȲ<::jZwu,$Ç\l6eyhhf޲ ×v,6888gk{vZ .puCr!)lS;@eFh eYXR8u™`2fffjW )|{Gm)pGV tkm#8REzhlFqzz:|ߍFhɑyFYbinnޜ7 )''G9%#vK.9βw}78uH8DBcccEEE###EEEccc/^#Hu@0D츢 Ҟ^illܺu|vXtܹM6iW_96zy/ www_^)\rC&&&cջ#^:999>>n6ño>߯WTT\xQM :*?ym!! ~n{fkkW*cےz6?G?QANXJKz (T -bNc[l)..^xqgggpyu]Ϸq>=666^{{{AAAp/Do` Crչ555Jv{qqB"w !; _,#GZd2^̙3JW`UUlnmm .1Ç+kiD/^\v~駒$}#HCBT>BNs&9TlO}Y$OS$@\n jisCd/1gi 7)RIxIً9 u"HA R|/̉@"yZq7bۙG^zI][TT~YH`:H^z7|3Fq!]j%vI?\Gyk0%Vm.hZ/XtipҥNzꩧFT:t;44OZ1醿KRR0|/p$I}v2Էt 6|D ӨOB B9q\l6չ\.`0l'N(CCC;vnSXz<6ǝNg~~~eehjjj۶mvb{g|ڃ=ydCCz n 3PR~ju,YdwV aB+ɓq"g%sxQoii۰a͛.vwtt(%֭MMM555UUUm8Φ&^nܸqʕ[nݹsGwaK_OG?駟|DnH7w㌴:E:z?P)ѿ:Xqy,dgȖjcLx"lGFFdYFQ)l^W塡!n4,--x<,ZVn޼wawuFA&iff&%qFZ>T~W^ޅj`#< 6\RHafu3,Vzԩ/rff&Fqjj*''gffd2ilfsV˛ԦdR"5 :2LJ0g6#UX,ooSTRR?8R qouJѬa?5VOs=K!\F1H̕"aCػ`0(ɘBc]|'G$93Dyv=ٳG{Iño>===~{m:Ν;w|>۽i&6nz}>߮]4?_W_3$?A3Ҋ(..00d039!ogjRUcׯ_裏Hjhho$;$C Az~mgTI/˶]@*$Atӎ[9m>C  -"˲rҜwT 2HG$9MV}͋ B$8ʤL:ˤ9dnW[ܽs1(7][ԐHEEE3!XB!S%Sǵ@$9F}qVd'11 YUUcFa$IXbŢEVX{ ;h/„$6Qe6Kmmm.\йц&lMs )qL*|4|XҦq`ۿ{277WX__?|O?'?iooG S oT}Nsk999:Oޅ-뼭7Ⱦ,eY -D0- u@h$ѣ>4s<؄>T$Ç\Ȳ<::jZBzeYlᭅ3H&&&{,,Ȉ,ˁ@h4*± ݼy3P8KJ_ {///߻wkґ-))]8L3K%%%޵g^, 7Eaaz vL}>A嚅 䄼^3+>}7޸tvSR}P$,y=7S=x[$I&I lvv`0(Ʃք-3ĩS˙Qn24 O0x؅L&r>.Rxב g.]yW^yE;33ґu1uzfI%(,n! 3x&@Pyt[n}W?LMMxW_%=Dߖ_ZZfffmbܻwOwZ,h4Ib/o?zh||voڴI)ׯW W\yСn=~n{ :} 5؅nz}>߮]B,'콡իf9Ѿ}GG;u!~vvvNLL߿_{x̒p Z0|yęm4rG*&moo/(("d?eY|7n,,,,//S nwuuunnnMMRxq^\\|Bzeŋk׮3ZVŢ@+UUUfYQՄ_P زeKqqŋ;;;B,Zx#GZd2^̙3۷o7UUU{ ):-$w緵)-|P&gr:Ys>a<={#vɺݻw?$ yW\yd(L~sbD:{,Nhnn~{ァJS9p>oLDf};YbѣG /%.e۽16^6Qj2IB |-tc hS[Rjv{H8zZ1GmgW}r-cxFY:!wa~ff?5G Nz-[/^٩JVbQNv52ͭzt=S'l3I>|pQQ:#ґ#GVZe2V^}8g au 'v!w緵T7I^xqڵs]O?TO?Tx!q #m:GSz89 g3ݾ+W޿_n?|rx#@8ihhnn~{s Ϟ=K`di@,gپXѣɎ%q̍{oH* Iw'RݚUr[$9bFxq&RCn$@ Dd' $In7`9@2-sp@ "9é4p 93 Ep%@l)' F ³H$9@c9zj /ZT yH0  'g8bG[4R -(< ($92 IB ($92 IB ($92 IB ($92 IB ($92 IbJv@V0 H ,';@J qH  qxYNp  H4$8k89!$y HrF+@$98!$ y HrD9@b$|92%;@!$I Ak"!(\,9 jbՀe0/_^XXz<ـ$H]ϟpӧ#655ԌVUU577Y@3p Jj!"=c0-[pHnzjii]fͽ{"-,gGSЏ Fqjj*''gff&//ozzZY bՀfX7w޵X, HrV__ׯ_?<270grܮ600K/]|ȑ#eee.Rb 8u`O($92 IB ($9@2|c¬[TT3\< ai ON wIߴ6d,]ԩSO=ԜK&lQ=e! W 9Oq^K"''gvvVϒ$9@HnWR`l%%%'NXr9\WWrb{5k-ZTQQ I9U&]ϯt-0NdY3$a?'In߾m&''# 럥m۶vӣ=!:70a.͡ꂂ;vh@l)r[ZZ6lذytgϞ;wN)eYy=KޅK8Φ&C.ϟ={VG -.nܸqʕ[nݹsGcB$wሄm677[nxxX#3 d7$I###,Ѩ|65 Cݻڵk@@g9{/--x<,Z9;CB.__eN:5g` {HuTVVvH'D&]8"a6ʲ<44g &_$>`?FoXB{Ȓ˖-{OBeoog?x8#-bٲe_}UHpBo`ޅ#io߾-s%X<7o={B^x W^7ꗬV˗ܹssݛ6m#=ߩ"RaH%I{_|9 Ck,q֭^ڵK)?!B5.طo~, pt~AjXÇ8rȪUL&իϜ9~ X]%}>ƍ ;xڵk5"ԨRȟ˲Gk\ëk,"lٲxŝ.*ڪ?1#*uuuw~_:t裏>:~xp)0%;|^s~dtl6 ?OЅ+90 p%@F!QHrka™-**bi `h zNoҥNzꩧb,Ğ  8uVrض =S1j@RkJJJN8Qr9\WWr>>>t:+++n:˗vuuE*x}:RaSSSMMhUUUssC466?xٳΝ )\RcD!3/lS(g^XƍW\u֝;w4 5.\ĝ B^$iddD@ `4t111mR#jU088dHfz, l6{ڵ@ ޗz%#(aGQqn޼P=?! a' B,QɩS˙`L&`>"^h4rrrfff򦧧tgutt\t믿~w^yH} )\2҈"9pi=&IB /45VbՀ/ۏ=GR733mG[bܻwOwZ,hWVV=zt``رc!t^r#/g^X4p:B$IRѠ ~3NsΝ>voڴ)===~{VohhzlVdZ/_Sc!K.pD 766nݺ|]vi\СCib(54Rgڅ)zj'4H0])2u %wox}Q5~㩮RL.JVA `RH{ϟn+.>zfEDŪț4UTRlئnumTqަۏmי{E扻PbM-_2 ~MZ%8LN>q4TP(p7Bofe4]n{>%.EeYfbIߩZ<{Ϳը{_t._P'# ?gCPh3M戲-Jl:L.YB%Ck(٤I$L(~6IwA~G{UD๞ڽ+j.bQ4>gG>ᬛEti9.O%nϮ;vI=\tlƑ05-A}ɨެ f:U6iN8 #"b8 /֯%ήk pY7Qo060Bߨ#0LIa&7;L1\P/"{ʽQ_عw0PA4m)Wc۾̲z$t gܕ64 O ҏұX%wXW[Kk 4ITK לq0SkV%r8Owbkj,?-,5=-m4iwsx aZQh):z*njqn9\[xޅNUN>G@53ʹ~A &Ǩ; H zŸA4KG*ϝP-lz‰P Hꥷ( ȟ$EЂK; uhM.?68(eq 4'@]Nus'R]$rUgn\=>=I4/˖0΀S]$ϸe gfZL:C*2lC )pSI ڵ_J?+b=0wuZ]XQj+,-W&\*Cys$݁ɵs] YvCd" :8 سy=rZ.]ɸqؿr~=+HNZjjyaws旑k, G%Hx _8ŝ~-xGӵNX="(/d=[1@S LjNl٫}^,,7_mA(ou%LoZ;@CM 'kKA$ Q.r PpM둽/[n7a99Rpfd?C@Ʊp8yro@Lo7ӂD-h"?qսw "Π.qq!q8vrMxp%_nڊP(>r th LԺ[ ϱ@Rkt%󙔡ѯIέ(Թc6͏*Ml(aÈH  t¼XMyk4 g|Qث6%Ue!%Ac5$i*83r1O-0<+sc@fv!h u[;\5"QTgԺQ[,=0bD(:aMlAi)!Vq;F;\REk Epּ|b\}4QlZ I𿰰ja<:)VffWJRW{KmZڵ{K癜-~hYlBïz ('T-$P%@Ө=DbJ=}#jQ;W WT메9qz֗t5r;Ew~3OsIԿRr vcixi Y9j^Ѱ4 O5I~ra6q\NJ&깆?r3&|v*7_+cy'أ8f}٤Ayj,= ~9%h5\ic!NNXoޤDzgF;q#v"nyC3 l̪@Z{POsF-14,-9_΅) i%őS}I@u2jFu#Y':4B\nGA$;Zy^Ts)Κi2~.Gba)9Y_O <<ӭ޲#{Pβzvb=X8둤̍%iy%tHS$4KKzΗȳ =1L)R!ļ\=G- A2XPqo=F|)O69O2mC Ę+^ kJYQpt6jL6dmBӘQw"Dzaڊ d^$Z>/[J-i lHTÝ1՚Cl3Dк:#Ւoǯq)?ƧWgg&UNKX0#I"@g =sZCN#!ȣ "*_K>1?yGYYYooo__ȈZ޿?\ldttK\q HDAQSSS UԲeX3?jΝ;; aj8))~ݻNVSr}ȑm۶QoW_}E:\/ΪȂAx_qE4B@9/jZj7פ8KsssǏwsBr<"".jxLLjƻ]/bc>)D`E Q;~m۶w}wxxp<|:r ffgNHHW繹y$WW 6o\]]]]]eNdz^nfWoAdzzZRTARdl6pqq1=[oo>w^~駹eرcUUUY6sU$4Ao 缿 G|MJJ\.ꩧKjd___VVBHJJwءV>3??_VVjժj~_uqH0 p#9׋,gծ׏+8LBijQn1ų!CqlU_`JQ_q,}}Fr 0BdП@A8?\ >/$IWtC [>>W^ ojjڲe˲e"##-7JE |@`xyj^_ZB)--=p@qq?he2YNN΂ s A>, Boy,no,vjinnTԘ .|~k}GxW|jwR:h%%%ٳg}YJx7xcttgddd׮]&>>~~d2LNNV*iii̝RWWe0?UTTT*cǎ1|ÇTHó}Xs rB  c_Y!P(?~S[js>z(11QR=\ss3nkkkI=== mlnhh']ti5kITR93}XSά0hnWY`ott vddPrK$Rl6qjjhXkD)aT*jXV{_{;TWW/+(df^;s} AxxUuf61DGGLNN$9;;n8 N_h<#?199d2:ts6q3B v=)O'dLfoRVVFrP1fzR$I>s~-֭kmmo*fscc#=A9޽hHd~(qك@XA- u\__ٞ|w2\ZZ:66ff_MMMZZZOOUWZZ=bdggwttLNNZ,\Wj̩SRRRfgg{ϟnГgoM6= ">>>//b300RL&ɓ'fIԿ=IMM=rR'nܸQժ 6׻Z51G5 h4VVV2g2 T̥V}ҥ^z)***&&fw!Iz^ۏGo癅\_jqns⼜H}t!|~߾}; z^Gp~~zQQ߇B᝶P!B/KO v$ 缒%!Cz'aH=!$ %dO>dp b{Ba,Iz>5 74RO _aS:p7C p#dv%O\ܖ !9XBw@}  rsKHgd 9>o E>ϡ ]xC>IC8:7ی:+?pdNz8؂;}%~G8$x&@I '|O*TQ>8G>G>G@A‘kjjڲe˲e"##- 'l.O?fd999n,(CAK:;;j55&;;… /}.--VRRB ={gUT/~7FGGy겲 ǙeggT$$۷ӏ=*LSoEEEjjRLMM=vs'NFRVQQjֲx⨨(|9' =Ȯ]4M||ݻm6fϐ.]jZ]M%"##ڵkvjܹs֭Ԥ˗/_xT__|ovfff``ߦK\b4[ZZ<ԩSnwtt$%%9sT[[k4v{gggrr2]#OkYyÇ?|nwww1[Τ뫫o߾gj҄nQ(?Y֭[kZjx jxڵ9K#I/Kff?Ovl6744Г.]zjjx͚5MMMFzyZˢR8[ѣGf^;FdFGGyw81ԀFau0#""Ijzjj =rnWT*FRTtIBZ{Ž;տ vHaa^g^;s}}sokk]K111l~~munݺuUsn~獍 &СCDGGLNN$9;;p8|?333%JYYՅǬ[̵k;X,K,_*zfscc#=A9/Okyܽ{Wѐ$)Xicss3].d+\"5`X;::fff&''-Knn.5nŊW\:z{{yj̩SRRRfgg{ϟOMM' i-+<99911QYYrJ$/_L4\ZZ:66f麜g^;s}}z444lڴ'P(yyy $[[[7nܨjj }oJzyفx!WsQѨP(Fcee%s*Z^^.L.]z饗bbb6o|$O>ڟRL&ɓ'I3 > #d#d#d#d#d? >G>G>GKQ'-x K!{ {J}N7CIWḍ' >ɜ$$h!|#_l7IC-.d':d.dW(d 9>@@Z} T7| H}_C  _1C@pw BȾ?QA:}@C  }A d(  :DA >qA 0}b 'ROdء>i@O2XϾ`ljU$-Obb[~{}N$ 0$Hvgݐ} .7}! >k"B $jkc;{)( ~c*Vf,ȾPJ@!d_c!@Cb!B8Op ]lL,dCg: CѵHv6F3>>N}6kWmmm&6LIk?T* Z[[I> `DYC^㟙9bdggwttLNNZ,\nZXX8:::;;NEY^Dž IϟgNb0ͥccc6.11|AW!u p!8(%HlmmݸqVU6lwndd$??ѢEJ233̙3=j4 ErrǙVUU rzJe2Nbj Ca}$kRkkk{{믿^TTDīw^:22O۷>`tttpps>QVV722V6Wp6b2t:ݒ+Wfdd,thm͛7a|M wʼnpȚ1jxjjJPJZl^?00@ ߹sy;1<77G ?~3aB!ryDD]2O;]5<񘘘Q3>>Nw^ ibbb>|H ?x@yS!|(3yd۶m᠎O  =sBB 5L?Q]$]U$Nۼysuuuuu-[t:zZHi2\r?jwʕ)>U*J/L>f 3[:Y{/觟~ ::_hA ;VUUUXXȿ^LUj3'Wxܱc_<[S£so&%%E.?S_~%5/++KP$%%};vPqqq|vf/++KJJ\jUMM 5?պ8$Ip |srV^~￯t:=!So8 mfe39^ݪa>^|>tq(y [/L}B-Tpl>+}>\@8B@8B@8Օx<@IENDB`./oar-2.5.2/docs/schemas/almighty_automaton_finaud_part.dia0000644000175000017500000000241011757171206022071 0ustar plbplbZnF}W"(r(u@X+jkj),|O?ŖxDRTl@᜝936xZf! TV"K.Po*Ɩ?^w z'~<֒񧩼|n<~||6D<`'}4FcJx-[E328(ZpaCG@mLb;*x6D'oÀfZ{&?+.a^yx<+@ ݢOE(V]G:ۚj?$E0XOHSIɆ@1)K{,8?@\n |HT ^8Mg)}GKU18t&h@AUuR2Pt]*x{{EGF0./oar-2.5.2/docs/schemas/almighty_automaton_changenode_part.png0000644000175000017500000002710111757171206022751 0ustar plbplbPNG  IHDR/W (!sBITO IDATxyxeIIӖ=Hr`A@`W"Q~ExUú+"W]Z h (,GhEZhWǫc2LdLkf3F MӔ*9&% 0lnHPJ$xS0%d%fF߁__߅߁__?CV@\w!+o .l#! !+3p$)x9hrs+]~:1[ü䎊l4 58~ rp^yG}TPO|2Yxڵ#G̖ 91S<ކ bbbzߒ%o?~R6lؕ+WkeJ$UVGRZ,ٳ端:jԨ%K[QGGGnnnlllTTԇ~H֎5*$$dԨQLՖq.rqS{Gyd…n7ns=G+<7ۙ-9riL˗\/\,YhQ=n޼9~ ,Ā8p,yW\9u_Vo;wԩSK]]Y`{キi᯼ 1gE 9w5tNkq&ٵ74M RI^4M_v BJ޼y鶶6TJի馦ݻ,SHEʤ(샭rzG)&&"ٻsqM!%ݳ#N޽{UUU]]]6R#((K&,avy-H&I"L&2TdLۚd v+HRG)$$ZE 9w9;8).ٛ<%8x j}]^mfattt}}=EQ:!?A^ fw=U[(f-swprrw8NMs0+Fclll[[7fᣏ>h42,-ѽ{?ی?7hnnr4u_yd#<Ƽ~29w''wp=s wG1 >\P,Z!e˖RدLR7mdLkcVe?>Ʊ޶֭ʕ++W9R.{W^婈s!:'wg;%{q ޯ>w;>Y `6d%mJ>+_ΦwgH+ ,kTYNr0+#e Ff{IMM h46mi'R|?Yk䯝mԩ˖-kiiٷoߏ?N!<ȳ~o:s /_O? 8y.a\#cǎGmll^|E=8xF2D"!Acذa_իW ɏXk'R|?Y=h4fĉOnoo}Bi'G=>g#g\)+h/;dwuWii)O;-Ghܯ/Wa ^؆ Y `6d%mJې!+lcy%m00 Y `g;$?}@tl_ ~ (.f~z#.p%&_Dvf_ˈ8PނpJqPJWkP?d% 11JDlW` `9 !d\>V~70%%%?R2eNsUrp^*-yyy ,?{lnݲ\0;c='''&&&22rժUdڵkj\.x YlٲǏ8qNT.\в1-^ڵkUUUW\Yd ҥKXŴiz9rc6qvl!8qd޲Rkp EQiiiݻ7***55gA'}#Gty68O%$$?l7VLfX-/\@^WWWjsׯӲUUUUϟ'UTWW3 VzWrO7BzQ[[˿=ekrgV'MD^O8q͚5YZ({˓'O&%%r^Y)J;::eޖ5JRQ&sTI^wtt2L*JҠ ZF±*=:v䢢"Jmv^pn/0+mnC466FDDtF},Z]rj}p(6ۅ[J˷MMM&֭[カ8yebbbMMVƞBWVUU 0Lvիe6io/87 V򺡡i:++kʕ+Vxgjkt~^YPPw1rGH. ++O>wܚk BP(.]ľ_)33s mٳgϚ5… Oɓ'n:.;;|0+++77L͛'3gΐytgg'EQmVZkkkgϞ-pLhڍ7kZqVmofꫥCuNwG[|7RO>};lmm2eRѣǻ+iZly;hРVzVjz=Maڴi 9*o>`L6py睰0.n۳ v!ZuVԃt3g7%#5o޼7o999n Me/,,d;v,55NOO߽{7j׮]Æ cJ;s ͛*%h|'ׯ_O.9JJ ̨T*^^EQRyR$z}tt` L&v˭oo#)׏hV8OcܚJsf!+d#tǏs8p?ȼ.O+ +9W_+zi[o`u޽O<Y=gΜ9s0_Wr<=n&tl*JT>C;wd>vڔLf!]H/_ ^#pCԬ w_B@@~kg 6$d%L󣦂 +);a%߰q@V"P  +*#Y |tMK"CV6deB>YXE$ΈĮw˿%+ y~#_kBDz 3mq&dX$5P/ᐕb)"P YLdC Yt +&^iHd/D +}gX>YC> Y>Y}1߇&L';YiHI%¼C~ YvHI@VR@4p]by["y!(YbJQ5 %D J@Pt  +J!(AJt +토@J (J A Ȑ (6% +m@P䇠BŸi &8pd%!7Yi8|/+qoNg;8_>p A).Kww] rQWb`6d%mAni؂y%mJۄf={^zM0 /< \t믿>{쪪*^sND2fw7NL\4+**:|R$KW_{nn=\zҥKd,Zضmw߭P(uO3սˡ={3g^'% ϧ(ڰaCRRRppp~֯_Ϟg 2$$$D|֚_ܷoߍ7?iӦj*U={6##[n!!!_|iwcmcмbbbt:E|y66333,,l߾}---4ML&g2겷a^u^^^qqqRRݻ}]Ӵxl2k,Yw1x>E=s~+++o*Ӌwr .j/4-H _d%=vGGGd={?捤1c(>L`V4n:Z-3FRJC=sNkYvڔLafTn:Vk׮|0$$$""'8SN""s x (@81 EQ4Mo^ɼЈ >&@4DD~4+@1 ~Gܡ J{XG0.qy`':dQ~Yi<+)NA^9Б"d~}ˋGy' 35jgsέ]f͝w)J/_\ZZsQ5'58+yX!?$PTuuuaaavUQR} >aWKxfd.**:tBP՟~)|۶mw}B֭O?]__o!Ch4(ڴiSJJJppp~֯_xkݱVD"ٶm¤R;s;X}x;f!v튎~x-[3s΄h4VWWO:)655mmm:n̙ƍc>URRUXXh4+++c0%%IIIL,ɹpvAƿ=JCKKˎ;.]:iҤ|#G~}?s ͛*y{2o;J^1bϞ=̪bά)G,{}pg.:ϟ_]]]RRjD"1LƳo)T*^>((7n(J\דnJ2K.H:;;\}K,fp^LLULfY62 M;<ۡ(0+MrssɼQWWMQ{~qUiii`߿yȼ={M庸a 9 E :`08p ==ޣi(11q޽mmmfv, a^:СCF4##/[QQ~qi4~%Ozdo0 zGB΅3gjL>rگz :Lߖ=ZR)ʇzhΝ̪|F#n:uuu&Lm BVǎc_7!d\>V~70>w~ YIQ޸He~FAVzYGGGnnnlllTT|@[N2ԩSE]x' W(?N(J"XnIQ`>}Jի׊+lkѢE=z lnn6kҌF /Э[nݺ͚5h4R6sLRʕ+ɖ6(..4hyH/W˜e7Ͷҋ?0111((s{ͦZ[v9Fʔ9%%]Y +)o?|K.Ԑeee卍O=ԬY(;vyt:]]]݀rssmIQԒ%KnݺuӧO}ܹskjjrrrmf0 Bt~7444440fϞ=k֬ .tvv>}:33S` 'OLtl\27o*Μ9CѝEEFFKxn۳ LKK[j^={11j7nܘjY?<.=;;{QF IDATQQyyyY7|,JGQ)S(=zr鮮e˖j\>hРVzVjz=Maڴi+VmV}d4;"pvܞ]' "j'|B944LFјL&f]Y:yLu#ؘ߃[L;w.##ŋē ~! \ׯϟ?ܸqn _ecN;3V@aD߿_~o[wcY#yqi95878@%Jne@fbZJ0WdU d%\%Y) !+mÍK@V YiL-R(L-nZ d0XJG`j hn@JG )Ktn\dY%@@V: q .=dk . Y2KCV@.%d!.YKAV @Ln d{!.YvK@VzIDvPb Ә\{ށq&\ \{އq߇eeW\&d/8AV(L0| ҧ!1| 1;+0'`x 1<Y"OBV1L0<Y $&[;C"b &; + Vs3$ !" Y)rg80{8~w Yp‰$2@r%Dv{Y0ʹ IFd%PDsq ` *J&g + Gi&Є@>֮֊ ?%d%b6$ `NJpYntV"W퀬aߙr^YOECSǯ"VH'dV⁖J$m۶ 6,444,,l̘1?Sqq}ݧRƏ___l_TT4dȐF駟,/888%%e-7mڔܯ_׳Wսˡ={3g^X߱5(!!<pڬˮ-?~=hР544ݻwOKKۿh$KƏO6.))*,,4߱c,kLIIhkk;rHrrraa!YUXXȬ:|pRR7o6 555SL;wӬ"g fO?ļy&EQ?3{IXXy}3FcRSSmVgY={#F GaZo߾ݻwok lY E.qw^݁s|üH$&r ^R+_FPPPWWu5zRIRlhh7ثT*N[hў={_NjdN`;ZH\A+=s#/ 04M/\0222**^3Lj^d3L . ̰}43S>?Z6lذ~*++O]{=nVzzmnȼ={`^߿?--NKK3[ż>xP((*++cVY+SXUVk׮Ur|'Oi… cǎ ymIӴ^6my|rR]ggkT*'O|mVqf0^x`04m4g̘AXb*v=p@\V7nh9n4FH/AiYcr MӧNJNN6LdJNNeZZ֌9r޽޽{>gJ:?5h[+7f/)--:tCFcKKKiiiFFeuƍj$ ٥۷<ꫯȪ/l'==}ҥ7nEEE{fV%$$۷9`ӬN|}ѫW666Λ7˳e|| jRZ>wYx={Z*jf*W^}UΡ&PH/jkkL9{?4iy=q5k g:hm't 򸀼J9jڟml'Y)J;::eޖ5JRabzp~P*vvvdL&JR4(()fpG;6:::99ȲRݴ J:.""Q`_8A' +Sj$l'k敖o L&ӭ[({mqqqĚ2+=8Xa2vիW/Jmv^pnouCCYItVVʕ+WX3 g:}+{nfw<ӧϝ;)''fAP(K.͚5744444撅gϞ5kօ :;;O>)'O7oM>K&SXEVV֙3g


x畭SLQ*=zxwr9M]]]˖-#s TPP*[^jõZ^i`0L6T<Y  XRRB;M6s{v'N2dL&S՟| мd2i4F~isqVm-kL&+pBv;=@Bc<U'xܹs/^Lu&~Q;!z ?ill~Ǎֺld vh4/22vk]n8`܊4l~|>lL/mJېpUqN!+!.!CVpcBb:=7L*)d%ĥD3`abd% HL/X?38#Z(IENDB`./oar-2.5.2/docs/schemas/almighty_automaton_changenode_part.dia0000644000175000017500000000305211757171206022721 0ustar plbplb[ݒ6ߧ`-+#@ftz\u:hm~f65ԨRSn_ήW> U%yh7\+Lݩ9+5:=-`9Xsf@35Ν;O10er>&&! RJ}bJdWb_=?XAfWrݾ ,hH#p ƒlÄ]7c_i7vlE .e)4'Aݯ2J^#רw[ jXHnٱ3/!k 1p>}KXϋ?E)-LXt{l]:mUŏa`X <}A~t^J"Wd0T|JX"*O>:Hg'} $G.uz)NK[Fv9E n] 64%꺊X2Ee9#z,e`M3i*Iv~~suD<5F./oar-2.5.2/docs/schemas/Almighty.ps0000644000175000017500000002612511757171206015264 0ustar plbplb%!PS-Adobe-2.0 %%Title: Almighty.ps %%Creator: fig2dev Version 3.2 Patchlevel 3d %%CreationDate: Thu Mar 6 19:37:13 2003 %%For: ghuard@piednoir (Guillaume Huard,,,) %%Orientation: Landscape %%Pages: 1 %%BoundingBox: 0 0 612 792 %%BeginSetup %%IncludeFeature: *PageSize Letter %%EndSetup %%Magnification: 1.0000 %%EndComments /$F2psDict 200 dict def $F2psDict begin $F2psDict /mtrx matrix put /col-1 {0 setgray} bind def /col0 {0.000 0.000 0.000 srgb} bind def /col1 {0.000 0.000 1.000 srgb} bind def /col2 {0.000 1.000 0.000 srgb} bind def /col3 {0.000 1.000 1.000 srgb} bind def /col4 {1.000 0.000 0.000 srgb} bind def /col5 {1.000 0.000 1.000 srgb} bind def /col6 {1.000 1.000 0.000 srgb} bind def /col7 {1.000 1.000 1.000 srgb} bind def /col8 {0.000 0.000 0.560 srgb} bind def /col9 {0.000 0.000 0.690 srgb} bind def /col10 {0.000 0.000 0.820 srgb} bind def /col11 {0.530 0.810 1.000 srgb} bind def /col12 {0.000 0.560 0.000 srgb} bind def /col13 {0.000 0.690 0.000 srgb} bind def /col14 {0.000 0.820 0.000 srgb} bind def /col15 {0.000 0.560 0.560 srgb} bind def /col16 {0.000 0.690 0.690 srgb} bind def /col17 {0.000 0.820 0.820 srgb} bind def /col18 {0.560 0.000 0.000 srgb} bind def /col19 {0.690 0.000 0.000 srgb} bind def /col20 {0.820 0.000 0.000 srgb} bind def /col21 {0.560 0.000 0.560 srgb} bind def /col22 {0.690 0.000 0.690 srgb} bind def /col23 {0.820 0.000 0.820 srgb} bind def /col24 {0.500 0.190 0.000 srgb} bind def /col25 {0.630 0.250 0.000 srgb} bind def /col26 {0.750 0.380 0.000 srgb} bind def /col27 {1.000 0.500 0.500 srgb} bind def /col28 {1.000 0.630 0.630 srgb} bind def /col29 {1.000 0.750 0.750 srgb} bind def /col30 {1.000 0.880 0.880 srgb} bind def /col31 {1.000 0.840 0.000 srgb} bind def end save newpath 0 792 moveto 0 0 lineto 612 0 lineto 612 792 lineto closepath clip newpath 40.0 40.0 translate 90 rotate 1 -1 scale /cp {closepath} bind def /ef {eofill} bind def /gr {grestore} bind def /gs {gsave} bind def /sa {save} bind def /rs {restore} bind def /l {lineto} bind def /m {moveto} bind def /rm {rmoveto} bind def /n {newpath} bind def /s {stroke} bind def /sh {show} bind def /slc {setlinecap} bind def /slj {setlinejoin} bind def /slw {setlinewidth} bind def /srgb {setrgbcolor} bind def /rot {rotate} bind def /sc {scale} bind def /sd {setdash} bind def /ff {findfont} bind def /sf {setfont} bind def /scf {scalefont} bind def /sw {stringwidth} bind def /tr {translate} bind def /tnt {dup dup currentrgbcolor 4 -2 roll dup 1 exch sub 3 -1 roll mul add 4 -2 roll dup 1 exch sub 3 -1 roll mul add 4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb} bind def /shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul 4 -2 roll mul srgb} bind def /DrawEllipse { /endangle exch def /startangle exch def /yrad exch def /xrad exch def /y exch def /x exch def /savematrix mtrx currentmatrix def x y tr xrad yrad sc 0 0 1 startangle endangle arc closepath savematrix setmatrix } def /$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def /$F2psEnd {$F2psEnteredState restore end} def $F2psBegin 10 setmiterlimit 0.06000 0.06000 sc %%Page: 1 1 % % Fig objects follow % 7.500 slw % Ellipse n 1800 1200 600 300 0 360 DrawEllipse gs col0 s gr /Times-Roman ff 240.00 scf sf 1650 1275 m gs 1 -1 sc (Init) col0 sh gr % Ellipse n 4200 2100 600 300 0 360 DrawEllipse gs col0 s gr /Times-Roman ff 240.00 scf sf 3975 2175 m gs 1 -1 sc (Qget) col0 sh gr % Ellipse n 5100 5100 900 300 0 360 DrawEllipse gs col0 s gr /Times-Roman ff 240.00 scf sf 4650 5175 m gs 1 -1 sc (Scheduler) col0 sh gr % Ellipse n 1800 5100 900 300 0 360 DrawEllipse gs col0 s gr /Times-Roman ff 240.00 scf sf 1200 5175 m gs 1 -1 sc (Time update) col0 sh gr % Ellipse n 5100 6600 600 300 0 360 DrawEllipse gs col0 s gr /Times-Roman ff 240.00 scf sf 4725 6675 m gs 1 -1 sc (Runner) col0 sh gr % Ellipse n 7800 5100 600 300 0 360 DrawEllipse gs col0 s gr /Times-Roman ff 240.00 scf sf 7575 5175 m gs 1 -1 sc (Leon) col0 sh gr % Ellipse n 7500 3450 1200 450 0 360 DrawEllipse gs col0 s gr /Times-Roman ff 240.00 scf sf 6600 3450 m gs 1 -1 sc (Check for villains) col0 sh gr /Times-Roman ff 240.00 scf sf 7200 3675 m gs 1 -1 sc (\(Sarko\)) col0 sh gr % Arc gs clippath 1767 4806 m 1824 4822 l 1892 4588 l 1822 4724 l 1834 4571 l cp eoclip n 5974.0 5904.8 4317.8 -122.2 -165.2 arcn gs col0 s gr gr % arrowhead 30.000 slw n 1834 4571 m 1822 4724 l 1892 4588 l 1855 4608 l 1834 4571 l cp gs 0.00 setgray ef gr col0 s % Arc 7.500 slw gs clippath 2268 5297 m 2216 5326 l 2337 5539 l 2289 5394 l 2389 5509 l cp eoclip n 4358.7 4226.6 2377.6 86.6 152.5 arc gs col0 s gr gr % arrowhead 30.000 slw n 2389 5509 m 2289 5394 l 2337 5539 l 2348 5498 l 2389 5509 l cp gs 0.00 setgray ef gr col0 s % Arc 7.500 slw gs clippath 3611 2206 m 3618 2147 l 3375 2116 l 3521 2165 l 3368 2176 l cp eoclip n 3278.8 4020.8 1873.5 155.4 -80.1 arc gs col0 s gr gr % arrowhead 30.000 slw n 3368 2176 m 3521 2165 l 3375 2116 l 3401 2150 l 3368 2176 l cp gs 0.00 setgray ef gr col0 s % Arc 7.500 slw gs clippath 7197 4983 m 7228 4932 l 7019 4803 l 7132 4908 l 6988 4855 l cp eoclip n 10857.6 -1154.2 7116.1 150.7 120.9 arcn gs col0 s gr gr % arrowhead 30.000 slw n 6988 4855 m 7132 4908 l 7019 4803 l 7029 4845 l 6988 4855 l cp gs 0.00 setgray ef gr col0 s % Arc 7.500 slw gs clippath 1823 5376 m 1766 5395 l 1843 5626 l 1825 5475 l 1900 5607 l cp eoclip n 4800.0 4500.0 3132.1 16.7 163.3 arc gs col0 s gr gr % arrowhead 30.000 slw n 1900 5607 m 1825 5475 l 1843 5626 l 1862 5588 l 1900 5607 l cp gs 0.00 setgray ef gr col0 s % Arc 7.500 slw gs clippath 4635 4905 m 4682 4868 l 4531 4676 l 4601 4813 l 4484 4714 l cp eoclip n 6685.7 3150.0 2668.3 -163.7 139.7 arcn gs col0 s gr gr % arrowhead 30.000 slw n 4484 4714 m 4601 4813 l 4531 4676 l 4526 4719 l 4484 4714 l cp gs 0.00 setgray ef gr col0 s % Polyline 7.500 slw gs clippath 3600 1983 m 3626 1929 l 3405 1824 l 3528 1916 l 3379 1879 l cp eoclip n 2325 1350 m 3600 1950 l gs col0 s gr gr % arrowhead 30.000 slw n 3379 1879 m 3528 1916 l 3405 1824 l 3419 1864 l 3379 1879 l cp gs 0.00 setgray ef gr col0 s % Polyline 7.500 slw gs clippath 5070 6315 m 5130 6315 l 5130 6071 l 5100 6221 l 5070 6071 l cp eoclip n 5100 5400 m 5100 6300 l gs col0 s gr gr % arrowhead 30.000 slw n 5070 6071 m 5100 6221 l 5130 6071 l 5100 6101 l 5070 6071 l cp gs 0.00 setgray ef gr col0 s % Polyline 7.500 slw gs clippath 7776 4823 m 7833 4804 l 7756 4573 l 7775 4725 l 7699 4592 l cp eoclip n 7500 3900 m 7800 4800 l gs col0 s gr gr % arrowhead 30.000 slw n 7699 4592 m 7775 4725 l 7756 4573 l 7737 4611 l 7699 4592 l cp gs 0.00 setgray ef gr col0 s % Polyline 7.500 slw gs clippath 6523 3183 m 6552 3131 l 6340 3011 l 6456 3111 l 6310 3063 l cp eoclip n 4800 2175 m 6525 3150 l gs col0 s gr gr % arrowhead 30.000 slw n 6310 3063 m 6456 3111 l 6340 3011 l 6351 3051 l 6310 3063 l cp gs 0.00 setgray ef gr col0 s % Polyline 7.500 slw gs clippath 2685 5070 m 2685 5130 l 2929 5130 l 2779 5100 l 2929 5070 l cp eoclip n 4200 5100 m 2700 5100 l gs col0 s gr gr % arrowhead 30.000 slw n 2929 5070 m 2779 5100 l 2929 5130 l 2899 5100 l 2929 5070 l cp gs 0.00 setgray ef gr col0 s % Polyline 7.500 slw n 3900 1350 m 4500 1350 l 4500 1650 l 3900 1650 l cp gs col0 s gr % Polyline n 3900 1050 m 4500 1050 l 4500 1350 l 3900 1350 l cp gs col0 s gr % Polyline n 3900 750 m 4500 750 l 4500 1050 l 3900 1050 l cp gs col0 s gr % Polyline n 3900 750 m 3900 450 l gs col0 s gr % Polyline n 4500 750 m 4500 450 l gs col0 s gr % Polyline 2 slj gs clippath 1530 5339 m 1492 5292 l 1302 5445 l 1438 5375 l 1339 5492 l cp eoclip n 8400 3750 m 8401 3750 l 8402 3751 l 8405 3753 l 8410 3755 l 8417 3758 l 8427 3763 l 8439 3769 l 8455 3777 l 8473 3786 l 8494 3797 l 8519 3809 l 8546 3824 l 8577 3840 l 8610 3858 l 8647 3878 l 8685 3899 l 8726 3923 l 8770 3948 l 8814 3974 l 8860 4002 l 8908 4032 l 8955 4063 l 9004 4095 l 9052 4129 l 9100 4164 l 9148 4200 l 9194 4237 l 9240 4276 l 9284 4315 l 9326 4356 l 9367 4397 l 9405 4440 l 9441 4485 l 9474 4530 l 9504 4577 l 9531 4626 l 9555 4676 l 9575 4727 l 9591 4781 l 9603 4836 l 9611 4894 l 9613 4954 l 9611 5016 l 9602 5081 l 9588 5148 l 9568 5218 l 9542 5290 l 9508 5365 l 9467 5443 l 9418 5523 l 9362 5605 l 9297 5689 l 9225 5775 l 9167 5838 l 9106 5901 l 9042 5964 l 8975 6027 l 8906 6089 l 8836 6150 l 8765 6210 l 8694 6269 l 8622 6327 l 8550 6384 l 8479 6439 l 8408 6492 l 8339 6544 l 8270 6595 l 8203 6644 l 8137 6691 l 8072 6737 l 8009 6781 l 7948 6824 l 7888 6866 l 7829 6906 l 7772 6944 l 7716 6982 l 7662 7018 l 7609 7053 l 7558 7087 l 7507 7120 l 7458 7152 l 7410 7184 l 7362 7214 l 7315 7244 l 7269 7273 l 7224 7302 l 7179 7330 l 7134 7358 l 7089 7385 l 7044 7413 l 6999 7439 l 6953 7466 l 6907 7493 l 6861 7520 l 6813 7546 l 6765 7573 l 6716 7600 l 6665 7627 l 6613 7654 l 6559 7682 l 6504 7709 l 6447 7737 l 6388 7766 l 6327 7795 l 6264 7823 l 6198 7853 l 6130 7882 l 6060 7912 l 5987 7942 l 5911 7973 l 5832 8003 l 5751 8034 l 5667 8064 l 5580 8094 l 5491 8124 l 5399 8154 l 5304 8183 l 5207 8212 l 5108 8239 l 5007 8266 l 4904 8291 l 4800 8315 l 4695 8336 l 4590 8356 l 4484 8374 l 4379 8388 l 4275 8400 l 4145 8410 l 4018 8416 l 3895 8416 l 3778 8413 l 3666 8406 l 3559 8395 l 3459 8382 l 3365 8366 l 3277 8347 l 3196 8327 l 3121 8304 l 3051 8281 l 2988 8255 l 2930 8229 l 2877 8202 l 2830 8175 l 2787 8146 l 2749 8117 l 2714 8088 l 2684 8059 l 2656 8029 l 2632 7999 l 2610 7968 l 2591 7938 l 2574 7907 l 2558 7876 l 2544 7845 l 2531 7813 l 2519 7781 l 2507 7749 l 2495 7717 l 2483 7684 l 2471 7650 l 2457 7616 l 2443 7582 l 2428 7546 l 2411 7510 l 2393 7473 l 2373 7436 l 2350 7397 l 2325 7357 l 2298 7317 l 2269 7275 l 2237 7232 l 2202 7188 l 2164 7142 l 2124 7096 l 2081 7048 l 2036 6999 l 1988 6949 l 1939 6898 l 1887 6846 l 1835 6793 l 1782 6740 l 1728 6686 l 1676 6632 l 1624 6578 l 1575 6525 l 1515 6456 l 1460 6389 l 1411 6324 l 1368 6263 l 1331 6204 l 1299 6148 l 1272 6095 l 1249 6045 l 1232 5998 l 1218 5954 l 1208 5911 l 1201 5871 l 1198 5834 l 1198 5797 l 1200 5763 l 1205 5730 l 1212 5699 l 1221 5669 l 1232 5641 l 1244 5613 l 1258 5587 l 1273 5562 l 1289 5538 l 1305 5515 l 1322 5493 l 1339 5473 l 1357 5454 l 1374 5436 l 1390 5419 l 1406 5404 l 1421 5390 l 1435 5377 l 1448 5367 l 1459 5357 l 1469 5349 l 1477 5342 l 1484 5337 l 1490 5333 l 1500 5325 l gs col0 s gr gr % arrowhead 0 slj 30.000 slw n 1339 5492 m 1438 5375 l 1302 5445 l 1344 5449 l 1339 5492 l cp gs 0.00 setgray ef gr col0 s /Times-Roman ff 240.00 scf sf 5550 2475 m gs 1 -1 sc (Villains) col0 sh gr /Times-Roman ff 240.00 scf sf 5325 3150 m gs 1 -1 sc (Qdel) col0 sh gr /Times-Roman ff 240.00 scf sf 4200 3450 m gs 1 -1 sc (Qsub) col0 sh gr /Times-Roman ff 240.00 scf sf 4275 3750 m gs 1 -1 sc (Term) col0 sh gr /Times-Roman ff 240.00 scf sf 4350 4050 m gs 1 -1 sc (Scheduler) col0 sh gr /Times-Roman ff 240.00 scf sf 2625 3525 m gs 1 -1 sc (Time) col0 sh gr /Times-Roman ff 240.00 scf sf 7725 4350 m gs 1 -1 sc (NewFrag) col0 sh gr /Times-Roman ff 240.00 scf sf 9750 4875 m gs 1 -1 sc (NoNewFrag) col0 sh gr /Times-Roman ff 240.00 scf sf 5175 5850 m gs 1 -1 sc (NewLaunch) col0 sh gr /Times-Roman ff 240.00 scf sf 2775 5475 m gs 1 -1 sc (NoNewLaunch) col0 sh gr /Times-Roman ff 240.00 scf sf 4650 1275 m gs 1 -1 sc (Qget requests) col0 sh gr $F2psEnd rs showpage ./oar-2.5.2/docs/schemas/Almighty.fig0000644000175000017500000000650611757171206015410 0ustar plbplb#FIG 3.2 Landscape Center Inches Letter 100.00 Single -2 1200 2 5 1 0 1 0 7 50 0 -1 0.000 0 1 1 0 5974.039 5904.808 3675 2250 2475 3375 1800 4800 2 1 3.00 60.00 120.00 5 1 0 1 0 7 50 0 -1 0.000 0 0 1 0 4358.692 4226.573 4500 6600 2775 6000 2250 5325 2 1 3.00 60.00 120.00 5 1 0 1 0 7 50 0 -1 0.000 0 0 1 0 3278.765 4020.762 1575 4800 1950 2700 3600 2175 2 1 3.00 60.00 120.00 5 1 0 1 0 7 50 0 -1 0.000 0 1 1 0 10857.598 -1154.167 4650 2325 5925 3975 7200 4950 2 1 3.00 60.00 120.00 5 1 0 1 0 7 50 0 -1 0.000 0 0 1 0 4800.000 4500.000 7800 5400 3900 7500 1800 5400 2 1 3.00 60.00 120.00 5 1 0 1 0 7 50 0 -1 0.000 0 1 1 0 6685.714 3150.000 4125 2400 4125 3900 4650 4875 2 1 3.00 60.00 120.00 6 1200 900 2400 1500 1 1 0 1 0 7 50 0 -1 0.000 1 0.0000 1800 1200 600 300 1800 1200 2400 1500 4 0 0 50 0 0 16 0.0000 4 165 315 1650 1275 Init\001 -6 6 3600 1800 4800 2400 1 1 0 1 0 7 50 0 -1 0.000 1 0.0000 4200 2100 600 300 4200 2100 4800 2400 4 0 0 50 0 0 16 0.0000 4 225 465 3975 2175 Qget\001 -6 6 4200 4800 6000 5400 1 1 0 1 0 7 50 0 -1 0.000 1 0.0000 5100 5100 900 300 5100 5100 6000 5400 4 0 0 50 0 0 16 0.0000 4 165 945 4650 5175 Scheduler\001 -6 6 900 4800 2700 5400 1 1 0 1 0 7 50 0 -1 0.000 1 0.0000 1800 5100 900 300 1800 5100 2700 5400 4 0 0 50 0 0 16 0.0000 4 210 1185 1200 5175 Time update\001 -6 6 4500 6300 5700 6900 1 1 0 1 0 7 50 0 -1 0.000 1 0.0000 5100 6600 600 300 5100 6600 5700 6900 4 0 0 50 0 0 16 0.0000 4 165 705 4725 6675 Runner\001 -6 6 7200 4800 8400 5400 1 1 0 1 0 7 50 0 -1 0.000 1 0.0000 7800 5100 600 300 7800 5100 8400 5400 4 0 0 50 0 0 16 0.0000 4 165 495 7575 5175 Leon\001 -6 6 6300 3000 8700 3900 1 1 0 1 0 7 50 0 -1 0.000 1 0.0000 7500 3450 1200 450 7500 3450 8700 3900 4 0 0 50 0 0 16 0.0000 4 165 1680 6600 3450 Check for villains\001 4 0 0 50 0 0 16 0.0000 4 210 705 7200 3675 (Sarko)\001 -6 2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2 2 1 3.00 60.00 120.00 2325 1350 3600 1950 2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2 2 1 3.00 60.00 120.00 5100 5400 5100 6300 2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2 2 1 3.00 60.00 120.00 7500 3900 7800 4800 2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2 2 1 3.00 60.00 120.00 4800 2175 6525 3150 2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2 2 1 3.00 60.00 120.00 4200 5100 2700 5100 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 3900 1350 4500 1350 4500 1650 3900 1650 3900 1350 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 3900 1050 4500 1050 4500 1350 3900 1350 3900 1050 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 3900 750 4500 750 4500 1050 3900 1050 3900 750 2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2 3900 750 3900 450 2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2 4500 750 4500 450 3 2 0 1 0 7 50 0 -1 0.000 0 1 0 5 2 1 3.00 60.00 120.00 8400 3750 9225 5775 4275 8400 1575 6525 1500 5325 0.000 -1.000 -1.000 -1.000 0.000 4 0 0 50 0 0 16 0.0000 4 165 735 5550 2475 Villains\001 4 0 0 50 0 0 16 0.0000 4 210 465 5325 3150 Qdel\001 4 0 0 50 0 0 16 0.0000 4 210 510 4200 3450 Qsub\001 4 0 0 50 0 0 16 0.0000 4 165 510 4275 3750 Term\001 4 0 0 50 0 0 16 0.0000 4 165 945 4350 4050 Scheduler\001 4 0 0 50 0 0 16 0.0000 4 165 495 2625 3525 Time\001 4 0 0 50 0 0 16 0.0000 4 225 900 7725 4350 NewFrag\001 4 0 0 50 0 0 16 0.0000 4 225 1200 9750 4875 NoNewFrag\001 4 0 0 50 0 0 16 0.0000 4 165 1185 5175 5850 NewLaunch\001 4 0 0 50 0 0 16 0.0000 4 165 1485 2775 5475 NoNewLaunch\001 4 0 0 50 0 0 16 0.0000 4 225 1290 4650 1275 Qget requests\001 ./oar-2.5.2/docs/documentation/0000755000175000017500000000000011757171206014362 5ustar plbplb./oar-2.5.2/docs/documentation/CHANGELOG0000777000175000017500000000000011757171206017431 2../../CHANGELOGustar plbplb./oar-2.5.2/docs/documentation/CPUSET0000777000175000017500000000000011757171206016661 2../../CPUSETustar plbplb./oar-2.5.2/docs/documentation/FAQ-USER0000777000175000017500000000000011757171206017221 2../../FAQ-USERustar plbplb./oar-2.5.2/docs/documentation/INSTALL0000777000175000017500000000000011757171206017067 2../../INSTALLustar plbplb./oar-2.5.2/docs/documentation/scheduler0000777000175000017500000000000011757171206024751 2../../sources/core/modules/scheduler/ustar plbplb./oar-2.5.2/docs/documentation/oaradmin.rst0000644000175000017500000000431111757171206016705 0ustar plbplb *oaradmin* ---------- This command permits to create resources and manage admission rules easily. An optional feature permits versioning changes in admission rules and conf files. :Requirements: For oaradmin, the following packages must be installed: - Perl-Yaml - Ruby 1.8 or greater - Ruby-Yaml - Ruby-DBI - Subversion for the optional versioning feature Options for resources subcommand are: :: -a, --add Add new resources --cpusetproperty=prop Property name for cpuset numbers -s, --select Select resources for update -p, --property Set value for a property -d, --delete Delete resources -c, --commit Commit in oar database Examples: :: # oaradmin resources -a /node=mycluster{12}.domain/cpu={2}/core={2} # oaradmin resources -a /node=mycluster-[1-250].domain/cpu={2} # oaradmin resources -a /node=mycluster-[1-250].domain/cpu={2} -p memnode=1024 -p cpufreq=3.2 -p cputype=xeon Options for rules subcommand are: :: -l, --list List admission rules -a, --add Add an admission rule -f, --file File which contains script for admission rule -d, --delete Delete admission rules -x, --export Export admission rules -e, --edit Edit an admission rule -1, --enable Enable the admission rule (removing comments) -0, --disable Disable the admission rule (commenting the code) -H, --history Show all changes made on the admission rule -R, --revert Revert to the admission rule as it existed in a revision number Examples: :: # oaradmin rules -l # oaradmin rules -lll 3 # oaradmin rules -e 3 Options for conf subcommand are: :: -e, --edit Edit the conf file -H, --history Show all changes made on the conf file -R, --revert Revert to the conf file as it existed in a revision number Examples: :: # oaradmin conf -e /etc/oar/oar.conf # oaradmin conf -R /etc/oar/oar.conf 3 ./oar-2.5.2/docs/documentation/oar-documentation-devel.rst0000644000175000017500000000121511757171206021640 0ustar plbplb===================================== OAR Documentation - Developer Guide ===================================== .. include:: doc_header.rst :Dedication: For developers and experimenters. **BE CAREFULL : THIS DOCUMENTATION IS FOR OAR >= 2.5.0** PDF version : ``_ .. section-numbering:: .. contents:: Table of Contents ------------------------------------------------------------------------------- Coding Guidelines ================= .. include:: OAR-DOCUMENTATION-API-DEVEL.rst Database scheme =============== .. include:: doc_internal_mechanisms.rst .. include:: faq-devel.rst .. include:: CHANGELOG ./oar-2.5.2/docs/documentation/faq-devel.rst0000644000175000017500000000003111757171206016752 0ustar plbplbFAQ - DEVEL =========== ./oar-2.5.2/docs/documentation/doc_usecases.rst0000644000175000017500000005211711757171206017562 0ustar plbplbOAR Use Cases ============= Interactive jobs ---------------- Job submission ~~~~~~~~~~~~~~ :: jdoe@idpot:~$ oarsub -I -l /nodes=3/core=1 [ADMISSION RULE] Set default walltime to 7200. [ADMISSION RULE] Modify resource description with type constraints OAR_JOB_ID=4924 Interactive mode : waiting... [2007-03-07 08:51:04] Starting... Connect to OAR job 4924 via the node idpot5.grenoble.grid5000.fr jdoe@idpot5:~$ Connecting to the other cores ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :: jdoe@idpot5:~$ cat $OAR_NODEFILE idpot5.grenoble.grid5000.fr idpot8.grenoble.grid5000.fr idpot9.grenoble.grid5000.fr jdoe@idpot5:~$ oarsh idpot8 Last login: Tue Mar 6 18:00:37 2007 from idpot.imag.fr jdoe@idpot8:~$ oarsh idpot9 Last login: Wed Mar 7 08:48:30 2007 from idpot.imag.fr jdoe@idpot9:~$ oarsh idpot5 Last login: Wed Mar 7 08:51:45 2007 from idpot5.imag.fr jdoe@idpot5:~$ Copying a file from one node to another ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :: jdoe@idpot5:~$ hostname > /tmp/my_hostname jdoe@idpot5:~$ oarcp /tmp/my_hostname idpot8:/tmp/my_hostname jdoe@idpot5:~$ oarsh idpot8 cat /tmp/my_hostname idpot5 jdoe@idpot5:~$ Connecting to our job ~~~~~~~~~~~~~~~~~~~~~ :: jdoe@idpot:~$ OAR_JOB_ID=4924 oarsh idpot9 Last login: Wed Mar 7 08:52:09 2007 from idpot8.imag.fr jdoe@idpot9:~$ oarsh idpot5 Last login: Wed Mar 7 08:52:18 2007 from idpot9.imag.fr jdoe@idpot5:~$ Batch mode job -------------- Submission using a script ~~~~~~~~~~~~~~~~~~~~~~~~~ :: jdoe@paramount:~$ oarsub -l core=10 ./runhpl Generate a job key... [ADMISSION RULE] Set default walltime to 3600. [ADMISSION RULE] Modify resource description with type constraints OAR_JOB_ID=199522 Watching results ~~~~~~~~~~~~~~~~ :: jdoe@paramount:~$ cat OAR.199522.stdout ... Submission using an inline command ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Sometimes it is very useful to run a little command in oarsub: :: jdoe@paramount:~$ oarsub -l core=1 'echo $PATH;which ssh' Generate a job key... [ADMISSION RULE] Set default walltime to 3600. [ADMISSION RULE] Modify resource description with type constraints OAR_JOB_ID=199523 Watching results ~~~~~~~~~~~~~~~~ :: jdoe@paramount:~$ cat OAR.199523.stdout ... Reservations ------------ The date format to pass to the -r option is YYYY-MM-DD HH:MM:SS: :: jdoe@paramount:~$ oarsub -l core=10 ./runhpl -r "2007-10-10 18:00:00" Generate a job key... [ADMISSION RULE] Set default walltime to 3600. [ADMISSION RULE] Modify resource description with type constraints OAR_JOB_ID=199524 Reservation mode : waiting validation... Reservation valid --> OK jdoe@paramount:~$ Examples of resource requests ----------------------------- Using the resource hierarchy ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ask for 1 core on 15 nodes on a same cluster (total = 15 cores) :: oarsub -I -l /cluster=1/nodes=15/core=1 - ask for 1 core on 15 nodes on 2 clusters (total = 30 cores) :: oarsub -I -l /cluster=2/nodes=15/core=1 - ask for 1 core on 2 cpus on 15 nodes on a same cluster (total = 30 cores) :: oarsub -I -l /cluster=1/nodes=15/cpu=2/core=1 - ask for 10 cpus on 2 clusters (total = 20 cpus, information regarding the node ou core count depend on the topology of the machines) :: oarsub -I -l /cluster=2/cpu=10 - ask for 1 core on 3 different network switches (total = 3 cores) :: oarsub -I -l /switch=3/core=1 Using properties ~~~~~~~~~~~~~~~~ See OAR properties for a description of all available properties, and watch Monika. - ask for 10 cores of the cluster azur :: oarsub -I -l core=10 -p "cluster='azur'" - ask for 2 nodes with 4096 GB of memory and Infiniband 10G :: oarsub -I -p "memnode=4096 and ib10g='YES'" -l nodes=2 - ask for any 4 nodes except gdx-45 :: oarsub -I -p "not host like 'gdx-45.%'" -l nodes=4 Mixing every together ~~~~~~~~~~~~~~~~~~~~~ - ask for 1 core on 2 nodes on the same cluster with 4096 GB of memory and Infiniband 10G + 1 cpu on 2 nodes on the same switch with bicore processors for a walltime of 4 hours :: oarsub -I -l "{memnode=4096 and ib10g='YES'}/cluster=1/nodes=2/core=1+{cpucore=2}/switch=1/nodes=2/cpu=1,walltime=4:0:0" Warning _______ 1. walltime must always be the last argument of -l <...> 2. if no resource matches your request, oarsub will exit with the message :: Generate a job key... [ADMISSION RULE] Set default walltime to 3600. [ADMISSION RULE] Modify resource description with type constraints There are not enough resources for your request OAR_JOB_ID=-5 Oarsub failed: please verify your request syntax or ask for support to your admin. Moldable jobs ~~~~~~~~~~~~~ - ask for 4 nodes and a walltime of 2 hours or 2 nodes and a walltime of 4 hours :: oarsub -I -l nodes=4,walltime=2 -l nodes=2,walltime=4 Types of job ~~~~~~~~~~~~ OAR2 feature the concept of job "type". Among them, the type deploy (that used to be a queue with OAR 1.6) and the type besteffort. - ask for 4 nodes on the same cluster in order to deploy a customized environment: :: oarsub -I -l cluster=1/nodes=4,walltime=6 -t deploy - submit besteffort jobs :: for param in $(< ./paramlist); do oarsub -t besteffort -l core=1 "./my_script.sh $param" done X11 forwarding -------------- Some users complained about the lack of X11 forwarding in oarsub or oarsh. It is now enabled. We are using xeyes to test X: 2 big eyes should appear on your screen, and follow the moves of your mouse. Shell 1 ~~~~~~~ Check DISPLAY _____________ :: jdoe@idpot:~$ echo $DISPLAY localhost:11.0 Job submission ______________ :: jdoe@idpot:~$ oarsub -I -l /nodes=2/core=1 [ADMISSION RULE] Set default walltime to 7200. [ADMISSION RULE] Modify resource description with type constraints OAR_JOB_ID=4926 Interactive mode : waiting... [2007-03-07 09:01:16] Starting... Initialize X11 forwarding... Connect to OAR job 4926 via the node idpot8.grenoble.grid5000.fr jdoe@idpot8:~$ xeyes & [1] 14656 jdoe@idpot8:~$ cat $OAR_NODEFILE idpot8.grenoble.grid5000.fr idpot9.grenoble.grid5000.fr [1]+ Done xeyes jdoe@idpot8:~$ oarsh idpot9 xeyes Error: Can't open display: jdoe@idpot8:~$ oarsh -X idpot9 xeyes Shell 2 ~~~~~~~ :: jdoe@idpot:~$ echo $DISPLAY localhost:13.0 jdoe@idpot:~$ OAR_JOB_ID=4928 oarsh -X idpot9 xeyes Using a parallel launcher: taktuk --------------------------------- Warning: Taktuk MUST BE installed on all nodes to test this point Shell 1 ~~~~~~~ Unset DISPLAY so that X does not bother... __________________________________________ :: jdoe@idpot:~$ unset DISPLAY Job submission ______________ :: jdoe@idpot:~$ oarsub -I -l /nodes=20/core=1 [ADMISSION RULE] Set default walltime to 7200. [ADMISSION RULE] Modify resource description with type constraints OAR_JOB_ID=4930 Interactive mode : waiting... [2007-03-07 09:15:13] Starting... Connect to OAR job 4930 via the node idpot1.grenoble.grid5000.fr Running the taktuk command __________________________ :: jdoe@idpot1:~$ taktuk -c "oarsh" -f $OAR_FILE_NODES broadcast exec [ date ] idcalc12.grenoble.grid5000.fr-1: date (11567): output > Thu May 3 18:56:58 CEST 2007 idcalc12.grenoble.grid5000.fr-1: date (11567): status > Exited with status 0 idcalc4.grenoble.grid5000.fr-8: date (31172): output > Thu May 3 19:00:09 CEST 2007 idcalc2.grenoble.grid5000.fr-2: date (32368): output > Thu May 3 19:01:56 CEST 2007 idcalc3.grenoble.grid5000.fr-5: date (31607): output > Thu May 3 18:56:44 CEST 2007 idcalc3.grenoble.grid5000.fr-5: date (31607): status > Exited with status 0 idcalc7.grenoble.grid5000.fr-13: date (31188): output > Thu May 3 18:59:54 CEST 2007 idcalc9.grenoble.grid5000.fr-15: date (32426): output > Thu May 3 18:56:45 CEST 2007 idpot6.grenoble.grid5000.fr-20: date (16769): output > Thu May 3 18:59:54 CEST 2007 idcalc4.grenoble.grid5000.fr-8: date (31172): status > Exited with status 0 idcalc5.grenoble.grid5000.fr-9: date (10288): output > Thu May 3 18:56:39 CEST 2007 idcalc5.grenoble.grid5000.fr-9: date (10288): status > Exited with status 0 idcalc6.grenoble.grid5000.fr-11: date (11290): output > Thu May 3 18:57:52 CEST 2007 idcalc6.grenoble.grid5000.fr-11: date (11290): status > Exited with status 0 idcalc7.grenoble.grid5000.fr-13: date (31188): status > Exited with status 0 idcalc8.grenoble.grid5000.fr-14: date (10450): output > Thu May 3 18:57:34 CEST 2007 idcalc8.grenoble.grid5000.fr-14: date (10450): status > Exited with status 0 idcalc9.grenoble.grid5000.fr-15: date (32426): status > Exited with status 0 idpot1.grenoble.grid5000.fr-16: date (18316): output > Thu May 3 18:57:19 CEST 2007 idpot1.grenoble.grid5000.fr-16: date (18316): status > Exited with status 0 idpot10.grenoble.grid5000.fr-17: date (31547): output > Thu May 3 18:56:27 CEST 2007 idpot10.grenoble.grid5000.fr-17: date (31547): status > Exited with status 0 idpot2.grenoble.grid5000.fr-18: date (407): output > Thu May 3 18:56:21 CEST 2007 idpot2.grenoble.grid5000.fr-18: date (407): status > Exited with status 0 idpot4.grenoble.grid5000.fr-19: date (2229): output > Thu May 3 18:55:37 CEST 2007 idpot4.grenoble.grid5000.fr-19: date (2229): status > Exited with status 0 idpot6.grenoble.grid5000.fr-20: date (16769): status > Exited with status 0 idcalc2.grenoble.grid5000.fr-2: date (32368): status > Exited with status 0 idpot11.grenoble.grid5000.fr-6: date (12319): output > Thu May 3 18:59:54 CEST 2007 idpot7.grenoble.grid5000.fr-10: date (7355): output > Thu May 3 18:57:39 CEST 2007 idpot5.grenoble.grid5000.fr-12: date (13093): output > Thu May 3 18:57:23 CEST 2007 idpot3.grenoble.grid5000.fr-3: date (509): output > Thu May 3 18:59:55 CEST 2007 idpot3.grenoble.grid5000.fr-3: date (509): status > Exited with status 0 idpot8.grenoble.grid5000.fr-4: date (13252): output > Thu May 3 18:56:32 CEST 2007 idpot8.grenoble.grid5000.fr-4: date (13252): status > Exited with status 0 idpot11.grenoble.grid5000.fr-6: date (12319): status > Exited with status 0 idpot9.grenoble.grid5000.fr-7: date (17810): output > Thu May 3 18:57:42 CEST 2007 idpot9.grenoble.grid5000.fr-7: date (17810): status > Exited with status 0 idpot7.grenoble.grid5000.fr-10: date (7355): status > Exited with status 0 idpot5.grenoble.grid5000.fr-12: date (13093): status > Exited with status 0 Setting the connector definitively and running taktuk again ___________________________________________________________ :: jdoe@idpot1:~$ export TAKTUK_CONNECTOR=oarsh jdoe@idpot1:~$ taktuk -m idpot3 -m idpot4 broadcast exec [ date ] idpot3-1: date (12293): output > Wed Mar 7 09:20:25 CET 2007 idpot4-2: date (7508): output > Wed Mar 7 09:20:19 CET 2007 idpot3-1: date (12293): status > Exited with status 0 idpot4-2: date (7508): status > Exited with status 0 Using MPI with OARSH -------------------- To use MPI, you must setup your MPI stack so that it use OARSH instead of the default RSH or SSH connector. All required steps for the main different flavors of MPI are presented below. MPICH1 ~~~~~~ Mpich1 connector can be changed using the P4_RSHCOMMAND environment variable. This variable must be set in the shell configuration files. For instance for bash, within ~/.bashrc :: export P4_RSHCOMMAND=oarsh Please consider setting the P4_GLOBMEMSIZE as well. You can then run your mpich1 application: :: jdoe@idpot4:~/mpi/mpich$ mpirun.mpich -machinefile $OAR_FILE_NODES -np 6 ./hello Hello world from process 0 of 6 running on idpot4.grenoble.grid5000.fr Hello world from process 4 of 6 running on idpot6.grenoble.grid5000.fr Hello world from process 1 of 6 running on idpot4.grenoble.grid5000.fr Hello world from process 3 of 6 running on idpot5.grenoble.grid5000.fr Hello world from process 2 of 6 running on idpot5.grenoble.grid5000.fr Hello world from process 5 of 6 running on idpot6.grenoble.grid5000.fr MPICH2 ~~~~~~ Tested version: 1.0.5p2 MPICH2 uses daemons on nodes that may be started with the "mpdboot" command. This command takes oarsh has an argument (--rsh=oarsh) and all goes well: :: jdoe@idpot2:~/mpi/mpich/mpich2-1.0.5p2/bin$ ./mpicc -o hello ../../../hello.c jdoe@idpot2:~/mpi/mpich/mpich2-1.0.5p2/bin$ ./mpdboot --file=$OAR_NODEFILE --rsh=oarsh -n 2 jdoe@idpot2:~/mpi/mpich/mpich2-1.0.5p2/bin$ ./mpdtrace -l idpot2_39441 (129.88.70.2) idpot4_36313 (129.88.70.4) jdoe@idpot2:~/mpi/mpich/mpich2-1.0.5p2/bin$ ./mpiexec -np 8 ./hello Hello world from process 0 of 8 running on idpot2 Hello world from process 1 of 8 running on idpot4 Hello world from process 3 of 8 running on idpot4 Hello world from process 2 of 8 running on idpot2 Hello world from process 5 of 8 running on idpot4 Hello world from process 4 of 8 running on idpot2 Hello world from process 6 of 8 running on idpot2 Hello world from process 7 of 8 running on idpot4 LAM/MPI ~~~~~~~ Tested version: 7.1.3 You can use export LAMRSH=oarsh before starting lamboot; otherwise the "lamboot" command takes -ssi boot_rsh_agent "oarsh" option has an argument (this is not in the manual!). Also note that OARSH doesn't automatically sends the environnement of the user, so, you may need to specify the path to LAM distribution on the nodes with this option: -prefix :: jdoe@idpot2:~/mpi/lam$ ./bin/lamboot -prefix ~/mpi/lam \ -ssi boot_rsh_agent "oarsh" \ -d $OAR_FILE_NODES jdoe@idpot2:~/mpi/lam$ ./bin/mpirun -np 8 hello Hello world from process 2 of 8 running on idpot2 Hello world from process 3 of 8 running on idpot2 Hello world from process 0 of 8 running on idpot2 Hello world from process 1 of 8 running on idpot2 Hello world from process 4 of 8 running on idpot4 Hello world from process 6 of 8 running on idpot4 Hello world from process 5 of 8 running on idpot4 Hello world from process 7 of 8 running on idpot4 OpenMPI ~~~~~~~ Tested version: 1.1.4 The magic option to use with OpenMPI and OARSH is "-mca pls_rsh_agent "oarsh"". Also note that OpenMPI works with daemons that are started on the nodes (orted), but "mpirun" starts them on-demand. The "-prefix" option can help if OpenMPI is not installed in a standard path on the cluster nodes. :: jdoe@idpot2:~/mpi/openmpi$ ./bin/mpirun -prefix ~/mpi/openmpi \ -machinefile $OAR_FILE_NODES \ -mca pls_rsh_agent "oarsh" \ -np 8 hello Hello world from process 0 of 8 running on idpot2 Hello world from process 4 of 8 running on idpot4 Hello world from process 1 of 8 running on idpot2 Hello world from process 5 of 8 running on idpot4 Hello world from process 2 of 8 running on idpot2 Hello world from process 6 of 8 running on idpot4 Hello world from process 7 of 8 running on idpot4 Hello world from process 3 of 8 running on idpot2 Tests of the CPUSET mechanism ----------------------------- Processus isolation ~~~~~~~~~~~~~~~~~~~ In this test, we run 4 yes commands in a job whose resources is only one core. (syntax tested with bash as the user's shell) :: jdoe@idpot:~$ oarsub -l core=1 "yes > /dev/null & yes > /dev/null & yes > /dev/null & yes > /dev/null" [ADMISSION RULE] Set default walltime to 7200. [ADMISSION RULE] Modify resource description with type constraints OAR_JOB_ID=8683 Then we connect to the node and run top :: jdoe@idpot:~$ oarsub -C 8683 Initialize X11 forwarding... Connect to OAR job 8683 via the node idpot9.grenoble.grid5000.fr jdoe@idpot9:~$ ps -eo fname,pcpu,psr | grep yes yes 23.2 1 yes 23.1 1 yes 24.0 1 yes 23.0 1 This shows that the 4 processus are indeed restricted to the core the job was assigned to, as expected. Don't forget to delete your job: :: jdoe@idpot:~$ oardel 8683 Using best effort mode jobs --------------------------- Best effort job campaign ~~~~~~~~~~~~~~~~~~~~~~~~ OAR 2 provides a way to specify that jobs are best effort, which means that the server can delete them if room is needed to fit other jobs. One can submit such jobs using the besteffort type of job. For instance you can run a job campaign as follows: :: for param in $(< ./paramlist); do oarsub -t besteffort -l core=1 "./my_script.sh $param" done In this example, the file ./paramlist contains a list of parameters for a parametric application. The following demonstrates the mechanism. Best effort job mechanism ~~~~~~~~~~~~~~~~~~~~~~~~~ Running a besteffort job in a first shell _________________________________________ :: jdoe@idpot:~$ oarsub -I -l nodes=23 -t besteffort [ADMISSION RULE] Added automatically besteffort resource constraint [ADMISSION RULE] Redirect automatically in the besteffort queue [ADMISSION RULE] Set default walltime to 7200. [ADMISSION RULE] Modify resource description with type constraints OAR_JOB_ID=9630 Interactive mode : waiting... [2007-05-10 11:06:25] Starting... Initialize X11 forwarding... Connect to OAR job 9630 via the node idcalc1.grenoble.grid5000.fr Running a non best effort job on the same set of resources in a second shell ____________________________________________________________________________ :: jdoe@idpot:~$ oarsub -I [ADMISSION RULE] Set default walltime to 7200. [ADMISSION RULE] Modify resource description with type constraints OAR_JOB_ID=9631 Interactive mode : waiting... [2007-05-10 11:06:50] Start prediction: 2007-05-10 11:06:50 (Karma = 0.000) [2007-05-10 11:06:53] Starting... Initialize X11 forwarding... Connect to OAR job 9631 via the node idpot9.grenoble.grid5000.fr As expected, meanwhile the best effort job was stopped (watch the first shell): :: jdoe@idcalc1:~$ bash: line 1: 23946 Killed /bin/bash -l Connection to idcalc1.grenoble.grid5000.fr closed. Disconnected from OAR job 9630 jdoe@idpot:~$ Testing the checkpointing trigger mechanism ------------------------------------------- Writing the test script ~~~~~~~~~~~~~~~~~~~~~~~ Here is a script feature an infinite loop and a signal handler trigged by SIGUSR2 (default signal for OAR's checkpointing mechanism). :: #!/bin/bash handler() { echo "Caught checkpoint signal at: `date`"; echo "Terminating."; exit 0; } trap handler SIGUSR2 cat < 75, default => 25 }; # specify the target percentages for users (0 if not specified) my $Karma_user_targets = { oar => 100 }; # weight given to each criteria my $Karma_coeff_project_consumption = 3; my $Karma_coeff_user_consumption = 2; my $Karma_coeff_user_asked_consumption = 1; ############################################################################### This scheduler takes its historical data in the accounting_ table. To fill this, the command oaraccounting_ have to be run periodically (in a cron job for example). Otherwise the scheduler cannot be aware of new user consumptions. Hulot ----- This module is responsible of the advanced management of the standby mode of the nodes. It's related to the energy saving features of OAR. It is an optional module activated with the ENERGY_SAVING_INTERNAL=yes configuration variable. It runs as a fourth "Almighty" daemon and opens a pipe on which it receives commands from the MetaScheduler. It also communicates with a library called "WindowForker" that is responsible of forking shut-down/wake-up commands in a way that not too much commands are started at a time. -------------------------------------------------------------------------------- - Hulot general commands process schema: .. image:: ../schemas/hulot_general_commands_process.png When Hulot is activated, the metascheduler sends, each time it is executed, a list of nodes that need to be woken-up or may be halted. Hulot maintains a list of commands that have already been sent to the nodes and asks to the windowforker to actually execute the commands only when it is appropriate. A special feature is the "keepalive" of nodes depending on some properties: even if the metascheduler asks to shut-down some nodes, it's up to Hulot to check if the keepalive constraints are still satisfied. If not, Hulot refuses to halt the corresponding nodes. -------------------------------------------------------------------------------- - Hulot checking process schema: .. image:: ../schemas/hulot_checking_process.png Hulot is called each time the metascheduler is called, to do all the checking process. This process is also executed when Hulot receives normal halt or wake-up commands from the scheduler. Hulot checks if waking-up nodes are actually Alive or not and suspects the nodes if they haven't woken-up before the timeout. It also checks keepalive constraints and decides to wake-up nodes if a constraint is no more satisfied (for example because new jobs are running on nodes that are now busy, and no more idle). Hulot also checks the results of the commands sent by the windowforker and may also suspect a node if the command exited with non-zero status. -------------------------------------------------------------------------------- - Hulot wake-up process schema .. image:: ../schemas/hulot_wakeup_process.png -------------------------------------------------------------------------------- - Hulot shutdown process schema .. image:: ../schemas/hulot_shutdown_process.png -------------------------------------------------------------------------------- ./oar-2.5.2/docs/documentation/doc_mechanisms.rst0000644000175000017500000003131611757171206020074 0ustar plbplbMechanisms ========== .. _INTERACTIVE: How does an interactive *oarsub* work? -------------------------------------- .. figure:: ../schemas/interactive_oarsub_scheme.png :width: 17cm :alt: interactive oarsub decomposition :target: ../schemas/interactive_oarsub_scheme.png Interactive oarsub decomposition `interactive_oarsub_scheme.svg <../schemas/interactive_oarsub_scheme.svg>`_ Job launch ---------- For PASSIVE jobs, the mechanism is similar to the INTERACTIVE_ one, except for the shell launched from the frontal node. The job is finished when the user command ends. Then oarexec return its exit value (what errors occured) on the Almighty via the SERVER_PORT if DETACH_JOB_FROM_SERVER was set to 1 otherwise it returns directly. CPUSET ------ The cpuset name is effectively created on each nodes and is composed as "user_jobid". OAR system steps: 1. Before each job, the Runner initialize the CPUSET (see `CPUSET definition`) with OPENSSH_CMD and an efficient launching tool : `Taktuk `_. If it is not installed and configured (TAKTUK_CMD) then OAR uses an internal launching tool less optimized. The processors assigned to this cpuset are taken from the defined database field by JOB_RESOURCE_MANAGER_PROPERTY_DB_FIELD in the table resources. 2. After each job, OAR deletes all processes stored in the associated CPUSET. Thus all nodes are clean after a OAR job. If you don't want to use this feature, you can, but nothing will warranty that every user processes will be killed after the end of a job. If you want you can implement your own cpuset management. This is done by editing 3 files (see also `CPUSET installation`): - cpuset_manager.pl : this script creates the cpuset on each nodes and also delete it at the end of the job. For more informations, you have to look at this script (there are several comments). - oarsh : (OARSH) this script is used to replace the standard "ssh" command. It gets the cpuset name where it is running and transfer this information via "ssh" and the "SendEnv" option. In this file, you have to change the "get_current_cpuset" function. - oarsh_shell : (OARSH_SHELL) this script is the shell of the oar user on each nodes. It gets environment variables and look at if there is a cpuset name. So if there is one it assigns the current process and its father to this cpusetname. So all further user processes will remind in the cpuset. In this file you just have to change the "add_process_to_cpuset" function. SSH connection key definition ----------------------------- This function is performed by oarsub_ with the --ssh_private_key and --ssh_public_key options. It enables the user to define a ssh key pair to connect on their nodes. So oarsh can be used on nodes of different clusters to connect each others if the same ssh keys are used with each oarsub_. So a grid reservation ("-r" option of oarsub_ on each OAR batch scheduler of each wanted clusters) can be done with this functionality. Example:: ssh-keygen -f oar_key oarsub --ssh_private_key "$(cat oar_key)" --ssh_public_key "$(cat oar_key.pub)" ./script.sh Suspend/resume -------------- Jobs can be suspended with the command oarhold_ (send a "SIGSTOP" on every processes on every nodes) to allow other jobs to be executed. "Suspended" jobs can be resumed with the command oarresume_ (send a "SIGSTOP" on every suspended processes on every nodes). They will pass into "Running" when assigned resources will be free. IMPORTANT: This feature is available only if CPUSET_ is configured. You can specify 2 scripts if you have to perform any actions just after (JUST_AFTER_SUSPEND_EXEC_FILE) suspend and just before resume (JUST_BEFORE_RESUME_EXEC_FILE). Moreover you can perform other actions (than send signals to processes) if you want: just edit the "suspend_resume_manager.pl" file. Job deletion ------------ Leon tries to connect to OAR Perl script running on the first job node (find it thanks to the file */tmp/oar/pid_of_oarexec_for_jobId_id*) and sends a "SIGTERM" signal. Then the script catch it and normally end the job (kill processes that it has launched). If this method didn't succeed then Leon will flush the OAR database for the job and nodes will be "Suspected" by NodeChangeState. If your job is check pointed and is of the type *idempotent* (oarsub_ "-t" option) and its exit code is equal to 99 then another job is automatically created and scheduled with same behaviours. Checkpoint ---------- The checkpoint is just a signal sent to the program specified with the oarsub_ command. If the user uses "--checkpoint" option then Sarko will ask the OAR Perl script running on the first node to send the signal to the process (SIGUSR2 or the one specified with "--signal"). You can also use oardel_ command to send the signal. Scheduling ---------- General steps used to schedule a job: 1. All previous scheduled jobs are stored in a Gantt data structure. 2. All resources that match property constraints of the job("-p" option and indication in the "{...}" from the "-l" option of the oarsub_) are stored in a tree data structure according to the hierarchy given with the "-l" option. 3. Then this tree is given to the Gantt library to find the first hole where the job can be launched. 4. The scheduler stores its decision into the database in the gantt_jobs_predictions and gantt_jobs_resources tables. See User section from the FAQ for more examples and features. Job dependencies ---------------- A job dependency is a situation where a job needs the ending of another job to start. OAR deals with job dependency problems by refusing to schedule dependant jobs if their required job is in Terminated state and have an exit code != 0 (an error occured). If the required job is resubmited, its jobId is no longer the same and OAR updates the database and sets the job_id_required field to this new jobId for the dependant job. User notification ----------------- This section explains how the "--notify" oarsub_ option is handled by OAR: - The user wants to receive an email: The syntax is "mail:name@domain.com". Mail section in the `Configuration file` must be present otherwise the mail cannot be sent. The subject of the mail is of the form: \*OAR\* [*TAG*]: job_id (job_name) on OAR_server_hostname - The user wants to launch a script: The syntax is "exec:/path/to/script args". OAR server will connect (using OPENSSH_CMD) on the node where the oarsub_ command was invoked and then launches the script with the following arguments : *job_id*, *job_name*, *TAG*, *comments*. *TAG* can be: - RUNNING : when the job is launched - END : when the job is finished normally - ERROR : when the job is finished abnormally - INFO : used when oardel is called on the job - SUSPENDED : when the job is suspended - RESUMING : when the job is resumed Accounting aggregator --------------------- In the `Configuration file` you can set the ACCOUNTING_WINDOW parameter. Thus the command oaraccounting will split the time with this amount and feed the table accounting. So this is very easily and faster to get usage statistics of the cluster. We can see that like a "data warehousing" information extraction method. Dynamic nodes coupling features ------------------------------- We are working with the `Icatis `_ company on clusters composed by Intranet computers. These nodes can be switch in computing mode only at specific times. So we have implemented a functionality that can request to power on some hardware if they can be in the cluster. We are using the field *available_upto* from the table resources to know when a node will be inaccessible in the cluster mode (easily settable with oarnodesetting command). So when the OAR scheduler wants some potential available computers to launch the jobs then it executes the command SCHEDULER_NODE_MANAGER_WAKE_UP_CMD. Moreover if a node didn't execute a job for SCHEDULER_NODE_MANAGER_IDLE_TIME seconds and no job is scheduled on it before SCHEDULER_NODE_MANAGER_SLEEP_TIME seconds then OAR will launch the command SCHEDULER_NODE_MANAGER_SLEEP_CMD. Timesharing ----------- It is possible to share the slot time of a job with other ones. To perform this feature you have to specify the type *timesharing* when you use oarsub_. You have 4 different ways to share your slot: 1. *timesharing=\*,\** : This is the default behavior if nothing but timesharing is specified. It indicates that the job can be shared with all users and every job names. 2. *timesharing=user,\** : This indicates that the job can be shared only with the same user and every job names. 3. *timesharing=\*,job_name* : This indicates that the job can be shared with all users but only one with the same name. 4. *timesharing=user,job_name* : This indicates that the job can be shared only with the same user and one with the same job name. See User section from the FAQ for more examples and features. Container jobs -------------- With this functionality it is possible to execute jobs within another one. So it is like a sub-scheduling mechanism. First a job of the type *container* must be submitted, for example:: oarsub -I -t container -l nodes=10,walltime=2:10:00 ... OAR_JOB_ID=42 ... Then it is possible to use the *inner* type to schedule the new jobs within the previously created container job:: oarsub -I -t inner=42 -l nodes=7 oarsub -I -t inner=42 -l nodes=1 oarsub -I -t inner=42 -l nodes=10 Notes: - In the case: :: oarsub -I -t inner=42 -l nodes=11 This job will never be scheduled because the container job "42" reserved only 10 nodes. - "-t container" is handled by every kind of jobs (passive, interactive and reservations). But "-t inner=..." cannot be used with a reservation. Besteffort jobs --------------- Besteffort jobs are scheduled in the besteffort queue. Their particularity is that they are deleted if another not besteffort job wants resources where they are running. For example you can use this feature to maximize the use of your cluster with multiparametric jobs. This what it is done by the `CIGRI `_ project. When you submit a job you have to use "-t besteffort" option of oarsub_ to specify that this is a besteffort job. Important : a besteffort job cannot be a reservation. If your job is of the type *besteffort* and *idempotent* (oarsub_ "-t" option) and killed by the OAR scheduler then another job is automatically created and scheduled with same behaviours. Cosystem jobs ------------- This feature enables to reserve some resources without launching any program on corresponding nodes. Thus nothing is done by OAR on computing nodes when a job is starting except on the COSYSTEM_HOSTNAME defined in the configuration file. This is useful with an other launching system that will declare its time slot in OAR. So yo can have two different batch scheduler. When you submit a job you have to use "-t cosystem" option of oarsub_ to specify that this is a cosystem job. These jobs are stopped by the oardel_ command or when they reach their walltime or their command has finished. They also use the node COSYSTEM_HOSTNAME to launch the specified program or shell. Deploy jobs ----------- This feature is useful when you want to enable the users to reinstall their reserved nodes. So the OAR jobs will not log on the first computer of the reservation but on the DEPLOY_HOSTNAME. So prologue and epilogue scripts are executed on DEPLOY_HOSTNAME and if the user wants to launch a script it is also executed on DEPLOY_HOSTNAME. OAR does nothing on computing nodes because they normally will be rebooted to install a new system image. This feature is strongly used in the `Grid5000 `_ project with `Kadeploy `_ tools. When you submit a job you have to use "-t deploy" option of oarsub_ to specify that this is a deploy job. Desktop computing ----------------- If you cannot contact the computers via SSH you can install the "desktop computing" OAR mode. This kind of installation is based on two programs: - oar-cgi : this is a web CGI used by the nodes to communicate with the OAR server via a HTTP server on the OAR server node. - oar-agent.pl : This program asks periodically the server web CGI to know what it has to do. This method replaces the SSH command. Computers which want to register them into OAR just has to be able to contact OAR HTTP server. In this situation we don't have a NFS file system to share the same directories over all nodes so we have to use a stagein/stageout solution. In this case you can use the oarsub_ option "stagein" to migrate your data. ./oar-2.5.2/docs/documentation/doc_internal_mechanisms.rst0000644000175000017500000000041411757171206021763 0ustar plbplbInternal mechanisms =================== Job execution ------------- .. figure:: ../schemas/job_execution.png -------------------------------------------------------------------------------- Scheduling ---------- .. figure:: ../schemas/scheduling.png ./oar-2.5.2/docs/documentation/doc_header.rst0000644000175000017500000000075111757171206017174 0ustar plbplb.. image:: ../schemas/oar_logo.png :alt: OAR logo :align: center :width: 8cm :Authors: Capit Nicolas, Emeras Joseph :Address: Laboratoire d'Informatique de Grenoble Bat. ENSIMAG - antenne de Montbonnot ZIRST 51, avenue Jean Kuntzmann 38330 MONTBONNOT SAINT MARTIN :Contact: nicolas.capit@imag.fr, joseph.emeras@imag.fr :Authors: LIG laboratory :Organization: LIG laboratory :Status: Stable :Copyright: licenced under the GNU GENERAL PUBLIC LICENSE ./oar-2.5.2/docs/documentation/doc_abstract.rst0000644000175000017500000000033111757171206017541 0ustar plbplb:Abstract: OAR is a resource manager (or batch scheduler) for large clusters. By it's functionnalities, it's near of PBS, LSF, CCS and Condor. It's suitable for productive plateforms and research experiments. ./oar-2.5.2/docs/documentation/desktop-computing/0000755000175000017500000000000011757171206020036 5ustar plbplb./oar-2.5.2/docs/documentation/desktop-computing/desktop-computing.ps0000644000175000017500000023366511757171206024075 0ustar plbplb%!PS-Adobe-2.0 %%Creator: dvips(k) 5.92b Copyright 2002 Radical Eye Software %%Title: desktop-computing.dvi %%Pages: 5 %%PageOrder: Ascend %%BoundingBox: 0 0 596 842 %%DocumentFonts: CMSY10 %%DocumentPaperSizes: a4 %%EndComments %DVIPSWebPage: (www.radicaleye.com) %DVIPSCommandLine: dvips -t a4 -o desktop-computing.ps %+ desktop-computing.dvi %DVIPSParameters: dpi=600, compressed %DVIPSSource: TeX output 2004.11.15:1355 %%BeginProcSet: texc.pro %! /TeXDict 300 dict def TeXDict begin/N{def}def/B{bind def}N/S{exch}N/X{S N}B/A{dup}B/TR{translate}N/isls false N/vsize 11 72 mul N/hsize 8.5 72 mul N/landplus90{false}def/@rigin{isls{[0 landplus90{1 -1}{-1 1}ifelse 0 0 0]concat}if 72 Resolution div 72 VResolution div neg scale isls{ landplus90{VResolution 72 div vsize mul 0 exch}{Resolution -72 div hsize mul 0}ifelse TR}if Resolution VResolution vsize -72 div 1 add mul TR[ matrix currentmatrix{A A round sub abs 0.00001 lt{round}if}forall round exch round exch]setmatrix}N/@landscape{/isls true N}B/@manualfeed{ statusdict/manualfeed true put}B/@copies{/#copies X}B/FMat[1 0 0 -1 0 0] N/FBB[0 0 0 0]N/nn 0 N/IEn 0 N/ctr 0 N/df-tail{/nn 8 dict N nn begin /FontType 3 N/FontMatrix fntrx N/FontBBox FBB N string/base X array /BitMaps X/BuildChar{CharBuilder}N/Encoding IEn N end A{/foo setfont}2 array copy cvx N load 0 nn put/ctr 0 N[}B/sf 0 N/df{/sf 1 N/fntrx FMat N df-tail}B/dfs{div/sf X/fntrx[sf 0 0 sf neg 0 0]N df-tail}B/E{pop nn A definefont setfont}B/Cw{Cd A length 5 sub get}B/Ch{Cd A length 4 sub get }B/Cx{128 Cd A length 3 sub get sub}B/Cy{Cd A length 2 sub get 127 sub} B/Cdx{Cd A length 1 sub get}B/Ci{Cd A type/stringtype ne{ctr get/ctr ctr 1 add N}if}B/id 0 N/rw 0 N/rc 0 N/gp 0 N/cp 0 N/G 0 N/CharBuilder{save 3 1 roll S A/base get 2 index get S/BitMaps get S get/Cd X pop/ctr 0 N Cdx 0 Cx Cy Ch sub Cx Cw add Cy setcachedevice Cw Ch true[1 0 0 -1 -.1 Cx sub Cy .1 sub]/id Ci N/rw Cw 7 add 8 idiv string N/rc 0 N/gp 0 N/cp 0 N{ rc 0 ne{rc 1 sub/rc X rw}{G}ifelse}imagemask restore}B/G{{id gp get/gp gp 1 add N A 18 mod S 18 idiv pl S get exec}loop}B/adv{cp add/cp X}B /chg{rw cp id gp 4 index getinterval putinterval A gp add/gp X adv}B/nd{ /cp 0 N rw exit}B/lsh{rw cp 2 copy get A 0 eq{pop 1}{A 255 eq{pop 254}{ A A add 255 and S 1 and or}ifelse}ifelse put 1 adv}B/rsh{rw cp 2 copy get A 0 eq{pop 128}{A 255 eq{pop 127}{A 2 idiv S 128 and or}ifelse} ifelse put 1 adv}B/clr{rw cp 2 index string putinterval adv}B/set{rw cp fillstr 0 4 index getinterval putinterval adv}B/fillstr 18 string 0 1 17 {2 copy 255 put pop}for N/pl[{adv 1 chg}{adv 1 chg nd}{1 add chg}{1 add chg nd}{adv lsh}{adv lsh nd}{adv rsh}{adv rsh nd}{1 add adv}{/rc X nd}{ 1 add set}{1 add clr}{adv 2 chg}{adv 2 chg nd}{pop nd}]A{bind pop} forall N/D{/cc X A type/stringtype ne{]}if nn/base get cc ctr put nn /BitMaps get S ctr S sf 1 ne{A A length 1 sub A 2 index S get sf div put }if put/ctr ctr 1 add N}B/I{cc 1 add D}B/bop{userdict/bop-hook known{ bop-hook}if/SI save N @rigin 0 0 moveto/V matrix currentmatrix A 1 get A mul exch 0 get A mul add .99 lt{/QV}{/RV}ifelse load def pop pop}N/eop{ SI restore userdict/eop-hook known{eop-hook}if showpage}N/@start{ userdict/start-hook known{start-hook}if pop/VResolution X/Resolution X 1000 div/DVImag X/IEn 256 array N 2 string 0 1 255{IEn S A 360 add 36 4 index cvrs cvn put}for pop 65781.76 div/vsize X 65781.76 div/hsize X}N /p{show}N/RMat[1 0 0 -1 0 0]N/BDot 260 string N/Rx 0 N/Ry 0 N/V{}B/RV/v{ /Ry X/Rx X V}B statusdict begin/product where{pop false[(Display)(NeXT) (LaserWriter 16/600)]{A length product length le{A length product exch 0 exch getinterval eq{pop true exit}if}{pop}ifelse}forall}{false}ifelse end{{gsave TR -.1 .1 TR 1 1 scale Rx Ry false RMat{BDot}imagemask grestore}}{{gsave TR -.1 .1 TR Rx Ry scale 1 1 false RMat{BDot} imagemask grestore}}ifelse B/QV{gsave newpath transform round exch round exch itransform moveto Rx 0 rlineto 0 Ry neg rlineto Rx neg 0 rlineto fill grestore}B/a{moveto}B/delta 0 N/tail{A/delta X 0 rmoveto}B/M{S p delta add tail}B/b{S p tail}B/c{-4 M}B/d{-3 M}B/e{-2 M}B/f{-1 M}B/g{0 M} B/h{1 M}B/i{2 M}B/j{3 M}B/k{4 M}B/w{0 rmoveto}B/l{p -4 w}B/m{p -3 w}B/n{ p -2 w}B/o{p -1 w}B/q{p 1 w}B/r{p 2 w}B/s{p 3 w}B/t{p 4 w}B/x{0 S rmoveto}B/y{3 2 roll p a}B/bos{/SS save N}B/eos{SS restore}B end %%EndProcSet %%BeginProcSet: bbad153f.enc % Thomas Esser, Dec 2002. public domain % % Encoding for: % cmsy10 cmsy5 cmsy6 cmsy7 cmsy8 cmsy9 % /TeXbbad153fEncoding [ /minus /periodcentered /multiply /asteriskmath /divide /diamondmath /plusminus /minusplus /circleplus /circleminus /circlemultiply /circledivide /circledot /circlecopyrt /openbullet /bullet /equivasymptotic /equivalence /reflexsubset /reflexsuperset /lessequal /greaterequal /precedesequal /followsequal /similar /approxequal /propersubset /propersuperset /lessmuch /greatermuch /precedes /follows /arrowleft /arrowright /arrowup /arrowdown /arrowboth /arrownortheast /arrowsoutheast /similarequal /arrowdblleft /arrowdblright /arrowdblup /arrowdbldown /arrowdblboth /arrownorthwest /arrowsouthwest /proportional /prime /infinity /element /owner /triangle /triangleinv /negationslash /mapsto /universal /existential /logicalnot /emptyset /Rfractur /Ifractur /latticetop /perpendicular /aleph /A /B /C /D /E /F /G /H /I /J /K /L /M /N /O /P /Q /R /S /T /U /V /W /X /Y /Z /union /intersection /unionmulti /logicaland /logicalor /turnstileleft /turnstileright /floorleft /floorright /ceilingleft /ceilingright /braceleft /braceright /angbracketleft /angbracketright /bar /bardbl /arrowbothv /arrowdblbothv /backslash /wreathproduct /radical /coproduct /nabla /integral /unionsq /intersectionsq /subsetsqequal /supersetsqequal /section /dagger /daggerdbl /paragraph /club /diamond /heart /spade /arrowleft /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /minus /periodcentered /multiply /asteriskmath /divide /diamondmath /plusminus /minusplus /circleplus /circleminus /.notdef /.notdef /circlemultiply /circledivide /circledot /circlecopyrt /openbullet /bullet /equivasymptotic /equivalence /reflexsubset /reflexsuperset /lessequal /greaterequal /precedesequal /followsequal /similar /approxequal /propersubset /propersuperset /lessmuch /greatermuch /precedes /follows /arrowleft /spade /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef ] def %%EndProcSet %%BeginProcSet: texps.pro %! TeXDict begin/rf{findfont dup length 1 add dict begin{1 index/FID ne 2 index/UniqueID ne and{def}{pop pop}ifelse}forall[1 index 0 6 -1 roll exec 0 exch 5 -1 roll VResolution Resolution div mul neg 0 0]FontType 0 ne{/Metrics exch def dict begin Encoding{exch dup type/integertype ne{ pop pop 1 sub dup 0 le{pop}{[}ifelse}{FontMatrix 0 get div Metrics 0 get div def}ifelse}forall Metrics/Metrics currentdict end def}{{1 index type /nametype eq{exit}if exch pop}loop}ifelse[2 index currentdict end definefont 3 -1 roll makefont/setfont cvx]cvx def}def/ObliqueSlant{dup sin S cos div neg}B/SlantFont{4 index mul add}def/ExtendFont{3 -1 roll mul exch}def/ReEncodeFont{CharStrings rcheck{/Encoding false def dup[ exch{dup CharStrings exch known not{pop/.notdef/Encoding true def}if} forall Encoding{]exch pop}{cleartomark}ifelse}if/Encoding exch def}def end %%EndProcSet %%BeginFont: CMSY10 %!PS-AdobeFont-1.1: CMSY10 1.0 %%CreationDate: 1991 Aug 15 07:20:57 % Copyright (C) 1997 American Mathematical Society. All Rights Reserved. 11 dict begin /FontInfo 7 dict dup begin /version (1.0) readonly def /Notice (Copyright (C) 1997 American Mathematical Society. All Rights Reserved) readonly def /FullName (CMSY10) readonly def /FamilyName (Computer Modern) readonly def /Weight (Medium) readonly def /ItalicAngle -14.035 def /isFixedPitch false def end readonly def /FontName /CMSY10 def /PaintType 0 def /FontType 1 def /FontMatrix [0.001 0 0 0.001 0 0] readonly def /Encoding 256 array 0 1 255 {1 index exch /.notdef put} for dup 0 /.notdef put readonly def /FontBBox{-29 -960 1116 775}readonly def /UniqueID 5000820 def currentdict end currentfile eexec D9D66F633B846A97B686A97E45A3D0AA052F09F9C8ADE9D907C058B87E9B6964 7D53359E51216774A4EAA1E2B58EC3176BD1184A633B951372B4198D4E8C5EF4 A213ACB58AA0A658908035BF2ED8531779838A960DFE2B27EA49C37156989C85 E21B3ABF72E39A89232CD9F4237FC80C9E64E8425AA3BEF7DED60B122A52922A 221A37D9A807DD01161779DDE7D31FF2B87F97C73D63EECDDA4C49501773468A 27D1663E0B62F461F6E40A5D6676D1D12B51E641C1D4E8E2771864FC104F8CBF 5B78EC1D88228725F1C453A678F58A7E1B7BD7CA700717D288EB8DA1F57C4F09 0ABF1D42C5DDD0C384C7E22F8F8047BE1D4C1CC8E33368FB1AC82B4E96146730 DE3302B2E6B819CB6AE455B1AF3187FFE8071AA57EF8A6616B9CB7941D44EC7A 71A7BB3DF755178D7D2E4BB69859EFA4BBC30BD6BB1531133FD4D9438FF99F09 4ECC068A324D75B5F696B8688EEB2F17E5ED34CCD6D047A4E3806D000C199D7C 515DB70A8D4F6146FE068DC1E5DE8BC5703711DA090312BA3FC00A08C453C609 C627A8BECD6E1FA14A3B02476E90AAD8B4700C400380BC9AFFBF7847EB28661B 9DC3AA0F44C533F2E07DCC4DE19D367BF223E33DC321D0247A0E6EF6ABC8FA52 15AE044094EF678A8726CD7C011F02BFF8AB6EAEEE391AD837120823BED0B5D8 F8B15245377871A64F78378BB4330149D6941F7A86FBFFC49B93C94155F5FA7D F22E7214511C0A92693F4CDBF38411651540572F2DD70D924AE0F18E1CD581F3 C871399127FF5D07A868885B5FF7CDEB50B8323B2533DEF8DC973B1AE84FA0A2 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 cleartomark %%EndFont TeXDict begin 39158280 55380996 1000 600 600 (desktop-computing.dvi) @start /Fa 240[42 15[{ TeXbbad153fEncoding ReEncodeFont }1 83.022 /CMSY10 rf %DVIPSBitmapFont: Fb ecbx1000 10 41 /Fb 41 234 df<913803FFC0027F13F00103B512FC010FEB00FED93FF8133FD97FE0EBFF 8049485A5A1480484A13C04A6C1380A36F1300167E93C7FCA592383FFFC0B8FCA4000390 C7FCB3ABB5D8FC3F13FFA4303A7EB935>28 D<141C143C14F8EB01F0EB03E01307EB0FC0 EB1F8014005B137E13FE5B12015B1203A2485AA2120F5B121FA25B123FA4485AA512FFB1 127FA56C7EA4121F7FA2120F7F1207A26C7EA212017F12007F137E7F7F1480EB0FC0EB07 E01303EB01F0EB00F8143C141C165377BD25>40 D<12E07E127C7E7E7F6C7E6C7E12037F 6C7E7F12007F137E137FA2EB3F80A214C0131F14E0A2130F14F0A4EB07F8A514FCB114F8 A5EB0FF0A414E0131FA214C0133F1480A2EB7F00A2137E13FE5B12015B485A5B1207485A 485A90C7FC123E5A12F05A16537BBD25>I45 D<141E143E14FE1307137FB5FCA3138FEA000FB3B3A5007FB61280A4213679B530>49 DIII<001C15C0D81F80130701F8137F90B612 80A216005D5D15F05D15804AC7FC14F090C9FCA7EB03FE90381FFFE0017F13F89038FE07 FC9038F003FFD9C0011380496C13C090C7FC000E15E0C8127F16F0A216F8A3121FEA3FC0 487E12FF7FA316F05B15FFD87F8014E0007EC713C0003E5B003F4913806C6C481300390F F01FFE6CB512F8000114E06C6C1380D90FF8C7FC25377BB530>II<123C123EEA3FE090B71280A41700485D5E5E5E5EA2007CC7 EA0FC000784A5A4BC7FC00F8147E485C5D14014A5AC7485A4A5AA24A5A143F4AC8FCA214 FEA213015C1303A21307A2130F5CA2131FA5133FA96D5A6D5A6D5A29397BB730>I65 D67 D73 D 79 D82 DI85 D97 D<13FFB5FCA412077EAF4AB47E020F13F0023F13FC9138FE03FFDAF000 13804AEB7FC00280EB3FE091C713F0EE1FF8A217FC160FA217FEAA17FCA3EE1FF8A217F0 6E133F6EEB7FE06E14C0903AFDF001FF80903AF8FC07FE009039F03FFFF8D9E00F13E0D9 C00390C7FC2F3A7EB935>I<903801FFC0010F13FC017F13FFD9FF8013802603FE0013C0 48485AEA0FF8121F13F0123F6E13804848EB7F00151C92C7FC12FFA9127FA27F123FED01 E06C7E15036C6CEB07C06C6C14806C6C131FC69038C07E006DB45A010F13F00101138023 257DA42A>I I<903803FF80011F13F0017F13FC3901FF83FE3A03FE007F804848133F484814C0001FEC 1FE05B003FEC0FF0A2485A16F8150712FFA290B6FCA301E0C8FCA4127FA36C7E1678121F 6C6C14F86D14F000071403D801FFEB0FE06C9038C07FC06DB51200010F13FC010113E025 257DA42C>II<161FD907FEEBFFC090387FFFE348B6EAEFE02607FE07138F260FF801131F48486C13 8F003F15CF4990387FC7C0EEC000007F81A6003F5DA26D13FF001F5D6C6C4890C7FC3907 FE07FE48B512F86D13E0261E07FEC8FC90CAFCA2123E123F7F6C7E90B512F8EDFF8016E0 6C15F86C816C815A001F81393FC0000F48C8138048157F5A163FA36C157F6C16006D5C6C 6C495AD81FF0EB07FCD807FEEB3FF00001B612C06C6C91C7FC010713F02B377DA530>I< 13FFB5FCA412077EAFED7FC0913803FFF8020F13FE91381F03FFDA3C01138014784A7E4A 14C05CA25CA291C7FCB3A3B5D8FC3F13FFA4303A7DB935>II<141FEC 7FC0ECFFE0A24913F0A56D13E0A2EC7FC0EC1F0091C7FCA9EC0FF0EB0FFFA4EB007F143F B3B0121FEA3F80EA7FC0EAFFE0EC7FE0A215C014FF6C481380903883FE006CB45A000F13 F0000113801C4B86BA1D>I<13FFB5FCA412077EB3B3ACB512FCA4163A7DB91B>108 D<01FED97FE0EB0FFC00FF902601FFFC90383FFF80020701FF90B512E0DA1F81903983F0 3FF0DA3C00903887801F000749DACF007F00034914DE6D48D97FFC6D7E4A5CA24A5CA291 C75BB3A3B5D8FC1FB50083B512F0A44C257DA451>I<01FEEB7FC000FF903803FFF8020F 13FE91381F03FFDA3C011380000713780003497E6D4814C05CA25CA291C7FCB3A3B5D8FC 3F13FFA430257DA435>I<903801FFC0010F13F8017F13FFD9FF807F3A03FE003FE04848 6D7E48486D7E48486D7EA2003F81491303007F81A300FF1680A9007F1600A3003F5D6D13 07001F5DA26C6C495A6C6C495A6C6C495A6C6C6CB45A6C6CB5C7FC011F13FC010113C029 257DA430>I<9039FF01FF80B5000F13F0023F13FC9138FE07FFDAF00113800003496C13 C00280EB7FE091C713F0EE3FF8A2EE1FFCA3EE0FFEAA17FC161FA217F8163F17F06E137F 6E14E06EEBFFC0DAF00313809139FC07FE0091383FFFF8020F13E0020390C7FC91C9FCAC B512FCA42F357EA435>I<9038FE03F000FFEB0FFEEC3FFF91387C7F809138F8FFC00007 5B6C6C5A5CA29138807F80ED3F00150C92C7FC91C8FCB3A2B512FEA422257EA427>114 D<90383FF0383903FFFEF8000F13FF381FC00F383F0003007E1301007C130012FC15787E 7E6D130013FCEBFFE06C13FCECFF806C14C06C14F06C14F81203C614FC131F9038007FFE 140700F0130114007E157E7E157C6C14FC6C14F8EB80019038F007F090B512C000F81400 38E01FF81F257DA426>I<130FA55BA45BA25B5BA25A1207001FEBFFE0B6FCA3000390C7 FCB21578A815F86CEB80F014816CEBC3E090383FFFC06D1380903803FE001D357EB425> I<01FFEC3FC0B5EB3FFFA4000714016C80B3A35DA25DA26C5C6E4813E06CD9C03E13FF90 387FFFFC011F13F00103138030257DA435>II120 D201 D233 D E %EndDVIPSBitmapFont %DVIPSBitmapFont: Fc ecbx1440 14.4 41 /Fc 41 234 df28 D<151E153E15FCEC01F8EC03F0EC07E0140FEC1FC0EC3F80EC7F005C5C 495A1303495AA2495A131F5C133F5C137F5C13FFA24890C7FCA25A5B1207A25B120FA348 5AA4123FA25BA2127FA65B12FFB3A3127F7FA6123FA27FA2121FA46C7EA312077FA21203 7F7EA26C7FA2137F80133F80131F80130F6D7EA26D7E13016D7E8080EC3F80EC1FC0EC0F E01407EC03F0EC01F8EC00FC153E151E1F7974D933>40 D<12F07E127E7E6C7E6C7E7F6C 7E6C7E6C7E7F1200137F806D7EA26D7E80130F80130780130380A26D7EA215807F15C0A2 147F15E0A3EC3FF0A415F8A2141FA215FCA6140F15FEB3A315FC141FA615F8A2143FA215 F0A4EC7FE0A315C014FFA215805B1500A2495AA25C13075C130F5C131F5C495AA2495A91 C7FC13FE12015B485A485A485A5B485A48C8FC127E12F85A1F7979D933>I45 D<151E153E15FE1403140F147FEB07FF0003B5FCB6FCA3EBF87FEAFC00C7 FCB3B3B3A6007FB712FCA52E4E76CD42>49 DI< 913807FFC0027F13FC0103B67E010F15E090261FF80313F890267FC0007F01FEC7EA3FFE 48488148486E138013FE486C6C6D13C0804817E080A66C5B18C06C5B6C90C75AD8003816 8090C8FC4C1300A24C5A5F4C5A4B5B4B13C0030F5BDB7FFEC7FC91387FFFF816C016FCEE FF80DA000313E09238007FF8EE3FFE707E70138018C07013E018F07013F8A218FC82A218 FEA3EA03C0EA0FF0EA3FFC487EA2B5FCA218FCA25E18F8A26C4816F0495C4916E0D83FE0 4A13C06C485CD80FF04A1380D807FE91387FFE003B03FFE003FFFC6C90B65A6C6C15E001 0F92C7FC010114FCD9001F1380374F7BCD42>I<17FC1601A216031607160FA2161F163F 167FA216FF5D5DA25D5D5D167F153E157E15FC15F8EC01F01403EC07E015C0EC0F80141F EC3F00143E5C14FC495A5C495A1307495A5C49C7FC5B137E137C5B1201485A5B485A120F 485A90C8FC123E127E5ABA1280A5C901FCC7FCAF021FB71280A5394F7CCE42>I<486C15 0601F0153E01FEEC01FED9FFF0133F91B65A5F5F5F5F5F94C7FC16FC5E16E093C8FC15FC 01F0138091CAFCAC913807FF80023F13F891B512FE01F36E7E9026FFFC0113E09139E000 7FF891C76C7E496E7E01F86E7E5B7013804916C0C9FC18E08218F0A418F8A31203EA0FE0 EA3FF8487EA212FF7FA218F0A25B5E6C4816E05B01C016C06CC85A18806C6C4A13007FD8 0FF04A5A6C6CECFFFCD803FE4913F02701FFE00F5B6C6CB612806D92C7FC010F14F80101 14C09026003FFCC8FC354F7ACD42>II<121F7F7FEBFF8091B8FCA4 5A18FE18FC18F818F0A218E018C018804817000180C8123E007EC9127E5F007C4B5A4C5A 5F16074C5A484B5A4CC7FC167E167CC912FC4B5A4B5AA24B5A150F4B5AA24B5AA24BC8FC 5DA25C5D1403A214075D140FA3141FA2143FA34A5AA414FFA65BAB6D5B6E5A6E5A6E5A38 5279D042>I<173FA24D7EA34D7EA24C7FA34C7FA24C7FA34C7FA24C7FA34C7F163E8304 7E80EE7C3F04FC8016F8830301814C7E03038116E0830307814C7E030F81168083031F81 1600834B81033E80037E82157C8403FC824B800201835D840203834B800207835D92B8FC 4A83A34A8392C9FC4A83143E85027E84027C8202FC845C850101854A820103855C850107 854A82A2494884D93FF082B600F0020FB712C0A55A547CD363>65 D<932603FFF01407047F01FF140F0307B600E0131F033F03F8133F92B700FE137F020391 26C003FF13FF020F01F8C7EA3FC1023F01C0EC0FE391B5C80003B5FC4901FC8149498149 01E082011F498249498292CA7E4948834948835A4A83485B4885A24849187FA2485B1B3F A2485B1B1FA25AA21B0091CDFCA2B5FCAE7EA280A36C1A1FA36C7FA21B3F6C7F1B3E6C7F 1B7E6C6D187C6C1AFC6E18F86C19016D6CEF03F06D7E6FEE07E06D6DEE0FC001076DEE1F 806D01F8EE3F006D6D16FE6D01FF4B5A023F01C0EC07F8020F01FCEC3FF00203903AFFC0 01FFC0020091B6C7FC033F15FC030715F0DB007F1480040301F0C8FC505479D25F>67 D73 D<93381FFF800303B512FC033FECFFC092B712F00207D9F80113FE021F903A80001FFF80 4A48C700077FDAFFF8020113F049496E7F4901C0ED3FFC49496F7E4990C96C7E4948707F 013F854948707F4948707F48864A8248864A177F48864849717EA3481B804A83481BC0A4 4890CB6C13E0A5B51AF0AF6C1BE06E5FA46C1BC0A26E5F6C1B80A36C6D4D1300A26C6D4D 5AA26C626C6D4C5B6E5E6C626D6C4C5B6E5E6D6D4B5B6D6D4B5B6D6D4B90C7FC6D6D4B5A 01016D4A13F86D01FE02075B91263FFFC0013F13C06ED9F801B55A020790B648C8FC0201 16F8DA003F15C003074AC9FCDB001F1380545479D263>79 D82 DI85 D97 DI<913803FFE0023F13FE91B67E010315 E0010F9038003FF8D93FFCEB07FC4948497E4948131F4849497E485B485BA24890C7FC5A 5B003F6F5A705A705A007F92C8FC5BA312FFAD127F7FA3123F7F6CEE0F80A26C6D141F18 006C6D5C6C6D143E6C6D147E6C6D5C6D6C495A6DB4EB07F0010F9038C01FE06D90B51280 01014AC7FCD9003F13F80203138031387CB63A>I<943803FF80040FB5FCA5EE003F170F B3A4913803FF80023F13F849B512FE0107ECFF8F011F9038C03FEF90273FFE0007B5FCD9 7FF8130149487F484980484980484980488291C8FC5A5B123FA2127F5BA312FFAD127FA3 7F123FA3121F7F6C5E6C6D5C5F6C6D91B5FC6C6D5B6C6D4914E0D97FFCD90FEFEBFF80D9 1FFFEB7F8F010790B5120F010114FC6D6C13E00207010049C7FC41547CD249>I<913807 FF80027F13F849B512FE01076E7E011F010313E0903A3FFC007FF0D97FF06D7E49486D7E 4849130F48496D7E48824890C77E1880485A82003F17C0A3485A18E082A212FFA290B8FC A401FCCAFCA6127FA37F123FA2EF03E06C7E17076C17C06C6D140F18806C6D141F6C6DEC 3F006C6D147ED97FFC495AD91FFFEB07F86D9038E03FF0010390B512C001005D023F01FC C7FC020113E033387CB63C>IIII<133FEBFFC0487F487FA2487FA66C5BA26C5B6C5B013FC7FC90C8FCAEEB1F F8B5FCA512017EB3B3A6B612F0A51C547CD324>I<153FEDFFC04A13E04A13F0A24A13F8 A66E13F0A26E13E06E13C0ED3F0092C7FCAEED3FF849B5FCA5EB00031400B3B3B1EA0780 EA1FE0487E487E486C14F05CA216E0A24A13C0D87FF814804A13006C48485A001FEB3FFC 6CB512F06C5C000191C7FC38003FF8256C87D328>I108 DII<913801FFC0 023F13FE91B67E010315E0010F018013F8903A3FFC001FFED97FF0EB07FF49486D7F4849 6D7F48496D7F91C8127F4883488349153F001F83A2003F8349151FA2007F83A400FF1880 AC007F1800A3003F5F6D153FA2001F5FA26C6C4B5AA26C6D4A5A6C5F6C6D495B6C6D495B 6D6C4990C7FCD93FFCEB1FFE6DB46CB45A010790B512F0010115C0D9003F49C8FC020313 E039387CB642>II<90393FF001FCB590380FFF804B13E0037F13F09238FE1FF89138F1F83F000191 38F07FFC6CEBF3E015C0ECF780A2ECFF00EE3FF84AEB1FF0EE0FE093C7FC5CA45CB3ABB6 12FEA52E367DB535>114 D<903903FFC00E011FEBFC1E90B6127E000315FE3907FE003F D80FF0130F4848130348481301491300127F90C8127EA248153EA27FA27F01F091C7FC13 FCEBFF806C13FEECFFF06C14FE6F7E6C15E06C816C15FC6C81C681133F010F15801301D9 000F14C0EC003F030713E0150100F880167F6C153FA2161F7EA217C07E6D143F17807F6D EC7F0001F85C6DEB03FE9039FF801FFC486CB512F0D8F81F14C0D8F00791C7FC39E0007F F02B387CB634>I<147CA614FCA41301A31303A21307A2130F131F133F137F13FF120300 0F90B512FEB7FCA426007FFCC8FCB3A9EE0F80ABEE1F006D7EA2011F143E806D6D5A6DEB C1F86DEBFFF001005C023F1380DA03FEC7FC294D7ECB33>II< B600E090381FFFFCA5000101F8C7000113006CEE007C6E15FC017F5E8017016D6C5D1703 6D5E6F13076D5E6F130FA26D6D5C171F6D93C7FC6F5B6D153E6F137E6D157C8117FC027F 5CEDFE01023F5CEDFF036E5C168316876E5C16CF6E5C16FF6E91C8FCA36E5BA26E5BA26F 5AA36F5AA26F5AA26F5AA23E367DB445>I120 D<183FF0FF804D7F05077F5F173F4D7F4CB5FC4C5C160F5E047F5C96C8FC7013E005FCC9 FC70CAFC93CBFCA6BB12FEA5D8000F01E0C700077FF0007F191F190785858586A2F23F80 A21A1FA31A0FA4DD01F014C01A07A497C7FCA21703A31707170F171F17FF92B6FCA5EDE0 00171F170F17071703A317011BF8A3F201F0A394C8FCA21A03A21BE0A21A07A31A0F1BC0 1A1F1A3FA21A7F1AFF4F13801907191F197F060FB5FCBCFCA21B00A34D687CE756>201 D233 D E %EndDVIPSBitmapFont %DVIPSBitmapFont: Fd ecrm1000 10 81 /Fd 81 250 df<486C1360000314E039070001C0000EEB038048EB070000181306003813 0E0030130C0070131C00601318A200E01338481330A400CEEB338039FF803FE001C013F0 A3007F131FA2393F800FE0390E0003801C1981B91C>16 D<001C1307007FEB1FC039FF80 3FE0A201C013F0A3007F131F001CEB073000001300A400011470491360A2000314E090C7 12C048130100061480000E130348EB070048130E485B006013181C1980B91C>I27 DI30 D<007C137C00FE13FEEAFF01A3EAFE00A7007E13FC007C137CA8003C137800381338A700 181330171E77BA2A>34 D<121C127FEAFF80A213C0A3127F121C1200A412011380A21203 13005A1206120E5A5A5A12600A1979B917>39 D<146014E0EB01C0EB0380EB0700130E13 1E5B5BA25B485AA2485AA212075B120F90C7FCA25A121EA2123EA35AA65AB2127CA67EA3 121EA2121F7EA27F12077F1203A26C7EA26C7E1378A27F7F130E7FEB0380EB01C0EB00E0 1460135278BD20>I<12C07E12707E7E7E120F6C7E6C7EA26C7E6C7EA21378A2137C133C 133E131EA2131F7FA21480A3EB07C0A6EB03E0B2EB07C0A6EB0F80A31400A25B131EA213 3E133C137C1378A25BA2485A485AA2485A48C7FC120E5A5A5A5A5A13527CBD20>I<121C 127FEAFF80A213C0A3127F121C1200A412011380A2120313005A1206120E5A5A5A12600A 19798817>44 DI<121C127FEAFF80A5EA7F00121C0909798817> I<1506A2150E150CA2151C151815381530A215701560A215E015C0A214011580A2140315 005C1406A2140E140CA2141C1418A214381430A21470146014E05CA213015CA2130391C7 FCA25B1306A2130E130C131C1318A213381330A213701360A213E05BA212015B120390C8 FCA25A1206A2120E120CA2121C1218A21238123012701260A212E05AA21F537BBD2A>I< EB01C013031307131F13FFB5FCA2131F1200B3B3A7497E007FB512F0A31C3779B62A>49 DII<1538A2157815F8A214011403 1407A2140F141F141B14331473146314C313011483EB030313071306130C131C13181330 1370136013C01201EA038013005A120E120C5A123812305A12E0B712F8A3C73803F800AA 4A7E0103B512F8A325387EB72A>I<0006140CD80780133C9038F003F890B5FC5D5D1580 92C7FC14FC38067FE090C9FCAAEB07F8EB1FFE9038780F809038E007E03907C003F0496C 7E130000066D7E81C8FC8181A21680A4121C127F5A7FA390C713005D12FC00605C12704A 5A6C5C6C1303001E495A6C6C485A3907E03F800001B5C7FC38007FFCEB1FE021397CB62A >I<121C127FEAFF80A5EA7F00121CC7FCB2121C127FEAFF80A5EA7F00121C092479A317> 58 D<007FB812F8B912FCCCFCB0B912FC6C17F836147B9E41>61 D<12E01278121EEA07C0EA01F0EA003C130FEB03C0EB00F0143C140FEC03E0EC00F8151E ED0780ED01E0ED0078161EEE07C0EE01F0EE003C170FEF03C0A2EF0F00173CEE01F0EE07 C0041EC7FC1678ED01E0ED0780031EC8FC15F8EC03E0020FC9FC143C14F0EB03C0010FCA FC133CEA01F0EA07C0001ECBFC127812E0322E79AB41>III<1538A3157CA315FE A34A7EA34A6C7EA202077FEC063FA2020E7FEC0C1FA2021C7FEC180FA202387FEC3007A2 02707FEC6003A202C07F1501A2D901807F81A249C77F167FA20106810107B6FCA2498101 0CC7121FA2496E7EA3496E7EA3496E7EA213E0707E1201486C81D80FFC02071380B56C90 B512FEA3373C7DBB3E>I<913A01FF800180020FEBE003027F13F8903A01FF807E07903A 03FC000F0FD90FF0EB039F4948EB01DFD93F80EB00FF49C8127F01FE153F12014848151F 4848150FA248481507A2485A1703123F5B007F1601A35B00FF93C7FCAD127F6DED0180A3 123F7F001F160318006C7E5F6C7E17066C6C150E6C6C5D00001618017F15386D6C5CD91F E05C6D6CEB03C0D903FCEB0F80902701FF803FC7FC9039007FFFFC020F13F00201138031 3D7BBA3C>67 DIIIIII76 DIIIIIII<003FB812E0A3D9C003EB001F273E00 01FE130348EE01F00078160000701770A300601730A400E01738481718A4C71600B3B091 3807FF80011FB612E0A335397DB83C>IIII<007FB590383FFFFCA3C601F8 01071380D97FE0D903FCC7FC013FEC01F06D6C5C5F6D6C5C6D6C13034CC8FC6D6C130616 0E6D6C5B6DEB8018163891387FC0306E6C5A16E06E6C5A91380FF18015FB6EB4C9FC5D14 036E7EA26E7F6F7EA24B7E15DF9138019FF09138038FF8150F91380607FC91380E03FE14 0C4A6C7EEC38000230804A6D7E14E04A6D7E49486D7E130391C76C7E01066E7E130E010C 6E7E011C1401013C8101FE822607FF80010713E0B500E0013FEBFF80A339397EB83E>I< B500FE91383FFFE0A3000301E0913807FE00C649EC03F0017F6F5A606D6C5D6D6C140395 C7FC6D6C1406A26D6C5C6D6C141C17186D6C143817306D6D5B6E6C13E05F91383FE0015F 91381FF003DA0FF890C8FC1606913807FC0E160C913803FE1C913801FF185E6E13B016E0 157F6F5AB3A24B7E023FB512C0A33B397FB83E>I91 D93 D<007FB81280B912C0A26C17803204797041>95 DIIIIII<147E903803FF8090380FC1E0EB1F8790383F0F F0137EA213FCA23901F803C091C7FCADB512FCA3D801F8C7FCB3AB487E387FFFF8A31C3B 7FBA19>IIIIIII<2703F00FF0EB1FE000FFD93FFCEB7FF8913AF03F01E07E903B F1C01F83803F3D0FF3800FC7001F802603F70013CE01FE14DC49D907F8EB0FC0A2495CA3 495CB3A3486C496CEB1FE0B500C1B50083B5FCA340257EA445>I<3903F00FF000FFEB3F FCECF03F9039F1C01F803A0FF3800FC03803F70013FE496D7EA25BA35BB3A3486C497EB5 00C1B51280A329257EA42E>II<3903F01FE000FFEB7FF89038F1E07E 9039F3801F803A07F7000FC0D803FEEB07E049EB03F04914F849130116FC150016FEA316 7FAA16FEA3ED01FCA26DEB03F816F06D13076DEB0FE001F614C09039F7803F009038F1E0 7E9038F0FFF8EC1FC091C8FCAB487EB512C0A328357EA42E>II<3807E01F00FFEB7F C09038E1E3E09038E387F0380FE707EA03E613EE9038EC03E09038FC0080491300A45BB3 A2487EB512F0A31C257EA421>II<1318A51338A31378A313F8120112031207001FB5FCB6FCA2D801 F8C7FCB215C0A93800FC011580EB7C03017E13006D5AEB0FFEEB01F81A347FB220>IIIIII<1318133E137F80806D7EEB0FF0EB03F86D7EEB007E141F1407140191C8FCA3EB 1FE0EBFFFC3803E03F3907000F80390F8007E0486C6C7E13E06E7EA26E7E6C5A6C5AC8FC A4147FEB07FFEB3FE0EBFE00EA03F8EA0FF0EA1FC0123F485A90C7FC160C12FEA31401A2 6C13036CEB077C903980063E18383FC01E3A0FE0781FF03A03FFF00FE03A007F8007C026 377DB52A>224 D<1303497E497E497EEB3CF0EB7878497EEBE01C48487E48487E48486C 7E000E6D7E00046D5ACAFCA2EB1FE0EBFFFC3803E03F3907000F80390F8007E0486C6C7E 13E06E7EA26E7E6C5A6C5AC8FCA4147FEB07FFEB3FE0EBFE00EA03F8EA0FF0EA1FC0123F 485A90C7FC160C12FEA31401A26C13036CEB077C903980063E18383FC01E3A0FE0781FF0 3A03FFF00FE03A007F8007C026367DB42A>226 D231 D<1306130F497E80806D7E6D7EEB01FCEB00 7E141FEC0F80140314001500A3EB07F8EB1FFF90387C0FC03901F803E03903F001F0D807 E013F8380FC0004848137CA248C7127E153E5A153F127E12FEA3B7FCA248C8FCA5127EA2 127FA26C14037F001F14076C6C13060007140E6D131CD801F013386C6C137090387E03E0 90381FFF80903803FC0020377EB525>I<1418143C147E14FE1301495A495AEB0FE0EB1F 80013EC7FC5B1370134090C8FCA3EB07F8EB1FFF90387C0FC03901F803E03903F001F0D8 07E013F8380FC0004848137CA248C7127E153E5A153F127E12FEA3B7FCA248C8FCA5127E A2127FA26C14037F001F14076C6C13060007140E6D131CD801F013386C6C137090387E03 E090381FFF80903803FC0020377EB525>I<497E497E497E497EEB1E78497EEB381C497E 497E3901C0038039038001C048C712E000021440C9FCA2EB07F8EB1FFF90387C0FC03901 F803E03903F001F0D807E013F8380FC0004848137CA248C7127E153E5A153F127E12FEA3 B7FCA248C8FCA5127EA2127FA26C14037F001F14076C6C13060007140E6D131CD801F013 386C6C137090387E03E090381FFF80903803FC0020367EB425>I<1303497E497E80806D 7E6D7EEB00FE143FEC0F806E7E1401EC004092C8FCA4D803F0EB07E000FFEB01FFA3000F EB001F00031407B3A4150FA3151F12016D133F0000EC77F86D9038E7FF8090383F03C790 381FFF87903A03FC07E00029377EB52E>249 D E %EndDVIPSBitmapFont %DVIPSBitmapFont: Fe ecrm1200 12 23 /Fe 23 122 df<121EEA7F80A2EAFFC0A4EA7F80A2EA1E000A0A78891B>46 D<14FF010713E090381F81F890383E007C01FC133F4848EB1F8049130F4848EB07C04848 EB03E0A2000F15F0491301001F15F8A2003F15FCA390C8FC4815FEA54815FFB3A46C15FE A56D1301003F15FCA3001F15F8A26C6CEB03F0A36C6CEB07E0000315C06D130F6C6CEB1F 806C6CEB3F00013E137C90381F81F8903807FFE0010090C7FC28447CC131>48 D50 D52 D<14FF010713E0011F13F890387F80FC9038FC007E48487F4848EB1F804848EB0FC0000F EC07E0485AED03F0485A16F8007F140190C713FCA25AA216FE1500A516FFA46C5CA36C7E 5D121F7F000F5C6C6C1306150E6C6C5B6C6C5BD8007C5B90383F01E090390FFF80FE9038 01FE0090C8FC150116FCA4ED03F8A216F0D80F801307486C14E0486C130F16C0ED1F80A2 49EB3F0049137E001EC75A001C495A000F495A3907E01FE06CB51280C649C7FCEB1FF028 447CC131>57 D<1960F001E0F00780F03E0018F8EF03C0050FC7FC173CEE01F0EE07C004 1EC8FC1678ED01E0ED0F80033EC9FC15F0EC03C0020FCAFC147CEB01F0EB0780011ECBFC 1378EA03E0EA0F80003CCCFC12F0A2123CEA0F80EA03E0EA0078131EEB0780EB01F0EB00 7C140FEC03C0EC00F0153EED0F80ED01E0ED0078161EEE07C0EE01F0EE003C170FEF03C0 EF00F8183EF00780F001E0F000603B3678B34C>60 D<12C012F0123CEA0F80EA03E0EA00 78131EEB0780EB01F0EB007C140FEC03C0EC00F0153EED0F80ED01E0ED0078161EEE07C0 EE01F0EE003C170FEF03C0EF00F8183EF00780F001E0A2F00780F03E0018F8EF03C0050F C7FC173CEE01F0EE07C0041EC8FC1678ED01E0ED0F80033EC9FC15F0EC03C0020FCAFC14 7CEB01F0EB0780011ECBFC1378EA03E0EA0F80003CCCFC12F012C03B3678B34C>62 D64 D 78 D 80 D97 DII101 D105 D109 D<3901FC01FE00FF903807FFC091381E07F091383801F800 0701707F0003EBE0002601FDC07F5C01FF147F91C7FCA25BA35BB3A8486CECFF80B5D8F8 3F13FEA32F2C7DAB36>II<3901FC03FC00 FF90380FFF8091383C07E091387001F83A07FDE000FE00010180137F01FFEC3F8091C7EA 1FC04915E049140F17F0160717F8160317FCA3EE01FEABEE03FCA3EE07F8A217F0160F6D 15E0EE1FC06D143F17806EEB7E00D9FDC05B9039FCF003F891383C0FE091381FFF80DA03 FCC7FC91C9FCAE487EB512F8A32F3F7DAB36>I<3903F803F000FFEB1FFCEC3C3EEC707F 0007EBE0FF3803F9C000015B13FBEC007E153C01FF13005BA45BB3A748B4FCB512FEA320 2C7DAB26>114 D<90383FE0183901FFFC383907E01F78390F0003F8001E130148130000 7C1478127800F81438A21518A27EA27E6C6C13006C7E13FC383FFFE06C13FC6C13FF6C14 C06C14E0C614F0011F13F81300EC0FFC140300C0EB01FE1400157E7E153EA27EA36C143C 6C147C15786C14F86CEB01F039F38003E039F1F00F8039E07FFE0038C00FF01F2E7DAC26 >I<1306A5130EA4131EA3133E137EA213FE12011207001FB512F0B6FCA2C648C7FCB3A4 150CAA017E131C017F1318A26D133890381F8030ECC070903807E0E0903801FFC0903800 7F001E3E7EBC26>I121 D E %EndDVIPSBitmapFont %DVIPSBitmapFont: Ff ecrm1728 17.28 21 /Ff 21 118 df45 D<170FA34D7EA24D7EA34D7EA34D7EA34C7F 17DFA29338039FFC178FA29338070FFE1707040F7FEE0E03A2041E80EE1C01A2043C80EE 3800A24C80187FA24C80183FA24B4880181F0303814C130FA203078193C71207A24B8103 0E80A24B8284A24B8284A24B82197F03F0824B153FA20201834B151FA202038392B8FCA2 4A83A292C91207020E8385A24A8485023C84023882A20278840270177FA202F0844A173F A24948841A1FA24948841A0FA249CB7F1A074985865B496C85497E48486C4D7F000F01F8 051F13F0B60407B612F0A45C657CE465>65 D67 DI77 D<933801FFE0043F13FF4BB612E003079038003FF8DB1FF0EB03FEDB7FC0903800FF804A 48C8EA3FE0DA03FCED0FF0DA0FF0ED03FC4A486F7E4A486F7E4A48707E4ACA6C7E494871 7E4948717E4948717E4948717E4948717E013F854A83017F864948727EA24890CC6C7EA2 4848737EA24848737EA2000F87491907001F87A34848737EA4007F1C80A24985A400FF1C C0AF6C6C4F1380A5003F1C006D61A3001F63A26D190F000F63A26C6C4F5AA36C6C4F5AA2 6C6D4E5A6C636E18FF017F626D6C4D90C7FC6E5F011F616D6C4D5A6D6C4D5A0103616E17 1F6D6C4D5A6D6D4C5ADA3FC04CC8FCDA1FF0ED03FE6E6C4B5A6E6C4B5ADA01FFED3FE091 26007FC0ECFF80DB1FF0D903FEC9FCDB07FFEB3FF8030190B512E0DB003F91CAFC040113 E05A6679E369>79 D82 D<181EEF3FFEEE07FFA4EE000F1703A21701B3AAEDFF80020F13F8023F13FE9139FF803F 81903A03FC0007C14948EB01E1D91FE0EB00F94948147D4948143D49C8121F4848150F49 1507120348481503491501120F121F5BA2123F5B127FA45B12FFAD127F7FA3123FA27F12 1FA26C6C1503A26C6C150712036D150F6C6C151F0000163D137F6D6CECF9FF6D6CEB01F1 D90FF0D903C113C06D6CD90F81EBFF80D901FFEB7F019039007FFFFC021F13E002010100 91C7FC41657BE34B>100 DI<187FDA07FC903803FFC091273FFF800F13E091B539E01F83F0903B03FC 07F87C07903A07F001FCF0903A0FC0007FC04948EB3F80013FEE03E049C76C6CC7FC01FE 6E7EA248486E7EA2000382A2491403000782AA00035E6D1407A200015EA26C6C4A5AA201 7F4A5A6D6C495A011F92C8FC496C137E903937F001FC903973FC07F80160B512E0D9E03F 1380DA07FCC9FC4848CBFCA57FA37F7F7F6CB4FC91B512FC6DECFFE017FC6D15FF6D16C0 6D16F0013F8201FEC700037FD803F89138001FFED807E015074848ED01FF484881003FEF 7F8090CA123F127EF01FC012FE48170FA66C171F007E1880A2007F173F6C6CEE7F00001F 177E6C6C5E6D1501D803F8ED07F06C6C4B5A6CB4ED3FC0D93FE049B4C7FCD90FFEEB1FFC 0103B612F0D9007F1480020301F0C8FC3C5E7CBF43>103 D<1378EA01FE487E487FA66C 90C7FC6C5AEA007890C8FCB3A2EB0780EA0FFFB5FCA41203C6FCA2137FB3B3AC497E487F B61280A4195F7BDE25>105 D107 D III<4AB47E 020F13F0027F13FE4AC67ED903F8EB1FC0D907E0EB07E0D91FC0EB03F849486D7E49C87E 01FE157F49814848ED1F80000317C04848ED0FE0A24848ED07F0A2001F17F8491503003F 17FCA3007F17FE491501A400FF17FFAC007F17FEA26D1503A3003F17FCA2001F17F86D15 07A2000F17F06D150F000717E06C6CED1FC0A26C6CED3F806C6CED7F00017F15FE6D6C49 5A6D6C495A6D6C495AD903F8EB1FC06DB4EBFF806D6CB448C7FC020F13F0020113803841 7BBF43>II<010FEB07F8D80FFFEB1FFEB590387FFF809238F81FC0913801E03F913903C07FE000 03EB0780C6EB0F00140E6D5A0218EB3FC00238EB1F800230EB0600027090C7FCA2146014 E0A25CA55CB3B0497E4813F0B612F8A42B3F7BBE34>114 D<9138FFC003010FEBF80701 7FEBFE0F3A01FF003F9FD803F0EB07DF48486DB4FCD80F801300001F8148C8FC003E8100 7E81127C00FC81A4827EA27E7F6C7E6D91C7FC13F8EA3FFE381FFFE06C13FF15F0000314 FE6C6E7E6C6C14E0011F14F801078001008002077FDA003F13801507030113C0ED007F00 E0ED3FE0161F17F06C150F1607A36C1503A37EA26C16E016077E17C06D140F6D15806D14 1FD8FDF0EC3F00D8F8F8147E017C495A3AF01F801FF06DB512C0D8E00391C7FC39C0007F F02C417CBF35>I<1470A714F0A51301A31303A21307A2130FA2131F133F137F13FF1203 000F90B6FCB8FCA326000FF0C8FCB3AEEE01C0AE6D6CEB0380A316076D6C14005E6D6C13 0E6D6C131E6E6C5A91383FE0F86EB45A020713C0020090C7FC2A597ED734>II E %EndDVIPSBitmapFont end %%EndProlog %%BeginSetup %%Feature: *Resolution 600dpi TeXDict begin %%BeginPaperSize: a4 a4 %%EndPaperSize end %%EndSetup %%Page: 1 1 TeXDict begin 1 0 bop 707 872 a Ff(Mo)t(dule)45 b(O)l(AR)g(p)t(our)g (le)h(Desktop-Computing)1026 1112 y Fe(Pierre)33 b(Neyron)g ()1610 1307 y(29)f(o)s(ctobre)g(2004)639 1614 y Fd(Ce)24 b(papier)f(pr\351sen)n(te)f(le)i(tra)n(v)-5 b(ail)22 b(r\351alis\351)h(sur)g(O)n(AR)g(dans)g(le)h(con)n(text)f(du)h (con)n(trat)e(A)n(CI)515 1714 y(CIGRI-Icatis.)515 1989 y Fc(Con)l(ten)l(ts)515 2171 y Fb(1)76 b(Ob)5 b(jectifs)2316 b(1)515 2354 y(2)76 b(Sous)31 b(ob)5 b(jectifs)32 b(\(cahier)h(des)e(c) m(harges\))1254 b(1)515 2536 y(3)76 b(Arc)m(hitecture)2166 b(2)515 2719 y(4)76 b(In)m(t\351gration)32 b(dans)g(le)f(fonctionnemen) m(t)f(de)i(O)m(AR)g(pr\351-existan)m(t)324 b(3)515 2902 y(5)76 b(Comp)s(osan)m(ts)30 b(et)i(mo)s(di\034cation)d(de)j(O)m(AR)f (n\351cessaires)665 b(3)515 3084 y(6)76 b(Utilisation)2252 b(4)515 3267 y(7)76 b(\311v)m(olutions)31 b(en)m(visag\351es)1791 b(5)515 3541 y Fc(1)131 b(Ob)7 b(jectifs)515 3723 y Fd(D\351v)n(elopp)r (emen)n(t)20 b(d'un)g(clien)n(t)g(de)h(calcul)e(O)n(AR)h(l\351ger,)h(p) r(ermettan)n(t)f(d'utiliser)g(O)n(AR)g(dans)515 3823 y(un)37 b(en)n(vironnemen)n(t)f(non)h(cluster,)j(c'est)d(\340)g(dire)g (une)g(grille)f(ou)h(in)n(ternet)h(de)f(mani\350re)515 3922 y(g\351n\351rale.)515 4197 y Fc(2)131 b(Sous)43 b(ob)7 b(jectifs)45 b(\(cahier)g(des)f(c)l(harges\))515 4379 y Fd(Le)27 b(clien)n(t)h(l\351ger)e(de)i(calcul)f(doit:)639 4561 y Fa(\017)41 b Fd(in)n(ter-op)r(\351rer)26 b(a)n(v)n(ec)g(les)h (\034rew)n(alls)g(\(passer)f(les)h(\034rew)n(alls\))639 4728 y Fa(\017)41 b Fd(ne)28 b(pas)f(\352tre)g(d\351p)r(endan)n(t)h(de) g(NFS)g(\(en)n(vironnemen)n(t)f(non)g(cluster\))639 4894 y Fa(\017)41 b Fd(\352tre)f(su\036sammen)n(t)f(dynamique)h(p)r(our)f (fonctionner)h(dans)f(un)h(en)n(vironnemen)n(t)722 4993 y(p)r(eu)28 b(stable)g(comme)f(celui)h(du)f(desktop-computing)g(\(mo)r (de)h(pull\))1926 5255 y(1)p eop end %%Page: 2 2 TeXDict begin 2 1 bop 639 523 a Fa(\017)41 b Fd(\352tre)18 b(su\036sammen)n(t)g(propre)f(p)r(our)h(ne)h(pas)f(p)r(erturb)r(er)g (l'en)n(vironnemen)n(t)f(d'ex\351cution)722 623 y(de)27 b(la)f(mac)n(hine)g(de)g(calcul,)g(celle-ci)g(ne)h(p)r(ouv)-5 b(an)n(t)26 b(a)g(priori)f(pas)h(\352tre)g(netto)n(y)n(\351e)f(\340)h (la)722 722 y(main)i(par)f(un)g(administrateur)g(a)n(v)n(erti,)f(comme) h(un)h(no)r(eud)g(de)g(cluster.)639 888 y Fa(\017)41 b Fd(\352tre)28 b(l\351ger)e(\340)h(d\351plo)n(y)n(er)f(sur)h(les)h (mac)n(hines)e(de)i(calcul)515 1163 y Fc(3)131 b(Arc)l(hitecture)515 1345 y Fd(A\034n)32 b(de)h(passer)d(les)i(\034rew)n(alls,)g(le)g(clien) n(t)g(l\351ger)f(base)g(ses)h(comm)n(unications)f(r\351seau)g(sur)515 1444 y(HTTP)-7 b(.)31 b(En)h(e\033et)f(la)g(plupart)f(des)h(\034rew)n (alls)e(autorise)h(d'une)h(fa\347on)f(directe)h(ou)f(via)h(un)515 1544 y(pro)n(xy)d(HTTP)-7 b(,)30 b(le)g(transit)f(de)g(messages)f(HTTP) -7 b(.)31 b(Le)e(mo)r(dule)h(desktop-computing)e(est)515 1644 y(donc)j(comp)r(os\351)g(de)h(deux)g(\351l\351men)n(ts,)h(un)f (clien)n(t)g(\(agen)n(t\))f(dialoguan)n(t)f(en)i(HTTP)h(et)f(un)515 1743 y(in)n(terpr\350te)e(traduisan)n(t)f(les)h(messages)f(HTTP)j(en)e (donn\351es)g(O)n(AR)g(et)h(r\351cipro)r(quemen)n(t.)515 1843 y(Seul)21 b(le)g(deuxi\350me)h(\351l\351men)n(t)f(est)g(en)h(con)n (tact)e(a)n(v)n(ec)g(les)h(autres)f(mo)r(dules)h(O)n(AR,)g(Almigh)n(t)n (y)-7 b(,)515 1942 y(base)34 b(de)h(donn\351es,)h(etc.)58 b(P)n(our)34 b(encapsuler)g(dans)g(des)h(messages)e(HTTP)i(les)g (donn\351es)515 2042 y(\351c)n(hang\351es)19 b(en)n(tre)h(l'agen)n(t)f (et)i(l'in)n(terpr\350te,)h(une)e(m\351tho)r(de)h(de)g (s\351rialisation)e(a)h(\351t\351)g(mise)h(en)515 2142 y(place.)36 b(L'\351tude)26 b(de)g(proto)r(coles)e(comme)i(SO)n(AP)g (\(w)n(eb)g(services\))f(a)h(\351t\351)g(en)n(visag\351e,)f(mais)515 2241 y(un)e(m\351canisme)g(plus)g(simple)h(a)f(\351t\351)g (pr\351f\351r\351)g(\(Il)g(est)h(bas\351)e(sur)h(le)g(mo)r(dule)h (Data::Dump)r(er)515 2341 y(de)j(P)n(erl\).)639 2441 y(L'absence)35 b(de)g(syst\350me)g(de)g(\034c)n(hiers)f(partag\351)g (implique)h(quan)n(t)g(\340)g(elle)g(la)g(mise)g(en)515 2540 y(place)j(de)h(m\351canisme)f(de)h(stage-in)e(et)i(de)g (stage-out.)69 b(Ceux-ci)38 b(son)n(t)h(r\351alis\351s)e(gr\342ce)515 2640 y(aux)h(p)r(ossibilit\351s)h(o\033ertes)f(par)g(HTTP)h(p)r(our)f (le)h(do)n(wnload)f(et)h(l'upload)f(de)h(\034c)n(hiers.)515 2739 y(Des)32 b(m\351canismes)g(de)h(cac)n(hes)e(on)n(t)h(\351galemen)n (t)f(\351t\351)i(mis)g(en)f(place.)51 b(Un)33 b(m\351canisme)f(de)515 2839 y(s\351lection)22 b(en)g(fonction)h(de)f(la)g(bande)h(passan)n(te) e(v)n(ers)g(les)h(no)r(euds)h(de)f(calcul)g(l\351gers,)h(et)f(des)515 2939 y(tailles)h(des)g(donn\351es)f(\340)h(\351c)n(hanger)e(p)r (ourrait)h(\352tre)h(en)n(visag\351.)34 b(Les)23 b(comm)n(unications)f (en)n(tre)515 3038 y(l'agen)n(t)29 b(et)h(O)n(AR)f(son)n(t)g(toujours)g (\340)g(l'initiativ)n(e)h(de)g(l'agen)n(t.)42 b(Cela)29 b(p)r(ermet)h(d'en)n(visager)515 3138 y(sereinemen)n(t)h(un)h(con)n (texte)f(ou)g(des)g(clien)n(ts)h(p)r(euv)n(en)n(t)f(\352tre)h(n'imp)r (orte)f(o\371,)i(quitte)f(\340)f(ne)515 3238 y(pas)d(\352tre)h (accessibles)e(directemen)n(t)i(depuis)g(le)g(serv)n(eur)e(O)n(AR)h (\(Ex:)41 b(mac)n(hines)28 b(derri\350re)515 3337 y(un)35 b(\034rew)n(all)e(a)n(v)n(ec)g(translation)g(d'IP)-7 b(,)36 b(mac)n(hines)e(sur)g(un)h(in)n(tranet)f(d'en)n(treprise)f (ferm\351)515 3437 y(a)n(v)n(ec)26 b(pro)n(xy)g(HTTP\).)639 3536 y(Des)c(m\351canismes)f(d'ex\351cution)g(de)h(st)n(yle)f(sand-b)r (o)n(x)f(n'on)n(t)h(pas)g(pu)h(\352tre)f(utilis\351s)g(p)r(our)515 3636 y(ce)31 b(clien)n(t)h(l\351ger)f(\(aucun)h(m\351canisme)f (satisfaisan)n(t)f(et)i(su\036sammen)n(t)g(l\351ger)e(de)i(ce)g(t)n(yp) r(e)515 3736 y(ne)37 b(sem)n(ble)f(exister\).)65 b(Les)37 b(jobs)f(son)n(t)h(donc)g(simplemen)n(t)g(ex\351cut\351s)f(en)i(temps)f (qu'un)515 3835 y(utilisateur)d(normal)g(sur)g(la)h(mac)n(hine)f (\(celui-ci)h(p)r(eut)g(cep)r(endan)n(t)g(\352tre)f(un)h(utilisateur) 515 3935 y(d\351di\351\).)66 b(Il)38 b(est)f(tr\350s)g(di\036cile)g(de) h(garan)n(tir)d(qu'aucun)i(job)g(ex\351cut\351)g(sur)g(le)h(no)r(eud)f (de)515 4035 y(calcul)29 b(ne)h(p)r(erturb)r(era)f(pas)h(un)g(jour)g (le)g(fonctionnemen)n(t)g(g\351n\351ral)e(de)i(la)g(mac)n(hine.)44 b(Un)515 4134 y(minim)n(um)d(de)g(garan)n(tie)e(est)i(o\033ert)g(par)f (l'utilisation)h(d'un)g(utilisateur)g(d\351di\351,)j(don)n(t)515 4234 y(l'\351limination)25 b(de)g(tous)g(les)f(pro)r(cessus)g (supprimera)g(tout)h(d\351sagr\351men)n(t)e(constat\351.)36 b(P)n(our)515 4333 y(ce)27 b(netto)n(y)n(age,)f(des)h(m\351canismes)g (plus)h(\034ns)g(on)n(t)f(\351t\351)h(et)f(son)n(t)g(toujours)g(\340)g (\351tudier.)639 4433 y(Cot\351)f(d\351plo)n(y)n(emen)n(t,)g(l'agen)n (t)g(est)g(un)h(script)f(P)n(erl)g(ind\351p)r(endan)n(t,)h (n\351cessitan)n(t)f(seule-)515 4533 y(men)n(t)i(la)f(pr\351sence)g(de) h(quelques)f(comp)r(osan)n(ts)f(comme)i(la)f(commande)g(`tar')g(sur)g (le)h(sys-)515 4632 y(t\350me)d(de)f(la)h(mac)n(hine)f(de)g(calcul.)36 b(Le)24 b(syst\350me)h(cible)f(est)h(GNU)g(Lin)n(ux,)g(un)g(p)r(ortage) e(v)n(ers)515 4732 y(MS)j(Windo)n(ws/Cygwin)f(p)r(ourrait)g (certainemen)n(t)g(\352tre)h(en)n(visag\351,)e(mais)i(il)g (n\351cessiterait)515 4832 y(\351galemen)n(t)39 b(le)h(d\351v)n(elopp)r (emen)n(t)g(d'un)h(m\351canisme)e(de)h(s\351lection)g(suppl\351men)n (taire)f(au)515 4931 y(niv)n(eau)c(de)h(O)n(AR)g(p)r(our)g(le)g(c)n (hoix)f(du)h(syst\350me)g(d'ex\351cution)g(appropri\351)e(p)r(our)i(un) g(job)1926 5255 y(2)p eop end %%Page: 3 3 TeXDict begin 3 2 bop 515 523 a Fd(\(compatibilit\351)41 b(d'en)n(vironnemen)n(t)e(d'ex\351cution\).)76 b(Des)41 b(pac)n(k)-5 b(ages)39 b(RPM)i(et)g(Debian)515 623 y(son)n(t)27 b(disp)r(onibles)g(p)r(our)g(une)h(installation.)515 894 y Fc(4)131 b(In)l(t\351gration)72 b(dans)f(le)g(fonctionnemen)l(t)h (de)f(O)l(AR)712 1043 y(pr\351-existan)l(t)515 1225 y Fd(Un)22 b(des)g(ob)5 b(jectifs)22 b(sous-jasen)n(t)e(de)i(ce)g(d\351v) n(elopp)r(emen)n(t)f(a)g(\351t\351)h(de)g(ne)g(pas)g(trop)f (d\351naturer,)515 1324 y(c'est)h(\340)h(dire)f(n\351cessiter)g(de)h(c) n(hangemen)n(ts,)g(dans)f(le)h(fonctionnemen)n(t)g(de)g(O)n(AR)f(en)h (mo)r(de)515 1424 y(cluster,)35 b(tout)g(en)f(p)r(ermettan)n(t)h(un)g (fonctionnemen)n(t)f(en)g(mo)r(de)h(Desktop-Computing.)515 1524 y(Cela)27 b(a)g(conduit)h(\340)f(un)h(certain)f(nom)n(bre)f(de)i (c)n(hoix)f(et)h(de)f(limitations.)639 1623 y(Une)38 b(des)f(principales)f(cons\351quences)g(est)h(le)g(fait)g(que)g(con)n (trairemen)n(t)e(\340)i(des)g(sys-)515 1723 y(t\350mes)30 b(comme)g(Seti@Home,)h(c'est)f(toujours)g(le)g(syst\350me)g(O)n(AR)g (qui)g(attribue)g(les)g(jobs)515 1823 y(aux)g(no)r(euds,)i(et)g(ce)f (de)g(mani\350re)f(async)n(hrone)f(par)h(rapp)r(ort)g(aux)h (requ\352tes)f(des)h(agen)n(ts)515 1922 y(de)36 b(desktop-computing.)60 b(Un)36 b(agen)n(t)f(signale)g(donc)g(simplemen)n(t)i(sa)e(pr\351sence) g(et)h(ses)515 2022 y(p)r(ossibilit\351s)j(d'ex\351cution)g(\(\340)h (in)n(terv)-5 b(alle)38 b(r\351gulier\),)k(puis)d(dans)g(un)h (deuxi\350me)g(temps)515 2121 y(seulemen)n(t,)30 b(r\351cup)r(\350re)f (des)h(jobs)f(\340)g(ex\351cuter)h(si)f(l'ordonnanceur)f(de)i(O)n(AR)f (lui)h(en)g(a)g(at-)515 2221 y(tribu\351.)65 b(Un)37 b(mo)r(de)g(pull)g(plus)g(classique)f(corresp)r(ondrait)f(plus)i(\340)f (un)i(m\351canisme)e(de)515 2321 y(v)n(ol)24 b(de)h(tac)n(he.)36 b(Quand)24 b(l'agen)n(t)h(s'arr\352te)e(normalemen)n(t,)i(il)g(en)g (informe)g(l'infrastructure)515 2420 y(O)n(AR)37 b(qui)g(marque)f(le)h (no)r(eud)g(comme)g(absen)n(t.)65 b(Si)37 b(l'agen)n(t)f(est)i(in)n (terrompu,)g(il)f(sera)515 2520 y(marqu\351)23 b(absen)n(t)i(dans)f(O)n (AR)g(d\350s)h(que)f(sa)g(date)h(d'expiration)e(sera)h(d\351pass\351e,) g(l'agen)n(t)g(ne)515 2620 y(mettan)n(t)k(en)f(e\033et)h(plus)g (celle-ci)f(\340)g(jour.)639 2719 y(L'agen)n(t)g(a)g(d'autre)g(part)g (\351t\351)h(restrein)n(t)f(aux)g(limitations)g(suiv)-5 b(an)n(tes:)639 2881 y Fa(\017)41 b Fd(un)20 b(job)f ("Desktop-Computing")e(ne)i(p)r(eut)h(s'ex\351cuter)e(que)g(sur)h(une)g (seule)g(et)g(m\352me)722 2981 y(mac)n(hine.)76 b(S'il)42 b(requiert)d(un)j(p)r(oids)e(sup)r(\351rieur)g(\340)h(un,)j(il)d(p)r (ourra)f(uniquemen)n(t)722 3080 y(\352tre)35 b(ex\351cut\351)g(sur)g (des)g(mac)n(hines)f(o\033ran)n(t)g(un)i(p)r(oids)f(\351gal)f(ou)h(sup) r(\351rieur.)59 b(Sans)722 3180 y(parler)34 b(des)g(probl\350mes)g (li\351s)g(\340)h(la)f(disp)r(ersion)g(tr\350s)g(probables)f(des)i(mac) n(hines)f(et)722 3279 y(donc)22 b(de)h(leur)f(in)n(terconnexion)f(non)h (garan)n(tie,)f(p)r(ermettre)h(une)h(ex\351cution)f(de)g(jobs)722 3379 y(m)n(ulti-no)r(eud)g(demanderait)f(trop)h(de)g(mo)r (di\034cations)f(dans)g(l'infrastructure)g(O)n(AR)722 3479 y(actuelle)31 b(p)r(our)f(\352tre)h(supp)r(ort\351)f(\(cela)h (n\351cessiterait)e(notammen)n(t)i(l'a)5 b(jout)30 b(d'\351tats)722 3578 y(dans)d(l'automate,)g(et)h(de)g(mo)r(di\034er)f (l'ordonnanceur\).)639 3736 y Fa(\017)41 b Fd(du)27 b(fait)f(de)g(la)g (disp)r(ersion)f(p)r(ossible)g(des)h(mac)n(hines)f(de)h(calcul)g(et)g (de)g(la)g(limitation)722 3836 y(pr\351c\351den)n(te,)g(il)h(est)f (di\036cile)h(d'en)n(visager)d(des)i(jobs)g(fortemen)n(t)g (parall\350les)f(dans)h(le)722 3935 y(con)n(texte)33 b(du)g(desktop)f(computing.)52 b(L'usage)32 b(est)h(donc)f(essen)n (tiellemen)n(t)h(pr\351vu)722 4035 y(p)r(our)27 b(des)h(jobs)f (s\351quen)n(tiels,)g(a)n(v)n(ec)f(v)-5 b(ariation)27 b(des)g(param\350tres.)515 4306 y Fc(5)131 b(Comp)t(osan)l(ts)28 b(et)h(mo)t(di\034cation)g(de)g(O)l(AR)h(n\351cessaires)515 4488 y Fd(Comme)38 b(dit)h(pr\351c\351demmen)n(t,)i(le)d(mo)r(dule)h (de)g(desktop-computing)e(est)h(comp)r(os\351)g(de)515 4587 y(deux)27 b(comp)r(osan)n(ts:)639 4749 y Fa(\017)41 b Fd(oar-agen)n(t)25 b(qui)j(est)f(l'agen)n(t)g(lanc\351)g(sur)g(les)h (no)r(euds)f(de)h(calcul.)639 4907 y Fa(\017)41 b Fd(oar-cgi)25 b(qui)i(est)g(l'in)n(terpr\350te)f(qui)h(in)n(terface)f(le)h (syst\350me)g(O)n(AR)f(a)n(v)n(ec)g(le)g(proto)r(cole)722 5006 y(HTTP)j(\(CGI)f(P)n(erl\).)1926 5255 y(3)p eop end %%Page: 4 4 TeXDict begin 4 3 bop 515 523 a Fd(Ces)38 b(deux)h(comp)r(osan)n(ts)e (son)n(t)i(fournis)f(dans)g(deux)h(pac)n(k)-5 b(ages)37 b(\340)i(part)f(en)n(ti\350re)g(\(RPM)515 623 y(ou)c(Debian\).)58 b(Le)34 b(premier)g(pac)n(k)-5 b(age)33 b(\(oar-agen)n(t\),)h(nomm\351) g(oar-desktop-computing-)515 722 y(agen)n(t,)25 b(doit)h(\352tre)f (install\351)h(sur)f(les)h(no)r(euds)g(de)g(calcul.)36 b(Il)25 b(est)h(ind\351p)r(endan)n(t.)37 b(Le)26 b(second,)515 822 y(oar-desktop-computing-cgi)36 b(est)k(d\351p)r(endan)n(t)g(d'un)g (serv)n(eur)f(HTTP)h(et)g(du)h(pac)n(k)-5 b(age)515 922 y(oar-common.)55 b(Il)34 b(doit)h(\352tre)f(install\351)g(sur)g(une)g (mac)n(hine)g(du)h(cluster)f(O)n(AR)g(disp)r(osan)n(t)515 1021 y(d'un)k(serv)n(eur)e(HTTP)-7 b(.)39 b(Une)f(installation)f (simple)h(consiste)f(\340)h(installer)f(oar-desktop-)515 1121 y(computing-cgi)j(et)h(Apac)n(he)g(sur)g(la)f(m\352me)i(mac)n (hine)f(o\371)f(a)h(\351t\351)h(install\351)e(le)i(pac)n(k)-5 b(age)515 1220 y(oar-serv)n(er.)639 1320 y(M\352me)24 b(si)g(elles)f(on)n(t)g(\351t\351)h(le)g(moins)f(in)n(trusiv)n(es)f(p)r (ossible,)i(le)g(d\351v)n(elopp)r(emen)n(t)f(du)h(mo)r(d-)515 1420 y(ule)41 b(de)g(Desktop-Computing)f(a)h(n\351cessite)f(d'app)r (orter)g(quelques)h(mo)r(di\034cations)f(au)515 1519 y(syst\350me)27 b(O)n(AR.)g(En)i(v)n(oici)d(un)i(r\351sum\351:)639 1702 y Fa(\017)41 b Fd(Ajout)25 b(d'une)f(propri\351t\351)f ("DesktopComputing")g(sur)g(les)h(no)r(euds)g(p)r(our)f(iden)n (ti\034er)722 1802 y(les)i(no)r(euds)h(faisan)n(t)e(du)i (Desktop-Computing,)f(et)h(d'une)f(colonne)g("expiryDate")722 1901 y(dans)k(la)g(table)g(no)r(des)g(p)r(our)g(le)h(m\351canisme)f(de) g(d\351tection)g(de)h(la)f(disparition)f(des)722 2001 y(no)r(euds.)639 2167 y Fa(\017)41 b Fd(Ajout)35 b(de)f(la)g(table)g (\034les)g(et)g(d'une)h(colonne)e(dans)g(la)h(table)g(job)g(p)r(our)g (g\351rer)e(les)722 2267 y(\020stage-ins\021.)639 2433 y Fa(\017)41 b Fd(Ajout)36 b(d'un)h(certain)d(nom)n(bre)h(de)h (fonctions)f(dans)g(iolib.)61 b(Certaines)34 b(fonctions)722 2532 y(existan)n(tes)27 b(on)n(t)g(\351galemen)n(t)g(\351t\351)h(mo)r (di\034\351es,)f(mais)g(leur)g(nom)n(bre)g(a)g(\351t\351)h(limit\351.) 639 2698 y Fa(\017)41 b Fd(Mo)r(di\034cation)30 b(de)g(Leon,)g(Runner,) g(Sark)n(o,)f(oarno)r(desetting,)g(oarsub)g(p)r(our)g(qu'ils)722 2798 y(prennen)n(t)f(en)f(compte)h(le)f(desktop-computing.)515 3072 y Fc(6)131 b(Utilisation)515 3254 y Fd(Un)20 b(fois)g (install\351,)h(oar-agen)n(t)c(p)r(eut)j(\352tre)g(lanc\351)f(a)n(v)n (ec)f(comme)i(param\350tre)e(l'URL)i(p)r(oin)n(tan)n(t)515 3354 y(v)n(ers)30 b(\020oar-cgi\021.)45 b(A)n(tten)n(tion,)32 b(l'utilisateur)f(qui)g(lance)g(\020oar-agen)n(t\021)k(ex\351cutera)30 b(aussi)g(les)515 3453 y(jobs.)82 b(L'utilisation)43 b(d'un)g(utilisateur)f(d\351di\351)h(p)r(eut)h(p)r(ermettre)f(un)g (netto)n(y)n(age)e(plus)515 3553 y(rapide)24 b(en)g(cas)g(de)h (dysfonctionnemen)n(t.)35 b(Des)25 b(options)f(p)r(ermetten)n(t)h(de)f (pr\351ciser)g(le)g(nom)515 3653 y(du)30 b(no)r(eud)g(\(si)g(ce)g (n'est)g(pas)f(le)h(nom)g(donn\351)f(par)g(`hostname'\),)i(le)f(p)r (oids)f(\(uniquemen)n(t)515 3752 y(\340)i(la)g(premi\350re)g (utilisation\),)i(et)f(de)f(passer)g(en)g(mo)r(de)h(\020v)n(erb)r (ose\021,)e(etc)i(\(v)n(oir)f(`oar-agen)n(t)515 3852 y(-h'\).)67 b(Un)38 b(sous)f(r\351p)r(ertoire)f(dans)h(le)h(r\351p)r (ertoire)e(couran)n(t)g(est)i(cr\351\351)f(p)r(our)g(sto)r(c)n(k)n(er)f (les)515 3952 y(\020stage-ins\021)29 b(\(a)n(v)n(ec)23 b(un)h(m\351canisme)g(de)g(cac)n(he\),)g(ainsi)g(que)g(p)r(our)g(c)n (haque)f(job)h(\340)g(ex\351cuter.)515 4051 y(Les)j(r\351p)r(ertoires)f (des)h(jobs)h(son)n(t)f(supprim\351s)g(quand)g(ceux-ci)g(se)g(terminen) n(t.)639 4151 y(P)n(our)g(la)f(soumission)g(de)h(job)g(p)r(our)f(le)h (desktop-computing,)f(la)h(pro)r(cedure)f(consiste)515 4250 y(a)c(donner)f(au)h(job)g(la)g(propri\351t\351)f (\020DesktopComputing\021.)34 b(Il)23 b(est)f(de)g(plus)h(p)r(ossible)e (de)i(pre-)515 4350 y(ciser)i(l'en)n(vironnemen)n(t)h(de)g (\020stage-in\021)32 b(sous)25 b(forme)h(d'une)h(arc)n(hiv)n(e)e (cr\351\351e)g(au)h(pr\351alable,)515 4450 y(ou)g(bien)h(en)f(donnan)n (t)g(simplemen)n(t)h(un)g(rep)r(ertoire.)35 b(V)-7 b(oici)26 b(un)h(exemple)g(de)f(soumission:)639 4549 y(>)18 b(oarsub)f(-p)h (\020DesktopComputing='YES\021)9 b(')18 b(-s)g(m)n (y_stage_in_directory)d(\020)25 b(./m)n(y_job.sh)515 4649 y(param1)h(param2\021)639 4749 y(A)e(noter)e(qu'il)h(sera)f(p)r (ossible)h(de)g(cr\351\351r)f(une)h(queue)g(sp)r(\351ciale)g (\020Desktop-Computing\021)515 4848 y(a)n(v)n(ec)j(une)i(r\350gle)e (d'admission)h(qui)h(p)r(ositionnera)e(automatiquemen)n(t)h(la)g (propri\351t\351.)1926 5255 y(4)p eop end %%Page: 5 5 TeXDict begin 5 4 bop 515 545 a Fc(7)131 b(\311v)l(olutions)45 b(en)l(visag\351es)639 726 y Fa(\017)c Fd([F)-7 b(ait])39 b(Finir)f(de)h(mettre)f(au)g(p)r(oin)n(t)h(le)f(m\351canisme)g(de)g (netto)n(y)n(age)f(r\351cursif)g(des)722 826 y(pro)r(cessus)24 b(des)h(jobs)g(a)n(v)n(ec)e(leur)i(descendance)f(dans)h(les)f(cas)h(de) g(la)f(suppression)g(de)722 926 y(jobs)g(ou)f(de)h(l'in)n(terruption)f (de)g(l'agen)n(t.)35 b(Un)24 b(m\351canisme)f(bas\351)g(sur)g(les)h (sessions-id)722 1025 y(des)k(pro)r(cessus)e(p)r(eut)i(\351galemen)n(t) f(\352tre)g(a)5 b(joute)27 b(en)h(deuxi\350me)f(recourt.)639 1191 y Fa(\017)41 b Fd([F)-7 b(ait])26 b(Am\351liorer)f(les)g (m\351canismes)g(de)g(\020stage-in\021)31 b(et)26 b(de)g (\020stage-out\021)k(p)r(our)25 b(qu'ils)722 1291 y(soien)n(t)30 b(in)n(terruptibles,)h(par)e(exemple)h(quand)g(le)h(job)f(concern\351)f (est)i(tu\351.)45 b(Le)30 b(m\351-)722 1391 y(canisme)25 b(de)g(cac)n(he)f(p)r(eut)i(\351galemen)n(t)e(\352tre)g(a\036n\351)h (a\034n)g(de)g(prendre)f(en)h(compte)g(les)722 1490 y(temps)34 b(de)g(t\351l\351c)n(hargemen)n(t)f(en)h(plus)g(de)g(la)f(taille)h(et)g (la)f(date)h(de)g(dernier)f(acc\350s)722 1590 y(aux)27 b(\034c)n(hiers.)639 1756 y Fa(\017)41 b Fd(V)-7 b(oir)39 b(si)h(il)f(serait)g(vraimen)n(t)f(in)n(t\351ressan)n(t)g(de)i(ra)5 b(jouter)38 b(des)h(\351tats)g(sur)g(les)g(jobs)722 1856 y(p)r(our)29 b(d\351limiter)h(les)f(p)r(\351rio)r(des)g(de)g (\020stage-in\021)34 b(\(Launc)n(hing)29 b(?\),)h(et)g(de)f (\020stage-out\021)722 1955 y(\(T)-7 b(erminating)27 b(?\).)639 2121 y Fa(\017)41 b Fd(Ajouter)21 b(un)g(m\351canisme)e (d'authen)n(ti\034cation)i(des)f(no)r(euds)g(d'ex\351cution)h (l\351gers)e(via)722 2221 y(une)28 b(\020host)f(k)n(ey\021)33 b(\340)28 b(la)f(SSH)h(par)f(exemple.)639 2387 y Fa(\017)41 b Fd(Mettre)28 b(\340)f(disp)r(osition)g(d'agen)n(t)g(sous)g(forme)g (d'un)h(service)e(\(d\351mon\))i(UNIX.)639 2553 y Fa(\017)41 b Fd(Coupler)28 b(l'agen)n(t)f(a)n(v)n(ec)g(un)i(logiciel)f(d'\351cran) f(de)i(v)n(eille,)f(ou)g(tout)g(autre)g(d\351tecteur)722 2653 y(de)g(c)n(harge)e(ou)h(d'activit\351)h(d'une)g(mac)n(hine.)1926 5255 y(5)p eop end %%Trailer userdict /end-hook known{end-hook}if %%EOF ./oar-2.5.2/docs/documentation/desktop-computing/desktop-computing.lyx0000644000175000017500000003022411757171206024251 0ustar plbplb#LyX 1.3 created this file. For more info see http://www.lyx.org/ \lyxformat 221 \textclass article \language english \inputencoding auto \fontscheme default \graphics default \paperfontsize default \spacing single \papersize Default \paperpackage a4 \use_geometry 0 \use_amsmath 0 \use_natbib 0 \use_numerical_citations 0 \paperorientation portrait \secnumdepth 3 \tocdepth 3 \paragraph_separation indent \defskip medskip \quotes_language english \quotes_times 2 \papercolumns 1 \papersides 1 \paperpagestyle default \layout Title \added_space_top vfill \added_space_bottom vfill Module OAR pour le Desktop-Computing \layout Author Pierre Neyron \layout Date 29 octobre 2004 \layout Standard Ce papier prsente le travail ralis sur OAR dans le context du contrat ACI CIGRI-Icatis. \layout Standard \begin_inset LatexCommand \tableofcontents{} \end_inset \layout Section Objectifs \layout Standard Dveloppement d'un client de calcul OAR lger, permettant d'utiliser OAR dans un environnement non cluster, c'est dire une grille ou internet de manire gnrale. \layout Section Sous objectifs (cahier des charges) \layout Standard Le client lger de calcul doit: \layout Itemize inter-oprer avec les firewalls (passer les firewalls) \layout Itemize ne pas tre dpendant de NFS (environnement non cluster) \layout Itemize tre suffisamment dynamique pour fonctionner dans un environnement peu stable comme celui du desktop-computing (mode pull) \layout Itemize tre suffisamment propre pour ne pas perturber l'environnement d'excution de la machine de calcul, celle-ci ne pouvant a priori pas tre nettoye la main par un administrateur averti, comme un noeud de cluster. \layout Itemize tre lger dployer sur les machines de calcul \layout Section Architecture \layout Standard Afin de passer les firewalls, le client lger base ses communications rseau sur HTTP. En effet la plupart des firewalls autorise d'une faon directe ou via un proxy HTTP, le transit de messages HTTP. Le module desktop-computing est donc compos de deux lments, un client (agent) dialoguant en HTTP et un interprte traduisant les messages HTTP en donnes OAR et rciproquement. Seul le deuxime lment est en contact avec les autres modules OAR, Almighty, base de donnes, etc. Pour encapsuler dans des messages HTTP les donnes changes entre l'agent et l'interprte, une mthode de srialisation a t mise en place. L'tude de protocoles comme SOAP (web services) a t envisage, mais un mcanisme plus simple a t prfr (Il est bas sur le module Data::Dumper de Perl). \layout Standard L'absence de systme de fichiers partag implique quant elle la mise en place de mcanisme de stage-in et de stage-out. Ceux-ci sont raliss grce aux possibilits offertes par HTTP pour le download et l'upload de fichiers. Des mcanismes de caches ont galement t mis en place. Un mcanisme de slection en fonction de la bande passante vers les noeuds de calcul lgers, et des tailles des donnes changer pourrait tre envisag. Les communications entre l'agent et OAR sont toujours l'initiative de l'agent. Cela permet d'envisager sereinement un contexte ou des clients peuvent tre n'importe o, quitte ne pas tre accessibles directement depuis le serveur OAR (Ex: machines derrire un firewall avec translation d'IP, machines sur un intranet d'entreprise ferm avec proxy HTTP). \layout Standard Des mcanismes d'excution de style sand-box n'ont pas pu tre utiliss pour ce client lger (aucun mcanisme satisfaisant et suffisamment lger de ce type ne semble exister). Les jobs sont donc simplement excuts en temps qu'un utilisateur normal sur la machine (celui-ci peut cependant tre un utilisateur ddi). Il est trs difficile de garantir qu'aucun job excut sur le noeud de calcul ne perturbera pas un jour le fonctionnement gnral de la machine. Un minimum de garantie est offert par l'utilisation d'un utilisateur ddi, dont l'limination de tous les processus supprimera tout dsagrment constat. Pour ce nettoyage, des mcanismes plus fins ont t et sont toujours tudier. \layout Standard Cot dployement, l'agent est un script Perl indpendant, ncessitant seulement la prsence de quelques composants comme la commande `tar' sur le systme de la machine de calcul. Le systme cible est GNU Linux, un portage vers MS Windows/Cygwin pourrait certainement tre envisag, mais il ncessiterait galement le dveloppement d'un mcanisme de slection supplmentaire au niveau de OAR pour le choix du systme d'excution appropri pour un job (compatibilit d'environnement d'excution). Des packages RPM et Debian sont disponibles pour une installation. \layout Section Intgration dans le fonctionnement de OAR pr-existant \layout Standard Un des objectifs sous-jasent de ce dveloppement a t de ne pas trop dnaturer, c'est dire ncessiter de changements, dans le fonctionnement de OAR en mode cluster, tout en permettant un fonctionnement en mode Desktop-Computing. Cela a conduit un certain nombre de choix et de limitations. \layout Standard Une des principales consquences est le fait que contrairement des systmes comme Seti@Home, c'est toujours le systme OAR qui attribue les jobs aux noeuds, et ce de manire asynchrone par rapport aux requtes des agents de desktop-computing. Un agent signale donc simplement sa prsence et ses possibilits d'excution ( intervalle rgulier), puis dans un deuxime temps seulement, rcupre des jobs excuter si l'ordonnanceur de OAR lui en a attribu. Un mode pull plus classique correspondrait plus un mcanisme de vol de tache. Quand l'agent s'arrte normalement, il en informe l'infrastructure OAR qui marque le noeud comme absent. Si l'agent est interrompu, il sera marqu absent dans OAR ds que sa date d'expiration sera dpasse, l'agent ne mettant en effet plus celle-ci jour. \layout Standard L'agent a d'autre part t restreint aux limitations suivantes: \layout Itemize un job "Desktop-Computing" ne peut s'excuter que sur une seule et mme machine. S'il requiert un poids suprieur un, il pourra uniquement tre excut sur des machines offrant un poids gal ou suprieur. Sans parler des problmes lis la dispersion trs probables des machines et donc de leur interconnexion non garantie, permettre une excution de jobs multi-noeud demanderait trop de modifications dans l'infrastructure OAR actuelle pour tre support (cela ncessiterait notamment l'ajout d'tats dans l'automate, et de modifier l'ordonnanceur). \layout Itemize du fait de la dispersion possible des machines de calcul et de la limitation prcdente, il est difficile d'envisager des jobs fortement parallles dans le contexte du desktop computing. L'usage est donc essentiellement prvu pour des jobs squentiels, avec variation des paramtres. \layout Section Composants et modification de OAR ncessaires \layout Standard Comme dit prcdemment, le module de desktop-computing est compos de deux composants: \layout Itemize oar-agent qui est l'agent lanc sur les noeuds de calcul. \layout Itemize oar-cgi qui est l'interprte qui interface le systme OAR avec le protocole HTTP (CGI Perl). \layout Standard Ces deux composants sont fournis dans deux packages part entire (RPM ou Debian). Le premier package (oar-agent), nomm oar-desktop-computing-agent, doit tre install sur les noeuds de calcul. Il est indpendant. Le second, oar-desktop-computing-cgi est dpendant d'un serveur HTTP et du package oar-common. Il doit tre install sur une machine du cluster OAR disposant d'un serveur HTTP. Une installation simple consiste installer oar-desktop-computing-cgi et Apache sur la mme machine o a t install le package oar-server. \layout Standard Mme si elles ont t le moins intrusives possible, le dveloppement du module de Desktop-Computing a ncessite d'apporter quelques modifications au systme OAR. En voici un rsum: \layout Itemize Ajout d'une proprit "DesktopComputing" sur les noeuds pour identifier les noeuds faisant du Desktop-Computing, et d'une colonne "expiryDate" dans la table nodes pour le mcanisme de dtection de la disparition des noeuds. \layout Itemize Ajout de la table files et d'une colonne dans la table job pour grer les \begin_inset Quotes eld \end_inset stage-ins \begin_inset Quotes erd \end_inset . \layout Itemize Ajout d'un certain nombre de fonctions dans iolib. Certaines fonctions existantes ont galement t modifies, mais leur nombre a t limit. \layout Itemize Modification de Leon, Runner, Sarko, oarnodesetting, oarsub pour qu'ils prennent en compte le desktop-computing. \layout Section Utilisation \layout Standard Un fois install, oar-agent peut tre lanc avec comme paramtre l'URL pointant vers \begin_inset Quotes eld \end_inset oar-cgi \begin_inset Quotes erd \end_inset . Attention, l'utilisateur qui lance \begin_inset Quotes eld \end_inset oar-agent \begin_inset Quotes erd \end_inset excutera aussi les jobs. L'utilisation d'un utilisateur ddi peut permettre un nettoyage plus rapide en cas de dysfonctionnement. Des options permettent de prciser le nom du noeud (si ce n'est pas le nom donn par `hostname'), le poids (uniquement la premire utilisation), et de passer en mode \begin_inset Quotes eld \end_inset verbose \begin_inset Quotes erd \end_inset , etc (voir `oar-agent -h'). Un sous rpertoire dans le rpertoire courant est cr pour stocker les \begin_inset Quotes eld \end_inset stage-ins \begin_inset Quotes erd \end_inset (avec un mcanisme de cache), ainsi que pour chaque job excuter. Les rpertoires des jobs sont supprims quand ceux-ci se terminent. \layout Standard Pour la soumission de job pour le desktop-computing, la procedure consiste a donner au job la proprit \begin_inset Quotes eld \end_inset DesktopComputing \begin_inset Quotes erd \end_inset . Il est de plus possible de preciser l'environnement de \begin_inset Quotes eld \end_inset stage-in \begin_inset Quotes erd \end_inset sous forme d'une archive cre au pralable, ou bien en donnant simplement un repertoire. Voici un exemple de soumission: \layout Standard > oarsub -p \begin_inset Quotes eld \end_inset DesktopComputing='YES' \begin_inset Quotes erd \end_inset -s my_stage_in_directory \begin_inset Quotes eld \end_inset ./my_job.sh param1 param2 \begin_inset Quotes erd \end_inset \layout Standard A noter qu'il sera possible de crr une queue spciale \begin_inset Quotes eld \end_inset Desktop-Computing \begin_inset Quotes erd \end_inset avec une rgle d'admission qui positionnera automatiquement la proprit. \layout Section volutions envisages \layout Itemize [Fait] Finir de mettre au point le mcanisme de nettoyage rcursif des processus des jobs avec leur descendance dans les cas de la suppression de jobs ou de l'interruption de l'agent. Un mcanisme bas sur les sessions-id des processus peut galement tre ajoute en deuxime recourt. \layout Itemize [Fait] Amliorer les mcanismes de \begin_inset Quotes eld \end_inset stage-in \begin_inset Quotes erd \end_inset et de \begin_inset Quotes eld \end_inset stage-out \begin_inset Quotes erd \end_inset pour qu'ils soient interruptibles, par exemple quand le job concern est tu. Le mcanisme de cache peut galement tre affin afin de prendre en compte les temps de tlchargement en plus de la taille et la date de dernier accs aux fichiers. \layout Itemize Voir si il serait vraiment intressant de rajouter des tats sur les jobs pour dlimiter les priodes de \begin_inset Quotes eld \end_inset stage-in \begin_inset Quotes erd \end_inset (Launching ?), et de \begin_inset Quotes eld \end_inset stage-out \begin_inset Quotes erd \end_inset (Terminating ?). \layout Itemize Ajouter un mcanisme d'authentification des noeuds d'excution lgers via une \begin_inset Quotes eld \end_inset host key \begin_inset Quotes erd \end_inset la SSH par exemple. \layout Itemize Mettre disposition d'agent sous forme d'un service (dmon) UNIX. \layout Itemize Coupler l'agent avec un logiciel d'cran de veille, ou tout autre dtecteur de charge ou d'activit d'une machine. \the_end ./oar-2.5.2/docs/documentation/RSPEC-DOCUMENTATION.pdf0000644000175000017500000032055211757171206017747 0ustar plbplb%PDF-1.4 %äüöß 2 0 obj <> stream xVM0W\۲ B۶BOnt F#˱.!<Ҍ{3G ^?B\Tشn|y_މ߼>/?~ʰ҅ԅw PLO#FҢYmseɤ陌-o9dySoNӔ=Oɢڧ,j?%>e)0v&FaAA QԲ-UT~Aubz4˥A񣗍MTΦȠiF*)4ʁce }YeZƃ;7aR5];نtHp.^,w׺Vgua8dIy`Z:8>sMk?+Tdw)ςP. " P'+; }4&?_2QmBkT=.ەDOaCvlX[p9*lq/\;o("-[nb.bGisqyhCkQ%#,R|ZQ%-a==lӆ^Io0 U>Rq.%8*͛_ 4@,%68ި%AF 4 endstream endobj 3 0 obj 646 endobj 5 0 obj <> stream xZIF_sNWK[M !l2e~z$ a,kEVg込ө4~N[7tEnRy~}@{+ U.'xU+ 7H_K_4q G0'\N.o߿^>@R@'%Ӌ:O'߽"?ubџu?RU^c~Uʢz/ibNVj3B/ofU4w{\N^G-O;5/xՃݢǭ.ZħinȻrk|@]`Y4:3ڰ•}Be ^>Wga>uۊnO,?SX>f]`(C5xMڜyoYh@lfm/o/!? &qhfBx^qBm؍ f[D#ڹ8s;]?;CKUdG*"yQX",w `1TK>z4l-Q#an0SDP@kFxϢ;xsZ6. l UC=l,랒O!B8Z"Ϡ0|O WVT<z7\D"`:Ƭ9Tv0ؕ1g\`(BH/zsC;W-[n H٣-Mo ՘ipyfsIT,i:j1SjD)pTfey"^Y9Y̻ zoQÑ =sNq<#ei#< 2F\wx^=ƏPCA:IóՃuFN,vZruE)ʅ%ͥ-רe,*cYB9cMr4j8A[2ݰ+)zj Г?ktiqapuv_i9 k|ADU bU͠~52H9t4NXeYea{nf)2>X>*!]06P ѽdDS)%e׃:ӹla[|СzLz(~Dm<|">=%rEA ٪zOI}ʂ65 zLU %(c묺O$MKdy~ޅB?wBAp=A-丣 U$줪gX)$`VjؕJ00OV2T&vkrp  gML+p#Xd C P,-5e} =Y=}?wE5m'X DD`2T_Fbgs is0Y>8,, XN'O ~ecJ-&5c,_m`n 145r@hI`$PƾwNZ}amF嶩k|<;ٷQ6@ d_AC'휽S=;ZofS҃y~ag/_4'rcU:* 3P WZǩe=r endstream endobj 6 0 obj 1964 endobj 8 0 obj <> stream xWɎ0 +|.T,/C[KI$E(%Q㣢P>TU+@0}>~d{~z̫V/*ۧځkaP#OVoN 67Q O=1+GaVMAu}8ieД&v;K>@"xαq>'"L}Ӊ?箪 /T v-lVs@`nhP\{ W""霄9"B̠^B(@bdqfLZ@xKB&^/jc8-J.d$YeOt ) wH@zKgX#  ܭ˚T2mM5D`CL1F~Ќ$h^b-:iM2=f+ł:Qb97,TCZ4b^8 A&\ 娓5E۳;l WBSMnm|-UK q0Ijr^&Rz\G0FS&|'1 C--Te6X(%:{?7HɋZc\my)'`#QbLi(p/^⛵o~J] 6G=t) 9qlPkyQ^ȕN-̔R,jew; MʅGy:J?/2mszv endstream endobj 9 0 obj 1023 endobj 11 0 obj <> stream xXˮ6+.`7CeKk&-P4M~3R{j449 | 8IA*8Oy|jCy,L gѷ,ӵG8å=BO\ũ@/Ĭ( Z{I7,>ZǏqfB4߾5ߒ?p@?}Ҁ'/5?Gʅ*8?S灗!.$b$i5sZĀb,Se1n+aUCuV> &V!/y󂺨&jYy c3q&eTw2ct ZzWi\^<W~mZI2$M w$"O9(<Тs'o|w5E Hd W&٬;GIĆ!>m R3H oN+'8:wPI2܆]ƫ\c ?U6U;ֈd!PhFY)l8`6B6B7'11&_ 7] Z_FwrY"Ajkn/ѽ :roTEl]2iNXmt@$-'P~Jqf#XW9{:$Yg{K\5dp: 2{Iݴ6)6c u~5a)ܰ`8V,s-•xčgF:pYl|^Q q;by):1Ҹ 90>yyުp bYTKEXVt0$TQO0˫ AVZ)lv.fRL\\u)1rPw[:䗒f *NS jXWr]fɧ 8"e5Hgha8! ]2v3ZiB'6q G30Yz>YtMv<Ëv"H TkTT7%)C9 a\Yfsđ42$E $.jpt|ճ1K&|?vM\Yq ^-7;C,MIvQґo#x~Rm2Pۉ;xu(Lr \-}@<Խ *-zRSl΅76Jb~hT endstream endobj 12 0 obj 1501 endobj 14 0 obj <> stream xYI#7_sJ[K` v6ɃBN d&j{KUEUzono6_[f<ǣS2po3`{?-JwB+_`H*pH+GN] moY604HN3ހWAxP7!91eT><2o:NYZe(b+xs%*Cn= ?aEFqC +\*-1f%vHɈCj 9Ƶ ůX+"u?TbPg+Aߢ!dE| `DXw]@3CZkp˔U"q! )N{.4<Ԩ K\[,Sݨ&%yz&ƕ8F*BݟĉϵA IwzU &z8"Ns]$!x8)j5jO Q4sg4 |}LQLT[Yvg^f̻\ڤi&& KUV}B)%^%>&eao7Bh쇨gS;E]ѹw]`*Sc ΣKE?x%K=G[:"KOaZsw/%mgro헐J/Z!qixa1)v6!-_|?9p^{X{ѳg⿚ `g^]r@cb\v MNd| a9yjLǽ ,[Mq1HaG,ugix){l-Ҟ fWHHgَ^p,D&Jf&|Cxpn4W p+[סNJ#?HX9Sz$!V5+1]Υ.W)7Bf0j\rgڌO.__#.%{hY2βgWp&#Oo ج62|Ճ :71jaCnynSVړ"Bu0ukinLo6 p6u7_t:qg@vR\ endstream endobj 15 0 obj 1662 endobj 17 0 obj <> stream xYɊFWLeJ|0>y1x.-))/^D /^۞?~?|^ o-\[ݺoop{}Wwj xV3_+|㛧3f pv[Қ_?^̭~VsN:- <ȅ匞@ WK?aBG Kwcc7ʥNo7#^Oד)xMȌPP/o0 \\͊!z `qwr-eEֱQL3iRhdF^GђYkV!q8IAx<藀? Qč~񌶏"ܭ8oyB4߷$G:g0&2aga7s t KDkM[=97& .,=OuZ/).p@  !2`.S2Va?:dڮIwU82:ɨ6_ġV̢н-;Y Βɒ׬T+:Șe3,I^K{ww5gDq ڤ>S" HR$璑XRi$E^0PrJ[A٪!^Wˋuc99r~78JYYpJ=zu1ALP.1}Vu$S-2St6r̘a3ջ&pΔ*B}4m:iK"^a٩t]jә e :K_p:tMTK)ـfύf9W3-ùA9]l=Xʪ*Od5)lfmZ?PgY4E FGێ B~^N]c-疄 s m$%$Jݯ,5gqp!b䪲bZb7f.n\fX"*KKFvV8iE бEMj?%l=1-6=x`ǀ'mmzDUm5OCbe+ۀǥ&a(3@zbX@ CnZ#r{q="d1Dz[9\f qisJv1vx:<: -zc7VFԇ荱rQtl!M@ۤ Z2a3QȚp.|sE5L!G9Q 2{V< 'Y3U—D( LwA$h`DC@YJqd t+cB&]EvU xw(k=jp3MwCѼG wXkGpئ?-0'$s YSlI!U ;&E7Y(E-,&`(>ܻ<ʟȚe㰫QFj @Z ewX2O̿DS)њ.bbU#|(ɵ8b=g79%W.Q\3YbMgK endstream endobj 18 0 obj 1944 endobj 20 0 obj <> stream xTɊ@ W#f=ir0r9 \V㞦\%=II V.T56ͼ}^o.rlBt< ]{0`-v=cz.Tp.3 r< KPGB+9;I)IAv+;Fá*TmhxJ[ĠEj誅Y:ɝ.t4 k}nnTW>Ĉ.l/X"Iq D׭. _k5+ dߡ5Bl D !Jٻl L)m&Y01s̗ =eV-tK@Cz+׃ICÉ2c!qlOu%_,r-[׵xMp$ ;_s8/󑌏RoevV6{F)z⪝L #r_SԀoB hJ%'޾I+*_"0KU`uw:&;au:A雩jsds3x[#ôU\,u endstream endobj 21 0 obj 562 endobj 23 0 obj <> stream xXK6Wx]HȒ @l'nU_Pz ;/َ_IS>DzO#T_U::7kjꇯ?!>vhUr7W_?9>›t |i.U;iqy..G׻ $iGz+Ѹ2@8qB# ݣ-Z5^ $|sO/wA >U>Sԛ?e)Azp!GQ<9FA|q@P!3B|L<΂l,tZzk.ɼ"\,轼qet5,>歮H\lDJGtu-m'W݆ky'9U$٬竱=Mi+7 druVcdC;ש(hKYtnm4%{ m 1Md,Vhb1:shԥQWWUJdLe@5_>DGb&zokAUrHiiH|ּ'//1aAeP/Qnu7nPLxa zfPoBk9y_ 4_5a^~e*\ =+nxFA#KZ_dkb\$ICE:W`Ay!!,s /oBYxyҴɖE1%b"zn&^H9κ71mwGYFGIlֻF[ckUoFkHeBU=iJ%ҿŊ֎%wV}>g\!/ $$ǢMҵ!ư|e^h^ߡѐ ݩY H )Hpy r:On/MhyNobiZ̅by?QK7ڰ:\j@mCF7HiiӊʊG7X;Pދ.9nS[%5! ´Nsb-~B4=us\ZV9pܡz}Oݠ6?39Q Y'/{:;QՇ6+.˸ aEBW|-jX,|lIRk=6euB@+4RX0Q]9&;XgӫC#AK-kFC V1r?bՖJ9 &a|GZt8q5:ݾ#`Ǣ}80 SH% t4~AXV0P"-ӻgk^rjcXgM,&^R7?{,1~J&' 5n$Ly5gAgXHMskHGo zV}%ӂ`A?~MquP+iş*)kbdd"`[Ea!uչPLOOYzWHk%N}A.W5Τ%b3kbC7Z endstream endobj 24 0 obj 1548 endobj 26 0 obj <> stream xX6Ou!f$KCl'8t tQ;7َc24#w_^53e~~w?d~^%Wxy`_9h_<^_ď5 >08QOL&w~|u5 !?~g]n0:tI3_gSX(^5>Ƭ| /%t ahɕ՜fMLIQL&hu: 렬'@8(#"H@9(froW1/kSl[l͠6D*.s{Sb#3[+@/H^#}L㸄 2-Qn,œE$.dЎfI۪7d:JgF NYS+ӿRpY1 cYr9SY0S:޴Ⱦ -vvhF (hx!I;WLm@͇"0%<IG`k9Bȃ{M 9 `o~G; %rp[m> dGnKH^}wԯ9Ւ!{Tjz";a~""ilT!kv$Y RC^zB: (wDe[;UspILfT!0q}!{/85Ә3UAawøH>'=Tްm> P)єlD"T Ȥf@ިd`u۷=pTP%FA9v0KWxԴb3*"]7[XJ]1cL0ioZe"E.duc$4=4`tjfe\5W d>BG"oD&ϗhpfu0C^ܑYN6CΒ` Ľ,em\S]5"JO~>2OhgxF5Ôp*D #ÆP'Dž,@c%P=iSzώhi]'[l)eşT,uLyOi#i1Uk Gأ9;RK=1K~Nq,:i&]i+B缴y9c*Ч_ct= :gA7f2ٷS[QBrwqC<גocǡgq}MŀAn6j74:u'*^wO|(fIN.it"oyŕq թ3ivWJoxdP](c@fBvvVڭc GJ::ڙ8߬vHZ#Lc`&'jv hujpCsDW7S&ة`QkǽT$ sqK{b7U|j-*ϔ"8tiOSg oϗ  endstream endobj 27 0 obj 1520 endobj 29 0 obj <> stream xXK6Wx]pӂ`Hċ.(Ewӿьd[B18,fΜy _?1-H;߾?~7A/yay /f9<"(/ fóf-`\HT'4nrϼ*MT??8=/REje$U#~utTicqupIğão 9:=ڴ/ڣq]09E 6=ZA~T`Sag5'LچZXwN*ʶJ':|T1*UC^:ӎPte= ŋa~ DG]ZYRC=w%,ucUL*+ȩ`DOmy ZAK.HKTO/b_wl;4'up]IP8Dsmf.> stream xYK6ޟ_u!F% !qEw]. f-Y)mo8Ix_v_eV@>* WP9pxN*f|F/$މaҴNOto\1sQH%F3U`hdpSTHI;h=vXx}iǕM5u7>QV>@yذ^,_K$в dE.0,gz  .^ۍ%ȴ$>_8NՠRtG#+%fڑAsz7EݍRq"Z6f-J6c ľ.Ւ`HbzX'YۀKBXq8 1Z!-/Gu)q EY0,o  !^ , 9 TvJdi'克taV-"Z伊d ^KFB7Tuhjޢh=Ka=po4Tmtq8 3|hZbXCaӂ^AJ12m5 -JOa oLސ7F>Ѣ}ysE,L\P,L2P$';t;\s g$QӢ8B/px& EjQ 9tAVNj:gw ܕ.؉-syr4VTehu|}0lZaA -)2} d<*㟑31%qJҶp-XOF[Y-mŊIHa3R xB.iV+U⸬V]\]h,)|JlzN-MˆZ16j2i)͛rrLmc/&GR eAT7(XDt[t4a]1z!VPMԨv;OJ,ZiJ'KAi}Szl{->r:3MN07^ST|6JuNxdM&#Eؤz t+U*4[g9uVضhT~N^_j-(wxn9iJ^V(`+Xi< a q'}Pn X^Ey,Z8nA.(K XC}Uڂ*۪L)Gn9<1xh:C-XvEUD X+ ̀-Hֹ ze&< Srٱ66qpc;;QGzJE;8"_f*Y^ ,'%8)E6_T95(Y,Lg6䧡6Ib=ղPG2اgj,[z$;ظpom|dՠdlcۋtcfNF5rפd]۝:ceRIK7)Rݪ,b:|q;xXIA8 @P(LѢ( :c{*`A1,yTŎ=?o?4vmEa0*󨭄yTWQ<֋ iL8ȯ Ml[TV"Bڝ[袷;XJm;ܰ|=ڛ mӿ5N endstream endobj 33 0 obj 1727 endobj 35 0 obj <> stream xUM0W\p:OƇ =,u[衰{ٿ'$HIh{4#W0tmzV߿T{}CyjM5bU^6lMlX@1G Dkm֘hG.y4n3'g+"t]@$pp%2!t D TdJZ ߽ܚۜv;nBLttz@A% <*M\N<{%)#.qY꾪=/Aut ZgfY'G&=MH='0lCY펞2mրY.o^!e"JLe,lUy^RFhz9K`D^^^dɫ{ċ8qP[TŹLL c^/]mf M \YQS&cQq}pGD8N+ e\.*=h)#}huaZf[xeW9;A'<[7!^jsit%vV5-b 1SSrңZ-IrY9\0 rn{SڛnWA endstream endobj 36 0 obj 632 endobj 38 0 obj <> stream xXI69/rACN dTly ڶTZn|רg ௟h~^]om[t+4??:^weeVZF}W wxV-Qc{?nwBi@KIԔ-}]>@JB+ۧu͗`O?\N@>5`[74?́mFVyeVUM2)^I4#EY-/S^TlU?Bl,,s_k)9"uPuM u~-R/`NMx8cy`i?yd݄"fR+"Ju%-.2@FцNfh;ocݱ# Ov `gm :@K):,pGP YI@oQ':5}@)ny k 5CWhf Ad36z$g@(^ҝ#Z  L5S(g-B"ʐ}b,{cT0EԘ#JڿdQF ʔUwivUM3zA#G|::DizV>wr!SHIų*1t"[> of=c|5&ӊϛzZ7y>ILE$WGww-I}Tf}cUG`Y&9p)} ƦzҨ]:4NTFnS,D:u,VS葻jmwi:پ}VMơ}͠'|}hȸlqpաuԑ-]r4+s晳p\ )@c[v!EAkȵ9Bm=c5?B=kK\&]ѐa6,?d:%h#g|?=;F:~N>9Y"pwJ!$3B@33s^1jgkN2&?l]4я}LE Oik['zI:|5SZ:1Ŷ!L7&h^}h59"/tk|^#E_WR*G GI/ƈ/\sPӴ#uۙ,/A;`F*҇Nm endstream endobj 39 0 obj 1472 endobj 46 0 obj <> stream xzy|[Օ=i_vZSd[e[5DؖK۱Mر;la2$qBIL!u((ЖG|tM(Ѕ~mi9I6 {޽{+N%:@8;4u|Bȷ ܬx[2%BSc{Kb?5!2|l !Bo{K )w"qDL(w9K̘|9J^  j/IDH{[Ύ-7mlM%[64O4fM}]mMHEyYI0/8&hk5jR!qHYsePLeŁTC1r5MZī)H=ebxq-Y[^&6w9A@^-,+*zClv7i-s̓MV!aTS^FhjJ@:ZҼ %*={m+jIwt47ִ!$5 ˴bCZ)'qLxk0 ]ד憰"׼x$m CtW8tY9f\7m]yϦw_ iy&8WcE6a` wq% ,.. |`N8Ռ&=\Ki~p'۲uSҹ'MZ!!sM4 P(SMمBgO.]$ k9bf- -+M]=iYQHe||( L>mX4HD+ZG&Ĵł+uY䥊bɍ/(6a|ܸ̓eT8m=DTF hIR_:J[+djꑺ以dp8+inborC`=OXҙ*XT&Fl߀몸ygdwZtN-D/*73J(t _ޘl l$Ɋ&α%VzG؂@q->"< \²ڸV7YaCbhSկb*giCjUφ]e 5%@E6ּ t͍Gr^vU a+L閰JᦓR}zOsr lZdyGޚ&l 'LnihM;Zϋg ٶ] ,zJhAnqeFi[cy38y&Gz1:,aL!<)aUDVabE%ѻL`'$T>D©q@_Qrcj92F,0;88D iC֝Х5ƴ6 ߐ+^+P^v"xY.x҄y7FJRqHdY̻=tY"Hp -gJ/kL~Sobˌ˻p;,!޾#4ّswn@6S5à5Ok֨ڹALEbEcH htue,Ujj+0pgu?~.XװƲFY{c\hp\][6)?|؟d 9֜,*op7I &sDƂPl nr5XDи ܥnBOD[T *sOTEH"0|)=2n/f4<԰XHx) ՕVU8GcњZ Ud:ML->w+F8$k6~L62vp098:%x ɢxCm d)˻K j˽n3[˅M*c奥kw݂:̾} QOh] 5~A/ӛǽ ' ( h$HRʔ`CiP!ϷqZTSI~=0Db~YS=p_LYVksXtJk(u(wCV@`)~!3ah4VkrwuêPk?>vlsHCQoۜ,(!tߧ& MbUZ"%VGm5&foj=eZi߬CJ#JJP`n^݄R:չuaw(n \.%ȘpktL&$mf31zpp j*Pb fw9PۥE(ֿ?fI3Gaѕ벦h1eL `gX`&L\[*~]ءTW_oԶNncwK1>gFsu%K ft!Lu)(ԇ; !% hF,@;@,8~\ ll_/=Y. \Sb @s[lm&/}KP}W|tMkKQ;ѿŽq n^/Z3=g@Shi4Hę=$aıZV\PK¼RN8fDxPNtJ{Pm^'8uNN  AU,"e,! NN2oJVN\[xPRYm@{Ea&%xV)r(+$meWkIڽ̧e5C-{.JZs#)M5ք ݷ~fe-5ݵj'澻@BdB's>A~YТ|v c0BUFj4oMnQ?;M_'%ܾ~aPPv(YqJVW*Lj;T'-04hs$-K}ddp7c?rER+`ṁ&ͅ-5g%(קep8ACKDJ͹ .HIjύ#`r箁ܵj+O7Caw_[9Xl`'s.QF2=b *P2\X6'cdϤM UÙwT FCK}b$T%@jB殝g>(E6]W~;VQZ ZrQ앛G\U5jSlIBv_f3u'%5%TY(UAHW壜dމ% D <)4ۥ2SX5o k$zUDt:&˹쥄Im8`˧,ѯ%\CJYmfr+n_ ’c询Qq9/1s8_B3,1 *P(v̻Չg dדu Lŧt_Ӎ4^Eݑ͵;[J2%@O.WeM`z]PT\);Zvn)h]{Ng(5&'bJkw`:&y-#)k|>s h1  =yKC5LJ~jg\@/ nl\{Ul\#-Fr_? u/m_F^؎[hnݲi}~oۏEv=y&Ylj3m~nϣ0p`@4l[̄7ihӓ2R`o 58e/,43G—G52fϹiߵ*E_js[ڰaM{"{ha5k Y1Sq<|nkNϔ7/ᘍ$@&u~PbMk2;t^qDAjˇ8SiRDqR4XPt|Lq_(\m+r?F@,|v6Me,RXɌKYK%5R+|55rr ܱjsjzܳWy@"mkDJ#m!iWczsy .Zv{IU$w_XZ{\-w*XØgC'xߦ0;:Pn%Di4N ]TQk"fK|6k\Ѩ"dZ)gjuKRZHTմzB02MXq qi,X雱Xmf[+$[v!ʄdM\6~&(i9tA.'4|'*GEibwKQ)(e4BLfg7t-+_r[tUlSW@qR t ;ԩa;qكuJφҭ;]rCC5=u/s//ʕƪM5VF|O$͵Ӊ4\?lyw=j;ѐ9E:/c(/k}C26$H$\J(J9`h4N2I5V'e $iAï:Z6\~J/FNZ+lBN*9rG}[K79V dt͞mQwEpMeik}SUK{-g jJ5dQ)`7Z!i/wDj\2h6+y̍*>Ŝ.z\.ou8G5^gaLLxrWX[^PP6*ʸ xk5֊MZVdUуf |H.oI>fxkkɳ3j<DPMnpkjj^ 4 j>A|dP)xw6D "Y$4ǎB$ͭ< A ęjrڜtƝsjJHrѯV- ۫tuɒw*?oꗯCEmZҝH j>J7 V]άSxU.wl5WVU9 ו0\ߺSgwL w7l"P 6ȡZcyW[Ez^?Ynk{%!1 2+`TΛ Fc9_kJ^`Qn,!GG4۵f֓ 7Dbz폆Y]BOb'>~\_c k?ɝڱC'cw,2wM5{B[Z*ژj{^k!rIctM_M^rUljTZ]dHMÇ[ .ѦW١0m&(G S^->U? *SNE/:VBc$9g`ߘ0+n.rSjwm]8ەRq8K:{W񋚈~lg,B`lG%-UDٯ 4~c?: CΖ:︦Ϟի~6۪v<0*>̅OjoUA]BUTU׺ Z;UT(%.%Z9Zʁss,׹˼vDKoqV-*L='T:49ʗ7W0a|sGܻ3Xk &i0h)<4|An1yH40o(nyK_#SjRVss=Y;anٞ=%y3U Q Gpr# ŏlJVy/92p&H'^D cnz㘃 O";r-8~ܯ9XU7S@* :*xU#N(;c0^uh3~fOSwN{q46/]~odn"|@f8 5&B"(9VmҘuڭ^e ;ov1첊@t`J$9Ed cAFcQZJx&ؔ6LD/cOFM(]O3k&EYe/][ji)J4X6B-J% ݣӘF7Wۮ:d#_2_j0}~'V[X9=S*!J\/"}4^&A*9omSXwv"#)ߞd'A,a{[Rle3d_>lBx q%Ènl~&Fa:!ev0s4G+)P^*ZNkioޥ۬[W_p1em61/c6IN@k !)y-.̡%ڑeor#a) Jr#y&a51<1 #nzrPN!6bk t[Ӌy;}0az8yXAJ{yXEJdu<%urk֑d| d3Mc7#CCCbV\?3;#NΌNύTl5kF'v7N[gL 0)K%^AڶNϰw$rFW&f!qvzhdtVZjhLl7\F>B7+tl߽{bxTBN!83#m3'ѹQq٩5|Pxi+'F^ S#3cP{tό̎IQ6g'vTDîCcӣ{LH#o<  N͢D\g4RD&޳xHF^xa}bNRIjZOf0CQ2 d)9$ v~D36#Fd}wUؾK¼_$]f&#UHxvbfjO%˭|= IjLR ^,7)mk^k)g4}ȱ"/1ˬ;G9!F.Cɤ9+mD}؎o-w a7K$y\OHAJoyn3%nNz ?+#6.զ@2/}*jy)f Xg/ *${Q_ENR$?#ő IZ pJrT8h/!Wx^0>E$>)I#Wps[{Mʔ$g}I(qGi%Zex2aH-xN1YbB[o_yW+DŽt}4Nhc/Ƅq1AX5<ޕx}X0IidP+"/6\lxO_|.f/ |Ys|&+ 1ԓԺyZ2,|%駞~>OM>uOO!ٗϓ '_Yz4XcO?xd'p* 4=yAxUG#~#rN;+z(}qxWޟ^9(8{I- 1A]G' { "1d7^6Xgރx_k"$nAkᮓwq3&<>YY!rNp h1r1رXm1۲F̷?wj[ `VLNN>ar_/H_@ 91[\}i9p^<*'"ާ!!hiac{ehNagd;O }OV0w[bn9p2|p w;)Hߵ-tdۡ;۸1Z1=5ƤIݞunjH7H9p&\1fh02rF@䨨guÛ)[7;p4]Şξht9ݔv#$=c'33330z33:֤e`Y"=Ðk"30c b/> q:@X3,g? :rZ~;aUs6 vE endstream endobj 47 0 obj 10185 endobj 48 0 obj <> endobj 49 0 obj <> stream x]Mn0FtMFBH I$Qi@"cV"ٞoxdթ2a]oin ouEvhllL3 <76n/NMl>گ뻵_0E>O}n)?e#h-Y5Li5AE/"kڵҗFQ+<+4AyL5 SdȏO}}D>p ԧ 5 5r>M#?#BYOG:#.e_\SdO쯨?dOȫ8??c9?rd4krob~ endstream endobj 50 0 obj <> endobj 51 0 obj <> stream xԼy|[Ź0<9$kבdIdyeٖ8vNj;NۉI lh 44(i --@ ,w}߱333:F7!=څRVzEXVli/ϦUC+eG!Z\vkMc(Ls񪾞޷<::JWфQm4jV`μ+zZ6Ϳs]ϖM]4[߳eOi\C#l1V>~t != XAT5VO2MffNWMIY9P~(R\RZ6|zE匪jefͬں9s/ Ȉs\{loB38N[/zԄ54y 5t_V0:Kӄѵ迀{Sc豩_h:H[nh"GEs4tUl.6XT9̘.T(dJRU|uKYXFk <| m(hPGCcف3]<릟HĚ#u=cM j~{(o!P˳,^j֘W`]G|g -{=KH-߿g Ԏl{IG7 Z6O3b0&f":.OI2L"ݿ>߽Į)^b5Rg'~|=Vc{LO yu1Q[CSu?7O4lDAAq842kA[s-f_yhfLZRX&1|]p2_yV u'7r |h0>-mcJ- (=9;S%z  ldݪXƋ$fRcZֲnwm 'Pd3>"2`y̺mc^i6LihkgF1m[óZ. ]6-ёxNȨ@;^ %1M׆ݤh5{LE8OeZSk7&XJEFNX3+O(l_aZBÐ"T4LI NF@_=741p,'qb id!s>c <>^={2۷_p?<ўCif7~ρĔ9??(W1ݻ?CS r{k˂ܖPf5gw.h{DM-mbkϤӼ'|TWTRY"XL#~BAhx8 i&8i4LӄxCgɹ:_/Wng4dc Tҏi}5c@ KftKWSBy:C\iRpZjPQ΅3*OL a"K~TJQ`ߜ7k/wV8U+źŃNdEJZxS Z/AsK`3@Z>.)‘3k#̚H&`<5fu/;4Ѻ]1jSa-QZ‚+/JYdWg][<Lڏ,Ks N`PpdL[TH<;qqIEdBEBQ5o=r u +̀$`*~V f59qtKs"lgzcQ)EV%ghw-]}s]Aia2ٌ; Pɔyy%/@&:ؘ%L.ӠLI d-`)smw?lR(BK[EJ3t|oh-ލ*}w{`]NǟJɅKYŽkp9Z67vVSrUWdZt[4G^<]ysKdLlK2EwXG`.YBpOkfˀh^D%#9@sZssHY9;qVqkg7DohlqKM> [Ed_x vQOHgQx觳3 Y!ϐ9IPU*[ WL[pBk,ȳն;<׮OklK{o&:e֚l§ Kxnhqu19F;pMNǩW)O2EuAO-HLyR\ʵŵEuveE&E%Nm& vɈ8/qDh a#|6))ɯ/(m[Pdl;sa{gAA ZihZYxWwB?-l*Ou7NgDsƟ 3lKQFo7%zA3bQ`$K&&l&:p!*#j$v#/tnJ~&oi XeNɖ9%KScc\H|JoE@V,*>XTGc_ͨQʋ/G9c '?c9ó!pf'NB|'DaZdϋj#6]x/MHEQ9t+-e{[*X4[1KYa:Kg5 "1xO2G1!pMGIΡQu2"# p0H, n$ rDHu8#yTVlah\ e2p o)Rk+Xvv*2)EMEbFYP @vӦTH 4%R`tEPaɘ:.7P8Shn36R}Bz !N'f,hnΜ7'7om 3,lx\pߊ553SĨ3kEJ"̙`5ͭ޺PAs3R(M,Փ(oǔmhe%@4yY,7 L$ >liyV)cogp,P)g[܄ bLvaQVV;߻^ux,(*PME T#LDd.w1a|:fPSYe[Tj wpQ $NO$sZalb``.9fLծЏpdzOfF:IĢJ|Qj&.JK@uȳYdC3J*(}ao^RJ # 6!/iPc*_[UTx H *Heb؜] hjBuM&;jNMf+;&& +6Ȥȸ\:E\I Kk\u7]QfU VSP_=gÍiZg_ڿn 4Y9+CZ.r*,E¶sڿ{xskX3jY{;`w )2mBg}Oo=Ne5cJ QZC^ ( 3sJŶ<{gHqCijSi4s=`74(Y4%>[\0`*i" 0ޔvkل"0H:d?yQNKmIWW2%rL^WJI#*,l8߲(3T_g-(cL}̙SZx}-RatVߝGCC?dwr0lx 2n{Nf۠ j048o6 C"NfʧX>_32&Pk7PR ~awD&n)n0qʥk)/NA 6]0]Ƣ8 8_Ln3KHE)ZyM9!srWe2me[EYY2H򊎕`po:=kb5oԀd]toChNF}շ4\%ʳ/ocwWgJTG9a+tG뒋 4 Vn^uWeu{y-=9̧LqV* 9%34nMPCX6 A|9Qi=M曜8?tƜri65t 搜'Et2}I5!&dḷ *U=3Zvl !v* c"L!*@4{[l\^YME t9%˚PEfG|'OBw/z͛G]0b]0d)YPvebF’r$G$]: uNrKx H1DI\i$'/%a, %oK DRj꣩ *^ޖ>H8 6JMn)Hkd"-~LQi7훢7E TTEj;A -/h7>]9%myr粄yx6\ 'ef_Jm䗨\l07K'ܰ!a9YQfS>SFEʱL(2-%EE]]$L 9^&#r!Ԉ._V'نK\.He.bQYnjgLO~@^mvWfAkua45)(IfZ#%9AN={MMV߬?& Oq0FY J"ꋦƈ}c$lf0B'i9-{Qk+9^᲍*Aݜdы+Y7\GV⾃ ˅oQFu:v;HBOXݔ dHNPQ6l#1:h4NeTm\WG"a3E6Ghsܫg? Y8  {dUJЮUخ~{{4BTbGsp(ߵS)JKyhLg*@s< )KkHilƬMҬiJ$qK3) TQ.F]ʸ«NuA,wf#SU9vWXl9ʨkӚ:#|BLFMUn1]YӊP4gqS> "ICz/q9FjUwAiWN(w>\VI7`Hj( 4vl5%HId ʥ_Iz:Ceu;{*{zJՉ79/7?|W1i-K7~:W2$#hf`6fSv49OPwT;;5f3>mcf@fmmFը؛fO.; 8ZpCB']Pv^[޸g?ø|ofOgcsP3C%kƹj??'Mn^@gгg#*Hvg@ $:& /26R?j0NP㳚I%fs.e©9W`,,Nnm4eЧO>I} & ޲@a;km1,Ɛ1vbPc8R[z\=h]ccS&l2yg'v)^/4y![ e:_N )pxO0Atk@z kd5FJ}qrP /S5uOAӳ9Ilf`ћ "V\Av\Y~ͧAmϥDokƿmn\2EZ8fʍ, #QjH;`15P$gȊ0F(hH &:iv4LFQ'x.j\!)0% %O&N%+IJAhZVUc~:W4-$c7)63Y$z*HLKN~@6y &ϋw'O,z-bkONE ҽ䞷x1xGpyc/0b׼o{U#sd VN:[%S+?U^ Y@9K1 9'C@{m|iI:qW ]v9lIef q ,DS6 eq2<֍ h;:˨f3KJ-*SS-n/V}Ho?[WwMlv\7w9-v5? ?hhğ?۱IJH-Z@٢UZ-E4|U :dX̰e,A;ւ㧱&wI[) m=pp=ҡRū5HI' R Իo#4br Jv2/F fZ~;zp%cpNZQ$!yU6%kEԧn? a! $ےq aȌJqv 铤LيVXq`qVϣ)9@Y2^\QIW/&W(/*5[{wT9߬'o]xhϞ#'WYN-yk{)576r"[ck%1G,X}_5'j~8[)iHk  mlm%AAL]`XHbqdpdzX\QDq8BI(˔|#!ufr[q9f5܅2 D:9F? C-4eVe4!Z6ph+P{5FS&55ҲP"ێ#hkX8"o8W9:C2Yy77J5գR[j#ٍL`JKlxE.ed XLA:T._0~ԓa#9@3誣_1p3Jn[ucˡd0֯-Jc2DƞUKg<& 8LQQ47߇?rfy8%qG#N;ӯڼpIWV8Sn˭Xt*oK 斖sKAmG/(~'8s8>s_:~8;1U;`i֐ CCD@hkh_rC6n;`6ݝKlܤ Y0nъj#AdZ5Ef$}.&Q}jx 01 jE$bSUHʉ۩.t0Zio AK$ dV(ζntcHn`j[>ARAć hpKT=]邹ilѬE3@|/iS%%\̪Η.ײU%{.=vͲjsN\}D{`:O_{pUæN=>ykO5T5sz~,RYae?͵f"$ْv v9a.; ۡmvqIjjP&#CP1Χ>NӅ@K"vPu;fU| [/IȞ)!S VUTʃZlpǵ ~6g{o>eh3h GeAMm3Np a5^#lx˔r. 0&2< G~ >ѮbҲKIn^SAjtm #q㧈{a,΋U`Z3.\C^,s*, J +Sj%l7Hdpw%/ ~گvIѾj#G#'iCoRPƔP `( p>(ZCyfQpVTZY!30C a.g/z^og]J@W2Jdsſv-ac~`%%Qjp̥"Tvngf͛ 9Ëv V5w}sf67μ;~U#8~kUkkKҲkg/ݿ$nw*9=sVGq%Ӕ5 +Y_ɥOJ:a!'q:*W 湦GmV3TvQa|+F[Vm ޴M؈MIϊl*k ԊC_8c"4]L??qNa ,B?Yvg\UjDz+fDgPI;٤=AE`̓#͑8 hnnbT_0dGZmp9LYʄ[ sEvd4FuiHڦѨWA@ONDU*"TlQ&+yd2rAY9~" Kdvj2dO7eI0"+ߺ> c)-vš+d2얗kxmxA k%!)kdeP4UL㛙dc41V+cNɊ~B2L[V\ XfOZLTen@24@lPk@4]jd YIF{AT} S2lK*1exC2<)ip@ '0Wj?1:( ?(b֣b0Kh\l^AuQO-ahpY/a?.Q|h'%%Wa;jq@`ߘގM1Q7A< o;ҶG1:&; lA#@kmb-ob] FђD2ͨV bڤ(im6~$Cl&(jK$Ec16?ɷ6X9ȔN7A5  n|q.;n=0VK GukUŻ5O20VnoEb5blpLHے[`U CmT$3ӓHRip8!Rd@ҩ1K]NB4]W`w$RY7 #|lyF$>N99&n&?xNr$c4i*`op+ܢgI/x!?[?ܗ #݄K!6`hg+!pIKsfEh6Yo>+BvZ'x_zf [747IP%͗I>DISK.G0:|_7^ZjF]Z ;pr"WZ}{wUzYx⽉)կԵ:B?WXC\M "> IOVi*u?K՝N]Tp)H̳d%@y*~t4|ף7<+wG>uë)߻{c)iI|͙/q7xc7^dDHy}Ļd;YUA:,{ 9]>R$: ,nԵI!]I"qfecEqbgߔ7ĹzZ=Hc ‚EwIyRyi%;BW}+Nh&F<.]B'hKlj\lAj􎲠n\ #TMKoX.% 4 ʛ`m%+$#G_ Uּqu_ZF(ʿ?qL;:v|tô+7;;>ځwye:^ծ~,c6-] g~'v9PMY/033V+Ef |=Y:/}ali/C ;ہO}gwg=uwUq`ݒ߶#?Эl0(?۠wE79/ ]?aʜJ^u1rVw=>":ʯQ,bWE j]X<(;\o0GyjHQF :پۼ\lߠs.xO` ָ` 溠%RTYt (Q\P L.aXץDyw{q<аClu_b~`?QN|~HDz+D翊r LEtқtgIN*Iwߑ\&ksSsqn]*وSq5n;!|/V͈bi9|,8ŢK&/jn; IDzى2~$yT^8?k62(-VUZjRXǂn]Yae0+wgqsyΖl| ͸pMCMr_4637~tB'vr`5Q)՗JRSǭYZbQ eRPl~NG;\)$%C7BeUdfJ S%Wq[+lä8aROHYYI/NX]VpHhַ+/{ 6Lh=S뷧)^4ȋ*des\B(fcP{yM&FҪ]jMfF(VWT15v%%c=L ej]W}!~MJ˱-pzۆLS5vm>>{^G#V3i]w^жOŃ-UJ/aMt] G#8q]v)|Vyg]s|ә]WVئ30Ufܼ",\5A^{|.t\VS,g&hfuu{4UKR^`}3&P>ȇ,[9Xm!UA;V/jV|{ԞeOZ"cC)Rv-PtVQTT**;[v 8avgD,9egCgn9fHBX2)eԶbmxƑE缌b V-Ęb*ukX!-+7()`~AKL\#gkm93S_EB:ڵWp~,|G8Th-`S qe- 7  &Ec[F|XvaT8\LG"/QFA k %K$*߭mm] C؀|*8~cԍV!m]4:hJ$(aJ{VƂE7rN7P)eGth%K( qohN]6a hF\e듒S!5R'ѿ9k0!U\*S5|X=0$ו2cĒʮح5~)g҅"v 6~׏&fav~Ò&_tX;. vdKYP1C#̭"U옞@fPe͎=3Ew{Ѯlvn>[)scW^EQYRၑgA LwՂsi]{c^om9?Ӱ/dCQ^Mqu=wHRFEAsf.˩ 9h|PL_ƛ7r;!6T%}4.Pt.]AyFJ J) YL!QjR_.V5L]#a@xۼ E{=4j{;[Z~ǚM+[S3gGvοm?ׇSm7 uoc]m=gtOM|*z'VPJS6ܐBi&r!D؈Gx9\.D@SFp;i,]F|:s\u s|8~z-p@Jύ0zZ_Ig|{Cߓ&bZtiAE/|ȷ\ O FrxrymFe ;tyT?-^pb5ԫA 6q09@22Kj4)uH=|/:DH@Ȟ)]e Z@b4~Rj}c6**6-R\SVOs]+# ^ڳ)R93eŞyGZs" 1Z]al[OimFiZEŝ& >z4"h"?H،5+e5 mah 4c\ nw${݊FLЈJ/\1<]b.9܄aG7^/N-y1k6>){%\pC-t,:{`Тj,9Zš'}W^VvUJڹY +iPV͙_R'%oGdJRDZ$eC]oK uRB 7;abB$AT  ?EJP/EvYg'N6y+I[7yv/][!2 6Wv|f;4MY+6/ވx]7鉲|#W 7A>4j^ |a S?|CSFN UQ\@@\&Yd1Ln'F=\~TgAK)z)VK.-BoHkF-ptR$HRTjVIb|O`qһ~+!qiz ?(uGɧ*tP%X'A$!AHz 3OK@)n nk%X+YI|\H' ^B{BKᕉKKqLPxk/J%ni5ύ&Mab'LM |9Y K''Ƒ=U.1!>wH*c~7p8ePp.R|.KLuw)>.7_籯lE8ӱ .m[YX@2 >:,9G9p!Q/͠߼K uyП)efKKO#>h""FcH"Sbhjj}Do2#+H֓/dur2-K.'w/J 59y祷tcg貗\;=Լ-|M9k=+9sj#]#U>?ntFsǒ۸~S7-]7dxWǺuZC_Z b-" Zl4_o &  @s0Ҩ%d 0lŰC#aF4R["|/fp<~̎O7AT,w쾲GǏZ/ѠH߅W8"o!w|5|-7֗9 㥶N:$VV">2~ v9f0{㵸b"Zf} qrdZoR$G$ZGR*Gꔑ$E|6R ;Yk8a,RZ)[0~zEYܺrSoS=K[܎[]% O6N6tZg M;)Xy^l^}XWE>ef5&,^]WĔ?u+?ES?牌Xz(67Rcuh/D?@ "E ATMZhGYur]9Sm04]VDPveV0m)O#m7qN#MyE ?EZ[bkjW$__JuMw 75|n\ko-ɖ?Y-s9enʹyɆmn}{vxH'8}'iӾ \*}(g]mI56 4'4į#$JoRaT)&( M ܒH%*$cmCga A0aUSp '2@/ LcM Ia","'JU(lNr"A퉰<3MHh)Ip=+*((Yѷo7kpxhpgt`p}oڵၕFG|}#}Ûz , Y;b|w%//[GXZa~AEZ_oto]`#3盽~E>ʁѾa8޷(agog}e`c>ox_qx`w`km$poS爵ѾFGÛ7oI+ׅgy[zFV_5n>6Utv~dtspVF)lo=-ڳroFͫVmYohbg];Uբ~FgG?=4CCO*e44f=C4e!v-tV*7c}nmS1}Th6Z^ U:Ϡ[y\!q7rF ǣ?©v}by|.;9? y9o^bSÁ[MSVpQ Kؗ>ټMKpgѱ :ynO+ m"fbc[kc}Nq'd搩qTlxM(ZЙMX=hK=e}6NnIlO] `js _L #w}~:,zDϟ^ ѷkW2Ό7⇲/_2JT9[>F.e:HN:SM:5́L_O=]|p? KTj4D.NQ)%X<-E;]",Pi?o`yyClBo-INScپ,ab#kƚ#d3 K8c!,Q7T. p=5rиD ^mUukcC0<9LB,DFRmZ%`ʟT fg&:E8'%9'>}>pF R" !m#6. +F ?; P땰FJFccw[[bבpm;b7VcgG vh"BXFG=GӲlߝL'sIawm`Fy⚌ԏ];m4$R4NX3dum0 TȜ!1X r|A F$x/;p.Yնx3::l?s;ɽx rM܎v W 2$0MOT׽d[.*'nV_6/0Yv]^11+~tF endstream endobj 52 0 obj 19018 endobj 53 0 obj <> endobj 54 0 obj <> stream x]O0| e׹ s?\c|>ާWalλpy>7ӗ ]|~y<% )φ(؅ԴanKVeW: C߻slh5ZRC;յƽCWZ/B2O a\ѯ-Q{jp +-8 kz&&]ARMc/Vk_ޠo&.Woї߃So7_УAQWїߩ&W;!GB~0l֦#%,A/=Z;[;|k!wo^ѻMGcVΥG~~]?4䷨?zqQבbn.k-VqHiq}<+@/=8K8a~R endstream endobj 55 0 obj <> endobj 56 0 obj <> stream xݼy|׵8~ϝh˒,e[e,/a</2ƀm066m NK @iҼ64MS MHЖ潾4 5im}/ڦ}Mޑ Nw왹˹۹3ُ h1H޸w)B`ݸkw\fF6m+1 6m;pҽPc`o.F("uT jx=[G'm[yJwl32=Ő8nӻ_B̀FihֳGHoIG/* ˩No0̼j;+-f{r>n OKJes#UskEע.(ra3Ϗ\i!4M=Z:(O}S`C] O QXJ>$QPr|0Oo?p>6H}tCBּ<}RUx3y#܆!z_ut=  佄LKʿmB{RZ#I]HB Yoʹ57؇ WPg;;Vl[_޺l-ƆE jVWU) @[xɨi5j2dō_2'bY =IIj(Lף> )ȁAiH&$h~I1K]$|!KN*eJW"FI _k_ciƞRYnQ`QIPOBɂY(XJ4;HM2Rco_2A;K[@)U&UjJ::;[|=x'd H2aIK(YhH?YhhLhKnV$>;D|)`AM_ý'6||YH#0wR}DL6ә{a^fMmKk;Xj _":N~:#dD+:q Cr8ԙ=4Lc͙ɹY'@fsʎIVj 4MNl NEO*_MS^ \>A )5Z0DLM&E@ jh=ƞAWRSޑH@Qٲ0)ChAd80o'VHXҾ(z6fJ%Í e_ឆth]ϡ>@ XXD*pG@#6ILpgPҜ_i1w,YXcn# Z+5~@\R#i|Xd: O|M$OI!7ORR@n$ } 8H%ET4JYURI/0)Hd1$ j$Ky_G?%xE 2g#Y"hB~=L6M6+Dz[f}5%+ yKQZDeh%he=>+t-e{8w8cM8ȧh[VחfV6w8++:#sγy$9Bi*MКHDM(7^ifmiL&il:MVEf5HpLwϧ:tRG$2$u>POhz]:]EՄ2@󍁿JtVmWJ ?fdYs &AtM>V瀦G,~/U> 5_e|_^7Pʂ,`Y8tavhCre],`f .`?tg}=ꄖq*b.2Y^^79i G7c Xk,|/)_Ym Vsnj )Z(!D`?=S{^vݦ0Tk0}MۗyT"-YLb 7wM׸UQvжln)SrqE D^ \1Bnj7G6c99s.L_2 ZGvy-C4NÐ7/x5ԉ4T@-X%($7RjݓKDwh<ziNJtϠq0ʊB D ඎ_n88_ӑOw4л {Aߝ[0\81!g ,XS1unlAgSxM|EbYCQUhN] _oiZb#d@~tN[元n@٠B( \`o  $ܙL@Y`$p<] 9%W8 ʱ@O✗B/6`զ.hz 1B(Q[ݽct44J$@r}2[D`6J[|#oO5\~;#IƷ/ʙz2l,Dj n9&IMnݝu0 cs7q|yeyrɼdwFG"ΦJV+usG_/=1ת_fv;.n_h" a6[L˅W oν\M` N@uS/Թꦓ<fVPHɐ}˵pY[xߩNJxo^(;87кp_q-ۦ[t3w~gOÔ0X\Io䍔M6Z ['/8{\P`c{mp ng0kަ9|u!@^BՓ?IQyMҡdYҫY!P(uFGOm_ CmᇁqܱgKO~kc~4s iJSxy{9 !  Ȏ5V5 d._ gO[.*Ů P d7˺9yr:djJeEP;f#5 0sno~x|7ܒ*N*.3{.ܱP={?l{7| i7v>seٹΘb jhyh5YB f6v힜XlOxK9|NYszr9Wrp9Ibspuxef;VlmC/I*(1UEAP$b" <1=k_t5^Bso^Wvϻo<'FHuA 㗗V79sPpo ƂAf(o>gR,1}&.5hB}*+QfhU)ʐ1}/g$/:eI?Q(*7LAt}Q hɋVD`3t5D*\#Q|$ =xCV.ӁB2 ODaty%JKxt$:MFٺ((hPY3+/V^d+dW\sYAel#Mc PhSMr>ψ@JM8Gt8GV嚒ʵ=5O/[tPZ&N[ۘ.XkBy.dw׻p}#ފ &%7m'؇L_=Z>1s&31bl65;y}varoNs .w )?" .ڏc27kWOމDFd` oNp@:qy^k0;m+`K+PM )\9PYGPxB3K4n^x8wTٞW&, 7TtvND6*Bt[IV(*Zq`3y ֩G*'*1]zVVګQ5,Wڐ=WU`r8lϕ )luQ YPj9iU.ҴQ+e}wZ0혌Df/3sA"\&ƑF ]S H}[ypCNokh^[__rubo{r/+k1?pL}U3mEK6c5^ߩ :U`{xUqI#1EZI+:|Y:5bu 5ɾ1㕽q/& IE/yysJ3"1#qIji(tywyE[(4oJYzԫ>B\]3d q{\؟Atܾ {Wث= lb`/ u&/BT#.Z30gKg~釻~O(ɤffFWM-Yz*R2֪<mlIx&X?/> ǎWՕ]C͟?M;+Uvu}S׿z4OSa(\{om_f%iRtd(zR;>/oE~Fq{< XLG`n@sP5ˣZ!pxk͵=Ske#31[­rDom' 0AU5Oݓ-Bo „{)ZHɊ@3F*bcb-Ѣ#%mh3盋׬ʪZ9gIrݧsbZhbB~eZ2W糚Kb5 6KV]V}4/- 85;3sym}Iמ1kDWX`0=l' x!A@ҬpJ3wSOcqX-* 旸%{hZn- 44TIVVI#1q1Cm?f2'] 5jVqƥ,騵Fo^uO;eZy_m.G!L׍rw^!5bnH\s)P38R >3п4mL䯈mGq#eF8bh&IbFIXJh%VG94rqmĞ>nv'ݩ@]eWd[l;xh(fa>oNEML%쎻;V!s3vlԓoebvGlZ KւVVO\r~Z QKMJ 4Bg}- @pjKek7UV͍[64n#]OkSY;xO@uDd:.g;D:LJUUQЎK#㖔>"O%*K|%L4^r%񒑒%ɒ%jOY&a4MDF>7~,;'&r72yN`~pL-dAlX(kVᷜ%ՍZ>m)u_@N>Y_+46տ^L/()6×(zc:]6|Iwj)#Kr.7v'>j*44#F|>*! 0P& _㲰G'Ű WEi [s̢Cq(HYVD2 T0%&Q٩ZuKK/=p\`O]첺@dEGjSSwj1BKfӮTŶϭ T^J [Cdܤ sld-v8Si~R@+(:KB<G^GR3zYY^=$WLX_?gT4KK*JhFG!X(#=mUj>+B7CUP(bFW.ƻc]-L!@s7b1Vlm^We-vlĖoY2Tjt}n+ОT9κz7WԌƛ?dLk$Ϭ1|OC&V;l;G΋kN1ju 'PZ){B֨~"9eY I_9 ѓz5/KZιN56h>ݩCht׿ 9(rN\FUpJר71&:ODS ^H5E$Lq Z⨂62R)ZS $4(iYB7c`YY*b`Jʗ '(>J~/iU$} %9'~B! oEpH%a5 ?(=!abE…R"1"ߕ><&Ѻ%vI}1IyIxBI@Z.6KaIy,ce骤JuRB'=$=/-o%$ĨV$KLHN!GNJ+5IVt&S#HˉmF Z:1 $DP6v #@ekh -5}>ȇB%"5׈xǁ қJbe𛑷~" م$Y!&=7#o{6#49QIhr5EzPAj WjuoWZ۴6m 8ck ӘS/5u* 1b WlB2e:3n<|M5C~֣翷sޱpG0ǚ̩[ө8oW̰ ft؄LPm5CV trCǁu!U1Ќ x a"FpLhT\Ao7LfS y$Uf2Q žAũDL}GG QD-VK}1 8+ O' }#䜿^p]S1A>X6N  R9sF-̈́!EqPAjvxDx_`JWhQ~4] R vR9/#iwH3ݍAWh[_8/`Y 8D{ H\&$Zvs [ 2*ajhaB7B=5#t[#ԝi_Yn7W;*tm!K$쬩%+L6O{v.t)/ D,Jh}fe}.-z}j!6%%5z{\[v⮯@4D'sLƏ--ab<$ou8+̅@v~30 \z>V3s, 99FF1KLf.=Nd`UN6'cjӘಐm^aPmɴRM՚ {}XBx iFY ) ƺ6,C؅<# a?ӞԫxݛR0.m?Au wDS:>HDfјb_gSSȟ3g70r0u;fgX,Bn7Yg년jaU qe9]EWrÊas'Ot3{jM_!AQDӧ]k}۝!6`{Gfzk(kD -u_+y*FarJGc-VYY[X_NC 4N=OXݥf%v1S42,%\1e v5T6;=2#Q,6[V%2IlRى"l6.vQVb#=1 ׅ>e{< ϋ?xyx0=-49Q$px8Y3?T)o[XzYV6p,CbV NDE&B.H_*o ^E]7O Y莼(ɄPBG$3/ʭ~p#tiWt3W2.yz?N-ȅ7xpbjv-8By;׳9M}uOQ6}Rjd"nyq7v03#"nDݒc9 wcyBwS^ TnŒg*fܫO)2vfW=~*I5bݚ,3N=m-gJ{)Jk5jKVh\24_m0pIVQ){MdJ5y +#nP;d dJ SM՚ S#b+9zb4 +(+8^p YT{ ׁd'dY-Qs28 NvSIэY'މqTt5w9ӛt㓹TF&և5!߲ UkU:[CssJ${h|xd'π˖z{]Țү~uՎѠՉe{V1 fIZ]z3ޣ{\rF14/=yphw2i48+5Gg!Tžb&/\ˊxHdbCl.EE,2, ..-60Hlbu61sVJP+.o)VT-ۧ'6/ H׌>9V,9MЗo:) YZ5'~er9Xї܈uŠVѭ wJxЍ^Ꞛ%6ۆlơΕlxp[ͲC ]̩EW! Ӄ%WJp M1YbifT/ykl7^%!&?φ _b789E-6BƟ+/Qf`uZVP%haԀN`}zYF&JmRM j>́Sqƨ8MoijE{"^-DAA /Ŀ '&Ƚ"CȜaI b'2*Zg,-bYnj{@ǝDɊ&"~5RHW?.€r=!qhL% "@hɽJ^;i~(/)&D:$&VLZ@Gr<h[U32`‘E]"l(lj|6|IGD [)BF"<_ȌNq\qHZ.c"D^,;.:.;X-f!ިq"39@ʚCT7쑐`ƦIN->q?[3+vRfÿ*Ol[k(SNv7"Z̐чՎ[< +=5cݩN"GpY_/7'-n+FFd W?c1N$5vbp`Y UAANMaAd4u݅\.kWQ0bhtjn}m!n׹ fv~ +y/Y{}MAiU-.A4/jL7"rqܸ߈*62qZMHeuƣ5'm.љ2.vd$|0U9-۵@K:ʌV,fGTiSm󦌿%rfxS;M6V=gclԷY݁ݎj#A` W>y%ƃ˂rǃ=dJZa$x.p*rrpnF~s[%^͑O}W| 25KGk`aZw,?=v[. Y hAnHPuRV_pH B(Ʈ ov )2j(@+d  ?&<'D`TjPE(H ;‹-1RF6)TNhM@ɫyX-l=4lL.ząiS8. S҃"-BKфŒnI~jB7gfg@:ћ)Xrl["f_ns~HLfkl9ԥM/#::.ỳrFoة{NyorET~-n]"""̦0VKN)(1b5Z ZEԧjSkQs GGEEDuDt8z5td_ѷQ4,|^I(:F^&4r Wkr=v=jӛz3Ȟ~5\8 Ͱw Y_*IHhs `;+VF|vE/=YV,-:n,I|7Rz=1YYN֭zL]! oN}7-iZGm]QiU/Z*#UN2""/4vю.2x0?>IzX6 zZ5L1Y&ԥԖSoNpN9{1zaC*^19-X`EXKP >ձC,Ǣ,@ݓ w[j33!20E$%~TWjteAS1c.҇6'e>!.`*bժZ.&̈>aq(EML Mo / 6mmisvؚצ0nbE<ѫigٞZ0="jphɿ#ˁDlҬAOp\& mcPJR&/\JxHdR5f|/c}!rwsq`ZbC5By2^ڭ4pZɮ,͎,UK|Wems/E]jC,d~) Bߚh&Dyct"F > tX  [ywX,zE:Xn/OԳf׵ܕp.cG/2Uy{ӣ}oG|V4Qk**UZ%F@jן̸ *(/QkL5ɼ.gL\YM7N_K;ҧG E™mPyXҋdG#/w#gi\)iIR]q@z8h&Pv,ſb׹cCO_U8ѹ_5pbxw&RuYshwA]]=e*,sc.k+c{%u%^S,ErL۰)\΅s1aM?zb{+d*$rj֯e~ֵDv mcQDoZIcj%y^㨚Oq *!}8:E!eH+&yKlɯRՠ>"w=Hܫh<佌;[CIMf=ND$?>}GGqDX4}H= 14@A䷳SWp>O1V8+pCYk>LwJ_' yF)S~~km;:. O۝]VW+u[.٧< Vhh;700_I?:KI7H[+$d[;>?6}p||d^8{ F[qx[;?6i;|xgx`t~lx`|wh? 7Ƈ lo+vRwh6:ҝJwmݸd`W5wzSԀ&r6ԏrC$KBjZF@Q U& V#A7ɛݥU Jʭ>RI"jl=IJ rJn߇\݄CzYFI5*_hiH?鸒CGG6-RZHٍ >)6)+u!W T\)I4]jHy[ܧk& 7X$-731.eTzKis>PTb#ha[+%0ycR%@ߖG{I_St{fK:\PP\x@yS̏)%IOz1  hÙz)|lϴKDKyӝp[F 6B1$2$NRBc$D d i` |4FqE]O| endstream endobj 57 0 obj 16584 endobj 58 0 obj <> endobj 59 0 obj <> stream x]M09ncJ( ġ*C#$2yVؙ?yaw<}:e3#y?tKZwm笈mxݖȟ6tk}Kc{}̚&9_ګ/4x)>E׆*v>gl~d~VӹنjbhY,d 씫xE֘JY4Vv˜=U w7;xq߀ɯ=\S2^vECC5p < -8`;e g_c_o #Wu8WRsCVo&E_O}=6Wo_i zh 䏻5>&=7&?rx=8Z::Sa}fd7- endstream endobj 60 0 obj <> endobj 61 0 obj <> stream xyyT[GwU==˓b@ bGF2 `##dL'^qǓdl,a'mtLgNrfsthbz@ &༩\ 8o7=: 499HP.A 5smc^od+)n͍BjT 1㇎ps G4(1*mg)+47 w 3Ŋ͍ޘy]˘ٯL|ݜ`#eB<xgg}f7 / 9<;TNxaVK1|GbL`$&l%ul荑\7|fSѤ[{8SyBl7^Р,}1kk{[ffkWlL2lGAxSFyVm},jbtl7e5#tqfP񚽁wz4p%űfKݽ1O#`F9 Z`F|1y"7߲'?,XW$,oPUm{漳x]ȹte3^t2 򼳽#16`6M1O *ݙcڱ*1xNs1\.&͕rH  yL+wz׋hY+伡Ƅ_K)ͻSC61_= FS)C )jr x,T^sgb/?7 [uU, zYwkʷߨnYf֮Y^9[bwaO(~~=}AXİ<;ky_YsWKq7ߗbfsf|c΃u g4PwICo.z/sW\sy&_ RAx{P+Byh#']a44OSUI2Y֪׫Tߣ:R:M4kb20`2HF_ôfX<Ȕbq*-YG}dv:m~[W]u^V5iwڐ Qr 64yYXpؑ~x$2 ;KHe4)sv^yYEe,58JPbQREyD,·/I7V2J,cTi{~*5j%y/s-]+ eQ>>5 Z;[wM2՘x>~cƂiuj믨#+3K^,&[.]֠|Ty)#ނEl( .aUi1Q=Y9j5C%FAJ 2$U"I2RjB0jhzm*SxL@9$502K2]}G:w~otHyR'_$ZJ!̈́uqLא//xENIɆcTk=XU>tt݆T_;]MB\\KO]Th-K?<DˈJօ;]#BHpXӾ;Naq =#lG(SUSZѫ5ڜ%N-j)T%r^R.% N)uJu)Z:i>Ȼ-vZNC 6XMIv`x{R;|J"9J.gJzylvյ]3曶!xaGpsmNTӂ/"Ϣb,1rF3aF8S$NJz"314F;RZ)ůDg58ɟn;aSbXz!`ůXL[l{ڦ5I#?7R/Љ EX*zӔbSa {%x@sx񰑈zmBjjT?slr,4#9A=ӯat|Gd/;v}QlMCr]%vo$\nru9WNʥV אtk l6eSyYo^@Mj3{9îsZ9Rg{ҧD#9$KuV6[4͉3([d9הlsM}D=3=O~Gۧ/yApFQ;OXB(1%ˆ  M4uvv^Dc>7 .©O|nJr*0"|.HزsJ/Y\G&O; GT ] afTXҩ2b*!:GU7`LaN."]\}Ƭ5 << џg0̞t<Գ}GwLg h\_!6?ݙ_IJo_o3IU֎7NOo.݄s*XrꋱhN7 }5 /evٟ%~sЃX\ mh=X"k3Ŀ_) O&H4g-B4iDGfh-EzeHkrl+%hڋO'h5?b Y1"o&hMz-BTaQ*ՓŨږ%K Z DG e.&hh#-I*H%h5Z+6m sh;ϕPh|845''“Xxʭܺ<pHhr:4lm pK4ulV\\ܵrX%xo5pA.:m Nn#\'̡9-ZƇ0chhc\x sݷ Ph2pt~Xdxl-b ]t[FCh4:QcرLu(ꢻ&Báqu4mkO$': sc#a|$< L r0Ȇ8'4M'CmLvPh" f[F46CBڍB,8HA(:vT JAC 3!4 a [? u7vZ W14(pnjďc+{ ?ݞKFv %倱{XPmL6*sIxa4ygBbadњ@p%*K {@K -yBoT=zlGtsk=@>4%X5|Et \-guBi6>V[PBU]rH:Uй #x}x,vG7|Dh[-?!?^c8ٚx om٦Sw`C% u|!ADŽcznc*h fԍx(MW_{W>ŋ_ jǮ^ǯ^ǖ[Nꯖ]m}5zuUL9}Ddo߯?c?tOXŧ8_?O+>W~z+犳wŗʾPN(WP=+>J6aHdi.* >n.E `2|N :dDzG-GG|H}p؇"͇d'p`abafr/`n$A' :O s;n?N8>FُbQ(9$*W\H|y5F9ӓFqx=N9@Sw-30<θӲ<6zR}L~>I4ޙ$nfqjO3GzĠp ? A_E_18} `xX@qfvnzx'zhIQ)Rb5>\ wy~#{cx:ʫ|{~b׀Ր mymm wmWs=* +>ﺏ4G5=L`XhƭՈ4]֜|YHU0s]Kd5&oC.>tP slv}a <<19g@}h$:e "F/P F`pH"!jrK(HjPWŠ^W417E,hS$&maTHD endstream endobj 62 0 obj 7630 endobj 63 0 obj <> endobj 64 0 obj <> stream x]Kn0=2]D牄R$}{H-cg3i՜}&قg6<ݜU$Licg4Զal0EٻNj!I_͕>6ۛ_0'e ᜧ>w#jݨ%qQ+rR0Nu]&`Կ,~ ,D9`Aޡ73z-8zGs|AE'r~@Wx2 9fj>8M#ߜԿ37ˆT{Ū|Z헸 endstream endobj 65 0 obj <> endobj 66 0 obj <> stream xԼ{`SǙ8:9u$Y-!F'qbll bIyEN !dC&4KӔЍx۸$ܼ薤풴K_JK4BwH摴s;3|_a2qD|-FiZz !pzicd[*9;GF@heˆ/Z0V҂'rwji>E󡕷rMZ#Z-/ͻBi[VD7h~+}߇k֭FKݭ֏]1׾'D@&5, kNo0j;)JJ)P2&LoM3gvtvu+f̹n_aD ߊ,hTC {^߹9>rB=ѯ@ *\ -eCze#ɿ.@ѿ]mc>T֠O@@(Ohu 6ӯ59rU)@yq4 R<<_ M6V?|tn4݋f#xѳ?QbZ܄b|Q:-:w0:xܹwQzys3{1}ܖl2>^Z]UQ^Jh1 z:],ʂRe`Uu]&Ϩ׶iˑ/--AOGӫA펠eKwguj+S3& h{e? g붕;3A~fp }u:7ФуP5+:H0fIsp65,5;*4SjUUlh`^L8^7KѾI۲h2ؑ댛|E*љ2= .+!ˇŠ_N-YV(фſ"ݾ+ڞپK˃~1Ѹ}bzi/p7ВY ZГp2ZBMހud߫Fef{y?Z=XtIgXds=ٳw{ vRX_N&A1knYͱ%j[?լU,_FB{]݁R ]T3O^2``gn _]UOf֨`MXKC]l,8u/'V窅jBcfen(:;؛3!0XPu~CK:Xci&#Ҍw7^]Pi,YTH_P aߋd m(Mۧ6,?"\-e> ^4ٚ#[\Qh9T&iX™xKOu‹i!U"a h`"K7yopEpIp?+zzT,⼰VkrW! hd!3^lUT=kڿ],΀ ,b$,7Yg9صnb,mYۃ {){{ @O2Ax`AX{LCL!Z{̏bV Y2 c2Bj-^ enx2L˸|]%Jcʿ;l}dFH! $N0yƘWg vVVTWݵ] ]mAETբŦrڃItbjt_L?Ldzќ=T/RvLJ[gT²Ta{j7B˗&䚒2qFRUl0۫H*PWQ }7KQ,{[Kij1YxM4JC?@2'\%mKDr˧Bp(ϝf.oX鸾ԅ:;%/t.팀ж`?l)vFlzLy#h7}˝RY⯔2 ,YՠU;cXロ]VV.&4)VT;^i"U;ZDZ> j!*q( NS%ީ))D"vf4Hv``l _ hN)Ǖ$TňHkU{5>剺\KؗoSf RIlxm>1;`0u9w,&sGvGE;GES"h\v`aǒ̋unʔR UArjoչ*REI {ʮT% H(k)o.ض:nhtlp@| CJ(0*N3aS_bEpζ%"׌5\Ϋ=[7h_oX 6Aix%w~I3-c7ΙqK<ι" X0=@yCgWJHʕ{+OT+K#PKxoć}ʝXN9,blwEO3i  w5 $'1@) Dޭu37tKsÔ-fϮ9~lۛ;bOH3jF9Ȏ|iy!mѿ'/V==Gԁ{= Z/:^q|ЈUqrR|vN-͖Nr;iQԧ۫>e$*B WY˩>D)SZK_ :H7œt;P dy`rFkI$X%TQb+{D\]J[]﩮ѳBvRszB.+f))a`Q|j6d|Yb}Ci @nw;ՕכypMQCb"iR:U*g{J)R;% )c[Hm-(#wl3Q~ 6ox `ךA7~#L5U![д&wIìEC5M)M3veqS#7Ԓs>O"8=ٟ,t97mvJ8lJfKg0F=" mfRb/%LL R~*UYY VMTDWdM" vD̩T}5@*j7(ĢL$tDٞa;G y>6L&iNrN e9kw<1kZ}܉nX 287cq.A%7]`x+Ǣ:>IC&: *fv;@;-"HbdoPDLDNG潑sNQb#hd<3BX) jT=CDDtH#© TC)g {}% UKe1SF^Q1&eCTJAC+ũ5ͭ{dmsn|jV%z.>6\D#v㱪yK7I E /"+&NNKaJ ^T7ѱm =,=:CcX n` : m&y)#S(R@(Hn]iœ"+ZZ_ '>ZEqTA:u+t:fJOb`lbEye^c1G1I$ƒ28h:2uju<6;ww>6<KM_K^OϘ:DeJ[aPrj Tա 5Ttc99@Z Ҍʳ {\&j9AUac:b/5i]7Zu ǐ@7iD'*E6>6`. s82G&Thx<3L6f,{*"~8QzT#SAKh(M}NBgCײͨ*cv_N_-)il $%Fٻp &TPڷ͸I*^„ڼXo&)GƁ-"""Θ:,N|@[h޶H5/k<ݑ`}PK:$Twh/Zj³J-vwyF^3CAұm+(ih5ڈ ΋+Mı܋ qjujR *7(; A4 { YÄA&; Tg:SC!,FTqRI)+˰{*DLu^t|WgcY'\;_:g] wq_0) B~ \J28Sv eβpp*vabpǮ .]v$X Ww%,*~*% hJ@Pwee[c܎dW$L# $|v& #I\v%$O$Y)YF{p$xdLJ¾$4$'G(Ǔ|( R$O'$<<[p[$.ّeI~iu'y'amJB{BIMMi%d^OBKCʂ$`CMbҾ(%% G$ٗd 'a+kr ,ِ8iKb:狏&$|lTgubç`j؛P{XQ=f({>5Ղ+/u$ pA8WE Y]Aڹb3+A+`?kbVf4 ?B1Aل׫4b 5Tp<3  bOAm2wmyЊR'*JŖ lʻ~4URmCU hUUUUUel0 o7~i>`rR>\tY]j8XU7s/o[47 3Ni:=Sj _Y$%>%2q_lUz~/9/<_tB@>7wRijI2sy%}H2-y;":,If`'`Ga4a7tksF`4X(=`b`+_f%1JF3FDRH%$8"^ :ViH - >C43%@b@ežr[*4.Ȑ~*Q5&bz@&XsF \mj jU65e;Vb|%5ϛi'ٮny6l(eQ.H-8ä煄WMsf^Y>L`r拻 }x'=M!ŘxzYܢ}TY,xBFΤX =I L-cz vvڔs:-Ơ fɢ) CfV4jƴFQ 5P\ތi:jZEo֋Eo -xOxy5&G;i-z-Puf)ۣqłStDpZͩ8 &8Sdr`wj 3mK\1Uڨ;\o&[lMlQjo&˫EW8T[`i]Wˊ^g9ِ;#͋sùͷ8z]?]2|xl[k^j}H햗duu(L`t[ 6`to)0dZ&\3VJMLӼ\)-͖r~51T5;G(1 tVs݁+ (($*tg 5!}Oz#Dv`qh}HR>;Cphu ao(~4D=!x8Cc)O/,FyP}.'B$ }POxB> o aM; }ox8B1=.!848dL JH2!\j+BBC[hJ6Dg} զ[C <6DB e!bzSQ^БZb]}΢)S@icF*=#r> TBk73fj ~TTlop'zQA#= ?'_Yl/vo<~ROUnTyXV"DP˥ϹOVdܵ°CG.vS^re) )gLC k?Z_)S9YY:`&0IȉT"5;[(ۚm=ʷ2Q2Jϭ O[eZ'Zibk#\+izv;zU3Ֆ٫74CLJlϔfP3Jt,1@i]yGBW II=3Ob6_{]ݾC:7621xr^^߲F=H>kmǻFf.EuU\wkfWo-\uol֨q7еQCNo;^j3 LfLu^ :S%U h+_{+NWm_8x+#!c0PL/LTBTr%@qA )XEoHIows_jv1/C6bL_ʮg?QDq@i/7 ur': a:<ص7+Z#dJ&M=z+ZFwp.TL- ?+gsk[dykmßy-ctwNA?3xc~JNǔRt\"Hi{~tlKZ쟤j LՓN".CCQ^cy} 9 =7Ϯv έcΆznN::gfGϹ38/Ch <,8{+Ĺ8C`7EV-󇙩EvbH_i`R{JTMCSkJxJؽZ]rOQ^A!:ԟݶ%eCj~h'h3? r=fW}[Wih..I5siXKq`rAMH4N94W_Țβu_Y=;ض7߽m`zI􁶶Rʧ.qP)C٫X/JA_^쎈!ș^!9/F^\]0DhduGCW"w+ìF3Vxd{#= @?kZ`oUܢ6/#rR6FثNE>hZF(d&LDND4H&2J3\LFD@`aT%yR򎏡y3y"sx.uj3AW0 Hy͢!o;<.\  G^%c@m!񕸌DJ/-ś"[F-Sns<>/订_;]S!cSω ְ&&3+yY¾v1"ÎLf4ziʮi3eΜ2U*y4O0<,]ͼl杳A(`^/zˆᐯmXy#OJ|}Rm:I+8Y\JX>l_aZd~#vg14&ӻD Ę"z'RI p24clmT|_?ZL+ &D'vL61ce:|,Ѩ?1-lʧȝ=sXpsv'Aa7|O@Ogn?0BOႿȗXgDBu,y!f#W=\SJɻB-g=0ROv"D{åX똧C.y^^6SR<|MW񾫾\qxfj<J=߰id=xj``H$Bf kSvTׂ%NfS݁vRFlӐ)攤$Ǩ-Gc_ U&*,mh'Hχo)Iΰ]yJ\E_уHS}$?( ֕@_WTZ?ψvPi 3k[JJKXRb싷Vh:MmFj:h52}>'});SNNpT@TmU*Afw(VUpEڡ{pBiiwX$*|}*ÏEOh@;ؗ=ʛPWc_TcFޱpi՜Y6ݷ{ zfSWwMia}ohm^ ;n ﻣ{|28zjs7/){?oXZkߒx|uQdʯxgpZd#<^ YܯԲy?cՃ-62˕< t ~% pHUةOsfsj[FO`Ubye5_XL$eq;# Οj^,x>Ias`%GG}q];޲fp:_'7fɋqz `988 qE80HثlLd#0g"WS8d5+/N@M9ΐ[8?Bqs!NvѦdu~vX2eZ=<">f9lV.09QDߍ쭖xic n'(9T<]]o|| _$@359{5%IxY]aW݂W7:W9'3Wj Q -J:\&n+T䬜^7K-.&$/`R2q{ ug?AH3l3FurV+Ɯ.9+~q_(;۾w*\+ۉTwvߣ>b:n:i"&/\4uRp&)d"-A7mU;1NN@kL)Sƴה5Mqq4CAT1ACa`8zQ>ơjL+2o=:AmC3p0USD*'3s R۔ve 6sfa bQsF-gC25>z\  1U@|keCqb tq"o oNƑ.;~?g!lͭaR>S !f ilWiPAKixmlrRRCX8W&PRC9!v8rEIf9 /0XU"sCz$\0 P9y-?i)TKsn˽+NC.~Hh3w7z~@YX1_ =Vy},݇zѢ% n>c{>gw8ck~dS{BNߕ{1>~ZW\`IۋAE;J FR_VlUF}$@"hZk 4<NF}{}'|ӗ§1NE#O<9}^'{}]C%d@bR ;OE PBvWIz/ž oz 6btC} 8FK}?|៽om|]E=oxx+*yx`.,18GyLMU<4]v„NHN";!sbtsv81r[Xm΍G 't8ӓՇ_VΖBn)e8⧑sܹיuruN8W~jS@436=䝁B9вN4p}>u[n]qH ;w:I\%) C>]F%Q9NgQ|,(665v־]fj:SXˁP+ފ/q`W־$m/+kM$ٷZ>z#1e)1kj ϑcz3f2 "q c^Oiy3ҊZfpqUuv-b+=#`''pc^ Ű:G8mK:8=:=Ůw,M[Pkoc=d;|b3v|u;YkH3v\gcc zV;V[pvֆV=ksofƵ;XkGгLSɡu`i"jo [/*sUKN'/RvVboI]&ІA;_%t Z#5t4 Wȿ@w؏^^_p͎ߕi-2F j<1ah_[ʮM u} u D¾Zcx]oт,]̮ѕ[~ސ + LZ,&Kt\ϫ%aK*%;>Ϳ®%X.I$|" ޗ`@;%LT2YF_stTuoHXtB_oˤڒv>޶CqHS NJ.%Rv!kRIp0*0uF;=ҋIJB}\5K!` _oC%^IW;;\WJ=9XHGn>M&Y4 ͐RѨh…0znaF)8TʼnVFjD<ٍN/ 1KanTmP^>)_/]M˪ ~F&Q8rsr{s0z `U5,?}?/yGbi9Kѷb gDQ 6W.9J|<9Km6l$q6#{ w` ^Ci{b1BBV">Y5`ȚHP\[x@V|'S0/}QT8$hg pI%ЊS/殜[5Ĵ8geY9groν#>x@Fx"ݘ{hw-ŋ}WhU`U\L꫱&b+*b䴻yE0fu#i\hAvx@E#diF㽦oTl& nl4bsaJ NШ6M!.>s*7GsqSEKD p2Xѽa=o6Li~*!^ɭf6 :)]n| mDE^=x q7O skYC eYZ:9ޚLz4tӐV?Q iNOTj}MgxkzWN4piG:&8>ƯKdi+/)3iVY_J4fwR4٠Iڃu|,i9Jgikh4ZZ)tZ+,N|2M[9A}5W(4ڠM?'߬K .w]3-|TK$Gff rlʕA&+ ^>Y|A ,VޱhHü.6*B:3ZHBD[ [ew&=B\IZł&?l# Om o0cg{.$3E}brw˷spwSp;T7ir-W^΍Kq]-'<{rMO~0pOo;s~mܪhȅܑF?!ƔeXOC w 㱷6j>h} 8=q褚݀c`yl)G_=|]).MB}ڛ`WKazb8l :\9D MMpiMNo:M{7̦Mx'Me7M<˦MX[w[Ыݿnec4FyBXzapdӀ7q\93+i%ےl,#`lS[cY?ز-$  R9h/GaBJBs_^/KH[JI)B{3Br]-̛7ͬfgm`8(UOb>78b nT<,9PXDaETˆ[c/|$ G^p'yw?)jS&d,YwSlY*㸌dLX}l'k2d*e)'R@ƹ,Rޒ1$MrD&rL<,hLM*2=l2qFO&ssj<9A bp #{FS▏r ]{P'g[?֩''I~.>a%ۭ6Y#V+ÄJɐ3 "Ǫ?Zk+/*yP1#) ӧ{Ŭ&uMFz[`𛲵_7P,-[g0Yiz~چæ7Y:H+ޭsL/T(ՔS%2+@9/1?jK"J (N89K/RB{uEàxeh/LR VHHW%@賔l))Y x>C_q@K_Ыt oP|gX9.ά1/~^b D!8L{XA=Q=@Y7ۨh5̥7S젥 FjD+5h:%c7$;(P\K[))8bDMQn$7[sF ńةt^ZrxŜ5D0q1BR)fscb:KբJ;PL77I1P8;e+T3oezm`~:ɪrƈo7 y!0ioi9wΟے3jO[oe/keۭCZ4S3sIu73#䴣GzMHW0)&! *G"\"6bX|S%Y] A8a&8 d>utk;toЍM@E0ypgz<.{H1.}{܁*b Qo~ϒ5Ĭ/K%QkFJ9SCHޣU#në_(Y$i1gyԁU32C3R8C)JsC-"kQȒ3D`za2K/3/pRU/Q ~BEN+)q,פ$ªq#,JÒ@*v )~Pm s`8YS rb@h^iPLO/}Cw.3R-}$4@h鋪=A%aIzJw"1lv $$̦yY!N1"2IH(0n`? X4=DAJ8DV\9YLh"FSc:fj\22yMӠI,= C&n5cNLM&E^>1GFC_ޕRۍ<(hn z25ӯ~@ ԘDaS~M(z/azc{Qa <^YA5$4\zd=Ct[}r6EcCBH`ka#lK z^}2(N~,d/$hn(ygSLf9`(󄲑E#'Dsčg2\f7 l,;QaourN g|$}>1/;?0uYȉUTŔ}ևU[ VCa VTeAWz|Sˢ>{Z^ ,ev5}h{ٔwngouvK#CfB=mr]}}WT)@B&)F{^dQK( !)__. FM tv1_ k ̓Uxu8NGS8QC|1Y 6CZuk!o!F-$ih=DHYϋ68pY} LVm&*9jע.VW=;K@Ӽu4y53is䜰TtISV=-c?p?%~j49b>5AؒRH}kb[k&*=#ӌY6b.> endobj 69 0 obj <> stream x]n@aډQI%TéCev㰋nim48姮ox&Y)y5Sz|/s\];Myʿs璔phx?b,UnWY{N9q.-ҢxY d\5gO%3+5Ƿ{ y6` K52 5r"_\%5%{oK,%s|AF_WB,B@~~(=^CMY77DxB Ψ+ΨɏuhYOe*/x/Jz h?ދ&8&?޻ү+qK~-c_o觥\߰%$?FפFl+ޣѯ8_8@R^sOp\\-ysZ$]ue, `+ endstream endobj 70 0 obj <> endobj 71 0 obj <> endobj 72 0 obj <> endobj 73 0 obj <> endobj 1 0 obj <>/Contents 2 0 R>> endobj 4 0 obj <>/Contents 5 0 R>> endobj 7 0 obj <>/Contents 8 0 R>> endobj 10 0 obj <>/Contents 11 0 R>> endobj 13 0 obj <>/Contents 14 0 R>> endobj 16 0 obj <>/Contents 17 0 R>> endobj 19 0 obj <>/Contents 20 0 R>> endobj 22 0 obj <>/Contents 23 0 R>> endobj 25 0 obj <>/Contents 26 0 R>> endobj 28 0 obj <>/Contents 29 0 R>> endobj 31 0 obj <>/Contents 32 0 R>> endobj 34 0 obj <>/Contents 35 0 R>> endobj 37 0 obj <>/Contents 38 0 R>> endobj 74 0 obj <> endobj 75 0 obj < /Dest[13 0 R/XYZ 90 311.3 0]/Parent 74 0 R>> endobj 45 0 obj <> endobj 40 0 obj <> >> endobj 41 0 obj <> >> endobj 42 0 obj <> >> endobj 43 0 obj <> >> endobj 44 0 obj <> >> endobj 76 0 obj <> /Outlines 74 0 R /Lang(en-IN) >> endobj 77 0 obj < /Author /Creator /Producer /CreationDate(D:20100801081800+05'30')>> endobj xref 0 78 0000000000 65535 f 0000101336 00000 n 0000000019 00000 n 0000000736 00000 n 0000101480 00000 n 0000000756 00000 n 0000002791 00000 n 0000101624 00000 n 0000002812 00000 n 0000003906 00000 n 0000101768 00000 n 0000003927 00000 n 0000005501 00000 n 0000101914 00000 n 0000005523 00000 n 0000007258 00000 n 0000102078 00000 n 0000007280 00000 n 0000009297 00000 n 0000102224 00000 n 0000009319 00000 n 0000009954 00000 n 0000102370 00000 n 0000009975 00000 n 0000011596 00000 n 0000102516 00000 n 0000011618 00000 n 0000013211 00000 n 0000102662 00000 n 0000013233 00000 n 0000014506 00000 n 0000102808 00000 n 0000014528 00000 n 0000016328 00000 n 0000102954 00000 n 0000016350 00000 n 0000017055 00000 n 0000103100 00000 n 0000017076 00000 n 0000018621 00000 n 0000103689 00000 n 0000103864 00000 n 0000104080 00000 n 0000104298 00000 n 0000104450 00000 n 0000103506 00000 n 0000018643 00000 n 0000028915 00000 n 0000028938 00000 n 0000029148 00000 n 0000029580 00000 n 0000029878 00000 n 0000048983 00000 n 0000049006 00000 n 0000049212 00000 n 0000049835 00000 n 0000050305 00000 n 0000066976 00000 n 0000066999 00000 n 0000067202 00000 n 0000067761 00000 n 0000068174 00000 n 0000075891 00000 n 0000075913 00000 n 0000076116 00000 n 0000076492 00000 n 0000076737 00000 n 0000099775 00000 n 0000099798 00000 n 0000099994 00000 n 0000100644 00000 n 0000101135 00000 n 0000101198 00000 n 0000101281 00000 n 0000103285 00000 n 0000103341 00000 n 0000104623 00000 n 0000104783 00000 n trailer < ] /DocChecksum /72F2D342ADA55365B38B0AA8C24F11C9 >> startxref 105093 %%EOF ./oar-2.5.2/docs/documentation/RELEASE_HOWTO.git0000644000175000017500000000435411757171206017135 0ustar plbplbThese are the steps to perform when you want to release a new version of OAR: 1. Commit your changes in the svn. (put the right version number into Tools/oarversion.pm) 2. Make a tag (for sources and Debian packaging) with the number that you want to assign. For example:: svn cp branches/2.2 tags/2.2.5 svn cp branches/2.2-debian tags/2.2.5-debian svn cp branches/2.2-rpm tags/2.2.5-rpm This example creates the tag "2.2.5" from the branch "2.2" 3. Use git-build-package.sh script to make the packages 3a - If you have not yet a local git repository, here is how you can initiate it. You have to create it in a subdirectory called "git", for example ~/oar/git. mkdir ~/oar && cd ~/oar # The following action maybe very long! git svn clone https://scm.gforge.inria.fr/svn/oar --trunk=trunk --branches=branches --tags=tags git cd git git branch trunk-work trunk git branch 2.3-work 2.3 git checkout trunk-work cd .. 3b - Get the git-build-package.sh script and put it in the directory above your git directory, for example: cd ~/oar cp git/Misc/pkg_building/git-build-package.sh . 3c - Make the Debian package: ./git-build-package.sh deb trunk-work The changelog will open into an editor to allow you to specify the package release number. You'll notice that the changelog is automatically filled with all the commits since the previous release. If you want just a snapshot of your work, you can use the -s flag: ./git-build-package.sh -s deb trunk-work 3d - Make the RPM package: Edit manually the package release number into rpm/SPECS/oar.spec and then: ./git-build-package.sh rpm trunk 3e - Make the TGZ tarball: ./git-build-package.sh tgz trunk Everything is created into the *build-area* directory. To summarize, you should have a working directory ~/oar which looks like: ~/oar git-build-package.sh git/ build-area/ 4 - Don't forget to push the package version numbers into the svn: Check the commits (git whatchanged) and then "git-svn dcommit" on your branch. ./oar-2.5.2/docs/documentation/OAR-TESTSUITES-DOCUMENTATION.pdf0000644000175000017500000152767211757171206021243 0ustar plbplb%PDF-1.4 %äüöß 2 0 obj <> stream xZIϯ9YHI@C@7'A^⿟bHIݶy3Zڤ.Nv{Kt Uos1`-a^(U?pUV95Ajorw|PvpL1ǏOx7x$8~..Ow/iC7C$WVPS_xѝRAuΆIMb ^P$ =cgGs?/'zLE!_Ph+s6JEO!O-8}g7 L~' o[be*VDP0ߑ'3e[Tnd+=2!t)H#+4Xh uĭFóxFElǼj&yCk Oѯ`NO^.O"wJ؁]#Ie&o8܁3{:qGu4àDLrw0h'bdюWm#%5 1OO I o7H /&zF߅a ݧy™7-oxcX2EB H.  /؎e a6]c1D@-<Ш79^pAߕ,y m%"lyE|q2Y 9=AHjPNhrGtRs觚/o\bv/Ûa ?z0&+GĠc@9)_J~+i^!b'$#s |7C*?88H\6 -)"+5`.3)9kG/dٷJcyc2Q{e?caȺL  : pђ.#MYVC[r*~4ճ,,OU"D-\<{ϥ#D3噾ʺ_@+Ym usxtvK+rArDw8OIO0p8UWA.i`gƈ4t3--`2:m9=E #I\ Tٲα@>IuMHok>>*_r7P6.hk}9N{nщi۟0 EFucy?$/~v ϭfosy.@\j[%(JGD߻DS̲K| T3{@Cz+mayȍ9yk fg ۘatV.W@c ìFF4 mW |W(Nۢ^!ڱݲװ"67u,w5h;?E 2x'ߩFT6f* +C6B)|wwܝ9 Fr7eNN@H"j-g g輸S|d(p( \С7n$( @X"QnOfԪ}hXEoPW+Șwz5wSfQ7)I6ӈWѮIbKPYt*_1FeB&`9#6@dD{Ik4Pz"$*FJ&\ќ$FRLy1Q+]L3թ\Bz,RfMOz=kOȢ6"ƬX_qǺ6xҳ(. t<؀G"c3bՔM'6Kih/Ea"i@> l.K]qkB-;}݆Ȉ&[+h1T%.,ypΔ4vf7ǰz ;g,fkkPJx ^KC 8l*`! 'J- ɱhhg7 Yo/ Z?7:e=r,u*w584Jk#l_噩瑜V{J>47o|=R<9vv9dGdmYtÐ>EUPe#D(^QV")֓*v |Q*;MTuuv, ꏗ$yuKc&$!jь,welˌ;h.nc$ uMPn[7\LAUSk`ȁx[C3VPż){*fn/4R)А{.Z.hU|Tz71Ɂk)uz¶m>=<OS #}Sv QҗgRH#7Vx`xO?5 ƘߕV2=+18ǏF\;N+ۚ endstream endobj 3 0 obj 2629 endobj 5 0 obj <> stream xZM ϯȹ>m '^Eo@ҿ_J)ɒd^ER ~E?7LV_~?]߫˯|}s:]˟Wt7e^ZީU;rYI0c'>+ ߾6niXso8#7߿)'w'Hݿ$}ާF<'@-ǙRKatCXzCEQ?mlmtw*>zC#ul7c~c7]6[1D> ]U20Kz=ͻTȀOj'଱!}#"A}2|Rm2KF~Fխsh(}?(ZW&P8pEFJ}Qqd&_Q[!2W:e2RSH[˵ISjv,l:$ *J4\DF@2ЙK66,2;}7mRzGkeAk2YMyB#~W$H~pmC/J]c0xKL-GfSnJ|cLn/|SO tȓNx!LqcRSOQ )L]DJmxT7P4arj#&W.f/Pꍿ:1G>}}WEOXi({|i*;rgl;X s ^ k>v48e:]-KC8~4&\ږ2ϖ_A"NF6W*8a|esZ9m=1jZu Uokgjme8lM4*E}0 k)lPMU(̟WA2^@)H#f$Z)Oe{5眅v>@ԏRmʚDJ`MWW0U@ hcʘ V׋FzB?7D,};B5OAU|uZ]SVV|;KϊyNn!OJ{Ln3eyU}T/Aϩ9-+-rL)"`/ U@ZRUlWTJ[-c[C`n9Ad6_-\"eWER;L|@m魞59‰(Zj @Ւ`UjkgVXȕ?U6K5[eR>j'ǵc1oA75u*%eJJ'ܥz`:ǜ{6Q} 1a{_54wv+8:2*TM"K%:K Vq8r:ם.Rӆ3C:B qG Ԯ(* n6\bp0&]Z] EU*/\,n:P=XI\OӘ+I\cyZ?Pލħ`䢮!H9D<%sq&fE.4iNS,L/DJu!'GkBUͦÆdA@ C90]߇&m$|av?,-&36J1o!ph`C9԰9G-eМ4FZ1"+riQ/E'9P>o[p; ~A[Ǽ!M=zld+&`w׺~Pzma+ oVl'ˎޅ*H|m qpw+lH*I|J㕂፭ncMz֠-cf,'> yNoWt%heMާ>4wz݋# r^(((Yl+n;#ߞ3CV:5Y3E c^ptLE.p endstream endobj 6 0 obj 2738 endobj 8 0 obj <> stream xXI#7W9<*m m[ 9A`f.)U~/sڿT ??7ou?~yr6V)tU.8"4]J>BݻQA:V, NG65C*%ttG$ m&;4-DXaG"iW ~,3/~Y(a/BnueR_>9RGLԺ7C( -5o&!'OT´D]-Tf.cmTŸJV7TukEȧ1a8(;$KݕwMnX%S4 HYОb=!nR$_C򦋯'[= /oQQ H\7d;=aoUz^Yx2sO53WM`G+䛟F]]6 /_NicL_ WlƃbH 0OHWYz> stream JFIFCCP"  .QF\,,g/JTƯllcM lrIƹlŢ~5tj9%֢6_^az-5_eٟ5ng/@"[4jvƿmxX ׇO)V+1bŀ(|(PrsYekJ >sւg7:ƿKA`H5 }KVƲ -jXHSe|^"ylHڠ})Fq݃ZwtKk^tQ,[{iM͗Zi'Nam q eI/i1Mo&$ԣ$#$#.>mR}?PmAy*YjdxΔޜCixZcՊF8vqƺcY_'޵HӃr+|FUA__w`J[_K[v 9o%bEoKآ_E6>/ =J'7C\OFG{A_`!?9q~*U#q^jye_'fq ~~P 1܈T_żeB[*  +|*  <İ+g[#/GV{,rb}yr +NYٌYf:& ;1É;|C[v*"ݍj38NNf0og #uy״Jv {+. 9Vjw=z@ 3d_( l]c9o'}|q}|q|9XW>g!P 4Qؑؑؑؑؑؑؑؑؑ0OL@+5`L_<l>O1K:ׁjValNl~nΕ: ϭ'rμQxfݏ]S_;j~5t)꼡꼡’b;y/OW2zTU*n/>{=wϥ'Ro6 旭 FUA_^ӹ(bx]= Іq, wP#U~)a&O /0ib6 po*6{HMsak֭0ڴV_^~FEvR 7ĭI_9ۜ|ub3iVAq쿚mp}ϰ$䂠~-ޡPT% oEB[PT% oEB\!F9:i |ԁxGv+1Z"O+`[-~T6@&"gDɞ889&x@&1 bA#jQHd 'Zx=vG[X))#h*8PmAy*#)7/&yK.=Ӯ#ַJٯ4s6ϥKQ[v #h*#RT~/QQ2j2j2"MFD&"dddzl# ?107` @!3FP4A"6Kq+W? If4}OG|(L) S+Z CٯCIBpCb| *)&!0z[! Iqy#VT'ثll+Hf=ۂ(3D> h۶̸TGZPlNMtIYdF5 o߼gD 3:LM({ʲ+qG/H׺eɎAʱ`$ʪ6 }hv!׶,±hiI91퐷Bo,٤D#t3 'FOKU7og"z_)3rs]/$ as|lzd8q`Dtv V {!q2 ̔h0CD˭s\E)lk 7]n}W0jC1ZH1#ŸB>`YYYYYYOd=_*, ,<]'<&8_@>J+-:fddZyOJa9P0EFFs3M 4Ji"묔kkkkkkkkkkkkkkkk 9RQ0]5WPĉʖӁ[齛'Եy0z`Ƀד&^L=y0z`Ƀד&^L=y0z`Q5dFK~cǚc(2 Co(\A/m.lX"pe| :~+ 9QR `T!=orN[I,xx=fs݂u"@̔lNs{. 2?͐TxIC,soK-x*XV'qdéF'P"Nn:.5FDRK %]fl"0e$ҙy5cOέG%I9Or6Xm~D,N414! }p+y\YE-['\YJodEs ,α9pyZm~' 2ekY ۘMm4fKIUWX]` u*b;4ܼV7{,}`q+m#WÊj@{[΢&ˆ㓡8A3+řS;L$E؄&- ŽM@`eIʸ++ C ~k8qJ G5^> P0Io;)/bw1 ]B!WsU*bw1 ]B!WsU*bw1 rE'W 2\Жg8* QȘU8<*2Ϗ+L2; DBKJ=QDԖ2,; %I~<[ϣq--,M=&h|OoB{xOX*Λ:y(hV3bkvs'2:nGg\1@|u! *>*/.'Y?$<ܗJ㋉C V *~.=P5 lf><[d+t-[jO E@B3܎ B{\ {⍫Q.7k멯k멯k멮k멯qo*+7-M Qa!#1Aq34RT "$BCDPd0St2@pbcs?f1)lx:]rsL7)'Epl?E2LZ^P)F(IyF2+%?ta}Go̪<$aUrd0섊EM[T|:['.p' ˠ 'H |UIUM_1'd+ciP^pQ4>K¶@I%"틱8IZ/ir.PVJIZAZ;`pruC{ Nd.̀וaس{i`+O\ݠ%I*uv9۟^jcM*&-$sO#^ 45cs|pŽ7iRB]R[D֝eBJqzHf l-j {}uWۖՓP ;x?oNJWr{ovZPݵ\W|ڀG$؈I-X-D2{&겙Cht &O/pn:͉8)+IcepZwo'5>bK|vU`/zHc Մ&ԛrny:C33 lS wG '7{I#<~' MBPǨdJܞ`wwW3| :`97=9¼cx'*NU19Wbr^1ʼcxې=os%k&+ofs@g~K|0xxЯأᦚ$mN:0:uܴx{޾C? ʦm Cy=3-qy~cOHomMY+d+:U6;t8'܅͇U@ oOoo S la'Lاp?k$R_xQ7nERR 5k*'(UNP]"rZ d)/\rZ5k*Jj?Q !1QaAq#3T"$04DP 2Rd@Ce%5ptuBSbr?ɜj]'g0D|GSY1͔N=Cډ"*btt&F;#P+\ngqbzo%鈉x?'Em@&GA)*n`9f'M{ A%7 $/jo2iJ"2_C&ZT6.<:lTӢ%A;C%'*XE(\[3a!IO+l:FK;L3ަeoh8A$se̿eɢ= AF#7(Li:H:Mη2/i)N.MuR")&Knr@d8Y w L">ו80™L-+&5%;eO0V@$\' ^x\5P4^cp˂rt yS tz)hvJNK\i6q9oPatU;{|}3Z^ӏ>9TiQ'k㧪eQ*?^; fğ1 ʒvw ^[:7L{:% %VTۺkeI4%a;"ykFM$Anf1/u%y[j_70BM;V 4iX$ҰIa 4!&$ Ńǝ`aP SE lAҥSzu@4YD A:'6G3-t'O}!t RYQ!p˷(Km쪼֟QuDXEa+ :3IwV)4[]ݲg)*גW"d|gqw|#xPAۂ^ 쓪PHf#ɬoEOzpJJJJU@k=7r?:B &ec'&wcplW?^{.HxKw?ğ%+Ɵ1ifΠ+[u:~mt1 9a$OdoJ5>;{'N#K];~K] o f&KIKBfc!+d%a !2BW  \2™)?x4;>DTQ(⊼  ;, oyK( pGAnj7TMՎrrT|M}4ӕU|Xn|,ƞ7zY$"WJJlPFn&?{*4 +s]O;=Eym 5xJ%++2XMM#Ǟ:%9.$]bQGlXNQ9m!2CAlze,]c? %?t(irXIb%D)W`7Cp# ,R$H! k   $!#4"%1356Dd 027ACQTtv&S`aq@BEcsFPReuwUbfV??<>q~!hʍ[IX͇n ^юc8bR}epjW7#+|jx0$pP&’1RFA r#Hux| euEs71<3ڥ0e["Ux[dp>gT 9U;#r Iu]\x|$'F| C~G +SP8 A*rj[$!ftFPH;^,2蜳cÛ$Ȍ\E4qGR4myI:eIvW8 QA0^ĥB`zK=ROu}QRR6:+ U õĨ 4! eq533B'=4$XjR):18>Uz76s Zv{}[#G: Y'ꡨʁ#`(FˣMFe [v8LV!j2X1xrf}6=l}D;IK.UM7ݐBK{8(8x\]PRuѸ0$d-2,&eHVc⟕E{CV :=aα!x.&Ұhi@ ༺B@X [49yyzr+F|>E zv*kIްEWA'gYxL$9^#T!M[<ƑU.@ U"E-}ݘߧ*dG@ Tc*Z`|TšO=4ܯip:ʖU7E' 0bEi! j!%+yq^E깚i&:V«J5b4GG0Bj2e\ NC10K t:pjt5̉ *X[K z庙DM T۴7+ pnof*)1W1Q<_) W֦vj~l h5fM8ibځc ZV(bIYP:ZrXNU4FTڝޯBA蜳cÛ G$Ȍ\EcM!hYXNsnEeAOiȸPZi& @#! Q.{JTFJZ|FX\ExQ6n(QkYl"k[7u&o"K(@X@Θya/XCWsxXh],k>e:@ G / QYJv $[bKÙK2:d4{j6i wfh#tkP^Qԭr42]T1 $[Fd.bvZQLUTs{ڋNM8deF+B#Jk40BSwO,t-VR4 Ch4ӹŖ$PjB܌"c\ lOw9$[gڏU=˴͈HH2%U8P%$nJ0LL;]d mU#ZWUeBۥjV$huttvoi#"\K1I%We<04xxP8# t : Y/!94%c_)ohse7Z>I10 MH@u'P:^zI9zP huV7vexF)*袣++Z@Uz}`8٩`8VJ^.덟0H Jl:bm*miv2u&^\ީlKBZ" ;V ddY?T "b ʨ.fzXlVHs\ 4?~&|88Ţbc},m]FO!tb]jD4M/G A-d+!. z`FiaAN0וX%ĻAtf*'1mzG%Ö<*1bP1:t8\FD2V.P=݂#植;1RCCli2SChzŐdP@=ثD PĐfl(CDUՊG 5io@uzTabuFXӵԮk4H]c9SPvrtLιW !U(5xTS[cHI dl\*1%3u }0 Q:KTr?#7!Tz^^^0W+,npj_V wmRn]@d5Hpe24!j]F:tG2hʼf1- Ž4,`Q0 FXtzbe[ODtO!ŮfxT8."͞;Swn5`iGvXY老dAk5 GSK^kD#?SU9Etqpj4[ZKgxfԠU`&gme'RufF#ڝNU5 {cplo/,ZrY ,Q-E׮8T1A3EnKGҥ b9&bb< Z6 \bUoA˕PУ5J:)Wiιf')z#XC^)}<8]2m)M64ylB46(ZăgP0bc,յ35k="3lϦQ7Ps E9]0k;Q&UG);: APZxX8$DdmFdBX6Y"e(aMTDjoY oz2XFP*0TSu<1ZȶS>+ƴq<nN=ņJAͥ_Hj^->vH8%4]Q^u5Vƶx hڲgCH 4Y"9 k Bj,X cU 6=0n-JLb1~ۗ RBj txlʤm57;Me WUy)WMI\Uz_!5`:  FOIET-3>idV``.Z֗>F0v&ꄌ+HgԌ2!5tCLsZރkn]Ƿ9fg. p{dyWJ 9.}I%–g٫]- 91y^:.9@u<T5D29Ds8M6@C~ Š:C4[XNP 㨙c?f?UD8ZmlmIvw" DpZ: LaFQ ]uK2P^طS29\־Scw,wZNk_)ܱMk;;|r}5X例־Scw,wZNk_)ܱMk;;|r}5X例־Scw,Aj~}'G욍r{I>Էz M-^i%1@Φ4 %O c0A4~vYpJ-H{ߜbaC )D "6yK\%PYm C7#@hL5r7ֹu󷥒g6a^O/:5hkCI?9ۡ?q67:͈vx9zmD !#"wf$}yɭlN(<@lԉFQYagL(&*G74ԽwlzusylZă>Za. %)f"Fs3b?ͣsR1/чMl 1nIt#fSocXBlƘ}4H4[kaSd2o%)g&Btv:.f,sx !Ko+@XuK=q/V6@*!BE\ἦV(-+ WԡT`?@HT-D퍞Q5{ٽPٙA&zU{U-8*:aȲ \]]cPJ2NH9-FI %59N̹I rVrFR`ӈ=]'J9&M;eBAw/d=gYr tJ|\|,{CSʀr$Zt.>[>xp=i'=tTLtD4i( ~sЩl\vL2s{mniygތz}tdSCk!I'" Fu++L2-ojӓ$e6z㗵N֠Sї 1 r=Y!ٚiT;g䭻s{V?o{]ƽU8`!] ˍR,"6!a̷FYbT,ur7{/>[>xpGK_PpUcSHTV?Rކ~sѷBR^~:pӫ 騊pU)/1#1U#q$CjnS:d7BN*>iR97  ?qaa))]*[G5|Qqgep\R!\*X:IiY &Dh%<ִnqg9 -?gN7:G)qF[ s\.-=p~S]RqH {\1(<@}ˌnvwӈ(yӏϞ? dE! 7 TVnܞ뾽M YL^ @ఔR+\ub\7k Z&}](vDUzpB*-$ j+3ce;Tbd0(ᄂC:P$'I({# 8==YM+4x^|)tv'nk!97 O /YŤ{-t& Q4D&DUf- B!mR3l/,63{B~{/c#-zyxny7MPVIB6בr6L[Gnm }j]bP@ID<I4K$$=J ~GtwJ ~GtwJ ~GtwJ ~GtwJ ~GtwJ ~GtwJ ~GtwJ ~GtwJ ~Gtwףٕ*MX;c,0Pm |8g)G7g3gt}xQYdC:)! C0 B\ $ ?빼$" <?]h&Bze|gބi%tY02!c64>.gVJ.Jo6M/>cDiߗ_t`r7[fNd _Ɗ Qi}z8h2Gd"\PiO;à賓zb)U]J:l52! ,a~`'9*wT)*Lf*i/)RjKF VN(<#l̯d,`;v]׷I5P4: '`=%׈wZ*wTBA5E@Uy=N_KhiP"3"!z~CءP paQgf P ֔pӢYTqUyٹ8 B+ sWZXeL##SMy)16㗸mmd$")jelI2rq*T9m?4! [t mI0@"( Pvs~g9=iuf;>;mN;>;lN;>;lN;>;lN;>;lN;>;lN;>;lN;>;lN;>;lN;>;lN;>;lN;>;lN;>;lN;>;lN;>;lN;>;lN;>;mN;>;mN#+c[9B8C8!hpv=[pZ% 0HuF]n c uǩz¥_lHSvN1h#LkbV'4H`z{]knn07c%yfq8 KiUSVsHt_Yv[uv>+xac)&:΂u=7t'+OS;)Pc N2gVy 9) |̈ĕѓ^$#("È ŗƶf LrI8Hί*8!B<9S7)„ls'=H㒿~9}r mFr~ѱܦ߀lw)!mFr~ѱܦ߀lw)!mFr~ѱܦ߀lw)!mFr~ѱܦ߀lw)!mFr~ѱ, g2p Mگߚs_^iݤMJΌ!uN$-[\-cK}%_)RITMIx3<9'،1[wz:*T #48u B FôKHCJG^Hھ}^SFizIMqs~߷C#81HnT~ָ(k!s4fȗ^7Su QaRՌ2#4:>۔4#_gϨut>ʛKGprt+-m%e3jȾCH "͕kMIj1pY pEL)ٌb&@@wR_c @ܭ36T9`ֺ 8G#Cu L()4u唺7 쥶/–d9pji-&{vG,9!Н@0 ! =/#ݏÚͭaid@#$~i(vDLS:vA\ƭ<ÏϞ?=-MOfUktS*(bgfëMr ;sG=L 2!P 3iSڂBҖTAa!$&(CCzIF &T N-;XNT⋨r:,kAxd -[YXZ]j'̠[Nfjz׶q)pJK.\OƌDAtj7mw160C=dn+rj%)3= 9 Ɨ'b]~H~5;5e9Ru+ S1_ 6(L;]25H9k"&eysv/7l;a̜ӍM]P*MAk֥/iw}ndY}Vm$^BjuV ]Lf{#0kᾜֻ$q 35@ =P\ &}2c2 yOdovZ˳K4;srϢ4E Of翯z\u&m'~E nEtq n!Zf1Hի9 3_/H/r\SP+ӂ148VanyqÇ{˓B"#( r0B+#PWxDf(. E"9-Kta8aN&7N,!("!(#7f4󞍺 `e|Ҽ5㑟۲/.)N.l6bmTf|I{d?5<1& ߂w#ܸy vA_ [G/1EU&tzۿ=[S隥o-os܁$:A &(r+с`fN`Gpk рQbraLi#'twmۮV\vNF.`HDBqް>cgx8=~Bb3kgs)2 K57cZKuLuIȆ:Ai\1k+2&|ߞ[ `bل+Ch>˯=7.c+Id3gTQOZ v9 Ԑ;Xi/ܯ؇Ekgz/q2f ߝpZz-G^d@n 5jwXuwXuwXuwXuwXuwXuwXuwXuwXu$ m]DFgdfMeV(#}q BhFK7U,)<“J#I DiFgdZR4E '"bQ}o'W1MXs/d_6D}ƦΖsՔ  TcuoO$,ކicAEdN3zO7_9)4)d8oprUҌ$3/m7 *X%*2DŽp$ԡ"3zF9f'x1#"]6<&XYh*I zIa鸎:|ya03 hG 5,ޤm;h޾3~BBZ J@cZz=yh{gƠJwrKIgu us(*OƷ{?mcr>pRx?Ɯ~-͕cnCyeVo)4c,|{Bgf_{D"WWꅱm*U#D;dԺ9N]J\QeqAN)a1lNDM$E~p+B )PeP4󞍺 =?8J8Ο$Ԋ ?4ǀh[SyͮpfͮRz cvv%-s?!ku773? \5s3] BZ /7t7p#t%d@mw5in>[>xpŕgʋۑ`БDՒ׭qPgSU`aX,do'{qrAs7j BIpIY3/(YJ]ji5%>KJ}g)rcPvB 8;`I QTU]xlaI>&4|jC1O]查T~Cjh^߬#5!7I*JDS96Tb#xbҔft@>8 0%)21Xh6'6 sMKe&oa(ו 'Ppwq}2bX:ӬHT\<S 6>XbVYY&J5QБxC9-ᤓ45O5]{`N!NpEx֋Efخ91uAL5GT(:?DVjC[>;nw?FJ.j](X֫U ojȪȂj#yqzzJ+gI~#hK0װ UiOkx5P;9[&xa|}|甗Vϛ]bD+[*6"SlYkDp(22 KY4&йK\CW|AvSY'@/.A,)K @2K6Y5tn!T xȖ͗_2hZE7g ҟ@`+kkJ3=iz pё$i v:5A& {w Qoo= (B2V(a,4봖C4a x[sj(3O)ŇF@sѝc\Pㇳ׽؍οSxEӯ e×fkFcN) 94BZ*"L=^o,TjLlP3\{aI©ς^f_x%QϢ ~BLWp} U7|&渄61JC_zR4ƩteP U\hӇ尪݉ϏiucX{u"sb ˯ێ) xTcq/Jl6 b$,ƿ?34^BoIrMcϮB>rבu k`A YuãC$pnwHI8=8l8beF%qqxs:*)r滲a7{W8#ʊz5P3YAL$:'7/%H i$9 ax` 4OB1yV<8Mm l̥+?~sz,X^ 甗Vϛ] a (ekI!OHg@pѲ\ x%=I>T୽n[uoj'FfVʶFwt\,NӶ̱Èv1j^$+x3Tm3[s]s{@S(‘Y [V+trMBGjwsMņjES9š݊^zٱ%Jk cxp4Q宫Hmԭr H`gdR ֒ EϚ^ܝ Q'*jw64e<vCut:K051Tl-tS+\w<M!<ȝ6U5{b '-bBp vtwKT+?-C/iHpelc4#2V#jNJQe׽!qwR:Ev?P*04.Qkk`/%qnJܯ@H)YeUIRڨ}+-^ AHi+% %jQL6nx7оΜtrUB C0F;C6R$:X^xTDԗ"MÒy}G+ p2ʢJWD!J7+)A eI)%M4I,e"]Lape I$cB9袅EO[q*52.T$E+kZAxqux_6ʚ fdšȑ"VIgW,# x4wd= kc<}&C'ZúO7Sÿ́Lk64o( ؅PV &4.ނ Գ?=0)W/|z`R&_h>M\H|}5rGǦ!jL Cɫ>=0)W/|z`R&_h>M\H|}5rGǦ!jL Cɫ>=0)W/|L3IR\K\sATD)vtCm!' K.*xJANDQSNO4/<|VEG/-FpXkc΃T&$Ej^ C{n׎6I6I6I6I6I6I6I' ELEVD QZq{`cTc\Qnol"_{JixcMͣTQO` D@Ќ.ÍDae^wIU΄3ڒQF cS+a8 45z:I:)%.Zr$hEC1eOExy|5WEt?fVgS+M0{l9S.f^ ܇EF9ҝ7KGX.̸(/*g:qA)A͑%QgN,XAٴq[~iG4ZOæMYj[oCg֒kAJ ]drn e~c)r[y9rN*5"U{X 0HS5b$([ S9582Hi VKd߅blW]W#v"qYZj$a}{5qaV3/Q]aoJ!Up*HU8 >n؎̊b)a$"XbP*˴cD{5'&:gei]B^A) J;0;~igH~#ꕝV)Zi?= -8#DbYbY[8,:R̵ P>SS.1mobƖڞQGӶJ"`wb#d-{ixY Hvid 5AAYZx_9v+J_MH"K~-ju<6kgu9 {)Lpj-iSXh8 I:Dq<#0(P3>n oJЗ7,LUS]Ȳ]\u$M}{A ̑&u9%tc3ɵ(c`PV \X3TT5C.sv]P!v{׸uFs;j !dN魦rm,gfiLoOehK> c82<O=dotN:HMfV14fd.bMGGV+K&fb&(1B :!`LUsSdTmF:jP4m3;Ѽg%X$"LY!NlN\9^j_7??r0=ʁ2"^\巂||i+6]kXGF?f3w#h]sZ\ş2'!C= ]Dl }׼ 1QR~ٝ6KYSPmCqIGsNΡw&Ũ$Bw~nk7E4{[*yXB%wA@[AmhzEjap:< :",{|l "4QJ3klZ3\ji 0Ht@{$j4U1N^(ثb/ZWt/yׯH ~{֩R^{Sb/QJnD[9:9){QG aGPJ镽aHN/Ir-h[cOg>;AEU4QJNʫ>Zr%Ž':NvxFE}wk(>a1 jJQ)<+pK.ĚQ3D Yrf:[+ԛAjTQ8ٜ mnzI#%HFxLR+RUE 0k+ǘ"ifNQE"KSh:*Έ!lgfTeQGTڻX0Lwa%c×1Jɇ/c_q+&V>L9Ĭ|r+~Uym5iFMB q֪ևgY8,Y;o 78ʫAhO(A밉kW|#6^2,$#K_5>-]GgWЛTaѠ=i'=tgAbggAsW]ɥqC=ÄJ'ܴmC|47VIĪRY+)vJz5 uL<ě D5| Qe} <PU{\ fq :)hڸ]>E+3#4ĥHcA(iF[<4D 0.1 2I"̊T9ͷ!{3 jSD-aP&ԎsHz{m `Xβ*%O Si".LA^x/nl5O#NOS1s 8-9NAx̱W7I>:d$레ZVv~ Xf|\m@#BEAuB]m= J,B~gK- àtmB iCGsD}c'Q?(g:Ex8yO+;΢~Q^>1uq󨟔WwD}c'Q?(g:Ex8yO+;΢~Q^>1uq󨟔WwD}c'CbP*Kryv_Jt/op˪ɒT&KHKP1%4!0<06DnVbsӳkJ3uLb#pBbTQ}d^d3ЁmCAF{u@JmpVSvu }勄DxDc%,B,82/|Ƙ[?Tt07R]z+v|w#KzYS:?*&uIUuIl!$R.՞[11P1V܁1@{Oz67VB VĨ-I~cz]ᜇU1OӸYӋ8+": +5O w]{y0xY!l5>5YQ]@ͳe9vrirFdv,.m|%T SCE\ІxF[C]neowVx]j4vw6aH^j% Ƒ*jҫ땠֊+u=,{t+@6昡FR.M҄{ʓ_MsLGm$15Ə+pe*2O?}}HX&gxэnO LE~=?:_'EgR}ۃw-_]q=f8Q4.W ʲ<RYr(BV/V蒒v{zIR(cO7 4ݕFhNtJt#KtjwM53J/KSZ"!Z$^#P^Kj ŠFZJŜ (j : $F7ͫǍ"P9ZۨiN"qȵ+F-.p6z1ǣryz7/'ǣryz7/'ǣryz7/'ǣryz7/'ac I2?2 !]rwqy\ %y5_5@ Rbnf*Y!)n.7TuփSڐ)4-Ib8"bNԔ%EQqM4z $o2sK-#*,6x98I!Y8K>[>xp㦢˯FR$R(,e82C4UN>N/3^Yv]a+p~qb.3k+Dx^bm-Dݐ[x'_ӍY2vAo^[zg1ᱦ r5WYUȶ s.i863g 9(/eA7!N.v lNmM%9e؂;F䇞F-3 \6R!F%9g6* B9.L S($CnK:dA+_8o4Qbjay e|yur^+Z:)\]vƄ5t$2Y-۱1ss92hNA0@aq]۹^7r}@lTJřo[4{t*GMmbր? 3USI;0T*15Tz.ܠ~I<8f8dܯs\R]V3CY*Qh 䠹o썣2A⡑ o_ӍJyTMY8r+,ىΗߺ.>[>xp=i#=t4jYe:$3ul֫ +俵z!frh`vJZ8 * 9͋.@=~vwqQT =ך+$@k,3O@mʁf@ oQ2 㱧`-G'%ggFՔ94)\Z8YZxm<]bi܍G0\ݖ]C@7sޝl P bU &hKȱErrWm,Ï>7AދjZƷ9Z !bz^ـt6HXns,1 *8-_B.M˒X3I@aX k4$i7DL|FUH݈oMrx q)Üsp?i0dXzU2dx5<OK 7V5.3.ý qwgp&)idEazP@[v0yQl.MNU 炊,nNƹWa&g1et^ٟ+u7?17Y[ј +Nӂ? ̇ӕc:6(F}}o:0ƌ cMli S[C>q lErܬL;%À9gkx{-ks lVJ >fƬYڼtu oz\~Xnj 'E96]ءNrc;[5-w>#^sچzX^: 2#tud*3#3FN 5Їn7fd 3B D)Ü`֝@O4d1<DbNm3%h$^j\c„c:!.`8+$=XF~.c#-HAWeU[Vzf\%TfQIi%Zd]jەJnmlrӴȺ񃊱ɦi!Ԑ43BdL7}^H4=J' dv9VV@Q5 9dQ#taJQ::V`0]Sv 9 kQ gqDЌ8-eJo}V^Ȏ0(kqԴ&;㾧|n1`J1P_ Y_i.C]%[L  QXY!˄D+&i_RSr*4WAxP8HT!jM"i>ӊЃ+rpzj%fWREќGuX'}Q&wN*&WNa9^ R+Dp8u IlE#eɪ +%h1Fl}XuY%$ᦪS'"~*asތO fnyf^'&=ҋ1IԞuiYl^3]ϣt9وm]^xxxf;krupLS|u 5R|-.{F1n^jsXNxp ,sj&R̢;REUɊWunF1 +h'3N Ĺ3b59èm=+5CFsO)esb#aJ^ ZUHhrP.)˶G3 S!D3 E49\\5cy#. "xHC"aFBc,,: $nV\&̪oWҼ?,34=NӃ$=:_u vvnA]_It*8S7D*deȁĵx ZBMFD:5[.G球y}нƀn`&1͞2#(YhgG8q/EyykikvWHkIkGR٧=?R,* QTekhh֓Tzci[b.R+tG(xD58=Bf-IV *W`u Y;%IY[K TEV"whis U`U"~" K % eK !IpH8‡gNtl.0ؠ(g֕{~,*tfXSsAkeldBx,0ByUaԓ?вbݹJ8C:#SK9r͖r6챣&D&3IՖ^ٕ2A/qjVc5<@5bN("PFhW5/05VYwBAi5$S.t&Nj5{T&pb3s@H2":9 ClD!P)7ޯ?1w,FR<9BblL-Uf!,,;bmxp)QZ^_ %*U CLʢࡒ 9f2 -dQʸ9dA|wyT=8lVma7Zd Q4{j Ġ=KQ 1/?Y_ LT,H)_;ys3HU<Bj,*6胚99 ==ۊ E]{}?Cmm} dc\:UF84+9k/:\VY+w.;:ל-sَ9o{ C.ĿlVm^giq3Km e5I#IITNm#ޥr˔$I6wAp ȋG(Lx7cKYEikGg1)%NP! +5lu tEjv-H*ӄ)X;ZǖK.N o{,@'} L`àk֎O c.vCʔ,L(o0ߢ`:oqvIHe9i yXܪ1COVl^\>(ZqJ(kOC@(oj }a4CBW]QXEV\5f\h,,Ϲ^!Fhw r':JW*<397RE%TNuY } Epc'.Dt\m?c;?S+$i>} uA|$ dFVqy4tGza>R%NTZ,IS /1]\Jz> Kuʶ6ΰ,R+KE\W&r~8d/8wg. J( f@FL Gz,FPC%!|rٛN%[i%*A7Qg璏b  `ȫ +Pq-m]8CU *Zt7琵K6eۆ 4hw(D/XWnԌF2Ǎ3Sq:"F1!9D<=.r8q̯H2 !zd 4,{u M,eV*p-NkQi!e! Whfs13Hb>*Nd>C%8AjPSKF'(5AK&:8y 4 7-uR-Ƶ&q@PWUΙJGeFbIQ;SI彡l˕^ow ?Y3;W r;%?3Yd3by(S?koq` u`M}9;'lIFw鬊|\ 8|~PDI*:M *$1$I$I$I$I$^~0CѲi DڪQxYQrdeB 4)60qϓ $5+*d+3̈z4M:]yRy ܺ YZM;3,(QSTH)za<55rWϔi'}u>GS G⟎W c` wɷ/_w88E"$4b@'7eNen im^|O$NŐSAhfX& }Ġ?=v#WNfizB( ڎ ҊbMarI!Jy wkUBH[IW>6D 79 cVC2Qw"). BYt`-L[B9FShƆBVYtFN2{%F .<>{&jb T_3^`I+^]0ptr!;*|2^ICL3l Q0XzwUFRvD̸MwHp ؝+2H$("\JYeނRFMSWI9z Af:K\/89FG+NS\¯8"bX)6Lg߻6(PBBbNj@ m&9c2K Wԛ(sZG-(t?*,Ąz|`T%+m {ջ[]5.a5t @ [ ]8Ǹ1bƒ]?5_33xz!EmS=BWHxd.]pЭT_|gv۷nݻv۷nebIppdFGF5U 9~Z#[팋nB (>¹|5%Az0^&5=۷nݻv۷nݻvڴ3_䴜y0Ս&Ռ ޑlB㋂P)Z%3\c*$uLT)nБ5.'8!A=&R]`|N|4rgEID]T[=hP\;FA1_"zi1K[LBMh]&:;W=#?7#^Kѻͯ@i y6 ]ԂcF+ѧɋcRS~|J VQ A~>{")7qV"G C"+Erx )Rߞ_t[D؂[èI  0` 0`!Sdupg}nFDĊ_[3a滗L$ $rDݥvQĩ؃:Pᐫ4Dq$@']ݸ?X큞u+`8NyW y2\FS;E6*L&MYX;2{Avn*5Ԭ)DNpr̪ B ,|NA 嗘gQ/>O_ 7O 9VEp Tӡ}Ե)MlKʁj:`%Lmcvx_O9ݤfͱ7368=3l8geI9\J!jH%a[jHe,3/-2}+SΝ XH 7ToHI ZzL`y:C܀2 }Jz&cڭhTâ'!3:a$ :vc-'o]4gPf9 $!>eR}tߑa#uQ=|ua-</a{k1hXcH9>f`bˠDోR~V2$H7]"G,pd$8'H{2:W;q5$<J1C'Hv"7qDtaI?*u4sov*`~PKOP>?.vC>Q2]⧸հ0tLyy _lu1!SFj␫&9~,Hl !OQ ՊIb#e5i1@-U2*#><le8K)5F`BYMuzt$` 0` 0hG39hB _Y@|;sp^ɣa''GZ"Di82)a> 05Yf̹ GK**SʳxupBnܕFZ%qN`MTz0c^'wu}Ҝa`gB?$|^<$LGO G`c42U t>3j)vJkH]YTk5z[<%@69cIƘ[C*K&lOGn,ɍRJ(V]) ҙt$p}؉ @ӣ^1ϝ{97"2 }#i7f1D4 @ @>͉3~RAM*DžizCIr PĖNmW#|5sC8\.O'˓&s0 h]1'٧ p pB tϐh9kD:/+]dնg9jcnT&xu6?lةaF0?Sp7"ʅ&|.&*zmh9$K@]*5tca9vjj fY:GhskJQ`+E˩15ǭ/ h FJ+ơ-xunX)`7뼎 "x ؈"̢衠Wiɼ A@w.֛vdR5zIيXg#M3Xj15WiǷobe1 j1R1zѳf͛6Kч&%ɮC U5""WK6y#A=,ċS)$""$b+}({:a`Gi/o1–v,HtL\O=+E9\ ލbsVy> o;V[|0(X`p{@ @ @'P$s _W։_wp(ߟdo;6Ƹ]Hb/Iݎ;8r])`12*&L=^d:6fd}Sp7)!]Ŀ*1zݙXk\(/&v`/Y2kf%uy@Eig2#.usXZ.ȴR pG:Lq@_RscaT܁C<9g=4̽HppV_(t>*N]Դt1}L BOr[XC3ia3OyO ӹ4:~LLk(NpZVrK, 'bŋ,X YJϕ7sF 3a=ȧ I\toѮz?5ܓ6 @KKJӼE$I^ @Dzr CZS'&s_=y.nD8as!+nwT)`*J|{pT79vT(}Tƺ={%#s2W:AZۈ_g2tuKFPO^`7d"xh`Be[ !R #K`beWp&[uB!U?O0Y_]ӦB{@b{n!Ba*;X?߾I3Yހ@V9d/;H ,op5шt0BP!'GDm%lAyI'ا?~߿~߿_ipQi ׺4Q&ޓ/f؂ТHx@ άg'%\$TcG lsf3lPTDdcT# 6Pֱ[ /g^4/?f#׆t qW FBٙ`''#z$s雁"mK4jbZp6QR嘗@v>Tq9ܧ3/J:HG7&pJ?TT/[$[POw+qY5{eEC =*Su% x(ׯ'^oD. 'H@/p}y(q~Godc];¹BwS;aIAٮts(U~PH3X[dΏjuu3^=Y^ь|НTHZiAqB"}I$I$I$$I$G* 3D{KD-_ BOzv{O1 =CZOa@ @G 1,0Q7z$+-6D:Az@)\@1 1<zFр40@`H s\# 0 0 Zj5< 010 @ c@hBD@<@(0D% V*uM<@ 0;vc0D(W R0 0d 0( H-C,VrPӳ 8  E "B^' 0 0ÀC\"MNF<8,N IBH )@Q!B+D֕m$04 00` +!1AQaq P0@p?7œ D 9 9 x#B(qJ6"Ag[eIe 0 Hlfam\ @e%HP9AeQIװx.Nt0rdrI@ Q' ''H>5P=6$Pf]JFP%b" [މq!M̛<Հ $@RM3h;`$4UV ݍVڿ'4J!X̯hsmLh-6A)30"euTvB\dFJ0 "Z {jXHYb`ԀoO'8vN^adIꕤBr;Fۜᝉ#\,@ NAw9(8 pM*]! K[n΀#L.1H7T"h(BL ( SyH"*,,AoZ)-J@'..q΋2`lPB:"3{6Bʷy*q2Ab%XnrdkX6z@I+0B+6pằլ" &7i8 X $ JEڴ݉K+:%pp 2Ztf&E/$E1H,q!BdGA4qAPQ4Ǐ G?pyt_P8`龪e f>'䇔2LCٳ|{u K[?RsnXE~5& \6Gmjb$S7-d54HB8@2,P5h2ђ(y"1*I e & L@ 1#mX_V:}a79j+{Jj`t{DzLXɛw=BRAIqP%IөAt-JE鈉ebMu''o[]93k[萑r3$bG~5yon-|@&Kq%UV;|z: H 1E=ή;> -x:wQki! _٬P-'r1ers^lÊC?sʦ4"L8r9oAH#A?; c0TF!sT5 IntB?\ņs5/քD1X 9>gnΛ=jgoLX5 ]Q^TlCCc" }M.rBA,`kTvJs ,d#Fde"5 #¡eX`l[u?{NO9>g_kRr{$;~xQ[&790G -`Gɠ$BN5D ۍI_$Uԓ85J[{8*Elq!hBP.s`ԭ g ᘈV0F R=Vpz>7pn_kIߓMM{)wDx -ay]:"ŇL{S!ɯsp0n/I8nRq5OK>:/ş>mCYp=wΚs)W ԬB]^'o5C114LP**g}E<4^ܝ 둜e ޚ"j,bbmLʒ IX;j} q pKMMY3[:8/=cPp{|~ ,qG]~8xgFn6|OF;os|7u5u_`q]}\NM*YOΩ26 Ssh4QLXD!$4r}eBH1NEڠkGs?3D+*tE(^o' }>_^>x:Q@:-uuu2Qy!Jl[~\ZT. !n0qԵۢqwbt=+?Hрߌs 糜\u|GRxܿ ۡ6ܳ?m#mO?gz/Q mi13l\oξ?c\9}~겾V_3#"&gyRJh\X7:δ0$Rxv 3zs~w@N"~!6PY]P0$Lߥ;/ߟ}ö'ӯQE`Kb"Ux6̚L,:Ǒd=);. qT\\hYVH8lCuH C)TAАX;q3,,e[XI,0B%D'"M?p95`xG{;?!ypf#!;vBxoOONz%5ɱ  =Lm|FiV0Ć|CD<& s\"a’Ld2eЌ5(;L@pGZ@*b%HmU"HD?_r2 $,j@4x@HL;ԦRJZ;]i&Io]6`)A.8뾾_j>d_j>뾾ļۙ66k//0$J#')fB>x|T K8ڼ YMʑhVrN|5'?sP:{:x}nJ9_?w= $6C8Vޅ\&M5I7mA.omvƩ}Њڝ@a6xB&2FqbPR<_1wZ*g!x!m2EI2Tm1|_[gcXf(?3B$%󥙀)8&kU&ҐA\s:0o~qDF gG "!&.oP∂P i{HAHFgE8RbIFBы*baeHo<U?sZwo#bb^}ܡm'UUDHHpgQ,% DI)-#:p\wjN"Iٲ_C~UvTyuAe riKFգl{mxV&mF>^u+dڕ~Ell;ؖyt<4ӅTk @Q'c?+!1AQaqP 0@p?n5Dԇ0ʦel)&PMB s1lBE0QnHfBI) i!{jI&XK!2 *Q…2MiARD1R&"$er0-E6- JH)jHDXZ(Mh+F,  JBFHkq !B~$ 6Hu0s 74Q2ġ,0^]֋%,.$!PDI@A#%fBsJrhHMP- ɭ*,s=: %{sK.L(݌)OM  lJ0q:ad D`mqd)%RB #TY^"` [rTc,d"U(hI,2ifk8 Dca^#dy~B&ڎDəYmXE `'C)= I 2F$%<&l$h/``r31 ehŐ]% 5J(z ,VDIe3%E'6<""j!0jʬ0omʌ MDҀq <,ޜ 2fb2Mr+77 q(*SId\q4h>z~;tGFݽ.?D`ۡE3 i꽣)8hȍw^G cU FzsRjEC" Dk)n-@@B [w~B^ln,,+X'mLyQ ^+;H pg|PC8PS510Nu.>dnCoI렰n63h3l2KJb1;\fE2I8tLTb@dkm*n#eS0L.c5aĞM>\AQ$ nt?&e&b3zW$gשּׁ ߮5^]c箺]u=tHJDqIO]̸:g7J& vwm LQfgN'ffW*,/=wb$mmc"A$#vLaNa:st¦v,`,czχSΥ3\c?Ǯsej\sRR@ЬoM%Dޠf!2`Ox"Sfi$= 1<:13Ĵm3M5)^*&F*–MoIߍ! `ۋ##6;Dnk)RP4D[Jq~7 !Jh(ē? @>ڒ&HkC<pИ1Ӛoө;c*BS3Jd iT0H7;b{G5^}#=aq/qKo4at;V"v2ߍqsHefMǀ SG6HaNapGTqx~?YLR.b$& qز33(0E3qcxy.&YMl.ʗx5ֲRd&"-ː`T7tM,^^k@ &(ԣsCptXy&7;FѷxԼKOJƾ:B=Q= )]uߞ:cx8RX aOh1 !%j@(A̘Fd2虁pX1r04o]eR0Σ-d!0̄11 eRJERR%Kl C̐ h&PJu='R  Iȳ r-2`nb.Ncnt|?|t|?|t|FhF$ D4Q޿NK_dˣ+ -WYR݋%yQ2K pr3!bKKIMy0a{TP+1"K-F 08?pB4;9?EG5П~k'_?x99xL[}kЬhqHN@$MÇ1A*r_l H-jXuw9j@3?gIyԜu''Rr|u''Rr|u''IyԜ?gyueaf4`ZEGB *2zZQXm%%$N%7DK2M%I8`Y{=[E[9_tǠZK1NK?}jE_cD'K(UůܢǯN!w$e9^ oL1OM;~w5Jy+(K0OÅ)I Л<$an{KX/^v MTLZ%7ucqLL>H+!`r29#иnSFS݇;w1RMb2^EꥹoҴgHxFQ.2~j2lˤZ]ۄBAL9KF&F1:!u, :?@I⑾?Q7U}&RՅ4"$ hhDƌ`շqp`pj}^Vim\"Η(Ml¥xSۺ8>EH@|``P $WšYJCvX <R,) Zٔ9&55@ȜC"ED1z4),{-D"& cnczs;>8ӂ0,Zr@JK6VD2rHaRshN0SLhz|GZkTZ8E*Ǖ ah_KR'NT@xKMˀEPy b,r[Yak8ZLBxV@ q_Z-7܏P*ʖ,ZdHs#ŃqK$׾C,o#h4駚 * 0hTÇ <JC yq%$rZٝfvڶgjٝfvڶgjٝfvڶgjٝfvڶgjٝfvn $36֋p0'E=#~: DHABd?>0 PWP=q %!Ռ%Tm7i˺Y4[ 7Lb8zJ$-i۵&G$mE@T6^1$duk(xXN!hHˬlIoNHό ҆(s+Jh )ɦ3d|  /Q* j(R0 d >b3BYljj73șE`u9T}|K)4w JQ ?C|2atyդ0 ` ʅ^܍a\IdA7р/c%==mLKgOva{Tl]>$yU.VUX&?IvB(H~]-m˙Nl\,eR΢6LT̲\sfe!$@db ,Qig^O.Klqքkه-j!Ć-sWK5rC!8@Y(Q?6oN*eDCU\)/d<\ O7P 8<.3mw!!OVAh& 5tmZ,s!81,)и)_(Fj".kiLb;;ukdfoѽDZ*ev#䥀`"Zfˣ F#Y SPUJr` ĬVjs5әzE'9n9 uq۟8ZϜov7ֻs]n|}k>q۟8ZϜov7ֻs]q7Ҧqz `H(/ ".F4kIWoDB'gGTuC moâc&Bi #$ml5Ԅ*umEB̐iZI ?ϔt "ieDŽDؗ,Ҁ][FKƘzw 2F3M oVBAK0?ГSRju?* _6D 40IHQiPs]GcWD ĀH87jchdԳ^9R8R58Z'%4u'oec@U,: '2ZѬa40 &! *Eu^i:G!瀁"zw{hn9N<8|jW34ra=kS!N&qpKv@Gc&AgS2&9ND<֮pȭK]`xDdU'Ra!Ew X)'abG2S a $7kvx)EPO&)jH?jhsp0K ߖ5[Aɒ1)IQA-E&#)0j RH[P>h'(34xOX*TIHbŋ,Xbŋ,XgCM Qj dHbf䀟6XPM 2M U&B0m-}.yBc Qd1*&@"ĄUg¶t F%ME RO$ P%;9˪̖Ӗ&m뢶dA|E|Xd\CmBC%ȩقAk@" &A@k,%$bK`Ѐ%Q(e2Ή)! Kޠx4m œZxd3l ( (݄_e^ [Ɇ&I G B7Ʒ^dKph`֌1lݞ?v!d[5&!pbDE"@lvelE=ʇ8>t`Ķoj}!â2$+doI( aM!_q @wfUfn_+KڠB, JPʞ0G3:pu]J/}&,P׬KDC4%9IM j *4>e R rS}B,gZh^2J | 4 %e~bHVs v/\  Y(PZ@j #r@PFvJC$ىJ&ćL1*oD+oo8!wTEO 8Iܠ{B) _I)p8$㡪-j򨥠䣷 2 k*Xj! 8D֐r83oIbR d i7BBe<ư%1*:8NV0HkV0XA 9B S\G!Cք*GlMƈϚCng`#"$G7"R!RI#Mf*yC5r@ ֜d,\TD7baJA H2mu6!ْD&:X@))%<6}`zGeKp7X76l(OAzbBm2xZꅟZdN%Y x[&\y?[$E+0b<\`8X;ꖚ1i*>UTQ1hUYذ!#+.G̚fib&{dW Ʉd3Wr[!k@;aI9,20aTQjRo< 50AU"5i\JZO.)"7QѠf\]J'g+mnAɲ|ch^$ÍI  9ZC#Q+>Ba&qVn 'gdTdȵ**>?ob%Lj. dfk(CPht* AШ4:CbnkX 94)û@\Dto}جjuBf J #Lؓ "ՊbD6bzLw㮑Z漌iMO'.**ޒHz?dERszQ98 6SHM{~T-Tx6 +O>J2eᗫf!oi Dkco8~\ak.sߗ9Z˜-wv8慐 [@5%JDRA]N [WCvڄ%\pylQ86 s}uԅzX5 偍h$  cTJ|nJDMCLMp&9"ⅸ9 (4 vY B%2I8\Miɢ\m{s0@ ӿRll Zj gIGÇ8pÇ8pr(aI7}{KMS> pV' Inc탨#`3<9_ğ Q7E4ALb6z SE4:@ @D̙>>$IOI7I R$P(3m&" W'HΣf"222&/#L]$B ZbPeE&ecxL1m `| @Q`"DԖK UB dclE0Zw2_%a9al3~DfBe[ #`8( {E  DHVDDhPN@*J„E2Y&%™ \x&F-bTp)H"GFĈ*@tOT%*zHKՐ7m(DWoQceq I=0~D$H" % gjy $َMi :-OAQ ^8 ^{oN0q:6<8q: AШ4:B[J"C3/#8̋jeD,i`c\M#92֠T CPht* AШ4:K*@$Bp'6لsd7fz8zD!(TjGHA;ݬY3~.<3ē%a9&C 5&&O>Q0Zw"`Favd! "@%L5m"!e'T3dS qeĎ밭/D!1a$w0T BWJ;zg-AJӿx^/}f8 `&jHYX^t #fX x54A7>#((JPrx,ZHGnw)8j$o! k[ ".48wvNbX1 -1Ò$>%gB!M}OaslAJҙ*fdă/(N2HgoȄEJMjKܶ<9|/_ )_du?tf4q sXŒ<ݟЇ v%*L#D5[QY0>)&}&?IL :Q=DŽ0m$(PB (PB #lHAow)/pG֔(+#4J.Œ#KRaPHc"9ByȊ*EqSD%n77Qթ+@˳?z4z]zAyR d?E'B%R c|q g_]+_|PZK%l XG d|"1V 6`,Xbŋ,Xbŋ,,5RByP+e 6f-RHR0^) 8Z'V$Eo6Si 0u(K:%/$010'O #2\%$&i8L(LGEI󴡛\qtIF3JL9sAI+-WUbV!#%4$ ६$ R؈|oa<(wq%-3@)ʥ꯹<%!,YZIfbDsBYj}NsPAN")խG8 &*/2|\I5VNPiJQpŷʊ%@"x:]ۣ [k s2J{,tp7sVEe67zEBA%cD4LyeH5~\pw T kX4\z0FH4.1C%阠:)loN+_O:I @X/+.b@Ș̩&8z3>Ŀ0v1N"#jl%@Z1L4YqQBZ"O;fV0+?6i2p28 'E}#~٤ 6BM'Q7!D@~Sab RXgg~p;j=b@Iٜe3l;[3l;[3l;[l;[3lH9v:#RFeS ?`ҠҠҠҠҠҠҠҠҠҠҠBT:T:T:T:T:T: endstream endobj 12 0 obj <> stream xWK8Wh=TG/`qr ݅,JW3ӁB;n<'- -鼾;>4QV^՛)zzP(Ӹ{û_}ݏ"Z}0Nwh+nwU`Ө!>^nו eP>EFmlALxyE#,`N5H~uaC,7ɧܮ#&?G+hB^u?c#?5(;}f LIzWCʬx '>[ȵ3q $͘ Ōt߅X~O-(AlF pN)̨K")'ϭ=ʼl- XC3b-IsnOy`!9|"MKB׊&hxJb$`,G!󃪭~,3TQwRʪ Yc?' GMJf{j#( ?V0n":&*AU:R7L9'P}Ef)-R..]cڗuOްVgXњٸփLrJURj 'S+SY_om4Pv %AClC~!z23}y*E{rmz'SS>)~ZGO+]MڝH=gxl eՅOUHRzCDZ϶,2'2,txp#]@sk40Co,-pè0lk׍';u.] |y_o}#D i]hvah.:%kXQ~yS9=KRI:i6 IH!|~ %ڋ\|^Pq!pR3]DmF\ҙ$o㲡8cBR4mK;΂dʷfѢw떈*ycK6.1i{l mB# endstream endobj 13 0 obj 1057 endobj 14 0 obj <> stream JFIFCCz"  *kly[ZsaQ:ɴ&6lJԶPsvkfĤ OSZ<}u=ls<ʦo}R: Icѐydc#'?$hc1LydcǓ^zcy|Od1^g/&B(@@$:;'?2\YKYKYKY^nJ==>˘ p/j#:d y'K)w|`H5\MOx|2HyGAHqHX\k%Fx]q}'}J]}KqfLbQmӐ / !nkaocnnlvuƲTkױVo³2:1 r?]RߺŪ'1/-e`HqJs:$PJ=$1d-̌j67Ffn D˷k fQ&>J̈́Nvd-ᛥ'->Bwc<6Ә :$@3| `:Y*5YGym1 $tI-ĀkAS@]qdkvDٰU=u9öc},@:$ʫTA|d0:E=|l R uLGVۮNZk~dl|Oĭt4s.1'F&8_v>]('y~:6 u3 XOn(h[Rߴܨ!%i[k2c@Pót`Uy:J(l uy9^*pG7BVo u YW\>j$R[*PEh=  1lށhs훐P^N}Y_$)PRJWqŎ] F۾Lz:!BtKT l t~9?0 w`qTO/1d\6/7ll}l ܍*[Y)XeVǻ2x_ذ>n?''}%(V^lbK_ c!NBEwe4叹|l|0{ߗ{n_4?mi]p S֎;%G+"|a3(z'?'*??UVأ[*֣ηa6~}4.S wCٸ- ֕ u (Scؿ)|GVu3Q}~S'sap2l8[!u ڕW7Diy\*gt.:8'*^QQǏaG3X`ljϯjt:YJzޗs%cwZ;*DR@8XVdZ9#0Y\ܓ ѝ7{E*@$*!V^DM^`W=9Tu!sa9n 㫴aa.ߔ7JrjVQ~ư7=)f) _=0/=)f)7^F ݄G\ y!<2k<2U[P;@1C+BYag z NWlmZ Who#"1ۦOC7!y@9 (T DGy 4O>ݹ7J 8^?*X$sC;X$2lxuӶ4nӶ`vI,VsZS\s7˦}qgh&– 7ZHx"^U$/{1rmNYv]I,ٱ]+_ς=:ڮqڄ9Fw?r~#ώZt.Ӕ)cLzW(˼eˏ. Z#[<"=O =en.ʜ'k;G k.F}&<]f]p,nBM~^=/Ы}m}/Oy-_ط;;˺<m >_;2ǗK(M,9~`J\?辛cҪ-U0'ז{,[es\P|TGJw9hЙU6)/j5ZW1ETQQEETQR 606 5@P!1324"A%#&`nw۷:;:w x-:gs r' NyVj$Z60&':]=CWC"Ab4hZ8y-2F"ORhXe!|i}v3k,Đx88S.Izo0nGb723m"ģӉǭFNp_TnQx^!c+'8V;U{G0gn['44 2nʯ9. f8SJ9hQtQ!MLXBtՃӆO#l2WR IwH x 0 . 0 . 2}Y -V[+el[8"yom>vXl1-tRta|풛aw풟,?wJ| Qn)vC?wJ|T@E`dMr9ltږ$S0jj]jRVB- K{G3gMj+VaZkVaZ헷\H/_0|µ +_0|´]BmJ|RD;ĝ?Mu"# )6'z PVfs2S=F4ȧ6a<nٷKx>AϔekSG3%G2QC",V@&Kݪo&.$jj~Dʼnf->S坚EF6 )af g 4e ^NomcqZo*NV2r:A И͢F6->lۥr |SCz({֥X L1LA@  dKL/ Q[?\5oC?w"wLEuQ z^4qVFHVcZ0d2tY*1\ڦ僔͛X~|;/mt%5# M2$1 4sgMݣ8i=ȞyU^m`mnٷKHb&eYD9/hAt0P,k6*CBM̕5L!3r'Us6o9,.J2 8FXH  C ܉x;vͺ[!|9ȞyWlۥr | ܉x{af׷lۥf% ۝+eﲢh[|ߕά3pKnej^ۭt&t#LQ+O^,Vȳo|=6͈V+sb ؅Jb|db?J0QAfcH ԝ%h\RE ؅nlB6!YB㍭ݡk4w »a]1E _P0 }w »aIG1!&W&TXMGE1'=vzId5PR4=\,b0q0d;~=.ڝ mSfF3 O{~W_ᅞjzjl;$ h[ToV-<šKs~ׯu%bͲj,86uf{H~~OX$YfU;KXV]G\IFyvVڛ׷*v-k \p$z@nI$VkelE-m쵶REꄐBɸ>[yW|Q礯Vp~Rtr"nFH|Ѭ߿;oW0DuVGHd֭}*_6aAӧ]7P2|2m[kof͵mm+mg9֖⽼[vYa,Aɇ;\rmk+̗'"~ ˗Ax7.) N^%xht袮U%^4ŭ?j d]W_<3xYkj.Uv ןVerՎ.KzvJt;'d;QJa&~M7[n99{ˊ)G_e}6_qjY*]ȋ5|ߣ-ІlfWof刢,6+#eS?rʼn?n񬡾b {a|7,V\;@;IqӂXG o@*ހUzV[=nJVͶM۫V7e_ǝw' \tmlbF-|3j&tfdtmS%Ik|;6[YU47vM4klA$j%,_,ݫM͗sw椅&gr#cξNdJ=B?Xl/-R2n3}_Wk嚱+8I`en] E!&}7y6F8HşK&C,mŶY9C !StGȇB,fpdѱޏed9"4&7o햕M{9c='ޓy:m!ӳ].5aHGW6W ԠCڸo6delgdG l=CY9JvjFp.yMTyUukmڨ mordWwQiAtwVFDMdYYdw$8c,-lp"zs=I՜I2NOqug {Y HNH@g@ۺU-tzq$"l kYVN>Τ`vE1ԛYj^5#ɾgR2t6n2cy}6]ƙiȞ^p(̫ufU*YnʷVe[2ՙV̫ufU*YnʷVe[0Ȍ @\K!-I]nNU[*ӕVʫtUr9UnNU[*Ք ?R iZ  !1AQaq"$034T #25@U%BCDRr6uEdt&P`bp?֠л2/ПWVjG2N)Sw6'!u3'A`ŶʋlkO)hvQ2ơЋ:"3պ2WQUo׼WF. `O?'\}jEE;Yڠlc"0$m&{Qؓ=Iv$m3Q^1.;YtAz?*4ZS>>RS9t aQ;Ćv\jCw4eB`;ĆU8.(;xv\jCxxpFMp1֑A;ʉ)VUن4T NTal#&f@(6q zp/V^}idUP *jrUjJ9 *$QƤxjԒRRc+to~([EYn3O#u+Zz*DzP艢ބc(wW#y<s:tIXP54=?AJ.fm $.)24'{=+-ET(I?w9(7q #zNw l>8h4V٭@jH Ie8m@ e۩`U`,bd>w$s8(拘_/:@G6hիeeYqe=> @@j]g\g J,uyuy*,!Lc JzM=3гYcR0T- r˝Ӱ]?DȨmGXђQgQukBrWP=)QFG ~^0ʪ93kO}ݿ+H)Fˢ3tyhUkRz+ٳflu;dL a벿!DS]9M=l!fxXgz&Zۑ5dِ6cP>+dlJ:3d@3:_t|#)$e*"熔DwkDwkDw~$`X GⅮ#Y, ǦDwkDwkwKI24XTQͶ1mLnFfʪô\]z@-C^ 2P;0Խumʠ!= P/Q&mJ3h`Y$2wf~vQfѭ{F1>Gpp yG͝%]|G3XT^ g*w 6q?@"e/xƂ9biH噲.ap F5z݄7YZ5V)b6d݇*ĹÇ@+CPxO)7u/׮)\w@9&&LMd27͒do$6IlȶX2c]y$TNZP֯Zx+=ԞJ`=5 x+=)DPMB`=5 :$$4*ɻeJHr~74+QZ   !1AQaq045T "#$23@DRU%BdrsubE6CP`p?톒^<- hj1X*L[P5fbƓYKŃ7}V1VoD%dv3;Xd/ YÄ *VyI^0ɀtǝ}Ef+ײ1P5[VP`Ix+scX>&ˍN CcH8&FɈK"eA@X]qNz}@e쒆nTcv zNq0Nx8YmnrKĵBگ6zWS[A^֘X¹L01ggk2:s3ݟ~ԆRj,oGڷoU|4&T I%]^Ua$-XD<|27Uc=dnz'ɧb+v d4 wיBQPp7]$[0x.<::mzRB,6t/_Ţ+CՐiiV{-X2ɜ"mP 5 s<k t Mnc Eh qjM9k>\>[0|xk7[oY`w5Ŗh Xt⻘?+==Y:4[vzT 5MDWmW&ħ\MSC?.f8% `v@Yk 7qL]]yBWh{۟YT`l:U*n q|8%M!3Ԣ-3-t*7bۇ#(DUxXOك+ <[red ɋ mlj''Cob2 {ȮRA6tR3D_Jn zZЛFK PO'\YfhPL^ɮ2#4 -te:VYI&|'=_/% ^P[ ]9W8AZa'B\٢Q(Xb7JSqΥ"xh qoa1-$vYk\D߇m*JJ¨NYPA`QrB){R* `!654ɬ>LRz,|ۈ0W2r -1W 'K^ kت(Y&U R"]WKO,9 Q' Poyk) jVK b;z4R3K{!/3(xєFH 3$\ҼpO8lA}7ǷDz8, ON@TF|dݫ$HP(.újZ!!Zu%E" ޢ Ow /긃wj.\脣߈° D=s 4/NyZejc_okq]IxcOԧ`kuեv:[n 'Przyg  fZ T7y[|G*O+ pK't}ZI:OUϥwj;qPnjlt9y{).JmbG#JX9:SmWH]hm] B׎X\RhC|?DXqa @ Jۘ2y]@8eTDӝ.72(j'nylfIqB< OC.\ eɾG,Nnc{ KIY=o 1ix$gݰ-b.SNh I뮆&K_  !+(xSD&>ƕ%}`ˊ4!^d'hWJ3 |Cxnz߷oe$-"MLQ*'}UAq X$$Эڄߣn0}wHQ`~#uE7уFo{sXdiPڙXG741lMM Gi*zP&kX\l9[(+^7MLpK[ҩ+yͮ9^GJS,bZKڈ+&V}9E DiZX *6{6Ub))WNR-f]{w',I@aZJred9DS)TdD11WM?(Īd& p V.I*ie<7܍x,r4}:&eJ8_Fx_,)Ye7uUlZsnc 4 2.7av~,U4C#n\ , Lqo&bitV4U+A1#M{_"oP]L`nFg}oF;|⢪IM-(v0aҴup*`g<)1i>ǧp;[0#۔Ann`VLYAh2aG&?5m`);X#m`);超\\],+Ay$+ :NUrp:BlDwDwDwDwDwDwpr6RMIul #$!346"0125@CDSTdtuv %ABPQacsREUq&bep?'bC0/D;9/rEʿ:''H/zRT櫾YF{arU93|=x5!#1W9+oс](vo"S1!"sDހZuNNMm{S%>Eٌ/;7@(nQȂ-܉wLe,Hfxe%d (fi/33l=-nZ_q[v9uO;:FA4uXj%$q`1']x;}70.V٤\&XW(Ap.M(L>u̱J^Yp+9s ( )J>oОϡ %lfZ6U%Op X6N\W_k.0aDPQe u+ZVv2"Ir^WX&jGAčxZYZ01Ql=E5v-ݕviH)v6ZVbG5ޝܛw= 9+%H$%)OZay_5_Gku[}a 4xe0Ҕn/ڮU{fWb,%D QC$e"ht'grĜT--~5y.OB֐b2}եiq3eW!Y1E2:̬6e$y[q]uGuQܗ)13^d>*,-2U}ue8{8)nAFZ} 4 xk0f)nFk٭Vܿ/Z\=>-"ntXV^[;K>&IYUidbS+>e62*IŽZܘko9rVpukFsU6f~H׹ 鮳!TU%Rۃb5+m0e3Nh̋06bfDa~]qKOJϷ1x:7.|fp:!$]e 4v.^visp{[IDJee,\#~[J w5zxDY.EpL"%ydY -d{@5erBm+ú3ݷ)7|v!\b#c]dbJՊG.+'I[2, OZ2Uq G-fJl=\SCK$Nk*#&I0L1jyk6xLJ"fZQY`Y9w^ =4\;WfgbR;c.ZMr5OsS+˰f%8bscW\U+'$Uʐ e` [g = 4^+hc(b,י)jʋ.: nwB r+_]uYje1NqsV& \ҬG3AQw{ͿY#}W0MKr5d&e̓5\3fZ$;z ˉwT7J9m)hhL՜ ( n ؋Uc[rgq3BhɄR7..Uɘ! 01eϯ[.JKw_a< D7kf5.zrtS'Zkǥ{P|T=_9>;H7po\w|,w|,w|,w|,whؙUt%lzSp m~וfƺ)EĖq^^@ܻ9vKBR{Z?-]C,ޕڂy;p Ǯ.^; -<~~=PSx+}Qgv x]-M 54|EOIܜ?Ɂ&m?gSxnsly&ٯ/siq-Dn/R\cG2IJKNjt;K-WkCWOپ|Y>e?-׵,}TDxdaLS!a)SWRX0!G0JT%^C /ߧg^h}*"15ʕ`S@9 rKHSAs&a,DlZeX=X6ފ4Tj_*/-2Vcfc46;h;WkƯ]JV^L* X\CHm9EU]ۼg3]]<1mZ :춸!v5UֽjbJmYWbV#ctST_Byޟ<H;15L<_e@Y䣽>$T&\U iTc_1O'8T%n^7r:G@Vs~ٮ~c.?n6,qa0Mi愤]NS6xـV:ZsT5qI'bvI\̣ _M PLWrkPV'bW9(Àze͑*^vބuQ]K?P,NBBb4![_C{1^Zʹnn9P) Qyuǘۛ|^qLe)ygdd BiWWeX)xwQYKHU,ƍiM£en]mi^oݫ֤}J}LB pR eFT_nOsV+YmU6O{_x>}^x' iR?SQ ]DW+,^u1z(w;':oU)٭99q' og+cmzH>+a ]Nk]cr%#dĊHCq~I +Ż ."NopQ od]-w>nE\G87.*/F.^%j/u|K({E{)>k؟&H`ODG :DFJ[@Կ*iL+%[W͙h>o*W]^#X9I. HLDv=uϢ 4]iLtG%G=yz>*H`13~צS&wV4OTW8(Ȣ}hy^6lg/}`^1 K$~u!9{_>}SzR 1.I:Gmp§P˜,-\[uݶ=JӞY{lڪ H*Hc|H[k!iG=})kE[itlb>w~ XǍpBYVuyuWv% vxi `M:SuL^ݤQ"Tw`O}{N{G&0|Boy?ԏ}TDx1H HԲ퍎,y/oaio,&b!z(K2DtA&=Q9…uTvjA:7WRM4uےCŝZ7Z6u3Q9MSEyRԭW6d0}aOS*5sȉ~Z{f̓e؁GE=._<ۭK3.vP+3%e-ssF[!,#G^$# M)s@@n?㒓ws׶%VfR2MJ"KAjf=}ϰQz)q3.^V?é:~<%UCs$ZFn42/km7Im}? vFa=3`}p1~D=rii|E 8(&0pE(Dѓ'Bm `;aPCF0#@+|!VA̍*g 2pTۭ?we8ϲk*~][T08s@(@uO$PX-̃aI[ qrk~N_*lp_#ݐ&0~1uK'lc-LvY/u|K({_ mcZEFw{)>k؟}rhnV3d`B[Ϸ14Ubn^ZX823w㻻ѾOёmV<ְF@t%۸eLaJVI'T8NzYuz۞N|Y %36՝$ߩ)Ԛfu~+YIkЖuK@[77dHtv2fd֛]$cn`^}9zSQ 5d>y k?v\O 02$ch#wA#enMF(_2Fu\0O7:/u|K({E{)>k؟&H`ODGҞi]k[ibkدfk؟&H`ODGg/u|K({E{)>k؟&H`ODGg/u|K({Z׆|2"ˬJ̯C hK1㨰)șI&0|BY5J=}kE2Myc~mUz7 uz8bXIDe\ΑZE$-[bEv#_O8{SZZ˸ {AO#hfG(]2H~%96r'+baׄ*:̳i[bj v)ܜ:fT܈}m^YnE|^o.nb" ȗ%c;˟Fρ#~mr6zsHߛbۗ-E1/An=:[RK~.5򙗒ˇef̤)sh 9t6VI_খfDz(ܧ4h@%S4|eV콥wwWމ\<12G bJoH]&\.^p1%蛿I8˖ ^vʖ0@x.h\$oͱޮ\7W.| lw>+eS-]mmQ6)JS|-![ YB5>q7/O ׆U}OnmPU}ѿEoG~S;E |u^36_wWh_w5Œim*z9x;[{}ѿEoS2I7q"/ ,_-bOkZ_0OO%'x :\lo39HܼKnJܢx݆ӊUI ]tvcMLD덕[`VY.Hyk_R,:~ 60 Vjp4rײ$R׵fOʹxeb􃸛)EDH,lhG`ZJ}\0ta?y#Rd1Lm]mzq}̊4x(kWz!۬2dO荧{Ժ:gO/ܵ?Ա5DG6W&7άәeЅfkQcZ8\qZV*!R%rL4oMo^L.R6/,lT@cĿv'T)l"&-ih(C9Qc;e::[|c+[])rN-M_n"dz%feu_usc[Zִguw2ײ$R׵t-,2vjB+7ϖhZ f[+LBV=shk6A+ y j뜧9VNvmyZiyo~ZDXS#s~R_gnm%ub92[䗾z7s.uVKM¼+jtjc < "3VT]!2NS1^H꼗*-lNik  :`b$)GV':K_}5u2PВwWrՍ}7G+\v.c<ٻPV6谜P*2 _Uԉˌ|{M/@$9ڬS\]qgG (XeU,kƯEJ㕬ØIuN&.‹bی udBB_AcYQqwC$t( pemJ5X(8,ujވc^)SLLrwI?W+fYj[Fr\ˬ_xT[\ht7/<+ 4[0㥽3VeůI%(&Yu+wOOykOCxމWxW^/yn8/<֓<L!<1N50"4OsR HC}g"\'kZPnZ@/nL%+<]L ڢX+_\=ӁY-͚)\۞Ì[TpQ5k.{ۜ9N]][`Ix^ߨk+ﻃOkVP -ˮ 8pg !Gw@?WO,]脻臧Jo\mj hvm7 5qSͻ=6t=}Q(ڱVYS !?pDnU_ Sv٧T]8ߕn87綸Id8Mb =O$bycS~$z`7JʥOeO!᭿LWa/Wsla5Bի;&?> aZjsh7hn7]/ֱ6ؙud#"5|CڑlzB>%_a=> kfK2$MͶq<[B"cֺ\qu\6wЇPy"$(DAxB[o!!1Q >CVN pRpptz~y)_ŶmWvn[-~Bn%imYlewhSiazVi^!mmy2*v1t֙{jD=^ÉKk\hQ"Eyud^ ~[NnrU ceLvt8g Ç\dN+e'Zk|\l c$ʴKk)>$`VsnR۵4{cS~}g-m)!ˮW#˵-QEr  gH;f$dm1av1 z㞲G(|=Uﲔa.(g ٯ-k^Km2vE,|nK{j[iO2˭ko Ժ%lanQAG*ʭp]3\~użɛ>ٸ@J;\?ݯFU/Lv[~̖wMߋqtwq|/N :beRW-~aH 5@"7Ssׇ Ў FH_E)0i⣉%j .mrѲB\@/}0P/ltøme\;nVmz~<\_6Ǖ ؤ%QWWTk^: ec9uQc#ͱtw+wuUG61ow%zcEZe]m]ɬӆ^N|km)j*y<-=uy ]Lv 3ݮSM\Z.[1}}sַ[*޼+!)tI!n(𨌪4ۇe%nm{LDO(;Pכw" ˜Jn-kZuyZia2왖%l2i΃qڽ[cc<+<ۖ=(b|oS}?Yw O:4f(j7h6`[5.A)b~ O'mʼThX\‰K?GTk%,c)PL?lyKj6ǔ?lyKj6ġ 5Q a>퓞~3 Ck*X@:Dvwʗ偪SͬFcGmW;=?l=gjIN{3ǵO'=@g╎96ܯH# tI_} s'Y[L96-GdymSnk~qCa .m>uAE.ޑBFT B({3EE`Ԡso\MpHPݰd,(wrZcC,/=k{ukvڣI{"#Dn @=y]E\jVY2QiTCvΟm+j&>*Wr<޸s2<(THGp}lc npܻ:" :x fH#fJ22Ev&nu6Z8ۖ=˿(b|oSc<'<ۖ=(b|oS}cg~Px3o0$bGs,cy^-y@Zu/Lw|1޺&;_z>_ؘ*euXg2k=Rz{Ebcu/Lw|1޺&;[,C:nnBÊG{KF@'7qh GLC-NZV\w cO~ B{X߂};d'!=,w cO~ B{X߂};d'!=,w bC-WKBp\%?rK|r)^g'Y1> Sܒ {-yf@bJO >0,P~YEkl\ZF?ŵiyDu\~<\Gk\&e eTwZXzp7bNXրZbT"` ,(;xt=QZENNӗ66t{q}ARrbTLt%% jX#*e.`󅋉+Q@YǪX*mp^[ H ;O荦iq:陏ۧ,Kn.Ӱ>)Zg,+ FB],{REB_"x^*b:]Y}X $ $cL!OAC͆-Zz?YP 1ULޓHYik0ˏ|~ͭskXp 6 @6M01sH._Yqg~'pG#6܉eIO:vSo\7\׊KlyMq4j]c~soYWA{aE+%mS HYol9|8!j J{qmo<\9yKܴ/,/19V QBSiF1A-!-B,>?@ rѶ߲ҿQb=Z{`훑'G/;kV%rNWĥđsKMn{fSA&,A_@?Ώ.yyZ(ޅv0?'\12;øӱn{F-%sc?[1tKEr>j޲eG6_1^ z)څaR=FOSܼYk/2V34Kr L5,_h=4=Q*#6?xF$rYIW—Uwtf hz:6[u}#Կ`l5O>4Qo ߩc7Tհ6@qFK_t›?ĥ3 Fo1E٢Xc\VƆٴJShݵtq8y]D kې 9=Uv̆2_S4]zrqKeú aA$XM;d$l]?5 meX]:EvC.o0K&I;e4DX,ǟqjm͠덗6sC1>O>h-~14f=9qrԶCŠ6@P\Q: ml ̐_cye]Mβ".bTZwr[Nm|0%j l-ato+c`'vMӵe$AQӴn<$iR\ÁT7 VSsiI^F)ye lFL\>}4An? Nnm{#-_K~cᕒ}^T=e ~PE`|₍/!yCK?|VRtϋIq×Is g8ꃴظWiux5Wೀ" \ci-z}рP[JGcJ]uwۆ^9kT?tpu1ih~#0DTߋiJpnp 61^iș+]/cA0Zx~~p1JMhi~AIzij% s,h\n?;[1Nc6aD5΢+)4GUg& t0F1G 7(+M0#DbP.]e7_aNiʦ@3 f\{ދc/2fįaKKx'0id$^NYswۺ14{̭IJ9wwRppګ_uJS~RQIEgx. 0:EO#Py>¾ 2ld NTcX|,2X;Z{?Xyks-cpk`,l]z 8> /ceE:UfΤ B~i8b/ ,G GC{|dձmtp].:m|LIBL{N;k217yd{2 6ބ25htS'b5}o~lFG{2,X;d&uNJk٢ӖNc-!1AQ0@aq P`?!ݘl&d&{ERs)1@V3 g1}V`N̠7JK8^0 ]ޡx2M8 ?jN?TNvO," ˅dg`pCPN5r8Ȓ9'Gpp>UNEL g0'RAްR-k_2JkB͍IHkgW$rB@AP2ߞz8h@CW?;4]Tbc׶M a{DoWA;TL>L?O3(b1ֶ 2-'i" LF$RJ[[#eȚ\q,f"jdm錿Wrn+b`i P@ET݃:%1 s# 1W#I &T0zЇN7H03(j r`_o}F0$x.?P[O #ćR 1dDn1J5UZڙsHMQA(&a0ߐAH>X\$wxclaoUUi#ߌ`r 8E)]+Ē%m`C˅!)9[/8 c <+dgȋ(0 05! i8")+7: l"X(P꛰SuN:U0;Q}L8T;%ĆaʨǨ@ @hzNZUuV]3M,"T36 n6`ɰ I˅xLV ᶕ*zFY6F*|arjԹGƔȬC S) `1Pb-ga(a.&CϴD ?_jY++ҩ;thY/,Vp=>!,:CP>br1˶ÜC>TЗi*{#NAƆr`BϸFTP` 킽y $ MVVr ,Vp=> ɸjG4DpS/'ŪJae$#> p @G3.A+$S"Mԫ4+]Hrw&M@&6,}}F.9*5 qHs@9eLc+T $ͧau Y tKR3JrQ&Æ:~'t8*!Xfr)g ݉CbbS-D ʆ?P9P,!ZgyBϹy`jvY"7'Ra&-傁0- .B6C}bhP4bzdͽaڊ#{}\O rn!pNTF`02ƴu] ݣuyhг^X8Zzq4hY/,Vp=84,9ńbjÊ01M:oU3U[ϸ%)+Pi$fU2טm 1'D:m,~) lLC$"P[$᠚mPX ~Tϟ> Kb+*r[vr̄$aðݔq+R @Y9>>}?ND.K\3dРp5eS+_+1ci/ZBj' LGJcd MD$&_szaU n0cM{*j5U.st]>i{󯽛f}YX*8nlCh|r -_q}g\P ZdYDY A$=T$1#'0J DsE+L(9vp6A Zs_h´\v-`8rNV pw$ Z{f(g 8lEle phU@]8VKpA圚"{?C2 \e߉KZ(He=AIX JLw1(]$@GH8- PhiQէ7\ba Y;DQ\Y0&-ֺRPrR]٭o8g!z%7 J&f(Hd? РUZAe39eG{@H?a E1gU ;H~jwJ9 ի H@fSW $J/x 2*jDA0;9+}x01RȐC   [u`0!uR3T`RXI`\#p6q?Y3JZ^6^fAnK)ës|Fzzs>wNȂ%Ez%L#*'9oCV,zU;J=vS;ݨܮ.EPt-U!Lh͐#4 5"ePgt?_E x{xevhId1ZMFI3w3ږi@p=\@\a">Uy,ƨ=;5lΟX7muA)Kti|?͜eӜoEyVLle|xҵbIG,h yD2" CC![@T+g'ؐxi{UV+cֆ"GoV,z: ʹh $6 8rIMf܉"}*T`\bx tlc,`ir.^PXs+mճK:}wb2 P><*B*;g@7X"W2 D_J[%q!4XS6'vt1 vBĂDh7媎V @]%b Irz7R!EbTJߛr ''10Ù[[;9`%t*¢Q,($b rSǰSԶv?sM݁P܃tP7 e!D2: -pu!뿢\g$8tDf3w}"2Rc= ΐJxi> Ͱ~cH!R7ǧU3_zO(0`@ D7 (@*B`G5) M5!F`::uL28?`0vi$`P ʈbg, 83 :_eRMs@٫C J)ַĂT?1e%:#UЗQN4Q )bʅ†URJ-N!FĞu"V =?7jYZ}gZ}gZuZycbX5}gZ}f)ubi L nbŋ,`!CG%i,eko8&WFt9o^@Zb4ŒQg]9?,BV!0_!:k'%YJL djY-0Yd uf%j-3'@i(;rS͈ .Bb,f9'b7i 5w&U<GˮKq8D? o|* 9eVFIV!lD5'FRxQ?w#P<ol(JQN.B.Bx2=N\A/ؓ?Aܬ!GiYthv}t")s_k ( zσSn}*  bXe 6%r1Qa]&taae"??B̡fTRFT?hѣtɮthAhjm5w9Ɖ z4r2cJk:a^_Po|*9n(ZCiDB JknB%{b PAn<&pjիVC>^$Є>ui3- *e,ڟux!`5(3߉xxwܐ#F0I0 25l[pZ' BbEi]LB V"/0%:tc!9%CN]`߂JD= zϊSkC/`p[XMNkGRH| EØAS<VF@Zॲ T蘂N2dɓ&L2dߒK/(0BP^aZ#\/\w[$|2dɓ&L2dVoUrib(vp0$o2ޡ BJ49B%+s8BB @  x#h 0( YFB< =yP K(@ 4< P i!W C ZØ#n>H)AHo3e4V) D̃m $8҈  Xܬ$"* [  @x1 !zG0! Ci1Mk1`XB $B H0'<P(P( b`(  ラ<8Ah)  @P&] CG LJ()  @T|0\  H08\l0  0 0*!1AQaq 0@`?IC3>36@:'3n󮐛;2eaH 7 %${raJ0`LD`*) $GBMq"%3$%`N&QX(Ckr8Et)rE#޻qpX`G Bl-ٰ]v\r:MMCρYLgkG9- ۸Jt+E *[S9ۉ48ZefGc'79]N= V @mo;?W}*ywnbm88~yכ}yU߮g=wΝU˝2oxz:o1ݓ>= ?ٱ~f sggL!/-^4SdƔnl^xxҔ19ŻƔ,ۙ:ɜv3Lg[M`q5ZDG{Snj_HQm,. to#dw\IiON=7IR d'1JHHam[f qI[wyOMEmQ($ s2{^rec<; 9T`DVz[϶y?d( !Yh;L@Zv"$\ϋ7y|٧sD0@bK m#1 2$)+S|w^= vGQjjs)b| 0X ˙siv.h\M`]kajO^'_)+RJ,"jp3BWB17hxȁC*(\ak` f,^S1*`pQ7}g΢h )@io\/οe, P* *NIbdj -psrH==K ^vА&ڙdA(= Ud5UFLCfmB;S#BWAkӁjW"7^]WC8{-r"͒sgbYYkȆ!j3J9T(߶ )peYzN{?m~v}S*QƇ8\ 6RqʷV27}:0'?$7>\ Wˏ}"۾N0 﫷,Ys=N=2aNx}i6E(&;1 1Bdf5juW )nG$f% m1Y"Òrjby.MOq9@32WYws5 ;.W0r'OQ:j9MYY;"v>5#8\j9ѿΩ <;1EwŭQڱ5bd3s=dfuto٠aQ9;;|wV+6'H:nHWvYwC q7)`@`d7N:hZ xO@*=1/ks}^_~3~?'#~{o88ohN٧gQhnaJdI6 :w_?v=|qzw=>oN߷(|q:9h'`+mS0 ,#`N6N?yg㟏? sw? YG(>VT g óht` MϿ+!1AQaq0 @P`? Q2=zh#`-yD@Xpy5,@+hjA$(!7 *BaQDi d oLӦbjM"#Ut &1&lˡKIFX1Z7- A(P$]g E2DYhQAhzOW]q|VA ;$%¡,y8s<|ijC2 @Nj|Z?G3mWCGk/!E^PDIPt'XZjŤB& %*O@#!Bɉ "dBJ 3" Ch(2ŕ`LO`чZPDWlh-ݾt|EK! 1ceA2+U%@ ۉ+N<@,VVe-vVneQ9YV\ pEFy 9 4RQ%9FVE"fCፑt#{_kzwҘM ueNL#]U\b!f!%vv.eq^acZL8w!0_7;˶lBE1CJTJՉET(R2a #EK֒94ArQ0TpjXA73XM Sb([H"*g1 ¤CQk'eFU'5 %1">zj`'j':o?YTFIt$<53XoƂ•* '@Y DƊi@7e+TQ18 )b(b&ge 6n>ڌV_MY8@`0] 0@Ki!'rrFX~=IyNORr|zԜ?g<)(@R.o^=gO~ IA h]qgd(GGcyΟNppe-#Q 9hprCBhhR@.&zW\\Ǐ}=?{};>}B5 fBjq'X81ݽ ߡ[ƺ҉q1V1"Pa) Ɍ)bpQ'pK!&m >"e</YӄƳ gO 3Y%{9xF\^^L4/b!:UJkЮ,/=#Scb#so>D3xa nRns`>S_2K5;(vKrL1ƄҦPl#9%`,80`gKYrmwYaP5pq뜳30΋!M񉈫Mzj(B$YoЀIMfk0 D 3BbvڬOT┹A&Hq6ٜ (w`vP^.(LJ]1/J1IR(E}Ѡf,ܦpTQ MK(M&/Pȓp&u XIbqehS,MYyF[|#!OBsy5//cB{1~COcHFԝ71e L`iVLG1޳1Rc0?dj傚W㞽>,QqnxQ"f;MJUMV+k1mNcӄqдv=p[RPݒCy5m_&;ߋY~<6~/Ǔ]fokӕ$!I) (R(+ 4ic7Hr6zφg{ z>H>}|5=#kN?'WZ;TMAarD>F58l~h_/VmP,%ty5 c{ƭWjpag6R?VD86'#5껶-igyMM=j(JA *m2kOK&s+ȖlcP@hXN#vqBw3b TZfHثCvgLkV@ z/7o(QB&L`͡ exi67=P7\bpU$,iQF2nnY+}|7]l3%fG=nW[cmO@ꉚL;(I;fqoJ:Jk bP J+DR(glqT%2+$g4V ضڦ|JA!Wi+*qGBj ^jeH t!W Tkj`KFҹ?)IJR^ur8 ʮwdpIʕ68IX8|/I3\Ϭ(5y*4Ȗ5as $s '|):}~4d穽 ˋg]K]۩R8cuv<8q: WzT 4? .}KzN&&"mhɗ{~oxO=O G?<'ߧ+ٍ$'E]T8P3zc$gᥭȖ5S2lWW/LJqy}<;ss_G/p&FΏ#_:C^؆Mz}1pob f'";8"?^ JԍL@ *mSe]E|6숼4ٗ4q#m&:Pp ^ 7d1aK+9 11G4#hHQkKM5sI:'f(QcEY [KC|שC#5~(ZEXV,+4DD8dQL&tPdb8j4mD HH!)  zL$6رy eP-Am^4>Cy?VǒP_5}GР L^DnZ($h̗(.Q@dd@Vi]-2])vFU[bZO-|o6΋7&Yo?!2j lg2JԽ BBDRo] O%@Wc,WP_5}IAB„IfGcQpрBD`qfR 0Z`Zęx`!q3D @0;PsxhEm;PLOj:tvU멳Bdq*m; !@  k"f>$[-B68p TujEA"(@ yrLu/5}@Gg yP^6d xU @8pFыJ%d3sX.94&R='EL1% (r) ^#[i=Wzr9 X܌p" !yѻZ5@I ɒ9˿\*_O(=Wzv+c\SI֬{@V% IJ:#7t1) s@]0w~^5Ɠr]*ezr19!2UI3HT9Siȅk$$x7w}A§vzJb+0gfg'h HX2 @w0VzR6n >wFI7o'xk;xҰ2m 0r?{c,Y0LN-^7I?9H9'nu*M<@ɨm msS8 F"//hh=VETz1o3,dpW?I|uYݴi+&Du%<%#dSZyƦO?s&S&SȊ֤dt6zWK JsRnu7:Dy{oFR#esk.כ J@ ] <,j\ZDhacI"'Vw1KxN:YM&эģFglG x%SQp,HK6Jms)o/c!hMFXpI6h&#fHH瘌˚i!#N ,bF3XPne<' ڻy4U uӁh)x]|*\ҕwh/"18Ds$; 9\!Hϫ<(1Ǯ2;m}bp BWFq`~y7>zP3D![4BFdCJRWirא_8Jg޶Jqۼ} !u6C$T+7"$5BVBb'5xXZUg<ۅE =f7KjPG(2ȧ/9-Z cm{z ¡tem.3Ba!͑@ ax,5`hE &2ANqa% 1-=25l6)q?DH(ePqiM;<;w&SDKv1$/ah&Zb'$)TMxyx㉵s}lt) AAШ6:e,nwtdbBFR *t̂Y@[##7y.iOG):x^K!•.fԻj]ޯo8Ԋ-,49 g%Pc)E <?#A~" sb, \`/FSY c|u Ͼ%r~LM_/r~RLI;kҹ|N_/r~a/  q?}ޘIٴ֝neж,b3C|Y_M:ԛN&ӭI׽δSi6f1DP@ϐ0*8yiCIxs:K!KkNp5& [o拃p< <2آH8B*1ղ'^aZb3 5^oŕ}soRR"CJ |p圱RdN ydV9SP,Kvى4cBRgNr,tM8xTDq12AX*M/N8#Rq)hgVŴuɼP|F է, F쨂0"23H,, \GVh#mDwKr֙2 [RbL"1!G IƑh!RS8! &̭#f/͙#c$DQ+GH "@"F .uo{r )0t|ON$=*IL[e, V$ApB#nPY^L @_ kC†\"u,eb;ie-ư9ɦ%~!e!+x q:q ƐḼ℧r˚r=ްzs<޹^? 9&H+Z;%OȢnn3+cDakS"IV)-{/',L71AUPfzGjOrl-_2 ;"1 [Z(.D7jCrQ&2kAprmNӬ4 WLF2رl/%Hrlj#ip"mP(CM4XCf"q63:MFJAcl*+!.w>5׶5%޶ /C<9RM#YXblbèv~Ǐ7ׁ٩HT̔$mj.X8f1gDg0V;Ƽ$+UnBNwGys #߆w!rto0-5ʀCD+c⿘>+X"ؼ[_:!6;&8܍O>1@9ߏ5 :j }TB h.!y!R}f a{-.kO7ׁ٨ 1F[EZc9zR(Ei""-i#|~* OOU'\oY8~8jN \N&^:l_ T9;}aFC X[$Ύ107z"RpOR7( ނe%,<_m#"+\B6ֵ:$╹e13s(0!W)D'Yߓuw89 EIJj"C/ͪQ`Kvfn%(ERe"bMu5e24*ɀBm{/!2 $G$7jUd-d[R7*Ug.7.ܻh^K5al(Zɾ+<3$9,.]PdRW2L&,=0*ȅWEkT2Hƃ!K6u8z/&S/$DNǚ q:}%TET0\Z I9#:R, cC x*_{=goI%5):HZ"IXCTe!nb&Pltm?+Ed0&Fl 'E.wnƪLb+6C)PTg7BHLCy9gR]|g%o<sdM 5l1zD\e !]_EF+q[ PeBDPCڠPlt>9 0Gz97g%ݣ`ߙ47=3ӻ4.̺D_}UD`o $o(1qI: rmf Vibb:R FqFE}&3I8;frAT &U!ھISW[u,GTXڋ\m^Tyͯ.p WW/CD[<{xsmO3~Z xvpwS2 D0}.[[޷|f \vA8A=8xn)y%{.2p8 ,NFư„C@` ;ȋeiQ8\ pQ3(jrr\ jDCF-Y@C=0 o1UqʞATD^8퓆N8d#IIk]OG/}3Г = [X24lM9K% >hmw8FWk@,ڎu0¯U.Ѷ75=-ǿ ϿVPFON;Wmz~G Τ=JDs|?:`,Z-S)mR9%̴gG2]|-C%nUO7b>GU$@?Qp}֐~D7IO]7o|*H$3bPoΖc1snWd@hgҵMg̿|*0kyNSI=]1& `K8ۑu  pafܥvַ+{fIĄN ,0s2+rbVŌtMyϧ#%nmrUpy\ `@'w땂qa+8E{m}'=7XkROͨ8#ҙ޻"fn i=Av025Zux!  mgh= Hᚓsz5&sjMոw?FD6L(9Yɧ2/Ic2|! )gÍ5292gJ_ skq`ha36\gT%j]0ci7wSҎcJJG+69<(+M J8;]`G[uM%8O7 R zCJ=YζHy.E+f.p#vړ7-Z1׍vc/wM9m@ Kj'k\#<(DHg8 x!åC@l &]WwG?^_Ƌm>5$ڌw;^41S!푬(#J8qr"&s 5̈ Uŕ] ݔN  w|*+jYDj"β7 J-S ]֞dc>)֬ u6J;M[uS3-dѹWO U~߽⼗ӑP y :q+Ȥ9d~>@R'[i49sPtO P L-ר#/u7[WV ݫp,{.% c;cy/Y(ˆ( Qvm#a%C*n}lXYk)_ŕ H`uU-; Ci2[I\:^׋:PMXkV!^)̭j=~}qYag6}7 176i+sExmH>CX/ x&9v;mj_N^4s!Q2mJ_XU$xCFG rOcP#`2` cPlt* AШ6:BT cPlt* I*#h#AmA endstream endobj 16 0 obj <> stream x[K6ϯs`:*=,t@!< @=\Uzز{g ˀi[꫇JqÿO39C8=?>oO;ko'}qx㗓_ g{bhdQÿt}{([Ny $[/Sǟ6A#yFĜ=?/meHϮ-ɐ>'Z &V \HG7EX|Oa@ )mzH CsdhM}J;:(Ɔ&4[_Zl]~!28a`G~`0DFS5rp1D6 ~G62e]'tE YĔËo&` ]K^BZB`h( >ٌiÚH@‹J%`5VS#v4تfr`ӌi! lYntoKw-4!_=%h}$;JLa sX79M-Tҕb%^ >eogFB W1r 9{T%x!n9P1IZLfa̢}ښ*U ˶ GHe*n,eMadӠ&U9` @iwgRW/rވPN0p:2têp3zp&bNPvl#wI9kCÙEj 3&7upVF30NBLqE"2Kq_vuEa#71F/u6fOh=olPt\񝹻ixjtw,=&*jp:9 v=d)0bWZJ$ ;+XB0հ> &'h7'\ڪ{]PyނJ_IT{DZ0Xd&^JM|o&^zh2jJ $Tޮ:~,t XHi,0D1r%E`F!c}pNpRbm +dYO i*wxs 9Qo JHk܀Ҟ'Kl++(8CYۙe a|aZWi+R;n<=VfJ`WR?2Y.NJ@f",R=E+Pxd1 1},\r:cF`bj4)=C:*k¹ ooZ" {+{5U[s$BUi ?>*fJլf錤T~ ,t  ªC^\p{u?Gc:8f/ ڕMKuVfx (x&~⻧yW ZhOv#N\n(Kט}gEpN\R"ʩ=ɱl@5Nj tQ\ endstream endobj 17 0 obj 2105 endobj 19 0 obj <> stream x[Kϯ9@wX$EJ@~)@nN)qoU$مg֪EXԨ#4Ҩ_۷GtV7OsK]k^y O :^d.tOr?uhOa!\p/חfthW[]o'" |#8z^ ^hzthͬ=!Qe 010dM <XDOaeVe^SU,h[m>G^/.iGDYf@wk \يIg.6,C4IǏ I9ǀ$z pF-. -LE)1YµZެbt+LPtm̈́UлDw,eC_DejyafԤv2VG3ݒ.$}Cˆjy*R?1BՊ1.^(D*6V\UI&%N2y:Ћ1Lk" +nnCA5'ŷe%ݢCzE3bP73(Q)>Oh΍cY30bceۮ|'S dDVL-2(f TvA~&tnAf&$+ӱQEBN;LĢ!,X4ؐVpI#I _2?ޥ :R~i48Hz:˜BڝoE:z"ޜFtEɻUti"'=r-R X"S(:Vi[ϵ aEV@kXoRY1mn)UG{y:, *@葥 l(; M↜؝njHuR(ZQDn&|Z6+Xý|a.΋I N|WQ10A!h){p?]0W+bX qӦw0\f@f00zպ-:vJSAy_VlV{nD)σgY4OʖwKqʀ̌ Xuu𫱵ؚhcg:(V)vj,=ɚbMo'ɰNk͕oo(l~ّXu*Ru,_1:xBV{&i/3>/{xZ(8?wqk{gds:2Vې':H(6.ٿ3t䞫C8H5~wᭈs\) e>EUa9t5FHmĄ<xnY>I~(}W:Fiy;vW2m=m]ыUmPYC=;FAӰK' %@J –n3Y#GJ^ {M٘2*$gmOrrP (~kY\~ db@N}\2\︶l@c+Kf*ۍ=Ǎݯ9(D9cI[xiϼ*y+\ z}oԞ('DԵiF9O0"W3>otC7*VJd%`?#ΪTuj dfz>ǀ2) p`/\]2u50~Μ8D6nj<;t1[&BJ(w^^Щ n;=Nq9B+⴬]7KLmIjzeƈTury!{Vш&Ȳл;@A Kz2,n|g39X2af`[߳}!b= P@On'HDy+tͦ_> stream xVɊ@+u"HnC!IS[K,كAK]d[Wu098|`ݚ_UwbSgRc. XsZx]ah]yxΫ!^`ǰxءw'O6H r@ "bGQag(gO@{ZOPbiXJW'uFG`éz '+yk,TXٜxF~FE"q. :1x'VNig'Y%[&`I9-v:,<ү#kZ ںļQl_'t@.8J#ӒȒϐO~ZN.B{ R:P}zXSWhJQ)h'.|&,]FiHړA=6 V66E0Qʮڜu _) ݬ8%Ĭ/9$:Y:Daԯd4T22\̖H EN8#TQ^F8Nvzx̪]~K#VyAo0T܎+"h|,S 6Eܒ%y5蒌)`Bv{Ħ+bdR=}zq -5x,dJ`$C endstream endobj 23 0 obj 715 endobj 25 0 obj <> stream JFIFCC."  ÆDx[5 )^vҥJܩtϼ_[z=5`1\iڧeY -[,D>(ʒs5I5Q;਷.@ʺsx񈛮X7_5^XE l /×=2r$ 58m߲op <Y٫ҟ4*3:3xEp_g_YtԌ7V_rXwm υWjޓe+,Ӽ@ dգMWblJ ī*J*6"(;p=o&X62h4uՃ\NZ^q%ͣn;rE}ت v3Z\ڶp<vl!Xe`YxMHhމEٵ~:Tv/$gh=^m_kj}&}|/:ކW ^;_M{cioqiL|$qm͢y/0rjwl;*=JuGj v?Z&7\'Ze@mcv #Kðt91Djy%9+%5Kh[=V8lʥZ^Uۧ.U1%sF*$pȄtMDNl?IukV!gZnsO+#U_3'&T98Uhz^gFQL9@$0:֧BYLyl-L$J;DS_neJGvuИ7_ym(1?\i5C:xrdf }CfYk&{;h6\!f$saa|x7|}x{돼i>dO[>N%tVٸz^&H95A5;~==fq>7`oeo{Em⻀Gq1N6pW;a\nf}K>*R/pVNEW3"f\ճMsfc:k{'%kw«V[3^}'>BOcGóCl1ޟNagpVtu!l̡7R-2d."+_D]ZW+S;Rl`  o2O5nO9= (WU\HKZArkpKpũ݇-ƯԼCk(G +> PK(<@-XWBWN_tBP!}_qBP'].n,#dЪ|V"J=&kV 3XcydI?F<xgd2<{+UP=+B\f2m Јy03mO"9 *OeZ{glW7;T9)$ ēDmY}LS'u2q]LWS'u2q]LWS'u2q]LWS'u2qW3 4uQe%n٪7ø{$6En̒g5-5t&U.%Vå;;'Comq#Z>BuQ.wH7o+~8-p7tj)[>˙cbJ'Hvmah*z_[|qM*^NOuz]&d[c$.; #ζ4g4 x&s-jFRmRDIe-ZY-FkW5j1MA UCiisw!}"Ǿs4d*ⓩs鱝9>T?joj>^5!)J&-UFL^ 98Ԅ.L&RIy9$fHSoːffZ>mS56PXICyuC`F D W#)%p(%wa ] /uU=un Dp毹%JKuS "*[iٸwyR]i7Q : ]7Zn9׵z> ^[{:bKx k-Q}c  2(_^`:Ef<$1~Y]h5% `8-n=-dH8/&™8S|֬:_{oީzI4pQwݬKKGDa FxI7s>_+JAHNnlyyِg' c2Mqf?|3 V|EMS g 5vDlFx()R{axg'x0s1 ;kީz(RtԬ)CdfoE%ӌ5(M%d6ſ:f7por9]cS +JWa2mnEM@uikag jغ{ğ}N`9 s*`UW;v\ s*`UW;̀Ӭ ?,<=ؐr՛^yBcvR6r61NOi1. }z[^49$8f_g9ɶ۾6J*<ԛYo }vbY9>BRF7UeI|b#x@lfs:w"dRIx$ٶIx뿌Ghe=,z"""""""""""""""&~cn'\T c`3u^n}ڼ^."5tzzf `mHX+A"!A;`]gf(`!̽h.8 ~)`p¸W \+p¸W \+=mH%BQ廷ؤin 9vɝ[cF\$k _ +2}$b;o뗢qպݵq5|#1 oT  !1aq#$4AQTU "235@C%BDPRdEbpruĤ?L cZR-gf,sN:cVVmѷ,tc҉`OKoǤx nRZ)3) ߟܤ}Ȫ9leJ7 q%;(n@ƀ;t4qۥP1;Mr\vk h K|b-q:Uu:*#?)v!Y1.e2}ó/+UbYs2)NH$K̚l=_ύBCB9|â)ˀh^ORS DҤŵ?Vn:7GeQ$;ekf6SYdO2x'vzk+o|V2+δK>O16: E(]3UxpZCδGWư+#1F(ḅ}rC"핯śPUغYY&DyJ<œL\β)]-9x⿭1 _DW>}JoBUБIYg)1~E\3_u9<_} ~E\3EpO?}_u9<_\3I'x;KbUVrI$t3#j (g1&jH'=0 dӟR#K4It ]CDA>>%oO4It ]CDA>>R=Y*%h3.-/H  Qa!#13qAT @C$4DPU"%02BRSdp?Jo8|b"ב8C ]hq`="u208ϷBRr׺>/!OEnwaixJ,‰d"mBoU (iB'yZ Y S:JpIN)%8XC INϝX!$_H1M#V5B^$M/q8Nz^L? '%gSZ||cJQ Y^. 7,+-*k[ g4*hS~.5?d ?¼+I])wpF*;1lfN¸Ui; Vk٤+YffN¸Ui; Uk٤+]uQ0[y  !$#14"%36AD 2BCEQSTacdqs05PRbtuv&7@`eFUrVfwGp?v:M <n'sҬ,Bdxr<،;lhyDdRaSPQbiW\.8%L+ 9YSBUPLN/-j-fX },orZfURj&8ǤSZ{ʘfo9^KEՌ 0:̻cֽdvE>拈-$Z!$dSZ@*eZ A3BxW!~dbncmfQc LQ+id8t z$8EQ(MJ;#;Ԇ3r9Tf9GE*tI6m)rC`rZ8:B)wG"y LA  @:L٧8@,K a"3Эy8ly_V)7ά24DV2$ftZ % %BY%kXƴ_tqÝăk G% ASw}0c}" %,7KFN:Xs^tSqч{6Б T@'aȱǿ)\]$!i]N+[z@ABK^Y~r].hywO1:I&)Q?Tuvҩ }gКJWmDNȌedb!ΩۧlEJ#c^ӴӃ]2 N4 v8`G*o%ڍ^ո!@? C橯4 dvl>>71qQ>6mnψO^Zͤj$Y!+'"\h]" \dγ-t&RR͑gSu9:ZpPТfȚ,%M{Qnu 9Ҵ3bR;asN3oÓd7P77-0ՓuJ^9je.kYe 6Xށ;KYgfo!%izk ]ܑ]KXZۚ^X\qv7>ьBB @';VpT%/[G9F>Ů<e𼝙{VXGpm?JOL%NΗHN2`EO ByV0FEV1d ()(Lmٍ-6ƖvcK}gM1&ݘnio f4۳[?mٍ-6ƖvcK}gM1nio f4۲[?ms+}FV*Yp ̷EZqq#>{YlI~%{ayCs;.Ljia(I.]{GٍF% :{yj9tjjp#*Xa]Wsv>OE,"j806~-M*k_P#bq5٩{'|W|2M0I$ɈMlpnBbJwG6_ރyBy; Q+)S;55(Mn=OD9է}Ώڧ!z$pɾUc렽R?E$7jUw<7,,a8ȚcG|cҥũZ8ԩFڌ5"j ]~wsԹܶ߳%TW.T fxD@9y Jj-fʂ}uy)ĀI@3IGJ Ԙ9=t*RBe]^vXoX|jo!yޢqn4HfgZz;7[! ~pIANp0eC=8+z&Bnp9! $mX!|GX]M0$21yj1rEZuN(b]/5R*Dt%sn#j ̵K42Yg ?A_1B 93ղrKJCIy {¯8;G*I<C'vAi呓Vo3լ>zzx u[g&/51ivx'?La Οq5X0(ITmh&>p'Tz;SL#,o!nIDTzN b-\Վ(3S5SY$HE%HY@ch,G@gRRA@DRyytAa CeM%!kxV;L|y&_/$ݡIFiSj8Gdf#J_lM :?i3f,JfRu<"SkSx@x6)..O--)j: C}܌䲹MϱP͋L-O&tӸuE^x6)..O$RNڃUeѦ; U'TA dϦȰ3:h $b682q.yKUXLjuI1kOO#0}fMč$8Ezp^"<ٗy14x)lEgY4O'`8٢2(f͇& 8GDSCXf;2,7?gR\rE2B p+('X,؄ʁݕйTj5o'>ea91OnM58N/vXefIVt-9qG=!XCr['q<}UJ XȪFJ)d(:f?1|7yM_VU^l,F$`C6 !.78 K4VS,tɎ1u`QCNOJX`w9sn^1c@gm8]"(Y$0D?=7QI9U=V3ݶl-Ʉ S̘L`9,E+OY9)+o EB7"=fv 0ω{%kM/&czt>GM+#03ry8jBFU(z{38ndGV :&.Gy7c*0ΦI]~GL=dT6,< !%I{RNc9T+!9{};9<фuL}ڙE*T\gHc9_5œ(py4,UQ!%>?1\ Ai|%EH%Wh3nH4Bb k'uAIbKkNlyH 0[ܻl ΤJrU;",NBH PX)h"%mQ1?Հ؜HCyaΨJ K׸}p|to1уMHNRL$'NjuI%hq6!7|oFjjj:":-fB9܎~(ʹv14ć9@o &7d([k;REZPB1S5ȍh7vPi+5_7()ڍw2{ܟ幤b1X ĘuU, j;tç"滑J:ĽȺݔ鲰o)ŸMI$Wb6N]x|:+w'o,n.AfqS!n7]d)@誦F*[='|k-z>h 2LoJm3z|'6J@M(e;v-2c]*J'=G '49u+5̼u+,52K"ȩYt3.Ac5o;IiK0eIGIhKu^86)>.!M}L?k+Y*|)bmnL4RnzIk=M۳jo;^ 'Y<^%oB-HxCHvBs.?vIu*#6 uAOηR~0kYLӢM-5Ҩa4t$$!ѾXFnՔcEUQT YS2p!q/Ekܲ$> QZN+A"/PD˴3@]ac zy^@VX6hoA]b'+82h |#8!O4tJjƕЋ$')HB );G/3umh}#o%0B</Ԛs0=^e:t*tew˽kࢼN\ ϣ;- *Lf肯D- 8/SXՌC+& 2řsAO& GvMW4Id*{PUv[:{HAYWߟ)M&RgmMGW"B˷@XLcvv޴fjfu}3;jhj}>6t~.=_`쉗Cnu*"6qSNM綇R9osCun40ңӺJ|W~"iKmCWʦ࿄Oh^. UJO-Mg:QSl]\Q?'+6r,ӏLQJveʰ89>T;s^ qr,Ʒ&`Fcn"Mdڥ4pigiuTьik:FB; k1 5dHz~ihX5Khl 2к. qB d{^1{R:%|R[:%|R[:%|R[:%|R[:%|R[:%|R[:%|RZp۟cZ\]fE fW*9_:?R?2_1ZI̧EsD K6@6vM?lmW J>-!VOQg |ίFSgg<^aٛA]ۻ+҆W)Kکoa[oΫOb*6h ˯ue4՟K[slCoAe,Bty(dFYHAqQG6aTvͲ!XUJyDYVwpܒ_6p`UPYqPod'3c)~(US..b;Sca\GB9nECT3*xc3ut~}/ ~G_nES!p4JE%RYm-S^-Ziz*3ϣ Ãm-/#HK:'I:99>*ueS|{ n%$HɆ!CO dHɣ>z7,4fL$K$-5rr&J@Ҩ/Ԋ׈YT.4I{;{+?g-BN7?OeIA6 ){~ƚGME5Ɛjk_.=FS~%cEC7dmKK 72T¿-c'fU3C5fN(3:;nƱl"qJ,٠9 qs) p75ȇnT4DVCL^"Sjr !0N]ͅ9*2s})=Yd|2tj&tKק=8 Af{$>dzWAZ(x񏡨o;u37 o6`Hmj[f5;g-Ðqlnq-eюtB3Hk\p\#N¥$)*C%$7 NВIYeCJo.]==$[E:?!$[E:?!$[E:?!$[E:?!$[E:?!$[E:?!$[E:?!$[#U¯JM%D(酡 <@ce~ˎW_p5P}?jF~gZYeE(oU}* i?67.D Yf>Efi@z 4o TM:Y^*q>ط`LE<~l[s!AH."6Vh eEn(җX72+ je) ˌ,z$o#S{p$OϮ0ӽ -Nٌ:_9R,p[:cO毈,sF:8nنnYk(䂋 7O`ĎCP`0 뫻nzK4e6BQ7)Xz!${jgY!;ÓiqPMdQ:"2.H?;9 o0}D5B& RVu9b_K+*XY=qm2.:q)M}Dsc\jF;㑵HV=DU2}e 7NA7*_z 7TKOkž;j N%8S(w(Fd8 & @"<ٓU*ǎaryzg7? Uh[YUK.٦R&fYBە7\,4KHf~ʌ O"f쳴(ә(%%U<&0B OnH"D$H"DNKF^eĩcѣZvӾs  B1Q䚞 ACOV{+" ܣ8(H^ݐ΃:fNv`͸ Jّa6~ `%h/2#ٺNAG sL$&!2ětuD7"1+ ppNsmҴEDE8yfǛ6lٳc>\"w ޣB0wF\>qLU6Pu6@8g fdL 7=G]|*<ڴŧ6Nmߋh20GS(.YM69*=y5(;މizY, wLrtPc6E$\E׌ɵvԳ6Fo|yo7m+/i85~zhAA֪,9sUHcе) h̍;`&@{Mv\-+5$py24d+hlK (5 ĒXNzrJJ8ƫz  A82t0g YθFgt㮸XcZ3ҠtDzlƠƧȵE2As(jȅ|sMNb=z,`˴7ǞW sXWb3aX*_t5xgKX,GA}r~bc&!p1{(1 gXg,/T/S ncp1F{ӠS7,5(Nl0Q=u6GC n(F`XI>&V_[!` ]-Jr<9b%CCضVe) Xuv/XA@oh5wbM$ZO/lII`e8JF.Y2Úel/kms t6DQkapJkr=_s%"єSpl ! aX;DPfc:^t)#Ѥbp՞EI%EBI^ۖrܯjA2@B#CAx[+S)zk 'x3UU[N" Y rSWCw}T Xsؔo% p94[I>qeq.ra2ӻ,16ziO1z;nkOmqK}}T 6b^SBwK !5gy[G0k.EٲYNԡVxWRSGQ-`eMoJ  kkUx2Q[#69սer"iQIs(S8ԿV:؞-Zwǚi\6ۼ5~r8aӰ(iIkƨ%@hā}+NP5IhI\"# Nghe͏HҾ{04/%wRA%M FOǧ]JD^w"5f^P֢h-lC85Lpx:Ҫfez~<̫?څTFEm1"$efd&!@,H1AF`m.}$aW ˶laYNy4'̃f ؅h L76U6^wU͒b` e'lh:j)tؓZHO[-bd!y3!>Ayn̸$CJc V!x Ph%ڐm7H}kPno<N_LX3d'~zlH#6̐>m8H[ָGjiꓱ.0o|y֥&XhvU8|"a*tU]2̛[6uOG{[c5'7AH# NfObƑuHJB(?h' Sxx4+?(u)G ,mvWcP~6`(8%e2eh[{p@z *2H苖7.s@f2SܮD+ ]4N{ K-тjMnE .I9 ͒$,To?kNyy@@4QeBİh:-Z h ,dG7,E3Av,TQ-@^XF~G܉ E8/$hoSzUt|ae)όS `"; YF׀2,HSS_z,FeJ fU<>?zsE5McA霬 a|gT]{=v3 =(=Y&o5S&Kˤk궁]|MYQ*xx`&acE1`_{΃P~-UOu@40?q42m?KauS^Ng q}:vE}dOyצj,Boda<\ƪ[>M h69   beV)vvgHA˿A\2=:/ \9/ʄ mZ+z|?5[# Q)q{Ǫ]u]u]ub+t8 خ[`T_ &5^Z+O)$G)14%a}׬Ke,L?7$޴_3gLEY?s0AC ]%žm\I@ځ2Z$nڂp `[ Ia p}sIz=,Ny!ӆ]1X% 18,r{ϥO5>|SO5>|SO5>|SO5>jj ?kg̙2aD7k,tT'=5f^gNO2Y-ͥ բ ${Wih a zPl\/e/H?K Z!8a O塌ӌ5kn ӜQ!xΣrw 08 $: .A֡  @@a6!q^lĐ8Y|hC"+~B 2@,@? F=C;;2+&x=+\" 4K#Ʊ*I 6 hB$" (xF{]7Wy?PVOwGcOwvw<=^tQyΘ # ـ!@|% )e+)0HT]\}#hz'm8%x.VG$zS?pvɜ r 6:tΥfp k{Bj zmd *A2R 1*Pr*R@#0Dg25B$! -+8 QH 0&!b2!}IŊ}_]\m;޹!Axv"y&T0 ܲ2_W=3>|j"#>r^:T2,LTSdb/P:m6=+ϩ9(e1_a塸idK]'F˺"Ab-d 9e34蹱h9qM+$%TB!34@MhˆႳcmW dY5*ڡ 0 EÀP&p%":2PFY$ձdm#0x-c1C$ /1ҙW۸"D&\g,F[2fvl:<ߴ|´f,b *QE$ɂZl0!)BYg뫮 # m-2mQ|w D~"X fyhy<< #r2{vrU"@cɂHk>ߪk>ߪk>ߪk>ߪk>ߪk>ߪ~{ybH6VfjmUm\/^qtN|XPpw=?_+!1AQaq 0@Pp?A\cy 0/5>O>4&H=E)h.7}~KHx~O:aI .5lKGhh,,.=zt[iX,zDބ3s4r'N\}uA$Ld),#1iQշsL]NGXLc }L*nD Ek DʀVry5''Rr}=(iĤ;15Q~DX/k~qtabxz]cu+NуTV'-O0EbnT馤gb 6od^ڜ;ڟej _>˳|^vOGSP㈵R RKT69C&HܵhTm& 0u0Q )C(d,!N%Ù\yDW,q=5s$uBIyWNO&jNOٝ9q@Ai/x,%jЁYR#hx IK>`EEi$EU3ϫP>4yrFc.P) 6wH44,$FXzm]Б1db 8;6O4X9ۨ>t.:qZ*9C5ͺ|TjY1#*/fř2fdgw7Jkh7fIo}R.vfr@UFJVodȅHrvf52>G.qYua$uڟVJ8H شq8>RQ4f}coV4!.p%Tc0 Q1R㉈h]s8bqsgCeNv[b^žk:N^_1D@yw(ȂE$'V'@Q1Q1C;wj c hBQX6M̗huaY!Ko-Kú3c1zچ3C;ʸ'$( YŠ J:D9?󂞓yrsbHAߘĝox4e>箣P} 4dJx=t+]3Z+ƚI̛Sl։SfզY>+c<$@BC鶰$&H(KA."^BP"\Q54FY^ [E`~(8>XRLz[6p7N2<ǯ'~gο3_y~<1ۏ=Su E|4ҁU[;D `Kֲ;:PGTaѪ#ye^s8h5ߦ0uN؂",Bg}:gH$9u7ol=5A禽:|׶}OϠ^>zkM "&QITJGVr>fDm5?AT`~F~σ-,!1AQaq 0P@`?QNwK?:ckh`oآw|k1^sN"=skr% D/ԓz.Dz^`'fȰki) 堲ڠޫ9‘oH!CuC,t1 0GU4lidĎ!v;J|+HYzfCfC`ŃJ,leL|@*ކ&˟3bԷ w؉@=(!!g^?dGa5TN4ie1gJrbv,Zh+~U c|;cĴ x\}e +Xb* W1n kjh*w( gJ[%T4`}ӘkAP  QJQ.vn Lʳ$WCs%S Y1'Fr1??.lU珟9dos/`%%6"5X[ G9sZBQ6u{zޘ0?Edd@6 2Q$/ax\[hl7ߦ(8 d@1bH#4LY$j5wÚP ]6,USF`Pn )DA%H1}pǭ, ZGBcU,?΃bIy0%#1(ulA!|A=n4zZu 2 wT!-~Ci((0e 2)' hVk[ڻK >&rG?L]k8""+j\)AVt*P !t@ЀtkEe(Kt`X#8"OQqi'{2s)=?)=?)=?)=?)=?)=?)=?)ls,Wb"nԸR{߯_ɝ' YuH eBk)H"о&q"R#Blu L0M+O.*(8"4H"FviHc}*&- S8}{/h=V.|7ctf=_]a=@<Əl?=h3<Əl?=h3<Əl?=h3<ƏlIc8/h@@l-6vu] SO<]7DB1D&@2H@W%5īCqӦk}~rdrs"g S9aRs9{< Yt :FyÞUJkuIuS)*et,6Xw)]J^KƾBG:4^L?? J  y]BS J%hUZ08Dg11{!W&w=g8q_sߗy~ ˟ o.<Ϯz> w|7ݿ~k_nx~! 4&J#qw9iamnM?9 8?&שfo)^ֵ&AebP< ݧ@B@*߉5l%ze,̴st9ch̫G+9_L 5@ oTER@ oưM158OeFB"A0 kcqN4%qge+\PD(ho%hPB22y-aEig^\}wr?>m:|?gyi~ '(vo^{k^u=az˾5y?|k`:?]ujR[siO 2}ɫcPVUÒF6;7$g6w9hO io<0W+YYh^hPXBaO^9N)L b#4ZTQjdNmZ*M4 0!_ j z_?تrBF9[7M6⤴-öl6uϰ'TmUGӫdؕNvutц@*5TnlJ\ڢ#_#_TboTHn[Li`P@A Ԭpեf+$!(1F}ʴriXF%8tZiD! ``RHq%fѐZtWÖ?m/Uyx|"EFq0PuwkW B#j|ݘ:eM\PC茇#tpCUhqMp jo3 44 T<#gns>H!)$$,mvv>h'詟7ף07E D 25;EArƛdVf|)1EF[h3\..݋هH oN}s> 49EDE:M[/)+* "[~L T=gtPE"J=(䡓2^5S%i Q)! X$omY@Sā5T*a5$F#[ҍ6G'WPLPc"&KGCGn(qr8{!fwL 0m~Pc!:Um_]&2 TGߎ}2)e/nNR^~e;~w=<#l u:VOVrONX ~-"INkv5?2HL2 0XͶ ir})h/oۜ' 9^I~)˙S|!fӨqV>ԲTA_ptO'Ӊ=iӟ<0!7{|z'E7ӷy`ǖz2?!V菭 {;Ngg@$?{EX ";ݶMO±RzF|@afe{E*\J>3c0pYX9G)0 4FA7k޾2f~4][$RUKEB 〕&XB(PNp#"+*9L9[?9O8zeELv=ǞL4pv"N^6r-jd "l5̭pYRsEbeheJugXB 7fG{ivh|C4؇IbFEOY铽*ߚ!Q @+<|O?|C!\2pF= %) ߝzkm<>}pg;CB9: $`1%bI r_-t5' ?w+O_Nqrnv:ju /[~Baʼn,B"q0,΅q@*ԧ@@J*öJn~Ʀ\C?njJ܍VAi"%|e\hαXw^p5fIGX!I7ͰMB]~q?Cg0<,to^uX?39c+~7?T9 X \!P_[fbø3 ˨N9)$źBȞ'Pڍ"Ue LH:Mc甆F*YM&d2J.{$]lѯ!cll)1 &vze$*14f0#J/<p0N8 hUDGJ"d;d;v2}0o`( #(D~ &P4iDUCiA9IaR<$ZOԆX1Ȼ28vWABY^^X3\o]ښ@.)R6KF-N<}7޿?w bjJ!z<@$a>v'_Z1b"(5D9P G( Ho+@d t"`46`e#Bn,)B">]JT Hm&~Y4D1D>ׁYiL-DO*OS{LH+8&Qh00 %/,V8@4[؁vZa <5Hz? )\J$Kl6VQiO_g oYrxmB#j-#7T@LI0&Ȋ1^Y1bp@ ugMbJWV:_Ro:U BtՔGl+-@0"AA^ynM 2}*OEF;NOQܰ@ \qM N=?!(EYf2>h8Zk~*!h,ֈАX_?S5|Te,ru~> d9r_;qgiܨ 䆚 +d~F-8I` QtFVw{ޝrTBnwd$A-LSFߖ-|j__ 1@ܞg\4" 2{je|7Vit吏4)%(^lh_K?.\r˗.\iƟ]rzp?~BI-S|v.B=wGdLc QRS"f?>]~nLgBl-ʻYWY1'M}4t%L ȂZ8ZȴX]US(%0W0YRVLDצi ]J l(YPSf4֓J[J&h S82Au.>ȹ߇㎚ /_i ^/%y0/$)ߩbLVgџ/XW<e^E/F Clqt d`\8rSܨTU|2 *A$ P/5EZeËUQ-T𬂜3Lh *,,xwk\j8-{m$> stream JFIFCC"  %c!X `d@M7yٌ[K5)gE+t ::_XwS UYZ%I-;Qe0IQj:[t]YuuaϽ-UZibo͵sh}Ѝ{UҧGr孜noy?F={Džɺ4 ܳ}'R,]%T͛mFLs'.:" dkty̾Ό=kGwZblh2f^h7{xuԛrZ{Z/Tjj|b"MsնU["=Zkmv\'AeEZ!mʪk@FEMRZe5WUDzdFa dH@Ha>Qz%G79"|)^^*c?eqhpZ5\WUhq+J'?f5.>(C5P/} _uBP} _uBP!} _uBz"ˎ|韅dzsL}qO1Zg !irk,6 |;] ϗwr=N[Z{ V^|wK8YPl0:U^Wt> c9][<>SlG|خ)pr&tTh^tť.rW} bltQE}X'x#˼A-޷>ه;RN>Ewt39}|is?ETѝSsM>52ݵWxAuFSI HĎ\p%0 ~c_z;;Zl=0Mvrii'lA]ף]>uFޯc|rd~IL>ǍPl3]y<5.[zfkv[֪/+?kh9 XYiRKLYuk/=YYSz++PAoK؁&yv3oȪ5\ߘ p]2n۳ $ A]q@>^jϫ՜4mYUqy^M[=rmfEގ)Z7[M6}E2X܊IzOh%q-P 9uy}[%mqPx:y2\V{{ss8|0bp:kӫ]^S5>M㑩ͪ[թ爧_ "ieN+ A A A S[綁$Uj5Gy >-Pyt齦8 2 065P!17@A"p ͓FrQU;&fl #DZ|/Ͳu RvFm9Ficd.d8aVl,0h I2,'N6'umfZ 432&1+&rP^FtĎq֕3K(Ix1ާgTZEͱ|!Y쨒Ώ/ekp]{+6vn2).]e)2C |Zu ;'/J~W;WX[u__lmz78Fv^'9((\u1K*v_f[?bj/.uYkNOm:\;1C/ QPJ+(s E4Իgl!Ee"1+&% M!ńl{{ɽe{`4)sՕHDҊҳ޻M+ PN#h%0n)TIFE,\Mn+&v*y{EP0 k+yAV˅4TkV^ߨR̔L4$巜%䏔YIq YdG r:CE&BBոU9_v7&UU h޻[]Thlxŋb``OcMؚYf0-q_e^fJTFNӛvr l<,F땇3l݄qY+:f]o?[Żc]c^2$Wbtzvv&h:A6IjcX́ϯsy;d914dw̪쭲䱞k&4sLcXdGGT'y|^/:Ћ`q/bK泸1gf8lL8*3`a9> B~y6th[G=>:tt +WG@]:tt +WG@]:tt +WG@]:tt +WG@Z>"!a^Dy@=q狸r&?E3?{.Jg =x3L)q:t2x°7*>^+0)QYQ` pїd!2K 3DZ2x4&x@|k΁8_SC+ (3bP; dY~I )Lq>?x0\[}5eC0ODmOo%L{$Ə7D.R%[wqeu}jw_+u~ЮB$l&V#b>,jα.)"%W?ݑ#xoQ 6_+u~ЮBhU̱0C>`-a$QPkTRG],zlR+bH9`koTɑGk`(-zת^-YJ JYqY{/j{rsy{縉M$9 u/[kOkQkQkU 1K.ڽB"^uǟ58jFjFjFjڔEob0K~#;4]C&FÅnH $65h{ {YB[fd9a=`^L/|Po++)Afg4| >!%\ F91GēY[_oHI#$ڧc(w;_?=6sK\Ǘ? (2[Z [}xSY&09vyfqM'ZMnea8tđ X1-!e B+.S .aR   !a1AQq"#3d 0CT$24RSBDt@P`bcru?zgI1dd23$223C (֦ 2'\}R[UWyK)gkNlKFjT~Q8ḶUuʿu :HBYqwmq [>E3F^H5 tg 2Ǝ\Z&juKMM4o? ߓ\{FK<"dx2G u7Q0PHmCS"0`MD, Z ;>[eY`cu[*J/3ytVgQ#a~l\NFEIlY*c`N\kg"~eZ)kq3_ӧC,D Gl `T6og2s VUXo?? E?Ct2xO2l $b~0Ĵ2nݢ'8pac8K 3;!LqlQ,ZHˢ[!Pr|0f&t@&'a3S1r[@+`+`Q%SWRGxДДנM3W %bYf%bYf%bYm^"Ym4󝝍c$#DogNaNZ<IcNύW# Gmj'e I3,;8)[[+]ocܰ_ua+Wgxz܃gw(3HUT&^t/@}T4ꡘ{|b{{,d=N]?d 'rV"{zi ֩T8ISČ455 #RS-W&N Y`} _E1"cZDƴiR6DpFLV  !1QAaq3T "#$24Rdr05BDtCSb%@P`?:YQVw\+L@F̨ c/[tAHl?V g~ِS󒑐t3v ,)`>z] Y0e\$KҸ{Z:6 d5֍ӉWV./Oc4fL`_G"e|ZrP59'b.7HH+V/lh͘h:aj,k$+^*U/?,cxR^N\R;,ĬukX^Ҹf v 3j΋*FVSOt"iiGUVJ DR. [Vz2,4jF))sG `YN/)A`mǃV;\޴eF US 4 _9Y2jhZyx#"(Uh5$8^T+A`ƗNJ?#O "4iHa'>QeQ̆%}C:(D@DbJ S0 5Dj, (jB9?I*@+kK(DQQxX"EйnkcZ(1߬fa֨Fz|4SSԊ.YZFI}Vx:6^FMLf4\,p[ :d ( jC%Iy#;Qv @= MW̍E1m,"dY{5^#G:}E?IH/QjF=e$m$Eפ^zqacJA)7ܥ M۪ȵiR>=?>\?>\=? A0 {RkM8I]])L-Ҝ$qҜ$ݝ)LrNcNanΔ&NcWJpJpS[:p vt 0gJp vt'(=r}]Z4]o޳{Ek*81* .g=9})0w F숍'v9Qfc U ǭ zndjϵ"PGP;!P& ȳ~T#ó&A?w?Vw?iWU-D2Al+p S곫|ݫn*i>~d9:1GՈ=#&4V:s$kM"2U@lѕ,e-9$ff arldBh|=hLjW͒P`Y0Npc ,%ԺQ/7cR=kmesJȌE`0'<`˹bDy֌x|Ќ(Ȃ{%ۙ8pQ2psdNgCTG^ZҴ(%Uy3dկne ,Yt%mʌ6i\Fl(v-EڲjT񾴡T+ѧB4gD֋͕$ yRlJ.MKk])'fuwgU6~ ;("T uGW(񂲡韌Eo$Q2p:=Њ<ƽ32#&L%4] Acwsjȍi`C{%ERe"k˓8\v"`1ƨ 6k~َ hҗ lC0E[oz٨ aY!"+4ʔ)RèŇQJ)@aR:%ŇQJ)@aw(UUs`lׄ % 㸌yZGԼuS zR;@1w ;I>aOU.u˟/U卹 A>e˪GM/6 PR#URO!t[e>}/RsgMT&JX%(cu~iiuBjcy֪=nO[; *lިFW.|˛l`_d^lޣeUQ}H_X&ピDCKOԮhԙӗ?ac_vqGPr$tC:0- z jY[n₸M)a%U{>>B2pu+e#5iݥtm@O\T;M+ϖoKHە|lG8I,T\LŸoNVFȇ$-g1N&kNyvӵ; '-0֠V6|F{K"s@3uc/=J8ީ;e5v+՛?[M"R\WtK*sEwr;yaii5RSUoÒ_6IJKa%Ml4#!ZH7jߩ92E**Rc(!&iad}iSkE\n\7sSȒUwqoem9$m=PԳlpomYuKީM TS%aK;| ˉ8k|ݑv^i? ,ɩAR&Ze/>e *L骚*O_1}-5]Mަ~\TED)썯mL]3G6 :F~"BTfia ԧ(*^*.;"j q#*S.PgW=T{Rr;ίn5$xTeE9qm\TD60z@͸dÄ8- VB_gMb=V̯iv놶y%YvФբ-5ӳZQ2߭ M5M/[ՖwG eunu; ؿ{>x_]?ɦ]q%"K.A3TRK& 'R<YAMW_u> l/V`ؗCs=/]|0q=SeJW䘀rJu;#t]UPEwVѳ׵#\v{Qdªwᚃ;u&RlHĵp$[UqiO8uOTq֛|[,zzA>>^\رruy1f R~1ԧ{3Yk͞drѲٹď'2x4 #Xʤ 3UM%q(8W= %(؉Y!!wz@GZ01- \I{[v(Tc UQzo.*>󦄼ʼn(; !; W1V̨@ʫkT&6qcRG_wbpT˪;AEE>ז=ajZ1aU fVh`L" T6~wH,Q!M9HAt B7ڌ]+֞MlDI"<_D#BU PnKDsas*(u9wxjyB^FgPv* R#'O5eBU\Z?y-rCyvupA] .d*K{s}:&[M:T2\хvdh)vhׂ$繞|tDlDfzȶ% Z%w;#H'+u\ʹ.4] $ l3R(_]O E^Ȏ{h{5䱦jp"&JU \Z-K^+ZwN6vK G!n`*ue"}=iϊoצfП>TAkSO++*RFn| 蓓f$q!95F5 wiy3:;[ "Ŝx9$$taj;\K#4ۗ]TSzD߅U\*u?M+_gB=XEְf-ݧsgFwJJZ\7krPD2ۍdE|WG|OdUUq{Z7Qn뚲h, Lq{:G#MϷUeeCKr~yaŤSwb5HfaO8RZ]]G`{, q{e&XۣNЎţCFuԓ _k wX*7q!@eĵbOUVQW|Or)dbLlpT554E<3Lk68{c#:ܗ,΢PkfԃԷV" R-@>kQ5S̳NƸG+~ঋV㭖dNRyn%|azIzUc\]G]9c屛YS֫-u&23>Z/6.xc,Lerqyn;Tƣ]R*tT(gQ,%#^0\)"UYd$ [?fI$dL8{79L TD3QQL]!*u'>-l[ ECfM |4=UU"Rv0K[[c>=C8툤q*M3whWHKI]qiEL3 sQQQ)KN2kn`'ˌ#x"8ڲ/4KUECd ѠGArQ10d xrbQp+g># @bHi zAGe~Y XEKhTR׭FpܓT]KvN+'μ2NyG ^#V"R H4֦ HdjMwÖ`DBt*zrnJ t5 nK@iY1 JVN`NppgUcA2dD B^XGьF7Lj[y!F ۂJ2~\("$;1b#$Zb1etaUf+ny$X1f( Zi֗A|7Ii yA+%ZCʎ Ĩ*#)R[ԨPff&z=j QEP)EuRvT#L4ɩ(7$ʁ(#/BgGH$*EΚ35Jj*:ܪ6 !WS3uDJzf*e$-APµ!o̘sVA#'ۑ4Ch廅+]IMBq-bڼ.^ўg M#:iDJڶHrOCH\FŸYϙ'J;\u1(^ [֋HSp2L;t9:?81ROĐ@wvhm⢡#}ft [NJ*6 fDfu> UcJhʅ#ҷcɌoebԶN0gNĨOU" DMځۮXS=?rǚ%|zFp@%ў/򚖫O'=v#蒊`Q&M1=$] W6"̶a*/Qa[3PN%67Y'r 09EKwZt9uޡ!Zni^f7So0b:2(ک$NXxjbgm<%+ vbJQ%bv >D,H>;}YtT' BI9>q#ck^ɘrb_(| )12űkl YCpߒbCZ=wHߕdDPݡqYK% laƾ>"3:E[XTMH[`g!=tκDlKi FTug 1>i>?ǹE1>a}>aJ:܆N'3VT:l 3GΩoOy暘$g8Nojd)'vW??xJ$$ ֺp+8M~ݪҼQY3h!^6 j}Rg̩\GM|LJOh(y'xϢ Uw:.XUA˱KF^#$#uMF\nҍ)O#{ͳ&Eu( {wtՁ} ';{l= ?M"bwkGDԶ7JA;plqL?$U0̓\f>h.E(D iE#TԵ+h_}1ge1  1qNvy 1 ?JrHeC:fѾ{ {wMy/ m]D#,q\$N__ <8]sD \&Qb&'`X#vN(qaXe?5,0;/Fogk\q32EHQɥJ0ip:rca( LCwa(GD?+Vf ݇fJ*碣" j+J\$ngA[Da 8G3Z}vFjazZ4l,Ð'(r$deG+FHPH7B q]w,j mGA7cqoȲ`,%\v2e.=l#AaCuBU9IOYd.Gj,\ mm7udPm8KwAӓsY e: *aÝΰgjs?,_ӽI X+>͖0ׅ,?(ТU!TԤ`&TSaۃNM0r2;=1,J.i;k+MFrj4<I1v<>ol}Bw6>\hLG]Oi,CM-?4{j*"/D-Kɺ@Ӂz-N5qj:O#}= ,?= IIM/Gch ˁv\BD~;x7r/'tm_S>/?pzwhf{6~mԹ?3 Cg"孖 hAڵ_}AF yXXh|f`źZŤ^jzf;Nj𦨩}\5Bv([x٠1|gs7I0jˑ8K,{j00T$W}.')\M_Q5kn60mv\vr)1J>CՎvc uf{ L 'TI,f= CgAAGndG5!>6N d9g_2ʑThDċ*x<~_F(O*[iXjMwza#:'6Iz֚~[- _4Rj}vu9}/v!]C}Sҵ:};EA:oK vit͎?cDb|>K*iI|i_U_N2g8:R3X>ľ9OmsUQ' %cHd K*R|ol_ӽYRD'(+(8jWߩ(>訌sSm^ h̎ x"4zI~0JU,o O\~m`RJN0 jp0|t`,f2S2U%HHݺw)?& Ga'F5襈?}?w{ 6ҮjOk*ߙxSdH2#>_dcot76pJ kaza+е~RhOo *wc. a`ck U:{Iyi .ޝ'91 ex o>BR`Sne6K&Q2)eP*M5uz/ޣ1wD"qі`DqdDc1²]7z4bX:LsPGCbìNa25Ft3)rW,6bu<`ݷd7^)uTѧ;nDT ,\{xL:g;uDcTҞm5Œk/ZeYw$x]?6xIsն#{ޡoAmoƌKstֆ-XNH]fz 2Є`&YoaK@((^^\xayyp~q⍅9NJ6(^^\xayyp~q;5{C+l۩yw'rr$>Z[ 2װc8"\>zؚ30Zz#?k֫Vj .!֤$dXDE=QtE# S1/i_vMiz3UM]o?jGf,n"B(.pc>13%^6uG;ԍLWA1q'YnI#۱UZ Q-H XB2Kȍ 2s1P&BXzEr<8x5a~1CjA9DRuLKё.'hGb=h]&~{Ofux2 2̬s1k J6U9F=;1V%#?'hz,}L{,hӣ?@F!FA$= q8͞ySu(qPK[]<(nh1Fu 5:/3%0f* Jx#VRmJ|ej a4j3k u@<͝${Ycץ.V|d/_Y?ыcwj[׻wm̟Y&.~.>*2DI}5}[o?׽>qĔS 3 5u]Yy<Lc!NRggYc=! 3Ȑ{*T Ub(a+Q 3Li-j ;3s8;3Nɘ0݈-t`D6,G&:)2͇WSHL8į5E\=Ł(Zsv=u׹4/ ~O*T5E?]fٯ:ܫw i۹W?5W yuQEϐ.jTC[s>F۞6v>磱5|=m %gIػ̒imj 9(d9'R%MoG4k2տZsQ|DdM[5fA^\!bO :@&4 h١ VL,2Ȉ`|~)(_f.J-[::P>5+ɧ0CT툹bJY9V׾,}DhCDGD8cPUf^mGB#@ NF۞6v>磱5|=&6{aE1g[ U 0[ְaO]sҚ2 J(in.awƐ9tK&d KٚU3ɦ}I(-AiuU54\ 7M[ |"6qٞ)Fi]Xy{vCN݅Y=O0kЄ[ .Ee4.> [dv(ykC_f}=6= 3N4R3CS3DAvMt_~nQ!y[5ˤ>߸Ɣ]`AktGxnCOdS6xvULjxiuicUbQ*k*%[\߷yMӿ4Q.3 }e'c!)Y.K4Rbׄ Inxgfހl@'\Z=ucj!ک G5_(&-hp$͸)䦞F3 $jjb@Տ4w4ٲ!C%Afk_y&œ)NR!:HeMÇߘS2)ښn[K՗ſ:}6dkDH<9Y:0"CL\Eµ|rb,j2^>><-|7H$uƟB&/8YIۜnԢ$WhzjhO5q0mLI_LU-qb+ Wqϰ/J3_6yLc;*յ7YwJXs!MK;:>Yl[7\ v# $oTDo ]\ K_jVhJڴ-0 h!$n8Gˀ~)gC#lnj Ϟd-#dzRr)]`*IJF3 'ct_QTL7) twEڬ#ez;s2%n=lI4TuM g I>7T&/Jj;b글d6kt[qmdtԤgxNiа/JMi GEsԘ,<*hWf.^D?j'77ioP~gdnл^e:~޺Op$o %5.‚;7EK3,Zċkj|-t N3)fbd2* n3X8 }7͉}_"?QGu=C56d;s?7{iۻ2D߮Sꍯ3c'SO>2J80:U=5?PALihޫ.뽷WU :wYZIHNbw3dG ̤Kh n([tG(bRt=^.闑N.N,HxpJ0 Ҟ06iԚ3RrfُM I4^^ IY(8H1`FFU 7`"hA⡄vM*w+51cvqۦsSN;{3u9]g9CSIڇ(s5E?Q$’I# ^ibsv9xwͱc㿐 ?6Ǐ @~m@?<~;lxwͱMrJ\qZHȸ҉5c)ÌA̠;6r$2(hqmamMeKh'7i=̽f?M6߮)wn5.WO?Gc!UĖٍOxqM5۫v8\A?Fhr)-靐FS .F~xV;O_ibeon?cq#/ X _Ȗa3Їa TC̱q=xeySxeySxeySxeySxeySxeySxeySxeySxeySxeySxeySxeySxeySxeySxeySxeySxeySxeySxeySxeySxeySxeySxeySxeySxeySxeySxeySxeySxeySxeySxeySxeyS!aBw J։_=0]kY}vys*)ZN)C2FIvd\s۰@oZ`!iZkQcjn'.!1AQa0q @P`p?!6u7FhfluZU6nLY7&Ipa`Ȋ>G~(蛘6){76; PX RG<@rb y2Ťe@=f ~LgV :* bĥ.> QA/ykX 6@ԢӸ:$d] 3%9YU [ =z#x3n1ﳍ3*ThaDQS~J[pL GQB阧g #<-_-kov0è$&h!fX.儍=1hby20sgZc m)a|,. [XD r 4QTDOR u774b|2ޢYu68#C B남bX YQ lI, ,V>+gG0T(@ yKVn%r/Cv 1*O.p e[QFs(s1Ak A-7Ve[-.tjv鋇v DI~&8*GQ3 :S ˮM)*VJOc&ϸz $ˡp*.=$ S{_2}z643O=huY4boIs\*dtɃh]U dq Ńl6#-c"W  Jօ@vNkjY%z9HWBeiیwPb)S8IRGQf^!>YMۼ-ʈv(}F `ܚQˆzHȖ  L3!qƶfpe?7' I,'VZ{0QCؙ^RV-M6Xq]$1N-G99viq]/vgز(tjk*+3 O "b,/t 5=V_54.Ċ ܖm5s#,j`ڶmvZ>l*FQ"JŽ6A+N -2UIxۄ?za 1P?G)crHi u"T7fhtC x"?FP2D43GKK$_o?G/^,%򆦥_*Rw*_z鉌ô)q:|+pb/)=Gq嘩5Q(J%D? g3L&~? zo†-QFE&P{?|M |GJsjxwv}uv+Slv$Xbĉ2tށ_WnG;H L?AVZjիVZjիVZjիVZjiز+ru0zxYiOcWGG(fJsqNY=сڬ#kUcuD T!a](%bq3 HU=VGC#F +{Sr^pl8oZWF 84d4wn8dy4̚ n'OO_O‚Z8/槙5R=W=γ4Yw(Um9n >jWǷc*̌0q7/)i:[ \ \9׆&fE 07~NmiAyQ¢4Ҫwa,GYjҵty>RHK?9pZƍ! ىwΦ8e'sEeMt?C–`+>Ve!w |rgXwRS"R?9 YigM1gx*L=余lvS+j"K6DlZ8秗|Hosk~y[Y VZB4/̕Sr[`8 3QDqrgdz3ЙL%bwX{Uj_W1wvB !7q缮17xD3ЙL&z= tBkb]q|n\y83"caScEC_:Kǧz5-+y=MvGWθ^&̺^+g_՛6lٕ81|6n5TS# \J@ijp7YRJ9%U2Nqc//l׭ֻ3r5 F6ˆF&NF(P[^}&כ6lٳTl z[ѺdSA #Lܒ-Ia )ǠBK)5IZR=@g{q},O8Vsԙb׆#sڒ&( .I]mrpX2'-5^+T AA6BlѦ*߬SC+UXxhQ BJ)y KAGG{}Eu(|HRdfoF ˗~Tk)r# U]4gt;:h[}d"beaGWw=DWܢVW:β"fj6"JFQ@$J?yϏ\v: 9)Ԉ @/Bc&V%0C_:uN:r[yb tk}UJHy}잞"KH0ԾqݗuRR㪽&U6=kasXٔSRI#b?+mylXm}UpP-JsnTGip`813C#-Nmh1=4S)jP">#a>@pK0SBA u yBRɾ笴jQG2?dMQ{!yGϟ>|ρ&C:],NJJ?X ܢq)9BX,dG^]_W?WwZ TK\! %DQ(J%\Tm~6lٳf͛6lٳf͛6lٳf͛6lٳf͚,=(nIiV3zͯtxٮBʡzn_ 00 0 $GwΞgn G$=@f7! ~RӬ0  @ c\1(0   parZ +Z$@a   P9֨ 0qC<-'ښG󓟬>޿=>ש'?>' ВmTiPظDo6 ^E+0M3HƂ ,%V* h@*E.A"2bӠ@#-d:!J8D@UĽȊ#: 8%Ik'M8P͆@BV*af;3&L]$1ަ,'N"d<`K`aP26(R,Dk )2օ"`T2KF(CPJ"8dCS5[4 Śm@LЬ# @7.}0ޚ*ZlZJ[LTp*SmxCBZ"`YPXI$iH0Brnn6"Ķ-G[Dc XCJp0zLiL]+G%9΁e'WEGHэPAaB1B#J[Cݙ5I gN}Gӯxw#t~#(}W5O|gBzn=8^sqס?=U-qkPQCQ35zb*`2æ"QbjC+{&&f*!s4 6HUKe@! 0kux?=x{ƄI5fC=p} o1pk;) kx߿A?I>N:qH LڤdbZ}u r%,3N*u;LRV|!y!ً43G8#P]e33"r%D ( @ 3tLHD`RBo Di:z 1WʘvmwkjH`2D3ޘ Z􄞝FNjɭC -NM N2mSkm;7;>p|x?А^yt=j{v!{j{wLk4C0bg?Ơ5H)iHLLͤۮ>|lщs\u#{ Ng`fD:BJ ŹypoyLGJdQ `Cn-ᾈTsT+!1QAaq 0@Pp?xϧNN7&۟ONuN~O?ߋg:'|?9Ω=v? ƘTiGu1F[`Im43N\iی9I`:0L(X.(W2EIG8 Mō@Y Fln2fl50 W-`Rt3lib$FF.EX4ak{ aH(U "$2 TⰽNMQg qq~'\(&YR3n!NL,T]BN!%vtk@"ȺS%  E:;F\ ۽ Po iF 7Q5 P9i^+t` aJ(@C niݽu3ca+EɅvv14m 硔W0A4rHOQQ[\sdU1bV#qq շJ%hhv D ԓ qh3)\Ibd ;:R!@!f bwN(=_2#jj-@0.zϸ6WKsٔS 4䩁vQw!i'}FmڷLѺ[Qv9l̥TDw1JƚKS屙ڴ|c1=%!+m:Nw!8ʪli&d0;?/|/uk_]Odzzƻϣ;D&3g^_?x<~/LgюzjƶO`Y IF t7ݩTG bX%Dd`튀$8nt:}㶁z]Ʒ=8>GaFtz =Fk#űʹ[Y;dhpa J@vi Ns#)qQ 7@D '`%DF+%ap}K:mU+R[6_];HR[hq{4r`cvY jMB'K&:{o5; Z@rXu ij<Xcl][vvFn8ۋx蔙Tpɵ1Ej(*@镊QáTd]\쁧Ә@UJJT,aY1\5JJGt>6kNޞ;b_ӷsG+Db#Gï]8={^u^_/ocB)轿C6z8s ^`q[^W{~ƕcc=޻̛4<>5^_>ƫ]߷?, W6fFcz{w룄#rg;}d|PJe1TBnLaPh,V]P!%JI. #Q7D!,#Cy4( 9'Ez lz;|:0>_7Ϭ(st8+cikAdzFB; () waKxHJrhS61zV .K js: 7CuFDEFU&&Ѐ+KT0 aãL"a""[opn.e)Q)`{]Έv]߶q?+c<>>y{~{8|I=ڏjmt6;u\mM6qEkTQ*Z aTrP$C H%11f]J yDAQoժ26LSx4bF d dltAfda(@gc?3prfB85E(!͚Q) @-H 4~鎧Σ'Gyx|}Di(sv>uۿcǟ߱Ξ߱7<T WX7݀4 jKnޟ}/z|kw<\v}q\}{vظwύ}޸=)ӹG^,v|he,D0nCx}p}`8<Pj Cpx58<Pj Cpx5`=-!1AQaq 0@P`?LMsI֤sRnTڤRoR8Grߏ\nus|I|q&$7:ԛʓsJCI&ZrTO7*M뀶nZxeu#jMε&Z|.eB4R!V(MX @jHHAYa `03, Y[͟DJJO3\S M%T0V=fCWI4?J]&" N>Ԋ`;C3 U/NB43 |O E4Tq"-$/w )-4.+mi8z+ e5$0wل= clc>:8S>cEˍYfcAXmid׆-e>1IQDŽjn`Nf }wrkUNKg]ҒHn7z%R4?$"b_W.CS eZPA@eoyFf +E8j2Pq o7t@p~s(`\g~%bqne<( !f֞{&6U1/pAx.{<+ T]b܇AB&"0$F#@ W?*n@ja,Rs(w^ Gjc .K4 ./H =Ba,\g)H6St ,"@1"͆u gݜP@81!Trj״G =(%B7  $+~[(1F$AbZ4#"DIQEF1bH:`qd ٲEi&/ө¦ 6gMdͨ% Pn+\)@$Uy2z$_-&$Y62d[[gw+PR#s|HXH}m.C_g1©+pj)Q7nqP\3m`ft]mB/4)n9ARάE/o_NLWT?v`3V+HjF\P6J^âCM&n\+li!U4ҧ*a#.zs8 Ay[J]9PP !pkյO>Kx)#hS_9I=9t6$Pz)mgo=x9A3VēQ[Ynʥ#ңMQD)@U+]eB zwlA± uV*)H%m[IQh(\UzY:x([&hSd[)5o~!Y"XxLN7?h'3Iķ1L6񎍬Z0UriK^%7 kRa4J/c?YAnNL;RDqƤn0> t~/%5'LSVQtbi>8ZNKY[<5jnk-TYI4x^1YTpD=6s$ sYy)"?FN(L"TK>95ѝV7q$q-ύ,J[ WLFSᩚ>&ӬCaI:>.EX 5FbkwO&1s1_ \|MzN륫0su+@&%>%" Q4mP94H<*(xHR\yA)sUﹴsX9|D+:S3PԱ1eϬ `:YPWIv+\7oȻoVp7.)%Fڶ|Bmٌ8t4|1}F6|LTb.ڼ =jTzP&'IZ|ר `B^ (O@c>iwҍ3o΍Ad5Db&oZ<mLI |A6snNLJ'_v&BZ7_x.E_e/WZXŇ'0%vzi/_4 C,~1pZ&.D g<$:ůL-1n8'P̗ 2Ɨ`­㧓RᘉřVԷp[qGR. ooxm3w6??QJr>cYQJ_'i pV[9{Mt0UI|@sԉA+yqRm"- kن^Saӝܻn]g.3ۗp˸xL<&xr3&c[p9OfZ2 qL!(p|sHN sW T Z@ݎ @ITi޷kmWL% CS@s:ѥ!yk2ZǑtk@- *l.&f 7^ᅳce'滼{]O0x51`ǽޛ/FЬvJHW?/=6lٳf͛6lٳf͛6lٳf͛6lٳ}tQB |Qtr| ^P8 -c0\ Duߎz}f:(kԄZt(6Sy#{K{7]{ޕ]]]V5÷kPJ J(b)b%dD*8l/&",bqMO?(* =7d,RIRp)(S!HxXPooPr[cOJ~ĪXBH\yk,`x6 FL0nFF"F.0ưHbj*Ƣ2^ oxi(Qn 5k@K3GE^j%To՗Mrà S3ff{ר,>cdLr$Mk1Z"E=Q9었mp 9cmJiU:ND>RN*>Lm,VnFfvl "QK=1LsGvx5?|}kӗ=S쐵h"9,盰B(n+v.OJ2h] D!ܬoW >:ȑvO4p"@9ęOS҄I)IQ=>e),p'z_ fHOid4R!,,ϟ>nUI-r G\) J60xf<DDU "MJ!.QL#3C+FEY&-ƛ.+xZP7v٥ ,G3޽GS#cDMlOϟ>|FOmTG`ZveoE$zmM ]ʦt\i[<&e!ƍ6^isUUMӌl}nVN x=dmܔ $-K<+/?>,ORWr [Z"(05}tKP'U(q&؊xyFIj4dcAO7޲9>Gᐥ4V-)Z 9}>ᆬK_]F@TKO(\4H*T%MHO]$Qرϟ>x/kSAQo+H'o ^vD'W~=BNgi v;#pcD8[H""{ T"KF@ ~]0 $[Ao*J`6bd3{\97WugzT䅁VJ5Տ>|N5Nn1-oD_IW0B^&HF?/ۆ:i}3ZI$^5DiRԪ*$b44iS8TI6oUhI5B S)B@ #)y@J *@+[R%إf(́J(aH$7%P]sܮ"% J E g6!8 R`}Ǣ%G0, jY)E&gĺǐWH 4I~I:ou`} bk>+6ZoWc'syʀ]2fԄat)) w/RaҥR7KKk9mJWK[ݾx7Gz< rـA`, FkݗE KS y"I Z"*EFu c aJU8N][3 6|hdApDJRiVK5"`TAw(axmnΑnM`ýH&NTa b…D T 6Se@d #, Q*xP%[ƑMN3\/W^p_z5~k \/W^!o3wĠ}~U7̊GA 0` 0` 0` 0@h?$_lA< :Vb_t2gD*M m/dC{&dñÃҡǣ endstream endobj 27 0 obj <> stream xm 1}-&& e 5\#,3÷I#)Ȋwnig\^Ю+NF*FXm n b_fM%s=Sw`V & endstream endobj 28 0 obj 132 endobj 29 0 obj <> stream JFIFCC@J"   >R U?h̬:NQm <*wZ}Iu hEB1`9q^vTRmgWMmur|He+V8tĄZ4%ZeNXm#'DIfQ_bH`3a$áwƑzHrafaU|6FA|MY_mWw}\U gT+-U=vR=Kf=LU:W9wAԦ=~*3ħs~~evп~ηP5LW#<tW21`I]IJ}tc\kPi4,N+;à%Gz| )B`Əg}ge|{socjWGLGnX^{b]803~֣xl/H.@`px_?.^ K?Es]ªmskow!]yٶ]BBl4U9,u:f/O\Mf:c8<`N럗/ѥG.:W4Ufq͝oũ*Xϡ(_qqdҍ{t!p:ӫtiucK*E:O81}d>z;oCH.nM_6tKξ21ij~^Zxz$/ j>`8{pU= = = = 6Pէہ c!0gqҮ橘-bXbժj-bX}qoZٹo }+Pty`w mD2s$gF}=ԙ)@q`Q[Ӣܡ3>> &ŕŜThd v߉H[QcJV/6 O;PlHWɩ;Y%ۅUJh(\PPft""IfmNu*>̎Edڼ"DXzj[ɊHHTaܗ Ԍ6j3IDs>=8Yha20$8bt, &3}cHFxlO4aW fb`XG.3ٛ>J|`@7-Y2lÙ FJ[|LʉW2vkx߄co@J3*PMV(aׁf(aZ MQ/BJ9{7ybm#30c9!dž!μ'`@bganm/vЇ&YBIHf#AceH7p > $ߔ3#;G).LIY{e_PQ\NYV2Ay!\M;3ݷa-d8oKC(r'Ƴ\VU६c䃫$Uc,,r`YH]]!q-q-q-q-q-q-q-q-q-q-q-q-q-q-q-q-q-q-q-q-q-q-q-q-q-q-q-loI };@I-7! "ckW\ɸާkb7 8c]cRoetOLDܔp@ 2 % ضb;=Y`v2t YfX qh,吾}*8oQoetOMtmr b!!!"#!t4F_.Zn>V#\'(jTovK~TEip&/(7Ƶ.0k*,rY䳵gk%K;\v,rY䳵gk%K;\v,rY䳵gk%K;\v,rY䳵ucLJ YjEs0T.Go.QsU-G>鮟qLi!)i ï1(]GX=[+F>.-~͛Q}EC;||ڕP|/n5d-! 0[om=oX@z6|VB[7{}(Q#y0h1,5~Dsoxs"&" Wd,ey!K4b'T}˔YNNG_55*6Uʗn3Z6w;:&S^d{ZdjÚk2r\o@ǯm6wSڦ&U6=5÷VG)7.?=7K!3z2QSUŘpN-KY~^7y,޷@6K>~OEW,uCn' !/џaYEec _-mD7(Ծ%׬4ljٵ[#/.]W*!Tݿ n9ᘑ;i쫻( 9$L3Q4DMd4W 0J䫮C 7ZYۓ`Ǯ-xVWv zE~?4 H9ib>8Mm.يļv;]1#)ɂ!QV]zLn<&Apҧ%~zrV p8ds|;-V&ixt d6+~Ǣ0'@g_-DAw{z#*;?gZAmJ8-ejL _D{dH¹f+5u, Խ* d!SǺF|"cs l9(Ц^Xeii/y\4; *\5VB7CI1Ι:"Xn7?=7n=l[s!P .!sbK}Y8u!2Ż(+4f7!c܃fF5"}$Gqa[,;*}CrSFl|k+9WTg ,3nQTNn5ImoHzo=E2 B^Y@FR_jaϗ&xDy1XX@[k>CgJFH#gCz~YfưߏnmY$<00=!ϙ%T̀.B¼^O|z3?EΛ^C-?اXG>i`l7wXEt]m~Ucگ"whjM|,ɵ3Xs:(!.6-^* \xޮddmP :߉og)Uz(OM}-okq5nͫ]}هXG>m^?=780M2CR(O( Y'َIHz" 0Dَǂx~un?Mׇ7^x~un?Mׇ7R?܍B;%!X|Jqbw E4"k xTȁ) 0C^g2ȑjRCܽ{9]pG#"Gե*⡬Qu,C鏨xH7I}wJ҆z[;.Ѿmho&]-~;@ǖs:O^T]xj Qu.5E׆^T]xj Qu.5ELzNavo&̲>`v2p{ty6^.^P)r::::::::e A" M*3/p p p p p p p p @(N(^  !1AQaq "$2Pt#03456CTbdu@BDScpR`er?Kqk`wͫR~]h]KJ+w}3+Lq+ }.!Ci4=Vl@(6?:exTvPAm շzer- X"2c)g53(ّJ)-;mhIQtBy(p30G4^8dťFJ_Ck.ui(3 s_Tag"i٫V0.3&?Cm(09~r匿89F;ӍW0QfĊb3 &月=yl3w@:_*- @r!ɶFոB" 3Agn # x V"HhghBϧ*Ykq'=V5?-2oH!+܋6qUm!;Xb"v?>g5E2}+6Ȯv Г3+ z."`HX."`HX."`HX.gdg" ԦmZL;yHr9) Mţ`렀n_s{//49r6kSWإqj·u&$v~P@(BK1'd]jR!,:ꗮR=O08B<_ xbH%!9>4B@)i7m kl:+EcfeYS0dIBz߼qkjH$Rċ[REB{$$,ef6"z )t*`zcc-c+D(#KH`YJF5Vx@^/ hw8R!")REHih**z[K̆H̺SҒ9w{3n4>7#f!HbgWkD'Ô"$H'!uT!6{%a4K/Jfʟ-}lR.v2p % e¹C t-<؃MH:|)O~-*3gI2͎HxNTɂ +摘jPxa@+>ͤ r`;g vgć ]#aa*Y7kD'Ô"$H'!uʕ-+W4,ڂ!j3P$A`"l-e9'CvVQ#& Zi 6iLQdk[m\zr$Dz5._C621˯fNˆ~֚)Mo{Hї xt-DZA<%Wt2R_J,uڞlU>t54>&;Q(-U{1ei0Z0Z5f4Y_<^!> A> ` TLM@t g!_"YȠ m2v8xª\:ՋvtB|9O"@=D|^EE46Pur`g~ 6 Vue-7TYC@NG;$fM 2R[V]]S䈐Q x1FБ-#L+k|$Cw (sDLRMŘAe'`dZaVeaQ P1i}!] 7$tAHpH#l,TaLaVR7)JP3A+IyѲ.u !ښ=`L ”&ȡBu,BBE(c"E*ƑJkSXRiFFZ‘e)e)e)kcrgT% mjo`g~!,],5,-HII;h 2esc֭f*>O Lz&ݚ4 wX;H ^+"-C*:ʙdʰecBt&7ra *sEV+*X= 0s;8f{fPpAGS0P}nt~I7Ԟ=N$#ī͢"_ a$tˉ K!*#FM>!#3"NoXYOo'5.Bho tŽ=> O#CE:j7S+Phi Zw 1+G#A]81`6$R: ;hjsɝU8m!Jݎ{#=%\}3p%+e(*W tE4Aްq~BxPM/*.V|D|xB8:R;ؑx;̥.uFA.*[4ى C'*]ȅAMGsh;QגZb'^I'(xoj/ˎx8U۴Y ԑ~vG7?(AzkwuѱY#yuHiz y/j}޲!wWQh, sNZqrg%rRUO|i/1zF=z{G73}ٱ3~5>p}7S^tטQc^tƽF=),Hy/?W DoJrIo5z4zHPqafG:ss}N (Ѣ9m=~7#kAkAkAkAkAkAk/  z}Kdv-< [ ?fBt!ٍ;|*0wGy'=1]y97mflNjd&L8(Ωv[vA*w#ts=h wֿ4_Ωáq ] ]T.(B}CW%51o%w% is-95SSJzvVKf n<4~1ڶ|%YMD-|Ce)v! 4&PGi}YBqOFżE"(tMdLƚeEƵkjҚ0w+BrtpUP'iNitMU[DS7Vխh}QZc:Sg 8h]UN-/oF_3]Q4 )^*4.޴#a0tkc/;&hL]GJ*h]YH6'uU@'+ \L]ej6qPv<7yDj !S $Aо{ĒJyUBZc+BNJz t6~W~ ֆGX hCc5ׇ30zz֤MWs#%G4T@ #m ?!fwQWOHFP ?!U()YHhh0I]Zo%"ϋ^r]UTZJԜJFlAa37T1"Ð9}g<AC P(E&#e]KDDXfAE*$%1F7 )WHTR] Vi?=w$~h^R7XQ~Ϝ/eN F YB)#i=w1C;u=,YS߁'߈5}5t 54#mg#aC ߈Ρg8⁓zLQgoJ*N6toh?iƊ ;%;ʮ1cT JtFF.-/wiΩ<7gTsC/E'߈5*HQE)h(t(ZaȰaƫ(/,nׁ%-KP_yx@N5$Ihe7mY"O,=4I^tg%g[d'$5XNnW79J8Fj~%i،<.5hb$xiXn}q|]xT^qs "xg"z<1+3Nso4놆; ,Ќ'=UqG"io5=N]o5=/|rWVgloG4hYAz&vY!t:Z]u]*3DJ.Da1ޔY.{Ҝjg !<9FOdE;ZM/k{=mUQpؕij~%5ώllwӢwy=sx'뷧+KBy>d!Yub( 5A2 A]v  !$1"#46A%02357@Qav BCDS`dqt&PRTbcsEUruwe'Fp?k]]rye1w&1=u|>6uAgfRA{t% 44z\I1w.B_uWh" )m X’p4Zi@ r&oF+%% %UBR>G  %sd3#ɝTl4Mpq߲Pu!f{enHLHDl"I(m[ҙXyH|zN*ƞB$uaaua}0i0vs/a +FikG]$oUݷ★ 2՛zZ=fjԺ7+!p`x,+sYe8 f,tM\ jΆsZj'Ӄ*nq՗p)!ƿ rdl'H5GJz Q3*"r3f#jb1fž|˯36Q]^d8r@k5ᰰӷc4!o[Ҧ !D H\^s2`6kT`􃱾-ɁES!ܮy1NʮJ#V`7g| U,ud 1]$ ߇ ۟.K]@2"z:ĄƔT*iYDL=zD$!Ŏ7/Tk.a-Ѓr6Hkp,/Gb SJ-&xH2U#q`Xíᅉ{uI/4p#葬v)1UJ vlT &䑈|`k ZSё13DKU\+7U~7WUOiKAtjq&%cHd KeUR_kEU\ƕQ"mTD.H~*ZI;I2-pM&mĭ’yַԱ)s"ƐU0AKDzp[uՇ~mc4곝e-X">K"QtrJBg|6"btyP`/qQ]LjZQy3%U-V}c+NLԨgyjRT".G̏'^ïjt,ro>ŻR! iKAъ'܋M9SDDL\c' ADDL m3uh F1\)E1,pۗ%D[,95I)iH.#"Ŝm"Ѽ;z#JaYc1ZÞrERWrrM{g f2҄ƢPrQ4ҫPqqq.<׉C 2SCZξ%.*Z )ݬ ɡl{y1S4xD0!KRX0N T>h2p2n@am+.JokCl4)=\޲?qCPI0T̨]Ž6ڦSb26ˮ(sjL D>^kf&r# X\vnn!M|]ˆCpV$| 7o=Vܬ)4g8hQώ>c/]qٕ8Fv݌j*RTBr|U+]iۯ^/FM&`XN{!|Bl iaw>ɺy[%{~C+DA#^=5aG|5Hғf6c 14芼.8ZR<QZ[mȳ5 4F)dPE/3m]\%(~7iO%Q ݪE\s]ãHs3oLCHbDT܎XDO=t'qđHSRQY@W*F `ϣeJ^_cOЧ[VV]pT i'^e;v3;H|HMtח_iM"HN/yQIJtPu+~VJI#tzQ hN~nGdآR\tic 񍷥ە+}2Z W\*a l0Ne7Lx.[UǍb#XiAfѥiYN\T\W9"W0b;T6̴4H*iDD[:cyLP}-C*ȜҖ,ЊFY!Q<1χh{2txNiGc='.T8CB%#,Y|zheNZ\y{>֌0 CLcMSO6z'"aWXofGU {'U +Z7xñ/:F+6TU+Mإd*5YDuXn*:V6K+[Kg\QF6D12pGR=)D>Cʙ:1ȐW{GF.+iOÊGbK勦KޑDX2R.h TbZT糦b-sW3hܑAr{UqE%qs6jڿ#ry#WW.%Uի|.$՗^kKVjQQδdsZ .oTݴ*^ogīYf96q\/u }vWi Db 2朔znߒ;bzeCpceeF( \S gyBJqw-[EˮqWo/sE-'}XetEp]WTTLڗ+y9HNB8X`!PY-jVk (hr{X -"pq>C`PľUQU(?;'y)kv4ʥŞC$Lq=QJSM}ٛkUtIiX|e. 9GŰjԉ1F.~ǯu|m:b39v{x_k,HdC+EDXhKœ쌴u[`62qmRI1*UT1 /:s#AE qXbeM"mI;"V/gÞ'bƭdcrdhцE;C QUҞ " c!..fa!m;\v#߾@G/٥3 \ YseʇcVù2[B*$J&^֐zҌOƶQf\g>KO(a "hY󊨲՗ q#:#V$OdܱePA"5ʍQQܘ k㔅rQ!A|Q:Exriz M%7C>+x]b)G4T^,s.TݥrIHXUFuuJ"п:I4YosG]i fH[éG$KUd-X=R=t}5tciF%amTi\l.AOհЭ8~[:Lt2옲ib-p;iF}\pK2$yLgLX/]cX󫿐:zkuwOUbή@걬_y=V5:Ʊ}W tX/]cX󫿐:zkuwOUbή@걬_y=V5:Ʊ}W tX/]cX󫿐:zkuwOUbή@걬_y=V5:Ʊ}W tX/]cX󫿐:m3[槩F!|cprK.6qt1P#2b^d&j"~&TM=uUb]6-ѶQDd 'aܝnpʆ [% 6eiءfH9>FgiTA$5qD\6KI39A&nXΫdd\?B`;5DhwŜW@Fÿ2A4isF\W1CU4DhK4=#;}Y+2H\Ìz;/Ddr4\|jFZ}'*CE69}&/asZI:\`CR"Ķ;8`V;oB4[$;(l8ї:x˧"~&!?:j Y@) rb7O-h b'{1,|kL$\(v_q鿳u0dUBpS@cۆ !ѷ0>5l 2UqFLf;cTL0l2,iJ2\EWUi( F7._pŘ×^<`1t[rvao^L@ ~#yY >]6Λ8]čF?e˾ƗVH;/-. Lz3q|`JM{@k*`b6;4fPߔ3gbRyL|tTd @{goL9FL0=7|?YOaDMqihخ-#@} F ;^!9;ϋҋ39h*|']tȗ2"4.I.,1=+G(&;:T9I}C vB'q3鏼K?r;1JtƏK<V>T+RۏAlC4_-3 wmѐT[4DKXoKx\]©YІx[7MDP^W4hgMtI?=˰#g˴$Zǩ0FHF)7?f!)Fz״{Sh=+m\CE@qwD'! Fq@DU)iͧ&63< Oo?c>;ώ&63< Oo?c>;ώ&63< Oo?c>;ώ&63< Oo?c>;ώ&63< Oo?c>;ώ&63< Oo?c>;ώ&63< Oo?c>;r [q&sg&I%7^|A|`dhqr.iO.H7ܙ[W+#&).@Zc.6.ѶۭV7] FNKs/qKǟ(wM"4ύCVtٱؖLrmu+y"HNW*ה&eؚ;D~('\ht)5rCT 5;SYk ^ud snTT \F 5 M0 )t⊺w&we<*:%t*UETSU>r~8GA)诼{kҸQ(W"ya#sb Issћ.蛞S bLc-DdQW:Ҁ$mnI$R_V֝~~[YtZE]|hَ ULkzi.h I"|n{2w8,,Fm5B6'i9'WoL+M;/ ;E{lOU7pKwx$5M[ &ȂRZh͹weuL jo\CS.t ݢ L.(ҶuҫybR2*SRo5&"wc*\ܱ$.\ԭ&N.(7A3HK^bIXۮ J l{2q*>mʪ?UJ%>v_ku<81QIKGQrMlE5JѶ=bv$:qJջ tNZѓ 1vMx[8،Kl AD&mYQH4*7,ԭ \Ԩqm'6STNx&+Ѵ#nF4낒N 1 [3PTu;r3ъ<]$Fccm&ew;ذWk,2Z4*V,jH,}?j6fh"nE0 /TW2$tI7,ADԸyq7$0Q,{͇0\a]-T*J1|?zip.3CD⡥,#AJw[RgIׂ,c|jDQE6)-sŀDa+r1PAgĞ,"IP6-l#m]&콣Nט4l;3q5"n;XZSu[pSNe\GDIBJX>&n*650WJBD}I]Y| L}*45EF 3*jnNFLol v\8Y\ГʐkȊS׬Oj1ЯBǎoMY@ >2-/n hUs,)Kv6|g.8˂t}"S!8T`;! Cd #2`,HnR6,mkxOAa}s0qSSKҷQOgJkzmh|% ĎT¤CC^s3EKT}!3 ;l4#m|L&uyX;ouˎnPw$ F)}~hsm@2+E׾ 9)EVטVvcG}ދtRA;JVsKVݔ؛)l:ReRBA\ Oո6ق6Xϰg|ʔ{9:q;`ZgҚwyɄbiXhL`Q1z@ @P]Tû>vյQ Υ_6(Ҝa҈vy+ 2=K߯y^[_pb7(y.K]zYqF]c&=**˧wJj5@谗:'.H/ZL/216|[%nN@~ETEZj^KTM Jv 5U肞?64$Um[ ­"s(~ S߬[87:K(>Ty ͅ쇦k;Ufm6+J4^+&ڗGqVsF5έ̜Y4{(Zzk;RثRY Fiٙ`p&!$`슲d?/UffSS4JscQQII23'lTWUvu%\[,yI3'8^lNNHC{UuD8`s c&M}.,XhZZK]~N.vf#Q&9/NI8*{)7n:wWU4" 9k"A@>8Jg½t^䝐G6ڋ8/IJMq2Fl̨UH>*ipKyjlfƵΌ87u\iQjbj+RiqAx!F X[Qq#Ɋ}˿,Zz ME !Bf)w/R>5(-94kdxf2jJw3,2\ ~S#vf켭K(Wn0zd)W,QQlԊH.X_M_$k) j4{.sq$D,V4 qBV>b*[PDrJy@u#GEK'68Ń$7{}ƖԱU싑͉1UsF-QshIb'ɹ& .pp˩GMy%&PxhHGLIk͚ 0>RHl7rHy;:꯯M`?p\V^ŝY&=Q0yϷaJc}'rT%RB2oîʸ.7nJ?o0в (YuN(tݚtcMkJO1%Ǟ+ȼ-qQ0ngV e^)Ë/ҩ辳[Lw7uJK,&Z\z#J]+Jl vڢP}nVvq"LYDZS$L B -"20=3퇍\Lȓ Oj6?Vغ_tpXN@>NqR#tzU šviI%U+QQ,UĹS[~|5ZU0EE&_YrB8HTO3:A)FȵøKg37CRgFqMXbd _ZmK="\.H}Za(ir[]ВC,MIѥHQB)ӊx,b*$Gg a%?oKRS$.N.lI㓏)Bdm%Gmj=qBa2UbNCrP2mƑH^"*Qy"i6>]$ E\y.:#BbՕ}7X&IF$I"6)W(;DDD9=aSKmU]ulQJ}`i&Z]0 eHaz)nuyuv//akyD2QRkB\%[,-mJE?SUl<81b:L' DU`SAbՎ){SȞ_)O3i)7KZ=S)5^~ph=L),}+#^ZyMɀ㌛]0Ds+9Ws:emv ʋ2T^\ciT7=-K74WD;nUr=$p;"ԔeM!2 0|? =#,K-ES!Wk^E[mLuē2dcYCo3U B׶Z+ƶ^W-&U+ܸo<BBtEzhkv=&F39)N>ܥ D-3ԗ6EbMF7SQ`}(5BkdvȻrd $ dbg/{K\ Dɽo'i==c`\1} nXp DUF2V5Q21+KDm5˄*;]`ɱ;^4̓Pg]>%7dc{򁢵)P7ۖ.'Dqu"m WL/$>0]KT5zC[!/8`oY)EחxDѼ ~wpW{B0y kM%/;9C̃J1F1z:D@J ެ%Y8R:-[ZyvCw؝23[:ݛz"Tb$Ys;*?Ozu{bus [$hLf%xA(R _@Yyk h.JfiM|ղ/dvX悺n.x &$FXfPQbp2l2b'L+#?zYoYVxQa&"P@fz3Ag`ic0uID-Be8V,39eED(NXq(T;c I[}2fuJg1xҚvF\vZ6e_"\C\WUG,4%"mԔ,-9 412pN#oo##tv(0)U>1'hoZTp$iYf:3 N;DUt:STIýRi`yewnGTJR9S"0bHwה;J1<+eNO.&H!0Lr5_QoJu+dn#n0귙{/cy梖^cǧ|:c=@nkqpQ3[L7ݒ[{r]y":}q Ȅj>=\Հ'} g]3o֙+a)npǾ: FfptjcZT=O{F''ƲDܦ'V1Zчl"%Jf=p Q]1+.h5 ~ ?ж}])O P -UVsY]pz`ccZ1ڼ * ݌$ARt{K4aO~j݊!4jhuݹeiQR'Yi[Ua`!s G(2bNáu{SȞ_)O\<ڲC-[]"wF8~{ 2ӯ;U_8<_F]Qm۲`@,oþ^A )"-)JV ܂.-bdڝiC#NvEITTJL.+vB؆ "?y/kNS-k| o b" G2St1gA3eƴ"CJX,-y+gF4ZݒppVՙ(#.´W綹l'%=L6o,p$SYqQ7Ȉ)Vr֣CIR![8#Z0LĦc'dw&Rz+tZbUQfUS8VՉӵ[k*ُ7zu?ţN9:yi"[D[GOLAl(vSŒ|L:OP-KnˬSTcۍk6dd^^’bh!Y(xH)At%]t+jGD[wc E/%"XFH<1zĎ<2yNїǥipjj2&zje|b>baLeL̦C,)ѵ5Fi^t#wݷ8<8l({(|֪Ӆ_bQlm_^U7${mK%#d+$ RT(eEHu-;E ob r;2}ONn̪lyvg"iĄC&" OѶqŲh7*޲ CCGyJtڑ7#u6Rw\p{ aւKg_BX[J1WgZ/:LTɗQ ŝ< #MJ8He+k$}k.@a$_Y+P?=S4R@ݑlFwL_pzȡ&֬GWѫ nWH[$IYn KyhAA/vD9= =`v ZVn].2q| JŖ$B&)ʞwI{T^BAMŸjEscR{~`=$<ЅL0E񞕖-6% ֎K*G=l ⰷ]#jQ9JUIk&xíȺj6;h xBB :YUE*Jyc`]ٴ<$p6ۛ߀< ]\QQa"a6qDh_m-i,wm jF,;!Aɩ^! c:!\XԌAJS5rTcM4s?ǼjL"cRf&H+bryMp-nXgbJ^ &.|[pKdC䆨BYtjG,9ԆZOބ}eɸȭŶ"ѷȘ NuDDKh+Y0SLG,9'FNUSB)S5TKvւH@6Ò|p/XƱVd-QjhM] :}~zamO~;K$' _]4z/ՠ5ŷsHo c}7=1.RXj 4Qi^wĝ^ߋ8k]mӃ r*q1P'BR L҈,?&jy?g$u~1'd:谥c["̒HQCW 1Qy):{/̄~;i rgU詤5+i9#=..>ˬ‘e?-Cq)VAF؛bx[`$<+`B.Q gCXR=,`\p&ʊA^oYuɑ⢱ {S'Ju`y2fEQ0UM(oB8|/J8Ǜl@Ő9$7Sϒ) sVAT/2;M,n8#pe3Ba xG^r~Y r)IP ZAy2bټg o[ns!7m8sgPG*k7DCnwJ@G.,ID 5ig=/emFW71 m=__sdpEL+_Ǧ6t:&ֱ슱e AKU&IOMm^' Ƿ]qXFײ-ΐ_ƉE KmN4NY:T=O{F'm͟;+NZm=(p<F ɛG7Y^zMu7C?q Mf1۸yï2o A1co[*> &dAXģ *.oUR+7x-/M^`!jU(|eSec20D|Ds}bV^O>zK>=bhrϛأrN{>ZʮPKYNb&<ޤsDD{tCLp姅6Ñ| # +3"fݩTʝxt뻺]uu;1͘V~,Ե *"UAd%xҕU:x~I3R)B3<.*QkIJP"%%^tJESmW}k0-i.|KdJ^ƿwrΆ=*;|xiypwx#a~Owx#a~Owx#a~Owx#a~Owx#a~Owx#a~Owx#a~Owx#a~Owx#a~Owx#a~OwZwl.-r$KPͻ:҇a&c<̕8o#1!2^mڮt۔mi]ч'T<etMU{'߿Z:–ƞ">مUa闏<~N1:uY:63Ti ZЍ-p$P,1NdvUWj)T F{gKjyXSsf# ![B쏓PYcfj6I4lؒR֩Rw\[L5/NXgv[Ń(`v;k,Z; {04{8 `1[Pe3y.>䎸-X(hYO 7gs#~n(3 $ȻIJ p隴&JXQ@зjFx|8 GD 5"M*z9 /գgR`I}JGޅ #&^Hkb@krsP`Lkf0`tT\:>`n;͊+vU;^咼t3SSlۗU;=۬&}u>6D,W0jErW8hpGKyTzCLjcuLg 0ƽ s *rEɇ0rN:g?"5]0h@9&SLĀ1Om@-4c[D.n &v`]0_r/8 )EA9\<;In~E1:f.+w3 !ST/гT F3k]pЇM<4dtOk"d)E3V"5\ S,)Ў|ݚ a $Y,ġk]ieVV^݇حͰătnc.Qs}8[/QPOl7Wb;],[7+L Ua SPY뿛rR ܝn#܊ -_aLi&Kc &@5p]9inpJ *i%RXjу;M',m+T!]|5/)nTQq\fPqN-!(@OI 0j ){= O~s)tO=Ξ 9?:z@'OHSߜ{= O~s)tO=Ξ 9?:z@'OHSߜ Y}39"07, HVj&AXeN~a_$y.aYYPefb[.N27gE)n.8"!B8!,xfdlH6A&su%n>LU?e7cp4؈i\.8>`Vً̏&W„!1bqN眤iIau ӬJƘ][%ӲJːE:ZBbyZ:\a_+`&Q2tǠ9%G6BnFZZ`. Yf ގ0aG AbiR33xTL?YdZJTpǂ\IybLjJH DaP\/ԕ|/Be"WP_YZ"wȹ!QrAƟUO3ano93EzecS $ժl0/D[$Iecھ*{L2 q^20]'XDۓ!6vܜ 0,toK{|ުߘvW aUE=n3Y[8X$eɤ؇$}_6ccJQ̃QAs:CoQ xzFAOF<oQήʭW  <(?)1Lti 1i@S7^oń$%EyeUt"sv,"DQ)=既z=d!YtZR>70 ʡ3Y+ikq]Pnp\&)ڳ@p 5T11,(1B(n2V^ḫP2Pn77Sq#yu׳ ;B `K0/Pkl5p;J|!jU*J]nkivۢƋWk4W7ߔ7ownI\].ȖVZ>!Ax{{'K# 7%kXix1m&d^zE~2_B $zZqo6<{fw 0Ǿ sizF=Y8vZph1#ۗ˾KEл8Tvc#;<*-'{zs8Vs-sXsXt=xJTm:r,4ɯ9Qpq,`eJUjÛc~-Ûc~-Ûc~-Ûc~-Ûc~-Ûc~-hJ٪gź B 5Kӣ2}:a *}\>s,16h8TQqֆc-ߑ5@?kE-7NG"SLaD4W|b~O8*Eq9*yWk/eE^gݏssVf6pWϫM&k| ?9m_).jQDƷŽ":J 9iM44iH ۓSIq*gV̧Nq=,^ g5<okqo۱.~8ܓc8M37LƯ[kI%TukQ_ȈN_*/KfgFbdhؑYA?ј,ӿ4dQGb4Ĺj?Ѱð.h*WE xYwH`8.Z+tW[ֲo=Fs~V?26@_8Jnv gXFc# ))׆JeڐF.[ypGe(F]c'RR)UrU  '4 ӎV7o܌ q1n&/ȅP!wƳ>ANJ$%-bDjM:Yx=Jh)hآ~s&&?5ͥbJ^ӹc#jD0ZXg0&BmcR&ct\짃q[SOз\. i̷JMb"Gt:H 2NZWܪ03y,P}{WOS9 ^s߄ʽ=L,?zz~X*Zդ4NUAau$4Zrﻳ})څ+w\,e_Cʉ/M.)Bq" mź#3F} E-;47M;QG]>nt2X zl9ow}tcJUl粹,2aupS-Uq[d6o%FarHyQfLSrY6[Adw Lv:nS&ZR~ޭrNvᔌIqc\`߰q2E*\q]%ʧ31[.l@c.7{cI˲"r+b5Tt,zoiF/B'#f-#B"+JpaMZTl~1 3FE^67$wǑw%R#%*8yʰ(ꄳʪtz\|z5Ds8A cW["b߫_~n6<4-PR'L0:k"P29ArrŲBӶWgNRL!7=O&àUrO$AM^J_7[Uu9Et-Œn9(`+֤\R4$`x= :M0•1ݭj?\Xd #D:#PT <5H-<l?O}<l?O}<l?O}<l?O}؇Hp\ }+ʼ5w̹/"ܧioX}ω?>v?baWKb1F_8 y$a$vvz1̴erg|0?@@>NFY-aIs.N07>^EƝ.X"^V8~h`*J+Q?[eY/UDǐ.UW秂i൹$ox-n|  [Fmi൹$ox-n|  [FmK ~]w?Ÿ-!1AQaq 0@`P?!5&{׷5:gL{sYE[Q;Rx;{pE]@0;@\q,ҤSJ*zhdw[i'eYU.ٺ&~ $U )=GqA  O07͙ 0 U l2-8ębʴa<tVr?m;Cpg،U-,If:YbY:Tn`Z3-al#R;:NJaf*k)ԘU>J+Y@cZU:L_D̤fTԿL8@A8v٭Em\H;Ҩ&ܘi73,:Am-ql((cZ\eql(#mb:jbg}ρhɄ'%\GpF߉'&3`qđ+tN, !1.dt]X!jJ1 ᱦ,/h0va]TޛPkx9G N_TJJvoUZ/9FZO|Qb$9D?L"d&T"X)#͠h30uQ5D%bA`_ MS Ͽ /|cc ʊG0ޤ'(JFNeу:bcOk4:"Q\Cb4yxaM"s {גn7ӧZvڵ˷n-':m $40hp˯`Vk .u'Ffp3T&,|[fΠcG<)OEkHle9$ABQEOpLh\ljl~ g였a%c4< `cChvI<,AgG i( 6Yaֺx"XѶMm 3] 29K׋Ј\n_PP4oThi WjB!T͛Szf*s[21߿~رbǿ~ر߿~ԘTielF[@d*Ke_Of3olJu?NW'&2'?0B BFÖd Q]Sh_&) nL},ziEuߚlzxuwZI_Ƈ˽|pKWvUb:rn::v@1"jbHqR@wwߟ6kKsDAy8\,`LҴ|"JS^*WI,>\seY t=$Y/R8ϬaL VSf8hIBщmƨwqål*8c~LNNG_ Hav_#RGX q}Vchþ +SR<{T;g?Wȑ"D$H"D$H"D$H"L`mqv(QsF9L>3ӪME5tjoEpQd5,,qN@*'w!%^Ϙڰue0hY,ɷKwRcNq hݚܵKM}vr x&oR^# YL8L'JkvLR`#[q@աe\%A=Kv)n6%ut'tDqPS\!A j ^VD>GԻ؃K@)8vn(V%r(Ž{j{9RBx2tdХ 7#5cK+9&p6myPZ:okJ7k: Sɘ5t-2/hd d3'W()s<`6C8 ԓ-&js셞V 7.Rwua#Nv1Ʉ-1y9S2TG16v5ZvC]P`&`#4șNtSS74cG"o^*RX6PC2='NK`;w9!g8Zl/vmjhr[%fm=uBgH"ם 1+ 8>xߓH)iVYvLoL Xk啯-:9wu'q Q,v,=3Hԧ5&g]pԆ+td&v[#Cu& 6oz|8Mwl`<24% S0:,.tܚHJ6Oz XEg/8QF IkmmPg5WgdP|=:T ",nb"3eN WqFV(W$0JrH Zi.vd~FOs='Gdee׾1ƍL$Rp3r4y݄tL 4㶷") 2} hL)tYE H Xfs^/h0OzHM/ʗb'91=؄-طb4q [ x f.* +O8lK9}u$|Yl<A-1pk +#bwt5Jt&eR˔ICz+yC(?*> #-H6PjR 8g.Sɭ`?R5ڂM'w"tC GcŪ&E9w`Ch EL aZ?5P|h!<-sg_З% IJ4frnuGB!|Ѐцq`)L2dɓ&O''S뗃;XzOiА< Y)CzA8$e~yB&T` CruLtOy;FE[tb[6Yz1=ꪐZLQr'xL!%xm<PyQ;XmL6N5КGiO41  #4X6>iTF(J. tl= %?owFz qoi<9izoD~\%8wzo3\fd1kN53c :+kpvF3k8F=辊k,Mf|ʮ6vc/iӦ}ryC!^p05.@6WOf7V6?h`S" " 76Q)Z)k94eLF~jOw&\$϶O3;7?pIz'INF(q֒4z}ÃGL[c i.m̌j >+Ӱ[& cz8˟?vSLom[Q7ϛ,0^u D-NRT=Vѵ\x (> ʅ=hwkB7y퟼tqA$*,_0c.Vv^s諨12 ȱ'LEosit͠ĢSH?!%=Ϻ?'Ƿ}sz¢޲[][Lđ7!41Hqz:9wbCt1am\/ݧ/_ǻTF) ߄_C"G_0ݧ/_ǻnųԬ4FF# /*xׄ󓓀Mq(uzlgL8H&'U4`v6ZKVQIL0Fr VWLia@B}?.wu./Gc(`ƣ-rՌ"1DO$g*8 W9Gd~$ƉhC{( $;=_z2w;y_sëL~'G!xns:7'x>'-8`D>>9z=nT o_d wps\'?5?8oUw?xrx7|UX[F޿i4(8:j޴,4<{ypj1i_mߐo:x L!G:0.@8?UkkvDw nE&8\u~j6߲ϴ1X!6C նq` lgl;鶡״ sv ;Vxhy(N W>$<^izp" H\_!O'Cp^|.WųqO)4m<]X ickA;oX ` k71Mc4@u$h Su +3½p Ho)xu΅ }wr}M!vPJ禙>9z=}~!Q# Ym HbCgCOr_ތ_@ ڂ{B+hP!W.\_,_\Rg/˗>ݧ/_ǻcD́:P1WՎ_ZuDdYj,HdvTsPGPytEYj )9Nr, c!.Ǚ{_ЀE\]'Dz!ݧ/_ǽ˭8TMU{" 7>Ī9{GZAJIS>F™p*~F13eҨ/G#pwm_|-YEQ!cNPЁhp|2gg*ږ,2&["cӗކ40/7 sD*ϰ ]2|0$V舷c0̤קb. ]HF1yqLa,>GV'"ݧ/_jKC+s4VnF0TNDC *ùTdeX:3T/NyETVϑm=#/n<~H}N^>l|Y $ FC;O\vG_>9zUm&qW&}ej|4Bsy fԁ#ާɡ.M^?0{+УQC3 u=#cC\d@[9ڞ/_FXyxTpr Rp&hM[~#C,ѝIkd?:g8pk)aLs+mr†bXmC@Ç (uBu1(8,j}`1uܢHCr5azeK*OܕcĻl Ǔ>Jfz%ڄA'4`JrmEb# HҬ=EsY&GȈ&LSXj>#R[q0dh:tUlץ(#Ū减-K-KJ8 I @3ٹW(ޭ]/XWi7d$, $0Cr~8*mub} j*\߰fRXza }١*9,tDqPC\QF~Em| jyuv^B@ C,PǷ[W"DdgK=?oeȡNqqqsdJyHi׿!3=Χ>hz`H/)â0|gysĊaC k />{== 0`ԣ.@[\S,y| Cuko5ݻv۷l YO&a#g? 10'L4Q (0XCdgA ( J0 C P  "B *o}63qqqD0@Y7*0 @  蒄IB "O` mTV&k_Er+ng#U i <7QFO0~R@OE  )sJ. +r +V g`=PR@K\$@1ܐx0@  2M?M<$UIЀR +N#@^ BH$ 'G_[Þ=#3o砯@%^ s>7砺-Odԉ#I6{ϖhK*/{s$ Sث4fFN"7G$k>׿,C&_i354ȡɰؑV>:8&'M~9<\uθGs %*SP\Yw,e@B@[Kc,IDʩhUG3v!=c3T$"olbQj$P+YhM%Ӵr!D3d"LQq!$K$1 ,f(hH- :Qr%X9Y`0'&Ldj ,;pC%iHf =ap{DJtmHX7Sέ [\B9eLoC:o/88i͸:ziCO5GVHt=?`fBİߧڼAw)]@A@mf \C`Smԕ&[1D%v7 k.>z۟N`5d dI8DDh2dn=FCB@4H`Jsnf1n;an]$s3rj^h&|+Xғ ``e$a(a:Lr;UidJ呵hn"I,ĤH)|ǟɣh[u7^svwǖq'xi7\Djј&εJD8D TG W-1qӶztb1lO( 1gDט/KORQy/G}&ba%$Ӿ zcO8}-yl$뿙F]0^H0BIŤu&0%'j⯟T6,乜`rfЕTgURD)UmH6 F3[1Nq`ͯzA5cy:9YƢ%&e7޴BF'9y`PU BFvAҀJP+2@S"$%f#/#Np i0c2h FI44 <;51[#Mu{5,?c6OQ B+*' BQu(H( Vf@ i%4f2ʠ;f7O$/2\i Hյ`AFS Ie@VZX":Pb6 ُ[:cxW騔 PDaw 7'L++B_PG64#UX>Y R&@24LY&b.(Ѧ.QMEhTXeR'adsE',E#{h'1}H[uhԗ0b6G oϞ s3Qe1-]wm=qqjqLƌ6dCmR6=GcShrrh\0+O>Z%r,߇tGLi@rJ'ƀZFѓ*&!?e0$@M$&9/,ر$p)Mz5''gY}Gm*}r¡5oXwg}\1{18C͒H%G1r CHm+zN'l9[ HDVړ3\usb.dRrujD1{[rhͽCJmxn㶻pt{u{;Tޒs"Y̻B6A/vY [jRc0H(3l|÷^4B1E U ok&E4 FW̽wlH`ʞ?X=B IJ2Zv_e9omX @4=DG}|=􎿐ǶofO~='r|$?sHz}]C3Qw[gzi;C,7 ,X"T( )PB$I#) XBYŨ4ͩAPsQ[Ï$=@V gs`QnQXjqRba`!H1I[%Kò]Z `b7]'^oiWGYbD-H*$ՑCE"8L=NuY'wuE# EHT8 $dn~Pb+$|\~ ΀=B4j υDۥRrhAb0-$0`!.B 5Z4I@<(M x#eZxߟׇb:qIzه*xԜg<''Uzib<:mKϿay}-Kyj^_o,!1AQaq 0P@`p?wNtavxdggF!Du'_闦NC]xvE("x_뷯cwN89s=k/~x=Ԇ5Y~g Ϯ޴zq0eا2;L1S9w>Z2m]V0 X}.n_zm.%G;~y*3xɽ=U +$T * tK$ BTauf8.nxF@ C &T[>A;Dqd(` ma^uT6gӝ\C`a@%48ؘ+( A"&]fE75gLRPeԣ1  kъ (Qmά@oE 0J#B:)DqQ4$ͨq sc@"4 @$@lcn&"߿CP]xm66P]xm֡ɳBZn+?oN4(2Kyqֽ>?|zj!1&&ӏ/Ag;=gSzꝞTø'In?xcGaR*\ 0>:6A3$l6;Ɇ}@9l3\t9,ǮXK1c38O[7񂋂o5U1B,781 .ZH2ȭTZdU'؇ף}}7k4,Z 'N砍ψ(N܍㉶w7Ҝn?:_S^+xxZn?:n?:Sw{>zn?:]7'7N~] @3b2*¨Xq笰A|[7t"gؐ6w76ܳx(i#@TNf%bEj$Gr^HwGJb'?|џ](Jan @0"8\54 LLBQ 1wD/jG"8DyHaaSΥx Ҕ7# ͵D9LS90_S~㿏^?>Uw[S /+. 0``m\j} uz8*d92wFwE Sя b{(LJ'0APIps +BT.*{ƨڟg 6En @1 zа L;)%8v}gUp;E&̬: rs7<Zf!j/r`V# yIy* RM!T پχTꤚYbVyH q2!"iJaaCIG(Q&7+V9,ΓI)]-WpF+,@(wbSh s4;0R3"AP @;7 `7lՆh=g~iAϓZ\6 fB8 ֈ4cƗ2-m> yL ֠UM)B@8E{qI79RrbUILDc% X2 uC"fbK/ikHiןS¤f#ۍwww _ P1ē- sQGGDBW(F: ,-3R*B"tC3їRETQ v Lܯ5v6i^ l㌙X.߱z#IJcs ɹ~Z s@cr9 >KA ,94YD$*MGF P7B܉K%&KR\2$ a7rx?hR._=K!b"#""%gF"27Qޙ ~C"gWr8& mMIMdW꼿_O=5x+Mʷy3B-eeգd`\r'0\e6ѱ}^t.DU)f|L UY~<Ѵnq<L); P &D kL%B2@aze $$r LY@|~ Tfvm XyV5@܀vYm /,H b [.dO0 cr8rmYsM5 ET%"r84WU`݆ "eI C {O;qH83q*""$,xZ7p0!@8WHBSm 6F* =+BaJs#]oq[zq֩=~Bis" ?ãz|!=?{=|$%iqu" H¥*sՀ ʅG*M dRPQb찅P5@2z/% /9,@$ &e]9Mݵ = )Ө3+e]0Ey݊DS(! I dA;uy9e"Ao%8((sVJ"<Zs:=?{]|We)Q"@+߆y|]rsTv]UaA@ ǃ@I2XY1?Kg?VMG2A#፵g31Sу>^mF=}Y8.׷{qׂ yc<^~4*8hh'%ә%vsv(ӇēN+Q$tKyMk[_ dγn3`+[."PMtwX 2/ǪQ;&H Q@= #LGKEm\ AŢϞ](wܮI^&q8WL]\oq}ͭn$X[`jh8'~w9BM!J&Ɉ`+7.PίQx0\łOTj! " D2 .S[d0 WdzWzO:a>N;B<4(h=>)sH\>oo0$0COvu}{EqId@ @0=aOMZ$H`L6@WŮX! b*.XWcl?pHtjjjj+5>v¾Q23GWb=xD\t| 80f*s̕>>z|l,!1AQaq 0@`P?Xq:ႉBӟyZN^!̍b's|MZ0cxu:GL^t:iMJ˔S#KJ< %@'x'v,#u%pϾ2%uHE&4=ˋ2c٧Ɛҟ^Q[3a^ OF ~ -R{SD@BvZ,&2&gWୄ >R[FGjitO*q /EDBė& No3*Hb!k)>Q$4+jQ]Bǥʕ&wʗ5hX|wV@ymկ ҏ4M0E}ҥ%j1迠\7Rlz<ʕ pĤ ?8jC0dUc| ~ICow0z` bGN1NYwQJE9]pMw$൘#x,/OhA&dHCK'JR6+{M]kvА@C("=2pzZ4p,Q&Ib?돋A^" P5C%YɫW*CaCpF?G\5Ӓ]&kJvi jJNr!%+/](&:%YFj.dar!hKMk?iy5),CK:OPt i2 {j2:Q&>O9@2YS@|dD%,ŝʶiRO{fB !u`E{ЫWAkc}$V ܔ1*.zk|j2T2x)9;"n]@W$`RAwƸWӜoQΒ8EDjUO*Y(z¨pUi¹'I\WZIIs,y!Xm”wC:"<Kk.9{l @I2~j7;3a]B@x\q aYs&C 4AШ3ޠ_6:TAf ZA:\mPl[6:Q>4v Tt-ȥҠ CLt Pl_v3q\K]1*HIg~Z3DW]EA $a,iUz (^jNF,F H2`͝{ʥ{#!Y蛬G?,L DʁZa_)`"8LHb#'6Rº[J(QKbCY,?'3{WZ@Vq$!T*`O(׮Ym].VHf=K59XaA0X n[<*! *B|_^bEC&!4,! 0C` DlDg64Tb g4(A@>J 0F 83ʐ뮦 ^KDITd Տ6|&RRnTjI'mjMʓ~{xIO8j4**X>Y2LTH.b5%[<9[5тT袸(] `]Qv뻴R+G?22AߏlBdEކY.9u덴l l 6*U@` fc`AAK)¸w]׸(i#$Mȣ`4KCw"n6ȿV rɽ3AlG"Q&b";ɦHnJ6O#I'0$ J+B7 o7Z0tv=E ;K,J5|kDvNUH%& $7sG&"MUHhv=UJOE٨dy0 0.P~J" @Ak< AqNsO܄-4zFG'k MPr4U,sj磾|3(Ḽ* AAlp&`6* MP"ض-lcqҀ:/*8@'5IR\2QA3#o xnHP6'I'KDB~N&ӭIRnw:ɹRnw:ԛN&ӯ6aV;lT\x]eZt! '^'F*f@jH~M^Ԅg^|yv_Ç);׃>N;x{mxw<8~xϟ=x3y}yH痮~4*ebX" K/̒efW90St'(WuJrp %J32K;)/\CExS]֡cooQ=]Rx5mp |F3HA'Z3&.dсUg.Q@q T$BJZy$@9=*E&i+߽G5@81J+@AD)(ڙ^2M0KD`yT$-(g]5KQg-$7e!(Gt20$G&S3D Ѝjnj l R7#fI /(Ke>f #֒S2j'TA `umyڌ&JMxST4np,hd \PŽZ$@ʰbP=x cΐ h0hָϧR(aO)h[@̫}6, HHȰk}&Oa0B*m`a>8B c}mԿ׬P3j.S6FpxJĂuQ7kJQdv!gkcoG_=ʎkjz8:T;^x*Dɛ\R~7Vd ]gI2%c8 Uؽ3OÆJl,}bvÓJ9Q b/<ǘ8Fkd&)ㅢѱ./^"Ă])Om ╘GЈԕԫ%iL$;qszim!ׅ\5to ΄@X0G#e!%e Mk4y&LXJ%t*Ŗ&\A'}qLMzlG`:D/kyėHig\|ʮjێå %=ҙk@.joH #gT(t>hèmA x #y}+J\xm{Eyf~8_Qc*B$Xbf(R<|-вs\Q"4 )@AGO3it&ّSѴSIŬx1(AYU[{#)=2 -ve*t$Z6H Ŕ {-_e֦)ϗMGlhDkT{,jfqRc={z@]  6  λiaBp1 gZD3 Bs`:/[IKA Pxhz!U׼%NXv 1E[$"AcU\sJ u;=;PJ>q7ϧFBSj됏qe,dFfti ~xް?Z h`cLW}rC`TJ!tk`Lir&,/Ʃ%A$SFY˜X!d0=mk* mc:=Z#Td~F n:9AX2b`Q$_KօM]n@w0B`}<}Cx 9B I1(K|lzIVyN% [O'`| U~@t;ġ^M,5?*8R|u#ȓH{h/zP\EX*Zp&R"M buѠV#A4;7 Ġ)c1p!$AD.y1j86̚DhpA, e`>B΂fyiwi fE0qYN[xҒN}m1eq Q#+N do-KXH@ 72vh,ޑt%C6Ug~)ϖBP"`i|[E;Se6'2? DKu3ץUcic6 mu@1.MamX%3mLIH r1q /$ 18郮Πtյҧ 317m޴'Uݚ2s=$[5=;.j'ҒKEPܱ[diyWw}n()?>4tZ(i:dIR?یtӗ[ԱTR2|%RwzԻZw s/KQ/-^Pi{Jz*AfAg˘8C0\H ,L 6W%BQƥUԔ>"T{]6 [ȔuI|_K[jI wh~o_(\ZކzBUZwQ*7x?5|Bs(`⎽e4{֛qy>2:P]ɫ9؊L!BTbu2;lσ{"`4 DKѸ<d" U.!p-C&DP($K8Ԃ`B}XY ;憞ORVp5.PD-|("`}<}Cݬ(N cTv(`S q'rb#PЫC`Azx|۶P2): ZIJtk$?,%l0ۆD.7Bmq\l}dރ&_Q؃`=4VAK: [ @?4b Oa@k#c&HYd%&5 0 lHH"&1$tO , k\* bu. uă-$WfLS>I)#% ]Gىx2dISD&`$& R  @ eK@BK1Q5-+BeX) C.>uGgn&]2J[ʉCw2] Շ jSy/v!ůH\A˟0U1V.X#$L\vǑQ(L8Y(f^Y巷s+un K֥ԹwwK}O 'ݸcqeThwNߝlj'c:%D4ъDyD;Qes.*@L|jQC^ ;sxD 6sպ&ѷT7Roǻo| x/ 8xq `@m'IIu~!'B3|AAX*xuLŜ4л )blիq-vW!I -*f(>@)fYc۾åAPlw:Tsz|`oV۾׭A}JcҠt6;>)Ϝ_Z@%*'ARm:oObGѾkf+bTYC_50ؕ*2Vڬ)a eZ7g/(QdT.?,Ϳ8s=UZVPށ @VF#*Q_i=dޣ1v-?ڋƆewoF<%zQ4VQnm7Am&'S=A| xgczP}X<>86Plw:Tw؀6H,=U* O<7A)%d̒Ia٩W#w'=@RRX9ϥ]JQaT:. Y"r5){Cn&}d% ݪm,spD`T2QC ` w"7@-Ƌԩ?Һ &kgszѳiQ!=[.-srδ\9$pخbTg_"aR$u%&|ϟ>|EDvlzP҈Sx/j^T mWؐyP#)3Z+_6}`ҌAY]eiE!8"0P~ƒj5#|` |rĂxl[֥-.TsWHHrn4Ȫ5$઎+)C̥&uAh"!;1UHA Euhip $s,ٞNV1JI%I^I*FB0rT:Wuһ}tcw{)-~2BErc* Q ]@pj@*!R<^9NF_<U!r FU vvr$Tcӏ pZ0{GQ0<Wp2A#Z8)gD((~4D; .q (-ᎽP.BO,y-$ICx8YK'1(?N'GL$s>9 ^FZM-p#sWO(oy|z5}Ѯw_|kN,h%6 3$cBRX$WhLț\M6J,@)mߨ2kQ-ȏ=eIz*"pbfi S%0dҤCrgHoEc!OsJ{| R L* p3#piprBb X1CL$'&R'Bu1„J"hXsÏW8>p;}_4WV ` Ky8p-$g{P)f hr< PC6k(p5,J۫unW_۫unWL#.3s9 $6* <r:=gܽt:t@O<8߷{ϓTiW7EÖdSL*^򬷵;OXw&HC. L* tj vS+o8He(ǘW0SN:t)~"S[\b,}o:Ɍٳf͛6aJCb(I? endstream endobj 31 0 obj <> stream xWI#7WGi)eCrk0r$s[$*wO*[Tvͳ my<0n̳ q.v<͇8g̓Z֢|9^7gysn0!& ` 1 Zop" :'{&1О=Uab/{HMlןD>Unܱ={tݖ~n;v[ܳfJыiMf\޳`#Zڣ ,)4<ږ'NEP\$l!$dDy 3oD}J5!6UX AGd=A}tdtt3yج6|ڗry@$䅋A(9^N_Bf =Tt4FC֨R!P*F6 <Vh}" Aԉ]HLtG.Q4$Fώ:@sfUsȉ(G]0ТG=Ui+f!^^-.B^(Rv5d8nPx@?0Х<\*_9¸^n })hdy~ll .(%JqȬns`\vAKy"( ZZ9жGB2Bt4ڹcru$L[Hvnԩ+* rնUlk@Jbx6qHY6)F\)ѓB rrr#0I9W劜0p4N7Ɂ^z,b86L߫;o:$+qf ݾ&)-Pqy(ACz̗Z c뽥‗9[0LU)nP{!bi8ar߳CwH'5i endstream endobj 32 0 obj 1208 endobj 33 0 obj <> stream JFIFCCmt"   >WoU-󺪻_AZ>Ӫ4c_* Zm ЩQ5(w{ 1fsJ5R|))>~~(@hLZ~]UWb @@a@Go`&r+e]Zq6_7b"kYk#|,ouP63_{Zʝ:85'm3mlDnUXΗ odO﷍GoDD?̙;(k5rlw=?,y+i.=Z;pϏn1TY)X̀0d|4Z+g}+[^?LT;y[zgN4c9[hRΊ+N^|e_ަtef(M,̉>zztu.2UJD~no ԌԖWѽ{`ݾwѥK;Kz-Q[?{&oC+E1ۭJQ˹lR/?秲彨kz/eH20y盽}Uq]Wq?02`6Enu\ a>ь=9nHo{C!wcWz=*1#IQwfc" 1@c c*q iW-='ϙS^K+MK03Wj,ҫ{|I߼X{nWܗO\x. 6뗪ː3*O*?~5Pr]/fѧByggX)^]3wU!$UN]Kӹd-"%pҳl֓WgpN=Ap9@CPwS|Dc 0V z#9濵}?Lq>B@@$AoJ2Vyhu}5}yMRL?d6666A~@,Vˏu!)cހ|ƣ]<*Fv56Li2gY&5C{PyCbO!]9fX[7>2>.K_'j4K^%mt^rtӵQƖUk5o/ ;؍@um*եHwT.O ~ݤXC5#HZ5, d|J,i){ zzR<+$ʵYŚuB0,V 3I NAJ"Ո. ;q<pNƀYJ`byhtYzg=07&+l默?Ovb4Ge\cg6[.^5Ar>@:oS` CTc X`dX3/~1`W.>8$ꇦ{oƨDžw43/åH_ezN˔tutw]kXmVR(*"_q|v\u̎)ï@ǵTjrjSM/7En5nW$8vejZR.h8pwHynҷY)[8]4DE+z޹21@F2 g:Sp?}?~,2 dAio8Yw }ǛG TO;Q[,W$ɉdǘUgm<A@[3k8-*lj޷I aȝڒv!٥u4޾nhxg|m6*\}L}>m+\IE]l<;S<.>z٨Sh笿_53Gwq(^Qu6jصsW{Y+\Hy*6ْ+Wh[iEr->QYez*Zkp\z0cJL q/ܵOY3k` طB]Zi/B}L4`~ҪTf7TI956]i^޻z06HFsoq1]%_LFPA8 ?4{`+дiTSwp)籕Q<Md3Y^P8i#.ZRB ln[-p`e jM~lv dDJܮagTvBkچf::p,IV)=m?YzeClqkifή؂¼ψȕVmc8 D+ZkYokP,,J}qHaY.Y'RW @E lb KTUI;S,w$RF ABbW2P5(Ҏ|Zu/Քh^DHx?tTRοEKqNUNO?Ӥ1ǜlWEwh:lYapC{xڟԫ+v []C$F3NQomqe~s "y^ۏɥձ>UhXe! 3i]Ts=qHTZV5pMlEm1)qco_]{v4dXk3i7&:[a6air&ؤ $4mj}oXS;%vJ ،,;[ g1k1u-^ 2~Vʮ+E2=Iv<4u)C6xpvH ⻩ЦS^1b;4 M caŒ+] 5K1er@ˊ[vr\5IC;z&~ƹ%|ON vuo?ӯ6l. +h~٥m>_ͱc\v yZm%gƉ6rbԠnRnrc"V}^{bƚxA"vD9`C(ir49ǯݧgRؔږI{zo[4EnF2Զ69>?߷g촣3D$Ed;rKӡ5vIF Z#.URo hVQ0̆ 0BG\/޻3_h;J v<;?b?b?b?bTc>T <FM•iZ$xcqb[UDc ekJPE[irzCVn+j}iNu e_llZhd1mfЋḳ@"!MƘv+RRkNֲErQ0{78c|sAޓyZj syL\6Zt¶Ҕj߻)2̦Y_fZwuP|HaQļ3h@Fo p{Zc :~*L_t*QUKBëigp}:S}Et1w k؏i%:Ɵ{G]w((lư\Y 0<$fIhqZzùn;ur!!_({^[ZRBx6U+UjwlB,?V/.9>"!FgG[cS(k{2WD*1RV M\i=%AN4G,!TȺoz,5 raNؤ/IdрT^@B{M4vXֆ (5s`24~|(fo+@W BKxGP1({Nq+ -P̪xP0'ֺ(l8ukVfLbf.]fR4ra6AkU܆s1nѐp"NMuoo~'+PDKE.4-rV:>6tѬt&vň*2^ |}2Hk\.͐DMSxI bz1e@&.2:6ϟ^㈏e a~@CoU>w[aw-j5CRF vuݰ]wl]vuݰ]wl]vuݰ]#92 v;uf$W"-ʧ>9q>J+~ "u9r~AWhif= èOoU>w&("S8{4 {L| RO<Ʒj Tlٳn߮9!]k. *,u*Wt]G%z!9+A|hfY%Xhp8$F{2qW[A>'_(qTU =ʕWE|u0&f 9U_ȹMsNRNNNNNՄiuRaB0XyqszOv=t~{M 节C@Asg8W8m fੱ{@/*I d?;ɱV(nYR?}}O1TJ)kE5jIOABŹ!nZ[p!`2 ~+1>/UW v[]?HF4hHDvi Xu)jX׈ggL7?ӍKfDyrt.#ynj[үVF'xJɞ5\Tga<J AZ  (HPD4o6 a2c!eQgKsL8= Smn/;z~|vk@(VT ړc?v*Nxnt16|y|ȃ~~8ܱy% U 3Xr'PI@Cr$.c?uq|Oqa,[vq~]a-$+ m罩+Q UbMl_v:p)>=mE~ܙy u}0ԝ{(ԝ$]]!TkKMׯg1.氽oNHN$|Wz%a~@#MJłR~S?2tp88]=&eW~ժ`Sc$HC}+m6 ^{2VExkEQk(ma8cʧQ$]̇0߸ǘlVT8Ux 6ǭ/T -0+wСHe/P.XUGQ/?~¼:>~z,+|;?ѴV3@y_pNCj mkfNXDZM`+ 2j"HnHlL õ~pxi HdGV.=YVC ΙҾx}6mkCkX/j4ѱjk<ߚ֡Bp0SQ^ fg2!{/\ҳ(ERDiT5J~ VjPKn[e$U48OU52R8(mVe~ڨWTsO[|KXG9+~eVFx5=v.8 ITqQ{UsiVo5[Jfe=Xa,kpVNm#3ef' Vc[Y~d:#|޻[  !1AQ"aq#$4@CT23BDPbd 0rt%5RSpsu`c?^-!LCp+Dq%yr eWt}("]GLo@ًuuJ0f2<&sKp5Nw[N\[/dhedEzZr7:šzM}"1j7Rɩk>L B`8<}rB}3`hːWֵM-lt=DSZ ga> uSmU3UA57S[(*UL$NؓENjTox#ΙQ܊cLmiWdVw2tʱ"ݿ:w}n˄]afرeY^{3IkP0C2rb\MdCBuEpDj|B{^{A(Ł _* ( V1!g9;ڌئB-LI2~FBS^(Zs"Ehu&[̫ݣRkGkO%\6!>ȱ:83jBaXڙZ <]l$;ȨѦ[9.{dwy_@lVa`‡7]BÊ,kv*ĊF+ü|۠f٤7لC.YbqT ="cw.z)\K$Ȩ@Y0<[OȿtElNCP?&a-dslycVLPݰ,J̇v犛iEz kܜVgy|9{RPgJcF0dk06`C*06e{#ȯ*&YSU#ȣLJw 'Nb*"Q4:(3L(=%\Unq>wN\c_ \xK aMo657GS {<ѽ0u&[EdiSgbkUCZF0F?JJo" A'40O *I~qJs:ON\3AD `(I/@@h_~ ʼnPe+P24dT5& P ̬[ &@0 2I%2Hٛ$X?֊Wh@^H ZƕcޭF`N6+:,;}nh%W 0 mc3@nl"?IL70 oQti L nHB6!efzgN22t!*jI,B<7BY"AI`Ղl"*N拫J<8Ȭ`igH?pdߠO\ !1"AQaq$#234@TBDPRbds0Cr 5Sptu%`c?k\[ M+JIgD0MW`%ZcVi >r=-˔Mj?ֶɟ{jtugo*XCD];CkyA\/6ie)خ_-c}7 yE͡Y{4I)%eŝtޞ'Tӎ51Bd%H9ra&=/p?;rWM73 f̓ZIL)LsM7-t40`DZJxkϯxh2STzld@P6ft4Ovקc'Y|Ohr-TS]~y`onr$ˆK~_ pfN|œ#u?77+$Vt,g<|c?V#"ސea 3Uw3xS&~+u!U鱡2%%ؙ7pv;=.h`"qbR .xqo[ʕy:{:mW2iLo|ԞpXZ| +5֭ 68fʞ.N"tzkCf=݃D/~_,Ѿz%}<Z%KE{o?+DWxTd()Zn{T>Ud)\p`=4i怢>X{>ϒwk&Y>'R hGrdMMG<^M_/~.C0$Φ&G NfpwWWſ2s1 ;PR&qisgS 4eG-oͲx1}HC{ngLkԚ<ϋl&oR3l%zYPl|drVw,@Oli2ҙCz,QW;NTˡ]dÞ?oW{?(Ÿ[)t)PlCH-v)-4c# xTk+5@x̊떌 Zeri|EiSs8T³]R]l^˂O#8-]\N4JSȭC(4ſ571oiօVX}fkʻq֦,/$ b/.kۂGh TgNZ4N˸& [o@I|/ kԠ9Ƃ+spe<3xڨO@1dchL :}W*M{V]<\ޠ)WnL#WX(*)WQ=mo]>Wr7`rSRs1bfm,YAVFa3uAQTQ/ڥnn;({I4mI ͻӪh58c&付^O=nY5E :9G =]!ۍ~ev!gh@;;[έs34zf*qRk0 d۰e;i\Nnտ͞vCgݶ,}?soegŴ-y•־BJxOhR&񴺃1͠u}lȔ݊]8vgg+!*)[NIML s,BtMJ ga+2,M]Y簎EQ~^5&iQ|JjKGV96{-!Ix'B7ݦ'u]mi=,gDy||?nϿá~| JܧCqNO:̰qg{Sz|& D/nT< ™5vnKBrT;޸u7iȣR4y"Q'1=KWp2F ϹYiSh;rKd~95,'EaFCPӗ[Dfcđ^8[kv,`awn98Ң|Z/^'9PmG mo,;BcVE6I{{i`]n׭sZ.VP(awZ){ {J|hجсz^0An ^wMCL`Ѱ7c!R42B_3hKp,ڃ⤓`ݙ5L,Դ#sty]n82O`^q}~ܧCqNO:%aJD\X>6˗W?g<5?*"UrCE?ܠai\uDԟ>(HiO_4Tv:",{W6CοG^5E¸ D$=u3RZ8au%3,ѕE,yr -:ӍQ<ߩd*ACs>;'=AK`M2.>?ϯ=KFZ}< TJIPy:a1t#HҎ[e=i HXNmg*3<~} {1vtJ|e_]J_N$gQ/"d75\,*yE^\cfܬGaw$7롞/DWנQ`PgyKdfW)MV7wOPWʟ{0J֭d?Ͼ4igodSLPB;3 ˬJucօg]IGPV3j  !$"#1%234AQ 05@Dau&6BCP`cqtvERSTbdes'FrUpw?IOGUSKpU#- +>X-6!wlEevБHqVRWvWqv'j`*!a2r#1DWp6P1.L^L)M).n~ ӾT[O)2PP "!}2:qIBUAPL]XT  >'r8,8d͋*yeD,{qQ7˨dծ#\b⳴#vttt_tqCgVo5BΊ6SY59= (x+E}  [M5}}aųdFp߅q#?z\\vҤ嫖 ,g e=վUYVd͸U\jFJ8m@!m&q;kc]Ics ~4Erj^ {"}C头a̎W)IY_T⯤AmEwQ-7SdEX ͰN¯ j%[xT7BeY!;H"<SkhKeWWkWMXN}] MS =mǭ渑 1`ńo;`,3n,Ty=&(/2@Z rw0^Xj M䤂hV7nk{2"g ecu#rUܼ1dAir^t ɪLA>@pf".54ofݔDa&}0MTî&2vIhAfC(fTA]j>1ȍ /+[˙BVӈQOM<)EmZcsy;.9J):  h7cu{bO ]%U-/"lTu㶵}l\9(OT*m#lv5b\<8Ǭ2BKt;TGL XDOY@ke1 :<㴙sb놬ƫ\夳2aMrҨR~C" fB`T%G5Z_P5fubzE F]9 P6hՄw*=e rbCzu 'nAuݧWxpWEG.kf #o+}9 )QD*"ў].[D "4G>/8)c3 jjgcIRxnm'y5عHY<9VQd75Dhnn+LQR^bf`F99Wk{'8J"-GCHuxY4񹢹$q+<10i !Oӛ F('ld39 je}tB4#洨NJIvŏ;wdǃ&UMtf#(u;g"^:ꩮp"#N[$g4&s$uu-ECͧ2s uUj>:| sjzEQ,<ø?g ^;$lNJ;A(kliMekyxci"k#y!?Cmo~X=;t胺)׵`Z˷b4 (LCK:욱X'ʥn߬Sw@ow tn~S;cvy66ge^0981۸:jGN`d:¦lA UTh?0vobTw]_A[g=<->HkQrH-)i15Ip4I_SEymy:_U(Z1C]vp7r~pF46:}UՕ)y4y# cĔM!X5él;M\Sz*RdNU+"I/"\D¢jtAIf/Ps;Wqg] HoMQZ0]DW9^ q`Z6EAJQÆXmNڎ9b\Sr 3J{d C'o&-װq H9`C<7^'Rz2Hո8%tTD3HM|SvTV%Oc=SW،QD_r㮜Asa =ԵtMu&? AHY'yYx2ii\;*x&Xoc4 "QSO PV`FN#o< 0~A}GyQR,#٦olj;4;!*;_'s5Eu?8r]􏦋 l7{xw;Wcӯ^mӧ^4] xF@ӇbEC Ԭr Nl);TEI~Ј~nCG$óϷh:Сar+?XÝ@N)&MHV?8 lwp/{ה@,UVp6Pm "TLX0Tݿw|"G3b1Io(rՅ% D_l}(#Ozu8(֍岳!l$'wW)+(|\tW΃@QvY+U0Vn`ab_25~^VWΝS an6+~B@]gDit)SOט wO ORQu2u~'dCQғp@3sbbz4B|rI;%7_ F߉cb'6##6kNTzMMi],0n>*6x)Q`A0&Ta'&C0X>]CZJ a^/-כ:DFg&dE Ġq,͸&;Ykմ>&մC>Lu9@E R`eu(!Etj΃_[N,kĬ*r ~[pNnPFm<=>z2/O^cu_)U[3ۚB=fm _]~a5v a\brPΜ]%OTyL[;/K8U]6@b9̐=wI]WY0 m|׏O*?6??LTl<9Q+J:2'*L ]>zZ/ Ab%..c##H2R>Dr]ۅ*Er&e8pTy0$ EtD֓$6$T3jueu*] y6b5fв4mi%ڜvqk\LּzQ#Do YUU:g438pP[mWh`8/3XlØ>pG5+<@B .gkOS6Q-Yq@` ybQi@$b*s~ [ڈ:Ya :QE<&cK̤sgDGlm [9iN+dڌDcnq&2Qυˑrxx1ɖXƍ<2&MdP<C7q0%91ՙ:TOsSOF̎1?ɩ_?86dq~MgO28?&Mq-UH0* *(e܀|V -$5A|ۇz)Jœkgo[u/GTy7'6Ca&[ 5uDФgh}JԂSMR5^P+ T]^1YB1zf֑Ldt_HW9z:ŨcJPep(HP}D __;r]hrmuQ|M7 uOa{XE&MjDş,]DIbtݲgpĕ(f:Ϯemڰmz+zxs!klo72HjQ| ƞ[d%rcҎ*{4 q9`ui*7JJ #J_3)ĺC-涶|(ApsXm_U5Pޕۗihy[KX%d»I=TE8:|# \ȋ1u2a#0#A_~6Sy.. >dcXwCcV8̟s Qa+jlƩ@;삮B߇0PXϷ~6MdoәQ<4_>ҧb+ѾϬ1;YF1ߑcA &(X 7[|ReTa^d6R$cgtWqqjjyCmǎka~)eq8)&~:⟷.?ʃ,/ *{ B25+P]Q.rYL26%~MeDiӢ9I`]jeRPs ]]avu]tYq1$S;ij..8$FC -D{4wh9TP҉@E|2ET9LaSGwQ^wb:y>( ӳ)0r-1Y-}ww,h+iyq!B!<tՔBd_UV  t#8<2m$SDXe[S@6sj4TvGo23Hsz-B4&K7tzB@%W]/ҤE/*E=.`hv,1n+ôƻs7 ?Mx}tKun_^O#cVKhJ嬍XskFjJ Vcsc=O ؓTyѩM?}jnb) B:otl0| 㙊G5o꾯o<>'6j-t45wXbҁjuЕWg?h e9AFoW]Egl 9}1 @!bB! bkwo$02* ZS;途kDO[`, }M,s&|}8㐑avGy=|5-~Gh48$w@Y\_o3vCYdٞ܍p41/rwD:C9)rqE `-T9TPUK%$3l .˫`niM9.KIa0*ܽ[6gnqh@E '8ԛ'<(xmq7[47:x&>IilÝN$ɱo(Sjj<{Z YP= >jZ.yi9~d^VUo<'eXYDDmFˬQo/^3}ϲ@RHP8~Pu|`t:x3‹ɴ&j %MX SɴJxUHdnƄ&3%O-ڛs*  9~K{mTd57$7mfhC]R'Y*ÀwtF<[h"+?I7Jb cGBYz(|~2*~Fο5wu3/E֗r0|1CXa0LzVO"m Q|$yz?7"Qk=!{ݘPMav!6dm/2QYCrBctn]Փfy38m~geG]6vZ<ؠ0a"홑V Q **͹M AJ_]tՖ$v8q ν ֖!L72`Mh7;]YvI2`Fv$༃$0wˇx.VNYfi Ѵ-e֋BUvPT.ff\8H#`⶘޴D䋽ڎVNTst3{K3k[F_k>ˉN0ng;ӗ~=8;;,566b 8@(p֐i`ώiRPZ2g $k7dYm]@gpS5] dCWA0aU]>2\5%:wɾȳJ܉Oy|ƛM=t߆֠<aޢNg֊t;2C 3&qpތd$)O[ . UE`j6Zra! ޞa>DZ *{%qVڪpS6)fC" m.m2;r>%T/Yiۢ&&ձ.9ǧ@8˸6bA>w.OW…ɃкBm(LM ֤cB"*uVAv UDMQ- XM ,zw@bS; ճF<]ڜG/-,#^"jk5\s KalD;J4t5n`]~_1UkOmk[c"z+ve7gG{Qq_d>/S](&:udDJeaM7r]6} Oc ݸGlw:}ONIr6-rVNuD` %>jݧ3Y#Gf} 4g#xbSR so*o%vO@Y)@ّlnASFà:ܾrKF৑M1Wޒ]=tW!N@v:tEDE]8(.&StH(`U W5QW~c29^~>xVQQy}-S`w܊mٲ &^&$8Yպ( 8 'ՙ{\jjy<"B'͛ޛ됿i)綅Y:=LWTЗq'jJ)[XɘYMcbCGX~hVB3]r"`tH`*6U{b kl5ڒ ֠HcIPވaUZYAaDD@JC]5TUE(B j:beY:!Dv(XT[7d *'{]Mvea_:9+y"d޷a tNz:+}ڋixl(rp`2{+:e%+@Zr:w'񸲤#%Rb%?ˆ7CW:E#4ď~XGd!^#tB8 mCy^kNz ${Zh.9T,/**z//Z轷k.'tUQEjZJ:ƀo#ʄbAI0e$H T_1L2#7sR,'d@F3jM2WeR涥YY ."Qbk&n}*])tGHDkG `^#dz(:+j?e 3kͅÕȮHݢ}{O&Or/{,n4?o^|ˡ=ۅf9Ԏ -kG5"-VtfTMQ=}TG^ (AI=蓭+OO̯ŵ%8̢ӣ܇ٿ%&ϾkLXkC')f+6攅o̗!(kӑ2*R}(SUBvg[Hc T}6Zl?!f /D]WR<2^DZYSQ#aDmTETOWD^2Koɷ#c3!Z}]d̏dp`]{Ӟr؉VZ(vHvhò01ڒ Q )\jXΌ"TM6i4GEҰi|fO'Ҝq}]P%3+6 l+*gHFk$"%E 0\6Vbݓ8(6>Ezknq ֑zqYsXi ܖrg^1}(6bYϓ^m5D~KglW'7)|"?6꽱8qBd?S> >WaiDbqR=5qIY`/d5c9(u^4iK70Fxw1Lٮ QDNIp T"ömQĩwEj;wN+Dz9S¿O=h ]%rN2MykTxv׀Z\\|Zd!tg4TvV&g˓SV9=@ fȚ4, a55^T,Z\A,n, p3i|spV`H`)bbdFF= 5mH0 \lh!ڃhZ# f%MX0tn̺OU^+tZH"c0İth )JmCZP5 mmn<7war rAڒp)UeZ/u}cs[WiPܶ " z*z+ jRL6KQ Sjure^CHIDנ5y"AV\7nثR5-Zz)akqd;`ϴ+m$PaXZ,8EmJx&ڑyfPO9heNuKtIiսXe(d<:X-qTJ.j0BRԿ"+~j׆nCqUu44DHQ()h觿U JcN^_>K z/ @C*Kb5bB~-u$N[V2"EcC,MA8Tk9SF4ζVU WZY<-&SSۗRX9U/[FeLR>QV[)-NlMA X6yXod)!ª-bm@( u~껕jC5}n+Ϩ_J,{.񄃱*v`W h^UDH'<adV׾Hiն[vB7v]Ү߹=>&˾")zI±QVMg-ȻFAȟg9,Y&n|0]4D\N 0@ɩ}ʜw%>z֌P+>(6Km椎K&Ye޷M<}SUSEN,.嚺yGw!=΂IX;0BZ)CTM|,)"6@S Pz<A4|'i+IUxZ+ N\-$_$h"ϗFb5@r!_z'|VWS_VzT=s,R( C\9QQl0ږ]1xaRG'4үբp<ָSCNtBEp5DuJt_t]Fr ;9ETTdS'Ȭ٩}q:sGPI*B}+Y{$${( A=K1A; 䐛_HSE;2|+,leQ.Ic2'w"jAGOqkXƫY%BlAbQw%\0RU@cOA Df,^U>fr]X*򗏕p 5 DɸCE^hXWu64F6$AGpLbwE9uzjK8D*&SGݢed3w(nM l3Dz{h [Ҡ;^ХlH$뼁N`H4֚~'b3l}%##wUסGw;d_oAb.Kf~oƢp݇ 1R/*Ka] gQJCn]RʖWVKC6Z"q܍KL}Eoއ+PuϺa>%r\'T^2+Jʂ"KKE)h66RyBpzHƸG Iȱa>QwS~J9Y$a|evb+̜:{LI]yz]2_^lvyoqvoIhxggP5s ! hQ&8/`JZv cI[f&SW5l$x)w$ {Ôp6V`ե2IOXlďesT4qcЕX # Ϭ(pǴV(:a=SUx9"H+Tj+"\͎YqinL@A8 _7 $zqto0I C*IiʄQB9J@hl/%ے p@븝nTúƢ&C$kAjFv3z(QnWTox2kksdvnT v9a 9tOӀ.k&BkHkyz‘M>Z[ZqR-ēQOQ}#t7)kge3kOuI=0 tSnk:G6戁o /?O/K"ggM&X97'mu'Uh@5iE z+#Z&MZGI |sz0)7D^UV/8vKn-6QIj ARƪjkht,I/UTfJc=:sp'zNCiYCr 29uUkU;<{iOYE{ih4ƒ5e{;r"wD}Z{l3/FֹD8m?"btZ>ژCWQ4 Ȫ)@iQ^x^; ǥO4kߕ?\ojX'i}2^FN~=τsr5Qʞ.ne%z<zW2|l7"r%hd}e㷨I쩴z4@\\bXwyBeE|ۋ*m̽SV:Qxo ){MkmX&sf[7Q)tw 2;@1khr=Ҿ0!.5$@MWj%b js+2dnv9 TB_4kTPbRiڞCgb<Qlj*\)\YQLhek0X"[`W$L+H,NO .t^_Wk1ǫFY?DAO Q#qŠ-l_78RHb%|ww4}.&]VZʨ3aYu5q'.P/π[-#ʥWIقpjV]ݗdݖi_Chf ܼVl+LԙSl⤒iѰYK2-jPM_Ս*AYǻ#gc-EH/T629et>ie$qU/N$Ls#6y_g_r[Wm:?GpNRAJUn+vgc['gͰ0blvR"O=%2^ ԝl m3@16Ҏ܏r&eX^si=1RSj>Ϊ*ޜ+3oua,/ zKPߧ OZ\&ՁWEKWQX{'~IП@#O;VcJ9`kh{db$^`frJ3 pw ҒLI4pYeUDH<|~[F;u$◓*Z!1(ӎѱEH'߱p_3!}sECF^59b-5z:\._l|rGh9Q6F{R*UѯWeӶ11xIkp]tr ,k4lg̝F2uΣΉaE^UI{*ar2XmQɢ4ӳCC*(xtRpˡƖ"5o+Z߈>; wLa>*Oa/#5𮧣.jV4KO" p~OP 9$֮m."rw&7'{'gxT35 |@_x>@_x> ĜaXYd0ΖJ;* #'!HZې@C9V jkQ@BdtbC+:dQL#CD^wPV6+~9}0T2>D/3*'V'-ޕR9[GQ"F05x+ y $:V/tvp_3!}sE3U[x~+S؇p=e ![>!A/Pc9|z3l,K#wПqZ1TTkv6$Vqtz;nFKa 7P یw|vw5^U顐CeԾZcƀ>Νp@o>\~4? Aٗ#2B\tneO >aZ f8N1wdL`#*@>ql5b@}渹n{-EQ #~q-HX g)$Gqh!OUY]) i,O/ eG ΐ7y rui# 0BcowHqH>RAL˪ C.DJpF9%hW¹E ,P ?#OD 5: yL_Gk5щ᫐^\;'Q Iatak9s['2;6i>'/vTDjuv_}_,w9ĀYrI"ZNmkQa#zIlj]q"t`idu}m)gs5~q m3$٫D"/ea6''D8"F ZEnao$Y_  5+Թޢ9>cо9zރNz/K]^3yJn_SA"9<q;QyUQ9/U=H%ۏ}$T9)TUAjC/yJ.{}V<^L$W@־#`%TUU>_Y86!s*}tEs!N뱱 ~`mPޤsC,f*.>1 WQ:fs2xN)#l*+6.uc]v_iN5=D\79 ! !mkMO8-/dyPR2Na`7+TBvz M]*@|$ V;XkN;5mo&QDÌ#7.q-4(Epȯ(Lm$1 h=UqE\'40} /8eތ97s9|4R!9EթSsKQ)WGeTM5x}S yXGVNB>6g"^[[cJq߃8U^LP"ܻ[lDzjʌRɅч TrKYvngnZ`IFquط87"n:0@K!E21n.wO97aiY/F@Vq\*ɷ8LK0+tGٔ7Q!W4;;\S]a%>3:'"nisR4_Rz=eCkI hHk,3l(?"{#OZ9E1YXN"zִ̋R A"X !Az~SV'h͑vpuBN _{[|4Kb!π;Do9l: 솪M*΄'RL nÈb٬s[c=ijhK_sxRBhiSh0^.@cyi 'IҼhE JsP$7\ٓm1 W AwsɦksPtܢ⣹r2, Z1a& :k %1@:zstvbqR5 )Vm j!.#-}]^;7?ƈƢM|X=WWjhQX?KPl0/KJ/x[費7 ̀"yRE3f[ &|jn'#`̊Hʊ?/l -Sd-FEPԘIETSTğ4<3OLUJD%Rީ7Kx: K2S${z$:=Kvr̊VN9Rzdr׾I*0Y pg1su#|{+QOv]C!SsPⵙ|&(=>̛aeC/P}\iXՏG!R< H0ΟpiUtDDԐuI*SVq6yaH2}tk+<\:ޒ4 |ނf 8*'o5i۷xw1Qw%Wᨮ۲~ynNU_ pe @!n:ݱpŸ@U;TtuRE69z !*PCg̚KHf_Z}C} +#E(<2_d2cumX?2[9tjD!%<@6Fyt: :C6}vʚsn}.컜:ꊸlƨmVmy(wnA6+{j%ch_$^! ޖf8䭋; YNP4ݗ<2[ k~F!C`EH^>5Z>QYN$u -W%,n|pķqiZ&x."< kK:)5ct'Pe QsUZ .9p]-t:W NVGY&X39X׷p.|zRhv[ xu:[}s֜O 3W"`v9uQ!f5 h ZS]lQz-uh0qԕҢ"5e9vǚTKm^Csг 'm]vݢ(^(o10XBxcB,8@0 pDª8ˢnlRZuA; 9!09d;9s#FֿrSZ)eDxBΰ:oEQ\+~#HxF`sQ$w,d}#+%w<,z1.f  P<$"r5]_+r)%UWW:5 .$ޖg kPInie$5,V&|B.ieyl[7bNiF6wI$.I_PEe^ r9 RA3 bM?d8Mh"5}3t0pIH إEZ?\yNj^ l}UfND44VQ)"J$VZ:< 5 i^U4 Csxɫ6޾jvⲆPJv~n tNږ7Qijt YeM2<74I3ID~lKűDfktM~|HiX -hg=^SjYױSXzǭg:'j_*"JnURm&HA6e<%NdMişO_<ѶHg!TSd 22VxdID6,{ax2Og.g@|UIsidW}TIM2}5O. A A3߽!Ħ Jldm8kC<6r?T9P\ ֔֜x]"KmBrUy]|j[b"eԢ'M֕7ǜCY}eD];DT+֒$')Ha!+бd  nsAX:M7r/Y6~n€9KsDFZjX VJr21Ab'WC?ogiNw?N)2{H|u:~'a/^m:*$UzG_%67;a°pZn ^>উ`#م*E!E2hKU»Qڜ;3ݷp'eNsх@W,]HK㐑: pNcaP^":)3,1?ej0=:@<9^jX:O"4DklƘ{#>o Tpk﮵[P=ڌ́F ɣYvXۭ@rqa cNά4D<` [2~4_C&$}MLzӭM/XN.= ܆V ^m+V X=1)W1 V< Leoxbg2f!b &f̾WЩ9aVi1ZFAgGhG"&.ohv5CJvkM+j4(K &/q동?kc\bHWg8)Ph1@ɝQsWh]p <_&ADX?$t?ta"mA,-wk\ dx3 "n sfmK;%E"bIQh5k*΢#/fЅ?``AƇl 5Z㹕<81;CBѼRqrX1)C|JvYIUkD C9)~-aw&'eiJnKm范U!KQ\ nWjOfw8A+H,ok@UPM׳ZZ̨nB ,bd;FAtD^;P^Y'®jj'NJبg6SڭyUwz'ݥIsiNȌSeU@ Y zG[b9FMTQ5d]27EOMЯti\r_ krÁa J;ZZĄ;w *spۜc?~dex,F=55Ip_-<ᷣ>w.#C8;G763Z}%jueЃZ֏KL";Lvp]eq2 rӝ11IUXxIQ-uFt4U1W\B3 HeMVPcA`oԝ^5wK-#xbhD0G0?v-IͷG.?ښ=U3S0K3gU\GIX~-H=V l Ema6EiY{ўŃmjFi?M]QFM| `^ 3ʵc 7j4& ӥ5lJZVc4RYaZTԬP߬ZRQ\vɟIH@>骒F?+z񄺱΍SY$wܼ*U<67Ii.YZEv8F Ri)SeG8MJM]E{vI96ROf6-ՍQ $}Rynn֕dF%TFx2}dW+VҲQ Z]PN.:_fKnO:̞|FGsOe?6T~EwȤDy*1ZJ}'YCH7Ha<|l)I; hTp9lGrhi69nF۔Q  0luډ`zH@¬HSRyqۚ].nEM:*k 2b:ƝR#@S ^rp.%fz!HbB#ѭ_./3aY%u. L8']0wCx.1vѽ)]LU>MZ&JRBn-CA_wE9EBi2!%^;M.3d,^l1^/{ p{Ɣvm DTMc[iDuF qk® ]yWKh ՙ.1=U2 $}RiBd^lN٢h1)Mn&;/Q%fEWMi7ZfcEݐ{[޳:4],&l2JIMCP5U*[j|,yRp83K㦛_< Orn?qȺ%0ϭ3vj/Įki lKzmFYg; N Ji];E}Å5h AyuĻUV|jLG#=+㼭RXII֙Z ۔ߔ^;4e%6!iluठv%e\"W`tv3Z81eXowT)m*{1Zs)>I?Uaq!Qҝz` 6x 'Z?,LdD~* rf޼;"F,n淫Xo}la!*\Mn/Y{7d5DGwUq3M:g%tAFU $T5ՙ>3|eW65GIr5g]Q5ӹz/ӌh-d֗A>IlO$? bྯќ,8ǫ,s$b<ءQ/KERwգZT"iܯ*~CQ =he) ZQKjuFGOuOTVe6׽BX<aө͸Mrx Ք5%50Jt΄X dECEur{Xѵ ܭƜLd =j3.TOC#>Č*2iPZ}ó4H5ݒi+G GP>> CHG~eOSOͲt.x51Ȓ\4ATUߧY §BY^-5|D <)JYE4xIQtܖ(mF^R\ AkO=yϓ[Z?|VVd8Z8&.wCA&{jJmcn~ZdRKQ2[O @M4C.gʩT4OUvyD y c'L7xu6IAc.r{-q^>'c/LߘmaTU 5 ""h|"?8^85io;[S&a2MJ$]7h_=-BK:d]j|cW+oL=M;Sk_˸/>wsfAjk{|CzhDu bNDz\/k3LJ(+1E uA#= ^U5|mSxF1kyZFkwce13}Z$0xTo r⿟*#M(<rQXܣDVWu1x $&D"k7NO7dx69Α4akºs"LTZ}')duW4B,C,eh02Hg#o0uܮ]!O쿛I^VCB$_ldo$ e'K%#vh#^Ӂ:6?ZXNφK=n0K]pUb/tx=3h<1Ӓpw"xqȝ~γ xB@ 3x )G|)&AZ|gfaLłphyǬgDn{#Tӗ-f3Ԓ-1hŸZ5ՑTK3>1!lFu+݁-$ޠ! kL08_2&rH \bmԈ\ kIA9>^ij |?<2|`#"s|b[@7ta1fb܀Lھ/Frf!0Uۘ TF ^3-/Aí+j诮1?ǵ:hJ H'7 j}Dz:B Cq@ڙ̴Q zUݚd_U2Yq;ZJd;:'_o}.CoZ į:Aw0Feh!|S2{ {\p2|v j)DEIuHmb552u4=aȫZۉqA2 JĬ,Q˦q ng/Q:(#M_$9Bv:Y+leS5Hz]^kl)(N<ӃydR ^z'Kx ZPVlTƘe9i[ӈ^P\ ?#2so܁4OQ,>,->{ȋ2i,z?{SwX{0u5;DӺSeo0;1>*Bn1&5 Q$:t:7NQZO_CwNm8[Q4n MD bgs V* B{ j: nԸ:{ ,wR@ V=J0T0:0D̠ܜf"hȳ4wMd# ޹6d!b.?lجԙlj,f"-CuaN rS.i+m=%NAa-b\imYP 5`f!*Y͝~STj+bI}ML]}IWGȒ1 wWQ6\0Oܻ'ǤK#f":T;" P5T>nF'dyv5mYbe.ʴx Ca _TYt| Xǜڣ aX .pKGJMŀ0ͷI29: 'XRn<>**y'vU]1!!T_"O/|hFEA6 8]6ʄ6wUyOlcngQˏ:MuS{ýiRu0ݦD&Ug,KL/,lup"su{r &(ܦDưo%p ඤ,z辪#TqlmF_6 J5}\eNq#on([g7v*9Mx(A" qGn|3Z4Q4ǢK:|.uIV$M >aR*J!=];D qj*r}ۺ>ߣ.lHQtNٚ9#]TtO-1wtnuni.麯\ȩf8HXM[; M擗p]FS d^5We؃; ;yW٭A5"{ e۪"'wk :G(roX N<.M-Ȟ.ES)]@fF(P*"vAM4Y8Qclއ ,uc q(VFsDyO9­+PQu.eW J@#Z_"]>"3q QeFYXN2 ktrk^QZҊEs YUE_iS^ gdӁq*40)I]fRhƻ&yL$7!AM`dynn"*"8٘+8.s';މ^gTz>5٦ uHf%4>S7skY7IRֱXE'VWk4䮳?^]aP.!1AQa 0@qP`p?!~or>:k~+e> ќFe|(BљEM Z20fz-.>J&(b4787lY1: X扈¢vٰF% ]d:ڥx١Y@>:ø*@g*LBJdY/Mi2匐M&kVߎ-+g C Pw4]Lml2mbËM}GrWjIWnM@%huGRa!dYfJb5Xz.GEDA{3ݔaKqp1ZzCT,$`!Xi@% ~EG'. XëX}x )$1Q1r,bgg=Yo\0"'plz/Ga;0n`Ze/3l  Crafg?x^L`ѩvnb#Λd@4 ݄bhe¢uDM Aq<92yoFhV,BYەԶ. CidnB #F$Rc]9rĺl@4.CxnhΚޡ`hz:k6m_O_!SCHNo޲=<"QOan=qu /pw[@ &m(L̓ bjZ맹6i!B5%ҙfE)pUPRԒ$KnG5Y] B2w$s9 v` L^]Odٗ_.pk=Mpe7V5Uڑ8|j dnnY)mD_n!U $d()cu-Eo4jыQg(@MNe1JJŗ?aK٤x&UD.E :<^ӖdZ$h3xvth(d0o;@@"A!zF fzz5_ܜ45=g{Ֆ[/i NA̯8T]_kRffKɩOC~+B?L { uu :Ԡë;_^t c2Kõt)3?˓! $[bQǁ_cgkI?_+Lt< κ\Mų !L?2}O7WLO/:g9;#}"+ޤi{\`XvzUOdcC/7'w" mfSȩJN<ΞҾ`|S[>wΥ-gpͶ0!nBC%Rk@ۗ(6_t CBJ$@:]y\ŭ(%,rlb5g~F>1hN 8~t!"e.*xeNZ͘GY㖮EoZJdZ@g26X/AHUΉКe~[(ǭv@^ݤFmSa+?gERob;oΙR"&*ROWҢ'x]aH8jB5:5G&60.D5X^Gk4D&)IbWߝmP+B)DK8W-+smn~n+ u1$pbx"iOԥfCn2jJ AXVJR103t6!Ӡԝ֌5-F(g#ًza:k5læLY~ߋfwvo I ^:>G`U GjrGGw{P#8[ SX Uk1\ҸMѭ\x=xWvްߩ9_l)nO`u< |r4J> L#Yu^(ӏN?S;[C揠'94%s \t!_?ZIg8"`8@pϖ(2ZI-:y'9 ŊRKҡ7V䙸 DIwkcsBx6~\8l$KI"wq &h"s.jg_])-j&0"h(2c!N*&7"q 7Xr%[W _|a؉167 S5Sf5͌Xl$a>ZY (tt @xShaس4w*M\FnO;:^ǐAѼQ#CηohhUmnp~yo<NnT78ȐXr%&H)T׽p8 uP?ᙼ4r)p7}/4ns ]7tF aCGgK,(oX߆90a4.(`Ej%Z# :Һp_pB;\2gs/aJ`/`w֧2r}#dԸ˰B2XZP,=eQ]#L14J=FB`ؚB5}}n*ᐨbB;LXjg@%T?@}xICP5fieF:T5 ,[/xt=~2z)ȁ4%!ɗ~*x4&8g5𷨜 FH#'p;DWKA:^̍˯gwΆ>}0 f/vP0|rH2Da`'W ϲE\x'͒5TC>Mn fi O"#+Je3p[(aI1sW2JFYi|_C'ZVg z8s`䕆jl+v@Ai{ q,JwΉЍ|$U晟3Eՠao,J&K֐{B1(; ψF  %J%[3ty|zcҗ/T:%[$ 3nLd&tqD`@+6uJ0j 5Hjbwj{zJܫ|G)V * uP3ƜRnH$?!1昙@.4Tjae˜ VEml`>okr#5ψ9e^FԎ)j8@T5p o ! KR_M3MԽ< 0Y;!tUu.`PJ%35z2#=O@j}l6R1ހt5'7"(wER„cQb-q{zs $8Hsn0F ioj 4:=xTr`"I1F-KJL ,ܧV( .Y^N"g sB)BU*Z`Wa q&)<`Ȏmh AX%V/zākY]}b *Q![Na&Lf\  ;Vk?"tNI@0o1|x:eӐ۝y{z4+d:&K!<jPQu0yɉOt`n2s{!)@@Gd_}fв, he Gr1I"^ E͕ò.[$ q&L@gW[$|WapDߊ'O"uFv]NF]J(v,ЈHbPf F-i xZd:."&ء'/{p3>v[IY7: PLus[B 6aN9)hl]"x5)JR+->fRۻXD;R7 91˴]vZCtxU:}~޵~𽶔X5uy }@oǚX p ~_Z&<; /c[\+A-猳۴V\ MY'͓+uWwO$M #R?wydޠ04??G_׏&)V6SY%:rcT8wDuPdI lrjYvn K~hw70u|TGn/9_ڊEyH Esq,W!X"n"JH?slxH>ufG>wM{So-:fkdPwdc>Cod dRF:5Կqs3fvK 0` (U_3S]^ P ay,"d}ɢ/xOd&Ps {υƳj`gg.?wusnglmABUVMyC/g81n=̩nHw2rT'֐X`ƥS!@*_>)w H0J"ea{`u aO#,.D!%u.jcd\A.[$;.pЦ QsMMˎ" !wPwmu))q-\vujs)lid|HR88)/gސ сG9r `|mCbVBM {u@57j#Uvw?.s2ug SǛ|ʵ<ʹLhbVRj=9r R#NYA#LAȬPiavrKeL3PmXZ䭧 JUfؖa`*LݕQF,I 50FKnˆ2]s 8ZΨV, WcKx*wl$*#Pξ9:ѳXj_We+๒U9q L}BIfF"Y:<`]w:EcK5dJdANfAG4Ԡ {F k ٘eęh8Ur_U镎 q omp|TJ`Z5b5iT=28C2#y/{ƂAte&Hzah6YiBϞ}n&仭YĂ$JX&l1Ywm tQod^ hH2cc뒯:_1Ro0)Q(dHHԝ Aſsepel!kpRU0͓>*! N oUX#Y HO%ƍ)TxM.MMj5MjCW..,5{]r  h#yNV g<]휐 MPKdK<V4O}.1|&7~A*>c,:WetB3krrK[6Sp$U6|7uhj4V\96%&85OeUvGTqDe̪8#_~@Z ^6>RcN̲F5Q%[g'b \8+irz&\HIĀATE2a<%?.gL(_Ȳ̓ҩ/>ςqܫ5l  2:y nݾBKFymBEi}_%63Y.sm jɢp|sLdt@k(mQcr#7 \z^=a&Mxҟ(V ;[ǚuC*[ 1Ӎl{6~`ҳ0>50+RQs6ˮ>Nv *s&>G; DZUe \,G L]pZ&tR j?̌a!Bם,փPtDawb'`0@ dcƊC;4 ͌] 2'$Hn;9&s^%S25!G;\p ڣ ){h.IQ uPsMFr' R2HaV (P,h;1L'wɮ -F(lڀK~h2TƔ4֪c9 zgvsL\ɓ}}fCYzז7Zz [7sF ]ܱ@ 0  O{u0 r ^pNz @S1:أ:o.7/0( ^ 7Ҁ!@&fWbc$D-T q<^ qU  YPc AGD"'"@́  `<wǰ}?#Dg{4t @0I3 jp(( P@4>ڟ؁Q@,Q Ȉh]0B  R C B 0 ( K~CBkb,% QX0 @%3r=? H@2 4Hʈ@ H,!1AQaq@0P `p?ꅥZ><*OswG3'Cj1LC%?g kMmpL84%[.٣\ ZgK%ra 89T-ty޽ wu':.|HDA"F<љͧfgXPÁ:ޮqϟ;3XYu:)phx῟>O>1J@q9&Y訐S[|X %Tm$!1k/ &/gEF"!NEvE"`ˌx*( 9,6\J4*FzQE{YZ)a/'Yr  q9R#!ݡ%dn&]D )QASm0>t̽7fXP& &H÷Bʇ 1˷/s<Ev=[IIKid:V Vzv)h8UWL]*dy]2#u(]?/1'=l;ukOx"R1d 0D# Co<5& @{x-V+Yz<2ƾqsr뭒<Cvcla oH֔[]y=VKr9/0ou}8G3o_P~Wy-~O|ioǿ:9hw9H~h|ᱬ@qROMPuX@Hfx2h Ϯ~_9< W/]gPI*?L/ e Ê_29&(O'Ck@ C,r}cG. %!U H+`A.Aceq0F(;uPQT3dV1f PbQy6ȄÂ<2Rd oZ~2Z 66pMemB]|8#_?o|to60}p> !@ uS.]Pr;eU~Ǔ> dA3gDᄐ9s/cGh1#={Nym15GxZ׍cJ*._o8\@ )Ҏ{&_?=}XϟV'0@څD&NRXyTXՕamzg p0_:e`_1L5f6Na2Sp#Azx\B7nj #|CȀNDV b`F'%1 3u 8lXĝ~܇~\/tU|ےx.4T~bX53)a&<5!`24a&.[+sDj3~]2$rDusm1MZ;: R&FQfh;51kWݤOj\.w?+!1AQaq @0P`?"8 T,M @ȸI.sIvb7g[$$\_KgSyH{_}Bak Rӆ`-ZLuC 2Su"yV],5ĦI`IH7Ds^Z>"*2 .5Z>DZ aw6՞I׍qM j\d|0wjT6=C Lc1{YVwx7~>}||{Jy:~?}~#`2OJG#RDCo6ދ  k٭.1D : xUֲ#Q̭Z S<>P ï#q6L ;*# *Tpt 0FP;#)&dbq Qn { f" 4xy+|5[eIYrsӳnUw7+mcw|6D=ő@%0, y ĴLD=| |=/OZ>GPwko<Ƿ=:kW]:@ccN}W NX{z]'~js/ (KH` ]ܲ؉iQʚSoj[^kv.L*o{˔L֜3\ <4Dbڜe( l b Ү-5Ã!ńTpXB;\՚*Ʈ,=8 tFg6u`:03'|@EP5Zjc gX1^Lj(hP!{c|C ;:`)yg"$rB( 3[0x|HDDD^uF^z2_ 6M`KQo'0"!ࡱF pE@b`\_шZc4Z͆WdR7ן_~~>29{c?As}~_~O>/,@v]v@(J2`E!`WY9QOx5uʻ#3}^Z2U#BFd9Jkl>I=jTvIpdu1zo^"wK0g ߏG%S3sd>CG[sPP˲3s)om'>(34Ȥ#6+ 5K>U_*yP I dzlV?>1RY|P`!B,! &J.qMhư+dT!L,ƏYo4?P}Y;R:q?הN_~y(C9U9h8&j+6*j_:?U5ʥ.E`fT逻86RBJ{q ) a,>>#Yki&,2g7~n$p̗̿|X˸ry: g&}O'϶u82=s Wp^cc&7ējJcnk_c7j To\$ټi|t@|1e\Fo?Nu%؊7!TMH.8.zv4tJQBf*S}RDiU$J`Rzjɒ3mQ"ePdB9L4@  Mp*Nkx|h>L#c$N­Z:ćʳeQ}omߎ^~>n.9}||޾q [>-}/zoh_Gw*yj䀸 .V=dž"CW?f''yx]2(u]fX 1@e0rڴ ׋X*7it`*BR+a4ˆ(FWanKP,S)ADrđՀs*<e4){T@:Q1e5>V8 2nn8lCv%* >JH v,aMibɥcܢ'ҹ}@%gI!Ϗ4>1GDm_cpƺo`:8qZ0fc $*{F$ƖWO=*[ms#M,ÃT0Ie+$13r[' yw>^mjV7/dg;یhxl9vj֪&-zctD.co|c˿?/ׯ<7?'y?':~?* Ph:ِab : $&+ E$0."'pbP6Ȋ81H1 rBaLZ \'+"fjBЌA(q$hZH;f  frcUWd,H q^d.ozL(sXlV6 {5Y J0A0Q-`c9Dp1lF]21,_aNK +ph`o+2\3TT&T肑KYWq56Bc2jE!V`఻ <~.d#G|x WŃ43GGCh~~2x޿n= yu)ϔoĮu8/dv=PThn1-S|XZf cޓydS eXڞ*DG+rgY`-Gl60TR6iSFp ±±5u3u %; Pm: ڙ!alSB'HJ3\-"^hӊb|'Yg[PՐsX %B0kљ2N LAsTHa\$xC $& $2q!LG0TO3)%@dXW.k3C&2&< Q<+2Y*KX &D( QIh pYp')lIf jaZ\ҪEVIP܈E~v#Fމ  )+l{7Od吙ŀ,=,*W|+~Y]/B0d $SP7wk`xTnLb5cB3"\W'/i;a^ޫ+}#WbCtU92pƺ>=M`pA\>ou?]n ߌc,!1AQaq 0@P`?4˘p)+& k1k邘d0֣[7sK~bWrਕK>~=BFdO" PhϝF<"Jxa^V!QFOL&ZRg9`͘Rs_6@F*R.E+"Y.`ju"EHlM k.1ez;ޫjISѥΆ!RHa66Q+:%ЎLZ!9G7epNd]Џ )HZ@=/t )cv)Z$"t]!u,׍U+ B (PDE0Pq SJ#gk&6pU 9 7 >. )7,_~~MiZ? .1lu-ND@J@̀ŦH kq;]-A!`ۉ,fzQJV7n rSZdʠ  ן˃Iqa I}ޤky(//&sRkLv^?H9AlpcAxAڠm2bINԛ 7*MʒbI_LI;I;ԛԛ&rۊsC,xIGԛN*M Ō@~|ks(a zO2nU'L8(QC^fXih"sJ"^ء!6'Zsk8(`΀k,7Hm$q趭YΥ 8seȧl.WK ̇qI]cEiliAA!gZ`}& |찙Hi&Ag^] ^Z28E@)bH.N,F "Q>>Ա< L28 Ozh ~Krwhl!X5_`K3rrԐ5ƵǂI-EcY -̦47KhFq.sW2J9-)f.Cos!6Rr*/@kKW|9}KEP<),h@ji$vm aX'.;D5OyY˅(?ا`Th:hFmUS*thTCXp#| K#T`4nV*ع0&8vmd by 0y MNH`1&Oٮk+IQ+%Do/}6r2$8@ i6|[J1e6Vksr?"ifBk_B ~:uĨv!.Z^_Ψgpa_"Bc`ȇGPW)8+At~ĐDʁ2ëL hU<L a͔"2X\x1mC@ҝQRAmzI0R?pfRUP4 ?^@Ͱ`Xc$:5&zsF&;nWz8բR$bM/d̳bMXY&(1yefӫ,O?좼`䪋Q F8]r w"ɣ_/*obwĩtFw{BKU1lȒӬ+4\>x?$Q^FIzb#3a/hNޜeyU7$5X#SifRTہ5a$l ,ҫ0 iU ʵŠqFGW0|截`BRyv'Ki/s*LȓsI&2nT&q[_W{ hdژ#%)XΥ҈EkZaN5V*Zvph ?):hΗ{n<(3o`D<eH E9}S8o7g%6aQaTǕ=) ̺3r嶑J$4FX{I7I_t( c{NgEXeS[f3=^2]əHdFu?!d0# ="):_a9J2bpsʸbb- }S( Fj I N٤c9i6AL_jQ1 8d"D%18P=N[7[*_3Q f}oYf čtj qpYbVyid&Cw&ͷp4aML!BX9K AFfMK)Y5PIȠ<J(FaS5>Yi@]01>!UzJn%)6b%>k֙`D$?@afq.F<"Qm|Ysy0] HdeV; mػwV@w5?Y$?=c^ڽc{3oBɅ«e@Ҷ?Y5"D/ k=<@ iLnŞ5yYA{~ICq*:, RN̫g{](J 0v傴q= ZL=0\Ne%jvC`Z.J_q[|&{$3v-=/L,)ϙ>&%Z'C g(y;|BEȋQ!8"U" & 94F*?R1Oe9 x 5}> ؽIbXp\D "EJL'䊿D )i" @KD>66ǯ^'2"n.v|@3SS1 BxCm&F|.*FxF@.G5.3L10Pd)s)&Cq՜ Jֹ9$ТVdM?dHʼ5tâUWpf*UqH&_QJ#%`wCXK:{!.~q[(<Jq{>[5>ᤌ17x9u@M*Y'3#wCٳx ̑mlFF7rE:I.x7z"Zǭ_O^`-:h$ R';/RҦȑ8(٠ؒ$8 հ--23?3bN DbK*?.ԕCvo>ɧ Kg]nj7*g2Z]5٩ =B\|7$ & o{W? `S$Eߑnt oR "ݭЂӑ|_|PDAiܱFi) L*M\0}9%B1(eu#|$IʓsI\XSpXTO i Rq7m>Q[tFĵ?9:\ݟ$(9?O7r ]jW\ب"`Ito@Utcwo]ӥiD`IP N@G4XWbBbUh Q{O#~th(WwD4#?)N=A!2Dp8I*,\8"e_ V6y'c ΜbJ/w>1A5Q zI^AHj#?\C0ވ}Db= LRho^dtxmC,z!D=ބagUhLw_w> _5L[6ub&HdSTn$˗Jg݉M܇xrT'3⬹Ŕ(vLUKMGļ0q! Ǟ3OmP/:PPD_XNj >6vmcI!*4xx99[|%}Rk4*d5zR_j.qП o8a6u) DQZ5X%ů8fZ$ /To"Y Y1 Uc 7ra-a0wɀCkZ N 5-gJЩj~vnvJ9"i#g;0g O!郳 Ec )NbBdv]q7P>P0}D+Đ'jHDžDE[UXU{YfO椏c`cKBQ#foXuM\K`pn8,NP+`)ڙLppZ=ڑIglOȓ>"?pL<@RpBNvY!O `B F YR)J^*$f<UD BԃpyRi\)|D~>h"av0[Pf 7CHiEW\0d\Iw,">`> pI8:LP0wѠ% o5#6-MFF0+~_IOGՃ;Yߴ AloF8&{JrAVh ь'?YҩԘq25 k [7ll,y l7 !)QJX`mՏ&j)ˤ qF&dH@|LRybjL90M6x98%bx>^rEpJ,^L*aIjРMJL +e|bK3T`8@ eE1x댕(cw}MdLat 8}hሒ*P~Yȡ ˶Z&ƤIiޤԁ(AZ#|NOσIm,XbŬGQ`b  @ #qB4Okmy̔, yh u"!aR1 k45҂8i*ML_Sx,Z"T&!&kSA )1yIG:^"^=HMHNfKl0[vNL ܝܸܞ&l$_)Žfdv*o%z0HZkcLi+6É\[Y]--ҥm7qܤ$ Mf(q esw~t3y0 pOx4҉ubv9ޅږ$hsR@23t{tLN^<8Ɯ -,7/Bfd‚X/H|k.ohFx1BN9Yo~=_(;igHo TN$~lA)|92aӞ8Q1b BG*#,Z_ր.=I}l QqKveL~"ATo1&)[:W ZM.6Ꮉ` TR\k)6!26B!C,DwLj'ADd[M]~% 5Sغ3#sdU?KpEcIRp9&z*6YcR|^EeBӉIzP̠[tR^BjAJJ(MU=y*Twg8G:bps$i03Q2꾕Z2WhX$6<PGmC' $m-(tyD:9*bX2`m)^mLz.Q4@[4egO׋;.?Q\E}#`$P{T8 %͊H$,BPkmq`0p~١‰I.n&z&FU!SUUSRm=ňU`ZGZSŽTr/1AJa4uNhU)H`9IKHMKATBU{"*5ٛ汴gDØCHt1dY30Z0KdՇP(Lɍ"7cuOMƙ\p񥾔A]ܿ6!vili4:-T{ݵJY89|-i MVl{ڔ%Dcޞ  ⷈ2]çK?^ň1o:\"ea3$8:r"*[=k`}8}zXo8 (0('0ϰ)R* x?<$oҌ@dD8 C.=~LYdðH @)% $ED pɒu[AZ{AR %%O/Ez g\bL*. yx%[2Ltc!0OV;b|y$0,d}ӓ.B @LEE~PQ[n(\p4jA?0 M+ xahYy @ɹޤܾ8ɹliC(s7;ԛʓsʓsI&NzsIޤT&Iޤܩ7*M&2oRnw7~=7#&¤ܩ7;<þ0K̗U659՝_d&p#Q(JZPR~C`<"þ+2ZW(Udh%.8S8*7un'݌ZU@~ưX,A.I#+:r"SS}HHU%+P3L &I*eCi^ aa@6t&ਔ"ee &O** a{3RB$װ+4&& (mO+e -O~0A'(~DeDzcU58 |4K,8.QBr\yHc EX}%(NÂBZyR"il¸Q5j/e]慿EJ?";# TxAS\pX ua04Mdt(HMPa2QL`P%|`#M8A_htp:FZ; Oͯџ) #hܶcB%ml;YsfaL\K_\ T(!>B~hjU0Xq .1HfNnLTBHIZGA gb[7q.*Ƈ93@'O'NT-; ]wM@I49"ꃨ/n}%Q)a $vksrJ}d-ܩ 6%Ss^!,J3l."_/@d"3YugZaУ!6Gt, &0 SK#ۋ~ k[nU:P8=%d<{%7 B20O˄(Hg@/-2t.4(V'nAb L( b[p(PTZBU>^ űx@DŽx'x6;P* C ̣nD !?޺Kf AaS+濲>&`jbPlv) &0Bب6* ب6x|lxP[jbPlTQv#ō}>yRB|Y^~k3; MkGTݰ[+wͬQrmuk`_Cr Q!Ep|BPB}0B5~}I5RԤbLgG퐠? aWY=Y"q( `RO_6I=G\e6-:-e0j:)*$G$IiX=u?TZFNb( E\h8J`5҆@~II3¤8He&}Roî&}#Mq~Sb)MuŊ lD%!zIޤ7bдZ􏮃YR66s;Ԁ/}/z6t:roJLߊܛRfCXVok9)IגM[4YVĶ64l;kε߹$dayQBD}:m, [$Jn2C/[BAח`ԥN~yS+bKR Y3ku+R_ :pM.yQX S!Z4 $NuN5o1AP6=>&׽3jg\7rƚ+c 'fFgӋϜZBO=Mބd5k@!ۏl[坰V텋 }*ѥ.3 auyoҜX 3{*C&΄[ӦS`m:cB^-P#WFkDy<]V58s/y];_!ܩO}J WwN ZKi`Y yU~z u3& @&R9 %WK\%qJ %ݬc`GP'˕`? )K*`2G>'_pՊ'Nطv*P z @FIv1:q!H (0E <5-UZ6hz@7]d#pCS%e(狓JpJ=I&3x8!tM#/k@Bm[DxXIlsq""VdDGJa p1*A4& D>YJ,(!12''=D,PrH昗v)F!MUhU_OD"|BeT(B4{G"F sBRISilE;E|)ԝa^ЃcS#=Cce-UKbT0B=2M^ VuݲM@f[{F9,&U;)p B*_[Qhf`Cl "3S.mʪg D&ڤш:m*Cd!] 0*Fb6$,0񝷵0Vq_:zaHBdC# $Q(/V^dz 51$QBmԶPZ繡_' '',]NMr /,! wiUl&&DvkdbJ+j8݉ʺ4xP_ټ%Hɴ,#]bZAb4{b` \ΝicM%]\T۵`bg, 8kF͜^a@C,S`4Lkv=) gD9VQn|4~R?s~!R0m3,nB>gB-I2Bl4kQ+ossB_܍D#~RԘu65)$ pzM+Yz_- I-&o7D9ޑ#6n3ڀ VK4D{> stream x\Ic790ַ`>h!r$hTtLzO*U}4lwc^^%vw뷗ۋnϻoj2~_53b:|w_U}ƏgqMq ;3#Ƿy?1~39RV7p{г05΃\׭R9X~L*lGQ" D[ٴ/ lX 0g!JW-FC,ݛVU7<*x&d~`.Mg_>Ne+|uuO7H#ːh.7دe$㉶xdGr0W/8J,_ Y {S +}cA$54!!@/bdpq~./@Z,ӆI냜HpzYB0y[D)9C; eWu 9,K25pt*# {Z)E#R -XHPn-CٚF[b}z_ Q\JS'3ljW7\ 'V02N_lly;}SM 0V endstream endobj 36 0 obj 2802 endobj 38 0 obj <> stream xWK6ya:* а[vrd.[,aC3BV=TUߧƜOgp]{}]M4O!sRv;ݿ~~%] , $3e:67٘ ]4::%.QQP#qv/ԡ8fr˹ZQ4Βiz?}م/¨YbH\3UvdC| m֋Xf /kCV^/7I .G9f-&WNm uϺORGޱoY:ZA׀Z'M@>{nHJ$]. ^24W3l BKY\Vl":#f)^H3 XWX8Տ " תo8- cH%-SB`EP46[C9͹)$puGf-*Yr 6eͶNijX'\@!6 ҏ`=ptDTPbM"IPQkKּY\)/Z`QְAt+YQAY%6–Pbù"GKk4Hm8Ng3>*ǫs9 VdlXX~) jGc eB%pȵ70}1-\pr*OQPCvHt#l#XãC{VbsrR/f0I7l{L!Ɲ k]:<`/H%٢щ[=D6/{ri<%pWZsVP+m|8mZEVs80)D*/>5Mۜ(.S[=÷mj( /3o=E XWo> stream xUoU^Z.mh g.k@ޠv&2,gw{! BM4F59`LP!Z4A%x1Z=]l?=LϥT(3X@X6BoHkLVG$XΘOvH}k~ w|hF!O[` |~HohSK7YzЧb N4v8ql~(㌀\xQapTxnM9x 2\A boN3uw;4wN9Y$LB.FTMp9lSN(MqiaΈ*M ND'ԧVTZ髨PKq^/;[C\!('ӄ7D98%!.OWI%L0{^Ҕ7 .']h5$%C([mqUȺMo۱gm~9f:t2ȭCy!iXd2HeVh cc?$) +b ;W#O#;ɏhIR->.i#tuѲET!_(Y}Vͬ D>72^️B[wO.G&|Y#EL/ 8hdj@X=7o(S'VEZ./P˗KHţe˹%<)mSYs83J̚O\rqa*[^|1R$6!TQtբcRQϳ6ޠ8ݲe%:,IL,rƩ=FJfx uO/Q(!00V<aJ7H'n*}fL`2s0 *@ZNnTq1.H@b'dYN^0V; oma< i endstream endobj 50 0 obj 1357 endobj 51 0 obj <> endobj 52 0 obj <> stream x]AO 96&f&=?´؁L)VM<@x7kQ13.qe0H|pPewMJ oKƹ16ү-78=8Y;{@ޯ~Mg j[8=O6=u.;"_mKd USU-4[? }Xd-ISSNc2K2{?'ŴSe}~my endstream endobj 53 0 obj <> endobj 54 0 obj <> stream xݼ{\T׵8>g3̙יã08*#DPP@(Lih$6Mmi&iFcs61M6iWcޛPm0|>3(Issc^{{=@dp$eG;B~BX}*ӗ@6! eN]BO{GܔگES?{ #[=LHGv7:z#$};Η0-OFLYrJ),X6CtRN`aQq,\>"2裺8Ȉj11gpH69C̻,wZ1*O|);d#>} Ό/ n>AB9l%!l|T?|f`A3q@ӓke믍z%չ}U+W,o756/[]xQta͂yҒ`{]v`2:F9iICח,Mx}X77âƏ$} 1d,  oYTZk/|{u'tSJz 3~?5}I5$ mG| eerZoSi(\J6,M"3NIOD|UπeW G;^wڠS'c]]΁.hHK؝_1IutZVww. $]r:4dV:u!FL3J)cպEN,4#YkG;-bS,Y]b/12g8%QD1Z:/kdscQ!BZu|$7XH& #f1$%nI_?V<[; eGޜ$c ,~4&磧c1ض=h?h\@te%-QWZ¬tn[}:ww>-t[G tYo]||GHL)e5*1B&Z^)P[Qʴe@2ab.)e샫Bnkhoq""E$!Xrژꒆ@+er5+ g% JHvTEkWCN /:TiEg8Irc*V|FV-⯧T>ܓRzEo̻WT+IcloLȰr` D/^_.-dS*z-X"8ße:`C,arI,aOO?n]K׸s[cY.]*am]L R3]m]ň%(THI qٺ8iS)f57&MI|q "jP'Q%cg[0|M84SN/whP_̇-اocllB>;o[F 98@A%(08d0M7 $~6 LƘá̪:&+1P{`;B wF޹ r >s[V k`[9OaycVy/Ѽ"RE.ʋlbKm~[ale޳K-sۣ/ʴN9~Y~$?=GOݝSu/Z4)mPvz}㷯~,h(rPxy󿼳iQS3df Jb|F7]*yPp`@{ GhFARM њ,SV]vwSD9M&ul~Unsu/rܼ=>f[-_t_r_qsua\тvt]•ei 5ӥ٠$5G6!C6ڕ-PM(qY.\rc)JvjpO8?gUwrcG Ŗm€ 46-hiAh56j[mZFC ,1'6QqFM&"!6-z -Ϟ7Oye"c95&wU]7Qμ˝'"##6wX qHuڭV`- AB .zfqcŅ X7x>x1? 4mzN\eTuӸ-\W'gjSK f&g%qDUZvdg̼m3pEŶo׍%Uz}nUI^w楱޷b7o+*Ѷ?>M3c:!~o#YOgqY> 34]nr<ؐ|=Lݒw!<-hy^,%.]Sy`4$<]M9s.9^S0䴺YX[pB &w%ƐP=T!V+K8_a3k:Kkz 6J;VX*ݸvpB2-W];6M/Us1gޥHd}fr ą8І:T4 MIU>ERͫ :i[MjGj#/NUXj|)RS@%JFGEՎ,l^s~ᅪeYUK#t+ Msi^Vo783urϑ\RHvZfΦ{ ԜͧpB F_~y~,d~2|:?8\\[()>Xx/_.4M%ԫŴZ=P!N6ig&H\ i*'.ЦHf787?g_&_S aaٍVʬ)v}@˖\Qc _XBX 9/s\wnq.*Sj6JPTBw c tb.6/17 qF[P[cE56iSἪxCnP|Ϛłx9וղWﳚKV5Vw\VC4/pfEZs en֛ f,6z^#=d1:58A5M]a]-⩮& UthsN֨Ԫnn4(YsV7GGC h2SLjb*a%xA bT<&rQq;/vxxAҠ?N*"[a"`_|PT)5g=7T};xu͈:"ax#3!BӄwA=*}`/h壈E;6Ίo>6~pfߋ2ý]gEDb#0\LKf+&Zɂ,@ U˙<M1"@o/aIb1EEl:T2!c4*BYxK$D#B-Ca?MqCV:MImOݘ:΍ݴ#p~<6>iw#L@͍4l؞q6l:w_G0?A_J9ȷ.GMP'W*oF'1w>ȇ0SD$![#{JXagRttOs5Keƿʌ:s~C%y4HrIV(*YiQs֫F+'+)*OT^ujR -mh;Ī۪{'/UcJ HXp=/3=ǀvMq&@Csw" C"KC.o*ENS*DėEpxHDWW V^EQHW^#'15ir"wjXu"43qDGD9/Ej@FO/-~UwDx]=EKFv%JjD(A'f}{>">-HS2eC.Jb$!WwDc 8"b$V}&eZTsblT+*6cET͈2ӀmSHp"w2)"<* $?}"(u9nPS+3b`:gfM3QȘT=LE F/j,8i}}U|sB> P"5^.D*,H_F}kNp[ٍRZA0$ලNO]O=:/qP ûy N_ u-j6 =0@3rQũTo楽ޓޤz!=Zq/g?n&(D9Q)wjLDp/+!6Ptf85eDzU[Prw{V@|n18%UEܻDfLžGig Ԕ4pw Spl&*-0݄*Xie~ m+!|+WҶ 5B VԬ\@h,9Yenfƹjy|,{soU.p9.BYBAAhՉX5ƘRxQbse2:aAPA]!:cT|, % ᙚWWSu`]k.Jܯ̼}?g쁭y%^TsEaUc7-mi]Z^kۿXq# -Y}cGFœ̚m̓܊GR {9򝈜؁wȯ9$DNrnQ5_vYGe-+}d)J}\b)-/,M/Ր\m2k!m^rIVMB^a>ZY@ˮ&K^h+Wl8˸lg2e _—jO*adbL5uw'R,(vW%}Q2Edw~e gcj'\[jjK0]KIno.MtիQ&8agGO {}tfrZ"ۼ#q?Nd6'{lz7NFS\aX:q/ui!l8BvŚw=CtDkh~Gw"vP_0RRZZ)o$A!H(IbޮZ; "YY9D+յXZ\.}-HzX\]8XTv.2lIG~Wx2[\gD9i:2&4G=)z *ݘšY?sME/>h-IzTl)VRSga g 2c}2gm=ވGeC .l)/eWe|VlHߖpA~Y-C2cu2wLCFl+/3 tQ;a* 8ҽo992ەAXGgVjrBʏ2ȱEO"BeX'e8(C<)_aS%6{=Vf{[,x : 8)Znvmpw7\Bтsg?k0.`Ӯ]@e5%=TDWs~]b }lSgw W-J_ڡ3K ML$Hjb=9/zrmһWlf!q J\Oql]?.e׎nҕk }߂Ywo]Xrˣ΅Krle7?9g8R Š*|͕WTlD,p/{􆘡@{ ' Ig`˛Iy%68u̱@W3Nhllxh+:;)1EE\46< D-ZeMM7tQu (햪-퇞l,o\~ڱo.H=4V{)jS Ʋ r%nSruL=Vdz5"hLK`;YPb_s R+jxS^z-o^귯3wjC5@ȕ .7Zv'3O}vtD|MTVw$ , X-<>Nχ5EDl,8 1KbK &Qn=f`&BڏV4׍YLƕZbA sL`Eh,,+.<HN_ @dug:&f)ocl2/*Usk0upV(bwu2jN-hpD457jMk [jt9砑"pNtg7t(, <O9^ϫJ`$EB1+&=H$:A@A IQF~,A):,_<=&MJ'%wY3e[9=oNhL"Wb$Зu=>ǘMBEF8:҆Igܰ#@#3DAAKOS S);1FfwjEjSu_[c++*hyt8?H 7\.(tpAZn`~u~S>7N!+>,G f.f^:bMmhA`W(<Q>h-QE5QQz! OFa8?zo[E܏D3Qx$ EHn`jb_މQx1 v(zw2$%!JkX_g3KG' -ʰaz}!JEɯ+^wG>n64B]G > g/G=G7+Ѫhc9}MϣܽldXrJzO M2b5ҍb~@r%?:?JE?g=> U,@ # p> FF(юû& (<Q6z"rmd4k@%M,zY+b€V*GI(Y3GbˌBFܘylh͜ɦ᯷͖ oS nA6m(dt}[͜ݏ?vjuk[RiY>z/_TIyܚP!p1O-ӍswspJI6pQj:iVVUqU7>3=dOA>s ΔnoGʰC MmMwhW9f_Y8H\FT5WΧ=Cnkxojd9]l6vjbz:cdh8*z0z}mKJ\ܴ<0|Wf챉޿ <XiU_\uj,l|7{"8O?b!엏t rA'g$*SśT GY$#/exDfd`~8 {Qz\h!e &$_c^ǃ[zrFV˱8dz9e0!3|^VxbnaisrLZ5";|urdY\ d-r%7Wb#knߺyL DeNQn=6&vq#Ш$== {j= Aq[*x|rϨGufk/y.{'8/a$6g3g_3e|P +U5"t}.pH(Ia1!(#^<|ƪrZ )7{Li/>mjST:u|_>]lkSM>ҖUM ]æRkKS~[E2(wc-hNkʡ-ܰ ZkZjhЬo4ik&ˬdYfB*@P%L`L:ƐМաj RIswE n:iP#qRD_~.U!!I6Cғ w{KRĩY+="#/In(zx2^#5K4=vIҽE!$zؒJ`P'c7#D/ݬ~ {,%Rl\%)q 18W nK|6f`}U`z2ʄqAE`T~l% /IpčEi+Z VJJ%HT.q3Cpw\t[l&Ih%f`s7Ӑqj&3ec>H#&lztjPʜŮgz59coa47&FaK#=.ԃpՉNف ̼;.mU5efySY@ ʝ~~]`~ 3+|y3PG%oOHrZMКkxֆ-ǾLgYϱO&UTŸ݀JȄjhc(7-0g&̕'Gz&$NKWXU!&4cl1*UQ>wNH)t PrʏtP٥i(vm=?dTl#,0bȚJv]éRM5jvR}@9`8RtZ=u(KOdY!GqϺEY}3 B/"LATo X< K` @bBkTz-Z*r<>x2p!sr !״X$qmSDmS{z㫨uޝ9zw7jP k{dؓ[n Xtwha nqAIH] P(FG8s/"lm$"[\\p/Ef"D"aQZޫy%œh *-?@cnr&(v-}O3%|W ƛyXPǿ>}`7\ t$V>6ھmk&Omm]YI<>R?k9oN|{rm&-쌍\NEm$Dn<9Ar]"(7 $7cN%|RZ\@ML Wym1+rl]'bG ʉ Vv e\kr~D\ S}%l+c!&?ͅ/lRo^1AZS| Vʼn̏ 1UWuZ凘NMT+PXk[ J9B{g:?ܹӵn󊵿"UQ~y4rk:j=iiL 3gL4WL7v,~oN&~]GfE֡O}ͳjGwWOdڞ5@{7'̋BC>:?i|J-\R=ZǙt?p0?U]R!"vXS:o';,|_s ;Ҋ[Af *"XWeFU*,o~ ^lgfr7Q "XU5L\Aaz@5s_|@7rSf䆯sԧ&MzICެ֬_/[Z-ZvZ`; ̵/~I9s3w[WrlY~ܸHfwk][ @X&M O2iHG&ȤUHdjb'_̤5y$֢Ȥu$ 3i T}t ,6b^I:>t&MIs$FIg*Z2i5)dw,֒B`ύiY?IFj&m")L:S:<1|`7{WQ^^[:e`goX2n o /[1y ]kZRǫ} yeҏ5&vm }%[pp['ưpxomY{ob`焯ogZU[-c}<21޶{lxx m쓈>1gobb`|dpx޽e}-[edG?:?0>u'Nlhb k|&psf<876?{-݀؉M `3ݭx!ݾ-[F'" \ kz2LwO0߇G[ASK8ق0;ba22Q٧2a9?uJn߬27+%HR2LI܀w)~w}CSƯQ#> 3N(5l;=Fcو25Ǖ%jƶ[z2mUL(Ӑ Ѯ@)-&v*P*q3^ܢfk< \8(}T'`uCJn,D&{2(-eJjB 1?ԏ+\3e kn]F:~*oFqOYGnFJ(cG2x ؙZu]shW|p.#XlQp*ퟃveϔzNSEm5?y|'6OK޷"oU䍵o9fMM־ޑAgFW jClכ۞}vYM^rO%:Oj*x?4|ggY>ꉙ'o>0}a}j=pXE[ zɭ‰g0+3ČwΦovĵV֪/㩲Hpz l0rd6L7xFnCwMOt_nEkUH q9/W˭FZ۾mCt#[r+M>9aq@&o͉Kkňcky-"dm> endobj 57 0 obj <> stream x]M0 {WBd#5@I@9TzH؞? ysaɿũ;E6c)\1FC+)-zS]gtv[C=m>e؇89>Ͽ5*F|n/5|> endobj 59 0 obj <> stream x| XUUZ{8pxD ^8 (^9AE3L  B̩tASYRMT_vp1k9^{wZjȈ Eյ9jB–euVM1BB\I⊞*4!c\\$p<Ш "ÖނP~L)tJ B q<x.UE?p ]칢/) ZYT7s<^Pa{_]^*+|!M0a?#4e,Q5Z`2->~ACBBìCmâedظIcƎKN?aT{Z̬)SMϙ7|_:ſCp/=Htc^:) =u{'ꄹspmB;&'/ Q8m_t1$d.14@\ kV܅|xz_G=0[GϢt="\D":P䩬UO(m;ڎסnT+"HH,P"Sa{bz"w>Hm~ m]^I@d!ZHݲjD&JF\\sb\)h.ڤ\ez\?$P*GIc=fXas`iibmOFEO9ځ33T 3`.4tCfV= _R"S09SFKiX*=K^B"b}ڱVZ'(\O<29m7"rՒ`gY5f'LԉQfi$(P¡Fo/#,4dHpPl0uZYAtrbdİP0&iXFML#oᎦ=/yUvni륭*%Ni'42 ?5M+Yc;^(,ށQ=@IrU=ta ,WOB@<!0Fd' ]u8k̞b^#U:* 4=@h oJ,8@ (NR+L`Bv]RafaI!qaqָ6Ԇۄ6}a_[@[`[Gؤn'4zx&? =<_xa3kr?om%68*$U~9k8|y!3#**??aBt61{ 3g ivbAjY ZA'x'z$tuY7ύ@QWIy$NAg"̟WwtϚ7{{*u7rHhAFS+X|'pfln@8R} P|p~uUtA;oз(̢J >!vO oF-Y?dK2Ẁ}>-uBȾ`W݀ GzN5tC M 5#OFhϼ`3[#DdWP4+LW.Y j1Xeb+_k+XmVA/sm߿r1٘[ O ڹjϡΤykϰۇ~Cv܇ԇ}zN6XO/>{ѷv" 9:=ӊVշ{⟟ݻQӃ7,@I jiԶzv S%' }_u7ح@!iuIvq7z66s?nL!i(KsT^cw!>q&K~M؈w?q`Q\GLبmq#) d/z! hYdz,D:[<;X}٧MXr)n)-" jgz{zDџoeBf6yxzx{zz=QsyRZlLo$s,LNv۸ƤUY ,,gML֬=yg6g[yY-k'VXcG):O Ğkm oAdhסvUvlF^B^_f3U=s)^³Lel"d 3^ !VeOO,m4i'~g5κj[l[i6E0}B'1(BM<=Щ+x :%3ʃ1d&-,#-.s4 62aيjCq8g#;lgxiٲ+fޟߓd /^l Ys{{~|uPEw q9Q"KD@4~J# E1A:{v$RI䓗{tq,C #p(BȵAF"T+:]K?U,?CZDEڨA-:mJP$6.+B[J%2ž8>gc\s:]ާx+D"BvD!>..P1㫮ۋqVlb@}H_ lSx$PAl}WkAr/0%nq䕸}}s؋}7ÛXo"YZB-͡C}Cp6!|4˝kg'X!ĊRbTST*Jr&UKէPy;ѝNN]h%"].yfvn~a?ڏ ~q_ޯٯݯۯo8arX<,kk~ u)rxt|N: yn$j>y`Q?1f̼qonhؼa˷W|¥3g#o$}1x,kwӵ>z7ވū0[Kl %+A=WنzhiQPD2<"&@HTMcR.+q1_ ccp_d qg<236Ϟ_Uy~ڻtݾyޟ&u-[M[\Tm=ӗR-hTm>rh?4GFs#CǏLC;y}GG$>qu.%CqSȋeu{{_n^@qJɊnjO-皥K׮]tMヶu3z࡫<-cʓRWtO8{M_cv_Vf|KgML }>O~1$(ip "09bOI뱴bL%ߓzPK ɮJ1Je9LHmKׯ[e۶r3%|pWFj\ 6hQh1\WOB?D?4c,I|;eK'tBϗO/WoAedH3"@Ěa薰M> [pgxIj%dV.5^Dg:?t)KWA=^C)1S#`:;`V y<6)Fy]?J+9pɉESů|.7޻AsIcb׏QR̈g' dX{y~Xvt7t_jvd"K]{dwAA_s X=a*Y'$ !HN͠BE^#bQJr2FRNg(4<AS4 W"guB?;hnAN8J\eo~ S?[!fNvPC %Մ7zPjrRʩz(7_Ƃ.GY=ST ze]1{^ƓY훜"L$ O0>rrU˩SRO:Ҡ:TaF t@гZI,k(NR6\^'xY8_@#8%z>@LZ("f\"0?Q1zwZ_v F,`z%"O=%u9n+gN{P3_ xxԢkf[ F/I7PbݷO1Cn=cھǻ}";i܌q%n1D;MD ?9nO뀩_%Zȏ J?] MrO3p&\AاKd/ Z hmP" $h_  Mv8kƓP}%kjlqSwJ"sCvtKZ!əzgPN8-r1d6L!sND[O2jpdT*a됰aq^ׇM~>mD$MI,"_"|Z<6D짓OL>Pc\,JWPY-6 * j$ӕ& ?V+_>ۮb7?Ae=xtA&om;$tk74rĻ-2#+Q^bو[ 3ü ȷLpWعga|Rlmowz.k2+ÂKF^: I0kBMqwg6iXxٛ\E?<3Zq}g\SחKt]'Ls{k"-^(8FN$e}Y{ 1=M6m޺aC o{88ϡr젟@Mkx#ހ5<4GCЭh m@`A>n2?,ևGP0.Wϑ#/|m@?a^y1h,/iC녧]p@#k^^j01{߾}q̤{ZVmI|:>Kd4avmWmx$Yd&3 2kSˆSzeuNqmjoŹ<jVW(xpW"5"]p8Ţ}c Dl 7oI>; /#xH2q-h,zƾl,^FqT3@ǠRXȺQXNƒ+#$KD%Ҫ@ñF&F8vdx`p[;0X|ǁCczEâW.\l7ՀSy:4 '2GF %-"u(Qki+Dⶨa?6m܂G?!!էe_YT2^q&{6Ԛzs?7}6c3/5py9! ,6bt}䭊|n=GuMzTCBh9n&RKot^_Ҍ}p"D2Л@*1;4$>!5qF1})að_ޠi08{lԣP({z\sS,b-ˀ:p8IdU:G^'z7H!N8tu,p4wAǣEk,>1aP K@eO_#/TvN,.yeўپaC,]E_z"itZSQr[Qh5!A-si ;#xj?[LavTip;Ag8.v%6OX~<nb^!Uj}=!6Z庑dlt 0#"@a̵t)!K#qxymo=Zq(v[D-Z,͡QO;4KABN?/TљV_ܖޗ0EF ՜Pa7n}\oͻ(pkƚYrx4nz0Ϟ=v鉶x{=k*|5Дi#eGq%Bnj Ѕ =|~zF!\3/=F[VE  nݺ>a(c)?yu6}DһE[ǁX>@Zw~26Ouz`0@.ΎlM9> 8>{׎\~|iS4Ht,oH=;t/̗ a-xHõ#u60cBi6E70X |a.#ΖnokkK+2qn!AyaaǛd:yX꾞A"\FM/BOk>q") i oh41eg5kvFӏӷn1 6gkxxzz=<Uui4yMf9l~ӢL춼a=>>5>oZ}s};,~.@(VhBL"lF{>`y`\&_EhSV-#OKm)m?ީ=t[j2JE}ީ߭1mi k6j[^-kj[FC m-0ԶCն0\J\UlqiuxkB\\u kzY]m]"ښ]YcM+/Q|gfp.)]o-.-\쬵8eEeVGUEQY{LAQeuFUeUzUn|-&$)ث#K*:@:%6cjk%U51κ,>H#:.rW-c7'rEVr~O3ze(Yj*^?YSQV9K5NXkqMQem-a lUY*WX0jQ\VV)ȺR⪊jԕt`Y19EUeEpYYWT))+gkAUIryIQ_`e@X٢:'aЄhRqya(SbkVZȉV89\ֈfkVXk ]߰4CV3Fש -/&JX':UEKuGq9$#QM UѢeNNE>%1*L*֖Q* PAtVU^X+j7%ZYR (H ~[Qr1E+*Ճ-r88 }^E5|!lq%GcqZ6ihQ1e3޸qaEDƥ"XYZ6HՁ'O*X֨edqΩQk5ngH'GEN&XVUև:kQu5XѢr'{oLiQ :+pҡ"1دD(dkʙes1AY˙{q.*^Z[b Z ,/aHMʹfZ gZ 3fggdfX# 9":'pjެB+O-g˲γN͈fΝYP`˷fϘ }ٹsfedNüܼBkNBZǧ3 cZzvNvhkVva.@Ӭ3 'I˷Μ?3 `dܬ|X%sF&&͜=eja4L*hka~ZF挴 < 9ʇÚ9M.cM.,(L2L͛x4+7#0;/ך d*)sҲgD[3fM,_ Sg0%373?-'Z03sr6k3'{DGwr^nA歳ƹLKio2nj 28y}.Ȍg0]&OhdUe2b}ffd/Ƃve]쬮c+Ug4Z OUx,G Ѫe~˜k+bdyY-tUjܫ-*`V(E05e0eyMY8kQ=֔ݣ5TH[Fkʖ9W8&enUsץ}hu1!)3_]^, |1եձD!F+P *CCV4pO@qI"aE0:DEPMECo61JC>X w'YWԣ h-.RhW'Q[J%\a"[0 -nS03`T%|[ql-ZqJ*7gf U8RrqKAWMe0>Uhv5;1 s@ss-_JcprI:Uh9e2# &SMWV8W8RhV7v?e*}qR+\e39 _إSk1_k)o})V)U1U=WVu˸V(vìX "U5ZF++Vhp-cs^*9E*}VVp(u?%*Wxx+0;gׁ-(zV 멆kR)㺶5~}hՖzErTT Jza .OfG }trc{P`\,ߦ9>A)ZQoආ3+U Vt+[#'bO3PU/P1_1.S1MY*U3`//=u5붕~ Y9Em)PW] %q1P οm1[V5Ef>sf\@wm{Ow:E /Qg X5z)\Sb% T-RmN%}2:y(:yT@>eC|x327%a#5΁6fqX |2g4,67kd>2Þ9pTDZg<IJQe\Umc(B+1O6 `gsx h)3K4Af0'F9΂LWiV4d{L" p kSB[P)ddlW,O2kCQy?oN|B)Innݙ!ӣY4·.dqb>k8Kϼ>mGqP`, O9*}pߕ q;u}~{p=gv`&x)|l {Ĭg`w宒?ugVjٯJ.Xۗ(/3Yt#{|]zuƍg lڛp"ԍb5*yNL}X Uq Uտ.j*fd ~0([7H_tcxxUN[S?_^ Xg~1< [h> {p)mE͈u Rh{Yjmzpm_Ol'#Հt|yU2# xϣRһ(Nɵ!*FJ~8"^2OH\&]G?y|K(6\!4@B萾j&KJ?O>>F>$5Pr?>_~3{)yoAzϗB;۔'g)y33C~uJN7S 6%')9FɫBR%(9J˔xG|a#!@!5KlK Kv񐍼H ͤ)Mzv_#g)yA:_= OKIJb!(ybDIcNɟ%yyl%)y]RIHh"ГEvP݃>#RH i[IÇfJzyxAmfR{Jm єF[|fiFi5flf~J֛P}d-%k&]%UdU(%+==,d%uFދw`d_3#bJTV * HpRNRz,>DJ(qR⠤xQTL"d"JRr'%w7Hwxrk6x͇7>d%)(J POɭLJ|H.%3(i%9dZv4m,ɞli*%SidS!@2#c,n&[n׉i^RwRIGɞjށSQ'Ij$J&  NR(I';ȸ it21|1$M'イI")I Db@bt~R!2*[CFulhY& fq-6i$%[ld"d8%Q "6tɖIzHJ"J­U:M'r(%! $H( $E|GI~$">& }[(1tbLfbRxi;Oi$ > endobj 62 0 obj <> stream x]͎0}"bDk'3Bb`XGe!14R Q ޾9ܶRľ| ^l}Mo9)?u};>6)?sgmLvͥ %S^dUަ\s=|/i>Cʽ^;RknCݤ)[*_v,^<OzyhQb5g,Ȣ9j5d"W̯/_yy\!1; y˼C~Gڱ&<d 2%u:~g fG`/o{<~fG`_j3?W> endobj 64 0 obj <> stream xԼ{|Tչ7ssۓdfO.3I&d22ل\6e!1$"^PI=kRxzGm=Jb/X2kpo2uy|g=k_a2DCn\Ђz!p,ݸ>piE4|!Z>tͥ9M˟r!E~bOE9BKh:]A#l#!yo-Sμ,])l!4͋o8zܲe7ZGКu| ˆ4q@L:v h2[6pܒ'ϛ+Br,-UM$j4NmJ6+Z|!9zOff]Vˣt=>@}vB+s53>)ԃC;A1͗Fc _ =^DqSRftm0IIe tzUg\~Uh?ޅf31cXD'Ok^OOZ;НwZ6Ұ.2\+՝h&MC7]UputL_bz/a<yH;~LC]i[=k9_7s uz494qJC}]mbrU8,&\vfA9ӁWR vZL#_ ШkZ9sWr*ٜ 偶P`PZCcguZ+n,&%my+Zco\-J;l2NM_f(G&4Xih0&A Ҷ)1,c$ܶxp,5 .(1f jIhVn^25 .?c"ZCo#iٝmcXYu3yCmcQVkHb(ov'tO, ݹ=hߙ޹إ%yl9FGic~7~11:>s9{ +m}A<@c :%fldNw> %]4,D{>KH\<9{1jckJOT1`hh-hf tXh PJaEvڍ嬏>4h5P[:ݸ"V(S٩SZi@YU1ZbqNVmb1W|f׭sMC饹RcV@tk МQ5ߋqTtJWm;}t-tc":B1B#Tv>.=q O]kH6UDž۾RMۗܘGь"@e*Ӈ/bL tM+ -kcT3rNԦc/(Tc=ؠI$L9ô-e@whYhQhE`LIuF97ژ뚻 6cQՃ;֡_U]%"$Xp[%핈KZuk.ꓢRDtD 1Lf#`!@sZF*n!J:h怹ʬ }Y՚֘[Qrp8Exsl51Ǣ Fk׮"gځkQC?4!Jm.L雈߰{bK;艿{\ DP!Wff"s0?i݀ ++%O\-D0 uB |BuR42A Zjp<aA#@1r)uN"(܅=pǝ˚ƪ"C%5eZQ7&/?J^GyhKTb,5 :Uǭ':Z?|X\NlqhK9lkMfGx 1i;Tr-z(AGS QV[adz00$oBJ)JE2+Q'X!ϛWX'E&SQ9*HhrRV**H^*CP*R^Q7.iH).>ğVijܮBNb6Ye@{Rݼ%v}wņo {4d7Q>p~c/u6dQ6`矕?q.<[pǬYw,|cr#E\MQSB]%.>.ӱKPc曗`zVYd7,aNVK"GJɭT\rU "hIEyPG$8IMwYyNhW%*GQ HMQubE&@!sfS20"1N)Zc97,qEEYƝxhv])p/ҔY*Lqd^̼^}dŻM{tƷehKC?k$3wVow)Lvi{djoߟi cytEѭvT:6zUAuv@ ud*pVTE1טZMM$` e[o5kZP6}}H[NTUT{ 3ʾu%&y(Ǩៗtm^?R1hIK߲HޅyZ̏uyeg&򿿧ti=ӕ֐͞TkecYaLɻ(q<4do|H>5^0F0z;L wz]]\kt=zK'&U(+=ESEEcENj=49VT5ϧ]"<\ޔgejEjx)Jw M Xv 1"JRI^X"Iv2m>:0g2zlK2ʍD@1o;LOMX2 'g\0^QX + ôa I8L wH, ɢE:( ESQw{r18E5@oȨຮQQrjU`&yWQvδ8Ah ,kuVOÒ=,(˼xs;6ɒT5 3=­ݹ1[l"u`9e:)^w #VʍPp澩SIOQ7ҍQ }~80s 8PC:@4(hSg{RngJB%)B(0hT|SbJ cD9}V9F]Ib+qkE-UOK` |)  Gnolx6DW|^ŗ/{+{xD{>S&C{2  fROjvl'D 3rҤ5-G~IӘشE@,By"[{7.asVc X VSA)AdTa(J0 t$kRT=e});UD"FJz%U %rߞ/o13]/I3ÿ\Bŏ[%nhXx./@oהK10-ݑTѦҏSxż {M\Ha\TW'HJČ% l`4Û xe`Q \_, VI|_~ޜ~JN&F~WQ~>%=%nNʑWMJć d )q_&V'uYPrSi;'O $$@˓gՔ+o' o%}R-۶,mjX VaAgd?~,ʷgx0 uF-h*5ޓ۷ʷd03QyDUT/[:?lc010LRJ5j@='U] Q+@AJAN RʔS "JlmLi#8!r#rf!F]z>nhY}x͊'WƇ̢v0܊'Mo8<6[Q;qL`!q`h \Oǧݬ{XKתKt2H*e#ee'Εe5RP_dψy)M,4iga˓񽬵RNh_6~RV0r}:wQR3Y2d]4aҶPᬻNip#/oyz93{\^z?GվUy S ,L`uL1ajTs"敢tUton/>=^͈O0>?etd%RV)%0%$rZ5s@w) o'ߚO-kCA!-Q뭸b8Qز/fӛ-4 aLuY7+MvH_XZ ]~s H ~(J.)4P !TH^)oI ^LnI/>/1;2%h2,2lqZMu2Q/N\&ߑa p z,@kβol}ڃ 2 dxkQQWiZ.Xꣴ2~S>̐V,X+G۴O&zyeeLS$!X"䴌O_?"\e蒡]~5+)W⮊6wjOi M(gnp<pt4j,cfZfS ٗy02gE[~ɤIX.X%gOѽ~bolļ+)noPɐf^,l^x\bp4l!F!_26lwwUK^\U[Dɻ4/^B9/xOx_%R,w%p za ʹf-Wi| P rǼkhJK+xGx,gfM4hW=j8K<@+f᪼+;Ǭc^<D6 ݜlZ*Fc;EَӊS3nëw ݃-čj =O|:}5 %^NRZU=Y}d@D69Mh9:LAcq:\Xh6"O>2QOB9a&^w{C;[ tt3phтRrAM+M&TT  H#G, Z|cx|.a]3MAi7pqKLf'Q5ˍlnmvX-  G5vS3U}WJ N35k0_ S |ZrZn . T ^(ů[KY W gc].&\ΰ>O=CFg=1˒J. _I&hD?I; 듰$ ],,%ϒp"y*y&I$`j iE) \'D%_JIؘEII&qqZ|}ܩ$yF{$M`RIhI:mN%$$pD\ĭ 4B]:m$-|$9d e٨WMbt$1/,{wcL+,?LXx&&&I*$'F$k<=Z#8kILHaSI:{Qgð.f2<ϼæf iaDݖoZR`Àl"!ܠcF8o8/+c̃-Jsm-5LGURWvH(>;tQ:4~(wׅw'dE\K&_4%޴Մ[I9CH!, #.#{vz&ޅA t h!sq̱19Tw{|mFp>8烣3>5Vǝ^hef52|a/[]| ]q#ڊ;M!Kﴏ=)iiā 6l0l3``:hlz˄:tܳNΐYm͇+jhwǏ!Gי5Pg UuC?c7=96yvm|S"?O%3f'&*?~/-?A秙Q^P,WW#)!6@_\Pw& S3!n(.E#9>tTKЙB~\NhYVeYa{QՊ=ݚݯ"&|X! !x%-*4r5˰7DR{P;'>z% e9]!b fX\QF<9:^N'(A TJFB{Bcӡs!A gtwYz "7O͑+V\6k "n]JS~db %d~`~u駣s6̨h/\!_~VE'&;iOt]kRk8\>ƫȆ菊e6Es^u<̥ i 2QAq=3tyWuآI뜋hiYK{10Xt.([#0"DϙT!'N54) ]8 L lǡO~)k4$HLǣ13sg^z{n`9[07+5xZ1z?!҃!9yܱ%,Oj֦drj@. 45A^)yF!00>D8#p]U)̧ 1SL#i +Ӡ+֚tcik뜔we1]|09qBVx2[rK2_p~Xb~&W:"||yEif^VnؿOyey$a{˔^DB*^1Pw[Fh9/ZOnmT;O+!Q㨕5gGWj;tÀi}`LRrz7OoƋkËOEn}Qe:5S:p{#1:r%>wyf#r7l5SisTnR[^ ˃X[[#"J1hOE4ʔ2z\ \E*@ir9Hdg0KC.ۅH+h GC7F"7>s\Os}Ϟvt޿]]OuԳ̑# ͛Y'.H"ʝG/mc޻͋#j3TSb5-!>jͦ7B>mz. ^ YfT*Ь/tړrSH[,v>9`a s0ހp-w~+7C"sn _@苭K3<xGyPH)l/ƶӵE4 >8X{4psSnWʯzA(OEൃ+ z<4'Cvpe2J-N: = D.%Uǰ˸߈vN>rmq)a+I+Rz]-+]=X,`Eq1^WX\g7U5iCgidZRkK5A5ht&~ؔb{5hMt(Ct#H :4Ns mme3iѳ~vD9{,jV%\lzu+]8t7z}+UVGW#_̣Pl?\[U6cԅ,I4n~_/cpD1!nGFnѼbvb}764?yPQãm])KL$n1C'q"? '9'1; [3* Z5%yʻU:pAmR)Me$E.0KfGccذ&Ҥ|Ɋ9-ʧe&*` nEv8vxI>-;mCQH,+7LA"'Bl}»o-ΎeӃ6Y-Gk޾5؜ƃ{wݹ^ǣۨkDI%"Sf2E隢H7EW ~ĨC:mx;ɧ:l2Ox8c7ZL'~,mWjW fLP÷^s}*-$͟򴮎g+ɏ~95<833|H>@^A(=&.~YV M:d/CP߁x!w8Z]8TokdzRI4r)ޗ3m,FdK: '̫1wT *Úߺ)~k" ,NU;"ķxa$TuO^lʄ?en,;Ut ?Y(^\"dcy& ][ͰV5(BJ+fy(aT sI_{'14; RGAv&q3ͩ_6.1s9ci|6sp/ B\3yaoϙ{|r`\y'Ubyn=S^8.CA;lֹ70>f[?QQuR1#&m`x 83ֈ8a΄;fm+%~65&VG/ƫwhI,x_=GC/G?kKUrz62RgSKb{ s>=awjBi_5{xgH'CI bVJRbg(%;}k6픇&g9́t  ]kS| E8 ֋xmF0̽) w.(vAfeYj/v7g$!We1 /0a`p y3cʦKijpLQwk[~u76=|oJ¦*nB&IY/{w/sB.r޺olaA7rJ z I7w5ߦcMZtH[@Nvu]]|M`H<aN(_>jbUs;zTc>]?Z+^cdR*b"J3b/_gC.`^6}ɺ{)'J^J͗ f R71@Llldl0h]X-߽yޮt>l 9bcxnS_Ytznj}xfQ ]ДϣKϽR.3f}ΓW4Do:=~lvvVVP9ڤLކhS'O$LP\\f#|%T*R<]ɕKi뤢uK6::mĆm6ٌW ??@S\Cxl(B;H >:a㋛3Z|OsIc??⨨k#bEe[ʑWlm3_}(tqg}\ nEyƭFNخ&rLgz\|&I0s.8#Md{Md 8{aG3x0՗!c<=X$&6- _D%#Q1kEG="AIxNNj1U.LVոR$靈d~R%ɯ朅Ů6 ?`I{X vDW=,j"E/Zfq_-=4I|GpQ1g/{sX|gx(z[=$S DXI3^-@DB4E^{[ZHc9Io"m E=n-*R_NnȬhLq;]'?^\w[a1hi?g4'n3(QO~ǔ ̀BXfZP&Rts1МUBDFbv> ߂ >18 +]CRّ0-XI2Bn׋B.s(* ȉ̈́iPc7-~zWdŏo>#]Sӿ};{y-hX1}Fv\l`WH>)cAT#{*hX B*lrtCO)Nrnb2 Q򔴙6!8 ,.Oi(-s ;oqҋ?ަC=^~J{pǢGq~*X)82 fXSHb6ͪ5\ mhk~J?>gb> unr(_N_Sd[< 1`v|9{zz懔&t=wu%^dXi w)QtPk 4XokA 2)' pp€G { 0hXo VzAQy2l1)h9q0d͆ʏii: {C8"pz3(N_ 믢g&|/g6'!ə7e7=#7g}wA O6'p}}4':q9wWnOaTi*.WI+Ԗ c`CE"lH%l66å̀6On>f^Q6nCL66E޳gcm oeduD"ua{ ǣxs1Tgf&a݉|!gocHQ@4?^W5sEMT _1P)wYU򦽝P;OU7~/mËlBaoF ) U}+]E>q-/8ϟo}}?8~?3Փ35Qoh/הxJKΎ&n~-m G~kk_5gv |ɚͽy>#̞ڌ㰿e?GzsxƖAP۳Go$W|Uj/zkb~vmu ݯU6ygQ|km~Ja 3oPȟy(ޅr@ؚ-(3Dlc[6T"o+尟 M(h mjem+#A^];B> ۇHvHmv~qm=|=L{`<-%GYo]G\{y.Lɼ ?AAK-=[ۺbIqɤ.FK\Ty]bxRxOnaۧO=Ի W{zzU-U)gڕ w=: O7\,ϸesuk&(Ygcgy("(c ᤍ]I($[msчh$1yoz^ic9~ccM6vB1.m{=Pq9^q.u\Fxgӵ^,턚HHH͸JtX7ҺX J%CLZdj_vaX3Sz\~j+X!qQ͔2:HԆ>7(3Rc*D5R $j))lJQRSRǚbW1ºDGF Lhq:\/:J/*z*iY}xP(#YjB5LZ#1SXIttqPih$KGYU8R :B#e'xR:JJ<-o_AAl≏Ml{(B _|iU A&; ~y&멸cl A~?<ԗQ46D0;=Ɠ k]W5ƽޓj,N;q#Wbyn` jD8˛V,f;+11hgRQI`RLc[qԧY{(giuI;Cmy`z8z3ؘ/2*OyIK> B0AwqnzǾ&;Sv[1vTև"[k<4&Ӱj&3Qm H٣*զu[#9gq+a,:NH&ڙ^g9eu<Ϗ&wUp^q#DO&$/I]qfynk]]Y⫗wx/mw\lMjb|R],.k^ |3HK=S7_\uNzu*wx/}osS;-N oF*.._>|ަpĹ|)nfln3QOb*C,)k_a WVz^"qsMẢ:PˋN:ݞhr󶊭VGCML 7"FB[7B.3< u@(\JcE5[bo##5 yWgŽ /gM L;52u]Wښg=Yle;ֽ5zmb]G2yԙ3pyvwپcͳ udʯciuM,#-ȣH1DW 44Ly "L)1n(WfRfqϿW endstream endobj 65 0 obj 20005 endobj 66 0 obj <> endobj 67 0 obj <> stream x]n@=O2]DĒeɱcɋNEaw=V"|s͐/ٷylO~I/>~̭OI!i׷K,==˸^'̏iۍg)ɾΝ>؝c~4O60f|f]x/0_cuAJ;v>5uoIZqlPZ<|X.kd\ ;\;Yk֔/oy˚+6egMܲZ~U_c_E至װ0 9;dDD?"ﰮ` BC%mKDwد_Z;D}/VO`_JC4?0(jW~J`_Acn((=*(*ݧĺ5~o+G`/.6Gኧ ߁?7m?9]Xؙi{2Fo@ endstream endobj 68 0 obj <> endobj 69 0 obj <> stream x8kTיߝ=@=x#Ì4LN5'hTFrnYСcpO_`Fy _ 񒱉-ICl^m'"LU,Qdd"3KڵSDe7)j::"~}i#8r*^yg= W> k阺xs/Ю_SEg{oR~T"0|?@ eVio08sj a/^ChgZ,.lXNIwV=<ڃ ' Ӛ| 8={~ϿCp>` e&fɄϢ8nhQbYO(be78w,ٽz?fmTqիPy $ӘI_>{* tw t^sGv[iSvKMU.|CYi䚌,]fVVq,C',,qeAB BXz$e1jI/J|BқJBV(,]![^AZ2̕H"V+jA"a'[ނ.Ylf:*`!S!\Z DrYtZ-Eb:*ڥlEfAlRR7K٤Qa幻0ȍ!뛛;"Mll+ȣRjG<$( s#.]Q(R@VL\(΅"AQ0s zܔ3 j-^,0G# ^Z (ehsfPe}8&H2L j]BU2azXe&P+j'߾4 8*$=]mAQj[tF$%\└+6֓udEMm <'v<4 ,\f =M ̚Dr//tƈG;{B4zJ:#B2J(B-u!-g2 2>H@iWh4͸Bcƥi^F/Ro0Ls[\8cFHDl QL1$&Jo4]M O>ķ/0ê 5\ pX^P~92K*J>QsPd5ZMFX.!/=½%M]T='5ۼI@b<Ór#!99rFChYӪal=TQ"="R(L9иԸD\K&7>*?qNF\3w266pb&/7㾻y>}cnj.~?>3;H n{'7dǟ=stC۾c<uotlEx+ԧ}} 򖪞*vtٽd)2~-!w\=Kg z[tdX1r.M>tyrcnR5Fo.]dq55kHb:%L wvݢGj4,jU//zN65u {uneXFi2g_ 7PmvȾ-bقT1tloV 4.Pa_ǨU*0Bcnb`մUш +ljXJ]"ԵCW٧%y|yo5Z۞EX~qR V F9Koרt!ЧQjWg$#195 hrsi} r .+;l6# z 7ɘQv6׺$.vڵ~Uo,[d(\E"n6kXe).Ĭ5ש6=?]t ob4ܜlFYu <]>`*.Pl2o//6:mIWQEgXK,z;LqTWh)gv䓺|˳\RK8KJ m&[[}'Lx SZ PudcE?VbhL5pV'5Fccco\2b в#rH3^\lC{M1[ KE^NܞԸ q /JT8q֚m}-{?j2KH:sw|c|C'"_.կa(V\w`g{7u֘ZFNJryOYy\>w߼V˃GE>U`YX+coQ`^Vk ,(r]3 4*}ع&')p&)p6ؙ|1,M ̷=sfYR`lP`5TX~YP=;:½zQY+p6VOFc0IFa[b(:9SH2t DžX2!LG}a`4ӱdd<6>) %>;:*geeY{ӑDd3B|D8d>9D'GcdtI!v H2:"BϪbHl(* ǓcޙXb86DgK8dt_TI&X29ڿ3s(>C䁩p4cɉ`"JIaWD<GtƟJ 8FOtU#tFxXlhL8"CCѩ$ferv|UI1;Af AIRY!6J&Ge+IvZ2&D,5iƒlT5fGdWHɶi,iqǔ9JR8)ݲw9w!7&cSxsosՖN@ɿT/ ܵJ gPޕ\x#|BH'V+O`1Q9b\KeƕzL*F'MRә+r_gGmLə/5fS]1ub9MOcXv?WѫbkxP{Hx6W\b`*94l`]l#;fOpTl7۵BC'd;vG{m m|m?kMxf\yA1nC!X 7]1 '*H[;5)#G"wJ' ط'@=5?MERuwH vHx)0h!z=} ` Lq勤$R`Dc"M(I]Fɛ7nm$E._6- endstream endobj 70 0 obj 4895 endobj 71 0 obj <> endobj 72 0 obj <> stream x]Pj0+tLA!|胺YZZk࿯i =H,t5S=::d,j (j% A[-7;k7a/Ǹ_l-0>O?XV;&l(#oh㪬K(DpRqWkbէȬ"c ^g|>d  'K'|*?䙷iz:Ϗ+V(0[I&3{*oP{W endstream endobj 73 0 obj <> endobj 74 0 obj <> endobj 75 0 obj <> /ProcSet[/PDF/Text/ImageC/ImageI/ImageB] >> endobj 1 0 obj <>/Contents 2 0 R>> endobj 4 0 obj <>/Contents 5 0 R>> endobj 7 0 obj <>/Contents 8 0 R>> endobj 11 0 obj <>/Contents 12 0 R>> endobj 15 0 obj <>/Contents 16 0 R>> endobj 18 0 obj <>/Contents 19 0 R>> endobj 21 0 obj <>/Contents 22 0 R>> endobj 26 0 obj <>/Contents 27 0 R>> endobj 30 0 obj <>/Contents 31 0 R>> endobj 34 0 obj <>/Contents 35 0 R>> endobj 37 0 obj <>/Contents 38 0 R>> endobj 76 0 obj <> endobj 77 0 obj < /Dest[1 0 R/XYZ 133.2 771.9 0]/Parent 76 0 R/Next 78 0 R>> endobj 78 0 obj < /Dest[1 0 R/XYZ 56.9 733.2 0]/Parent 76 0 R/Prev 77 0 R/Next 79 0 R>> endobj 79 0 obj < /Dest[1 0 R/XYZ 56.7 499.9 0]/Parent 76 0 R/Prev 78 0 R/Next 80 0 R>> endobj 80 0 obj < /Dest[1 0 R/XYZ 56.7 222.4 0]/Parent 76 0 R/Prev 79 0 R/Next 81 0 R>> endobj 81 0 obj < /Dest[4 0 R/XYZ 56.7 785.3 0]/Parent 76 0 R/Prev 80 0 R/Next 82 0 R>> endobj 82 0 obj < /Dest[7 0 R/XYZ 56.7 719.9 0]/Parent 76 0 R/Prev 81 0 R/Next 83 0 R>> endobj 83 0 obj < /Dest[7 0 R/XYZ 56.7 452.7 0]/Parent 76 0 R/Prev 82 0 R/Next 84 0 R>> endobj 84 0 obj < /Dest[7 0 R/XYZ 56.7 130.8 0]/Parent 76 0 R/Prev 83 0 R/Next 85 0 R>> endobj 85 0 obj < /Dest[11 0 R/XYZ 56.7 289.2 0]/Parent 76 0 R/Prev 84 0 R/Next 86 0 R>> endobj 86 0 obj < /Dest[11 0 R/XYZ 56.7 157.1 0]/Parent 76 0 R/Prev 85 0 R/Next 87 0 R>> endobj 87 0 obj < /Dest[21 0 R/XYZ 56.7 652.7 0]/Parent 76 0 R/Prev 86 0 R/Next 88 0 R>> endobj 88 0 obj < /Dest[30 0 R/XYZ 56.7 298.9 0]/Parent 76 0 R/Prev 87 0 R/Next 89 0 R>> endobj 89 0 obj < /Dest[34 0 R/XYZ 56.7 526.1 0]/Parent 76 0 R/Prev 88 0 R/Next 90 0 R>> endobj 90 0 obj < /Dest[37 0 R/XYZ 56.7 698.7 0]/Parent 76 0 R/Prev 89 0 R>> endobj 48 0 obj <> endobj 40 0 obj <> >> endobj 41 0 obj <> >> endobj 42 0 obj <> >> endobj 43 0 obj <> >> endobj 44 0 obj <> >> endobj 45 0 obj <> >> endobj 46 0 obj <> >> endobj 47 0 obj <> >> endobj 91 0 obj <> endobj 92 0 obj < /Creator /Producer /CreationDate(D:20100816204616+05'30')>> endobj xref 0 93 0000000000 65535 f 0000429644 00000 n 0000000019 00000 n 0000002719 00000 n 0000429806 00000 n 0000002740 00000 n 0000005549 00000 n 0000429950 00000 n 0000005570 00000 n 0000007328 00000 n 0000007349 00000 n 0000430112 00000 n 0000074051 00000 n 0000075181 00000 n 0000075203 00000 n 0000430276 00000 n 0000131543 00000 n 0000133721 00000 n 0000430422 00000 n 0000133743 00000 n 0000136350 00000 n 0000430586 00000 n 0000136372 00000 n 0000137160 00000 n 0000175201 00000 n 0000137181 00000 n 0000430757 00000 n 0000214858 00000 n 0000215063 00000 n 0000215084 00000 n 0000430921 00000 n 0000283098 00000 n 0000284379 00000 n 0000284401 00000 n 0000431085 00000 n 0000364509 00000 n 0000367384 00000 n 0000431231 00000 n 0000367406 00000 n 0000368572 00000 n 0000434410 00000 n 0000434567 00000 n 0000434733 00000 n 0000434917 00000 n 0000435095 00000 n 0000435267 00000 n 0000435434 00000 n 0000435607 00000 n 0000434241 00000 n 0000368594 00000 n 0000370037 00000 n 0000370059 00000 n 0000370250 00000 n 0000370541 00000 n 0000370702 00000 n 0000385438 00000 n 0000385461 00000 n 0000385664 00000 n 0000386189 00000 n 0000386570 00000 n 0000400981 00000 n 0000401004 00000 n 0000401200 00000 n 0000401844 00000 n 0000402334 00000 n 0000422426 00000 n 0000422449 00000 n 0000422645 00000 n 0000423240 00000 n 0000423679 00000 n 0000428660 00000 n 0000428682 00000 n 0000428888 00000 n 0000429213 00000 n 0000429410 00000 n 0000429483 00000 n 0000431377 00000 n 0000431434 00000 n 0000431649 00000 n 0000431791 00000 n 0000432017 00000 n 0000432231 00000 n 0000432457 00000 n 0000432703 00000 n 0000432901 00000 n 0000433123 00000 n 0000433278 00000 n 0000433497 00000 n 0000433636 00000 n 0000433867 00000 n 0000434102 00000 n 0000435781 00000 n 0000435896 00000 n trailer < <9FE3AD69152C149520A9AEBA1A961569> ] /DocChecksum /246EB60196FB7912DF3C5B9D9ED1A738 >> startxref 436137 %%EOF ./oar-2.5.2/docs/documentation/OAR-TESTSUITES-DOCUMENTATION.odt0000644000175000017500000011512611757171206021242 0ustar plbplbPKz=^2 ''mimetypeapplication/vnd.oasis.opendocument.textPKz=Configurations2/statusbar/PKz='Configurations2/accelerator/current.xmlPKz=Configurations2/floater/PKz=Configurations2/popupmenu/PKz=Configurations2/progressbar/PKz=Configurations2/menubar/PKz=Configurations2/toolbar/PKz=Configurations2/images/Bitmaps/PKz= layout-cachecd`d(g``d``1x$l SF(0-0ѵȢ`0 PKݷ#:sPKz= content.xml=rH5C gd֎靘U@6qV)))%U i@YYyWf?~zG^wgq \O6>^m|F7p) ˠۓ4xSg׽f%ɭ׸;56{'sҴ3-gfo7gM;c[@}49:> D $IeflDcw||l `'kG\bY'vS N "O?ysh?T Gv8/>uzbյH:`s0Hz`GGΔejr) ͸TغKLV$M$H@09gEFalg@. Mztюe=AXy=ݎ9 BvDC+cQvoAWCLEa"9EI6+Èc W ";NGw_s=R؞NmxvAtŊZ/NڎHnka$Qb-AˣBH+%"~bxd~r u &ǖ? Nзa<aEaT*rU ~6bay'X#/J8,-?vt4O6?'_-ߘ,z$EvoAM#e-_t{|et;:~EEwwt_~]_Qk>k>>:?¸_6hV4͸G3ь{4͸G3ь{4~fh=qfܣh=qfܣ5㬹fe?"y'FI'ăaOx\~11~s_}(GrEzm'HʱSx?4Ndst}zkr& <`鿪X ĩCJ#pϻ//wې"\]$:Z042bT;}궄LMi=zuyuag_~|w 3RXͥ3KÅ᪹0>MSǩ 70'B!ưn ܓ =ۥL |?f{g=~b'̎8)80ww쏬 bye' .-@R3q3^$p[jٟǁf-0r]n?#ЁcүXX>+ \x,u~z'n}K{zr%;",EUTToPUE13i|!=0=]`GLf8n C0 9Y)<ɚzl4Ys\6l1E'30fg3 /@ Ԇq %4p%b5@;. o0 >qJl"`` vhlW@zk|՘A /RT rz}e;S3O&CZZ4HK)O iœ  .e&{@L룢\,^JSy,TguVK_<R,?Ux!,LIf\}<(Ҭ4J2Ecy@"BHDN_ۊc14tQJiL>[cky GA7tbLoℇ1 KCS+$DPetKI=5)v,$LXlmv!2*D! [C/g \41~h~656<m6"dbܮ V)`d\ (<9@okk*k!%AذwF񸑸Tknq !ȵ{;Cm-ٙ Sn mE4Q!G0՜ rːQ X=[)/Pcv2ZL C33kA/~L9Ynx#u#)yى)3tB<KN C9`>3|DZRbQIQmz,R_ `,H.8n*aW I+e ;nScYcv,2V.ka= w˛L:oI|c l5wr[z g.)Pz܎7A2+!Dk-S_tŋ"LVj LF|ƦO+~ZD:s7\Qv9*@`h,|M4-Ϯqn4|*dYz1 AUN&I@3^9X#+ib7(.}(řH%`(Pu~dɎAIљ=,?*6!"R4=B).H\2_UiSF'ybm$ mHL S 04;nAˀ*1rv F]"!4!KTLj*bg!6^O$Xq:mPCv%r&lL\(0PabD&y4 epq߈^0=Ћf"0wlecHD"u٩k@qz a.Oiqں\z9j,D"GYʿ/o_]i A[r"4(ݸ_KlHӀ0@^*""|R5R49UhRm=rP1 g$H=̀{֞ ;a?3yC&WcGNgpIr`Vo5ݼY8sS^߳yP[tq`;X&$ PB2D}Ti[/U U $!6rW6BXΞҙ0x7 PY>J/)4gcjɝK  %> r!|H @旊e9'yo$Z1`׶eQ>! D_~d6c.tdz!HS^^ 횻zP)?5Gem,{ĀeMK45(_Id֕jvE٬ą&Y6l .[/Q[SYhb4Bb@} 9Hn'cRHvs2H1/ҡCRG?W%\e@DDH*fdF4ɨqh.m3o,s|t n/:=qh\Z}GFJ<4!<3J s"mO.XpBZzlpgg,.qZEqigm/&xaG4цM2t

Xp-CRPPP-dLXdᜊhYc䚳9zviDU弸 ^K:s{[K2XMeˤ{ /19jW #'2rT+V=!Eb#.Le{lMx2 Z xe:?X; ܴCFi;rٷ Kj< Ti$s$$atɊ77 {[d)~2xI0>ϳU:!18PA?yc{@&a{p,$O+ 'K%VdxQkKEV5Ţۯa]ꖰ@/'QfJYoAǴLoS?:|toI phgsOY*_@fZՌW]Dw071O$S߽h%*'F늄H$֔fpqHB,o@;2J>9m*<:YBMF$م MWH!&{.A@E^HjSl8@!'@!i /X&j]``?W\m\x+W.t K+kd8ҵM9_ݽP5uocY|nRaw"}ejYcXڔ= }Pq.^wzzb/CnJ+SYŵ.f+S.iz˔^^pZ A5{5koeJY=۪,uB(pe3B{7E%XVTH[ɜM%+2Lg!Weӑ_?d^='ڸqlADAj&C7g1z趫d(._(m Ufht=%"#pDM?byinēuYdvyW'Z;hRB'+ĝ*jXL5CkYC]_vt3R_R`~KetTw[@V9 jY)onnЖ* tk$R8ssT U ٥583ʵY/h}z;nk`~B/E#}^W6t*t͙՛WW n+|34{X:@Ժ  #‚xކUAJvYrvi߀mNL%dR"tZa)EwTiڊ#UD zz-W[Fxʗpy/PeJIK$`tX˹@timRf4BnVʋ:6nI֊Ēs>TcҰ̝O=8-wE{f ̥oAҚ|[ IOt'A'nζi@g쵮.;T!R~Ѳc-M, 5qL·P&K&gp`q|A"'2C.G/P֙Mٷ,>-+ 0Vb]:E} 0CoI'Ϻ:VNj 0B*֛$=n)$}q0;{'a ElUԪbA׆ 3A":υY~ J.X}Nۗ-_[t^`T d}a$'Glss5lIet6%܂.r#:t1̦Z6^vXk=5 ;9EΡx{Z'}3ՂlBzm'ߦ 0TPzYȇ~GM*&4VGGGL_žIyA8/x hau&<eA(݄C[QTDl甩2|%ՠP sFB}~2L=cׂ5Um0FUr9(&m"ֆ1B~7Ь4\bx`lƑkj|p7J#-ࠈHM!DPu\::#C|>oϯ^eN/Nz3;dN/;|TaW..??61_0rw;>PK]tsr#+PKz==<1 manifest.rdfn0EwrfYdhjCP}]ҢUչ:8<{Όl[7?QF=O,-p*Q,Tk!ɲ C"[2b,#V`_A":q4v!Zl2 Kc~|' *S "tr5M }j"'.#*u15teJ]a2zPKz= styles.xml[ݏ۸_aоɶYM6@rW i_DYH@R_3(Qwybp8ͧFy6Q!/`fx̊oW' :QBR3*gkC|*Q9L SVњ.ZeV5[уy;{fɚ ySw{§n>O$8dJb7s.aQWV"\q" ˛SEꇼJEo QĪr4QJd]{{s_O= y;PEiFU`T. ޟe pأɢq|8|C7/=*n!hɅjI'(@'l+Uy6^H[ǃB 1LެΛNk%H!- T {ɹrD#"=\@VQ '9>4pH (ޛlg7   -Wβc8/wjn )dYG&[ZPrSQtXJ"̄h|F/SҌW3= QT<'`/P 4=%9z%GhNM;euiBnZǭ e"ֿR@ Š)Vc-S%U[o"sxA˒Dд)N2d WgwFt yv5,cT60!t$h\| }R)gkrJ2%FP-T`re)XP7 G/t܀1{;Xd\>M%)PU84`JTP9n4hk)-BīB pO4ק Vtn2}(1+r~W6X#K˩D2z$6Ԕe6$C h'Yc+hLiӘҺuy vYD+"R2 ?֕A`xӖf,,lc·gZ/k:>羊^Fm軞9a1i1bR'euC 78OFJ҃}a)-}ŷT<ƧB(DhˈQUCI8XhF-* ~ mx|Rl +ކԶ>Aš1%%d{rO2ny^ 4QH:O̐ '9:䗸-|޺sq[39:/GRϽ" XGc!gfHٔM݀-n5 hqQCRS6@g{CL'hh=坵+U< nj <_ ׅ7ҡ֎|Q?c[0pRp2jv^IŒFm yó`p܄fl{0Q1 ci'Bftmis>Vsfah&Q#]z +?9㙮_AZmvs沽BJӳL4KN=/YN3S`;5 ]AuאC\3y;pzj?-jٛ,8A 33-GX,Ɨz!r֦Mқ5* 54smQey=3>YguNh؝ϱ4Sv1 &u%ErcI)f]D>kif|A5:J0y:1Dwx%D/9%^VlnOP{Oְ:z uM+c*pZEeo%c.HzNll0|;$R֋ 2KmT ]v>`5绔jd4QKu ωHo ` "R:H[ՑycH#@7z0߾1[JhC:wo -!\wo p..%\7t..!\Cn8M;wJ~xc0?Rz@Ցo vԖ$+*7a۪6%+=dHmG,ڍҁ@1 ʳ }kHxLA6 3|ڧ?<!tj!- hۚƊH͟~#18-P`mrv_<z=Mn!<- 5!~`pi4\Lwup]Bc[4gG5gX=~op4'X<$OXHo ujpv^˻asK@½x*uSO 9z%P{PKeR| O5PKz=rcHHmeta.xml Narayanan 2010-08-16T20:11:362010-08-16T20:46:01Narayanan PT00H32M16S10OpenOffice.org/3.2$Linux OpenOffice.org_project/320m12$Build-9483PKz=Thumbnails/thumbnail.pngC0L$ڶm۶m۶m۶m۶mwf=˷ɪ]VITԉĄVomo) Q,!,}d7+tXbml8I.$Lv(-, o2#w%HlCzV4sukG x0_ 3cc6B<|ƒ]}[͞mÿ9G`#u[Dž Kel3$|`+: (GSTˌwWR7st^g}5~G y1TW`8jҥW+|sG墎e}X6Gl,:~'ѷ!f6ko,$l&>+#r-jdV9!&d=AqqTuka"_sT؎3YվsjDpl| bGtWk4M#/BWUkgS@XG#l0K,u8Jt?vX~IdecJg3-^XYժ]Zp5߄&AN_Z!j߰m+ScνVQ\^]]0ZXLoNA9Lmͦ \JSxM[&:7 yzI"` :Xsf~!DDsZRW"ѓe0`F9ѹP GRx i" )>G흝T̊&.VJUJɬ2x*W>{;kIN .|2SZuBbrba:xn&1/ZIb斘/: uSrxÖ:5:mBHBE@[pp(M6Sie$e.qMJ nOJ/$BB2E3Ǥ&@t$tPKZ;` ՊaN|3|%6g¡({xJ6m\ȔI$dEEc Ypuy_c D!3ᡘ\$2p;ds.o戃n ,k {>N첸͆p#U͟[{b/D*}|3O=1jyH *PC9\4R/9Y\SQQ]` W[5XDC22uR6ct?,\w!;+p)h& r ,/O=,o,FAiE Z>BLSc:*SJ`5[=!_anu~c hűA*zߤzeΦթ5q!'^>ݿJp] ;igebD> 85kH/"'`A͡I;S:81*ChuFJ*K,&vf[`x6n9N|xLF2Ø<:6<7gq[k6fvxX~x2RrAGe]etXgBx+J6ǩۤL]z79b#d~{9 ~wpuxD0'Nx nn>sp=0~utb>TP܇x|dku8[v(ا7\:8KgSv>R<iOElؚ_?$8Bz.lq8NYL e*%hR)71' =n_V /P?Rld&X0?p^w }h"a- @b|IhB#(s13CPV& 5!`8ּ8KxOi9< u ̘]q}ykn[E{[B5:n*_Q,omln9A8ŝ@4xx"߲v byܾ2aݯ`EˆH~w}_:Ft;xP~\iP#xT"PMJ,<)~ZX@vNm]p 2x"!L-IܤqWFmt&Er׫.cfx96%I\P^EMW(?DHNϲ7?,e|p.rM{Vre[O>iPn:iRDL=XÇy2."D5uRO>7)))]لe*)-նwLZ;õse'Y >EÚ(X~ޤ:_h|:[_N]h>&[Eh|OXE(AX )^&KMIJ&›v^-:<1:» du;m^p|4f's0ySf(rMBt^~mP˯cZc)qs ϖA"dfrMZ=u\6G#+ YDFir$5۬lr6Bjgi]vMTo+Vv} _7wc~|Z;U,z/~o=]U#=0DK ^b?ѝ:JPdSznqrVۢ[Kvs>U|>"Tu>lso5kw ,[<:N?$8fIڳMbP|5VĦ jPTqZGqc9^bpBe h*Q:} ̽=9ũ&mXŀ J5 \ SE]]滘ۚpJq=RT?Lr +Sh' :+CKÔ Qb.*ҝ{0 )]f5(4`(Y8@V1M 2UPȭ_G6N*!,6C BBƾ0xrؼkE\Y%DzߡME`32*$+e.veLuwTavU~: ѱ]Seu-?Z6IĠ_v\] g+iw,d'|q_bnدh:8}-@a F"eBcD\ct\Yof׫WHm7L?H<.0O+bPAv %nk&w6%Zzʭ'P{} >p"Jy+=}.'a ZZL\̴D&V;||XTM}+ao~x|D5ni"-T^8^Op9W\>Q&Gpx+L?v-R1w0 *\/njɞٵVVxRWj]f 䲔Y1V<g'[.$VN0ŪpSA\ ڔ%HBj͂*h )A|&FI z&'1 T w.P[,h\&'X[FDT!e\JwFV\G"w-@\ Y MӇtYxƤ)^П3ZrGL.vw) ~Ov-]*a!ap+7=}I]o\GNsKEޚr,#! )oOPޅ "_z;+R3FSmфJ-'+|W?EUfW6̪1<.;Qrf~'KoT;/-n댉Vڲ!ӊ5|U͵f O4]•ץ W&67epv視tR2l_9*5v MQM]aC5zaBOGehUXD\w날0MIO+8:HU+@:U# ~މ/_JL-!fփjNO^6?iK+mLO*>o$4HAÕELΐ7^KEM۝4gf Uqm9c0YO\Pu#G5Q cgްW8?vb!%^^& t8󌷻QwI8Z>܃c 1Y?P LCu^v. Tu9M=?^w:\Lcdg& !|)[(ΠFM |_[c{ ,הYP2|`$HɃYGl3YYP"^!,K;5d%2s $D/gXKv ̙ V.9 /C$>A`lOJqQWgAgd~J623<֫cWr(l"sxTDt~򉍂{So[ޑ~l}n{q^4?_8IHZomdN/|Ć*ȂEO1)?8 m (->@;Sa'j1i2r Rj$JRƓe JUJBU# B95"),ThT5҄ >NMګ0ۢLui&== aT],E/.[\- B^=kkݾ5"Ld{ @jP {ӣ0'1@TˆM}"2be"3v2mFEP.GQ@Gb۸K=΍N]ud?𯘓"u0bt^1_d`rn۔[no^_ OkvnR`,i8+kGYk?bD7,-Z^ RID[yK>ƽKslqƼ `!r/9'ٯ 8Xj$Sc g2N߉QĪ'Ԕn=87LăIǨj{;e+کK8QyGȸ<{N?>$pe E}%,.9 I"1!Ɍ?*>~>s:n&CjÑpjֺ&Ew5}[)8l?w!˳-˩hmԈ|'w<)K _=-ueKYc[oFS CApCcZ z74|-'ͼRD)mXu?kT5=sbVj7R3H0K onLTgEB*:J:Rwye{m&W:❩\*K%_'av z°1O>[O'ze$I55< %!wvRν쇗f(So",c}2<׳É̌ǻ6\YɩDtH}WC\*pn2KFyuH-(BeUp 1*b;ZQdyLP J6"A4Mh?4` @Ȑ~b}uOEaQ*.Uc?8\F'8;CyïYt(1D$j5<85ckDE;12t綺/M)dd (4q uŔ+ E"N7@)$܄y| uIRΤZfc[@K33@f|-'nKv<0ǏY%s'`Th(k{  :fU!VUA0=,~&R QU\k>#+ 0FR$}9.w%T^%P}xDXZ $2*"*v:.v"hC B.8 -"c *()kX?~dC ܃*'ppي^o|'2{] }C1z&N*:zj1 hc[a}>̝k$?J)UjDJr/[b]>!4Ix3!˘>BdH0yj$s0kg7K4,'JC4w}Jˎ4iFM&`}>Cp#| K"oѠp·Pm˺$ iWy?U(Px6QqqFĸ1:񂔅CTą!2*RW53Twv-I淪i#Q T'&5 zR%kbg %n<w;2a;GKWus9W} Y>hVU*,ZڪW 2joz]6 0hi7&Z!tsEIWi4~Ҝsz#nm~b 촧邌H=[8gݏؘj_2RG[ǶLLnE|= ZMGw<7wQk@c.Дy'Ϯڵ@ y fbEP@fݾ^*G0 j(3TW ׋ȭQvNv+oi0SQZt C$ɡ e>QD~ F$}q3;P@m6nCrm%y%F%އke%05?YO*!3vLỷDO)vF .#n_]aRaz˶)  )lQh!U$UBt/)ƚuRs>yU0L3&@#S!]x[\^:p1Id~3&m^1 2WjmPSQqpaߥ̉5Z^]zB\C IڰXB3G2DWEM Cr7ZlPNS|QlF˝EXW}=ȼ:%1;)4d GY`~pX2}亗-nM߁rDĀŰ3iSpX^Da ܨ1yjá Qż5v]ZZ Vc⮨ñ`s][fZ)Z@w qa E>`f˳q7^,z%e .0k P u+4/aj?Zy%,27-rsf6ُP)7S6WQ6/RY02 }ϲD$7'/bυ1kZj'ob 9zwSuIݫQ~(RFv7)Eϕ$R!sSIT5g5YsfcԊeЪ5(kS&|YTEM$l@>\ aQ{><Ӣ}OnEMIǃh0ĝ]Vce|dE2hi!Y$&̟.pVw$FwﭖCf)xLؠzO6J>`kG9AoԂ5.F&pf8|7<,"R27{Am9;ŝ|)Zϖ/1mτ ^]l )yS8^ \xqnWs'pru ɩ+/M#UFP<8Oy\癡=+ kr(S3zBI'95|MzA9I6x>O)=\ ?뚘 \mebxaAM E\?—<"_)6.b4W7oV.L]lH 9Јf< 8k0Y)H_^TD^d)DO=Hf8 €&Lppڊ`jAiڏS2HO!?!CJ`n*B[{p;/l.+f A,j;͇c ȼ.wktLN<9ryUNώhC9a!eETl_,UW y5 \WVR O hpY>#?u2D҉-(~/یh|xh˰NR^W <#ps8\n;3b`ݻDS࿸iLLncIs iX9F_az׺<<0#ărJ sc7rd'hvlDZӤy|}uriX슕؀-iK軻-yyM$B(TL9*{@M_06WN__gZ26߁7ѶM>^\I# '15h,}nѻT{KdxBpl[Iآ_>oZogjjq$aba7د ~X9S(FsanޚYȠ/m?O֞8zd131b"baE9\[,,kk~-p,6@4G\ӗ6a-nõ/VQ]D"$wsfk62+_C/d ?k9'&2`=1|(ĘIPfaVd 0phy:x䅯]vfD+*lڙl׎<1j rFPuaPBSg;t2i[=tC`fd!qwZ^!(mvϻk*~ z&qk&QND8%ˎUI8R$7VcP1-z ] v _\]34ӆ?\n,j*X=Q}_UF4Dα^c]ה;dh?V_=+A6)--Fat7V1F)xIxw~mtld_ק$>(ZXU*d fKK/oZg '8c[؎3sd+{;- asiX}^n&RA2$ 1ך`3i?U!CCΎD1v5@8~ sjN0CP<{y;6JLsRr؅~ j.,a}z?S\fI*Mǫ&߿0Si' ;4WEf-IơOYpP+S +@aƏu7Aa\] R+ MBGF^n  !sһ3U7y[%:Ikfz.c[ܒ%|^I3$)-D#3 QBDH:MQsHឨӺمtYJB{LǍU*ZS& SQ@ȁ3Յ  >3euyC+"P nNFTHed0J &N"քN- oV~HvG`Waew>Fk8ڭڥ'DOvb5jXQW"nnnN=w<Mp%QO{S`cZ(*8  тR *y5ւyKSvzW$#|2œVͮ9~IF &rs?U'\(Vf+euK*4%hG٣?tS*xWwpo#c|ۯmHDL (yH Jdpwr@mS%(RN vN~?/!Yh87̈B`;y<$@sr yz#z+|ƫ.At|߯k|?Ȧ9٥/fPKxc"tB0tmE;MS$_^+׽ yK} ϑVJ%D xxepa5E \IH1SVh!+v:DHqzH=^q_7O^xF \ x"TG@-G{vSkF^r4)3%G篢}6U]c`5zիL@̖l :WcPR>P5{ !+:i|SS~Ńa @P ox4&Wm ;'Zm+ujEa?f@/Ɯ z|^`n?LP]$L쁬X/0n祆DA0|_ 3y LVe5>.QD RgX Xd_lDNM\LWgI 7FoY.h(ŹVm-2\~Z[ЀP. 㦽+aZb!8FAsI= Y L*5.KߗJ^|Ϧ@]/8&mۇa/9f|A-1`L9lQqۚˀ$F0F--U.(h9ƺwnO}*/҅i*V, #E3B-;:9r+4HJܖIQ^ 5 nwX;|dw[B6*l2Z}G;s#<n|O~T}" a'<(; i[̳7s{Nϭ>XH V%(_U%->(@H)Ua*Û;74TW;ӬNg(a5r&;˒(䂂l(mkn58l$jd&ŒZPP`s_.P!m2ftJv+ht}X̟*c^)<&}? O;QDt6L"!LJ?Y5'7O@É,Y3.{i<_؉oFw!z LEXj"nv(8IJS)0Ȥgvg]KFvx"C#,P=,5iF/gրXVyc4)Q E(F8G2_@(ӗ0`~SQt%ǽXTڊӁao&W(r=Z+$>iHshn~)G 8QF 2H8E=-|] dGI:KO-(>u.kPV 0!Lf L ~ Zџ\ug45 C̤) Øo*) z/,OP*IDq4#xZօREd $E*X8-//ҟ薫r܅>\sYR/v!L0X~ЇheD>3 e_p0nϳ1"RdT[i~d51CQ_SEYa)TPM He0UgU׍{Mlۮ0^@O&Oͱ173aMi_}T;/&..7"sK_CTƐ(+i11gYLݯ*!2M7:{%QbvlG^Ghɒ/fj~UInA6W2y8~X~/TU`{bR8z6o׬, olTbf]Wx'rjU3 ZARlN aM<>A1a1 V@w.!{7z%-lSKdo咽gՓ{ЃT~w]"]na7}{5SX)V@`yzj'F:23x`p-HHk:# ͫ'%h{ɝMw1E]H2g# Cv7c"[$bncpG֟ף RͫÝS?Oў򩤄 kymN,>@7ǚAIx O' 46ò {yU3vobrGMiq/tBhrFFpI2,cק5萂~gܿ7=`%CK1C-'9~Y ɼ+G3i cRك=ݟ7;>Y/GH"6)*s.C+9ЕCaa5 u~ZPS:iN0-0’:&wU*M&~}\lhtNd\6jSxq(n/N&K(Z݋kЦwpywifYk_8^ ?SVx/H/DhSR̯Ypi[kFƌ;GdW;ZhE*Ub\ |x޹#Dk"c=ko ߆zA_-_d2Q%9U:30E4>28W9z77T_EyGW891Ye Sv.尪WwuxY>Uk5x U/oi&'ԺgK?YI*em)<}5ȪUK*86K䤀`W5\׻ԯUdztUg;=8]Sy.BH(GZ \ ;6 D0T vب'm - x%٥tXo`qLO] M`äĢ6I%+ƈueZg72Dﬧ2]0gr @Wzr #W4$`ܢoC9*QB ɱvUV)(6Hk_3fU[N/}n( w=h3I= sfNY;-UaaeՓ8tJ-Zl\̈́楬L}iiA ;iQx &&W{f7W-ߜ* e2ENp% 1^ `FWQc^O{c۷'.-;f9!ZipqcO*c7wjVEw7CqGKo$i~ڌ8z,L`K[egw47?MʏuIS%J4$ڋn$`fn?@JӦZR^M"egalUo]m‰~V7%xc/:g)ݼ3@7n\xR~&?b\}C- Hʝh9&JvHSI] #-8L~XE? 7qμ7ySBB/E3A8ZTx, 8+w cv\o?$AsZ 6"g {56:A;8Җwmxd v=u1>b ^țr"0ȿ2`;0ze2f]|I͙-0qb*,\iD 'j)ŧQlZ9( cOSV쩑Fܞ]+eDzv+fKEV 39 I+_fZٖ҆:! B\j½ኻn4)Syoc=, rb DC)pmVp7EjR#i9Ԩ{wBY@86cXŭyPbr0P𧗟!G{ rjM_S91q=C_0Am{CL =e;axR#-X+~'鲉p&Fd:S#}}ZrZ7ŖXԑh8f ιW?64EN ZYB^ms=V!m˒wğe 4dB_)=QJTTv}c}?o*(ژ C@cV@R| p{w>ɿ^N)goik>vDV6N Ny &MY% RIeѿ" o)Ϩ  raU ^v˹gVpVٲbPm~1CIV*UHKf1`~u UNqզ}:*5`Dzb!B 0Cq&A;NI5Wh[ Wh…q}">o bhyc`v7]AE156N᝔悇:2FMG't2[Gvy_VOBv|/CG='H~X]"~wnu y/9FP)z^F%3N;pA` J=~#gڼFE9Ma4/PcaK[}OcE!sfޚU| yqXvdm=d$i2 A 4Nz](c'ָr]9,YEFur%=|(S>7mf-(7|):q`|>q0n5pl;ʡgWwCCVa GcFJtlC3b@>;7\pX&`[ɠ}r9 IAԨI>.[ASzDβ"WUq&2^aGgcak]E>퇠ph6+|]y(UT3nL0@;n}qP;_/nOE)`)} Loc$y%JmE/(|ϳ~{t=syw h׉/gZh._`CC]ކ sb.|m|QUhg aBo'lZnbe U ߐI :Y4{JLŌ0-sV;^^82b4O u dMƩq#rj23)QWA.g?,AwT4v_s1@1fрlz뎟) *5|΋Vy ;U5h Όd`^9Ui(s41:?(S5Vy-?J#-dLߝݍ`f1Ir_kSQM'pM }2Y2pл\ݓu<E.xܾ~ Ѓvvi2!ѹ6k[$Dmi_Ҳpzp|r hOCo4yz>5}ƝxQZ!; ɹR\5֔ J>hdYJj O]'z'(Ъ})7+w?ەMY}ijı `AVBΰtY*Ĺ; shQ]#}vfT-*rljf0 M xMPW2 V ϛ l-a_ixtt<鄲ZS16N4_ ũ$J7#.QYOQ[D@jV g3*f7 ͿIܚ{7P]bwR;+SȾUH͒N.@!U,$ڻ娞)Q'k?Ӆ$ߔڼUS4V`$QtSmFzE"`si CZz%j' }љmșUlݱF悾MTͰAFDktoZیyz2VI-Rs'{"+*^ w a~)+1Du>"Xl˅Gm.m&V-$R`6YllvaBKoԜ= 7௡릃ywLe ϖݤKxr`Ńa:)9;.ᔛ#WncEcxJ$YFNVœwM&aQL:=-,}ЯI4^b`>Еc3E$}8~5]fK{]J|j(ýѼ+>s4,X46 x׬ ^bsX=XDU&Nr:k/o8>rpT#X\mϵ9AoO=72[GxHzHZTR 6k.W\–]'c-=ٶDhWWL [Mm)ljϠjV y(L2m7/J#50=UI%dU#zH_YwG(ɱk_+ZyyRY5@ }[4n}v=F}^|"@m>+ztNԊJBXxoc *J̓Bw(\$ s1,eteI -{_bYgbS@EwI cr3a5QZS-X*_̖W34^HOb+y4|a6d1(\L#ˁNV6 KmgP挩j&dnafD HtmgW9>ލjL 5:j'MFWӃLN;W03aً㮱D$l\r d)x's `ٟfr{]{U6O.C 2ɌTX3 BdRZr8EI-bhؚj;Vv͹?/yޮ|x'eAgiȢQ,( ׾.wX?/H%F_Bbw.ژKqs/-4u5}WwVHsvQ*U̐}+Fӂȫ2t}vS/7&7fUDQ-|_a4<Ya髽0}:k[? >{in vɍ(GglIF-7W3cN%ID8@nʷ+7г4D)a=V 5CVij28CJ4㘠;23d(%Gq FԆ;_|YzvϾ P)YƼ-; #TQWb+hrN tsG%46{Gn&QMX<6k!fJͿjԂ̵!וEN\؀p(v2Dž!0~V-rd.XNd4 m;aF&v.k /-ŧ|6Bu;RJ_&, T׫+{gƹ9CDC@j QKѫqQFg*iVPKIlYYPKz= settings.xmlZs8~"w$^$t )-- fo^@H2?[ِKXOҮvk>#q9{~Y.@r9ǝGS;Ny$i*%K^{ dd mk]HeOւŽ77&nTru}jVVӷ)*[V"*äʮjjw=\y͝v7 ?n >GHeca5ؾ_6۽1pifz(t=b_xh^Δl~z19R|A{ &Q ѕ-+ B)d.CXCxP< 6d^k3$'KW K|"DIԩa^~\|=Kt (/ 'b4&I6GuFDr?˒B\Ї~:~zQwŽ~[`{Ū &^EbjS? LQ11n@Hi,Q$^FtwPלA">q(5g~U(C~s2sBb|BfI<(O}9`3y;bKžl .9Z*xG#$W&B*UC L 7=tJ(ŒK\^D SNJA_p Qie`֎|F: AEnMK6^W$JQu|`٭\FOC(z6F9)<5yQ,K:8g ʞ XfFA.3;Õ6TwKL|J wL:^czɒL+ì._-BYLpʎ|)Ѥ)LA~$|32/-=%y"QS[ot[[%Y) ?+MeTv XsԤl[ nlbnL&L6,KUE.V-{/}(AyeU0v]cgwd0*3WRsCoՂ`m;ARGۑh>gؕKP074 cg=Xr"풡dP~NA-~vEf8yw{Tb0hdPTf <^1hkؓpi*x ˀ /ؤaPF!X!1- HkT=4}o3[-g Fv>τӴaћB61$jдVDHZΊp') @'ߙ:)";d5,wQmeWk:G<P yATs3OLκ͈=|'=Z&N֪lRCHBVƕ_Hf|=U4T [1@>)E_L{q\R].nszc@8-~"QO]oPK\!e(PKz=^2 ''mimetypePKz=MConfigurations2/statusbar/PKz='Configurations2/accelerator/current.xmlPKz=Configurations2/floater/PKz=Configurations2/popupmenu/PKz=:Configurations2/progressbar/PKz=tConfigurations2/menubar/PKz=Configurations2/toolbar/PKz=Configurations2/images/Bitmaps/PKz=ݷ#:s layout-cachePKz=]tsr#+ content.xmlPKz==<1 <&manifest.rdfPKz=eR| O5 Y'styles.xmlPKz=rcHH 1meta.xmlPKz=IlYY{5Thumbnails/thumbnail.pngPKz=`a9! Îsettings.xmlPKz=\!e(6META-INF/manifest.xmlPKbޕ./oar-2.5.2/docs/documentation/OAR-DOCUMENTATION-USER.rst0000644000175000017500000003101311757171206020316 0ustar plbplb=============================== OAR Documentation - User Guide =============================== .. include:: doc_header.rst :Dedication: For users. .. include:: doc_abstract.rst **BE CAREFULL : THIS DOCUMENTATION IS FOR OAR >= 2.3.0** PDF version : ``_ .. section-numbering:: .. contents:: Table of Contents ------------------------------------------------------------------------------- .. include:: doc_oar-presentation.rst Description of the different commands ===================================== All user commands are installed on cluster login nodes. So you must connect to one of these computers first. *oarstat* --------- This command prints jobs in execution mode on the terminal. Options :: -j, --job show informations only for the specified job (even if it is finished) -f, --full show full informations -s, --state show only the state of a job (optimized query) -u, --user show informations for this user only -g, --gantt show job informations between two date-times -e, --events show job events -p, --properties show job properties --accounting show accounting informations between two dates --sql restricts display by applying the SQL where clause on the table jobs (ex: "project = 'p1'") -D, --dumper print result in DUMPER format -X, --xml print result in XML format -Y, --yaml print result in YAML format --backward-compatible OAR 1.* version like display -V, --version print OAR version number -h, --help show this help screen Examples :: # oarstat # oarstat -j 42 -f # oarstat --sql "project = 'p1'" # oarstat -s -j 42 *oarnodes* ---------- This command prints informations about cluster resources (state, which jobs on which resources, resource properties, ...). Options :: -a : shows all resources with their properties -r : show only properties of a resource -s : shows only resource states -l : shows only resource list --sql "sql where" : Display resources which matches this sql where clause -D : formats outputs in Perl Dumper -X : formats outputs in XML -Y : formats outputs in YAML Examples :: # oarnodes # oarnodes -s # oarnodes --sql "state = 'Suspected'" *oarsub* -------- The user can submit a job with this command. So, what is a job in our context? A job is defined by needed resources and a script/program to run. So, the user must specify how many resources and what kind of them are needed by his application. Thus, OAR system will give him or not what he wants and will control the execution. When a job is launched, OAR executes user program only on the first reservation node. So this program can access some environment variables to know its environment: :: $OAR_NODEFILE contains the name of a file which lists all reserved nodes for this job $OAR_JOB_ID contains the OAR job identificator $OAR_RESOURCE_PROPERTIES_FILE contains the name of a file which lists all resources and their properties $OAR_JOB_NAME name of the job given by the "-n" option $OAR_PROJECT_NAME job project name Options:: -I, --interactive Request an interactive job. Open a login shell on the first node of the reservation instead of running a script. -C, --connect= Connect to a running job -l, --resource= Set the requested resources for the job. The different parameters are resource properties registered in OAR database, and `walltime' which specifies the duration before the job must be automatically terminated if still running. Walltime format is [hour:mn:sec|hour:mn|hour]. Ex: nodes=4/cpu=1,walltime=2:00:00 -S, --scanscript Batch mode only: asks oarsub to scan the given script for OAR directives (#OAR -l ...) -q, --queue= Set the queue to submit the job to -p, --property="" Add constraints to properties for the job. (format is a WHERE clause from the SQL syntax) -r, --reservation= Request a job start time reservation, instead of a submission. The date format is "YYYY-MM-DD HH:MM:SS". --checkpoint= Enable the checkpointing for the job. A signal is sent DELAY seconds before the walltime on the first processus of the job --signal=<#sig> Specify the signal to use when checkpointing Use signal numbers, default is 12 (SIGUSR2) -t, --type= Specify a specific type (deploy, besteffort, cosystem, checkpoint, timesharing) -d, --directory=

Specify the directory where OAR will launch the command (default is current directory) --project= Specify a name of a project the job belongs to -n, --name= Specify an arbitrary name for the job -a, --anterior= Anterior job that must be terminated to start this new one --notify= Specify a notification method (mail or command to execute). Ex: --notify "mail:name\@domain.com" --notify "exec:/path/to/script args" --resubmit= Resubmit the given job as a new one -k, --use-job-key Activate the job-key mechanism. -i, --import-job-key-from-file= Import the job-key to use from a files instead of generating a new one. --import-job-key-inline= Import the job-key to use inline instead of generating a new one. -e --export-job-key-to-file= Export the job key to a file. Warning: the file will be overwritten if it already exists. (the %jobid% pattern is automatically replaced) -O --stdout= Specify the file that will store the standart output stream of the job. (the %jobid% pattern is automatically replaced) -E --stderr= Specify the file that will store the standart error stream of the job. (the %jobid% pattern is automatically replaced) --hold Set the job state into Hold instead of Waiting, so that it is not scheduled (you must run "oarresume" to turn it into the Waiting state) -D, --dumper Print result in DUMPER format -X, --xml Print result in XML format -Y, --yaml Print result in YAML format -h, --help Print this help message -V, --version Print OAR version number Wanted resources have to be described in a hierarchical manner using the "-l" syntax option. Moreover it is possible to give a specification that must be matched on properties. So the long and complete syntax is of the form:: "{ sql1 }/prop1=1/prop2=3+{sql2}/prop3=2/prop4=1/prop5=1+...,walltime=1:00:00" where: - *sql1* : SQL WHERE clause on the table of resources that filters resource names used in the hierarchical description - *prop1* : first type of resources - *prop2* : second type of resources - *+* : add another resource hierarchy to the previous one - *sql2* : SQL WHERE clause to apply on the second hierarchy request - ... So we want to reserve 3 resources with the same value of the type *prop2* and with the same property *prop1* and these resources must fit *sql1*. To that possible resources we want to add 2 others which fit *sql2* and the hierarchy */prop3=2/prop4=1/prop5=1*. .. figure:: ../schemas/hierarchical_resources.png :width: 17cm :target: ../schemas/hierarchical_resources.png :alt: Hierarchical resource example Example of a resource hierarchy and 2 different oarsub commands `hierarchical_resources.svg <../schemas/hierarchical_resources.svg>`_ Examples :: # oarsub -l /nodes=4 test.sh (the "test.sh" script will be run on 4 entire nodes in the default queue with the default walltime) :: # oarsub --stdout='test12.%jobid%.stdout' --stderr='test12.%jobid%.stderr' -l /nodes=4 test.sh ... OAR_JOB_ID=702 ... (same example than above but here the standard output of "test.sh" will be written in the file "test12.702.stdout" and the standard error in "test12.702.stderr") :: # oarsub -q default -l /nodes=10/cpu=3,walltime=2:15:00 \ -p "switch = 'sw1'" /home/users/toto/prog (the "/home/users/toto/prog" script will be run on 10 nodes with 3 cpus (so a total of 30 cpus) in the default queue with a walltime of 2:15:00. Moreover "-p" option restricts resources only on the switch 'sw1') :: # oarsub -r "2009-04-27 11:00:00" -l /nodes=12/cpu=2 (a reservation will begin at "2009-04-27 11:00:00" on 12 nodes with 2 cpus on each one) :: # oarsub -C 42 (connects to the job 42 on the first node and set all OAR environment variables) :: # oarsub -p "not host like 'nodename.%'" (To exclude a node from the request) :: # oarsub -I (gives a shell on a resource) *oardel* -------- This command is used to delete or checkpoint job(s). They are designed by their identifier. Option :: --sql : delete/checkpoint jobs which respond to the SQL where clause on the table jobs (ex: "project = 'p1'") -c job_id : send checkpoint signal to the job (signal was definedwith "--signal" option in oarsub) Examples :: # oardel 14 42 (delete jobs 14 and 42) :: # oardel -c 42 (send checkpoint signal to the job 42) *oarhold* --------- This command is used to remove a job from the scheduling queue if it is in the "Waiting" state. Moreover if its state is "Running" oarhold_ can suspend the execution and enable other jobs to use its resources. In that way, a **SIGINT** signal is sent to every processes. Options :: --sql : hold jobs which respond to the SQL where clause on the table jobs (ex: "project = 'p1'") -r : Manage not only Waiting jobs but also Running one (can suspend the job) *oarresume* ----------- This command resumes jobs in the states *Hold* or *Suspended* Option :: --sql : resume jobs which respond to the SQL where clause on the table jobs (ex: "project = 'p1'") Desktop computing ================= If you want to compute jobs on nodes without SSH connections then this feature is for you. On the nodes you have to run "oar-agent.pl". This script polls the OAR server via a CGI HTTP script. Usage examples: - if you want to run a program that you know is installed on nodes:: oarsub -t desktop_computing /path/to/program Then /path/to/program is run and the files created in the oar-agent.pl running directory is retrieved where oarsub was launched. - if you want to copy a working environment and then launch the program:: oarsub -t desktop_computing -s . ./script.sh The content of "." is transfred to the node, "./script.sh" is run and everything will go back. Visualisation tools =================== Monika ------ This is a web cgi normally installed on the cluster frontal. This tool connects to the DB, gets relevant information then format data in a html page. Thus you can have a global view of cluster state and where your jobs are running. DrawOARGantt ------------ This is also a web cgi. It creates a Gantt chart which shows job repartition on nodes in the time. It is very useful to see cluster occupation in the past and to know when a job will be launched in the future. .. include:: doc_mechanisms.rst .. include:: FAQ-USER .. include:: CHANGELOG ./oar-2.5.2/docs/documentation/OAR-DOCUMENTATION-API-USER.rst0000644000175000017500000015404611757171206020741 0ustar plbplb=========================================== OAR Documentation - REST API - User's doc =========================================== :Dedication: For users whishing to make programs interfaced with OAR .. include:: doc_abstract.rst **BE CAREFULL : THIS DOCUMENTATION IS FOR OAR >= 2.5.0** PDF version : ``_ .. section-numbering:: .. contents:: Table of Contents ------------------------------------------------------------------------------- Introduction ============ The OAR REST API allows to interact with OAR over http using a REST library. Most of the operations usually done with the oar Unix commands may be done using this API from your favourite language. Concepts ======== Access ------ A simple GET query to the API using wget may look like this:: # Get the list of resources wget -O - http://www.mydomain.org/oarapi/resources.yaml?structure=simple You can also access to the API using a browser. Make it point to http://www.myoarcluster.local/oarapi/index.html and you'll see a very simple HTML interface allowing you to browse the cluster resources, post a job using a form or even create resources if you are a OAR administrator. (of course, replace www.myoarcluster.local by a valid name allowing you to join the http service of the host where the API is installed). But generally, you'll use a REST client or a REST library provided for your favorite language. You'll see examples using a ruby rest library in the next parts of this document. Check your system administrator to know on which URI the OAR API is installed. Authentication -------------- Most of the time, you'll make requests that needs you to be authenticated. The way you are authenticated depends on what your local admistrator configured. There's almost as many possibilities as what Apache (the http server used by this API) may manage. The simplest method is a "Basic authentication" with a login/password. It may be binded to a local directory (for example LDAP). You may also find an "ident" based authentication that guesses automatically your login from a little daemon running on your client host. If the "ident" method is used, your unix login is automatically used. But as only a few hosts may be trusted, you'll probably have to open a tunnel to one of this host. You may use ssh to do this. For example, supposing access.mycluster.fr is a gateway host trusted by the api host:: $ ssh -NL 8080:api.mycluster.fr:80 login@access.mycluster.fr Then, point your REST client to:: # http://localhost:8080 Formats and data structure types -------------------------------- The API currently can serve data into *YAML*, *JSON* or *HTML*. Posted data can also be coded into *YAML*, *JSON* or *x-www-form-urlencoded* (for HTML from posts). You may specify the requested format by 2 ways: * giving an extension to resources: **.yaml**, **.json** or **.html** * setting the **HTTP_ACCEPT** header variable to **text/yaml**, **application/json** or **text/html** For the posted data, you have to correctly set the **HTTP_CONTENT_TYPE** variable to **text/yaml**, **application/json** or **application/x-www-form-urlencoded**. Sometimes, the data structures returned (not the coding format, but the contents: array, hashes, array of hashes,...) may be changed. Currently, we have 2 available data structure types: *simple* and *oar*. The structure is passed through the variable *structure* that you may pass in the url, for example: ?structure=simple * The **simple** data structure tries to be as simple as possible, using simple arrays in place of hashes wherever it is possible * The **oar** data structure serves data in the way oar does with the oarnodes/oarstat export options (-Y, -D, -J,...) Be aware that this data structure is not meant to be maintained since 2.5 release of OAR. The simple data structure is highly recommended. By default, we use the *simple* data structure. Here are some examples, using the ruby restclient (see next section):: # Getting resources infos # in JSON irb(main):004:0> puts get('/resources.json') # in YAML irb(main):005:0> puts get('/resources.yaml') # Same thing irb(main):050:0> puts get('/resources', :accept=>"text/yaml") # Specifying the "oar" data structure irb(main):050:0> puts get('/resources.json?structure=oar') # Specifying the "simple" data structure irb(main):050:0> puts get('/resources.json?structure=simple') Errors and debug ---------------- When the API returns an error, it generally uses a standard HTTP return status (404 NOT FOUND, 406 NOT ACCEPTABLE, ...). But it also returns a body containing a hash like the following:: { "title" : "ERROR 406 - Invalid content type required */*", "message" : "Valid types are text/yaml, application/json or text/html", "code" : "200" } This error body is formated in the requested format. But if this format was not given, it uses JSON by default. To allow you to see the error body, you may find it useful to activate the **debug=1** variable. It will force the API to always return a 200 OK status, even if there's an error so that you can see the body with a simple browser or a rest client without having to manage the errors. For example:: wget -nv -O - "http://localhost:8080/oargridapi/sites/grenoble?debug=1" Here is an example of error catching in ruby:: # Function to get objects from the api # We use the JSON format def get(api,uri) begin return JSON.parse(api[uri].get(:accept => 'application/json')) rescue => e if e.respond_to?('http_code') puts "ERROR #{e.http_code}:\n #{e.response.body}" else puts "Parse error:" puts e.inspect end exit 1 end end Ruby REST client ================ One of the easiest way for testing this API is to use the rest-client ruby module: http://rest-client.heroku.com/rdoc/ It may be used from ruby scripts (http://www.ruby.org/) or interactively. It is available as a rubygem, so to install it, simply install rubygems and do "gem install rest-client". Then, you can run the interactive client which is nothing else than irb with shortcuts. Here is an example irb session:: $ export PATH=$PATH:/var/lib/gems/1.8/bin $ restclient http://localhost/oarapi irb(main):001:0> puts get('/jobs.yaml') --- - api_timestamp: 1246457384 id: 551 name: ~ owner: bzizou queue: besteffort state: Waiting submission: 1245858042 uri: /jobs/551 => nil irb(main):002:0> or, if an http basic auth is required:: restclient http://localhost/api ... Pagination and common rules into output data ============================================ Results served by the API are mainly of 2 kinds: "items" and "collections". A collection is actually an array of items. Some uris serve collections that may have a very big amount of items (for example all the terminated jobs of a cluster). For that reason, collections are often "paginated". It means that the collections are presented into pages that have got meta data to give you informations about offset, numbers, and links to previous/next page. Furthermore, items are often composed of commonly used kind of data, especially 'id' and 'links'. We have tried to normalize this as much as possible, so, here is a description of the common properties of items and collections: Items ----- Items have the following features: :Hash: Items should be hashes (sometimes hash of hashes for the 'oar' data structure, but it is to be avoided) :the 'id' key: In general, when an item may be uniquely identified by an integer, it is given in the "id" key. Note that OAR nodes are identified by the 'network_address' key that is an integer, but this is an exception. :the 'links' array: Items, especially when listed in a collection, often give links to more informations or relative data. The links are listed in the links array. Each element of this array (a link) is composed of at least: a 'rel' key and a 'href' key. The 'rel' key is a string telling what is the relation between the current item and the resource pointed by the link. The 'href' key is a string giving the URI of the link relative to the root of the API. It's possible that other keys will be implemented in the future (for example a 'title' key.) Common values for 'rel' are: 'self', 'parent', 'next', 'previous'. :the 'api_timestamp' value: Each item has a 'api_timestamp' key giving the epoch unix date at which the API constructed the item. This field may be omitted when items are listed inside a collection; then the collection has a global api_timestamp value. This date is given in the timezone provided by the "GET /timezone uri". Collections ----------- Collections have the following features: :the 'items' array: The items array is the purpose of a collection. It lists all the items of the current page of a collection. :the 'total' number: It's an integer giving the total number of items in the collection. If the items array contains less elements than this number, then the collection has been paginated and a 'next' and/or 'previous' link should be provided. :the 'offset' number: It gives the offset at which the paginated list starts. If 0, then, it is the first page. :the 'limit' parameter: This is not in the output, but a parameter common to all paginable uris. If you specify a limit, then it gives the size of the pages. :the 'links' array: For a collection, the links array often gives the uri of the next/previous page. But it also gives the uri of the current page ('self') and may point to more informations relative to this collection. See the links array description from above for items, it is similar for the collection. Examples -------- *An item looks like this (yaml output):* :: api_timestamp: 1286894740 available_upto: 2147483646 besteffort: YES core: 1 cpu: 1 cpuset: 0 deploy: NO desktop_computing: NO expiry_date: 0 finaud_decision: NO id: 1 last_available_upto: 0 last_job_date: 1286885902 links: - href: /resources/nodes/fake1 rel: node - href: /resources/1 rel: self - href: /resources/1/jobs rel: jobs network_address: fake1 next_finaud_decision: NO next_state: UnChanged resource_id: 1 scheduler_priority: 0 state: Alive state_num: 1 suspended_jobs: NO type: default *A collection looks like this (yaml output):* :: api_timestamp: 1286894823 items: - api_timestamp: 1286894823 id: 2 links: - href: /jobs/2 rel: self - href: /jobs/2/resources rel: resources name: ~ owner: kameleon queue: default state: Error submission: 1284034267 - api_timestamp: 1286894823 id: 3 links: - href: /jobs/3 rel: self - href: /jobs/3/resources rel: resources name: ~ owner: kameleon queue: default state: Error submission: 1284034383 links: - href: /jobs.yaml?state=Error&limit=2&offset=0 rel: self - href: /jobs.yaml?state=Error&limit=2&offset=2 rel: next offset: 0 total: 2623 REST requests description ========================= Examples are given in the YAML format because we think that it is the more human readable and so very suitable for this kind of documentation. But you can also use the JSON format for your input/output data. Each resource uri may be postfixed by .yaml, .jso of .html. In this section, we describe every REST resources of the OAR API. The authentication may be: - public: everybody can query this resource - user: only authenticated and valid users can query this resource - oar: only the oar user can query this resource (administration usage) GET /index ---------- :description: Home page for the HTML browsing :formats: html :authentication: public :output: *example*: :: OAR REST API
RESOURCES    JOBS    SUBMISSION   
Welcome on the oar API :note: Header of the HTML resources may be customized into the **/etc/oar/api_html_header.pl** file. GET /version ------------ :description: Gives version informations about OAR and OAR API. Also gives the timezone of the API server. :formats: html , yaml , json :authentication: public :output: *structure*: hash *yaml example*: :: --- api: 0.1.2 api_timestamp: 1245582255 api_timezone: CEST apilib: 0.1.6 oar: 2.4.0 :usage example: :: wget -q -O - http://localhost/oarapi/version.yaml GET /timezone ------------- :description: Gives the timezone of the OAR API server. The api_timestamp given in each query is an UTC timestamp (epoch unix time). This timezone information allows you to re-construct the local time. :formats: html , yaml , json :authentication: public :output: *structure*: hash *yaml example*: :: --- api_timestamp: 1245768107 timezone: CEST :usage example: :: wget -q -O - http://localhost/oarapi/timezone.yaml GET /jobs --------- :description: List jobs (by default only the jobs in queue) :formats: html , yaml , json :authentication: public :parameters: - **state**: comma separated list of states for filtering the jobs. Possible values: Terminated, Running, Error, Waiting, Launching, Hold,... - **array** (integer): to get the jobs belonging to an array - **from** (timestamp): restrict the list to the jobs that are running or not yet started before this date. Using this parameters disables the default behavior of listing only the jobs that are in queue. - **to** (timestamp): restrict the list to the jobs that are running or not yet finished at this date. Using this parameters disables the default behavior of listing only the jobs that are in queue. - **user**: restrict the list to the jobs owned by this username :output: *structure*: collection *yaml example*: :: api_timestamp: 1286895857 items: - api_timestamp: 1286895857 id: 58 links: - href: /jobs/58 rel: self - href: /jobs/58/resources rel: resources name: ~ owner: kameleon queue: default state: Terminated submission: 1284109267 - api_timestamp: 1286895857 id: 59 links: - href: /jobs/59 rel: self - href: /jobs/59/resources rel: resources name: ~ owner: kameleon queue: default state: Terminated submission: 1284109846 links: - href: /jobs.yaml?state=Terminated&limit=2&offset=48 rel: self - href: /jobs.yaml?state=Terminated&limit=2&offset=50 rel: next - href: /jobs.yaml?state=Terminated&limit=2&offset=46 rel: previous offset: 48 total: 206 :note: The "rel: resources" link of a job lists the assigned or reserved resources of a job. :usage example: :: wget -q -O - http://localhost/oarapi/jobs.yaml?state=Terminated,Running&limit=2&offset=48" GET /jobs/details ----------------- :description: Same as /jobs, but with more details and "resources" and "nodes" links developped. :formats: html , yaml , json :authentication: public :parameters: - **state**: comma separated list of states for filtering the jobs. Possible values: Terminated, Running, Error, Waiting, Launching, Hold,... :output: *structure*: collection *yaml example*: :: api_timestamp: 1286896291 items: - api_timestamp: 1286896291 array_id: 0 array_index: 10 command: ~ cpuset_name: kameleon_58 dependencies: [] events: - date: 1284109395 description: '[bipbip 58] Ask to change the job state' event_id: 84 job_id: 58 to_check: NO type: SWITCH_INTO_TERMINATE_STATE exit_code: ~ id: 58 initial_request: ~ launching_directory: ~ links: - href: /jobs/58 rel: self - href: /jobs/58/resources rel: resources message: ~ name: ~ nodes: - api_timestamp: 1286896291 id: 0 links: - href: /resources/nodes/fake2 rel: self network_address: fake2 owner: kameleon project: ~ properties: ~ queue: default reservation: ~ resources: - api_timestamp: 1286896291 id: 5 links: - href: /resources/5 rel: self - href: /resources/5/jobs rel: jobs resource_id: 5 status: assigned resubmit_job_id: 0 scheduled_start: 0 start_time: 0 state: Terminated submission_time: 1284109267 type: ~ types: [] walltime: 7200 wanted_resources: "-l \"{type = 'default'}/network_address=1/core=1,walltime=2:0:0\" " - api_timestamp: 1286896291 array_id: 0 array_index: 1 command: ~ cpuset_name: kameleon_59 dependencies: [] events: - date: 1284109910 description: '[bipbip 59] Ask to change the job state' event_id: 85 job_id: 59 to_check: NO type: SWITCH_INTO_TERMINATE_STATE exit_code: ~ id: 59 initial_request: ~ launching_directory: ~ links: - href: /jobs/59 rel: self - href: /jobs/59/resources rel: resources message: ~ name: ~ nodes: - api_timestamp: 1286896291 id: 0 links: - href: /resources/nodes/fake2 rel: self network_address: fake2 owner: kameleon project: ~ properties: ~ queue: default reservation: ~ resources: - api_timestamp: 1286896291 id: 5 links: - href: /resources/5 rel: self - href: /resources/5/jobs rel: jobs resource_id: 5 status: assigned resubmit_job_id: 0 scheduled_start: 0 start_time: 0 state: Terminated submission_time: 1284109846 type: ~ types: [] walltime: 7200 wanted_resources: "-l \"{type = 'default'}/network_address=1/core=1,walltime=2:0:0\" " links: - href: /jobs/details.yaml?state=Terminated&limit=2&offset=48 rel: self - href: /jobs/details.yaml?state=Terminated&limit=2&offset=50 rel: next - href: /jobs/details.yaml?state=Terminated&limit=2&offset=46 rel: previous offset: 48 total: 206 usage example: :: wget -q -O - http://localhost/oarapi/jobs/details.yaml GET /jobs/table --------------- :description: Same as /jobs but outputs the data of the SQL job table :formats: html , yaml , json :authentication: public :parameters: - **state**: comma separated list of states for filtering the jobs. Possible values: Terminated, Running, Error, Waiting, Launching, Hold,... :output: *structure*: collection *yaml example*: :: --- items: - accounted: NO api_timestamp: 1253017554 array_id: 566 assigned_moldable_job: 566 checkpoint: 0 checkpoint_signal: 12 command: '' exit_code: ~ file_id: ~ info_type: bart:33033 initial_request: oarsub -I job_env: ~ job_group: '' job_id: 566 job_name: ~ job_type: INTERACTIVE job_user: bzizou launching_directory: /home/bzizou/git/oar/git message: FIFO scheduling OK notify: ~ project: default properties: desktop_computing = 'NO' queue_name: default reservation: None resubmit_job_id: 0 scheduler_info: FIFO scheduling OK start_time: 1253017553 state: Launching stderr_file: OAR.%jobid%.stderr stdout_file: OAR.%jobid%.stdout stop_time: 0 submission_time: 1253017551 suspended: NO uri: /jobs/566 - accounted: NO api_timestamp: 1253017554 array_id: 560 assigned_moldable_job: 0 checkpoint: 0 checkpoint_signal: 12 command: /usr/bin/id exit_code: ~ file_id: ~ info_type: 'bart:' initial_request: oarsub --resource=/nodes=2/cpu=1 --use_job_key=1 /usr/bin/id job_env: ~ job_group: '' job_id: 560 job_name: ~ job_type: PASSIVE job_user: bzizou launching_directory: /home/bzizou message: Cannot find enough resources which fit for the job 560 notify: ~ project: default properties: desktop_computing = 'NO' queue_name: default reservation: None resubmit_job_id: 0 scheduler_info: Cannot find enough resources which fit for the job 560 start_time: 0 state: Waiting stderr_file: OAR.%jobid%.stderr stdout_file: OAR.%jobid%.stdout stop_time: 0 submission_time: 1246948570 suspended: NO uri: /jobs/560 links: - href: '/jobs/table.html?state=Terminated&limit=15&offset=0' rel: previous - href: '/jobs/table.html?state=Terminated&limit=15&offset=15' rel: self - href: '/jobs/table.html?state=Terminated&limit=15&offset=30' rel: next offset: 15 total: 41 *note*: Field names may vary from the other job lists because this query results more like a dump of the jobs table. :usage example: :: wget -q -O - http://localhost/oarapi/jobs/table.yaml GET /jobs/ -------------- :description: Get details about the given job :parameters: - **id**: the id of a job :formats: html , yaml , json :authentication: user :output: *structure*: hash *yaml example*: :: --- Job_Id: 547 id: 547 uri: /jobs/547 array_id: 547 array_index: 1 assigned_network_address: - liza-2 assigned_resources: - 6 command: '' cpuset_name: bzizou_547 dependencies: [] events: - date: 1245775464 description: User root requested to frag the job 547 event_id: 1315 job_id: 547 to_check: NO type: FRAG_JOB_REQUEST - date: 1245775464 description: '[sarko] Job [547] from 1245768251 with 7200; current time=1245775463 (Elapsed)' event_id: 1316 job_id: 547 to_check: NO type: WALLTIME - date: 1245775464 description: '[Leon] Send kill signal to oarexec on liza-2 for the job 547' event_id: 1318 job_id: 547 to_check: NO type: SEND_KILL_JOB - date: 1245775469 description: '[bipbip 547] Ask to change the job state' event_id: 1320 job_id: 547 to_check: NO type: SWITCH_INTO_ERROR_STATE exit_code: ~ initial_request: '' jobType: INTERACTIVE job_uid: ~ job_user: bzizou launchingDirectory: /home/bzizou message: FIFO scheduling OK name: ~ owner: bzizou project: default properties: desktop_computing = 'NO' queue: default reservation: None resources_uri: /jobs/547/resources resubmit_job_id: 0 scheduledStart: ~ startTime: 1245768251 state: Error submissionTime: 1245768249 types: [] walltime: 7200 wanted_resources: "-l \"{type = 'default'}/resource_id=1,walltime=2:0:0\" " :usage example: :: wget --user test --password test -q -O - http://localhost/oarapi/jobs/547.yaml GET /jobs//resources ------------------------ :description: Get resources reserved or assigned to a job :parameters: - **id**: the id of a job :formats: html , yaml , json :authentication: public :output: *structure*: hash *yaml example*: :: --- api_timestamp: 1253279408 assigned_nodes: - node: liza-1 node_uri: /resources/nodes/liza-1 assigned_resources: - resource_id: 4 resource_uri: /resources/4 - resource_id: 5 resource_uri: /resources/5 job_id: 622 job_uri: /jobs/622 reserved_resources: [] :usage example: :: wget -q -O - http://localhost/oarapi/jobs/547/resources.yaml POST /jobs//deletions/new ----------------------------- :description: Deletes a job :parameters: - **id**: the id of a job :formats: html , yaml , json :authentication: user :output: *structure*: hash *yaml example*: :: --- api_timestamp: 1253025331 cmd_output: | Deleting the job = 567 ...REGISTERED. The job(s) [ 567 ] will be deleted in a near future. id: 567 status: Delete request registered :usage example: :: irb(main):148:0> puts post('/jobs/567/deletions/new.yaml','') POST /jobs//checkpoints/new ------------------------------- :description: Send the checkpoint signal to a job :parameters: - **id**: the id of a job :formats: html , yaml , json :authentication: user :output: *structure*: hash *yaml example*: :: --- api_timestamp: 1253025555 cmd_output: | Checkpointing the job 568 ...DONE. The job 568 was notified to checkpoint itself. id: 568 status: Checkpoint request registered :usage example: :: irb(main):148:0> puts post('/jobs/568/checkpoints/new.yaml','') POST /jobs//holds/new ------------------------- :description: Asks to hold a waiting job :parameters: - **id**: the id of a job :formats: html , yaml , json :authentication: user :output: *structure*: hash *yaml example*: :: --- api_timestamp: 1253025718 cmd_output: "[560] Hold request was sent to the OAR server.\n" id: 560 status: Hold request registered :usage example: :: irb(main):148:0> puts post('/jobs/560/holds/new.yaml','') POST /jobs//rholds/new -------------------------- :description: Asks to hold a running job :parameters: - **id**: the id of a job :formats: html , yaml , json :authentication: oar :output: *structure*: hash *yaml example*: :: --- api_timestamp: 1253025868 cmd_output: "[569] Hold request was sent to the OAR server.\n" id: 569 status: Hold request registered :usage example: :: irb(main):148:0> puts post('/jobs/560/rholds/new.yaml','') POST /jobs//resumptions/new ------------------------------- :description: Asks to resume a holded job :parameters: - **id**: the id of a job :formats: html , yaml , json :authentication: user :output: *structure*: hash *yaml example*: :: --- api_timestamp: 1253026081 cmd_output: "[569] Resume request was sent to the OAR server.\n" id: 569 status: Resume request registered :usage example: :: irb(main):148:0> puts post('/jobs/560/resumptions/new.yaml','') POST /jobs//signals/ -------------------------------- :description: Asks to resume a holded job :parameters: - **id**: the id of a job - **signal**: the number of a signal (see kill -l) :formats: html , yaml , json :authentication: user :output: *structure*: hash *yaml example*: :: --- api_timestamp: 1253102493 cmd_output: | Signaling the job 574 with 12 signal. DONE. The job 574 was notified to signal itself with 12. id: 574 status: Signal sending request registered :usage example: :: irb(main):148:0> puts post('/jobs/560/signals/12.yaml','') POST /jobs ---------- :description: Creates (submit) a new job :formats: html , yaml , json :authentication: user :input: Only [resource] and [command] are mandatory. Please, refer to the documentation of the *oarsub* command for the resource syntax which correspond to the -l (--resource) option. *structure*: hash with possible arrays (for options that may be passed multiple times) *fields*: - **resource** (*string*): the resources description as required by oar (example: "/nodes=1/cpu=2") - **command** (*string*): a command name or a script that is executed when the job starts - **workdir** (*string*): the path of the directory from where the job will be submited - **param_file** (*string*): the content of a parameters file, for the submission of an array job. For example: {"resource":"/nodes=1, "command":"sleep", "param_file":"60\n90\n30"} - **All other option accepted by the oarsub unix command**: every long option that may be passed to the oarsub command is known as a key of the input hash. If the option is a toggle (no value), you just have to set it to "1" (for example: 'use-job-key' => '1'). Some options may be arrays (for example if you want to specify several 'types' for a job) *yaml example*: :: --- stdout: /tmp/outfile command: /usr/bin/id;echo "OK" resource: /nodes=2/cpu=1 workdir: ~bzizou/tmp type: - besteffort - timesharing use-job-key: 1 :output: *structure*: hash *yaml example*: :: --- api_timestamp: 1332323792 cmd_output: | [ADMISSION RULE] Modify resource description with type constraints OAR_JOB_ID=4 id: 4 links: - href: /oarapi-priv/jobs/4 rel: self *note*: more informations about the submited job may be obtained with a GET on the provided *uri*. :usage example: :: # Submitting a job using ruby rest client irb(main):010:0> require 'json' irb(main):012:0> j={ 'resource' => '/nodes=2/cpu=1', 'command' => '/usr/bin/id' } irb(main):015:0> job=post('/jobs' , j.to_json , :content_type => 'application/json') # Submitting a job with a provided inline script irb(main):024:0> script="#!/bin/bash irb(main):025:0" echo \"Hello world\" irb(main):026:0" whoami irb(main):027:0" sleep 300 irb(main):028:0" " irb(main):029:0> j={ 'resource' => '/nodes=2/cpu=1', 'script' => script , 'workdir' => '~bzizou/tmp'} irb(main):030:0> job=post('/jobs' , j.to_json , :content_type => 'application/json') POST /jobs/ --------------- :description: Updates a job. In fact, as some clients (www browsers) doesn't support the DELETE method, this POST resource has been created mainly to workaround this and provide another way to delete a job. It also provides *checkpoint*, *hold* and *resume* methods, but one should preferably use the /checkpoints, /holds and /resumptions resources. :formats: html , yaml , json :authentication: user :input: *structure*: hash {"action" => "delete"} *yaml example*: :: --- method: delete :output: *structure*: hash *yaml example*: :: --- api_timestamp: 1245944206 cmd_output: | Deleting the job = 554 ...REGISTERED. The job(s) [ 554 ] will be deleted in a near future. id: 554 status: Delete request registered :usage example: :: # Deleting a job in the ruby rest client puts post('/jobs/554.yaml','{"method":"delete"}',:content_type => "application/json") DELETE /jobs/ ----------------- :description: Delete or kill a job. :formats: html , yaml , json :authentication: user :output: *structure*: hash returning the status *yaml example*: :: --- api_timestamp: 1245944206 cmd_output: | Deleting the job = 554 ...REGISTERED. The job(s) [ 554 ] will be deleted in a near future. id: 554 status: Delete request registered :usage example: :: # Deleting a job in the ruby rest client puts delete('/jobs/554.yaml') :note: Not all clients support the DELETE method, especially some www browsers. So, you can do the same thing with a POST of a {"method":"delete"} hash on the /jobs/ resource. GET /jobs/form -------------- :description: HTML form for posting (submiting) new jobs from a browser :formats: html :authentication: user :output: *example*: :: OAR REST API
RESOURCES    JOBS    SUBMISSION   
Job submission
Resources
Name
Properties
Program to run
Types
Reservation dates
Directory
:note: This form may be customized in the **/etc/oar/api_html_postform.pl** file GET /resources -------------- :description: Get the list of resources and their state :formats: html , yaml , json :authentication: public :output: *structure*: hash *fields*: - **items** : list of resources - **links** : links to previous, current and next resources - **offset** : current offset - **total** : resources total *yaml example*: :: --- items: - api_timestamp: 1253201950 jobs_uri: /resources/4/jobs network_address: liza-1 node_uri: /resources/nodes/liza-1 resource_id: 4 state: Alive uri: /resources/4 - api_timestamp: 1253201950 jobs_uri: /resources/5/jobs network_address: liza-1 node_uri: /resources/nodes/liza-1 resource_id: 5 state: Alive uri: /resources/5 - api_timestamp: 1253201950 jobs_uri: /resources/6/jobs network_address: liza-2 node_uri: /resources/nodes/liza-2 resource_id: 6 state: Alive uri: /resources/6 - api_timestamp: 1253201950 jobs_uri: /resources/7/jobs network_address: liza-2 node_uri: /resources/nodes/liza-2 resource_id: 7 state: Alive uri: /resources/7 links: - href: '/resources.yaml?limit=5&offset=2' rel: previous - href: '/resources.yaml?limit=5&offset=7' rel: self - href: '/resources.yaml?limit=5&offset=12' rel: next offset: 2 total: 49 *note*: More details about a resource can be obtained with a GET on the provided *uri*. The list of all the resources of the same node may be obtained with a GET on *node_uri*. The list of running jobs on a resource can be obtained with a GET on the jobs_uri resource. *note*: The following parameters can be passed through the requested URL - limit : limit of resources to be shown per page - offset : the page result offset :usage example: :: wget -q -O - http://localhost/oarapi/resources.yaml GET /resources/full ------------------- :description: Get the list of resources and all the details about them :formats: html , yaml , json :authentication: public :output: *structure*: hash *fields*: - **items** : list of resources - **links** : links to previous, current and next resources - **offset** : current offset - **total** : total of resources *yaml example*: :: --- items: - api_timestamp: 1281967035 available_upto: 0 besteffort: YES core: ~ cpu: 0 cpufreq: ~ cpuset: 0 cputype: ~ deploy: NO desktop_computing: NO expiry_date: 0 finaud_decision: NO jobs_uri: '/resources/1/jobs.html' last_available_upto: 0 last_job_date: 1278588052 memnode: ~ network_address: node1 next_finaud_decision: NO next_state: UnChanged node_uri: '/resources/nodes/node1.html' resource_id: 1 scheduler_priority: 0 state: Suspected state_num: 3 suspended_jobs: NO type: default uri: '/resources/1.html' - api_timestamp: 1281967035 available_upto: 0 besteffort: YES core: ~ cpu: 0 cpufreq: ~ cpuset: 0 cputype: ~ deploy: NO desktop_computing: NO expiry_date: 0 finaud_decision: NO jobs_uri: '/resources/2/jobs.html' last_available_upto: 0 last_job_date: 1278588052 memnode: ~ network_address: node1 next_finaud_decision: NO next_state: UnChanged node_uri: '/resources/nodes/node1.html' resource_id: 2 scheduler_priority: 0 state: Suspected state_num: 3 suspended_jobs: NO type: default uri: '/resources/2.html' - api_timestamp: 1281967035 available_upto: 0 besteffort: YES core: ~ cpu: 1 cpufreq: ~ cpuset: 0 cputype: ~ deploy: NO desktop_computing: NO expiry_date: 0 finaud_decision: NO jobs_uri: '/resources/3/jobs.html' last_available_upto: 0 last_job_date: 1278588052 memnode: ~ network_address: node1 next_finaud_decision: NO next_state: UnChanged node_uri: '/resources/nodes/node1.html' resource_id: 3 scheduler_priority: 0 state: Suspected state_num: 3 suspended_jobs: NO type: default uri: '/resources/3.html' links: - href: '/resources/full.yaml?limit=5&offset=2' rel: previous - href: '/resources/full.yaml?limit=5&offset=7' rel: self - href: '/resources/full.yaml?limit=5&offset=12' rel: next offset: 2 total: 49 :usage example: :: wget -q -O - http://localhost/oarapi/resources/full.yaml *note*: The following parameters can be passed through the requested URL - limit : limit of resources to be shown per page - offset : the page result offset GET /resources/ ------------------- :description: Get details about the resource identified by *id* :formats: html , yaml , json :authentication: public :output: *structure*: 1 element array of hash *yaml example*: :: --- api_timestamp: 1253202322 available_upto: 0 besteffort: YES cluster: 0 cpu: 20 cpuset: 0 deploy: NO desktop_computing: NO expiry_date: 0 finaud_decision: NO jobs_uri: /resources/1/jobs last_available_upto: 0 last_job_date: 1253201845 licence: ~ network_address: bart-1 next_finaud_decision: NO next_state: UnChanged node_uri: /resources/nodes/bart-1 resource_id: 1 scheduler_priority: 0 state: Alive state_num: 1 suspended_jobs: NO test: ~ type: default uri: /resources/1 :usage example: :: wget -q -O - http://localhost/oarapi/resources/1.yaml GET /resources/nodes/ -------------------------------------- :description: Get details about the resources belonging to the node identified by *network_address* :formats: html , yaml , json :authentication: public :output: *structure*: array of hashes *yaml example*: :: --- - api_timestamp: 1253202379 jobs_uri: /resources/4/jobs network_address: liza-1 node_uri: /resources/nodes/liza-1 resource_id: 4 state: Alive uri: /resources/4 - api_timestamp: 1253202379 jobs_uri: /resources/5/jobs network_address: liza-1 node_uri: /resources/nodes/liza-1 resource_id: 5 state: Alive uri: /resources/5 :usage example: :: wget -q -O - http://localhost/oarapi/resources/nodes/liza-1.yaml POST /resources/generate ------------------------ :description: Generates (outputs) a set of resources using oaradmin. The result may then be directly sent to /resources for actual creation. :formats: html , yaml , json :authentication: oar :input: [resources] and [properties] entries are mandatory *structure*: hash describing the resources to generate *fields*: - **resources** (*string*): A string corresponding to the resources definition as it could have been passed to the "oaradmin resources -a" command (see man oaradmin). - **properties** (*hash*): an optional hash defining some common properties for these new resources *yaml example*: :: --- ressources: /nodes=node{2}.test.generate/cpu={2}/core={2} properties: memnode: 1050 cpufreq: 5 :output: *structure*: an array of hashes containing the generated resources that may be pushed to POST /resources for actual creation *yaml example*: :: --- api_timestamp: 1321366378 items: - core: 1 cpu: 1 cpuset: 0 network_address: node1.test.generate - core: 2 cpu: 1 cpuset: 1 network_address: node1.test.generate - core: 3 cpu: 2 cpuset: 2 network_address: node1.test.generate - core: 4 cpu: 2 cpuset: 3 network_address: node1.test.generate - core: 5 cpu: 3 cpuset: 0 network_address: node2.test.generate - core: 6 cpu: 3 cpuset: 1 network_address: node2.test.generate - core: 7 cpu: 4 cpuset: 2 network_address: node2.test.generate - core: 8 cpu: 4 cpuset: 3 network_address: node2.test.generate links: - href: /oarapi-priv/resources/generate.yaml rel: self offset: 0 total: 8 :usage example: :: # Generating new resources with curl curl -i -X POST http://oar:kameleon@localhost/oarapi-priv/resources/generate -H'Content-Type: application/json' -d '{"resources":"/nodes=node{2}.test.generate/cpu={2}/core={2}"}' POST /resources --------------- :description: Creates a new resource or a set of new resources :formats: html , yaml , json :authentication: oar :input: A [hostname] or [network_address] entry is mandatory *structure*: A hash describing the resource to be created. An array of hashes may be given for creating a set of new resources. The result of a /resources/generate query may be directly posted to /resources. *fields*: - **hostname** alias **network_address** (*string*): the network address given to the resource - **properties** (*hash*): an optional hash defining some properties for this new resource *yaml example*: :: --- hostname: test2 properties: besteffort: "NO" cpu: "10" :output: *structure*: hash returning the id of the newly created resource and status (or an array of hashes if a set of resources has been given on the input) *yaml example*: :: --- api_timestamp: 1245946199 id: 32 status: ok uri: /resources/32 warnings: [] :usage example: :: # Adding a new resource with the ruby rest client (oar user only) irb(main):078:0> r={ 'hostname'=>'test2', 'properties'=> { 'besteffort'=>'NO' , 'cpu' => '10' } } irb(main):078:0> puts post('/resources', r.to_json , :content_type => 'application/json') POST /resources//state -------------------------- :description: Change the state :formats: html , yaml , json :authentication: oar :input: A [state] entry is mandatory and must be "Absent", "Alive" or "Dead" *structure*: hash of state *fields*: - **state**: Alive, Absent or Dead *yaml example*: :: --- state: Dead :output: *structure*: *yaml example*: :: --- api_timestamp: 1253283492 id: 34 status: Change state request registered uri: /resources/34 :usage example: :: irb DELETE /resources/ ---------------------- :description: Delete the resource identified by *id* :formats: html , yaml , json :authentication: oar :output: *structure*: hash returning the status *yaml example*: :: --- api_timestamp: 1245946801 status: deleted :usage example: :: # Deleting a resource with the ruby rest client puts delete('/resources/32.yaml') :note: If the resource could not be deleted, returns a 403 and the reason into the message body. DELETE /resources// ------------------------------------ :description: Delete the resource corresponding to *cpuset_id* on node *node*. It is useful when you don't know about the ids, but only the number of cpus on physical nodes. :formats: html , yaml , json :authentication: oar :output: *structure*: hash returning the status *yaml example*: :: --- api_timestamp: 1246459253 status: deleted => nil :usage example: :: # Deleting a resource with the ruby rest client puts delete('/resources/test/0.yaml') :note: If the resource could not be deleted, returns a 403 and the reason into the message body. GET /admission_rules -------------------- :description: Get the list of admission rules :formats: html , yaml , json :authentication: public :output: *structure*: hash *fields*: - **items** : list of admission rules - **links** : links to previous, current and next admission rules - **offset** : current offset - **total** : total of admission rules *yaml example*: :: --- items: - id: 1 links: href: /admission_rules/1 rel: self rule: 'if (not defined($queue_name)) {$queue_name="default";}' - id: 2 links: href: /admission_rules/2 rel: self rule: 'die ("[ADMISSION RULE] root and oar users are not allowed to submit jobs.\n") if ( $user eq "root" or $user eq "oar" );' - id: 3 links: href: /admission_rules/3 rel: self rule: |2 my $admin_group = "admin"; if ($queue_name eq "admin") { my $members; (undef,undef,undef, $members) = getgrnam($admin_group); my %h = map { $_ => 1 } split(/\s+/,$members); if ( $h{$user} ne 1 ) { {die("[ADMISSION RULE] Only member of the group ".$admin_group." can submit jobs in the admin queue\n");} } } links: - href: '/admission_rules.yaml?limit=5&offset=0' rel: previous - href: '/admission_rules.yaml?limit=5&offset=5' rel: self - href: '/admission_rules.yaml?limit=5&offset=10' rel: next offset: 5 total: 5 :usage example: :: wget -q -O - http://localhost/oarapi/admission_rules.yaml *note*: The following parameters can be passed through the requested URL - limit : limit of admission rules to be shown per page - offset : the page result offset GET /admission_rules/ ------------------------- :description: Get details about the admission rule identified by *id* :formats: html , yaml , json :authentication: public :output: *structure*: 1 element array of hash *yaml example*: :: --- - id: 1 links: href: /admission_rules/1 rel: self rule: 'if (not defined($queue_name)) {$queue_name="default";}' :usage example: :: wget -q -O - http://localhost/oarapi/admission_rules/1.yaml DELETE /admission_rule/ --------------------------- :description: Delete the admission rule identified by *id* :formats: html , yaml , json :authentication: oar :output: *structure*: hash returning the status *yaml example*: :: --- id: 32 api_timestamp: 1245946801 status: deleted :usage example: :: # Deleting an admisssion rule with the ruby rest client puts delete('/admission_rules/32.yaml') :note: :note: Not all clients support the DELETE method, especially some www browsers. So, you can do the same thing with a POST of a {"method":"delete"} hash on the /admission_rule/ rule. If the admission rule could not be deleted, returns a 403 and the reason into the message body. GET /config ----------- :description: Get the list of configured variables :formats: html , yaml , json :authentication: oar :output: *structure*: array of hashes *yaml example*: :: --- - id: DB_BASE_NAME links: href: /config/DB_BASE_NAME rel: self value: oar - id: OARSUB_FORCE_JOB_KEY links: href: /config/OARSUB_FORCE_JOB_KEY rel: self value: no - id: SCHEDULER_GANTT_HOLE_MINIMUM_TIME links: href: /config/SCHEDULER_GANTT_HOLE_MINIMUM_TIME rel: self value: 300 - id: SCHEDULER_RESOURCE_ORDER links: href: /config/SCHEDULER_RESOURCE_ORDER rel: self value: 'scheduler_priority ASC, suspended_jobs ASC, network_address DESC, resource_id ASC' - id: SCHEDULER_PRIORITY_HIERARCHY_ORDER links: href: /config/SCHEDULER_PRIORITY_HIERARCHY_ORDER rel: self value: network_address/resource_id - id: OARSUB_NODES_RESOURCES links: href: /config/OARSUB_NODES_RESOURCES rel: self value: network_address - id: SCHEDULER_JOB_SECURITY_TIME links: href: /config/SCHEDULER_JOB_SECURITY_TIME rel: self value: 60 - id: DETACH_JOB_FROM_SERVER links: href: /config/DETACH_JOB_FROM_SERVER rel: self value: 0 - id: LOG_LEVEL links: href: /config/LOG_LEVEL rel: self value: 2 - id: OAREXEC_DEBUG_MODE links: href: /config/OAREXEC_DEBUG_MODE rel: self value: 0 ..... ..... :usage example: :: curl -i -X GET http://login:password@localhost/oarapi-priv/config.yaml GET /config/ ---------------------- :description: Get details about the configuration variable identified by *variable* :formats: html , yaml , json :authentication: oar :output: *structure*: 1 element array of hash *yaml example*: :: --- - id: DB_TYPE links: href: /config/DB_TYPE rel: self value: mysql :usage example: :: curl -i -X GET http://login:password@localhost/oarapi-priv/config/DB_TYPE.yaml POST /config/ ----------------------- :description: Change the value of the configuration variable identified by *variable* :formats: html , yaml , json :authentication: oar :input: A [value] entry is mandatory *structure*: hash describing the new value of the variable *fields*: - **value** (*string*): the value of the given variable *yaml example*: :: --- value: 'state=Finishing,Running,Resuming,Suspended,Launching,toLaunch,Waiting,toAckReservation,Hold,Terminated' :output: *structure*: hash returning the variable and his new value *yaml example*: :: --- API_JOBS_URI_DEFAULT_PARAMS: value: 'state=Finishing,Running,Resuming,Suspended,Launching,toLaunch,Waiting,toAckReservation,Hold,Terminated' :usage example: :: curl -i -X POST http://login:password@localhost/oarapi-priv/config/API_JOBS_URI_DEFAULT_PARAMS.yaml -H'Content-Type: text/yaml' -T config.yaml :note: config.yaml contains the value of the variable. GET /media/ ---------------------- :description: Get a file located on the API host, into the path given by *file_path*. The *file_path* may contain the special character "~" that is expanded to the home directory of the user that is making the request. :formats: application/octet-stream :authentication: user :output: octet-stream :usage example: :: curl -i -H'Content-Type: application/octet-stream' http://kameleon:kameleon@localhost/oarapi-priv/media/~/cigri-3/CHANGELOG :note: returns a 404 if the file does not exist, or a 403 if the file is not readable. Errors in debug mode (with ?debug=1) are formated into yaml. POST /media/ ----------------------- :description: Upload or create a file on the API host, into the path given by *file_path*. The *file_path* may contain the special character "~" that is expanded to the home directory of the user that is making the request. If the path does not exists, the directories are automatically created. If no data is passed, an empty file is created. If binary data is sent as POSTDATA, then it is a file to upload. :formats: application/octet-stream :authentication: user :output: 201 if ok :usage example: :: curl -i -X POST -H'Content-Type: application/octet-stream' --data-binary @/etc/group http://kameleon:kameleon@localhost/oarapi-priv/media/~/testdir/testfile DELETE /media/ ------------------------- :description: Delete the file or directory given by *file_path*. The *file_path* may contain the special character "~" that is expanded to the home directory of the user that is making the request. If the path is a directory, then it is deleted recursively. :formats: application/octet-stream :authentication: user :output: 204 if ok :usage example: :: curl -i -X DELETE -H'Content-Type: application/octet-stream' http://kameleon:kameleon@localhost/oarapi-priv/media/~/testdir Some equivalences with oar command line ======================================= =============================== ====================================== OAR command REST request =============================== ====================================== oarstat GET /jobs.html oarstat -Y GET /jobs/details.yaml?structure=oar oarstat -Y -fj GET /jobs/.yaml oardel DELETE /jobs/.yaml oardel *(alternative way)* POST /jobs/deletions//new.yaml oarnodes -Y GET /resources/full.yaml?structure=oar oarnodes -Y -r1 GET /resources/1.yaml?structure=oar =============================== ====================================== ./oar-2.5.2/docs/documentation/OAR-DOCUMENTATION-API-DEVEL.rst0000644000175000017500000000150611757171206021012 0ustar plbplb==================================================== OAR Documentation - REST API - Developper's guide ==================================================== :Dedication: For OAR developpers whishing to add or modify features of the REST API (not for developpers of third party products that make use of the OAR REST API: read the User's guide) .. include:: doc_abstract.rst **BE CAREFULL : THIS DOCUMENTATION IS FOR OAR >= 2.5.0** PDF version : ``_ .. section-numbering:: .. contents:: Table of Contents ------------------------------------------------------------------------------- Introduction ============ ...To be written... How does it work ================ ...To be written... Sources organization ==================== ...To be written... Rspec tests =========== ...To be written... ./oar-2.5.2/docs/documentation/OAR-DOCUMENTATION-API-ADMIN.rst0000644000175000017500000000375511757171206021013 0ustar plbplb====================================================== OAR Documentation - REST API - Administrator's guide ====================================================== :Dedication: For OAR administrators whishing to set up the REST API .. include:: doc_abstract.rst **BE CAREFULL : THIS DOCUMENTATION IS FOR OAR >= 2.5.0** PDF version : ``_ .. section-numbering:: .. contents:: Table of Contents ------------------------------------------------------------------------------- Introduction ============ The OAR REST API is currently a cgi script being served by an http server (we recommend Apache) that allows the programming of interfaces to OAR using a REST library. Most of the operations usually done with the oar Unix commands may be done using this API from the favourite language of the users. But this API may also be used as an administrator portal as it provides you a convenient way to create resources, edit configuration variables or admission rules. Installation ============ ...To be written... Authentication setup ==================== The API authentication relies on the authentication mechanism of the http server used to serve the CGI script. The API may be configured to use the IDENT protocol for authentication from trusted hosts, like a cluster frontend. In this case, a unix login is automatically used by the API. This only works for hosts that have been correctly configured (for which the security rules are trusted by the admistrator). If IDENT is not used or not trusted, the API can use the basic HTTP authentication. You may also want to set-up https certificates. In summary, the API authentication is based on the http server's configuration. The API uses the **X_REMOTE_IDENT** http header variable, so the administrator has to set up this variable inside the http server configuration. Look at the provided apache sample configuration files (api/apache2.conf of the OAR sources or the installed /etc/oar/apache-api.conf of packages) for more details. ./oar-2.5.2/docs/documentation/OAR-DOCUMENTATION-ADMIN.rst0000644000175000017500000013737211757171206020407 0ustar plbplb================================ OAR Documentation - Admin Guide ================================ .. include:: doc_header.rst :Dedication: For administrators. .. include:: doc_abstract.rst **BE CAREFULL : THIS DOCUMENTATION IS FOR OAR >= 2.3.0** PDF version : ``_ .. section-numbering:: .. contents:: Table of Contents ------------------------------------------------------------------------------- .. include:: doc_oar-presentation.rst .. include:: INSTALL .. include:: doc_security.rst Administrator commands ====================== *oarproperty* ------------- This command manages OAR resource properties stored in the database. Options are: :: -l : list properties -a NAME : add a property -c : sql new field of type VARCHAR(255) (default is integer) -d NAME : delete a property -r "OLD_NAME,NEW_NAME" : rename property OLD_NAME into NEW_NAME Examples: :: # oarproperty -a cpu_freq # oarproperty -a type # oarproperty -r "cpu_freq,freq" *oarnodesetting* ---------------- This command permits to change the state or a property of a node or of several resources resources. By default the node name used by `oarnodesetting`_ is the result of the command *hostname*. Options are: :: -a : add a new resource -s : state to assign to the node: * "Alive" : a job can be run on the node. * "Absent" : administrator wants to remove the node from the pool for a moment. * "Dead" : the node will not be used and will be deleted. -h : specify the node name (override hostname). -r : specify the resource number --sql : get resource identifiers which respond to the SQL where clause on the table jobs (ex: "type = 'default'") -p : change the value of a property specified resources. -n : specify this option if you do not want to wait the end of jobs running on this node when you change its state into "Absent" or "Dead". .. include:: oaradmin.rst *oarremoveresource* ------------------- This command permits to remove a resource from the database. The node must be in the state "Dead" (use `oarnodesetting`_ to do this) and then you can use this command to delete it. *oaraccounting* --------------- This command permits to update the `accounting`_ table for jobs ended since the last launch. Option "--reinitialize" removes everything in the `accounting`_ table and switches the "accounted" field of the table `jobs`_ into "NO". So when you will launch the oaraccounting_ command again, it will take the whole jobs. Option "--delete_before" removes records from the `accounting`_ table that are older than the amount of time specified. So if the table becomes too big you can shrink old data; for example:: oaraccounting --delete_before 2678400 (Remove everything older than 31 days) *oarnotify* ----------- This command sends commands to the `Almighty`_ module and manages scheduling queues. Option are: :: Almighty_tag send this tag to the Almighty (default is TERM) -e active an existing queue -d inactive an existing queue -E active all queues -D inactive all queues --add_queue add a new queue; syntax is name,priority,scheduler (ex: "name,3,"oar_sched_gantt_with_timesharing" --remove_queue remove an existing queue -l list all queues and there status -h show this help screen -v print OAR version number *oarmonitor* ------------ This command collects monitoring data from compute nodes and stores them into the database. The TAKTUK_CMD_ is mandatory in the *oar.conf* and data comes from the sensor file OARMONITOR_SENSOR_FILE_ (parse */proc* filesystem for example) and print it in the right way. For example, the user "oar" or "root" can run the following command on the server: oarmonitor -j 4242 -f 10 (Retrieve data from compute nodes of the job 4242 every 10 seconds and store them into database tables monitoring_*) For now, there is just a very minimalist command for the user to view these data. It creates PNG images and a movie... oarmonitor_graph_gen.pl -j 4242 Then the user can look into the directory *OAR.1653.monitoring* in the current directory. Database scheme =============== .. figure:: ../schemas/db_scheme.png :width: 17cm :target: ../schemas/db_scheme.svg :alt: Database scheme Database scheme (red lines seem PRIMARY KEY, blue lines seem INDEX) Note : all dates and duration are stored in an integer manner (number of seconds since the EPOCH). *accounting* ------------ ================== ==================== ======================================= Fields Types Descriptions ================== ==================== ======================================= window_start INT UNSIGNED start date of the accounting interval window_stop INT UNSIGNED stop date of the accounting interval accounting_user VARCHAR(20) user name accounting_project VARCHAR(255) name of the related project queue_name VARCHAR(100) queue name consumption_type ENUM("ASKED", "ASKED" corresponds to the walltimes "USED") specified by the user. "USED" corresponds to the effective time used by the user. consumption INT UNSIGNED number of seconds used ================== ==================== ======================================= :Primary key: window_start, window_stop, accounting_user, queue_name, accounting_project, consumption_type :Index fields: window_start, window_stop, accounting_user, queue_name, accounting_project, consumption_type This table is a summary of the consumption for each user on each queue. This increases the speed of queries about user consumptions and statistic generation. Data are inserted through the command `oaraccounting`_ (when a job is treated the field *accounted* in table jobs is passed into "YES"). So it is possible to regenerate this table completely in this way : - Delete all data of the table: :: DELETE FROM accounting; - Set the field *accounted* in the table jobs to "NO" for each row: :: UPDATE jobs SET accounted = "NO"; - Run the `oaraccounting`_ command. You can change the amount of time for each window : edit the oar configuration file and change the value of the tag ACCOUNTING_WINDOW_. *admission_rules* ----------------- ================ ==================== ======================================= Fields Types Descriptions ================ ==================== ======================================= id INT UNSIGNED id number rule TEXT rule written in Perl applied when a job is going to be registered ================ ==================== ======================================= :Primary key: id :Index fields: *None* You can use these rules to change some values of some properties when a job is submitted. So each admission rule is executed in the order of the id field and it can set several variables. If one of them exits then the others will not be evaluated and oarsub returns an error. Some examples are better than a long description : - Specify the default value for queue parameter :: INSERT INTO admission_rules (rule) VALUES(' if (not defined($queue_name)) { $queue_name="default"; } '); - Avoid users except oar to go in the admin queue :: INSERT INTO admission_rules (rule) VALUES (' if (($queue_name eq "admin") && ($user ne "oar")) { die("[ADMISSION RULE] Only oar user can submit jobs in the admin queue\\n"); } '); - Restrict the maximum of the walltime for interactive jobs :: INSERT INTO admission_rules (rule) VALUES (' my $max_walltime = OAR::IO::sql_to_duration("12:00:00"); if ($jobType eq "INTERACTIVE"){ foreach my $mold (@{$ref_resource_list}){ if ( (defined($mold->[1])) and ($max_walltime < $mold->[1]) ){ print("[ADMISSION RULE] Walltime to big for an INTERACTIVE job so it is set to $max_walltime.\\n"); $mold->[1] = $max_walltime; } } } '); - Specify the default walltime :: INSERT INTO admission_rules (rule) VALUES (' my $default_wall = OAR::IO::sql_to_duration("2:00:00"); foreach my $mold (@{$ref_resource_list}){ if (!defined($mold->[1])){ print("[ADMISSION RULE] Set default walltime to $default_wall.\\n"); $mold->[1] = $default_wall; } } '); - How to perform actions if the user name is in a file :: INSERT INTO admission_rules (rule) VALUES (' open(FILE, "/tmp/users.txt"); while (($queue_name ne "admin") and ($_ = )){ if ($_ =~ m/^\\s*$user\\s*$/m){ print("[ADMISSION RULE] Change assigned queue into admin\\n"); $queue_name = "admin"; } } close(FILE); '); *event_logs* ------------ ================ ==================== ======================================= Fields Types Descriptions ================ ==================== ======================================= event_id INT UNSIGNED event identifier type VARCHAR(50) event type job_id INT UNSIGNED job related of the event date INT UNSIGNED event date description VARCHAR(255) textual description of the event to_check ENUM('YES', 'NO') specify if the module *NodeChangeState* must check this event to Suspect or not some nodes ================ ==================== ======================================= :Primary key: event_id :Index fields: type, to_check The different event types are: - "PING_CHECKER_NODE_SUSPECTED" : the system detected via the module "finaud" that a node is not responding. - "PROLOGUE_ERROR" : an error occurred during the execution of the job prologue (exit code != 0). - "EPILOGUE_ERROR" : an error occurred during the execution of the job epilogue (exit code != 0). - "CANNOT_CREATE_TMP_DIRECTORY" : OAR cannot create the directory where all information files will be stored. - "CAN_NOT_WRITE_NODE_FILE" : the system was not able to write file which had to contain the node list on the first node (*/tmp/OAR_job_id*). - "CAN_NOT_WRITE_PID_FILE" : the system was not able to write the file which had to contain the pid of oarexec process on the first node (*/tmp/pid_of_oarexec_for_job_id*). - "USER_SHELL" : the system was not able to get informations about the user shell on the first node. - "EXIT_VALUE_OAREXEC" : the oarexec process terminated with an unknown exit code. - "SEND_KILL_JOB" : signal that OAR has transmitted a kill signal to the oarexec of the specified job. - "LEON_KILL_BIPBIP_TIMEOUT" : Leon module has detected that something wrong occurred during the kill of a job and so kill the local *bipbip* process. - "EXTERMINATE_JOB" : Leon module has detected that something wrong occurred during the kill of a job and so clean the database and terminate the job artificially. - "WORKING_DIRECTORY" : the directory from which the job was submitted does not exist on the node assigned by the system. - "OUTPUT_FILES" : OAR cannot write the output files (stdout and stderr) in the working directory. - "CANNOT_NOTIFY_OARSUB" : OAR cannot notify the `oarsub` process for an interactive job (maybe the user has killed this process). - "WALLTIME" : the job has reached its walltime. - "SCHEDULER_REDUCE_NB_NODES_FOR_RESERVATION" : this means that there is not enough nodes for the reservation and so the scheduler do the best and gives less nodes than the user wanted (this occurres when nodes become Suspected or Absent). - "BESTEFFORT_KILL" : the job is of the type *besteffort* and was killed because a normal job wanted the nodes. - "FRAG_JOB_REQUEST" : someone wants to delete a job. - "CHECKPOINT" : the checkpoint signal was sent to the job. - "CHECKPOINT_ERROR" : OAR cannot send the signal to the job. - "CHECKPOINT_SUCCESS" : system has sent the signal correctly. - "SERVER_EPILOGUE_TIMEOUT" : epilogue server script has time outed. - "SERVER_EPILOGUE_EXIT_CODE_ERROR" : epilogue server script did not return 0. - "SERVER_EPILOGUE_ERROR" : cannot find epilogue server script file. - "SERVER_PROLOGUE_TIMEOUT" : prologue server script has time outed. - "SERVER_PROLOGUE_EXIT_CODE_ERROR" : prologue server script did not return 0. - "SERVER_PROLOGUE_ERROR" : cannot find prologue server script file. - "CPUSET_CLEAN_ERROR" : OAR cannot clean correctly cpuset files for a job on the remote node. - "MAIL_NOTIFICATION_ERROR" : a mail cannot be sent. - "USER_MAIL_NOTIFICATION" : user mail notification cannot be performed. - "USER_EXEC_NOTIFICATION_ERROR" : user script execution notification cannot be performed. - "BIPBIP_BAD_JOBID" : error when retrieving informations about a running job. - "BIPBIP_CHALLENGE" : OAR is configured to detach jobs when they are launched on compute nodes and the job return a bad challenge number. - "RESUBMIT_JOB_AUTOMATICALLY" : the job was automatically resubmitted. - "WALLTIME" : the job reached its walltime. - "REDUCE_RESERVATION_WALLTIME" : the reservation job was shrunk. - "SSH_TRANSFER_TIMEOUT" : node OAR part script was too long to transfer. - "BAD_HASHTABLE_DUMP" : OAR transfered a bad hashtable. - "LAUNCHING_OAREXEC_TIMEOUT" : oarexec was too long to initialize itself. - "RESERVATION_NO_NODE" : All nodes were detected as bad for the reservation job. *event_log_hostnames* --------------------- ================ ==================== ======================================= Fields Types Descriptions ================ ==================== ======================================= event_id INT UNSIGNED event identifier hostname VARCHAR(255) name of the node where the event has occured ================ ==================== ======================================= :Primary key: event_id :Index fields: hostname This table stores hostnames related to events like "PING_CHECKER_NODE_SUSPECTED". *files* ------- ================ ==================== ======================================= Fields Types Descriptions ================ ==================== ======================================= idFile INT UNSIGNED md5sum VARCHAR(255) location VARCHAR(255) method VARCHAR(255) compression VARCHAR(255) size INT UNSIGNED ================ ==================== ======================================= :Primary key: idFile :Index fields: md5sum *frag_jobs* ----------- ================ ========================== ================================= Fields Types Descriptions ================ ========================== ================================= frag_id_job INT UNSIGNED job id frag_date INT UNSIGNED kill job decision date frag_state ENUM('LEON', 'TIMER_ARMED' state to tell Leon what to do , 'LEON_EXTERMINATE', 'FRAGGED') DEFAULT 'LEON' ================ ========================== ================================= :Primary key: frag_id_job :Index fields: frag_state What do these states mean: - "LEON" : the Leon module must try to kill the job and change the state into "TIMER_ARMED". - "TIMER_ARMED" : the Sarko module must wait a response from the job during a timeout (default is 60s) - "LEON_EXTERMINATE" : the Sarko module has decided that the job time outed and asked Leon to clean up the database. - "FRAGGED" : job is fragged. *gantt_jobs_resources* ---------------------- ================ ==================== ======================================= Fields Types Descriptions ================ ==================== ======================================= moldable_job_id INT UNSIGNED moldable job id resource_id INT UNSIGNED resource assigned to the job ================ ==================== ======================================= :Primary key: moldable_job_id, resource_id :Index fields: *None* This table specifies which resources are attributed to which jobs. *gantt_jobs_resources_visu* --------------------------- ================ ==================== ======================================= Fields Types Descriptions ================ ==================== ======================================= moldable_job_id INT UNSIGNED moldable job id resource_id INT UNSIGNED resource assigned to the job ================ ==================== ======================================= :Primary key: moldable_job_id, resource_id :Index fields: *None* This table is the same as `gantt_jobs_resources`_ and is used by visualisation tools. It is updated atomically (a lock is used). *gantt_jobs_predictions* ------------------------ ================ ==================== ======================================= Fields Types Descriptions ================ ==================== ======================================= moldable_job_id INT UNSIGNED job id start_time INT UNSIGNED date when the job is scheduled to start ================ ==================== ======================================= :Primary key: moldable_job_id :Index fields: *None* With this table and `gantt_jobs_resources`_ you can know exactly what are the decisions taken by the schedulers for each waiting jobs. :note: The special job id "0" is used to store the scheduling reference date. *gantt_jobs_predictions_visu* ----------------------------- ================ ==================== ======================================= Fields Types Descriptions ================ ==================== ======================================= moldable_job_id INT UNSIGNED job id start_time INT UNSIGNED date when the job is scheduled to start ================ ==================== ======================================= :Primary key: job_id :Index fields: *None* This table is the same as `gantt_jobs_predictions`_ and is used by visualisation tools. It is made up to date in an atomic action (with a lock). *jobs* ------ ===================== ====================== ======================================= Fields Types Descriptions ===================== ====================== ======================================= job_id INT UNSIGNED job identifier job_name VARCHAR(100) name given by the user cpuset_name VARCHAR(255) name of the cpuset directory used for this job on each nodes job_type ENUM('INTERACTIVE', specify if the user wants to launch a 'PASSIVE') DEFAULT program or get an interactive shell 'PASSIVE' info_type VARCHAR(255) some informations about `oarsub` command state ENUM('Waiting','Hold', job state 'toLaunch', 'toError', 'toAckReservation', 'Launching', 'Running' 'Suspended', 'Resuming', , 'Finishing', 'Terminated', 'Error') reservation ENUM('None', specify if the job is a reservation 'toSchedule', and the state of this one 'Scheduled') DEFAULT 'None' message VARCHAR(255) readable information message for the user job_user VARCHAR(255) user name command TEXT program to run queue_name VARCHAR(100) queue name properties TEXT properties that assigned nodes must match launching_directory TEXT path of the directory where to launch the user process submission_time INT UNSIGNED date when the job was submitted start_time INT UNSIGNED date when the job was launched stop_time INT UNSIGNED date when the job was stopped file_id INT UNSIGNED accounted ENUM("YES", "NO") specify if the job was considered by DEFAULT "NO" the accounting mechanism or not notify VARCHAR(255) gives the way to notify the user about the job (mail or script ) assigned_moldable_job INT UNSIGNED moldable job chosen by the scheduler checkpoint INT UNSIGNED number of seconds before the walltime to send the checkpoint signal to the job checkpoint_signal INT UNSIGNED signal to use when checkpointing the job stdout_file TEXT file name where to redirect program STDOUT stderr_file TEXT file name where to redirect program STDERR resubmit_job_id INT UNSIGNED if a job is resubmitted then the new one store the previous project VARCHAR(255) arbitrary name given by the user or an admission rule suspended ENUM("YES","NO") specify if the job was suspended (oarhold) job_env TEXT environment variables to set for the job exit_code INT DEFAULT 0 exit code for passive jobs job_group VARCHAR(255) not used ===================== ====================== ======================================= :Primary key: job_id :Index fields: state, reservation, queue_name, accounted, suspended Explications about the "state" field: - "Waiting" : the job is waiting OAR scheduler decision. - "Hold" : user or administrator wants to hold the job (`oarhold` command). So it will not be scheduled by the system. - "toLaunch" : the OAR scheduler has attributed some nodes to the job. So it will be launched. - "toError" : something wrong occurred and the job is going into the error state. - "toAckReservation" : the OAR scheduler must say "YES" or "NO" to the waiting `oarsub` command because it requested a reservation. - "Launching" : OAR has launched the job and will execute the user command on the first node. - "Running" : the user command is executing on the first node. - "Suspended" : the job was in Running state and there was a request (`oarhold` with "-r" option) to suspend this job. In this state other jobs can be scheduled on the same resources (these resources has the "suspended_jobs" field to "YES"). - "Finishing" : the user command has terminated and OAR is doing work internally - "Terminated" : the job has terminated normally. - "Error" : a problem has occurred. Explications about the "reservation" field: - "None" : the job is not a reservation. - "toSchedule" : the job is a reservation and must be approved by the scheduler. - "Scheduled" : the job is a reservation and is scheduled by OAR. *job_dependencies* ------------------ ================ ==================== ======================================= Fields Types Descriptions ================ ==================== ======================================= job_id INT UNSIGNED job identifier job_id_required INT UNSIGNED job needed to be completed before launching job_id ================ ==================== ======================================= :Primary key: job_id, job_id_required :Index fields: job_id, job_id_required This table is feeded by `oarsub` command with the "-a" option. *moldable_job_descriptions* --------------------------- ================= ==================== ======================================= Fields Types Descriptions ================= ==================== ======================================= moldable_id INT UNSIGNED moldable job identifier moldable_job_id INT UNSIGNED corresponding job identifier moldable_walltime INT UNSIGNED instance duration ================= ==================== ======================================= :Primary key: moldable_id :Index fields: moldable_job_id A job can be described with several instances. Thus OAR scheduler can choose one of them. For example it can calculate which instance will finish first. So this table stores all instances for all jobs. *job_resource_groups* --------------------- ===================== ==================== ======================================= Fields Types Descriptions ===================== ==================== ======================================= res_group_id INT UNSIGNED group identifier res_group_moldable_id INT UNSIGNED corresponding moldable job identifier res_group_property TEXT SQL constraint properties ===================== ==================== ======================================= :Primary key: res_group_id :Index fields: res_group_moldable_id As you can specify job global properties with `oarsub` and the "-p" option, you can do the same thing for each resource groups that you define with the "-l" option. *job_resource_descriptions* --------------------------- ===================== ==================== ======================================= Fields Types Descriptions ===================== ==================== ======================================= res_job_group_id INT UNSIGNED corresponding group identifier res_job_resource_type VARCHAR(255) resource type (name of a field in resources) res_job_value INT wanted resource number res_job_order INT UNSIGNED order of the request ===================== ==================== ======================================= :Primary key: res_job_group_id, res_job_resource_type, res_job_order :Index fields: res_job_group_id This table store the hierarchical resource description given with `oarsub` and the "-l" option. *job_state_logs* ---------------- ================= ==================== ======================================= Fields Types Descriptions ================= ==================== ======================================= job_state_log_id INT UNSIGNED identifier job_id INT UNSIGNED corresponding job identifier job_state ENUM('Waiting', job state during the interval 'Hold', 'toLaunch', 'toError', 'toAckReservation', 'Launching', 'Finishing', 'Running', 'Suspended', 'Resuming', 'Terminated', 'Error') date_start INT UNSIGNED start date of the interval date_stop INT UNSIGNED end date of the interval ================= ==================== ======================================= :Primary key: job_state_log_id :Index fields: job_id, job_state This table keeps informations about state changes of jobs. *job_types* ----------- ================ ==================== ======================================= Fields Types Descriptions ================ ==================== ======================================= job_type_id INT UNSIGNED identifier job_id INT UNSIGNED corresponding job identifier type VARCHAR(255) job type like "deploy", "timesharing", ... type_index ENUM('CURRENT', index field 'LOG') ================ ==================== ======================================= :Primary key: job_type_id :Index fields: job_id, type This table stores job types given with the `oarsub` command and "-t" options. *resources* ----------- ==================== ==================== ======================================= Fields Types Descriptions ==================== ==================== ======================================= resource_id INT UNSIGNED resource identifier type VARCHAR(100) resource type (used for licence DEFAULT "default" resources for example) network_address VARCHAR(100) node name (used to connect via SSH) state ENUM('Alive', 'Dead' resource state , 'Suspected', 'Absent') next_state ENUM('UnChanged', state for the resource to switch 'Alive', 'Dead', 'Absent', 'Suspected') DEFAULT 'UnChanged' finaud_decision ENUM('YES', 'NO') tell if the actual state results in a DEFAULT 'NO' "finaud" module decision next_finaud_decision ENUM('YES', 'NO') tell if the next node state results in DEFAULT 'NO' a "finaud" module decision state_num INT corresponding state number (useful with the SQL "ORDER" query) suspended_jobs ENUM('YES','NO') specify if there is at least one suspended job on the resource scheduler_priority INT UNSIGNED arbitrary number given by the system to select resources with more intelligence switch VARCHAR(50) name of the switch cpu INT UNSIGNED global cluster cpu number cpuset INT UNSIGNED field used with the JOB_RESOURCE_MANAGER_PROPERTY_DB_FIELD_ besteffort ENUM('YES','NO') accept or not besteffort jobs deploy ENUM('YES','NO') specify if the resource is deployable expiry_date INT UNSIGNED field used for the desktop computing feature desktop_computing ENUM('YES','NO') tell if it is a desktop computing resource (with an agent) last_job_date INT UNSIGNED store the date when the resource was used for the last time available_upto INT UNSIGNED used with compute mode features to know if an Absent resource can be switch on ==================== ==================== ======================================= :Primary key: resource_id :Index fields: state, next_state, type, suspended_jobs State explications: - "Alive" : the resource is ready to accept a job. - "Absent" : the oar administrator has decided to pull out the resource. This computer can come back. - "Suspected" : OAR system has detected a problem on this resource and so has suspected it (you can look in the `event_logs`_ table to know what has happened). This computer can come back (automatically if this is a "finaud" module decision). - "Dead" : The oar administrator considers that the resource will not come back and will be removed from the pool. This table permits to specify different properties for each resources. These can be used with the `oarsub` command ("-p" and "-l" options). You can add your own properties with `oarproperty`_ command. These properties can be updated with the `oarnodesetting`_ command ("-p" option). Several properties are added by default: - switch : you have to register the name of the switch where the node is plugged. - cpu : this is a unique name given to each cpus. This enables OAR scheduler to distinguish all cpus. - cpuset : this is the name of the cpu on the node. The Linux kernel sets this to an integer beginning at 0. This field is linked to the configuration tag JOB_RESOURCE_MANAGER_PROPERTY_DB_FIELD_. *resource_logs* --------------- ================= ==================== ======================================= Fields Types Descriptions ================= ==================== ======================================= resource_log_id INT UNSIGNED unique id resource_id INT UNSIGNED resource identifier attribute VARCHAR(255) name of corresponding field in resources value VARCHAR(255) value of the field date_start INT UNSIGNED interval start date date_stop INT UNSIGNED interval stop date finaud_decision ENUM('YES','NO') store if this is a system change or a human one ================= ==================== ======================================= :Primary key: *None* :Index fields: resource_id, attribute This table permits to keep a trace of every property changes (consequence of the `oarnodesetting`_ command with the "-p" option). *assigned_resources* -------------------- ================ ==================== ======================================= Fields Types Descriptions ================ ==================== ======================================= moldable_job_id INT UNSIGNED job id resource_id INT UNSIGNED resource assigned to the job ================ ==================== ======================================= :Primary key: moldable_job_id, resource_id :Index fields: moldable_job_id This table keeps informations for jobs on which resources they were scheduled. *queues* -------- ================ ==================== ======================================= Fields Types Descriptions ================ ==================== ======================================= queue_name VARCHAR(100) queue name priority INT UNSIGNED the scheduling priority scheduler_policy VARCHAR(100) path of the associated scheduler state ENUM('Active', permits to stop the scheduling for a 'notActive') queue DEFAULT 'Active' ================ ==================== ======================================= :Primary key: queue_name :Index fields: *None* This table contains the schedulers executed by the *oar_meta_scheduler* module. Executables are launched one after one in the specified priority. *challenges* ------------ ================ ==================== ======================================= Fields Types Descriptions ================ ==================== ======================================= job_id INT UNSIGNED job identifier challenge VARCHAR(255) challenge string ssh_private_key TEXT DEFAULT NULL ssh private key given by the user (in grid usage it enables to connect onto all nodes of the job of all clusers with oarsh) ssh_public_key TEXT DEFAULT NULL ssh public key ================ ==================== ======================================= :Primary key: job_id :Index fields: *None* This table is used to share a secret between OAR server and oarexec process on computing nodes (avoid a job id being stolen/forged by malicious user). For security reasons, this table **must not be readable** for a database account given to users who want to access OAR internal informations(like statistics). Configuration file ================== Be careful, the syntax of this file must be bash compliant(so after editing you must be able to launch in bash 'source /etc/oar.conf' and have variables assigned). Each configuration tag found in /etc/oar.conf is now described: - Database type : you can use a MySQL or a PostgreSQL database (tags are "mysql" or "Pg"):: DB_TYPE=mysql - Database hostname:: DB_HOSTNAME=localhost - Database port:: DB_PORT=3306 - Database base name:: DB_BASE_NAME=oar - DataBase user name:: DB_BASE_LOGIN=oar - DataBase user password:: DB_BASE_PASSWD=oar .. _DB_BASE_LOGIN_RO: - DataBase read only user name:: DB_BASE_LOGIN_RO=oar_ro .. _DB_BASE_PASSWD_RO: - DataBase read only user password:: DB_BASE_PASSWD_RO=oar_ro - OAR server hostname:: SERVER_HOSTNAME=localhost .. _SERVER_PORT: - OAR server port:: SERVER_PORT=6666 - When the user does not specify a -l option then oar use this:: OARSUB_DEFAULT_RESOURCES="/resource_id=1" - Force use of job key even if --use-job-key or -k is not set in oarsub:: OARSUB_FORCE_JOB_KEY="no" .. _DEPLOY_HOSTNAME: - Specify where we are connected in the deploy queue(the node to connect to when the job is in the deploy queue):: DEPLOY_HOSTNAME="127.0.0.1" .. _COSYSTEM_HOSTNAME: - Specify where we are connected with a job of the cosystem type:: COSYSTEM_HOSTNAME="127.0.0.1" .. _DETACH_JOB_FROM_SERVER: - Set DETACH_JOB_FROM_SERVER to 1 if you do not want to keep a ssh connection between the node and the server. Otherwise set this tag to 0:: DETACH_JOB_FROM_SERVER=1 - Set the directory where OAR will store its temporary files on each nodes of the cluster. This value MUST be the same in all oar.conf on all nodes:: OAR_RUNTIME_DIRECTORY="/tmp/oar_runtime" - Specify the database field to use to fill the file on the first node of the job in $OAR_NODE_FILE (default is 'network_address'). Only resources with type=default are displayed in this file:: NODE_FILE_DB_FIELD="network_address" - Specify the database field that will be considered to fill the node file used by the user on the first node of the job. for each different value of this field then OAR will put 1 line in the node file(by default "cpu"):: NODE_FILE_DB_FIELD_DISTINCT_VALUES="core" - By default OAR uses the ping command to detect if nodes are down or not. To enhance this diagnostic you can specify one of these other methods ( give the complete command path): * OAR taktuk:: PINGCHECKER_TAKTUK_ARG_COMMAND="-t 3 broadcast exec [ true ]" If you use sentinelle.pl then you must use this tag:: PINGCHECKER_SENTINELLE_SCRIPT_COMMAND="/var/lib/oar/sentinelle.pl -t 5 -w 20" * OAR fping:: PINGCHECKER_FPING_COMMAND="/usr/bin/fping -q" * OAR nmap : it will test to connect on the ssh port (22):: PINGCHECKER_NMAP_COMMAND="/usr/bin/nmap -p 22 -n -T5" * OAR generic : a specific script may be used instead of ping to check aliveness of nodes. The script must return bad nodes on STDERR (1 line for a bad node and it must have exactly the same name that OAR has given in argument of the command):: PINGCHECKER_GENERIC_COMMAND="/path/to/command arg1 arg2" - OAR log level: 3(debug+warnings+errors), 2(warnings+errors), 1(errors):: LOG_LEVEL=2 - OAR log file:: LOG_FILE="/var/log/oar.log" - If you want to debug oarexec on nodes then affect 1 (only effective if DETACH_JOB_FROM_SERVER = 1):: OAREXEC_DEBUG_MODE=0 .. _ACCOUNTING_WINDOW: - Set the granularity of the OAR accounting feature (in seconds). Default is 1 day (86400s):: ACCOUNTING_WINDOW="86400" .. _MAIL: - OAR informations may be notified by email to the administror. Set accordingly to your configuration the next lines to activate this feature:: MAIL_SMTP_SERVER="smtp.serveur.com" MAIL_RECIPIENT="user@domain.com" MAIL_SENDER="oar@domain.com" - Set the timeout for the prologue and epilogue execution on computing nodes:: PROLOGUE_EPILOGUE_TIMEOUT=60 - Files to execute before and after each job on the first computing node (by default nothing is executed):: PROLOGUE_EXEC_FILE="/path/to/prog" EPILOGUE_EXEC_FILE="/path/to/prog" - Set the timeout for the prologue and epilogue execution on the OAR server:: SERVER_PROLOGUE_EPILOGUE_TIMEOUT=60 .. _SERVER_SCRIPT_EXEC_FILE: - Files to execute before and after each job on the OAR server (by default nothing is executed):: SERVER_PROLOGUE_EXEC_FILE="/path/to/prog" SERVER_EPILOGUE_EXEC_FILE="/path/to/prog" - Set the frequency for checking Alive and Suspected resources:: FINAUD_FREQUENCY=300 .. _DEAD_SWITCH_TIME: - Set time after which resources become Dead (default is 0 and it means never):: DEAD_SWITCH_TIME=600 .. _SCHEDULER_TIMEOUT: - Maximum of seconds used by a scheduler:: SCHEDULER_TIMEOUT=10 - Time to wait when a reservation has not got all resources that it has reserved (some resources could have become Suspected or Absent since the job submission) before to launch the job in the remaining resources:: RESERVATION_WAITING_RESOURCES_TIMEOUT=300 .. _SCHEDULER_JOB_SECURITY_TIME: - Time to add between each jobs (time for administration tasks or time to let computers to reboot):: SCHEDULER_JOB_SECURITY_TIME=1 .. _SCHEDULER_GANTT_HOLE_MINIMUM_TIME: - Minimum time in seconds that can be considered like a hole where a job could be scheduled in:: SCHEDULER_GANTT_HOLE_MINIMUM_TIME=300 .. _SCHEDULER_RESOURCE_ORDER: - You can add an order preference on resource assigned by the system(SQL ORDER syntax):: SCHEDULER_RESOURCE_ORDER="switch ASC, network_address DESC, resource_id ASC" .. _SCHEDULER_RESOURCES_ALWAYS_ASSIGNED_TYPE: - You can specify resources from a resource type that will be always assigned for each job (for example: enable all jobs to be able to log on the cluster frontales). For more information, see the FAQ:: SCHEDULER_RESOURCES_ALWAYS_ASSIGNED_TYPE="42 54 12 34" - This says to the scheduler to treate resources of these types, where there is a suspended job, like free ones. So some other jobs can be scheduled on these resources. (list resource types separate with spaces; Default value is nothing so no other job can be scheduled on suspended job resources):: SCHEDULER_AVAILABLE_SUSPENDED_RESOURCE_TYPE="default licence vlan" - Name of the perl script that manages suspend/resume. You have to install your script in $OARDIR and give only the name of the file without the entire path. (default is suspend_resume_manager.pl):: SUSPEND_RESUME_FILE="suspend_resume_manager.pl" .. _JUST_AFTER_SUSPEND_EXEC_FILE: .. _JUST_BEFORE_RESUME_EXEC_FILE: - Files to execute just after a job was suspended and just before a job was resumed:: JUST_AFTER_SUSPEND_EXEC_FILE="/path/to/prog" JUST_BEFORE_RESUME_EXEC_FILE="/path/to/prog" - Timeout for the two previous scripts:: SUSPEND_RESUME_SCRIPT_TIMEOUT=60 .. _JOB_RESOURCE_MANAGER_PROPERTY_DB_FIELD: - Indicate the name of the database field that contains the cpu number of the node. If this option is set then users must use oarsh instead of ssh to walk on each nodes that they have reserved via oarsub. :: JOB_RESOURCE_MANAGER_PROPERTY_DB_FIELD=cpuset .. _JOB_RESOURCE_MANAGER_FILE: - Name of the perl script that manages cpuset. You have to install your script in $OARDIR and give only the name of the file without the entire path. (default is cpuset_manager.pl which handles the linux kernel cpuset) :: JOB_RESOURCE_MANAGER_FILE="cpuset_manager.pl" .. _JOB_RESOURCE_MANAGER_JOB_UID_TYPE: - Resource "type" DB field to use if you want to enable the job uid feature. (create a unique user id per job on each nodes of the job) :: JOB_RESOURCE_MANAGER_JOB_UID_TYPE="userid" .. _TAKTUK_CMD: - If you have installed taktuk and want to use it to manage cpusets then give the full command path (with your options except "-m" and "-o" and "-c"). You don't also have to give any taktuk command.(taktuk version must be >= 3.6) :: TAKTUK_CMD="/usr/bin/taktuk -s" - If you want to manage nodes to be started and stoped. OAR gives you this API: .. _SCHEDULER_NODE_MANAGER_WAKE_UP_CMD: * When OAR scheduler wants some nodes to wake up then it launches this command and puts on its STDIN the list of nodes to wake up (one hostname by line).The scheduler looks at *available_upto* field in the resources_ table to know if the node will be started for enough time:: SCHEDULER_NODE_MANAGER_WAKE_UP_CMD="/path/to/the/command with your args" .. _SCHEDULER_NODE_MANAGER_SLEEP_CMD: * When OAR considers that some nodes can be shut down, it launches this command and puts the node list on its STDIN(one hostname by line):: SCHEDULER_NODE_MANAGER_SLEEP_CMD="/path/to/the/command args" .. _SCHEDULER_NODE_MANAGER_IDLE_TIME: + Parameters for the scheduler to decide when a node is idle(number of seconds since the last job was terminated on the nodes):: SCHEDULER_NODE_MANAGER_IDLE_TIME=600 .. _SCHEDULER_NODE_MANAGER_SLEEP_TIME: + Parameters for the scheduler to decide if a node will have enough time to sleep(number of seconds before the next job):: SCHEDULER_NODE_MANAGER_SLEEP_TIME=600 .. _OPENSSH_CMD: - Command to use to connect to other nodes (default is "ssh" in the PATH) :: OPENSSH_CMD="/usr/bin/ssh" - These are configuration tags for OAR in the desktop-computing mode:: DESKTOP_COMPUTING_ALLOW_CREATE_NODE=0 DESKTOP_COMPUTING_EXPIRY=10 STAGEOUT_DIR="/var/lib/oar/stageouts/" STAGEIN_DIR="/var/lib/oar/stageins" STAGEIN_CACHE_EXPIRY=144 - This variable must be set to enable the use of oarsh from a frontale node. Otherwise you must not set this variable if you are not on a frontale:: OARSH_OARSTAT_CMD="/usr/bin/oarstat" .. _OARSH_OPENSSH_DEFAULT_OPTIONS: - The following variable adds options to ssh. If one option is not handled by your ssh version just remove it BUT be careful because these options are there for security reasons:: OARSH_OPENSSH_DEFAULT_OPTIONS="-oProxyCommand=none -oPermitLocalCommand=no" .. _OARMONITOR_SENSOR_FILE: - Name of the perl script the retrive monitoring data from compute nodes. This is used in oarmonitor command. OARMONITOR_SENSOR_FILE="/etc/oar/oarmonitor_sensor.pl" .. include:: doc_modules.rst .. include:: doc_internal_mechanisms.rst .. include:: FAQ-ADMIN .. include:: CHANGELOG ./oar-2.5.2/docs/documentation/Makefile0000644000175000017500000000262011757171206016022 0ustar plbplb#!/usr/bin/make #Under Debian or Ubuntu you must install the package "python-docutils tetex-latex-recommended texlive-fonts-recommended make" RST2LATEX := $(shell if which "rst2newlatex" > /dev/null 2>/dev/null; then echo "rst2newlatex"; else echo "rst2latex"; fi) RST2HTML := rst2html PDFLATEX := pdflatex all: doc-admin doc-user doc-usecases doc-api doc-devel doc-admin: OAR-DOCUMENTATION-ADMIN.html doc-user: OAR-DOCUMENTATION-USER.html doc-devel: oar-documentation-devel.html doc-usecases: doc_usecases.html doc-api: OAR-DOCUMENTATION-API-USER.html OAR-DOCUMENTATION-API-ADMIN.html OAR-DOCUMENTATION-API-DEVEL.html latex: OAR-DOCUMENTATION-ADMIN.tex OAR-DOCUMENTATION-USER.tex OAR-DOCUMENTATION-API-USER.tex OAR-DOCUMENTATION-API-ADMIN.tex OAR-DOCUMENTATION-API-DEVEL.tex oar-documentation-devel.tex pdf-api: OAR-DOCUMENTATION-API-USER.pdf OAR-DOCUMENTATION-API-ADMIN.pdf OAR-DOCUMENTATION-API-DEVEL.pdf oar-documentation-devel.pdf pdf: OAR-DOCUMENTATION-ADMIN.pdf OAR-DOCUMENTATION-USER.pdf pdf-api %.html: %.rst $(RST2HTML) -stg $< $@ %.tex: %.rst $(RST2LATEX) --no-section-numbering $< $@ %.pdf: %.tex $(PDFLATEX) $< $@ $(PDFLATEX) $< $@ clean: for i in OAR-DOCUMENTATION-ADMIN OAR-DOCUMENTATION-USER OAR-DOCUMENTATION-API-USER OAR-DOCUMENTATION-API-ADMIN OAR-DOCUMENTATION-API-DEVEL doc_usecases oar-documentation-devel; do rm -f $$i.pdf $$i.html $$i.tex $$i.aux $$i.log $$i.out $$i.toc; done ./oar-2.5.2/docs/documentation/FAQ-ADMIN0000644000175000017500000002431311757171206015545 0ustar plbplbFAQ - ADMIN =========== Release policy -------------- Since the version 2.2, release numbers are divided into 3 parts: - The first represents the design and the implementation used. - The second represents a set of OAR functionalities. - The third is incremented after bug fixes. What means the error "Bad configuration option: PermitLocalCommand" when I am using oarsh? ------------------------------------------------------------------------------------------ For security reasons, on the latest OpenSSH releases you are able to execute a local command when you are connecting to the remote host and we must deactivate this option because the oarsh wrapper executes the *ssh* command into the user oar. So if you encounter this error message it means that your OpenSSH does not know this option and you have to remove it from the oar.conf. There is a variable named OARSH_OPENSSH_DEFAULT_OPTIONS_ in oar.conf used by oarsh. So you have just to remove the not yet implemented option. How to manage start/stop of the nodes? -------------------------------------- You have to add a script in /etc/init.d which switches resources of the node into the "Alive" or "Absent" state. So when this script is called at boot time, it will change the state into "Alive". And when it is called at halt time, it will change into "Absent". There two ways to perform this action: 1. Install OAR "oar-libs" part on all nodes. Thus you will be able to launch the command oarnodesetting_ (be careful to right configure "oar.conf" with database login and password AND to allow network connections on this database). So you can execute:: oarnodesetting -s Alive -h node_hostname or oarnodesetting -s Absent -h node_hostname 2. You do not want to install anything else on each node. So you have to enable oar user to connect to the server via ssh (for security you can use another SSH key with restrictions on the command that oar can launch with this one). Thus you will have in you init script something like:: sudo -u oar ssh oar-server "oarnodesetting -s Alive -h node_hostname" or sudo -u oar ssh oar-server "oarnodesetting -s Absent -h node_hostname" In this case, further OAR software upgrade will be more painless. How can I manage scheduling queues? ----------------------------------- see oarnotify_. How can I handle licence tokens? -------------------------------- OAR does not manage resources with an empty "network_address". So you can define resources that are not linked with a real node. So the steps to configure OAR with the possibility to reserve licences (or whatever you want that are other notions): 1. Add a new field in the table resources_ to specify the licence name. :: oarproperty -a licence -c 2. Add your licence name resources with oarnodesetting_. :: oarnodesetting -a -h "" -p type=mathlab -p licence=l1 oarnodesetting -a -h "" -p type=mathlab -p licence=l2 oarnodesetting -a -h "" -p type=fluent -p licence=l1 ... After this configuration, users can perform submissions like :: oarsub -I -l "/switch=2/nodes=10+{type = 'mathlab'}/licence=20" So users ask OAR to give them some other resource types but nothing block their program to take more licences than they asked. You can resolve this problem with the SERVER_SCRIPT_EXEC_FILE_ configuration. In these files you have to bind OAR allocated resources to the licence servers to restrict user consumptions to what they asked. This is very dependant of the licence management. How can I handle multiple clusters with one OAR? ------------------------------------------------ These are the steps to follow: 1. create a resource property to identify the corresponding cluster (like "cluster"):: oarproperty -a cluster (you can see this new property when you use oarnodes) 2. with oarnodesetting_ you have to fill this field for all resources; for example:: oarnodesetting -h node42.cluster1.com -p cluster=1 oarnodesetting -h node43.cluster1.com -p cluster=1 oarnodesetting -h node2.cluster2.com -p cluster=2 ... 3. Then you have to restrict properties for new job type. So an admission rule performs this job (this is a SQL syntax to use in a database interpreter):: INSERT IGNORE INTO admission_rules (rule) VALUES (' my $cluster_constraint = 0; if (grep(/^cluster1$/, @{$type_list})){ $cluster_constraint = 1; }elsif (grep(/^cluster2$/, @{$type_list})){ $cluster_constraint = 2; } if ($cluster_constraint > 0){ if ($jobproperties ne ""){ $jobproperties = "($jobproperties) AND cluster = $cluster_constraint"; }else{ $jobproperties = "cluster = $cluster_constraint"; } print("[ADMISSION RULE] Added automatically cluster resource constraint\\n"); } '); 4. Edit the admission rule which checks the right job types and add "cluster1" and "cluster2" in. So when you will use oarsub to submit a "cluster2" job type only resources with the property "cluster=2" is used. This is the same when you will use the "cluster1" type. How to configure a more ecological cluster (or how to make some power consumption economies)? --------------------------------------------------------------------------------------------- This feature can be performed with the `Dynamic nodes coupling features`. First you have to make sure that you have a command to wake up a computer that is stopped. For example you can use the WoL (Wake on Lan) feature (generally you have to right configure the BIOS and add right options to the Linux Ethernet driver; see "ethtool"). If you want to enable a node to be woke up the next 12 hours:: ((DATE=$(date +%s)+3600*12)) oarnodesetting -h host_name -p cm_availability=$DATE Otherwise you can disable the wake up of nodes (but not the halt) by:: oarnodesetting -h host_name -p cm_availability=1 If you want to disable the halt on a node (but not the wakeup):: oarnodesetting -h host_name -p cm_availability=2147483647 2147483647 = 2^31 - 1 : we take this value as infinite and it is used to disable the halt mechanism. And if you want to disable the halt and the wakeup:: oarnodesetting -h host_name -p cm_availability=0 Note: In the unstable 2.4 OAR version, cm_availability has been renamed into available_upto. Your `SCHEDULER_NODE_MANAGER_WAKE_UP_CMD`_ must be a script that read node names and translate them into the right wake up command. So with the right OAR and node configurations you can optimize the power consumption of your cluster (and your air conditioning infrastructure) without drawback for the users. Take a look at your cluster occupation and your electricity bill to know if it could be interesting for you ;-) How to configure temporary UID for each job? -------------------------------------------- For a better way to handle job processes we introduce the temporary user id feature. This feature creates a user for each job on assigned nodes. Hence it is possible to clean temporary files, IPC, every generated processes, ... Furthermore a lot of system features could be used like bandwidth management (iptables rules on the user id). To configure this feature, CPUSET must be activated and the tag JOB_RESOURCE_MANAGER_JOB_UID_TYPE has to be configured in the oar.conf file. The value is the content of the "type" field into the resources_ table. After that you have to add resources in the database with this type and fill the cpuset field with a unique UID (not used by real users). The maximum number of concurrent jobs is the number of resources of this type. For example, if you put this in your oar.onf:: JOB_RESOURCE_MANAGER_PROPERTY_DB_FIELD="cpuset" JOB_RESOURCE_MANAGER_JOB_UID_TYPE="user" Then you can add temporary UID:: oarnodesetting -a -h fake -p cpuset=23000 -p type=user oarnodesetting -a -h fake -p cpuset=23001 -p type=user oarnodesetting -a -h fake -p cpuset=23002 -p type=user ... You can put what you want in the place of the hostname (here "fake"). The drawback of this feature is that users haven't their UID only their GID. How to enable jobs to connect to the frontales from the nodes using oarsh? -------------------------------------------------------------------------- First you have to install the node part of OAR on the wanted nodes. After that you have to register the frontales into the database using oarnodesetting with the "frontal" (for example) type and assigned the desired cpus into the cpuset field; for example:: oarnodesetting -a -h frontal1 -p type=frontal -p cpuset=0 oarnodesetting -a -h frontal1 -p type=frontal -p cpuset=1 oarnodesetting -a -h frontal2 -p type=frontal -p cpuset=0 ... Thus you will be able to see resources identifier of these resources with oarnodes; try to type:: oarnodes --sql "type='frontal'" Then put this type name (here "frontal") into the *oar.conf* file on the OAR server into the tag SCHEDULER_RESOURCES_ALWAYS_ASSIGNED_TYPE_. Notes: - if one of these resources become "Suspected" then the scheduling will stop. - you can disable this feature with oarnodesetting_ and put these resources into the "Absent" state. A job remains in the "Finishing" state, what can I do? ------------------------------------------------------ If you have waited more than a couple of minutes (10mn for example) then something wrong occurred (frontal has crashed, out of memory, ...). So you are able to turn manually a job into the "Error" state by typing with the root user (example with a bash shell):: export OARCONFFILE=/etc/oar/oar.conf perl -e 'use OAR::IO; $db = OAR::IO::connect(); OAR::IO::set_job_state($db,42,"Error")' (Replace 42 by your job identifier) How can I write my own scheduler? --------------------------------- .. include:: scheduler/README What is the syntax of this documentation? ----------------------------------------- We are using the RST format from the `Docutils `_ project. This syntax is easily readable and can be converted into HTML, LaTex or XML. You can find basic informations on http://docutils.sourceforge.net/docs/user/rst/quickref.html ./oar-2.5.2/UPGRADING0000644000175000017500000000263411757171206012031 0ustar plbplbUpgrading the OAR batch system ============================== This part explains the important issues when upgrading the OAR Bach system from 2.4.x to 2.5.x. Updating the Perl admission rules --------------------------------- Due to a perl module renaming, you need to update the perl module name used in the admission rules. Here are the correspondance : - oarstat_lib -> OAR::Stat - oarnodes_lib -> OAR::Nodes - oarapi_lib -> OAR::API - oarconf_lib -> OAR::Conf - oario_lib -> OAR::IO - oarsub_lib -> OAR::Sub - oar_Tools -> OAR::Tools - oar_Version -> OAR::Version - window_forker -> OAR::WindowForker - ping_checker -> OAR::PingChecker - oar_hulot -> OAR::Modules::Hulot - Judas -> OAR::Modules::Judas - resource_tree -> OAR::Schedulers::ResourceTree - gantt_hole_storage -> OAR::Schedulers::GanttHoleStorage The OAR default values ---------------------- Please take a look on the default oar.conf (in the /usr/share/doc/oar-common/exemples/). Some default values has changed. Upgrading the sql database scheme --------------------------------- Before restarting the OAR server, you need to upgrade your database scheme. This can be done, with the following instruction (don't forget to make a copy of your database before): oar-database --setup ./oar-2.5.2/README.sources0000644000175000017500000000141111757171206013120 0ustar plbplb Source structures ================= sources/core/database/ contains the sql data and the scrips to initialize and/or upgrade a oar server database. docs/ contains the documentations of OAR Makefiles/ contains the makefile for each oar module setup/ contains the setup scripts executed on the target operating system sources/core/man/ contains manual page of the qfunctions / modules sources/core/common/ contains the common oar stuff. sources/core/common-libs/ contains the common oar libraries. There is one subdirectory for each langage used. sources/core/qfunctions/ contains the user/admin commands sources/core/modules/ contains the oar commands misc/ contains oar related sources/core/scripts/ tests/ third_party/ ./oar-2.5.2/README0000644000175000017500000000334011757171206011441 0ustar plbplb OAR resource manager -------------------- OAR is a resource manager (or batch scheduler if you prefer this wording) for small to large clusters. It is suitable for production use and may also be used as a tool to do research experiments. See INSTALL file to be shown with a detailed installation procedure. Feel free to check the manpages and the OAR web site (http://oar.imag.fr/) for further information. Some features of OAR : - Batch and Interactive jobs - Admission rules - Walltime - Matching of resources (job/node properties) - Hold and resume jobs - Multi-schedulers support (simple fifo and fifo with matching) - Multi-queues with priority - Best-effort queues (for exploiting idle resources) - Check compute nodes before launching - Epilogue/Prologue scripts - No Daemon on compute nodes - Dynamic insertion/deletion of compute node - Logging - Backfiling - First-Fit Scheduler with matching resource (very soon) - Advance Reservation Have a good time. License ------- Copyright (c) 2003-2008 LIG 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 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 $Id$ ./oar-2.5.2/QUICKSTART-USER0000644000175000017500000001021011757171206012744 0ustar plbplbUsing OAR - Basic steps ======================= Visualising the cluster State ----------------------------- Many tools are available to visualize the cluster state. Shell commands: ~~~~~~~~~~~~~~~ * oarnodes: this command shows the resources states. Warning: in our context, a resource is not necessary a machine. It is generally a cpu, a core or a host, but it can be much more... like licence tokens, memory banks... The oarnodes command gives information about the network address where is located this resource, its type, its state and many other (interesting) information. * oarstat: this command shows information about running or planned jobs. (The -f option shows full infomation) Graphical tools: ~~~~~~~~~~~~~~~~ * Monika: this web page shows current resources states and jobs information. On this page you can have more information about a particular resource or job. * DrawGantt: this web page shows the gantt diagram of the scheduling. It represents the current, former and future jobs. Submitting a job in an interactive shell ---------------------------------------- Submission ~~~~~~~~~~ To submit on interactive job we use the "oarsub" command with the "-I" option:: submachine:~$> oarsub -I OAR returns then an unique job ID that will identify your job in the system:: OAR_JOB_ID=1234 Once the job is scheduled, when the requested resources are available, OAR connects you to the first allocated node. OAR initiates environment variables that inform you of your submission properties:: node:~$> env | grep OAR Particularly, the allocated nodes list is contained in the $OAR_NODEFILE:: node:~$> cat $OAR_NODEFILE Visualisation ~~~~~~~~~~~~~ You can get information about your job by looking at the Monika or DrawGantt interfaces or by typing in a command line console:: submachine:~$> oarstat -fj OAR_JOB_ID Exiting the job ~~~~~~~~~~~~~~~ To terminate an interactive job you just have to disconnect from the resource:: node:~$> exit You can likewise kill the job by typing:: submachine:~$> oardel JOB_ID In this case, the session will be violently killed. Interactive submission on many resources ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The "-l" option allows to specify wanted resources. For example, if we need to work in interactive mode on 2 cpu for a max duration of 30 minutes we will ask:: submachine:~$> oarsub -I -l /cpu=2,walltime=00:30:00 The walltime is the job's max duration. If the job overruns its walltime, it will be killed by the system. Thus, you better have to set your walltime correctly depending on how long will take your job to prevent beeing killed if the walltime has been set too short or beeing scheduled later if it is too long. Then, once the job is scheduled and started, OAR connects you on the first reserved node. You still can access the list of the other resources via the $OAR_NODEFILE env variable. Batch submission ---------------- OAR allows to execute scripts in "passive mode". In this mode, the user specifies a script at the submission time. This script will be executed on the first reserved node. It's within this script that the user will define the way to operate parallel resources. All the $OAR_* env variables are reachable within the script. Submission ~~~~~~~~~~ In this case, the principle is the same that interactive submission, just replace the "-I" option with the path of your script:: submachine:~$> oarsub -l /cpu=2,walltime=00:30:00 ./hello_mpi.sh Getting the results of the submission ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In passive mode, OAR creates 2 files: OAR..stdout for the stdout and OAR..stderr for the stderr. Connecting a running job ~~~~~~~~~~~~~~~~~~~~~~~~ You can connect a running job with the "-C" option to oarsub:: submachine:~$> oarsub -C Thus, you will be connected to the first reserved node. Reservations ------------ Until now we only asked for immediate start for our submission. However it is also possible to plan a job in the future. This feature is available through the "-r " option:: submachine:~$> oarsub -r '2008-03-07 16:45:00' -l nodes=2,walltime=0:10:00 ./hello_mpi.sh ./oar-2.5.2/QUICKSTART-ADMIN0000644000175000017500000001171711757171206013033 0ustar plbplbInstalling OAR (almost) without pain ==================================== There are currently 3 methods to install OAR. * with debian packages * with rpm packages * with the sources The first thing you have to know is about the OAR architecture. A common OAR installation is composed of: * the OAR server * the DataBase server * the job submission frontale * several machines that are computing nodes To ease the first installation, you might consider using the same machine for the OAR server, the DataBase, the frontale and to create some virtual resources too on the machine. This might be done by using 127.0.*.* IP addresses for your resources. Otherwise, you will have to copy the oar user keys on each machine to allow passwordless connections between them for the user oar. I) Installation --------------- Installation from debian packages _________________________________ If you want to install oar from the debian packages, you will have to install the oar-server package on the OAR server, the oar-user package on the submission frontale and the oar-node package on the computing nodes. * add to your apt.conf file the line given in http://oar.imag.fr/downloads.html depending on which release you want to install. * install the oar-* packages on the different machines like explained above. * install postgresql or mysql-server depending which database you want to use. * add:: environment="OAR_KEY=1" at the beginning of the public key in the **~oar/.ssh/authorized_keys** file. Installation from rpm packages ______________________________ To complete... Installation from the sources _____________________________ Prerequisites: Install the libs ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ First of all you have to install the following packages: * Perl * Perl-base * openssh (server and client) version >= 3.9 * Perl-Mysql * Perl-DBI * MySQL * libmysql * perl-suid You also have to install a >= 2.6 kernel in order to use cpuset feature. 1°) Pave the way for install ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * add a user named "oar" in the group "oar" * create a set of ssh keys for the user "oar" with ssh-keygen (for instance 'id_dsa.pub' and 'id_dsa') * copy these keys in the ".ssh" folder of the user "oar" * append the contents of 'id_dsa.pub' to the file **~oar/.ssh/authorized_keys** * in **~/.ssh/config** (create the file if it doesn't exists) add the lines:: Host * ForwardX11 no StrictHostKeyChecking no PasswordAuthentication no AddressFamily inet * add in your ssh server config file:: AcceptEnv OAR_CPUSET OAR_JOB_USER PermitUserEnvironment yes UseLogin no AllowUsers oar * add:: environment="OAR_KEY=1" at the beginning of the public key in the **~oar/.ssh/authorized_keys** file. * add in your **~oar/.bashrc** file:: export PATH=/usr/local/oar/oardodo:$PATH 2°) Install the OAR modules ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Just go into the OAR source directory and as root type:: make server-install user-install node-install 3°) Launch the OAR server ~~~~~~~~~~~~~~~~~~~~~~~~~ Finally, you will have to launch as oar the **Almighty** daemon (the oar server). II) Initiate the database ------------------------- As root run oar_mysql_db_init or oar_psql_db_init depending your DataBase type. III) Configuration file ----------------------- This part is optionnal for now. You can come back to it later. To configure your oar server, edit its config file **/etc/oar/oar.conf**. Take a look at the different sections and customize it if you want to. IV) Cpusets ----------- This part is optionnal too right now if you're installing OAR for the first time. It is easier to begin using OAR without this feature. Once everything's ok and your jobs are running on your resources, you can activate this feature. The cpuset feature will allow OAR to restrict the use of one (or a group) of cpu for a job. Each computing resource has a **cpuset** field that will refer to the cpu id that will run the job for this resource. In order to use this feature, you have to uncomment the line with the **JOB_RESOURCE_MANAGER_PROPERTY_DB_FIELD** field in your oar.conf file. Then, for each computing resource in your database, set the cpuset field to the cpu id you want your resource to be restricted on. V) Adding resources to the system --------------------------------- To add resources to your system, you can use (as root) the command oarnodesetting. For a complete comprehension of what does this command, type man oarnodesetting. For now, the two options you will need will be **-a** (means add a resource) and **-h** (defines the resource hostname or ip adress). For example, to add a computing resource locally to test OAR installation type:: oarnodesetting -a -h 127.0.10.1 This will add a resource with 127.0.10.1 as host IP address. You also can modify resources properties with **-p** option, for example:: oarnodesetting -r 1 -p "deploy=YES" will allow the resource #1 to accept jobs of the type deploy. ./oar-2.5.2/Makefiles/0000755000175000017500000000000011757171206012461 5ustar plbplb./oar-2.5.2/Makefiles/www-conf.mk0000644000175000017500000000053211757171206014561 0ustar plbplbMODULE=www-conf SRCDIR=sources EXAMPLEDIR_FILES= $(SRCDIR)/visualization_interfaces/apache.conf.in include Makefiles/shared/shared.mk clean: clean_shared # nothing to do build: build_shared # nothing to do install: install_shared # nothing to do uninstall: uninstall_shared # Nothing to do .PHONY: install setup uninstall build clean ./oar-2.5.2/Makefiles/user.mk0000644000175000017500000000601411757171206013771 0ustar plbplbMODULE=user SRCDIR=sources/core OARDIR_BINFILES = $(SRCDIR)/qfunctions/oarnodes \ $(SRCDIR)/qfunctions/oardel \ $(SRCDIR)/qfunctions/oarstat \ $(SRCDIR)/qfunctions/oarsub \ $(SRCDIR)/qfunctions/oarhold \ $(SRCDIR)/qfunctions/oarresume MANDIR_FILES = $(SRCDIR)/man/man1/oardel.1 \ $(SRCDIR)/man/man1/oarnodes.1 \ $(SRCDIR)/man/man1/oarresume.1 \ $(SRCDIR)/man/man1/oarstat.1 \ $(SRCDIR)/man/man1/oarsub.1 \ $(SRCDIR)/man/man1/oarhold.1 \ $(SRCDIR)/man/man1/oarmonitor_graph_gen.1 include Makefiles/shared/shared.mk clean: clean_shared $(MAKE) -f Makefiles/man.mk clean $(OARDO_CLEAN) CMD_WRAPPER=$(OARDIR)/oarnodes CMD_TARGET=$(DESTDIR)$(BINDIR)/oarnodes $(OARDO_CLEAN) CMD_WRAPPER=$(OARDIR)/oardel CMD_TARGET=$(DESTDIR)$(BINDIR)/oardel $(OARDO_CLEAN) CMD_WRAPPER=$(OARDIR)/oarstat CMD_TARGET=$(DESTDIR)$(BINDIR)/oarstat $(OARDO_CLEAN) CMD_WRAPPER=$(OARDIR)/oarsub CMD_TARGET=$(DESTDIR)$(BINDIR)/oarsub $(OARDO_CLEAN) CMD_WRAPPER=$(OARDIR)/oarhold CMD_TARGET=$(DESTDIR)$(BINDIR)/oarhold $(OARDO_CLEAN) CMD_WRAPPER=$(OARDIR)/oarresume CMD_TARGET=$(DESTDIR)$(BINDIR)/oarresume build: build_shared $(MAKE) -f Makefiles/man.mk build $(OARDO_BUILD) CMD_WRAPPER=$(OARDIR)/oarnodes CMD_TARGET=$(DESTDIR)$(BINDIR)/oarnodes $(OARDO_BUILD) CMD_WRAPPER=$(OARDIR)/oardel CMD_TARGET=$(DESTDIR)$(BINDIR)/oardel $(OARDO_BUILD) CMD_WRAPPER=$(OARDIR)/oarstat CMD_TARGET=$(DESTDIR)$(BINDIR)/oarstat $(OARDO_BUILD) CMD_WRAPPER=$(OARDIR)/oarsub CMD_TARGET=$(DESTDIR)$(BINDIR)/oarsub $(OARDO_BUILD) CMD_WRAPPER=$(OARDIR)/oarhold CMD_TARGET=$(DESTDIR)$(BINDIR)/oarhold $(OARDO_BUILD) CMD_WRAPPER=$(OARDIR)/oarresume CMD_TARGET=$(DESTDIR)$(BINDIR)/oarresume install: install_shared install -d $(DESTDIR)$(BINDIR) install -m 0755 $(SRCDIR)/tools/oarmonitor_graph_gen.pl $(DESTDIR)$(BINDIR)/oarmonitor_graph_gen # Install wrappers $(OARDO_INSTALL) CMD_WRAPPER=$(OARDIR)/oarnodes CMD_TARGET=$(DESTDIR)$(BINDIR)/oarnodes $(OARDO_INSTALL) CMD_WRAPPER=$(OARDIR)/oardel CMD_TARGET=$(DESTDIR)$(BINDIR)/oardel $(OARDO_INSTALL) CMD_WRAPPER=$(OARDIR)/oarstat CMD_TARGET=$(DESTDIR)$(BINDIR)/oarstat $(OARDO_INSTALL) CMD_WRAPPER=$(OARDIR)/oarsub CMD_TARGET=$(DESTDIR)$(BINDIR)/oarsub $(OARDO_INSTALL) CMD_WRAPPER=$(OARDIR)/oarhold CMD_TARGET=$(DESTDIR)$(BINDIR)/oarhold $(OARDO_INSTALL) CMD_WRAPPER=$(OARDIR)/oarresume CMD_TARGET=$(DESTDIR)$(BINDIR)/oarresume uninstall: uninstall_shared rm -f $(DESTDIR)$(BINDIR)/oarmonitor_graph_gen $(OARDO_UNINSTALL) CMD_WRAPPER=$(OARDIR)/oarnodes CMD_TARGET=$(DESTDIR)$(BINDIR)/oarnodes $(OARDO_UNINSTALL) CMD_WRAPPER=$(OARDIR)/oardel CMD_TARGET=$(DESTDIR)$(BINDIR)/oardel $(OARDO_UNINSTALL) CMD_WRAPPER=$(OARDIR)/oarstat CMD_TARGET=$(DESTDIR)$(BINDIR)/oarstat $(OARDO_UNINSTALL) CMD_WRAPPER=$(OARDIR)/oarsub CMD_TARGET=$(DESTDIR)$(BINDIR)/oarsub $(OARDO_UNINSTALL) CMD_WRAPPER=$(OARDIR)/oarhold CMD_TARGET=$(DESTDIR)$(BINDIR)/oarhold $(OARDO_UNINSTALL) CMD_WRAPPER=$(OARDIR)/oarresume CMD_TARGET=$(DESTDIR)$(BINDIR)/oarresume .PHONY: install setup uninstall build clean ./oar-2.5.2/Makefiles/tools.mk0000644000175000017500000000155211757171206014155 0ustar plbplbMODULE=tools SRCDIR=sources/core OARDIR_BINFILES = $(SRCDIR)/qfunctions/oaradmin/oaradmin.rb \ $(SRCDIR)/qfunctions/oaradmin/oar_modules.rb \ $(SRCDIR)/qfunctions/oaradmin/oaradmin_modules.rb MANDIR_FILES = $(SRCDIR)/man/man1/oaradmin.1 include Makefiles/shared/shared.mk clean: clean_shared $(MAKE) -f Makefiles/man.mk clean $(OARDO_CLEAN) CMD_WRAPPER=$(OARDIR)/oaradmin.rb CMD_TARGET=$(DESTDIR)$(SBINDIR)/oaradmin build: build_shared $(MAKE) -f Makefiles/man.mk build $(OARDO_BUILD) CMD_WRAPPER=$(OARDIR)/oaradmin.rb CMD_TARGET=$(DESTDIR)$(SBINDIR)/oaradmin install: build install_shared $(OARDO_INSTALL) CMD_WRAPPER=$(OARDIR)/oaradmin.rb CMD_TARGET=$(DESTDIR)$(SBINDIR)/oaradmin uninstall: uninstall_shared $(OARDO_UNINSTALL) CMD_WRAPPER=$(OARDIR)/oaradmin.rb CMD_TARGET=$(DESTDIR)$(SBINDIR)/oaradmin .PHONY: install setup uninstall build clean ./oar-2.5.2/Makefiles/shared/0000755000175000017500000000000011757171206013727 5ustar plbplb./oar-2.5.2/Makefiles/shared/shared.mk0000644000175000017500000002266211757171206015536 0ustar plbplb export OARDO_BUILD = $(MAKE) -f Makefiles/oardo/oardo.mk build export OARDO_CLEAN = $(MAKE) -f Makefiles/oardo/oardo.mk clean export OARDO_INSTALL = $(MAKE) -f Makefiles/oardo/oardo.mk install export OARDO_UNINSTALL = $(MAKE) -f Makefiles/oardo/oardo.mk uninstall SHARED_INSTALL = $(MAKE) -f Makefiles/shared/common_target.mk install SHARED_UNINSTALL = $(MAKE) -f Makefiles/shared/common_target.mk uninstall # == TARGET_DIST?=$(shell if [ -f /etc/debian_version ]; then echo "debian"; fi; \ if [ -f /etc/redhat-release ]; then echo "redhat"; fi; \ ) # == # == debian ifeq "$(TARGET_DIST)" "debian" ifeq "$(SETUP_TYPE)" "deb" include Makefiles/shared/dist/debian-deb.mk else include Makefiles/shared/dist/debian-tgz.mk endif endif # == debian # == redhat ifeq "$(TARGET_DIST)" "redhat" ifeq "$(SETUP_TYPE)" "rpm" include Makefiles/shared/dist/redhat-rpm.mk else include Makefiles/shared/dist/redhat-tgz.mk endif endif # == redhat include Makefiles/shared/dist/common.mk all: setup: setup_shared SHARED_ACTIONS=perllib oardata oarbin doc man1 bin sbin examples setup_scripts init logrotate default cron cgi www clean_shared: clean_templates clean_man1 clean_setup_scripts build_shared: build_templates build_man1 build_setup_scripts rm -f setup/templates/header.sh install_shared: $(patsubst %, install_%,$(SHARED_ACTIONS)) install_setup_scripts setup_shared: run_setup_scripts uninstall_shared: $(patsubst %, uninstall_%,$(SHARED_ACTIONS)) uninstall_setup_scripts # # template processing (*.in) # MODULE_SETUP_SOURCE_FILES = $(wildcard setup/$(MODULE)*.in) MODULE_SETUP_TOBUILD_FILES = $(patsubst %.in, %, $(MODULE_SETUP_SOURCE_FILES)) MODULE_SETUP_BUILDED_FILES = $(patsubst %.in, %.out, $(MODULE_SETUP_SOURCE_FILES)) MODULE_SETUP_TARGET_FILES = $(addprefix $(DESTDIR)$(OARDIR)/setup/,$(notdir $(basename $(MODULE_SETUP_SOURCE_FILES)))) TEMPLATE_SOURCE_FILES=$(filter %.in, $(PROCESS_TEMPLATE_FILES) \ $(MANDIR_FILES) \ $(INITDIR_FILES) \ $(DEFAULTDIR_FILES) \ $(LOGROTATEDIR_FILES) \ $(CRONDIR_FILES) \ $(CRONHOURLYDIR_FILES) \ $(OARDIR_BINFILES) \ $(OARDIR_DATAFILES) \ $(DOCDIR_FILES) \ $(BINDIR_FILES) \ $(SBINDIR_FILES) \ $(EXAMPLEDIR_FILES) \ $(CGIDIR_FILES) \ $(WWWDIR_FILES) \ $(MODULE_SETUP_SOURCE_FILES) \ setup/templates/header.sh.in \ ) TEMPLATE_BUILDED_FILES=$(patsubst %.in,%,$(TEMPLATE_SOURCE_FILES)) build_templates: $(TEMPLATE_BUILDED_FILES) $(TEMPLATE_BUILDED_FILES) : $(patsubst %, %.in,$*) perl -pe "s#%%PREFIX%%#$(PREFIX)#g;;\ s#%%BINDIR%%#$(BINDIR)#g;;\ s#%%CGIDIR%%#$(CGIDIR)#g;;\ s#%%DOCDIR%%#$(DOCDIR)#g;;\ s#%%EXAMPLEDIR%%#$(EXAMPLEDIR)#g;;\ s#%%ETCDIR%%#$(ETCDIR)#g;;\ s#%%OARCONFDIR%%#$(OARCONFDIR)#g;;\ s#%%OARDIR%%#$(OARDIR)#g;;\ s#%%SHAREDIR%%#$(SHAREDIR)#g;;\ s#%%PERLLIBDIR%%#$(PERLLIBDIR)#g;;\ s#%%RUNDIR%%#$(RUNDIR)#g;;\ s#%%LOGDIR%%#$(LOGDIR)#g;;\ s#%%MANDIR%%#$(MANDIR)#g;;\ s#%%SBINDIR%%#$(SBINDIR)#g;;\ s#%%VARLIBDIR%%#$(VARLIBDIR)#g;;\ s#%%OARHOMEDIR%%#$(OARHOMEDIR)#g;;\ s#%%ROOTUSER%%#$(ROOTUSER)#g;;\ s#%%ROOTGROUP%%#$(ROOTGROUP)#g;;\ s#%%OARDO_DEFAULTUSER%%#$(OARDO_DEFAULTUSER)#g;;\ s#%%OARDO_DEFAULTGROUP%%#$(OARDO_DEFAULTGROUP)#g;;\ s#%%OARUSER%%#$(OARUSER)#g;;\ s#%%OAROWNER%%#$(OAROWNER)#g;;\ s#%%OAROWNERGROUP%%#$(OAROWNERGROUP)#g;;\ s#%%WWWUSER%%#$(WWWUSER)#g;;\ s#%%APACHECONFDIR%%#$(APACHECONFDIR)#g;;\ s#%%WWW_ROOTDIR%%#$(WWW_ROOTDIR)#g;;\ s#%%WWWDIR%%#$(WWWDIR)#g;;\ s#%%XAUTHCMDPATH%%#$(XAUTHCMDPATH)#g;;\ s#%%OARSHCMD%%#$(OARSHCMD)#g;;\ s#%%INITDIR%%#$(INITDIR)#g;;\ s#%%DEFAULTDIR%%#$(DEFAULTDIR)#g;;\ s#%%SETUP_TYPE%%#$(SETUP_TYPE)#g;;\ s#%%TARGET_DIST%%#$(TARGET_DIST)#g;;\ " "$@.in" > $@ clean_templates: -rm -f $(TEMPLATE_BUILDED_FILES) # # setup scripts # MODULE_SETUP_FILE:=$(DESTDIR)$(OARDIR)/setup/$(MODULE).sh MODULE_SETUP_FUNC:=$(subst -,_,$(MODULE))_setup run_setup_scripts: if [ -f "$(MODULE_SETUP_FILE)" ]; then . $(MODULE_SETUP_FILE) && $(MODULE_SETUP_FUNC); fi install_setup_scripts: $(MODULE_SETUP_TARGET_FILES) build_setup_scripts: $(MODULE_SETUP_BUILDED_FILES) $(MODULE_SETUP_BUILDED_FILES): $(MODULE_SETUP_TOBUILD_FILES) cat setup/templates/header.sh $< > $@ clean_setup_scripts: -rm -f $(MODULE_SETUP_BUILDED_FILES) uninstall_setup_scripts: -rm -f $(MODULE_SETUP_TARGET_FILES) $(MODULE_SETUP_TARGET_FILES): $(MODULE_SETUP_BUILDED_FILES) install -d $(DESTDIR)$(OARDIR)/setup install -m 0755 $< $@ # # OAR_PERLLIB # ifdef OAR_PERLLIB install_perllib: install -m 0755 -d $(DESTDIR)$(PERLLIBDIR) cp -r $(OAR_PERLLIB)/* $(DESTDIR)$(PERLLIBDIR)/ uninstall_perllib: (cd $(OAR_PERLLIB) && find . -type f -exec rm -f $(DESTDIR)$(PERLLIBDIR)/{} \;) else install_perllib: uninstall_perllib: endif # # OARDIR_DATAFILES # install_oardata: $(SHARED_INSTALL) TARGET_DIR="$(DESTDIR)$(OARDIR)" SOURCE_FILES="$(OARDIR_DATAFILES)" TARGET_FILE_RIGHTS=0644 uninstall_oardata: $(SHARED_UNINSTALL) TARGET_DIR="$(DESTDIR)$(OARDIR)" SOURCE_FILES="$(OARDIR_DATAFILES)" TARGET_FILE_RIGHTS=0644 # # OARDIR_BINFILES # install_oarbin: $(SHARED_INSTALL) TARGET_DIR="$(DESTDIR)$(OARDIR)" SOURCE_FILES="$(OARDIR_BINFILES)" TARGET_FILE_RIGHTS=0755 uninstall_oarbin: $(SHARED_UNINSTALL) TARGET_DIR="$(DESTDIR)$(OARDIR)" SOURCE_FILES="$(OARDIR_BINFILES)" TARGET_FILE_RIGHTS=0755 # # DOCDIR_FILES # install_doc: $(SHARED_INSTALL) TARGET_DIR="$(DESTDIR)$(DOCDIR)" SOURCE_FILES="$(DOCDIR_FILES)" TARGET_FILE_RIGHTS=0644 uninstall_doc: $(SHARED_UNINSTALL) TARGET_DIR="$(DESTDIR)$(DOCDIR)" SOURCE_FILES="$(DOCDIR_FILES)" TARGET_FILE_RIGHTS=0644 # # MANDIR_FILES # SOURCE_MANDIR_FILES = $(filter %.pod, $(patsubst %.pod.in, %.pod, $(MANDIR_FILES))) BUILD_MANDIR_FILES = $(patsubst %.pod, %.1, $(SOURCE_MANDIR_FILES)) $(filter %.1,$(MANDIR_FILES)) TARGET_MANDIR_FILES = $(addprefix $(DESTDIR)$(MANDIR)/man1, $(notdir $(BUILD_MANDIR_FILES))) install_man1: $(SHARED_INSTALL) TARGET_DIR="$(DESTDIR)$(MANDIR)/man1" SOURCE_FILES="$(BUILD_MANDIR_FILES)" TARGET_FILE_RIGHTS=0644 uninstall_man1: $(SHARED_UNINSTALL) TARGET_DIR="$(DESTDIR)$(MANDIR)/man1" SOURCE_FILES="$(BUILD_MANDIR_FILES)" TARGET_FILE_RIGHTS=0644 build_man1: $(BUILD_MANDIR_FILES) clean_man1: -rm -f $(BUILD_MANDIR_FILES) %.1: %.pod pod2man --section=1 --release="$(notdir $(basename $<))" --center "OAR commands" --name="$(notdir $(basename $<))" "$<" > $@ # # BINDIR_FILES # install_bin: $(SHARED_INSTALL) TARGET_DIR="$(DESTDIR)$(BINDIR)" SOURCE_FILES="$(BINDIR_FILES)" TARGET_FILE_RIGHTS=0755 uninstall_bin: $(SHARED_UNINSTALL) TARGET_DIR="$(DESTDIR)$(BINDIR)" SOURCE_FILES="$(BINDIR_FILES)" TARGET_FILE_RIGHTS=0755 # # SBINDIR_FILES # install_sbin: $(SHARED_INSTALL) TARGET_DIR="$(DESTDIR)$(SBINDIR)" SOURCE_FILES="$(SBINDIR_FILES)" TARGET_FILE_RIGHTS=0755 uninstall_sbin: $(SHARED_UNINSTALL) TARGET_DIR="$(DESTDIR)$(SBINDIR)" SOURCE_FILES="$(SBINDIR_FILES)" TARGET_FILE_RIGHTS=0755 # # EXAMPLEDIR_FILES # install_examples: $(SHARED_INSTALL) TARGET_DIR="$(DESTDIR)$(EXAMPLEDIR)" SOURCE_FILES="$(EXAMPLEDIR_FILES)" TARGET_FILE_RIGHTS=0644 uninstall_examples: $(SHARED_UNINSTALL) TARGET_DIR="$(DESTDIR)$(EXAMPLEDIR)" SOURCE_FILES="$(EXAMPLEDIR_FILES)" TARGET_FILE_RIGHTS=0644 # # INITDIR_FILES # install_init: $(SHARED_INSTALL) TARGET_DIR="$(DESTDIR)$(EXAMPLEDIR)/init.d" SOURCE_FILES="$(INITDIR_FILES)" TARGET_FILE_RIGHTS=0755 uninstall_init: $(SHARED_UNINSTALL) TARGET_DIR="$(DESTDIR)$(EXAMPLEDIR)/init.d" SOURCE_FILES="$(INITDIR_FILES)" TARGET_FILE_RIGHTS=0755 # # CRONDIR_FILES # install_cron: $(SHARED_INSTALL) TARGET_DIR="$(DESTDIR)$(EXAMPLEDIR)/cron.d" SOURCE_FILES="$(CRONDIR_FILES)" TARGET_FILE_RIGHTS=0644 $(SHARED_INSTALL) TARGET_DIR="$(DESTDIR)$(EXAMPLEDIR)/cron.hourly" SOURCE_FILES="$(CRONHOURLYDIR_FILES)" TARGET_FILE_RIGHTS=0755 uninstall_cron: $(SHARED_UNINSTALL) TARGET_DIR="$(DESTDIR)$(EXAMPLEDIR)/cron.d" SOURCE_FILES="$(CRONDIR_FILES)" TARGET_FILE_RIGHTS=0644 $(SHARED_UNINSTALL) TARGET_DIR="$(DESTDIR)$(EXAMPLEDIR)/cron.hourly" SOURCE_FILES="$(CRONHOURLYDIR_FILES)" TARGET_FILE_RIGHTS=0755 # # DEFAULTDIR_FILES # install_default: $(SHARED_INSTALL) TARGET_DIR="$(DESTDIR)$(EXAMPLEDIR)/default" SOURCE_FILES="$(DEFAULTDIR_FILES)" TARGET_FILE_RIGHTS=0644 uninstall_default: $(SHARED_UNINSTALL) TARGET_DIR="$(DESTDIR)$(EXAMPLEDIR)/default" SOURCE_FILES="$(DEFAULTDIR_FILES)" TARGET_FILE_RIGHTS=0644 # # LOGROTATEDIR_FILES # install_logrotate: $(SHARED_INSTALL) TARGET_DIR="$(DESTDIR)$(EXAMPLEDIR)/logrotate.d" SOURCE_FILES="$(LOGROTATEDIR_FILES)" TARGET_FILE_RIGHTS=0644 uninstall_logrotate: $(SHARED_UNINSTALL) TARGET_DIR="$(DESTDIR)$(EXAMPLEDIR)/logrotate.d" SOURCE_FILES="$(LOGROTATEDIR_FILES)" TARGET_FILE_RIGHTS=0644 # # CGIDIR_FILES # install_cgi: $(SHARED_INSTALL) TARGET_DIR="$(DESTDIR)$(CGIDIR)" SOURCE_FILES="$(CGIDIR_FILES)" TARGET_FILE_RIGHTS=0755 uninstall_cgi: $(SHARED_UNINSTALL) TARGET_DIR="$(DESTDIR)$(CGIDIR)" SOURCE_FILES="$(CGIDIR_FILES)" TARGET_FILE_RIGHTS=0755 # # WWWDIR_FILES # install_www: $(SHARED_INSTALL) TARGET_DIR="$(DESTDIR)$(WWWDIR)" SOURCE_FILES="$(WWWDIR_FILES)" TARGET_FILE_RIGHTS=0644 uninstall_www: $(SHARED_UNINSTALL) TARGET_DIR="$(DESTDIR)$(WWWDIR)" SOURCE_FILES="$(WWWDIR_FILES)" TARGET_FILE_RIGHTS=0644 .PHONY: install setup uninstall build clean ./oar-2.5.2/Makefiles/shared/dist/0000755000175000017500000000000011757171206014672 5ustar plbplb./oar-2.5.2/Makefiles/shared/dist/redhat-tgz.mk0000644000175000017500000000051411757171206017274 0ustar plbplbexport SETUP_TYPE?=tgz export TARGET_DIST?=redhat export PREFIX?=/usr/local export WWWDIR?=/usr/share/oar-web-status export CGIDIR?=/var/www/cgi-bin export PERLLIBDIR?=$(PREFIX)/share/perl5 export INITDIR?=$(ETCDIR)/rc.d/init.d export DEFAULTDIR?=$(ETCDIR)/sysconfig export WWWUSER?=apache export APACHECONFDIR?=$(ETCDIR)/httpd ./oar-2.5.2/Makefiles/shared/dist/redhat-rpm.mk0000644000175000017500000000043511757171206017270 0ustar plbplbexport SETUP_TYPE?=rpm export TARGET_DIST?=redhat export PREFIX?=/usr export CGIDIR?=/var/www/cgi-bin export PERLLIBDIR?=$(PREFIX)/share/perl5 export INITDIR?=$(ETCDIR)/rc.d/init.d export DEFAULTDIR?=$(ETCDIR)/sysconfig export WWWUSER?=apache export APACHECONFDIR?=$(ETCDIR)/httpd ./oar-2.5.2/Makefiles/shared/dist/debian-tgz.mk0000644000175000017500000000011411757171206017243 0ustar plbplbexport PREFIX?=/usr/local export SETUP_TYPE?=tgz export TARGET_DIST?=debian ./oar-2.5.2/Makefiles/shared/dist/debian-deb.mk0000644000175000017500000000010711757171206017173 0ustar plbplbexport PREFIX?=/usr export SETUP_TYPE?=deb export TARGET_DIST?=debian ./oar-2.5.2/Makefiles/shared/dist/darwin-tgz.mk0000644000175000017500000000017111757171206017310 0ustar plbplbexport SETUP_TYPE?=tgz export TARGET_DIST?=darwin export WWWUSER?=_www export ROOTUSER?=admin export ROOTGROUP?=admin ./oar-2.5.2/Makefiles/shared/dist/common.mk0000644000175000017500000000231511757171206016514 0ustar plbplbexport CFLAGS?=-g -Wall export PREFIX?=/usr/local export RUNDIR?=/var/run export LOGDIR?=/var/log export INITDIR?=$(ETCDIR)/init.d export CRONDIR?=$(ETCDIR)/cron.d export CRONHOURLYDIR?=$(ETCDIR)/cron.hourly export LOGROTATEDIR?=$(ETCDIR)/logrotate.d export DEFAULTDIR?=$(ETCDIR)/default export SHAREDIR?=$(PREFIX)/share/oar-$(strip $(MODULE)) export MANDIR?=$(PREFIX)/share/man export BINDIR?=$(PREFIX)/bin export SBINDIR?=$(PREFIX)/sbin export DOCDIR?=$(PREFIX)/share/doc/oar-$(strip $(MODULE)) export EXAMPLEDIR?=$(DOCDIR)/examples export WWWDIR?=$(PREFIX)/share/oar-web-status export CGIDIR?=$(PREFIX)/lib/cgi-bin export PERLLIBDIR?=$(PREFIX)/lib/site_perl export VARLIBDIR?=/var/lib export ETCDIR?=/etc export OARHOMEDIR?=$(VARLIBDIR)/oar export OARDIR?=$(PREFIX)/lib/oar export OARCONFDIR?=$(ETCDIR)/oar export SETUP_TYPE?=tgz export TARGET_DIST?=debian export ROOTUSER?=root export ROOTGROUP?=root export OARUSER?=oar export OAROWNER?=$(OARUSER) export OAROWNERGROUP?=$(OAROWNER) export OARDO_DEFAULTUSER?=$(ROOTUSER) export OARDO_DEFAULTGROUP?=$(OAROWNERGROUP) export WWWUSER?=www-data export APACHECONFDIR?=$(ETCDIR)/apache2 export WWW_ROOTDIR?= export XAUTHCMDPATH?=/usr/bin/xauth export OARSHCMD?=oarsh_oardo ./oar-2.5.2/Makefiles/shared/common_target.mk0000644000175000017500000000102711757171206017116 0ustar plbplb# This file factorize the shared target {mandir|sbindir|bindir|...}{install|uninstall} ifndef TARGET_DIR echo "No TARGET_DIR defined. Fail !" exit 1 endif TARGET_DIR_RIGHTS?=755 TARGET_FILE_RIGHTS?=755 BUILDED_FILES=$(patsubst %.in,%,$(SOURCE_FILES)) TARGET_FILES=$(addprefix $(TARGET_DIR)/,$(notdir $(BUILDED_FILES))) ifdef SOURCE_FILES install: install -m $(TARGET_DIR_RIGHTS) -d $(TARGET_DIR) install -m $(TARGET_FILE_RIGHTS) $(BUILDED_FILES) $(TARGET_DIR) uninstall: -rm -f $(TARGET_FILES) else install: uninstall: endif ./oar-2.5.2/Makefiles/server.mk0000644000175000017500000001575311757171206014333 0ustar plbplbMODULE=server SRCDIR=sources/core OARDIR_BINFILES = $(SRCDIR)/modules/runner/runner \ $(SRCDIR)/modules/scheduler/oar_meta_sched \ $(SRCDIR)/qfunctions/oarnotify \ $(SRCDIR)/qfunctions/oarremoveresource \ $(SRCDIR)/qfunctions/oaraccounting \ $(SRCDIR)/qfunctions/oarproperty \ $(SRCDIR)/qfunctions/oarmonitor \ $(SRCDIR)/modules/runner/bipbip.in \ $(SRCDIR)/tools/detect_resources \ $(SRCDIR)/tools/oar_checkdb.pl OAR_PERLLIB = $(SRCDIR)/server/lib OARDIR_DATAFILES = $(SRCDIR)/modules/runner/oarexec OARSCHEDULER_BINFILES = $(SRCDIR)/modules/scheduler/oar_sched_gantt_with_timesharing \ $(SRCDIR)/modules/scheduler/oar_sched_gantt_with_timesharing_and_fairsharing \ $(SRCDIR)/modules/scheduler/oar_sched_gantt_with_timesharing_and_fairsharing_and_placeholder OARCONFDIR_BINFILES = $(SRCDIR)/tools/oar_phoenix.pl MANDIR_FILES = $(SRCDIR)/man/man1/Almighty.1 \ $(SRCDIR)/man/man1/oaraccounting.1 \ $(SRCDIR)/man/man1/oarmonitor.1 \ $(SRCDIR)/man/man1/oarnotify.1 \ $(SRCDIR)/man/man1/oarproperty.1 \ $(SRCDIR)/man/man1/oarremoveresource.1 \ $(SRCDIR)/man/man1/oar-server.1 \ $(SRCDIR)/man/man1/oar_resources_init.1 \ $(SRCDIR)/man/man1/oar_phoenix.1 \ $(SRCDIR)/man/man1/oar_checkdb.1 SBINDIR_FILES = $(SRCDIR)/server/sbin/oar-server.in EXAMPLEDIR_FILES = $(SRCDIR)/tools/job_resource_manager.pl \ $(SRCDIR)/tools/job_resource_manager_cgroups.pl \ $(SRCDIR)/tools/suspend_resume_manager.pl \ $(SRCDIR)/tools/oarmonitor_sensor.pl \ $(SRCDIR)/scripts/server_epilogue \ $(SRCDIR)/scripts/server_prologue \ $(SRCDIR)/tools/wake_up_nodes.sh \ $(SRCDIR)/tools/shut_down_nodes.sh DEFAULTDIR_FILES = setup/default/oar-server.in INITDIR_FILES = setup/init.d/oar-server.in CRONDIR_FILES = setup/cron.d/oar-server.in include Makefiles/shared/shared.mk clean: clean_shared $(MAKE) -f Makefiles/man.mk clean $(OARDO_CLEAN) CMD_WRAPPER=$(OARDIR)/detect_resources CMD_TARGET=$(DESTDIR)$(SBINDIR)/oar_resources_init $(OARDO_CLEAN) CMD_WRAPPER=$(OARDIR)/Almighty CMD_TARGET=$(DESTDIR)$(SBINDIR)/Almighty $(OARDO_CLEAN) CMD_WRAPPER=$(OARDIR)/oarnotify CMD_TARGET=$(DESTDIR)$(SBINDIR)/oarnotify $(OARDO_CLEAN) CMD_WRAPPER=$(OARDIR)/oarremoveresource CMD_TARGET=$(DESTDIR)$(SBINDIR)/oarremoveresource $(OARDO_CLEAN) CMD_WRAPPER=$(OARDIR)/oaraccounting CMD_TARGET=$(DESTDIR)$(SBINDIR)/oaraccounting $(OARDO_CLEAN) CMD_WRAPPER=$(OARDIR)/oarproperty CMD_TARGET=$(DESTDIR)$(SBINDIR)/oarproperty $(OARDO_CLEAN) CMD_WRAPPER=$(OARDIR)/oarmonitor CMD_TARGET=$(DESTDIR)$(SBINDIR)/oarmonitor $(OARDO_CLEAN) CMD_WRAPPER=$(OARDIR)/detect_resources CMD_TARGET=$(DESTDIR)$(SBINDIR)/oar_resources_init $(OARDO_CLEAN) CMD_WRAPPER=$(OARDIR)/oar_checkdb.pl CMD_TARGET=$(DESTDIR)$(SBINDIR)/oar_checkdb $(OARDO_CLEAN) CMD_WRAPPER=$(OARCONFDIR)/oar_phoenix.pl CMD_TARGET=$(DESTDIR)$(SBINDIR)/oar_phoenix build: build_shared $(MAKE) -f Makefiles/man.mk build $(OARDO_BUILD) CMD_WRAPPER=$(OARDIR)/detect_resources CMD_TARGET=$(DESTDIR)$(SBINDIR)/oar_resources_init $(OARDO_BUILD) CMD_WRAPPER=$(OARDIR)/Almighty CMD_TARGET=$(DESTDIR)$(SBINDIR)/Almighty $(OARDO_BUILD) CMD_WRAPPER=$(OARDIR)/oarnotify CMD_TARGET=$(DESTDIR)$(SBINDIR)/oarnotify $(OARDO_BUILD) CMD_WRAPPER=$(OARDIR)/oarremoveresource CMD_TARGET=$(DESTDIR)$(SBINDIR)/oarremoveresource $(OARDO_BUILD) CMD_WRAPPER=$(OARDIR)/oaraccounting CMD_TARGET=$(DESTDIR)$(SBINDIR)/oaraccounting $(OARDO_BUILD) CMD_WRAPPER=$(OARDIR)/oarproperty CMD_TARGET=$(DESTDIR)$(SBINDIR)/oarproperty $(OARDO_BUILD) CMD_WRAPPER=$(OARDIR)/oarmonitor CMD_TARGET=$(DESTDIR)$(SBINDIR)/oarmonitor $(OARDO_BUILD) CMD_WRAPPER=$(OARDIR)/detect_resources CMD_TARGET=$(DESTDIR)$(SBINDIR)/oar_resources_init $(OARDO_BUILD) CMD_WRAPPER=$(OARDIR)/oar_checkdb.pl CMD_TARGET=$(DESTDIR)$(SBINDIR)/oar_checkdb $(OARDO_BUILD) CMD_WRAPPER=$(OARCONFDIR)/oar_phoenix.pl CMD_TARGET=$(DESTDIR)$(SBINDIR)/oar_phoenix install: build install_shared install -d $(DESTDIR)$(OARDIR)/schedulers install -m 0755 $(OARSCHEDULER_BINFILES) $(DESTDIR)$(OARDIR)/schedulers install -d $(DESTDIR)$(OARCONFDIR) install -m 0750 $(OARCONFDIR_BINFILES) $(DESTDIR)$(OARCONFDIR) install -m 0755 $(SRCDIR)/modules/almighty.pl $(DESTDIR)$(OARDIR)/Almighty install -m 0755 $(SRCDIR)/modules/leon.pl $(DESTDIR)$(OARDIR)/Leon install -m 0755 $(SRCDIR)/modules/sarko.pl $(DESTDIR)$(OARDIR)/sarko install -m 0755 $(SRCDIR)/modules/finaud.pl $(DESTDIR)$(OARDIR)/finaud install -m 0755 $(SRCDIR)/modules/node_change_state.pl $(DESTDIR)$(OARDIR)/NodeChangeState $(OARDO_INSTALL) CMD_WRAPPER=$(OARDIR)/detect_resources CMD_TARGET=$(DESTDIR)$(SBINDIR)/oar_resources_init $(OARDO_INSTALL) CMD_WRAPPER=$(OARDIR)/Almighty CMD_TARGET=$(DESTDIR)$(SBINDIR)/Almighty $(OARDO_INSTALL) CMD_WRAPPER=$(OARDIR)/oarnotify CMD_TARGET=$(DESTDIR)$(SBINDIR)/oarnotify $(OARDO_INSTALL) CMD_WRAPPER=$(OARDIR)/oarremoveresource CMD_TARGET=$(DESTDIR)$(SBINDIR)/oarremoveresource $(OARDO_INSTALL) CMD_WRAPPER=$(OARDIR)/oaraccounting CMD_TARGET=$(DESTDIR)$(SBINDIR)/oaraccounting $(OARDO_INSTALL) CMD_WRAPPER=$(OARDIR)/oarproperty CMD_TARGET=$(DESTDIR)$(SBINDIR)/oarproperty $(OARDO_INSTALL) CMD_WRAPPER=$(OARDIR)/oarmonitor CMD_TARGET=$(DESTDIR)$(SBINDIR)/oarmonitor $(OARDO_INSTALL) CMD_WRAPPER=$(OARDIR)/detect_resources CMD_TARGET=$(DESTDIR)$(SBINDIR)/oar_resources_init $(OARDO_INSTALL) CMD_WRAPPER=$(OARDIR)/oar_checkdb.pl CMD_TARGET=$(DESTDIR)$(SBINDIR)/oar_checkdb $(OARDO_INSTALL) CMD_WRAPPER=$(OARCONFDIR)/oar_phoenix.pl CMD_TARGET=$(DESTDIR)$(SBINDIR)/oar_phoenix uninstall: uninstall_shared @for file in $(OARCONFDIR_FILES); do rm -f $(DESTDIR)$(OARCONFDIR)/`basename $$file`; done @for file in $(OARSCHEDULER_BINFILES); do rm -f $(DESTDIR)$(OARDIR)/schedulers/`basename $$file`; done rm -f $(DESTDIR)$(OARDIR)/Almighty rm -f $(DESTDIR)$(OARDIR)/Leon rm -f $(DESTDIR)$(OARDIR)/sarko rm -f $(DESTDIR)$(OARDIR)/finaud rm -f $(DESTDIR)$(OARDIR)/NodeChangeState rm -rf $(DESTDIR)$(EXAMPLEDIR) $(OARDO_UNINSTALL) CMD_WRAPPER=$(OARDIR)/detect_resources CMD_TARGET=$(DESTDIR)$(SBINDIR)/oar_resources_init $(OARDO_UNINSTALL) CMD_WRAPPER=$(OARDIR)/Almighty CMD_TARGET=$(DESTDIR)$(SBINDIR)/Almighty $(OARDO_UNINSTALL) CMD_WRAPPER=$(OARDIR)/oarnotify CMD_TARGET=$(DESTDIR)$(SBINDIR)/oarnotify $(OARDO_UNINSTALL) CMD_WRAPPER=$(OARDIR)/oarremoveresource CMD_TARGET=$(DESTDIR)$(SBINDIR)/oarremoveresource $(OARDO_UNINSTALL) CMD_WRAPPER=$(OARDIR)/oaraccounting CMD_TARGET=$(DESTDIR)$(SBINDIR)/oaraccounting $(OARDO_UNINSTALL) CMD_WRAPPER=$(OARDIR)/oarproperty CMD_TARGET=$(DESTDIR)$(SBINDIR)/oarproperty $(OARDO_UNINSTALL) CMD_WRAPPER=$(OARDIR)/oarmonitor CMD_TARGET=$(DESTDIR)$(SBINDIR)/oarmonitor $(OARDO_UNINSTALL) CMD_WRAPPER=$(OARDIR)/detect_resources CMD_TARGET=$(DESTDIR)$(SBINDIR)/oar_resources_init $(OARDO_UNINSTALL) CMD_WRAPPER=$(OARDIR)/oar_checkdb.pl CMD_TARGET=$(DESTDIR)$(SBINDIR)/oar_checkdb $(OARDO_UNINSTALL) CMD_WRAPPER=$(OARCONFDIR)/oar_phoenix.pl CMD_TARGET=$(DESTDIR)$(SBINDIR)/oar_phoenix .PHONY: install setup uninstall build clean ./oar-2.5.2/Makefiles/scheduler-ocaml.mk0000644000175000017500000000136311757171206016064 0ustar plbplbMODULE=scheduler-ocaml SRCDIR=sources/extra/ocaml-schedulers include Makefiles/shared/shared.mk OARDIR_BINFILES= $(SRCDIR)/misc/hierarchy_extractor.rb clean: clean_shared $(MAKE) -C $(SRCDIR)/simple_cbf_mb_h_ct_oar clean rm -rf \ $(SRCDIR)/simple_cbf_mb_h_ct_oar/._d \ $(SRCDIR)/simple_cbf_mb_h_ct_oar/common build: build_shared $(MAKE) -C $(SRCDIR)/simple_cbf_mb_h_ct_oar install: install_shared install -d $(DESTDIR)$(OARDIR)/schedulers install \ $(SRCDIR)/simple_cbf_mb_h_ct_oar/simple_cbf_mb_h_ct_oar_mysql \ $(DESTDIR)$(OARDIR)/schedulers/oar_sched_ocaml_simple_cbf_mysql uninstall: uninstall_shared rm -f $(DESTDIR)$(OARDIR)/schedulers/oar_sched_ocaml_simple_cbf_mysql .PHONY: install setup uninstall build clean ./oar-2.5.2/Makefiles/oardo/0000755000175000017500000000000011757171206013565 5ustar plbplb./oar-2.5.2/Makefiles/oardo/oardo.mk0000644000175000017500000000331511757171206015224 0ustar plbplbMODULE=oardo include Makefiles/shared/shared.mk ifeq "$(OARCONFFILE)" "" OARCONFFILE=$(OARCONFDIR)/oar.conf endif ifeq "$(OARXAUTHLOCATION)" "" OARXAUTHLOCATION=$(XAUTHCMDPATH) endif ifeq "$(CMD_OWNER)" "" CMD_OWNER=$(OAROWNER) endif ifeq "$(CMD_USERTOBECOME)" "" CMD_USERTOBECOME=$(OARUSER) endif ifeq "$(CMD_GROUP)" "" CMD_GROUP=$(OAROWNERGROUP) endif ifeq "$(CMD_RIGHTS)" "" CMD_RIGHTS=6750 endif ifeq "$(CMD_WRAPPER)" "" echo "no CMD_WRAPPER given. Fail !" exit 1 endif CMD_BUILDTARGET=Makefiles/oardo/build/$(subst /,%,$(CMD_TARGET)) clean: -rm -rf $$(dirname $(CMD_BUILDTARGET)) build: $(CMD_BUILDTARGET) # Nothing to do $(CMD_BUILDTARGET).c: mkdir -p $$(dirname $(CMD_BUILDTARGET)) perl -pe "s#define OARDIR .*#define OARDIR \"$(OARDIR)\"#;;\ s#define OARCONFFILE .*#define OARCONFFILE \"$(OARCONFFILE)\"#;;\ s#define OARXAUTHLOCATION .*#define OARXAUTHLOCATION \"$(OARXAUTHLOCATION)\"#;;\ s#define USERTOBECOME .*#define USERTOBECOME \"$(CMD_USERTOBECOME)\"#;;\ s#define PATH2SET .*#define PATH2SET \"/bin:/sbin:/usr/bin:/usr/sbin:$(BINDIR):$(SBINDIR):$(OARDIR)/oardodo\"#;;\ s#define CMD_WRAPPER .*#define CMD_WRAPPER \"$(CMD_WRAPPER)\"#;;\ " sources/core/tools/oardo.c > "$(CMD_BUILDTARGET).c" $(CMD_BUILDTARGET): $(CMD_BUILDTARGET).c ifeq "$(CMD_TARGET)" "" echo "no CMD_TARGET given. Fail !" exit 1 endif $(CC) $(CFLAGS) $(LDFLAGS) $(CPPFLAGS) -o $(CMD_BUILDTARGET) "$(CMD_BUILDTARGET).c" install: $(CMD_TARGET) $(CMD_TARGET): $(CMD_BUILDTARGET) install -d `dirname $(CMD_TARGET)` install -m 0750 $(CMD_BUILDTARGET) $(CMD_TARGET) uninstall: ifeq "$(CMD_TARGET)" "" echo "no CMD_TARGET given. Fail !" exit 1 endif rm -f $(CMD_TARGET) .PHONY: install setup uninstall build clean ./oar-2.5.2/Makefiles/node.mk0000644000175000017500000000217311757171206013742 0ustar plbplbMODULE=node SRCDIR=sources/core OARDIR_BINFILES=$(SRCDIR)/tools/oarnodecheck/oarnodecheckrun.in BINDIR_FILES=$(SRCDIR)/tools/oarnodecheck/oarnodechecklist.in \ $(SRCDIR)/tools/oarnodecheck/oarnodecheckquery.in EXAMPLEDIR_FILES= $(SRCDIR)/scripts/prologue \ $(SRCDIR)/scripts/epilogue \ $(SRCDIR)/tools/sshd_config.in MANDIR_FILES = $(SRCDIR)/man/man1/oarnodechecklist.1 \ $(SRCDIR)/man/man1/oarnodecheckquery.1 INITDIR_FILES = setup/init.d/oar-node.in CRONDIR_FILES = setup/cron.d/oar-node.in DEFAULTDIR_FILES = setup/default/oar-node.in \ setup/default/oar-node.exemple1.in include Makefiles/shared/shared.mk build: build_shared $(MAKE) -f Makefiles/man.mk build clean: clean_shared $(MAKE) -f Makefiles/man.mk clean install: install_shared install -d $(DESTDIR)$(OARCONFDIR)/check.d install -d $(DESTDIR)$(DOCDIR)/oarnodecheck install -m 0644 sources/core/tools/oarnodecheck/README $(DESTDIR)$(DOCDIR)/oarnodecheck install -m 0644 sources/core/tools/oarnodecheck/template $(DESTDIR)$(DOCDIR)/oarnodecheck uninstall: uninstall_shared .PHONY: install setup uninstall build clean ./oar-2.5.2/Makefiles/monika.mk0000644000175000017500000000074511757171206014276 0ustar plbplbMODULE=monika SRCDIR=sources/visualization_interfaces/Monika OAR_PERLLIB=$(SRCDIR)/lib EXAMPLEDIR_FILES= $(SRCDIR)/monika.conf.in \ $(SRCDIR)/userInfos.cgi CGIDIR_FILES = $(SRCDIR)/monika.cgi.in WWWDIR_FILES = $(SRCDIR)/monika.css include Makefiles/shared/shared.mk clean: clean_shared # Nothing to do build: build_shared # Nothing to do install: install_shared # Nothing to do uninstall: uninstall_shared # Nothing to do .PHONY: install setup uninstall build clean ./oar-2.5.2/Makefiles/man.mk0000644000175000017500000000034711757171206013571 0ustar plbplbMODULE=man MANDIR_FILES=$(wildcard sources/core/man/man1/*.pod) $(wildcard sources/core/man/man1/*.pod.in) include Makefiles/shared/shared.mk clean: clean_shared build: build_shared .PHONY: install setup uninstall build clean ./oar-2.5.2/Makefiles/keyring.mk0000644000175000017500000000067611757171206014473 0ustar plbplbMODULE=keyring include Makefiles/shared/shared.mk clean: clean_shared # Nothing to do build: build_shared # Nothing to do uninstall: uninstall_shared # Nothing to do install: install_shared install -d $(DESTDIR)/usr/share/oar-keyring install -m 0644 misc/apt_keyring/oar.gpg $(DESTDIR)/usr/share/oar-keyring install -m 0644 misc/apt_keyring/oarmaster.gpg $(DESTDIR)/usr/share/oar-keyring .PHONY: install setup uninstall build clean ./oar-2.5.2/Makefiles/draw-gantt.mk0000644000175000017500000000214411757171206015063 0ustar plbplbMODULE=draw-gantt SRCDIR=sources/visualization_interfaces/DrawGantt EXAMPLEDIR_FILES=$(SRCDIR)/drawgantt.conf PROCESS_TEMPLATE_FILES= $(SRCDIR)/drawgantt.conf.in \ $(SRCDIR)/drawgantt.cgi.in include Makefiles/shared/shared.mk clean: clean_shared # Nothing to do build: build_shared # Nothing to do install: install_shared install -d $(DESTDIR)$(CGIDIR) install -m 0755 $(SRCDIR)/drawgantt.cgi $(DESTDIR)$(CGIDIR) install -d $(DESTDIR)$(WWWDIR)/drawgantt-files/Icons install -m 0644 $(SRCDIR)/Icons/*.png $(DESTDIR)$(WWWDIR)/drawgantt-files/Icons install -d $(DESTDIR)$(WWWDIR)/drawgantt-files/js install -m 0644 $(SRCDIR)/js/*.js $(DESTDIR)$(WWWDIR)/drawgantt-files/js uninstall: uninstall_shared rm -f \ $(DESTDIR)$(CGIDIR)/drawgantt.cgi \ $(DESTDIR)$(WWWDIR)/drawgantt-files/Icons/*.png \ $(DESTDIR)$(WWWDIR)/drawgantt-files/js/*.js -rmdir \ $(DESTDIR)$(OARHOMEDIR)/drawgantt-files/cache \ $(DESTDIR)$(WWWDIR)/drawgantt-files/js \ $(DESTDIR)$(WWWDIR)/drawgantt-files/Icons \ $(DESTDIR)$(WWWDIR)/drawgantt-files .PHONY: install setup uninstall build clean ./oar-2.5.2/Makefiles/doc.mk0000644000175000017500000000453011757171206013561 0ustar plbplbMODULE=doc SRCDIR= include Makefiles/shared/shared.mk clean: clean_shared $(MAKE) -C docs/documentation clean rm -f docs/documentation/oar-documentation-devel.html build: build_shared build-html-doc install: build install_shared install -d $(DESTDIR)$(DOCDIR) install -d $(DESTDIR)$(DOCDIR)/html install -d $(DESTDIR)$(DOCDIR)/scripts/prologue_epilogue install -d $(DESTDIR)$(DOCDIR)/scripts install -d $(DESTDIR)$(DOCDIR)/scripts/job_resource_manager install -m 0644 docs/documentation/OAR-DOCUMENTATION-USER.html $(DESTDIR)$(DOCDIR)/html install -m 0644 docs/documentation/OAR-DOCUMENTATION-ADMIN.html $(DESTDIR)$(DOCDIR)/html install -m 0644 docs/documentation/OAR-DOCUMENTATION-API-USER.html $(DESTDIR)$(DOCDIR)/html install -m 0644 docs/documentation/OAR-DOCUMENTATION-API-ADMIN.html $(DESTDIR)$(DOCDIR)/html install -m 0644 docs/documentation/OAR-DOCUMENTATION-API-DEVEL.html $(DESTDIR)$(DOCDIR)/html install -m 0644 docs/schemas/oar_logo.png $(DESTDIR)$(DOCDIR)/html install -m 0644 docs/schemas/db_scheme.png $(DESTDIR)$(DOCDIR)/html install -m 0644 docs/schemas/interactive_oarsub_scheme.png $(DESTDIR)$(DOCDIR)/html install -m 0644 docs/schemas/Almighty.fig $(DESTDIR)$(DOCDIR)/html install -m 0644 docs/schemas/Almighty.ps $(DESTDIR)$(DOCDIR)/html install -m 0644 sources/core/tools/job_resource_manager.pl $(DESTDIR)$(DOCDIR)/scripts/job_resource_manager/ install -m 0644 sources/core/scripts/oar_prologue $(DESTDIR)$(DOCDIR)/scripts/prologue_epilogue/ install -m 0644 sources/core/scripts/oar_epilogue $(DESTDIR)$(DOCDIR)/scripts/prologue_epilogue/ install -m 0644 sources/core/scripts/oar_prologue_local $(DESTDIR)$(DOCDIR)/scripts/prologue_epilogue/ install -m 0644 sources/core/scripts/oar_epilogue_local $(DESTDIR)$(DOCDIR)/scripts/prologue_epilogue/ install -m 0644 sources/core/scripts/oar_diffuse_script $(DESTDIR)$(DOCDIR)/scripts/prologue_epilogue/ install -m 0644 sources/core/scripts/lock_user.sh $(DESTDIR)$(DOCDIR)/scripts/prologue_epilogue/ install -m 0644 sources/core/scripts/oar_server_proepilogue.pl $(DESTDIR)$(DOCDIR)/scripts/prologue_epilogue/ uninstall: uninstall_shared rm -rf \ $(DESTDIR)$(DOCDIR)/html \ $(DESTDIR)$(DOCDIR)/scripts/job_resource_manager/ \ $(DESTDIR)$(DOCDIR)/scripts/prologue_epilogue/ build-html-doc: $(MAKE) -C docs/documentation all .PHONY: install setup uninstall build clean ./oar-2.5.2/Makefiles/desktop-computing-cgi.mk0000644000175000017500000000300011757171206017217 0ustar plbplbMODULE=desktop-computing-cgi SRCDIR=sources/desktop_computing OARDIR_BINFILES = $(SRCDIR)/oarcache.pl \ $(SRCDIR)/oarres.pl \ $(SRCDIR)/oar-cgi.pl CRONHOURLYDIR_FILES = setup/cron.hourly/oar-desktop-computing-cgi.in MANDIR_FILES = $(SRCDIR)/agent/man/man1/oarcache.pod include Makefiles/shared/shared.mk clean: clean_shared $(OARDO_CLEAN) CMD_WRAPPER=$(OARDIR)/oarcache.pl CMD_TARGET=$(DESTDIR)$(SBINDIR)/oarcache $(OARDO_CLEAN) CMD_WRAPPER=$(OARDIR)/oarres.pl CMD_TARGET=$(DESTDIR)$(OARDIR)/oarres $(OARDO_CLEAN) CMD_WRAPPER=$(OARDIR)/oar-cgi.pl CMD_TARGET=$(DESTDIR)$(CGIDIR)/oar-cgi build: build_shared $(OARDO_BUILD) CMD_WRAPPER=$(OARDIR)/oarcache.pl CMD_TARGET=$(DESTDIR)$(SBINDIR)/oarcache $(OARDO_BUILD) CMD_WRAPPER=$(OARDIR)/oarres.pl CMD_TARGET=$(DESTDIR)$(OARDIR)/oarres $(OARDO_BUILD) CMD_WRAPPER=$(OARDIR)/oar-cgi.pl CMD_TARGET=$(DESTDIR)$(CGIDIR)/oar-cgi install: install_shared $(OARDO_INSTALL) CMD_WRAPPER=$(OARDIR)/oarcache.pl CMD_TARGET=$(DESTDIR)$(SBINDIR)/oarcache $(OARDO_INSTALL) CMD_WRAPPER=$(OARDIR)/oarres.pl CMD_TARGET=$(DESTDIR)$(OARDIR)/oarres $(OARDO_INSTALL) CMD_WRAPPER=$(OARDIR)/oar-cgi.pl CMD_TARGET=$(DESTDIR)$(CGIDIR)/oar-cgi uninstall: uninstall_shared $(OARDO_UNINSTALL) CMD_WRAPPER=$(OARDIR)/oarcache.pl CMD_TARGET=$(DESTDIR)$(SBINDIR)/oarcache $(OARDO_UNINSTALL) CMD_WRAPPER=$(OARDIR)/oarres.pl CMD_TARGET=$(DESTDIR)$(OARDIR)/oarres $(OARDO_UNINSTALL) CMD_WRAPPER=$(OARDIR)/oar-cgi.pl CMD_TARGET=$(DESTDIR)$(CGIDIR)/oar-cgi .PHONY: install setup uninstall build clean ./oar-2.5.2/Makefiles/desktop-computing-agent.mk0000644000175000017500000000224011757171206017560 0ustar plbplbMODULE=desktop-computing-agent SRCDIR=sources/desktop_computing/agent AGENTDIR_FILES = $(SRCDIR)/lib/job.rb \ $(SRCDIR)/lib/client.rb \ $(SRCDIR)/lib/config.rb \ $(SRCDIR)/lib/job_execution_exception.rb \ $(SRCDIR)/lib/job_resource.rb INITDIR_FILES = setup/init.d/oar-desktop-computing-agent.in MANDIR_FILES = $(SRCDIR)/man/man1/oar-agent.pod \ $(SRCDIR)/man/man1/oar-agent-daemon.pod include Makefiles/shared/shared.mk build: build_shared # Nothing to do clean: clean_shared # Nothing to do install: install_shared install -d $(DESTDIR)$(OARDIR)/desktop_computing install -m 0644 $(AGENTDIR_FILES) $(DESTDIR)$(OARDIR)/desktop_computing install -d $(DESTDIR)$(BINDIR) install -m 0755 $(SRCDIR)/lib/agent.rb $(DESTDIR)$(BINDIR)/oar-agent install -d $(DESTDIR)$(SBINDIR) install -m 0755 $(SRCDIR)/lib/daemon.rb $(DESTDIR)$(SBINDIR)/oar-agent-daemon uninstall: uninstall_shared for file in $(AGENTDIR_FILES); do rm -f $(DESTDIR)$(OARDIR)/desktop_computing/`basename $$file`; done rm -f $(DESTDIR)$(BINDIR)/oar-agent rm -f $(DESTDIR)$(SBINDIR)/oar-agent-daemon rm -rf $(DESTDIR)$(EXAMPLEDIR) .PHONY: install setup uninstall build clean ./oar-2.5.2/Makefiles/database.mk0000644000175000017500000000105011757171206014552 0ustar plbplbMODULE=database SRCDIR=sources/core MANDIR_FILES = $(SRCDIR)/man/man1/oar-database.pod.in SBINDIR_FILES = $(SRCDIR)/database/oar-database.in include Makefiles/shared/shared.mk clean: clean_shared build: build_shared install: install_shared install -d $(DESTDIR)$(OARDIR)/database/ cp -f $(SRCDIR)/database/*.sql $(DESTDIR)$(OARDIR)/database/ uninstall: uninstall_shared rm -f $(DESTDIR)$(OARDIR)/oar_mysql_db_init rm -f $(DESTDIR)$(OARDIR)/oar_psql_db_init rm -rf $(DESTDIR)$(OARDIR)/database .PHONY: install setup uninstall build clean ./oar-2.5.2/Makefiles/common.mk0000644000175000017500000000453411757171206014310 0ustar plbplbMODULE=common SRCDIR=sources/core OARDIR_BINFILES = $(SRCDIR)/tools/oarsh/oarsh_shell.in \ $(SRCDIR)/tools/oarsh/oarsh.in \ $(SRCDIR)/qfunctions/oarnodesetting \ $(SRCDIR)/tools/sentinelle.pl MANDIR_FILES = $(SRCDIR)/man/man1/oarsh.1 \ $(SRCDIR)/man/man1/oarprint.1 \ $(SRCDIR)/man/man1/oarnodesetting.1 EXAMPLEDIR_FILES = $(SRCDIR)/tools/oar.conf.in \ $(SRCDIR)/tools/oarnodesetting_ssh.in \ $(SRCDIR)/tools/update_cpuset_id.sh.in LOGROTATEDIR_FILES = setup/logrotate.d/oar-common.in PROCESS_TEMPLATE_FILES = $(SRCDIR)/tools/oarsh/oarcp.in \ $(SRCDIR)/tools/oarsh/oarsh_sudowrapper.sh.in \ $(SRCDIR)/tools/oardodo.c.in include Makefiles/shared/shared.mk clean: clean_shared $(MAKE) -f Makefiles/man.mk clean $(OARDO_CLEAN) CMD_WRAPPER=$(OARDIR)/oarsh CMD_TARGET=$(DESTDIR)$(OARDIR)/oarsh_oardo $(OARDO_CLEAN) CMD_WRAPPER=$(OARDIR)/oarnodesetting CMD_TARGET=$(DESTDIR)$(SBINDIR)/oarnodesetting -rm -f $(SRCDIR)/tools/oardodo build: build_shared $(MAKE) -f Makefiles/man.mk build $(OARDO_BUILD) CMD_WRAPPER=$(OARDIR)/oarsh CMD_TARGET=$(DESTDIR)$(OARDIR)/oarsh_oardo $(OARDO_BUILD) CMD_WRAPPER=$(OARDIR)/oarnodesetting CMD_TARGET=$(DESTDIR)$(SBINDIR)/oarnodesetting $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $(SRCDIR)/tools/oardodo $(SRCDIR)/tools/oardodo.c install: install_shared $(OARDO_INSTALL) CMD_WRAPPER=$(OARDIR)/oarsh CMD_TARGET=$(DESTDIR)$(OARDIR)/oarsh_oardo $(OARDO_INSTALL) CMD_WRAPPER=$(OARDIR)/oarnodesetting CMD_TARGET=$(DESTDIR)$(SBINDIR)/oarnodesetting install -d $(DESTDIR)$(BINDIR) install -m 0755 $(SRCDIR)/tools/oarsh/oarsh_sudowrapper.sh $(DESTDIR)$(BINDIR)/oarsh install -m 0755 $(SRCDIR)/tools/oarsh/oarcp $(DESTDIR)$(BINDIR)/ install -m 0755 $(SRCDIR)/qfunctions/oarprint $(DESTDIR)$(BINDIR) install -d $(DESTDIR)$(OARDIR)/oardodo install -m 0754 $(SRCDIR)/tools/oardodo $(DESTDIR)$(OARDIR)/oardodo cp -f $(DESTDIR)$(MANDIR)/man1/oarsh.1 $(DESTDIR)$(MANDIR)/man1/oarcp.1 uninstall: uninstall_shared $(OARDO_UNINSTALL) CMD_WRAPPER=$(OARDIR)/oarsh CMD_TARGET=$(DESTDIR)$(OARDIR)/oarsh_oardo $(OARDO_UNINSTALL) CMD_WRAPPER=$(OARDIR)/oarnodesetting CMD_TARGET=$(DESTDIR)$(SBINDIR)/oarnodesetting rm -f $(DESTDIR)$(MANDIR)/man1/oarcp.1 rm -rf $(DESTDIR)$(OARDIR)/oardodo rm -rf $(DESTDIR)$(EXAMPLEDIR) .PHONY: install setup uninstall build clean ./oar-2.5.2/Makefiles/common-libs.mk0000644000175000017500000000051011757171206015225 0ustar plbplbMODULE=common-libs SRCDIR=sources/core OAR_PERLLIB= $(SRCDIR)/common-libs/lib include Makefiles/shared/shared.mk clean: clean_shared # Nothing to do build: build_shared # Nothing to do install: install_shared # Nothing to do uninstall: uninstall_shared # Nothing to do .PHONY: install setup uninstall build clean ./oar-2.5.2/Makefiles/api.mk0000644000175000017500000000261111757171206013563 0ustar plbplbMODULE=api SRCDIR= sources/api OAR_PERLLIB = $(SRCDIR)/lib OARDIR_BINFILES = $(SRCDIR)/oarapi.pl EXAMPLEDIR_FILES = $(SRCDIR)/oarapi_examples.txt \ $(SRCDIR)/chandler.rb \ $(SRCDIR)/apache2.conf.in \ $(SRCDIR)/api_html_header.pl \ $(SRCDIR)/api_html_postform.pl \ $(SRCDIR)/api_html_postform_resources.pl \ $(SRCDIR)/api_html_postform_rule.pl \ $(SRCDIR)/INSTALL \ $(SRCDIR)/TODO include Makefiles/shared/shared.mk clean: clean_shared $(OARDO_CLEAN) CMD_WRAPPER=$(OARDIR)/oarapi.pl CMD_TARGET=$(DESTDIR)$(CGIDIR)/oarapi/oarapi.cgi $(OARDO_CLEAN) CMD_WRAPPER=$(OARDIR)/oarapi.pl CMD_TARGET=$(DESTDIR)$(CGIDIR)/oarapi/oarapi-debug.cgi build: build_shared $(OARDO_BUILD) CMD_WRAPPER=$(OARDIR)/oarapi.pl CMD_TARGET=$(DESTDIR)$(CGIDIR)/oarapi/oarapi.cgi $(OARDO_BUILD) CMD_WRAPPER=$(OARDIR)/oarapi.pl CMD_TARGET=$(DESTDIR)$(CGIDIR)/oarapi/oarapi-debug.cgi install: install_shared $(OARDO_INSTALL) CMD_WRAPPER=$(OARDIR)/oarapi.pl CMD_TARGET=$(DESTDIR)$(CGIDIR)/oarapi/oarapi.cgi $(OARDO_INSTALL) CMD_WRAPPER=$(OARDIR)/oarapi.pl CMD_TARGET=$(DESTDIR)$(CGIDIR)/oarapi/oarapi-debug.cgi uninstall: uninstall_shared $(OARDO_UNINSTALL) CMD_WRAPPER=$(OARDIR)/oarapi.pl CMD_TARGET=$(DESTDIR)$(CGIDIR)/oarapi/oarapi.cgi $(OARDO_UNINSTALL) CMD_WRAPPER=$(OARDIR)/oarapi.pl CMD_TARGET=$(DESTDIR)$(CGIDIR)/oarapi/oarapi-debug.cgi .PHONY: install setup uninstall build clean ./oar-2.5.2/Makefile0000644000175000017500000001574711757171206012237 0ustar plbplb#/usr/bin/make # $Id$ export SHELL=/bin/bash # Modules that can be builded MODULES = server user node monika draw-gantt doc desktop-computing-agent desktop-computing-cgi tools api scheduler-ocaml www-conf common common-libs database MODULES_LIST= $(patsubst %,% |, $(MODULES))| OPTIONS_LIST= OARCONFDIR | OARUSER | OAROWNER | PREFIX | MANDIR | OARDIR | BINDIR | SBINDIR | DOCDIR # Define the makefile targets TARGETS_SUFFIX = build clean install uninstall TARGETS_BUILD = $(MODULES:=-build) TARGETS_CLEAN = $(MODULES:=-clean) TARGETS_INSTALL = $(MODULES:=-install) TARGETS_SETUP = $(MODULES:=-setup) TARGETS_UNINSTALL = $(MODULES:=-uninstall) TARGETS = $(TARGETS_BUILD) $(TARGETS_CLEAN) $(TARGETS_INSTALL) $(TARGETS_UNINSTALL) $(TARGETS_SETUP) all: usage build: $(filter-out scheduler-ocaml% , $(TARGETS_BUILD)) install: $(filter-out scheduler-ocaml% , $(TARGETS_INSTALL)) clean: $(filter-out scheduler-ocaml% , $(TARGETS_CLEAN)) uninstall: $(filter-out scheduler-ocaml% , $(TARGETS_UNINSTALL)) setup: $(filter-out scheduler-ocaml% , $(TARGETS_SETUP)) tarball: .git ./misc/make_tarball .git: @echo "Must be used from a git repository!" exit 1 usage: @echo "Usage: make [ OPTIONS=<...> ] [MODULES-]{install|build|clean|uninstall|setup}" @echo "" @echo "Where MODULES := { $(MODULES_LIST:||=) }" @echo "" @echo " OPTIONS := { $(OPTIONS_LIST) }" sanity-check: @[ "`id root`" = "`id`" ] || echo "Warning: root-privileges are required to install some files !" sanity-setup-check: sanity-check @id $(OAROWNER) > /dev/null || ( echo "Error: User $(OAROWNER) does not exist!" ; exit -1 ) # Meta targets $(TARGETS_BUILD): MODULE = $(patsubst %-build,%,$@) $(TARGETS_BUILD): ACTION = build $(TARGETS_INSTALL): MODULE = $(patsubst %-install,%,$@) $(TARGETS_INSTALL): ACTION = install $(TARGETS_SETUP): MODULE = $(patsubst %-setup,%,$@) $(TARGETS_SETUP): ACTION = setup $(TARGETS_UNINSTALL): MODULE = $(patsubst %-uninstall,%,$@) $(TARGETS_UNINSTALL): ACTION = uninstall $(TARGETS_CLEAN): MODULE = $(patsubst %-clean,%,$@) $(TARGETS_CLEAN): ACTION = clean $(TARGETS_INSTALL): sanity-check $(MAKE) $(strip $(MODULE))-build $(MAKE) -f Makefiles/$(strip $(MODULE)).mk install $(TARGETS_UNINSTALL): $(MAKE) -f Makefiles/$(strip $(MODULE)).mk uninstall $(TARGETS_CLEAN): $(MAKE) -f Makefiles/$(strip $(MODULE)).mk clean $(TARGETS_BUILD): $(MAKE) -f Makefiles/$(strip $(MODULE)).mk build $(TARGETS_SETUP): -$(MAKE) -s -f Makefiles/$(strip $(MODULE)).mk setup # Dependencies server-setup: common-setup common-libs-setup database-setup server-install: sanity-check common-install common-libs-install database-install server-clean: common-clean common-libs-clean database-clean server-build: common-build common-libs-build database-build server-uninstall: common-uninstall common-libs-uninstall database-uninstall user-setup: common-setup common-libs-setup user-install: sanity-check common-install common-libs-install user-clean: common-clean common-libs-clean user-build: common-build common-libs-build user-uninstall: common-uninstall common-libs-uninstall node-setup: common-setup node-install: sanity-check common-install node-clean: common-clean node-build: common-build node-uninstall: common-uninstall draw-gantt-setup: www-conf-setup draw-gantt-install: www-conf-install draw-gantt-clean: www-conf-clean draw-gantt-build: www-conf-build draw-gantt-uninstall: www-conf-uninstall monika-setup: www-conf-setup monika-install: www-conf-install monika-clean: www-conf-clean monika-build: www-conf-build monika-uninstall: www-conf-uninstall desktop-computing-cgi-setup: common-setup common-libs-setup desktop-computing-cgi-install: sanity-check common-install common-libs-install desktop-computing-cgi-clean: common-clean common-libs-clean desktop-computing-cgi-build: common-build common-libs-build desktop-computing-cgi-uninstall: common-uninstall common-libs-uninstall tools-setup: common-setup common-libs-setup tools-install: sanity-check common-install common-libs-install tools-clean: common-clean common-libs-clean tools-build: common-build common-libs-build tools-uninstall: common-uninstall common-libs-uninstall api-setup: common-setup common-libs-setup api-install: sanity-check common-install common-libs-install api-build: common-build common-libs-build api-clean: common-clean common-libs-clean api-uninstall: common-uninstall common-libs-uninstall P_ACTIONS = build install clean P_TARGETS = $(patsubst %,packages-%,$(P_ACTIONS)) packages-build: P_ACTION = build packages-install: P_ACTION = install packages-clean: P_ACTION = clean $(P_TARGETS): # oar-doc $(MAKE) -f Makefiles/doc.mk $(P_ACTION) \ DESTDIR=$(PACKAGES_DIR)/oar-doc # oar-common mkdir -p $(PACKAGES_DIR)/oar-common/var/lib/oar $(MAKE) -f Makefiles/common.mk $(P_ACTION) \ DESTDIR=$(PACKAGES_DIR)/oar-common # liboar-perl mkdir -p $(PACKAGES_DIR)/liboar-perl/var/lib/oar $(MAKE) -f Makefiles/common-libs.mk $(P_ACTION) \ DESTDIR=$(PACKAGES_DIR)/liboar-perl # oar-server mkdir -p $(PACKAGES_DIR)/oar-server/var/lib/oar $(MAKE) -f Makefiles/server.mk $(P_ACTION)\ DESTDIR=$(PACKAGES_DIR)/oar-server $(MAKE) -f Makefiles/database.mk $(P_ACTION)\ DESTDIR=$(PACKAGES_DIR)/oar-server \ DOCDIR=/usr/share/doc/oar-server # oar-node mkdir -p $(PACKAGES_DIR)/oar-node/var/lib/oar mkdir -p $(PACKAGES_DIR)/oar-node/etc/init.d $(MAKE) -f Makefiles/node.mk $(P_ACTION)\ DESTDIR=$(PACKAGES_DIR)/oar-node # oar-user mkdir -p $(PACKAGES_DIR)/oar-user/var/lib/oar $(MAKE) -f Makefiles/user.mk $(P_ACTION)\ DESTDIR=$(PACKAGES_DIR)/oar-user # oar-web-status $(MAKE) -f Makefiles/monika.mk $(P_ACTION) \ DESTDIR=$(PACKAGES_DIR)/oar-web-status \ DOCDIR=/usr/share/doc/oar-web-status \ WWWDIR=/usr/share/oar-web-status $(MAKE) -f Makefiles/draw-gantt.mk $(P_ACTION) \ DESTDIR=$(PACKAGES_DIR)/oar-web-status \ DOCDIR=/usr/share/doc/oar-web-status \ WWWDIR=/usr/share/oar-web-status $(MAKE) -f Makefiles/www-conf.mk $(P_ACTION) \ DESTDIR=$(PACKAGES_DIR)/oar-web-status \ DOCDIR=/usr/share/doc/oar-web-status \ WWWDIR=/usr/share/oar-web-status # oar-admin $(MAKE) -f Makefiles/tools.mk $(P_ACTION) \ DESTDIR=$(PACKAGES_DIR)/oar-admin \ DOCDIR=/usr/share/doc/oar-admin # oar-desktop-computing-agent $(MAKE) -f Makefiles/desktop-computing-agent.mk $(P_ACTION) \ DESTDIR=$(PACKAGES_DIR)/oar-desktop-computing-agent # oar-desktop-computing-cgi $(MAKE) -f Makefiles/desktop-computing-cgi.mk $(P_ACTION) \ DESTDIR=$(PACKAGES_DIR)/oar-desktop-computing-cgi # oar-restful-api $(MAKE) -f Makefiles/api.mk $(P_ACTION) \ DOCDIR=/usr/share/doc/oar-restful-api \ DESTDIR=$(PACKAGES_DIR)/oar-restful-api # keyring $(MAKE) -f Makefiles/keyring.mk $(P_ACTION) \ DESTDIR=$(PACKAGES_DIR)/oar-keyring # scheduler-ocaml-mysql #$(MAKE) -f Makefiles/scheduler-ocaml.mk $(P_ACTION) \ # DESTDIR=$(PACKAGES_DIR)/oar-scheduler-ocaml-mysql ./oar-2.5.2/INSTALL0000644000175000017500000005657011757171206011627 0ustar plbplbInstalling the OAR batch system =============================== Overview -------- There are currently 3 methods to install OAR (All of them are documented in this page) : - with the debian packages - with the rpm packages - with the sources The first thing you have to know is about the OAR architecture. A common OAR installation is composed of: - a **server node** which will hold all of OAR "smartness". This node will run the oar server daemon; - **frontend nodes** on which you will be allowed to login, then reserve some computing nodes (oarsub, oarstat, oarnodes, ...); - several **computing nodes** (a.k.a. the nodes), on which the jobs will run. - and optionally a **visualisation node** on which all the visualisation web interfaces (monika, draw-gantt, ...) will be accessible ; Computing nodes --------------- Installation from the packages ______________________________ **Instructions** *For redhat like systems*:: # Add the oar repository cat < /etc/yum.repos.d/oar.repo [oar] name=OAR Packages for Enterprise Linux 6 - \$basearch baseurl=http://oar-ftp.imag.fr/oar/2.5/rpm/stable/ enabled=1 gpgcheck=0 EOF # Install OAR node yum install oar-node *For the debian like systems*:: # Add the OAR repository (choose the right one. See http://oar.imag.fr/repositories/) echo "deb http://oar-ftp.imag.fr/oar/2.5/debian squeeze main" > /etc/apt/sources.list.d/oar.list curl http://oar-ftp.imag.fr/oar/oarmaster.asc | sudo apt-key add - apt-get update # Install OAR node apt-get install oar-node Installation from the tarball _____________________________ **Requirements** *For redhat like systems*:: # Build dependencies yum install gcc make tar python-docutils # Common dependencies yum install Perl Perl-base openssh *For debian like system*:: # Build dependencies apt-get install gcc make tar python-docutils # Common dependencies apt-get install perl perl-base openssh-client openssh-server **Instructions** Get the sources:: OAR_VERSION=2.5.2 curl http://oar-ftp.imag.fr/oar/2.5/sources/stable/oar-${OAR_VERSION}.tgz | tar xzvf - cd oar-${OAR_VERSION}/ build/install/setup:: # build make node-build # install make node-install # setup make node-setup Configuration _____________ oar node ssh access ~~~~~~~~~~~~~~~~~~~ You need to ensure that the oar user can access to each nodes through ssh. To ensure that, you can just copy the ``/var/lib/oar/.ssh`` folder from the oar server to each nodes (ensure that ``/var/lib/oar/.ssh`` has the right permissions). Init.d scripts ~~~~~~~~~~~~~~ If you have installed OAR from sources, you need to become root user and install manually the {init.d,default,sysconfig} scripts present in the folders:: $PREFIX/share/doc/oar-node/examples/scripts/{init.d,default,sysconfig} Then you just need to use the script ``/etc/init.d/oar-node`` to start the ssh daemon dedicated to oar-node. Server node ----------- Installation from the packages ______________________________ **Instructions** *For redhat like systems*:: # Add the epel repository (choose the right version depending on your operating system) rpm -i http://download.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-5.noarch.rpm # Add the oar repository cat < /etc/yum.repos.d/oar.repo [oar] name=OAR Packages for Enterprise Linux 6 - \$basearch baseurl=http://oar-ftp.imag.fr/oar/2.5/rpm/stable/ enabled=1 gpgcheck=0 EOF # Install OAR server for the PostgreSQL backend yum install oar-server oar-server-pgsql # or Install OAR server for the MySQL backend yum install oar-server oar-server-mysql *For the debian like systems*:: # Add the OAR repository (choose the right one. See http://oar.imag.fr/repositories/) echo "deb http://oar-ftp.imag.fr/oar/2.5/debian squeeze main" > /etc/apt/sources.list.d/oar.list curl http://oar-ftp.imag.fr/oar/oarmaster.asc | sudo apt-key add - apt-get update # Install OAR server for the PostgreSQL backend apt-get install oar-server oar-server-pgsql # or Install OAR server for the MySQL backend apt-get install oar-server oar-server-mysql Installation from the tarball _____________________________ **Requirements** *For redhat like systems*:: # Add the epel repository (choose the right version depending on your operating system) rpm -i http://download.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-5.noarch.rpm # Build dependencies yum install gcc make tar python-docutils # Common dependencies yum install Perl Perl-base openssh Perl-DBI perl-Sort-Versions # MySQL dependencies yum install mysql-server mysql perl-DBD-MySQL # PostgreSQL dependencies yum install postgresql-server postgresql perl-DBD-Pg *For debian like system*:: # Build dependencies apt-get install gcc make tar python-docutils # Common dependencies apt-get install perl perl-base openssh-client openssh-server libdbi-perl libsort-versions-perl # MySQL dependencies apt-get install mysql-server mysql-client libdbd-mysql-perl # PostgreSQL dependencies apt-get install postgresql-server postgresql-client libdbd-pg-perl **Instructions** Get the sources:: OAR_VERSION=2.5.2 curl http://oar-ftp.imag.fr/oar/2.5/sources/stable/oar-${OAR_VERSION}.tgz | tar xzvf - cd oar-${OAR_VERSION}/ Build/Install/Setup the OAR server:: # build make server-build # install make server-install # setup make server-setup Configuration _____________ The oar database ~~~~~~~~~~~~~~~~ Define the database configuration in /etc/oar/oar.conf. You need to set the variables ``DB_TYPE, DB_HOSTNAME, DB_PORT, DB_BASE_NAME, DB_BASE_LOGIN, DB_BASE_PASSWD, DB_BASE_LOGIN_RO, DB_BASE_PASSWD_RO``:: vi /etc/oar/oar.conf Create the database and the database users:: # General case oar-database --create --db-admin-user --db-admin-pass # OR, for PostgreSQL, in case the database is installed locally oar-database --create --db-is-local Init.d scripts ~~~~~~~~~~~~~~ If you have installed OAR from sources, you need to become root user and install manually the init.d/default/sysconfig scripts present in the folders:: $PREFIX/share/doc/oar-server/examples/scripts/{init.d,default,sysconfig} Then use the script ``/etc/init.d/oar-server`` to start the OAR server daemon. Adding resources to the system ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you want to **automatically** initialize your cluster then you just need to launch ``oar_resources_init``. It will detect the resources from the nodes that you put in a file and store right OAR commands to initialize the database with the appropriate values for the memory and the cpuset properties. Just try... There is also a tool to help you managing your oar resources and admission rules : ``oaradmin``. Take a look at the oaradmin documentation in the administrator commands section for more details. You can also read this tips:: http://wiki-oar.imag.fr/index.php/Customization_tips#Using_oaradmin_to_initiate_the_resources *Otherwise:* To add resources to your system, you can use (as root) the command oarnodesetting. For a complete comprehension of what does this command, type man oarnodesetting. For now, the two options you will need will be **-a** (means add a resource) and **-h** (defines the resource hostname or ip adress). For example, to add a computing resource on the node to OAR installation, you can type:: oarnodesetting -a -h This will add a resource with as host IP address. You also can modify resources properties with **-p** option, for example:: oarnodesetting -r 1 -p "deploy=YES" will allow the resource #1 to accept jobs of the type deploy. Notes _____ Security issues ~~~~~~~~~~~~~~~ For security reasons it is hardly **recommended** to configure a read only account for the OAR database (like the above example). Thus you will be able to add this data in DB_BASE_LOGIN_RO and DB_BASE_PASSWD_RO in *oar.conf*. PostgreSQL : autovacuum ~~~~~~~~~~~~~~~~~~~~~~~ Be sure to activate the "autovacuum" feature in the "postgresql.conf" file (OAR creates and deletes a lot of records and this setting cleans the postgres database from unneeded records). PostgreSQL : authentication ~~~~~~~~~~~~~~~~~~~~~~~~~~~ In case you've installed a PostgreSQL database remotly, if your PostgreSQL installation doesn't authorize the local connections by default, you need to enable the connections to this database for the oar users. Supposing the OAR server has the address , you can add the following lines in the ``pg_hba.conf``: # in /etc/postgresql/8.1/main/pg_hba.conf or /var/lib/pgsql/data/pg_hba.conf host oar oar_ro /32 md5 host oar oar /32 md5 About X11 usage in OAR ~~~~~~~~~~~~~~~~~~~~~~ The easiest and scalable way to use X11 application on cluster nodes is to open X11 ports and set the right DISPLAY environment variable by hand. Otherwise users can use X11 forwarding via ssh to access cluster frontal. After that you must configure ssh server on this frontal with :: X11Forwarding yes X11UseLocalhost no With this configuration, users can launch X11 applications after a 'oarsub -I' on the given node or "oarsh -X node12". Using Taktuk ~~~~~~~~~~~~ If you want to use taktuk to manage remote administration commands, you have to install it. You can find information about taktuk from its website: http://taktuk.gforge.inria.fr. **Note**: Taktuk is scalable remote command execution without the need to install special stuffs on nodes. So it is very useful to administer a large amount of server. Then, you have to edit your oar configuration file and to fill in the different related parameters: - TAKTUK_CMD (the path to the taktuk command) - PINGCHECKER_TAKTUK_ARG_COMMAND (the command used to check resources states) - SCHEDULER_NODE_MANAGER_SLEEP_CMD (command used for halting nodes) CPUSET feature ~~~~~~~~~~~~~~ OAR uses the CPUSET features provided with the Linux kernel >= 2.6. This enables to restrict user processes only on reserved processors and to clean correctly the nodes after the end of the jobs. For more information, look at the CPUSET file. Energy saving ~~~~~~~~~~~~~ Starting with version 2.4.3, OAR provides a module responsible of advanced management of wake-up/shut-down of nodes when they are not used. To activate this feature, you have to: - provide 2 commands or scripts which will be executed on the oar server to shutdown (or set into standby) some nodes and to wake-up some nodes (configure the path of those commands into the ENERGY_SAVING_NODE_MANAGER_WAKE_UP_CMD and ENERGY_SAVING_NODE_MANAGER_SHUT_DOWN_CMD variables into oar.conf) - configure the "available_upto" property of all your nodes: - available_upto=0 : to disable the wake-up and halt - available_upto=1 : to disable the wake-up (but not the halt) - available_upto=2147483647 : to disable the halt (but not the wake-up) - available_upto=2147483646 : to enable wake-up/halt forever - available_upto= : to enable the halt, and the wake-up until the date given by - activate the energy saving module by setting ENERGY_SAVING_INTERNAL="yes" and configuring the ENERGY_* variables into oar.conf - configure the metascheduler time values into SCHEDULER_NODE_MANAGER_IDLE_TIME, SCHEDULER_NODE_MANAGER_SLEEP_TIME and SCHEDULER_NODE_MANAGER_WAKEUP_TIME variables of the oar.conf file. - restart the oar server (you should see an "Almighty" process more). You need to restart OAR each time you change an ENERGY_* variable. More informations are available inside the oar.conf file itself. For more details about the mechanism, take a look at the "Hulot" module documentation. Disabling SELinux ~~~~~~~~~~~~~~~~~ On some distributions, SELinux is enabled by default. There is currently no OAR support for SELinux. So, you need to disable SELinux, if enabled. Intel cpuset id issue ~~~~~~~~~~~~~~~~~~~~~ The cpuset ids on an intel platform are not persistent across reboot. So you need to update the cpuset ids in the resource database at startup for each computing node. You can do this by using the ``/etc/oar/update_cpuset_id.sh`` script. The following page give more informations on how configuring it: http://wiki-oar.imag.fr/index.php/Configuration_tips#Start.2Fstop_of_nodes_using_ssh_keys Other issues ~~~~~~~~~~~~ You can take a look at the "Customizaion tips" on the OAR Wiki: http://wiki-oar.imag.fr/index.php/Customization_tips Frontend nodes -------------- Installation from the packages ______________________________ **Instructions** *For redhat like systems*:: # Add the epel repository (choose the right version depending on your operating system) rpm -i http://download.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-5.noarch.rpm # Add the oar repository cat < /etc/yum.repos.d/oar.repo [oar] name=OAR Packages for Enterprise Linux 6 - \$basearch baseurl=http://oar-ftp.imag.fr/oar/2.5/rpm/stable/ enabled=1 gpgcheck=0 EOF # Install OAR user for the PostgreSQL backend yum install oar-user oar-user-pgsql # or Install OAR user for the MySQL backend yum install oar-user oar-user-mysql *For the debian like systems*:: # Add the OAR repository (choose the right one. See http://oar.imag.fr/repositories/) echo "deb http://oar-ftp.imag.fr/oar/2.5/debian squeeze main" > /etc/apt/sources.list.d/oar.list curl http://oar-ftp.imag.fr/oar/oarmaster.asc | sudo apt-key add - apt-get update # Install OAR server for the PostgreSQL backend apt-get install oar-user oar-user-pgsql # or Install OAR server for the MySQL backend apt-get install oar-user oar-user-mysql Installation from the tarball _____________________________ **Requirements** *For redhat like systems*:: # Build dependencies yum install gcc make tar python-docutils # Common dependencies yum install Perl Perl-base openssh Perl-DBI # MySQL dependencies yum install mysql perl-DBD-MySQL # PostgreSQL dependencies yum install postgresql perl-DBD-Pg *For debian like system*:: # Build dependencies apt-get install gcc make tar python-docutils # Common dependencies apt-get install perl perl-base openssh-client openssh-server libdbi-perl # MySQL dependencies apt-get install mysql-client libdbd-mysql-perl # PostgreSQL dependencies apt-get install postgresql-client libdbd-pg-perl **Instructions** Get the sources:: OAR_VERSION=2.5.2 curl http://oar-ftp.imag.fr/oar/2.5/sources/stable/oar-${OAR_VERSION}.tgz | tar xzvf - cd oar-${OAR_VERSION}/ Build/Install/setup:: # build make user-build # install make user-install # setup make user-setup Configuration _____________ Coherent configuration files between server node and user nodes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You need to have a coherent oar configuration between the server node and the user nodes. So you can just copy the /etc/oar directory from to server node to the user nodes. OAR RESTful API Installation ____________________________ Since the version 2.5.2, OAR offers an API for users and admins interactions. This api must be installed on a frontend node (with the user module installed). From the packaging ~~~~~~~~~~~~~~~~~~ *For redhat like systems*:: # Add the oar repository cat < /etc/yum.repos.d/oar.repo [oar] name=OAR Packages for Enterprise Linux 6 - \$basearch baseurl=http://oar-ftp.imag.fr/oar/2.5/rpm/stable/ enabled=1 gpgcheck=0 EOF # Install apache FastCGI module (optional but highly recommended) FIXME: # Install OAR Restful api yum install oar-restful-api *For the debian like systems*:: # Add the OAR repository (choose the right one. See http://oar.imag.fr/repositories/) echo "deb http://oar-ftp.imag.fr/oar/2.5/debian squeeze main" > /etc/apt/sources.list.d/oar.list curl http://oar-ftp.imag.fr/oar/oarmaster.asc | sudo apt-key add - apt-get update # Install apache FastCGI module (optional but highly recommended) apt-get install libapache2-mod-fastcgi # Install OAR Restful api apt-get install oar-restful-api From the sources ~~~~~~~~~~~~~~~~ **Requirements**: *For redhat like systems*:: # Build dependencies yum install gcc make tar python-docutils # Common dependencies yum install perl perl-base perl-DBI perl-CGI perl-JSON perl-YAML perl-libwww-perl httpd # FastCGI dependency (optional but highly recommended) FIXME: # MySQL dependencies yum install mysql perl-DBD-MySQL # PostgreSQL dependencies yum install postgresql perl-DBD-Pg *For debian like system*:: # Build dependencies apt-get install gcc make tar python-docutils # Common dependencies apt-get install perl perl-base libdbi-perl libjson-perl libyaml-perl libwww-perl httpd-cgi libcgi-fast-perl # FastCGI dependency (optional but highly recommended) apt-get install libapache2-mod-fastcgi # MySQL dependencies apt-get install mysql-server mysql-client libdbd-mysql-perl # PostgreSQL dependencies apt-get install postgresql-server postgresql-client libdbd-pg-perl **Instructions** Get the sources:: OAR_VERSION=2.5.2 curl http://oar-ftp.imag.fr/oar/2.5/sources/stable/oar-${OAR_VERSION}.tgz | tar xzvf - cd oar-${OAR_VERSION}/ build/install/setup:: # build make api-build # install make api-install # setup make api-setup Configuration ~~~~~~~~~~~~~ *Configuring OAR* For the moment, the API needs the user tools to be installed on the same host ('``make user-install``' or oar-user package). A suitable ``/etc/oar/oar.conf`` should be present. For the API to work, you should have the oarstat/oarnodes/oarsub commands to work (on the same host you installed the API) *Configuring Apache* The api provides a default configuration file (``/etc/oar/apache-api.conf``) that is using a identd user identification enabled only from localhost. Edit the ``/etc/oar/apache-api.conf`` file and customize it to reflect the authentication mechanism you want to use. For ident, you may have to install a "identd" daemon on your distrib. The steps may be: - Install and run an identd daemon on your server (like *pidentd*). - Activate the ident auth mechanism into apache (``a2enmod ident``). - Activate the headers apache module (``a2enmod headers``). - Activate the rewrite apache module (``a2enmod rewrite``). - Customize apache-api.conf to allow the hosts you trust for ident. *YAML, JSON, XML* You need at least one of the YAML or JSON perl module to be installed on the host running the API. *Test* You may test the API with a simple wget:: wget -O - http://localhost/oarapi/resources.html It should give you the list of resources in the yaml format but enclosed in an html page. To test if the authentication works, you need to post a new job. See the example.txt file that gives you example queries with a ruby rest client. Visualization node ------------------ Description ___________ There are two different tools. One, named Monika which displays the current cluster state with all active and waiting jobs. The other, named drawgantt which displays node occupation in a lapse of time. These tools are CGI scripts and generate HTML pages. Installation from the packages ______________________________ **Instructions** *For redhat like systems*:: # Add the oar repository cat < /etc/yum.repos.d/oar.repo [oar] name=OAR Packages for Enterprise Linux 6 - \$basearch baseurl=http://oar-ftp.imag.fr/oar/2.5/rpm/stable/ enabled=1 gpgcheck=0 EOF yum install oar-web-status *For the debian like systems*:: # Add the OAR repository (choose the right one. See http://oar.imag.fr/repositories/) echo "deb http://oar-ftp.imag.fr/oar/2.5/debian squeeze main" > /etc/apt/sources.list.d/oar.list curl http://oar-ftp.imag.fr/oar/oarmaster.asc | sudo apt-key add - apt-get update apt-get install oar-web-status Installation from the tarball _____________________________ **Requirements**: *For redhat like systems*:: # Build dependencies yum install gcc make tar python-docutils # Common dependencies yum install perl perl-base perl-DBI ruby-GD ruby-DBI perl-Tie-IxHash perl-Sort-Naturally perl-AppConfig # MySQL dependencies yum install mysql perl-DBD-MySQL ruby-mysql # PostgreSQL dependencies yum install postgresql perl-DBD-Pg ruby-pg *For debian like system*:: # Build dependencies apt-get install gcc make tar python-docutils # Common dependencies apt-get install perl perl-base ruby libgd-ruby1.8 libdbi-perl libtie-ixhash-perl libappconfig-perl libsort-naturally-perl # MySQL dependencies apt-get install libdbd-mysql-perl libdbd-mysql-ruby # PostgreSQL dependencies apt-get install libdbd-pg-perl libdbd-pg-ruby **Instructions** Get the sources:: OAR_VERSION=2.5.2 curl http://oar-ftp.imag.fr/oar/2.5/sources/stable/oar-${OAR_VERSION}.tgz | tar xzvf - cd oar-${OAR_VERSION}/ build/install/setup:: # build make monika-build draw-gantt-build www-conf-build # install make monika-install draw-gantt-install www-conf-install # setup make monika-setup draw-gantt-setup www-conf-setup Configuration _____________ **Drawgantt configuration** - Edit ``/etc/oar/drawgantt.conf`` to fit your configuration. **Monika configuration** - Edit ``/etc/oar/monika.conf`` to fit your configuration. **httpd configuration** - You need to edit ``/etc/oar/apache.conf`` to fit your needs and verify that you http server configured. Further informations -------------------- For further information, please check the documentation section on the OAR website http://oar.imag.fr/. ./oar-2.5.2/GUIDELINES0000644000175000017500000000023711757171206012136 0ustar plbplbFor syntax and name conventions see: http://www.perl.com/doc/manual/html/pod/perlstyle.html (for now not the whole code is compatible with these guidelines) ./oar-2.5.2/FAQ-USER0000644000175000017500000000573511757171206011701 0ustar plbplbFAQ - USER ========== Release policy -------------- Since the version 2.2, release numbers are divided into 3 parts: - The first represents the design and the implementation used. - The second represents a set of OAR functionalities. - The third is incremented after bug fixes. How can I submit a moldable job? -------------------------------- You just have to use several "-l" oarsub_ option(one for each moldable description). By default the OAR scheduler will launch the moldable job which will end first. So you can see some free resources but the scheduler can decide to start your job later because they will have more free resources and the job walltime will be smaller. How can I submit a job with a non uniform description? ------------------------------------------------------ Example: :: oarsub -I -l '{switch = "sw1" or switch = "sw5"}/switch=1+/node=1' This example asks OAR to reserve all resources from the switch sw1 or the switch sw2 **and** a node on another switch. You can see the "+" syntax as a sub-reservation directive. Can I perform a fix scheduled reservation and then launch several jobs in it? ----------------------------------------------------------------------------- Yes. You have to use the OAR scheduler "timesharing" feature. To use it, the reservation and your further jobs must be of the type timesharing (only for you). Example: 1. Make your reservation: :: oarsub -r "2006-09-12 8:00:00" -l /switch=1 -t 'timesharing=user,*' This command asks all resources from one switch at the given date for the default walltime. It also specifies that this job can be shared with himself and without a constraint on the job name. 2. Once your reservation has begun then you can launch: :: oarsub -I -l /node=2,walltime=0:50:00 -p 'switch = "nom_du_switch_schedule"'\ -t 'timesharing=user,*' So this job will be scheduled on nodes assigned from the previous reservation. The "timesharing" oarsub_ command possibilities are enumerated in Timesharing_. How can a checkpointable job be resubmitted automatically? ---------------------------------------------------------- You have to specify that your job is *idempotent* and exit from your script with the exit code 99. So, after a successful checkpoint, if the job is resubmitted then all will go right and there will have no problem (like file creation, deletion, ...). Example: :: oarsub --checkpoint 600 --signal 2 -t idempotent /path/to/prog So this job will send a signal *SIGINT* (see *man kill* to know signal numbers) 10 minutes before the walltime ends. Then if everything goes well and the exit code is 99 it will be resubmitted. How to submit a non disturbing job for other users? --------------------------------------------------- You can use the *besteffort* job type. Thus your job will be launched only if there is a hole and will be deleted if another job wants its resources. Example: :: oarsub -t besteffort /path/to/prog ./oar-2.5.2/FAQ-ADMIN0000644000175000017500000002177411757171206011754 0ustar plbplbFAQ - ADMIN =========== Release policy -------------- Since the version 2.2, release numbers are divided into 3 parts: - The first represents the design and the implementation used. - The second represents a set of OAR functionalities. - The third is incremented after bug fixes. What means the error "Bad configuration option: PermitLocalCommand" when I am using oarsh? ------------------------------------------------------------------------------------------ For security reasons, on the latest OpenSSH releases you are able to execute a local command when you are connecting to the remote host and we must deactivate this option because the oarsh_ wrapper executes the *ssh* command into the user oar. So if you encounter this error message it means that your OpenSSH does not know this option and you have to remove it from the oar.conf. There is a variable named OARSH_OPENSSH_DEFAULT_OPTIONS_ in oar.conf used by oarsh. So you have just to remove the not yet implemented option. How to manage start/stop of the nodes? -------------------------------------- You have to add a script in /etc/init.d which switches resources of the node into the "Alive" or "Absent" state. So when this script is called at boot time, it will change the state into "Alive". And when it is called at halt time, it will change into "Absent". There are two ways to perform this action: 1. Install OAR "oar-libs" part on all nodes. Thus you will be able to launch the command oarnodesetting_ (be careful to right configure "oar.conf" with database login and password AND to allow network connections on this database). So you can execute:: oarnodesetting -s Alive -h node_hostname or oarnodesetting -s Absent -h node_hostname 2. You do not want to install anything else on each node. So you have to enable oar user to connect to the server via ssh (for security you can use another SSH key with restrictions on the command that oar can launch with this one). Thus you will have in your init script something like:: sudo -u oar ssh oar-server "oarnodesetting -s Alive -h node_hostname" or sudo -u oar ssh oar-server "oarnodesetting -s Absent -h node_hostname" In this case, further OAR software upgrade will be more painless. Take a look in "/etc/default/oar-node" for Debian packaging and in "/etc/sysconfig/oar-node" for redhat. How can I manage scheduling queues? ----------------------------------- see oarnotify_. How can I handle licence tokens? -------------------------------- OAR does not manage resources with an empty "network_address". So you can define resources that are not linked with a real node. So the steps to configure OAR with the possibility to reserve licences (or whatever you want that are other notions): 1. Add a new field in the table resources_ to specify the licence name. :: oarproperty -a licence -c 2. Add your licence name resources with oarnodesetting_. :: oarnodesetting -a -h "" -p type=mathlab -p licence=l1 oarnodesetting -a -h "" -p type=mathlab -p licence=l2 oarnodesetting -a -h "" -p type=fluent -p licence=l1 ... After this configuration, users can perform submissions like :: oarsub -I -l "/switch=2/nodes=10+{type = 'mathlab'}/licence=20" So users ask OAR to give them some other resource types but nothing blocks their program to take more licences than they asked. You can resolve this problem with the SERVER_SCRIPT_EXEC_FILE_ configuration. In these files you have to bind OAR allocated resources to the licence servers to restrict user consumptions to what they asked. This is very dependant of the licence management. How can I handle multiple clusters with one OAR? ------------------------------------------------ These are the steps to follow: 1. create a resource property to identify the corresponding cluster (like "cluster"):: oarproperty -a cluster (you can see this new property when you use oarnodes) 2. with oarnodesetting_ you have to fill this field for all resources; for example:: oarnodesetting -h node42.cluster1.com -p cluster=1 oarnodesetting -h node43.cluster1.com -p cluster=1 oarnodesetting -h node2.cluster2.com -p cluster=2 ... 3. Then you have to restrict properties for new job type. So an admission rule performs this job (you can insert this new rule with the oaradmin_ command):: my $cluster_constraint = 0; if (grep(/^cluster1$/, @{$type_list})){ $cluster_constraint = 1; }elsif (grep(/^cluster2$/, @{$type_list})){ $cluster_constraint = 2; } if ($cluster_constraint > 0){ if ($jobproperties ne ""){ $jobproperties = "($jobproperties) AND cluster = $cluster_constraint"; }else{ $jobproperties = "cluster = $cluster_constraint"; } print("[ADMISSION RULE] Added automatically cluster resource constraint\n"); } 4. Edit the admission rule which checks the right job types and add "cluster1" and "cluster2" in. So when you will use oarsub to submit a "cluster2" job type only resources with the property "cluster=2" is used. This is the same when you will use the "cluster1" type. For example:: oarsub -I -t cluster2 #is equivalent to oarsub -I -p "cluster = 2" How to configure a more ecological cluster (or how to make some power consumption economies)? --------------------------------------------------------------------------------------------- This feature can be performed with the `Dynamic nodes coupling features`. First you have to make sure that you have a command to wake up a computer that is stopped. For example you can use the WoL (Wake on Lan) feature (generally you have to right configure the BIOS and add right options to the Linux Ethernet driver; see "ethtool"). If you want to enable a node to be woke up the next 12 hours:: ((DATE=$(date +%s)+3600*12)) oarnodesetting -h host_name -p cm_availability=$DATE Otherwise you can disable the wake up of nodes (but not the halt) by:: oarnodesetting -h host_name -p cm_availability=1 If you want to disable the halt on a node (but not the wakeup):: oarnodesetting -h host_name -p cm_availability=2147483647 2147483647 = 2^31 - 1 : we take this value as infinite and it is used to disable the halt mechanism. And if you want to disable the halt and the wakeup:: oarnodesetting -h host_name -p cm_availability=0 Note: In the unstable 2.4 OAR version, cm_availability has been renamed into available_upto. Your `SCHEDULER_NODE_MANAGER_WAKE_UP_CMD`_ must be a script that read node names and translate them into the right wake up command. So with the right OAR and node configurations you can optimize the power consumption of your cluster (and your air conditioning infrastructure) without drawback for the users. Take a look at your cluster occupation and your electricity bill to know if it could be interesting for you ;-) How to enable jobs to connect to the frontales from the nodes using oarsh? -------------------------------------------------------------------------- First you have to install the node part of OAR on the wanted nodes. After that you have to register the frontales into the database using oarnodesetting with the "frontal" (for example) type and assigned the desired cpus into the cpuset field; for example:: oarnodesetting -a -h frontal1 -p type=frontal -p cpuset=0 oarnodesetting -a -h frontal1 -p type=frontal -p cpuset=1 oarnodesetting -a -h frontal2 -p type=frontal -p cpuset=0 ... Thus you will be able to see resources identifier of these resources with oarnodes; try to type:: oarnodes --sql "type='frontal'" Then put this type name (here "frontal") into the *oar.conf* file on the OAR server into the tag SCHEDULER_RESOURCES_ALWAYS_ASSIGNED_TYPE_. Notes: - if one of these resources become "Suspected" then the scheduling will stop. - you can disable this feature with oarnodesetting_ and put these resources into the "Absent" state. A job remains in the "Finishing" state, what can I do? ------------------------------------------------------ If you have waited more than a couple of minutes (30mn for example) then something wrong occurred (frontal has crashed, out of memory, ...). So you are able to turn manually a job into the "Error" state by typing in the OAR install directory with the root user (example with a bash shell):: export OARCONFFILE=/etc/oar/oar.conf perl -e 'use OAR::IO; $db = OAR::IO::connect(); OAR::IO::set_job_state($db,42,"Error")' (Replace 42 by your job identifier) How can I write my own scheduler? --------------------------------- .. include:: scheduler/README What is the syntax of this documentation? ----------------------------------------- We are using the RST format from the `Docutils `_ project. This syntax is easily readable and can be converted into HTML, LaTex or XML. You can find basic informations on http://docutils.sourceforge.net/docs/user/rst/quickref.html ./oar-2.5.2/CPUSET0000644000175000017500000000744111757171206011515 0ustar plbplbSince OAR 2.5, the cpuset feature is configured by default (the majotiry of the Linux distribution is compiled with cpuset and/or cgroups). What are "oarsh" and "oarsh_shell" scripts ? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ "oarsh" and "oarsh_shell" are two scripts that can restrict user processes to stay in the same cpuset on all nodes. This feature is very usefull to restrict processor consumption on multiprocessors computers and to kill all processes of a same OAR job on several nodes. Moreover "oarsh" replaces the ssh or rsh commands in the user scripts (they don't have to configure ssh keys, oarsh works between all the nodes for a job). CPUSET definition ~~~~~~~~~~~~~~~~~ CPUSET is a module integrated in the Linux kernel since 2.6.x. In the kernel documentation, you can read:: Cpusets provide a mechanism for assigning a set of CPUs and Memory Nodes to a set of tasks. Cpusets constrain the CPU and Memory placement of tasks to only the resources within a tasks current cpuset. They form a nested hierarchy visible in a virtual file system. These are the essential hooks, beyond what is already present, required to manage dynamic job placement on large systems. Each task has a pointer to a cpuset. Multiple tasks may reference the same cpuset. Requests by a task, using the sched_setaffinity(2) system call to include CPUs in its CPU affinity mask, and using the mbind(2) and set_mempolicy(2) system calls to include Memory Nodes in its memory policy, are both filtered through that tasks cpuset, filtering out any CPUs or Memory Nodes not in that cpuset. The scheduler will not schedule a task on a CPU that is not allowed in its cpus_allowed vector, and the kernel page allocator will not allocate a page on a node that is not allowed in the requesting tasks mems_allowed vector. OARSH ~~~~~ "oarsh" is a wrapper around the "ssh" command (tested with openSSH). Its goal is to propagate two environment variables: - OAR_CPUSET : The name of the OAR job cpuset - OAR_JOB_USER : The name of the user corresponding to the job "oarsh" uses the oar ssh keys to do the ssh connection. That's why the ssh server used for OAR is configured to propagete the environment variables OAR_CPUSET and OAR_JOB_USER and only accepts the oar user. OARSH_SHELL ~~~~~~~~~~~ "oarsh_shell" must be the shell of the oar user on each nodes where you want "oarsh" to work. This script takes "OAR_CPUSET" and "OAR_JOB_USER" environment variables and adds its PID in OAR_CPUSET cpuset. Then it searches user shell and home and executes the right command (like ssh). Important notes ~~~~~~~~~~~~~~~ - the command "scp" can be used with oarsh. The syntax is:: scp -S /usr/bin/oarsh ... See scp man page. You can also use the "oarcp" command which do this for you. See the oarsh man page. - If you want to use oarsh from the user frontale, you can. You have to define the environment OAR_JOB_ID and then launch oarsh on a node used by your OAR job. This feature works only where the oarstat command is configured:: OAR_JOB_ID=42 oarsh node12 or:: export OAR_JOB_ID=42 oarsh node12 This command gives you a shell on the "node12" from the OAR job 42. - You can disable the cpuset security mechanism by setting the OARSH_BYPASS_WHOLE_SECURITY field to 1 in your oar.conf file. WARNING: this is a critical functionality (this is only useful if users want to have a command to connect on every nodes without taking care of their ssh configuration and act like a ssh). DO NOT USE THIS IF YOU ARE UNSURE. If you want more tips how to use oarsh with, for example, different MPI implementation then take a look at http://oar.imag.fr/TIPS.html . ./oar-2.5.2/COPYRIGHT0000644000175000017500000001153711757171206012063 0ustar plbplbFormat: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: oar Upstream-Contact: oar-contact@lists.gforge.inria.fr Source: http://oar-ftp.imag.fr/oar/sources/ Files: * Copyright: 2009-2012 LIG http://lig.imag.fr License: GPL-2+ Files: sources/extra/oar_drmaa/* Copyright: 2006-2009 FedStage Systems License: GPL-3+ Files: sources/visualization_interfaces/DrawGantt/js/overlib.js sources/visualization_interfaces/Monika/overlib.js Copyright: Erik Bosrup 1998-2002. All rights reserved. License: Artistic Files: sources/extra/orpheus/modules/lua-signal/lsignal.c Copyright: 2010 Patrick J. Donnelly (batrick@batbytes.com) License: MIT/X11 (BSD like) Files: sources/extra/ocaml-schedulers/common/OCamlMakefile Copyright: 1999-2007 Markus Mottl License: LGPL-2.1 License: GPL-2+ 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 General Public License for more details. . You should have received a copy of the GNU General Public License along with this package; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA . On Debian systems, the full text of the GNU General Public License version 2 can be found in the file `/usr/share/common-licenses/GPL-2'. License: GPL-3+ 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 3 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 General Public License for more details. . You should have received a copy of the GNU General Public License along with this program. If not, see . License: LGPL-2.1 This software is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, version 2.1. On Debian systems you can find a copy of this license in /usr/share/common-licenses/LGPL-2.1 License: MIT/X11 (BSD like) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: . The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. . THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. License: Artistic overLIB 3.51 -- This notice must remain untouched at all times. Copyright Erik Bosrup 1998-2002. All rights reserved. . By Erik Bosrup (erik@bosrup.com). Last modified 2002-11-01. Portions by Dan Steinman (dansteinman.com). Additions by other people are listed on the overLIB homepage. . Get the latest version at http://www.bosrup.com/web/overlib/ . This script is published under an open source license. Please read the license agreement online at: http://www.bosrup.com/web/overlib/license.html If you have questions regarding the license please contact erik@bosrup.com. . This script library was originally created for personal use. By request it has later been made public. This is free software. Do not sell this as your own work, or remove this copyright notice. For full details on copying or changing this script please read the license agreement at the link above. . Please give credit on sites that use overLIB and submit changes of the script so other people can use them as well. This script is free to use, don't abuse. . The license at the link above states the Artistic license for this software. . On Debian systems, the complete text of the Artistic License can be found in `/usr/share/common-licenses/Artistic'. ./oar-2.5.2/COPYING0000644000175000017500000004313111757171206011616 0ustar plbplb GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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 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 Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. ./oar-2.5.2/CHANGELOG0000644000175000017500000010100411757171206011767 0ustar plbplbOAR CHANGELOG ============= version 2.5.2: -------------- - Bugfix: /var/lib/oar/.bash_oar was empty due to an error in the common setup script. - Bugfix: the PINGCHECKER_COMMAND in oar.conf depends now on %%OARDIR%%. - Bug #13939: the job_resource_manager.pl and job_resource_manager_cgroups.pl now deletes the user files in /tmp, /var/tmp and /dev/shm at the end of the jobs. - Bugfix: in oardodo.c, the preprocessed variables was not defined correclty. - Finaud: fix race condition when there was a PINGCHECKER error jsut before another problem. The node became Alive again when the PINGCHECKER said OK BUT there was another error to resolve. - Bugfix: The feature CHECK_NODES_WITH_RUNNING_JOB=yes never worked before. - Speedup monika (X5). - Monika: Add the conf max_cores_per_line to have several lines if the number of cores are too big. - Minor changes into API: - added cmd_output into POST /jobs. - API: Added GET /select_all?query= (read only mode). - Add the field "array_index" into the jobs table. So that resubmit a job from an array will have the right array_index anvironment variable. - oarstat: order the output by job_id. - Speedup oarnodes. - Fix a spelling error in the oaradmin manpage. - Bugfix #14122 : the oar-node init.d script wasn't executing start_oar_node/stop_oar_node during the 'restart' action. - Allow the dash character into the --notify "exec:..." oarsub option. - Remove some old stuffs from the tarball: - visualization_interfaces/{tgoar,accounting,poar}; - scheduler/moldable; - pbs-oar-lib. - Fix some licence issues. version 2.5.1: -------------- - Sources directories reorganized - New "Phoenix" tool to try to reboot automatically broken nodes (to setup into /etc/oar/oar_phoenix.pl) - New (experimental!) scheduler written in Ocaml - Cpusets are activated by default - Bugfix #11065: oar_resource_init fix (add a space) - Bug 10999: memory leak into Hulot when used with postgresql. The leak has been minimized, but it is still there (DBD::Pg bug) - Almighty cleans ipcs used by oar on exit - Bugfix #10641 and #10999 : Hulot is automatically and periodically restarted - Feature request #10565: add the possibility to check the aliveness of the nodes of a job at the end of this one (pingchecker) - REST API heavily updated: new data structures with paginated results, desktop computing functions, rspec tests, oaradmin resources management, admission rules edition, relative/absolutes uris fixed - New ruby desktop computing agent using REST API (experimental) - Experimental testsuite - Poar: web portal using the REST API (experimental) - Oaradmin YAML export support for resources creation (for the REST API) - Bugfix #10567: enabling to bypass window mechanism of hulot. - Bugfix #10568: Wake up timeout changing with the number of nodes - Add in oar.conf the tag "RUNNER_SLIDING_WINDOW_SIZE": it allows the runner to use a sliding window to launch the bipbip processes if "DETACH_JOB_FROM_SERVER=1". This feature avoids the overload of the server if plenty of jobs have to be launched at the same time. - Fix problem when deleting a job in the Suspended state (oarexec was stopped by a SIGSTOP so it was not able to handle the delete operation) - Make the USER_SIGNAL feature of oardel multi job independant and remove the temporary file at the end of the job - Monika: display if the job is of timesharing type or not add in the job listing the initial_request (is there a reason to not display it?) - IoLib: update scheduler_priority resources property for timesharing jobs. So the scheduler will be able to avoid to launch every timesharing jobs on the same resources (they can be dispatched) - OAREXEC: unmask SIGHUP and SIGPIPE for user script - node_change_state: do not Suspect the first node of a job which was EXTERMINATED by Leon if the cpuset feature is configured (let do the job by the cpuset) - OAREXEC: ESRF detected that sometime oarexec think that he notified the Almighty with it exit code but nothing was seen on the server. So try to resend the exit code until oarexec is killed. - oar_Tools: add in notify_almighty a check on the print and on the close of the socket connected to Almighty. - oaraccounting: --sql is now possible into a "oarstat --accounting" query - Add more logs to the command "oarnodes -e host" when a node turns into Suspected - Execute user commands with /proc/self/oom_adj to 15. So the first processes that will be killed when there is no more memory available is the user ones. Hence the system will remain up and running and the user job will finished. Drawback: this file can be changed manually by the user so if someone knows a method to do the same thing but only managed by root, we take??? - Bugfix API: quotes where badly escaped into job submission (Ugo.Meda@insa-rennes.fr) - Add the possibility to automatically resubmit idempotent job which ends with an exit code of 99: oarsub -t idempotent "sleep 5; exit 99" - Bugfix API: Some informations where missing into jobs/details, especially the scheduled resources. - API: added support of "param_file" value for array job submissions. This value is a string representing the content of a parameters file. Sample submission:: {"resource":"/cpu=1", "command":"sleep", "param_file":"60\n90\n30"} This submits 3 sleep jobs with differents sleep values. - Remove any reference to gridlibs and gridapi as these components are obselete - Add stdout and stderr files of each job in oarstat output. - API now supports fastcgi (big performance raise!) - Add "-f" option to oarnodesetting to read hostnames from a file. - API can get/upload files (GET or POST /media/) - Make "X11 forwarding" working even if the user XAUTHORITY environment variable does not contain ~/.Xauthority (GDM issue). - Add job_resource_manager_cgroups which handles cpuset + other cgroup features like network packet tagging, IO disk shares, ... - Bugfix #13351: now oar_psql_db_init is executed with root privileges - Bugfix #13434: reservation were not handled correctly with the energy saving feature - Add cgroups FREEZER feature to the suspend/resume script (better than kill SIGSTOP/SIGCONT). This is doable thanks to the new job_resource_manager_cgroups. - Implement a new script 'oar-database' to manage the oar database. oar_mysql_init & oar_psql_init are dropped. - Huge code reorganisation to allow a better packaging and system integration - Drop the oarsub/oarstat 2.3 version that was kept for compatiblity issues during the 2.4.x branch. - By default the oar scheduler is now 'oar_sched_gantt_with_timesharing_and_fairsharing' and the following values has been set in oar.conf: SCHEDULER_TIMEOUT to 30, SCHEDULER_NB_PROCESSES to 4 and SCHEDULER_FAIRSHARING_MAX_JOB_PER_USER to 30 - Add a limitation on the number of concurrent bipbip processes on the server (for detached jobs). - Add IPC cleaning to the job_resource_manager* when there is no other job of the same user on the nodes. - make better scheduling behaviour for dependency jobs - API: added missing stop_time into /jobs/details version 2.4.4: -------------- - oar_resource_init: bad awk delimiter. There's a space and if the property is the first one then there is not a ','. - job suspend: oardo does not exist anymore (long long time ago). Replace it with oardodo. - oarsub: when an admission rule died micheline returns an integer and not an array ref. Now oarsub ends nicely. - Monika: add a link on each jobid on the node display area. - sshd_config: with nodes with a lot of core, 10 // connections could be too few version 2.4.3: -------------- - Hulot module now has customizable keepalive feature - Added a hook to launch a healing command when nodes are suspected (activate the SUSPECTED_HEALING_EXEC_FILE variable) - Bugfix #9995: oaraccouting script doesn't freeze anymore when db is unreachable. - Bugfix #9990: prevent from inserting jobs with invalid username (like an empty username) - Oarnodecheck improvements: node is not checked if a job is already running - New oaradmin option: --auto-offset - Feature request #10565: add the possibility to check the aliveness of the nodes of a job at the end of this one (pingchecker) version 2.4.2: -------------- - New "Hulot" module for intelligent and configurable energy saving - Bug #9906: fix bad optimization in the gantt lib (so bad scheduling version 2.4.1: -------------- - Bug #9038: Security flaw in oarsub --notify option - Bug #9601: Cosystem jobs are no more killed when a resource is set to Absent - Fixed some packaging bugs - API bug fixes in job submission parsing - Added standby info into `oarnodes -s` and available_upto info into /resources uri of the API - Bug Grid'5000 #2687 Fix possible crashes of the scheduler. - Bug fix: with MySQL DB Finaud suspected resources which are not of the "default" type. - Signed debian packages (install oar-keyring package) version 2.4.0: -------------- - Bug #8791: added CHECK_NODES_WITH_RUNNING_JOB=no to prevent from checking occupied nodes - Fix bug in oarnodesetting command generated by oar_resources_init (detect_resources) - Added a --state option to oarstat to only get the status of specified jobs (optimized query, to allow scripting) - Added a REST API for OAR and OARGRID - Added JSON support into oarnodes, oarstat and oarsub - New Makefile adapted to build packages as non-root user - add the command "oar_resources_init" to easily detect and initialize the whole resources of a cluster. - "oaradmin version" : now retrieve the most recent database schema number - Fix rights on the "schema" table in postgresql. - Bug #7509: fix bug in add_micheline_subjob for array jobs + jobtypes - Ctrl-C was not working anymore in oarsub. It seems that the signal handler does not handle the previous syntax ($SIG = 'qdel') - Fix bug in oarsh with the "-l" option - Bug #7487: bad initialisation of the gnatt for the container jobs. - Scheduler: move the "delete_unnecessary_subtrees" directly into "find_first_hole". Thus this is possible to query a job like:: oarsub -I -l nodes=1/core=1+nodes=4/core=2 (no hard separation between each group) For the same behaviour as before, you can query: oarsub -I -l {prop=1}/nodes=1/core=1+{prop=2}/nodes=4/core=2 - Bug #7634: test if the resource property value is effectively defined otherwise print a '' - Optional script to take into account cpu/core topology of the nodes at boot time (to activate inside oarnodesetting_ssh) - Bug #7174: Cleaned default PATH from "./" into oardodo - Bug #7674: remove the computation of the scheduler_priority field for besteffort jobs from the asynchronous OAR part. Now the value is set when the jobs are turned into toLaunch state and in Error/Terminated. - Bug #7691: add --array and --array-param-file options parsing into the submitted script. Fix also some parsing errors. - Bug #7962: enable resource property "cm_availability" to be manipulated by the oarnodesetting command - Added the (standby) information to a node state in oarnodes when it's state is Absent and cm_availability != 0 - Changed the name of cm_availability to available_upto which is more relevant - add a --maintenance option to oarnodesetting that sets the state of a resource to Absent and its available_upto to 0 if maintenance is on and resets previous values if maintenance is off. - added a --signal option to oardel that allow a user to send a signal to one of his jobs - added a name field in the schema table that will refer to the OAR version name - added a table containing scheduler name, script and description - Bug #8559: Almighty: Moved OAREXEC_XXXX management code out of the queue for immediate action, to prevent potential problems in case of scheduler timeouts. - oarnodes, oarstat and the REST API are no more making retry connections to the database in case of failure, but exit with an error instead. The retry behavior is left for daemons. - improved packaging (try to install files in more standard places) - improved init script for Almighty (into deb and rpm packages) - fixed performance issue on oarstat (array_id index missing) - fixed performance issue (job_id index missing in event_log table) - fixed a performance issue at job submission (optimized a query and added an index on challenges table) decisions). version 2.3.5: -------------- - Bug #8139: Drawgantt nil error (Add condition to test the presence of nil value in resources table.) - Bug #8416: When a the automatic halt/wakeup feature is enabled then there was a problem to determine idle nodes. - Debug a mis-initialization of the Gantt with running jobs in the metascheduler (concurrency access to PG database) version 2.3.4: -------------- - add the command "oar_resources_init" to easily detect and initialize the whole resources of a cluster. - "oaradmin version" : now retrieve the most recent database schema number - Fix rights on the "schema" table in postgresql. - Bug #7509: fix bug in add_micheline_subjob for array jobs + jobtypes - Ctrl-C was not working anymore in oarsub. It seems that the signal handler does not handle the previous syntax ($SIG = 'qdel') - Bug #7487: bad initialisation of the gnatt for the container jobs. - Fix bug in oarsh with the "-l" option - Bug #7634: test if the resource property value is effectively defined otherwise print a '' - Bug #7674: remove the computation of the scheduler_priority field for besteffort jobs from the asynchronous OAR part. Now the value is set when the jobs are turned into toLaunch state and in Error/Terminated. - Bug #7691: add --array and --array-param-file options parsing into the submitted script. Fix also some parsing errors. - Bug #7962: enable resource property "cm_availability" to be manipulated by the oarnodesetting command version 2.3.3: -------------- - Fix default admission rules: case unsensitive check for properties used in oarsub - Add new oaradmin subcommand : oaradmin conf. Useful to edit conf files and keep changes in a Subversion repository. - Kill correctly each taktuk command children in case of a timeout. - New feature: array jobs (option --array) (on oarsub, oarstat oardel, oarhold and oarresume) and file-based parametric array jobs (oarsub --array-param-file) /!\ in this version the DB scheme has changed. If you want to upgrade your installation from a previous 2.3 release then you have to execute in your database one of these SQL script (stop OAR before):: mysql: DB/mysql_structure_upgrade_2.3.1-2.3.3.sql postgres: DB/pg_structure_upgrade_2.3.1-2.3.3.sql version 2.3.2: -------------- - Change scheduler timeout implementation to schedule the maximum of jobs. - Bug #5879: do not show initial_request in oarstat when it is not a job of the user who launched the oarstat command (oar or root). - Add a --event option to oarnodes and oarstat to display events recorded for a job or node - Display reserved resources for a validated waiting reservation, with a hint in their state - Fix oarproperty: property names are lowercase - Fix OAR_JOB_PROPERTIES_FILE: do not display system properties - Add a new user command: oarprint which allow to pretty print resource properties of a job - Debug temporary job UID feature - Add 'kill -9' on subprocesses that reached a timeout (avoid Perl to wait something) - desktop computing feature is now available again. (ex: oarsub -t desktop_computing date) - Add versioning feature for admission rules with Subversion version 2.3.1: -------------- - Add new oarmonitor command. This will permit to monitor OAR jobs on compute nodes. - Remove sudo dependency and replace it by the commands "oardo" and "oardodo". - Add possibility to create a temporary user for each jobs on compute nodes. So you can perform very strong restrictions for each job (ex: bandwidth restrictions with iptable, memory management, ... everything that can be handled with a user id) - Debian packaging: Run OAR specific sshd with root privileges (under heavy load, kernel may be more responsive for root processes...) - Remove ALLOWED_NETWORKS tag in oar.conf (added more complexeity than resolving problems) - /!\ change database scheme for the field *exit_code* in the table *jobs*. Now *oarstat* *exit_code* line reflects the right exit code of the user passive job (before, even when the user script was not launched the *exit_code* was 0 which was BAD) - /!\ add DB field *initial_request* in the table *jobs* that stores the oarsub line of the user - Feature Request #4868: Add a parameter to specify what the "nodes" resource is a synomym for. Network_address must be seen as an internal data and not used. - Scheduler: add timeout for each job == 1/4 of the remaining scheduler timeout. - Bug #4866: now the whole node is Suspected instead of just the par where there is no job onto. So it is possible to have a job on Suspected nodes. - Add job walltime (in seconds) in parameter of prologue and epilogue on compute nodes. - oarnodes does not show system properties anymore. - New feature: container job type now allows to submit inner jobs for a scheduling within the container job - Monika refactoring and now in the oar packaging. - Added a table schema in the db with the field version, reprensenting the version of the db schema. - Added a field DB_PORT in the oar config file. - Bug #5518: add right initialization of the job user name. - Add new oaradmin command. This will permit to create resources and manage admission rules more easily. - Bug #5692: change source code into a right Perl 5.10 syntax. version 2.2.12: --------------- - Bug #5239: fix the bug if there are spaces into job name or project - Fix the bug in Iolib if DEAD_SWITCH_TIME >0 - Fix a bug in bipbip when calling the cpuset_manager to clean jobs in error - Bug #5469: fix the bug with reservations and Dead resources - Bug #5535: checks for reservations made at a same time was wrong. - New feature: local checks on nodes can be plugged in the oarnodecheck mechanism. Results can be asynchronously checked from the server (taktuk ping checker) - Add 2 new tables to keep track of the scheduling decisions (gantt_jobs_predictions_log and gantt_jobs_resources_log). This will help debugging scheduling troubles (see SCHEDULER_LOG_DECISIONS in oar.conf) - Now reservations are scheduled only once (at submission time). Resources allocated to a reservations are definitively set once the validated is done and won't change in next scheduler's pass. - Fix DrawGantt to not display besteffort jobs in the future which is meaningless. version 2.2.11: --------------- - Fix Debian package dependency on a CGI web server. - Fix little bug: remove notification (scheduled start time) for Interactive reservation. - Fix bug in reservation: take care of the SCHEDULER_JOB_SECURITY_TIME for reservations to check. - Fix bug: add a lock around the section which creates and feed the OAR cpuset. - Taktuk command line API has changed (we need taktuk >= 3.6). - Fix extra ' in the name of output files when using a job name. - Bug #4740: open the file in oarsub with user privileges (-S option) - Bug #4787: check if the remote socket is defined (problem of timing with nmap) - Feature Request #4874: check system names when renaming properties - DrawGantt can export charts to be reused to build a global multi-OAR view (e.g. DrawGridGantt). - Bug #4990: DrawGantt now uses the database localtime as its time reference. version 2.2.10: --------------- - Job dependencies: if the required jobs do not have an exit code == 0 and in the state Terminated then the schedulers refuse to schedule this job. - Add the possibility to disable the halt command on nodes with cm_availability value. - Enhance oarsub "-S" option (more #OAR parsed). - Add the possibility to use oarsh without configuring the CPUSETs (can be useful for users that don't want to configure there ssh keys) version 2.2.9: -------------- - Bug 4225: Dump only 1 data structure when using -X or -Y or -D. - Bug fix in Finishing sequence (Suspect right nodes). version 2.2.8: -------------- - Bug 4159: remove unneeded Dump print from oarstat. - Bug 4158: replace XML::Simple module by XML::Dumper one. - Bug fix for reservation (recalculate the right walltime). - Print job dependencies in oarstat. version 2.2.7: -------------- version 2.2.11: --------------- - Fix Debian package dependency on a CGI web server. - Fix little bug: remove notification (scheduled start time) for Interactive reservation. - Fix bug in reservation: take care of the SCHEDULER_JOB_SECURITY_TIME for reservations to check. - Fix bug: add a lock around the section which creates and feed the OAR cpuset. - Taktuk command line API has changed (we need taktuk >= 3.6). - Fix extra ' in the name of output files when using a job name. - Bug #4740: open the file in oarsub with user privileges (-S option) - Bug #4787: check if the remote socket is defined (problem of timing with nmap) - Feature Request #4874: check system names when renaming properties - DrawGantt can export charts to be reused to build a global multi-OAR view (e.g. DrawGridGantt). - Bug #4990: DrawGantt now uses the database localtime as its time reference. version 2.2.10: --------------- - Job dependencies: if the required jobs do not have an exit code == 0 and in the state Terminated then the schedulers refuse to schedule this job. - Add the possibility to disable the halt command on nodes with cm_availability value. - Enhance oarsub "-S" option (more #OAR parsed). - Add the possibility to use oarsh without configuring the CPUSETs (can be useful for users that don't want to configure there ssh keys) version 2.2.9: -------------- - Bug 4225: Dump only 1 data structure when using -X or -Y or -D. - Bug fix in Finishing sequence (Suspect right nodes). version 2.2.8: -------------- - Bug 4159: remove unneeded Dump print from oarstat. - Bug 4158: replace XML::Simple module by XML::Dumper one. - Bug fix for reservation (recalculate the right walltime). - Print job dependencies in oarstat. version 2.2.7: -------------- - Bug 4106: fix oarsh and oarcp issue with some options (erroneous leading space). - Bug 4125: remove exit_code data when it is not relevant. - Fix potential bug when changing asynchronously the state of the jobs into "Terminated" or "Error". version 2.2.6: -------------- - Bug fix: job types was not sent to cpuset manager script anymore. (border effect from bug 4069 resolution) version 2.2.5: -------------- - Bug fix: remove user command when oar execute the epilogue script on the nodes. - Clean debug and mail messages format. - Remove bad oarsub syntax from oarsub doc. - Debug xauth path. - bug 3995: set project correctly when resubmitting a job - debug 'bash -c' on Fedora - bug 4069: reservations with CPUSET_ERROR (remove bad hosts and continue with a right integrity in the database) - bug 4044: fix free resources query for reservation (get the nearest hole from the beginning of the reservation) - bug 4013: now Dead, Suspected and Absent resources have different colors in drawgantt with a popup on them. version 2.2.4: -------------- - Redirect third party commands into oar.log (easier to debug). - Add user info into drawgantt interface. - Some bug fixes. version 2.2.3: -------------- - Debug prologue and epilogue when oarexec receives a signal. version 2.2.2: -------------- - Switch nice value of the user processes into 0 in oarsh_shell (in case of sshd was launched with a different priority). - debug taktuk zombies in pingchecker and oar_Tools version 2.2.1: -------------- - install the "allow_clasic_ssh" feature by default - debug DB installer version 2.2: ------------ - oar_server_proepilogue.pl: can be used for server prologue and epilogue to authorize users to access to nodes that are completely allocated by OAR. If the whole node is assigned then it kills all jobs from the user if all cpus are assigned. - the same thing can be done with cpuset_manager_PAM.pl as the script used to configure the cpuset. More efficent if cpusets are configured. - debug cm_availability feature to switch on and off nodes automatically depending on waiting jobs. - reservations now take care of cm_availability field version 2.1.0: -------------- - add "oarcp" command to help the users to copy files using oarsh. - add sudo configuration to deal with bash. Now oarsub and oarsh have the same behaviour as ssh (the bash configuration files are loaded correctly) - bug fix in drawgantt (loose jobs after submission of a moldable one) - add SCHEDULER_RESOURCES_ALWAYS_ASSIGNED_TYPE into oar.conf. Thus admin can add some resources for each jobs (like frontale node) - add possibility to use taktuk to check the aliveness of the nodes - %jobid% is now replaced in stdout and stderr file names by the effective job id - change interface to shu down or wake up nodes automatically (now the node list is read on STDIN) - add OARSUB_FORCE_JOB_KEY in oar.conf. It says to create a job ssh key by default for each job. - %jobid% is now replaced in the ssh job key name (oarsub -k ...). - add NODE_FILE_DB_FIELD_DISTINCT_VALUES in oar.conf that enables the admin to configure the generated containt of the OAR_NODE_FILE - change ssh job key oarsub options behaviour - add options "--reinitialize" and "--delete-before" to the oaraccounting command - cpuset are now stored in /dev/cpuset/oar - debian packaging: configure and launch a specific sshd for the user oar - use a file descriptor to send the node list --> able to handle a very large amount of nodes - every config files are now in /etc/oar/ - oardel can add a besteffort type to jobs and vis versa version 2.0.2: -------------- - add warnings and exit code to oarnodesetting when there is a bad node name or resource number - change package version - change default behaviour for the cpuset_manager.pl (more portable) - enable a user to use the same ssh key for several jobs (at his own risk!) - add node hostnames in oarstat -f - add --accounting and -u options in oarstat - bug fix on index fields in the database (syncro): bug 2020 - bug fix about server pro/epilogue: bug 2022 - change the default output of oarstat. Now it is usable: bug 1875 - remove keys in authorized_keys of oar (on the nodes) that do not correspond to an active cpuset (clean after a reboot) - reread oar.conf after each database connection tries - add support for X11 forwarding in oarsub -I and -C - debug mysql initialization script in debian package - add a variable in oarsh for the default options of ssh to use (more useful to change if the ssh version installed does not handle one of these options) - read oar.conf in oarsh (so admin can more easily change options in this script) - add support for X11 forwarding via oarsh - change variable for oarsh: OARSH_JOB_ID --> OAR_JOB_ID version 2.0.0: -------------- - Now, with the ability to declare any type of resources like licences, VLAN, IP range, computing resources must have the type *default* and a network_address not null. - Possibility to declare associated resources like licences, IP ranges, ... and to reserve them like others. - Now you can connect to your jobs (not only for reservations). - Add "cosystem" job type (execute and do nothing for these jobs). - New scheduler : "oar_sched_gantt_with_timesharing". You can specify jobs with the type "timesharing" that indicates that this scheduler can launch more than 1 job on a resource at a time. It is possible to restrict this feature with words "user and name". For example, '-t timesharing=user,name' indicates that only a job from the same user with the same name can be launched in the same time than it. - Add PostGresSQL support. So there is a choice to make between MySQL and PostgresSQL. - New approach for the scheduling : administrators have to insert into the databases descriptions about resources and not nodes. Resources have a network address (physical node) and properties. For example, if you have dual-processor, then you can create 2 different resources with the same natwork address but with 2 different processor names. - The scheduler can now handle resource properties in a hierarchical manner. Thus, for example, you can do "oarsub -l /switch=1/cpu=5" which submit a job on 5 processors on the same switch. - Add a signal handler in oarexec and propagate this signal to the user process. - Support '#OAR -p ...' options in user script. - Add in oar.conf: * DB_BASE_PASSWD_RO : for security issues, it is possible to execute request with parts specified by users with a read only account (like "-p" option). * OARSUB_DEFAULT_RESOURCES : when nothing is specified with the oarsub command then OAR takes this default resource description. * OAREXEC_DEBUG_MODE : turn on or off debug mode in oarexec (create /tmp/oar/oar.log on nodes). * FINAUD_FREQUENCY : indicates the frequency when OAR launchs Finaud (search dead nodes). * SCHEDULER_TIMEOUT : indicates to the scheduler the amount of time after what it must end itself. * SCHEDULER_JOB_SECURITY_TIME : time between each job. * DEAD_SWITCH_TIME : after this time Absent and Suspected resources are turned on the Dead state. * PROLOGUE_EPILOGUE_TIMEOUT : the possibility to specify a different timeout for prologue and epilogue (PROLOGUE_EPILOGUE_TIMEOUT). * PROLOGUE_EXEC_FILE : you can specify the path of the prologue script executed on nodes. * EPILOGUE_EXEC_FILE : you can specify the path of the epilogue script executed on nodes. * GENERIC_COMMAND : a specific script may be used instead of ping to check aliveness of nodes. The script must return bad nodes on STDERR (1 line for a bad node and it must have exactly the same name that OAR has given in argument of the command). * JOBDEL_SOFTWALLTIME : time after a normal frag that the system waits to retry to frag the job. * JOBDEL_WALLTIME : time after a normal frag that the system waits before to delete the job arbitrary and suspects nodes. * LOG_FILE : specify the path of OAR log file (default : /var/log/oar.log). - Add wait() in pingchecker to avoid zombies. - Better code modularization. - Remove node install part to launch jobs. So it is easier to upgrade from one version to an other (oarnodesetting must already be installed on each nodes if we want to use it). - Users can specify a method to be notified (mail or script). - Add cpuset support - Add prologue and epilogue script to be executed on the OAR server before and after launching a job. - Add dependancy support between jobs ("-a" option in oarsub). - In oarsub you can specify the launching directory ("-d" option). - In oarsub you can specify a job name ("-n" option). - In oarsub you can specify stdout and stderr file names. - User can resubmit a job (option "--resubmit" in oarsub). - It is possible to specify a read only database account and it will be used to evaluate SQL properties given by the user with the oarsub command (more scecure). - Add possibility to order assigned resources with their properties by the scheduler. So you can privilege some resources than others (SCHEDULER_RESOURCE_ORDER tag in oar.conf file) - a command can be specified to switch off idle nodes (SCHEDULER_NODE_MANAGER_SLEEP_CMD, SCHEDULER_NODE_MANAGER_IDLE_TIME, SCHEDULER_NODE_MANAGER_SLEEP_TIME in oar.conf) - a command can be specified to switch on nodes in the Absent state according to the resource property *cm_availability* in the table resources (SCHEDULER_NODE_MANAGER_WAKE_UP_CMD in oar.conf). - if a job goes in Error state and this is not its fault then OAR will resubmit this one. ./oar-2.5.2/AUTHORS0000644000175000017500000000167111757171206011636 0ustar plbplb OAR is developped within the Laboratoire ID-IMAG ENSIMAG-Antenne de Montbonnot 51 avenue Jean KUNTZMANN 38330 Montbonnot Saint-Martin FRANCE http://oar.imag.fr Developers Nicolas Capit < nicolas.capit@imag.fr > Georges Da Costa < Georges.Da-Costa@imag.fr > Joseph Emeras < Joseph.Emeras@imag.fr > Lionel Eyraud < lionel.eyraud@imag.fr > Guillaume Huard < Guillaume.Huard@imag.fr > Philippe Le Brouster < philippe.le-brouster@imag.fr > Cyrille Martin < Cyrille.Martin@imag.fr > Gregory Mounie < Gregory.Mounie@imag.fr > Olivier Richard < olivier.richard@imag.fr > Pierre Neyron < pierre.neyron@imag.fr > Ali Saidi < saidi@umich.edu > Francois Visconte < francois.visconte@inria.fr >