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/loadbalancer/drivers
Viewing File: /opt/imh-python/lib/python3.9/site-packages/libcloud/loadbalancer/drivers/dimensiondata.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 withv # 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. from libcloud.utils.py3 import ET from libcloud.common.dimensiondata import DimensionDataConnection from libcloud.common.dimensiondata import DimensionDataPool from libcloud.common.dimensiondata import DimensionDataPoolMember from libcloud.common.dimensiondata import DimensionDataVirtualListener from libcloud.common.dimensiondata import DimensionDataVIPNode from libcloud.common.dimensiondata import DimensionDataDefaultHealthMonitor from libcloud.common.dimensiondata import DimensionDataPersistenceProfile from libcloud.common.dimensiondata import \ DimensionDataVirtualListenerCompatibility from libcloud.common.dimensiondata import DimensionDataDefaultiRule from libcloud.common.dimensiondata import API_ENDPOINTS from libcloud.common.dimensiondata import DEFAULT_REGION from libcloud.common.dimensiondata import TYPES_URN from libcloud.utils.misc import reverse_dict from libcloud.utils.xml import fixxpath, findtext, findall from libcloud.loadbalancer.types import State from libcloud.loadbalancer.base import Algorithm, Driver, LoadBalancer from libcloud.loadbalancer.base import Member from libcloud.loadbalancer.types import Provider class DimensionDataLBDriver(Driver): """ DimensionData node driver. """ selected_region = None connectionCls = DimensionDataConnection name = 'Dimension Data Load Balancer' website = 'https://cloud.dimensiondata.com/' type = Provider.DIMENSIONDATA api_version = 1.0 network_domain_id = None _VALUE_TO_ALGORITHM_MAP = { 'ROUND_ROBIN': Algorithm.ROUND_ROBIN, 'LEAST_CONNECTIONS': Algorithm.LEAST_CONNECTIONS, 'SHORTEST_RESPONSE': Algorithm.SHORTEST_RESPONSE, 'PERSISTENT_IP': Algorithm.PERSISTENT_IP } _ALGORITHM_TO_VALUE_MAP = reverse_dict(_VALUE_TO_ALGORITHM_MAP) _VALUE_TO_STATE_MAP = { 'NORMAL': State.RUNNING, 'PENDING_ADD': State.PENDING, 'PENDING_CHANGE': State.PENDING, 'PENDING_DELETE': State.PENDING, 'FAILED_ADD': State.ERROR, 'FAILED_CHANGE': State.ERROR, 'FAILED_DELETE': State.ERROR, 'REQUIRES_SUPPORT': State.ERROR } def __init__(self, key, secret=None, secure=True, host=None, port=None, api_version=None, region=DEFAULT_REGION, **kwargs): if region not in API_ENDPOINTS and host is None: raise ValueError( 'Invalid region: %s, no host specified' % (region)) if region is not None: self.selected_region = API_ENDPOINTS[region] super(DimensionDataLBDriver, self).__init__(key=key, secret=secret, secure=secure, host=host, port=port, api_version=api_version, region=region, **kwargs) def _ex_connection_class_kwargs(self): """ Add the region to the kwargs before the connection is instantiated """ kwargs = super(DimensionDataLBDriver, self)._ex_connection_class_kwargs() kwargs['region'] = self.selected_region return kwargs def create_balancer(self, name, port=None, protocol=None, algorithm=None, members=None, ex_listener_ip_address=None): """ Create a new load balancer instance :param name: Name of the new load balancer (required) :type name: ``str`` :param port: An integer in the range of 1-65535. If not supplied, it will be taken to mean 'Any Port' :type port: ``int`` :param protocol: Loadbalancer protocol, defaults to http. :type protocol: ``str`` :param members: list of Members to attach to balancer (optional) :type members: ``list`` of :class:`Member` :param algorithm: Load balancing algorithm, defaults to ROUND_ROBIN. :type algorithm: :class:`.Algorithm` :param ex_listener_ip_address: Must be a valid IPv4 in dot-decimal notation (x.x.x.x). :type ex_listener_ip_address: ``str`` :rtype: :class:`LoadBalancer` """ network_domain_id = self.network_domain_id if protocol is None: protocol = 'http' if algorithm is None: algorithm = Algorithm.ROUND_ROBIN # Create a pool first pool = self.ex_create_pool( network_domain_id=network_domain_id, name=name, ex_description=None, balancer_method=self._ALGORITHM_TO_VALUE_MAP[algorithm]) # Attach the members to the pool as nodes if members is not None: for member in members: node = self.ex_create_node( network_domain_id=network_domain_id, name=member.ip, ip=member.ip, ex_description=None) self.ex_create_pool_member( pool=pool, node=node, port=port) # Create the virtual listener (balancer) listener = self.ex_create_virtual_listener( network_domain_id=network_domain_id, name=name, ex_description=name, port=port, pool=pool, listener_ip_address=ex_listener_ip_address) return LoadBalancer( id=listener.id, name=listener.name, state=State.RUNNING, ip=listener.ip, port=port, driver=self, extra={'pool_id': pool.id, 'network_domain_id': network_domain_id, 'listener_ip_address': ex_listener_ip_address} ) def list_balancers(self, ex_network_domain_id=None): """ List all loadbalancers inside a geography or in given network. In Dimension Data terminology these are known as virtual listeners :param ex_network_domain_id: UUID of Network Domain if not None returns only balancers in the given network if None then returns all pools for the organization :type ex_network_domain_id: ``str`` :rtype: ``list`` of :class:`LoadBalancer` """ params = None if ex_network_domain_id is not None: params = {"networkDomainId": ex_network_domain_id} return self._to_balancers( self.connection .request_with_orgId_api_2('networkDomainVip/virtualListener', params=params).object) def get_balancer(self, balancer_id): """ Return a :class:`LoadBalancer` object. :param balancer_id: id of a load balancer you want to fetch :type balancer_id: ``str`` :rtype: :class:`LoadBalancer` """ bal = self.connection \ .request_with_orgId_api_2('networkDomainVip/virtualListener/%s' % balancer_id).object return self._to_balancer(bal) def list_protocols(self): """ Return a list of supported protocols. Since all protocols are support by Dimension Data, this is a list of common protocols. :rtype: ``list`` of ``str`` """ return ['http', 'https', 'tcp', 'udp', 'ftp', 'smtp'] def balancer_list_members(self, balancer): """ Return list of members attached to balancer. In Dimension Data terminology these are the members of the pools within a virtual listener. :param balancer: LoadBalancer which should be used :type balancer: :class:`LoadBalancer` :rtype: ``list`` of :class:`Member` """ pool_members = self.ex_get_pool_members(balancer.extra['pool_id']) members = [] for pool_member in pool_members: members.append(Member( id=pool_member.id, ip=pool_member.ip, port=pool_member.port, balancer=balancer, extra=None )) return members def balancer_attach_member(self, balancer, member): """ Attach a member to balancer :param balancer: LoadBalancer which should be used :type balancer: :class:`LoadBalancer` :param member: Member to join to the balancer :type member: :class:`Member` :return: Member after joining the balancer. :rtype: :class:`Member` """ node = self.ex_create_node( network_domain_id=balancer.extra['network_domain_id'], name='Member.' + member.ip, ip=member.ip, ex_description='' ) if node is False: return False pool = self.ex_get_pool(balancer.extra['pool_id']) pool_member = self.ex_create_pool_member( pool=pool, node=node, port=member.port) member.id = pool_member.id return member def balancer_detach_member(self, balancer, member): """ Detach member from balancer :param balancer: LoadBalancer which should be used :type balancer: :class:`LoadBalancer` :param member: Member which should be used :type member: :class:`Member` :return: ``True`` if member detach was successful, otherwise ``False``. :rtype: ``bool`` """ create_pool_m = ET.Element('removePoolMember', {'xmlns': TYPES_URN, 'id': member.id}) result = self.connection.request_with_orgId_api_2( 'networkDomainVip/removePoolMember', method='POST', data=ET.tostring(create_pool_m)).object response_code = findtext(result, 'responseCode', TYPES_URN) return response_code in ['IN_PROGRESS', 'OK'] def destroy_balancer(self, balancer): """ Destroy a load balancer (virtual listener) :param balancer: LoadBalancer which should be used :type balancer: :class:`LoadBalancer` :return: ``True`` if the destroy was successful, otherwise ``False``. :rtype: ``bool`` """ delete_listener = ET.Element('deleteVirtualListener', {'xmlns': TYPES_URN, 'id': balancer.id}) result = self.connection.request_with_orgId_api_2( 'networkDomainVip/deleteVirtualListener', method='POST', data=ET.tostring(delete_listener)).object response_code = findtext(result, 'responseCode', TYPES_URN) return response_code in ['IN_PROGRESS', 'OK'] def ex_set_current_network_domain(self, network_domain_id): """ Set the network domain (part of the network) of the driver :param network_domain_id: ID of the pool (required) :type network_domain_id: ``str`` """ self.network_domain_id = network_domain_id def ex_get_current_network_domain(self): """ Get the current network domain ID of the driver. :return: ID of the network domain :rtype: ``str`` """ return self.network_domain_id def ex_create_pool_member(self, pool, node, port=None): """ Create a new member in an existing pool from an existing node :param pool: Instance of ``DimensionDataPool`` (required) :type pool: ``DimensionDataPool`` :param node: Instance of ``DimensionDataVIPNode`` (required) :type node: ``DimensionDataVIPNode`` :param port: Port the the service will listen on :type port: ``str`` :return: The node member, instance of ``DimensionDataPoolMember`` :rtype: ``DimensionDataPoolMember`` """ create_pool_m = ET.Element('addPoolMember', {'xmlns': TYPES_URN}) ET.SubElement(create_pool_m, "poolId").text = pool.id ET.SubElement(create_pool_m, "nodeId").text = node.id if port is not None: ET.SubElement(create_pool_m, "port").text = str(port) ET.SubElement(create_pool_m, "status").text = 'ENABLED' response = self.connection.request_with_orgId_api_2( 'networkDomainVip/addPoolMember', method='POST', data=ET.tostring(create_pool_m)).object member_id = None node_name = None for info in findall(response, 'info', TYPES_URN): if info.get('name') == 'poolMemberId': member_id = info.get('value') if info.get('name') == 'nodeName': node_name = info.get('value') return DimensionDataPoolMember( id=member_id, name=node_name, status=State.RUNNING, ip=node.ip, port=port, node_id=node.id ) def ex_create_node(self, network_domain_id, name, ip, ex_description, connection_limit=25000, connection_rate_limit=2000): """ Create a new node :param network_domain_id: Network Domain ID (required) :type name: ``str`` :param name: name of the node (required) :type name: ``str`` :param ip: IPv4 address of the node (required) :type ip: ``str`` :param ex_description: Description of the node (required) :type ex_description: ``str`` :param connection_limit: Maximum number of concurrent connections per sec :type connection_limit: ``int`` :param connection_rate_limit: Maximum number of concurrent sessions :type connection_rate_limit: ``int`` :return: Instance of ``DimensionDataVIPNode`` :rtype: ``DimensionDataVIPNode`` """ create_node_elm = ET.Element('createNode', {'xmlns': TYPES_URN}) ET.SubElement(create_node_elm, "networkDomainId") \ .text = network_domain_id ET.SubElement(create_node_elm, "name").text = name ET.SubElement(create_node_elm, "description").text \ = str(ex_description) ET.SubElement(create_node_elm, "ipv4Address").text = ip ET.SubElement(create_node_elm, "status").text = 'ENABLED' ET.SubElement(create_node_elm, "connectionLimit") \ .text = str(connection_limit) ET.SubElement(create_node_elm, "connectionRateLimit") \ .text = str(connection_rate_limit) response = self.connection.request_with_orgId_api_2( action='networkDomainVip/createNode', method='POST', data=ET.tostring(create_node_elm)).object node_id = None node_name = None for info in findall(response, 'info', TYPES_URN): if info.get('name') == 'nodeId': node_id = info.get('value') if info.get('name') == 'name': node_name = info.get('value') return DimensionDataVIPNode( id=node_id, name=node_name, status=State.RUNNING, ip=ip ) def ex_update_node(self, node): """ Update the properties of a node :param pool: The instance of ``DimensionDataNode`` to update :type pool: ``DimensionDataNode`` :return: The instance of ``DimensionDataNode`` :rtype: ``DimensionDataNode`` """ create_node_elm = ET.Element('editNode', {'xmlns': TYPES_URN}) ET.SubElement(create_node_elm, "connectionLimit") \ .text = str(node.connection_limit) ET.SubElement(create_node_elm, "connectionRateLimit") \ .text = str(node.connection_rate_limit) self.connection.request_with_orgId_api_2( action='networkDomainVip/createNode', method='POST', data=ET.tostring(create_node_elm)).object return node def ex_set_node_state(self, node, enabled): """ Change the state of a node (enable/disable) :param pool: The instance of ``DimensionDataNode`` to update :type pool: ``DimensionDataNode`` :param enabled: The target state of the node :type enabled: ``bool`` :return: The instance of ``DimensionDataNode`` :rtype: ``DimensionDataNode`` """ create_node_elm = ET.Element('editNode', {'xmlns': TYPES_URN}) ET.SubElement(create_node_elm, "status") \ .text = "ENABLED" if enabled is True else "DISABLED" self.connection.request_with_orgId_api_2( action='networkDomainVip/editNode', method='POST', data=ET.tostring(create_node_elm)).object return node def ex_create_pool(self, network_domain_id, name, balancer_method, ex_description, health_monitors=None, service_down_action='NONE', slow_ramp_time=30): """ Create a new pool :param network_domain_id: Network Domain ID (required) :type name: ``str`` :param name: name of the node (required) :type name: ``str`` :param balancer_method: The load balancer algorithm (required) :type balancer_method: ``str`` :param ex_description: Description of the node (required) :type ex_description: ``str`` :param health_monitors: A list of health monitors to use for the pool. :type health_monitors: ``list`` of :class:`DimensionDataDefaultHealthMonitor` :param service_down_action: What to do when node is unavailable NONE, DROP or RESELECT :type service_down_action: ``str`` :param slow_ramp_time: Number of seconds to stagger ramp up of nodes :type slow_ramp_time: ``int`` :return: Instance of ``DimensionDataPool`` :rtype: ``DimensionDataPool`` """ # Names cannot contain spaces. name.replace(' ', '_') create_node_elm = ET.Element('createPool', {'xmlns': TYPES_URN}) ET.SubElement(create_node_elm, "networkDomainId") \ .text = network_domain_id ET.SubElement(create_node_elm, "name").text = name ET.SubElement(create_node_elm, "description").text \ = str(ex_description) ET.SubElement(create_node_elm, "loadBalanceMethod") \ .text = str(balancer_method) if health_monitors is not None: for monitor in health_monitors: ET.SubElement(create_node_elm, "healthMonitorId") \ .text = str(monitor.id) ET.SubElement(create_node_elm, "serviceDownAction") \ .text = service_down_action ET.SubElement(create_node_elm, "slowRampTime").text \ = str(slow_ramp_time) response = self.connection.request_with_orgId_api_2( action='networkDomainVip/createPool', method='POST', data=ET.tostring(create_node_elm)).object pool_id = None for info in findall(response, 'info', TYPES_URN): if info.get('name') == 'poolId': pool_id = info.get('value') return DimensionDataPool( id=pool_id, name=name, description=ex_description, status=State.RUNNING, load_balance_method=str(balancer_method), health_monitor_id=None, service_down_action=service_down_action, slow_ramp_time=str(slow_ramp_time) ) def ex_create_virtual_listener(self, network_domain_id, name, ex_description, port=None, pool=None, listener_ip_address=None, persistence_profile=None, fallback_persistence_profile=None, irule=None, protocol='TCP', connection_limit=25000, connection_rate_limit=2000, source_port_preservation='PRESERVE'): """ Create a new virtual listener (load balancer) :param network_domain_id: Network Domain ID (required) :type name: ``str`` :param name: name of the listener (required) :type name: ``str`` :param ex_description: Description of the node (required) :type ex_description: ``str`` :param port: An integer in the range of 1-65535. If not supplied, it will be taken to mean 'Any Port' :type port: ``int`` :param pool: The pool to use for the listener :type pool: :class:`DimensionDataPool` :param listener_ip_address: The IPv4 Address of the virtual listener :type listener_ip_address: ``str`` :param persistence_profile: Persistence profile :type persistence_profile: :class:`DimensionDataPersistenceProfile` :param fallback_persistence_profile: Fallback persistence profile :type fallback_persistence_profile: :class:`DimensionDataPersistenceProfile` :param irule: The iRule to apply :type irule: :class:`DimensionDataDefaultiRule` :param protocol: For STANDARD type, ANY, TCP or UDP for PERFORMANCE_LAYER_4 choice of ANY, TCP, UDP, HTTP :type protcol: ``str`` :param connection_limit: Maximum number of concurrent connections per sec :type connection_limit: ``int`` :param connection_rate_limit: Maximum number of concurrent sessions :type connection_rate_limit: ``int`` :param source_port_preservation: Choice of PRESERVE, PRESERVE_STRICT or CHANGE :type source_port_preservation: ``str`` :return: Instance of the listener :rtype: ``DimensionDataVirtualListener`` """ if (port == 80) or (port == 443): listener_type = 'PERFORMANCE_LAYER_4' else: listener_type = 'STANDARD' create_node_elm = ET.Element('createVirtualListener', {'xmlns': TYPES_URN}) ET.SubElement(create_node_elm, "networkDomainId") \ .text = network_domain_id ET.SubElement(create_node_elm, "name").text = name ET.SubElement(create_node_elm, "description").text = \ str(ex_description) ET.SubElement(create_node_elm, "type").text = listener_type ET.SubElement(create_node_elm, "protocol") \ .text = protocol if listener_ip_address is not None: ET.SubElement(create_node_elm, "listenerIpAddress").text = \ str(listener_ip_address) if port is not None: ET.SubElement(create_node_elm, "port").text = str(port) ET.SubElement(create_node_elm, "enabled").text = 'true' ET.SubElement(create_node_elm, "connectionLimit") \ .text = str(connection_limit) ET.SubElement(create_node_elm, "connectionRateLimit") \ .text = str(connection_rate_limit) ET.SubElement(create_node_elm, "sourcePortPreservation") \ .text = source_port_preservation if pool is not None: ET.SubElement(create_node_elm, "poolId") \ .text = pool.id if persistence_profile is not None: ET.SubElement(create_node_elm, "persistenceProfileId") \ .text = persistence_profile.id if fallback_persistence_profile is not None: ET.SubElement(create_node_elm, "fallbackPersistenceProfileId") \ .text = fallback_persistence_profile.id if irule is not None: ET.SubElement(create_node_elm, "iruleId") \ .text = irule.id response = self.connection.request_with_orgId_api_2( action='networkDomainVip/createVirtualListener', method='POST', data=ET.tostring(create_node_elm)).object virtual_listener_id = None virtual_listener_ip = None for info in findall(response, 'info', TYPES_URN): if info.get('name') == 'virtualListenerId': virtual_listener_id = info.get('value') if info.get('name') == 'listenerIpAddress': virtual_listener_ip = info.get('value') return DimensionDataVirtualListener( id=virtual_listener_id, name=name, ip=virtual_listener_ip, status=State.RUNNING ) def ex_get_pools(self, ex_network_domain_id=None): """ Get all of the pools inside the current geography or in given network. :param ex_network_domain_id: UUID of Network Domain if not None returns only balancers in the given network if None then returns all pools for the organization :type ex_network_domain_id: ``str`` :return: Returns a ``list`` of type ``DimensionDataPool`` :rtype: ``list`` of ``DimensionDataPool`` """ params = None if ex_network_domain_id is not None: params = {"networkDomainId": ex_network_domain_id} pools = self.connection \ .request_with_orgId_api_2('networkDomainVip/pool', params=params).object return self._to_pools(pools) def ex_get_pool(self, pool_id): """ Get a specific pool inside the current geography :param pool_id: The identifier of the pool :type pool_id: ``str`` :return: Returns an instance of ``DimensionDataPool`` :rtype: ``DimensionDataPool`` """ pool = self.connection \ .request_with_orgId_api_2('networkDomainVip/pool/%s' % pool_id).object return self._to_pool(pool) def ex_update_pool(self, pool): """ Update the properties of an existing pool only method, serviceDownAction and slowRampTime are updated :param pool: The instance of ``DimensionDataPool`` to update :type pool: ``DimensionDataPool`` :return: ``True`` for success, ``False`` for failure :rtype: ``bool`` """ create_node_elm = ET.Element('editPool', {'xmlns': TYPES_URN}) ET.SubElement(create_node_elm, "loadBalanceMethod") \ .text = str(pool.load_balance_method) ET.SubElement(create_node_elm, "serviceDownAction") \ .text = pool.service_down_action ET.SubElement(create_node_elm, "slowRampTime").text \ = str(pool.slow_ramp_time) response = self.connection.request_with_orgId_api_2( action='networkDomainVip/editPool', method='POST', data=ET.tostring(create_node_elm)).object response_code = findtext(response, 'responseCode', TYPES_URN) return response_code in ['IN_PROGRESS', 'OK'] def ex_destroy_pool(self, pool): """ Destroy an existing pool :param pool: The instance of ``DimensionDataPool`` to destroy :type pool: ``DimensionDataPool`` :return: ``True`` for success, ``False`` for failure :rtype: ``bool`` """ destroy_request = ET.Element('deletePool', {'xmlns': TYPES_URN, 'id': pool.id}) result = self.connection.request_with_orgId_api_2( action='networkDomainVip/deletePool', method='POST', data=ET.tostring(destroy_request)).object response_code = findtext(result, 'responseCode', TYPES_URN) return response_code in ['IN_PROGRESS', 'OK'] def ex_get_pool_members(self, pool_id): """ Get the members of a pool :param pool: The instance of a pool :type pool: ``DimensionDataPool`` :return: Returns an ``list`` of ``DimensionDataPoolMember`` :rtype: ``list`` of ``DimensionDataPoolMember`` """ members = self.connection \ .request_with_orgId_api_2('networkDomainVip/poolMember?poolId=%s' % pool_id).object return self._to_members(members) def ex_get_pool_member(self, pool_member_id): """ Get a specific member of a pool :param pool: The id of a pool member :type pool: ``str`` :return: Returns an instance of ``DimensionDataPoolMember`` :rtype: ``DimensionDataPoolMember`` """ member = self.connection \ .request_with_orgId_api_2('networkDomainVip/poolMember/%s' % pool_member_id).object return self._to_member(member) def ex_set_pool_member_state(self, member, enabled=True): request = ET.Element('editPoolMember', {'xmlns': TYPES_URN, 'id': member.id}) state = "ENABLED" if enabled is True else "DISABLED" ET.SubElement(request, 'status').text = state result = self.connection.request_with_orgId_api_2( action='networkDomainVip/editPoolMember', method='POST', data=ET.tostring(request)).object response_code = findtext(result, 'responseCode', TYPES_URN) return response_code in ['IN_PROGRESS', 'OK'] def ex_destroy_pool_member(self, member, destroy_node=False): """ Destroy a specific member of a pool :param pool: The instance of a pool member :type pool: ``DimensionDataPoolMember`` :param destroy_node: Also destroy the associated node :type destroy_node: ``bool`` :return: ``True`` for success, ``False`` for failure :rtype: ``bool`` """ # remove the pool member destroy_request = ET.Element('removePoolMember', {'xmlns': TYPES_URN, 'id': member.id}) result = self.connection.request_with_orgId_api_2( action='networkDomainVip/removePoolMember', method='POST', data=ET.tostring(destroy_request)).object if member.node_id is not None and destroy_node is True: return self.ex_destroy_node(member.node_id) else: response_code = findtext(result, 'responseCode', TYPES_URN) return response_code in ['IN_PROGRESS', 'OK'] def ex_get_nodes(self, ex_network_domain_id=None): """ Get the nodes within this geography or in given network. :param ex_network_domain_id: UUID of Network Domain if not None returns only balancers in the given network if None then returns all pools for the organization :type ex_network_domain_id: ``str`` :return: Returns an ``list`` of ``DimensionDataVIPNode`` :rtype: ``list`` of ``DimensionDataVIPNode`` """ params = None if ex_network_domain_id is not None: params = {"networkDomainId": ex_network_domain_id} nodes = self.connection \ .request_with_orgId_api_2('networkDomainVip/node', params=params).object return self._to_nodes(nodes) def ex_get_node(self, node_id): """ Get the node specified by node_id :return: Returns an instance of ``DimensionDataVIPNode`` :rtype: Instance of ``DimensionDataVIPNode`` """ nodes = self.connection \ .request_with_orgId_api_2('networkDomainVip/node/%s' % node_id).object return self._to_node(nodes) def ex_destroy_node(self, node_id): """ Destroy a specific node :param node_id: The ID of of a ``DimensionDataVIPNode`` :type node_id: ``str`` :return: ``True`` for success, ``False`` for failure :rtype: ``bool`` """ # Destroy the node destroy_request = ET.Element('deleteNode', {'xmlns': TYPES_URN, 'id': node_id}) result = self.connection.request_with_orgId_api_2( action='networkDomainVip/deleteNode', method='POST', data=ET.tostring(destroy_request)).object response_code = findtext(result, 'responseCode', TYPES_URN) return response_code in ['IN_PROGRESS', 'OK'] def ex_wait_for_state(self, state, func, poll_interval=2, timeout=60, *args, **kwargs): """ Wait for the function which returns a instance with field status to match Keep polling func until one of the desired states is matched :param state: Either the desired state (`str`) or a `list` of states :type state: ``str`` or ``list`` :param func: The function to call, e.g. ex_get_vlan :type func: ``function`` :param poll_interval: The number of seconds to wait between checks :type poll_interval: `int` :param timeout: The total number of seconds to wait to reach a state :type timeout: `int` :param args: The arguments for func :type args: Positional arguments :param kwargs: The arguments for func :type kwargs: Keyword arguments """ return self.connection.wait_for_state(state, func, poll_interval, timeout, *args, **kwargs) def ex_get_default_health_monitors(self, network_domain_id): """ Get the default health monitors available for a network domain :param network_domain_id: The ID of of a ``DimensionDataNetworkDomain`` :type network_domain_id: ``str`` :rtype: `list` of :class:`DimensionDataDefaultHealthMonitor` """ result = self.connection.request_with_orgId_api_2( action='networkDomainVip/defaultHealthMonitor', params={'networkDomainId': network_domain_id}, method='GET').object return self._to_health_monitors(result) def ex_get_default_persistence_profiles(self, network_domain_id): """ Get the default persistence profiles available for a network domain :param network_domain_id: The ID of of a ``DimensionDataNetworkDomain`` :type network_domain_id: ``str`` :rtype: `list` of :class:`DimensionDataPersistenceProfile` """ result = self.connection.request_with_orgId_api_2( action='networkDomainVip/defaultPersistenceProfile', params={'networkDomainId': network_domain_id}, method='GET').object return self._to_persistence_profiles(result) def ex_get_default_irules(self, network_domain_id): """ Get the default iRules available for a network domain :param network_domain_id: The ID of of a ``DimensionDataNetworkDomain`` :type network_domain_id: ``str`` :rtype: `list` of :class:`DimensionDataDefaultiRule` """ result = self.connection.request_with_orgId_api_2( action='networkDomainVip/defaultIrule', params={'networkDomainId': network_domain_id}, method='GET').object return self._to_irules(result) def _to_irules(self, object): irules = [] matches = object.findall( fixxpath('defaultIrule', TYPES_URN)) for element in matches: irules.append(self._to_irule(element)) return irules def _to_irule(self, element): compatible = [] matches = element.findall( fixxpath('virtualListenerCompatibility', TYPES_URN)) for match_element in matches: compatible.append( DimensionDataVirtualListenerCompatibility( type=match_element.get('type'), protocol=match_element.get('protocol', None))) irule_element = element.find(fixxpath('irule', TYPES_URN)) return DimensionDataDefaultiRule( id=irule_element.get('id'), name=irule_element.get('name'), compatible_listeners=compatible ) def _to_persistence_profiles(self, object): profiles = [] matches = object.findall( fixxpath('defaultPersistenceProfile', TYPES_URN)) for element in matches: profiles.append(self._to_persistence_profile(element)) return profiles def _to_persistence_profile(self, element): compatible = [] matches = element.findall( fixxpath('virtualListenerCompatibility', TYPES_URN)) for match_element in matches: compatible.append( DimensionDataVirtualListenerCompatibility( type=match_element.get('type'), protocol=match_element.get('protocol', None))) return DimensionDataPersistenceProfile( id=element.get('id'), fallback_compatible=bool( element.get('fallbackCompatible') == "true"), name=findtext(element, 'name', TYPES_URN), compatible_listeners=compatible ) def _to_health_monitors(self, object): monitors = [] matches = object.findall(fixxpath('defaultHealthMonitor', TYPES_URN)) for element in matches: monitors.append(self._to_health_monitor(element)) return monitors def _to_health_monitor(self, element): return DimensionDataDefaultHealthMonitor( id=element.get('id'), name=findtext(element, 'name', TYPES_URN), node_compatible=bool( findtext(element, 'nodeCompatible', TYPES_URN) == "true"), pool_compatible=bool( findtext(element, 'poolCompatible', TYPES_URN) == "true"), ) def _to_nodes(self, object): nodes = [] for element in object.findall(fixxpath("node", TYPES_URN)): nodes.append(self._to_node(element)) return nodes def _to_node(self, element): ipaddress = findtext(element, 'ipv4Address', TYPES_URN) if ipaddress is None: ipaddress = findtext(element, 'ipv6Address', TYPES_URN) name = findtext(element, 'name', TYPES_URN) node = DimensionDataVIPNode( id=element.get('id'), name=name, status=self._VALUE_TO_STATE_MAP.get( findtext(element, 'state', TYPES_URN), State.UNKNOWN), connection_rate_limit=findtext(element, 'connectionRateLimit', TYPES_URN), connection_limit=findtext(element, 'connectionLimit', TYPES_URN), ip=ipaddress) return node def _to_balancers(self, object): loadbalancers = [] for element in object.findall(fixxpath("virtualListener", TYPES_URN)): loadbalancers.append(self._to_balancer(element)) return loadbalancers def _to_balancer(self, element): ipaddress = findtext(element, 'listenerIpAddress', TYPES_URN) name = findtext(element, 'name', TYPES_URN) port = findtext(element, 'port', TYPES_URN) extra = {} pool_element = element.find(fixxpath( 'pool', TYPES_URN)) if pool_element is None: extra['pool_id'] = None else: extra['pool_id'] = pool_element.get('id') extra['network_domain_id'] = findtext(element, 'networkDomainId', TYPES_URN) balancer = LoadBalancer( id=element.get('id'), name=name, state=self._VALUE_TO_STATE_MAP.get( findtext(element, 'state', TYPES_URN), State.UNKNOWN), ip=ipaddress, port=port, driver=self.connection.driver, extra=extra ) return balancer def _to_members(self, object): members = [] for element in object.findall(fixxpath("poolMember", TYPES_URN)): members.append(self._to_member(element)) return members def _to_member(self, element): port = findtext(element, 'port', TYPES_URN) if port is not None: port = int(port) pool_member = DimensionDataPoolMember( id=element.get('id'), name=element.find(fixxpath( 'node', TYPES_URN)).get('name'), status=findtext(element, 'state', TYPES_URN), node_id=element.find(fixxpath( 'node', TYPES_URN)).get('id'), ip=element.find(fixxpath( 'node', TYPES_URN)).get('ipAddress'), port=port ) return pool_member def _to_pools(self, object): pools = [] for element in object.findall(fixxpath("pool", TYPES_URN)): pools.append(self._to_pool(element)) return pools def _to_pool(self, element): pool = DimensionDataPool( id=element.get('id'), name=findtext(element, 'name', TYPES_URN), status=findtext(element, 'state', TYPES_URN), description=findtext(element, 'description', TYPES_URN), load_balance_method=findtext(element, 'loadBalanceMethod', TYPES_URN), health_monitor_id=findtext(element, 'healthMonitorId', TYPES_URN), service_down_action=findtext(element, 'serviceDownAction', TYPES_URN), slow_ramp_time=findtext(element, 'slowRampTime', TYPES_URN), ) return pool