#!/usr/bin/env python
"""
Run a command and notify by email if it fails.

1. Run command and arguments.
2. If return status is non-zero, send results to the provided
   email address.
"""
import optparse
from smtplib import SMTP
import socket
from subprocess import Popen, PIPE

class OptionParser(optparse.OptionParser):
    def check_required (self, opt):
        option = self.get_option(opt)
        # Assumes the option's 'default' is set to None!
        if getattr(self.values, option.dest) is None:
            self.error("%s option not supplied" % option)

def sendMail(options, cmd, body="", status=""):
    """Try to send mail.
    Will raise socket or SMTP exceptions if server cannot be found
    or refuses the connection.
    """
    sbj = options.subject
    if sbj.find('%prog') >= 0:
        sbj = sbj.replace('%prog', cmd)
    if body.find('%prog') >= 0:
        body = body.replace('%prog', cmd)
    if sbj.find('%host') >= 0:
        fqdn = socket.getfqdn()
        try:
            hostname = socket.gethostbyname(fqdn)
        except (socket.herror, socket.gaierror):
            hostname = 'Unknown host'
        host = "%s (%s)" % (hostname, fqdn)
        sbj = sbj.replace('%host', host)
    if status and sbj.find('%status') >= 0:
        sbj = sbj.replace('%status', status)
    msg = "Subject: " + sbj + "\r\n" + body
    if options.test:
        print "Connect to %s:%d" % (options.server_host, options.server_port)
        print "To: %s\nFrom: %s" % (options.to_addr, options.from_addr)
        print msg
    else:
        s = SMTP(options.server_host, options.server_port)
        s.sendmail(options.from_addr, options.to_addr, msg)

def main():
    usage = "%prog [options] command args.."
    desc = __doc__.split('\n')[1]
    parser = OptionParser(usage=usage, version="%prog 0.1", 
                          description=desc)
    default_subject = "Error on %host from '%prog'"
    parser.add_option('-b', '--subject', dest="subject", default=default_subject,
                      help="Email subject (default=%default)")
    parser.add_option('-f', '--from', dest="from_addr", metavar="user@host",
                      default=None, help="Set 'From:' to user@host (required)")
    parser.add_option('-g', '--nagios', dest="nagios", action="store_true",
                      default=False, help="Nagios mode. Put first line of "
                      "standard output in '%status'. Add this to default "
                      "subject line (default=No)")
    parser.add_option('-n', '--test', dest="test", action="store_true",
                      default=False, help="Print to stdout instead of "
                      "sending email")
    parser.add_option('-p', '--port', dest="server_port", type="int",
                      default=25, help="SMTP server port (default=%default)")
    parser.add_option('-s', '--server', dest="server_host", metavar="HOST",
                       default='localhost', help="SMTP server host "
                      "(default=%default)")     
    parser.add_option('-t', '--to', dest="to_addr", metavar="user@host",
                      default=None, help="Set 'To:' to user@host (required)")
    options, args= parser.parse_args()
    if not args:
        parser.error("Command not supplied")
    for req_arg in 'from', 'to':
        parser.check_required('--' + req_arg)
    # split up args, to allow for quoted parts with spaces
    args_tmp = [ ]
    for arg in args:
        args_tmp.extend(arg.split())
    args = args_tmp
    cmd = ' '.join(args)
    if options.nagios:
        # if the user didn't change the subject, add nagios-mode status
        if options.subject is default_subject:
            options.subject += ": %status"
    status = ''
    try:
        p = Popen(args, stdout=PIPE, stderr=PIPE)
        p_out, p_err = p.communicate()
        if options.nagios:
            status = p_out.split('\n')[0]
    except OSError, E:
        sendMail(options, cmd, body="Cannot run '%%prog': %s" % E, status=status)
    else:
        if p.returncode != 0:
            s = "-- stdout --\n%s\n-- stderr --\n%s" % (p_out, p_err)
            sendMail(options, cmd, body="Output from '%%prog':\n%s" % s, 
                     status=status)

if __name__ == '__main__':
    main()
