#!/usr/bin/env python

## Peter Akey, (c) FireEye, Inc.
## 15 Dec 2015
##
## This is the xmlrpc client that will run on Jenkins
## This issues a replay request to the server/host
## Which will be permitted to start tcpreplay in the background
##
## -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

import argparse
import errno
import logging
import xmlrpclib
from socket import error as socket_error

#my_logger = logging.getLogger('XMLRPCReplay')
#my_logger.setLevel(logging.DEBUG)
#handler = logging.FileHandler('/home/jenkins/tcp-client.log')                           # Create file handler
#formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')   # Logging format
#handler.setFormatter(formatter)
#my_logger.addHandler(handler)                                                           # Add the handlers to the logger

def main():
    s = xmlrpclib.ServerProxy('http://{host}:{tcpport}'.format(host=args.generator.lower(),tcpport=args.tcpport), allow_none=True)
    if args.cmd == 'replay' or args.cmd == 'overlay':
        print 'Replay ({c}) request: rate:{rate} pps:{pps} loop:{loop} pcap:{pcap} device:{d} port:{p} unique:{u}'.format(c=args.cmd,rate=args.rate,pps=args.pps,loop=args.loop,pcap=args.pcap,d=args.device,p=args.port,u=args.unique)
        try:
            print s.replay(args.prog, args.cmd, args.rate, args.pps, args.loop, args.pcap, args.device, args.port, args.unique)
            #print('Error encountered: {e}'.format(e=e.message))
        except socket_error as e:
            if e.errno == errno.ECONNREFUSED:
                print('Error encountered: Connection refused from {h} on TCP port {p}'.format(h=args.generator,p=args.tcpport))
            elif e.errno == errno.EHOSTUNREACH:
                print('Error encountered: No route to host {h} (often seen when firewall is blocking) '.format(h=args.generator))
    elif args.cmd == 'kill':
#        my_logger.debug('Kill request: prog={prog} d={d} p={p}'.format(prog=args.prog,d=args.device,p=args.port))
        try:
            print s.kill(args.prog,args.device,args.port)
        except socket_error as e:
            if e.errno == errno.ECONNREFUSED:
                print('Error encountered: Connection refused from {h} on TCP port {p}'.format(h=args.generator,p=args.tcpport))
            elif e.errno == errno.EHOSTUNREACH:
                print('Error encountered: No route to host {h} '.format(h=args.generator))
    elif args.cmd == 'status' and args.check_replay:
        print s.status(args.prog,args.device,args.port)
    elif args.cmd == 'status' and args.check_pcap:
        try:
           print s.check_pcap(args.pcap)
        except:
           print "tcp-client error!"
           pass
    elif args.cmd == 'status' and args.check_rate:
        print s.check_rate(args.device,args.port)
    elif args.cmd == 'status' and args.check_loop:
        print s.check_loop(args.device,args.port)
    elif args.cmd == 'get' and args.get_pcap:
        print s.get_pcap(args.pcap)

