#!/usr/bin/env python
# -*- coding: utf-8 -*-


import json
from flask import g

from jimvc.models import app_config
from jimvc.models import Database as db


__author__ = 'James Iter'
__date__ = '2017/9/19'
__contact__ = 'james.iter.cn@gmail.com'
__copyright__ = '(c) 2017 by James Iter.'


class Host(object):

    def __init__(self):
        pass

    @staticmethod
    def alive_check(v):
        """
        60 秒内没收到更新，作为判断 [计算节点] 是否在线的标准。包含了 [计算节点] 与 [控制节点] 间的时间差。
        """

        coupler_length = 60

        if 'timestamp' not in v:
            return v

        v['alive'] = False
        if v['timestamp'] + coupler_length >= g.ts:
            v['alive'] = True

        if 'threads_status' not in v:
            v['threads_status'] = {
                'instruction_process_engine': {
                    'timestamp': 0
                },
                'host_state_report_engine': {
                    'timestamp': 0
                },
                'guest_creating_progress_report_engine': {
                    'timestamp': 0
                },
                'guest_performance_collection_engine': {
                    'timestamp': 0
                },
                'host_performance_collection_engine': {
                    'timestamp': 0
                }
            }

        v['threads_status']['instruction_process_engine']['alive'] = False
        if v['threads_status']['instruction_process_engine']['timestamp'] + coupler_length >= g.ts:
            v['threads_status']['instruction_process_engine']['alive'] = True

        v['threads_status']['host_state_report_engine']['alive'] = False
        if v['threads_status']['host_state_report_engine']['timestamp'] + coupler_length >= g.ts:
            v['threads_status']['host_state_report_engine']['alive'] = True

        v['threads_status']['guest_creating_progress_report_engine']['alive'] = False
        if v['threads_status']['guest_creating_progress_report_engine']['timestamp'] + coupler_length >= g.ts:
            v['threads_status']['guest_creating_progress_report_engine']['alive'] = True

        v['threads_status']['guest_performance_collection_engine']['alive'] = False
        if v['threads_status']['guest_performance_collection_engine']['timestamp'] + coupler_length >= g.ts:
            v['threads_status']['guest_performance_collection_engine']['alive'] = True

        v['threads_status']['host_performance_collection_engine']['alive'] = False
        if v['threads_status']['host_performance_collection_engine']['timestamp'] + coupler_length >= g.ts:
            v['threads_status']['host_performance_collection_engine']['alive'] = True

        return v

    @staticmethod
    def set_allocation_mode(hosts_name=None, random=True):
        if not isinstance(hosts_name, list):
            raise ValueError('The hosts_name must be a list.')

        if random:
            db.r.sadd(app_config['compute_nodes_of_allocation_by_nonrandom'], *hosts_name)

        else:
            db.r.srem(app_config['compute_nodes_of_allocation_by_nonrandom'], *hosts_name)

    @classmethod
    def get_all(cls):

        ret = list()
        compute_nodes_of_allocation_by_nonrandom = \
            list(db.r.smembers(app_config['compute_nodes_of_allocation_by_nonrandom']))

        for k, v in db.r.hgetall(app_config['hosts_info']).items():
            v = json.loads(v)
            v = cls.alive_check(v)
            v['node_id'] = k

            if v['hostname'] in compute_nodes_of_allocation_by_nonrandom:
                v['nonrandom'] = True
            else:
                v['nonrandom'] = False

            ret.append(v)

        if ret.__len__() > 1:
            ret.sort(key=lambda _k: _k['boot_time'])

        return ret

    @classmethod
    def get_available_hosts(cls, nonrandom=None):
        """
        :param nonrandom: {None, True, False}
            None for all;
            False for host can be allocation guest by random;
            True on the contrary.
        :return:
        """

        hosts = list()

        for host in cls.get_all():

            if not host['alive']:
                continue

            if nonrandom is not None and host['nonrandom'] != nonrandom:
                continue

            host['system_load_per_cpu'] = float(host['system_load'][0]) / host['cpu']
            hosts.append(host)

        hosts.sort(key=lambda _k: _k['system_load_per_cpu'])

        return hosts

    @staticmethod
    def get_lightest_host():
        # 负载最小的宿主机
        lightest_host = None
        for k, v in db.r.hgetall(app_config['hosts_info']).items():
            v = json.loads(v)

            if lightest_host is None:
                lightest_host = v

            if float(lightest_host['system_load'][0]) / lightest_host['cpu'] > \
                    float(v['system_load'][0]) / v['cpu']:
                lightest_host = v

        return lightest_host
