# Copyright (c) 2016-2021 VMware, Inc. All rights reserved. -- VMware Confidential

"""
vpxd component module following the B2B patching principles
"""

import errno
import os
import sys
from extensions import extend, Hook
from l10n import msgMetadata as _T, localizedString as _
import vcsa_utils
import logging

from extensions import extend, Hook

from patch_specs import Requirements, PatchInfo, RequirementsResult

from reporting import getProgressReporter
from fss_utils import getTargetFSS

logger = logging.getLogger(__name__)

NDU_LIMITED_DOWNTIME = "NDU_Limited_Downtime"

PATH_ETC_VMWAREVPX = "/etc/vmware-vpx/"
MARKER_FILE_B2B = PATH_ETC_VMWAREVPX + "vpxd_b2b_upg.cfg"
MARKER_FILE_NDU = PATH_ETC_VMWAREVPX + "vpxd_upg.cfg"
MARKER_FILE_NDU_TMP = MARKER_FILE_NDU + ".tmp"

PATH_CATALOG_ZIP = PATH_ETC_VMWAREVPX + "locale/catalog.zip"
REPL_NONE = ""

PATH_VPX_VCDB_PROPS = PATH_ETC_VMWAREVPX + "vcdb.properties"
PATH_VPX_VCDB_PROPS_TMP = PATH_VPX_VCDB_PROPS + ".tmp"

PATH_VPXD_CFG = PATH_ETC_VMWAREVPX + "vpxd.cfg"
PATH_VPXD_CFG_TMP = PATH_VPXD_CFG + ".tmp"

def _copy_file(src, dst):
   """
      copy a file to tmp file for non-destructive upgrade use.
   """
   import shutil
   shutil.copyfile(src, dst)
   if not os.path.isfile(dst):
      raise Exception('Failed to copy file %s to %s' % (src, dst))

@extend(Hook.Discovery)
def discover(ctx):
   '''DiscoveryResult discover(PatchContext sharedCtx) throw UserUpgradeError'''
   # Modify copies of config files run on source VC if possible.
   # Modify and replace the vcdb.properties with sslmode disabled.
   # Disable replication for catalog.zip. it will be created on target.
   replicationConfig = {
      MARKER_FILE_NDU_TMP: MARKER_FILE_NDU,
      PATH_CATALOG_ZIP: REPL_NONE,
      PATH_VPX_VCDB_PROPS_TMP: PATH_VPX_VCDB_PROPS,
      PATH_VPXD_CFG_TMP: PATH_VPXD_CFG,
   }
   return vcsa_utils.getComponentDiscoveryResult("vpxd",
      displayName=_(_T("VPXD.displayName", "VMware vCenter Server")),
      replicationConfig=replicationConfig)

@extend(Hook.Requirements)
def collectRequirements(ctx):
   '''RequirementsResult collectRequirements(PatchContext ctx)'''
   mismatches = []
   requirements = Requirements()
   patchInfo = PatchInfo()
   return RequirementsResult(requirements, patchInfo, mismatches)

@extend(Hook.Validation)
def validate(ctx):
   '''ValidationResult validate(PatchContext ctx)'''
   pass

