#!/usr/bin/env python
"""
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements.  See the NOTICE file
distributed with this work for additional information
regarding copyright ownership.  The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License.  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

"""
# Python Imports
import subprocess
import os
import re
import time
import shutil
from datetime import datetime
import json

# Ambari Commons & Resource Management imports
from resource_management.libraries.script.script import Script
from resource_management.libraries.functions import format
from resource_management.libraries.functions.check_process_status import check_process_status
from resource_management.core.source import InlineTemplate
from resource_management.core.resources.system import Execute

# Imports needed for Rolling/Express Upgrade
from resource_management.libraries.functions import StackFeature
from resource_management.libraries.functions.stack_features import check_stack_feature
from resource_management.libraries.functions import conf_select
from resource_management.libraries.functions import stack_select
from resource_management.libraries.functions.copy_tarball import copy_to_hdfs

from resource_management.core import shell
from resource_management.core.exceptions import Fail
from resource_management.core.logger import Logger
from ambari_commons import OSCheck, OSConst
from ambari_commons.os_family_impl import OsFamilyImpl

from resource_management.core.exceptions import ComponentIsNotRunning
from resource_management.libraries.functions.decorator import retry
from resource_management.libraries.functions.security_commons import build_expectations, \
  cached_kinit_executor, get_params_from_filesystem, validate_security_config_properties, \
  FILE_TYPE_XML

# Local Imports
from setup_ranger_hive import setup_ranger_hive
from hive_service_interactive import hive_service_interactive
from hive_interactive import hive_interactive
from hive_server import HiveServerDefault
from setup_ranger_hive_interactive import setup_ranger_hive_interactive

import traceback

class HiveServerInteractive(Script):
  pass


