#-*- coding: utf-8 -*-

# Copyright 2010 Calculate Ltd. http://www.calculate-linux.org
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.

import os
import re
import sys
import time
import traceback
from os import path
from StringIO import StringIO
from time import sleep
from subprocess import PIPE,STDOUT
from shutil import copy2
from calculate.lib.utils.files import (runOsCommand,pathJoin,scanDirectory,
                                       process,listDirectory,STDOUT,checkUtils)
from calculate.lib.utils.common import (appendProgramToEnvFile,
                        removeProgramToEnvFile, getTupleVersion,
                        cmpVersion,getUserPassword,
                        getSupportArch, getInstalledVideo )
from calculate.lib.utils.device import (detectDeviceForPartition,
                        getUdevDeviceInfo, getLvmPartitions, refreshLVM,
                        refreshUdev,countPartitions)
from calculate.lib.cl_vars_share import varsShare
from calculate.lib import cl_overriding
from calculate.lib.utils import ip
from calculate.lib.datavars import VariableError
from datavars import DataVarsInstall, __version__,__app__
from calculate.install.variables.autopartition import (AutopartitionError,
    AutoPartition)
from calculate.install.fs_manager import FileSystemManager

from cl_kernel_utils import KernelConfig,InitRamFs

from calculate.lib.variables.locale import Locale
from calculate.lib.cl_template import Template,TemplatesError,ProgressTemplate
from calculate.lib.datavars import DataVars
from calculate.lib.cl_print import color_print
from cl_distr import (PartitionDistributive,
                DistributiveError, ScratchPartitionDistributive,
                MultiPartitions, FlashDistributive, 
                Distributive)
from calculate.lib.utils.text import tableReport
from calculate.lib.server.utils import dialogYesNo
from subprocess import Popen,PIPE,STDOUT
from itertools import *

class InstallError(Exception):
    """Installation Error"""

from cl_migrate_users import migrate, currentUsers, MigrationError
from calculate.lib.encrypt import encrypt

from calculate.lib.cl_lang import setLocalTranslate
setLocalTranslate('cl_install3',sys.modules[__name__])

class printNoColor:
    def colorPrint(self,attr,fg,bg,string):
        sys.stdout.write(string)

class cltCopy(scanDirectory):
    """Copy clt files

    Example:
    cltCopier = cltCopy(target="/targetdir")
    # copy all clt files from etc directory to /targetdir/etc
    cltCopier.performCopy('/etc')
    """
    def __init__(self,target='/tmp'):
        self.target = target

    performCopy = scanDirectory.scanningDirectory

    def processingFile(self,pathname,prefix):
        try:
            if pathname.endswith(".clt"):
                targetDir = pathJoin(self.target,path.dirname(pathname))
                if not path.exists(targetDir):
                    os.makedirs(targetDir)
                copy2(pathname,pathJoin(self.target,pathname))
        except Exception,e:
            raise InstallError(_("Failed to copy '%(src)s' to '%(dst)s'")%
                    {'src':pathname,'dst':pathJoin(self.target,pathname)})
        return True

class otherfilesCopy(scanDirectory):
    """Copy some files

    Example:
    otherfilesCopier = otherfilesCopy(target="/targetdir")
    # copy need files from etc directory to /targetdir/etc
    otherfilesCopier.performCopy('/etc')
    """
    def __init__(self,target='/tmp',reTest=".*"):
        self.target = target
        self.reTest = re.compile(reTest,re.S)

    performCopy = scanDirectory.scanningDirectory

    def processingFile(self,pathname,prefix):
        try:
            if self.reTest.search(pathname):
                targetDir = pathJoin(self.target,path.dirname(pathname))
                if not path.exists(targetDir):
                    os.makedirs(targetDir)
                copy2(pathname,pathJoin(self.target,pathname))
        except Exception,e:
            raise InstallError(_("Failed to copy '%(src)s' to '%(dst)s'")%
                    {'src':pathname,'dst':pathJoin(self.target,pathname)})
        return True

