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

# Copyright 2008-2012 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 sys
import re
from os import path
from time import sleep
from calculate.lib.datavars import TableVariable,Variable,VariableError, \
                                   ReadonlyVariable, ReadonlyTableVariable
from calculate.lib.utils.device import (getUdevDeviceInfo,getDeviceType,
                        getPartitionType,getPartitionDevice,getRaidPartitions,
                        getLvmPartitions,getPartitionSize,getUUIDDict,
                        humanreadableSize)
from calculate.lib.utils.files import (listDirectory,pathJoin,readFile,FStab,
                                        readLinesFile,process,getProgPath)
from calculate.install.cl_distr import PartitionDistributive
from calculate.install.fs_manager import FileSystemManager
from itertools import *
import operator


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

class Sizes(object):
    kB = kilobyte = 1000
    K = kibibyte = 1024
    M = mibibyte = K * 1024
    Mb = megabyte = kB * 1000
    G = gibibyte = M * 1024
    Gb = gigabyte = Mb * 1000
    T = tibibyte = G * 1024
    Tb = terabyte = Gb * 1000

    def __getattr__(self,name):
        if name.startswith('from_'):
            return lambda x:x*getattr(Sizes,name[5:])
        elif name.startswith('to_'):
            return lambda x:x/getattr(Sizes,name[3:])
        else:
            raise AttributeError

MINROOTSIZE=7*Sizes.G

class AutopartitionError(Exception):
    """
    Autopartition error
    """
    pass