@OsFamilyImpl(os_family=OsFamilyImpl.DEFAULT)
class HiveServerInteractiveDefault(HiveServerInteractive):

    def get_component_name(self):
      return "hive-server2-hive2"

    def install(self, env):
      import params
      self.install_packages(env)

    def configure(self, env):
      import params
      env.set_params(params)
      hive_interactive(name='hiveserver2')

    def pre_upgrade_restart(self, env, upgrade_type=None):
      Logger.info("Executing Hive Server Interactive Stack Upgrade pre-restart")
      import params
      env.set_params(params)

      if params.version and check_stack_feature(StackFeature.ROLLING_UPGRADE, params.version):
        stack_select.select("hive-server2-hive2", params.version)
        conf_select.select(params.stack_name, "hive2", params.version)

        # Copy hive.tar.gz and tez.tar.gz used by Hive Interactive to HDFS
        resource_created = copy_to_hdfs(
          "hive2",
          params.user_group,
          params.hdfs_user,
          host_sys_prepped=params.host_sys_prepped)

        resource_created = copy_to_hdfs(
          "tez_hive2",
          params.user_group,
          params.hdfs_user,
          host_sys_prepped=params.host_sys_prepped) or resource_created

        if resource_created:
          params.HdfsResource(None, action="execute")

    def start(self, env, upgrade_type=None):
      import params
      env.set_params(params)
      self.configure(env)

      if params.security_enabled:
        # Do the security setup, internally calls do_kinit()
        self.setup_security()

      # TODO : We need have conditional [re]start of LLAP once "status check command" for LLAP is ready.
      # Check status and based on that decide on [re]starting.

      # Start LLAP before Hive Server Interactive start.
      status = self._llap_start(env)
      if not status:
        raise Fail("Skipping START of Hive Server Interactive since LLAP app couldn't be STARTED.")

      # TODO : test the workability of Ranger and Hive2 during upgrade
      setup_ranger_hive_interactive(upgrade_type=upgrade_type)
      hive_service_interactive('hiveserver2', action='start', upgrade_type=upgrade_type)


    def stop(self, env, upgrade_type=None):
      import params
      env.set_params(params)

      if params.security_enabled:
        self.do_kinit()

      # Stop Hive Interactive Server first
      hive_service_interactive('hiveserver2', action='stop')

      self._llap_stop(env)

    def status(self, env):
      import status_params
      env.set_params(status_params)

      # We are not doing 'llap' status check done here as part of status check for 'HSI', as 'llap' status
      # check is a heavy weight operation.

      pid_file = format("{hive_pid_dir}/{hive_interactive_pid}")
      # Recursively check all existing gmetad pid files
      check_process_status(pid_file)

    def security_status(self, env):
      import status_params
      env.set_params(status_params)

      if status_params.security_enabled:
        props_value_check = {"hive.server2.authentication": "KERBEROS",
                             "hive.metastore.sasl.enabled": "true",
                             "hive.security.authorization.enabled": "true"}
        props_empty_check = ["hive.server2.authentication.kerberos.keytab",
                             "hive.server2.authentication.kerberos.principal",
                             "hive.server2.authentication.spnego.principal",
                             "hive.server2.authentication.spnego.keytab"]

        props_read_check = ["hive.server2.authentication.kerberos.keytab",
                            "hive.server2.authentication.spnego.keytab"]
        hive_site_props = build_expectations('hive-site', props_value_check, props_empty_check,
                                             props_read_check)

        hive_expectations ={}
        hive_expectations.update(hive_site_props)

        security_params = get_params_from_filesystem(status_params.hive_server_interactive_conf_dir,
                                                     {'hive-site.xml': FILE_TYPE_XML})
        result_issues = validate_security_config_properties(security_params, hive_expectations)
        if not result_issues: # If all validations passed successfully
          try:
            # Double check the dict before calling execute
            if 'hive-site' not in security_params \
              or 'hive.server2.authentication.kerberos.keytab' not in security_params['hive-site'] \
              or 'hive.server2.authentication.kerberos.principal' not in security_params['hive-site'] \
              or 'hive.server2.authentication.spnego.keytab' not in security_params['hive-site'] \
              or 'hive.server2.authentication.spnego.principal' not in security_params['hive-site']:
              self.put_structured_out({"securityState": "UNSECURED"})
              self.put_structured_out({"securityIssuesFound": "Keytab file or principal are not set property."})
              return

            cached_kinit_executor(status_params.kinit_path_local,
                                  status_params.hive_user,
                                  security_params['hive-site']['hive.server2.authentication.kerberos.keytab'],
                                  security_params['hive-site']['hive.server2.authentication.kerberos.principal'],
                                  status_params.hostname,
                                  status_params.tmp_dir)
            cached_kinit_executor(status_params.kinit_path_local,
                                  status_params.hive_user,
                                  security_params['hive-site']['hive.server2.authentication.spnego.keytab'],
                                  security_params['hive-site']['hive.server2.authentication.spnego.principal'],
                                  status_params.hostname,
                                  status_params.tmp_dir)
            self.put_structured_out({"securityState": "SECURED_KERBEROS"})
          except Exception as e:
            self.put_structured_out({"securityState": "ERROR"})
            self.put_structured_out({"securityStateErrorInfo": str(e)})
        else:
          issues = []
          for cf in result_issues:
            issues.append("Configuration file %s did not pass the validation. Reason: %s" % (cf, result_issues[cf]))
          self.put_structured_out({"securityIssuesFound": ". ".join(issues)})
          self.put_structured_out({"securityState": "UNSECURED"})
      else:
        self.put_structured_out({"securityState": "UNSECURED"})

    def restart_llap(self, env):
      """
      Custom command to Restart LLAP
      """
      Logger.info("Custom Command to retart LLAP")
      import params
      env.set_params(params)

      if params.security_enabled:
        self.do_kinit()

      self._llap_stop(env)
      self._llap_start(env)

    def _llap_stop(self, env):
      import params
      Logger.info("Stopping LLAP")
      SLIDER_APP_NAME = "llap0"

      stop_cmd = ["slider", "stop", SLIDER_APP_NAME]

      code, output, error = shell.call(stop_cmd, user=params.hive_user, stderr=subprocess.PIPE, logoutput=True)
      if code == 0:
        Logger.info(format("Stopped {SLIDER_APP_NAME} application on Slider successfully"))
      elif code == 69 and output is not None and "Unknown application instance" in output:
        Logger.info(format("Application {SLIDER_APP_NAME} was already stopped on Slider"))
      else:
        raise Fail(format("Could not stop application {SLIDER_APP_NAME} on Slider. {error}\n{output}"))

      # Will exit with code 4 if need to run with "--force" to delete directories and registries.
      Execute(('slider', 'destroy', SLIDER_APP_NAME, "--force"),
              user=params.hive_user,
              timeout=30,
              ignore_failures=True,