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

# Copyright 2015-2016 Mir Calculate. 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.
from itertools import chain

import sys
from os import path
import os
import re
from calculate.install.distr import DistributiveError, IsoDistributive
from calculate.install.variables.kernel import KernelConfig, VariableInterface
from calculate.lib.utils.dracut import Dracut
from calculate.lib.utils.grub import GrubCommand
from calculate.lib.utils.portage import getSquashList, isPkgInstalled
from calculate.lib.variables.system import RootType
import calculate.update.variables.update as update
from .action import Actions
import linux as vars_linux
import datetime
from calculate.install import distr
import calculate.lib.utils.device as device
from calculate.lib.utils.device import humanreadableSize
from calculate.lib.utils.files import (process, typeFile,
                                       listDirectory,
                                       pathJoin, readFile, check_rw)
from calculate.lib.utils.mount import isMount
from calculate.lib.utils.kernel import InitrdFile
from calculate.lib.utils.text import _u8
from calculate.lib.utils.tools import max_default
from calculate.lib.utils.git import Git
from ..build_storage import BuildStorage, Build
from ..drive_spool import DriveSpool
from .action import Actions as BuilderActions
from calculate.lib.datavars import (Variable, VariableError, ReadonlyVariable,
                                    TableVariable, WRITEABLE,
                                    ReadonlyTableVariable, HumanReadable)
from functools import wraps

_ = lambda x: x

from calculate.lib.cl_lang import setLocalTranslate, getLazyLocalTranslate

setLocalTranslate('cl_builder3', sys.modules[__name__])

__ = getLazyLocalTranslate(_)

def is_action(*available_action, **action_kwargs):
    def decorator(func):
        @wraps(func)
        def _wrapped_func(self, *args, **kw):
            if self.Get('cl_action') in available_action:
                return func(self, *args, **kw)
            else:
                return action_kwargs.get('default_value', '')

        return _wrapped_func

    return decorator


class IsoTypeHelper(VariableInterface):
    """
    Вспомогательный объект для переменных контейнера
    """
    def uncompatible(self):
        if self.GetBool("cl_builder_container_set"):
            return _("This option is not compatible with container image")
        return ""


class ContainerHelper(VariableInterface):
    """
    Вспомогательный объект для переменных контейнера
    """
    def uncompatible(self):
        if not self.GetBool("cl_builder_container_set"):
            return _("This option is not compatible with ISO image")
        return ""

class ImagePathHelper(VariableInterface):
    """
    Создание временного каталога для подготовки образа
    """
    @property
    def base_dn(self):
        return self.Get('cl_builder_iso_base_path')

    @property
    def build_id(self):
        return self.Get('cl_builder_id')

    @property
    def build_id_path(self):
        return self.Get('cl_builder_id_path')

    prefix = ""

    def get(self):
        if self.build_id:
            dn = "%s%s" % (self.prefix, self.build_id_path)
            directory = path.join(self.base_dn, dn)
            new_dn = directory
            for i in range(0, 9999):
                if not path.exists(new_dn):
                    return new_dn
                else:
                    new_dn = "%s.%04d" % (directory, i)
            return new_dn
        return ""

class VariableClBuilderStorage(ReadonlyVariable):
    type = "object"

    def get(self):
        return BuildStorage()


class VariableClBuilderAvailableDev(Variable):
    """
    Список дисков, которые можно использовать для сборки
    """
    type = "list"

    def get(self):
        if self.Get('os_root_type_ext') in RootType.Live:
            return [path.join(Dracut.Workspace, "var/calculate/builder")]
        return []


class VariableClBuilderDeviceSpool(ReadonlyVariable):
    """
    Стэк дисков, которые можно использовать для сборки
    """
    type = "object"

    def get(self):
        ds = DriveSpool(self.Get('cl_builder_available_dev'))
        return ds


class BaseBuildId(Variable):
    """
    Базовый класс для переменной id
    """
    opt = ["--id"]
    metavalue = "ID"

    def init(self):
        self.label = _("Build ID")
        self.help = _("build ID")


class VariableClBuilderVideoDriverPath(Variable):
    """
    Имя файла, содержащего данные об установки драйверов во время загрузки
    """

    def get(self):
        builder_path = self.Get('cl_builder_path')
        return path.join(
            builder_path,
            'var/cache/calculate/video_drivers')


class VariableClBuilderVideodrvSet(IsoTypeHelper, Variable):
    """
    Нужно ли скачивать пакеты в дистрибутив для установки проприетарных
    драйверов во время загрузки
    """
    type = "bool"
    opt = ["--video", "-V"]
    autodetect = False
    default_value = "off"

    def init(self):
        self.label = _("Include proprietary video drivers")
        self.help = _("include proprietary video drivers")

    def get(self):
        if self.autodetect:
            fn = self.Get('cl_builder_video_driver_path')
            return "on" if path.exists(fn) else "off"
        else:
            return self.default_value


class VariableClBuilderSourceFilename(Variable):
    """
    Названия файла исходного дистрибутива
    """
    type = "file"
    element = 'file'
    opt = ["--source"]
    metavalue = "SOURCE"
    untrusted = True
    check_after = ["cl_builder_layered_set"]

    def init(self):
        self.label = _("Source image")
        self.help = _("source image")

    def check(self, isoimage):
        """Set image file"""
        if self.Get('cl_action') in Actions.NewAssemble and not isoimage:
            raise VariableError(_("You need to select a source image"))
        if self.GetBool('cl_builder_layered_set'):
            if not isinstance(self.Get('cl_builder_source'),
                              IsoDistributive):
                raise VariableError(
                    _("Wrong source distributive for layered build"))
        shortname = self.Get('os_builder_linux_shortname')
        build = self.Get('os_builder_linux_build')
        arch = self.Get('os_builder_arch_machine')
        local_arch = self.Get('os_arch_machine')
        if not build or not shortname or not arch:
            raise VariableError(_("Wrong image file"))
        if local_arch == "i686" and arch != local_arch:
            raise VariableError(
                _("Unable to build the system {what} from {local}").format(
                    what=arch, local=local_arch))

    def humanReadable(self):
        fullname = self.Get('os_builder_linux_name')
        subname = self.Get('os_builder_linux_subname')
        ver = self.Get('os_builder_linux_ver')
        if subname:
            subname = " %s" % subname
        if ver:
            ver = " %s" % ver
        arch = self.Get('os_builder_arch_machine')
        build = self.Get('os_builder_linux_build')

        return "{fullname}{ver} {arch} {build}".format(
            fullname="%s%s" % (fullname, subname),
            build=build, ver=ver, arch=arch)

    def get(self):
        if self.Get('os_root_type') == 'livecd':
            return Dracut.IsoLive
        return ""


