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

"""
Тулза для бэкапа информации о рейдах и дисках в почту и свн
Умеет в mdadm и zfs
"""

import os
import sys
import subprocess
import socket
import smtplib
from email.mime.text import MIMEText

SVN_BIN = '/usr/bin/svn'
SVN_URL = 'svn+ssh://svn.yandex-team.ru'
SVN_REPO = 'direct-admin-raid'
REP_DIR = '/root/raid_backup/%s/trunk/' % SVN_REPO

LC_USER="robot-direct-admin"
SSH_KEY="/home/%s/.ssh/id_dsa" % LC_USER
SVN_SSH="ssh -l %s -o UserKnownHostsFile=/dev/null -o ConnectTimeout=15 -o StrictHostKeyChecking=no -i %s" % (LC_USER, SSH_KEY)

ML_NAME = 'direct-admin-raid@yandex-team.ru'

CACHE_DIR = '/var/cache/yandex-du-array-info/'


def checkSubversion():
    if not os.path.isfile(SVN_BIN):
        sys.exit('Subversion not found')

def getCache():
    arrays = []
    if not os.path.exists(CACHE_DIR):
        return []
    else:
        for type_array in os.listdir(CACHE_DIR):
            with open(CACHE_DIR + type_array, 'r') as cfile:
                arrays.extend(cfile.readline().split())
        return arrays


def setCache(type_array, arrays):
    if not os.path.exists(CACHE_DIR):
        os.mkdir(CACHE_DIR)
    with open(CACHE_DIR + type_array, 'w') as cache:
        for array in arrays:
            cache.write(array + ' ')


def getRepoAndSetWorkDir(): 
    if not os.path.exists(REP_DIR):
        os.makedirs(REP_DIR)
    os.chdir(REP_DIR)
    my_env = os.environ.copy()
    my_env['SVN_SSH'] = SVN_SSH
    runShell('svn co ' + '%s/%s/trunk/%s'%(SVN_URL, SVN_REPO, getHostname()), my_env)
#    trunk_dir = REP_DIR + SVN_REPO + '/trunk/'
    work_dir = REP_DIR + getHostname()
    if not os.path.exists(work_dir):
        os.mkdir(work_dir)
    os.chdir(work_dir)


def doCommit():
    my_env = os.environ.copy()
    my_env['SVN_SSH'] = SVN_SSH
    not_svn_files = runShell('svn st')
    if not_svn_files == "svn: warning: '.' is not a working copy\n":
        runShell('svn add ' + os.getcwd())
    elif not_svn_files:
        for files in not_svn_files.splitlines():
            if '?' in files.split():
                runShell('svn add ' + files.split()[1]) 
    runShell('svn ci -m "%s"' % getHostname(), my_env)


def sendMail(all_out):
    msg = MIMEText(all_out)
    msg['From'] = LC_USER
    msg['To'] = ML_NAME
    msg['Subject'] = getHostname()
    s = smtplib.SMTP('localhost')
    s.sendmail(LC_USER, ML_NAME, msg.as_string())
    s.quit()


def getHostname():
    if socket.gethostname().find('.')>=0:
        hname=socket.gethostname()
    else:
        hname=socket.gethostbyaddr(socket.gethostname())[0]
    return hname

def toFile(filename, text):
    with open(filename, 'w') as out:
        out.write(text)


def runShell(command, my_env = {}):
    if not my_env: my_env = os.environ.copy()
    sp = subprocess.Popen(command.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=my_env)
    output, error = sp.communicate()
    output += error
    if sp.returncode != 0:
        sys.exit('Can\'t execute "%s"' % command)
#    print(error)
    return output


def getBlockDevices():
    disks = []
    devices = os.listdir('/sys/block/')
    for folder in devices:
        if not (folder.startswith('ram') or folder.startswith('loop') or folder.startswith('md') or folder.startswith('dm')):
            disks.append(folder)
    return disks
    

def getMDArrays():
    mdstat={}
    with open('/proc/mdstat', 'r') as proc_mdstat:
        for line in proc_mdstat:
            if line.startswith('md'):
                array, info = line.strip().split(":")
                array = array.rstrip()
                mdstat[array] = info
    return mdstat


def getPools():
    if not os.path.isfile('/sbin/zpool'):
        return []
    zpools = runShell('/sbin/zpool list -o name').splitlines()
    zpools.remove('NAME')
    if zpools != []:
        return(zpools)
    else:
#        print("zfs not found")
        return []


if __name__ == '__main__':

    checkSubversion()
    all_out = ''
    getRepoAndSetWorkDir()
    cache_arrays = getCache()
    md_arrays = getMDArrays()
    zfs_arrays = getPools()
    setCache('md', md_arrays.keys())
    setCache('zpool', zfs_arrays)
    new_array = [x for x in md_arrays.keys() + zfs_arrays if not x in cache_arrays]
    lost_array = [x for x in cache_arrays if not x in md_arrays.keys() + zfs_arrays]

    for array in sorted(md_arrays.keys()):
        if 'active' in md_arrays.get(array):
            all_out += runShell('/sbin/mdadm --detail /dev/' + array.rstrip())

    if os.path.isfile('/sbin/zpool'):
        for pool in sorted(zfs_arrays):
            all_out += runShell('/sbin/zpool status ' + pool)    

    for disk in getBlockDevices():
        toFile(disk + '_list', runShell('/sbin/gdisk -l /dev/' + disk))
        runShell('/sbin/sgdisk -b=' + disk + ' /dev/' + disk)

    if (new_array or lost_array):
        sendMail(all_out) 
        toFile('md_list', all_out)
        doCommit()

