#!/usr/bin/env python

#
# Copyright 2016 VMware, Inc.  All rights reserved. -- VMware Confidential
#
"""
Contains a patch script that is used when upgrading from vSphere releases that use
Virgo 3.6.4 to ones using Virgo 3.7.0.

Patch scripts must be named "patch_XX.py" and must define a unique sub-class of the
PatchBase abstract class. Patches will be sorted by version and will be applied
incrementally.
"""
import sys
import logging
import fnmatch
import os

sys.path.append(os.environ['VMWARE_PYTHON_PATH'])

from xml.etree import ElementTree
from .path_constants import VIRGO_INSTANCE_CONFIG_DIR, TOMCAT_XML_FILE, \
    CONFIG_INI_FILE, \
    BUNDLES_INFO_FILE, USERREGION_PROP_FILE, SERVICEABILITY_XML_FILE, \
    VIRGO_KERNEL_PLUGINS_DIR, VIRGO_KERNEL_LIB_DIR, REPO_PROPS_FILE, \
    VIRGO_INSTANCE_BASE_DIR
from .file_util import text_subst_in_file
from .base import PatchBase
from l10n import localizedString, msgMetadata as _T
from distutils.version import LooseVersion

logger = logging.getLogger(__name__)


class Patch1(PatchBase):
    """
    Virgo 3.6.4 to 3.7.0 patch.
    """

    def __init__(self):
        """
        Constructor
        Set the patch version and summary.
        """

        version = LooseVersion("6.8.1.99999999999999")
        summary = localizedString(_T(
                "vsphere.client.patch01.summary",
                "This patch facilitates the upgrade from Virgo 3.6.4 to 3.7.0"))
        PatchBase.__init__(self, version, summary)

    def do_patching(self, patch_context):
        """
        Patch logic goes here.

        :param patch_context: Context given by the patch framework
        :type patch_context: PatchContext

        :raise UserError: Error that will be reported to the user.
        """
        duplicated_files = self.find_files(VIRGO_INSTANCE_CONFIG_DIR, '*.rpmnew')

        self.fix_tomcat_xml(duplicated_files)

        logger.info('Applying patch to ' + CONFIG_INI_FILE)
        self.fix_config_ini(duplicated_files)

        logger.info('Applying patch to ' + BUNDLES_INFO_FILE)
        self.fix_bundles_info(duplicated_files)

        logger.info('Applying patch to ' + USERREGION_PROP_FILE)
        self.fix_userregion_props(duplicated_files)

        logger.info('Applying patch to ' + REPO_PROPS_FILE)
        self.fix_repo_props(duplicated_files)

        logger.info('Applying patch to ' + SERVICEABILITY_XML_FILE)
        self.fix_serviceability_xml(duplicated_files)

        for filename in duplicated_files:
            orig_name = filename[0:-7]

            logger.info('Replacing old configuration with new in file ' + orig_name)
            os.remove(orig_name)
            os.rename(filename, orig_name)

        logger.info('Patch 01 was applied')

    def find_files(self, directory, pattern):
        files = []
        for root, dirnames, filenames in os.walk(directory):
            for filename in fnmatch.filter(filenames, pattern):
                files.append(os.path.join(root, filename))

        return files

    def fix_tomcat_xml(self, duplicated_files):
        tomcat_xml = TOMCAT_XML_FILE + '.rpmnew'
        if tomcat_xml not in duplicated_files:
            # For TLS protocols - if the tomcat-server.xml.rpmnew is not found -
            # we need to update the original one.
            # See https://bugzilla.eng.vmware.com/show_bug.cgi?id=1992932
            tomcat_xml = TOMCAT_XML_FILE
        tomcat_xml_tree = ElementTree.parse(tomcat_xml)
        self.retain_tls_protocol(tomcat_xml_tree)
        self.fix_tomcat_connectors(tomcat_xml_tree)
        tomcat_xml_tree.write(tomcat_xml)

    def retain_tls_protocol(self, tomcat_xml_tree):

        # retain TLSv1.2 only
        retained_tls_protocols = "TLSv1.2"

        new_connector = tomcat_xml_tree.find('.//SSLHostConfig')

        logger.info('Before: .//SSLHostconfig/protocols = {0}'.format(
            new_connector.get('protocols')))

        new_connector.set('protocols', retained_tls_protocols)

        logger.info('After: .//SSLHostconfig/protocols = {0}'.format(
            new_connector.get('protocols')))

    def fix_tomcat_connectors(self, tomcat_xml_tree):
        connectors = tomcat_xml_tree.findall('.//Connector')
        connectors = list(filter(lambda connector: not connector.get('secure'), connectors))
        if len(connectors) < 2:
            return
        service = tomcat_xml_tree.find('.//Service')
        service.remove(connectors[0])
        del connectors[1].attrib['address']

    def fix_config_ini(self, duplicated_files):
        new_config_ini = CONFIG_INI_FILE + '.rpmnew'
        if new_config_ini not in duplicated_files:
            return

        osgi_framework_path = r'osgi.framework=file\:' + VIRGO_KERNEL_PLUGINS_DIR + \
                            '/org.eclipse.osgi_'

        debug_path = os.path.join(VIRGO_INSTANCE_CONFIG_DIR, 'osgi-debug.properties')

        config_ini_subs = {
            r'osgi.framework=file\:plugins/org.eclipse.osgi_': osgi_framework_path,
            'configuration/serviceability.xml': SERVICEABILITY_XML_FILE,
            'configuration/tomcat-server.xml': TOMCAT_XML_FILE,
            'org.eclipse.equinox.simpleconfigurator/bundles.info': BUNDLES_INFO_FILE,
            'configuration/osgi-debug.properties': debug_path
        }

        text_subst_in_file(new_config_ini, config_ini_subs)

    def fix_bundles_info(self, duplicated_files):
        new_bundles_info = BUNDLES_INFO_FILE + '.rpmnew'
        if new_bundles_info not in duplicated_files:
            return

        text_subst_in_file(new_bundles_info, {'plugins': VIRGO_KERNEL_PLUGINS_DIR})

    def fix_userregion_props(self, duplicated_files):
        new_userregion_props = USERREGION_PROP_FILE + '.rpmnew'
        if new_userregion_props not in duplicated_files:
            return

        userregion_prop_subs = {
            'plugins': VIRGO_KERNEL_PLUGINS_DIR,
            'file\:lib': 'file\:' + VIRGO_KERNEL_LIB_DIR
        }

        text_subst_in_file(new_userregion_props, userregion_prop_subs)

    def fix_repo_props(self, duplicated_files):
        new_repo_props = REPO_PROPS_FILE + '.rpmnew'
        if new_repo_props not in duplicated_files:
            return

        usr_search_pattern = r'usr.searchPattern=' + VIRGO_INSTANCE_BASE_DIR + \
                             '/repository/usr/{artifact}'
        usr_repo_type = 'usr.type=external'
        repo_prop_subs = {
            r'usr.watchDirectory=repository/usr': usr_search_pattern,
            r'usr.type=watched': usr_repo_type
        }

        text_subst_in_file(new_repo_props, repo_prop_subs)

    def fix_serviceability_xml(self, duplicated_files):
        new_serviceability_xml = SERVICEABILITY_XML_FILE + '.rpmnew'
        if new_serviceability_xml not in duplicated_files:
            return

        log_path = '/var/log/vmware/vsphere-client/'
        serviceability_xml_substs = {
            '<file>serviceability/': '<file>' + log_path,
            '<FileNamePattern>serviceability/': '<FileNamePattern>' + log_path
        }

        text_subst_in_file(new_serviceability_xml, serviceability_xml_substs)