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

# Copyright 2008-2013 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
from os import path,R_OK
import socket
import platform
import re
from calculate.lib.datavars import Variable,VariableError,ReadonlyVariable
from calculate.lib.utils.files import readFile,process, listDirectory,isMount
from calculate.lib.utils.device import getUdevDeviceInfo,loadEfiVars
from calculate.lib.utils.common import getKernelUid

class VariableOsArchMachine(ReadonlyVariable):
    """
    Computer architecture (i686,x86_64)
    """
    def get(self):
        return platform.machine()

class VariableOsArchMachineGentoo(ReadonlyVariable):
    """
    Архитектура пакетов
    """
    def get(self):
        map_arch = {'x86_64': 'amd64', 'i686': 'x86', 'i386': 'x86'}
        arch = self.Get('os_arch_machine')
        return map_arch.get(arch,arch)

class VariableOsRootDev(ReadonlyVariable):
    """
    Root partition of current system
    """
    def get(self):
        """Root filesystem device"""
        record = readFile('/proc/cmdline').strip()
        re_resRealRoot=re.search('(?:^|\s)real_root=(\S+)(\s|$)',record)
        re_resFakeRoot=re.search('(?:^|\s)root=(\S+)(\s|$)',record)
        # param real_root priority that root
        re_res = re_resRealRoot or re_resFakeRoot
        if re_res:
            rootparam=re_res.group(1)
            # check root for /dev/sd view
            if re.match("^\/dev\/[a-z]+.*$", rootparam):
                return getUdevDeviceInfo(
                   name=rootparam.strip()).get('DEVNAME',rootparam)
            # check root set by uuid
            if re.match("^UUID=.*$",rootparam):
                uuid = rootparam[5:].strip("\"'")
                blkidProcess = process('/sbin/blkid','-c','/dev/null','-U',
                                    uuid)
                if blkidProcess.success():
                    return getUdevDeviceInfo(
                       name=blkidProcess.read().strip()).get('DEVNAME','')
            # check root set by label
            if re.match("^LABEL=.*$",rootparam):
                uuid = rootparam[6:].strip("\"'")
                blkidProcess = process('/sbin/blkid','-c','/dev/null','-L',
                                    uuid)
                if blkidProcess.success():
                    return getUdevDeviceInfo(
                       name=blkidProcess.read().strip()).get('DEVNAME','')
        # get device mounted to root
        dfProcess = process('df','/',envdict={'LANG':'C'})
        if dfProcess.failed():
            return ""
        dfLines = dfProcess.readlines()
        if len(dfLines)>1:
            root_dev = dfLines[1].split(" ")[0].strip()
            if root_dev:
                return {
                    'none':'/dev/ram0',
                    'overlay':'/dev/ram0',
                }.get(root_dev,root_dev)
        return ""

class VariableOsMapperRootDev(ReadonlyVariable):
    """
    Название диска через devicemapper если возможно
    """
    def get(self):
        rootdev = self.Get('os_root_dev')
        devLinks = getUdevDeviceInfo(name=rootdev).get("DEVLINKS","")
        if devLinks:
            mapperName = filter(lambda x:"/dev/mapper" in x,
                         devLinks.split())
            if mapperName:
                return mapperName[0]
        return rootdev

class VariableOsRootType(ReadonlyVariable):
    """
    Root type (ram, hdd, usb-hdd, livecd)
    """
    def get(self):
        """Root device type (ram, hdd, livecd)"""
        def link2pair(linkfile):
            """Return pair (target,link) from link"""
            basedir = path.dirname(linkfile)
            targetfile = os.readlink(linkfile)
            return (path.normpath(os.path.join(basedir,targetfile)),linkfile)
        rootDev = self.Get("os_root_dev")
        if rootDev:
            if any(x in rootDev for x in ("/dev/ram","/dev/nfs")):
                return "livecd"
            idDict = dict(map(link2pair,
                     filter(lambda x:path.islink(x),
                     map(lambda x:path.join('/dev/disk/by-id',x),
                     listDirectory('/dev/disk/by-id')))))
            if "usb-" in idDict.get(rootDev,""):
                return "usb-hdd"
        return "hdd"

class VariableClChrootStatus(ReadonlyVariable):
    """
    Is chroot active
    """
    def isChroot(self,pid):
        """Detect chroot mode by different mountinfo"""
        if not os.access('/proc/self/mountinfo',R_OK) or \
            not os.access('/proc/1/mountinfo',R_OK):
            return False
        return open('/proc/1/mountinfo','r').read() != \
               open('/proc/self/mountinfo','r').read()

    def get(self):
        try:
            return "on" if self.isChroot(os.getpid()) else "off"
        except:
            return "off"

class VariableClKernelUid(Variable):
    """
    Kernel UID get by UUID of root device
    """
    def get(self):
        return getKernelUid(self.Get('os_root_dev'))

class VariableOsScratch(ReadonlyVariable):
    """
    Is current system scratch
    """
    def get(self):
        """Current system is scratch"""
        if self.Get('os_root_type') == 'livecd':
            return "on" if isMount('/mnt/scratch/workspace') else "off"
        else:
            return "on" if isMount('/mnt/scratch') else "off"

class VariableOsUefiSet(ReadonlyVariable):
    """
    Current loaded system
    """
    def get(self):
        if not path.exists('/sys/firmware/efi'):
            loadEfiVars()
        if path.exists('/sys/firmware/efi'):
            return "on"
        else:
            return "off"

class VariableClUsedAction(ReadonlyVariable):
    """
    Переменная содержит все найденные при обработке шаблонов
    событийные переменные (ac_)
    """
    type = "table"
    def get(self):
        return []

class VariableClSystemBootSet(ReadonlyVariable):
    """
    Система находится в стадии загрузки
    """
    type = "bool"
    def get(self):
        if os.readlink('/proc/self/fd/0') == '/dev/console':
            return "on"
        else:
            return "off"