class AutoPartition:
    """
    Autopartition maker
    """
    def recreateSpace(self,table,device,data,lvm,vgname):
        if lvm:
            self.recreateLvm(table,device,data,vgname)
        else:
            self.recreatePartitionTable(table,device,data)

    def recreatePartitionTable(self,table,device,data):
        """
        """
        mapDispatch = {'dos':self.recreatePartitionTableDos,
                       'gpt':self.recreatePartitionTableGpt}
        if table in mapDispatch:
            mapDispatch[table](device,data)
        else:
            raise AutoPartitionError(
                    _('Autopartitioning for %s is not supported')%table)

    def recreatePartitionTableDos(self,device,data):
        """
        Create DOS partition table by /sbin/fdisk
        """
        NEW_PARTITION_TABLE = "o\n"
        NEW_PRIMARY_PARTITION = "n\np\n\n\n"
        NEW_EXTENDED_PARTITION = "n\ne\n\n"
        NEW_LOGICAL_PARTITION = "n\n\n"
        MAX_SIZE = "\n"
        WRITE_AND_QUIT = "w\nq\n"

        fdiskProg = getProgPath('/sbin/fdisk')
        fdisk = process(fdiskProg,device[0])
        fdisk.write(NEW_PARTITION_TABLE)
        num = 1
        for size in map(lambda x:str(Sizes().to_K(int(x))) \
                                 if x.isdigit() else x,
                    map(operator.itemgetter(3),data)):
            if num == 4:
                fdisk.write(NEW_EXTENDED_PARTITION+MAX_SIZE)
                num += 1
            size = MAX_SIZE if size == "allfree" else "+%sK\n"%size
            if num < 4:
                fdisk.write(NEW_PRIMARY_PARTITION+size)
            else:
                fdisk.write(NEW_LOGICAL_PARTITION+size)
            num +=1
        fdisk.write(WRITE_AND_QUIT)

        fdisk.success()
        self._waitDevice(device[0]+str(num-1))

    def recreatePartitionTableGpt(self,device,data):
        """
        Create GPT partition table by /sbin/gdisk
        """
        NEW_PARTITION_TABLE = "3\no\ny\n"
        NEW_PARTITION = "n\n\n\n"
        NEW_BIOSBOOT_PARTITION = "n\n\n\n%s\nef02\n"
        MAX_SIZE = "\n\n"
        WRITE_AND_QUIT = "w\ny\n"

        fdiskProg = getProgPath('/usr/sbin/gdisk')
        fdisk = process(fdiskProg,device[0])
        fdisk.write(NEW_PARTITION_TABLE)
        num = 1
        biosBootCreated = False
        for size in map(lambda x:str(Sizes().to_K(int(x))) \
                                 if x.isdigit() else x,
                    map(operator.itemgetter(3),data)):
            if num == 4:
                fdisk.write(NEW_BIOSBOOT_PARTITION%"+2M")
                biosBootCreated = True
                num += 1
            if size == "allfree":
                if biosBootCreated:
                    size = MAX_SIZE
                else:
                    size = "-2M\n\n"
            else:
                size = "+%sK\n\n"%size
            fdisk.write(NEW_PARTITION+size)
            num +=1
        if not biosBootCreated:
            fdisk.write(NEW_BIOSBOOT_PARTITION%"")
        fdisk.write(WRITE_AND_QUIT)
        fdisk.success()
        self._waitDevice(device[0]+str(num-1))

    def _waitDevice(self,device):
        for waittime in (0.1,0.2,0.5,1,2,4):
            if path.exists(device):
                return True
            else:
                sleep(waittime)
        raise AutopartitionError(
            _("Failed to found partition %s after creating the partition table")
                %device)

    def _createPhisicalVolumes(self,devices):
        pvCreateProg = getProgPath('/sbin/pvcreate')
        return process(pvCreateProg,"-ff",*devices).success()

    def _createVolumesGroup(self,vgname,disks):
        vgCreateProg = getProgPath('/sbin/vgcreate')
        return process(vgCreateProg,vgname,*disks).success()

    def _createLogicalVolume(self,vgname,lvname,size):
        if size.isdigit():
            size = str(Sizes().to_K(int(size)))
        if size == "allfree":
            sizeparam = "-l100%FREE"
        else:
            sizeparam = "-L%sK"%size
        lvCreateProg = getProgPath('/sbin/lvcreate')
        return process(lvCreateProg,sizeparam,vgname,"-n",lvname).success()

    def _removeVolumeGroup(self,vgname):
        vgRemoveProg = getProgPath('/sbin/vgremove')
        # double remove volume group
        return process(vgRemoveProg,vgname,"-f").success() or \
               process(vgRemoveProg,vgname,"-f").success()

    def clearLvm(self,devices,dv):
        """
        Remove lvm physical volumes from devices
        """
        vgRemoveProg = getProgPath('/sbin/vgremove')
        pvRemoveProg = getProgPath('/sbin/pvremove')
        lvRemoveProg = getProgPath('/sbin/lvremove')
        disks = dv.Select('os_disk_dev',where='os_disk_parent',_in=devices)
        failed = False
        for group, pair in groupby(dv.Select(['os_lvm_vgname','os_lvm_lvname'],
                                             where='os_lvm_pvname',_in=disks,
                                             sort="ASC/1"),
                                   operator.itemgetter(0)):
            for vgname,lvname in pair:
                failed |= self.doubleExec(lvRemoveProg,
                                  "%s/%s"%(vgname,lvname),"-f")
            failed |= self.doubleExec(vgRemoveProg,group,"-f")
        for pvname in list(set(disks)&set(dv.Get('os_lvm_pvname'))):
            failed |= self.doubleExec(pvRemoveProg,pvname,"-ffy")
        return not failed

    def doubleExec(self,*args):
        """
        Running double exec command with 2 seconds interval
        """
        if process(*args).failed():
            sleep(2)
            return process(*args).failed()
        return False

    def clearRaid(self,devices,dv):
        """
        Remove raid from devices
        """
        mdAdmProg = getProgPath('/sbin/mdadm')
        failed = False
        for disktype,grouped in groupby(
                                dv.Select(['os_disk_type','os_disk_dev'],
                                          where='os_disk_parent',
                                          _in=['/dev/sda','/dev/sdb'],
                                          sort="ASC/1"),
                                operator.itemgetter(0)):
            if disktype.endswith('-raid'):
                for disk_type, disk_dev in grouped:
                    failed |= self.doubleExec(mdAdmProg,'-S',disk_dev)
            if "raidmember" in disktype:
                for disk_type, disk_dev in grouped:
                    failed |= self.doubleExec(mdAdmProg,
                                  '--zero-superblock',disk_dev)
        return not failed

    def recreateLvm(self,table,devices,data,vgname):
        """
        Create GPT partition table by /sbin/gdisk
        """
        for device in devices:
            self.recreatePartitionTable(table,[device],[['','','','allfree']])

        disks = map(lambda x:"%s1"%x,devices)
        if not self._createPhisicalVolumes(disks):
            raise AutopartitionError(
                _("Failed to create physical volumes from %s")
                %",".join(devices))

        if not self._createVolumesGroup(vgname,disks):
            raise AutopartitionError(
                _("Failed to create volume group {groupname} from {disks}")
                .format(groupname=vgname,
                        disks=",".join(devices)))

        lvCreateProg = getProgPath('/sbin/lvcreate')
        for disk,size in map(operator.itemgetter(0,3),data):
            lvname = disk.rpartition('/')[2]
            if not self._createLogicalVolume(vgname,lvname,size):
                raise AutopartitionError(
                    _("Failed to create logical volume {name}")
                    .format(name=lvname))
        self._waitDevice('/dev/%s/root'%vgname)

