class DataCenter(models.Model):
    dc_en = models.CharField(max_length=30, blank=True, verbose_name=u'机房简称')
    dc_cn = models.CharField(max_length=30, blank=True, verbose_name=u'机房全称')

    def __unicode__(self):
        return u'%s %s' % (self.dc_en, self.dc_cn) class SaltAPI(object):
    def __init__(self, url, username, password):
        self.__url = url.rstrip('/')
        self.__user = username
        self.__password = password
        self.__token_id = self.saltLogin()

    def saltLogin(self):
        params = {'eauth': 'pam', 'username': self.__user, 'password': self.__password}
        encode = urllib.urlencode(params)
        obj = urllib.unquote(encode)
        headers = {'X-Auth-Token': ''}
        url = self.__url + '/login'
        req = urllib2.Request(url, obj, headers)
        opener = urllib2.urlopen(req)
        content = json.loads(opener.read())
        try:
            token = content['return'][0]['token']
            return token
        except KeyError:
            raise KeyError

    def postRequest(self, obj, prefix='/'):
        url = self.__url + prefix
        headers = {'X-Auth-Token': self.__token_id}
        req = urllib2.Request(url, obj, headers)
        opener = urllib2.urlopen(req)
        content = json.loads(opener.read())
        return content

    def asyncMasterToMinion(self, tgt, fun, arg):
        '''
        异步执行，当target为部分minion时，Master操作Minion；
        :param target: 目标服务器ID组成的字符串；
        :param fun: 使用的salt模块，如state.sls, cmd.run
        :param arg: 传入的命令或sls文件
        :return: jid字符串
        '''
        if tgt == '*':
            params = {'client': 'local_async', 'tgt': tgt, 'fun': fun, 'arg': arg}
        else:
            params = {'client': 'local_async', 'tgt': tgt, 'fun': fun, 'arg': arg, 'expr_form': 'list'}
        obj = urllib.urlencode(params)
        content = self.postRequest(obj)
        jid = content['return'][0]['jid']
        return jid

    def masterToMinionContent(self, tgt, fun, arg):
        '''
            Master控制Minion，返回的结果是内容，不是jid；
            目标参数tgt是一个如下格式的字符串：'*' 或 'zhaogb-201, zhaogb-202, zhaogb-203, ...'
        '''
        if tgt == '*':
            params = {'client': 'local', 'tgt': tgt, 'fun': fun, 'arg': arg}
        else:
            params = {'client': 'local', 'tgt': tgt, 'fun': fun, 'arg': arg, 'expr_form': 'list'}
        obj = urllib.urlencode(params)
        content = self.postRequest(obj)
        result = content['return'][0]
        return result

    def allMinionKeys(self):
        '''
        返回所有Minion keys；
        分别为 已接受、待接受、已拒绝；
        :return: [u'local', u'minions_rejected', u'minions_denied', u'minions_pre', u'minions']
        '''
        params = {'client': 'wheel', 'fun': 'key.list_all'}
        obj = urllib.urlencode(params)
        content = self.postRequest(obj)
        minions = content['return'][0]['data']['return']['minions']
        minions_pre = content['return'][0]['data']['return']['minions_pre']
        minions_rej = content['return'][0]['data']['return']['minions_rejected']
        return minions, minions_pre, minions_rej

    def actionKyes(self, keystrings, action):
        '''
        对Minion keys 进行指定处理；
        :param keystrings: 将要处理的minion id字符串；
        :param action: 将要进行的处理，如接受、拒绝、删除；
        :return:
        {"return": [{"tag": "salt/wheel/20160322171740805129", "data": {"jid": "20160322171740805129", "return": {}, "success": true, "_stamp": "2016-03-22T09:17:40.899757", "tag": "salt/wheel/20160322171740805129", "user": "zhaogb", "fun": "wheel.key.delete"}}]}
        '''
        func = 'key.' + action
        params = {'client': 'wheel', 'fun': func, 'match': keystrings}
        obj = urllib.urlencode(params)
        content = self.postRequest(obj)
        ret = content['return'][0]['data']['success']
        return ret

    def acceptKeys(self, keystrings):
        '''
        接受Minion发过来的key；
        :return:
        '''
        params = {'client': 'wheel', 'fun': 'key.accept', 'match': keystrings}
        obj = urllib.urlencode(params)
        content = self.postRequest(obj)
        ret = content['return'][0]['data']['success']
        return ret

    def deleteKeys(self, keystrings):
        '''
        删除Minion keys；
        :param node_name:
        :return:
        '''
        params = {'client': 'wheel', 'fun': 'key.delete', 'match': keystrings}
        obj = urllib.urlencode(params)
        content = self.postRequest(obj)
        ret = content['return'][0]['data']['success']
        return ret def outFormat(result):
    '''
    将从数据中获取到的结果，进行格式化输出;
    :param result:
        #result = {
        # 'zhaogb-201':
        #   {'file_|-info_so_1_|-/usr/lib64/libodbc.so.1_|-managed':
        #       {'comment': 'zhaogb-201', 'name': '/usr/lib64/libodbc.so.1',
        #           'start_time': 'zhaogb-201',
        #           'result': True,
        #           'duration': 'zhaogb-201',
        #           '__run_num__': 2,
        #           'changes': {}
        #       }
        #   },
        # 'zhaogb-202':
        #    {'file_|-info_so_1_|-/usr/lib64/libodbc.so.1_|-managed':
        #       {'comment': 'zhaogb-202',
        #           'name': 'zhaogb-202',
        #           'start_time': 'zhaogb-202',
        #           'result': True,
        #           'duration': 'zhaogb-202',
        #           '__run_num__': 2,
        #           'changes':
        #               {'diff': 'New file',
        #               'mode': '0644'
        #               }
        #       }
        #    }
        # }
    :return: 多维列表，形如：[
                            [ip1', {'status': value1, 'cont': longstrings1}],
                            [ip2', {'status': value2, 'cont': longstrings2}],
                            ... ...
                           ]
    '''
    hostfa = 0
    hosttr = 0
    unret = {}

    for ka, va in result.iteritems():
        # result {'zhaogb-201':{},'zhaogb-202':{}}
        # ka zhaogb-201,zhaogb-202
        # va {'mo_watch':{'comment':'','result':'',...}}
        valcon = {}
        longstrva = ''
        falseStatus = 0
        trueStatus = 0
        liv = []

        minion_data = HostList.objects.get(minionid=ka)
        minion_ip = minion_data.ip

        if isinstance(va, dict):
            for kva in va.keys():
                # kva mo_watch,...
                liva = kva.split('_|-')
                liv.append(va[kva]['result'])

                if va[kva]['changes']:
                    changesStr = ''
                    if liva[0] == 'file':
                        if va[kva]['changes'].keys():
                            if ('diff' in va[kva]['changes'].keys()
                                and va[kva]['changes']['diff'] != ''):
                                changesStr += u'\n\t对比 : \n\t\t{0}'.format(
                                    va[kva]['changes']['diff'])
                            if ('mode' in va[kva]['changes'].keys()
                                and va[kva]['changes']['mode'] != ''):
                                changesStr += u'\n\t权限 : \n\t\t{0}'.format(
                                    va[kva]['changes']['mode'])
                            if ('diff' not in va[kva]['changes'].keys()
                                and 'mode' not in va[kva]['changes'].keys()):
                                for ck, cv in va[kva]['changes'].iteritems():
                                    changesStr += u'\n\t{0}'.format(ck)
                                    if ('diff' in va[kva]['changes'][ck].keys()
                                        and va[kva]['changes'][ck]['diff'] != ''):
                                        changesStr += u'\n\t\t对比 : \n\t\t\t{0}'.format(cv['diff'])
                                    if ('mode' in va[kva]['changes'][ck].keys()
                                        and va[kva]['changes'][ck]['mode'] != ''):
                                        changesStr += u'\n\t\t权限 : \n\t\t\t{0}'.format(cv['mode'])

                    elif liva[0] == 'cmd':
                        if ('pid' in va[kva]['changes'].keys()
                            and va[kva]['changes']['pid'] != ''):
                            changesStr += u'\n\tPID : {0}'.format(va[kva]['changes']['pid'])
                        if ('retcode' in va[kva]['changes'].keys()
                            and va[kva]['changes']['retcode'] != ''):
                            changesStr += u'\n\t返回代码 : {0}'.format(va[kva]['changes']['retcode'])
                        if ('stderr' in va[kva]['changes'].keys()
                            and va[kva]['changes']['stderr'] != ''):
                            changesStr += u'\n\t错误 : {0}'.format(va[kva]['changes']['stderr'])
                        if ('stdout' in va[kva]['changes'].keys()
                            and va[kva]['changes']['stdout'] != ''):
                            changesStr += u'\n\t输出 : {0}'.format(va[kva]['changes']['stdout'])
                    else:
                        pass
                    va[kva]['changes'] = changesStr.encode('utf8')
                else:
                    pass

                strva = '结果 : {0}\n标签 : {1}\n操作 : {2}\n开始 : {3}\n耗时 : {4} ms\n变动 : {5}\n{6}\n'.format(
                    va[kva]['result'],
                    liva[1],
                    va[kva]['comment'],
                    va[kva]['start_time'],
                    va[kva]['duration'],
                    va[kva]['changes'],
                    '-' * 60)
                longstrva += strva

            if False in liv:
                colour = 'False'
                hostfa += 1
            elif True in liv:
                colour = 'True'
                hosttr += 1
            else:
                pass
                # error write to logging

            totalStatus = len(liv)
            for livStatus in liv:
                if livStatus == False:
                    falseStatus += 1
                elif livStatus == True:
                    trueStatus += 1
                else:
                    pass
                    # error write to logging

            longstrva += '失败 : {0}\n成功 : {1}\n总计 : {2}'.format(falseStatus, trueStatus, totalStatus)

            valcon['status'] = colour
            valcon['cont'] = longstrva
            unret[minion_ip] = valcon
        else:
            valcon['status'] = 'False'
            valcon['cont'] = str(va)
            unret[minion_ip] = valcon

    # 结果排序工作改为由前端处理，此处注释
    # result_keys_list = unret.keys()
    # result_keys_list.sort()
    # log.debug('Result keys list sorted: {0}'.format(str(result_keys_list)))
    # for ip in result_keys_list:
    #     content = unret.get(ip)
    #     sub_list = [ip, content]
    #     result.append(sub_list)

    return unret, hostfa, hosttr def datacenterToMinionID(datacenter_list):
    '''
    由机房名称组成的列表转换成由MinionID组成的集合；
    如果传过来‘*’，会返回全部MinionKeys组成的列表，将用于计算目标主机数量；
    **注意**: **这里没有判断传过来的列表是否为空，请调用该函数之前自己判断**
    :param dc_list: ['dctest1', 'dctest2', 'dctest3', ...] or ['*']
    :return: a set, set(['zhaogb-201', 'zhaogb-202', 'zhaogb-203', ..., 'zhaogb-nnn'])
    '''
    all_mininon_id_list = []
    if '*' in datacenter_list:
        result_data = HostList.objects.all().values_list("minionid")
        for row_data in result_data:
            minion_id = row_data[0]
            all_mininon_id_list.append(minion_id)
    else:
        for dc in datacenter_list:
            result_data_dccn = DataCenter.objects.get(dcen=dc)
            result_data_hosts = HostList.objects.filter(dccn=result_data_dccn.dccn)
            for row_data in result_data_hosts:
                minion_id = row_data.minionid
                all_mininon_id_list.append(minion_id)

    minion_id_set = set(all_mininon_id_list)

    return minion_id_set def findJob(minionids_set, jid):
    '''

    :return:
    '''
    target_list_to_str = ','.join(list(minionids_set))
    log.debug('target_list: %s' % str(target_list_to_str))
    fun = 'saltutil.find_job'
    diff_send_receive = []
    loop = True

    sapi = SaltAPI(
        url=settings.SALT_API['url'],
        username=settings.SALT_API['user'],
        password=settings.SALT_API['password'])

    while loop:
        counter = 0
        # log.debug('The loop variable')
        log.debug('into loop start [common.views.findJob]')
        time.sleep(10)
        find_job_result = sapi.masterToMinionContent(target_list_to_str, fun, jid)
        log.debug('find_job_result: %s' % str(find_job_result))
        find_job_result_set = set(find_job_result.keys())
        diff_send_receive.extend(list(minionids_set.difference(find_job_result_set)))
        find_job_result_value = find_job_result.values()
        for eachDict in find_job_result_value:
            if eachDict:
                log.debug('The find job result is Dict, It is values list is Not null.')
                break
            else:
                counter += 1
        if counter == len(find_job_result_set):
            loop = False

    diff_send_receive_set = set(diff_send_receive)

    return diff_send_receive_set def mysqlReturns(jid):
    '''

    :param jid: u'20160217142922771111'
    :return:{'host1':{'dict_content'},'host2':{'dict_content'},...}
    '''
    jid = jid.encode('utf-8')
    dict_result = {}
    try:
        log.debug('Query the database salt_returns for %s' % jid)
        return_data = SaltReturns.objects.filter(jid=jid)
        for row in return_data:
            data_return = json.loads(row.return_field)
            dict_result[row.id] = data_return
    except BaseException, e:
        log.error(str(e))

    return dict_result def manageResult(send_ids_set, recv_ips_list):
    '''
    计算目标客户端数量，回收结果数量，未返回结果的数量，未返回结果的IP列表；
    :param send_ids_set:
    :param recv_ips_list:
    :return: {digit, digit, digit, strings}
    '''
    diff_ip_list = []
    info_keys = ('send_count', 'recv_count', 'unrecv_count', 'unrecv_strings')
    send_ids_count = len(send_ids_set)
    recv_ips_count = len(recv_ips_list)
    if send_ids_count == recv_ips_count:
        info_values = [send_ids_count, recv_ips_count, 0, '']
        send_recv_info = dict(zip(info_keys, info_values))
    elif recv_ips_count == 0:
        unrecv_ip_list = []
        for i in send_ids_set:
            data = HostList.objects.get(minionid=i)
            unrecv_ip_list.append(data.ip)
        unrecv_strings = ', '.join(unrecv_ip_list)
        info_values = [send_ids_count, 0, send_ids_count, unrecv_strings]
        send_recv_info = dict(zip(info_keys, info_values))
    else:
        recv_ids_list = []
        for i in recv_ips_list:
            minion_data = HostList.objects.get(ip=i)
            minion_id = minion_data.minionid
            recv_ids_list.append(minion_id)
        recv_ids_set = set(recv_ids_list)
        diff_set = send_ids_set.difference(recv_ids_set)
        unrecv_count = len(diff_set)
        for i in diff_set:
            data = HostList.objects.get(minionid=i)
            diff_ip_list.append(data.ip)
        unrecv_strings = ', '.join(diff_ip_list)
        info_values = [send_ids_count, recv_ips_count, unrecv_count, unrecv_strings]
        send_recv_info = dict(zip(info_keys, info_values))
    return send_recv_info def moduleDetection(module, user):
    '''
    检测如state.sls/cmd.run等模块是否被占用；
    :param module: 'cmd.run' 或 'state.sls'
    :param user: 'zhaogb' 或其他用户名；
    :return: 如果模块被占用，则返回如 "zhaogb is using cmd.run"；如果模块没有被占用，则返回空字符串；
    '''
    log.debug('%s detection module %s occupied' % (user, module))
    try:
        module_exist = ModulesLock.objects.get(module=module)
        module_status = module_exist.status
        module_user = module_exist.user
        if module_status == 'True':
            status = '%s is using module %s' % (module_user, module)
            log.info(status)
        elif module_status == 'False':
            status = ''
            log.info("Nobody uses module %s" % module)
        else:
            pass
    except ModulesLock.DoesNotExist:
        status = ''
        log.info("The %s module has never been used" % module)

    return status def moduleLock(module, user):
    '''
    将模块锁定；
    :param module: 'cmd.run' 或 'state.sls'
    :param user: 'zhaogb' 或其他用户名；
    :return: None
    '''
    log.debug('%s Lock Module : %s' % (user, module))
    try:
        module_exist = ModulesLock.objects.get(module=module)
        module_status = module_exist.status
        if module_status == 'False':
            module_exist.status = 'True'
            module_exist.user = user
            module_exist.save()
            log.info("%s Lock Module %s Successed!" % (user, module))
        else:
            log.info("Someone could use this module %s" % module)
    except ModulesLock.DoesNotExist:
        log.info("The %s module has never been used" % module)
        module_lock = ModulesLock.objects.create(module=module, status='True', user=user)
        log.info("%s Lock Module %s Successed!" % (user, module)) def moduleUnlock(module, user):
    '''
    将锁定的模块解锁；
    :param module: 'cmd.run' 或 'state.sls'
    :param user: 'zhaogb' 或其他用户名；
    :return: None
    '''
    log.debug('%s Unlock Module %s' % (user, module))
    module_unlock = ModulesLock.objects.get(module=module)
    module_unlock.status = 'False'
    module_unlock.user = ''
    module_unlock.save()
    log.info('%s unlock module %s successed!' % (user, module)) BASE_DIR = os.path.dirname(os.path.dirname(__file__))
