PNG  IHDRX cHRMz&u0`:pQ<bKGD pHYsodtIME MeqIDATxw]Wug^Qd˶ 6`!N:!@xI~)%7%@Bh&`lnjVF29gΨ4E$|>cɚ{gk= %,a KX%,a KX%,a KX%,a KX%,a KX%,a KX%, b` ǟzeאfp]<!SJmɤY޲ڿ,%c ~ع9VH.!Ͳz&QynֺTkRR.BLHi٪:l;@(!MԴ=žI,:o&N'Kù\vRmJ雵֫AWic H@" !: Cé||]k-Ha oݜ:y F())u]aG7*JV@J415p=sZH!=!DRʯvɱh~V\}v/GKY$n]"X"}t@ xS76^[bw4dsce)2dU0 CkMa-U5tvLƀ~mlMwfGE/-]7XAƟ`׮g ewxwC4\[~7@O-Q( a*XGƒ{ ՟}$_y3tĐƤatgvێi|K=uVyrŲlLӪuܿzwk$m87k( `múcE)"@rK( z4$D; 2kW=Xb$V[Ru819קR~qloѱDyįݎ*mxw]y5e4K@ЃI0A D@"BDk_)N\8͜9dz"fK0zɿvM /.:2O{ Nb=M=7>??Zuo32 DLD@D| &+֎C #B8ַ`bOb $D#ͮҪtx]%`ES`Ru[=¾!@Od37LJ0!OIR4m]GZRJu$‡c=%~s@6SKy?CeIh:[vR@Lh | (BhAMy=݃  G"'wzn޺~8ԽSh ~T*A:xR[ܹ?X[uKL_=fDȊ؂p0}7=D$Ekq!/t.*2ʼnDbŞ}DijYaȲ(""6HA;:LzxQ‘(SQQ}*PL*fc\s `/d'QXW, e`#kPGZuŞuO{{wm[&NBTiiI0bukcA9<4@SӊH*؎4U/'2U5.(9JuDfrޱtycU%j(:RUbArLֺN)udA':uGQN"-"Is.*+k@ `Ojs@yU/ H:l;@yyTn}_yw!VkRJ4P)~y#)r,D =ě"Q]ci'%HI4ZL0"MJy 8A{ aN<8D"1#IJi >XjX֔#@>-{vN!8tRݻ^)N_╗FJEk]CT՟ YP:_|H1@ CBk]yKYp|og?*dGvzنzӴzjֺNkC~AbZƷ`.H)=!QͷVTT(| u78y֮}|[8-Vjp%2JPk[}ԉaH8Wpqhwr:vWª<}l77_~{s۴V+RCģ%WRZ\AqHifɤL36: #F:p]Bq/z{0CU6ݳEv_^k7'>sq*+kH%a`0ԣisqにtү04gVgW΂iJiS'3w.w}l6MC2uԯ|>JF5`fV5m`Y**Db1FKNttu]4ccsQNnex/87+}xaUW9y>ͯ骵G{䩓Գ3+vU}~jJ.NFRD7<aJDB1#ҳgSb,+CS?/ VG J?|?,2#M9}B)MiE+G`-wo߫V`fio(}S^4e~V4bHOYb"b#E)dda:'?}׮4繏`{7Z"uny-?ǹ;0MKx{:_pÚmFמ:F " .LFQLG)Q8qN q¯¯3wOvxDb\. BKD9_NN &L:4D{mm o^tֽ:q!ƥ}K+<"m78N< ywsard5+вz~mnG)=}lYݧNj'QJS{S :UYS-952?&O-:W}(!6Mk4+>A>j+i|<<|;ر^߉=HE|V#F)Emm#}/"y GII웻Jі94+v뾧xu~5C95~ūH>c@덉pʃ1/4-A2G%7>m;–Y,cyyaln" ?ƻ!ʪ<{~h~i y.zZB̃/,雋SiC/JFMmBH&&FAbϓO^tubbb_hZ{_QZ-sύodFgO(6]TJA˯#`۶ɟ( %$&+V'~hiYy>922 Wp74Zkq+Ovn錄c>8~GqܲcWꂎz@"1A.}T)uiW4="jJ2W7mU/N0gcqܗOO}?9/wìXžΏ0 >֩(V^Rh32!Hj5`;O28؇2#ݕf3 ?sJd8NJ@7O0 b־?lldщ̡&|9C.8RTWwxWy46ah嘦mh٤&l zCy!PY?: CJyв]dm4ǜҐR޻RլhX{FƯanшQI@x' ao(kUUuxW_Ñ줮[w8 FRJ(8˼)_mQ _!RJhm=!cVmm ?sFOnll6Qk}alY}; "baӌ~M0w,Ggw2W:G/k2%R,_=u`WU R.9T"v,<\Ik޽/2110Ӿxc0gyC&Ny޽JҢrV6N ``یeA16"J³+Rj*;BϜkZPJaÍ<Jyw:NP8/D$ 011z֊Ⱳ3ι֘k1V_"h!JPIΣ'ɜ* aEAd:ݺ>y<}Lp&PlRfTb1]o .2EW\ͮ]38؋rTJsǏP@芎sF\> P^+dYJLbJ C-xϐn> ι$nj,;Ǖa FU *择|h ~izť3ᤓ`K'-f tL7JK+vf2)V'-sFuB4i+m+@My=O҈0"|Yxoj,3]:cо3 $#uŘ%Y"y죯LebqtҢVzq¼X)~>4L׶m~[1_k?kxֺQ`\ |ٛY4Ѯr!)N9{56(iNq}O()Em]=F&u?$HypWUeB\k]JɩSع9 Zqg4ZĊo oMcjZBU]B\TUd34ݝ~:7ڶSUsB0Z3srx 7`:5xcx !qZA!;%͚7&P H<WL!džOb5kF)xor^aujƍ7 Ǡ8/p^(L>ὴ-B,{ۇWzֺ^k]3\EE@7>lYBȝR.oHnXO/}sB|.i@ɥDB4tcm,@ӣgdtJ!lH$_vN166L__'Z)y&kH;:,Y7=J 9cG) V\hjiE;gya~%ks_nC~Er er)muuMg2;֫R)Md) ,¶ 2-wr#F7<-BBn~_(o=KO㭇[Xv eN_SMgSҐ BS헃D%g_N:/pe -wkG*9yYSZS.9cREL !k}<4_Xs#FmҶ:7R$i,fi!~' # !6/S6y@kZkZcX)%5V4P]VGYq%H1!;e1MV<!ϐHO021Dp= HMs~~a)ަu7G^];git!Frl]H/L$=AeUvZE4P\.,xi {-~p?2b#amXAHq)MWǾI_r`S Hz&|{ +ʖ_= (YS(_g0a03M`I&'9vl?MM+m~}*xT۲(fY*V4x@29s{DaY"toGNTO+xCAO~4Ϳ;p`Ѫ:>Ҵ7K 3}+0 387x\)a"/E>qpWB=1 ¨"MP(\xp߫́A3+J] n[ʼnӼaTbZUWb={~2ooKױӰp(CS\S筐R*JغV&&"FA}J>G֐p1ٸbk7 ŘH$JoN <8s^yk_[;gy-;߉DV{c B yce% aJhDȶ 2IdйIB/^n0tNtџdcKj4϶v~- CBcgqx9= PJ) dMsjpYB] GD4RDWX +h{y`,3ꊕ$`zj*N^TP4L:Iz9~6s) Ga:?y*J~?OrMwP\](21sZUD ?ܟQ5Q%ggW6QdO+\@ ̪X'GxN @'4=ˋ+*VwN ne_|(/BDfj5(Dq<*tNt1х!MV.C0 32b#?n0pzj#!38}޴o1KovCJ`8ŗ_"]] rDUy޲@ Ȗ-;xџ'^Y`zEd?0„ DAL18IS]VGq\4o !swV7ˣι%4FѮ~}6)OgS[~Q vcYbL!wG3 7띸*E Pql8=jT\꘿I(z<[6OrR8ºC~ډ]=rNl[g|v TMTղb-o}OrP^Q]<98S¤!k)G(Vkwyqyr޽Nv`N/e p/~NAOk \I:G6]4+K;j$R:Mi #*[AȚT,ʰ,;N{HZTGMoּy) ]%dHء9Պ䠬|<45,\=[bƟ8QXeB3- &dҩ^{>/86bXmZ]]yޚN[(WAHL$YAgDKp=5GHjU&99v簪C0vygln*P)9^͞}lMuiH!̍#DoRBn9l@ xA/_v=ȺT{7Yt2N"4!YN`ae >Q<XMydEB`VU}u]嫇.%e^ánE87Mu\t`cP=AD/G)sI"@MP;)]%fH9'FNsj1pVhY&9=0pfuJ&gޤx+k:!r˭wkl03׼Ku C &ѓYt{.O.zҏ z}/tf_wEp2gvX)GN#I ݭ߽v/ .& и(ZF{e"=V!{zW`, ]+LGz"(UJp|j( #V4, 8B 0 9OkRrlɱl94)'VH9=9W|>PS['G(*I1==C<5"Pg+x'K5EMd؞Af8lG ?D FtoB[je?{k3zQ vZ;%Ɠ,]E>KZ+T/ EJxOZ1i #T<@ I}q9/t'zi(EMqw`mYkU6;[t4DPeckeM;H}_g pMww}k6#H㶏+b8雡Sxp)&C $@'b,fPߑt$RbJ'vznuS ~8='72_`{q纶|Q)Xk}cPz9p7O:'|G~8wx(a 0QCko|0ASD>Ip=4Q, d|F8RcU"/KM opKle M3#i0c%<7׿p&pZq[TR"BpqauIp$ 8~Ĩ!8Սx\ւdT>>Z40ks7 z2IQ}ItԀ<-%S⍤};zIb$I 5K}Q͙D8UguWE$Jh )cu4N tZl+[]M4k8֦Zeq֮M7uIqG 1==tLtR,ƜSrHYt&QP윯Lg' I,3@P'}'R˪e/%-Auv·ñ\> vDJzlӾNv5:|K/Jb6KI9)Zh*ZAi`?S {aiVDԲuy5W7pWeQJk֤#5&V<̺@/GH?^τZL|IJNvI:'P=Ϛt"¨=cud S Q.Ki0 !cJy;LJR;G{BJy޺[^8fK6)=yʊ+(k|&xQ2`L?Ȓ2@Mf 0C`6-%pKpm')c$׻K5[J*U[/#hH!6acB JA _|uMvDyk y)6OPYjœ50VT K}cǻP[ $:]4MEA.y)|B)cf-A?(e|lɉ#P9V)[9t.EiQPDѠ3ϴ;E:+Օ t ȥ~|_N2,ZJLt4! %ա]u {+=p.GhNcŞQI?Nd'yeh n7zi1DB)1S | S#ًZs2|Ɛy$F SxeX{7Vl.Src3E℃Q>b6G ўYCmtկ~=K0f(=LrAS GN'ɹ9<\!a`)֕y[uՍ[09` 9 +57ts6}b4{oqd+J5fa/,97J#6yν99mRWxJyѡyu_TJc`~W>l^q#Ts#2"nD1%fS)FU w{ܯ R{ ˎ󅃏џDsZSQS;LV;7 Od1&1n$ N /.q3~eNɪ]E#oM~}v֯FڦwyZ=<<>Xo稯lfMFV6p02|*=tV!c~]fa5Y^Q_WN|Vs 0ҘދU97OI'N2'8N֭fgg-}V%y]U4 峧p*91#9U kCac_AFңĪy뚇Y_AiuYyTTYЗ-(!JFLt›17uTozc. S;7A&&<ԋ5y;Ro+:' *eYJkWR[@F %SHWP 72k4 qLd'J "zB6{AC0ƁA6U.'F3:Ȅ(9ΜL;D]m8ڥ9}dU "v!;*13Rg^fJyShyy5auA?ɩGHRjo^]׽S)Fm\toy 4WQS@mE#%5ʈfFYDX ~D5Ϡ9tE9So_aU4?Ѽm%&c{n>.KW1Tlb}:j uGi(JgcYj0qn+>) %\!4{LaJso d||u//P_y7iRJ߬nHOy) l+@$($VFIQ9%EeKʈU. ia&FY̒mZ=)+qqoQn >L!qCiDB;Y<%} OgBxB!ØuG)WG9y(Ą{_yesuZmZZey'Wg#C~1Cev@0D $a@˲(.._GimA:uyw֬%;@!JkQVM_Ow:P.s\)ot- ˹"`B,e CRtaEUP<0'}r3[>?G8xU~Nqu;Wm8\RIkբ^5@k+5(By'L&'gBJ3ݶ!/㮻w҅ yqPWUg<e"Qy*167΃sJ\oz]T*UQ<\FԎ`HaNmڜ6DysCask8wP8y9``GJ9lF\G g's Nn͵MLN֪u$| /|7=]O)6s !ĴAKh]q_ap $HH'\1jB^s\|- W1:=6lJBqjY^LsPk""`]w)󭃈,(HC ?䔨Y$Sʣ{4Z+0NvQkhol6C.婧/u]FwiVjZka&%6\F*Ny#8O,22+|Db~d ~Çwc N:FuuCe&oZ(l;@ee-+Wn`44AMK➝2BRՈt7g*1gph9N) *"TF*R(#'88pm=}X]u[i7bEc|\~EMn}P瘊J)K.0i1M6=7'_\kaZ(Th{K*GJyytw"IO-PWJk)..axӝ47"89Cc7ĐBiZx 7m!fy|ϿF9CbȩV 9V-՛^pV̌ɄS#Bv4-@]Vxt-Z, &ֺ*diؠ2^VXbs֔Ìl.jQ]Y[47gj=幽ex)A0ip׳ W2[ᎇhuE^~q흙L} #-b۸oFJ_QP3r6jr+"nfzRJTUqoaۍ /$d8Mx'ݓ= OՃ| )$2mcM*cЙj}f };n YG w0Ia!1Q.oYfr]DyISaP}"dIӗթO67jqR ҊƐƈaɤGG|h;t]䗖oSv|iZqX)oalv;۩meEJ\!8=$4QU4Xo&VEĊ YS^E#d,yX_> ۘ-e\ "Wa6uLĜZi`aD9.% w~mB(02G[6y.773a7 /=o7D)$Z 66 $bY^\CuP. (x'"J60׿Y:Oi;F{w佩b+\Yi`TDWa~|VH)8q/=9!g߆2Y)?ND)%?Ǐ`k/sn:;O299yB=a[Ng 3˲N}vLNy;*?x?~L&=xyӴ~}q{qE*IQ^^ͧvü{Huu=R|>JyUlZV, B~/YF!Y\u_ݼF{_C)LD]m {H 0ihhadd nUkf3oٺCvE\)QJi+֥@tDJkB$1!Đr0XQ|q?d2) Ӣ_}qv-< FŊ߫%roppVBwü~JidY4:}L6M7f٬F "?71<2#?Jyy4뷢<_a7_=Q E=S1И/9{+93֮E{ǂw{))?maÆm(uLE#lïZ  ~d];+]h j?!|$F}*"4(v'8s<ŏUkm7^7no1w2ؗ}TrͿEk>p'8OB7d7R(A 9.*Mi^ͳ; eeUwS+C)uO@ =Sy]` }l8^ZzRXj[^iUɺ$tj))<sbDJfg=Pk_{xaKo1:-uyG0M ԃ\0Lvuy'ȱc2Ji AdyVgVh!{]/&}}ċJ#%d !+87<;qN޼Nفl|1N:8ya  8}k¾+-$4FiZYÔXk*I&'@iI99)HSh4+2G:tGhS^繿 Kتm0 вDk}֚+QT4;sC}rՅE,8CX-e~>G&'9xpW,%Fh,Ry56Y–hW-(v_,? ; qrBk4-V7HQ;ˇ^Gv1JVV%,ik;D_W!))+BoS4QsTM;gt+ndS-~:11Sgv!0qRVh!"Ȋ(̦Yl.]PQWgٳE'`%W1{ndΗBk|Ž7ʒR~,lnoa&:ü$ 3<a[CBݮwt"o\ePJ=Hz"_c^Z.#ˆ*x z̝grY]tdkP*:97YľXyBkD4N.C_[;F9`8& !AMO c `@BA& Ost\-\NX+Xp < !bj3C&QL+*&kAQ=04}cC!9~820G'PC9xa!w&bo_1 Sw"ܱ V )Yl3+ס2KoXOx]"`^WOy :3GO0g;%Yv㐫(R/r (s } u B &FeYZh0y> =2<Ϟc/ -u= c&׭,.0"g"7 6T!vl#sc>{u/Oh Bᾈ)۴74]x7 gMӒ"d]U)}" v4co[ ɡs 5Gg=XR14?5A}D "b{0$L .\4y{_fe:kVS\\O]c^W52LSBDM! C3Dhr̦RtArx4&agaN3Cf<Ԉp4~ B'"1@.b_/xQ} _߃҉/gٓ2Qkqp0շpZ2fԫYz< 4L.Cyυι1t@鎫Fe sYfsF}^ V}N<_`p)alٶ "(XEAVZ<)2},:Ir*#m_YӼ R%a||EƼIJ,,+f"96r/}0jE/)s)cjW#w'Sʯ5<66lj$a~3Kʛy 2:cZ:Yh))+a߭K::N,Q F'qB]={.]h85C9cr=}*rk?vwV렵ٸW Rs%}rNAkDv|uFLBkWY YkX מ|)1!$#3%y?pF<@<Rr0}: }\J [5FRxY<9"SQdE(Q*Qʻ)q1E0B_O24[U'],lOb ]~WjHޏTQ5Syu wq)xnw8~)c 쫬gٲߠ H% k5dƝk> kEj,0% b"vi2Wس_CuK)K{n|>t{P1򨾜j>'kEkƗBg*H%'_aY6Bn!TL&ɌOb{c`'d^{t\i^[uɐ[}q0lM˕G:‚4kb祔c^:?bpg… +37stH:0}en6x˟%/<]BL&* 5&fK9Mq)/iyqtA%kUe[ڛKN]Ě^,"`/ s[EQQm?|XJ߅92m]G.E΃ח U*Cn.j_)Tѧj̿30ڇ!A0=͜ar I3$C^-9#|pk!)?7.x9 @OO;WƝZBFU keZ75F6Tc6"ZȚs2y/1 ʵ:u4xa`C>6Rb/Yм)^=+~uRd`/|_8xbB0?Ft||Z\##|K 0>>zxv8۴吅q 8ĥ)"6>~\8:qM}#͚'ĉ#p\׶ l#bA?)|g g9|8jP(cr,BwV (WliVxxᡁ@0Okn;ɥh$_ckCgriv}>=wGzβ KkBɛ[˪ !J)h&k2%07δt}!d<9;I&0wV/ v 0<H}L&8ob%Hi|޶o&h1L|u֦y~󛱢8fٲUsւ)0oiFx2}X[zVYr_;N(w]_4B@OanC?gĦx>мgx>ΛToZoOMp>40>V Oy V9iq!4 LN,ˢu{jsz]|"R޻&'ƚ{53ўFu(<٪9:΋]B;)B>1::8;~)Yt|0(pw2N%&X,URBK)3\zz&}ax4;ǟ(tLNg{N|Ǽ\G#C9g$^\}p?556]/RP.90 k,U8/u776s ʪ_01چ|\N 0VV*3H鴃J7iI!wG_^ypl}r*jɤSR 5QN@ iZ#1ٰy;_\3\BQQ x:WJv츟ٯ$"@6 S#qe딇(/P( Dy~TOϻ<4:-+F`0||;Xl-"uw$Цi󼕝mKʩorz"mϺ$F:~E'ҐvD\y?Rr8_He@ e~O,T.(ފR*cY^m|cVR[8 JҡSm!ΆԨb)RHG{?MpqrmN>߶Y)\p,d#xۆWY*,l6]v0h15M˙MS8+EdI='LBJIH7_9{Caз*Lq,dt >+~ّeʏ?xԕ4bBAŚjﵫ!'\Ը$WNvKO}ӽmSşذqsOy?\[,d@'73'j%kOe`1.g2"e =YIzS2|zŐƄa\U,dP;jhhhaxǶ?КZ՚.q SE+XrbOu%\GتX(H,N^~]JyEZQKceTQ]VGYqnah;y$cQahT&QPZ*iZ8UQQM.qo/T\7X"u?Mttl2Xq(IoW{R^ ux*SYJ! 4S.Jy~ BROS[V|žKNɛP(L6V^|cR7i7nZW1Fd@ Ara{詑|(T*dN]Ko?s=@ |_EvF]׍kR)eBJc" MUUbY6`~V޴dJKß&~'d3i WWWWWW
Current Directory: /opt/imh-python/lib/python3.9/site-packages/libcloud/compute/drivers
Viewing File: /opt/imh-python/lib/python3.9/site-packages/libcloud/compute/drivers/abiquo.py
# Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ Abiquo Compute Driver The driver implements the compute Abiquo functionality for the Abiquo API. This version is compatible with the following versions of Abiquo: * Abiquo 3.4 (http://wiki.abiquo.com/display/ABI34/The+Abiquo+API) """ from libcloud.utils.py3 import ET from libcloud.compute.base import NodeDriver, NodeSize from libcloud.compute.types import Provider, LibcloudError from libcloud.common.abiquo import (AbiquoConnection, get_href, AbiquoResponse) from libcloud.compute.base import NodeLocation, NodeImage, Node from libcloud.utils.py3 import tostring class AbiquoNodeDriver(NodeDriver): """ Implements the :class:`NodeDriver`'s for the Abiquo Compute Provider """ type = Provider.ABIQUO name = 'Abiquo' website = 'http://www.abiquo.com/' connectionCls = AbiquoConnection timeout = 2000 # some images take a lot of time! # Media Types NODES_MIME_TYPE = 'application/vnd.abiquo.virtualmachines+xml' NODE_MIME_TYPE = 'application/vnd.abiquo.virtualmachine+xml' VAPPS_MIME_TYPE = 'application/vnd.abiquo.virtualappliances+xml' VAPP_MIME_TYPE = 'application/vnd.abiquo.virtualappliance+xml' VM_TASK_MIME_TYPE = 'application/vnd.abiquo.virtualmachinetask+xml' USER_MIME_TYPE = 'application/vnd.abiquo.user+xml' ENT_MIME_TYPE = 'application/vnd.abiquo.enterprise+xml' VDCS_MIME_TYPE = 'application/vnd.abiquo.virtualdatacenters+xml' VDC_MIME_TYPE = 'application/vnd.abiquo.virtualdatacenter+xml' DCS_MIME_TYPE = 'application/vnd.abiquo.datacenters+xml' VMTPLS_MIME_TYPE = 'application/vnd.abiquo.virtualmachinetemplates+xml' VMTPL_MIME_TYPE = 'application/vnd.abiquo.virtualmachinetemplate+xml' NICS_MIME_TYPE = 'application/vnd.abiquo.nics+xml' DCRS_MIME_TYPE = 'application/vnd.abiquo.datacenterrepositories+xml' DCR_MIME_TYPE = 'application/vnd.abiquo.datacenterrepository+xml' AR_MIME_TYPE = 'application/vnd.abiquo.acceptedrequest+xml' # Others constants GIGABYTE = 1073741824 def __init__(self, user_id, secret, endpoint, **kwargs): """ Initializes Abiquo Driver Initializes the :class:`NodeDriver` object and populate the cache. :param user_id: identifier of Abiquo user (required) :type user_id: ``str`` :param secret: password of the Abiquo user (required) :type secret: ``str`` :param endpoint: Abiquo API endpoint (required) :type endpoint: ``str`` that can be parsed as URL """ self.endpoint = endpoint super(AbiquoNodeDriver, self).__init__(key=user_id, secret=secret, secure=False, host=None, port=None, **kwargs) self.ex_populate_cache() def create_node(self, image, name=None, size=None, location=None, ex_group_name=None): """ Create a new node instance in Abiquo All the :class:`Node`s need to be defined inside a VirtualAppliance (called :class:`NodeGroup` here). If there is no group name defined, 'libcloud' name will be used instead. This method wraps these Abiquo actions: 1. Create a group if it does not exist. 2. Register a new node in the group. 3. Deploy the node and boot it. 4. Retrieves it again to get schedule-time attributes (such as ips and vnc ports). The rest of the driver methods has been created in a way that, if any of these actions fail, the user can not reach an inconsistent state :keyword name: The name for this new node (required) :type name: ``str`` :keyword size: The size of resources allocated to this node. :type size: :class:`NodeSize` :keyword image: OS Image to boot on node. (required) :type image: :class:`NodeImage` :keyword location: Which data center to create a node in. If empty, undefined behavior will be selected. (optional) :type location: :class:`NodeLocation` :keyword ex_group_name: Which group this node belongs to. If empty, it will be created into 'libcloud' group. If it does not found any group in the target location (random location if you have not set the parameter), then it will create a new group with this name. :type ex_group_name: c{str} :return: The newly created node. :rtype: :class:`Node` """ # Define the location # To be clear: # 'xml_loc' is the xml element we navigate into (we need links) # 'loc' is the :class:`NodeLocation` entity xml_loc, loc = self._define_create_node_location(image=image, location=location) # Define the Group group = self._define_create_node_group(xml_loc, loc, ex_group_name) # Register the Node vm = self._define_create_node_node(group, name=name, size=size, image=image) # Execute the 'create' in hypervisor action self._deploy_remote(vm) # Retrieve it again, to get some schedule-time defined values edit_vm = get_href(vm, 'edit') headers = {'Accept': self.NODE_MIME_TYPE} vm = self.connection.request(edit_vm, headers=headers).object return self._to_node(vm, self) def destroy_node(self, node): """ Destroy a node Depending on the provider, this may destroy all data associated with the node, including backups. :param node: The node to be destroyed :type node: :class:`Node` :return: True if the destroy was successful, otherwise False :rtype: ``bool`` """ # Refresh node state headers = {'Accept': self.NODE_MIME_TYPE} e_vm = self.connection.request(node.extra['uri_id'], headers=headers).object state = e_vm.findtext('state') if state in ['ALLOCATED', 'CONFIGURED', 'LOCKED', 'UNKNOWN']: raise LibcloudError('Invalid Node state', self) if state != 'NOT_ALLOCATED': # prepare the element that forces the undeploy vm_task = ET.Element('virtualmachinetask') force_undeploy = ET.SubElement(vm_task, 'forceUndeploy') force_undeploy.text = 'True' # Set the URI destroy_uri = node.extra['uri_id'] + '/action/undeploy' # Prepare the headers headers = {'Accept': self.AR_MIME_TYPE, 'Content-type': self.VM_TASK_MIME_TYPE} res = self.connection.async_request(action=destroy_uri, method='POST', data=tostring(vm_task), headers=headers) if state == 'NOT_ALLOCATED' or res.async_success(): self.connection.request(action=node.extra['uri_id'], method='DELETE') return True else: return False def ex_run_node(self, node): """ Runs a node Here there is a bit difference between Abiquo states and libcloud states, so this method is created to have better compatibility. In libcloud, if the node is not running, then it does not exist (avoiding UNKNOWN and temporal states). In Abiquo, you can define a node, and then deploy it. If the node is in :class:`NodeState.TERMINATED` libcloud's state and in 'NOT_DEPLOYED' Abiquo state, there is a way to run and recover it for libcloud using this method. There is no way to reach this state if you are using only libcloud, but you may have used another Abiquo client and now you want to recover your node to be used by libcloud. :param node: The node to run :type node: :class:`Node` :return: The node itself, but with the new state :rtype: :class:`Node` """ # Refresh node state e_vm = self.connection.request(node.extra['uri_id']).object state = e_vm.findtext('state') if state != 'NOT_ALLOCATED': raise LibcloudError('Invalid Node state', self) # -------------------------------------------------------- # Deploy the Node # -------------------------------------------------------- self._deploy_remote(e_vm) # -------------------------------------------------------- # Retrieve it again, to get some schedule-defined # values. # -------------------------------------------------------- edit_vm = get_href(e_vm, 'edit') headers = {'Accept': self.NODE_MIME_TYPE} e_vm = self.connection.request(edit_vm, headers=headers).object return self._to_node(e_vm, self) def ex_populate_cache(self): """ Populate the cache. For each connection, it is good to store some objects that will be useful for further requests, such as the 'user' and the 'enterprise' objects. Executes the 'login' resource after setting the connection parameters and, if the execution is successful, it sets the 'user' object into cache. After that, it also requests for the 'enterprise' and 'locations' data. List of locations should remain the same for a single libcloud connection. However, this method is public and you are able to refresh the list of locations any time. """ user_headers = {'Accept': self.USER_MIME_TYPE} user = self.connection.request('/login', headers=user_headers).object self.connection.cache['user'] = user e_ent = get_href(self.connection.cache['user'], 'enterprise') ent_headers = {'Accept': self.ENT_MIME_TYPE} ent = self.connection.request(e_ent, headers=ent_headers).object self.connection.cache['enterprise'] = ent vdcs_headers = {'Accept': self.VDCS_MIME_TYPE} uri_vdcs = '/cloud/virtualdatacenters' e_vdcs = self.connection.request(uri_vdcs, headers=vdcs_headers).object params = {"idEnterprise": self._get_enterprise_id()} dcs_headers = {'Accept': self.DCS_MIME_TYPE} e_dcs = self.connection.request('/admin/datacenters', headers=dcs_headers, params=params).object dc_dict = {} for dc in e_dcs.findall('datacenter'): key = get_href(dc, 'self') dc_dict[key] = dc # Populate locations name cache self.connection.cache['locations'] = {} for e_vdc in e_vdcs.findall('virtualDatacenter'): loc = get_href(e_vdc, 'location') if loc is not None: self.connection.cache['locations'][loc] = get_href(e_vdc, 'edit') def ex_create_group(self, name, location=None): """ Create an empty group. You can specify the location as well. :param group: name of the group (required) :type group: ``str`` :param location: location were to create the group :type location: :class:`NodeLocation` :returns: the created group :rtype: :class:`NodeGroup` """ # prepare the element vapp = ET.Element('virtualAppliance') vapp_name = ET.SubElement(vapp, 'name') vapp_name.text = name if location is None: location = self.list_locations()[0] elif location not in self.list_locations(): raise LibcloudError('Location does not exist') link_vdc = self.connection.cache['locations'][location] hdr_vdc = {'Accept': self.VDC_MIME_TYPE} e_vdc = self.connection.request(link_vdc, headers=hdr_vdc).object creation_link = get_href(e_vdc, 'virtualappliances') headers = {'Accept': self.VAPP_MIME_TYPE, 'Content-type': self.VAPP_MIME_TYPE} vapp = self.connection.request(creation_link, data=tostring(vapp), headers=headers, method='POST').object uri_vapp = get_href(vapp, 'edit') return NodeGroup(self, vapp.findtext('name'), uri=uri_vapp) def ex_destroy_group(self, group): """ Destroy a group. Be careful! Destroying a group means destroying all the :class:`Node` instances there and the group itself! If there is currently any action over any :class:`Node` of the :class:`NodeGroup`, then the method will raise an exception. :param name: The group (required) :type name: :class:`NodeGroup` :return: If the group was destroyed successfully :rtype: ``bool`` """ # Refresh group state e_group = self.connection.request(group.uri).object state = e_group.findtext('state') if state not in ['NOT_DEPLOYED', 'DEPLOYED']: error = 'Can not destroy group because of current state' raise LibcloudError(error, self) if state == 'DEPLOYED': # prepare the element that forces the undeploy vm_task = ET.Element('virtualmachinetask') force_undeploy = ET.SubElement(vm_task, 'forceUndeploy') force_undeploy.text = 'True' # Set the URI undeploy_uri = group.uri + '/action/undeploy' # Prepare the headers headers = {'Accept': self.AR_MIME_TYPE, 'Content-type': self.VM_TASK_MIME_TYPE} res = self.connection.async_request(action=undeploy_uri, method='POST', data=tostring(vm_task), headers=headers) if state == 'NOT_DEPLOYED' or res.async_success(): # The node is no longer deployed. Unregister it. self.connection.request(action=group.uri, method='DELETE') return True else: return False def ex_list_groups(self, location=None): """ List all groups. :param location: filter the groups by location (optional) :type location: a :class:`NodeLocation` instance. :return: the list of :class:`NodeGroup` """ groups = [] for vdc in self._get_locations(location): link_vdc = self.connection.cache['locations'][vdc] hdr_vdc = {'Accept': self.VDC_MIME_TYPE} e_vdc = self.connection.request(link_vdc, headers=hdr_vdc).object apps_link = get_href(e_vdc, 'virtualappliances') hdr_vapps = {'Accept': self.VAPPS_MIME_TYPE} vapps = self.connection.request(apps_link, headers=hdr_vapps).object for vapp in vapps.findall('virtualAppliance'): nodes = [] vms_link = get_href(vapp, 'virtualmachines') headers = {'Accept': self.NODES_MIME_TYPE} vms = self.connection.request(vms_link, headers=headers).object for vm in vms.findall('virtualMachine'): nodes.append(self._to_node(vm, self)) group = NodeGroup(self, vapp.findtext('name'), nodes, get_href(vapp, 'edit')) groups.append(group) return groups def list_images(self, location=None): """ List images on Abiquo Repositories :keyword location: The location to list images for. :type location: :class:`NodeLocation` :return: list of node image objects :rtype: ``list`` of :class:`NodeImage` """ enterprise_id = self._get_enterprise_id() uri = '/admin/enterprises/%s/datacenterrepositories/' % (enterprise_id) repos_hdr = {'Accept': self.DCRS_MIME_TYPE} repos = self.connection.request(uri, headers=repos_hdr).object images = [] for repo in repos.findall('datacenterRepository'): # filter by location. Skips when the name of the location # is different from the 'datacenterRepository' element for vdc in self._get_locations(location): # Check if the virtual datacenter belongs to this repo link_vdc = self.connection.cache['locations'][vdc] hdr_vdc = {'Accept': self.VDC_MIME_TYPE} e_vdc = self.connection.request(link_vdc, headers=hdr_vdc).object dc_link_vdc = get_href(e_vdc, 'location') dc_link_repo = get_href(repo, 'datacenter') if dc_link_vdc.split("/")[-1] == dc_link_repo.split("/")[-1]: # Filter the template in case we don't have it yet url_templates = get_href(repo, 'virtualmachinetemplates') hypervisor_type = e_vdc.findtext('hypervisorType') params = {'hypervisorTypeName': hypervisor_type} headers = {'Accept': self.VMTPLS_MIME_TYPE} templates = self.connection.request(url_templates, params, headers=headers).object for templ in templates.findall('virtualMachineTemplate'): # Avoid duplicated templates id_template = templ.findtext('id') ids = [image.id for image in images] if id_template not in ids: images.append(self._to_nodeimage(templ, self, get_href(repo, 'edit'))) return images def list_locations(self): """ Return list of locations where the user has access to. :return: the list of :class:`NodeLocation` available for the current user :rtype: ``list`` of :class:`NodeLocation` """ return list(self.connection.cache['locations'].keys()) def list_nodes(self, location=None): """ List all nodes. :param location: Filter the groups by location (optional) :type location: a :class:`NodeLocation` instance. :return: List of node objects :rtype: ``list`` of :class:`Node` """ nodes = [] for group in self.ex_list_groups(location): nodes.extend(group.nodes) return nodes def list_sizes(self, location=None): """ List sizes on a provider. Abiquo does not work with sizes. However, this method returns a list of predefined ones (copied from :class:`DummyNodeDriver` but without price neither bandwidth) to help the users to create their own. If you call the method :class:`AbiquoNodeDriver.create_node` with the size informed, it will just override the 'ram' value of the 'image' template. So it is no too much usefull work with sizes... :return: The list of sizes :rtype: ``list`` of :class:`NodeSizes` """ return [ NodeSize(id=1, name='Small', ram=128, disk=4, bandwidth=None, price=None, driver=self), NodeSize(id=2, name='Medium', ram=512, disk=16, bandwidth=None, price=None, driver=self), NodeSize(id=3, name='Big', ram=4096, disk=32, bandwidth=None, price=None, driver=self), NodeSize(id=4, name="XXL Big", ram=4096 * 2, disk=32 * 4, bandwidth=None, price=None, driver=self) ] def reboot_node(self, node): """ Reboot a node. :param node: The node to be rebooted :type node: :class:`Node` :return: True if the reboot was successful, otherwise False :rtype: ``bool`` """ reboot_uri = node.extra['uri_id'] + '/action/reset' reboot_hdr = {'Accept': self.AR_MIME_TYPE} res = self.connection.async_request(action=reboot_uri, method='POST', headers=reboot_hdr) return res.async_success() # ------------------------- # Extenstion methods # ------------------------- def _ex_connection_class_kwargs(self): """ Set the endpoint as an extra :class:`AbiquoConnection` argument. According to Connection code, the "url" argument should be parsed properly to connection. :return: ``dict`` of :class:`AbiquoConnection` input arguments """ return {'url': self.endpoint} def _deploy_remote(self, e_vm): """ Asynchronous call to create the node. """ # -------------------------------------------------------- # Deploy the Node # -------------------------------------------------------- # prepare the element that forces the deploy vm_task = ET.Element('virtualmachinetask') force_deploy = ET.SubElement(vm_task, 'forceEnterpriseSoftLimits') force_deploy.text = 'True' # Prepare the headers headers = {'Accept': self.AR_MIME_TYPE, 'Content-type': self.VM_TASK_MIME_TYPE} link_deploy = get_href(e_vm, 'deploy') res = self.connection.async_request(action=link_deploy, method='POST', data=tostring(vm_task), headers=headers) if not res.async_success(): raise LibcloudError('Could not run the node', self) def _to_location(self, vdc, dc, driver): """ Generates the :class:`NodeLocation` class. """ identifier = vdc.findtext('id') name = vdc.findtext('name') country = dc.findtext('name') return NodeLocation(identifier, name, country, driver) def _to_node(self, vm, driver): """ Generates the :class:`Node` class. """ identifier = vm.findtext('id') name = vm.findtext('label') state = AbiquoResponse.NODE_STATE_MAP[vm.findtext('state')] link_image = get_href(vm, 'virtualmachinetemplate') link_hdr = {'Accept': self.VMTPL_MIME_TYPE} image_element = self.connection.request(link_image, headers=link_hdr).object repo_link = get_href(image_element, 'datacenterrepository') image = self._to_nodeimage(image_element, self, repo_link) # Fill the 'ips' data private_ips = [] public_ips = [] nics_hdr = {'Accept': self.NICS_MIME_TYPE} nics_element = self.connection.request(get_href(vm, 'nics'), headers=nics_hdr).object for nic in nics_element.findall('nic'): ip = nic.findtext('ip') for link in nic.findall('link'): rel = link.attrib['rel'] if rel == 'privatenetwork': private_ips.append(ip) elif rel in ['publicnetwork', 'externalnetwork', 'unmanagednetwork']: public_ips.append(ip) extra = {'uri_id': get_href(vm, 'edit')} if vm.find('vdrpIp') is not None: extra['vdrp_ip'] = vm.findtext('vdrpIP') extra['vdrp_port'] = vm.findtext('vdrpPort') return Node(identifier, name, state, public_ips, private_ips, driver, image=image, extra=extra) def _to_nodeimage(self, template, driver, repo): """ Generates the :class:`NodeImage` class. """ identifier = template.findtext('id') name = template.findtext('name') url = get_href(template, 'edit') hdreqd = template.findtext('hdRequired') extra = {'repo': repo, 'url': url, 'hdrequired': hdreqd} return NodeImage(identifier, name, driver, extra) def _get_locations(self, location=None): """ Returns the locations as a generator. """ if location is not None: yield location else: for loc in self.list_locations(): yield loc def _get_enterprise_id(self): """ Returns the identifier of the logged user's enterprise. """ return self.connection.cache['enterprise'].findtext('id') def _define_create_node_location(self, image, location): """ Search for a location where to create the node. Based on 'create_node' **kwargs argument, decide in which location will be created. """ # First, get image location if not image: error = "'image' parameter is mandatory" raise LibcloudError(error, self) # Get the location argument if location: if location not in self.list_locations(): raise LibcloudError('Location does not exist') # Check if the image is compatible with any of the locations or # the input location loc = None target_loc = None for candidate_loc in self._get_locations(location): link_vdc = self.connection.cache['locations'][candidate_loc] hdr_vdc = {'Accept': self.VDC_MIME_TYPE} e_vdc = self.connection.request(link_vdc, headers=hdr_vdc).object for img in self.list_images(candidate_loc): if img.id == image.id: loc = e_vdc target_loc = candidate_loc break if loc is None: error = 'The image can not be used in any location' raise LibcloudError(error, self) return loc, target_loc def _define_create_node_group(self, xml_loc, loc, group_name=None): """ Search for a group where to create the node. If we can not find any group, create it into argument 'location' """ if not group_name: group_name = NodeGroup.DEFAULT_GROUP_NAME # We search if the group is already defined into the location groups_link = get_href(xml_loc, 'virtualappliances') groups_hdr = {'Accept': self.VAPPS_MIME_TYPE} vapps_element = self.connection.request(groups_link, headers=groups_hdr).object target_group = None for vapp in vapps_element.findall('virtualAppliance'): if vapp.findtext('name') == group_name: uri_vapp = get_href(vapp, 'edit') return NodeGroup(self, vapp.findtext('name'), uri=uri_vapp) # target group not found: create it. Since it is an extension of # the basic 'libcloud' functionality, we try to be as flexible as # possible. if target_group is None: return self.ex_create_group(group_name, loc) def _define_create_node_node(self, group, name=None, size=None, image=None): """ Defines the node before to create. In Abiquo, you first need to 'register' or 'define' the node in the API before to create it into the target hypervisor. """ vm = ET.Element('virtualMachine') if name: vmname = ET.SubElement(vm, 'label') vmname.text = name attrib = {'type': self.VMTPL_MIME_TYPE, 'rel': 'virtualmachinetemplate', 'href': image.extra['url']} ET.SubElement(vm, 'link', attrib=attrib) headers = {'Accept': self.NODE_MIME_TYPE, 'Content-type': self.NODE_MIME_TYPE} if size: # Override the 'NodeSize' data ram = ET.SubElement(vm, 'ram') ram.text = str(size.ram) # Create the virtual machine nodes_link = group.uri + '/virtualmachines' vm = self.connection.request(nodes_link, data=tostring(vm), headers=headers, method='POST').object edit_vm = get_href(vm, 'edit') headers = {'Accept': self.NODE_MIME_TYPE} return self.connection.request(edit_vm, headers=headers).object class NodeGroup(object): """ Group of virtual machines that can be managed together All :class:`Node`s in Abiquo must be defined inside a Virtual Appliance. We offer a way to handle virtual appliances (called NodeGroup to maintain some kind of name conventions here) inside the :class:`AbiquoNodeDriver` without breaking compatibility of the rest of libcloud API. If the user does not want to handle groups, all the virtual machines will be created inside a group named 'libcloud' """ DEFAULT_GROUP_NAME = 'libcloud' def __init__(self, driver, name=DEFAULT_GROUP_NAME, nodes=[], uri=''): """ Initialize a new group object. """ self.driver = driver self.name = name self.nodes = nodes self.uri = uri def __repr__(self): return (('<NodeGroup: name=%s, nodes=[%s] >') % (self.name, ",".join(map(str, self.nodes)))) def destroy(self): """ Destroys the group delegating the execution to :class:`AbiquoNodeDriver`. """ return self.driver.ex_destroy_group(self)