class AutopartitionHelper:
    """
    Helper for autopartiton device and mount point creating
    """
    def deviceOpts(self,listvalue): 
        for i in ("root","data"):
            if i in listvalue:
                beforeVal = i
                break
        else:
            beforeVal = ""
        for line in listvalue:
            if line in ("home","lvm","raid"):
                continue
            if line == beforeVal:
                yield "root2"
            yield line
        if not beforeVal:
            yield "root2"

    def bindOpts(self,listvalue):
        return filter(lambda x:x in ("home",),
               listvalue)

    def mpByOpts(self,value):
        mapMp = {'swap':'swap',
                 'boot':'/boot',
                 'root2':'/',
                 'root':'',
                 'data':'/var/calculate'}
        return mapMp.get(value,'')

    def sourceMpByOpts(self,value):
        mapMp = {'home':'/var/calculate/home'}
        return mapMp.get(value,'')
        
    def targetMpByOpts(self,value):
        mapMp = {'home':'/home'}
        return mapMp.get(value,'')

    def getAutopartitionScheme(self):
        optOrder = {'boot':0,
                    'swap':1,
                    'root':2,
                    'data':3,
                    'home':4}
        return sorted(self.Get('cl_autopartition_scheme'),key=optOrder.get)

    def uncompatible(self):
        if self.Get('cl_autopartition_set') == "off":
            return \
                _("Autopartition options are not available with manual "
                  "partitioning")
        return ""

class VariableHrMemorySize(ReadonlyVariable):
    """
    Memory size in bytes
    """
    type = "int"

    def get(self):
        reMemTotal = re.compile(r'^MemTotal:\s*(\d+)\s*kB$')
        totalMemList = filter(lambda x:x,
                       map(reMemTotal.search,
                       readLinesFile('/proc/meminfo')))
        if totalMemList:
            size = int(totalMemList[0].group(1))*Sizes.K
            return str(size)
        return "0"

    def humanReadable(self):
        return humanreadableSize(self.Get())

class VariableClAutopartitionSwapSize(Variable):
    """
    Swap size
    """
    type = "int"

    def get(self):
        size = self.Get('hr_memory_size')
        if size < Sizes.G:
            size = Sizes.G
        return str(size)

class VariableClAutopartitionDevice(AutopartitionHelper,Variable):
    """
    Device for autopartition
    """
    type = "choice-list"
    element = "selecttable"
    opt = ["-D"]
    metavalue = "DEVICE"
    untrusted = True

    def init(self):
        self.help = _("set the device for autopartition")
        self.label = _("Devices for install")

    def get(self):
        choiceVal = map(lambda x:x[0],self.choice())
        if len(choiceVal) == 1:
            return [choiceVal[0]]
        return []

    def choice(self):
        deviceParentMap = dict(self.ZipVars('os_device_dev','os_device_name'))
        return map(lambda x:(x,"%s (%s)"%(x,
                        deviceParentMap.get(x,_("Unknown")))),
                    self.Get('os_device_dev'))

    def checkNeeded(self,valuelist,usedDevices,agregationType):
        needDevices = list(set(usedDevices) - set(valuelist))
        if needDevices:
            raise VariableError(_("Disks {selecteddisk} are part of "
                  "{agrtype}\nYou need to use {needdisk} as well or "
                  "clear {agrtype} manually")
                 .format(selecteddisk=",".join(
                                     list(set(usedDevices)&set(valuelist))),
                         needdisk=",".join(needDevices),
                         agrtype=agregationType))


    def checkOnLvm(self,valuelist):
        disks = self.Select('os_disk_dev',
                            where='os_disk_parent',_in=valuelist)
        vgroups = self.Select('os_lvm_vgname',
                               where='os_lvm_pvname',_in=disks)
        lvmDisks = self.Select('os_lvm_pvname',
                               where='os_lvm_vgname',_in=vgroups)
        lvmDevices = self.Select('os_disk_parent',where='os_disk_dev',
                                 _in=lvmDisks)
        self.checkNeeded(valuelist,lvmDevices,"LVM")

    def checkOnRaid(self,valuelist):
        disks = self.Select('os_disk_dev',
                            where='os_disk_parent',_in=valuelist)
        raids = filter(None,self.Select('os_disk_raid',
                            where='os_disk_dev',_in=disks))
        raidDisks = self.Select('os_disk_dev',where='os_disk_raid',_in=raids)
        raidDevices = self.Select('os_disk_parent',
                                  where='os_disk_dev',
                                  _in=raidDisks)
        self.checkNeeded(valuelist,raidDevices,"RAID")

    def check(self,valuelist):
        if self.Get('cl_autopartition_set') == "on":
            if not valuelist:
                raise VariableError(_("For autopartition, please select the device"))
            useDisks = self.Select('os_disk_parent',
                                   where='os_disk_mount',ne='')
            for value in valuelist:
                if value in useDisks:
                    raise VariableError(
                        _("Device %s is already in use by the current "
                          "system")%value)
            self.checkOnLvm(valuelist)
            self.checkOnRaid(valuelist)
            if len(valuelist) > 1 and \
                self.Get('cl_autopartition_lvm_set') == 'off':
                raise VariableError(
                    _("You should use LVM to install on more that one device"))
            freeSize = int(self.Get('cl_autopartition_free_size'))
            if freeSize < 0 and (abs(freeSize))/(Sizes.M*100) > 0:
                availSize = int(self.Get('cl_autopartition_device_size'))
                raise VariableError(
                    _("There is not enough space on this device")+"\n"+
                    _("{avail} available, {need} needed").format(
                    avail=humanreadableSize(availSize),
                    need=humanreadableSize(availSize-freeSize)))