SECRET_KEY = '2vuicou20ne9t&h)#c2!7sz4cc+6hcfxfmy!4agjs7@7-$nddq'
DEBUG = True
TEMPLATE_DEBUG = True
ALLOWED_HOSTS = []
SESSION_SAVE_EVERY_REQUEST = True
SESSION_EXPIRE_AT_BROWSER_CLOSE = True
INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'index',
    'dzhops',
    'hostlist',
    'replacedata',
    'saltstack',
    'record',
    'newtest',
)
MIDDLEWARE_CLASSES = (
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.locale.LocaleMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
)
ROOT_URLCONF = 'dzhops.urls'
WSGI_APPLICATION = 'dzhops.wsgi.application'
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'dzhops',
        'USER': 'dzhops',
        'PORT': 33066,
        'HOST': 'db-dev.dzh-inc.com',
        'PASSWORD': 'dzhops',
    }
}
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_L10N = True
USE_TZ = False
STATICFILES_DIRS = (
    BASE_DIR + '/static',
)
STATIC_URL = '/static/'
TEMPLATE_DIRS = (
    os.path.join(BASE_DIR, 'templates'),
)
SALT_API = {
    'url': 'http://10.15.201.102:18000/',
    'user': 'zhaogb',
    'password': 'zhaogb'
}
LOGGING = {
    'version': 1,
    'disable_existing_loggers': True,
    'formatters': {
        'standard': {
            'format': '%(asctime)s %(levelname)s %(module)s %(funcName)s %(lineno)d %(message)s'
        },
        'simple': {
            'format': '%(levelname)s %(message)s'
        },
    },
    'filters': {
        'require_debug_false': {
            '()': 'django.utils.log.RequireDebugFalse'
        },
    },
    'handlers': {
        'mail_admins': {
            'level': 'ERROR',
            'filters': ['require_debug_false'],
            'class': 'django.utils.log.AdminEmailHandler'
        },
        'log_handler': {
            'level': 'DEBUG',
            'class': 'logging.handlers.RotatingFileHandler',
            'filename': os.path.join(BASE_DIR, 'log', 'dzhops.log'),
            'formatter': 'standard',
        },
    },
    'loggers': {
        'django.request': {
            'handlers': ['mail_admins'],
            'level': 'ERROR',
            'propagate': True,
        },
        'dzhops': {
            'handlers': ['log_handler'],
            'level': 'DEBUG',
            'propagate': False
        },
    }
}
from django.shortcuts import render
from django.http import HttpResponse
from django.contrib.auth.decorators import login_required
from replacedata.models import StockExchage
from hostlist.models import DataCenter
from record.models import OperateRecord, ReturnRecord
from saltstack.saltapi import SaltAPI
from saltstack.util import outFormat, datacenterToMinionID, findJob, mysqlReturns, manageResult, moduleDetection, moduleLock, moduleUnlock
from dzhops import settings
import os, time, logging, json
# -*- coding: utf-8 -*-



# Create your views here.
log = logging.getLogger('dzhops')

@login_required
def repairHistoryData(request):
    '''
    本功能最初设计目的是补错误的历史数据。
    :param request:
    :return:
    '''
    user = request.user.username
    dc_list = []
    data_centers = {}
    stock_exchanges = []


    result_dc = DataCenter.objects.all()