#!/usr/bin/env python

#
# Copyright 2019 VMware, Inc.  All rights reserved. -- VMware Confidential
#
"""
Patch script that does the following:
Fixes grpc endpoint registration if it is incorrect
"""

import logging
import os
import re
import sys
import warnings

from l10n import msgMetadata as _T, localizedString as _

VPXD_SVCS_PATCHES_DIR = os.path.dirname(os.path.dirname(__file__))
sys.path.append(VPXD_SVCS_PATCHES_DIR)
vmware_python_path = os.getenv('VMWARE_PYTHON_PATH')
if vmware_python_path and os.path.exists(vmware_python_path):
    sys.path.append(vmware_python_path)

from cis.cisreglib import LookupServiceClient, SsoClient, VmafdClient, _get_syscfg_info
from cis.utils import readprop, replace_properties_in_file, run_command, write_props_to_file
from cis.vecs import SsoGroup, cli_path, vmafd_machine_id
from cis.tools import get_install_parameter
from constants import DATASERVICES_PROP, VERSION
from fss_utils import getTargetFSS
import cis.cisreglib as crlib


logger = logging.getLogger(__name__)

# Suppress warnings coming from vmafd and identity imports.
with warnings.catch_warnings():
    warnings.simplefilter('ignore', RuntimeWarning)
    import vmafdpy3
    from identity.vmkeystore import VmKeyStore

IS_PRODUCT_ID = 'com.vmware.cis'
IS_TYPE_ID = 'cs.inventory'
MACHINE_NAME = 'machine'
SYNC_PROPERTY_NAME = 'Syncable'
SYNC_PROPERTY_VALUE = 'ELM,SPOG'
GRPC_ENDPOINT_PROTOCOL = 'gRPC'
GRPC_ENDPOINT_PORT = 4004
GRPC_ENDPOINT_TYPE = 'tagging'
LOCALURL_ATTRIBUTE_KEY = "cis.common.ep.localurl"
LOCALURL_ATTRIBUTE_VAL = "http://localhost:" + str(GRPC_ENDPOINT_PORT)
SUBSCRIBABLE_ATTRIBUTE_NAME = 'Subscribable'
SUBSCRIBABLE_ATTRIBUTE_TRUE_VALUE = 'true'
TAGGING_GRPC_PORT_NAME = 'dataservice.tagging.grpc.port'

def getChanges():
    return _(_T("vpxd-svcs.patch.summary",
                "Register GRPC endpoints for tagging service"))

def doPatching(ctx):
    if getTargetFSS("HybridVC_SyncaaS"):
        logger.info("vpxd-svcs patch: being executed %s", ctx)
        logger.info("Updating grpc endpoint.")
        update_hvc_properties()
        update_endpoints()
        addEnvoyRoutingForTopology()
        replace_properties_in_file(DATASERVICES_PROP, {VERSION: 5})

def update_hvc_properties():
    tagging_grpc_port = readprop(DATASERVICES_PROP, TAGGING_GRPC_PORT_NAME)
    if tagging_grpc_port is None:
        prop = [(TAGGING_GRPC_PORT_NAME, GRPC_ENDPOINT_PORT)]
        write_props_to_file(DATASERVICES_PROP, prop, mode='a')

def addEnvoyRoutingForTopology():
    from syncGrpcUtil import  addSyncableServiceCluster, addSyncableRouteToLocalService
    logger.info("Configuring envoy for routing grpc requests for tagging service")
    try:
        addSyncableServiceCluster("tagging-grpc-cluster", 4004)
        addSyncableRouteToLocalService("tagging", "tagging-grpc-cluster")
    except Exception as e:
        logger.error("Failed to add routes to envoy for tagging grpc service")
        logger.exception(e)