class VariableClBuilderSource(ReadonlyVariable):
    """
    Объект исходного дистрибутива для разворачивания сборки
    """
    type = "object"

    def get(self):
        """Get image file from distributive repository"""
        try:
            filename = self.Get('cl_builder_source_filename')
            if filename:
                return distr.Distributive.fromFile(filename)
        except distr.DistributiveError:
            pass
        return ""

    def humanReadable(self):
        filename = self.Get('cl_builder_source')
        if filename:
            return filename.getType()
        return filename


class VariableClBuilderPreparePath(Variable):
    """
    Путь по умолчанию для собираемой системы
    """
    value = "/var/calculate/builder"


class VariableClBuilderDiskDev(Variable):
    """
    Диск или директория, куда будет развёрнут образ
    """
    type = "choiceedit"
    untrusted = True
    opt = ["-d", "--disk"]
    metavalue = "DEST"

    def init(self):
        self.label = _("Build files")
        self.help = _("partition or directory intended for build")

    def get_builder_image_dn(self, ds=None):
        base_name = self.Get('cl_builder_id_path')
        if self.Get('os_root_type') != "livecd":
            default_path = self.Get('cl_builder_prepare_path')
            for i in chain((0,), range(2, 9999)):
                if i:
                    dn = "%s.%02d" % (base_name, i)
                else:
                    dn = base_name
                full_dn = path.join(default_path, dn)
                if ((not path.exists(full_dn) or not isMount(full_dn)) and
                            ds is None or
                            isinstance(ds, DriveSpool) and
                            ds.lock_device(full_dn)):
                    return full_dn
            else:
                return ""
        return ""

    def get(self):
        ds = self.Get('cl_builder_device_spool')
        return ds.get() or self.get_builder_image_dn(ds)

    def choice(self):
        devices = self.Select(
            'install.os_disk_dev',
            where='install.os_disk_mount', eq='')
        devices = [x for x in devices if not isMount(x)]
        default_dn = self.get_builder_image_dn()
        if default_dn:
            return [default_dn] + devices
        else:
            return devices

    def check(self, value):
        if not value:
            raise VariableError(
                _("You need to select a destination for build"))
        if value.startswith("/dev"):
            cnDisk = device.udev.get_device_info(
                name=value).get('DEVNAME', value)
            if cnDisk not in self.Get('install.os_disk_dev'):
                raise VariableError(_("Wrong device '%s'") % value)
        if not value.startswith('/'):
            raise VariableError(
                _("Wrong directory '%s'") % value)
        if isMount(value):
            raise VariableError(
                _("Destination '%s' is already in use") % value)


class DiskFreeHelper(ReadonlyVariable):
    """
    Переменные содержащие свободное место на дисках
    """
    type = "int"

    def get_free_for(self, dn):
        try:
            dfdn = dn
            while True:
                dfProcess = process("/bin/df", "--output=avail", dfdn)
                data = dfProcess.readlines()
                if len(data) > 1:
                    return int(data[1].strip()) * 1024
                if dfdn == '/':
                    return 0
                dfdn = path.dirname(dfdn)
        except ValueError:
            return 0
        return 0

    def humanReadable(self):
        value = self.Get()
        if not value:
            value = "0"
        return humanreadableSize(int(value))


class VariableClBuilderDiskSize(DiskFreeHelper):
    """

    """

    def init(self):
        self.label = _("Free disk space")

    @is_action(Actions.Prepare)
    def get(self):
        device = self.Get('cl_builder_disk_dev')
        if device:
            if device.startswith('/dev/'):
                return self.Select('install.os_disk_size',
                                   where='install.os_disk_dev',
                                   eq=device, limit=1) or "0"
            else:
                return str(self.get_free_for(device))
        return "0"


class VariableClBuilderPrepareFreeSize(IsoTypeHelper, DiskFreeHelper):
    """
    Свободное место используемое для подготовки образа
    """

    def init(self):
        self.label = _("Free disk space for ISO building")

    @is_action(Actions.Image)
    def get(self):
        dn = self.Get('cl_builder_iso_base_path')
        return str(self.get_free_for(dn))


class VariableClBuilderImageFreeSize(IsoTypeHelper, DiskFreeHelper):
    """
    Свободное место на диске, где создается iso образ
    """

    def init(self):
        self.label = _("Free disk space for ISO image")

    @is_action(Actions.Image)
    def get(self):
        dn = self.Get('cl_builder_image_filename')
        return str(self.get_free_for(dn))


class VariableClBuilderContainerFreeSize(ContainerHelper, DiskFreeHelper):
    """
    Свободное место на диске, где создается iso образ
    """

    def init(self):
        self.label = _("Free disk space for container image")

    @is_action(Actions.Image)
    def get(self):
        dn = self.Get('cl_builder_image_filename')
        return str(self.get_free_for(dn))


class VariableClBuilderLayeredSet(Variable):
    """
    Сборка будет выполняться в слое
    """
    type = "bool"
    opt = ["--layers"]
    kernel_opt = "CONFIG_OVERLAY_FS"

    def init(self):
        self.help = _("use layers for build")
        self.label = _("Use layers for build")

    def check_kernel_option(self):
        """
        Проверить возможность ядра использовать overlay fs
        """
        return (
            self.kernel_opt in self.Get('install.os_kernel_config'))

    def get(self):
        try:
            self.check_on()
            return "on"
        except VariableError:
            return "off"

    def check_on(self):
        if not self.check_kernel_option():
            raise VariableError(
                _("You need kernel with %s for use layers") % self.kernel_opt)
        if self.Get('cl_builder_disk_dev').startswith('/dev'):
            raise VariableError(
                _("Layers are used for building in a directory only"))
        if isinstance(self.Get('cl_builder_source'),
                      distr.ArchiveDistributive):
            raise VariableError(
                _("Layers are used for building from ISO image"))

    def check(self, value):
        if value == "on":
            self.check_on()


class VariableClBuilderPath(ReadonlyVariable):
    """
    Путь, где будет собираться дистрбутив
    """

    def get(self):
        try:
            image = self.Get('cl_builder_target')
            if image:
                return image.getDirectory()
        except DistributiveError:
            return ""
        return ""


