#!/usr/bin/env python
# Copyright 2016 VMware, Inc.
# All rights reserved. -- VMware Confidential

"""
    This script is a utility to be used by both day0 and b2b scripts
"""
import sys
import os
import logging
import subprocess
import json
import tempfile
import time
import spwd
import re
from time import sleep
from datetime import datetime
from os.path import join, exists, abspath, dirname

from fss_utils import getTargetFSS

logger = logging.getLogger(__name__)

# Used to find what is the deployment type
DEPLOYMENT_TYPE_FILE = "/etc/vmware/deployment.node.type"

#Name of the ignore list file, need to provide the deployment type to it
IGNORELISTPATTERN = "%s_ignore.lst"

DEPLOYMENT_ROOT = abspath(join(dirname(__file__), os.pardir))
RPM_MANIFEST_FILE = join(DEPLOYMENT_ROOT, 'rpm-manifest.json')
MANIFEST_FILE  = join(DEPLOYMENT_ROOT, 'manifest-latest.xml')
SERVICES_JSON = join(DEPLOYMENT_ROOT,
                     "scripts/patches/libs/sdk/config/services.json")

def _searchRpm(rpmManifestJson, pattern):
    for entry in list(rpmManifestJson.values()):
        if re.match(pattern, entry["relativepath"]):
            return [entry["relativepath"]]
    return []

def getRPMBlacklist():
    """Retrieves the rpm blacklist based on the current deployment type.

    @return: Rpms which should not be installed during the update workflow
    @rtype: list
    """
    deployType = ""
    if exists(DEPLOYMENT_TYPE_FILE):
        with open(DEPLOYMENT_TYPE_FILE) as f:
            deployType = f.read().strip()

    logger.debug("vCSA deployment Type: %s", deployType)
    ignoreFile = join(DEPLOYMENT_ROOT, "ignorelists",
                      IGNORELISTPATTERN % (deployType))

    ignoreRpms = []
    if exists(ignoreFile):
        with open(ignoreFile) as f:
            ignoreRpms = [line.strip() for line in f if line.strip()]
    else:
        logger.debug("File %s does not exist", ignoreFile)

    # process feature switches in target services.json
    with open(SERVICES_JSON) as f:
        servicesJson = json.load(f)
    with open(RPM_MANIFEST_FILE) as f:
        rpmManifestJson = json.load(f)
    for name, service in list(servicesJson.items()):
        if "FSSname" in service and getTargetFSS(service["FSSname"]) is False:
            files = service["appliance"]["installFiles"]
            logger.debug("Ignoring service %s, rpms %r", name, files)
            for file in files:
                file = file.replace('.','\.')
                pattern = file.replace('@version@','.*')
                logger.debug("File pattern %s" % pattern)
                ignoreRpms.extend(_searchRpm(rpmManifestJson["files"], pattern))

    return ignoreRpms

def _backspaceStdout(l):
    """ Go back l characters in stdout and replace them with spaces
    """

    sys.stdout.write("%s%s%s" % (("\b" * l), (" " * l), ("\b" * l)))
    sys.stdout.flush()

def _getFormattedTime():
    """ Output the current time in a format consistent with
        the logging.
        example: 2014-07-24 08:42:37,084
    """

    d = datetime.now()
    return ("%04d-%02d-%02d %02d:%02d:%02d,%03d" % \
        (d.year, d.month, d.day, d.hour, d.minute, d.second, d.microsecond / 1000))

def runCommand(command, progress=False, message="", onSubcmdStart=None):
    """Run a command and waits until its completion.

    @param command: The command to run
    @type command: list

    @param progress: Flag indicating whether there should be a progress bar
    @type progress: boolean

    @param message: The message to be displayed if the ahead of the progress bar
    @type message: str

    @param onSubcmdStart: Executed when a subcommand is started
    @type onSubcmdStart: Callback
      Example: def onSubcmdStart():

    @return: Tuple of (stdout, stderr, processExitCode)
    @rtype: tuple
    """

    logger.debug("Running command: %s", command)

    with tempfile.NamedTemporaryFile(prefix='update-script-out', dir='/var/log/vmware/applmgmt') as out:
        with tempfile.NamedTemporaryFile(prefix='update-script-err', dir='/var/log/vmware/applmgmt') as err:
            logger.debug('You can find the output of the command in temp files '
                         'out %s, err %s', out.name, err.name)
            proc = subprocess.Popen(command, stdout=out,
                                    stderr=err, stdin=None)

            if onSubcmdStart:
                onSubcmdStart()

            if progress and not os.environ.get('B2B_no_stdout'):
                # This code creates a progress bar and polls to see if the
                # process has completed, once the progress bar has finished.
                # If the process has completed the return with the error code,
                # otherwise repeat the progress bar.
                progressbar_width = 4
                progressbar_time = 1.0
                # setup progress bar
                sys.stdout.write(" [%s] : %s%s" % (_getFormattedTime(), message, \
                                                   (" " * progressbar_width)))
                sys.stdout.flush()
                while (proc.poll() == None):
                    _backspaceStdout(progressbar_width)
                    for _i in range(progressbar_width):
                        sleep(progressbar_time / progressbar_width)
                        sys.stdout.write(".")
                        sys.stdout.flush()
                sys.stdout.write("\n")
                sys.stdout.flush()

            rc = proc.wait()
            out.seek(os.SEEK_SET)
            err.seek(os.SEEK_SET)
            stdout = out.read()
            stderr = err.read()
            logger.debug("STDOUT: %s" % stdout)
            logger.debug("STDERR: %s" % stderr)
            return (stdout, stderr, rc)

def isRootPasswdExpired():
    '''
    Check if VCSA root password is expired
    before patching start.
    '''
    pwdCfg = spwd.getspnam('root')
    maxdays = pwdCfg[4]
    if maxdays != -1:
        if time.time() > (maxdays + pwdCfg[2]) * 86400:
            return True
    return False
