# Copyright 2016 VMware, Inc.
# All rights reserved. -- VMware Confidential
"""
The module contains common patching functionality for VMware vCenter services.
The component should be executed as last component in patching workflow.
"""
import os
import logging

from patch_specs import (
    DiscoveryResult, ValidationResult, Requirements, PatchInfo,
    RequirementsResult)
from extensions import extend, Hook
from l10n import msgMetadata as _T, localizedString as _
from os_utils import executeCommand

logger = logging.getLogger(__name__)
MY_PAYLOAD_DIR = os.path.dirname(__file__)


@extend(Hook.Discovery)
def discover(ctx):  # pylint: disable=W0613
    '''DiscoveryResult discover(PatchContext sharedCtx) throw UserUpgradeError
    '''
    # There is special handling for last-patching-component component,
    # guaranteed
    # That all other components will be executed before this one
    return DiscoveryResult(displayName=_(_T("common.comp.displayName",
                                            "VMware vCenter Server Patcher")),
                           componentId="last-patching-component")


@extend(Hook.Requirements)
def collectRequirements(ctx):  # pylint: disable=W0613
    '''RequirementsResult collectRequirements(PatchContext sharedCtx)'''
    mismatches = []
    requirements = Requirements()
    patchInfo = PatchInfo()

    return RequirementsResult(requirements, patchInfo, mismatches)


@extend(Hook.Validation)
def validate(ctx):  # pylint: disable=W0613
    '''ValidationResult validate(PatchContext sharedCtx)'''
    mismatches = []

    return ValidationResult(mismatches)


def _executePreInstallScript(scriptLocation):
    '''Executes party pre-isntall script. The script contains update logic
    necessary to be executed just before the rpms are installed. It could also
    contain shared patching logic, e.g. update the OS patcher rpm, up front the
    update of all rpms.

    It also stops the affected services

    @param scriptLocation: Location to the script which needs to be executed
    @type scriptLocation: str
    '''
    logger.info("Running pre-install script %s", scriptLocation)
    stdout, stderr, exitCode = executeCommand(['/bin/python', scriptLocation])
    logger.info("pre-install script completed. Stdout=%s, Stderr=%s,"
                "exit-code=%s", stdout, stderr, exitCode)
    if exitCode:
        raise Exception("Unable to execute script %s", scriptLocation)


def _getStagedScript(ctx, scriptName):
    '''Gets location of the script downloaded to staged directory.

    @param ctx: Patch context shared among all patching hooks
    @type ctx: PatchContext

    @param scriptName: Name of the script
    @type scriptName: str

    @return: The full path location on the disk
    @rtype: str
    '''
    # Stage directory points to $STAGE_DIR/patch_runner/<compName> defined in
    # update_script.py
    stageDir = ctx.stageDirectory
    result = os.path.abspath(os.path.join(stageDir, os.pardir, os.pardir,
                                          "scripts", scriptName))
    return result


@extend(Hook.Prepatch)
def prePatch(ctx):  # pylint: disable=W0613
    '''void prePatch(PatchContext sharedCtx) throw UserUpgradeError'''
    preInstallScript = _getStagedScript(ctx, "pre-install.py")
    _executePreInstallScript(preInstallScript)


def _executePostPatchScript(scriptLocation):
    '''Executes post-patch script. The script contains update logic necessary to
    be executed just after all components patching hook complete successfully.

    @param scriptLocation: Location to the script which needs to be executed
    @type scriptLocation: str
    '''
    logger.info("Running post-patch script %s", scriptLocation)
    stdout, stderr, exitCode = executeCommand([scriptLocation])
    logger.info("post-patch script completed. Stdout=%s,Stderr=%s,"
                "exit-code=%s", stdout, stderr, exitCode)
    if exitCode:
        raise Exception("Unable to execute script %s", scriptLocation)


@extend(Hook.Patch)
def patch(ctx):  # pylint: disable=W0613
    '''void patch(PatchContext sharedCtx) throw UserUpgradeError'''
    prePatchScript = _getStagedScript(ctx, "post-patch.sh")
    _executePostPatchScript(prePatchScript)