class VariableClBuilderTarget(Variable):
    """
    Объект собираемого дистрибутива
    """
    type = "object"

    def get_create(self):
        builder_disk_dev = self.Get('cl_builder_disk_dev')
        if not builder_disk_dev:
            return ""
        build_id_path = self.Get('cl_builder_id_path')
        mount_dir = path.join(distr.DefaultMountPath.BaseMountPath,
                              build_id_path)
        source = self.Get('cl_builder_source_filename')
        if self.GetBool('cl_builder_layered_set'):
            dist_obj = distr.LayeredDistributive(mount_dir, builder_disk_dev,
                                                 source)
            return dist_obj
        else:
            if builder_disk_dev.startswith('/dev'):
                dist_obj = distr.PartitionDistributive(builder_disk_dev,
                                                       mdirectory=mount_dir)
                return dist_obj
            else:
                dist_obj = distr.DirectoryDistributive(builder_disk_dev,
                                                       mdirectory=mount_dir)
                return dist_obj

    def get_worked(self):
        build = self.Get('cl_builder_build')
        if build:
            dist_obj = build.distributive
            return dist_obj
        return ""

    def get(self):
        action = self.Get('cl_action')
        if action in Actions.NewAssemble:
            return self.get_create()
        elif action in Actions.WorkAssemble:
            return self.get_worked()
        elif action in Actions.BrokenAssemble:
            return self.get_worked()
        else:
            return ""


class VariableClBuilderClearSet(Variable):
    """
    Очистить дистрибутив при отключении сборки
    """
    type = "bool"
    value = "on"
    opt = ["--clear"]

    def init(self):
        self.label = _("Clear after unmount")
        self.help = _("clear data after unmount")


class VariableClBuilderClearPkgSet(Variable):
    """
    Удалять бинарные пакеты при отключении сборки
    """
    type = "bool"
    value = "off"
    opt = ["--clear-pkg"]

    def init(self):
        self.label = _("Clear binary packages after unmount")
        self.help = _("clear binary packages after unmount")


class VariableClBuilderBuild(Variable):
    """
    Объект сборки
    """
    type = "object"

    def get_create(self):
        distr = self.Get('cl_builder_target')
        if not distr:
            return ""
        storage = self.Get('cl_builder_storage')
        buildid = self.Get('cl_builder_id')
        build = Build(buildid, distr, storage)
        return build

    def get_worked(self):
        storage = self.Get('cl_builder_storage')
        buildid = self.Get('cl_builder_id')
        build = storage.get_build(buildid)
        if build and build.distributive:
            build.distributive.reserve()
            for child in build.distributive.childs:
                child.reserve()
        return build or ""

    def get(self):
        action = self.Get('cl_action')
        if action in Actions.NewAssemble:
            return self.get_create()
        if action in Actions.WorkAssemble:
            return self.get_worked()
        elif action in Actions.BrokenAssemble:
            return self.get_worked()
        return ""


class VariableClBuilderNewId(BaseBuildId):
    """
    Id сборки при развертывании
    """
    value = ""
    untrusted = True

    def get(self):
        return self.Get('cl_builder_profile_name')

    def check(self, value):
        if not value and self.Get('cl_builder_source_filename'):
            raise VariableError(_("Please specify the build ID"))
        if value and not re.match("^[A-Za-z][A-Za-z0-9/:_+-]+$", value):
            raise VariableError(_("Wrong symbols in the build ID"))
        if value in self.Get('cl_builder_storage'):
            raise VariableError(_("Build %s already exists") % _u8(value))


class VariableClBuilderPreparedId(BaseBuildId):
    """
    Id развёрнутой сборки
    """
    type = "choice"
    untrusted = True

    def available(self):
        bs = self.Get('cl_builder_storage')
        action = self.Get('cl_action')
        for x in bs:
            build = bs.get_build(x)
            if (build and (action == Actions.Break or
                                   build.status == Build.Status.Worked)):
                yield x

    def get(self):
        l = list(self.available())
        if l and len(l) == 1:
            return l[0]
        return ""

    def choice(self):
        bs = self.Get('cl_builder_storage')
        for x in bs:
            build = bs.get_build(x)
            if build:
                if build.status == Build.Status.Broken:
                    yield (x, "%s (%s)" % (x, _("broken")))
                else:
                    yield (x, x)

    def _is_iso_compatible(self):
        prefix = self.Get('cl_builder_path')
        if len(self.Get('cl_builder_kernel_config')):
            for pn in ('sys-kernel/calculate-sources', 'sys-kernel/dracut',
                       'media-gfx/gfxboot-themes-calculate', 'sys-boot/grub'):
                if not isPkgInstalled(pn, prefix=prefix):
                    return False
            return True
        return False

    def check(self, value):
        if not value:
            raise VariableError(_("Please select the build ID"))
        l = list(self.available())
        if not l:
            raise VariableError(_("Build %s is not found") % value)
        if (self.Get('cl_builder_build').status == Build.Status.Broken and
                    self.Get('cl_action') != Actions.Break):
            raise VariableError(
                _("Build %s is broken, try to restore build") % value)

        image_type = self.Get('cl_builder_image_type')
        if image_type == "iso":
            if not self._is_iso_compatible():
                prefix = self.Get('cl_builder_path')
                for pn in ('sys-kernel/calculate-sources', 'sys-kernel/dracut',
                           'media-gfx/gfxboot-themes-calculate',
                           'sys-boot/grub'):
                    if not isPkgInstalled(pn, prefix=prefix):
                        raise VariableError(
                            _("Build without %s can be container only") % pn)
                raise VariableError(
                    _("Build without kernel can be container only"))



class VariableClBuilderBrokenId(BaseBuildId):
    """
    Id развёрнутой сборки
    """
    type = "choice"
    untrusted = True

    def available(self):
        bs = self.Get('cl_builder_storage')
        for x in bs:
            build = bs.get_build(x)
            if build and build.status == Build.Status.Broken:
                yield x

    @is_action(Actions.Restore)
    def get(self):
        l = list(self.available())
        if l and len(l) == 1:
            return l[0]
        return ""

    @is_action(Actions.Restore, default_value=[])
    def choice(self):
        bs = self.Get('cl_builder_storage')
        for x in bs:
            build = bs.get_build(x)
            if build.status == Build.Status.Broken:
                yield x

    def check(self, value):
        if not value:
            raise VariableError(_("Please select the build ID"))
        l = list(self.available())
        if not l:
            raise VariableError(_("Build %s is not found") % value)


class VariableClBuilderIdPath(ReadonlyVariable):
    """
    Преобразование сборки id в имя походящее для путей
    """

    def get(self):
        build_id = self.Get('cl_builder_id')
        if build_id:
            return re.sub("[/:]", "_", self.Get('cl_builder_id'))
        return ""


class VariableClBuilderId(ReadonlyVariable):
    """
    Общий id сборки
    """

    def get(self):
        action = self.Get('cl_action')
        if action in Actions.NewAssemble:
            return self.Get('cl_builder_new_id')
        elif action in Actions.WorkAssemble:
            return self.Get('cl_builder_prepared_id')
        elif action in Actions.BrokenAssemble:
            return self.Get('cl_builder_broken_id')
        return ""


class VariableOsBuilderMakeopts(Variable):
    """
    Параметры MAKEOPTS
    """

    def get(self):
        return self.Get('install.os_install_makeopts')


