Home | History | Annotate | Download | only in examples
      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 
     26 import os, sys, time, re, getopt, getpass
     27 import traceback
     28 import pexpect
     29 
     30 #
     31 # Some constants.
     32 #
     33 COMMAND_PROMPT = '[#$] ' ### This is way too simple for industrial use -- we will change is ASAP.
     34 TERMINAL_PROMPT = '(?i)terminal type\?'
     35 TERMINAL_TYPE = 'vt100'
     36 # This is the prompt we get if SSH does not have the remote host's public key stored in the cache.
     37 SSH_NEWKEY = '(?i)are you sure you want to continue connecting'
     38 
     39 def exit_with_usage():
     40 
     41     print globals()['__doc__']
     42     os._exit(1)
     43 
     44 def main():
     45 
     46     global COMMAND_PROMPT, TERMINAL_PROMPT, TERMINAL_TYPE, SSH_NEWKEY
     47     ######################################################################
     48     ## Parse the options, arguments, get ready, etc.
     49     ######################################################################
     50     try:
     51         optlist, args = getopt.getopt(sys.argv[1:], 'h?s:u:p:', ['help','h','?'])
     52     except Exception, e:
     53         print str(e)
     54         exit_with_usage()
     55     options = dict(optlist)
     56     if len(args) > 1:
     57         exit_with_usage()
     58 
     59     if [elem for elem in options if elem in ['-h','--h','-?','--?','--help']]:
     60         print "Help:"
     61         exit_with_usage()
     62 
     63     if '-s' in options:
     64         host = options['-s']
     65     else:
     66         host = raw_input('hostname: ')
     67     if '-u' in options:
     68         user = options['-u']
     69     else:
     70         user = raw_input('username: ')
     71     if '-p' in options:
     72         password = options['-p']
     73     else:
     74         password = getpass.getpass('password: ')
     75 
     76     #
     77     # Login via SSH
     78     #
     79     child = pexpect.spawn('ssh -l %s %s'%(user, host))
     80     i = child.expect([pexpect.TIMEOUT, SSH_NEWKEY, COMMAND_PROMPT, '(?i)password'])
     81     if i == 0: # Timeout
     82         print 'ERROR! could not login with SSH. Here is what SSH said:'
     83         print child.before, child.after
     84         print str(child)
     85         sys.exit (1)
     86     if i == 1: # In this case SSH does not have the public key cached.
     87         child.sendline ('yes')
     88         child.expect ('(?i)password')
     89     if i == 2:
     90         # This may happen if a public key was setup to automatically login.
     91         # But beware, the COMMAND_PROMPT at this point is very trivial and
     92         # could be fooled by some output in the MOTD or login message.
     93         pass
     94     if i == 3:
     95         child.sendline(password)
     96         # Now we are either at the command prompt or
     97         # the login process is asking for our terminal type.
     98         i = child.expect ([COMMAND_PROMPT, TERMINAL_PROMPT])
     99         if i == 1:
    100             child.sendline (TERMINAL_TYPE)
    101             child.expect (COMMAND_PROMPT)
    102     #
    103     # Set command prompt to something more unique.
    104     #
    105     COMMAND_PROMPT = "\[PEXPECT\]\$ "
    106     child.sendline ("PS1='[PEXPECT]\$ '") # In case of sh-style
    107     i = child.expect ([pexpect.TIMEOUT, COMMAND_PROMPT], timeout=10)
    108     if i == 0:
    109         print "# Couldn't set sh-style prompt -- trying csh-style."
    110         child.sendline ("set prompt='[PEXPECT]\$ '")
    111         i = child.expect ([pexpect.TIMEOUT, COMMAND_PROMPT], timeout=10)
    112         if i == 0:
    113             print "Failed to set command prompt using sh or csh style."
    114             print "Response was:"
    115             print child.before
    116             sys.exit (1)
    117 
    118     # Now we should be at the command prompt and ready to run some commands.
    119     print '---------------------------------------'
    120     print 'Report of commands run on remote host.'
    121     print '---------------------------------------'
    122 
    123     # Run uname.
    124     child.sendline ('uname -a')
    125     child.expect (COMMAND_PROMPT)
    126     print child.before
    127     if 'linux' in child.before.lower():
    128         LINUX_MODE = 1
    129     else:
    130         LINUX_MODE = 0
    131 
    132     # Run and parse 'uptime'.
    133     child.sendline ('uptime')
    134     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])')
    135     duration, users, av1, av5, av15 = child.match.groups()
    136     days = '0'
    137     hours = '0'
    138     mins = '0'
    139     if 'day' in duration:
    140         child.match = re.search('([0-9]+)\s+day',duration)
    141         days = str(int(child.match.group(1)))
    142     if ':' in duration:
    143         child.match = re.search('([0-9]+):([0-9]+)',duration)
    144         hours = str(int(child.match.group(1)))
    145         mins = str(int(child.match.group(2)))
    146     if 'min' in duration:
    147         child.match = re.search('([0-9]+)\s+min',duration)
    148         mins = str(int(child.match.group(1)))
    149     print
    150     print 'Uptime: %s days, %s users, %s (1 min), %s (5 min), %s (15 min)' % (
    151         duration, users, av1, av5, av15)
    152     child.expect (COMMAND_PROMPT)
    153 
    154     # Run iostat.
    155     child.sendline ('iostat')
    156     child.expect (COMMAND_PROMPT)
    157     print child.before
    158 
    159     # Run vmstat.
    160     child.sendline ('vmstat')
    161     child.expect (COMMAND_PROMPT)
    162     print child.before
    163 
    164     # Run free.
    165     if LINUX_MODE:
    166         child.sendline ('free') # Linux systems only.
    167         child.expect (COMMAND_PROMPT)
    168         print child.before
    169 
    170     # Run df.
    171     child.sendline ('df')
    172     child.expect (COMMAND_PROMPT)
    173     print child.before
    174     
    175     # Run lsof.
    176     child.sendline ('lsof')
    177     child.expect (COMMAND_PROMPT)
    178     print child.before
    179 
    180 #    # Run netstat
    181 #    child.sendline ('netstat')
    182 #    child.expect (COMMAND_PROMPT)
    183 #    print child.before
    184 
    185 #    # Run MySQL show status.
    186 #    child.sendline ('mysql -p -e "SHOW STATUS;"')
    187 #    child.expect (PASSWORD_PROMPT_MYSQL)
    188 #    child.sendline (password_mysql)
    189 #    child.expect (COMMAND_PROMPT)
    190 #    print
    191 #    print child.before
    192 
    193     # Now exit the remote host.
    194     child.sendline ('exit')
    195     index = child.expect([pexpect.EOF, "(?i)there are stopped jobs"])
    196     if index==1:
    197         child.sendline("exit")
    198         child.expect(EOF)
    199 
    200 if __name__ == "__main__":
    201 
    202     try:
    203         main()
    204     except Exception, e:
    205         print str(e)
    206         traceback.print_exc()
    207         os._exit(1)
    208 
    209