#!/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.
"""
from smtplib import SMTP
import socket
from subprocess import Popen, PIPE
import sys
from netlogger.nllog import OptionParser, get_logger

## Functions

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.
    """
    log = get_logger(__file__)
    sbj = options.subject
    prog = cmd.split()[0]
    if sbj.find('%prog') >= 0:
        sbj = sbj.replace('%prog', prog)
    if sbj.find('%cmd') >= 0:
        sbj = sbj.replace('%cmd', cmd)
    if body.find('%prog') >= 0:
        body = body.replace('%prog', prog)
    if body.find('%cmd') >= 0:
        body = body.replace('%cmd', cmd)
    if sbj.find('%host') >= 0:
        fqdn = socket.getfqdn()
        try:
            hostname = socket.gethostbyname(fqdn)
        except (socket.herror, socket.gaierror):
            hostname = 'localhost'
        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:
        try:
            s = SMTP(options.server_host, options.server_port)
            s.sendmail(options.from_addr, options.to_addr, msg)
        except socket.error, E:
            log.error("sendmail.error", msg=E)

def main():
    usage = "%prog [options] command args.."
    desc = __doc__.split('\n')[1]
    parser = OptionParser(usage=usage, 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()
    log = get_logger(__file__)  # Should be first done, just after parsing 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"
    # Run
    log.info("run.start", args=args)
    status, retcode = 'failed', -1
    try:
        p = Popen(args, stdout=PIPE, stderr=PIPE)
        p_out, p_err = p.communicate()
        if options.nagios:
            status = p_out.split('\n')[0]
        retcode = p.wait()
    except OSError, err:
        sendMail(options, cmd, body="Cannot run '%%prog': %s" % err,
                 status=status)
    else:
        if retcode != 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)
        else:
            status = "OK"
            if options.test:
                print "Success. No email would be sent."
    log.info("run.end", status=retcode, msg=status)
    return retcode

if __name__ == '__main__':
    sys.exit(main())