def update_endpoints():

    logger.info("Connecting to Lookup Service")
    ls_url, domain_name = _get_syscfg_info()
    ls_obj = LookupServiceClient(ls_url, retry_count=5)
    logger.info("Getting STS endpoint")
    sts_url, sts_cert_data = ls_obj.get_sts_endpoint_data()

    logger.info("Logging into SSO AdminClient as machine solution user")
    cert = None
    key = None

    try:
        with VmKeyStore('VKS') as ks:
            ks.load(MACHINE_NAME)
            cert = ks.get_certificate(MACHINE_NAME)
            key = ks.get_key(MACHINE_NAME)

        sso_client = SsoClient(sts_url, sts_cert_data, None, None, cert=cert,
                               key=key)
        ls_obj.set_sso_client(sso_client)

        logger.info("Check if gRPC endpoints exist")
        is_service_info_with_gRPC = ls_obj.get_local_service_info(IS_PRODUCT_ID, IS_TYPE_ID, GRPC_ENDPOINT_PROTOCOL)
        rproxy_port2 = get_install_parameter('rhttpproxy.ext.port2', '443', quiet=True)
        if is_service_info_with_gRPC:
            logger.info("Tagging service gRPC endpoints exist")
            service_info = ls_obj.service_content.serviceRegistration.Get(is_service_info_with_gRPC.serviceId)
            mutable_spec = ls_obj._svcinfo_to_setspec(service_info)
            logger.info("Updating spec and re-registering service")
            updateGrpcEndpoint(mutable_spec, GRPC_ENDPOINT_TYPE, rproxy_port2)
            ls_obj.reregister_service(service_info.serviceId, mutable_spec)
            sso_client.cleanup()
            return

        logger.info("Fetching service Info for Tagging from Lookup Service")
        is_service_info = ls_obj.get_local_service_info(IS_PRODUCT_ID, IS_TYPE_ID)
        # Get the service info in the format the reregister API accepts
        service_info = ls_obj.service_content.serviceRegistration.Get(is_service_info.serviceId)
        mutable_spec = ls_obj._svcinfo_to_setspec(service_info)


        logger.info("Adding Endpoint and Syncable property to Tagging")
        addSyncableAttribute(mutable_spec)
        logger.info("Adding Subscribable  property to Tagging")
        addSubscribableAttribute(mutable_spec)
        addGrpcEndpoint(mutable_spec, GRPC_ENDPOINT_TYPE, rproxy_port2)
        ls_obj.reregister_service(service_info.serviceId, mutable_spec)

        sso_client.cleanup()
    except Exception as e:
        logger.error("Failed to reregister Tagging service grpc endpoints with Lookup Service")
        logger.exception(e)

def addSyncableAttribute(mutable_spec):
    attribute = crlib.lookup.ServiceRegistration.Attribute()
    attribute.key = SYNC_PROPERTY_NAME
    attribute.value = SYNC_PROPERTY_VALUE
    mutable_spec.serviceAttributes.append(attribute)

def addSubscribableAttribute(mutable_spec):
    subscribablAttribute = crlib.lookup.ServiceRegistration.Attribute()
    subscribablAttribute.key = SUBSCRIBABLE_ATTRIBUTE_NAME
    subscribablAttribute.value = SUBSCRIBABLE_ATTRIBUTE_TRUE_VALUE
    mutable_spec.serviceAttributes.append(subscribablAttribute)

def updateGrpcEndpoint(mutable_spec, ep_id , rproxy_port):
    logger.info("gRPC endpoint found. Updating it with the reverse proxy port")
    vmafd_client = VmafdClient()
    for endpoint in mutable_spec.serviceEndpoints:
        if endpoint.endpointType.type == ep_id:
            endpoint_url = 'https://' + vmafd_client.get_pnid() + ':' \
                   + str(rproxy_port)
            endpoint.url = endpoint_url
            found = None
            attribute_list = []
            if not  endpoint.endpointAttributes:
                logger.info("No attributes found for endpoint. Adding property local url")
            else:
                attribute_list = endpoint.endpointAttributes
                for attr in endpoint.endpointAttributes:
                    if LOCALURL_ATTRIBUTE_KEY in attr.key:
                        found = True
                        logger.info('attibute local url found , updating the value')
                        attr.value = LOCALURL_ATTRIBUTE_VAL
            if not found:
                 print('Adding the grpc endpoint property local url')
                 attribute = crlib.lookup.ServiceRegistration.Attribute()
                 attribute.key = LOCALURL_ATTRIBUTE_KEY
                 attribute.value = LOCALURL_ATTRIBUTE_VAL
                 attribute_list.append(attribute)
                 endpoint.endpointAttributes = attribute_list

def addGrpcEndpoint(mutable_spec, ep_id, rproxy_port):
    endpoint = crlib.lookup.ServiceRegistration.Endpoint()

    vmafd_client = VmafdClient()
    endpoint_url = 'https://' + vmafd_client.get_pnid() + ':' \
                   + str(rproxy_port)
    logger.info("Endpoint Url for gRPC endpoint: " + endpoint_url)
    endpoint.url = endpoint_url

    endpoint_type = crlib.lookup.ServiceRegistration.EndpointType()
    endpoint_type.protocol = GRPC_ENDPOINT_PROTOCOL
    endpoint_type.type = ep_id
    endpoint.endpointType = endpoint_type
    endpoint.sslTrust = mutable_spec.serviceEndpoints[0].sslTrust
    attribute_list = []
    attribute = crlib.lookup.ServiceRegistration.Attribute()
    attribute.key = LOCALURL_ATTRIBUTE_KEY
    attribute.value = LOCALURL_ATTRIBUTE_VAL
    attribute_list.append(attribute)
    endpoint.endpointAttributes = attribute_list
    mutable_spec.serviceEndpoints.append(endpoint)
