# Copyright (c) 2016-2017,2019, 2021 VMware, Inc. All rights reserved.
# VMware Confidential
"""Content Library Patch Logic for minor releases

"""
from datetime import datetime
import fileinput
import grp
import logging
import os
import pwd
import shutil

from base import patch_info, patch_test
from patches.constants import (
    CONTENT_LIBRARY_CONFIG_HOME, CONTENT_LIBRARY_HOME, CONTENT_LIBRARY_SERVICE_SPEC,
    CONTENT_LIBRARY_SERVICE_REGISTER_SPEC, KEY_STORE_NAME_MACHINE, CLS_LOG_DIR,
    PATCH_VERSION_6_6_9,PATCH_VERSION_6_7_0,
    REVERSE_PROXY_ADDRESS_KEY, REVERSE_PROXY_CONNECTION_TYPE_KEY, REGISTRY_EXTENSION_PATH,
    REVERSE_PROXY_CONNECTION_TYPE_LOCAL, SOLUTION_USER_NAME_KEY, SOLUTION_USER_ID_KEY,
    TC_SERVER_DIR, CLOUDVM_COMPONENTS_FOR_ID_TRANSLATION, USER_ROOT, USER_VAPI_ENDPOINT,
    VAPI_ENDPOINT_PROPERTIES_FILE, VMTX_EXTENSION_PATH, VDC_PROPERTIES_FILE,
    USER_CLS, VMTX_SERVICE_VAPI_CLI_JSON, VMTX_TEMPLATE_VAPI_COMPONENT,
    REGISTRY_SERVICE_VAPI_CLI_JSON, REGISTRY_TEMPLATE_VAPI_COMPONENT, CLS_TRUST_STORE_FILE)
from patches.messages import (PATCH_SUMMARY_6_6_9, PATCH_SUMMARY_6_7_0)
from utils import find_vdc_property, read_config


__author__ = 'VMware, Inc.'
__copyright__ = 'Copyright 2016-2017,2019, 2021 VMware, Inc. All rights reserved.'


logger = logging.getLogger(__name__)


@patch_info(PATCH_VERSION_6_6_9, PATCH_SUMMARY_6_6_9)
def patch_executor_6_6_9(patch_context):
    """
    Create content library trust store if it is not present already.
    The trust store is part of secure content library feature.
    """
    logger.info("Creating content library trust store")
    from cls_truststore import CLSTrustStore
    cls_trust_store = CLSTrustStore(CONTENT_LIBRARY_CONFIG_HOME)
    cls_trust_store.setup_cls_truststore()

@patch_info(PATCH_VERSION_6_7_0, PATCH_SUMMARY_6_7_0)
def patch_executor_6_7_0(patch_context):
    """
    Change content library files and directory ownership.
    """
    logger.info("Changing content library files and directory ownership")
    cls_uid = pwd.getpwnam(USER_CLS).pw_uid
    try:
        cls_grp = grp.getgrnam('cls')
        cls_grp_id = cls_grp.gr_gid
        change_owner(CONTENT_LIBRARY_CONFIG_HOME, usr_id=cls_uid, grp_id=cls_grp_id)
    except KeyError:
        raise Exception("cls, content-library service group non found!")


def change_owner(dir, usr_id=-1, grp_id=-1, permission=None):
    if not os.path.exists(dir):
        return
    os.chown(dir, usr_id, grp_id)
    for (root_dir, dirs, files) in os.walk(dir, topdown=True):
        os.chown(root_dir, usr_id, grp_id)
        for _file in files:
            file_path = os.path.join(root_dir, _file)
            os.chown(file_path, usr_id, grp_id)
            if(permission):
                os.chmod(file_path, permission)


def register_new_service(service_cli_json):
    """
    Register given service with lookup service as a patch, which is a re-registration. The
    registration uses existing info from service registration spec and uses key store name "machine"
    for lookup service connection.

    Args:
        service_cli_json : json cli file generated by vmodl code generation for the service
    """
    from cis_register import RegCIS

    logger.info("Read registered info from %s", CONTENT_LIBRARY_SERVICE_REGISTER_SPEC)
    service_register_properties = read_config(CONTENT_LIBRARY_SERVICE_REGISTER_SPEC)

    solution_user_name = None
    solution_user_id = None
    reverse_proxy_address = None
    for key, value in service_register_properties.items():
        if service_cli_json in value:
            logger.info("%s service already registered, skip service registration.",
                        service_cli_json)
            return
        if key == SOLUTION_USER_NAME_KEY:
            solution_user_name = value
        elif key == SOLUTION_USER_ID_KEY:
            solution_user_id = value
        elif key == REVERSE_PROXY_ADDRESS_KEY:
            reverse_proxy_address = value
        elif key == REVERSE_PROXY_CONNECTION_TYPE_KEY:
            reverse_proxy_connection_type = value
            if reverse_proxy_connection_type != REVERSE_PROXY_CONNECTION_TYPE_LOCAL:
                raise Exception("proxy connection type should be local in a patch setup!")

    logger.info("Registering %s service with lookup service", service_cli_json)
    logger.info("Using solution user name: %s, solution user id: %s, reverse proxy address: %s",
                solution_user_name, solution_user_id, reverse_proxy_address)

    # Note: This is a re-registration by specifying is_patch as true, and reverse proxy address for
    #       local connection is the port only
    service_reg_info = RegCIS(CONTENT_LIBRARY_HOME, services_spec=CONTENT_LIBRARY_SERVICE_SPEC,
                              vdc_cfg_dir=CONTENT_LIBRARY_CONFIG_HOME,
                              service_http_port=reverse_proxy_address, is_patch=True,
                              key_store_name=KEY_STORE_NAME_MACHINE)
    service_reg_info.registerAll(solution_user_name, solution_user_id)
    logger.info("Registered %s service successfully", service_cli_json)