class VariableClAutopartitionSet(Variable):
    """
    Using autopartition
    """
    type = "bool"
    value = "off"
    element = "radio"
    opt = ["--autopartition"]

    def init(self):
        self.label = _("Partitions")
        self.help = _("autopartition")

    def choice(self):
        return [("off",_("Use the current partitions")),
                ("on",_("Autopartition"))]

class VariableClAutopartitionScheme(AutopartitionHelper,Variable,AutoPartition):
    """
    Autopartition scheme
    """
    type = "choice-list"
    value = ["swap","root","data"]
    element = "selecttable"
    opt = ["--auto-scheme"]
    metavalue = "AUTOPARTOPTS"

    def init(self):
        self.help = _("autopartition options")
        self.label = _("Autopartition options")

    def choice(self):
        return [
                ("swap",_("Swap partition")),
                ("root",_("Additional root partition")),
                ("data",_("/var/calculate partition")),
                ("home",_("Mount /var/calculate/home to /home")),
                ("boot",_("Separate boot partition")),
                ("lvm",_("Use LVM"))
               ]

class VariableClAutopartitionRootSize(AutopartitionHelper,Variable):
    """
    Root partition size for autopartition
    """
    opt = ["--root-size"]
    metavalue = "SIZE"
    untrusted = True

    def init(self):
        self.label = _("Root partition size")+ " (MB)"
        self.help = _("set the root partition size for autopartition")

    def get(self):
        if self.Get('os_install_linux_system') == 'server':
            size = 1024*20 # in M
        else:
            size = 1024*10 # in M
        deviceSize = self.Get('cl_autopartition_device_size')
        if Sizes().from_M(size) >= deviceSize:
            size = max(Sizes().to_M(deviceSize),Sizes().to_M(MINROOTSIZE))
        return str(size)

    def set(self,value):
        # convert table from value to MB
        sizeMap = {'kB':1/1000.0,
                   'K':1/1024.0,
                   'M':1.0,
                   'Mb':1000/1024.0,
                   'G':1024,
                   'Gb':1000,
                   'T':1024*1024,
                   'Tb':1000*1000}
        value = value.strip()
        reSizeValue = re.compile('^(\d+)\s*(%s)?'%"|".join(sizeMap.keys()))
        res = reSizeValue.search(value)
        if not res:
                return "0"
        intValue = int(res.group(1))
        if res.group(2):
            intValue = intValue * sizeMap[res.group(2)]
        return str(int(intValue))

    def check(self,value):
        if self.Get('cl_autopartition_device') and \
            self.Get('cl_autopartition_set') == "on":
            if int(value) < Sizes().to_M(MINROOTSIZE):
                raise VariableError(
                    _("The root partition should be at least {size}").format(
                        size="7 Gb"))