class VariableClBuilderBuildpkgSet(Variable):
    """
    Собирать бинарные пакеты в сборке
    """
    type = "bool"
    value = "off"


class VariableClBuilderBasePath(Variable):
    """
    Базовый путь до сборок (директория, куда будут помещены готовые
    iso образы, бинарные пакеты и т.д.)
    """
    value = "/var/calculate/remote/builder"


class VariableClBuilderParentPath(ReadonlyVariable):
    """
    Путь в ".." до родительской системы
    """

    def get(self):
        builder_path = self.Get('cl_builder_path')
        return ("../" * len(filter(None,
                                   builder_path.split('/'))))[:-1]


class VariableClBuilderStageSet(ReadonlyVariable):
    """
    Разворачиваемый образ является stage (Gentoo системой)
    """
    type = "bool"

    def get(self):
        return ("on"
                if self.Get('os_builder_linux_shortname') == "Gentoo"
                else "off")


class VariableClBuilderPkgdir(Variable):
    """
    Путь собираемых бинарных архивов
    """

    def fallback(self):
        return path.join(self.Get('cl_builder_base_path'),
                         self.Get('cl_builder_id_path'))

    def get(self):
        action = self.Get('cl_action')
        if action in Actions.NewAssemble:
            return self.fallback()
        elif action in Actions.WorkAssemble:
            build = self.Get('cl_builder_build')
            return build.pkgdir or self.fallback()
        else:
            return ""

class VariableClBuilderPkgdirStable(Variable):
    """
    Директория со стабильным набором бинарных пакетов
    """
    value_format = "{cl_builder_pkgdir}.stable"


class VariableClBuilderPkgdirFull(ReadonlyVariable):
    """
    Директория с полным набором бинарных пакетов зависит
    от того использует ли builder base бинхост
    """

    def get(self):
        if self.GetBool('cl_builder_binhost_base_set'):
            return self.Get('cl_builder_pkgdir_stable')
        else:
            return self.Get('cl_builder_pkgdir')


class VariableClBuilderPkgdirTrunk(Variable):
    """
    Директория со набором бинарных пакетов, осавшихся в результате
    прерывания сборки
    """
    value_format = "{cl_builder_pkgdir}.trunk"

class VariableClBuilderAction(ReadonlyVariable):
    """
    Дополнительное действие по созданию образа: iso, squash.
    """
    value = ""


class VariableClBuilderImagePath(Variable):
    """
    Путь по умолчанию для образов
    """
    value = '/var/calculate/linux'


class VariableClBuilderContainerPath(Variable):
    """
    Путь по умолчанию для образов LXC
    """
    value = '/var/calculate/container'


class VariableClBuilderConainterIndexPath(Variable):
    """
    Путь по умолчанию для создания индекса контейнеров
    """
    value = "{cl_builder_container_path}/meta/1.0/index-system"


class VariableClBuilderImageFilename(Variable):
    """
    Название iso образа
    """
    opt = ["--image"]
    metavalue = "IMAGE"
    untrusted = True
    check_after = "cl_builder_container_set"

    def init(self):
        self.label = _("Image path")
        self.help = _("set image path")

    def check(self, value):
        if self.Get('cl_builder_id'):
            if not value:
                raise VariableError(_("You must specify image filename"))
            if not self.GetBool("cl_builder_container_set"):
                if not value.endswith(".iso"):
                    raise VariableError(_("You must specify image filename with "
                                          "'iso' suffix"))
                if path.isdir(value):
                    raise VariableError(_("The image shouldn't be a directory"))
                for line in process("/bin/losetup", "-j", value):
                    lodev, op, line = line.partition(":")
                    if lodev:
                        raise VariableError(
                            _("Image %s is already used") % value)
            elif path.exists(value) and not path.isdir(value):
                raise VariableError(_("The image should be a directory"))

    def _isoname(self, dn, rewrite=False, addon_dn=None, suffix=".iso"):
        shortname = self.Get('os_builder_linux_shortname').lower()
        buildnumber = self.Get('os_builder_linux_build')
        arch = self.Get('os_builder_arch_machine')
        for i in range(0, 999):
            if i:
                isoname = "%s-%s-%d-%s%s" % (shortname, buildnumber,
                                             i, arch, suffix)
            else:
                isoname = "%s-%s-%s%s" % (shortname, buildnumber,
                                          arch, suffix)
            fullname = ""
            for i_dn in filter(None, (addon_dn, dn)):
                fullname = path.join(i_dn, isoname)
                if not rewrite and path.exists(fullname):
                    break
            else:
                return fullname

    def get(self):
        build_id = self.Get('cl_builder_id')
        if self.GetBool('cl_builder_container_set'):
            base_dn = self.Get('cl_builder_container_path')
        else:
            base_dn = self.Get('cl_builder_image_path')

        if build_id:
            root_type_ext = self.Get('os_root_type_ext')
            if root_type_ext in RootType.RebuildAvailable:
                base_dn = self.Get('cl_builder_flash_repository')
                addon_dn = base_dn.replace("/linux", "/iso")
                return self._isoname(base_dn, rewrite=False, addon_dn=addon_dn)
            elif root_type_ext in RootType.IsoScanGrub:
                base_dn = self.Get('cl_builder_livemenu_path')
                if not base_dn:
                    raise VariableError(
                        _("Failed to determine the isoscan path"))
                return self._isoname(base_dn, rewrite=False)
            elif self.GetBool('cl_builder_container_set'):
                return self._isoname(base_dn, rewrite=False, suffix='')
            else:
                return self._isoname(base_dn, rewrite=False)
        return ""


class VariableClBuilderIsoBasePath(Variable):
    """
    Базовый путь, где будут подготавливаться данные, которые будут запакованы
    """
    default_value = "/var/calculate/tmp"

    def get(self):
        root_type_ext = self.Get('os_root_type_ext')
        if root_type_ext in RootType.RebuildAvailable:
            return path.join(self.Get('cl_builder_flash_path'), "tmp")
        if root_type_ext in RootType.IsoScanGrub:
            return path.join(self.Get('cl_isoscan_base_path'), "tmp")
        return self.default_value


class VariableClBuilderIsoPath(ImagePathHelper, ReadonlyVariable):
    """
    Путь, где будут подготавливаться данные, которые будут запакованы в iso
    """
    prefix = "iso-"


class VariableClBuilderSquashPath(ReadonlyVariable):
    """
    Путь от iso до содержимого squash
    """

    @is_action(Actions.Image)
    def get(self):
        return path.relpath(self.Get('cl_builder_path'),
                            self.Get('cl_builder_iso_path'))