def update_service_vapi_component(service_vapi_component):
    """
    This function ensures that the given component is included in the list (if it is not
    already) of components for which ID translation is performed.
    Also ensure that endpoint property file ownership is restored after the update

    Args:
        service_vapi_component : the service name added to vapi server endpoint properties
    """
    logger.info("Updating VAPI endpoint properties if %s endpoint is not included yet",
                service_vapi_component)
    with fileinput.FileInput(VAPI_ENDPOINT_PROPERTIES_FILE, inplace=True) as file:
        for line in file:
            line = line.rstrip('\n')
            if line.startswith(CLOUDVM_COMPONENTS_FOR_ID_TRANSLATION + "="):
                if service_vapi_component not in line:
                    logger.info("Adding %s API endpoint", service_vapi_component)
                    line = line + "," + service_vapi_component
            # write back to file
            print(line)

    # restore endpoint property file ownership
    root_uid = pwd.getpwnam(USER_ROOT).pw_uid
    vapi_endpoint_uid = pwd.getpwnam(USER_VAPI_ENDPOINT).pw_uid
    os.chown(VAPI_ENDPOINT_PROPERTIES_FILE, vapi_endpoint_uid, root_uid)
    logger.info("Finished checking and updating VAPI endpoint properties")


@patch_test(PATCH_VERSION_6_6_9)
def patch_tester_6_6_9(patch_context):
    verify_cls_trust_store_exists()

@patch_test(PATCH_VERSION_6_7_0)
def patch_tester_6_7_0(patch_context):
    """
    Verify CLS logs are owned by content-library.
    """
    CLS_LOG_FILE = os.path.join(CLS_LOG_DIR, 'cls.log')
    exists = os.path.isfile(CLS_LOG_FILE)
    if not exists:
        logger.warn("Could not verify cls file permission")
        return
    cls_uid = pwd.getpwnam(USER_CLS).pw_uid
    current_uid = os.stat(CLS_LOG_FILE).st_uid
    if cls_uid != current_uid:
        error_message = "Content library logs '%s' do not have right owner:" % CLS_LOG_DIR
        logger.error(error_message)
        raise Exception(error_message)


def verify_cls_trust_store_exists():
    """
    Verify CLS trust store file is created.
    """
    exists = os.path.isfile(CLS_TRUST_STORE_FILE)
    if not exists:
        error_message = "CLS trust store file: %s not found" % CLS_TRUST_STORE_FILE
        logger.error(error_message)
        raise Exception(error_message)
    logger.info("CLS trust store file: %s exists!!", CLS_TRUST_STORE_FILE)


def verify_service_registration(service_cli_json):
    """
    This function verifies the given service is registered

    Args:
        service_cli_json : json cli file generated by vmodl code generation for the service
    """
    # verify given service is registered
    service_registered = False
    service_register_properties = read_config(CONTENT_LIBRARY_SERVICE_REGISTER_SPEC)
    for key, value in service_register_properties.items():
        if service_cli_json in value:
            service_registered = True
            break
    if not service_registered:
        error_message = "%s is not registered properly!" % service_cli_json
        logger.error(error_message)
        raise Exception(error_message)
    logger.info("Registered %s successfully", service_cli_json)


def verify_service_vapi_endpoint(service_vapi_component):
    """
    This function verifies the vAPI component endpoint is configured for given service

    Args:
        service_vapi_component : the service name added to vAPI server endpoint properties
    """
    service_endpoint_updated = False
    vapi_properties = read_config(VAPI_ENDPOINT_PROPERTIES_FILE)
    for key, value in vapi_properties.items():
        if key == CLOUDVM_COMPONENTS_FOR_ID_TRANSLATION:
            service_endpoint_updated = service_vapi_component in value
    if not service_endpoint_updated:
        error_message = "%s is not configured with VAPI endpoints!" % service_vapi_component
        logger.error(error_message)
        raise Exception(error_message)
    logger.info("%s endpoint is configured with VAPI endpoints properly.", service_vapi_component)
