from __future__ import division
import redis
import time
import psutil
import socket
import pymongo
from pymongo import MongoClient

redis_report_ip = '192.168.4.94'
redis_report_port = 6379
mongodb_stats = "mongodb://192.168.4.97:27017"
mongodb_info = "mongodb://192.168.4.98:27017"


class DockerStats(object):
    def __init__(self, redis_report_ip, redis_report_port, redis_port, container):
        # self.cli = Client(base_url='tcp://%s:2375' % docker_ip)
        self.cli = Client(base_url='unix://var/run/docker.sock')
        self.redis_report_ip = redis_report_ip
        self.redis_report_port = redis_report_port
        self.redis_port = redis_port
        self.container = container
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.connect(('192.168.4.254', 0))
        self.host = s.getsockname()[0]

    @property
    def container_stat(self):
        self.stats_obj = self.cli.stats(self.container, stream=False)
        return self.stats_obj

    @property
    def docker_conns(self):
        for conn in psutil.net_connections():
            try:
                if conn[3][1] == self.redis_port:
                    redis_pid = conn[-1]
                    break
            except:
                pass
        redis_ins = psutil.Process(redis_pid)
        return len(redis_ins.connections())

    def redis_info(self):
        r = redis.StrictRedis(host=self.host, port=self.redis_port, db=0)
        info = r.info()
        redis_key = '%s:%s' % (self.host, self.redis_port)
        info['time'] = int(time.time())
        return redis_key, info

    def write_redis(self):
        r = redis.StrictRedis(host=self.redis_report_ip, port=self.redis_report_port, db=0)
        now_result = self.container_stat  # Dict
        memory_usage = now_result["memory_stats"]["usage"]
        ##pid connections###
        cpuPercent = 0.0
        TotalUsage = now_result["cpu_stats"]["cpu_usage"]["total_usage"]
        SystemUsage = now_result["cpu_stats"]["system_cpu_usage"]
        previous = '%s:%s:%s' % (self.host, self.redis_port, 'previous')
        get_previous = r.hget('redis_stats_docker', previous)
        if not get_previous:
            r.hset('redis_stats_docker', previous, [TotalUsage, SystemUsage])
        else:
            previousCPU, previousSystem = eval(r.hget('redis_stats_docker', previous))
            cpuDelta = TotalUsage - previousCPU
            systemDelta = SystemUsage - previousSystem
            r.hset('redis_stats_docker', previous, [TotalUsage, SystemUsage])
            cpuPercent = '%.2f' % (
                (cpuDelta / systemDelta) * len(self.stats_obj["cpu_stats"]["cpu_usage"]["percpu_usage"]) * 100.0)
            ######################################{'conns': 33, 'cpu_usage': '1.20', 'mem_usage': 2788642816}
        redis_key = '%s:%s' % (self.host, self.redis_port)
        redis_value = {'conns': self.docker_conns,
                       'cpu_usage': cpuPercent,
                       'mem_usage': memory_usage,
                       'time': int(time.time())
                       }
        r.hset('redis_stats', redis_key, redis_value)
        return redis_key, redis_value


class RedisStats(object):
    def __init__(self, redis_report_ip, redis_report_port, psinfo):
        self.redis_report_ip = redis_report_ip
        self.redis_report_port = redis_report_port
        self.redis = psinfo
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.connect(('192.168.4.254', 0))
        self.host = s.getsockname()[0]

    @property
    def cpu_usage(self):
        self.cpu = '%.2f' % self.redis.cpu_percent(interval=0.8)
        return self.cpu

    @property
    def mem_usage(self):
        self.memory = self.redis.memory_info().rss
        return self.memory

    @property
    def conns(self):
        self.connections = self.redis.connections()
        return self.connections

    @property
    def get_redis_port(self):
        self.conns
        for conn in self.connections:
            if conn.status == 'LISTEN':
                self.port = conn.laddr[1]
                return self.port

    def redis_info(self):
        self.port = self.get_redis_port
        r = redis.StrictRedis(host=self.host, port=self.port, db=0)
        info = r.info()
        redis_key = '%s:%s' % (self.host, self.port)
        info['time'] = int(time.time())
        return redis_key, info

    def write_redis(self):
        self.cpu_usage
        self.mem_usage
        self.conns
        self.get_redis_port
        r = redis.StrictRedis(host=self.redis_report_ip, port=self.redis_report_port, db=0)
        key = '%s:%s' % (self.host, self.port)
        value = {'cpu_usage': self.cpu,
                 'mem_usage': self.memory,
                 'conns': len(self.connections),
                 'time': int(time.time())
                 }
        r.hset('redis_stats', key, value)
        return key, value


def get_container():
    contianers = []
    cli = Client(base_url='unix://var/run/docker.sock')
    for contianer in cli.containers():
        try:
            if contianer['State'] == 'running' and 'dyredis' in contianer['Image']:
                contianers.append(contianer['Names'][0][1:])
        except:
            pass
    return contianers


def get_instance_ps():
    pslist = []
    ps = psutil.process_iter()
    for psinfo in ps:
        if psinfo.name() == 'redis-server':
            pslist.append(psutil.Process(psinfo.pid))
    return pslist


def write_mongodb(redis_key, redis_value, type, isindex):
    if type == 'stats':
        client = MongoClient(mongodb_stats)
    elif type == 'info':
        client = MongoClient(mongodb_info)
    dbname = 'redis_stats' + str(redis_value['time'] // (60 * 60 * 24 * 30))
    collection_name = 'info' + str(redis_value['time'] // (60 * 60 * 24))
    redis_value.update({'instance': redis_key})
    json_data = redis_value
    db = eval('%s.%s' % ("client", dbname))
    collection = eval('%s.%s' % ("db", collection_name))
    if isindex:
        if  "instance_1" in collection.index_information().keys():
            collection.ensure_index([("instance", pymongo.ASCENDING)])
        if "time_1" in collection.index_information().keys():
            collection.ensure_index([("time", pymongo.ASCENDING)])
    collection.insert_one(json_data)
    return collection_name


def run(redis_report_ip, redis_report_port, last_collection_name, last_time, param):
    if param:
        loop = get_container()
    else:
        loop = get_instance_ps()
    try:
        for contianer in loop:
            if param:
                contianer_port = int(contianer.split('_')[-1])
                stats = DockerStats(redis_report_ip, redis_report_port, contianer_port, contianer)
            else:
                stats = RedisStats(redis_report_ip, redis_report_port, contianer)
            stats_key, stats_val = stats.write_redis()
            collection_name = 'info' + str(stats_val['time'] // (60 * 60 * 24))
            if int(time.time()) // 3600 > last_time:
                issharptime = True
                info_key, info_val = stats.redis_info()
            if collection_name == last_collection_name:
                last_collection_name = write_mongodb(stats_key, stats_val, 'stats', 0)
                last_time = int(time.time()) // 3600
                if issharptime:
                    last_collection_name = write_mongodb(info_key, info_val, 'info', 0)
            else:
                last_collection_name = write_mongodb(stats_key, stats_val, 'stats', 1)
                last_time = int(time.time()) // 3600
                if issharptime:
                    last_collection_name = write_mongodb(info_key, info_val, 'info', 1)
    except:
        pass
    return last_collection_name, last_time


if __name__ == '__main__':
    last_collection_name = ""
    last_time = 0
    try:
        from docker import Client
    except:
        sys_env = 0
    else:
        sys_env = 1
    while 1:
        last_collection_name, last_time = run(redis_report_ip, redis_report_port, last_collection_name, last_time,
                                                  sys_env)
        time.sleep(3)
