#!/usr/bin/env python

#
# Copyright 2017 VMware, Inc.  All rights reserved. -- VMware Confidential
#
"""
Contains a patch script that handles two B2B upgrade scenarios:
1. Upgrading from H5C releases that use the Eclipse Virgo server
to ones using a pure Tomcat server.
2. Upgrading from H5C releases that use a pure Tomcat server to
subsequent releases that also use Tomcat.


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 os
import shutil

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

from xml.etree import ElementTree
from .path_constants import VIRGO_INSTANCE_CONFIG_DIR, SERVER_XML_FILE,\
    TOMCAT_CONFIG_DIR, VIRGO_INSTANCE_REPOSITORY_DIR,\
    VIRGO_INSTANCE_WRAPPER_DIR
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.x-to-Tomcat and Tomcat-to-Tomcat patch.
    """

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

        version = LooseVersion("6.8.1.9999999")
        summary = localizedString(_T(
                "vsphere.ui.patch01.summary",
                "This patch facilitates the upgrade from H5C using Virgo 3.x"
                "one using Tomcat and from H5C using Tomcat to a subsequent"
                "version which is also using Tomcat"))
        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.
        """

        self.fix_server_xml()

        self.remove_dir(VIRGO_INSTANCE_CONFIG_DIR)
        self.remove_dir(VIRGO_INSTANCE_REPOSITORY_DIR)
        self.remove_dir(VIRGO_INSTANCE_WRAPPER_DIR)

        logger.info('Patch 01 was applied')

    def find(self, path, filename):
        """
        Finds a file with the given file name inside the given directory.
        NOTE: Does not search recursively.

        :param path: the path to search
        :param filename: the name of the file to search for
        :return: the full path to the file or an empty string
         if such a file does not exist
        """

        for root, dirs, files in os.walk(path):
            if filename in files:
                return os.path.join(root, filename)

        return ''

    def fix_server_xml(self):
        # If the server.xml.rpmnew is not found -
        # we need to update the original one, otherwise we remove the
        # original and use the .rpmnew altogether.
        # See https://bugzilla.eng.vmware.com/show_bug.cgi?id=1992932
        new_server_xml = self.find(TOMCAT_CONFIG_DIR, SERVER_XML_FILE + '.rpmnew')
        if new_server_xml:
            logger.info('Replacing old configuration with new in file ' +
                        SERVER_XML_FILE)
            os.remove(SERVER_XML_FILE)
            os.rename(new_server_xml, SERVER_XML_FILE)

        server_xml_tree = ElementTree.parse(SERVER_XML_FILE)
        self.retain_tls_protocol(server_xml_tree)
        self.fix_tomcat_connectors(server_xml_tree)
        server_xml_tree.write(SERVER_XML_FILE)

    def retain_tls_protocol(self, server_xml_tree):
        # retain TLSv1.2 only
        retained_tls_protocols = "TLSv1.2"

        new_connector = server_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, server_xml_tree):
        connectors = server_xml_tree.findall('.//Connector')
        connectors = list(filter(
            lambda connector: not connector.get('secure'),
            connectors))
        if len(connectors) < 2:
            return
        service = server_xml_tree.find('.//Service')

        logger.info("Removing extra Connector from " + SERVER_XML_FILE)
        service.remove(connectors[0])
        del connectors[1].attrib['address']

    def remove_dir(self, path):
        """
        Recursively deletes a given directory.

        :param path: the path that represents the directory to remove
        """
        if os.path.isdir(path):
            logger.info('Deleting folder ' + path)
            shutil.rmtree(path)
