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

# This is a script which will be launched by the software-packages plugin
# prior to starting the RPM install transaction.

import sys
import os
import json
import subprocess
import glob
from os.path import join, abspath, dirname, isfile

import logging
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
logger.addHandler(logging.StreamHandler())
sys.path.append(join(
    dirname(__file__),
    "patches",
    "libs",
    "feature-state"))
from update_utils import runCommand
sys.path.extend(os.environ['VMWARE_PYTHON_PATH'].split(':'))

# This code is to suppress logs from featureState.py printed to stdout in
# cis.defaults. They init feature state again based on their internal flag
# cis.defaults.is_initialized.
import featureState
featureState.init(False)
import cis.defaults
cis.defaults.is_initialized = True

from cis.svcsController import (get_services_status,
                                update_services_runstate)

# import the scripts used to do a minor upgrade configurations for the vsphere-ui
sys.path.append(join(
    dirname(__file__),
    "patches",
    "payload",
    "components-script",
    "vsphere_ui"))
from vsphere_ui_minor_upgrade import create_vsphere_client_solution_user_role_and_assign, register_vsphere_client_extension

scriptsDir = abspath(dirname(__file__))
# PR 1324322 - Workaround for upgrading kernel RPMs.
# This directory must exist when the kernel rpms are being installed
# in order for the grub menu.lst file to be updated.
try:
    os.makedirs("/var/log/YaST2/")
except:
    pass


try:
    STAGEDIR = '/storage/core/software-packages'
    with open(STAGEDIR + '/staged-configuration.json') as f:
        swUpdateState = json.load(f)
except:
    STAGEDIR = abspath(join(scriptsDir, ".."))
    with open(STAGEDIR + '/rpm-manifest.json') as f:
        swUpdateState = json.load(f)


def is_vcha_configured():
    '''
    Return True if vcha is configured
    '''
    VCHA_CFG_FILE_PATH = '/etc/vmware-vcha/vcha.aware'
    try:
        if isfile(VCHA_CFG_FILE_PATH):
            return True
    except Exception as e:
        logger.debug('Exception while checking VCHA is enabled: %s' % e)
    return False


def modifyPythonLinks():
    '''
    Modify the links to point to python3
    :return:
    '''
    commonScript = join(scriptsDir, "common-patch.sh")
    out, err, rc = runCommand(["/bin/bash", "--login", "-c", commonScript],
                              progress=True,
                              message="Running common-patch script.")
    logger.debug("common-patch script \n out=%s\nerror=%s\nrc=%d\n" %
                 (out, err, rc))
    if rc:
        logger.debug("Running common-patch.sh failed.")


def calculateServicesToStop():
    '''
    Calculate the list of services that will be stopped during upgrade process
    @return servicesToStop - List of services that will be stopped
    @return nonLeafServiceWillBeUpdated - if any non-leaf service will be
        updated
    '''
    leafServices = swUpdateState['header'].get('leaf_services', [])
    logger.debug('Leaf services from patch: %r', leafServices)
    pkgList = swUpdateState['files']
    leafServicesToStop = []
    nonLeafServiceWillBeUpdated = False
    statuses = get_services_status(None)
    logger.debug("Statuses %r", statuses)
    for name in pkgList:
        if pkgList[name]['type'] != 'rpm':
            continue
        name = name.lower()
        if name not in leafServices:
            logger.debug('Non-leaf rpm found: %r', name)
            nonLeafServiceWillBeUpdated = True
        else:
            logger.debug('Leaf rpm found: %r', name)
            if name in statuses:
                logger.debug('Status: %r', statuses[name])
                v = statuses[name]
                if isinstance(v, tuple):
                    v = v[0]
                if statuses[name][0] == 'RUNNING':
                    logger.debug('Adding to stop-list: %r', name)
                    leafServicesToStop.append(name)
            else:
                logger.debug('Service %s is unknown', name)
    runningNonLeafServices = []
    for k, v in list(statuses.items()):
        if isinstance(v, tuple):
            v = v[0]
        if v == 'RUNNING' and k not in leafServices:
            logger.debug('Non-leaf service %s is running.'
                         ' Adding to the non-leaf stop-list', k)
            runningNonLeafServices.append(k)
    servicesToStop = leafServicesToStop
    if nonLeafServiceWillBeUpdated:
        servicesToStop.extend(runningNonLeafServices)

    return servicesToStop, nonLeafServiceWillBeUpdated


def stopAffectedServices(servicesToStop):
    '''
    Stop affected services
    '''
    if not servicesToStop:
        return
    update_services_runstate(
        'stop', None, False, False, servicesToStop,

        ignore_err=False)

def extractUIFiles():
    APPLMGMT_RPM = "/storage/updatemgr/software-update/stage/applmgmt-6*.rpm"
    REDIRECT_JS = "/opt/vmware/share/htdocs/redirect.js"

    if glob.glob(APPLMGMT_RPM):
        rc = subprocess.call(' '.join(["/usr/bin/rpm2cpio", "`/usr/bin/ls", APPLMGMT_RPM,"` |",
                               "/usr/bin/cpio","-iv","--to-stdout","."+REDIRECT_JS,">"+REDIRECT_JS]),shell=True)
        if rc:
            logger.debug("Failed to replace redirect.js")

#PR 2158498
#This is hack.
#To workaround "0-" issue in UI we are extracting redirect.js file
#from applmgmt RPM and replacing it before actual patching starts.
# with this change updated redirect.js file will be loaded in UI
#to give proper error message[Instead 0- message] when backend is not reachable
extractUIFiles()

with open('/etc/applmgmt/appliance/update.conf') as f:
    version = json.load(f)['version']

if is_vcha_configured() and int(version.split(".")[3])  < 40000:
    raise Exception("This Update will impact VCHA Cluster.\
                    Please follow kb %s" %"https://kb.vmware.com/s/article/55938")

servicesToStop, nonLeafServiceWillBeUpdated = calculateServicesToStop()

# Execute methods needed by the vsphere-ui minor upgrade
try:
   create_vsphere_client_solution_user_role_and_assign()
   register_vsphere_client_extension()
except Exception as ex:
   logging.info("Failed to run the vsphere_ui_minor_upgrade scripts. Exception is: %s" % str(ex))

stopAffectedServices(servicesToStop)
if nonLeafServiceWillBeUpdated:
    modifyPythonLinks()

try:
    from subprocess import DEVNULL  # Python 3.
except ImportError:
    DEVNULL = open(os.devnull, 'wb')

if 'applmgmt' in servicesToStop:
    logger.info('applmgmt is started as a standalone process for patching')
    subprocess.Popen(["/usr/lib/applmgmt/applmgmt.launcher"],
                     stdout=DEVNULL,
                     stderr=DEVNULL)
