#!/usr/bin/python
# -*- coding: utf-8 -*-
#       DESCRIPTION:    operation install method 
#
#       SCRIPT NAME:    ri_oper.py
#
#       MENTOR:         Li Zhi
#
#       AUTHOR:         Ling Fen
#
#       EMAIL:          fling@linx-info.com
#
#       DATE:           2010-09-20
#
#       HISTORY:
#       REVISOR         DATE                    MODIFICATION
#       Ling Fen        2010-09-04              create

import ri_data
import ri_dep
import os,sys,time
import subprocess
import signal
sys.path.append('../new_partition/')
import partition_data as p_d

display_operation = None
display_sub_operation = None
display_scale = None
language = 'english'

class Rate:
    ''' class for installing progress, value is from 0 to 100'''
    value = 0
    @staticmethod
    def increase(n, max=100):
        if Rate.value+n <= max:
            Rate.value += n
    @staticmethod
    def set(n):
        Rate.value = n

class Operation:
    ''' This is a common base class for all install operations.
    The instances of its derived class should have the following
    data members:
    chinese_name - operation name
    english_name - operation name
    script       - shell script name
    return_value - shell script return value
    score        - an abstract value, for example score is 5,
    steps        - the subprocess of the number of runs
    current_step         - the subprocess run one time
    total is 100, if this operations is completed, 5% work is done.

    and the following methods:
    get_arguments - prepare arguments for script
    get_stdin     - prepare stdin for script
    install       - install progress '''

    def __init__(self,e,c,s,scr):
        ''' Operation(base class) init method
    return_value is a dict, key is return code, value is a tuple, first
    element is English, second element is Chinese.'''
        self.return_value = {0:('Success', u'成功'), \
                1:('Argument error',u'参数错误'), \
                2:("Deivce node doesn't exist",u"设备节点不存在"),\
                3:("File system hasn't been implemented",u'文件系统没有实现'),\
                }
        self.english_name = e
        self.chinese_name = c
        self.script       = s
        self.score        = scr
        self.steps        = 1
        self.current_step = 0

    def install(self):
        if display_operation:
            display_operation(language == 'chinese' and self.chinese_name or self.english_name)

        max = Rate.value+self.score <100 and Rate.value+self.score or 100
        process = subprocess.Popen("./%s " %self.script + ' '.join(self.get_arguments()),\
                stdin=subprocess.PIPE,stdout=subprocess.PIPE, shell=True, \
                preexec_fn=self.sigpipe_handle, \
                cwd="%s/../operation" %os.path.split(os.path.realpath(__file__))[0])
        process.stdin.write(self.get_stdin())
        process.stdin.close()
        while True:
            line = process.stdout.readline()
            if not line:
                break
            if line[0] == '@' and self.current_step < self.steps:
                self.current_step += 1
                if display_sub_operation:
                    display_sub_operation(line[1:])
                Rate.increase(float(self.score)/self.steps, max)
                if display_scale: display_scale()
        ret = process.wait()
        Rate.set(max)
        if display_scale: display_scale()
        return ret

    
    def sigpipe_handle(self):
        signal.signal(signal.SIGPIPE,signal.SIG_DFL) 

    def get_arguments(self):
        return []
    def get_stdin(self):
        return ''
    @staticmethod
    def getarch():
        cmd='uname -m'
        result = subprocess.Popen(cmd,stdout=subprocess.PIPE,shell=True)
        arch = result.stdout.readline().rstrip()
        return arch 

