PNG  IHDRQgAMA a cHRMz&u0`:pQ<bKGDgmIDATxwUﹻ& ^CX(J I@ "% (** BX +*i"]j(IH{~R)[~>h{}gy)I$Ij .I$I$ʊy@}x.: $I$Ii}VZPC)I$IF ^0ʐJ$I$Q^}{"r=OzI$gRZeC.IOvH eKX $IMpxsk.쒷/&r[޳<v| .I~)@$updYRa$I |M.e JaֶpSYR6j>h%IRز if&uJ)M$I vLi=H;7UJ,],X$I1AҒJ$ XY XzI@GNҥRT)E@;]K*Mw;#5_wOn~\ DC&$(A5 RRFkvIR}l!RytRl;~^ǷJj اy뷦BZJr&ӥ8Pjw~vnv X^(I;4R=P[3]J,]ȏ~:3?[ a&e)`e*P[4]T=Cq6R[ ~ޤrXR Հg(t_HZ-Hg M$ãmL5R uk*`%C-E6/%[t X.{8P9Z.vkXŐKjgKZHg(aK9ڦmKjѺm_ \#$5,)-  61eJ,5m| r'= &ڡd%-]J on Xm|{ RҞe $eڧY XYrԮ-a7RK6h>n$5AVڴi*ֆK)mѦtmr1p| q:흺,)Oi*ֺK)ܬ֦K-5r3>0ԔHjJئEZj,%re~/z%jVMڸmrt)3]J,T K֦OvԒgii*bKiNO~%PW0=dii2tJ9Jݕ{7"I P9JKTbu,%r"6RKU}Ij2HKZXJ,妝 XYrP ެ24c%i^IK|.H,%rb:XRl1X4Pe/`x&P8Pj28Mzsx2r\zRPz4J}yP[g=L) .Q[6RjWgp FIH*-`IMRaK9TXcq*I y[jE>cw%gLRԕiFCj-ďa`#e~I j,%r,)?[gp FI˨mnWX#>mʔ XA DZf9,nKҲzIZXJ,L#kiPz4JZF,I,`61%2s $,VOϚ2/UFJfy7K> X+6 STXIeJILzMfKm LRaK9%|4p9LwJI!`NsiazĔ)%- XMq>pk$-$Q2x#N ؎-QR}ᶦHZډ)J,l#i@yn3LN`;nڔ XuX5pF)m|^0(>BHF9(cզEerJI rg7 4I@z0\JIi䵙RR0s;$s6eJ,`n 䂦0a)S)A 1eJ,堌#635RIgpNHuTH_SԕqVe ` &S)>p;S$魁eKIuX`I4춒o}`m$1":PI<[v9^\pTJjriRŭ P{#{R2,`)e-`mgj~1ϣLKam7&U\j/3mJ,`F;M'䱀 .KR#)yhTq;pcK9(q!w?uRR,n.yw*UXj#\]ɱ(qv2=RqfB#iJmmL<]Y͙#$5 uTU7ӦXR+q,`I}qL'`6Kͷ6r,]0S$- [RKR3oiRE|nӦXR.(i:LDLTJjY%o:)6rxzҒqTJjh㞦I.$YR.ʼnGZ\ֿf:%55 I˼!6dKxm4E"mG_ s? .e*?LRfK9%q#uh$)i3ULRfK9yxm܌bj84$i1U^@Wbm4uJ,ҪA>_Ij?1v32[gLRD96oTaR׿N7%L2 NT,`)7&ƝL*꽙yp_$M2#AS,`)7$rkTA29_Iye"|/0t)$n XT2`YJ;6Jx".e<`$) PI$5V4]29SRI>~=@j]lp2`K9Jaai^" Ԋ29ORI%:XV5]JmN9]H;1UC39NI%Xe78t)a;Oi Ҙ>Xt"~G>_mn:%|~ޅ_+]$o)@ǀ{hgN;IK6G&rp)T2i୦KJuv*T=TOSV>(~D>dm,I*Ɛ:R#ۙNI%D>G.n$o;+#RR!.eU˽TRI28t)1LWϚ>IJa3oFbu&:tJ*(F7y0ZR ^p'Ii L24x| XRI%ۄ>S1]Jy[zL$adB7.eh4%%누>WETf+3IR:I3Xה)3אOۦSRO'ٺ)S}"qOr[B7ϙ.edG)^ETR"RtRݜh0}LFVӦDB^k_JDj\=LS(Iv─aTeZ%eUAM-0;~˃@i|l @S4y72>sX-vA}ϛBI!ݎߨWl*)3{'Y|iSlEڻ(5KtSI$Uv02,~ԩ~x;P4ցCrO%tyn425:KMlD ^4JRxSهF_}شJTS6uj+ﷸk$eZO%G*^V2u3EMj3k%)okI]dT)URKDS 7~m@TJR~荪fT"֛L \sM -0T KfJz+nإKr L&j()[E&I ߴ>e FW_kJR|!O:5/2跌3T-'|zX ryp0JS ~^F>-2< `*%ZFP)bSn"L :)+pʷf(pO3TMW$~>@~ū:TAIsV1}S2<%ޟM?@iT ,Eūoz%i~g|`wS(]oȤ8)$ ntu`өe`6yPl IzMI{ʣzʨ )IZ2= ld:5+請M$-ї;U>_gsY$ÁN5WzWfIZ)-yuXIfp~S*IZdt;t>KūKR|$#LcԀ+2\;kJ`]YǔM1B)UbG"IRߊ<xܾӔJ0Z='Y嵤 Leveg)$znV-º^3Ւof#0Tfk^Zs[*I꯳3{)ˬW4Ւ4 OdpbZRS|*I 55#"&-IvT&/윚Ye:i$ 9{LkuRe[I~_\ؠ%>GL$iY8 9ܕ"S`kS.IlC;Ҏ4x&>u_0JLr<J2(^$5L s=MgV ~,Iju> 7r2)^=G$1:3G< `J3~&IR% 6Tx/rIj3O< ʔ&#f_yXJiގNSz; Tx(i8%#4 ~AS+IjerIUrIj362v885+IjAhK__5X%nV%Iͳ-y|7XV2v4fzo_68"S/I-qbf; LkF)KSM$ Ms>K WNV}^`-큧32ŒVؙGdu,^^m%6~Nn&͓3ŒVZMsRpfEW%IwdǀLm[7W&bIRL@Q|)* i ImsIMmKmyV`i$G+R 0tV'!V)֏28vU7͒vHꦼtxꗞT ;S}7Mf+fIRHNZUkUx5SAJㄌ9MqμAIRi|j5)o*^'<$TwI1hEU^c_j?Е$%d`z cyf,XO IJnTgA UXRD }{H}^S,P5V2\Xx`pZ|Yk:$e ~ @nWL.j+ϝYb퇪bZ BVu)u/IJ_ 1[p.p60bC >|X91P:N\!5qUB}5a5ja `ubcVxYt1N0Zzl4]7­gKj]?4ϻ *[bg$)+À*x쳀ogO$~,5 زUS9 lq3+5mgw@np1sso Ӻ=|N6 /g(Wv7U;zωM=wk,0uTg_`_P`uz?2yI!b`kĸSo+Qx%!\οe|އԁKS-s6pu_(ֿ$i++T8=eY; צP+phxWQv*|p1. ά. XRkIQYP,drZ | B%wP|S5`~́@i޾ E;Չaw{o'Q?%iL{u D?N1BD!owPHReFZ* k_-~{E9b-~P`fE{AܶBJAFO wx6Rox5 K5=WwehS8 (JClJ~ p+Fi;ŗo+:bD#g(C"wA^ r.F8L;dzdIHUX݆ϞXg )IFqem%I4dj&ppT{'{HOx( Rk6^C٫O.)3:s(۳(Z?~ٻ89zmT"PLtw䥈5&b<8GZ-Y&K?e8,`I6e(֍xb83 `rzXj)F=l($Ij 2*(F?h(/9ik:I`m#p3MgLaKjc/U#n5S# m(^)=y=đx8ŬI[U]~SцA4p$-F i(R,7Cx;X=cI>{Km\ o(Tv2vx2qiiDJN,Ҏ!1f 5quBj1!8 rDFd(!WQl,gSkL1Bxg''՞^ǘ;pQ P(c_ IRujg(Wz bs#P­rz> k c&nB=q+ؔXn#r5)co*Ũ+G?7< |PQӣ'G`uOd>%Mctz# Ԫڞ&7CaQ~N'-P.W`Oedp03C!IZcIAMPUۀ5J<\u~+{9(FbbyAeBhOSܳ1 bÈT#ŠyDžs,`5}DC-`̞%r&ڙa87QWWp6e7 Rϫ/oY ꇅ Nܶըtc!LA T7V4Jsū I-0Pxz7QNF_iZgúWkG83 0eWr9 X]㾮݁#Jˢ C}0=3ݱtBi]_ &{{[/o[~ \q鯜00٩|cD3=4B_b RYb$óBRsf&lLX#M*C_L܄:gx)WΘsGSbuL rF$9';\4Ɍq'n[%p.Q`u hNb`eCQyQ|l_C>Lb꟟3hSb #xNxSs^ 88|Mz)}:](vbۢamŖ࿥ 0)Q7@0=?^k(*J}3ibkFn HjB׻NO z x}7p 0tfDX.lwgȔhԾŲ }6g E |LkLZteu+=q\Iv0쮑)QٵpH8/2?Σo>Jvppho~f>%bMM}\//":PTc(v9v!gոQ )UfVG+! 35{=x\2+ki,y$~A1iC6#)vC5^>+gǵ@1Hy٪7u;p psϰu/S <aʸGu'tD1ԝI<pg|6j'p:tպhX{o(7v],*}6a_ wXRk,O]Lܳ~Vo45rp"N5k;m{rZbΦ${#)`(Ŵg,;j%6j.pyYT?}-kBDc3qA`NWQū20/^AZW%NQ MI.X#P#,^Ebc&?XR tAV|Y.1!؅⨉ccww>ivl(JT~ u`ٵDm q)+Ri x/x8cyFO!/*!/&,7<.N,YDŽ&ܑQF1Bz)FPʛ?5d 6`kQձ λc؎%582Y&nD_$Je4>a?! ͨ|ȎWZSsv8 j(I&yj Jb5m?HWp=g}G3#|I,5v珿] H~R3@B[☉9Ox~oMy=J;xUVoj bUsl_35t-(ՃɼRB7U!qc+x4H_Qo֮$[GO<4`&č\GOc[.[*Af%mG/ ňM/r W/Nw~B1U3J?P&Y )`ѓZ1p]^l“W#)lWZilUQu`-m|xĐ,_ƪ|9i:_{*(3Gѧ}UoD+>m_?VPۅ15&}2|/pIOʵ> GZ9cmíتmnz)yߐbD >e}:) r|@R5qVSA10C%E_'^8cR7O;6[eKePGϦX7jb}OTGO^jn*媓7nGMC t,k31Rb (vyܴʭ!iTh8~ZYZp(qsRL ?b}cŨʊGO^!rPJO15MJ[c&~Z`"ѓޔH1C&^|Ш|rʼ,AwĴ?b5)tLU)F| &g٣O]oqSUjy(x<Ϳ3 .FSkoYg2 \_#wj{u'rQ>o;%n|F*O_L"e9umDds?.fuuQbIWz |4\0 sb;OvxOSs; G%T4gFRurj(֍ڑb uԖKDu1MK{1^ q; C=6\8FR艇!%\YÔU| 88m)֓NcLve C6z;o&X x59:q61Z(T7>C?gcļxѐ Z oo-08jہ x,`' ҔOcRlf~`jj".Nv+sM_]Zk g( UOPyεx%pUh2(@il0ݽQXxppx-NS( WO+轾 nFߢ3M<;z)FBZjciu/QoF 7R¥ ZFLF~#ȣߨ^<쩡ݛкvџ))ME>ώx4m#!-m!L;vv#~Y[đKmx9.[,UFS CVkZ +ߟrY٧IZd/ioi$%͝ب_ֶX3ܫhNU ZZgk=]=bbJS[wjU()*I =ώ:}-蹞lUj:1}MWm=̛ _ ¾,8{__m{_PVK^n3esw5ӫh#$-q=A̟> ,^I}P^J$qY~Q[ Xq9{#&T.^GVj__RKpn,b=`żY@^՝;z{paVKkQXj/)y TIc&F;FBG7wg ZZDG!x r_tƢ!}i/V=M/#nB8 XxЫ ^@CR<{䤭YCN)eKOSƟa $&g[i3.C6xrOc8TI;o hH6P&L{@q6[ Gzp^71j(l`J}]e6X☉#͕ ׈$AB1Vjh㭦IRsqFBjwQ_7Xk>y"N=MB0 ,C #o6MRc0|$)ف"1!ixY<B9mx `,tA>)5ػQ?jQ?cn>YZe Tisvh# GMމȇp:ԴVuږ8ɼH]C.5C!UV;F`mbBk LTMvPʍϤj?ԯ/Qr1NB`9s"s TYsz &9S%U԰> {<ؿSMxB|H\3@!U| k']$U+> |HHMLޢ?V9iD!-@x TIî%6Z*9X@HMW#?nN ,oe6?tQwڱ.]-y':mW0#!J82qFjH -`ѓ&M0u Uγmxϵ^-_\])@0Rt.8/?ٰCY]x}=sD3ojަЫNuS%U}ԤwHH>ڗjܷ_3gN q7[q2la*ArǓԖ+p8/RGM ]jacd(JhWko6ڎbj]i5Bj3+3!\j1UZLsLTv8HHmup<>gKMJj0@H%,W΃7R) ">c, xixј^ aܖ>H[i.UIHc U1=yW\=S*GR~)AF=`&2h`DzT󑓶J+?W+}C%P:|0H܆}-<;OC[~o.$~i}~HQ TvXΈr=b}$vizL4:ȰT|4~*!oXQR6Lk+#t/g lԁߖ[Jڶ_N$k*". xsxX7jRVbAAʯKҎU3)zSNN _'s?f)6X!%ssAkʱ>qƷb hg %n ~p1REGMHH=BJiy[<5 ǁJҖgKR*倳e~HUy)Ag,K)`Vw6bRR:qL#\rclK/$sh*$ 6덤 KԖc 3Z9=Ɣ=o>X Ώ"1 )a`SJJ6k(<c e{%kϊP+SL'TcMJWRm ŏ"w)qc ef꒵i?b7b('"2r%~HUS1\<(`1Wx9=8HY9m:X18bgD1u ~|H;K-Uep,, C1 RV.MR5άh,tWO8WC$ XRVsQS]3GJ|12 [vM :k#~tH30Rf-HYݺ-`I9%lIDTm\ S{]9gOڒMNCV\G*2JRŨ;Rҏ^ڽ̱mq1Eu?To3I)y^#jJw^Ńj^vvlB_⋌P4x>0$c>K†Aļ9s_VjTt0l#m>E-,,x,-W)سo&96RE XR.6bXw+)GAEvL)͞K4$p=Ũi_ѱOjb HY/+@θH9޼]Nԥ%n{ &zjT? Ty) s^ULlb,PiTf^<À] 62R^V7)S!nllS6~͝V}-=%* ʻ>G DnK<y&>LPy7'r=Hj 9V`[c"*^8HpcO8bnU`4JȪAƋ#1_\ XϘHPRgik(~G~0DAA_2p|J묭a2\NCr]M_0 ^T%e#vD^%xy-n}-E\3aS%yN!r_{ )sAw ڼp1pEAk~v<:`'ӭ^5 ArXOI驻T (dk)_\ PuA*BY]yB"l\ey hH*tbK)3 IKZ򹞋XjN n *n>k]X_d!ryBH ]*R 0(#'7 %es9??ښFC,ՁQPjARJ\Ρw K#jahgw;2$l*) %Xq5!U᢯6Re] |0[__64ch&_}iL8KEgҎ7 M/\`|.p,~`a=BR?xܐrQ8K XR2M8f ?`sgWS%" Ԉ 7R%$ N}?QL1|-эټwIZ%pvL3Hk>,ImgW7{E xPHx73RA @RS CC !\ȟ5IXR^ZxHл$Q[ŝ40 (>+ _C >BRt<,TrT {O/H+˟Pl6 I B)/VC<6a2~(XwV4gnXR ϱ5ǀHٻ?tw똤Eyxp{#WK qG%5],(0ӈH HZ])ג=K1j&G(FbM@)%I` XRg ʔ KZG(vP,<`[ Kn^ SJRsAʠ5xՅF`0&RbV tx:EaUE/{fi2;.IAwW8/tTxAGOoN?G}l L(n`Zv?pB8K_gI+ܗ #i?ޙ.) p$utc ~DžfՈEo3l/)I-U?aԅ^jxArA ΧX}DmZ@QLےbTXGd.^|xKHR{|ΕW_h] IJ`[G9{).y) 0X YA1]qp?p_k+J*Y@HI>^?gt.06Rn ,` ?);p pSF9ZXLBJPWjgQ|&)7! HjQt<| ؅W5 x W HIzYoVMGP Hjn`+\(dNW)F+IrS[|/a`K|ͻ0Hj{R,Q=\ (F}\WR)AgSG`IsnAR=|8$}G(vC$)s FBJ?]_u XRvύ6z ŨG[36-T9HzpW̞ú Xg큽=7CufzI$)ki^qk-) 0H*N` QZkk]/tnnsI^Gu't=7$ Z;{8^jB% IItRQS7[ϭ3 $_OQJ`7!]W"W,)Iy W AJA;KWG`IY{8k$I$^%9.^(`N|LJ%@$I}ֽp=FB*xN=gI?Q{٥4B)mw $Igc~dZ@G9K X?7)aK%݅K$IZ-`IpC U6$I\0>!9k} Xa IIS0H$I H ?1R.Чj:4~Rw@p$IrA*u}WjWFPJ$I➓/6#! LӾ+ X36x8J |+L;v$Io4301R20M I$-E}@,pS^ޟR[/s¹'0H$IKyfŸfVOπFT*a$I>He~VY/3R/)>d$I>28`Cjw,n@FU*9ttf$I~<;=/4RD~@ X-ѕzἱI$: ԍR a@b X{+Qxuq$IЛzo /~3\8ڒ4BN7$IҀj V]n18H$IYFBj3̵̚ja pp $Is/3R Ӻ-Yj+L;.0ŔI$Av? #!5"aʄj}UKmɽH$IjCYs?h$IDl843.v}m7UiI=&=0Lg0$I4: embe` eQbm0u? $IT!Sƍ'-sv)s#C0:XB2a w I$zbww{."pPzO =Ɔ\[ o($Iaw]`E).Kvi:L*#gР7[$IyGPI=@R 4yR~̮´cg I$I/<tPͽ hDgo 94Z^k盇΄8I56^W$I^0̜N?4*H`237}g+hxoq)SJ@p|` $I%>-hO0eO>\ԣNߌZD6R=K ~n($I$y3D>o4b#px2$yڪtzW~a $I~?x'BwwpH$IZݑnC㧄Pc_9sO gwJ=l1:mKB>Ab<4Lp$Ib o1ZQ@85b̍ S'F,Fe,^I$IjEdù{l4 8Ys_s Z8.x m"+{~?q,Z D!I$ϻ'|XhB)=…']M>5 rgotԎ 獽PH$IjIPhh)n#cÔqA'ug5qwU&rF|1E%I$%]!'3AFD/;Ck_`9 v!ٴtPV;x`'*bQa w I$Ix5 FC3D_~A_#O݆DvV?<qw+I$I{=Z8".#RIYyjǪ=fDl9%M,a8$I$Ywi[7ݍFe$s1ՋBVA?`]#!oz4zjLJo8$I$%@3jAa4(o ;p,,dya=F9ً[LSPH$IJYЉ+3> 5"39aZ<ñh!{TpBGkj}Sp $IlvF.F$I z< '\K*qq.f<2Y!S"-\I$IYwčjF$ w9 \ߪB.1v!Ʊ?+r:^!I$BϹB H"B;L'G[ 4U#5>੐)|#o0aڱ$I>}k&1`U#V?YsV x>{t1[I~D&(I$I/{H0fw"q"y%4 IXyE~M3 8XψL}qE$I[> nD?~sf ]o΁ cT6"?'_Ἣ $I>~.f|'!N?⟩0G KkXZE]ޡ;/&?k OۘH$IRۀwXӨ<7@PnS04aӶp.:@\IWQJ6sS%I$e5ڑv`3:x';wq_vpgHyXZ 3gЂ7{{EuԹn±}$I$8t;b|591nءQ"P6O5i }iR̈́%Q̄p!I䮢]O{H$IRϻ9s֧ a=`- aB\X0"+5"C1Hb?߮3x3&gşggl_hZ^,`5?ߎvĸ%̀M!OZC2#0x LJ0 Gw$I$I}<{Eb+y;iI,`ܚF:5ܛA8-O-|8K7s|#Z8a&><a&/VtbtLʌI$I$I$I$I$I$IRjDD%tEXtdate:create2022-05-31T04:40:26+00:00!Î%tEXtdate:modify2022-05-31T04:40:26+00:00|{2IENDB`Mini Shell

HOME


Mini Shell 1.0
DIR:/opt/cloudlinux/venv/lib64/python3.11/site-packages/lvestats/plugins/other/
Upload File :
Current File : //opt/cloudlinux/venv/lib64/python3.11/site-packages/lvestats/plugins/other/v1_db_migrator.py
# coding=utf-8
#
# Copyright © Cloud Linux GmbH & Cloud Linux Software, Inc 2010-2019 All Rights Reserved
#
# Licensed under CLOUD LINUX LICENSE AGREEMENT
# http://cloudlinux.com/docs/LICENSE.TXT

import logging
import os
import time
from datetime import datetime, timedelta

import sqlalchemy
from sqlalchemy import Column, Float, Integer, String, func, insert
from sqlalchemy.exc import DatabaseError, SQLAlchemyError
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

from lvestats.core.plugin import LveStatsPlugin, LveStatsPluginTerminated
from lvestats.lib import uidconverter
from lvestats.lib.commons.dateutil import gm_datetime_to_unixtimestamp
from lvestats.lib.commons.func import get_chunks
from lvestats.lib.dbengine import fix_lost_keep_alive, validate_database
from lvestats.orm.history import history, history_x60
from lvestats.orm.history_gov import history_gov

STATE_FILE = '/var/lve/v1_migration_last.ts'
V2_KEYS = [
    'id',
    'mem',
    'mem_limit',
    'mem_fault',
    'memphy',
    'lmemphy',
    'memphy_fault',
    'mep',
    'mep_limit',
    'mep_fault',
    'nproc',
    'lnproc',
    'nproc_fault',
    'iops',
    'liops',
]

V2_GOV_KEYS = [
    'username',
    'sum_cpu',
    'sum_write',
    'sum_read',
    'limit_cpu_on_period_end',
    'limit_read_on_period_end',
    'limit_write_on_period_end',
    'cause_of_restrict',
]

V1Base = declarative_base()


class V1HistoryGov(V1Base):
    """
    Mapping out v1 gov history table
    """

    __tablename__ = 'history_gov'

    ts = Column('ts', Integer, primary_key=True)
    username = Column('username', String(64), primary_key=True)
    sum_cpu = Column('sum_cpu', Float)
    sum_write = Column('sum_write', Float)
    sum_read = Column('sum_read', Float)
    limit_cpu_on_period_end = Column('limit_cpu_on_period_end', Integer)
    limit_read_on_period_end = Column('limit_read_on_period_end', Integer)
    limit_write_on_period_end = Column('limit_write_on_period_end', Integer)
    cause_of_restrict = Column('cause_of_restrict', Integer)
    server_id = Column('server_id', String(10), primary_key=True)
    weight = Column('weight', Integer)


class V1History(V1Base):
    """
    Mapping out v1 history table
    """

    __tablename__ = 'history'

    id = Column('id', Integer, primary_key=True)
    cpu = Column('cpu', Integer)
    cpu_limit = Column('cpu_limit', Integer)
    cpu_max = Column('cpu_max', Integer)
    ncpu = Column('ncpu', Integer)
    mep = Column('mep', Integer)
    mep_limit = Column('mep_limit', Integer)
    mep_max = Column('mep_max', Integer)
    io = Column('io', Integer)
    io_max = Column('io_max', Integer)
    io_limit = Column('io_limit', Integer)
    mem = Column('mem', Integer)
    mem_limit = Column('mem_limit', Integer)
    mem_max = Column('mem_max', Integer)
    mem_fault = Column('mem_fault', Integer)
    mep_fault = Column('mep_fault', Integer)
    created = Column('created', sqlalchemy.types.DateTime, primary_key=True)
    weight = Column('weight', Integer)
    server_id = Column('server_id', String(10))
    lmemphy = Column('lmemphy', Integer)
    memphy = Column('memphy', Integer)
    memphy_max = Column('memphy_max', Integer)
    memphy_fault = Column('memphy_fault', Integer)
    lnproc = Column('lnproc', Integer)
    nproc = Column('nproc', Integer)
    nproc_max = Column('nproc_max', Integer)
    nproc_fault = Column('nproc_fault', Integer)
    iops = Column('iops', Integer)
    iops_max = Column('iops_max', Integer)
    liops = Column('liops', Integer)


class V1TimeInterval(object):
    """
    The way it would work - on first run, the /var/lve/v1_migration_last.ts will be non-existant, and we will
    use latest timestamp from V1 db as the 'starting point'
    After that on each call of get_data we will use that 'starting point' to get_period from start point to 1 hour
    before. As soon as our start point is > 30 days old -- we will return as part of get_period third parameter true
    which means that ok, the rest of data is too old, lets move on.
    V1DBMigrator will convert data for that period, and then will call save_state(from) -- this will be new starting
    point for the next plugin run. We will store it in a property (last_ts), and save it to the file.
    So, that even if software restarted, we don't just ignore it.
    """

    def __init__(self, v1session, ts_file=STATE_FILE, server_id='localhost'):
        self.ts_file = ts_file
        self.server_id = server_id
        self.last_ts = None
        self.last_uid = -1
        self.v1session = v1session
        self.read_state()

    def save_ts_to_file(self, ts, uid=None):
        with open(self.ts_file, 'w', encoding='utf-8') as f:
            f.write(ts.strftime(self.get_ts_format()))
            self.last_ts = ts
            if uid is not None:
                f.write('\n' + str(uid))
                self.last_uid = uid or -1
            f.close()

    @staticmethod
    def get_ts_format():
        return "%Y-%m-%d %H:%M:%S.%f"

    def save_timestamp(self, ts):
        self._save_state(ts)

    def save_uid(self, uid=None):
        self._save_state(self.last_ts, uid)

    def _save_state(self, ts, uid=None):
        try:
            self.save_ts_to_file(ts, uid)
        except IOError as e:
            logging.getLogger('plugin.V1DBMigrator.TimeInterval').error("Unable to save v1 migration TS %s", str(e))

    def _read_state(self):
        ts = None
        try:
            with open(self.ts_file, 'r', encoding='utf-8') as f:
                ts = datetime.strptime(f.readline().rstrip(), self.get_ts_format())
                uid = int(f.readline().rstrip() or -1)
                return ts, uid
        except IOError:
            return ts, -1
        except ValueError as e:
            logging.getLogger('plugin.V1DBMigrator.TimeInterval').warning(
                "Unable to read %s (%s)",
                self.ts_file,
                e,
            )
            return ts, -1

    def read_state(self):
        self.last_ts, self.last_uid = self._read_state()
        if self.last_ts is None:
            res = (
                self.v1session.query(func.max(V1History.created))
                .filter(V1History.server_id == self.server_id).first()
            )
            # set very old datetime if no rows in database
            last_ts_from_db = res[0] or datetime(1, 1, 1)
            self.last_ts = last_ts_from_db + timedelta(microseconds=1)

    def _to_ts(self):
        self.read_state()
        return self.last_ts - timedelta(microseconds=1)

    def is_too_old(self):
        return datetime.now() - timedelta(days=30) > self._to_ts()

    def get_uid(self):
        self.read_state()
        return self.last_uid

    def convert_username_to_uid(self, username):
        pass

    def _get_history_gov_users(self):
        from_ts, to_ts = self.get_period()
        from_ts_ = gm_datetime_to_unixtimestamp(from_ts)
        to_ts_ = gm_datetime_to_unixtimestamp(to_ts)
        usernames_ = (
            self.v1session.query(V1HistoryGov)
            .filter(V1HistoryGov.ts.between(from_ts_, to_ts_), V1HistoryGov.server_id == self.server_id)
            .distinct(V1HistoryGov.username)
            .group_by(V1HistoryGov.username)
        )
        return [item.username for item in usernames_]

    def _get_history_uids(self):
        from_ts, to_ts = self.get_period()
        uids_ = (
            self.v1session.query(V1History)
            .filter(
                V1History.created.between(from_ts, to_ts),
                V1History.server_id == self.server_id,
                V1History.id > self.last_uid,
            )
            .distinct(V1History.id)
            .group_by(V1History.id)
        )
        return [item.id for item in uids_]

    def get_uids(self):
        uids_list = self._get_history_uids()
        for username in self._get_history_gov_users():
            uid = self.convert_username_to_uid(username)
            if uid is not None and uid > self.last_uid and uid not in uids_list:
                uids_list.append(uid)
        return sorted(uids_list)

    def get_period(self):
        """We want to go 1 hour at a time, up to 1 month back, starting from now"""
        to_ts = self._to_ts()
        from_ts = self.last_ts - timedelta(hours=1)
        return from_ts, to_ts


class Break(Exception):
    pass


class V1DBMigrator(LveStatsPlugin):
    PLUGIN_LOCATION = '/usr/share/lve-stats/plugins/v1_db_migrator.py'
    timeout = 18  # change default timeout
    is_done = False
    period = 60  # every minute
    order = 9500  # We pretty much want to be last one standing
    v1_connect_string = None
    V1Session = None  # We will need it to create session on each execution
    time_interval = None
    debug = True
    skip_on_error = True  # What if we cannot save data for some reason, if True, skip it
    v2_server_id = 'localhost'
    v1_server_id = 'localhost'

    def __init__(self):
        self.log = logging.getLogger('plugin.V1DBMigrator')
        self._username_to_uid_cache = {}
        self._no_such_uid_cache = []
        self._procs = 1
        self.now = 0  # This changes in MainLoop
        self.log.info("V1 Migration Started")
        self._time_commit = self.timeout * 0.5  # time limit for stopping plugin
        self.control_time = True
        self._conn = None
        self._database_does_not_exist = False

    def set_config(self, config):
        self.v1_server_id = config.get('v1_server_id', 'localhost')
        self.v2_server_id = config.get('server_id', 'localhost')
        self.v1_connect_string = config.get('v1_connect_string')
        self.debug = config.get('debug', 'F').lower() in ('t', 'y', 'true', 'yes', 1)
        self.init_v1_db()

    def init_v1_db(self, ts=STATE_FILE):
        if self.v1_connect_string is None:
            self._database_does_not_exist = True
            return
        # check present sqlite database
        sqlite = 'sqlite:///'
        if self.v1_connect_string.startswith(sqlite) and not os.path.exists(self.v1_connect_string[len(sqlite):]):
            self.log.warning('Database "%s" does not exist', self.v1_connect_string)
            self._database_does_not_exist = True
            return
        # create database engine
        try:
            v1_db_engine = sqlalchemy.engine.create_engine(self.v1_connect_string, echo=self.debug)
        except SQLAlchemyError as e:
            self.log.warning(str(e))
            self._database_does_not_exist = True
            return

        # check present history table
        if not v1_db_engine.dialect.has_table(v1_db_engine, V1History.__tablename__):
            self.log.warning(
                'Table "%s" in database "%s" does not exist',
                V1History.__tablename__,
                self.v1_connect_string,
            )
            self._database_does_not_exist = True
            return

        result = validate_database(v1_db_engine, hide_logging=True, base=V1Base)
        if result['column_error'] or result['table_error']:
            self.log.warning('V1 database malformed, migration skipped.')
            self._database_does_not_exist = True
            return

        self.V1Session = sessionmaker(bind=v1_db_engine)
        self.time_interval = V1TimeInterval(self.get_v1_session(), ts, self.v1_server_id)
        self.time_interval.convert_username_to_uid = self.convert_username_to_uid

    def get_v1_session(self):
        return self.V1Session()

    def execute(self, lve_data):
        self._procs = lve_data.get('procs', 1)
        if self.is_done:  # all data had been migrated
            return
        if self._database_does_not_exist or self.time_interval.is_too_old():
            self.log.warning("V1 Migration Done")
            self.cleanup()
            self.fix_lost_keep_alive_records()
        else:
            self.convert_all()

    def fix_lost_keep_alive_records(self):
        session = sessionmaker(bind=self.engine)()
        fix_lost_keep_alive(session, server_id=self.v2_server_id, log_=self.log)
        session.close()

    def cleanup(self):
        """
        There is not much to do on clean up. Lets just set flag done = True, and remove plugin
        so that on next restart it would't be running any more
        :return:
        """
        self.is_done = True
        try:
            os.remove(V1DBMigrator.PLUGIN_LOCATION)
            # remove compiled python code
            os.remove(V1DBMigrator.PLUGIN_LOCATION + 'c')
        except (IOError, OSError) as e:
            self.log.error("Unable to remove %s: %s", V1DBMigrator.PLUGIN_LOCATION, str(e))
        session = sessionmaker(bind=self.engine)()
        try:
            session.query(history_x60).filter(history_x60.server_id == self.v2_server_id).delete()
            session.commit()
        except SQLAlchemyError:
            session.rollback()

    def get_v1_gov_data(self, from_ts, to_ts, username):
        from_ts_ = gm_datetime_to_unixtimestamp(from_ts)
        to_ts_ = gm_datetime_to_unixtimestamp(to_ts)
        return (
            self.get_v1_session()
            .query(V1HistoryGov)
            .filter(
                V1HistoryGov.ts.between(from_ts_, to_ts_),
                V1HistoryGov.username == username,
                V1HistoryGov.server_id == self.v1_server_id,
            )
            .all()
        )

    def get_v1_data(self, from_ts, to_ts, uid):
        return (
            self.get_v1_session()
            .query(V1History)
            .filter(
                V1History.created.between(from_ts, to_ts),
                V1History.server_id == self.v1_server_id,
                V1History.id == uid
            )
            .order_by(V1History.id)
            .all()
        )

    def _convert_data(self, from_ts, to_ts, uid, trans):
        username = self.convert_uid_to_username(uid)
        try:
            v2_rows_insert_list = []
            for row in self.get_v1_data(from_ts, to_ts, uid):
                v2_rows = self.convert_row(row, self._procs)
                v2_rows_insert_list.extend(v2_rows)
            if v2_rows_insert_list:
                for chunk in get_chunks(v2_rows_insert_list):
                    self._conn.execute(insert(history), chunk)

            v2_gov_rows_insert_list = []
            if username and username != 'root':  # ignore uid 0 (root)
                for row in self.get_v1_gov_data(from_ts, to_ts, username):
                    v2_gov_rows = self.convert_gov_row(row)
                    v2_gov_rows_insert_list.extend(v2_gov_rows)
            if v2_gov_rows_insert_list:
                for chunk in get_chunks(v2_gov_rows_insert_list):
                    self._conn.execute(insert(history_gov), chunk)
        except (SQLAlchemyError, DatabaseError) as e:
            trans.rollback()
            self.log.warning('Can not save data to database: %s', str(e))
            if not self.skip_on_error:
                raise e
        except LveStatsPluginTerminated as e:
            trans.commit()
            self.log.debug("Plugin is terminated.")
            raise Break() from e

    def _work_time(self):
        return time.time() - self.now  # calculate plugin working time

    def _need_break(self):
        return self.timeout - self._work_time() < self._time_commit * 1.2

    def convert_data(self, from_ts, to_ts):
        self.log.debug('Start converting from %s to %s', from_ts, to_ts)
        uids = self.time_interval.get_uids()  # obtain uids need convert
        if not uids:
            return
        trans = self._conn.begin()
        for uid in uids:
            self._convert_data(from_ts, to_ts, uid, trans)
            self.time_interval.save_uid(uid)
            self.log.debug(
                'Converted from %s to %s uid: %s; plugin work time %s',
                from_ts,
                to_ts,
                uid,
                self._work_time(),
            )

            # control plugin work time
            if self.control_time and self._need_break():
                self.log.debug(
                    'Stop converting; plugin work time %s',
                    self._work_time(),
                )
                raise Break()
        if trans.is_active:
            trans.commit()

    def convert_all(self):
        with self.engine.begin() as self._conn:
            try:
                while not self._need_break() and not self.time_interval.is_too_old():
                    from_ts, to_ts = self.time_interval.get_period()
                    self.convert_data(from_ts, to_ts)
                    self.time_interval.save_timestamp(from_ts)  # save timestamp if not breacke cycle only
            except Break:  # for break all cycles
                pass
            time_start = time.time()
        commit_time = time.time() - time_start
        self._time_commit = max(self._time_commit, commit_time)
        self.log.debug('Commit time %s', commit_time)

    @staticmethod
    def fault_count(limit, _max):
        if limit == _max:
            return 1
        else:
            return 0

    @staticmethod
    def convert_iops_faults(v1_row, v2_row):
        # v1 & v2 store IOPS the same way, but faults are not tracked in v1
        v2_row['iops_fault'] = V1DBMigrator.fault_count(v1_row.liops, v1_row.iops_max)

    @staticmethod
    def convert_io(v1_row, v2_row):
        # v1 stores IO in KB/s, v2 in B/s
        v2_row['io'] = v1_row.io * 1024
        v2_row['io_limit'] = v1_row.io_limit * 1024
        v2_row['io_fault'] = V1DBMigrator.fault_count(v1_row.io_limit, v1_row.io_max)

    @staticmethod
    def convert_cpu_(procs, cpu, cpu_limit, cpu_max, ncpu):
        """
        v1 holds CPU relative to total cores, where on 4 core system 1 core is 25%
        it also limits by ncpu (whatever is less), so on 4 cores system 2 ncpu and 30% is 30%
        of all cores (as 2ncpu = 50%, and we take smaller), and 2 ncpu and 70% is 50%, as
        2ncpu = 50% / we take smaller
        To switch to new limit, we need to talke old limit and multiply it by 100
        So 25% on 4 core system in v1 (1 core), is 25 * 4 * 100 = 10,000
        """
        v2_cpu_limit = min(100 * cpu_limit * procs, ncpu * 100 * 100)
        # no matter what mistake we make, lets not ever set CPU usage > CPU limit
        v2_cpu = min(v2_cpu_limit, cpu * procs * 100)
        # if cpu_limit == cpu_max, lets consider it to be a fault, note we loose precision
        # anyway, so if weight was 60, we will add 60 faults... oh well.
        v2_cpu_faults = V1DBMigrator.fault_count(v2_cpu_limit, 100 * cpu_max * procs)
        return v2_cpu, v2_cpu_limit, v2_cpu_faults

    def convert_cpu(self, row, v2_row, procs):
        v2_row['cpu'], v2_row['cpu_limit'], v2_row['cpu_fault'] = self.convert_cpu_(
            procs, row.cpu, row.cpu_limit, row.cpu_max, row.ncpu
        )

    def convert_username_to_uid(self, username):
        if username in self._username_to_uid_cache:
            return self._username_to_uid_cache[username]
        uid = uidconverter.username_to_uid_local(username)
        self._username_to_uid_cache[username] = uid
        if uid is None:
            self.log.warning('Can not find uid for user %s', username)
        return uid

    def convert_uid_to_username(self, uid):
        if uid in self._no_such_uid_cache:
            return
        for username_, uid_ in self._username_to_uid_cache.items():
            if uid == uid_:
                return username_
        username_ = uidconverter.uid_to_username_local(uid)
        if username_ is None:
            self._no_such_uid_cache.append(uid)
            self.log.warning('Can not find user name for uid %s', uid)
        else:
            self._username_to_uid_cache[username_] = uid
        return username_

    def convert_gov_row(self, row):
        to_ts = row.ts
        result = []
        for i in range(0, row.weight):
            v2_gov_row = {'server_id': self.v2_server_id, 'ts': to_ts - 60 * i}
            for key in V2_GOV_KEYS:
                v2_gov_row[key] = getattr(row, key)
            uid = self.convert_username_to_uid(v2_gov_row.pop('username'))
            if uid:
                v2_gov_row['uid'] = uid
                result.append(v2_gov_row)
        return result

    def convert_row(self, row, procs):
        to_ts = gm_datetime_to_unixtimestamp(row.created)
        result = []
        for i in range(0, row.weight):
            v2_row = {'server_id': self.v2_server_id, 'created': to_ts - 60 * i}
            for key in V2_KEYS:
                v2_row[key] = getattr(row, key)
            self.convert_cpu(row, v2_row, procs)
            self.convert_io(row, v2_row)
            self.convert_iops_faults(row, v2_row)
            result.append(v2_row)
        return result