├── .gitignore ├── example_-_show_users ├── show_users.cmd.cisco ├── show_users.cmd.h3c ├── show_users.cmd.huawei ├── show_users.cmd.cisco_nexus ├── example1.txt ├── example2.txt └── example3.txt ├── README.md └── w-sw-ssh.py /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea 3 | -------------------------------------------------------------------------------- /example_-_show_users/show_users.cmd.cisco: -------------------------------------------------------------------------------- 1 | show users 2 | -------------------------------------------------------------------------------- /example_-_show_users/show_users.cmd.h3c: -------------------------------------------------------------------------------- 1 | disp users 2 | -------------------------------------------------------------------------------- /example_-_show_users/show_users.cmd.huawei: -------------------------------------------------------------------------------- 1 | disp users 2 | -------------------------------------------------------------------------------- /example_-_show_users/show_users.cmd.cisco_nexus: -------------------------------------------------------------------------------- 1 | show users 2 | -------------------------------------------------------------------------------- /example_-_show_users/example1.txt: -------------------------------------------------------------------------------- 1 | 2 | [root@TEST w-sw-ssh]# 3 | [root@TEST w-sw-ssh]# w-sw-ssh.py --uid npc -p --host 192.168.161.10 --cmd "disp users" 4 | Password: 5 | 6 | [2015-11-27 19:46:37] ssh -p 22 -l npc 192.168.161.10 (pexpect timeout 10s) 7 | 8 | npc@192.168.161.10's password: 9 | 10 | 11 | AAA: npc user, you can show 12 | ****************************************************************************** 13 | * Copyright (c) 2004-2015 Hangzhou H3C Tech. Co., Ltd. All rights reserved. * 14 | * Without the owner's prior written consent, * 15 | * no decompiling or reverse-engineering shall be allowed. * 16 | ****************************************************************************** 17 | 18 | 19 | 20 | display version | in (Huawei|H3C).*(Software|uptime) 21 | H3C Comware Platform Software 22 | H3C S5500-52SC-HI uptime is 35 weeks, 0 day, 19 hours, 21 minutes 23 | 24 | 25 | screen-length disable 26 | % Screen-length configuration is disabled for current user. 27 | 28 | 29 | disp users 30 | The user application information of the user interface(s): 31 | Idx UI Delay Type Userlevel 32 | + 25 VTY 0 00:00:00 SSH 3 33 | 34 | Following are more details. 35 | VTY 0 : 36 | User name: npc 37 | Location: 172.16.140.12 38 | + : Current operation user. 39 | F : Current operation user work in async mode. 40 | 41 | 42 | 43 | ======================================= 44 | Device: 192.168.161.10 45 | Vendor: h3c 46 | Model: S5500-52SC-HI 47 | L2_Uplink: 48 | Commands: 49 | 1) disp users 50 | 51 | 52 | 53 | 54 | [root@TEST w-sw-ssh]# 55 | 56 | -------------------------------------------------------------------------------- /example_-_show_users/example2.txt: -------------------------------------------------------------------------------- 1 | 2 | [root@TEST w-sw-ssh]# 3 | [root@TEST w-sw-ssh]# w-sw-ssh.py --uid npc -p --host 192.168.161.10 --cmd_prefix ./show_users 4 | Password: 5 | 6 | [2015-11-27 20:11:51] ssh -p 22 -l npc 192.168.161.10 (pexpect timeout 10s) 7 | 8 | npc@192.168.161.10's password: 9 | 10 | 11 | AAA: npc user, you can show 12 | ****************************************************************************** 13 | * Copyright (c) 2004-2015 Hangzhou H3C Tech. Co., Ltd. All rights reserved. * 14 | * Without the owner's prior written consent, * 15 | * no decompiling or reverse-engineering shall be allowed. * 16 | ****************************************************************************** 17 | 18 | 19 | 20 | display version | in (Huawei|H3C).*(Software|uptime) 21 | H3C Comware Platform Software 22 | H3C S5500-52SC-HI uptime is 35 weeks, 0 day, 19 hours, 46 minutes 23 | 24 | 25 | screen-length disable 26 | % Screen-length configuration is disabled for current user. 27 | 28 | 29 | disp users 30 | The user application information of the user interface(s): 31 | Idx UI Delay Type Userlevel 32 | + 25 VTY 0 00:00:00 SSH 3 33 | 34 | Following are more details. 35 | VTY 0 : 36 | User name: npc 37 | Location: 172.16.140.12 38 | + : Current operation user. 39 | F : Current operation user work in async mode. 40 | 41 | 42 | 43 | ======================================= 44 | Device: 192.168.161.10 45 | Vendor: h3c 46 | Model: S5500-52SC-HI 47 | L2_Uplink: 48 | Commands: 49 | 1) disp users 50 | 51 | 52 | 53 | 54 | [root@TEST w-sw-ssh]# 55 | 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # w-sw-ssh 2 | This is a simple tool to execute multiple commands on network device, such as router and switch, and it supports Cisco Catalyst, Cisco Nexus, Huawei and H3C. 3 | 4 | 5 | Author 6 | ============== 7 | Wang Dayong (Email: wandering_997@sina.com, http://weibo.com/wandering997) 8 | 9 | 10 | Depends 11 | ============== 12 | pexpect (https://pypi.python.org/pypi/pexpect/) 13 | 14 | 15 | Help 16 | ============== 17 | 18 | [root@TEST w-sw-ssh]# w-sw-ssh.py 19 | 20 | Usage: w-sw-ssh.py 21 | 22 | --uid SSH username 23 | 24 | --pwd SSH password 25 | 26 | -p Get password from user input 27 | 28 | --host ip[:port] list of remote ssh server, default port is 22 29 | 30 | --host_file Filename of ip[:port] list 31 | 32 | --cmd Command list to execute on remote ssh server 33 | 34 | --cmd_prefix Prefix of command list files. For example: 35 | test.cmd.cisco 36 | test.cmd.cisco_nexus 37 | test.cmd.h3c 38 | test.cmd.huawei 39 | 'test' is the prefix (--cmd_prefix). 40 | 41 | --cmd_interval Time to wait after a command being executed, default is 0.5s. 42 | And some devices would get error if execute command too fast. 43 | 44 | --save Save config on device automatically after user cmd being executed. 45 | 46 | --log_dir Log command output to // instead of stdout. 47 | Example: 48 | /var/log/test/$(date "+%Y")/$(date "+%Y%m%d")/ 49 | 50 | --thread The maximum threads could be spread each time, default is 1000. 51 | 52 | --timeout Time to wait for command executing, default is 10 seconds. 53 | Try to set higher value in case of seeing 'pexpect timed out' error. 54 | 55 | --l2_sw Check the layer-2 switch only infomation, such as uplink, gateway etc. 56 | 57 | 58 | Caution: 59 | 60 | --host has higher priority than --host_file 61 | --cmd has higher priority than --cmd_prefix 62 | 63 | 64 | Example: 65 | 66 | w-sw-ssh.py --uid npc -p --host 192.168.161.10 --cmd "disp users" 67 | w-sw-ssh.py --uid npc -p --host_file ~/ip.test --cmd_prefix ~/cmd.test 68 | 69 | 70 | [root@TEST w-sw-ssh]# 71 | 72 | 73 | -------------------------------------------------------------------------------- /example_-_show_users/example3.txt: -------------------------------------------------------------------------------- 1 | 2 | [root@TEST w-sw-ssh]# 3 | [root@TEST w-sw-ssh]# 4 | [root@TEST w-sw-ssh]# ls -l 5 | total 16 6 | -rw-r--r-- 1 root root 11 Nov 27 20:10 show_users.cmd.cisco 7 | -rw-r--r-- 1 root root 11 Nov 27 20:10 show_users.cmd.cisco_nexus 8 | -rw-r--r-- 1 root root 11 Nov 27 20:10 show_users.cmd.h3c 9 | -rw-r--r-- 1 root root 11 Nov 27 20:10 show_users.cmd.huawei 10 | [root@TEST w-sw-ssh]# 11 | [root@TEST w-sw-ssh]# 12 | [root@TEST w-sw-ssh]# w-sw-ssh.py --uid npc -p --host 192.168.161.10,192.168.128.180 --cmd_prefix ./show_users --log_dir ./test 13 | Password: 14 | 15 | [2015-11-28 12:18:03] ssh -p 22 -l npc 192.168.128.180 (pexpect timeout 10s) 16 | 17 | [2015-11-28 12:18:03] ssh -p 22 -l npc 192.168.161.10 (pexpect timeout 10s) 18 | 19 | 20 | [root@TEST w-sw-ssh]# 21 | [root@TEST w-sw-ssh]# 22 | [root@TEST w-sw-ssh]# ls -l 23 | total 20 24 | -rw-r--r-- 1 root root 11 Nov 27 20:10 show_users.cmd.cisco 25 | -rw-r--r-- 1 root root 11 Nov 27 20:10 show_users.cmd.cisco_nexus 26 | -rw-r--r-- 1 root root 11 Nov 27 20:10 show_users.cmd.h3c 27 | -rw-r--r-- 1 root root 11 Nov 27 20:10 show_users.cmd.huawei 28 | drwxr-xr-x 2 root root 4096 Nov 28 12:18 test 29 | [root@TEST w-sw-ssh]# 30 | [root@TEST w-sw-ssh]# 31 | [root@TEST w-sw-ssh]# ls -l test/ 32 | total 8 33 | -rw-r--r-- 1 root root 1349 Nov 28 12:18 192.168.128.180 34 | -rw-r--r-- 1 root root 1413 Nov 28 12:18 192.168.161.10 35 | [root@TEST w-sw-ssh]# 36 | [root@TEST w-sw-ssh]# 37 | [root@TEST w-sw-ssh]# cat test/192.168.128.180 38 | AAA Service of bj_office (172.16.140.12) 39 | Password: 40 | 41 | AAA: npc user, you can show 42 | 43 | 2960G# 44 | 2960G#$sion | in (Huawei|H3C).*(Software|uptime) 45 | ^ 46 | % Invalid input detected at '^' marker. 47 | 48 | 2960G# 49 | 2960G#$n | in Cisco.*Software|cisco.*(Chassis|processor) 50 | Cisco IOS Software, C2960S Software (C2960S-UNIVERSALK9-M), Version 12.2(55)SE3, RELEASE SOFTWARE (fc1) 51 | cisco WS-C2960S-48TS-L (PowerPC) processor (revision F0) with 131072K bytes of memory. 52 | 2960G# 53 | 2960G#terminal length 0 54 | 2960G# 55 | 2960G#show users 56 | Line User Host(s) Idle Location 57 | * 1 vty 0 npc idle 00:00:00 localhost 58 | 59 | Interface User Mode Idle Peer Address 60 | 61 | 2960G# 62 | 63 | ======================================= 64 | Device: 192.168.128.180 65 | Vendor: cisco 66 | Model: WS-C2960S-48TS-L 67 | L2_Uplink: 68 | Commands: 69 | 1) show users 70 | 71 | 72 | [root@TEST w-sw-ssh]# 73 | [root@TEST w-sw-ssh]# 74 | [root@TEST w-sw-ssh]# cat test/192.168.161.10 75 | npc@192.168.161.10's password: 76 | 77 | AAA: npc user, you can show 78 | ****************************************************************************** 79 | * Copyright (c) 2004-2015 Hangzhou H3C Tech. Co., Ltd. All rights reserved. * 80 | * Without the owner's prior written consent, * 81 | * no decompiling or reverse-engineering shall be allowed. * 82 | ****************************************************************************** 83 | 84 | 85 | display version | in (Huawei|H3C).*(Software|uptime) 86 | H3C Comware Platform Software 87 | H3C S5500-52SC-HI uptime is 35 weeks, 1 day, 11 hours, 52 minutes 88 | 89 | screen-length disable 90 | % Screen-length configuration is disabled for current user. 91 | 92 | disp users 93 | The user application information of the user interface(s): 94 | Idx UI Delay Type Userlevel 95 | + 25 VTY 0 00:00:00 SSH 3 96 | 97 | Following are more details. 98 | VTY 0 : 99 | User name: npc 100 | Location: 172.16.140.12 101 | + : Current operation user. 102 | F : Current operation user work in async mode. 103 | 104 | 105 | ======================================= 106 | Device: 192.168.161.10 107 | Vendor: h3c 108 | Model: S5500-52SC-HI 109 | L2_Uplink: 110 | Commands: 111 | 1) disp users 112 | 113 | 114 | [root@TEST w-sw-ssh]# 115 | [root@TEST w-sw-ssh]# 116 | 117 | -------------------------------------------------------------------------------- /w-sw-ssh.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | Emulates SSH login and executes cli commands as user interactive to network 5 | devices such as switches, routers etc. 6 | 7 | Copyright (c) Dayong Wang, wandering_997@sina.com 8 | Distributable under the terms of the GNU General Public License 9 | version 2. Provided with no warranties of any sort. 10 | 11 | Revision history 12 | ~~~~~~~~~~~~~~~~ 13 | 2015/11/07 14 | creates by Dayong Wang: 15 | - Just remove some unnecessary information. 16 | 17 | 2016/10/11 18 | bug fix by Dayong Wang: 19 | - Exit while password is wrong and ask for input again. 20 | 21 | Last commit info: 22 | ~~~~~~~~~~~~~~~~~ 23 | $LastChangedDate: $ 24 | $Rev: $ 25 | $Author: $ 26 | 27 | """ 28 | 29 | import getopt 30 | import getpass 31 | import os 32 | import pexpect 33 | import re 34 | import subprocess 35 | import sys 36 | import threading 37 | import time 38 | 39 | 40 | 41 | def help_and_exit(): 42 | 43 | print(''' 44 | Usage: %s 45 | 46 | --uid SSH username 47 | 48 | --pwd SSH password 49 | 50 | -p Get password from user input 51 | 52 | --host ip[:port] list of remote ssh server, default port is 22 53 | 54 | --host_file File of ip[:port] list 55 | 56 | --cmd Command list to execute on remote ssh server 57 | 58 | --cmd_prefix Prefix of command list files. For example: 59 | test.cmd.cisco 60 | test.cmd.cisco_nexus 61 | test.cmd.h3c 62 | test.cmd.huawei 63 | 'test' is the prefix (--cmd_prefix). 64 | 65 | --cmd_interval Time to wait after a command being executed, default is 0.5s. 66 | And some devices would get error if execute command too fast. 67 | 68 | --save Save config on device automatically after user cmd being executed. 69 | 70 | --log_dir Log command output to // instead of stdout. 71 | Example: 72 | /var/log/test/$(date "+%%Y")/$(date "+%%Y%%m%%d")/ 73 | 74 | --thread The maximum threads could be spread each time, default is 1000. 75 | 76 | --timeout Time to wait for command executing, default is 10 seconds. 77 | Try to set higher value in case of seeing 'pexpect timed out' error. 78 | 79 | --l2_sw Check the layer-2 switch only infomation, such as uplink, gateway etc. 80 | 81 | 82 | Caution: 83 | 84 | --host has higher priority than --host_file 85 | --cmd has higher priority than --cmd_prefix 86 | 87 | 88 | Example: 89 | 90 | w-sw-ssh.py --uid npc -p --host 192.168.161.10 --cmd "disp users" 91 | w-sw-ssh.py --uid npc -p --host_file ~/ip.test --cmd_prefix ~/cmd.test 92 | 93 | ''' % (os.path.basename(__file__))) 94 | 95 | sys.exit() 96 | 97 | 98 | 99 | def w_time(time_format = '%Y-%m-%d %H:%M:%S'): 100 | 101 | return time.strftime(time_format, time.localtime(time.time())) 102 | 103 | 104 | 105 | def sys_cmd(str_cmd): 106 | 107 | sp = subprocess.Popen(str_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 108 | str_out = sp.stdout.read() 109 | str_err = sp.stderr.read() 110 | sp.wait() 111 | return [str_out, str_err] 112 | 113 | 114 | 115 | def w_threading(func_name, func_args, max_thread): 116 | 117 | # multi threading 118 | if func_name is None or func_name == '': 119 | print('w_threading() error: func_name is empty.\n') 120 | return False 121 | if func_args is None or not isinstance(func_args, list): 122 | print('w_threading() error: func_args is wrong.\n') 123 | return False 124 | if not isinstance(max_thread, int) or max_thread is None or max_thread == '': 125 | max_thread = 1000 126 | 127 | # create thread pool 128 | thread_pool = list() 129 | for i in range(0, len(func_args)): 130 | th = threading.Thread(target=func_name, args=func_args[i]) 131 | thread_pool.append(th) 132 | 133 | # execute threads for max_thread number of threads each time 134 | thread_count = len(thread_pool) 135 | if thread_count > max_thread: 136 | i_begin = 0 137 | i_end = 0 138 | round_num = int(thread_count / max_thread) 139 | if thread_count % max_thread > 0: 140 | round_num += 1 141 | # max_thread: How many threads (test) could be executed at one time 142 | for j in range(0, round_num): 143 | i_begin = j * max_thread 144 | if j == round_num - 1: # the last round 145 | i_end = thread_count 146 | else: 147 | i_end = i_begin + max_thread 148 | # start threads 149 | for i in range(i_begin, i_end): 150 | thread_pool[i].start() 151 | # terminate threads 152 | for i in range(i_begin, i_end): 153 | thread_pool[i].join() 154 | # === thread_count <= max_thread === 155 | else: 156 | # start threads 157 | for i in range(0, thread_count): 158 | thread_pool[i].start() 159 | # terminate threads 160 | for i in range(0, thread_count): 161 | thread_pool[i].join() 162 | # ========== Run threads - End ========== 163 | #___ End of w_threading() ____ 164 | 165 | 166 | 167 | def uf_login_expect(ssh, timeout, f_out): 168 | # 169 | #_____ yes/no | password _____ 170 | # 171 | # [root@SERVER bin]# ssh npc@172.22.131.63 172 | # The authenticity of host '172.22.131.63 (172.22.131.63)' can't be established. 173 | # RSA key fingerprint is 9c:9c:e2:41:7d:83:76:80:d5:fa:97:38:da:fe:4d:23. 174 | # Are you sure you want to continue connecting (yes/no)? yes 175 | # Warning: Permanently added '172.22.131.63' (RSA) to the list of known hosts. 176 | # npc@172.22.131.63's password: 177 | # Password incorrect. 178 | # 179 | # Login failed, check your uid or pwd.Permission denied, please try again. 180 | # npc@172.22.131.63's password: 181 | # 182 | #_____ known_hosts _____ 183 | # 184 | # [root@SERVER bin]# ssh npc@172.22.131.63 185 | # @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 186 | # @ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @ 187 | # @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 188 | # IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY! 189 | # Someone could be eavesdropping on you right now (man-in-the-middle attack)! 190 | # It is also possible that the RSA host key has just been changed. 191 | # The fingerprint for the RSA key sent by the remote host is 192 | # 9c:9c:e2:41:7d:83:76:80:d5:fa:97:38:da:fe:4d:23. 193 | # Please contact your system administrator. 194 | # Add correct host key in /root/.ssh/known_hosts to get rid of this message. 195 | # Offending key in /root/.ssh/known_hosts:2330 196 | # RSA host key for 172.22.131.63 has changed and you have requested strict checking. 197 | # Host key verification failed. 198 | # [root@SERVER bin]# 199 | # 200 | cmd_out = '' 201 | try: 202 | idx = ssh.expect(['(P|p)assword: $', '\(yes/no\)\?', 'Host key verification failed'], timeout=timeout) 203 | cmd_out = '%s%s' % (ssh.before, ssh.after) 204 | if f_out is None: 205 | print(cmd_out) 206 | else: 207 | f_out.write(cmd_out) 208 | return [idx, cmd_out] 209 | except: 210 | return [-1, cmd_out] 211 | 212 | 213 | 214 | def uf_login_fix_known_hosts(cmd_out): 215 | 216 | tmp_out = cmd_out.split("\n") 217 | for tmp_row in tmp_out: 218 | # Offending key in /root/.ssh/known_hosts:2330 219 | if re.search('known_hosts:[0-9]+', tmp_row, re.IGNORECASE) is None: 220 | continue 221 | tmp_row = re.sub('^.* /', '/', tmp_row).strip() 222 | tmp_hosts, tmp_line = tmp_row.split(':') 223 | tmp_cmd = "sed -i '%sd' %s" % (tmp_line, tmp_hosts) 224 | return sys_cmd(tmp_cmd) 225 | return '' 226 | 227 | 228 | 229 | def uf_login_send_yes(ssh, sleep_time): 230 | 231 | ssh.sendline('yes') 232 | time.sleep(sleep_time) 233 | return True 234 | 235 | 236 | 237 | def uf_login_send_pwd(ssh, sleep_time, pwd): 238 | 239 | ssh.sendline(pwd) 240 | time.sleep(sleep_time) 241 | return True 242 | 243 | 244 | 245 | def uf_ssh_login(ssh, timeout, output_file, f_out, ip, port, uid, pwd, sleep_time): 246 | # 247 | # Depends on: 248 | # 249 | # uf_login_expect() 250 | # uf_login_fix_known_hosts() 251 | # uf_login_send_yes() 252 | # uf_login_send_pwd() 253 | # 254 | idx, cmd_out = uf_login_expect(ssh, timeout, f_out) # login expect 255 | # login error 256 | if idx == -1: 257 | print('[%s] %s:%s Error: uid <%s> login failed (1)' % (w_time(), ip, port, uid)) 258 | return False 259 | # wrong known_hosts 260 | if idx == 2: 261 | uf_login_fix_known_hosts(cmd_out) 262 | print('[%s] %s:%s Error: Host key was fixed and try again.' % (w_time(), ip, port)) 263 | return False 264 | # ask for yes/no 265 | if idx == 1: 266 | uf_login_send_yes(ssh, sleep_time) 267 | idx, cmd_out = uf_login_expect(ssh, timeout, f_out) # login expect 268 | # ask for password 269 | if idx == 0: 270 | uf_login_send_pwd(ssh, sleep_time, pwd) 271 | return True 272 | 273 | 274 | 275 | def uf_expect_prompt(ssh, timeout, f_out): 276 | # 277 | # Prompt: 278 | # 279 | # ^MBJ_XX_311-F-02_N7718-1# 280 | # 281 | # 282 | # ^@ 283 | # BJ-XXX-2-2960S-2016# 284 | # [~BJ_XX_320-I-10_CE5810] 285 | # 286 | # Characters: 287 | # 288 | # < > a-z A-Z 0-9 ~ @ * / _ - [] () 289 | # 290 | # Q: Why put a . next to \\n)? 291 | # A: Some devices output a prompt likes ^@ 292 | # 293 | prompt = "(\\r|\\n).?[<>a-zA-Z0-9~@\*/_\-\[\]\(\)]+(>|%|#|\\$|\]) *$" 294 | cmd_out = '' 295 | try: 296 | idx = ssh.expect([prompt, pexpect.TIMEOUT], timeout=timeout) 297 | cmd_out = '%s%s' % (ssh.before, ssh.after) 298 | if f_out is None: 299 | print(cmd_out) 300 | else: 301 | f_out.write(cmd_out) 302 | return [idx, cmd_out] 303 | except: 304 | return [-1, cmd_out] 305 | 306 | 307 | 308 | def uf_expect_sendline(ssh, timeout, f_out, sleep_time, content): 309 | 310 | ssh.sendline('') 311 | ssh.sendline(content) 312 | time.sleep(sleep_time) 313 | return uf_expect_prompt(ssh, timeout, f_out) 314 | 315 | 316 | 317 | def uf_get_vendor_model(ssh, timeout, f_out, sleep_time): 318 | 319 | vendor = '' 320 | model = '' 321 | cmd_out = '' 322 | #___ Get vendor ___ 323 | reg_vendor_search = '' 324 | reg_vendor_sub = '' 325 | # 1st, for h3c or huawei devices 326 | tmp_cmd = 'display version | in (Huawei|H3C).*(Software|uptime)' 327 | idx, cmd_out = uf_expect_sendline(ssh, timeout, f_out, sleep_time, tmp_cmd) 328 | if idx == 1: 329 | print("[%s] %s:%s Error: pexpect timed out." % (w_time(), ip, port)) 330 | return [vendor, model] 331 | if re.search('% Invalid|Unrecognized command', cmd_out) is None: 332 | if cmd_out.find('H3C ') >= 0: 333 | vendor = '%s%s' % (vendor, 'h3c') 334 | reg_vendor_search = '^h3c.*uptime' 335 | reg_vendor_sub = ' *uptime.*$' 336 | if cmd_out.find('Huawei ') >= 0: 337 | vendor = '%s%s' % (vendor, 'huawei') 338 | reg_vendor_search = '^huawei.*uptime' 339 | reg_vendor_sub = ' *uptime.*$' 340 | # 2nd, for cisco devices 341 | else: 342 | tmp_cmd = 'show version | in Cisco.*Software|cisco.*(Chassis|processor)' 343 | idx, cmd_out = uf_expect_sendline(ssh, timeout, f_out, sleep_time, tmp_cmd) 344 | if idx == 1: 345 | print("[%s] %s:%s Error: pexpect timed out." % (w_time(), ip, port)) 346 | return [vendor, model] 347 | if cmd_out.find('Cisco Nexus ') >= 0: 348 | vendor = 'cisco_nexus' 349 | reg_vendor_search = '^cisco.*chassis' 350 | reg_vendor_sub = ' *(\(|chassis).*$' 351 | elif cmd_out.find('Cisco ') >= 0: 352 | vendor = 'cisco' 353 | reg_vendor_search = '^cisco.*processor' 354 | reg_vendor_sub = ' *(\(|chassis).*$' 355 | #___ Get model ___ 356 | tmp_row = '' 357 | tmp_out = cmd_out.split('\n') 358 | for tmp_row in tmp_out: 359 | tmp_row = tmp_row.strip() 360 | if re.search(reg_vendor_search, tmp_row, re.IGNORECASE) is None: 361 | continue 362 | else: 363 | model = re.sub(reg_vendor_sub, '', tmp_row, re.IGNORECASE) 364 | model = re.sub('^(cisco nexus|cisco|h3c|huawei) *', '', model, re.IGNORECASE) 365 | break 366 | # Return 367 | return [vendor, model] 368 | 369 | 370 | 371 | def uf_set_nomore(ssh, timeout, f_out, sleep_time, vendor): 372 | 373 | cmd_nomore = '' 374 | if vendor == 'cisco': 375 | cmd_nomore = 'terminal length 0' 376 | if vendor == 'cisco_nexus': 377 | cmd_nomore = 'terminal length 0' 378 | if vendor == 'h3c': 379 | cmd_nomore = 'screen-length disable' 380 | if vendor == 'huawei': 381 | cmd_nomore = 'screen-length 0 temp' 382 | return uf_expect_sendline(ssh, timeout, f_out, sleep_time, cmd_nomore) 383 | 384 | 385 | 386 | def uf_save(ssh, timeout, f_out, sleep_time, vendor): 387 | 388 | cmd_save = '' 389 | if vendor == 'cisco': 390 | cmd_save = 'end\rcopy run start\r' 391 | if vendor == 'cisco_nexus': 392 | cmd_save = 'copy run start' 393 | if vendor == 'h3c': 394 | cmd_save = 'save force' 395 | if vendor == 'huawei': 396 | cmd_save = 'return\rsave\r\y\r' 397 | return uf_expect_sendline(ssh, timeout, f_out, sleep_time, cmd_save) 398 | 399 | 400 | 401 | def uf_logout(ssh, timeout, f_out, sleep_time, vendor): 402 | 403 | cmd_logout = '' 404 | if vendor == 'cisco': 405 | cmd_logout = 'end\rexit' 406 | if vendor == 'cisco_nexus': 407 | cmd_logout = 'end\rexit' 408 | if vendor == 'h3c': 409 | cmd_logout = 'quit\rquit\r' 410 | if vendor == 'huawei': 411 | cmd_logout = 'quit\rquit\r' 412 | return uf_expect_sendline(ssh, timeout, f_out, sleep_time, cmd_logout) 413 | 414 | 415 | 416 | def uf_get_l2_uplink(ssh, timeout, f_out, sleep_time, vendor): 417 | 418 | l2_uplink = '' 419 | gw_ip = '' 420 | gw_mac = '' 421 | cmd_get_gw_ip = '' 422 | cmd_get_gw_mac = '' 423 | cmd_get_gw_uplink = '' 424 | reg_get_gw_ip_search = '\s?[1-9]\d{0,2}(\.\d{1,3}){3}\s?' # match ip but not 0.0.0.0 425 | reg_get_gw_mac_search = '\s?([\da-f]{4}[\.-]){2}[\da-f]{4}\s?' 426 | reg_get_gw_uplink_sub = '' 427 | 428 | # Init cmd 429 | if vendor == 'cisco': 430 | cmd_get_gw_ip = 'show ip default-gateway' 431 | cmd_get_gw_mac = 'show ip arp _IP_' 432 | cmd_get_gw_uplink = 'show mac address-table address _MAC_' 433 | if vendor == 'cisco_nexus': 434 | cmd_get_gw_ip = 'show ip route 0.0.0.0/0' 435 | cmd_get_gw_mac = 'show ip arp _IP_' 436 | cmd_get_gw_uplink = 'show mac address-table address _MAC_' 437 | if vendor == 'h3c': 438 | cmd_get_gw_ip = 'display ip routing-table 0.0.0.0 0' 439 | cmd_get_gw_mac = 'disp arp _IP_' 440 | cmd_get_gw_uplink = 'display mac-address _MAC_' 441 | if vendor == 'huawei': 442 | cmd_get_gw_ip = 'display ip routing-table 0.0.0.0 0' 443 | cmd_get_gw_mac = 'disp arp dynamic | include _IP_' 444 | cmd_get_gw_uplink = 'display mac-address _MAC_' 445 | 446 | # Get gateway IP 447 | idx, cmd_out = uf_expect_sendline(ssh, timeout, f_out, sleep_time, cmd_get_gw_ip) 448 | if idx == 1: 449 | print("[%s] %s:%s Error: pexpect timed out." % (w_time(), ip, port)) 450 | return l2_uplink 451 | if idx == -1: 452 | return l2_uplink 453 | tmp_row = '' 454 | tmp_out = cmd_out.split('\n') 455 | for tmp_row in tmp_out: 456 | tmp_row = tmp_row.strip() 457 | tmp_re = re.search(reg_get_gw_ip_search, tmp_row, re.IGNORECASE) 458 | if tmp_re is None: 459 | continue 460 | else: 461 | gw_ip = tmp_re.group(0).strip() 462 | break 463 | if gw_ip == '': 464 | return l2_uplink 465 | 466 | # Get gateway MAC 467 | cmd_get_gw_mac = re.sub('_IP_', gw_ip, cmd_get_gw_mac) 468 | idx, cmd_out = uf_expect_sendline(ssh, timeout, f_out, sleep_time, cmd_get_gw_mac) 469 | if idx == 1: 470 | print("[%s] %s:%s Error: pexpect timed out." % (w_time(), ip, port)) 471 | return l2_uplink 472 | if idx == -1: 473 | return l2_uplink 474 | tmp_row = '' 475 | tmp_out = cmd_out.split('\n') 476 | for tmp_row in tmp_out: 477 | tmp_row = tmp_row.strip() 478 | tmp_re = re.search(reg_get_gw_mac_search, tmp_row, re.IGNORECASE) 479 | if tmp_re is None: 480 | continue 481 | else: 482 | gw_mac = tmp_re.group(0).strip() 483 | break 484 | if gw_mac == '': 485 | return l2_uplink 486 | 487 | # Get gateway uplink 488 | cmd_get_gw_uplink = re.sub('_MAC_', gw_mac, cmd_get_gw_uplink) 489 | idx, cmd_out = uf_expect_sendline(ssh, timeout, f_out, sleep_time, cmd_get_gw_uplink) 490 | if idx == 1: 491 | print("[%s] %s:%s Error: pexpect timed out." % (w_time(), ip, port)) 492 | return l2_uplink 493 | if idx == -1: 494 | return l2_uplink 495 | tmp_row = '' 496 | tmp_out = cmd_out.split('\n') 497 | for tmp_row in tmp_out: 498 | tmp_row = tmp_row.strip() 499 | if re.search(cmd_get_gw_uplink, tmp_row) != None: 500 | continue 501 | tmp_re = re.search(reg_get_gw_mac_search, tmp_row, re.IGNORECASE) 502 | if tmp_re is None: 503 | continue 504 | else: 505 | l2_uplink = re.sub('^.*%s' % (gw_mac), '', tmp_row).strip() 506 | break 507 | 508 | if l2_uplink != '': 509 | l2_uplink_list = l2_uplink.split() 510 | if vendor == 'h3c': 511 | l2_uplink = l2_uplink_list[2] 512 | elif vendor == 'huawei': 513 | l2_uplink = l2_uplink_list[1] 514 | elif vendor == 'cisco_nexus': 515 | l2_uplink = l2_uplink_list[4] 516 | elif vendor == 'cisco': 517 | l2_uplink = l2_uplink_list[1] 518 | else: 519 | l2_uplink = '' 520 | 521 | return l2_uplink 522 | 523 | 524 | 525 | def w_main(ip, port, uid, pwd, cmd, cmd_prefix, cmd_interval, log_dir, flt_timeout, save, l2_sw): 526 | #_________ start of arguments init _________ 527 | # arg: ip 528 | if not isinstance(ip, str) or ip.strip() == '': 529 | print('[%s] Error: incorrect IP address <%s>' % (w_time(), ip)) 530 | return False 531 | # arg: port 532 | if not isinstance(port, str) or port.strip() == '': 533 | port = '22' 534 | # arg: uid 535 | if not isinstance(uid, str) or uid.strip() == '': 536 | print('[%s] %s:%s Error: incorrect UID' % (w_time(), ip, port)) 537 | return False 538 | # arg: pwd 539 | if not isinstance(pwd, str) or pwd.strip() == '': 540 | print('[%s] %s:%s Error: incorrect PWD' % (w_time(), ip, port)) 541 | return False 542 | # arg: cmd, cmd_prefix 543 | cmd_list = list() 544 | if not isinstance(cmd, str) or cmd is None or cmd.strip() == '': 545 | if not isinstance(cmd_prefix, str) or cmd_prefix is None or cmd_prefix.strip() == '': 546 | print('[%s] %s:%s Warning: neither --cmd nor --cmd_prefix was specified.\n' % (w_time(), ip, port)) 547 | else: 548 | cmd_list = None 549 | # Then go to place where vendor was alreay identified. 550 | else: 551 | cmd_list = cmd.split(';') 552 | # arg: cmd_interval, default 0.5 553 | if isinstance(cmd_interval, float): 554 | sleep_time = cmd_interval 555 | else: 556 | sleep_time = 0.5 557 | if sleep_time <= 0: 558 | sleep_time = 0.5 559 | # arg: log_dir 560 | f_out = None 561 | if not isinstance(log_dir, str) or log_dir is None or log_dir.strip() == '': 562 | output_file = '' 563 | else: 564 | output_file = '%s/%s' % (log_dir, ip) 565 | output_path = os.path.dirname(output_file) 566 | if not os.path.exists(output_path): 567 | try: 568 | sys_cmd('mkdir -p %s' % (output_path)) 569 | except: 570 | print('[%s] %s:%s Error: mkdir %s failed!' % (w_time(), ip, port, output_path)) 571 | return False 572 | if os.path.exists(output_path): 573 | try: 574 | f_out = open(output_file, 'w') 575 | except: 576 | print('[%s] %s:%s Error: file %s is failed to open.' % (w_time(), ip, port, output_file)) 577 | return False 578 | else: 579 | output_file = '' 580 | if output_file == '': 581 | f_out = None 582 | # arg: timeout 583 | if isinstance(flt_timeout, float): 584 | timeout = flt_timeout 585 | else: 586 | timeout = 10 587 | # arg: save 588 | if not isinstance(save, str) or save.strip() == '': 589 | save = 'no' 590 | # arg: l2_sw 591 | if not isinstance(l2_sw, str) or l2_sw.strip() == '': 592 | l2_sw = 'no' 593 | #_________ end of arguments init _________ 594 | 595 | # Login - ssh 596 | try: 597 | print('[%s] ssh -p %s -l %s %s' % (w_time(), port, uid, ip)) 598 | ssh = pexpect.spawn('ssh -p %s -l %s %s' % (port, uid, ip)) 599 | time.sleep(sleep_time) 600 | except: 601 | print('[%s] %s:%s Error: ssh failed' % (w_time(), ip, port)) 602 | return False 603 | if not uf_ssh_login(ssh, timeout, output_file, f_out, ip, port, uid, pwd, sleep_time): 604 | return False 605 | idx, cmd_out = uf_expect_prompt(ssh, timeout, f_out) 606 | if idx != 0: 607 | return False 608 | 609 | # Get vendor and model 610 | vendor, model = uf_get_vendor_model(ssh, timeout, f_out, sleep_time) 611 | if vendor == '': 612 | print("[%s] %s:%s Error: can not get device vendor." % (w_time(), ip, port)) 613 | return False 614 | if model == '': 615 | print("[%s] %s:%s Error: can not get device type." % (w_time(), ip, port)) 616 | return False 617 | 618 | # Set no-more 619 | idx, cmd_out = uf_set_nomore(ssh, timeout, f_out, sleep_time, vendor) 620 | if idx == 1: 621 | print("[%s] %s:%s Error: pexpect timed out." % (w_time(), ip, port)) 622 | return False 623 | 624 | # Get l2-uplink 625 | l2_uplink = '' 626 | if l2_sw == 'yes': 627 | l2_uplink = uf_get_l2_uplink(ssh, timeout, f_out, sleep_time, vendor) 628 | 629 | # if cmd_prefix was prefered. 630 | if cmd_list is None: 631 | cmd_prefix = '%s.cmd.%s' % (cmd_prefix, vendor) 632 | if not os.path.exists(cmd_prefix): 633 | print('[%s] %s:%s Error: %s does not exist.\n' % (w_time(), ip, port, cmd_prefix)) 634 | return False 635 | f_cmd = open(cmd_prefix) 636 | cmd_list = f_cmd.readlines() 637 | f_cmd.close() 638 | 639 | # Execute the command 640 | cmd_idx = 0 641 | cmd_all = '' 642 | for i in range(0, len(cmd_list)): 643 | cmd_idx = i + 2 644 | cmd_line = cmd_list[i].strip() 645 | cmd_all = '%s\n%s) %s' % (cmd_all, str(i+1).rjust(5), cmd_line) 646 | time.sleep(sleep_time) 647 | try: 648 | idx, cmd_out = uf_expect_sendline(ssh, timeout, f_out, sleep_time, cmd_line) 649 | if idx == 1: 650 | print("[%s] %s:%s Error: pexpect timed out." % (w_time(), ip, port)) 651 | return False 652 | except: 653 | print('\n[%s] %s:%s Error: command %s is failed to be executed.' % (w_time(), ip, port, cmd_list[i].strip())) 654 | 655 | # Save config 656 | if save == 'yes': 657 | if timeout < 10: 658 | timeout = 10 659 | idx, cmd_out = uf_save(ssh, timeout, f_out, sleep_time, vendor) 660 | cmd_all = '%s\n%s) %s' % (cmd_all, str(cmd_idx).rjust(5), '[Save Config]') 661 | if idx == 1: 662 | print("[%s] %s:%s Error: save config timed out." % (w_time(), ip, port)) 663 | return False 664 | if idx == -1: 665 | print("[%s] %s:%s Error: save config failed." % (w_time(), ip, port)) 666 | 667 | # Logout 668 | uf_logout(ssh, timeout, f_out, sleep_time, vendor) 669 | ssh.close() 670 | the_end = ''' 671 | 672 | ======================================= 673 | Device: %s 674 | Vendor: %s 675 | Model: %s 676 | L2_Uplink: %s 677 | Commands: %s 678 | 679 | \r\n''' % (ip, vendor, model, l2_uplink, cmd_all) 680 | if f_out is None: 681 | print(the_end) 682 | else: 683 | f_out.write(the_end) 684 | f_out.close() 685 | 686 | return True 687 | 688 | #___ End of w_main() ___ 689 | 690 | 691 | 692 | if __name__ == '__main__': 693 | 694 | host = '' 695 | uid = '' 696 | pwd = '' 697 | cmd = '' 698 | host_file = '' 699 | cmd_prefix = '' 700 | cmd_interval = 0.5 701 | log_dir = '' 702 | thread = 1000 703 | timeout = 10 704 | save = 'no' 705 | l2_sw = 'no' 706 | 707 | try: 708 | opts, args = getopt.getopt(sys.argv[1:], "hp", ['uid=','pwd=','host=','host_file=','cmd=','cmd_prefix=','cmd_interval=','log_dir=','thread=','timeout=','save','l2_sw']) 709 | except: 710 | print("Wrong options!") 711 | print("Try '-h' to get more information.") 712 | sys.exit() 713 | if len(opts) == 0: 714 | help_and_exit() 715 | 716 | for op, value in opts: 717 | 718 | if op == '-h': 719 | help_and_exit() 720 | 721 | elif op == '--uid': 722 | uid = value 723 | 724 | elif op == '--pwd': 725 | pwd = value 726 | 727 | elif op == '-p': 728 | pwd = getpass.getpass() 729 | 730 | elif op == '--host': 731 | host = value 732 | 733 | elif op == '--host_file': 734 | host_file = value 735 | 736 | elif op == '--cmd': 737 | cmd = value 738 | 739 | elif op == '--cmd_prefix': 740 | cmd_prefix = value 741 | 742 | elif op == '--cmd_interval': 743 | try: 744 | cmd_interval = float(value) 745 | except ValueError: 746 | print('Wrong option: --cmd_interval only accepts a float value.') 747 | print("Try '-h' to get more information.") 748 | sys.exit(1) 749 | 750 | elif op == '--log_dir': 751 | log_dir = value 752 | 753 | elif op == '--thread': 754 | if value.isalnum(): 755 | thread = int(value) 756 | 757 | elif op == '--timeout': 758 | try: 759 | timeout = float(value) 760 | except ValueError: 761 | print('Wrong option: --timeout only accepts a float value.') 762 | print("Try '-h' to get more information.") 763 | sys.exit(1) 764 | 765 | elif op == '--save': 766 | save = 'yes' 767 | 768 | elif op == '--l2_sw': 769 | l2_sw = 'yes' 770 | 771 | else: 772 | help_and_exit() 773 | 774 | #__________ multi-thread __________ 775 | 776 | # func_name 777 | func_name = w_main 778 | 779 | # func_args 780 | func_args = list() 781 | 782 | host_list = list() 783 | print('') 784 | if host != '': 785 | host_list = host.split(',') 786 | elif host_file != '': 787 | if not os.path.exists(host_file): 788 | print('%s does not exist, please specify host with --host or --host_file.\n' % (host_file)) 789 | help_and_exit() 790 | 791 | f_host = open(host_file) 792 | host_list = f_host.readlines() 793 | f_host.close() 794 | else: 795 | print('Please specify host with --host or --host_file.\n') 796 | help_and_exit() 797 | 798 | host_len = len(host_list) 799 | for i in range(0, host_len): 800 | if host_list[i].find(':') >= 0: 801 | ip, port = host_list[i].split(':') 802 | ip = ip.strip() 803 | port = port.strip() 804 | else: 805 | ip = host_list[i].strip() 806 | port = '' 807 | func_args.append([ip, port, uid, pwd, cmd, cmd_prefix, cmd_interval, log_dir, timeout, save, l2_sw]) 808 | 809 | # Start multi-threading 810 | w_threading(func_name, func_args, thread) 811 | 812 | # exit 813 | print('') 814 | sys.exit() 815 | 816 | 817 | --------------------------------------------------------------------------------