class VariableClBuilderImage(ReadonlyVariable):
    """
    Создаваемый образ
    """

    @is_action(Actions.Image)
    def get(self):
        image_name = self.Get('cl_builder_image_filename')
        bdn = self.Get('cl_builder_iso_path')
        exclude_list = self.Get('cl_builder_squash_exclude')
        include_list = self.Get('cl_builder_video_ebuilds')
        if self.GetBool('cl_builder_container_set'):
            image = distr.ContainerDistributive(
                image_name,
                exclude=exclude_list,
                include=include_list)
        else:
            image = distr.IsoDistributive(
                image_name, bdirectory=bdn,
                vol_id=self.Get('cl_builder_iso_label'),
                exclude=exclude_list,
                include=include_list,
                compress=self.Get('cl_builder_compress'))
        return image


class VariableClBuilderLiveSet(Variable):
    """
    Вызывать только live шаблоны при первой загрузке
    """
    type = "bool"
    value = "on"

    opt = ["--live"]

    def init(self):
        self.help = _("use only live templates on startup")
        self.label = _("Use only live templates on startup")


class VariableClBuilderCdname(ReadonlyVariable):
    """
    Type of iso (CD/DVD)
    """

    @is_action(Actions.Image)
    def get(self):
        squashfile = pathJoin(self.Get('cl_builder_iso_path'),
                              self.Get('cl_builder_current_squash'))
        kernelfile = pathJoin(self.Get('cl_builder_iso_path'),
                              self.Get('cl_builder_squash_path'),
                              'boot',
                              self.Get('cl_builder_kernel'))
        initrdfile = pathJoin(self.Get('cl_builder_iso_path'),
                              self.Get('cl_builder_squash_path'),
                              'boot',
                              self.Get('cl_builder_initrd_install'))
        if os.access(squashfile, os.R_OK) and os.access(kernelfile, os.R_OK) and \
                os.access(initrdfile, os.R_OK):
            isosize = path.getsize(squashfile) + path.getsize(kernelfile) + \
                      path.getsize(initrdfile) + 2 * 1024 * 1024
            if isosize > 700 * 1024 * 1024:
                return "DVD"
            else:
                return "CD"
        return ""


class VariableClBuilderIsoLabel(Variable):
    """
    LABEL для iso
    """

    @is_action(Actions.Image)
    def get(self):
        return "%s-%s" % (self.Get('os_builder_linux_shortname').upper(),
                          self.Get('os_builder_linux_build'))


class VariableClBuilderRootParam(Variable):
    """
    параметр root= для livecd
    """

    @is_action(Actions.Image)
    def get(self):
        return "live:LABEL=%s" % self.Get('cl_builder_iso_label')


class VariableClBuilderCurrentSquash(ReadonlyVariable):
    """
    Создаваемый livecd.squash
    """
    value = "livecd.squashfs"


class VariableClBuilderKernelCmd(ReadonlyVariable):
    """
    Параметры по умолчанию для calcboot
    """
    value = ""


class KernelInfo(ReadonlyVariable):
    def get_current_kernel_src(self, prefix):
        src_path = "usr/src"
        current_linux_src = path.join(src_path, "linux")
        symlink_kernel = path.join(prefix, current_linux_src)
        if not path.exists(symlink_kernel) or not path.islink(symlink_kernel):
            raise ValueError("Failed to determine current kernel version")
        return path.join(src_path, os.readlink(symlink_kernel))


class VariableClBuilderKernelConfig(KernelInfo):
    """
    Конфиг ядра
    """

    def get(self):
        prefix = self.Get('cl_builder_path')
        if prefix:
            try:
                kernel_src = self.get_current_kernel_src(prefix)
            except ValueError:
                return ""
            config_path = path.join(kernel_src, ".config")
            return KernelConfig(path.join(prefix, config_path))
        return ""


class VariableClBuilderKernelVer(KernelInfo):
    """
    Текущая версия ядра
    """

    def init(self):
        self.label = _("Kernel version")

    def get(self):
        prefix = self.Get('cl_builder_path')
        if prefix:
            try:
                current_src = self.get_current_kernel_src(prefix)
            except ValueError:
                return ""
            src = path.join(prefix, current_src)
            return self.get_src_kernel_version(src)

    def get_config_version(self, configfile):
        re_config = re.compile("Automatically generated file;.*\n"
                               ".*?Linux/\S+\s+(\S+)\s", re.M)
        if path.exists(configfile):
            with open(configfile) as f:
                match = re_config.search(f.read(200))
                if match:
                    return match.group(1)

    def get_src_kernel_version(self, kernel_src):
        """
        Get version of kernel from .config
        """
        config_path = path.join(kernel_src, ".config")
        makefile_path = path.join(kernel_src, "Makefile")

        # get version from config
        version = self.get_config_version(config_path)
        if version:
            return version

        # get version from Makefile
        re_makefile = re.compile("^VERSION = (\S+)\n"
                                 "PATCHLEVEL = (\S+)\n"
                                 "SUBLEVEL = (\S+)\n"
                                 "EXTRAVERSION = (\S*)\n", re.M)
        if path.exists(makefile_path):
            with open(makefile_path) as f:
                match = re_makefile.search(f.read(200))
                if match:
                    return "{0}.{1}.{2}{3}".format(*match.groups())
        return ""


class KernelData(ReadonlyVariable):
    """
    Данные о текущем ядре
    """
    kernel_object = "kernel"
    file_description = ""

    def filter(self, x, version=None):
        return x

    def list(self, prefix='/', bootdir='boot'):
        boot_dir = path.join(prefix, bootdir)
        return self.get_files_by_type(boot_dir, self.file_description)

    def get(self):
        prefix = self.Get('cl_builder_path')
        version = self.Get('cl_builder_kernel_ver')
        obj_file = max_default(
            self.filter(self.list(prefix), version=version),
            key=path.getmtime,
            default="")
        if obj_file:
            obj_file = path.basename(obj_file)
        return obj_file

    def get_files_by_type(self, pathname, descr):
        ftype = typeFile(magic=0x4).getMType
        for x in listDirectory(pathname, fullPath=True):
            if descr in ftype(x):
                yield x


class VariableClBuilderInitrdInstall(KernelData):
    """
    Текущий initrd
    """
    file_description = "ASCII cpio archive"

    def init(self):
        self.label = _("Init RAM fs")

    def filter(self, iterable, version=None):
        for fn in iterable:
            if InitrdFile(fn).get_kernel_version() == version:
                yield fn


class VariableClBuilderKernel(KernelData):
    """
    Текущее ядро
    """
    file_description = "boot executable bzImage"

    def init(self):
        self.label = _("Kernel file")

    def filter(self, iterable, version=None):
        ftype = typeFile(magic=0x4).getMType
        re_kver = re.compile("bzImage, version (\S+)\s")
        for fn in iterable:
            m = re_kver.search(ftype(fn))
            if m.group(1) == version:
                yield fn


