1#!/usr/bin/env python
2
3""" This runs a sequence of commands on a remote host using SSH. It runs a
4simple system checks such as uptime and free to monitor the state of the remote
5host.
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
12Example:
13    This will print information about the given host:
14        ./monitor.py -s www.example.com -u mylogin -p mypassword
15
16It 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
26import os, sys, time, re, getopt, getpass
27import traceback
28import pexpect
29
30#
31# Some constants.
32#
33COMMAND_PROMPT = '[#$] ' ### This is way too simple for industrial use -- we will change is ASAP.
34TERMINAL_PROMPT = '(?i)terminal type\?'
35TERMINAL_TYPE = 'vt100'
36# This is the prompt we get if SSH does not have the remote host's public key stored in the cache.
37SSH_NEWKEY = '(?i)are you sure you want to continue connecting'
38
39def exit_with_usage():
40
41    print globals()['__doc__']
42    os._exit(1)
43
44def 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
200if __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