class VariableClAutopartitionTable(AutopartitionHelper,Variable):
    """
    Partition table for autopartition
    """
    type = "choice"
    value = "gpt"
    opt = ["--partition-table"]
    metavalue = "TABLE"

    def init(self):
        self.label = _("Partition table")
        self.help = _("set the partition table for autopartition")

    def choice(self):
        return [("dos","DOS-type Partition Table"),
                ("gpt","GUID Partition Table (GPT)")]

class VariableClAutopartitionDiskData(ReadonlyTableVariable):
    """
    New partition data for autopart device
    """
    source = ['cl_autopartition_disk_dev',
              'cl_autopartition_disk_mount',
              'cl_autopartition_disk_format',
              'cl_autopartition_disk_size',
              'cl_autopartition_disk_part']

class VariableClAutopartitionLvmSet(ReadonlyVariable):
    """
    Using LVM for autopartition
    """
    type = "bool"

    def get(self):
        return "on" if "lvm" in self.Get('cl_autopartition_scheme') else "off"

class VariableClAutopartitionLvmVgname(Variable):
    """
    Volume group name for LVM autopartition
    """

    def get(self):
        def generateName(startName):
            yield startName
            for i in count(2):
                yield "%s%d"%(startName,i)
        for name in generateName("calculate"):
            disks = self.Select('os_lvm_pvname',where='os_lvm_vgname',eq=name)
            devices = self.Select('os_disk_parent',
                                  where='os_disk_dev',_in=disks)
            if set(devices) <= set(self.Get('cl_autopartition_device')):
                return name

class VariableClAutopartitionDiskDev(ReadonlyVariable,AutopartitionHelper):
    """
    Autopartition virtual disk on device
    """
    type = "list"

    def generateDisks(self,dos=False,devices=[],scheme=[],number=0):
        """
        Generate disks for automount scheme
        """
        number = 1
        for line in self.deviceOpts(scheme):
            # for dos 4 - extension
            # for gpt 4 - for bios_boot
            if number == 4:
                number += 1
            yield "%s%d"%(devices[0],number)
            number += 1

    def generateLvm(self,scheme=[],devices=[],**kwargs):
        vgname = self.Get('cl_autopartition_lvm_vgname')
        for line in map(lambda x:{'root':'root2',
                                  'root2':'root'}.get(x,x),
                    self.deviceOpts(scheme)):
            yield "/dev/%s/%s"%(vgname,line)

    def diskGenerator(self):
        if self.Get('cl_autopartition_lvm_set') == 'on':
            return self.generateLvm
        else:
            return self.generateDisks

    def get(self):
        if self.Get('cl_autopartition_set') == "on":
            scheme = self.getAutopartitionScheme()
            devices = self.Get('cl_autopartition_device')
            if devices:
                res = list(self.diskGenerator()(
                            devices=devices,
                            scheme=scheme,
                            dos=self.Get('cl_autopartition_table')=='dos'))
                return res
        return []

class VariableClAutopartitionDiskMount(ReadonlyVariable,AutopartitionHelper):
    """
    Autopartition mount points
    """
    type = "list"

    def generateMounts(self,scheme):
        """
        Generate mount points for automount scheme
        """
        for line in self.deviceOpts(scheme):
            yield self.mpByOpts(line)

    def get(self):
        if self.Get('cl_autopartition_set') == "on":
            scheme = self.getAutopartitionScheme()
            device = self.Get('cl_autopartition_device')
            if device:
                return list(self.generateMounts(scheme))
        return []

class VariableClAutopartitionDiskFormat(ReadonlyVariable,AutopartitionHelper):
    """
    Autopartition disk filesystem
    """
    type = "list"

    def generateFormat(self,scheme):
        """
        Generate filesystems for automount scheme
        """
        for line in self.deviceOpts(scheme):
            if line == "swap": yield "swap"
            else: yield FileSystemManager.defaultFS['hdd']

    def get(self):
        if self.Get('cl_autopartition_set') == "on":
            scheme = self.getAutopartitionScheme()
            device = self.Get('cl_autopartition_device')
            if device:
                return list(self.generateFormat(scheme))
        return []

class VariableClAutopartitionDiskPart(ReadonlyVariable,AutopartitionHelper):
    """
    Autopartition partition type (primary,extended,logical,gpt)
    """
    type = "list"

    def generatePart(self,scheme,dos):
        """
        Generate part type for automount scheme
        """
        number = 1
        for line in self.deviceOpts(scheme):
            if dos:
                if number < 4: yield "primary"
                else: yield "logical"
                number += 1
            else:
                yield "gpt"

    def get(self):
        if self.Get('cl_autopartition_set') == "on":
            scheme = self.getAutopartitionScheme()
            table = self.Get('cl_autopartition_table')
            if scheme:
                return list(self.generatePart(scheme,
                            self.Get('cl_autopartition_table')=='dos'))
        return []

