OLD | NEW |
(Empty) | |
| 1 #!/usr/bin/env python |
| 2 |
| 3 ''' This runs a sequence of commands on a remote host using SSH. It runs a |
| 4 simple system checks such as uptime and free to monitor the state of the remote |
| 5 host. |
| 6 |
| 7 ./monitor.py [-s server_hostname] [-u username] [-p password] |
| 8 -s : hostname of the remote server to login to. |
| 9 -u : username to user for login. |
| 10 -p : Password to user for login. |
| 11 |
| 12 Example: |
| 13 This will print information about the given host: |
| 14 ./monitor.py -s www.example.com -u mylogin -p mypassword |
| 15 |
| 16 It works like this: |
| 17 Login via SSH (This is the hardest part). |
| 18 Run and parse 'uptime'. |
| 19 Run 'iostat'. |
| 20 Run 'vmstat'. |
| 21 Run 'netstat' |
| 22 Run 'free'. |
| 23 Exit the remote host. |
| 24 |
| 25 PEXPECT LICENSE |
| 26 |
| 27 This license is approved by the OSI and FSF as GPL-compatible. |
| 28 http://opensource.org/licenses/isc-license.txt |
| 29 |
| 30 Copyright (c) 2012, Noah Spurrier <noah@noah.org> |
| 31 PERMISSION TO USE, COPY, MODIFY, AND/OR DISTRIBUTE THIS SOFTWARE FOR ANY |
| 32 PURPOSE WITH OR WITHOUT FEE IS HEREBY GRANTED, PROVIDED THAT THE ABOVE |
| 33 COPYRIGHT NOTICE AND THIS PERMISSION NOTICE APPEAR IN ALL COPIES. |
| 34 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| 35 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| 36 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| 37 ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| 38 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| 39 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
| 40 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| 41 |
| 42 ''' |
| 43 |
| 44 from __future__ import print_function |
| 45 |
| 46 from __future__ import absolute_import |
| 47 |
| 48 import os, sys, re, getopt, getpass |
| 49 import pexpect |
| 50 |
| 51 |
| 52 try: |
| 53 raw_input |
| 54 except NameError: |
| 55 raw_input = input |
| 56 |
| 57 |
| 58 # |
| 59 # Some constants. |
| 60 # |
| 61 COMMAND_PROMPT = '[#$] ' ### This is way too simple for industrial use -- we wil
l change is ASAP. |
| 62 TERMINAL_PROMPT = '(?i)terminal type\?' |
| 63 TERMINAL_TYPE = 'vt100' |
| 64 # This is the prompt we get if SSH does not have the remote host's public key st
ored in the cache. |
| 65 SSH_NEWKEY = '(?i)are you sure you want to continue connecting' |
| 66 |
| 67 def exit_with_usage(): |
| 68 |
| 69 print(globals()['__doc__']) |
| 70 os._exit(1) |
| 71 |
| 72 def main(): |
| 73 |
| 74 global COMMAND_PROMPT, TERMINAL_PROMPT, TERMINAL_TYPE, SSH_NEWKEY |
| 75 ###################################################################### |
| 76 ## Parse the options, arguments, get ready, etc. |
| 77 ###################################################################### |
| 78 try: |
| 79 optlist, args = getopt.getopt(sys.argv[1:], 'h?s:u:p:', ['help','h','?']
) |
| 80 except Exception as e: |
| 81 print(str(e)) |
| 82 exit_with_usage() |
| 83 options = dict(optlist) |
| 84 if len(args) > 1: |
| 85 exit_with_usage() |
| 86 |
| 87 if [elem for elem in options if elem in ['-h','--h','-?','--?','--help']]: |
| 88 print("Help:") |
| 89 exit_with_usage() |
| 90 |
| 91 if '-s' in options: |
| 92 host = options['-s'] |
| 93 else: |
| 94 host = raw_input('hostname: ') |
| 95 if '-u' in options: |
| 96 user = options['-u'] |
| 97 else: |
| 98 user = raw_input('username: ') |
| 99 if '-p' in options: |
| 100 password = options['-p'] |
| 101 else: |
| 102 password = getpass.getpass('password: ') |
| 103 |
| 104 # |
| 105 # Login via SSH |
| 106 # |
| 107 child = pexpect.spawn('ssh -l %s %s'%(user, host)) |
| 108 i = child.expect([pexpect.TIMEOUT, SSH_NEWKEY, COMMAND_PROMPT, '(?i)password
']) |
| 109 if i == 0: # Timeout |
| 110 print('ERROR! could not login with SSH. Here is what SSH said:') |
| 111 print(child.before, child.after) |
| 112 print(str(child)) |
| 113 sys.exit (1) |
| 114 if i == 1: # In this case SSH does not have the public key cached. |
| 115 child.sendline ('yes') |
| 116 child.expect ('(?i)password') |
| 117 if i == 2: |
| 118 # This may happen if a public key was setup to automatically login. |
| 119 # But beware, the COMMAND_PROMPT at this point is very trivial and |
| 120 # could be fooled by some output in the MOTD or login message. |
| 121 pass |
| 122 if i == 3: |
| 123 child.sendline(password) |
| 124 # Now we are either at the command prompt or |
| 125 # the login process is asking for our terminal type. |
| 126 i = child.expect ([COMMAND_PROMPT, TERMINAL_PROMPT]) |
| 127 if i == 1: |
| 128 child.sendline (TERMINAL_TYPE) |
| 129 child.expect (COMMAND_PROMPT) |
| 130 # |
| 131 # Set command prompt to something more unique. |
| 132 # |
| 133 COMMAND_PROMPT = "\[PEXPECT\]\$ " |
| 134 child.sendline ("PS1='[PEXPECT]\$ '") # In case of sh-style |
| 135 i = child.expect ([pexpect.TIMEOUT, COMMAND_PROMPT], timeout=10) |
| 136 if i == 0: |
| 137 print("# Couldn't set sh-style prompt -- trying csh-style.") |
| 138 child.sendline ("set prompt='[PEXPECT]\$ '") |
| 139 i = child.expect ([pexpect.TIMEOUT, COMMAND_PROMPT], timeout=10) |
| 140 if i == 0: |
| 141 print("Failed to set command prompt using sh or csh style.") |
| 142 print("Response was:") |
| 143 print(child.before) |
| 144 sys.exit (1) |
| 145 |
| 146 # Now we should be at the command prompt and ready to run some commands. |
| 147 print('---------------------------------------') |
| 148 print('Report of commands run on remote host.') |
| 149 print('---------------------------------------') |
| 150 |
| 151 # Run uname. |
| 152 child.sendline ('uname -a') |
| 153 child.expect (COMMAND_PROMPT) |
| 154 print(child.before) |
| 155 if 'linux' in child.before.lower(): |
| 156 LINUX_MODE = 1 |
| 157 else: |
| 158 LINUX_MODE = 0 |
| 159 |
| 160 # Run and parse 'uptime'. |
| 161 child.sendline ('uptime') |
| 162 child.expect('up\s+(.*?),\s+([0-9]+) users?,\s+load averages?: ([0-9]+\.[0-9
][0-9]),?\s+([0-9]+\.[0-9][0-9]),?\s+([0-9]+\.[0-9][0-9])') |
| 163 duration, users, av1, av5, av15 = child.match.groups() |
| 164 days = '0' |
| 165 hours = '0' |
| 166 mins = '0' |
| 167 if 'day' in duration: |
| 168 child.match = re.search('([0-9]+)\s+day',duration) |
| 169 days = str(int(child.match.group(1))) |
| 170 if ':' in duration: |
| 171 child.match = re.search('([0-9]+):([0-9]+)',duration) |
| 172 hours = str(int(child.match.group(1))) |
| 173 mins = str(int(child.match.group(2))) |
| 174 if 'min' in duration: |
| 175 child.match = re.search('([0-9]+)\s+min',duration) |
| 176 mins = str(int(child.match.group(1))) |
| 177 print() |
| 178 print('Uptime: %s days, %s users, %s (1 min), %s (5 min), %s (15 min)' % ( |
| 179 duration, users, av1, av5, av15)) |
| 180 child.expect (COMMAND_PROMPT) |
| 181 |
| 182 # Run iostat. |
| 183 child.sendline ('iostat') |
| 184 child.expect (COMMAND_PROMPT) |
| 185 print(child.before) |
| 186 |
| 187 # Run vmstat. |
| 188 child.sendline ('vmstat') |
| 189 child.expect (COMMAND_PROMPT) |
| 190 print(child.before) |
| 191 |
| 192 # Run free. |
| 193 if LINUX_MODE: |
| 194 child.sendline ('free') # Linux systems only. |
| 195 child.expect (COMMAND_PROMPT) |
| 196 print(child.before) |
| 197 |
| 198 # Run df. |
| 199 child.sendline ('df') |
| 200 child.expect (COMMAND_PROMPT) |
| 201 print(child.before) |
| 202 |
| 203 # Run lsof. |
| 204 child.sendline ('lsof') |
| 205 child.expect (COMMAND_PROMPT) |
| 206 print(child.before) |
| 207 |
| 208 # # Run netstat |
| 209 # child.sendline ('netstat') |
| 210 # child.expect (COMMAND_PROMPT) |
| 211 # print child.before |
| 212 |
| 213 # # Run MySQL show status. |
| 214 # child.sendline ('mysql -p -e "SHOW STATUS;"') |
| 215 # child.expect (PASSWORD_PROMPT_MYSQL) |
| 216 # child.sendline (password_mysql) |
| 217 # child.expect (COMMAND_PROMPT) |
| 218 # print |
| 219 # print child.before |
| 220 |
| 221 # Now exit the remote host. |
| 222 child.sendline ('exit') |
| 223 index = child.expect([pexpect.EOF, "(?i)there are stopped jobs"]) |
| 224 if index==1: |
| 225 child.sendline("exit") |
| 226 child.expect(EOF) |
| 227 |
| 228 if __name__ == "__main__": |
| 229 main() |
OLD | NEW |