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/saltstack/salt/lib/python3.10/site-packages/salt/states
Viewing File: /opt/saltstack/salt/lib/python3.10/site-packages/salt/states/dellchassis.py
""" Manage chassis via Salt Proxies. .. versionadded:: 2015.8.2 Below is an example state that sets basic parameters: .. code-block:: yaml my-dell-chassis: dellchassis.chassis: - chassis_name: my-dell-chassis - datacenter: dc-1-us - location: my-location - mode: 2 - idrac_launch: 1 - slot_names: - server-1: my-slot-name - server-2: my-other-slot-name - blade_power_states: - server-1: on - server-2: off - server-3: powercycle However, it is possible to place the entire set of chassis configuration data in pillar. Here's an example pillar structure: .. code-block:: yaml proxy: host: 10.27.20.18 admin_username: root fallback_admin_username: root passwords: - super-secret - old-secret proxytype: fx2 chassis: name: fx2-1 username: root password: saltstack1 datacenter: london location: rack-1-shelf-3 management_mode: 2 idrac_launch: 0 slot_names: - 'server-1': blade1 - 'server-2': blade2 servers: server-1: idrac_password: saltstack1 ipmi_over_lan: True ip: 172.17.17.132 netmask: 255.255.0.0 gateway: 172.17.17.1 server-2: idrac_password: saltstack1 ipmi_over_lan: True ip: 172.17.17.2 netmask: 255.255.0.0 gateway: 172.17.17.1 server-3: idrac_password: saltstack1 ipmi_over_lan: True ip: 172.17.17.20 netmask: 255.255.0.0 gateway: 172.17.17.1 server-4: idrac_password: saltstack1 ipmi_over_lan: True ip: 172.17.17.2 netmask: 255.255.0.0 gateway: 172.17.17.1 switches: switch-1: ip: 192.168.1.2 netmask: 255.255.255.0 gateway: 192.168.1.1 snmp: nonpublic password: saltstack1 switch-2: ip: 192.168.1.3 netmask: 255.255.255.0 gateway: 192.168.1.1 snmp: nonpublic password: saltstack1 And to go with it, here's an example state that pulls the data from the pillar stated above: .. code-block:: jinja {% set details = pillar.get('proxy:chassis', {}) %} standup-step1: dellchassis.chassis: - name: {{ details['name'] }} - location: {{ details['location'] }} - mode: {{ details['management_mode'] }} - idrac_launch: {{ details['idrac_launch'] }} - slot_names: {% for entry details['slot_names'] %} - {{ next(iter(entry)) }}: {{ entry[next(iter(entry))] }} {% endfor %} blade_powercycle: dellchassis.chassis: - blade_power_states: - server-1: powercycle - server-2: powercycle - server-3: powercycle - server-4: powercycle # Set idrac_passwords for blades. racadm needs them to be called 'server-x' {% for k, v in details['servers'].iteritems() %} {{ k }}: dellchassis.blade_idrac: - idrac_password: {{ v['idrac_password'] }} {% endfor %} # Set management ip addresses, passwords, and snmp strings for switches {% for k, v in details['switches'].iteritems() %} {{ k }}-switch-setup: dellchassis.switch: - name: {{ k }} - ip: {{ v['ip'] }} - netmask: {{ v['netmask'] }} - gateway: {{ v['gateway'] }} - password: {{ v['password'] }} - snmp: {{ v['snmp'] }} {% endfor %} .. note:: This state module relies on the dracr.py execution module, which runs racadm commands on the chassis, blades, etc. The racadm command runs very slowly and, depending on your state, the proxy minion return might timeout before the racadm commands have completed. If you are repeatedly seeing minions timeout after state calls, please use the ``-t`` CLI argument to increase the timeout variable. For example: .. code-block:: bash salt '*' state.sls my-dell-chasis-state-name -t 60 .. note:: The Dell CMC units perform adequately but many iDRACs are **excruciatingly** slow. Some functions can take minutes to execute. """ import logging import os from salt.exceptions import CommandExecutionError # Import Salt lobs # Get logging started log = logging.getLogger(__name__) def __virtual__(): if "chassis.cmd" in __salt__: return True return (False, "chassis module could not be loaded") def blade_idrac( name, idrac_password=None, idrac_ipmi=None, idrac_ip=None, idrac_netmask=None, idrac_gateway=None, idrac_dnsname=None, idrac_dhcp=None, ): """ Set parameters for iDRAC in a blade. :param idrac_password: Password to use to connect to the iDRACs directly (idrac_ipmi and idrac_dnsname must be set directly on the iDRAC. They can't be set through the CMC. If this password is present, use it instead of the CMC password) :param idrac_ipmi: Enable/Disable IPMI over LAN :param idrac_ip: Set IP address for iDRAC :param idrac_netmask: Set netmask for iDRAC :param idrac_gateway: Set gateway for iDRAC :param idrac_dhcp: Turn on DHCP for iDRAC (True turns on, False does nothing becaause setting a static IP will disable DHCP). :return: A standard Salt changes dictionary NOTE: If any of the IP address settings is configured, all of ip, netmask, and gateway must be present """ ret = {"name": name, "result": True, "changes": {}, "comment": ""} if not idrac_password: (username, password) = __salt__["chassis.chassis_credentials"]() else: password = idrac_password module_network = __salt__["chassis.cmd"]("network_info", module=name) current_idrac_ip = module_network["Network"]["IP Address"] if idrac_ipmi is not None: if idrac_ipmi is True or idrac_ipmi == 1: idrac_ipmi = "1" if idrac_ipmi is False or idrac_ipmi == 0: idrac_ipmi = "0" current_ipmi = __salt__["dracr.get_general"]( "cfgIpmiLan", "cfgIpmiLanEnable", host=current_idrac_ip, admin_username="root", admin_password=password, ) if current_ipmi != idrac_ipmi: ch = {"Old": current_ipmi, "New": idrac_ipmi} ret["changes"]["IPMI"] = ch if idrac_dnsname is not None: dnsret = __salt__["dracr.get_dns_dracname"]( host=current_idrac_ip, admin_username="root", admin_password=password ) current_dnsname = dnsret["[Key=iDRAC.Embedded.1#NIC.1]"]["DNSRacName"] if current_dnsname != idrac_dnsname: ch = {"Old": current_dnsname, "New": idrac_dnsname} ret["changes"]["DNSRacName"] = ch if idrac_dhcp is not None or idrac_ip or idrac_netmask or idrac_gateway: if idrac_dhcp is True or idrac_dhcp == 1: idrac_dhcp = 1 else: idrac_dhcp = 0 if str(module_network["Network"]["DHCP Enabled"]) == "0" and idrac_dhcp == 1: ch = {"Old": module_network["Network"]["DHCP Enabled"], "New": idrac_dhcp} ret["changes"]["DRAC DHCP"] = ch if idrac_dhcp == 0 and all([idrac_ip, idrac_netmask, idrac_netmask]): current_network = __salt__["chassis.cmd"]("network_info", module=name) old_ipv4 = {} new_ipv4 = {} if current_network["Network"]["IP Address"] != idrac_ip: old_ipv4["ip"] = current_network["Network"]["IP Address"] new_ipv4["ip"] = idrac_ip if current_network["Network"]["Subnet Mask"] != idrac_netmask: old_ipv4["netmask"] = current_network["Network"]["Subnet Mask"] new_ipv4["netmask"] = idrac_netmask if current_network["Network"]["Gateway"] != idrac_gateway: old_ipv4["gateway"] = current_network["Network"]["Gateway"] new_ipv4["gateway"] = idrac_gateway if new_ipv4 != {}: ret["changes"]["Network"] = {} ret["changes"]["Network"]["Old"] = old_ipv4 ret["changes"]["Network"]["New"] = new_ipv4 if ret["changes"] == {}: ret["comment"] = "iDRAC on blade is already in the desired state." return ret if __opts__["test"] and ret["changes"] != {}: ret["result"] = None ret["comment"] = "iDRAC on blade will change." return ret if "IPMI" in ret["changes"]: ipmi_result = __salt__["dracr.set_general"]( "cfgIpmiLan", "cfgIpmiLanEnable", idrac_ipmi, host=current_idrac_ip, admin_username="root", admin_password=password, ) if not ipmi_result: ret["result"] = False ret["changes"]["IPMI"]["success"] = False if "DNSRacName" in ret["changes"]: dnsracname_result = __salt__["dracr.set_dns_dracname"]( idrac_dnsname, host=current_idrac_ip, admin_username="root", admin_password=password, ) if dnsracname_result["retcode"] == 0: ret["changes"]["DNSRacName"]["success"] = True else: ret["result"] = False ret["changes"]["DNSRacName"]["success"] = False ret["changes"]["DNSRacName"]["return"] = dnsracname_result if "DRAC DHCP" in ret["changes"]: dhcp_result = __salt__["chassis.cmd"]("set_niccfg", dhcp=idrac_dhcp) if dhcp_result["retcode"]: ret["changes"]["DRAC DHCP"]["success"] = True else: ret["result"] = False ret["changes"]["DRAC DHCP"]["success"] = False ret["changes"]["DRAC DHCP"]["return"] = dhcp_result if "Network" in ret["changes"]: network_result = __salt__["chassis.cmd"]( "set_niccfg", ip=idrac_ip, netmask=idrac_netmask, gateway=idrac_gateway, module=name, ) if network_result["retcode"] == 0: ret["changes"]["Network"]["success"] = True else: ret["result"] = False ret["changes"]["Network"]["success"] = False ret["changes"]["Network"]["return"] = network_result return ret def chassis( name, chassis_name=None, password=None, datacenter=None, location=None, mode=None, idrac_launch=None, slot_names=None, blade_power_states=None, ): """ Manage a Dell Chassis. chassis_name The name of the chassis. datacenter The datacenter in which the chassis is located location The location of the chassis. password Password for the chassis. Note: If this password is set for the chassis, the current implementation of this state will set this password both on the chassis and the iDrac passwords on any configured blades. If the password for the blades should be distinct, they should be set separately with the blade_idrac function. mode The management mode of the chassis. Viable options are: - 0: None - 1: Monitor - 2: Manage and Monitor idrac_launch The iDRAC launch method of the chassis. Viable options are: - 0: Disabled (launch iDRAC using IP address) - 1: Enabled (launch iDRAC using DNS name) slot_names The names of the slots, provided as a list identified by their slot numbers. blade_power_states The power states of a blade server, provided as a list and identified by their server numbers. Viable options are: - on: Ensure the blade server is powered on. - off: Ensure the blade server is powered off. - powercycle: Power cycle the blade server. Example: .. code-block:: yaml my-dell-chassis: dellchassis.chassis: - chassis_name: my-dell-chassis - location: my-location - datacenter: london - mode: 2 - idrac_launch: 1 - slot_names: - 1: my-slot-name - 2: my-other-slot-name - blade_power_states: - server-1: on - server-2: off - server-3: powercycle """ ret = { "name": chassis_name, "chassis_name": chassis_name, "result": True, "changes": {}, "comment": "", } chassis_cmd = "chassis.cmd" cfg_tuning = "cfgRacTuning" mode_cmd = "cfgRacTuneChassisMgmtAtServer" launch_cmd = "cfgRacTuneIdracDNSLaunchEnable" inventory = __salt__[chassis_cmd]("inventory") if idrac_launch: idrac_launch = str(idrac_launch) current_name = __salt__[chassis_cmd]("get_chassis_name") if chassis_name != current_name: ret["changes"].update({"Name": {"Old": current_name, "New": chassis_name}}) current_dc = __salt__[chassis_cmd]("get_chassis_datacenter") if datacenter and datacenter != current_dc: ret["changes"].update({"Datacenter": {"Old": current_dc, "New": datacenter}}) if password: ret["changes"].update({"Password": {"Old": "******", "New": "******"}}) if location: current_location = __salt__[chassis_cmd]("get_chassis_location") if location != current_location: ret["changes"].update( {"Location": {"Old": current_location, "New": location}} ) if mode: current_mode = __salt__[chassis_cmd]("get_general", cfg_tuning, mode_cmd) if mode != current_mode: ret["changes"].update( {"Management Mode": {"Old": current_mode, "New": mode}} ) if idrac_launch: current_launch_method = __salt__[chassis_cmd]( "get_general", cfg_tuning, launch_cmd ) if idrac_launch != current_launch_method: ret["changes"].update( { "iDrac Launch Method": { "Old": current_launch_method, "New": idrac_launch, } } ) if slot_names: current_slot_names = __salt__[chassis_cmd]("list_slotnames") for s in slot_names: key = next(iter(s)) new_name = s[key] if key.startswith("slot-"): key = key[5:] current_slot_name = current_slot_names.get(key).get("slotname") if current_slot_name != new_name: old = {key: current_slot_name} new = {key: new_name} if ret["changes"].get("Slot Names") is None: ret["changes"].update({"Slot Names": {"Old": {}, "New": {}}}) ret["changes"]["Slot Names"]["Old"].update(old) ret["changes"]["Slot Names"]["New"].update(new) current_power_states = {} target_power_states = {} if blade_power_states: for b in blade_power_states: key = next(iter(b)) status = __salt__[chassis_cmd]("server_powerstatus", module=key) current_power_states[key] = status.get("status", -1) if b[key] == "powerdown": if current_power_states[key] != -1 and current_power_states[key]: target_power_states[key] = "powerdown" if b[key] == "powerup": if current_power_states[key] != -1 and not current_power_states[key]: target_power_states[key] = "powerup" if b[key] == "powercycle": if current_power_states[key] != -1 and not current_power_states[key]: target_power_states[key] = "powerup" if current_power_states[key] != -1 and current_power_states[key]: target_power_states[key] = "powercycle" for k, v in target_power_states.items(): old = {k: current_power_states[k]} new = {k: v} if ret["changes"].get("Blade Power States") is None: ret["changes"].update({"Blade Power States": {"Old": {}, "New": {}}}) ret["changes"]["Blade Power States"]["Old"].update(old) ret["changes"]["Blade Power States"]["New"].update(new) if ret["changes"] == {}: ret["comment"] = "Dell chassis is already in the desired state." return ret if __opts__["test"]: ret["result"] = None ret["comment"] = "Dell chassis configuration will change." return ret # Finally, set the necessary configurations on the chassis. name = __salt__[chassis_cmd]("set_chassis_name", chassis_name) if location: location = __salt__[chassis_cmd]("set_chassis_location", location) pw_result = True if password: pw_single = True if __salt__[chassis_cmd]( "change_password", username="root", uid=1, password=password ): for blade in inventory["server"]: pw_single = __salt__[chassis_cmd]( "deploy_password", username="root", password=password, module=blade ) if not pw_single: pw_result = False else: pw_result = False if datacenter: datacenter_result = __salt__[chassis_cmd]("set_chassis_datacenter", datacenter) if mode: mode = __salt__[chassis_cmd]("set_general", cfg_tuning, mode_cmd, mode) if idrac_launch: idrac_launch = __salt__[chassis_cmd]( "set_general", cfg_tuning, launch_cmd, idrac_launch ) if ret["changes"].get("Slot Names") is not None: slot_rets = [] for s in slot_names: key = next(iter(s)) new_name = s[key] if key.startswith("slot-"): key = key[5:] slot_rets.append(__salt__[chassis_cmd]("set_slotname", key, new_name)) if any(slot_rets) is False: slot_names = False else: slot_names = True powerchange_all_ok = True for k, v in target_power_states.items(): powerchange_ok = __salt__[chassis_cmd]("server_power", v, module=k) if not powerchange_ok: powerchange_all_ok = False if ( any([name, location, mode, idrac_launch, slot_names, powerchange_all_ok]) is False ): ret["result"] = False ret["comment"] = "There was an error setting the Dell chassis." ret["comment"] = "Dell chassis was updated." return ret def switch( name, ip=None, netmask=None, gateway=None, dhcp=None, password=None, snmp=None ): """ Manage switches in a Dell Chassis. name The switch designation (e.g. switch-1, switch-2) ip The Static IP Address of the switch netmask The netmask for the static IP gateway The gateway for the static IP dhcp True: Enable DHCP False: Do not change DHCP setup (disabling DHCP is automatic when a static IP is set) password The access (root) password for the switch snmp The SNMP community string for the switch Example: .. code-block:: yaml my-dell-chassis: dellchassis.switch: - switch: switch-1 - ip: 192.168.1.1 - netmask: 255.255.255.0 - gateway: 192.168.1.254 - dhcp: True - password: secret - snmp: public """ ret = {"name": name, "result": True, "changes": {}, "comment": ""} current_nic = __salt__["chassis.cmd"]("network_info", module=name) try: if current_nic.get("retcode", 0) != 0: ret["result"] = False ret["comment"] = current_nic["stdout"] return ret if ip or netmask or gateway: if not ip: ip = current_nic["Network"]["IP Address"] if not netmask: ip = current_nic["Network"]["Subnet Mask"] if not gateway: ip = current_nic["Network"]["Gateway"] if current_nic["Network"]["DHCP Enabled"] == "0" and dhcp: ret["changes"].update( { "DHCP": { "Old": {"DHCP Enabled": current_nic["Network"]["DHCP Enabled"]}, "New": {"DHCP Enabled": dhcp}, } } ) if ( (ip or netmask or gateway) and not dhcp and ( ip != current_nic["Network"]["IP Address"] or netmask != current_nic["Network"]["Subnet Mask"] or gateway != current_nic["Network"]["Gateway"] ) ): ret["changes"].update( { "IP": { "Old": current_nic["Network"], "New": { "IP Address": ip, "Subnet Mask": netmask, "Gateway": gateway, }, } } ) if password: if "New" not in ret["changes"]: ret["changes"]["New"] = {} ret["changes"]["New"].update({"Password": "*****"}) if snmp: if "New" not in ret["changes"]: ret["changes"]["New"] = {} ret["changes"]["New"].update({"SNMP": "*****"}) if ret["changes"] == {}: ret["comment"] = "Switch " + name + " is already in desired state" return ret except AttributeError: ret["changes"] = {} ret["comment"] = "Something went wrong retrieving the switch details" return ret if __opts__["test"]: ret["result"] = None ret["comment"] = "Switch " + name + " configuration will change" return ret # Finally, set the necessary configurations on the chassis. dhcp_ret = net_ret = password_ret = snmp_ret = True if dhcp: dhcp_ret = __salt__["chassis.cmd"]("set_niccfg", module=name, dhcp=dhcp) if ip or netmask or gateway: net_ret = __salt__["chassis.cmd"]( "set_niccfg", ip, netmask, gateway, module=name ) if password: password_ret = __salt__["chassis.cmd"]( "deploy_password", "root", password, module=name ) if snmp: snmp_ret = __salt__["chassis.cmd"]("deploy_snmp", snmp, module=name) if any([password_ret, snmp_ret, net_ret, dhcp_ret]) is False: ret["result"] = False ret["comment"] = f"There was an error setting the switch {name}." ret["comment"] = f"Dell chassis switch {name} was updated." return ret def _firmware_update(firmwarefile="", host="", directory=""): """ Update firmware for a single host """ dest = os.path.join(directory, firmwarefile[7:]) __salt__["cp.get_file"](firmwarefile, dest) username = __pillar__["proxy"]["admin_user"] password = __pillar__["proxy"]["admin_password"] __salt__["dracr.update_firmware"]( dest, host=host, admin_username=username, admin_password=password ) def firmware_update(hosts=None, directory=""): """ State to update the firmware on host using the ``racadm`` command firmwarefile filename (string) starting with ``salt://`` host string representing the hostname supplied to the ``racadm`` command directory Directory name where firmwarefile will be downloaded .. code-block:: yaml dell-chassis-firmware-update: dellchassis.firmware_update: hosts: cmc: salt://firmware_cmc.exe server-1: salt://firmware.exe directory: /opt/firmwares """ ret = {} ret.changes = {} success = True for host, firmwarefile in hosts: try: _firmware_update(firmwarefile, host, directory) ret["changes"].update( { "host": { "comment": f"Firmware update submitted for {host}", "success": True, } } ) except CommandExecutionError as err: success = False ret["changes"].update( { "host": { "comment": f"FAILED to update firmware for {host}", "success": False, "reason": str(err), } } ) ret["result"] = success return ret