
from __future__ import absolute_import
from __future__ import division

import errno
import os
import selinux
import shutil
import sys
import time

from vdsm.tool import confmeta
from vdsm.common import commands
from vdsm.common.cmdutils import CommandPath

from . import YES, NO

_SYSTEMCTL = CommandPath("systemctl", "/bin/systemctl", "/usr/bin/systemctl")

_LVMLOCAL_CUR = "/etc/lvm/lvmlocal.conf"
_LVMLOCAL_VDSM = "/usr/share/vdsm/lvmlocal.conf"
_LVMETAD_SERVICE = "lvm2-lvmetad.service"
_LVMETAD_SOCKET = "lvm2-lvmetad.socket"


name = "lvm"
services = []


def configure():
    if not _lvm_conf_configured():
        _backup_file(_LVMLOCAL_CUR)
        _install_file(_LVMLOCAL_VDSM, _LVMLOCAL_CUR)
    if not _lvmetad_configured():
        _systemctl("mask", _LVMETAD_SERVICE, _LVMETAD_SOCKET)
        _systemctl("disable", _LVMETAD_SERVICE, _LVMETAD_SOCKET)
        _systemctl("stop", _LVMETAD_SERVICE, _LVMETAD_SOCKET)


def isconfigured():
    if _lvm_conf_configured() and _lvmetad_configured():
        _log("lvm is configured for vdsm")
        return YES
    else:
        _log("lvm requires configuration")
        return NO


def _lvm_conf_configured():
    try:
        cur_conf = confmeta.read_metadata(_LVMLOCAL_CUR)
    except EnvironmentError as e:
        if e.errno != errno.ENOENT:
            raise
        return False

    if cur_conf.revision is None:
        _log("WARNING: LVM local configuration: %s is not based on vdsm "
             "configuration", _LVMLOCAL_CUR)
        return False

    vdsm_conf = confmeta.read_metadata(_LVMLOCAL_VDSM)
    if cur_conf.private:
        _log("Using private lvm local configuration: %s", _LVMLOCAL_CUR)
        if cur_conf.revision < vdsm_conf.revision:
            _log("WARNING: Installed lvm local configuration: %s was based "
                 "on an older revision. Please update the file form vdsm "
                 "configuration: %s", _LVMLOCAL_CUR, _LVMLOCAL_VDSM)
        return True

    return vdsm_conf.revision == cur_conf.revision


def _lvmetad_configured():
    out = _systemctl("show", "--property=Names,LoadState,ActiveState",
                     _LVMETAD_SERVICE, _LVMETAD_SOCKET)

    units = {}
    for unit in out.strip().split("\n\n"):
        info = dict(prop.split("=", 1) for prop in unit.split("\n"))
        names = info.pop("Names")
        for name in names.split(","):
            units[name] = info

    not_configured = {}
    for name, state in units.items():
        if state["LoadState"] != "masked" or state["ActiveState"] == "active":
            not_configured[name] = state

    if not_configured:
        _log("Units need configuration: %s", not_configured)
        return False

    return True


def _install_file(src, dst):
    _log("Installing %s at %s", src, dst)
    tmpfile = _LVMLOCAL_CUR + ".tmp"
    shutil.copyfile(_LVMLOCAL_VDSM, tmpfile)
    try:
        selinux.restorecon(tmpfile)
        os.chmod(tmpfile, 0o644)
        os.rename(tmpfile, _LVMLOCAL_CUR)
    except:
        try:
            os.unlink(tmpfile)
        except Exception:
            _log("ERROR: cannot remove temporary file: %s", tmpfile)
        raise


def _backup_file(path):
    if os.path.exists(path):
        backup = path + '.' + time.strftime("%Y%m%d%H%M")
        _log("Backing up %s to %s", path, backup)
        shutil.copyfile(path, backup)


def _systemctl(*args):
    cmd = [_SYSTEMCTL.cmd]
    cmd.extend(args)
    return commands.run(cmd)


def _log(fmt, *args):
    sys.stdout.write(fmt % args + "\n")