class VariableClBuilderTemplateLocation(Variable):
    """
    Устанавливаются только дистрибутивные шаблоны
    """
    type = "list"

    def get(self):
        return [x for x in self.Get('main.cl_template_location')
                if x not in ('remote', 'local')]


class VariableClBuilderOutdateSet(ReadonlyVariable):
    """
    Флаг устанавливаемый в ходе обновления репозиториев,
    сообщающий что хотя бы один из запланированных репозиториев
    обновлен и следует обновляет различные метаданные

    Если обновляются прочие оверлеи - данные считаются что устарели
    """
    type = "bool"

    def get(self):
        if (self.Get('update.cl_update_other_set') == 'on' and
                self.Get('cl_builder_other_rep_name')):
            return "on"
        return "off"


class VariableClBuilderBranchData(TableVariable):
    """
    Выбор веток репозиториев до которых необходимо обновиться
    """
    opt = ["--branch"]
    metavalue = 'REFS'
    untrusted = True
    source = ["cl_builder_branch_rep",
              "cl_builder_branch_name"]

    def init(self):
        self.help = _("set references for repository (REPOSITORY:REF)")
        self.label = _("Repositories references")

    def raiseReadonlyIndexError(self, fieldname="", variablename="", value=""):
        """
        Неизвестный оврелей
        """
        raise VariableError(_("Repository %s not found") % value)


class VariableClBuilderBriefBranchData(ReadonlyTableVariable):
    """
    Вывод синхронизируемые репозиториев для brief
    """
    source = ["cl_builder_branch_rep",
              "cl_builder_branch_name"]

    def init(self):
        self.help = _("set references for repository (REPOSITORY:REF)")
        self.label = _("Repositories references")

    def get(self, hr=HumanReadable.No):
        return list(reversed(self.Get('cl_builder_branch_data',
                                      humanreadable=hr)))


class VariableClBuilderBranchRep(ReadonlyVariable):
    """
    Список доступных репозиториев
    """
    type = "list"

    def init(self):
        self.label = _("Repository")

    def get(self):
        dv = self.Get('cl_builder_linux_datavars')
        if dv:
            return dv.Get('cl_update_rep_name')
        return []

    def humanReadable(self):
        return [x.capitalize() for x in self.Get()]


class VariableClBuilderBranchName(Variable):
    """
    Список доступных репозиторием
    """
    type = "choiceedit-list"

    def init(self):
        self.label = _("Reference")

    def choice(self):
        return [
            Git.Reference.Tag,
            Git.Reference.Master,
            Git.Reference.Develop,
            Git.Reference.Update]

    def get(self):
        dv = self.Get('cl_builder_linux_datavars')
        if dv:
            if dv.GetBool('cl_update_usetag_set'):
                return [Git.Reference.Tag for x in dv.Get('cl_update_rep_name')]
            else:
                branch = dv.Get('cl_update_branch')
                return [branch for x in dv.Get('cl_update_rep_name')]
        return []


class VariableClBuilderCompress(IsoTypeHelper, Variable):
    """
    Тип сжатия образа squash
    """
    type = "choice"
    opt = ["-c", "--compress"]
    metavalue = "COMPRESS"
    untrusted = True
    check_after = ["cl_builder_id", "cl_builder_container_set"]

    def init(self):
        self.label = _("Compressor")
        self.help = _("set the compressor")

    def choice(self):
        config_param_prefix = "CONFIG_SQUASHFS_%s"
        kernel_config = self.Get('cl_builder_kernel_config')
        params = {"xz": "XZ",
                  "lzma": "XZ",
                  "lzo": "LZO",
                  "gzip": "ZLIB"}

        def generator():
            for compress in getSquashList():
                if compress in params:
                    if config_param_prefix % params[compress] in kernel_config:
                        yield compress

                else:
                    yield compress

        weight = {'xz': 2, 'gzip': 1}

        ret = list(sorted(generator(),
                          key=lambda x: (-weight.get(x, 0), x)))
        if not ret:
            return [""]
        return ret

    def get(self):
        if self.GetBool("cl_builder_container_set"):
            return "xz"
        values = self.choice()
        if values:
            return values[0]
        return ""


class VariableClBuilderIsohybridSet(IsoTypeHelper, Variable):
    """
    Преобразовать полученный iso образ в гибридный
    """
    type = "bool"
    opt = ["--isohybrid"]
    check_after = ["cl_builder_container_set"]

    def init(self):
        self.help = _("create the ISO image with isohybrid")
        self.label = _("ISO hybrid feature")

    def check(self, value):
        if self.GetBool("cl_builder_container_set") and value != "off":
            raise VariableError(_("Iso hybrid is uncompatible with container"))

    def get(self):
        if self.GetBool("cl_builder_container_set"):
            return "off"
        return "on"


class VariableClBuilderKeepTreeSet(Variable):
    """
    Не удалять ебилды из портежей о оверлеев
    """
    type = "bool"
    opt = ["--keep-tree"]
    value = "on"

    def init(self):
        self.help = _("keep portage tree in image")
        self.label = _("Keep portage tree")

class VariableClBuilderVideoEbuilds(Variable):
    """
    Переменная заполняемая при выполнении задачи утилиты
    """
    type = "list"
    value = []

class VariableClBuilderSquashExclude(ReadonlyVariable):
    """
    Список файлов, которые не будут запакованы в livecd.squashfs
    """
    type = "list"

    def get(self):
        builder_path = self.Get('cl_builder_path')
        keep_tree = self.GetBool('cl_builder_keep_tree_set')
        excludes = ["", "metadata"]
        important = ["distfiles", "packages", "eclass", "metadata",
                     "profiles", "layout.conf"] 
        exclude_dirs = ["profiles/templates/deprecated", "profiles/updates"]
        important_files = self.Get('cl_builder_video_ebuilds')

        def generator():
            for rep_dn in self.Get('cl_builder_repository_location'):
                for exclude_dn in excludes:
                    dn = pathJoin(builder_path, rep_dn, exclude_dn)
                    for exclude_name in (x for x in listDirectory(dn)
                                         if x not in important):
                        yield path.join(rep_dn, exclude_dn, exclude_name)[1:]
                for exclude_dn in exclude_dirs:
                    dn = pathJoin(builder_path, rep_dn, exclude_dn)
                    if path.exists(dn):
                        yield path.join(rep_dn, exclude_dn)[1:]

        if not keep_tree and builder_path:
            return list(generator())
        return []


class VariableClBuilderPrelinkSet(Variable):
    """
    Выполнять ли prelink
    """
    type = Variable.Types.Boolean
    opt = ["--prelink"]
    value = Variable.Off

    def init(self):
        self.help = _("perform prelink")
        self.label = _("Perform prelink")