class MakePartitions(Operation):
	'''class for make partitions'''
	def __init__(self, scr):
		Operation.__init__(self, 'Make partition', u'磁盘分区', 'partition_disks.sh', scr)

	def get_stdin(self):
		args = ' '
		n = 0
		disks =  p_d.Partition.dict.keys()
		disks.sort()
		for d in disks:
			if p_d.Partition.dict[d]['partition_table'] == '':
				continue
			if p_d.Partition.dict[d]['from_os'] == 'no':
				args += '%s mklabel %s\n' %(d, p_d.Partition.dict[d]['partition_table'])
			partitions = p_d.sort_partitions(p_d.Partition.dict, d, 'partition')
			for p in partitions:
				fs_type = ''
				p_type = ''
				flags = ''
				set_flg = ''
				if p_d.Partition.dict[d]['partition'][p]['from_os'] == 'yes':
					continue
				#fs_type = p_d.Partition.dict[d]['partition'][p]['filesystem']
				end = p_d.Partition.dict[d]['partition'][p]['end']
				start = p_d.Partition.dict[d]['partition'][p]['start']
				p_type = p_d.Partition.dict[d]['partition'][p]['type']
				partition = p_d.Partition.dict[d]['partition'][p]['num']
				flags = p_d.Partition.dict[d]['partition'][p]['flags']
				#mkpart = "%s mkpart %s %s %s %s" %(d, p_type, fs_type, start, end)
				mkpart = "%s mkpart %s %s %s" %(d, p_type, start, end)
				if flags.strip():
					set_flg = "set %s %s on" %(partition, flags)

				n += 1
				args += "%s %s\n" %(mkpart, set_flg)

		self.steps += n
		return args


class Format(Operation):
    ''' class for format partition '''
    def __init__(self, scr):
        Operation.__init__(self, 'Format partition', u'格式化分区', 'format_partition.sh', scr)
        self.return_value[127] = ('No tool to format partitions', u'没有格式化硬盘分区的工具')

    def get_stdin(self):
		format = ''
		n = 0
		disks =  p_d.Partition.dict.keys()
		disks.sort()
		mds =  ri_data.Raid.dict.keys()
		mds.sort()

		for d in disks:
			partitions = p_d.sort_partitions(p_d.Partition.dict, d, 'partition')
			for p in partitions:
				fs_type = p_d.Partition.dict[d]['partition'][p]['filesystem']
				fmt = p_d.Partition.dict[d]['partition'][p]['format']
				if fmt.strip() == 'yes' and fs_type.strip():
					format += "%s %s\n" %(p, fs_type)
					n += 1
		for m in mds:
			R = ri_data.Raid.dict[m]
			fs_type = R.filesystem
			fmt = R.fmt
			if fmt.strip() == 'yes' and fs_type.strip():
				format += "/dev/%s %s\n" %(m, fs_type)
				n += 1

		self.steps += n
		return format

class MakeRaid(Operation):
    """class for make raid"""
    def __init__(self,scr):
        Operation.__init__(self,"Making a RAID", u"正在制作RAID磁盘阵列", "mkraid_wrapper.sh", scr)
        self.return_value[2] = ('this levelY need more devices',u'制作这个级别的raid需要更多的分区.')
        self.return_value[3] = ('the raid name has exist',u'这个raid已经存在.')
        self.return_value[4] = ('this shell not support this RAID level', u'不能做这个级别的raid.')
        self.return_value[5] = ('level0 donot need spare devices', u'raid0不需要备用分区')
        self.return_value[6] = ('incorrect active and spare devices name, maybe it has been used', u'这个分区已经被做过raid了.')
        self.return_value[7] = ('create or assimble raid device lose', u'制作raid不成功')

    def get_stdin(self):
        args = ''
        n = 0
        for instance in ri_data.Raid.dict.values():
            if instance.from_os == 'no':
                args += '-n /dev/%s -l %s -a %s' %(instance.device, instance.level,','.join([ '/dev/%s' %ac for ac in instance.active_components]))
                if instance.spare_components :
                    args += ' -s %s' % ','.join([ '/dev/%s' %sp for sp in instance.spare_components])
                args +='\n'
                n += 1
        self.steps += n
        return args

class MakeRaidConfigure(Operation):
    """class for make raid configure"""
    def __init__(self,scr):
        Operation.__init__(self,"Generate /etc/mdadm.conf",u"生成raid配置文件","mkraidconf.sh",scr)

    def install(self):
        if ri_data.Raid.dict.values():
            return Operation.install(self)
        else:
            max = Rate.value+self.score <100 and Rate.value+self.score or 100
            Rate.set(max)
            return 0

    def get_arguments(self):
        return ["/etc/mdadm.conf"]

