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/modules
Viewing File: /opt/saltstack/salt/lib/python3.10/site-packages/salt/modules/nxos.py
""" Execution module for Cisco NX OS Switches. .. versionadded:: 2016.11.0 This module supports execution using a Proxy Minion or Native Minion: 1) Proxy Minion: Connect over SSH or NX-API HTTP(S). See :mod:`salt.proxy.nxos <salt.proxy.nxos>` for proxy minion setup details. 2) Native Minion: Connect over NX-API Unix Domain Socket (UDS). Install the minion inside the GuestShell running on the NX-OS device. :maturity: new :platform: nxos .. note:: To use this module over remote NX-API the feature must be enabled on the NX-OS device by executing ``feature nxapi`` in configuration mode. This is not required for NX-API over UDS. Configuration example: .. code-block:: bash switch# conf t switch(config)# feature nxapi To check that NX-API is properly enabled, execute ``show nxapi``. Output example: .. code-block:: bash switch# show nxapi nxapi enabled HTTPS Listen on port 443 Native minion configuration options: .. code-block:: yaml nxos: cookie: 'username' save_config: False cookie Use the option to override the default cookie 'admin:local' when connecting over UDS and use 'username:local' instead. This is needed when running the salt-minion in the GuestShell using a non-admin user. This option is ignored for SSH and NX-API Proxy minions. save_config: If True, 'copy running-config starting-config' is issues for every configuration command. If False, Running config is not saved to startup config Default: True The recommended approach is to use the `save_running_config` function instead of this option to improve performance. The default behavior controlled by this option is preserved for backwards compatibility. The APIs defined in this execution module can also be executed using salt-call from the GuestShell environment as follows. .. code-block:: bash salt-call --local nxos.sendline 'show lldp neighbors' raw_text .. note:: The functions in this module should be executed like so: salt '*' nxos.<function> salt '*' nxos.get_user username=admin For backwards compatibility, the following syntax will be supported until the 3001 release. salt '*' nxos.cmd <function> salt '*' nxos.cmd get_user username=admin """ import ast import difflib import logging import re from socket import error as socket_error import salt.utils.nxos import salt.utils.platform from salt.exceptions import CommandExecutionError, NxosError, SaltInvocationError from salt.utils.args import clean_kwargs from salt.utils.pycrypto import gen_hash from salt.utils.versions import warn_until __virtualname__ = "nxos" log = logging.getLogger(__name__) DEVICE_DETAILS = {"grains_cache": {}} COPY_RS = "copy running-config startup-config" CONNECTION_ERROR_MSG = """ Unable to send command to the NX-OS device. Please verify the following and re-try: - 'feature ssh' must be enabled for SSH proxy minions. - 'feature nxapi' must be enabled for NX-API proxy minions. - Settings in the proxy minion configuration file must match device settings. - NX-OS device is reachable from the Salt Master. """ def __virtual__(): return __virtualname__ def ping(**kwargs): """ Ping the device on the other end of the connection. .. code-block: bash salt '*' nxos.ping """ if salt.utils.platform.is_proxy(): return __proxy__["nxos.ping"]() return __utils__["nxos.ping"](**kwargs) # ----------------------------------------------------------------------------- # Device Get Functions # ----------------------------------------------------------------------------- def check_password(username, password, encrypted=False, **kwargs): """ Verify user password. username Username on which to perform password check password Password to check encrypted Whether or not the password is encrypted Default: False .. code-block: bash salt '*' nxos.check_password username=admin password=admin salt '*' nxos.check_password username=admin \\ password='$5$2fWwO2vK$s7.Hr3YltMNHuhywQQ3nfOd.gAPHgs3SOBYYdGT3E.A' \\ encrypted=True """ hash_algorithms = { "1": "md5", "2a": "blowfish", "5": "sha256", "6": "sha512", } password_line = get_user(username, **kwargs) if not password_line: return None if "!" in password_line: return False cur_hash = re.search(r"(\$[0-6](?:\$[^$ ]+)+)", password_line).group(0) if encrypted is False: hash_type, cur_salt, hashed_pass = re.search( r"^\$([0-6])\$([^$]+)\$(.*)$", cur_hash ).groups() new_hash = gen_hash( crypt_salt=cur_salt, password=password, algorithm=hash_algorithms[hash_type], ) else: new_hash = password if new_hash == cur_hash: return True return False def check_role(username, role, **kwargs): """ Verify role assignment for user. .. code-block:: bash salt '*' nxos.check_role username=admin role=network-admin """ return role in get_roles(username, **kwargs) def cmd(command, *args, **kwargs): """ NOTE: This function is preserved for backwards compatibility. This allows commands to be executed using either of the following syntactic forms. salt '*' nxos.cmd <function> or salt '*' nxos.<function> command function from `salt.modules.nxos` to run args positional args to pass to `command` function kwargs key word arguments to pass to `command` function .. code-block:: bash salt '*' nxos.cmd sendline 'show ver' salt '*' nxos.cmd show_run salt '*' nxos.cmd check_password username=admin password='$5$lkjsdfoi$blahblahblah' encrypted=True """ warn_until("Argon", "'nxos.cmd COMMAND' is deprecated in favor of 'nxos.COMMAND'") for k in list(kwargs): if k.startswith("__pub_"): kwargs.pop(k) local_command = ".".join(["nxos", command]) log.info("local command: %s", local_command) if local_command not in __salt__: return False return __salt__[local_command](*args, **kwargs) def find(pattern, **kwargs): """ Find all instances where the pattern is in the running configuration. .. code-block:: bash salt '*' nxos.find '^snmp-server.*$' .. note:: This uses the `re.MULTILINE` regex format for python, and runs the regex against the whole show_run output. """ matcher = re.compile(pattern, re.MULTILINE) return matcher.findall(show_run(**kwargs)) def get_roles(username, **kwargs): """ Get roles assigned to a username. .. code-block: bash salt '*' nxos.get_roles username=admin """ user = get_user(username) if not user: return [] command = f"show user-account {username}" info = sendline(command, **kwargs) if isinstance(info, list): info = info[0] roles = re.search(r"^\s*roles:(.*)$", info, re.MULTILINE) if roles: roles = roles.group(1).strip().split(" ") else: roles = [] return roles def get_user(username, **kwargs): """ Get username line from switch. .. code-block: bash salt '*' nxos.get_user username=admin """ command = f'show run | include "^username {username} password 5 "' info = sendline(command, **kwargs) if isinstance(info, list): info = info[0] return info def grains(**kwargs): """ Get grains for minion. .. code-block: bash salt '*' nxos.grains """ if not DEVICE_DETAILS["grains_cache"]: ret = salt.utils.nxos.system_info(show_ver(**kwargs)) log.debug(ret) DEVICE_DETAILS["grains_cache"].update(ret["nxos"]) return DEVICE_DETAILS["grains_cache"] def grains_refresh(**kwargs): """ Refresh the grains for the NX-OS device. .. code-block: bash salt '*' nxos.grains_refresh """ DEVICE_DETAILS["grains_cache"] = {} return grains(**kwargs) def sendline(command, method="cli_show_ascii", **kwargs): """ Send arbitrary commands to the NX-OS device. command The command or list of commands to be sent. ['cmd1', 'cmd2'] is converted to 'cmd1 ; cmd2'. method: ``cli_show_ascii``: Return raw test or unstructured output. ``cli_show``: Return structured output. ``cli_conf``: Send configuration commands to the device. Defaults to ``cli_show_ascii``. NOTE: method is ignored for SSH proxy minion. All data is returned unstructured. error_pattern Use the option to pass in a regular expression to search for in the returned output of the command that indicates an error has occurred. This option is only used when proxy minion connection type is ssh and otherwise ignored. .. code-block: bash salt '*' nxos.sendline 'show run | include "^username admin password"' salt '*' nxos.sendline "['show inventory', 'show version']" salt '*' nxos.sendline 'show inventory ; show version' """ smethods = ["cli_show_ascii", "cli_show", "cli_conf"] if method not in smethods: msg = """ INPUT ERROR: Second argument 'method' must be one of {} Value passed: {} Hint: White space separated commands should be wrapped by double quotes """.format( smethods, method ) return msg try: if salt.utils.platform.is_proxy(): return __proxy__["nxos.sendline"](command, method, **kwargs) else: return _nxapi_request(command, method, **kwargs) except socket_error as e: return e.strerror + "\n" + CONNECTION_ERROR_MSG except NxosError as e: return e.strerror + "\n" + CONNECTION_ERROR_MSG def show(commands, raw_text=True, **kwargs): """ Execute one or more show (non-configuration) commands. commands The commands to be executed. raw_text: ``True`` Whether to return raw text or structured data. NOTE: raw_text option is ignored for SSH proxy minion. Data is returned unstructured. CLI Example: .. code-block:: bash salt-call --local nxos.show 'show version' salt '*' nxos.show 'show bgp sessions ; show processes' raw_text=False salt 'regular-minion' nxos.show 'show interfaces' host=sw01.example.com username=test password=test """ warn_until( "Argon", "'nxos.show commands' is deprecated in favor of 'nxos.sendline commands'", ) if not isinstance(raw_text, bool): msg = """ INPUT ERROR: Second argument 'raw_text' must be either True or False Value passed: {} Hint: White space separated show commands should be wrapped by double quotes """.format( raw_text ) return msg if raw_text: method = "cli_show_ascii" else: method = "cli_show" response_list = sendline(commands, method, **kwargs) if isinstance(response_list, list): ret = [response for response in response_list if response] if not ret: ret = [""] return ret else: return response_list def show_ver(**kwargs): """ Shortcut to run `show version` on the NX-OS device. .. code-block:: bash salt '*' nxos.show_ver """ command = "show version" info = sendline(command, **kwargs) if isinstance(info, list): info = info[0] return info def show_run(**kwargs): """ Shortcut to run `show running-config` on the NX-OS device. .. code-block:: bash salt '*' nxos.show_run """ command = "show running-config" info = sendline(command, **kwargs) if isinstance(info, list): info = info[0] return info def system_info(**kwargs): """ Return system information for grains of the minion. .. code-block:: bash salt '*' nxos.system_info """ warn_until("Argon", "'nxos.system_info' is deprecated in favor of 'nxos.grains'") return salt.utils.nxos.system_info(show_ver(**kwargs))["nxos"] # ----------------------------------------------------------------------------- # Device Set Functions # ----------------------------------------------------------------------------- def add_config(lines, **kwargs): """ Add one or more config lines to the NX-OS device running config. lines Configuration lines to add save_config If False, don't save configuration commands to startup configuration. If True, save configuration to startup configuration. Default: True .. code-block:: bash salt '*' nxos.add_config 'snmp-server community TESTSTRINGHERE group network-operator' .. note:: For more than one config added per command, lines should be a list. """ warn_until( "Argon", "'nxos.add_config lines' is deprecated in favor of 'nxos.config commands'", ) kwargs = clean_kwargs(**kwargs) return config(lines, **kwargs) def config( commands=None, config_file=None, template_engine="jinja", context=None, defaults=None, saltenv="base", **kwargs, ): """ Configures the Nexus switch with the specified commands. This method is used to send configuration commands to the switch. It will take either a string or a list and prepend the necessary commands to put the session into config mode. .. warning:: All the commands will be applied directly to the running-config. config_file The source file with the configuration commands to be sent to the device. The file can also be a template that can be rendered using the template engine of choice. This can be specified using the absolute path to the file, or using one of the following URL schemes: - ``salt://``, to fetch the file from the Salt fileserver. - ``http://`` or ``https://`` - ``ftp://`` - ``s3://`` - ``swift://`` commands The commands to send to the switch in config mode. If the commands argument is a string it will be cast to a list. The list of commands will also be prepended with the necessary commands to put the session in config mode. .. note:: This argument is ignored when ``config_file`` is specified. template_engine: ``jinja`` The template engine to use when rendering the source file. Default: ``jinja``. To simply fetch the file without attempting to render, set this argument to ``None``. context Variables to add to the template context. defaults Default values of the context_dict. save_config If False, don't save configuration commands to startup configuration. If True, save configuration to startup configuration. Default: True CLI Example: .. code-block:: bash salt '*' nxos.config commands="['spanning-tree mode mstp']" salt '*' nxos.config config_file=salt://config.txt salt '*' nxos.config config_file=https://bit.ly/2LGLcDy context="{'servers': ['1.2.3.4']}" """ kwargs = clean_kwargs(**kwargs) initial_config = sendline("show running-config", **kwargs) if isinstance(initial_config, list): initial_config = initial_config[0] if config_file: file_str = __salt__["cp.get_file_str"](config_file, saltenv=saltenv) if file_str is False: raise CommandExecutionError(f"Source file {config_file} not found") elif commands: if isinstance(commands, str): commands = [commands] file_str = "\n".join(commands) # unify all the commands in a single file, to render them in a go else: raise CommandExecutionError( "Either arg <config_file> or <commands> must be specified" ) if template_engine: file_str = __salt__["file.apply_template_on_contents"]( file_str, template_engine, context, defaults, saltenv ) # whatever the source of the commands would be, split them line by line commands = [line for line in file_str.splitlines() if line.strip()] try: config_result = _configure_device(commands, **kwargs) except socket_error as e: return e.strerror + "\n" + CONNECTION_ERROR_MSG except NxosError as e: return e.strerror + "\n" + CONNECTION_ERROR_MSG config_result = _parse_config_result(config_result) current_config = sendline("show running-config", **kwargs) if isinstance(current_config, list): current_config = current_config[0] diff = difflib.unified_diff( initial_config.splitlines(1)[4:], current_config.splitlines(1)[4:] ) clean_diff = "".join([x.replace("\r", "") for x in diff]) head = "COMMAND_LIST: " cc = config_result[0] cr = config_result[1] return head + cc + "\n" + cr + "\n" + clean_diff def _parse_config_result(data): command_list = " ; ".join([x.strip() for x in data[0]]) config_result = data[1] if isinstance(config_result, list): result = "" if isinstance(config_result[0], dict): for key in config_result[0]: result += config_result[0][key] config_result = result else: config_result = config_result[0] return [command_list, config_result] def delete_config(lines, **kwargs): """ Delete one or more config lines to the switch running config. lines Configuration lines to remove. save_config If False, don't save configuration commands to startup configuration. If True, save configuration to startup configuration. Default: True .. code-block:: bash salt '*' nxos.delete_config 'snmp-server community TESTSTRINGHERE group network-operator' .. note:: For more than one config deleted per command, lines should be a list. """ if not isinstance(lines, list): lines = [lines] for i, _ in enumerate(lines): lines[i] = "no " + lines[i] result = None try: kwargs = clean_kwargs(**kwargs) result = config(lines, **kwargs) except CommandExecutionError as e: # Some commands will generate error code 400 if they do not exist # and we try to remove them. These can be ignored. if ast.literal_eval(e.message)["code"] != "400": raise return result def remove_user(username, **kwargs): """ Remove user from switch. username Username to remove save_config If False, don't save configuration commands to startup configuration. If True, save configuration to startup configuration. Default: True .. code-block:: bash salt '*' nxos.remove_user username=daniel """ user_line = f"no username {username}" kwargs = clean_kwargs(**kwargs) return config(user_line, **kwargs) def replace(old_value, new_value, full_match=False, **kwargs): """ Replace string or full line matches in switch's running config. If full_match is set to True, then the whole line will need to be matched as part of the old value. .. code-block:: bash salt '*' nxos.replace 'TESTSTRINGHERE' 'NEWTESTSTRINGHERE' """ if full_match is False: matcher = re.compile(f"^.*{re.escape(old_value)}.*$", re.MULTILINE) repl = re.compile(re.escape(old_value)) else: matcher = re.compile(old_value, re.MULTILINE) repl = re.compile(old_value) lines = {"old": [], "new": []} for line in matcher.finditer(show_run()): lines["old"].append(line.group(0)) lines["new"].append(repl.sub(new_value, line.group(0))) kwargs = clean_kwargs(**kwargs) if lines["old"]: delete_config(lines["old"], **kwargs) if lines["new"]: config(lines["new"], **kwargs) return lines def save_running_config(**kwargs): """ Save the running configuration to startup configuration. .. code-block:: bash salt '*' nxos.save_running_config """ return config(COPY_RS, **kwargs) def set_password( username, password, encrypted=False, role=None, crypt_salt=None, algorithm="sha256", **kwargs, ): """ Set users password on switch. username Username to configure password Password to configure for username encrypted Whether or not to encrypt the password Default: False role Configure role for the username Default: None crypt_salt Configure crypt_salt setting Default: None algorithm Encryption algorithm Default: sha256 save_config If False, don't save configuration commands to startup configuration. If True, save configuration to startup configuration. Default: True .. code-block:: bash salt '*' nxos.set_password admin TestPass salt '*' nxos.set_password admin \\ password='$5$2fWwO2vK$s7.Hr3YltMNHuhywQQ3nfOd.gAPHgs3SOBYYdGT3E.A' \\ encrypted=True """ if algorithm == "blowfish": raise SaltInvocationError("Hash algorithm requested isn't available on nxos") get_user(username, **kwargs) # verify user exists if encrypted is False: hashed_pass = gen_hash( crypt_salt=crypt_salt, password=password, algorithm=algorithm ) else: hashed_pass = password password_line = f"username {username} password 5 {hashed_pass}" if role is not None: password_line += f" role {role}" kwargs = clean_kwargs(**kwargs) return config(password_line, **kwargs) def set_role(username, role, **kwargs): """ Assign role to username. username Username for role configuration role Configure role for username save_config If False, don't save configuration commands to startup configuration. If True, save configuration to startup configuration. Default: True .. code-block:: bash salt '*' nxos.set_role username=daniel role=vdc-admin. """ role_line = f"username {username} role {role}" kwargs = clean_kwargs(**kwargs) return config(role_line, **kwargs) def unset_role(username, role, **kwargs): """ Remove role from username. username Username for role removal role Role to remove save_config If False, don't save configuration commands to startup configuration. If True, save configuration to startup configuration. Default: True .. code-block:: bash salt '*' nxos.unset_role username=daniel role=vdc-admin """ role_line = f"no username {username} role {role}" kwargs = clean_kwargs(**kwargs) return config(role_line, **kwargs) # ----------------------------------------------------------------------------- # helper functions # ----------------------------------------------------------------------------- def _configure_device(commands, **kwargs): """ Helper function to send configuration commands to the device over a proxy minion or native minion using NX-API or SSH. """ if salt.utils.platform.is_proxy(): return __proxy__["nxos.proxy_config"](commands, **kwargs) else: return _nxapi_config(commands, **kwargs) def _nxapi_config(commands, **kwargs): """ Helper function to send configuration commands using NX-API. """ api_kwargs = __salt__["config.get"]("nxos", {}) api_kwargs.update(**kwargs) if not isinstance(commands, list): commands = [commands] try: ret = _nxapi_request(commands, **kwargs) if api_kwargs.get("save_config"): _nxapi_request(COPY_RS, **kwargs) for each in ret: if "Failure" in each: log.error(each) except CommandExecutionError as e: log.error(e) return [commands, repr(e)] return [commands, ret] def _nxapi_request(commands, method="cli_conf", **kwargs): """ Helper function to send exec and config requests over NX-API. commands The exec or config commands to be sent. method: ``cli_show`` ``cli_show_ascii``: Return raw test or unstructured output. ``cli_show``: Return structured output. ``cli_conf``: Send configuration commands to the device. Defaults to ``cli_conf``. """ if salt.utils.platform.is_proxy(): return __proxy__["nxos._nxapi_request"](commands, method=method, **kwargs) else: api_kwargs = __salt__["config.get"]("nxos", {}) api_kwargs.update(**kwargs) return __utils__["nxos.nxapi_request"](commands, method=method, **api_kwargs)