class Install(color_print):
    """Primary class for templates appling and system installation"""

    def __init__(self):
        self.clVars = None
        self.clTempl = None
        self.listDisksOptions = []
        self.listBindsOptions = []
        self.listSwapsOptions = []
        self.startMessage = ""
        self.lenStartMessage = 0
        self.stdoutHide = None
        self.stderrHide = None
        # refresh information about LVM
        refreshLVM()
        # refresh information about device in udevadm info
        refreshUdev()


    def setNoColor(self):
        self.color = False

    def initVars(self,datavars=None):
        """Primary initialization of variables"""
        if not datavars:
            self.clVars = DataVarsInstall()
            self.clVars.importInstall()
            self.clVars.flIniFile()
        else:
            self.clVars = datavars


    def canInstallGrub2(self,target):
        """Check that system has grub2 in current and installed system"""
        if self.clVars.Get('os_grub2_path'):
            return bool(
                   filter(lambda x:x.startswith('grub-1.99'),
                   listDirectory('/var/db/pkg/sys-boot')))
        return False

    def prepareBoot(self,targetDistr):
        """Prepare system for boot"""
        if self.clVars.Get('os_install_root_type') == "flash":
            self.installSyslinuxBootloader(targetDistr)
        else:
            if self.canInstallGrub2(targetDistr):
                self.installGrub2Bootloader(targetDistr)
            else:
                self.installLegacyGrubBootloader(targetDistr)

    def closeClTemplate(self):
        if self.clTempl:
            if self.clTempl.cltObj:
                self.clTempl.cltObj.closeFiles()
            self.clTempl.closeFiles()
            self.clTempl = None

    def applyTemplatesStartup(self):
        """Apply templates for root of system."""
        #self.clVars.Set("cl_root_path","/", True)
        self.clVars.Set("cl_chroot_path","/", True)
        templates_locate = self.clVars.Get('cl_templates_locate')

