PNG  IHDRX cHRMz&u0`:pQ<bKGD pHYsodtIME MeqIDATxw]Wug^Qd˶ 6`!N:!@xI~)%7%@Bh&`lnjVF29gΨ4E$|>cɚ{gk= %,a KX%,a KX%,a KX%,a KX%,a KX%,a KX%, b` ǟzeאfp]<!SJmɤY޲ڿ,%c ~ع9VH.!Ͳz&QynֺTkRR.BLHi٪:l;@(!MԴ=žI,:o&N'Kù\vRmJ雵֫AWic H@" !: Cé||]k-Ha oݜ:y F())u]aG7*JV@J415p=sZH!=!DRʯvɱh~V\}v/GKY$n]"X"}t@ xS76^[bw4dsce)2dU0 CkMa-U5tvLƀ~mlMwfGE/-]7XAƟ`׮g ewxwC4\[~7@O-Q( a*XGƒ{ ՟}$_y3tĐƤatgvێi|K=uVyrŲlLӪuܿzwk$m87k( `múcE)"@rK( z4$D; 2kW=Xb$V[Ru819קR~qloѱDyįݎ*mxw]y5e4K@ЃI0A D@"BDk_)N\8͜9dz"fK0zɿvM /.:2O{ Nb=M=7>??Zuo32 DLD@D| &+֎C #B8ַ`bOb $D#ͮҪtx]%`ES`Ru[=¾!@Od37LJ0!OIR4m]GZRJu$‡c=%~s@6SKy?CeIh:[vR@Lh | (BhAMy=݃  G"'wzn޺~8ԽSh ~T*A:xR[ܹ?X[uKL_=fDȊ؂p0}7=D$Ekq!/t.*2ʼnDbŞ}DijYaȲ(""6HA;:LzxQ‘(SQQ}*PL*fc\s `/d'QXW, e`#kPGZuŞuO{{wm[&NBTiiI0bukcA9<4@SӊH*؎4U/'2U5.(9JuDfrޱtycU%j(:RUbArLֺN)udA':uGQN"-"Is.*+k@ `Ojs@yU/ H:l;@yyTn}_yw!VkRJ4P)~y#)r,D =ě"Q]ci'%HI4ZL0"MJy 8A{ aN<8D"1#IJi >XjX֔#@>-{vN!8tRݻ^)N_╗FJEk]CT՟ YP:_|H1@ CBk]yKYp|og?*dGvzنzӴzjֺNkC~AbZƷ`.H)=!QͷVTT(| u78y֮}|[8-Vjp%2JPk[}ԉaH8Wpqhwr:vWª<}l77_~{s۴V+RCģ%WRZ\AqHifɤL36: #F:p]Bq/z{0CU6ݳEv_^k7'>sq*+kH%a`0ԣisqにtү04gVgW΂iJiS'3w.w}l6MC2uԯ|>JF5`fV5m`Y**Db1FKNttu]4ccsQNnex/87+}xaUW9y>ͯ骵G{䩓Գ3+vU}~jJ.NFRD7<aJDB1#ҳgSb,+CS?/ VG J?|?,2#M9}B)MiE+G`-wo߫V`fio(}S^4e~V4bHOYb"b#E)dda:'?}׮4繏`{7Z"uny-?ǹ;0MKx{:_pÚmFמ:F " .LFQLG)Q8qN q¯¯3wOvxDb\. BKD9_NN &L:4D{mm o^tֽ:q!ƥ}K+<"m78N< ywsard5+вz~mnG)=}lYݧNj'QJS{S :UYS-952?&O-:W}(!6Mk4+>A>j+i|<<|;ر^߉=HE|V#F)Emm#}/"y GII웻Jі94+v뾧xu~5C95~ūH>c@덉pʃ1/4-A2G%7>m;–Y,cyyaln" ?ƻ!ʪ<{~h~i y.zZB̃/,雋SiC/JFMmBH&&FAbϓO^tubbb_hZ{_QZ-sύodFgO(6]TJA˯#`۶ɟ( %$&+V'~hiYy>922 Wp74Zkq+Ovn錄c>8~GqܲcWꂎz@"1A.}T)uiW4="jJ2W7mU/N0gcqܗOO}?9/wìXžΏ0 >֩(V^Rh32!Hj5`;O28؇2#ݕf3 ?sJd8NJ@7O0 b־?lldщ̡&|9C.8RTWwxWy46ah嘦mh٤&l zCy!PY?: CJyв]dm4ǜҐR޻RլhX{FƯanшQI@x' ao(kUUuxW_Ñ줮[w8 FRJ(8˼)_mQ _!RJhm=!cVmm ?sFOnll6Qk}alY}; "baӌ~M0w,Ggw2W:G/k2%R,_=u`WU R.9T"v,<\Ik޽/2110Ӿxc0gyC&Ny޽JҢrV6N ``یeA16"J³+Rj*;BϜkZPJaÍ<Jyw:NP8/D$ 011z֊Ⱳ3ι֘k1V_"h!JPIΣ'ɜ* aEAd:ݺ>y<}Lp&PlRfTb1]o .2EW\ͮ]38؋rTJsǏP@芎sF\> P^+dYJLbJ C-xϐn> ι$nj,;Ǖa FU *择|h ~izť3ᤓ`K'-f tL7JK+vf2)V'-sFuB4i+m+@My=O҈0"|Yxoj,3]:cо3 $#uŘ%Y"y죯LebqtҢVzq¼X)~>4L׶m~[1_k?kxֺQ`\ |ٛY4Ѯr!)N9{56(iNq}O()Em]=F&u?$HypWUeB\k]JɩSع9 Zqg4ZĊo oMcjZBU]B\TUd34ݝ~:7ڶSUsB0Z3srx 7`:5xcx !qZA!;%͚7&P H<WL!džOb5kF)xor^aujƍ7 Ǡ8/p^(L>ὴ-B,{ۇWzֺ^k]3\EE@7>lYBȝR.oHnXO/}sB|.i@ɥDB4tcm,@ӣgdtJ!lH$_vN166L__'Z)y&kH;:,Y7=J 9cG) V\hjiE;gya~%ks_nC~Er er)muuMg2;֫R)Md) ,¶ 2-wr#F7<-BBn~_(o=KO㭇[Xv eN_SMgSҐ BS헃D%g_N:/pe -wkG*9yYSZS.9cREL !k}<4_Xs#FmҶ:7R$i,fi!~' # !6/S6y@kZkZcX)%5V4P]VGYq%H1!;e1MV<!ϐHO021Dp= HMs~~a)ަu7G^];git!Frl]H/L$=AeUvZE4P\.,xi {-~p?2b#amXAHq)MWǾI_r`S Hz&|{ +ʖ_= (YS(_g0a03M`I&'9vl?MM+m~}*xT۲(fY*V4x@29s{DaY"toGNTO+xCAO~4Ϳ;p`Ѫ:>Ҵ7K 3}+0 387x\)a"/E>qpWB=1 ¨"MP(\xp߫́A3+J] n[ʼnӼaTbZUWb={~2ooKױӰp(CS\S筐R*JغV&&"FA}J>G֐p1ٸbk7 ŘH$JoN <8s^yk_[;gy-;߉DV{c B yce% aJhDȶ 2IdйIB/^n0tNtџdcKj4϶v~- CBcgqx9= PJ) dMsjpYB] GD4RDWX +h{y`,3ꊕ$`zj*N^TP4L:Iz9~6s) Ga:?y*J~?OrMwP\](21sZUD ?ܟQ5Q%ggW6QdO+\@ ̪X'GxN @'4=ˋ+*VwN ne_|(/BDfj5(Dq<*tNt1х!MV.C0 32b#?n0pzj#!38}޴o1KovCJ`8ŗ_"]] rDUy޲@ Ȗ-;xџ'^Y`zEd?0„ DAL18IS]VGq\4o !swV7ˣι%4FѮ~}6)OgS[~Q vcYbL!wG3 7띸*E Pql8=jT\꘿I(z<[6OrR8ºC~ډ]=rNl[g|v TMTղb-o}OrP^Q]<98S¤!k)G(Vkwyqyr޽Nv`N/e p/~NAOk \I:G6]4+K;j$R:Mi #*[AȚT,ʰ,;N{HZTGMoּy) ]%dHء9Պ䠬|<45,\=[bƟ8QXeB3- &dҩ^{>/86bXmZ]]yޚN[(WAHL$YAgDKp=5GHjU&99v簪C0vygln*P)9^͞}lMuiH!̍#DoRBn9l@ xA/_v=ȺT{7Yt2N"4!YN`ae >Q<XMydEB`VU}u]嫇.%e^ánE87Mu\t`cP=AD/G)sI"@MP;)]%fH9'FNsj1pVhY&9=0pfuJ&gޤx+k:!r˭wkl03׼Ku C &ѓYt{.O.zҏ z}/tf_wEp2gvX)GN#I ݭ߽v/ .& и(ZF{e"=V!{zW`, ]+LGz"(UJp|j( #V4, 8B 0 9OkRrlɱl94)'VH9=9W|>PS['G(*I1==C<5"Pg+x'K5EMd؞Af8lG ?D FtoB[je?{k3zQ vZ;%Ɠ,]E>KZ+T/ EJxOZ1i #T<@ I}q9/t'zi(EMqw`mYkU6;[t4DPeckeM;H}_g pMww}k6#H㶏+b8雡Sxp)&C $@'b,fPߑt$RbJ'vznuS ~8='72_`{q纶|Q)Xk}cPz9p7O:'|G~8wx(a 0QCko|0ASD>Ip=4Q, d|F8RcU"/KM opKle M3#i0c%<7׿p&pZq[TR"BpqauIp$ 8~Ĩ!8Սx\ւdT>>Z40ks7 z2IQ}ItԀ<-%S⍤};zIb$I 5K}Q͙D8UguWE$Jh )cu4N tZl+[]M4k8֦Zeq֮M7uIqG 1==tLtR,ƜSrHYt&QP윯Lg' I,3@P'}'R˪e/%-Auv·ñ\> vDJzlӾNv5:|K/Jb6KI9)Zh*ZAi`?S {aiVDԲuy5W7pWeQJk֤#5&V<̺@/GH?^τZL|IJNvI:'P=Ϛt"¨=cud S Q.Ki0 !cJy;LJR;G{BJy޺[^8fK6)=yʊ+(k|&xQ2`L?Ȓ2@Mf 0C`6-%pKpm')c$׻K5[J*U[/#hH!6acB JA _|uMvDyk y)6OPYjœ50VT K}cǻP[ $:]4MEA.y)|B)cf-A?(e|lɉ#P9V)[9t.EiQPDѠ3ϴ;E:+Օ t ȥ~|_N2,ZJLt4! %ա]u {+=p.GhNcŞQI?Nd'yeh n7zi1DB)1S | S#ًZs2|Ɛy$F SxeX{7Vl.Src3E℃Q>b6G ўYCmtկ~=K0f(=LrAS GN'ɹ9<\!a`)֕y[uՍ[09` 9 +57ts6}b4{oqd+J5fa/,97J#6yν99mRWxJyѡyu_TJc`~W>l^q#Ts#2"nD1%fS)FU w{ܯ R{ ˎ󅃏џDsZSQS;LV;7 Od1&1n$ N /.q3~eNɪ]E#oM~}v֯FڦwyZ=<<>Xo稯lfMFV6p02|*=tV!c~]fa5Y^Q_WN|Vs 0ҘދU97OI'N2'8N֭fgg-}V%y]U4 峧p*91#9U kCac_AFңĪy뚇Y_AiuYyTTYЗ-(!JFLt›17uTozc. S;7A&&<ԋ5y;Ro+:' *eYJkWR[@F %SHWP 72k4 qLd'J "zB6{AC0ƁA6U.'F3:Ȅ(9ΜL;D]m8ڥ9}dU "v!;*13Rg^fJyShyy5auA?ɩGHRjo^]׽S)Fm\toy 4WQS@mE#%5ʈfFYDX ~D5Ϡ9tE9So_aU4?Ѽm%&c{n>.KW1Tlb}:j uGi(JgcYj0qn+>) %\!4{LaJso d||u//P_y7iRJ߬nHOy) l+@$($VFIQ9%EeKʈU. ia&FY̒mZ=)+qqoQn >L!qCiDB;Y<%} OgBxB!ØuG)WG9y(Ą{_yesuZmZZey'Wg#C~1Cev@0D $a@˲(.._GimA:uyw֬%;@!JkQVM_Ow:P.s\)ot- ˹"`B,e CRtaEUP<0'}r3[>?G8xU~Nqu;Wm8\RIkբ^5@k+5(By'L&'gBJ3ݶ!/㮻w҅ yqPWUg<e"Qy*167΃sJ\oz]T*UQ<\FԎ`HaNmڜ6DysCask8wP8y9``GJ9lF\G g's Nn͵MLN֪u$| /|7=]O)6s !ĴAKh]q_ap $HH'\1jB^s\|- W1:=6lJBqjY^LsPk""`]w)󭃈,(HC ?䔨Y$Sʣ{4Z+0NvQkhol6C.婧/u]FwiVjZka&%6\F*Ny#8O,22+|Db~d ~Çwc N:FuuCe&oZ(l;@ee-+Wn`44AMK➝2BRՈt7g*1gph9N) *"TF*R(#'88pm=}X]u[i7bEc|\~EMn}P瘊J)K.0i1M6=7'_\kaZ(Th{K*GJyytw"IO-PWJk)..axӝ47"89Cc7ĐBiZx 7m!fy|ϿF9CbȩV 9V-՛^pV̌ɄS#Bv4-@]Vxt-Z, &ֺ*diؠ2^VXbs֔Ìl.jQ]Y[47gj=幽ex)A0ip׳ W2[ᎇhuE^~q흙L} #-b۸oFJ_QP3r6jr+"nfzRJTUqoaۍ /$d8Mx'ݓ= OՃ| )$2mcM*cЙj}f };n YG w0Ia!1Q.oYfr]DyISaP}"dIӗթO67jqR ҊƐƈaɤGG|h;t]䗖oSv|iZqX)oalv;۩meEJ\!8=$4QU4Xo&VEĊ YS^E#d,yX_> ۘ-e\ "Wa6uLĜZi`aD9.% w~mB(02G[6y.773a7 /=o7D)$Z 66 $bY^\CuP. (x'"J60׿Y:Oi;F{w佩b+\Yi`TDWa~|VH)8q/=9!g߆2Y)?ND)%?Ǐ`k/sn:;O299yB=a[Ng 3˲N}vLNy;*?x?~L&=xyӴ~}q{qE*IQ^^ͧvü{Huu=R|>JyUlZV, B~/YF!Y\u_ݼF{_C)LD]m {H 0ihhadd nUkf3oٺCvE\)QJi+֥@tDJkB$1!Đr0XQ|q?d2) Ӣ_}qv-< FŊ߫%roppVBwü~JidY4:}L6M7f٬F "?71<2#?Jyy4뷢<_a7_=Q E=S1И/9{+93֮E{ǂw{))?maÆm(uLE#lïZ  ~d];+]h j?!|$F}*"4(v'8s<ŏUkm7^7no1w2ؗ}TrͿEk>p'8OB7d7R(A 9.*Mi^ͳ; eeUwS+C)uO@ =Sy]` }l8^ZzRXj[^iUɺ$tj))<sbDJfg=Pk_{xaKo1:-uyG0M ԃ\0Lvuy'ȱc2Ji AdyVgVh!{]/&}}ċJ#%d !+87<;qN޼Nفl|1N:8ya  8}k¾+-$4FiZYÔXk*I&'@iI99)HSh4+2G:tGhS^繿 Kتm0 вDk}֚+QT4;sC}rՅE,8CX-e~>G&'9xpW,%Fh,Ry56Y–hW-(v_,? ; qrBk4-V7HQ;ˇ^Gv1JVV%,ik;D_W!))+BoS4QsTM;gt+ndS-~:11Sgv!0qRVh!"Ȋ(̦Yl.]PQWgٳE'`%W1{ndΗBk|Ž7ʒR~,lnoa&:ü$ 3<a[CBݮwt"o\ePJ=Hz"_c^Z.#ˆ*x z̝grY]tdkP*:97YľXyBkD4N.C_[;F9`8& !AMO c `@BA& Ost\-\NX+Xp < !bj3C&QL+*&kAQ=04}cC!9~820G'PC9xa!w&bo_1 Sw"ܱ V )Yl3+ס2KoXOx]"`^WOy :3GO0g;%Yv㐫(R/r (s } u B &FeYZh0y> =2<Ϟc/ -u= c&׭,.0"g"7 6T!vl#sc>{u/Oh Bᾈ)۴74]x7 gMӒ"d]U)}" v4co[ ɡs 5Gg=XR14?5A}D "b{0$L .\4y{_fe:kVS\\O]c^W52LSBDM! C3Dhr̦RtArx4&agaN3Cf<Ԉp4~ B'"1@.b_/xQ} _߃҉/gٓ2Qkqp0շpZ2fԫYz< 4L.Cyυι1t@鎫Fe sYfsF}^ V}N<_`p)alٶ "(XEAVZ<)2},:Ir*#m_YӼ R%a||EƼIJ,,+f"96r/}0jE/)s)cjW#w'Sʯ5<66lj$a~3Kʛy 2:cZ:Yh))+a߭K::N,Q F'qB]={.]h85C9cr=}*rk?vwV렵ٸW Rs%}rNAkDv|uFLBkWY YkX מ|)1!$#3%y?pF<@<Rr0}: }\J [5FRxY<9"SQdE(Q*Qʻ)q1E0B_O24[U'],lOb ]~WjHޏTQ5Syu wq)xnw8~)c 쫬gٲߠ H% k5dƝk> kEj,0% b"vi2Wس_CuK)K{n|>t{P1򨾜j>'kEkƗBg*H%'_aY6Bn!TL&ɌOb{c`'d^{t\i^[uɐ[}q0lM˕G:‚4kb祔c^:?bpg… +37stH:0}en6x˟%/<]BL&* 5&fK9Mq)/iyqtA%kUe[ڛKN]Ě^,"`/ s[EQQm?|XJ߅92m]G.E΃ח U*Cn.j_)Tѧj̿30ڇ!A0=͜ar I3$C^-9#|pk!)?7.x9 @OO;WƝZBFU keZ75F6Tc6"ZȚs2y/1 ʵ:u4xa`C>6Rb/Yм)^=+~uRd`/|_8xbB0?Ft||Z\##|K 0>>zxv8۴吅q 8ĥ)"6>~\8:qM}#͚'ĉ#p\׶ l#bA?)|g g9|8jP(cr,BwV (WliVxxᡁ@0Okn;ɥh$_ckCgriv}>=wGzβ KkBɛ[˪ !J)h&k2%07δt}!d<9;I&0wV/ v 0<H}L&8ob%Hi|޶o&h1L|u֦y~󛱢8fٲUsւ)0oiFx2}X[zVYr_;N(w]_4B@OanC?gĦx>мgx>ΛToZoOMp>40>V Oy V9iq!4 LN,ˢu{jsz]|"R޻&'ƚ{53ўFu(<٪9:΋]B;)B>1::8;~)Yt|0(pw2N%&X,URBK)3\zz&}ax4;ǟ(tLNg{N|Ǽ\G#C9g$^\}p?556]/RP.90 k,U8/u776s ʪ_01چ|\N 0VV*3H鴃J7iI!wG_^ypl}r*jɤSR 5QN@ iZ#1ٰy;_\3\BQQ x:WJv츟ٯ$"@6 S#qe딇(/P( Dy~TOϻ<4:-+F`0||;Xl-"uw$Цi󼕝mKʩorz"mϺ$F:~E'ҐvD\y?Rr8_He@ e~O,T.(ފR*cY^m|cVR[8 JҡSm!ΆԨb)RHG{?MpqrmN>߶Y)\p,d#xۆWY*,l6]v0h15M˙MS8+EdI='LBJIH7_9{Caз*Lq,dt >+~ّeʏ?xԕ4bBAŚjﵫ!'\Ը$WNvKO}ӽmSşذqsOy?\[,d@'73'j%kOe`1.g2"e =YIzS2|zŐƄa\U,dP;jhhhaxǶ?КZ՚.q SE+XrbOu%\GتX(H,N^~]JyEZQKceTQ]VGYqnah;y$cQahT&QPZ*iZ8UQQM.qo/T\7X"u?Mttl2Xq(IoW{R^ ux*SYJ! 4S.Jy~ BROS[V|žKNɛP(L6V^|cR7i7nZW1Fd@ Ara{詑|(T*dN]Ko?s=@ |_EvF]׍kR)eBJc" MUUbY6`~V޴dJKß&~'d3i WWWWWW
Current Directory: /usr/lib/python3.6/site-packages/tuned/plugins
Viewing File: /usr/lib/python3.6/site-packages/tuned/plugins/base.py
import re import tuned.consts as consts import tuned.profiles.variables import tuned.logs import collections from tuned.utils.commands import commands import os from subprocess import Popen, PIPE log = tuned.logs.get() class Plugin(object): """ Base class for all plugins. Plugins change various system settings in order to get desired performance or power saving. Plugins use Monitor objects to get information from the running system. Intentionally a lot of logic is included in the plugin to increase plugin flexibility. """ def __init__(self, monitors_repository, storage_factory, hardware_inventory, device_matcher, device_matcher_udev, instance_factory, global_cfg, variables): """Plugin constructor.""" self._storage = storage_factory.create(self.__class__.__name__) self._monitors_repository = monitors_repository self._hardware_inventory = hardware_inventory self._device_matcher = device_matcher self._device_matcher_udev = device_matcher_udev self._instance_factory = instance_factory self._instances = collections.OrderedDict() self._init_commands() self._global_cfg = global_cfg self._variables = variables self._has_dynamic_options = False self._devices_inited = False self._options_used_by_dynamic = self._get_config_options_used_by_dynamic() self._cmd = commands() def cleanup(self): self.destroy_instances() def init_devices(self): if not self._devices_inited: self._init_devices() self._devices_inited = True @property def name(self): return self.__class__.__module__.split(".")[-1].split("_", 1)[1] # # Plugin configuration manipulation and helpers. # @classmethod def _get_config_options(self): """Default configuration options for the plugin.""" return {} @classmethod def get_config_options_hints(cls): """Explanation of each config option function""" return {} @classmethod def _get_config_options_used_by_dynamic(self): """List of config options used by dynamic tuning. Their previous values will be automatically saved and restored.""" return [] def _get_effective_options(self, options): """Merge provided options with plugin default options.""" # TODO: _has_dynamic_options is a hack effective = self._get_config_options().copy() for key in options: if key in effective or self._has_dynamic_options: effective[key] = options[key] else: log.warn("Unknown option '%s' for plugin '%s'." % (key, self.__class__.__name__)) return effective def _option_bool(self, value): if type(value) is bool: return value value = str(value).lower() return value == "true" or value == "1" # # Interface for manipulation with instances of the plugin. # def create_instance(self, name, devices_expression, devices_udev_regex, script_pre, script_post, options): """Create new instance of the plugin and seize the devices.""" if name in self._instances: raise Exception("Plugin instance with name '%s' already exists." % name) effective_options = self._get_effective_options(options) instance = self._instance_factory.create(self, name, devices_expression, devices_udev_regex, \ script_pre, script_post, effective_options) self._instances[name] = instance return instance def destroy_instance(self, instance): """Destroy existing instance.""" if instance._plugin != self: raise Exception("Plugin instance '%s' does not belong to this plugin '%s'." % (instance, self)) if instance.name not in self._instances: raise Exception("Plugin instance '%s' was already destroyed." % instance) instance = self._instances[instance.name] self._destroy_instance(instance) del self._instances[instance.name] def initialize_instance(self, instance): """Initialize an instance.""" log.debug("initializing instance %s (%s)" % (instance.name, self.name)) self._instance_init(instance) def destroy_instances(self): """Destroy all instances.""" for instance in list(self._instances.values()): log.debug("destroying instance %s (%s)" % (instance.name, self.name)) self._destroy_instance(instance) self._instances.clear() def _destroy_instance(self, instance): self.release_devices(instance) self._instance_cleanup(instance) def _instance_init(self, instance): raise NotImplementedError() def _instance_cleanup(self, instance): raise NotImplementedError() # # Devices handling # def _init_devices(self): self._devices_supported = False self._assigned_devices = set() self._free_devices = set() def _get_device_objects(self, devices): """Override this in a subclass to transform a list of device names (e.g. ['sda']) to a list of pyudev.Device objects, if your plugin supports it""" return None def _get_matching_devices(self, instance, devices): if instance.devices_udev_regex is None: return set(self._device_matcher.match_list(instance.devices_expression, devices)) else: udev_devices = self._get_device_objects(devices) if udev_devices is None: log.error("Plugin '%s' does not support the 'devices_udev_regex' option", self.name) return set() udev_devices = self._device_matcher_udev.match_list(instance.devices_udev_regex, udev_devices) return set([x.sys_name for x in udev_devices]) def assign_free_devices(self, instance): if not self._devices_supported: return log.debug("assigning devices to instance %s" % instance.name) to_assign = self._get_matching_devices(instance, self._free_devices) instance.active = len(to_assign) > 0 if not instance.active: log.warn("instance %s: no matching devices available" % instance.name) else: name = instance.name if instance.name != self.name: name += " (%s)" % self.name log.info("instance %s: assigning devices %s" % (name, ", ".join(to_assign))) instance.assigned_devices.update(to_assign) # cannot use |= self._assigned_devices |= to_assign self._free_devices -= to_assign def release_devices(self, instance): if not self._devices_supported: return to_release = (instance.processed_devices \ | instance.assigned_devices) \ & self._assigned_devices instance.active = False instance.processed_devices.clear() instance.assigned_devices.clear() self._assigned_devices -= to_release self._free_devices |= to_release # # Tuning activation and deactivation. # def _run_for_each_device(self, instance, callback, devices): if not self._devices_supported: devices = [None, ] for device in devices: callback(instance, device) def _instance_pre_static(self, instance, enabling): pass def _instance_post_static(self, instance, enabling): pass def _call_device_script(self, instance, script, op, devices, rollback = consts.ROLLBACK_SOFT): if script is None: return None if len(devices) == 0: log.warn("Instance '%s': no device to call script '%s' for." % (instance.name, script)) return None if not script.startswith("/"): log.error("Relative paths cannot be used in script_pre or script_post. " \ + "Use ${i:PROFILE_DIR}.") return False dir_name = os.path.dirname(script) ret = True for dev in devices: environ = os.environ environ.update(self._variables.get_env()) arguments = [op] if rollback == consts.ROLLBACK_FULL: arguments.append("full_rollback") arguments.append(dev) log.info("calling script '%s' with arguments '%s'" % (script, str(arguments))) log.debug("using environment '%s'" % str(list(environ.items()))) try: proc = Popen([script] + arguments, \ stdout=PIPE, stderr=PIPE, \ close_fds=True, env=environ, \ cwd = dir_name, universal_newlines = True) out, err = proc.communicate() if proc.returncode: log.error("script '%s' error: %d, '%s'" % (script, proc.returncode, err[:-1])) ret = False except (OSError,IOError) as e: log.error("script '%s' error: %s" % (script, e)) ret = False return ret def instance_apply_tuning(self, instance): """ Apply static and dynamic tuning if the plugin instance is active. """ if not instance.active: return if instance.has_static_tuning: self._call_device_script(instance, instance.script_pre, "apply", instance.assigned_devices) self._instance_pre_static(instance, True) self._instance_apply_static(instance) self._instance_post_static(instance, True) self._call_device_script(instance, instance.script_post, "apply", instance.assigned_devices) if instance.has_dynamic_tuning and self._global_cfg.get(consts.CFG_DYNAMIC_TUNING, consts.CFG_DEF_DYNAMIC_TUNING): self._run_for_each_device(instance, self._instance_apply_dynamic, instance.assigned_devices) instance.processed_devices.update(instance.assigned_devices) instance.assigned_devices.clear() def instance_verify_tuning(self, instance, ignore_missing): """ Verify static tuning if the plugin instance is active. """ if not instance.active: return None if len(instance.assigned_devices) != 0: log.error("BUG: Some devices have not been tuned: %s" % ", ".join(instance.assigned_devices)) devices = instance.processed_devices.copy() if instance.has_static_tuning: if self._call_device_script(instance, instance.script_pre, "verify", devices) == False: return False if self._instance_verify_static(instance, ignore_missing, devices) == False: return False if self._call_device_script(instance, instance.script_post, "verify", devices) == False: return False return True else: return None def instance_update_tuning(self, instance): """ Apply dynamic tuning if the plugin instance is active. """ if not instance.active: return if instance.has_dynamic_tuning and self._global_cfg.get(consts.CFG_DYNAMIC_TUNING, consts.CFG_DEF_DYNAMIC_TUNING): self._run_for_each_device(instance, self._instance_update_dynamic, instance.processed_devices.copy()) def instance_unapply_tuning(self, instance, rollback = consts.ROLLBACK_SOFT): """ Remove all tunings applied by the plugin instance. """ if rollback == consts.ROLLBACK_NONE: return if instance.has_dynamic_tuning and self._global_cfg.get(consts.CFG_DYNAMIC_TUNING, consts.CFG_DEF_DYNAMIC_TUNING): self._run_for_each_device(instance, self._instance_unapply_dynamic, instance.processed_devices) if instance.has_static_tuning: self._call_device_script(instance, instance.script_post, "unapply", instance.processed_devices, rollback = rollback) self._instance_pre_static(instance, False) self._instance_unapply_static(instance, rollback) self._instance_post_static(instance, False) self._call_device_script(instance, instance.script_pre, "unapply", instance.processed_devices, rollback = rollback) def _instance_apply_static(self, instance): self._execute_all_non_device_commands(instance) self._execute_all_device_commands(instance, instance.assigned_devices) def _instance_verify_static(self, instance, ignore_missing, devices): ret = True if self._verify_all_non_device_commands(instance, ignore_missing) == False: ret = False if self._verify_all_device_commands(instance, devices, ignore_missing) == False: ret = False return ret def _instance_unapply_static(self, instance, rollback = consts.ROLLBACK_SOFT): self._cleanup_all_device_commands(instance, instance.processed_devices) self._cleanup_all_non_device_commands(instance) def _instance_apply_dynamic(self, instance, device): for option in [opt for opt in self._options_used_by_dynamic if self._storage_get(instance, self._commands[opt], device) is None]: self._check_and_save_value(instance, self._commands[option], device) self._instance_update_dynamic(instance, device) def _instance_unapply_dynamic(self, instance, device): raise NotImplementedError() def _instance_update_dynamic(self, instance, device): raise NotImplementedError() # # Registration of commands for static plugins. # def _init_commands(self): """ Initialize commands. """ self._commands = collections.OrderedDict() self._autoregister_commands() self._check_commands() def _autoregister_commands(self): """ Register all commands marked using @command_set, @command_get, and @command_custom decorators. """ for member_name in self.__class__.__dict__: if member_name.startswith("__"): continue member = getattr(self, member_name) if not hasattr(member, "_command"): continue command_name = member._command["name"] info = self._commands.get(command_name, {"name": command_name}) if "set" in member._command: info["custom"] = None info["set"] = member info["per_device"] = member._command["per_device"] info["priority"] = member._command["priority"] elif "get" in member._command: info["get"] = member elif "custom" in member._command: info["custom"] = member info["per_device"] = member._command["per_device"] info["priority"] = member._command["priority"] self._commands[command_name] = info # sort commands by priority self._commands = collections.OrderedDict(sorted(iter(self._commands.items()), key=lambda name_info: name_info[1]["priority"])) def _check_commands(self): """ Check if all commands are defined correctly. """ for command_name, command in list(self._commands.items()): # do not check custom commands if command.get("custom", False): continue # automatic commands should have 'get' and 'set' functions if "get" not in command or "set" not in command: raise TypeError("Plugin command '%s' is not defined correctly" % command_name) # # Operations with persistent storage for status data. # def _storage_key(self, instance_name = None, command_name = None, device_name = None): class_name = type(self).__name__ instance_name = "" if instance_name is None else instance_name command_name = "" if command_name is None else command_name device_name = "" if device_name is None else device_name return "%s/%s/%s/%s" % (class_name, instance_name, command_name, device_name) def _storage_set(self, instance, command, value, device_name=None): key = self._storage_key(instance.name, command["name"], device_name) self._storage.set(key, value) def _storage_get(self, instance, command, device_name=None): key = self._storage_key(instance.name, command["name"], device_name) return self._storage.get(key) def _storage_unset(self, instance, command, device_name=None): key = self._storage_key(instance.name, command["name"], device_name) return self._storage.unset(key) # # Command execution, verification, and cleanup. # def _execute_all_non_device_commands(self, instance): for command in [command for command in list(self._commands.values()) if not command["per_device"]]: new_value = self._variables.expand(instance.options.get(command["name"], None)) if new_value is not None: self._execute_non_device_command(instance, command, new_value) def _execute_all_device_commands(self, instance, devices): for command in [command for command in list(self._commands.values()) if command["per_device"]]: new_value = self._variables.expand(instance.options.get(command["name"], None)) if new_value is None: continue for device in devices: self._execute_device_command(instance, command, device, new_value) def _verify_all_non_device_commands(self, instance, ignore_missing): ret = True for command in [command for command in list(self._commands.values()) if not command["per_device"]]: new_value = self._variables.expand(instance.options.get(command["name"], None)) if new_value is not None: if self._verify_non_device_command(instance, command, new_value, ignore_missing) == False: ret = False return ret def _verify_all_device_commands(self, instance, devices, ignore_missing): ret = True for command in [command for command in list(self._commands.values()) if command["per_device"]]: new_value = instance.options.get(command["name"], None) if new_value is None: continue for device in devices: if self._verify_device_command(instance, command, device, new_value, ignore_missing) == False: ret = False return ret def _process_assignment_modifiers(self, new_value, current_value): if new_value is not None: nws = str(new_value) if len(nws) <= 1: return new_value op = nws[:1] val = nws[1:] if current_value is None: return val if op in ["<", ">"] else new_value try: if op == ">": if int(val) > int(current_value): return val else: return None elif op == "<": if int(val) < int(current_value): return val else: return None except ValueError: log.warn("cannot compare new value '%s' with current value '%s' by operator '%s', using '%s' directly as new value" % (val, current_value, op, new_value)) return new_value def _get_current_value(self, command, device = None, ignore_missing=False): if device is not None: return command["get"](device, ignore_missing=ignore_missing) else: return command["get"]() def _check_and_save_value(self, instance, command, device = None, new_value = None): current_value = self._get_current_value(command, device) new_value = self._process_assignment_modifiers(new_value, current_value) if new_value is not None and current_value is not None: self._storage_set(instance, command, current_value, device) return new_value def _execute_device_command(self, instance, command, device, new_value): if command["custom"] is not None: command["custom"](True, new_value, device, False, False) else: new_value = self._check_and_save_value(instance, command, device, new_value) if new_value is not None: command["set"](new_value, device, sim = False, remove = False) def _execute_non_device_command(self, instance, command, new_value): if command["custom"] is not None: command["custom"](True, new_value, False, False) else: new_value = self._check_and_save_value(instance, command, None, new_value) if new_value is not None: command["set"](new_value, sim = False, remove = False) def _norm_value(self, value): v = self._cmd.unquote(str(value)) if re.match(r'\s*(0+,?)+([\da-fA-F]*,?)*\s*$', v): return re.sub(r'^\s*(0+,?)+', "", v) return v def _verify_value(self, name, new_value, current_value, ignore_missing, device = None): if new_value is None: return None ret = False if current_value is None and ignore_missing: if device is None: log.info(consts.STR_VERIFY_PROFILE_VALUE_MISSING % name) else: log.info(consts.STR_VERIFY_PROFILE_DEVICE_VALUE_MISSING % (device, name)) return True if current_value is not None: current_value = self._norm_value(current_value) new_value = self._norm_value(new_value) try: ret = int(new_value) == int(current_value) except ValueError: try: ret = int(new_value, 16) == int(current_value, 16) except ValueError: ret = str(new_value) == str(current_value) if not ret: vals = str(new_value).split('|') for val in vals: val = val.strip() ret = val == current_value if ret: break self._log_verification_result(name, ret, new_value, current_value, device = device) return ret def _log_verification_result(self, name, success, new_value, current_value, device = None): if success: if device is None: log.info(consts.STR_VERIFY_PROFILE_VALUE_OK % (name, str(current_value).strip())) else: log.info(consts.STR_VERIFY_PROFILE_DEVICE_VALUE_OK % (device, name, str(current_value).strip())) return True else: if device is None: log.error(consts.STR_VERIFY_PROFILE_VALUE_FAIL % (name, str(current_value).strip(), str(new_value).strip())) else: log.error(consts.STR_VERIFY_PROFILE_DEVICE_VALUE_FAIL % (device, name, str(current_value).strip(), str(new_value).strip())) return False def _verify_device_command(self, instance, command, device, new_value, ignore_missing): if command["custom"] is not None: return command["custom"](True, new_value, device, True, ignore_missing) current_value = self._get_current_value(command, device, ignore_missing=ignore_missing) new_value = self._process_assignment_modifiers(new_value, current_value) if new_value is None: return None new_value = command["set"](new_value, device, True, False) return self._verify_value(command["name"], new_value, current_value, ignore_missing, device) def _verify_non_device_command(self, instance, command, new_value, ignore_missing): if command["custom"] is not None: return command["custom"](True, new_value, True, ignore_missing) current_value = self._get_current_value(command) new_value = self._process_assignment_modifiers(new_value, current_value) if new_value is None: return None new_value = command["set"](new_value, True, False) return self._verify_value(command["name"], new_value, current_value, ignore_missing) def _cleanup_all_non_device_commands(self, instance): for command in reversed([command for command in list(self._commands.values()) if not command["per_device"]]): if (instance.options.get(command["name"], None) is not None) or (command["name"] in self._options_used_by_dynamic): self._cleanup_non_device_command(instance, command) def _cleanup_all_device_commands(self, instance, devices, remove = False): for command in reversed([command for command in list(self._commands.values()) if command["per_device"]]): if (instance.options.get(command["name"], None) is not None) or (command["name"] in self._options_used_by_dynamic): for device in devices: self._cleanup_device_command(instance, command, device, remove) def _cleanup_device_command(self, instance, command, device, remove = False): if command["custom"] is not None: command["custom"](False, None, device, False, False) else: old_value = self._storage_get(instance, command, device) if old_value is not None: command["set"](old_value, device, sim = False, remove = remove) self._storage_unset(instance, command, device) def _cleanup_non_device_command(self, instance, command): if command["custom"] is not None: command["custom"](False, None, False, False) else: old_value = self._storage_get(instance, command) if old_value is not None: command["set"](old_value, sim = False, remove = False) self._storage_unset(instance, command)