class Mount(Operation):
    """class for mount partition"""
    # dict saved only device and mount point
    dict={}
    def __init__(self,scr):
        Operation.__init__(self,'Mount partition', u'挂载分区', 'mount_partition.sh',scr)

    def get_stdin(self):
        mount = ''
        n = 0
        disks =  p_d.Partition.dict.keys()
        disks.sort()
	mds = ri_data.Raid.dict.keys()
	mds.sort()
        for d in disks:
			partitions = p_d.sort_partitions(p_d.Partition.dict, d, 'partition')
			for p in partitions:
				mp = p_d.Partition.dict[d]['partition'][p]['mount_point']
				fs = p_d.Partition.dict[d]['partition'][p]['filesystem']
				if mp.strip() and fs.strip():
					mount += "%s %s %s\n" %(p, mp, fs)
					n += 1
        for m in mds:
			R = ri_data.Raid.dict[m]
			mp = R.mp
			fs = R.filesystem
			if mp.strip() and fs.strip():
				mount += "/dev/%s %s %s\n" %(m, mp, fs)
				n += 1
			
        self.steps += n
        return mount

class InstallPkg(Operation):
    """class for install packages"""
    def __init__(self,scr):
        Operation.__init__(self,'Install packages',u'安装软件包','install_pkg.sh',scr)
        self.return_value[2]=("Software source directory doesn't exist",u"软件包所在目录不存在")
        self.return_value[3]=("Software package doesn't exist",u"软件包不存在")

    def get_stdin(self):
        pkgname = set(ri_data.Group.get_install_pkgs() + self.get_dependency_pkg())
        self.steps += len(pkgname)
        return '\n'.join(pkgname)+'\n'

    def get_arguments(self):
        return ["-s","/Rocky/packages"]

    def get_dependency_pkg(self):
        ri_dep.dependency_init()
        return ri_dep.dependency_init.dep_dict.keys() + ri_dep.dependency_init.func_dep_list

class ConfigureFstab(Mount):
    """class for configure /etc/fstab"""
    def __init__(self,scr):
        Operation.__init__(self,'Configure /etc/fstab',u'配置/etc/fstab文件','configure_fstab.sh',scr)
        self.return_value[2]=("/etc/fstab doesn't exist",u"/etc/fstab文件不存在")

    def get_stdin(self):
        fstab=''
        disks =  p_d.Partition.dict.keys()
        disks.sort()
        mds =  ri_data.Raid.dict.keys()
        mds.sort()

        for d in disks:
			partitions = p_d.sort_partitions(p_d.Partition.dict, d, 'partition')
			for p in partitions:
				mp = p_d.Partition.dict[d]['partition'][p]['mount_point']
				fs = p_d.Partition.dict[d]['partition'][p]['filesystem']
				if fs.strip() == 'linux-swap':
					fs = 'swap'
					mp = 'swap'
				if mp.strip() and fs.strip():
					fstab += "%s %s %s\n" %(p, mp, fs)
        for m in mds:
			R = ri_data.Raid.dict[m]
			mp = R.mp
			fs = R.filesystem
			if fs.strip() == 'linux-swap':
				fs = 'swap'
				mp = 'swap'
			if mp.strip() and fs.strip():
				fstab += "/dev/%s %s %s\n" %(m, mp, fs)

        # process fstab to sort
        fstab_new = ''
        cmd_t = "echo '%s' | sort -k 2" %fstab
        std_out = os.popen(cmd_t).readlines()
        for l in std_out:
            fstab_new += l
        return fstab_new

class GenerateIssue(Operation):
    """class for generate /etc/issue"""
    def __init__(self,scr):
        Operation.__init__(self,'Generate /etc/issue',u'生成/etc/issue文件','generate_issue.sh',scr)

    def get_arguments(self):
        args=[]
        # FIXME if tag file format is error?
        if os.path.isfile("/tag"):
            fd=open("/tag","r")
            string = fd.read()
            list = string.split('\n')[0].split('-')
            args = ['-v']+[list[0][5:]]+["-a"]+[list[1]]+["-r"]+[list[2]]+['-d']+['-'.join(list[3:])]
        fd.close()
        return args 