class VariableClBuilderRebuildChangedSet(Variable):
    """
    
    """
    type = Variable.Types.Boolean
    opt = ["--rebuild-changed-packages"]
    value = Variable.On

    def init(self):
        self.help = _("rebuild changed packages")
        self.label = _("Rebuild changed packages")


class VariableClBuilderRebuiltBinariesSet(Variable):
    """
    Выполнить замещение пакетов на бинарную версию если их BUILDTIME
    меньше, чем в Packages
    """
    type = Variable.Types.Boolean
    opt = ["-B", "--rebuilt-binaries"]
    value = Variable.Off

    def init(self):
        self.help = _("replace installed packages with binary packages "
                      "that have been rebuilt")
        self.label = _("Rebuild installed packages with binary packages")


class VariableClBuilderRescratchSet(ReadonlyVariable):
    """
    Сборка iso образа на самой флешке
    """
    type = "bool"

    def get(self):
        if self.Get('os_root_type_ext') in RootType.RebuildAvailable:
            isoimage = self.Get('cl_builder_image_filename')
            flash_rep = self.Get('cl_builder_flash_repository')
            isoimage_dir = path.dirname(isoimage)
            if isoimage_dir == flash_rep:
                return "on"
        return "off"


class VariableClBuilderFlashRepository(ReadonlyVariable):
    """
    Хранилище по умолчанию для iso образов на flash
    """
    suffix = "linux"

    def get(self):
        flash_path = self.Get('cl_builder_flash_path')
        if flash_path:
            return path.join(flash_path, self.suffix)
        return ""


class VariableClBuilderLivemenuType(Variable):
    """
    Тип обновляемого live меню
    """
    type = "choice"
    opt = ["--type"]
    metavalue = "TYPE"

    def init(self):
        self.label = _("Type")
        self.help = _("set livemenu type")

    untrusted = True

    def choice(self):
        return [("grub", _("Grub")),
                ("flash", _("Flash"))]

    def get(self):
        root_type_ext = self.Get('os_root_type_ext')
        if root_type_ext in RootType.Grubable:
            return "grub"
        elif root_type_ext in RootType.RebuildAvailable:
            return "flash"
        return "flash"


class VariableClBuilderImageRepository(ReadonlyVariable):
    """
    Хранилище образов
    """

    def get(self):
        livemenu_type = self.Get('cl_builder_livemenu_type')
        if livemenu_type == "grub":
            return self.Get('cl_builder_livemenu_path')
        else:
            return self.Get('cl_builder_flash_repository')


class VariableClLivemenuPath(ReadonlyVariable):
    """
    Текущий путь до репозитория образов
    """
    grub_config = "/etc/grub.d/35_calculate"

    def get(self):
        data = readFile(self.grub_config)
        m = re.search('^livepath=([^\n]+)', data, re.M)
        if m:
            return m.group(1).strip("'\"")
        return ""


class VariableClBuilderLivemenuPath(Variable):
    """
    Путь до устройства flash
    """
    opt = ["cl_builder_livemenu_path"]
    type = "choiceedit"
    metavalue = "PATH"
    untrusted = True
    none_humanreadable = __("Clear the boot from ISO images")

    def init(self):
        self.help = _("set path")
        self.label = _("Path")

    def get_isoscan_grub_repository(self):
        isoscan_path = self.Get('cl_isoscan_fullpath')
        if isoscan_path:
            return path.dirname(isoscan_path)
        return ""

    def get_live_path(self):
        live_path = self.Get('cl_livemenu_path')
        if not live_path and self.Get('cl_action') == BuilderActions.Image:
            live_path = self.Get('cl_live_path')
        return live_path

    def get(self):
        menutype = self.Get('cl_builder_livemenu_type')
        root_type_ext = self.Get('os_root_type_ext')
        if menutype == "flash":
            if root_type_ext in RootType.LiveFlash:
                return Dracut.IsoLive
            if root_type_ext in RootType.IsoScanFlash:
                return Dracut.IsoScanPath
        else:
            if root_type_ext in RootType.IsoScanGrub:
                return self.get_isoscan_grub_repository()
            elif root_type_ext in RootType.Grubable:
                return self.get_live_path()
        return ""

    def choice(self):
        root_type_ext = self.Get('os_root_type_ext')
        if root_type_ext in RootType.LiveFlash:
            yield (Dracut.IsoLive, _("Boot Flash"))
        elif root_type_ext in RootType.IsoScanFlash:
            yield (Dracut.IsoScanPath, _("Boot Flash"))
        elif root_type_ext in RootType.IsoScanGrub:
            grub_rep = self.get_isoscan_grub_repository()
            if grub_rep:
                yield (grub_rep, _("HDD Grub"))
        elif root_type_ext in RootType.Grubable:
            live_path = self.get_live_path()
            if live_path:
                yield (live_path, _("Distributions directory (%s)") % live_path)
        for dev in self.select('install.os_disk_dev',
                               install_os_disk_format="vfat"):
            yield (dev, self.flash_name(dev))
        if root_type_ext in RootType.Grubable and \
                        root_type_ext not in RootType.IsoScanGrub:
            yield ("none", str(self.none_humanreadable))

    def check_flash(self, value):
        try:
            with IsoDistributive(value) as image:
                d = image.getIsoContentDirectory()
                if not check_rw(d):
                    raise ValueError("Selected device is read-only")
        except DistributiveError as e:
            raise VariableError(str(e).strip())

    def flash_name(self, dev):
        parent_dev = self.select("install.os_disk_parent",
                                 install_os_disk_dev=dev, limit=1)
        if parent_dev:
            dev_name = self.select("install.os_device_name",
                                   install_os_device_dev=parent_dev,
                                   limit=1)
            if dev_name:
                return "%s (%s)" % (dev_name, dev)
        return dev

    def humanReadable(self):
        """
        Преобразовать указанный путь в устройство и получить его имя
        """
        value = self.Get()
        if not value:
            return str(self.none_humanreadable)
        if not value.startswith('/dev/'):
            m = isMount(value)
        else:
            m = value
        if m:
            return self.flash_name(m)
        return value

    def check(self, value):
        livemenu_type = self.Get('cl_builder_livemenu_type')
        if livemenu_type == "flash":
            if not value:
                raise VariableError(_("Please specify the Flash drive"))
            if not path.exists(value):
                raise VariableError(_("Flash drive %s not found") % value)
            if not self.Get('cl_builder_flash_path'):
                self.check_flash(value)
        else:
            root_type_ext = self.Get('os_root_type_ext')
            if not value and root_type_ext in RootType.IsoScanGrub:
                raise VariableError(_("Please specify the images directory"))
            if (not path.exists(value) or value.startswith('/dev') or
                    not path.isdir(value)) and value:
                raise VariableError(_("Please specify the directory"))
            if root_type_ext in RootType.Grubable:
                if root_type_ext in RootType.IsoScanGrub:
                    grub_rep = self.get_isoscan_grub_repository()
                    if grub_rep and value != grub_rep:
                        raise VariableError(
                            _("You cannot change the images directory"))
                else:
                    if value:
                        gc = GrubCommand()
                        if not gc.is_available(value):
                            raise VariableError(
                                _("%s is unavailable for the Grub") % value)
            else:
                grub_file = path.join(value, "grub.cfg")
                if not path.exists(grub_file):
                    raise VariableError(
                        _("Please specify the LiveHDD directory"))

    def set(self, value):
        if value == "none":
            value = ""
        if value:
            return path.normpath(value)
        return ""