@extend(Hook.Expand)
def expand(ctx):
   '''void expand(PatchContext sharedCtx) throw UserError'''
   # Expand is run for NDU and B2B update and replaces prepatch.
   # python path for vpxd python scripts.
   sys.path.append(os.environ['VMWARE_PYTHON_PATH'])
   from cis.defaults import (get_cis_install_dir, def_by_os)
   installbin = os.path.join(get_cis_install_dir(),
                          def_by_os('vmware-vpx', 'vpxd'), 'py')
   sys.path.append(installbin)
   logger.info('Vpxd expand phase started')

   progressReporter = getProgressReporter()
   progressReporter.updateProgress(0, _(_T("vpxd.expand.begin",
                                           'Expand phase started')))

   # Expand hook is always run.
   # Expand hook runs on source VC during NDU.
   # NDU replicates only the ndu marker to target.
   # TODO: not sure if this check will work with both B2B and NDU set.
   # Testing shows it returns true if B2B and NDU FSS are both set.
   if vcsa_utils.isDisruptiveUpgrade(ctx):
      logger.info('Placed B2B marker')
      open(MARKER_FILE_B2B, "a").close()
   if getTargetFSS(NDU_LIMITED_DOWNTIME):
      logger.info('Placed temp NDU marker')
      open(MARKER_FILE_NDU_TMP, "a").close()

   # copy vcdb and obdc files to vmware-vpx for upgrade modification.
   progressReporter.updateProgress(30)
   try:
      _copy_file(PATH_VPX_VCDB_PROPS, PATH_VPX_VCDB_PROPS_TMP)
      _copy_file(PATH_VPXD_CFG, PATH_VPXD_CFG_TMP)
      logger.info('Copied vcdb and vpxd cfg files to tmp')
   except Exception as ex:
      logger.error('Exception copying files: %s' % ex.message)
      err = _(_T("install.vpxd.expand.failed",
                 "Failed to copy vcdb and vpxd cfg files for vpxd."))
      eI = ErrorInfo([err])
      raise InvokeCommandException(eI)

   # patch_utils require modules that may not be present in the source system.
   # Those can't be imported when running discovery on the source system.
   from .patch_utils import (VCDBPropPatch, VPXDCfgPatch)

   # VCDB jdbc properties patch.
   # NDU does dbconfig firstboot as a short term hack so leave this as-is.
   # TODO: Need to improve this and remove this code.
   progressReporter.updateProgress(50)
   vcdb_patch = VCDBPropPatch(PATH_VPX_VCDB_PROPS_TMP)
   vcdb_patch.doPatching()
   del vcdb_patch
   logger.info('Completed VCDB properties tmp modification')

   # VPXd Cfg properties patching.
   # TLS hack to remove protocols entry because TLS version setup
   # is not correctly handled at present so need this hack.
   # Also put in the alarms patch hack from vpxd prestart to improve prestart.
   # TODO: Need to improve this and remove this code when possible
   progressReporter.updateProgress(70)
   vpxd_cfg_patch = VPXDCfgPatch(PATH_VPXD_CFG_TMP)
   vpxd_cfg_patch.doPatching()
   del vpxd_cfg_patch
   logger.info('Completed vpxd.cfg.tmp modification')

   # VPXD pre-start will make all other upgrade or B2B related changes
   # that cannot be made on the source system in a copy of a config file.

   logger.info('Vpxd expand phase completed')
   progressReporter.updateProgress(100, _(_T("vpxd.expand.complete",
                                             'Expand phase completed')))

@extend(Hook.Contract)
def contract(ctx):
   '''void contract(PatchContext ctx) throw UserError'''
   # contract hook used in future to cleanup any properties added in expand.
   # IFF there are properties that can be cleaned up after vpxd has started.
   pass

@extend(Hook.Revert)
def revert(ctx):
   '''void revert(PatchContext ctx) throw UserError'''
   # cleanup temp vcdb.properties file.
   progressReporter = getProgressReporter()
   progressReporter.updateProgress(0, _(_T("vpxd.revert.begin",
                                           'Reverting VPXD started')))
   logger.info('Vpxd revert phase started')

   if (os.path.isfile(PATH_VPX_VCDB_PROPS_TMP)):
      os.remove(PATH_VPX_VCDB_PROPS_TMP)
   if (os.path.isfile(PATH_VPXD_CFG_TMP)):
      os.remove(PATH_VPXD_CFG_TMP)
   if (os.path.isfile(MARKER_FILE_B2B)):
      os.remove(MARKER_FILE_B2B)
   if (os.path.isfile(MARKER_FILE_NDU_TMP)):
      os.remove(MARKER_FILE_NDU_TMP)
   if (os.path.isfile(MARKER_FILE_NDU)):
      os.remove(MARKER_FILE_NDU)

   logger.info('Vpxd revert phase completed')
   progressReporter.updateProgress(100, _(_T("vpxd.revert.complete",
                                             'Reverting VPXD completed')))