class ConfigureNetwork(Operation):
    """class for configure network"""
    def __init__(self,scr):
        Operation.__init__(self,'Configure network',u'配置网络','configure_network.sh',scr)
        self.return_value[2]=("Ip address, network, or geteway is incorrect",u"Ip地址、子网掩码、网关不正确")
        self.return_value[3]=("/etc/hosts, /etc/resolv.conf, /etc/sysconfig/network-devices/ifcfg.template, or /etc/sysconfig/network doesn't exist",\
                u"文件/etc/hosts,/etc/resolv.conf,/etc/sysconfig/network-devices/ifcfg.template或/etc/sysconfig/network文件不存在")


    def get_arguments(self):
        network=''
        data=ri_data.Network
        if data.configuration == "static":
            network+="-t static "
            if data.hostname is not '':
                network+="-h %s "%data.hostname
            if data.domain is not '':
                network+="-d %s "%data.domain
            if data.ip is not '':
                network+="-i %s "%data.ip
            if data.mask is not '':
                network+="-n %s "%data.mask
            if data.gateway is not '':
                network+="-g %s "%data.gateway
            if data.primary_dns is not '':
                network+="-p %s "%data.primary_dns
            if data.secondary_dns is not '':
                network+="-s %s "%data.secondary_dns
        else:
            network+="-t dynamic "
            if data.hostname:
                network+="-h %s "%data.hostname
        return [network]

class MakeServiceAutoBoot(Operation):
    """class for make service autoboot"""
    def __init__(self,scr):
        Operation.__init__(self,"Make service autoboot",u"启动服务器","mk_serv_autoboot.sh",scr)
        self.return_value[2]=("Boot script doesn't exist",u"引导脚本不存在")

    def get_stdin(self):
        serv=''
        n = 0
        for i in ri_data.Service.list:
            if i.start == "yes":
                serv+='%s %s %s %s\n'%(i.package,i.script,i.name,i.number)
                n += 1
        self.steps += n
        return serv
    
class CopyKernel(Operation):
    """class for copy kernel"""
    def __init__(self,scr):
        Operation.__init__(self,"Copy kernel",u"拷贝内核","copy_kernels.sh",scr)
        self.return_value[1]=("kernel directory, modules directory, initrd.gz, or makeinitrd doesn't exist",u"内核目录、模块目录、initrd.gz文件、makeinitrd指令不存在")

class ExecFinishInstall(Operation):
    """class for exec finish install"""
    def __init__(self,scr):
        Operation.__init__(self,"Exec finish_install script",u"安装完成执行的脚本","exec_finish_install.sh",scr)

    def get_stdin(self):
        return "99finish_install.sh\n"

class BootLoader(Operation):
    """class for bootloader"""
    def __init__(self,scr):
        Operation.__init__(self,"Install bootloader",u"安装引导程序","install_bootloader.sh",scr)
        self.return_value[2]=("Bootloader type hasn't been specified",u"没有指定引导程序的类型")
        self.return_value[3]=("Bootloader hasn't been implemented",u"没有实现bootloader")
        self.return_value[127]=("No tool to bootloader. E.g grub",u"没有安装引导程序的工具。如grub2等")

    def get_arguments(self):
        if self.getarch() == "ppc64":
            pass
        elif self.getarch() == "ia64": 
            return ["-t elilo"]
        else: 
            return ["-t grub2"]

class ConfigureBootloader(Operation):
    """class for configure bootloader file"""
    def __init__(self,scr):
        Operation.__init__(self,"Configure bootloader",u"配置bootloader","configure_bootloader_cnf.sh",scr)
        self.return_value[2]=("Bootloader type hasn't specified",u"没有指定引导程序的类型")
        self.return_value[3]=("Bootloader hasn't been implemented",u"没有实现bootloader")
        self.return_value[4]=("Bootloader configuration file doesn't exist",u"引导程序配置文件不存在")

    def get_arguments(self):
        list=[]

	if self.getarch() == "ppc64":
            pass
        elif self.getarch() == "ia64": 
            tp = "elilo"
        else: 
            tp = "grub2"
        # FIXME if tag file format is error?
        if os.path.isfile("/tag"):
            fd = open("/tag","r")
            string = fd.read()
            list = string.split('-')
            fd.close()

        #bootloader=['-t',tp,'-o',list[0][5:].split('.')[0]+'.'+list[0][5:].split('.')[1],'-k','"linx_serial=%s vga=791 quiet"' %ri_data.SerialNumber.value.strip()]
	bootloader=['-t',tp,'-k','"linx_serial=%s vga=791"' %ri_data.SerialNumber.value.strip()]

        #for k in Mount.dict.keys():
        #    if '/' == Mount.dict[k]:
        #        bootloader += ['-r',k]
        #    if '/boot' == Mount.dict[k]:
        #        bootloader += ['-b',k]

        return bootloader