class VariableClAutopartitionDiskType(ReadonlyVariable,AutopartitionHelper):
    """
    Autopartition partition scheme (simple - disk-partition)
    """
    type = "list"
    
    def get(self):
        if self.Get('cl_autopartition_set') == "on":
            if self.Get('cl_autopartition_lvm_set')=="on":
                diskType = "LVM"
            else:
                diskType = "disk-partition"
            return [diskType]*len(self.Get('cl_autopartition_disk_dev'))
        return []

class VariableClAutopartitionDiskSize(ReadonlyVariable,AutopartitionHelper):
    """
    Autopartition disk size
    """
    type = "list"

    def generateSize(self,scheme,memory,bootsize,rootsize,availsize):
        args = {'swap':memory,
                'root': rootsize,
                'root2': rootsize,
                'boot': bootsize,
               }
        deviceOpts = list(self.deviceOpts(scheme))
        for line in deviceOpts[:-1]:
            availsize -= int(args.get(line,0))
            yield str(args.get(line,0))
        if "root" in deviceOpts[-1] and availsize < MINROOTSIZE:
                yield str(MINROOTSIZE)
        elif availsize < 1*Sizes.G:
            yield str(1*Sizes.G)
        else:
            yield "allfree"

    def get(self):
        if self.Get('cl_autopartition_set') == "on":
            scheme = self.getAutopartitionScheme()
            device = self.Get('cl_autopartition_device')
            availSize = self.Get('cl_autopartition_device_size')
            if device:
                return list(self.generateSize(scheme,
                           self.Get('cl_autopartition_swap_size'),
                           self.Get('cl_autopartition_boot_size'),
                           str(int(self.Get('cl_autopartition_root_size'))
                                   *Sizes.M),int(availSize)))
        return []

    def humanReadable(self):
        allSize = self.Get('cl_autopartition_free_size')
        return map(humanreadableSize,
               map(lambda x:allSize if x == "allfree" else x,
               self.Get()))

class VariableClAutopartitionBootSize(Variable):
    """
    Size of boot partition
    """
    value = str(512*Sizes.M)

class VariableClAutopartitionDeviceSize(ReadonlyVariable):
    """
    Available devices size
    """
    def get(self):
        devices = self.Get('cl_autopartition_device')
        if not devices:
            return '0'
        sizeDevice = map(int,self.Select('os_device_size',
                                 where='os_device_dev',
                                 _in=devices))
        # TODO: remove set 10G
        #return str(1024*1024*1024*10)
        return str(reduce(operator.add,sizeDevice))

class VariableClAutopartitionFreeSize(ReadonlyVariable):
    """
    Freesize of device with current root_size and memory
    """
    type = "int"

    def get(self):
        sizeDevice = int(self.Get('cl_autopartition_device_size'))
        sizes = self.Get('cl_autopartition_disk_size')
        return str(reduce(lambda x,y: (x - int(y)) if y.isdigit() else x,
               sizes,int(sizeDevice)))

class VariableClAutopartitionBindData(ReadonlyTableVariable):
    """
    Autopartition bind data
    """
    source = ['cl_autopartition_bind_path',
              'cl_autopartition_bind_mountpoint']

class VariableClAutopartitionBindPath(ReadonlyVariable,AutopartitionHelper):
    """
    Autopartition bind points
    """
    type = "list"

    def generatePath(self,scheme):
        for line in self.bindOpts(scheme):
            yield self.sourceMpByOpts(line)

    def get(self):
        if self.Get('cl_autopartition_set') == "on":
            scheme = self.getAutopartitionScheme()
            device = self.Get('cl_autopartition_device')
            return list(self.generatePath(scheme))
        return []

class VariableClAutopartitionBindMountpoint(ReadonlyVariable,
                                            AutopartitionHelper):
    """
    Autopartition bind points
    """
    type = "list"

    def generateMountPoint(self,scheme):
        for line in self.bindOpts(scheme):
            yield self.targetMpByOpts(line)

    def get(self):
        if self.Get('cl_autopartition_set') == "on":
            scheme = self.getAutopartitionScheme()
            device = self.Get('cl_autopartition_device')
            return list(self.generateMountPoint(scheme))
        return []
