# Copyright 2016 VMware, Inc.  All rights reserved. -- VMware Confidential
#
'''
Script responsible to setup environment and execute the SystemPrepare hook of vCSA
components. The script calls only those components which have been discovered
in prior discovery phase.
'''

import logging

from extensions import Hook
from patch_errors import UserError, ComponentError

from vmware.patching.executor.execution_facade import executeComponentHook
from vmware.patching.data.model import PatchPhaseContext
from vmware.patching.utils.reporting_utils import setupReporting, configureReportingFactory, \
    FRAMEWORK_COMPONENT, reportProgressError, configureLocalization

logger = logging.getLogger(__name__)

def _prepareComponents(ctx, userData, reportingQueue):
    '''Executes the components SystemPrepare hook.

    @param ctx: Global CPO patch phase context.
    @type ctx: vmware.patching.data.model.PatchPhaseContext

    @param userData: Customer input, as result of component questions raised
      in @Discovery patching hook.
    @type userData: dict

    @param reportQueue: A queue to be used for reporting purposes by the patching
        hook
    @type reportQueue: multiprocessor.Queue

    @raise Exception: if SystemPrepare hook execution fails.
    '''
    for c in ctx.getPatchableComponents(sort=True):
        logger.info("Running system prepare for component %s", c.name)
        executeComponentHook(Hook.SystemPrepare, ctx, c, userData, reportingQueue)

def prepare(stageDir, userData, outputFile):
    '''The entry point of prepare CPO phase.

    The system preparation phase is responsible to setup environment and execute the
    SystemPreparation hook of vCSA components. The SystemPreparation hook is executed only
    on those components which have been discovered in prior discovery phase.

    @param stageDir: global CPO stage directory. All components stage
      directories will be created as sub-directories of global stage directory.
    @type stageDir: str

    @param userData: Customer input, as result of component questions raised
      in @Discovery patching hook.
    @type userData: dict

    @param outputFile: File where the output of the command needs to be written
      to
    @type outputFile: str

    @return: True if the SystemPreparation phase succeed and False otherwise
    @rtype: bool

    @raise Exception: if SystemPreparation phase raise unhandled exception
    '''
    statusAggregator = None
    try:
        ctx = PatchPhaseContext.load(stageDir)
        configureLocalization(ctx.locale)
        statusAggregator = setupReporting(outputFile, len(ctx.getPatchableComponents()))
        configureReportingFactory(FRAMEWORK_COMPONENT, statusAggregator.reportingQueue)

        _prepareComponents(ctx, userData, statusAggregator.reportingQueue)
        return True
    except ComponentError as wrapperError:
        logger.exception("Patch hook %s got UserError.", Hook.SystemPrepare)
        if isinstance(wrapperError.baseError, UserError):
            reportProgressError(wrapperError.baseError, wrapperError.componentKey)
        else:
            reportProgressError(identifier=wrapperError.componentKey)
        return False
    except UserError as userError:
        logger.exception("Patch hook %s got UserError.", Hook.SystemPrepare)
        reportProgressError(userError)
        return False
    except Exception:
        logger.exception("Patch hook %s got unhandled exception.", Hook.SystemPrepare)
        reportProgressError()
        return False
    finally:
        if statusAggregator:
            statusAggregator.stop()