class VariableClBuilderFlashDev(ReadonlyVariable):
    """
    Устройство flash
    """
    object = ""

    def get(self):
        typemenu = self.Get('cl_builder_livemenu_type')
        if typemenu == "flash":
            try:
                iso_path = self.Get('cl_builder_livemenu_path')
                if iso_path.startswith('/dev/') and isMount(iso_path):
                    iso_path = isMount(iso_path)
                if iso_path:
                    return IsoDistributive(iso_path)
            except DistributiveError:
                return ""
        return ""


class VariableClBuilderFlashPath(ReadonlyVariable):
    """
    Путь, куда подключена flash (загрузка либо с флешки, либо с iso)
    """

    def get(self):
        try:
            iso = self.Get('cl_builder_flash_dev')
            if iso:
                return iso.getIsoContentDirectory()
        except DistributiveError:
            return ""
        return ""


class VariableClBuilderFlashUuid(ReadonlyVariable):
    """
    UUID flash, используется для перестроения меню
    """

    def get(self):
        flash_path = self.Get('cl_builder_flash_path')
        if flash_path:
            dev = isMount(flash_path)
            if dev:
                return self.select(
                    'install.os_disk_uuid',
                    install_os_disk_dev=dev, limit=1)
        return ""


class VariableClBuilderGrubRefreshSet(ReadonlyVariable):
    """
    Обновить меню grub (HDD c включенным LiveHDD или isoscan-grub
    """

    def get(self):
        root_type_ext = self.Get('os_root_type_ext')
        action = self.Get('cl_action')
        if action == Actions.Image:
            if root_type_ext in RootType.HDDs:
                rep_dir = self.Get('cl_livemenu_path')
                if rep_dir:
                    isoimage = self.Get('cl_builder_image_filename')
                    isoimage_dir = path.dirname(isoimage)
                    if isoimage_dir == rep_dir:
                        return "on"
            elif root_type_ext in RootType.IsoScanGrub:
                isoimage = self.Get('cl_builder_image_filename')
                rep_dir = self.Get('cl_builder_livemenu_path')
                isoimage_dir = path.dirname(isoimage)
                if isoimage_dir == rep_dir:
                    return "on"
        return "off"


class VariableClBuilderEixRepositories(ReadonlyVariable):
    """
    Отображаемый список обновляемых репозиториев eix
    """

    def get(self):
        return ", ".join(
            x.capitalize() for x in reversed(list(chain(
                self.Get('update.cl_update_rep_name'),
                self.Get('builder.cl_builder_other_rep_name')))))


class VariableClBuilderBinhostStableSet(
    vars_linux.BuilderLinux, update.VariableClUpdateBinhostStableSet):
    """
    Использовать стабильное или нестабильное зеркало
    """
    variable = "cl_update_binhost_stable_set"

    def get_fallback(self):
        return Variable.On


class VariableClBuilderBinhostStableOptSet(
    update.VariableClUpdateBinhostStableOptSet):
    """
    Использовать стабильное или нестабильное зеркало
    """
    mode = Variable.Writable

    def get(self):
        return self.Get('cl_builder_binhost_stable_set')


class VariableClBuilderBinhostScanSet(Variable):
    """
    Сканировать или нет зеркала (переменная переключается во время действия)
    """
    value = "off"


class VariableClBuilderCheckAutomagicSet(Variable):
    """
    Выполнить проверку на наличие неописанных автоматических зависимостей
    """
    type = "bool"
    value = "off"

    opt = ["--check-autodep"]

    def init(self):
        self.label = _("Check for auto depends")
        self.help = _("check for auto depends")


class VariableClBuilderUpdateThemesSet(Variable):
    """
    Перенастроить темы перед запаковкой
    """
    type = "bool"
    value = "off"

    opt = ["--update-themes", "-U"]

    def init(self):
        self.label = _("Update themes")
        self.help = _("update themes")


class VariableClBuilderImageType(Variable):
    """
    Тип собираемого образа
    """
    type = "choice"

    def init(self):
        self.label = _("Image type")
        self.help = _("set image type")

    def choice(self):
        return (("container", _("Container")),
                ("iso", _("ISO")))


class VariableClBuilderContainerSet(ReadonlyVariable):
    """
    Запаковать образ как LXC контейнер
    """
    type = "bool"

    def get(self):
        if self.Get('cl_builder_image_type') == 'container':
            return 'on'
        return 'off'

class VariableClBuilderContainerDist(Variable):
    """
    Название операционной системы
    """
    value = "calculate"


class VariableClBuilderContainerRelease(Variable):
    """
    Версия ОС контейнера
    """

    def get(self):
        return self.Get('os_builder_linux_ver')


class VariableClBuilderContainerArch(Variable):
    """
    Архитектура ОС в контейнере
    """

    def get(self):
        return self.Get('os_builder_arch_machine_gentoo')

class VariableClBuilderContainerVariant(Variable):
    """
    Variant of image
    """
    value = "default"


class VariableClBuilderContainerExpiryTimestamp(Variable):
    """
    Expire date
    """
    month = 60 * 60 * 24 * 30
    type = "int"

    def get(self):
        return str(int(self.Get('cl_builder_container_create_timestamp')) +
                   self.month)

class VariableClBuilderContainerCreateTimestamp(Variable):
    """
    Expire date
    """
    type = "int"

    def get(self):
        return datetime.datetime.now().strftime("%s")

class VariableClBuilderContainerDate(Variable):
    """
    Creation date of image
    """
    def get(self):
        ts = int(self.Get('cl_builder_container_create_timestamp'))
        return datetime.datetime.fromtimestamp(ts).strftime("%Y%m%d_%H:%M")

class VariableClBuilderContainerBasePath(Variable):
    """
    Базовый путь для подготовки архивов meta.tar.xz lxd.tar.xz
    """
    value = "/var/calculate/tmp"

class VariableClBuilderContainerDataPath(ImagePathHelper, Variable):
    """
    Путь где будут подготавливаться данные для контейнера
    """
    @property
    def base_dn(self):
        return self.Get('cl_builder_container_base_path')

    prefix = "lxc-"