#        cltObj = True if 'clt' in templates_locate else False
        dirs_list, files_list = ([],[])
        useClt = "clt" in templates_locate
        self.clVars.Set("cl_template_path",
            map(lambda x:x[1],
            filter(lambda x:x[0] in templates_locate,
            zip(self.clVars.Get('cl_template_location'),
                self.clVars.Get('cl_template_path')))),
            True)

        self.clTempl = ProgressTemplate(self.setProgress, self.clVars,
                                        cltObj=useClt,
                                        cltFilter=False)

        dirsFiles = self.clTempl.applyTemplates()
        try:
            if self.clTempl.getError():
                self.printERROR(self.clTempl.getError())
                return False
        except AttributeError:
            pass
        return dirsFiles

    def applyTemplatesFlash(self,directory):
        """Apply templates for root of system."""
        #self.clVars.Set("cl_root_path",directory, True)
        self.clVars.Set("cl_chroot_path","/", True)
        self.clVars.Set("cl_chroot_grub","/", True)
        self.clVars.Set("cl_root_path",directory, True)
        self.clTempl = ProgressTemplate(self.setProgress,self.clVars,
                                        cltObj=False)
        dirsFiles = self.clTempl.applyTemplates()
        if self.clTempl.getError():
            raise InstallError(self.clTempl.getError())
        else:
            return dirsFiles

    def applyTemplates(self,directory,grubDirectory):
        """Apply templates for root of system."""
        self.clVars.Set("cl_chroot_path",directory, True)
        self.clVars.Set("cl_chroot_grub",grubDirectory, True)
        clTemplateCltPath = \
            filter(lambda x:path.exists(x),
            map(lambda x:pathJoin(directory,x),
            self.clVars.Get('cl_template_clt_path')))
        self.clVars.Set('cl_template_clt_path',clTemplateCltPath,True)
        self.clTempl = ProgressTemplate(self.setProgress,self.clVars,
                                        cltFilter=False)
        dirsFiles = self.clTempl.applyTemplates()
        if self.clTempl.getError():
            raise InstallError(self.clTempl.getError())
        else:
            return dirsFiles

    def checkDuplicate(self,datalist,name,key=lambda x:x):
        """Check on duplicate and print error"""
        keydata = map(key,datalist)
        dupKey = set(filter(lambda x:keydata.count(x)>1, keydata))
        if dupKey:
            self.printERROR(_("Duplicated {keyname}: {keylist}").format(
                                keyname=name,keylist=",".join(dupKey)))
            return False
        return True

    def setVarList(self,varsList,matrix):
        """Set matrix to vars"""
        if not matrix:
            map(lambda x:self.clVars.Set(x,[],True),varsList)
        else:
            map(lambda x:self.clVars.Set(x[0],list(x[1]),True),
            zip(varsList,zip(*matrix)))

    def setDisks(self):
        """
        TODO: remove check grub2_path in disk variables
        """
        try:
            if not self.clVars.Get('os_grub2_path'):
                self.checkForLegacyGrub()
            else:
                self.checkForGrub2()
        except InstallError,e:
            error.append(e)
        return True

    def setUsers(self,listUsers,autologinUser):
        """Set users data (migration)"""
        if autologinUser == "none":
            autologinUser = ""
        if autologinUser or autologinUser == "":
            self.clVars.Set('cl_autologin', autologinUser, force=True)
        else:
            autologinUser = self.clVars.Get('cl_autologin')
        autologinUser = [autologinUser] if autologinUser else []
        listUsers = listUsers or []
        if listUsers or autologinUser:
            migrateUsers = list(set(listUsers+autologinUser))
            migrateUsers.sort()
            migrateUsers = ["root"] + filter(lambda x: x!="root", migrateUsers)
            self.clVars.Set('cl_migrate_user', migrateUsers, force=True)
        return True


    def setInstallOptions(self, listDisks, listBinds, listSwaps, listUsers,
                          autologinUser, brDisk):
        """Set install options (set users, disks and boot record"""
        try:
            if self.setUsers(listUsers,autologinUser) and \
                self.setDisks(listDisks,listBinds,listSwaps) and \
                self.setBR(brDisk):
                return self.createListOptions()
            else:
                return False
        except InstallError,e:
            self.printERROR(str(e).strip())
            return False

    def getDeviceByField(self,field,value, secondPrefix="os_disk"):
        """Get device by fields (install load format uuid grub part name)"""
        return self.getFieldByField('dev',field,value,
                                    secondPrefix=secondPrefix)

    def getFieldByField(self,resField,field,value, firstPrefix="os_disk",
                        secondPrefix="os_disk"):
        res = filter(lambda x: x[1] == value,
                zip(self.clVars.Get('%s_%s'%(firstPrefix,resField)),
                    self.clVars.Get('%s_%s'%(secondPrefix,field)))) or\
                    [("","")]
        return res[0][0]

    def getFieldByDevice(self,field,device):
        """Get value of field by device"""
        device_hash = self.clVars.Get('os_disk_hash')
        if device in device_hash and field in device_hash[device]:
            return device_hash[device][field]
        else:
            return ""

    def setActivePartition(self,partition):
        """Change partition id, specified by systemid,set active"""
        deviceName = detectDeviceForPartition(partition)
        if deviceName is None:
            raise DistributiveError(
                _("Failed to determine the parent device for %s")%partition)
        # device hasn't any partition
        elif deviceName == "":
            return True
        fdiskProg, gdiskProg, partedProg = checkUtils('/sbin/fdisk',
                    '/usr/sbin/gdisk','/usr/sbin/parted')

        disk = self.clVars.Select('os_install_disk_parent',
                                  where='os_install_disk_dev',eq=partition,
                                  limit=1)
        parttable = self.clVars.Select('os_device_table',where='os_device_dev',
                                  eq=disk,limit=1)
        partitionNumber = \
            getUdevDeviceInfo(name=partition).get('ID_PART_ENTRY_NUMBER','')
        devicePartitionCount = countPartitions(deviceName)
        if deviceName and not partitionNumber:
            raise DistributiveError(
                _("Failed to determine the partition number for %s")%partition)
        bootFlag = "boot" if parttable == "dos" else "legacy_boot"
        if parttable == "dos":
            fdisk = process(fdiskProg, "-l",deviceName)
            DEVICENUM,AFLAG = 0,1
            changeActive = \
                map(lambda x:x[DEVICENUM],
                filter(lambda x:x[DEVICENUM] != partitionNumber and \
                                x[AFLAG] == "*" or \
                                x[DEVICENUM] == partitionNumber and \
                                not x[AFLAG] == "*",
                list(map(lambda x:[str(x[0]),x[1][1].strip()],
                # enumerate partitions
                enumerate(filter(None,
                map(lambda x:x.split()[:2],
                # drop string before information about partitions
                dropwhile(lambda x:not x.lstrip().startswith("Device"),
                fdisk.readlines()))))))[1:]))
        else:
            parted = process(partedProg, "-m",deviceName,"print")
            DEVICENUM,FLAGS = 0,6
            changeActive = \
                map(lambda x:x[DEVICENUM],
                filter(lambda x:x[DEVICENUM] != partitionNumber and \
                                bootFlag in x[FLAGS].strip(';').split(', ') or \
                                x[DEVICENUM] == partitionNumber and \
                                not bootFlag in x[FLAGS].strip(';').split(', '),
                filter(lambda x:len(x)>=7,
                map(lambda x:x.split(':'),
                parted.readlines()[2:]))))
        if not changeActive:
            return True
        if parttable == "dos":
            pipe = Popen([fdiskProg,deviceName],
                stdin=PIPE, stdout=PIPE,stderr=PIPE)
            for partnum in changeActive:
                pipe.stdin.write("a\n%s\n"%partnum)
            pipe.stdin.write("w\n")
            pipe.stdin.close()
            pipe.wait()
        elif parttable == "gpt":
            pipe = Popen([gdiskProg,deviceName],
                stdin=PIPE, stdout=PIPE,stderr=PIPE)
            if devicePartitionCount > 1:
                pipe.stdin.write("x\n")
                for partnum in changeActive:
                    pipe.stdin.write("a\n%s\n2\n\n"%partnum)
                pipe.stdin.write("w\nY\n")
            else:
                pipe.stdin.write("x\na\n2\n\nw\nY\n")
            pipe.stdin.close()
            pipe.wait()
        for waittime in (0.1,0.2,0.5,1,2,4):
            if path.exists(partition):
                return True
            else:
                sleep(waittime)
        raise InstallError(
            _("Failed to find partition %s after changing the activity")%
                partition)

    def installSyslinuxBootloader(self,target):
        """Install boot loader by syslinux

        Perform syslinux installation to flash.
        """
        if not self.clVars.Get('os_install_mbr'):
            return
        ddProcess = process("/bin/dd","if=/usr/share/syslinux/mbr.bin",
                            "of=%s"%self.clVars.Get('os_install_mbr')[0],
                            stderr=STDOUT)
        if ddProcess.failed():
            raise DistributiveError(
                _("Failed to write the master boot record\n%s")%
                            ddProcess.read())
        target.close()
        installRootDev = self.clVars.Get('os_install_root_dev')
        syslinuxProcess = process("/usr/bin/syslinux",
                                  installRootDev, stderr=STDOUT)
        if syslinuxProcess.failed():
            raise DistributiveError(_("Failed to install syslinux\n%s")%
                            syslinuxProcess.read())
        # is partition active
        return self.setActivePartition(self.clVars.Get('os_install_root_dev'))

    def varSelect(self,selField,where="os_disk_dev",eq=""):
        """Select value from os_disk/device matrix"""
        res = filter(lambda x: x[1] == eq,
                zip(self.clVars.Get(selField),
                    self.clVars.Get(where))) or\
                    [("","")]
        return res[0][0]

    def installGrub2Bootloader(self,target):
        """
        Install grub2 boot loader
        """
        for bootPath in ("/boot","/"):
            bootDisk = self.clVars.Select("os_install_disk_dev",
                where="os_install_disk_mount",eq=bootPath,limit=1)
            if bootDisk:
                self.setActivePartition(bootDisk)
                break
        cmdGrubInstall = self.clVars.Get('os_grub2_path')
        if not cmdGrubInstall:
            raise DistributiveError(_("Failed to install the bootloader"))

        for mbrDisk in self.clVars.Get('os_install_mbr'):
            process("sync").success()
            if self.clVars.Get('os_install_scratch') == "on" and \
                self.clVars.Get('cl_action') != "system":
                prefixBoot = "/mnt/scratch"
            else:
                prefixBoot = "/"
            grubProcess = process(cmdGrubInstall,
                              "--boot-directory=%s"%pathJoin(prefixBoot,
                              target.getBootDirectory()),
                              mbrDisk, "-f", stderr=STDOUT,envdict=os.environ)
            if grubProcess.failed():
                raise DistributiveError(_("Failed to install the bootloader"))

    def installLegacyGrubBootloader(self,target):
        """
        Install legecy grub boot loader

        Perform grub installation to disk, which has root partition
        """
        cmdGrub = varsShare().getProgPath('/sbin/grub')
        if not cmdGrub:
            raise DistributiveError(_("Failed to install the bootloader"))
        grubProcess = process(cmdGrub,
               "--device-map=%s/boot/grub/device.map"%target.getDirectory(),
               "--batch",stderr=STDOUT)
        bootDisk = self.Select('os_install_disk_grub',
                              where='os_install_disk_mount',
                              _in=('/','/boot'),
                              sort='DESC',limit=1)
        if not bootDisk:
            raise DistributiveError(_("Failed to determine the boot disk"))
        self.setActivePartition(bootDisk)
        for mbrDisk in self.clVars.Get('os_install_mbr'):
            mbrDiskNum = self.Select("os_device_map",
                                     where="os_device_dev",
                                     eq=mbrDisk)
            if not mbrDiskNum and mbrDiskNum != 0:
                raise DistributiveError(_("Failed to determine mbr"))
            for line in ("root (hd%s)"%bootDisk,
                         "setup (hd%d)"%mbrDiskNum,
                         "quit"):
                grubProcess.write("%s\n"%line)
            if grubProcess.failed():
                raise DistributiveError(_("Failed to install the bootloader"))

    def setupOpenGL(self):
        """Setup opengl for current video driver"""
        defaultGL = "xorg-x11"
        pathGlModules = path.join(self.clVars.Get('cl_chroot_path'),
                                 'usr/lib/opengl')
        openGLenv = path.join(self.clVars.Get('cl_chroot_path'),
                                 'etc/env.d/03opengl')

        openGlMods = filter(lambda x:x != "global",
                               listDirectory(pathGlModules))
        mapGL_drivers = {'fglrx':"ati" if "ati" in openGlMods
                                 else defaultGL,
                         'nvidia':"nvidia" if "nvidia" in openGlMods
                                  else defaultGL}
        x11_driver = self.clVars.Get('os_install_x11_video_drv')
        if x11_driver in mapGL_drivers:
            newModuleName = mapGL_drivers[x11_driver]
        else:
            newModuleName = defaultGL
        curModuleName = map(lambda x:x.strip().rpartition('=')[-1].strip('"\''),
                   filter(lambda x: x.startswith("OPENGL_PROFILE="),
                          open(openGLenv,'r')))
        curModuleName = curModuleName[-1] if curModuleName else ""
        if curModuleName == newModuleName:
            return True
        res,errmes = runOsCommand('eselect opengl set %s'%newModuleName)
        if res == 0:
            return True
        else:
            return False

    def checkVideoDriver(self):
        """Check video driver and install nvidia driver"""
        binPackages = '/usr/portage/packages'
        nvidiaBinDrivers = path.join(binPackages,'x11-drivers')
        if self.clVars.Get('hr_video') != 'nvidia' or \
            not path.exists(nvidiaBinDrivers):
            return True
        maskFile = '/etc/portage/package.mask'
        nvidiaMaskFile = path.join(maskFile,'nvidia-drivers')
        # convert file package.mask to directory package mask
        if path.isfile(maskFile):
            os.rename(maskFile,maskFile+"2")
            os.mkdir(maskFile,mode=0755)
            os.rename(maskFile+"2",path.join(maskFile,"default"))
        if path.exists(nvidiaMaskFile):
            curNvidiaMask = open(nvidiaMaskFile,'r').read().strip()
        else:
            curNvidiaMask = ""

        maskNvidia = self.clVars.Get('os_nvidia_mask')
        if maskNvidia == curNvidiaMask:
            return True
        self.printByResult(True)
        self.startTask(_("Driver installation for %s")%
            self.clVars.Get('hr_video'))
        open(nvidiaMaskFile,'w').write(maskNvidia)
        return True

    def configureSystem(self,autologin):
        """configuring the current system"""
        configureMessage = _("Configuring the system")
        error = None
        keyInter = None
        try:
            try:
                if autologin:
                    objUsers = currentUsers()
                    if not objUsers.hasUsers(autologin):
                        self.printERROR(_("User %s does not exist")%autologin)
                    else:
                        self.setUsers([],autologin)
                # install this package
                self.installPackage()
                self.printMessageForTest(configureMessage)
                self.printByResult(self.applyTemplatesStartup())

                linuxShortname = self.clVars.Get('os_install_linux_shortname')
                # install desktop package
                if linuxShortname in ("CLD","CLS","CLDG","CLDX"):
                    desktopLib = '/usr/lib/calculate-2.2/calculate-desktop/pym'
                    if path.exists(desktopLib):
                        sys.path.insert(0, path.abspath(desktopLib))
                        from cl_desktop import desktop
                        self.printMessageForTest(
                             _("calculate-desktop enabled "
                               "for package configuration"))
                        self.hideStdout()
                        objDesktop = desktop()
                        objDesktop.createClVars()
                        res = objDesktop.installProg()
                        self.showStdout()

                # install client package
                if linuxShortname in ("CLD","CLDG","CLDX"):
                    clientLib = '/usr/lib/calculate-2.2/calculate-client/pym'
                    if path.exists(clientLib):
                        sys.path.insert(0, path.abspath(clientLib))
                        from cl_client import client
                        self.printMessageForTest(
                         _("calculate-client enabled "
                           "for package configuration"))
                        objClient = client()
                        objClient.createClVars()
                        self.hideStdout()
                        if hasattr(objClient,"updateEnvFiles") and \
                            objClient.updateEnvFiles():
                            objClient.clVars.flIniFile()
                        res = objClient.installProg(onlyEnv=True)
                        self.showStdout()
                        self.printByResult(res)

                pathGlModules = path.join(self.clVars.Get('cl_chroot_path'),
                                         'usr/lib/opengl')
                self.printMessageForTest(_("Checking the video driver"))
                self.printByResult(self.checkVideoDriver())
                if path.exists(pathGlModules):
                    self.printMessageForTest(_("Configuring OpenGL"))
                    self.printByResult(self.setupOpenGL())
                # if change video driver, then restore initramfs
                installDrv = self.clVars.Get('os_install_x11_video_drv')
                if self.clVars.Get('os_x11_video_drv') != installDrv:
                    initrdPath = path.join('/boot',
                                self.clVars.Get('os_install_initrd'))
                    initrdInstallPath = path.join('/boot',
                                self.clVars.Get('os_install_initrd_install'))
                    needMod = {'radeon':'radeon',
                               'intel':'i915',
                               'nouveau':'nouveau'}.get(installDrv,'uvesafb')
                    if not InitRamFs(initrdPath).isModuleInside(needMod):
                        self.printMessageForTest(_("Restoring initramfs"))
                        self.printByResult(InitRamFs(initrdInstallPath)\
                                .cleanInitRamFs(initrdPath))
                oldXdrv = self.clVars.Get('os_x11_video_drv')
                newXdrv = self.clVars.Get('os_install_x11_video_drv')
                if oldXdrv != newXdrv:
                    kmsDrv = ("radeon","i915","intel","nouveau","ati")
                    self.defaultPrint("\n")
                    if oldXdrv in kmsDrv or newXdrv in kmsDrv:
                        self.defaultPrint(
                            _("To apply the changes, reboot the system")
                            +".\n")
                    else:
                        self.defaultPrint(
                            _("To apply the changes, restart the X server")
                            +".\n")
            except (InstallError,VariableError,DistributiveError),e:
                error = e
            except Exception,e:
                error = ""
                for i in apply(traceback.format_exception, sys.exc_info()):
                    error += i
            except KeyboardInterrupt:
                keyInter = True
        except KeyboardInterrupt:
            keyInter = True
        self.showStdout()
        if keyInter:
            if self.startMessage:
                self.printByResult(False)
            self.defaultPrint("\n")
            self.printWARNING(_("System configuration interrupted"))
            error = _("Configuration manually interrupted")
        if self.startMessage:
            self.printByResult(False)
        if error:
            for line in filter(lambda x: x,str(error).split('\n')):
                self.printERROR(line)
            self.printERROR(_("System configuration failed"))
            return False
        return True

    def cleanInitrd(self):
        """Clean initrd from needless modules"""
        # get path to initrd and initrd-install in new system
        # (/boot/initramfs-...-install,/boot/initramfs-...)
        chrootPath = path.join(self.clVars.Get('cl_chroot_path'),'boot')
        initrdPath = path.join(chrootPath,self.clVars.Get('os_install_initrd'))
        initrdInstallPath = path.join(chrootPath,
            self.clVars.Get('os_install_initrd_install'))
        if path.exists(initrdInstallPath):
            copy2(initrdInstallPath,initrdPath);
        return True

    def afterCopyHDDinstall(self,targetDistr):
        """Action performed after distributive copy for hdd install"""
        # copy clt files from current system
        self.startTask(_("Copying clt templates to the new system"))
        cltCpy = cltCopy(target=targetDistr.getDirectory())
        for directory in self.clVars.Get('cl_template_clt_path'):
            cltCpy.performCopy(directory)
        self.endTask()

        self.startTask(_("Copying configuration files to the new system"))
        fileMask = r"/etc/udev/rules\.d/70-persistent-net\.rules"
        if self.clVars.Get('os_root_type') != "livecd":
            fileMask = \
                "(%s|/etc/ssh/ssh_host_.*|/root/.ssh/(id_.*|known_hosts))"% \
                    fileMask
        fileCpy = otherfilesCopy(target=targetDistr.getDirectory(),
                                 reTest=fileMask)
        fileCpy.performCopy('/etc')
        if path.exists('/root/.ssh'):
            fileCpy.performCopy('/root/.ssh')
        self.endTask()

        # optimize initrd
        self.clVars.Set("cl_chroot_path",targetDistr.getDirectory(), True)
        self.startTask(_("Creating a new initrd file"))
        self.cleanInitrd()
        self.endTask("")

        # join templates
        self.startTask(_("Updating the configuration"),progress=True)
        self.applyTemplates(targetDistr.getDirectory(),
                    targetDistr.getBootDirectory()[:-4])
        # mount bind mount points

        self.endTask()

        self.startTask(_("Post-install configuration"))
        targetDistr.postinstallMountBind()
        self.endTask()

        # migrate users
        self.startTask(_("Migrating users"))
        objMigrate = migrate(targetDistr.getDirectory())
        if not objMigrate.migrate(self.clVars.Get('cl_migrate_data'),
                          self.clVars.Get('cl_migrate_root_pwd'),[],[],):
            raise InstallError(_("Failed to migrate users onto the new system"))
        self.endTask()

    def setupVideo(self):
        """Setup video"""
        pathGlModules = path.join(self.clVars.Get('cl_chroot_path'),
                                 'usr/lib/opengl')
        self.startTask(_("Checking the video driver"))
        self.checkVideoDriver()
        self.endTask()
        if path.exists(pathGlModules):
            self.startTask(_("Configuring OpenGL"))
            self.setupOpenGL()
            self.endTask()
        oldXdrv = self.clVars.Get('os_x11_video_drv')
        newXdrv = self.clVars.Get('os_install_x11_video_drv')
        if oldXdrv != newXdrv:
            kmsDrv = ("radeon","i915","intel","nouveau","ati")
            self.defaultPrint("\n")
            if oldXdrv in kmsDrv or newXdrv in kmsDrv:
                self.printWARNING(
                    _("To apply the changes, reboot the system")
                    +".\n")
            else:
                self.printWARNING(
                    _("To apply the changes, restart the X server")
                    +".\n")

    def hideStdout(self):
        if not self.stdoutHide:
            self.stdoutHide = sys.stdout
            self.stderrHide = sys.stderr
            sys.stdout = StringIO()
            sys.stderr = StringIO()
            sys.stdout.fileno = self.stdoutHide.fileno
            sys.stderr.fileno = self.stderrHide.fileno

    def showStdout(self):
        if self.stdoutHide:
            sys.stdout = self.stdoutHide
            sys.stderr = self.stderrHide
            self.stdoutHide = None
            self.stderrHide = None

    def installDesktopClient(self):
        """
        Install desktop and client package
        """
        linuxShortname = self.clVars.Get('os_install_linux_shortname')
        # install desktop package
        if linuxShortname in ("CLD","CLS","CLDG","CLDX"):
            generalLib = '/usr/lib/calculate-2.2/calculate-lib/pym'
            desktopLib = '/usr/lib/calculate-2.2/calculate-desktop/pym'
            if path.exists(generalLib) and path.exists(desktopLib):
                sys.path.insert(0, path.abspath(generalLib))
                sys.path.insert(0, path.abspath(desktopLib))
                from cl_desktop import desktop
                self.startTask(_("calculate-desktop enabled "
                                 "for package configuration"))
                self.hideStdout()
                objDesktop = desktop()
                objDesktop.createClVars()
                if objDesktop.installProg():
                    self.endTask()
                else:
                    self.printERROR()
                self.showStdout()
                sys.path.pop(0)
                sys.path.pop(0)

        # install client package
        if linuxShortname in ("CLD","CLDG","CLDX"):
            generalLib = '/usr/lib/calculate-2.2/calculate-lib/pym'
            clientLib = '/usr/lib/calculate-2.2/calculate-client/pym'
            if path.exists(generalLib) and path.exists(clientLib):
                sys.path.insert(0, path.abspath(generalLib))
                sys.path.insert(0, path.abspath(clientLib))
                from cl_client import client
                self.startTask(
                 _("calculate-client enabled "
                   "for package configuration"))
                objClient = client()
                objClient.createClVars()
                self.hideStdout()
                if hasattr(objClient,"updateEnvFiles") and \
                    objClient.updateEnvFiles():
                    objClient.clVars.flIniFile()
                if objClient.installProg():
                    self.endTask()
                else:
                    self.printERROR()
                self.showStdout()
                sys.path.pop(0)
                sys.path.pop(0)

    def setupSystem(self,variables):
        error = None
        if variables:
            self.clVars = variables
        else:
            self.initVars()
        try:
            target = None
            setupType = self.clVars.Get('cl_setup',humanreadable=True)

            if setupType:
                self.startTask((_("%s are being configured")%
                               setupType).capitalize(),progress=True)
            else:
                self.startTask(_("System configuration"),progress=True)
            res = self.applyTemplatesStartup()
            self.endTask()
            if setupType and self.clVars.Get('cl_setup') == 'network':
                pass
            if not setupType or self.clVars.Get('cl_setup') == 'video':
                self.setupVideo()
            if setupType and self.clVars.Get('cl_setup') == 'users':
                pass
            if self.clVars.Get('cl_setup') == 'boot' and \
                self.clVars.Get('os_root_type') != 'livecd':
                target = self.clVars.Get('cl_image')
                if self.clVars.Get('os_install_mbr'):
                    self.startTask(_("Installing the bootloader"))
                    self.prepareBoot(target)
                    self.endTask()
                root_dev = self.clVars.Select('os_disk_parent',
                                       where='os_disk_mount',
                                       eq='/',limit=1)
                if root_dev:
                    self.startTask(_("Changing the I/O scheduler"))
                    try:
                        schedpath = ("/sys%s/queue/scheduler"%
                          (getUdevDeviceInfo(name=root_dev).get('DEVPATH','')))
                        if path.exists(schedpath):
                            open(schedpath,'w').write(
                                self.clVars.Get('os_install_kernel_scheduler'))
                        self.endTask()
                    except:
                        self.printERROR(_("Unable to change the I/O scheduler"))
                        pass
            if not setupType:
                self.installDesktopClient()
        except KeyboardInterrupt:
            error = _("Configuration manually interrupted")
            self.printERROR(error)
        except (MigrationError,
                TemplatesError,
                InstallError,
                AutopartitionError,
                DistributiveError) as e:
            error = str(e)
            self.printERROR(error)
        except (BaseException),e:
            error = ""
            for i in apply(traceback.format_exception, sys.exc_info()):
                error += i
            self.printERROR(error)
            return False
        finally:
            try:
                self.clVars.close()
                if target:
                    target.close()
            except (MigrationError,
                    TemplatesError,
                    InstallError,
                    AutopartitionError,
                    DistributiveError) as e:
                error = str(e)
                self.printERROR(error)
            except (BaseException),e:
                error = ""
                for i in apply(traceback.format_exception, sys.exc_info()):
                    error += i
                self.printERROR(error)
                return False
        if not error:
            self.printSUCCESS(_("System configured")+"!")
            self.endTask()
            self.endFrame()
        return True

    def installSystem(self,variables):
        """install System by current variable enviroment"""
        error = None
        if variables:
            self.clVars = variables
        else:
            self.initVars()
        try:
            results = []
            self.writeFile()
            # Помещение данных в словарь процессов
            self.briefParams('install_view')
            #self.beginFrame()
            targetDistr = None
            sourceDistr = None
            try:
                #self.clVars.printVars()
                if self.clVars.Get('cl_autopartition_set') == 'on':
                    self.startTask(_("Creating a new partition table"),
                                   progress=True)
                    ap = AutoPartition()
                    devices = self.clVars.Get('cl_autopartition_device')
                    ap.clearLvm(devices,self.clVars)
                    ap.clearRaid(devices,self.clVars)
                    ap.recreateSpace(
                        self.clVars.Get('cl_autopartition_table'),
                        devices,
                        self.clVars.Get('cl_autopartition_disk_data'),
                        self.clVars.GetBool('cl_autopartition_lvm_set'),
                        self.clVars.Get('cl_autopartition_lvm_vgname'))
                    self.endTask()
                targetDistr = self.clVars.Get('cl_target')
                sourceDistr = self.clVars.Get('cl_image')
                self.setTaskNumber(25)
                # cmd options
                if targetDistr.needFormat:
                    self.startTask(_("Formatting the partitions"),progress=True)
                    targetDistr.performFormat()
                    self.endTask()
                # install distributive
                #raise InstallError("Manual stop")
                self.startTask(_("Unpacking the system image to the target"),
                               progress=True,num=18)
                self.clVars.Get('os_grub_conf')
                # set spinner flag
                filesnum = self.clVars.Get('os_install_linux_files')
                if filesnum.isdigit():
                    filesnum = int(filesnum)
                else:
                    filesnum = 0
                targetDistr.installFrom(sourceDistr,
                    callbackProgress=self.setProgress,
                    filesnum=filesnum)
                if self.clVars.Get('os_install_pxe') == 'off':
                    targetDistr.convertToDirectory()
                self.endTask()

                self.clVars.processRefresh()
                if self.clVars.Get('os_install_root_type') != "flash" and \
                    self.clVars.Get('os_install_pxe') == 'off':
                    self.afterCopyHDDinstall(targetDistr)
                else:
                    # join templates
                    if self.clVars.Get('os_install_pxe') == 'on':
                        self.startTask(
                            _("Configuring the PXE install"),progress=True)
                        self.applyTemplatesFlash('/')
                    else:
                        self.startTask(
                            _("Configuring the Flash install"),progress=True)
                        self.applyTemplatesFlash(targetDistr.getDirectory())
                    self.endTask()
                    self.closeClTemplate()
                # change boot config
                if self.clVars.Get('os_install_mbr') and \
                    self.clVars.Get('os_install_pxe') == "off":
                    self.startTask(_("Preparing the system for reboot"))
                    self.prepareBoot(targetDistr)
                self.endTask()
            except EOFError as e:
                error = str(e)
            except (MigrationError,
                    TemplatesError,
                    InstallError,
                    AutopartitionError,
                    DistributiveError) as e:
                error = str(e)
            except Exception as e:
                error = ""
                for i in apply(traceback.format_exception, sys.exc_info()):
                    error += i
            except KeyboardInterrupt,e:
                self.printWARNING(_("Installation interrupted"))
                error = _("Installation manually interrupted")
            if error:
                self.printERROR(error)
            try:
                self.closeClTemplate()
                if sourceDistr and sourceDistr.childs:
                    self.startTask(_("Letting go the source distribution"))
                    sourceDistr.close()
                    self.endTask()
                if targetDistr and targetDistr.childs:
                    self.startTask(_("Unmounting the target system volume"))
                    targetDistr.close()
                    self.endTask()
            except (VariableError,TemplatesError,
                    InstallError,DistributiveError),e:
                error = "%s\n%s" % (str(error),_("Unmounting error"))
                self.printERROR(error)
            except KeyboardInterrupt,e:
                pass
            if error:
                self.printERROR(_("Failed to install the system")+"!")
                return False
            self.printSUCCESS(_("System successfully installed")+"!")
            self.endTask()
            self.endFrame()
            # necessary for correct complete the process
            return True
        except (BaseException),e:
            error = ""
            for i in apply(traceback.format_exception, sys.exc_info()):
                error += i
            self.printERROR(error)
            return False
        finally:
            try:
                self.clVars.close()
            except (BaseException),e:
                error = ""
                for i in apply(traceback.format_exception, sys.exc_info()):
                    error += i
                self.printERROR(error)
                return False
        return True
