#!/usr/bin/env python

# Author: Mike Chester <mchester@uvic.ca>
# Copyright (C) 2013 University of Victoria
# You may distribute under the terms of either the GNU General Public
# License or the Apache v2 License.

import sys
import json
import pika
import time
import netifaces
import uuid
import socket
import logging
import urllib2

from shoal_agent import config as config

# Time interval to send data
INTERVAL = config.interval

LOG_FORMAT = '%(asctime)s - %(levelname)s - [%(filename)s:%(lineno)s] - %(message)s'
logger = logging.getLogger('shoal-agent')

private_address = ('10.','172.','192.')
external_ip = config.external_ip

def amqp_send(data):
    exchange = config.amqp_exchange
    exchange_type = config.amqp_exchange_type
    host = config.amqp_server_url
    cloud = config.cloud
    routing_key = cloud + '.info'

    try:
        connection = pika.BlockingConnection(pika.URLParameters(host))
        channel = connection.channel()
        channel.basic_publish(exchange=exchange,
                              routing_key=routing_key,
                              body=data,
                              properties=pika.BasicProperties(content_type='application/json', delivery_mode=1))
        connection.close()
    except Exception as e:
        logger.error("Could not connect to AMQP Server. Is it running at url: {0}\nPlease check configuration settings.\nError: {1}".format(host,e))
        sys.exit(1)

def get_load_data(path):
    try:
        with open(path) as tx:
            tx1 = int(tx.read())

        time.sleep(1)

        with open(path) as tx:
            tx2 = int(tx.read())

        tx_rate = (tx2 - tx1) / 1024
        return tx_rate
    
    except Exception as e:
        logger.error("Unable to generate load data, does this file exist: '{0}'.\nPlease check settings in configuration file.\n{1}".format(path,e))
        sys.exit(1)

def get_ip_addresses():
    public = private = None
    for interface in netifaces.interfaces():
        try:
            for link in netifaces.ifaddresses(interface)[netifaces.AF_INET]:
                if link['addr'].startswith(private_address):
                    private = link['addr']
                elif not link['addr'].startswith('127.'):
                    public = link['addr']
        except Exception as e:
            continue
    return public, private

def main():
    config.setup()
    try:
        logging.basicConfig(level=logging.INFO, format=LOG_FORMAT, filename=config.log_file)
    except IOError as e:
        print "Could not set logger. Please check config file.", e
        sys.exit(1)
    # make a UUID based on the, host ID and current time
    id = str(uuid.uuid1())
    hostname = socket.gethostname()

    data = {
            'uuid': id,
            'hostname': hostname,
           }

    public_ip, private_ip = get_ip_addresses()
    external_ip = config.external_ip
    tx_bytes_path = config.tx_bytes_path

    if public_ip:
        data['public_ip'] = ('{0}:{1}').format(public_ip, config.squid_port)
    elif external_ip:
        data['external_ip'] = external_ip
    else:
        logger.error("Shoal-Agent was unable to find a public IP or external IP for this squid. Please set an external IP in shoal-agent config file.")
        sys.exit(1)

    if private_ip:
        data['private_ip'] = ('{0}:{1}').format(private_ip, config.squid_port)

    if not (private_ip or public_ip):
        logger.error("Shoal_Agent was unable to find a public or private IP for this squid. Please contact the developers.")
        sys.exit(1)

    while True:
        try:
            # if shoal was down when service started, external ip will be none
            # this could also occur if there was no internet connectivity when shoal_agent was started.
            # we need a public_ip or external_ip to generate the geolocation data, this will make sure atleast one is set.
            if not (public_ip or external_ip):
                external_ip = get_external_ip()
                data['external_ip'] = external_ip


            data['timestamp'] = time.time()
            data['load'] = get_load_data(tx_bytes_path)

            if 'public_ip' in data or 'external_ip' in data:
                amqp_send(json.dumps(data))

            time.sleep(INTERVAL)
        except KeyboardInterrupt:
            sys.exit(0)

if __name__ == '__main__':
    main()