if __name__=='__main__':
    producer_parser = argparse.ArgumentParser(add_help=False, description='Issue replay or status commands against a traffic producer')
    producer_parser.add_argument('-g', '--generator', dest='generator', help='XMLRPC server host (traffic generator)', required=True, type=str)
    producer_parser.add_argument('-t', '--tcpport', dest='tcpport', help='XMLRPC listening port', default=10101, type=int)

    pcap_parser = argparse.ArgumentParser(add_help=False, description='Issue replay or status commands against a traffic producer')
    pcap_parser.add_argument('-g', '--generator', dest='generator', help='XMLRPC server host (traffic generator)', required=True, type=str)
    pcap_parser.add_argument('-t', '--tcpport', dest='tcpport', help='XMLRPC listening port', default=10101, type=int)
    pcap_parser.add_argument('-f', '--pcapfile', dest='pcap', help='Specify pcap file to play', type=str)

    port_parser = argparse.ArgumentParser(add_help=False)
    port_parser.add_argument('-s', '--source', dest='prog', help='The program (hhreplay|tcpreplay) that we\'re going to send the request to', type=str)
    port_parser.add_argument('-d', '--device', dest='device', help='Adapter type ("eth") or number (typically 0,1)')
    port_parser.add_argument('-p', '--port', dest='port', help='Port over which to replay traffic; if --device is empty, Eth port is assumed')

    base_parser = argparse.ArgumentParser()
    subparsers = base_parser.add_subparsers(dest='cmd')

    kill_group = subparsers.add_parser('kill', parents=[producer_parser,port_parser])

    replay_group = subparsers.add_parser('replay', parents=[producer_parser,port_parser])
    replay_group.add_argument('-l', '--loop', dest='loop', help='How many times to loop', default=0, type=int)
    replay_group.add_argument('-f', '--pcapfile', dest='pcap', help='Specify pcap file to play', type=str)
    replay_group.add_argument('--pps', dest='pps', help='Packets per second', type=int, default=None)
    replay_group.add_argument('--unique-ip', dest='unique', help='Modify IP addresses each loop iteration to generate unique flows', action='store_true')
    replay_group.add_argument('--rate', dest='rate', help='Rate; valid for hhreplay (in Mbps)', type=int, default=1)

    get_group = subparsers.add_parser('get', parents=[pcap_parser])
    get_group.add_argument('--pcap', dest='get_pcap', help="Initiate transfer of pcap from pcap_store to producer", action='store_true')

    status_group = subparsers.add_parser('status', parents=[pcap_parser, port_parser])
    status_group.add_argument('--loop', dest='check_loop', help="Get current loop count for device/port", action='store_true')
    status_group.add_argument('--pcap', dest='check_pcap', help='Check to see if pcap exists', action='store_true')
    status_group.add_argument('--rate', dest='check_rate', help="Get current Mbps of device/port", action='store_true')
    status_group.add_argument('--replay', dest='check_replay', help='Get status of replay', action='store_true')

    args = base_parser.parse_args()

    try:
        if args.check_loop is None:
            args.check_loop = False
    except AttributeError as ae:
        args.check_loop = False
    try:
        if args.check_pcap is None:
            args.check_pcap = False
    except AttributeError as ae:
        args.check_pcap = False
    try:
        if args.check_rate is None:
            args.check_rate = False
    except AttributeError as ae:
        args.check_rate = False
    try:
        if args.check_replay is None:
            args.check_replay = False
    except AttributeError as ae:
        args.check_replay = False
    try:
        if args.device is None:
            args.device = ''
    except AttributeError as ae:
        args.device = ''
    try:
        if args.pcap is None:
            args.pcap = ''
    except AttributeError as ae:
        args.pcap = ''
    try:
        if args.port is None:
            args.port = ''
    except AttributeError as ae:
        args.port = ''
    try:
        if args.pps is None:
            args.pps = ''
    except AttributeError as ae:
        args.pps = ''
    try:
        if args.prog is None:
            args.prog = ''
    except (TypeError, AttributeError) as ae:
        args.prog = ''
    try:
        if args.rate is None:
            args.rate = ''
    except AttributeError as ae:
        args.rate = ''

    # Uber debug
    #print "cmd={0}|chkreplay={1}|prog={2}|pcap={3}|dev={4}|port={5}".format(args.cmd, args.check_replay, args.prog, args.pcap, args.device, args.port)
    # Cause my own errors
    if args.cmd == 'get' and not args.get_pcap:
        base_parser.error('A "get" action is required.  For instance, --pcap to "get pcap".')
        sys.exit(1)
    if args.cmd == 'get' and args.get_pcap and args.pcap == '':
        base_parser.error('-f is required if checking pcap status')
        sys.exit(1)
    if args.cmd == 'kill' and (args.prog == 'hhreplay' and (args.device == '' or args.port == '')):
        base_parser.error('--device and --port are both required if cmd={0} and source={1}'.format(args.cmd, args.prog))
        sys.exit(1)
    if (args.cmd == 'replay' or args.cmd == 'overlay') and args.pcap == '':
        base_parser.error('-f pcapfile required if cmd is {0}'.format(args.cmd))
        sys.exit(1)
    if args.cmd == 'replay' and args.prog == '':
        base_parser.error('[--source tcpreplay|hhreplay] is required if cmd=replay')
        sys.exit(1)
    if args.cmd == 'replay' and args.prog == 'tcpreplay' and args.pps == '':
        base_parser.error('--pps is required if cmd={0} and prog={1}'.format(args.cmd,args.prog))
        sys.exit(1)
    if args.cmd == 'status' and (args.check_loop and (args.device == '' or args.port == '')):
        base_parser.error('--device and --port are both required if cmd={0} and "--rate"'.format(args.cmd))
        sys.exit(1)
    if args.cmd == 'status' and (args.check_rate and (args.device == '' or args.port == '')):
        base_parser.error('--device and --port are both required if cmd={0} and "--rate"'.format(args.cmd))
        sys.exit(1)
    if args.cmd == 'status' and (args.check_replay and (args.prog == 'hhreplay' and (args.device == '' or args.port == ''))):
        base_parser.error('--device and --port are both required if cmd={0} and source={1}'.format(args.cmd, args.prog))
        sys.exit(1)
    if args.cmd == 'status' and not (args.check_loop or args.check_pcap or args.check_rate or args.check_replay):
        base_parser.error('--replay or --pcap is required when checking status')
        sys.exit(1)
    if args.cmd == 'status' and args.check_replay and args.prog == '':
        base_parser.error('[--source hhreplay|tcpreplay] is required when asking for replay status')
        sys.exit(1)
    if args.cmd == 'status' and args.check_pcap and (args.pcap == '' or args.pcap == ''):
        base_parser.error('-f is required if checking pcap status')
        sys.exit(1)

    main()
